Differences From
Artifact [22f71ce9d0]:
232 232 pstr account;
233 233 pstr pw;
234 234 };
235 235
236 236 enum term_clear {term_clear_line,
237 237 term_clear_screen};
238 238
239 -void term_clear(enum term_clear behavior) {
239 +void term_clear(int tty, enum term_clear behavior) {
240 240 switch(behavior) {
241 - case term_clear_line: write(1,"\r\x1b[2K",5); break;
242 - case term_clear_screen: write(1,"\r\x1b[3J",5); break;
241 + case term_clear_line: write(tty,"\r\x1b[2K",5); break;
242 + case term_clear_screen: write(tty,"\r\x1b[3J",5); break;
243 243 }
244 244 }
245 -void term_bell() {
246 - write(1,"\a",1);
245 +void term_bell(int tty) {
246 + write(tty,"\a",1);
247 247 }
248 248
249 249 typedef char password[kpw_db_pw_max + 1];
250 250 bad pwread(bool obscure, char* dest, size_t* out_len, const char* prompt, const size_t plen) {
251 251 if (isatty(0)) {
252 + int tty = 1;
253 + if (!isatty(tty)) tty = open("/dev/tty", O_WRONLY);
254 + if (tty == -1) return bad_insane;
255 +
252 256 struct termios initial; {
253 257 /* in order to take PW input, we need to shut
254 258 * off echo and canonical mode. now we're in
255 259 * charge of reading each keypress. */
256 - tcgetattr(1, &initial);
260 + tcgetattr(tty, &initial);
257 261 struct termios nt = initial;
258 262 nt.c_lflag &= (~ECHO & ~ICANON);
259 - tcsetattr(1, TCSANOW, &nt);
263 + tcsetattr(tty, TCSANOW, &nt);
260 264 }
261 265
262 266 *dest = 0;
263 267 char* p = dest;
264 268
265 269 do {
266 - term_clear(term_clear_line);
267 - if (_g_term_type[1] >= ansi_term) write(1, "\x1b[1m", 4);
268 - write(1, prompt, plen);
269 - if (_g_term_type[1] >= ansi_term) write(1, "\x1b[21m", 5);
270 + term_clear(tty,term_clear_line);
271 + if (_g_term_type[0] >= ansi_term) write(tty, "\x1b[1m", 4);
272 + write(tty, prompt, plen);
273 + if (_g_term_type[0] >= ansi_term) write(tty, "\x1b[21m", 5);
270 274
271 275 if (obscure) for(size_t i = 0; i < p - dest; ++i)
272 - write(1, "*", 1);
273 - else write(1, dest, p-dest);
276 + write(tty, "*", 1);
277 + else write(tty, dest, p-dest);
274 278
275 279 char c;
276 280 if (read(0, &c, 1) == 1) {
277 281 switch (c) {
278 282 case '\n': case '\r':
279 283 /* accept pw */
280 284 if (p > dest) goto end_read_loop;
281 285 else break;
282 286 case '\x1b': /* escape */
283 - term_clear(term_clear_line);
287 + term_clear(tty, term_clear_line);
284 288 return bad_cancel;
285 289 case '\b': case '\x7f':
286 290 if (p > dest) *p--=0;
287 - else term_bell();
291 + else term_bell(tty);
288 292 break;
289 293 default:
290 294 if (p - dest != 64) *p++=c;
291 - else term_bell();
295 + else term_bell(tty);
292 296 }
293 297 } else {
294 298 /* either EOF or an error - either way,
295 299 * we're finished here */
296 300 break;
297 301 }
298 302 } while(1);
299 - end_read_loop: term_clear(term_clear_line);
303 + end_read_loop: term_clear(tty, term_clear_line);
300 304 *p = 0;
301 305 if (out_len!=NULL) *out_len = p - dest;
302 306
303 307 /* return the terminal to normal */
304 - tcsetattr(1, TCSANOW, &initial);
308 + tcsetattr(tty, TCSANOW, &initial);
309 +
310 + if (tty != 1) close(tty);
305 311 } else {
306 312 alert(a_warn, "reading pw from standard input");
307 313 ssize_t ct = read(0, dest, kpw_db_pw_max);
308 314 dest[ct] = 0;
309 315 }
310 316 }
311 317
312 -#include <stdio.h>
313 318 int
314 319 dbopen(int flags) {
315 320 const char* dbpath = getenv("kpw_db");
316 321 int db;
317 322 if (dbpath == NULL) {
318 323 const char* cfg = getenv("XDG_CONFIG_HOME");
319 324 if (cfg == NULL) {
................................................................................
333 338 char buf[cfglen + path[1].len + 1];
334 339 bzero(buf, sz(buf));
335 340 impose(path, sz(path), NULL, buf);
336 341
337 342 db = open(buf, flags, 0600);
338 343 }
339 344 } else {
340 - printf("path is %s", dbpath);
341 345 db = open(dbpath, flags, 0600);
342 346 }
343 347
344 348 return db;
345 349 }
346 350
347 351 void bytedump(uint8_t* bytes, size_t sz) {
................................................................................
349 353 char tpl[] ="\x1b[35m \x1b[m";
350 354 char* c = tpl + 5;
351 355 *c = bytes[i];
352 356 if (*c < ' ' || *c > '~') *c='.', write(2, tpl, sz(tpl));
353 357 else write(2, c, 1);
354 358 }
355 359 }
360 +
356 361 void hexdump(uint8_t* bytes, size_t sz) {
357 362 if(!_g_debug_msgs) return;
358 363 alert(a_debug, "printing hex dump");
359 364 uint8_t* st = bytes;
360 365 for (size_t i = 0; i < sz; ++i) {
361 366 char hex[5] = " ";
362 367 kitoa(16, bytes[i], hex, hex + 2, NULL, true);
................................................................................
369 374 } else if (i == sz - 1) {
370 375 write(2, _str("│ "));
371 376 bytedump(st, (bytes + sz) - st);
372 377 write(2, "\n", 1);
373 378 }
374 379 }
375 380 }
381 +
382 +enum bad
383 +dbdecrypt(int db, uint8_t* pubkey, uint8_t* privkey) {
384 + password dbpw; size_t pwlen;
385 + bad e = pwread(true, dbpw, &pwlen,_str("database key: "));
386 +
387 + uint8_t salt [crypto_pwhash_SALTBYTES],
388 + key [db_privkey_len],
389 + priv_enc [db_privkey_len],
390 + priv [db_privkey_len],
391 + pub [db_pubkey_len];
392 + uint8_t salt_enc [crypto_box_SEALBYTES + sz(salt)],
393 + salt_dec [sz(salt)];
394 +
395 + alert(a_debug, "loading public key");
396 + ssize_t sr = read(db, pub, sz(pub));
397 + if (sr != sz(pub)) return bad_db_corrupt;
398 + hexdump(pub, sz(pub));
399 +
400 + alert(a_debug, "loading password salt");
401 + sr = read(db, salt, sz(salt));
402 + if (sr != sz(salt)) return bad_db_corrupt;
403 + hexdump(salt, sz(salt));
404 +
405 + alert(a_debug, "deriving secret");
406 + if(crypto_pwhash(key, sz(key), dbpw, pwlen, salt,
407 + crypto_pwhash_OPSLIMIT_INTERACTIVE,
408 + crypto_pwhash_MEMLIMIT_INTERACTIVE,
409 + crypto_pwhash_ALG_DEFAULT) != 0) {
410 + return bad_mem;
411 + }
412 + hexdump(key, sz(key));
413 +
414 + alert(a_debug, "loading encrypted private key");
415 + read(db, priv_enc, sz(priv_enc));
416 + hexdump(priv_enc, sz(priv_enc));
417 +
418 + alert(a_debug, "decrypting private key");
419 + for (size_t i = 0; i < sz(key); ++i) {
420 + priv[i] = priv_enc[i] ^ key[i];
421 + }
422 + hexdump(priv, sz(priv));
423 +
424 + alert(a_debug, "loading verification hash");
425 + read(db, salt_enc, sz(salt_enc));
426 + hexdump(salt_enc, sz(salt_enc));
427 +
428 + alert(a_debug, "decrypting verification hash");
429 + int r = crypto_box_seal_open(salt_dec, salt_enc,
430 + sz(salt_enc), pub, priv);
431 + if (r != 0) return bad_pw;
432 + hexdump(salt_dec, sz(salt_dec));
433 +
434 + if (memcmp(salt,salt_dec,sz(salt)) != 0) return bad_db_corrupt;
435 +
436 + /* TODO refactor to avoid unnecessary memcpy */
437 + memcpy(privkey, priv, sz(priv));
438 + memcpy(pubkey, pub, sz(pub));
439 +
440 + return ok;
441 +}
442 +
443 +#include<stdio.h>
444 +void bright(int fd, const char* str, size_t len) {
445 + if (_g_term_type[fd] >= ansi_term) write(fd, _str("\x1b[1m"));
446 + write(fd, str, len);
447 + if (_g_term_type[fd] >= ansi_term) write(fd, _str("\x1b[21m"));
448 +}
376 449
377 450 int
378 451 kpw(int argc, const char** argv) {
379 452 if (argc == 0) return bad_insane;
380 453 _g_binary_name = argv[0];
381 454
382 455 enum genmode
................................................................................
430 503 return bad_usage;
431 504 }
432 505
433 506 if (sodium_init() < 0)
434 507 return bad_lib_sodium_init;
435 508
436 509 switch(op) {
437 - case getpw:{ /* kpw <acct> */
438 - break;
439 - }
440 -
510 +
441 511 case genpw: /* kpw -g[lmu] <acct> [<len>] */
442 512 case addpw: { /* kpw -a <acct> [<pw>] */
443 513 if (param > 2 || param < 1) return bad_syntax;
444 514 const char* acct = params[0],
445 515 * prm = (param == 2 ? params[1] : NULL);
446 516
447 517 alert(a_debug, "opening database");
................................................................................
521 591 break;
522 592 }
523 593
524 594 case delpw:{ /* kpw -d <acct> */
525 595 break;
526 596 }
527 597
598 + case getpw: /* kpw <acct> */
528 599 case lspw: { /* kpw -t[p] [<prefix>] */
529 600 alert(a_debug, "opening database for reading");
530 - int db = dbopen(O_RDONLY);
531 - if (db == -1) return bad_db_load;
532 - password dbpw; size_t pwlen;
533 - bad e = pwread(!print, dbpw, &pwlen,_str("database key: "));
534 -
535 - uint8_t salt [crypto_pwhash_SALTBYTES],
536 - key [db_privkey_len],
537 - priv_enc [db_privkey_len],
538 - priv [db_privkey_len],
539 - pub [db_pubkey_len];
540 - uint8_t salt_enc [crypto_box_SEALBYTES + sz(salt)],
541 - salt_dec [sz(salt)];
542 - bzero(salt_dec, sz(salt_dec));
543 -
544 - alert(a_debug, "loading public key");
545 - ssize_t sr = read(db, pub, sz(pub));
546 - if (sr != sz(pub)) return bad_db_corrupt;
547 - hexdump(pub, sz(pub));
548 -
549 - alert(a_debug, "loading password salt");
550 - sr = read(db, salt, sz(salt));
551 - if (sr != sz(salt)) return bad_db_corrupt;
552 - hexdump(salt, sz(salt));
553 -
554 - alert(a_debug, "deriving secret");
555 - if(crypto_pwhash(key, sz(key), dbpw, pwlen, salt,
556 - crypto_pwhash_OPSLIMIT_INTERACTIVE,
557 - crypto_pwhash_MEMLIMIT_INTERACTIVE,
558 - crypto_pwhash_ALG_DEFAULT) != 0) {
559 - return bad_mem;
601 + int db = dbopen(O_RDONLY);
602 + if (db == -1) return bad_db_load;
603 +
604 + const char* target;
605 + if (param == 1) target = params[0];
606 + else if (param == 0) target = NULL;
607 + else return bad_syntax;
608 +
609 + uint8_t priv [db_privkey_len],
610 + pub [db_pubkey_len];
611 +
612 + /* try to decrypt db */ {
613 + bad e = dbdecrypt(db,pub,priv);
614 + if (e != ok) return e;
615 + /* TODO allow multiple tries */
616 + }
617 +
618 + /* cursor should now be positioned
619 + * on first record */
620 + alert(a_debug, "beginning to scan records");
621 + read_rec: {
622 + uint8_t acctlen;
623 + if (read(db, &acctlen, 1) != 1)
624 + goto done_reading;
625 + uint8_t ciphertext[acctlen];
626 + if (read(db, &ciphertext, acctlen) != acctlen)
627 + return bad_db_corrupt;
628 + alert(a_debug, "scanned record");
629 + hexdump(ciphertext, sz(ciphertext));
630 +
631 + uint8_t plaintext[sz(ciphertext) - crypto_box_SEALBYTES];
632 + if(crypto_box_seal_open(plaintext, ciphertext, sz(ciphertext), pub, priv) != 0)
633 + return bad_db_corrupt;
634 +
635 + alert(a_debug, "record deciphered");
636 + hexdump(plaintext, sz(plaintext));
637 +
638 + uint8_t record_name_len = plaintext[0],
639 + record_pw_len = plaintext[record_name_len + 1];
640 +
641 + pstr record_name = {record_name_len, plaintext + 1},
642 + record_pw = {record_pw_len,
643 + plaintext + record_name_len + 2};
644 +
645 + if(op == lspw) {
646 + bright(1, record_name.ptr, record_name.len);
647 + if (print || !isatty(1)) {
648 + write(1, ": ", 2);
649 + write(1, record_pw.ptr, record_pw.len);
650 + }
651 + write(1, "\n", 1);
652 + } else if (op == getpw) {
653 + if (strncmp(record_name.ptr,target,record_name.len) == 0) {
654 + if (print || _g_term_type[1] == plain_term) {
655 + write(1, record_pw.ptr, record_pw.len);
656 + if(_g_term_type[1] > plain_term)
657 + write(1, "\n", 1);
658 + }
659 +
660 + if (_g_term_type[1] > plain_term) {
661 + if (copy_pw) copy(record_pw.ptr, record_pw.len);
662 + }
663 + goto done_reading;
664 + }
665 + }
666 +
667 + goto read_rec;
560 668 }
561 - hexdump(key, sz(key));
669 + return bad_index;
562 670
563 - alert(a_debug, "loading encrypted private key");
564 - read(db, priv_enc, sz(priv_enc));
565 - hexdump(priv_enc, sz(priv_enc));
566 -
567 - alert(a_debug, "decrypting private key");
568 - for (size_t i = 0; i < sz(key); ++i) {
569 - priv[i] = priv_enc[i] ^ key[i];
570 - }
571 - hexdump(priv, sz(priv));
572 -
573 - alert(a_debug, "loading verification hash");
574 - read(db, salt_enc, sz(salt_enc));
575 - hexdump(salt_enc, sz(salt_enc));
576 -
577 - alert(a_debug, "decrypting verification hash");
578 - hexdump(pub, sz(pub));
579 - hexdump(priv, sz(priv));
580 - printf("sz salt_enc = %zu\n / crypto_box bytes = %zu", sz(salt_enc), crypto_box_SEALBYTES);
581 - int r = crypto_box_seal_open(salt_dec, salt_enc,
582 - sz(salt_enc), pub, priv);
583 - printf("result code: %d\n",r);
584 - hexdump(salt_dec, sz(salt_dec));
585 - break;
671 + done_reading: break;
586 672 }
587 673
588 674 case createdb: { /* kpw -C [<db>] */
589 675 alert(a_debug, "creating new database");
590 676 if (clobber)
591 677 alert(a_warn, "will clobber any existing database");
592 678 /* before we open our new file, we need to generate
................................................................................
647 733 for (size_t i = 0; i < sz(key); ++i) {
648 734 priv_enc[i] = priv[i] ^ key[i];
649 735 }
650 736 alert(a_debug, "private key encrypted");
651 737 hexdump(priv_enc, sz(priv_enc));
652 738
653 739 alert(a_debug, "encrypting salt");
654 - crypto_box_seal(salt_enc, salt, sz(salt), priv);
740 + crypto_box_seal(salt_enc, salt, sz(salt), pub);
655 741 hexdump(salt_enc, sz(salt_enc));
656 742
657 743 /* we have everything we need. now we create the
658 744 * file, failing if it already exists so as not
659 745 * to clobber anyone's passwords.
660 746 */
661 747 alert(a_debug, "creating new database on disk");