1/* SASL server API implementation
2 * Rob Siemborski
3 * Tim Martin
4 * $Id: checkpw.c,v 1.6 2005/05/17 21:56:43 snsimon 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 */
104static 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, 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 */
131static 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
232int _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 */
305static 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 */
349static 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 */
408static 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)
468static 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 */
512static 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 */
540static 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
748static 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
778static 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;
816fail:
817    if (s >= 0)
818	close(s);
819    return -1;
820}
821
822static 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
853static 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
867static 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
877static 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
911static 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);
940out:
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
950static 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
962struct 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};
978