~ubuntu-branches/ubuntu/gutsy/cyrus-sasl2/gutsy-201105300151

« back to all changes in this revision

Viewing changes to lib/checkpw.c

  • Committer: Bazaar Package Importer
  • Author(s): Scott James Remnant
  • Date: 2006-11-27 12:59:40 UTC
  • mfrom: (1.1.2 upstream)
  • Revision ID: james.westby@ubuntu.com-20061127125940-na3q3g3tkydvyvgl
Tags: 2.1.22.dfsg1-4ubuntu1
* Merge from debian unstable, remaining changes:
  - remove stop links from rc0 and rc6
  - build against db4.3 instead of 4.2
  - build against heimdal-dev instead of libkrb5-dev
  - depend on, don't recommend, modules

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* SASL server API implementation
 
2
 * Rob Siemborski
 
3
 * Tim Martin
 
4
 * $Id: checkpw.c,v 1.73 2006/03/13 18:30:41 mel Exp $
 
5
 */
 
6
/* 
 
7
 * Copyright (c) 1998-2003 Carnegie Mellon University.  All rights reserved.
 
8
 *
 
9
 * Redistribution and use in source and binary forms, with or without
 
10
 * modification, are permitted provided that the following conditions
 
11
 * are met:
 
12
 *
 
13
 * 1. Redistributions of source code must retain the above copyright
 
14
 *    notice, this list of conditions and the following disclaimer. 
 
15
 *
 
16
 * 2. Redistributions in binary form must reproduce the above copyright
 
17
 *    notice, this list of conditions and the following disclaimer in
 
18
 *    the documentation and/or other materials provided with the
 
19
 *    distribution.
 
20
 *
 
21
 * 3. The name "Carnegie Mellon University" must not be used to
 
22
 *    endorse or promote products derived from this software without
 
23
 *    prior written permission. For permission or any other legal
 
24
 *    details, please contact  
 
25
 *      Office of Technology Transfer
 
26
 *      Carnegie Mellon University
 
27
 *      5000 Forbes Avenue
 
28
 *      Pittsburgh, PA  15213-3890
 
29
 *      (412) 268-4387, fax: (412) 268-7395
 
30
 *      tech-transfer@andrew.cmu.edu
 
31
 *
 
32
 * 4. Redistributions of any form whatsoever must retain the following
 
33
 *    acknowledgment:
 
34
 *    "This product includes software developed by Computing Services
 
35
 *     at Carnegie Mellon University (http://www.cmu.edu/computing/)."
 
36
 *
 
37
 * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
 
38
 * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
 
39
 * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
 
40
 * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 
41
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
 
42
 * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
 
43
 * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 
44
 */
 
45
 
 
46
#include <config.h>
 
47
 
 
48
/* checkpw stuff */
 
49
 
 
50
#include <stdio.h>
 
51
#include "sasl.h"
 
52
#include "saslutil.h"
 
53
#include "saslplug.h"
 
54
#include "saslint.h"
 
55
 
 
56
#include <assert.h>
 
57
#ifdef HAVE_UNISTD_H
 
58
#include <unistd.h>
 
59
#endif
 
60
#include <fcntl.h>
 
61
#ifdef USE_DOORS
 
62
#include <sys/mman.h>
 
63
#include <door.h>
 
64
#endif
 
65
 
 
66
#include <stdlib.h>
 
67
 
 
68
#ifndef WIN32
 
69
#include <strings.h>
 
70
#include <netdb.h>
 
71
#include <netinet/in.h>
 
72
#include <sys/un.h>
 
73
#else
 
74
#include <string.h>
 
75
#endif
 
76
 
 
77
#include <sys/types.h>
 
78
#include <ctype.h>
 
79
 
 
80
#ifdef HAVE_PWD_H
 
81
#include <pwd.h>
 
82
#endif /* HAVE_PWD_H */
 
83
#ifdef HAVE_SHADOW_H
 
84
#include <shadow.h>
 
85
#endif /* HAVE_SHADOW_H */
 
86
 
 
87
#if defined(HAVE_PWCHECK) || defined(HAVE_SASLAUTHD) || defined(HAVE_AUTHDAEMON)
 
88
# include <errno.h>
 
89
# include <sys/types.h>
 
90
# include <sys/socket.h>
 
91
# include <sys/un.h>
 
92
# ifdef HAVE_UNISTD_H
 
93
#  include <unistd.h>
 
94
# endif
 
95
#endif
 
96
 
 
97
 
 
98
/* we store the following secret to check plaintext passwords:
 
99
 *
 
100
 * <salt> \0 <secret>
 
101
 *
 
102
 * where <secret> = MD5(<salt>, "sasldb", <pass>)
 
103
 */
 
104
static int _sasl_make_plain_secret(const char *salt, 
 
105
                                   const char *passwd, size_t passlen,
 
106
                                   sasl_secret_t **secret)
 
107
{
 
108
    MD5_CTX ctx;
 
109
    unsigned sec_len = 16 + 1 + 16; /* salt + "\0" + hash */
 
110
 
 
111
    *secret = (sasl_secret_t *) sasl_ALLOC(sizeof(sasl_secret_t) +
 
112
                                           sec_len * sizeof(char));
 
113
    if (*secret == NULL) {
 
114
        return SASL_NOMEM;
 
115
    }
 
116
 
 
117
    _sasl_MD5Init(&ctx);
 
118
    _sasl_MD5Update(&ctx, salt, 16);
 
119
    _sasl_MD5Update(&ctx, "sasldb", 6);
 
120
    _sasl_MD5Update(&ctx, passwd, (unsigned int) passlen);
 
121
    memcpy((*secret)->data, salt, 16);
 
122
    (*secret)->data[16] = '\0';
 
123
    _sasl_MD5Final((*secret)->data + 17, &ctx);
 
124
    (*secret)->len = sec_len;
 
125
    
 
126
    return SASL_OK;
 
127
}
 
128
 
 
129
/* erase & dispose of a sasl_secret_t
 
130
 */
 
131
static int auxprop_verify_password(sasl_conn_t *conn,
 
132
                                   const char *userstr,
 
133
                                   const char *passwd,
 
134
                                   const char *service __attribute__((unused)),
 
135
                                   const char *user_realm __attribute__((unused)))
 
136
{
 
137
    int ret = SASL_FAIL;
 
138
    char *userid = NULL;
 
139
    char *realm = NULL;
 
140
    int result = SASL_OK;
 
141
    sasl_server_conn_t *sconn = (sasl_server_conn_t *)conn;
 
142
    const char *password_request[] = { SASL_AUX_PASSWORD,
 
143
                                       "*cmusaslsecretPLAIN",
 
144
                                       NULL };
 
145
    struct propval auxprop_values[3];
 
146
    
 
147
    if (!conn || !userstr)
 
148
        return SASL_BADPARAM;
 
149
 
 
150
    /* We need to clear any previous results and re-canonify to 
 
151
     * ensure correctness */
 
152
 
 
153
    prop_clear(sconn->sparams->propctx, 0);
 
154
        
 
155
    /* ensure its requested */
 
156
    result = prop_request(sconn->sparams->propctx, password_request);
 
157
 
 
158
    if(result != SASL_OK) return result;
 
159
 
 
160
    result = _sasl_canon_user(conn, userstr, 0,
 
161
                              SASL_CU_AUTHID | SASL_CU_AUTHZID,
 
162
                              &(conn->oparams));
 
163
    if(result != SASL_OK) return result;
 
164
    
 
165
    result = prop_getnames(sconn->sparams->propctx, password_request,
 
166
                           auxprop_values);
 
167
    if(result < 0)
 
168
        return result;
 
169
 
 
170
    if((!auxprop_values[0].name
 
171
         || !auxprop_values[0].values || !auxprop_values[0].values[0])
 
172
       && (!auxprop_values[1].name
 
173
         || !auxprop_values[1].values || !auxprop_values[1].values[0]))
 
174
            return SASL_NOUSER;
 
175
        
 
176
    /* It is possible for us to get useful information out of just
 
177
     * the lookup, so we won't check that we have a password until now */
 
178
    if(!passwd) {
 
179
        ret = SASL_BADPARAM;
 
180
        goto done;
 
181
    }
 
182
 
 
183
    /* At the point this has been called, the username has been canonified
 
184
     * and we've done the auxprop lookup.  This should be easy. */
 
185
    if(auxprop_values[0].name
 
186
       && auxprop_values[0].values
 
187
       && auxprop_values[0].values[0]
 
188
       && !strcmp(auxprop_values[0].values[0], passwd)) {
 
189
        /* We have a plaintext version and it matched! */
 
190
        return SASL_OK;
 
191
    } else if(auxprop_values[1].name
 
192
              && auxprop_values[1].values
 
193
              && auxprop_values[1].values[0]) {
 
194
        const char *db_secret = auxprop_values[1].values[0];
 
195
        sasl_secret_t *construct;
 
196
        
 
197
        ret = _sasl_make_plain_secret(db_secret, passwd,
 
198
                                      strlen(passwd),
 
199
                                      &construct);
 
200
        if (ret != SASL_OK) {
 
201
            goto done;
 
202
        }
 
203
 
 
204
        if (!memcmp(db_secret, construct->data, construct->len)) {
 
205
            /* password verified! */
 
206
            ret = SASL_OK;
 
207
        } else {
 
208
            /* passwords do not match */
 
209
            ret = SASL_BADAUTH;
 
210
        }
 
211
 
 
212
        sasl_FREE(construct);
 
213
    } else {
 
214
        /* passwords do not match */
 
215
        ret = SASL_BADAUTH;
 
216
    }
 
217
 
 
218
    /* erase the plaintext password */
 
219
    sconn->sparams->utils->prop_erase(sconn->sparams->propctx,
 
220
                                      password_request[0]);
 
221
 
 
222
 done:
 
223
    if (userid) sasl_FREE(userid);
 
224
    if (realm)  sasl_FREE(realm);
 
225
 
 
226
    /* We're not going to erase the property here because other people
 
227
     * may want it */
 
228
    return ret;
 
229
}
 
230
 
 
231
#ifdef DO_SASL_CHECKAPOP
 
232
int _sasl_auxprop_verify_apop(sasl_conn_t *conn,
 
233
                              const char *userstr,
 
234
                              const char *challenge,
 
235
                              const char *response,
 
236
                              const char *user_realm __attribute__((unused)))
 
237
{
 
238
    int ret = SASL_BADAUTH;
 
239
    char *userid = NULL;
 
240
    char *realm = NULL;
 
241
    unsigned char digest[16];
 
242
    char digeststr[33];
 
243
    const char *password_request[] = { SASL_AUX_PASSWORD, NULL };
 
244
    struct propval auxprop_values[2];
 
245
    sasl_server_conn_t *sconn = (sasl_server_conn_t *)conn;
 
246
    MD5_CTX ctx;
 
247
    int i;
 
248
 
 
249
    if (!conn || !userstr || !challenge || !response)
 
250
       PARAMERROR(conn)
 
251
 
 
252
    /* We've done the auxprop lookup already (in our caller) */
 
253
    /* sadly, APOP has no provision for storing secrets */
 
254
    ret = prop_getnames(sconn->sparams->propctx, password_request,
 
255
                        auxprop_values);
 
256
    if(ret < 0) {
 
257
        sasl_seterror(conn, 0, "could not perform password lookup");
 
258
        goto done;
 
259
    }
 
260
    
 
261
    if(!auxprop_values[0].name ||
 
262
       !auxprop_values[0].values ||
 
263
       !auxprop_values[0].values[0]) {
 
264
        sasl_seterror(conn, 0, "could not find password");
 
265
        ret = SASL_NOUSER;
 
266
        goto done;
 
267
    }
 
268
    
 
269
    _sasl_MD5Init(&ctx);
 
270
    _sasl_MD5Update(&ctx, challenge, strlen(challenge));
 
271
    _sasl_MD5Update(&ctx, auxprop_values[0].values[0],
 
272
                    strlen(auxprop_values[0].values[0]));
 
273
    _sasl_MD5Final(digest, &ctx);
 
274
 
 
275
    /* erase the plaintext password */
 
276
    sconn->sparams->utils->prop_erase(sconn->sparams->propctx,
 
277
                                      password_request[0]);
 
278
 
 
279
    /* convert digest from binary to ASCII hex */
 
280
    for (i = 0; i < 16; i++)
 
281
      sprintf(digeststr + (i*2), "%02x", digest[i]);
 
282
 
 
283
    if (!strncasecmp(digeststr, response, 32)) {
 
284
      /* password verified! */
 
285
      ret = SASL_OK;
 
286
    } else {
 
287
      /* passwords do not match */
 
288
      ret = SASL_BADAUTH;
 
289
    }
 
290
 
 
291
 done:
 
292
    if (ret == SASL_BADAUTH) sasl_seterror(conn, SASL_NOLOG,
 
293
                                           "login incorrect");
 
294
    if (userid) sasl_FREE(userid);
 
295
    if (realm)  sasl_FREE(realm);
 
296
 
 
297
    return ret;
 
298
}
 
299
#endif /* DO_SASL_CHECKAPOP */
 
300
 
 
301
#if defined(HAVE_PWCHECK) || defined(HAVE_SASLAUTHD) || defined(HAVE_AUTHDAEMON)
 
302
/*
 
303
 * Wait for file descriptor to be writable. Return with error if timeout.
 
304
 */
 
305
static int write_wait(int fd, unsigned delta)
 
306
{
 
307
    fd_set wfds;
 
308
    fd_set efds;
 
309
    struct timeval tv;
 
310
 
 
311
    /* 
 
312
     * Wait for file descriptor fd to be writable. Retry on
 
313
     * interruptions. Return with error upon timeout.
 
314
     */
 
315
    while (1) {
 
316
        FD_ZERO(&wfds);
 
317
        FD_ZERO(&efds);
 
318
        FD_SET(fd, &wfds);
 
319
        FD_SET(fd, &efds);
 
320
        tv.tv_sec = (long) delta;
 
321
        tv.tv_usec = 0;
 
322
        switch(select(fd + 1, 0, &wfds, &efds, &tv)) {
 
323
        case 0:
 
324
            /* Timeout. */
 
325
            errno = ETIMEDOUT;
 
326
            return -1;
 
327
        case +1:
 
328
            if (FD_ISSET(fd, &wfds)) {
 
329
                /* Success, file descriptor is writable. */
 
330
                return 0;
 
331
            }
 
332
            return -1;
 
333
        case -1:
 
334
            if (errno == EINTR || errno == EAGAIN)
 
335
                continue;
 
336
        default:
 
337
            /* Error catch-all. */
 
338
            return -1;
 
339
        }
 
340
    }
 
341
    /* Not reached. */
 
342
    return -1;
 
343
}
 
344
 
 
345
/*
 
346
 * Keep calling the writev() system call with 'fd', 'iov', and 'iovcnt'
 
347
 * until all the data is written out or an error/timeout occurs.
 
348
 */
 
349
static int retry_writev(int fd, struct iovec *iov, int iovcnt, unsigned delta)
 
350
{
 
351
    int n;
 
352
    int i;
 
353
    int written = 0;
 
354
    static int iov_max =
 
355
#ifdef MAXIOV
 
356
        MAXIOV
 
357
#else
 
358
#ifdef IOV_MAX
 
359
        IOV_MAX
 
360
#else
 
361
        8192
 
362
#endif
 
363
#endif
 
364
        ;
 
365
    
 
366
    for (;;) {
 
367
        while (iovcnt && iov[0].iov_len == 0) {
 
368
            iov++;
 
369
            iovcnt--;
 
370
        }
 
371
 
 
372
        if (!iovcnt) return written;
 
373
 
 
374
        if (delta > 0) {
 
375
            if (write_wait(fd, delta))
 
376
                return -1;
 
377
        }
 
378
        n = writev(fd, iov, iovcnt > iov_max ? iov_max : iovcnt);
 
379
        if (n == -1) {
 
380
            if (errno == EINVAL && iov_max > 10) {
 
381
                iov_max /= 2;
 
382
                continue;
 
383
            }
 
384
            if (errno == EINTR) continue;
 
385
            return -1;
 
386
        }
 
387
 
 
388
        written += n;
 
389
 
 
390
        for (i = 0; i < iovcnt; i++) {
 
391
            if ((int) iov[i].iov_len > n) {
 
392
                iov[i].iov_base = (char *)iov[i].iov_base + n;
 
393
                iov[i].iov_len -= n;
 
394
                break;
 
395
            }
 
396
            n -= iov[i].iov_len;
 
397
            iov[i].iov_len = 0;
 
398
        }
 
399
 
 
400
        if (i == iovcnt) return written;
 
401
    }
 
402
}
 
403
 
 
404
#endif
 
405
 
 
406
#ifdef HAVE_PWCHECK
 
407
/* pwcheck daemon-authenticated login */
 
408
static int pwcheck_verify_password(sasl_conn_t *conn,
 
409
                                   const char *userid, 
 
410
                                   const char *passwd,
 
411
                                   const char *service __attribute__((unused)),
 
412
                                   const char *user_realm 
 
413
                                               __attribute__((unused)))
 
414
{
 
415
    int s;
 
416
    struct sockaddr_un srvaddr;
 
417
    int r;
 
418
    struct iovec iov[10];
 
419
    static char response[1024];
 
420
    unsigned start, n;
 
421
    char pwpath[1024];
 
422
 
 
423
    if (strlen(PWCHECKDIR)+8+1 > sizeof(pwpath)) return SASL_FAIL;
 
424
 
 
425
    strcpy(pwpath, PWCHECKDIR);
 
426
    strcat(pwpath, "/pwcheck");
 
427
 
 
428
    s = socket(AF_UNIX, SOCK_STREAM, 0);
 
429
    if (s == -1) return errno;
 
430
 
 
431
    memset((char *)&srvaddr, 0, sizeof(srvaddr));
 
432
    srvaddr.sun_family = AF_UNIX;
 
433
    strncpy(srvaddr.sun_path, pwpath, sizeof(srvaddr.sun_path));
 
434
    r = connect(s, (struct sockaddr *)&srvaddr, sizeof(srvaddr));
 
435
    if (r == -1) {
 
436
        sasl_seterror(conn,0,"cannot connect to pwcheck server");
 
437
        return SASL_FAIL;
 
438
    }
 
439
 
 
440
    iov[0].iov_base = (char *)userid;
 
441
    iov[0].iov_len = strlen(userid)+1;
 
442
    iov[1].iov_base = (char *)passwd;
 
443
    iov[1].iov_len = strlen(passwd)+1;
 
444
 
 
445
    retry_writev(s, iov, 2, 0);
 
446
 
 
447
    start = 0;
 
448
    while (start < sizeof(response) - 1) {
 
449
        n = read(s, response+start, sizeof(response) - 1 - start);
 
450
        if (n < 1) break;
 
451
        start += n;
 
452
    }
 
453
 
 
454
    close(s);
 
455
 
 
456
    if (start > 1 && !strncmp(response, "OK", 2)) {
 
457
        return SASL_OK;
 
458
    }
 
459
 
 
460
    response[start] = '\0';
 
461
    sasl_seterror(conn,0,response);
 
462
    return SASL_BADAUTH;
 
463
}
 
464
 
 
465
#endif
 
466
 
 
467
#if defined(HAVE_SASLAUTHD) || defined(HAVE_AUTHDAEMON)
 
468
static int read_wait(int fd, unsigned delta)
 
469
{
 
470
    fd_set rfds;
 
471
    fd_set efds;
 
472
    struct timeval tv;
 
473
    /* 
 
474
     * Wait for file descriptor fd to be readable. Retry on 
 
475
     * interruptions. Return with error upon timeout.
 
476
     */
 
477
    while (1) {
 
478
        FD_ZERO(&rfds);
 
479
        FD_ZERO(&efds);
 
480
        FD_SET(fd, &rfds);
 
481
        FD_SET(fd, &efds);
 
482
        tv.tv_sec = (long) delta;
 
483
        tv.tv_usec = 0;
 
484
        switch(select(fd + 1, &rfds, 0, &efds, &tv)) {
 
485
        case 0:
 
486
            /* Timeout. */
 
487
            errno = ETIMEDOUT;
 
488
            return -1;
 
489
        case +1:
 
490
            if (FD_ISSET(fd, &rfds)) {
 
491
                /* Success, file descriptor is readable. */
 
492
                return 0;
 
493
            }
 
494
            return -1;
 
495
        case -1:
 
496
            if (errno == EINTR || errno == EAGAIN)
 
497
                continue;
 
498
        default:
 
499
            /* Error catch-all. */
 
500
            return -1;
 
501
        }
 
502
    }
 
503
    /* Not reached. */
 
504
    return -1;
 
505
}
 
506
 
 
507
/*
 
508
 * Keep calling the read() system call until all the data is read in, 
 
509
 * timeout, EOF, or an error occurs. This function returns the number 
 
510
 * of useful bytes, or -1 if timeout/error.
 
511
 */
 
512
static int retry_read(int fd, void *buf0, unsigned nbyte, unsigned delta)
 
513
{
 
514
    int nr;
 
515
    unsigned nleft = nbyte;
 
516
    char *buf = (char*) buf0;
 
517
    
 
518
    while (nleft >= 1) {
 
519
        if (delta > 0) {
 
520
            if (read_wait(fd, delta))
 
521
                return -1;
 
522
        }
 
523
        nr = read(fd, buf, nleft);
 
524
        if (nr < 0) {
 
525
            if (errno == EINTR || errno == EAGAIN)
 
526
                continue;
 
527
            return -1;
 
528
        } else if (nr == 0) {
 
529
            break;
 
530
        }
 
531
      buf += nr;
 
532
      nleft -= nr;
 
533
    }
 
534
    return nbyte - nleft;
 
535
}
 
536
#endif
 
537
 
 
538
#ifdef HAVE_SASLAUTHD
 
539
/* saslauthd-authenticated login */
 
540
static int saslauthd_verify_password(sasl_conn_t *conn,
 
541
                                     const char *userid, 
 
542
                                     const char *passwd,
 
543
                                     const char *service,
 
544
                                     const char *user_realm)
 
545
{
 
546
    char response[1024];
 
547
    char query[8192];
 
548
    char *query_end = query;
 
549
    int s;
 
550
    struct sockaddr_un srvaddr;
 
551
    sasl_getopt_t *getopt;
 
552
    void *context;
 
553
    char pwpath[sizeof(srvaddr.sun_path)];
 
554
    const char *p = NULL;
 
555
    char *freeme = NULL;
 
556
#ifdef USE_DOORS
 
557
    door_arg_t arg;
 
558
#endif
 
559
 
 
560
    /* check to see if the user configured a rundir */
 
561
    if (_sasl_getcallback(conn, SASL_CB_GETOPT, &getopt, &context) == SASL_OK) {
 
562
        getopt(context, NULL, "saslauthd_path", &p, NULL);
 
563
    }
 
564
    if (p) {
 
565
        strncpy(pwpath, p, sizeof(pwpath));
 
566
    } else {
 
567
        if (strlen(PATH_SASLAUTHD_RUNDIR) + 4 + 1 > sizeof(pwpath))
 
568
            return SASL_FAIL;
 
569
 
 
570
        strcpy(pwpath, PATH_SASLAUTHD_RUNDIR);
 
571
        strcat(pwpath, "/mux");
 
572
    }
 
573
 
 
574
    /* Split out username/realm if necessary */
 
575
    if(strrchr(userid,'@') != NULL) {
 
576
        char *rtmp;
 
577
        
 
578
        if(_sasl_strdup(userid, &freeme, NULL) != SASL_OK)
 
579
            goto fail;
 
580
 
 
581
        userid = freeme;
 
582
        rtmp = strrchr(userid,'@');
 
583
        *rtmp = '\0';
 
584
        user_realm = rtmp + 1;
 
585
    }
 
586
 
 
587
    /*
 
588
     * build request of the form:
 
589
     *
 
590
     * count authid count password count service count realm
 
591
     */
 
592
    {
 
593
        unsigned short u_len, p_len, s_len, r_len;
 
594
 
 
595
        u_len = (strlen(userid));
 
596
        p_len = (strlen(passwd));
 
597
        s_len = (strlen(service));
 
598
        r_len = ((user_realm ? strlen(user_realm) : 0));
 
599
 
 
600
        if (u_len + p_len + s_len + r_len + 30 > (unsigned short) sizeof(query)) {
 
601
            /* request just too damn big */
 
602
            sasl_seterror(conn, 0, "saslauthd request too large");
 
603
            goto fail;
 
604
        }
 
605
 
 
606
        u_len = htons(u_len);
 
607
        p_len = htons(p_len);
 
608
        s_len = htons(s_len);
 
609
        r_len = htons(r_len);
 
610
 
 
611
        memcpy(query_end, &u_len, sizeof(unsigned short));
 
612
        query_end += sizeof(unsigned short);
 
613
        while (*userid) *query_end++ = *userid++;
 
614
 
 
615
        memcpy(query_end, &p_len, sizeof(unsigned short));
 
616
        query_end += sizeof(unsigned short);
 
617
        while (*passwd) *query_end++ = *passwd++;
 
618
 
 
619
        memcpy(query_end, &s_len, sizeof(unsigned short));
 
620
        query_end += sizeof(unsigned short);
 
621
        while (*service) *query_end++ = *service++;
 
622
 
 
623
        memcpy(query_end, &r_len, sizeof(unsigned short));
 
624
        query_end += sizeof(unsigned short);
 
625
        if (user_realm) while (*user_realm) *query_end++ = *user_realm++;
 
626
    }
 
627
 
 
628
#ifdef USE_DOORS
 
629
    s = open(pwpath, O_RDONLY);
 
630
    if (s < 0) {
 
631
        sasl_seterror(conn, 0, "cannot open door to saslauthd server: %m", errno);
 
632
        goto fail;
 
633
    }
 
634
 
 
635
    arg.data_ptr = query;
 
636
    arg.data_size = query_end - query;
 
637
    arg.desc_ptr = NULL;
 
638
    arg.desc_num = 0;
 
639
    arg.rbuf = response;
 
640
    arg.rsize = sizeof(response);
 
641
 
 
642
    if (door_call(s, &arg) < 0) {
 
643
      /* Parameters are undefined */
 
644
      close(s);
 
645
      sasl_seterror(conn, 0, "door call to saslauthd server failed: %m", errno);
 
646
      goto fail;
 
647
    }
 
648
 
 
649
    if (arg.data_ptr != response || arg.data_size >= sizeof(response)) {
 
650
        /* oh damn, we got back a really long response */
 
651
        munmap(arg.rbuf, arg.rsize);
 
652
        close(s);
 
653
        sasl_seterror(conn, 0, "saslauthd sent an overly long response");
 
654
        goto fail;
 
655
    }
 
656
    response[arg.data_size] = '\0';
 
657
 
 
658
    close(s);
 
659
#else
 
660
    /* unix sockets */
 
661
 
 
662
    s = socket(AF_UNIX, SOCK_STREAM, 0);
 
663
    if (s == -1) {
 
664
        sasl_seterror(conn, 0, "cannot create socket for saslauthd: %m", errno);
 
665
        goto fail;
 
666
    }
 
667
 
 
668
    memset((char *)&srvaddr, 0, sizeof(srvaddr));
 
669
    srvaddr.sun_family = AF_UNIX;
 
670
    strncpy(srvaddr.sun_path, pwpath, sizeof(srvaddr.sun_path));
 
671
 
 
672
    {
 
673
        int r = connect(s, (struct sockaddr *) &srvaddr, sizeof(srvaddr));
 
674
        if (r == -1) {
 
675
            close(s);
 
676
            sasl_seterror(conn, 0, "cannot connect to saslauthd server: %m", errno);
 
677
            goto fail;
 
678
        }
 
679
    }
 
680
 
 
681
    {
 
682
        struct iovec iov[8];
 
683
 
 
684
        iov[0].iov_len = query_end - query;
 
685
        iov[0].iov_base = query;
 
686
 
 
687
        if (retry_writev(s, iov, 1, 0) == -1) {
 
688
            close(s);
 
689
            sasl_seterror(conn, 0, "write failed");
 
690
            goto fail;
 
691
        }
 
692
    }
 
693
 
 
694
    {
 
695
        unsigned short count = 0;
 
696
 
 
697
        /*
 
698
         * read response of the form:
 
699
         *
 
700
         * count result
 
701
         */
 
702
        if (retry_read(s, &count, sizeof(count), 0) < (int) sizeof(count)) {
 
703
            sasl_seterror(conn, 0, "size read failed");
 
704
            goto fail;
 
705
        }
 
706
        
 
707
        count = ntohs(count);
 
708
        if (count < 2) { /* MUST have at least "OK" or "NO" */
 
709
            close(s);
 
710
            sasl_seterror(conn, 0, "bad response from saslauthd");
 
711
            goto fail;
 
712
        }
 
713
        
 
714
        count = (int)sizeof(response) <= count ? sizeof(response) - 1 : count;
 
715
        if (retry_read(s, response, count, 0) < count) {
 
716
            close(s);
 
717
            sasl_seterror(conn, 0, "read failed");
 
718
            goto fail;
 
719
        }
 
720
        response[count] = '\0';
 
721
    }
 
722
 
 
723
    close(s);
 
724
#endif /* USE_DOORS */
 
725
  
 
726
    if(freeme) free(freeme);
 
727
 
 
728
    if (!strncmp(response, "OK", 2)) {
 
729
        return SASL_OK;
 
730
    }
 
731
  
 
732
    sasl_seterror(conn, SASL_NOLOG, "authentication failed");
 
733
    return SASL_BADAUTH;
 
734
 
 
735
 fail:
 
736
    if (freeme) free(freeme);
 
737
    return SASL_FAIL;
 
738
}
 
739
 
 
740
#endif
 
741
 
 
742
#ifdef HAVE_AUTHDAEMON
 
743
/* 
 
744
 * Preliminary support for Courier's authdaemond.
 
745
 */
 
746
#define AUTHDAEMON_IO_TIMEOUT 30
 
747
 
 
748
static int authdaemon_blocking(int fd, int block)
 
749
{
 
750
    int f, r;
 
751
 
 
752
    /* Get the fd's blocking bit. */
 
753
    f = fcntl(fd, F_GETFL, 0);
 
754
    if (f == -1)
 
755
        return -1;
 
756
 
 
757
    /* Adjust the bitmap accordingly. */
 
758
#ifndef O_NONBLOCK
 
759
#define NB_BITMASK FNDELAY
 
760
#else
 
761
#define NB_BITMASK O_NONBLOCK
 
762
#endif
 
763
    if (block)
 
764
        f &= ~NB_BITMASK;
 
765
    else
 
766
        f |=  NB_BITMASK;
 
767
#undef NB_BITMASK
 
768
 
 
769
    /* Adjust the fd's blocking bit. */
 
770
    r = fcntl(fd, F_SETFL, f);
 
771
    if (r)
 
772
        return -1;
 
773
 
 
774
    /* Success. */
 
775
    return 0;
 
776
}
 
777
 
 
778
static int authdaemon_connect(sasl_conn_t *conn, const char *path)
 
779
{
 
780
    int r, s = -1;
 
781
    struct sockaddr_un srvaddr;
 
782
 
 
783
    if (strlen(path) >= sizeof(srvaddr.sun_path)) {
 
784
        sasl_seterror(conn, 0, "unix socket path too large", errno);
 
785
        goto fail;
 
786
    }
 
787
 
 
788
    s = socket(AF_UNIX, SOCK_STREAM, 0);
 
789
    if (s == -1) {
 
790
        sasl_seterror(conn, 0, "cannot create socket for connection to Courier authdaemond: %m", errno);
 
791
        goto fail;
 
792
    }
 
793
 
 
794
    memset((char *)&srvaddr, 0, sizeof(srvaddr));
 
795
    srvaddr.sun_family = AF_UNIX;
 
796
    strncpy(srvaddr.sun_path, path, sizeof(srvaddr.sun_path) - 1);
 
797
 
 
798
    /* Use nonblocking unix socket connect(2). */
 
799
    if (authdaemon_blocking(s, 0)) {
 
800
        sasl_seterror(conn, 0, "cannot set nonblocking bit: %m", errno);
 
801
        goto fail;
 
802
    }
 
803
 
 
804
    r = connect(s, (struct sockaddr *) &srvaddr, sizeof(srvaddr));
 
805
    if (r == -1) {
 
806
        sasl_seterror(conn, 0, "cannot connect to Courier authdaemond: %m", errno);
 
807
        goto fail;
 
808
    }
 
809
 
 
810
    if (authdaemon_blocking(s, 1)) {
 
811
        sasl_seterror(conn, 0, "cannot clear nonblocking bit: %m", errno);
 
812
        goto fail;
 
813
    }
 
814
 
 
815
    return s;
 
816
fail:
 
817
    if (s >= 0)
 
818
        close(s);
 
819
    return -1;
 
820
}
 
821
 
 
822
static char *authdaemon_build_query(const char *service,
 
823
                                    const char *authtype,
 
824
                                    const char *user,
 
825
                                    const char *passwd)
 
826
{
 
827
    int sz;
 
828
    int l = strlen(service) 
 
829
            + 1
 
830
            + strlen(authtype) 
 
831
            + 1
 
832
            + strlen(user)
 
833
            + 1
 
834
            + strlen(passwd) 
 
835
            + 1;
 
836
    char *buf, n[5];
 
837
    if (snprintf(n, sizeof(n), "%d", l) >= (int)sizeof(n))
 
838
        return NULL;
 
839
    sz = strlen(n) + l + 20;
 
840
    if (!(buf = sasl_ALLOC(sz)))
 
841
        return NULL;
 
842
    snprintf(buf, 
 
843
             sz, 
 
844
             "AUTH %s\n%s\n%s\n%s\n%s\n\n",
 
845
             n,
 
846
             service,
 
847
             authtype,
 
848
             user,
 
849
             passwd);
 
850
    return buf;
 
851
}
 
852
 
 
853
static int authdaemon_read(int fd, void *buf0, unsigned sz)
 
854
{
 
855
    int nr;
 
856
    char *buf = (char*) buf0;
 
857
    if (sz <= 1)
 
858
        return -1;
 
859
    if ((nr = retry_read(fd, buf0, sz - 1, AUTHDAEMON_IO_TIMEOUT)) < 0)
 
860
        return -1;
 
861
    /* We need a null-terminated buffer. */
 
862
    buf[nr] = 0;
 
863
    /* Check for overflow condition. */
 
864
    return nr + 1 < (int)sz ? 0 : -1;
 
865
}
 
866
 
 
867
static int authdaemon_write(int fd, void *buf0, unsigned sz)
 
868
{
 
869
    int nw;
 
870
    struct iovec io;
 
871
    io.iov_len = sz;
 
872
    io.iov_base = buf0;
 
873
    nw = retry_writev(fd, &io, 1, AUTHDAEMON_IO_TIMEOUT);
 
874
    return nw == (int)sz ? 0 : -1;
 
875
}
 
876
 
 
877
static int authdaemon_talk(sasl_conn_t *conn, int sock, char *authreq)
 
878
{
 
879
    char *str;
 
880
    char buf[8192];
 
881
 
 
882
    if (authdaemon_write(sock, authreq, strlen(authreq)))
 
883
        goto _err_out;
 
884
    if (authdaemon_read(sock, buf, sizeof(buf)))
 
885
        goto _err_out;
 
886
    for (str = buf; *str; ) {
 
887
        char *sub;
 
888
 
 
889
        for (sub = str; *str; ++str) {
 
890
            if (*str == '\n') {
 
891
                *str++ = 0;
 
892
                break;
 
893
            }
 
894
        }
 
895
        if (strcmp(sub, ".") == 0) {
 
896
            /* success */
 
897
            return SASL_OK;
 
898
        }
 
899
        if (strcmp(sub, "FAIL") == 0) {
 
900
            /* passwords do not match */
 
901
            sasl_seterror(conn, SASL_NOLOG, "authentication failed");
 
902
            return SASL_BADAUTH;
 
903
        }
 
904
    }
 
905
_err_out:
 
906
    /* catchall: authentication error */
 
907
    sasl_seterror(conn, 0, "could not verify password");
 
908
    return SASL_FAIL;
 
909
}
 
910
 
 
911
static int authdaemon_verify_password(sasl_conn_t *conn,
 
912
                                      const char *userid, 
 
913
                                      const char *passwd,
 
914
                                      const char *service,
 
915
                                      const char *user_realm __attribute__((unused)))
 
916
{
 
917
    const char *p = NULL;
 
918
    sasl_getopt_t *getopt;
 
919
    void *context;
 
920
    int result = SASL_FAIL;
 
921
    char *query = NULL;
 
922
    int sock = -1;
 
923
 
 
924
    /* check to see if the user configured a rundir */
 
925
    if (_sasl_getcallback(conn, SASL_CB_GETOPT, &getopt, &context) == SASL_OK) {
 
926
        getopt(context, NULL, "authdaemond_path", &p, NULL);
 
927
    }
 
928
    if (!p) {
 
929
        /*
 
930
         * XXX should we peek at Courier's build-time config ?
 
931
         */
 
932
        p = PATH_AUTHDAEMON_SOCKET;
 
933
    }
 
934
 
 
935
    if ((sock = authdaemon_connect(conn, p)) < 0)
 
936
        goto out;
 
937
    if (!(query = authdaemon_build_query(service, "login", userid, passwd)))
 
938
        goto out;
 
939
    result = authdaemon_talk(conn, sock, query);
 
940
out:
 
941
    if (sock >= 0)
 
942
        close(sock), sock = -1;
 
943
    if (query)
 
944
        sasl_FREE(query), query = 0;
 
945
    return result;
 
946
}
 
947
#endif
 
948
 
 
949
#ifdef HAVE_ALWAYSTRUE
 
950
static int always_true(sasl_conn_t *conn,
 
951
                       const char *userstr,
 
952
                       const char *passwd __attribute__((unused)),
 
953
                       const char *service __attribute__((unused)),
 
954
                       const char *user_realm __attribute__((unused))) 
 
955
{
 
956
    _sasl_log(conn, SASL_LOG_WARN, "AlwaysTrue Password Verifier Verified: %s",
 
957
              userstr);
 
958
    return SASL_OK;
 
959
}
 
960
#endif
 
961
 
 
962
struct sasl_verify_password_s _sasl_verify_password[] = {
 
963
    { "auxprop", &auxprop_verify_password },
 
964
#ifdef HAVE_PWCHECK
 
965
    { "pwcheck", &pwcheck_verify_password },
 
966
#endif
 
967
#ifdef HAVE_SASLAUTHD
 
968
    { "saslauthd", &saslauthd_verify_password },
 
969
#endif
 
970
#ifdef HAVE_AUTHDAEMON
 
971
    { "authdaemond", &authdaemon_verify_password },
 
972
#endif
 
973
#ifdef HAVE_ALWAYSTRUE
 
974
    { "alwaystrue", &always_true },
 
975
#endif
 
976
    { NULL, NULL }
 
977
};