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 */
23
#error SCOD implementation is currently broken. If you have the guts, please fix it and share your changes.
27
#include "sx/scod/scod.h"
30
typedef struct _sx_sasl_st {
33
sx_sasl_callback_t cb;
39
/* mechanisms to offer */
40
#define SX_SASL_MECH_ANONYMOUS (1<<4)
41
#define SX_SASL_MECH_PLAIN (1<<5)
42
#define SX_SASL_MECH_DIGESTMD5 (1<<6)
44
/** move the stream to the auth state */
45
void _sx_sasl_open(sx_t s, scod_t sd) {
49
method = (char *) malloc(sizeof(char) * (strlen(sd->mech->name) + 6));
50
sprintf(method, "SASL/%s", sd->mech->name);
53
sx_auth(s, method, sd->authzid);
58
/** make the stream suthenticated second time round */
59
static void _sx_sasl_stream(sx_t s, sx_plugin_t p) {
60
scod_t sd = (scod_t) s->plugin_data[p->index];
62
/* do nothing the first time */
68
_sx_debug(ZONE, "not auth'd, not advancing to auth'd state yet");
72
/* otherwise, its auth time */
76
static void _sx_sasl_features(sx_t s, sx_plugin_t p, nad_t nad) {
77
_sx_sasl_t ctx = (_sx_sasl_t) p->private;
78
scod_t sd = (scod_t) s->plugin_data[p->index];
81
if(s->type != type_SERVER)
84
if(sd != NULL && sd->authd) {
85
_sx_debug(ZONE, "already auth'd, not offering sasl mechanisms");
89
if(!(s->flags & SX_SASL_OFFER)) {
90
_sx_debug(ZONE, "application didn't ask us to offer sasl, so we won't");
94
if(!(s->flags & SX_SASL_MECH_ANONYMOUS || s->flags & SX_SASL_MECH_PLAIN || s->flags & SX_SASL_MECH_DIGESTMD5)) {
95
_sx_debug(ZONE, "application didn't provide any mechanisms we can offer");
100
if((s->flags & SX_SSL_STARTTLS_REQUIRE) && s->ssf == 0) {
101
_sx_debug(ZONE, "ssl not established yet but the app requires it, not offering mechanisms");
106
_sx_debug(ZONE, "offering sasl mechanisms");
108
ns = nad_add_namespace(nad, uri_SASL, NULL);
109
nad_append_elem(nad, ns, "mechanisms", 1);
111
for(i = 0; i < ctx->scod_ctx->nmechs; i++)
112
if(ctx->scod_ctx->mechs[i]->flags == 0 || ctx->flags & ctx->scod_ctx->mechs[i]->flags) {
113
if((s->flags & SX_SASL_MECH_ANONYMOUS && strcmp("ANONYMOUS", ctx->scod_ctx->names[i]) == 0) ||
114
(s->flags & SX_SASL_MECH_PLAIN && strcmp("PLAIN", ctx->scod_ctx->names[i]) == 0) ||
115
(s->flags & SX_SASL_MECH_DIGESTMD5 && strcmp("DIGEST-MD5", ctx->scod_ctx->names[i]) == 0)) {
116
_sx_debug(ZONE, "offering mechanism: %s", ctx->scod_ctx->names[i]);
118
nad_append_elem(nad, ns, "mechanism", 2);
119
nad_append_cdata(nad, ctx->scod_ctx->names[i], strlen(ctx->scod_ctx->names[i]), 3);
124
/** utility: generate a success nad */
125
static nad_t _sx_sasl_success(sx_t s) {
129
nad = nad_new(s->nad_cache);
130
ns = nad_add_namespace(nad, uri_SASL, NULL);
132
nad_append_elem(nad, ns, "success", 0);
137
/** utility: generate a failure nad */
138
static nad_t _sx_sasl_failure(sx_t s, const char *err) {
142
nad = nad_new(s->nad_cache);
143
ns = nad_add_namespace(nad, uri_SASL, NULL);
145
nad_append_elem(nad, ns, "failure", 0);
147
nad_append_elem(nad, ns, err, 1);
152
/** utility: generate a challenge nad */
153
static nad_t _sx_sasl_challenge(sx_t s, char *data, int dlen) {
157
nad = nad_new(s->nad_cache);
158
ns = nad_add_namespace(nad, uri_SASL, NULL);
160
nad_append_elem(nad, ns, "challenge", 0);
162
nad_append_cdata(nad, data, dlen, 1);
167
/** utility: generate a response nad */
168
static nad_t _sx_sasl_response(sx_t s, char *data, int dlen) {
172
nad = nad_new(s->nad_cache);
173
ns = nad_add_namespace(nad, uri_SASL, NULL);
175
nad_append_elem(nad, ns, "response", 0);
177
nad_append_cdata(nad, data, dlen, 1);
182
/** utility: generate an abort nad */
183
static nad_t _sx_sasl_abort(sx_t s) {
187
nad = nad_new(s->nad_cache);
188
ns = nad_add_namespace(nad, uri_SASL, NULL);
190
nad_append_elem(nad, ns, "abort", 0);
195
/** utility: decode incoming handshake data */
196
static void _sx_sasl_decode(char *in, int inlen, char **out, int *outlen) {
197
*outlen = apr_base64_decode_len(in, inlen);
198
*out = (char *) malloc(sizeof(char) * (*outlen + 1));
199
apr_base64_decode(*out, in, inlen);
202
/** utility: encode outgoing handshake data */
203
static void _sx_sasl_encode(char *in, int inlen, char **out, int *outlen) {
204
*outlen = apr_base64_encode_len(inlen);
205
*out = (char *) malloc(sizeof(char) * *outlen);
206
apr_base64_encode(*out, in, inlen);
210
/** auth done, restart the stream */
211
static void _sx_sasl_notify_success(sx_t s, void *arg) {
212
_sx_debug(ZONE, "auth completed, resetting");
216
sx_server_init(s, s->flags);
219
/** process handshake packets from the client */
220
static void _sx_sasl_client_process(sx_t s, sx_plugin_t p, scod_t sd, char *mech, char *in, int inlen) {
221
_sx_sasl_t ctx = (_sx_sasl_t) p->private;
223
char *buf = NULL, *out = NULL;
224
int buflen, outlen, ret;
227
_sx_debug(ZONE, "auth request from client (mechanism=%s)", mech);
229
if(!((s->flags & SX_SASL_MECH_ANONYMOUS && strcmp("ANONYMOUS", mech) == 0) ||
230
(s->flags & SX_SASL_MECH_PLAIN && strcmp("PLAIN", mech) == 0) ||
231
(s->flags & SX_SASL_MECH_DIGESTMD5 && strcmp("DIGEST-MD5", mech) == 0))) {
232
_sx_debug(ZONE, "client requested mechanism that we didn't offer");
233
_sx_nad_write(s, _sx_sasl_failure(s, _sasl_err_INVALID_MECHANISM), 0);
238
sd = scod_new(ctx->scod_ctx, sd_type_SERVER);
240
_sx_debug(ZONE, "scod_new failed, no sasl for this conn");
241
_sx_nad_write(s, _sx_sasl_failure(s, _sasl_err_TEMPORARY_FAILURE), 0);
245
_sx_debug(ZONE, "sasl context initialised for %d", s->tag);
247
s->plugin_data[p->index] = (void *) sd;
249
sd->app_private = (void *) s;
253
assert((ctx->cb != NULL));
254
(ctx->cb)(sx_sasl_cb_GET_REALM, (void *) s, (void **) realm, s, ctx->cbarg);
256
/* decode and process */
257
_sx_sasl_decode(in, inlen, &buf, &buflen);
258
ret = scod_server_start(sd, mech, realm, buf, buflen, &out, &outlen);
262
/* decode and process */
263
_sx_sasl_decode(in, inlen, &buf, &buflen);
265
_sx_debug(ZONE, "response send before auth request enabling mechanism (decoded: %.*s)", buflen, buf);
266
_sx_nad_write(s, _sx_sasl_failure(s, _sasl_err_MECH_TOO_WEAK), 0);
267
if(buf != NULL) free(buf);
270
_sx_debug(ZONE, "response from client (decoded: %.*s)", buflen, buf);
271
ret = scod_server_step(sd, buf, buflen, &out, &outlen);
274
if(buf != NULL) free(buf);
277
if(ret == sd_SUCCESS) {
278
_sx_debug(ZONE, "sasl handshake completed");
280
if(out != NULL) free(out);
283
_sx_nad_write(s, _sx_sasl_success(s), 0);
285
/* set a notify on the success nad buffer */
286
((sx_buf_t) s->wbufq->front->data)->notify = _sx_sasl_notify_success;
287
((sx_buf_t) s->wbufq->front->data)->notify_arg = (void *) p;
293
if(ret == sd_CONTINUE) {
294
_sx_debug(ZONE, "sasl handshake in progress (challenge: %.*s)", outlen, out);
296
/* encode the challenge */
297
_sx_sasl_encode(out, outlen, &buf, &buflen);
299
if(out != NULL) free(out);
301
_sx_nad_write(s, _sx_sasl_challenge(s, buf, buflen), 0);
308
if(out != NULL) free(out);
311
_sx_debug(ZONE, "sasl handshake failed: (%d)", ret);
313
/* !!! check ret and flag error appropriately */
314
_sx_nad_write(s, _sx_sasl_failure(s, _sasl_err_MALFORMED_REQUEST), 0);
317
/** process handshake packets from the server */
318
static void _sx_sasl_server_process(sx_t s, sx_plugin_t p, scod_t sd, char *in, int inlen) {
320
int buflen, outlen, ret;
322
_sx_debug(ZONE, "challenge from client");
324
/* decode the response */
325
_sx_sasl_decode(in, inlen, &buf, &buflen);
327
/* process the data */
328
ret = scod_client_step(sd, buf, buflen, &out, &outlen);
329
if(buf != NULL) free(buf);
332
if(ret == sd_SUCCESS || ret == sd_CONTINUE) {
333
_sx_debug(ZONE, "sasl handshake in progress (response: %.*s)", outlen, out);
335
/* encode the response */
336
_sx_sasl_encode(out, outlen, &buf, &buflen);
338
if(out != NULL) free(out);
340
_sx_nad_write(s, _sx_sasl_response(s, buf, buflen), 0);
342
if(buf != NULL) free(buf);
347
if(out != NULL) free(out);
350
_sx_debug(ZONE, "sasl handshake aborted: (%d)", ret);
352
_sx_nad_write(s, _sx_sasl_abort(s), 0);
355
/** main nad processor */
356
static int _sx_sasl_process(sx_t s, sx_plugin_t p, nad_t nad) {
357
scod_t sd = (scod_t) s->plugin_data[p->index];
362
char *ns = NULL, *to = NULL, *from = NULL, *version = NULL;
364
/* only want sasl packets */
365
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)
368
/* quietly drop it if sasl is disabled, or if not ready */
369
if(s->state != state_STREAM) {
370
_sx_debug(ZONE, "not correct state for sasl, ignoring");
375
/* packets from the client */
376
if(s->type == type_SERVER) {
377
if(!(s->flags & SX_SASL_OFFER)) {
378
_sx_debug(ZONE, "they tried to do sasl, but we never offered it, ignoring");
384
if((s->flags & SX_SSL_STARTTLS_REQUIRE) && s->ssf == 0) {
385
_sx_debug(ZONE, "they tried to do sasl, but they have to do starttls first, ignoring");
392
if(NAD_ENAME_L(nad, 0) == 4 && strncmp("auth", NAD_ENAME(nad, 0), NAD_ENAME_L(nad, 0)) == 0) {
393
/* require mechanism */
394
if((attr = nad_find_attr(nad, 0, -1, "mechanism", NULL)) < 0) {
395
_sx_nad_write(s, _sx_sasl_failure(s, _sasl_err_INVALID_MECHANISM), 0);
401
snprintf(mech, 127, "%.*s", NAD_AVAL_L(nad, attr), NAD_AVAL(nad, attr));
404
_sx_sasl_client_process(s, p, sd, mech, NAD_CDATA(nad, 0), NAD_CDATA_L(nad, 0));
411
else if(NAD_ENAME_L(nad, 0) == 8 && strncmp("response", NAD_ENAME(nad, 0), NAD_ENAME_L(nad, 0)) == 0) {
413
_sx_sasl_client_process(s, p, sd, NULL, NAD_CDATA(nad, 0), NAD_CDATA_L(nad, 0));
420
else if(NAD_ENAME_L(nad, 0) == 5 && strncmp("abort", NAD_ENAME(nad, 0), NAD_ENAME_L(nad, 0)) == 0) {
421
_sx_debug(ZONE, "sasl handshake aborted");
423
_sx_nad_write(s, _sx_sasl_failure(s, _sasl_err_ABORTED), 0);
430
/* packets from the server */
431
else if(s->type == type_CLIENT) {
433
_sx_debug(ZONE, "got sasl client packets, but they never started sasl, ignoring");
439
if(NAD_ENAME_L(nad, 0) == 9 && strncmp("challenge", NAD_ENAME(nad, 0), NAD_ENAME_L(nad, 0)) == 0) {
441
_sx_sasl_server_process(s, p, sd, NAD_CDATA(nad, 0), NAD_CDATA_L(nad, 0));
448
else if(NAD_ENAME_L(nad, 0) == 7 && strncmp("success", NAD_ENAME(nad, 0), NAD_ENAME_L(nad, 0)) == 0) {
449
_sx_debug(ZONE, "sasl handshake completed, resetting");
452
/* save interesting bits */
455
if(s->ns != NULL) ns = strdup(s->ns);
457
if(s->req_to != NULL) to = strdup(s->req_to);
458
if(s->req_from != NULL) from = strdup(s->req_from);
459
if(s->req_version != NULL) version = strdup(s->req_version);
464
_sx_debug(ZONE, "restarting stream with sasl layer established");
466
/* second time round */
467
sx_client_init(s, flags, ns, to, from, version);
470
if(ns != NULL) free(ns);
471
if(to != NULL) free(to);
472
if(from != NULL) free(from);
473
if(version != NULL) free(version);
479
else if(NAD_ENAME_L(nad, 0) == 7 && strncmp("failure", NAD_ENAME(nad, 0), NAD_ENAME_L(nad, 0)) == 0) {
481
_sx_gen_error(sxe, SX_ERR_AUTH, "Authentication failed", NULL);
482
_sx_event(s, event_ERROR, (void *) &sxe);
487
s->plugin_data[p->index] = NULL;
494
/* invalid sasl command, quietly drop it */
495
_sx_debug(ZONE, "unknown sasl command '%.*s', ignoring", NAD_ENAME_L(nad, 0), NAD_ENAME(nad, 0));
502
static void _sx_sasl_free(sx_t s, sx_plugin_t p) {
503
scod_t sd = (scod_t) s->plugin_data[p->index];
508
_sx_debug(ZONE, "cleaning up conn state");
511
s->plugin_data[p->index] = NULL;
514
static int _sx_sasl_scod_callback(scod_t sd, int cb, void *arg, void **res, void *cbarg) {
515
_sx_sasl_t ctx = (_sx_sasl_t) cbarg;
519
case sd_cb_DIGEST_MD5_CHOOSE_REALM:
521
if(xhash_iter_first(realms))
522
xhash_iter_get(realms, (const char **) res, NULL);
528
assert((ctx->cb != NULL));
529
return (ctx->cb)(sx_sasl_cb_GET_PASS, arg, res, NULL, ctx->cbarg);
531
case sd_cb_CHECK_PASS:
532
assert((ctx->cb != NULL));
533
return (ctx->cb)(sx_sasl_cb_CHECK_PASS, arg, res, NULL, ctx->cbarg);
535
case sd_cb_CHECK_AUTHZID:
536
assert((ctx->cb != NULL));
537
return (ctx->cb)(sx_sasl_cb_CHECK_AUTHZID, arg, res, NULL, ctx->cbarg);
539
case sd_cb_ANONYMOUS_GEN_AUTHZID:
540
assert((ctx->cb != NULL));
541
return (ctx->cb)(sx_sasl_cb_GEN_AUTHZID, arg, res, NULL, ctx->cbarg);
550
static void _sx_sasl_unload(sx_plugin_t p) {
551
scod_ctx_free( ((_sx_sasl_t) p->private)->scod_ctx);
555
/** args: realm callback, cb arg, scod flags */
556
int sx_sasl_init(sx_env_t env, sx_plugin_t p, va_list args) {
557
sx_sasl_callback_t cb;
562
_sx_debug(ZONE, "initialising sasl plugin");
564
cb = va_arg(args, sx_sasl_callback_t);
565
cbarg = va_arg(args, void *);
566
flags = va_arg(args, int);
568
ctx = (_sx_sasl_t) calloc(1, sizeof(struct _sx_sasl_st));
574
ctx->scod_ctx = scod_ctx_new(_sx_sasl_scod_callback, ctx);
575
if(ctx->scod_ctx == NULL) {
576
_sx_debug(ZONE, "couldn't create scod context, disabling");
581
_sx_debug(ZONE, "sasl context initialised");
583
p->private = (void *) ctx;
585
p->unload = _sx_sasl_unload;
587
p->stream = _sx_sasl_stream;
588
p->features = _sx_sasl_features;
589
p->process = _sx_sasl_process;
591
p->free = _sx_sasl_free;
596
/** kick off the auth handshake */
597
int sx_sasl_auth(sx_plugin_t p, sx_t s, char *appname, char *mech, char *user, char *pass) {
598
_sx_sasl_t ctx = (_sx_sasl_t) p->private;
601
int ret, buflen, outlen, ns;
606
assert((mech != NULL));
607
assert((user != NULL));
608
assert((pass != NULL));
610
if(s->type != type_CLIENT || s->state != state_STREAM) {
611
_sx_debug(ZONE, "need client in stream state for sasl auth");
616
sd = scod_new(ctx->scod_ctx, sd_type_CLIENT);
618
_sx_debug(ZONE, "couldn't create scod instance, not authing");
622
/* handshake start */
623
ret = scod_client_start(sd, mech, user, user, pass, &out, &outlen);
624
if(ret != sd_SUCCESS && ret != sd_CONTINUE) {
625
_sx_debug(ZONE, "scod_client_start failed (%d), not authing", ret);
627
if(out != NULL) free(out);
635
s->plugin_data[p->index] = (void *) sd;
638
_sx_debug(ZONE, "sending auth request to server, mech '%s': %.*s", mech, outlen, out);
640
/* encode the challenge */
641
_sx_sasl_encode(out, outlen, &buf, &buflen);
645
nad = nad_new(s->nad_cache);
646
ns = nad_add_namespace(nad, uri_SASL, NULL);
648
nad_append_elem(nad, ns, "auth", 0);
649
nad_append_attr(nad, -1, "mechanism", mech);
651
nad_append_cdata(nad, buf, buflen, 1);
656
sx_nad_write(s, nad);