Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sudo-project
GitHub Repository: sudo-project/sudo
Path: blob/main/lib/util/getentropy.c
1532 views
1
/*
2
* SPDX-License-Identifier: ISC
3
*
4
* Copyright (c) 2014 Theo de Raadt <[email protected]>
5
* Copyright (c) 2014 Bob Beck <[email protected]>
6
*
7
* Permission to use, copy, modify, and distribute this software for any
8
* purpose with or without fee is hereby granted, provided that the above
9
* copyright notice and this permission notice appear in all copies.
10
*
11
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18
*
19
* Emulation of getentropy(2) as documented at:
20
* http://man.openbsd.org/getentropy.2
21
*/
22
23
#include <config.h>
24
25
#ifndef HAVE_GETENTROPY
26
27
#include <sys/param.h>
28
#include <sys/mman.h>
29
#include <sys/resource.h>
30
#include <sys/socket.h>
31
#include <sys/stat.h>
32
#ifdef HAVE_SYS_SYSCTL_H
33
# include <sys/sysctl.h>
34
#endif
35
#ifdef HAVE_SYS_STATVFS_H
36
# include <sys/statvfs.h>
37
#endif
38
#include <sys/stat.h>
39
#include <sys/time.h>
40
#ifdef HAVE_SYS_SYSCALL_H
41
# include <sys/syscall.h>
42
#endif
43
#ifdef HAVE_LINUX_RANDOM_H
44
# include <linux/types.h>
45
# include <linux/random.h>
46
#endif
47
#include <errno.h>
48
#include <fcntl.h>
49
#include <signal.h>
50
#include <stdio.h>
51
#include <stdlib.h>
52
#include <string.h>
53
#include <termios.h>
54
#include <time.h>
55
#include <unistd.h>
56
#if defined(HAVE_STDINT_H)
57
# include <stdint.h>
58
#elif defined(HAVE_INTTYPES_H)
59
# include <inttypes.h>
60
#endif
61
#ifdef HAVE_GETAUXVAL
62
# include <sys/auxv.h>
63
#endif
64
#ifdef HAVE_DL_ITERATE_PHDR
65
# include <link.h>
66
#endif
67
#ifdef HAVE_OPENSSL
68
# if defined(HAVE_WOLFSSL)
69
# include <wolfssl/options.h>
70
# endif
71
# include <openssl/rand.h>
72
#endif
73
74
#include <sudo_compat.h>
75
#include <sudo_digest.h>
76
#include <sudo_rand.h>
77
78
#if !defined(MAP_ANON) && defined(MAP_ANONYMOUS)
79
# define MAP_ANON MAP_ANONYMOUS
80
#endif
81
82
#ifndef MAP_FAILED
83
# define MAP_FAILED ((void *) -1)
84
#endif
85
86
#define REPEAT 5
87
#define min(a, b) (((a) < (b)) ? (a) : (b))
88
89
#define HX(a, b) \
90
do { \
91
if ((a)) \
92
HD(errno); \
93
else \
94
HD(b); \
95
} while (0)
96
97
#define HR(x, l) (sudo_digest_update(ctx, (char *)(x), (l)))
98
#define HD(x) (sudo_digest_update(ctx, (char *)&(x), sizeof (x)))
99
#define HF(x) (sudo_digest_update(ctx, (char *)&(x), sizeof (void*)))
100
101
int sudo_getentropy(void *buf, size_t len);
102
103
static int getentropy_getrandom(void *buf, size_t len);
104
static int getentropy_sysctl(void *buf, size_t len);
105
static int getentropy_urandom(void *buf, size_t len, const char *path,
106
int devfscheck);
107
static int getentropy_fallback(void *buf, size_t len);
108
static int gotdata(char *buf, size_t len);
109
#ifdef HAVE_DL_ITERATE_PHDR
110
static int getentropy_phdr(struct dl_phdr_info *info, size_t size, void *data);
111
#endif
112
113
static void *
114
mmap_anon(void *addr, size_t len, int prot, int flags, off_t offset)
115
{
116
#ifdef MAP_ANON
117
return mmap(addr, len, prot, flags | MAP_ANON, -1, offset);
118
#else
119
int fd;
120
121
if ((fd = open("/dev/zero", O_RDWR)) == -1)
122
return MAP_FAILED;
123
addr = mmap(addr, len, prot, flags, fd, offset);
124
close(fd);
125
return addr;
126
#endif
127
}
128
129
int
130
sudo_getentropy(void *buf, size_t len)
131
{
132
int ret = -1;
133
134
if (len > 256) {
135
errno = EIO;
136
return (-1);
137
}
138
139
ret = getentropy_getrandom(buf, len);
140
if (ret != -1)
141
return (ret);
142
143
#ifdef HAVE_OPENSSL
144
if (RAND_bytes(buf, (int)len) == 1)
145
return (0);
146
#endif
147
148
ret = getentropy_sysctl(buf, len);
149
if (ret != -1)
150
return (ret);
151
152
/*
153
* Try to get entropy with /dev/urandom
154
*/
155
ret = getentropy_urandom(buf, len, "/dev/urandom", 0);
156
if (ret != -1)
157
return (ret);
158
159
/*
160
* Entropy collection via /dev/urandom has failed.
161
*
162
* No other API exists for collecting entropy, and we have no
163
* failsafe way to get it that is not sensitive to resource exhaustion.
164
*
165
* We have very few options:
166
* - Even syslog_r is unsafe to call at this low level, so
167
* there is no way to alert the user or program.
168
* - Cannot call abort() because some systems have unsafe
169
* corefiles.
170
* - Could raise(SIGKILL) resulting in silent program termination.
171
* - Return EIO, to hint that arc4random's stir function
172
* should raise(SIGKILL)
173
* - Do the best under the circumstances....
174
*
175
* This code path exists to bring light to the issue that the OS
176
* does not provide a failsafe API for entropy collection.
177
*
178
* We hope this demonstrates that the OS should consider
179
* providing a new failsafe API which works in a chroot or
180
* when file descriptors are exhausted.
181
*/
182
#undef FAIL_INSTEAD_OF_TRYING_FALLBACK
183
#ifdef FAIL_INSTEAD_OF_TRYING_FALLBACK
184
raise(SIGKILL);
185
#endif
186
ret = getentropy_fallback(buf, len);
187
if (ret != -1)
188
return (ret);
189
190
errno = EIO;
191
return (ret);
192
}
193
194
/*
195
* Basic validity checking; wish we could do better.
196
*/
197
static int
198
gotdata(char *buf, size_t len)
199
{
200
char any_set = 0;
201
size_t i;
202
203
for (i = 0; i < len; ++i)
204
any_set |= buf[i];
205
if (any_set == 0)
206
return (-1);
207
return (0);
208
}
209
210
static int
211
getentropy_urandom(void *buf, size_t len, const char *path, int devfscheck)
212
{
213
struct stat st;
214
size_t i;
215
int fd, flags;
216
int save_errno = errno;
217
218
start:
219
220
/* We do not use O_NOFOLLOW since /dev/urandom is a link on Solaris. */
221
flags = O_RDONLY;
222
#ifdef O_CLOEXEC
223
flags |= O_CLOEXEC;
224
#endif
225
fd = open(path, flags, 0);
226
if (fd == -1) {
227
if (errno == EINTR)
228
goto start;
229
goto nodevrandom;
230
}
231
#ifndef O_CLOEXEC
232
fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) | FD_CLOEXEC);
233
#endif
234
235
/* Lightly verify that the device node looks OK */
236
if (fstat(fd, &st) == -1 || !S_ISCHR(st.st_mode)) {
237
close(fd);
238
goto nodevrandom;
239
}
240
for (i = 0; i < len; ) {
241
size_t wanted = len - i;
242
size_t ret = (size_t)read(fd, (char *)buf + i, wanted);
243
244
if (ret == (size_t)-1) {
245
if (errno == EAGAIN || errno == EINTR)
246
continue;
247
close(fd);
248
goto nodevrandom;
249
}
250
i += ret;
251
}
252
close(fd);
253
if (gotdata(buf, len) == 0) {
254
errno = save_errno;
255
return (0); /* satisfied */
256
}
257
nodevrandom:
258
errno = EIO;
259
return (-1);
260
}
261
262
#if defined(HAVE_SYSCTL) && defined(KERN_ARND)
263
static int
264
getentropy_sysctl(void *buf, size_t len)
265
{
266
int save_errno = errno;
267
int mib[2];
268
size_t i;
269
270
mib[0] = CTL_KERN;
271
mib[1] = KERN_ARND;
272
273
for (i = 0; i < len; ) {
274
size_t chunk = len - i;
275
276
if (sysctl(mib, 2, (char *)buf + i, &chunk, NULL, 0) == -1)
277
goto sysctlfailed;
278
i += chunk;
279
}
280
if (gotdata(buf, len) == 0) {
281
errno = save_errno;
282
return (0); /* satisfied */
283
}
284
sysctlfailed:
285
errno = EIO;
286
return (-1);
287
}
288
#elif defined(SYS__sysctl) && defined(RANDOM_UUID)
289
static int
290
getentropy_sysctl(void *buf, size_t len)
291
{
292
static int mib[3];
293
size_t i;
294
int save_errno = errno;
295
296
mib[0] = CTL_KERN;
297
mib[1] = KERN_RANDOM;
298
mib[2] = RANDOM_UUID;
299
300
for (i = 0; i < len; ) {
301
size_t chunk = min(len - i, 16);
302
303
/* SYS__sysctl because some systems already removed sysctl() */
304
struct __sysctl_args args = {
305
.name = mib,
306
.nlen = 3,
307
.oldval = (char *)buf + i,
308
.oldlenp = &chunk,
309
};
310
if (syscall(SYS__sysctl, &args) != 0)
311
goto sysctlfailed;
312
i += chunk;
313
}
314
if (gotdata(buf, len) == 0) {
315
errno = save_errno;
316
return (0); /* satisfied */
317
}
318
sysctlfailed:
319
errno = EIO;
320
return (-1);
321
}
322
#else
323
static int
324
getentropy_sysctl(void *buf, size_t len)
325
{
326
errno = ENOTSUP;
327
return (-1);
328
}
329
#endif
330
331
#if defined(SYS_getrandom) && defined(GRND_NONBLOCK)
332
static int
333
getentropy_getrandom(void *buf, size_t len)
334
{
335
int pre_errno = errno;
336
long ret;
337
338
/*
339
* Try descriptor-less getrandom(), in non-blocking mode.
340
*
341
* The design of Linux getrandom is broken. It has an
342
* uninitialized phase coupled with blocking behaviour, which
343
* is unacceptable from within a library at boot time without
344
* possible recovery. See http://bugs.python.org/issue26839#msg267745
345
*/
346
do {
347
ret = syscall(SYS_getrandom, buf, len, GRND_NONBLOCK);
348
} while (ret == -1 && errno == EINTR);
349
350
if (ret < 0 || (size_t)ret != len)
351
return (-1);
352
errno = pre_errno;
353
return (0);
354
}
355
#else
356
static int
357
getentropy_getrandom(void *buf, size_t len)
358
{
359
errno = ENOTSUP;
360
return (-1);
361
}
362
#endif
363
364
#ifdef HAVE_CLOCK_GETTIME
365
static const int cl[] = {
366
CLOCK_REALTIME,
367
#ifdef CLOCK_MONOTONIC
368
CLOCK_MONOTONIC,
369
#endif
370
#ifdef CLOCK_MONOTONIC_RAW
371
CLOCK_MONOTONIC_RAW,
372
#endif
373
#ifdef CLOCK_TAI
374
CLOCK_TAI,
375
#endif
376
#ifdef CLOCK_VIRTUAL
377
CLOCK_VIRTUAL,
378
#endif
379
#ifdef CLOCK_UPTIME
380
CLOCK_UPTIME,
381
#endif
382
#ifdef CLOCK_PROCESS_CPUTIME_ID
383
CLOCK_PROCESS_CPUTIME_ID,
384
#endif
385
#ifdef CLOCK_THREAD_CPUTIME_ID
386
CLOCK_THREAD_CPUTIME_ID,
387
#endif
388
};
389
#endif /* HAVE_CLOCK_GETTIME */
390
391
#ifdef HAVE_DL_ITERATE_PHDR
392
static int
393
getentropy_phdr(struct dl_phdr_info *info, size_t size, void *data)
394
{
395
struct sudo_digest *ctx = data;
396
397
sudo_digest_update(ctx, &info->dlpi_addr, sizeof (info->dlpi_addr));
398
return (0);
399
}
400
#endif
401
402
static int
403
getentropy_fallback(void *buf, size_t len)
404
{
405
unsigned char *results = NULL;
406
int save_errno = errno, e, faster = 0;
407
int ret = -1;
408
static size_t cnt;
409
unsigned int repeat;
410
size_t pgs;
411
struct timespec ts;
412
struct timeval tv;
413
struct rusage ru;
414
sigset_t set;
415
struct stat st;
416
struct sudo_digest *ctx;
417
static pid_t lastpid;
418
pid_t pid;
419
size_t i, ii, m, digest_len;
420
char *p;
421
422
if (len == 0)
423
return 0;
424
pgs = (size_t)sysconf(_SC_PAGESIZE);
425
if (pgs == (size_t)-1)
426
return -1;
427
if ((ctx = sudo_digest_alloc(SUDO_DIGEST_SHA512)) == NULL)
428
return -1;
429
digest_len = sudo_digest_getlen(SUDO_DIGEST_SHA512);
430
if (digest_len == 0 || (results = malloc(digest_len)) == NULL)
431
goto done;
432
433
pid = getpid();
434
if (lastpid == pid) {
435
faster = 1;
436
repeat = 2;
437
} else {
438
faster = 0;
439
lastpid = pid;
440
repeat = REPEAT;
441
}
442
i = 0;
443
do {
444
unsigned int j;
445
for (j = 0; j < repeat; j++) {
446
HX((e = gettimeofday(&tv, NULL)) == -1, tv);
447
if (e != -1) {
448
cnt += (size_t)tv.tv_sec;
449
cnt += (size_t)tv.tv_usec;
450
}
451
#ifdef HAVE_DL_ITERATE_PHDR
452
dl_iterate_phdr(getentropy_phdr, ctx);
453
#endif
454
455
#ifdef HAVE_CLOCK_GETTIME
456
for (ii = 0; ii < sizeof(cl)/sizeof(cl[0]); ii++)
457
HX(clock_gettime(cl[ii], &ts) == -1, ts);
458
#endif /* HAVE_CLOCK_GETTIME */
459
460
HX((pid = getpid()) == -1, pid);
461
HX((pid = getsid(pid)) == -1, pid);
462
HX((pid = getppid()) == -1, pid);
463
HX((pid = getpgrp()) == -1, pid);
464
HX((e = getpriority(0, 0)) == -1, e);
465
466
if (!faster) {
467
ts.tv_sec = 0;
468
ts.tv_nsec = 1;
469
(void) nanosleep(&ts, NULL);
470
}
471
472
HX(sigpending(&set) == -1, set);
473
HX(sigprocmask(SIG_BLOCK, NULL, &set) == -1, set);
474
475
HF(sudo_getentropy); /* an addr in this library */
476
HF(printf); /* an addr in libc */
477
p = (char *)&p;
478
HD(p); /* an addr on stack */
479
p = (char *)&errno;
480
HD(p); /* the addr of errno */
481
482
if (i == 0) {
483
#ifdef HAVE_SYS_STATVFS_H
484
struct statvfs stvfs;
485
#endif
486
struct termios tios;
487
off_t off;
488
489
/*
490
* Prime-sized mappings encourage fragmentation;
491
* thus exposing some address entropy.
492
*/
493
struct mm {
494
size_t npg;
495
void *p;
496
} mm[] = {
497
{ 17, MAP_FAILED }, { 3, MAP_FAILED },
498
{ 11, MAP_FAILED }, { 2, MAP_FAILED },
499
{ 5, MAP_FAILED }, { 3, MAP_FAILED },
500
{ 7, MAP_FAILED }, { 1, MAP_FAILED },
501
{ 57, MAP_FAILED }, { 3, MAP_FAILED },
502
{ 131, MAP_FAILED }, { 1, MAP_FAILED },
503
};
504
505
for (m = 0; m < sizeof mm/sizeof(mm[0]); m++) {
506
HX(mm[m].p = mmap_anon(NULL,
507
mm[m].npg * pgs,
508
PROT_READ|PROT_WRITE,
509
MAP_PRIVATE,
510
(off_t)0), mm[m].p);
511
if (mm[m].p != MAP_FAILED) {
512
size_t mo;
513
514
/* Touch some memory... */
515
p = mm[m].p;
516
mo = cnt %
517
(mm[m].npg * pgs - 1);
518
p[mo] = 1;
519
cnt += (size_t)mm[m].p / pgs;
520
}
521
522
#ifdef HAVE_CLOCK_GETTIME
523
/* Check cnts and times... */
524
for (ii = 0; ii < sizeof(cl)/sizeof(cl[0]);
525
ii++) {
526
HX((e = clock_gettime(cl[ii],
527
&ts)) == -1, ts);
528
if (e != -1)
529
cnt += (size_t)ts.tv_nsec;
530
}
531
#endif /* HAVE_CLOCK_GETTIME */
532
533
HX((e = getrusage(RUSAGE_SELF,
534
&ru)) == -1, ru);
535
if (e != -1) {
536
cnt += (size_t)ru.ru_utime.tv_sec;
537
cnt += (size_t)ru.ru_utime.tv_usec;
538
}
539
}
540
541
for (m = 0; m < sizeof mm/sizeof(mm[0]); m++) {
542
if (mm[m].p != MAP_FAILED)
543
munmap(mm[m].p, mm[m].npg * pgs);
544
mm[m].p = MAP_FAILED;
545
}
546
547
HX(stat(".", &st) == -1, st);
548
HX(stat("/", &st) == -1, st);
549
550
#ifdef HAVE_SYS_STATVFS_H
551
HX(statvfs(".", &stvfs) == -1, stvfs);
552
HX(statvfs("/", &stvfs) == -1, stvfs);
553
#endif
554
HX((e = fstat(0, &st)) == -1, st);
555
if (e == -1) {
556
if (S_ISREG(st.st_mode) ||
557
S_ISFIFO(st.st_mode) ||
558
S_ISSOCK(st.st_mode)) {
559
#ifdef HAVE_SYS_STATVFS_H
560
HX(fstatvfs(0, &stvfs) == -1,
561
stvfs);
562
#endif
563
HX((off = lseek(0, (off_t)0,
564
SEEK_CUR)) < 0, off);
565
}
566
if (S_ISCHR(st.st_mode)) {
567
HX(tcgetattr(0, &tios) == -1,
568
tios);
569
#if 0
570
} else if (S_ISSOCK(st.st_mode)) {
571
struct sockaddr_storage ss;
572
socklen_t ssl;
573
memset(&ss, 0, sizeof ss);
574
ssl = sizeof(ss);
575
HX(getpeername(0,
576
(void *)&ss, &ssl) == -1,
577
ss);
578
#endif
579
}
580
}
581
582
HX((e = getrusage(RUSAGE_CHILDREN,
583
&ru)) == -1, ru);
584
if (e != -1) {
585
cnt += (size_t)ru.ru_utime.tv_sec;
586
cnt += (size_t)ru.ru_utime.tv_usec;
587
}
588
} else {
589
/* Subsequent hashes absorb previous result */
590
HR(results, digest_len);
591
}
592
593
HX((e = gettimeofday(&tv, NULL)) == -1, tv);
594
if (e != -1) {
595
cnt += (size_t)tv.tv_sec;
596
cnt += (size_t)tv.tv_usec;
597
}
598
599
HD(cnt);
600
}
601
602
#ifdef HAVE_GETAUXVAL
603
#ifdef AT_RANDOM
604
/* Not as random as you think but we take what we are given */
605
p = (char *) getauxval(AT_RANDOM);
606
if (p)
607
HR(p, 16);
608
#endif
609
#ifdef AT_SYSINFO_EHDR
610
p = (char *) getauxval(AT_SYSINFO_EHDR);
611
if (p)
612
HR(p, pgs);
613
#endif
614
#ifdef AT_BASE
615
p = (char *) getauxval(AT_BASE);
616
if (p)
617
HD(p);
618
#endif
619
#endif /* HAVE_GETAUXVAL */
620
621
sudo_digest_final(ctx, results);
622
sudo_digest_reset(ctx);
623
memcpy((char *)buf + i, results, min(digest_len, len - i));
624
i += min(digest_len, len - i);
625
} while (i < len);
626
if (gotdata(buf, len) == 0) {
627
errno = save_errno;
628
ret = 0; /* satisfied */
629
} else {
630
errno = EIO;
631
}
632
done:
633
sudo_digest_free(ctx);
634
if (results != NULL)
635
freezero(results, digest_len);
636
return (ret);
637
}
638
639
#endif /* HAVE_GETENTROPY */
640
641