diff options
Diffstat (limited to 'lib/libpam/modules/pam_unix')
-rw-r--r-- | lib/libpam/modules/pam_unix/Makefile | 52 | ||||
-rw-r--r-- | lib/libpam/modules/pam_unix/Makefile.depend | 19 | ||||
-rw-r--r-- | lib/libpam/modules/pam_unix/Makefile.depend.options | 7 | ||||
-rw-r--r-- | lib/libpam/modules/pam_unix/pam_unix.8 | 215 | ||||
-rw-r--r-- | lib/libpam/modules/pam_unix/pam_unix.c | 492 |
5 files changed, 785 insertions, 0 deletions
diff --git a/lib/libpam/modules/pam_unix/Makefile b/lib/libpam/modules/pam_unix/Makefile new file mode 100644 index 000000000000..2e76f054c502 --- /dev/null +++ b/lib/libpam/modules/pam_unix/Makefile @@ -0,0 +1,52 @@ +# Copyright 1998 Juniper Networks, Inc. +# All rights reserved. +# Copyright (c) 2002 Networks Associates Technology, Inc. +# All rights reserved. +# +# Portions of this software was developed for the FreeBSD Project by +# ThinkSec AS and NAI Labs, the Security Research Division of Network +# Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035 +# ("CBOSS"), as part of the DARPA CHATS research program. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. The name of the author may not be used to endorse or promote +# products derived from this software without specific prior written +# permission. +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# + +.include <src.opts.mk> +.include <bsd.init.mk> + +PACKAGE= runtime + +LIB= pam_unix +SRCS= pam_unix.c +MAN= pam_unix.8 + +LIBADD+= util crypt + +.if ${MK_NIS} != "no" +CFLAGS+= -DYP +LIBADD+= ypclnt +.endif + +.include <bsd.lib.mk> diff --git a/lib/libpam/modules/pam_unix/Makefile.depend b/lib/libpam/modules/pam_unix/Makefile.depend new file mode 100644 index 000000000000..e852c4988ea6 --- /dev/null +++ b/lib/libpam/modules/pam_unix/Makefile.depend @@ -0,0 +1,19 @@ +# Autogenerated - do NOT edit! + +DIRDEPS = \ + include \ + include/arpa \ + include/xlocale \ + lib/${CSU_DIR} \ + lib/libc \ + lib/libcompiler_rt \ + lib/libcrypt \ + lib/libpam/libpam \ + lib/libutil \ + + +.include <dirdeps.mk> + +.if ${DEP_RELDIR} == ${_DEP_RELDIR} +# local dependencies - needed for -jN in clean tree +.endif diff --git a/lib/libpam/modules/pam_unix/Makefile.depend.options b/lib/libpam/modules/pam_unix/Makefile.depend.options new file mode 100644 index 000000000000..a43cdcfaffb4 --- /dev/null +++ b/lib/libpam/modules/pam_unix/Makefile.depend.options @@ -0,0 +1,7 @@ +# This file is not autogenerated - take care! + +DIRDEPS_OPTIONS= NIS + +DIRDEPS.NIS.yes= lib/libypclnt + +.include <dirdeps-options.mk> diff --git a/lib/libpam/modules/pam_unix/pam_unix.8 b/lib/libpam/modules/pam_unix/pam_unix.8 new file mode 100644 index 000000000000..5c2ae5cf52e2 --- /dev/null +++ b/lib/libpam/modules/pam_unix/pam_unix.8 @@ -0,0 +1,215 @@ +.\" Copyright (c) 2001 Mark R V Murray +.\" All rights reserved. +.\" Copyright (c) 2001 Networks Associates Technology, Inc. +.\" All rights reserved. +.\" +.\" This software was developed for the FreeBSD Project by ThinkSec AS and +.\" NAI Labs, the Security Research Division of Network Associates, Inc. +.\" under DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"), as part of the +.\" DARPA CHATS research program. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. The name of the author may not be used to endorse or promote +.\" products derived from this software without specific prior written +.\" permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.Dd April 3, 2020 +.Dt PAM_UNIX 8 +.Os +.Sh NAME +.Nm pam_unix +.Nd UNIX PAM module +.Sh SYNOPSIS +.Op Ar service-name +.Ar module-type +.Ar control-flag +.Pa pam_unix +.Op Ar options +.Sh DESCRIPTION +The +.Ux +authentication service module for PAM, +.Nm +provides functionality for three PAM categories: +authentication, account management, and password management. +In terms of the +.Ar module-type +parameter, they are the +.Dq Li auth , +.Dq Li account , +and +.Dq Li password +features. +It also provides a null function for session management. +.Ss Ux Ss Authentication Module +The +.Ux +authentication component provides functions to verify the identity of +a user +.Pq Fn pam_sm_authenticate , +which obtains the relevant +.Xr passwd 5 +entry. +It prompts the user for a password and verifies that this is correct with +.Xr crypt 3 . +.Pp +The following options may be passed to the authentication module: +.Bl -tag -width ".Cm use_first_pass" +.It Cm debug +.Xr syslog 3 +debugging information at +.Dv LOG_DEBUG +level. +.It Cm use_first_pass +If the authentication module is not the first in the stack, and a +previous module obtained the user's password, that password is used to +authenticate the user. +If this fails, the authentication module returns failure without +prompting the user for a password. +This option has no effect if the authentication module is the first in +the stack, or if no previous modules obtained the user's password. +.It Cm try_first_pass +This option is similar to the +.Cm use_first_pass +option, except that if the previously obtained password fails, the +user is prompted for another password. +.It Cm auth_as_self +This option will require the user to authenticate themselves as +themselves, not as the account they are attempting to access. +This is primarily for services like +.Xr su 1 , +where the user's ability to retype their own password might be deemed +sufficient. +.It Cm nullok +If the password database has no password for the entity being +authenticated, then this option will forgo password prompting, and +silently allow authentication to succeed. +.Pp +.Sy NOTE: +If +.Nm +is invoked by a process that does not have the privileges required to +access the password database (in most cases, this means root +privileges), the +.Cm nullok +option may cause +.Nm +to allow any user to log in with any password. +.It Cm emptyok +If the password database contains the password for the entity being +authenticated, but the password matches an empty string, +then this option will forgo password prompting, and +silently allow authentication to succeed. +.Pp +The difference between this and +.Cm nullok +is that it avoids prompting for password when the password is set +to an empty string, as opposed to not being set. +.It Cm local_pass +Use only the local password database, even if NIS is in use. +This will cause an authentication failure if the system is configured +to only use NIS. +.It Cm nis_pass +Use only the NIS password database. +This will cause an authentication failure if the system is not +configured to use NIS. +.El +.Ss Ux Ss Account Management Module +The +.Ux +account management component provides a function to perform account +management, +.Fn pam_sm_acct_mgmt . +The function verifies that the authenticated user is allowed to log +into the local user account by checking the following criteria: +.Bl -dash -offset indent +.It +locked status of the account compatible with +.Xr pw 8 +.Cm lock ; +.It +the password expiry date from +.Xr passwd 5 ; +.It +.Xr login.conf 5 +restrictions on the remote host, login time, and tty. +.El +.Pp +The following options may be passed to the management module: +.Bl -tag -width ".Cm use_first_pass" +.It Cm debug +.Xr syslog 3 +debugging information at +.Dv LOG_DEBUG +level. +.El +.Ss Ux Ss Password Management Module +The +.Ux +password management component provides a function to perform password +management, +.Fn pam_sm_chauthtok . +The function changes +the user's password. +.Pp +The following options may be passed to the password module: +.Bl -tag -width ".Cm use_first_pass" +.It Cm debug +.Xr syslog 3 +debugging information at +.Dv LOG_DEBUG +level. +.It Cm no_warn +suppress warning messages to the user. +These messages include reasons why the user's authentication attempt +was declined. +.It Cm local_pass +forces the password module to change a local password in favour of a +NIS one. +.It Cm nis_pass +forces the password module to change a NIS password in favour of a +local one. +.El +.Sh FILES +.Bl -tag -width ".Pa /etc/master.passwd" -compact +.It Pa /etc/master.passwd +default +.Ux +password database. +.El +.Sh SEE ALSO +.Xr passwd 1 , +.Xr getlogin 2 , +.Xr crypt 3 , +.Xr getpwent 3 , +.Xr pam 3 , +.Xr syslog 3 , +.Xr nsswitch.conf 5 , +.Xr passwd 5 , +.Xr pw 8 , +.Xr yp 8 +.Sh BUGS +The +.Nm +module ignores the +.Dv PAM_CHANGE_EXPIRED_AUTHTOK +flag. diff --git a/lib/libpam/modules/pam_unix/pam_unix.c b/lib/libpam/modules/pam_unix/pam_unix.c new file mode 100644 index 000000000000..88313f6ebae8 --- /dev/null +++ b/lib/libpam/modules/pam_unix/pam_unix.c @@ -0,0 +1,492 @@ +/*- + * SPDX-License-Identifier: BSD-3-Clause + * + * Copyright 1998 Juniper Networks, Inc. + * All rights reserved. + * Copyright (c) 2002-2003 Networks Associates Technology, Inc. + * All rights reserved. + * + * Portions of this software was developed for the FreeBSD Project by + * ThinkSec AS and NAI Labs, the Security Research Division of Network + * Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035 + * ("CBOSS"), as part of the DARPA CHATS research program. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/param.h> +#include <sys/socket.h> +#include <sys/time.h> +#include <netinet/in.h> +#include <arpa/inet.h> + +#include <login_cap.h> +#include <netdb.h> +#include <pwd.h> +#include <stdlib.h> +#include <string.h> +#include <stdio.h> +#include <syslog.h> +#include <time.h> +#include <unistd.h> + +#include <libutil.h> + +#ifdef YP +#include <ypclnt.h> +#endif + +#define PAM_SM_AUTH +#define PAM_SM_ACCOUNT +#define PAM_SM_PASSWORD + +#include <security/pam_appl.h> +#include <security/pam_modules.h> +#include <security/pam_mod_misc.h> + +#define PASSWORD_HASH "md5" +#define DEFAULT_WARN (2L * 7L * 86400L) /* Two weeks */ +#define SALTSIZE 32 + +#define LOCKED_PREFIX "*LOCKED*" +#define LOCKED_PREFIX_LEN (sizeof(LOCKED_PREFIX) - 1) + +static void makesalt(char [SALTSIZE + 1]); + +static char password_hash[] = PASSWORD_HASH; + +#define PAM_OPT_LOCAL_PASS "local_pass" +#define PAM_OPT_NIS_PASS "nis_pass" + +/* + * authentication management + */ +PAM_EXTERN int +pam_sm_authenticate(pam_handle_t *pamh, int flags, + int argc __unused, const char *argv[] __unused) +{ + login_cap_t *lc; + struct passwd *pwd; + int retval; + const char *pass, *user, *realpw, *prompt; + const char *emptypasswd = ""; + + if (openpam_get_option(pamh, PAM_OPT_AUTH_AS_SELF)) { + user = getlogin(); + } else { + retval = pam_get_user(pamh, &user, NULL); + if (retval != PAM_SUCCESS) + return (retval); + } + pwd = getpwnam(user); + + PAM_LOG("Got user: %s", user); + + if (pwd != NULL) { + PAM_LOG("Doing real authentication"); + realpw = pwd->pw_passwd; + if (realpw[0] == '\0') { + if (!(flags & PAM_DISALLOW_NULL_AUTHTOK) && + openpam_get_option(pamh, PAM_OPT_NULLOK)) + return (PAM_SUCCESS); + PAM_LOG("Password is empty, using fake password"); + realpw = "*"; + } + /* + * Check whether the saved password hash matches the one + * generated from an empty password - as opposed to empty + * saved password hash, which is handled above. + */ + if (!(flags & PAM_DISALLOW_NULL_AUTHTOK) && + openpam_get_option(pamh, PAM_OPT_EMPTYOK) && + strcmp(crypt(emptypasswd, realpw), realpw) == 0) + return (PAM_SUCCESS); + lc = login_getpwclass(pwd); + } else { + PAM_LOG("Doing dummy authentication"); + realpw = "*"; + lc = login_getclass(NULL); + } + prompt = login_getcapstr(lc, "passwd_prompt", NULL, NULL); + retval = pam_get_authtok(pamh, PAM_AUTHTOK, &pass, prompt); + login_close(lc); + if (retval != PAM_SUCCESS) + return (retval); + PAM_LOG("Got password"); + if (strnlen(pass, _PASSWORD_LEN + 1) > _PASSWORD_LEN) { + PAM_LOG("Password is too long, using fake password"); + realpw = "*"; + } + if (strcmp(crypt(pass, realpw), realpw) == 0) + return (PAM_SUCCESS); + + PAM_VERBOSE_ERROR("UNIX authentication refused"); + return (PAM_AUTH_ERR); +} + +PAM_EXTERN int +pam_sm_setcred(pam_handle_t *pamh __unused, int flags __unused, + int argc __unused, const char *argv[] __unused) +{ + + return (PAM_SUCCESS); +} + +/* + * account management + */ +PAM_EXTERN int +pam_sm_acct_mgmt(pam_handle_t *pamh, int flags __unused, + int argc __unused, const char *argv[] __unused) +{ + struct addrinfo hints, *res; + struct passwd *pwd; + struct timeval tp; + login_cap_t *lc; + time_t warntime; + int retval; + const char *user; + const void *rhost, *tty; + char rhostip[MAXHOSTNAMELEN] = ""; + + retval = pam_get_user(pamh, &user, NULL); + if (retval != PAM_SUCCESS) + return (retval); + + if (user == NULL || (pwd = getpwnam(user)) == NULL) + return (PAM_SERVICE_ERR); + + PAM_LOG("Got user: %s", user); + + retval = pam_get_item(pamh, PAM_RHOST, &rhost); + if (retval != PAM_SUCCESS) + return (retval); + + retval = pam_get_item(pamh, PAM_TTY, &tty); + if (retval != PAM_SUCCESS) + return (retval); + + if (*pwd->pw_passwd == '\0' && + (flags & PAM_DISALLOW_NULL_AUTHTOK) != 0) + return (PAM_NEW_AUTHTOK_REQD); + + if (strncmp(pwd->pw_passwd, LOCKED_PREFIX, LOCKED_PREFIX_LEN) == 0) + return (PAM_AUTH_ERR); + + lc = login_getpwclass(pwd); + if (lc == NULL) { + PAM_LOG("Unable to get login class for user %s", user); + return (PAM_SERVICE_ERR); + } + + PAM_LOG("Got login_cap"); + + if (pwd->pw_change || pwd->pw_expire) + gettimeofday(&tp, NULL); + + /* + * Check pw_expire before pw_change - no point in letting the + * user change the password on an expired account. + */ + + if (pwd->pw_expire) { + warntime = login_getcaptime(lc, "warnexpire", + DEFAULT_WARN, DEFAULT_WARN); + if (tp.tv_sec >= pwd->pw_expire) { + login_close(lc); + return (PAM_ACCT_EXPIRED); + } else if (pwd->pw_expire - tp.tv_sec < warntime && + (flags & PAM_SILENT) == 0) { + pam_error(pamh, "Warning: your account expires on %s", + ctime(&pwd->pw_expire)); + } + } + + retval = PAM_SUCCESS; + if (pwd->pw_change) { + warntime = login_getcaptime(lc, "warnpassword", + DEFAULT_WARN, DEFAULT_WARN); + if (tp.tv_sec >= pwd->pw_change) { + retval = PAM_NEW_AUTHTOK_REQD; + } else if (pwd->pw_change - tp.tv_sec < warntime && + (flags & PAM_SILENT) == 0) { + pam_error(pamh, "Warning: your password expires on %s", + ctime(&pwd->pw_change)); + } + } + + /* + * From here on, we must leave retval untouched (unless we + * know we're going to fail), because we need to remember + * whether we're supposed to return PAM_SUCCESS or + * PAM_NEW_AUTHTOK_REQD. + */ + + if (rhost && *(const char *)rhost != '\0') { + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_UNSPEC; + if (getaddrinfo(rhost, NULL, &hints, &res) == 0) { + getnameinfo(res->ai_addr, res->ai_addrlen, + rhostip, sizeof(rhostip), NULL, 0, + NI_NUMERICHOST); + } + if (res != NULL) + freeaddrinfo(res); + } + + /* + * Check host / tty / time-of-day restrictions + */ + + if (!auth_hostok(lc, rhost, rhostip) || + !auth_ttyok(lc, tty) || + !auth_timeok(lc, time(NULL))) + retval = PAM_AUTH_ERR; + + login_close(lc); + + return (retval); +} + +/* + * password management + * + * standard Unix and NIS password changing + */ +PAM_EXTERN int +pam_sm_chauthtok(pam_handle_t *pamh, int flags, + int argc __unused, const char *argv[] __unused) +{ +#ifdef YP + struct ypclnt *ypclnt; + const void *yp_domain, *yp_server; +#endif + char salt[SALTSIZE + 1]; + login_cap_t *lc; + struct passwd *pwd, *old_pwd; + const char *user, *old_pass, *new_pass; + char *encrypted; + time_t passwordtime; + int pfd, tfd, retval; + + if (openpam_get_option(pamh, PAM_OPT_AUTH_AS_SELF)) + user = getlogin(); + else { + retval = pam_get_user(pamh, &user, NULL); + if (retval != PAM_SUCCESS) + return (retval); + } + pwd = getpwnam(user); + + if (pwd == NULL) + return (PAM_AUTHTOK_RECOVERY_ERR); + + PAM_LOG("Got user: %s", user); + + if (flags & PAM_PRELIM_CHECK) { + + PAM_LOG("PRELIM round"); + + if (getuid() == 0 && + (pwd->pw_fields & _PWF_SOURCE) == _PWF_FILES) + /* root doesn't need the old password */ + return (pam_set_item(pamh, PAM_OLDAUTHTOK, "")); +#ifdef YP + if (getuid() == 0 && + (pwd->pw_fields & _PWF_SOURCE) == _PWF_NIS) { + + yp_domain = yp_server = NULL; + (void)pam_get_data(pamh, "yp_domain", &yp_domain); + (void)pam_get_data(pamh, "yp_server", &yp_server); + + ypclnt = ypclnt_new(yp_domain, "passwd.byname", yp_server); + if (ypclnt == NULL) + return (PAM_BUF_ERR); + + if (ypclnt_connect(ypclnt) == -1) { + ypclnt_free(ypclnt); + return (PAM_SERVICE_ERR); + } + + retval = ypclnt_havepasswdd(ypclnt); + ypclnt_free(ypclnt); + if (retval == 1) + return (pam_set_item(pamh, PAM_OLDAUTHTOK, "")); + else if (retval == -1) + return (PAM_SERVICE_ERR); + } +#endif + if (pwd->pw_passwd[0] == '\0' + && openpam_get_option(pamh, PAM_OPT_NULLOK)) { + /* + * No password case. XXX Are we giving too much away + * by not prompting for a password? + * XXX check PAM_DISALLOW_NULL_AUTHTOK + */ + old_pass = ""; + retval = PAM_SUCCESS; + } else { + retval = pam_get_authtok(pamh, + PAM_OLDAUTHTOK, &old_pass, NULL); + if (retval != PAM_SUCCESS) + return (retval); + } + PAM_LOG("Got old password"); + /* always encrypt first */ + encrypted = crypt(old_pass, pwd->pw_passwd); + if (old_pass[0] == '\0' && + !openpam_get_option(pamh, PAM_OPT_NULLOK)) + return (PAM_PERM_DENIED); + if (strcmp(encrypted, pwd->pw_passwd) != 0) + return (PAM_PERM_DENIED); + } + else if (flags & PAM_UPDATE_AUTHTOK) { + PAM_LOG("UPDATE round"); + + retval = pam_get_authtok(pamh, + PAM_OLDAUTHTOK, &old_pass, NULL); + if (retval != PAM_SUCCESS) + return (retval); + PAM_LOG("Got old password"); + + /* get new password */ + for (;;) { + retval = pam_get_authtok(pamh, + PAM_AUTHTOK, &new_pass, NULL); + if (retval != PAM_TRY_AGAIN) + break; + pam_error(pamh, "Mismatch; try again, EOF to quit."); + } + PAM_LOG("Got new password"); + if (retval != PAM_SUCCESS) { + PAM_VERBOSE_ERROR("Unable to get new password"); + return (retval); + } + + if (getuid() != 0 && new_pass[0] == '\0' && + !openpam_get_option(pamh, PAM_OPT_NULLOK)) + return (PAM_PERM_DENIED); + + if ((old_pwd = pw_dup(pwd)) == NULL) + return (PAM_BUF_ERR); + + lc = login_getclass(pwd->pw_class); + if (login_setcryptfmt(lc, password_hash, NULL) == NULL) + openpam_log(PAM_LOG_ERROR, + "can't set password cipher, relying on default"); + + /* set password expiry date */ + pwd->pw_change = 0; + passwordtime = login_getcaptime(lc, "passwordtime", 0, 0); + if (passwordtime > 0) + pwd->pw_change = time(NULL) + passwordtime; + + login_close(lc); + makesalt(salt); + pwd->pw_passwd = crypt(new_pass, salt); +#ifdef YP + switch (old_pwd->pw_fields & _PWF_SOURCE) { + case _PWF_FILES: +#endif + retval = PAM_SERVICE_ERR; + if (pw_init(NULL, NULL)) + openpam_log(PAM_LOG_ERROR, "pw_init() failed"); + else if ((pfd = pw_lock()) == -1) + openpam_log(PAM_LOG_ERROR, "pw_lock() failed"); + else if ((tfd = pw_tmp(-1)) == -1) + openpam_log(PAM_LOG_ERROR, "pw_tmp() failed"); + else if (pw_copy(pfd, tfd, pwd, old_pwd) == -1) + openpam_log(PAM_LOG_ERROR, "pw_copy() failed"); + else if (pw_mkdb(pwd->pw_name) == -1) + openpam_log(PAM_LOG_ERROR, "pw_mkdb() failed"); + else + retval = PAM_SUCCESS; + pw_fini(); +#ifdef YP + break; + case _PWF_NIS: + yp_domain = yp_server = NULL; + (void)pam_get_data(pamh, "yp_domain", &yp_domain); + (void)pam_get_data(pamh, "yp_server", &yp_server); + ypclnt = ypclnt_new(yp_domain, + "passwd.byname", yp_server); + if (ypclnt == NULL) { + retval = PAM_BUF_ERR; + } else if (ypclnt_connect(ypclnt) == -1 || + ypclnt_passwd(ypclnt, pwd, old_pass) == -1) { + openpam_log(PAM_LOG_ERROR, "%s", ypclnt->error); + retval = PAM_SERVICE_ERR; + } else { + retval = PAM_SUCCESS; + } + ypclnt_free(ypclnt); + break; + default: + openpam_log(PAM_LOG_ERROR, "unsupported source 0x%x", + pwd->pw_fields & _PWF_SOURCE); + retval = PAM_SERVICE_ERR; + } +#endif + free(old_pwd); + } + else { + /* Very bad juju */ + retval = PAM_ABORT; + PAM_LOG("Illegal 'flags'"); + } + + return (retval); +} + +/* Mostly stolen from passwd(1)'s local_passwd.c - markm */ + +static unsigned char itoa64[] = /* 0 ... 63 => ascii - 64 */ + "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; + +static void +to64(char *s, long v, int n) +{ + while (--n >= 0) { + *s++ = itoa64[v&0x3f]; + v >>= 6; + } +} + +/* Salt suitable for traditional DES and MD5 */ +static void +makesalt(char salt[SALTSIZE + 1]) +{ + int i; + + /* These are not really random numbers, they are just + * numbers that change to thwart construction of a + * dictionary. + */ + for (i = 0; i < SALTSIZE; i += 4) + to64(&salt[i], arc4random(), 4); + salt[SALTSIZE] = '\0'; +} + +PAM_MODULE_ENTRY("pam_unix"); |