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
ensure_exists(char * f,ulong perm)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);
22*f5b871d1SDavid du Colombier if(fd < 0)
23*f5b871d1SDavid du Colombier sysfatal("unable to create %s: %r", f);
24f5794baaSDavid du Colombier close(fd);
25f5794baaSDavid du Colombier }
26f5794baaSDavid du Colombier
27f5794baaSDavid du Colombier
28*f5b871d1SDavid du Colombier void
main(int argc,char ** argv)299a747e4fSDavid du Colombier main(int argc, char **argv)
309a747e4fSDavid du Colombier {
319a747e4fSDavid du Colombier int isnew;
32b27b55e2SDavid du Colombier char *id, buf[Maxmsg], home[Maxmsg], prompt[100], *hexHi;
33b27b55e2SDavid du Colombier char *pass, *passck;
349a747e4fSDavid du Colombier long expsecs;
359a747e4fSDavid du Colombier mpint *H = mpnew(0), *Hi = mpnew(0);
369a747e4fSDavid du Colombier PW *pw;
379a747e4fSDavid du Colombier Tm *tm;
389a747e4fSDavid du Colombier
399a747e4fSDavid du Colombier ARGBEGIN{
409a747e4fSDavid du Colombier case 'v':
419a747e4fSDavid du Colombier verbose++;
429a747e4fSDavid du Colombier break;
439a747e4fSDavid du Colombier }ARGEND;
449a747e4fSDavid du Colombier if(argc!=1){
45*f5b871d1SDavid du Colombier fprint(2, "usage: secuser [-v] <user>\n");
469a747e4fSDavid du Colombier exits("usage");
479a747e4fSDavid du Colombier }
489a747e4fSDavid du Colombier
49f5794baaSDavid du Colombier ensure_exists(SECSTORE_DIR, DMDIR|0755L);
50f5794baaSDavid du Colombier snprint(home, sizeof(home), "%s/who", SECSTORE_DIR);
51f5794baaSDavid du Colombier ensure_exists(home, DMDIR|0755L);
52f5794baaSDavid du Colombier snprint(home, sizeof(home), "%s/store", SECSTORE_DIR);
53f5794baaSDavid du Colombier ensure_exists(home, DMDIR|0700L);
54f5794baaSDavid du Colombier
559a747e4fSDavid du Colombier id = argv[0];
569a747e4fSDavid du Colombier if(verbose)
579a747e4fSDavid du Colombier fprint(2,"secuser %s\n", id);
58369036d9SDavid du Colombier if((pw = getPW(id,1)) == nil){
599a747e4fSDavid du Colombier isnew = 1;
60369036d9SDavid du Colombier print("new account (because %s/%s %r)\n", SECSTORE_DIR, id);
619a747e4fSDavid du Colombier pw = emalloc(sizeof(*pw));
629a747e4fSDavid du Colombier pw->id = estrdup(id);
639a747e4fSDavid du Colombier snprint(home, sizeof(home), "%s/store/%s", SECSTORE_DIR, id);
64*f5b871d1SDavid du Colombier if(access(home, AEXIST) == 0)
65*f5b871d1SDavid du Colombier sysfatal("new user, but directory %s already exists",
66*f5b871d1SDavid du Colombier home);
6719dc9ffeSDavid du Colombier }else{
689a747e4fSDavid du Colombier isnew = 0;
6919dc9ffeSDavid du Colombier }
709a747e4fSDavid du Colombier
719a747e4fSDavid du Colombier /* get main password for id */
729a747e4fSDavid du Colombier for(;;){
739a747e4fSDavid du Colombier if(isnew)
749a747e4fSDavid du Colombier snprint(prompt, sizeof(prompt), "%s password: ", id);
759a747e4fSDavid du Colombier else
769a747e4fSDavid du Colombier snprint(prompt, sizeof(prompt), "%s password [default = don't change]: ", id);
77c65b13b8SDavid du Colombier pass = getpassm(prompt);
78*f5b871d1SDavid du Colombier if(pass == nil)
79*f5b871d1SDavid du Colombier sysfatal("getpassm failed");
809a747e4fSDavid du Colombier if(verbose)
81b27b55e2SDavid du Colombier print("%ld characters\n", strlen(pass));
82b27b55e2SDavid du Colombier if(pass[0] == '\0' && isnew == 0)
839a747e4fSDavid du Colombier break;
84b27b55e2SDavid du Colombier if(strlen(pass) >= 7)
859a747e4fSDavid du Colombier break;
869a747e4fSDavid du Colombier print("password must be at least 7 characters\n");
879a747e4fSDavid du Colombier }
889a747e4fSDavid du Colombier
89b27b55e2SDavid du Colombier if(pass[0] != '\0'){
909a747e4fSDavid du Colombier snprint(prompt, sizeof(prompt), "retype password: ");
919a747e4fSDavid du Colombier if(verbose)
929a747e4fSDavid du Colombier print("confirming...\n");
93c65b13b8SDavid du Colombier passck = getpassm(prompt);
94*f5b871d1SDavid du Colombier if(passck == nil)
95*f5b871d1SDavid du Colombier sysfatal("getpassm failed");
96*f5b871d1SDavid du Colombier if(strcmp(pass, passck) != 0)
97*f5b871d1SDavid du Colombier sysfatal("passwords didn't match");
98b27b55e2SDavid du Colombier memset(passck, 0, strlen(passck));
99b27b55e2SDavid du Colombier free(passck);
100b27b55e2SDavid du Colombier hexHi = PAK_Hi(id, pass, H, Hi);
101b27b55e2SDavid du Colombier memset(pass, 0, strlen(pass));
102b27b55e2SDavid du Colombier free(pass);
1039a747e4fSDavid du Colombier free(hexHi);
1049a747e4fSDavid du Colombier mpfree(H);
1059a747e4fSDavid du Colombier pw->Hi = Hi;
1069a747e4fSDavid du Colombier }
1079a747e4fSDavid du Colombier
1089a747e4fSDavid du Colombier /* get expiration time (midnight of date specified) */
1099a747e4fSDavid du Colombier if(isnew)
1109a747e4fSDavid du Colombier expsecs = time(0) + 365*24*60*60;
1119a747e4fSDavid du Colombier else
1129a747e4fSDavid du Colombier expsecs = pw->expire;
1139a747e4fSDavid du Colombier
1149a747e4fSDavid du Colombier for(;;){
1159a747e4fSDavid du Colombier tm = localtime(expsecs);
1169a747e4fSDavid du Colombier print("expires [DDMMYYYY, default = %2.2d%2.2d%4.4d]: ",
117*f5b871d1SDavid du Colombier tm->mday, tm->mon+1, tm->year+1900);
1189a747e4fSDavid du Colombier userinput(buf, sizeof(buf));
1199a747e4fSDavid du Colombier if(strlen(buf) == 0)
1209a747e4fSDavid du Colombier break;
1219a747e4fSDavid du Colombier if(strlen(buf) != 8){
1229a747e4fSDavid du Colombier print("!bad date format: %s\n", buf);
1239a747e4fSDavid du Colombier continue;
1249a747e4fSDavid du Colombier }
1259a747e4fSDavid du Colombier tm->mday = (buf[0]-'0')*10 + (buf[1]-'0');
1269a747e4fSDavid du Colombier if(tm->mday > 31 || tm->mday < 1){
1279a747e4fSDavid du Colombier print("!bad day of month: %d\n", tm->mday);
1289a747e4fSDavid du Colombier continue;
1299a747e4fSDavid du Colombier }
1309a747e4fSDavid du Colombier tm->mon = (buf[2]-'0')*10 + (buf[3]-'0') - 1;
131*f5b871d1SDavid du Colombier if(tm->mon > 11 || tm->mon < 0){
1329a747e4fSDavid du Colombier print("!bad month: %d\n", tm->mon + 1);
1339a747e4fSDavid du Colombier continue;
1349a747e4fSDavid du Colombier }
1359a747e4fSDavid du Colombier tm->year = atoi(buf+4) - 1900;
1369a747e4fSDavid du Colombier if(tm->year < 70){
1379a747e4fSDavid du Colombier print("!bad year: %d\n", tm->year + 1900);
1389a747e4fSDavid du Colombier continue;
1399a747e4fSDavid du Colombier }
1409a747e4fSDavid du Colombier tm->sec = 59;
1419a747e4fSDavid du Colombier tm->min = 59;
1429a747e4fSDavid du Colombier tm->hour = 23;
1436b6b9ac8SDavid du Colombier tm->yday = 0;
1449a747e4fSDavid du Colombier expsecs = tm2sec(tm);
1459a747e4fSDavid du Colombier break;
1469a747e4fSDavid du Colombier }
1479a747e4fSDavid du Colombier pw->expire = expsecs;
1489a747e4fSDavid du Colombier
1499a747e4fSDavid du Colombier /* failed logins */
1509a747e4fSDavid du Colombier if(pw->failed != 0 )
1519a747e4fSDavid du Colombier print("clearing %d failed login attempts\n", pw->failed);
1529a747e4fSDavid du Colombier pw->failed = 0;
1539a747e4fSDavid du Colombier
1549a747e4fSDavid du Colombier /* status bits */
1559a747e4fSDavid du Colombier if(isnew)
1569a747e4fSDavid du Colombier pw->status = Enabled;
1579a747e4fSDavid du Colombier for(;;){
1589a747e4fSDavid du Colombier print("Enabled or Disabled [default %s]: ",
1599a747e4fSDavid du Colombier (pw->status & Enabled) ? "Enabled" : "Disabled" );
1609a747e4fSDavid du Colombier userinput(buf, sizeof(buf));
1619a747e4fSDavid du Colombier if(strlen(buf) == 0)
1629a747e4fSDavid du Colombier break;
1639a747e4fSDavid du Colombier if(buf[0]=='E' || buf[0]=='e'){
1649a747e4fSDavid du Colombier pw->status |= Enabled;
1659a747e4fSDavid du Colombier break;
1669a747e4fSDavid du Colombier }
1679a747e4fSDavid du Colombier if(buf[0]=='D' || buf[0]=='d'){
1689a747e4fSDavid du Colombier pw->status = pw->status & ~Enabled;
1699a747e4fSDavid du Colombier break;
1709a747e4fSDavid du Colombier }
1719a747e4fSDavid du Colombier }
1729a747e4fSDavid du Colombier for(;;){
1739a747e4fSDavid du Colombier print("require STA? [default %s]: ",
1749a747e4fSDavid du Colombier (pw->status & STA) ? "yes" : "no" );
1759a747e4fSDavid du Colombier userinput(buf, sizeof(buf));
1769a747e4fSDavid du Colombier if(strlen(buf) == 0)
1779a747e4fSDavid du Colombier break;
1789a747e4fSDavid du Colombier if(buf[0]=='Y' || buf[0]=='y'){
1799a747e4fSDavid du Colombier pw->status |= STA;
1809a747e4fSDavid du Colombier break;
1819a747e4fSDavid du Colombier }
1829a747e4fSDavid du Colombier if(buf[0]=='N' || buf[0]=='n'){
1839a747e4fSDavid du Colombier pw->status = pw->status & ~STA;
1849a747e4fSDavid du Colombier break;
1859a747e4fSDavid du Colombier }
1869a747e4fSDavid du Colombier }
1879a747e4fSDavid du Colombier
1889a747e4fSDavid du Colombier /* free form field */
1899a747e4fSDavid du Colombier if(isnew)
1909a747e4fSDavid du Colombier pw->other = nil;
1919a747e4fSDavid du Colombier print("comments [default = %s]: ", (pw->other == nil) ? "" : pw->other);
1929a747e4fSDavid du Colombier userinput(buf, 72); /* 72 comes from password.h */
1939a747e4fSDavid du Colombier if(buf[0])
1949a747e4fSDavid du Colombier if((pw->other = strdup(buf)) == nil)
1959a747e4fSDavid du Colombier sysfatal("strdup");
1969a747e4fSDavid du Colombier
1979a747e4fSDavid du Colombier syslog(0, LOG, "CHANGELOGIN for '%s'", pw->id);
198*f5b871d1SDavid du Colombier if(putPW(pw) < 0)
199*f5b871d1SDavid du Colombier sysfatal("can't write password file: %r");
200*f5b871d1SDavid du Colombier else{
2019a747e4fSDavid du Colombier print("change written\n");
202*f5b871d1SDavid du Colombier if(isnew && create(home, OREAD, DMDIR | 0775L) < 0)
203*f5b871d1SDavid du Colombier sysfatal("unable to create %s: %r", home);
20419dc9ffeSDavid du Colombier }
2059a747e4fSDavid du Colombier
2069a747e4fSDavid du Colombier exits("");
2079a747e4fSDavid du Colombier }
2089a747e4fSDavid du Colombier
2099a747e4fSDavid du Colombier
2109a747e4fSDavid du Colombier static void
userinput(char * buf,int blen)2119a747e4fSDavid du Colombier userinput(char *buf, int blen)
2129a747e4fSDavid du Colombier {
2139a747e4fSDavid du Colombier int n;
2149a747e4fSDavid du Colombier
215eed6406fSDavid du Colombier for(;;){
2169a747e4fSDavid du Colombier n = read(0, buf, blen);
2179a747e4fSDavid du Colombier if(n<=0)
2189a747e4fSDavid du Colombier exits("read error");
2199a747e4fSDavid du Colombier if(buf[n-1]=='\n'){
2209a747e4fSDavid du Colombier buf[n-1] = '\0';
2219a747e4fSDavid du Colombier return;
2229a747e4fSDavid du Colombier }
2239a747e4fSDavid du Colombier buf += n; blen -= n;
2249a747e4fSDavid du Colombier if(blen<=0)
2259a747e4fSDavid du Colombier exits("input too large");
2269a747e4fSDavid du Colombier }
2279a747e4fSDavid du Colombier }
228