@@ -24,8 +24,9 @@ #include #include #include +#include #include #include #include #include @@ -46,9 +47,10 @@ # define copy(str,len) #endif enum /* constants */ { - null = 0, true = 1, false = 0 + null = 0, true = 1, false = 0, + kpw_shm_key = 0x3CC215A, }; #include "err.inc" @@ -96,20 +98,8 @@ const char* reftbl = str_num str_ucase str_lcase; const char* reftbl_lcase = str_num str_lcase; const char* _g_binary_name; -#ifdef _CLIPBOARD -char* const* -cbd_cmds[] = { - /* NOTE: these commands must be specified in order of - * most- to least-specific. more than one utility may - * be present on a given system, so we need to make sure - * the right one is called. */ - (char* const[]){"termux-clipboard-set", null}, - (char* const[]){"xsel", "-bi", null}, - /* TODO: allow command to be specified by env var */ - null -}; enum { plain_term, ansi_term, color_term } _g_term_type[3]; enum alert {a_notice, a_warn, a_debug, a_fatal = 1 << 8}; pstr alert_msg[] = { @@ -147,8 +137,22 @@ write(2,"\n",1); if (kind & a_fatal) exit(kind & (~a_fatal)); } + +#ifdef _CLIPBOARD +char* const* +cbd_cmds[] = { + /* NOTE: these commands must be specified in order of + * most- to least-specific. more than one utility may + * be present on a given system, so we need to make sure + * the right one is called. */ + (char* const[]){"termux-clipboard-set", null}, + (char* const[]){"xsel", "-bi", null}, + /* TODO: allow command to be specified by env var */ + null +}; + enum bad copy(const char* str, size_t len) { alert(a_debug, "copying password to clipboard"); @@ -459,26 +463,40 @@ dbunlock(byte* priv_enc, byte* salt, byte* priv) { const size_t priv_sz = sizeof(key_priv); byte key [db_privkey_len]; - password dbpw; size_t pwlen; - bad e = pwread(true, dbpw, &pwlen,_str("database key: ")); - if (e != ok) return e; - - alert(a_debug, "deriving secret"); - if(crypto_pwhash(key, sz(key), dbpw, pwlen, salt, - crypto_pwhash_OPSLIMIT_INTERACTIVE, - crypto_pwhash_MEMLIMIT_INTERACTIVE, - crypto_pwhash_ALG_DEFAULT) != 0) { - return bad_mem; - } - hexdump(key, sz(key)); - - alert(a_debug, "attempting to decrypt private key"); - for (size_t i = 0; i < sz(key); ++i) { - priv[i] = priv_enc[i] ^ key[i]; - } - hexdump(priv, sz(key)); + /* is the private key loaded into memory? */ + int shm = shmget(*((key_t*) salt), sizeof(key_priv), 0); + if (shm == -1) { + /* no key in memory - read password from stdin instead */ + password dbpw; size_t pwlen; + bad e = pwread(true, dbpw, &pwlen,_str("database key: ")); + if (e != ok) return e; + + alert(a_debug, "deriving secret"); + if(crypto_pwhash(key, sz(key), dbpw, pwlen, salt, + crypto_pwhash_OPSLIMIT_INTERACTIVE, + crypto_pwhash_MEMLIMIT_INTERACTIVE, + crypto_pwhash_ALG_DEFAULT) != 0) { + return bad_mem; + } + hexdump(key, sz(key)); + + alert(a_debug, "attempting to decrypt private key"); + for (size_t i = 0; i < sz(key); ++i) { + priv[i] = priv_enc[i] ^ key[i]; + } + hexdump(priv, sz(key)); + } else { + /* found a key in memory; loading it into *priv */ + alert(a_notice, "using saved key"); + key_priv* saved = shmat(shm, 0, 0); + if (saved == (void*)-1) + return bad_shm; + hexdump((byte*)saved, sizeof(key_priv)); + memcpy(priv, saved, sizeof(key_priv)); + shmdt(saved); + } return ok; } @@ -548,10 +566,11 @@ _g_binary_name = argv[0]; enum genmode mode = lower; - enum {usage,getpw,addpw,delpw,lspw,genpw,regen, - chpw,keyin,logout,createdb,rekeydb,help} + enum {usage,getpw,addpw,delpw,lspw, + genpw,regen,mergedb,chpw,keyin, + logout,createdb,rekeydb,help} op = getpw; const char* params[3]; uint8_t param = 0; @@ -594,14 +613,50 @@ if (sodium_init() < 0) return bad_lib_sodium_init; switch(op) { + case logout: + case keyin: { + if (param != 0) return bad_syntax; + + int db = dbopen(O_RDONLY); + key_pub pub; + key_priv priv, priv_enc; + byte salt [crypto_pwhash_SALTBYTES], + salt_enc [crypto_box_SEALBYTES + sz(salt)]; + + bad e; + if ((e = dbheader_load(db, salt, salt_enc, pub, priv_enc)) != ok) + return e; + + key_t ipck = *((key_t*)salt); + if (op == keyin) { + if ((e = dbunlock(priv_enc, salt, priv)) != ok) + return e; + if ((e = dbverify(salt, salt_enc, pub, priv)) != ok) + return e; + int shm = shmget(ipck, sizeof(key_priv), + IPC_CREAT | IPC_EXCL | 0600); + if (shm == -1) return bad_shm_exist; + + key_priv* saved = shmat(shm, 0, 0); + if (saved == (void*)-1) return bad_shm; + memcpy(saved, priv, sz(priv)); + shmdt(saved); + } else { + int shm = shmget(ipck, sizeof(key_priv), 0); + if (shm == -1) return bad_no_shm; + shmctl(shm, IPC_RMID, NULL); + } + + return ok; + } case genpw: case addpw: { if (param == 0) return emit_usage( op == addpw ? " -a[p] []\n" : - /* genpw */" -g[lmup] []\n"); + /* genpw */" -g[lmusp] []\n"); if (param > 2 || param < 1) return bad_syntax; const char* acct = params[0], * prm = (param == 2 ? params[1] : NULL); @@ -623,13 +678,13 @@ if (op == addpw) { if (prm == NULL) { pstr prompt_l[] = { _p("- new password for "), {0, acct}, _p(": "), }; - char prompt[pstrsum(prompt_l, sz(prompt_l)) + 1]; + char prompt[pstrsum(prompt_l, sz(prompt_l))]; if (tty_in) pstrcoll(prompt_l, sz(prompt_l), prompt); bad e = pwread(!print, pw, &pwlen, - prompt, sz(prompt) - 1); + prompt, sz(prompt)); if (e != ok) return e; if (tty_in && !print) { password pw_conf; e = pwread(true, pw_conf, NULL, _str("- confirm: ")); @@ -637,9 +692,9 @@ if (strcmp(pw,pw_conf) != 0) return bad_pw_match; } acct_pw = pw; - } else acct_pw = prm; + } else acct_pw = prm, pwlen = strlen(prm); } else if (op == genpw) { unsigned long long len; if (prm != NULL) { @@ -656,9 +711,11 @@ if(tty_out) write(1, "\n", 1); } pwlen = len; } - if (copy_pw) copy(pw, pwlen); +# ifdef _CLIPBOARD + if (copy_pw) copy(pw, pwlen); +# endif alert(a_debug, "encoding database entry"); db_sz acctlen = strlen(acct); byte plaintext[1 + acctlen + 1 + pwlen]; @@ -682,13 +739,22 @@ close(db); break; } + case chpw: + case regen: case delpw:{ /* kpw -d */ - if (param == 0) return emit_usage(" -d \n"); - if (param != 1) return bad_syntax; + if (param == 0) return emit_usage + (op==delpw ? " -d \n" : + op==regen ? " -r[lmusp] []" : + /* op==chpw */ " -c []"); + + if (param < 1 || param > (op == delpw ? 1 : 2)) + return bad_syntax; const char* target = params[0]; - bad e; + const char* delta; + if (param == 2) delta=params[1]; + else delta=NULL; int db = dbopen(O_RDWR); if (db == -1) return bad_db_load; @@ -700,8 +766,9 @@ byte salt [crypto_pwhash_SALTBYTES], salt_enc [crypto_box_SEALBYTES + sz(salt)]; + bad e; if ((e = dbheader_load(db, salt, salt_enc, pub, priv_enc)) != ok) return e; if ((e = dbunlock(priv_enc, salt, priv)) != ok) return e; @@ -736,13 +803,88 @@ if((d = dbnext(db, &rec, ctlen, pub, priv, ctbuf, ptbuf)) != ok) return d; if(strncmp(target, rec.acct.ptr, rec.acct.len) == 0) { - /* found a matching record; do not copy it */ - alert(a_debug, "found record to delete"); + /* found a matching record; determine + * what to do to the fucker */ + alert(a_debug, "found target record"); found = true; + if (op == delpw) continue; + + password pwbuf; + const char* newpass; + size_t pwlen; + if (op == regen) { + alert(a_debug, "generating new password"); + /* generating a new password. use the default + * length if the user hasn't supplied one herself, + * or if she has, convert it to an integer. */ + if (delta == NULL) pwlen = default_pw_len; else { + unsigned long long value; + bad k = katoi(10, delta, &value); + if (k != ok) return bad_num; + pwlen = value; + } + bad m = mkpw(mode, pwbuf, pwlen); + if (m != ok) return m; + newpass = pwbuf; + } else if (op == chpw) { + /* the user has requested a password change. take + * it from the command line if available, otherwise + * generate a prompt and read from stdin */ + + if (delta == NULL) { + pstr prompt_l[] = { _p("- new password for "), + {0, target}, _p(": "), }; + char prompt[pstrsum(prompt_l, sz(prompt_l))]; + if (_g_term_type[0] > plain_term) + pstrcoll(prompt_l, sz(prompt_l), prompt); + + bad p = pwread(!print, pwbuf, &pwlen, prompt, sz(prompt)); + if (p != ok) return p; + /* prompt again to make sure the user entered + * her new password correctly */ + if(!print && _g_term_type[0] > plain_term) { + password passconf; + p = pwread(!print, passconf, NULL, _str("confirm: ")); + if (p != ok) return p; + if (strcmp(passconf, pwbuf) != 0) + return bad_pw_match; + } + newpass = pwbuf; + } else newpass = delta, pwlen = strlen(delta); + } else return bad_assert; + + if (op == regen && print) { + write(1, newpass, pwlen); + if (_g_term_type[1] > plain_term) write(1, "\n", 1); + } + +# ifdef _CLIPBOARD + if (copy_pw) copy(newpass, pwlen); +# endif + + /* new pw is pointed to by `newpass`. encrypt it + * and insert it into the new database image */ + byte plaintext [1 + rec.acct.len + + 1 + pwlen ]; + plaintext[0] = rec.acct.len; + memcpy(plaintext + 1, rec.acct.ptr, rec.acct.len); + plaintext[1 + rec.acct.len] = pwlen; + memcpy(plaintext + 2 + rec.acct.len, newpass, pwlen); + + alert(a_debug, "enciphering plaintext of modified record"); + hexdump(plaintext, sz(plaintext)); + + byte ciphertext [sz(plaintext) + crypto_box_SEALBYTES]; + crypto_box_seal(ciphertext, plaintext, sz(plaintext), pub); + db_sz new_ct_len = sz(ciphertext); + + alert(a_debug, "copying ciphertext into database"); + cursor = transcribe(cursor, &new_ct_len, 1); + cursor = transcribe(cursor, ciphertext, sz(ciphertext)); } else { - /* not our target, copy it into the buffer */ + /* not our target, copy it into the buffer as-is */ cursor = transcribe(cursor, &ctlen, sizeof(db_sz)); cursor = transcribe(cursor, ctbuf, sz(ctbuf)); } @@ -759,9 +901,9 @@ /* we're done here, time to close up shop */ close(db); - break; + return ok; } case getpw: /* kpw */ case lspw: { /* kpw -t[p] [] */ @@ -813,11 +955,13 @@ if(_g_term_type[1] > plain_term) write(1, "\n", 1); } - if (_g_term_type[1] > plain_term) { - if (copy_pw) copy(rec.pw.ptr, rec.pw.len); - } +# ifdef _CLIPBOARD + if (_g_term_type[1] > plain_term) { + if (copy_pw) copy(rec.pw.ptr, rec.pw.len); + } +# endif found = true; break; } } @@ -825,9 +969,9 @@ if (scanned == 0) return bad_db_empty; if (result != fail) return result; else if (!found) return bad_index; - break; + return ok; } case createdb: { /* kpw -C [] */ alert(a_debug, "creating new database"); @@ -927,18 +1071,12 @@ write(db, salt_enc, sz(salt_enc)); close(db); alert(a_debug, "database created"); - break; + return ok; } } -# ifdef _CLIPBOARD - if (copy_pw) { - /* copy(buf,len); */ - } -# endif - return ok; } int