shadow password fixes: empty fields should read as -1 not 0
[musl] / src / passwd / getspnam_r.c
1 #include <fcntl.h>
2 #include <unistd.h>
3 #include <sys/stat.h>
4 #include <ctype.h>
5 #include "pwf.h"
6
7 /* This implementation support Openwall-style TCB passwords in place of
8  * traditional shadow, if the appropriate directories and files exist.
9  * Thus, it is careful to avoid following symlinks or blocking on fifos
10  * which a malicious user might create in place of his or her TCB shadow
11  * file. It also avoids any allocation to prevent memory-exhaustion
12  * attacks via huge TCB shadow files. */
13
14 static long xatol(const char *s)
15 {
16         return isdigit(*s) ? atol(s) : -1;
17 }
18
19 int getspnam_r(const char *name, struct spwd *sp, char *buf, size_t size, struct spwd **res)
20 {
21         char path[20+NAME_MAX];
22         FILE *f = 0;
23         int rv = 0;
24         int fd;
25         size_t k, l = strlen(name);
26         char *s;
27         int skip = 0;
28
29         *res = 0;
30
31         /* Disallow potentially-malicious user names */
32         if (*name=='.' || strchr(name, '/') || !l)
33                 return EINVAL;
34
35         /* Buffer size must at least be able to hold name, plus some.. */
36         if (size < l+100) return ERANGE;
37
38         /* Protect against truncation */
39         if (snprintf(path, sizeof path, "/etc/tcb/%s/shadow", name) >= sizeof path)
40                 return EINVAL;
41
42         fd = open(path, O_RDONLY|O_NOFOLLOW|O_NONBLOCK);
43         if (fd >= 0) {
44                 struct stat st = { 0 };
45                 errno = EINVAL;
46                 if (fstat(fd, &st) || !S_ISREG(st.st_mode) || !(f = fdopen(fd, "rb"))) {
47                         close(fd);
48                         return errno;
49                 }
50         } else {
51                 f = fopen("/etc/shadow", "rb");
52                 if (!f) return errno;
53         }
54
55         while (fgets(buf, size, f) && (k=strlen(buf))>0) {
56                 if (skip || strncmp(name, buf, l)) {
57                         skip = buf[k-1] != '\n';
58                         continue;
59                 }
60                 if (buf[k-1] != '\n') {
61                         rv = ERANGE;
62                         break;
63                 }
64                 buf[k-1] = 0;
65
66                 s = buf;
67                 sp->sp_namp = s;
68                 if (!(s = strchr(s, ':'))) continue;
69
70                 *s++ = 0; sp->sp_pwdp = s;
71                 if (!(s = strchr(s, ':'))) continue;
72
73                 *s++ = 0; sp->sp_lstchg = xatol(s);
74                 if (!(s = strchr(s, ':'))) continue;
75
76                 *s++ = 0; sp->sp_min = xatol(s);
77                 if (!(s = strchr(s, ':'))) continue;
78
79                 *s++ = 0; sp->sp_max = xatol(s);
80                 if (!(s = strchr(s, ':'))) continue;
81
82                 *s++ = 0; sp->sp_warn = xatol(s);
83                 if (!(s = strchr(s, ':'))) continue;
84
85                 *s++ = 0; sp->sp_inact = xatol(s);
86                 if (!(s = strchr(s, ':'))) continue;
87
88                 *s++ = 0; sp->sp_expire = xatol(s);
89                 if (!(s = strchr(s, ':'))) continue;
90
91                 *s++ = 0; sp->sp_flag = xatol(s);
92                 *res = sp;
93                 break;
94         }
95         fclose(f);
96         return rv;
97 }