COMMAND

    pw suite

SYTEM AFFECTED

    FreeBSD

PROBLEM

    The FreeBSD  account administration  pw suite  is able  to produce
    "random" passwords for new accounts. Due to the simplicity of  the
    password generation algorithm  involved, the passwords  are easily
    predictable amid a particular  range of possibilities. This  range
    may  be  very  narrow,  depending  on  what sort of information is
    available to the attacker.

    The  pid  and  the  current  time  in seconds are ored (NOT xored)
    together and used  as the seed.  Because of this,  the probability
    of a  lower bit  in the  seed of  being on  is 3/4.  The msb's  of
    the  seed  change  very  slowly  and  predictably over time and in
    effect contain no entropy.

    If the attacker has  access to the file  system then the time  the
    account  was  created  on  can  by  guessed  very  accurately   by
    examining the  creation date  of the  new account's  directory, or
    the creation date of the new account's mail spool file.

    With  the  time  part  of  the  seed  known it is then possible to
    generate an  a mask  to eliminate  50% of  the remaining  possible
    pid values.  The pid  can be  further approximated retrospectively
    by correlation  between time  stamps and  pid values  from a  wide
    number of  file system  sources such  as the  pid of  the sendmail
    task  used  to  deliver  the  new-user  mail, to names of /tmp and
    queue files.  An active  attack (looking  for the  "pw" process or
    other  signs  of  account  creation)  will  of  course  locate the
    pid very closely, if not exactly.

SOLUTION

    Patch   addresses   the   problem   is   given   by   Julian    A.
    (proff@suburbia.net) who originally described this vulnerability.

--- /usr/src/usr.sbin/pw/pw_user.c.orig Thu Dec 12 02:10:47 1996
+++ /usr/src/usr.sbin/pw/pw_user.c      Sat Dec 14 11:37:50 1996
@@ -33,6 +33,10 @@
 #include <sys/param.h>
 #include <dirent.h>
 #include <termios.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <md5.h>
 #include "pw.h"
 #include "bitmap.h"
 #include "pwupd.h"
@@ -738,19 +742,61 @@
        return strcpy(buf, crypt(password, salt));
 }

+u_char *
+pw_genmd5rand (u_char *d)              /* cryptographically secure rng */
+{
+       MD5_CTX md5_ctx;
+       struct timeval tv, tvo;
+       struct rusage ru;
+       int n=0;
+       int t;
+       MD5Init (&md5_ctx);
+       t=getpid();
+       MD5Update (&md5_ctx, (u_char*)&t, sizeof t);
+       t=getppid();
+       MD5Update (&md5_ctx, (u_char*)&t, sizeof t);
+       gettimeofday (&tvo, NULL);
+       do {
+               getrusage (RUSAGE_SELF, &ru);
+               MD5Update (&md5_ctx, (u_char*)&ru, sizeof ru);
+               gettimeofday (&tv, NULL);
+               MD5Update (&md5_ctx, (u_char*)&tv, sizeof tv);
+       } while (n++<20 || tv.tv_usec-tvo.tv_usec< 100*1000);
+       MD5Final (d, &md5_ctx);
+       return d;
+}
+
+static u_char *
+pw_getrand(u_char *buf, int len)
+{
+       int             fd;
+       fd = open("/dev/urandom", O_RDONLY);
+       if (fd == -1 || read(fd, buf, len)!=len) {
+               int n;
+               for (n=0;n<len;n+=16) {
+                       u_char ubuf[16];
+                       pw_genmd5rand(ubuf);
+                       memcpy(buf+n, ubuf, MIN(16, len-n));
+               }
+       }
+       close(fd);
+       return buf;
+}

 static char    *
 pw_password(struct userconf * cnf, struct cargs * args, char const * user)
 {
        int             i, l;
        char            pwbuf[32];
+       u_char          rndbuf[sizeof pwbuf];

        switch (cnf->default_password) {
        case -1:                /* Random password */
                srandom((unsigned) (time(NULL) | getpid()));
                l = (random() % 8 + 8); /* 8 - 16 chars */
+               pw_getrand(rndbuf, l);
                for (i = 0; i<l; i++)
-                       pwbuf[i] = chars[random() % sizeof(chars)];
+                       pwbuf[i] = chars[rndbuf[i] % sizeof(chars)];
                pwbuf[i] = '\0';

                /*