secstore.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578
  1. /* network login client */
  2. #include <u.h>
  3. #include <libc.h>
  4. #include <mp.h>
  5. #include <libsec.h>
  6. #include <authsrv.h>
  7. #include "SConn.h"
  8. #include "secstore.h"
  9. enum{ CHK = 16, MAXFILES = 100 };
  10. typedef struct AuthConn{
  11. SConn *conn;
  12. char pass[64];
  13. int passlen;
  14. } AuthConn;
  15. int verbose;
  16. Nvrsafe nvr;
  17. void
  18. usage(void)
  19. {
  20. fprint(2, "usage: secstore [-cin] [-g getfile] [-p putfile] [-r rmfile] [-s tcp!server!5356] [-u user] [-v]\n");
  21. exits("usage");
  22. }
  23. static int
  24. getfile(SConn *conn, char *gf, uchar **buf, ulong *buflen, uchar *key, int nkey)
  25. {
  26. int fd = -1;
  27. int i, n, nr, nw, len;
  28. char s[Maxmsg+1];
  29. uchar skey[SHA1dlen], ib[Maxmsg+CHK], *ibr, *ibw, *bufw, *bufe;
  30. AESstate aes;
  31. DigestState *sha;
  32. if(strchr(gf, '/')){
  33. fprint(2, "simple filenames, not paths like %s\n", gf);
  34. return -1;
  35. }
  36. memset(&aes, 0, sizeof aes);
  37. snprint(s, Maxmsg, "GET %s\n", gf);
  38. conn->write(conn, (uchar*)s, strlen(s));
  39. /* get file size */
  40. s[0] = '\0';
  41. bufw = bufe = nil;
  42. if(readstr(conn, s) < 0){
  43. fprint(2, "remote: %s\n", s);
  44. return -1;
  45. }
  46. len = atoi(s);
  47. if(len == -1){
  48. fprint(2, "remote file %s does not exist\n", gf);
  49. return -1;
  50. }else if(len == -3){
  51. fprint(2, "implausible filesize for %s\n", gf);
  52. return -1;
  53. }else if(len < 0){
  54. fprint(2, "GET refused for %s\n", gf);
  55. return -1;
  56. }
  57. if(buf != nil){
  58. *buflen = len - AESbsize - CHK;
  59. *buf = bufw = emalloc(len);
  60. bufe = bufw + len;
  61. }
  62. /* directory listing */
  63. if(strcmp(gf,".")==0){
  64. if(buf != nil)
  65. *buflen = len;
  66. for(i=0; i < len; i += n){
  67. if((n = conn->read(conn, (uchar*)s, Maxmsg)) <= 0){
  68. fprint(2, "empty file chunk\n");
  69. return -1;
  70. }
  71. if(buf == nil)
  72. write(1, s, n);
  73. else
  74. memmove((*buf)+i, s, n);
  75. }
  76. return 0;
  77. }
  78. /* conn is already encrypted against wiretappers,
  79. but gf is also encrypted against server breakin. */
  80. if(buf == nil && (fd =create(gf, OWRITE, 0600)) < 0){
  81. fprint(2, "can't open %s: %r\n", gf);
  82. return -1;
  83. }
  84. ibr = ibw = ib;
  85. for(nr=0; nr < len;){
  86. if((n = conn->read(conn, ibw, Maxmsg)) <= 0){
  87. fprint(2, "empty file chunk n=%d nr=%d len=%d: %r\n", n, nr, len);
  88. return -1;
  89. }
  90. nr += n;
  91. ibw += n;
  92. if(!aes.setup){ /* first time, read 16 byte IV */
  93. if(n < AESbsize){
  94. fprint(2, "no IV in file\n");
  95. return -1;
  96. }
  97. sha = sha1((uchar*)"aescbc file", 11, nil, nil);
  98. sha1(key, nkey, skey, sha);
  99. setupAESstate(&aes, skey, AESbsize, ibr);
  100. memset(skey, 0, sizeof skey);
  101. ibr += AESbsize;
  102. n -= AESbsize;
  103. }
  104. aesCBCdecrypt(ibw-n, n, &aes);
  105. n = ibw-ibr-CHK;
  106. if(n > 0){
  107. if(buf == nil){
  108. nw = write(fd, ibr, n);
  109. if(nw != n){
  110. fprint(2, "write error on %s", gf);
  111. return -1;
  112. }
  113. }else{
  114. assert(bufw+n <= bufe);
  115. memmove(bufw, ibr, n);
  116. bufw += n;
  117. }
  118. ibr += n;
  119. }
  120. memmove(ib, ibr, ibw-ibr);
  121. ibw = ib + (ibw-ibr);
  122. ibr = ib;
  123. }
  124. if(buf == nil)
  125. close(fd);
  126. n = ibw-ibr;
  127. if((n != CHK) || (memcmp(ib, "XXXXXXXXXXXXXXXX", CHK) != 0)){
  128. fprint(2,"decrypted file failed to authenticate!\n");
  129. return -1;
  130. }
  131. return 0;
  132. }
  133. // This sends a file to the secstore disk that can, in an emergency, be
  134. // decrypted by the program aescbc.c.
  135. static int
  136. putfile(SConn *conn, char *pf, uchar *buf, ulong len, uchar *key, int nkey)
  137. {
  138. int i, n, fd, ivo, bufi, done;
  139. char s[Maxmsg];
  140. uchar skey[SHA1dlen], b[CHK+Maxmsg], IV[AESbsize];
  141. AESstate aes;
  142. DigestState *sha;
  143. /* create initialization vector */
  144. srand(time(0)); /* doesn't need to be unpredictable */
  145. for(i=0; i<AESbsize; i++)
  146. IV[i] = 0xff & rand();
  147. sha = sha1((uchar*)"aescbc file", 11, nil, nil);
  148. sha1(key, nkey, skey, sha);
  149. setupAESstate(&aes, skey, AESbsize, IV);
  150. memset(skey, 0, sizeof skey);
  151. snprint(s, Maxmsg, "PUT %s\n", pf);
  152. conn->write(conn, (uchar*)s, strlen(s));
  153. if(buf == nil){
  154. /* get file size */
  155. if((fd = open(pf, OREAD)) < 0){
  156. fprint(2, "can't open %s: %r\n", pf);
  157. return -1;
  158. }
  159. len = seek(fd, 0, 2);
  160. seek(fd, 0, 0);
  161. } else {
  162. fd = -1;
  163. }
  164. if(len > MAXFILESIZE){
  165. fprint(2, "implausible filesize %ld for %s\n", len, pf);
  166. return -1;
  167. }
  168. /* send file size */
  169. snprint(s, Maxmsg, "%ld", len+AESbsize+CHK);
  170. conn->write(conn, (uchar*)s, strlen(s));
  171. /* send IV and file+XXXXX in Maxmsg chunks */
  172. ivo = AESbsize;
  173. bufi = 0;
  174. memcpy(b, IV, ivo);
  175. for(done = 0; !done; ){
  176. if(buf == nil){
  177. n = read(fd, b+ivo, Maxmsg-ivo);
  178. if(n < 0){
  179. fprint(2, "read error on %s: %r\n", pf);
  180. return -1;
  181. }
  182. }else{
  183. if((n = len - bufi) > Maxmsg-ivo)
  184. n = Maxmsg-ivo;
  185. memcpy(b+ivo, buf+bufi, n);
  186. bufi += n;
  187. }
  188. n += ivo;
  189. ivo = 0;
  190. if(n < Maxmsg){ /* EOF on input; append XX... */
  191. memset(b+n, 'X', CHK);
  192. n += CHK; // might push n>Maxmsg
  193. done = 1;
  194. }
  195. aesCBCencrypt(b, n, &aes);
  196. if(n > Maxmsg){
  197. assert(done==1);
  198. conn->write(conn, b, Maxmsg);
  199. n -= Maxmsg;
  200. memmove(b, b+Maxmsg, n);
  201. }
  202. conn->write(conn, b, n);
  203. }
  204. if(buf == nil)
  205. close(fd);
  206. fprint(2, "saved %ld bytes\n", len);
  207. return 0;
  208. }
  209. static int
  210. removefile(SConn *conn, char *rf)
  211. {
  212. char buf[Maxmsg];
  213. if(strchr(rf, '/')){
  214. fprint(2, "simple filenames, not paths like %s\n", rf);
  215. return -1;
  216. }
  217. snprint(buf, Maxmsg, "RM %s\n", rf);
  218. conn->write(conn, (uchar*)buf, strlen(buf));
  219. return 0;
  220. }
  221. static int
  222. cmd(AuthConn *c, char **gf, int *Gflag, char **pf, char **rf)
  223. {
  224. ulong len;
  225. int rv = -1;
  226. uchar *memfile, *memcur, *memnext;
  227. while(*gf != nil){
  228. if(verbose)
  229. fprint(2, "get %s\n", *gf);
  230. if(getfile(c->conn, *gf, *Gflag ? &memfile : nil, &len, (uchar*)c->pass, c->passlen) < 0)
  231. goto Out;
  232. if(*Gflag){
  233. // write one line at a time, as required by /mnt/factotum/ctl
  234. memcur = memfile;
  235. while(len>0){
  236. memnext = (uchar*)strchr((char*)memcur, '\n');
  237. if(memnext){
  238. write(1, memcur, memnext-memcur+1);
  239. len -= memnext-memcur+1;
  240. memcur = memnext+1;
  241. }else{
  242. write(1, memcur, len);
  243. break;
  244. }
  245. }
  246. free(memfile);
  247. }
  248. gf++;
  249. Gflag++;
  250. }
  251. while(*pf != nil){
  252. if(verbose)
  253. fprint(2, "put %s\n", *pf);
  254. if(putfile(c->conn, *pf, nil, 0, (uchar*)c->pass, c->passlen) < 0)
  255. goto Out;
  256. pf++;
  257. }
  258. while(*rf != nil){
  259. if(verbose)
  260. fprint(2, "rm %s\n", *rf);
  261. if(removefile(c->conn, *rf) < 0)
  262. goto Out;
  263. rf++;
  264. }
  265. c->conn->write(c->conn, (uchar*)"BYE", 3);
  266. rv = 0;
  267. Out:
  268. c->conn->free(c->conn);
  269. return rv;
  270. }
  271. static int
  272. chpasswd(AuthConn *c, char *id)
  273. {
  274. ulong len;
  275. int rv = -1, newpasslen = 0;
  276. mpint *H, *Hi;
  277. uchar *memfile;
  278. char *newpass, *passck;
  279. char *list, *cur, *next, *hexHi;
  280. char *f[8], prompt[128];
  281. H = mpnew(0);
  282. Hi = mpnew(0);
  283. // changing our password is vulnerable to connection failure
  284. for(;;){
  285. snprint(prompt, sizeof(prompt), "new password for %s: ", id);
  286. newpass = getpassm(prompt);
  287. if(newpass == nil)
  288. goto Out;
  289. if(strlen(newpass) >= 7)
  290. break;
  291. else if(strlen(newpass) == 0){
  292. fprint(2, "!password change aborted\n");
  293. goto Out;
  294. }
  295. print("!password must be at least 7 characters\n");
  296. }
  297. newpasslen = strlen(newpass);
  298. snprint(prompt, sizeof(prompt), "retype password: ");
  299. passck = getpassm(prompt);
  300. if(passck == nil){
  301. fprint(2, "getpassm failed\n");
  302. goto Out;
  303. }
  304. if(strcmp(passck, newpass) != 0){
  305. fprint(2, "passwords didn't match\n");
  306. goto Out;
  307. }
  308. c->conn->write(c->conn, (uchar*)"CHPASS", strlen("CHPASS"));
  309. hexHi = PAK_Hi(id, newpass, H, Hi);
  310. c->conn->write(c->conn, (uchar*)hexHi, strlen(hexHi));
  311. free(hexHi);
  312. mpfree(H);
  313. mpfree(Hi);
  314. if(getfile(c->conn, ".", (uchar **) &list, &len, nil, 0) < 0){
  315. fprint(2, "directory listing failed.\n");
  316. goto Out;
  317. }
  318. /* Loop over files and reencrypt them; try to keep going after error */
  319. for(cur=list; (next=strchr(cur, '\n')) != nil; cur=next+1){
  320. *next = '\0';
  321. if(tokenize(cur, f, nelem(f))< 1)
  322. break;
  323. fprint(2, "reencrypting '%s'\n", f[0]);
  324. if(getfile(c->conn, f[0], &memfile, &len, (uchar*)c->pass, c->passlen) < 0){
  325. fprint(2, "getfile of '%s' failed\n", f[0]);
  326. continue;
  327. }
  328. if(putfile(c->conn, f[0], memfile, len, (uchar*)newpass, newpasslen) < 0)
  329. fprint(2, "putfile of '%s' failed\n", f[0]);
  330. free(memfile);
  331. }
  332. free(list);
  333. c->conn->write(c->conn, (uchar*)"BYE", 3);
  334. rv = 0;
  335. Out:
  336. if(newpass != nil){
  337. memset(newpass, 0, newpasslen);
  338. free(newpass);
  339. }
  340. c->conn->free(c->conn);
  341. return rv;
  342. }
  343. static AuthConn*
  344. login(char *id, char *dest, int pass_stdin, int pass_nvram)
  345. {
  346. AuthConn *c;
  347. int fd, n, ntry = 0;
  348. char *S, *PINSTA = nil, *nl, s[Maxmsg+1], *pass;
  349. if(dest == nil){
  350. fprint(2, "tried to login with nil dest\n");
  351. exits("nil dest");
  352. }
  353. c = emalloc(sizeof(*c));
  354. if(pass_nvram){
  355. if(readnvram(&nvr, 0) < 0)
  356. exits("readnvram: %r");
  357. strecpy(c->pass, c->pass+sizeof c->pass, nvr.config);
  358. }
  359. if(pass_stdin){
  360. n = readn(0, s, Maxmsg-2); // so len(PINSTA)<Maxmsg-3
  361. if(n < 1)
  362. exits("no password on standard input");
  363. s[n] = 0;
  364. nl = strchr(s, '\n');
  365. if(nl){
  366. *nl++ = 0;
  367. PINSTA = estrdup(nl);
  368. nl = strchr(PINSTA, '\n');
  369. if(nl)
  370. *nl = 0;
  371. }
  372. strecpy(c->pass, c->pass+sizeof c->pass, s);
  373. }
  374. while(1){
  375. if(verbose)
  376. fprint(2, "dialing %s\n", dest);
  377. if((fd = dial(dest, nil, nil, nil)) < 0){
  378. fprint(2, "can't dial %s\n", dest);
  379. free(c);
  380. return nil;
  381. }
  382. if((c->conn = newSConn(fd)) == nil){
  383. free(c);
  384. return nil;
  385. }
  386. ntry++;
  387. if(!pass_stdin && !pass_nvram){
  388. pass = getpassm("secstore password: ");
  389. if(strlen(pass) >= sizeof c->pass){
  390. fprint(2, "password too long, skipping secstore login\n");
  391. exits("password too long");
  392. }
  393. strcpy(c->pass, pass);
  394. memset(pass, 0, strlen(pass));
  395. free(pass);
  396. }
  397. if(c->pass[0]==0){
  398. fprint(2, "null password, skipping secstore login\n");
  399. exits("no password");
  400. }
  401. if(PAKclient(c->conn, id, c->pass, &S) >= 0)
  402. break;
  403. c->conn->free(c->conn);
  404. if(pass_stdin)
  405. exits("invalid password on standard input");
  406. if(pass_nvram)
  407. exits("invalid password in nvram");
  408. // and let user try retyping the password
  409. if(ntry==3)
  410. fprint(2, "Enter an empty password to quit.\n");
  411. }
  412. c->passlen = strlen(c->pass);
  413. fprint(2, "%s\n", S);
  414. free(S);
  415. if(readstr(c->conn, s) < 0){
  416. c->conn->free(c->conn);
  417. free(c);
  418. return nil;
  419. }
  420. if(strcmp(s, "STA") == 0){
  421. long sn;
  422. if(pass_stdin){
  423. if(PINSTA)
  424. strncpy(s+3, PINSTA, (sizeof s)-3);
  425. else
  426. exits("missing PIN+SecureID on standard input");
  427. free(PINSTA);
  428. }else{
  429. pass = getpassm("STA PIN+SecureID: ");
  430. strncpy(s+3, pass, (sizeof s)-4);
  431. memset(pass, 0, strlen(pass));
  432. free(pass);
  433. }
  434. sn = strlen(s+3);
  435. if(verbose)
  436. fprint(2, "%ld\n", sn);
  437. c->conn->write(c->conn, (uchar*)s, sn+3);
  438. readstr(c->conn, s);
  439. }
  440. if(strcmp(s, "OK") != 0){
  441. fprint(2, "%s\n", s);
  442. c->conn->free(c->conn);
  443. free(c);
  444. return nil;
  445. }
  446. return c;
  447. }
  448. int
  449. main(int argc, char **argv)
  450. {
  451. int chpass = 0, pass_stdin = 0, pass_nvram = 0, rc;
  452. int ngfile = 0, npfile = 0, nrfile = 0, Gflag[MAXFILES+1];
  453. char *gfile[MAXFILES], *pfile[MAXFILES], *rfile[MAXFILES];
  454. char *serve, *tcpserve, *user;
  455. AuthConn *c;
  456. serve = "$auth";
  457. user = getenv("user");
  458. memset(Gflag, 0, sizeof Gflag);
  459. ARGBEGIN{
  460. case 'c':
  461. chpass = 1;
  462. break;
  463. case 'G':
  464. Gflag[ngfile]++;
  465. /* fall through */
  466. case 'g':
  467. if(ngfile >= MAXFILES)
  468. exits("too many gfiles");
  469. gfile[ngfile++] = ARGF();
  470. if(gfile[ngfile-1] == nil)
  471. usage();
  472. break;
  473. case 'i':
  474. pass_stdin = 1;
  475. break;
  476. case 'n':
  477. pass_nvram = 1;
  478. break;
  479. case 'p':
  480. if(npfile >= MAXFILES)
  481. exits("too many pfiles");
  482. pfile[npfile++] = ARGF();
  483. if(pfile[npfile-1] == nil)
  484. usage();
  485. break;
  486. case 'r':
  487. if(nrfile >= MAXFILES)
  488. exits("too many rfiles");
  489. rfile[nrfile++] = ARGF();
  490. if(rfile[nrfile-1] == nil)
  491. usage();
  492. break;
  493. case 's':
  494. serve = EARGF(usage());
  495. break;
  496. case 'u':
  497. user = EARGF(usage());
  498. break;
  499. case 'v':
  500. verbose++;
  501. break;
  502. default:
  503. usage();
  504. break;
  505. }ARGEND;
  506. gfile[ngfile] = nil;
  507. pfile[npfile] = nil;
  508. rfile[nrfile] = nil;
  509. if(argc!=0 || user==nil)
  510. usage();
  511. if(chpass && (ngfile || npfile || nrfile)){
  512. fprint(2, "Get, put, and remove invalid with password change.\n");
  513. exits("usage");
  514. }
  515. rc = strlen(serve)+sizeof("tcp!!99990");
  516. tcpserve = emalloc(rc);
  517. if(strchr(serve,'!'))
  518. strcpy(tcpserve, serve);
  519. else
  520. snprint(tcpserve, rc, "tcp!%s!5356", serve);
  521. c = login(user, tcpserve, pass_stdin, pass_nvram);
  522. free(tcpserve);
  523. if(c == nil){
  524. fprint(2, "secstore authentication failed\n");
  525. exits("secstore authentication failed");
  526. }
  527. if(chpass)
  528. rc = chpasswd(c, user);
  529. else
  530. rc = cmd(c, gfile, Gflag, pfile, rfile);
  531. if(rc < 0){
  532. fprint(2, "secstore cmd failed\n");
  533. exits("secstore cmd failed");
  534. }
  535. exits("");
  536. return 0;
  537. }