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