diff options
Diffstat (limited to 'src/passwd/getspnam_r.c')
-rw-r--r-- | src/passwd/getspnam_r.c | 89 |
1 files changed, 89 insertions, 0 deletions
diff --git a/src/passwd/getspnam_r.c b/src/passwd/getspnam_r.c new file mode 100644 index 0000000..1dd39ce --- /dev/null +++ b/src/passwd/getspnam_r.c @@ -0,0 +1,89 @@ +#include <fcntl.h> +#include <unistd.h> +#include "pwf.h" + +/* This implementation support Openwall-style TCB passwords in place of + * traditional shadow, if the appropriate directories and files exist. + * Thus, it is careful to avoid following symlinks or blocking on fifos + * which a malicious user might create in place of his or her TCB shadow + * file. It also avoids any allocation to prevent memory-exhaustion + * attacks via huge TCB shadow files. */ + +int getspnam_r(const char *name, struct spwd *sp, char *buf, size_t size, struct spwd **res) +{ + char path[20+NAME_MAX]; + FILE *f = 0; + int rv = 0; + int fd; + size_t k, l = strlen(name); + char *s; + int skip = 0; + + *res = 0; + + /* Disallow potentially-malicious user names */ + if (*name=='.' || strchr(name, '/') || !l) + return EINVAL; + + /* Buffer size must at least be able to hold name, plus some.. */ + if (size < l+100) return ERANGE; + + /* Protect against truncation */ + if (snprintf(path, sizeof path, "/etc/tcb/%s/shadow", name) >= sizeof path) + return EINVAL; + + fd = open(path, O_RDONLY|O_NOFOLLOW|O_NONBLOCK); + if (fd >= 0) { + f = fdopen(fd, "rb"); + if (!f) { + close(fd); + return errno; + } + } else { + f = fopen("/etc/shadow", "rb"); + if (!f) return errno; + } + + while (fgets(buf, size, f) && (k=strlen(buf))>0) { + if (skip || strncmp(name, buf, l)) { + skip = buf[k-1] != '\n'; + continue; + } + if (buf[k-1] != '\n') { + rv = ERANGE; + break; + } + buf[k-1] = 0; + + s = buf; + sp->sp_namp = s; + if (!(s = strchr(s, ':'))) continue; + + *s++ = 0; sp->sp_pwdp = s; + if (!(s = strchr(s, ':'))) continue; + + *s++ = 0; sp->sp_lstchg = atol(s); + if (!(s = strchr(s, ':'))) continue; + + *s++ = 0; sp->sp_min = atol(s); + if (!(s = strchr(s, ':'))) continue; + + *s++ = 0; sp->sp_max = atol(s); + if (!(s = strchr(s, ':'))) continue; + + *s++ = 0; sp->sp_warn = atol(s); + if (!(s = strchr(s, ':'))) continue; + + *s++ = 0; sp->sp_inact = atol(s); + if (!(s = strchr(s, ':'))) continue; + + *s++ = 0; sp->sp_expire = atol(s); + if (!(s = strchr(s, ':'))) continue; + + *s++ = 0; sp->sp_flag = atol(s); + *res = sp; + break; + } + fclose(f); + return rv; +} |