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