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$"); 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 71 enum { PAM_OPT_AUTH_AS_SELF=PAM_OPT_STD_MAX, PAM_OPT_NULLOK, PAM_OPT_LOCAL_PASS, PAM_OPT_NIS_PASS }; 72 73 static 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 82 int pam_use_yp = 0; 83 int yp_errno = YP_TRUE; 84 #endif 85 86 char *tempname = NULL; 87 static int local_passwd(const char *user, const char *pass); 88 #ifdef YP 89 static int yp_passwd(const char *user, const char *pass); 90 #endif 91 92 /* 93 * authentication management 94 */ 95 PAM_EXTERN int 96 pam_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 } 156 else { 157 158 PAM_LOG("Doing dummy authentication"); 159 160 /* 161 * User unknown. 162 * Encrypt a dummy password so as to not give away too much. 163 */ 164 retval = pam_get_pass(pamh, &pass, password_prompt, 165 &options); 166 if (retval != PAM_SUCCESS) 167 PAM_RETURN(retval); 168 PAM_LOG("Got password"); 169 crypt(pass, "xx"); 170 retval = PAM_AUTH_ERR; 171 } 172 173 /* 174 * The PAM infrastructure will obliterate the cleartext 175 * password before returning to the application. 176 */ 177 if (retval != PAM_SUCCESS) 178 PAM_VERBOSE_ERROR("UNIX authentication refused"); 179 180 PAM_RETURN(retval); 181 } 182 183 PAM_EXTERN int 184 pam_sm_setcred(pam_handle_t *pamh, int flags, int argc, const char **argv) 185 { 186 struct options options; 187 188 pam_std_option(&options, other_options, argc, argv); 189 190 PAM_LOG("Options processed"); 191 192 PAM_RETURN(PAM_SUCCESS); 193 } 194 195 /* 196 * account management 197 */ 198 PAM_EXTERN int 199 pam_sm_acct_mgmt(pam_handle_t *pamh, int flags, int argc, const char **argv) 200 { 201 struct options options; 202 struct passwd *pw; 203 struct timeval tp; 204 login_cap_t *lc; 205 time_t warntime; 206 int retval; 207 const char *user; 208 char buf[128]; 209 210 pam_std_option(&options, other_options, argc, argv); 211 212 PAM_LOG("Options processed"); 213 214 retval = pam_get_item(pamh, PAM_USER, (const void **)&user); 215 if (retval != PAM_SUCCESS || user == NULL) 216 /* some implementations return PAM_SUCCESS here */ 217 PAM_RETURN(PAM_USER_UNKNOWN); 218 219 pw = getpwnam(user); 220 if (pw == NULL) 221 PAM_RETURN(PAM_USER_UNKNOWN); 222 223 PAM_LOG("Got user: %s", user); 224 225 retval = PAM_SUCCESS; 226 lc = login_getpwclass(pw); 227 228 if (pw->pw_change || pw->pw_expire) 229 gettimeofday(&tp, NULL); 230 231 warntime = login_getcaptime(lc, "warnpassword", DEFAULT_WARN, 232 DEFAULT_WARN); 233 234 PAM_LOG("Got login_cap"); 235 236 if (pw->pw_change) { 237 if (tp.tv_sec >= pw->pw_change) 238 /* some implementations return PAM_AUTHTOK_EXPIRED */ 239 retval = PAM_NEW_AUTHTOK_REQD; 240 else if (pw->pw_change - tp.tv_sec < warntime) { 241 snprintf(buf, sizeof(buf), 242 "Warning: your password expires on %s", 243 ctime(&pw->pw_change)); 244 pam_prompt(pamh, PAM_ERROR_MSG, buf, NULL); 245 } 246 } 247 248 warntime = login_getcaptime(lc, "warnexpire", DEFAULT_WARN, 249 DEFAULT_WARN); 250 251 if (pw->pw_expire) { 252 if (tp.tv_sec >= pw->pw_expire) 253 retval = PAM_ACCT_EXPIRED; 254 else if (pw->pw_expire - tp.tv_sec < warntime) { 255 snprintf(buf, sizeof(buf), 256 "Warning: your account expires on %s", 257 ctime(&pw->pw_expire)); 258 pam_prompt(pamh, PAM_ERROR_MSG, buf, NULL); 259 } 260 } 261 262 login_close(lc); 263 264 PAM_RETURN(retval); 265 } 266 267 /* 268 * session management 269 * 270 * logging only 271 */ 272 PAM_EXTERN int 273 pam_sm_open_session(pam_handle_t *pamh, int flags, int argc, const char **argv) 274 { 275 struct options options; 276 277 pam_std_option(&options, other_options, argc, argv); 278 279 PAM_LOG("Options processed"); 280 281 PAM_RETURN(PAM_SUCCESS); 282 } 283 284 PAM_EXTERN int 285 pam_sm_close_session(pam_handle_t *pamh, int flags, int argc, const char **argv) 286 { 287 struct options options; 288 289 pam_std_option(&options, other_options, argc, argv); 290 291 PAM_LOG("Options processed"); 292 293 PAM_RETURN(PAM_SUCCESS); 294 } 295 296 /* 297 * password management 298 * 299 * standard Unix and NIS password changing 300 */ 301 PAM_EXTERN int 302 pam_sm_chauthtok(pam_handle_t *pamh, int flags, int argc, const char **argv) 303 { 304 struct options options; 305 struct passwd *pwd; 306 int retval, retry, res, got; 307 const char *user, *pass; 308 char *new_pass, *new_pass_, *encrypted; 309 310 pam_std_option(&options, other_options, argc, argv); 311 312 PAM_LOG("Options processed"); 313 314 if (pam_test_option(&options, PAM_OPT_AUTH_AS_SELF, NULL)) 315 pwd = getpwnam(getlogin()); 316 else { 317 retval = pam_get_user(pamh, &user, NULL); 318 if (retval != PAM_SUCCESS) 319 PAM_RETURN(retval); 320 pwd = getpwnam(user); 321 } 322 323 PAM_LOG("Got user: %s", user); 324 325 if (flags & PAM_PRELIM_CHECK) { 326 327 PAM_LOG("PRELIM round; checking user password"); 328 329 if (pwd->pw_passwd[0] == '\0' 330 && pam_test_option(&options, PAM_OPT_NULLOK, NULL)) { 331 /* 332 * No password case. XXX Are we giving too much away 333 * by not prompting for a password? 334 */ 335 PAM_LOG("No password, and null password OK"); 336 PAM_RETURN(PAM_SUCCESS); 337 } 338 else { 339 retval = pam_get_pass(pamh, &pass, 340 PASSWORD_PROMPT_EXPIRED, &options); 341 if (retval != PAM_SUCCESS) 342 PAM_RETURN(retval); 343 PAM_LOG("Got password: %s", pass); 344 } 345 encrypted = crypt(pass, pwd->pw_passwd); 346 if (pass[0] == '\0' && pwd->pw_passwd[0] != '\0') 347 encrypted = ":"; 348 349 PAM_LOG("Encrypted password 1 is: %s", encrypted); 350 PAM_LOG("Encrypted password 2 is: %s", pwd->pw_passwd); 351 352 if (strcmp(encrypted, pwd->pw_passwd) != 0) 353 PAM_RETURN(PAM_AUTH_ERR); 354 355 retval = pam_set_item(pamh, PAM_OLDAUTHTOK, (const void *)pass); 356 pass = NULL; 357 if (retval != PAM_SUCCESS) 358 PAM_RETURN(retval); 359 360 PAM_LOG("Stashed old password"); 361 362 retval = pam_set_item(pamh, PAM_AUTHTOK, (const void *)pass); 363 if (retval != PAM_SUCCESS) 364 PAM_RETURN(retval); 365 366 PAM_LOG("Voided old password"); 367 368 PAM_RETURN(PAM_SUCCESS); 369 } 370 else if (flags & PAM_UPDATE_AUTHTOK) { 371 PAM_LOG("UPDATE round; checking user password"); 372 373 retval = pam_get_item(pamh, PAM_OLDAUTHTOK, 374 (const void **)&pass); 375 if (retval != PAM_SUCCESS) 376 PAM_RETURN(retval); 377 378 PAM_LOG("Got old password: %s", pass); 379 380 got = 0; 381 retry = 0; 382 while (retry++ < MAX_TRIES) { 383 new_pass = NULL; 384 retval = pam_prompt(pamh, PAM_PROMPT_ECHO_OFF, 385 NEW_PASSWORD_PROMPT_1, &new_pass); 386 387 if (new_pass == NULL) 388 new_pass = ""; 389 390 if (retval == PAM_SUCCESS) { 391 new_pass_ = NULL; 392 retval = pam_prompt(pamh, PAM_PROMPT_ECHO_OFF, 393 NEW_PASSWORD_PROMPT_2, &new_pass_); 394 395 if (new_pass_ == NULL) 396 new_pass_ = ""; 397 398 if (retval == PAM_SUCCESS) { 399 if (strcmp(new_pass, new_pass_) == 0) { 400 got = 1; 401 break; 402 } 403 else 404 PAM_VERBOSE_ERROR("Password mismatch"); 405 } 406 } 407 } 408 409 if (!got) { 410 PAM_VERBOSE_ERROR("Unable to get valid password"); 411 PAM_RETURN(PAM_PERM_DENIED); 412 } 413 414 PAM_LOG("Got new password: %s", new_pass); 415 416 #ifdef YP 417 /* If NIS is set in the passwd database, use it */ 418 res = use_yp((char *)user, 0, 0); 419 if (res == USER_YP_ONLY) { 420 if (!pam_test_option(&options, PAM_OPT_LOCAL_PASS, 421 NULL)) 422 retval = yp_passwd(user, new_pass); 423 else { 424 /* Reject 'local' flag if NIS is on and the user 425 * is not local 426 */ 427 retval = PAM_PERM_DENIED; 428 PAM_LOG("Unknown local user: %s", user); 429 } 430 } 431 else if (res == USER_LOCAL_ONLY) { 432 if (!pam_test_option(&options, PAM_OPT_NIS_PASS, NULL)) 433 retval = local_passwd(user, new_pass); 434 else { 435 /* Reject 'nis' flag if user is only local */ 436 retval = PAM_PERM_DENIED; 437 PAM_LOG("Unknown NIS user: %s", user); 438 } 439 } 440 else if (res == USER_YP_AND_LOCAL) { 441 if (pam_test_option(&options, PAM_OPT_NIS_PASS, NULL)) 442 retval = yp_passwd(user, new_pass); 443 else 444 retval = local_passwd(user, new_pass); 445 } 446 else 447 retval = PAM_ABORT; /* Bad juju */ 448 #else 449 retval = local_passwd(user, new_pass); 450 #endif 451 452 /* XXX wipe the mem as well */ 453 pass = NULL; 454 new_pass = NULL; 455 } 456 else { 457 /* Very bad juju */ 458 retval = PAM_ABORT; 459 PAM_LOG("Illegal 'flags'"); 460 } 461 462 PAM_RETURN(retval); 463 } 464 465 /* Mostly stolen from passwd(1)'s local_passwd.c - markm */ 466 467 static unsigned char itoa64[] = /* 0 ... 63 => ascii - 64 */ 468 "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; 469 470 static void 471 to64(char *s, long v, int n) 472 { 473 while (--n >= 0) { 474 *s++ = itoa64[v&0x3f]; 475 v >>= 6; 476 } 477 } 478 479 static int 480 local_passwd(const char *user, const char *pass) 481 { 482 login_cap_t * lc; 483 struct passwd *pwd; 484 struct timeval tv; 485 int pfd, tfd; 486 char *crypt_type, salt[32]; 487 488 pwd = getpwnam(user); 489 if (pwd == NULL) 490 return(PAM_ABORT); /* Really bad things */ 491 492 #ifdef YP 493 pwd = (struct passwd *)&local_password; 494 #endif 495 pw_init(); 496 497 pwd->pw_change = 0; 498 lc = login_getclass(NULL); 499 crypt_type = login_getcapstr(lc, "passwd_format", 500 PASSWORD_HASH, PASSWORD_HASH); 501 if (login_setcryptfmt(lc, crypt_type, NULL) == NULL) 502 syslog(LOG_ERR, "cannot set password cipher"); 503 login_close(lc); 504 /* Salt suitable for anything */ 505 srandomdev(); 506 gettimeofday(&tv, 0); 507 to64(&salt[0], random(), 3); 508 to64(&salt[3], tv.tv_usec, 3); 509 to64(&salt[6], tv.tv_sec, 2); 510 to64(&salt[8], random(), 5); 511 to64(&salt[13], random(), 5); 512 to64(&salt[17], random(), 5); 513 to64(&salt[22], random(), 5); 514 salt[27] = '\0'; 515 516 pwd->pw_passwd = crypt(pass, salt); 517 518 pfd = pw_lock(); 519 tfd = pw_tmp(); 520 pw_copy(pfd, tfd, pwd); 521 522 if (!pw_mkdb((char *)user)) 523 pw_error((char *)NULL, 0, 1); 524 525 return PAM_SUCCESS; 526 } 527 528 #ifdef YP 529 /* Stolen from src/usr.bin/passwd/yp_passwd.c, carrying copyrights of: 530 * Copyright (c) 1992/3 Theo de Raadt <deraadt@fsa.ca> 531 * Copyright (c) 1994 Olaf Kirch <okir@monad.swb.de> 532 * Copyright (c) 1995 Bill Paul <wpaul@ctr.columbia.edu> 533 */ 534 int 535 yp_passwd(const char *user, const char *pass) 536 { 537 struct master_yppasswd master_yppasswd; 538 struct passwd *pwd; 539 struct rpc_err err; 540 struct timeval tv; 541 struct yppasswd yppasswd; 542 CLIENT *clnt; 543 login_cap_t *lc; 544 int *status; 545 uid_t uid; 546 char *master, *sockname = YP_SOCKNAME, salt[32]; 547 548 _use_yp = 1; 549 550 uid = getuid(); 551 552 master = get_yp_master(1); 553 if (master == NULL) 554 return PAM_ABORT; /* Major disaster */ 555 556 /* 557 * It is presumed that by the time we get here, use_yp() 558 * has been called and that we have verified that the user 559 * actually exists. This being the case, the yp_password 560 * stucture has already been filled in for us. 561 */ 562 563 /* Use the correct password */ 564 pwd = (struct passwd *)&yp_password; 565 566 pwd->pw_change = 0; 567 568 /* Initialize password information */ 569 if (suser_override) { 570 master_yppasswd.newpw.pw_passwd = strdup(pwd->pw_passwd); 571 master_yppasswd.newpw.pw_name = strdup(pwd->pw_name); 572 master_yppasswd.newpw.pw_uid = pwd->pw_uid; 573 master_yppasswd.newpw.pw_gid = pwd->pw_gid; 574 master_yppasswd.newpw.pw_expire = pwd->pw_expire; 575 master_yppasswd.newpw.pw_change = pwd->pw_change; 576 master_yppasswd.newpw.pw_fields = pwd->pw_fields; 577 master_yppasswd.newpw.pw_gecos = strdup(pwd->pw_gecos); 578 master_yppasswd.newpw.pw_dir = strdup(pwd->pw_dir); 579 master_yppasswd.newpw.pw_shell = strdup(pwd->pw_shell); 580 master_yppasswd.newpw.pw_class = pwd->pw_class != NULL ? 581 strdup(pwd->pw_class) : ""; 582 master_yppasswd.oldpass = ""; 583 master_yppasswd.domain = yp_domain; 584 } else { 585 yppasswd.newpw.pw_passwd = strdup(pwd->pw_passwd); 586 yppasswd.newpw.pw_name = strdup(pwd->pw_name); 587 yppasswd.newpw.pw_uid = pwd->pw_uid; 588 yppasswd.newpw.pw_gid = pwd->pw_gid; 589 yppasswd.newpw.pw_gecos = strdup(pwd->pw_gecos); 590 yppasswd.newpw.pw_dir = strdup(pwd->pw_dir); 591 yppasswd.newpw.pw_shell = strdup(pwd->pw_shell); 592 yppasswd.oldpass = ""; 593 } 594 595 if (login_setcryptfmt(lc, "md5", NULL) == NULL) 596 syslog(LOG_ERR, "cannot set password cipher"); 597 login_close(lc); 598 /* Salt suitable for anything */ 599 srandomdev(); 600 gettimeofday(&tv, 0); 601 to64(&salt[0], random(), 3); 602 to64(&salt[3], tv.tv_usec, 3); 603 to64(&salt[6], tv.tv_sec, 2); 604 to64(&salt[8], random(), 5); 605 to64(&salt[13], random(), 5); 606 to64(&salt[17], random(), 5); 607 to64(&salt[22], random(), 5); 608 salt[27] = '\0'; 609 610 if (suser_override) 611 master_yppasswd.newpw.pw_passwd = crypt(pass, salt); 612 else 613 yppasswd.newpw.pw_passwd = crypt(pass, salt); 614 615 if (suser_override) { 616 if ((clnt = clnt_create(sockname, MASTER_YPPASSWDPROG, 617 MASTER_YPPASSWDVERS, "unix")) == NULL) { 618 syslog(LOG_ERR, 619 "Cannot contact rpc.yppasswdd on host %s: %s", 620 master, clnt_spcreateerror("")); 621 return PAM_ABORT; 622 } 623 } 624 else { 625 if ((clnt = clnt_create(master, YPPASSWDPROG, 626 YPPASSWDVERS, "udp")) == NULL) { 627 syslog(LOG_ERR, 628 "Cannot contact rpc.yppasswdd on host %s: %s", 629 master, clnt_spcreateerror("")); 630 return PAM_ABORT; 631 } 632 } 633 /* 634 * The yppasswd.x file said `unix authentication required', 635 * so I added it. This is the only reason it is in here. 636 * My yppasswdd doesn't use it, but maybe some others out there 637 * do. --okir 638 */ 639 clnt->cl_auth = authunix_create_default(); 640 641 if (suser_override) 642 status = yppasswdproc_update_master_1(&master_yppasswd, clnt); 643 else 644 status = yppasswdproc_update_1(&yppasswd, clnt); 645 646 clnt_geterr(clnt, &err); 647 648 auth_destroy(clnt->cl_auth); 649 clnt_destroy(clnt); 650 651 if (err.re_status != RPC_SUCCESS || status == NULL || *status) 652 return PAM_ABORT; 653 654 return (err.re_status || status == NULL || *status) 655 ? PAM_ABORT : PAM_SUCCESS; 656 } 657 #endif /* YP */ 658 659 PAM_MODULE_ENTRY("pam_unix"); 660