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

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

    21     21    *  TODO prevent pw reads from going off the edge of
    22     22    *       the screen and fucking up all the shit
    23     23    */
    24     24   
    25     25   #include <unistd.h>
    26     26   #include <sys/random.h>
    27     27   #include <sys/syscall.h>
           28  +#include <sys/shm.h>
    28     29   #include <stddef.h>
    29     30   #include <stdint.h>
    30     31   #include <stdlib.h>
    31     32   #include <string.h>
    32     33   #include <fcntl.h>
    33     34   #include <sodium.h>
    34     35   #include <termios.h>
................................................................................
    43     44   #	include <pwd.h>
    44     45   #	include <stdlib.h>
    45     46   #else
    46     47   #   define copy(str,len)
    47     48   #endif
    48     49    
    49     50   enum /* constants */ {
    50         -	null = 0, true = 1, false = 0
           51  +	null = 0, true = 1, false = 0,
           52  +	kpw_shm_key = 0x3CC215A,
    51     53   };
    52     54   
    53     55   #include "err.inc"
    54     56   
    55     57   enum /* db format constants */ {
    56     58   	db_pubkey_len = crypto_box_PUBLICKEYBYTES,
    57     59   	db_privkey_len = crypto_box_SECRETKEYBYTES,
................................................................................
    93     95   #define str_ucase "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
    94     96   #define str_lcase "abcdefghijklmnopqrstuvwxyz"
    95     97   #define str_num "0123456789" 
    96     98   const char* reftbl = str_num str_ucase str_lcase;
    97     99   const char* reftbl_lcase = str_num str_lcase;
    98    100   
    99    101   const char* _g_binary_name;
   100         -#ifdef _CLIPBOARD
   101         -char* const*
   102         -cbd_cmds[] = {
   103         -	/* NOTE: these commands must be specified in order of
   104         -	 * most- to least-specific. more than one utility may
   105         -	 * be present on a given system, so we need to make sure
   106         -	 * the right one is called. */
   107         -	(char* const[]){"termux-clipboard-set", null},
   108         -	(char* const[]){"xsel", "-bi", null},
   109         -	/* TODO: allow command to be specified by env var */
   110         -	null
   111         -};
   112    102   
   113    103   enum { plain_term, ansi_term, color_term } _g_term_type[3];
   114    104   enum alert {a_notice, a_warn, a_debug, a_fatal = 1 << 8};
   115    105   pstr alert_msg[] = {
   116    106   	_p("(kpw notice)"), _p("(kpw warn)"),
   117    107   	_p("(kpw debug)"),  _p("(kpw fatal)")
   118    108   };
................................................................................
   144    134   	if (_g_term_type[2] != plain_term) write(2,"\x1b[m ",4);
   145    135   		else write(2," ",1);
   146    136   	write(2,msg,strlen(msg));
   147    137   	write(2,"\n",1);
   148    138   
   149    139   	if (kind & a_fatal) exit(kind & (~a_fatal));
   150    140   }
          141  +
          142  +#ifdef _CLIPBOARD
          143  +char* const*
          144  +cbd_cmds[] = {
          145  +	/* NOTE: these commands must be specified in order of
          146  +	 * most- to least-specific. more than one utility may
          147  +	 * be present on a given system, so we need to make sure
          148  +	 * the right one is called. */
          149  +	(char* const[]){"termux-clipboard-set", null},
          150  +	(char* const[]){"xsel", "-bi", null},
          151  +	/* TODO: allow command to be specified by env var */
          152  +	null
          153  +};
          154  +
   151    155   
   152    156   enum bad
   153    157   copy(const char* str, size_t len) {
   154    158   	alert(a_debug, "copying password to clipboard");
   155    159   	if (geteuid() == 0) {
   156    160   		/* on a sane system, what we'd do is hike up the process
   157    161   		 * tree til we found a non-root user. alas, this is UNIX. */
................................................................................
   456    460   }
   457    461   
   458    462   enum bad
   459    463   dbunlock(byte* priv_enc, byte* salt, byte* priv) {
   460    464   	const size_t priv_sz = sizeof(key_priv);
   461    465   	byte key [db_privkey_len];
   462    466   
   463         -	password dbpw; size_t pwlen;
   464         -	bad e = pwread(true, dbpw, &pwlen,_str("database key: "));
   465         -	if (e != ok) return e;
   466         -
   467         -	alert(a_debug, "deriving secret");
   468         -	if(crypto_pwhash(key, sz(key), dbpw, pwlen, salt,
   469         -				crypto_pwhash_OPSLIMIT_INTERACTIVE,
   470         -				crypto_pwhash_MEMLIMIT_INTERACTIVE,
   471         -				crypto_pwhash_ALG_DEFAULT) != 0) {
   472         -		return bad_mem;
   473         -	}
   474         -	hexdump(key, sz(key));
   475         -
   476         -	alert(a_debug, "attempting to decrypt private key");
   477         -	for (size_t i = 0; i < sz(key); ++i) {
   478         -		priv[i] = priv_enc[i] ^ key[i];
   479         -	}
   480         -	hexdump(priv, sz(key));
          467  +	/* is the private key loaded into memory? */
          468  +	int shm = shmget(*((key_t*) salt), sizeof(key_priv), 0);
          469  +	if (shm == -1) {
          470  +		/* no key in memory - read password from stdin instead */
          471  +		password dbpw; size_t pwlen;
          472  +		bad e = pwread(true, dbpw, &pwlen,_str("database key: "));
          473  +		if (e != ok) return e;
          474  +
          475  +		alert(a_debug, "deriving secret");
          476  +		if(crypto_pwhash(key, sz(key), dbpw, pwlen, salt,
          477  +					crypto_pwhash_OPSLIMIT_INTERACTIVE,
          478  +					crypto_pwhash_MEMLIMIT_INTERACTIVE,
          479  +					crypto_pwhash_ALG_DEFAULT) != 0) {
          480  +			return bad_mem;
          481  +		}
          482  +		hexdump(key, sz(key));
          483  +
          484  +		alert(a_debug, "attempting to decrypt private key");
          485  +		for (size_t i = 0; i < sz(key); ++i) {
          486  +			priv[i] = priv_enc[i] ^ key[i];
          487  +		}
          488  +		hexdump(priv, sz(key));
          489  +	} else {
          490  +		/* found a key in memory; loading it into *priv */
          491  +		alert(a_notice, "using saved key");
          492  +		key_priv* saved = shmat(shm, 0, 0);
          493  +		if (saved == (void*)-1)
          494  +			return bad_shm;
          495  +		hexdump((byte*)saved, sizeof(key_priv));
          496  +		memcpy(priv, saved, sizeof(key_priv));
          497  +		shmdt(saved);
          498  +	}
   481    499   
   482    500   	return ok;
   483    501   }
   484    502   
   485    503   enum bad
   486    504   dbverify(byte* salt, byte* salt_enc, byte* pub, byte* priv) {
   487    505   	byte salt_dec [crypto_pwhash_SALTBYTES];
................................................................................
   545    563   int
   546    564   kpw(int argc, const char** argv) {
   547    565   	if (argc == 0) return bad_insane;
   548    566   	_g_binary_name = argv[0];
   549    567   
   550    568   	enum genmode
   551    569   		mode = lower;
   552         -	enum {usage,getpw,addpw,delpw,lspw,genpw,regen,
   553         -	      chpw,keyin,logout,createdb,rekeydb,help}
          570  +	enum {usage,getpw,addpw,delpw,lspw,
          571  +	      genpw,regen,mergedb,chpw,keyin,
          572  +		  logout,createdb,rekeydb,help}
   554    573   		op = getpw;
   555    574   
   556    575   	const char* params[3];
   557    576   	uint8_t param = 0;
   558    577   
   559    578   	bool print = false,
   560    579   		 clobber = false,
................................................................................
   591    610   
   592    611   	if (op == getpw && param == 0) return emit_usage(NULL);
   593    612   
   594    613   	if (sodium_init() < 0) 
   595    614   		return bad_lib_sodium_init;
   596    615   
   597    616   	switch(op) {
          617  +		case logout:
          618  +		case keyin: {
          619  +			if (param != 0) return bad_syntax;
          620  +
          621  +			int db = dbopen(O_RDONLY);
          622  +			key_pub  pub;
          623  +			key_priv priv, priv_enc;
          624  +			byte salt     [crypto_pwhash_SALTBYTES],
          625  +			     salt_enc [crypto_box_SEALBYTES + sz(salt)];
          626  +
          627  +			bad e;
          628  +			if ((e = dbheader_load(db, salt, salt_enc, pub, priv_enc)) != ok)
          629  +				return e;
          630  +			
          631  +			key_t ipck = *((key_t*)salt);
          632  +			if (op == keyin) {
          633  +				if ((e = dbunlock(priv_enc, salt, priv)) != ok)
          634  +					return e;
          635  +				if ((e = dbverify(salt, salt_enc, pub, priv)) != ok)
          636  +					return e;
          637  +				int shm = shmget(ipck, sizeof(key_priv),
          638  +						IPC_CREAT | IPC_EXCL | 0600);
          639  +				if (shm == -1) return bad_shm_exist;
          640  +
          641  +				key_priv* saved = shmat(shm, 0, 0);
          642  +				if (saved == (void*)-1) return bad_shm;
          643  +				memcpy(saved, priv, sz(priv));
          644  +				shmdt(saved);
          645  +			} else {
          646  +				int shm = shmget(ipck, sizeof(key_priv), 0);
          647  +				if (shm == -1) return bad_no_shm;
          648  +				shmctl(shm, IPC_RMID, NULL);
          649  +			}
          650  +
          651  +			return ok;
          652  +		}
   598    653    
   599    654   		case genpw:   
   600    655   		case addpw: {
   601    656   			if (param == 0) return emit_usage(
   602    657   					op == addpw ? " -a[p] <account> [<pw>]\n"       :
   603         -					   /* genpw */" -g[lmup] <account> [<pw len>]\n");
          658  +					   /* genpw */" -g[lmusp] <account> [<pw len>]\n");
   604    659   
   605    660   			if (param > 2 || param < 1) return bad_syntax;
   606    661   			const char* acct = params[0],
   607    662   			          * prm = (param == 2 ? params[1] : NULL);
   608    663   
   609    664   			alert(a_debug, "opening database");
   610    665   			int db = dbopen(O_RDWR);
................................................................................
   620    675   
   621    676   			password pw; size_t pwlen;
   622    677   			const char* acct_pw;
   623    678   			if (op == addpw) {
   624    679   				if (prm == NULL) {
   625    680   					pstr prompt_l[] = { _p("- new password for "),
   626    681   						{0, acct}, _p(": "), };
   627         -					char prompt[pstrsum(prompt_l, sz(prompt_l)) + 1];
          682  +					char prompt[pstrsum(prompt_l, sz(prompt_l))];
   628    683   					if (tty_in) pstrcoll(prompt_l, sz(prompt_l), prompt);
   629    684   
   630    685   					bad e = pwread(!print, pw, &pwlen,
   631         -							prompt, sz(prompt) - 1);
          686  +							prompt, sz(prompt));
   632    687   					if (e != ok) return e;
   633    688   					if (tty_in && !print) {
   634    689   						password pw_conf;
   635    690   						e = pwread(true, pw_conf, NULL, _str("- confirm: "));
   636    691   						if (e != ok) return e;
   637    692   						if (strcmp(pw,pw_conf) != 0)
   638    693   							return bad_pw_match;
   639    694   					}
   640    695   					acct_pw = pw;
   641         -				} else acct_pw = prm;
          696  +				} else acct_pw = prm, pwlen = strlen(prm);
   642    697   
   643    698   			} else if (op == genpw) {
   644    699   				unsigned long long len; 
   645    700   				if (prm != NULL) {
   646    701   					alert(a_debug, "converting length parameter to integer");
   647    702   					bad e = katoi(10, prm, &len);
   648    703   					if (e != ok) return bad_num;
................................................................................
   653    708   				if (mkpw(mode, pw, len) == bad_entropy) return bad_entropy;
   654    709   				if (print || !tty_out) {
   655    710   					write(1, pw, len);
   656    711   					if(tty_out) write(1, "\n", 1);
   657    712   				}
   658    713   				pwlen = len;
   659    714   			}
   660         -			if (copy_pw) copy(pw, pwlen);
          715  +#			ifdef _CLIPBOARD
          716  +				if (copy_pw) copy(pw, pwlen);
          717  +#			endif
   661    718   			alert(a_debug, "encoding database entry");
   662    719   			db_sz acctlen = strlen(acct);
   663    720   			byte plaintext[1 + acctlen +
   664    721   			               1 + pwlen];
   665    722   			plaintext[0] = acctlen;
   666    723   			strncpy(plaintext + 1, acct, acctlen);
   667    724   			plaintext[1 + acctlen] = pwlen;
................................................................................
   679    736   			write(db, &ciphertext_len, 1);
   680    737   			write(db, &ciphertext, ciphertext_len);
   681    738   			
   682    739   			close(db);
   683    740   			break;
   684    741   		}
   685    742   
          743  +		case chpw:
          744  +		case regen:
   686    745   		case delpw:{ /* kpw -d <acct> */
   687         -			if (param == 0) return emit_usage(" -d <account>\n");
   688         -			if (param != 1) return bad_syntax;
          746  +			if (param == 0) return emit_usage
          747  +				  (op==delpw ? " -d <account>\n"          :
          748  +				   op==regen ? " -r[lmusp] <account> [<pw len>]" :
          749  +				/* op==chpw */ " -c <account> [<new pw>]");
          750  +
          751  +			if (param < 1 || param > (op == delpw ? 1 : 2))
          752  +				return bad_syntax;
   689    753   			const char* target = params[0];
   690         -			bad e;
          754  +			const char* delta;
          755  +			if (param == 2) delta=params[1];
          756  +				else delta=NULL;
   691    757   
   692    758   			int db = dbopen(O_RDWR);
   693    759   			if (db == -1) return bad_db_load;
   694    760   
   695    761   			const size_t dbsz = lseek(db, 0, SEEK_END);
   696    762   			lseek(db, 0, SEEK_SET);
   697    763   
   698    764   			key_pub  pub;
   699    765   			key_priv priv, priv_enc;
   700    766   
   701    767   			byte salt     [crypto_pwhash_SALTBYTES],
   702    768   			     salt_enc [crypto_box_SEALBYTES + sz(salt)];
   703    769   
          770  +			bad e;
   704    771   			if ((e = dbheader_load(db, salt, salt_enc, pub, priv_enc)) != ok)
   705    772   				return e;
   706    773   			if ((e = dbunlock(priv_enc, salt, priv)) != ok)
   707    774   				return e;
   708    775   			if ((e = dbverify(salt, salt_enc, pub, priv)) != ok)
   709    776   				return e;
   710    777   
................................................................................
   733    800   				struct dbrecord rec;
   734    801   
   735    802   				bad d;
   736    803   				if((d = dbnext(db, &rec, ctlen,
   737    804   					pub, priv, ctbuf, ptbuf)) != ok) return d;
   738    805   
   739    806   				if(strncmp(target, rec.acct.ptr, rec.acct.len) == 0) {
   740         -					/* found a matching record; do not copy it */
   741         -					alert(a_debug, "found record to delete");
          807  +					/* found a matching record; determine
          808  +					 * what to do to the fucker */
          809  +					alert(a_debug, "found target record");
   742    810   					found = true;
          811  +					if (op == delpw) continue;
          812  +
          813  +					password pwbuf;
          814  +					const char* newpass;
          815  +					size_t pwlen; 
          816  +					if (op == regen) {
          817  +						alert(a_debug, "generating new password");
          818  +						/* generating a new password. use the default
          819  +						 * length if the user hasn't supplied one herself,
          820  +						 * or if she has, convert it to an integer. */
          821  +						if (delta == NULL) pwlen = default_pw_len; else {
          822  +							unsigned long long value;
          823  +							bad k = katoi(10, delta, &value);
          824  +							if (k != ok) return bad_num;
          825  +							pwlen = value;
          826  +						}
          827  +						bad m = mkpw(mode, pwbuf, pwlen);
          828  +						if (m != ok) return m;
          829  +						newpass = pwbuf;
          830  +					} else if (op == chpw) {
          831  +						/* the user has requested a password change. take
          832  +						 * it from the command line if available, otherwise
          833  +						 * generate a prompt and read from stdin */
          834  +
          835  +						if (delta == NULL) {
          836  +							pstr prompt_l[] = { _p("- new password for "),
          837  +								{0, target}, _p(": "), };
          838  +							char prompt[pstrsum(prompt_l, sz(prompt_l))];
          839  +							if (_g_term_type[0] > plain_term)
          840  +								pstrcoll(prompt_l, sz(prompt_l), prompt);
          841  +
          842  +							bad p = pwread(!print, pwbuf, &pwlen, prompt, sz(prompt));
          843  +							if (p != ok) return p;
          844  +							/* prompt again to make sure the user entered
          845  +							 * her new password correctly */
          846  +							if(!print && _g_term_type[0] > plain_term) {
          847  +								password passconf;
          848  +								p = pwread(!print, passconf, NULL, _str("confirm: "));
          849  +								if (p != ok) return p;
          850  +								if (strcmp(passconf, pwbuf) != 0)
          851  +									return bad_pw_match;
          852  +							}
          853  +							newpass = pwbuf;
          854  +						} else newpass = delta, pwlen = strlen(delta);
          855  +					} else return bad_assert;
          856  +
          857  +					if (op == regen && print) {
          858  +						write(1, newpass, pwlen);
          859  +						if (_g_term_type[1] > plain_term) write(1, "\n", 1);
          860  +					}
          861  +
          862  +#					ifdef _CLIPBOARD
          863  +						if (copy_pw) copy(newpass, pwlen);
          864  +#					endif
          865  +
          866  +					/* new pw is pointed to by `newpass`. encrypt it
          867  +					 * and insert it into the new database image */
          868  +					byte plaintext [1 + rec.acct.len +
          869  +					                1 + pwlen        ];
          870  +					plaintext[0] = rec.acct.len;
          871  +					memcpy(plaintext + 1, rec.acct.ptr, rec.acct.len);
          872  +					plaintext[1 + rec.acct.len] = pwlen;
          873  +					memcpy(plaintext + 2 + rec.acct.len, newpass, pwlen);
          874  +
          875  +					alert(a_debug, "enciphering plaintext of modified record");
          876  +					hexdump(plaintext, sz(plaintext));
          877  +
          878  +					byte ciphertext [sz(plaintext) + crypto_box_SEALBYTES];
          879  +					crypto_box_seal(ciphertext, plaintext, sz(plaintext), pub);
          880  +					db_sz new_ct_len = sz(ciphertext);
          881  +
          882  +					alert(a_debug, "copying ciphertext into database");
          883  +					cursor = transcribe(cursor, &new_ct_len, 1);
          884  +					cursor = transcribe(cursor, ciphertext, sz(ciphertext));
   743    885   				} else {
   744         -					/* not our target, copy it into the buffer */
          886  +					/* not our target, copy it into the buffer as-is */
   745    887   					cursor = transcribe(cursor, &ctlen, sizeof(db_sz));
   746    888   					cursor = transcribe(cursor, ctbuf, sz(ctbuf));
   747    889   				}
   748    890   
   749    891   			} if (e != fail) return e;
   750    892   			if (scanned == 0) return bad_db_empty;
   751    893   			if (!found) return bad_index;
................................................................................
   756    898   			ftruncate(db,0);
   757    899   			lseek(db, 0, SEEK_SET);
   758    900   			write(db, newdb, cursor-newdb);
   759    901   
   760    902   			/* we're done here, time to close up shop */
   761    903   			close(db);
   762    904   
   763         -			break;
          905  +			return ok;
   764    906   		}
   765    907   
   766    908   		case getpw:  /* kpw <acct> */
   767    909   		case lspw: { /* kpw -t[p] [<prefix>] */
   768    910   			const char* target;
   769    911   			if (param == 1) target = params[0];
   770    912   			else if (param == 0) target = NULL;
................................................................................
   810    952   					if (strncmp(rec.acct.ptr,target,rec.acct.len) == 0) {
   811    953   						if (print || _g_term_type[1] == plain_term) {
   812    954   							write(1, rec.pw.ptr, rec.pw.len);
   813    955   							if(_g_term_type[1] > plain_term)
   814    956   								write(1, "\n", 1);
   815    957   						}
   816    958   
   817         -						if (_g_term_type[1] > plain_term) {
   818         -							if (copy_pw) copy(rec.pw.ptr, rec.pw.len);
   819         -						}
          959  +#						ifdef _CLIPBOARD
          960  +							if (_g_term_type[1] > plain_term) {
          961  +								if (copy_pw) copy(rec.pw.ptr, rec.pw.len);
          962  +							}
          963  +#						endif
   820    964   						found = true;
   821    965   						break;
   822    966   					}
   823    967   				}
   824    968   			}
   825    969   			if (scanned == 0) return bad_db_empty;
   826    970   			if (result != fail) return result;
   827    971   			else if (!found) return bad_index;
   828    972   			
   829         -			break;
          973  +			return ok;
   830    974   		}
   831    975   
   832    976   		case createdb: { /* kpw -C [<db>] */
   833    977   			alert(a_debug, "creating new database");
   834    978   			if (clobber)
   835    979   				alert(a_warn, "will clobber any existing database");
   836    980   			/* before we open our new file, we need to generate
................................................................................
   924   1068   			write(db, pub, sz(pub));
   925   1069   			write(db, salt, sz(salt));
   926   1070   			write(db, priv_enc, sz(priv_enc));
   927   1071   			write(db, salt_enc, sz(salt_enc));
   928   1072   			close(db);
   929   1073   
   930   1074   			alert(a_debug, "database created");
   931         -			break;
         1075  +			return ok;
   932   1076   		}
   933   1077   	}
   934   1078   
   935         -#	ifdef _CLIPBOARD
   936         -		if (copy_pw) {
   937         -			/* copy(buf,len); */
   938         -		}
   939         -#	endif
   940         -	
   941   1079   	return ok;
   942   1080   }
   943   1081   
   944   1082   int
   945   1083   main (int argc, const char** argv) {
   946   1084   	const char* colorterm = getenv("COLORTERM");
   947   1085   	const char* term = getenv("TERM");

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

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