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