void auth(char *user) { char *p, *pass; struct spwd *sp; SHA1Digest *digest; fprint(stderr, "\n"); if(user == nil) user = readcons("user", nil, 0); pwd = getpwnam(user); if(pwd == nil) fatal(1, "unknown user %s", user); sp = getspnam(pwd->pw_name); if(sp != nil){ pass = readcons("password", nil, 1); digest = sha1(pass, strlen(pass), nil); p = sha1pickle(digest); if(strcmp(sp->sp_pwdp, p)) fatal(1, "wrong password"); free(digest); free(p); } fprint(stderr, "\n"); }
/* * prompt user for a key. don't care about memory leaks, runs standalone */ static Attr* promptforkey(char *params) { char *v; int fd; Attr *a, *attr; char *def; fd = open("/dev/cons", ORDWR); if(fd < 0) sysfatal("opening /dev/cons: %r"); attr = _parseattr(params); fprint(fd, "\n!Adding key:"); for(a=attr; a; a=a->next) if(a->type != AttrQuery && a->name[0] != '!') fprint(fd, " %q=%q", a->name, a->val); fprint(fd, "\n"); for(a=attr; a; a=a->next){ v = a->name; if(a->type != AttrQuery || v[0]=='!') continue; def = nil; if(strcmp(v, "user") == 0) def = getuser(); a->val = readcons(v, def, 0); if(a->val == nil) sysfatal("user terminated key input"); a->type = AttrNameval; } for(a=attr; a; a=a->next){ v = a->name; if(a->type != AttrQuery || v[0]!='!') continue; def = nil; if(strcmp(v+1, "user") == 0) def = getuser(); a->val = readcons(v+1, def, 1); if(a->val == nil) sysfatal("user terminated key input"); a->type = AttrNameval; } fprint(fd, "!\n"); close(fd); return attr; }
static char* xreadcons(char *prompt, char *def, int secret, char *buf, int nbuf) { char *p; p = readcons(prompt, def, secret); if(p == nil) return nil; strecpy(buf, buf+nbuf, p); memset(p, 0, strlen(p)); free(p); return buf; }
static int outin(char *prompt, char *def, int len) { char *s; s = readcons(prompt, def, 0); if(s == nil) return -1; if(s == nil) sysfatal("s==nil???"); strncpy(def, s, len); def[len-1] = 0; free(s); return strlen(def); }
static AuthConn* login(char *id, char *dest, int pass_stdin, int pass_nvram) { AuthConn *c; int fd, n, ntry = 0; char *S, *PINSTA = nil, *nl, s[Maxmsg+1], *pass; if(dest == nil){ fprint(2, "tried to login with nil dest\n"); exits("nil dest"); } c = emalloc(sizeof(*c)); if(pass_nvram){ /* if(readnvram(&nvr, 0) < 0) */ exits("readnvram: %r"); strecpy(c->pass, c->pass+sizeof c->pass, nvr.config); } if(pass_stdin){ n = readn(0, s, Maxmsg-2); /* so len(PINSTA)<Maxmsg-3 */ if(n < 1) exits("no password on standard input"); s[n] = 0; nl = strchr(s, '\n'); if(nl){ *nl++ = 0; PINSTA = estrdup(nl); nl = strchr(PINSTA, '\n'); if(nl) *nl = 0; } strecpy(c->pass, c->pass+sizeof c->pass, s); } while(1){ if(verbose) fprint(2, "dialing %s\n", dest); if((fd = dial(dest, nil, nil, nil)) < 0){ fprint(2, "can't dial %s\n", dest); free(c); return nil; } if((c->conn = newSConn(fd)) == nil){ free(c); return nil; } ntry++; if(!pass_stdin && !pass_nvram){ pass = readcons("secstore password", nil, 1); if(pass == nil) pass = estrdup(""); if(strlen(pass) >= sizeof c->pass){ fprint(2, "password too long, skipping secstore login\n"); exits("password too long"); } strcpy(c->pass, pass); memset(pass, 0, strlen(pass)); free(pass); } if(c->pass[0]==0){ fprint(2, "null password, skipping secstore login\n"); exits("no password"); } if(PAKclient(c->conn, id, c->pass, &S) >= 0) break; c->conn->free(c->conn); if(pass_stdin) exits("invalid password on standard input"); if(pass_nvram) exits("invalid password in nvram"); /* and let user try retyping the password */ if(ntry==3) fprint(2, "Enter an empty password to quit.\n"); } c->passlen = strlen(c->pass); fprint(2, "server: %s\n", S); free(S); if(readstr(c->conn, s) < 0){ c->conn->free(c->conn); free(c); return nil; } if(strcmp(s, "STA") == 0){ long sn; if(pass_stdin){ if(PINSTA) strncpy(s+3, PINSTA, (sizeof s)-3); else exits("missing PIN+SecureID on standard input"); free(PINSTA); }else{ pass = readcons("STA PIN+SecureID", nil, 1); if(pass == nil) pass = estrdup(""); strncpy(s+3, pass, (sizeof s)-4); memset(pass, 0, strlen(pass)); free(pass); } sn = strlen(s+3); if(verbose) fprint(2, "%ld\n", sn); c->conn->write(c->conn, (uchar*)s, sn+3); readstr(c->conn, s); } if(strcmp(s, "OK") != 0){ fprint(2, "%s\n", s); c->conn->free(c->conn); free(c); return nil; } return c; }
static int chpasswd(AuthConn *c, char *id) { ulong len; int rv = -1, newpasslen = 0; mpint *H, *Hi; uchar *memfile; char *newpass, *passck; char *list, *cur, *next, *hexHi; char *f[8], prompt[128]; H = mpnew(0); Hi = mpnew(0); /* changing our password is vulnerable to connection failure */ for(;;){ snprint(prompt, sizeof(prompt), "new password for %s: ", id); newpass = readcons(prompt, nil, 1); if(newpass == nil) goto Out; if(strlen(newpass) >= 7) break; else if(strlen(newpass) == 0){ fprint(2, "!password change aborted\n"); goto Out; } print("!password must be at least 7 characters\n"); } newpasslen = strlen(newpass); snprint(prompt, sizeof(prompt), "retype password: "******"readcons failed\n"); goto Out; } if(strcmp(passck, newpass) != 0){ fprint(2, "passwords didn't match\n"); goto Out; } c->conn->write(c->conn, (uchar*)"CHPASS", strlen("CHPASS")); hexHi = PAK_Hi(id, newpass, H, Hi); c->conn->write(c->conn, (uchar*)hexHi, strlen(hexHi)); free(hexHi); mpfree(H); mpfree(Hi); if(getfile(c->conn, ".", (uchar **)(void*)&list, &len, nil, 0) < 0){ fprint(2, "directory listing failed.\n"); goto Out; } /* Loop over files and reencrypt them; try to keep going after error */ for(cur=list; (next=strchr(cur, '\n')) != nil; cur=next+1){ *next = '\0'; if(tokenize(cur, f, nelem(f))< 1) break; fprint(2, "reencrypting '%s'\n", f[0]); if(getfile(c->conn, f[0], &memfile, &len, (uchar*)c->pass, c->passlen) < 0){ fprint(2, "getfile of '%s' failed\n", f[0]); continue; } if(putfile(c->conn, f[0], memfile, len, (uchar*)newpass, newpasslen) < 0) fprint(2, "putfile of '%s' failed\n", f[0]); free(memfile); } free(list); c->conn->write(c->conn, (uchar*)"BYE", 3); rv = 0; Out: if(newpass != nil){ memset(newpass, 0, newpasslen); free(newpass); } c->conn->free(c->conn); return rv; }
/* * get key info out of nvram. since there isn't room in the PC's nvram use * a disk partition there. */ int readnvram(Nvrsafe *safep, int flag) { int err; char buf[512], in[128]; /* 512 for floppy i/o */ Nvrsafe *safe; Nvrwhere loc; err = 0; safe = (Nvrsafe*)buf; memset(&loc, 0, sizeof loc); findnvram(&loc); if (loc.safelen < 0) loc.safelen = sizeof *safe; else if (loc.safelen > sizeof buf) loc.safelen = sizeof buf; if (loc.safeoff < 0) { fprint(2, "readnvram: can't find nvram\n"); if(!(flag&NVwritemem)) memset(safep, 0, sizeof(*safep)); safe = safep; /* * allow user to type the data for authentication, * even if there's no nvram to store it in. */ } if(flag&NVwritemem) safe = safep; else { memset(safep, 0, sizeof(*safep)); if(loc.fd >= 0) werrstr(""); if(loc.fd < 0 || seek(loc.fd, loc.safeoff, 0) < 0 || read(loc.fd, buf, loc.safelen) != loc.safelen){ err = 1; if(flag&(NVwrite|NVwriteonerr)) { if(loc.fd < 0 && nvrfile != nil) fprint(2, "can't open %s: %r\n", nvrfile); else if(loc.fd < 0){ /* this will have been printed above */ // fprint(2, "can't find nvram: %r\n"); }else if (seek(loc.fd, loc.safeoff, 0) < 0) fprint(2, "can't seek %s to %d: %r\n", nvrfile, loc.safeoff); else fprint(2, "can't read %d bytes from %s: %r\n", loc.safelen, nvrfile); } /* start from scratch */ memset(safep, 0, sizeof(*safep)); safe = safep; }else{ *safep = *safe; /* overwrite arg with data read */ safe = safep; /* verify data read */ err |= check(safe->machkey, DESKEYLEN, safe->machsum, "bad authentication password"); // err |= check(safe->config, CONFIGLEN, safe->configsum, // "bad secstore password"); err |= check(safe->authid, ANAMELEN, safe->authidsum, "bad authentication id"); err |= check(safe->authdom, DOMLEN, safe->authdomsum, "bad authentication domain"); if(err == 0) if(safe->authid[0]==0 || safe->authdom[0]==0){ fprint(2, "empty nvram authid or authdom\n"); err = 1; } } } if((flag&(NVwrite|NVwritemem)) || (err && (flag&NVwriteonerr))){ if (!(flag&NVwritemem)) { readcons("authid", nil, 0, safe->authid, sizeof safe->authid); readcons("authdom", nil, 0, safe->authdom, sizeof safe->authdom); for(;;){ if(readcons("auth password", nil, 1, in, sizeof in) == nil) goto Out; if(passtokey(safe->machkey, in)) break; } readcons("secstore password", nil, 1, safe->config, sizeof safe->config); } // safe->authsum = nvcsum(safe->authkey, DESKEYLEN); safe->machsum = nvcsum(safe->machkey, DESKEYLEN); safe->configsum = nvcsum(safe->config, CONFIGLEN); safe->authidsum = nvcsum(safe->authid, sizeof safe->authid); safe->authdomsum = nvcsum(safe->authdom, sizeof safe->authdom); *(Nvrsafe*)buf = *safe; if(loc.fd >= 0) werrstr(""); if(loc.fd < 0 || seek(loc.fd, loc.safeoff, 0) < 0 || write(loc.fd, buf, loc.safelen) != loc.safelen){ fprint(2, "can't write key to nvram: %r\n"); err = 1; }else err = 0; } Out: if (loc.fd >= 0) close(loc.fd); return err? -1: 0; }
/* * local function to read an expression */ static LVAL _readexpr(tpLspObject pLSP,FILE *f) { int ch,ch1,ch2,i; LVAL p; char *s; double dval; long lval; spaceat(ch,f); if( ch == EOF ) { return NIL; } if( ch == pLSP->cClose ) { return NIL; } if( ch == pLSP->cOpen )/* Read a cons node. */ return readcons(pLSP,f); /**** Note: XLISP allows 1E++10 as a symbol. This is dangerous. We do not change XLISP (so far), but here I exclude all symbol names starting with numeral. */ if( const_p1(ch) )/* Read a symbol. */ { for( i = 0 ; const_p(ch) ; i++ ){ if( storech(pLSP,i,ch) )return NIL; ch = getC(pLSP,f); } UNGETC(ch); /* Recognize NIL and nil symbols. */ if( !strcmp(BUFFER,"NIL") || !strcmp(BUFFER,"nil") ) return NIL; p = newsymbol(); s = StrDup( BUFFER ); if( null(p) || s == NULL )return NIL; setsymbol(p,s); return p; } if( ch == '\"' ){ ch = GETC(f); storech(pLSP,0,0); /* inititalize the buffer */ if( ch != '\"' )goto SimpleString; ch = GETC(f); if( ch != '\"' ){ UNGETC(ch); ch = '\"';/* ch should hold the first character of the string that is " now */ goto SimpleString; } ch = GETC(f); /* multi line string */ for( i = 0 ; ch != EOF ; i++ ){ if( ch == '\"' ){ ch1 = GETC(f); ch2 = GETC(f); if( ch1 == '\"' && ch2 == '\"' )break; UNGETC(ch2); UNGETC(ch1); } if( ch == '\\' ){ ch = GETC(f); s = escapers; while( *s ){ if( *s++ == ch ){ ch = *s; break; } if( *s )s++; } } if( storech(pLSP,i,ch) )return NIL; ch = GETC(f); } p = newstring(); s = StrDup( BUFFER ); if( null(p) || s == NULL )return NIL; setstring(p,s); return p; } if( ch == '\"' ){/* Read a string. */ ch = GETC(f);/* Eat the " character. */ SimpleString: for( i = 0 ; ch != '\"' && ch != EOF ; i++ ){ if( ch == '\\' ){ ch = GETC(f); s = escapers; while( *s ){ if( *s++ == ch ){ ch = *s; break; } if( *s )s++; } } if( ch == '\n' )return NIL; if( storech(pLSP,i,ch) )return NIL; ch = GETC(f); } p = newstring(); s = StrDup( BUFFER ); if( null(p) || s == NULL ) { return NIL; } setstring(p,s); return p; } if( numeral1(ch) ) { for( i = 0 ; isinset(ch,"0123456789+-eE.") ; i++ ) { if( storech(pLSP,i,ch) )return NIL; ch = getC(pLSP,f); } UNGETC(ch); cnumeric(BUFFER,&i,&dval,&lval); switch( i ) { case 0: return NIL; case 1: /* A float number is coming. */ p = newfloat(); if( null(p) ) { return NIL; } setfloat(p,dval); return p; case 2: /* An integer is coming. */ p = newint(); if( null(p) ) { return NIL; } setint(p,lval); return p; default: return NIL; } } return NIL; }
void cpumain(int argc, char **argv) { char dat[MaxStr], buf[MaxStr], cmd[MaxStr], *err, *secstoreserver, *p, *s; int fd, ms, data; /* see if we should use a larger message size */ fd = open("/dev/draw", OREAD); if(fd > 0){ ms = iounit(fd); if(msgsize < ms+IOHDRSZ) msgsize = ms+IOHDRSZ; close(fd); } user = getenv("USER"); secstoreserver = nil; authserver = getenv("auth"); if(authserver == nil) authserver = "p9auth.cs.bell-labs.com"; system = getenv("cpu"); if(system == nil) system = "plan9.bell-labs.com"; ARGBEGIN{ case 'a': authserver = EARGF(usage()); break; case 'c': system = EARGF(usage()); break; case 'd': dbg++; break; case 'e': ealgs = EARGF(usage()); if(*ealgs == 0 || strcmp(ealgs, "clear") == 0) ealgs = nil; break; case 'C': cflag++; cmd[0] = '!'; cmd[1] = '\0'; while((p = ARGF()) != nil) { strcat(cmd, " "); strcat(cmd, p); } break; case 'k': keyspec = EARGF(usage()); break; case 'r': base = EARGF(usage()); break; case 's': secstoreserver = EARGF(usage()); break; case 'u': user = EARGF(usage()); break; default: usage(); }ARGEND; if(argc != 0) usage(); if(user == nil) user = readcons("user", nil, 0); if(mountfactotum() < 0){ if(secstoreserver == nil) secstoreserver = authserver; if(havesecstore(secstoreserver, user)){ s = secstorefetch(secstoreserver, user, nil); if(s){ if(strlen(s) >= sizeof secstorebuf) sysfatal("secstore data too big"); strcpy(secstorebuf, s); } } } if((err = rexcall(&data, system, srvname))) fatal(1, "%s: %s", err, system); /* Tell the remote side the command to execute and where our working directory is */ if(cflag) writestr(data, cmd, "command", 0); if(getcwd(dat, sizeof(dat)) == 0) writestr(data, "NO", "dir", 0); else writestr(data, dat, "dir", 0); /* * Wait for the other end to execute and start our file service * of /mnt/term */ if(readstr(data, buf, sizeof(buf)) < 0) fatal(1, "waiting for FS: %r"); if(strncmp("FS", buf, 2) != 0) { print("remote cpu: %s", buf); exits(buf); } if(readstr(data, buf, sizeof buf) < 0) fatal(1, "waiting for remote export: %r"); if(strcmp(buf, "/") != 0){ print("remote cpu: %s" , buf); exits(buf); } write(data, "OK", 2); /* Begin serving the gnot namespace */ exportfs(data, msgsize); fatal(1, "starting exportfs"); }
/* * get key info out of nvram. since there isn't room in the PC's nvram use * a disk partition there. */ int readnvram(Nvrsafe *safep, int flag) { char buf[1024], in[128], *cputype, *nvrfile, *nvrlen, *nvroff, *v[2]; int fd, err, i, safeoff, safelen; Nvrsafe *safe; err = 0; memset(safep, 0, sizeof(*safep)); nvrfile = getenv("nvram"); cputype = getenv("cputype"); if(cputype == nil) cputype = "mips"; if(strcmp(cputype, "386")==0 || strcmp(cputype, "alpha")==0) cputype = "pc"; safe = (Nvrsafe*)buf; fd = -1; safeoff = -1; safelen = -1; if(nvrfile != nil){ /* accept device and device!file */ i = gettokens(nvrfile, v, nelem(v), "!"); fd = open(v[0], ORDWR); safelen = sizeof(Nvrsafe); if(strstr(v[0], "/9fat") == nil) safeoff = 0; nvrlen = getenv("nvrlen"); if(nvrlen != nil) safelen = atoi(nvrlen); nvroff = getenv("nvroff"); if(nvroff != nil){ if(strcmp(nvroff, "dos") == 0) safeoff = -1; else safeoff = atoi(nvroff); } if(safeoff < 0 && fd >= 0){ safelen = 512; safeoff = finddosfile(fd, i == 2 ? v[1] : "plan9.nvr"); if(safeoff < 0){ close(fd); fd = -1; } } free(nvrfile); if(nvrlen != nil) free(nvrlen); if(nvroff != nil) free(nvroff); }else{ for(i=0; i<nelem(nvtab); i++){ if(strcmp(cputype, nvtab[i].cputype) != 0) continue; if((fd = open(nvtab[i].file, ORDWR)) < 0) continue; safeoff = nvtab[i].off; safelen = nvtab[i].len; if(safeoff == -1){ safeoff = finddosfile(fd, "plan9.nvr"); if(safeoff < 0){ close(fd); fd = -1; continue; } } break; } } if(fd < 0 || seek(fd, safeoff, 0) < 0 || read(fd, buf, safelen) != safelen){ err = 1; if(flag&(NVwrite|NVwriteonerr)) fprint(2, "can't read nvram: %r\n"); memset(safep, 0, sizeof(*safep)); safe = safep; }else{ *safep = *safe; safe = safep; err |= check(safe->machkey, DESKEYLEN, safe->machsum, "bad nvram key"); // err |= check(safe->config, CONFIGLEN, safe->configsum, "bad secstore key"); err |= check(safe->authid, ANAMELEN, safe->authidsum, "bad authentication id"); err |= check(safe->authdom, DOMLEN, safe->authdomsum, "bad authentication domain"); } if((flag&NVwrite) || (err && (flag&NVwriteonerr))){ readcons("authid", nil, 0, safe->authid, sizeof(safe->authid)); readcons("authdom", nil, 0, safe->authdom, sizeof(safe->authdom)); readcons("secstore key", nil, 1, safe->config, sizeof(safe->config)); for(;;){ if(readcons("password", nil, 1, in, sizeof in) == nil) goto Out; if(passtokey(safe->machkey, in)) break; } safe->machsum = nvcsum(safe->machkey, DESKEYLEN); safe->configsum = nvcsum(safe->config, CONFIGLEN); safe->authidsum = nvcsum(safe->authid, sizeof(safe->authid)); safe->authdomsum = nvcsum(safe->authdom, sizeof(safe->authdom)); *(Nvrsafe*)buf = *safe; if(seek(fd, safeoff, 0) < 0 || write(fd, buf, safelen) != safelen){ fprint(2, "can't write key to nvram: %r\n"); err = 1; }else err = 0; } Out: close(fd); return err ? -1 : 0; }
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 */ }