util  Check-in [a3ccd193d5]

Overview
Comment:add kpw source
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA3-256: a3ccd193d5e01d37bba34a8e88b4a0d3975665a214ddce0a3e67ec9e63b90189
User & Date: lexi on 2019-08-15 05:45:12
Other Links: manifest | tags
Context
2019-08-15
08:34
updates check-in: c2ffe127f3 user: lexi tags: trunk
05:45
add kpw source check-in: a3ccd193d5 user: lexi tags: trunk
2019-08-13
02:43
fixes for script compat check-in: 2592d3e6e0 user: lexi tags: trunk
Changes

Modified clib/compose.c from [d981619adf] to [5d94fe752b].

    18     18   
    19     19   #include <string.h>
    20     20   
    21     21   typedef struct pstr { size_t len; union {
    22     22   	const char* ptr;
    23     23   	char* mutptr;
    24     24   }; } pstr;
    25         -#define _p(str) { sizeof str - 1, str }
           25  +#define _p(str) { sizeof (str) - 1, (str) }
    26     26   
    27     27   typedef struct safestr {
    28     28   	union {
    29     29   		const char* ptr;
    30     30   		char* mutptr;
    31     31   	}; // im sorry okay
    32     32   #	ifndef k_static
................................................................................
    55     55   	for (size_t i = 0; i < ct; ++i) {
    56     56   		if (lst[i].len == 0) {
    57     57   			if (lst[i].ptr == NULL) continue;
    58     58   			lst[i].len = strlen(lst[i].ptr);
    59     59   		}
    60     60   		len += lst[i].len;
    61     61   	}
           62  +	return len;
    62     63   })
    63     64   
    64     65   char* pstrcoll(pstr* lst, size_t ct, char* ptr) fn({
    65     66   	for (size_t i = 0; i < ct; ++i) {
    66     67   		if (lst[i].len == 0) continue;
    67     68   		strncpy(ptr,lst[i].ptr,lst[i].len);
    68     69   		ptr += lst[i].len;

Added kpw.d/db.md version [f52a921e08].

            1  +# kpw db format
            2  +
            3  +kpw uses a simple binary database format. it consists of a number of values of constant size, followed by a series of encrypted records.
            4  +
            5  + 1. public key
            6  + 2. password salt
            7  + 3. encrypt(password, private key)
            8  + 4. encrypt(private key, password salt) [for pw verification]
            9  + 5. record *
           10  +
           11  +each record takes the form of
           12  +
           13  + 1. account name length (1 byte)
           14  + 2. account name
           15  + 3. password length (4 bytes)
           16  + 4. password
           17  +
           18  +records are added simply by encrypting them with the public key and appending them to the end of the file. thus, adding a new password does not require the decryption password.

Added kpw.d/errtab version [203a9ab500].

            1  +ok			completed successfully				debug	0
            2  +fail		unknown error
            3  +mem			out of memory
            4  +user		kpw started as wrong user
            5  +option		unrecognized option passed
            6  +syntax		invalid invocation syntax
            7  +entropy		could not acquire entropy
            8  +copy		could not copy password
            9  +insane		your environment is not sane
           10  +db_create	database could not be created
           11  +cancel		user canceled operation				notice
           12  +pw			invalid password
           13  +pw_match	passwords do not match
           14  +usage		usage displayed to user				debug	64
           15  +lib				unspecified library error		fatal	128
           16  +lib_sodium_init	could not initialize libsodium	fatal

Added kpw.d/errtab.awk version [ec5262a4e4].

            1  +BEGIN {
            2  +	if (emit == "enum") {
            3  +		print "typedef enum bad {"
            4  +	} else if (emit == "msg") {
            5  +		print "#define kpw_emit_error_switch \\"
            6  +		code = 0
            7  +	}
            8  +}
            9  +
           10  +function name(n) {
           11  +	if (NR > 2) {
           12  +		return "bad_" n
           13  +	} else {
           14  +		return n
           15  +	}
           16  +}
           17  +
           18  +emit == "enum" {
           19  +	print "\t" name($1) ","
           20  +}
           21  +
           22  +emit == "msg" {
           23  +	if (NF >= 4) {
           24  +		retval = $4
           25  +		code = retval + 1
           26  +	} else {
           27  +		retval = code++
           28  +	}
           29  +
           30  +	if (NF >= 3) {
           31  +		level = "a_" $3
           32  +		if (level == "a_fatal") {
           33  +			level = level " | " retval
           34  +		}
           35  +	} else {
           36  +		level = "a_fatal | " retval
           37  +	}
           38  +
           39  +	print "\t" \
           40  +		  "case " name($1)":" \
           41  +		  "level=" level ";" \
           42  +		  "msg=\""$2"\";" \
           43  +		  "rv=" retval";" \
           44  +		  "break; \\"
           45  +}
           46  +
           47  +END {
           48  +	if (emit == "enum") {
           49  +		print "} bad;"
           50  +	} else if (emit == "msg") {
           51  +		print ""
           52  +	}
           53  +}

Added kpw.d/kpw.c version [f865f78c63].

            1  +/* [ʞ] kpw.c - password manager
            2  + *  ↳ derivative of mkpw.c
            3  + *  ~ lexi hale <lexi@hale.su>
            4  + *  © AGPLv3
            5  + *  $ cc -O4 kpw.c -okpw [-D_CLIPBOARD]
            6  + *    - D_CLIPBOARD enables kpw to automatically
            7  + *      copy passwords to the clipboard. it does
            8  + *      this by attempting to execute a sequence
            9  + *      of binaries, and then writing the password
           10  + *      to STDIN of the binary that succeeds.
           11  + *  ? generates passwords
           12  + *  → kpw is unlikely to be portable to non-POSIX
           13  + *    systems, but should run fine on Linux as well
           14  + *    as BSDs with getrandom() support.
           15  + *  ! for getrandom() to work with the version of
           16  + *    libc on my android phone, the getrandom() call
           17  + *    had to be converted to use the syscall()
           18  + *    interface. this is unlikely to cause problems,
           19  + *    but should be kept in mind.
           20  + */
           21  +
           22  +#include <unistd.h>
           23  +#include <sys/random.h>
           24  +#include <sys/syscall.h>
           25  +#include <stddef.h>
           26  +#include <stdint.h>
           27  +#include <stdlib.h>
           28  +#include <string.h>
           29  +#include <fcntl.h>
           30  +#include <sodium.h>
           31  +#include <termios.h>
           32  +
           33  +
           34  +#define sz(a) ( sizeof (a) / sizeof (a) [0] )
           35  +#define say(x) (write(2, (x), sizeof (x)))
           36  +
           37  +#ifdef _CLIPBOARD
           38  +#	include <sys/types.h>
           39  +#	include <pwd.h>
           40  +#	include <stdlib.h>
           41  +#else
           42  +#   define copy(str,len)
           43  +#endif
           44  + 
           45  +enum /* constants */ {
           46  +	null = 0, true = 1, false = 0
           47  +};
           48  +
           49  +#include "err.inc"
           50  +
           51  +enum /* db format constants */ {
           52  +	db_pubkey_len = crypto_box_PUBLICKEYBYTES,
           53  +	db_privkey_len = crypto_box_SECRETKEYBYTES,
           54  +	kpw_db_pw_max = 64,
           55  +};
           56  +
           57  +typedef _Bool bool;
           58  +typedef unsigned long long iaia_word_type;
           59  +typedef bad iaia_error_type;
           60  +enum /* iaia errors */ {
           61  +	iaia_e_ok = ok,
           62  +	iaia_e_base = fail,
           63  +	iaia_e_domain = fail,
           64  +	iaia_e_overflow = fail,
           65  +};
           66  +#define _IAIA_FN_ATOI katoi
           67  +#define _IAIA_FN_ITOA kitoa
           68  +#define _IAIA_EXTERNAL_TYPES
           69  +#include "clib/iaia.c"
           70  +
           71  +#define k_static
           72  +#include "clib/compose.c"
           73  +#undef k_static
           74  +
           75  +typedef enum {
           76  +	tbl_ok = ok, tbl_error = fail
           77  +} tbl_error_type;
           78  +typedef unsigned char tbl_word_type;
           79  +#include "clib/tbl.c"
           80  +
           81  +#include "opt.inc"
           82  +
           83  +#define str_ucase "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
           84  +#define str_lcase "abcdefghijklmnopqrstuvwxyz"
           85  +#define str_num "0123456789" 
           86  +const char* reftbl = str_num str_ucase str_lcase;
           87  +const char* reftbl_lcase = str_num str_lcase;
           88  +
           89  +#ifdef _CLIPBOARD
           90  +char* const*
           91  +cbd_cmds[] = {
           92  +	/* NOTE: these commands must be specified in order of
           93  +	 * most- to least-specific. more than one utility may
           94  +	 * be present on a given system, so we need to make sure
           95  +	 * the right one is called. */
           96  +	(char* const[]){"termux-clipboard-set", null},
           97  +	(char* const[]){"xsel", "-bi", null},
           98  +	/* TODO: allow command to be specified by env var */
           99  +	null
          100  +};
          101  +
          102  +bad
          103  +copy(const char* str, size_t len) {
          104  +	char* const clipboard_env = getenv("mkpw_clipboard_setter");
          105  +	char* const clipboard_env_arg = getenv("mkpw_clipboard_setter_arg");
          106  +	// FIXME: allow multiple args
          107  +	int fds[2];
          108  +	if (pipe(fds) != 0) return 63;
          109  +	if (!fork()) {
          110  +		close(fds[1]);
          111  +		dup2(fds[0], 0);
          112  +		if (clipboard_env != null) {
          113  +			execvp(clipboard_env, (char* const[]){
          114  +					clipboard_env, clipboard_env_arg, null});
          115  +			return bad_copy;
          116  +		} else for(char* const** cmd = cbd_cmds; *cmd != null; ++cmd) {
          117  +			execvp((*cmd)[0], *cmd);
          118  +		}
          119  +		return bad_copy;
          120  +	} else {
          121  +		close(fds[0]);
          122  +		write(fds[1], str, len);
          123  +		write(fds[1], "\n", 1);
          124  +		close(fds[1]);
          125  +		return ok;
          126  +	}
          127  +}
          128  +#endif
          129  +
          130  +enum genmode { upper, mix, lower, stupid };
          131  +
          132  +bad
          133  +mkpw(enum genmode mode, char* buf, size_t const len) {
          134  +	const unsigned char chars = (sizeof str_num - 1) +
          135  +		((mode == upper) ? (sizeof str_ucase - 1) :
          136  +		((mode == lower) ? (sizeof str_lcase - 1) :
          137  +			((sizeof str_ucase - 1) + (sizeof str_lcase - 1))));
          138  +	const char* tbl = (mode == upper) ? reftbl :
          139  +			((mode == lower) ? reftbl_lcase : reftbl);
          140  +
          141  +	unsigned char noise[len];
          142  +	unsigned char* cur = noise;
          143  +	/* getrandom(noise, len, 0); // android doesnt like it */
          144  +	if(syscall(SYS_getrandom, noise, len, 0) == -1) 
          145  +		return bad_entropy;
          146  +
          147  +	for(char* ptr = buf; ptr < buf + len; ++ptr, ++cur) {
          148  +		*ptr = tbl[*cur % chars]; /* such a waste of entropy… :( */
          149  +	}
          150  +
          151  +	if(mode == stupid) {
          152  +		/* appease systems with stupid password reqs */
          153  +		buf[0] = '!';
          154  +		buf[1] = 'a' + (noise[1] % 26);
          155  +		buf[2] = 'A' + (noise[1] % 26);
          156  +	}
          157  +
          158  +	buf[len] = '\n';
          159  +
          160  +	return ok;
          161  +}
          162  +
          163  +struct entry {
          164  +	pstr account;
          165  +	pstr pw;
          166  +};
          167  +
          168  +enum term_clear {term_clear_line,
          169  +                 term_clear_screen};
          170  +
          171  +enum alert {a_notice, a_warn, a_debug, a_fatal = 1 << 8};
          172  +pstr alert_msg[] = {
          173  +	_p("(notice)"), _p("(warn)"),
          174  +	_p("(debug)"),  _p("(fatal)")
          175  +};
          176  +
          177  +bool _g_alert_quiet = false,
          178  +     _g_debug_msgs  = false;
          179  +void alert(uint16_t kind, const char* msg) {
          180  +	if (!((kind >= a_fatal) ||
          181  +	      (_g_debug_msgs && kind == a_debug) ||
          182  +	      (!_g_alert_quiet && kind != a_debug))) return;
          183  +
          184  +	uint8_t idx;
          185  +	if (kind & a_fatal) idx = a_debug + 1;
          186  +		else idx = kind;
          187  +	
          188  +
          189  +	if (isatty(2)) {
          190  +		char msgcode[] = "\x1b[1;30m";
          191  +		char* color = msgcode+5;
          192  +		*color = '0' + (4 - idx);
          193  +		write(2,msgcode, sz(msgcode));
          194  +	}
          195  +
          196  +	write(2,alert_msg[idx].ptr,alert_msg[idx].len);
          197  +	if (isatty(2)) write(2,"\x1b[m ",4);
          198  +		else write(2," ",1);
          199  +	write(2,msg,strlen(msg));
          200  +	write(2,"\n",1);
          201  +
          202  +	if (kind & a_fatal) exit(kind & (~a_fatal));
          203  +}
          204  +
          205  +void term_clear(enum term_clear behavior) {
          206  +	switch(behavior) {
          207  +		case term_clear_line:   write(1,"\r\x1b[2K",5); break;
          208  +		case term_clear_screen: write(1,"\r\x1b[3J",5); break;
          209  +	}
          210  +}
          211  +void term_bell() {
          212  +	write(1,"\a",1);
          213  +}
          214  +
          215  +typedef char password[kpw_db_pw_max + 1];
          216  +bad pwread(char* dest, size_t* out_len, const char* prompt, const size_t plen) {
          217  +	if (isatty(0)) {
          218  +		struct termios initial; {
          219  +			/* in order to take PW input, we need to shut
          220  +			 * off echo and canonical mode. now we're in
          221  +			 * charge of reading each keypress. */
          222  +			tcgetattr(1, &initial);
          223  +			struct termios nt = initial;
          224  +			nt.c_lflag &= (~ECHO & ~ICANON);
          225  +			tcsetattr(1, TCSANOW, &nt);
          226  +		}
          227  +
          228  +		*dest = 0;
          229  +		char* p = dest;
          230  +
          231  +		do {
          232  +			term_clear(term_clear_line);
          233  +			write(1, "\x1b[1m", 4);
          234  +			write(1, prompt, plen);
          235  +			write(1, "\x1b[21m", 5);
          236  +
          237  +			for(size_t i = 0; i < p - dest; ++i)
          238  +				write(1, "*", 1);
          239  +
          240  +			char c;
          241  +			if (read(0, &c, 1) == 1) {
          242  +				switch (c) {
          243  +					case '\n': case '\r':
          244  +						/* accept pw */
          245  +						if (p > dest) goto end_read_loop;
          246  +							else break;
          247  +					case '\x1b': /* escape */
          248  +						term_clear(term_clear_line);
          249  +						return bad_cancel;
          250  +					case '\b': case '\x7f':
          251  +						if (p > dest) *p--=0;
          252  +							else term_bell();
          253  +						break;
          254  +					default:
          255  +						if (p - dest != 64) *p++=c;
          256  +							else term_bell();
          257  +				}
          258  +			} else {
          259  +				/* either EOF or an error - either way,
          260  +				 * we're finished here */
          261  +				break;
          262  +			}
          263  +		} while(1);
          264  +		end_read_loop: term_clear(term_clear_line);
          265  +		*p = 0;
          266  +		if (out_len!=NULL) *out_len = p - dest;
          267  +
          268  +		/* return the terminal to normal */
          269  +		tcsetattr(1, TCSANOW, &initial);
          270  +	} else {
          271  +		alert(a_warn, "reading pw from standard input");
          272  +		ssize_t ct = read(0, dest, kpw_db_pw_max);
          273  +		dest[ct] = 0;
          274  +	}
          275  +}
          276  +
          277  +#include <stdio.h>
          278  +int
          279  +dbopen(int flags) {
          280  +	const char* dbpath = getenv("kpw_db");
          281  +	int db;
          282  +	if (dbpath == NULL) {
          283  +		const char* cfg = getenv("XDG_CONFIG_HOME");
          284  +		if (cfg == NULL) {
          285  +			const char* home = getenv("HOME");
          286  +			if (home == NULL) exit(bad_insane);
          287  +
          288  +			size_t homelen = strlen(home);
          289  +			pstr path[] = { {homelen, home}, _p("/.config/kpw.db") };
          290  +			char buf[homelen + path[1].len + 1];
          291  +			bzero(buf, sz(buf));
          292  +			impose(path, sz(path), NULL, buf);
          293  +
          294  +			db = open(buf, flags, 0600);
          295  +		} else {
          296  +			size_t cfglen = strlen(cfg);
          297  +			pstr path[] = { {cfglen, cfg}, _p("/kpw.db") };
          298  +			char buf[cfglen + path[1].len + 1];
          299  +			bzero(buf, sz(buf));
          300  +			impose(path, sz(path), NULL, buf);
          301  +
          302  +			db = open(buf, flags, 0600);
          303  +		}
          304  +	} else {
          305  +		printf("path is %s", dbpath);
          306  +		db = open(dbpath, flags, 0600);
          307  +	}
          308  +
          309  +	return db;
          310  +}
          311  +
          312  +int
          313  +kpw(int argc, const char** argv) {
          314  +	if (argc == 0) return -1;
          315  +	if (argc == 1) { 
          316  +		size_t namelen = strlen(argv[0]);
          317  +		say("\x1b[1musage:\x1b[m ");
          318  +		write(2, argv[0], namelen);
          319  +		write(2, kpw_optstr, sz(kpw_optstr));
          320  +		write(2, kpw_usage,  sz(kpw_usage));
          321  +		return bad_usage;
          322  +	}
          323  +
          324  +	enum genmode
          325  +		mode = lower;
          326  +	enum {usage,getpw,addpw,delpw,genpw,
          327  +	      chpw,keyin,logout,createdb,rekeydb}
          328  +		op = getpw;
          329  +
          330  +	const char* params[3];
          331  +	uint8_t param = 0;
          332  +
          333  +	bool print = false,
          334  +		 clobber = false;
          335  +#	ifdef _CLIPBOARD
          336  +		bool copy_pw = true;
          337  +#	endif
          338  +	for (const char** arg = argv + 1; *arg != null; ++arg) {
          339  +		if ((*arg)[0] == '-') {
          340  +			if ((*arg)[1] == '-') { /* long opt */
          341  +				unsigned char a;
          342  +				if (tblget(sz(argtbl), argtbl, *arg + 2, &a) == ok) switch (a) {
          343  +					kpw_emit_long_option_switch
          344  +				} else {
          345  +					return bad_option;
          346  +				}
          347  +			} else { /* short opt */
          348  +				for(const char* ptr = (*arg) + 1; *ptr != 0; ++ptr) {
          349  +					switch(*ptr) {
          350  +						kpw_emit_short_option_switch
          351  +						default: return bad_option;
          352  +					}
          353  +				}
          354  +			}
          355  +		} else {
          356  +			if (param > sz(params)) return bad_syntax;
          357  +			params[param++] = *arg;
          358  +		}
          359  +	}
          360  +
          361  +	if (sodium_init() < 0) 
          362  +		return bad_lib_sodium_init;
          363  +
          364  +	switch(op) {
          365  +		case getpw:{ /* kpw <acct> */
          366  +			break;
          367  +		}
          368  +
          369  +		case addpw:{ /* kpw -a <acct> [<pw>] */
          370  +			break;
          371  +		}
          372  +
          373  +		case delpw:{ /* kpw -d <acct> */
          374  +			break;
          375  +		}
          376  +
          377  +		case genpw:{ /* kpw -g[lmu] <acct> [<len>] */
          378  +			break;
          379  +		}
          380  +
          381  +		case createdb: { /* kpw -C [<db>] */
          382  +			alert(a_debug, "creating new database");
          383  +			if (clobber)
          384  +				alert(a_warn, "will clobber any existing database");
          385  +			/* before we open our new file, we need to generate
          386  +			 * our keypairs.  the private key will be encrypted
          387  +			 * with a blake2 hash of a user-supplied passphrase 
          388  +			 * and stored in the database after the unencrypted
          389  +			 * public key.
          390  +			 */
          391  +			uint8_t pub [crypto_box_PUBLICKEYBYTES],
          392  +			        priv[crypto_box_SECRETKEYBYTES];
          393  +			uint8_t priv_enc[sz(priv)];
          394  +
          395  +			alert(a_debug, "generating keypair");
          396  +			crypto_box_keypair(pub, priv);
          397  +
          398  +			/* kpw   works  very   differently  compared   to
          399  +			 * most  password  managers. it  uses  public-key
          400  +			 * encryption so that new  passwords can be saved
          401  +			 * to the  db without bothering the  user for the
          402  +			 * password.  now we  have  our  keypair, we  can
          403  +			 * prompt  the user for a secret  passphrase with
          404  +			 * which to encrypt the private key with.
          405  +			 */
          406  +
          407  +
          408  +			alert(a_notice, "database keypair generated, encrypting");
          409  +			password dbpw;
          410  +			size_t pwlen;
          411  +#			define _str(s) (s),sizeof(s)
          412  +			bad e = pwread(dbpw, &pwlen, _str("database passphrase: "));
          413  +			if (e != ok) return e;
          414  +			if (isatty(0)) {
          415  +				password dbpw_conf;
          416  +				e = pwread(dbpw_conf, NULL, _str("confirm: "));
          417  +#				undef _str
          418  +				if (e != ok) return e;
          419  +
          420  +				if(strcmp(dbpw,dbpw_conf) != 0)
          421  +					return bad_pw_match;
          422  +			}
          423  +
          424  +			uint8_t salt[crypto_pwhash_SALTBYTES],
          425  +			         key[db_privkey_len];
          426  +			uint8_t salt_enc[crypto_box_SEALBYTES + sz(salt)];
          427  +
          428  +			if (syscall(SYS_getrandom, salt, sz(salt), 0) == -1)
          429  +				return bad_entropy;
          430  +
          431  +			if(crypto_pwhash(key, sz(key), dbpw, pwlen, salt,
          432  +					crypto_pwhash_OPSLIMIT_INTERACTIVE,
          433  +					crypto_pwhash_MEMLIMIT_INTERACTIVE,
          434  +					crypto_pwhash_ALG_DEFAULT) != 0) {
          435  +				return bad_mem;
          436  +			}
          437  +
          438  +			for (size_t i = 0; i < sz(key); ++i) {
          439  +				priv_enc[i] = priv[i] ^ key[i];
          440  +			}
          441  +
          442  +			crypto_box_seal(salt_enc, salt, sz(salt), priv);
          443  +
          444  +			/* we have everything we need. now we create the
          445  +			 * file, failing  if it already exists so as not
          446  +			 * to clobber anyone's passwords.
          447  +			 */
          448  +			int db;
          449  +			const int flags = O_CREAT | (clobber ? O_TRUNC : O_EXCL);
          450  +			if(param == 0)
          451  +				db = dbopen(flags);
          452  +			else if (param == 1)
          453  +				db = open(params[0], flags, 0600);
          454  +			else return bad_syntax;
          455  +
          456  +			if (db == -1) return bad_db_create;
          457  +
          458  +			write(db, pub, sz(pub));
          459  +			write(db, salt, sz(salt));
          460  +			write(db, priv_enc, sz(priv_enc));
          461  +			write(db, salt_enc, sz(salt_enc));
          462  +			close(db);
          463  +
          464  +			for (int i = 0; i < sz(key); ++i) {
          465  +				printf("%2x ", key[i]);
          466  +				if (!((i+1)%8)) printf("\n");
          467  +			}
          468  +			break;
          469  +		}
          470  +	}
          471  +
          472  +#	ifdef _CLIPBOARD
          473  +	if (geteuid() == 0 && copy_pw) {
          474  +		/* on a sane system, what we'd do is hike up the process
          475  +		 * tree til we found a non-root user. alas, this is UNIX. */
          476  +		const char* realuser = getenv("SUDO_USER");
          477  +		if (realuser == null) realuser = "nobody";
          478  +
          479  +		say("\x1b[1;33mwarning:\x1b[m you are running \x1b[4m");
          480  +			size_t namelen = strlen(argv[0]);
          481  +			write(2,argv[0],namelen);
          482  +		say("\x1b[24m as \x1b[1mroot\x1b[m! dropping to \x1b[1m");
          483  +			write(2,realuser,strlen(realuser));
          484  +			setenv("USER", realuser, true);
          485  +		say("\x1b[m to prevent malicious behavior\n");
          486  +
          487  +		struct passwd* nobody = getpwnam(realuser);
          488  +		if (nobody == null) {
          489  +			say("\x1b[1;31mfatal:\x1b[m could not get UID to drop privileges; bailing");
          490  +			return bad_user;
          491  +		} else {
          492  +			setenv("HOME", nobody -> pw_dir, true);
          493  +			setenv("SHELL", "/dev/null", true);
          494  +			setuid(nobody -> pw_uid);
          495  +		}
          496  +
          497  +	}
          498  +#	endif
          499  +	
          500  +
          501  +	/* char buf[len+1]; */
          502  +	/* *buf = 0; */
          503  +	/* mkpw(mode,buf,len); */
          504  +	/* write(1, buf, len + 1); */
          505  +
          506  +#	ifdef _CLIPBOARD
          507  +		if (copy_pw) {
          508  +			copy(buf,len);
          509  +		}
          510  +#	endif
          511  +	
          512  +	return ok;
          513  +}
          514  +
          515  +int
          516  +main (int argc, const char** argv) {
          517  +	int e = 0;
          518  +	const char* msg;
          519  +	uint16_t level;
          520  +	uint8_t rv;
          521  +	switch (e = kpw(argc, argv)) {
          522  +		kpw_emit_error_switch
          523  +	}
          524  +
          525  +	alert(level, msg);
          526  +	return rv;
          527  +}

Added kpw.d/makefile version [0ee772709b].

            1  +include ../makerules
            2  +
            3  +cdeps = compose.c iaia.c tbl.c
            4  +cpaths = $(cdeps:%=$(root)/clib/%)
            5  +
            6  +$(root)/kpw: kpw.c opt.inc err.inc $(cpaths)
            7  +	$(cc) -I$(root) $< -lsodium -o $@ $(flags) $(cc-post)
            8  +
            9  +tab = cat $< | awk -v emit=$1 -F'\t+' -f $<.awk >> $@
           10  +
           11  +opt.inc: optab optab.awk
           12  +	:>$@
           13  +	$(call tab,cond)
           14  +	$(call tab,enum)
           15  +	$(call tab,argtbl)
           16  +	$(call tab,olong)
           17  +	$(call tab,oshort)
           18  +	$(call tab,usage)
           19  +
           20  +err.inc: errtab errtab.awk
           21  +	:>$@
           22  +	$(call tab,enum)
           23  +	$(call tab,msg)
           24  +	
           25  +.PHONY: dbg

Added kpw.d/optab version [4bfd28626b].

            1  +C	create	op = createdb			create a new password database
            2  +R	rekey	op = rekeydb			change database passphrase and key
            3  +a	add		op = addpw				add password to the database
            4  +g	gen		op = genpw				add account with a random password
            5  +d	del		op = delpw				delete password from the database
            6  +c	chpw	op = chpw				change password in the database
            7  +l	lower	mode = lower			generate lowercase password
            8  +m	mix		mode = mix				generate mix-case password
            9  +u	upper	mode = upper			generate uppercase password
           10  +s	stupid	mode = stupid			circumvent dumb pw restrictions
           11  +k	key		op = keyin				save database key in session memory
           12  +o	logout	op = logout				delete db key from session memory
           13  +n	nocopy	copy_pw = false			don't copy generated pw to clipboard	_CLIPBOARD
           14  +p	print	print = true			display generated password
           15  +q	quiet	_g_alert_quiet = true	hide non-fatal reports
           16  +v	verbose	_g_debug_msgs = true	display debug reports
           17  +!	clobber	clobber = true			disable safety checks

Added kpw.d/optab.awk version [b43a4e1b77].

            1  +BEGIN {
            2  +	end = ""
            3  +	if (emit == "enum") {
            4  +		print "enum args {"
            5  +	} else if (emit == "argtbl") {
            6  +		print "struct tblrow argtbl[] = {"
            7  +	} else if (emit == "olong") {
            8  +		print "#define kpw_emit_long_option_switch \\"
            9  +		end = "\\"
           10  +	} else if (emit == "oshort") {
           11  +		print "#define kpw_emit_short_option_switch \\"
           12  +		end = "\\"
           13  +	} else if (emit == "usage") {
           14  +		print "const char kpw_usage[] ="
           15  +	}
           16  +	globalc = 0
           17  +}
           18  +
           19  +function say(line) {
           20  +	if (NF == 5) {
           21  +		print "\tkpw_only_" $5 "(" line ") " end
           22  +		globals[globalc] = $5
           23  +		++ globalc
           24  +	} else {
           25  +		print "\t" line " " end
           26  +	}
           27  +}
           28  +
           29  +{ optstr = optstr $1 }
           30  +emit == "enum"   { say("arg_" $2 ",") }
           31  +emit == "argtbl" { say("{ arg_" $2", \"" $2 "\" },") }
           32  +emit == "olong"  { say("case arg_" $2 ": " $3 "; break;") }
           33  +emit == "oshort" { say("case '" $1 "': " $3 "; break;") }
           34  +emit == "usage"  { say("\"\\t-"$1", --"$2": "$4"\\n\"") }
           35  +
           36  +emit == "cond" {
           37  +	if (NF == 5 && !($5 in condlist)) {
           38  +		condlist[$5] = 1
           39  +		print "#ifdef " $5
           40  +		print "#	define kpw_only_" $5 "(x...) x"
           41  +		print "#else"
           42  +		print "#	define kpw_only_" $5 "(...)"
           43  +		print "#endif"
           44  +	}
           45  +}
           46  +
           47  +END {
           48  +	if (emit == "olong" || emit == "oshort") {
           49  +		print ""
           50  +	} else if (emit == "usage") {
           51  +		print ";"
           52  +		print "const char kpw_optstr[] = \" [-" optstr "] [args]\\n\";"
           53  +	} else if (emit != "cond") {
           54  +		print "};"
           55  +	}
           56  +}

Modified makefile from [bb9a75f0d8] to [6fa77053ea].

     6      6   # to build project contained in their own directory, run 
     7      7   # $ make (dir).proj
     8      8   
     9      9   all: ctl conv xutil gen
    10     10   
    11     11   xutil: xpriv safekill
    12     12     # X11 tools
    13         -gen: mkpw rosshil
           13  +gen: mkpw kpw rosshil
    14     14     # procedural generators
    15     15   conv: ord
    16     16     # converters
    17     17   ctl: nkvd.so bgrd
    18     18     # manipulating disobedient software
    19     19   
    20     20   nkvd.so: nkvd.c
................................................................................
    24     24   	$(cc) $< -lutil -o$@ $(cc-post)
    25     25   
    26     26   safekill: safekill.c
    27     27   	$(cc) $< -lX11 -o$@ $(cc-post)
    28     28   
    29     29   xpriv: xpriv.c
    30     30   	$(cc) $< -lrt -lutil -lX11 -o $@ $(cc-post)
           31  +
           32  +kpw: kpw.d/makefile
           33  +	$(MAKE) root=$(realpath .) flags=$(kpw-flags) -C kpw.d $(realpath .)/$@
           34  +
           35  +.PHONY: kpw

Modified makerules from [8c92429cd1] to [77fcbe1814].

    29     29   	$(sc) $< -o $@ $(sc-post)
    30     30   
    31     31   %.proj: %/makefile
    32     32   	cd $* && make $*
    33     33   
    34     34   %.proj: %/make.sh
    35     35   	cd $* && ./make.sh
           36  +
           37  +dep/%:
           38  +	make -C dep $*

Modified mkpw.c from [d671829e71] to [f1f30ece5b].

     7      7    *      does this by attempting to execute a sequence
     8      8    *      of binaries, and then writing the password
     9      9    *      to STDIN of the binary that succeeds.
    10     10    *  ? generates passwords
    11     11    *  → mkpw is unlikely to be portable to non-POSIX
    12     12    *    systems, but should run fine on Linux as well
    13     13    *    as BSDs with getrandom() support.
           14  + *  ! for getrandom() to work with the version of
           15  + *    libc on my android phone, the getrandom() call
           16  + *    had to be converted to use the syscall()
           17  + *    interface. this is unlikely to cause problems,
           18  + *    but should be kept in mind.
    14     19    */
    15     20   
    16     21   #include <unistd.h>
    17     22   #include <sys/random.h>
    18     23   #include <sys/syscall.h>
    19     24   #include <stddef.h>
    20     25   #include <stdint.h>
................................................................................
    43     48   
    44     49   typedef enum bad {
    45     50   	ok = 0,
    46     51   	fail = 1,
    47     52   	bad_user,
    48     53   	bad_option,
    49     54   	bad_syntax,
           55  +	bad_entropy,
           56  +	bad_copy,
    50     57   	bad_usage = 64, /* fleabsd idiom */
    51     58   } bad;
    52     59   
    53     60   typedef _Bool bool;
    54     61   typedef unsigned long long iaia_word_type;
    55     62   typedef bad iaia_error_type;
    56     63   enum /* iaia errors */ {
................................................................................
   195    202   
   196    203   	char buf[len+1];
   197    204   	/* *buf = 0; */
   198    205   
   199    206   	unsigned char noise[len];
   200    207   	for (size_t i = 0; i<q; ++i) {
   201    208   		unsigned char* cur = noise;
   202         -		syscall(SYS_getrandom, noise, len, 0);
          209  +		if(syscall(SYS_getrandom, noise, len, 0) == -1)
          210  +			return bad_entropy;
   203    211   		/* getrandom(noise, len, 0); // android doesnt like it */
   204    212   
   205    213   		for(char* ptr = buf; ptr < buf + len; ++ptr, ++cur) {
   206    214   			*ptr = tbl[*cur % chars]; /* such a waste of entropy… :( */
   207    215   		}
   208    216   
   209    217   		buf[len] = '\n';
................................................................................
   219    227   			if (pipe(fds) != 0) return 63;
   220    228   			if (!fork()) {
   221    229   				close(fds[1]);
   222    230   				dup2(fds[0], 0);
   223    231   				if (clipboard_env != null) {
   224    232   					execvp(clipboard_env, (char* const[]){
   225    233   							clipboard_env, clipboard_env_arg, null});
   226         -					return -1;
          234  +					return bad_copy;
   227    235   				} else for(char* const** cmd = cbd_cmds; *cmd != null; ++cmd) {
   228    236   					execvp((*cmd)[0], *cmd);
   229    237   				}
   230         -				return -1; /* exec failed */
          238  +				return bad_copy; /* exec failed */
   231    239   			} else {
   232    240   				close(fds[0]);
   233    241   				write(fds[1], buf, len);
   234    242   				write(fds[1], "\n", 1);
   235    243   				close(fds[1]);
   236    244   			}
   237    245   		}
   238    246   #	endif
   239    247   	
   240    248   	return ok;
   241    249   }