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