Overview
Comment: | add kpw source |
---|---|
Downloads: | Tarball | ZIP archive | SQL archive |
Timelines: | family | ancestors | descendants | both | trunk |
Files: | files | file ages | folders |
SHA3-256: |
a3ccd193d5e01d37bba34a8e88b4a0d3 |
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 }