/* [ʞ] mkpw.c - make password * ~ lexi hale * © AGPLv3 * $ cc -O4 mkpw.c -omkpw [-D_CLIPBOARD] * - D_CLIPBOARD enables mkpw to automatically * copy new passwords to the clipboard. it * does this by attempting to execute a sequence * of binaries, and then writing the password * to STDIN of the binary that succeeds. * ? generates passwords * → mkpw is unlikely to be portable to non-POSIX * 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 #include #include #include #include #include #define sz(a) ( sizeof (a) / sizeof (a) [0] ) #define say(x) (write(2, (x), sizeof (x))) #ifdef _CLIPBOARD # include # include # include # define _cl_opt o('n',nocopy,copy = false) #else # define _cl_opt o('n',nocopy,/* do nothing*/) #endif #define options \ o('l',lower,mode = lower) \ o('m',mix,mode = mix) \ o('u',upper,mode = upper)\ _cl_opt enum /* constants */ { null = 0, true = 1, false = 0 }; typedef enum bad { ok = 0, fail = 1, bad_user, bad_option, bad_syntax, bad_entropy, bad_copy, bad_usage = 64, /* fleabsd idiom */ } bad; typedef _Bool bool; typedef unsigned long long iaia_word_type; typedef bad iaia_error_type; enum /* iaia errors */ { iaia_e_ok = ok, iaia_e_base = fail, iaia_e_domain = fail, iaia_e_overflow = fail, }; #define _IAIA_FN_ATOI katoi #define _IAIA_EXTERNAL_TYPES #include "clib/iaia.c" typedef enum { tbl_ok = ok, tbl_error = fail } tbl_error_type; typedef unsigned char tbl_word_type; #include "clib/tbl.c" enum args { #define o(short, long, code) arg_##long, options #undef o }; struct tblrow argtbl[] = { #define o(short, long, code) {arg_##long, #long}, options #undef o }; #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 }; #endif int main(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); say(" [-lmu" # ifdef _CLIPBOARD "n" # endif "] [--lower] [--mix] [--upper]" # ifdef _CLIPBOARD " [--nocopy]" # endif " []"); return bad_usage; } enum { upper, mix, lower } mode = lower; unsigned long long len, q = 1; bool gotlen = false; bool gotq = false; # ifdef _CLIPBOARD bool copy = true; # endif for (const char** arg = argv; *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) { # define o(short, long, code) case arg_##long:code;break; options # undef o } else { return bad_option; } } else { /* short opt */ for(const char* ptr = (*arg) + 1; *ptr != 0; ++ptr) { switch(*ptr) { # define o(short, long, code) case short:code;break; options # undef o default: return bad_option; } } } } else { if (gotq) return bad_syntax; else if (gotlen) { if (katoi(10, *arg, &q) == ok) { gotq = true; } } else if(katoi(10, *arg, &len) == ok) { gotlen = true; } } } if (!gotlen) return bad_syntax; # ifdef _CLIPBOARD if (geteuid() == 0 && copy) { /* 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); 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) : ((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); char buf[len+1]; /* *buf = 0; */ unsigned char noise[len]; for (size_t i = 0; i