| #include <u.h> |
| #include <libc.h> |
| #include <mp.h> |
| #include <libsec.h> |
| #include "SConn.h" |
| #include "secstore.h" |
| |
| int verbose; |
| |
| static void userinput(char *, int); |
| char *SECSTORE_DIR; |
| |
| static void |
| ensure_exists(char *f, ulong perm) |
| { |
| int fd; |
| |
| if(access(f, AEXIST) >= 0) |
| return; |
| if(verbose) |
| fprint(2,"first time setup for secstore: create %s %lo\n", f, perm); |
| fd = create(f, OREAD, perm); |
| if(fd < 0){ |
| fprint(2, "unable to create %s\n", f); |
| exits("secstored directories"); |
| } |
| close(fd); |
| } |
| |
| |
| int |
| main(int argc, char **argv) |
| { |
| int isnew; |
| char *id, buf[Maxmsg], home[Maxmsg], prompt[100], *hexHi; |
| char *pass, *passck; |
| long expsecs; |
| mpint *H = mpnew(0), *Hi = mpnew(0); |
| PW *pw; |
| Tm *tm; |
| |
| SECSTORE_DIR = unsharp("#9/secstore"); |
| |
| ARGBEGIN{ |
| case 'v': |
| verbose++; |
| break; |
| }ARGEND; |
| if(argc!=1){ |
| print("usage: secuser [-v] <user>\n"); |
| exits("usage"); |
| } |
| |
| ensure_exists(SECSTORE_DIR, DMDIR|0755L); |
| snprint(home, sizeof(home), "%s/who", SECSTORE_DIR); |
| ensure_exists(home, DMDIR|0755L); |
| snprint(home, sizeof(home), "%s/store", SECSTORE_DIR); |
| ensure_exists(home, DMDIR|0700L); |
| |
| id = argv[0]; |
| if(verbose) |
| fprint(2,"secuser %s\n", id); |
| if((pw = getPW(id,1)) == nil){ |
| isnew = 1; |
| print("new account (because %s/%s %r)\n", SECSTORE_DIR, id); |
| pw = emalloc(sizeof(*pw)); |
| pw->id = estrdup(id); |
| snprint(home, sizeof(home), "%s/store/%s", SECSTORE_DIR, id); |
| if(access(home, AEXIST) == 0){ |
| print("new user, but directory %s already exists\n", home); |
| exits(home); |
| } |
| }else{ |
| isnew = 0; |
| } |
| |
| /* get main password for id */ |
| for(;;){ |
| if(isnew) |
| snprint(prompt, sizeof(prompt), "%s password", id); |
| else |
| snprint(prompt, sizeof(prompt), "%s password [default = don't change]", id); |
| pass = readcons(prompt, nil, 1); |
| if(pass == nil){ |
| print("getpass failed\n"); |
| exits("getpass failed"); |
| } |
| if(verbose) |
| print("%ld characters\n", strlen(pass)); |
| if(pass[0] == '\0' && isnew == 0) |
| break; |
| if(strlen(pass) >= 7) |
| break; |
| print("password must be at least 7 characters\n"); |
| } |
| |
| if(pass[0] != '\0'){ |
| snprint(prompt, sizeof(prompt), "retype password"); |
| if(verbose) |
| print("confirming...\n"); |
| passck = readcons(prompt, nil, 1); |
| if(passck == nil){ |
| print("getpass failed\n"); |
| exits("getpass failed"); |
| } |
| if(strcmp(pass, passck) != 0){ |
| print("passwords didn't match\n"); |
| exits("no match"); |
| } |
| memset(passck, 0, strlen(passck)); |
| free(passck); |
| hexHi = PAK_Hi(id, pass, H, Hi); |
| memset(pass, 0, strlen(pass)); |
| free(pass); |
| free(hexHi); |
| mpfree(H); |
| pw->Hi = Hi; |
| } |
| |
| /* get expiration time (midnight of date specified) */ |
| if(isnew) |
| expsecs = time(0) + 365*24*60*60; |
| else |
| expsecs = pw->expire; |
| |
| for(;;){ |
| tm = localtime(expsecs); |
| print("expires [DDMMYYYY, default = %2.2d%2.2d%4.4d]: ", |
| tm->mday, tm->mon, tm->year+1900); |
| userinput(buf, sizeof(buf)); |
| if(strlen(buf) == 0) |
| break; |
| if(strlen(buf) != 8){ |
| print("!bad date format: %s\n", buf); |
| continue; |
| } |
| tm->mday = (buf[0]-'0')*10 + (buf[1]-'0'); |
| if(tm->mday > 31 || tm->mday < 1){ |
| print("!bad day of month: %d\n", tm->mday); |
| continue; |
| } |
| tm->mon = (buf[2]-'0')*10 + (buf[3]-'0') - 1; |
| if(tm->mon > 11 || tm->mday < 0){ |
| print("!bad month: %d\n", tm->mon + 1); |
| continue; |
| } |
| tm->year = atoi(buf+4) - 1900; |
| if(tm->year < 70){ |
| print("!bad year: %d\n", tm->year + 1900); |
| continue; |
| } |
| tm->sec = 59; |
| tm->min = 59; |
| tm->hour = 23; |
| tm->yday = 0; |
| expsecs = tm2sec(tm); |
| break; |
| } |
| pw->expire = expsecs; |
| |
| /* failed logins */ |
| if(pw->failed != 0 ) |
| print("clearing %d failed login attempts\n", pw->failed); |
| pw->failed = 0; |
| |
| /* status bits */ |
| if(isnew) |
| pw->status = Enabled; |
| for(;;){ |
| print("Enabled or Disabled [default %s]: ", |
| (pw->status & Enabled) ? "Enabled" : "Disabled" ); |
| userinput(buf, sizeof(buf)); |
| if(strlen(buf) == 0) |
| break; |
| if(buf[0]=='E' || buf[0]=='e'){ |
| pw->status |= Enabled; |
| break; |
| } |
| if(buf[0]=='D' || buf[0]=='d'){ |
| pw->status = pw->status & ~Enabled; |
| break; |
| } |
| } |
| for(;;){ |
| print("require STA? [default %s]: ", |
| (pw->status & STA) ? "yes" : "no" ); |
| userinput(buf, sizeof(buf)); |
| if(strlen(buf) == 0) |
| break; |
| if(buf[0]=='Y' || buf[0]=='y'){ |
| pw->status |= STA; |
| break; |
| } |
| if(buf[0]=='N' || buf[0]=='n'){ |
| pw->status = pw->status & ~STA; |
| break; |
| } |
| } |
| |
| /* free form field */ |
| if(isnew) |
| pw->other = nil; |
| print("comments [default = %s]: ", (pw->other == nil) ? "" : pw->other); |
| userinput(buf, 72); /* 72 comes from password.h */ |
| if(buf[0]) |
| if((pw->other = strdup(buf)) == nil) |
| sysfatal("strdup"); |
| |
| syslog(0, LOG, "CHANGELOGIN for '%s'", pw->id); |
| if(putPW(pw) < 0){ |
| print("error writing entry: %r\n"); |
| exits("can't write password file"); |
| }else{ |
| print("change written\n"); |
| if(isnew && create(home, OREAD, DMDIR | 0775L) < 0){ |
| print("unable to create %s: %r\n", home); |
| exits(home); |
| } |
| } |
| |
| exits(""); |
| return 1; /* keep other compilers happy */ |
| } |
| |
| |
| static void |
| userinput(char *buf, int blen) |
| { |
| int n; |
| |
| while(1){ |
| n = read(0, buf, blen); |
| if(n<=0) |
| exits("read error"); |
| if(buf[n-1]=='\n'){ |
| buf[n-1] = '\0'; |
| return; |
| } |
| buf += n; blen -= n; |
| if(blen<=0) |
| exits("input too large"); |
| } |
| } |
| |