]> granicus.if.org Git - linux-pam/blob - modules/pam_unix/passverify.c
Relevant BUGIDs: 1836981
[linux-pam] / modules / pam_unix / passverify.c
1 /*
2  * Copyright information at end of file.
3  */
4 #include "config.h"
5 #include <security/_pam_macros.h>
6 #include <security/pam_modules.h>
7 #include "support.h"
8 #include <stdio.h>
9 #include <string.h>
10 #include <sys/types.h>
11 #include <unistd.h>
12 #include <pwd.h>
13 #include <shadow.h>
14 #include <syslog.h>
15 #include <stdarg.h>
16 #include <signal.h>
17 #include <errno.h>
18 #include <time.h>
19 #include <sys/time.h>
20 #include <sys/stat.h>
21 #include <fcntl.h>
22 #ifdef HAVE_CRYPT_H
23 #include <crypt.h>
24 #endif
25
26 #include "md5.h"
27 #include "bigcrypt.h"
28 #include "passverify.h"
29
30 #ifdef WITH_SELINUX
31 #include <selinux/selinux.h>
32 #define SELINUX_ENABLED is_selinux_enabled()>0
33 #else
34 #define SELINUX_ENABLED 0
35 #endif
36
37 #ifdef HELPER_COMPILE
38 #define pam_modutil_getpwnam(h,n) getpwnam(n)
39 #define pam_modutil_getspnam(h,n) getspnam(n)
40 #define pam_syslog(h,a,b,c) helper_log_err(a,b,c)
41 #else
42 #include <security/pam_modutil.h>
43 #include <security/pam_ext.h>
44 #endif
45
46 #if defined(USE_LCKPWDF) && !defined(HAVE_LCKPWDF)
47 # include "./lckpwdf.-c"
48 #endif
49
50 static void
51 strip_hpux_aging(char *hash)
52 {
53         static const char valid[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
54                 "abcdefghijklmnopqrstuvwxyz"
55                 "0123456789./";
56         if ((*hash != '$') && (strlen(hash) > 13)) {
57                 for (hash += 13; *hash != '\0'; hash++) {
58                         if (strchr(valid, *hash) == NULL) {
59                                 *hash = '\0';
60                                 break;
61                         }
62                 }
63         }
64 }
65
66 int
67 verify_pwd_hash(const char *p, char *hash, unsigned int nullok)
68 {
69         size_t hash_len;
70         char *pp = NULL;
71         int retval;
72         D(("called"));
73
74         strip_hpux_aging(hash);
75         hash_len = strlen(hash);
76         if (!hash_len) {
77                 /* the stored password is NULL */
78                 if (nullok) { /* this means we've succeeded */
79                         D(("user has empty password - access granted"));
80                         retval = PAM_SUCCESS;
81                 } else {
82                         D(("user has empty password - access denied"));
83                         retval = PAM_AUTH_ERR;
84                 }
85         } else if (!p || *hash == '*' || *hash == '!') {
86                 retval = PAM_AUTH_ERR;
87         } else {
88                 if (!strncmp(hash, "$1$", 3)) {
89                         pp = Goodcrypt_md5(p, hash);
90                         if (pp && strcmp(pp, hash) != 0) {
91                                 _pam_delete(pp);
92                                 pp = Brokencrypt_md5(p, hash);
93                         }
94                 } else if (*hash != '$' && hash_len >= 13) {
95                         pp = bigcrypt(p, hash);
96                         if (pp && hash_len == 13 && strlen(pp) > hash_len) {
97                                 _pam_overwrite(pp + hash_len);
98                         }
99                 } else {
100                         /*
101                          * Ok, we don't know the crypt algorithm, but maybe
102                          * libcrypt knows about it? We should try it.
103                          */
104 #ifdef HAVE_CRYPT_R
105                         struct crypt_data *cdata;
106                         cdata = malloc(sizeof(*cdata));
107                         if (cdata != NULL) {
108                                 cdata->initialized = 0;
109                                 pp = x_strdup(crypt_r(p, hash, cdata));
110                                 memset(cdata, '\0', sizeof(*cdata));
111                                 free(cdata);
112                         }
113 #else
114                         pp = x_strdup(crypt(p, hash));
115 #endif
116                 }
117                 p = NULL;               /* no longer needed here */
118
119                 /* the moment of truth -- do we agree with the password? */
120                 D(("comparing state of pp[%s] and salt[%s]", pp, salt));
121
122                 if (pp && strcmp(pp, hash) == 0) {
123                         retval = PAM_SUCCESS;
124                 } else {
125                         retval = PAM_AUTH_ERR;
126                 }
127         }
128         
129         if (pp)
130                 _pam_delete(pp);
131         D(("done [%d].", retval));
132
133         return retval;
134 }
135
136 int
137 is_pwd_shadowed(const struct passwd *pwd)
138 {
139         if (pwd != NULL) {
140                 if (strcmp(pwd->pw_passwd, "x") == 0) {
141                         return 1;
142                 }
143                 if ((pwd->pw_passwd[0] == '#') &&
144                     (pwd->pw_passwd[1] == '#') &&
145                     (strcmp(pwd->pw_name, pwd->pw_passwd + 2) == 0)) {
146                         return 1;
147                 }
148         }
149         return 0;
150 }
151
152 #ifdef HELPER_COMPILE
153 int
154 get_account_info(const char *name,
155         struct passwd **pwd, struct spwd **spwdent)
156 #else
157 int
158 get_account_info(pam_handle_t *pamh, const char *name,
159         struct passwd **pwd,  struct spwd **spwdent)
160 #endif
161 {
162         /* UNIX passwords area */
163         *pwd = pam_modutil_getpwnam(pamh, name);        /* Get password file entry... */
164         *spwdent = NULL;
165
166         if (*pwd != NULL) {
167                 if (strcmp((*pwd)->pw_passwd, "*NP*") == 0)
168                 { /* NIS+ */
169 #ifdef HELPER_COMPILE
170                         uid_t save_euid, save_uid;
171
172                         save_euid = geteuid();
173                         save_uid = getuid();
174                         if (save_uid == (*pwd)->pw_uid)
175                                 setreuid(save_euid, save_uid);
176                         else  {
177                                 setreuid(0, -1);
178                                 if (setreuid(-1, (*pwd)->pw_uid) == -1) {
179                                         setreuid(-1, 0);
180                                         setreuid(0, -1);
181                                         if(setreuid(-1, (*pwd)->pw_uid) == -1)
182                                                 return PAM_CRED_INSUFFICIENT;
183                                 }
184                         }
185
186                         *spwdent = pam_modutil_getspnam(pamh, name);
187                         if (save_uid == (*pwd)->pw_uid)
188                                 setreuid(save_uid, save_euid);
189                         else {
190                                 setreuid(-1, 0);
191                                 setreuid(save_uid, -1);
192                                 setreuid(-1, save_euid);
193                         }
194
195                         if (*spwdent == NULL || (*spwdent)->sp_pwdp == NULL)
196                                 return PAM_AUTHINFO_UNAVAIL;
197 #else
198                         /* we must run helper for NIS+ passwords */
199                         return PAM_UNIX_RUN_HELPER;
200 #endif
201                 } else if (is_pwd_shadowed(*pwd)) {
202                         /*
203                          * ...and shadow password file entry for this user,
204                          * if shadowing is enabled
205                          */
206 #ifndef HELPER_COMPILE
207                         if (geteuid() || SELINUX_ENABLED)
208                                 return PAM_UNIX_RUN_HELPER;
209 #endif
210                         *spwdent = pam_modutil_getspnam(pamh, name);
211                         if (*spwdent == NULL || (*spwdent)->sp_pwdp == NULL)
212                                 return PAM_AUTHINFO_UNAVAIL;
213                 }
214         } else {
215                 return PAM_USER_UNKNOWN;
216         }
217         return PAM_SUCCESS;
218 }
219
220 #ifdef HELPER_COMPILE
221 int
222 get_pwd_hash(const char *name,
223         struct passwd **pwd, char **hash)
224 #else
225 int
226 get_pwd_hash(pam_handle_t *pamh, const char *name,
227         struct passwd **pwd, char **hash)
228 #endif
229 {
230         int retval;
231         struct spwd *spwdent = NULL;
232
233 #ifdef HELPER_COMPILE
234         retval = get_account_info(name, pwd, &spwdent);
235 #else
236         retval = get_account_info(pamh, name, pwd, &spwdent);
237 #endif  
238         if (retval != PAM_SUCCESS) {
239                 return retval;
240         }
241
242         if (spwdent)
243                 *hash = x_strdup(spwdent->sp_pwdp);
244         else
245                 *hash = x_strdup((*pwd)->pw_passwd);
246         if (*hash == NULL)
247                 return PAM_BUF_ERR;
248
249         return PAM_SUCCESS;
250 }
251
252 #ifdef HELPER_COMPILE
253 int
254 check_shadow_expiry(struct spwd *spent, int *daysleft)
255 #else
256 int
257 check_shadow_expiry(pam_handle_t *pamh, struct spwd *spent, int *daysleft)
258 #endif
259 {
260         long int curdays;
261         *daysleft = -1;
262         curdays = (long int)(time(NULL) / (60 * 60 * 24));
263         D(("today is %d, last change %d", curdays, spent->sp_lstchg));
264         if ((curdays > spent->sp_expire) && (spent->sp_expire != -1)) {
265                 D(("account expired"));
266                 return PAM_ACCT_EXPIRED;
267         }
268         if (spent->sp_lstchg == 0) {
269                 D(("need a new password"));
270                 *daysleft = 0;
271                 return PAM_NEW_AUTHTOK_REQD;
272         }
273         if (curdays < spent->sp_lstchg) {
274                 pam_syslog(pamh, LOG_DEBUG,
275                          "account %s has password changed in future",
276                          spent->sp_namp);
277                 return PAM_SUCCESS;
278         }
279         if ((curdays - spent->sp_lstchg > spent->sp_max)
280             && (curdays - spent->sp_lstchg > spent->sp_inact)
281             && (curdays - spent->sp_lstchg > spent->sp_max + spent->sp_inact)
282             && (spent->sp_max != -1) && (spent->sp_inact != -1)) {
283                 *daysleft = (int)((spent->sp_lstchg + spent->sp_max) - curdays);
284                 D(("authtok expired"));
285                 return PAM_AUTHTOK_EXPIRED;
286         }
287         if ((curdays - spent->sp_lstchg > spent->sp_max) && (spent->sp_max != -1)) {
288                 D(("need a new password 2"));
289                 return PAM_NEW_AUTHTOK_REQD;
290         }
291         if ((curdays - spent->sp_lstchg > spent->sp_max - spent->sp_warn)
292             && (spent->sp_max != -1) && (spent->sp_warn != -1)) {
293                 *daysleft = (int)((spent->sp_lstchg + spent->sp_max) - curdays);
294                 D(("warn before expiry"));
295         }
296         return PAM_SUCCESS;
297
298 }
299
300 /* passwd/salt conversion macros */
301
302 #define PW_TMPFILE              "/etc/npasswd"
303 #define SH_TMPFILE              "/etc/nshadow"
304 #define OPW_TMPFILE             "/etc/security/nopasswd"
305
306 /*
307  * i64c - convert an integer to a radix 64 character
308  */
309 static int
310 i64c(int i)
311 {
312         if (i < 0)
313                 return ('.');
314         else if (i > 63)
315                 return ('z');
316         if (i == 0)
317                 return ('.');
318         if (i == 1)
319                 return ('/');
320         if (i >= 2 && i <= 11)
321                 return ('0' - 2 + i);
322         if (i >= 12 && i <= 37)
323                 return ('A' - 12 + i);
324         if (i >= 38 && i <= 63)
325                 return ('a' - 38 + i);
326         return ('\0');
327 }
328
329 /* <where> must point to a buffer of at least <length>+1 length */
330 static void
331 crypt_make_salt(char *where, int length)
332 {       
333         struct timeval tv;
334         MD5_CTX ctx;
335         unsigned char tmp[16];
336         unsigned char *src = (unsigned char *)where;
337         int i;
338 #ifdef PAM_PATH_RANDOMDEV
339         int fd;
340         int rv;
341
342         if ((rv = fd = open(PAM_PATH_RANDOMDEV, O_RDONLY)) != -1) {
343                 while ((rv = read(fd, where, length)) != length && errno == EINTR);
344                 close (fd);
345         }
346         if (rv != length) {
347 #endif
348         /*
349          * Code lifted from Marek Michalkiewicz's shadow suite. (CG)
350          * removed use of static variables (AGM)
351          *
352          * will work correctly only for length <= 16 */
353         src = tmp;
354         GoodMD5Init(&ctx);
355         gettimeofday(&tv, (struct timezone *) 0);
356         GoodMD5Update(&ctx, (void *) &tv, sizeof tv);
357         i = getpid();
358         GoodMD5Update(&ctx, (void *) &i, sizeof i);
359         i = clock();
360         GoodMD5Update(&ctx, (void *) &i, sizeof i);
361         GoodMD5Update(&ctx, src, length);
362         GoodMD5Final(tmp, &ctx);
363 #ifdef PAM_PATH_RANDOMDEV
364         }
365 #endif
366         for (i = 0; i < length; i++)
367                 *where++ = i64c(src[i] & 077);
368         *where = '\0';
369 }
370
371 char *
372 crypt_md5_wrapper(const char *pass_new)
373 {
374         unsigned char result[16];
375         char *cp = (char *) result;
376
377         cp = stpcpy(cp, "$1$");      /* magic for the MD5 */
378         crypt_make_salt(cp, 8);
379
380         /* no longer need cleartext */
381         cp = Goodcrypt_md5(pass_new, (const char *) result);
382         pass_new = NULL;
383
384         return cp;
385 }
386
387 char *
388 create_password_hash(const char *password, unsigned int ctrl, int rounds)
389 {
390         const char *algoid;
391         char salt[64]; /* contains rounds number + max 16 bytes of salt + algo id */
392         char *sp;
393
394         if (on(UNIX_MD5_PASS, ctrl)) {
395                 return crypt_md5_wrapper(password);
396         }
397         if (on(UNIX_SHA256_PASS, ctrl)) {
398                 algoid = "$5$";
399         } else if (on(UNIX_SHA512_PASS, ctrl)) {
400                 algoid = "$6$";
401         } else { /* must be crypt/bigcrypt */
402                 char tmppass[9];
403                 char *crypted;
404
405                 crypt_make_salt(salt, 2);
406                 if (off(UNIX_BIGCRYPT, ctrl) && strlen(password) > 8) {
407                         strncpy(tmppass, password, sizeof(tmppass)-1);
408                         tmppass[sizeof(tmppass)-1] = '\0';
409                         password = tmppass;
410                 }
411                 crypted = bigcrypt(password, salt);
412                 memset(tmppass, '\0', sizeof(tmppass));
413                 password = NULL;
414                 return crypted;
415         }
416
417         sp = stpcpy(salt, algoid);
418         if (on(UNIX_ALGO_ROUNDS, ctrl)) {
419                 sp += snprintf(sp, sizeof(salt) - 3, "rounds=%u$", rounds);
420         }
421         crypt_make_salt(sp, 8);
422         /* For now be conservative so the resulting hashes
423          * are not too long. 8 bytes of salt prevents dictionary
424          * attacks well enough. */
425         sp = crypt(password, salt);
426         if (strncmp(algoid, sp, strlen(algoid)) != 0) {
427         /* libc doesn't know the algorithm, use MD5 */
428                 memset(sp, '\0', strlen(sp));
429                 return crypt_md5_wrapper(password);
430         }
431         
432         return x_strdup(sp);
433 }
434
435 #ifdef WITH_SELINUX
436 int
437 unix_selinux_confined(void)
438 {
439     static int confined = -1;
440     int fd;
441     char tempfile[]="/etc/.pwdXXXXXX";
442
443     if (confined != -1)
444         return confined;
445
446     /* cannot be confined without SELinux enabled */
447     if (!SELINUX_ENABLED){
448         confined = 0;
449         return confined;
450     }
451     
452     /* let's try opening shadow read only */
453     if ((fd=open("/etc/shadow", O_RDONLY)) != -1) {
454         close(fd);
455         confined = 0;
456         return confined;
457     }
458
459     if (errno == EACCES) {
460         confined = 1;
461         return confined;
462     }
463     
464     /* shadow opening failed because of other reasons let's try 
465        creating a file in /etc */
466     if ((fd=mkstemp(tempfile)) != -1) {
467         unlink(tempfile);
468         close(fd);
469         confined = 0;
470         return confined;
471     }
472     
473     confined = 1;
474     return confined;
475 }
476
477 #else
478 int
479 unix_selinux_confined(void)
480 {
481     return 0;
482 }
483 #endif
484
485 #ifdef USE_LCKPWDF
486 int
487 lock_pwdf(void)
488 {
489         int i;
490         int retval;
491
492 #ifndef HELPER_COMPILE
493         if (unix_selinux_confined()) {
494                 return PAM_SUCCESS;
495         }
496 #endif
497         /* These values for the number of attempts and the sleep time
498            are, of course, completely arbitrary.
499            My reading of the PAM docs is that, once pam_chauthtok() has been
500            called with PAM_UPDATE_AUTHTOK, we are obliged to take any
501            reasonable steps to make sure the token is updated; so retrying
502            for 1/10 sec. isn't overdoing it. */
503         i=0;
504         while((retval = lckpwdf()) != 0 && i < 100) {
505                 usleep(1000);
506                 i++;
507         }
508         if(retval != 0) {
509                 return PAM_AUTHTOK_LOCK_BUSY;
510         }
511         return PAM_SUCCESS;
512 }
513
514 void
515 unlock_pwdf(void)
516 {
517 #ifndef HELPER_COMPILE
518         if (unix_selinux_confined()) {
519                 return;
520         }
521 #endif
522         ulckpwdf();
523 }
524 #else
525 int
526 lock_pwdf(void)
527 {
528         return PAM_SUCCESS;
529 }
530
531 void
532 unlock_pwdf(void)
533 {
534         return;
535 }
536 #endif
537
538 int
539 save_old_password(const char *forwho, const char *oldpass,
540                   int howmany)
541 {
542     static char buf[16384];
543     static char nbuf[16384];
544     char *s_luser, *s_uid, *s_npas, *s_pas, *pass;
545     int npas;
546     FILE *pwfile, *opwfile;
547     int err = 0;
548     int oldmask;
549     int found = 0;
550     struct passwd *pwd = NULL;
551     struct stat st;
552     security_context_t prev_context=NULL;
553
554     if (howmany < 0) {
555         return PAM_SUCCESS;
556     }
557
558     if (oldpass == NULL) {
559         return PAM_SUCCESS;
560     }
561
562     oldmask = umask(077);
563
564 #ifdef WITH_SELINUX
565     if (SELINUX_ENABLED) {
566       security_context_t passwd_context=NULL;
567       if (getfilecon("/etc/passwd",&passwd_context)<0) {
568         return PAM_AUTHTOK_ERR;
569       };
570       if (getfscreatecon(&prev_context)<0) {
571         freecon(passwd_context);
572         return PAM_AUTHTOK_ERR;
573       }
574       if (setfscreatecon(passwd_context)) {
575         freecon(passwd_context);
576         freecon(prev_context);
577         return PAM_AUTHTOK_ERR;
578       }
579       freecon(passwd_context);
580     }
581 #endif
582     pwfile = fopen(OPW_TMPFILE, "w");
583     umask(oldmask);
584     if (pwfile == NULL) {
585       err = 1;
586       goto done;
587     }
588
589     opwfile = fopen(OLD_PASSWORDS_FILE, "r");
590     if (opwfile == NULL) {
591         fclose(pwfile);
592       err = 1;
593       goto done;
594     }
595
596     if (fstat(fileno(opwfile), &st) == -1) {
597         fclose(opwfile);
598         fclose(pwfile);
599         err = 1;
600         goto done;
601     }
602
603     if (fchown(fileno(pwfile), st.st_uid, st.st_gid) == -1) {
604         fclose(opwfile);
605         fclose(pwfile);
606         err = 1;
607         goto done;
608     }
609     if (fchmod(fileno(pwfile), st.st_mode) == -1) {
610         fclose(opwfile);
611         fclose(pwfile);
612         err = 1;
613         goto done;
614     }
615
616     while (fgets(buf, 16380, opwfile)) {
617         if (!strncmp(buf, forwho, strlen(forwho))) {
618             char *sptr = NULL;
619             found = 1;
620             if (howmany == 0)
621                 continue;
622             buf[strlen(buf) - 1] = '\0';
623             s_luser = strtok_r(buf, ":", &sptr);
624             s_uid = strtok_r(NULL, ":", &sptr);
625             s_npas = strtok_r(NULL, ":", &sptr);
626             s_pas = strtok_r(NULL, ":", &sptr);
627             npas = strtol(s_npas, NULL, 10) + 1;
628             while (npas > howmany) {
629                 s_pas = strpbrk(s_pas, ",");
630                 if (s_pas != NULL)
631                     s_pas++;
632                 npas--;
633             }
634             pass = crypt_md5_wrapper(oldpass);
635             if (s_pas == NULL)
636                 snprintf(nbuf, sizeof(nbuf), "%s:%s:%d:%s\n",
637                          s_luser, s_uid, npas, pass);
638             else
639                 snprintf(nbuf, sizeof(nbuf),"%s:%s:%d:%s,%s\n",
640                          s_luser, s_uid, npas, s_pas, pass);
641             _pam_delete(pass);
642             if (fputs(nbuf, pwfile) < 0) {
643                 err = 1;
644                 break;
645             }
646         } else if (fputs(buf, pwfile) < 0) {
647             err = 1;
648             break;
649         }
650     }
651     fclose(opwfile);
652
653     if (!found) {
654         pwd = getpwnam(forwho);
655         if (pwd == NULL) {
656             err = 1;
657         } else {
658             pass = crypt_md5_wrapper(oldpass);
659             snprintf(nbuf, sizeof(nbuf), "%s:%lu:1:%s\n",
660                      forwho, (unsigned long)pwd->pw_uid, pass);
661             _pam_delete(pass);
662             if (fputs(nbuf, pwfile) < 0) {
663                 err = 1;
664             }
665         }
666     }
667
668     if (fclose(pwfile)) {
669         D(("error writing entries to old passwords file: %m"));
670         err = 1;
671     }
672
673 done:
674     if (!err) {
675         if (rename(OPW_TMPFILE, OLD_PASSWORDS_FILE))
676             err = 1;
677     }
678 #ifdef WITH_SELINUX
679     if (SELINUX_ENABLED) {
680       if (setfscreatecon(prev_context)) {
681         err = 1;
682       }
683       if (prev_context)
684         freecon(prev_context);
685       prev_context=NULL;
686     }
687 #endif
688     if (!err) {
689         return PAM_SUCCESS;
690     } else {
691         unlink(OPW_TMPFILE);
692         return PAM_AUTHTOK_ERR;
693     }
694 }
695
696 #ifdef HELPER_COMPILE
697 int
698 unix_update_passwd(const char *forwho, const char *towhat)
699 #else
700 int
701 unix_update_passwd(pam_handle_t *pamh, const char *forwho, const char *towhat)
702 #endif
703 {
704     struct passwd *tmpent = NULL;
705     struct stat st;
706     FILE *pwfile, *opwfile;
707     int err = 1;
708     int oldmask;
709     security_context_t prev_context=NULL;
710
711     oldmask = umask(077);
712 #ifdef WITH_SELINUX
713     if (SELINUX_ENABLED) {
714       security_context_t passwd_context=NULL;
715       if (getfilecon("/etc/passwd",&passwd_context)<0) {
716         return PAM_AUTHTOK_ERR;
717       };
718       if (getfscreatecon(&prev_context)<0) {
719         freecon(passwd_context);
720         return PAM_AUTHTOK_ERR;
721       }
722       if (setfscreatecon(passwd_context)) {
723         freecon(passwd_context);
724         freecon(prev_context);
725         return PAM_AUTHTOK_ERR;
726       }
727       freecon(passwd_context);
728     }
729 #endif
730     pwfile = fopen(PW_TMPFILE, "w");
731     umask(oldmask);
732     if (pwfile == NULL) {
733       err = 1;
734       goto done;
735     }
736
737     opwfile = fopen("/etc/passwd", "r");
738     if (opwfile == NULL) {
739         fclose(pwfile);
740         err = 1;
741         goto done;
742     }
743
744     if (fstat(fileno(opwfile), &st) == -1) {
745         fclose(opwfile);
746         fclose(pwfile);
747         err = 1;
748         goto done;
749     }
750
751     if (fchown(fileno(pwfile), st.st_uid, st.st_gid) == -1) {
752         fclose(opwfile);
753         fclose(pwfile);
754         err = 1;
755         goto done;
756     }
757     if (fchmod(fileno(pwfile), st.st_mode) == -1) {
758         fclose(opwfile);
759         fclose(pwfile);
760         err = 1;
761         goto done;
762     }
763
764     tmpent = fgetpwent(opwfile);
765     while (tmpent) {
766         if (!strcmp(tmpent->pw_name, forwho)) {
767             /* To shut gcc up */
768             union {
769                 const char *const_charp;
770                 char *charp;
771             } assigned_passwd;
772             assigned_passwd.const_charp = towhat;
773
774             tmpent->pw_passwd = assigned_passwd.charp;
775             err = 0;
776         }
777         if (putpwent(tmpent, pwfile)) {
778             D(("error writing entry to password file: %m"));
779             err = 1;
780             break;
781         }
782         tmpent = fgetpwent(opwfile);
783     }
784     fclose(opwfile);
785
786     if (fclose(pwfile)) {
787         D(("error writing entries to password file: %m"));
788         err = 1;
789     }
790
791 done:
792     if (!err) {
793         if (!rename(PW_TMPFILE, "/etc/passwd"))
794 #ifdef HELPER_COMPILE
795             helper_log_err(
796 #else
797             pam_syslog(pamh, 
798 #endif
799                 LOG_NOTICE, "password changed for %s", forwho);
800         else
801             err = 1;
802     }
803 #ifdef WITH_SELINUX
804     if (SELINUX_ENABLED) {
805       if (setfscreatecon(prev_context)) {
806         err = 1;
807       }
808       if (prev_context)
809         freecon(prev_context);
810       prev_context=NULL;
811     }
812 #endif
813     if (!err) {
814         return PAM_SUCCESS;
815     } else {
816         unlink(PW_TMPFILE);
817         return PAM_AUTHTOK_ERR;
818     }
819 }
820
821 #ifdef HELPER_COMPILE
822 int
823 unix_update_shadow(const char *forwho, char *towhat)
824 #else
825 int
826 unix_update_shadow(pam_handle_t *pamh, const char *forwho, char *towhat)
827 #endif
828 {
829     struct spwd *spwdent = NULL, *stmpent = NULL;
830     struct stat st;
831     FILE *pwfile, *opwfile;
832     int err = 1;
833     int oldmask;
834     security_context_t prev_context=NULL;
835
836     spwdent = getspnam(forwho);
837     if (spwdent == NULL) {
838         return PAM_USER_UNKNOWN;
839     }
840     oldmask = umask(077);
841
842 #ifdef WITH_SELINUX
843     if (SELINUX_ENABLED) {
844       security_context_t shadow_context=NULL;
845       if (getfilecon("/etc/shadow",&shadow_context)<0) {
846         return PAM_AUTHTOK_ERR;
847       };
848       if (getfscreatecon(&prev_context)<0) {
849         freecon(shadow_context);
850         return PAM_AUTHTOK_ERR;
851       }
852       if (setfscreatecon(shadow_context)) {
853         freecon(shadow_context);
854         freecon(prev_context);
855         return PAM_AUTHTOK_ERR;
856       }
857       freecon(shadow_context);
858     }
859 #endif
860     pwfile = fopen(SH_TMPFILE, "w");
861     umask(oldmask);
862     if (pwfile == NULL) {
863         err = 1;
864         goto done;
865     }
866
867     opwfile = fopen("/etc/shadow", "r");
868     if (opwfile == NULL) {
869         fclose(pwfile);
870         err = 1;
871         goto done;
872     }
873
874     if (fstat(fileno(opwfile), &st) == -1) {
875         fclose(opwfile);
876         fclose(pwfile);
877         err = 1;
878         goto done;
879     }
880
881     if (fchown(fileno(pwfile), st.st_uid, st.st_gid) == -1) {
882         fclose(opwfile);
883         fclose(pwfile);
884         err = 1;
885         goto done;
886     }
887     if (fchmod(fileno(pwfile), st.st_mode) == -1) {
888         fclose(opwfile);
889         fclose(pwfile);
890         err = 1;
891         goto done;
892     }
893
894     stmpent = fgetspent(opwfile);
895     while (stmpent) {
896
897         if (!strcmp(stmpent->sp_namp, forwho)) {
898             stmpent->sp_pwdp = towhat;
899             stmpent->sp_lstchg = time(NULL) / (60 * 60 * 24);
900             err = 0;
901             D(("Set password %s for %s", stmpent->sp_pwdp, forwho));
902         }
903
904         if (putspent(stmpent, pwfile)) {
905             D(("error writing entry to shadow file: %m"));
906             err = 1;
907             break;
908         }
909
910         stmpent = fgetspent(opwfile);
911     }
912     fclose(opwfile);
913
914     if (fclose(pwfile)) {
915         D(("error writing entries to shadow file: %m"));
916         err = 1;
917     }
918
919  done:
920     if (!err) {
921         if (!rename(SH_TMPFILE, "/etc/shadow"))
922 #ifdef HELPER_COMPILE
923             helper_log_err(
924 #else
925             pam_syslog(pamh, 
926 #endif
927                 LOG_NOTICE, "password changed for %s", forwho);
928         else
929             err = 1;
930     }
931
932 #ifdef WITH_SELINUX
933     if (SELINUX_ENABLED) {
934       if (setfscreatecon(prev_context)) {
935         err = 1;
936       }
937       if (prev_context)
938         freecon(prev_context);
939       prev_context=NULL;
940     }
941 #endif
942
943     if (!err) {
944         return PAM_SUCCESS;
945     } else {
946         unlink(SH_TMPFILE);
947         return PAM_AUTHTOK_ERR;
948     }
949 }
950
951 #ifdef HELPER_COMPILE
952
953 int
954 helper_verify_password(const char *name, const char *p, int nullok)
955 {
956         struct passwd *pwd = NULL;
957         char *salt = NULL;
958         int retval;
959
960         retval = get_pwd_hash(name, &pwd, &salt);
961
962         if (pwd == NULL || salt == NULL) {
963                 helper_log_err(LOG_WARNING, "check pass; user unknown");
964                 retval = PAM_USER_UNKNOWN;
965         } else {
966                 retval = verify_pwd_hash(p, salt, nullok);
967         }
968
969         if (salt) {
970                 _pam_overwrite(salt);
971                 _pam_drop(salt);
972         }
973
974         p = NULL;               /* no longer needed here */
975
976         return retval;
977 }
978
979 void
980 helper_log_err(int err, const char *format, ...)
981 {
982         va_list args;
983
984         va_start(args, format);
985         openlog(HELPER_COMPILE, LOG_CONS | LOG_PID, LOG_AUTHPRIV);
986         vsyslog(err, format, args);
987         va_end(args);
988         closelog();
989 }
990
991 static void
992 su_sighandler(int sig)
993 {
994 #ifndef SA_RESETHAND
995         /* emulate the behaviour of the SA_RESETHAND flag */
996         if ( sig == SIGILL || sig == SIGTRAP || sig == SIGBUS || sig = SIGSERV )
997                 signal(sig, SIG_DFL);
998 #endif
999         if (sig > 0) {
1000                 _exit(sig);
1001         }
1002 }
1003
1004 void
1005 setup_signals(void)
1006 {
1007         struct sigaction action;        /* posix signal structure */
1008
1009         /*
1010          * Setup signal handlers
1011          */
1012         (void) memset((void *) &action, 0, sizeof(action));
1013         action.sa_handler = su_sighandler;
1014 #ifdef SA_RESETHAND
1015         action.sa_flags = SA_RESETHAND;
1016 #endif
1017         (void) sigaction(SIGILL, &action, NULL);
1018         (void) sigaction(SIGTRAP, &action, NULL);
1019         (void) sigaction(SIGBUS, &action, NULL);
1020         (void) sigaction(SIGSEGV, &action, NULL);
1021         action.sa_handler = SIG_IGN;
1022         action.sa_flags = 0;
1023         (void) sigaction(SIGTERM, &action, NULL);
1024         (void) sigaction(SIGHUP, &action, NULL);
1025         (void) sigaction(SIGINT, &action, NULL);
1026         (void) sigaction(SIGQUIT, &action, NULL);
1027 }
1028
1029 char *
1030 getuidname(uid_t uid)
1031 {
1032         struct passwd *pw;
1033         static char username[256];
1034
1035         pw = getpwuid(uid);
1036         if (pw == NULL)
1037                 return NULL;
1038
1039         strncpy(username, pw->pw_name, sizeof(username));
1040         username[sizeof(username) - 1] = '\0';
1041
1042         return username;
1043 }
1044
1045 int
1046 read_passwords(int fd, int npass, char **passwords)
1047 {
1048         int rbytes = 0;
1049         int offset = 0;
1050         int i = 0;
1051         char *pptr;
1052         while (npass > 0) {
1053                 rbytes = read(fd, passwords[i]+offset, MAXPASS-offset);
1054
1055                 if (rbytes < 0) {
1056                         if (errno == EINTR) continue;
1057                         break;
1058                 }
1059                 if (rbytes == 0)
1060                         break;
1061
1062                 while (npass > 0 && (pptr=memchr(passwords[i]+offset, '\0', rbytes))
1063                         != NULL) {
1064                         rbytes -= pptr - (passwords[i]+offset) + 1;
1065                         i++;
1066                         offset = 0;
1067                         npass--;
1068                         if (rbytes > 0) {
1069                                 if (npass > 0)
1070                                         memcpy(passwords[i], pptr+1, rbytes);
1071                                 memset(pptr+1, '\0', rbytes);
1072                         }
1073                 }
1074                 offset += rbytes;
1075         }               
1076
1077         /* clear up */
1078         if (offset > 0 && npass > 0) {
1079                 memset(passwords[i], '\0', offset);
1080         }
1081
1082         return i;
1083 }
1084
1085 #endif
1086 /* ****************************************************************** *
1087  * Copyright (c) Jan Rêkorajski 1999.
1088  * Copyright (c) Andrew G. Morgan 1996-8.
1089  * Copyright (c) Alex O. Yuriev, 1996.
1090  * Copyright (c) Cristian Gafton 1996.
1091  * Copyright (c) Red Hat, Inc. 1996, 2007, 2008.
1092  *
1093  * Redistribution and use in source and binary forms, with or without
1094  * modification, are permitted provided that the following conditions
1095  * are met:
1096  * 1. Redistributions of source code must retain the above copyright
1097  *    notice, and the entire permission notice in its entirety,
1098  *    including the disclaimer of warranties.
1099  * 2. Redistributions in binary form must reproduce the above copyright
1100  *    notice, this list of conditions and the following disclaimer in the
1101  *    documentation and/or other materials provided with the distribution.
1102  * 3. The name of the author may not be used to endorse or promote
1103  *    products derived from this software without specific prior
1104  *    written permission.
1105  *
1106  * ALTERNATIVELY, this product may be distributed under the terms of
1107  * the GNU Public License, in which case the provisions of the GPL are
1108  * required INSTEAD OF the above restrictions.  (This clause is
1109  * necessary due to a potential bad interaction between the GPL and
1110  * the restrictions contained in a BSD-style copyright.)
1111  *
1112  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
1113  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
1114  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
1115  * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
1116  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
1117  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
1118  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
1119  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
1120  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
1121  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
1122  * OF THE POSSIBILITY OF SUCH DAMAGE.
1123  */