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