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