Coverage Report

Created: 2024-05-21 06:33

/src/cryptsetup/lib/random.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * cryptsetup kernel RNG access functions
3
 *
4
 * Copyright (C) 2010-2024 Red Hat, Inc. All rights reserved.
5
 *
6
 * This program is free software; you can redistribute it and/or
7
 * modify it under the terms of the GNU General Public License
8
 * as published by the Free Software Foundation; either version 2
9
 * of the License, or (at your option) any later version.
10
 *
11
 * This program is distributed in the hope that it will be useful,
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
 * GNU General Public License for more details.
15
 *
16
 * You should have received a copy of the GNU General Public License
17
 * along with this program; if not, write to the Free Software
18
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19
 */
20
21
#include <stdlib.h>
22
#include <string.h>
23
#include <errno.h>
24
#include <sys/select.h>
25
26
#include "libcryptsetup.h"
27
#include "internal.h"
28
29
static int random_initialised = 0;
30
31
11.0k
#define URANDOM_DEVICE  "/dev/urandom"
32
static int urandom_fd = -1;
33
34
11.0k
#define RANDOM_DEVICE "/dev/random"
35
static int random_fd = -1;
36
37
/* Read random chunk - gathered data usually appears with this granularity */
38
0
#define RANDOM_DEVICE_CHUNK 8
39
40
/* Timeout to print warning if no random data (entropy) */
41
0
#define RANDOM_DEVICE_TIMEOUT 5
42
43
/* URANDOM_DEVICE access */
44
static int _get_urandom(char *buf, size_t len)
45
4.01k
{
46
4.01k
  int r;
47
4.01k
  size_t old_len = len;
48
4.01k
  char *old_buf = buf;
49
50
4.01k
  assert(urandom_fd != -1);
51
52
8.03k
  while (len) {
53
4.01k
    r = read(urandom_fd, buf, len);
54
4.01k
    if (r == -1 && errno != EINTR)
55
0
      return -EINVAL;
56
4.01k
    if (r > 0) {
57
4.01k
      len -= r;
58
4.01k
      buf += r;
59
4.01k
    }
60
4.01k
  }
61
62
4.01k
  assert(len == 0);
63
4.01k
  assert((size_t)(buf - old_buf) == old_len);
64
65
4.01k
  return 0;
66
4.01k
}
67
68
static void _get_random_progress(struct crypt_device *ctx, int warn,
69
         size_t expected_len, size_t read_len)
70
0
{
71
0
  if (warn)
72
0
    log_std(ctx,
73
0
      _("System is out of entropy while generating volume key.\n"
74
0
        "Please move mouse or type some text in another window "
75
0
        "to gather some random events.\n"));
76
77
0
  log_std(ctx, _("Generating key (%d%% done).\n"),
78
0
    (int)((expected_len - read_len) * 100 / expected_len));
79
0
}
80
81
/* RANDOM_DEVICE access */
82
static int _get_random(struct crypt_device *ctx, char *buf, size_t len)
83
0
{
84
0
  int r, warn_once = 1;
85
0
  size_t n, old_len = len;
86
0
  char *old_buf = buf;
87
0
  fd_set fds;
88
0
  struct timeval tv;
89
90
0
  assert(random_fd != -1);
91
92
0
  while (len) {
93
0
    FD_ZERO(&fds);
94
0
    FD_SET(random_fd, &fds);
95
96
0
    tv.tv_sec = RANDOM_DEVICE_TIMEOUT;
97
0
    tv.tv_usec = 0;
98
99
0
    r = select(random_fd + 1, &fds, NULL, NULL, &tv);
100
0
    if(r == -1)
101
0
      return -EINVAL;
102
103
0
    if(!r) {
104
0
      _get_random_progress(ctx, warn_once, old_len, len);
105
0
      warn_once = 0;
106
0
      continue;
107
0
    }
108
109
0
    do {
110
0
      n = RANDOM_DEVICE_CHUNK;
111
0
      if (len < RANDOM_DEVICE_CHUNK)
112
0
        n = len;
113
114
0
      r = read(random_fd, buf, n);
115
116
0
      if (r == -1 && errno == EINTR) {
117
0
        r = 0;
118
0
        continue;
119
0
      }
120
121
      /* bogus read? */
122
0
      if(r > (int)n)
123
0
        return -EINVAL;
124
125
      /* random device is opened with O_NONBLOCK, EAGAIN is expected */
126
0
      if (r == -1 && (errno != EAGAIN && errno != EWOULDBLOCK))
127
0
        return -EINVAL;
128
129
0
      if (r > 0) {
130
0
        len -= r;
131
0
        buf += r;
132
0
      }
133
0
    } while (len && r > 0);
134
0
  }
135
136
0
  assert(len == 0);
137
0
  assert((size_t)(buf - old_buf) == old_len);
138
139
0
  if (!warn_once)
140
0
    _get_random_progress(ctx, 0, old_len, len);
141
142
0
  return 0;
143
0
}
144
/* Initialisation of both RNG file descriptors is mandatory */
145
int crypt_random_init(struct crypt_device *ctx)
146
22.3k
{
147
22.3k
  if (random_initialised)
148
22.3k
    return 0;
149
150
  /* Used for CRYPT_RND_NORMAL */
151
3
  if(urandom_fd == -1)
152
3
    urandom_fd = open(URANDOM_DEVICE, O_RDONLY | O_CLOEXEC);
153
3
  if(urandom_fd == -1)
154
0
    goto err;
155
156
  /* Used for CRYPT_RND_KEY */
157
3
  if(random_fd == -1)
158
3
    random_fd = open(RANDOM_DEVICE, O_RDONLY | O_NONBLOCK | O_CLOEXEC);
159
3
  if(random_fd == -1)
160
0
    goto err;
161
162
3
  if (crypt_fips_mode())
163
0
    log_verbose(ctx, _("Running in FIPS mode."));
164
165
3
  random_initialised = 1;
166
3
  return 0;
167
0
err:
168
0
  crypt_random_exit();
169
0
  log_err(ctx, _("Fatal error during RNG initialisation."));
170
0
  return -ENOSYS;
171
3
}
172
173
/* coverity[ -taint_source : arg-1 ] */
174
int crypt_random_get(struct crypt_device *ctx, char *buf, size_t len, int quality)
175
4.01k
{
176
4.01k
  int status, rng_type;
177
178
4.01k
  switch(quality) {
179
0
  case CRYPT_RND_NORMAL:
180
0
    status = _get_urandom(buf, len);
181
0
    break;
182
4.01k
  case CRYPT_RND_SALT:
183
4.01k
    if (crypt_fips_mode())
184
0
      status = crypt_backend_rng(buf, len, quality, 1);
185
4.01k
    else
186
4.01k
      status = _get_urandom(buf, len);
187
4.01k
    break;
188
0
  case CRYPT_RND_KEY:
189
0
    if (crypt_fips_mode()) {
190
0
      status = crypt_backend_rng(buf, len, quality, 1);
191
0
      break;
192
0
    }
193
0
    rng_type = ctx ? crypt_get_rng_type(ctx) :
194
0
         crypt_random_default_key_rng();
195
0
    switch (rng_type) {
196
0
    case CRYPT_RNG_URANDOM:
197
0
      status = _get_urandom(buf, len);
198
0
      break;
199
0
    case CRYPT_RNG_RANDOM:
200
0
      status = _get_random(ctx, buf, len);
201
0
      break;
202
0
    default:
203
0
      abort();
204
0
    }
205
0
    break;
206
0
  default:
207
0
    log_err(ctx, _("Unknown RNG quality requested."));
208
0
    return -EINVAL;
209
4.01k
  }
210
211
4.01k
  if (status)
212
0
    log_err(ctx, _("Error reading from RNG."));
213
214
4.01k
  return status;
215
4.01k
}
216
217
void crypt_random_exit(void)
218
0
{
219
0
  random_initialised = 0;
220
221
0
  if(random_fd != -1) {
222
0
    (void)close(random_fd);
223
0
    random_fd = -1;
224
0
  }
225
226
0
  if(urandom_fd != -1) {
227
0
    (void)close(urandom_fd);
228
0
    urandom_fd = -1;
229
0
  }
230
0
}
231
232
int crypt_random_default_key_rng(void)
233
11.0k
{
234
  /* coverity[pointless_string_compare] */
235
11.0k
  if (!strcmp(DEFAULT_RNG, RANDOM_DEVICE))
236
0
    return CRYPT_RNG_RANDOM;
237
238
  /* coverity[pointless_string_compare] */
239
11.0k
  if (!strcmp(DEFAULT_RNG, URANDOM_DEVICE))
240
11.0k
    return CRYPT_RNG_URANDOM;
241
242
  /* RNG misconfiguration is fatal */
243
0
  abort();
244
11.0k
}