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