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 */
80
#if !((__GLIBC__ == 2) && (__GLIBC_MINOR__ >= 1))
81
extern int getrpcport(const char *host, unsigned long prognum,
82
unsigned long versnum, unsigned int proto);
83
#endif /* GNU libc 2.1 */
86
* PAM framework looks for these entry-points to pass control to the
87
* password changing module.
91
# include "./lckpwdf.-c"
94
extern char *bigcrypt(const char *key, const char *salt);
98
Gets in username (has to be done) from the calling program
99
Does authentication of user (only if we are not running as root)
100
Gets new password/checks for sanity
104
/* passwd/salt conversion macros */
106
#define ascii_to_bin(c) ((c)>='a'?(c-59):(c)>='A'?((c)-53):(c)-'.')
107
#define bin_to_ascii(c) ((c)>=38?((c)-38+'a'):(c)>=12?((c)-12+'A'):(c)+'.')
111
#define _UNIX_OLD_AUTHTOK "-UN*X-OLD-PASS"
112
#define _UNIX_NEW_AUTHTOK "-UN*X-NEW-PASS"
114
#define MAX_PASSWD_TRIES 3
115
#define PW_FILE "/etc/passwd"
116
#define PW_TMPFILE "/etc/npasswd"
117
#define SH_TMPFILE "/etc/nshadow"
118
#define CRACKLIB_DICTS "/usr/share/dict/cracklib_dict"
119
#define OPW_TMPFILE "/etc/security/nopasswd"
120
#define OLD_PASSWORDS_FILE "/etc/security/opasswd"
122
extern const char *obscure_msg(const char *, const char *, const struct passwd *,
126
* i64c - convert an integer to a radix 64 character
128
static int i64c(int i)
138
if (i >= 2 && i <= 11)
139
return ('0' - 2 + i);
140
if (i >= 12 && i <= 37)
141
return ('A' - 12 + i);
142
if (i >= 38 && i <= 63)
143
return ('a' - 38 + i);
147
static char *crypt_md5_wrapper(const char *pass_new)
150
* Code lifted from Marek Michalkiewicz's shadow suite. (CG)
151
* removed use of static variables (AGM)
156
unsigned char result[16];
157
char *cp = (char *) result;
158
unsigned char tmp[16];
163
gettimeofday(&tv, (struct timezone *) 0);
164
GoodMD5Update(&ctx, (void *) &tv, sizeof tv);
166
GoodMD5Update(&ctx, (void *) &i, sizeof i);
168
GoodMD5Update(&ctx, (void *) &i, sizeof i);
169
GoodMD5Update(&ctx, result, sizeof result);
170
GoodMD5Final(tmp, &ctx);
171
strcpy(cp, "$1$"); /* magic for the MD5 */
173
for (i = 0; i < 8; i++)
174
*cp++ = i64c(tmp[i] & 077);
177
/* no longer need cleartext */
178
x = Goodcrypt_md5(pass_new, (const char *) result);
183
static char *getNISserver(pam_handle_t *pamh)
189
if ((err = yp_get_default_domain(&domainname)) != 0) {
190
_log_err(LOG_WARNING, pamh, "can't get local yp domain: %s\n",
194
if ((err = yp_master(domainname, "passwd.byname", &master)) != 0) {
195
_log_err(LOG_WARNING, pamh, "can't find the master ypserver: %s\n",
199
port = getrpcport(master, YPPASSWDPROG, YPPASSWDPROC_UPDATE, IPPROTO_UDP);
201
_log_err(LOG_WARNING, pamh,
202
"yppasswdd not running on NIS master host\n");
205
if (port >= IPPORT_RESERVED) {
206
_log_err(LOG_WARNING, pamh,
207
"yppasswd daemon running on illegal port.\n");
213
static struct passwd *_unix_getpwnam(const char *name)
215
struct passwd *ent = NULL;
218
pwfile = fopen(PW_FILE, "r");
219
if (pwfile != NULL) {
220
ent = fgetpwent(pwfile);
221
while (ent && (strcmp(ent->pw_name, name) != 0))
222
ent = fgetpwent(pwfile);
228
static int check_old_password(const char *forwho, const char *newpass)
230
static char buf[16384];
231
char *s_luser, *s_uid, *s_npas, *s_pas;
232
int retval = PAM_SUCCESS;
235
opwfile = fopen(OLD_PASSWORDS_FILE, "r");
237
return PAM_AUTHTOK_ERR;
239
while (fgets(buf, 16380, opwfile)) {
240
if (!strncmp(buf, forwho, strlen(forwho))) {
241
buf[strlen(buf) - 1] = '\0';
242
s_luser = strtok(buf, ":,");
243
s_uid = strtok(NULL, ":,");
244
s_npas = strtok(NULL, ":,");
245
s_pas = strtok(NULL, ":,");
246
while (s_pas != NULL) {
247
char *md5pass = Goodcrypt_md5(newpass, s_pas);
248
if (!strcmp(md5pass, s_pas)) {
249
_pam_delete(md5pass);
250
retval = PAM_AUTHTOK_ERR;
253
s_pas = strtok(NULL, ":,");
254
_pam_delete(md5pass);
264
static int save_old_password(const char *forwho, const char *oldpass,
267
static char buf[16384];
268
static char nbuf[16384];
269
char *s_luser, *s_uid, *s_npas, *s_pas, *pass;
271
FILE *pwfile, *opwfile;
275
struct passwd *pwd = NULL;
281
if (oldpass == NULL) {
285
oldmask = umask(077);
286
pwfile = fopen(OPW_TMPFILE, "w");
288
if (pwfile == NULL) {
289
return PAM_AUTHTOK_ERR;
292
opwfile = fopen(OLD_PASSWORDS_FILE, "r");
293
if (opwfile == NULL) {
295
return PAM_AUTHTOK_ERR;
298
chown(OPW_TMPFILE, 0, 0);
299
chmod(OPW_TMPFILE, 0600);
301
while (fgets(buf, 16380, opwfile)) {
302
if (!strncmp(buf, forwho, strlen(forwho))) {
303
buf[strlen(buf) - 1] = '\0';
304
s_luser = strtok(buf, ":");
305
s_uid = strtok(NULL, ":");
306
s_npas = strtok(NULL, ":");
307
s_pas = strtok(NULL, ":");
308
npas = strtol(s_npas, NULL, 10) + 1;
309
while (npas > howmany) {
310
s_pas = strpbrk(s_pas, ",");
315
pass = crypt_md5_wrapper(oldpass);
317
snprintf(nbuf, sizeof(nbuf), "%s:%s:%d:%s\n",
318
s_luser, s_uid, npas, pass);
320
snprintf(nbuf, sizeof(nbuf),"%s:%s:%d:%s,%s\n",
321
s_luser, s_uid, npas, s_pas, pass);
323
if (fputs(nbuf, pwfile) < 0) {
328
} else if (fputs(buf, pwfile) < 0) {
336
pwd = _unix_getpwnam(forwho);
340
pass = crypt_md5_wrapper(oldpass);
341
snprintf(nbuf, sizeof(nbuf), "%s:%d:1:%s\n",
342
forwho, pwd->pw_uid, pass);
344
if (fputs(nbuf, pwfile) < 0) {
350
if (fclose(pwfile)) {
351
D(("error writing entries to old passwords file: %s\n",
357
rename(OPW_TMPFILE, OLD_PASSWORDS_FILE);
361
return PAM_AUTHTOK_ERR;
365
static int _update_passwd(pam_handle_t *pamh,
366
const char *forwho, const char *towhat)
368
struct passwd *tmpent = NULL;
369
FILE *pwfile, *opwfile;
370
int err = 1, found = 0;
373
oldmask = umask(077);
374
pwfile = fopen(PW_TMPFILE, "w");
376
if (pwfile == NULL) {
377
return PAM_AUTHTOK_ERR;
380
opwfile = fopen("/etc/passwd", "r");
381
if (opwfile == NULL) {
383
return PAM_AUTHTOK_ERR;
386
chown(PW_TMPFILE, 0, 0);
387
chmod(PW_TMPFILE, 0644);
388
tmpent = fgetpwent(opwfile);
390
if (!strcmp(tmpent->pw_name, forwho)) {
393
const char *const_charp;
396
assigned_passwd.const_charp = towhat;
398
tmpent->pw_passwd = assigned_passwd.charp;
402
if (putpwent(tmpent, pwfile)) {
403
D(("error writing entry to password file: %s\n", strerror(errno)));
407
tmpent = fgetpwent(opwfile);
411
if (fclose(pwfile)) {
412
D(("error writing entries to password file: %s\n", strerror(errno)));
417
rename(PW_TMPFILE, "/etc/passwd");
418
_log_err(LOG_NOTICE, pamh, "password changed for %s", forwho);
422
return found?PAM_AUTHTOK_ERR:PAM_USER_UNKNOWN;
426
static int _update_shadow(const char *forwho, char *towhat)
428
struct spwd *spwdent = NULL, *stmpent = NULL;
429
FILE *pwfile, *opwfile;
430
int err = 1, found = 0;
433
spwdent = getspnam(forwho);
434
if (spwdent == NULL) {
435
return PAM_USER_UNKNOWN;
437
oldmask = umask(077);
438
pwfile = fopen(SH_TMPFILE, "w");
440
if (pwfile == NULL) {
441
return PAM_AUTHTOK_ERR;
444
opwfile = fopen("/etc/shadow", "r");
445
if (opwfile == NULL) {
447
return PAM_AUTHTOK_ERR;
450
/* Debian's shadow file is group shadow, and readable by group,
451
shouldn't all of them be this way? */
452
chown(SH_TMPFILE, 0, 42);
453
chmod(SH_TMPFILE, 0640);
454
stmpent = fgetspent(opwfile);
457
if (!strcmp(stmpent->sp_namp, forwho)) {
458
stmpent->sp_pwdp = towhat;
459
stmpent->sp_lstchg = time(NULL) / (60 * 60 * 24);
462
D(("Set password %s for %s", stmpent->sp_pwdp, forwho));
465
if (putspent(stmpent, pwfile)) {
466
D(("error writing entry to shadow file: %s\n", strerror(errno)));
471
stmpent = fgetspent(opwfile);
475
if (fclose(pwfile)) {
476
D(("error writing entries to shadow file: %s\n", strerror(errno)));
481
rename(SH_TMPFILE, "/etc/shadow");
485
return found?PAM_AUTHTOK_ERR:PAM_USER_UNKNOWN;
489
static int _do_setpass(pam_handle_t* pamh, const char *forwho, char *fromwhat,
490
char *towhat, unsigned int ctrl, int remember)
492
struct passwd *pwd = NULL;
497
pwd = getpwnam(forwho);
499
return PAM_AUTHTOK_ERR;
501
if (on(UNIX_NIS, ctrl)) {
502
struct timeval timeout;
503
struct yppasswd yppwd;
509
/* Make RPC call to NIS server */
510
if ((master = getNISserver(pamh)) == NULL)
511
return PAM_TRY_AGAIN;
513
/* Initialize password information */
514
yppwd.newpw.pw_passwd = pwd->pw_passwd;
515
yppwd.newpw.pw_name = pwd->pw_name;
516
yppwd.newpw.pw_uid = pwd->pw_uid;
517
yppwd.newpw.pw_gid = pwd->pw_gid;
518
yppwd.newpw.pw_gecos = pwd->pw_gecos;
519
yppwd.newpw.pw_dir = pwd->pw_dir;
520
yppwd.newpw.pw_shell = pwd->pw_shell;
521
yppwd.oldpass = fromwhat ? fromwhat : "";
522
yppwd.newpw.pw_passwd = towhat;
524
D(("Set password %s for %s", yppwd.newpw.pw_passwd, forwho));
526
/* The yppasswd.x file said `unix authentication required',
527
* so I added it. This is the only reason it is in here.
528
* My yppasswdd doesn't use it, but maybe some others out there
531
clnt = clnt_create(master, YPPASSWDPROG, YPPASSWDVERS, "udp");
532
clnt->cl_auth = authunix_create_default();
533
memset((char *) &status, '\0', sizeof(status));
536
err = clnt_call(clnt, YPPASSWDPROC_UPDATE,
537
(xdrproc_t) xdr_yppasswd, (char *) &yppwd,
538
(xdrproc_t) xdr_int, (char *) &status,
543
retval = PAM_TRY_AGAIN;
545
D(("Error while changing NIS password.\n"));
546
retval = PAM_TRY_AGAIN;
548
D(("The password has%s been changed on %s.",
549
(err || status) ? " not" : "", master));
550
_log_err(LOG_NOTICE, pamh, "password%s changed for %s on %s",
551
(err || status) ? " not" : "", pwd->pw_name, master);
553
auth_destroy(clnt->cl_auth);
555
if ((err || status) != 0) {
556
retval = PAM_TRY_AGAIN;
561
_log_err(LOG_NOTICE, pamh, "NIS Password for %s was changed on %s", forwho, master);
565
/* first, save old password */
566
if (save_old_password(forwho, fromwhat, remember)) {
567
return PAM_AUTHTOK_ERR;
572
* These values for the number of attempts and the sleep time
573
* are, of course, completely arbitrary.
575
* My reading of the PAM docs is that, once pam_chauthtok()
576
* has been called with PAM_UPDATE_AUTHTOK, we are obliged to
577
* take any reasonable steps to make sure the token is
578
* updated; so retrying for 1/10 sec. isn't overdoing it.
583
return PAM_AUTHTOK_LOCK_BUSY;
585
#endif /* def USE_LCKPWDF */
587
if (on(UNIX_SHADOW, ctrl) || (strcmp(pwd->pw_passwd, "x") == 0)) {
588
retval = _update_shadow(forwho, towhat);
589
if (retval == PAM_SUCCESS)
590
retval = _update_passwd(pamh, forwho, "x");
592
retval = _update_passwd(pamh, forwho, towhat);
595
if (retval == PAM_SUCCESS)
596
_log_err(LOG_NOTICE, pamh, "Password for %s was changed", forwho);
600
#endif /* def USE_LCKPWDF */
605
static int _unix_verify_shadow(const char *user, unsigned int ctrl)
607
struct passwd *pwd = NULL; /* Password and shadow password */
608
struct spwd *spwdent = NULL; /* file entries for the user */
610
int retval = PAM_SUCCESS;
612
/* UNIX passwords area */
613
pwd = _unix_getpwnam(user); /* Get password *file* entry... */
615
return PAM_AUTHINFO_UNAVAIL; /* We don't need to do the rest... */
617
if (strcmp(pwd->pw_passwd, "x") == 0) {
618
/* ...and shadow password file entry for this user, if shadowing
621
spwdent = getspnam(user);
625
return PAM_AUTHINFO_UNAVAIL;
627
if (strcmp(pwd->pw_passwd,"*NP*") == 0) { /* NIS+ */
630
save_uid = geteuid();
631
seteuid (pwd->pw_uid);
632
spwdent = getspnam( user );
636
return PAM_AUTHINFO_UNAVAIL;
641
if (spwdent != NULL) {
642
/* We have the user's information, now let's check if their account
643
has expired (60 * 60 * 24 = number of seconds in a day) */
645
if (off(UNIX__IAMROOT, ctrl)) {
646
/* Get the current number of days since 1970 */
647
curdays = time(NULL) / (60 * 60 * 24);
648
if ((curdays < (spwdent->sp_lstchg + spwdent->sp_min))
649
&& (spwdent->sp_min != -1))
650
retval = PAM_AUTHTOK_ERR;
651
else if ((curdays > (spwdent->sp_lstchg + spwdent->sp_max + spwdent->sp_inact))
652
&& (spwdent->sp_max != -1) && (spwdent->sp_inact != -1)
653
&& (spwdent->sp_lstchg != 0))
655
* Their password change has been put off too long,
657
retval = PAM_ACCT_EXPIRED;
658
else if ((curdays > spwdent->sp_expire) && (spwdent->sp_expire != -1)
659
&& (spwdent->sp_lstchg != 0))
661
* OR their account has just plain expired
663
retval = PAM_ACCT_EXPIRED;
669
static int _pam_unix_approve_pass(pam_handle_t * pamh
671
,const char *pass_old
672
,const char *pass_new)
675
const char *remark = NULL;
676
int retval = PAM_SUCCESS;
678
D(("&new=%p, &old=%p", pass_old, pass_new));
679
D(("new=[%s]", pass_new));
680
D(("old=[%s]", pass_old));
682
if (pass_new == NULL || (pass_old && !strcmp(pass_old, pass_new))) {
683
if (on(UNIX_DEBUG, ctrl)) {
684
_log_err(LOG_DEBUG, pamh, "bad authentication token");
686
_make_remark(pamh, ctrl, PAM_ERROR_MSG, pass_new == NULL ?
687
"No password supplied" : "Password unchanged");
688
return PAM_AUTHTOK_ERR;
691
* if one wanted to hardwire authentication token strength
692
* checking this would be the place - AGM
695
retval = pam_get_item(pamh, PAM_USER, (const void **) &user);
696
if (retval != PAM_SUCCESS) {
697
if (on(UNIX_DEBUG, ctrl)) {
698
_log_err(LOG_ERR, pamh, "Can not get username");
699
return PAM_AUTHTOK_ERR;
703
if (off(UNIX__IAMROOT, ctrl)) {
705
remark = FascistCheck(pass_new, CRACKLIB_DICTS);
706
D(("called cracklib [%s]", remark));
708
if (!remark && on(UNIX_REMEMBER_PASSWD, ctrl))
709
if ((retval = check_old_password(user, pass_new)) != PAM_SUCCESS)
710
remark = "Password has been already used. Choose another.";
712
if (!remark && pass_old != NULL) { /* only check if we don't already have a failure */
714
pwd = getpwnam(user);
715
remark = (char *)obscure_msg(pass_old,pass_new,pwd,ctrl); /* do obscure checks */
719
_make_remark(pamh, ctrl, PAM_ERROR_MSG, remark);
720
retval = PAM_AUTHTOK_ERR;
726
PAM_EXTERN int pam_sm_chauthtok(pam_handle_t * pamh, int flags,
727
int argc, const char **argv)
729
unsigned int ctrl, lctrl;
733
/* <DO NOT free() THESE> */
735
char *pass_old, *pass_new;
736
/* </DO NOT free() THESE> */
740
ctrl = _set_ctrl(pamh, flags, &remember, argc, argv);
743
* First get the name of a user
745
retval = pam_get_user(pamh, &user, NULL);
746
if (retval == PAM_SUCCESS) {
748
* Various libraries at various times have had bugs related to
749
* '+' or '-' as the first character of a user name. Don't take
750
* any chances here. Require that the username starts with an
751
* alphanumeric character.
753
if (user == NULL || !isalnum(*user)) {
754
_log_err(LOG_ERR, pamh, "bad username [%s]", user);
755
return PAM_USER_UNKNOWN;
757
if (retval == PAM_SUCCESS && on(UNIX_DEBUG, ctrl))
758
_log_err(LOG_DEBUG, pamh, "username [%s] obtained",
761
if (on(UNIX_DEBUG, ctrl))
762
_log_err(LOG_DEBUG, pamh,
763
"password - could not identify user");
767
D(("Got username of %s", user));
770
* This is not an AUTH module!
772
if (on(UNIX__NONULL, ctrl))
773
set(UNIX__NULLOK, ctrl);
775
if (on(UNIX__PRELIM, ctrl)) {
777
* obtain and verify the current password (OLDAUTHTOK) for
784
if (_unix_blankpasswd(ctrl, pamh, user)) {
786
} else if (off(UNIX__IAMROOT, ctrl) || on(UNIX_NIS, ctrl)) {
788
/* instruct user what is happening */
789
#define greeting "Changing password for "
790
Announce = (char *) malloc(sizeof(greeting) + strlen(user));
791
if (Announce == NULL) {
792
_log_err(LOG_CRIT, pamh,
793
"password - out of memory");
796
(void) strcpy(Announce, greeting);
797
(void) strcpy(Announce + sizeof(greeting) - 1, user);
801
set(UNIX__OLD_PASSWD, lctrl);
802
retval = _unix_read_password(pamh, lctrl
804
,(on(UNIX__IAMROOT, ctrl)
805
? "NIS server root password: "
806
: "(current) UNIX password: ")
809
,(const char **) &pass_old);
812
if (retval != PAM_SUCCESS) {
813
_log_err(LOG_NOTICE, pamh
814
,"password - (old) token not obtained");
817
/* verify that this is the password for this user
818
* if we're not using NIS */
820
if (off(UNIX_NIS, ctrl)) {
821
retval = _unix_verify_password(pamh, user, pass_old, ctrl);
824
D(("process run by root so do nothing this time around"));
826
retval = PAM_SUCCESS; /* root doesn't have too */
829
if (retval != PAM_SUCCESS) {
830
D(("Authentication failed"));
834
retval = pam_set_item(pamh, PAM_OLDAUTHTOK, (const void *) pass_old);
836
if (retval != PAM_SUCCESS) {
837
_log_err(LOG_CRIT, pamh,
838
"failed to set PAM_OLDAUTHTOK");
840
retval = _unix_verify_shadow(user, ctrl);
841
if (retval == PAM_AUTHTOK_ERR) {
842
if (off(UNIX__IAMROOT, ctrl))
843
_make_remark(pamh, ctrl, PAM_ERROR_MSG,
844
"You must wait longer to change your password");
846
retval = PAM_SUCCESS;
848
} else if (on(UNIX__UPDATE, ctrl)) {
850
* tpass is used below to store the _pam_md() return; it
851
* should be _pam_delete()'d.
858
* obtain the proposed password
864
* get the old token back. NULL was ok only if root [at this
865
* point we assume that this has already been enforced on a
866
* previous call to this function].
869
if (off(UNIX_NOT_SET_PASS, ctrl)) {
870
retval = pam_get_item(pamh, PAM_OLDAUTHTOK
871
,(const void **) &pass_old);
873
retval = pam_get_data(pamh, _UNIX_OLD_AUTHTOK
874
,(const void **) &pass_old);
875
if (retval == PAM_NO_MODULE_DATA) {
876
retval = PAM_SUCCESS;
880
D(("pass_old [%s]", pass_old));
882
if (retval != PAM_SUCCESS) {
883
_log_err(LOG_NOTICE, pamh, "user not authenticated");
886
retval = _unix_verify_shadow(user, ctrl);
887
if (retval != PAM_SUCCESS) {
888
_log_err(LOG_NOTICE, pamh, "user not authenticated 2");
891
D(("get new password now"));
895
if (on(UNIX_USE_AUTHTOK, lctrl)) {
896
set(UNIX_USE_FIRST_PASS, lctrl);
899
retval = PAM_AUTHTOK_ERR;
900
while ((retval != PAM_SUCCESS) && (retry++ < MAX_PASSWD_TRIES)) {
902
* use_authtok is to force the use of a previously entered
903
* password -- needed for pluggable password strength checking
906
retval = _unix_read_password(pamh, lctrl
908
,"Enter new UNIX password: "
909
,"Retype new UNIX password: "
911
,(const char **) &pass_new);
913
if (retval != PAM_SUCCESS) {
914
if (on(UNIX_DEBUG, ctrl)) {
915
_log_err(LOG_ALERT, pamh
916
,"password - new password not obtained");
918
pass_old = NULL; /* tidy up */
921
D(("returned to _unix_chauthtok"));
924
* At this point we know who the user is and what they
925
* propose as their new password. Verify that the new
926
* password is acceptable.
929
if (pass_new[0] == '\0') { /* "\0" password = NULL */
932
retval = _pam_unix_approve_pass(pamh, ctrl, pass_old, pass_new);
935
if (retval != PAM_SUCCESS) {
936
_log_err(LOG_NOTICE, pamh,
937
"new password not acceptable");
938
pass_new = pass_old = NULL; /* tidy up */
942
* By reaching here we have approved the passwords and must now
943
* rebuild the password database file.
947
* First we encrypt the new password.
950
if (on(UNIX_MD5_PASS, ctrl)) {
951
tpass = crypt_md5_wrapper(pass_new);
954
* Salt manipulation is stolen from Rick Faith's passwd
955
* program. Sorry Rick :) -- alex
962
salt[0] = bin_to_ascii(tm & 0x3f);
963
salt[1] = bin_to_ascii((tm >> 6) & 0x3f);
966
if (off(UNIX_BIGCRYPT, ctrl) && strlen(pass_new) > 8) {
968
* to avoid using the _extensions_ of the bigcrypt()
969
* function we truncate the newly entered password
970
* [Problems that followed from this are fixed as per
973
char *temp = malloc(9);
976
_log_err(LOG_CRIT, pamh,
977
"out of memory for password");
978
pass_new = pass_old = NULL; /* tidy up */
981
/* copy first 8 bytes of password */
982
strncpy(temp, pass_new, 8);
985
/* no longer need cleartext */
986
tpass = bigcrypt(temp, salt);
988
_pam_delete(temp); /* tidy up */
990
tpass = bigcrypt(pass_new, salt);
994
D(("password processed"));
996
/* update the password database(s) -- race conditions..? */
998
retval = _do_setpass(pamh, user, pass_old, tpass, ctrl,
1002
pass_old = pass_new = NULL;
1003
} else { /* something has broken with the module */
1004
_log_err(LOG_ALERT, pamh,
1005
"password received unknown request");
1009
D(("retval was %d", retval));
1015
/* static module data */
1017
struct pam_module _pam_unix_passwd_modstruct = {