19a10bb17SJohn Polstra /*- 29a10bb17SJohn Polstra * Copyright 1998 Juniper Networks, Inc. 39a10bb17SJohn Polstra * All rights reserved. 4f03a4b81SDag-Erling Smørgrav * Copyright (c) 2002 Networks Associates Technology, Inc. 5e9cc7b1dSDag-Erling Smørgrav * All rights reserved. 6e9cc7b1dSDag-Erling Smørgrav * 7e9cc7b1dSDag-Erling Smørgrav * Portions of this software was developed for the FreeBSD Project by 8e9cc7b1dSDag-Erling Smørgrav * ThinkSec AS and NAI Labs, the Security Research Division of Network 9e9cc7b1dSDag-Erling Smørgrav * Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035 10e9cc7b1dSDag-Erling Smørgrav * ("CBOSS"), as part of the DARPA CHATS research program. 119a10bb17SJohn Polstra * 129a10bb17SJohn Polstra * Redistribution and use in source and binary forms, with or without 139a10bb17SJohn Polstra * modification, are permitted provided that the following conditions 149a10bb17SJohn Polstra * are met: 159a10bb17SJohn Polstra * 1. Redistributions of source code must retain the above copyright 169a10bb17SJohn Polstra * notice, this list of conditions and the following disclaimer. 179a10bb17SJohn Polstra * 2. Redistributions in binary form must reproduce the above copyright 189a10bb17SJohn Polstra * notice, this list of conditions and the following disclaimer in the 199a10bb17SJohn Polstra * documentation and/or other materials provided with the distribution. 20e9cc7b1dSDag-Erling Smørgrav * 3. The name of the author may not be used to endorse or promote 21e9cc7b1dSDag-Erling Smørgrav * products derived from this software without specific prior written 22e9cc7b1dSDag-Erling Smørgrav * permission. 239a10bb17SJohn Polstra * 249a10bb17SJohn Polstra * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 259a10bb17SJohn Polstra * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 269a10bb17SJohn Polstra * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 279a10bb17SJohn Polstra * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 289a10bb17SJohn Polstra * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 299a10bb17SJohn Polstra * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 309a10bb17SJohn Polstra * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 319a10bb17SJohn Polstra * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 329a10bb17SJohn Polstra * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 339a10bb17SJohn Polstra * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 349a10bb17SJohn Polstra * SUCH DAMAGE. 359a10bb17SJohn Polstra */ 369a10bb17SJohn Polstra 37ceaf33f5SMatthew Dillon #include <sys/cdefs.h> 38ceaf33f5SMatthew Dillon __FBSDID("$FreeBSD$"); 39ceaf33f5SMatthew Dillon 40e9cc7b1dSDag-Erling Smørgrav #include <sys/param.h> 41e9cc7b1dSDag-Erling Smørgrav #include <sys/socket.h> 42d65b34dbSJohn Polstra #include <sys/time.h> 43e9cc7b1dSDag-Erling Smørgrav #include <netinet/in.h> 44e9cc7b1dSDag-Erling Smørgrav #include <arpa/inet.h> 45e9cc7b1dSDag-Erling Smørgrav 463d55a6c0SMark Murray #ifdef YP 473d55a6c0SMark Murray #include <rpc/rpc.h> 483d55a6c0SMark Murray #include <rpcsvc/yp_prot.h> 493d55a6c0SMark Murray #include <rpcsvc/ypclnt.h> 503d55a6c0SMark Murray #include <rpcsvc/yppasswd.h> 513d55a6c0SMark Murray #endif 52e9cc7b1dSDag-Erling Smørgrav 53d65b34dbSJohn Polstra #include <login_cap.h> 54e9cc7b1dSDag-Erling Smørgrav #include <netdb.h> 559a10bb17SJohn Polstra #include <pwd.h> 569a10bb17SJohn Polstra #include <stdlib.h> 579a10bb17SJohn Polstra #include <string.h> 58d65b34dbSJohn Polstra #include <stdio.h> 597f28386aSDag-Erling Smørgrav #include <syslog.h> 609a10bb17SJohn Polstra #include <unistd.h> 619a10bb17SJohn Polstra 623d55a6c0SMark Murray #include <pw_copy.h> 633d55a6c0SMark Murray #include <pw_util.h> 643d55a6c0SMark Murray 653d55a6c0SMark Murray #ifdef YP 663d55a6c0SMark Murray #include <pw_yp.h> 673d55a6c0SMark Murray #include "yppasswd_private.h" 683d55a6c0SMark Murray #endif 693d55a6c0SMark Murray 709a10bb17SJohn Polstra #define PAM_SM_AUTH 71d65b34dbSJohn Polstra #define PAM_SM_ACCOUNT 723d55a6c0SMark Murray #define PAM_SM_SESSION 733d55a6c0SMark Murray #define PAM_SM_PASSWORD 743d55a6c0SMark Murray 758c66575dSDag-Erling Smørgrav #include <security/pam_appl.h> 769a10bb17SJohn Polstra #include <security/pam_modules.h> 778c66575dSDag-Erling Smørgrav #include <security/pam_mod_misc.h> 789a10bb17SJohn Polstra 793d55a6c0SMark Murray #define PASSWORD_HASH "md5" 801642eb1aSMark Murray #define DEFAULT_WARN (2L * 7L * 86400L) /* Two weeks */ 818c3ea588SMark Murray #define SALTSIZE 32 828c3ea588SMark Murray 838c3ea588SMark Murray static void makesalt(char []); 841642eb1aSMark Murray 85ac569969SMark Murray static char password_hash[] = PASSWORD_HASH; 868c3ea588SMark Murray static char colon[] = ":"; 87ac569969SMark Murray 88e9cc7b1dSDag-Erling Smørgrav enum { 89e9cc7b1dSDag-Erling Smørgrav PAM_OPT_AUTH_AS_SELF = PAM_OPT_STD_MAX, 90e9cc7b1dSDag-Erling Smørgrav PAM_OPT_NULLOK, 91e9cc7b1dSDag-Erling Smørgrav PAM_OPT_LOCAL_PASS, 92e9cc7b1dSDag-Erling Smørgrav PAM_OPT_NIS_PASS 93e9cc7b1dSDag-Erling Smørgrav }; 941642eb1aSMark Murray 951642eb1aSMark Murray static struct opttab other_options[] = { 961642eb1aSMark Murray { "auth_as_self", PAM_OPT_AUTH_AS_SELF }, 971642eb1aSMark Murray { "nullok", PAM_OPT_NULLOK }, 983d55a6c0SMark Murray { "local_pass", PAM_OPT_LOCAL_PASS }, 993d55a6c0SMark Murray { "nis_pass", PAM_OPT_NIS_PASS }, 1001642eb1aSMark Murray { NULL, 0 } 1011642eb1aSMark Murray }; 1029a10bb17SJohn Polstra 1033d55a6c0SMark Murray #ifdef YP 1043d55a6c0SMark Murray int pam_use_yp = 0; 1053d55a6c0SMark Murray int yp_errno = YP_TRUE; 1063d55a6c0SMark Murray #endif 1073d55a6c0SMark Murray 1083d55a6c0SMark Murray char *tempname = NULL; 1093d55a6c0SMark Murray static int local_passwd(const char *user, const char *pass); 1103d55a6c0SMark Murray #ifdef YP 1113d55a6c0SMark Murray static int yp_passwd(const char *user, const char *pass); 1123d55a6c0SMark Murray #endif 1133d55a6c0SMark Murray 114d65b34dbSJohn Polstra /* 115d65b34dbSJohn Polstra * authentication management 116d65b34dbSJohn Polstra */ 1179a10bb17SJohn Polstra PAM_EXTERN int 118ac569969SMark Murray pam_sm_authenticate(pam_handle_t *pamh, int flags __unused, int argc, const char **argv) 1199a10bb17SJohn Polstra { 1203d55a6c0SMark Murray login_cap_t *lc; 1211642eb1aSMark Murray struct options options; 1229a10bb17SJohn Polstra struct passwd *pwd; 1231642eb1aSMark Murray int retval; 1243d55a6c0SMark Murray const char *pass, *user; 1253d55a6c0SMark Murray char *encrypted, *password_prompt; 1269a10bb17SJohn Polstra 1271642eb1aSMark Murray pam_std_option(&options, other_options, argc, argv); 1281642eb1aSMark Murray 1291642eb1aSMark Murray PAM_LOG("Options processed"); 1301642eb1aSMark Murray 1311642eb1aSMark Murray if (pam_test_option(&options, PAM_OPT_AUTH_AS_SELF, NULL)) 1323d55a6c0SMark Murray pwd = getpwnam(getlogin()); 1334448b21cSMark Murray else { 1341642eb1aSMark Murray retval = pam_get_user(pamh, &user, NULL); 1351642eb1aSMark Murray if (retval != PAM_SUCCESS) 1361642eb1aSMark Murray PAM_RETURN(retval); 1374448b21cSMark Murray pwd = getpwnam(user); 1384448b21cSMark Murray } 1391642eb1aSMark Murray 1401642eb1aSMark Murray PAM_LOG("Got user: %s", user); 1411642eb1aSMark Murray 1424448b21cSMark Murray if (pwd != NULL) { 1431642eb1aSMark Murray 1441642eb1aSMark Murray PAM_LOG("Doing real authentication"); 1451642eb1aSMark Murray 1461642eb1aSMark Murray if (pwd->pw_passwd[0] == '\0' 1471642eb1aSMark Murray && pam_test_option(&options, PAM_OPT_NULLOK, NULL)) { 14846efbac2SMark Murray /* 14946efbac2SMark Murray * No password case. XXX Are we giving too much away 15046efbac2SMark Murray * by not prompting for a password? 15146efbac2SMark Murray */ 1521642eb1aSMark Murray PAM_LOG("No password, and null password OK"); 1531642eb1aSMark Murray PAM_RETURN(PAM_SUCCESS); 1541642eb1aSMark Murray } 15546efbac2SMark Murray else { 156a8b1e59eSDag-Erling Smørgrav lc = login_getpwclass(pwd); 157a8b1e59eSDag-Erling Smørgrav password_prompt = login_getcapstr(lc, "passwd_prompt", 158a8b1e59eSDag-Erling Smørgrav NULL, NULL); 159111ccd25SDag-Erling Smørgrav retval = pam_get_authtok(pamh, PAM_AUTHTOK, 160111ccd25SDag-Erling Smørgrav &pass, password_prompt); 161a8b1e59eSDag-Erling Smørgrav login_close(lc); 1621642eb1aSMark Murray if (retval != PAM_SUCCESS) 1631642eb1aSMark Murray PAM_RETURN(retval); 1641642eb1aSMark Murray PAM_LOG("Got password"); 16546efbac2SMark Murray } 1663d55a6c0SMark Murray encrypted = crypt(pass, pwd->pw_passwd); 1673d55a6c0SMark Murray if (pass[0] == '\0' && pwd->pw_passwd[0] != '\0') 1688c3ea588SMark Murray encrypted = colon; 1699a10bb17SJohn Polstra 1703d55a6c0SMark Murray PAM_LOG("Encrypted password 1 is: %s", encrypted); 1713d55a6c0SMark Murray PAM_LOG("Encrypted password 2 is: %s", pwd->pw_passwd); 1721642eb1aSMark Murray 1739a10bb17SJohn Polstra retval = strcmp(encrypted, pwd->pw_passwd) == 0 ? 1749a10bb17SJohn Polstra PAM_SUCCESS : PAM_AUTH_ERR; 1751642eb1aSMark Murray } 1761642eb1aSMark Murray else { 1771642eb1aSMark Murray 1781642eb1aSMark Murray PAM_LOG("Doing dummy authentication"); 1791642eb1aSMark Murray 1809a10bb17SJohn Polstra /* 1811642eb1aSMark Murray * User unknown. 1821642eb1aSMark Murray * Encrypt a dummy password so as to not give away too much. 1839a10bb17SJohn Polstra */ 184a8b1e59eSDag-Erling Smørgrav lc = login_getclass(NULL); 185a8b1e59eSDag-Erling Smørgrav password_prompt = login_getcapstr(lc, "passwd_prompt", 186a8b1e59eSDag-Erling Smørgrav NULL, NULL); 187111ccd25SDag-Erling Smørgrav retval = pam_get_authtok(pamh, 188111ccd25SDag-Erling Smørgrav PAM_AUTHTOK, &pass, password_prompt); 189a8b1e59eSDag-Erling Smørgrav login_close(lc); 1901642eb1aSMark Murray if (retval != PAM_SUCCESS) 1911642eb1aSMark Murray PAM_RETURN(retval); 1921642eb1aSMark Murray PAM_LOG("Got password"); 1933d55a6c0SMark Murray crypt(pass, "xx"); 1949a10bb17SJohn Polstra retval = PAM_AUTH_ERR; 1959a10bb17SJohn Polstra } 1961642eb1aSMark Murray 1979a10bb17SJohn Polstra /* 1989a10bb17SJohn Polstra * The PAM infrastructure will obliterate the cleartext 1999a10bb17SJohn Polstra * password before returning to the application. 2009a10bb17SJohn Polstra */ 2013d55a6c0SMark Murray if (retval != PAM_SUCCESS) 2023d55a6c0SMark Murray PAM_VERBOSE_ERROR("UNIX authentication refused"); 2033d55a6c0SMark Murray 2041642eb1aSMark Murray PAM_RETURN(retval); 2059a10bb17SJohn Polstra } 2069a10bb17SJohn Polstra 2079a10bb17SJohn Polstra PAM_EXTERN int 208ac569969SMark Murray pam_sm_setcred(pam_handle_t *pamh __unused, int flags __unused, int argc, const char **argv) 2099a10bb17SJohn Polstra { 2103d55a6c0SMark Murray struct options options; 2113d55a6c0SMark Murray 2123d55a6c0SMark Murray pam_std_option(&options, other_options, argc, argv); 2133d55a6c0SMark Murray 2143d55a6c0SMark Murray PAM_LOG("Options processed"); 2153d55a6c0SMark Murray 2163d55a6c0SMark Murray PAM_RETURN(PAM_SUCCESS); 2179a10bb17SJohn Polstra } 2189294327dSJohn Polstra 219d65b34dbSJohn Polstra /* 220d65b34dbSJohn Polstra * account management 221d65b34dbSJohn Polstra */ 2223d55a6c0SMark Murray PAM_EXTERN int 223ac569969SMark Murray pam_sm_acct_mgmt(pam_handle_t *pamh, int flags __unused, int argc, const char **argv) 224d65b34dbSJohn Polstra { 225e9cc7b1dSDag-Erling Smørgrav struct addrinfo hints, *res; 2261642eb1aSMark Murray struct options options; 227e9cc7b1dSDag-Erling Smørgrav struct passwd *pwd; 228d65b34dbSJohn Polstra struct timeval tp; 2291642eb1aSMark Murray login_cap_t *lc; 230d65b34dbSJohn Polstra time_t warntime; 231d65b34dbSJohn Polstra int retval; 232e9cc7b1dSDag-Erling Smørgrav const char *rhost, *tty, *user; 233e9cc7b1dSDag-Erling Smørgrav char rhostip[MAXHOSTNAMELEN]; 2341642eb1aSMark Murray 2351642eb1aSMark Murray pam_std_option(&options, other_options, argc, argv); 2361642eb1aSMark Murray 2371642eb1aSMark Murray PAM_LOG("Options processed"); 238d65b34dbSJohn Polstra 239111ccd25SDag-Erling Smørgrav retval = pam_get_user(pamh, &user, NULL); 240e9cc7b1dSDag-Erling Smørgrav if (retval != PAM_SUCCESS) 241e9cc7b1dSDag-Erling Smørgrav PAM_RETURN(retval); 242d65b34dbSJohn Polstra 243e9cc7b1dSDag-Erling Smørgrav if (user == NULL || (pwd = getpwnam(user)) == NULL) 244e9cc7b1dSDag-Erling Smørgrav PAM_RETURN(PAM_SERVICE_ERR); 2451642eb1aSMark Murray 2461642eb1aSMark Murray PAM_LOG("Got user: %s", user); 247d65b34dbSJohn Polstra 248e9cc7b1dSDag-Erling Smørgrav retval = pam_get_item(pamh, PAM_RHOST, (const void **)&rhost); 249e9cc7b1dSDag-Erling Smørgrav if (retval != PAM_SUCCESS) 250e9cc7b1dSDag-Erling Smørgrav PAM_RETURN(retval); 251d65b34dbSJohn Polstra 252e9cc7b1dSDag-Erling Smørgrav retval = pam_get_item(pamh, PAM_TTY, (const void **)&tty); 253e9cc7b1dSDag-Erling Smørgrav if (retval != PAM_SUCCESS) 254e9cc7b1dSDag-Erling Smørgrav PAM_RETURN(retval); 255d65b34dbSJohn Polstra 256e9cc7b1dSDag-Erling Smørgrav if (*pwd->pw_passwd == '\0' && 257e9cc7b1dSDag-Erling Smørgrav (flags & PAM_DISALLOW_NULL_AUTHTOK) != 0) 258e9cc7b1dSDag-Erling Smørgrav return (PAM_NEW_AUTHTOK_REQD); 259e9cc7b1dSDag-Erling Smørgrav 260e9cc7b1dSDag-Erling Smørgrav lc = login_getpwclass(pwd); 261e9cc7b1dSDag-Erling Smørgrav if (lc == NULL) { 262e9cc7b1dSDag-Erling Smørgrav PAM_LOG("Unable to get login class for user %s", user); 263e9cc7b1dSDag-Erling Smørgrav return (PAM_SERVICE_ERR); 264e9cc7b1dSDag-Erling Smørgrav } 265d65b34dbSJohn Polstra 2661642eb1aSMark Murray PAM_LOG("Got login_cap"); 2671642eb1aSMark Murray 268e9cc7b1dSDag-Erling Smørgrav if (pwd->pw_change || pwd->pw_expire) 269e9cc7b1dSDag-Erling Smørgrav gettimeofday(&tp, NULL); 270d65b34dbSJohn Polstra 271e9cc7b1dSDag-Erling Smørgrav /* 272e9cc7b1dSDag-Erling Smørgrav * Check pw_expire before pw_change - no point in letting the 273e9cc7b1dSDag-Erling Smørgrav * user change the password on an expired account. 274e9cc7b1dSDag-Erling Smørgrav */ 275d65b34dbSJohn Polstra 276e9cc7b1dSDag-Erling Smørgrav if (pwd->pw_expire) { 277e9cc7b1dSDag-Erling Smørgrav warntime = login_getcaptime(lc, "warnexpire", 278e9cc7b1dSDag-Erling Smørgrav DEFAULT_WARN, DEFAULT_WARN); 279e9cc7b1dSDag-Erling Smørgrav if (tp.tv_sec >= pwd->pw_expire) { 280e9cc7b1dSDag-Erling Smørgrav login_close(lc); 281e9cc7b1dSDag-Erling Smørgrav PAM_RETURN(PAM_ACCT_EXPIRED); 282e9cc7b1dSDag-Erling Smørgrav } else if (pwd->pw_expire - tp.tv_sec < warntime && 283e9cc7b1dSDag-Erling Smørgrav (flags & PAM_SILENT) == 0) { 284519b6a4cSDag-Erling Smørgrav pam_error(pamh, "Warning: your account expires on %s", 285e9cc7b1dSDag-Erling Smørgrav ctime(&pwd->pw_expire)); 286d65b34dbSJohn Polstra } 287d65b34dbSJohn Polstra } 288d65b34dbSJohn Polstra 289e9cc7b1dSDag-Erling Smørgrav retval = PAM_SUCCESS; 290e9cc7b1dSDag-Erling Smørgrav if (pwd->pw_change) { 291e9cc7b1dSDag-Erling Smørgrav warntime = login_getcaptime(lc, "warnpassword", 292e9cc7b1dSDag-Erling Smørgrav DEFAULT_WARN, DEFAULT_WARN); 293e9cc7b1dSDag-Erling Smørgrav if (tp.tv_sec >= pwd->pw_change) { 294e9cc7b1dSDag-Erling Smørgrav retval = PAM_NEW_AUTHTOK_REQD; 295e9cc7b1dSDag-Erling Smørgrav } else if (pwd->pw_change - tp.tv_sec < warntime && 296e9cc7b1dSDag-Erling Smørgrav (flags & PAM_SILENT) == 0) { 297519b6a4cSDag-Erling Smørgrav pam_error(pamh, "Warning: your password expires on %s", 298e9cc7b1dSDag-Erling Smørgrav ctime(&pwd->pw_change)); 299e9cc7b1dSDag-Erling Smørgrav } 300e9cc7b1dSDag-Erling Smørgrav } 301e9cc7b1dSDag-Erling Smørgrav 302e9cc7b1dSDag-Erling Smørgrav /* 303e9cc7b1dSDag-Erling Smørgrav * From here on, we must leave retval untouched (unless we 304e9cc7b1dSDag-Erling Smørgrav * know we're going to fail), because we need to remember 305e9cc7b1dSDag-Erling Smørgrav * whether we're supposed to return PAM_SUCCESS or 306e9cc7b1dSDag-Erling Smørgrav * PAM_NEW_AUTHTOK_REQD. 307e9cc7b1dSDag-Erling Smørgrav */ 308e9cc7b1dSDag-Erling Smørgrav 309e9cc7b1dSDag-Erling Smørgrav if (rhost) { 310e9cc7b1dSDag-Erling Smørgrav memset(&hints, 0, sizeof(hints)); 311e9cc7b1dSDag-Erling Smørgrav hints.ai_family = AF_UNSPEC; 312e9cc7b1dSDag-Erling Smørgrav if (getaddrinfo(rhost, NULL, &hints, &res) == 0) { 313e9cc7b1dSDag-Erling Smørgrav getnameinfo(res->ai_addr, res->ai_addrlen, 314e9cc7b1dSDag-Erling Smørgrav rhostip, sizeof(rhostip), NULL, 0, 315e9cc7b1dSDag-Erling Smørgrav NI_NUMERICHOST|NI_WITHSCOPEID); 316e9cc7b1dSDag-Erling Smørgrav } 317e9cc7b1dSDag-Erling Smørgrav if (res != NULL) 318e9cc7b1dSDag-Erling Smørgrav freeaddrinfo(res); 319e9cc7b1dSDag-Erling Smørgrav } 320e9cc7b1dSDag-Erling Smørgrav 321e9cc7b1dSDag-Erling Smørgrav /* 322e9cc7b1dSDag-Erling Smørgrav * Check host / tty / time-of-day restrictions 323e9cc7b1dSDag-Erling Smørgrav */ 324e9cc7b1dSDag-Erling Smørgrav 325e9cc7b1dSDag-Erling Smørgrav if (!auth_hostok(lc, rhost, rhostip) || 326e9cc7b1dSDag-Erling Smørgrav !auth_ttyok(lc, tty) || 327e9cc7b1dSDag-Erling Smørgrav !auth_timeok(lc, time(NULL))) 328e9cc7b1dSDag-Erling Smørgrav retval = PAM_AUTH_ERR; 329e9cc7b1dSDag-Erling Smørgrav 330d65b34dbSJohn Polstra login_close(lc); 3311642eb1aSMark Murray 3321642eb1aSMark Murray PAM_RETURN(retval); 333d65b34dbSJohn Polstra } 334d65b34dbSJohn Polstra 3353d55a6c0SMark Murray /* 3363d55a6c0SMark Murray * session management 3373d55a6c0SMark Murray * 3383d55a6c0SMark Murray * logging only 3393d55a6c0SMark Murray */ 3403d55a6c0SMark Murray PAM_EXTERN int 341ac569969SMark Murray pam_sm_open_session(pam_handle_t *pamh __unused, int flags __unused, int argc, const char **argv) 3423d55a6c0SMark Murray { 3433d55a6c0SMark Murray struct options options; 3443d55a6c0SMark Murray 3453d55a6c0SMark Murray pam_std_option(&options, other_options, argc, argv); 3463d55a6c0SMark Murray 3473d55a6c0SMark Murray PAM_LOG("Options processed"); 3483d55a6c0SMark Murray 3493d55a6c0SMark Murray PAM_RETURN(PAM_SUCCESS); 3503d55a6c0SMark Murray } 3513d55a6c0SMark Murray 3523d55a6c0SMark Murray PAM_EXTERN int 353ac569969SMark Murray pam_sm_close_session(pam_handle_t *pamh __unused, int flags __unused, int argc, const char **argv) 3543d55a6c0SMark Murray { 3553d55a6c0SMark Murray struct options options; 3563d55a6c0SMark Murray 3573d55a6c0SMark Murray pam_std_option(&options, other_options, argc, argv); 3583d55a6c0SMark Murray 3593d55a6c0SMark Murray PAM_LOG("Options processed"); 3603d55a6c0SMark Murray 3613d55a6c0SMark Murray PAM_RETURN(PAM_SUCCESS); 3623d55a6c0SMark Murray } 3633d55a6c0SMark Murray 3643d55a6c0SMark Murray /* 3653d55a6c0SMark Murray * password management 3663d55a6c0SMark Murray * 3673d55a6c0SMark Murray * standard Unix and NIS password changing 3683d55a6c0SMark Murray */ 3693d55a6c0SMark Murray PAM_EXTERN int 3703d55a6c0SMark Murray pam_sm_chauthtok(pam_handle_t *pamh, int flags, int argc, const char **argv) 3713d55a6c0SMark Murray { 3723d55a6c0SMark Murray struct options options; 3733d55a6c0SMark Murray struct passwd *pwd; 374111ccd25SDag-Erling Smørgrav const char *user, *pass, *new_pass; 375111ccd25SDag-Erling Smørgrav char *encrypted, *usrdup; 376111ccd25SDag-Erling Smørgrav int retval, res; 3773d55a6c0SMark Murray 3783d55a6c0SMark Murray pam_std_option(&options, other_options, argc, argv); 3793d55a6c0SMark Murray 3803d55a6c0SMark Murray PAM_LOG("Options processed"); 3813d55a6c0SMark Murray 3823d55a6c0SMark Murray if (pam_test_option(&options, PAM_OPT_AUTH_AS_SELF, NULL)) 3833d55a6c0SMark Murray pwd = getpwnam(getlogin()); 3843d55a6c0SMark Murray else { 3853d55a6c0SMark Murray retval = pam_get_user(pamh, &user, NULL); 3863d55a6c0SMark Murray if (retval != PAM_SUCCESS) 3873d55a6c0SMark Murray PAM_RETURN(retval); 3883d55a6c0SMark Murray pwd = getpwnam(user); 3893d55a6c0SMark Murray } 3903d55a6c0SMark Murray 3913d55a6c0SMark Murray PAM_LOG("Got user: %s", user); 3923d55a6c0SMark Murray 3933d55a6c0SMark Murray if (flags & PAM_PRELIM_CHECK) { 3943d55a6c0SMark Murray 3953d55a6c0SMark Murray PAM_LOG("PRELIM round; checking user password"); 3963d55a6c0SMark Murray 3973d55a6c0SMark Murray if (pwd->pw_passwd[0] == '\0' 3983d55a6c0SMark Murray && pam_test_option(&options, PAM_OPT_NULLOK, NULL)) { 3993d55a6c0SMark Murray /* 4003d55a6c0SMark Murray * No password case. XXX Are we giving too much away 4013d55a6c0SMark Murray * by not prompting for a password? 402111ccd25SDag-Erling Smørgrav * XXX check PAM_DISALLOW_NULL_AUTHTOK 4033d55a6c0SMark Murray */ 404111ccd25SDag-Erling Smørgrav PAM_LOG("Got password"); 4053d55a6c0SMark Murray PAM_RETURN(PAM_SUCCESS); 4063d55a6c0SMark Murray } 4073d55a6c0SMark Murray else { 408111ccd25SDag-Erling Smørgrav retval = pam_get_authtok(pamh, 409111ccd25SDag-Erling Smørgrav PAM_OLDAUTHTOK, &pass, NULL); 4103d55a6c0SMark Murray if (retval != PAM_SUCCESS) 4113d55a6c0SMark Murray PAM_RETURN(retval); 412111ccd25SDag-Erling Smørgrav PAM_LOG("Got password"); 4133d55a6c0SMark Murray } 4143d55a6c0SMark Murray encrypted = crypt(pass, pwd->pw_passwd); 4153d55a6c0SMark Murray if (pass[0] == '\0' && pwd->pw_passwd[0] != '\0') 4168c3ea588SMark Murray encrypted = colon; 4173d55a6c0SMark Murray 418111ccd25SDag-Erling Smørgrav if (strcmp(encrypted, pwd->pw_passwd) != 0) { 419111ccd25SDag-Erling Smørgrav pam_set_item(pamh, PAM_OLDAUTHTOK, NULL); 4203d55a6c0SMark Murray PAM_RETURN(PAM_AUTH_ERR); 421111ccd25SDag-Erling Smørgrav } 4223d55a6c0SMark Murray 4233d55a6c0SMark Murray PAM_RETURN(PAM_SUCCESS); 4243d55a6c0SMark Murray } 4253d55a6c0SMark Murray else if (flags & PAM_UPDATE_AUTHTOK) { 4263d55a6c0SMark Murray PAM_LOG("UPDATE round; checking user password"); 4273d55a6c0SMark Murray 428111ccd25SDag-Erling Smørgrav retval = pam_get_authtok(pamh, PAM_OLDAUTHTOK, &pass, NULL); 4293d55a6c0SMark Murray if (retval != PAM_SUCCESS) 4303d55a6c0SMark Murray PAM_RETURN(retval); 4313d55a6c0SMark Murray 432111ccd25SDag-Erling Smørgrav PAM_LOG("Got old password"); 4333d55a6c0SMark Murray 434111ccd25SDag-Erling Smørgrav for (;;) { 435111ccd25SDag-Erling Smørgrav retval = pam_get_authtok(pamh, 436111ccd25SDag-Erling Smørgrav PAM_AUTHTOK, &new_pass, NULL); 437111ccd25SDag-Erling Smørgrav if (retval != PAM_TRY_AGAIN) 4383d55a6c0SMark Murray break; 439111ccd25SDag-Erling Smørgrav pam_error(pamh, "Mismatch; try again, EOF to quit."); 4403d55a6c0SMark Murray } 4413d55a6c0SMark Murray 442111ccd25SDag-Erling Smørgrav if (retval != PAM_SUCCESS) { 443111ccd25SDag-Erling Smørgrav PAM_VERBOSE_ERROR("Unable to get new password"); 4443d55a6c0SMark Murray PAM_RETURN(PAM_PERM_DENIED); 4453d55a6c0SMark Murray } 4463d55a6c0SMark Murray 4473d55a6c0SMark Murray PAM_LOG("Got new password: %s", new_pass); 4483d55a6c0SMark Murray 4493d55a6c0SMark Murray #ifdef YP 4503d55a6c0SMark Murray /* If NIS is set in the passwd database, use it */ 4514004c08eSDag-Erling Smørgrav if ((usrdup = strdup(user)) == NULL) 4524004c08eSDag-Erling Smørgrav PAM_RETURN(PAM_BUF_ERR); 4534004c08eSDag-Erling Smørgrav res = use_yp(usrdup, 0, 0); 4544004c08eSDag-Erling Smørgrav free(usrdup); 4553d55a6c0SMark Murray if (res == USER_YP_ONLY) { 4563d55a6c0SMark Murray if (!pam_test_option(&options, PAM_OPT_LOCAL_PASS, 4573d55a6c0SMark Murray NULL)) 4583d55a6c0SMark Murray retval = yp_passwd(user, new_pass); 4593d55a6c0SMark Murray else { 4603d55a6c0SMark Murray /* Reject 'local' flag if NIS is on and the user 4613d55a6c0SMark Murray * is not local 4623d55a6c0SMark Murray */ 4633d55a6c0SMark Murray retval = PAM_PERM_DENIED; 4643d55a6c0SMark Murray PAM_LOG("Unknown local user: %s", user); 4653d55a6c0SMark Murray } 4663d55a6c0SMark Murray } 4673d55a6c0SMark Murray else if (res == USER_LOCAL_ONLY) { 4683d55a6c0SMark Murray if (!pam_test_option(&options, PAM_OPT_NIS_PASS, NULL)) 4693d55a6c0SMark Murray retval = local_passwd(user, new_pass); 4703d55a6c0SMark Murray else { 4713d55a6c0SMark Murray /* Reject 'nis' flag if user is only local */ 4723d55a6c0SMark Murray retval = PAM_PERM_DENIED; 4733d55a6c0SMark Murray PAM_LOG("Unknown NIS user: %s", user); 4743d55a6c0SMark Murray } 4753d55a6c0SMark Murray } 4763d55a6c0SMark Murray else if (res == USER_YP_AND_LOCAL) { 4773d55a6c0SMark Murray if (pam_test_option(&options, PAM_OPT_NIS_PASS, NULL)) 4783d55a6c0SMark Murray retval = yp_passwd(user, new_pass); 4793d55a6c0SMark Murray else 4803d55a6c0SMark Murray retval = local_passwd(user, new_pass); 4813d55a6c0SMark Murray } 4823d55a6c0SMark Murray else 4834004c08eSDag-Erling Smørgrav retval = PAM_SERVICE_ERR; /* Bad juju */ 4843d55a6c0SMark Murray #else 4853d55a6c0SMark Murray retval = local_passwd(user, new_pass); 4863d55a6c0SMark Murray #endif 4873d55a6c0SMark Murray } 4883d55a6c0SMark Murray else { 4893d55a6c0SMark Murray /* Very bad juju */ 4903d55a6c0SMark Murray retval = PAM_ABORT; 4913d55a6c0SMark Murray PAM_LOG("Illegal 'flags'"); 4923d55a6c0SMark Murray } 4933d55a6c0SMark Murray 4943d55a6c0SMark Murray PAM_RETURN(retval); 4953d55a6c0SMark Murray } 4963d55a6c0SMark Murray 4973d55a6c0SMark Murray /* Mostly stolen from passwd(1)'s local_passwd.c - markm */ 4983d55a6c0SMark Murray 4993d55a6c0SMark Murray static unsigned char itoa64[] = /* 0 ... 63 => ascii - 64 */ 5003d55a6c0SMark Murray "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; 5013d55a6c0SMark Murray 5023d55a6c0SMark Murray static void 5033d55a6c0SMark Murray to64(char *s, long v, int n) 5043d55a6c0SMark Murray { 5053d55a6c0SMark Murray while (--n >= 0) { 5063d55a6c0SMark Murray *s++ = itoa64[v&0x3f]; 5073d55a6c0SMark Murray v >>= 6; 5083d55a6c0SMark Murray } 5093d55a6c0SMark Murray } 5103d55a6c0SMark Murray 5113d55a6c0SMark Murray static int 5123d55a6c0SMark Murray local_passwd(const char *user, const char *pass) 5133d55a6c0SMark Murray { 5143d55a6c0SMark Murray login_cap_t * lc; 5153d55a6c0SMark Murray struct passwd *pwd; 5163d55a6c0SMark Murray int pfd, tfd; 5178c3ea588SMark Murray char *crypt_type, salt[SALTSIZE + 1]; 5183d55a6c0SMark Murray 5193d55a6c0SMark Murray pwd = getpwnam(user); 5203d55a6c0SMark Murray if (pwd == NULL) 5214004c08eSDag-Erling Smørgrav return(PAM_SERVICE_ERR); /* Really bad things */ 5223d55a6c0SMark Murray 5233d55a6c0SMark Murray #ifdef YP 5243d55a6c0SMark Murray pwd = (struct passwd *)&local_password; 5253d55a6c0SMark Murray #endif 5263d55a6c0SMark Murray pw_init(); 5273d55a6c0SMark Murray 5283d55a6c0SMark Murray pwd->pw_change = 0; 5293d55a6c0SMark Murray lc = login_getclass(NULL); 5303d55a6c0SMark Murray crypt_type = login_getcapstr(lc, "passwd_format", 531ac569969SMark Murray password_hash, password_hash); 5323d55a6c0SMark Murray if (login_setcryptfmt(lc, crypt_type, NULL) == NULL) 5333d55a6c0SMark Murray syslog(LOG_ERR, "cannot set password cipher"); 5343d55a6c0SMark Murray login_close(lc); 5358c3ea588SMark Murray makesalt(salt); 5363d55a6c0SMark Murray pwd->pw_passwd = crypt(pass, salt); 5373d55a6c0SMark Murray 5383d55a6c0SMark Murray pfd = pw_lock(); 5393d55a6c0SMark Murray tfd = pw_tmp(); 54051906f45SCrist J. Clark pw_copy(pfd, tfd, pwd, NULL); 5413d55a6c0SMark Murray 542ac569969SMark Murray if (!pw_mkdb(user)) 5433d55a6c0SMark Murray pw_error((char *)NULL, 0, 1); 5443d55a6c0SMark Murray 5454004c08eSDag-Erling Smørgrav return (PAM_SUCCESS); 5463d55a6c0SMark Murray } 5473d55a6c0SMark Murray 5483d55a6c0SMark Murray #ifdef YP 5493d55a6c0SMark Murray /* Stolen from src/usr.bin/passwd/yp_passwd.c, carrying copyrights of: 5503d55a6c0SMark Murray * Copyright (c) 1992/3 Theo de Raadt <deraadt@fsa.ca> 5513d55a6c0SMark Murray * Copyright (c) 1994 Olaf Kirch <okir@monad.swb.de> 5523d55a6c0SMark Murray * Copyright (c) 1995 Bill Paul <wpaul@ctr.columbia.edu> 5533d55a6c0SMark Murray */ 5543d55a6c0SMark Murray int 555519b6a4cSDag-Erling Smørgrav yp_passwd(const char *user __unused, const char *pass) 5563d55a6c0SMark Murray { 557111ccd25SDag-Erling Smørgrav struct yppasswd yppwd; 558519b6a4cSDag-Erling Smørgrav struct master_yppasswd master_yppwd; 5593d55a6c0SMark Murray struct passwd *pwd; 5603d55a6c0SMark Murray struct rpc_err err; 5613d55a6c0SMark Murray CLIENT *clnt; 5623d55a6c0SMark Murray login_cap_t *lc; 5633d55a6c0SMark Murray int *status; 5643d55a6c0SMark Murray uid_t uid; 5658c3ea588SMark Murray char *master, sockname[] = YP_SOCKNAME, salt[SALTSIZE + 1]; 5663d55a6c0SMark Murray 5673d55a6c0SMark Murray _use_yp = 1; 5683d55a6c0SMark Murray 5693d55a6c0SMark Murray uid = getuid(); 5703d55a6c0SMark Murray 5713d55a6c0SMark Murray master = get_yp_master(1); 5723d55a6c0SMark Murray if (master == NULL) 5734004c08eSDag-Erling Smørgrav return (PAM_SERVICE_ERR); /* Major disaster */ 5743d55a6c0SMark Murray 5753d55a6c0SMark Murray /* 5763d55a6c0SMark Murray * It is presumed that by the time we get here, use_yp() 5773d55a6c0SMark Murray * has been called and that we have verified that the user 5783d55a6c0SMark Murray * actually exists. This being the case, the yp_password 5793d55a6c0SMark Murray * stucture has already been filled in for us. 5803d55a6c0SMark Murray */ 5813d55a6c0SMark Murray 5823d55a6c0SMark Murray /* Use the correct password */ 5833d55a6c0SMark Murray pwd = (struct passwd *)&yp_password; 5843d55a6c0SMark Murray 5853d55a6c0SMark Murray pwd->pw_change = 0; 5863d55a6c0SMark Murray 5873d55a6c0SMark Murray /* Initialize password information */ 5883d55a6c0SMark Murray if (suser_override) { 589519b6a4cSDag-Erling Smørgrav master_yppwd.newpw.pw_passwd = strdup(pwd->pw_passwd); 590519b6a4cSDag-Erling Smørgrav master_yppwd.newpw.pw_name = strdup(pwd->pw_name); 591519b6a4cSDag-Erling Smørgrav master_yppwd.newpw.pw_uid = pwd->pw_uid; 592519b6a4cSDag-Erling Smørgrav master_yppwd.newpw.pw_gid = pwd->pw_gid; 593519b6a4cSDag-Erling Smørgrav master_yppwd.newpw.pw_expire = pwd->pw_expire; 594519b6a4cSDag-Erling Smørgrav master_yppwd.newpw.pw_change = pwd->pw_change; 595519b6a4cSDag-Erling Smørgrav master_yppwd.newpw.pw_fields = pwd->pw_fields; 596519b6a4cSDag-Erling Smørgrav master_yppwd.newpw.pw_gecos = strdup(pwd->pw_gecos); 597519b6a4cSDag-Erling Smørgrav master_yppwd.newpw.pw_dir = strdup(pwd->pw_dir); 598519b6a4cSDag-Erling Smørgrav master_yppwd.newpw.pw_shell = strdup(pwd->pw_shell); 599519b6a4cSDag-Erling Smørgrav master_yppwd.newpw.pw_class = pwd->pw_class != NULL ? 600ac569969SMark Murray strdup(pwd->pw_class) : strdup(""); 601519b6a4cSDag-Erling Smørgrav master_yppwd.oldpass = strdup(""); 602519b6a4cSDag-Erling Smørgrav master_yppwd.domain = yp_domain; 6033d55a6c0SMark Murray } else { 604519b6a4cSDag-Erling Smørgrav yppwd.newpw.pw_passwd = strdup(pwd->pw_passwd); 605519b6a4cSDag-Erling Smørgrav yppwd.newpw.pw_name = strdup(pwd->pw_name); 606519b6a4cSDag-Erling Smørgrav yppwd.newpw.pw_uid = pwd->pw_uid; 607519b6a4cSDag-Erling Smørgrav yppwd.newpw.pw_gid = pwd->pw_gid; 608519b6a4cSDag-Erling Smørgrav yppwd.newpw.pw_gecos = strdup(pwd->pw_gecos); 609519b6a4cSDag-Erling Smørgrav yppwd.newpw.pw_dir = strdup(pwd->pw_dir); 610519b6a4cSDag-Erling Smørgrav yppwd.newpw.pw_shell = strdup(pwd->pw_shell); 611519b6a4cSDag-Erling Smørgrav yppwd.oldpass = strdup(""); 6123d55a6c0SMark Murray } 6133d55a6c0SMark Murray 6143d55a6c0SMark Murray if (login_setcryptfmt(lc, "md5", NULL) == NULL) 6153d55a6c0SMark Murray syslog(LOG_ERR, "cannot set password cipher"); 6163d55a6c0SMark Murray login_close(lc); 6173d55a6c0SMark Murray 6188c3ea588SMark Murray makesalt(salt); 6193d55a6c0SMark Murray if (suser_override) 620519b6a4cSDag-Erling Smørgrav master_yppwd.newpw.pw_passwd = crypt(pass, salt); 6213d55a6c0SMark Murray else 622519b6a4cSDag-Erling Smørgrav yppwd.newpw.pw_passwd = crypt(pass, salt); 6233d55a6c0SMark Murray 6243d55a6c0SMark Murray if (suser_override) { 6253d55a6c0SMark Murray if ((clnt = clnt_create(sockname, MASTER_YPPASSWDPROG, 6263d55a6c0SMark Murray MASTER_YPPASSWDVERS, "unix")) == NULL) { 6273d55a6c0SMark Murray syslog(LOG_ERR, 6283d55a6c0SMark Murray "Cannot contact rpc.yppasswdd on host %s: %s", 6293d55a6c0SMark Murray master, clnt_spcreateerror("")); 6304004c08eSDag-Erling Smørgrav return (PAM_SERVICE_ERR); 6313d55a6c0SMark Murray } 6323d55a6c0SMark Murray } 6333d55a6c0SMark Murray else { 6343d55a6c0SMark Murray if ((clnt = clnt_create(master, YPPASSWDPROG, 6353d55a6c0SMark Murray YPPASSWDVERS, "udp")) == NULL) { 6363d55a6c0SMark Murray syslog(LOG_ERR, 6373d55a6c0SMark Murray "Cannot contact rpc.yppasswdd on host %s: %s", 6383d55a6c0SMark Murray master, clnt_spcreateerror("")); 6394004c08eSDag-Erling Smørgrav return (PAM_SERVICE_ERR); 6403d55a6c0SMark Murray } 6413d55a6c0SMark Murray } 6423d55a6c0SMark Murray /* 6433d55a6c0SMark Murray * The yppasswd.x file said `unix authentication required', 6443d55a6c0SMark Murray * so I added it. This is the only reason it is in here. 6453d55a6c0SMark Murray * My yppasswdd doesn't use it, but maybe some others out there 6463d55a6c0SMark Murray * do. --okir 6473d55a6c0SMark Murray */ 6483d55a6c0SMark Murray clnt->cl_auth = authunix_create_default(); 6493d55a6c0SMark Murray 6503d55a6c0SMark Murray if (suser_override) 651519b6a4cSDag-Erling Smørgrav status = yppasswdproc_update_master_1(&master_yppwd, clnt); 6523d55a6c0SMark Murray else 653519b6a4cSDag-Erling Smørgrav status = yppasswdproc_update_1(&yppwd, clnt); 6543d55a6c0SMark Murray 6553d55a6c0SMark Murray clnt_geterr(clnt, &err); 6563d55a6c0SMark Murray 6573d55a6c0SMark Murray auth_destroy(clnt->cl_auth); 6583d55a6c0SMark Murray clnt_destroy(clnt); 6593d55a6c0SMark Murray 6603d55a6c0SMark Murray if (err.re_status != RPC_SUCCESS || status == NULL || *status) 6614004c08eSDag-Erling Smørgrav return (PAM_SERVICE_ERR); 6623d55a6c0SMark Murray 6634004c08eSDag-Erling Smørgrav if (err.re_status || status == NULL || *status) 6644004c08eSDag-Erling Smørgrav return (PAM_SERVICE_ERR); 6654004c08eSDag-Erling Smørgrav return (PAM_SUCCESS); 6663d55a6c0SMark Murray } 6673d55a6c0SMark Murray #endif /* YP */ 6683d55a6c0SMark Murray 6698c3ea588SMark Murray /* Salt suitable for traditional DES and MD5 */ 6708c3ea588SMark Murray void 6718c3ea588SMark Murray makesalt(char salt[SALTSIZE]) 6728c3ea588SMark Murray { 6738c3ea588SMark Murray int i; 6748c3ea588SMark Murray 6758c3ea588SMark Murray /* These are not really random numbers, they are just 6768c3ea588SMark Murray * numbers that change to thwart construction of a 6778c3ea588SMark Murray * dictionary. This is exposed to the public. 6788c3ea588SMark Murray */ 6798c3ea588SMark Murray for (i = 0; i < SALTSIZE; i += 4) 6808c3ea588SMark Murray to64(&salt[i], arc4random(), 4); 6818c3ea588SMark Murray salt[SALTSIZE] = '\0'; 6828c3ea588SMark Murray } 6838c3ea588SMark Murray 6849294327dSJohn Polstra PAM_MODULE_ENTRY("pam_unix"); 685