]> granicus.if.org Git - linux-pam/blob - modules/pam_unix/pam_unix_passwd.c
Relevant BUGIDs: none
[linux-pam] / modules / pam_unix / pam_unix_passwd.c
1 /*
2  * Main coding by Elliot Lee <sopwith@redhat.com>, Red Hat Software.
3  * Copyright (C) 1996.
4  * Copyright (c) Jan Rêkorajski, 1999.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
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
17  *    written permission.
18  *
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.)
24  *
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.
36  */
37
38 #include "config.h"
39
40 #include <stdio.h>
41 #include <stdlib.h>
42 #include <stdarg.h>
43 #include <string.h>
44 #include <malloc.h>
45 #include <unistd.h>
46 #include <errno.h>
47 #include <sys/types.h>
48 #include <pwd.h>
49 #include <syslog.h>
50 #include <shadow.h>
51 #include <time.h>               /* for time() */
52 #include <fcntl.h>
53 #include <ctype.h>
54 #include <sys/time.h>
55 #include <sys/stat.h>
56 #include <rpc/rpc.h>
57 #include <rpcsvc/yp_prot.h>
58 #include <rpcsvc/ypclnt.h>
59
60 #include <signal.h>
61 #include <errno.h>
62 #include <sys/wait.h>
63 #ifdef WITH_SELINUX
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))
68 #endif
69
70 #ifdef USE_CRACKLIB
71 #include <crack.h>
72 #endif
73
74 #include <security/_pam_macros.h>
75
76 /* indicate the following groups are defined */
77
78 #define PAM_SM_PASSWORD
79
80 #include <security/pam_modules.h>
81
82 #ifndef LINUX_PAM
83 #include <security/pam_appl.h>
84 #endif                          /* LINUX_PAM */
85
86 #include <security/_pam_modutil.h>
87
88 #include "yppasswd.h"
89 #include "md5.h"
90 #include "support.h"
91
92 #if !((__GLIBC__ == 2) && (__GLIBC_MINOR__ >= 1))
93 extern int getrpcport(const char *host, unsigned long prognum,
94                       unsigned long versnum, unsigned int proto);
95 #endif                          /* GNU libc 2.1 */
96
97 /*
98  * PAM framework looks for these entry-points to pass control to the
99  * password changing module.
100  */
101
102 #if defined(USE_LCKPWDF) && !defined(HAVE_LCKPWDF)
103 # include "./lckpwdf.-c"
104 #endif
105
106 extern char *bigcrypt(const char *key, const char *salt);
107
108 /*
109    How it works:
110    Gets in username (has to be done) from the calling program
111    Does authentication of user (only if we are not running as root)
112    Gets new password/checks for sanity
113    Sets it.
114  */
115
116 /* passwd/salt conversion macros */
117
118 #define ascii_to_bin(c) ((c)>='a'?(c-59):(c)>='A'?((c)-53):(c)-'.')
119 #define bin_to_ascii(c) ((c)>=38?((c)-38+'a'):(c)>=12?((c)-12+'A'):(c)+'.')
120
121 /* data tokens */
122
123 #define _UNIX_OLD_AUTHTOK       "-UN*X-OLD-PASS"
124 #define _UNIX_NEW_AUTHTOK       "-UN*X-NEW-PASS"
125
126 #define MAX_PASSWD_TRIES        3
127 #define PW_TMPFILE              "/etc/npasswd"
128 #define SH_TMPFILE              "/etc/nshadow"
129 #ifndef CRACKLIB_DICTS
130 #define CRACKLIB_DICTS          NULL
131 #endif
132 #define OPW_TMPFILE             "/etc/security/nopasswd"
133 #define OLD_PASSWORDS_FILE      "/etc/security/opasswd"
134
135 /*
136  * i64c - convert an integer to a radix 64 character
137  */
138 static int i64c(int i)
139 {
140         if (i < 0)
141                 return ('.');
142         else if (i > 63)
143                 return ('z');
144         if (i == 0)
145                 return ('.');
146         if (i == 1)
147                 return ('/');
148         if (i >= 2 && i <= 11)
149                 return ('0' - 2 + i);
150         if (i >= 12 && i <= 37)
151                 return ('A' - 12 + i);
152         if (i >= 38 && i <= 63)
153                 return ('a' - 38 + i);
154         return ('\0');
155 }
156
157 static char *crypt_md5_wrapper(const char *pass_new)
158 {
159         /*
160          * Code lifted from Marek Michalkiewicz's shadow suite. (CG)
161          * removed use of static variables (AGM)
162          */
163
164         struct timeval tv;
165         MD5_CTX ctx;
166         unsigned char result[16];
167         char *cp = (char *) result;
168         unsigned char tmp[16];
169         int i;
170         char *x = NULL;
171
172         GoodMD5Init(&ctx);
173         gettimeofday(&tv, (struct timezone *) 0);
174         GoodMD5Update(&ctx, (void *) &tv, sizeof tv);
175         i = getpid();
176         GoodMD5Update(&ctx, (void *) &i, sizeof i);
177         i = clock();
178         GoodMD5Update(&ctx, (void *) &i, sizeof i);
179         GoodMD5Update(&ctx, result, sizeof result);
180         GoodMD5Final(tmp, &ctx);
181         strcpy(cp, "$1$");      /* magic for the MD5 */
182         cp += strlen(cp);
183         for (i = 0; i < 8; i++)
184                 *cp++ = i64c(tmp[i] & 077);
185         *cp = '\0';
186
187         /* no longer need cleartext */
188         x = Goodcrypt_md5(pass_new, (const char *) result);
189
190         return x;
191 }
192
193 static char *getNISserver(pam_handle_t *pamh)
194 {
195         char *master;
196         char *domainname;
197         int port, err;
198
199         if ((err = yp_get_default_domain(&domainname)) != 0) {
200                 _log_err(LOG_WARNING, pamh, "can't get local yp domain: %s\n",
201                          yperr_string(err));
202                 return NULL;
203         }
204         if ((err = yp_master(domainname, "passwd.byname", &master)) != 0) {
205                 _log_err(LOG_WARNING, pamh, "can't find the master ypserver: %s\n",
206                          yperr_string(err));
207                 return NULL;
208         }
209         port = getrpcport(master, YPPASSWDPROG, YPPASSWDPROC_UPDATE, IPPROTO_UDP);
210         if (port == 0) {
211                 _log_err(LOG_WARNING, pamh,
212                          "yppasswdd not running on NIS master host\n");
213                 return NULL;
214         }
215         if (port >= IPPORT_RESERVED) {
216                 _log_err(LOG_WARNING, pamh,
217                          "yppasswd daemon running on illegal port.\n");
218                 return NULL;
219         }
220         return master;
221 }
222
223 #ifdef WITH_SELINUX
224
225 static int _unix_run_shadow_binary(pam_handle_t *pamh, unsigned int ctrl, const char *user, const char *fromwhat, const char *towhat)
226 {
227     int retval, child, fds[2];
228     void (*sighandler)(int) = NULL;
229
230     D(("called."));
231     /* create a pipe for the password */
232     if (pipe(fds) != 0) {
233         D(("could not make pipe"));
234         return PAM_AUTH_ERR;
235     }
236
237     if (off(UNIX_NOREAP, ctrl)) {
238         /*
239          * This code arranges that the demise of the child does not cause
240          * the application to receive a signal it is not expecting - which
241          * may kill the application or worse.
242          *
243          * The "noreap" module argument is provided so that the admin can
244          * override this behavior.
245          */
246         sighandler = signal(SIGCHLD, SIG_DFL);
247     }
248
249     /* fork */
250     child = fork();
251     if (child == 0) {
252         size_t i=0;
253         struct rlimit rlim;
254         static char *envp[] = { NULL };
255         char *args[] = { NULL, NULL, NULL, NULL };
256
257         /* XXX - should really tidy up PAM here too */
258
259         close(0); close(1);
260         /* reopen stdin as pipe */
261         close(fds[1]);
262         dup2(fds[0], STDIN_FILENO);
263
264         if (getrlimit(RLIMIT_NOFILE,&rlim)==0) {
265           for (i=2; i < rlim.rlim_max; i++) {
266             if ((unsigned int)fds[0] != i)
267                    close(i);
268           }
269         }
270         /* exec binary helper */
271         args[0] = x_strdup(CHKPWD_HELPER);
272         args[1] = x_strdup(user);
273         args[2] = x_strdup("shadow");
274
275         execve(CHKPWD_HELPER, args, envp);
276
277         /* should not get here: exit with error */
278         D(("helper binary is not available"));
279         exit(PAM_AUTHINFO_UNAVAIL);
280     } else if (child > 0) {
281         /* wait for child */
282         /* if the stored password is NULL */
283         int rc=0;
284         if (fromwhat)
285           _pammodutil_write(fds[1], fromwhat, strlen(fromwhat)+1);
286         else
287           _pammodutil_write(fds[1], "", 1);
288         if (towhat) {
289           _pammodutil_write(fds[1], towhat, strlen(towhat)+1);
290         }
291         else
292           _pammodutil_write(fds[1], "", 1);
293
294         close(fds[0]);       /* close here to avoid possible SIGPIPE above */
295         close(fds[1]);
296         rc=waitpid(child, &retval, 0);  /* wait for helper to complete */
297         if (rc<0) {
298           _log_err(LOG_ERR, pamh, "unix_chkpwd waitpid returned %d: %s", rc, strerror(errno));
299           retval = PAM_AUTH_ERR;
300         } else {
301           retval = WEXITSTATUS(retval);
302         }
303     } else {
304         D(("fork failed"));
305         close(fds[0]);
306         close(fds[1]);
307         retval = PAM_AUTH_ERR;
308     }
309
310     if (sighandler != NULL) {
311         (void) signal(SIGCHLD, sighandler);   /* restore old signal handler */
312     }
313
314     return retval;
315 }
316 #endif
317
318 static int check_old_password(const char *forwho, const char *newpass)
319 {
320         static char buf[16384];
321         char *s_luser, *s_uid, *s_npas, *s_pas;
322         int retval = PAM_SUCCESS;
323         FILE *opwfile;
324
325         opwfile = fopen(OLD_PASSWORDS_FILE, "r");
326         if (opwfile == NULL)
327                 return PAM_ABORT;
328
329         while (fgets(buf, 16380, opwfile)) {
330                 if (!strncmp(buf, forwho, strlen(forwho))) {
331                         buf[strlen(buf) - 1] = '\0';
332                         s_luser = strtok(buf, ":,");
333                         s_uid = strtok(NULL, ":,");
334                         s_npas = strtok(NULL, ":,");
335                         s_pas = strtok(NULL, ":,");
336                         while (s_pas != NULL) {
337                                 char *md5pass = Goodcrypt_md5(newpass, s_pas);
338                                 if (!strcmp(md5pass, s_pas)) {
339                                         _pam_delete(md5pass);
340                                         retval = PAM_AUTHTOK_ERR;
341                                         break;
342                                 }
343                                 s_pas = strtok(NULL, ":,");
344                                 _pam_delete(md5pass);
345                         }
346                         break;
347                 }
348         }
349         fclose(opwfile);
350
351         return retval;
352 }
353
354 static int save_old_password(pam_handle_t *pamh,
355                              const char *forwho, const char *oldpass,
356                              int howmany)
357 {
358     static char buf[16384];
359     static char nbuf[16384];
360     char *s_luser, *s_uid, *s_npas, *s_pas, *pass;
361     int npas;
362     FILE *pwfile, *opwfile;
363     int err = 0;
364     int oldmask;
365     int found = 0;
366     struct passwd *pwd = NULL;
367     struct stat st;
368
369     if (howmany < 0) {
370         return PAM_SUCCESS;
371     }
372
373     if (oldpass == NULL) {
374         return PAM_SUCCESS;
375     }
376
377     oldmask = umask(077);
378
379 #ifdef WITH_SELINUX
380     if (SELINUX_ENABLED) {
381       security_context_t passwd_context=NULL;
382       if (getfilecon("/etc/passwd",&passwd_context)<0) {
383         return PAM_AUTHTOK_ERR;
384       };
385       if (getfscreatecon(&prev_context)<0) {
386         freecon(passwd_context);
387         return PAM_AUTHTOK_ERR;
388       }
389       if (setfscreatecon(passwd_context)) {
390         freecon(passwd_context);
391         freecon(prev_context);
392         return PAM_AUTHTOK_ERR;
393       }
394       freecon(passwd_context);
395     }
396 #endif
397     pwfile = fopen(OPW_TMPFILE, "w");
398     umask(oldmask);
399     if (pwfile == NULL) {
400       err = 1;
401       goto done;
402     }
403
404     opwfile = fopen(OLD_PASSWORDS_FILE, "r");
405     if (opwfile == NULL) {
406         fclose(pwfile);
407       err = 1;
408       goto done;
409     }
410
411     if (fstat(fileno(opwfile), &st) == -1) {
412         fclose(opwfile);
413         fclose(pwfile);
414         err = 1;
415         goto done;
416     }
417
418     if (fchown(fileno(pwfile), st.st_uid, st.st_gid) == -1) {
419         fclose(opwfile);
420         fclose(pwfile);
421         err = 1;
422         goto done;
423     }
424     if (fchmod(fileno(pwfile), st.st_mode) == -1) {
425         fclose(opwfile);
426         fclose(pwfile);
427         err = 1;
428         goto done;
429     }
430
431     while (fgets(buf, 16380, opwfile)) {
432         if (!strncmp(buf, forwho, strlen(forwho))) {
433             buf[strlen(buf) - 1] = '\0';
434             s_luser = strtok(buf, ":");
435             s_uid = strtok(NULL, ":");
436             s_npas = strtok(NULL, ":");
437             s_pas = strtok(NULL, ":");
438             npas = strtol(s_npas, NULL, 10) + 1;
439             while (npas > howmany) {
440                 s_pas = strpbrk(s_pas, ",");
441                 if (s_pas != NULL)
442                     s_pas++;
443                 npas--;
444             }
445             pass = crypt_md5_wrapper(oldpass);
446             if (s_pas == NULL)
447                 snprintf(nbuf, sizeof(nbuf), "%s:%s:%d:%s\n",
448                          s_luser, s_uid, npas, pass);
449             else
450                 snprintf(nbuf, sizeof(nbuf),"%s:%s:%d:%s,%s\n",
451                          s_luser, s_uid, npas, s_pas, pass);
452             _pam_delete(pass);
453             if (fputs(nbuf, pwfile) < 0) {
454                 err = 1;
455                 break;
456             }
457             found = 1;
458         } else if (fputs(buf, pwfile) < 0) {
459             err = 1;
460             break;
461         }
462     }
463     fclose(opwfile);
464
465     if (!found) {
466         pwd = _pammodutil_getpwnam(pamh, forwho);
467         if (pwd == NULL) {
468             err = 1;
469         } else {
470             pass = crypt_md5_wrapper(oldpass);
471             snprintf(nbuf, sizeof(nbuf), "%s:%d:1:%s\n",
472                      forwho, pwd->pw_uid, pass);
473             _pam_delete(pass);
474             if (fputs(nbuf, pwfile) < 0) {
475                 err = 1;
476             }
477         }
478     }
479
480     if (fclose(pwfile)) {
481         D(("error writing entries to old passwords file: %s\n",
482            strerror(errno)));
483         err = 1;
484     }
485
486 done:
487     if (!err) {
488         if (rename(OPW_TMPFILE, OLD_PASSWORDS_FILE))
489             err = 1;
490     }
491 #ifdef WITH_SELINUX
492     if (SELINUX_ENABLED) {
493       if (setfscreatecon(prev_context)) {
494         err = 1;
495       }
496       if (prev_context)
497         freecon(prev_context);
498       prev_context=NULL;
499     }
500 #endif
501     if (!err) {
502         return PAM_SUCCESS;
503     } else {
504         unlink(OPW_TMPFILE);
505         return PAM_AUTHTOK_ERR;
506     }
507 }
508
509 static int _update_passwd(pam_handle_t *pamh,
510                           const char *forwho, const char *towhat)
511 {
512     struct passwd *tmpent = NULL;
513     struct stat st;
514     FILE *pwfile, *opwfile;
515     int err = 1;
516     int oldmask;
517
518     oldmask = umask(077);
519 #ifdef WITH_SELINUX
520     if (SELINUX_ENABLED) {
521       security_context_t passwd_context=NULL;
522       if (getfilecon("/etc/passwd",&passwd_context)<0) {
523         return PAM_AUTHTOK_ERR;
524       };
525       if (getfscreatecon(&prev_context)<0) {
526         freecon(passwd_context);
527         return PAM_AUTHTOK_ERR;
528       }
529       if (setfscreatecon(passwd_context)) {
530         freecon(passwd_context);
531         freecon(prev_context);
532         return PAM_AUTHTOK_ERR;
533       }
534       freecon(passwd_context);
535     }
536 #endif
537     pwfile = fopen(PW_TMPFILE, "w");
538     umask(oldmask);
539     if (pwfile == NULL) {
540       err = 1;
541       goto done;
542     }
543
544     opwfile = fopen("/etc/passwd", "r");
545     if (opwfile == NULL) {
546         fclose(pwfile);
547         err = 1;
548         goto done;
549     }
550
551     if (fstat(fileno(opwfile), &st) == -1) {
552         fclose(opwfile);
553         fclose(pwfile);
554         err = 1;
555         goto done;
556     }
557
558     if (fchown(fileno(pwfile), st.st_uid, st.st_gid) == -1) {
559         fclose(opwfile);
560         fclose(pwfile);
561         err = 1;
562         goto done;
563     }
564     if (fchmod(fileno(pwfile), st.st_mode) == -1) {
565         fclose(opwfile);
566         fclose(pwfile);
567         err = 1;
568         goto done;
569     }
570
571     tmpent = fgetpwent(opwfile);
572     while (tmpent) {
573         if (!strcmp(tmpent->pw_name, forwho)) {
574             /* To shut gcc up */
575             union {
576                 const char *const_charp;
577                 char *charp;
578             } assigned_passwd;
579             assigned_passwd.const_charp = towhat;
580
581             tmpent->pw_passwd = assigned_passwd.charp;
582             err = 0;
583         }
584         if (putpwent(tmpent, pwfile)) {
585             D(("error writing entry to password file: %s\n", strerror(errno)));
586             err = 1;
587             break;
588         }
589         tmpent = fgetpwent(opwfile);
590     }
591     fclose(opwfile);
592
593     if (fclose(pwfile)) {
594         D(("error writing entries to password file: %s\n", strerror(errno)));
595         err = 1;
596     }
597
598 done:
599     if (!err) {
600         if (!rename(PW_TMPFILE, "/etc/passwd"))
601             _log_err(LOG_NOTICE, pamh, "password changed for %s", forwho);
602         else
603             err = 1;
604     }
605 #ifdef WITH_SELINUX
606     if (SELINUX_ENABLED) {
607       if (setfscreatecon(prev_context)) {
608         err = 1;
609       }
610       if (prev_context)
611         freecon(prev_context);
612       prev_context=NULL;
613     }
614 #endif
615     if (!err) {
616         return PAM_SUCCESS;
617     } else {
618         unlink(PW_TMPFILE);
619         return PAM_AUTHTOK_ERR;
620     }
621 }
622
623 static int _update_shadow(pam_handle_t *pamh, const char *forwho, char *towhat)
624 {
625     struct spwd *spwdent = NULL, *stmpent = NULL;
626     struct stat st;
627     FILE *pwfile, *opwfile;
628     int err = 1;
629     int oldmask;
630
631     spwdent = getspnam(forwho);
632     if (spwdent == NULL) {
633         return PAM_USER_UNKNOWN;
634     }
635     oldmask = umask(077);
636
637 #ifdef WITH_SELINUX
638     if (SELINUX_ENABLED) {
639       security_context_t shadow_context=NULL;
640       if (getfilecon("/etc/shadow",&shadow_context)<0) {
641         return PAM_AUTHTOK_ERR;
642       };
643       if (getfscreatecon(&prev_context)<0) {
644         freecon(shadow_context);
645         return PAM_AUTHTOK_ERR;
646       }
647       if (setfscreatecon(shadow_context)) {
648         freecon(shadow_context);
649         freecon(prev_context);
650         return PAM_AUTHTOK_ERR;
651       }
652       freecon(shadow_context);
653     }
654 #endif
655     pwfile = fopen(SH_TMPFILE, "w");
656     umask(oldmask);
657     if (pwfile == NULL) {
658         err = 1;
659         goto done;
660     }
661
662     opwfile = fopen("/etc/shadow", "r");
663     if (opwfile == NULL) {
664         fclose(pwfile);
665         err = 1;
666         goto done;
667     }
668
669     if (fstat(fileno(opwfile), &st) == -1) {
670         fclose(opwfile);
671         fclose(pwfile);
672         err = 1;
673         goto done;
674     }
675
676     if (fchown(fileno(pwfile), st.st_uid, st.st_gid) == -1) {
677         fclose(opwfile);
678         fclose(pwfile);
679         err = 1;
680         goto done;
681     }
682     if (fchmod(fileno(pwfile), st.st_mode) == -1) {
683         fclose(opwfile);
684         fclose(pwfile);
685         err = 1;
686         goto done;
687     }
688
689     stmpent = fgetspent(opwfile);
690     while (stmpent) {
691
692         if (!strcmp(stmpent->sp_namp, forwho)) {
693             stmpent->sp_pwdp = towhat;
694             stmpent->sp_lstchg = time(NULL) / (60 * 60 * 24);
695             err = 0;
696             D(("Set password %s for %s", stmpent->sp_pwdp, forwho));
697         }
698
699         if (putspent(stmpent, pwfile)) {
700             D(("error writing entry to shadow file: %s\n", strerror(errno)));
701             err = 1;
702             break;
703         }
704
705         stmpent = fgetspent(opwfile);
706     }
707     fclose(opwfile);
708
709     if (fclose(pwfile)) {
710         D(("error writing entries to shadow file: %s\n", strerror(errno)));
711         err = 1;
712     }
713
714  done:
715     if (!err) {
716         if (!rename(SH_TMPFILE, "/etc/shadow"))
717             _log_err(LOG_NOTICE, pamh, "password changed for %s", forwho);
718         else
719             err = 1;
720     }
721
722 #ifdef WITH_SELINUX
723     if (SELINUX_ENABLED) {
724       if (setfscreatecon(prev_context)) {
725         err = 1;
726       }
727       if (prev_context)
728         freecon(prev_context);
729       prev_context=NULL;
730     }
731 #endif
732
733     if (!err) {
734         return PAM_SUCCESS;
735     } else {
736         unlink(SH_TMPFILE);
737         return PAM_AUTHTOK_ERR;
738     }
739 }
740
741 static int _do_setpass(pam_handle_t* pamh, const char *forwho,
742                        const char *fromwhat,
743                        char *towhat, unsigned int ctrl, int remember)
744 {
745         struct passwd *pwd = NULL;
746         int retval = 0;
747         int unlocked = 0;
748         char *master = NULL;
749
750         D(("called"));
751
752         pwd = getpwnam(forwho);
753
754         if (pwd == NULL) {
755                 retval = PAM_AUTHTOK_ERR;
756                 goto done;
757         }
758
759         if (on(UNIX_NIS, ctrl) && _unix_comesfromsource(pamh, forwho, 0, 1)) {
760             if ((master=getNISserver(pamh)) != NULL) {
761                 struct timeval timeout;
762                 struct yppasswd yppwd;
763                 CLIENT *clnt;
764                 int status;
765                 int err = 0;
766
767                 /* Unlock passwd file to avoid deadlock */
768 #ifdef USE_LCKPWDF
769                 ulckpwdf();
770 #endif
771                 unlocked = 1;
772
773                 /* Initialize password information */
774                 yppwd.newpw.pw_passwd = pwd->pw_passwd;
775                 yppwd.newpw.pw_name = pwd->pw_name;
776                 yppwd.newpw.pw_uid = pwd->pw_uid;
777                 yppwd.newpw.pw_gid = pwd->pw_gid;
778                 yppwd.newpw.pw_gecos = pwd->pw_gecos;
779                 yppwd.newpw.pw_dir = pwd->pw_dir;
780                 yppwd.newpw.pw_shell = pwd->pw_shell;
781                 yppwd.oldpass = fromwhat ? strdup (fromwhat) : strdup ("");
782                 yppwd.newpw.pw_passwd = towhat;
783
784                 D(("Set password %s for %s", yppwd.newpw.pw_passwd, forwho));
785
786                 /* The yppasswd.x file said `unix authentication required',
787                  * so I added it. This is the only reason it is in here.
788                  * My yppasswdd doesn't use it, but maybe some others out there
789                  * do.                                        --okir
790                  */
791                 clnt = clnt_create(master, YPPASSWDPROG, YPPASSWDVERS, "udp");
792                 clnt->cl_auth = authunix_create_default();
793                 memset((char *) &status, '\0', sizeof(status));
794                 timeout.tv_sec = 25;
795                 timeout.tv_usec = 0;
796                 err = clnt_call(clnt, YPPASSWDPROC_UPDATE,
797                                 (xdrproc_t) xdr_yppasswd, (char *) &yppwd,
798                                 (xdrproc_t) xdr_int, (char *) &status,
799                                 timeout);
800
801                 free (yppwd.oldpass);
802
803                 if (err) {
804                         _make_remark(pamh, ctrl, PAM_TEXT_INFO,
805                                 clnt_sperrno(err));
806                 } else if (status) {
807                         D(("Error while changing NIS password.\n"));
808                 }
809                 D(("The password has%s been changed on %s.",
810                    (err || status) ? " not" : "", master));
811                 _log_err(LOG_NOTICE, pamh, "password%s changed for %s on %s",
812                          (err || status) ? " not" : "", pwd->pw_name, master);
813
814                 auth_destroy(clnt->cl_auth);
815                 clnt_destroy(clnt);
816                 if (err || status) {
817                         _make_remark(pamh, ctrl, PAM_TEXT_INFO,
818                                 _("NIS password could not be changed."));
819                         retval = PAM_TRY_AGAIN;
820                 }
821 #ifdef DEBUG
822                 sleep(5);
823 #endif
824             } else {
825                     retval = PAM_TRY_AGAIN;
826             }
827         }
828
829         if (_unix_comesfromsource(pamh, forwho, 1, 0)) {
830 #ifdef USE_LCKPWDF
831                 if(unlocked) {
832                         int i = 0;
833                         /* These values for the number of attempts and the sleep time
834                            are, of course, completely arbitrary.
835                            My reading of the PAM docs is that, once pam_chauthtok() has been
836                            called with PAM_UPDATE_AUTHTOK, we are obliged to take any
837                            reasonable steps to make sure the token is updated; so retrying
838                            for 1/10 sec. isn't overdoing it. */
839                         while((retval = lckpwdf()) != 0 && i < 100) {
840                                 usleep(1000);
841                                 i++;
842                         }
843                         if(retval != 0) {
844                                 return PAM_AUTHTOK_LOCK_BUSY;
845                         }
846                 }
847 #endif
848                 /* first, save old password */
849                 if (save_old_password(pamh, forwho, fromwhat, remember)) {
850                         retval = PAM_AUTHTOK_ERR;
851                         goto done;
852                 }
853                 if (on(UNIX_SHADOW, ctrl) || _unix_shadowed(pwd)) {
854                         retval = _update_shadow(pamh, forwho, towhat);
855 #ifdef WITH_SELINUX
856                         if (retval != PAM_SUCCESS && SELINUX_ENABLED)
857                           retval = _unix_run_shadow_binary(pamh, ctrl, forwho, fromwhat, towhat);
858 #endif
859                         if (retval == PAM_SUCCESS)
860                                 if (!_unix_shadowed(pwd))
861                                         retval = _update_passwd(pamh, forwho, "x");
862                 } else {
863                         retval = _update_passwd(pamh, forwho, towhat);
864                 }
865         }
866
867
868 done:
869 #ifdef USE_LCKPWDF
870         ulckpwdf();
871 #endif
872
873         return retval;
874 }
875
876 static int _unix_verify_shadow(pam_handle_t *pamh, const char *user, unsigned int ctrl)
877 {
878         struct passwd *pwd = NULL;      /* Password and shadow password */
879         struct spwd *spwdent = NULL;    /* file entries for the user */
880         time_t curdays;
881         int retval = PAM_SUCCESS;
882
883         /* UNIX passwords area */
884         pwd = getpwnam(user);   /* Get password file entry... */
885         if (pwd == NULL)
886                 return PAM_AUTHINFO_UNAVAIL;    /* We don't need to do the rest... */
887
888         if (_unix_shadowed(pwd)) {
889                 /* ...and shadow password file entry for this user, if shadowing
890                    is enabled */
891                 setspent();
892                 spwdent = getspnam(user);
893                 endspent();
894
895 #ifdef WITH_SELINUX
896                 if (spwdent == NULL && SELINUX_ENABLED )
897                     spwdent = _unix_run_verify_binary(pamh, ctrl, user);
898 #endif
899                 if (spwdent == NULL)
900                         return PAM_AUTHINFO_UNAVAIL;
901         } else {
902                 if (strcmp(pwd->pw_passwd,"*NP*") == 0) { /* NIS+ */
903                         uid_t save_uid;
904
905                         save_uid = geteuid();
906                         seteuid (pwd->pw_uid);
907                         spwdent = getspnam( user );
908                         seteuid (save_uid);
909
910                         if (spwdent == NULL)
911                                 return PAM_AUTHINFO_UNAVAIL;
912                 } else
913                         spwdent = NULL;
914         }
915
916         if (spwdent != NULL) {
917                 /* We have the user's information, now let's check if their account
918                    has expired (60 * 60 * 24 = number of seconds in a day) */
919
920                 if (off(UNIX__IAMROOT, ctrl)) {
921                         /* Get the current number of days since 1970 */
922                         curdays = time(NULL) / (60 * 60 * 24);
923                         if ((curdays < (spwdent->sp_lstchg + spwdent->sp_min))
924                             && (spwdent->sp_min != -1))
925                                 retval = PAM_AUTHTOK_ERR;
926                         else if ((curdays > (spwdent->sp_lstchg + spwdent->sp_max + spwdent->sp_inact))
927                                  && (spwdent->sp_max != -1) && (spwdent->sp_inact != -1)
928                                  && (spwdent->sp_lstchg != 0))
929                                 /*
930                                  * Their password change has been put off too long,
931                                  */
932                                 retval = PAM_ACCT_EXPIRED;
933                         else if ((curdays > spwdent->sp_expire) && (spwdent->sp_expire != -1)
934                                  && (spwdent->sp_lstchg != 0))
935                                 /*
936                                  * OR their account has just plain expired
937                                  */
938                                 retval = PAM_ACCT_EXPIRED;
939                 }
940         }
941         return retval;
942 }
943
944 static int _pam_unix_approve_pass(pam_handle_t * pamh
945                                   ,unsigned int ctrl
946                                   ,const char *pass_old
947                                   ,const char *pass_new)
948 {
949         const void *user;
950         const char *remark = NULL;
951         int retval = PAM_SUCCESS;
952
953         D(("&new=%p, &old=%p", pass_old, pass_new));
954         D(("new=[%s]", pass_new));
955         D(("old=[%s]", pass_old));
956
957         if (pass_new == NULL || (pass_old && !strcmp(pass_old, pass_new))) {
958                 if (on(UNIX_DEBUG, ctrl)) {
959                         _log_err(LOG_DEBUG, pamh, "bad authentication token");
960                 }
961                 _make_remark(pamh, ctrl, PAM_ERROR_MSG, pass_new == NULL ?
962                         _("No password supplied") : _("Password unchanged"));
963                 return PAM_AUTHTOK_ERR;
964         }
965         /*
966          * if one wanted to hardwire authentication token strength
967          * checking this would be the place - AGM
968          */
969
970         retval = pam_get_item(pamh, PAM_USER, &user);
971         if (retval != PAM_SUCCESS) {
972                 if (on(UNIX_DEBUG, ctrl)) {
973                         _log_err(LOG_ERR, pamh, "Can not get username");
974                         return PAM_AUTHTOK_ERR;
975                 }
976         }
977         if (off(UNIX__IAMROOT, ctrl)) {
978 #ifdef USE_CRACKLIB
979                 remark = FascistCheck (pass_new, CRACKLIB_DICTS);
980                 D(("called cracklib [%s]", remark));
981 #else
982                 if (strlen(pass_new) < 6)
983                   remark = _("You must choose a longer password");
984                 D(("length check [%s]", remark));
985 #endif
986                 if (on(UNIX_REMEMBER_PASSWD, ctrl)) {
987                         if ((retval = check_old_password(user, pass_new)) == PAM_AUTHTOK_ERR)
988                           remark = _("Password has been already used. Choose another.");
989                         if (retval == PAM_ABORT) {
990                                 _log_err(LOG_ERR, pamh, "can't open %s file to check old passwords",
991                                         OLD_PASSWORDS_FILE);
992                                 return retval;
993                         }
994                 }
995         }
996         if (remark) {
997                 _make_remark(pamh, ctrl, PAM_ERROR_MSG, remark);
998                 retval = PAM_AUTHTOK_ERR;
999         }
1000         return retval;
1001 }
1002
1003
1004 PAM_EXTERN int pam_sm_chauthtok(pam_handle_t * pamh, int flags,
1005                                 int argc, const char **argv)
1006 {
1007         unsigned int ctrl, lctrl;
1008         int retval, i;
1009         int remember = -1;
1010
1011         /* <DO NOT free() THESE> */
1012         const char *user;
1013         const void *pass_old, *pass_new;
1014         /* </DO NOT free() THESE> */
1015
1016         D(("called."));
1017
1018         ctrl = _set_ctrl(pamh, flags, &remember, argc, argv);
1019
1020         /*
1021          * First get the name of a user
1022          */
1023         retval = pam_get_user(pamh, &user, NULL);
1024         if (retval == PAM_SUCCESS) {
1025                 /*
1026                  * Various libraries at various times have had bugs related to
1027                  * '+' or '-' as the first character of a user name. Don't take
1028                  * any chances here. Require that the username starts with an
1029                  * alphanumeric character.
1030                  */
1031                 if (user == NULL || !isalnum(*user)) {
1032                         _log_err(LOG_ERR, pamh, "bad username [%s]", user);
1033                         return PAM_USER_UNKNOWN;
1034                 }
1035                 if (retval == PAM_SUCCESS && on(UNIX_DEBUG, ctrl))
1036                         _log_err(LOG_DEBUG, pamh, "username [%s] obtained",
1037                                  user);
1038         } else {
1039                 if (on(UNIX_DEBUG, ctrl))
1040                         _log_err(LOG_DEBUG, pamh,
1041                                  "password - could not identify user");
1042                 return retval;
1043         }
1044
1045         D(("Got username of %s", user));
1046
1047         /*
1048          * Before we do anything else, check to make sure that the user's
1049          * info is in one of the databases we can modify from this module,
1050          * which currently is 'files' and 'nis'.  We have to do this because
1051          * getpwnam() doesn't tell you *where* the information it gives you
1052          * came from, nor should it.  That's our job.
1053          */
1054         if (_unix_comesfromsource(pamh, user, 1, 1) == 0) {
1055                 _log_err(LOG_DEBUG, pamh,
1056                          "user \"%s\" does not exist in /etc/passwd or NIS",
1057                          user);
1058                 return PAM_USER_UNKNOWN;
1059         } else {
1060                 struct passwd *pwd;
1061                 _unix_getpwnam(pamh, user, 1, 1, &pwd);
1062                 if (pwd == NULL) {
1063                         _log_err(LOG_DEBUG, pamh,
1064                                 "user \"%s\" has corrupted passwd entry",
1065                                 user);
1066                         return PAM_USER_UNKNOWN;
1067                 }
1068                 if (!_unix_shadowed(pwd) &&
1069                     (strchr(pwd->pw_passwd, '*') != NULL)) {
1070                         _log_err(LOG_DEBUG, pamh,
1071                                 "user \"%s\" does not have modifiable password",
1072                                 user);
1073                         return PAM_USER_UNKNOWN;
1074                 }
1075         }
1076
1077         /*
1078          * This is not an AUTH module!
1079          */
1080         if (on(UNIX__NONULL, ctrl))
1081                 set(UNIX__NULLOK, ctrl);
1082
1083         if (on(UNIX__PRELIM, ctrl)) {
1084                 /*
1085                  * obtain and verify the current password (OLDAUTHTOK) for
1086                  * the user.
1087                  */
1088                 char *Announce;
1089
1090                 D(("prelim check"));
1091
1092                 if (_unix_blankpasswd(pamh, ctrl, user)) {
1093                         return PAM_SUCCESS;
1094                 } else if (off(UNIX__IAMROOT, ctrl)) {
1095
1096                         /* instruct user what is happening */
1097 #define greeting "Changing password for "
1098                         Announce = (char *) malloc(sizeof(greeting) + strlen(user));
1099                         if (Announce == NULL) {
1100                                 _log_err(LOG_CRIT, pamh,
1101                                          "password - out of memory");
1102                                 return PAM_BUF_ERR;
1103                         }
1104                         (void) strcpy(Announce, greeting);
1105                         (void) strcpy(Announce + sizeof(greeting) - 1, user);
1106 #undef greeting
1107
1108                         lctrl = ctrl;
1109                         set(UNIX__OLD_PASSWD, lctrl);
1110                         retval = _unix_read_password(pamh, lctrl
1111                                                      ,Announce
1112                                              ,"(current) UNIX password: "
1113                                                      ,NULL
1114                                                      ,_UNIX_OLD_AUTHTOK
1115                                              ,&pass_old);
1116                         free(Announce);
1117
1118                         if (retval != PAM_SUCCESS) {
1119                                 _log_err(LOG_NOTICE, pamh
1120                                  ,"password - (old) token not obtained");
1121                                 return retval;
1122                         }
1123                         /* verify that this is the password for this user */
1124
1125                         retval = _unix_verify_password(pamh, user, pass_old, ctrl);
1126                 } else {
1127                         D(("process run by root so do nothing this time around"));
1128                         pass_old = NULL;
1129                         retval = PAM_SUCCESS;   /* root doesn't have too */
1130                 }
1131
1132                 if (retval != PAM_SUCCESS) {
1133                         D(("Authentication failed"));
1134                         pass_old = NULL;
1135                         return retval;
1136                 }
1137                 retval = pam_set_item(pamh, PAM_OLDAUTHTOK, (const void *) pass_old);
1138                 pass_old = NULL;
1139                 if (retval != PAM_SUCCESS) {
1140                         _log_err(LOG_CRIT, pamh,
1141                                  "failed to set PAM_OLDAUTHTOK");
1142                 }
1143                 retval = _unix_verify_shadow(pamh,user, ctrl);
1144                 if (retval == PAM_AUTHTOK_ERR) {
1145                         if (off(UNIX__IAMROOT, ctrl))
1146                                 _make_remark(pamh, ctrl, PAM_ERROR_MSG,
1147                                              _("You must wait longer to change your password"));
1148                         else
1149                                 retval = PAM_SUCCESS;
1150                 }
1151         } else if (on(UNIX__UPDATE, ctrl)) {
1152                 /*
1153                  * tpass is used below to store the _pam_md() return; it
1154                  * should be _pam_delete()'d.
1155                  */
1156
1157                 char *tpass = NULL;
1158                 int retry = 0;
1159
1160                 /*
1161                  * obtain the proposed password
1162                  */
1163
1164                 D(("do update"));
1165
1166                 /*
1167                  * get the old token back. NULL was ok only if root [at this
1168                  * point we assume that this has already been enforced on a
1169                  * previous call to this function].
1170                  */
1171
1172                 if (off(UNIX_NOT_SET_PASS, ctrl)) {
1173                         retval = pam_get_item(pamh, PAM_OLDAUTHTOK
1174                                               ,&pass_old);
1175                 } else {
1176                         retval = pam_get_data(pamh, _UNIX_OLD_AUTHTOK
1177                                               ,&pass_old);
1178                         if (retval == PAM_NO_MODULE_DATA) {
1179                                 retval = PAM_SUCCESS;
1180                                 pass_old = NULL;
1181                         }
1182                 }
1183                 D(("pass_old [%s]", pass_old));
1184
1185                 if (retval != PAM_SUCCESS) {
1186                         _log_err(LOG_NOTICE, pamh, "user not authenticated");
1187                         return retval;
1188                 }
1189
1190                 D(("get new password now"));
1191
1192                 lctrl = ctrl;
1193
1194                 if (on(UNIX_USE_AUTHTOK, lctrl)) {
1195                         set(UNIX_USE_FIRST_PASS, lctrl);
1196                 }
1197                 retry = 0;
1198                 retval = PAM_AUTHTOK_ERR;
1199                 while ((retval != PAM_SUCCESS) && (retry++ < MAX_PASSWD_TRIES)) {
1200                         /*
1201                          * use_authtok is to force the use of a previously entered
1202                          * password -- needed for pluggable password strength checking
1203                          */
1204
1205                         retval = _unix_read_password(pamh, lctrl
1206                                                      ,NULL
1207                                              ,"Enter new UNIX password: "
1208                                             ,"Retype new UNIX password: "
1209                                                      ,_UNIX_NEW_AUTHTOK
1210                                              ,&pass_new);
1211
1212                         if (retval != PAM_SUCCESS) {
1213                                 if (on(UNIX_DEBUG, ctrl)) {
1214                                         _log_err(LOG_ALERT, pamh
1215                                                  ,"password - new password not obtained");
1216                                 }
1217                                 pass_old = NULL;        /* tidy up */
1218                                 return retval;
1219                         }
1220                         D(("returned to _unix_chauthtok"));
1221
1222                         /*
1223                          * At this point we know who the user is and what they
1224                          * propose as their new password. Verify that the new
1225                          * password is acceptable.
1226                          */
1227
1228                         if (*(const char *)pass_new == '\0') {  /* "\0" password = NULL */
1229                                 pass_new = NULL;
1230                         }
1231                         retval = _pam_unix_approve_pass(pamh, ctrl, pass_old, pass_new);
1232                 }
1233
1234                 if (retval != PAM_SUCCESS) {
1235                         _log_err(LOG_NOTICE, pamh,
1236                                  "new password not acceptable");
1237                         pass_new = pass_old = NULL;     /* tidy up */
1238                         return retval;
1239                 }
1240 #ifdef USE_LCKPWDF
1241                 /* These values for the number of attempts and the sleep time
1242                    are, of course, completely arbitrary.
1243                    My reading of the PAM docs is that, once pam_chauthtok() has been
1244                    called with PAM_UPDATE_AUTHTOK, we are obliged to take any
1245                    reasonable steps to make sure the token is updated; so retrying
1246                    for 1/10 sec. isn't overdoing it. */
1247                 i=0;
1248                 while((retval = lckpwdf()) != 0 && i < 100) {
1249                         usleep(1000);
1250                         i++;
1251                 }
1252                 if(retval != 0) {
1253                         return PAM_AUTHTOK_LOCK_BUSY;
1254                 }
1255 #endif
1256
1257                 if (pass_old) {
1258                         retval = _unix_verify_password(pamh, user, pass_old, ctrl);
1259                         if (retval != PAM_SUCCESS) {
1260                                 _log_err(LOG_NOTICE, pamh, "user password changed by another process");
1261 #ifdef USE_LCKPWDF
1262                                 ulckpwdf();
1263 #endif
1264                                 return retval;
1265                         }
1266                 }
1267
1268                 retval = _unix_verify_shadow(pamh, user, ctrl);
1269                 if (retval != PAM_SUCCESS) {
1270                         _log_err(LOG_NOTICE, pamh, "user not authenticated 2");
1271 #ifdef USE_LCKPWDF
1272                         ulckpwdf();
1273 #endif
1274                         return retval;
1275                 }
1276
1277                 retval = _pam_unix_approve_pass(pamh, ctrl, pass_old, pass_new);
1278                 if (retval != PAM_SUCCESS) {
1279                         _log_err(LOG_NOTICE, pamh,
1280                                  "new password not acceptable 2");
1281                         pass_new = pass_old = NULL;     /* tidy up */
1282 #ifdef USE_LCKPWDF
1283                         ulckpwdf();
1284 #endif
1285                         return retval;
1286                 }
1287
1288                 /*
1289                  * By reaching here we have approved the passwords and must now
1290                  * rebuild the password database file.
1291                  */
1292
1293                 /*
1294                  * First we encrypt the new password.
1295                  */
1296
1297                 if (on(UNIX_MD5_PASS, ctrl)) {
1298                         tpass = crypt_md5_wrapper(pass_new);
1299                 } else {
1300                         /*
1301                          * Salt manipulation is stolen from Rick Faith's passwd
1302                          * program.  Sorry Rick :) -- alex
1303                          */
1304
1305                         time_t tm;
1306                         char salt[3];
1307
1308                         time(&tm);
1309                         salt[0] = bin_to_ascii(tm & 0x3f);
1310                         salt[1] = bin_to_ascii((tm >> 6) & 0x3f);
1311                         salt[2] = '\0';
1312
1313                         if (off(UNIX_BIGCRYPT, ctrl) && strlen(pass_new) > 8) {
1314                                 /*
1315                                  * to avoid using the _extensions_ of the bigcrypt()
1316                                  * function we truncate the newly entered password
1317                                  * [Problems that followed from this are fixed as per
1318                                  *  Bug 521314.]
1319                                  */
1320                                 char *temp = malloc(9);
1321
1322                                 if (temp == NULL) {
1323                                         _log_err(LOG_CRIT, pamh,
1324                                                  "out of memory for password");
1325                                         pass_new = pass_old = NULL;     /* tidy up */
1326 #ifdef USE_LCKPWDF
1327                                         ulckpwdf();
1328 #endif
1329                                         return PAM_BUF_ERR;
1330                                 }
1331                                 /* copy first 8 bytes of password */
1332                                 strncpy(temp, pass_new, 8);
1333                                 temp[8] = '\0';
1334
1335                                 /* no longer need cleartext */
1336                                 tpass = bigcrypt(temp, salt);
1337
1338                                 _pam_delete(temp);      /* tidy up */
1339                         } else {
1340                                 tpass = bigcrypt(pass_new, salt);
1341                         }
1342                 }
1343
1344                 D(("password processed"));
1345
1346                 /* update the password database(s) -- race conditions..? */
1347
1348                 retval = _do_setpass(pamh, user, pass_old, tpass, ctrl,
1349                                      remember);
1350                 /* _do_setpass has called ulckpwdf for us */
1351
1352                 _pam_delete(tpass);
1353                 pass_old = pass_new = NULL;
1354         } else {                /* something has broken with the module */
1355                 _log_err(LOG_ALERT, pamh,
1356                          "password received unknown request");
1357                 retval = PAM_ABORT;
1358         }
1359
1360         D(("retval was %d", retval));
1361
1362         return retval;
1363 }
1364
1365
1366 /* static module data */
1367 #ifdef PAM_STATIC
1368 struct pam_module _pam_unix_passwd_modstruct = {
1369     "pam_unix_passwd",
1370     NULL,
1371     NULL,
1372     NULL,
1373     NULL,
1374     NULL,
1375     pam_sm_chauthtok,
1376 };
1377 #endif