xref: /plan9/sys/src/cmd/auth/secstore/secuser.c (revision eed6406f36d695b1749e6b99b0ebfb624403dfc7)
19a747e4fSDavid du Colombier #include <u.h>
29a747e4fSDavid du Colombier #include <libc.h>
39a747e4fSDavid du Colombier #include <mp.h>
49a747e4fSDavid du Colombier #include <libsec.h>
59a747e4fSDavid du Colombier #include "SConn.h"
69a747e4fSDavid du Colombier #include "secstore.h"
79a747e4fSDavid du Colombier 
89a747e4fSDavid du Colombier int verbose;
99a747e4fSDavid du Colombier 
109a747e4fSDavid du Colombier static void userinput(char *, int);
119a747e4fSDavid du Colombier 
12f5794baaSDavid du Colombier static void
13f5794baaSDavid du Colombier ensure_exists(char *f, ulong perm)
14f5794baaSDavid du Colombier {
15f5794baaSDavid du Colombier 	int fd;
16f5794baaSDavid du Colombier 
17f5794baaSDavid du Colombier 	if(access(f, AEXIST) >= 0)
18f5794baaSDavid du Colombier 		return;
19f5794baaSDavid du Colombier 	if(verbose)
20f5794baaSDavid du Colombier 		fprint(2,"first time setup for secstore: create %s %lo\n", f, perm);
21f5794baaSDavid du Colombier 	fd = create(f, OREAD, perm);
22f5794baaSDavid du Colombier 	if(fd < 0){
23d854de59SDavid du Colombier 		fprint(2, "secuser: unable to create %s\n", f);
24f5794baaSDavid du Colombier 		exits("secstored directories");
25f5794baaSDavid du Colombier 	}
26f5794baaSDavid du Colombier 	close(fd);
27f5794baaSDavid du Colombier }
28f5794baaSDavid du Colombier 
29f5794baaSDavid du Colombier 
30b27b55e2SDavid du Colombier int
319a747e4fSDavid du Colombier main(int argc, char **argv)
329a747e4fSDavid du Colombier {
339a747e4fSDavid du Colombier 	int isnew;
34b27b55e2SDavid du Colombier 	char *id, buf[Maxmsg], home[Maxmsg], prompt[100], *hexHi;
35b27b55e2SDavid du Colombier 	char *pass, *passck;
369a747e4fSDavid du Colombier 	long expsecs;
379a747e4fSDavid du Colombier 	mpint *H = mpnew(0), *Hi = mpnew(0);
389a747e4fSDavid du Colombier 	PW *pw;
399a747e4fSDavid du Colombier 	Tm *tm;
409a747e4fSDavid du Colombier 
419a747e4fSDavid du Colombier 	ARGBEGIN{
429a747e4fSDavid du Colombier 	case 'v':
439a747e4fSDavid du Colombier 		verbose++;
449a747e4fSDavid du Colombier 		break;
459a747e4fSDavid du Colombier 	}ARGEND;
469a747e4fSDavid du Colombier 	if(argc!=1){
47f5794baaSDavid du Colombier 		print("usage: secuser [-v] <user>\n");
489a747e4fSDavid du Colombier 		exits("usage");
499a747e4fSDavid du Colombier 	}
509a747e4fSDavid du Colombier 
51f5794baaSDavid du Colombier 	ensure_exists(SECSTORE_DIR, DMDIR|0755L);
52f5794baaSDavid du Colombier 	snprint(home, sizeof(home), "%s/who", SECSTORE_DIR);
53f5794baaSDavid du Colombier 	ensure_exists(home, DMDIR|0755L);
54f5794baaSDavid du Colombier 	snprint(home, sizeof(home), "%s/store", SECSTORE_DIR);
55f5794baaSDavid du Colombier 	ensure_exists(home, DMDIR|0700L);
56f5794baaSDavid du Colombier 
579a747e4fSDavid du Colombier 	id = argv[0];
589a747e4fSDavid du Colombier 	if(verbose)
599a747e4fSDavid du Colombier 		fprint(2,"secuser %s\n", id);
60369036d9SDavid du Colombier 	if((pw = getPW(id,1)) == nil){
619a747e4fSDavid du Colombier 		isnew = 1;
62369036d9SDavid du Colombier 		print("new account (because %s/%s %r)\n", SECSTORE_DIR, id);
639a747e4fSDavid du Colombier 		pw = emalloc(sizeof(*pw));
649a747e4fSDavid du Colombier 		pw->id = estrdup(id);
659a747e4fSDavid du Colombier 		snprint(home, sizeof(home), "%s/store/%s", SECSTORE_DIR, id);
669a747e4fSDavid du Colombier 		if(access(home, AEXIST) == 0){
679a747e4fSDavid du Colombier 			print("new user, but directory %s already exists\n", home);
689a747e4fSDavid du Colombier 			exits(home);
699a747e4fSDavid du Colombier 		}
7019dc9ffeSDavid du Colombier 	}else{
719a747e4fSDavid du Colombier 		isnew = 0;
7219dc9ffeSDavid du Colombier 	}
739a747e4fSDavid du Colombier 
749a747e4fSDavid du Colombier 	/* get main password for id */
759a747e4fSDavid du Colombier 	for(;;){
769a747e4fSDavid du Colombier 		if(isnew)
779a747e4fSDavid du Colombier 			snprint(prompt, sizeof(prompt), "%s password: ", id);
789a747e4fSDavid du Colombier 		else
799a747e4fSDavid du Colombier 			snprint(prompt, sizeof(prompt), "%s password [default = don't change]: ", id);
80c65b13b8SDavid du Colombier 		pass = getpassm(prompt);
81b27b55e2SDavid du Colombier 		if(pass == nil){
82c65b13b8SDavid du Colombier 			print("getpassm failed\n");
83c65b13b8SDavid du Colombier 			exits("getpassm failed");
849a747e4fSDavid du Colombier 		}
859a747e4fSDavid du Colombier 		if(verbose)
86b27b55e2SDavid du Colombier 			print("%ld characters\n", strlen(pass));
87b27b55e2SDavid du Colombier 		if(pass[0] == '\0' && isnew == 0)
889a747e4fSDavid du Colombier 			break;
89b27b55e2SDavid du Colombier 		if(strlen(pass) >= 7)
909a747e4fSDavid du Colombier 			break;
919a747e4fSDavid du Colombier 		print("password must be at least 7 characters\n");
929a747e4fSDavid du Colombier 	}
939a747e4fSDavid du Colombier 
94b27b55e2SDavid du Colombier 	if(pass[0] != '\0'){
959a747e4fSDavid du Colombier 		snprint(prompt, sizeof(prompt), "retype password: ");
969a747e4fSDavid du Colombier 		if(verbose)
979a747e4fSDavid du Colombier 			print("confirming...\n");
98c65b13b8SDavid du Colombier 		passck = getpassm(prompt);
99b27b55e2SDavid du Colombier 		if(passck == nil){
100c65b13b8SDavid du Colombier 			print("getpassm failed\n");
101c65b13b8SDavid du Colombier 			exits("getpassm failed");
1029a747e4fSDavid du Colombier 		}
103b27b55e2SDavid du Colombier 		if(strcmp(pass, passck) != 0){
1049a747e4fSDavid du Colombier 			print("passwords didn't match\n");
1059a747e4fSDavid du Colombier 			exits("no match");
1069a747e4fSDavid du Colombier 		}
107b27b55e2SDavid du Colombier 		memset(passck, 0, strlen(passck));
108b27b55e2SDavid du Colombier 		free(passck);
109b27b55e2SDavid du Colombier 		hexHi = PAK_Hi(id, pass, H, Hi);
110b27b55e2SDavid du Colombier 		memset(pass, 0, strlen(pass));
111b27b55e2SDavid du Colombier 		free(pass);
1129a747e4fSDavid du Colombier 		free(hexHi);
1139a747e4fSDavid du Colombier 		mpfree(H);
1149a747e4fSDavid du Colombier 		pw->Hi = Hi;
1159a747e4fSDavid du Colombier 	}
1169a747e4fSDavid du Colombier 
1179a747e4fSDavid du Colombier 	/* get expiration time (midnight of date specified) */
1189a747e4fSDavid du Colombier 	if(isnew)
1199a747e4fSDavid du Colombier 		expsecs = time(0) + 365*24*60*60;
1209a747e4fSDavid du Colombier 	else
1219a747e4fSDavid du Colombier 		expsecs = pw->expire;
1229a747e4fSDavid du Colombier 
1239a747e4fSDavid du Colombier 	for(;;){
1249a747e4fSDavid du Colombier 		tm = localtime(expsecs);
1259a747e4fSDavid du Colombier 		print("expires [DDMMYYYY, default = %2.2d%2.2d%4.4d]: ",
1269a747e4fSDavid du Colombier 				tm->mday, tm->mon, tm->year+1900);
1279a747e4fSDavid du Colombier 		userinput(buf, sizeof(buf));
1289a747e4fSDavid du Colombier 		if(strlen(buf) == 0)
1299a747e4fSDavid du Colombier 			break;
1309a747e4fSDavid du Colombier 		if(strlen(buf) != 8){
1319a747e4fSDavid du Colombier 			print("!bad date format: %s\n", buf);
1329a747e4fSDavid du Colombier 			continue;
1339a747e4fSDavid du Colombier 		}
1349a747e4fSDavid du Colombier 		tm->mday = (buf[0]-'0')*10 + (buf[1]-'0');
1359a747e4fSDavid du Colombier 		if(tm->mday > 31 || tm->mday < 1){
1369a747e4fSDavid du Colombier 			print("!bad day of month: %d\n", tm->mday);
1379a747e4fSDavid du Colombier 			continue;
1389a747e4fSDavid du Colombier 		}
1399a747e4fSDavid du Colombier 		tm->mon = (buf[2]-'0')*10 + (buf[3]-'0') - 1;
1409a747e4fSDavid du Colombier 		if(tm->mon > 11 || tm->mday < 0){
1419a747e4fSDavid du Colombier 			print("!bad month: %d\n", tm->mon + 1);
1429a747e4fSDavid du Colombier 			continue;
1439a747e4fSDavid du Colombier 		}
1449a747e4fSDavid du Colombier 		tm->year = atoi(buf+4) - 1900;
1459a747e4fSDavid du Colombier 		if(tm->year < 70){
1469a747e4fSDavid du Colombier 			print("!bad year: %d\n", tm->year + 1900);
1479a747e4fSDavid du Colombier 			continue;
1489a747e4fSDavid du Colombier 		}
1499a747e4fSDavid du Colombier 		tm->sec = 59;
1509a747e4fSDavid du Colombier 		tm->min = 59;
1519a747e4fSDavid du Colombier 		tm->hour = 23;
1526b6b9ac8SDavid du Colombier 		tm->yday = 0;
1539a747e4fSDavid du Colombier 		expsecs = tm2sec(tm);
1549a747e4fSDavid du Colombier 		break;
1559a747e4fSDavid du Colombier 	}
1569a747e4fSDavid du Colombier 	pw->expire = expsecs;
1579a747e4fSDavid du Colombier 
1589a747e4fSDavid du Colombier 	/* failed logins */
1599a747e4fSDavid du Colombier 	if(pw->failed != 0 )
1609a747e4fSDavid du Colombier 		print("clearing %d failed login attempts\n", pw->failed);
1619a747e4fSDavid du Colombier 	pw->failed = 0;
1629a747e4fSDavid du Colombier 
1639a747e4fSDavid du Colombier 	/* status bits */
1649a747e4fSDavid du Colombier 	if(isnew)
1659a747e4fSDavid du Colombier 		pw->status = Enabled;
1669a747e4fSDavid du Colombier 	for(;;){
1679a747e4fSDavid du Colombier 		print("Enabled or Disabled [default %s]: ",
1689a747e4fSDavid du Colombier 			(pw->status & Enabled) ? "Enabled" : "Disabled" );
1699a747e4fSDavid du Colombier 		userinput(buf, sizeof(buf));
1709a747e4fSDavid du Colombier 		if(strlen(buf) == 0)
1719a747e4fSDavid du Colombier 			break;
1729a747e4fSDavid du Colombier 		if(buf[0]=='E' || buf[0]=='e'){
1739a747e4fSDavid du Colombier 			pw->status |= Enabled;
1749a747e4fSDavid du Colombier 			break;
1759a747e4fSDavid du Colombier 		}
1769a747e4fSDavid du Colombier 		if(buf[0]=='D' || buf[0]=='d'){
1779a747e4fSDavid du Colombier 			pw->status = pw->status & ~Enabled;
1789a747e4fSDavid du Colombier 			break;
1799a747e4fSDavid du Colombier 		}
1809a747e4fSDavid du Colombier 	}
1819a747e4fSDavid du Colombier 	for(;;){
1829a747e4fSDavid du Colombier 		print("require STA? [default %s]: ",
1839a747e4fSDavid du Colombier 			(pw->status & STA) ? "yes" : "no" );
1849a747e4fSDavid du Colombier 		userinput(buf, sizeof(buf));
1859a747e4fSDavid du Colombier 		if(strlen(buf) == 0)
1869a747e4fSDavid du Colombier 			break;
1879a747e4fSDavid du Colombier 		if(buf[0]=='Y' || buf[0]=='y'){
1889a747e4fSDavid du Colombier 			pw->status |= STA;
1899a747e4fSDavid du Colombier 			break;
1909a747e4fSDavid du Colombier 		}
1919a747e4fSDavid du Colombier 		if(buf[0]=='N' || buf[0]=='n'){
1929a747e4fSDavid du Colombier 			pw->status = pw->status & ~STA;
1939a747e4fSDavid du Colombier 			break;
1949a747e4fSDavid du Colombier 		}
1959a747e4fSDavid du Colombier 	}
1969a747e4fSDavid du Colombier 
1979a747e4fSDavid du Colombier 	/* free form field */
1989a747e4fSDavid du Colombier 	if(isnew)
1999a747e4fSDavid du Colombier 		pw->other = nil;
2009a747e4fSDavid du Colombier 	print("comments [default = %s]: ", (pw->other == nil) ? "" : pw->other);
2019a747e4fSDavid du Colombier 	userinput(buf, 72);  /* 72 comes from password.h */
2029a747e4fSDavid du Colombier 	if(buf[0])
2039a747e4fSDavid du Colombier 		if((pw->other = strdup(buf)) == nil)
2049a747e4fSDavid du Colombier 			sysfatal("strdup");
2059a747e4fSDavid du Colombier 
2069a747e4fSDavid du Colombier 	syslog(0, LOG, "CHANGELOGIN for '%s'", pw->id);
20719dc9ffeSDavid du Colombier 	if(putPW(pw) < 0){
2089a747e4fSDavid du Colombier 		print("error writing entry: %r\n");
20919dc9ffeSDavid du Colombier 		exits("can't write password file");
21019dc9ffeSDavid du Colombier 	}else{
2119a747e4fSDavid du Colombier 		print("change written\n");
21219dc9ffeSDavid du Colombier 		if(isnew && create(home, OREAD, DMDIR | 0775L) < 0){
21319dc9ffeSDavid du Colombier 			print("unable to create %s: %r\n", home);
21419dc9ffeSDavid du Colombier 			exits(home);
21519dc9ffeSDavid du Colombier 		}
21619dc9ffeSDavid du Colombier 	}
2179a747e4fSDavid du Colombier 
2189a747e4fSDavid du Colombier 	exits("");
219b27b55e2SDavid du Colombier 	return 1;  /* keep  other compilers happy */
2209a747e4fSDavid du Colombier }
2219a747e4fSDavid du Colombier 
2229a747e4fSDavid du Colombier 
2239a747e4fSDavid du Colombier static void
2249a747e4fSDavid du Colombier userinput(char *buf, int blen)
2259a747e4fSDavid du Colombier {
2269a747e4fSDavid du Colombier 	int n;
2279a747e4fSDavid du Colombier 
228*eed6406fSDavid du Colombier 	for(;;){
2299a747e4fSDavid du Colombier 		n = read(0, buf, blen);
2309a747e4fSDavid du Colombier 		if(n<=0)
2319a747e4fSDavid du Colombier 			exits("read error");
2329a747e4fSDavid du Colombier 		if(buf[n-1]=='\n'){
2339a747e4fSDavid du Colombier 			buf[n-1] = '\0';
2349a747e4fSDavid du Colombier 			return;
2359a747e4fSDavid du Colombier 		}
2369a747e4fSDavid du Colombier 		buf += n;  blen -= n;
2379a747e4fSDavid du Colombier 		if(blen<=0)
2389a747e4fSDavid du Colombier 			exits("input too large");
2399a747e4fSDavid du Colombier 	}
2409a747e4fSDavid du Colombier }
2419a747e4fSDavid du Colombier 
242