GSS_S_PROMPTING_NEEDED is a bit
[cyrus-sasl.git] / lib / checkpw.c
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 };