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