jabberd2  2.5.0
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  assert(ctx);
326  assert(ctx->cb);
327 
328  if(mech != NULL) {
329  _sx_debug(ZONE, "auth request from client (mechanism=%s)", mech);
330 
331  if(!gsasl_server_support_p(ctx->gsasl_ctx, mech)) {
332  _sx_debug(ZONE, "client requested mechanism (%s) that we didn't offer", mech);
334  return;
335  }
336 
337  /* startup */
338  ret = gsasl_server_start(ctx->gsasl_ctx, mech, &sd);
339  if(ret != GSASL_OK) {
340  _sx_debug(ZONE, "gsasl_server_start failed, no sasl for this conn; (%d): %s", ret, gsasl_strerror(ret));
342  return;
343  }
344 
345  /* get the realm */
346  (ctx->cb)(sx_sasl_cb_GET_REALM, NULL, (void **) &realm, s, ctx->cbarg);
347 
348  /* cleanup any existing session context */
349  sctx = gsasl_session_hook_get(sd);
350  if (sctx != NULL) free(sctx);
351 
352  /* allocate and initialize our per session context */
353  sctx = (_sx_sasl_sess_t) calloc(1, sizeof(struct _sx_sasl_sess_st));
354  sctx->s = s;
355  sctx->ctx = ctx;
356  gsasl_session_hook_set(sd, (void *) sctx);
357  gsasl_property_set(sd, GSASL_SERVICE, ctx->appname);
358  gsasl_property_set(sd, GSASL_REALM, realm);
359 
360  /* get hostname */
361  hostname[0] = '\0';
362  gethostname(hostname, 256);
363  hostname[255] = '\0';
364  gsasl_property_set(sd, GSASL_HOSTNAME, hostname);
365 
366  /* get EXTERNAL data from the ssl plugin */
367  ext_id = NULL;
368 #ifdef HAVE_SSL
369  for(i = 0; i < s->env->nplugins; i++)
370  if(s->env->plugins[i]->magic == SX_SSL_MAGIC && s->plugin_data[s->env->plugins[i]->index] != NULL)
371  ext_id = ((_sx_ssl_conn_t) s->plugin_data[s->env->plugins[i]->index])->external_id;
372  if (ext_id != NULL) {
373  //_sx_debug(ZONE, "sasl context ext id '%s'", ext_id);
374  /* if there is, store it for later */
375  for (i = 0; i < SX_CONN_EXTERNAL_ID_MAX_COUNT; i++)
376  if (ext_id[i] != NULL) {
377  ctx->ext_id[i] = strdup(ext_id[i]);
378  } else {
379  ctx->ext_id[i] = NULL;
380  break;
381  }
382  }
383 #endif
384 
385  _sx_debug(ZONE, "sasl context initialised for %d", s->tag);
386 
387  s->plugin_data[p->index] = (void *) sd;
388 
389  if(strcmp(mech, "ANONYMOUS") == 0) {
390  /*
391  * special case for SASL ANONYMOUS: ignore the initial
392  * response provided by the client and generate a random
393  * authid to use as the jid node for the user, as
394  * specified in XEP-0175
395  */
396  (ctx->cb)(sx_sasl_cb_GEN_AUTHZID, NULL, (void **)&out, s, ctx->cbarg);
397  buf = strdup(out);
398  buflen = strlen(buf);
399  } else if (strstr(in, "<") != NULL && strncmp(in, "=", strstr(in, "<") - in ) == 0) {
400  /* XXX The above check is hackish, but `in` is just weird */
401  /* This is a special case for SASL External c2s. See XEP-0178 */
402  _sx_debug(ZONE, "gsasl auth string is empty");
403  buf = strdup("");
404  buflen = strlen(buf);
405  } else {
406  /* decode and process */
407  ret = gsasl_base64_from(in, inlen, &buf, &buflen);
408  if (ret != GSASL_OK) {
409  _sx_debug(ZONE, "gsasl_base64_from failed, no sasl for this conn; (%d): %s", ret, gsasl_strerror(ret));
411  if(buf != NULL) free(buf);
412  return;
413  }
414  }
415 
416  ret = gsasl_step(sd, buf, buflen, &out, &outlen);
417  if(ret != GSASL_OK && ret != GSASL_NEEDS_MORE) {
418  _sx_debug(ZONE, "gsasl_step failed, no sasl for this conn; (%d): %s", ret, gsasl_strerror(ret));
420  if(out != NULL) free(out);
421  if(buf != NULL) free(buf);
422  return;
423  }
424  }
425 
426  else {
427  /* decode and process */
428  ret = gsasl_base64_from(in, inlen, &buf, &buflen);
429  if (ret != GSASL_OK) {
430  _sx_debug(ZONE, "gsasl_base64_from failed, no sasl for this conn; (%d): %s", ret, gsasl_strerror(ret));
432  return;
433  }
434 
435  if(!sd) {
436  _sx_debug(ZONE, "response send before auth request enabling mechanism (decoded: %.*s)", buflen, buf);
438  if(buf != NULL) free(buf);
439  return;
440  }
441  _sx_debug(ZONE, "response from client (decoded: %.*s)", buflen, buf);
442  ret = gsasl_step(sd, buf, buflen, &out, &outlen);
443  }
444 
445  if(buf != NULL) free(buf);
446 
447  /* auth completed */
448  if(ret == GSASL_OK) {
449  _sx_debug(ZONE, "sasl handshake completed");
450 
451  /* encode the leftover response */
452  ret = gsasl_base64_to(out, outlen, &buf, &buflen);
453  if (ret == GSASL_OK) {
454  /* send success */
455  _sx_nad_write(s, _sx_sasl_success(s, buf, buflen), 0);
456  free(buf);
457 
458  /* set a notify on the success nad buffer */
459  ((sx_buf_t) s->wbufq->front->data)->notify = _sx_sasl_notify_success;
460  ((sx_buf_t) s->wbufq->front->data)->notify_arg = (void *) p;
461  }
462  else {
463  _sx_debug(ZONE, "gsasl_base64_to failed, no sasl for this conn; (%d): %s", ret, gsasl_strerror(ret));
465  if(buf != NULL) free(buf);
466  }
467 
468  if(out != NULL) free(out);
469 
470  return;
471  }
472 
473  /* in progress */
474  if(ret == GSASL_NEEDS_MORE) {
475  _sx_debug(ZONE, "sasl handshake in progress (challenge: %.*s)", outlen, out);
476 
477  /* encode the challenge */
478  ret = gsasl_base64_to(out, outlen, &buf, &buflen);
479  if (ret == GSASL_OK) {
480  _sx_nad_write(s, _sx_sasl_challenge(s, buf, buflen), 0);
481  free(buf);
482  }
483  else {
484  _sx_debug(ZONE, "gsasl_base64_to failed, no sasl for this conn; (%d): %s", ret, gsasl_strerror(ret));
486  if(buf != NULL) free(buf);
487  }
488 
489  if(out != NULL) free(out);
490 
491  return;
492  }
493 
494  if(out != NULL) free(out);
495 
496  /* its over */
497  _sx_debug(ZONE, "sasl handshake failed; (%d): %s", ret, gsasl_strerror(ret));
498 
499  /* !!! TODO XXX check ret and flag error appropriately */
501 }
502 
504 static void _sx_sasl_server_process(sx_t s, sx_plugin_t p, Gsasl_session *sd, const char *in, int inlen) {
505  char *buf = NULL, *out = NULL;
506  size_t buflen, outlen;
507  int ret;
508 
509  _sx_debug(ZONE, "data from client");
510 
511  /* decode the response */
512  ret = gsasl_base64_from(in, inlen, &buf, &buflen);
513 
514  if (ret == GSASL_OK) {
515  _sx_debug(ZONE, "decoded data: %.*s", buflen, buf);
516 
517  /* process the data */
518  ret = gsasl_step(sd, buf, buflen, &out, &outlen);
519  if(buf != NULL) free(buf); buf = NULL;
520 
521  /* in progress */
522  if(ret == GSASL_OK || ret == GSASL_NEEDS_MORE) {
523  _sx_debug(ZONE, "sasl handshake in progress (response: %.*s)", outlen, out);
524 
525  /* encode the response */
526  ret = gsasl_base64_to(out, outlen, &buf, &buflen);
527 
528  if (ret == GSASL_OK) {
529  _sx_nad_write(s, _sx_sasl_response(s, buf, buflen), 0);
530  }
531 
532  if(out != NULL) free(out);
533  if(buf != NULL) free(buf);
534 
535  return;
536  }
537  }
538  if(out != NULL) free(out);
539  if(buf != NULL) free(buf);
540 
541  /* its over */
542  _sx_debug(ZONE, "sasl handshake aborted; (%d): %s", ret, gsasl_strerror(ret));
543 
544  _sx_nad_write(s, _sx_sasl_abort(s), 0);
545 }
546 
548 static int _sx_sasl_process(sx_t s, sx_plugin_t p, nad_t nad) {
549  Gsasl_session *sd = (Gsasl_session *) s->plugin_data[p->index];
550  int attr;
551  char mech[128];
552  sx_error_t sxe;
553  int flags;
554  char *ns = NULL, *to = NULL, *from = NULL, *version = NULL;
555 
556  /* only want sasl packets */
557  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)
558  return 1;
559 
560  /* quietly drop it if sasl is disabled, or if not ready */
561  if(s->state != state_STREAM) {
562  _sx_debug(ZONE, "not correct state for sasl, ignoring");
563  nad_free(nad);
564  return 0;
565  }
566 
567  /* packets from the client */
568  if(s->type == type_SERVER) {
569  if(!(s->flags & SX_SASL_OFFER)) {
570  _sx_debug(ZONE, "they tried to do sasl, but we never offered it, ignoring");
571  nad_free(nad);
572  return 0;
573  }
574 
575 #ifdef HAVE_SSL
576  if((s->flags & SX_SSL_STARTTLS_REQUIRE) && s->ssf == 0) {
577  _sx_debug(ZONE, "they tried to do sasl, but they have to do starttls first, ignoring");
578  nad_free(nad);
579  return 0;
580  }
581 #endif
582 
583  /* auth */
584  if(NAD_ENAME_L(nad, 0) == 4 && strncmp("auth", NAD_ENAME(nad, 0), NAD_ENAME_L(nad, 0)) == 0) {
585  /* require mechanism */
586  if((attr = nad_find_attr(nad, 0, -1, "mechanism", NULL)) < 0) {
588  nad_free(nad);
589  return 0;
590  }
591 
592  /* extract */
593  snprintf(mech, 127, "%.*s", NAD_AVAL_L(nad, attr), NAD_AVAL(nad, attr));
594 
595  /* go */
596  _sx_sasl_client_process(s, p, sd, mech, NAD_CDATA(nad, 0), NAD_CDATA_L(nad, 0));
597 
598  nad_free(nad);
599  return 0;
600  }
601 
602  /* response */
603  else if(NAD_ENAME_L(nad, 0) == 8 && strncmp("response", NAD_ENAME(nad, 0), NAD_ENAME_L(nad, 0)) == 0) {
604  /* process it */
605  _sx_sasl_client_process(s, p, sd, NULL, NAD_CDATA(nad, 0), NAD_CDATA_L(nad, 0));
606 
607  nad_free(nad);
608  return 0;
609  }
610 
611  /* abort */
612  else if(NAD_ENAME_L(nad, 0) == 5 && strncmp("abort", NAD_ENAME(nad, 0), NAD_ENAME_L(nad, 0)) == 0) {
613  _sx_debug(ZONE, "sasl handshake aborted");
614 
616 
617  nad_free(nad);
618  return 0;
619  }
620  }
621 
622  /* packets from the server */
623  else if(s->type == type_CLIENT) {
624  if(sd == NULL) {
625  _sx_debug(ZONE, "got sasl client packets, but they never started sasl, ignoring");
626  nad_free(nad);
627  return 0;
628  }
629 
630  /* challenge */
631  if(NAD_ENAME_L(nad, 0) == 9 && strncmp("challenge", NAD_ENAME(nad, 0), NAD_ENAME_L(nad, 0)) == 0) {
632  /* process it */
633  _sx_sasl_server_process(s, p, sd, NAD_CDATA(nad, 0), NAD_CDATA_L(nad, 0));
634 
635  nad_free(nad);
636  return 0;
637  }
638 
639  /* success */
640  else if(NAD_ENAME_L(nad, 0) == 7 && strncmp("success", NAD_ENAME(nad, 0), NAD_ENAME_L(nad, 0)) == 0) {
641  _sx_debug(ZONE, "sasl handshake completed, resetting");
642  nad_free(nad);
643 
644  /* save interesting bits */
645  flags = s->flags;
646 
647  if(s->ns != NULL) ns = strdup(s->ns);
648 
649  if(s->req_to != NULL) to = strdup(s->req_to);
650  if(s->req_from != NULL) from = strdup(s->req_from);
651  if(s->req_version != NULL) version = strdup(s->req_version);
652 
653  /* reset state */
654  _sx_reset(s);
655 
656  _sx_debug(ZONE, "restarting stream with sasl layer established");
657 
658  /* second time round */
659  sx_client_init(s, flags, ns, to, from, version);
660 
661  /* free bits */
662  if(ns != NULL) free(ns);
663  if(to != NULL) free(to);
664  if(from != NULL) free(from);
665  if(version != NULL) free(version);
666 
667  return 0;
668  }
669 
670  /* failure */
671  else if(NAD_ENAME_L(nad, 0) == 7 && strncmp("failure", NAD_ENAME(nad, 0), NAD_ENAME_L(nad, 0)) == 0) {
672  /* fire the error */
673  _sx_gen_error(sxe, SX_ERR_AUTH, "Authentication failed", NULL);
674  _sx_event(s, event_ERROR, (void *) &sxe);
675 
676  /* cleanup */
677  gsasl_finish(sd);
678 
679  s->plugin_data[p->index] = NULL;
680 
681  nad_free(nad);
682  return 0;
683  }
684  }
685 
686  /* invalid sasl command, quietly drop it */
687  _sx_debug(ZONE, "unknown sasl command '%.*s', ignoring", NAD_ENAME_L(nad, 0), NAD_ENAME(nad, 0));
688 
689  nad_free(nad);
690  return 0;
691 }
692 
694 static void _sx_sasl_free(sx_t s, sx_plugin_t p) {
695  Gsasl_session *sd = (Gsasl_session *) s->plugin_data[p->index];
696  _sx_sasl_sess_t sctx;
697 
698  if(sd == NULL)
699  return;
700 
701  _sx_debug(ZONE, "cleaning up conn state");
702 
703  /* we need to clean up our per session context but keep sasl ctx */
704  sctx = gsasl_session_hook_get(sd);
705  if (sctx != NULL){
706  free(sctx);
707  gsasl_session_hook_set(sd, (void *) NULL);
708  }
709 
710  gsasl_finish(sd);
711  s->plugin_data[p->index] = NULL;
712 }
713 
714 static int _sx_sasl_gsasl_callback(Gsasl *gsasl_ctx, Gsasl_session *sd, Gsasl_property prop) {
715  _sx_sasl_sess_t sctx = gsasl_session_hook_get(sd);
716  _sx_sasl_t ctx = NULL;
717  struct sx_sasl_creds_st creds = {NULL, NULL, NULL, NULL};
718  char *value, *node, *host;
719  int len, i;
720 
721  /*
722  * session hook data is not always available while its being set up,
723  * also not needed in many of the cases below.
724  */
725  if(sctx != NULL) {
726  ctx = sctx->ctx;
727  }
728 
729  _sx_debug(ZONE, "in _sx_sasl_gsasl_callback, property: %d", prop);
730 
731  switch(prop) {
732  case GSASL_PASSWORD:
733  /* GSASL_AUTHID, GSASL_AUTHZID, GSASL_REALM */
734  assert(ctx);
735  assert(ctx->cb);
736  creds.authnid = gsasl_property_fast(sd, GSASL_AUTHID);
737  creds.realm = gsasl_property_fast(sd, GSASL_REALM);
738  if(!creds.authnid) return GSASL_NO_AUTHID;
739  if(!creds.realm) return GSASL_NO_AUTHZID;
740  if((ctx->cb)(sx_sasl_cb_GET_PASS, &creds, (void **)&value, sctx->s, ctx->cbarg) == sx_sasl_ret_OK) {
741  gsasl_property_set(sd, GSASL_PASSWORD, value);
742  }
743  return GSASL_NEEDS_MORE;
744 
745  case GSASL_SERVICE:
746  gsasl_property_set(sd, GSASL_SERVICE, "xmpp");
747  return GSASL_OK;
748 
749  case GSASL_HOSTNAME:
750  {
751  char hostname[256];
752  /* get hostname */
753  hostname[0] = '\0';
754  gethostname(hostname, 256);
755  hostname[255] = '\0';
756 
757  gsasl_property_set(sd, GSASL_HOSTNAME, hostname);
758  }
759  return GSASL_OK;
760 
761  case GSASL_VALIDATE_SIMPLE:
762  /* GSASL_AUTHID, GSASL_AUTHZID, GSASL_PASSWORD */
763  assert(ctx);
764  assert(ctx->cb);
765  creds.authnid = gsasl_property_fast(sd, GSASL_AUTHID);
766  creds.realm = gsasl_property_fast(sd, GSASL_REALM);
767  creds.pass = gsasl_property_fast(sd, GSASL_PASSWORD);
768  if(!creds.authnid) return GSASL_NO_AUTHID;
769  if(!creds.realm) return GSASL_NO_AUTHZID;
770  if(!creds.pass) return GSASL_NO_PASSWORD;
771  if((ctx->cb)(sx_sasl_cb_CHECK_PASS, &creds, NULL, sctx->s, ctx->cbarg) == sx_sasl_ret_OK)
772  return GSASL_OK;
773  else
774  return GSASL_AUTHENTICATION_ERROR;
775 
776  case GSASL_VALIDATE_GSSAPI:
777  /* GSASL_AUTHZID, GSASL_GSSAPI_DISPLAY_NAME */
778  creds.authnid = gsasl_property_fast(sd, GSASL_GSSAPI_DISPLAY_NAME);
779  if(!creds.authnid) return GSASL_NO_AUTHID;
780  creds.authzid = gsasl_property_fast(sd, GSASL_AUTHZID);
781  if(!creds.authzid) return GSASL_NO_AUTHZID;
782  gsasl_property_set(sd, GSASL_AUTHID, creds.authnid);
783  return GSASL_OK;
784 
785  case GSASL_VALIDATE_ANONYMOUS:
786  /* GSASL_ANONYMOUS_TOKEN */
787  creds.authnid = gsasl_property_fast(sd, GSASL_ANONYMOUS_TOKEN);
788  if(!creds.authnid) return GSASL_NO_ANONYMOUS_TOKEN;
789  /* set token as authid for later use */
790  gsasl_property_set(sd, GSASL_AUTHID, creds.authnid);
791  return GSASL_OK;
792 
793  case GSASL_VALIDATE_EXTERNAL:
794  /* GSASL_AUTHID */
795  assert(ctx);
796  assert(ctx->ext_id);
797  creds.authzid = gsasl_property_fast(sd, GSASL_AUTHZID);
798  _sx_debug(ZONE, "sasl external");
799  _sx_debug(ZONE, "sasl creds.authzid is '%s'", creds.authzid);
800 
801  for (i = 0; i < SX_CONN_EXTERNAL_ID_MAX_COUNT; i++) {
802  if (ctx->ext_id[i] == NULL)
803  break;
804  _sx_debug(ZONE, "sasl ext_id(%d) is '%s'", i, ctx->ext_id[i]);
805  /* XXX hackish.. detect c2s by existance of @ */
806  value = strstr(ctx->ext_id[i], "@");
807 
808  if(value == NULL && creds.authzid != NULL && strcmp(ctx->ext_id[i], creds.authzid) == 0) {
809  // s2s connection and it's valid
810  /* TODO Handle wildcards and other thigs from XEP-0178 */
811  _sx_debug(ZONE, "sasl ctx->ext_id doesn't have '@' in it. Assuming s2s");
812  return GSASL_OK;
813  }
814  if(value != NULL &&
815  ((creds.authzid != NULL && strcmp(ctx->ext_id[i], creds.authzid) == 0) ||
816  (creds.authzid == NULL)) ) {
817  // c2s connection
818  // creds.authzid == NULL condition is from XEP-0178 '=' auth reply
819 
820  // This should be freed by gsasl_finish() but I'm not sure
821  // node = authnid
822  len = value - ctx->ext_id[i];
823  node = (char *) malloc(sizeof(char) * (len + 1)); // + null termination
824  strncpy(node, ctx->ext_id[i], len);
825  node[len] = '\0'; // null terminate the string
826  // host = realm
827  len = strlen(value) - 1 + 1; // - the @ + null termination
828  host = (char *) malloc(sizeof(char) * (len));
829  strcpy(host, value + 1); // skip the @
830  gsasl_property_set(sd, GSASL_AUTHID, node);
831  gsasl_property_set(sd, GSASL_REALM, host);
832  return GSASL_OK;
833  }
834 
835  }
836  return GSASL_AUTHENTICATION_ERROR;
837 
838  default:
839  break;
840  }
841 
842  return GSASL_NO_CALLBACK;
843 }
844 
845 static void _sx_sasl_unload(sx_plugin_t p) {
846  _sx_sasl_t ctx = (_sx_sasl_t) p->private;
847  int i;
848  assert(ctx);
849 
850  if (ctx->gsasl_ctx != NULL) gsasl_done (ctx->gsasl_ctx);
851  if (ctx->appname != NULL) free(ctx->appname);
852  for (i = 0; i < SX_CONN_EXTERNAL_ID_MAX_COUNT; i++)
853  if(ctx->ext_id[i] != NULL)
854  free(ctx->ext_id[i]);
855  else
856  break;
857 
858  free(ctx);
859 }
860 
862 int sx_sasl_init(sx_env_t env, sx_plugin_t p, va_list args) {
863  const char *appname;
865  void *cbarg;
866  _sx_sasl_t ctx;
867  int ret, i;
868 
869  _sx_debug(ZONE, "initialising sasl plugin");
870 
871  appname = va_arg(args, const char *);
872  if(appname == NULL) {
873  _sx_debug(ZONE, "appname was NULL, failing");
874  return 1;
875  }
876 
877  cb = va_arg(args, sx_sasl_callback_t);
878  cbarg = va_arg(args, void *);
879 
880  ctx = (_sx_sasl_t) calloc(1, sizeof(struct _sx_sasl_st));
881 
882  ctx->appname = strdup(appname);
883  ctx->cb = cb;
884  ctx->cbarg = cbarg;
885  for (i = 0; i < SX_CONN_EXTERNAL_ID_MAX_COUNT; i++)
886  ctx->ext_id[i] = NULL;
887 
888  ret = gsasl_init(&ctx->gsasl_ctx);
889  if(ret != GSASL_OK) {
890  _sx_debug(ZONE, "couldn't initialize libgsasl (%d): %s", ret, gsasl_strerror (ret));
891  free(ctx);
892  return 1;
893  }
894 
895  gsasl_callback_set (ctx->gsasl_ctx, &_sx_sasl_gsasl_callback);
896 
897  _sx_debug(ZONE, "sasl context initialised");
898 
899  p->private = (void *) ctx;
900 
901  p->unload = _sx_sasl_unload;
902  p->wio = _sx_sasl_wio;
903  p->rio = _sx_sasl_rio;
904 
905  p->stream = _sx_sasl_stream;
908 
909  p->free = _sx_sasl_free;
910 
911  return 0;
912 }
913 
915 int sx_sasl_auth(sx_plugin_t p, sx_t s, const char *appname, const char *mech, const char *user, const char *pass) {
916  _sx_sasl_t ctx = (_sx_sasl_t) p->private;
917  _sx_sasl_sess_t sctx = NULL;
918  Gsasl_session *sd;
919  char *buf = NULL, *out = NULL;
920  char hostname[256];
921  int ret, ns;
922  size_t buflen, outlen;
923  nad_t nad;
924 
925  assert((p != NULL));
926  assert((s != NULL));
927  assert((appname != NULL));
928  assert((mech != NULL));
929  assert((user != NULL));
930  assert((pass != NULL));
931 
932  if(s->type != type_CLIENT || s->state != state_STREAM) {
933  _sx_debug(ZONE, "need client in stream state for sasl auth");
934  return 1;
935  }
936 
937  /* handshake start */
938  ret = gsasl_client_start(ctx->gsasl_ctx, mech, &sd);
939  if(ret != GSASL_OK) {
940  _sx_debug(ZONE, "gsasl_client_start failed, not authing; (%d): %s", ret, gsasl_strerror(ret));
941 
942  return 1;
943  }
944 
945  /* get hostname */
946  hostname[0] = '\0';
947  gethostname(hostname, 256);
948  hostname[255] = '\0';
949 
950  /* cleanup any existing session context */
951  sctx = gsasl_session_hook_get(sd);
952  if (sctx != NULL) free(sctx);
953 
954  /* allocate and initialize our per session context */
955  sctx = (_sx_sasl_sess_t) calloc(1, sizeof(struct _sx_sasl_sess_st));
956  sctx->s = s;
957  sctx->ctx = ctx;
958 
959  /* set user data in session handle */
960  gsasl_session_hook_set(sd, (void *) sctx);
961  gsasl_property_set(sd, GSASL_AUTHID, user);
962  gsasl_property_set(sd, GSASL_PASSWORD, pass);
963  gsasl_property_set(sd, GSASL_SERVICE, appname);
964  gsasl_property_set(sd, GSASL_HOSTNAME, hostname);
965 
966  /* handshake step */
967  ret = gsasl_step(sd, NULL, 0, &out, &outlen);
968  if(ret != GSASL_OK && ret != GSASL_NEEDS_MORE) {
969  _sx_debug(ZONE, "gsasl_step failed, not authing; (%d): %s", ret, gsasl_strerror(ret));
970 
971  gsasl_finish(sd);
972 
973  return 1;
974  }
975 
976  /* save userdata */
977  s->plugin_data[p->index] = (void *) sd;
978 
979  /* in progress */
980  _sx_debug(ZONE, "sending auth request to server, mech '%s': %.*s", mech, outlen, out);
981 
982  /* encode the challenge */
983  ret = gsasl_base64_to(out, outlen, &buf, &buflen);
984  if(ret != GSASL_OK) {
985  _sx_debug(ZONE, "gsasl_base64_to failed, not authing; (%d): %s", ret, gsasl_strerror(ret));
986 
987  gsasl_finish(sd);
988 
989  if (out != NULL) free(out);
990  return 1;
991  }
992  free(out);
993 
994  /* build the nad */
995  nad = nad_new();
996  ns = nad_add_namespace(nad, uri_SASL, NULL);
997 
998  nad_append_elem(nad, ns, "auth", 0);
999  nad_append_attr(nad, -1, "mechanism", mech);
1000  if(buf != NULL) {
1001  nad_append_cdata(nad, buf, buflen, 1);
1002  free(buf);
1003  }
1004 
1005  /* its away */
1006  sx_nad_write(s, nad);
1007 
1008  return 0;
1009 }
Definition: nad.h:93
void(* free)(sx_t s, sx_plugin_t p)
Definition: sx.h:357
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:745
#define sx_sasl_cb_CHECK_MECH
Definition: plugins.h:115
void * cbarg
Definition: sasl.c:35
_sx_state_t state
Definition: sx.h:319
#define _sx_event(s, e, data)
Definition: sx.h:395
#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:862
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
int nad_find_attr(nad_t nad, unsigned 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:237
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:504
void(* unload)(sx_plugin_t p)
Definition: sx.h:378
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:753
void(* features)(sx_t s, sx_plugin_t p, nad_t nad)
Definition: sx.h:373
an environment
Definition: sx.h:382
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:347
int nad_add_namespace(nad_t nad, const char *uri, const char *prefix)
bring a new namespace into scope
Definition: nad.c:778
#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:711
const char * ns
Definition: sx.h:279
void nad_free(nad_t nad)
free that nad
Definition: nad.c:180
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:915
#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:694
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:371
#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:408
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:343
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:354
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:363
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&#39;s contents.
Definition: sx.c:299
static void _sx_sasl_unload(sx_plugin_t p)
Definition: sasl.c:845
int(* process)(sx_t s, sx_plugin_t p, nad_t nad)
Definition: sx.h:376
int(* rio)(sx_t s, sx_plugin_t p, sx_buf_t buf)
Definition: sx.h:364
#define sx_sasl_cb_GEN_AUTHZID
Definition: plugins.h:114
char * appname
Definition: sasl.c:31
static int _sx_sasl_gsasl_callback(Gsasl *gsasl_ctx, Gsasl_session *sd, Gsasl_property prop)
Definition: sasl.c:714
static int _sx_sasl_process(sx_t s, sx_plugin_t p, nad_t nad)
main nad processor
Definition: sasl.c:548
const char * pass
Definition: plugins.h:129
int index
Definition: sx.h:352
int _sx_nad_write(sx_t s, nad_t nad, int elem)
send a new nad out
Definition: io.c:417
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:330
#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