Article View: comp.security.unix
Article #1passwd generator
From: Olaf Titz
Date: Mon, 05 Sep 1994 16:34
Date: Mon, 05 Sep 1994 16:34
556 lines
19620 bytes
19620 bytes
In article <CvDFy0.Cu1@lat.com>, Ben Dubin <ben@lat.com> wrote:
> I am looking for a public domain unix password generator.
Try this one. It's been lifted from a rather old comp.sources.misc
archive and slightly changed to support modern systems.
Olaf
#! /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 <file", e.g.. If this archive is complete, you
# will see the following message at the end:
# "End of shell archive."
# Contents: pwgen pwgen/Makefile pwgen/README pwgen/README.ot
# pwgen/pwgen.1 pwgen/pwgen.c
# Wrapped by titz@askdonald on Mon May 2 14:58:07 1994
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test ! -d 'pwgen' ; then
echo shar: Creating directory \"'pwgen'\"
mkdir 'pwgen'
fi
if test -f 'pwgen/Makefile' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'pwgen/Makefile'\"
else
echo shar: Extracting \"'pwgen/Makefile'\" \(734 characters\)
sed "s/^X//" >'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" <allbery@ncoast.UUCP>
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 <titz@askdonald.ask.uni-karlsruhe.de>
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 <allbery@ncoast.UUCP>
X.P
XCapitals/digits and man page by Olaf Titz
X<titz@askdonald.ask.uni-karlsruhe.de>
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 <stdio.h>
X#include <math.h>
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
--
___ olaf@bigred.ka.sub.org - uknf@rz.uni-karlsruhe.de
__ o <a href="http://rzstud1.rz.uni-karlsruhe.de/~uknf/">click</a>
__/<_ also: s_titz@ira.uka.de - uknf@dkauni2.bitnet - praetorius@irc
_)>(_)_________ "now i find that most of the time love's not enough in itself"
Technical Details
Message-ID:
<34fha8$cj7@nz12.rz.uni-karlsruhe.de>Path:
arc1-ca.pugleaf.net!wupost!psuvax1!news.pop.psu.edu!news.cac.psu.edu!howland.reston.ans.net!math.ohio-state.edu!jussieu.fr!univ-lyon1.fr!swidir.switch.ch!newsfeed.ACO.net!Austria.EU.net!EU.net!uunet!zib-berlin.de!news.belwue.de!news.uni-ulm.de!rz.uni-karlsruhe.de!rzstud1.rz.uni-karlsruhe.de!uknfReferences:
<CvDFy0.Cu1@lat.com>Show Full Headers
Complete Article Headers
Path: arc1-ca.pugleaf.net!wupost!psuvax1!news.pop.psu.edu!news.cac.psu.edu!howland.reston.ans.net!math.ohio-state.edu!jussieu.fr!univ-lyon1.fr!swidir.switch.ch!newsfeed.ACO.net!Austria.EU.net!EU.net!uunet!zib-berlin.de!news.belwue.de!news.uni-ulm.de!rz.uni-karlsruhe.de!rzstud1.rz.uni-karlsruhe.de!uknf From: uknf@rzstud1.rz.uni-karlsruhe.de (Olaf Titz) Newsgroups: comp.security.unix,alt.sources Subject: passwd generator Followup-To: comp.security.unix,alt.sources.d Date: 5 Sep 1994 16:34:16 GMT Organization: Fachschaft math/inf, Uni Karlsruhe, FRG Lines: 556 Message-ID: <34fha8$cj7@nz12.rz.uni-karlsruhe.de> References: <CvDFy0.Cu1@lat.com> NNTP-Posting-Host: rzstud1.rz.uni-karlsruhe.de Mime-Version: 1.0 Content-Type: text/plain; charset=iso-8859-1 Content-Transfer-Encoding: 8bit