util  Diff

Differences From Artifact [22f71ce9d0]:

To Artifact [7a6fbd2214]:


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