jabberd2  2.2.16
sx/sasl_gsasl.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 #include "sx.h"
00024 #include "sasl.h"
00025 #include <gsasl.h>
00026 #include <gsasl-mech.h>
00027 #include <string.h>
00028 
00030 typedef struct _sx_sasl_st {
00031     char                        *appname;
00032     Gsasl                       *gsasl_ctx;
00033 
00034     sx_sasl_callback_t          cb;
00035     void                        *cbarg;
00036 
00037     char                        *ext_id[SX_CONN_EXTERNAL_ID_MAX_COUNT];
00038 } *_sx_sasl_t;
00039 
00040 /* Per-session library handle. */
00041 /* defined here to be able to get mechanism from handle */
00042 struct Gsasl_session
00043 {
00044   Gsasl *ctx;
00045   int clientp;
00046   Gsasl_mechanism *mech;
00047   void *mech_data;
00048   void *application_hook;
00049   /* Properties. */
00050   char *anonymous_token;
00051   char *authid;
00052   char *authzid;
00053   char *password;
00054   char *passcode;
00055   char *pin;
00056   char *suggestedpin;
00057   char *service;
00058   char *hostname;
00059   char *realm;
00060 #ifndef GSASL_NO_OBSOLETE
00061   /* Obsolete stuff. */
00062   void *application_data;
00063 #endif
00064 };
00065 
00066 /* another internal GSASL structures needed to hack into its internals */
00067 #define DIGEST_MD5_LENGTH 16
00068 
00069 struct digest_md5_challenge
00070 {
00071   size_t nrealms;
00072   char **realms;
00073   char *nonce;
00074   int qops;
00075   int stale;
00076   unsigned long servermaxbuf;
00077   int utf8;
00078   int ciphers;
00079 };
00080 typedef struct digest_md5_challenge digest_md5_challenge;
00081 
00082 enum digest_md5_qop
00083 {
00084   DIGEST_MD5_QOP_AUTH = 1,
00085   DIGEST_MD5_QOP_AUTH_INT = 2,
00086   DIGEST_MD5_QOP_AUTH_CONF = 4
00087 };
00088 typedef enum digest_md5_qop digest_md5_qop;
00089 
00090 enum digest_md5_cipher
00091 {
00092   DIGEST_MD5_CIPHER_DES = 1,
00093   DIGEST_MD5_CIPHER_3DES = 2,
00094   DIGEST_MD5_CIPHER_RC4 = 4,
00095   DIGEST_MD5_CIPHER_RC4_40 = 8,
00096   DIGEST_MD5_CIPHER_RC4_56 = 16,
00097   DIGEST_MD5_CIPHER_AES_CBC = 32
00098 };
00099 typedef enum digest_md5_cipher digest_md5_cipher;
00100 
00101 #define DIGEST_MD5_RESPONSE_LENGTH 32
00102 struct digest_md5_response
00103 {
00104   char *username;
00105   char *realm;
00106   char *nonce;
00107   char *cnonce;
00108   unsigned long nc;
00109   digest_md5_qop qop;
00110   char *digesturi;
00111   unsigned long clientmaxbuf;
00112   int utf8;
00113   digest_md5_cipher cipher;
00114   char *authzid;
00115   char response[DIGEST_MD5_RESPONSE_LENGTH + 1];
00116 };
00117 typedef struct digest_md5_response digest_md5_response;
00118 
00119 struct digest_md5_finish
00120 {
00121   char rspauth[DIGEST_MD5_RESPONSE_LENGTH + 1];
00122 };
00123 typedef struct digest_md5_finish digest_md5_finish;
00124 
00125 struct _Gsasl_digest_md5_server_state
00126 {
00127   int step;
00128   unsigned long readseqnum, sendseqnum;
00129   char secret[DIGEST_MD5_LENGTH];
00130   char kic[DIGEST_MD5_LENGTH];
00131   char kcc[DIGEST_MD5_LENGTH];
00132   char kis[DIGEST_MD5_LENGTH];
00133   char kcs[DIGEST_MD5_LENGTH];
00134   digest_md5_challenge challenge;
00135   digest_md5_response response;
00136   digest_md5_finish finish;
00137 };
00138 typedef struct _Gsasl_digest_md5_server_state _Gsasl_digest_md5_server_state;
00139 
00141 static nad_t _sx_sasl_success(sx_t s, char *data, int dlen) {
00142     nad_t nad;
00143     int ns;
00144 
00145     nad = nad_new();
00146     ns = nad_add_namespace(nad, uri_SASL, NULL);
00147 
00148     nad_append_elem(nad, ns, "success", 0);
00149     if(data != NULL)
00150         nad_append_cdata(nad, data, dlen, 1);
00151 
00152     return nad;
00153 }
00154 
00156 static nad_t _sx_sasl_failure(sx_t s, const char *err) {
00157     nad_t nad;
00158     int ns;
00159 
00160     nad = nad_new();
00161     ns = nad_add_namespace(nad, uri_SASL, NULL);
00162 
00163     nad_append_elem(nad, ns, "failure", 0);
00164     if(err != NULL)
00165         nad_append_elem(nad, ns, err, 1);
00166 
00167     return nad;
00168 }
00169 
00171 static nad_t _sx_sasl_challenge(sx_t s, char *data, int dlen) {
00172     nad_t nad;
00173     int ns;
00174 
00175     nad = nad_new();
00176     ns = nad_add_namespace(nad, uri_SASL, NULL);
00177 
00178     nad_append_elem(nad, ns, "challenge", 0);
00179     if(data != NULL)
00180         nad_append_cdata(nad, data, dlen, 1);
00181 
00182     return nad;
00183 }
00184 
00186 static nad_t _sx_sasl_response(sx_t s, char *data, int dlen) {
00187     nad_t nad;
00188     int ns;
00189 
00190     nad = nad_new();
00191     ns = nad_add_namespace(nad, uri_SASL, NULL);
00192 
00193     nad_append_elem(nad, ns, "response", 0);
00194     if(data != NULL)
00195         nad_append_cdata(nad, data, dlen, 1);
00196 
00197     return nad;
00198 }
00199 
00201 static nad_t _sx_sasl_abort(sx_t s) {
00202     nad_t nad;
00203     int ns;
00204 
00205     nad = nad_new();
00206     ns = nad_add_namespace(nad, uri_SASL, NULL);
00207 
00208     nad_append_elem(nad, ns, "abort", 0);
00209 
00210     return nad;
00211 }
00212 
00213 static int _sx_sasl_wio(sx_t s, sx_plugin_t p, sx_buf_t buf) {
00214     sx_error_t sxe;
00215     size_t len;
00216     int ret;
00217     char *out;
00218     Gsasl_session *sd = (Gsasl_session *) s->plugin_data[p->index];
00219 
00220     _sx_debug(ZONE, "doing sasl encode");
00221 
00222     /* encode the output */
00223     ret = gsasl_encode(sd, buf->data, buf->len, &out, &len);
00224     if (ret != GSASL_OK) {
00225         _sx_debug(ZONE, "gsasl_encode failed (%d): %s", ret, gsasl_strerror (ret));
00226         /* Fatal error */
00227         _sx_gen_error(sxe, SX_ERR_AUTH, "SASL Stream encoding failed", (char*) gsasl_strerror (ret));
00228         _sx_event(s, event_ERROR, (void *) &sxe);
00229         return -1;
00230     }
00231     
00232     /* replace the buffer */
00233     _sx_buffer_set(buf, out, len, NULL);
00234     free(out);
00235 
00236     _sx_debug(ZONE, "%d bytes encoded for sasl channel", buf->len);
00237     
00238     return 1;
00239 }
00240 
00241 static int _sx_sasl_rio(sx_t s, sx_plugin_t p, sx_buf_t buf) {
00242     sx_error_t sxe;
00243     size_t len;
00244     int ret;
00245     char *out;
00246     Gsasl_session *sd = (Gsasl_session *) s->plugin_data[p->index];
00247 
00248     _sx_debug(ZONE, "doing sasl decode");
00249 
00250     /* decode the input */
00251     ret = gsasl_decode(sd, buf->data, buf->len, &out, &len);
00252     if (ret != GSASL_OK) {
00253         _sx_debug(ZONE, "gsasl_decode failed (%d): %s", ret, gsasl_strerror (ret));
00254         /* Fatal error */
00255         _sx_gen_error(sxe, SX_ERR_AUTH, "SASL Stream decoding failed", (char*) gsasl_strerror (ret));
00256         _sx_event(s, event_ERROR, (void *) &sxe);
00257         return -1;
00258     }
00259     
00260     /* replace the buffer */
00261     _sx_buffer_set(buf, out, len, NULL);
00262     free(out);
00263 
00264     _sx_debug(ZONE, "%d bytes decoded from sasl channel", len);
00265     
00266     return 1;
00267 }
00268 
00270 void _sx_sasl_open(sx_t s, Gsasl_session *sd) {
00271     char *method, *authzid;
00272     const char *realm = NULL;
00273     struct sx_sasl_creds_st creds = {NULL, NULL, NULL, NULL};
00274     _sx_sasl_t ctx = gsasl_session_hook_get(sd);
00275     
00276     /* get the method */
00277     method = (char *) malloc(sizeof(char) * (strlen(sd->mech->name) + 6));
00278     sprintf(method, "SASL/%s", sd->mech->name);
00279 
00280     /* and the authorization identifier */
00281     creds.authzid = gsasl_property_fast(sd, GSASL_AUTHZID);
00282     creds.authnid = gsasl_property_fast(sd, GSASL_AUTHID);
00283     creds.realm   = gsasl_property_fast(sd, GSASL_REALM);
00284 
00285     if(0 && ctx && ctx->cb) { /* not supported yet */
00286         if((ctx->cb)(sx_sasl_cb_CHECK_AUTHZID, &creds, NULL, s, ctx->cbarg)!=sx_sasl_ret_OK) {
00287             _sx_debug(ZONE, "stream authzid: %s verification failed, not advancing to auth state", creds.authzid);
00288             free(method);
00289             return;
00290         }
00291     } else if (NULL != gsasl_property_fast(sd, GSASL_GSSAPI_DISPLAY_NAME)) {
00292         creds.authzid = strdup(gsasl_property_fast(sd, GSASL_GSSAPI_DISPLAY_NAME));
00293         authzid = NULL;
00294     } else {
00295         /* override unchecked arbitrary authzid */
00296         if(creds.realm && creds.realm[0] != '\0') {
00297             realm = creds.realm;
00298         } else {
00299             realm = s->req_to;
00300         }
00301         authzid = (char *) malloc(sizeof(char) * (strlen(creds.authnid) + strlen(realm) + 2));
00302         sprintf(authzid, "%s@%s", creds.authnid, realm);
00303         creds.authzid = authzid;
00304     }
00305 
00306     /* proceed stream to authenticated state */
00307     sx_auth(s, method, creds.authzid);
00308 
00309     free(method);
00310     if(authzid) free(authzid);
00311 }
00312 
00314 static void _sx_sasl_stream(sx_t s, sx_plugin_t p) {
00315     Gsasl_session *sd = (Gsasl_session *) s->plugin_data[p->index];
00316 
00317     /* do nothing the first time */
00318     if(sd == NULL)
00319         return;
00320 
00321     /* are we auth'd? */
00322     if(NULL == gsasl_property_fast(sd, GSASL_AUTHID)) {
00323         _sx_debug(ZONE, "not auth'd, not advancing to auth'd state yet");
00324         return;
00325     }
00326 
00327     /* otherwise, its auth time */
00328     _sx_sasl_open(s, sd);
00329 }
00330 
00331 static void _sx_sasl_features(sx_t s, sx_plugin_t p, nad_t nad) {
00332     _sx_sasl_t ctx = (_sx_sasl_t) p->private;
00333     Gsasl_session *sd = (Gsasl_session *) s->plugin_data[p->index];
00334     int nmechs, ret;
00335     char *mechs, *mech, *c;
00336 
00337     if(s->type != type_SERVER)
00338         return;
00339 
00340     if(sd != NULL) {
00341         _sx_debug(ZONE, "already auth'd, not offering sasl mechanisms");
00342         return;
00343     }
00344 
00345     if(!(s->flags & SX_SASL_OFFER)) {
00346         _sx_debug(ZONE, "application didn't ask us to offer sasl, so we won't");
00347         return;
00348     }
00349 
00350 #ifdef HAVE_SSL
00351     if((s->flags & SX_SSL_STARTTLS_REQUIRE) && s->ssf == 0) {
00352         _sx_debug(ZONE, "ssl not established yet but the app requires it, not offering mechanisms");
00353         return;
00354     }
00355 #endif
00356     
00357     _sx_debug(ZONE, "offering sasl mechanisms");
00358     
00359     ret = gsasl_server_mechlist(ctx->gsasl_ctx, &mechs);
00360     if(ret != GSASL_OK) {
00361         _sx_debug(ZONE, "gsasl_server_mechlist failed (%d): %s, not offering sasl for this conn", ret, gsasl_strerror (ret));
00362         return;
00363     }
00364 
00365     mech = mechs;
00366     nmechs = 0;
00367     while(mech != NULL) {
00368         c = strchr(mech, ' ');
00369         if(c != NULL)
00370             *c = '\0';
00371 
00372         if ((ctx->cb)(sx_sasl_cb_CHECK_MECH, mech, NULL, s, ctx->cbarg)==sx_sasl_ret_OK) {
00373             if (nmechs == 0) {
00374                 int ns = nad_add_namespace(nad, uri_SASL, NULL);
00375                 nad_append_elem(nad, ns, "mechanisms", 1);
00376             }
00377             _sx_debug(ZONE, "offering mechanism: %s", mech);
00378 
00379             nad_append_elem(nad, -1 /*ns*/, "mechanism", 2);
00380             nad_append_cdata(nad, mech, strlen(mech), 3);
00381             nmechs++;
00382         }
00383 
00384         if(c == NULL)
00385             mech = NULL;
00386         else
00387             mech = ++c;
00388     }
00389     
00390     free(mechs);
00391 }
00392 
00394 static void _sx_sasl_notify_success(sx_t s, void *arg) {
00395     sx_plugin_t p = (sx_plugin_t) arg;
00396 
00397     _sx_chain_io_plugin(s, p);
00398     _sx_debug(ZONE, "auth completed, resetting");
00399 
00400     _sx_reset(s);
00401 
00402     sx_server_init(s, s->flags);
00403 }
00404 
00406 static void _sx_sasl_client_process(sx_t s, sx_plugin_t p, Gsasl_session *sd, char *mech, char *in, int inlen) {
00407     _sx_sasl_t ctx = (_sx_sasl_t) p->private;
00408     char *buf = NULL, *out = NULL, *realm = NULL, **ext_id;
00409     char hostname[256];
00410     int ret;
00411 #ifdef HAVE_SSL
00412     int i;
00413 #endif
00414     size_t buflen, outlen;
00415 
00416     if(mech != NULL) {
00417         _sx_debug(ZONE, "auth request from client (mechanism=%s)", mech);
00418 
00419         if(!gsasl_server_support_p(ctx->gsasl_ctx, mech)) {
00420              _sx_debug(ZONE, "client requested mechanism (%s) that we didn't offer", mech);
00421              _sx_nad_write(s, _sx_sasl_failure(s, _sasl_err_INVALID_MECHANISM), 0);
00422              return;
00423         }
00424 
00425         /* startup */
00426         ret = gsasl_server_start(ctx->gsasl_ctx, mech, &sd);
00427         if(ret != GSASL_OK) {
00428             _sx_debug(ZONE, "gsasl_server_start failed, no sasl for this conn; (%d): %s", ret, gsasl_strerror(ret));
00429             _sx_nad_write(s, _sx_sasl_failure(s, _sasl_err_TEMPORARY_FAILURE), 0);
00430             return;
00431         }
00432 
00433         /* get the realm */
00434         if(ctx->cb != NULL)
00435             (ctx->cb)(sx_sasl_cb_GET_REALM, NULL, (void **) &realm, s, ctx->cbarg);
00436         
00437         gsasl_session_hook_set(sd, (void *) ctx);
00438         gsasl_property_set(sd, GSASL_SERVICE, ctx->appname);
00439         gsasl_property_set(sd, GSASL_REALM, realm);
00440 
00441         /* allow only qop=auth for DIGEST-MD5 */
00442         if (!strncmp(mech, "DIGEST-MD5", 10)) {
00443             _Gsasl_digest_md5_server_state *state = sd->mech_data;
00444             state->challenge.qops = DIGEST_MD5_QOP_AUTH;
00445         }
00446 
00447         /* get hostname */
00448         hostname[0] = '\0';
00449         gethostname(hostname, 256);
00450         hostname[255] = '\0';
00451         gsasl_property_set(sd, GSASL_HOSTNAME, hostname);
00452 
00453         /* get EXTERNAL data from the ssl plugin */
00454         ext_id = NULL;
00455 #ifdef HAVE_SSL
00456         for(i = 0; i < s->env->nplugins; i++)
00457             if(s->env->plugins[i]->magic == SX_SSL_MAGIC && s->plugin_data[s->env->plugins[i]->index] != NULL)
00458                 ext_id = ((_sx_ssl_conn_t) s->plugin_data[s->env->plugins[i]->index])->external_id;
00459         if (ext_id != NULL) {
00460             //_sx_debug(ZONE, "sasl context ext id '%s'", ext_id);
00461             /* if there is, store it for later */
00462             for (i = 0; i < SX_CONN_EXTERNAL_ID_MAX_COUNT; i++)
00463                 if (ext_id[i] != NULL) {
00464                     ctx->ext_id[i] = strdup(ext_id[i]);
00465                 } else {
00466                     ctx->ext_id[i] = NULL;
00467                     break;
00468                 }
00469         }
00470 #endif
00471 
00472         _sx_debug(ZONE, "sasl context initialised for %d", s->tag);
00473 
00474         s->plugin_data[p->index] = (void *) sd;
00475 
00476         if(strcmp(mech, "ANONYMOUS") == 0) {
00477             /*
00478              * special case for SASL ANONYMOUS: ignore the initial
00479              * response provided by the client and generate a random
00480              * authid to use as the jid node for the user, as
00481              * specified in XEP-0175
00482              */
00483             (ctx->cb)(sx_sasl_cb_GEN_AUTHZID, NULL, (void **)&out, s, ctx->cbarg);
00484             buf = strdup(out);
00485             buflen = strlen(buf);
00486         } else if (strstr(in, "<") != NULL && strncmp(in, "=", strstr(in, "<") - in ) == 0) {
00487             /* XXX The above check is hackish, but `in` is just weird */
00488             /* This is a special case for SASL External c2s. See XEP-0178 */
00489             _sx_debug(ZONE, "gsasl auth string is empty");
00490             buf = strdup("");
00491             buflen = strlen(buf);
00492         } else {
00493             /* decode and process */
00494             ret = gsasl_base64_from(in, inlen, &buf, &buflen);
00495             if (ret != GSASL_OK) {
00496                 _sx_debug(ZONE, "gsasl_base64_from failed, no sasl for this conn; (%d): %s", ret, gsasl_strerror(ret));
00497                 _sx_nad_write(s, _sx_sasl_failure(s, _sasl_err_INCORRECT_ENCODING), 0);
00498                 if(buf != NULL) free(buf);
00499                 return;
00500             }
00501         }
00502 
00503         ret = gsasl_step(sd, buf, buflen, &out, &outlen);
00504         if(ret != GSASL_OK && ret != GSASL_NEEDS_MORE) {
00505             _sx_debug(ZONE, "gsasl_step failed, no sasl for this conn; (%d): %s", ret, gsasl_strerror(ret));
00506             _sx_nad_write(s, _sx_sasl_failure(s, _sasl_err_MALFORMED_REQUEST), 0);
00507             if(out != NULL) free(out);
00508             if(buf != NULL) free(buf);
00509             return;
00510         }
00511     }
00512 
00513     else {
00514         /* decode and process */
00515         ret = gsasl_base64_from(in, inlen, &buf, &buflen);
00516         if (ret != GSASL_OK) {
00517             _sx_debug(ZONE, "gsasl_base64_from failed, no sasl for this conn; (%d): %s", ret, gsasl_strerror(ret));
00518             _sx_nad_write(s, _sx_sasl_failure(s, _sasl_err_INCORRECT_ENCODING), 0);
00519             return;
00520         }
00521 
00522         if(!sd) {
00523             _sx_debug(ZONE, "response send before auth request enabling mechanism (decoded: %.*s)", buflen, buf);
00524             _sx_nad_write(s, _sx_sasl_failure(s, _sasl_err_MECH_TOO_WEAK), 0);
00525             if(buf != NULL) free(buf);
00526             return;
00527         }
00528         _sx_debug(ZONE, "response from client (decoded: %.*s)", buflen, buf);
00529         ret = gsasl_step(sd, buf, buflen, &out, &outlen);
00530     }
00531 
00532     if(buf != NULL) free(buf);
00533 
00534     /* auth completed */
00535     if(ret == GSASL_OK) {
00536         _sx_debug(ZONE, "sasl handshake completed");
00537 
00538         /* encode the leftover response */
00539         ret = gsasl_base64_to(out, outlen, &buf, &buflen);
00540         if (ret == GSASL_OK) {
00541             /* send success */
00542             _sx_nad_write(s, _sx_sasl_success(s, buf, buflen), 0);
00543             free(buf);
00544 
00545             /* set a notify on the success nad buffer */
00546             ((sx_buf_t) s->wbufq->front->data)->notify = _sx_sasl_notify_success;
00547             ((sx_buf_t) s->wbufq->front->data)->notify_arg = (void *) p;
00548         }
00549         else {
00550             _sx_debug(ZONE, "gsasl_base64_to failed, no sasl for this conn; (%d): %s", ret, gsasl_strerror(ret));
00551             _sx_nad_write(s, _sx_sasl_failure(s, _sasl_err_INCORRECT_ENCODING), 0);
00552             if(buf != NULL) free(buf);
00553         }
00554 
00555         if(out != NULL) free(out);
00556 
00557         return;
00558     }
00559 
00560     /* in progress */
00561     if(ret == GSASL_NEEDS_MORE) {
00562         _sx_debug(ZONE, "sasl handshake in progress (challenge: %.*s)", outlen, out);
00563 
00564         /* encode the challenge */
00565         ret = gsasl_base64_to(out, outlen, &buf, &buflen);
00566         if (ret == GSASL_OK) {
00567             _sx_nad_write(s, _sx_sasl_challenge(s, buf, buflen), 0);
00568             free(buf);
00569         }
00570         else {
00571             _sx_debug(ZONE, "gsasl_base64_to failed, no sasl for this conn; (%d): %s", ret, gsasl_strerror(ret));
00572             _sx_nad_write(s, _sx_sasl_failure(s, _sasl_err_INCORRECT_ENCODING), 0);
00573             if(buf != NULL) free(buf);
00574         }
00575 
00576         if(out != NULL) free(out);
00577 
00578         return;
00579     }
00580 
00581     if(out != NULL) free(out);
00582 
00583     /* its over */
00584     _sx_debug(ZONE, "sasl handshake failed; (%d): %s", ret, gsasl_strerror(ret));
00585 
00586     /* !!! TODO XXX check ret and flag error appropriately */
00587     _sx_nad_write(s, _sx_sasl_failure(s, _sasl_err_MALFORMED_REQUEST), 0);
00588 }
00589 
00591 static void _sx_sasl_server_process(sx_t s, sx_plugin_t p, Gsasl_session *sd, char *in, int inlen) {
00592     char *buf = NULL, *out = NULL;
00593     size_t buflen, outlen;
00594     int ret;
00595 
00596     _sx_debug(ZONE, "data from client");
00597 
00598     /* decode the response */
00599     ret = gsasl_base64_from(in, inlen, &buf, &buflen);
00600 
00601     if (ret == GSASL_OK) {
00602         _sx_debug(ZONE, "decoded data: %.*s", buflen, buf);
00603     
00604         /* process the data */
00605         ret = gsasl_step(sd, buf, buflen, &out, &outlen);
00606         if(buf != NULL) free(buf); buf = NULL;
00607     
00608         /* in progress */
00609         if(ret == GSASL_OK || ret == GSASL_NEEDS_MORE) {
00610             _sx_debug(ZONE, "sasl handshake in progress (response: %.*s)", outlen, out);
00611     
00612             /* encode the response */
00613             ret = gsasl_base64_to(out, outlen, &buf, &buflen);
00614 
00615             if (ret == GSASL_OK) {
00616                 _sx_nad_write(s, _sx_sasl_response(s, buf, buflen), 0);
00617             }
00618 
00619             if(out != NULL) free(out);
00620             if(buf != NULL) free(buf);
00621     
00622             return;
00623         }
00624     }
00625     if(out != NULL) free(out);
00626     if(buf != NULL) free(buf);
00627 
00628     /* its over */
00629     _sx_debug(ZONE, "sasl handshake aborted; (%d): %s", ret, gsasl_strerror(ret));
00630 
00631     _sx_nad_write(s, _sx_sasl_abort(s), 0);
00632 }
00633 
00635 static int _sx_sasl_process(sx_t s, sx_plugin_t p, nad_t nad) {
00636     Gsasl_session *sd = (Gsasl_session *) s->plugin_data[p->index];
00637     int attr;
00638     char mech[128];
00639     sx_error_t sxe;
00640     int flags;
00641     char *ns = NULL, *to = NULL, *from = NULL, *version = NULL;
00642 
00643     /* only want sasl packets */
00644     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)
00645         return 1;
00646 
00647     /* quietly drop it if sasl is disabled, or if not ready */
00648     if(s->state != state_STREAM) {
00649         _sx_debug(ZONE, "not correct state for sasl, ignoring");
00650         nad_free(nad);
00651         return 0;
00652     }
00653 
00654     /* packets from the client */
00655     if(s->type == type_SERVER) {
00656         if(!(s->flags & SX_SASL_OFFER)) {
00657             _sx_debug(ZONE, "they tried to do sasl, but we never offered it, ignoring");
00658             nad_free(nad);
00659             return 0;
00660         }
00661 
00662 #ifdef HAVE_SSL
00663         if((s->flags & SX_SSL_STARTTLS_REQUIRE) && s->ssf == 0) {
00664             _sx_debug(ZONE, "they tried to do sasl, but they have to do starttls first, ignoring");
00665             nad_free(nad);
00666             return 0;
00667         }
00668 #endif
00669 
00670         /* auth */
00671         if(NAD_ENAME_L(nad, 0) == 4 && strncmp("auth", NAD_ENAME(nad, 0), NAD_ENAME_L(nad, 0)) == 0) {
00672             /* require mechanism */
00673             if((attr = nad_find_attr(nad, 0, -1, "mechanism", NULL)) < 0) {
00674                 _sx_nad_write(s, _sx_sasl_failure(s, _sasl_err_INVALID_MECHANISM), 0);
00675                 nad_free(nad);
00676                 return 0;
00677             }
00678 
00679             /* extract */
00680             snprintf(mech, 127, "%.*s", NAD_AVAL_L(nad, attr), NAD_AVAL(nad, attr));
00681 
00682             /* go */
00683             _sx_sasl_client_process(s, p, sd, mech, NAD_CDATA(nad, 0), NAD_CDATA_L(nad, 0));
00684 
00685             nad_free(nad);
00686             return 0;
00687         }
00688 
00689         /* response */
00690         else if(NAD_ENAME_L(nad, 0) == 8 && strncmp("response", NAD_ENAME(nad, 0), NAD_ENAME_L(nad, 0)) == 0) {
00691             /* process it */
00692             _sx_sasl_client_process(s, p, sd, NULL, NAD_CDATA(nad, 0), NAD_CDATA_L(nad, 0));
00693 
00694             nad_free(nad);
00695             return 0;
00696         }
00697 
00698         /* abort */
00699         else if(NAD_ENAME_L(nad, 0) == 5 && strncmp("abort", NAD_ENAME(nad, 0), NAD_ENAME_L(nad, 0)) == 0) {
00700             _sx_debug(ZONE, "sasl handshake aborted");
00701 
00702             _sx_nad_write(s, _sx_sasl_failure(s, _sasl_err_ABORTED), 0);
00703 
00704             nad_free(nad);
00705             return 0;
00706         }
00707     }
00708     
00709     /* packets from the server */
00710     else if(s->type == type_CLIENT) {
00711         if(sd == NULL) {
00712             _sx_debug(ZONE, "got sasl client packets, but they never started sasl, ignoring");
00713             nad_free(nad);
00714             return 0;
00715         }
00716 
00717         /* challenge */
00718         if(NAD_ENAME_L(nad, 0) == 9 && strncmp("challenge", NAD_ENAME(nad, 0), NAD_ENAME_L(nad, 0)) == 0) {
00719             /* process it */
00720             _sx_sasl_server_process(s, p, sd, NAD_CDATA(nad, 0), NAD_CDATA_L(nad, 0));
00721 
00722             nad_free(nad);
00723             return 0;
00724         }
00725 
00726         /* success */
00727         else if(NAD_ENAME_L(nad, 0) == 7 && strncmp("success", NAD_ENAME(nad, 0), NAD_ENAME_L(nad, 0)) == 0) {
00728             _sx_debug(ZONE, "sasl handshake completed, resetting");
00729             nad_free(nad);
00730 
00731             /* save interesting bits */
00732             flags = s->flags;
00733 
00734             if(s->ns != NULL) ns = strdup(s->ns);
00735 
00736             if(s->req_to != NULL) to = strdup(s->req_to);
00737             if(s->req_from != NULL) from = strdup(s->req_from);
00738             if(s->req_version != NULL) version = strdup(s->req_version);
00739 
00740             /* reset state */
00741             _sx_reset(s);
00742 
00743             _sx_debug(ZONE, "restarting stream with sasl layer established");
00744 
00745             /* second time round */
00746             sx_client_init(s, flags, ns, to, from, version);
00747 
00748             /* free bits */
00749             if(ns != NULL) free(ns);
00750             if(to != NULL) free(to);
00751             if(from != NULL) free(from);
00752             if(version != NULL) free(version);
00753 
00754             return 0;
00755         }
00756 
00757         /* failure */
00758         else if(NAD_ENAME_L(nad, 0) == 7 && strncmp("failure", NAD_ENAME(nad, 0), NAD_ENAME_L(nad, 0)) == 0) {
00759             /* fire the error */
00760             _sx_gen_error(sxe, SX_ERR_AUTH, "Authentication failed", NULL);
00761             _sx_event(s, event_ERROR, (void *) &sxe);
00762 
00763             /* cleanup */
00764             gsasl_finish(sd);
00765 
00766             s->plugin_data[p->index] = NULL;
00767 
00768             nad_free(nad);
00769             return 0;
00770         }
00771     }
00772 
00773     /* invalid sasl command, quietly drop it */
00774     _sx_debug(ZONE, "unknown sasl command '%.*s', ignoring", NAD_ENAME_L(nad, 0), NAD_ENAME(nad, 0));
00775 
00776     nad_free(nad);
00777     return 0;
00778 }
00779 
00781 static void _sx_sasl_free(sx_t s, sx_plugin_t p) {
00782     Gsasl_session *sd = (Gsasl_session *) s->plugin_data[p->index];
00783 
00784     if(sd == NULL)
00785         return;
00786 
00787     _sx_debug(ZONE, "cleaning up conn state");
00788 
00789     gsasl_finish(sd);
00790     s->plugin_data[p->index] = NULL;
00791 }
00792 
00793 static int _sx_sasl_gsasl_callback(Gsasl *gsasl_ctx, Gsasl_session *sd, Gsasl_property prop) {
00794     _sx_sasl_t ctx = gsasl_session_hook_get(sd);
00795     struct sx_sasl_creds_st creds = {NULL, NULL, NULL, NULL};
00796     char *value, *node, *host;
00797     int len, i;
00798 
00799     _sx_debug(ZONE, "in _sx_sasl_gsasl_callback, property: %d", prop);
00800     switch(prop) {
00801         case GSASL_PASSWORD:
00802             /* GSASL_AUTHID, GSASL_AUTHZID, GSASL_REALM */
00803             assert((ctx->cb != NULL));
00804             creds.authnid = gsasl_property_fast(sd, GSASL_AUTHID);
00805             creds.realm   = gsasl_property_fast(sd, GSASL_REALM);
00806             if(!creds.authnid) return GSASL_NO_AUTHID;
00807             if(!creds.realm) return GSASL_NO_AUTHZID;
00808             if((ctx->cb)(sx_sasl_cb_GET_PASS, &creds, (void **)&value, NULL, ctx->cbarg) == sx_sasl_ret_OK) {
00809                 gsasl_property_set(sd, GSASL_PASSWORD, value);
00810             }
00811             return GSASL_NEEDS_MORE;
00812 
00813         case GSASL_SERVICE:
00814             gsasl_property_set(sd, GSASL_SERVICE, "xmpp");
00815             return GSASL_OK;
00816 
00817         case GSASL_HOSTNAME:
00818             { 
00819                 char hostname[256];
00820                 /* get hostname */
00821                 hostname[0] = '\0';
00822                 gethostname(hostname, 256);
00823                 hostname[255] = '\0';
00824 
00825                 gsasl_property_set(sd, GSASL_HOSTNAME, hostname);
00826            }
00827            return GSASL_OK;
00828 
00829         case GSASL_VALIDATE_SIMPLE:
00830             /* GSASL_AUTHID, GSASL_AUTHZID, GSASL_PASSWORD */
00831             assert((ctx->cb != NULL));
00832             creds.authnid = gsasl_property_fast(sd, GSASL_AUTHID);
00833             creds.realm   = gsasl_property_fast(sd, GSASL_REALM);
00834             creds.pass    = gsasl_property_fast(sd, GSASL_PASSWORD);
00835             if(!creds.authnid) return GSASL_NO_AUTHID;
00836             if(!creds.realm) return GSASL_NO_AUTHZID;
00837             if(!creds.pass) return GSASL_NO_PASSWORD;
00838             if((ctx->cb)(sx_sasl_cb_CHECK_PASS, &creds, NULL, NULL, ctx->cbarg) == sx_sasl_ret_OK)
00839                 return GSASL_OK;
00840             else
00841                 return GSASL_AUTHENTICATION_ERROR;
00842 
00843         case GSASL_VALIDATE_GSSAPI:
00844             /* GSASL_AUTHZID, GSASL_GSSAPI_DISPLAY_NAME */
00845             creds.authnid = gsasl_property_fast(sd, GSASL_GSSAPI_DISPLAY_NAME);
00846             if(!creds.authnid) return GSASL_NO_AUTHID;
00847             creds.authzid = gsasl_property_fast(sd, GSASL_AUTHZID);
00848             if(!creds.authzid) return GSASL_NO_AUTHZID;
00849             gsasl_property_set(sd, GSASL_AUTHID, creds.authnid);
00850             return GSASL_OK;
00851 
00852         case GSASL_VALIDATE_ANONYMOUS:
00853             /* GSASL_ANONYMOUS_TOKEN */
00854             creds.authnid = gsasl_property_fast(sd, GSASL_ANONYMOUS_TOKEN);
00855             if(!creds.authnid) return GSASL_NO_ANONYMOUS_TOKEN;
00856             /* set token as authid for later use */
00857             gsasl_property_set(sd, GSASL_AUTHID, creds.authnid);
00858             return GSASL_OK;
00859 
00860         case GSASL_VALIDATE_EXTERNAL:
00861             /* GSASL_AUTHID */
00862             creds.authzid = gsasl_property_fast(sd, GSASL_AUTHZID);
00863             _sx_debug(ZONE, "sasl external");
00864             _sx_debug(ZONE, "sasl creds.authzid is '%s'", creds.authzid);
00865 
00866             for (i = 0; i < SX_CONN_EXTERNAL_ID_MAX_COUNT; i++) {
00867                 if (ctx->ext_id[i] == NULL)
00868                     break;
00869                 _sx_debug(ZONE, "sasl ext_id(%d) is '%s'", i, ctx->ext_id[i]);
00870                 /* XXX hackish.. detect c2s by existance of @ */
00871                 value = strstr(ctx->ext_id[i], "@");
00872 
00873                 if(value == NULL && creds.authzid != NULL && strcmp(ctx->ext_id[i], creds.authzid) == 0) {
00874                     // s2s connection and it's valid
00875                     /* TODO Handle wildcards and other thigs from XEP-0178 */
00876                     _sx_debug(ZONE, "sasl ctx->ext_id doesn't have '@' in it. Assuming s2s");
00877                     return GSASL_OK;
00878                 }
00879                 if(value != NULL &&
00880                     ((creds.authzid != NULL && strcmp(ctx->ext_id[i], creds.authzid) == 0) ||
00881                      (creds.authzid == NULL)) ) {
00882                     // c2s connection
00883                     // creds.authzid == NULL condition is from XEP-0178 '=' auth reply
00884 
00885                     // This should be freed by gsasl_finish() but I'm not sure
00886                     // node  = authnid
00887                     len = value - ctx->ext_id[i];
00888                     node = (char *) malloc(sizeof(char) * (len + 1)); // + null termination
00889                     strncpy(node, ctx->ext_id[i], len);
00890                     node[len] = '\0'; // null terminate the string
00891                     // host = realm
00892                     len = strlen(value) - 1 + 1; // - the @ + null termination
00893                     host = (char *) malloc(sizeof(char) * (len));
00894                     strcpy(host, value + 1); // skip the @
00895                     gsasl_property_set(sd, GSASL_AUTHID, node);
00896                     gsasl_property_set(sd, GSASL_REALM, host);
00897                     return GSASL_OK;
00898                 }
00899 
00900             }
00901             return GSASL_AUTHENTICATION_ERROR;
00902 
00903         default:
00904             break;
00905     }
00906 
00907     return GSASL_NO_CALLBACK;
00908 }
00909 
00910 static void _sx_sasl_unload(sx_plugin_t p) {
00911     _sx_sasl_t ctx = (_sx_sasl_t) p->private;
00912     int i;
00913 
00914     if (ctx->gsasl_ctx != NULL) gsasl_done (ctx->gsasl_ctx);
00915     if (ctx->appname != NULL) free(ctx->appname);
00916     for (i = 0; i < SX_CONN_EXTERNAL_ID_MAX_COUNT; i++)
00917         if(ctx->ext_id[i] != NULL)
00918             free(ctx->ext_id[i]);
00919         else
00920             break;
00921 
00922     if (ctx != NULL) free(ctx);
00923 }
00924 
00926 int sx_sasl_init(sx_env_t env, sx_plugin_t p, va_list args) {
00927     char *appname;
00928     sx_sasl_callback_t cb;
00929     void *cbarg;
00930     _sx_sasl_t ctx;
00931     int ret, i;
00932 
00933     _sx_debug(ZONE, "initialising sasl plugin");
00934 
00935     appname = va_arg(args, char *);
00936     if(appname == NULL) {
00937         _sx_debug(ZONE, "appname was NULL, failing");
00938         return 1;
00939     }
00940 
00941     cb = va_arg(args, sx_sasl_callback_t);
00942     cbarg = va_arg(args, void *);
00943 
00944     ctx = (_sx_sasl_t) calloc(1, sizeof(struct _sx_sasl_st));
00945 
00946     ctx->appname = strdup(appname);
00947     ctx->cb = cb;
00948     ctx->cbarg = cbarg;
00949     for (i = 0; i < SX_CONN_EXTERNAL_ID_MAX_COUNT; i++)
00950         ctx->ext_id[i] = NULL;
00951 
00952     ret = gsasl_init(&ctx->gsasl_ctx);
00953     if(ret != GSASL_OK) {
00954         _sx_debug(ZONE, "couldn't initialize libgsasl (%d): %s", ret, gsasl_strerror (ret));
00955         free(ctx);
00956         return 1;
00957     }
00958 
00959     gsasl_callback_set (ctx->gsasl_ctx, &_sx_sasl_gsasl_callback);
00960 
00961     _sx_debug(ZONE, "sasl context initialised");
00962 
00963     p->private = (void *) ctx;
00964 
00965     p->unload = _sx_sasl_unload;
00966     p->wio = _sx_sasl_wio;
00967     p->rio = _sx_sasl_rio;
00968 
00969     p->stream = _sx_sasl_stream;
00970     p->features = _sx_sasl_features;
00971     p->process = _sx_sasl_process;
00972 
00973     p->free = _sx_sasl_free;
00974 
00975     return 0;
00976 }
00977 
00979 int sx_sasl_auth(sx_plugin_t p, sx_t s, char *appname, char *mech, char *user, char *pass) {
00980     _sx_sasl_t ctx = (_sx_sasl_t) p->private;
00981     Gsasl_session *sd;
00982     char *buf = NULL, *out = NULL;
00983     char hostname[256];
00984     int ret, ns;
00985     size_t buflen, outlen;
00986     nad_t nad;
00987 
00988     assert((p != NULL));
00989     assert((s != NULL));
00990     assert((appname != NULL));
00991     assert((mech != NULL));
00992     assert((user != NULL));
00993     assert((pass != NULL));
00994 
00995     if(s->type != type_CLIENT || s->state != state_STREAM) {
00996         _sx_debug(ZONE, "need client in stream state for sasl auth");
00997         return 1;
00998      }
00999     
01000     /* handshake start */
01001     ret = gsasl_client_start(ctx->gsasl_ctx, mech, &sd);
01002     if(ret != GSASL_OK) {
01003         _sx_debug(ZONE, "gsasl_client_start failed, not authing; (%d): %s", ret, gsasl_strerror(ret));
01004 
01005         return 1;
01006     }
01007 
01008     /* get hostname */
01009     hostname[0] = '\0';
01010     gethostname(hostname, 256);
01011     hostname[255] = '\0';
01012 
01013     /* set user data in session handle */
01014     gsasl_session_hook_set(sd, (void *) ctx);
01015     gsasl_property_set(sd, GSASL_AUTHID, user);
01016     gsasl_property_set(sd, GSASL_PASSWORD, pass);
01017     gsasl_property_set(sd, GSASL_SERVICE, appname);
01018     gsasl_property_set(sd, GSASL_HOSTNAME, hostname);
01019 
01020     /* handshake step */
01021     ret = gsasl_step(sd, NULL, 0, &out, &outlen);
01022     if(ret != GSASL_OK && ret != GSASL_NEEDS_MORE) {
01023         _sx_debug(ZONE, "gsasl_step failed, not authing; (%d): %s", ret, gsasl_strerror(ret));
01024 
01025         gsasl_finish(sd);
01026 
01027         return 1;
01028     }
01029 
01030     /* save userdata */
01031     s->plugin_data[p->index] = (void *) sd;
01032 
01033     /* in progress */
01034     _sx_debug(ZONE, "sending auth request to server, mech '%s': %.*s", mech, outlen, out);
01035 
01036     /* encode the challenge */
01037     ret = gsasl_base64_to(out, outlen, &buf, &buflen);
01038     if(ret != GSASL_OK) {
01039         _sx_debug(ZONE, "gsasl_base64_to failed, not authing; (%d): %s", ret, gsasl_strerror(ret));
01040 
01041         gsasl_finish(sd);
01042 
01043         if (out != NULL) free(out);
01044         return 1;
01045     }
01046     free(out);
01047 
01048     /* build the nad */
01049     nad = nad_new();
01050     ns = nad_add_namespace(nad, uri_SASL, NULL);
01051 
01052     nad_append_elem(nad, ns, "auth", 0);
01053     nad_append_attr(nad, -1, "mechanism", mech);
01054     if(buf != NULL) {
01055         nad_append_cdata(nad, buf, buflen, 1);
01056         free(buf);
01057     }
01058 
01059     /* its away */
01060     sx_nad_write(s, nad);
01061 
01062     return 0;
01063 }