~ubuntu-branches/ubuntu/oneiric/jabberd2/oneiric-security

« back to all changes in this revision

Viewing changes to sx/sasl_gsasl.c

  • Committer: Bazaar Package Importer
  • Author(s): Nicolai Spohrer
  • Date: 2008-08-12 16:13:43 UTC
  • mfrom: (1.1.3 upstream) (0.1.2 sid)
  • Revision ID: james.westby@ubuntu.com-20080812161343-6trz3r97dtevxd17
Tags: 2.2.1-1ubuntu1
* Merge with Debian unstable (LP: #257130), remaining changes:
  - debian/control:
    + Modify Maintainer field as per spec
    + Depend on libdb4.6-dev instead of libdb4.4-dev
    + Added Conflicts and Replaces: ..., jabber for jabberd2
  - debian/rules: Added libtoolize call (jabberd2 ships with
     an older ltmain.sh version that conflicts with the
     current libtool version)
  - debian/init: create /var/run/jabber directory with correct
     permissions
* Dropped changes:
  - Debian already depends on libpq-dev

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * jabberd - Jabber Open Source Server
 
3
 * Copyright (c) 2002 Jeremie Miller, Thomas Muldowney,
 
4
 *                    Ryan Eatmon, Robert Norris
 
5
 *
 
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.
 
10
 *
 
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.
 
15
 *
 
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
 
19
 */
 
20
 
 
21
/* SASL authentication handler */
 
22
 
 
23
#include "sx.h"
 
24
#include "sasl.h"
 
25
#include <gsasl.h>
 
26
#include <gsasl-mech.h>
 
27
#include <string.h>
 
28
 
 
29
/** our context */
 
30
typedef struct _sx_sasl_st {
 
31
    char                        *appname;
 
32
    Gsasl                       *gsasl_ctx;
 
33
 
 
34
    sx_sasl_callback_t          cb;
 
35
    void                        *cbarg;
 
36
 
 
37
    char                        *ext_id;
 
38
} *_sx_sasl_t;
 
39
 
 
40
/* Per-session library handle. */
 
41
/* defined here to be able to get mechanism from handle */
 
42
struct Gsasl_session
 
43
{
 
44
  Gsasl *ctx;
 
45
  int clientp;
 
46
  Gsasl_mechanism *mech;
 
47
  void *mech_data;
 
48
  void *application_hook;
 
49
  /* Properties. */
 
50
  char *anonymous_token;
 
51
  char *authid;
 
52
  char *authzid;
 
53
  char *password;
 
54
  char *passcode;
 
55
  char *pin;
 
56
  char *suggestedpin;
 
57
  char *service;
 
58
  char *hostname;
 
59
  char *gssapi_display_name;
 
60
  char *realm;
 
61
#ifndef GSASL_NO_OBSOLETE
 
62
  /* Obsolete stuff. */
 
63
  void *application_data;
 
64
#endif
 
65
};
 
66
 
 
67
/** utility: generate a success nad */
 
68
static nad_t _sx_sasl_success(sx_t s) {
 
69
    nad_t nad;
 
70
    int ns;
 
71
 
 
72
    nad = nad_new(s->nad_cache);
 
73
    ns = nad_add_namespace(nad, uri_SASL, NULL);
 
74
 
 
75
    nad_append_elem(nad, ns, "success", 0);
 
76
 
 
77
    return nad;
 
78
}
 
79
 
 
80
/** utility: generate a failure nad */
 
81
static nad_t _sx_sasl_failure(sx_t s, const char *err) {
 
82
    nad_t nad;
 
83
    int ns;
 
84
 
 
85
    nad = nad_new(s->nad_cache);
 
86
    ns = nad_add_namespace(nad, uri_SASL, NULL);
 
87
 
 
88
    nad_append_elem(nad, ns, "failure", 0);
 
89
    if(err != NULL)
 
90
        nad_append_elem(nad, ns, err, 1);
 
91
 
 
92
    return nad;
 
93
}
 
94
 
 
95
/** utility: generate a challenge nad */
 
96
static nad_t _sx_sasl_challenge(sx_t s, char *data, int dlen) {
 
97
    nad_t nad;
 
98
    int ns;
 
99
 
 
100
    nad = nad_new(s->nad_cache);
 
101
    ns = nad_add_namespace(nad, uri_SASL, NULL);
 
102
 
 
103
    nad_append_elem(nad, ns, "challenge", 0);
 
104
    if(data != NULL)
 
105
        nad_append_cdata(nad, data, dlen, 1);
 
106
 
 
107
    return nad;
 
108
}
 
109
 
 
110
/** utility: generate a response nad */
 
111
static nad_t _sx_sasl_response(sx_t s, char *data, int dlen) {
 
112
    nad_t nad;
 
113
    int ns;
 
114
 
 
115
    nad = nad_new(s->nad_cache);
 
116
    ns = nad_add_namespace(nad, uri_SASL, NULL);
 
117
 
 
118
    nad_append_elem(nad, ns, "response", 0);
 
119
    if(data != NULL)
 
120
        nad_append_cdata(nad, data, dlen, 1);
 
121
 
 
122
    return nad;
 
123
}
 
124
 
 
125
/** utility: generate an abort nad */
 
126
static nad_t _sx_sasl_abort(sx_t s) {
 
127
    nad_t nad;
 
128
    int ns;
 
129
 
 
130
    nad = nad_new(s->nad_cache);
 
131
    ns = nad_add_namespace(nad, uri_SASL, NULL);
 
132
 
 
133
    nad_append_elem(nad, ns, "abort", 0);
 
134
 
 
135
    return nad;
 
136
}
 
137
 
 
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);
 
144
    
 
145
    /* get the method */
 
146
    method = (char *) malloc(sizeof(char) * (strlen(sd->mech->name) + 6));
 
147
    sprintf(method, "SASL/%s", sd->mech->name);
 
148
 
 
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);
 
153
 
 
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);
 
157
            free(method);
 
158
            return;
 
159
        }
 
160
    } else {
 
161
        /* override unchecked arbitrary authzid */
 
162
        if(creds.realm && creds.realm[0] != '\0') {
 
163
            realm = creds.realm;
 
164
        } else {
 
165
            realm = s->req_to;
 
166
        }
 
167
        authzid = (char *) malloc(sizeof(char) * (strlen(creds.authnid) + strlen(realm) + 2));
 
168
        sprintf(authzid, "%s@%s", creds.authnid, realm);
 
169
        creds.authzid = authzid;
 
170
    }
 
171
 
 
172
    /* proceed stream to authenticated state */
 
173
    sx_auth(s, method, creds.authzid);
 
174
 
 
175
    free(method);
 
176
    if(authzid) free(authzid);
 
177
}
 
178
 
 
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];
 
182
 
 
183
    /* do nothing the first time */
 
184
    if(sd == NULL)
 
185
        return;
 
186
 
 
187
    /* are we auth'd? */
 
188
    if(NULL == gsasl_property_fast(sd, GSASL_AUTHID)) {
 
189
        _sx_debug(ZONE, "not auth'd, not advancing to auth'd state yet");
 
190
        return;
 
191
    }
 
192
 
 
193
    /* otherwise, its auth time */
 
194
    _sx_sasl_open(s, sd);
 
195
}
 
196
 
 
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];
 
200
    int ns, nmechs, ret;
 
201
    char *mechs, *mech, *c;
 
202
 
 
203
    if(s->type != type_SERVER)
 
204
        return;
 
205
 
 
206
    if(sd != NULL) {
 
207
        _sx_debug(ZONE, "already auth'd, not offering sasl mechanisms");
 
208
        return;
 
209
    }
 
210
 
 
211
    if(!(s->flags & SX_SASL_OFFER)) {
 
212
        _sx_debug(ZONE, "application didn't ask us to offer sasl, so we won't");
 
213
        return;
 
214
    }
 
215
 
 
216
#ifdef HAVE_SSL
 
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");
 
219
        return;
 
220
    }
 
221
#endif
 
222
    
 
223
    _sx_debug(ZONE, "offering sasl mechanisms");
 
224
    
 
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));
 
228
        return;
 
229
    }
 
230
 
 
231
    mech = mechs;
 
232
    nmechs = 0;
 
233
    while(mech != NULL) {
 
234
        c = strchr(mech, ' ');
 
235
        if(c != NULL)
 
236
            *c = '\0';
 
237
 
 
238
        if ((ctx->cb)(sx_sasl_cb_CHECK_MECH, mech, NULL, s, ctx->cbarg)==sx_sasl_ret_OK) {
 
239
            if (nmechs == 0) {
 
240
                ns = nad_add_namespace(nad, uri_SASL, NULL);
 
241
                nad_append_elem(nad, ns, "mechanisms", 1);
 
242
            }
 
243
            _sx_debug(ZONE, "offering mechanism: %s", mech);
 
244
 
 
245
            nad_append_elem(nad, ns, "mechanism", 2);
 
246
            nad_append_cdata(nad, mech, strlen(mech), 3);
 
247
            nmechs++;
 
248
        }
 
249
 
 
250
        if(c == NULL)
 
251
            mech = NULL;
 
252
        else
 
253
            mech = ++c;
 
254
    }
 
255
    
 
256
    free(mechs);
 
257
}
 
258
 
 
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");
 
262
 
 
263
    _sx_reset(s);
 
264
 
 
265
    sx_server_init(s, s->flags);
 
266
}
 
267
 
 
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;
 
272
    char hostname[256];
 
273
    int ret, i;
 
274
    size_t buflen, outlen;
 
275
 
 
276
    if(mech != NULL) {
 
277
        _sx_debug(ZONE, "auth request from client (mechanism=%s)", mech);
 
278
 
 
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);
 
282
             return;
 
283
        }
 
284
 
 
285
        /* startup */
 
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);
 
290
            return;
 
291
        }
 
292
 
 
293
        /* get the realm */
 
294
        if(ctx->cb != NULL)
 
295
            (ctx->cb)(sx_sasl_cb_GET_REALM, NULL, (void **) &realm, s, ctx->cbarg);
 
296
        
 
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);
 
300
 
 
301
        /* get hostname */
 
302
        hostname[0] = '\0';
 
303
        gethostname(hostname, 256);
 
304
        hostname[255] = '\0';
 
305
        gsasl_property_set(sd, GSASL_HOSTNAME, hostname);
 
306
 
 
307
        /* get EXTERNAL data from the ssl plugin */
 
308
        ext_id = NULL;
 
309
#ifdef HAVE_SSL
 
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;
 
313
 
 
314
        /* if there is, store it for later */
 
315
        if(ext_id != NULL) {
 
316
            ctx->ext_id = strdup(ext_id);
 
317
        }
 
318
#endif
 
319
 
 
320
        _sx_debug(ZONE, "sasl context initialised for %d", s->tag);
 
321
 
 
322
        s->plugin_data[p->index] = (void *) sd;
 
323
 
 
324
        if(strcmp(mech, "ANONYMOUS") == 0) {
 
325
            /*
 
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
 
330
             */
 
331
            (ctx->cb)(sx_sasl_cb_GEN_AUTHZID, NULL, (void **)&out, s, ctx->cbarg);
 
332
            buf = strdup(out);
 
333
            buflen = strlen(buf);
 
334
        } else {
 
335
            /* decode and process */
 
336
            gsasl_base64_from(in, inlen, &buf, &buflen);
 
337
        }
 
338
 
 
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);
 
345
            return;
 
346
        }
 
347
    }
 
348
 
 
349
    else {
 
350
        /* decode and process */
 
351
        gsasl_base64_from(in, inlen, &buf, &buflen);
 
352
        if(!sd) {
 
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);
 
356
            return;
 
357
        }
 
358
        _sx_debug(ZONE, "response from client (decoded: %.*s)", buflen, buf);
 
359
        ret = gsasl_step(sd, buf, buflen, &out, &outlen);
 
360
    }
 
361
 
 
362
    if(buf != NULL) free(buf);
 
363
 
 
364
    /* auth completed */
 
365
    if(ret == GSASL_OK) {
 
366
        _sx_debug(ZONE, "sasl handshake completed");
 
367
 
 
368
        if(out != NULL) free(out);
 
369
 
 
370
        /* send success */
 
371
        _sx_nad_write(s, _sx_sasl_success(s), 0);
 
372
 
 
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;
 
376
 
 
377
        return;
 
378
    }
 
379
 
 
380
    /* in progress */
 
381
    if(ret == GSASL_NEEDS_MORE) {
 
382
        _sx_debug(ZONE, "sasl handshake in progress (challenge: %.*s)", outlen, out);
 
383
 
 
384
        /* encode the challenge */
 
385
        gsasl_base64_to(out, outlen, &buf, &buflen);
 
386
        
 
387
        if(out != NULL) free(out);
 
388
 
 
389
        _sx_nad_write(s, _sx_sasl_challenge(s, buf, buflen), 0);
 
390
 
 
391
        free(buf);
 
392
 
 
393
        return;
 
394
    }
 
395
 
 
396
    if(out != NULL) free(out);
 
397
 
 
398
    /* its over */
 
399
    _sx_debug(ZONE, "sasl handshake failed; (%d): %s", ret, gsasl_strerror(ret));
 
400
 
 
401
    /* !!! TODO XXX check ret and flag error appropriately */
 
402
    _sx_nad_write(s, _sx_sasl_failure(s, _sasl_err_MALFORMED_REQUEST), 0);
 
403
}
 
404
 
 
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) {
 
407
    char *buf, *out;
 
408
    size_t buflen, outlen;
 
409
    int ret;
 
410
 
 
411
    _sx_debug(ZONE, "data from client");
 
412
 
 
413
    /* decode the response */
 
414
    gsasl_base64_from(in, inlen, &buf, &buflen);
 
415
    _sx_debug(ZONE, "decoded data: %.*s", buflen, buf);
 
416
 
 
417
    /* process the data */
 
418
    ret = gsasl_step(sd, buf, buflen, &out, &outlen);
 
419
    if(buf != NULL) free(buf);
 
420
 
 
421
    /* in progress */
 
422
    if(ret == GSASL_OK || ret == GSASL_NEEDS_MORE) {
 
423
        _sx_debug(ZONE, "sasl handshake in progress (response: %.*s)", outlen, out);
 
424
 
 
425
        /* encode the response */
 
426
        gsasl_base64_to(out, outlen, &buf, &buflen);
 
427
 
 
428
        if(out != NULL) free(out);
 
429
 
 
430
        _sx_nad_write(s, _sx_sasl_response(s, buf, buflen), 0);
 
431
 
 
432
        if(buf != NULL) free(buf);
 
433
 
 
434
        return;
 
435
    }
 
436
 
 
437
    if(out != NULL) free(out);
 
438
 
 
439
    /* its over */
 
440
    _sx_debug(ZONE, "sasl handshake aborted; (%d): %s", ret, gsasl_strerror(ret));
 
441
 
 
442
    _sx_nad_write(s, _sx_sasl_abort(s), 0);
 
443
}
 
444
 
 
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];
 
448
    int attr;
 
449
    char mech[128];
 
450
    sx_error_t sxe;
 
451
    int flags;
 
452
    char *ns = NULL, *to = NULL, *from = NULL, *version = NULL;
 
453
 
 
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)
 
456
        return 1;
 
457
 
 
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");
 
461
        nad_free(nad);
 
462
        return 0;
 
463
    }
 
464
 
 
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");
 
469
            nad_free(nad);
 
470
            return 0;
 
471
        }
 
472
 
 
473
#ifdef HAVE_SSL
 
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");
 
476
            nad_free(nad);
 
477
            return 0;
 
478
        }
 
479
#endif
 
480
 
 
481
        /* auth */
 
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);
 
486
                nad_free(nad);
 
487
                return 0;
 
488
            }
 
489
 
 
490
            /* extract */
 
491
            snprintf(mech, 127, "%.*s", NAD_AVAL_L(nad, attr), NAD_AVAL(nad, attr));
 
492
 
 
493
            /* go */
 
494
            _sx_sasl_client_process(s, p, sd, mech, NAD_CDATA(nad, 0), NAD_CDATA_L(nad, 0));
 
495
 
 
496
            nad_free(nad);
 
497
            return 0;
 
498
        }
 
499
 
 
500
        /* response */
 
501
        else if(NAD_ENAME_L(nad, 0) == 8 && strncmp("response", NAD_ENAME(nad, 0), NAD_ENAME_L(nad, 0)) == 0) {
 
502
            /* process it */
 
503
            _sx_sasl_client_process(s, p, sd, NULL, NAD_CDATA(nad, 0), NAD_CDATA_L(nad, 0));
 
504
 
 
505
            nad_free(nad);
 
506
            return 0;
 
507
        }
 
508
 
 
509
        /* abort */
 
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");
 
512
 
 
513
            _sx_nad_write(s, _sx_sasl_failure(s, _sasl_err_ABORTED), 0);
 
514
 
 
515
            nad_free(nad);
 
516
            return 0;
 
517
        }
 
518
    }
 
519
    
 
520
    /* packets from the server */
 
521
    else if(s->type == type_CLIENT) {
 
522
        if(sd == NULL) {
 
523
            _sx_debug(ZONE, "got sasl client packets, but they never started sasl, ignoring");
 
524
            nad_free(nad);
 
525
            return 0;
 
526
        }
 
527
 
 
528
        /* challenge */
 
529
        if(NAD_ENAME_L(nad, 0) == 9 && strncmp("challenge", NAD_ENAME(nad, 0), NAD_ENAME_L(nad, 0)) == 0) {
 
530
            /* process it */
 
531
            _sx_sasl_server_process(s, p, sd, NAD_CDATA(nad, 0), NAD_CDATA_L(nad, 0));
 
532
 
 
533
            nad_free(nad);
 
534
            return 0;
 
535
        }
 
536
 
 
537
        /* success */
 
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");
 
540
            nad_free(nad);
 
541
 
 
542
            /* save interesting bits */
 
543
            flags = s->flags;
 
544
 
 
545
            if(s->ns != NULL) ns = strdup(s->ns);
 
546
 
 
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);
 
550
 
 
551
            /* reset state */
 
552
            _sx_reset(s);
 
553
 
 
554
            _sx_debug(ZONE, "restarting stream with sasl layer established");
 
555
 
 
556
            /* second time round */
 
557
            sx_client_init(s, flags, ns, to, from, version);
 
558
 
 
559
            /* free bits */
 
560
            if(ns != NULL) free(ns);
 
561
            if(to != NULL) free(to);
 
562
            if(from != NULL) free(from);
 
563
            if(version != NULL) free(version);
 
564
 
 
565
            return 0;
 
566
        }
 
567
 
 
568
        /* failure */
 
569
        else if(NAD_ENAME_L(nad, 0) == 7 && strncmp("failure", NAD_ENAME(nad, 0), NAD_ENAME_L(nad, 0)) == 0) {
 
570
            /* fire the error */
 
571
            _sx_gen_error(sxe, SX_ERR_AUTH, "Authentication failed", NULL);
 
572
            _sx_event(s, event_ERROR, (void *) &sxe);
 
573
 
 
574
            /* cleanup */
 
575
            gsasl_finish(sd);
 
576
 
 
577
            s->plugin_data[p->index] = NULL;
 
578
 
 
579
            nad_free(nad);
 
580
            return 0;
 
581
        }
 
582
    }
 
583
 
 
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));
 
586
 
 
587
    nad_free(nad);
 
588
    return 0;
 
589
}
 
590
 
 
591
/** cleanup */
 
592
static void _sx_sasl_free(sx_t s, sx_plugin_t p) {
 
593
    Gsasl_session *sd = (Gsasl_session *) s->plugin_data[p->index];
 
594
 
 
595
    if(sd == NULL)
 
596
        return;
 
597
 
 
598
    _sx_debug(ZONE, "cleaning up conn state");
 
599
 
 
600
    gsasl_finish(sd);
 
601
    s->plugin_data[p->index] = NULL;
 
602
}
 
603
 
 
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};
 
607
    char *value;
 
608
 
 
609
    _sx_debug(ZONE, "in _sx_sasl_gsasl_callback, property: %d", prop);
 
610
    switch(prop) {
 
611
        case GSASL_PASSWORD:
 
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);
 
620
            }
 
621
            return GSASL_NEEDS_MORE;
 
622
 
 
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)
 
633
                return GSASL_OK;
 
634
            else
 
635
                return GSASL_AUTHENTICATION_ERROR;
 
636
 
 
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);
 
643
            return GSASL_OK;
 
644
 
 
645
        case GSASL_VALIDATE_EXTERNAL:
 
646
            /* GSASL_AUTHID */
 
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)
 
650
                    return GSASL_OK;
 
651
            }
 
652
            return GSASL_AUTHENTICATION_ERROR;
 
653
 
 
654
        default:
 
655
            break;
 
656
    }
 
657
 
 
658
    return GSASL_NO_CALLBACK;
 
659
}
 
660
 
 
661
static void _sx_sasl_unload(sx_plugin_t p) {
 
662
    _sx_sasl_t ctx = (_sx_sasl_t) p->private;
 
663
 
 
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);
 
668
}
 
669
 
 
670
/** args: appname, callback, cb arg */
 
671
int sx_sasl_init(sx_env_t env, sx_plugin_t p, va_list args) {
 
672
    char *appname;
 
673
    sx_sasl_callback_t cb;
 
674
    void *cbarg;
 
675
    _sx_sasl_t ctx;
 
676
    int ret;
 
677
 
 
678
    _sx_debug(ZONE, "initialising sasl plugin");
 
679
 
 
680
    appname = va_arg(args, char *);
 
681
    if(appname == NULL) {
 
682
        _sx_debug(ZONE, "appname was NULL, failing");
 
683
        return 1;
 
684
    }
 
685
 
 
686
    cb = va_arg(args, sx_sasl_callback_t);
 
687
    cbarg = va_arg(args, void *);
 
688
 
 
689
    ctx = (_sx_sasl_t) calloc(1, sizeof(struct _sx_sasl_st));
 
690
 
 
691
    ctx->appname = strdup(appname);
 
692
    ctx->cb = cb;
 
693
    ctx->cbarg = cbarg;
 
694
    ctx->ext_id = NULL;
 
695
 
 
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));
 
699
        free(ctx);
 
700
        return 1;
 
701
    }
 
702
 
 
703
    gsasl_callback_set (ctx->gsasl_ctx, &_sx_sasl_gsasl_callback);
 
704
 
 
705
    _sx_debug(ZONE, "sasl context initialised");
 
706
 
 
707
    p->private = (void *) ctx;
 
708
 
 
709
    p->unload = _sx_sasl_unload;
 
710
 
 
711
    p->stream = _sx_sasl_stream;
 
712
    p->features = _sx_sasl_features;
 
713
    p->process = _sx_sasl_process;
 
714
 
 
715
    p->free = _sx_sasl_free;
 
716
 
 
717
    return 0;
 
718
}
 
719
 
 
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;
 
723
    Gsasl_session *sd;
 
724
    char *buf, *out;
 
725
    char hostname[256];
 
726
    int ret, ns;
 
727
    size_t buflen, outlen;
 
728
    nad_t nad;
 
729
 
 
730
    assert((p != NULL));
 
731
    assert((s != NULL));
 
732
    assert((appname != NULL));
 
733
    assert((mech != NULL));
 
734
    assert((user != NULL));
 
735
    assert((pass != NULL));
 
736
 
 
737
    if(s->type != type_CLIENT || s->state != state_STREAM) {
 
738
        _sx_debug(ZONE, "need client in stream state for sasl auth");
 
739
        return 1;
 
740
     }
 
741
    
 
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));
 
746
 
 
747
        return 1;
 
748
    }
 
749
 
 
750
    /* get hostname */
 
751
    hostname[0] = '\0';
 
752
    gethostname(hostname, 256);
 
753
    hostname[255] = '\0';
 
754
 
 
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);
 
761
 
 
762
    /* handshake step */
 
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));
 
766
 
 
767
        gsasl_finish(sd);
 
768
 
 
769
        return 1;
 
770
    }
 
771
 
 
772
    /* save userdata */
 
773
    s->plugin_data[p->index] = (void *) sd;
 
774
 
 
775
    /* in progress */
 
776
    _sx_debug(ZONE, "sending auth request to server, mech '%s': %.*s", mech, outlen, out);
 
777
 
 
778
    /* encode the challenge */
 
779
    gsasl_base64_to(out, outlen, &buf, &buflen);
 
780
    free(out);
 
781
 
 
782
    /* build the nad */
 
783
    nad = nad_new(s->nad_cache);
 
784
    ns = nad_add_namespace(nad, uri_SASL, NULL);
 
785
 
 
786
    nad_append_elem(nad, ns, "auth", 0);
 
787
    nad_append_attr(nad, -1, "mechanism", mech);
 
788
    if(buf != NULL) {
 
789
        nad_append_cdata(nad, buf, buflen, 1);
 
790
        free(buf);
 
791
    }
 
792
 
 
793
    /* its away */
 
794
    sx_nad_write(s, nad);
 
795
 
 
796
    return 0;
 
797
}