Coverage Report

Created: 2025-12-14 06:35

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