2
* jabberd - Jabber Open Source Server
3
* Copyright (c) 2002 Jeremie Miller, Thomas Muldowney,
4
* Ryan Eatmon, Robert Norris
6
* This program is free software; you can redistribute it and/or modify
7
* it under the terms of the GNU General Public License as published by
8
* the Free Software Foundation; either version 2 of the License, or
9
* (at your option) any later version.
11
* This program is distributed in the hope that it will be useful,
12
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the
14
* GNU General Public License for more details.
16
* You should have received a copy of the GNU General Public License
17
* along with this program; if not, write to the Free Software
18
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA02111-1307USA
21
/* SASL authentication handler */
26
#include <gsasl-mech.h>
30
typedef struct _sx_sasl_st {
34
sx_sasl_callback_t cb;
40
/* Per-session library handle. */
41
/* defined here to be able to get mechanism from handle */
46
Gsasl_mechanism *mech;
48
void *application_hook;
50
char *anonymous_token;
59
char *gssapi_display_name;
61
#ifndef GSASL_NO_OBSOLETE
63
void *application_data;
67
/** utility: generate a success nad */
68
static nad_t _sx_sasl_success(sx_t s) {
72
nad = nad_new(s->nad_cache);
73
ns = nad_add_namespace(nad, uri_SASL, NULL);
75
nad_append_elem(nad, ns, "success", 0);
80
/** utility: generate a failure nad */
81
static nad_t _sx_sasl_failure(sx_t s, const char *err) {
85
nad = nad_new(s->nad_cache);
86
ns = nad_add_namespace(nad, uri_SASL, NULL);
88
nad_append_elem(nad, ns, "failure", 0);
90
nad_append_elem(nad, ns, err, 1);
95
/** utility: generate a challenge nad */
96
static nad_t _sx_sasl_challenge(sx_t s, char *data, int dlen) {
100
nad = nad_new(s->nad_cache);
101
ns = nad_add_namespace(nad, uri_SASL, NULL);
103
nad_append_elem(nad, ns, "challenge", 0);
105
nad_append_cdata(nad, data, dlen, 1);
110
/** utility: generate a response nad */
111
static nad_t _sx_sasl_response(sx_t s, char *data, int dlen) {
115
nad = nad_new(s->nad_cache);
116
ns = nad_add_namespace(nad, uri_SASL, NULL);
118
nad_append_elem(nad, ns, "response", 0);
120
nad_append_cdata(nad, data, dlen, 1);
125
/** utility: generate an abort nad */
126
static nad_t _sx_sasl_abort(sx_t s) {
130
nad = nad_new(s->nad_cache);
131
ns = nad_add_namespace(nad, uri_SASL, NULL);
133
nad_append_elem(nad, ns, "abort", 0);
138
/** move the stream to the auth state */
139
void _sx_sasl_open(sx_t s, Gsasl_session *sd) {
140
char *method, *authzid;
141
const char *realm = NULL;
142
struct sx_sasl_creds_st creds = {NULL, NULL, NULL, NULL};
143
_sx_sasl_t ctx = gsasl_session_hook_get(sd);
146
method = (char *) malloc(sizeof(char) * (strlen(sd->mech->name) + 6));
147
sprintf(method, "SASL/%s", sd->mech->name);
149
/* and the authorization identifier */
150
creds.authzid = gsasl_property_fast(sd, GSASL_AUTHZID);
151
creds.authnid = gsasl_property_fast(sd, GSASL_AUTHID);
152
creds.realm = gsasl_property_fast(sd, GSASL_REALM);
154
if(0 && ctx && ctx->cb) { /* not supported yet */
155
if((ctx->cb)(sx_sasl_cb_CHECK_AUTHZID, &creds, NULL, s, ctx->cbarg)!=sx_sasl_ret_OK) {
156
_sx_debug(ZONE, "stream authzid: %s verification failed, not advancing to auth state", creds.authzid);
161
/* override unchecked arbitrary authzid */
162
if(creds.realm && creds.realm[0] != '\0') {
167
authzid = (char *) malloc(sizeof(char) * (strlen(creds.authnid) + strlen(realm) + 2));
168
sprintf(authzid, "%s@%s", creds.authnid, realm);
169
creds.authzid = authzid;
172
/* proceed stream to authenticated state */
173
sx_auth(s, method, creds.authzid);
176
if(authzid) free(authzid);
179
/** make the stream suthenticated second time round */
180
static void _sx_sasl_stream(sx_t s, sx_plugin_t p) {
181
Gsasl_session *sd = (Gsasl_session *) s->plugin_data[p->index];
183
/* do nothing the first time */
188
if(NULL == gsasl_property_fast(sd, GSASL_AUTHID)) {
189
_sx_debug(ZONE, "not auth'd, not advancing to auth'd state yet");
193
/* otherwise, its auth time */
194
_sx_sasl_open(s, sd);
197
static void _sx_sasl_features(sx_t s, sx_plugin_t p, nad_t nad) {
198
_sx_sasl_t ctx = (_sx_sasl_t) p->private;
199
Gsasl_session *sd = (Gsasl_session *) s->plugin_data[p->index];
201
char *mechs, *mech, *c;
203
if(s->type != type_SERVER)
207
_sx_debug(ZONE, "already auth'd, not offering sasl mechanisms");
211
if(!(s->flags & SX_SASL_OFFER)) {
212
_sx_debug(ZONE, "application didn't ask us to offer sasl, so we won't");
217
if((s->flags & SX_SSL_STARTTLS_REQUIRE) && s->ssf == 0) {
218
_sx_debug(ZONE, "ssl not established yet but the app requires it, not offering mechanisms");
223
_sx_debug(ZONE, "offering sasl mechanisms");
225
ret = gsasl_server_mechlist(ctx->gsasl_ctx, &mechs);
226
if(ret != GSASL_OK) {
227
_sx_debug(ZONE, "gsasl_server_mechlist failed (%d): %s, not offering sasl for this conn", ret, gsasl_strerror (ret));
233
while(mech != NULL) {
234
c = strchr(mech, ' ');
238
if ((ctx->cb)(sx_sasl_cb_CHECK_MECH, mech, NULL, s, ctx->cbarg)==sx_sasl_ret_OK) {
240
ns = nad_add_namespace(nad, uri_SASL, NULL);
241
nad_append_elem(nad, ns, "mechanisms", 1);
243
_sx_debug(ZONE, "offering mechanism: %s", mech);
245
nad_append_elem(nad, ns, "mechanism", 2);
246
nad_append_cdata(nad, mech, strlen(mech), 3);
259
/** auth done, restart the stream */
260
static void _sx_sasl_notify_success(sx_t s, void *arg) {
261
_sx_debug(ZONE, "auth completed, resetting");
265
sx_server_init(s, s->flags);
268
/** process handshake packets from the client */
269
static void _sx_sasl_client_process(sx_t s, sx_plugin_t p, Gsasl_session *sd, char *mech, char *in, int inlen) {
270
_sx_sasl_t ctx = (_sx_sasl_t) p->private;
271
char *buf = NULL, *out = NULL, *realm = NULL, *ext_id;
274
size_t buflen, outlen;
277
_sx_debug(ZONE, "auth request from client (mechanism=%s)", mech);
279
if(!gsasl_server_support_p(ctx->gsasl_ctx, mech)) {
280
_sx_debug(ZONE, "client requested mechanism (%s) that we didn't offer", mech);
281
_sx_nad_write(s, _sx_sasl_failure(s, _sasl_err_INVALID_MECHANISM), 0);
286
ret = gsasl_server_start(ctx->gsasl_ctx, mech, &sd);
287
if(ret != GSASL_OK) {
288
_sx_debug(ZONE, "gsasl_server_start failed, no sasl for this conn; (%d): %s", ret, gsasl_strerror(ret));
289
_sx_nad_write(s, _sx_sasl_failure(s, _sasl_err_TEMPORARY_FAILURE), 0);
295
(ctx->cb)(sx_sasl_cb_GET_REALM, NULL, (void **) &realm, s, ctx->cbarg);
297
gsasl_session_hook_set(sd, (void *) ctx);
298
gsasl_property_set(sd, GSASL_SERVICE, ctx->appname);
299
gsasl_property_set(sd, GSASL_REALM, realm);
303
gethostname(hostname, 256);
304
hostname[255] = '\0';
305
gsasl_property_set(sd, GSASL_HOSTNAME, hostname);
307
/* get EXTERNAL data from the ssl plugin */
310
for(i = 0; i < s->env->nplugins; i++)
311
if(s->env->plugins[i]->magic == SX_SSL_MAGIC && s->plugin_data[s->env->plugins[i]->index] != NULL)
312
ext_id = ((_sx_ssl_conn_t) s->plugin_data[s->env->plugins[i]->index])->external_id;
314
/* if there is, store it for later */
316
ctx->ext_id = strdup(ext_id);
320
_sx_debug(ZONE, "sasl context initialised for %d", s->tag);
322
s->plugin_data[p->index] = (void *) sd;
324
if(strcmp(mech, "ANONYMOUS") == 0) {
326
* special case for SASL ANONYMOUS: ignore the initial
327
* response provided by the client and generate a random
328
* authid to use as the jid node for the user, as
329
* specified in XEP-0175
331
(ctx->cb)(sx_sasl_cb_GEN_AUTHZID, NULL, (void **)&out, s, ctx->cbarg);
333
buflen = strlen(buf);
335
/* decode and process */
336
gsasl_base64_from(in, inlen, &buf, &buflen);
339
ret = gsasl_step(sd, buf, buflen, &out, &outlen);
340
if(ret != GSASL_OK && ret != GSASL_NEEDS_MORE) {
341
_sx_debug(ZONE, "gsasl_step failed, no sasl for this conn; (%d): %s", ret, gsasl_strerror(ret));
342
_sx_nad_write(s, _sx_sasl_failure(s, _sasl_err_MALFORMED_REQUEST), 0);
343
if(out != NULL) free(out);
344
if(buf != NULL) free(buf);
350
/* decode and process */
351
gsasl_base64_from(in, inlen, &buf, &buflen);
353
_sx_debug(ZONE, "response send before auth request enabling mechanism (decoded: %.*s)", buflen, buf);
354
_sx_nad_write(s, _sx_sasl_failure(s, _sasl_err_MECH_TOO_WEAK), 0);
355
if(buf != NULL) free(buf);
358
_sx_debug(ZONE, "response from client (decoded: %.*s)", buflen, buf);
359
ret = gsasl_step(sd, buf, buflen, &out, &outlen);
362
if(buf != NULL) free(buf);
365
if(ret == GSASL_OK) {
366
_sx_debug(ZONE, "sasl handshake completed");
368
if(out != NULL) free(out);
371
_sx_nad_write(s, _sx_sasl_success(s), 0);
373
/* set a notify on the success nad buffer */
374
((sx_buf_t) s->wbufq->front->data)->notify = _sx_sasl_notify_success;
375
((sx_buf_t) s->wbufq->front->data)->notify_arg = (void *) p;
381
if(ret == GSASL_NEEDS_MORE) {
382
_sx_debug(ZONE, "sasl handshake in progress (challenge: %.*s)", outlen, out);
384
/* encode the challenge */
385
gsasl_base64_to(out, outlen, &buf, &buflen);
387
if(out != NULL) free(out);
389
_sx_nad_write(s, _sx_sasl_challenge(s, buf, buflen), 0);
396
if(out != NULL) free(out);
399
_sx_debug(ZONE, "sasl handshake failed; (%d): %s", ret, gsasl_strerror(ret));
401
/* !!! TODO XXX check ret and flag error appropriately */
402
_sx_nad_write(s, _sx_sasl_failure(s, _sasl_err_MALFORMED_REQUEST), 0);
405
/** process handshake packets from the server */
406
static void _sx_sasl_server_process(sx_t s, sx_plugin_t p, Gsasl_session *sd, char *in, int inlen) {
408
size_t buflen, outlen;
411
_sx_debug(ZONE, "data from client");
413
/* decode the response */
414
gsasl_base64_from(in, inlen, &buf, &buflen);
415
_sx_debug(ZONE, "decoded data: %.*s", buflen, buf);
417
/* process the data */
418
ret = gsasl_step(sd, buf, buflen, &out, &outlen);
419
if(buf != NULL) free(buf);
422
if(ret == GSASL_OK || ret == GSASL_NEEDS_MORE) {
423
_sx_debug(ZONE, "sasl handshake in progress (response: %.*s)", outlen, out);
425
/* encode the response */
426
gsasl_base64_to(out, outlen, &buf, &buflen);
428
if(out != NULL) free(out);
430
_sx_nad_write(s, _sx_sasl_response(s, buf, buflen), 0);
432
if(buf != NULL) free(buf);
437
if(out != NULL) free(out);
440
_sx_debug(ZONE, "sasl handshake aborted; (%d): %s", ret, gsasl_strerror(ret));
442
_sx_nad_write(s, _sx_sasl_abort(s), 0);
445
/** main nad processor */
446
static int _sx_sasl_process(sx_t s, sx_plugin_t p, nad_t nad) {
447
Gsasl_session *sd = (Gsasl_session *) s->plugin_data[p->index];
452
char *ns = NULL, *to = NULL, *from = NULL, *version = NULL;
454
/* only want sasl packets */
455
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)
458
/* quietly drop it if sasl is disabled, or if not ready */
459
if(s->state != state_STREAM) {
460
_sx_debug(ZONE, "not correct state for sasl, ignoring");
465
/* packets from the client */
466
if(s->type == type_SERVER) {
467
if(!(s->flags & SX_SASL_OFFER)) {
468
_sx_debug(ZONE, "they tried to do sasl, but we never offered it, ignoring");
474
if((s->flags & SX_SSL_STARTTLS_REQUIRE) && s->ssf == 0) {
475
_sx_debug(ZONE, "they tried to do sasl, but they have to do starttls first, ignoring");
482
if(NAD_ENAME_L(nad, 0) == 4 && strncmp("auth", NAD_ENAME(nad, 0), NAD_ENAME_L(nad, 0)) == 0) {
483
/* require mechanism */
484
if((attr = nad_find_attr(nad, 0, -1, "mechanism", NULL)) < 0) {
485
_sx_nad_write(s, _sx_sasl_failure(s, _sasl_err_INVALID_MECHANISM), 0);
491
snprintf(mech, 127, "%.*s", NAD_AVAL_L(nad, attr), NAD_AVAL(nad, attr));
494
_sx_sasl_client_process(s, p, sd, mech, NAD_CDATA(nad, 0), NAD_CDATA_L(nad, 0));
501
else if(NAD_ENAME_L(nad, 0) == 8 && strncmp("response", NAD_ENAME(nad, 0), NAD_ENAME_L(nad, 0)) == 0) {
503
_sx_sasl_client_process(s, p, sd, NULL, NAD_CDATA(nad, 0), NAD_CDATA_L(nad, 0));
510
else if(NAD_ENAME_L(nad, 0) == 5 && strncmp("abort", NAD_ENAME(nad, 0), NAD_ENAME_L(nad, 0)) == 0) {
511
_sx_debug(ZONE, "sasl handshake aborted");
513
_sx_nad_write(s, _sx_sasl_failure(s, _sasl_err_ABORTED), 0);
520
/* packets from the server */
521
else if(s->type == type_CLIENT) {
523
_sx_debug(ZONE, "got sasl client packets, but they never started sasl, ignoring");
529
if(NAD_ENAME_L(nad, 0) == 9 && strncmp("challenge", NAD_ENAME(nad, 0), NAD_ENAME_L(nad, 0)) == 0) {
531
_sx_sasl_server_process(s, p, sd, NAD_CDATA(nad, 0), NAD_CDATA_L(nad, 0));
538
else if(NAD_ENAME_L(nad, 0) == 7 && strncmp("success", NAD_ENAME(nad, 0), NAD_ENAME_L(nad, 0)) == 0) {
539
_sx_debug(ZONE, "sasl handshake completed, resetting");
542
/* save interesting bits */
545
if(s->ns != NULL) ns = strdup(s->ns);
547
if(s->req_to != NULL) to = strdup(s->req_to);
548
if(s->req_from != NULL) from = strdup(s->req_from);
549
if(s->req_version != NULL) version = strdup(s->req_version);
554
_sx_debug(ZONE, "restarting stream with sasl layer established");
556
/* second time round */
557
sx_client_init(s, flags, ns, to, from, version);
560
if(ns != NULL) free(ns);
561
if(to != NULL) free(to);
562
if(from != NULL) free(from);
563
if(version != NULL) free(version);
569
else if(NAD_ENAME_L(nad, 0) == 7 && strncmp("failure", NAD_ENAME(nad, 0), NAD_ENAME_L(nad, 0)) == 0) {
571
_sx_gen_error(sxe, SX_ERR_AUTH, "Authentication failed", NULL);
572
_sx_event(s, event_ERROR, (void *) &sxe);
577
s->plugin_data[p->index] = NULL;
584
/* invalid sasl command, quietly drop it */
585
_sx_debug(ZONE, "unknown sasl command '%.*s', ignoring", NAD_ENAME_L(nad, 0), NAD_ENAME(nad, 0));
592
static void _sx_sasl_free(sx_t s, sx_plugin_t p) {
593
Gsasl_session *sd = (Gsasl_session *) s->plugin_data[p->index];
598
_sx_debug(ZONE, "cleaning up conn state");
601
s->plugin_data[p->index] = NULL;
604
static int _sx_sasl_gsasl_callback(Gsasl *gsasl_ctx, Gsasl_session *sd, Gsasl_property prop) {
605
_sx_sasl_t ctx = gsasl_session_hook_get(sd);
606
struct sx_sasl_creds_st creds = {NULL, NULL, NULL, NULL};
609
_sx_debug(ZONE, "in _sx_sasl_gsasl_callback, property: %d", prop);
612
/* GSASL_AUTHID, GSASL_AUTHZID, GSASL_REALM */
613
assert((ctx->cb != NULL));
614
creds.authnid = gsasl_property_fast(sd, GSASL_AUTHID);
615
creds.realm = gsasl_property_fast(sd, GSASL_REALM);
616
if(!creds.authnid) return GSASL_NO_AUTHID;
617
if(!creds.realm) return GSASL_NO_AUTHZID;
618
if((ctx->cb)(sx_sasl_cb_GET_PASS, &creds, (void **)&value, NULL, ctx->cbarg) == sx_sasl_ret_OK) {
619
gsasl_property_set(sd, GSASL_PASSWORD, value);
621
return GSASL_NEEDS_MORE;
623
case GSASL_VALIDATE_SIMPLE:
624
/* GSASL_AUTHID, GSASL_AUTHZID, GSASL_PASSWORD */
625
assert((ctx->cb != NULL));
626
creds.authnid = gsasl_property_fast(sd, GSASL_AUTHID);
627
creds.realm = gsasl_property_fast(sd, GSASL_REALM);
628
creds.pass = gsasl_property_fast(sd, GSASL_PASSWORD);
629
if(!creds.authnid) return GSASL_NO_AUTHID;
630
if(!creds.realm) return GSASL_NO_AUTHZID;
631
if(!creds.pass) return GSASL_NO_PASSWORD;
632
if((ctx->cb)(sx_sasl_cb_CHECK_PASS, &creds, NULL, NULL, ctx->cbarg) == sx_sasl_ret_OK)
635
return GSASL_AUTHENTICATION_ERROR;
637
case GSASL_VALIDATE_ANONYMOUS:
638
/* GSASL_ANONYMOUS_TOKEN */
639
creds.authnid = gsasl_property_fast(sd, GSASL_ANONYMOUS_TOKEN);
640
if(!creds.authnid) return GSASL_NO_ANONYMOUS_TOKEN;
641
/* set token as authid for later use */
642
gsasl_property_set(sd, GSASL_AUTHID, creds.authnid);
645
case GSASL_VALIDATE_EXTERNAL:
647
if(ctx->ext_id != NULL) {
648
creds.authzid = gsasl_property_fast(sd, GSASL_AUTHZID);
649
if(creds.authzid != NULL && strcmp(ctx->ext_id, creds.authzid) == 0)
652
return GSASL_AUTHENTICATION_ERROR;
658
return GSASL_NO_CALLBACK;
661
static void _sx_sasl_unload(sx_plugin_t p) {
662
_sx_sasl_t ctx = (_sx_sasl_t) p->private;
664
if (ctx->gsasl_ctx != NULL) gsasl_done (ctx->gsasl_ctx);
665
if (ctx->appname != NULL) free(ctx->appname);
666
if (ctx->ext_id != NULL) free(ctx->ext_id);
667
if (ctx != NULL) free(ctx);
670
/** args: appname, callback, cb arg */
671
int sx_sasl_init(sx_env_t env, sx_plugin_t p, va_list args) {
673
sx_sasl_callback_t cb;
678
_sx_debug(ZONE, "initialising sasl plugin");
680
appname = va_arg(args, char *);
681
if(appname == NULL) {
682
_sx_debug(ZONE, "appname was NULL, failing");
686
cb = va_arg(args, sx_sasl_callback_t);
687
cbarg = va_arg(args, void *);
689
ctx = (_sx_sasl_t) calloc(1, sizeof(struct _sx_sasl_st));
691
ctx->appname = strdup(appname);
696
ret = gsasl_init(&ctx->gsasl_ctx);
697
if(ret != GSASL_OK) {
698
_sx_debug(ZONE, "couldn't initialize libgsasl (%d): %s", ret, gsasl_strerror (ret));
703
gsasl_callback_set (ctx->gsasl_ctx, &_sx_sasl_gsasl_callback);
705
_sx_debug(ZONE, "sasl context initialised");
707
p->private = (void *) ctx;
709
p->unload = _sx_sasl_unload;
711
p->stream = _sx_sasl_stream;
712
p->features = _sx_sasl_features;
713
p->process = _sx_sasl_process;
715
p->free = _sx_sasl_free;
720
/** kick off the auth handshake */
721
int sx_sasl_auth(sx_plugin_t p, sx_t s, char *appname, char *mech, char *user, char *pass) {
722
_sx_sasl_t ctx = (_sx_sasl_t) p->private;
727
size_t buflen, outlen;
732
assert((appname != NULL));
733
assert((mech != NULL));
734
assert((user != NULL));
735
assert((pass != NULL));
737
if(s->type != type_CLIENT || s->state != state_STREAM) {
738
_sx_debug(ZONE, "need client in stream state for sasl auth");
742
/* handshake start */
743
ret = gsasl_client_start(ctx->gsasl_ctx, mech, &sd);
744
if(ret != GSASL_OK) {
745
_sx_debug(ZONE, "gsasl_client_start failed, not authing; (%d): %s", ret, gsasl_strerror(ret));
752
gethostname(hostname, 256);
753
hostname[255] = '\0';
755
/* set user data in session handle */
756
gsasl_session_hook_set(sd, (void *) ctx);
757
gsasl_property_set(sd, GSASL_AUTHID, user);
758
gsasl_property_set(sd, GSASL_PASSWORD, pass);
759
gsasl_property_set(sd, GSASL_SERVICE, appname);
760
gsasl_property_set(sd, GSASL_HOSTNAME, hostname);
763
ret = gsasl_step(sd, NULL, 0, &out, &outlen);
764
if(ret != GSASL_OK && ret != GSASL_NEEDS_MORE) {
765
_sx_debug(ZONE, "gsasl_step failed, not authing; (%d): %s", ret, gsasl_strerror(ret));
773
s->plugin_data[p->index] = (void *) sd;
776
_sx_debug(ZONE, "sending auth request to server, mech '%s': %.*s", mech, outlen, out);
778
/* encode the challenge */
779
gsasl_base64_to(out, outlen, &buf, &buflen);
783
nad = nad_new(s->nad_cache);
784
ns = nad_add_namespace(nad, uri_SASL, NULL);
786
nad_append_elem(nad, ns, "auth", 0);
787
nad_append_attr(nad, -1, "mechanism", mech);
789
nad_append_cdata(nad, buf, buflen, 1);
794
sx_nad_write(s, nad);