lsh-make-seed.c 30.7 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
/* lsh-make-seed.c
 *
 * Creates an initial yarrow seed file
 *
 */

/* lsh, an implementation of the ssh protocol
 *
 * Copyright (C) 2001 Niels Mller
 *
 * 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 (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

26
27
28
#if HAVE_CONFIG_H
#include "config.h"
#endif
29

Niels Möller's avatar
Niels Möller committed
30
31
/* FIXME: Rewrite using select. Then we won't need jpoll.c anymore */
   
32
#include <assert.h>
33
#include <errno.h>
34
#include <stdlib.h>
35
#include <string.h>
36

Niels Möller's avatar
Niels Möller committed
37
38
#include <fcntl.h>

39
40
41
42
43
44
#if HAVE_UNISTD_H
# include <unistd.h>
#endif

#include <termios.h>

45
46
47
48
49
50
51
52
53
54
55
/* getpwnam needed to lookup the user "nobody" */
#include <pwd.h>

/* It seems setgroups isn't defined in unistd.h */
#include <grp.h>

#include <sys/types.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/wait.h>

56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
#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 */


74
75
#include "nettle/yarrow.h"

76
77
78
79
#include "environ.h"
#include "format.h"
#include "io.h"
#include "lock_file.h"
Niels Möller's avatar
Niels Möller committed
80
#include "lsh_string.h"
81
82
83
84
#include "version.h"
#include "werror.h"
#include "xalloc.h"

85
86
87
88
89
90
91
92
93
#include "lsh-make-seed.c.x"

/* Option parsing */

const char *argp_program_version
= "lsh-make-seed-" VERSION;

const char *argp_program_bug_address = BUG_ADDRESS;

Niels Möller's avatar
Niels Möller committed
94
#define OPT_SLOPPY 0x200
Niels Möller's avatar
Niels Möller committed
95
#define OPT_SERVER 0x201
Niels Möller's avatar
Niels Möller committed
96

97
98
99
/* GABA:
   (class
     (name lsh_make_seed_options)
100
     (super werror_config)
101
     (vars
Niels Möller's avatar
Niels Möller committed
102
103
       ; Directory that should be created if needed
       (directory string)
104
       (filename string)
Niels Möller's avatar
Niels Möller committed
105
106
       (force . int)
       (sloppy . int)))
107
108
109
110
111
112
*/

static struct lsh_make_seed_options *
make_options(void)
{
  NEW(lsh_make_seed_options, self);
113
  init_werror_config(&self->super);
114

Niels Möller's avatar
Niels Möller committed
115
  self->directory = NULL;
116
117
118
119
120
121
122
123
124
125
126
  self->filename = NULL;
  self->force = 0;
  
  return self;
}

static const struct argp_option
main_options[] =
{
  /* Name, key, arg-name, flags, doc, group */
  { "output-file", 'o', "Filename", 0, "Default is ~/.lsh/seed-file", 0 },
Niels Möller's avatar
Niels Möller committed
127
128
  { "server", OPT_SERVER, NULL, 0,
    "Save seed file where the lshd server expects it", 0 },
129
  { "force", 'f', NULL, 0, "Overwrite any existing seed file.", 0 },
Niels Möller's avatar
Niels Möller committed
130
  { "sloppy", OPT_SLOPPY, NULL, 0, "Generate seed file even if we can't "
Niels Möller's avatar
Niels Möller committed
131
    "collect a good amount of randomness from the environment.", 0 },
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
  { NULL, 0, NULL, 0, NULL, 0 }
};
  
static const struct argp_child
main_argp_children[] =
{
  { &werror_argp, 0, "", 0 },
  { NULL, 0, NULL, 0}
};

static error_t
main_argp_parser(int key, char *arg, struct argp_state *state)
{
  CAST(lsh_make_seed_options, self, state->input);

  switch(key)
    {
    default:
      return ARGP_ERR_UNKNOWN;

    case ARGP_KEY_INIT:
153
      state->child_inputs[0] = &self->super;
154
155
156
      break;

    case ARGP_KEY_END:
157
158
159
      if (!werror_init(&self->super))
	argp_failure(state, EXIT_FAILURE, errno, "Failed to open log file");

160
161
      if (!self->filename)
	{
162
	  char *home = getenv(ENV_HOME);
163
164
165
166
167
168
169
170
	  
	  if (!home)
	    {
	      argp_failure(state, EXIT_FAILURE, 0, "$HOME not set.");
	      return EINVAL;
	    }
	  else
	    {
Niels Möller's avatar
Niels Möller committed
171
	      self->directory = ssh_format("%lz/.lsh", home);
172
	      self->filename = ssh_format("%lz/.lsh/yarrow-seed-file", home);
173
174
175
176
177
	    }
	}
      break;
      
    case 'o':
Niels Möller's avatar
Niels Möller committed
178
179
180
181
      if (self->filename)
	argp_error(state, "You can use at most one -o or --server option.");
      else
	self->filename = make_string(arg);
182
      break;
Niels Möller's avatar
Niels Möller committed
183
      
Niels Möller's avatar
Niels Möller committed
184
185
186
187
    case OPT_SLOPPY:
      self->sloppy = 1;
      break;
      
Niels Möller's avatar
Niels Möller committed
188
189
190
191
192
193
194
195
196
197
    case OPT_SERVER:
      if (self->filename)
	argp_error(state, "You can use at most one -o or --server option.");
      else
	{
	  self->directory = make_string("/var/spool/lsh");
	  self->filename =  make_string("/var/spool/lsh/yarrow-seed-file");
	}
      break;
      
198
199
    case 'f':
      self->force = 1;
Niels Möller's avatar
Niels Möller committed
200
      break;
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
    }
  
  return 0;
}

static const struct argp
main_argp =
{ main_options, main_argp_parser, 
  NULL,
  "Creates an initial random seed file for the YARROW pseudorandomness"
  "generator used by lsh.",
  main_argp_children,
  NULL, NULL
};

Niels Möller's avatar
Niels Möller committed
216

Niels Möller's avatar
Niels Möller committed
217
218
/* For cleanup. FIXME: Arrange so that we clean up even if killed by
 * SIGINT or SIGTERM. */
219
220
221
222
223
224
225
226
227
228
229
230
231
232
static struct resource *lock = NULL;
int tty_needs_reset = 0;
struct termios tty_original_mode;

static void
cleanup(void)
{
  if (lock)
    KILL_RESOURCE(lock);

  if (tty_needs_reset)
    tcsetattr(STDIN_FILENO, TCSAFLUSH, &tty_original_mode);
}

Niels Möller's avatar
Niels Möller committed
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
/* The sources we use. */
enum source_type
  {
    /* Data from /dev/random, if available */
    SOURCE_DEV_RANDOM,
    /* Data from /dev/mem, if we have permissions */
    SOURCE_DEV_MEM,
    /* Output from miscellaneous commands */
    SOURCE_SYSTEM,
    /* As a last resort, ask the user to type on the keyboard. */
    SOURCE_USER,

    /* Number of sources */
    NSOURCES
  };

249
#define DEVRANDOM_SIZE 40
Niels Möller's avatar
Niels Möller committed
250

Niels Möller's avatar
Niels Möller committed
251
252
253
/* FIXME: Add a similar function for egd, cryptlib tries to read
 * "/var/run/egd-pool", "/dev/egd-pool", "/etc/egd-pool". */

Niels Möller's avatar
Niels Möller committed
254
255
256
static void
get_dev_random(struct yarrow256_ctx *ctx, enum source_type source)
{
Niels Möller's avatar
Niels Möller committed
257
258
259
  static const char *names[] =
    { "/dev/random", "/dev/urandom",
      NULL };
260
261
262
263
264

  int fd = -1;
  unsigned i;
  int res;
  
265
  uint8_t buffer[DEVRANDOM_SIZE];
266
267
268

  for (i = 0; names[i]; i++)
    {
269
      fd = open(names[i], O_RDONLY);
270
271
272
273
274
275
276
      if (fd >= 0)
	break;
    }

  if (fd < 0)
    return;

Niels Möller's avatar
Niels Möller committed
277
  verbose("Reading %z...\n", names[i]);
278
279
280
281
282
283

  do
    { res = read(fd, buffer, DEVRANDOM_SIZE); }
  while ( (res < 0) && (errno == EINTR));
  
  if (res < 0)
284
285
    werror("Reading from %z failed %e\n",
	   names[i], errno);
286
287
288
289

  else if (res > 0)
    {
      /* Count 4 bits of entropy for each byte. */
Niels Möller's avatar
Niels Möller committed
290
291
      verbose("Read %i bytes from /dev/random.\n",
              res);
292
293
294
295
296
297
298
      yarrow256_update(ctx, source, res * 4, res, buffer);
    }
  else
    werror("unix_random.c: No data available on %z\n",
	   names[i]);
  
  close(fd);
Niels Möller's avatar
Niels Möller committed
299
300
301
}


302
303
304
/* List of commands based on Peter Gutmann's cryptlib,
 * misc/rndunix.c. <URL:
 * http://www.cs.auckland.ac.nz/~pgut001/cryptlib/> */
Niels Möller's avatar
Niels Möller committed
305
306

#if 0
307
static struct RI {
Niels Möller's avatar
Niels Möller committed
308
309
310
311
312
313
314
315
316
317
318
	const char *path;		/* Path to check for existence of source */
	const char *arg;		/* Args for source */
	const int usefulness;	/* Usefulness of source */
	FILE *pipe;				/* Pipe to source as FILE * */
	int pipeFD;				/* Pipe to source as FD */
	pid_t pid;				/* pid of child for waitpid() */
	int length;				/* Quantity of output produced */
	const BOOLEAN hasAlternative;	/* Whether source has alt.location */
	} dataSources[] = {
	{ "/bin/vmstat", "-s", SC( -3 ), NULL, 0, 0, 0, TRUE },
	{ "/usr/bin/vmstat", "-s", SC( -3 ), NULL, 0, 0, 0, FALSE },
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
	... };
#endif

/* FIXME: Are we too conservative here? Most sources get credited with
 * only one or two bits per KB of output. On my system, get_system
 * estimates a total of eight bits of entropy... */
#define UNIX_RANDOM_SOURCE_SCALE 8192

struct unix_random_source
{
  const char *path;
  const char *arg; /* For now, use at most one argument. */
  int has_alternative;

  /* If non-zero, count quality bits of entropy (corresponding to 1K)
   * if the amount of output exceeds this value. */
  unsigned small;
  /* Bits of entropy per UNIX_RANDOM_SOURCE_SCALE bytes of output. */
  unsigned quality;
};

/* Lots of output expected; round downwards. */
#define WLARGE(x) 0, x
/* Small but significant output expected; round upwards */
#define WSMALL(x) 100, x

static const struct unix_random_source
system_sources[] = {
  { "/bin/vmstat", "-s", 1, WSMALL(30) },
  { "/usr/bin/vmstat", "-s", 0, WSMALL(30) },
  { "/bin/vmstat", "-c", 1, WSMALL(30) },
  { "/usr/bin/vmstat", "-c", 0, WSMALL(30) },
  { "/usr/bin/pfstat", NULL, 0, WSMALL(20) },
  { "/bin/vmstat", "-i", 1, WSMALL(20) },
  { "/usr/bin/vmstat", "-i", 0, WSMALL(20) },
  { "/usr/ucb/netstat", "-s", 1, WLARGE(20) },
  { "/usr/bin/netstat", "-s", 1, WLARGE(20) },
  { "/usr/sbin/netstat", "-s", 1, WLARGE(20) },
  { "/bin/netstat", "-s", 1, WLARGE(20) },
  { "/usr/etc/netstat", "-s", 0, WLARGE(20) },
  { "/usr/bin/nfsstat", NULL, 0, WLARGE(20) },
  { "/usr/ucb/netstat", "-m", 1, WSMALL(10) },
  { "/usr/bin/netstat", "-m", 1, WSMALL(10) },
  { "/usr/sbin/netstat", "-m", 1, WSMALL(10) },
  { "/bin/netstat", "-m", 1, WSMALL(10) },
  { "/usr/etc/netstat", "-m", 0, WSMALL(10) },
  { "/usr/ucb/netstat", "-in", 1, WSMALL(10) },
  { "/usr/bin/netstat", "-in", 1, WSMALL(10) },
  { "/usr/sbin/netstat", "-in", 1, WSMALL(10) },
  { "/bin/netstat", "-in", 1, WSMALL(10) },
  { "/usr/etc/netstat", "-in", 0, WSMALL(10) },
  { "/usr/sbin/ntptrace", "-r2 -t1 -nv", 0, WSMALL(10) },
  { "/usr/sbin/snmp_request", "localhost public get 1.3.6.1.2.1.7.1.0",

    0, WSMALL(10) }, /* UDP in */
  { "/usr/sbin/snmp_request", "localhost public get 1.3.6.1.2.1.7.4.0",
    0, WSMALL(10) }, /* UDP out */
  { "/usr/sbin/snmp_request", "localhost public get 1.3.6.1.2.1.4.3.0",
    0, WSMALL(10) }, /* IP ? */
  { "/usr/sbin/snmp_request", "localhost public get 1.3.6.1.2.1.6.10.0",
    0, WSMALL(10) }, /* TCP ? */
  { "/usr/sbin/snmp_request", "localhost public get 1.3.6.1.2.1.6.11.0",
    0, WSMALL(10) }, /* TCP ? */
  { "/usr/sbin/snmp_request", "localhost public get 1.3.6.1.2.1.6.13.0",
    0, WSMALL(10) }, /* TCP ? */
  { "/usr/bin/mpstat", NULL, 0, WLARGE(10) },
  { "/usr/bin/w", NULL, 1, WLARGE(10) },
  { "/usr/bsd/w", NULL, 0, WLARGE(10) },
  { "/usr/bin/df", NULL, 1, WLARGE(10) },
  { "/bin/df", NULL, 0, WLARGE(10) },
  { "/usr/sbin/portstat", NULL, 0, WLARGE(10) },
  { "/usr/bin/iostat", NULL, 0, WLARGE(0) },
  { "/usr/bin/uptime", NULL, 1, WLARGE(0) },
  { "/usr/bsd/uptime", NULL, 0, WLARGE(0) },
  { "/bin/vmstat", "-f", 1, WLARGE(0) },
  { "/usr/bin/vmstat", "-f", 0, WLARGE(0) },
  { "/bin/vmstat", NULL, 1, WLARGE(0) },
  { "/usr/bin/vmstat", NULL, 0, WLARGE(0) },
  { "/usr/ucb/netstat", "-n", 1, WLARGE(5) },
  { "/usr/bin/netstat", "-n", 1, WLARGE(5) },
  { "/usr/sbin/netstat", "-n", 1, WLARGE(5)  },
  { "/bin/netstat", "-n", 1, WLARGE(5)  },
  { "/usr/etc/netstat", "-n", 0, WLARGE(5)  },
Niels Möller's avatar
Niels Möller committed
402
#if defined( __sgi ) || defined( __hpux )
403
  { "/bin/ps", "-el", 1, WLARGE(3) },
Niels Möller's avatar
Niels Möller committed
404
#endif /* __sgi || __hpux */
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
  { "/usr/ucb/ps", "aux", 1, WLARGE(3) },
  { "/usr/bin/ps", "aux", 1, WLARGE(3) },
  { "/bin/ps", "aux", 0, WLARGE(3) },
  { "/usr/bin/ipcs", "-a", 1, WLARGE(5) },
  { "/bin/ipcs", "-a", 0, WLARGE(5) },
  /* Unreliable source, depends on system usage */
  { "/etc/pstat", "-p", 1, WLARGE(5) },
  { "/bin/pstat", "-p", 0, WLARGE(5) },
  { "/etc/pstat", "-S", 1, WLARGE(2) },
  { "/bin/pstat", "-S", 0, WLARGE(2) },
  { "/etc/pstat", "-v", 1, WLARGE(2) },
  { "/bin/pstat", "-v", 0, WLARGE(2) },
  { "/etc/pstat", "-x", 1, WLARGE(2) },
  { "/bin/pstat", "-x", 0, WLARGE(2) },
  { "/etc/pstat", "-t", 1, WLARGE(1) },
  { "/bin/pstat", "-t", 0, WLARGE(1) },
  /* pstat is your friend */
  { "/usr/bin/last", "-n 50", 1, WLARGE(3) },
Niels Möller's avatar
Niels Möller committed
423
#ifdef __sgi
424
  { "/usr/bsd/last", "-50", 0, WLARGE(3) },
Niels Möller's avatar
Niels Möller committed
425
426
#endif /* __sgi */
#ifdef __hpux
427
  { "/etc/last", "-50", 0, WLARGE(3) },
Niels Möller's avatar
Niels Möller committed
428
#endif /* __hpux */
429
  { "/usr/bsd/last", "-n 50", 0, WLARGE(3) },
Niels Möller's avatar
Niels Möller committed
430
#ifdef sun
431
432
433
  { "/usr/bin/showrev", "-a", 0, WLARGE(1) },
  { "/usr/sbin/swap", "-l", 0, WLARGE(0) },
  { "/usr/sbin/prtconf", "-v", 0, WLARGE(0) },
Niels Möller's avatar
Niels Möller committed
434
#endif /* sun */
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
  { "/usr/sbin/psrinfo", NULL, 0, WLARGE(0) },
  { "/usr/local/bin/lsof", "-lnwP", 0, WLARGE(3) },
  /* Output is very system and version-dependent */
  { "/usr/sbin/snmp_request", "localhost public get 1.3.6.1.2.1.5.1.0",
    0, WLARGE(1) }, /* ICMP ? */
  { "/usr/sbin/snmp_request", "localhost public get 1.3.6.1.2.1.5.3.0",
    0, WLARGE(1) }, /* ICMP ? */
  { "/etc/arp", "-a", 1, WLARGE(1) },
  { "/usr/etc/arp", "-a", 1, WLARGE(1) },
  { "/usr/bin/arp", "-a", 1, WLARGE(1) },
  { "/usr/sbin/arp", "-a", 0, WLARGE(1) },
  { "/usr/sbin/ripquery", "-nw 1 127.0.0.1", 0, WLARGE(1) },
  { "/bin/lpstat", "-t", 1, WLARGE(1) },
  { "/usr/bin/lpstat", "-t", 1, WLARGE(1) },
  { "/usr/ucb/lpstat", "-t", 0, WLARGE(1) },
  { "/usr/bin/tcpdump", "-c 5 -efvvx", 0, WLARGE(10) },
  /* This is very environment-dependant.  If
     network traffic is low, it'll probably time
     out before delivering 5 packets, which is OK
     because it'll probably be fixed stuff like
     ARP anyway */
  { "/usr/sbin/advfsstat", "-b usr_domain", 0, WLARGE(0) },
  { "/usr/sbin/advfsstat", "-l 2 usr_domain", 0, WLARGE(5) },
  { "/usr/sbin/advfsstat", "-p usr_domain", 0, WLARGE(0) },

  /* This is a complex and screwball program.  Some
     systems have things like rX_dmn, x = integer,
     for RAID systems, but the statistics are
     pretty dodgy */
Niels Möller's avatar
Niels Möller committed
464
#if 0
465
466
467
468
469
470
471
472
  /* The following aren't enabled since they're somewhat slow and not very
     unpredictable, however they give an indication of the sort of sources
     you can use (for example the finger might be more useful on a
     firewalled internal network) */
  { "/usr/bin/finger", "@ml.media.mit.edu", 0, WLARGE(9) },
  { "/usr/local/bin/wget", "-O - http://lavarand.sgi.com/block.html",
    0, WLARGE(9) },
  { "/bin/cat", "/usr/spool/mqueue/syslog", 0, WLARGE(9) },
Niels Möller's avatar
Niels Möller committed
473
#endif /* 0 */
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
  { NULL, NULL, 0, 0, 0 }
};

struct unix_random_source_state
{
  const struct unix_random_source *source;
  pid_t pid;       /* Running process. */
  int fd;
  unsigned length; /* Amount of data read so far. */

  unsigned entropy; /* Entropy estimate. Used for status messages
		     * only, its yarrow's internal counters that matter
		     * for reseeding. */

  unsigned remainder;   /* Partial entropy, number of bits times
			 *  UNIX_RANDOM_SOURCE_SCALE */
};

static int
spawn_source_process(unsigned *index,
		     struct unix_random_source_state *state,
		     int dev_null, uid_t uid, gid_t gid)
{
  unsigned i;
  pid_t pid;
  /* output[0] for reading, output[1] for writing. */
  int output[2];
      
  for (i = *index; system_sources[i].path; )
    {
      if (access(system_sources[i].path, X_OK) < 0)
	{
506
507
	  debug("unix_random.c: spawn_source_process: Skipping '%z'; not executable %e\n",
		system_sources[i].path, errno);
508
509
510
511
512
513
514
515
516
517
518
	  i++;
	}
      else
	break;
    }

  if (!system_sources[i].path)
    {
      *index = i;
      return 0;
    }
Niels Möller's avatar
Niels Möller committed
519
  
520
521
  if (!lsh_make_pipe(output))
    {
522
      werror("spawn_source_process: Can't create pipe %e\n", errno);
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
      return 0;
    }
  
  state->source = system_sources + i;
  
  if (!system_sources[i].has_alternative)
    i++;
  else
    /* Skip alternatives */
    while (system_sources[i].has_alternative)
      i++;
  
  *index = i;

  verbose("Starting %z %z\n",
	  state->source->path,
	  state->source->arg ? state->source->arg : "");
  
  pid = fork();
  switch(pid)
    {
    default:
      /* Parent */
      close (output[1]);
      state->fd = output[0];
      state->pid = pid;
      io_set_close_on_exec(state->fd);
      io_set_nonblocking(state->fd);

      state->length = 0;
      state->entropy = 0;
      state->remainder = 0;
      
      return 1;
      
    case -1:
      /* Error */
      close(output[0]);
      close(output[1]);
      return 0;
    case 0:
      /* Child */
      /* Change uid to nobody */
      if (uid)
	{
	  if (setgroups(0, NULL) < 0)
	    {
570
	      werror("Failed to clear supplimentary groups list %e\n", errno);
571
572
573
574
575
	      _exit(EXIT_FAILURE);
	    }
	      
	  if (setgid(gid) < 0)
	    {
576
	      werror("Failed to change gid %e\n", errno);
577
578
579
580
	      _exit(EXIT_FAILURE);
	    }
	  if (setuid(uid) < 0)
	    {
581
	      werror("Failed to change uid %e\n", errno);
582
583
584
585
586
587
	      _exit(EXIT_FAILURE);
	    }
	}
	  
      if (dup2(output[1], STDOUT_FILENO) < 0)
	{
588
	  werror("spawn_source_process: dup2 for stdout failed %e\n", errno);
589
590
591
592
593
594
	  _exit(EXIT_FAILURE);
	}

      /* Ignore stderr. */
      if (dup2(dev_null, STDERR_FILENO) < 0)
	{
595
	  werror("spawn_source_process: dup2 for stderr failed %e\n", errno);
596
597
598
599
600
	  _exit(EXIT_FAILURE);
	}
	
      close (output[0]);
      close (output[1]);
601
602
      close (dev_null);

603
604
605
606
      /* Works also if state->source->arg == NULL */
      execl(state->source->path, state->source->path,
	    state->source->arg, NULL);
      
607
608
      werror("spawn_source_process: execl '%z' failed %e\n",
	     state->source->path, errno);
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
      
      _exit(EXIT_FAILURE);
    }
}

/* Figure out the uid and gid we should run the randomness polling
 * sub-processes as. Set to zero if we shouldn't try changing our
 * identity. */
static void
nobody_ids(uid_t *uid, gid_t *gid)
{
  if (getuid())
    /* We're not running as root, so don't try changing uid. */
    *uid = *gid = 0;
  else
    {
      struct passwd *pw = getpwnam("nobody");
      if (pw)
	{
	  *uid = pw->pw_uid;
	  *gid = pw->pw_gid;
	}
      else
	{
	  werror("No user `nobody' found. will run processes as -1:-1\n");
	  *uid = (uid_t) -1;
	  *gid = (gid_t) -1;
	}
    }
}

/* Spawn this number of processes. */
#define UNIX_RANDOM_POLL_PROCESSES 10

/* Count entropy bits of entropy if we can read at least limit bytes
 * of data. */
struct unix_proc_source
{
  const char *name;
  unsigned limit;
  unsigned entropy;
};

static const struct unix_proc_source
linux_proc_sources[] = {
  /* Say we have four kinds of interrupts, three of which provide 2
   * bits of entropy each. */
  { "/proc/interrupts", 100, 6 },
  /* Five values, count about half a bit for each. */
  { "/proc/loadavg", 10, 3 },
  /* Fairly static information, about 150 bytes per lock.
   * Count 10 bits if we have more than 10 locks. */
  { "/proc/locks", 1500 , 10 },
  /* Count 1 bits each for 5 of the values. */
  { "/proc/meminfo", 300, 5 },
  /* 12 lines, count 1 bits per line. */
  { "/proc/stat", 300, 12 },
  /* About 30 lines, count 1 bit each for five of those. */
  { "/proc/slabinfo", 800, 5 },
  /* Count 5 bits if have more ten 20 lines. Use the same estimate
   * for all these network types. */
  { "/proc/net/tcp", 2000, 10 },
  { "/proc/net/udp", 2000, 10 },
  { "/proc/net/ipx", 2000, 10 },

  /* We should have at least two interfaces. Coutn 5 bits for the
   * information for the primary interface. */
  { "/proc/net/dev", 400, 5 },
  { NULL, 0, 0 }
};
  
static void
get_system(struct yarrow256_ctx *ctx, enum source_type source)
{
  uid_t uid;
  gid_t gid;

  struct unix_random_source_state state[UNIX_RANDOM_POLL_PROCESSES];
  unsigned running = 0;
  unsigned i;
  unsigned index = 0;
  
  int dev_null;

  unsigned count = 0;

  struct {
    pid_t pid;
    struct timeval now;
  } unique;

  werror("Reading system state...\n");

  nobody_ids(&uid, &gid);

  dev_null = open("/dev/null", O_WRONLY);

  if (dev_null < 0)
    {
708
      werror("Failed to open /dev/null %e\n", errno);
709
710
711
712
713
714
715
      return;
    }

  /* Make sure two runs don't get exactly the same data */
  unique.pid = getpid();
  if (gettimeofday(&unique.now, NULL) < 0)
    {
716
      werror("getimeofday failed %e\n", errno);
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
      return;
    }

  yarrow256_update(ctx, source,
		   0,
		   sizeof(unique), (uint8_t *) &unique);
  
  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, dev_null,
				uid, gid))
	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);

      trace("get_system: calling poll, nfds = %i\n", nfds);
      if (poll(fds, nfds, -1) < 0)
	{
753
	  werror("get_system: poll failed %e\n", errno);
754
755
756
757
758
759
760
761
762
763
764
765
766
	  break;
	}

      trace("get_system: returned from poll.\n");

      for (i = 0; i<nfds; i++)
	{
	  trace("get_system: 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
Niels Möller's avatar
Niels Möller committed
767
#endif
768
769
770
	  if (fds[i].revents & (MY_POLLIN | POLLHUP))
	    {
#define BUFSIZE 1024
771
	      uint8_t buffer[BUFSIZE];
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
	      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);

	      trace("get_system: reading from source %z\n",
		    state[j].source->path);
	      
	      res = read(fds[i].fd, buffer, BUFSIZE);
#undef BUFSIZE
	    
	      if (res < 0)
		{
		  if (errno != EINTR)
		    {
791
792
		      werror("get_system: background_poll read failed %e\n",
			     errno);
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
		      return;
		    }
		}
	      else if (res > 0)
		{
		  /* Estimate entropy */

		  unsigned entropy;
		  unsigned old_length = state[j].length;
		  state[j].length += res;
		  
		  state[j].remainder += state[j].source->quality * res;

		  /* Small sources are credited 1K of input as soon is
		   * we get the "small" number of input butes. */
		  if ( (old_length < state[j].source->small) &&
		       (state[j].length >= state[j].source->small) )
		    state[j].remainder += state[j].source->quality * 1024;

		  entropy = state[j].remainder / UNIX_RANDOM_SOURCE_SCALE;
		  if (entropy)
		    state[j].remainder %= UNIX_RANDOM_SOURCE_SCALE;

		  state[j].entropy += entropy;
		  
		  yarrow256_update(ctx, source,
				   entropy,
				   res, buffer);
		}
	      else
		{ /* EOF */
		  int status;
		
		  close(fds[i].fd);

		  state[j].fd = -1;

		  verbose("Read %i bytes from %z %z, entropy estimate: %i bits\n",
			  state[j].length,
			  state[j].source->path,
			  state[j].source->arg ? state[j].source->arg : "",
			  state[j].entropy);
		  count += state[j].entropy;
		  
		  if (waitpid(state[j].pid, &status, 0) < 0)
		    {
839
		      werror("waitpid failed %e\n", errno);
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
		      return;
		    }
		  if (WIFEXITED(status))
		    {
		      if (WEXITSTATUS(status))
			verbose("Command %z %z failed.\n",
				state[j].source->path,
				state[j].source->arg ? state[j].source->arg : "");
		    }
		  else if (WIFSIGNALED(status))
		    {
		      werror("Command %z %z died from signal %i (%z).\n",
			     state[j].source->path,
			     state[j].source->arg ? state[j].source->arg : "",
			     WTERMSIG(status),
			     STRSIGNAL(WTERMSIG(status)));
		    }

		  if (!spawn_source_process(&index, state + j, dev_null,
					    uid, gid))
		    running--;
		}
	    }
	}
    }
  
  for (i = 0; linux_proc_sources[i].name; i++)
    {
      int fd = open(linux_proc_sources[i].name, O_RDONLY);
      if (fd > 0)
	{
#define BUFSIZE 5000
872
	  uint8_t buffer[BUFSIZE];
873
874
875
876
877
#undef BUFSIZE
	  int res;
	  
	  do
	    res = read(fd, buffer, sizeof(buffer));
878
	  while ( (res < 0) && (errno == EINTR) );
879
880

	  if (res < 0)
881
882
	    werror("Reading %z failed %e\n",
		   linux_proc_sources[i].name, errno);
883
884
885
886
	  else
	    {
	      unsigned entropy = 0;
	      if (res > linux_proc_sources[i].limit)
Niels Möller's avatar
Niels Möller committed
887
		entropy = linux_proc_sources[i].entropy;
888
889
890
891
892
893
894
895
896
897
898
899

	      verbose("Read %i bytes from %z, entropy estimate: %i bits\n",
		      res, linux_proc_sources[i].name, entropy);
	      
	      yarrow256_update(ctx, source, entropy, res, buffer);

	      count += entropy;
	    }
	  close(fd);
	}
    }
  werror("Got %i bits of entropy from system state.\n", count);
Niels Möller's avatar
Niels Möller committed
900
901
}

902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
static long
time_usec_diff(const struct timeval *first,
	       const struct timeval *second)
{
  return (second->tv_sec - first->tv_sec) * 1000000
    + (second->tv_usec - first->tv_usec);
}

static unsigned
get_time_accuracy(void)
{
  struct timeval start;
  struct timeval next;
  long diff;
  
  if (gettimeofday(&start, NULL))
    {
919
      werror("gettimeofday failed %e\n", errno);
920
921
922
923
924
925
      return 0;
    }

  do
    if (gettimeofday(&next, NULL))
      {
926
	werror("gettimeofday failed %e\n", errno);
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
	return 0;
      }
  
  while ( (next.tv_sec == start.tv_sec)
	  && (next.tv_usec == start.tv_usec));
  
  diff = time_usec_diff(&start, &next);

  if (diff <= 0)
    return 0;

  /* If accuract is worse than 0.1 second, it's no use. */
  if (diff > 100000)
    {
      werror("gettimeofday accuracy seems too bad to be useful");
      return 0;
    }
Niels Möller's avatar
Niels Möller committed
944
  
945
946
947
948
  return diff;
}


Niels Möller's avatar
Niels Möller committed
949
950
951
static void
get_interact(struct yarrow256_ctx *ctx, enum source_type source)
{
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
  unsigned accuracy = get_time_accuracy();
  unsigned count;
  unsigned progress;
  unsigned keys;
  
  struct yarrow_key_event_ctx estimator;

  if (!accuracy)
    return;

  verbose("gettimeofday accuracy: %i microseconds.\n",
	  accuracy);

  werror("Please type some random data. You better do this\n");
  werror("when connected directly to a console, typing over\n");
  werror("the network provides worse timing information, and\n");
  werror("more opportunities for eavesdropping.\n");

  werror_progress("----------------------------------------\n");
  
  yarrow_key_event_init(&estimator);

  if (!tcgetattr(STDIN_FILENO, &tty_original_mode))
    {
      struct termios tty_mode = tty_original_mode;

      tty_needs_reset = 1;
Niels Möller's avatar
Niels Möller committed
979
      
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
      tty_mode.c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP|INLCR|IGNCR|ICRNL|IXON);
      tty_mode.c_cflag &= ~(CSIZE|PARENB); tty_mode.c_cflag |= CS8;
      tty_mode.c_lflag &= ~(ECHO|ECHONL|ICANON|IEXTEN);
      tty_mode.c_cc[VMIN] = 1;
      tty_mode.c_cc[VTIME] = 0;
      
      tcsetattr(STDIN_FILENO, TCSADRAIN, &tty_mode);
    }
      
  for (count = 0, progress = 5, keys = 0; count < 200; )
    {
      struct {
	struct timeval now;
	int c;
      } event;

      int entropy;
      unsigned time;

      keys++;
      
      event.c = getchar();
      if (gettimeofday(&event.now, NULL) < 0)
	{
1004
	  werror("gettimeofday failed %e\n", errno);
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
	  return;
	}

      if (event.c < 0)
	{
	  werror_progress("\n");
	  werror("Reading keystrokes failed.\n");
	  return;
	}

      /* Compute time as the number of seconds and microseconds
       * divided by accuracy. Taking the numer of seconds mod 1000
       * ensures that the calculation doesn't overflow. */

      time = ( (event.now.tv_sec % 1000) * 1000000 + event.now.tv_usec)
	/ accuracy;
      
      /* We only look at the microsecond data,  */
      entropy = yarrow_key_event_estimate(&estimator,
					  event.c, time);
Niels Möller's avatar
Niels Möller committed
1025
      
1026
1027
1028
1029
1030
      debug("Got char `%c', time: %i, entropy: %i\n", event.c, time, entropy);
      
      yarrow256_update(ctx, source,
		       entropy,
		       sizeof(event), (uint8_t *) &event);
Niels Möller's avatar
Niels Möller committed
1031
      
1032
      count += entropy;
Niels Möller's avatar
Niels Möller committed
1033
      
1034
1035
1036
1037
1038
1039
      if (count >= progress)
	{
	  werror_progress(".");
	  progress += 5;
	}
    }
Niels Möller's avatar
Niels Möller committed
1040
  
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
  werror_progress("\n");
  werror("Got %i keystrokes, estimating %i bits of entropy.\n",
	 keys, count);
  
  werror("You can stop typing now.\n");
  
  if (tty_needs_reset)
    {
      /* Wait a moment for the user to stop typing */
      sleep(1);
Niels Möller's avatar
Niels Möller committed
1051
      
1052
1053
1054
1055
      /* Reset terminal mode, and disgard buffered input. */  
      tcsetattr(STDIN_FILENO, TCSAFLUSH, &tty_original_mode);
      tty_needs_reset = 0;
    }
Niels Möller's avatar
Niels Möller committed
1056
1057
}

1058
1059
1060
int
main(int argc, char **argv)
{
Niels Möller's avatar
Niels Möller committed
1061
  struct lsh_make_seed_options *options = make_options();
1062
1063
1064
1065
  struct lsh_file_lock_info *lock_info;

  int overwrite = 0;
  
1066
  int fd;
Niels Möller's avatar
Niels Möller committed
1067
1068
1069
1070

  struct yarrow256_ctx yarrow;
  struct yarrow_source sources[NSOURCES];

1071
1072
  argp_parse(&main_argp, argc, argv, 0, NULL, options);

1073
1074
1075
1076
1077
  if (atexit(cleanup) < 0)
    {
      werror("atexit failed!?\n");
      return EXIT_FAILURE;
    }
Niels Möller's avatar
Niels Möller committed
1078
1079
1080
1081
1082

  if (options->directory
      && (mkdir(lsh_get_cstring(options->directory), 0755) < 0)
      && (errno != EEXIST) )
    {
1083
1084
      werror("Creating `%S' failed %e.\n",
	     options->directory, errno);
Niels Möller's avatar
Niels Möller committed
1085
1086
1087
      return EXIT_FAILURE;
    }

1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
  lock_info = make_lsh_file_lock_info(ssh_format("%lS.lock",
						 options->filename));

  /* Try to fail early. */
  if (LSH_FILE_LOCK_P(lock_info))
    {
      werror("File `%S' is locked\n", options->filename);
      return EXIT_FAILURE;
    }

  if (!options->force)
    {
      struct stat sbuf;
      if (stat(lsh_get_cstring(options->filename), &sbuf) == 0)
	{
	  werror("File `%S' already exists.\n", options->filename);
	  return EXIT_FAILURE;
	}
    }
    
Niels Möller's avatar
Niels Möller committed
1108
1109
1110
1111
1112
1113
1114
1115
1116
  yarrow256_init(&yarrow, NSOURCES, sources);

  get_dev_random(&yarrow, SOURCE_DEV_RANDOM);
  get_system(&yarrow, SOURCE_SYSTEM);

  if (!yarrow256_is_seeded(&yarrow))
    {
      /* Get the number of additional sources that need to get above
       * the reseed threshold before a reseed happens. */
Niels Möller's avatar
Niels Möller committed
1117
      if (!options->sloppy && (yarrow256_needed_sources(&yarrow) > 1))
Niels Möller's avatar
Niels Möller committed
1118
1119
        {
          werror("Couldn't get enough randomness from the environment.\n");
1120

Niels Möller's avatar
Niels Möller committed
1121
1122
          return EXIT_FAILURE;
        }
1123
      if (!werror_quiet_p())
Niels Möller's avatar
Niels Möller committed
1124
	get_interact(&yarrow, SOURCE_USER);
Niels Möller's avatar
Niels Möller committed
1125
    }
1126

Niels Möller's avatar
Niels Möller committed
1127
  if (!options->sloppy && !yarrow256_is_seeded(&yarrow))
1128
1129
1130
1131
1132
1133
1134
1135
    {
      werror("Couldn't get enough randomness from the environment.\n");

      return EXIT_FAILURE;
    }

  yarrow256_force_reseed(&yarrow);

Niels Möller's avatar
Niels Möller committed
1136
  lock = LSH_FILE_LOCK(lock_info, 5);
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154

  if (!lock)
    {
      werror("Failed to lock file `%S'\n", options->filename);
      return EXIT_FAILURE;
    }

  /* Create file, readable only be the user. */
  fd = open(lsh_get_cstring(options->filename),
	    O_EXCL | O_CREAT | O_WRONLY,
	    0600);

  if (options->force && (fd < 0) && (errno == EEXIST))
    {
      werror("Overwriting `%S'\n",
	     options->filename);

      overwrite = 1;
1155
1156

      /* FIXME: Use O_TRUNC? */
1157
1158
1159
1160
1161
1162
1163
      fd = open(lsh_get_cstring(options->filename),
		O_WRONLY,
		0600);
    }
  
  if (fd < 0)
    {
1164
1165
      werror("Failed to open file `%S' %e\n",
	     options->filename, errno);
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178

      KILL_RESOURCE(lock);
      return EXIT_FAILURE;
    }

  if (overwrite)
    {
      /* If we're overwriting an existing file, make sure it has
       * reasonable permissions */

      struct stat sbuf;
      if (fstat(fd, &sbuf) < 0)
	{
1179
1180
	  werror("Failed to stat file `%S' %e\n",
		 options->filename, errno);
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201

	  close(fd);
	  KILL_RESOURCE(lock);
	  return EXIT_FAILURE;
	}

      if (sbuf.st_uid != getuid())
	{
	  werror("The file `%S' is owned by somebody else.\n");

	  close(fd);
	  KILL_RESOURCE(lock);
	  return EXIT_FAILURE;
	}

      if (sbuf.st_mode & (S_IRWXG | S_IRWXO))
	{
	  werror("Too permissive permissions on `%S', trying to fix it.\n",
		 options->filename);
	  if (fchmod(fd, sbuf.st_mode & ~(S_IRWXG | S_IRWXO)) < 0)
	    {
1202
	      werror("Failed to change permissions %e\n", errno);
1203
1204
1205
1206
1207
	      close(fd);
	      KILL_RESOURCE(lock);
	      return EXIT_FAILURE;
	    }
	}
1208
1209

      /* FIXME: Use O_TRUNC instead? */
1210
1211
      if (ftruncate(fd, 0) < 0)
	{
1212
1213
	  werror("Failed to truncate file `%S' %e\n",
		 options->filename, errno);
1214
1215
1216
1217
1218
1219
1220

	  close(fd);
	  KILL_RESOURCE(lock);
	  return EXIT_FAILURE;
	}
    }
  
1221
  if (!write_raw(fd, sizeof(yarrow.seed_file), yarrow.seed_file))
1222
    {
1223
      werror("Writing seed file failed: %e\n", errno);
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236

      /* If we're overwriting the file, it's already truncated now,
       * we can't leave it unmodified. So just delete it. */

      unlink(lsh_get_cstring(options->filename));
      close(fd);
      KILL_RESOURCE(lock);
      return EXIT_FAILURE;
    }
  
  KILL_RESOURCE(lock);
  close(fd);
  
1237
1238
  return EXIT_SUCCESS;
}