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