Hi The gpw program is BSD-3-clause so eventually compatible with the randnum.c in GPL. It just needs to retain the GPL copyright in the debian/copyright file to clarify. Anyway, I will forward the bug to upstream, who could be eventually interested in supporting even after 26 years...
-cheers On Sun, Nov 22, 2020 at 04:35:06PM +0000, Carles Pina i Estany wrote:
Package: gpw Version: 0.0.19940601-9+b1 Severity: important Tags: patch upstream Dear Maintainer, Recently I read https://github.com/rclone/rclone/issues/4783 . rclone was generating weak passwords: all the passwords were in a set of 33 million possible generated passwords. I checked Debian packages that could have the same problem and found that "gpw" has a similar problem (1 million sets of passwords for a given password length). I initially reported this bug to the Debian security team and they suggested to open a public bug. The problem is that "gpw" uses a seed for the random generator based only on microseconds: a number between 0 and 999999. The generated passwords for a certain password length depend on the dictionaries installed at compilation time (the same for all the Debian users) and the seed. There are only 1 million sets of different passwords (for the same length) generated by "gpw". E.g, if faketime is installed, Debian 9 or Debian 10 gpw would generate: carles@pinux:~$ faketime -f '2008-12-24 08:15:43' gpw demoduls pebfurge ratentso prockerm ndivical ualksect alidedit iredgedr pledonsu lizensms carles@pinux:~$ (faketime doesn't support microseconds and by default sets it to .0 microseconds) In other words: If anyone is generating passwords with "gpw 1" (in Debian) there are only 1 million possible sets of passwords for this length. Or even doing "gpw 1 99": only 1 million possible passwords of length 99. I am not an expert in cryptography and my solution might be wrong but here is a possible patch: Possible patch: I copied two functions from "pwgen" and used them from "gpw". Then the seed is one in 9223372036854775807 (2^63-1) instead of one in 1000000 (2^20 approx.). Another approach would be to use libgcrypt from gpw (other software, like apg, use this approach). I'm unsure if the license of gpw is compatible with GPL (randnum.c is GPL). Perhaps the maintainer or upstream can clarify this / provide a different fix if it's not correct, etc. Interestingly: "pwgen" in 2013 had a bug with a similar issue: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=767008 . In this case it generated the seed using /dev/urandom. If it failed it used /dev/random and if this also failed it used a combination of seconds, getpgrp(), getpid() and microseconds. This was considered too weak and it was updated to use only /dev/urandom and /dev/random (https://github.com/tytso/pwgen/commit/ccda6f21c678188074aaa1c673008a8c7ac1b3cf). -- System Information: Debian Release: 10.6 APT prefers stable-updates APT policy: (500, 'stable-updates'), (500, 'stable') Architecture: amd64 (x86_64) Foreign Architectures: i386 Kernel: Linux 4.19.0-12-amd64 (SMP w/4 CPU cores) Kernel taint flags: TAINT_WARN, TAINT_OOT_MODULE, TAINT_UNSIGNED_MODULE Locale: LANG=ca_ES.UTF-8, LC_CTYPE=ca_ES.UTF-8 (charmap=UTF-8), LANGUAGE=ca_ES.UTF-8 (charmap=UTF-8) Shell: /bin/sh linked to /bin/dash Init: systemd (via /run/systemd/system) LSM: AppArmor: enabled Versions of packages gpw depends on: ii libc6 2.28-10 gpw recommends no packages. gpw suggests no packages. -- no debconf information
diff --git a/Makefile b/Makefile index 8b2c826..4fd5dee 100644 --- a/Makefile +++ b/Makefile @@ -9,7 +9,10 @@ all : gpw loadtris echo gpw created, can delete loadtris gpw : gpw.o - $(COMPILER) $(DEBUGARGS) -o gpw gpw.o + $(COMPILER) $(DEBUGARGS) -o gpw gpw.o randnum.o + +randnum.o: randnum.c + $(COMPILER) $(DEBUGARGS) -o randnum.o -c randnum.c trigram.h : loadtris ./loadtris /usr/dict/words | sed "s/, }/}/" > trigram.h @@ -24,4 +27,4 @@ loadtris.o : loadtris.c $(COMPILER) $(DEBUGARGS) -o loadtris.o -c loadtris.c clean : - rm gpw loadtris loadtris.o gpw.o # trigram.h + rm gpw loadtris loadtris.o gpw.o randnum.o # trigram.h diff --git a/Makefile.Debian b/Makefile.Debian index 3e7baed..60ef181 100644 --- a/Makefile.Debian +++ b/Makefile.Debian @@ -14,8 +14,11 @@ BIN=$(DESTDIR)/usr/bin all : gpw loadtris echo gpw created, can delete loadtris -gpw : gpw.o - $(COMPILER) $(DEBUGARGS) -o gpw gpw.o +gpw : gpw.o randnum.o + $(COMPILER) $(DEBUGARGS) -o gpw gpw.o randnum.o + +randnum.o: randnum.c + $(COMPILER) $(DEBUGARGS) -o randnum.o -c randnum.c trigram.h : loadtris ./loadtris /usr/share/dict/words | sed "s/, }/}/" > trigram.h @@ -30,7 +33,7 @@ loadtris.o : loadtris.c $(COMPILER) $(DEBUGARGS) -o loadtris.o -c loadtris.c clean : - rm -f gpw loadtris loadtris.o gpw.o trigram.h + rm -f gpw loadtris loadtris.o gpw.o randnum.o trigram.h diff --git a/gpw.c b/gpw.c index be3c307..2316f95 100644 --- a/gpw.c +++ b/gpw.c @@ -13,11 +13,6 @@ and looking for real words in its output.. they are very rare, on the order of one in a thousand. - This program uses "drand48()" to get random numbers, and "srand48()" - to set the seed to the microsecond part of the system clock. Works - for AIX C++ compiler and runtime. Might have to change this to port - to another environment. - The best way to use this program is to generate multiple words. Then pick one you like and transform it with punctuation, capitalization, and other changes to come up with a new password. @@ -35,6 +30,7 @@ /* #include <bsd/sys/time.h> */ /* following for BSD */ #include <sys/time.h> +#include <limits.h> int main (int argc, char ** argv) { int password_length; /* how long should each password be */ @@ -47,14 +43,11 @@ int main (int argc, char ** argv) { long sum; /* running total of frequencies */ char password[100]; /* buffer to develop a password */ int nchar; /* number of chars in password so far */ - struct timeval systime; /* time reading for random seed */ - struct timezone tz; /* unused arg to gettimeofday */ password_length = 8; /* Default value for password length */ n_passwords = 10; /* Default value for number of pws to generate */ - gettimeofday (&systime, &tz); /* Read clock. */ - srand48 (systime.tv_usec); /* Set random seed. */ + srand48 (pw_random_number(LONG_MAX)); /* Set random seed. */ if (argc > 1) { /* If args are given, convert to numbers. */ n_passwords = atoi (&argv[1][0]); diff --git a/randnum.c b/randnum.c new file mode 100644 index 0000000..7e55264 --- /dev/null +++ b/randnum.c @@ -0,0 +1,77 @@ +/* + * randnum.c -- generate (good) randum numbers. + * + * Copyright (C) 2001,2002 by Theodore Ts'o + * + * This file may be distributed under the terms of the GNU Public + * License. + */ + + /** This file is from pwgen package, adapted for gpw */ + +#include <stdio.h> +#include <unistd.h> +#include <stdlib.h> +#include <sys/types.h> +#include <sys/time.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <errno.h> + +#ifdef HAVE_DRAND48 +extern double drand48(void); +#endif + +static int get_random_fd(void); + +/* Borrowed/adapted from e2fsprogs's UUID generation code */ +static int get_random_fd() +{ + struct timeval tv; + static int fd = -2; + + if (fd == -2) { + gettimeofday(&tv, 0); + fd = open("/dev/urandom", O_RDONLY); + if (fd == -1) + fd = open("/dev/random", O_RDONLY | O_NONBLOCK); + } + return fd; +} + +/* + * Generate a random number n, where 0 <= n < max_num, using + * /dev/urandom if possible. + */ +long int pw_random_number(max_num) + long int max_num; +{ + long int rand_num; + int i, fd = get_random_fd(); + int lose_counter = 0, nbytes = sizeof(rand_num); + char *cp = (char *) &rand_num; + + if (fd >= 0) { + while (nbytes > 0) { + i = read(fd, cp, nbytes); + if ((i < 0) && + ((errno == EINTR) || (errno == EAGAIN))) + continue; + if (i <= 0) { + if (lose_counter++ == 8) + break; + continue; + } + nbytes -= i; + cp += i; + lose_counter = 0; + } + } + if (nbytes == 0) + return (rand_num % max_num); + + /* We weren't able to use /dev/random, fail hard */ + + fprintf(stderr, "No entropy available!\n"); + exit(1); +}
-- Francesco P. Lovergine