util  Check-in [178e52ca55]

Overview
Comment:fix missing return that was breaking build under clang, add ability to delete records
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA3-256: 178e52ca5577a2a6f7e7fed5b68b4b886e2c02bc878a7c4918c4e0016c78f0e8
User & Date: lexi on 2019-08-15 23:49:54
Other Links: manifest | tags
Context
2019-08-16
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
12:31
my monster liiiiives check-in: 5089ec59d3 user: lexi tags: trunk
Changes

Modified kpw.d/errtab from [763d2287dd] to [ce1ef8cc06].

     9      9   copy		could not copy password
    10     10   pipe		could not create pipe
    11     11   insane		your environment is not sane
    12     12   index		no such entry in database
    13     13   db_create	database could not be created
    14     14   db_load		database could not be read
    15     15   db_corrupt	database corrupt
           16  +db_empty	no records in database				notice
    16     17   cancel		user canceled operation				notice
    17     18   pw			invalid password
    18     19   pw_match	passwords do not match
    19     20   usage		usage displayed to user				debug	64
    20     21   lib				unspecified library error		fatal	128
    21     22   lib_sodium_init	could not initialize libsodium

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

    80     80   typedef enum {
    81     81   	tbl_ok = ok, tbl_error = fail
    82     82   } tbl_error_type;
    83     83   typedef unsigned char tbl_word_type;
    84     84   #include "clib/tbl.c"
    85     85   
    86     86   #include "opt.inc"
           87  +
           88  +typedef uint8_t key_priv [db_privkey_len];
           89  +typedef uint8_t key_pub [db_pubkey_len];
           90  +typedef uint8_t db_sz;
           91  +typedef uint8_t byte;
    87     92   
    88     93   #define str_ucase "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
    89     94   #define str_lcase "abcdefghijklmnopqrstuvwxyz"
    90     95   #define str_num "0123456789" 
    91     96   const char* reftbl = str_num str_ucase str_lcase;
    92     97   const char* reftbl_lcase = str_num str_lcase;
    93     98   
................................................................................
   224    229   	}
   225    230   
   226    231   	buf[len] = '\n';
   227    232   
   228    233   	return ok;
   229    234   }
   230    235   
   231         -struct entry {
   232         -	pstr account;
   233         -	pstr pw;
   234         -};
          236  +void
          237  +bytedump(byte* bytes, size_t sz) {
          238  +	for (size_t i = 0; i < sz; ++i) {
          239  +		char tpl[] ="\x1b[35m \x1b[m";
          240  +		char* c = tpl + 5;
          241  +		*c = bytes[i];
          242  +		if (*c < ' ' || *c > '~') *c='.', write(2, tpl, sz(tpl));
          243  +		else write(2, c, 1);
          244  +	}
          245  +}
          246  +
          247  +void
          248  +hexdump(byte* bytes, size_t sz) {
          249  +	if(!_g_debug_msgs) return;
          250  +	alert(a_debug, "printing hex dump");
          251  +	byte* st = bytes;
          252  +	write(2, _str("\t\x1b[94m"));
          253  +	for (size_t i = 0; i < sz; ++i) {
          254  +		char hex[5] = "    ";
          255  +		kitoa(16, bytes[i], hex, hex + 2, NULL, true);
          256  +		write(2, hex, 4);
          257  +		if(!((i+1)%8)) {
          258  +			write(2, _str("\x1b[;1m│\x1b[m "));
          259  +			bytedump(st, 8);
          260  +			write(2, "\n\t\x1b[94m", (i == sz - 1 ? 1 : 7));
          261  +			st += 8;
          262  +		} else if (i == sz - 1) {
          263  +			write(2, _str("\x1b[;1m│\x1b[m "));
          264  +			bytedump(st, (bytes + sz) - st);
          265  +			write(2, _str("\n\x1b[m"));
          266  +		}
          267  +	}
          268  +}
          269  +
          270  +struct dbrecord { pstr acct; pstr pw; };
          271  +
          272  +enum bad
          273  +dbtell(int db, db_sz* ciphlen, db_sz* plainlen){
          274  +	if (read(db, ciphlen, 1) != 1)
          275  +		return fail;
          276  +	*plainlen = *ciphlen - crypto_box_SEALBYTES;
          277  +	return ok;
          278  +}
          279  +
          280  +enum bad
          281  +dbnext(int db, struct dbrecord* rec, db_sz acctlen,
          282  +		byte* pub, byte* priv,
          283  +		byte ciphertext[static acctlen],
          284  +		byte plaintext[static acctlen - crypto_box_SEALBYTES]) {
          285  +	db_sz plaintext_sz = acctlen - crypto_box_SEALBYTES;
          286  +
          287  +	if (read(db, ciphertext, acctlen) != acctlen)
          288  +		return bad_db_corrupt;
          289  +	alert(a_debug, "scanned record");
          290  +	hexdump(ciphertext, acctlen);
          291  +
          292  +	if(crypto_box_seal_open(plaintext, ciphertext, acctlen, pub, priv) != 0)
          293  +		return bad_db_corrupt;
          294  +
          295  +	alert(a_debug, "record deciphered");
          296  +	hexdump(plaintext, plaintext_sz);
          297  +
          298  +	db_sz record_name_len = plaintext[0],
          299  +	record_pw_len = plaintext[record_name_len + 1];
          300  +
          301  +	rec -> acct.len = record_name_len;
          302  +	rec -> acct.ptr = plaintext + 1;
          303  +	rec -> pw.len = record_pw_len;
          304  +	rec -> pw.ptr = plaintext + record_name_len + 2;
          305  +
          306  +	return ok;
          307  +}
          308  +
          309  +enum bad
          310  +dbappend(struct dbrecord rec) {
          311  +	return ok;
          312  +}
   235    313   
   236    314   enum term_clear {term_clear_line,
   237    315                    term_clear_screen};
   238    316   
   239    317   void term_clear(int tty, enum term_clear behavior) {
   240    318   	switch(behavior) {
   241    319   		case term_clear_line:   write(tty,"\r\x1b[2K",5); break;
   242    320   		case term_clear_screen: write(tty,"\r\x1b[3J",5); break;
   243    321   	}
   244    322   }
   245    323   void term_bell(int tty) {
   246    324   	write(tty,"\a",1);
   247    325   }
   248         -
          326  +#include<stdio.h>
   249    327   typedef char password[kpw_db_pw_max + 1];
   250    328   bad pwread(bool obscure, char* dest, size_t* out_len, const char* prompt, const size_t plen) {
   251    329   	if (isatty(0)) {
   252    330   		int tty = 1;
   253    331   		if (!isatty(tty)) tty = open("/dev/tty", O_WRONLY);
   254    332   		if (tty == -1) return bad_insane;
   255    333   
................................................................................
   309    387   
   310    388   		if (tty != 1) close(tty);
   311    389   	} else {
   312    390   		alert(a_warn, "reading pw from standard input");
   313    391   		ssize_t ct = read(0, dest, kpw_db_pw_max);
   314    392   		dest[ct] = 0;
   315    393   	}
          394  +	return ok;
   316    395   }
   317    396   
   318    397   int
   319    398   dbopen(int flags) {
   320    399   	const char* dbpath = getenv("kpw_db");
   321    400   	int db;
   322    401   	if (dbpath == NULL) {
................................................................................
   344    423   	} else {
   345    424   		db = open(dbpath, flags, 0600);
   346    425   	}
   347    426   
   348    427   	return db;
   349    428   }
   350    429   
   351         -void bytedump(uint8_t* bytes, size_t sz) {
   352         -	for (size_t i = 0; i < sz; ++i) {
   353         -		char tpl[] ="\x1b[35m \x1b[m";
   354         -		char* c = tpl + 5;
   355         -		*c = bytes[i];
   356         -		if (*c < ' ' || *c > '~') *c='.', write(2, tpl, sz(tpl));
   357         -		else write(2, c, 1);
   358         -	}
   359         -}
   360         -
   361         -void hexdump(uint8_t* bytes, size_t sz) {
   362         -	if(!_g_debug_msgs) return;
   363         -	alert(a_debug, "printing hex dump");
   364         -	uint8_t* st = bytes;
   365         -	for (size_t i = 0; i < sz; ++i) {
   366         -		char hex[5] = "    ";
   367         -		kitoa(16, bytes[i], hex, hex + 2, NULL, true);
   368         -		write(2, hex, 4);
   369         -		if(!((i+1)%8)) {
   370         -			write(2, _str("│ "));
   371         -			bytedump(st, 8);
   372         -			write(2, "\n", 1);
   373         -			st += 8;
   374         -		} else if (i == sz - 1) {
   375         -			write(2, _str("│ "));
   376         -			bytedump(st, (bytes + sz) - st);
   377         -			write(2, "\n", 1);
   378         -		}
   379         -	}
          430  +enum bad
          431  +dbheader_load (int db, byte* salt, byte* salt_enc, byte* pub, byte* priv_enc) {
          432  +	const size_t salt_sz = crypto_pwhash_SALTBYTES,
          433  +	             salt_enc_sz = crypto_box_SEALBYTES + salt_sz,
          434  +				 pub_sz = sizeof(key_pub),
          435  +				 priv_sz = sizeof(key_priv);
          436  +
          437  +	alert(a_debug, "loading public key");
          438  +	ssize_t sr = read(db, pub, pub_sz);
          439  +	if (sr != pub_sz) return bad_db_corrupt;
          440  +	hexdump(pub, pub_sz);
          441  +
          442  +	alert(a_debug, "loading password salt");
          443  +	sr = read(db, salt, salt_sz);
          444  +	if (sr != salt_sz) return bad_db_corrupt;
          445  +	hexdump(salt, salt_sz);
          446  +
          447  +	alert(a_debug, "loading encrypted private key");
          448  +	read(db, priv_enc, priv_sz);
          449  +	hexdump(priv_enc, priv_sz);
          450  +
          451  +	alert(a_debug, "loading verification hash");
          452  +	read(db, salt_enc, salt_enc_sz);
          453  +	hexdump(salt_enc, salt_enc_sz);
          454  +
          455  +	return ok;
   380    456   }
   381    457   
   382    458   enum bad
   383         -dbdecrypt(int db, uint8_t* pubkey, uint8_t* privkey) {
          459  +dbunlock(byte* priv_enc, byte* salt, byte* priv) {
          460  +	const size_t priv_sz = sizeof(key_priv);
          461  +	byte key [db_privkey_len];
          462  +
   384    463   	password dbpw; size_t pwlen;
   385    464   	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));
          465  +	if (e != ok) return e;
   404    466   
   405    467   	alert(a_debug, "deriving secret");
   406    468   	if(crypto_pwhash(key, sz(key), dbpw, pwlen, salt,
   407    469   				crypto_pwhash_OPSLIMIT_INTERACTIVE,
   408    470   				crypto_pwhash_MEMLIMIT_INTERACTIVE,
   409    471   				crypto_pwhash_ALG_DEFAULT) != 0) {
   410    472   		return bad_mem;
   411    473   	}
   412    474   	hexdump(key, sz(key));
   413    475   
   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");
          476  +	alert(a_debug, "attempting to decrypt private key");
   419    477   	for (size_t i = 0; i < sz(key); ++i) {
   420    478   		priv[i] = priv_enc[i] ^ key[i];
   421    479   	}
   422         -	hexdump(priv, sz(priv));
          480  +	hexdump(priv, sz(key));
   423    481   
   424         -	alert(a_debug, "loading verification hash");
   425         -	read(db, salt_enc, sz(salt_enc));
   426         -	hexdump(salt_enc, sz(salt_enc));
          482  +	return ok;
          483  +}
          484  +
          485  +enum bad
          486  +dbverify(byte* salt, byte* salt_enc, byte* pub, byte* priv) {
          487  +	byte salt_dec [crypto_pwhash_SALTBYTES];
   427    488   
   428    489   	alert(a_debug, "decrypting verification hash");
   429    490   	int r = crypto_box_seal_open(salt_dec, salt_enc,
   430         -			sz(salt_enc), pub, priv);
          491  +			sz(salt_dec) + crypto_box_SEALBYTES, pub, priv);
   431    492   	if (r != 0) return bad_pw;
   432    493   	hexdump(salt_dec, sz(salt_dec));
   433    494   
   434         -	if (memcmp(salt,salt_dec,sz(salt)) != 0) return bad_db_corrupt;
          495  +	if (memcmp(salt,salt_dec,sz(salt_dec)) != 0) return bad_db_corrupt;
          496  +	else return ok;
          497  +}
          498  +
          499  +enum bad
          500  +dbdecrypt(int db, byte* pubkey, byte* privkey) {
          501  +	byte salt     [crypto_pwhash_SALTBYTES],
          502  +	     priv_enc [db_privkey_len],
          503  +	     priv     [db_privkey_len],
          504  +	     pub      [db_pubkey_len];
          505  +	byte salt_enc [crypto_box_SEALBYTES + sz(salt)];
          506  +	bad e;
          507  +
          508  +	if ((e = dbheader_load(db, salt, salt_enc, pub, priv_enc)) != ok)
          509  +		return e;
          510  +	if ((e = dbunlock(priv_enc, salt, priv)) != ok)
          511  +		return e;
          512  +	if ((e = dbverify(salt, salt_enc, pub, priv)) != ok)
          513  +		return e;
   435    514   
   436    515   	/* TODO refactor to avoid unnecessary memcpy */
   437    516   	memcpy(privkey, priv, sz(priv));
   438    517   	memcpy(pubkey, pub, sz(pub));
   439    518   	
   440    519   	return ok;
   441    520   }
   442    521   
   443         -#include<stdio.h>
   444         -void bright(int fd, const char* str, size_t len) {
          522  +void
          523  +bright(int fd, const char* str, size_t len) {
   445    524   	if (_g_term_type[fd] >= ansi_term) write(fd, _str("\x1b[1m"));
   446    525   	write(fd, str, len);
   447    526   	if (_g_term_type[fd] >= ansi_term) write(fd, _str("\x1b[21m"));
   448    527   }
          528  +
          529  +void*
          530  +transcribe(void* dest, void* src, size_t sz) {
          531  +	memcpy(dest, src, sz); return dest + sz;
          532  +}
          533  +
          534  +enum bad
          535  +emit_usage(const char* text) {
          536  +	say("\x1b[1musage:\x1b[m ");
          537  +	write(2, _g_binary_name, strlen(_g_binary_name));
          538  +	if (text == NULL) {
          539  +		write(2, kpw_optstr, sz(kpw_optstr));
          540  +		write(2, kpw_usage,  sz(kpw_usage));
          541  +	} else write(2, text, strlen(text));
          542  +	return bad_usage;
          543  +}
   449    544   
   450    545   int
   451    546   kpw(int argc, const char** argv) {
   452    547   	if (argc == 0) return bad_insane;
   453    548   	_g_binary_name = argv[0];
   454    549   
   455    550   	enum genmode
   456    551   		mode = lower;
   457         -	enum {usage,getpw,addpw,delpw,lspw,genpw,
   458         -	      chpw,keyin,logout,createdb,rekeydb}
          552  +	enum {usage,getpw,addpw,delpw,lspw,genpw,regen,
          553  +	      chpw,keyin,logout,createdb,rekeydb,help}
   459    554   		op = getpw;
   460    555   
   461    556   	const char* params[3];
   462    557   	uint8_t param = 0;
   463    558   
   464    559   	bool print = false,
   465    560   		 clobber = false,
................................................................................
   490    585   			}
   491    586   		} else {
   492    587   			if (param > sz(params)) return bad_syntax;
   493    588   			params[param++] = *arg;
   494    589   		}
   495    590   	}
   496    591   
   497         -	if (op == getpw && param == 0) { 
   498         -		size_t namelen = strlen(argv[0]);
   499         -		say("\x1b[1musage:\x1b[m ");
   500         -		write(2, argv[0], namelen);
   501         -		write(2, kpw_optstr, sz(kpw_optstr));
   502         -		write(2, kpw_usage,  sz(kpw_usage));
   503         -		return bad_usage;
   504         -	}
          592  +	if (op == getpw && param == 0) return emit_usage(NULL);
   505    593   
   506    594   	if (sodium_init() < 0) 
   507    595   		return bad_lib_sodium_init;
   508    596   
   509    597   	switch(op) {
   510    598    
   511         -		case genpw:   /* kpw -g[lmu] <acct> [<len>] */
   512         -		case addpw: { /* kpw -a      <acct> [<pw>] */
          599  +		case genpw:   
          600  +		case addpw: {
          601  +			if (param == 0) return emit_usage(
          602  +					op == addpw ? " -a[p] <account> [<pw>]\n"       :
          603  +					   /* genpw */" -g[lmup] <account> [<pw len>]\n");
          604  +
   513    605   			if (param > 2 || param < 1) return bad_syntax;
   514    606   			const char* acct = params[0],
   515    607   			          * prm = (param == 2 ? params[1] : NULL);
   516    608   
   517    609   			alert(a_debug, "opening database");
   518    610   			int db = dbopen(O_RDWR);
   519    611   			if (db == -1) return bad_db_load;
   520    612   			alert(a_debug, "reading in public key");
   521         -			uint8_t key [db_pubkey_len];
          613  +			byte key [db_pubkey_len];
   522    614   			ssize_t e = read(db, key, sz(key));
   523    615   			if(e < sz(key)) return bad_db_corrupt;
   524    616   
   525    617   			lseek(db, 0, SEEK_END);
   526    618   			bool tty_in = isatty(0),
   527    619   				 tty_out = isatty(1);
   528    620   
................................................................................
   563    655   					write(1, pw, len);
   564    656   					if(tty_out) write(1, "\n", 1);
   565    657   				}
   566    658   				pwlen = len;
   567    659   			}
   568    660   			if (copy_pw) copy(pw, pwlen);
   569    661   			alert(a_debug, "encoding database entry");
   570         -			uint8_t acctlen = strlen(acct);
   571         -			uint8_t plaintext[1 + acctlen +
   572         -			                  1 + pwlen];
          662  +			db_sz acctlen = strlen(acct);
          663  +			byte plaintext[1 + acctlen +
          664  +			               1 + pwlen];
   573    665   			plaintext[0] = acctlen;
   574    666   			strncpy(plaintext + 1, acct, acctlen);
   575    667   			plaintext[1 + acctlen] = pwlen;
   576    668   			strncpy(plaintext + acctlen + 2, pw, pwlen);
   577    669   			hexdump(plaintext, sz(plaintext));
   578    670   
   579    671   			alert(a_debug, "enciphering database entry");
   580    672   
   581         -			uint8_t ciphertext[sz(plaintext) + crypto_box_SEALBYTES];
          673  +			byte ciphertext[sz(plaintext) + crypto_box_SEALBYTES];
   582    674   			crypto_box_seal(ciphertext, plaintext, sz(plaintext), key);
   583    675   			hexdump(ciphertext, sz(ciphertext));
   584    676   
   585    677   			alert(a_debug, "writing ciphertext to db");
   586         -			uint8_t ciphertext_len = sz(ciphertext);
          678  +			db_sz ciphertext_len = sz(ciphertext);
   587    679   			write(db, &ciphertext_len, 1);
   588    680   			write(db, &ciphertext, ciphertext_len);
   589    681   			
   590    682   			close(db);
   591    683   			break;
   592    684   		}
   593    685   
   594    686   		case delpw:{ /* kpw -d <acct> */
          687  +			if (param == 0) return emit_usage(" -d <account>\n");
          688  +			if (param != 1) return bad_syntax;
          689  +			const char* target = params[0];
          690  +			bad e;
          691  +
          692  +			int db = dbopen(O_RDWR);
          693  +			if (db == -1) return bad_db_load;
          694  +
          695  +			const size_t dbsz = lseek(db, 0, SEEK_END);
          696  +			lseek(db, 0, SEEK_SET);
          697  +
          698  +			key_pub  pub;
          699  +			key_priv priv, priv_enc;
          700  +
          701  +			byte salt     [crypto_pwhash_SALTBYTES],
          702  +			     salt_enc [crypto_box_SEALBYTES + sz(salt)];
          703  +
          704  +			if ((e = dbheader_load(db, salt, salt_enc, pub, priv_enc)) != ok)
          705  +				return e;
          706  +			if ((e = dbunlock(priv_enc, salt, priv)) != ok)
          707  +				return e;
          708  +			if ((e = dbverify(salt, salt_enc, pub, priv)) != ok)
          709  +				return e;
          710  +
          711  +			byte newdb [dbsz];
          712  +			byte* cursor = newdb;
          713  +
          714  +			/* header is unchanged, so we copy it back out as is */
          715  +			cursor = transcribe(cursor, pub, sz(pub));
          716  +			cursor = transcribe(cursor, salt, sz(salt));
          717  +			cursor = transcribe(cursor, priv_enc, sz(priv_enc));
          718  +			cursor = transcribe(cursor, salt_enc, sz(salt_enc));
          719  +
          720  +			/* now we iterate through each record, decrypting them
          721  +			 * with the keys we've obtained to compare the name
          722  +			 * against the `target` specified by the user. all
          723  +			 * records that do not match are written back to newdb,
          724  +			 * while records that match are skipped. */
          725  +			db_sz ctlen, ptlen; bool found = false; size_t scanned = 0;
          726  +			while((e = dbtell(db, &ctlen, &ptlen)) == ok) {
          727  +				++ scanned;
          728  +				/* dbtell gives us the length of the buffers we
          729  +				 * need to allocate, allowing us to keep all the
          730  +				 * work on the stack and avoiding any malloc bs */
          731  +				byte ctbuf [ctlen],
          732  +				     ptbuf [ptlen];
          733  +				struct dbrecord rec;
          734  +
          735  +				bad d;
          736  +				if((d = dbnext(db, &rec, ctlen,
          737  +					pub, priv, ctbuf, ptbuf)) != ok) return d;
          738  +
          739  +				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");
          742  +					found = true;
          743  +				} else {
          744  +					/* not our target, copy it into the buffer */
          745  +					cursor = transcribe(cursor, &ctlen, sizeof(db_sz));
          746  +					cursor = transcribe(cursor, ctbuf, sz(ctbuf));
          747  +				}
          748  +
          749  +			} if (e != fail) return e;
          750  +			if (scanned == 0) return bad_db_empty;
          751  +			if (!found) return bad_index;
          752  +			/* newdb should now be an image of the database without
          753  +			 * the offending record. we can now copy it back out to
          754  +			 * disk, truncating the file to start from scratch. */
          755  +			alert(a_debug, "writing modified db back out to disk");
          756  +			ftruncate(db,0);
          757  +			lseek(db, 0, SEEK_SET);
          758  +			write(db, newdb, cursor-newdb);
          759  +
          760  +			/* we're done here, time to close up shop */
          761  +			close(db);
          762  +
   595    763   			break;
   596    764   		}
   597    765   
   598    766   		case getpw:  /* kpw <acct> */
   599    767   		case lspw: { /* kpw -t[p] [<prefix>] */
   600         -			alert(a_debug, "opening database for reading");
   601         -		   	int db = dbopen(O_RDONLY);
   602         -		   	if (db == -1) return bad_db_load;
   603         -
   604    768   			const char* target;
   605    769   			if (param == 1) target = params[0];
   606    770   			else if (param == 0) target = NULL;
   607    771   			else return bad_syntax;
   608    772   
   609         -			uint8_t priv [db_privkey_len],
   610         -					pub  [db_pubkey_len];
          773  +			alert(a_debug, "opening database for reading");
          774  +		   	int db = dbopen(O_RDONLY);
          775  +		   	if (db == -1) return bad_db_load;
          776  +
          777  +			key_pub pub;
          778  +			key_priv priv;
   611    779   
   612    780   			/* try to decrypt db */ { 
   613    781   				bad e = dbdecrypt(db,pub,priv);
   614    782   				if (e != ok) return e;
   615    783   				/* TODO allow multiple tries */
   616    784   			}
   617    785   
   618         -			/* cursor should now be positioned
   619         -			 * on first record */
          786  +			/* cursor should be positioned on first
          787  +			 * record if we've made it this far */
   620    788   			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};
          789  +			struct dbrecord rec;
          790  +			bool found = (op == lspw);
          791  +			enum bad result;
          792  +			db_sz ciphlen, plainlen;
          793  +			size_t scanned = 0;
          794  +			while ((result = dbtell(db, &ciphlen, &plainlen)) == ok) {
          795  +				++ scanned;
          796  +				byte ctbuf[ciphlen], ptbuf[plainlen];
          797  +				if ((result = dbnext(db, &rec, ciphlen,
          798  +								pub, priv,
          799  +								ctbuf, ptbuf)) != ok) 
          800  +					break;
   644    801   
   645    802   				if(op == lspw) {
   646         -					bright(1, record_name.ptr, record_name.len);
          803  +					bright(1, rec.acct.ptr, rec.acct.len);
   647    804   					if (print || !isatty(1)) {
   648    805   						write(1, ": ", 2);
   649         -						write(1, record_pw.ptr, record_pw.len);
          806  +						write(1, rec.pw.ptr, rec.pw.len);
   650    807   					}
   651    808   					write(1, "\n", 1);
   652    809   				} else if (op == getpw) {
   653         -					if (strncmp(record_name.ptr,target,record_name.len) == 0) {
          810  +					if (strncmp(rec.acct.ptr,target,rec.acct.len) == 0) {
   654    811   						if (print || _g_term_type[1] == plain_term) {
   655         -							write(1, record_pw.ptr, record_pw.len);
          812  +							write(1, rec.pw.ptr, rec.pw.len);
   656    813   							if(_g_term_type[1] > plain_term)
   657    814   								write(1, "\n", 1);
   658    815   						}
   659    816   
   660    817   						if (_g_term_type[1] > plain_term) {
   661         -							if (copy_pw) copy(record_pw.ptr, record_pw.len);
          818  +							if (copy_pw) copy(rec.pw.ptr, rec.pw.len);
   662    819   						}
   663         -						goto done_reading;
          820  +						found = true;
          821  +						break;
   664    822   					}
   665    823   				}
   666         -
   667         -				goto read_rec;
   668    824   			}
   669         -			return bad_index;
   670         -
   671         -			done_reading: break;
          825  +			if (scanned == 0) return bad_db_empty;
          826  +			if (result != fail) return result;
          827  +			else if (!found) return bad_index;
          828  +			
          829  +			break;
   672    830   		}
   673    831   
   674    832   		case createdb: { /* kpw -C [<db>] */
   675    833   			alert(a_debug, "creating new database");
   676    834   			if (clobber)
   677    835   				alert(a_warn, "will clobber any existing database");
   678    836   			/* before we open our new file, we need to generate
   679    837   			 * our keypairs.  the private key will be encrypted
   680    838   			 * with a blake2 hash of a user-supplied passphrase 
   681    839   			 * and stored in the database after the unencrypted
   682    840   			 * public key.
   683    841   			 */
   684         -			uint8_t pub [db_pubkey_len],
   685         -			        priv[db_privkey_len];
   686         -			uint8_t priv_enc[sz(priv)];
          842  +			byte pub [db_pubkey_len],
          843  +			     priv[db_privkey_len];
          844  +			byte priv_enc[sz(priv)];
   687    845   
   688    846   			alert(a_debug, "generating keypair");
   689    847   			crypto_box_keypair(pub, priv);
   690    848   
   691    849   			/* kpw   works  very   differently  compared   to
   692    850   			 * most  password  managers. it  uses  public-key
   693    851   			 * encryption so that new  passwords can be saved
................................................................................
   707    865   				e = pwread(!print, dbpw_conf, NULL, _str("- confirm: "));
   708    866   				if (e != ok) return e;
   709    867   
   710    868   				if(strcmp(dbpw,dbpw_conf) != 0)
   711    869   					return bad_pw_match;
   712    870   			}
   713    871   
   714         -			uint8_t salt[crypto_pwhash_SALTBYTES],
   715         -			         key[db_privkey_len];
   716         -			uint8_t salt_enc[crypto_box_SEALBYTES + sz(salt)];
          872  +			byte salt [crypto_pwhash_SALTBYTES],
          873  +			     key[db_privkey_len];
          874  +			byte salt_enc[crypto_box_SEALBYTES + sz(salt)];
   717    875   
   718    876   			alert(a_debug, "generating salt");
   719    877   			if (syscall(SYS_getrandom, salt, sz(salt), 0) == -1)
   720    878   				return bad_entropy;
   721    879   			hexdump(salt, sz(salt));
   722    880   
   723    881   			alert(a_debug, "hashing database keyphrase");

Modified kpw.d/optab from [652ae10981] to [c9d99a07ce].

     1      1   C	create	op = createdb			create a new password database
     2      2   R	rekey	op = rekeydb			change database passphrase
            3  +!	clobber	clobber = true			disable safety checks
     3      4   a	add		op = addpw				add password to the database
     4      5   g	gen		op = genpw				add account with a random password
     5      6   d	del		op = delpw				delete password from the database
     6      7   t	list	op = lspw				list all accounts (with passwords if -p is set)
     7      8   c	chpw	op = chpw				change password in the database
            9  +r	regen	op = regen				generate new password for existing account
     8     10   l	lower	mode = lower			generate lowercase password
     9     11   m	mix		mode = mix				generate mix-case password
    10     12   u	upper	mode = upper			generate uppercase password
    11     13   s	stupid	mode = stupid			circumvent dumb pw restrictions
    12     14   k	key		op = keyin				save database key in session memory
    13     15   o	logout	op = logout				delete db key from session memory
    14     16   n	no-copy	copy_pw = false			print password instead of copying to clipboard	_CLIPBOARD
    15     17   p	print	print = true			display passwords onscreen
    16     18   q	quiet	_g_alert_quiet = true	hide non-fatal reports
    17     19   v	verbose	_g_debug_msgs = true	display debug reports
    18         -!	clobber	clobber = true			disable safety checks
           20  +h	help	op = help				display help text