summaryrefslogtreecommitdiff
path: root/contrib/libpam/modules/pam_unix/pam_unix_passwd.c
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/libpam/modules/pam_unix/pam_unix_passwd.c')
-rw-r--r--contrib/libpam/modules/pam_unix/pam_unix_passwd.c1615
1 files changed, 903 insertions, 712 deletions
diff --git a/contrib/libpam/modules/pam_unix/pam_unix_passwd.c b/contrib/libpam/modules/pam_unix/pam_unix_passwd.c
index de1345e852850..3fe8a27a6997d 100644
--- a/contrib/libpam/modules/pam_unix/pam_unix_passwd.c
+++ b/contrib/libpam/modules/pam_unix/pam_unix_passwd.c
@@ -1,8 +1,7 @@
-
-/* Main coding by Elliot Lee <sopwith@redhat.com>, Red Hat Software.
- Copyright (C) 1996. */
-
/*
+ * Main coding by Elliot Lee <sopwith@redhat.com>, Red Hat Software.
+ * Copyright (C) 1996.
+ * Copyright (c) Jan Rêkorajski, 1999.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -16,13 +15,13 @@
* 3. The name of the author may not be used to endorse or promote
* products derived from this software without specific prior
* written permission.
- *
+ *
* ALTERNATIVELY, this product may be distributed under the terms of
* the GNU Public License, in which case the provisions of the GPL are
* required INSTEAD OF the above restrictions. (This clause is
* necessary due to a potential bad interaction between the GPL and
* the restrictions contained in a BSD-style copyright.)
- *
+ *
* THIS SOFTWARE IS PROVIDED ``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
@@ -36,778 +35,970 @@
* OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-/*
- How it works:
- Gets in username (has to be done) from the calling program
- Does authentication of user (only if we are not running as root)
- Gets new password/checks for sanity
- Sets it.
- */
-
-#define PAM_SM_PASSWORD
-
-/* #define DEBUG 1 */
+#include <security/_pam_aconf.h>
#include <stdio.h>
-#include <sys/time.h>
-#define _BSD_SOURCE
-#define _SVID_SOURCE
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <malloc.h>
+#include <unistd.h>
#include <errno.h>
-#define __USE_BSD
-#define _BSD_SOURCE
-#include <pwd.h>
#include <sys/types.h>
+#include <pwd.h>
+#include <syslog.h>
+#include <shadow.h>
+#include <time.h> /* for time() */
+#include <fcntl.h>
+#include <ctype.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <rpc/rpc.h>
+#include <rpcsvc/yp_prot.h>
+#include <rpcsvc/ypclnt.h>
-/* why not defined? */
-void setpwent(void);
-void endpwent(void);
-int chmod(const char *path, mode_t mode);
-struct passwd *fgetpwent(FILE *stream);
-int putpwent(const struct passwd *p, FILE *stream);
-
-#include <unistd.h>
-char *crypt(const char *key, const char *salt);
#ifdef USE_CRACKLIB
#include <crack.h>
#endif
-#include <ctype.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <syslog.h>
-#include <string.h>
-#include <stdarg.h>
-#include <malloc.h>
+
#include <security/_pam_macros.h>
-#ifndef LINUX /* AGM added this as of 0.2 */
-#include <security/pam_appl.h>
-#endif /* ditto */
+/* indicate the following groups are defined */
+
+#define PAM_SM_PASSWORD
+
#include <security/pam_modules.h>
-#ifdef HAVE_SHADOW_H
-#include <shadow.h>
-#endif
-#define MAX_PASSWD_TRIES 3
-#define OLD_PASSWORD_PROMPT "Password: "
-#define NEW_PASSWORD_PROMPT "New password: "
-#define AGAIN_PASSWORD_PROMPT "New password (again): "
-#define PW_TMPFILE "/etc/npasswd"
-#define SH_TMPFILE "/etc/nshadow"
-#define CRACKLIB_DICTS "/usr/lib/cracklib_dict"
-
-/* Various flags for the getpass routine to send back in... */
-#define PPW_EXPIRED 1
-#define PPW_EXPIRING 2
-#define PPW_WILLEXPIRE 4
-#define PPW_NOSUCHUSER 8
-#define PPW_SHADOW 16
-#define PPW_TOOEARLY 32
-#define PPW_ERROR 64
-
-#ifndef DO_TEST
-#define STATIC static
-#else
-#define STATIC
-#endif
-/* Sets a password for the specified user to the specified password
- Returns flags PPW_*, or'd. */
-STATIC int _do_setpass(char *forwho, char *towhat, int flags);
-/* Gets a password for the specified user
- Returns flags PPW_*, or'd. */
-STATIC int _do_getpass(char *forwho, char **theirpass);
-/* Checks whether the password entered is same as listed in the database
- 'entered' should not be crypt()'d or anything (it should be as the
- user entered it...), 'listed' should be as it is listed in the
- password database file */
-STATIC int _do_checkpass(const char *entered, char *listed);
-
-/* sends a one-way message to the user, either error or info... */
-STATIC int conv_sendmsg(struct pam_conv *aconv, const char *message, int style);
-/* sends a message and returns the results of the conversation */
-STATIC int conv_getitem(struct pam_conv *aconv, char *message, int style,
- char **result);
-
-PAM_EXTERN
-int pam_sm_chauthtok( pam_handle_t *pamh,
- int flags,
- int argc,
- const char **argv);
-
-static void _pam_log(int err, const char *format, ...)
-{
- va_list args;
+#ifndef LINUX_PAM
+#include <security/pam_appl.h>
+#endif /* LINUX_PAM */
- va_start(args, format);
- openlog("PAM-unix_passwd", LOG_CONS|LOG_PID, LOG_AUTH);
- vsyslog(err, format, args);
- va_end(args);
- closelog();
-}
+#include "yppasswd.h"
+#include "md5.h"
+#include "support.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
-/* This is a hack, but until libc and glibc both include this function
- * by default (libc only includes it if nys is not being used, at the
- * moment, and glibc doesn't appear to have it at all) we need to have
- * it here, too. :-(
- *
- * This should not become an official part of PAM.
- *
- * BEGIN_HACK
-*/
+#include "./lckpwdf.-c"
+#endif
+
+extern char *bigcrypt(const char *key, const char *salt);
/*
- * lckpwdf.c -- prevent simultaneous updates of password files
- *
- * Before modifying any of the password files, call lckpwdf(). It may block
- * for up to 15 seconds trying to get the lock. Return value is 0 on success
- * or -1 on failure. When you are done, call ulckpwdf() to release the lock.
- * The lock is also released automatically when the process exits. Only one
- * process at a time may hold the lock.
- *
- * These functions are supposed to be conformant with AT&T SVID Issue 3.
- *
- * Written by Marek Michalkiewicz <marekm@i17linuxb.ists.pwr.wroc.pl>,
- * public domain.
+ How it works:
+ Gets in username (has to be done) from the calling program
+ Does authentication of user (only if we are not running as root)
+ Gets new password/checks for sanity
+ Sets it.
*/
-#include <fcntl.h>
-#include <signal.h>
+/* passwd/salt conversion macros */
-#define LOCKFILE "/etc/.pwd.lock"
-#define TIMEOUT 15
+#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)+'.')
-static int lockfd = -1;
+/* data tokens */
-static int
-set_close_on_exec(int fd)
+#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)
{
- int flags = fcntl(fd, F_GETFD, 0);
- if (flags == -1)
- return -1;
- flags |= FD_CLOEXEC;
- return fcntl(fd, F_SETFD, flags);
+ 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 int
-do_lock(int fd)
+static char *crypt_md5_wrapper(const char *pass_new)
{
- struct flock fl;
+ /*
+ * 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;
+}
- memset(&fl, 0, sizeof fl);
- fl.l_type = F_WRLCK;
- fl.l_whence = SEEK_SET;
- return fcntl(fd, F_SETLKW, &fl);
+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, pamh, "can't get local yp domain: %s\n",
+ yperr_string(err));
+ return NULL;
+ }
+ if ((err = yp_master(domainname, "passwd.byname", &master)) != 0) {
+ _log_err(LOG_WARNING, pamh, "can't find the master ypserver: %s\n",
+ yperr_string(err));
+ return NULL;
+ }
+ port = getrpcport(master, YPPASSWDPROG, YPPASSWDPROC_UPDATE, IPPROTO_UDP);
+ if (port == 0) {
+ _log_err(LOG_WARNING, pamh,
+ "yppasswdd not running on NIS master host\n");
+ return NULL;
+ }
+ if (port >= IPPORT_RESERVED) {
+ _log_err(LOG_WARNING, pamh,
+ "yppasswd daemon running on illegal port.\n");
+ return NULL;
+ }
+ return master;
}
-static void
-alarm_catch(int sig)
+static int check_old_password(const char *forwho, const char *newpass)
{
-/* does nothing, but fcntl F_SETLKW will fail with EINTR */
+ static char buf[16384];
+ char *s_luser, *s_uid, *s_npas, *s_pas;
+ int retval = PAM_SUCCESS;
+ FILE *opwfile;
+
+ opwfile = fopen(OLD_PASSWORDS_FILE, "r");
+ if (opwfile == NULL)
+ return PAM_AUTHTOK_ERR;
+
+ 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, ":,");
+ while (s_pas != NULL) {
+ if (!strcmp(Goodcrypt_md5(newpass, s_pas), s_pas)) {
+ retval = PAM_AUTHTOK_ERR;
+ break;
+ }
+ s_pas = strtok(NULL, ":,");
+ }
+ break;
+ }
+ }
+ fclose(opwfile);
+
+ return retval;
}
-static int lckpwdf(void)
+static int save_old_password(const char *forwho, const char *oldpass, int howmany)
{
- struct sigaction act, oldact;
- sigset_t set, oldset;
-
- if (lockfd != -1)
- return -1;
-
- lockfd = open(LOCKFILE, O_CREAT | O_WRONLY, 0600);
- if (lockfd == -1)
- return -1;
- if (set_close_on_exec(lockfd) == -1)
- goto cleanup_fd;
-
- memset(&act, 0, sizeof act);
- act.sa_handler = alarm_catch;
- act.sa_flags = 0;
- sigfillset(&act.sa_mask);
- if (sigaction(SIGALRM, &act, &oldact) == -1)
- goto cleanup_fd;
-
- sigemptyset(&set);
- sigaddset(&set, SIGALRM);
- if (sigprocmask(SIG_UNBLOCK, &set, &oldset) == -1)
- goto cleanup_sig;
-
- alarm(TIMEOUT);
- if (do_lock(lockfd) == -1)
- goto cleanup_alarm;
- alarm(0);
- sigprocmask(SIG_SETMASK, &oldset, NULL);
- sigaction(SIGALRM, &oldact, NULL);
- return 0;
-
-cleanup_alarm:
- alarm(0);
- sigprocmask(SIG_SETMASK, &oldset, NULL);
-cleanup_sig:
- sigaction(SIGALRM, &oldact, NULL);
-cleanup_fd:
- close(lockfd);
- lockfd = -1;
- return -1;
+ 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
-ulckpwdf(void)
+static int _update_passwd(const char *forwho, const char *towhat)
{
- unlink(LOCKFILE);
- if (lockfd == -1)
- return -1;
+ 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 (close(lockfd) == -1) {
- lockfd = -1;
- return -1;
+ if (fclose(pwfile)) {
+ fprintf(stderr, "error writing entries to password file: %s\n",
+ strerror(errno));
+ retval = PAM_AUTHTOK_ERR;
+ err = 1;
}
- lockfd = -1;
- return 0;
+ if (!err)
+ rename(PW_TMPFILE, "/etc/passwd");
+ else
+ unlink(PW_TMPFILE);
+
+ return retval;
}
-/* END_HACK */
-#endif
-#define PAM_FAIL_CHECK if(retval != PAM_SUCCESS) { 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;
+}
-PAM_EXTERN
-int pam_sm_chauthtok(pam_handle_t *pamh, int flags, int argc, const char **argv)
+static int _do_setpass(pam_handle_t* pamh, const char *forwho, char *fromwhat,
+ char *towhat, unsigned int ctrl, int remember)
{
- char *usrname, *curpass, *newpass; /* pointers to the username,
- current password, and new password */
-
- struct pam_conv *appconv; /* conversation with the app */
- struct pam_message msg, *pmsg; /* Misc for conversations */
- struct pam_response *resp;
-
- int retval=0; /* Gets the return values for all our function calls */
- unsigned int pflags=0; /* Holds the flags from our getpass & setpass
- functions */
-
- const char *cmiscptr; /* Utility variables, used for different purposes at
- different times */
- char *miscptr; /* Utility variables, used for different purposes at
- different times */
- unsigned int miscint;
- int fascist = 1; /* Be fascist by default. If compiled with cracklib,
- call cracklib. Otherwise just check length... */
-
- char argbuf[256],argval[256];
- int i;
-
-
- retval = pam_get_item(pamh,PAM_CONV,(const void **) &appconv);
- PAM_FAIL_CHECK;
-
- retval = pam_get_item(pamh,PAM_USER,(const void **) &usrname);
- PAM_FAIL_CHECK;
- if(flags & PAM_PRELIM_CHECK) {
- pflags = _do_getpass(usrname,&miscptr);
- if(pflags & PPW_NOSUCHUSER)
- return PAM_USER_UNKNOWN;
- else if(pflags & ~(PPW_SHADOW|PPW_EXPIRING|PPW_WILLEXPIRE))
- return PAM_AUTHTOK_ERR;
- else
- return PAM_SUCCESS;
- } /* else... */
+ struct passwd *pwd = NULL;
+ int retval = 0;
+
+ D(("called"));
+
+ setpwent();
+ pwd = getpwnam(forwho);
+ endpwent();
+
+ if (pwd == NULL)
+ return PAM_AUTHTOK_ERR;
+
+ if (on(UNIX_NIS, ctrl)) {
+ struct timeval timeout;
+ struct yppasswd yppwd;
+ CLIENT *clnt;
+ char *master;
+ int status;
+ int err = 0;
+
+ /* Make RPC call to NIS server */
+ if ((master = getNISserver(pamh)) == NULL)
+ return PAM_TRY_AGAIN;
+
+ /* Initialize password information */
+ yppwd.newpw.pw_passwd = pwd->pw_passwd;
+ yppwd.newpw.pw_name = pwd->pw_name;
+ yppwd.newpw.pw_uid = pwd->pw_uid;
+ yppwd.newpw.pw_gid = pwd->pw_gid;
+ 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.newpw.pw_passwd = towhat;
+
+ D(("Set password %s for %s", yppwd.newpw.pw_passwd, forwho));
+
+ /* The yppasswd.x file said `unix authentication required',
+ * so I added it. This is the only reason it is in here.
+ * My yppasswdd doesn't use it, but maybe some others out there
+ * do. --okir
+ */
+ clnt = clnt_create(master, YPPASSWDPROG, YPPASSWDVERS, "udp");
+ clnt->cl_auth = authunix_create_default();
+ memset((char *) &status, '\0', sizeof(status));
+ timeout.tv_sec = 25;
+ timeout.tv_usec = 0;
+ err = clnt_call(clnt, YPPASSWDPROC_UPDATE,
+ (xdrproc_t) xdr_yppasswd, (char *) &yppwd,
+ (xdrproc_t) xdr_int, (char *) &status,
+ timeout);
+
+ if (err) {
+ clnt_perrno(err);
+ retval = PAM_TRY_AGAIN;
+ } else if (status) {
+ fprintf(stderr, "Error while changing NIS password.\n");
+ retval = PAM_TRY_AGAIN;
+ }
+ printf("\nThe password has%s been changed on %s.\n",
+ (err || status) ? " not" : "", master);
+
+ auth_destroy(clnt->cl_auth);
+ clnt_destroy(clnt);
+ if ((err || status) != 0) {
+ retval = PAM_TRY_AGAIN;
+ }
#ifdef DEBUG
- fprintf(stderr,"Got username of %s\n",usrname);
+ sleep(5);
#endif
- if((usrname == NULL) || (strlen(usrname) < 1)) {
- /* The app is supposed to get us the username! */
- retval = PAM_USER_UNKNOWN;
- PAM_FAIL_CHECK;
- }
-
- for(i=0; i < argc; i++) {
- {
- char *tmp = x_strdup(argv[i]);
- strncpy(argbuf,strtok(tmp ,"="),255);
- strncpy(argval,strtok(NULL,"="),255);
- free(tmp);
- }
-
- /* For PC functionality use "strict" -- historically "fascist" */
- if(!strcmp(argbuf,"strict") || !strcmp(argbuf, "fascist"))
-
- if(!strcmp(argval,"true"))
- fascist = 1;
- else if(!strcmp(argval,"false"))
- fascist = 0;
- else
- return PAM_SERVICE_ERR;
- else {
- _pam_log(LOG_ERR,"Unknown option: %s",argbuf);
- return PAM_SERVICE_ERR;
- }
- }
-
-
- /* Now we have all the initial information we need from the app to
- set things up (we assume that getting the username succeeded...) */
- retval = pam_get_item(pamh,PAM_OLDAUTHTOK,(const void **) &curpass);
- PAM_FAIL_CHECK;
- if(getuid()) { /* If this is being run by root, we don't need to get their
- old password.
- note */
- /* If we haven't been given a password yet, prompt for one... */
- miscint=0;
- while((curpass == NULL) && (miscint++ < MAX_PASSWD_TRIES)) {
- pflags = _do_getpass(usrname,&miscptr);
- if(pflags & PPW_NOSUCHUSER)
- return PAM_USER_UNKNOWN; /* If the user that was passed in doesn't
- exist, say so and exit (app passes in
- username) */
-
- /* Get the password from the user... */
- pmsg = &msg;
-
- msg.msg_style = PAM_PROMPT_ECHO_OFF;
- msg.msg = OLD_PASSWORD_PROMPT;
- resp = NULL;
-
- retval = appconv->conv(1, (const struct pam_message **) &pmsg,
- &resp, appconv->appdata_ptr);
-
- PAM_FAIL_CHECK;
- curpass = resp->resp;
- free (resp);
- if(_do_checkpass(curpass?curpass:"",miscptr)) {
- int abortme = 0;
-
- /* password is incorrect... */
- if (curpass && curpass[0] == '\0') {
- /* ...and it was zero-length; user wishes to abort change */
- abortme = 1;
- }
- if (curpass) { free (curpass); }
- curpass = NULL;
- if (abortme) {
- conv_sendmsg(appconv,"Password change aborted.",PAM_ERROR_MSG);
- return PAM_AUTHTOK_ERR;
- }
- }
- }
-
- if(curpass == NULL)
- return PAM_AUTH_ERR; /* They didn't seem to enter the right password
- for three tries - error */
- pam_set_item(pamh, PAM_OLDAUTHTOK, (void *)curpass);
- } else {
-#ifdef DEBUG
- fprintf(stderr,"I am ROOT!\n");
-#endif
- pflags = _do_getpass(usrname,&curpass);
- if(curpass == NULL)
- curpass = x_strdup("");
- }
- if(pflags & PPW_TOOEARLY) {
- conv_sendmsg(appconv,"You must wait longer to change your password",
- PAM_ERROR_MSG);
- return PAM_AUTHTOK_ERR;
- }
- if(pflags & PPW_WILLEXPIRE)
- conv_sendmsg(appconv,"Your password is about to expire",PAM_TEXT_INFO);
- else if(pflags & PPW_EXPIRED)
- return PAM_ACCT_EXPIRED; /* If their account has expired, we can't auth
- them to change their password */
- if(!(pflags & PPW_EXPIRING) && (flags & PAM_CHANGE_EXPIRED_AUTHTOK))
- return PAM_SUCCESS;
- /* If we haven't been given a password yet, prompt for one... */
- miscint=0;
- pam_get_item(pamh,PAM_AUTHTOK,(const void **)&newpass);
- cmiscptr = NULL;
- while((newpass == NULL) && (miscint++ < MAX_PASSWD_TRIES)) {
-
- /* Get the password from the user... */
- pmsg = &msg;
-
- msg.msg_style = PAM_PROMPT_ECHO_OFF;
- msg.msg = NEW_PASSWORD_PROMPT;
- resp = NULL;
-
- retval = appconv->conv(1, (const struct pam_message **) &pmsg,
- &resp, appconv->appdata_ptr);
-
- PAM_FAIL_CHECK;
- newpass = resp->resp;
- free (resp);
+ return retval;
+ }
+ /* 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);
+ }
-#ifdef DEBUG
- if(newpass)
- fprintf(stderr,"Got password of %s\n",newpass);
- else
- fprintf(stderr,"No new password...\n");
-#endif
- if (newpass[0] == '\0') { free (newpass); newpass = (char *) 0; }
- cmiscptr=NULL;
- if(newpass) {
+ return retval;
+}
+
+static int _unix_verify_shadow(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;
+
+ /* 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;
+ }
+
+ 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;
+ }
+ }
+ return retval;
+}
+
+static int _pam_unix_approve_pass(pam_handle_t * pamh
+ ,unsigned int ctrl
+ ,const char *pass_old
+ ,const char *pass_new)
+{
+ const char *user;
+ const char *remark = NULL;
+ int retval = PAM_SUCCESS;
+
+ D(("&new=%p, &old=%p", pass_old, pass_new));
+ D(("new=[%s]", pass_new));
+ D(("old=[%s]", pass_old));
+
+ if (pass_new == NULL || (pass_old && !strcmp(pass_old, pass_new))) {
+ if (on(UNIX_DEBUG, ctrl)) {
+ _log_err(LOG_DEBUG, pamh, "bad authentication token");
+ }
+ _make_remark(pamh, ctrl, PAM_ERROR_MSG, pass_new == NULL ?
+ "No password supplied" : "Password unchanged");
+ return PAM_AUTHTOK_ERR;
+ }
+ /*
+ * if one wanted to hardwire authentication token strength
+ * checking this would be the place - AGM
+ */
+
+ retval = pam_get_item(pamh, PAM_USER, (const void **) &user);
+ if (retval != PAM_SUCCESS) {
+ if (on(UNIX_DEBUG, ctrl)) {
+ _log_err(LOG_ERR, pamh, "Can not get username");
+ return PAM_AUTHTOK_ERR;
+ }
+ }
+ if (off(UNIX__IAMROOT, ctrl)) {
#ifdef USE_CRACKLIB
- if(fascist && getuid())
- cmiscptr = FascistCheck(newpass,CRACKLIB_DICTS);
+ remark = FascistCheck(pass_new, CRACKLIB_DICTS);
+ D(("called cracklib [%s]", remark));
#else
- if(fascist && getuid() && strlen(newpass) < 6)
- cmiscptr = "You must choose a longer password";
+ if (strlen(pass_new) < 6)
+ remark = "You must choose a longer password";
+ D(("lenth check [%s]", remark));
#endif
- if(curpass)
- if(!strcmp(curpass,newpass)) {
- cmiscptr="You must choose a new password.";
- newpass=NULL;
+ if (on(UNIX_REMEMBER_PASSWD, ctrl))
+ if ((retval = check_old_password(user, pass_new)) != PAM_SUCCESS)
+ remark = "Password has been already used. Choose another.";
}
- } else {
- /* We want to abort the password change */
- conv_sendmsg(appconv,"Password change aborted",PAM_ERROR_MSG);
- return PAM_AUTHTOK_ERR;
- }
- if(!cmiscptr) {
- /* We ask them to enter their password again... */
- /* Get the password from the user... */
- pmsg = &msg;
-
- msg.msg_style = PAM_PROMPT_ECHO_OFF;
- msg.msg = AGAIN_PASSWORD_PROMPT;
- resp = NULL;
-
- retval = appconv->conv(1, (const struct pam_message **) &pmsg,
- &resp, appconv->appdata_ptr);
-
- PAM_FAIL_CHECK;
- miscptr = resp->resp;
- free (resp);
- if (miscptr[0] == '\0') { free (miscptr); miscptr = (char *) 0; }
- if(!miscptr) { /* Aborting password change... */
- conv_sendmsg(appconv,"Password change aborted",PAM_ERROR_MSG);
- return PAM_AUTHTOK_ERR;
- }
- if(!strcmp(newpass,miscptr)) {
- miscptr=NULL;
- break;
- }
- conv_sendmsg(appconv,"You must enter the same password twice.",
- PAM_ERROR_MSG);
- miscptr=NULL;
- newpass=NULL;
- }
- else {
- conv_sendmsg(appconv,cmiscptr,PAM_ERROR_MSG);
- newpass = NULL;
- }
- }
- if(cmiscptr) {
- /* conv_sendmsg(appconv,cmiscptr,PAM_ERROR_MSG); */
- return PAM_AUTHTOK_ERR;
- } else if(newpass == NULL)
- return PAM_AUTHTOK_ERR; /* They didn't seem to enter the right password
- for three tries - error */
-#ifdef DEBUG
- printf("Changing password for sure!\n");
-#endif
- /* From now on, we are bound and determined to get their password
- changed :-) */
- pam_set_item(pamh, PAM_AUTHTOK, (void *)newpass);
- retval = _do_setpass(usrname,newpass,pflags);
-#ifdef DEBUG
- fprintf(stderr,"retval was %d\n",retval);
-#endif
- if(retval & ~PPW_SHADOW) {
- conv_sendmsg(appconv,"Error: Password NOT changed",PAM_ERROR_MSG);
- return PAM_AUTHTOK_ERR;
- } else {
- conv_sendmsg(appconv,"Password changed",PAM_TEXT_INFO);
- return PAM_SUCCESS;
- }
+ if (remark) {
+ _make_remark(pamh, ctrl, PAM_ERROR_MSG, remark);
+ retval = PAM_AUTHTOK_ERR;
+ }
+ return retval;
}
-/* _do_checkpass() returns 0 on success, non-0 on failure */
-STATIC int _do_checkpass(const char *entered, char *listed)
+
+PAM_EXTERN int pam_sm_chauthtok(pam_handle_t * pamh, int flags,
+ int argc, const char **argv)
{
- char salt[3];
- if ((strlen(listed) == 0) &&(strlen(entered) == 0)) {
- /* no password in database; no password entered */
- return (0);
- }
- salt[0]=listed[0]; salt[1]=listed[1]; salt[2]='\0';
- return strcmp(crypt(entered,salt),listed);
-}
+ unsigned int ctrl, lctrl;
+ int retval, i;
+ int remember = -1;
-STATIC char mksalt(int seed) {
- int num = seed % 64;
-
- if (num < 26)
- return 'a' + num;
- else if (num < 52)
- return 'A' + (num - 26);
- else if (num < 62)
- return '0' + (num - 52);
- else if (num == 63)
- return '.';
- else
- return '/';
-}
+ /* <DO NOT free() THESE> */
+ const char *user;
+ char *pass_old, *pass_new;
+ /* </DO NOT free() THESE> */
-STATIC int _do_setpass(char *forwho, char *towhat,int flags)
-{
- struct passwd *pwd=NULL, *tmpent=NULL;
- FILE *pwfile,*opwfile;
- char thesalt[3];
- int retval=0;
- struct timeval time1;
- int err = 0;
-#ifdef HAVE_SHADOW_H
- struct spwd *spwdent=NULL, *stmpent=NULL;
+ 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
- if(flags & PPW_SHADOW) { retval |= PPW_SHADOW; }
- gettimeofday(&time1, NULL);
- srand(time1.tv_usec);
- thesalt[0]=mksalt(rand());
- thesalt[1]=mksalt(rand());
- thesalt[2]='\0';
-
- /* lock the entire password subsystem */
+ ctrl = _set_ctrl(pamh, flags, &remember, argc, argv);
+
+ /*
+ * First get the name of a user
+ */
+ retval = pam_get_user(pamh, &user, "Username: ");
+ 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.
+ */
+ if (user == NULL || !isalnum(*user)) {
+ _log_err(LOG_ERR, pamh, "bad username [%s]", user);
#ifdef USE_LCKPWDF
- lckpwdf();
+ ulckpwdf();
#endif
- setpwent();
- pwd = getpwnam(forwho);
-#ifdef DEBUG
- printf("Got %p, for %s (salt %s)\n",pwd,
- forwho,thesalt);
+ return PAM_USER_UNKNOWN;
+ }
+ if (retval == PAM_SUCCESS && on(UNIX_DEBUG, ctrl))
+ _log_err(LOG_DEBUG, pamh, "username [%s] obtained",
+ user);
+ } else {
+ if (on(UNIX_DEBUG, ctrl))
+ _log_err(LOG_DEBUG, pamh,
+ "password - could not identify user");
+#ifdef USE_LCKPWDF
+ ulckpwdf();
#endif
- if(pwd == NULL)
- return PPW_NOSUCHUSER;
- endpwent();
-
-#ifdef HAVE_SHADOW_H
- if(flags & PPW_SHADOW) {
- spwdent = getspnam(forwho);
- if(spwdent == NULL)
- return PPW_NOSUCHUSER;
- spwdent->sp_pwdp = towhat;
- spwdent->sp_lstchg = time(NULL)/(60*60*24);
- pwfile = fopen(SH_TMPFILE,"w");
- opwfile = fopen("/etc/shadow","r");
- if(pwfile == NULL || opwfile == NULL)
- return PPW_ERROR;
- chown(SH_TMPFILE,0,0);
- chmod(SH_TMPFILE,0600);
- stmpent=fgetspent(opwfile);
- while(stmpent) {
- if(!strcmp(stmpent->sp_namp,forwho)) {
- stmpent->sp_pwdp = crypt(towhat,thesalt);
- stmpent->sp_lstchg = time(NULL)/(60*60*24);
-#ifdef DEBUG
- fprintf(stderr,"Set password %s for %s\n",stmpent->sp_pwdp,
- forwho);
+ return retval;
+ }
+
+ D(("Got username of %s", user));
+
+ /*
+ * This is not an AUTH module!
+ */
+ if (on(UNIX__NONULL, ctrl))
+ set(UNIX__NULLOK, ctrl);
+
+ if (on(UNIX__PRELIM, ctrl)) {
+ /*
+ * obtain and verify the current password (OLDAUTHTOK) for
+ * the user.
+ */
+ char *Announce;
+
+ D(("prelim check"));
+
+ if (_unix_blankpasswd(ctrl, user)) {
+#ifdef USE_LCKPWDF
+ ulckpwdf();
#endif
- }
- if (putspent(stmpent,pwfile)) {
- fprintf(stderr, "error writing entry to shadow file: %s\n",
- strerror(errno));
- err = 1;
- retval = PPW_ERROR;
- break;
- }
- stmpent=fgetspent(opwfile);
- }
- fclose(opwfile);
-
- if (fclose(pwfile)) {
- fprintf(stderr, "error writing entries to shadow file: %s\n",
- strerror(errno));
- retval = PPW_ERROR;
- err = 1;
- }
-
- if (!err)
- rename(SH_TMPFILE,"/etc/shadow");
- else
- unlink(SH_TMPFILE);
- } else {
- pwd->pw_passwd = towhat;
- pwfile = fopen(PW_TMPFILE,"w");
- opwfile = fopen("/etc/passwd","r");
- if(pwfile == NULL || opwfile == NULL)
- return PPW_ERROR;
- chown(PW_TMPFILE,0,0);
- chmod(PW_TMPFILE,0644);
- tmpent=fgetpwent(opwfile);
- while(tmpent) {
- if(!strcmp(tmpent->pw_name,forwho)) {
- tmpent->pw_passwd = crypt(towhat,thesalt);
- }
- if (putpwent(tmpent,pwfile)) {
- fprintf(stderr, "error writing entry to password file: %s\n",
- strerror(errno));
- err = 1;
- retval = PPW_ERROR;
- break;
- }
- tmpent=fgetpwent(opwfile);
- }
- fclose(opwfile);
-
- if (fclose(pwfile)) {
- fprintf(stderr, "error writing entries to password file: %s\n",
- strerror(errno));
- retval = PPW_ERROR;
- err = 1;
- }
-
- if (!err)
- rename(PW_TMPFILE,"/etc/passwd");
- else
- unlink(PW_TMPFILE);
- }
-#else
- pwd->pw_passwd = towhat;
- pwfile = fopen(PW_TMPFILE,"w");
- opwfile = fopen("/etc/passwd","r");
- if(pwfile == NULL || opwfile == NULL)
- return PPW_ERROR;
- chown(PW_TMPFILE,0,0);
- chmod(PW_TMPFILE,0644);
- tmpent=fgetpwent(opwfile);
- while(tmpent) {
- if(!strcmp(tmpent->pw_name,forwho)) {
- tmpent->pw_passwd = crypt(towhat,thesalt);
- }
- if (putpwent(tmpent,pwfile)) {
- fprintf(stderr, "error writing entry to shadow file: %s\n",
- strerror(errno));
- err = 1;
- retval = PPW_ERROR;
- break;
- }
- tmpent=fgetpwent(opwfile);
- }
- fclose(opwfile);
-
- if (fclose(pwfile)) {
- fprintf(stderr, "error writing entries to password file: %s\n",
- strerror(errno));
- retval = PPW_ERROR;
- err = 1;
- }
-
- if (!err)
- rename(PW_TMPFILE,"/etc/passwd");
- else
- unlink(PW_TMPFILE);
+ 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, pamh,
+ "password - out of memory");
+#ifdef USE_LCKPWDF
+ ulckpwdf();
#endif
- /* unlock the entire password subsystem */
+ 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: "
+ ,NULL
+ ,_UNIX_OLD_AUTHTOK
+ ,(const char **) &pass_old);
+ free(Announce);
+
+ if (retval != PAM_SUCCESS) {
+ _log_err(LOG_NOTICE, pamh
+ ,"password - (old) token not obtained");
#ifdef USE_LCKPWDF
- ulckpwdf();
+ ulckpwdf();
#endif
- return retval;
-}
-
-STATIC int _do_getpass(char *forwho, char **theirpass)
-{
- struct passwd *pwd=NULL; /* Password and shadow password */
-#ifdef HAVE_SHADOW_H
- struct spwd *spwdent=NULL; /* file entries for the user */
- time_t curdays;
+ return retval;
+ }
+ /* verify that this is the password for this user */
+
+ retval = _unix_verify_password(pamh, user, pass_old, ctrl);
+ } else {
+ D(("process run by root so do nothing this time around"));
+ pass_old = NULL;
+ retval = PAM_SUCCESS; /* root doesn't have too */
+ }
+
+ if (retval != PAM_SUCCESS) {
+ D(("Authentication failed"));
+ pass_old = NULL;
+#ifdef USE_LCKPWDF
+ ulckpwdf();
#endif
- int retval=0;
- /* UNIX passwords area */
- setpwent();
- pwd = getpwnam(forwho); /* Get password file entry... */
- endpwent();
- if(pwd == NULL)
- return PPW_NOSUCHUSER; /* We don't need to do the rest... */
-#ifdef HAVE_SHADOW_H
- if(!strcmp(pwd->pw_passwd,"x")) {
- /* ...and shadow password file entry for this user, if shadowing
- is enabled */
- retval |= PPW_SHADOW;
- setspent();
- spwdent = getspnam(forwho);
- endspent();
- if(spwdent == NULL)
- return PPW_NOSUCHUSER;
- *theirpass = x_strdup(spwdent->sp_pwdp);
-
- /* We have the user's information, now let's check if their account
- has expired (60 * 60 * 24 = number of seconds in a day) */
-
- /* 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 |= PPW_TOOEARLY;
- else if((curdays
- > (spwdent->sp_lstchg + spwdent->sp_max + spwdent->sp_inact))
- && (spwdent->sp_max != -1) && (spwdent->sp_inact != -1))
- /* Their password change has been put off too long,
- OR their account has just plain expired */
- retval |= PPW_EXPIRED;
- else if((curdays > (spwdent->sp_lstchg + spwdent->sp_max))
- && (spwdent->sp_max != -1))
- /* Their passwd needs to be changed */
- retval |= PPW_EXPIRING;
- else if((curdays > (spwdent->sp_lstchg
- + spwdent->sp_max - spwdent->sp_warn))
- && (spwdent->sp_max != -1) && (spwdent->sp_warn != -1))
- retval |= PPW_WILLEXPIRE;
-/* if(spwdent->sp_lstchg < 0)
- retval &= ~(PPW_WILLEXPIRE | PPW_EXPIRING | PPW_EXPIRED);
- if(spwdent->sp_max < 0)
- retval &= ~(PPW_EXPIRING | PPW_EXPIRED); */
- } else {
- *theirpass = (char *)x_strdup(pwd->pw_passwd);
- }
-
-#else
- *theirpass = (char *) x_strdup(pwd->pw_passwd);
+ return retval;
+ }
+ retval = pam_set_item(pamh, PAM_OLDAUTHTOK, (const void *) pass_old);
+ pass_old = NULL;
+ if (retval != PAM_SUCCESS) {
+ _log_err(LOG_CRIT, pamh,
+ "failed to set PAM_OLDAUTHTOK");
+ }
+ retval = _unix_verify_shadow(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");
+ else
+ retval = PAM_SUCCESS;
+ }
+ } else if (on(UNIX__UPDATE, ctrl)) {
+ /*
+ * tpass is used below to store the _pam_md() return; it
+ * should be _pam_delete()'d.
+ */
+
+ char *tpass = NULL;
+ int retry = 0;
+
+ /*
+ * obtain the proposed password
+ */
+
+ D(("do update"));
+
+ /*
+ * get the old token back. NULL was ok only if root [at this
+ * point we assume that this has already been enforced on a
+ * previous call to this function].
+ */
+
+ if (off(UNIX_NOT_SET_PASS, ctrl)) {
+ retval = pam_get_item(pamh, PAM_OLDAUTHTOK
+ ,(const void **) &pass_old);
+ } else {
+ retval = pam_get_data(pamh, _UNIX_OLD_AUTHTOK
+ ,(const void **) &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, pamh, "user not authenticated");
+#ifdef USE_LCKPWDF
+ ulckpwdf();
+#endif
+ return retval;
+ }
+ retval = _unix_verify_shadow(user, ctrl);
+ if (retval != PAM_SUCCESS) {
+ _log_err(LOG_NOTICE, pamh, "user not authenticated 2");
+#ifdef USE_LCKPWDF
+ ulckpwdf();
+#endif
+ return retval;
+ }
+ D(("get new password now"));
+
+ lctrl = ctrl;
+
+ if (on(UNIX_USE_AUTHTOK, lctrl)) {
+ set(UNIX_USE_FIRST_PASS, lctrl);
+ }
+ retry = 0;
+ retval = PAM_AUTHTOK_ERR;
+ while ((retval != PAM_SUCCESS) && (retry++ < MAX_PASSWD_TRIES)) {
+ /*
+ * use_authtok is to force the use of a previously entered
+ * password -- needed for pluggable password strength checking
+ */
+
+ retval = _unix_read_password(pamh, lctrl
+ ,NULL
+ ,"Enter new UNIX password: "
+ ,"Retype new UNIX password: "
+ ,_UNIX_NEW_AUTHTOK
+ ,(const char **) &pass_new);
+
+ if (retval != PAM_SUCCESS) {
+ if (on(UNIX_DEBUG, ctrl)) {
+ _log_err(LOG_ALERT, pamh
+ ,"password - new password not obtained");
+ }
+ pass_old = NULL; /* tidy up */
+#ifdef USE_LCKPWDF
+ ulckpwdf();
+#endif
+ return retval;
+ }
+ D(("returned to _unix_chauthtok"));
+
+ /*
+ * At this point we know who the user is and what they
+ * propose as their new password. Verify that the new
+ * password is acceptable.
+ */
+
+ if (pass_new[0] == '\0') { /* "\0" password = NULL */
+ pass_new = NULL;
+ }
+ retval = _pam_unix_approve_pass(pamh, ctrl, pass_old, pass_new);
+ }
+
+ if (retval != PAM_SUCCESS) {
+ _log_err(LOG_NOTICE, pamh,
+ "new password not acceptable");
+ _pam_overwrite(pass_new);
+ _pam_overwrite(pass_old);
+ pass_new = pass_old = NULL; /* tidy up */
+#ifdef USE_LCKPWDF
+ ulckpwdf();
+#endif
+ 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, pamh,
+ "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);
+ }
+ }
+
+ D(("password processed"));
+
+ /* update the password database(s) -- race conditions..? */
+
+ retval = _do_setpass(pamh, user, pass_old, tpass, ctrl,
+ remember);
+ _pam_overwrite(pass_new);
+ _pam_overwrite(pass_old);
+ _pam_delete(tpass);
+ pass_old = pass_new = NULL;
+ } else { /* something has broken with the module */
+ _log_err(LOG_ALERT, pamh,
+ "password received unknown request");
+ retval = PAM_ABORT;
+ }
- return retval;
-}
+ D(("retval was %d", retval));
-STATIC int conv_sendmsg(struct pam_conv *aconv, const char *message, int style)
-{
- struct pam_message msg,*pmsg;
- struct pam_response *resp;
- int retval;
-
- /* Get the password from the user... */
- pmsg = &msg;
-
- msg.msg_style = style;
- msg.msg = message;
- resp = NULL;
-
- retval = aconv->conv(1, (const struct pam_message **) &pmsg,
- &resp, aconv->appdata_ptr);
- if (resp) {
- _pam_drop_reply(resp, 1);
- }
- return retval;
+#ifdef USE_LCKPWDF
+ ulckpwdf();
+#endif
+ return retval;
}
-STATIC int conv_getitem(struct pam_conv *aconv, char *message, int style,
- char **result)
-{
- struct pam_message msg,*pmsg;
- struct pam_response *resp;
- int retval;
-
- D(("called."));
-
- /* Get the password from the user... */
- pmsg = &msg;
- msg.msg_style = style;
- msg.msg = message;
- resp = NULL;
-
- retval = aconv->conv(1, (const struct pam_message **) &pmsg,
- &resp, aconv->appdata_ptr);
- if(retval != PAM_SUCCESS)
- return retval;
- if(resp != NULL) {
- *result = resp->resp; free(resp);
- return PAM_SUCCESS;
- }
- else
- return PAM_SERVICE_ERR;
-}
+/* static module data */
+#ifdef PAM_STATIC
+struct pam_module _pam_unix_passwd_modstruct = {
+ "pam_unix_passwd",
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ pam_sm_chauthtok,
+};
+#endif
+