[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

Intent to Rewrite: pwgen



So because of the licensing issues of the existing pwgen program (see
Debian bug #39130) --- basically, there is no licensing statement, and
without being able to identify all the people who have worked on it, it
will be difficult to resolve them --- I've taken up the challenge of
rewriting it from scratch.  It turns out that the existing code is
overly complicated and somewhat buggy, and it's easier to rewrite it
from scratch than to fix the existing code anyway.

I've already rewritten the core parts of the password generator from
scratch, and it's already producing better passwords (resolving Debian
bugs #66006 and #70058) than the original.  What's currently missing is
the command-line options parser (front-end interface) and man page, and
other fussy items like that.

What I'd like to do at this point is ask for some input.

1)  Is it important that I maintain compatibility with the existing
    command-line interface?  I'd much rather use one which is a bit more
    user-friendly.  However, if there are a largish number of scripts
    that are depending on the existing command-line interface, I'd like
    to know that.

2)  Once this code is finished, will Debian be willing to use this
    codebase to replace the existing pwgen package?

3)  Is the maintainer of pwgen still active?  The bugs database and
    package changelog seem to indicate that there hasn't been any
    activity in the pwgen in quite a while, and the last upload was an
    NMU.

4)  Are there any other requirements/features you'd like to see in a
    program like pwgen?

Many thanks!!

						- Ted

P.S.  Since the new code is quite compact, I've included it below just
to prove that I'm not talking about vaporware, but something that's
quite real and functional already.  :-)  

Comments about the code and existing algorithm are welcome.  

#!/bin/sh
# This is a shell archive (produced by GNU sharutils 4.2.1).
# To extract the files from this archive, save it to some FILE, remove
# everything before the `!/bin/sh' line above, then type `sh FILE'.
#
# Made on 2001-05-31 08:51 EDT by <tytso@snap>.
# Source directory was `/home/tytso/src/pwgen-2'.
#
# Existing files will *not* be overwritten unless `-c' is specified.
# This format requires very little intelligence at unshar time.
# "if test", "echo", "mkdir", and "sed" may be needed.
#
# This shar contains:
# length mode       name
# ------ ---------- ------------------------------------------
#   3216 -rw-r--r-- pwgen.c
#    537 -rw-r--r-- pwgen.h
#   1855 -rw-r--r-- randnum.c
#    194 -rw-r--r-- Makefile
#
echo=echo
if mkdir _sh23390; then
  $echo 'x -' 'creating lock directory'
else
  $echo 'failed to create lock directory'
  exit 1
fi
# ============= pwgen.c ==============
if test -f 'pwgen.c' && test "$first_param" != -c; then
  $echo 'x -' SKIPPING 'pwgen.c' '(file already exists)'
else
  $echo 'x -' extracting 'pwgen.c' '(text)'
  sed 's/^X//' << 'SHAR_EOF' > 'pwgen.c' &&
X/*
X * pwgen.c --- generate secure passwords
X *
X * Copyright (C) 2001 by Theodore Ts'o
X * 
X * This file may be distributed under the terms of the GNU Public
X * License.
X */
X
X#include "pwgen.h"
X
Xstruct pw_element elements[] = {
X	{ "a",	VOWEL },
X	{ "ae", VOWEL | DIPTHONG },
X	{ "ah",	VOWEL | DIPTHONG },
X	{ "ai", VOWEL | DIPTHONG },
X	{ "b",  CONSONANT },
X	{ "c",	CONSONANT },
X	{ "ch", CONSONANT | DIPTHONG },
X	{ "d",	CONSONANT },
X	{ "e",	VOWEL },
X	{ "ee", VOWEL | DIPTHONG },
X	{ "ei",	VOWEL | DIPTHONG },
X	{ "f",	CONSONANT },
X	{ "g",	CONSONANT },
X	{ "gh", CONSONANT | DIPTHONG | NOT_FIRST },
X	{ "h",	CONSONANT },
X	{ "i",	VOWEL },
X	{ "ie", VOWEL | DIPTHONG },
X	{ "j",	CONSONANT },
X	{ "k",	CONSONANT },
X	{ "l",	CONSONANT },
X	{ "m",	CONSONANT },
X	{ "n",	CONSONANT },
X	{ "ng",	CONSONANT | DIPTHONG | NOT_FIRST },
X	{ "o",	VOWEL },
X	{ "oh",	VOWEL | DIPTHONG },
X	{ "oo",	VOWEL | DIPTHONG},
X	{ "p",	CONSONANT },
X	{ "ph",	CONSONANT | DIPTHONG },
X	{ "qu",	CONSONANT | DIPTHONG},
X	{ "r",	CONSONANT },
X	{ "s",	CONSONANT },
X	{ "sh",	CONSONANT | DIPTHONG},
X	{ "t",	CONSONANT },
X	{ "th",	CONSONANT | DIPTHONG},
X	{ "u",	VOWEL },
X	{ "v",	CONSONANT },
X	{ "w",	CONSONANT },
X	{ "x",	CONSONANT },
X	{ "y",	CONSONANT },
X	{ "z",	CONSONANT }
X};
X
X#define NUM_ELEMENTS (sizeof(elements) / sizeof (struct pw_element))
X
Xvoid pwgen(char *buf, int size, int pw_flags)
X{
X	int	c = 0, i, len, flags;
X	int	prev = 0, should_be = 0, first = 1;
X	char	*str;
X
X	should_be = pw_random_number(1) ? VOWEL : CONSONANT;
X	
X	while (c < size) {
X		i = pw_random_number(NUM_ELEMENTS);
X		str = elements[i].str;
X		len = strlen(str);
X		flags = elements[i].flags;
X		/* Filter on the basic type of the next element */
X		if ((flags & should_be) == 0)
X			continue;
X		/* Handle the NOT_FIRST flag */
X		if (first && (flags & NOT_FIRST))
X			continue;
X		/* Don't allow VOWEL followed a Vowel/Dipthong pair */
X		if ((prev & VOWEL) && (flags & VOWEL) &&
X		    (flags & DIPTHONG))
X			continue;
X		/* Don't allow us to overflow the buffer */
X		if (len > size-c)
X			continue;
X		/*
X		 * OK, we found an element which matches our criteria,
X		 * let's do it!
X		 */
X		strcpy(buf+c, str);
X
X		/* Handle PW_ONE_CASE */
X		if (pw_flags & PW_ONE_CASE) {
X			if ((first || flags & CONSONANT) &&
X			    (pw_random_number(10) < 3)) {
X				buf[c] = toupper(buf[c]);
X				pw_flags &= ~PW_ONE_CASE;
X			}
X		}
X		
X		c += len;
X		
X		/* Time to stop? */
X		if (c >= size)
X			break;
X		
X		/*
X		 * Handle PW_ONE_NUMBER
X		 */
X		if (pw_flags & PW_ONE_NUMBER) {
X			if (!first && (pw_random_number(10) < 3)) {
X				buf[c++] = pw_random_number(9)+'0';
X				buf[c] = 0;
X				pw_flags &= ~PW_ONE_NUMBER;
X				
X				first = 1;
X				prev = 0;
X				should_be = pw_random_number(1) ?
X					VOWEL : CONSONANT;
X				continue;
X			}
X		}
X				
X		/*
X		 * OK, figure out what the next element should be
X		 */
X		if (should_be == CONSONANT) {
X			should_be = VOWEL;
X		} else { /* should_be == VOWEL */
X			if ((prev & VOWEL) ||
X			    (flags & DIPTHONG) ||
X			    (pw_random_number(10) > 3))
X				should_be = CONSONANT;
X			else
X				should_be = VOWEL;
X		}
X		prev = flags;
X		first = 0;
X	}
X}
X
Xint main(int argc, char **argv)
X{
X	int	i;
X	char	buf[16];
X
X	for (i=0; i < 20; i++) {
X		pwgen(buf, 8, PW_ONE_NUMBER | PW_ONE_CASE);
X		printf("%s\n", buf);
X	}
X}
X
SHAR_EOF
  : || $echo 'restore of' 'pwgen.c' 'failed'
fi
# ============= pwgen.h ==============
if test -f 'pwgen.h' && test "$first_param" != -c; then
  $echo 'x -' SKIPPING 'pwgen.h' '(file already exists)'
else
  $echo 'x -' extracting 'pwgen.h' '(text)'
  sed 's/^X//' << 'SHAR_EOF' > 'pwgen.h' &&
X/*
X * pwgen.h --- header file for password generator
X *
X * Copyright (C) 2001 by Theodore Ts'o
X * 
X * This file may be distributed under the terms of the GNU Public
X * License.
X */
X
Xstruct pw_element {
X	char	*str;
X	int	flags;
X};
X
X/*
X * Flags for the pw_element
X */
X#define CONSONANT	0x0001
X#define VOWEL		0x0002
X#define DIPTHONG	0x0004
X#define NOT_FIRST	0x0008
X
X/*
X * Flags for the pwgen function
X */
X#define PW_ONE_NUMBER	0x0001
X#define PW_ONE_CASE	0x0002
X
X
X/* Function prototypes */
X
X/* randnum.c */
Xint pw_random_number(int max_num);
SHAR_EOF
  : || $echo 'restore of' 'pwgen.h' 'failed'
fi
# ============= randnum.c ==============
if test -f 'randnum.c' && test "$first_param" != -c; then
  $echo 'x -' SKIPPING 'randnum.c' '(file already exists)'
else
  $echo 'x -' extracting 'randnum.c' '(text)'
  sed 's/^X//' << 'SHAR_EOF' > 'randnum.c' &&
X/*
X * randnum.c -- generate (good) randum numbers.
X *
X * Copyright (C) 2001 by Theodore Ts'o
X * 
X * This file may be distributed under the terms of the GNU Public
X * License.
X */
X
X#include <unistd.h>
X#include <stdlib.h>
X#include <sys/types.h>
X#include <sys/stat.h>
X#include <fcntl.h>
X#include <errno.h>
X
X#include "pwgen.h"
X
X#ifdef HAVE_DRAND48
Xextern double drand48();
X#endif
X
X/* Borrowed/adapted from e2fsprogs's UUID generation code */
Xstatic int get_random_fd(void)
X{
X	struct timeval	tv;
X	static int	fd = -2;
X	int		i;
X
X	if (fd == -2) {
X		gettimeofday(&tv, 0);
X		fd = open("/dev/urandom", O_RDONLY);
X		if (fd == -1)
X			fd = open("/dev/random", O_RDONLY | O_NONBLOCK);
X#ifdef HAVE_DRAND48
X		srand48((tv.tv_sec<<9) ^ (getpgrp()<<15) ^
X			(getpid()) ^ (tv.tv_usec>>11));
X#else
X		srandom((getpid() << 16) ^ (getpgrp() << 8) ^ getuid() 
X		      ^ tv.tv_sec ^ tv.tv_usec);
X#endif
X	}
X	/* Crank the random number generator a few times */
X	gettimeofday(&tv, 0);
X	for (i = (tv.tv_sec ^ tv.tv_usec) & 0x1F; i > 0; i--)
X#ifdef HAVE_DRAND48
X		drand48();
X#else
X		rand();
X#endif
X	return fd;
X}
X
X/*
X * Generate a random number n, where 0 <= n < max_num, using
X * /dev/urandom if possible.
X */
Xint pw_random_number(int max_num)
X{
X	int i, fd = get_random_fd();
X	int lose_counter = 0, nbytes=4;
X	unsigned int rand;
X	char *cp = (char *) &rand;
X
X	if (fd >= 0) {
X		while (nbytes > 0) {
X			i = read(fd, cp, nbytes);
X			if ((i < 0) &&
X			    ((errno == EINTR) || (errno == EAGAIN)))
X				continue;
X			if (i <= 0) {
X				if (lose_counter++ == 8)
X					break;
X				continue;
X			}
X			nbytes -= i;
X			cp += i;
X			lose_counter = 0;
X		}
X	}
X	if (nbytes == 0)
X		return (rand % max_num);
X
X	/* OK, we weren't able to use /dev/random, fall back to rand/rand48 */
X
X#ifdef RAND48
X	return ((int) ((drand48() * max_num)));
X#else
X	return ((int) (random() / ((float) RAND_MAX) * max_num));
X#endif
X}
SHAR_EOF
  : || $echo 'restore of' 'randnum.c' 'failed'
fi
# ============= Makefile ==============
if test -f 'Makefile' && test "$first_param" != -c; then
  $echo 'x -' SKIPPING 'Makefile' '(file already exists)'
else
  $echo 'x -' extracting 'Makefile' '(text)'
  sed 's/^X//' << 'SHAR_EOF' > 'Makefile' &&
XCC = gcc
XRM = rm -f
XCFLAGS = -g -O2
XDEFS =  -DHAVE_RAND38
XTAR = tar
X
XCCOPTS = $(CFLAGS) $(DEFS)
X
XOBJS= pwgen.o randnum.o
X
Xpwgen: $(OBJS)
X	$(CC) -o pwgen $(OBJS)
X
Xclean:
X	$(RM) $(OBJS) pwgen *~
X
SHAR_EOF
  : || $echo 'restore of' 'Makefile' 'failed'
fi
rm -fr _sh23390
exit 0



Reply to: