/* * Generate (hopefully) pronounceable random passwords. These can often be * remembered more easily than completely random passwords, and are immune to * dictionary searches, etc. * * The original version of this program was written in BASIC on an OSI * Superboard II SBC. That version is long gone (the SB2's cassette drive * was never trustworthy, it eventually scrambled the tape), but the basic * (pardon the pun) idea lives on here, with a few modification like basing * the selection on "graphs" (actually, they are a restricted set of phonemes) * and having randomly-selected spellings for those graphs. */ #include #include #include #include #include #ifdef ALLBYOPTS #include #endif #ifdef RAND48 extern double drand48(); #define RANDOM(c) ((int) ((drand48() * (c)))) #else #define RANDOM(c) ((int) (rand(c) / 32767.0 * (c))) #endif char *spelling[] = { /*a*/ "a", (char *) 0, /* 2*/ /*A*/ "a", "ae", "ai", (char *) 0, /* 6*/ /*b*/ "b", (char *) 0, /* 8*/ /*ch*/ "ch", (char *) 0, /*10*/ /*d*/ "d", (char *) 0, /*12*/ /*e*/ "e", (char *) 0, /*14*/ /*E*/ "e", "ee", "ie", (char *) 0, /*18*/ /*f*/ "f", "ph", "gh", (char *) 0, /*22*/ /*g*/ "g", (char *) 0, /*24*/ /*h*/ "h", (char *) 0, /*26*/ /*i*/ "i", "e", (char *) 0, /*29*/ /*I*/ "i", "ai", (char *) 0, /*32*/ /*i'*/ "i", "ei", (char *) 0, /*35*/ /*j*/ "j", "g", (char *) 0, /*38*/ /*k*/ "k", "c", (char *) 0, /*41*/ /*l*/ "l", (char *) 0, /*43*/ /*m*/ "m", (char *) 0, /*45*/ /*n*/ "n", (char *) 0, /*47*/ /*ng*/ "ng", (char *) 0, /*49*/ /*o*/ "o", "a", "ah", (char *) 0, /*53*/ /*O*/ "o", "oh", (char *) 0, /*56*/ #ifndef ALLBYOPT /*place */"oo", "u", (char *) 0, /*59*/ /*holder*/"oo", "w", (char *) 0, /*62*/ #else #ifdef ALTPHON /*oo*/ "u", "u", (char *) 0, /*59*/ /*OO*/ "oo", "w", (char *) 0, /*62*/ #else /*oo*/ "oo", "u", (char *) 0, /*59*/ /*OO*/ "oo", "w", (char *) 0, /*62*/ #endif #endif /*p*/ "p", (char *) 0, /*64*/ /*qu*/ "qu", (char *) 0, /*66*/ /*r*/ "r", (char *) 0, /*68*/ /*s*/ "s", "c", (char *) 0, /*71*/ /*sh*/ "sh", "s", (char *) 0, /*74*/ /*t*/ "t", (char *) 0, /*76*/ /*th*/ "th", (char *) 0, /*78*/ /*TH*/ "th", (char *) 0, /*80*/ /*u*/ "u", (char *) 0, /*82*/ /*U*/ "u", "oo", (char *) 0, /*85*/ /*v*/ "v", (char *) 0, /*87*/ /*x*/ "x", (char *) 0, /*89*/ /*y*/ "y", (char *) 0, /*91*/ /*z*/ "z", "s", (char *) 0, /*94*/ }; #ifdef ALLBYOPTS char *alt_phonics[] = { /* 0*/ /*56*/ /*oo*/ "u", "u", (char *) 0, /* 3*/ /*59*/ /*OO*/ "oo", "w", (char *) 0 /* 6*/ /*62*/ }; char *reg_phonics[] = { /* 0*/ /*56*/ /*oo*/ "oo", "u", (char *) 0, /* 3*/ /*59*/ /*OO*/ "oo", "w", (char *) 0 /* 6*/ /*62*/ }; void alter_spelling_table(int wants_alternate_phonics) { char **p; char **q = &spelling[56]; int i; if(wants_alternate_phonics) p = alt_phonics; else p = reg_phonics; for(i = 0; i < 6; ++i) *q++ = *p++; } #endif struct graph { char *graph; char type; #define CONSONANT 0 #define VOWEL_LONG 1 #define VOWEL_SHORT 2 #define VOWEL_OTHER 3 #define VOWEL_MASK 3 #ifdef ALLBYOPTS #define NUMERAL 8 #else #ifdef NUMERALS #define NUMERAL 8 #endif #endif #define iscons(c) (((c)->type & VOWEL_MASK) == 0) #define isvowel(c) (((c)->type & VOWEL_MASK) != 0) /* char frequency; */ /* unused for now */ char **spellings; /* struct graph **following; */ /* maybe later */ } graph[] = { "a", VOWEL_SHORT, &spelling[0], "A", VOWEL_LONG, &spelling[2], "b", CONSONANT, &spelling[6], "ch", CONSONANT, &spelling[8], "d", CONSONANT, &spelling[10], "e", VOWEL_SHORT, &spelling[12], "E", VOWEL_LONG, &spelling[14], "f", CONSONANT, &spelling[18], "g", CONSONANT, &spelling[22], "h", CONSONANT, &spelling[24], "i", VOWEL_SHORT, &spelling[26], "I", VOWEL_LONG, &spelling[29], "i'", VOWEL_OTHER, &spelling[32], "j", CONSONANT, &spelling[35], "k", CONSONANT, &spelling[38], "l", CONSONANT, &spelling[41], "m", CONSONANT, &spelling[43], "n", CONSONANT, &spelling[45], "ng", CONSONANT, &spelling[47], "o", VOWEL_SHORT, &spelling[49], "O", VOWEL_LONG, &spelling[53], "oo", VOWEL_SHORT, &spelling[56], "OO", VOWEL_LONG, &spelling[59], "p", CONSONANT, &spelling[62], "qu", CONSONANT, &spelling[64], "r", CONSONANT, &spelling[66], "s", CONSONANT, &spelling[68], "sh", CONSONANT, &spelling[71], "t", CONSONANT, &spelling[74], "th", CONSONANT, &spelling[76], "TH", CONSONANT, &spelling[78], "u", VOWEL_SHORT, &spelling[80], "U", VOWEL_LONG, &spelling[82], "v", CONSONANT, &spelling[85], "x", CONSONANT, &spelling[87], "y", CONSONANT, &spelling[89], "z", CONSONANT, &spelling[91], 0, 0, &spelling[94], }; struct graph *vowel[] = { &graph[0], &graph[1], &graph[5], &graph[6], &graph[10], &graph[11], &graph[12], &graph[19], &graph[20], &graph[21], &graph[22], &graph[30], &graph[31], (struct graph *) 0, }; struct graph *consonant[] = { &graph[2], &graph[3], &graph[4], &graph[7], &graph[8], &graph[9], &graph[13], &graph[14], &graph[15], &graph[16], &graph[17], &graph[18], &graph[23], &graph[24], &graph[25], &graph[26], &graph[27], &graph[28], &graph[29], &graph[32], &graph[33], &graph[34], &graph[35], (struct graph *) 0, }; /* * Randomly select a graph from the specifield array. Eventually, this should * account for graph frequencies as well. */ struct graph *selgraph(graphs) struct graph **graphs; { register int cnt; for (cnt = 0; graphs[cnt] != (struct graph *) 0; cnt++) ; return graphs[RANDOM(cnt)]; } /* * Randomly select a spelling for the specified graph. This is not linear: * earlier spellings are preferred over later ones, but the latter do * sometimes sneak in. */ char *selspell(graph) struct graph *graph; { register int cnt, sel; for (cnt = 0; graph->spellings[cnt] != (char *) 0; cnt++) ; if (cnt == 0) { fprintf(stderr, "PANIC: selspell(%s) got count(spellings) == 0\n", graph->graph); exit(2); } if (cnt == 1) return *graph->spellings; /* * This may not be the best way to do it... maybe Weemba'd care to lend a * hand here? After all, my specialty is programming, NOT math. */ if ( (sel = cnt - (int) sqrt((double) RANDOM(cnt * cnt) + 1) - 1) < 0 || sel >= cnt ) { #ifdef BUGCATCH fprintf ( stderr, "PANIC: selspell(%s) got nlrand(%d) == %d\n", graph->graph, cnt, sel ); exit(2); #else sel = 0; #endif } return graph->spellings[sel]; } /* * Choose the next source for a graph. The rules are: a consonant MUST be * followed by a vowel; a vowel may be followed by a vowel of a different * type or by a consonant, but never more than two consecutive vowel graphs. */ choosenext(cur, prev) { if (cur == CONSONANT) return VOWEL_MASK; else if (prev == -1 || (prev & VOWEL_MASK) != 0) return CONSONANT; else if (RANDOM(10) == 5) return VOWEL_MASK; else return CONSONANT; } /* * We are passed an array of (struct graph *); choose an entry randomly and * assemble a string fitting the size constraint. We use the original (OSI) * paradigm: alternate consonants and vowels, with the option of two vowels * in a row occasionally. The only difference is that they must be different * *types* of vowels, a distinction that the OSI version didn't consider. */ #ifdef ALLBYOPTS pwgen ( struct graph **initial, char *pw, int maxlen, int wants_capitalize, int wants_numerals ) #else pwgen ( struct graph **initial, char *pw, int maxlen ) #endif { int pwlen, state, prev, tmp; struct graph *graph; char *spelling; int capc; capc=0; pwlen = 0; state = initial[0]->type; prev = -1; while (pwlen < maxlen - 1) { do { graph = selgraph(initial); } while (state != CONSONANT && graph->type == prev); if ((spelling = selspell(graph)) == (char *) 0) { fprintf ( stderr, "PANIC: got NULL in selspell(%s)\n", graph->graph ); exit(2); } strcpy(pw, spelling); if ( (state==CONSONANT) && ( (capc++)==1) ) { #ifdef ALLBYOPTS if(wants_numerals) { *pw++ = (RANDOM(10) + '0'); pwlen++; strcpy(pw, spelling); } #else #ifdef NUMERALS *pw++ = (RANDOM(10) + '0'); pwlen++; strcpy(pw, spelling); #endif #endif #ifdef ALLBYOPTS if(wants_capitalize) { *pw = toupper(*pw); } #else #ifdef CAPITALIZE *pw = toupper(*pw); #endif #endif } while (*pw != '\0') pwlen++, pw++; tmp = prev; prev = graph->type; if ((state = choosenext(prev, tmp)) == CONSONANT) initial = consonant; else initial = vowel; } } void sec_pass(char *buf, int len) { static const int low = '!'; static const int high = '~'; const float range = high - low; struct timeval t; int i; gettimeofday(&t, 0); srand48(t.tv_sec + t.tv_usec * 102457 + getpid() + getppid()); for ( i = 0 ; i < len ; i++ ) { int c = low + (int)(drand48() * range); switch ( c ) { case '\\': case '\'': case '\"': c = '%'; } buf[i]=c; } buf[i]='\0'; } #ifdef ALLBYOPTS #define ALTPHON_OPT 'a' #define CAPITALIZE_OPT 'c' #define NUMERALS_OPT 'n' #define SECURE_OPT 's' #define HELP_OPT 'h' #define END_SHORT_OPT '\0' #define END_LONG_OPT {(char *) 0, 0, (int *) 0, '\0'} const char short_blank = END_SHORT_OPT; const struct option long_blank = END_LONG_OPT; int optionlen(struct option *option_array) { int result = 0; while(option_array[result].name != NULL) result++; return result; } void remove_short_option(char option, char *option_array) { int i; int found_index = -1; /* index of found index !or! -1 if none */ int size = strlen(option_array); for(i = 0; i < size; i++) { if(option_array[i] == option) { found_index = i; break; } } if(found_index != -1) { if(found_index != size - 1) /* not last item? */ { /* replace found item with last item */ option_array[found_index] = option_array[size - 1]; } /* blank out last item */ option_array[size - 1] = short_blank; } } void remove_long_option(char option, struct option *option_array) { int i; int found_index = -1; /* index of found index !or! -1 if none */ int size = optionlen(option_array); for(i = 0; i < size; i++) { if(option_array[i].val == option) { found_index = i; break; } } if(found_index != -1) { if(found_index != size - 1) /* not last item? */ { /* replace found item with last item */ option_array[found_index] = option_array[size - 1]; } /* blank out last item */ option_array[size - 1] = long_blank; } } void print_option_array(struct option *option_array) { int i = 0; while(option_array[i].val != short_blank) { if(option_array[i].flag != NULL) printf ( "{%s, %d, %d, '%c'=%x}\n", option_array[i].name, option_array[i].has_arg, *option_array[i].flag, (char) option_array[i].val, option_array[i].val ); else printf ( "{%s, %d, NULL, '%c'=%x}\n", option_array[i].name, option_array[i].has_arg, (char) option_array[i].val, option_array[i].val ); i++; } } struct option longopts[] = { {"alt-phonics", no_argument, (int *) 0, ALTPHON_OPT }, {"capitalize", no_argument, (int *) 0, CAPITALIZE_OPT }, {"numerals", no_argument, (int *) 0, NUMERALS_OPT }, {"secure", no_argument, (int *) 0, SECURE_OPT }, {"help", no_argument, (int *) 0, HELP_OPT }, END_LONG_OPT }; char opt_string[] = { ALTPHON_OPT, CAPITALIZE_OPT, NUMERALS_OPT, SECURE_OPT, HELP_OPT, END_SHORT_OPT }; void disable_option(char *opt_chars) { while(*opt_chars) { remove_short_option(*opt_chars, opt_string); remove_long_option(*opt_chars, longopts); opt_chars++; } } #endif void usage(FILE *out, char *name) { fprintf(out, "Usage: %s ", name); #ifdef ALLBYOPTS fprintf(out, "[ -acns --alt-phonics --capitalize "); fprintf(out, "--numerals --secure ] "); fprintf(out, "maxlength [count]\n"); fprintf(out, "Use the command \"pwgen -s maxlen [count]\" "); #else fprintf(out, "maxlength [count]\n"); fprintf(out, "Use the command \"spwgen maxlen [count]\" "); #endif fprintf(out, "to generate more secure passwords.\n"); } int main(int argc, char **argv) { int first_nonopt = 1; /* index of the first non-option in argv */ int is_bad_opt = 0; /* true if unrecognized option */ int cnt, len, wants_secure=0; char buf[20]; #ifdef ALLBYOPTS int wants_alternate_phonics = 0; /* boolean from options */ int wants_capitalize = 0; /* boolean from options */ int wants_numerals = 0; /* boolean from options */ int wants_help = 0; /* boolean from options */ int getopt_result; int longindex = 0; getopt_result = getopt_long ( argc, argv, opt_string, longopts, &longindex ); while(getopt_result != EOF) { /* process opts here */ switch(getopt_result) { case ALTPHON_OPT: wants_alternate_phonics = 1; disable_option("ash"); break; case CAPITALIZE_OPT: wants_capitalize = 1; disable_option("csh"); break; case NUMERALS_OPT: wants_numerals = 1; disable_option("nsh"); break; case SECURE_OPT: wants_secure = 1; disable_option("acnsh"); break; case HELP_OPT: wants_help = 1; disable_option("acnsh"); break; case '?': default: is_bad_opt = 1; break; } if(is_bad_opt) break; else getopt_result = getopt_long ( argc, argv, opt_string, longopts, &longindex ); } if(! is_bad_opt) { alter_spelling_table(wants_alternate_phonics); } first_nonopt = optind; #endif if(wants_help) { usage(stdout, argv[0]); printf("You can use -a, -c or -n in any combination.\n"); printf("You can't use these with -s or vise versa.\n"); printf("\n"); printf("\n"); printf("OPTIONS\n"); printf("\n"); printf(" -a or --alt-phonics\n"); printf(" use a slightly altered phonics table to generate\n"); printf(" syllables.\n"); printf("\n"); printf(" -c or --capitalize\n"); printf(" use (not so) random capitalizations in generated\n"); printf(" passwords\n"); printf("\n"); printf(" -n or --numerals\n"); printf(" use (not so) random placement of numerals in\n"); printf(" generated passwords\n"); printf("\n"); printf(" -s or --secure\n"); printf(" generate totally random, unmemorable, unpronouncable,\n"); printf(" but secure and unguessable passwords\n"); printf("\n"); printf(" -h or --help\n"); printf(" print this message and exit successfully\n"); printf("\n"); printf("\n"); printf("ARGUMENTS\n"); printf("\n"); printf(" maxlength (required with -a, -c, -n or -s)\n"); printf(" a number spec ifying the masimum length\n"); printf(" of each generated password\n"); printf("\n"); printf(" count\n"); printf(" the number of passwords generated. each will\n"); printf(" be on its own line.\n"); printf("\n"); printf("SEE ALSO: man pwgen for details.\n"); printf("\n"); exit(0); } else if ( (argc < first_nonopt + 1) || (argc > first_nonopt + 3) || (is_bad_opt) ) { usage(stderr, argv[0]); return 1; } if ((len = atoi(argv[first_nonopt + 0])) < 4 || len > 16) { fprintf ( stderr, "%s: invalid maxlength %s\n", argv[0], argv[first_nonopt + 0] ); return 1; } if (argc == first_nonopt + 1) cnt = 1; else if ((cnt = atoi(argv[first_nonopt + 1])) < 1) { fprintf ( stderr, "%s: invalid count %s\n", argv[0], argv[first_nonopt + 1] ); return 1; } /* * Changed '|' into '^' in the srand() formula to get a better RNG * Vincent Renardias * Jan, 11th 1997. */ #ifdef RAND48 srand48((time(0)<<9) ^ (getpgrp()<<15) ^ (getpid()) ^ (time(0)>>11)); #else srand(time(0) ^ (getpgrp() << 8) + getpid()); #endif while (cnt-- != 0) { if (wants_secure == 1) { sec_pass(buf,len); } else { #ifdef ALLBYOPTS pwgen ( (RANDOM(10) < 4? vowel: consonant), buf, len, wants_capitalize, wants_numerals ); #else pwgen((RANDOM(10) < 4? vowel: consonant), buf, len); #endif } printf("%s\n", buf); } return 0; }