increase log level for this failed setsockopt call. EINVAL is a normal error on Solar...
[jra/samba/.git] / source / lib / genrand.c
index ab0dadebcf24d81100a4f5cf13c604387a79ea6a..4590b812c58271c85c820b456f6ef26084f1860e 100644 (file)
@@ -1,14 +1,13 @@
 /* 
-   Unix SMB/Netbios implementation.
-   Version 1.9.
+   Unix SMB/CIFS implementation.
 
    Functions to create reasonable random numbers for crypto use.
 
-   Copyright (C) Jeremy Allison 1998
+   Copyright (C) Jeremy Allison 2001
    
    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
-   the Free Software Foundation; either version 2 of the License, or
+   the Free Software Foundation; either version 3 of the License, or
    (at your option) any later version.
    
    This program is distributed in the hope that it will be useful,
    GNU General Public License for more details.
    
    You should have received a copy of the GNU General Public License
-   along with this program; if not, write to the Free Software
-   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
 
 #include "includes.h"
 
-extern int DEBUGLEVEL;
-static uint32 counter = 0;
+static unsigned char smb_arc4_state[258];
+static uint32 counter;
+
+static bool done_reseed = False;
+static void (*reseed_callback)(int *newseed);
+
+/**************************************************************** 
+ Copy any user given reseed data.
+*****************************************************************/
+
+void set_rand_reseed_callback(void (*fn)(int *))
+{
+       reseed_callback = fn;
+       set_need_random_reseed();
+}
+
+void set_need_random_reseed(void)
+{
+       done_reseed = False;
+}
+
+static void get_rand_reseed_data(int *reseed_data)
+{
+       if (reseed_callback) {
+               reseed_callback(reseed_data);
+       } else {
+               *reseed_data = 0;
+       }
+}
 
 /****************************************************************
-get a 16 byte hash from the contents of a file
-Note that the hash is not initialised.
+ Get a 16 byte hash from the contents of a file.
+ Note that the hash is not initialised.
 *****************************************************************/
-static void do_filehash(char *fname, unsigned char *hash)
+
+static void do_filehash(const char *fname, unsigned char *the_hash)
 {
        unsigned char buf[1011]; /* deliberate weird size */
        unsigned char tmp_md4[16];
        int fd, n;
 
        fd = sys_open(fname,O_RDONLY,0);
-       if (fd == -1) return;
+       if (fd == -1)
+               return;
 
        while ((n = read(fd, (char *)buf, sizeof(buf))) > 0) {
                mdfour(tmp_md4, buf, n);
                for (n=0;n<16;n++)
-                       hash[n] ^= tmp_md4[n];
+                       the_hash[n] ^= tmp_md4[n];
        }
        close(fd);
 }
 
-
-
-/****************************************************************
- Try and get a seed by looking at the atimes of files in a given
- directory. XOR them into the buf array.
-*****************************************************************/
-
-static void do_dirrand(char *name, unsigned char *buf, int buf_len)
-{
-  DIR *dp = opendir(name);
-  pstring fullname;
-  size_t len_left;
-  size_t fullname_len;
-  char *pos;
-
-  pstrcpy(fullname, name);
-  fullname_len = strlen(fullname);
-
-  if(fullname_len + 2 > sizeof(pstring))
-    return;
-
-  if(fullname[fullname_len] != '/') {
-    fullname[fullname_len] = '/';
-    fullname[fullname_len+1] = '\0';
-    fullname_len = strlen(fullname);
-  }
-
-  len_left = sizeof(pstring) - fullname_len - 1;
-  pos = &fullname[fullname_len];
-
-  if(dp != NULL) {
-    char *p;
-
-    while ((p = readdirname(dp))) {           
-      SMB_STRUCT_STAT st;
-
-      if(strlen(p) <= len_left)
-        pstrcpy(pos, p);
-
-      if(sys_stat(fullname,&st) == 0) {
-        SIVAL(buf, ((counter * 4)%(buf_len-4)), 
-              IVAL(buf,((counter * 4)%(buf_len-4))) ^ st.st_atime);
-        counter++;
-        DEBUG(10,("do_dirrand: value from file %s.\n", fullname));
-      }
-    }
-    closedir(dp); 
-  }
-}
-
 /**************************************************************
  Try and get a good random number seed. Try a number of
- different factors. Firstly, try /dev/urandom and try and
- read from this. If this fails iterate through /tmp and
- /dev and XOR all the file timestamps. Next add in
- a hash of the contents of /etc/shadow and the smb passwd
- file and a combination of pid and time of day (yes I know this
- sucks :-). Finally md4 the result.
+ different factors. Firstly, try /dev/urandom - use if exists.
 
  We use /dev/urandom as a read of /dev/random can block if
  the entropy pool dries up. This leads clients to timeout
  or be very slow on connect.
 
- The result goes in a 16 byte buffer passed from the caller
+ If we can't use /dev/urandom then seed the stream random generator
+ above...
 **************************************************************/
 
-static uint32 do_reseed(unsigned char *md4_outbuf)
+static int do_reseed(bool use_fd, int fd)
 {
-  unsigned char md4_inbuf[40];
-  BOOL got_random = False;
-  uint32 v1, v2, ret;
-  int fd;
-  struct timeval tval;
-  pid_t mypid;
-  struct passwd *pw;
-
-  memset(md4_inbuf, '\0', sizeof(md4_inbuf));
-
-  fd = sys_open( "/dev/urandom", O_RDONLY,0);
-  if(fd >= 0) {
-    /* 
-     * We can use /dev/urandom !
-     */
-    if(read(fd, md4_inbuf, 40) == 40) {
-      got_random = True;
-      DEBUG(10,("do_reseed: got 40 bytes from /dev/urandom.\n"));
-    }
-    close(fd);
-  }
-
-  if(!got_random) {
-    /*
-     * /dev/urandom failed - try /dev for timestamps.
-     */
-    do_dirrand("/dev", md4_inbuf, sizeof(md4_inbuf));
-  }
-
-  /* possibly add in some secret file contents */
-  do_filehash("/etc/shadow", &md4_inbuf[0]);
-  do_filehash(lp_smb_passwd_file(), &md4_inbuf[16]);
-
-  /* add in the root encrypted password. On any system where security is taken
-     seriously this will be secret */
-  pw = getpwnam("root");
-  if (pw && pw->pw_passwd) {
-         int i;
-         unsigned char md4_tmp[16];
-         mdfour(md4_tmp, (unsigned char *)pw->pw_passwd, strlen(pw->pw_passwd));
-         for (i=0;i<16;i++)
-                 md4_inbuf[8+i] ^= md4_tmp[i];
-  }
-
-  /*
-   * Finally add the counter, time of day, and pid.
-   */
-  GetTimeOfDay(&tval);
-  mypid = getpid();
-  v1 = (counter++) + mypid + tval.tv_sec;
-  v2 = (counter++) * mypid + tval.tv_usec;
-
-  SIVAL(md4_inbuf, 32, v1 ^ IVAL(md4_inbuf, 32));
-  SIVAL(md4_inbuf, 36, v2 ^ IVAL(md4_inbuf, 36));
-
-  mdfour(md4_outbuf, md4_inbuf, sizeof(md4_inbuf));
-
-  /*
-   * Return a 32 bit int created from XORing the
-   * 16 bit return buffer.
-   */
-
-  ret = IVAL(md4_outbuf, 0);
-  ret ^= IVAL(md4_outbuf, 4);
-  ret ^= IVAL(md4_outbuf, 8);
-  return (ret ^ IVAL(md4_outbuf, 12));
+       unsigned char seed_inbuf[40];
+       uint32 v1, v2; struct timeval tval; pid_t mypid;
+       struct passwd *pw;
+       int reseed_data = 0;
+
+       if (use_fd) {
+               if (fd != -1)
+                       return fd;
+
+               fd = sys_open( "/dev/urandom", O_RDONLY,0);
+               if(fd >= 0)
+                       return fd;
+       }
+
+       /* Add in some secret file contents */
+
+       do_filehash("/etc/shadow", &seed_inbuf[0]);
+       do_filehash(lp_smb_passwd_file(), &seed_inbuf[16]);
+
+       /*
+        * Add in the root encrypted password.
+        * On any system where security is taken
+        * seriously this will be secret.
+        */
+
+       pw = getpwnam_alloc(NULL, "root");
+       if (pw && pw->pw_passwd) {
+               size_t i;
+               unsigned char md4_tmp[16];
+               mdfour(md4_tmp, (unsigned char *)pw->pw_passwd, strlen(pw->pw_passwd));
+               for (i=0;i<16;i++)
+                       seed_inbuf[8+i] ^= md4_tmp[i];
+               TALLOC_FREE(pw);
+       }
+
+       /*
+        * Add the counter, time of day, and pid.
+        */
+
+       GetTimeOfDay(&tval);
+       mypid = sys_getpid();
+       v1 = (counter++) + mypid + tval.tv_sec;
+       v2 = (counter++) * mypid + tval.tv_usec;
+
+       SIVAL(seed_inbuf, 32, v1 ^ IVAL(seed_inbuf, 32));
+       SIVAL(seed_inbuf, 36, v2 ^ IVAL(seed_inbuf, 36));
+
+       /*
+        * Add any user-given reseed data.
+        */
+
+       get_rand_reseed_data(&reseed_data);
+       if (reseed_data) {
+               size_t i;
+               for (i = 0; i < sizeof(seed_inbuf); i++)
+                       seed_inbuf[i] ^= ((char *)(&reseed_data))[i % sizeof(reseed_data)];
+       }
+
+       smb_arc4_init(smb_arc4_state, seed_inbuf, sizeof(seed_inbuf));
+
+       return -1;
 }
 
 /*******************************************************************
  Interface to the (hopefully) good crypto random number generator.
 ********************************************************************/
 
-void generate_random_buffer( unsigned char *out, int len, BOOL re_seed)
+void generate_random_buffer( unsigned char *out, int len)
 {
-  static BOOL done_reseed = False;
-  static unsigned char md4_buf[16];
-  unsigned char tmp_buf[16];
-  unsigned char *p;
-
-  if(!done_reseed || re_seed) {
-         sys_srandom(do_reseed(md4_buf));
-         done_reseed = True;
-  }
-
-  /*
-   * Generate random numbers in chunks of 64 bytes,
-   * then md4 them & copy to the output buffer.
-   * Added XOR with output from random, seeded
-   * by the original md4_buf. This is to stop the
-   * output from this function being the previous
-   * md4_buf md4'ed. The output from this function
-   * is often output onto the wire, and so it should
-   * not be possible to guess the next output from
-   * this function based on the previous output.
-   * XORing in the output from random(), seeded by
-   * the original md4 hash should stop this. JRA.
-   */
-
-  p = out;
-  while(len > 0) {
-    int i;
-    int copy_len = len > 16 ? 16 : len;
-    mdfour(tmp_buf, md4_buf, sizeof(md4_buf));
-    memcpy(md4_buf, tmp_buf, sizeof(md4_buf));
-    /* XOR in output from random(). */
-    for(i = 0; i < 4; i++)
-      SIVAL(tmp_buf, i*4, (IVAL(tmp_buf, i*4) ^ (uint32)sys_random()));
-    memcpy(p, tmp_buf, copy_len);
-    p += copy_len;
-    len -= copy_len;
-  }
+       static int urand_fd = -1;
+       unsigned char md4_buf[64];
+       unsigned char tmp_buf[16];
+       unsigned char *p;
+
+       if(!done_reseed) {
+               urand_fd = do_reseed(True, urand_fd);
+               done_reseed = True;
+       }
+
+       if (urand_fd != -1 && len > 0) {
+
+               if (read(urand_fd, out, len) == len)
+                       return; /* len bytes of random data read from urandom. */
+
+               /* Read of urand error, drop back to non urand method. */
+               close(urand_fd);
+               urand_fd = -1;
+               do_reseed(False, -1);
+               done_reseed = True;
+       }
+
+       /*
+        * Generate random numbers in chunks of 64 bytes,
+        * then md4 them & copy to the output buffer.
+        * This way the raw state of the stream is never externally
+        * seen.
+        */
+
+       p = out;
+       while(len > 0) {
+               int copy_len = len > 16 ? 16 : len;
+
+               smb_arc4_crypt(smb_arc4_state, md4_buf, sizeof(md4_buf));
+               mdfour(tmp_buf, md4_buf, sizeof(md4_buf));
+               memcpy(p, tmp_buf, copy_len);
+               p += copy_len;
+               len -= copy_len;
+       }
+}
+
+/*******************************************************************
+ Use the random number generator to generate a random string.
+********************************************************************/
+
+static char c_list[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+_-#.,";
+
+char *generate_random_str(size_t len)
+{
+       static unsigned char retstr[256];
+       size_t i;
+
+       memset(retstr, '\0', sizeof(retstr));
+
+       if (len > sizeof(retstr)-1)
+               len = sizeof(retstr) -1;
+       generate_random_buffer( retstr, len);
+       for (i = 0; i < len; i++)
+               retstr[i] = c_list[ retstr[i] % (sizeof(c_list)-1) ];
+
+       retstr[i] = '\0';
+
+       return (char *)retstr;
 }