Coverage Report

Created: 2023-03-26 07:33

/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) syscall(SYS_getrandom, (void*)dst, (size_t)s, (unsigned int)flags)
59
#  else
60
static ssize_t _getrandom0(void *buf, size_t buflen, unsigned int flags)
61
{
62
  errno = ENOSYS;
63
  return -1;
64
}
65
66
#   define getrandom(dst,s,flags) _getrandom0(dst,s,flags)
67
#  endif
68
# endif
69
70
static unsigned have_getrandom(void)
71
2
{
72
2
  char c;
73
2
  int ret;
74
2
  ret = getrandom(&c, 1, 1 /*GRND_NONBLOCK */ );
75
2
  if (ret == 1 || (ret == -1 && errno == EAGAIN))
76
2
    return 1;
77
0
  return 0;
78
2
}
79
80
/* returns exactly the amount of bytes requested */
81
static int force_getrandom(void *buf, size_t buflen, unsigned int flags)
82
0
{
83
0
  int left = buflen;
84
0
  int ret;
85
0
  uint8_t *p = buf;
86
87
0
  while (left > 0) {
88
0
    ret = getrandom(p, left, flags);
89
0
    if (ret == -1) {
90
0
      if (errno != EINTR)
91
0
        return ret;
92
0
    }
93
94
0
    if (ret > 0) {
95
0
      left -= ret;
96
0
      p += ret;
97
0
    }
98
0
  }
99
100
0
  return buflen;
101
0
}
102
103
static int _rnd_get_system_entropy_getrandom(void *_rnd, size_t size)
104
0
{
105
0
  int ret;
106
0
  ret = force_getrandom(_rnd, size, 0);
107
0
  if (ret == -1) {
108
0
    int e = errno;
109
0
    gnutls_assert();
110
0
    _gnutls_debug_log("Failed to use getrandom: %s\n", strerror(e));
111
0
    return GNUTLS_E_RANDOM_DEVICE_ERROR;
112
0
  }
113
114
0
  return 0;
115
0
}
116
#else       /* not linux */
117
# define have_getrandom() 0
118
#endif
119
120
static int _rnd_get_system_entropy_urandom(void *_rnd, size_t size)
121
0
{
122
0
  uint8_t *rnd = _rnd;
123
0
  uint32_t done;
124
0
  int urandom_fd;
125
126
0
  urandom_fd = open("/dev/urandom", O_RDONLY);
127
0
  if (urandom_fd < 0) {
128
0
    _gnutls_debug_log("Cannot open /dev/urandom!\n");
129
0
    return GNUTLS_E_RANDOM_DEVICE_ERROR;
130
0
  }
131
132
0
  for (done = 0; done < size;) {
133
0
    int res;
134
0
    do {
135
0
      res = read(urandom_fd, rnd + done, size - done);
136
0
    } while (res < 0 && errno == EINTR);
137
138
0
    if (res <= 0) {
139
0
      int e = errno;
140
0
      if (res < 0) {
141
0
        _gnutls_debug_log
142
0
            ("Failed to read /dev/urandom: %s\n",
143
0
             strerror(e));
144
0
      } else {
145
0
        _gnutls_debug_log
146
0
            ("Failed to read /dev/urandom: end of file\n");
147
0
      }
148
149
0
      close(urandom_fd);
150
0
      return GNUTLS_E_RANDOM_DEVICE_ERROR;
151
0
    }
152
153
0
    done += res;
154
0
  }
155
156
0
  close(urandom_fd);
157
0
  return 0;
158
0
}
159
160
int _rnd_system_entropy_init(void)
161
2
{
162
2
  int urandom_fd;
163
164
2
#if defined(__linux__)
165
  /* Enable getrandom() usage if available */
166
2
  if (have_getrandom()) {
167
2
    _rnd_get_system_entropy = _rnd_get_system_entropy_getrandom;
168
2
    _gnutls_debug_log("getrandom random generator was selected\n");
169
2
    return 0;
170
2
  } else {
171
0
    _gnutls_debug_log("getrandom is not available\n");
172
0
  }
173
0
#endif
174
175
  /* Fallback: /dev/urandom */
176
177
  /* Check that we can open it */
178
0
  urandom_fd = open("/dev/urandom", O_RDONLY);
179
0
  if (urandom_fd < 0) {
180
0
    _gnutls_debug_log
181
0
        ("Cannot open /dev/urandom during initialization!\n");
182
0
    return gnutls_assert_val(GNUTLS_E_RANDOM_DEVICE_ERROR);
183
0
  }
184
0
  close(urandom_fd);
185
186
0
  _rnd_get_system_entropy = _rnd_get_system_entropy_urandom;
187
0
  _gnutls_debug_log("/dev/urandom random generator was selected\n");
188
189
0
  return 0;
190
0
}
191
192
void _rnd_system_entropy_deinit(void)
193
0
{
194
  /* A no-op now when we open and close /dev/urandom every time */
195
0
  return;
196
0
}