Overview
Comment: | fix so it compiles in non-clipboard mode |
---|---|
Downloads: | Tarball | ZIP archive | SQL archive |
Timelines: | family | ancestors | descendants | both | trunk |
Files: | files | file ages | folders |
SHA3-256: |
6f480eda3fa4630fbbf0138e4dec2367 |
User & Date: | lexi on 2019-08-16 01:56:59 |
Other Links: | manifest | tags |
Context
2019-08-16
| ||
02:00 | fix addpw/genpw bugs check-in: 37cc6dd034 user: lexi tags: trunk | |
01:56 | fix so it compiles in non-clipboard mode check-in: 6f480eda3f user: lexi tags: trunk | |
2019-08-15
| ||
23:49 | fix missing return that was breaking build under clang, add ability to delete records check-in: 178e52ca55 user: lexi tags: trunk | |
Changes
Modified kpw.d/errtab from [ce1ef8cc06] to [e6d4c7ddfd].
1 1 ok completed successfully debug 0 2 2 fail unknown error 3 +assert bug detected; please report this 3 4 mem out of memory 4 5 num not a number 5 6 user kpw started as wrong user 6 7 option unrecognized option passed 7 8 syntax invalid invocation syntax 8 9 entropy could not acquire entropy 9 10 copy could not copy password 10 11 pipe could not create pipe 11 12 insane your environment is not sane 12 13 index no such entry in database 14 +shm shared memory failure 15 +shm_exist key already in memory notice 16 +no_shm no key in memory notice 13 17 db_create database could not be created 14 18 db_load database could not be read 15 19 db_corrupt database corrupt 16 20 db_empty no records in database notice 17 21 cancel user canceled operation notice 18 22 pw invalid password 19 23 pw_match passwords do not match 20 24 usage usage displayed to user debug 64 21 25 lib unspecified library error fatal 128 22 26 lib_sodium_init could not initialize libsodium
Modified kpw.d/kpw.c from [9c1b0583ee] to [baa5c21b20].
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");
Modified kpw.d/optab from [c9d99a07ce] to [a42323bd02].
1 -C create op = createdb create a new password database 2 -R rekey op = rekeydb change database passphrase 3 -! clobber clobber = true disable safety checks 4 -a add op = addpw add password to the database 5 -g gen op = genpw add account with a random password 6 -d del op = delpw delete password from the database 7 -t list op = lspw list all accounts (with passwords if -p is set) 8 -c chpw op = chpw change password in the database 9 -r regen op = regen generate new password for existing account 10 -l lower mode = lower generate lowercase password 11 -m mix mode = mix generate mix-case password 12 -u upper mode = upper generate uppercase password 13 -s stupid mode = stupid circumvent dumb pw restrictions 14 -k key op = keyin save database key in session memory 15 -o logout op = logout delete db key from session memory 16 -n no-copy copy_pw = false print password instead of copying to clipboard _CLIPBOARD 17 -p print print = true display passwords onscreen 18 -q quiet _g_alert_quiet = true hide non-fatal reports 19 -v verbose _g_debug_msgs = true display debug reports 20 -h help op = help display help text 1 +C create op = createdb create a new password database 2 +R rekey op = rekeydb change database passphrase 3 +M merge op = mergedb merge in another database 4 +! clobber clobber = true disable safety checks 5 +t list op = lspw list all accounts (with passwords if -p is set) 6 +a add op = addpw add password to the database 7 +g gen op = genpw add account with a random password 8 +d del op = delpw delete password from the database 9 +c chg op = chpw change password in the database 10 +r regen op = regen generate new password for existing account 11 +l lower mode = lower generate lowercase password 12 +m mix mode = mix generate mix-case password 13 +u upper mode = upper generate uppercase password 14 +s stupid-mode mode = stupid circumvent dumb pw restrictions 15 +k install-key op = keyin install database key in session memory 16 +o logout op = logout delete db key from session memory 17 +n no-copy copy_pw = false print password instead of copying to clipboard _CLIPBOARD 18 +p print-pw print = true display passwords onscreen 19 +q quiet _g_alert_quiet = true hide non-fatal reports 20 +v verbose _g_debug_msgs = true display debug reports 21 +h help op = help display help text