19a10bb17SJohn Polstra /*- 25e53a4f9SPedro F. Giffuni * SPDX-License-Identifier: BSD-3-Clause 35e53a4f9SPedro F. Giffuni * 49a10bb17SJohn Polstra * Copyright 1998 Juniper Networks, Inc. 59a10bb17SJohn Polstra * All rights reserved. 64d6991c6SDag-Erling Smørgrav * Copyright (c) 2002-2003 Networks Associates Technology, Inc. 7e9cc7b1dSDag-Erling Smørgrav * All rights reserved. 8e9cc7b1dSDag-Erling Smørgrav * 9e9cc7b1dSDag-Erling Smørgrav * Portions of this software was developed for the FreeBSD Project by 10e9cc7b1dSDag-Erling Smørgrav * ThinkSec AS and NAI Labs, the Security Research Division of Network 11e9cc7b1dSDag-Erling Smørgrav * Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035 12e9cc7b1dSDag-Erling Smørgrav * ("CBOSS"), as part of the DARPA CHATS research program. 139a10bb17SJohn Polstra * 149a10bb17SJohn Polstra * Redistribution and use in source and binary forms, with or without 159a10bb17SJohn Polstra * modification, are permitted provided that the following conditions 169a10bb17SJohn Polstra * are met: 179a10bb17SJohn Polstra * 1. Redistributions of source code must retain the above copyright 189a10bb17SJohn Polstra * notice, this list of conditions and the following disclaimer. 199a10bb17SJohn Polstra * 2. Redistributions in binary form must reproduce the above copyright 209a10bb17SJohn Polstra * notice, this list of conditions and the following disclaimer in the 219a10bb17SJohn Polstra * documentation and/or other materials provided with the distribution. 22e9cc7b1dSDag-Erling Smørgrav * 3. The name of the author may not be used to endorse or promote 23e9cc7b1dSDag-Erling Smørgrav * products derived from this software without specific prior written 24e9cc7b1dSDag-Erling Smørgrav * permission. 259a10bb17SJohn Polstra * 269a10bb17SJohn Polstra * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 279a10bb17SJohn Polstra * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 289a10bb17SJohn Polstra * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 299a10bb17SJohn Polstra * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 309a10bb17SJohn Polstra * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 319a10bb17SJohn Polstra * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 329a10bb17SJohn Polstra * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 339a10bb17SJohn Polstra * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 349a10bb17SJohn Polstra * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 359a10bb17SJohn Polstra * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 369a10bb17SJohn Polstra * SUCH DAMAGE. 379a10bb17SJohn Polstra */ 389a10bb17SJohn Polstra 39ceaf33f5SMatthew Dillon #include <sys/cdefs.h> 40ceaf33f5SMatthew Dillon __FBSDID("$FreeBSD$"); 41ceaf33f5SMatthew Dillon 42e9cc7b1dSDag-Erling Smørgrav #include <sys/param.h> 43e9cc7b1dSDag-Erling Smørgrav #include <sys/socket.h> 44d65b34dbSJohn Polstra #include <sys/time.h> 45e9cc7b1dSDag-Erling Smørgrav #include <netinet/in.h> 46e9cc7b1dSDag-Erling Smørgrav #include <arpa/inet.h> 47e9cc7b1dSDag-Erling Smørgrav 48d65b34dbSJohn Polstra #include <login_cap.h> 49e9cc7b1dSDag-Erling Smørgrav #include <netdb.h> 509a10bb17SJohn Polstra #include <pwd.h> 519a10bb17SJohn Polstra #include <stdlib.h> 529a10bb17SJohn Polstra #include <string.h> 53d65b34dbSJohn Polstra #include <stdio.h> 547f28386aSDag-Erling Smørgrav #include <syslog.h> 55beb8ef4aSDag-Erling Smørgrav #include <time.h> 569a10bb17SJohn Polstra #include <unistd.h> 579a10bb17SJohn Polstra 58f1d05925SDag-Erling Smørgrav #include <libutil.h> 593d55a6c0SMark Murray 603d55a6c0SMark Murray #ifdef YP 61c82bffaaSDag-Erling Smørgrav #include <ypclnt.h> 623d55a6c0SMark Murray #endif 633d55a6c0SMark Murray 649a10bb17SJohn Polstra #define PAM_SM_AUTH 65d65b34dbSJohn Polstra #define PAM_SM_ACCOUNT 663d55a6c0SMark Murray #define PAM_SM_PASSWORD 673d55a6c0SMark Murray 688c66575dSDag-Erling Smørgrav #include <security/pam_appl.h> 699a10bb17SJohn Polstra #include <security/pam_modules.h> 708c66575dSDag-Erling Smørgrav #include <security/pam_mod_misc.h> 719a10bb17SJohn Polstra 723d55a6c0SMark Murray #define PASSWORD_HASH "md5" 731642eb1aSMark Murray #define DEFAULT_WARN (2L * 7L * 86400L) /* Two weeks */ 748c3ea588SMark Murray #define SALTSIZE 32 758c3ea588SMark Murray 76cf21ead5SYaroslav Tykhiy #define LOCKED_PREFIX "*LOCKED*" 77cf21ead5SYaroslav Tykhiy #define LOCKED_PREFIX_LEN (sizeof(LOCKED_PREFIX) - 1) 78cf21ead5SYaroslav Tykhiy 79*5ceec6c1SJohn Baldwin static void makesalt(char [SALTSIZE + 1]); 801642eb1aSMark Murray 81ac569969SMark Murray static char password_hash[] = PASSWORD_HASH; 82ac569969SMark Murray 83545aa471SDag-Erling Smørgrav #define PAM_OPT_LOCAL_PASS "local_pass" 84545aa471SDag-Erling Smørgrav #define PAM_OPT_NIS_PASS "nis_pass" 859a10bb17SJohn Polstra 86d65b34dbSJohn Polstra /* 87d65b34dbSJohn Polstra * authentication management 88d65b34dbSJohn Polstra */ 899a10bb17SJohn Polstra PAM_EXTERN int 906f4d99c6SFelix Palmen pam_sm_authenticate(pam_handle_t *pamh, int flags, 91545aa471SDag-Erling Smørgrav int argc __unused, const char *argv[] __unused) 929a10bb17SJohn Polstra { 933d55a6c0SMark Murray login_cap_t *lc; 949a10bb17SJohn Polstra struct passwd *pwd; 951642eb1aSMark Murray int retval; 96f2f306b6SRuslan Ermilov const char *pass, *user, *realpw, *prompt; 97bfd8b9b8SEdward Tomasz Napierala const char *emptypasswd = ""; 989a10bb17SJohn Polstra 99545aa471SDag-Erling Smørgrav if (openpam_get_option(pamh, PAM_OPT_AUTH_AS_SELF)) { 1001843e23cSDimitry Andric user = getlogin(); 10150000f00SDag-Erling Smørgrav } else { 1021642eb1aSMark Murray retval = pam_get_user(pamh, &user, NULL); 1031642eb1aSMark Murray if (retval != PAM_SUCCESS) 10424fe7ba0SDag-Erling Smørgrav return (retval); 1054448b21cSMark Murray } 1061843e23cSDimitry Andric pwd = getpwnam(user); 1071642eb1aSMark Murray 1081642eb1aSMark Murray PAM_LOG("Got user: %s", user); 1091642eb1aSMark Murray 1104448b21cSMark Murray if (pwd != NULL) { 1111642eb1aSMark Murray PAM_LOG("Doing real authentication"); 11250000f00SDag-Erling Smørgrav realpw = pwd->pw_passwd; 11350000f00SDag-Erling Smørgrav if (realpw[0] == '\0') { 11450000f00SDag-Erling Smørgrav if (!(flags & PAM_DISALLOW_NULL_AUTHTOK) && 115545aa471SDag-Erling Smørgrav openpam_get_option(pamh, PAM_OPT_NULLOK)) 11624fe7ba0SDag-Erling Smørgrav return (PAM_SUCCESS); 11779b67c8dSDag-Erling Smørgrav PAM_LOG("Password is empty, using fake password"); 11850000f00SDag-Erling Smørgrav realpw = "*"; 1191642eb1aSMark Murray } 120bfd8b9b8SEdward Tomasz Napierala /* 121bfd8b9b8SEdward Tomasz Napierala * Check whether the saved password hash matches the one 122bfd8b9b8SEdward Tomasz Napierala * generated from an empty password - as opposed to empty 123bfd8b9b8SEdward Tomasz Napierala * saved password hash, which is handled above. 124bfd8b9b8SEdward Tomasz Napierala */ 125bfd8b9b8SEdward Tomasz Napierala if (!(flags & PAM_DISALLOW_NULL_AUTHTOK) && 126bfd8b9b8SEdward Tomasz Napierala openpam_get_option(pamh, PAM_OPT_EMPTYOK) && 127bfd8b9b8SEdward Tomasz Napierala strcmp(crypt(emptypasswd, realpw), realpw) == 0) 128bfd8b9b8SEdward Tomasz Napierala return (PAM_SUCCESS); 129a8b1e59eSDag-Erling Smørgrav lc = login_getpwclass(pwd); 13050000f00SDag-Erling Smørgrav } else { 1311642eb1aSMark Murray PAM_LOG("Doing dummy authentication"); 13250000f00SDag-Erling Smørgrav realpw = "*"; 133a8b1e59eSDag-Erling Smørgrav lc = login_getclass(NULL); 13450000f00SDag-Erling Smørgrav } 13550000f00SDag-Erling Smørgrav prompt = login_getcapstr(lc, "passwd_prompt", NULL, NULL); 13650000f00SDag-Erling Smørgrav retval = pam_get_authtok(pamh, PAM_AUTHTOK, &pass, prompt); 137a8b1e59eSDag-Erling Smørgrav login_close(lc); 1381642eb1aSMark Murray if (retval != PAM_SUCCESS) 13924fe7ba0SDag-Erling Smørgrav return (retval); 1401642eb1aSMark Murray PAM_LOG("Got password"); 14179b67c8dSDag-Erling Smørgrav if (strnlen(pass, _PASSWORD_LEN + 1) > _PASSWORD_LEN) { 14279b67c8dSDag-Erling Smørgrav PAM_LOG("Password is too long, using fake password"); 14379b67c8dSDag-Erling Smørgrav realpw = "*"; 14479b67c8dSDag-Erling Smørgrav } 14550000f00SDag-Erling Smørgrav if (strcmp(crypt(pass, realpw), realpw) == 0) 14624fe7ba0SDag-Erling Smørgrav return (PAM_SUCCESS); 1471642eb1aSMark Murray 1483d55a6c0SMark Murray PAM_VERBOSE_ERROR("UNIX authentication refused"); 14924fe7ba0SDag-Erling Smørgrav return (PAM_AUTH_ERR); 1509a10bb17SJohn Polstra } 1519a10bb17SJohn Polstra 1529a10bb17SJohn Polstra PAM_EXTERN int 15324fe7ba0SDag-Erling Smørgrav pam_sm_setcred(pam_handle_t *pamh __unused, int flags __unused, 15424fe7ba0SDag-Erling Smørgrav int argc __unused, const char *argv[] __unused) 1559a10bb17SJohn Polstra { 1563d55a6c0SMark Murray 15724fe7ba0SDag-Erling Smørgrav return (PAM_SUCCESS); 1589a10bb17SJohn Polstra } 1599294327dSJohn Polstra 160d65b34dbSJohn Polstra /* 161d65b34dbSJohn Polstra * account management 162d65b34dbSJohn Polstra */ 1633d55a6c0SMark Murray PAM_EXTERN int 16424fe7ba0SDag-Erling Smørgrav pam_sm_acct_mgmt(pam_handle_t *pamh, int flags __unused, 165545aa471SDag-Erling Smørgrav int argc __unused, const char *argv[] __unused) 166d65b34dbSJohn Polstra { 167e9cc7b1dSDag-Erling Smørgrav struct addrinfo hints, *res; 168e9cc7b1dSDag-Erling Smørgrav struct passwd *pwd; 169d65b34dbSJohn Polstra struct timeval tp; 1701642eb1aSMark Murray login_cap_t *lc; 171d65b34dbSJohn Polstra time_t warntime; 172d65b34dbSJohn Polstra int retval; 17391e93869SDag-Erling Smørgrav const char *user; 17491e93869SDag-Erling Smørgrav const void *rhost, *tty; 1758f303102SDag-Erling Smørgrav char rhostip[MAXHOSTNAMELEN] = ""; 1761642eb1aSMark Murray 177111ccd25SDag-Erling Smørgrav retval = pam_get_user(pamh, &user, NULL); 178e9cc7b1dSDag-Erling Smørgrav if (retval != PAM_SUCCESS) 17924fe7ba0SDag-Erling Smørgrav return (retval); 180d65b34dbSJohn Polstra 181e9cc7b1dSDag-Erling Smørgrav if (user == NULL || (pwd = getpwnam(user)) == NULL) 18224fe7ba0SDag-Erling Smørgrav return (PAM_SERVICE_ERR); 1831642eb1aSMark Murray 1841642eb1aSMark Murray PAM_LOG("Got user: %s", user); 185d65b34dbSJohn Polstra 18691e93869SDag-Erling Smørgrav retval = pam_get_item(pamh, PAM_RHOST, &rhost); 187e9cc7b1dSDag-Erling Smørgrav if (retval != PAM_SUCCESS) 18824fe7ba0SDag-Erling Smørgrav return (retval); 189d65b34dbSJohn Polstra 19091e93869SDag-Erling Smørgrav retval = pam_get_item(pamh, PAM_TTY, &tty); 191e9cc7b1dSDag-Erling Smørgrav if (retval != PAM_SUCCESS) 19224fe7ba0SDag-Erling Smørgrav return (retval); 193d65b34dbSJohn Polstra 194e9cc7b1dSDag-Erling Smørgrav if (*pwd->pw_passwd == '\0' && 195e9cc7b1dSDag-Erling Smørgrav (flags & PAM_DISALLOW_NULL_AUTHTOK) != 0) 196e9cc7b1dSDag-Erling Smørgrav return (PAM_NEW_AUTHTOK_REQD); 197e9cc7b1dSDag-Erling Smørgrav 198cf21ead5SYaroslav Tykhiy if (strncmp(pwd->pw_passwd, LOCKED_PREFIX, LOCKED_PREFIX_LEN) == 0) 199cf21ead5SYaroslav Tykhiy return (PAM_AUTH_ERR); 200cf21ead5SYaroslav Tykhiy 201e9cc7b1dSDag-Erling Smørgrav lc = login_getpwclass(pwd); 202e9cc7b1dSDag-Erling Smørgrav if (lc == NULL) { 203e9cc7b1dSDag-Erling Smørgrav PAM_LOG("Unable to get login class for user %s", user); 204e9cc7b1dSDag-Erling Smørgrav return (PAM_SERVICE_ERR); 205e9cc7b1dSDag-Erling Smørgrav } 206d65b34dbSJohn Polstra 2071642eb1aSMark Murray PAM_LOG("Got login_cap"); 2081642eb1aSMark Murray 209e9cc7b1dSDag-Erling Smørgrav if (pwd->pw_change || pwd->pw_expire) 210e9cc7b1dSDag-Erling Smørgrav gettimeofday(&tp, NULL); 211d65b34dbSJohn Polstra 212e9cc7b1dSDag-Erling Smørgrav /* 213e9cc7b1dSDag-Erling Smørgrav * Check pw_expire before pw_change - no point in letting the 214e9cc7b1dSDag-Erling Smørgrav * user change the password on an expired account. 215e9cc7b1dSDag-Erling Smørgrav */ 216d65b34dbSJohn Polstra 217e9cc7b1dSDag-Erling Smørgrav if (pwd->pw_expire) { 218e9cc7b1dSDag-Erling Smørgrav warntime = login_getcaptime(lc, "warnexpire", 219e9cc7b1dSDag-Erling Smørgrav DEFAULT_WARN, DEFAULT_WARN); 220e9cc7b1dSDag-Erling Smørgrav if (tp.tv_sec >= pwd->pw_expire) { 221e9cc7b1dSDag-Erling Smørgrav login_close(lc); 22224fe7ba0SDag-Erling Smørgrav return (PAM_ACCT_EXPIRED); 223e9cc7b1dSDag-Erling Smørgrav } else if (pwd->pw_expire - tp.tv_sec < warntime && 224e9cc7b1dSDag-Erling Smørgrav (flags & PAM_SILENT) == 0) { 225519b6a4cSDag-Erling Smørgrav pam_error(pamh, "Warning: your account expires on %s", 226e9cc7b1dSDag-Erling Smørgrav ctime(&pwd->pw_expire)); 227d65b34dbSJohn Polstra } 228d65b34dbSJohn Polstra } 229d65b34dbSJohn Polstra 230e9cc7b1dSDag-Erling Smørgrav retval = PAM_SUCCESS; 231e9cc7b1dSDag-Erling Smørgrav if (pwd->pw_change) { 232e9cc7b1dSDag-Erling Smørgrav warntime = login_getcaptime(lc, "warnpassword", 233e9cc7b1dSDag-Erling Smørgrav DEFAULT_WARN, DEFAULT_WARN); 234e9cc7b1dSDag-Erling Smørgrav if (tp.tv_sec >= pwd->pw_change) { 235e9cc7b1dSDag-Erling Smørgrav retval = PAM_NEW_AUTHTOK_REQD; 236e9cc7b1dSDag-Erling Smørgrav } else if (pwd->pw_change - tp.tv_sec < warntime && 237e9cc7b1dSDag-Erling Smørgrav (flags & PAM_SILENT) == 0) { 238519b6a4cSDag-Erling Smørgrav pam_error(pamh, "Warning: your password expires on %s", 239e9cc7b1dSDag-Erling Smørgrav ctime(&pwd->pw_change)); 240e9cc7b1dSDag-Erling Smørgrav } 241e9cc7b1dSDag-Erling Smørgrav } 242e9cc7b1dSDag-Erling Smørgrav 243e9cc7b1dSDag-Erling Smørgrav /* 244e9cc7b1dSDag-Erling Smørgrav * From here on, we must leave retval untouched (unless we 245e9cc7b1dSDag-Erling Smørgrav * know we're going to fail), because we need to remember 246e9cc7b1dSDag-Erling Smørgrav * whether we're supposed to return PAM_SUCCESS or 247e9cc7b1dSDag-Erling Smørgrav * PAM_NEW_AUTHTOK_REQD. 248e9cc7b1dSDag-Erling Smørgrav */ 249e9cc7b1dSDag-Erling Smørgrav 25091e93869SDag-Erling Smørgrav if (rhost && *(const char *)rhost != '\0') { 251e9cc7b1dSDag-Erling Smørgrav memset(&hints, 0, sizeof(hints)); 252e9cc7b1dSDag-Erling Smørgrav hints.ai_family = AF_UNSPEC; 253e9cc7b1dSDag-Erling Smørgrav if (getaddrinfo(rhost, NULL, &hints, &res) == 0) { 254e9cc7b1dSDag-Erling Smørgrav getnameinfo(res->ai_addr, res->ai_addrlen, 255e9cc7b1dSDag-Erling Smørgrav rhostip, sizeof(rhostip), NULL, 0, 256d928d41cSHajimu UMEMOTO NI_NUMERICHOST); 257e9cc7b1dSDag-Erling Smørgrav } 258e9cc7b1dSDag-Erling Smørgrav if (res != NULL) 259e9cc7b1dSDag-Erling Smørgrav freeaddrinfo(res); 260e9cc7b1dSDag-Erling Smørgrav } 261e9cc7b1dSDag-Erling Smørgrav 262e9cc7b1dSDag-Erling Smørgrav /* 263e9cc7b1dSDag-Erling Smørgrav * Check host / tty / time-of-day restrictions 264e9cc7b1dSDag-Erling Smørgrav */ 265e9cc7b1dSDag-Erling Smørgrav 266e9cc7b1dSDag-Erling Smørgrav if (!auth_hostok(lc, rhost, rhostip) || 267e9cc7b1dSDag-Erling Smørgrav !auth_ttyok(lc, tty) || 268e9cc7b1dSDag-Erling Smørgrav !auth_timeok(lc, time(NULL))) 269e9cc7b1dSDag-Erling Smørgrav retval = PAM_AUTH_ERR; 270e9cc7b1dSDag-Erling Smørgrav 271d65b34dbSJohn Polstra login_close(lc); 2721642eb1aSMark Murray 27324fe7ba0SDag-Erling Smørgrav return (retval); 2743d55a6c0SMark Murray } 2753d55a6c0SMark Murray 2763d55a6c0SMark Murray /* 2773d55a6c0SMark Murray * password management 2783d55a6c0SMark Murray * 2793d55a6c0SMark Murray * standard Unix and NIS password changing 2803d55a6c0SMark Murray */ 2813d55a6c0SMark Murray PAM_EXTERN int 28224fe7ba0SDag-Erling Smørgrav pam_sm_chauthtok(pam_handle_t *pamh, int flags, 283545aa471SDag-Erling Smørgrav int argc __unused, const char *argv[] __unused) 2843d55a6c0SMark Murray { 285ff1bc287SDag-Erling Smørgrav #ifdef YP 286ff1bc287SDag-Erling Smørgrav struct ypclnt *ypclnt; 28764dbe1a7SDag-Erling Smørgrav const void *yp_domain, *yp_server; 288ff1bc287SDag-Erling Smørgrav #endif 289ff1bc287SDag-Erling Smørgrav char salt[SALTSIZE + 1]; 290ff1bc287SDag-Erling Smørgrav login_cap_t *lc; 291f1d05925SDag-Erling Smørgrav struct passwd *pwd, *old_pwd; 292ff1bc287SDag-Erling Smørgrav const char *user, *old_pass, *new_pass; 293ff1bc287SDag-Erling Smørgrav char *encrypted; 294cc5c81f8SDag-Erling Smørgrav time_t passwordtime; 295ff1bc287SDag-Erling Smørgrav int pfd, tfd, retval; 2963d55a6c0SMark Murray 297545aa471SDag-Erling Smørgrav if (openpam_get_option(pamh, PAM_OPT_AUTH_AS_SELF)) 298653d2f36SDon Lewis user = getlogin(); 2993d55a6c0SMark Murray else { 3003d55a6c0SMark Murray retval = pam_get_user(pamh, &user, NULL); 3013d55a6c0SMark Murray if (retval != PAM_SUCCESS) 30224fe7ba0SDag-Erling Smørgrav return (retval); 3033d55a6c0SMark Murray } 304653d2f36SDon Lewis pwd = getpwnam(user); 3053d55a6c0SMark Murray 306f1d05925SDag-Erling Smørgrav if (pwd == NULL) 307f1d05925SDag-Erling Smørgrav return (PAM_AUTHTOK_RECOVERY_ERR); 308f1d05925SDag-Erling Smørgrav 3093d55a6c0SMark Murray PAM_LOG("Got user: %s", user); 3103d55a6c0SMark Murray 3113d55a6c0SMark Murray if (flags & PAM_PRELIM_CHECK) { 3123d55a6c0SMark Murray 3137b733689SDag-Erling Smørgrav PAM_LOG("PRELIM round"); 3143d55a6c0SMark Murray 3150f6517b7SDag-Erling Smørgrav if (getuid() == 0 && 3160f6517b7SDag-Erling Smørgrav (pwd->pw_fields & _PWF_SOURCE) == _PWF_FILES) 3170f6517b7SDag-Erling Smørgrav /* root doesn't need the old password */ 3180f6517b7SDag-Erling Smørgrav return (pam_set_item(pamh, PAM_OLDAUTHTOK, "")); 319dd01398dSMartin Blapp #ifdef YP 320dd01398dSMartin Blapp if (getuid() == 0 && 321dd01398dSMartin Blapp (pwd->pw_fields & _PWF_SOURCE) == _PWF_NIS) { 3220f6517b7SDag-Erling Smørgrav 323dd01398dSMartin Blapp yp_domain = yp_server = NULL; 32491e93869SDag-Erling Smørgrav (void)pam_get_data(pamh, "yp_domain", &yp_domain); 32591e93869SDag-Erling Smørgrav (void)pam_get_data(pamh, "yp_server", &yp_server); 326dd01398dSMartin Blapp 327dd01398dSMartin Blapp ypclnt = ypclnt_new(yp_domain, "passwd.byname", yp_server); 328dd01398dSMartin Blapp if (ypclnt == NULL) 329dd01398dSMartin Blapp return (PAM_BUF_ERR); 330dd01398dSMartin Blapp 331dd01398dSMartin Blapp if (ypclnt_connect(ypclnt) == -1) { 332dd01398dSMartin Blapp ypclnt_free(ypclnt); 333dd01398dSMartin Blapp return (PAM_SERVICE_ERR); 334dd01398dSMartin Blapp } 335dd01398dSMartin Blapp 336dd01398dSMartin Blapp retval = ypclnt_havepasswdd(ypclnt); 337dd01398dSMartin Blapp ypclnt_free(ypclnt); 338dd01398dSMartin Blapp if (retval == 1) 339dd01398dSMartin Blapp return (pam_set_item(pamh, PAM_OLDAUTHTOK, "")); 340dd01398dSMartin Blapp else if (retval == -1) 341dd01398dSMartin Blapp return (PAM_SERVICE_ERR); 342dd01398dSMartin Blapp } 343dd01398dSMartin Blapp #endif 3443d55a6c0SMark Murray if (pwd->pw_passwd[0] == '\0' 345545aa471SDag-Erling Smørgrav && openpam_get_option(pamh, PAM_OPT_NULLOK)) { 3463d55a6c0SMark Murray /* 3473d55a6c0SMark Murray * No password case. XXX Are we giving too much away 3483d55a6c0SMark Murray * by not prompting for a password? 349111ccd25SDag-Erling Smørgrav * XXX check PAM_DISALLOW_NULL_AUTHTOK 3503d55a6c0SMark Murray */ 351ff1bc287SDag-Erling Smørgrav old_pass = ""; 35230f64800SDon Lewis retval = PAM_SUCCESS; 353ff1bc287SDag-Erling Smørgrav } else { 354111ccd25SDag-Erling Smørgrav retval = pam_get_authtok(pamh, 355ff1bc287SDag-Erling Smørgrav PAM_OLDAUTHTOK, &old_pass, NULL); 3563d55a6c0SMark Murray if (retval != PAM_SUCCESS) 35724fe7ba0SDag-Erling Smørgrav return (retval); 3583d55a6c0SMark Murray } 359111ccd25SDag-Erling Smørgrav PAM_LOG("Got old password"); 360ff1bc287SDag-Erling Smørgrav /* always encrypt first */ 361ff1bc287SDag-Erling Smørgrav encrypted = crypt(old_pass, pwd->pw_passwd); 362be01d58dSDag-Erling Smørgrav if (old_pass[0] == '\0' && 363545aa471SDag-Erling Smørgrav !openpam_get_option(pamh, PAM_OPT_NULLOK)) 364be01d58dSDag-Erling Smørgrav return (PAM_PERM_DENIED); 365be01d58dSDag-Erling Smørgrav if (strcmp(encrypted, pwd->pw_passwd) != 0) 366ff1bc287SDag-Erling Smørgrav return (PAM_PERM_DENIED); 3677b733689SDag-Erling Smørgrav } 3687b733689SDag-Erling Smørgrav else if (flags & PAM_UPDATE_AUTHTOK) { 3697b733689SDag-Erling Smørgrav PAM_LOG("UPDATE round"); 3707b733689SDag-Erling Smørgrav 3717b733689SDag-Erling Smørgrav retval = pam_get_authtok(pamh, 3720f6517b7SDag-Erling Smørgrav PAM_OLDAUTHTOK, &old_pass, NULL); 3737b733689SDag-Erling Smørgrav if (retval != PAM_SUCCESS) 3747b733689SDag-Erling Smørgrav return (retval); 3757b733689SDag-Erling Smørgrav PAM_LOG("Got old password"); 3763d55a6c0SMark Murray 377ff1bc287SDag-Erling Smørgrav /* get new password */ 378111ccd25SDag-Erling Smørgrav for (;;) { 379111ccd25SDag-Erling Smørgrav retval = pam_get_authtok(pamh, 380111ccd25SDag-Erling Smørgrav PAM_AUTHTOK, &new_pass, NULL); 381111ccd25SDag-Erling Smørgrav if (retval != PAM_TRY_AGAIN) 3823d55a6c0SMark Murray break; 383111ccd25SDag-Erling Smørgrav pam_error(pamh, "Mismatch; try again, EOF to quit."); 3843d55a6c0SMark Murray } 385ff1bc287SDag-Erling Smørgrav PAM_LOG("Got new password"); 386c8c20c52SDag-Erling Smørgrav if (retval != PAM_SUCCESS) { 387111ccd25SDag-Erling Smørgrav PAM_VERBOSE_ERROR("Unable to get new password"); 388ff1bc287SDag-Erling Smørgrav return (retval); 389c8c20c52SDag-Erling Smørgrav } 390ff1bc287SDag-Erling Smørgrav 391be01d58dSDag-Erling Smørgrav if (getuid() != 0 && new_pass[0] == '\0' && 392545aa471SDag-Erling Smørgrav !openpam_get_option(pamh, PAM_OPT_NULLOK)) 393be01d58dSDag-Erling Smørgrav return (PAM_PERM_DENIED); 394be01d58dSDag-Erling Smørgrav 395f1d05925SDag-Erling Smørgrav if ((old_pwd = pw_dup(pwd)) == NULL) 396f1d05925SDag-Erling Smørgrav return (PAM_BUF_ERR); 397f1d05925SDag-Erling Smørgrav 398d3cf5f15SDag-Erling Smørgrav lc = login_getclass(pwd->pw_class); 399ff1bc287SDag-Erling Smørgrav if (login_setcryptfmt(lc, password_hash, NULL) == NULL) 400ff1bc287SDag-Erling Smørgrav openpam_log(PAM_LOG_ERROR, 401ff1bc287SDag-Erling Smørgrav "can't set password cipher, relying on default"); 402cc5c81f8SDag-Erling Smørgrav 403cc5c81f8SDag-Erling Smørgrav /* set password expiry date */ 404cc5c81f8SDag-Erling Smørgrav pwd->pw_change = 0; 405cc5c81f8SDag-Erling Smørgrav passwordtime = login_getcaptime(lc, "passwordtime", 0, 0); 406cc5c81f8SDag-Erling Smørgrav if (passwordtime > 0) 407cc5c81f8SDag-Erling Smørgrav pwd->pw_change = time(NULL) + passwordtime; 408cc5c81f8SDag-Erling Smørgrav 409ff1bc287SDag-Erling Smørgrav login_close(lc); 410ff1bc287SDag-Erling Smørgrav makesalt(salt); 411ff1bc287SDag-Erling Smørgrav pwd->pw_passwd = crypt(new_pass, salt); 4123d55a6c0SMark Murray #ifdef YP 413f1d05925SDag-Erling Smørgrav switch (old_pwd->pw_fields & _PWF_SOURCE) { 414ff1bc287SDag-Erling Smørgrav case _PWF_FILES: 415ff1bc287SDag-Erling Smørgrav #endif 416ff1bc287SDag-Erling Smørgrav retval = PAM_SERVICE_ERR; 417f1d05925SDag-Erling Smørgrav if (pw_init(NULL, NULL)) 418f1d05925SDag-Erling Smørgrav openpam_log(PAM_LOG_ERROR, "pw_init() failed"); 419f1d05925SDag-Erling Smørgrav else if ((pfd = pw_lock()) == -1) 420f1d05925SDag-Erling Smørgrav openpam_log(PAM_LOG_ERROR, "pw_lock() failed"); 421f1d05925SDag-Erling Smørgrav else if ((tfd = pw_tmp(-1)) == -1) 422f1d05925SDag-Erling Smørgrav openpam_log(PAM_LOG_ERROR, "pw_tmp() failed"); 423f1d05925SDag-Erling Smørgrav else if (pw_copy(pfd, tfd, pwd, old_pwd) == -1) 424f1d05925SDag-Erling Smørgrav openpam_log(PAM_LOG_ERROR, "pw_copy() failed"); 425f1d05925SDag-Erling Smørgrav else if (pw_mkdb(pwd->pw_name) == -1) 426f1d05925SDag-Erling Smørgrav openpam_log(PAM_LOG_ERROR, "pw_mkdb() failed"); 427f1d05925SDag-Erling Smørgrav else 428f1d05925SDag-Erling Smørgrav retval = PAM_SUCCESS; 429f1d05925SDag-Erling Smørgrav pw_fini(); 430ff1bc287SDag-Erling Smørgrav #ifdef YP 431ff1bc287SDag-Erling Smørgrav break; 432ff1bc287SDag-Erling Smørgrav case _PWF_NIS: 433ff1bc287SDag-Erling Smørgrav yp_domain = yp_server = NULL; 43491e93869SDag-Erling Smørgrav (void)pam_get_data(pamh, "yp_domain", &yp_domain); 43591e93869SDag-Erling Smørgrav (void)pam_get_data(pamh, "yp_server", &yp_server); 436ff1bc287SDag-Erling Smørgrav ypclnt = ypclnt_new(yp_domain, 437ff1bc287SDag-Erling Smørgrav "passwd.byname", yp_server); 438f1d05925SDag-Erling Smørgrav if (ypclnt == NULL) { 439f1d05925SDag-Erling Smørgrav retval = PAM_BUF_ERR; 440f1d05925SDag-Erling Smørgrav } else if (ypclnt_connect(ypclnt) == -1 || 441ff1bc287SDag-Erling Smørgrav ypclnt_passwd(ypclnt, pwd, old_pass) == -1) { 442ff1bc287SDag-Erling Smørgrav openpam_log(PAM_LOG_ERROR, "%s", ypclnt->error); 443ff1bc287SDag-Erling Smørgrav retval = PAM_SERVICE_ERR; 444f1d05925SDag-Erling Smørgrav } else { 445f1d05925SDag-Erling Smørgrav retval = PAM_SUCCESS; 4463d55a6c0SMark Murray } 447ff1bc287SDag-Erling Smørgrav ypclnt_free(ypclnt); 448ff1bc287SDag-Erling Smørgrav break; 449ff1bc287SDag-Erling Smørgrav default: 450ff1bc287SDag-Erling Smørgrav openpam_log(PAM_LOG_ERROR, "unsupported source 0x%x", 451ff1bc287SDag-Erling Smørgrav pwd->pw_fields & _PWF_SOURCE); 452ff1bc287SDag-Erling Smørgrav retval = PAM_SERVICE_ERR; 4533d55a6c0SMark Murray } 4543d55a6c0SMark Murray #endif 455816c6c91SJuli Mallett free(old_pwd); 4563d55a6c0SMark Murray } 4573d55a6c0SMark Murray else { 4583d55a6c0SMark Murray /* Very bad juju */ 4593d55a6c0SMark Murray retval = PAM_ABORT; 4603d55a6c0SMark Murray PAM_LOG("Illegal 'flags'"); 4613d55a6c0SMark Murray } 4623d55a6c0SMark Murray 46324fe7ba0SDag-Erling Smørgrav return (retval); 4643d55a6c0SMark Murray } 4653d55a6c0SMark Murray 4663d55a6c0SMark Murray /* Mostly stolen from passwd(1)'s local_passwd.c - markm */ 4673d55a6c0SMark Murray 4683d55a6c0SMark Murray static unsigned char itoa64[] = /* 0 ... 63 => ascii - 64 */ 4693d55a6c0SMark Murray "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; 4703d55a6c0SMark Murray 4713d55a6c0SMark Murray static void 4723d55a6c0SMark Murray to64(char *s, long v, int n) 4733d55a6c0SMark Murray { 4743d55a6c0SMark Murray while (--n >= 0) { 4753d55a6c0SMark Murray *s++ = itoa64[v&0x3f]; 4763d55a6c0SMark Murray v >>= 6; 4773d55a6c0SMark Murray } 4783d55a6c0SMark Murray } 4793d55a6c0SMark Murray 4808c3ea588SMark Murray /* Salt suitable for traditional DES and MD5 */ 481068f3d2fSJung-uk Kim static void 482068f3d2fSJung-uk Kim makesalt(char salt[SALTSIZE + 1]) 4838c3ea588SMark Murray { 4848c3ea588SMark Murray int i; 4858c3ea588SMark Murray 4868c3ea588SMark Murray /* These are not really random numbers, they are just 4878c3ea588SMark Murray * numbers that change to thwart construction of a 488068f3d2fSJung-uk Kim * dictionary. 4898c3ea588SMark Murray */ 4908c3ea588SMark Murray for (i = 0; i < SALTSIZE; i += 4) 4918c3ea588SMark Murray to64(&salt[i], arc4random(), 4); 4928c3ea588SMark Murray salt[SALTSIZE] = '\0'; 4938c3ea588SMark Murray } 4948c3ea588SMark Murray 4959294327dSJohn Polstra PAM_MODULE_ENTRY("pam_unix"); 496