source: trunk/third/cyrus-sasl/lib/checkpw.c @ 17977

Revision 17977, 16.1 KB checked in by ghudson, 22 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r17976, which included commits to RCS files with non-trunk default branches.
Line 
1/* SASL server API implementation
2 * Rob Siemborski
3 * Tim Martin
4 * $Id: checkpw.c,v 1.1.1.1 2002-10-13 18:02:15 ghudson Exp $
5 */
6/*
7 * Copyright (c) 2001 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)
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
96extern int errno;
97#endif
98
99
100/* we store the following secret to check plaintext passwords:
101 *
102 * <salt> \0 <secret>
103 *
104 * where <secret> = MD5(<salt>, "sasldb", <pass>)
105 */
106static int _sasl_make_plain_secret(const char *salt,
107                                   const char *passwd, size_t passlen,
108                                   sasl_secret_t **secret)
109{
110    MD5_CTX ctx;
111    unsigned sec_len = 16 + 1 + 16; /* salt + "\0" + hash */
112
113    *secret = (sasl_secret_t *) sasl_ALLOC(sizeof(sasl_secret_t) +
114                                           sec_len * sizeof(char));
115    if (*secret == NULL) {
116        return SASL_NOMEM;
117    }
118
119    _sasl_MD5Init(&ctx);
120    _sasl_MD5Update(&ctx, salt, 16);
121    _sasl_MD5Update(&ctx, "sasldb", 6);
122    _sasl_MD5Update(&ctx, passwd, passlen);
123    memcpy((*secret)->data, salt, 16);
124    memcpy((*secret)->data + 16, "\0", 1);
125    _sasl_MD5Final((*secret)->data + 17, &ctx);
126    (*secret)->len = sec_len;
127   
128    return SASL_OK;
129}
130
131/* erase & dispose of a sasl_secret_t
132 */
133static int auxprop_verify_password(sasl_conn_t *conn,
134                                   const char *userstr,
135                                   const char *passwd,
136                                   const char *service __attribute__((unused)),
137                                   const char *user_realm __attribute__((unused)))
138{
139    int ret = SASL_FAIL;
140    char *userid = NULL;
141    char *realm = NULL;
142    int result = SASL_OK;
143    sasl_server_conn_t *sconn = (sasl_server_conn_t *)conn;
144    const char *password_request[] = { SASL_AUX_PASSWORD,
145                                       "*cmusaslsecretPLAIN",
146                                       NULL };
147    struct propval auxprop_values[3];
148   
149    if (!conn || !userstr)
150        return SASL_BADPARAM;
151
152    /* We need to clear any previous results and re-canonify to
153     * ensure correctness */
154
155    prop_clear(sconn->sparams->propctx, 0);
156       
157    /* ensure its requested */
158    result = prop_request(sconn->sparams->propctx, password_request);
159
160    if(result != SASL_OK) return result;
161
162    result = _sasl_canon_user(conn, userstr, 0,
163                              SASL_CU_AUTHID | SASL_CU_AUTHZID,
164                              &(conn->oparams));
165    if(result != SASL_OK) return result;
166   
167    result = prop_getnames(sconn->sparams->propctx, password_request,
168                           auxprop_values);
169    if(result < 0)
170        return result;
171
172    if((!auxprop_values[0].name
173         || !auxprop_values[0].values || !auxprop_values[0].values[0])
174       && (!auxprop_values[1].name
175         || !auxprop_values[1].values || !auxprop_values[1].values[0]))
176            return SASL_NOUSER;
177       
178    /* It is possible for us to get useful information out of just
179     * the lookup, so we won't check that we have a password until now */
180    if(!passwd) {
181        ret = SASL_BADPARAM;
182        goto done;
183    }
184
185    /* At the point this has been called, the username has been canonified
186     * and we've done the auxprop lookup.  This should be easy. */
187    if(auxprop_values[0].name
188       && auxprop_values[0].values
189       && auxprop_values[0].values[0]
190       && !strcmp(auxprop_values[0].values[0], passwd)) {
191        /* We have a plaintext version and it matched! */
192        return SASL_OK;
193    } else if(auxprop_values[1].name
194              && auxprop_values[1].values
195              && auxprop_values[1].values[0]) {
196        const char *db_secret = auxprop_values[1].values[0];
197        sasl_secret_t *construct;
198       
199        ret = _sasl_make_plain_secret(db_secret, passwd,
200                                      strlen(passwd),
201                                      &construct);
202        if (ret != SASL_OK) {
203            goto done;
204        }
205
206        if (!memcmp(db_secret, construct->data, construct->len)) {
207            /* password verified! */
208            ret = SASL_OK;
209        } else {
210            /* passwords do not match */
211            ret = SASL_BADAUTH;
212        }
213
214        sasl_FREE(construct);
215    } else {
216        /* passwords do not match */
217        ret = SASL_BADAUTH;
218    }
219
220 done:
221    if (userid) sasl_FREE(userid);
222    if (realm)  sasl_FREE(realm);
223
224    /* We're not going to erase the property here because other people
225     * may want it */
226    return ret;
227}
228
229#ifdef DO_SASL_CHECKAPOP
230int _sasl_auxprop_verify_apop(sasl_conn_t *conn,
231                              const char *userstr,
232                              const char *challenge,
233                              const char *response,
234                              const char *user_realm __attribute__((unused)))
235{
236    int ret = SASL_BADAUTH;
237    char *userid = NULL;
238    char *realm = NULL;
239    unsigned char digest[16];
240    char digeststr[32];
241    const char *password_request[] = { SASL_AUX_PASSWORD, NULL };
242    struct propval auxprop_values[2];
243    sasl_server_conn_t *sconn = (sasl_server_conn_t *)conn;
244    MD5_CTX ctx;
245    int i;
246
247    if (!conn || !userstr || !challenge || !response)
248       PARAMERROR(conn)
249
250    /* We've done the auxprop lookup already (in our caller) */
251    /* sadly, APOP has no provision for storing secrets */
252    ret = prop_getnames(sconn->sparams->propctx, password_request,
253                        auxprop_values);
254    if(ret < 0) {
255        sasl_seterror(conn, 0, "could not perform password lookup");
256        goto done;
257    }
258   
259    if(!auxprop_values[0].name ||
260       !auxprop_values[0].values ||
261       !auxprop_values[0].values[0]) {
262        sasl_seterror(conn, 0, "could not find password");
263        ret = SASL_NOUSER;
264        goto done;
265    }
266   
267    _sasl_MD5Init(&ctx);
268    _sasl_MD5Update(&ctx, challenge, strlen(challenge));
269    _sasl_MD5Update(&ctx, auxprop_values[0].values[0],
270                    strlen(auxprop_values[0].values[0]));
271    _sasl_MD5Final(digest, &ctx);
272
273    /* convert digest from binary to ASCII hex */
274    for (i = 0; i < 16; i++)
275      sprintf(digeststr + (i*2), "%02x", digest[i]);
276
277    if (!strncasecmp(digeststr, response, 32)) {
278      /* password verified! */
279      ret = SASL_OK;
280    } else {
281      /* passwords do not match */
282      ret = SASL_BADAUTH;
283    }
284
285 done:
286    if (ret == SASL_BADAUTH) sasl_seterror(conn, SASL_NOLOG,
287                                           "login incorrect");
288    if (userid) sasl_FREE(userid);
289    if (realm)  sasl_FREE(realm);
290
291    return ret;
292}
293#endif /* DO_SASL_CHECKAPOP */
294
295#if defined(HAVE_PWCHECK) || defined(HAVE_SASLAUTHD)
296/*
297 * Keep calling the writev() system call with 'fd', 'iov', and 'iovcnt'
298 * until all the data is written out or an error occurs.
299 */
300static int retry_writev(int fd, struct iovec *iov, int iovcnt)
301{
302    int n;
303    int i;
304    int written = 0;
305    static int iov_max =
306#ifdef MAXIOV
307        MAXIOV
308#else
309#ifdef IOV_MAX
310        IOV_MAX
311#else
312        8192
313#endif
314#endif
315        ;
316   
317    for (;;) {
318        while (iovcnt && iov[0].iov_len == 0) {
319            iov++;
320            iovcnt--;
321        }
322
323        if (!iovcnt) return written;
324
325        n = writev(fd, iov, iovcnt > iov_max ? iov_max : iovcnt);
326        if (n == -1) {
327            if (errno == EINVAL && iov_max > 10) {
328                iov_max /= 2;
329                continue;
330            }
331            if (errno == EINTR) continue;
332            return -1;
333        }
334
335        written += n;
336
337        for (i = 0; i < iovcnt; i++) {
338            if (iov[i].iov_len > (unsigned) n) {
339                iov[i].iov_base = (char *)iov[i].iov_base + n;
340                iov[i].iov_len -= n;
341                break;
342            }
343            n -= iov[i].iov_len;
344            iov[i].iov_len = 0;
345        }
346
347        if (i == iovcnt) return written;
348    }
349}
350
351#endif
352
353#ifdef HAVE_PWCHECK
354/* pwcheck daemon-authenticated login */
355static int pwcheck_verify_password(sasl_conn_t *conn,
356                                   const char *userid,
357                                   const char *passwd,
358                                   const char *service __attribute__((unused)),
359                                   const char *user_realm
360                                               __attribute__((unused)))
361{
362    int s;
363    struct sockaddr_un srvaddr;
364    int r;
365    struct iovec iov[10];
366    static char response[1024];
367    unsigned start, n;
368    char pwpath[1024];
369
370    if (strlen(PWCHECKDIR)+8+1 > sizeof(pwpath)) return SASL_FAIL;
371
372    strcpy(pwpath, PWCHECKDIR);
373    strcat(pwpath, "/pwcheck");
374
375    s = socket(AF_UNIX, SOCK_STREAM, 0);
376    if (s == -1) return errno;
377
378    memset((char *)&srvaddr, 0, sizeof(srvaddr));
379    srvaddr.sun_family = AF_UNIX;
380    strncpy(srvaddr.sun_path, pwpath, sizeof(srvaddr.sun_path));
381    r = connect(s, (struct sockaddr *)&srvaddr, sizeof(srvaddr));
382    if (r == -1) {
383        sasl_seterror(conn,0,"cannot connect to pwcheck server");
384        return SASL_FAIL;
385    }
386
387    iov[0].iov_base = (char *)userid;
388    iov[0].iov_len = strlen(userid)+1;
389    iov[1].iov_base = (char *)passwd;
390    iov[1].iov_len = strlen(passwd)+1;
391
392    retry_writev(s, iov, 2);
393
394    start = 0;
395    while (start < sizeof(response) - 1) {
396        n = read(s, response+start, sizeof(response) - 1 - start);
397        if (n < 1) break;
398        start += n;
399    }
400
401    close(s);
402
403    if (start > 1 && !strncmp(response, "OK", 2)) {
404        return SASL_OK;
405    }
406
407    response[start] = '\0';
408    sasl_seterror(conn,0,response);
409    return SASL_BADAUTH;
410}
411
412#endif
413
414#ifdef HAVE_SASLAUTHD
415
416/*
417 * Keep calling the read() system call with 'fd', 'buf', and 'nbyte'
418 * until all the data is read in or an error occurs.
419 */
420static int retry_read(int fd, void *buf0, unsigned nbyte)
421{
422    int n;
423    int nread = 0;
424    char *buf = buf0;
425
426    if (nbyte == 0) return 0;
427
428    for (;;) {
429        n = read(fd, buf, nbyte);
430        if (n == -1 || n == 0) {
431            if (errno == EINTR || errno == EAGAIN) continue;
432            return -1;
433        }
434
435        nread += n;
436
437        if (nread >= (int) nbyte) return nread;
438
439        buf += n;
440        nbyte -= n;
441    }
442}
443
444/* saslauthd-authenticated login */
445static int saslauthd_verify_password(sasl_conn_t *conn,
446                                     const char *userid,
447                                     const char *passwd,
448                                     const char *service,
449                                     const char *user_realm)
450{
451    char response[1024];
452    char query[8192];
453    char *query_end = query;
454    int s;
455    struct sockaddr_un srvaddr;
456    sasl_getopt_t *getopt;
457    void *context;
458    char pwpath[sizeof(srvaddr.sun_path)];
459    const char *p = NULL;
460#ifdef USE_DOORS
461    door_arg_t arg;
462#endif
463
464    /* check to see if the user configured a rundir */
465    if (_sasl_getcallback(conn, SASL_CB_GETOPT, &getopt, &context) == SASL_OK) {
466        getopt(context, NULL, "saslauthd_path", &p, NULL);
467    }
468    if (p) {
469        strncpy(pwpath, p, sizeof(pwpath));
470    } else {
471        if (strlen(PATH_SASLAUTHD_RUNDIR) + 4 + 1 > sizeof(pwpath))
472            return SASL_FAIL;
473
474        strcpy(pwpath, PATH_SASLAUTHD_RUNDIR);
475        strcat(pwpath, "/mux");
476    }
477
478    /*
479     * build request of the form:
480     *
481     * count authid count password count service count realm
482     */
483    {
484        unsigned short u_len, p_len, s_len, r_len;
485 
486        u_len = (strlen(userid));
487        p_len = (strlen(passwd));
488        s_len = (strlen(service));
489        r_len = ((user_realm ? strlen(user_realm) : 0));
490
491        if (u_len + p_len + s_len + r_len + 30 > (unsigned short) sizeof(query)) {
492            /* request just too damn big */
493            sasl_seterror(conn, 0, "saslauthd request too large");
494            return SASL_FAIL;
495        }
496
497        u_len = htons(u_len);
498        p_len = htons(p_len);
499        s_len = htons(s_len);
500        r_len = htons(r_len);
501
502        memcpy(query_end, &u_len, sizeof(unsigned short));
503        query_end += sizeof(unsigned short);
504        while (*userid) *query_end++ = *userid++;
505
506        memcpy(query_end, &p_len, sizeof(unsigned short));
507        query_end += sizeof(unsigned short);
508        while (*passwd) *query_end++ = *passwd++;
509
510        memcpy(query_end, &s_len, sizeof(unsigned short));
511        query_end += sizeof(unsigned short);
512        while (*service) *query_end++ = *service++;
513
514        memcpy(query_end, &r_len, sizeof(unsigned short));
515        query_end += sizeof(unsigned short);
516        if (user_realm) while (*user_realm) *query_end++ = *user_realm++;
517    }
518
519#ifdef USE_DOORS
520    s = open(pwpath, O_RDONLY);
521    if (s < 0) {
522        sasl_seterror(conn, 0, "cannot open door to saslauthd server: %m", errno);
523        return SASL_FAIL;
524    }
525
526    arg.data_ptr = query;
527    arg.data_size = query_end - query;
528    arg.desc_ptr = NULL;
529    arg.desc_num = 0;
530    arg.rbuf = response;
531    arg.rsize = sizeof(response);
532
533    door_call(s, &arg);
534
535    if (arg.data_ptr != response || arg.data_size >= sizeof(response)) {
536        /* oh damn, we got back a really long response */
537        munmap(arg.rbuf, arg.rsize);
538        sasl_seterror(conn, 0, "saslauthd sent an overly long response");
539        return SASL_FAIL;
540    }
541    response[arg.data_size] = '\0';
542
543    close(s);
544#else
545    /* unix sockets */
546
547    s = socket(AF_UNIX, SOCK_STREAM, 0);
548    if (s == -1) {
549        sasl_seterror(conn, 0, "cannot create socket for saslauthd: %m", errno);
550        return SASL_FAIL;
551    }
552
553    memset((char *)&srvaddr, 0, sizeof(srvaddr));
554    srvaddr.sun_family = AF_UNIX;
555    strncpy(srvaddr.sun_path, pwpath, sizeof(srvaddr.sun_path));
556
557    {
558        int r = connect(s, (struct sockaddr *) &srvaddr, sizeof(srvaddr));
559        if (r == -1) {
560            sasl_seterror(conn, 0, "cannot connect to saslauthd server: %m", errno);
561            return SASL_FAIL;
562        }
563    }
564
565    {
566        struct iovec iov[8];
567 
568        iov[0].iov_len = query_end - query;
569        iov[0].iov_base = query;
570
571        if (retry_writev(s, iov, 1) == -1) {
572            sasl_seterror(conn, 0, "write failed");
573            return SASL_FAIL;
574        }
575    }
576
577    {
578        unsigned short count = 0;
579
580        /*
581         * read response of the form:
582         *
583         * count result
584         */
585        if (retry_read(s, &count, sizeof(count)) < (int) sizeof(count)) {
586            sasl_seterror(conn, 0, "size read failed");
587            return SASL_FAIL;
588        }
589       
590        count = ntohs(count);
591        if (count < 2) { /* MUST have at least "OK" or "NO" */
592            close(s);
593            sasl_seterror(conn, 0, "bad response from saslauthd");
594            return SASL_FAIL;
595        }
596       
597        count = (int)sizeof(response) < count ? sizeof(response) : count;
598        if (retry_read(s, response, count) < count) {
599            close(s);
600            sasl_seterror(conn, 0, "read failed");
601            return SASL_FAIL;
602        }
603        response[count] = '\0';
604    }
605
606    close(s);
607#endif /* USE_DOORS */
608 
609    if (!strncmp(response, "OK", 2)) {
610        return SASL_OK;
611    }
612 
613    sasl_seterror(conn, SASL_NOLOG, "authentication failed");
614    return SASL_BADAUTH;
615}
616
617#endif
618
619#ifdef HAVE_ALWAYSTRUE
620static int always_true(sasl_conn_t *conn,
621                       const char *userstr,
622                       const char *passwd __attribute__((unused)),
623                       const char *service __attribute__((unused)),
624                       const char *user_realm __attribute__((unused)))
625{
626    _sasl_log(conn, SASL_LOG_WARN, "AlwaysTrue Password Verifier Verified: %s",
627              userstr);
628    return SASL_OK;
629}
630#endif
631
632struct sasl_verify_password_s _sasl_verify_password[] = {
633    { "auxprop", &auxprop_verify_password },
634#ifdef HAVE_PWCHECK
635    { "pwcheck", &pwcheck_verify_password },
636#endif
637#ifdef HAVE_SASLAUTHD
638    { "saslauthd", &saslauthd_verify_password },
639#endif
640#ifdef HAVE_ALWAYSTRUE
641    { "alwaystrue", &always_true },
642#endif     
643    { NULL, NULL }
644};
Note: See TracBrowser for help on using the repository browser.