Skip to content
GitLab
Projects
Groups
Snippets
/
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Menu
Open sidebar
LSH
lsh
Commits
b6a32a1d
Commit
b6a32a1d
authored
Oct 31, 2000
by
Niels Möller
Browse files
* src/unix_random.c: Hacked and debugged. Seems to work now.
Rev: src/unix_random.c:1.2
parent
7f77430f
Changes
1
Hide whitespace changes
Inline
Side-by-side
src/unix_random.c
View file @
b6a32a1d
...
...
@@ -25,14 +25,49 @@
*/
#include
"randomness.h"
#include
"crypto.h"
#include
"reaper.h"
#include
"xalloc.h"
#include
"werror.h"
#include
<assert.h>
#include
<string.h>
#if HAVE_UNISTD_H
#include
<unistd.h>
#endif
#include
<sys/types.h>
#include
<sys/stat.h>
#include
<fcntl.h>
#include
<sys/wait.h>
#include
<pwd.h>
#ifdef HAVE_POLL
# if HAVE_POLL_H
# include <poll.h>
# elif HAVE_SYS_POLL_H
# include <sys/poll.h>
# endif
#else
# include "jpoll.h"
#endif
/* Workaround for some version of FreeBSD. */
#ifdef POLLRDNORM
# define MY_POLLIN (POLLIN | POLLRDNORM)
#else
/* !POLLRDNORM */
# define MY_POLLIN POLLIN
#endif
/* !POLLRDNORM */
#include
<sys/time.h>
#include
<sys/resource.h>
enum
poll_status
{
POLL_NO_POLL
,
POLL_RUNNING
,
POLL_FINISHED
,
POLL_FAILED
};
#include
"unix_random.c.x"
/* GABA:
(class
(name unix_random)
...
...
@@ -61,12 +96,21 @@ enum poll_status { POLL_NO_POLL, POLL_RUNNING, POLL_FINISHED, POLL_FAILED };
static
void
do_unix_random_callback
(
struct
exit_callback
*
s
,
int
signalled
,
int
core
,
int
value
)
int
signalled
,
int
core
UNUSED
,
int
value
)
{
CAST
(
unix_random_callback
,
self
,
s
);
self
->
ctx
->
status
=
(
signalled
||
value
)
?
POLL_FAILED
:
POLL_FINISHED
;
if
(
signalled
||
value
)
{
self
->
ctx
->
status
=
POLL_FAILED
;
trace
(
"unix_random.c: do_unix_random_callback, background poll failed.
\n
"
);
}
else
{
self
->
ctx
->
status
=
POLL_FINISHED
;
trace
(
"unix_random.c: do_unix_random_callback, background poll finished.
\n
"
);
}
}
static
struct
exit_callback
*
...
...
@@ -97,7 +141,9 @@ struct unix_random_source
const
char
*
arg
;
/* For now, use at most one argument. */
int
has_alternative
;
unsigned
rounding
;
/* Add this before rounding */
/* If non-zero, count quality bits of entropy if the amount of output
* exceeds this value. */
unsigned
small
;
/* Bits of entropy per UNIX_RANDOM_SOURCE_SCALE bytes of output. */
unsigned
quality
;
};
...
...
@@ -105,7 +151,7 @@ struct unix_random_source
/* Lots of output expected; round downwards. */
#define WLARGE(x) 0, x
/* Small but significant output expected; round upwards */
#define WSMALL(x)
(UNIX_RANDOM_SOURCE_SCALE - 1)
, x
#define WSMALL(x)
100
, x
static
const
struct
unix_random_source
random_sources
[]
=
{
...
...
@@ -239,17 +285,19 @@ struct unix_random_source_state
static
int
spawn_source_process
(
unsigned
*
index
,
struct
unix_random_source_state
*
state
)
struct
unix_random_source_state
*
state
,
int
null
)
{
unsigned
i
;
pid_t
pid
;
/* output[0] for reading, output[1] for writing. */
int
output
[
2
];
for
(
i
=
*
index
;
random_sources
[
i
].
path
;
)
{
int
output
[
2
];
pid_t
pid
;
if
(
access
(
random_sources
[
i
].
path
,
X_OK
)
<
0
)
{
debug
(
"spawn_source_process:
Can'
t execut
e '%z'
: %z
\n
"
,
debug
(
"
unix_random.c:
spawn_source_process:
Skipping '%z'; no
t execut
able
: %z
\n
"
,
random_sources
[
i
].
path
,
STRERROR
(
errno
));
i
++
;
}
...
...
@@ -257,9 +305,15 @@ spawn_source_process(unsigned *index,
break
;
}
if
(
!
random_sources
[
i
].
path
)
{
*
index
=
i
;
return
0
;
}
if
(
!
lsh_make_pipe
(
output
))
{
werror
(
"spawn_source_process: Can't create pipe (errno = %i): %z
\n
"
,
werror
(
"
unix_random.c:
spawn_source_process: Can't create pipe (errno = %i): %z
\n
"
,
errno
,
STRERROR
(
errno
));
return
0
;
}
...
...
@@ -274,6 +328,10 @@ spawn_source_process(unsigned *index,
i
++
;
*
index
=
i
;
debug
(
"unix_random.c: Trying source %z %z
\n
"
,
state
->
source
->
path
,
state
->
source
->
arg
?
state
->
source
->
arg
:
""
);
pid
=
fork
();
switch
(
pid
)
...
...
@@ -282,51 +340,252 @@ spawn_source_process(unsigned *index,
/* Parent */
close
(
output
[
1
]);
state
->
fd
=
output
[
0
];
io_set_close_on_exec
(
self
->
fd
);
state
->
amount
=
0
;
state
->
pid
=
pid
;
io_set_close_on_exec
(
state
->
fd
);
io_set_nonblocking
(
state
->
fd
);
state
->
length
=
0
;
return
1
;
case
-
1
:
/* Error */
close
(
output
[
0
]);
close
(
output
[
1
]);
return
0
;
case
0
:
/* Child */
if
(
dup2
(
output
[
1
],
STDOUT_FILENO
))
if
(
dup2
(
output
[
1
],
STDOUT_FILENO
)
<
0
)
{
werror
(
"unix_random.c: spawn_source_process: dup2 for stdout failed (errno = %i): %z
\n
"
,
errno
,
STRERROR
(
errno
));
_exit
(
EXIT_FAILURE
);
}
/* Ignore stderr. */
if
(
dup2
(
null
,
STDERR_FILENO
)
<
0
)
{
werror
(
"spawn_source_process: dup2 for std
out
failed (errno = %i): %z
\n
"
,
werror
(
"
unix_random.c:
spawn_source_process: dup2 for std
err
failed (errno = %i): %z
\n
"
,
errno
,
STRERROR
(
errno
));
_exit
(
EXIT_FAILURE
);
}
close
(
output
[
0
]);
close
(
output
[
1
]);
/* Works also if state->source
.
arg == NULL */
execl
(
state
->
source
.
path
,
state
->
source
.
arg
,
NULL
);
/* Works also if state->source
->
arg == NULL */
execl
(
state
->
source
->
path
,
state
->
source
->
path
,
state
->
source
->
arg
,
NULL
);
werror
(
"spawn_source_process: execl '%z' failed (errno = %i): %z
\n
"
,
state
->
source
.
path
,
errno
,
STRERROR
(
errno
));
state
->
source
->
path
,
errno
,
STRERROR
(
errno
));
_exit
(
EXIT_FAILURE
);
}
}
static
unsigned
use_dev_random
(
struct
hash_instance
*
hash
)
{
#define DEVRANDOM_SIZE 40
UINT8
buffer
[
DEVRANDOM_SIZE
];
unsigned
count
=
0
;
int
res
;
int
fd
=
open
(
"/dev/urandom"
,
O_RDONLY
);
if
(
fd
<
0
)
return
0
;
do
{
res
=
read
(
fd
,
buffer
,
DEVRANDOM_SIZE
);
}
while
(
(
res
<
0
)
&&
(
errno
==
EINTR
));
if
(
res
<
0
)
werror
(
"unix_random.c: Reading from /dev/urandom failed (errno = %i): %z
\n
"
,
errno
,
STRERROR
(
errno
));
else
if
(
res
>
0
)
{
HASH_UPDATE
(
hash
,
res
,
buffer
);
/* Count 4 bits of entropy for each byte. */
count
=
res
*
4
;
}
else
werror
(
"unix_random.c: No data available on /dev/urandom!
\n
"
);
close
(
fd
);
return
count
;
}
static
unsigned
use_procfs
(
struct
hash_instance
*
hash
)
{
#if 0
/* Peter Gutmann's code uses the following sources. I'm not sure
* what quality to assign to them. */
static const char *proc_sources[] =
{
"/proc/interrupts", "/proc/loadavg", "/proc/locks",
"/proc/meminfo", "/proc/stat", "/proc/net/tcp", "/proc/net/udp",
"/proc/net/dev", "/proc/net/ipx", NULL
};
#endif
/* Not implemented. */
return
0
;
}
/* Spawn this number of processes. */
#define UNIX_RANDOM_POLL_PROCESSES 10
#define UNIX_RANDOM_THRESHOLD 200
static
void
background_poll
(
struct
unix_random_poll_result
*
result
)
static
unsigned
background_poll
(
struct
hash_instance
*
hash
)
{
struct
unix_random_source_state
state
[
UNIX_RANDOM_POLL_PROCESSES
];
unsigned
count
=
0
;
unsigned
running
=
0
;
unsigned
i
=
0
;
unsigned
i
;
unsigned
index
=
0
;
int
null
;
for
(
running
=
0
;
running
<
UNIX_RANDOM_POLL_PROCESSES
;
running
++
)
trace
(
"unix_random.c: background_poll
\n
"
);
null
=
open
(
"/dev/null"
,
O_WRONLY
);
if
(
null
<
0
)
{
werror
(
"unix_random.c: background_poll: Failed to open /dev/null (errno = %i): %z
\n
"
,
errno
,
STRERROR
(
errno
));
return
count
;
}
count
+=
use_dev_random
(
hash
);
count
+=
use_procfs
(
hash
);
for
(
i
=
0
;
i
<
UNIX_RANDOM_POLL_PROCESSES
;
i
++
)
state
[
i
].
fd
=
-
1
;
for
(
running
=
0
;
running
<
UNIX_RANDOM_POLL_PROCESSES
;
running
++
)
{
if
(
!
spawn_source_process
(
&
index
,
state
+
running
,
null
))
break
;
}
while
(
running
)
{
struct
pollfd
fds
[
UNIX_RANDOM_POLL_PROCESSES
];
unsigned
nfds
;
unsigned
i
;
for
(
i
=
0
,
nfds
=
0
;
i
<
UNIX_RANDOM_POLL_PROCESSES
;
i
++
)
if
(
state
[
i
].
fd
>
0
)
{
fds
[
nfds
].
fd
=
state
[
i
].
fd
;
fds
[
nfds
].
events
=
MY_POLLIN
;
nfds
++
;
}
assert
(
nfds
==
running
);
debug
(
"unix_random.c: calling poll, nfds = %i
\n
"
,
nfds
);
if
(
poll
(
fds
,
nfds
,
-
1
)
<
0
)
{
werror
(
"unix_random.c: background_poll poll() failed (errno = %i): %z
\n
"
,
errno
,
STRERROR
(
errno
));
return
count
;
}
debug
(
"unix_random.c: returned from poll.
\n
"
);
for
(
i
=
0
;
i
<
nfds
;
i
++
)
{
debug
(
"background_poll: poll for fd %i: events = 0x%xi, revents = 0x%xi.
\n
"
,
fds
[
i
].
fd
,
fds
[
i
].
events
,
fds
[
i
].
revents
);
/* Linux sets POLLHUP on EOF */
#ifndef POLLHUP
#define POLLHUP 0
#endif
if
(
fds
[
i
].
revents
&
(
MY_POLLIN
|
POLLHUP
))
{
#define BUFSIZE 1024
UINT8
buffer
[
BUFSIZE
];
int
res
;
unsigned
j
;
for
(
j
=
0
;
j
<
UNIX_RANDOM_POLL_PROCESSES
;
j
++
)
if
(
fds
[
i
].
fd
==
state
[
j
].
fd
)
break
;
assert
(
j
<
UNIX_RANDOM_POLL_PROCESSES
);
debug
(
"background_poll: reading from source %z
\n
"
,
state
[
j
].
source
->
path
);
res
=
read
(
fds
[
i
].
fd
,
buffer
,
BUFSIZE
);
#undef BUFSIZE
if
(
res
<
0
)
{
if
(
errno
!=
EINTR
)
{
werror
(
"unix_random.c: background_poll read() failed (errno = %i): %z
\n
"
,
errno
,
STRERROR
(
errno
));
return
count
;
}
}
else
if
(
res
>
0
)
{
HASH_UPDATE
(
hash
,
res
,
buffer
);
state
[
j
].
length
+=
res
;
}
else
{
/* EOF */
int
status
;
unsigned
entropy
;
close
(
fds
[
i
].
fd
);
state
[
j
].
fd
=
-
1
;
/* Estimate entropy */
entropy
=
state
[
j
].
length
*
state
[
j
].
source
->
quality
/
UNIX_RANDOM_SOURCE_SCALE
;
if
(
!
entropy
&&
state
[
j
].
source
->
small
&&
(
state
[
j
].
length
>
state
[
j
].
source
->
small
))
entropy
=
state
[
j
].
source
->
quality
;
debug
(
"unix_random.c: background_poll: Got %i bytes from %z %z,
\n
"
" estimating %i bits of entropy.
\n
"
,
state
[
j
].
length
,
state
[
j
].
source
->
path
,
state
[
j
].
source
->
arg
?
state
[
j
].
source
->
arg
:
""
,
entropy
);
count
+=
entropy
;
if
(
waitpid
(
state
[
j
].
pid
,
&
status
,
0
)
<
0
)
{
werror
(
"unix_random.c: background_poll waitpid() failed (errno = %i): %z
\n
"
,
errno
,
STRERROR
(
errno
));
return
count
;
}
if
(
WIFEXITED
(
status
))
{
if
(
WEXITSTATUS
(
status
))
werror
(
"unix_random.c: background_poll: %z exited unsuccessfully.
\n
"
,
state
[
j
].
source
->
path
);
}
else
if
(
WIFSIGNALED
(
status
))
{
werror
(
"unix_random.c: background_poll: %z died from signal %i (%z).
\n
"
,
state
[
j
].
source
->
path
,
WTERMSIG
(
status
),
STRSIGNAL
(
WTERMSIG
(
status
)));
}
if
(
!
((
count
<
UNIX_RANDOM_THRESHOLD
)
&&
spawn_source_process
(
&
index
,
state
+
j
,
null
)))
running
--
;
}
}
}
}
return
count
;
}
static
void
...
...
@@ -334,22 +593,16 @@ start_background_poll(struct unix_random *self)
{
pid_t
pid
;
int
output
[
2
];
int
null
;
assert
(
self
->
status
==
POLL_NO_POLL
);
trace
(
"unix_random.c: start_background_poll
\n
"
);
if
(
!
lsh_make_pipe
(
output
))
{
werror
(
"Failed to create pipe for background randomness poll.
\n
"
);
return
;
}
null
=
open
(
"/dev/null"
,
O_RDONLY
);
if
(
dup2
(
null
,
STDIN_FILENO
)
<
0
)
werror
(
"start_background_poll: dup2 for stdin failed (errno = %i): %z
\n
"
,
errno
,
STRERROR
(
errno
));
close
(
null
);
pid
=
fork
();
switch
(
pid
)
...
...
@@ -359,9 +612,16 @@ start_background_poll(struct unix_random *self)
close
(
output
[
1
]);
self
->
fd
=
output
[
0
];
io_set_close_on_exec
(
self
->
fd
);
if
(
self
->
reaper
)
REAP
(
self
->
reaper
,
pid
,
make_unix_random_callback
(
self
));
REAP
(
self
->
reaper
,
pid
,
make_unix_random_callback
(
self
))
;
self
->
pid
=
pid
;
self
->
status
=
POLL_RUNNING
;
werror
(
"unix_random.c: Started background poll. pid = %i.
\n
"
,
pid
);
return
;
case
-
1
:
...
...
@@ -374,7 +634,31 @@ start_background_poll(struct unix_random *self)
/* Child */
{
struct
unix_random_poll_result
result
;
struct
hash_instance
*
hash
=
MAKE_HASH
(
&
sha1_algorithm
);
#if 0
int hang = 1;
while (hang)
sleep(1);
#endif
int
null
=
open
(
"/dev/null"
,
O_RDONLY
);
if
(
null
<
0
)
{
werror
(
"start_background_poll: Failed to open /dev/null (errno = %i): %z
\n
"
,
errno
,
STRERROR
(
errno
));
_exit
(
EXIT_FAILURE
);
}
if
(
dup2
(
null
,
STDIN_FILENO
)
<
0
)
{
werror
(
"start_background_poll: dup2 for stdin failed (errno = %i): %z
\n
"
,
errno
,
STRERROR
(
errno
));
_exit
(
EXIT_FAILURE
);
}
close
(
null
);
close
(
output
[
0
]);
io_set_close_on_exec
(
output
[
1
]);
...
...
@@ -383,13 +667,17 @@ start_background_poll(struct unix_random *self)
setuid
(
self
->
poll_uid
);
if
(
!
getuid
())
_exit
(
1
);
_exit
(
EXIT_FAILURE
);
result
.
count
=
background_poll
(
hash
);
background_poll
(
&
result
);
assert
(
hash
->
hash_size
==
sizeof
(
result
.
data
));
HASH_DIGEST
(
hash
,
result
.
data
);
if
(
!
write_raw
(
output
[
1
],
sizeof
(
result
),
(
UINT8
*
)
&
result
))
_exit
(
0
);
_exit
(
EXIT_SUCCESS
);
_exit
(
2
);
_exit
(
EXIT_FAILURE
);
}
}
}
...
...
@@ -398,10 +686,10 @@ static void
wait_background_poll
(
struct
unix_random
*
self
)
{
int
status
;
pid_t
child
;
assert
(
self
->
state
==
POLL_RUNNING
);
self
->
state
=
POLL_FAILED
;
assert
(
self
->
status
==
POLL_RUNNING
);
trace
(
"unix_random.c: wait_background_poll
\n
"
);
self
->
status
=
POLL_FAILED
;
if
(
waitpid
(
self
->
pid
,
&
status
,
0
)
==
self
->
pid
)
{
...
...
@@ -409,14 +697,15 @@ wait_background_poll(struct unix_random *self)
self
->
status
=
POLL_FINISHED
;
}
REAP
(
self
->
reaper
,
self
->
pid
,
NULL
);
if
(
self
->
reaper
)
REAP
(
self
->
reaper
,
self
->
pid
,
NULL
);
}
static
unsigned
finish_background_poll
(
struct
unix_random
*
self
,
struct
hash_instance
*
hash
)
{
unsigned
count
;
unsigned
count
=
0
;
switch
(
self
->
status
)
{
...
...
@@ -438,7 +727,6 @@ finish_background_poll(struct unix_random *self, struct hash_instance *hash)
}
case
POLL_FAILED
:
werror
(
"Background randomness poll failed.
\n
"
);
count
=
0
;
break
;
case
POLL_NO_POLL
:
...
...
@@ -453,11 +741,32 @@ finish_background_poll(struct unix_random *self, struct hash_instance *hash)
return
count
;
}
static
unsigned
use_seed_file
(
struct
hash_instance
*
hash
)
{
if
(
getuid
())
{
/* Lock and read seed file. */
}
return
0
;
}
#define HASH_OBJECT(h, x) HASH_UPDATE((h), sizeof(x), (UINT8 *) &(x))
static
unsigned
do_unix_random_slow
(
struct
random_poll
*
s
,
struct
hash_instance
*
hash
)
{
CAST
(
unix_random
,
self
,
s
);
unsigned
count
;
time_t
now
=
time
(
NULL
);
pid_t
pid
=
getpid
();
/* Make sure we don't start with the same state, if several
* instances are initializing at about the same time. */
HASH_OBJECT
(
hash
,
now
);
HASH_OBJECT
(
hash
,
pid
);
if
(
self
->
status
==
POLL_NO_POLL
)
start_background_poll
(
self
);
...
...
@@ -467,17 +776,17 @@ do_unix_random_slow(struct random_poll *s, struct hash_instance *hash)
count
=
finish_background_poll
(
self
,
hash
);
/* Do this in the main process, as the background process may run as
* different user. */
count
+=
use_seed_file
(
hash
);
count
+=
use_procfs
(
hash
);
return
count
;
}
#define HASH_OBJECT(hash, x) HASH_UPDATE((h), sizeof(x), (UINT8 *) &(x))
static
unsigned
do_unix_random_fast
(
struct
random_poll
*
s
,
struct
hash_instance
*
hash
)
{
CAST
(
unix_random
,
self
,
s
);
unsigned
count
=
0
;
#if HAVE_GETRUSAGE
...
...
@@ -516,7 +825,7 @@ do_unix_random_fast(struct random_poll *s, struct hash_instance *hash)
count
++
;
HASH_OBJECT
(
hash
,
now
);
HASH_OBJECT
(
self
->
time_count
);
HASH_OBJECT
(
hash
,
self
->
time_count
);
self
->
time_count
=
0
;
self
->
previous_time
=
now
;
...
...
@@ -526,3 +835,38 @@ do_unix_random_fast(struct random_poll *s, struct hash_instance *hash)
return
count
;
}
static
void
do_unix_random_background
(
struct
random_poll
*
s
)
{
CAST
(
unix_random
,
self
,
s
);
start_background_poll
(
self
);
}
/* Using a NULL reaper argument is ok. It must be supplied only if the
* application is using a reaper, as that may get to our child process
* before we can waitpid it. */
struct
random_poll
*
make_unix_random
(
struct
reap
*
reaper
)
{
NEW
(
unix_random
,
self
);
struct
passwd
*
pw
=
getpwnam
(
"nobody"
);
self
->
super
.
slow
=
do_unix_random_slow
;
self
->
super
.
fast
=
do_unix_random_fast
;
self
->
super
.
background
=
do_unix_random_background
;
self
->
reaper
=
reaper
;
if
(
pw
&&
pw
->
pw_uid
)
self
->
poll_uid
=
pw
->
pw_uid
;
else
self
->
poll_uid
=
(
uid_t
)
-
1
;
self
->
status
=
POLL_NO_POLL
;