2 * Main coding by Elliot Lee <sopwith@redhat.com>, Red Hat Software.
4 * Copyright (c) Jan Rêkorajski, 1999.
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, and the entire permission notice in its entirety,
11 * including the disclaimer of warranties.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. The name of the author may not be used to endorse or promote
16 * products derived from this software without specific prior
19 * ALTERNATIVELY, this product may be distributed under the terms of
20 * the GNU Public License, in which case the provisions of the GPL are
21 * required INSTEAD OF the above restrictions. (This clause is
22 * necessary due to a potential bad interaction between the GPL and
23 * the restrictions contained in a BSD-style copyright.)
25 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
26 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
27 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
28 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
29 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
30 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
31 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
32 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
33 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
34 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
35 * OF THE POSSIBILITY OF SUCH DAMAGE.
47 #include <sys/types.h>
51 #include <time.h> /* for time() */
57 #include <rpcsvc/yp_prot.h>
58 #include <rpcsvc/ypclnt.h>
64 static int selinux_enabled=-1;
65 #include <selinux/selinux.h>
66 static security_context_t prev_context=NULL;
67 #define SELINUX_ENABLED (selinux_enabled!=-1 ? selinux_enabled : (selinux_enabled=is_selinux_enabled()>0))
74 #include <security/_pam_macros.h>
76 /* indicate the following groups are defined */
78 #define PAM_SM_PASSWORD
80 #include <security/pam_modules.h>
81 #include <security/pam_ext.h>
82 #include <security/pam_modutil.h>
88 #if !((__GLIBC__ == 2) && (__GLIBC_MINOR__ >= 1))
89 extern int getrpcport(const char *host, unsigned long prognum,
90 unsigned long versnum, unsigned int proto);
91 #endif /* GNU libc 2.1 */
94 * PAM framework looks for these entry-points to pass control to the
95 * password changing module.
98 #if defined(USE_LCKPWDF) && !defined(HAVE_LCKPWDF)
99 # include "./lckpwdf.-c"
102 extern char *bigcrypt(const char *key, const char *salt);
106 Gets in username (has to be done) from the calling program
107 Does authentication of user (only if we are not running as root)
108 Gets new password/checks for sanity
112 /* passwd/salt conversion macros */
114 #define ascii_to_bin(c) ((c)>='a'?(c-59):(c)>='A'?((c)-53):(c)-'.')
115 #define bin_to_ascii(c) ((c)>=38?((c)-38+'a'):(c)>=12?((c)-12+'A'):(c)+'.')
119 #define _UNIX_OLD_AUTHTOK "-UN*X-OLD-PASS"
120 #define _UNIX_NEW_AUTHTOK "-UN*X-NEW-PASS"
122 #define MAX_PASSWD_TRIES 3
123 #define PW_TMPFILE "/etc/npasswd"
124 #define SH_TMPFILE "/etc/nshadow"
125 #ifndef CRACKLIB_DICTS
126 #define CRACKLIB_DICTS NULL
128 #define OPW_TMPFILE "/etc/security/nopasswd"
129 #define OLD_PASSWORDS_FILE "/etc/security/opasswd"
132 * i64c - convert an integer to a radix 64 character
134 static int i64c(int i)
144 if (i >= 2 && i <= 11)
145 return ('0' - 2 + i);
146 if (i >= 12 && i <= 37)
147 return ('A' - 12 + i);
148 if (i >= 38 && i <= 63)
149 return ('a' - 38 + i);
153 static char *crypt_md5_wrapper(const char *pass_new)
156 * Code lifted from Marek Michalkiewicz's shadow suite. (CG)
157 * removed use of static variables (AGM)
162 unsigned char result[16];
163 char *cp = (char *) result;
164 unsigned char tmp[16];
169 gettimeofday(&tv, (struct timezone *) 0);
170 GoodMD5Update(&ctx, (void *) &tv, sizeof tv);
172 GoodMD5Update(&ctx, (void *) &i, sizeof i);
174 GoodMD5Update(&ctx, (void *) &i, sizeof i);
175 GoodMD5Update(&ctx, result, sizeof result);
176 GoodMD5Final(tmp, &ctx);
177 strcpy(cp, "$1$"); /* magic for the MD5 */
179 for (i = 0; i < 8; i++)
180 *cp++ = i64c(tmp[i] & 077);
183 /* no longer need cleartext */
184 x = Goodcrypt_md5(pass_new, (const char *) result);
189 static char *getNISserver(pam_handle_t *pamh)
195 if ((err = yp_get_default_domain(&domainname)) != 0) {
196 pam_syslog(pamh, LOG_WARNING, "can't get local yp domain: %s",
200 if ((err = yp_master(domainname, "passwd.byname", &master)) != 0) {
201 pam_syslog(pamh, LOG_WARNING, "can't find the master ypserver: %s",
205 port = getrpcport(master, YPPASSWDPROG, YPPASSWDPROC_UPDATE, IPPROTO_UDP);
207 pam_syslog(pamh, LOG_WARNING,
208 "yppasswdd not running on NIS master host");
211 if (port >= IPPORT_RESERVED) {
212 pam_syslog(pamh, LOG_WARNING,
213 "yppasswd daemon running on illegal port");
221 static int _unix_run_shadow_binary(pam_handle_t *pamh, unsigned int ctrl, const char *user, const char *fromwhat, const char *towhat)
223 int retval, child, fds[2];
224 void (*sighandler)(int) = NULL;
227 /* create a pipe for the password */
228 if (pipe(fds) != 0) {
229 D(("could not make pipe"));
233 if (off(UNIX_NOREAP, ctrl)) {
235 * This code arranges that the demise of the child does not cause
236 * the application to receive a signal it is not expecting - which
237 * may kill the application or worse.
239 * The "noreap" module argument is provided so that the admin can
240 * override this behavior.
242 sighandler = signal(SIGCHLD, SIG_DFL);
250 static char *envp[] = { NULL };
251 char *args[] = { NULL, NULL, NULL, NULL };
253 /* XXX - should really tidy up PAM here too */
256 /* reopen stdin as pipe */
258 dup2(fds[0], STDIN_FILENO);
260 if (getrlimit(RLIMIT_NOFILE,&rlim)==0) {
261 for (i=2; i < rlim.rlim_max; i++) {
262 if ((unsigned int)fds[0] != i)
267 if (SELINUX_ENABLED && geteuid() == 0) {
268 /* must set the real uid to 0 so the helper will not error
269 out if pam is called from setuid binary (su, sudo...) */
273 /* exec binary helper */
274 args[0] = x_strdup(CHKPWD_HELPER);
275 args[1] = x_strdup(user);
276 args[2] = x_strdup("shadow");
278 execve(CHKPWD_HELPER, args, envp);
280 /* should not get here: exit with error */
281 D(("helper binary is not available"));
282 exit(PAM_AUTHINFO_UNAVAIL);
283 } else if (child > 0) {
285 /* if the stored password is NULL */
288 pam_modutil_write(fds[1], fromwhat, strlen(fromwhat)+1);
290 pam_modutil_write(fds[1], "", 1);
292 pam_modutil_write(fds[1], towhat, strlen(towhat)+1);
295 pam_modutil_write(fds[1], "", 1);
297 close(fds[0]); /* close here to avoid possible SIGPIPE above */
299 rc=waitpid(child, &retval, 0); /* wait for helper to complete */
301 pam_syslog(pamh, LOG_ERR, "unix_chkpwd waitpid returned %d: %m", rc);
302 retval = PAM_AUTH_ERR;
304 retval = WEXITSTATUS(retval);
310 retval = PAM_AUTH_ERR;
313 if (sighandler != NULL) {
314 (void) signal(SIGCHLD, sighandler); /* restore old signal handler */
321 static int check_old_password(const char *forwho, const char *newpass)
323 static char buf[16384];
324 char *s_luser, *s_uid, *s_npas, *s_pas;
325 int retval = PAM_SUCCESS;
328 opwfile = fopen(OLD_PASSWORDS_FILE, "r");
332 while (fgets(buf, 16380, opwfile)) {
333 if (!strncmp(buf, forwho, strlen(forwho))) {
334 buf[strlen(buf) - 1] = '\0';
335 s_luser = strtok(buf, ":,");
336 s_uid = strtok(NULL, ":,");
337 s_npas = strtok(NULL, ":,");
338 s_pas = strtok(NULL, ":,");
339 while (s_pas != NULL) {
340 char *md5pass = Goodcrypt_md5(newpass, s_pas);
341 if (!strcmp(md5pass, s_pas)) {
342 _pam_delete(md5pass);
343 retval = PAM_AUTHTOK_ERR;
346 s_pas = strtok(NULL, ":,");
347 _pam_delete(md5pass);
357 static int save_old_password(pam_handle_t *pamh,
358 const char *forwho, const char *oldpass,
361 static char buf[16384];
362 static char nbuf[16384];
363 char *s_luser, *s_uid, *s_npas, *s_pas, *pass;
365 FILE *pwfile, *opwfile;
369 struct passwd *pwd = NULL;
376 if (oldpass == NULL) {
380 oldmask = umask(077);
383 if (SELINUX_ENABLED) {
384 security_context_t passwd_context=NULL;
385 if (getfilecon("/etc/passwd",&passwd_context)<0) {
386 return PAM_AUTHTOK_ERR;
388 if (getfscreatecon(&prev_context)<0) {
389 freecon(passwd_context);
390 return PAM_AUTHTOK_ERR;
392 if (setfscreatecon(passwd_context)) {
393 freecon(passwd_context);
394 freecon(prev_context);
395 return PAM_AUTHTOK_ERR;
397 freecon(passwd_context);
400 pwfile = fopen(OPW_TMPFILE, "w");
402 if (pwfile == NULL) {
407 opwfile = fopen(OLD_PASSWORDS_FILE, "r");
408 if (opwfile == NULL) {
414 if (fstat(fileno(opwfile), &st) == -1) {
421 if (fchown(fileno(pwfile), st.st_uid, st.st_gid) == -1) {
427 if (fchmod(fileno(pwfile), st.st_mode) == -1) {
434 while (fgets(buf, 16380, opwfile)) {
435 if (!strncmp(buf, forwho, strlen(forwho))) {
436 buf[strlen(buf) - 1] = '\0';
437 s_luser = strtok(buf, ":");
438 s_uid = strtok(NULL, ":");
439 s_npas = strtok(NULL, ":");
440 s_pas = strtok(NULL, ":");
441 npas = strtol(s_npas, NULL, 10) + 1;
442 while (npas > howmany) {
443 s_pas = strpbrk(s_pas, ",");
448 pass = crypt_md5_wrapper(oldpass);
450 snprintf(nbuf, sizeof(nbuf), "%s:%s:%d:%s\n",
451 s_luser, s_uid, npas, pass);
453 snprintf(nbuf, sizeof(nbuf),"%s:%s:%d:%s,%s\n",
454 s_luser, s_uid, npas, s_pas, pass);
456 if (fputs(nbuf, pwfile) < 0) {
461 } else if (fputs(buf, pwfile) < 0) {
469 pwd = pam_modutil_getpwnam(pamh, forwho);
473 pass = crypt_md5_wrapper(oldpass);
474 snprintf(nbuf, sizeof(nbuf), "%s:%d:1:%s\n",
475 forwho, pwd->pw_uid, pass);
477 if (fputs(nbuf, pwfile) < 0) {
483 if (fclose(pwfile)) {
484 D(("error writing entries to old passwords file: %s\n",
491 if (rename(OPW_TMPFILE, OLD_PASSWORDS_FILE))
495 if (SELINUX_ENABLED) {
496 if (setfscreatecon(prev_context)) {
500 freecon(prev_context);
508 return PAM_AUTHTOK_ERR;
512 static int _update_passwd(pam_handle_t *pamh,
513 const char *forwho, const char *towhat)
515 struct passwd *tmpent = NULL;
517 FILE *pwfile, *opwfile;
521 oldmask = umask(077);
523 if (SELINUX_ENABLED) {
524 security_context_t passwd_context=NULL;
525 if (getfilecon("/etc/passwd",&passwd_context)<0) {
526 return PAM_AUTHTOK_ERR;
528 if (getfscreatecon(&prev_context)<0) {
529 freecon(passwd_context);
530 return PAM_AUTHTOK_ERR;
532 if (setfscreatecon(passwd_context)) {
533 freecon(passwd_context);
534 freecon(prev_context);
535 return PAM_AUTHTOK_ERR;
537 freecon(passwd_context);
540 pwfile = fopen(PW_TMPFILE, "w");
542 if (pwfile == NULL) {
547 opwfile = fopen("/etc/passwd", "r");
548 if (opwfile == NULL) {
554 if (fstat(fileno(opwfile), &st) == -1) {
561 if (fchown(fileno(pwfile), st.st_uid, st.st_gid) == -1) {
567 if (fchmod(fileno(pwfile), st.st_mode) == -1) {
574 tmpent = fgetpwent(opwfile);
576 if (!strcmp(tmpent->pw_name, forwho)) {
579 const char *const_charp;
582 assigned_passwd.const_charp = towhat;
584 tmpent->pw_passwd = assigned_passwd.charp;
587 if (putpwent(tmpent, pwfile)) {
588 D(("error writing entry to password file: %s\n", strerror(errno)));
592 tmpent = fgetpwent(opwfile);
596 if (fclose(pwfile)) {
597 D(("error writing entries to password file: %s\n", strerror(errno)));
603 if (!rename(PW_TMPFILE, "/etc/passwd"))
604 pam_syslog(pamh, LOG_NOTICE, "password changed for %s", forwho);
609 if (SELINUX_ENABLED) {
610 if (setfscreatecon(prev_context)) {
614 freecon(prev_context);
622 return PAM_AUTHTOK_ERR;
626 static int _update_shadow(pam_handle_t *pamh, const char *forwho, char *towhat)
628 struct spwd *spwdent = NULL, *stmpent = NULL;
630 FILE *pwfile, *opwfile;
634 spwdent = getspnam(forwho);
635 if (spwdent == NULL) {
636 return PAM_USER_UNKNOWN;
638 oldmask = umask(077);
641 if (SELINUX_ENABLED) {
642 security_context_t shadow_context=NULL;
643 if (getfilecon("/etc/shadow",&shadow_context)<0) {
644 return PAM_AUTHTOK_ERR;
646 if (getfscreatecon(&prev_context)<0) {
647 freecon(shadow_context);
648 return PAM_AUTHTOK_ERR;
650 if (setfscreatecon(shadow_context)) {
651 freecon(shadow_context);
652 freecon(prev_context);
653 return PAM_AUTHTOK_ERR;
655 freecon(shadow_context);
658 pwfile = fopen(SH_TMPFILE, "w");
660 if (pwfile == NULL) {
665 opwfile = fopen("/etc/shadow", "r");
666 if (opwfile == NULL) {
672 if (fstat(fileno(opwfile), &st) == -1) {
679 if (fchown(fileno(pwfile), st.st_uid, st.st_gid) == -1) {
685 if (fchmod(fileno(pwfile), st.st_mode) == -1) {
692 stmpent = fgetspent(opwfile);
695 if (!strcmp(stmpent->sp_namp, forwho)) {
696 stmpent->sp_pwdp = towhat;
697 stmpent->sp_lstchg = time(NULL) / (60 * 60 * 24);
699 D(("Set password %s for %s", stmpent->sp_pwdp, forwho));
702 if (putspent(stmpent, pwfile)) {
703 D(("error writing entry to shadow file: %s\n", strerror(errno)));
708 stmpent = fgetspent(opwfile);
712 if (fclose(pwfile)) {
713 D(("error writing entries to shadow file: %s\n", strerror(errno)));
719 if (!rename(SH_TMPFILE, "/etc/shadow"))
720 pam_syslog(pamh, LOG_NOTICE, "password changed for %s", forwho);
726 if (SELINUX_ENABLED) {
727 if (setfscreatecon(prev_context)) {
731 freecon(prev_context);
740 return PAM_AUTHTOK_ERR;
744 static int _do_setpass(pam_handle_t* pamh, const char *forwho,
745 const char *fromwhat,
746 char *towhat, unsigned int ctrl, int remember)
748 struct passwd *pwd = NULL;
755 pwd = getpwnam(forwho);
758 retval = PAM_AUTHTOK_ERR;
762 if (on(UNIX_NIS, ctrl) && _unix_comesfromsource(pamh, forwho, 0, 1)) {
763 if ((master=getNISserver(pamh)) != NULL) {
764 struct timeval timeout;
765 struct yppasswd yppwd;
770 /* Unlock passwd file to avoid deadlock */
776 /* Initialize password information */
777 yppwd.newpw.pw_passwd = pwd->pw_passwd;
778 yppwd.newpw.pw_name = pwd->pw_name;
779 yppwd.newpw.pw_uid = pwd->pw_uid;
780 yppwd.newpw.pw_gid = pwd->pw_gid;
781 yppwd.newpw.pw_gecos = pwd->pw_gecos;
782 yppwd.newpw.pw_dir = pwd->pw_dir;
783 yppwd.newpw.pw_shell = pwd->pw_shell;
784 yppwd.oldpass = fromwhat ? strdup (fromwhat) : strdup ("");
785 yppwd.newpw.pw_passwd = towhat;
787 D(("Set password %s for %s", yppwd.newpw.pw_passwd, forwho));
789 /* The yppasswd.x file said `unix authentication required',
790 * so I added it. This is the only reason it is in here.
791 * My yppasswdd doesn't use it, but maybe some others out there
794 clnt = clnt_create(master, YPPASSWDPROG, YPPASSWDVERS, "udp");
795 clnt->cl_auth = authunix_create_default();
796 memset((char *) &status, '\0', sizeof(status));
799 err = clnt_call(clnt, YPPASSWDPROC_UPDATE,
800 (xdrproc_t) xdr_yppasswd, (char *) &yppwd,
801 (xdrproc_t) xdr_int, (char *) &status,
804 free (yppwd.oldpass);
807 _make_remark(pamh, ctrl, PAM_TEXT_INFO,
810 D(("Error while changing NIS password.\n"));
812 D(("The password has%s been changed on %s.",
813 (err || status) ? " not" : "", master));
814 pam_syslog(pamh, LOG_NOTICE, "password%s changed for %s on %s",
815 (err || status) ? " not" : "", pwd->pw_name, master);
817 auth_destroy(clnt->cl_auth);
820 _make_remark(pamh, ctrl, PAM_TEXT_INFO,
821 _("NIS password could not be changed."));
822 retval = PAM_TRY_AGAIN;
828 retval = PAM_TRY_AGAIN;
832 if (_unix_comesfromsource(pamh, forwho, 1, 0)) {
836 /* These values for the number of attempts and the sleep time
837 are, of course, completely arbitrary.
838 My reading of the PAM docs is that, once pam_chauthtok() has been
839 called with PAM_UPDATE_AUTHTOK, we are obliged to take any
840 reasonable steps to make sure the token is updated; so retrying
841 for 1/10 sec. isn't overdoing it. */
842 while((retval = lckpwdf()) != 0 && i < 100) {
847 return PAM_AUTHTOK_LOCK_BUSY;
851 /* first, save old password */
852 if (save_old_password(pamh, forwho, fromwhat, remember)) {
853 retval = PAM_AUTHTOK_ERR;
856 if (on(UNIX_SHADOW, ctrl) || _unix_shadowed(pwd)) {
857 retval = _update_shadow(pamh, forwho, towhat);
859 if (retval != PAM_SUCCESS && SELINUX_ENABLED)
860 retval = _unix_run_shadow_binary(pamh, ctrl, forwho, fromwhat, towhat);
862 if (retval == PAM_SUCCESS)
863 if (!_unix_shadowed(pwd))
864 retval = _update_passwd(pamh, forwho, "x");
866 retval = _update_passwd(pamh, forwho, towhat);
879 static int _unix_verify_shadow(pam_handle_t *pamh, const char *user, unsigned int ctrl)
881 struct passwd *pwd = NULL; /* Password and shadow password */
882 struct spwd *spwdent = NULL; /* file entries for the user */
884 int retval = PAM_SUCCESS;
886 /* UNIX passwords area */
887 pwd = getpwnam(user); /* Get password file entry... */
889 return PAM_AUTHINFO_UNAVAIL; /* We don't need to do the rest... */
891 if (_unix_shadowed(pwd)) {
892 /* ...and shadow password file entry for this user, if shadowing
895 spwdent = getspnam(user);
899 if (spwdent == NULL && SELINUX_ENABLED )
900 spwdent = _unix_run_verify_binary(pamh, ctrl, user);
903 return PAM_AUTHINFO_UNAVAIL;
905 if (strcmp(pwd->pw_passwd,"*NP*") == 0) { /* NIS+ */
908 save_uid = geteuid();
909 seteuid (pwd->pw_uid);
910 spwdent = getspnam( user );
914 return PAM_AUTHINFO_UNAVAIL;
919 if (spwdent != NULL) {
920 /* We have the user's information, now let's check if their account
921 has expired (60 * 60 * 24 = number of seconds in a day) */
923 if (off(UNIX__IAMROOT, ctrl)) {
924 /* Get the current number of days since 1970 */
925 curdays = time(NULL) / (60 * 60 * 24);
926 if ((curdays < (spwdent->sp_lstchg + spwdent->sp_min))
927 && (spwdent->sp_min != -1))
928 retval = PAM_AUTHTOK_ERR;
929 else if ((curdays > (spwdent->sp_lstchg + spwdent->sp_max + spwdent->sp_inact))
930 && (spwdent->sp_max != -1) && (spwdent->sp_inact != -1)
931 && (spwdent->sp_lstchg != 0))
933 * Their password change has been put off too long,
935 retval = PAM_ACCT_EXPIRED;
936 else if ((curdays > spwdent->sp_expire) && (spwdent->sp_expire != -1)
937 && (spwdent->sp_lstchg != 0))
939 * OR their account has just plain expired
941 retval = PAM_ACCT_EXPIRED;
947 static int _pam_unix_approve_pass(pam_handle_t * pamh
949 ,const char *pass_old
950 ,const char *pass_new)
953 const char *remark = NULL;
954 int retval = PAM_SUCCESS;
956 D(("&new=%p, &old=%p", pass_old, pass_new));
957 D(("new=[%s]", pass_new));
958 D(("old=[%s]", pass_old));
960 if (pass_new == NULL || (pass_old && !strcmp(pass_old, pass_new))) {
961 if (on(UNIX_DEBUG, ctrl)) {
962 pam_syslog(pamh, LOG_DEBUG, "bad authentication token");
964 _make_remark(pamh, ctrl, PAM_ERROR_MSG, pass_new == NULL ?
965 _("No password supplied") : _("Password unchanged"));
966 return PAM_AUTHTOK_ERR;
969 * if one wanted to hardwire authentication token strength
970 * checking this would be the place - AGM
973 retval = pam_get_item(pamh, PAM_USER, &user);
974 if (retval != PAM_SUCCESS) {
975 if (on(UNIX_DEBUG, ctrl)) {
976 pam_syslog(pamh, LOG_ERR, "Can not get username");
977 return PAM_AUTHTOK_ERR;
980 if (off(UNIX__IAMROOT, ctrl)) {
982 remark = FascistCheck (pass_new, CRACKLIB_DICTS);
983 D(("called cracklib [%s]", remark));
985 if (strlen(pass_new) < 6)
986 remark = _("You must choose a longer password");
987 D(("length check [%s]", remark));
989 if (on(UNIX_REMEMBER_PASSWD, ctrl)) {
990 if ((retval = check_old_password(user, pass_new)) == PAM_AUTHTOK_ERR)
991 remark = _("Password has been already used. Choose another.");
992 if (retval == PAM_ABORT) {
993 pam_syslog(pamh, LOG_ERR, "can't open %s file to check old passwords",
1000 _make_remark(pamh, ctrl, PAM_ERROR_MSG, remark);
1001 retval = PAM_AUTHTOK_ERR;
1007 PAM_EXTERN int pam_sm_chauthtok(pam_handle_t * pamh, int flags,
1008 int argc, const char **argv)
1010 unsigned int ctrl, lctrl;
1014 /* <DO NOT free() THESE> */
1016 const void *pass_old, *pass_new;
1017 /* </DO NOT free() THESE> */
1021 ctrl = _set_ctrl(pamh, flags, &remember, argc, argv);
1024 * First get the name of a user
1026 retval = pam_get_user(pamh, &user, NULL);
1027 if (retval == PAM_SUCCESS) {
1029 * Various libraries at various times have had bugs related to
1030 * '+' or '-' as the first character of a user name. Don't take
1031 * any chances here. Require that the username starts with an
1032 * alphanumeric character.
1034 if (user == NULL || !isalnum(*user)) {
1035 pam_syslog(pamh, LOG_ERR, "bad username [%s]", user);
1036 return PAM_USER_UNKNOWN;
1038 if (retval == PAM_SUCCESS && on(UNIX_DEBUG, ctrl))
1039 pam_syslog(pamh, LOG_DEBUG, "username [%s] obtained",
1042 if (on(UNIX_DEBUG, ctrl))
1043 pam_syslog(pamh, LOG_DEBUG,
1044 "password - could not identify user");
1048 D(("Got username of %s", user));
1051 * Before we do anything else, check to make sure that the user's
1052 * info is in one of the databases we can modify from this module,
1053 * which currently is 'files' and 'nis'. We have to do this because
1054 * getpwnam() doesn't tell you *where* the information it gives you
1055 * came from, nor should it. That's our job.
1057 if (_unix_comesfromsource(pamh, user, 1, on(UNIX_NIS, ctrl)) == 0) {
1058 pam_syslog(pamh, LOG_DEBUG,
1059 "user \"%s\" does not exist in /etc/passwd%s",
1060 user, on(UNIX_NIS, ctrl) ? " or NIS" : "");
1061 return PAM_USER_UNKNOWN;
1064 _unix_getpwnam(pamh, user, 1, 1, &pwd);
1066 pam_syslog(pamh, LOG_DEBUG,
1067 "user \"%s\" has corrupted passwd entry",
1069 return PAM_USER_UNKNOWN;
1071 if (!_unix_shadowed(pwd) &&
1072 (strchr(pwd->pw_passwd, '*') != NULL)) {
1073 pam_syslog(pamh, LOG_DEBUG,
1074 "user \"%s\" does not have modifiable password",
1076 return PAM_USER_UNKNOWN;
1081 * This is not an AUTH module!
1083 if (on(UNIX__NONULL, ctrl))
1084 set(UNIX__NULLOK, ctrl);
1086 if (on(UNIX__PRELIM, ctrl)) {
1088 * obtain and verify the current password (OLDAUTHTOK) for
1093 D(("prelim check"));
1095 if (_unix_blankpasswd(pamh, ctrl, user)) {
1097 } else if (off(UNIX__IAMROOT, ctrl)) {
1099 /* instruct user what is happening */
1100 #define greeting "Changing password for "
1101 Announce = (char *) malloc(sizeof(greeting) + strlen(user));
1102 if (Announce == NULL) {
1103 pam_syslog(pamh, LOG_CRIT,
1104 "password - out of memory");
1107 (void) strcpy(Announce, greeting);
1108 (void) strcpy(Announce + sizeof(greeting) - 1, user);
1112 set(UNIX__OLD_PASSWD, lctrl);
1113 retval = _unix_read_password(pamh, lctrl
1115 ,_("(current) UNIX password: ")
1121 if (retval != PAM_SUCCESS) {
1122 pam_syslog(pamh, LOG_NOTICE,
1123 "password - (old) token not obtained");
1126 /* verify that this is the password for this user */
1128 retval = _unix_verify_password(pamh, user, pass_old, ctrl);
1130 D(("process run by root so do nothing this time around"));
1132 retval = PAM_SUCCESS; /* root doesn't have too */
1135 if (retval != PAM_SUCCESS) {
1136 D(("Authentication failed"));
1140 retval = pam_set_item(pamh, PAM_OLDAUTHTOK, (const void *) pass_old);
1142 if (retval != PAM_SUCCESS) {
1143 pam_syslog(pamh, LOG_CRIT,
1144 "failed to set PAM_OLDAUTHTOK");
1146 retval = _unix_verify_shadow(pamh,user, ctrl);
1147 if (retval == PAM_AUTHTOK_ERR) {
1148 if (off(UNIX__IAMROOT, ctrl))
1149 _make_remark(pamh, ctrl, PAM_ERROR_MSG,
1150 _("You must wait longer to change your password"));
1152 retval = PAM_SUCCESS;
1154 } else if (on(UNIX__UPDATE, ctrl)) {
1156 * tpass is used below to store the _pam_md() return; it
1157 * should be _pam_delete()'d.
1164 * obtain the proposed password
1170 * get the old token back. NULL was ok only if root [at this
1171 * point we assume that this has already been enforced on a
1172 * previous call to this function].
1175 if (off(UNIX_NOT_SET_PASS, ctrl)) {
1176 retval = pam_get_item(pamh, PAM_OLDAUTHTOK
1179 retval = pam_get_data(pamh, _UNIX_OLD_AUTHTOK
1181 if (retval == PAM_NO_MODULE_DATA) {
1182 retval = PAM_SUCCESS;
1186 D(("pass_old [%s]", pass_old));
1188 if (retval != PAM_SUCCESS) {
1189 pam_syslog(pamh, LOG_NOTICE, "user not authenticated");
1193 D(("get new password now"));
1197 if (on(UNIX_USE_AUTHTOK, lctrl)) {
1198 set(UNIX_USE_FIRST_PASS, lctrl);
1201 retval = PAM_AUTHTOK_ERR;
1202 while ((retval != PAM_SUCCESS) && (retry++ < MAX_PASSWD_TRIES)) {
1204 * use_authtok is to force the use of a previously entered
1205 * password -- needed for pluggable password strength checking
1208 retval = _unix_read_password(pamh, lctrl
1210 ,_("Enter new UNIX password: ")
1211 ,_("Retype new UNIX password: ")
1215 if (retval != PAM_SUCCESS) {
1216 if (on(UNIX_DEBUG, ctrl)) {
1217 pam_syslog(pamh, LOG_ALERT,
1218 "password - new password not obtained");
1220 pass_old = NULL; /* tidy up */
1223 D(("returned to _unix_chauthtok"));
1226 * At this point we know who the user is and what they
1227 * propose as their new password. Verify that the new
1228 * password is acceptable.
1231 if (*(const char *)pass_new == '\0') { /* "\0" password = NULL */
1234 retval = _pam_unix_approve_pass(pamh, ctrl, pass_old, pass_new);
1237 if (retval != PAM_SUCCESS) {
1238 pam_syslog(pamh, LOG_NOTICE,
1239 "new password not acceptable");
1240 pass_new = pass_old = NULL; /* tidy up */
1244 /* These values for the number of attempts and the sleep time
1245 are, of course, completely arbitrary.
1246 My reading of the PAM docs is that, once pam_chauthtok() has been
1247 called with PAM_UPDATE_AUTHTOK, we are obliged to take any
1248 reasonable steps to make sure the token is updated; so retrying
1249 for 1/10 sec. isn't overdoing it. */
1251 while((retval = lckpwdf()) != 0 && i < 100) {
1256 return PAM_AUTHTOK_LOCK_BUSY;
1261 retval = _unix_verify_password(pamh, user, pass_old, ctrl);
1262 if (retval != PAM_SUCCESS) {
1263 pam_syslog(pamh, LOG_NOTICE, "user password changed by another process");
1271 retval = _unix_verify_shadow(pamh, user, ctrl);
1272 if (retval != PAM_SUCCESS) {
1273 pam_syslog(pamh, LOG_NOTICE, "user not authenticated 2");
1280 retval = _pam_unix_approve_pass(pamh, ctrl, pass_old, pass_new);
1281 if (retval != PAM_SUCCESS) {
1282 pam_syslog(pamh, LOG_NOTICE,
1283 "new password not acceptable 2");
1284 pass_new = pass_old = NULL; /* tidy up */
1292 * By reaching here we have approved the passwords and must now
1293 * rebuild the password database file.
1297 * First we encrypt the new password.
1300 if (on(UNIX_MD5_PASS, ctrl)) {
1301 tpass = crypt_md5_wrapper(pass_new);
1304 * Salt manipulation is stolen from Rick Faith's passwd
1305 * program. Sorry Rick :) -- alex
1312 salt[0] = bin_to_ascii(tm & 0x3f);
1313 salt[1] = bin_to_ascii((tm >> 6) & 0x3f);
1316 if (off(UNIX_BIGCRYPT, ctrl) && strlen(pass_new) > 8) {
1318 * to avoid using the _extensions_ of the bigcrypt()
1319 * function we truncate the newly entered password
1320 * [Problems that followed from this are fixed as per
1323 char *temp = malloc(9);
1326 pam_syslog(pamh, LOG_CRIT,
1327 "out of memory for password");
1328 pass_new = pass_old = NULL; /* tidy up */
1334 /* copy first 8 bytes of password */
1335 strncpy(temp, pass_new, 8);
1338 /* no longer need cleartext */
1339 tpass = bigcrypt(temp, salt);
1341 _pam_delete(temp); /* tidy up */
1343 tpass = bigcrypt(pass_new, salt);
1347 D(("password processed"));
1349 /* update the password database(s) -- race conditions..? */
1351 retval = _do_setpass(pamh, user, pass_old, tpass, ctrl,
1353 /* _do_setpass has called ulckpwdf for us */
1356 pass_old = pass_new = NULL;
1357 } else { /* something has broken with the module */
1358 pam_syslog(pamh, LOG_ALERT,
1359 "password received unknown request");
1363 D(("retval was %d", retval));
1369 /* static module data */
1371 struct pam_module _pam_unix_passwd_modstruct = {