]> granicus.if.org Git - linux-pam/blob - modules/pam_unix/passverify.c
Relevant BUGIDs:
[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 #ifdef WITH_SELINUX
553     security_context_t prev_context=NULL;
554 #endif
555
556     if (howmany < 0) {
557         return PAM_SUCCESS;
558     }
559
560     if (oldpass == NULL) {
561         return PAM_SUCCESS;
562     }
563
564     oldmask = umask(077);
565
566 #ifdef WITH_SELINUX
567     if (SELINUX_ENABLED) {
568       security_context_t passwd_context=NULL;
569       if (getfilecon("/etc/passwd",&passwd_context)<0) {
570         return PAM_AUTHTOK_ERR;
571       };
572       if (getfscreatecon(&prev_context)<0) {
573         freecon(passwd_context);
574         return PAM_AUTHTOK_ERR;
575       }
576       if (setfscreatecon(passwd_context)) {
577         freecon(passwd_context);
578         freecon(prev_context);
579         return PAM_AUTHTOK_ERR;
580       }
581       freecon(passwd_context);
582     }
583 #endif
584     pwfile = fopen(OPW_TMPFILE, "w");
585     umask(oldmask);
586     if (pwfile == NULL) {
587       err = 1;
588       goto done;
589     }
590
591     opwfile = fopen(OLD_PASSWORDS_FILE, "r");
592     if (opwfile == NULL) {
593         fclose(pwfile);
594       err = 1;
595       goto done;
596     }
597
598     if (fstat(fileno(opwfile), &st) == -1) {
599         fclose(opwfile);
600         fclose(pwfile);
601         err = 1;
602         goto done;
603     }
604
605     if (fchown(fileno(pwfile), st.st_uid, st.st_gid) == -1) {
606         fclose(opwfile);
607         fclose(pwfile);
608         err = 1;
609         goto done;
610     }
611     if (fchmod(fileno(pwfile), st.st_mode) == -1) {
612         fclose(opwfile);
613         fclose(pwfile);
614         err = 1;
615         goto done;
616     }
617
618     while (fgets(buf, 16380, opwfile)) {
619         if (!strncmp(buf, forwho, strlen(forwho))) {
620             char *sptr = NULL;
621             found = 1;
622             if (howmany == 0)
623                 continue;
624             buf[strlen(buf) - 1] = '\0';
625             s_luser = strtok_r(buf, ":", &sptr);
626             s_uid = strtok_r(NULL, ":", &sptr);
627             s_npas = strtok_r(NULL, ":", &sptr);
628             s_pas = strtok_r(NULL, ":", &sptr);
629             npas = strtol(s_npas, NULL, 10) + 1;
630             while (npas > howmany) {
631                 s_pas = strpbrk(s_pas, ",");
632                 if (s_pas != NULL)
633                     s_pas++;
634                 npas--;
635             }
636             pass = crypt_md5_wrapper(oldpass);
637             if (s_pas == NULL)
638                 snprintf(nbuf, sizeof(nbuf), "%s:%s:%d:%s\n",
639                          s_luser, s_uid, npas, pass);
640             else
641                 snprintf(nbuf, sizeof(nbuf),"%s:%s:%d:%s,%s\n",
642                          s_luser, s_uid, npas, s_pas, pass);
643             _pam_delete(pass);
644             if (fputs(nbuf, pwfile) < 0) {
645                 err = 1;
646                 break;
647             }
648         } else if (fputs(buf, pwfile) < 0) {
649             err = 1;
650             break;
651         }
652     }
653     fclose(opwfile);
654
655     if (!found) {
656         pwd = getpwnam(forwho);
657         if (pwd == NULL) {
658             err = 1;
659         } else {
660             pass = crypt_md5_wrapper(oldpass);
661             snprintf(nbuf, sizeof(nbuf), "%s:%lu:1:%s\n",
662                      forwho, (unsigned long)pwd->pw_uid, pass);
663             _pam_delete(pass);
664             if (fputs(nbuf, pwfile) < 0) {
665                 err = 1;
666             }
667         }
668     }
669
670     if (fclose(pwfile)) {
671         D(("error writing entries to old passwords file: %m"));
672         err = 1;
673     }
674
675 done:
676     if (!err) {
677         if (rename(OPW_TMPFILE, OLD_PASSWORDS_FILE))
678             err = 1;
679     }
680 #ifdef WITH_SELINUX
681     if (SELINUX_ENABLED) {
682       if (setfscreatecon(prev_context)) {
683         err = 1;
684       }
685       if (prev_context)
686         freecon(prev_context);
687       prev_context=NULL;
688     }
689 #endif
690     if (!err) {
691         return PAM_SUCCESS;
692     } else {
693         unlink(OPW_TMPFILE);
694         return PAM_AUTHTOK_ERR;
695     }
696 }
697
698 #ifdef HELPER_COMPILE
699 int
700 unix_update_passwd(const char *forwho, const char *towhat)
701 #else
702 int
703 unix_update_passwd(pam_handle_t *pamh, const char *forwho, const char *towhat)
704 #endif
705 {
706     struct passwd *tmpent = NULL;
707     struct stat st;
708     FILE *pwfile, *opwfile;
709     int err = 1;
710     int oldmask;
711 #ifdef WITH_SELINUX
712     security_context_t prev_context=NULL;
713 #endif
714
715     oldmask = umask(077);
716 #ifdef WITH_SELINUX
717     if (SELINUX_ENABLED) {
718       security_context_t passwd_context=NULL;
719       if (getfilecon("/etc/passwd",&passwd_context)<0) {
720         return PAM_AUTHTOK_ERR;
721       };
722       if (getfscreatecon(&prev_context)<0) {
723         freecon(passwd_context);
724         return PAM_AUTHTOK_ERR;
725       }
726       if (setfscreatecon(passwd_context)) {
727         freecon(passwd_context);
728         freecon(prev_context);
729         return PAM_AUTHTOK_ERR;
730       }
731       freecon(passwd_context);
732     }
733 #endif
734     pwfile = fopen(PW_TMPFILE, "w");
735     umask(oldmask);
736     if (pwfile == NULL) {
737       err = 1;
738       goto done;
739     }
740
741     opwfile = fopen("/etc/passwd", "r");
742     if (opwfile == NULL) {
743         fclose(pwfile);
744         err = 1;
745         goto done;
746     }
747
748     if (fstat(fileno(opwfile), &st) == -1) {
749         fclose(opwfile);
750         fclose(pwfile);
751         err = 1;
752         goto done;
753     }
754
755     if (fchown(fileno(pwfile), st.st_uid, st.st_gid) == -1) {
756         fclose(opwfile);
757         fclose(pwfile);
758         err = 1;
759         goto done;
760     }
761     if (fchmod(fileno(pwfile), st.st_mode) == -1) {
762         fclose(opwfile);
763         fclose(pwfile);
764         err = 1;
765         goto done;
766     }
767
768     tmpent = fgetpwent(opwfile);
769     while (tmpent) {
770         if (!strcmp(tmpent->pw_name, forwho)) {
771             /* To shut gcc up */
772             union {
773                 const char *const_charp;
774                 char *charp;
775             } assigned_passwd;
776             assigned_passwd.const_charp = towhat;
777
778             tmpent->pw_passwd = assigned_passwd.charp;
779             err = 0;
780         }
781         if (putpwent(tmpent, pwfile)) {
782             D(("error writing entry to password file: %m"));
783             err = 1;
784             break;
785         }
786         tmpent = fgetpwent(opwfile);
787     }
788     fclose(opwfile);
789
790     if (fclose(pwfile)) {
791         D(("error writing entries to password file: %m"));
792         err = 1;
793     }
794
795 done:
796     if (!err) {
797         if (!rename(PW_TMPFILE, "/etc/passwd"))
798 #ifdef HELPER_COMPILE
799             helper_log_err(
800 #else
801             pam_syslog(pamh,
802 #endif
803                 LOG_NOTICE, "password changed for %s", forwho);
804         else
805             err = 1;
806     }
807 #ifdef WITH_SELINUX
808     if (SELINUX_ENABLED) {
809       if (setfscreatecon(prev_context)) {
810         err = 1;
811       }
812       if (prev_context)
813         freecon(prev_context);
814       prev_context=NULL;
815     }
816 #endif
817     if (!err) {
818         return PAM_SUCCESS;
819     } else {
820         unlink(PW_TMPFILE);
821         return PAM_AUTHTOK_ERR;
822     }
823 }
824
825 #ifdef HELPER_COMPILE
826 int
827 unix_update_shadow(const char *forwho, char *towhat)
828 #else
829 int
830 unix_update_shadow(pam_handle_t *pamh, const char *forwho, char *towhat)
831 #endif
832 {
833     struct spwd *spwdent = NULL, *stmpent = NULL;
834     struct stat st;
835     FILE *pwfile, *opwfile;
836     int err = 1;
837     int oldmask;
838 #ifdef WITH_SELINUX
839     security_context_t prev_context=NULL;
840 #endif
841
842     spwdent = getspnam(forwho);
843     if (spwdent == NULL) {
844         return PAM_USER_UNKNOWN;
845     }
846     oldmask = umask(077);
847
848 #ifdef WITH_SELINUX
849     if (SELINUX_ENABLED) {
850       security_context_t shadow_context=NULL;
851       if (getfilecon("/etc/shadow",&shadow_context)<0) {
852         return PAM_AUTHTOK_ERR;
853       };
854       if (getfscreatecon(&prev_context)<0) {
855         freecon(shadow_context);
856         return PAM_AUTHTOK_ERR;
857       }
858       if (setfscreatecon(shadow_context)) {
859         freecon(shadow_context);
860         freecon(prev_context);
861         return PAM_AUTHTOK_ERR;
862       }
863       freecon(shadow_context);
864     }
865 #endif
866     pwfile = fopen(SH_TMPFILE, "w");
867     umask(oldmask);
868     if (pwfile == NULL) {
869         err = 1;
870         goto done;
871     }
872
873     opwfile = fopen("/etc/shadow", "r");
874     if (opwfile == NULL) {
875         fclose(pwfile);
876         err = 1;
877         goto done;
878     }
879
880     if (fstat(fileno(opwfile), &st) == -1) {
881         fclose(opwfile);
882         fclose(pwfile);
883         err = 1;
884         goto done;
885     }
886
887     if (fchown(fileno(pwfile), st.st_uid, st.st_gid) == -1) {
888         fclose(opwfile);
889         fclose(pwfile);
890         err = 1;
891         goto done;
892     }
893     if (fchmod(fileno(pwfile), st.st_mode) == -1) {
894         fclose(opwfile);
895         fclose(pwfile);
896         err = 1;
897         goto done;
898     }
899
900     stmpent = fgetspent(opwfile);
901     while (stmpent) {
902
903         if (!strcmp(stmpent->sp_namp, forwho)) {
904             stmpent->sp_pwdp = towhat;
905             stmpent->sp_lstchg = time(NULL) / (60 * 60 * 24);
906             err = 0;
907             D(("Set password %s for %s", stmpent->sp_pwdp, forwho));
908         }
909
910         if (putspent(stmpent, pwfile)) {
911             D(("error writing entry to shadow file: %m"));
912             err = 1;
913             break;
914         }
915
916         stmpent = fgetspent(opwfile);
917     }
918     fclose(opwfile);
919
920     if (fclose(pwfile)) {
921         D(("error writing entries to shadow file: %m"));
922         err = 1;
923     }
924
925  done:
926     if (!err) {
927         if (!rename(SH_TMPFILE, "/etc/shadow"))
928 #ifdef HELPER_COMPILE
929             helper_log_err(
930 #else
931             pam_syslog(pamh,
932 #endif
933                 LOG_NOTICE, "password changed for %s", forwho);
934         else
935             err = 1;
936     }
937
938 #ifdef WITH_SELINUX
939     if (SELINUX_ENABLED) {
940       if (setfscreatecon(prev_context)) {
941         err = 1;
942       }
943       if (prev_context)
944         freecon(prev_context);
945       prev_context=NULL;
946     }
947 #endif
948
949     if (!err) {
950         return PAM_SUCCESS;
951     } else {
952         unlink(SH_TMPFILE);
953         return PAM_AUTHTOK_ERR;
954     }
955 }
956
957 #ifdef HELPER_COMPILE
958
959 int
960 helper_verify_password(const char *name, const char *p, int nullok)
961 {
962         struct passwd *pwd = NULL;
963         char *salt = NULL;
964         int retval;
965
966         retval = get_pwd_hash(name, &pwd, &salt);
967
968         if (pwd == NULL || salt == NULL) {
969                 helper_log_err(LOG_WARNING, "check pass; user unknown");
970                 retval = PAM_USER_UNKNOWN;
971         } else {
972                 retval = verify_pwd_hash(p, salt, nullok);
973         }
974
975         if (salt) {
976                 _pam_overwrite(salt);
977                 _pam_drop(salt);
978         }
979
980         p = NULL;               /* no longer needed here */
981
982         return retval;
983 }
984
985 void
986 helper_log_err(int err, const char *format, ...)
987 {
988         va_list args;
989
990         va_start(args, format);
991         openlog(HELPER_COMPILE, LOG_CONS | LOG_PID, LOG_AUTHPRIV);
992         vsyslog(err, format, args);
993         va_end(args);
994         closelog();
995 }
996
997 static void
998 su_sighandler(int sig)
999 {
1000 #ifndef SA_RESETHAND
1001         /* emulate the behaviour of the SA_RESETHAND flag */
1002         if ( sig == SIGILL || sig == SIGTRAP || sig == SIGBUS || sig = SIGSERV )
1003                 signal(sig, SIG_DFL);
1004 #endif
1005         if (sig > 0) {
1006                 _exit(sig);
1007         }
1008 }
1009
1010 void
1011 setup_signals(void)
1012 {
1013         struct sigaction action;        /* posix signal structure */
1014
1015         /*
1016          * Setup signal handlers
1017          */
1018         (void) memset((void *) &action, 0, sizeof(action));
1019         action.sa_handler = su_sighandler;
1020 #ifdef SA_RESETHAND
1021         action.sa_flags = SA_RESETHAND;
1022 #endif
1023         (void) sigaction(SIGILL, &action, NULL);
1024         (void) sigaction(SIGTRAP, &action, NULL);
1025         (void) sigaction(SIGBUS, &action, NULL);
1026         (void) sigaction(SIGSEGV, &action, NULL);
1027         action.sa_handler = SIG_IGN;
1028         action.sa_flags = 0;
1029         (void) sigaction(SIGTERM, &action, NULL);
1030         (void) sigaction(SIGHUP, &action, NULL);
1031         (void) sigaction(SIGINT, &action, NULL);
1032         (void) sigaction(SIGQUIT, &action, NULL);
1033 }
1034
1035 char *
1036 getuidname(uid_t uid)
1037 {
1038         struct passwd *pw;
1039         static char username[256];
1040
1041         pw = getpwuid(uid);
1042         if (pw == NULL)
1043                 return NULL;
1044
1045         strncpy(username, pw->pw_name, sizeof(username));
1046         username[sizeof(username) - 1] = '\0';
1047
1048         return username;
1049 }
1050
1051 int
1052 read_passwords(int fd, int npass, char **passwords)
1053 {
1054         int rbytes = 0;
1055         int offset = 0;
1056         int i = 0;
1057         char *pptr;
1058         while (npass > 0) {
1059                 rbytes = read(fd, passwords[i]+offset, MAXPASS-offset);
1060
1061                 if (rbytes < 0) {
1062                         if (errno == EINTR) continue;
1063                         break;
1064                 }
1065                 if (rbytes == 0)
1066                         break;
1067
1068                 while (npass > 0 && (pptr=memchr(passwords[i]+offset, '\0', rbytes))
1069                         != NULL) {
1070                         rbytes -= pptr - (passwords[i]+offset) + 1;
1071                         i++;
1072                         offset = 0;
1073                         npass--;
1074                         if (rbytes > 0) {
1075                                 if (npass > 0)
1076                                         memcpy(passwords[i], pptr+1, rbytes);
1077                                 memset(pptr+1, '\0', rbytes);
1078                         }
1079                 }
1080                 offset += rbytes;
1081         }
1082
1083         /* clear up */
1084         if (offset > 0 && npass > 0) {
1085                 memset(passwords[i], '\0', offset);
1086         }
1087
1088         return i;
1089 }
1090
1091 #endif
1092 /* ****************************************************************** *
1093  * Copyright (c) Jan Rêkorajski 1999.
1094  * Copyright (c) Andrew G. Morgan 1996-8.
1095  * Copyright (c) Alex O. Yuriev, 1996.
1096  * Copyright (c) Cristian Gafton 1996.
1097  * Copyright (c) Red Hat, Inc. 1996, 2007, 2008.
1098  *
1099  * Redistribution and use in source and binary forms, with or without
1100  * modification, are permitted provided that the following conditions
1101  * are met:
1102  * 1. Redistributions of source code must retain the above copyright
1103  *    notice, and the entire permission notice in its entirety,
1104  *    including the disclaimer of warranties.
1105  * 2. Redistributions in binary form must reproduce the above copyright
1106  *    notice, this list of conditions and the following disclaimer in the
1107  *    documentation and/or other materials provided with the distribution.
1108  * 3. The name of the author may not be used to endorse or promote
1109  *    products derived from this software without specific prior
1110  *    written permission.
1111  *
1112  * ALTERNATIVELY, this product may be distributed under the terms of
1113  * the GNU Public License, in which case the provisions of the GPL are
1114  * required INSTEAD OF the above restrictions.  (This clause is
1115  * necessary due to a potential bad interaction between the GPL and
1116  * the restrictions contained in a BSD-style copyright.)
1117  *
1118  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
1119  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
1120  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
1121  * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
1122  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
1123  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
1124  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
1125  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
1126  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
1127  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
1128  * OF THE POSSIBILITY OF SUCH DAMAGE.
1129  */