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.
38 #include <security/_pam_aconf.h>
47 #include <sys/types.h>
51 #include <time.h> /* for time() */
57 #include <rpcsvc/yp_prot.h>
58 #include <rpcsvc/ypclnt.h>
64 #include <security/_pam_macros.h>
66 /* indicate the following groups are defined */
68 #define PAM_SM_PASSWORD
70 #include <security/pam_modules.h>
73 #include <security/pam_appl.h>
74 #endif /* LINUX_PAM */
76 #include <security/_pam_modutil.h>
82 #if !((__GLIBC__ == 2) && (__GLIBC_MINOR__ >= 1))
83 extern int getrpcport(const char *host, unsigned long prognum,
84 unsigned long versnum, unsigned int proto);
85 #endif /* GNU libc 2.1 */
88 * PAM framework looks for these entry-points to pass control to the
89 * password changing module.
93 # include "./lckpwdf.-c"
96 extern char *bigcrypt(const char *key, const char *salt);
100 Gets in username (has to be done) from the calling program
101 Does authentication of user (only if we are not running as root)
102 Gets new password/checks for sanity
106 /* passwd/salt conversion macros */
108 #define ascii_to_bin(c) ((c)>='a'?(c-59):(c)>='A'?((c)-53):(c)-'.')
109 #define bin_to_ascii(c) ((c)>=38?((c)-38+'a'):(c)>=12?((c)-12+'A'):(c)+'.')
113 #define _UNIX_OLD_AUTHTOK "-UN*X-OLD-PASS"
114 #define _UNIX_NEW_AUTHTOK "-UN*X-NEW-PASS"
116 #define MAX_PASSWD_TRIES 3
117 #define PW_TMPFILE "/etc/npasswd"
118 #define SH_TMPFILE "/etc/nshadow"
119 #define CRACKLIB_DICTS "/usr/share/dict/cracklib_dict"
120 #define OPW_TMPFILE "/etc/security/nopasswd"
121 #define OLD_PASSWORDS_FILE "/etc/security/opasswd"
124 * i64c - convert an integer to a radix 64 character
126 static int i64c(int i)
136 if (i >= 2 && i <= 11)
137 return ('0' - 2 + i);
138 if (i >= 12 && i <= 37)
139 return ('A' - 12 + i);
140 if (i >= 38 && i <= 63)
141 return ('a' - 38 + i);
145 static char *crypt_md5_wrapper(const char *pass_new)
148 * Code lifted from Marek Michalkiewicz's shadow suite. (CG)
149 * removed use of static variables (AGM)
154 unsigned char result[16];
155 char *cp = (char *) result;
156 unsigned char tmp[16];
161 gettimeofday(&tv, (struct timezone *) 0);
162 GoodMD5Update(&ctx, (void *) &tv, sizeof tv);
164 GoodMD5Update(&ctx, (void *) &i, sizeof i);
166 GoodMD5Update(&ctx, (void *) &i, sizeof i);
167 GoodMD5Update(&ctx, result, sizeof result);
168 GoodMD5Final(tmp, &ctx);
169 strcpy(cp, "$1$"); /* magic for the MD5 */
171 for (i = 0; i < 8; i++)
172 *cp++ = i64c(tmp[i] & 077);
175 /* no longer need cleartext */
176 x = Goodcrypt_md5(pass_new, (const char *) result);
181 static char *getNISserver(pam_handle_t *pamh)
187 if ((err = yp_get_default_domain(&domainname)) != 0) {
188 _log_err(LOG_WARNING, pamh, "can't get local yp domain: %s\n",
192 if ((err = yp_master(domainname, "passwd.byname", &master)) != 0) {
193 _log_err(LOG_WARNING, pamh, "can't find the master ypserver: %s\n",
197 port = getrpcport(master, YPPASSWDPROG, YPPASSWDPROC_UPDATE, IPPROTO_UDP);
199 _log_err(LOG_WARNING, pamh,
200 "yppasswdd not running on NIS master host\n");
203 if (port >= IPPORT_RESERVED) {
204 _log_err(LOG_WARNING, pamh,
205 "yppasswd daemon running on illegal port.\n");
211 static int check_old_password(const char *forwho, const char *newpass)
213 static char buf[16384];
214 char *s_luser, *s_uid, *s_npas, *s_pas;
215 int retval = PAM_SUCCESS;
218 opwfile = fopen(OLD_PASSWORDS_FILE, "r");
220 return PAM_AUTHTOK_ERR;
222 while (fgets(buf, 16380, opwfile)) {
223 if (!strncmp(buf, forwho, strlen(forwho))) {
224 buf[strlen(buf) - 1] = '\0';
225 s_luser = strtok(buf, ":,");
226 s_uid = strtok(NULL, ":,");
227 s_npas = strtok(NULL, ":,");
228 s_pas = strtok(NULL, ":,");
229 while (s_pas != NULL) {
230 char *md5pass = Goodcrypt_md5(newpass, s_pas);
231 if (!strcmp(md5pass, s_pas)) {
232 _pam_delete(md5pass);
233 retval = PAM_AUTHTOK_ERR;
236 s_pas = strtok(NULL, ":,");
237 _pam_delete(md5pass);
247 static int save_old_password(pam_handle_t *pamh,
248 const char *forwho, const char *oldpass,
251 static char buf[16384];
252 static char nbuf[16384];
253 char *s_luser, *s_uid, *s_npas, *s_pas, *pass;
255 FILE *pwfile, *opwfile;
259 struct passwd *pwd = NULL;
265 if (oldpass == NULL) {
269 oldmask = umask(077);
270 pwfile = fopen(OPW_TMPFILE, "w");
272 if (pwfile == NULL) {
273 return PAM_AUTHTOK_ERR;
276 opwfile = fopen(OLD_PASSWORDS_FILE, "r");
277 if (opwfile == NULL) {
279 return PAM_AUTHTOK_ERR;
282 chown(OPW_TMPFILE, 0, 0);
283 chmod(OPW_TMPFILE, 0600);
285 while (fgets(buf, 16380, opwfile)) {
286 if (!strncmp(buf, forwho, strlen(forwho))) {
287 buf[strlen(buf) - 1] = '\0';
288 s_luser = strtok(buf, ":");
289 s_uid = strtok(NULL, ":");
290 s_npas = strtok(NULL, ":");
291 s_pas = strtok(NULL, ":");
292 npas = strtol(s_npas, NULL, 10) + 1;
293 while (npas > howmany) {
294 s_pas = strpbrk(s_pas, ",");
299 pass = crypt_md5_wrapper(oldpass);
301 snprintf(nbuf, sizeof(nbuf), "%s:%s:%d:%s\n",
302 s_luser, s_uid, npas, pass);
304 snprintf(nbuf, sizeof(nbuf),"%s:%s:%d:%s,%s\n",
305 s_luser, s_uid, npas, s_pas, pass);
307 if (fputs(nbuf, pwfile) < 0) {
312 } else if (fputs(buf, pwfile) < 0) {
320 pwd = _pammodutil_getpwnam(pamh, forwho);
324 pass = crypt_md5_wrapper(oldpass);
325 snprintf(nbuf, sizeof(nbuf), "%s:%d:1:%s\n",
326 forwho, pwd->pw_uid, pass);
328 if (fputs(nbuf, pwfile) < 0) {
334 if (fclose(pwfile)) {
335 D(("error writing entries to old passwords file: %s\n",
341 rename(OPW_TMPFILE, OLD_PASSWORDS_FILE);
345 return PAM_AUTHTOK_ERR;
349 static int _update_passwd(pam_handle_t *pamh,
350 const char *forwho, const char *towhat)
352 struct passwd *tmpent = NULL;
354 FILE *pwfile, *opwfile;
358 oldmask = umask(077);
359 pwfile = fopen(PW_TMPFILE, "w");
361 if (pwfile == NULL) {
362 return PAM_AUTHTOK_ERR;
365 opwfile = fopen("/etc/passwd", "r");
366 if (opwfile == NULL) {
368 return PAM_AUTHTOK_ERR;
371 if (fstat(fileno(opwfile), &st) == -1) {
372 chown(PW_TMPFILE, 0, 0);
373 chmod(PW_TMPFILE, 0644);
375 chown(PW_TMPFILE, st.st_uid, st.st_gid);
376 chmod(PW_TMPFILE, st.st_mode);
378 tmpent = fgetpwent(opwfile);
380 if (!strcmp(tmpent->pw_name, forwho)) {
383 const char *const_charp;
386 assigned_passwd.const_charp = towhat;
388 tmpent->pw_passwd = assigned_passwd.charp;
391 if (putpwent(tmpent, pwfile)) {
392 D(("error writing entry to password file: %s\n", strerror(errno)));
396 tmpent = fgetpwent(opwfile);
400 if (fclose(pwfile)) {
401 D(("error writing entries to password file: %s\n", strerror(errno)));
406 rename(PW_TMPFILE, "/etc/passwd");
407 _log_err(LOG_NOTICE, pamh, "password changed for %s", forwho);
411 return PAM_AUTHTOK_ERR;
415 static int _update_shadow(const char *forwho, char *towhat)
417 struct spwd *spwdent = NULL, *stmpent = NULL;
419 FILE *pwfile, *opwfile;
423 spwdent = getspnam(forwho);
424 if (spwdent == NULL) {
425 return PAM_USER_UNKNOWN;
427 oldmask = umask(077);
428 pwfile = fopen(SH_TMPFILE, "w");
430 if (pwfile == NULL) {
431 return PAM_AUTHTOK_ERR;
434 opwfile = fopen("/etc/shadow", "r");
435 if (opwfile == NULL) {
437 return PAM_AUTHTOK_ERR;
440 if (fstat(fileno(opwfile), &st) == -1) {
441 chown(SH_TMPFILE, 0, 0);
442 chmod(SH_TMPFILE, 0600);
444 chown(SH_TMPFILE, st.st_uid, st.st_gid);
445 chmod(SH_TMPFILE, st.st_mode);
447 stmpent = fgetspent(opwfile);
450 if (!strcmp(stmpent->sp_namp, forwho)) {
451 stmpent->sp_pwdp = towhat;
452 stmpent->sp_lstchg = time(NULL) / (60 * 60 * 24);
454 D(("Set password %s for %s", stmpent->sp_pwdp, forwho));
457 if (putspent(stmpent, pwfile)) {
458 D(("error writing entry to shadow file: %s\n", strerror(errno)));
463 stmpent = fgetspent(opwfile);
467 if (fclose(pwfile)) {
468 D(("error writing entries to shadow file: %s\n", strerror(errno)));
473 rename(SH_TMPFILE, "/etc/shadow");
477 return PAM_AUTHTOK_ERR;
481 static int _do_setpass(pam_handle_t* pamh, const char *forwho, char *fromwhat,
482 char *towhat, unsigned int ctrl, int remember)
484 struct passwd *pwd = NULL;
489 pwd = getpwnam(forwho);
491 return PAM_AUTHTOK_ERR;
493 if (on(UNIX_NIS, ctrl)) {
494 struct timeval timeout;
495 struct yppasswd yppwd;
501 /* Make RPC call to NIS server */
502 if ((master = getNISserver(pamh)) == NULL)
503 return PAM_TRY_AGAIN;
505 /* Initialize password information */
506 yppwd.newpw.pw_passwd = pwd->pw_passwd;
507 yppwd.newpw.pw_name = pwd->pw_name;
508 yppwd.newpw.pw_uid = pwd->pw_uid;
509 yppwd.newpw.pw_gid = pwd->pw_gid;
510 yppwd.newpw.pw_gecos = pwd->pw_gecos;
511 yppwd.newpw.pw_dir = pwd->pw_dir;
512 yppwd.newpw.pw_shell = pwd->pw_shell;
513 yppwd.oldpass = fromwhat;
514 yppwd.newpw.pw_passwd = towhat;
516 D(("Set password %s for %s", yppwd.newpw.pw_passwd, forwho));
518 /* The yppasswd.x file said `unix authentication required',
519 * so I added it. This is the only reason it is in here.
520 * My yppasswdd doesn't use it, but maybe some others out there
523 clnt = clnt_create(master, YPPASSWDPROG, YPPASSWDVERS, "udp");
524 clnt->cl_auth = authunix_create_default();
525 memset((char *) &status, '\0', sizeof(status));
528 err = clnt_call(clnt, YPPASSWDPROC_UPDATE,
529 (xdrproc_t) xdr_yppasswd, (char *) &yppwd,
530 (xdrproc_t) xdr_int, (char *) &status,
535 retval = PAM_TRY_AGAIN;
537 D(("Error while changing NIS password.\n"));
538 retval = PAM_TRY_AGAIN;
540 D(("The password has%s been changed on %s.",
541 (err || status) ? " not" : "", master));
542 _log_err(LOG_NOTICE, pamh, "password%s changed for %s on %s",
543 (err || status) ? " not" : "", pwd->pw_name, master);
545 auth_destroy(clnt->cl_auth);
547 if ((err || status) != 0) {
548 retval = PAM_TRY_AGAIN;
555 /* first, save old password */
556 if (save_old_password(pamh, forwho, fromwhat, remember)) {
557 return PAM_AUTHTOK_ERR;
562 * These values for the number of attempts and the sleep time
563 * are, of course, completely arbitrary.
565 * My reading of the PAM docs is that, once pam_chauthtok()
566 * has been called with PAM_UPDATE_AUTHTOK, we are obliged to
567 * take any reasonable steps to make sure the token is
568 * updated; so retrying for 1/10 sec. isn't overdoing it.
573 return PAM_AUTHTOK_LOCK_BUSY;
575 #endif /* def USE_LCKPWDF */
577 if (on(UNIX_SHADOW, ctrl) || (strcmp(pwd->pw_passwd, "x") == 0)) {
578 retval = _update_shadow(forwho, towhat);
579 if (retval == PAM_SUCCESS)
580 retval = _update_passwd(pamh, forwho, "x");
582 retval = _update_passwd(pamh, forwho, towhat);
587 #endif /* def USE_LCKPWDF */
592 static int _unix_verify_shadow(const char *user, unsigned int ctrl)
594 struct passwd *pwd = NULL; /* Password and shadow password */
595 struct spwd *spwdent = NULL; /* file entries for the user */
597 int retval = PAM_SUCCESS;
599 /* UNIX passwords area */
600 pwd = getpwnam(user); /* Get password file entry... */
602 return PAM_AUTHINFO_UNAVAIL; /* We don't need to do the rest... */
604 if (strcmp(pwd->pw_passwd, "x") == 0) {
605 /* ...and shadow password file entry for this user, if shadowing
608 spwdent = getspnam(user);
612 return PAM_AUTHINFO_UNAVAIL;
614 if (strcmp(pwd->pw_passwd,"*NP*") == 0) { /* NIS+ */
617 save_uid = geteuid();
618 seteuid (pwd->pw_uid);
619 spwdent = getspnam( user );
623 return PAM_AUTHINFO_UNAVAIL;
628 if (spwdent != NULL) {
629 /* We have the user's information, now let's check if their account
630 has expired (60 * 60 * 24 = number of seconds in a day) */
632 if (off(UNIX__IAMROOT, ctrl)) {
633 /* Get the current number of days since 1970 */
634 curdays = time(NULL) / (60 * 60 * 24);
635 if ((curdays < (spwdent->sp_lstchg + spwdent->sp_min))
636 && (spwdent->sp_min != -1))
637 retval = PAM_AUTHTOK_ERR;
638 else if ((curdays > (spwdent->sp_lstchg + spwdent->sp_max + spwdent->sp_inact))
639 && (spwdent->sp_max != -1) && (spwdent->sp_inact != -1)
640 && (spwdent->sp_lstchg != 0))
642 * Their password change has been put off too long,
644 retval = PAM_ACCT_EXPIRED;
645 else if ((curdays > spwdent->sp_expire) && (spwdent->sp_expire != -1)
646 && (spwdent->sp_lstchg != 0))
648 * OR their account has just plain expired
650 retval = PAM_ACCT_EXPIRED;
656 static int _pam_unix_approve_pass(pam_handle_t * pamh
658 ,const char *pass_old
659 ,const char *pass_new)
662 const char *remark = NULL;
663 int retval = PAM_SUCCESS;
665 D(("&new=%p, &old=%p", pass_old, pass_new));
666 D(("new=[%s]", pass_new));
667 D(("old=[%s]", pass_old));
669 if (pass_new == NULL || (pass_old && !strcmp(pass_old, pass_new))) {
670 if (on(UNIX_DEBUG, ctrl)) {
671 _log_err(LOG_DEBUG, pamh, "bad authentication token");
673 _make_remark(pamh, ctrl, PAM_ERROR_MSG, pass_new == NULL ?
674 "No password supplied" : "Password unchanged");
675 return PAM_AUTHTOK_ERR;
678 * if one wanted to hardwire authentication token strength
679 * checking this would be the place - AGM
682 retval = pam_get_item(pamh, PAM_USER, (const void **) &user);
683 if (retval != PAM_SUCCESS) {
684 if (on(UNIX_DEBUG, ctrl)) {
685 _log_err(LOG_ERR, pamh, "Can not get username");
686 return PAM_AUTHTOK_ERR;
689 if (off(UNIX__IAMROOT, ctrl)) {
691 remark = FascistCheck(pass_new, CRACKLIB_DICTS);
692 D(("called cracklib [%s]", remark));
694 if (strlen(pass_new) < 6)
695 remark = "You must choose a longer password";
696 D(("lenth check [%s]", remark));
698 if (on(UNIX_REMEMBER_PASSWD, ctrl))
699 if ((retval = check_old_password(user, pass_new)) != PAM_SUCCESS)
700 remark = "Password has been already used. Choose another.";
703 _make_remark(pamh, ctrl, PAM_ERROR_MSG, remark);
704 retval = PAM_AUTHTOK_ERR;
710 PAM_EXTERN int pam_sm_chauthtok(pam_handle_t * pamh, int flags,
711 int argc, const char **argv)
713 unsigned int ctrl, lctrl;
717 /* <DO NOT free() THESE> */
719 char *pass_old, *pass_new;
720 /* </DO NOT free() THESE> */
724 ctrl = _set_ctrl(pamh, flags, &remember, argc, argv);
727 * First get the name of a user
729 retval = pam_get_user(pamh, &user, NULL);
730 if (retval == PAM_SUCCESS) {
732 * Various libraries at various times have had bugs related to
733 * '+' or '-' as the first character of a user name. Don't take
734 * any chances here. Require that the username starts with an
735 * alphanumeric character.
737 if (user == NULL || !isalnum(*user)) {
738 _log_err(LOG_ERR, pamh, "bad username [%s]", user);
739 return PAM_USER_UNKNOWN;
741 if (retval == PAM_SUCCESS && on(UNIX_DEBUG, ctrl))
742 _log_err(LOG_DEBUG, pamh, "username [%s] obtained",
745 if (on(UNIX_DEBUG, ctrl))
746 _log_err(LOG_DEBUG, pamh,
747 "password - could not identify user");
751 D(("Got username of %s", user));
754 * This is not an AUTH module!
756 if (on(UNIX__NONULL, ctrl))
757 set(UNIX__NULLOK, ctrl);
759 if (on(UNIX__PRELIM, ctrl)) {
761 * obtain and verify the current password (OLDAUTHTOK) for
768 if (_unix_blankpasswd(pamh, ctrl, user)) {
770 } else if (off(UNIX__IAMROOT, ctrl)) {
772 /* instruct user what is happening */
773 #define greeting "Changing password for "
774 Announce = (char *) malloc(sizeof(greeting) + strlen(user));
775 if (Announce == NULL) {
776 _log_err(LOG_CRIT, pamh,
777 "password - out of memory");
780 (void) strcpy(Announce, greeting);
781 (void) strcpy(Announce + sizeof(greeting) - 1, user);
785 set(UNIX__OLD_PASSWD, lctrl);
786 retval = _unix_read_password(pamh, lctrl
788 ,"(current) UNIX password: "
791 ,(const char **) &pass_old);
794 if (retval != PAM_SUCCESS) {
795 _log_err(LOG_NOTICE, pamh
796 ,"password - (old) token not obtained");
799 /* verify that this is the password for this user */
801 retval = _unix_verify_password(pamh, user, pass_old, ctrl);
803 D(("process run by root so do nothing this time around"));
805 retval = PAM_SUCCESS; /* root doesn't have too */
808 if (retval != PAM_SUCCESS) {
809 D(("Authentication failed"));
813 retval = pam_set_item(pamh, PAM_OLDAUTHTOK, (const void *) pass_old);
815 if (retval != PAM_SUCCESS) {
816 _log_err(LOG_CRIT, pamh,
817 "failed to set PAM_OLDAUTHTOK");
819 retval = _unix_verify_shadow(user, ctrl);
820 if (retval == PAM_AUTHTOK_ERR) {
821 if (off(UNIX__IAMROOT, ctrl))
822 _make_remark(pamh, ctrl, PAM_ERROR_MSG,
823 "You must wait longer to change your password");
825 retval = PAM_SUCCESS;
827 } else if (on(UNIX__UPDATE, ctrl)) {
829 * tpass is used below to store the _pam_md() return; it
830 * should be _pam_delete()'d.
837 * obtain the proposed password
843 * get the old token back. NULL was ok only if root [at this
844 * point we assume that this has already been enforced on a
845 * previous call to this function].
848 if (off(UNIX_NOT_SET_PASS, ctrl)) {
849 retval = pam_get_item(pamh, PAM_OLDAUTHTOK
850 ,(const void **) &pass_old);
852 retval = pam_get_data(pamh, _UNIX_OLD_AUTHTOK
853 ,(const void **) &pass_old);
854 if (retval == PAM_NO_MODULE_DATA) {
855 retval = PAM_SUCCESS;
859 D(("pass_old [%s]", pass_old));
861 if (retval != PAM_SUCCESS) {
862 _log_err(LOG_NOTICE, pamh, "user not authenticated");
865 retval = _unix_verify_shadow(user, ctrl);
866 if (retval != PAM_SUCCESS) {
867 _log_err(LOG_NOTICE, pamh, "user not authenticated 2");
870 D(("get new password now"));
874 if (on(UNIX_USE_AUTHTOK, lctrl)) {
875 set(UNIX_USE_FIRST_PASS, lctrl);
878 retval = PAM_AUTHTOK_ERR;
879 while ((retval != PAM_SUCCESS) && (retry++ < MAX_PASSWD_TRIES)) {
881 * use_authtok is to force the use of a previously entered
882 * password -- needed for pluggable password strength checking
885 retval = _unix_read_password(pamh, lctrl
887 ,"Enter new UNIX password: "
888 ,"Retype new UNIX password: "
890 ,(const char **) &pass_new);
892 if (retval != PAM_SUCCESS) {
893 if (on(UNIX_DEBUG, ctrl)) {
894 _log_err(LOG_ALERT, pamh
895 ,"password - new password not obtained");
897 pass_old = NULL; /* tidy up */
900 D(("returned to _unix_chauthtok"));
903 * At this point we know who the user is and what they
904 * propose as their new password. Verify that the new
905 * password is acceptable.
908 if (pass_new[0] == '\0') { /* "\0" password = NULL */
911 retval = _pam_unix_approve_pass(pamh, ctrl, pass_old, pass_new);
914 if (retval != PAM_SUCCESS) {
915 _log_err(LOG_NOTICE, pamh,
916 "new password not acceptable");
917 pass_new = pass_old = NULL; /* tidy up */
921 * By reaching here we have approved the passwords and must now
922 * rebuild the password database file.
926 * First we encrypt the new password.
929 if (on(UNIX_MD5_PASS, ctrl)) {
930 tpass = crypt_md5_wrapper(pass_new);
933 * Salt manipulation is stolen from Rick Faith's passwd
934 * program. Sorry Rick :) -- alex
941 salt[0] = bin_to_ascii(tm & 0x3f);
942 salt[1] = bin_to_ascii((tm >> 6) & 0x3f);
945 if (off(UNIX_BIGCRYPT, ctrl) && strlen(pass_new) > 8) {
947 * to avoid using the _extensions_ of the bigcrypt()
948 * function we truncate the newly entered password
949 * [Problems that followed from this are fixed as per
952 char *temp = malloc(9);
955 _log_err(LOG_CRIT, pamh,
956 "out of memory for password");
957 pass_new = pass_old = NULL; /* tidy up */
960 /* copy first 8 bytes of password */
961 strncpy(temp, pass_new, 8);
964 /* no longer need cleartext */
965 tpass = bigcrypt(temp, salt);
967 _pam_delete(temp); /* tidy up */
969 tpass = bigcrypt(pass_new, salt);
973 D(("password processed"));
975 /* update the password database(s) -- race conditions..? */
977 retval = _do_setpass(pamh, user, pass_old, tpass, ctrl,
981 pass_old = pass_new = NULL;
982 } else { /* something has broken with the module */
983 _log_err(LOG_ALERT, pamh,
984 "password received unknown request");
988 D(("retval was %d", retval));
994 /* static module data */
996 struct pam_module _pam_unix_passwd_modstruct = {