jabberd2
2.2.16
|
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 }