/*
- * Main coding by Elliot Lee <sopwith@redhat.com>, Red Hat Software.
+ * Main coding by Elliot Lee <sopwith@redhat.com>, Red Hat Software.
* Copyright (C) 1996.
* Copyright (c) Jan Rêkorajski, 1999.
+ * Copyright (c) Red Hat, Inc., 2007, 2008.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#define _BSD_SOURCE
-#define __USE_SVID
-
-#ifdef linux
-#define _GNU_SOURCE
-#include <features.h>
-#endif
+#include "config.h"
#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
#include <unistd.h>
#include <errno.h>
+#include <sys/types.h>
#include <pwd.h>
#include <syslog.h>
#include <shadow.h>
#include <fcntl.h>
#include <ctype.h>
#include <sys/time.h>
-#include <sys/types.h>
#include <sys/stat.h>
#include <rpc/rpc.h>
#include <rpcsvc/yp_prot.h>
#include <rpcsvc/ypclnt.h>
-#ifdef USE_CRACKLIB
-#include <crack.h>
+#include <signal.h>
+#include <errno.h>
+#include <sys/wait.h>
+#ifdef WITH_SELINUX
+static int selinux_enabled=-1;
+#include <selinux/selinux.h>
+#define SELINUX_ENABLED (selinux_enabled!=-1 ? selinux_enabled : (selinux_enabled=is_selinux_enabled()>0))
#endif
#include <security/_pam_macros.h>
#define PAM_SM_PASSWORD
#include <security/pam_modules.h>
-
-#ifndef LINUX_PAM
-#include <security/pam_appl.h>
-#endif /* LINUX_PAM */
+#include <security/pam_ext.h>
+#include <security/pam_modutil.h>
#include "yppasswd.h"
#include "md5.h"
#include "support.h"
+#include "passverify.h"
+#include "bigcrypt.h"
#if !((__GLIBC__ == 2) && (__GLIBC_MINOR__ >= 1))
extern int getrpcport(const char *host, unsigned long prognum,
unsigned long versnum, unsigned int proto);
#endif /* GNU libc 2.1 */
-/*
- * PAM framework looks for these entry-points to pass control to the
- * password changing module.
- */
-
-#ifdef NEED_LCKPWDF
-#include "./lckpwdf.-c"
-#endif
-
-extern char *bigcrypt(const char *key, const char *salt);
-
/*
How it works:
Gets in username (has to be done) from the calling program
Sets it.
*/
-/* passwd/salt conversion macros */
-
-#define ascii_to_bin(c) ((c)>='a'?(c-59):(c)>='A'?((c)-53):(c)-'.')
-#define bin_to_ascii(c) ((c)>=38?((c)-38+'a'):(c)>=12?((c)-12+'A'):(c)+'.')
-
/* data tokens */
#define _UNIX_OLD_AUTHTOK "-UN*X-OLD-PASS"
#define _UNIX_NEW_AUTHTOK "-UN*X-NEW-PASS"
#define MAX_PASSWD_TRIES 3
-#define PW_TMPFILE "/etc/npasswd"
-#define SH_TMPFILE "/etc/nshadow"
-#define CRACKLIB_DICTS "/usr/share/dict/cracklib_dict"
-#define OPW_TMPFILE "/etc/security/nopasswd"
-#define OLD_PASSWORDS_FILE "/etc/security/opasswd"
-/*
- * i64c - convert an integer to a radix 64 character
- */
-static int i64c(int i)
-{
- if (i < 0)
- return ('.');
- else if (i > 63)
- return ('z');
- if (i == 0)
- return ('.');
- if (i == 1)
- return ('/');
- if (i >= 2 && i <= 11)
- return ('0' - 2 + i);
- if (i >= 12 && i <= 37)
- return ('A' - 12 + i);
- if (i >= 38 && i <= 63)
- return ('a' - 38 + i);
- return ('\0');
-}
-
-static char *crypt_md5_wrapper(const char *pass_new)
-{
- /*
- * Code lifted from Marek Michalkiewicz's shadow suite. (CG)
- * removed use of static variables (AGM)
- */
-
- struct timeval tv;
- MD5_CTX ctx;
- unsigned char result[16];
- char *cp = (char *) result;
- unsigned char tmp[16];
- int i;
- char *x, *e = NULL;
-
- GoodMD5Init(&ctx);
- gettimeofday(&tv, (struct timezone *) 0);
- GoodMD5Update(&ctx, (void *) &tv, sizeof tv);
- i = getpid();
- GoodMD5Update(&ctx, (void *) &i, sizeof i);
- i = clock();
- GoodMD5Update(&ctx, (void *) &i, sizeof i);
- GoodMD5Update(&ctx, result, sizeof result);
- GoodMD5Final(tmp, &ctx);
- strcpy(cp, "$1$"); /* magic for the MD5 */
- cp += strlen(cp);
- for (i = 0; i < 8; i++)
- *cp++ = i64c(tmp[i] & 077);
- *cp = '\0';
-
- /* no longer need cleartext */
- e = Goodcrypt_md5(pass_new, (const char *) result);
- x = x_strdup(e); /* put e in malloc()ed memory */
- _pam_overwrite(e); /* clean up */
-
- return x;
-}
-
-static char *getNISserver(void)
+static char *getNISserver(pam_handle_t *pamh)
{
char *master;
char *domainname;
int port, err;
if ((err = yp_get_default_domain(&domainname)) != 0) {
- _log_err(LOG_WARNING, "can't get local yp domain: %s\n",
+ pam_syslog(pamh, LOG_WARNING, "can't get local yp domain: %s",
yperr_string(err));
return NULL;
}
if ((err = yp_master(domainname, "passwd.byname", &master)) != 0) {
- _log_err(LOG_WARNING, "can't find the master ypserver: %s\n",
+ pam_syslog(pamh, LOG_WARNING, "can't find the master ypserver: %s",
yperr_string(err));
return NULL;
}
port = getrpcport(master, YPPASSWDPROG, YPPASSWDPROC_UPDATE, IPPROTO_UDP);
if (port == 0) {
- _log_err(LOG_WARNING, "yppasswdd not running on NIS master host\n");
+ pam_syslog(pamh, LOG_WARNING,
+ "yppasswdd not running on NIS master host");
return NULL;
}
if (port >= IPPORT_RESERVED) {
- _log_err(LOG_WARNING, "yppasswd daemon running on illegal port.\n");
+ pam_syslog(pamh, LOG_WARNING,
+ "yppasswd daemon running on illegal port");
return NULL;
}
return master;
}
+#ifdef WITH_SELINUX
+
+static int _unix_run_update_binary(pam_handle_t *pamh, unsigned int ctrl, const char *user,
+ const char *fromwhat, const char *towhat, int remember)
+{
+ int retval, child, fds[2];
+ struct sigaction newsa, oldsa;
+
+ D(("called."));
+ /* create a pipe for the password */
+ if (pipe(fds) != 0) {
+ D(("could not make pipe"));
+ return PAM_AUTH_ERR;
+ }
+
+ if (off(UNIX_NOREAP, ctrl)) {
+ /*
+ * This code arranges that the demise of the child does not cause
+ * the application to receive a signal it is not expecting - which
+ * may kill the application or worse.
+ *
+ * The "noreap" module argument is provided so that the admin can
+ * override this behavior.
+ */
+ memset(&newsa, '\0', sizeof(newsa));
+ newsa.sa_handler = SIG_DFL;
+ sigaction(SIGCHLD, &newsa, &oldsa);
+ }
+
+ /* fork */
+ child = fork();
+ if (child == 0) {
+ int i=0;
+ struct rlimit rlim;
+ static char *envp[] = { NULL };
+ char *args[] = { NULL, NULL, NULL, NULL, NULL, NULL };
+ char buffer[16];
+
+ /* XXX - should really tidy up PAM here too */
+
+ /* reopen stdin as pipe */
+ dup2(fds[0], STDIN_FILENO);
+
+ if (getrlimit(RLIMIT_NOFILE,&rlim)==0) {
+ if (rlim.rlim_max >= MAX_FD_NO)
+ rlim.rlim_max = MAX_FD_NO;
+ for (i=0; i < (int)rlim.rlim_max; i++) {
+ if (i != STDIN_FILENO)
+ close(i);
+ }
+ }
+
+ if (SELINUX_ENABLED && geteuid() == 0) {
+ /* must set the real uid to 0 so the helper will not error
+ out if pam is called from setuid binary (su, sudo...) */
+ setuid(0);
+ }
+
+ /* exec binary helper */
+ args[0] = x_strdup(UPDATE_HELPER);
+ args[1] = x_strdup(user);
+ args[2] = x_strdup("update");
+ if (on(UNIX_SHADOW, ctrl))
+ args[3] = x_strdup("1");
+ else
+ args[3] = x_strdup("0");
+
+ snprintf(buffer, sizeof(buffer), "%d", remember);
+ args[4] = x_strdup(buffer);
+
+ execve(UPDATE_HELPER, args, envp);
+
+ /* should not get here: exit with error */
+ D(("helper binary is not available"));
+ exit(PAM_AUTHINFO_UNAVAIL);
+ } else if (child > 0) {
+ /* wait for child */
+ /* if the stored password is NULL */
+ int rc=0;
+ if (fromwhat)
+ pam_modutil_write(fds[1], fromwhat, strlen(fromwhat)+1);
+ else
+ pam_modutil_write(fds[1], "", 1);
+ if (towhat) {
+ pam_modutil_write(fds[1], towhat, strlen(towhat)+1);
+ }
+ else
+ pam_modutil_write(fds[1], "", 1);
+
+ close(fds[0]); /* close here to avoid possible SIGPIPE above */
+ close(fds[1]);
+ rc=waitpid(child, &retval, 0); /* wait for helper to complete */
+ if (rc<0) {
+ pam_syslog(pamh, LOG_ERR, "unix_update waitpid failed: %m");
+ retval = PAM_AUTH_ERR;
+ } else {
+ retval = WEXITSTATUS(retval);
+ }
+ } else {
+ D(("fork failed"));
+ close(fds[0]);
+ close(fds[1]);
+ retval = PAM_AUTH_ERR;
+ }
+
+ if (off(UNIX_NOREAP, ctrl)) {
+ sigaction(SIGCHLD, &oldsa, NULL); /* restore old signal handler */
+ }
+
+ return retval;
+}
+#endif
+
static int check_old_password(const char *forwho, const char *newpass)
{
static char buf[16384];
opwfile = fopen(OLD_PASSWORDS_FILE, "r");
if (opwfile == NULL)
- return PAM_AUTHTOK_ERR;
+ return PAM_ABORT;
while (fgets(buf, 16380, opwfile)) {
if (!strncmp(buf, forwho, strlen(forwho))) {
+ char *sptr;
buf[strlen(buf) - 1] = '\0';
- s_luser = strtok(buf, ":,");
- s_uid = strtok(NULL, ":,");
- s_npas = strtok(NULL, ":,");
- s_pas = strtok(NULL, ":,");
+ s_luser = strtok_r(buf, ":,", &sptr);
+ s_uid = strtok_r(NULL, ":,", &sptr);
+ s_npas = strtok_r(NULL, ":,", &sptr);
+ s_pas = strtok_r(NULL, ":,", &sptr);
while (s_pas != NULL) {
- if (!strcmp(Goodcrypt_md5(newpass, s_pas), s_pas)) {
+ char *md5pass = Goodcrypt_md5(newpass, s_pas);
+ if (!strcmp(md5pass, s_pas)) {
+ _pam_delete(md5pass);
retval = PAM_AUTHTOK_ERR;
break;
}
- s_pas = strtok(NULL, ":,");
+ s_pas = strtok_r(NULL, ":,", &sptr);
+ _pam_delete(md5pass);
}
break;
}
return retval;
}
-static int save_old_password(const char *forwho, const char *oldpass, int howmany)
-{
- static char buf[16384];
- static char nbuf[16384];
- char *s_luser, *s_uid, *s_npas, *s_pas, *pass;
- int retval = 0, npas;
- FILE *pwfile, *opwfile;
- int err = 0;
- int oldmask;
- int found = 0;
- struct passwd *pwd = NULL;
-
- if (howmany < 0)
- return retval;
-
- if (oldpass == NULL)
- return retval;
-
- oldmask = umask(077);
- pwfile = fopen(OPW_TMPFILE, "w");
- umask(oldmask);
- opwfile = fopen(OLD_PASSWORDS_FILE, "r");
- if (pwfile == NULL || opwfile == NULL)
- return PAM_AUTHTOK_ERR;
- chown(OPW_TMPFILE, 0, 0);
- chmod(OPW_TMPFILE, 0600);
-
- while (fgets(buf, 16380, opwfile)) {
- if (!strncmp(buf, forwho, strlen(forwho))) {
- buf[strlen(buf) - 1] = '\0';
- s_luser = strtok(buf, ":");
- s_uid = strtok(NULL, ":");
- s_npas = strtok(NULL, ":");
- s_pas = strtok(NULL, ":");
- npas = strtol(s_npas, NULL, 10) + 1;
- while (npas > howmany) {
- s_pas = strpbrk(s_pas, ",");
- if (s_pas != NULL)
- s_pas++;
- npas--;
- }
- pass = crypt_md5_wrapper(oldpass);
- if (s_pas == NULL)
- sprintf(nbuf, "%s:%s:%d:%s\n", s_luser, s_uid, npas, pass);
- else
- sprintf(nbuf, "%s:%s:%d:%s,%s\n", s_luser, s_uid, npas, s_pas, pass);
- if (fputs(nbuf, pwfile) < 0) {
- retval = PAM_AUTHTOK_ERR;
- err = 1;
- break;
- }
- found = 1;
- } else if (fputs(buf, pwfile) < 0) {
- retval = PAM_AUTHTOK_ERR;
- err = 1;
- break;
- }
- }
- fclose(opwfile);
- if (!found) {
- pwd = getpwnam(forwho);
- if (pwd == NULL) {
- retval = PAM_AUTHTOK_ERR;
- err = 1;
- } else {
- pass = crypt_md5_wrapper(oldpass);
- sprintf(nbuf, "%s:%d:1:%s\n", forwho, pwd->pw_uid, pass);
- if (fputs(nbuf, pwfile) < 0) {
- retval = PAM_AUTHTOK_ERR;
- err = 1;
- }
- }
- }
- if (fclose(pwfile)) {
- fprintf(stderr, "error writing entries to old passwords file: %s\n",
- strerror(errno));
- retval = PAM_AUTHTOK_ERR;
- err = 1;
- }
- if (!err)
- rename(OPW_TMPFILE, OLD_PASSWORDS_FILE);
- else
- unlink(OPW_TMPFILE);
-
- return retval;
-}
-
-static int _update_passwd(const char *forwho, char *towhat)
-{
- struct passwd *tmpent = NULL;
- FILE *pwfile, *opwfile;
- int retval = 0;
- int err = 0;
- int oldmask;
-
- oldmask = umask(077);
- pwfile = fopen(PW_TMPFILE, "w");
- umask(oldmask);
- opwfile = fopen("/etc/passwd", "r");
- if (pwfile == NULL || opwfile == NULL)
- return PAM_AUTHTOK_ERR;
- chown(PW_TMPFILE, 0, 0);
- chmod(PW_TMPFILE, 0644);
- tmpent = fgetpwent(opwfile);
- while (tmpent) {
- if (!strcmp(tmpent->pw_name, forwho)) {
- tmpent->pw_passwd = towhat;
- }
- if (putpwent(tmpent, pwfile)) {
- fprintf(stderr, "error writing entry to password file: %s\n",
- strerror(errno));
- err = 1;
- retval = PAM_AUTHTOK_ERR;
- break;
- }
- tmpent = fgetpwent(opwfile);
- }
- fclose(opwfile);
-
- if (fclose(pwfile)) {
- fprintf(stderr, "error writing entries to password file: %s\n",
- strerror(errno));
- retval = PAM_AUTHTOK_ERR;
- err = 1;
- }
- if (!err)
- rename(PW_TMPFILE, "/etc/passwd");
- else
- unlink(PW_TMPFILE);
-
- return retval;
-}
-
-static int _update_shadow(const char *forwho, char *towhat)
-{
- struct spwd *spwdent = NULL, *stmpent = NULL;
- FILE *pwfile, *opwfile;
- int retval = 0;
- int err = 0;
- int oldmask;
-
- spwdent = getspnam(forwho);
- if (spwdent == NULL)
- return PAM_USER_UNKNOWN;
- oldmask = umask(077);
- pwfile = fopen(SH_TMPFILE, "w");
- umask(oldmask);
- opwfile = fopen("/etc/shadow", "r");
- if (pwfile == NULL || opwfile == NULL)
- return PAM_AUTHTOK_ERR;
- chown(SH_TMPFILE, 0, 0);
- chmod(SH_TMPFILE, 0600);
- stmpent = fgetspent(opwfile);
- while (stmpent) {
- if (!strcmp(stmpent->sp_namp, forwho)) {
- stmpent->sp_pwdp = towhat;
- stmpent->sp_lstchg = time(NULL) / (60 * 60 * 24);
-
- D(("Set password %s for %s", stmpent->sp_pwdp, forwho));
- }
- if (putspent(stmpent, pwfile)) {
- fprintf(stderr, "error writing entry to shadow file: %s\n",
- strerror(errno));
- err = 1;
- retval = PAM_AUTHTOK_ERR;
- break;
- }
- stmpent = fgetspent(opwfile);
- }
- fclose(opwfile);
-
- if (fclose(pwfile)) {
- fprintf(stderr, "error writing entries to shadow file: %s\n",
- strerror(errno));
- retval = PAM_AUTHTOK_ERR;
- err = 1;
- }
- if (!err)
- rename(SH_TMPFILE, "/etc/shadow");
- else
- unlink(SH_TMPFILE);
-
- return retval;
-}
-
-static int _do_setpass(const char *forwho, char *fromwhat, char *towhat,
- unsigned int ctrl, int remember)
+static int _do_setpass(pam_handle_t* pamh, const char *forwho,
+ const char *fromwhat,
+ char *towhat, unsigned int ctrl, int remember)
{
struct passwd *pwd = NULL;
int retval = 0;
+ int unlocked = 0;
+ char *master = NULL;
D(("called"));
- setpwent();
pwd = getpwnam(forwho);
- endpwent();
- if (pwd == NULL)
- return PAM_AUTHTOK_ERR;
+ if (pwd == NULL) {
+ retval = PAM_AUTHTOK_ERR;
+ goto done;
+ }
- if (on(UNIX_NIS, ctrl)) {
+ if (on(UNIX_NIS, ctrl) && _unix_comesfromsource(pamh, forwho, 0, 1)) {
+ if ((master=getNISserver(pamh)) != NULL) {
struct timeval timeout;
struct yppasswd yppwd;
CLIENT *clnt;
- char *master;
int status;
- int err = 0;
+ enum clnt_stat err;
- /* Make RPC call to NIS server */
- if ((master = getNISserver()) == NULL)
- return PAM_TRY_AGAIN;
+ /* Unlock passwd file to avoid deadlock */
+ unlock_pwdf();
+ unlocked = 1;
/* Initialize password information */
yppwd.newpw.pw_passwd = pwd->pw_passwd;
yppwd.newpw.pw_gecos = pwd->pw_gecos;
yppwd.newpw.pw_dir = pwd->pw_dir;
yppwd.newpw.pw_shell = pwd->pw_shell;
- yppwd.oldpass = fromwhat;
+ yppwd.oldpass = fromwhat ? strdup (fromwhat) : strdup ("");
yppwd.newpw.pw_passwd = towhat;
D(("Set password %s for %s", yppwd.newpw.pw_passwd, forwho));
(xdrproc_t) xdr_int, (char *) &status,
timeout);
+ free (yppwd.oldpass);
+
if (err) {
- clnt_perrno(err);
- retval = PAM_TRY_AGAIN;
+ _make_remark(pamh, ctrl, PAM_TEXT_INFO,
+ clnt_sperrno(err));
} else if (status) {
- fprintf(stderr, "Error while changing NIS password.\n");
- retval = PAM_TRY_AGAIN;
+ D(("Error while changing NIS password.\n"));
}
- printf("\nThe password has%s been changed on %s.\n",
- (err || status) ? " not" : "", master);
+ D(("The password has%s been changed on %s.",
+ (err || status) ? " not" : "", master));
+ pam_syslog(pamh, LOG_NOTICE, "password%s changed for %s on %s",
+ (err || status) ? " not" : "", pwd->pw_name, master);
auth_destroy(clnt->cl_auth);
clnt_destroy(clnt);
- if ((err || status) != 0) {
+ if (err || status) {
+ _make_remark(pamh, ctrl, PAM_TEXT_INFO,
+ _("NIS password could not be changed."));
retval = PAM_TRY_AGAIN;
}
#ifdef DEBUG
sleep(5);
#endif
- return retval;
+ } else {
+ retval = PAM_TRY_AGAIN;
+ }
}
- /* first, save old password */
- if (save_old_password(forwho, fromwhat, remember)) {
- return PAM_AUTHTOK_ERR;
- }
- if (on(UNIX_SHADOW, ctrl) || (strcmp(pwd->pw_passwd, "x") == 0)) {
- retval = _update_shadow(forwho, towhat);
- if (retval == PAM_SUCCESS)
- retval = _update_passwd(forwho, "x");
- } else {
- retval = _update_passwd(forwho, towhat);
+
+ if (_unix_comesfromsource(pamh, forwho, 1, 0)) {
+ if(unlocked) {
+ if (lock_pwdf() != PAM_SUCCESS) {
+ return PAM_AUTHTOK_LOCK_BUSY;
+ }
+ }
+#ifdef WITH_SELINUX
+ if (unix_selinux_confined())
+ return _unix_run_update_binary(pamh, ctrl, forwho, fromwhat, towhat, remember);
+#endif
+ /* first, save old password */
+ if (save_old_password(pamh, forwho, fromwhat, remember)) {
+ retval = PAM_AUTHTOK_ERR;
+ goto done;
+ }
+ if (on(UNIX_SHADOW, ctrl) || is_pwd_shadowed(pwd)) {
+ retval = unix_update_shadow(pamh, forwho, towhat);
+ if (retval == PAM_SUCCESS)
+ if (!is_pwd_shadowed(pwd))
+ retval = unix_update_passwd(pamh, forwho, "x");
+ } else {
+ retval = unix_update_passwd(pamh, forwho, towhat);
+ }
}
+
+done:
+ unlock_pwdf();
+
return retval;
}
-static int _unix_verify_shadow(const char *user, unsigned int ctrl)
+static int _unix_verify_shadow(pam_handle_t *pamh, const char *user, unsigned int ctrl)
{
- struct passwd *pwd = NULL; /* Password and shadow password */
- struct spwd *spwdent = NULL; /* file entries for the user */
- time_t curdays;
- int retval = PAM_SUCCESS;
+ struct passwd *pwent = NULL; /* Password and shadow password */
+ struct spwd *spent = NULL; /* file entries for the user */
+ int daysleft;
+ int retval;
- /* UNIX passwords area */
- setpwent();
- pwd = getpwnam(user); /* Get password file entry... */
- endpwent();
- if (pwd == NULL)
- return PAM_AUTHINFO_UNAVAIL; /* We don't need to do the rest... */
-
- if (strcmp(pwd->pw_passwd, "x") == 0) {
- /* ...and shadow password file entry for this user, if shadowing
- is enabled */
- setspent();
- spwdent = getspnam(user);
- endspent();
-
- if (spwdent == NULL)
- return PAM_AUTHINFO_UNAVAIL;
- } else {
- if (strcmp(pwd->pw_passwd,"*NP*") == 0) { /* NIS+ */
- uid_t save_uid;
-
- save_uid = geteuid();
- seteuid (pwd->pw_uid);
- spwdent = getspnam( user );
- seteuid (save_uid);
-
- if (spwdent == NULL)
- return PAM_AUTHINFO_UNAVAIL;
- } else
- spwdent = NULL;
+ retval = get_account_info(pamh, user, &pwent, &spent);
+ if (retval == PAM_USER_UNKNOWN) {
+ return retval;
}
- if (spwdent != NULL) {
- /* We have the user's information, now let's check if their account
- has expired (60 * 60 * 24 = number of seconds in a day) */
-
- if (off(UNIX__IAMROOT, ctrl)) {
- /* Get the current number of days since 1970 */
- curdays = time(NULL) / (60 * 60 * 24);
- if ((curdays < (spwdent->sp_lstchg + spwdent->sp_min))
- && (spwdent->sp_min != -1))
- retval = PAM_AUTHTOK_ERR;
- else if ((curdays > (spwdent->sp_lstchg + spwdent->sp_max + spwdent->sp_inact))
- && (spwdent->sp_max != -1) && (spwdent->sp_inact != -1)
- && (spwdent->sp_lstchg != 0))
- /*
- * Their password change has been put off too long,
- */
- retval = PAM_ACCT_EXPIRED;
- else if ((curdays > spwdent->sp_expire) && (spwdent->sp_expire != -1)
- && (spwdent->sp_lstchg != 0))
- /*
- * OR their account has just plain expired
- */
- retval = PAM_ACCT_EXPIRED;
- }
+ if (retval == PAM_SUCCESS && spent == NULL)
+ return PAM_SUCCESS;
+
+ if (retval == PAM_UNIX_RUN_HELPER) {
+ retval = _unix_run_verify_binary(pamh, ctrl, user, &daysleft);
+ if (retval == PAM_AUTH_ERR || retval == PAM_USER_UNKNOWN)
+ return retval;
}
+ else if (retval == PAM_SUCCESS)
+ retval = check_shadow_expiry(pamh, spent, &daysleft);
+
+ if (on(UNIX__IAMROOT, ctrl) || retval == PAM_NEW_AUTHTOK_REQD)
+ return PAM_SUCCESS;
+
return retval;
}
,const char *pass_old
,const char *pass_new)
{
- const char *user;
- char *remark = NULL;
+ const void *user;
+ const char *remark = NULL;
int retval = PAM_SUCCESS;
D(("&new=%p, &old=%p", pass_old, pass_new));
if (pass_new == NULL || (pass_old && !strcmp(pass_old, pass_new))) {
if (on(UNIX_DEBUG, ctrl)) {
- _log_err(LOG_DEBUG, "bad authentication token");
+ pam_syslog(pamh, LOG_DEBUG, "bad authentication token");
}
_make_remark(pamh, ctrl, PAM_ERROR_MSG, pass_new == NULL ?
- "No password supplied" : "Password unchanged");
+ _("No password supplied") : _("Password unchanged"));
return PAM_AUTHTOK_ERR;
}
/*
* checking this would be the place - AGM
*/
- retval = pam_get_item(pamh, PAM_USER, (const void **) &user);
+ retval = pam_get_item(pamh, PAM_USER, &user);
if (retval != PAM_SUCCESS) {
if (on(UNIX_DEBUG, ctrl)) {
- _log_err(LOG_ERR, "Can not get username");
+ pam_syslog(pamh, LOG_ERR, "Can not get username");
return PAM_AUTHTOK_ERR;
}
}
if (off(UNIX__IAMROOT, ctrl)) {
-#ifdef USE_CRACKLIB
- remark = FascistCheck(pass_new, CRACKLIB_DICTS);
- D(("called cracklib [%s]", remark));
-#else
if (strlen(pass_new) < 6)
- remark = "You must choose a longer password";
- D(("lenth check [%s]", remark));
-#endif
- if (on(UNIX_REMEMBER_PASSWD, ctrl))
- if ((retval = check_old_password(user, pass_new)) != PAM_SUCCESS)
- remark = "Password has been already used. Choose another.";
+ remark = _("You must choose a longer password");
+ D(("length check [%s]", remark));
+ if (on(UNIX_REMEMBER_PASSWD, ctrl)) {
+ if ((retval = check_old_password(user, pass_new)) == PAM_AUTHTOK_ERR)
+ remark = _("Password has been already used. Choose another.");
+ if (retval == PAM_ABORT) {
+ pam_syslog(pamh, LOG_ERR, "can't open %s file to check old passwords",
+ OLD_PASSWORDS_FILE);
+ return retval;
+ }
+ }
}
if (remark) {
_make_remark(pamh, ctrl, PAM_ERROR_MSG, remark);
int argc, const char **argv)
{
unsigned int ctrl, lctrl;
- int retval, i;
+ int retval;
int remember = -1;
+ int rounds = -1;
/* <DO NOT free() THESE> */
const char *user;
- char *pass_old, *pass_new;
+ const void *pass_old, *pass_new;
/* </DO NOT free() THESE> */
D(("called."));
-#ifdef USE_LCKPWDF
- /* our current locking system requires that we lock the
- entire password database. This avoids both livelock
- and deadlock. */
- /* These values for the number of attempts and the sleep time
- are, of course, completely arbitrary.
- My reading of the PAM docs is that, once pam_chauthtok() has been
- called with PAM_UPDATE_AUTHTOK, we are obliged to take any
- reasonable steps to make sure the token is updated; so retrying
- for 1/10 sec. isn't overdoing it.
- The other possibility is to call lckpwdf() on the first
- pam_chauthtok() pass, and hold the lock until released in the
- second pass--but is this guaranteed to work? -SRL */
- i=0;
- while((retval = lckpwdf()) != 0 && i < 100) {
- usleep(1000);
- }
- if(retval != 0) {
- return PAM_AUTHTOK_LOCK_BUSY;
- }
-#endif
- ctrl = _set_ctrl(flags, &remember, argc, argv);
+ ctrl = _set_ctrl(pamh, flags, &remember, &rounds, argc, argv);
/*
* First get the name of a user
*/
- retval = pam_get_user(pamh, &user, "Username: ");
+ retval = pam_get_user(pamh, &user, NULL);
if (retval == PAM_SUCCESS) {
/*
* Various libraries at various times have had bugs related to
- * '+' or '-' as the first character of a user name. Don't take
- * any chances here. Require that the username starts with an
- * alphanumeric character.
+ * '+' or '-' as the first character of a user name. Don't
+ * allow them.
*/
- if (user == NULL || !isalnum(*user)) {
- _log_err(LOG_ERR, "bad username [%s]", user);
-#ifdef USE_LCKPWDF
- ulckpwdf();
-#endif
+ if (user == NULL || user[0] == '-' || user[0] == '+') {
+ pam_syslog(pamh, LOG_ERR, "bad username [%s]", user);
return PAM_USER_UNKNOWN;
}
if (retval == PAM_SUCCESS && on(UNIX_DEBUG, ctrl))
- _log_err(LOG_DEBUG, "username [%s] obtained", user);
+ pam_syslog(pamh, LOG_DEBUG, "username [%s] obtained",
+ user);
} else {
if (on(UNIX_DEBUG, ctrl))
- _log_err(LOG_DEBUG, "password - could not identify user");
-#ifdef USE_LCKPWDF
- ulckpwdf();
-#endif
+ pam_syslog(pamh, LOG_DEBUG,
+ "password - could not identify user");
return retval;
}
D(("Got username of %s", user));
+ /*
+ * Before we do anything else, check to make sure that the user's
+ * info is in one of the databases we can modify from this module,
+ * which currently is 'files' and 'nis'. We have to do this because
+ * getpwnam() doesn't tell you *where* the information it gives you
+ * came from, nor should it. That's our job.
+ */
+ if (_unix_comesfromsource(pamh, user, 1, on(UNIX_NIS, ctrl)) == 0) {
+ pam_syslog(pamh, LOG_DEBUG,
+ "user \"%s\" does not exist in /etc/passwd%s",
+ user, on(UNIX_NIS, ctrl) ? " or NIS" : "");
+ return PAM_USER_UNKNOWN;
+ } else {
+ struct passwd *pwd;
+ _unix_getpwnam(pamh, user, 1, 1, &pwd);
+ if (pwd == NULL) {
+ pam_syslog(pamh, LOG_DEBUG,
+ "user \"%s\" has corrupted passwd entry",
+ user);
+ return PAM_USER_UNKNOWN;
+ }
+ }
+
/*
* This is not an AUTH module!
*/
D(("prelim check"));
- if (_unix_blankpasswd(ctrl, user)) {
-#ifdef USE_LCKPWDF
- ulckpwdf();
-#endif
+ if (_unix_blankpasswd(pamh, ctrl, user)) {
return PAM_SUCCESS;
} else if (off(UNIX__IAMROOT, ctrl)) {
-
/* instruct user what is happening */
-#define greeting "Changing password for "
- Announce = (char *) malloc(sizeof(greeting) + strlen(user));
- if (Announce == NULL) {
- _log_err(LOG_CRIT, "password - out of memory");
-#ifdef USE_LCKPWDF
- ulckpwdf();
-#endif
+ if (asprintf(&Announce, _("Changing password for %s."),
+ user) < 0) {
+ pam_syslog(pamh, LOG_CRIT,
+ "password - out of memory");
return PAM_BUF_ERR;
}
- (void) strcpy(Announce, greeting);
- (void) strcpy(Announce + sizeof(greeting) - 1, user);
-#undef greeting
lctrl = ctrl;
set(UNIX__OLD_PASSWD, lctrl);
retval = _unix_read_password(pamh, lctrl
,Announce
- ,"(current) UNIX password: "
+ ,_("(current) UNIX password: ")
,NULL
,_UNIX_OLD_AUTHTOK
- ,(const char **) &pass_old);
+ ,&pass_old);
free(Announce);
if (retval != PAM_SUCCESS) {
- _log_err(LOG_NOTICE
- ,"password - (old) token not obtained");
-#ifdef USE_LCKPWDF
- ulckpwdf();
-#endif
+ pam_syslog(pamh, LOG_NOTICE,
+ "password - (old) token not obtained");
return retval;
}
/* verify that this is the password for this user */
if (retval != PAM_SUCCESS) {
D(("Authentication failed"));
pass_old = NULL;
-#ifdef USE_LCKPWDF
- ulckpwdf();
-#endif
return retval;
}
retval = pam_set_item(pamh, PAM_OLDAUTHTOK, (const void *) pass_old);
pass_old = NULL;
if (retval != PAM_SUCCESS) {
- _log_err(LOG_CRIT, "failed to set PAM_OLDAUTHTOK");
+ pam_syslog(pamh, LOG_CRIT,
+ "failed to set PAM_OLDAUTHTOK");
}
- retval = _unix_verify_shadow(user, ctrl);
+ retval = _unix_verify_shadow(pamh,user, ctrl);
if (retval == PAM_AUTHTOK_ERR) {
if (off(UNIX__IAMROOT, ctrl))
_make_remark(pamh, ctrl, PAM_ERROR_MSG,
- "You must wait longer to change your password");
+ _("You must wait longer to change your password"));
else
retval = PAM_SUCCESS;
}
if (off(UNIX_NOT_SET_PASS, ctrl)) {
retval = pam_get_item(pamh, PAM_OLDAUTHTOK
- ,(const void **) &pass_old);
+ ,&pass_old);
} else {
retval = pam_get_data(pamh, _UNIX_OLD_AUTHTOK
- ,(const void **) &pass_old);
+ ,&pass_old);
if (retval == PAM_NO_MODULE_DATA) {
retval = PAM_SUCCESS;
pass_old = NULL;
D(("pass_old [%s]", pass_old));
if (retval != PAM_SUCCESS) {
- _log_err(LOG_NOTICE, "user not authenticated");
-#ifdef USE_LCKPWDF
- ulckpwdf();
-#endif
- return retval;
- }
- retval = _unix_verify_shadow(user, ctrl);
- if (retval != PAM_SUCCESS) {
- _log_err(LOG_NOTICE, "user not authenticated 2");
-#ifdef USE_LCKPWDF
- ulckpwdf();
-#endif
+ pam_syslog(pamh, LOG_NOTICE, "user not authenticated");
return retval;
}
+
D(("get new password now"));
lctrl = ctrl;
retval = _unix_read_password(pamh, lctrl
,NULL
- ,"Enter new UNIX password: "
- ,"Retype new UNIX password: "
+ ,_("Enter new UNIX password: ")
+ ,_("Retype new UNIX password: ")
,_UNIX_NEW_AUTHTOK
- ,(const char **) &pass_new);
+ ,&pass_new);
if (retval != PAM_SUCCESS) {
if (on(UNIX_DEBUG, ctrl)) {
- _log_err(LOG_ALERT
- ,"password - new password not obtained");
+ pam_syslog(pamh, LOG_ALERT,
+ "password - new password not obtained");
}
pass_old = NULL; /* tidy up */
-#ifdef USE_LCKPWDF
- ulckpwdf();
-#endif
return retval;
}
D(("returned to _unix_chauthtok"));
* password is acceptable.
*/
- if (pass_new[0] == '\0') { /* "\0" password = NULL */
+ if (*(const char *)pass_new == '\0') { /* "\0" password = NULL */
pass_new = NULL;
}
retval = _pam_unix_approve_pass(pamh, ctrl, pass_old, pass_new);
+
+ if (retval != PAM_SUCCESS && off(UNIX_NOT_SET_PASS, ctrl)) {
+ pam_set_item(pamh, PAM_AUTHTOK, NULL);
+ }
}
if (retval != PAM_SUCCESS) {
- _log_err(LOG_NOTICE, "new password not acceptable");
- _pam_overwrite(pass_new);
- _pam_overwrite(pass_old);
+ pam_syslog(pamh, LOG_NOTICE,
+ "new password not acceptable");
pass_new = pass_old = NULL; /* tidy up */
-#ifdef USE_LCKPWDF
- ulckpwdf();
-#endif
return retval;
}
+ if (lock_pwdf() != PAM_SUCCESS) {
+ return PAM_AUTHTOK_LOCK_BUSY;
+ }
+
+ if (pass_old) {
+ retval = _unix_verify_password(pamh, user, pass_old, ctrl);
+ if (retval != PAM_SUCCESS) {
+ pam_syslog(pamh, LOG_NOTICE, "user password changed by another process");
+ unlock_pwdf();
+ return retval;
+ }
+ }
+
+ retval = _unix_verify_shadow(pamh, user, ctrl);
+ if (retval != PAM_SUCCESS) {
+ pam_syslog(pamh, LOG_NOTICE, "user shadow entry expired");
+ unlock_pwdf();
+ return retval;
+ }
+
+ retval = _pam_unix_approve_pass(pamh, ctrl, pass_old, pass_new);
+ if (retval != PAM_SUCCESS) {
+ pam_syslog(pamh, LOG_NOTICE,
+ "new password not acceptable 2");
+ pass_new = pass_old = NULL; /* tidy up */
+ unlock_pwdf();
+ return retval;
+ }
+
/*
* By reaching here we have approved the passwords and must now
* rebuild the password database file.
* First we encrypt the new password.
*/
- if (on(UNIX_MD5_PASS, ctrl)) {
- tpass = crypt_md5_wrapper(pass_new);
- } else {
- /*
- * Salt manipulation is stolen from Rick Faith's passwd
- * program. Sorry Rick :) -- alex
- */
-
- time_t tm;
- char salt[3];
-
- time(&tm);
- salt[0] = bin_to_ascii(tm & 0x3f);
- salt[1] = bin_to_ascii((tm >> 6) & 0x3f);
- salt[2] = '\0';
-
- if (off(UNIX_BIGCRYPT, ctrl) && strlen(pass_new) > 8) {
- /*
- * to avoid using the _extensions_ of the bigcrypt()
- * function we truncate the newly entered password
- */
- char *temp = malloc(9);
- char *e;
-
- if (temp == NULL) {
- _log_err(LOG_CRIT, "out of memory for password");
- _pam_overwrite(pass_new);
- _pam_overwrite(pass_old);
- pass_new = pass_old = NULL; /* tidy up */
-#ifdef USE_LCKPWDF
- ulckpwdf();
-#endif
- return PAM_BUF_ERR;
- }
- /* copy first 8 bytes of password */
- strncpy(temp, pass_new, 8);
- temp[8] = '\0';
-
- /* no longer need cleartext */
- e = bigcrypt(temp, salt);
- tpass = x_strdup(e);
-
- _pam_overwrite(e);
- _pam_delete(temp); /* tidy up */
- } else {
- char *e;
-
- /* no longer need cleartext */
- e = bigcrypt(pass_new, salt);
- tpass = x_strdup(e);
-
- _pam_overwrite(e);
- }
+ tpass = create_password_hash(pamh, pass_new, ctrl, rounds);
+ if (tpass == NULL) {
+ pam_syslog(pamh, LOG_CRIT,
+ "out of memory for password");
+ pass_new = pass_old = NULL; /* tidy up */
+ unlock_pwdf();
+ return PAM_BUF_ERR;
}
D(("password processed"));
/* update the password database(s) -- race conditions..? */
- retval = _do_setpass(user, pass_old, tpass, ctrl, remember);
- _pam_overwrite(pass_new);
- _pam_overwrite(pass_old);
+ retval = _do_setpass(pamh, user, pass_old, tpass, ctrl,
+ remember);
+ /* _do_setpass has called unlock_pwdf for us */
+
_pam_delete(tpass);
pass_old = pass_new = NULL;
} else { /* something has broken with the module */
- _log_err(LOG_ALERT, "password received unknown request");
+ pam_syslog(pamh, LOG_ALERT,
+ "password received unknown request");
retval = PAM_ABORT;
}
D(("retval was %d", retval));
-#ifdef USE_LCKPWDF
- ulckpwdf();
-#endif
return retval;
}
pam_sm_chauthtok,
};
#endif
-