github.com/afumu/libc@v0.0.6/musl/src/passwd/getspnam_r.c (about) 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(char **s) 16 { 17 long x; 18 if (**s == ':' || **s == '\n') return -1; 19 for (x=0; **s-'0'<10U; ++*s) x=10*x+(**s-'0'); 20 return x; 21 } 22 23 int __parsespent(char *s, struct spwd *sp) 24 { 25 sp->sp_namp = s; 26 if (!(s = strchr(s, ':'))) return -1; 27 *s = 0; 28 29 sp->sp_pwdp = ++s; 30 if (!(s = strchr(s, ':'))) return -1; 31 *s = 0; 32 33 s++; sp->sp_lstchg = xatol(&s); 34 if (*s != ':') return -1; 35 36 s++; sp->sp_min = xatol(&s); 37 if (*s != ':') return -1; 38 39 s++; sp->sp_max = xatol(&s); 40 if (*s != ':') return -1; 41 42 s++; sp->sp_warn = xatol(&s); 43 if (*s != ':') return -1; 44 45 s++; sp->sp_inact = xatol(&s); 46 if (*s != ':') return -1; 47 48 s++; sp->sp_expire = xatol(&s); 49 if (*s != ':') return -1; 50 51 s++; sp->sp_flag = xatol(&s); 52 if (*s != '\n') return -1; 53 return 0; 54 } 55 56 static void cleanup(void *p) 57 { 58 fclose(p); 59 } 60 61 int getspnam_r(const char *name, struct spwd *sp, char *buf, size_t size, struct spwd **res) 62 { 63 char path[20+NAME_MAX]; 64 FILE *f = 0; 65 int rv = 0; 66 int fd; 67 size_t k, l = strlen(name); 68 int skip = 0; 69 int cs; 70 int orig_errno = errno; 71 72 *res = 0; 73 74 /* Disallow potentially-malicious user names */ 75 if (*name=='.' || strchr(name, '/') || !l) 76 return errno = EINVAL; 77 78 /* Buffer size must at least be able to hold name, plus some.. */ 79 if (size < l+100) 80 return errno = ERANGE; 81 82 /* Protect against truncation */ 83 if (snprintf(path, sizeof path, "/etc/tcb/%s/shadow", name) >= sizeof path) 84 return errno = EINVAL; 85 86 fd = open(path, O_RDONLY|O_NOFOLLOW|O_NONBLOCK|O_CLOEXEC); 87 if (fd >= 0) { 88 struct stat st = { 0 }; 89 errno = EINVAL; 90 if (fstat(fd, &st) || !S_ISREG(st.st_mode) || !(f = fdopen(fd, "rb"))) { 91 pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cs); 92 close(fd); 93 pthread_setcancelstate(cs, 0); 94 return errno; 95 } 96 } else { 97 if (errno != ENOENT && errno != ENOTDIR) 98 return errno; 99 f = fopen("/etc/shadow", "rbe"); 100 if (!f) { 101 if (errno != ENOENT && errno != ENOTDIR) 102 return errno; 103 return 0; 104 } 105 } 106 107 pthread_cleanup_push(cleanup, f); 108 while (fgets(buf, size, f) && (k=strlen(buf))>0) { 109 if (skip || strncmp(name, buf, l) || buf[l]!=':') { 110 skip = buf[k-1] != '\n'; 111 continue; 112 } 113 if (buf[k-1] != '\n') { 114 rv = ERANGE; 115 break; 116 } 117 118 if (__parsespent(buf, sp) < 0) continue; 119 *res = sp; 120 break; 121 } 122 pthread_cleanup_pop(1); 123 errno = rv ? rv : orig_errno; 124 return rv; 125 }