150276Speter#include <fcntl.h>
2174993Srafan#include <unistd.h>
350276Speter#include <sys/stat.h>
450276Speter#include <ctype.h>
550276Speter#include <pthread.h>
650276Speter#include "pwf.h"
750276Speter
850276Speter/* This implementation support Openwall-style TCB passwords in place of
950276Speter * traditional shadow, if the appropriate directories and files exist.
1050276Speter * Thus, it is careful to avoid following symlinks or blocking on fifos
1150276Speter * which a malicious user might create in place of his or her TCB shadow
1250276Speter * file. It also avoids any allocation to prevent memory-exhaustion
1350276Speter * attacks via huge TCB shadow files. */
1450276Speter
1550276Speterstatic long xatol(char **s)
1650276Speter{
1750276Speter	long x;
1850276Speter	if (**s == ':' || **s == '\n') return -1;
1950276Speter	for (x=0; **s-'0'<10U; ++*s) x=10*x+(**s-'0');
2050276Speter	return x;
2150276Speter}
2250276Speter
2350276Speterint __parsespent(char *s, struct spwd *sp)
2450276Speter{
2550276Speter	sp->sp_namp = s;
2650276Speter	if (!(s = strchr(s, ':'))) return -1;
2750276Speter	*s = 0;
2850276Speter
2950276Speter	sp->sp_pwdp = ++s;
30174993Srafan	if (!(s = strchr(s, ':'))) return -1;
3150276Speter	*s = 0;
3250276Speter
3350276Speter	s++; sp->sp_lstchg = xatol(&s);
3450276Speter	if (*s != ':') return -1;
35166124Srafan
3650276Speter	s++; sp->sp_min = xatol(&s);
3750276Speter	if (*s != ':') return -1;
3850276Speter
3950276Speter	s++; sp->sp_max = xatol(&s);
4050276Speter	if (*s != ':') return -1;
4150276Speter
4250276Speter	s++; sp->sp_warn = xatol(&s);
43174993Srafan	if (*s != ':') return -1;
4450276Speter
4550276Speter	s++; sp->sp_inact = xatol(&s);
4650276Speter	if (*s != ':') return -1;
4750276Speter
4850276Speter	s++; sp->sp_expire = xatol(&s);
4976726Speter	if (*s != ':') return -1;
5062449Speter
5150276Speter	s++; sp->sp_flag = xatol(&s);
5262449Speter	if (*s != '\n') return -1;
53166124Srafan	return 0;
5450276Speter}
55166124Srafan
5650276Speterstatic void cleanup(void *p)
57174993Srafan{
58174993Srafan	fclose(p);
59174993Srafan}
60174993Srafan
61166124Srafanint getspnam_r(const char *name, struct spwd *sp, char *buf, size_t size, struct spwd **res)
6250276Speter{
6362449Speter	char path[20+NAME_MAX];
64174993Srafan	FILE *f = 0;
65174993Srafan	int rv = 0;
6650276Speter	int fd;
67174993Srafan	size_t k, l = strlen(name);
68174993Srafan	int skip = 0;
6962449Speter	int cs;
7062449Speter
7162449Speter	*res = 0;
7250276Speter
7362449Speter	/* Disallow potentially-malicious user names */
7462449Speter	if (*name=='.' || strchr(name, '/') || !l)
7597049Speter		return EINVAL;
7697049Speter
7762449Speter	/* Buffer size must at least be able to hold name, plus some.. */
7862449Speter	if (size < l+100) return ERANGE;
7962449Speter
8062449Speter	/* Protect against truncation */
8150276Speter	if (snprintf(path, sizeof path, "/etc/tcb/%s/shadow", name) >= sizeof path)
8262449Speter		return EINVAL;
83174993Srafan
84174993Srafan	fd = open(path, O_RDONLY|O_NOFOLLOW|O_NONBLOCK|O_CLOEXEC);
8562449Speter	if (fd >= 0) {
8650276Speter		struct stat st = { 0 };
8762449Speter		errno = EINVAL;
88174993Srafan		if (fstat(fd, &st) || !S_ISREG(st.st_mode) || !(f = fdopen(fd, "rb"))) {
89174993Srafan			pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cs);
90174993Srafan			close(fd);
91174993Srafan			pthread_setcancelstate(cs, 0);
92174993Srafan			return errno;
93174993Srafan		}
9450276Speter	} else {
95166124Srafan		f = fopen("/etc/shadow", "rbe");
96174993Srafan		if (!f) return errno;
97166124Srafan	}
98174993Srafan
99174993Srafan	pthread_cleanup_push(cleanup, f);
100174993Srafan	while (fgets(buf, size, f) && (k=strlen(buf))>0) {
101166124Srafan		if (skip || strncmp(name, buf, l) || buf[l]!=':') {
102166124Srafan			skip = buf[k-1] != '\n';
103166124Srafan			continue;
104166124Srafan		}
10550276Speter		if (buf[k-1] != '\n') {
10662449Speter			rv = ERANGE;
10750276Speter			break;
108166124Srafan		}
109174993Srafan
110174993Srafan		if (__parsespent(buf, sp) < 0) continue;
111174993Srafan		*res = sp;
112174993Srafan		break;
113174993Srafan	}
114174993Srafan	pthread_cleanup_pop(1);
11550276Speter	return rv;
11662449Speter}
11750276Speter