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