예제 #1
0
파일: login.c 프로젝트: finkler/avianos
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");
}
예제 #2
0
파일: cpu-bl.c 프로젝트: npe9/harvey
/*
 *  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;
}
예제 #3
0
파일: readnvram.c 프로젝트: 00001/plan9port
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;
}
예제 #4
0
파일: util.c 프로젝트: 99years/plan9
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);
}
예제 #5
0
파일: secstore.c 프로젝트: 00001/plan9port
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;
}
예제 #6
0
파일: secstore.c 프로젝트: 00001/plan9port
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;
}
예제 #7
0
파일: readnvram.c 프로젝트: keedon/harvey
/*
 *  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;
}
예제 #8
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;
}
예제 #9
0
파일: cpu-bl.c 프로젝트: npe9/harvey
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");
}
예제 #10
0
파일: readnvram.c 프로젝트: 0intro/drawterm
/*
 *  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;
}
예제 #11
0
파일: secuser.c 프로젝트: 00001/plan9port
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 */
}