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