jabberd2  2.3.6
sasl.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 
41 typedef struct _sx_sasl_sess_st {
45 
47 static nad_t _sx_sasl_success(sx_t s, const char *data, int dlen) {
48  nad_t nad;
49  int ns;
50 
51  nad = nad_new();
52  ns = nad_add_namespace(nad, uri_SASL, NULL);
53 
54  nad_append_elem(nad, ns, "success", 0);
55  if(data != NULL)
56  nad_append_cdata(nad, data, dlen, 1);
57 
58  return nad;
59 }
60 
62 static nad_t _sx_sasl_failure(sx_t s, const char *err) {
63  nad_t nad;
64  int ns;
65 
66  nad = nad_new();
67  ns = nad_add_namespace(nad, uri_SASL, NULL);
68 
69  nad_append_elem(nad, ns, "failure", 0);
70  if(err != NULL)
71  nad_append_elem(nad, ns, err, 1);
72 
73  return nad;
74 }
75 
77 static nad_t _sx_sasl_challenge(sx_t s, const char *data, int dlen) {
78  nad_t nad;
79  int ns;
80 
81  nad = nad_new();
82  ns = nad_add_namespace(nad, uri_SASL, NULL);
83 
84  nad_append_elem(nad, ns, "challenge", 0);
85  if(data != NULL)
86  nad_append_cdata(nad, data, dlen, 1);
87 
88  return nad;
89 }
90 
92 static nad_t _sx_sasl_response(sx_t s, const char *data, int dlen) {
93  nad_t nad;
94  int ns;
95 
96  nad = nad_new();
97  ns = nad_add_namespace(nad, uri_SASL, NULL);
98 
99  nad_append_elem(nad, ns, "response", 0);
100  if(data != NULL)
101  nad_append_cdata(nad, data, dlen, 1);
102 
103  return nad;
104 }
105 
108  nad_t nad;
109  int ns;
110 
111  nad = nad_new();
112  ns = nad_add_namespace(nad, uri_SASL, NULL);
113 
114  nad_append_elem(nad, ns, "abort", 0);
115 
116  return nad;
117 }
118 
119 static int _sx_sasl_wio(sx_t s, sx_plugin_t p, sx_buf_t buf) {
120  sx_error_t sxe;
121  size_t len;
122  int ret;
123  char *out;
124  Gsasl_session *sd = (Gsasl_session *) s->plugin_data[p->index];
125 
126  _sx_debug(ZONE, "doing sasl encode");
127 
128  /* encode the output */
129  ret = gsasl_encode(sd, buf->data, buf->len, &out, &len);
130  if (ret != GSASL_OK) {
131  _sx_debug(ZONE, "gsasl_encode failed (%d): %s", ret, gsasl_strerror (ret));
132  /* Fatal error */
133  _sx_gen_error(sxe, SX_ERR_AUTH, "SASL Stream encoding failed", (char*) gsasl_strerror (ret));
134  _sx_event(s, event_ERROR, (void *) &sxe);
135  return -1;
136  }
137 
138  /* replace the buffer */
139  _sx_buffer_set(buf, out, len, NULL);
140  free(out);
141 
142  _sx_debug(ZONE, "%d bytes encoded for sasl channel", buf->len);
143 
144  return 1;
145 }
146 
147 static int _sx_sasl_rio(sx_t s, sx_plugin_t p, sx_buf_t buf) {
148  sx_error_t sxe;
149  size_t len;
150  int ret;
151  char *out;
152  Gsasl_session *sd = (Gsasl_session *) s->plugin_data[p->index];
153 
154  _sx_debug(ZONE, "doing sasl decode");
155 
156  /* decode the input */
157  ret = gsasl_decode(sd, buf->data, buf->len, &out, &len);
158  if (ret != GSASL_OK) {
159  _sx_debug(ZONE, "gsasl_decode failed (%d): %s", ret, gsasl_strerror (ret));
160  /* Fatal error */
161  _sx_gen_error(sxe, SX_ERR_AUTH, "SASL Stream decoding failed", (char*) gsasl_strerror (ret));
162  _sx_event(s, event_ERROR, (void *) &sxe);
163  return -1;
164  }
165 
166  /* replace the buffer */
167  _sx_buffer_set(buf, out, len, NULL);
168  free(out);
169 
170  _sx_debug(ZONE, "%d bytes decoded from sasl channel", len);
171 
172  return 1;
173 }
174 
176 void _sx_sasl_open(sx_t s, Gsasl_session *sd) {
177  char *method, *authzid;
178  const char *realm = NULL;
179  struct sx_sasl_creds_st creds = {NULL, NULL, NULL, NULL};
180  _sx_sasl_sess_t sctx = gsasl_session_hook_get(sd);
181  _sx_sasl_t ctx = sctx->ctx;
182  const char *mechname = gsasl_mechanism_name (sd);
183 
184  /* get the method */
185  method = (char *) malloc(sizeof(char) * (strlen(mechname) + 6));
186  sprintf(method, "SASL/%s", mechname);
187 
188  /* and the authorization identifier */
189  creds.authzid = gsasl_property_fast(sd, GSASL_AUTHZID);
190  creds.authnid = gsasl_property_fast(sd, GSASL_AUTHID);
191  creds.realm = gsasl_property_fast(sd, GSASL_REALM);
192 
193  if(0 && ctx && ctx->cb) { /* not supported yet */
194  if((ctx->cb)(sx_sasl_cb_CHECK_AUTHZID, &creds, NULL, s, ctx->cbarg)!=sx_sasl_ret_OK) {
195  _sx_debug(ZONE, "stream authzid: %s verification failed, not advancing to auth state", creds.authzid);
196  free(method);
197  return;
198  }
199  } else if (NULL != gsasl_property_fast(sd, GSASL_GSSAPI_DISPLAY_NAME)) {
200  creds.authzid = strdup(gsasl_property_fast(sd, GSASL_GSSAPI_DISPLAY_NAME));
201  authzid = NULL;
202  } else {
203  /* override unchecked arbitrary authzid */
204  if(creds.realm && creds.realm[0] != '\0') {
205  realm = creds.realm;
206  } else {
207  realm = s->req_to;
208  }
209  authzid = (char *) malloc(sizeof(char) * (strlen(creds.authnid) + strlen(realm) + 2));
210  sprintf(authzid, "%s@%s", creds.authnid, realm);
211  creds.authzid = authzid;
212  }
213 
214  /* proceed stream to authenticated state */
215  sx_auth(s, method, creds.authzid);
216 
217  free(method);
218  if(authzid) free(authzid);
219 }
220 
222 static void _sx_sasl_stream(sx_t s, sx_plugin_t p) {
223  Gsasl_session *sd = (Gsasl_session *) s->plugin_data[p->index];
224 
225  /* do nothing the first time */
226  if(sd == NULL)
227  return;
228 
229  /* are we auth'd? */
230  if(NULL == gsasl_property_fast(sd, GSASL_AUTHID)) {
231  _sx_debug(ZONE, "not auth'd, not advancing to auth'd state yet");
232  return;
233  }
234 
235  /* otherwise, its auth time */
236  _sx_sasl_open(s, sd);
237 }
238 
239 static void _sx_sasl_features(sx_t s, sx_plugin_t p, nad_t nad) {
240  _sx_sasl_t ctx = (_sx_sasl_t) p->private;
241  Gsasl_session *sd = (Gsasl_session *) s->plugin_data[p->index];
242  int nmechs, ret;
243  char *mechs, *mech, *c;
244 
245  if(s->type != type_SERVER)
246  return;
247 
248  if(sd != NULL) {
249  _sx_debug(ZONE, "already auth'd, not offering sasl mechanisms");
250  return;
251  }
252 
253  if(!(s->flags & SX_SASL_OFFER)) {
254  _sx_debug(ZONE, "application didn't ask us to offer sasl, so we won't");
255  return;
256  }
257 
258 #ifdef HAVE_SSL
259  if((s->flags & SX_SSL_STARTTLS_REQUIRE) && s->ssf == 0) {
260  _sx_debug(ZONE, "ssl not established yet but the app requires it, not offering mechanisms");
261  return;
262  }
263 #endif
264 
265  _sx_debug(ZONE, "offering sasl mechanisms");
266 
267  ret = gsasl_server_mechlist(ctx->gsasl_ctx, &mechs);
268  if(ret != GSASL_OK) {
269  _sx_debug(ZONE, "gsasl_server_mechlist failed (%d): %s, not offering sasl for this conn", ret, gsasl_strerror (ret));
270  return;
271  }
272 
273  mech = mechs;
274  nmechs = 0;
275  while(mech != NULL) {
276  c = strchr(mech, ' ');
277  if(c != NULL)
278  *c = '\0';
279 
280  if ((ctx->cb)(sx_sasl_cb_CHECK_MECH, mech, NULL, s, ctx->cbarg)==sx_sasl_ret_OK) {
281  if (nmechs == 0) {
282  int ns = nad_add_namespace(nad, uri_SASL, NULL);
283  nad_append_elem(nad, ns, "mechanisms", 1);
284  }
285  _sx_debug(ZONE, "offering mechanism: %s", mech);
286 
287  nad_append_elem(nad, -1 /*ns*/, "mechanism", 2);
288  nad_append_cdata(nad, mech, strlen(mech), 3);
289  nmechs++;
290  }
291 
292  if(c == NULL)
293  mech = NULL;
294  else
295  mech = ++c;
296  }
297 
298  free(mechs);
299 }
300 
302 static void _sx_sasl_notify_success(sx_t s, void *arg) {
303  sx_plugin_t p = (sx_plugin_t) arg;
304 
305  _sx_chain_io_plugin(s, p);
306  _sx_debug(ZONE, "auth completed, resetting");
307 
308  _sx_reset(s);
309 
310  sx_server_init(s, s->flags);
311 }
312 
314 static void _sx_sasl_client_process(sx_t s, sx_plugin_t p, Gsasl_session *sd, const char *mech, const char *in, int inlen) {
315  _sx_sasl_t ctx = (_sx_sasl_t) p->private;
316  _sx_sasl_sess_t sctx = NULL;
317  char *buf = NULL, *out = NULL, *realm = NULL, **ext_id;
318  char hostname[256];
319  int ret;
320 #ifdef HAVE_SSL
321  int i;
322 #endif
323  size_t buflen, outlen;
324 
325  if(mech != NULL) {
326  _sx_debug(ZONE, "auth request from client (mechanism=%s)", mech);
327 
328  if(!gsasl_server_support_p(ctx->gsasl_ctx, mech)) {
329  _sx_debug(ZONE, "client requested mechanism (%s) that we didn't offer", mech);
331  return;
332  }
333 
334  /* startup */
335  ret = gsasl_server_start(ctx->gsasl_ctx, mech, &sd);
336  if(ret != GSASL_OK) {
337  _sx_debug(ZONE, "gsasl_server_start failed, no sasl for this conn; (%d): %s", ret, gsasl_strerror(ret));
339  return;
340  }
341 
342  /* get the realm */
343  if(ctx->cb != NULL)
344  (ctx->cb)(sx_sasl_cb_GET_REALM, NULL, (void **) &realm, s, ctx->cbarg);
345 
346  /* cleanup any existing session context */
347  sctx = gsasl_session_hook_get(sd);
348  if (sctx != NULL) free(sctx);
349 
350  /* allocate and initialize our per session context */
351  sctx = (_sx_sasl_sess_t) calloc(1, sizeof(struct _sx_sasl_sess_st));
352  sctx->s = s;
353  sctx->ctx = ctx;
354  gsasl_session_hook_set(sd, (void *) sctx);
355  gsasl_property_set(sd, GSASL_SERVICE, ctx->appname);
356  gsasl_property_set(sd, GSASL_REALM, realm);
357 
358  /* get hostname */
359  hostname[0] = '\0';
360  gethostname(hostname, 256);
361  hostname[255] = '\0';
362  gsasl_property_set(sd, GSASL_HOSTNAME, hostname);
363 
364  /* get EXTERNAL data from the ssl plugin */
365  ext_id = NULL;
366 #ifdef HAVE_SSL
367  for(i = 0; i < s->env->nplugins; i++)
368  if(s->env->plugins[i]->magic == SX_SSL_MAGIC && s->plugin_data[s->env->plugins[i]->index] != NULL)
369  ext_id = ((_sx_ssl_conn_t) s->plugin_data[s->env->plugins[i]->index])->external_id;
370  if (ext_id != NULL) {
371  //_sx_debug(ZONE, "sasl context ext id '%s'", ext_id);
372  /* if there is, store it for later */
373  for (i = 0; i < SX_CONN_EXTERNAL_ID_MAX_COUNT; i++)
374  if (ext_id[i] != NULL) {
375  ctx->ext_id[i] = strdup(ext_id[i]);
376  } else {
377  ctx->ext_id[i] = NULL;
378  break;
379  }
380  }
381 #endif
382 
383  _sx_debug(ZONE, "sasl context initialised for %d", s->tag);
384 
385  s->plugin_data[p->index] = (void *) sd;
386 
387  if(strcmp(mech, "ANONYMOUS") == 0) {
388  /*
389  * special case for SASL ANONYMOUS: ignore the initial
390  * response provided by the client and generate a random
391  * authid to use as the jid node for the user, as
392  * specified in XEP-0175
393  */
394  (ctx->cb)(sx_sasl_cb_GEN_AUTHZID, NULL, (void **)&out, s, ctx->cbarg);
395  buf = strdup(out);
396  buflen = strlen(buf);
397  } else if (strstr(in, "<") != NULL && strncmp(in, "=", strstr(in, "<") - in ) == 0) {
398  /* XXX The above check is hackish, but `in` is just weird */
399  /* This is a special case for SASL External c2s. See XEP-0178 */
400  _sx_debug(ZONE, "gsasl auth string is empty");
401  buf = strdup("");
402  buflen = strlen(buf);
403  } else {
404  /* decode and process */
405  ret = gsasl_base64_from(in, inlen, &buf, &buflen);
406  if (ret != GSASL_OK) {
407  _sx_debug(ZONE, "gsasl_base64_from failed, no sasl for this conn; (%d): %s", ret, gsasl_strerror(ret));
409  if(buf != NULL) free(buf);
410  return;
411  }
412  }
413 
414  ret = gsasl_step(sd, buf, buflen, &out, &outlen);
415  if(ret != GSASL_OK && ret != GSASL_NEEDS_MORE) {
416  _sx_debug(ZONE, "gsasl_step failed, no sasl for this conn; (%d): %s", ret, gsasl_strerror(ret));
418  if(out != NULL) free(out);
419  if(buf != NULL) free(buf);
420  return;
421  }
422  }
423 
424  else {
425  /* decode and process */
426  ret = gsasl_base64_from(in, inlen, &buf, &buflen);
427  if (ret != GSASL_OK) {
428  _sx_debug(ZONE, "gsasl_base64_from failed, no sasl for this conn; (%d): %s", ret, gsasl_strerror(ret));
430  return;
431  }
432 
433  if(!sd) {
434  _sx_debug(ZONE, "response send before auth request enabling mechanism (decoded: %.*s)", buflen, buf);
436  if(buf != NULL) free(buf);
437  return;
438  }
439  _sx_debug(ZONE, "response from client (decoded: %.*s)", buflen, buf);
440  ret = gsasl_step(sd, buf, buflen, &out, &outlen);
441  }
442 
443  if(buf != NULL) free(buf);
444 
445  /* auth completed */
446  if(ret == GSASL_OK) {
447  _sx_debug(ZONE, "sasl handshake completed");
448 
449  /* encode the leftover response */
450  ret = gsasl_base64_to(out, outlen, &buf, &buflen);
451  if (ret == GSASL_OK) {
452  /* send success */
453  _sx_nad_write(s, _sx_sasl_success(s, buf, buflen), 0);
454  free(buf);
455 
456  /* set a notify on the success nad buffer */
457  ((sx_buf_t) s->wbufq->front->data)->notify = _sx_sasl_notify_success;
458  ((sx_buf_t) s->wbufq->front->data)->notify_arg = (void *) p;
459  }
460  else {
461  _sx_debug(ZONE, "gsasl_base64_to failed, no sasl for this conn; (%d): %s", ret, gsasl_strerror(ret));
463  if(buf != NULL) free(buf);
464  }
465 
466  if(out != NULL) free(out);
467 
468  return;
469  }
470 
471  /* in progress */
472  if(ret == GSASL_NEEDS_MORE) {
473  _sx_debug(ZONE, "sasl handshake in progress (challenge: %.*s)", outlen, out);
474 
475  /* encode the challenge */
476  ret = gsasl_base64_to(out, outlen, &buf, &buflen);
477  if (ret == GSASL_OK) {
478  _sx_nad_write(s, _sx_sasl_challenge(s, buf, buflen), 0);
479  free(buf);
480  }
481  else {
482  _sx_debug(ZONE, "gsasl_base64_to failed, no sasl for this conn; (%d): %s", ret, gsasl_strerror(ret));
484  if(buf != NULL) free(buf);
485  }
486 
487  if(out != NULL) free(out);
488 
489  return;
490  }
491 
492  if(out != NULL) free(out);
493 
494  /* its over */
495  _sx_debug(ZONE, "sasl handshake failed; (%d): %s", ret, gsasl_strerror(ret));
496 
497  /* !!! TODO XXX check ret and flag error appropriately */
499 }
500 
502 static void _sx_sasl_server_process(sx_t s, sx_plugin_t p, Gsasl_session *sd, const char *in, int inlen) {
503  char *buf = NULL, *out = NULL;
504  size_t buflen, outlen;
505  int ret;
506 
507  _sx_debug(ZONE, "data from client");
508 
509  /* decode the response */
510  ret = gsasl_base64_from(in, inlen, &buf, &buflen);
511 
512  if (ret == GSASL_OK) {
513  _sx_debug(ZONE, "decoded data: %.*s", buflen, buf);
514 
515  /* process the data */
516  ret = gsasl_step(sd, buf, buflen, &out, &outlen);
517  if(buf != NULL) free(buf); buf = NULL;
518 
519  /* in progress */
520  if(ret == GSASL_OK || ret == GSASL_NEEDS_MORE) {
521  _sx_debug(ZONE, "sasl handshake in progress (response: %.*s)", outlen, out);
522 
523  /* encode the response */
524  ret = gsasl_base64_to(out, outlen, &buf, &buflen);
525 
526  if (ret == GSASL_OK) {
527  _sx_nad_write(s, _sx_sasl_response(s, buf, buflen), 0);
528  }
529 
530  if(out != NULL) free(out);
531  if(buf != NULL) free(buf);
532 
533  return;
534  }
535  }
536  if(out != NULL) free(out);
537  if(buf != NULL) free(buf);
538 
539  /* its over */
540  _sx_debug(ZONE, "sasl handshake aborted; (%d): %s", ret, gsasl_strerror(ret));
541 
542  _sx_nad_write(s, _sx_sasl_abort(s), 0);
543 }
544 
546 static int _sx_sasl_process(sx_t s, sx_plugin_t p, nad_t nad) {
547  Gsasl_session *sd = (Gsasl_session *) s->plugin_data[p->index];
548  int attr;
549  char mech[128];
550  sx_error_t sxe;
551  int flags;
552  char *ns = NULL, *to = NULL, *from = NULL, *version = NULL;
553 
554  /* only want sasl packets */
555  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)
556  return 1;
557 
558  /* quietly drop it if sasl is disabled, or if not ready */
559  if(s->state != state_STREAM) {
560  _sx_debug(ZONE, "not correct state for sasl, ignoring");
561  nad_free(nad);
562  return 0;
563  }
564 
565  /* packets from the client */
566  if(s->type == type_SERVER) {
567  if(!(s->flags & SX_SASL_OFFER)) {
568  _sx_debug(ZONE, "they tried to do sasl, but we never offered it, ignoring");
569  nad_free(nad);
570  return 0;
571  }
572 
573 #ifdef HAVE_SSL
574  if((s->flags & SX_SSL_STARTTLS_REQUIRE) && s->ssf == 0) {
575  _sx_debug(ZONE, "they tried to do sasl, but they have to do starttls first, ignoring");
576  nad_free(nad);
577  return 0;
578  }
579 #endif
580 
581  /* auth */
582  if(NAD_ENAME_L(nad, 0) == 4 && strncmp("auth", NAD_ENAME(nad, 0), NAD_ENAME_L(nad, 0)) == 0) {
583  /* require mechanism */
584  if((attr = nad_find_attr(nad, 0, -1, "mechanism", NULL)) < 0) {
586  nad_free(nad);
587  return 0;
588  }
589 
590  /* extract */
591  snprintf(mech, 127, "%.*s", NAD_AVAL_L(nad, attr), NAD_AVAL(nad, attr));
592 
593  /* go */
594  _sx_sasl_client_process(s, p, sd, mech, NAD_CDATA(nad, 0), NAD_CDATA_L(nad, 0));
595 
596  nad_free(nad);
597  return 0;
598  }
599 
600  /* response */
601  else if(NAD_ENAME_L(nad, 0) == 8 && strncmp("response", NAD_ENAME(nad, 0), NAD_ENAME_L(nad, 0)) == 0) {
602  /* process it */
603  _sx_sasl_client_process(s, p, sd, NULL, NAD_CDATA(nad, 0), NAD_CDATA_L(nad, 0));
604 
605  nad_free(nad);
606  return 0;
607  }
608 
609  /* abort */
610  else if(NAD_ENAME_L(nad, 0) == 5 && strncmp("abort", NAD_ENAME(nad, 0), NAD_ENAME_L(nad, 0)) == 0) {
611  _sx_debug(ZONE, "sasl handshake aborted");
612 
614 
615  nad_free(nad);
616  return 0;
617  }
618  }
619 
620  /* packets from the server */
621  else if(s->type == type_CLIENT) {
622  if(sd == NULL) {
623  _sx_debug(ZONE, "got sasl client packets, but they never started sasl, ignoring");
624  nad_free(nad);
625  return 0;
626  }
627 
628  /* challenge */
629  if(NAD_ENAME_L(nad, 0) == 9 && strncmp("challenge", NAD_ENAME(nad, 0), NAD_ENAME_L(nad, 0)) == 0) {
630  /* process it */
631  _sx_sasl_server_process(s, p, sd, NAD_CDATA(nad, 0), NAD_CDATA_L(nad, 0));
632 
633  nad_free(nad);
634  return 0;
635  }
636 
637  /* success */
638  else if(NAD_ENAME_L(nad, 0) == 7 && strncmp("success", NAD_ENAME(nad, 0), NAD_ENAME_L(nad, 0)) == 0) {
639  _sx_debug(ZONE, "sasl handshake completed, resetting");
640  nad_free(nad);
641 
642  /* save interesting bits */
643  flags = s->flags;
644 
645  if(s->ns != NULL) ns = strdup(s->ns);
646 
647  if(s->req_to != NULL) to = strdup(s->req_to);
648  if(s->req_from != NULL) from = strdup(s->req_from);
649  if(s->req_version != NULL) version = strdup(s->req_version);
650 
651  /* reset state */
652  _sx_reset(s);
653 
654  _sx_debug(ZONE, "restarting stream with sasl layer established");
655 
656  /* second time round */
657  sx_client_init(s, flags, ns, to, from, version);
658 
659  /* free bits */
660  if(ns != NULL) free(ns);
661  if(to != NULL) free(to);
662  if(from != NULL) free(from);
663  if(version != NULL) free(version);
664 
665  return 0;
666  }
667 
668  /* failure */
669  else if(NAD_ENAME_L(nad, 0) == 7 && strncmp("failure", NAD_ENAME(nad, 0), NAD_ENAME_L(nad, 0)) == 0) {
670  /* fire the error */
671  _sx_gen_error(sxe, SX_ERR_AUTH, "Authentication failed", NULL);
672  _sx_event(s, event_ERROR, (void *) &sxe);
673 
674  /* cleanup */
675  gsasl_finish(sd);
676 
677  s->plugin_data[p->index] = NULL;
678 
679  nad_free(nad);
680  return 0;
681  }
682  }
683 
684  /* invalid sasl command, quietly drop it */
685  _sx_debug(ZONE, "unknown sasl command '%.*s', ignoring", NAD_ENAME_L(nad, 0), NAD_ENAME(nad, 0));
686 
687  nad_free(nad);
688  return 0;
689 }
690 
692 static void _sx_sasl_free(sx_t s, sx_plugin_t p) {
693  Gsasl_session *sd = (Gsasl_session *) s->plugin_data[p->index];
694  _sx_sasl_sess_t sctx;
695 
696  if(sd == NULL)
697  return;
698 
699  _sx_debug(ZONE, "cleaning up conn state");
700 
701  /* we need to clean up our per session context but keep sasl ctx */
702  sctx = gsasl_session_hook_get(sd);
703  if (sctx != NULL){
704  free(sctx);
705  gsasl_session_hook_set(sd, (void *) NULL);
706  }
707 
708  gsasl_finish(sd);
709  s->plugin_data[p->index] = NULL;
710 }
711 
712 static int _sx_sasl_gsasl_callback(Gsasl *gsasl_ctx, Gsasl_session *sd, Gsasl_property prop) {
713  _sx_sasl_sess_t sctx = gsasl_session_hook_get(sd);
714  _sx_sasl_t ctx = NULL;
715  struct sx_sasl_creds_st creds = {NULL, NULL, NULL, NULL};
716  char *value, *node, *host;
717  int len, i;
718 
719  /*
720  * session hook data is not always available while its being set up,
721  * also not needed in many of the cases below.
722  */
723  if(sctx != NULL) {
724  ctx = sctx->ctx;
725  }
726 
727  _sx_debug(ZONE, "in _sx_sasl_gsasl_callback, property: %d", prop);
728 
729  switch(prop) {
730  case GSASL_PASSWORD:
731  /* GSASL_AUTHID, GSASL_AUTHZID, GSASL_REALM */
732  assert((ctx->cb != NULL));
733  creds.authnid = gsasl_property_fast(sd, GSASL_AUTHID);
734  creds.realm = gsasl_property_fast(sd, GSASL_REALM);
735  if(!creds.authnid) return GSASL_NO_AUTHID;
736  if(!creds.realm) return GSASL_NO_AUTHZID;
737  if((ctx->cb)(sx_sasl_cb_GET_PASS, &creds, (void **)&value, sctx->s, ctx->cbarg) == sx_sasl_ret_OK) {
738  gsasl_property_set(sd, GSASL_PASSWORD, value);
739  }
740  return GSASL_NEEDS_MORE;
741 
742  case GSASL_SERVICE:
743  gsasl_property_set(sd, GSASL_SERVICE, "xmpp");
744  return GSASL_OK;
745 
746  case GSASL_HOSTNAME:
747  {
748  char hostname[256];
749  /* get hostname */
750  hostname[0] = '\0';
751  gethostname(hostname, 256);
752  hostname[255] = '\0';
753 
754  gsasl_property_set(sd, GSASL_HOSTNAME, hostname);
755  }
756  return GSASL_OK;
757 
758  case GSASL_VALIDATE_SIMPLE:
759  /* GSASL_AUTHID, GSASL_AUTHZID, GSASL_PASSWORD */
760  assert((ctx->cb != NULL));
761  creds.authnid = gsasl_property_fast(sd, GSASL_AUTHID);
762  creds.realm = gsasl_property_fast(sd, GSASL_REALM);
763  creds.pass = gsasl_property_fast(sd, GSASL_PASSWORD);
764  if(!creds.authnid) return GSASL_NO_AUTHID;
765  if(!creds.realm) return GSASL_NO_AUTHZID;
766  if(!creds.pass) return GSASL_NO_PASSWORD;
767  if((ctx->cb)(sx_sasl_cb_CHECK_PASS, &creds, NULL, sctx->s, ctx->cbarg) == sx_sasl_ret_OK)
768  return GSASL_OK;
769  else
770  return GSASL_AUTHENTICATION_ERROR;
771 
772  case GSASL_VALIDATE_GSSAPI:
773  /* GSASL_AUTHZID, GSASL_GSSAPI_DISPLAY_NAME */
774  creds.authnid = gsasl_property_fast(sd, GSASL_GSSAPI_DISPLAY_NAME);
775  if(!creds.authnid) return GSASL_NO_AUTHID;
776  creds.authzid = gsasl_property_fast(sd, GSASL_AUTHZID);
777  if(!creds.authzid) return GSASL_NO_AUTHZID;
778  gsasl_property_set(sd, GSASL_AUTHID, creds.authnid);
779  return GSASL_OK;
780 
781  case GSASL_VALIDATE_ANONYMOUS:
782  /* GSASL_ANONYMOUS_TOKEN */
783  creds.authnid = gsasl_property_fast(sd, GSASL_ANONYMOUS_TOKEN);
784  if(!creds.authnid) return GSASL_NO_ANONYMOUS_TOKEN;
785  /* set token as authid for later use */
786  gsasl_property_set(sd, GSASL_AUTHID, creds.authnid);
787  return GSASL_OK;
788 
789  case GSASL_VALIDATE_EXTERNAL:
790  /* GSASL_AUTHID */
791  creds.authzid = gsasl_property_fast(sd, GSASL_AUTHZID);
792  _sx_debug(ZONE, "sasl external");
793  _sx_debug(ZONE, "sasl creds.authzid is '%s'", creds.authzid);
794 
795  for (i = 0; i < SX_CONN_EXTERNAL_ID_MAX_COUNT; i++) {
796  if (ctx->ext_id[i] == NULL)
797  break;
798  _sx_debug(ZONE, "sasl ext_id(%d) is '%s'", i, ctx->ext_id[i]);
799  /* XXX hackish.. detect c2s by existance of @ */
800  value = strstr(ctx->ext_id[i], "@");
801 
802  if(value == NULL && creds.authzid != NULL && strcmp(ctx->ext_id[i], creds.authzid) == 0) {
803  // s2s connection and it's valid
804  /* TODO Handle wildcards and other thigs from XEP-0178 */
805  _sx_debug(ZONE, "sasl ctx->ext_id doesn't have '@' in it. Assuming s2s");
806  return GSASL_OK;
807  }
808  if(value != NULL &&
809  ((creds.authzid != NULL && strcmp(ctx->ext_id[i], creds.authzid) == 0) ||
810  (creds.authzid == NULL)) ) {
811  // c2s connection
812  // creds.authzid == NULL condition is from XEP-0178 '=' auth reply
813 
814  // This should be freed by gsasl_finish() but I'm not sure
815  // node = authnid
816  len = value - ctx->ext_id[i];
817  node = (char *) malloc(sizeof(char) * (len + 1)); // + null termination
818  strncpy(node, ctx->ext_id[i], len);
819  node[len] = '\0'; // null terminate the string
820  // host = realm
821  len = strlen(value) - 1 + 1; // - the @ + null termination
822  host = (char *) malloc(sizeof(char) * (len));
823  strcpy(host, value + 1); // skip the @
824  gsasl_property_set(sd, GSASL_AUTHID, node);
825  gsasl_property_set(sd, GSASL_REALM, host);
826  return GSASL_OK;
827  }
828 
829  }
830  return GSASL_AUTHENTICATION_ERROR;
831 
832  default:
833  break;
834  }
835 
836  return GSASL_NO_CALLBACK;
837 }
838 
839 static void _sx_sasl_unload(sx_plugin_t p) {
840  _sx_sasl_t ctx = (_sx_sasl_t) p->private;
841  int i;
842 
843  if (ctx->gsasl_ctx != NULL) gsasl_done (ctx->gsasl_ctx);
844  if (ctx->appname != NULL) free(ctx->appname);
845  for (i = 0; i < SX_CONN_EXTERNAL_ID_MAX_COUNT; i++)
846  if(ctx->ext_id[i] != NULL)
847  free(ctx->ext_id[i]);
848  else
849  break;
850 
851  if (ctx != NULL) free(ctx);
852 }
853 
855 int sx_sasl_init(sx_env_t env, sx_plugin_t p, va_list args) {
856  const char *appname;
858  void *cbarg;
859  _sx_sasl_t ctx;
860  int ret, i;
861 
862  _sx_debug(ZONE, "initialising sasl plugin");
863 
864  appname = va_arg(args, const char *);
865  if(appname == NULL) {
866  _sx_debug(ZONE, "appname was NULL, failing");
867  return 1;
868  }
869 
870  cb = va_arg(args, sx_sasl_callback_t);
871  cbarg = va_arg(args, void *);
872 
873  ctx = (_sx_sasl_t) calloc(1, sizeof(struct _sx_sasl_st));
874 
875  ctx->appname = strdup(appname);
876  ctx->cb = cb;
877  ctx->cbarg = cbarg;
878  for (i = 0; i < SX_CONN_EXTERNAL_ID_MAX_COUNT; i++)
879  ctx->ext_id[i] = NULL;
880 
881  ret = gsasl_init(&ctx->gsasl_ctx);
882  if(ret != GSASL_OK) {
883  _sx_debug(ZONE, "couldn't initialize libgsasl (%d): %s", ret, gsasl_strerror (ret));
884  free(ctx);
885  return 1;
886  }
887 
888  gsasl_callback_set (ctx->gsasl_ctx, &_sx_sasl_gsasl_callback);
889 
890  _sx_debug(ZONE, "sasl context initialised");
891 
892  p->private = (void *) ctx;
893 
894  p->unload = _sx_sasl_unload;
895  p->wio = _sx_sasl_wio;
896  p->rio = _sx_sasl_rio;
897 
898  p->stream = _sx_sasl_stream;
901 
902  p->free = _sx_sasl_free;
903 
904  return 0;
905 }
906 
908 int sx_sasl_auth(sx_plugin_t p, sx_t s, const char *appname, const char *mech, const char *user, const char *pass) {
909  _sx_sasl_t ctx = (_sx_sasl_t) p->private;
910  _sx_sasl_sess_t sctx = NULL;
911  Gsasl_session *sd;
912  char *buf = NULL, *out = NULL;
913  char hostname[256];
914  int ret, ns;
915  size_t buflen, outlen;
916  nad_t nad;
917 
918  assert((p != NULL));
919  assert((s != NULL));
920  assert((appname != NULL));
921  assert((mech != NULL));
922  assert((user != NULL));
923  assert((pass != NULL));
924 
925  if(s->type != type_CLIENT || s->state != state_STREAM) {
926  _sx_debug(ZONE, "need client in stream state for sasl auth");
927  return 1;
928  }
929 
930  /* handshake start */
931  ret = gsasl_client_start(ctx->gsasl_ctx, mech, &sd);
932  if(ret != GSASL_OK) {
933  _sx_debug(ZONE, "gsasl_client_start failed, not authing; (%d): %s", ret, gsasl_strerror(ret));
934 
935  return 1;
936  }
937 
938  /* get hostname */
939  hostname[0] = '\0';
940  gethostname(hostname, 256);
941  hostname[255] = '\0';
942 
943  /* cleanup any existing session context */
944  sctx = gsasl_session_hook_get(sd);
945  if (sctx != NULL) free(sctx);
946 
947  /* allocate and initialize our per session context */
948  sctx = (_sx_sasl_sess_t) calloc(1, sizeof(struct _sx_sasl_sess_st));
949  sctx->s = s;
950  sctx->ctx = ctx;
951 
952  /* set user data in session handle */
953  gsasl_session_hook_set(sd, (void *) sctx);
954  gsasl_property_set(sd, GSASL_AUTHID, user);
955  gsasl_property_set(sd, GSASL_PASSWORD, pass);
956  gsasl_property_set(sd, GSASL_SERVICE, appname);
957  gsasl_property_set(sd, GSASL_HOSTNAME, hostname);
958 
959  /* handshake step */
960  ret = gsasl_step(sd, NULL, 0, &out, &outlen);
961  if(ret != GSASL_OK && ret != GSASL_NEEDS_MORE) {
962  _sx_debug(ZONE, "gsasl_step failed, not authing; (%d): %s", ret, gsasl_strerror(ret));
963 
964  gsasl_finish(sd);
965 
966  return 1;
967  }
968 
969  /* save userdata */
970  s->plugin_data[p->index] = (void *) sd;
971 
972  /* in progress */
973  _sx_debug(ZONE, "sending auth request to server, mech '%s': %.*s", mech, outlen, out);
974 
975  /* encode the challenge */
976  ret = gsasl_base64_to(out, outlen, &buf, &buflen);
977  if(ret != GSASL_OK) {
978  _sx_debug(ZONE, "gsasl_base64_to failed, not authing; (%d): %s", ret, gsasl_strerror(ret));
979 
980  gsasl_finish(sd);
981 
982  if (out != NULL) free(out);
983  return 1;
984  }
985  free(out);
986 
987  /* build the nad */
988  nad = nad_new();
989  ns = nad_add_namespace(nad, uri_SASL, NULL);
990 
991  nad_append_elem(nad, ns, "auth", 0);
992  nad_append_attr(nad, -1, "mechanism", mech);
993  if(buf != NULL) {
994  nad_append_cdata(nad, buf, buflen, 1);
995  free(buf);
996  }
997 
998  /* its away */
999  sx_nad_write(s, nad);
1000 
1001  return 0;
1002 }
Definition: nad.h:93
void(* free)(sx_t s, sx_plugin_t p)
Definition: sx.h:354
nad_t nad_new(void)
create a new nad
Definition: nad.c:125
Definition: sx.h:113
int nad_append_attr(nad_t nad, int ns, const char *name, const char *val)
attach new attr to the last elem
Definition: nad.c:729
#define sx_sasl_cb_CHECK_MECH
Definition: plugins.h:115
void * cbarg
Definition: sasl.c:35
_sx_state_t state
Definition: sx.h:316
#define _sx_event(s, e, data)
Definition: sx.h:392
#define NAD_CDATA_L(N, E)
Definition: nad.h:186
unsigned int flags
Definition: sx.h:276
#define sx_nad_write(s, nad)
Definition: sx.h:167
static nad_t _sx_sasl_response(sx_t s, const char *data, int dlen)
utility: generate a response nad
Definition: sasl.c:92
static nad_t _sx_sasl_success(sx_t s, const char *data, int dlen)
utility: generate a success nad
Definition: sasl.c:47
int sx_sasl_init(sx_env_t env, sx_plugin_t p, va_list args)
args: appname, callback, cb arg
Definition: sasl.c:855
static void _sx_sasl_notify_success(sx_t s, void *arg)
auth done, restart the stream
Definition: sasl.c:302
const char * req_to
Definition: sx.h:282
jqueue_t wbufq
Definition: sx.h:301
static void _sx_sasl_server_process(sx_t s, sx_plugin_t p, Gsasl_session *sd, const char *in, int inlen)
process handshake packets from the server
Definition: sasl.c:502
void(* unload)(sx_plugin_t p)
Definition: sx.h:375
Definition: sx.h:65
void nad_append_cdata(nad_t nad, const char *cdata, int len, int depth)
append new cdata to the last elem
Definition: nad.c:737
void(* features)(sx_t s, sx_plugin_t p, nad_t nad)
Definition: sx.h:370
an environment
Definition: sx.h:379
error info for event_ERROR
Definition: sx.h:99
#define _sasl_err_ABORTED
Definition: sasl.h:25
void * data
Definition: util.h:315
a plugin
Definition: sx.h:344
int nad_add_namespace(nad_t nad, const char *uri, const char *prefix)
bring a new namespace into scope
Definition: nad.c:762
#define _sasl_err_MALFORMED_REQUEST
Definition: sasl.h:29
void sx_server_init(sx_t s, unsigned int flags)
Definition: server.c:248
#define NAD_ENAME(N, E)
Definition: nad.h:183
void _sx_chain_io_plugin(sx_t s, sx_plugin_t p)
Definition: chain.c:25
struct _sx_sasl_sess_st * _sx_sasl_sess_t
our sasl per session context
char * ext_id[SX_CONN_EXTERNAL_ID_MAX_COUNT]
Definition: sasl.c:37
_jqueue_node_t front
Definition: util.h:327
int nad_append_elem(nad_t nad, int ns, const char *name, int depth)
create a new elem on the list
Definition: nad.c:695
const char * ns
Definition: sx.h:279
void nad_free(nad_t nad)
free that nad
Definition: nad.c:178
const char * authnid
Definition: plugins.h:126
#define sx_sasl_ret_OK
Definition: plugins.h:118
int sx_sasl_auth(sx_plugin_t p, sx_t s, const char *appname, const char *mech, const char *user, const char *pass)
kick off the auth handshake
Definition: sasl.c:908
#define sx_sasl_cb_CHECK_AUTHZID
Definition: plugins.h:113
#define SX_ERR_AUTH
Definition: sx.h:95
holds the state for a single stream
Definition: sx.h:253
#define SX_SSL_MAGIC
magic numbers, so plugins can find each other
Definition: plugins.h:37
char * data
Definition: sx.h:114
static nad_t _sx_sasl_abort(sx_t s)
utility: generate an abort nad
Definition: sasl.c:107
void sx_client_init(sx_t s, unsigned int flags, const char *ns, const char *to, const char *from, const char *version)
Definition: client.c:111
#define NAD_ENAME_L(N, E)
Definition: nad.h:184
#define NAD_NURI_L(N, NS)
Definition: nad.h:192
static void _sx_sasl_free(sx_t s, sx_plugin_t p)
cleanup
Definition: sasl.c:692
Definition: sx.h:82
int(* sx_sasl_callback_t)(int cb, void *arg, void **res, sx_t s, void *cbarg)
the callback function
Definition: plugins.h:107
static void _sx_sasl_stream(sx_t s, sx_plugin_t p)
make the stream authenticated second time round
Definition: sasl.c:222
void(* stream)(sx_t s, sx_plugin_t p)
Definition: sx.h:368
#define NAD_AVAL_L(N, A)
Definition: nad.h:190
const char * authzid
Definition: plugins.h:128
#define uri_SASL
Definition: uri.h:41
#define _sx_debug
Definition: sx.h:405
struct _sx_plugin_st * sx_plugin_t
Definition: sx.h:53
#define SX_SSL_STARTTLS_REQUIRE
Definition: plugins.h:27
const char * realm
Definition: plugins.h:127
#define NAD_AVAL(N, A)
Definition: nad.h:189
struct _sx_buf_st * sx_buf_t
utility: buffer
Definition: sx.h:112
#define _sasl_err_INCORRECT_ENCODING
Definition: sasl.h:26
static nad_t _sx_sasl_failure(sx_t s, const char *err)
utility: generate a failure nad
Definition: sasl.c:62
#define SX_SASL_OFFER
Definition: plugins.h:29
_sx_type_t type
Definition: sx.h:273
our sasl per session context
Definition: sasl.c:41
void _sx_reset(sx_t s)
utility; reset stream state
Definition: sx.c:154
const char * req_version
Definition: sx.h:284
const char * req_from
Definition: sx.h:283
Definition: sx.h:83
#define sx_sasl_cb_GET_REALM
Definition: plugins.h:110
_sx_sasl_t ctx
Definition: sasl.c:43
int ssf
Definition: sx.h:340
static int _sx_sasl_wio(sx_t s, sx_plugin_t p, sx_buf_t buf)
Definition: sasl.c:119
static void _sx_sasl_client_process(sx_t s, sx_plugin_t p, Gsasl_session *sd, const char *mech, const char *in, int inlen)
process handshake packets from the client
Definition: sasl.c:314
unsigned int len
Definition: sx.h:115
void * private
Definition: sx.h:351
static nad_t _sx_sasl_challenge(sx_t s, const char *data, int dlen)
utility: generate a challenge nad
Definition: sasl.c:77
int(* wio)(sx_t s, sx_plugin_t p, sx_buf_t buf)
Definition: sx.h:360
static void _sx_sasl_features(sx_t s, sx_plugin_t p, nad_t nad)
Definition: sasl.c:239
#define _sasl_err_MECH_TOO_WEAK
Definition: sasl.h:30
#define NAD_CDATA(N, E)
Definition: nad.h:185
void _sx_sasl_open(sx_t s, Gsasl_session *sd)
move the stream to the auth state
Definition: sasl.c:176
#define _sasl_err_TEMPORARY_FAILURE
Definition: sasl.h:32
#define _sx_gen_error(e, c, g, s)
helper macro to populate this struct
Definition: sx.h:106
#define ZONE
Definition: mio_impl.h:76
Gsasl * gsasl_ctx
Definition: sasl.c:32
#define NAD_NURI(N, NS)
Definition: nad.h:191
void _sx_buffer_set(sx_buf_t buf, char *newdata, int newlength, char *newheap)
utility: reset a sx_buf_t's contents.
Definition: sx.c:299
static void _sx_sasl_unload(sx_plugin_t p)
Definition: sasl.c:839
int(* process)(sx_t s, sx_plugin_t p, nad_t nad)
Definition: sx.h:373
int(* rio)(sx_t s, sx_plugin_t p, sx_buf_t buf)
Definition: sx.h:361
#define sx_sasl_cb_GEN_AUTHZID
Definition: plugins.h:114
char * appname
Definition: sasl.c:31
int nad_find_attr(nad_t nad, int elem, int ns, const char *name, const char *val)
get a matching attr on this elem, both name and optional val
Definition: nad.c:235
static int _sx_sasl_gsasl_callback(Gsasl *gsasl_ctx, Gsasl_session *sd, Gsasl_property prop)
Definition: sasl.c:712
static int _sx_sasl_process(sx_t s, sx_plugin_t p, nad_t nad)
main nad processor
Definition: sasl.c:546
const char * pass
Definition: plugins.h:129
int index
Definition: sx.h:349
int _sx_nad_write(sx_t s, nad_t nad, int elem)
send a new nad out
Definition: io.c:403
void sx_auth(sx_t s, const char *auth_method, const char *auth_id)
force advance into auth state
Definition: sx.c:141
#define SX_CONN_EXTERNAL_ID_MAX_COUNT
Definition: plugins.h:49
our sasl application context
Definition: sasl.c:30
static int _sx_sasl_rio(sx_t s, sx_plugin_t p, sx_buf_t buf)
Definition: sasl.c:147
void ** plugin_data
Definition: sx.h:327
#define sx_sasl_cb_CHECK_PASS
Definition: plugins.h:112
#define NAD_ENS(N, E)
Definition: nad.h:196
sx_sasl_callback_t cb
Definition: sasl.c:34
struct _sx_sasl_st * _sx_sasl_t
our sasl application context
#define sx_sasl_cb_GET_PASS
Definition: plugins.h:111
#define _sasl_err_INVALID_MECHANISM
Definition: sasl.h:28