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