1/* rndlinux.c  -  raw random number for OSes with /dev/random
2 * Copyright (C) 1998, 2001, 2002, 2003, 2007,
3 *               2009  Free Software Foundation, Inc.
4 *
5 * This file is part of Libgcrypt.
6 *
7 * Libgcrypt is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU Lesser General Public License as
9 * published by the Free Software Foundation; either version 2.1 of
10 * the License, or (at your option) any later version.
11 *
12 * Libgcrypt is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 * GNU Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this program; if not, see <http://www.gnu.org/licenses/>.
19 */
20
21
22#include <config.h>
23#include <stdio.h>
24#include <stdlib.h>
25#include <errno.h>
26#include <sys/time.h>
27#include <sys/types.h>
28#include <sys/stat.h>
29#ifdef HAVE_GETTIMEOFDAY
30# include <sys/times.h>
31#endif
32#include <string.h>
33#include <unistd.h>
34#include <fcntl.h>
35#include "types.h"
36#include "g10lib.h"
37#include "rand-internal.h"
38
39static int open_device ( const char *name );
40
41
42static int
43set_cloexec_flag (int fd)
44{
45  int oldflags;
46
47  oldflags= fcntl (fd, F_GETFD, 0);
48  if (oldflags < 0)
49    return oldflags;
50  oldflags |= FD_CLOEXEC;
51  return fcntl (fd, F_SETFD, oldflags);
52}
53
54
55
56/*
57 * Used to open the /dev/random devices (Linux, xBSD, Solaris (if it exists)).
58 */
59static int
60open_device ( const char *name )
61{
62  int fd;
63
64  fd = open ( name, O_RDONLY );
65  if ( fd == -1 )
66    log_fatal ("can't open %s: %s\n", name, strerror(errno) );
67
68  if (set_cloexec_flag (fd))
69    log_error ("error setting FD_CLOEXEC on fd %d: %s\n",
70               fd, strerror (errno));
71
72  /* We used to do the following check, however it turned out that this
73     is not portable since more OSes provide a random device which is
74     sometimes implemented as another device type.
75
76     struct stat sb;
77
78     if( fstat( fd, &sb ) )
79        log_fatal("stat() off %s failed: %s\n", name, strerror(errno) );
80     if( (!S_ISCHR(sb.st_mode)) && (!S_ISFIFO(sb.st_mode)) )
81        log_fatal("invalid random device!\n" );
82  */
83  return fd;
84}
85
86
87int
88_gcry_rndlinux_gather_random (void (*add)(const void*, size_t,
89                                          enum random_origins),
90                              enum random_origins origin,
91                              size_t length, int level )
92{
93  static int fd_urandom = -1;
94  static int fd_random = -1;
95  int fd;
96  int n;
97  byte buffer[768];
98  size_t n_hw;
99  size_t want = length;
100  size_t last_so_far = 0;
101  int any_need_entropy = 0;
102  int delay;
103
104  /* First read from a hardware source.  However let it account only
105     for up to 50% of the requested bytes.  */
106  n_hw = _gcry_rndhw_poll_slow (add, origin);
107  if (n_hw > length/2)
108    n_hw = length/2;
109  if (length > 1)
110    length -= n_hw;
111
112  /* Open the requested device.  */
113  if (level >= 2)
114    {
115      if( fd_random == -1 )
116        fd_random = open_device ( NAME_OF_DEV_RANDOM );
117      fd = fd_random;
118    }
119  else
120    {
121      if( fd_urandom == -1 )
122        fd_urandom = open_device ( NAME_OF_DEV_URANDOM );
123      fd = fd_urandom;
124    }
125
126  /* Enter the read loop.  */
127  delay = 0;  /* Start with 0 seconds so that we do no block on the
128                 first iteration and in turn call the progress function
129                 before blocking.  To give the OS a better chance to
130                 return with something we will actually use 100ms. */
131  while (length)
132    {
133      fd_set rfds;
134      struct timeval tv;
135      int rc;
136
137      FD_ZERO(&rfds);
138      FD_SET(fd, &rfds);
139      tv.tv_sec = delay;
140      tv.tv_usec = delay? 0 : 100000;
141      if ( !(rc=select(fd+1, &rfds, NULL, NULL, &tv)) )
142        {
143          if (!any_need_entropy || last_so_far != (want - length) )
144            {
145              last_so_far = want - length;
146              _gcry_random_progress ("need_entropy", 'X',
147                                     (int)last_so_far, (int)want);
148              any_need_entropy = 1;
149	    }
150          delay = 3; /* Use 3 seconds henceforth.  */
151	  continue;
152	}
153      else if( rc == -1 )
154        {
155          log_error ("select() error: %s\n", strerror(errno));
156          if (!delay)
157            delay = 1; /* Use 1 second if we encounter an error before
158                          we have ever blocked.  */
159          continue;
160        }
161
162      do
163        {
164          int nbytes = length < sizeof(buffer)? length : sizeof(buffer);
165          n = read(fd, buffer, nbytes );
166          if( n >= 0 && n > nbytes )
167            {
168              log_error("bogus read from random device (n=%d)\n", n );
169              n = nbytes;
170            }
171        }
172      while( n == -1 && errno == EINTR );
173      if ( n == -1 )
174        log_fatal("read error on random device: %s\n", strerror(errno));
175      (*add)( buffer, n, origin );
176      length -= n;
177    }
178  memset(buffer, 0, sizeof(buffer) );
179
180  if (any_need_entropy)
181    _gcry_random_progress ("need_entropy", 'X', (int)want, (int)want);
182
183  return 0; /* success */
184}
185