pam_unix.c revision 89529
1/*- 2 * Copyright 1998 Juniper Networks, Inc. 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 */ 26 27#include <sys/cdefs.h> 28__FBSDID("$FreeBSD: head/lib/libpam/modules/pam_unix/pam_unix.c 89529 2002-01-19 02:45:24Z ache $"); 29 30#include <sys/types.h> 31#include <sys/time.h> 32#ifdef YP 33#include <rpc/rpc.h> 34#include <rpcsvc/yp_prot.h> 35#include <rpcsvc/ypclnt.h> 36#include <rpcsvc/yppasswd.h> 37#endif 38#include <login_cap.h> 39#include <pwd.h> 40#include <stdlib.h> 41#include <string.h> 42#include <stdio.h> 43#include <unistd.h> 44 45#include <pw_copy.h> 46#include <pw_util.h> 47 48#ifdef YP 49#include <pw_yp.h> 50#include "yppasswd_private.h" 51#endif 52 53#define PAM_SM_AUTH 54#define PAM_SM_ACCOUNT 55#define PAM_SM_SESSION 56#define PAM_SM_PASSWORD 57 58#include <security/pam_modules.h> 59 60#include "pam_mod_misc.h" 61 62#define USER_PROMPT "Username: " 63#define PASSWORD_PROMPT "Password:" 64#define PASSWORD_PROMPT_EXPIRED "\nPassword expired\nOld Password:" 65#define NEW_PASSWORD_PROMPT_1 "New Password:" 66#define NEW_PASSWORD_PROMPT_2 "New Password (again):" 67#define PASSWORD_HASH "md5" 68#define DEFAULT_WARN (2L * 7L * 86400L) /* Two weeks */ 69#define MAX_TRIES 3 70 71enum { PAM_OPT_AUTH_AS_SELF=PAM_OPT_STD_MAX, PAM_OPT_NULLOK, PAM_OPT_LOCAL_PASS, PAM_OPT_NIS_PASS }; 72 73static struct opttab other_options[] = { 74 { "auth_as_self", PAM_OPT_AUTH_AS_SELF }, 75 { "nullok", PAM_OPT_NULLOK }, 76 { "local_pass", PAM_OPT_LOCAL_PASS }, 77 { "nis_pass", PAM_OPT_NIS_PASS }, 78 { NULL, 0 } 79}; 80 81#ifdef YP 82int pam_use_yp = 0; 83int yp_errno = YP_TRUE; 84#endif 85 86char *tempname = NULL; 87static int local_passwd(const char *user, const char *pass); 88#ifdef YP 89static int yp_passwd(const char *user, const char *pass); 90#endif 91 92/* 93 * authentication management 94 */ 95PAM_EXTERN int 96pam_sm_authenticate(pam_handle_t *pamh, int flags, int argc, const char **argv) 97{ 98 login_cap_t *lc; 99 struct options options; 100 struct passwd *pwd; 101 int retval; 102 const char *pass, *user; 103 char *encrypted, *password_prompt; 104 105 pam_std_option(&options, other_options, argc, argv); 106 107 PAM_LOG("Options processed"); 108 109 if (pam_test_option(&options, PAM_OPT_AUTH_AS_SELF, NULL)) 110 pwd = getpwnam(getlogin()); 111 else { 112 retval = pam_get_user(pamh, &user, NULL); 113 if (retval != PAM_SUCCESS) 114 PAM_RETURN(retval); 115 pwd = getpwnam(user); 116 } 117 118 PAM_LOG("Got user: %s", user); 119 120 lc = login_getclass(NULL); 121 password_prompt = login_getcapstr(lc, "passwd_prompt", 122 PASSWORD_PROMPT, PASSWORD_PROMPT); 123 login_close(lc); 124 lc = NULL; 125 126 if (pwd != NULL) { 127 128 PAM_LOG("Doing real authentication"); 129 130 if (pwd->pw_passwd[0] == '\0' 131 && pam_test_option(&options, PAM_OPT_NULLOK, NULL)) { 132 /* 133 * No password case. XXX Are we giving too much away 134 * by not prompting for a password? 135 */ 136 PAM_LOG("No password, and null password OK"); 137 PAM_RETURN(PAM_SUCCESS); 138 } 139 else { 140 retval = pam_get_pass(pamh, &pass, password_prompt, 141 &options); 142 if (retval != PAM_SUCCESS) 143 PAM_RETURN(retval); 144 PAM_LOG("Got password"); 145 } 146 encrypted = crypt(pass, pwd->pw_passwd); 147 if (pass[0] == '\0' && pwd->pw_passwd[0] != '\0') 148 encrypted = ":"; 149 150 PAM_LOG("Encrypted password 1 is: %s", encrypted); 151 PAM_LOG("Encrypted password 2 is: %s", pwd->pw_passwd); 152 153 retval = strcmp(encrypted, pwd->pw_passwd) == 0 ? 154 PAM_SUCCESS : PAM_AUTH_ERR; 155 if (pwd->pw_expire && time(NULL) >= pwd->pw_expire) 156 retval = PAM_AUTH_ERR; 157 } 158 else { 159 160 PAM_LOG("Doing dummy authentication"); 161 162 /* 163 * User unknown. 164 * Encrypt a dummy password so as to not give away too much. 165 */ 166 retval = pam_get_pass(pamh, &pass, password_prompt, 167 &options); 168 if (retval != PAM_SUCCESS) 169 PAM_RETURN(retval); 170 PAM_LOG("Got password"); 171 crypt(pass, "xx"); 172 retval = PAM_AUTH_ERR; 173 } 174 175 /* 176 * The PAM infrastructure will obliterate the cleartext 177 * password before returning to the application. 178 */ 179 if (retval != PAM_SUCCESS) 180 PAM_VERBOSE_ERROR("UNIX authentication refused"); 181 182 PAM_RETURN(retval); 183} 184 185PAM_EXTERN int 186pam_sm_setcred(pam_handle_t *pamh, int flags, int argc, const char **argv) 187{ 188 struct options options; 189 190 pam_std_option(&options, other_options, argc, argv); 191 192 PAM_LOG("Options processed"); 193 194 PAM_RETURN(PAM_SUCCESS); 195} 196 197/* 198 * account management 199 */ 200PAM_EXTERN int 201pam_sm_acct_mgmt(pam_handle_t *pamh, int flags, int argc, const char **argv) 202{ 203 struct options options; 204 struct passwd *pw; 205 struct timeval tp; 206 login_cap_t *lc; 207 time_t warntime; 208 int retval; 209 const char *user; 210 char buf[128]; 211 212 pam_std_option(&options, other_options, argc, argv); 213 214 PAM_LOG("Options processed"); 215 216 retval = pam_get_item(pamh, PAM_USER, (const void **)&user); 217 if (retval != PAM_SUCCESS || user == NULL) 218 /* some implementations return PAM_SUCCESS here */ 219 PAM_RETURN(PAM_USER_UNKNOWN); 220 221 pw = getpwnam(user); 222 if (pw == NULL) 223 PAM_RETURN(PAM_USER_UNKNOWN); 224 225 PAM_LOG("Got user: %s", user); 226 227 retval = PAM_SUCCESS; 228 lc = login_getpwclass(pw); 229 230 if (pw->pw_change || pw->pw_expire) 231 gettimeofday(&tp, NULL); 232 233 warntime = login_getcaptime(lc, "warnpassword", DEFAULT_WARN, 234 DEFAULT_WARN); 235 236 PAM_LOG("Got login_cap"); 237 238 if (pw->pw_change) { 239 if (tp.tv_sec >= pw->pw_change) 240 /* some implementations return PAM_AUTHTOK_EXPIRED */ 241 retval = PAM_NEW_AUTHTOK_REQD; 242 else if (pw->pw_change - tp.tv_sec < warntime) { 243 snprintf(buf, sizeof(buf), 244 "Warning: your password expires on %s", 245 ctime(&pw->pw_change)); 246 pam_prompt(pamh, PAM_ERROR_MSG, buf, NULL); 247 } 248 } 249 250 warntime = login_getcaptime(lc, "warnexpire", DEFAULT_WARN, 251 DEFAULT_WARN); 252 253 if (pw->pw_expire) { 254 if (tp.tv_sec >= pw->pw_expire) 255 retval = PAM_ACCT_EXPIRED; 256 else if (pw->pw_expire - tp.tv_sec < warntime) { 257 snprintf(buf, sizeof(buf), 258 "Warning: your account expires on %s", 259 ctime(&pw->pw_expire)); 260 pam_prompt(pamh, PAM_ERROR_MSG, buf, NULL); 261 } 262 } 263 264 login_close(lc); 265 266 PAM_RETURN(retval); 267} 268 269/* 270 * session management 271 * 272 * logging only 273 */ 274PAM_EXTERN int 275pam_sm_open_session(pam_handle_t *pamh, int flags, int argc, const char **argv) 276{ 277 struct options options; 278 279 pam_std_option(&options, other_options, argc, argv); 280 281 PAM_LOG("Options processed"); 282 283 PAM_RETURN(PAM_SUCCESS); 284} 285 286PAM_EXTERN int 287pam_sm_close_session(pam_handle_t *pamh, int flags, int argc, const char **argv) 288{ 289 struct options options; 290 291 pam_std_option(&options, other_options, argc, argv); 292 293 PAM_LOG("Options processed"); 294 295 PAM_RETURN(PAM_SUCCESS); 296} 297 298/* 299 * password management 300 * 301 * standard Unix and NIS password changing 302 */ 303PAM_EXTERN int 304pam_sm_chauthtok(pam_handle_t *pamh, int flags, int argc, const char **argv) 305{ 306 struct options options; 307 struct passwd *pwd; 308 int retval, retry, res, got; 309 const char *user, *pass; 310 char *new_pass, *new_pass_, *encrypted; 311 312 pam_std_option(&options, other_options, argc, argv); 313 314 PAM_LOG("Options processed"); 315 316 if (pam_test_option(&options, PAM_OPT_AUTH_AS_SELF, NULL)) 317 pwd = getpwnam(getlogin()); 318 else { 319 retval = pam_get_user(pamh, &user, NULL); 320 if (retval != PAM_SUCCESS) 321 PAM_RETURN(retval); 322 pwd = getpwnam(user); 323 } 324 325 PAM_LOG("Got user: %s", user); 326 327 if (flags & PAM_PRELIM_CHECK) { 328 329 PAM_LOG("PRELIM round; checking user password"); 330 331 if (pwd->pw_passwd[0] == '\0' 332 && pam_test_option(&options, PAM_OPT_NULLOK, NULL)) { 333 /* 334 * No password case. XXX Are we giving too much away 335 * by not prompting for a password? 336 */ 337 PAM_LOG("No password, and null password OK"); 338 PAM_RETURN(PAM_SUCCESS); 339 } 340 else { 341 retval = pam_get_pass(pamh, &pass, 342 PASSWORD_PROMPT_EXPIRED, &options); 343 if (retval != PAM_SUCCESS) 344 PAM_RETURN(retval); 345 PAM_LOG("Got password: %s", pass); 346 } 347 encrypted = crypt(pass, pwd->pw_passwd); 348 if (pass[0] == '\0' && pwd->pw_passwd[0] != '\0') 349 encrypted = ":"; 350 351 PAM_LOG("Encrypted password 1 is: %s", encrypted); 352 PAM_LOG("Encrypted password 2 is: %s", pwd->pw_passwd); 353 354 if (strcmp(encrypted, pwd->pw_passwd) != 0) 355 PAM_RETURN(PAM_AUTH_ERR); 356 357 retval = pam_set_item(pamh, PAM_OLDAUTHTOK, (const void *)pass); 358 pass = NULL; 359 if (retval != PAM_SUCCESS) 360 PAM_RETURN(retval); 361 362 PAM_LOG("Stashed old password"); 363 364 retval = pam_set_item(pamh, PAM_AUTHTOK, (const void *)pass); 365 if (retval != PAM_SUCCESS) 366 PAM_RETURN(retval); 367 368 PAM_LOG("Voided old password"); 369 370 PAM_RETURN(PAM_SUCCESS); 371 } 372 else if (flags & PAM_UPDATE_AUTHTOK) { 373 PAM_LOG("UPDATE round; checking user password"); 374 375 retval = pam_get_item(pamh, PAM_OLDAUTHTOK, 376 (const void **)&pass); 377 if (retval != PAM_SUCCESS) 378 PAM_RETURN(retval); 379 380 PAM_LOG("Got old password: %s", pass); 381 382 got = 0; 383 retry = 0; 384 while (retry++ < MAX_TRIES) { 385 new_pass = NULL; 386 retval = pam_prompt(pamh, PAM_PROMPT_ECHO_OFF, 387 NEW_PASSWORD_PROMPT_1, &new_pass); 388 389 if (new_pass == NULL) 390 new_pass = ""; 391 392 if (retval == PAM_SUCCESS) { 393 new_pass_ = NULL; 394 retval = pam_prompt(pamh, PAM_PROMPT_ECHO_OFF, 395 NEW_PASSWORD_PROMPT_2, &new_pass_); 396 397 if (new_pass_ == NULL) 398 new_pass_ = ""; 399 400 if (retval == PAM_SUCCESS) { 401 if (strcmp(new_pass, new_pass_) == 0) { 402 got = 1; 403 break; 404 } 405 else 406 PAM_VERBOSE_ERROR("Password mismatch"); 407 } 408 } 409 } 410 411 if (!got) { 412 PAM_VERBOSE_ERROR("Unable to get valid password"); 413 PAM_RETURN(PAM_PERM_DENIED); 414 } 415 416 PAM_LOG("Got new password: %s", new_pass); 417 418#ifdef YP 419 /* If NIS is set in the passwd database, use it */ 420 res = use_yp((char *)user, 0, 0); 421 if (res == USER_YP_ONLY) { 422 if (!pam_test_option(&options, PAM_OPT_LOCAL_PASS, 423 NULL)) 424 retval = yp_passwd(user, new_pass); 425 else { 426 /* Reject 'local' flag if NIS is on and the user 427 * is not local 428 */ 429 retval = PAM_PERM_DENIED; 430 PAM_LOG("Unknown local user: %s", user); 431 } 432 } 433 else if (res == USER_LOCAL_ONLY) { 434 if (!pam_test_option(&options, PAM_OPT_NIS_PASS, NULL)) 435 retval = local_passwd(user, new_pass); 436 else { 437 /* Reject 'nis' flag if user is only local */ 438 retval = PAM_PERM_DENIED; 439 PAM_LOG("Unknown NIS user: %s", user); 440 } 441 } 442 else if (res == USER_YP_AND_LOCAL) { 443 if (pam_test_option(&options, PAM_OPT_NIS_PASS, NULL)) 444 retval = yp_passwd(user, new_pass); 445 else 446 retval = local_passwd(user, new_pass); 447 } 448 else 449 retval = PAM_ABORT; /* Bad juju */ 450#else 451 retval = local_passwd(user, new_pass); 452#endif 453 454 /* XXX wipe the mem as well */ 455 pass = NULL; 456 new_pass = NULL; 457 } 458 else { 459 /* Very bad juju */ 460 retval = PAM_ABORT; 461 PAM_LOG("Illegal 'flags'"); 462 } 463 464 PAM_RETURN(retval); 465} 466 467/* Mostly stolen from passwd(1)'s local_passwd.c - markm */ 468 469static unsigned char itoa64[] = /* 0 ... 63 => ascii - 64 */ 470 "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; 471 472static void 473to64(char *s, long v, int n) 474{ 475 while (--n >= 0) { 476 *s++ = itoa64[v&0x3f]; 477 v >>= 6; 478 } 479} 480 481static int 482local_passwd(const char *user, const char *pass) 483{ 484 login_cap_t * lc; 485 struct passwd *pwd; 486 struct timeval tv; 487 int pfd, tfd; 488 char *crypt_type, salt[32]; 489 490 pwd = getpwnam(user); 491 if (pwd == NULL) 492 return(PAM_ABORT); /* Really bad things */ 493 494#ifdef YP 495 pwd = (struct passwd *)&local_password; 496#endif 497 pw_init(); 498 499 pwd->pw_change = 0; 500 lc = login_getclass(NULL); 501 crypt_type = login_getcapstr(lc, "passwd_format", 502 PASSWORD_HASH, PASSWORD_HASH); 503 if (login_setcryptfmt(lc, crypt_type, NULL) == NULL) 504 syslog(LOG_ERR, "cannot set password cipher"); 505 login_close(lc); 506 /* Salt suitable for anything */ 507 srandomdev(); 508 gettimeofday(&tv, 0); 509 to64(&salt[0], random(), 3); 510 to64(&salt[3], tv.tv_usec, 3); 511 to64(&salt[6], tv.tv_sec, 2); 512 to64(&salt[8], random(), 5); 513 to64(&salt[13], random(), 5); 514 to64(&salt[17], random(), 5); 515 to64(&salt[22], random(), 5); 516 salt[27] = '\0'; 517 518 pwd->pw_passwd = crypt(pass, salt); 519 520 pfd = pw_lock(); 521 tfd = pw_tmp(); 522 pw_copy(pfd, tfd, pwd); 523 524 if (!pw_mkdb((char *)user)) 525 pw_error((char *)NULL, 0, 1); 526 527 return PAM_SUCCESS; 528} 529 530#ifdef YP 531/* Stolen from src/usr.bin/passwd/yp_passwd.c, carrying copyrights of: 532 * Copyright (c) 1992/3 Theo de Raadt <deraadt@fsa.ca> 533 * Copyright (c) 1994 Olaf Kirch <okir@monad.swb.de> 534 * Copyright (c) 1995 Bill Paul <wpaul@ctr.columbia.edu> 535 */ 536int 537yp_passwd(const char *user, const char *pass) 538{ 539 struct master_yppasswd master_yppasswd; 540 struct passwd *pwd; 541 struct rpc_err err; 542 struct timeval tv; 543 struct yppasswd yppasswd; 544 CLIENT *clnt; 545 login_cap_t *lc; 546 int *status; 547 uid_t uid; 548 char *master, *sockname = YP_SOCKNAME, salt[32]; 549 550 _use_yp = 1; 551 552 uid = getuid(); 553 554 master = get_yp_master(1); 555 if (master == NULL) 556 return PAM_ABORT; /* Major disaster */ 557 558 /* 559 * It is presumed that by the time we get here, use_yp() 560 * has been called and that we have verified that the user 561 * actually exists. This being the case, the yp_password 562 * stucture has already been filled in for us. 563 */ 564 565 /* Use the correct password */ 566 pwd = (struct passwd *)&yp_password; 567 568 pwd->pw_change = 0; 569 570 /* Initialize password information */ 571 if (suser_override) { 572 master_yppasswd.newpw.pw_passwd = strdup(pwd->pw_passwd); 573 master_yppasswd.newpw.pw_name = strdup(pwd->pw_name); 574 master_yppasswd.newpw.pw_uid = pwd->pw_uid; 575 master_yppasswd.newpw.pw_gid = pwd->pw_gid; 576 master_yppasswd.newpw.pw_expire = pwd->pw_expire; 577 master_yppasswd.newpw.pw_change = pwd->pw_change; 578 master_yppasswd.newpw.pw_fields = pwd->pw_fields; 579 master_yppasswd.newpw.pw_gecos = strdup(pwd->pw_gecos); 580 master_yppasswd.newpw.pw_dir = strdup(pwd->pw_dir); 581 master_yppasswd.newpw.pw_shell = strdup(pwd->pw_shell); 582 master_yppasswd.newpw.pw_class = pwd->pw_class != NULL ? 583 strdup(pwd->pw_class) : ""; 584 master_yppasswd.oldpass = ""; 585 master_yppasswd.domain = yp_domain; 586 } else { 587 yppasswd.newpw.pw_passwd = strdup(pwd->pw_passwd); 588 yppasswd.newpw.pw_name = strdup(pwd->pw_name); 589 yppasswd.newpw.pw_uid = pwd->pw_uid; 590 yppasswd.newpw.pw_gid = pwd->pw_gid; 591 yppasswd.newpw.pw_gecos = strdup(pwd->pw_gecos); 592 yppasswd.newpw.pw_dir = strdup(pwd->pw_dir); 593 yppasswd.newpw.pw_shell = strdup(pwd->pw_shell); 594 yppasswd.oldpass = ""; 595 } 596 597 if (login_setcryptfmt(lc, "md5", NULL) == NULL) 598 syslog(LOG_ERR, "cannot set password cipher"); 599 login_close(lc); 600 /* Salt suitable for anything */ 601 srandomdev(); 602 gettimeofday(&tv, 0); 603 to64(&salt[0], random(), 3); 604 to64(&salt[3], tv.tv_usec, 3); 605 to64(&salt[6], tv.tv_sec, 2); 606 to64(&salt[8], random(), 5); 607 to64(&salt[13], random(), 5); 608 to64(&salt[17], random(), 5); 609 to64(&salt[22], random(), 5); 610 salt[27] = '\0'; 611 612 if (suser_override) 613 master_yppasswd.newpw.pw_passwd = crypt(pass, salt); 614 else 615 yppasswd.newpw.pw_passwd = crypt(pass, salt); 616 617 if (suser_override) { 618 if ((clnt = clnt_create(sockname, MASTER_YPPASSWDPROG, 619 MASTER_YPPASSWDVERS, "unix")) == NULL) { 620 syslog(LOG_ERR, 621 "Cannot contact rpc.yppasswdd on host %s: %s", 622 master, clnt_spcreateerror("")); 623 return PAM_ABORT; 624 } 625 } 626 else { 627 if ((clnt = clnt_create(master, YPPASSWDPROG, 628 YPPASSWDVERS, "udp")) == NULL) { 629 syslog(LOG_ERR, 630 "Cannot contact rpc.yppasswdd on host %s: %s", 631 master, clnt_spcreateerror("")); 632 return PAM_ABORT; 633 } 634 } 635 /* 636 * The yppasswd.x file said `unix authentication required', 637 * so I added it. This is the only reason it is in here. 638 * My yppasswdd doesn't use it, but maybe some others out there 639 * do. --okir 640 */ 641 clnt->cl_auth = authunix_create_default(); 642 643 if (suser_override) 644 status = yppasswdproc_update_master_1(&master_yppasswd, clnt); 645 else 646 status = yppasswdproc_update_1(&yppasswd, clnt); 647 648 clnt_geterr(clnt, &err); 649 650 auth_destroy(clnt->cl_auth); 651 clnt_destroy(clnt); 652 653 if (err.re_status != RPC_SUCCESS || status == NULL || *status) 654 return PAM_ABORT; 655 656 return (err.re_status || status == NULL || *status) 657 ? PAM_ABORT : PAM_SUCCESS; 658} 659#endif /* YP */ 660 661PAM_MODULE_ENTRY("pam_unix"); 662