Differences From
Artifact [9c1b0583ee]:
21 21 * TODO prevent pw reads from going off the edge of
22 22 * the screen and fucking up all the shit
23 23 */
24 24
25 25 #include <unistd.h>
26 26 #include <sys/random.h>
27 27 #include <sys/syscall.h>
28 +#include <sys/shm.h>
28 29 #include <stddef.h>
29 30 #include <stdint.h>
30 31 #include <stdlib.h>
31 32 #include <string.h>
32 33 #include <fcntl.h>
33 34 #include <sodium.h>
34 35 #include <termios.h>
................................................................................
43 44 # include <pwd.h>
44 45 # include <stdlib.h>
45 46 #else
46 47 # define copy(str,len)
47 48 #endif
48 49
49 50 enum /* constants */ {
50 - null = 0, true = 1, false = 0
51 + null = 0, true = 1, false = 0,
52 + kpw_shm_key = 0x3CC215A,
51 53 };
52 54
53 55 #include "err.inc"
54 56
55 57 enum /* db format constants */ {
56 58 db_pubkey_len = crypto_box_PUBLICKEYBYTES,
57 59 db_privkey_len = crypto_box_SECRETKEYBYTES,
................................................................................
93 95 #define str_ucase "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
94 96 #define str_lcase "abcdefghijklmnopqrstuvwxyz"
95 97 #define str_num "0123456789"
96 98 const char* reftbl = str_num str_ucase str_lcase;
97 99 const char* reftbl_lcase = str_num str_lcase;
98 100
99 101 const char* _g_binary_name;
100 -#ifdef _CLIPBOARD
101 -char* const*
102 -cbd_cmds[] = {
103 - /* NOTE: these commands must be specified in order of
104 - * most- to least-specific. more than one utility may
105 - * be present on a given system, so we need to make sure
106 - * the right one is called. */
107 - (char* const[]){"termux-clipboard-set", null},
108 - (char* const[]){"xsel", "-bi", null},
109 - /* TODO: allow command to be specified by env var */
110 - null
111 -};
112 102
113 103 enum { plain_term, ansi_term, color_term } _g_term_type[3];
114 104 enum alert {a_notice, a_warn, a_debug, a_fatal = 1 << 8};
115 105 pstr alert_msg[] = {
116 106 _p("(kpw notice)"), _p("(kpw warn)"),
117 107 _p("(kpw debug)"), _p("(kpw fatal)")
118 108 };
................................................................................
144 134 if (_g_term_type[2] != plain_term) write(2,"\x1b[m ",4);
145 135 else write(2," ",1);
146 136 write(2,msg,strlen(msg));
147 137 write(2,"\n",1);
148 138
149 139 if (kind & a_fatal) exit(kind & (~a_fatal));
150 140 }
141 +
142 +#ifdef _CLIPBOARD
143 +char* const*
144 +cbd_cmds[] = {
145 + /* NOTE: these commands must be specified in order of
146 + * most- to least-specific. more than one utility may
147 + * be present on a given system, so we need to make sure
148 + * the right one is called. */
149 + (char* const[]){"termux-clipboard-set", null},
150 + (char* const[]){"xsel", "-bi", null},
151 + /* TODO: allow command to be specified by env var */
152 + null
153 +};
154 +
151 155
152 156 enum bad
153 157 copy(const char* str, size_t len) {
154 158 alert(a_debug, "copying password to clipboard");
155 159 if (geteuid() == 0) {
156 160 /* on a sane system, what we'd do is hike up the process
157 161 * tree til we found a non-root user. alas, this is UNIX. */
................................................................................
456 460 }
457 461
458 462 enum bad
459 463 dbunlock(byte* priv_enc, byte* salt, byte* priv) {
460 464 const size_t priv_sz = sizeof(key_priv);
461 465 byte key [db_privkey_len];
462 466
463 - password dbpw; size_t pwlen;
464 - bad e = pwread(true, dbpw, &pwlen,_str("database key: "));
465 - if (e != ok) return e;
466 -
467 - alert(a_debug, "deriving secret");
468 - if(crypto_pwhash(key, sz(key), dbpw, pwlen, salt,
469 - crypto_pwhash_OPSLIMIT_INTERACTIVE,
470 - crypto_pwhash_MEMLIMIT_INTERACTIVE,
471 - crypto_pwhash_ALG_DEFAULT) != 0) {
472 - return bad_mem;
473 - }
474 - hexdump(key, sz(key));
475 -
476 - alert(a_debug, "attempting to decrypt private key");
477 - for (size_t i = 0; i < sz(key); ++i) {
478 - priv[i] = priv_enc[i] ^ key[i];
479 - }
480 - hexdump(priv, sz(key));
467 + /* is the private key loaded into memory? */
468 + int shm = shmget(*((key_t*) salt), sizeof(key_priv), 0);
469 + if (shm == -1) {
470 + /* no key in memory - read password from stdin instead */
471 + password dbpw; size_t pwlen;
472 + bad e = pwread(true, dbpw, &pwlen,_str("database key: "));
473 + if (e != ok) return e;
474 +
475 + alert(a_debug, "deriving secret");
476 + if(crypto_pwhash(key, sz(key), dbpw, pwlen, salt,
477 + crypto_pwhash_OPSLIMIT_INTERACTIVE,
478 + crypto_pwhash_MEMLIMIT_INTERACTIVE,
479 + crypto_pwhash_ALG_DEFAULT) != 0) {
480 + return bad_mem;
481 + }
482 + hexdump(key, sz(key));
483 +
484 + alert(a_debug, "attempting to decrypt private key");
485 + for (size_t i = 0; i < sz(key); ++i) {
486 + priv[i] = priv_enc[i] ^ key[i];
487 + }
488 + hexdump(priv, sz(key));
489 + } else {
490 + /* found a key in memory; loading it into *priv */
491 + alert(a_notice, "using saved key");
492 + key_priv* saved = shmat(shm, 0, 0);
493 + if (saved == (void*)-1)
494 + return bad_shm;
495 + hexdump((byte*)saved, sizeof(key_priv));
496 + memcpy(priv, saved, sizeof(key_priv));
497 + shmdt(saved);
498 + }
481 499
482 500 return ok;
483 501 }
484 502
485 503 enum bad
486 504 dbverify(byte* salt, byte* salt_enc, byte* pub, byte* priv) {
487 505 byte salt_dec [crypto_pwhash_SALTBYTES];
................................................................................
545 563 int
546 564 kpw(int argc, const char** argv) {
547 565 if (argc == 0) return bad_insane;
548 566 _g_binary_name = argv[0];
549 567
550 568 enum genmode
551 569 mode = lower;
552 - enum {usage,getpw,addpw,delpw,lspw,genpw,regen,
553 - chpw,keyin,logout,createdb,rekeydb,help}
570 + enum {usage,getpw,addpw,delpw,lspw,
571 + genpw,regen,mergedb,chpw,keyin,
572 + logout,createdb,rekeydb,help}
554 573 op = getpw;
555 574
556 575 const char* params[3];
557 576 uint8_t param = 0;
558 577
559 578 bool print = false,
560 579 clobber = false,
................................................................................
591 610
592 611 if (op == getpw && param == 0) return emit_usage(NULL);
593 612
594 613 if (sodium_init() < 0)
595 614 return bad_lib_sodium_init;
596 615
597 616 switch(op) {
617 + case logout:
618 + case keyin: {
619 + if (param != 0) return bad_syntax;
620 +
621 + int db = dbopen(O_RDONLY);
622 + key_pub pub;
623 + key_priv priv, priv_enc;
624 + byte salt [crypto_pwhash_SALTBYTES],
625 + salt_enc [crypto_box_SEALBYTES + sz(salt)];
626 +
627 + bad e;
628 + if ((e = dbheader_load(db, salt, salt_enc, pub, priv_enc)) != ok)
629 + return e;
630 +
631 + key_t ipck = *((key_t*)salt);
632 + if (op == keyin) {
633 + if ((e = dbunlock(priv_enc, salt, priv)) != ok)
634 + return e;
635 + if ((e = dbverify(salt, salt_enc, pub, priv)) != ok)
636 + return e;
637 + int shm = shmget(ipck, sizeof(key_priv),
638 + IPC_CREAT | IPC_EXCL | 0600);
639 + if (shm == -1) return bad_shm_exist;
640 +
641 + key_priv* saved = shmat(shm, 0, 0);
642 + if (saved == (void*)-1) return bad_shm;
643 + memcpy(saved, priv, sz(priv));
644 + shmdt(saved);
645 + } else {
646 + int shm = shmget(ipck, sizeof(key_priv), 0);
647 + if (shm == -1) return bad_no_shm;
648 + shmctl(shm, IPC_RMID, NULL);
649 + }
650 +
651 + return ok;
652 + }
598 653
599 654 case genpw:
600 655 case addpw: {
601 656 if (param == 0) return emit_usage(
602 657 op == addpw ? " -a[p] <account> [<pw>]\n" :
603 - /* genpw */" -g[lmup] <account> [<pw len>]\n");
658 + /* genpw */" -g[lmusp] <account> [<pw len>]\n");
604 659
605 660 if (param > 2 || param < 1) return bad_syntax;
606 661 const char* acct = params[0],
607 662 * prm = (param == 2 ? params[1] : NULL);
608 663
609 664 alert(a_debug, "opening database");
610 665 int db = dbopen(O_RDWR);
................................................................................
620 675
621 676 password pw; size_t pwlen;
622 677 const char* acct_pw;
623 678 if (op == addpw) {
624 679 if (prm == NULL) {
625 680 pstr prompt_l[] = { _p("- new password for "),
626 681 {0, acct}, _p(": "), };
627 - char prompt[pstrsum(prompt_l, sz(prompt_l)) + 1];
682 + char prompt[pstrsum(prompt_l, sz(prompt_l))];
628 683 if (tty_in) pstrcoll(prompt_l, sz(prompt_l), prompt);
629 684
630 685 bad e = pwread(!print, pw, &pwlen,
631 - prompt, sz(prompt) - 1);
686 + prompt, sz(prompt));
632 687 if (e != ok) return e;
633 688 if (tty_in && !print) {
634 689 password pw_conf;
635 690 e = pwread(true, pw_conf, NULL, _str("- confirm: "));
636 691 if (e != ok) return e;
637 692 if (strcmp(pw,pw_conf) != 0)
638 693 return bad_pw_match;
639 694 }
640 695 acct_pw = pw;
641 - } else acct_pw = prm;
696 + } else acct_pw = prm, pwlen = strlen(prm);
642 697
643 698 } else if (op == genpw) {
644 699 unsigned long long len;
645 700 if (prm != NULL) {
646 701 alert(a_debug, "converting length parameter to integer");
647 702 bad e = katoi(10, prm, &len);
648 703 if (e != ok) return bad_num;
................................................................................
653 708 if (mkpw(mode, pw, len) == bad_entropy) return bad_entropy;
654 709 if (print || !tty_out) {
655 710 write(1, pw, len);
656 711 if(tty_out) write(1, "\n", 1);
657 712 }
658 713 pwlen = len;
659 714 }
660 - if (copy_pw) copy(pw, pwlen);
715 +# ifdef _CLIPBOARD
716 + if (copy_pw) copy(pw, pwlen);
717 +# endif
661 718 alert(a_debug, "encoding database entry");
662 719 db_sz acctlen = strlen(acct);
663 720 byte plaintext[1 + acctlen +
664 721 1 + pwlen];
665 722 plaintext[0] = acctlen;
666 723 strncpy(plaintext + 1, acct, acctlen);
667 724 plaintext[1 + acctlen] = pwlen;
................................................................................
679 736 write(db, &ciphertext_len, 1);
680 737 write(db, &ciphertext, ciphertext_len);
681 738
682 739 close(db);
683 740 break;
684 741 }
685 742
743 + case chpw:
744 + case regen:
686 745 case delpw:{ /* kpw -d <acct> */
687 - if (param == 0) return emit_usage(" -d <account>\n");
688 - if (param != 1) return bad_syntax;
746 + if (param == 0) return emit_usage
747 + (op==delpw ? " -d <account>\n" :
748 + op==regen ? " -r[lmusp] <account> [<pw len>]" :
749 + /* op==chpw */ " -c <account> [<new pw>]");
750 +
751 + if (param < 1 || param > (op == delpw ? 1 : 2))
752 + return bad_syntax;
689 753 const char* target = params[0];
690 - bad e;
754 + const char* delta;
755 + if (param == 2) delta=params[1];
756 + else delta=NULL;
691 757
692 758 int db = dbopen(O_RDWR);
693 759 if (db == -1) return bad_db_load;
694 760
695 761 const size_t dbsz = lseek(db, 0, SEEK_END);
696 762 lseek(db, 0, SEEK_SET);
697 763
698 764 key_pub pub;
699 765 key_priv priv, priv_enc;
700 766
701 767 byte salt [crypto_pwhash_SALTBYTES],
702 768 salt_enc [crypto_box_SEALBYTES + sz(salt)];
703 769
770 + bad e;
704 771 if ((e = dbheader_load(db, salt, salt_enc, pub, priv_enc)) != ok)
705 772 return e;
706 773 if ((e = dbunlock(priv_enc, salt, priv)) != ok)
707 774 return e;
708 775 if ((e = dbverify(salt, salt_enc, pub, priv)) != ok)
709 776 return e;
710 777
................................................................................
733 800 struct dbrecord rec;
734 801
735 802 bad d;
736 803 if((d = dbnext(db, &rec, ctlen,
737 804 pub, priv, ctbuf, ptbuf)) != ok) return d;
738 805
739 806 if(strncmp(target, rec.acct.ptr, rec.acct.len) == 0) {
740 - /* found a matching record; do not copy it */
741 - alert(a_debug, "found record to delete");
807 + /* found a matching record; determine
808 + * what to do to the fucker */
809 + alert(a_debug, "found target record");
742 810 found = true;
811 + if (op == delpw) continue;
812 +
813 + password pwbuf;
814 + const char* newpass;
815 + size_t pwlen;
816 + if (op == regen) {
817 + alert(a_debug, "generating new password");
818 + /* generating a new password. use the default
819 + * length if the user hasn't supplied one herself,
820 + * or if she has, convert it to an integer. */
821 + if (delta == NULL) pwlen = default_pw_len; else {
822 + unsigned long long value;
823 + bad k = katoi(10, delta, &value);
824 + if (k != ok) return bad_num;
825 + pwlen = value;
826 + }
827 + bad m = mkpw(mode, pwbuf, pwlen);
828 + if (m != ok) return m;
829 + newpass = pwbuf;
830 + } else if (op == chpw) {
831 + /* the user has requested a password change. take
832 + * it from the command line if available, otherwise
833 + * generate a prompt and read from stdin */
834 +
835 + if (delta == NULL) {
836 + pstr prompt_l[] = { _p("- new password for "),
837 + {0, target}, _p(": "), };
838 + char prompt[pstrsum(prompt_l, sz(prompt_l))];
839 + if (_g_term_type[0] > plain_term)
840 + pstrcoll(prompt_l, sz(prompt_l), prompt);
841 +
842 + bad p = pwread(!print, pwbuf, &pwlen, prompt, sz(prompt));
843 + if (p != ok) return p;
844 + /* prompt again to make sure the user entered
845 + * her new password correctly */
846 + if(!print && _g_term_type[0] > plain_term) {
847 + password passconf;
848 + p = pwread(!print, passconf, NULL, _str("confirm: "));
849 + if (p != ok) return p;
850 + if (strcmp(passconf, pwbuf) != 0)
851 + return bad_pw_match;
852 + }
853 + newpass = pwbuf;
854 + } else newpass = delta, pwlen = strlen(delta);
855 + } else return bad_assert;
856 +
857 + if (op == regen && print) {
858 + write(1, newpass, pwlen);
859 + if (_g_term_type[1] > plain_term) write(1, "\n", 1);
860 + }
861 +
862 +# ifdef _CLIPBOARD
863 + if (copy_pw) copy(newpass, pwlen);
864 +# endif
865 +
866 + /* new pw is pointed to by `newpass`. encrypt it
867 + * and insert it into the new database image */
868 + byte plaintext [1 + rec.acct.len +
869 + 1 + pwlen ];
870 + plaintext[0] = rec.acct.len;
871 + memcpy(plaintext + 1, rec.acct.ptr, rec.acct.len);
872 + plaintext[1 + rec.acct.len] = pwlen;
873 + memcpy(plaintext + 2 + rec.acct.len, newpass, pwlen);
874 +
875 + alert(a_debug, "enciphering plaintext of modified record");
876 + hexdump(plaintext, sz(plaintext));
877 +
878 + byte ciphertext [sz(plaintext) + crypto_box_SEALBYTES];
879 + crypto_box_seal(ciphertext, plaintext, sz(plaintext), pub);
880 + db_sz new_ct_len = sz(ciphertext);
881 +
882 + alert(a_debug, "copying ciphertext into database");
883 + cursor = transcribe(cursor, &new_ct_len, 1);
884 + cursor = transcribe(cursor, ciphertext, sz(ciphertext));
743 885 } else {
744 - /* not our target, copy it into the buffer */
886 + /* not our target, copy it into the buffer as-is */
745 887 cursor = transcribe(cursor, &ctlen, sizeof(db_sz));
746 888 cursor = transcribe(cursor, ctbuf, sz(ctbuf));
747 889 }
748 890
749 891 } if (e != fail) return e;
750 892 if (scanned == 0) return bad_db_empty;
751 893 if (!found) return bad_index;
................................................................................
756 898 ftruncate(db,0);
757 899 lseek(db, 0, SEEK_SET);
758 900 write(db, newdb, cursor-newdb);
759 901
760 902 /* we're done here, time to close up shop */
761 903 close(db);
762 904
763 - break;
905 + return ok;
764 906 }
765 907
766 908 case getpw: /* kpw <acct> */
767 909 case lspw: { /* kpw -t[p] [<prefix>] */
768 910 const char* target;
769 911 if (param == 1) target = params[0];
770 912 else if (param == 0) target = NULL;
................................................................................
810 952 if (strncmp(rec.acct.ptr,target,rec.acct.len) == 0) {
811 953 if (print || _g_term_type[1] == plain_term) {
812 954 write(1, rec.pw.ptr, rec.pw.len);
813 955 if(_g_term_type[1] > plain_term)
814 956 write(1, "\n", 1);
815 957 }
816 958
817 - if (_g_term_type[1] > plain_term) {
818 - if (copy_pw) copy(rec.pw.ptr, rec.pw.len);
819 - }
959 +# ifdef _CLIPBOARD
960 + if (_g_term_type[1] > plain_term) {
961 + if (copy_pw) copy(rec.pw.ptr, rec.pw.len);
962 + }
963 +# endif
820 964 found = true;
821 965 break;
822 966 }
823 967 }
824 968 }
825 969 if (scanned == 0) return bad_db_empty;
826 970 if (result != fail) return result;
827 971 else if (!found) return bad_index;
828 972
829 - break;
973 + return ok;
830 974 }
831 975
832 976 case createdb: { /* kpw -C [<db>] */
833 977 alert(a_debug, "creating new database");
834 978 if (clobber)
835 979 alert(a_warn, "will clobber any existing database");
836 980 /* before we open our new file, we need to generate
................................................................................
924 1068 write(db, pub, sz(pub));
925 1069 write(db, salt, sz(salt));
926 1070 write(db, priv_enc, sz(priv_enc));
927 1071 write(db, salt_enc, sz(salt_enc));
928 1072 close(db);
929 1073
930 1074 alert(a_debug, "database created");
931 - break;
1075 + return ok;
932 1076 }
933 1077 }
934 1078
935 -# ifdef _CLIPBOARD
936 - if (copy_pw) {
937 - /* copy(buf,len); */
938 - }
939 -# endif
940 -
941 1079 return ok;
942 1080 }
943 1081
944 1082 int
945 1083 main (int argc, const char** argv) {
946 1084 const char* colorterm = getenv("COLORTERM");
947 1085 const char* term = getenv("TERM");