util  Check-in [c2ffe127f3]

Overview
Comment:updates
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA3-256: c2ffe127f32fd6e37b3c9df9523acdd864cf98edffc21f684825baef104ab6b8
User & Date: lexi on 2019-08-15 08:34:03
Other Links: manifest | tags
Context
2019-08-15
11:24
FUCK THIS SHIT check-in: 218f1d20c2 user: lexi tags: trunk
08:34
updates check-in: c2ffe127f3 user: lexi tags: trunk
05:45
add kpw source check-in: a3ccd193d5 user: lexi tags: trunk
Changes

Modified kpw.d/errtab from [203a9ab500] to [59271087af].

1
2
3

4
5
6
7
8

9
10


11
12
13
14
15
16
ok			completed successfully				debug	0
fail		unknown error
mem			out of memory

user		kpw started as wrong user
option		unrecognized option passed
syntax		invalid invocation syntax
entropy		could not acquire entropy
copy		could not copy password

insane		your environment is not sane
db_create	database could not be created


cancel		user canceled operation				notice
pw			invalid password
pw_match	passwords do not match
usage		usage displayed to user				debug	64
lib				unspecified library error		fatal	128
lib_sodium_init	could not initialize libsodium	fatal



>





>


>
>





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

Modified kpw.d/kpw.c from [f865f78c63] to [f4297483ea].

13
14
15
16
17
18
19



20
21
22
23
24
25
26
..
29
30
31
32
33
34
35

36
37
38
39
40
41
42
..
48
49
50
51
52
53
54

55
56
57
58
59
60
61
..
82
83
84
85
86
87
88

89
90
91
92
93
94
95
96
97
98
99
100
101







































102
103























104
105
106
107
108
109
110
111
112
113
114
115
...
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
...
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
...
226
227
228
229
230
231
232
233
234
235
236
237
238

239
240
241
242
243
244
245
...
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334

335
336
337
338
339
340




341
342
343
344
345
346
347
...
353
354
355
356
357
358
359









360
361
362
363
364
365
366
367
368

369


























































370
371
372
373
374
375
376
377

378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
...
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
...
442
443
444
445
446
447
448

449
450
451
452
453
454
455
456
457




458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516




















517
518
519
520
521
522
523
524
525
526
527
 *    systems, but should run fine on Linux as well
 *    as BSDs with getrandom() support.
 *  ! for getrandom() to work with the version of
 *    libc on my android phone, the getrandom() call
 *    had to be converted to use the syscall()
 *    interface. this is unlikely to cause problems,
 *    but should be kept in mind.



 */

#include <unistd.h>
#include <sys/random.h>
#include <sys/syscall.h>
#include <stddef.h>
#include <stdint.h>
................................................................................
#include <fcntl.h>
#include <sodium.h>
#include <termios.h>


#define sz(a) ( sizeof (a) / sizeof (a) [0] )
#define say(x) (write(2, (x), sizeof (x)))


#ifdef _CLIPBOARD
#	include <sys/types.h>
#	include <pwd.h>
#	include <stdlib.h>
#else
#   define copy(str,len)
................................................................................

#include "err.inc"

enum /* db format constants */ {
	db_pubkey_len = crypto_box_PUBLICKEYBYTES,
	db_privkey_len = crypto_box_SECRETKEYBYTES,
	kpw_db_pw_max = 64,

};

typedef _Bool bool;
typedef unsigned long long iaia_word_type;
typedef bad iaia_error_type;
enum /* iaia errors */ {
	iaia_e_ok = ok,
................................................................................

#define str_ucase "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
#define str_lcase "abcdefghijklmnopqrstuvwxyz"
#define str_num "0123456789" 
const char* reftbl = str_num str_ucase str_lcase;
const char* reftbl_lcase = str_num str_lcase;


#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
};








































bad
copy(const char* str, size_t len) {























	char* const clipboard_env = getenv("mkpw_clipboard_setter");
	char* const clipboard_env_arg = getenv("mkpw_clipboard_setter_arg");
	// FIXME: allow multiple args
	int fds[2];
	if (pipe(fds) != 0) return 63;
	if (!fork()) {
		close(fds[1]);
		dup2(fds[0], 0);
		if (clipboard_env != null) {
			execvp(clipboard_env, (char* const[]){
					clipboard_env, clipboard_env_arg, null});
			return bad_copy;
................................................................................
		return ok;
	}
}
#endif

enum genmode { upper, mix, lower, stupid };

bad
mkpw(enum genmode mode, char* buf, size_t const len) {
	const unsigned char chars = (sizeof str_num - 1) +
		((mode == upper) ? (sizeof str_ucase - 1) :
		((mode == lower) ? (sizeof str_lcase - 1) :
			((sizeof str_ucase - 1) + (sizeof str_lcase - 1))));
	const char* tbl = (mode == upper) ? reftbl :
			((mode == lower) ? reftbl_lcase : reftbl);
................................................................................
	pstr account;
	pstr pw;
};

enum term_clear {term_clear_line,
                 term_clear_screen};

enum alert {a_notice, a_warn, a_debug, a_fatal = 1 << 8};
pstr alert_msg[] = {
	_p("(notice)"), _p("(warn)"),
	_p("(debug)"),  _p("(fatal)")
};

bool _g_alert_quiet = false,
     _g_debug_msgs  = false;
void alert(uint16_t kind, const char* msg) {
	if (!((kind >= a_fatal) ||
	      (_g_debug_msgs && kind == a_debug) ||
	      (!_g_alert_quiet && kind != a_debug))) return;

	uint8_t idx;
	if (kind & a_fatal) idx = a_debug + 1;
		else idx = kind;
	

	if (isatty(2)) {
		char msgcode[] = "\x1b[1;30m";
		char* color = msgcode+5;
		*color = '0' + (4 - idx);
		write(2,msgcode, sz(msgcode));
	}

	write(2,alert_msg[idx].ptr,alert_msg[idx].len);
	if (isatty(2)) write(2,"\x1b[m ",4);
		else write(2," ",1);
	write(2,msg,strlen(msg));
	write(2,"\n",1);

	if (kind & a_fatal) exit(kind & (~a_fatal));
}

void term_clear(enum term_clear behavior) {
	switch(behavior) {
		case term_clear_line:   write(1,"\r\x1b[2K",5); break;
		case term_clear_screen: write(1,"\r\x1b[3J",5); break;
	}
}
void term_bell() {
	write(1,"\a",1);
}

typedef char password[kpw_db_pw_max + 1];
bad pwread(char* dest, size_t* out_len, const char* prompt, const size_t plen) {
	if (isatty(0)) {
		struct termios initial; {
			/* in order to take PW input, we need to shut
			 * off echo and canonical mode. now we're in
			 * charge of reading each keypress. */
			tcgetattr(1, &initial);
			struct termios nt = initial;
................................................................................
		}

		*dest = 0;
		char* p = dest;

		do {
			term_clear(term_clear_line);
			write(1, "\x1b[1m", 4);
			write(1, prompt, plen);
			write(1, "\x1b[21m", 5);

			for(size_t i = 0; i < p - dest; ++i)
				write(1, "*", 1);


			char c;
			if (read(0, &c, 1) == 1) {
				switch (c) {
					case '\n': case '\r':
						/* accept pw */
						if (p > dest) goto end_read_loop;
................................................................................
	}

	return db;
}

int
kpw(int argc, const char** argv) {
	if (argc == 0) return -1;
	if (argc == 1) { 
		size_t namelen = strlen(argv[0]);
		say("\x1b[1musage:\x1b[m ");
		write(2, argv[0], namelen);
		write(2, kpw_optstr, sz(kpw_optstr));
		write(2, kpw_usage,  sz(kpw_usage));
		return bad_usage;
	}

	enum genmode
		mode = lower;
	enum {usage,getpw,addpw,delpw,genpw,
	      chpw,keyin,logout,createdb,rekeydb}
		op = getpw;

	const char* params[3];
	uint8_t param = 0;

	bool print = false,
		 clobber = false;

#	ifdef _CLIPBOARD
		bool copy_pw = true;
#	endif
	for (const char** arg = argv + 1; *arg != null; ++arg) {
		if ((*arg)[0] == '-') {
			if ((*arg)[1] == '-') { /* long opt */




				unsigned char a;
				if (tblget(sz(argtbl), argtbl, *arg + 2, &a) == ok) switch (a) {
					kpw_emit_long_option_switch
				} else {
					return bad_option;
				}
			} else { /* short opt */
................................................................................
				}
			}
		} else {
			if (param > sz(params)) return bad_syntax;
			params[param++] = *arg;
		}
	}










	if (sodium_init() < 0) 
		return bad_lib_sodium_init;

	switch(op) {
		case getpw:{ /* kpw <acct> */
			break;
		}


		case addpw:{ /* kpw -a <acct> [<pw>] */


























































			break;
		}

		case delpw:{ /* kpw -d <acct> */
			break;
		}

		case genpw:{ /* kpw -g[lmu] <acct> [<len>] */

			break;
		}

		case createdb: { /* kpw -C [<db>] */
			alert(a_debug, "creating new database");
			if (clobber)
				alert(a_warn, "will clobber any existing database");
			/* before we open our new file, we need to generate
			 * our keypairs.  the private key will be encrypted
			 * with a blake2 hash of a user-supplied passphrase 
			 * and stored in the database after the unencrypted
			 * public key.
			 */
			uint8_t pub [crypto_box_PUBLICKEYBYTES],
			        priv[crypto_box_SECRETKEYBYTES];
			uint8_t priv_enc[sz(priv)];

			alert(a_debug, "generating keypair");
			crypto_box_keypair(pub, priv);

			/* kpw   works  very   differently  compared   to
			 * most  password  managers. it  uses  public-key
................................................................................
			 * encryption so that new  passwords can be saved
			 * to the  db without bothering the  user for the
			 * password.  now we  have  our  keypair, we  can
			 * prompt  the user for a secret  passphrase with
			 * which to encrypt the private key with.
			 */


			alert(a_notice, "database keypair generated, encrypting");
			password dbpw;
			size_t pwlen;
#			define _str(s) (s),sizeof(s)
			bad e = pwread(dbpw, &pwlen, _str("database passphrase: "));
			if (e != ok) return e;
			if (isatty(0)) {
				password dbpw_conf;
				e = pwread(dbpw_conf, NULL, _str("confirm: "));
#				undef _str
				if (e != ok) return e;

				if(strcmp(dbpw,dbpw_conf) != 0)
					return bad_pw_match;
			}

			uint8_t salt[crypto_pwhash_SALTBYTES],
................................................................................
			crypto_box_seal(salt_enc, salt, sz(salt), priv);

			/* we have everything we need. now we create the
			 * file, failing  if it already exists so as not
			 * to clobber anyone's passwords.
			 */
			int db;

			const int flags = O_CREAT | (clobber ? O_TRUNC : O_EXCL);
			if(param == 0)
				db = dbopen(flags);
			else if (param == 1)
				db = open(params[0], flags, 0600);
			else return bad_syntax;

			if (db == -1) return bad_db_create;





			write(db, pub, sz(pub));
			write(db, salt, sz(salt));
			write(db, priv_enc, sz(priv_enc));
			write(db, salt_enc, sz(salt_enc));
			close(db);

			for (int i = 0; i < sz(key); ++i) {
				printf("%2x ", key[i]);
				if (!((i+1)%8)) printf("\n");
			}
			break;
		}
	}

#	ifdef _CLIPBOARD
	if (geteuid() == 0 && copy_pw) {
		/* on a sane system, what we'd do is hike up the process
		 * tree til we found a non-root user. alas, this is UNIX. */
		const char* realuser = getenv("SUDO_USER");
		if (realuser == null) realuser = "nobody";

		say("\x1b[1;33mwarning:\x1b[m you are running \x1b[4m");
			size_t namelen = strlen(argv[0]);
			write(2,argv[0],namelen);
		say("\x1b[24m as \x1b[1mroot\x1b[m! dropping to \x1b[1m");
			write(2,realuser,strlen(realuser));
			setenv("USER", realuser, true);
		say("\x1b[m to prevent malicious behavior\n");

		struct passwd* nobody = getpwnam(realuser);
		if (nobody == null) {
			say("\x1b[1;31mfatal:\x1b[m could not get UID to drop privileges; bailing");
			return bad_user;
		} else {
			setenv("HOME", nobody -> pw_dir, true);
			setenv("SHELL", "/dev/null", true);
			setuid(nobody -> pw_uid);
		}

	}
#	endif
	

	/* char buf[len+1]; */
	/* *buf = 0; */
	/* mkpw(mode,buf,len); */
	/* write(1, buf, len + 1); */

#	ifdef _CLIPBOARD
		if (copy_pw) {
			copy(buf,len);
		}
#	endif
	
	return ok;
}

int
main (int argc, const char** argv) {




















	int e = 0;
	const char* msg;
	uint16_t level;
	uint8_t rv;
	switch (e = kpw(argc, argv)) {
		kpw_emit_error_switch
	}

	alert(level, msg);
	return rv;
}







>
>
>







 







>







 







>







 







>













>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
|

>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>




|







 







|







 







<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<











|







 







|

|

|

>







 







|
|
<
<
<
<
<
<
<



|







|
>




|

>
>
>
>







 







>
>
>
>
>
>
>
>
>









>
|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







<
>













|
|







 







<



|
<

|

|
<







 







>
|








>
>
>
>






|
<
<
<




<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<









|








>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>











13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
..
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
..
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
..
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
...
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
...
232
233
234
235
236
237
238


































239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
...
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
...
342
343
344
345
346
347
348
349
350







351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
...
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477

478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
...
501
502
503
504
505
506
507

508
509
510
511

512
513
514
515

516
517
518
519
520
521
522
...
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567



568
569
570
571



























572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
 *    systems, but should run fine on Linux as well
 *    as BSDs with getrandom() support.
 *  ! for getrandom() to work with the version of
 *    libc on my android phone, the getrandom() call
 *    had to be converted to use the syscall()
 *    interface. this is unlikely to cause problems,
 *    but should be kept in mind.
 *
 *  TODO prevent pw reads from going off the edge of
 *       the screen and fucking up all the shit
 */

#include <unistd.h>
#include <sys/random.h>
#include <sys/syscall.h>
#include <stddef.h>
#include <stdint.h>
................................................................................
#include <fcntl.h>
#include <sodium.h>
#include <termios.h>


#define sz(a) ( sizeof (a) / sizeof (a) [0] )
#define say(x) (write(2, (x), sizeof (x)))
#define _str(s) (s),sizeof(s)

#ifdef _CLIPBOARD
#	include <sys/types.h>
#	include <pwd.h>
#	include <stdlib.h>
#else
#   define copy(str,len)
................................................................................

#include "err.inc"

enum /* db format constants */ {
	db_pubkey_len = crypto_box_PUBLICKEYBYTES,
	db_privkey_len = crypto_box_SECRETKEYBYTES,
	kpw_db_pw_max = 64,
	default_pw_len = 32,
};

typedef _Bool bool;
typedef unsigned long long iaia_word_type;
typedef bad iaia_error_type;
enum /* iaia errors */ {
	iaia_e_ok = ok,
................................................................................

#define str_ucase "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
#define str_lcase "abcdefghijklmnopqrstuvwxyz"
#define str_num "0123456789" 
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[] = {
	_p("(kpw notice)"), _p("(kpw warn)"),
	_p("(kpw debug)"),  _p("(kpw fatal)")
};

bool _g_alert_quiet = false,
     _g_debug_msgs  = false;
void alert(uint16_t kind, const char* msg) {
	if (!((kind >= a_fatal) ||
	      (_g_debug_msgs && kind == a_debug) ||
	      (!_g_alert_quiet && kind != a_debug))) return;

	uint8_t idx;
	if (kind & a_fatal) idx = a_debug + 1;
		else idx = kind;
	

	if (_g_term_type[2] == color_term) {
		char msgcode[] = "\x1b[90m";
		char* color = msgcode+3;
		*color = '0' + (4 - idx);
		write(2,msgcode, sz(msgcode));
	} else if (_g_term_type[2] == ansi_term) {
		write(2,"\x1b[1m",4);
	}

	write(2,alert_msg[idx].ptr,
			alert_msg[idx].len);

	if (_g_term_type[2] != plain_term) write(2,"\x1b[m ",4);
		else write(2," ",1);
	write(2,msg,strlen(msg));
	write(2,"\n",1);

	if (kind & a_fatal) exit(kind & (~a_fatal));
}

enum bad
copy(const char* str, size_t len) {
	alert(a_debug, "copying password to clipboard");
	if (geteuid() == 0) {
		/* on a sane system, what we'd do is hike up the process
		 * tree til we found a non-root user. alas, this is UNIX. */
		const char* realuser = getenv("SUDO_USER");
		if (realuser == null) realuser = "nobody";

		alert(a_warn, "running as root! dropping privileges to prevent malicious use of copy functionality");
		setenv("USER", realuser, true);

		struct passwd* nobody = getpwnam(realuser);
		if (nobody == null) {
			alert(a_fatal | bad_user, "could not get UID to drop privileges; bailing");
			return bad_user;
		} else {
			setenv("HOME", nobody -> pw_dir, true);
			setenv("SHELL", "/dev/null", true);
			setuid(nobody -> pw_uid);
			if (geteuid() == 0)
				alert(a_fatal | bad_user, "i don't fucking think so, you sneaky bastard");
		}

	}
	char* const clipboard_env = getenv("mkpw_clipboard_setter");
	char* const clipboard_env_arg = getenv("mkpw_clipboard_setter_arg");
	// FIXME: allow multiple args
	int fds[2];
	if (pipe(fds) != 0) return bad_pipe;
	if (!fork()) {
		close(fds[1]);
		dup2(fds[0], 0);
		if (clipboard_env != null) {
			execvp(clipboard_env, (char* const[]){
					clipboard_env, clipboard_env_arg, null});
			return bad_copy;
................................................................................
		return ok;
	}
}
#endif

enum genmode { upper, mix, lower, stupid };

enum bad
mkpw(enum genmode mode, char* buf, size_t const len) {
	const unsigned char chars = (sizeof str_num - 1) +
		((mode == upper) ? (sizeof str_ucase - 1) :
		((mode == lower) ? (sizeof str_lcase - 1) :
			((sizeof str_ucase - 1) + (sizeof str_lcase - 1))));
	const char* tbl = (mode == upper) ? reftbl :
			((mode == lower) ? reftbl_lcase : reftbl);
................................................................................
	pstr account;
	pstr pw;
};

enum term_clear {term_clear_line,
                 term_clear_screen};



































void term_clear(enum term_clear behavior) {
	switch(behavior) {
		case term_clear_line:   write(1,"\r\x1b[2K",5); break;
		case term_clear_screen: write(1,"\r\x1b[3J",5); break;
	}
}
void term_bell() {
	write(1,"\a",1);
}

typedef char password[kpw_db_pw_max + 1];
bad pwread(bool obscure, char* dest, size_t* out_len, const char* prompt, const size_t plen) {
	if (isatty(0)) {
		struct termios initial; {
			/* in order to take PW input, we need to shut
			 * off echo and canonical mode. now we're in
			 * charge of reading each keypress. */
			tcgetattr(1, &initial);
			struct termios nt = initial;
................................................................................
		}

		*dest = 0;
		char* p = dest;

		do {
			term_clear(term_clear_line);
			if (_g_term_type[1] >= ansi_term) write(1, "\x1b[1m", 4);
			write(1, prompt, plen);
			if (_g_term_type[1] >= ansi_term) write(1, "\x1b[21m", 5);

			if (obscure) for(size_t i = 0; i < p - dest; ++i)
				write(1, "*", 1);
			else write(1, dest, p-dest);

			char c;
			if (read(0, &c, 1) == 1) {
				switch (c) {
					case '\n': case '\r':
						/* accept pw */
						if (p > dest) goto end_read_loop;
................................................................................
	}

	return db;
}

int
kpw(int argc, const char** argv) {
	if (argc == 0) return bad_insane;
	_g_binary_name = argv[0];








	enum genmode
		mode = lower;
	enum {usage,getpw,addpw,delpw,lspw,genpw,
	      chpw,keyin,logout,createdb,rekeydb}
		op = getpw;

	const char* params[3];
	uint8_t param = 0;

	bool print = false,
		 clobber = false,
		 no_more_opts = false;
#	ifdef _CLIPBOARD
		bool copy_pw = true;
#	endif
	for (const char** arg = argv + 1; *arg != null; ++arg) {
		if (!no_more_opts && (*arg)[0] == '-') {
			if ((*arg)[1] == '-') { /* long opt */
				if((*arg)[2] == 0) {
					no_more_opts = true;
					continue;
				}
				unsigned char a;
				if (tblget(sz(argtbl), argtbl, *arg + 2, &a) == ok) switch (a) {
					kpw_emit_long_option_switch
				} else {
					return bad_option;
				}
			} else { /* short opt */
................................................................................
				}
			}
		} else {
			if (param > sz(params)) return bad_syntax;
			params[param++] = *arg;
		}
	}

	if (op == getpw && param == 0) { 
		size_t namelen = strlen(argv[0]);
		say("\x1b[1musage:\x1b[m ");
		write(2, argv[0], namelen);
		write(2, kpw_optstr, sz(kpw_optstr));
		write(2, kpw_usage,  sz(kpw_usage));
		return bad_usage;
	}

	if (sodium_init() < 0) 
		return bad_lib_sodium_init;

	switch(op) {
		case getpw:{ /* kpw <acct> */
			break;
		}

		case genpw:   /* kpw -g[lmu] <acct> [<len>] */
		case addpw: { /* kpw -a      <acct> [<pw>] */
			if (param > 2 || param < 1) return bad_syntax;
			const char* acct = params[0],
			          * prm = (param == 2 ? params[1] : NULL);

			alert(a_debug, "opening database");
			int db = dbopen(O_RDWR);
			if (db == -1) return bad_db_load;
			alert(a_debug, "reading in public key");
			uint8_t key [db_pubkey_len];
			ssize_t e = read(db, key, sz(key));
			if(e < sz(key)) return bad_db_corrupt;

			lseek(db, 0, SEEK_END);
			bool tty_in = isatty(0),
				 tty_out = isatty(1);

			password pw; size_t pwlen;
			const char* acct_pw;
			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];
					if (tty_in) pstrcoll(prompt_l, sz(prompt_l), prompt);

					bad e = pwread(!print, pw, &pwlen,
							prompt, sz(prompt) - 1);
					if (e != ok) return e;
					if (tty_in && !print) {
						password pw_conf;
						e = pwread(true, pw_conf, NULL, _str("- confirm: "));
						if (e != ok) return e;
						if (strcmp(pw,pw_conf) != 0)
							return bad_pw_match;
					}
					acct_pw = pw;
				} else acct_pw = prm;

			} else if (op == genpw) {
				unsigned long long len; 
				if (prm != NULL) {
					alert(a_debug, "converting length parameter to integer");
					bad e = katoi(10, prm, &len);
					if (e != ok) return bad_num;
				} else alert(a_debug, "using default password length"),
				       len = default_pw_len;

				alert(a_debug, "generating new password");
				mkpw(mode, pw, len);
				if (print || !tty_out) {
					write(1, pw, len);
					if(tty_out) write(1, "\n", 1);
				}
				pwlen = len;
			}
			if (copy_pw) copy(pw, pwlen);
			alert(a_debug, "enciphering password");

			break;
		}

		case delpw:{ /* kpw -d <acct> */
			break;
		}


		case lspw: { /* kpw -t[p] [<prefix>] */
			break;
		}

		case createdb: { /* kpw -C [<db>] */
			alert(a_debug, "creating new database");
			if (clobber)
				alert(a_warn, "will clobber any existing database");
			/* before we open our new file, we need to generate
			 * our keypairs.  the private key will be encrypted
			 * with a blake2 hash of a user-supplied passphrase 
			 * and stored in the database after the unencrypted
			 * public key.
			 */
			uint8_t pub [db_pubkey_len],
			        priv[db_privkey_len];
			uint8_t priv_enc[sz(priv)];

			alert(a_debug, "generating keypair");
			crypto_box_keypair(pub, priv);

			/* kpw   works  very   differently  compared   to
			 * most  password  managers. it  uses  public-key
................................................................................
			 * encryption so that new  passwords can be saved
			 * to the  db without bothering the  user for the
			 * password.  now we  have  our  keypair, we  can
			 * prompt  the user for a secret  passphrase with
			 * which to encrypt the private key with.
			 */


			alert(a_notice, "database keypair generated, encrypting");
			password dbpw;
			size_t pwlen;
			bad e = pwread(!print, dbpw, &pwlen, _str("- database passphrase: "));

			if (e != ok) return e;
			if (!print && isatty(0)) {
				password dbpw_conf;
				e = pwread(!print, dbpw_conf, NULL, _str("- confirm: "));

				if (e != ok) return e;

				if(strcmp(dbpw,dbpw_conf) != 0)
					return bad_pw_match;
			}

			uint8_t salt[crypto_pwhash_SALTBYTES],
................................................................................
			crypto_box_seal(salt_enc, salt, sz(salt), priv);

			/* we have everything we need. now we create the
			 * file, failing  if it already exists so as not
			 * to clobber anyone's passwords.
			 */
			int db;
			const int flags = O_CREAT | O_WRONLY |
				(clobber ? O_TRUNC : O_EXCL);
			if(param == 0)
				db = dbopen(flags);
			else if (param == 1)
				db = open(params[0], flags, 0600);
			else return bad_syntax;

			if (db == -1) return bad_db_create;

			/* the file is safely created; all that's left to
			 * do is write out the header and then we can call
			 * it a day.
			 */
			write(db, pub, sz(pub));
			write(db, salt, sz(salt));
			write(db, priv_enc, sz(priv_enc));
			write(db, salt_enc, sz(salt_enc));
			close(db);

			alert(a_debug, "database created");



			break;
		}
	}




























	

	/* char buf[len+1]; */
	/* *buf = 0; */
	/* mkpw(mode,buf,len); */
	/* write(1, buf, len + 1); */

#	ifdef _CLIPBOARD
		if (copy_pw) {
			/* copy(buf,len); */
		}
#	endif
	
	return ok;
}

int
main (int argc, const char** argv) {
	const char* colorterm = getenv("COLORTERM");
	const char* term = getenv("TERM");
	bool color, ansi;

	if (colorterm != NULL)
		color = true;
	else if (term == NULL) 
		ansi = false, color = false;
	else if (strstr(term, "color") == NULL)
		ansi = true, color = false;
	else color = true;

	for (uint8_t i = 0; i < 3; ++i) {
		if(isatty(i)) {
			_g_term_type[i] = (color ? color_term :
			                    ansi ? ansi_term : plain_term);
		} else _g_term_type[i] = plain_term;
	}


	int e = 0;
	const char* msg;
	uint16_t level;
	uint8_t rv;
	switch (e = kpw(argc, argv)) {
		kpw_emit_error_switch
	}

	alert(level, msg);
	return rv;
}

Modified kpw.d/optab from [4bfd28626b] to [652ae10981].

1
2
3
4
5

6
7
8
9
10
11
12
13
14
15
16
17
C	create	op = createdb			create a new password database
R	rekey	op = rekeydb			change database passphrase and key
a	add		op = addpw				add password to the database
g	gen		op = genpw				add account with a random password
d	del		op = delpw				delete password from the database

c	chpw	op = chpw				change password in the database
l	lower	mode = lower			generate lowercase password
m	mix		mode = mix				generate mix-case password
u	upper	mode = upper			generate uppercase password
s	stupid	mode = stupid			circumvent dumb pw restrictions
k	key		op = keyin				save database key in session memory
o	logout	op = logout				delete db key from session memory
n	nocopy	copy_pw = false			don't copy generated pw to clipboard	_CLIPBOARD
p	print	print = true			display generated password
q	quiet	_g_alert_quiet = true	hide non-fatal reports
v	verbose	_g_debug_msgs = true	display debug reports
!	clobber	clobber = true			disable safety checks

|



>







|
|



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

Modified kpw.d/optab.awk from [b43a4e1b77] to [7d6b5c7a46].

11
12
13
14
15
16
17



18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
		print "#define kpw_emit_short_option_switch \\"
		end = "\\"
	} else if (emit == "usage") {
		print "const char kpw_usage[] ="
	}
	globalc = 0
}




function say(line) {
	if (NF == 5) {
		print "\tkpw_only_" $5 "(" line ") " end
		globals[globalc] = $5
		++ globalc
	} else {
		print "\t" line " " end
	}
}

{ optstr = optstr $1 }
emit == "enum"   { say("arg_" $2 ",") }
emit == "argtbl" { say("{ arg_" $2", \"" $2 "\" },") }
emit == "olong"  { say("case arg_" $2 ": " $3 "; break;") }
emit == "oshort" { say("case '" $1 "': " $3 "; break;") }
emit == "usage"  { say("\"\\t-"$1", --"$2": "$4"\\n\"") }

emit == "cond" {
	if (NF == 5 && !($5 in condlist)) {
		condlist[$5] = 1
		print "#ifdef " $5







>
>
>
|











|
|
|







11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
		print "#define kpw_emit_short_option_switch \\"
		end = "\\"
	} else if (emit == "usage") {
		print "const char kpw_usage[] ="
	}
	globalc = 0
}
function cify(str) {
	gsub(/[- ]/, "_", str);
	return str;
}
function say(line) {
	if (NF == 5) {
		print "\tkpw_only_" $5 "(" line ") " end
		globals[globalc] = $5
		++ globalc
	} else {
		print "\t" line " " end
	}
}

{ optstr = optstr $1 }
emit == "enum"   { say("arg_" cify($2) ",") }
emit == "argtbl" { say("{ arg_" cify($2)", \"" $2 "\" },") }
emit == "olong"  { say("case arg_" cify($2) ": " $3 "; break;") }
emit == "oshort" { say("case '" $1 "': " $3 "; break;") }
emit == "usage"  { say("\"\\t-"$1", --"$2": "$4"\\n\"") }

emit == "cond" {
	if (NF == 5 && !($5 in condlist)) {
		condlist[$5] = 1
		print "#ifdef " $5

Modified mkpw.c from [f1f30ece5b] to [9bb8a09cb3].

184
185
186
187
188
189
190


191
192
193
194
195
196
197
		if (nobody == null) {
			say("\x1b[1;31mfatal:\x1b[m could not get UID to drop privileges; bailing");
			return bad_user;
		} else {
			setenv("HOME", nobody -> pw_dir, true);
			setenv("SHELL", "/dev/null", true);
			setuid(nobody -> pw_uid);


		}

	}
#	endif
	
	const unsigned char chars = (sizeof str_num - 1) +
		((mode == upper) ? (sizeof str_ucase - 1) :







>
>







184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
		if (nobody == null) {
			say("\x1b[1;31mfatal:\x1b[m could not get UID to drop privileges; bailing");
			return bad_user;
		} else {
			setenv("HOME", nobody -> pw_dir, true);
			setenv("SHELL", "/dev/null", true);
			setuid(nobody -> pw_uid);
			if (geteuid() == 0)
				say("\x1b[1;31mnice try:\x1b[m i don't fucking think so, you sneaky bastard");
		}

	}
#	endif
	
	const unsigned char chars = (sizeof str_num - 1) +
		((mode == upper) ? (sizeof str_ucase - 1) :

Modified readme.md from [2e8787ac9f] to [dd56c0b568].

1
2
3
4
5

# util
various odds and ends. all code in this repository is licensed under the AGPL unless otherwise noted.

* **safekill.c**: utility to help keep from accidentally killing important windows; compile with `cc -Ofast safekill.c -lX11 -lc -osafekill`
* **bgrd.c**: it’s… a long story. just read the header.






>
1
2
3
4
5
6
# util
various odds and ends. all code in this repository is licensed under the AGPL unless otherwise noted.

* **safekill.c**: utility to help keep from accidentally killing important windows; compile with `cc -Ofast safekill.c -lX11 -lc -osafekill`
* **bgrd.c**: it’s… a long story. just read the header.
* **kpw**: an extremely simple, lightweight, secure password manager for POSIX OSes written in C. depends on libsodium for crypto primitives. compile with `make kpw`.