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 1423d55a6c0SMark Murray lc = login_getclass(NULL); 1433d55a6c0SMark Murray password_prompt = login_getcapstr(lc, "passwd_prompt", 144111ccd25SDag-Erling Smørgrav password_prompt, NULL); 1453d55a6c0SMark Murray login_close(lc); 1463d55a6c0SMark Murray lc = NULL; 1473d55a6c0SMark Murray 1484448b21cSMark Murray if (pwd != NULL) { 1491642eb1aSMark Murray 1501642eb1aSMark Murray PAM_LOG("Doing real authentication"); 1511642eb1aSMark Murray 1521642eb1aSMark Murray if (pwd->pw_passwd[0] == '\0' 1531642eb1aSMark Murray && pam_test_option(&options, PAM_OPT_NULLOK, NULL)) { 15446efbac2SMark Murray /* 15546efbac2SMark Murray * No password case. XXX Are we giving too much away 15646efbac2SMark Murray * by not prompting for a password? 15746efbac2SMark Murray */ 1581642eb1aSMark Murray PAM_LOG("No password, and null password OK"); 1591642eb1aSMark Murray PAM_RETURN(PAM_SUCCESS); 1601642eb1aSMark Murray } 16146efbac2SMark Murray else { 162111ccd25SDag-Erling Smørgrav retval = pam_get_authtok(pamh, PAM_AUTHTOK, 163111ccd25SDag-Erling Smørgrav &pass, password_prompt); 1641642eb1aSMark Murray if (retval != PAM_SUCCESS) 1651642eb1aSMark Murray PAM_RETURN(retval); 1661642eb1aSMark Murray PAM_LOG("Got password"); 16746efbac2SMark Murray } 1683d55a6c0SMark Murray encrypted = crypt(pass, pwd->pw_passwd); 1693d55a6c0SMark Murray if (pass[0] == '\0' && pwd->pw_passwd[0] != '\0') 1708c3ea588SMark Murray encrypted = colon; 1719a10bb17SJohn Polstra 1723d55a6c0SMark Murray PAM_LOG("Encrypted password 1 is: %s", encrypted); 1733d55a6c0SMark Murray PAM_LOG("Encrypted password 2 is: %s", pwd->pw_passwd); 1741642eb1aSMark Murray 1759a10bb17SJohn Polstra retval = strcmp(encrypted, pwd->pw_passwd) == 0 ? 1769a10bb17SJohn Polstra PAM_SUCCESS : PAM_AUTH_ERR; 1771642eb1aSMark Murray } 1781642eb1aSMark Murray else { 1791642eb1aSMark Murray 1801642eb1aSMark Murray PAM_LOG("Doing dummy authentication"); 1811642eb1aSMark Murray 1829a10bb17SJohn Polstra /* 1831642eb1aSMark Murray * User unknown. 1841642eb1aSMark Murray * Encrypt a dummy password so as to not give away too much. 1859a10bb17SJohn Polstra */ 186111ccd25SDag-Erling Smørgrav retval = pam_get_authtok(pamh, 187111ccd25SDag-Erling Smørgrav PAM_AUTHTOK, &pass, password_prompt); 1881642eb1aSMark Murray if (retval != PAM_SUCCESS) 1891642eb1aSMark Murray PAM_RETURN(retval); 1901642eb1aSMark Murray PAM_LOG("Got password"); 1913d55a6c0SMark Murray crypt(pass, "xx"); 1929a10bb17SJohn Polstra retval = PAM_AUTH_ERR; 1939a10bb17SJohn Polstra } 1941642eb1aSMark Murray 1959a10bb17SJohn Polstra /* 1969a10bb17SJohn Polstra * The PAM infrastructure will obliterate the cleartext 1979a10bb17SJohn Polstra * password before returning to the application. 1989a10bb17SJohn Polstra */ 1993d55a6c0SMark Murray if (retval != PAM_SUCCESS) 2003d55a6c0SMark Murray PAM_VERBOSE_ERROR("UNIX authentication refused"); 2013d55a6c0SMark Murray 2021642eb1aSMark Murray PAM_RETURN(retval); 2039a10bb17SJohn Polstra } 2049a10bb17SJohn Polstra 2059a10bb17SJohn Polstra PAM_EXTERN int 206ac569969SMark Murray pam_sm_setcred(pam_handle_t *pamh __unused, int flags __unused, int argc, const char **argv) 2079a10bb17SJohn Polstra { 2083d55a6c0SMark Murray struct options options; 2093d55a6c0SMark Murray 2103d55a6c0SMark Murray pam_std_option(&options, other_options, argc, argv); 2113d55a6c0SMark Murray 2123d55a6c0SMark Murray PAM_LOG("Options processed"); 2133d55a6c0SMark Murray 2143d55a6c0SMark Murray PAM_RETURN(PAM_SUCCESS); 2159a10bb17SJohn Polstra } 2169294327dSJohn Polstra 217d65b34dbSJohn Polstra /* 218d65b34dbSJohn Polstra * account management 219d65b34dbSJohn Polstra */ 2203d55a6c0SMark Murray PAM_EXTERN int 221ac569969SMark Murray pam_sm_acct_mgmt(pam_handle_t *pamh, int flags __unused, int argc, const char **argv) 222d65b34dbSJohn Polstra { 223e9cc7b1dSDag-Erling Smørgrav struct addrinfo hints, *res; 2241642eb1aSMark Murray struct options options; 225e9cc7b1dSDag-Erling Smørgrav struct passwd *pwd; 226d65b34dbSJohn Polstra struct timeval tp; 2271642eb1aSMark Murray login_cap_t *lc; 228d65b34dbSJohn Polstra time_t warntime; 229d65b34dbSJohn Polstra int retval; 230e9cc7b1dSDag-Erling Smørgrav const char *rhost, *tty, *user; 231e9cc7b1dSDag-Erling Smørgrav char rhostip[MAXHOSTNAMELEN]; 2321642eb1aSMark Murray 2331642eb1aSMark Murray pam_std_option(&options, other_options, argc, argv); 2341642eb1aSMark Murray 2351642eb1aSMark Murray PAM_LOG("Options processed"); 236d65b34dbSJohn Polstra 237111ccd25SDag-Erling Smørgrav retval = pam_get_user(pamh, &user, NULL); 238e9cc7b1dSDag-Erling Smørgrav if (retval != PAM_SUCCESS) 239e9cc7b1dSDag-Erling Smørgrav PAM_RETURN(retval); 240d65b34dbSJohn Polstra 241e9cc7b1dSDag-Erling Smørgrav if (user == NULL || (pwd = getpwnam(user)) == NULL) 242e9cc7b1dSDag-Erling Smørgrav PAM_RETURN(PAM_SERVICE_ERR); 2431642eb1aSMark Murray 2441642eb1aSMark Murray PAM_LOG("Got user: %s", user); 245d65b34dbSJohn Polstra 246e9cc7b1dSDag-Erling Smørgrav retval = pam_get_item(pamh, PAM_RHOST, (const void **)&rhost); 247e9cc7b1dSDag-Erling Smørgrav if (retval != PAM_SUCCESS) 248e9cc7b1dSDag-Erling Smørgrav PAM_RETURN(retval); 249d65b34dbSJohn Polstra 250e9cc7b1dSDag-Erling Smørgrav retval = pam_get_item(pamh, PAM_TTY, (const void **)&tty); 251e9cc7b1dSDag-Erling Smørgrav if (retval != PAM_SUCCESS) 252e9cc7b1dSDag-Erling Smørgrav PAM_RETURN(retval); 253d65b34dbSJohn Polstra 254e9cc7b1dSDag-Erling Smørgrav if (*pwd->pw_passwd == '\0' && 255e9cc7b1dSDag-Erling Smørgrav (flags & PAM_DISALLOW_NULL_AUTHTOK) != 0) 256e9cc7b1dSDag-Erling Smørgrav return (PAM_NEW_AUTHTOK_REQD); 257e9cc7b1dSDag-Erling Smørgrav 258e9cc7b1dSDag-Erling Smørgrav lc = login_getpwclass(pwd); 259e9cc7b1dSDag-Erling Smørgrav if (lc == NULL) { 260e9cc7b1dSDag-Erling Smørgrav PAM_LOG("Unable to get login class for user %s", user); 261e9cc7b1dSDag-Erling Smørgrav return (PAM_SERVICE_ERR); 262e9cc7b1dSDag-Erling Smørgrav } 263d65b34dbSJohn Polstra 2641642eb1aSMark Murray PAM_LOG("Got login_cap"); 2651642eb1aSMark Murray 266e9cc7b1dSDag-Erling Smørgrav if (pwd->pw_change || pwd->pw_expire) 267e9cc7b1dSDag-Erling Smørgrav gettimeofday(&tp, NULL); 268d65b34dbSJohn Polstra 269e9cc7b1dSDag-Erling Smørgrav /* 270e9cc7b1dSDag-Erling Smørgrav * Check pw_expire before pw_change - no point in letting the 271e9cc7b1dSDag-Erling Smørgrav * user change the password on an expired account. 272e9cc7b1dSDag-Erling Smørgrav */ 273d65b34dbSJohn Polstra 274e9cc7b1dSDag-Erling Smørgrav if (pwd->pw_expire) { 275e9cc7b1dSDag-Erling Smørgrav warntime = login_getcaptime(lc, "warnexpire", 276e9cc7b1dSDag-Erling Smørgrav DEFAULT_WARN, DEFAULT_WARN); 277e9cc7b1dSDag-Erling Smørgrav if (tp.tv_sec >= pwd->pw_expire) { 278e9cc7b1dSDag-Erling Smørgrav login_close(lc); 279e9cc7b1dSDag-Erling Smørgrav PAM_RETURN(PAM_ACCT_EXPIRED); 280e9cc7b1dSDag-Erling Smørgrav } else if (pwd->pw_expire - tp.tv_sec < warntime && 281e9cc7b1dSDag-Erling Smørgrav (flags & PAM_SILENT) == 0) { 282519b6a4cSDag-Erling Smørgrav pam_error(pamh, "Warning: your account expires on %s", 283e9cc7b1dSDag-Erling Smørgrav ctime(&pwd->pw_expire)); 284d65b34dbSJohn Polstra } 285d65b34dbSJohn Polstra } 286d65b34dbSJohn Polstra 287e9cc7b1dSDag-Erling Smørgrav retval = PAM_SUCCESS; 288e9cc7b1dSDag-Erling Smørgrav if (pwd->pw_change) { 289e9cc7b1dSDag-Erling Smørgrav warntime = login_getcaptime(lc, "warnpassword", 290e9cc7b1dSDag-Erling Smørgrav DEFAULT_WARN, DEFAULT_WARN); 291e9cc7b1dSDag-Erling Smørgrav if (tp.tv_sec >= pwd->pw_change) { 292e9cc7b1dSDag-Erling Smørgrav retval = PAM_NEW_AUTHTOK_REQD; 293e9cc7b1dSDag-Erling Smørgrav } else if (pwd->pw_change - tp.tv_sec < warntime && 294e9cc7b1dSDag-Erling Smørgrav (flags & PAM_SILENT) == 0) { 295519b6a4cSDag-Erling Smørgrav pam_error(pamh, "Warning: your password expires on %s", 296e9cc7b1dSDag-Erling Smørgrav ctime(&pwd->pw_change)); 297e9cc7b1dSDag-Erling Smørgrav } 298e9cc7b1dSDag-Erling Smørgrav } 299e9cc7b1dSDag-Erling Smørgrav 300e9cc7b1dSDag-Erling Smørgrav /* 301e9cc7b1dSDag-Erling Smørgrav * From here on, we must leave retval untouched (unless we 302e9cc7b1dSDag-Erling Smørgrav * know we're going to fail), because we need to remember 303e9cc7b1dSDag-Erling Smørgrav * whether we're supposed to return PAM_SUCCESS or 304e9cc7b1dSDag-Erling Smørgrav * PAM_NEW_AUTHTOK_REQD. 305e9cc7b1dSDag-Erling Smørgrav */ 306e9cc7b1dSDag-Erling Smørgrav 307e9cc7b1dSDag-Erling Smørgrav if (rhost) { 308e9cc7b1dSDag-Erling Smørgrav memset(&hints, 0, sizeof(hints)); 309e9cc7b1dSDag-Erling Smørgrav hints.ai_family = AF_UNSPEC; 310e9cc7b1dSDag-Erling Smørgrav if (getaddrinfo(rhost, NULL, &hints, &res) == 0) { 311e9cc7b1dSDag-Erling Smørgrav getnameinfo(res->ai_addr, res->ai_addrlen, 312e9cc7b1dSDag-Erling Smørgrav rhostip, sizeof(rhostip), NULL, 0, 313e9cc7b1dSDag-Erling Smørgrav NI_NUMERICHOST|NI_WITHSCOPEID); 314e9cc7b1dSDag-Erling Smørgrav } 315e9cc7b1dSDag-Erling Smørgrav if (res != NULL) 316e9cc7b1dSDag-Erling Smørgrav freeaddrinfo(res); 317e9cc7b1dSDag-Erling Smørgrav } 318e9cc7b1dSDag-Erling Smørgrav 319e9cc7b1dSDag-Erling Smørgrav /* 320e9cc7b1dSDag-Erling Smørgrav * Check host / tty / time-of-day restrictions 321e9cc7b1dSDag-Erling Smørgrav */ 322e9cc7b1dSDag-Erling Smørgrav 323e9cc7b1dSDag-Erling Smørgrav if (!auth_hostok(lc, rhost, rhostip) || 324e9cc7b1dSDag-Erling Smørgrav !auth_ttyok(lc, tty) || 325e9cc7b1dSDag-Erling Smørgrav !auth_timeok(lc, time(NULL))) 326e9cc7b1dSDag-Erling Smørgrav retval = PAM_AUTH_ERR; 327e9cc7b1dSDag-Erling Smørgrav 328d65b34dbSJohn Polstra login_close(lc); 3291642eb1aSMark Murray 3301642eb1aSMark Murray PAM_RETURN(retval); 331d65b34dbSJohn Polstra } 332d65b34dbSJohn Polstra 3333d55a6c0SMark Murray /* 3343d55a6c0SMark Murray * session management 3353d55a6c0SMark Murray * 3363d55a6c0SMark Murray * logging only 3373d55a6c0SMark Murray */ 3383d55a6c0SMark Murray PAM_EXTERN int 339ac569969SMark Murray pam_sm_open_session(pam_handle_t *pamh __unused, int flags __unused, int argc, const char **argv) 3403d55a6c0SMark Murray { 3413d55a6c0SMark Murray struct options options; 3423d55a6c0SMark Murray 3433d55a6c0SMark Murray pam_std_option(&options, other_options, argc, argv); 3443d55a6c0SMark Murray 3453d55a6c0SMark Murray PAM_LOG("Options processed"); 3463d55a6c0SMark Murray 3473d55a6c0SMark Murray PAM_RETURN(PAM_SUCCESS); 3483d55a6c0SMark Murray } 3493d55a6c0SMark Murray 3503d55a6c0SMark Murray PAM_EXTERN int 351ac569969SMark Murray pam_sm_close_session(pam_handle_t *pamh __unused, int flags __unused, int argc, const char **argv) 3523d55a6c0SMark Murray { 3533d55a6c0SMark Murray struct options options; 3543d55a6c0SMark Murray 3553d55a6c0SMark Murray pam_std_option(&options, other_options, argc, argv); 3563d55a6c0SMark Murray 3573d55a6c0SMark Murray PAM_LOG("Options processed"); 3583d55a6c0SMark Murray 3593d55a6c0SMark Murray PAM_RETURN(PAM_SUCCESS); 3603d55a6c0SMark Murray } 3613d55a6c0SMark Murray 3623d55a6c0SMark Murray /* 3633d55a6c0SMark Murray * password management 3643d55a6c0SMark Murray * 3653d55a6c0SMark Murray * standard Unix and NIS password changing 3663d55a6c0SMark Murray */ 3673d55a6c0SMark Murray PAM_EXTERN int 3683d55a6c0SMark Murray pam_sm_chauthtok(pam_handle_t *pamh, int flags, int argc, const char **argv) 3693d55a6c0SMark Murray { 3703d55a6c0SMark Murray struct options options; 3713d55a6c0SMark Murray struct passwd *pwd; 372111ccd25SDag-Erling Smørgrav const char *user, *pass, *new_pass; 373111ccd25SDag-Erling Smørgrav char *encrypted, *usrdup; 374111ccd25SDag-Erling Smørgrav int retval, res; 3753d55a6c0SMark Murray 3763d55a6c0SMark Murray pam_std_option(&options, other_options, argc, argv); 3773d55a6c0SMark Murray 3783d55a6c0SMark Murray PAM_LOG("Options processed"); 3793d55a6c0SMark Murray 3803d55a6c0SMark Murray if (pam_test_option(&options, PAM_OPT_AUTH_AS_SELF, NULL)) 3813d55a6c0SMark Murray pwd = getpwnam(getlogin()); 3823d55a6c0SMark Murray else { 3833d55a6c0SMark Murray retval = pam_get_user(pamh, &user, NULL); 3843d55a6c0SMark Murray if (retval != PAM_SUCCESS) 3853d55a6c0SMark Murray PAM_RETURN(retval); 3863d55a6c0SMark Murray pwd = getpwnam(user); 3873d55a6c0SMark Murray } 3883d55a6c0SMark Murray 3893d55a6c0SMark Murray PAM_LOG("Got user: %s", user); 3903d55a6c0SMark Murray 3913d55a6c0SMark Murray if (flags & PAM_PRELIM_CHECK) { 3923d55a6c0SMark Murray 3933d55a6c0SMark Murray PAM_LOG("PRELIM round; checking user password"); 3943d55a6c0SMark Murray 3953d55a6c0SMark Murray if (pwd->pw_passwd[0] == '\0' 3963d55a6c0SMark Murray && pam_test_option(&options, PAM_OPT_NULLOK, NULL)) { 3973d55a6c0SMark Murray /* 3983d55a6c0SMark Murray * No password case. XXX Are we giving too much away 3993d55a6c0SMark Murray * by not prompting for a password? 400111ccd25SDag-Erling Smørgrav * XXX check PAM_DISALLOW_NULL_AUTHTOK 4013d55a6c0SMark Murray */ 402111ccd25SDag-Erling Smørgrav PAM_LOG("Got password"); 4033d55a6c0SMark Murray PAM_RETURN(PAM_SUCCESS); 4043d55a6c0SMark Murray } 4053d55a6c0SMark Murray else { 406111ccd25SDag-Erling Smørgrav retval = pam_get_authtok(pamh, 407111ccd25SDag-Erling Smørgrav PAM_OLDAUTHTOK, &pass, NULL); 4083d55a6c0SMark Murray if (retval != PAM_SUCCESS) 4093d55a6c0SMark Murray PAM_RETURN(retval); 410111ccd25SDag-Erling Smørgrav PAM_LOG("Got password"); 4113d55a6c0SMark Murray } 4123d55a6c0SMark Murray encrypted = crypt(pass, pwd->pw_passwd); 4133d55a6c0SMark Murray if (pass[0] == '\0' && pwd->pw_passwd[0] != '\0') 4148c3ea588SMark Murray encrypted = colon; 4153d55a6c0SMark Murray 416111ccd25SDag-Erling Smørgrav if (strcmp(encrypted, pwd->pw_passwd) != 0) { 417111ccd25SDag-Erling Smørgrav pam_set_item(pamh, PAM_OLDAUTHTOK, NULL); 4183d55a6c0SMark Murray PAM_RETURN(PAM_AUTH_ERR); 419111ccd25SDag-Erling Smørgrav } 4203d55a6c0SMark Murray 4213d55a6c0SMark Murray PAM_RETURN(PAM_SUCCESS); 4223d55a6c0SMark Murray } 4233d55a6c0SMark Murray else if (flags & PAM_UPDATE_AUTHTOK) { 4243d55a6c0SMark Murray PAM_LOG("UPDATE round; checking user password"); 4253d55a6c0SMark Murray 426111ccd25SDag-Erling Smørgrav retval = pam_get_authtok(pamh, PAM_OLDAUTHTOK, &pass, NULL); 4273d55a6c0SMark Murray if (retval != PAM_SUCCESS) 4283d55a6c0SMark Murray PAM_RETURN(retval); 4293d55a6c0SMark Murray 430111ccd25SDag-Erling Smørgrav PAM_LOG("Got old password"); 4313d55a6c0SMark Murray 432111ccd25SDag-Erling Smørgrav for (;;) { 433111ccd25SDag-Erling Smørgrav retval = pam_get_authtok(pamh, 434111ccd25SDag-Erling Smørgrav PAM_AUTHTOK, &new_pass, NULL); 435111ccd25SDag-Erling Smørgrav if (retval != PAM_TRY_AGAIN) 4363d55a6c0SMark Murray break; 437111ccd25SDag-Erling Smørgrav pam_error(pamh, "Mismatch; try again, EOF to quit."); 4383d55a6c0SMark Murray } 4393d55a6c0SMark Murray 440111ccd25SDag-Erling Smørgrav if (retval != PAM_SUCCESS) { 441111ccd25SDag-Erling Smørgrav PAM_VERBOSE_ERROR("Unable to get new password"); 4423d55a6c0SMark Murray PAM_RETURN(PAM_PERM_DENIED); 4433d55a6c0SMark Murray } 4443d55a6c0SMark Murray 4453d55a6c0SMark Murray PAM_LOG("Got new password: %s", new_pass); 4463d55a6c0SMark Murray 4473d55a6c0SMark Murray #ifdef YP 4483d55a6c0SMark Murray /* If NIS is set in the passwd database, use it */ 4494004c08eSDag-Erling Smørgrav if ((usrdup = strdup(user)) == NULL) 4504004c08eSDag-Erling Smørgrav PAM_RETURN(PAM_BUF_ERR); 4514004c08eSDag-Erling Smørgrav res = use_yp(usrdup, 0, 0); 4524004c08eSDag-Erling Smørgrav free(usrdup); 4533d55a6c0SMark Murray if (res == USER_YP_ONLY) { 4543d55a6c0SMark Murray if (!pam_test_option(&options, PAM_OPT_LOCAL_PASS, 4553d55a6c0SMark Murray NULL)) 4563d55a6c0SMark Murray retval = yp_passwd(user, new_pass); 4573d55a6c0SMark Murray else { 4583d55a6c0SMark Murray /* Reject 'local' flag if NIS is on and the user 4593d55a6c0SMark Murray * is not local 4603d55a6c0SMark Murray */ 4613d55a6c0SMark Murray retval = PAM_PERM_DENIED; 4623d55a6c0SMark Murray PAM_LOG("Unknown local user: %s", user); 4633d55a6c0SMark Murray } 4643d55a6c0SMark Murray } 4653d55a6c0SMark Murray else if (res == USER_LOCAL_ONLY) { 4663d55a6c0SMark Murray if (!pam_test_option(&options, PAM_OPT_NIS_PASS, NULL)) 4673d55a6c0SMark Murray retval = local_passwd(user, new_pass); 4683d55a6c0SMark Murray else { 4693d55a6c0SMark Murray /* Reject 'nis' flag if user is only local */ 4703d55a6c0SMark Murray retval = PAM_PERM_DENIED; 4713d55a6c0SMark Murray PAM_LOG("Unknown NIS user: %s", user); 4723d55a6c0SMark Murray } 4733d55a6c0SMark Murray } 4743d55a6c0SMark Murray else if (res == USER_YP_AND_LOCAL) { 4753d55a6c0SMark Murray if (pam_test_option(&options, PAM_OPT_NIS_PASS, NULL)) 4763d55a6c0SMark Murray retval = yp_passwd(user, new_pass); 4773d55a6c0SMark Murray else 4783d55a6c0SMark Murray retval = local_passwd(user, new_pass); 4793d55a6c0SMark Murray } 4803d55a6c0SMark Murray else 4814004c08eSDag-Erling Smørgrav retval = PAM_SERVICE_ERR; /* Bad juju */ 4823d55a6c0SMark Murray #else 4833d55a6c0SMark Murray retval = local_passwd(user, new_pass); 4843d55a6c0SMark Murray #endif 4853d55a6c0SMark Murray } 4863d55a6c0SMark Murray else { 4873d55a6c0SMark Murray /* Very bad juju */ 4883d55a6c0SMark Murray retval = PAM_ABORT; 4893d55a6c0SMark Murray PAM_LOG("Illegal 'flags'"); 4903d55a6c0SMark Murray } 4913d55a6c0SMark Murray 4923d55a6c0SMark Murray PAM_RETURN(retval); 4933d55a6c0SMark Murray } 4943d55a6c0SMark Murray 4953d55a6c0SMark Murray /* Mostly stolen from passwd(1)'s local_passwd.c - markm */ 4963d55a6c0SMark Murray 4973d55a6c0SMark Murray static unsigned char itoa64[] = /* 0 ... 63 => ascii - 64 */ 4983d55a6c0SMark Murray "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; 4993d55a6c0SMark Murray 5003d55a6c0SMark Murray static void 5013d55a6c0SMark Murray to64(char *s, long v, int n) 5023d55a6c0SMark Murray { 5033d55a6c0SMark Murray while (--n >= 0) { 5043d55a6c0SMark Murray *s++ = itoa64[v&0x3f]; 5053d55a6c0SMark Murray v >>= 6; 5063d55a6c0SMark Murray } 5073d55a6c0SMark Murray } 5083d55a6c0SMark Murray 5093d55a6c0SMark Murray static int 5103d55a6c0SMark Murray local_passwd(const char *user, const char *pass) 5113d55a6c0SMark Murray { 5123d55a6c0SMark Murray login_cap_t * lc; 5133d55a6c0SMark Murray struct passwd *pwd; 5143d55a6c0SMark Murray int pfd, tfd; 5158c3ea588SMark Murray char *crypt_type, salt[SALTSIZE + 1]; 5163d55a6c0SMark Murray 5173d55a6c0SMark Murray pwd = getpwnam(user); 5183d55a6c0SMark Murray if (pwd == NULL) 5194004c08eSDag-Erling Smørgrav return(PAM_SERVICE_ERR); /* Really bad things */ 5203d55a6c0SMark Murray 5213d55a6c0SMark Murray #ifdef YP 5223d55a6c0SMark Murray pwd = (struct passwd *)&local_password; 5233d55a6c0SMark Murray #endif 5243d55a6c0SMark Murray pw_init(); 5253d55a6c0SMark Murray 5263d55a6c0SMark Murray pwd->pw_change = 0; 5273d55a6c0SMark Murray lc = login_getclass(NULL); 5283d55a6c0SMark Murray crypt_type = login_getcapstr(lc, "passwd_format", 529ac569969SMark Murray password_hash, password_hash); 5303d55a6c0SMark Murray if (login_setcryptfmt(lc, crypt_type, NULL) == NULL) 5313d55a6c0SMark Murray syslog(LOG_ERR, "cannot set password cipher"); 5323d55a6c0SMark Murray login_close(lc); 5338c3ea588SMark Murray makesalt(salt); 5343d55a6c0SMark Murray pwd->pw_passwd = crypt(pass, salt); 5353d55a6c0SMark Murray 5363d55a6c0SMark Murray pfd = pw_lock(); 5373d55a6c0SMark Murray tfd = pw_tmp(); 53851906f45SCrist J. Clark pw_copy(pfd, tfd, pwd, NULL); 5393d55a6c0SMark Murray 540ac569969SMark Murray if (!pw_mkdb(user)) 5413d55a6c0SMark Murray pw_error((char *)NULL, 0, 1); 5423d55a6c0SMark Murray 5434004c08eSDag-Erling Smørgrav return (PAM_SUCCESS); 5443d55a6c0SMark Murray } 5453d55a6c0SMark Murray 5463d55a6c0SMark Murray #ifdef YP 5473d55a6c0SMark Murray /* Stolen from src/usr.bin/passwd/yp_passwd.c, carrying copyrights of: 5483d55a6c0SMark Murray * Copyright (c) 1992/3 Theo de Raadt <deraadt@fsa.ca> 5493d55a6c0SMark Murray * Copyright (c) 1994 Olaf Kirch <okir@monad.swb.de> 5503d55a6c0SMark Murray * Copyright (c) 1995 Bill Paul <wpaul@ctr.columbia.edu> 5513d55a6c0SMark Murray */ 5523d55a6c0SMark Murray int 553519b6a4cSDag-Erling Smørgrav yp_passwd(const char *user __unused, const char *pass) 5543d55a6c0SMark Murray { 555111ccd25SDag-Erling Smørgrav struct yppasswd yppwd; 556519b6a4cSDag-Erling Smørgrav struct master_yppasswd master_yppwd; 5573d55a6c0SMark Murray struct passwd *pwd; 5583d55a6c0SMark Murray struct rpc_err err; 5593d55a6c0SMark Murray CLIENT *clnt; 5603d55a6c0SMark Murray login_cap_t *lc; 5613d55a6c0SMark Murray int *status; 5623d55a6c0SMark Murray uid_t uid; 5638c3ea588SMark Murray char *master, sockname[] = YP_SOCKNAME, salt[SALTSIZE + 1]; 5643d55a6c0SMark Murray 5653d55a6c0SMark Murray _use_yp = 1; 5663d55a6c0SMark Murray 5673d55a6c0SMark Murray uid = getuid(); 5683d55a6c0SMark Murray 5693d55a6c0SMark Murray master = get_yp_master(1); 5703d55a6c0SMark Murray if (master == NULL) 5714004c08eSDag-Erling Smørgrav return (PAM_SERVICE_ERR); /* Major disaster */ 5723d55a6c0SMark Murray 5733d55a6c0SMark Murray /* 5743d55a6c0SMark Murray * It is presumed that by the time we get here, use_yp() 5753d55a6c0SMark Murray * has been called and that we have verified that the user 5763d55a6c0SMark Murray * actually exists. This being the case, the yp_password 5773d55a6c0SMark Murray * stucture has already been filled in for us. 5783d55a6c0SMark Murray */ 5793d55a6c0SMark Murray 5803d55a6c0SMark Murray /* Use the correct password */ 5813d55a6c0SMark Murray pwd = (struct passwd *)&yp_password; 5823d55a6c0SMark Murray 5833d55a6c0SMark Murray pwd->pw_change = 0; 5843d55a6c0SMark Murray 5853d55a6c0SMark Murray /* Initialize password information */ 5863d55a6c0SMark Murray if (suser_override) { 587519b6a4cSDag-Erling Smørgrav master_yppwd.newpw.pw_passwd = strdup(pwd->pw_passwd); 588519b6a4cSDag-Erling Smørgrav master_yppwd.newpw.pw_name = strdup(pwd->pw_name); 589519b6a4cSDag-Erling Smørgrav master_yppwd.newpw.pw_uid = pwd->pw_uid; 590519b6a4cSDag-Erling Smørgrav master_yppwd.newpw.pw_gid = pwd->pw_gid; 591519b6a4cSDag-Erling Smørgrav master_yppwd.newpw.pw_expire = pwd->pw_expire; 592519b6a4cSDag-Erling Smørgrav master_yppwd.newpw.pw_change = pwd->pw_change; 593519b6a4cSDag-Erling Smørgrav master_yppwd.newpw.pw_fields = pwd->pw_fields; 594519b6a4cSDag-Erling Smørgrav master_yppwd.newpw.pw_gecos = strdup(pwd->pw_gecos); 595519b6a4cSDag-Erling Smørgrav master_yppwd.newpw.pw_dir = strdup(pwd->pw_dir); 596519b6a4cSDag-Erling Smørgrav master_yppwd.newpw.pw_shell = strdup(pwd->pw_shell); 597519b6a4cSDag-Erling Smørgrav master_yppwd.newpw.pw_class = pwd->pw_class != NULL ? 598ac569969SMark Murray strdup(pwd->pw_class) : strdup(""); 599519b6a4cSDag-Erling Smørgrav master_yppwd.oldpass = strdup(""); 600519b6a4cSDag-Erling Smørgrav master_yppwd.domain = yp_domain; 6013d55a6c0SMark Murray } else { 602519b6a4cSDag-Erling Smørgrav yppwd.newpw.pw_passwd = strdup(pwd->pw_passwd); 603519b6a4cSDag-Erling Smørgrav yppwd.newpw.pw_name = strdup(pwd->pw_name); 604519b6a4cSDag-Erling Smørgrav yppwd.newpw.pw_uid = pwd->pw_uid; 605519b6a4cSDag-Erling Smørgrav yppwd.newpw.pw_gid = pwd->pw_gid; 606519b6a4cSDag-Erling Smørgrav yppwd.newpw.pw_gecos = strdup(pwd->pw_gecos); 607519b6a4cSDag-Erling Smørgrav yppwd.newpw.pw_dir = strdup(pwd->pw_dir); 608519b6a4cSDag-Erling Smørgrav yppwd.newpw.pw_shell = strdup(pwd->pw_shell); 609519b6a4cSDag-Erling Smørgrav yppwd.oldpass = strdup(""); 6103d55a6c0SMark Murray } 6113d55a6c0SMark Murray 6123d55a6c0SMark Murray if (login_setcryptfmt(lc, "md5", NULL) == NULL) 6133d55a6c0SMark Murray syslog(LOG_ERR, "cannot set password cipher"); 6143d55a6c0SMark Murray login_close(lc); 6153d55a6c0SMark Murray 6168c3ea588SMark Murray makesalt(salt); 6173d55a6c0SMark Murray if (suser_override) 618519b6a4cSDag-Erling Smørgrav master_yppwd.newpw.pw_passwd = crypt(pass, salt); 6193d55a6c0SMark Murray else 620519b6a4cSDag-Erling Smørgrav yppwd.newpw.pw_passwd = crypt(pass, salt); 6213d55a6c0SMark Murray 6223d55a6c0SMark Murray if (suser_override) { 6233d55a6c0SMark Murray if ((clnt = clnt_create(sockname, MASTER_YPPASSWDPROG, 6243d55a6c0SMark Murray MASTER_YPPASSWDVERS, "unix")) == NULL) { 6253d55a6c0SMark Murray syslog(LOG_ERR, 6263d55a6c0SMark Murray "Cannot contact rpc.yppasswdd on host %s: %s", 6273d55a6c0SMark Murray master, clnt_spcreateerror("")); 6284004c08eSDag-Erling Smørgrav return (PAM_SERVICE_ERR); 6293d55a6c0SMark Murray } 6303d55a6c0SMark Murray } 6313d55a6c0SMark Murray else { 6323d55a6c0SMark Murray if ((clnt = clnt_create(master, YPPASSWDPROG, 6333d55a6c0SMark Murray YPPASSWDVERS, "udp")) == NULL) { 6343d55a6c0SMark Murray syslog(LOG_ERR, 6353d55a6c0SMark Murray "Cannot contact rpc.yppasswdd on host %s: %s", 6363d55a6c0SMark Murray master, clnt_spcreateerror("")); 6374004c08eSDag-Erling Smørgrav return (PAM_SERVICE_ERR); 6383d55a6c0SMark Murray } 6393d55a6c0SMark Murray } 6403d55a6c0SMark Murray /* 6413d55a6c0SMark Murray * The yppasswd.x file said `unix authentication required', 6423d55a6c0SMark Murray * so I added it. This is the only reason it is in here. 6433d55a6c0SMark Murray * My yppasswdd doesn't use it, but maybe some others out there 6443d55a6c0SMark Murray * do. --okir 6453d55a6c0SMark Murray */ 6463d55a6c0SMark Murray clnt->cl_auth = authunix_create_default(); 6473d55a6c0SMark Murray 6483d55a6c0SMark Murray if (suser_override) 649519b6a4cSDag-Erling Smørgrav status = yppasswdproc_update_master_1(&master_yppwd, clnt); 6503d55a6c0SMark Murray else 651519b6a4cSDag-Erling Smørgrav status = yppasswdproc_update_1(&yppwd, clnt); 6523d55a6c0SMark Murray 6533d55a6c0SMark Murray clnt_geterr(clnt, &err); 6543d55a6c0SMark Murray 6553d55a6c0SMark Murray auth_destroy(clnt->cl_auth); 6563d55a6c0SMark Murray clnt_destroy(clnt); 6573d55a6c0SMark Murray 6583d55a6c0SMark Murray if (err.re_status != RPC_SUCCESS || status == NULL || *status) 6594004c08eSDag-Erling Smørgrav return (PAM_SERVICE_ERR); 6603d55a6c0SMark Murray 6614004c08eSDag-Erling Smørgrav if (err.re_status || status == NULL || *status) 6624004c08eSDag-Erling Smørgrav return (PAM_SERVICE_ERR); 6634004c08eSDag-Erling Smørgrav return (PAM_SUCCESS); 6643d55a6c0SMark Murray } 6653d55a6c0SMark Murray #endif /* YP */ 6663d55a6c0SMark Murray 6678c3ea588SMark Murray /* Salt suitable for traditional DES and MD5 */ 6688c3ea588SMark Murray void 6698c3ea588SMark Murray makesalt(char salt[SALTSIZE]) 6708c3ea588SMark Murray { 6718c3ea588SMark Murray int i; 6728c3ea588SMark Murray 6738c3ea588SMark Murray /* These are not really random numbers, they are just 6748c3ea588SMark Murray * numbers that change to thwart construction of a 6758c3ea588SMark Murray * dictionary. This is exposed to the public. 6768c3ea588SMark Murray */ 6778c3ea588SMark Murray for (i = 0; i < SALTSIZE; i += 4) 6788c3ea588SMark Murray to64(&salt[i], arc4random(), 4); 6798c3ea588SMark Murray salt[SALTSIZE] = '\0'; 6808c3ea588SMark Murray } 6818c3ea588SMark Murray 6829294327dSJohn Polstra PAM_MODULE_ENTRY("pam_unix"); 683