Coverage Report

Created: 2025-07-12 06:28

/src/unbound/compat/arc4random.c
Line
Count
Source (jump to first uncovered line)
1
/*      $OpenBSD: arc4random.c,v 1.41 2014/07/12 13:24:54 deraadt Exp $ */
2
3
/*
4
 * Copyright (c) 1996, David Mazieres <dm@uun.org>
5
 * Copyright (c) 2008, Damien Miller <djm@openbsd.org>
6
 * Copyright (c) 2013, Markus Friedl <markus@openbsd.org>
7
 *
8
 * Permission to use, copy, modify, and distribute this software for any
9
 * purpose with or without fee is hereby granted, provided that the above
10
 * copyright notice and this permission notice appear in all copies.
11
 *
12
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
13
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
14
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
15
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
16
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
17
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
18
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19
 */
20
#include "config.h"
21
22
/*
23
 * ChaCha based random number generator for OpenBSD.
24
 */
25
26
#include <fcntl.h>
27
#include <limits.h>
28
#include <signal.h>
29
#ifdef HAVE_STDINT_H
30
#include <stdint.h>
31
#endif
32
#include <stdlib.h>
33
#include <string.h>
34
#include <unistd.h>
35
#include <sys/types.h>
36
#include <sys/param.h>
37
#include <sys/time.h>
38
#ifndef UB_ON_WINDOWS
39
#include <sys/mman.h>
40
#endif
41
42
#define KEYSTREAM_ONLY
43
#include "chacha_private.h"
44
45
0
#define arc4_min(a, b) ((a) < (b) ? (a) : (b))
46
#ifdef __GNUC__
47
#define inline __inline
48
#else       /* !__GNUC__ */
49
#define inline
50
#endif        /* !__GNUC__ */
51
#ifndef MAP_ANON
52
#define MAP_ANON MAP_ANONYMOUS
53
#endif
54
55
0
#define KEYSZ 32
56
0
#define IVSZ  8
57
#define BLOCKSZ 64
58
#define RSBUFSZ (16*BLOCKSZ)
59
60
0
#define REKEY_BASE  (1024*1024) /* NB. should be a power of 2 */
61
62
/* Marked MAP_INHERIT_ZERO, so zero'd out in fork children. */
63
static struct {
64
  size_t    rs_have;  /* valid bytes at end of rs_buf */
65
  size_t    rs_count; /* bytes till reseed */
66
} *rs;
67
68
/* Preserved in fork children. */
69
static struct {
70
  chacha_ctx  rs_chacha;  /* chacha context for random keystream */
71
  u_char    rs_buf[RSBUFSZ];  /* keystream blocks */
72
} *rsx;
73
74
static inline void _rs_rekey(u_char *dat, size_t datlen);
75
76
/*
77
 * Basic sanity checking; wish we could do better.
78
 */
79
static int
80
fallback_gotdata(char *buf, size_t len)
81
0
{
82
0
  char  any_set = 0;
83
0
  size_t  i;
84
85
0
  for (i = 0; i < len; ++i)
86
0
    any_set |= buf[i];
87
0
  if (any_set == 0)
88
0
    return -1;
89
0
  return 0;
90
0
}
91
92
/* fallback for getentropy in case libc returns failure */
93
static int
94
fallback_getentropy_urandom(void *buf, size_t len)
95
0
{
96
0
  size_t i;
97
0
  int fd, flags;
98
0
  int save_errno = errno;
99
100
0
start:
101
102
0
  flags = O_RDONLY;
103
0
#ifdef O_NOFOLLOW
104
0
  flags |= O_NOFOLLOW;
105
0
#endif
106
0
#ifdef O_CLOEXEC
107
0
  flags |= O_CLOEXEC;
108
0
#endif
109
0
  fd = open("/dev/urandom", flags, 0);
110
0
  if (fd == -1) {
111
0
    if (errno == EINTR)
112
0
      goto start;
113
0
    goto nodevrandom;
114
0
  }
115
#ifndef O_CLOEXEC
116
#  ifdef HAVE_FCNTL
117
  fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) | FD_CLOEXEC);
118
#  endif
119
#endif
120
0
  for (i = 0; i < len; ) {
121
0
    size_t wanted = len - i;
122
0
    ssize_t ret = read(fd, (char*)buf + i, wanted);
123
124
0
    if (ret == -1) {
125
0
      if (errno == EAGAIN || errno == EINTR)
126
0
        continue;
127
0
      close(fd);
128
0
      goto nodevrandom;
129
0
    }
130
0
    i += ret;
131
0
  }
132
0
  close(fd);
133
0
  if (fallback_gotdata(buf, len) == 0) {
134
0
    errno = save_errno;
135
0
    return 0;   /* satisfied */
136
0
  }
137
0
nodevrandom:
138
0
  errno = EIO;
139
0
  return -1;
140
0
}
141
142
static inline void
143
_rs_init(u_char *buf, size_t n)
144
0
{
145
0
  assert(buf);
146
0
  if (n < KEYSZ + IVSZ)
147
0
    return;
148
149
0
  if (rs == NULL) {
150
0
#ifndef UB_ON_WINDOWS
151
0
    if ((rs = mmap(NULL, sizeof(*rs), PROT_READ|PROT_WRITE,
152
0
        MAP_ANON|MAP_PRIVATE, -1, 0)) == MAP_FAILED)
153
0
      abort();
154
#ifdef MAP_INHERIT_ZERO
155
    if (minherit(rs, sizeof(*rs), MAP_INHERIT_ZERO) == -1)
156
      abort();
157
#endif
158
#else /* WINDOWS */
159
    rs = malloc(sizeof(*rs));
160
    if(!rs)
161
      abort();
162
#endif
163
0
  }
164
0
  if (rsx == NULL) {
165
0
#ifndef UB_ON_WINDOWS
166
0
    if ((rsx = mmap(NULL, sizeof(*rsx), PROT_READ|PROT_WRITE,
167
0
        MAP_ANON|MAP_PRIVATE, -1, 0)) == MAP_FAILED)
168
0
      abort();
169
#else /* WINDOWS */
170
    rsx = malloc(sizeof(*rsx));
171
    if(!rsx)
172
      abort();
173
#endif
174
0
  }
175
176
0
  chacha_keysetup(&rsx->rs_chacha, buf, KEYSZ * 8, 0);
177
0
  chacha_ivsetup(&rsx->rs_chacha, buf + KEYSZ);
178
0
}
179
180
static void
181
_rs_stir(void)
182
0
{
183
0
  u_char rnd[KEYSZ + IVSZ];
184
0
  uint32_t rekey_fuzz = 0;
185
186
0
  if (getentropy(rnd, sizeof rnd) == -1) {
187
0
    if(errno != ENOSYS ||
188
0
      fallback_getentropy_urandom(rnd, sizeof rnd) == -1) {
189
0
#ifdef SIGKILL
190
0
      raise(SIGKILL);
191
#else
192
      exit(9); /* windows */
193
#endif
194
0
    }
195
0
  }
196
197
0
  if (!rs)
198
0
    _rs_init(rnd, sizeof(rnd));
199
0
  else
200
0
    _rs_rekey(rnd, sizeof(rnd));
201
0
  explicit_bzero(rnd, sizeof(rnd)); /* discard source seed */
202
203
  /* invalidate rs_buf */
204
0
  rs->rs_have = 0;
205
0
  memset(rsx->rs_buf, 0, sizeof(rsx->rs_buf));
206
207
  /* rekey interval should not be predictable */
208
0
  chacha_encrypt_bytes(&rsx->rs_chacha, (uint8_t *)&rekey_fuzz,
209
0
      (uint8_t *)&rekey_fuzz, sizeof(rekey_fuzz));
210
0
  rs->rs_count = REKEY_BASE + (rekey_fuzz % REKEY_BASE);
211
0
}
212
213
static inline void
214
_rs_stir_if_needed(size_t len)
215
0
{
216
0
#ifndef MAP_INHERIT_ZERO
217
0
  static pid_t _rs_pid = 0;
218
0
  pid_t pid = getpid();
219
220
  /* If a system lacks MAP_INHERIT_ZERO, resort to getpid() */
221
0
  if (_rs_pid == 0 || _rs_pid != pid) {
222
0
    _rs_pid = pid;
223
0
    if (rs)
224
0
      rs->rs_count = 0;
225
0
  }
226
0
#endif
227
0
  if (!rs || rs->rs_count <= len)
228
0
    _rs_stir();
229
0
  if (rs->rs_count <= len)
230
0
    rs->rs_count = 0;
231
0
  else
232
0
    rs->rs_count -= len;
233
0
}
234
235
static inline void
236
_rs_rekey(u_char *dat, size_t datlen)
237
0
{
238
#ifndef KEYSTREAM_ONLY
239
  memset(rsx->rs_buf, 0, sizeof(rsx->rs_buf));
240
#endif
241
  /* fill rs_buf with the keystream */
242
0
  chacha_encrypt_bytes(&rsx->rs_chacha, rsx->rs_buf,
243
0
      rsx->rs_buf, sizeof(rsx->rs_buf));
244
  /* mix in optional user provided data */
245
0
  if (dat) {
246
0
    size_t i, m;
247
248
0
    m = arc4_min(datlen, KEYSZ + IVSZ);
249
0
    for (i = 0; i < m; i++)
250
0
      rsx->rs_buf[i] ^= dat[i];
251
0
  }
252
  /* immediately reinit for backtracking resistance */
253
0
  _rs_init(rsx->rs_buf, KEYSZ + IVSZ);
254
0
  memset(rsx->rs_buf, 0, KEYSZ + IVSZ);
255
0
  rs->rs_have = sizeof(rsx->rs_buf) - KEYSZ - IVSZ;
256
0
}
257
258
static inline void
259
_rs_random_buf(void *_buf, size_t n)
260
0
{
261
0
  u_char *buf = (u_char *)_buf;
262
0
  u_char *keystream;
263
0
  size_t m;
264
265
0
  _rs_stir_if_needed(n);
266
0
  while (n > 0) {
267
0
    if (rs->rs_have > 0) {
268
0
      m = arc4_min(n, rs->rs_have);
269
0
      keystream = rsx->rs_buf + sizeof(rsx->rs_buf)
270
0
          - rs->rs_have;
271
0
      memcpy(buf, keystream, m);
272
0
      memset(keystream, 0, m);
273
0
      buf += m;
274
0
      n -= m;
275
0
      rs->rs_have -= m;
276
0
    }
277
0
    if (rs->rs_have == 0)
278
0
      _rs_rekey(NULL, 0);
279
0
  }
280
0
}
281
282
static inline void
283
_rs_random_u32(uint32_t *val)
284
0
{
285
0
  u_char *keystream;
286
0
  _rs_stir_if_needed(sizeof(*val));
287
0
  if (rs->rs_have < sizeof(*val))
288
0
    _rs_rekey(NULL, 0);
289
0
  keystream = rsx->rs_buf + sizeof(rsx->rs_buf) - rs->rs_have;
290
0
  memcpy(val, keystream, sizeof(*val));
291
0
  memset(keystream, 0, sizeof(*val));
292
0
  rs->rs_have -= sizeof(*val);
293
0
}
294
295
uint32_t
296
arc4random(void)
297
0
{
298
0
  uint32_t val;
299
300
0
  _ARC4_LOCK();
301
0
  _rs_random_u32(&val);
302
0
  _ARC4_UNLOCK();
303
0
  return val;
304
0
}
305
306
void
307
arc4random_buf(void *buf, size_t n)
308
0
{
309
0
  _ARC4_LOCK();
310
0
  _rs_random_buf(buf, n);
311
0
  _ARC4_UNLOCK();
312
0
}