jabberd2  2.2.17
sasl_gsasl.c
Go to the documentation of this file.
1 /*
2  * jabberd - Jabber Open Source Server
3  * Copyright (c) 2002 Jeremie Miller, Thomas Muldowney,
4  * Ryan Eatmon, Robert Norris
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA02111-1307USA
19  */
20 
21 /* SASL authentication handler */
22 
23 #include "sx.h"
24 #include "sasl.h"
25 #include <gsasl.h>
26 #include <gsasl-mech.h>
27 #include <string.h>
28 
30 typedef struct _sx_sasl_st {
31  char *appname;
32  Gsasl *gsasl_ctx;
33 
35  void *cbarg;
36 
38 } *_sx_sasl_t;
39 
40 /* Per-session library handle. */
41 /* defined here to be able to get mechanism from handle */
43 {
44  Gsasl *ctx;
45  int clientp;
46  Gsasl_mechanism *mech;
47  void *mech_data;
49  /* Properties. */
51  char *authid;
52  char *authzid;
53  char *password;
54  char *passcode;
55  char *pin;
56  char *suggestedpin;
57  char *service;
58  char *hostname;
59  char *realm;
60 #ifndef GSASL_NO_OBSOLETE
61  /* Obsolete stuff. */
63 #endif
64 };
65 
66 /* another internal GSASL structures needed to hack into its internals */
67 #define DIGEST_MD5_LENGTH 16
68 
70 {
71  size_t nrealms;
72  char **realms;
73  char *nonce;
74  int qops;
75  int stale;
76  unsigned long servermaxbuf;
77  int utf8;
78  int ciphers;
79 };
81 
83 {
87 };
89 
91 {
98 };
100 
101 #define DIGEST_MD5_RESPONSE_LENGTH 32
103 {
104  char *username;
105  char *realm;
106  char *nonce;
107  char *cnonce;
108  unsigned long nc;
110  char *digesturi;
111  unsigned long clientmaxbuf;
112  int utf8;
114  char *authzid;
116 };
118 
120 {
122 };
124 
126 {
127  int step;
128  unsigned long readseqnum, sendseqnum;
137 };
139 
141 static nad_t _sx_sasl_success(sx_t s, char *data, int dlen) {
142  nad_t nad;
143  int ns;
144 
145  nad = nad_new();
146  ns = nad_add_namespace(nad, uri_SASL, NULL);
147 
148  nad_append_elem(nad, ns, "success", 0);
149  if(data != NULL)
150  nad_append_cdata(nad, data, dlen, 1);
151 
152  return nad;
153 }
154 
156 static nad_t _sx_sasl_failure(sx_t s, const char *err) {
157  nad_t nad;
158  int ns;
159 
160  nad = nad_new();
161  ns = nad_add_namespace(nad, uri_SASL, NULL);
162 
163  nad_append_elem(nad, ns, "failure", 0);
164  if(err != NULL)
165  nad_append_elem(nad, ns, err, 1);
166 
167  return nad;
168 }
169 
171 static nad_t _sx_sasl_challenge(sx_t s, char *data, int dlen) {
172  nad_t nad;
173  int ns;
174 
175  nad = nad_new();
176  ns = nad_add_namespace(nad, uri_SASL, NULL);
177 
178  nad_append_elem(nad, ns, "challenge", 0);
179  if(data != NULL)
180  nad_append_cdata(nad, data, dlen, 1);
181 
182  return nad;
183 }
184 
186 static nad_t _sx_sasl_response(sx_t s, char *data, int dlen) {
187  nad_t nad;
188  int ns;
189 
190  nad = nad_new();
191  ns = nad_add_namespace(nad, uri_SASL, NULL);
192 
193  nad_append_elem(nad, ns, "response", 0);
194  if(data != NULL)
195  nad_append_cdata(nad, data, dlen, 1);
196 
197  return nad;
198 }
199 
202  nad_t nad;
203  int ns;
204 
205  nad = nad_new();
206  ns = nad_add_namespace(nad, uri_SASL, NULL);
207 
208  nad_append_elem(nad, ns, "abort", 0);
209 
210  return nad;
211 }
212 
213 static int _sx_sasl_wio(sx_t s, sx_plugin_t p, sx_buf_t buf) {
214  sx_error_t sxe;
215  size_t len;
216  int ret;
217  char *out;
218  Gsasl_session *sd = (Gsasl_session *) s->plugin_data[p->index];
219 
220  _sx_debug(ZONE, "doing sasl encode");
221 
222  /* encode the output */
223  ret = gsasl_encode(sd, buf->data, buf->len, &out, &len);
224  if (ret != GSASL_OK) {
225  _sx_debug(ZONE, "gsasl_encode failed (%d): %s", ret, gsasl_strerror (ret));
226  /* Fatal error */
227  _sx_gen_error(sxe, SX_ERR_AUTH, "SASL Stream encoding failed", (char*) gsasl_strerror (ret));
228  _sx_event(s, event_ERROR, (void *) &sxe);
229  return -1;
230  }
231 
232  /* replace the buffer */
233  _sx_buffer_set(buf, out, len, NULL);
234  free(out);
235 
236  _sx_debug(ZONE, "%d bytes encoded for sasl channel", buf->len);
237 
238  return 1;
239 }
240 
241 static int _sx_sasl_rio(sx_t s, sx_plugin_t p, sx_buf_t buf) {
242  sx_error_t sxe;
243  size_t len;
244  int ret;
245  char *out;
246  Gsasl_session *sd = (Gsasl_session *) s->plugin_data[p->index];
247 
248  _sx_debug(ZONE, "doing sasl decode");
249 
250  /* decode the input */
251  ret = gsasl_decode(sd, buf->data, buf->len, &out, &len);
252  if (ret != GSASL_OK) {
253  _sx_debug(ZONE, "gsasl_decode failed (%d): %s", ret, gsasl_strerror (ret));
254  /* Fatal error */
255  _sx_gen_error(sxe, SX_ERR_AUTH, "SASL Stream decoding failed", (char*) gsasl_strerror (ret));
256  _sx_event(s, event_ERROR, (void *) &sxe);
257  return -1;
258  }
259 
260  /* replace the buffer */
261  _sx_buffer_set(buf, out, len, NULL);
262  free(out);
263 
264  _sx_debug(ZONE, "%d bytes decoded from sasl channel", len);
265 
266  return 1;
267 }
268 
271  char *method, *authzid;
272  const char *realm = NULL;
273  struct sx_sasl_creds_st creds = {NULL, NULL, NULL, NULL};
274  _sx_sasl_t ctx = gsasl_session_hook_get(sd);
275 
276  /* get the method */
277  method = (char *) malloc(sizeof(char) * (strlen(sd->mech->name) + 6));
278  sprintf(method, "SASL/%s", sd->mech->name);
279 
280  /* and the authorization identifier */
281  creds.authzid = gsasl_property_fast(sd, GSASL_AUTHZID);
282  creds.authnid = gsasl_property_fast(sd, GSASL_AUTHID);
283  creds.realm = gsasl_property_fast(sd, GSASL_REALM);
284 
285  if(0 && ctx && ctx->cb) { /* not supported yet */
286  if((ctx->cb)(sx_sasl_cb_CHECK_AUTHZID, &creds, NULL, s, ctx->cbarg)!=sx_sasl_ret_OK) {
287  _sx_debug(ZONE, "stream authzid: %s verification failed, not advancing to auth state", creds.authzid);
288  free(method);
289  return;
290  }
291  } else if (NULL != gsasl_property_fast(sd, GSASL_GSSAPI_DISPLAY_NAME)) {
292  creds.authzid = strdup(gsasl_property_fast(sd, GSASL_GSSAPI_DISPLAY_NAME));
293  authzid = NULL;
294  } else {
295  /* override unchecked arbitrary authzid */
296  if(creds.realm && creds.realm[0] != '\0') {
297  realm = creds.realm;
298  } else {
299  realm = s->req_to;
300  }
301  authzid = (char *) malloc(sizeof(char) * (strlen(creds.authnid) + strlen(realm) + 2));
302  sprintf(authzid, "%s@%s", creds.authnid, realm);
303  creds.authzid = authzid;
304  }
305 
306  /* proceed stream to authenticated state */
307  sx_auth(s, method, creds.authzid);
308 
309  free(method);
310  if(authzid) free(authzid);
311 }
312 
314 static void _sx_sasl_stream(sx_t s, sx_plugin_t p) {
315  Gsasl_session *sd = (Gsasl_session *) s->plugin_data[p->index];
316 
317  /* do nothing the first time */
318  if(sd == NULL)
319  return;
320 
321  /* are we auth'd? */
322  if(NULL == gsasl_property_fast(sd, GSASL_AUTHID)) {
323  _sx_debug(ZONE, "not auth'd, not advancing to auth'd state yet");
324  return;
325  }
326 
327  /* otherwise, its auth time */
328  _sx_sasl_open(s, sd);
329 }
330 
331 static void _sx_sasl_features(sx_t s, sx_plugin_t p, nad_t nad) {
332  _sx_sasl_t ctx = (_sx_sasl_t) p->private;
333  Gsasl_session *sd = (Gsasl_session *) s->plugin_data[p->index];
334  int nmechs, ret;
335  char *mechs, *mech, *c;
336 
337  if(s->type != type_SERVER)
338  return;
339 
340  if(sd != NULL) {
341  _sx_debug(ZONE, "already auth'd, not offering sasl mechanisms");
342  return;
343  }
344 
345  if(!(s->flags & SX_SASL_OFFER)) {
346  _sx_debug(ZONE, "application didn't ask us to offer sasl, so we won't");
347  return;
348  }
349 
350 #ifdef HAVE_SSL
351  if((s->flags & SX_SSL_STARTTLS_REQUIRE) && s->ssf == 0) {
352  _sx_debug(ZONE, "ssl not established yet but the app requires it, not offering mechanisms");
353  return;
354  }
355 #endif
356 
357  _sx_debug(ZONE, "offering sasl mechanisms");
358 
359  ret = gsasl_server_mechlist(ctx->gsasl_ctx, &mechs);
360  if(ret != GSASL_OK) {
361  _sx_debug(ZONE, "gsasl_server_mechlist failed (%d): %s, not offering sasl for this conn", ret, gsasl_strerror (ret));
362  return;
363  }
364 
365  mech = mechs;
366  nmechs = 0;
367  while(mech != NULL) {
368  c = strchr(mech, ' ');
369  if(c != NULL)
370  *c = '\0';
371 
372  if ((ctx->cb)(sx_sasl_cb_CHECK_MECH, mech, NULL, s, ctx->cbarg)==sx_sasl_ret_OK) {
373  if (nmechs == 0) {
374  int ns = nad_add_namespace(nad, uri_SASL, NULL);
375  nad_append_elem(nad, ns, "mechanisms", 1);
376  }
377  _sx_debug(ZONE, "offering mechanism: %s", mech);
378 
379  nad_append_elem(nad, -1 /*ns*/, "mechanism", 2);
380  nad_append_cdata(nad, mech, strlen(mech), 3);
381  nmechs++;
382  }
383 
384  if(c == NULL)
385  mech = NULL;
386  else
387  mech = ++c;
388  }
389 
390  free(mechs);
391 }
392 
394 static void _sx_sasl_notify_success(sx_t s, void *arg) {
395  sx_plugin_t p = (sx_plugin_t) arg;
396 
397  _sx_chain_io_plugin(s, p);
398  _sx_debug(ZONE, "auth completed, resetting");
399 
400  _sx_reset(s);
401 
402  sx_server_init(s, s->flags);
403 }
404 
406 static void _sx_sasl_client_process(sx_t s, sx_plugin_t p, Gsasl_session *sd, char *mech, char *in, int inlen) {
407  _sx_sasl_t ctx = (_sx_sasl_t) p->private;
408  char *buf = NULL, *out = NULL, *realm = NULL, **ext_id;
409  char hostname[256];
410  int ret;
411 #ifdef HAVE_SSL
412  int i;
413 #endif
414  size_t buflen, outlen;
415 
416  if(mech != NULL) {
417  _sx_debug(ZONE, "auth request from client (mechanism=%s)", mech);
418 
419  if(!gsasl_server_support_p(ctx->gsasl_ctx, mech)) {
420  _sx_debug(ZONE, "client requested mechanism (%s) that we didn't offer", mech);
422  return;
423  }
424 
425  /* startup */
426  ret = gsasl_server_start(ctx->gsasl_ctx, mech, &sd);
427  if(ret != GSASL_OK) {
428  _sx_debug(ZONE, "gsasl_server_start failed, no sasl for this conn; (%d): %s", ret, gsasl_strerror(ret));
430  return;
431  }
432 
433  /* get the realm */
434  if(ctx->cb != NULL)
435  (ctx->cb)(sx_sasl_cb_GET_REALM, NULL, (void **) &realm, s, ctx->cbarg);
436 
437  gsasl_session_hook_set(sd, (void *) ctx);
438  gsasl_property_set(sd, GSASL_SERVICE, ctx->appname);
439  gsasl_property_set(sd, GSASL_REALM, realm);
440 
441  /* allow only qop=auth for DIGEST-MD5 */
442  if (!strncmp(mech, "DIGEST-MD5", 10)) {
445  }
446 
447  /* get hostname */
448  hostname[0] = '\0';
449  gethostname(hostname, 256);
450  hostname[255] = '\0';
451  gsasl_property_set(sd, GSASL_HOSTNAME, hostname);
452 
453  /* get EXTERNAL data from the ssl plugin */
454  ext_id = NULL;
455 #ifdef HAVE_SSL
456  for(i = 0; i < s->env->nplugins; i++)
457  if(s->env->plugins[i]->magic == SX_SSL_MAGIC && s->plugin_data[s->env->plugins[i]->index] != NULL)
458  ext_id = ((_sx_ssl_conn_t) s->plugin_data[s->env->plugins[i]->index])->external_id;
459  if (ext_id != NULL) {
460  //_sx_debug(ZONE, "sasl context ext id '%s'", ext_id);
461  /* if there is, store it for later */
462  for (i = 0; i < SX_CONN_EXTERNAL_ID_MAX_COUNT; i++)
463  if (ext_id[i] != NULL) {
464  ctx->ext_id[i] = strdup(ext_id[i]);
465  } else {
466  ctx->ext_id[i] = NULL;
467  break;
468  }
469  }
470 #endif
471 
472  _sx_debug(ZONE, "sasl context initialised for %d", s->tag);
473 
474  s->plugin_data[p->index] = (void *) sd;
475 
476  if(strcmp(mech, "ANONYMOUS") == 0) {
477  /*
478  * special case for SASL ANONYMOUS: ignore the initial
479  * response provided by the client and generate a random
480  * authid to use as the jid node for the user, as
481  * specified in XEP-0175
482  */
483  (ctx->cb)(sx_sasl_cb_GEN_AUTHZID, NULL, (void **)&out, s, ctx->cbarg);
484  buf = strdup(out);
485  buflen = strlen(buf);
486  } else if (strstr(in, "<") != NULL && strncmp(in, "=", strstr(in, "<") - in ) == 0) {
487  /* XXX The above check is hackish, but `in` is just weird */
488  /* This is a special case for SASL External c2s. See XEP-0178 */
489  _sx_debug(ZONE, "gsasl auth string is empty");
490  buf = strdup("");
491  buflen = strlen(buf);
492  } else {
493  /* decode and process */
494  ret = gsasl_base64_from(in, inlen, &buf, &buflen);
495  if (ret != GSASL_OK) {
496  _sx_debug(ZONE, "gsasl_base64_from failed, no sasl for this conn; (%d): %s", ret, gsasl_strerror(ret));
498  if(buf != NULL) free(buf);
499  return;
500  }
501  }
502 
503  ret = gsasl_step(sd, buf, buflen, &out, &outlen);
504  if(ret != GSASL_OK && ret != GSASL_NEEDS_MORE) {
505  _sx_debug(ZONE, "gsasl_step failed, no sasl for this conn; (%d): %s", ret, gsasl_strerror(ret));
507  if(out != NULL) free(out);
508  if(buf != NULL) free(buf);
509  return;
510  }
511  }
512 
513  else {
514  /* decode and process */
515  ret = gsasl_base64_from(in, inlen, &buf, &buflen);
516  if (ret != GSASL_OK) {
517  _sx_debug(ZONE, "gsasl_base64_from failed, no sasl for this conn; (%d): %s", ret, gsasl_strerror(ret));
519  return;
520  }
521 
522  if(!sd) {
523  _sx_debug(ZONE, "response send before auth request enabling mechanism (decoded: %.*s)", buflen, buf);
525  if(buf != NULL) free(buf);
526  return;
527  }
528  _sx_debug(ZONE, "response from client (decoded: %.*s)", buflen, buf);
529  ret = gsasl_step(sd, buf, buflen, &out, &outlen);
530  }
531 
532  if(buf != NULL) free(buf);
533 
534  /* auth completed */
535  if(ret == GSASL_OK) {
536  _sx_debug(ZONE, "sasl handshake completed");
537 
538  /* encode the leftover response */
539  ret = gsasl_base64_to(out, outlen, &buf, &buflen);
540  if (ret == GSASL_OK) {
541  /* send success */
542  _sx_nad_write(s, _sx_sasl_success(s, buf, buflen), 0);
543  free(buf);
544 
545  /* set a notify on the success nad buffer */
546  ((sx_buf_t) s->wbufq->front->data)->notify = _sx_sasl_notify_success;
547  ((sx_buf_t) s->wbufq->front->data)->notify_arg = (void *) p;
548  }
549  else {
550  _sx_debug(ZONE, "gsasl_base64_to failed, no sasl for this conn; (%d): %s", ret, gsasl_strerror(ret));
552  if(buf != NULL) free(buf);
553  }
554 
555  if(out != NULL) free(out);
556 
557  return;
558  }
559 
560  /* in progress */
561  if(ret == GSASL_NEEDS_MORE) {
562  _sx_debug(ZONE, "sasl handshake in progress (challenge: %.*s)", outlen, out);
563 
564  /* encode the challenge */
565  ret = gsasl_base64_to(out, outlen, &buf, &buflen);
566  if (ret == GSASL_OK) {
567  _sx_nad_write(s, _sx_sasl_challenge(s, buf, buflen), 0);
568  free(buf);
569  }
570  else {
571  _sx_debug(ZONE, "gsasl_base64_to failed, no sasl for this conn; (%d): %s", ret, gsasl_strerror(ret));
573  if(buf != NULL) free(buf);
574  }
575 
576  if(out != NULL) free(out);
577 
578  return;
579  }
580 
581  if(out != NULL) free(out);
582 
583  /* its over */
584  _sx_debug(ZONE, "sasl handshake failed; (%d): %s", ret, gsasl_strerror(ret));
585 
586  /* !!! TODO XXX check ret and flag error appropriately */
588 }
589 
591 static void _sx_sasl_server_process(sx_t s, sx_plugin_t p, Gsasl_session *sd, char *in, int inlen) {
592  char *buf = NULL, *out = NULL;
593  size_t buflen, outlen;
594  int ret;
595 
596  _sx_debug(ZONE, "data from client");
597 
598  /* decode the response */
599  ret = gsasl_base64_from(in, inlen, &buf, &buflen);
600 
601  if (ret == GSASL_OK) {
602  _sx_debug(ZONE, "decoded data: %.*s", buflen, buf);
603 
604  /* process the data */
605  ret = gsasl_step(sd, buf, buflen, &out, &outlen);
606  if(buf != NULL) free(buf); buf = NULL;
607 
608  /* in progress */
609  if(ret == GSASL_OK || ret == GSASL_NEEDS_MORE) {
610  _sx_debug(ZONE, "sasl handshake in progress (response: %.*s)", outlen, out);
611 
612  /* encode the response */
613  ret = gsasl_base64_to(out, outlen, &buf, &buflen);
614 
615  if (ret == GSASL_OK) {
616  _sx_nad_write(s, _sx_sasl_response(s, buf, buflen), 0);
617  }
618 
619  if(out != NULL) free(out);
620  if(buf != NULL) free(buf);
621 
622  return;
623  }
624  }
625  if(out != NULL) free(out);
626  if(buf != NULL) free(buf);
627 
628  /* its over */
629  _sx_debug(ZONE, "sasl handshake aborted; (%d): %s", ret, gsasl_strerror(ret));
630 
631  _sx_nad_write(s, _sx_sasl_abort(s), 0);
632 }
633 
635 static int _sx_sasl_process(sx_t s, sx_plugin_t p, nad_t nad) {
636  Gsasl_session *sd = (Gsasl_session *) s->plugin_data[p->index];
637  int attr;
638  char mech[128];
639  sx_error_t sxe;
640  int flags;
641  char *ns = NULL, *to = NULL, *from = NULL, *version = NULL;
642 
643  /* only want sasl packets */
644  if(NAD_ENS(nad, 0) < 0 || NAD_NURI_L(nad, NAD_ENS(nad, 0)) != strlen(uri_SASL) || strncmp(NAD_NURI(nad, NAD_ENS(nad, 0)), uri_SASL, strlen(uri_SASL)) != 0)
645  return 1;
646 
647  /* quietly drop it if sasl is disabled, or if not ready */
648  if(s->state != state_STREAM) {
649  _sx_debug(ZONE, "not correct state for sasl, ignoring");
650  nad_free(nad);
651  return 0;
652  }
653 
654  /* packets from the client */
655  if(s->type == type_SERVER) {
656  if(!(s->flags & SX_SASL_OFFER)) {
657  _sx_debug(ZONE, "they tried to do sasl, but we never offered it, ignoring");
658  nad_free(nad);
659  return 0;
660  }
661 
662 #ifdef HAVE_SSL
663  if((s->flags & SX_SSL_STARTTLS_REQUIRE) && s->ssf == 0) {
664  _sx_debug(ZONE, "they tried to do sasl, but they have to do starttls first, ignoring");
665  nad_free(nad);
666  return 0;
667  }
668 #endif
669 
670  /* auth */
671  if(NAD_ENAME_L(nad, 0) == 4 && strncmp("auth", NAD_ENAME(nad, 0), NAD_ENAME_L(nad, 0)) == 0) {
672  /* require mechanism */
673  if((attr = nad_find_attr(nad, 0, -1, "mechanism", NULL)) < 0) {
675  nad_free(nad);
676  return 0;
677  }
678 
679  /* extract */
680  snprintf(mech, 127, "%.*s", NAD_AVAL_L(nad, attr), NAD_AVAL(nad, attr));
681 
682  /* go */
683  _sx_sasl_client_process(s, p, sd, mech, NAD_CDATA(nad, 0), NAD_CDATA_L(nad, 0));
684 
685  nad_free(nad);
686  return 0;
687  }
688 
689  /* response */
690  else if(NAD_ENAME_L(nad, 0) == 8 && strncmp("response", NAD_ENAME(nad, 0), NAD_ENAME_L(nad, 0)) == 0) {
691  /* process it */
692  _sx_sasl_client_process(s, p, sd, NULL, NAD_CDATA(nad, 0), NAD_CDATA_L(nad, 0));
693 
694  nad_free(nad);
695  return 0;
696  }
697 
698  /* abort */
699  else if(NAD_ENAME_L(nad, 0) == 5 && strncmp("abort", NAD_ENAME(nad, 0), NAD_ENAME_L(nad, 0)) == 0) {
700  _sx_debug(ZONE, "sasl handshake aborted");
701 
703 
704  nad_free(nad);
705  return 0;
706  }
707  }
708 
709  /* packets from the server */
710  else if(s->type == type_CLIENT) {
711  if(sd == NULL) {
712  _sx_debug(ZONE, "got sasl client packets, but they never started sasl, ignoring");
713  nad_free(nad);
714  return 0;
715  }
716 
717  /* challenge */
718  if(NAD_ENAME_L(nad, 0) == 9 && strncmp("challenge", NAD_ENAME(nad, 0), NAD_ENAME_L(nad, 0)) == 0) {
719  /* process it */
720  _sx_sasl_server_process(s, p, sd, NAD_CDATA(nad, 0), NAD_CDATA_L(nad, 0));
721 
722  nad_free(nad);
723  return 0;
724  }
725 
726  /* success */
727  else if(NAD_ENAME_L(nad, 0) == 7 && strncmp("success", NAD_ENAME(nad, 0), NAD_ENAME_L(nad, 0)) == 0) {
728  _sx_debug(ZONE, "sasl handshake completed, resetting");
729  nad_free(nad);
730 
731  /* save interesting bits */
732  flags = s->flags;
733 
734  if(s->ns != NULL) ns = strdup(s->ns);
735 
736  if(s->req_to != NULL) to = strdup(s->req_to);
737  if(s->req_from != NULL) from = strdup(s->req_from);
738  if(s->req_version != NULL) version = strdup(s->req_version);
739 
740  /* reset state */
741  _sx_reset(s);
742 
743  _sx_debug(ZONE, "restarting stream with sasl layer established");
744 
745  /* second time round */
746  sx_client_init(s, flags, ns, to, from, version);
747 
748  /* free bits */
749  if(ns != NULL) free(ns);
750  if(to != NULL) free(to);
751  if(from != NULL) free(from);
752  if(version != NULL) free(version);
753 
754  return 0;
755  }
756 
757  /* failure */
758  else if(NAD_ENAME_L(nad, 0) == 7 && strncmp("failure", NAD_ENAME(nad, 0), NAD_ENAME_L(nad, 0)) == 0) {
759  /* fire the error */
760  _sx_gen_error(sxe, SX_ERR_AUTH, "Authentication failed", NULL);
761  _sx_event(s, event_ERROR, (void *) &sxe);
762 
763  /* cleanup */
764  gsasl_finish(sd);
765 
766  s->plugin_data[p->index] = NULL;
767 
768  nad_free(nad);
769  return 0;
770  }
771  }
772 
773  /* invalid sasl command, quietly drop it */
774  _sx_debug(ZONE, "unknown sasl command '%.*s', ignoring", NAD_ENAME_L(nad, 0), NAD_ENAME(nad, 0));
775 
776  nad_free(nad);
777  return 0;
778 }
779 
781 static void _sx_sasl_free(sx_t s, sx_plugin_t p) {
782  Gsasl_session *sd = (Gsasl_session *) s->plugin_data[p->index];
783 
784  if(sd == NULL)
785  return;
786 
787  _sx_debug(ZONE, "cleaning up conn state");
788 
789  gsasl_finish(sd);
790  s->plugin_data[p->index] = NULL;
791 }
792 
793 static int _sx_sasl_gsasl_callback(Gsasl *gsasl_ctx, Gsasl_session *sd, Gsasl_property prop) {
794  _sx_sasl_t ctx = gsasl_session_hook_get(sd);
795  struct sx_sasl_creds_st creds = {NULL, NULL, NULL, NULL};
796  char *value, *node, *host;
797  int len, i;
798 
799  _sx_debug(ZONE, "in _sx_sasl_gsasl_callback, property: %d", prop);
800  switch(prop) {
801  case GSASL_PASSWORD:
802  /* GSASL_AUTHID, GSASL_AUTHZID, GSASL_REALM */
803  assert((ctx->cb != NULL));
804  creds.authnid = gsasl_property_fast(sd, GSASL_AUTHID);
805  creds.realm = gsasl_property_fast(sd, GSASL_REALM);
806  if(!creds.authnid) return GSASL_NO_AUTHID;
807  if(!creds.realm) return GSASL_NO_AUTHZID;
808  if((ctx->cb)(sx_sasl_cb_GET_PASS, &creds, (void **)&value, NULL, ctx->cbarg) == sx_sasl_ret_OK) {
809  gsasl_property_set(sd, GSASL_PASSWORD, value);
810  }
811  return GSASL_NEEDS_MORE;
812 
813  case GSASL_SERVICE:
814  gsasl_property_set(sd, GSASL_SERVICE, "xmpp");
815  return GSASL_OK;
816 
817  case GSASL_HOSTNAME:
818  {
819  char hostname[256];
820  /* get hostname */
821  hostname[0] = '\0';
822  gethostname(hostname, 256);
823  hostname[255] = '\0';
824 
825  gsasl_property_set(sd, GSASL_HOSTNAME, hostname);
826  }
827  return GSASL_OK;
828 
829  case GSASL_VALIDATE_SIMPLE:
830  /* GSASL_AUTHID, GSASL_AUTHZID, GSASL_PASSWORD */
831  assert((ctx->cb != NULL));
832  creds.authnid = gsasl_property_fast(sd, GSASL_AUTHID);
833  creds.realm = gsasl_property_fast(sd, GSASL_REALM);
834  creds.pass = gsasl_property_fast(sd, GSASL_PASSWORD);
835  if(!creds.authnid) return GSASL_NO_AUTHID;
836  if(!creds.realm) return GSASL_NO_AUTHZID;
837  if(!creds.pass) return GSASL_NO_PASSWORD;
838  if((ctx->cb)(sx_sasl_cb_CHECK_PASS, &creds, NULL, NULL, ctx->cbarg) == sx_sasl_ret_OK)
839  return GSASL_OK;
840  else
841  return GSASL_AUTHENTICATION_ERROR;
842 
843  case GSASL_VALIDATE_GSSAPI:
844  /* GSASL_AUTHZID, GSASL_GSSAPI_DISPLAY_NAME */
845  creds.authnid = gsasl_property_fast(sd, GSASL_GSSAPI_DISPLAY_NAME);
846  if(!creds.authnid) return GSASL_NO_AUTHID;
847  creds.authzid = gsasl_property_fast(sd, GSASL_AUTHZID);
848  if(!creds.authzid) return GSASL_NO_AUTHZID;
849  gsasl_property_set(sd, GSASL_AUTHID, creds.authnid);
850  return GSASL_OK;
851 
852  case GSASL_VALIDATE_ANONYMOUS:
853  /* GSASL_ANONYMOUS_TOKEN */
854  creds.authnid = gsasl_property_fast(sd, GSASL_ANONYMOUS_TOKEN);
855  if(!creds.authnid) return GSASL_NO_ANONYMOUS_TOKEN;
856  /* set token as authid for later use */
857  gsasl_property_set(sd, GSASL_AUTHID, creds.authnid);
858  return GSASL_OK;
859 
860  case GSASL_VALIDATE_EXTERNAL:
861  /* GSASL_AUTHID */
862  creds.authzid = gsasl_property_fast(sd, GSASL_AUTHZID);
863  _sx_debug(ZONE, "sasl external");
864  _sx_debug(ZONE, "sasl creds.authzid is '%s'", creds.authzid);
865 
866  for (i = 0; i < SX_CONN_EXTERNAL_ID_MAX_COUNT; i++) {
867  if (ctx->ext_id[i] == NULL)
868  break;
869  _sx_debug(ZONE, "sasl ext_id(%d) is '%s'", i, ctx->ext_id[i]);
870  /* XXX hackish.. detect c2s by existance of @ */
871  value = strstr(ctx->ext_id[i], "@");
872 
873  if(value == NULL && creds.authzid != NULL && strcmp(ctx->ext_id[i], creds.authzid) == 0) {
874  // s2s connection and it's valid
875  /* TODO Handle wildcards and other thigs from XEP-0178 */
876  _sx_debug(ZONE, "sasl ctx->ext_id doesn't have '@' in it. Assuming s2s");
877  return GSASL_OK;
878  }
879  if(value != NULL &&
880  ((creds.authzid != NULL && strcmp(ctx->ext_id[i], creds.authzid) == 0) ||
881  (creds.authzid == NULL)) ) {
882  // c2s connection
883  // creds.authzid == NULL condition is from XEP-0178 '=' auth reply
884 
885  // This should be freed by gsasl_finish() but I'm not sure
886  // node = authnid
887  len = value - ctx->ext_id[i];
888  node = (char *) malloc(sizeof(char) * (len + 1)); // + null termination
889  strncpy(node, ctx->ext_id[i], len);
890  node[len] = '\0'; // null terminate the string
891  // host = realm
892  len = strlen(value) - 1 + 1; // - the @ + null termination
893  host = (char *) malloc(sizeof(char) * (len));
894  strcpy(host, value + 1); // skip the @
895  gsasl_property_set(sd, GSASL_AUTHID, node);
896  gsasl_property_set(sd, GSASL_REALM, host);
897  return GSASL_OK;
898  }
899 
900  }
901  return GSASL_AUTHENTICATION_ERROR;
902 
903  default:
904  break;
905  }
906 
907  return GSASL_NO_CALLBACK;
908 }
909 
910 static void _sx_sasl_unload(sx_plugin_t p) {
911  _sx_sasl_t ctx = (_sx_sasl_t) p->private;
912  int i;
913 
914  if (ctx->gsasl_ctx != NULL) gsasl_done (ctx->gsasl_ctx);
915  if (ctx->appname != NULL) free(ctx->appname);
916  for (i = 0; i < SX_CONN_EXTERNAL_ID_MAX_COUNT; i++)
917  if(ctx->ext_id[i] != NULL)
918  free(ctx->ext_id[i]);
919  else
920  break;
921 
922  if (ctx != NULL) free(ctx);
923 }
924 
926 int sx_sasl_init(sx_env_t env, sx_plugin_t p, va_list args) {
927  char *appname;
929  void *cbarg;
930  _sx_sasl_t ctx;
931  int ret, i;
932 
933  _sx_debug(ZONE, "initialising sasl plugin");
934 
935  appname = va_arg(args, char *);
936  if(appname == NULL) {
937  _sx_debug(ZONE, "appname was NULL, failing");
938  return 1;
939  }
940 
941  cb = va_arg(args, sx_sasl_callback_t);
942  cbarg = va_arg(args, void *);
943 
944  ctx = (_sx_sasl_t) calloc(1, sizeof(struct _sx_sasl_st));
945 
946  ctx->appname = strdup(appname);
947  ctx->cb = cb;
948  ctx->cbarg = cbarg;
949  for (i = 0; i < SX_CONN_EXTERNAL_ID_MAX_COUNT; i++)
950  ctx->ext_id[i] = NULL;
951 
952  ret = gsasl_init(&ctx->gsasl_ctx);
953  if(ret != GSASL_OK) {
954  _sx_debug(ZONE, "couldn't initialize libgsasl (%d): %s", ret, gsasl_strerror (ret));
955  free(ctx);
956  return 1;
957  }
958 
959  gsasl_callback_set (ctx->gsasl_ctx, &_sx_sasl_gsasl_callback);
960 
961  _sx_debug(ZONE, "sasl context initialised");
962 
963  p->private = (void *) ctx;
964 
965  p->unload = _sx_sasl_unload;
966  p->wio = _sx_sasl_wio;
967  p->rio = _sx_sasl_rio;
968 
969  p->stream = _sx_sasl_stream;
972 
973  p->free = _sx_sasl_free;
974 
975  return 0;
976 }
977 
979 int sx_sasl_auth(sx_plugin_t p, sx_t s, char *appname, char *mech, char *user, char *pass) {
980  _sx_sasl_t ctx = (_sx_sasl_t) p->private;
981  Gsasl_session *sd;
982  char *buf = NULL, *out = NULL;
983  char hostname[256];
984  int ret, ns;
985  size_t buflen, outlen;
986  nad_t nad;
987 
988  assert((p != NULL));
989  assert((s != NULL));
990  assert((appname != NULL));
991  assert((mech != NULL));
992  assert((user != NULL));
993  assert((pass != NULL));
994 
995  if(s->type != type_CLIENT || s->state != state_STREAM) {
996  _sx_debug(ZONE, "need client in stream state for sasl auth");
997  return 1;
998  }
999 
1000  /* handshake start */
1001  ret = gsasl_client_start(ctx->gsasl_ctx, mech, &sd);
1002  if(ret != GSASL_OK) {
1003  _sx_debug(ZONE, "gsasl_client_start failed, not authing; (%d): %s", ret, gsasl_strerror(ret));
1004 
1005  return 1;
1006  }
1007 
1008  /* get hostname */
1009  hostname[0] = '\0';
1010  gethostname(hostname, 256);
1011  hostname[255] = '\0';
1012 
1013  /* set user data in session handle */
1014  gsasl_session_hook_set(sd, (void *) ctx);
1015  gsasl_property_set(sd, GSASL_AUTHID, user);
1016  gsasl_property_set(sd, GSASL_PASSWORD, pass);
1017  gsasl_property_set(sd, GSASL_SERVICE, appname);
1018  gsasl_property_set(sd, GSASL_HOSTNAME, hostname);
1019 
1020  /* handshake step */
1021  ret = gsasl_step(sd, NULL, 0, &out, &outlen);
1022  if(ret != GSASL_OK && ret != GSASL_NEEDS_MORE) {
1023  _sx_debug(ZONE, "gsasl_step failed, not authing; (%d): %s", ret, gsasl_strerror(ret));
1024 
1025  gsasl_finish(sd);
1026 
1027  return 1;
1028  }
1029 
1030  /* save userdata */
1031  s->plugin_data[p->index] = (void *) sd;
1032 
1033  /* in progress */
1034  _sx_debug(ZONE, "sending auth request to server, mech '%s': %.*s", mech, outlen, out);
1035 
1036  /* encode the challenge */
1037  ret = gsasl_base64_to(out, outlen, &buf, &buflen);
1038  if(ret != GSASL_OK) {
1039  _sx_debug(ZONE, "gsasl_base64_to failed, not authing; (%d): %s", ret, gsasl_strerror(ret));
1040 
1041  gsasl_finish(sd);
1042 
1043  if (out != NULL) free(out);
1044  return 1;
1045  }
1046  free(out);
1047 
1048  /* build the nad */
1049  nad = nad_new();
1050  ns = nad_add_namespace(nad, uri_SASL, NULL);
1051 
1052  nad_append_elem(nad, ns, "auth", 0);
1053  nad_append_attr(nad, -1, "mechanism", mech);
1054  if(buf != NULL) {
1055  nad_append_cdata(nad, buf, buflen, 1);
1056  free(buf);
1057  }
1058 
1059  /* its away */
1060  sx_nad_write(s, nad);
1061 
1062  return 0;
1063 }