diff --git a/libc/isystem/shadow.h b/libc/isystem/shadow.h new file mode 100644 index 000000000..5d67f4cf5 --- /dev/null +++ b/libc/isystem/shadow.h @@ -0,0 +1,4 @@ +#ifndef _SHADOW_H +#define _SHADOW_H +#include "third_party/musl/shadow.h" +#endif /* _SHADOW_H */ diff --git a/third_party/musl/fgetspent.c b/third_party/musl/fgetspent.c new file mode 100644 index 000000000..086411c8e --- /dev/null +++ b/third_party/musl/fgetspent.c @@ -0,0 +1,15 @@ +#include "pwf.internal.h" +#include + +struct spwd *fgetspent(FILE *f) +{ + static char *line; + static struct spwd sp; + size_t size = 0; + struct spwd *res = 0; + int cs; + pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cs); + if (getline(&line, &size, f) >= 0 && __parsespent(line, &sp) >= 0) res = &sp; + pthread_setcancelstate(cs, 0); + return res; +} diff --git a/third_party/musl/getspnam_r.c b/third_party/musl/getspnam_r.c new file mode 100644 index 000000000..2101a2212 --- /dev/null +++ b/third_party/musl/getspnam_r.c @@ -0,0 +1,125 @@ +#include +#include +#include +#include +#include +#include "pwf.internal.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. */ + +static long xatol(char **s) +{ + long x; + if (**s == ':' || **s == '\n') return -1; + for (x=0; **s-'0'<10U; ++*s) x=10*x+(**s-'0'); + return x; +} + +int __parsespent(char *s, struct spwd *sp) +{ + sp->sp_namp = s; + if (!(s = strchr(s, ':'))) return -1; + *s = 0; + + sp->sp_pwdp = ++s; + if (!(s = strchr(s, ':'))) return -1; + *s = 0; + + s++; sp->sp_lstchg = xatol(&s); + if (*s != ':') return -1; + + s++; sp->sp_min = xatol(&s); + if (*s != ':') return -1; + + s++; sp->sp_max = xatol(&s); + if (*s != ':') return -1; + + s++; sp->sp_warn = xatol(&s); + if (*s != ':') return -1; + + s++; sp->sp_inact = xatol(&s); + if (*s != ':') return -1; + + s++; sp->sp_expire = xatol(&s); + if (*s != ':') return -1; + + s++; sp->sp_flag = xatol(&s); + if (*s != '\n') return -1; + return 0; +} + +static void cleanup(void *p) +{ + fclose(p); +} + +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); + int skip = 0; + int cs; + int orig_errno = errno; + + *res = 0; + + /* Disallow potentially-malicious user names */ + if (*name=='.' || strchr(name, '/') || !l) + return errno = EINVAL; + + /* Buffer size must at least be able to hold name, plus some.. */ + if (size < l+100) + return errno = ERANGE; + + /* Protect against truncation */ + if (snprintf(path, sizeof path, "/etc/tcb/%s/shadow", name) >= sizeof path) + return errno = EINVAL; + + fd = open(path, O_RDONLY|O_NOFOLLOW|O_NONBLOCK|O_CLOEXEC); + if (fd >= 0) { + struct stat st = { 0 }; + errno = EINVAL; + if (fstat(fd, &st) || !S_ISREG(st.st_mode) || !(f = fdopen(fd, "rb"))) { + pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cs); + close(fd); + pthread_setcancelstate(cs, 0); + return errno; + } + } else { + if (errno != ENOENT && errno != ENOTDIR) + return errno; + f = fopen("/etc/shadow", "rbe"); + if (!f) { + if (errno != ENOENT && errno != ENOTDIR) + return errno; + return 0; + } + } + + pthread_cleanup_push(cleanup, f); + while (fgets(buf, size, f) && (k=strlen(buf))>0) { + if (skip || strncmp(name, buf, l) || buf[l]!=':') { + skip = buf[k-1] != '\n'; + continue; + } + if (buf[k-1] != '\n') { + rv = ERANGE; + break; + } + + if (__parsespent(buf, sp) < 0) continue; + *res = sp; + break; + } + pthread_cleanup_pop(1); + errno = rv ? rv : orig_errno; + return rv; +} diff --git a/third_party/musl/grp.c b/third_party/musl/grp.c index 87a395a14..0d4fc1100 100644 --- a/third_party/musl/grp.c +++ b/third_party/musl/grp.c @@ -46,8 +46,8 @@ static unsigned atou(char **s) { return x; } -static int __getgrent_a(FILE *f, struct group *gr, char **line, size_t *size, - char ***mem, size_t *nmem, struct group **res) { +int __getgrent_a(FILE *f, struct group *gr, char **line, size_t *size, + char ***mem, size_t *nmem, struct group **res) { ssize_t l; char *s, *mems; size_t i; @@ -103,9 +103,9 @@ end: return rv; } -static int __getgr_a(const char *name, gid_t gid, struct group *gr, char **buf, - size_t *size, char ***mem, size_t *nmem, - struct group **res) { +int __getgr_a(const char *name, gid_t gid, struct group *gr, char **buf, + size_t *size, char ***mem, size_t *nmem, + struct group **res) { FILE *f; int rv = 0; int cs; diff --git a/third_party/musl/lckpwdf.c b/third_party/musl/lckpwdf.c new file mode 100644 index 000000000..2feda617c --- /dev/null +++ b/third_party/musl/lckpwdf.c @@ -0,0 +1,11 @@ +#include + +int lckpwdf() +{ + return 0; +} + +int ulckpwdf() +{ + return 0; +} diff --git a/third_party/musl/putspent.c b/third_party/musl/putspent.c new file mode 100644 index 000000000..55c41bbc3 --- /dev/null +++ b/third_party/musl/putspent.c @@ -0,0 +1,13 @@ +#include +#include + +#define NUM(n) ((n) == -1 ? 0 : -1), ((n) == -1 ? 0 : (n)) +#define STR(s) ((s) ? (s) : "") + +int putspent(const struct spwd *sp, FILE *f) +{ + return fprintf(f, "%s:%s:%.*ld:%.*ld:%.*ld:%.*ld:%.*ld:%.*ld:%.*lu\n", + STR(sp->sp_namp), STR(sp->sp_pwdp), NUM(sp->sp_lstchg), + NUM(sp->sp_min), NUM(sp->sp_max), NUM(sp->sp_warn), + NUM(sp->sp_inact), NUM(sp->sp_expire), NUM(sp->sp_flag)) < 0 ? -1 : 0; +} diff --git a/third_party/musl/pwd.c b/third_party/musl/pwd.c index 0f9e7e6de..62299801d 100644 --- a/third_party/musl/pwd.c +++ b/third_party/musl/pwd.c @@ -115,7 +115,7 @@ atou(char **s) return x; } -static int +int __getpwent_a(FILE *f, struct passwd *pw, char **line, size_t *size, struct passwd **res) { @@ -161,7 +161,7 @@ __getpwent_a(FILE *f, struct passwd *pw, char **line, size_t *size, return rv; } -static int +int __getpw_a(const char *name, uid_t uid, struct passwd *pw, char **buf, size_t *size, struct passwd **res) { diff --git a/third_party/musl/pwf.internal.h b/third_party/musl/pwf.internal.h new file mode 100644 index 000000000..40f9fa3e5 --- /dev/null +++ b/third_party/musl/pwf.internal.h @@ -0,0 +1,15 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +int __getpwent_a(FILE *f, struct passwd *pw, char **line, size_t *size, struct passwd **res); +int __getpw_a(const char *name, uid_t uid, struct passwd *pw, char **buf, size_t *size, struct passwd **res); +int __getgrent_a(FILE *f, struct group *gr, char **line, size_t *size, char ***mem, size_t *nmem, struct group **res); +int __getgr_a(const char *name, gid_t gid, struct group *gr, char **buf, size_t *size, char ***mem, size_t *nmem, struct group **res); +int __parsespent(char *s, struct spwd *sp); diff --git a/third_party/musl/shadow.h b/third_party/musl/shadow.h new file mode 100644 index 000000000..b12501d9f --- /dev/null +++ b/third_party/musl/shadow.h @@ -0,0 +1,34 @@ +#ifndef COSMOPOLITAN_THIRD_PARTY_MUSL_SHADOW_H_ +#define COSMOPOLITAN_THIRD_PARTY_MUSL_SHADOW_H_ +#include "libc/stdio/internal.h" +COSMOPOLITAN_C_START_ + +#define SHADOW "/etc/shadow" + +struct spwd { + char *sp_namp; + char *sp_pwdp; + long sp_lstchg; + long sp_min; + long sp_max; + long sp_warn; + long sp_inact; + long sp_expire; + unsigned long sp_flag; +}; + +void setspent(void); +void endspent(void); +struct spwd *getspent(void); +struct spwd *fgetspent(FILE *); +struct spwd *sgetspent(const char *); +int putspent(const struct spwd *, FILE *); + +struct spwd *getspnam(const char *); +int getspnam_r(const char *, struct spwd *, char *, size_t, struct spwd **); + +int lckpwdf(void); +int ulckpwdf(void); + +COSMOPOLITAN_C_END_ +#endif /* COSMOPOLITAN_THIRD_PARTY_MUSL_SHADOW_H_ */