jabberd2  2.2.16
sx/sasl_cyrus.c
Go to the documentation of this file.
00001 /*
00002  * jabberd - Jabber Open Source Server
00003  * Copyright (c) 2002 Jeremie Miller, Thomas Muldowney,
00004  *                    Ryan Eatmon, Robert Norris
00005  *
00006  * This program is free software; you can redistribute it and/or modify
00007  * it under the terms of the GNU General Public License as published by
00008  * the Free Software Foundation; either version 2 of the License, or
00009  * (at your option) any later version.
00010  *
00011  * This program is distributed in the hope that it will be useful,
00012  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the
00014  * GNU General Public License for more details.
00015  *
00016  * You should have received a copy of the GNU General Public License
00017  * along with this program; if not, write to the Free Software
00018  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA02111-1307USA
00019  */
00020 
00021 /* SASL authentication handler */
00022 
00023 #error Cyrus SASL implementation is not supported! It is included here only for the brave ones, that do know what they are doing. You need to remove this line to compile it.
00024 
00025 #include "sx.h"
00026 #include "sasl.h"
00027 
00028 /* Gack - need this otherwise SASL's MD5 definitions conflict with OpenSSLs */
00029 #ifdef HEADER_MD5_H
00030 #  define MD5_H
00031 #endif
00032 #ifdef _WIN32
00033 # include <sasl.h>
00034 # include <saslutil.h>
00035 # include <saslplug.h>
00036 #else /* _WIN32 */
00037 # include <sasl/sasl.h>
00038 # include <sasl/saslutil.h>
00039 # include <sasl/saslplug.h>
00040 #endif /* _WIN32 */
00041 
00043 typedef struct _sx_sasl_st {
00044     char                        *appname;
00045     sasl_security_properties_t  sec_props;
00046 
00047     sx_sasl_callback_t          cb;
00048     void                        *cbarg;
00049 
00050     sasl_callback_t     *saslcallbacks;
00051 } *_sx_sasl_t;
00052 
00053 /* data for per-conncetion sasl handshakes */
00054 typedef struct _sx_sasl_data_st {
00055     char                        *user;
00056     sasl_secret_t               *psecret;
00057 
00058     sasl_callback_t             *callbacks;
00059 
00060     _sx_sasl_t                  ctx;
00061     sasl_conn_t                 *sasl;
00062     sx_t                        stream;
00063 } *_sx_sasl_data_t;
00064 
00065 
00066 /* Forward definitions */
00067 static void _sx_sasl_free(sx_t, sx_plugin_t);
00068 
00069 static int _sx_sasl_getopt(void * glob_context,
00070                const char *plugin_name,
00071                const char *option,
00072                const char **result,
00073                unsigned *len)
00074 {
00075     if (strcmp(option,"auxprop_plugin") == 0) {
00076         *result = "jabberdsx";
00077         if (len)
00078             *len = strlen("jabberdsx");
00079         return SASL_OK;
00080     }
00081     return SASL_FAIL;
00082 }
00083 
00084 #ifdef _WIN32
00085 /* This handles returning library path on Windows to current directory.
00086  */
00087 #include <windows.h>
00088 static int _sx_sasl_getpath(void *glob_context, const char **path_dest) {
00089     static char win32_path[MAX_PATH] = "\0";
00090 
00091     if(!path_dest) {
00092         return SASL_BADPARAM;
00093     }
00094 
00095     if(!*win32_path) {
00096         char *r;
00097         GetModuleFileName(NULL, win32_path, MAX_PATH - 5);
00098         if(!*win32_path || !(r = strrchr(win32_path, '\\')))
00099             return SASL_NOMEM;
00100         strcpy(r + 1, "sasl");
00101     }
00102 
00103     *path_dest = win32_path;
00104     return SASL_OK;
00105 }
00106 #endif /* _WIN32 */
00107 
00108 /* Support auxprop so that we can use the standard Jabber authreg plugins
00109  * with SASL mechanisms requiring passwords 
00110  */
00111 static void _sx_auxprop_lookup(void *glob_context,
00112                   sasl_server_params_t *sparams,
00113                   unsigned flags,
00114                   const char *user,
00115                   unsigned ulen) {
00116     const char *realm  = NULL;
00117     char *c;
00118     const struct propval *to_fetch, *current;
00119     char *user_buf = NULL;
00120     char *value;
00121     _sx_sasl_t ctx = (_sx_sasl_t) glob_context;
00122     struct sx_sasl_creds_st creds = {NULL, NULL, NULL, NULL};
00123 
00124     if (!sparams || !user) 
00125         return;
00126 
00127     /* It would appear that there's no guarantee that 'user' is NULL 
00128      * terminated, so we'd better terminate it ... 
00129      */
00130 
00131     user_buf = sparams->utils->malloc(ulen + 1);
00132     if (!user_buf)
00133         goto done;
00134 
00135     memcpy(user_buf, user, ulen);
00136     user_buf[ulen] = '\0';
00137 
00138     c = strchr(user_buf, '@');
00139     if (!c) {
00140         if (sparams->user_realm && sparams->user_realm[0])
00141             realm = sparams->user_realm;
00142         else
00143             realm = sparams->serverFQDN;
00144     } else {
00145         *c = '\0';
00146         realm = c+1;
00147     }
00148 
00149     /* At present, we only handle fetching the user's password */
00150     to_fetch = sparams->utils->prop_get(sparams->propctx);
00151     if (!to_fetch)
00152         goto done;
00153     for (current = to_fetch; current->name; current++) {
00154         if (strncmp(current->name, SASL_AUX_PASSWORD, sizeof(SASL_AUX_PASSWORD)) == 0) {
00155             /* If we've already got a value, see if we can override it */
00156             if (current->values) {
00157                 if (flags & SASL_AUXPROP_OVERRIDE) 
00158                     sparams->utils->prop_erase(sparams->propctx, current->name);
00159         else
00160             continue;
00161             }
00162 
00163             creds.authnid = user_buf;
00164             creds.realm = realm;
00165             if ((ctx->cb)(sx_sasl_cb_GET_PASS, &creds, (void **)&value, 
00166                           NULL, ctx->cbarg) == sx_sasl_ret_OK) {
00167                 sparams->utils->prop_set(sparams->propctx, current->name,
00168                                          value, strlen(value));
00169             }
00170         }
00171     }
00172  done:
00173     if (user_buf) sparams->utils->free(user_buf);
00174 }
00175 
00176 static sasl_auxprop_plug_t _sx_auxprop_plugin = 
00177     {0, 0, NULL, NULL, _sx_auxprop_lookup, "jabberdsx", NULL};
00178 
00179 static int 
00180 sx_auxprop_init(const sasl_utils_t *utils, int max_version, int *out_version,
00181                 sasl_auxprop_plug_t **plug, const char *plugname) {
00182 
00183     if (!out_version || !plug) 
00184         return SASL_BADPARAM;
00185     if (max_version < SASL_AUXPROP_PLUG_VERSION ) 
00186         return SASL_BADVERS;
00187 
00188     *out_version = SASL_AUXPROP_PLUG_VERSION;
00189     *plug = &_sx_auxprop_plugin;
00190 
00191     return SASL_OK;
00192 }
00193 
00194 /* This handles those authreg plugins which won't provide plaintext access
00195  * to the user's password. Note that there are very few mechanisms which
00196  * call the verify function, rather than asking for the password
00197  */
00198 static int _sx_sasl_checkpass(sasl_conn_t *conn, void *ctx, const char *user, const char *pass, unsigned passlen, struct propctx *propctx) {
00199     _sx_sasl_data_t sd = (_sx_sasl_data_t)ctx;
00200     struct sx_sasl_creds_st creds = {NULL, NULL, NULL, NULL};
00201     char *c;
00202     char *buf;
00203 
00204     /* SASL doesn't seem to pass us the username and realm as seperate items,
00205      * instead it combines them into the 'user' variable. In order to preserve
00206      * the existing behaviour, we need to split them up again ...
00207      */
00208 
00209     buf = strdup(user);
00210     c = strchr(buf,'@');
00211     if (c) {
00212         *c = '\0';
00213         creds.realm = c+1;
00214     }
00215     creds.authnid = buf;
00216     creds.pass = pass;
00217 
00218     if (sd->ctx->cb(sx_sasl_cb_CHECK_PASS, &creds, NULL, sd->stream, sd->ctx->cbarg)==sx_sasl_ret_OK) {
00219         free(buf);
00220         return SASL_OK;
00221     } else {
00222         free(buf);
00223         return SASL_BADAUTH;
00224     }
00225 }
00226 
00227 /* Canonicalize the username. Normally this does nothing, but if we're
00228  * calling from an anonymous plugin, then we need to generate a JID for
00229  * the user
00230  */
00231 
00232 static int _sx_sasl_canon_user(sasl_conn_t *conn, void *ctx, const char *user, unsigned ulen, unsigned flags, const char *user_realm, char *out_user, unsigned out_umax, unsigned *out_ulen) {
00233     char *buf;
00234     _sx_sasl_data_t sd = (_sx_sasl_data_t)ctx;
00235     sasl_getprop(conn, SASL_MECHNAME, (const void **) &buf);
00236     if (strncmp(buf, "ANONYMOUS", 10) == 0) {
00237         sd->ctx->cb(sx_sasl_cb_GEN_AUTHZID, NULL, (void **)&buf, sd->stream, sd->ctx->cbarg);
00238         strncpy(out_user, buf, out_umax);
00239         out_user[out_umax]='\0';
00240         *out_ulen=strlen(out_user);
00241     } else {
00242         memcpy(out_user,user,ulen);
00243         *out_ulen = ulen;
00244     }
00245     return SASL_OK;
00246 }
00247 
00248 /* Need to make sure that
00249  *  *) The authnid is permitted to become the given authzid
00250  *  *) The authnid is included in the given authreg systems DB
00251  */
00252 static int _sx_sasl_proxy_policy(sasl_conn_t *conn, void *ctx, const char *requested_user, int rlen, const char *auth_identity, int alen, const char *realm, int urlen, struct propctx *propctx) {
00253     _sx_sasl_data_t sd = (_sx_sasl_data_t) ctx;
00254     struct sx_sasl_creds_st creds = {NULL, NULL, NULL, NULL};
00255     char *buf, *c;
00256     size_t len;
00257     int ret;
00258 
00259     sasl_getprop(conn, SASL_MECHNAME, (const void **) &buf);
00260     if (strncmp(buf, "ANONYMOUS", 10) == 0) {
00261         /* If they're anonymous, their ID comes from us, so it must be OK! */
00262         return SASL_OK;
00263     } else {
00264         /* This will break with clients that give requested user as a JID,
00265          * where requested_user != auth_identity */
00266         if (!requested_user || !auth_identity || rlen == 0 || alen==0) {
00267           sasl_seterror(conn, 0,
00268                         "Bad identities provided");
00269           return SASL_BADAUTH;
00270       }
00271 
00272       /* No guarantee that realm is NULL terminated - so make a terminated
00273          * version before we do anything */
00274 
00275       /* XXX - Do we also need to check if realm contains NULL values, 
00276        *       and complain if it does?
00277          */
00278 
00279       buf = malloc(urlen + 1);
00280       strncpy(buf, realm?realm:"", urlen);
00281       buf[urlen] = '\0';
00282       creds.realm = buf;
00283 
00284       /* By this point, SASL's default canon_user plugin has appended the
00285          * realm to both the auth_identity, and the requested_user. This
00286          * isn't what we want.
00287          *   auth_identity should be a bare username
00288          *   requested_user should be a JID
00289          *
00290          * We can't just remove everything after the '@' as some mechanisms
00291          * (such as GSSAPI) use the @ to denote users in foreign realms.
00292          */
00293 
00294       buf = malloc(alen + 1);
00295       strncpy(buf, auth_identity, alen);
00296       buf[alen] = '\0';
00297       c = strrchr(buf, '@');
00298       if (c && strcmp(c+1, creds.realm) == 0)
00299             *c = '\0';
00300       creds.authnid = buf;
00301 
00302       /* Now, we need to turn requested_user into a JID 
00303          * (if it isn't already)
00304        *
00305        * XXX - This will break with s2s SASL, where the authzid is a domain
00306        */
00307       len = rlen;
00308       if (sd->stream->req_to)
00309           len+=strlen(sd->stream->req_to) + 2;
00310       buf = malloc(len + 1);
00311       strncpy(buf, requested_user, rlen);
00312       buf[rlen] = '\0';
00313       c = strrchr(buf, '@');
00314       if (c && strcmp(c + 1, creds.realm) == 0)
00315           *c = '\0';
00316       if (sd->stream->req_to && strchr(buf, '@') == 0) {
00317           strcat(buf, "@");
00318           strcat(buf, sd->stream->req_to);
00319       }
00320       creds.authzid = buf;
00321 
00322           /* If we start being fancy and allow auth_identity to be different from
00323            * requested_user, then this will need to be changed to permit it!
00324            */
00325         ret = (sd->ctx->cb)(sx_sasl_cb_CHECK_AUTHZID, &creds, NULL, sd->stream, sd->ctx->cbarg);
00326 
00327       free((void *)creds.authnid);
00328       free((void *)creds.authzid);
00329       free((void *)creds.realm);
00330 
00331       if (ret == sx_sasl_ret_OK) {
00332           return SASL_OK;
00333       } else {
00334           sasl_seterror(conn, 0, "Requested identity not permitted for authorization identity");
00335           return SASL_BADAUTH;
00336       }
00337     }
00338 }
00339 
00340 static int _sx_sasl_wio(sx_t s, sx_plugin_t p, sx_buf_t buf) {
00341     sasl_conn_t *sasl;
00342     int *x, len, pos, reslen, maxbuf;
00343     char *out, *result;
00344 
00345     sasl = ((_sx_sasl_data_t) s->plugin_data[p->index])->sasl;
00346 
00347     /* if there's no security layer, don't bother */
00348     sasl_getprop(sasl, SASL_SSF, (const void **) &x);
00349     if(*x == 0)
00350         return 1;
00351 
00352     _sx_debug(ZONE, "doing sasl encode");
00353 
00354     /* can only encode x bytes at a time */
00355     sasl_getprop(sasl, SASL_MAXOUTBUF, (const void **) &x);
00356     maxbuf = *x;
00357 
00358     /* encode the output */
00359     pos = 0;
00360     result = NULL; reslen = 0;
00361     while(pos < buf->len) {
00362         if((buf->len - pos) < maxbuf)
00363             maxbuf = buf->len - pos;
00364 
00365         sasl_encode(sasl, &buf->data[pos], maxbuf, (const char **) &out, &len);
00366         
00367         result = (char *) realloc(result, sizeof(char) * (reslen + len));
00368         memcpy(&result[reslen], out, len);
00369         reslen += len;
00370 
00371         pos += maxbuf;
00372     }
00373     
00374     /* replace the buffer */
00375     _sx_buffer_set(buf, result, reslen, result);
00376 
00377     _sx_debug(ZONE, "%d bytes encoded for sasl channel", buf->len);
00378     
00379     return 1;
00380 }
00381 
00382 static int _sx_sasl_rio(sx_t s, sx_plugin_t p, sx_buf_t buf) {
00383     sasl_conn_t *sasl;
00384     sx_error_t sxe;
00385     int *x, len;
00386     char *out;
00387 
00388     sasl = ((_sx_sasl_data_t) s->plugin_data[p->index])->sasl;
00389 
00390     /* if there's no security layer, don't bother */
00391     sasl_getprop(sasl, SASL_SSF, (const void **) &x);
00392     if(*x == 0)
00393         return 1;
00394 
00395     _sx_debug(ZONE, "doing sasl decode");
00396 
00397     /* decode the input */
00398     if (sasl_decode(sasl, buf->data, buf->len, (const char **) &out, &len)
00399       != SASL_OK) {
00400       /* Fatal error */
00401       _sx_gen_error(sxe, SX_ERR_AUTH, "SASL Stream decoding failed", NULL);
00402       _sx_event(s, event_ERROR, (void *) &sxe);
00403       return -1;
00404     }
00405     
00406     /* replace the buffer */
00407     _sx_buffer_set(buf, out, len, NULL);
00408 
00409     _sx_debug(ZONE, "%d bytes decoded from sasl channel", len);
00410     
00411     return 1;
00412 }
00413 
00415 void _sx_sasl_open(sx_t s, sasl_conn_t *sasl) {
00416     char *method;
00417     char *buf, *c;
00418     char *authzid;
00419     size_t len;
00420     int *ssf;
00421     
00422     /* get the method */
00423     sasl_getprop(sasl, SASL_MECHNAME, (const void **) &buf);
00424 
00425     method = (char *) malloc(sizeof(char) * (strlen(buf) + 17));
00426     sprintf(method, "SASL/%s", buf);
00427 
00428     /* get the ssf */
00429     if(s->ssf == 0) {
00430         sasl_getprop(sasl, SASL_SSF, (const void **) &ssf);
00431         s->ssf = *ssf;
00432     }
00433 
00434     /* and the authenticated id */
00435     sasl_getprop(sasl, SASL_USERNAME, (const void **) &buf);
00436 
00437     if (s->type == type_SERVER) {
00438         /* Now, we need to turn the id into a JID 
00439          * (if it isn't already)
00440          *
00441          * XXX - This will break with s2s SASL, where the authzid is a domain
00442          */
00443 
00444       len = strlen(buf);
00445       if (s->req_to)
00446           len+=strlen(s->req_to) + 2;
00447         authzid = malloc(len + 1);
00448         strcpy(authzid, buf);
00449 
00450         sasl_getprop(sasl, SASL_DEFUSERREALM, (const void **) &buf);
00451 
00452         c = strrchr(authzid, '@');
00453         if (c && buf && strcmp(c+1, buf) == 0)
00454             *c = '\0';
00455         if (s->req_to && strchr(authzid, '@') == 0) {
00456             strcat(authzid, "@");
00457             strcat(authzid, s->req_to);
00458         }
00459 
00460         /* schwing! */
00461         sx_auth(s, method, authzid);
00462         free(authzid);
00463     } else {
00464         sx_auth(s, method, buf);
00465     }
00466 
00467     free(method);
00468 }
00469 
00471 static void _sx_sasl_stream(sx_t s, sx_plugin_t p) {
00472     _sx_sasl_t ctx = (_sx_sasl_t) p->private;
00473     sasl_conn_t *sasl;
00474     _sx_sasl_data_t sd;
00475     int ret, i;
00476     char *realm = NULL, *ext_id, *mech;
00477     sasl_security_properties_t sec_props;
00478 
00479     /* First time around, we need to set up our SASL connection, otherwise
00480      * features will fall flat on its face */
00481     if (s->plugin_data[p->index] == NULL) {
00482         if(s->type == type_SERVER) {
00483 
00484             if(!(s->flags & SX_SASL_OFFER)) {
00485                 _sx_debug(ZONE, "application did not request sasl offer, not offering for this conn");
00486                 return;
00487             }
00488 
00489             _sx_debug(ZONE, "setting up sasl for this server conn");
00490 
00491             /* Initialise our data object */
00492             sd = (_sx_sasl_data_t) calloc(1, sizeof(struct _sx_sasl_data_st));
00493 
00494             /* get the realm */
00495             if(ctx->cb != NULL)
00496                 (ctx->cb)(sx_sasl_cb_GET_REALM, NULL, (void **) &realm, s, ctx->cbarg);
00497 
00498             /* Initialize our callbacks */
00499             sd->callbacks = calloc(sizeof(sasl_callback_t),4);
00500 
00501             sd->callbacks[0].id = SASL_CB_PROXY_POLICY;
00502             sd->callbacks[0].proc = &_sx_sasl_proxy_policy;
00503             sd->callbacks[0].context = sd;
00504 
00505             sd->callbacks[1].id = SASL_CB_CANON_USER;
00506             sd->callbacks[1].proc = &_sx_sasl_canon_user;
00507             sd->callbacks[1].context = sd;
00508 
00509             sd->callbacks[2].id = SASL_CB_SERVER_USERDB_CHECKPASS;
00510             sd->callbacks[2].proc = &_sx_sasl_checkpass;
00511             sd->callbacks[2].context = sd;
00512 
00513             sd->callbacks[3].id = SASL_CB_LIST_END;
00514 
00515             /* startup */
00516             ret = sasl_server_new(ctx->appname, NULL,
00517                                   realm ? (realm[0] == '\0' ? NULL : realm) : NULL,
00518                                   NULL, NULL, sd->callbacks,
00519                                   ctx->sec_props.security_flags, &sasl);
00520             if(ret != SASL_OK) {
00521                 _sx_debug(ZONE, "sasl_server_new failed (%s), not offering sasl for this conn", sasl_errstring(ret, NULL, NULL));
00522                 free(sd->callbacks);
00523                 free(sd);
00524                 return;
00525             }
00526 
00527             /* get external data from the ssl plugin */
00528             ext_id = NULL;
00529 #ifdef HAVE_SSL
00530             for(i = 0; i < s->env->nplugins; i++)
00531                 if(s->env->plugins[i]->magic == SX_SSL_MAGIC && s->plugin_data[s->env->plugins[i]->index] != NULL)
00532                     ext_id = ((_sx_ssl_conn_t) s->plugin_data[s->env->plugins[i]->index])->external_id;
00533 
00534             /* if we've got some, setup for external auth */
00535             if(ext_id != NULL) {
00536                 ret = sasl_setprop(sasl, SASL_AUTH_EXTERNAL, ext_id);
00537                 if(ret == SASL_OK) 
00538                     ret = sasl_setprop(sasl, SASL_SSF_EXTERNAL, &s->ssf);
00539             }
00540 #endif /* HAVE_SSL */
00541 
00542             /* security properties */
00543             sec_props = ctx->sec_props;
00544             if(s->ssf > 0)
00545                 /* if we're already encrypted, then no security layers */
00546                 sec_props.max_ssf = 0;
00547 
00548             if(ret == SASL_OK) 
00549                 ret = sasl_setprop(sasl, SASL_SEC_PROPS, &sec_props);
00550 
00551             if(ret != SASL_OK) {
00552                 _sx_debug(ZONE, "sasl_setprop failed (%s), not offering sasl for this conn", sasl_errstring(ret, NULL, NULL));
00553                 free(sd->callbacks);
00554                 free(sd);
00555                 return;
00556             }
00557 
00558             sd->sasl = sasl;
00559             sd->stream = s;
00560             sd->ctx = ctx;
00561 
00562             _sx_debug(ZONE, "sasl context initialised for %d", s->tag);
00563 
00564             s->plugin_data[p->index] = (void *) sd;
00565 
00566         }
00567 
00568         return;
00569     }
00570 
00571     sasl = ((_sx_sasl_data_t) s->plugin_data[p->index])->sasl;
00572 
00573     /* are we auth'd? */
00574     if (sasl_getprop(sasl, SASL_MECHNAME, (void *) &mech) == SASL_NOTDONE) {
00575         _sx_debug(ZONE, "not auth'd, not advancing to auth'd state yet");
00576         return;
00577     }
00578 
00579     /* otherwise, its auth time */
00580     _sx_sasl_open(s, sasl);
00581 }
00582 
00583 static void _sx_sasl_features(sx_t s, sx_plugin_t p, nad_t nad) {
00584     _sx_sasl_data_t sd = (_sx_sasl_data_t) s->plugin_data[p->index];
00585     int ret, nmechs, ns;
00586     char *mechs, *mech, *c;
00587 
00588     if(s->type != type_SERVER || sd == NULL || sd->sasl == NULL)
00589         return;
00590 
00591     if((ret = sasl_getprop(sd->sasl, SASL_MECHNAME, (void *) &mech)) != SASL_NOTDONE) {
00592         _sx_debug(ZONE, "already auth'd, not offering sasl mechanisms");
00593         return;
00594     }
00595 
00596     if(!(s->flags & SX_SASL_OFFER)) {
00597         _sx_debug(ZONE, "application didn't ask us to offer sasl, so we won't");
00598         return;
00599     }
00600 
00601 #ifdef HAVE_SSL
00602     if((s->flags & SX_SSL_STARTTLS_REQUIRE) && s->ssf == 0) {
00603         _sx_debug(ZONE, "ssl not established yet but the app requires it, not offering mechanisms");
00604         return;
00605     }
00606 #endif
00607     
00608     _sx_debug(ZONE, "offering sasl mechanisms");
00609 
00610     ret = sasl_listmech(sd->sasl, NULL, "", "|", "", (const char **) &mechs, NULL, &nmechs);
00611     if(ret != SASL_OK) {
00612         _sx_debug(ZONE, "sasl_listmech failed (%s), not offering sasl for this conn", sasl_errstring(ret, NULL, NULL));
00613         _sx_sasl_free(s,p);
00614         return;
00615     }
00616     
00617     if(nmechs <= 0) {
00618         _sx_debug(ZONE, "sasl_listmech returned no mechanisms, not offering sasl for this conn");
00619         _sx_sasl_free(s,p);
00620         return;
00621     }
00622 
00623     mech = mechs;
00624     nmechs = 0;
00625     while(mech != NULL) {
00626         c = strchr(mech, '|');
00627         if(c != NULL)
00628             *c = '\0';
00629 
00630         if ((sd->ctx->cb)(sx_sasl_cb_CHECK_MECH, mech, NULL, sd->stream, sd->ctx->cbarg)==sx_sasl_ret_OK) {
00631             if (nmechs == 0) {
00632                 ns = nad_add_namespace(nad, uri_SASL, NULL);
00633                 nad_append_elem(nad, ns, "mechanisms", 1);
00634             }
00635             _sx_debug(ZONE, "offering mechanism: %s", mech);
00636 
00637             nad_append_elem(nad, ns, "mechanism", 2);
00638             nad_append_cdata(nad, mech, strlen(mech), 3);
00639             nmechs++;
00640         }
00641 
00642         if(c == NULL)
00643             mech = NULL;
00644         else
00645             mech = ++c;
00646     }
00647 }
00648 
00650 static nad_t _sx_sasl_success(sx_t s) {
00651     nad_t nad;
00652     int ns;
00653 
00654     nad = nad_new();
00655     ns = nad_add_namespace(nad, uri_SASL, NULL);
00656 
00657     nad_append_elem(nad, ns, "success", 0);
00658 
00659     return nad;
00660 }
00661 
00663 static nad_t _sx_sasl_failure(sx_t s, const char *err) {
00664     nad_t nad;
00665     int ns;
00666 
00667     nad = nad_new();
00668     ns = nad_add_namespace(nad, uri_SASL, NULL);
00669 
00670     nad_append_elem(nad, ns, "failure", 0);
00671     if(err != NULL)
00672         nad_append_elem(nad, ns, err, 1);
00673 
00674     return nad;
00675 }
00676 
00678 static nad_t _sx_sasl_challenge(sx_t s, char *data, int dlen) {
00679     nad_t nad;
00680     int ns;
00681 
00682     nad = nad_new();
00683     ns = nad_add_namespace(nad, uri_SASL, NULL);
00684 
00685     nad_append_elem(nad, ns, "challenge", 0);
00686     if(data != NULL)
00687         nad_append_cdata(nad, data, dlen, 1);
00688 
00689     return nad;
00690 }
00691 
00693 static nad_t _sx_sasl_response(sx_t s, char *data, int dlen) {
00694     nad_t nad;
00695     int ns;
00696 
00697     nad = nad_new();
00698     ns = nad_add_namespace(nad, uri_SASL, NULL);
00699 
00700     nad_append_elem(nad, ns, "response", 0);
00701     if(data != NULL)
00702         nad_append_cdata(nad, data, dlen, 1);
00703 
00704     return nad;
00705 }
00706 
00708 static nad_t _sx_sasl_abort(sx_t s) {
00709     nad_t nad;
00710     int ns;
00711 
00712     nad = nad_new();
00713     ns = nad_add_namespace(nad, uri_SASL, NULL);
00714 
00715     nad_append_elem(nad, ns, "abort", 0);
00716 
00717     return nad;
00718 }
00719 
00721 static void _sx_sasl_decode(char *in, int inlen, char **out, int *outlen) {
00722     *out = (char *) malloc(sizeof(char) * (2 * inlen));
00723     sasl_decode64(in,inlen,*out,2*inlen,outlen);
00724 }
00725 
00727 static void _sx_sasl_encode(char *in, int inlen, char **out, int *outlen) {
00728     *out = (char *) malloc(sizeof(char) * (2 * inlen));
00729     sasl_encode64(in,inlen,*out,2*inlen,outlen);
00730 }
00731 
00733 static void _sx_sasl_notify_success(sx_t s, void *arg) {
00734     sx_plugin_t p = (sx_plugin_t) arg;
00735 
00736     _sx_chain_io_plugin(s, p);
00737     _sx_debug(ZONE, "auth completed, resetting");
00738 
00739     _sx_reset(s);
00740 
00741     sx_server_init(s, s->flags);
00742 }
00743 
00745 static void _sx_sasl_client_process(sx_t s, sx_plugin_t p, char *mech, char *in, int inlen) {
00746     _sx_sasl_data_t sd = (_sx_sasl_data_t) s->plugin_data[p->index];
00747     char *buf = NULL, *out = NULL;
00748     int buflen, outlen, ret;
00749 
00750     /* decode the response */
00751     _sx_sasl_decode(in, inlen, &buf, &buflen);
00752 
00753     if(mech != NULL) {
00754         _sx_debug(ZONE, "auth request from client (mechanism=%s)", mech);
00755     } else {
00756         _sx_debug(ZONE, "response from client (response: %.*s)", buflen, buf);
00757     }
00758 
00759     /* process the data */
00760     if(mech != NULL)
00761         ret = sasl_server_start(sd->sasl, mech, buf, buflen, (const char **) &out, &outlen);
00762     else {
00763         if(!sd->sasl) {
00764             _sx_debug(ZONE, "response send before auth request enabling mechanism (decoded: %.*s)", buflen, buf);
00765             _sx_nad_write(s, _sx_sasl_failure(s, _sasl_err_MECH_TOO_WEAK), 0);
00766             if(buf != NULL) free(buf);
00767             return;
00768         }
00769         ret = sasl_server_step(sd->sasl, buf, buflen, (const char **) &out, &outlen);
00770     }
00771 
00772     if(buf != NULL) free(buf);
00773 
00774     /* auth completed */
00775     if(ret == SASL_OK) {
00776         _sx_debug(ZONE, "sasl handshake completed");
00777 
00778         /* send success */
00779         _sx_nad_write(s, _sx_sasl_success(s), 0);
00780 
00781         /* set a notify on the success nad buffer */
00782         ((sx_buf_t) s->wbufq->front->data)->notify = _sx_sasl_notify_success;
00783         ((sx_buf_t) s->wbufq->front->data)->notify_arg = (void *) p;
00784 
00785     return;
00786     }
00787 
00788     /* in progress */
00789     if(ret == SASL_CONTINUE) {
00790         _sx_debug(ZONE, "sasl handshake in progress (challenge: %.*s)", outlen, out);
00791 
00792         /* encode the challenge */
00793         _sx_sasl_encode(out, outlen, &buf, &buflen);
00794 
00795         _sx_nad_write(s, _sx_sasl_challenge(s, buf, buflen), 0);
00796 
00797         free(buf);
00798 
00799         return;
00800     }
00801 
00802     /* its over */
00803     buf = (char *) sasl_errdetail(sd->sasl);
00804     if(buf == NULL)
00805         buf = "[no error message available]";
00806 
00807     _sx_debug(ZONE, "sasl handshake failed: %s", buf);
00808 
00809     _sx_nad_write(s, _sx_sasl_failure(s, _sasl_err_MALFORMED_REQUEST), 0);
00810 }
00811 
00813 static void _sx_sasl_server_process(sx_t s, sx_plugin_t p, char *in, int inlen) {
00814     _sx_sasl_data_t sd = (_sx_sasl_data_t)s->plugin_data[p->index];
00815     char *buf, *out;
00816     int buflen, outlen, ret;
00817     const char *err_buf;
00818 
00819     _sx_debug(ZONE, "challenge from client");
00820 
00821     /* decode the response */
00822     _sx_sasl_decode(in, inlen, &buf, &buflen);
00823 
00824     /* process the data */
00825     ret = sasl_client_step(sd->sasl, buf, buflen, NULL, (const char **) &out, &outlen);
00826     if(buf != NULL) free(buf);
00827 
00828     /* in progress */
00829     if(ret == SASL_OK || ret == SASL_CONTINUE) {
00830         _sx_debug(ZONE, "sasl handshake in progress (response: %.*s)", outlen, out);
00831 
00832         /* encode the response */
00833         _sx_sasl_encode(out, outlen, &buf, &buflen);
00834 
00835         _sx_nad_write(s, _sx_sasl_response(s, buf, buflen), 0);
00836 
00837         if(buf != NULL) free(buf);
00838 
00839         return;
00840     }
00841 
00842     /* its over */
00843     err_buf = sasl_errdetail(sd->sasl);
00844     if (err_buf == NULL)
00845         err_buf = "[no error message available]";
00846     
00847     _sx_debug(ZONE, "sasl handshake aborted: %s", err_buf);
00848 
00849     _sx_nad_write(s, _sx_sasl_abort(s), 0);
00850 }
00851 
00853 static int _sx_sasl_process(sx_t s, sx_plugin_t p, nad_t nad) {
00854     _sx_sasl_data_t sd = (_sx_sasl_data_t)s->plugin_data[p->index];
00855     int attr;
00856     char mech[128];
00857     sx_error_t sxe;
00858     int flags;
00859     char *ns = NULL, *to = NULL, *from = NULL, *version = NULL;
00860 
00861     /* only want sasl packets */
00862     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)
00863         return 1;
00864 
00865     /* quietly drop it if sasl is disabled, or if not ready */
00866     if(s->state != state_STREAM || sd == NULL) {
00867         _sx_debug(ZONE, "not correct state for sasl, ignoring");
00868         nad_free(nad);
00869         return 0;
00870     }
00871 
00872     /* packets from the client */
00873     if(s->type == type_SERVER) {
00874         if(!(s->flags & SX_SASL_OFFER)) {
00875             _sx_debug(ZONE, "they tried to do sasl, but we never offered it, ignoring");
00876             nad_free(nad);
00877             return 0;
00878         }
00879 
00880 #ifdef HAVE_SSL
00881         if((s->flags & SX_SSL_STARTTLS_REQUIRE) && s->ssf == 0) {
00882             _sx_debug(ZONE, "they tried to do sasl, but they have to do starttls first, ignoring");
00883             nad_free(nad);
00884             return 0;
00885         }
00886 #endif
00887 
00888         /* auth */
00889         if(NAD_ENAME_L(nad, 0) == 4 && strncmp("auth", NAD_ENAME(nad, 0), NAD_ENAME_L(nad, 0)) == 0) {
00890             /* require mechanism */
00891             if((attr = nad_find_attr(nad, 0, -1, "mechanism", NULL)) < 0) {
00892                 _sx_nad_write(s, _sx_sasl_failure(s, _sasl_err_INVALID_MECHANISM), 0);
00893                 nad_free(nad);
00894                 return 0;
00895             }
00896 
00897             /* extract */
00898             snprintf(mech, 127, "%.*s", NAD_AVAL_L(nad, attr), NAD_AVAL(nad, attr));
00899 
00900             /* go */
00901             _sx_sasl_client_process(s, p, mech, NAD_CDATA(nad, 0), NAD_CDATA_L(nad, 0));
00902 
00903             nad_free(nad);
00904             return 0;
00905         }
00906 
00907         /* response */
00908         else if(NAD_ENAME_L(nad, 0) == 8 && strncmp("response", NAD_ENAME(nad, 0), NAD_ENAME_L(nad, 0)) == 0) {
00909             /* process it */
00910             _sx_sasl_client_process(s, p, NULL, NAD_CDATA(nad, 0), NAD_CDATA_L(nad, 0));
00911 
00912             nad_free(nad);
00913             return 0;
00914         }
00915 
00916         /* abort */
00917         else if(NAD_ENAME_L(nad, 0) == 5 && strncmp("abort", NAD_ENAME(nad, 0), NAD_ENAME_L(nad, 0)) == 0) {
00918             _sx_debug(ZONE, "sasl handshake aborted");
00919 
00920             _sx_nad_write(s, _sx_sasl_failure(s, _sasl_err_ABORTED), 0);
00921 
00922             nad_free(nad);
00923             return 0;
00924         }
00925     }
00926     
00927     /* packets from the server */
00928     else if(s->type == type_CLIENT) {
00929         if(sd == NULL) {
00930             _sx_debug(ZONE, "got sasl client packets, but they never started sasl, ignoring");
00931             nad_free(nad);
00932             return 0;
00933         }
00934 
00935         /* challenge */
00936         if(NAD_ENAME_L(nad, 0) == 9 && strncmp("challenge", NAD_ENAME(nad, 0), NAD_ENAME_L(nad, 0)) == 0) {
00937             /* process it */
00938             _sx_sasl_server_process(s, p, NAD_CDATA(nad, 0), NAD_CDATA_L(nad, 0));
00939 
00940             nad_free(nad);
00941             return 0;
00942         }
00943 
00944         /* success */
00945         else if(NAD_ENAME_L(nad, 0) == 7 && strncmp("success", NAD_ENAME(nad, 0), NAD_ENAME_L(nad, 0)) == 0) {
00946             _sx_debug(ZONE, "sasl handshake completed, resetting");
00947             nad_free(nad);
00948 
00949             /* save interesting bits */
00950             flags = s->flags;
00951 
00952             if(s->ns != NULL) ns = strdup(s->ns);
00953 
00954             if(s->req_to != NULL) to = strdup(s->req_to);
00955             if(s->req_from != NULL) from = strdup(s->req_from);
00956             if(s->req_version != NULL) version = strdup(s->req_version);
00957 
00958             /* setup the encoder */
00959             _sx_chain_io_plugin(s, p);
00960 
00961             /* reset state */
00962             _sx_reset(s);
00963 
00964             _sx_debug(ZONE, "restarting stream with sasl layer established");
00965 
00966             /* second time round */
00967             sx_client_init(s, flags, ns, to, from, version);
00968 
00969             /* free bits */
00970             if(ns != NULL) free(ns);
00971             if(to != NULL) free(to);
00972             if(from != NULL) free(from);
00973             if(version != NULL) free(version);
00974 
00975             return 0;
00976         }
00977 
00978         /* failure */
00979         else if(NAD_ENAME_L(nad, 0) == 7 && strncmp("failure", NAD_ENAME(nad, 0), NAD_ENAME_L(nad, 0)) == 0) {
00980             /* fire the error */
00981             _sx_gen_error(sxe, SX_ERR_AUTH, "Authentication failed", NULL);
00982             _sx_event(s, event_ERROR, (void *) &sxe);
00983 
00984             /* cleanup */
00985             _sx_sasl_free(s,p);
00986 
00987             nad_free(nad);
00988             return 0;
00989         }
00990     }
00991 
00992     /* invalid sasl command, quietly drop it */
00993     _sx_debug(ZONE, "unknown sasl command '%.*s', ignoring", NAD_ENAME_L(nad, 0), NAD_ENAME(nad, 0));
00994 
00995     nad_free(nad);
00996     return 0;
00997 }
00998 
01000 static void _sx_sasl_free(sx_t s, sx_plugin_t p) {
01001     _sx_sasl_data_t sd = (_sx_sasl_data_t) s->plugin_data[p->index];
01002 
01003     if(sd == NULL)
01004         return;
01005 
01006     _sx_debug(ZONE, "cleaning up conn state");
01007 
01008     if(sd->sasl != NULL) sasl_dispose(&sd->sasl);
01009     if(sd->user != NULL) free(sd->user);
01010     if(sd->psecret != NULL) free(sd->psecret);
01011     if(sd->callbacks != NULL) free(sd->callbacks);
01012 
01013     free(sd);
01014 
01015     s->plugin_data[p->index] = NULL;
01016 }
01017 
01018 static void _sx_sasl_unload(sx_plugin_t p) {
01019     _sx_sasl_t ctx;
01020 
01021     ctx = (_sx_sasl_t) p->private;
01022 
01023     if (ctx->appname != NULL) free(ctx->appname);
01024     if (ctx->saslcallbacks != NULL) free(ctx->saslcallbacks);
01025 
01026     if (p->private != NULL) free(p->private);
01027 }
01028 
01030 int sx_sasl_init(sx_env_t env, sx_plugin_t p, va_list args) {
01031     char *appname;
01032     sx_sasl_callback_t cb;
01033     void *cbarg;
01034     int ret;
01035     _sx_sasl_t ctx;
01036 
01037     _sx_debug(ZONE, "initialising sasl plugin");
01038 
01039     appname = va_arg(args, char *);
01040     if(appname == NULL) {
01041         _sx_debug(ZONE, "appname was NULL, failing");
01042         return 1;
01043     }
01044 
01045     cb = va_arg(args, sx_sasl_callback_t);
01046     cbarg = va_arg(args, void *);
01047 
01048     /* Set up the auxiliary property plugin, which we use to gave SASL
01049      * mechanism plugins access to our passwords
01050      */
01051     sasl_auxprop_add_plugin("jabbersx", sx_auxprop_init);
01052 
01053     ctx = (_sx_sasl_t) calloc(1, sizeof(struct _sx_sasl_st));
01054 
01055     ctx->sec_props.min_ssf = 0;
01056     ctx->sec_props.max_ssf = -1;    /* sasl_ssf_t is typedef'd to unsigned, so -1 gets us the max possible ssf */
01057     ctx->sec_props.maxbufsize = 1024;
01058     ctx->sec_props.security_flags = 0;
01059 
01060     ctx->appname = strdup(appname);
01061     ctx->cb = cb;
01062     ctx->cbarg = cbarg;
01063   
01064     /* Push the location of our callbacks into the auxprop structure */
01065     
01066     _sx_auxprop_plugin.glob_context = (void *) ctx;
01067 
01068 #ifdef _WIN32
01069     ctx->saslcallbacks = calloc(sizeof(sasl_callback_t), 3);
01070 #else
01071     ctx->saslcallbacks = calloc(sizeof(sasl_callback_t), 2);
01072 #endif
01073     ctx->saslcallbacks[0].id = SASL_CB_GETOPT;
01074     ctx->saslcallbacks[0].proc = &_sx_sasl_getopt;
01075     ctx->saslcallbacks[0].context = NULL;
01076 #ifdef _WIN32
01077     ctx->saslcallbacks[1].id = SASL_CB_GETPATH;
01078     ctx->saslcallbacks[1].proc = &_sx_sasl_getpath;
01079     ctx->saslcallbacks[1].context = NULL;
01080 
01081     ctx->saslcallbacks[2].id = SASL_CB_LIST_END;
01082 #else
01083     ctx->saslcallbacks[1].id = SASL_CB_LIST_END;
01084 #endif
01085 
01086     ret = sasl_server_init(ctx->saslcallbacks, appname);
01087     if(ret != SASL_OK) {
01088         _sx_debug(ZONE, "sasl_server_init() failed (%s), disabling", sasl_errstring(ret, NULL, NULL));
01089         free(ctx->saslcallbacks);
01090         free(ctx);
01091         return 1;
01092     }
01093 
01094     _sx_debug(ZONE, "sasl context initialised; appname=%s", appname);
01095 
01096     p->private = (void *) ctx;
01097 
01098     p->unload = _sx_sasl_unload;
01099     p->wio = _sx_sasl_wio;
01100     p->rio = _sx_sasl_rio;
01101 
01102     p->stream = _sx_sasl_stream;
01103     p->features = _sx_sasl_features;
01104     p->process = _sx_sasl_process;
01105 
01106     p->free = _sx_sasl_free;
01107 
01108     return 0;
01109 }
01110 
01111 /* callback functions for client auth */
01112 static int _sx_sasl_cb_get_simple(void *ctx, int id, const char **result, unsigned *len)
01113 {
01114     _sx_sasl_data_t sd = (_sx_sasl_data_t) ctx;
01115 
01116     _sx_debug(ZONE, "in _sx_sasl_cb_get_simple (id 0x%x)", id);
01117 
01118     *result = sd->user;
01119     if(len != NULL)
01120         *len = strlen(*result);
01121 
01122     return SASL_OK;
01123 }
01124 
01125 static int _sx_sasl_cb_get_secret(sasl_conn_t *conn, void *ctx, int id, sasl_secret_t **psecret)
01126 {
01127     _sx_sasl_data_t sd = (_sx_sasl_data_t) ctx;
01128 
01129     _sx_debug(ZONE, "in _sx_sasl_cb_get_secret (id 0x%x)", id);
01130     
01131     /* sanity check */
01132     if(conn == NULL || psecret == NULL || id != SASL_CB_PASS)
01133         return SASL_BADPARAM;
01134 
01135     *psecret = sd->psecret;
01136 
01137     return SASL_OK;
01138 }
01139 
01141 int sx_sasl_auth(sx_plugin_t p, sx_t s, char *appname, char *mech, char *user, char *pass) {
01142     _sx_sasl_t ctx = (_sx_sasl_t) p->private;
01143     _sx_sasl_data_t sd;
01144     char *buf, *out, *ext_id;
01145     int i, ret, buflen, outlen, ns;
01146     sasl_security_properties_t sec_props;
01147     nad_t nad;
01148 #ifdef _WIN32
01149     static sasl_callback_t win32_callbacks[2] = {
01150         {SASL_CB_GETPATH, &_sx_sasl_getpath, NULL},
01151         {SASL_CB_LIST_END, NULL, NULL}};
01152 #endif
01153 
01154     assert((int) (p != NULL));
01155     assert((int) (s != NULL));
01156     assert((int) (appname != NULL));
01157     assert((int) (mech != NULL));
01158 
01159     if(s->type != type_CLIENT || s->state != state_STREAM) {
01160         _sx_debug(ZONE, "need client in stream state for sasl auth");
01161         return 1;
01162      }
01163     
01164     /* startup */
01165 #ifdef _WIN32
01166     ret = sasl_client_init(win32_callbacks);
01167 #else
01168     ret = sasl_client_init(NULL);
01169 #endif
01170     if(ret != SASL_OK) {
01171         _sx_debug(ZONE, "sasl_client_init() failed (%s), not authing", sasl_errstring(ret, NULL, NULL));
01172         return 1;
01173     }
01174 
01175     sd = (_sx_sasl_data_t) calloc(1, sizeof(struct _sx_sasl_data_st));
01176 
01177     if(user != NULL)
01178         sd->user = strdup(user);
01179 
01180     if(pass != NULL) {
01181         sd->psecret = (sasl_secret_t *) malloc(sizeof(sasl_secret_t) + strlen(pass) + 1);
01182         strcpy(sd->psecret->data, pass);
01183         sd->psecret->len = strlen(pass);
01184     }
01185 
01186     sd->callbacks=calloc(sizeof(sasl_callback_t),4);
01187 
01188     /* authentication name callback */
01189     sd->callbacks[0].id = SASL_CB_AUTHNAME;
01190     sd->callbacks[0].proc = &_sx_sasl_cb_get_simple;
01191     sd->callbacks[0].context = (void *) sd;
01192 
01193     /* password callback */
01194     sd->callbacks[1].id = SASL_CB_PASS;
01195     sd->callbacks[1].proc = &_sx_sasl_cb_get_secret;
01196     sd->callbacks[1].context = (void *) sd;
01197 
01198     /* user identity callback */
01199     sd->callbacks[2].id = SASL_CB_USER;
01200     sd->callbacks[2].proc = &_sx_sasl_cb_get_simple;
01201     sd->callbacks[2].context = (void *) sd;
01202 
01203     /* end of callbacks */
01204     sd->callbacks[3].id = SASL_CB_LIST_END;
01205     sd->callbacks[3].proc = NULL;
01206     sd->callbacks[3].context = NULL;
01207 
01208     /* handshake start */
01209     ret = sasl_client_new(appname, (s->req_to != NULL) ? s->req_to : "", NULL, NULL, sd->callbacks, 0, &sd->sasl);
01210     if(ret != SASL_OK) {
01211         _sx_debug(ZONE, "sasl_client_new failed, (%s), not authing", sasl_errstring(ret, NULL, NULL));
01212 
01213         if (sd->user != NULL) free(sd->user);
01214         if (sd->psecret != NULL) free(sd->psecret);
01215         free(sd->callbacks);
01216         free(sd);
01217 
01218         return 1;
01219     }
01220 
01221     /* get external data from the ssl plugin */
01222     ext_id = NULL;
01223 #ifdef HAVE_SSL
01224     for(i = 0; i < s->env->nplugins; i++)
01225         if(s->env->plugins[i]->magic == SX_SSL_MAGIC && s->plugin_data[s->env->plugins[i]->index] != NULL)
01226             ext_id = ((_sx_ssl_conn_t) s->plugin_data[s->env->plugins[i]->index])->external_id;
01227 
01228     /* !!! XXX certs */
01229     /*
01230     if(ext != NULL) {
01231         ext->external_id = strdup("foo");
01232         ext->external_ssf = 20;
01233     }
01234     */
01235 
01236     /* if we've got some, setup for external auth */
01237     if(ext_id != NULL) {
01238         ret = sasl_setprop(sd->sasl, SASL_AUTH_EXTERNAL, ext_id);
01239         if(ret == SASL_OK) ret = sasl_setprop(sd->sasl, SASL_SSF_EXTERNAL, &s->ssf);
01240     }
01241 #endif /* HAVE_SSL */
01242 
01243     /* setup security properties */
01244     sec_props = ctx->sec_props;
01245     if(s->ssf > 0)
01246         /* if we're already encrypted, then no security layers */
01247         sec_props.max_ssf = 0;
01248 
01249     ret = sasl_setprop(sd->sasl, SASL_SEC_PROPS, &sec_props);
01250     if(ret != SASL_OK) {
01251         _sx_debug(ZONE, "sasl_setprop failed (%s), not authing", sasl_errstring(ret, NULL, NULL));
01252 
01253         sasl_dispose(&sd->sasl);
01254 
01255         if (sd->user != NULL) free(sd->user);
01256         if (sd->psecret != NULL) free(sd->psecret);
01257         free(sd->callbacks);
01258         free(sd);
01259 
01260         return 1;
01261     }
01262 
01263     /* handshake start */
01264     ret = sasl_client_start(sd->sasl, mech, NULL, (const char **) &out, &outlen, NULL);
01265     if(ret != SASL_OK && ret != SASL_CONTINUE) {
01266         _sx_debug(ZONE, "sasl_client_start failed (%s), not authing", sasl_errstring(ret, NULL, NULL));
01267 
01268         sasl_dispose(&sd->sasl);
01269 
01270         if (sd->user != NULL) free(sd->user);
01271         if (sd->psecret != NULL) free(sd->psecret);
01272         free(sd->callbacks);
01273         free(sd);
01274 
01275         return 1;
01276     }
01277 
01278     /* save userdata */
01279     s->plugin_data[p->index] = (void *) sd;
01280 
01281     /* in progress */
01282     _sx_debug(ZONE, "sending auth request to server, mech '%s': %.*s", mech, outlen, out);
01283 
01284     /* encode the challenge */
01285     _sx_sasl_encode(out, outlen, &buf, &buflen);
01286 
01287     /* build the nad */
01288     nad = nad_new();
01289     ns = nad_add_namespace(nad, uri_SASL, NULL);
01290 
01291     nad_append_elem(nad, ns, "auth", 0);
01292     nad_append_attr(nad, -1, "mechanism", mech);
01293     if(buf != NULL) {
01294         nad_append_cdata(nad, buf, buflen, 1);
01295         free(buf);
01296     }
01297 
01298     /* its away */
01299     sx_nad_write(s, nad);
01300 
01301     return 0;
01302 }