#! /bin/sh # This is a shell archive. Remove anything before this line, then unpack # it by saving it into a file and typing "sh file". To overwrite existing # files, type "sh file -c". You can also feed this as standard input via # unshar, or by typing "sh 'pwgen/Makefile' <<'END_OF_FILE' X# Makefile for pwgen. X X# Compile-time program options XOPTS = -DCAPITALIZE -DALTPHON -DNUMERALS -DRAND48 X X# Standard items XCC = cc XCFLAGS = -g XLD = cc XLDFLAGS = -g XLIBS = -lm XINSTALL = cp XINFLAGS = X X# Where to install XDESTDIR = /usr/local/bin XMANDIR = /usr/local/man/man1 X X# End configuration section X Xall: pwgen X Xtest: pwgen X ./pwgen 8 20 X Xinstall: all X $(INSTALL) pwgen $(DESTDIR) X $(INSTALL) pwgen.1 $(MANDIR) X chmod 555 $(DESTDIR)/pwgen X chmod 444 $(MANDIR)/pwgen.1 X Xpwgen: pwgen.o X $(LD) $(LDFLAGS) -o pwgen pwgen.o $(LIBS) X Xpwgen.o: pwgen.c X $(CC) $(CFLAGS) $(OPTS) -c -o pwgen.o pwgen.c X Xclean: X rm -f core a.out pwgen *.o *.old *~ X Xtar: clean X cd ..; tar cvf pwgen.tar pwgen X Xdist: clean X cd ..; shar pwgen pwgen/* >pwgen.shar END_OF_FILE if test 734 -ne `wc -c <'pwgen/Makefile'`; then echo shar: \"'pwgen/Makefile'\" unpacked with wrong size! fi # end of 'pwgen/Makefile' fi if test -f 'pwgen/README' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'pwgen/README'\" else echo shar: Extracting \"'pwgen/README'\" \(2575 characters\) sed "s/^X//" >'pwgen/README' <<'END_OF_FILE' XPosting-number: Volume 5, Issue 59 XSubmitted-by: "Brandon S. Allbery" XArchive-name: pwgen X X[Dr. Jekyll and Mr. Hyde, anyone? ;-) ++bsa] X XI've described this program, in its OSI Superboard-II incarnation, in Xcomp.unix.wizards and news.sysadmin. Now, here's the code for a UN*X Xversion. It seems to work OK on ncoast, except for some oddness in the X"random" selections -- which are probably the result of our not having a Xgood RNG. (Could someone mail me one or more of the alternative Xgenerators? Or maybe even the source to BSD random(), assuming that it Xfalls outside the subset of BSD code that can be traced to AT&T? To put it Xmildly, our random number generator isn't.) X XThis is not as complete as the original program, which actually had a list Xof characters (the current one uses phonemes) which were most likely to Xfollow other phonemes. On the other hand, this version does use phonemes Xrather than characters, so its creations are more pronounceable as a rule Xthan those of the original. The resulting passwords aren't quite as X"natural" as the original, however. (That may be for the best; the original Xwas intended as a simple experiment to see if a rule for "proper English Xwords" could be defined in terms of rules which related letters. This was Xbefore I got to college and learned that such things were rather unlikely, Xbut the original program still did a pretty good job of spewing out some Xcommon English words. That can't be said for *this* program.) X XTo compile: cc -O -o pwgen pwgen.c -lm X(The sqrt() function is used in an attempt to produce a random number which Xis "weighted" toward one end of the range, so as to prefer one end of a list Xof "spellings" for a phoneme over the other end. It seems to work, but with Xthis rotted RNG, I can't be absolutely certain. A trial distribution seems Xto be correct, however.) X XWhat's the intent? I find that I can remember a "word" better if I can Xpronounce it. This may or may not be true for other people, but *anything* Xthat encourages the use of random passwords is an improvement on what I Xtypically see at client sites every day. So this program spits out Xpasswords which are virtually guaranteed not to be found in the dictionary, Xbut are (usually) pronounceable to speakers of English. (The tables can be Xmodified for other languages, but they're rather hairy. Perhaps I'll write Xa version which loads "compiled" language descriptions, and let the compiler Xdeal with the hairy stuff. Hey, they were even hairier in the BASIC Xversion!) X XOh, well, shar and enjoy. END_OF_FILE if test 2575 -ne `wc -c <'pwgen/README'`; then echo shar: \"'pwgen/README'\" unpacked with wrong size! fi # end of 'pwgen/README' fi if test -f 'pwgen/README.ot' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'pwgen/README.ot'\" else echo shar: Extracting \"'pwgen/README.ot'\" \(2048 characters\) sed "s/^X//" >'pwgen/README.ot' <<'END_OF_FILE' XI have pulled this from an old comp.sources.misc issue and inserted a Xfew improvements to accomodate the program to the needs of modern Un*x X"passwd" commands. Many of them require at least one capital letter Xand/or digit in passwords. So I've changed the algorithm as follows: XOptionally, the second syllable of the generated word is capitalized X(i.e. the starting consonant is), and on another option, the second Xsyllable is prefixed with a digit. This scheme is predictable but Xbetter than nothing - you can easily remember the word with the second Xsyllable stressed, so you know which letter to capitalize, and Xremember the digit along with the word and already know where to Xinsert it. X XThe following compile-time options are recognized. If none of them is Xactive, you get the original version. X XCAPITALIZE - put capital letters into the generated passwords. XNUMERALS - put numerals into the generated passwords. XALTPHON - use some alternate spellings. X Originally, the number of "o"s in the generated words is X disproportionally high, and "ooo" sequences are common. XRAND48 - set this to use the better pseudorandom number generator of X the drand48(3) family if your system has this. X XI've also taken the liberty to write a Makefile and a man page. X XFollowing are results of test runs how many duplicate words are Xgenerated. The test was run under HPUX 8.05 with all the above Xoptions set. For each length, 10 test runs were conducted which all Xhad to generate 100,000 passwords, and counted the number of words Xthat were repeated at least once (uniq -d). X Xlength=8 1232 1273 1266 1308 1291 1248 1280 1266 1250 1278 Xlength=9 178 190 174 184 210 192 204 237 210 205 Xlength=10 35 29 36 24 24 19 30 36 28 32 Xlength=11 7 3 4 7 3 7 6 3 4 7 Xlength=12 0 1 0 0 3 0 1 1 1 0 Xlength=13 0 0 0 0 0 0 0 0 0 0 Xlength=14 0 0 0 0 0 0 0 0 0 0 X X XOlaf Titz END_OF_FILE if test 2048 -ne `wc -c <'pwgen/README.ot'`; then echo shar: \"'pwgen/README.ot'\" unpacked with wrong size! fi # end of 'pwgen/README.ot' fi if test -f 'pwgen/pwgen.1' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'pwgen/pwgen.1'\" else echo shar: Extracting \"'pwgen/pwgen.1'\" \(1213 characters\) sed "s/^X//" >'pwgen/pwgen.1' <<'END_OF_FILE' X.TH PWGEN 1 X.SH NAME Xpwgen \- generate pronounceable passwords X.SH SYNOPSIS X.I pwgen length \[count\] X.SH DESCRIPTION X.I pwgen Xgenerates random, meaningless but pronounceable passwords. Depending Xon how the program was installed, these words contain either only Xlowercase letters, or upper and lower case mixed, or digits thrown Xin. Uppercase letters and digits are placed in a way that eases Xremembering their position when memorizing only the word. X.P X.I pwgen Xdoes not interact with the system's password handling. All it does is Xprint out one or more passwords that can subsequently be used as input Xfor X.I passwd X(1) or for similar applications. X.SH PARAMETERS X.TP X.I length X(number from 4 to 16) specifies the maximum password length. This Xparameter is required. X.TP X.I count Xspecifies how many passwords to produce. Defaults to one. X.SH SEE ALSO X.I passwd X(1) X.SH BUGS X.PD 0 XNot always uses the whole X.I length, Xin fact mostly it generates one character less. X.P XThe position of capitals and digits is too predictable. X.P XSometimes misses the capitals/digits. X.PD X.SH AUTHOR X.PD 0 XBrandon S. Allbery X.P XCapitals/digits and man page by Olaf Titz X X END_OF_FILE if test 1213 -ne `wc -c <'pwgen/pwgen.1'`; then echo shar: \"'pwgen/pwgen.1'\" unpacked with wrong size! fi # end of 'pwgen/pwgen.1' fi if test -f 'pwgen/pwgen.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'pwgen/pwgen.c'\" else echo shar: Extracting \"'pwgen/pwgen.c'\" \(8065 characters\) sed "s/^X//" >'pwgen/pwgen.c' <<'END_OF_FILE' X/* X * Generate (hopefully) pronounceable random passwords. These can often be X * remembered more easily than completely random passwords, and are immune to X * dictionary searches, etc. X * X * The original version of this program was written in BASIC on an OSI X * Superboard II SBC. That version is long gone (the SB2's cassette drive X * was never trustworthy, it eventually scrambled the tape), but the basic X * (pardon the pun) idea lives on here, with a few modification like basing X * the selection on "graphs" (actually, they are a restricted set of phonemes) X * and having randomly-selected spellings for those graphs. X */ X X#include X#include X X#ifdef RAND48 Xextern double drand48(); X#define RANDOM(c) ((int) ((drand48() * (c)))) X#else X#define RANDOM(c) ((int) (rand(c) / 32767.0 * (c))) X#endif X Xchar *spelling[] = { X/*a*/ "a", (char *) 0, /* 2*/ X/*A*/ "a", "ae", "ai", (char *) 0, /* 6*/ X/*b*/ "b", (char *) 0, /* 8*/ X/*ch*/ "ch", (char *) 0, /*10*/ X/*d*/ "d", (char *) 0, /*12*/ X/*e*/ "e", (char *) 0, /*14*/ X/*E*/ "e", "ee", "ie", (char *) 0, /*18*/ X/*f*/ "f", "ph", "gh", (char *) 0, /*22*/ X/*g*/ "g", (char *) 0, /*24*/ X/*h*/ "h", (char *) 0, /*26*/ X/*i*/ "i", "e", (char *) 0, /*29*/ X/*I*/ "i", "ai", (char *) 0, /*32*/ X/*i'*/ "i", "ei", (char *) 0, /*35*/ X/*j*/ "j", "g", (char *) 0, /*38*/ X/*k*/ "k", "c", (char *) 0, /*41*/ X/*l*/ "l", (char *) 0, /*43*/ X/*m*/ "m", (char *) 0, /*45*/ X/*n*/ "n", (char *) 0, /*47*/ X/*ng*/ "ng", (char *) 0, /*49*/ X/*o*/ "o", "a", "ah", (char *) 0, /*53*/ X/*O*/ "o", "oh", (char *) 0, /*56*/ X#ifdef ALTPHON X/*oo*/ "u", "u", (char *) 0, /*59*/ X/*OO*/ "oo", "w", (char *) 0, /*62*/ X#else X/*oo*/ "oo", "u", (char *) 0, /*59*/ X/*OO*/ "oo", "w", (char *) 0, /*62*/ X#endif X/*p*/ "p", (char *) 0, /*64*/ X/*qu*/ "qu", (char *) 0, /*66*/ X/*r*/ "r", (char *) 0, /*68*/ X/*s*/ "s", "c", (char *) 0, /*71*/ X/*sh*/ "sh", "s", (char *) 0, /*74*/ X/*t*/ "t", (char *) 0, /*76*/ X/*th*/ "th", (char *) 0, /*78*/ X/*TH*/ "th", (char *) 0, /*80*/ X/*u*/ "u", (char *) 0, /*82*/ X/*U*/ "u", "oo", (char *) 0, /*85*/ X/*v*/ "v", (char *) 0, /*87*/ X/*x*/ "x", (char *) 0, /*89*/ X/*y*/ "y", (char *) 0, /*91*/ X/*z*/ "z", "s", (char *) 0, /*94*/ X}; X Xstruct graph { X char *graph; X char type; X#define CONSONANT 0 X#define VOWEL_LONG 1 X#define VOWEL_SHORT 2 X#define VOWEL_OTHER 3 X#define VOWEL_MASK 3 X#ifdef NUMERALS X#define NUMERAL 8 X#endif X#define iscons(c) (((c)->type & VOWEL_MASK) == 0) X#define isvowel(c) (((c)->type & VOWEL_MASK) != 0) X/* char frequency; */ /* unused for now */ X char **spellings; X/* struct graph **following; */ /* maybe later */ X} graph[] = { X "a", VOWEL_SHORT, &spelling[0], X "A", VOWEL_LONG, &spelling[2], X "b", CONSONANT, &spelling[6], X "ch", CONSONANT, &spelling[8], X "d", CONSONANT, &spelling[10], X "e", VOWEL_SHORT, &spelling[12], X "E", VOWEL_LONG, &spelling[14], X "f", CONSONANT, &spelling[18], X "g", CONSONANT, &spelling[22], X "h", CONSONANT, &spelling[24], X "i", VOWEL_SHORT, &spelling[26], X "I", VOWEL_LONG, &spelling[29], X "i'", VOWEL_OTHER, &spelling[32], X "j", CONSONANT, &spelling[35], X "k", CONSONANT, &spelling[38], X "l", CONSONANT, &spelling[41], X "m", CONSONANT, &spelling[43], X "n", CONSONANT, &spelling[45], X "ng", CONSONANT, &spelling[47], X "o", VOWEL_SHORT, &spelling[49], X "O", VOWEL_LONG, &spelling[53], X "oo", VOWEL_SHORT, &spelling[56], X "OO", VOWEL_LONG, &spelling[59], X "p", CONSONANT, &spelling[62], X "qu", CONSONANT, &spelling[64], X "r", CONSONANT, &spelling[66], X "s", CONSONANT, &spelling[68], X "sh", CONSONANT, &spelling[71], X "t", CONSONANT, &spelling[74], X "th", CONSONANT, &spelling[76], X "TH", CONSONANT, &spelling[78], X "u", VOWEL_SHORT, &spelling[80], X "U", VOWEL_LONG, &spelling[82], X "v", CONSONANT, &spelling[85], X "x", CONSONANT, &spelling[87], X "y", CONSONANT, &spelling[89], X "z", CONSONANT, &spelling[91], X 0, 0, &spelling[94], X}; X Xstruct graph *vowel[] = { X &graph[0], &graph[1], &graph[5], &graph[6], X &graph[10], &graph[11], &graph[12], &graph[19], X &graph[20], &graph[21], &graph[22], &graph[30], X &graph[31], X (struct graph *) 0, X}; X Xstruct graph *consonant[] = { X &graph[2], &graph[3], &graph[4], &graph[7], X &graph[8], &graph[9], &graph[13], &graph[14], X &graph[15], &graph[16], &graph[17], &graph[18], X &graph[23], &graph[24], &graph[25], &graph[26], X &graph[27], &graph[28], &graph[29], &graph[32], X &graph[33], &graph[34], &graph[35], X (struct graph *) 0, X}; X X/* X * Randomly select a graph from the specifield array. Eventually, this should X * account for graph frequencies as well. X */ X Xstruct graph *selgraph(graphs) X struct graph **graphs; X{ X register int cnt; X X for (cnt = 0; graphs[cnt] != (struct graph *) 0; cnt++) X ; X return graphs[RANDOM(cnt)]; X} X X/* X * Randomly select a spelling for the specified graph. This is not linear: X * earlier spellings are preferred over later ones, but the latter do X * sometimes sneak in. X */ X Xchar *selspell(graph) X struct graph *graph; X{ X register int cnt, sel; X X for (cnt = 0; graph->spellings[cnt] != (char *) 0; cnt++) X ; X if (cnt == 0) { X fprintf(stderr, "PANIC: selspell(%s) got count(spellings) == 0\n", graph->graph); X exit(2); X } X if (cnt == 1) X return *graph->spellings; X/* X * This may not be the best way to do it... maybe Weemba'd care to lend a X * hand here? After all, my specialty is programming, NOT math. X */ X if ((sel = cnt - (int) sqrt((double) RANDOM(cnt * cnt) + 1) - 1) < 0 || sel >= cnt) { X#ifdef BUGCATCH X fprintf(stderr, "PANIC: selspell(%s) got nlrand(%d) == %d\n", graph->graph, cnt, sel); X exit(2); X#else X sel = 0; X#endif X } X return graph->spellings[sel]; X} X X/* X * Choose the next source for a graph. The rules are: a consonant MUST be X * followed by a vowel; a vowel may be followed by a vowel of a different X * type or by a consonant, but never more than two consecutive vowel graphs. X */ X Xchoosenext(cur, prev) X{ X if (cur == CONSONANT) X return VOWEL_MASK; X else if (prev == -1 || (prev & VOWEL_MASK) != 0) X return CONSONANT; X else if (RANDOM(10) == 5) X return VOWEL_MASK; X else X return CONSONANT; X} X X/* X * We are passed an array of (struct graph *); choose an entry randomly and X * assemble a string fitting the size constraint. We use the original (OSI) X * paradigm: alternate consonants and vowels, with the option of two vowels X * in a row occasionally. The only difference is that they must be different X * *types* of vowels, a distinction that the OSI version didn't consider. X */ X Xpwgen(initial, pw, maxlen) X struct graph **initial; X char *pw; X{ X int pwlen, state, prev, tmp; X struct graph *graph; X char *spelling; X int capc; X X capc=0; X pwlen = 0; X state = initial[0]->type; X prev = -1; X while (pwlen < maxlen - 1) { X do { X graph = selgraph(initial); X } while (state != CONSONANT && graph->type == prev); X if ((spelling = selspell(graph)) == (char *) 0) { X fprintf(stderr, "PANIC: got NULL in selspell(%s)\n", graph->graph); X exit(2); X } X strcpy(pw, spelling); X if ( (state==CONSONANT) && ( (capc++)==1) ) { X#ifdef NUMERALS X *pw++ = (RANDOM(10) + '0'); X pwlen++; X strcpy(pw, spelling); X#endif X#ifdef CAPITALIZE X *pw = toupper(*pw); X#endif X } X while (*pw != '\0') X pwlen++, pw++; X tmp = prev; X prev = graph->type; X if ((state = choosenext(prev, tmp)) == CONSONANT) X initial = consonant; X else X initial = vowel; X } X} X Xmain(argc, argv) X char **argv; X{ X int cnt, len; X char buf[20]; X X if (argc < 2 || argc > 3) { X fprintf(stderr, "usage: %s length [count]\n", argv[0]); X exit(1); X } X if ((len = atoi(argv[1])) < 4 || len > 16) { X fprintf(stderr, "%s: invalid length %s\n", argv[0], argv[1]); X exit(1); X } X if (argc == 2) X cnt = 1; X else if ((cnt = atoi(argv[2])) < 1) { X fprintf(stderr, "%s: invalid count %s\n", argv[0], argv[2]); X exit(1); X } X#ifdef RAND48 X srand48((time(0)<<9) | (getpgrp()<<15) | (getpid()) | (time(0)>>11)); X#else X srand(time(0) + (getpgrp() << 8) + getpid()); X#endif X while (cnt-- != 0) { X pwgen((RANDOM(10) < 4? vowel: consonant), buf, len); X printf("%s\n", buf); X } X exit(0); X} END_OF_FILE if test 8065 -ne `wc -c <'pwgen/pwgen.c'`; then echo shar: \"'pwgen/pwgen.c'\" unpacked with wrong size! fi # end of 'pwgen/pwgen.c' fi echo shar: End of shell archive. exit 0