secstore.c (15627B)
1 /* 2 * Various files from /sys/src/cmd/auth/secstore, just enough 3 * to download a file at boot time. 4 */ 5 6 #include "std.h" 7 #include "dat.h" 8 #include <ip.h> 9 10 enum{ CHK = 16}; 11 enum{ MAXFILESIZE = 10*1024*1024 }; 12 13 enum{/* PW status bits */ 14 Enabled = (1<<0), 15 STA = (1<<1) /* extra SecurID step */ 16 }; 17 18 static char testmess[] = "__secstore\tPAK\nC=%s\nm=0\n"; 19 char *secstore; 20 21 int 22 secdial(void) 23 { 24 char *p; 25 26 p = secstore; 27 if(p == nil) /* else use the authserver */ 28 p = getenv("secstore"); 29 if(p == nil) 30 p = getenv("auth"); 31 if(p == nil) 32 p = "secstore"; 33 34 return dial(netmkaddr(p, "net", "secstore"), 0, 0, 0); 35 } 36 37 38 int 39 havesecstore(void) 40 { 41 int m, n, fd; 42 uchar buf[500]; 43 44 n = snprint((char*)buf, sizeof buf, testmess, owner); 45 hnputs(buf, 0x8000+n-2); 46 47 fd = secdial(); 48 if(fd < 0){ 49 if(debug) 50 fprint(2, "secdial: %r\n"); 51 flog("secdial: %r"); 52 return 0; 53 } 54 if(write(fd, buf, n) != n || readn(fd, buf, 2) != 2){ 55 flog("secstore: no count"); 56 close(fd); 57 return 0; 58 } 59 n = ((buf[0]&0x7f)<<8) + buf[1]; 60 if(n+1 > sizeof buf){ 61 flog("secstore: bad count"); 62 werrstr("implausibly large count %d", n); 63 close(fd); 64 return 0; 65 } 66 m = readn(fd, buf, n); 67 close(fd); 68 if(m != n){ 69 flog("secstore: unexpected eof"); 70 if(m >= 0) 71 werrstr("short read from secstore"); 72 return 0; 73 } 74 buf[n] = 0; 75 if(strcmp((char*)buf, "!account expired") == 0){ 76 flog("secstore: account expired"); 77 werrstr("account expired"); 78 return 0; 79 } 80 if(strcmp((char*)buf, "!account exists") == 0){ 81 flog("secstore: account exists"); 82 return 1; 83 } 84 flog("secstore: %s", buf); 85 return 0; 86 } 87 88 /* delimited, authenticated, encrypted connection */ 89 enum{ Maxmsg=4096 }; /* messages > Maxmsg bytes are truncated */ 90 typedef struct SConn SConn; 91 92 extern SConn* newSConn(int); /* arg is open file descriptor */ 93 struct SConn{ 94 void *chan; 95 int secretlen; 96 int (*secret)(SConn*, uchar*, int);/* */ 97 int (*read)(SConn*, uchar*, int); /* <0 if error; errmess in buffer */ 98 int (*write)(SConn*, uchar*, int); 99 void (*free)(SConn*); /* also closes file descriptor */ 100 }; 101 /* secret(s,b,dir) sets secret for digest, encrypt, using the secretlen */ 102 /* bytes in b to form keys for the two directions; */ 103 /* set dir=0 in client, dir=1 in server */ 104 105 /* error convention: write !message in-band */ 106 #define readstr secstore_readstr 107 static void writerr(SConn*, char*); 108 static int readstr(SConn*, char*); /* call with buf of size Maxmsg+1 */ 109 /* returns -1 upon error, with error message in buf */ 110 111 typedef struct ConnState { 112 uchar secret[SHA1dlen]; 113 ulong seqno; 114 RC4state rc4; 115 } ConnState; 116 117 #undef SS 118 typedef struct SS { 119 int fd; /* file descriptor for read/write of encrypted data */ 120 int alg; /* if nonzero, "alg sha rc4_128" */ 121 ConnState in, out; 122 } SS; 123 124 static int 125 SC_secret(SConn *conn, uchar *sigma, int direction) 126 { 127 SS *ss = (SS*)(conn->chan); 128 int nsigma = conn->secretlen; 129 130 if(direction != 0){ 131 hmac_sha1(sigma, nsigma, (uchar*)"one", 3, ss->out.secret, nil); 132 hmac_sha1(sigma, nsigma, (uchar*)"two", 3, ss->in.secret, nil); 133 }else{ 134 hmac_sha1(sigma, nsigma, (uchar*)"two", 3, ss->out.secret, nil); 135 hmac_sha1(sigma, nsigma, (uchar*)"one", 3, ss->in.secret, nil); 136 } 137 setupRC4state(&ss->in.rc4, ss->in.secret, 16); /* restrict to 128 bits */ 138 setupRC4state(&ss->out.rc4, ss->out.secret, 16); 139 ss->alg = 1; 140 return 0; 141 } 142 143 static void 144 hash(uchar secret[SHA1dlen], uchar *data, int len, int seqno, uchar d[SHA1dlen]) 145 { 146 DigestState sha; 147 uchar seq[4]; 148 149 seq[0] = seqno>>24; 150 seq[1] = seqno>>16; 151 seq[2] = seqno>>8; 152 seq[3] = seqno; 153 memset(&sha, 0, sizeof sha); 154 sha1(secret, SHA1dlen, nil, &sha); 155 sha1(data, len, nil, &sha); 156 sha1(seq, 4, d, &sha); 157 } 158 159 static int 160 verify(uchar secret[SHA1dlen], uchar *data, int len, int seqno, uchar d[SHA1dlen]) 161 { 162 DigestState sha; 163 uchar seq[4]; 164 uchar digest[SHA1dlen]; 165 166 seq[0] = seqno>>24; 167 seq[1] = seqno>>16; 168 seq[2] = seqno>>8; 169 seq[3] = seqno; 170 memset(&sha, 0, sizeof sha); 171 sha1(secret, SHA1dlen, nil, &sha); 172 sha1(data, len, nil, &sha); 173 sha1(seq, 4, digest, &sha); 174 return memcmp(d, digest, SHA1dlen); 175 } 176 177 static int 178 SC_read(SConn *conn, uchar *buf, int n) 179 { 180 SS *ss = (SS*)(conn->chan); 181 uchar count[2], digest[SHA1dlen]; 182 int len, nr; 183 184 if(read(ss->fd, count, 2) != 2 || (count[0]&0x80) == 0){ 185 werrstr("!SC_read invalid count"); 186 return -1; 187 } 188 len = (count[0]&0x7f)<<8 | count[1]; /* SSL-style count; no pad */ 189 if(ss->alg){ 190 len -= SHA1dlen; 191 if(len <= 0 || readn(ss->fd, digest, SHA1dlen) != SHA1dlen){ 192 werrstr("!SC_read missing sha1"); 193 return -1; 194 } 195 if(len > n || readn(ss->fd, buf, len) != len){ 196 werrstr("!SC_read missing data"); 197 return -1; 198 } 199 rc4(&ss->in.rc4, digest, SHA1dlen); 200 rc4(&ss->in.rc4, buf, len); 201 if(verify(ss->in.secret, buf, len, ss->in.seqno, digest) != 0){ 202 werrstr("!SC_read integrity check failed"); 203 return -1; 204 } 205 }else{ 206 if(len <= 0 || len > n){ 207 werrstr("!SC_read implausible record length"); 208 return -1; 209 } 210 if( (nr = readn(ss->fd, buf, len)) != len){ 211 werrstr("!SC_read expected %d bytes, but got %d", len, nr); 212 return -1; 213 } 214 } 215 ss->in.seqno++; 216 return len; 217 } 218 219 static int 220 SC_write(SConn *conn, uchar *buf, int n) 221 { 222 SS *ss = (SS*)(conn->chan); 223 uchar count[2], digest[SHA1dlen], enc[Maxmsg+1]; 224 int len; 225 226 if(n <= 0 || n > Maxmsg+1){ 227 werrstr("!SC_write invalid n %d", n); 228 return -1; 229 } 230 len = n; 231 if(ss->alg) 232 len += SHA1dlen; 233 count[0] = 0x80 | len>>8; 234 count[1] = len; 235 if(write(ss->fd, count, 2) != 2){ 236 werrstr("!SC_write invalid count"); 237 return -1; 238 } 239 if(ss->alg){ 240 hash(ss->out.secret, buf, n, ss->out.seqno, digest); 241 rc4(&ss->out.rc4, digest, SHA1dlen); 242 memcpy(enc, buf, n); 243 rc4(&ss->out.rc4, enc, n); 244 if(write(ss->fd, digest, SHA1dlen) != SHA1dlen || 245 write(ss->fd, enc, n) != n){ 246 werrstr("!SC_write error on send"); 247 return -1; 248 } 249 }else{ 250 if(write(ss->fd, buf, n) != n){ 251 werrstr("!SC_write error on send"); 252 return -1; 253 } 254 } 255 ss->out.seqno++; 256 return n; 257 } 258 259 static void 260 SC_free(SConn *conn) 261 { 262 SS *ss = (SS*)(conn->chan); 263 264 close(ss->fd); 265 free(ss); 266 free(conn); 267 } 268 269 SConn* 270 newSConn(int fd) 271 { 272 SS *ss; 273 SConn *conn; 274 275 if(fd < 0) 276 return nil; 277 ss = (SS*)emalloc(sizeof(*ss)); 278 conn = (SConn*)emalloc(sizeof(*conn)); 279 ss->fd = fd; 280 ss->alg = 0; 281 conn->chan = (void*)ss; 282 conn->secretlen = SHA1dlen; 283 conn->free = SC_free; 284 conn->secret = SC_secret; 285 conn->read = SC_read; 286 conn->write = SC_write; 287 return conn; 288 } 289 290 static void 291 writerr(SConn *conn, char *s) 292 { 293 char buf[Maxmsg]; 294 295 snprint(buf, Maxmsg, "!%s", s); 296 conn->write(conn, (uchar*)buf, strlen(buf)); 297 } 298 299 static int 300 readstr(SConn *conn, char *s) 301 { 302 int n; 303 304 n = conn->read(conn, (uchar*)s, Maxmsg); 305 if(n >= 0){ 306 s[n] = 0; 307 if(s[0] == '!'){ 308 memmove(s, s+1, n); 309 n = -1; 310 } 311 }else{ 312 strcpy(s, "read error"); 313 } 314 return n; 315 } 316 317 static int 318 getfile(SConn *conn, uchar *key, int nkey) 319 { 320 char *buf; 321 int nbuf, n, nr, len; 322 char s[Maxmsg+1], *gf, *p, *q; 323 uchar skey[SHA1dlen], ib[Maxmsg+CHK], *ibr, *ibw; 324 AESstate aes; 325 DigestState *sha; 326 327 gf = "factotum"; 328 memset(&aes, 0, sizeof aes); 329 330 snprint(s, Maxmsg, "GET %s\n", gf); 331 conn->write(conn, (uchar*)s, strlen(s)); 332 333 /* get file size */ 334 s[0] = '\0'; 335 if(readstr(conn, s) < 0){ 336 werrstr("secstore: %r"); 337 return -1; 338 } 339 if((len = atoi(s)) < 0){ 340 werrstr("secstore: remote file %s does not exist", gf); 341 return -1; 342 }else if(len > MAXFILESIZE){/*assert */ 343 werrstr("secstore: implausible file size %d for %s", len, gf); 344 return -1; 345 } 346 347 ibr = ibw = ib; 348 buf = nil; 349 nbuf = 0; 350 for(nr=0; nr < len;){ 351 if((n = conn->read(conn, ibw, Maxmsg)) <= 0){ 352 werrstr("secstore: empty file chunk n=%d nr=%d len=%d: %r", n, nr, len); 353 return -1; 354 } 355 nr += n; 356 ibw += n; 357 if(!aes.setup){ /* first time, read 16 byte IV */ 358 if(n < 16){ 359 werrstr("secstore: no IV in file"); 360 return -1; 361 } 362 sha = sha1((uchar*)"aescbc file", 11, nil, nil); 363 sha1(key, nkey, skey, sha); 364 setupAESstate(&aes, skey, AESbsize, ibr); 365 memset(skey, 0, sizeof skey); 366 ibr += AESbsize; 367 n -= AESbsize; 368 } 369 aesCBCdecrypt(ibw-n, n, &aes); 370 n = ibw-ibr-CHK; 371 if(n > 0){ 372 buf = erealloc(buf, nbuf+n+1); 373 memmove(buf+nbuf, ibr, n); 374 nbuf += n; 375 ibr += n; 376 } 377 memmove(ib, ibr, ibw-ibr); 378 ibw = ib + (ibw-ibr); 379 ibr = ib; 380 } 381 n = ibw-ibr; 382 if((n != CHK) || (memcmp(ib, "XXXXXXXXXXXXXXXX", CHK) != 0)){ 383 werrstr("secstore: decrypted file failed to authenticate!"); 384 free(buf); 385 return -1; 386 } 387 if(nbuf == 0){ 388 werrstr("secstore got empty file"); 389 return -1; 390 } 391 buf[nbuf] = '\0'; 392 p = buf; 393 n = 0; 394 while(p){ 395 if(q = strchr(p, '\n')) 396 *q++ = '\0'; 397 n++; 398 if(ctlwrite(p) < 0){ 399 flog("secstore %s:%d: %r", gf, n); 400 fprint(2, "secstore(%s) line %d: %r\n", gf, n); 401 } 402 p = q; 403 } 404 free(buf); 405 return 0; 406 } 407 408 static char VERSION[] = "secstore"; 409 410 typedef struct PAKparams{ 411 mpint *q, *p, *r, *g; 412 } PAKparams; 413 414 static PAKparams *pak; 415 416 /* This group was generated by the seed EB7B6E35F7CD37B511D96C67D6688CC4DD440E1E. */ 417 static void 418 initPAKparams(void) 419 { 420 if(pak) 421 return; 422 pak = (PAKparams*)emalloc(sizeof(*pak)); 423 pak->q = strtomp("E0F0EF284E10796C5A2A511E94748BA03C795C13", nil, 16, nil); 424 pak->p = strtomp("C41CFBE4D4846F67A3DF7DE9921A49D3B42DC33728427AB159CEC8CBBD" 425 "B12B5F0C244F1A734AEB9840804EA3C25036AD1B61AFF3ABBC247CD4B384224567A86" 426 "3A6F020E7EE9795554BCD08ABAD7321AF27E1E92E3DB1C6E7E94FAAE590AE9C48F96D9" 427 "3D178E809401ABE8A534A1EC44359733475A36A70C7B425125062B1142D", nil, 16, nil); 428 pak->r = strtomp("DF310F4E54A5FEC5D86D3E14863921E834113E060F90052AD332B3241CEF" 429 "2497EFA0303D6344F7C819691A0F9C4A773815AF8EAECFB7EC1D98F039F17A32A7E887" 430 "D97251A927D093F44A55577F4D70444AEBD06B9B45695EC23962B175F266895C67D21" 431 "C4656848614D888A4", nil, 16, nil); 432 pak->g = strtomp("2F1C308DC46B9A44B52DF7DACCE1208CCEF72F69C743ADD4D2327173444" 433 "ED6E65E074694246E07F9FD4AE26E0FDDD9F54F813C40CB9BCD4338EA6F242AB94CD41" 434 "0E676C290368A16B1A3594877437E516C53A6EEE5493A038A017E955E218E7819734E3E" 435 "2A6E0BAE08B14258F8C03CC1B30E0DDADFCF7CEDF0727684D3D255F1", nil, 16, nil); 436 } 437 438 /* H = (sha(ver,C,sha(passphrase)))^r mod p, */ 439 /* a hash function expensive to attack by brute force. */ 440 static void 441 longhash(char *ver, char *C, uchar *passwd, mpint *H) 442 { 443 uchar *Cp; 444 int i, n, nver, nC; 445 uchar buf[140], key[1]; 446 447 nver = strlen(ver); 448 nC = strlen(C); 449 n = nver + nC + SHA1dlen; 450 Cp = (uchar*)emalloc(n); 451 memmove(Cp, ver, nver); 452 memmove(Cp+nver, C, nC); 453 memmove(Cp+nver+nC, passwd, SHA1dlen); 454 for(i = 0; i < 7; i++){ 455 key[0] = 'A'+i; 456 hmac_sha1(Cp, n, key, sizeof key, buf+i*SHA1dlen, nil); 457 } 458 memset(Cp, 0, n); 459 free(Cp); 460 betomp(buf, sizeof buf, H); 461 mpmod(H, pak->p, H); 462 mpexp(H, pak->r, pak->p, H); 463 } 464 465 /* Hi = H^-1 mod p */ 466 static char * 467 PAK_Hi(char *C, char *passphrase, mpint *H, mpint *Hi) 468 { 469 uchar passhash[SHA1dlen]; 470 471 sha1((uchar *)passphrase, strlen(passphrase), passhash, nil); 472 initPAKparams(); 473 longhash(VERSION, C, passhash, H); 474 mpinvert(H, pak->p, Hi); 475 return mptoa(Hi, 64, nil, 0); 476 } 477 478 /* another, faster, hash function for each party to */ 479 /* confirm that the other has the right secrets. */ 480 static void 481 shorthash(char *mess, char *C, char *S, char *m, char *mu, char *sigma, char *Hi, uchar *digest) 482 { 483 SHA1state *state; 484 485 state = sha1((uchar*)mess, strlen(mess), 0, 0); 486 state = sha1((uchar*)C, strlen(C), 0, state); 487 state = sha1((uchar*)S, strlen(S), 0, state); 488 state = sha1((uchar*)m, strlen(m), 0, state); 489 state = sha1((uchar*)mu, strlen(mu), 0, state); 490 state = sha1((uchar*)sigma, strlen(sigma), 0, state); 491 state = sha1((uchar*)Hi, strlen(Hi), 0, state); 492 state = sha1((uchar*)mess, strlen(mess), 0, state); 493 state = sha1((uchar*)C, strlen(C), 0, state); 494 state = sha1((uchar*)S, strlen(S), 0, state); 495 state = sha1((uchar*)m, strlen(m), 0, state); 496 state = sha1((uchar*)mu, strlen(mu), 0, state); 497 state = sha1((uchar*)sigma, strlen(sigma), 0, state); 498 sha1((uchar*)Hi, strlen(Hi), digest, state); 499 } 500 501 /* On input, conn provides an open channel to the server; */ 502 /* C is the name this client calls itself; */ 503 /* pass is the user's passphrase */ 504 /* On output, session secret has been set in conn */ 505 /* (unless return code is negative, which means failure). */ 506 /* If pS is not nil, it is set to the (alloc'd) name the server calls itself. */ 507 static int 508 PAKclient(SConn *conn, char *C, char *pass, char **pS) 509 { 510 char *mess, *mess2, *eol, *S, *hexmu, *ks, *hexm, *hexsigma = nil, *hexHi; 511 char kc[2*SHA1dlen+1]; 512 uchar digest[SHA1dlen]; 513 int rc = -1, n; 514 mpint *x, *m = mpnew(0), *mu = mpnew(0), *sigma = mpnew(0); 515 mpint *H = mpnew(0), *Hi = mpnew(0); 516 517 hexHi = PAK_Hi(C, pass, H, Hi); 518 519 /* random 1<=x<=q-1; send C, m=g**x H */ 520 x = mprand(164, genrandom, nil); 521 mpmod(x, pak->q, x); 522 if(mpcmp(x, mpzero) == 0) 523 mpassign(mpone, x); 524 mpexp(pak->g, x, pak->p, m); 525 mpmul(m, H, m); 526 mpmod(m, pak->p, m); 527 hexm = mptoa(m, 64, nil, 0); 528 mess = (char*)emalloc(2*Maxmsg+2); 529 mess2 = mess+Maxmsg+1; 530 snprint(mess, Maxmsg, "%s\tPAK\nC=%s\nm=%s\n", VERSION, C, hexm); 531 conn->write(conn, (uchar*)mess, strlen(mess)); 532 533 /* recv g**y, S, check hash1(g**xy) */ 534 if(readstr(conn, mess) < 0){ 535 fprint(2, "error: %s\n", mess); 536 writerr(conn, "couldn't read g**y"); 537 goto done; 538 } 539 eol = strchr(mess, '\n'); 540 if(strncmp("mu=", mess, 3) != 0 || !eol || strncmp("\nk=", eol, 3) != 0){ 541 writerr(conn, "verifier syntax error"); 542 goto done; 543 } 544 hexmu = mess+3; 545 *eol = 0; 546 ks = eol+3; 547 eol = strchr(ks, '\n'); 548 if(!eol || strncmp("\nS=", eol, 3) != 0){ 549 writerr(conn, "verifier syntax error for secstore 1.0"); 550 goto done; 551 } 552 *eol = 0; 553 S = eol+3; 554 eol = strchr(S, '\n'); 555 if(!eol){ 556 writerr(conn, "verifier syntax error for secstore 1.0"); 557 goto done; 558 } 559 *eol = 0; 560 if(pS) 561 *pS = estrdup(S); 562 strtomp(hexmu, nil, 64, mu); 563 mpexp(mu, x, pak->p, sigma); 564 hexsigma = mptoa(sigma, 64, nil, 0); 565 shorthash("server", C, S, hexm, hexmu, hexsigma, hexHi, digest); 566 enc64(kc, sizeof kc, digest, SHA1dlen); 567 if(strcmp(ks, kc) != 0){ 568 writerr(conn, "verifier didn't match"); 569 goto done; 570 } 571 572 /* send hash2(g**xy) */ 573 shorthash("client", C, S, hexm, hexmu, hexsigma, hexHi, digest); 574 enc64(kc, sizeof kc, digest, SHA1dlen); 575 snprint(mess2, Maxmsg, "k'=%s\n", kc); 576 conn->write(conn, (uchar*)mess2, strlen(mess2)); 577 578 /* set session key */ 579 shorthash("session", C, S, hexm, hexmu, hexsigma, hexHi, digest); 580 memset(hexsigma, 0, strlen(hexsigma)); 581 n = conn->secret(conn, digest, 0); 582 memset(digest, 0, SHA1dlen); 583 if(n < 0){/*assert */ 584 writerr(conn, "can't set secret"); 585 goto done; 586 } 587 588 rc = 0; 589 done: 590 mpfree(x); 591 mpfree(sigma); 592 mpfree(mu); 593 mpfree(m); 594 mpfree(Hi); 595 mpfree(H); 596 free(hexsigma); 597 free(hexHi); 598 free(hexm); 599 free(mess); 600 return rc; 601 } 602 603 int 604 secstorefetch(void) 605 { 606 int rv = -1, fd; 607 char s[Maxmsg+1]; 608 SConn *conn; 609 char *pass, *sta; 610 611 sta = nil; 612 conn = nil; 613 pass = readcons("secstore password", nil, 1); 614 if(pass==nil || strlen(pass)==0){ 615 werrstr("cancel"); 616 goto Out; 617 } 618 if((fd = secdial()) < 0) 619 goto Out; 620 if((conn = newSConn(fd)) == nil) 621 goto Out; 622 if(PAKclient(conn, owner, pass, nil) < 0){ 623 werrstr("password mistyped?"); 624 goto Out; 625 } 626 if(readstr(conn, s) < 0) 627 goto Out; 628 if(strcmp(s, "STA") == 0){ 629 sta = readcons("STA PIN+SecureID", nil, 1); 630 if(sta==nil || strlen(sta)==0){ 631 werrstr("cancel"); 632 goto Out; 633 } 634 if(strlen(sta) >= sizeof s - 3){ 635 werrstr("STA response too long"); 636 goto Out; 637 } 638 strcpy(s+3, sta); 639 conn->write(conn, (uchar*)s, strlen(s)); 640 readstr(conn, s); 641 } 642 if(strcmp(s, "OK") !=0){ 643 werrstr("%s", s); 644 goto Out; 645 } 646 if(getfile(conn, (uchar*)pass, strlen(pass)) < 0) 647 goto Out; 648 conn->write(conn, (uchar*)"BYE", 3); 649 rv = 0; 650 651 Out: 652 if(rv < 0) 653 flog("secstorefetch: %r"); 654 if(conn) 655 conn->free(conn); 656 if(pass) 657 free(pass); 658 if(sta) 659 free(sta); 660 return rv; 661 }