1 /* 2 * Copyright 2003 Sun Microsystems, Inc. All rights reserved. 3 * Use is subject to license terms. 4 */ 5 6 /* SASL server API implementation 7 * Rob Siemborski 8 * Tim Martin 9 * $Id: checkpw.c,v 1.62 2003/03/19 18:25:27 rjs3 Exp $ 10 */ 11 /* 12 * Copyright (c) 1998-2003 Carnegie Mellon University. All rights reserved. 13 * 14 * Redistribution and use in source and binary forms, with or without 15 * modification, are permitted provided that the following conditions 16 * are met: 17 * 18 * 1. Redistributions of source code must retain the above copyright 19 * notice, this list of conditions and the following disclaimer. 20 * 21 * 2. Redistributions in binary form must reproduce the above copyright 22 * notice, this list of conditions and the following disclaimer in 23 * the documentation and/or other materials provided with the 24 * distribution. 25 * 26 * 3. The name "Carnegie Mellon University" must not be used to 27 * endorse or promote products derived from this software without 28 * prior written permission. For permission or any other legal 29 * details, please contact 30 * Office of Technology Transfer 31 * Carnegie Mellon University 32 * 5000 Forbes Avenue 33 * Pittsburgh, PA 15213-3890 34 * (412) 268-4387, fax: (412) 268-7395 35 * tech-transfer@andrew.cmu.edu 36 * 37 * 4. Redistributions of any form whatsoever must retain the following 38 * acknowledgment: 39 * "This product includes software developed by Computing Services 40 * at Carnegie Mellon University (http://www.cmu.edu/computing/)." 41 * 42 * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO 43 * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 44 * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE 45 * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 46 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN 47 * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING 48 * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 49 */ 50 51 #include <config.h> 52 53 /* checkpw stuff */ 54 55 #include <stdio.h> 56 #include "sasl.h" 57 #include "saslutil.h" 58 #include "saslplug.h" 59 #include "saslint.h" 60 61 #include <assert.h> 62 #ifdef HAVE_UNISTD_H 63 #include <unistd.h> 64 #endif 65 #include <fcntl.h> 66 #ifdef USE_DOORS 67 #include <sys/mman.h> 68 #include <door.h> 69 #endif 70 71 #include <stdlib.h> 72 73 #ifndef WIN32 74 #include <strings.h> 75 #include <netdb.h> 76 #include <netinet/in.h> 77 #include <sys/un.h> 78 #else 79 #include <string.h> 80 #endif 81 82 #include <sys/types.h> 83 #include <ctype.h> 84 85 #ifdef HAVE_PWD_H 86 #include <pwd.h> 87 #endif /* HAVE_PWD_H */ 88 #ifdef HAVE_SHADOW_H 89 #include <shadow.h> 90 #endif /* HAVE_SHADOW_H */ 91 92 #if defined(HAVE_PWCHECK) || defined(HAVE_SASLAUTHD) 93 # include <errno.h> 94 # include <sys/types.h> 95 # include <sys/socket.h> 96 # include <sys/un.h> 97 # ifdef HAVE_UNISTD_H 98 # include <unistd.h> 99 # endif 100 101 extern int errno; 102 #endif 103 104 105 /* we store the following secret to check plaintext passwords: 106 * 107 * <salt> \0 <secret> 108 * 109 * where <secret> = MD5(<salt>, "sasldb", <pass>) 110 */ 111 #ifdef _SUN_SDK_ 112 static int _sasl_make_plain_secret(const sasl_utils_t *utils, const char *salt, 113 const char *passwd, size_t passlen, 114 sasl_secret_t **secret) 115 #else 116 static int _sasl_make_plain_secret(const char *salt, 117 const char *passwd, size_t passlen, 118 sasl_secret_t **secret) 119 #endif /* _SUN_SDK_ */ 120 { 121 MD5_CTX ctx; 122 unsigned sec_len = 16 + 1 + 16; /* salt + "\0" + hash */ 123 124 #ifdef _SUN_SDK_ 125 *secret = (sasl_secret_t *)utils->malloc(sizeof(sasl_secret_t) + 126 sec_len * sizeof(char)); 127 #else 128 *secret = (sasl_secret_t *) sasl_ALLOC(sizeof(sasl_secret_t) + 129 sec_len * sizeof(char)); 130 #endif /* _SUN_SDK_ */ 131 if (*secret == NULL) { 132 return SASL_NOMEM; 133 } 134 135 _sasl_MD5Init(&ctx); 136 _sasl_MD5Update(&ctx, salt, 16); 137 _sasl_MD5Update(&ctx, "sasldb", 6); 138 _sasl_MD5Update(&ctx, passwd, passlen); 139 memcpy((*secret)->data, salt, 16); 140 (*secret)->data[16] = '\0'; 141 _sasl_MD5Final((*secret)->data + 17, &ctx); 142 (*secret)->len = sec_len; 143 144 return SASL_OK; 145 } 146 147 /* erase & dispose of a sasl_secret_t 148 */ 149 static int auxprop_verify_password(sasl_conn_t *conn, 150 const char *userstr, 151 const char *passwd, 152 const char *service __attribute__((unused)), 153 const char *user_realm __attribute__((unused))) 154 { 155 int ret = SASL_FAIL; 156 char *userid = NULL; 157 #ifndef _SUN_SDK_ 158 char *realm = NULL; 159 #endif /* !_SUN_SDK_ */ 160 int result = SASL_OK; 161 sasl_server_conn_t *sconn = (sasl_server_conn_t *)conn; 162 const char *password_request[] = { SASL_AUX_PASSWORD, 163 "*cmusaslsecretPLAIN", 164 NULL }; 165 struct propval auxprop_values[3]; 166 167 if (!conn || !userstr) 168 return SASL_BADPARAM; 169 170 /* We need to clear any previous results and re-canonify to 171 * ensure correctness */ 172 173 prop_clear(sconn->sparams->propctx, 0); 174 175 /* ensure its requested */ 176 result = prop_request(sconn->sparams->propctx, password_request); 177 178 if(result != SASL_OK) return result; 179 180 result = _sasl_canon_user(conn, userstr, 0, 181 SASL_CU_AUTHID | SASL_CU_AUTHZID, 182 &(conn->oparams)); 183 if(result != SASL_OK) return result; 184 185 result = prop_getnames(sconn->sparams->propctx, password_request, 186 auxprop_values); 187 if(result < 0) 188 return result; 189 190 if((!auxprop_values[0].name 191 || !auxprop_values[0].values || !auxprop_values[0].values[0]) 192 && (!auxprop_values[1].name 193 || !auxprop_values[1].values || !auxprop_values[1].values[0])) 194 return SASL_NOUSER; 195 196 /* It is possible for us to get useful information out of just 197 * the lookup, so we won't check that we have a password until now */ 198 if(!passwd) { 199 ret = SASL_BADPARAM; 200 goto done; 201 } 202 203 /* At the point this has been called, the username has been canonified 204 * and we've done the auxprop lookup. This should be easy. */ 205 if(auxprop_values[0].name 206 && auxprop_values[0].values 207 && auxprop_values[0].values[0] 208 && !strcmp(auxprop_values[0].values[0], passwd)) { 209 /* We have a plaintext version and it matched! */ 210 return SASL_OK; 211 } else if(auxprop_values[1].name 212 && auxprop_values[1].values 213 && auxprop_values[1].values[0]) { 214 const char *db_secret = auxprop_values[1].values[0]; 215 sasl_secret_t *construct; 216 217 #ifdef _SUN_SDK_ 218 ret = _sasl_make_plain_secret(sconn->sparams->utils, db_secret, 219 passwd, strlen(passwd), 220 &construct); 221 #else 222 ret = _sasl_make_plain_secret(db_secret, passwd, 223 strlen(passwd), 224 &construct); 225 #endif /* _SUN_SDK_ */ 226 if (ret != SASL_OK) { 227 goto done; 228 } 229 230 if (!memcmp(db_secret, construct->data, construct->len)) { 231 /* password verified! */ 232 ret = SASL_OK; 233 } else { 234 /* passwords do not match */ 235 ret = SASL_BADAUTH; 236 } 237 238 #ifdef _SUN_SDK_ 239 sconn->sparams->utils->free(construct); 240 #else 241 sasl_FREE(construct); 242 #endif /* _SUN_SDK_ */ 243 } else { 244 /* passwords do not match */ 245 ret = SASL_BADAUTH; 246 } 247 248 done: 249 #ifdef _SUN_SDK_ 250 if (userid) sconn->sparams->utils->free(userid); 251 #else 252 if (userid) sasl_FREE(userid); 253 if (realm) sasl_FREE(realm); 254 #endif /* _SUN_SDK_ */ 255 256 /* We're not going to erase the property here because other people 257 * may want it */ 258 return ret; 259 } 260 261 #ifdef DO_SASL_CHECKAPOP 262 int _sasl_auxprop_verify_apop(sasl_conn_t *conn, 263 const char *userstr, 264 const char *challenge, 265 const char *response, 266 const char *user_realm __attribute__((unused))) 267 { 268 int ret = SASL_BADAUTH; 269 char *userid = NULL; 270 #ifndef _SUN_SDK_ 271 char *realm = NULL; 272 #endif /* !_SUN_SDK_ */ 273 unsigned char digest[16]; 274 char digeststr[33]; 275 const char *password_request[] = { SASL_AUX_PASSWORD, NULL }; 276 struct propval auxprop_values[2]; 277 sasl_server_conn_t *sconn = (sasl_server_conn_t *)conn; 278 MD5_CTX ctx; 279 int i; 280 281 if (!conn || !userstr || !challenge || !response) 282 PARAMERROR(conn) 283 284 /* We've done the auxprop lookup already (in our caller) */ 285 /* sadly, APOP has no provision for storing secrets */ 286 ret = prop_getnames(sconn->sparams->propctx, password_request, 287 auxprop_values); 288 if(ret < 0) { 289 #ifdef _SUN_SDK_ 290 _sasl_log(conn, SASL_LOG_ERR, "could not perform password lookup"); 291 #else 292 sasl_seterror(conn, 0, "could not perform password lookup"); 293 #endif /* _SUN_SDK_ */ 294 goto done; 295 } 296 297 if(!auxprop_values[0].name || 298 !auxprop_values[0].values || 299 !auxprop_values[0].values[0]) { 300 #ifdef _INTEGRATED_SOLARIS_ 301 sasl_seterror(conn, 0, gettext("could not find password")); 302 #else 303 sasl_seterror(conn, 0, "could not find password"); 304 #endif /* _INTEGRATED_SOLARIS_ */ 305 ret = SASL_NOUSER; 306 goto done; 307 } 308 309 _sasl_MD5Init(&ctx); 310 _sasl_MD5Update(&ctx, challenge, strlen(challenge)); 311 _sasl_MD5Update(&ctx, auxprop_values[0].values[0], 312 strlen(auxprop_values[0].values[0])); 313 _sasl_MD5Final(digest, &ctx); 314 315 /* convert digest from binary to ASCII hex */ 316 for (i = 0; i < 16; i++) 317 sprintf(digeststr + (i*2), "%02x", digest[i]); 318 319 if (!strncasecmp(digeststr, response, 32)) { 320 /* password verified! */ 321 ret = SASL_OK; 322 } else { 323 /* passwords do not match */ 324 ret = SASL_BADAUTH; 325 } 326 327 done: 328 #ifdef _INTEGRATED_SOLARIS_ 329 if (ret == SASL_BADAUTH) sasl_seterror(conn, SASL_NOLOG, 330 gettext("login incorrect")); 331 #else 332 if (ret == SASL_BADAUTH) sasl_seterror(conn, SASL_NOLOG, 333 "login incorrect"); 334 #endif /* _INTEGRATED_SOLARIS_ */ 335 #ifdef _SUN_SDK_ 336 if (userid) sconn->sparams->utils->free(userid); 337 #else 338 if (userid) sasl_FREE(userid); 339 if (realm) sasl_FREE(realm); 340 #endif /* _SUN_SDK_ */ 341 342 return ret; 343 } 344 #endif /* DO_SASL_CHECKAPOP */ 345 346 #if defined(HAVE_PWCHECK) || defined(HAVE_SASLAUTHD) 347 /* 348 * Keep calling the writev() system call with 'fd', 'iov', and 'iovcnt' 349 * until all the data is written out or an error occurs. 350 */ 351 static int retry_writev(int fd, struct iovec *iov, int iovcnt) 352 { 353 int n; 354 int i; 355 int written = 0; 356 static int iov_max = 357 #ifdef MAXIOV 358 MAXIOV 359 #else 360 #ifdef IOV_MAX 361 IOV_MAX 362 #else 363 8192 364 #endif 365 #endif 366 ; 367 368 for (;;) { 369 while (iovcnt && iov[0].iov_len == 0) { 370 iov++; 371 iovcnt--; 372 } 373 374 if (!iovcnt) return written; 375 376 n = writev(fd, iov, iovcnt > iov_max ? iov_max : iovcnt); 377 if (n == -1) { 378 if (errno == EINVAL && iov_max > 10) { 379 iov_max /= 2; 380 continue; 381 } 382 if (errno == EINTR) continue; 383 return -1; 384 } 385 386 written += n; 387 388 for (i = 0; i < iovcnt; i++) { 389 if (iov[i].iov_len > (unsigned) n) { 390 iov[i].iov_base = (char *)iov[i].iov_base + n; 391 iov[i].iov_len -= n; 392 break; 393 } 394 n -= iov[i].iov_len; 395 iov[i].iov_len = 0; 396 } 397 398 if (i == iovcnt) return written; 399 } 400 } 401 402 #endif 403 404 #ifdef HAVE_PWCHECK 405 /* pwcheck daemon-authenticated login */ 406 static int pwcheck_verify_password(sasl_conn_t *conn, 407 const char *userid, 408 const char *passwd, 409 const char *service __attribute__((unused)), 410 const char *user_realm 411 __attribute__((unused))) 412 { 413 int s; 414 struct sockaddr_un srvaddr; 415 int r; 416 struct iovec iov[10]; 417 static char response[1024]; 418 unsigned start, n; 419 char pwpath[1024]; 420 421 if (strlen(PWCHECKDIR)+8+1 > sizeof(pwpath)) return SASL_FAIL; 422 423 strcpy(pwpath, PWCHECKDIR); 424 strcat(pwpath, "/pwcheck"); 425 426 s = socket(AF_UNIX, SOCK_STREAM, 0); 427 if (s == -1) return errno; 428 429 memset((char *)&srvaddr, 0, sizeof(srvaddr)); 430 srvaddr.sun_family = AF_UNIX; 431 strncpy(srvaddr.sun_path, pwpath, sizeof(srvaddr.sun_path)); 432 r = connect(s, (struct sockaddr *)&srvaddr, sizeof(srvaddr)); 433 if (r == -1) { 434 sasl_seterror(conn,0,"cannot connect to pwcheck server"); 435 return SASL_FAIL; 436 } 437 438 iov[0].iov_base = (char *)userid; 439 iov[0].iov_len = strlen(userid)+1; 440 iov[1].iov_base = (char *)passwd; 441 iov[1].iov_len = strlen(passwd)+1; 442 443 retry_writev(s, iov, 2); 444 445 start = 0; 446 while (start < sizeof(response) - 1) { 447 n = read(s, response+start, sizeof(response) - 1 - start); 448 if (n < 1) break; 449 start += n; 450 } 451 452 close(s); 453 454 if (start > 1 && !strncmp(response, "OK", 2)) { 455 return SASL_OK; 456 } 457 458 response[start] = '\0'; 459 sasl_seterror(conn,0,response); 460 return SASL_BADAUTH; 461 } 462 463 #endif 464 465 #ifdef HAVE_SASLAUTHD 466 467 /* 468 * Keep calling the read() system call with 'fd', 'buf', and 'nbyte' 469 * until all the data is read in or an error occurs. 470 */ 471 static int retry_read(int fd, void *buf0, unsigned nbyte) 472 { 473 int n; 474 int nread = 0; 475 char *buf = buf0; 476 477 if (nbyte == 0) return 0; 478 479 for (;;) { 480 n = read(fd, buf, nbyte); 481 if (n == -1 || n == 0) { 482 if (errno == EINTR || errno == EAGAIN) continue; 483 return -1; 484 } 485 486 nread += n; 487 488 if (nread >= (int) nbyte) return nread; 489 490 buf += n; 491 nbyte -= n; 492 } 493 } 494 495 /* saslauthd-authenticated login */ 496 static int saslauthd_verify_password(sasl_conn_t *conn, 497 const char *userid, 498 const char *passwd, 499 const char *service, 500 const char *user_realm) 501 { 502 char response[1024]; 503 char query[8192]; 504 char *query_end = query; 505 int s; 506 struct sockaddr_un srvaddr; 507 sasl_getopt_t *getopt; 508 void *context; 509 char pwpath[sizeof(srvaddr.sun_path)]; 510 const char *p = NULL; 511 #ifdef USE_DOORS 512 door_arg_t arg; 513 #endif 514 515 /* check to see if the user configured a rundir */ 516 if (_sasl_getcallback(conn, SASL_CB_GETOPT, &getopt, &context) == SASL_OK) { 517 getopt(context, NULL, "saslauthd_path", &p, NULL); 518 } 519 if (p) { 520 strncpy(pwpath, p, sizeof(pwpath)); 521 } else { 522 if (strlen(PATH_SASLAUTHD_RUNDIR) + 4 + 1 > sizeof(pwpath)) 523 return SASL_FAIL; 524 525 strcpy(pwpath, PATH_SASLAUTHD_RUNDIR); 526 strcat(pwpath, "/mux"); 527 } 528 529 /* 530 * build request of the form: 531 * 532 * count authid count password count service count realm 533 */ 534 { 535 unsigned short u_len, p_len, s_len, r_len; 536 537 u_len = (strlen(userid)); 538 p_len = (strlen(passwd)); 539 s_len = (strlen(service)); 540 r_len = ((user_realm ? strlen(user_realm) : 0)); 541 542 if (u_len + p_len + s_len + r_len + 30 > (unsigned short) sizeof(query)) { 543 /* request just too damn big */ 544 sasl_seterror(conn, 0, "saslauthd request too large"); 545 return SASL_FAIL; 546 } 547 548 u_len = htons(u_len); 549 p_len = htons(p_len); 550 s_len = htons(s_len); 551 r_len = htons(r_len); 552 553 memcpy(query_end, &u_len, sizeof(unsigned short)); 554 query_end += sizeof(unsigned short); 555 while (*userid) *query_end++ = *userid++; 556 557 memcpy(query_end, &p_len, sizeof(unsigned short)); 558 query_end += sizeof(unsigned short); 559 while (*passwd) *query_end++ = *passwd++; 560 561 memcpy(query_end, &s_len, sizeof(unsigned short)); 562 query_end += sizeof(unsigned short); 563 while (*service) *query_end++ = *service++; 564 565 memcpy(query_end, &r_len, sizeof(unsigned short)); 566 query_end += sizeof(unsigned short); 567 if (user_realm) while (*user_realm) *query_end++ = *user_realm++; 568 } 569 570 #ifdef USE_DOORS 571 s = open(pwpath, O_RDONLY); 572 if (s < 0) { 573 sasl_seterror(conn, 0, "cannot open door to saslauthd server: %m", errno); 574 return SASL_FAIL; 575 } 576 577 arg.data_ptr = query; 578 arg.data_size = query_end - query; 579 arg.desc_ptr = NULL; 580 arg.desc_num = 0; 581 arg.rbuf = response; 582 arg.rsize = sizeof(response); 583 584 door_call(s, &arg); 585 586 if (arg.data_ptr != response || arg.data_size >= sizeof(response)) { 587 /* oh damn, we got back a really long response */ 588 munmap(arg.rbuf, arg.rsize); 589 sasl_seterror(conn, 0, "saslauthd sent an overly long response"); 590 return SASL_FAIL; 591 } 592 response[arg.data_size] = '\0'; 593 594 close(s); 595 #else 596 /* unix sockets */ 597 598 s = socket(AF_UNIX, SOCK_STREAM, 0); 599 if (s == -1) { 600 sasl_seterror(conn, 0, "cannot create socket for saslauthd: %m", errno); 601 return SASL_FAIL; 602 } 603 604 memset((char *)&srvaddr, 0, sizeof(srvaddr)); 605 srvaddr.sun_family = AF_UNIX; 606 strncpy(srvaddr.sun_path, pwpath, sizeof(srvaddr.sun_path)); 607 608 { 609 int r = connect(s, (struct sockaddr *) &srvaddr, sizeof(srvaddr)); 610 if (r == -1) { 611 sasl_seterror(conn, 0, "cannot connect to saslauthd server: %m", errno); 612 return SASL_FAIL; 613 } 614 } 615 616 { 617 struct iovec iov[8]; 618 619 iov[0].iov_len = query_end - query; 620 iov[0].iov_base = query; 621 622 if (retry_writev(s, iov, 1) == -1) { 623 sasl_seterror(conn, 0, "write failed"); 624 return SASL_FAIL; 625 } 626 } 627 628 { 629 unsigned short count = 0; 630 631 /* 632 * read response of the form: 633 * 634 * count result 635 */ 636 if (retry_read(s, &count, sizeof(count)) < (int) sizeof(count)) { 637 sasl_seterror(conn, 0, "size read failed"); 638 return SASL_FAIL; 639 } 640 641 count = ntohs(count); 642 if (count < 2) { /* MUST have at least "OK" or "NO" */ 643 close(s); 644 sasl_seterror(conn, 0, "bad response from saslauthd"); 645 return SASL_FAIL; 646 } 647 648 count = (int)sizeof(response) < count ? sizeof(response) : count; 649 if (retry_read(s, response, count) < count) { 650 close(s); 651 sasl_seterror(conn, 0, "read failed"); 652 return SASL_FAIL; 653 } 654 response[count] = '\0'; 655 } 656 657 close(s); 658 #endif /* USE_DOORS */ 659 660 if (!strncmp(response, "OK", 2)) { 661 return SASL_OK; 662 } 663 664 sasl_seterror(conn, SASL_NOLOG, "authentication failed"); 665 return SASL_BADAUTH; 666 } 667 668 #endif 669 670 #ifdef HAVE_ALWAYSTRUE 671 static int always_true(sasl_conn_t *conn, 672 const char *userstr, 673 const char *passwd __attribute__((unused)), 674 const char *service __attribute__((unused)), 675 const char *user_realm __attribute__((unused))) 676 { 677 _sasl_log(conn, SASL_LOG_WARN, "AlwaysTrue Password Verifier Verified: %s", 678 userstr); 679 return SASL_OK; 680 } 681 #endif 682 683 struct sasl_verify_password_s _sasl_verify_password[] = { 684 { "auxprop", &auxprop_verify_password }, 685 #ifdef HAVE_PWCHECK 686 { "pwcheck", &pwcheck_verify_password }, 687 #endif 688 #ifdef HAVE_SASLAUTHD 689 { "saslauthd", &saslauthd_verify_password }, 690 #endif 691 #ifdef HAVE_ALWAYSTRUE 692 { "alwaystrue", &always_true }, 693 #endif 694 { NULL, NULL } 695 }; 696