xref: /plan9/sys/src/cmd/auth/secstore/secstore.c (revision 2e54da428eebf47f42ea14de775e10f38b9f44a1)
1868c5196SDavid du Colombier /* secstore - network login client */
29a747e4fSDavid du Colombier #include <u.h>
39a747e4fSDavid du Colombier #include <libc.h>
49a747e4fSDavid du Colombier #include <mp.h>
59a747e4fSDavid du Colombier #include <libsec.h>
6369036d9SDavid du Colombier #include <authsrv.h>
79a747e4fSDavid du Colombier #include "SConn.h"
89a747e4fSDavid du Colombier #include "secstore.h"
9868c5196SDavid du Colombier 
109a747e4fSDavid du Colombier enum{ CHK = 16, MAXFILES = 100 };
119a747e4fSDavid du Colombier 
12d9306527SDavid du Colombier typedef struct AuthConn{
13d9306527SDavid du Colombier 	SConn	*conn;
14d9306527SDavid du Colombier 	char	pass[64];
15d9306527SDavid du Colombier 	int	passlen;
16d9306527SDavid du Colombier } AuthConn;
17d9306527SDavid du Colombier 
189a747e4fSDavid du Colombier int verbose;
19369036d9SDavid du Colombier Nvrsafe nvr;
209a747e4fSDavid du Colombier 
219a747e4fSDavid du Colombier void
usage(void)229a747e4fSDavid du Colombier usage(void)
239a747e4fSDavid du Colombier {
24868c5196SDavid du Colombier 	fprint(2, "usage: secstore [-cinv] [-[gG] getfile] [-p putfile] "
25868c5196SDavid du Colombier 		"[-r rmfile] [-s tcp!server!5356] [-u user]\n");
269a747e4fSDavid du Colombier 	exits("usage");
279a747e4fSDavid du Colombier }
289a747e4fSDavid du Colombier 
299a747e4fSDavid du Colombier static int
getfile(SConn * conn,char * gf,uchar ** buf,ulong * buflen,uchar * key,int nkey)309a747e4fSDavid du Colombier getfile(SConn *conn, char *gf, uchar **buf, ulong *buflen, uchar *key, int nkey)
319a747e4fSDavid du Colombier {
32868c5196SDavid du Colombier 	int fd = -1, i, n, nr, nw, len;
339a747e4fSDavid du Colombier 	char s[Maxmsg+1];
349a747e4fSDavid du Colombier 	uchar skey[SHA1dlen], ib[Maxmsg+CHK], *ibr, *ibw, *bufw, *bufe;
359a747e4fSDavid du Colombier 	AESstate aes;
369a747e4fSDavid du Colombier 	DigestState *sha;
379a747e4fSDavid du Colombier 
389a747e4fSDavid du Colombier 	memset(&aes, 0, sizeof aes);
399a747e4fSDavid du Colombier 
40252470cdSDavid du Colombier 	snprint(s, Maxmsg, "GET %s", gf);
419a747e4fSDavid du Colombier 	conn->write(conn, (uchar*)s, strlen(s));
429a747e4fSDavid du Colombier 
439a747e4fSDavid du Colombier 	/* get file size */
449a747e4fSDavid du Colombier 	s[0] = '\0';
459a747e4fSDavid du Colombier 	bufw = bufe = nil;
469a747e4fSDavid du Colombier 	if(readstr(conn, s) < 0){
47d854de59SDavid du Colombier 		fprint(2, "secstore: remote: %s\n", s);
489a747e4fSDavid du Colombier 		return -1;
499a747e4fSDavid du Colombier 	}
50be0c1e85SDavid du Colombier 	len = atoi(s);
51be0c1e85SDavid du Colombier 	if(len == -1){
52d854de59SDavid du Colombier 		fprint(2, "secstore: remote file %s does not exist\n", gf);
539a747e4fSDavid du Colombier 		return -1;
54be0c1e85SDavid du Colombier 	}else if(len == -3){
55d854de59SDavid du Colombier 		fprint(2, "secstore: implausible filesize for %s\n", gf);
56be0c1e85SDavid du Colombier 		return -1;
57be0c1e85SDavid du Colombier 	}else if(len < 0){
58d854de59SDavid du Colombier 		fprint(2, "secstore: GET refused for %s\n", gf);
599a747e4fSDavid du Colombier 		return -1;
609a747e4fSDavid du Colombier 	}
619a747e4fSDavid du Colombier 	if(buf != nil){
629a747e4fSDavid du Colombier 		*buflen = len - AESbsize - CHK;
639a747e4fSDavid du Colombier 		*buf = bufw = emalloc(len);
649a747e4fSDavid du Colombier 		bufe = bufw + len;
659a747e4fSDavid du Colombier 	}
669a747e4fSDavid du Colombier 
679a747e4fSDavid du Colombier 	/* directory listing */
689a747e4fSDavid du Colombier 	if(strcmp(gf,".")==0){
699a747e4fSDavid du Colombier 		if(buf != nil)
709a747e4fSDavid du Colombier 			*buflen = len;
719a747e4fSDavid du Colombier 		for(i=0; i < len; i += n){
729a747e4fSDavid du Colombier 			if((n = conn->read(conn, (uchar*)s, Maxmsg)) <= 0){
73d854de59SDavid du Colombier 				fprint(2, "secstore: empty file chunk\n");
749a747e4fSDavid du Colombier 				return -1;
759a747e4fSDavid du Colombier 			}
769a747e4fSDavid du Colombier 			if(buf == nil)
779a747e4fSDavid du Colombier 				write(1, s, n);
789a747e4fSDavid du Colombier 			else
79868c5196SDavid du Colombier 				memmove(*buf + i, s, n);
809a747e4fSDavid du Colombier 		}
819a747e4fSDavid du Colombier 		return 0;
829a747e4fSDavid du Colombier 	}
839a747e4fSDavid du Colombier 
84868c5196SDavid du Colombier 	/*
85868c5196SDavid du Colombier 	 * conn is already encrypted against wiretappers, but gf is also
86868c5196SDavid du Colombier 	 * encrypted against server breakin.
87868c5196SDavid du Colombier 	 */
889a747e4fSDavid du Colombier 	if(buf == nil && (fd = create(gf, OWRITE, 0600)) < 0){
89d854de59SDavid du Colombier 		fprint(2, "secstore: can't open %s: %r\n", gf);
909a747e4fSDavid du Colombier 		return -1;
919a747e4fSDavid du Colombier 	}
929a747e4fSDavid du Colombier 
939a747e4fSDavid du Colombier 	ibr = ibw = ib;
949a747e4fSDavid du Colombier 	for(nr=0; nr < len;){
959a747e4fSDavid du Colombier 		if((n = conn->read(conn, ibw, Maxmsg)) <= 0){
96d854de59SDavid du Colombier 			fprint(2, "secstore: empty file chunk n=%d nr=%d len=%d: %r\n",
97d854de59SDavid du Colombier 				n, nr, len);
989a747e4fSDavid du Colombier 			return -1;
999a747e4fSDavid du Colombier 		}
1009a747e4fSDavid du Colombier 		nr += n;
1019a747e4fSDavid du Colombier 		ibw += n;
1029a747e4fSDavid du Colombier 		if(!aes.setup){		/* first time, read 16 byte IV */
1039a747e4fSDavid du Colombier 			if(n < AESbsize){
104d854de59SDavid du Colombier 				fprint(2, "secstore: no IV in file\n");
1059a747e4fSDavid du Colombier 				return -1;
1069a747e4fSDavid du Colombier 			}
1079a747e4fSDavid du Colombier 			sha = sha1((uchar*)"aescbc file", 11, nil, nil);
1089a747e4fSDavid du Colombier 			sha1(key, nkey, skey, sha);
1099a747e4fSDavid du Colombier 			setupAESstate(&aes, skey, AESbsize, ibr);
1109a747e4fSDavid du Colombier 			memset(skey, 0, sizeof skey);
1119a747e4fSDavid du Colombier 			ibr += AESbsize;
1129a747e4fSDavid du Colombier 			n   -= AESbsize;
1139a747e4fSDavid du Colombier 		}
1149a747e4fSDavid du Colombier 		aesCBCdecrypt(ibw-n, n, &aes);
1159a747e4fSDavid du Colombier 		n = ibw - ibr - CHK;
1169a747e4fSDavid du Colombier 		if(n > 0){
1179a747e4fSDavid du Colombier 			if(buf == nil){
1189a747e4fSDavid du Colombier 				nw = write(fd, ibr, n);
1199a747e4fSDavid du Colombier 				if(nw != n){
120d854de59SDavid du Colombier 					fprint(2, "secstore: write error on %s", gf);
1219a747e4fSDavid du Colombier 					return -1;
1229a747e4fSDavid du Colombier 				}
1239a747e4fSDavid du Colombier 			}else{
1249a747e4fSDavid du Colombier 				assert(bufw + n <= bufe);
1259a747e4fSDavid du Colombier 				memmove(bufw, ibr, n);
1269a747e4fSDavid du Colombier 				bufw += n;
1279a747e4fSDavid du Colombier 			}
1289a747e4fSDavid du Colombier 			ibr += n;
1299a747e4fSDavid du Colombier 		}
1309a747e4fSDavid du Colombier 		memmove(ib, ibr, ibw-ibr);
1319a747e4fSDavid du Colombier 		ibw = ib + (ibw-ibr);
1329a747e4fSDavid du Colombier 		ibr = ib;
1339a747e4fSDavid du Colombier 	}
1349a747e4fSDavid du Colombier 	if(buf == nil)
1359a747e4fSDavid du Colombier 		close(fd);
1369a747e4fSDavid du Colombier 	n = ibw-ibr;
137868c5196SDavid du Colombier 	if(n != CHK || memcmp(ib, "XXXXXXXXXXXXXXXX", CHK) != 0){
138d854de59SDavid du Colombier 		fprint(2, "secstore: decrypted file failed to authenticate!\n");
1399a747e4fSDavid du Colombier 		return -1;
1409a747e4fSDavid du Colombier 	}
1419a747e4fSDavid du Colombier 	return 0;
1429a747e4fSDavid du Colombier }
1439a747e4fSDavid du Colombier 
144868c5196SDavid du Colombier /*
145868c5196SDavid du Colombier  * This sends a file to the secstore disk that can, in an emergency, be
146868c5196SDavid du Colombier  * decrypted by the program aescbc.c.
147868c5196SDavid du Colombier  */
1489a747e4fSDavid du Colombier static int
putfile(SConn * conn,char * pf,uchar * buf,ulong len,uchar * key,int nkey)1499a747e4fSDavid du Colombier putfile(SConn *conn, char *pf, uchar *buf, ulong len, uchar *key, int nkey)
1509a747e4fSDavid du Colombier {
1519a747e4fSDavid du Colombier 	int i, n, fd, ivo, bufi, done;
1529a747e4fSDavid du Colombier 	char s[Maxmsg];
1539a747e4fSDavid du Colombier 	uchar skey[SHA1dlen], b[CHK+Maxmsg], IV[AESbsize];
1549a747e4fSDavid du Colombier 	AESstate aes;
1559a747e4fSDavid du Colombier 	DigestState *sha;
1569a747e4fSDavid du Colombier 
1579a747e4fSDavid du Colombier 	/* create initialization vector */
1589a747e4fSDavid du Colombier 	srand(time(0));			/* doesn't need to be unpredictable */
1599a747e4fSDavid du Colombier 	for(i=0; i<AESbsize; i++)
1609a747e4fSDavid du Colombier 		IV[i] = 0xff & rand();
1619a747e4fSDavid du Colombier 	sha = sha1((uchar*)"aescbc file", 11, nil, nil);
1629a747e4fSDavid du Colombier 	sha1(key, nkey, skey, sha);
1639a747e4fSDavid du Colombier 	setupAESstate(&aes, skey, AESbsize, IV);
1649a747e4fSDavid du Colombier 	memset(skey, 0, sizeof skey);
1659a747e4fSDavid du Colombier 
166252470cdSDavid du Colombier 	snprint(s, Maxmsg, "PUT %s", pf);
1679a747e4fSDavid du Colombier 	conn->write(conn, (uchar*)s, strlen(s));
1689a747e4fSDavid du Colombier 
1699a747e4fSDavid du Colombier 	if(buf == nil){
1709a747e4fSDavid du Colombier 		/* get file size */
1719a747e4fSDavid du Colombier 		if((fd = open(pf, OREAD)) < 0){
172d854de59SDavid du Colombier 			fprint(2, "secstore: can't open %s: %r\n", pf);
1739a747e4fSDavid du Colombier 			return -1;
1749a747e4fSDavid du Colombier 		}
1759a747e4fSDavid du Colombier 		len = seek(fd, 0, 2);
1769a747e4fSDavid du Colombier 		seek(fd, 0, 0);
177868c5196SDavid du Colombier 	} else
1789a747e4fSDavid du Colombier 		fd = -1;
1799a747e4fSDavid du Colombier 	if(len > MAXFILESIZE){
180d854de59SDavid du Colombier 		fprint(2, "secstore: implausible filesize %ld for %s\n",
181d854de59SDavid du Colombier 			len, pf);
1829a747e4fSDavid du Colombier 		return -1;
1839a747e4fSDavid du Colombier 	}
1849a747e4fSDavid du Colombier 
1859a747e4fSDavid du Colombier 	/* send file size */
1869a747e4fSDavid du Colombier 	snprint(s, Maxmsg, "%ld", len + AESbsize + CHK);
1879a747e4fSDavid du Colombier 	conn->write(conn, (uchar*)s, strlen(s));
1889a747e4fSDavid du Colombier 
1899a747e4fSDavid du Colombier 	/* send IV and file+XXXXX in Maxmsg chunks */
1909a747e4fSDavid du Colombier 	ivo = AESbsize;
1919a747e4fSDavid du Colombier 	bufi = 0;
1929a747e4fSDavid du Colombier 	memcpy(b, IV, ivo);
1939a747e4fSDavid du Colombier 	for(done = 0; !done; ){
1949a747e4fSDavid du Colombier 		if(buf == nil){
1959a747e4fSDavid du Colombier 			n = read(fd, b+ivo, Maxmsg-ivo);
1969a747e4fSDavid du Colombier 			if(n < 0){
197d854de59SDavid du Colombier 				fprint(2, "secstore: read error on %s: %r\n",
198d854de59SDavid du Colombier 					pf);
1999a747e4fSDavid du Colombier 				return -1;
2009a747e4fSDavid du Colombier 			}
2019a747e4fSDavid du Colombier 		}else{
2029a747e4fSDavid du Colombier 			if((n = len - bufi) > Maxmsg-ivo)
2039a747e4fSDavid du Colombier 				n = Maxmsg-ivo;
2049a747e4fSDavid du Colombier 			memcpy(b+ivo, buf+bufi, n);
2059a747e4fSDavid du Colombier 			bufi += n;
2069a747e4fSDavid du Colombier 		}
2079a747e4fSDavid du Colombier 		n += ivo;
2089a747e4fSDavid du Colombier 		ivo = 0;
2099a747e4fSDavid du Colombier 		if(n < Maxmsg){		/* EOF on input; append XX... */
2109a747e4fSDavid du Colombier 			memset(b+n, 'X', CHK);
211868c5196SDavid du Colombier 			n += CHK;	/* might push n>Maxmsg */
2129a747e4fSDavid du Colombier 			done = 1;
2139a747e4fSDavid du Colombier 		}
2149a747e4fSDavid du Colombier 		aesCBCencrypt(b, n, &aes);
2159a747e4fSDavid du Colombier 		if(n > Maxmsg){
2169a747e4fSDavid du Colombier 			assert(done==1);
2179a747e4fSDavid du Colombier 			conn->write(conn, b, Maxmsg);
2189a747e4fSDavid du Colombier 			n -= Maxmsg;
2199a747e4fSDavid du Colombier 			memmove(b, b+Maxmsg, n);
2209a747e4fSDavid du Colombier 		}
2219a747e4fSDavid du Colombier 		conn->write(conn, b, n);
2229a747e4fSDavid du Colombier 	}
2239a747e4fSDavid du Colombier 
2249a747e4fSDavid du Colombier 	if(buf == nil)
2259a747e4fSDavid du Colombier 		close(fd);
226d854de59SDavid du Colombier 	fprint(2, "secstore: saved %ld bytes\n", len);
2279a747e4fSDavid du Colombier 
2289a747e4fSDavid du Colombier 	return 0;
2299a747e4fSDavid du Colombier }
2309a747e4fSDavid du Colombier 
2319a747e4fSDavid du Colombier static int
removefile(SConn * conn,char * rf)2329a747e4fSDavid du Colombier removefile(SConn *conn, char *rf)
2339a747e4fSDavid du Colombier {
2349a747e4fSDavid du Colombier 	char buf[Maxmsg];
2359a747e4fSDavid du Colombier 
236868c5196SDavid du Colombier 	if(strchr(rf, '/') != nil){
237d854de59SDavid du Colombier 		fprint(2, "secstore: simple filenames, not paths like %s\n", rf);
2389a747e4fSDavid du Colombier 		return -1;
2399a747e4fSDavid du Colombier 	}
2409a747e4fSDavid du Colombier 
241252470cdSDavid du Colombier 	snprint(buf, Maxmsg, "RM %s", rf);
2429a747e4fSDavid du Colombier 	conn->write(conn, (uchar*)buf, strlen(buf));
2439a747e4fSDavid du Colombier 
2449a747e4fSDavid du Colombier 	return 0;
2459a747e4fSDavid du Colombier }
2469a747e4fSDavid du Colombier 
2479a747e4fSDavid du Colombier static int
cmd(AuthConn * c,char ** gf,int * Gflag,char ** pf,char ** rf)248d9306527SDavid du Colombier cmd(AuthConn *c, char **gf, int *Gflag, char **pf, char **rf)
2499a747e4fSDavid du Colombier {
2509a747e4fSDavid du Colombier 	ulong len;
251d9306527SDavid du Colombier 	int rv = -1;
2529a747e4fSDavid du Colombier 	uchar *memfile, *memcur, *memnext;
2539a747e4fSDavid du Colombier 
2549a747e4fSDavid du Colombier 	while(*gf != nil){
2559a747e4fSDavid du Colombier 		if(verbose)
2569a747e4fSDavid du Colombier 			fprint(2, "get %s\n", *gf);
257868c5196SDavid du Colombier 		if(getfile(c->conn, *gf, *Gflag? &memfile: nil, &len,
258868c5196SDavid du Colombier 		    (uchar*)c->pass, c->passlen) < 0)
2599a747e4fSDavid du Colombier 			goto Out;
2609a747e4fSDavid du Colombier 		if(*Gflag){
261868c5196SDavid du Colombier 			/* write 1 line at a time, as required by /mnt/factotum/ctl */
2629a747e4fSDavid du Colombier 			memcur = memfile;
2639a747e4fSDavid du Colombier 			while(len>0){
2649a747e4fSDavid du Colombier 				memnext = (uchar*)strchr((char*)memcur, '\n');
2659a747e4fSDavid du Colombier 				if(memnext){
2669a747e4fSDavid du Colombier 					write(1, memcur, memnext-memcur+1);
2679a747e4fSDavid du Colombier 					len -= memnext-memcur+1;
2689a747e4fSDavid du Colombier 					memcur = memnext+1;
2699a747e4fSDavid du Colombier 				}else{
2709a747e4fSDavid du Colombier 					write(1, memcur, len);
2719a747e4fSDavid du Colombier 					break;
2729a747e4fSDavid du Colombier 				}
2739a747e4fSDavid du Colombier 			}
2749a747e4fSDavid du Colombier 			free(memfile);
2759a747e4fSDavid du Colombier 		}
2769a747e4fSDavid du Colombier 		gf++;
2779a747e4fSDavid du Colombier 		Gflag++;
2789a747e4fSDavid du Colombier 	}
2799a747e4fSDavid du Colombier 	while(*pf != nil){
2809a747e4fSDavid du Colombier 		if(verbose)
2819a747e4fSDavid du Colombier 			fprint(2, "put %s\n", *pf);
282d9306527SDavid du Colombier 		if(putfile(c->conn, *pf, nil, 0, (uchar*)c->pass, c->passlen) < 0)
2839a747e4fSDavid du Colombier 			goto Out;
2849a747e4fSDavid du Colombier 		pf++;
2859a747e4fSDavid du Colombier 	}
2869a747e4fSDavid du Colombier 	while(*rf != nil){
2879a747e4fSDavid du Colombier 		if(verbose)
2889a747e4fSDavid du Colombier 			fprint(2, "rm  %s\n", *rf);
289d9306527SDavid du Colombier 		if(removefile(c->conn, *rf) < 0)
2909a747e4fSDavid du Colombier 			goto Out;
2919a747e4fSDavid du Colombier 		rf++;
2929a747e4fSDavid du Colombier 	}
293d9306527SDavid du Colombier 
294d9306527SDavid du Colombier 	c->conn->write(c->conn, (uchar*)"BYE", 3);
295d9306527SDavid du Colombier 	rv = 0;
296d9306527SDavid du Colombier 
297d9306527SDavid du Colombier Out:
298d9306527SDavid du Colombier 	c->conn->free(c->conn);
299d9306527SDavid du Colombier 	return rv;
300d9306527SDavid du Colombier }
301d9306527SDavid du Colombier 
302d9306527SDavid du Colombier static int
chpasswd(AuthConn * c,char * id)303d9306527SDavid du Colombier chpasswd(AuthConn *c, char *id)
304d9306527SDavid du Colombier {
305d9306527SDavid du Colombier 	int rv = -1, newpasslen = 0;
306868c5196SDavid du Colombier 	ulong len;
307d9306527SDavid du Colombier 	uchar *memfile;
308868c5196SDavid du Colombier 	char *newpass, *passck, *list, *cur, *next, *hexHi;
309b27b55e2SDavid du Colombier 	char *f[8], prompt[128];
310868c5196SDavid du Colombier 	mpint *H, *Hi;
311d9306527SDavid du Colombier 
312d9306527SDavid du Colombier 	H = mpnew(0);
313d9306527SDavid du Colombier 	Hi = mpnew(0);
314868c5196SDavid du Colombier 	/* changing our password is vulnerable to connection failure */
3159a747e4fSDavid du Colombier 	for(;;){
3169a747e4fSDavid du Colombier 		snprint(prompt, sizeof(prompt), "new password for %s: ", id);
317c65b13b8SDavid du Colombier 		newpass = getpassm(prompt);
318b27b55e2SDavid du Colombier 		if(newpass == nil)
3199a747e4fSDavid du Colombier 			goto Out;
320b27b55e2SDavid du Colombier 		if(strlen(newpass) >= 7)
3219a747e4fSDavid du Colombier 			break;
322b27b55e2SDavid du Colombier 		else if(strlen(newpass) == 0){
3239a747e4fSDavid du Colombier 			fprint(2, "!password change aborted\n");
3249a747e4fSDavid du Colombier 			goto Out;
3259a747e4fSDavid du Colombier 		}
3269a747e4fSDavid du Colombier 		print("!password must be at least 7 characters\n");
3279a747e4fSDavid du Colombier 	}
3289a747e4fSDavid du Colombier 	newpasslen = strlen(newpass);
3299a747e4fSDavid du Colombier 	snprint(prompt, sizeof(prompt), "retype password: ");
330c65b13b8SDavid du Colombier 	passck = getpassm(prompt);
331b27b55e2SDavid du Colombier 	if(passck == nil){
332d854de59SDavid du Colombier 		fprint(2, "secstore: getpassm failed\n");
3339a747e4fSDavid du Colombier 		goto Out;
3349a747e4fSDavid du Colombier 	}
335b27b55e2SDavid du Colombier 	if(strcmp(passck, newpass) != 0){
336d854de59SDavid du Colombier 		fprint(2, "secstore: passwords didn't match\n");
3379a747e4fSDavid du Colombier 		goto Out;
3389a747e4fSDavid du Colombier 	}
3399a747e4fSDavid du Colombier 
340d9306527SDavid du Colombier 	c->conn->write(c->conn, (uchar*)"CHPASS", strlen("CHPASS"));
3419a747e4fSDavid du Colombier 	hexHi = PAK_Hi(id, newpass, H, Hi);
342d9306527SDavid du Colombier 	c->conn->write(c->conn, (uchar*)hexHi, strlen(hexHi));
3439a747e4fSDavid du Colombier 	free(hexHi);
3449a747e4fSDavid du Colombier 	mpfree(H);
3459a747e4fSDavid du Colombier 	mpfree(Hi);
3469a747e4fSDavid du Colombier 
347d9306527SDavid du Colombier 	if(getfile(c->conn, ".", (uchar **) &list, &len, nil, 0) < 0){
348d854de59SDavid du Colombier 		fprint(2, "secstore: directory listing failed.\n");
3499a747e4fSDavid du Colombier 		goto Out;
3509a747e4fSDavid du Colombier 	}
3519a747e4fSDavid du Colombier 
3529a747e4fSDavid du Colombier 	/* Loop over files and reencrypt them; try to keep going after error */
3539a747e4fSDavid du Colombier 	for(cur=list; (next=strchr(cur, '\n')) != nil; cur=next+1){
3549a747e4fSDavid du Colombier 		*next = '\0';
3559a747e4fSDavid du Colombier 		if(tokenize(cur, f, nelem(f))< 1)
3569a747e4fSDavid du Colombier 			break;
357d854de59SDavid du Colombier 		fprint(2, "secstore: reencrypting '%s'\n", f[0]);
358868c5196SDavid du Colombier 		if(getfile(c->conn, f[0], &memfile, &len, (uchar*)c->pass,
359868c5196SDavid du Colombier 		    c->passlen) < 0){
360d854de59SDavid du Colombier 			fprint(2, "secstore: getfile of '%s' failed\n", f[0]);
3619a747e4fSDavid du Colombier 			continue;
3629a747e4fSDavid du Colombier 		}
363868c5196SDavid du Colombier 		if(putfile(c->conn, f[0], memfile, len, (uchar*)newpass,
364868c5196SDavid du Colombier 		    newpasslen) < 0)
365d854de59SDavid du Colombier 			fprint(2, "secstore: putfile of '%s' failed\n", f[0]);
3669a747e4fSDavid du Colombier 		free(memfile);
3679a747e4fSDavid du Colombier 	}
3689a747e4fSDavid du Colombier 	free(list);
369d9306527SDavid du Colombier 	c->conn->write(c->conn, (uchar*)"BYE", 3);
3709a747e4fSDavid du Colombier 	rv = 0;
3719a747e4fSDavid du Colombier 
3729a747e4fSDavid du Colombier Out:
3739a747e4fSDavid du Colombier 	if(newpass != nil){
3749a747e4fSDavid du Colombier 		memset(newpass, 0, newpasslen);
3759a747e4fSDavid du Colombier 		free(newpass);
3769a747e4fSDavid du Colombier 	}
377d9306527SDavid du Colombier 	c->conn->free(c->conn);
3789a747e4fSDavid du Colombier 	return rv;
3799a747e4fSDavid du Colombier }
3809a747e4fSDavid du Colombier 
381d9306527SDavid du Colombier static AuthConn*
login(char * id,char * dest,int pass_stdin,int pass_nvram)382369036d9SDavid du Colombier login(char *id, char *dest, int pass_stdin, int pass_nvram)
383d9306527SDavid du Colombier {
384d9306527SDavid du Colombier 	int fd, n, ntry = 0;
385b27b55e2SDavid du Colombier 	char *S, *PINSTA = nil, *nl, s[Maxmsg+1], *pass;
386868c5196SDavid du Colombier 	AuthConn *c;
387d9306527SDavid du Colombier 
388d854de59SDavid du Colombier 	if(dest == nil)
389d854de59SDavid du Colombier 		sysfatal("tried to login with nil dest");
390d9306527SDavid du Colombier 	c = emalloc(sizeof(*c));
391369036d9SDavid du Colombier 	if(pass_nvram){
392369036d9SDavid du Colombier 		if(readnvram(&nvr, 0) < 0)
393369036d9SDavid du Colombier 			exits("readnvram: %r");
394369036d9SDavid du Colombier 		strecpy(c->pass, c->pass+sizeof c->pass, nvr.config);
395369036d9SDavid du Colombier 	}
396d9306527SDavid du Colombier 	if(pass_stdin){
397868c5196SDavid du Colombier 		n = readn(0, s, Maxmsg-2);	/* so len(PINSTA)<Maxmsg-3 */
398d9306527SDavid du Colombier 		if(n < 1)
399d9306527SDavid du Colombier 			exits("no password on standard input");
400d9306527SDavid du Colombier 		s[n] = 0;
401d9306527SDavid du Colombier 		nl = strchr(s, '\n');
402d9306527SDavid du Colombier 		if(nl){
403d9306527SDavid du Colombier 			*nl++ = 0;
404d9306527SDavid du Colombier 			PINSTA = estrdup(nl);
405d9306527SDavid du Colombier 			nl = strchr(PINSTA, '\n');
406d9306527SDavid du Colombier 			if(nl)
407d9306527SDavid du Colombier 				*nl = 0;
408d9306527SDavid du Colombier 		}
409369036d9SDavid du Colombier 		strecpy(c->pass, c->pass+sizeof c->pass, s);
410d9306527SDavid du Colombier 	}
411eed6406fSDavid du Colombier 	for(;;){
412d9306527SDavid du Colombier 		if(verbose)
413d9306527SDavid du Colombier 			fprint(2, "dialing %s\n", dest);
414d9306527SDavid du Colombier 		if((fd = dial(dest, nil, nil, nil)) < 0){
415d854de59SDavid du Colombier 			fprint(2, "secstore: can't dial %s\n", dest);
416d9306527SDavid du Colombier 			free(c);
417d9306527SDavid du Colombier 			return nil;
418d9306527SDavid du Colombier 		}
419d9306527SDavid du Colombier 		if((c->conn = newSConn(fd)) == nil){
420d9306527SDavid du Colombier 			free(c);
421d9306527SDavid du Colombier 			return nil;
422d9306527SDavid du Colombier 		}
423d9306527SDavid du Colombier 		ntry++;
424b27b55e2SDavid du Colombier 		if(!pass_stdin && !pass_nvram){
425c65b13b8SDavid du Colombier 			pass = getpassm("secstore password: ");
426b27b55e2SDavid du Colombier 			if(strlen(pass) >= sizeof c->pass){
427d854de59SDavid du Colombier 				fprint(2, "secstore: password too long, skipping secstore login\n");
428b27b55e2SDavid du Colombier 				exits("password too long");
429b27b55e2SDavid du Colombier 			}
430b27b55e2SDavid du Colombier 			strcpy(c->pass, pass);
431b27b55e2SDavid du Colombier 			memset(pass, 0, strlen(pass));
432b27b55e2SDavid du Colombier 			free(pass);
433b27b55e2SDavid du Colombier 		}
434d9306527SDavid du Colombier 		if(c->pass[0]==0){
435d854de59SDavid du Colombier 			fprint(2, "secstore: null password, skipping secstore login\n");
436d9306527SDavid du Colombier 			exits("no password");
437d9306527SDavid du Colombier 		}
438d9306527SDavid du Colombier 		if(PAKclient(c->conn, id, c->pass, &S) >= 0)
439d9306527SDavid du Colombier 			break;
440d9306527SDavid du Colombier 		c->conn->free(c->conn);
441d9306527SDavid du Colombier 		if(pass_stdin)
442d9306527SDavid du Colombier 			exits("invalid password on standard input");
443369036d9SDavid du Colombier 		if(pass_nvram)
444369036d9SDavid du Colombier 			exits("invalid password in nvram");
445868c5196SDavid du Colombier 		/* and let user try retyping the password */
446d9306527SDavid du Colombier 		if(ntry==3)
447d9306527SDavid du Colombier 			fprint(2, "Enter an empty password to quit.\n");
448d9306527SDavid du Colombier 	}
449d9306527SDavid du Colombier 	c->passlen = strlen(c->pass);
450d9306527SDavid du Colombier 	fprint(2, "%s\n", S);
451d9306527SDavid du Colombier 	free(S);
452d9306527SDavid du Colombier 	if(readstr(c->conn, s) < 0){
453d9306527SDavid du Colombier 		c->conn->free(c->conn);
454d9306527SDavid du Colombier 		free(c);
455d9306527SDavid du Colombier 		return nil;
456d9306527SDavid du Colombier 	}
457d9306527SDavid du Colombier 	if(strcmp(s, "STA") == 0){
458d9306527SDavid du Colombier 		long sn;
459868c5196SDavid du Colombier 
460d9306527SDavid du Colombier 		if(pass_stdin){
461d9306527SDavid du Colombier 			if(PINSTA)
462868c5196SDavid du Colombier 				strncpy(s+3, PINSTA, sizeof s - 3);
463d9306527SDavid du Colombier 			else
464d9306527SDavid du Colombier 				exits("missing PIN+SecureID on standard input");
465d9306527SDavid du Colombier 			free(PINSTA);
466d9306527SDavid du Colombier 		}else{
467c65b13b8SDavid du Colombier 			pass = getpassm("STA PIN+SecureID: ");
468868c5196SDavid du Colombier 			strncpy(s+3, pass, sizeof s - 4);
469b27b55e2SDavid du Colombier 			memset(pass, 0, strlen(pass));
470b27b55e2SDavid du Colombier 			free(pass);
471d9306527SDavid du Colombier 		}
472d9306527SDavid du Colombier 		sn = strlen(s+3);
473d9306527SDavid du Colombier 		if(verbose)
474d9306527SDavid du Colombier 			fprint(2, "%ld\n", sn);
475d9306527SDavid du Colombier 		c->conn->write(c->conn, (uchar*)s, sn+3);
4767ec5746aSDavid du Colombier 		readstr(c->conn, s);	/* TODO: check for error? */
477d9306527SDavid du Colombier 	}
478d9306527SDavid du Colombier 	if(strcmp(s, "OK") != 0){
479*2e54da42SDavid du Colombier 		fprint(2, "%s: %s\n", argv0, s);
480d9306527SDavid du Colombier 		c->conn->free(c->conn);
481d9306527SDavid du Colombier 		free(c);
482d9306527SDavid du Colombier 		return nil;
483d9306527SDavid du Colombier 	}
484d9306527SDavid du Colombier 	return c;
485d9306527SDavid du Colombier }
486d9306527SDavid du Colombier 
487868c5196SDavid du Colombier void
main(int argc,char ** argv)4889a747e4fSDavid du Colombier main(int argc, char **argv)
4899a747e4fSDavid du Colombier {
490369036d9SDavid du Colombier 	int chpass = 0, pass_stdin = 0, pass_nvram = 0, rc;
4919a747e4fSDavid du Colombier 	int ngfile = 0, npfile = 0, nrfile = 0, Gflag[MAXFILES+1];
4929a747e4fSDavid du Colombier 	char *serve, *tcpserve, *user;
493868c5196SDavid du Colombier 	char *gfile[MAXFILES], *pfile[MAXFILES], *rfile[MAXFILES];
494d9306527SDavid du Colombier 	AuthConn *c;
4959a747e4fSDavid du Colombier 
4969a747e4fSDavid du Colombier 	serve = "$auth";
497c402ecc6SDavid du Colombier 	user = getuser();
4989a747e4fSDavid du Colombier 	memset(Gflag, 0, sizeof Gflag);
4999a747e4fSDavid du Colombier 
5009a747e4fSDavid du Colombier 	ARGBEGIN{
5019a747e4fSDavid du Colombier 	case 'c':
5029a747e4fSDavid du Colombier 		chpass = 1;
5039a747e4fSDavid du Colombier 		break;
5049a747e4fSDavid du Colombier 	case 'G':
5059a747e4fSDavid du Colombier 		Gflag[ngfile]++;
5069a747e4fSDavid du Colombier 		/* fall through */
5079a747e4fSDavid du Colombier 	case 'g':
5089a747e4fSDavid du Colombier 		if(ngfile >= MAXFILES)
5099a747e4fSDavid du Colombier 			exits("too many gfiles");
510868c5196SDavid du Colombier 		gfile[ngfile++] = EARGF(usage());
5119a747e4fSDavid du Colombier 		break;
512d9306527SDavid du Colombier 	case 'i':
513d9306527SDavid du Colombier 		pass_stdin = 1;
514d9306527SDavid du Colombier 		break;
515369036d9SDavid du Colombier 	case 'n':
516369036d9SDavid du Colombier 		pass_nvram = 1;
517369036d9SDavid du Colombier 		break;
5189a747e4fSDavid du Colombier 	case 'p':
5199a747e4fSDavid du Colombier 		if(npfile >= MAXFILES)
5209a747e4fSDavid du Colombier 			exits("too many pfiles");
521868c5196SDavid du Colombier 		pfile[npfile++] = EARGF(usage());
5229a747e4fSDavid du Colombier 		break;
5239a747e4fSDavid du Colombier 	case 'r':
5249a747e4fSDavid du Colombier 		if(nrfile >= MAXFILES)
5259a747e4fSDavid du Colombier 			exits("too many rfiles");
526868c5196SDavid du Colombier 		rfile[nrfile++] = EARGF(usage());
5279a747e4fSDavid du Colombier 		break;
5289a747e4fSDavid du Colombier 	case 's':
5299a747e4fSDavid du Colombier 		serve = EARGF(usage());
5309a747e4fSDavid du Colombier 		break;
5319a747e4fSDavid du Colombier 	case 'u':
5329a747e4fSDavid du Colombier 		user = EARGF(usage());
5339a747e4fSDavid du Colombier 		break;
5349a747e4fSDavid du Colombier 	case 'v':
5359a747e4fSDavid du Colombier 		verbose++;
5369a747e4fSDavid du Colombier 		break;
5375d459b5aSDavid du Colombier 	default:
5385d459b5aSDavid du Colombier 		usage();
5395d459b5aSDavid du Colombier 		break;
5409a747e4fSDavid du Colombier 	}ARGEND;
5419a747e4fSDavid du Colombier 	gfile[ngfile] = nil;
5429a747e4fSDavid du Colombier 	pfile[npfile] = nil;
5439a747e4fSDavid du Colombier 	rfile[nrfile] = nil;
5449a747e4fSDavid du Colombier 
5459a747e4fSDavid du Colombier 	if(argc!=0 || user==nil)
5469a747e4fSDavid du Colombier 		usage();
5479a747e4fSDavid du Colombier 
5489a747e4fSDavid du Colombier 	if(chpass && (ngfile || npfile || nrfile)){
549d854de59SDavid du Colombier 		fprint(2, "secstore: Get, put, and remove invalid with password change.\n");
5509a747e4fSDavid du Colombier 		exits("usage");
5519a747e4fSDavid du Colombier 	}
5529a747e4fSDavid du Colombier 
553868c5196SDavid du Colombier 	rc = strlen(serve) + sizeof "tcp!!99990";
5543ff48bf5SDavid du Colombier 	tcpserve = emalloc(rc);
5553ff48bf5SDavid du Colombier 	if(strchr(serve,'!'))
5569a747e4fSDavid du Colombier 		strcpy(tcpserve, serve);
5579a747e4fSDavid du Colombier 	else
5589a747e4fSDavid du Colombier 		snprint(tcpserve, rc, "tcp!%s!5356", serve);
559369036d9SDavid du Colombier 	c = login(user, tcpserve, pass_stdin, pass_nvram);
5609a747e4fSDavid du Colombier 	free(tcpserve);
561d854de59SDavid du Colombier 	if(c == nil)
562d854de59SDavid du Colombier 		sysfatal("secstore authentication failed");
563d9306527SDavid du Colombier 	if(chpass)
564d9306527SDavid du Colombier 		rc = chpasswd(c, user);
565d9306527SDavid du Colombier 	else
566d9306527SDavid du Colombier 		rc = cmd(c, gfile, Gflag, pfile, rfile);
567d854de59SDavid du Colombier 	if(rc < 0)
568d854de59SDavid du Colombier 		sysfatal("secstore cmd failed");
5699a747e4fSDavid du Colombier 	exits("");
5709a747e4fSDavid du Colombier }
571