Specify copyright holders in source files.
[platform/upstream/cryptsetup.git] / lib / random.c
1 /*
2  * cryptsetup kernel RNG access functions
3  *
4  * Copyright (C) 2010-2011, 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  * version 2 as published by the Free Software Foundation.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18  */
19
20 #include <stdlib.h>
21 #include <string.h>
22 #include <fcntl.h>
23 #include <errno.h>
24 #include <assert.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(struct crypt_device *ctx, 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);
153         if(urandom_fd == -1)
154                 goto fail;
155
156         /* Used for CRYPT_RND_KEY */
157         if(random_fd == -1)
158                 random_fd = open(RANDOM_DEVICE, O_RDONLY | O_NONBLOCK);
159         if(random_fd == -1)
160                 goto fail;
161
162         random_initialised = 1;
163         return 0;
164 fail:
165         crypt_random_exit();
166         log_err(ctx, _("Fatal error during RNG initialisation.\n"));
167         return -ENOSYS;
168 }
169
170 int crypt_random_get(struct crypt_device *ctx, char *buf, size_t len, int quality)
171 {
172         int status, rng_type;
173
174         switch(quality) {
175         case CRYPT_RND_NORMAL:
176                 status = _get_urandom(ctx, buf, len);
177                 break;
178         case CRYPT_RND_KEY:
179                 rng_type = ctx ? crypt_get_rng_type(ctx) :
180                                  crypt_random_default_key_rng();
181                 switch (rng_type) {
182                 case CRYPT_RNG_URANDOM:
183                         status = _get_urandom(ctx, buf, len);
184                         break;
185                 case CRYPT_RNG_RANDOM:
186                         status = _get_random(ctx, buf, len);
187                         break;
188                 default:
189                         abort();
190                 }
191                 break;
192         default:
193                 log_err(ctx, _("Unknown RNG quality requested.\n"));
194                 return -EINVAL;
195         }
196
197         if (status)
198                 log_err(ctx, _("Error %d reading from RNG: %s\n"),
199                         errno, strerror(errno));
200
201         return status;
202 }
203
204 void crypt_random_exit()
205 {
206         random_initialised = 0;
207
208         if(random_fd != -1) {
209                 (void)close(random_fd);
210                 random_fd = -1;
211         }
212
213         if(urandom_fd != -1) {
214                 (void)close(urandom_fd);
215                 urandom_fd = -1;
216         }
217 }
218
219 int crypt_random_default_key_rng()
220 {
221         if (!strcmp(DEFAULT_RNG, RANDOM_DEVICE))
222                 return CRYPT_RNG_RANDOM;
223
224         if (!strcmp(DEFAULT_RNG, URANDOM_DEVICE))
225                 return CRYPT_RNG_URANDOM;
226
227         /* RNG misconfiguration is fatal */
228         abort();
229 }