Coverage Report

Created: 2025-03-06 06:58

/src/gnutls/lib/nettle/sysrng-linux.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * Copyright (C) 2010-2016 Free Software Foundation, Inc.
3
 * Copyright (C) 2015-2016 Red Hat, Inc.
4
 *
5
 * Author: Nikos Mavrogiannopoulos
6
 *
7
 * This file is part of GNUTLS.
8
 *
9
 * The GNUTLS library is free software; you can redistribute it and/or
10
 * modify it under the terms of the GNU Lesser General Public License
11
 * as published by the Free Software Foundation; either version 2.1 of
12
 * the License, or (at your option) any later version.
13
 *
14
 * This library is distributed in the hope that it will be useful, but
15
 * WITHOUT ANY WARRANTY; without even the implied warranty of
16
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17
 * Lesser General Public License for more details.
18
 *
19
 * You should have received a copy of the GNU Lesser General Public License
20
 * along with this program.  If not, see <https://www.gnu.org/licenses/>
21
 *
22
 */
23
24
/* The Linux style system random generator: That is,
25
 * getrandom() -> /dev/urandom, where "->" indicates fallback.
26
 */
27
28
#ifndef RND_NO_INCLUDES
29
#include "gnutls_int.h"
30
#include "errors.h"
31
#include "num.h"
32
#include <errno.h>
33
#include "rnd-common.h"
34
#endif
35
36
#include <sys/types.h>
37
#include <sys/stat.h>
38
#include <unistd.h>
39
40
/* gnulib wants to claim strerror even if it cannot provide it. WTF */
41
#undef strerror
42
43
#include <time.h>
44
#include <sys/types.h>
45
#include <sys/stat.h>
46
#include <sys/time.h>
47
#include <fcntl.h>
48
49
get_entropy_func _rnd_get_system_entropy = NULL;
50
51
#if defined(__linux__)
52
#ifdef HAVE_GETRANDOM
53
#include <sys/random.h>
54
#else
55
#include <sys/syscall.h>
56
#undef getrandom
57
#if defined(SYS_getrandom)
58
#define getrandom(dst, s, flags) \
59
  syscall(SYS_getrandom, (void *)dst, (size_t)s, (unsigned int)flags)
60
#else
61
static ssize_t _getrandom0(void *buf, size_t buflen, unsigned int flags)
62
{
63
  errno = ENOSYS;
64
  return -1;
65
}
66
67
#define getrandom(dst, s, flags) _getrandom0(dst, s, flags)
68
#endif
69
#endif
70
71
static unsigned have_getrandom(void)
72
2
{
73
2
  char c;
74
2
  int ret;
75
2
  ret = getrandom(&c, 1, 1 /*GRND_NONBLOCK */);
76
2
  if (ret == 1 || (ret == -1 && errno == EAGAIN))
77
2
    return 1;
78
0
  return 0;
79
2
}
80
81
/* returns exactly the amount of bytes requested */
82
static int force_getrandom(void *buf, size_t buflen, unsigned int flags)
83
0
{
84
0
  int left = buflen;
85
0
  int ret;
86
0
  uint8_t *p = buf;
87
88
0
  while (left > 0) {
89
0
    ret = getrandom(p, left, flags);
90
0
    if (ret == -1) {
91
0
      if (errno != EINTR)
92
0
        return ret;
93
0
    }
94
95
0
    if (ret > 0) {
96
0
      left -= ret;
97
0
      p += ret;
98
0
    }
99
0
  }
100
101
0
  return buflen;
102
0
}
103
104
static int _rnd_get_system_entropy_getrandom(void *_rnd, size_t size)
105
0
{
106
0
  int ret;
107
0
  ret = force_getrandom(_rnd, size, 0);
108
0
  if (ret == -1) {
109
0
    int e = errno;
110
0
    gnutls_assert();
111
0
    _gnutls_debug_log("Failed to use getrandom: %s\n", strerror(e));
112
0
    return GNUTLS_E_RANDOM_DEVICE_ERROR;
113
0
  }
114
115
0
  return 0;
116
0
}
117
#else /* not linux */
118
#define have_getrandom() 0
119
#endif
120
121
static int _rnd_get_system_entropy_urandom(void *_rnd, size_t size)
122
0
{
123
0
  uint8_t *rnd = _rnd;
124
0
  uint32_t done;
125
0
  int urandom_fd;
126
127
0
  urandom_fd = open("/dev/urandom", O_RDONLY);
128
0
  if (urandom_fd < 0) {
129
0
    _gnutls_debug_log("Cannot open /dev/urandom!\n");
130
0
    return GNUTLS_E_RANDOM_DEVICE_ERROR;
131
0
  }
132
133
0
  for (done = 0; done < size;) {
134
0
    int res;
135
0
    do {
136
0
      res = read(urandom_fd, rnd + done, size - done);
137
0
    } while (res < 0 && errno == EINTR);
138
139
0
    if (res <= 0) {
140
0
      int e = errno;
141
0
      if (res < 0) {
142
0
        _gnutls_debug_log(
143
0
          "Failed to read /dev/urandom: %s\n",
144
0
          strerror(e));
145
0
      } else {
146
0
        _gnutls_debug_log(
147
0
          "Failed to read /dev/urandom: end of file\n");
148
0
      }
149
150
0
      close(urandom_fd);
151
0
      return GNUTLS_E_RANDOM_DEVICE_ERROR;
152
0
    }
153
154
0
    done += res;
155
0
  }
156
157
0
  close(urandom_fd);
158
0
  return 0;
159
0
}
160
161
int _rnd_system_entropy_init(void)
162
2
{
163
2
  int urandom_fd;
164
165
2
#if defined(__linux__)
166
  /* Enable getrandom() usage if available */
167
2
  if (have_getrandom()) {
168
2
    _rnd_get_system_entropy = _rnd_get_system_entropy_getrandom;
169
2
    _gnutls_debug_log("getrandom random generator was selected\n");
170
2
    return 0;
171
2
  } else {
172
0
    _gnutls_debug_log("getrandom is not available\n");
173
0
  }
174
0
#endif
175
176
  /* Fallback: /dev/urandom */
177
178
  /* Check that we can open it */
179
0
  urandom_fd = open("/dev/urandom", O_RDONLY);
180
0
  if (urandom_fd < 0) {
181
0
    _gnutls_debug_log(
182
0
      "Cannot open /dev/urandom during initialization!\n");
183
0
    return gnutls_assert_val(GNUTLS_E_RANDOM_DEVICE_ERROR);
184
0
  }
185
0
  close(urandom_fd);
186
187
0
  _rnd_get_system_entropy = _rnd_get_system_entropy_urandom;
188
0
  _gnutls_debug_log("/dev/urandom random generator was selected\n");
189
190
0
  return 0;
191
0
}
192
193
void _rnd_system_entropy_deinit(void)
194
0
{
195
  /* A no-op now when we open and close /dev/urandom every time */
196
0
  return;
197
0
}