xref: /plan9/sys/src/cmd/auth/factotum/secstore.c (revision d854de5991c7e0df387efa3ede5edcfdd69b65da)
19a747e4fSDavid du Colombier /*
29a747e4fSDavid du Colombier  * Various files from /sys/src/cmd/auth/secstore, just enough
39a747e4fSDavid du Colombier  * to download a file at boot time.
49a747e4fSDavid du Colombier  */
59a747e4fSDavid du Colombier 
69a747e4fSDavid du Colombier #include "dat.h"
79a747e4fSDavid du Colombier #include <ip.h>
89a747e4fSDavid du Colombier 
99a747e4fSDavid du Colombier enum{ CHK = 16};
109a747e4fSDavid du Colombier enum{ MAXFILESIZE = 10*1024*1024 };
119a747e4fSDavid du Colombier 
129a747e4fSDavid du Colombier enum{// PW status bits
139a747e4fSDavid du Colombier 	Enabled 	= (1<<0),
149a747e4fSDavid du Colombier 	STA 		= (1<<1),	// extra SecurID step
159a747e4fSDavid du Colombier };
169a747e4fSDavid du Colombier 
179a747e4fSDavid du Colombier static char testmess[] = "__secstore\tPAK\nC=%s\nm=0\n";
189a747e4fSDavid du Colombier 
199a747e4fSDavid du Colombier int
havesecstore(void)209a747e4fSDavid du Colombier havesecstore(void)
219a747e4fSDavid du Colombier {
229a747e4fSDavid du Colombier 	int m, n, fd;
239a747e4fSDavid du Colombier 	uchar buf[500];
249a747e4fSDavid du Colombier 
25fb7f0c93SDavid du Colombier 	n = snprint((char*)buf, sizeof buf, testmess, owner);
269a747e4fSDavid du Colombier 	hnputs(buf, 0x8000+n-2);
279a747e4fSDavid du Colombier 
289a747e4fSDavid du Colombier 	fd = secdial();
299a747e4fSDavid du Colombier 	if(fd < 0)
309a747e4fSDavid du Colombier 		return 0;
319a747e4fSDavid du Colombier 	if(write(fd, buf, n) != n || readn(fd, buf, 2) != 2){
329a747e4fSDavid du Colombier 		close(fd);
339a747e4fSDavid du Colombier 		return 0;
349a747e4fSDavid du Colombier 	}
359a747e4fSDavid du Colombier 	n = ((buf[0]&0x7f)<<8) + buf[1];
369a747e4fSDavid du Colombier 	if(n+1 > sizeof buf){
379a747e4fSDavid du Colombier 		werrstr("implausibly large count %d", n);
389a747e4fSDavid du Colombier 		close(fd);
399a747e4fSDavid du Colombier 		return 0;
409a747e4fSDavid du Colombier 	}
419a747e4fSDavid du Colombier 	m = readn(fd, buf, n);
429a747e4fSDavid du Colombier 	close(fd);
439a747e4fSDavid du Colombier 	if(m != n){
449a747e4fSDavid du Colombier 		if(m >= 0)
459a747e4fSDavid du Colombier 			werrstr("short read from secstore");
469a747e4fSDavid du Colombier 		return 0;
479a747e4fSDavid du Colombier 	}
489a747e4fSDavid du Colombier 	buf[n] = 0;
499a747e4fSDavid du Colombier 	if(strcmp((char*)buf, "!account expired") == 0){
509a747e4fSDavid du Colombier 		werrstr("account expired");
519a747e4fSDavid du Colombier 		return 0;
529a747e4fSDavid du Colombier 	}
539a747e4fSDavid du Colombier 	return strcmp((char*)buf, "!account exists") == 0;
549a747e4fSDavid du Colombier }
559a747e4fSDavid du Colombier 
569a747e4fSDavid du Colombier // delimited, authenticated, encrypted connection
579a747e4fSDavid du Colombier enum{ Maxmsg=4096 };	// messages > Maxmsg bytes are truncated
589a747e4fSDavid du Colombier typedef struct SConn SConn;
599a747e4fSDavid du Colombier 
609a747e4fSDavid du Colombier extern SConn* newSConn(int);	// arg is open file descriptor
619a747e4fSDavid du Colombier struct SConn{
629a747e4fSDavid du Colombier 	void *chan;
639a747e4fSDavid du Colombier 	int secretlen;
649a747e4fSDavid du Colombier 	int (*secret)(SConn*, uchar*, int);//
659a747e4fSDavid du Colombier 	int (*read)(SConn*, uchar*, int); // <0 if error;  errmess in buffer
669a747e4fSDavid du Colombier 	int (*write)(SConn*, uchar*, int);
679a747e4fSDavid du Colombier 	void (*free)(SConn*);		// also closes file descriptor
689a747e4fSDavid du Colombier };
699a747e4fSDavid du Colombier // secret(s,b,dir) sets secret for digest, encrypt, using the secretlen
709a747e4fSDavid du Colombier //		bytes in b to form keys 	for the two directions;
719a747e4fSDavid du Colombier //	  set dir=0 in client, dir=1 in server
729a747e4fSDavid du Colombier 
739a747e4fSDavid du Colombier // error convention: write !message in-band
749a747e4fSDavid du Colombier #define readstr secstore_readstr
759a747e4fSDavid du Colombier static void writerr(SConn*, char*);
769a747e4fSDavid du Colombier static int readstr(SConn*, char*);  // call with buf of size Maxmsg+1
779a747e4fSDavid du Colombier 	// returns -1 upon error, with error message in buf
789a747e4fSDavid du Colombier 
799a747e4fSDavid du Colombier typedef struct ConnState {
809a747e4fSDavid du Colombier 	uchar secret[SHA1dlen];
819a747e4fSDavid du Colombier 	ulong seqno;
829a747e4fSDavid du Colombier 	RC4state rc4;
839a747e4fSDavid du Colombier } ConnState;
849a747e4fSDavid du Colombier 
859a747e4fSDavid du Colombier typedef struct SS{
869a747e4fSDavid du Colombier 	int fd;		// file descriptor for read/write of encrypted data
879a747e4fSDavid du Colombier 	int alg;	// if nonzero, "alg sha rc4_128"
889a747e4fSDavid du Colombier 	ConnState in, out;
899a747e4fSDavid du Colombier } SS;
909a747e4fSDavid du Colombier 
919a747e4fSDavid du Colombier static int
SC_secret(SConn * conn,uchar * sigma,int direction)929a747e4fSDavid du Colombier SC_secret(SConn *conn, uchar *sigma, int direction)
939a747e4fSDavid du Colombier {
949a747e4fSDavid du Colombier 	SS *ss = (SS*)(conn->chan);
959a747e4fSDavid du Colombier 	int nsigma = conn->secretlen;
969a747e4fSDavid du Colombier 
979a747e4fSDavid du Colombier 	if(direction != 0){
989a747e4fSDavid du Colombier 		hmac_sha1(sigma, nsigma, (uchar*)"one", 3, ss->out.secret, nil);
999a747e4fSDavid du Colombier 		hmac_sha1(sigma, nsigma, (uchar*)"two", 3, ss->in.secret, nil);
1009a747e4fSDavid du Colombier 	}else{
1019a747e4fSDavid du Colombier 		hmac_sha1(sigma, nsigma, (uchar*)"two", 3, ss->out.secret, nil);
1029a747e4fSDavid du Colombier 		hmac_sha1(sigma, nsigma, (uchar*)"one", 3, ss->in.secret, nil);
1039a747e4fSDavid du Colombier 	}
1049a747e4fSDavid du Colombier 	setupRC4state(&ss->in.rc4, ss->in.secret, 16); // restrict to 128 bits
1059a747e4fSDavid du Colombier 	setupRC4state(&ss->out.rc4, ss->out.secret, 16);
1069a747e4fSDavid du Colombier 	ss->alg = 1;
1079a747e4fSDavid du Colombier 	return 0;
1089a747e4fSDavid du Colombier }
1099a747e4fSDavid du Colombier 
1109a747e4fSDavid du Colombier static void
hash(uchar secret[SHA1dlen],uchar * data,int len,int seqno,uchar d[SHA1dlen])1119a747e4fSDavid du Colombier hash(uchar secret[SHA1dlen], uchar *data, int len, int seqno, uchar d[SHA1dlen])
1129a747e4fSDavid du Colombier {
1139a747e4fSDavid du Colombier 	DigestState sha;
1149a747e4fSDavid du Colombier 	uchar seq[4];
1159a747e4fSDavid du Colombier 
1169a747e4fSDavid du Colombier 	seq[0] = seqno>>24;
1179a747e4fSDavid du Colombier 	seq[1] = seqno>>16;
1189a747e4fSDavid du Colombier 	seq[2] = seqno>>8;
1199a747e4fSDavid du Colombier 	seq[3] = seqno;
1209a747e4fSDavid du Colombier 	memset(&sha, 0, sizeof sha);
1219a747e4fSDavid du Colombier 	sha1(secret, SHA1dlen, nil, &sha);
1229a747e4fSDavid du Colombier 	sha1(data, len, nil, &sha);
1239a747e4fSDavid du Colombier 	sha1(seq, 4, d, &sha);
1249a747e4fSDavid du Colombier }
1259a747e4fSDavid du Colombier 
1269a747e4fSDavid du Colombier static int
verify(uchar secret[SHA1dlen],uchar * data,int len,int seqno,uchar d[SHA1dlen])1279a747e4fSDavid du Colombier verify(uchar secret[SHA1dlen], uchar *data, int len, int seqno, uchar d[SHA1dlen])
1289a747e4fSDavid du Colombier {
1299a747e4fSDavid du Colombier 	DigestState sha;
1309a747e4fSDavid du Colombier 	uchar seq[4];
1319a747e4fSDavid du Colombier 	uchar digest[SHA1dlen];
1329a747e4fSDavid du Colombier 
1339a747e4fSDavid du Colombier 	seq[0] = seqno>>24;
1349a747e4fSDavid du Colombier 	seq[1] = seqno>>16;
1359a747e4fSDavid du Colombier 	seq[2] = seqno>>8;
1369a747e4fSDavid du Colombier 	seq[3] = seqno;
1379a747e4fSDavid du Colombier 	memset(&sha, 0, sizeof sha);
1389a747e4fSDavid du Colombier 	sha1(secret, SHA1dlen, nil, &sha);
1399a747e4fSDavid du Colombier 	sha1(data, len, nil, &sha);
1409a747e4fSDavid du Colombier 	sha1(seq, 4, digest, &sha);
1419a747e4fSDavid du Colombier 	return memcmp(d, digest, SHA1dlen);
1429a747e4fSDavid du Colombier }
1439a747e4fSDavid du Colombier 
1449a747e4fSDavid du Colombier static int
SC_read(SConn * conn,uchar * buf,int n)1459a747e4fSDavid du Colombier SC_read(SConn *conn, uchar *buf, int n)
1469a747e4fSDavid du Colombier {
1479a747e4fSDavid du Colombier 	SS *ss = (SS*)(conn->chan);
1489a747e4fSDavid du Colombier 	uchar count[2], digest[SHA1dlen];
1499a747e4fSDavid du Colombier 	int len, nr;
1509a747e4fSDavid du Colombier 
1519a747e4fSDavid du Colombier 	if(read(ss->fd, count, 2) != 2 || count[0]&0x80 == 0){
1529a747e4fSDavid du Colombier 		werrstr("!SC_read invalid count");
1539a747e4fSDavid du Colombier 		return -1;
1549a747e4fSDavid du Colombier 	}
1559a747e4fSDavid du Colombier 	len = (count[0]&0x7f)<<8 | count[1];	// SSL-style count; no pad
1569a747e4fSDavid du Colombier 	if(ss->alg){
1579a747e4fSDavid du Colombier 		len -= SHA1dlen;
1589a747e4fSDavid du Colombier 		if(len <= 0 || readn(ss->fd, digest, SHA1dlen) != SHA1dlen){
1599a747e4fSDavid du Colombier 			werrstr("!SC_read missing sha1");
1609a747e4fSDavid du Colombier 			return -1;
1619a747e4fSDavid du Colombier 		}
1629a747e4fSDavid du Colombier 		if(len > n || readn(ss->fd, buf, len) != len){
1639a747e4fSDavid du Colombier 			werrstr("!SC_read missing data");
1649a747e4fSDavid du Colombier 			return -1;
1659a747e4fSDavid du Colombier 		}
1669a747e4fSDavid du Colombier 		rc4(&ss->in.rc4, digest, SHA1dlen);
1679a747e4fSDavid du Colombier 		rc4(&ss->in.rc4, buf, len);
1689a747e4fSDavid du Colombier 		if(verify(ss->in.secret, buf, len, ss->in.seqno, digest) != 0){
1699a747e4fSDavid du Colombier 			werrstr("!SC_read integrity check failed");
1709a747e4fSDavid du Colombier 			return -1;
1719a747e4fSDavid du Colombier 		}
1729a747e4fSDavid du Colombier 	}else{
1739a747e4fSDavid du Colombier 		if(len <= 0 || len > n){
1749a747e4fSDavid du Colombier 			werrstr("!SC_read implausible record length");
1759a747e4fSDavid du Colombier 			return -1;
1769a747e4fSDavid du Colombier 		}
1779a747e4fSDavid du Colombier 		if( (nr = readn(ss->fd, buf, len)) != len){
1789a747e4fSDavid du Colombier 			werrstr("!SC_read expected %d bytes, but got %d", len, nr);
1799a747e4fSDavid du Colombier 			return -1;
1809a747e4fSDavid du Colombier 		}
1819a747e4fSDavid du Colombier 	}
1829a747e4fSDavid du Colombier 	ss->in.seqno++;
1839a747e4fSDavid du Colombier 	return len;
1849a747e4fSDavid du Colombier }
1859a747e4fSDavid du Colombier 
1869a747e4fSDavid du Colombier static int
SC_write(SConn * conn,uchar * buf,int n)1879a747e4fSDavid du Colombier SC_write(SConn *conn, uchar *buf, int n)
1889a747e4fSDavid du Colombier {
1899a747e4fSDavid du Colombier 	SS *ss = (SS*)(conn->chan);
19028495efeSDavid du Colombier 	uchar count[2], digest[SHA1dlen], enc[Maxmsg+1];
1919a747e4fSDavid du Colombier 	int len;
1929a747e4fSDavid du Colombier 
1939a747e4fSDavid du Colombier 	if(n <= 0 || n > Maxmsg+1){
1949a747e4fSDavid du Colombier 		werrstr("!SC_write invalid n %d", n);
1959a747e4fSDavid du Colombier 		return -1;
1969a747e4fSDavid du Colombier 	}
1979a747e4fSDavid du Colombier 	len = n;
1989a747e4fSDavid du Colombier 	if(ss->alg)
1999a747e4fSDavid du Colombier 		len += SHA1dlen;
2009a747e4fSDavid du Colombier 	count[0] = 0x80 | len>>8;
2019a747e4fSDavid du Colombier 	count[1] = len;
2029a747e4fSDavid du Colombier 	if(write(ss->fd, count, 2) != 2){
2039a747e4fSDavid du Colombier 		werrstr("!SC_write invalid count");
2049a747e4fSDavid du Colombier 		return -1;
2059a747e4fSDavid du Colombier 	}
2069a747e4fSDavid du Colombier 	if(ss->alg){
2079a747e4fSDavid du Colombier 		hash(ss->out.secret, buf, n, ss->out.seqno, digest);
2089a747e4fSDavid du Colombier 		rc4(&ss->out.rc4, digest, SHA1dlen);
20928495efeSDavid du Colombier 		memcpy(enc, buf, n);
21028495efeSDavid du Colombier 		rc4(&ss->out.rc4, enc, n);
2119a747e4fSDavid du Colombier 		if(write(ss->fd, digest, SHA1dlen) != SHA1dlen ||
21228495efeSDavid du Colombier 				write(ss->fd, enc, n) != n){
2139a747e4fSDavid du Colombier 			werrstr("!SC_write error on send");
2149a747e4fSDavid du Colombier 			return -1;
2159a747e4fSDavid du Colombier 		}
2169a747e4fSDavid du Colombier 	}else{
2179a747e4fSDavid du Colombier 		if(write(ss->fd, buf, n) != n){
2189a747e4fSDavid du Colombier 			werrstr("!SC_write error on send");
2199a747e4fSDavid du Colombier 			return -1;
2209a747e4fSDavid du Colombier 		}
2219a747e4fSDavid du Colombier 	}
2229a747e4fSDavid du Colombier 	ss->out.seqno++;
2239a747e4fSDavid du Colombier 	return n;
2249a747e4fSDavid du Colombier }
2259a747e4fSDavid du Colombier 
2269a747e4fSDavid du Colombier static void
SC_free(SConn * conn)2279a747e4fSDavid du Colombier SC_free(SConn *conn)
2289a747e4fSDavid du Colombier {
2299a747e4fSDavid du Colombier 	SS *ss = (SS*)(conn->chan);
2309a747e4fSDavid du Colombier 
2319a747e4fSDavid du Colombier 	close(ss->fd);
2329a747e4fSDavid du Colombier 	free(ss);
2339a747e4fSDavid du Colombier 	free(conn);
2349a747e4fSDavid du Colombier }
2359a747e4fSDavid du Colombier 
2369a747e4fSDavid du Colombier SConn*
newSConn(int fd)2379a747e4fSDavid du Colombier newSConn(int fd)
2389a747e4fSDavid du Colombier {
2399a747e4fSDavid du Colombier 	SS *ss;
2409a747e4fSDavid du Colombier 	SConn *conn;
2419a747e4fSDavid du Colombier 
2429a747e4fSDavid du Colombier 	if(fd < 0)
2439a747e4fSDavid du Colombier 		return nil;
2449a747e4fSDavid du Colombier 	ss = (SS*)emalloc(sizeof(*ss));
2459a747e4fSDavid du Colombier 	conn = (SConn*)emalloc(sizeof(*conn));
2469a747e4fSDavid du Colombier 	ss->fd  = fd;
2479a747e4fSDavid du Colombier 	ss->alg = 0;
2489a747e4fSDavid du Colombier 	conn->chan = (void*)ss;
2499a747e4fSDavid du Colombier 	conn->secretlen = SHA1dlen;
2509a747e4fSDavid du Colombier 	conn->free = SC_free;
2519a747e4fSDavid du Colombier 	conn->secret = SC_secret;
2529a747e4fSDavid du Colombier 	conn->read = SC_read;
2539a747e4fSDavid du Colombier 	conn->write = SC_write;
2549a747e4fSDavid du Colombier 	return conn;
2559a747e4fSDavid du Colombier }
2569a747e4fSDavid du Colombier 
2579a747e4fSDavid du Colombier static void
writerr(SConn * conn,char * s)2589a747e4fSDavid du Colombier writerr(SConn *conn, char *s)
2599a747e4fSDavid du Colombier {
2609a747e4fSDavid du Colombier 	char buf[Maxmsg];
2619a747e4fSDavid du Colombier 
2629a747e4fSDavid du Colombier 	snprint(buf, Maxmsg, "!%s", s);
2639a747e4fSDavid du Colombier 	conn->write(conn, (uchar*)buf, strlen(buf));
2649a747e4fSDavid du Colombier }
2659a747e4fSDavid du Colombier 
2669a747e4fSDavid du Colombier static int
readstr(SConn * conn,char * s)2679a747e4fSDavid du Colombier readstr(SConn *conn, char *s)
2689a747e4fSDavid du Colombier {
2699a747e4fSDavid du Colombier 	int n;
2709a747e4fSDavid du Colombier 
2719a747e4fSDavid du Colombier 	n = conn->read(conn, (uchar*)s, Maxmsg);
2729a747e4fSDavid du Colombier 	if(n >= 0){
2739a747e4fSDavid du Colombier 		s[n] = 0;
2749a747e4fSDavid du Colombier 		if(s[0] == '!'){
2759a747e4fSDavid du Colombier 			memmove(s, s+1, n);
2769a747e4fSDavid du Colombier 			n = -1;
2779a747e4fSDavid du Colombier 		}
2789a747e4fSDavid du Colombier 	}else{
2799a747e4fSDavid du Colombier 		strcpy(s, "read error");
2809a747e4fSDavid du Colombier 	}
2819a747e4fSDavid du Colombier 	return n;
2829a747e4fSDavid du Colombier }
2839a747e4fSDavid du Colombier 
2849a747e4fSDavid du Colombier static int
getfile(SConn * conn,uchar * key,int nkey)2859a747e4fSDavid du Colombier getfile(SConn *conn, uchar *key, int nkey)
2869a747e4fSDavid du Colombier {
2879a747e4fSDavid du Colombier 	char *buf;
2889a747e4fSDavid du Colombier 	int nbuf, n, nr, len;
2899a747e4fSDavid du Colombier 	char s[Maxmsg+1], *gf, *p, *q;
2909a747e4fSDavid du Colombier 	uchar skey[SHA1dlen], ib[Maxmsg+CHK], *ibr, *ibw;
2919a747e4fSDavid du Colombier 	AESstate aes;
2929a747e4fSDavid du Colombier 	DigestState *sha;
2939a747e4fSDavid du Colombier 
2949a747e4fSDavid du Colombier 	gf = "factotum";
2959a747e4fSDavid du Colombier 	memset(&aes, 0, sizeof aes);
2969a747e4fSDavid du Colombier 
2979a747e4fSDavid du Colombier 	snprint(s, Maxmsg, "GET %s\n", gf);
2989a747e4fSDavid du Colombier 	conn->write(conn, (uchar*)s, strlen(s));
2999a747e4fSDavid du Colombier 
3009a747e4fSDavid du Colombier 	/* get file size */
3019a747e4fSDavid du Colombier 	s[0] = '\0';
3029a747e4fSDavid du Colombier 	if(readstr(conn, s) < 0){
3039a747e4fSDavid du Colombier 		werrstr("secstore: %r");
3049a747e4fSDavid du Colombier 		return -1;
3059a747e4fSDavid du Colombier 	}
3069a747e4fSDavid du Colombier 	if((len = atoi(s)) < 0){
3079a747e4fSDavid du Colombier 		werrstr("secstore: remote file %s does not exist", gf);
3089a747e4fSDavid du Colombier 		return -1;
3099a747e4fSDavid du Colombier 	}else if(len > MAXFILESIZE){//assert
3109a747e4fSDavid du Colombier 		werrstr("secstore: implausible file size %d for %s", len, gf);
3119a747e4fSDavid du Colombier 		return -1;
3129a747e4fSDavid du Colombier 	}
3139a747e4fSDavid du Colombier 
3149a747e4fSDavid du Colombier 	ibr = ibw = ib;
3159a747e4fSDavid du Colombier 	buf = nil;
3169a747e4fSDavid du Colombier 	nbuf = 0;
3179a747e4fSDavid du Colombier 	for(nr=0; nr < len;){
3189a747e4fSDavid du Colombier 		if((n = conn->read(conn, ibw, Maxmsg)) <= 0){
3199a747e4fSDavid du Colombier 			werrstr("secstore: empty file chunk n=%d nr=%d len=%d: %r", n, nr, len);
3209a747e4fSDavid du Colombier 			return -1;
3219a747e4fSDavid du Colombier 		}
3229a747e4fSDavid du Colombier 		nr += n;
3239a747e4fSDavid du Colombier 		ibw += n;
3249a747e4fSDavid du Colombier 		if(!aes.setup){ /* first time, read 16 byte IV */
3259a747e4fSDavid du Colombier 			if(n < 16){
3269a747e4fSDavid du Colombier 				werrstr("secstore: no IV in file");
3279a747e4fSDavid du Colombier 				return -1;
3289a747e4fSDavid du Colombier 			}
3299a747e4fSDavid du Colombier 			sha = sha1((uchar*)"aescbc file", 11, nil, nil);
3309a747e4fSDavid du Colombier 			sha1(key, nkey, skey, sha);
3319a747e4fSDavid du Colombier 			setupAESstate(&aes, skey, AESbsize, ibr);
3329a747e4fSDavid du Colombier 			memset(skey, 0, sizeof skey);
3339a747e4fSDavid du Colombier 			ibr += AESbsize;
3349a747e4fSDavid du Colombier 			n -= AESbsize;
3359a747e4fSDavid du Colombier 		}
3369a747e4fSDavid du Colombier 		aesCBCdecrypt(ibw-n, n, &aes);
3379a747e4fSDavid du Colombier 		n = ibw-ibr-CHK;
3389a747e4fSDavid du Colombier 		if(n > 0){
3399a747e4fSDavid du Colombier 			buf = erealloc(buf, nbuf+n+1);
3409a747e4fSDavid du Colombier 			memmove(buf+nbuf, ibr, n);
3419a747e4fSDavid du Colombier 			nbuf += n;
3429a747e4fSDavid du Colombier 			ibr += n;
3439a747e4fSDavid du Colombier 		}
3449a747e4fSDavid du Colombier 		memmove(ib, ibr, ibw-ibr);
3459a747e4fSDavid du Colombier 		ibw = ib + (ibw-ibr);
3469a747e4fSDavid du Colombier 		ibr = ib;
3479a747e4fSDavid du Colombier 	}
3489a747e4fSDavid du Colombier 	n = ibw-ibr;
3499a747e4fSDavid du Colombier 	if((n != CHK) || (memcmp(ib, "XXXXXXXXXXXXXXXX", CHK) != 0)){
3509a747e4fSDavid du Colombier 		werrstr("secstore: decrypted file failed to authenticate!");
3519a747e4fSDavid du Colombier 		free(buf);
3529a747e4fSDavid du Colombier 		return -1;
3539a747e4fSDavid du Colombier 	}
3549a747e4fSDavid du Colombier 	if(nbuf == 0){
3559a747e4fSDavid du Colombier 		werrstr("secstore got empty file");
3569a747e4fSDavid du Colombier 		return -1;
3579a747e4fSDavid du Colombier 	}
3589a747e4fSDavid du Colombier 	buf[nbuf] = '\0';
3599a747e4fSDavid du Colombier 	p = buf;
3609a747e4fSDavid du Colombier 	n = 0;
3619a747e4fSDavid du Colombier 	while(p){
3629a747e4fSDavid du Colombier 		if(q = strchr(p, '\n'))
3639a747e4fSDavid du Colombier 			*q++ = '\0';
3649a747e4fSDavid du Colombier 		n++;
36570b8e010SDavid du Colombier 		if(ctlwrite(p, 0) < 0)
366*d854de59SDavid du Colombier 			fprint(2, "factotum: secstore(%s) line %d: %r\n", gf, n);
3679a747e4fSDavid du Colombier 		p = q;
3689a747e4fSDavid du Colombier 	}
369d9306527SDavid du Colombier 	free(buf);
3709a747e4fSDavid du Colombier 	return 0;
3719a747e4fSDavid du Colombier }
3729a747e4fSDavid du Colombier 
3739a747e4fSDavid du Colombier static char VERSION[] = "secstore";
3749a747e4fSDavid du Colombier 
3759a747e4fSDavid du Colombier typedef struct PAKparams{
3769a747e4fSDavid du Colombier 	mpint *q, *p, *r, *g;
3779a747e4fSDavid du Colombier } PAKparams;
3789a747e4fSDavid du Colombier 
3799a747e4fSDavid du Colombier static PAKparams *pak;
3809a747e4fSDavid du Colombier 
3819a747e4fSDavid du Colombier // This group was generated by the seed EB7B6E35F7CD37B511D96C67D6688CC4DD440E1E.
3829a747e4fSDavid du Colombier static void
initPAKparams(void)3839a747e4fSDavid du Colombier initPAKparams(void)
3849a747e4fSDavid du Colombier {
3859a747e4fSDavid du Colombier 	if(pak)
3869a747e4fSDavid du Colombier 		return;
3879a747e4fSDavid du Colombier 	pak = (PAKparams*)emalloc(sizeof(*pak));
3889a747e4fSDavid du Colombier 	pak->q = strtomp("E0F0EF284E10796C5A2A511E94748BA03C795C13", nil, 16, nil);
3899a747e4fSDavid du Colombier 	pak->p = strtomp("C41CFBE4D4846F67A3DF7DE9921A49D3B42DC33728427AB159CEC8CBBD"
3909a747e4fSDavid du Colombier 		"B12B5F0C244F1A734AEB9840804EA3C25036AD1B61AFF3ABBC247CD4B384224567A86"
3919a747e4fSDavid du Colombier 		"3A6F020E7EE9795554BCD08ABAD7321AF27E1E92E3DB1C6E7E94FAAE590AE9C48F96D9"
3929a747e4fSDavid du Colombier 		"3D178E809401ABE8A534A1EC44359733475A36A70C7B425125062B1142D", nil, 16, nil);
3939a747e4fSDavid du Colombier 	pak->r = strtomp("DF310F4E54A5FEC5D86D3E14863921E834113E060F90052AD332B3241CEF"
3949a747e4fSDavid du Colombier 		"2497EFA0303D6344F7C819691A0F9C4A773815AF8EAECFB7EC1D98F039F17A32A7E887"
3959a747e4fSDavid du Colombier 		"D97251A927D093F44A55577F4D70444AEBD06B9B45695EC23962B175F266895C67D21"
3969a747e4fSDavid du Colombier 		"C4656848614D888A4", nil, 16, nil);
3979a747e4fSDavid du Colombier 	pak->g = strtomp("2F1C308DC46B9A44B52DF7DACCE1208CCEF72F69C743ADD4D2327173444"
3989a747e4fSDavid du Colombier 		"ED6E65E074694246E07F9FD4AE26E0FDDD9F54F813C40CB9BCD4338EA6F242AB94CD41"
3999a747e4fSDavid du Colombier 		"0E676C290368A16B1A3594877437E516C53A6EEE5493A038A017E955E218E7819734E3E"
4009a747e4fSDavid du Colombier 		"2A6E0BAE08B14258F8C03CC1B30E0DDADFCF7CEDF0727684D3D255F1", nil, 16, nil);
4019a747e4fSDavid du Colombier }
4029a747e4fSDavid du Colombier 
4039a747e4fSDavid du Colombier // H = (sha(ver,C,sha(passphrase)))^r mod p,
4049a747e4fSDavid du Colombier // a hash function expensive to attack by brute force.
4059a747e4fSDavid du Colombier static void
longhash(char * ver,char * C,uchar * passwd,mpint * H)4069a747e4fSDavid du Colombier longhash(char *ver, char *C, uchar *passwd, mpint *H)
4079a747e4fSDavid du Colombier {
4089a747e4fSDavid du Colombier 	uchar *Cp;
4099a747e4fSDavid du Colombier 	int i, n, nver, nC;
4109a747e4fSDavid du Colombier 	uchar buf[140], key[1];
4119a747e4fSDavid du Colombier 
4129a747e4fSDavid du Colombier 	nver = strlen(ver);
4139a747e4fSDavid du Colombier 	nC = strlen(C);
4149a747e4fSDavid du Colombier 	n = nver + nC + SHA1dlen;
4159a747e4fSDavid du Colombier 	Cp = (uchar*)emalloc(n);
4169a747e4fSDavid du Colombier 	memmove(Cp, ver, nver);
4179a747e4fSDavid du Colombier 	memmove(Cp+nver, C, nC);
4189a747e4fSDavid du Colombier 	memmove(Cp+nver+nC, passwd, SHA1dlen);
4199a747e4fSDavid du Colombier 	for(i = 0; i < 7; i++){
4209a747e4fSDavid du Colombier 		key[0] = 'A'+i;
4219a747e4fSDavid du Colombier 		hmac_sha1(Cp, n, key, sizeof key, buf+i*SHA1dlen, nil);
4229a747e4fSDavid du Colombier 	}
4239a747e4fSDavid du Colombier 	memset(Cp, 0, n);
4249a747e4fSDavid du Colombier 	free(Cp);
4259a747e4fSDavid du Colombier 	betomp(buf, sizeof buf, H);
4269a747e4fSDavid du Colombier 	mpmod(H, pak->p, H);
4279a747e4fSDavid du Colombier 	mpexp(H, pak->r, pak->p, H);
4289a747e4fSDavid du Colombier }
4299a747e4fSDavid du Colombier 
4309a747e4fSDavid du Colombier // Hi = H^-1 mod p
4319a747e4fSDavid du Colombier static char *
PAK_Hi(char * C,char * passphrase,mpint * H,mpint * Hi)4329a747e4fSDavid du Colombier PAK_Hi(char *C, char *passphrase, mpint *H, mpint *Hi)
4339a747e4fSDavid du Colombier {
4349a747e4fSDavid du Colombier 	uchar passhash[SHA1dlen];
4359a747e4fSDavid du Colombier 
4369a747e4fSDavid du Colombier 	sha1((uchar *)passphrase, strlen(passphrase), passhash, nil);
4379a747e4fSDavid du Colombier 	initPAKparams();
4389a747e4fSDavid du Colombier 	longhash(VERSION, C, passhash, H);
4399a747e4fSDavid du Colombier 	mpinvert(H, pak->p, Hi);
4409a747e4fSDavid du Colombier 	return mptoa(Hi, 64, nil, 0);
4419a747e4fSDavid du Colombier }
4429a747e4fSDavid du Colombier 
4439a747e4fSDavid du Colombier // another, faster, hash function for each party to
4449a747e4fSDavid du Colombier // confirm that the other has the right secrets.
4459a747e4fSDavid du Colombier static void
shorthash(char * mess,char * C,char * S,char * m,char * mu,char * sigma,char * Hi,uchar * digest)4469a747e4fSDavid du Colombier shorthash(char *mess, char *C, char *S, char *m, char *mu, char *sigma, char *Hi, uchar *digest)
4479a747e4fSDavid du Colombier {
4489a747e4fSDavid du Colombier 	SHA1state *state;
4499a747e4fSDavid du Colombier 
4509a747e4fSDavid du Colombier 	state = sha1((uchar*)mess, strlen(mess), 0, 0);
4519a747e4fSDavid du Colombier 	state = sha1((uchar*)C, strlen(C), 0, state);
4529a747e4fSDavid du Colombier 	state = sha1((uchar*)S, strlen(S), 0, state);
4539a747e4fSDavid du Colombier 	state = sha1((uchar*)m, strlen(m), 0, state);
4549a747e4fSDavid du Colombier 	state = sha1((uchar*)mu, strlen(mu), 0, state);
4559a747e4fSDavid du Colombier 	state = sha1((uchar*)sigma, strlen(sigma), 0, state);
4569a747e4fSDavid du Colombier 	state = sha1((uchar*)Hi, strlen(Hi), 0, state);
4579a747e4fSDavid du Colombier 	state = sha1((uchar*)mess, strlen(mess), 0, state);
4589a747e4fSDavid du Colombier 	state = sha1((uchar*)C, strlen(C), 0, state);
4599a747e4fSDavid du Colombier 	state = sha1((uchar*)S, strlen(S), 0, state);
4609a747e4fSDavid du Colombier 	state = sha1((uchar*)m, strlen(m), 0, state);
4619a747e4fSDavid du Colombier 	state = sha1((uchar*)mu, strlen(mu), 0, state);
4629a747e4fSDavid du Colombier 	state = sha1((uchar*)sigma, strlen(sigma), 0, state);
4639a747e4fSDavid du Colombier 	sha1((uchar*)Hi, strlen(Hi), digest, state);
4649a747e4fSDavid du Colombier }
4659a747e4fSDavid du Colombier 
4669a747e4fSDavid du Colombier // On input, conn provides an open channel to the server;
4679a747e4fSDavid du Colombier //	C is the name this client calls itself;
4689a747e4fSDavid du Colombier //	pass is the user's passphrase
4699a747e4fSDavid du Colombier // On output, session secret has been set in conn
4709a747e4fSDavid du Colombier //	(unless return code is negative, which means failure).
4719a747e4fSDavid du Colombier //    If pS is not nil, it is set to the (alloc'd) name the server calls itself.
4729a747e4fSDavid du Colombier static int
PAKclient(SConn * conn,char * C,char * pass,char ** pS)4739a747e4fSDavid du Colombier PAKclient(SConn *conn, char *C, char *pass, char **pS)
4749a747e4fSDavid du Colombier {
4759a747e4fSDavid du Colombier 	char *mess, *mess2, *eol, *S, *hexmu, *ks, *hexm, *hexsigma = nil, *hexHi;
4769a747e4fSDavid du Colombier 	char kc[2*SHA1dlen+1];
4779a747e4fSDavid du Colombier 	uchar digest[SHA1dlen];
4789a747e4fSDavid du Colombier 	int rc = -1, n;
4799a747e4fSDavid du Colombier 	mpint *x, *m = mpnew(0), *mu = mpnew(0), *sigma = mpnew(0);
4809a747e4fSDavid du Colombier 	mpint *H = mpnew(0), *Hi = mpnew(0);
4819a747e4fSDavid du Colombier 
4829a747e4fSDavid du Colombier 	hexHi = PAK_Hi(C, pass, H, Hi);
4839a747e4fSDavid du Colombier 
4849a747e4fSDavid du Colombier 	// random 1<=x<=q-1; send C, m=g**x H
4859a747e4fSDavid du Colombier 	x = mprand(164, genrandom, nil);
4869a747e4fSDavid du Colombier 	mpmod(x, pak->q, x);
4879a747e4fSDavid du Colombier 	if(mpcmp(x, mpzero) == 0)
4889a747e4fSDavid du Colombier 		mpassign(mpone, x);
4899a747e4fSDavid du Colombier 	mpexp(pak->g, x, pak->p, m);
4909a747e4fSDavid du Colombier 	mpmul(m, H, m);
4919a747e4fSDavid du Colombier 	mpmod(m, pak->p, m);
4929a747e4fSDavid du Colombier 	hexm = mptoa(m, 64, nil, 0);
4939a747e4fSDavid du Colombier 	mess = (char*)emalloc(2*Maxmsg+2);
4949a747e4fSDavid du Colombier 	mess2 = mess+Maxmsg+1;
4959a747e4fSDavid du Colombier 	snprint(mess, Maxmsg, "%s\tPAK\nC=%s\nm=%s\n", VERSION, C, hexm);
4969a747e4fSDavid du Colombier 	conn->write(conn, (uchar*)mess, strlen(mess));
4979a747e4fSDavid du Colombier 
4989a747e4fSDavid du Colombier 	// recv g**y, S, check hash1(g**xy)
4999a747e4fSDavid du Colombier 	if(readstr(conn, mess) < 0){
500*d854de59SDavid du Colombier 		fprint(2, "factotum: error: %s\n", mess);
5019a747e4fSDavid du Colombier 		writerr(conn, "couldn't read g**y");
5029a747e4fSDavid du Colombier 		goto done;
5039a747e4fSDavid du Colombier 	}
5049a747e4fSDavid du Colombier 	eol = strchr(mess, '\n');
5059a747e4fSDavid du Colombier 	if(strncmp("mu=", mess, 3) != 0 || !eol || strncmp("\nk=", eol, 3) != 0){
5069a747e4fSDavid du Colombier 		writerr(conn, "verifier syntax error");
5079a747e4fSDavid du Colombier 		goto done;
5089a747e4fSDavid du Colombier 	}
5099a747e4fSDavid du Colombier 	hexmu = mess+3;
5109a747e4fSDavid du Colombier 	*eol = 0;
5119a747e4fSDavid du Colombier 	ks = eol+3;
5129a747e4fSDavid du Colombier 	eol = strchr(ks, '\n');
5139a747e4fSDavid du Colombier 	if(!eol || strncmp("\nS=", eol, 3) != 0){
5149a747e4fSDavid du Colombier 		writerr(conn, "verifier syntax error for secstore 1.0");
5159a747e4fSDavid du Colombier 		goto done;
5169a747e4fSDavid du Colombier 	}
5179a747e4fSDavid du Colombier 	*eol = 0;
5189a747e4fSDavid du Colombier 	S = eol+3;
5199a747e4fSDavid du Colombier 	eol = strchr(S, '\n');
5209a747e4fSDavid du Colombier 	if(!eol){
5219a747e4fSDavid du Colombier 		writerr(conn, "verifier syntax error for secstore 1.0");
5229a747e4fSDavid du Colombier 		goto done;
5239a747e4fSDavid du Colombier 	}
5249a747e4fSDavid du Colombier 	*eol = 0;
5259a747e4fSDavid du Colombier 	if(pS)
5269a747e4fSDavid du Colombier 		*pS = estrdup(S);
5279a747e4fSDavid du Colombier 	strtomp(hexmu, nil, 64, mu);
5289a747e4fSDavid du Colombier 	mpexp(mu, x, pak->p, sigma);
5299a747e4fSDavid du Colombier 	hexsigma = mptoa(sigma, 64, nil, 0);
5309a747e4fSDavid du Colombier 	shorthash("server", C, S, hexm, hexmu, hexsigma, hexHi, digest);
5319a747e4fSDavid du Colombier 	enc64(kc, sizeof kc, digest, SHA1dlen);
5329a747e4fSDavid du Colombier 	if(strcmp(ks, kc) != 0){
5339a747e4fSDavid du Colombier 		writerr(conn, "verifier didn't match");
5349a747e4fSDavid du Colombier 		goto done;
5359a747e4fSDavid du Colombier 	}
5369a747e4fSDavid du Colombier 
5379a747e4fSDavid du Colombier 	// send hash2(g**xy)
5389a747e4fSDavid du Colombier 	shorthash("client", C, S, hexm, hexmu, hexsigma, hexHi, digest);
5399a747e4fSDavid du Colombier 	enc64(kc, sizeof kc, digest, SHA1dlen);
5409a747e4fSDavid du Colombier 	snprint(mess2, Maxmsg, "k'=%s\n", kc);
5419a747e4fSDavid du Colombier 	conn->write(conn, (uchar*)mess2, strlen(mess2));
5429a747e4fSDavid du Colombier 
5439a747e4fSDavid du Colombier 	// set session key
5449a747e4fSDavid du Colombier 	shorthash("session", C, S, hexm, hexmu, hexsigma, hexHi, digest);
5459a747e4fSDavid du Colombier 	memset(hexsigma, 0, strlen(hexsigma));
5469a747e4fSDavid du Colombier 	n = conn->secret(conn, digest, 0);
5479a747e4fSDavid du Colombier 	memset(digest, 0, SHA1dlen);
5489a747e4fSDavid du Colombier 	if(n < 0){//assert
5499a747e4fSDavid du Colombier 		writerr(conn, "can't set secret");
5509a747e4fSDavid du Colombier 		goto done;
5519a747e4fSDavid du Colombier 	}
5529a747e4fSDavid du Colombier 
5539a747e4fSDavid du Colombier 	rc = 0;
5549a747e4fSDavid du Colombier done:
5559a747e4fSDavid du Colombier 	mpfree(x);
5569a747e4fSDavid du Colombier 	mpfree(sigma);
5579a747e4fSDavid du Colombier 	mpfree(mu);
5589a747e4fSDavid du Colombier 	mpfree(m);
5599a747e4fSDavid du Colombier 	mpfree(Hi);
5609a747e4fSDavid du Colombier 	mpfree(H);
5619a747e4fSDavid du Colombier 	free(hexsigma);
5629a747e4fSDavid du Colombier 	free(hexHi);
5639a747e4fSDavid du Colombier 	free(hexm);
5649a747e4fSDavid du Colombier 	free(mess);
5659a747e4fSDavid du Colombier 	return rc;
5669a747e4fSDavid du Colombier }
5679a747e4fSDavid du Colombier 
5689a747e4fSDavid du Colombier int
secstorefetch(char * password)5699a747e4fSDavid du Colombier secstorefetch(char *password)
5709a747e4fSDavid du Colombier {
5719a747e4fSDavid du Colombier 	int rv = -1, fd;
5729a747e4fSDavid du Colombier 	char s[Maxmsg+1];
5739a747e4fSDavid du Colombier 	SConn *conn;
5742ebbfa15SDavid du Colombier 	char *pass, *sta;
5759a747e4fSDavid du Colombier 
5769a747e4fSDavid du Colombier 	sta = nil;
5779a747e4fSDavid du Colombier 	conn = nil;
5789a747e4fSDavid du Colombier 	if(password != nil && *password)
5792ebbfa15SDavid du Colombier 		pass = estrdup(password);
5809a747e4fSDavid du Colombier 	else
5819a747e4fSDavid du Colombier 		pass = readcons("secstore password", nil, 1);
5822ebbfa15SDavid du Colombier 	if(pass==nil || strlen(pass)==0){
5839a747e4fSDavid du Colombier 		werrstr("cancel");
5849a747e4fSDavid du Colombier 		goto Out;
5859a747e4fSDavid du Colombier 	}
5869a747e4fSDavid du Colombier 	if((fd = secdial()) < 0)
5879a747e4fSDavid du Colombier 		goto Out;
5889a747e4fSDavid du Colombier 	if((conn = newSConn(fd)) == nil)
5899a747e4fSDavid du Colombier 		goto Out;
590fb7f0c93SDavid du Colombier 	if(PAKclient(conn, owner, pass, nil) < 0){
5919a747e4fSDavid du Colombier 		werrstr("password mistyped?");
5929a747e4fSDavid du Colombier 		goto Out;
5939a747e4fSDavid du Colombier 	}
5949a747e4fSDavid du Colombier 	if(readstr(conn, s) < 0)
5959a747e4fSDavid du Colombier 		goto Out;
5969a747e4fSDavid du Colombier 	if(strcmp(s, "STA") == 0){
5979a747e4fSDavid du Colombier 		sta = readcons("STA PIN+SecureID", nil, 1);
5982ebbfa15SDavid du Colombier 		if(sta==nil || strlen(sta)==0){
5999a747e4fSDavid du Colombier 			werrstr("cancel");
6009a747e4fSDavid du Colombier 			goto Out;
6019a747e4fSDavid du Colombier 		}
6022ebbfa15SDavid du Colombier 		if(strlen(sta) >= sizeof s - 3){
6039a747e4fSDavid du Colombier 			werrstr("STA response too long");
6049a747e4fSDavid du Colombier 			goto Out;
6059a747e4fSDavid du Colombier 		}
6062ebbfa15SDavid du Colombier 		strcpy(s+3, sta);
6079a747e4fSDavid du Colombier 		conn->write(conn, (uchar*)s, strlen(s));
6089a747e4fSDavid du Colombier 		readstr(conn, s);
6099a747e4fSDavid du Colombier 	}
6109a747e4fSDavid du Colombier 	if(strcmp(s, "OK") !=0){
6119a747e4fSDavid du Colombier 		werrstr("%s", s);
6129a747e4fSDavid du Colombier 		goto Out;
6139a747e4fSDavid du Colombier 	}
6142ebbfa15SDavid du Colombier 	if(getfile(conn, (uchar*)pass, strlen(pass)) < 0)
6159a747e4fSDavid du Colombier 		goto Out;
6169a747e4fSDavid du Colombier 	conn->write(conn, (uchar*)"BYE", 3);
6179a747e4fSDavid du Colombier 	rv = 0;
6189a747e4fSDavid du Colombier 
6199a747e4fSDavid du Colombier Out:
6209a747e4fSDavid du Colombier 	if(conn)
6219a747e4fSDavid du Colombier 		conn->free(conn);
6229a747e4fSDavid du Colombier 	if(pass)
6232ebbfa15SDavid du Colombier 		free(pass);
6249a747e4fSDavid du Colombier 	if(sta)
6252ebbfa15SDavid du Colombier 		free(sta);
6269a747e4fSDavid du Colombier 	return rv;
6279a747e4fSDavid du Colombier }
6289a747e4fSDavid du Colombier 
629