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