util  Check-in [6f480eda3f]

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: 6f480eda3fa4630fbbf0138e4dec236702d0b71dfe81a67aa721eca42faecf88
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
2

3
4
5
6
7
8
9
10
11
12



13
14
15
16
17
18
19
20
21
22
ok			completed successfully				debug	0
fail		unknown error

mem			out of memory
num			not a number
user		kpw started as wrong user
option		unrecognized option passed
syntax		invalid invocation syntax
entropy		could not acquire entropy
copy		could not copy password
pipe		could not create pipe
insane		your environment is not sane
index		no such entry in database



db_create	database could not be created
db_load		database could not be read
db_corrupt	database corrupt
db_empty	no records in database				notice
cancel		user canceled operation				notice
pw			invalid password
pw_match	passwords do not match
usage		usage displayed to user				debug	64
lib				unspecified library error		fatal	128
lib_sodium_init	could not initialize libsodium


>










>
>
>










1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
ok			completed successfully				debug	0
fail		unknown error
assert		bug detected; please report this
mem			out of memory
num			not a number
user		kpw started as wrong user
option		unrecognized option passed
syntax		invalid invocation syntax
entropy		could not acquire entropy
copy		could not copy password
pipe		could not create pipe
insane		your environment is not sane
index		no such entry in database
shm			shared memory failure
shm_exist	key already in memory				notice
no_shm		no key in memory					notice
db_create	database could not be created
db_load		database could not be read
db_corrupt	database corrupt
db_empty	no records in database				notice
cancel		user canceled operation				notice
pw			invalid password
pw_match	passwords do not match
usage		usage displayed to user				debug	64
lib				unspecified library error		fatal	128
lib_sodium_init	could not initialize libsodium

Modified kpw.d/kpw.c from [9c1b0583ee] to [baa5c21b20].

21
22
23
24
25
26
27

28
29
30
31
32
33
34
..
43
44
45
46
47
48
49
50

51
52
53
54
55
56
57
..
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
...
144
145
146
147
148
149
150














151
152
153
154
155
156
157
...
456
457
458
459
460
461
462




463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480










481
482
483
484
485
486
487
...
545
546
547
548
549
550
551
552

553
554
555
556
557
558
559
560
...
591
592
593
594
595
596
597




































598
599
600
601
602
603
604
605
606
607
608
609
610
...
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
...
653
654
655
656
657
658
659

660

661
662
663
664
665
666
667
...
679
680
681
682
683
684
685


686
687





688
689
690



691
692
693
694
695
696
697
698
699
700
701
702
703

704
705
706
707
708
709
710
...
733
734
735
736
737
738
739
740

741
742










































































743
744
745
746
747
748
749
750
751
...
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
...
810
811
812
813
814
815
816

817
818
819

820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
...
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
 *  TODO prevent pw reads from going off the edge of
 *       the screen and fucking up all the shit
 */

#include <unistd.h>
#include <sys/random.h>
#include <sys/syscall.h>

#include <stddef.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <sodium.h>
#include <termios.h>
................................................................................
#	include <pwd.h>
#	include <stdlib.h>
#else
#   define copy(str,len)
#endif
 
enum /* constants */ {
	null = 0, true = 1, false = 0

};

#include "err.inc"

enum /* db format constants */ {
	db_pubkey_len = crypto_box_PUBLICKEYBYTES,
	db_privkey_len = crypto_box_SECRETKEYBYTES,
................................................................................
#define str_ucase "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
#define str_lcase "abcdefghijklmnopqrstuvwxyz"
#define str_num "0123456789" 
const char* reftbl = str_num str_ucase str_lcase;
const char* reftbl_lcase = str_num str_lcase;

const char* _g_binary_name;
#ifdef _CLIPBOARD
char* const*
cbd_cmds[] = {
	/* NOTE: these commands must be specified in order of
	 * most- to least-specific. more than one utility may
	 * be present on a given system, so we need to make sure
	 * the right one is called. */
	(char* const[]){"termux-clipboard-set", null},
	(char* const[]){"xsel", "-bi", null},
	/* TODO: allow command to be specified by env var */
	null
};

enum { plain_term, ansi_term, color_term } _g_term_type[3];
enum alert {a_notice, a_warn, a_debug, a_fatal = 1 << 8};
pstr alert_msg[] = {
	_p("(kpw notice)"), _p("(kpw warn)"),
	_p("(kpw debug)"),  _p("(kpw fatal)")
};
................................................................................
	if (_g_term_type[2] != plain_term) write(2,"\x1b[m ",4);
		else write(2," ",1);
	write(2,msg,strlen(msg));
	write(2,"\n",1);

	if (kind & a_fatal) exit(kind & (~a_fatal));
}















enum bad
copy(const char* str, size_t len) {
	alert(a_debug, "copying password to clipboard");
	if (geteuid() == 0) {
		/* on a sane system, what we'd do is hike up the process
		 * tree til we found a non-root user. alas, this is UNIX. */
................................................................................
}

enum bad
dbunlock(byte* priv_enc, byte* salt, byte* priv) {
	const size_t priv_sz = sizeof(key_priv);
	byte key [db_privkey_len];





	password dbpw; size_t pwlen;
	bad e = pwread(true, dbpw, &pwlen,_str("database key: "));
	if (e != ok) return e;

	alert(a_debug, "deriving secret");
	if(crypto_pwhash(key, sz(key), dbpw, pwlen, salt,
				crypto_pwhash_OPSLIMIT_INTERACTIVE,
				crypto_pwhash_MEMLIMIT_INTERACTIVE,
				crypto_pwhash_ALG_DEFAULT) != 0) {
		return bad_mem;
	}
	hexdump(key, sz(key));

	alert(a_debug, "attempting to decrypt private key");
	for (size_t i = 0; i < sz(key); ++i) {
		priv[i] = priv_enc[i] ^ key[i];
	}
	hexdump(priv, sz(key));











	return ok;
}

enum bad
dbverify(byte* salt, byte* salt_enc, byte* pub, byte* priv) {
	byte salt_dec [crypto_pwhash_SALTBYTES];
................................................................................
int
kpw(int argc, const char** argv) {
	if (argc == 0) return bad_insane;
	_g_binary_name = argv[0];

	enum genmode
		mode = lower;
	enum {usage,getpw,addpw,delpw,lspw,genpw,regen,

	      chpw,keyin,logout,createdb,rekeydb,help}
		op = getpw;

	const char* params[3];
	uint8_t param = 0;

	bool print = false,
		 clobber = false,
................................................................................

	if (op == getpw && param == 0) return emit_usage(NULL);

	if (sodium_init() < 0) 
		return bad_lib_sodium_init;

	switch(op) {




































 
		case genpw:   
		case addpw: {
			if (param == 0) return emit_usage(
					op == addpw ? " -a[p] <account> [<pw>]\n"       :
					   /* genpw */" -g[lmup] <account> [<pw len>]\n");

			if (param > 2 || param < 1) return bad_syntax;
			const char* acct = params[0],
			          * prm = (param == 2 ? params[1] : NULL);

			alert(a_debug, "opening database");
			int db = dbopen(O_RDWR);
................................................................................

			password pw; size_t pwlen;
			const char* acct_pw;
			if (op == addpw) {
				if (prm == NULL) {
					pstr prompt_l[] = { _p("- new password for "),
						{0, acct}, _p(": "), };
					char prompt[pstrsum(prompt_l, sz(prompt_l)) + 1];
					if (tty_in) pstrcoll(prompt_l, sz(prompt_l), prompt);

					bad e = pwread(!print, pw, &pwlen,
							prompt, sz(prompt) - 1);
					if (e != ok) return e;
					if (tty_in && !print) {
						password pw_conf;
						e = pwread(true, pw_conf, NULL, _str("- confirm: "));
						if (e != ok) return e;
						if (strcmp(pw,pw_conf) != 0)
							return bad_pw_match;
					}
					acct_pw = pw;
				} else acct_pw = prm;

			} else if (op == genpw) {
				unsigned long long len; 
				if (prm != NULL) {
					alert(a_debug, "converting length parameter to integer");
					bad e = katoi(10, prm, &len);
					if (e != ok) return bad_num;
................................................................................
				if (mkpw(mode, pw, len) == bad_entropy) return bad_entropy;
				if (print || !tty_out) {
					write(1, pw, len);
					if(tty_out) write(1, "\n", 1);
				}
				pwlen = len;
			}

			if (copy_pw) copy(pw, pwlen);

			alert(a_debug, "encoding database entry");
			db_sz acctlen = strlen(acct);
			byte plaintext[1 + acctlen +
			               1 + pwlen];
			plaintext[0] = acctlen;
			strncpy(plaintext + 1, acct, acctlen);
			plaintext[1 + acctlen] = pwlen;
................................................................................
			write(db, &ciphertext_len, 1);
			write(db, &ciphertext, ciphertext_len);
			
			close(db);
			break;
		}



		case delpw:{ /* kpw -d <acct> */
			if (param == 0) return emit_usage(" -d <account>\n");





			if (param != 1) return bad_syntax;
			const char* target = params[0];
			bad e;




			int db = dbopen(O_RDWR);
			if (db == -1) return bad_db_load;

			const size_t dbsz = lseek(db, 0, SEEK_END);
			lseek(db, 0, SEEK_SET);

			key_pub  pub;
			key_priv priv, priv_enc;

			byte salt     [crypto_pwhash_SALTBYTES],
			     salt_enc [crypto_box_SEALBYTES + sz(salt)];


			if ((e = dbheader_load(db, salt, salt_enc, pub, priv_enc)) != ok)
				return e;
			if ((e = dbunlock(priv_enc, salt, priv)) != ok)
				return e;
			if ((e = dbverify(salt, salt_enc, pub, priv)) != ok)
				return e;

................................................................................
				struct dbrecord rec;

				bad d;
				if((d = dbnext(db, &rec, ctlen,
					pub, priv, ctbuf, ptbuf)) != ok) return d;

				if(strncmp(target, rec.acct.ptr, rec.acct.len) == 0) {
					/* found a matching record; do not copy it */

					alert(a_debug, "found record to delete");
					found = true;










































































				} else {
					/* not our target, copy it into the buffer */
					cursor = transcribe(cursor, &ctlen, sizeof(db_sz));
					cursor = transcribe(cursor, ctbuf, sz(ctbuf));
				}

			} if (e != fail) return e;
			if (scanned == 0) return bad_db_empty;
			if (!found) return bad_index;
................................................................................
			ftruncate(db,0);
			lseek(db, 0, SEEK_SET);
			write(db, newdb, cursor-newdb);

			/* we're done here, time to close up shop */
			close(db);

			break;
		}

		case getpw:  /* kpw <acct> */
		case lspw: { /* kpw -t[p] [<prefix>] */
			const char* target;
			if (param == 1) target = params[0];
			else if (param == 0) target = NULL;
................................................................................
					if (strncmp(rec.acct.ptr,target,rec.acct.len) == 0) {
						if (print || _g_term_type[1] == plain_term) {
							write(1, rec.pw.ptr, rec.pw.len);
							if(_g_term_type[1] > plain_term)
								write(1, "\n", 1);
						}


						if (_g_term_type[1] > plain_term) {
							if (copy_pw) copy(rec.pw.ptr, rec.pw.len);
						}

						found = true;
						break;
					}
				}
			}
			if (scanned == 0) return bad_db_empty;
			if (result != fail) return result;
			else if (!found) return bad_index;
			
			break;
		}

		case createdb: { /* kpw -C [<db>] */
			alert(a_debug, "creating new database");
			if (clobber)
				alert(a_warn, "will clobber any existing database");
			/* before we open our new file, we need to generate
................................................................................
			write(db, pub, sz(pub));
			write(db, salt, sz(salt));
			write(db, priv_enc, sz(priv_enc));
			write(db, salt_enc, sz(salt_enc));
			close(db);

			alert(a_debug, "database created");
			break;
		}
	}

#	ifdef _CLIPBOARD
		if (copy_pw) {
			/* copy(buf,len); */
		}
#	endif
	
	return ok;
}

int
main (int argc, const char** argv) {
	const char* colorterm = getenv("COLORTERM");
	const char* term = getenv("TERM");







>







 







|
>







 







<
<
<
<
<
<
<
<
<
<
<
<







 







>
>
>
>
>
>
>
>
>
>
>
>
>
>







 







>
>
>
>
|
|
|

|
|
|
|
|
|
|
|

|
|
|
|
|
>
>
>
>
>
>
>
>
>
>







 







|
>
|







 







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>





|







 







|



|









|







 







>
|
>







 







>
>

|
>
>
>
>
>
|

<
>
>
>













>







 







|
>
|

>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>

|







 







|







 







>
|
|
|
>









|







 







|



<
<
<
<
<
<







21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
..
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
..
95
96
97
98
99
100
101












102
103
104
105
106
107
108
...
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
...
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
...
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
...
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
...
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
...
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
...
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753

754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
...
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
...
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
...
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
....
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078






1079
1080
1081
1082
1083
1084
1085
 *  TODO prevent pw reads from going off the edge of
 *       the screen and fucking up all the shit
 */

#include <unistd.h>
#include <sys/random.h>
#include <sys/syscall.h>
#include <sys/shm.h>
#include <stddef.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <sodium.h>
#include <termios.h>
................................................................................
#	include <pwd.h>
#	include <stdlib.h>
#else
#   define copy(str,len)
#endif
 
enum /* constants */ {
	null = 0, true = 1, false = 0,
	kpw_shm_key = 0x3CC215A,
};

#include "err.inc"

enum /* db format constants */ {
	db_pubkey_len = crypto_box_PUBLICKEYBYTES,
	db_privkey_len = crypto_box_SECRETKEYBYTES,
................................................................................
#define str_ucase "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
#define str_lcase "abcdefghijklmnopqrstuvwxyz"
#define str_num "0123456789" 
const char* reftbl = str_num str_ucase str_lcase;
const char* reftbl_lcase = str_num str_lcase;

const char* _g_binary_name;













enum { plain_term, ansi_term, color_term } _g_term_type[3];
enum alert {a_notice, a_warn, a_debug, a_fatal = 1 << 8};
pstr alert_msg[] = {
	_p("(kpw notice)"), _p("(kpw warn)"),
	_p("(kpw debug)"),  _p("(kpw fatal)")
};
................................................................................
	if (_g_term_type[2] != plain_term) write(2,"\x1b[m ",4);
		else write(2," ",1);
	write(2,msg,strlen(msg));
	write(2,"\n",1);

	if (kind & a_fatal) exit(kind & (~a_fatal));
}

#ifdef _CLIPBOARD
char* const*
cbd_cmds[] = {
	/* NOTE: these commands must be specified in order of
	 * most- to least-specific. more than one utility may
	 * be present on a given system, so we need to make sure
	 * the right one is called. */
	(char* const[]){"termux-clipboard-set", null},
	(char* const[]){"xsel", "-bi", null},
	/* TODO: allow command to be specified by env var */
	null
};


enum bad
copy(const char* str, size_t len) {
	alert(a_debug, "copying password to clipboard");
	if (geteuid() == 0) {
		/* on a sane system, what we'd do is hike up the process
		 * tree til we found a non-root user. alas, this is UNIX. */
................................................................................
}

enum bad
dbunlock(byte* priv_enc, byte* salt, byte* priv) {
	const size_t priv_sz = sizeof(key_priv);
	byte key [db_privkey_len];

	/* is the private key loaded into memory? */
	int shm = shmget(*((key_t*) salt), sizeof(key_priv), 0);
	if (shm == -1) {
		/* no key in memory - read password from stdin instead */
		password dbpw; size_t pwlen;
		bad e = pwread(true, dbpw, &pwlen,_str("database key: "));
		if (e != ok) return e;

		alert(a_debug, "deriving secret");
		if(crypto_pwhash(key, sz(key), dbpw, pwlen, salt,
					crypto_pwhash_OPSLIMIT_INTERACTIVE,
					crypto_pwhash_MEMLIMIT_INTERACTIVE,
					crypto_pwhash_ALG_DEFAULT) != 0) {
			return bad_mem;
		}
		hexdump(key, sz(key));

		alert(a_debug, "attempting to decrypt private key");
		for (size_t i = 0; i < sz(key); ++i) {
			priv[i] = priv_enc[i] ^ key[i];
		}
		hexdump(priv, sz(key));
	} else {
		/* found a key in memory; loading it into *priv */
		alert(a_notice, "using saved key");
		key_priv* saved = shmat(shm, 0, 0);
		if (saved == (void*)-1)
			return bad_shm;
		hexdump((byte*)saved, sizeof(key_priv));
		memcpy(priv, saved, sizeof(key_priv));
		shmdt(saved);
	}

	return ok;
}

enum bad
dbverify(byte* salt, byte* salt_enc, byte* pub, byte* priv) {
	byte salt_dec [crypto_pwhash_SALTBYTES];
................................................................................
int
kpw(int argc, const char** argv) {
	if (argc == 0) return bad_insane;
	_g_binary_name = argv[0];

	enum genmode
		mode = lower;
	enum {usage,getpw,addpw,delpw,lspw,
	      genpw,regen,mergedb,chpw,keyin,
		  logout,createdb,rekeydb,help}
		op = getpw;

	const char* params[3];
	uint8_t param = 0;

	bool print = false,
		 clobber = false,
................................................................................

	if (op == getpw && param == 0) return emit_usage(NULL);

	if (sodium_init() < 0) 
		return bad_lib_sodium_init;

	switch(op) {
		case logout:
		case keyin: {
			if (param != 0) return bad_syntax;

			int db = dbopen(O_RDONLY);
			key_pub  pub;
			key_priv priv, priv_enc;
			byte salt     [crypto_pwhash_SALTBYTES],
			     salt_enc [crypto_box_SEALBYTES + sz(salt)];

			bad e;
			if ((e = dbheader_load(db, salt, salt_enc, pub, priv_enc)) != ok)
				return e;
			
			key_t ipck = *((key_t*)salt);
			if (op == keyin) {
				if ((e = dbunlock(priv_enc, salt, priv)) != ok)
					return e;
				if ((e = dbverify(salt, salt_enc, pub, priv)) != ok)
					return e;
				int shm = shmget(ipck, sizeof(key_priv),
						IPC_CREAT | IPC_EXCL | 0600);
				if (shm == -1) return bad_shm_exist;

				key_priv* saved = shmat(shm, 0, 0);
				if (saved == (void*)-1) return bad_shm;
				memcpy(saved, priv, sz(priv));
				shmdt(saved);
			} else {
				int shm = shmget(ipck, sizeof(key_priv), 0);
				if (shm == -1) return bad_no_shm;
				shmctl(shm, IPC_RMID, NULL);
			}

			return ok;
		}
 
		case genpw:   
		case addpw: {
			if (param == 0) return emit_usage(
					op == addpw ? " -a[p] <account> [<pw>]\n"       :
					   /* genpw */" -g[lmusp] <account> [<pw len>]\n");

			if (param > 2 || param < 1) return bad_syntax;
			const char* acct = params[0],
			          * prm = (param == 2 ? params[1] : NULL);

			alert(a_debug, "opening database");
			int db = dbopen(O_RDWR);
................................................................................

			password pw; size_t pwlen;
			const char* acct_pw;
			if (op == addpw) {
				if (prm == NULL) {
					pstr prompt_l[] = { _p("- new password for "),
						{0, acct}, _p(": "), };
					char prompt[pstrsum(prompt_l, sz(prompt_l))];
					if (tty_in) pstrcoll(prompt_l, sz(prompt_l), prompt);

					bad e = pwread(!print, pw, &pwlen,
							prompt, sz(prompt));
					if (e != ok) return e;
					if (tty_in && !print) {
						password pw_conf;
						e = pwread(true, pw_conf, NULL, _str("- confirm: "));
						if (e != ok) return e;
						if (strcmp(pw,pw_conf) != 0)
							return bad_pw_match;
					}
					acct_pw = pw;
				} else acct_pw = prm, pwlen = strlen(prm);

			} else if (op == genpw) {
				unsigned long long len; 
				if (prm != NULL) {
					alert(a_debug, "converting length parameter to integer");
					bad e = katoi(10, prm, &len);
					if (e != ok) return bad_num;
................................................................................
				if (mkpw(mode, pw, len) == bad_entropy) return bad_entropy;
				if (print || !tty_out) {
					write(1, pw, len);
					if(tty_out) write(1, "\n", 1);
				}
				pwlen = len;
			}
#			ifdef _CLIPBOARD
				if (copy_pw) copy(pw, pwlen);
#			endif
			alert(a_debug, "encoding database entry");
			db_sz acctlen = strlen(acct);
			byte plaintext[1 + acctlen +
			               1 + pwlen];
			plaintext[0] = acctlen;
			strncpy(plaintext + 1, acct, acctlen);
			plaintext[1 + acctlen] = pwlen;
................................................................................
			write(db, &ciphertext_len, 1);
			write(db, &ciphertext, ciphertext_len);
			
			close(db);
			break;
		}

		case chpw:
		case regen:
		case delpw:{ /* kpw -d <acct> */
			if (param == 0) return emit_usage
				  (op==delpw ? " -d <account>\n"          :
				   op==regen ? " -r[lmusp] <account> [<pw len>]" :
				/* op==chpw */ " -c <account> [<new pw>]");

			if (param < 1 || param > (op == delpw ? 1 : 2))
				return bad_syntax;
			const char* target = params[0];

			const char* delta;
			if (param == 2) delta=params[1];
				else delta=NULL;

			int db = dbopen(O_RDWR);
			if (db == -1) return bad_db_load;

			const size_t dbsz = lseek(db, 0, SEEK_END);
			lseek(db, 0, SEEK_SET);

			key_pub  pub;
			key_priv priv, priv_enc;

			byte salt     [crypto_pwhash_SALTBYTES],
			     salt_enc [crypto_box_SEALBYTES + sz(salt)];

			bad e;
			if ((e = dbheader_load(db, salt, salt_enc, pub, priv_enc)) != ok)
				return e;
			if ((e = dbunlock(priv_enc, salt, priv)) != ok)
				return e;
			if ((e = dbverify(salt, salt_enc, pub, priv)) != ok)
				return e;

................................................................................
				struct dbrecord rec;

				bad d;
				if((d = dbnext(db, &rec, ctlen,
					pub, priv, ctbuf, ptbuf)) != ok) return d;

				if(strncmp(target, rec.acct.ptr, rec.acct.len) == 0) {
					/* found a matching record; determine
					 * what to do to the fucker */
					alert(a_debug, "found target record");
					found = true;
					if (op == delpw) continue;

					password pwbuf;
					const char* newpass;
					size_t pwlen; 
					if (op == regen) {
						alert(a_debug, "generating new password");
						/* generating a new password. use the default
						 * length if the user hasn't supplied one herself,
						 * or if she has, convert it to an integer. */
						if (delta == NULL) pwlen = default_pw_len; else {
							unsigned long long value;
							bad k = katoi(10, delta, &value);
							if (k != ok) return bad_num;
							pwlen = value;
						}
						bad m = mkpw(mode, pwbuf, pwlen);
						if (m != ok) return m;
						newpass = pwbuf;
					} else if (op == chpw) {
						/* the user has requested a password change. take
						 * it from the command line if available, otherwise
						 * generate a prompt and read from stdin */

						if (delta == NULL) {
							pstr prompt_l[] = { _p("- new password for "),
								{0, target}, _p(": "), };
							char prompt[pstrsum(prompt_l, sz(prompt_l))];
							if (_g_term_type[0] > plain_term)
								pstrcoll(prompt_l, sz(prompt_l), prompt);

							bad p = pwread(!print, pwbuf, &pwlen, prompt, sz(prompt));
							if (p != ok) return p;
							/* prompt again to make sure the user entered
							 * her new password correctly */
							if(!print && _g_term_type[0] > plain_term) {
								password passconf;
								p = pwread(!print, passconf, NULL, _str("confirm: "));
								if (p != ok) return p;
								if (strcmp(passconf, pwbuf) != 0)
									return bad_pw_match;
							}
							newpass = pwbuf;
						} else newpass = delta, pwlen = strlen(delta);
					} else return bad_assert;

					if (op == regen && print) {
						write(1, newpass, pwlen);
						if (_g_term_type[1] > plain_term) write(1, "\n", 1);
					}

#					ifdef _CLIPBOARD
						if (copy_pw) copy(newpass, pwlen);
#					endif

					/* new pw is pointed to by `newpass`. encrypt it
					 * and insert it into the new database image */
					byte plaintext [1 + rec.acct.len +
					                1 + pwlen        ];
					plaintext[0] = rec.acct.len;
					memcpy(plaintext + 1, rec.acct.ptr, rec.acct.len);
					plaintext[1 + rec.acct.len] = pwlen;
					memcpy(plaintext + 2 + rec.acct.len, newpass, pwlen);

					alert(a_debug, "enciphering plaintext of modified record");
					hexdump(plaintext, sz(plaintext));

					byte ciphertext [sz(plaintext) + crypto_box_SEALBYTES];
					crypto_box_seal(ciphertext, plaintext, sz(plaintext), pub);
					db_sz new_ct_len = sz(ciphertext);

					alert(a_debug, "copying ciphertext into database");
					cursor = transcribe(cursor, &new_ct_len, 1);
					cursor = transcribe(cursor, ciphertext, sz(ciphertext));
				} else {
					/* not our target, copy it into the buffer as-is */
					cursor = transcribe(cursor, &ctlen, sizeof(db_sz));
					cursor = transcribe(cursor, ctbuf, sz(ctbuf));
				}

			} if (e != fail) return e;
			if (scanned == 0) return bad_db_empty;
			if (!found) return bad_index;
................................................................................
			ftruncate(db,0);
			lseek(db, 0, SEEK_SET);
			write(db, newdb, cursor-newdb);

			/* we're done here, time to close up shop */
			close(db);

			return ok;
		}

		case getpw:  /* kpw <acct> */
		case lspw: { /* kpw -t[p] [<prefix>] */
			const char* target;
			if (param == 1) target = params[0];
			else if (param == 0) target = NULL;
................................................................................
					if (strncmp(rec.acct.ptr,target,rec.acct.len) == 0) {
						if (print || _g_term_type[1] == plain_term) {
							write(1, rec.pw.ptr, rec.pw.len);
							if(_g_term_type[1] > plain_term)
								write(1, "\n", 1);
						}

#						ifdef _CLIPBOARD
							if (_g_term_type[1] > plain_term) {
								if (copy_pw) copy(rec.pw.ptr, rec.pw.len);
							}
#						endif
						found = true;
						break;
					}
				}
			}
			if (scanned == 0) return bad_db_empty;
			if (result != fail) return result;
			else if (!found) return bad_index;
			
			return ok;
		}

		case createdb: { /* kpw -C [<db>] */
			alert(a_debug, "creating new database");
			if (clobber)
				alert(a_warn, "will clobber any existing database");
			/* before we open our new file, we need to generate
................................................................................
			write(db, pub, sz(pub));
			write(db, salt, sz(salt));
			write(db, priv_enc, sz(priv_enc));
			write(db, salt_enc, sz(salt_enc));
			close(db);

			alert(a_debug, "database created");
			return ok;
		}
	}







	return ok;
}

int
main (int argc, const char** argv) {
	const char* colorterm = getenv("COLORTERM");
	const char* term = getenv("TERM");

Modified kpw.d/optab from [c9d99a07ce] to [a42323bd02].

1
2

3

4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
C	create	op = createdb			create a new password database
R	rekey	op = rekeydb			change database passphrase

!	clobber	clobber = true			disable safety checks

a	add		op = addpw				add password to the database
g	gen		op = genpw				add account with a random password
d	del		op = delpw				delete password from the database
t	list	op = lspw				list all accounts (with passwords if -p is set)
c	chpw	op = chpw				change password in the database
r	regen	op = regen				generate new password for existing account
l	lower	mode = lower			generate lowercase password
m	mix		mode = mix				generate mix-case password
u	upper	mode = upper			generate uppercase password
s	stupid	mode = stupid			circumvent dumb pw restrictions
k	key		op = keyin				save database key in session memory
o	logout	op = logout				delete db key from session memory
n	no-copy	copy_pw = false			print password instead of copying to clipboard	_CLIPBOARD
p	print	print = true			display passwords onscreen
q	quiet	_g_alert_quiet = true	hide non-fatal reports
v	verbose	_g_debug_msgs = true	display debug reports
h	help	op = help				display help text
|
|
>
|
>
|
|
|
<
|
|
|
|
|
|
|
|
|
|
|
|
|
1
2
3
4
5
6
7
8

9
10
11
12
13
14
15
16
17
18
19
20
21
C	create		op = createdb			create a new password database
R	rekey		op = rekeydb			change database passphrase
M	merge		op = mergedb			merge in another database
!	clobber		clobber = true			disable safety checks
t	list		op = lspw				list all accounts (with passwords if -p is set)
a	add			op = addpw				add password to the database
g	gen			op = genpw				add account with a random password
d	del			op = delpw				delete password from the database

c	chg			op = chpw				change password in the database
r	regen		op = regen				generate new password for existing account
l	lower		mode = lower			generate lowercase password
m	mix			mode = mix				generate mix-case password
u	upper		mode = upper			generate uppercase password
s	stupid-mode	mode = stupid			circumvent dumb pw restrictions
k	install-key	op = keyin				install database key in session memory
o	logout		op = logout				delete db key from session memory
n	no-copy		copy_pw = false			print password instead of copying to clipboard	_CLIPBOARD
p	print-pw	print = true			display passwords onscreen
q	quiet		_g_alert_quiet = true	hide non-fatal reports
v	verbose		_g_debug_msgs = true	display debug reports
h	help		op = help				display help text