xref: /illumos-gate/usr/src/lib/libcryptoutil/common/random.c (revision 069e6b7e31ba5dcbc5441b98af272714d9a5455c)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
24  * Copyright 2014, OmniTI Computer Consulting, Inc. All rights reserved.
25  */
26 
27 #include <stdio.h>
28 #include <unistd.h>
29 #include <errno.h>
30 #include <string.h>
31 #include <fcntl.h>
32 #include <locale.h>
33 #include <stdarg.h>
34 #include <cryptoutil.h>
35 #include <pthread.h>
36 
37 #pragma init(pkcs11_random_init)
38 
39 static pthread_mutex_t	random_mutex = PTHREAD_MUTEX_INITIALIZER;
40 static pthread_mutex_t	urandom_mutex = PTHREAD_MUTEX_INITIALIZER;
41 
42 static pthread_mutex_t	random_seed_mutex = PTHREAD_MUTEX_INITIALIZER;
43 static pthread_mutex_t	urandom_seed_mutex = PTHREAD_MUTEX_INITIALIZER;
44 
45 #define	RANDOM_DEVICE		"/dev/random"	/* random device name */
46 #define	URANDOM_DEVICE		"/dev/urandom"	/* urandom device name */
47 
48 static int	random_fd = -1;
49 static int	urandom_fd = -1;
50 
51 static int	random_seed_fd = -1;
52 static int	urandom_seed_fd = -1;
53 
54 
55 /*
56  * Equivalent of open(2) insulated from EINTR.
57  * Also sets close-on-exec.
58  */
59 int
60 open_nointr(const char *path, int oflag, ...)
61 {
62 	int	fd;
63 	mode_t	pmode;
64 	va_list	alist;
65 
66 	va_start(alist, oflag);
67 	pmode = va_arg(alist, mode_t);
68 	va_end(alist);
69 
70 	do {
71 		if ((fd = open(path, oflag, pmode)) >= 0) {
72 			(void) fcntl(fd, F_SETFD, FD_CLOEXEC);
73 			break;
74 		}
75 		/* errno definitely set by failed open() */
76 	} while (errno == EINTR);
77 	return (fd);
78 }
79 
80 /*
81  * Equivalent of read(2) insulated from EINTR.
82  */
83 ssize_t
84 readn_nointr(int fd, void *dbuf, size_t dlen)
85 {
86 	char	*marker = dbuf;
87 	size_t	left = dlen;
88 	ssize_t	nread = 0, err;
89 
90 	for (err = 0; left > 0 && nread != -1; marker += nread, left -= nread) {
91 		if ((nread = read(fd, marker, left)) < 0) {
92 			if (errno == EINTR) {	/* keep trying */
93 				nread = 0;
94 				continue;
95 			}
96 			err = nread;		/* hard error */
97 			break;
98 		} else if (nread == 0) {
99 			break;
100 		}
101 	}
102 	return (err != 0 ? err : dlen - left);
103 }
104 
105 /*
106  * Equivalent of write(2) insulated from EINTR.
107  */
108 ssize_t
109 writen_nointr(int fd, void *dbuf, size_t dlen)
110 {
111 	char	*marker = dbuf;
112 	size_t	left = dlen;
113 	ssize_t	nwrite = 0, err;
114 
115 	for (err = 0; left > 0 && nwrite != -1; marker += nwrite,
116 	    left -= nwrite) {
117 		if ((nwrite = write(fd, marker, left)) < 0) {
118 			if (errno == EINTR) {	/* keep trying */
119 				nwrite = 0;
120 				continue;
121 			}
122 			err = nwrite;		/* hard error */
123 			break;
124 		} else if (nwrite == 0) {
125 			break;
126 		}
127 	}
128 	return (err != 0 ? err : dlen - left);
129 }
130 
131 /*
132  * Opens the random number generator devices if not already open.
133  * Always returns the opened fd of the device, or error.
134  */
135 static int
136 pkcs11_open_common(int *fd, pthread_mutex_t *mtx, const char *dev, int oflag)
137 {
138 	(void) pthread_mutex_lock(mtx);
139 	if (*fd < 0)
140 		*fd = open_nointr(dev, oflag);
141 	(void) pthread_mutex_unlock(mtx);
142 
143 	return (*fd);
144 }
145 
146 static int
147 pkcs11_open_random(void)
148 {
149 	return (pkcs11_open_common(&random_fd, &random_mutex,
150 	    RANDOM_DEVICE, O_RDONLY));
151 }
152 
153 static int
154 pkcs11_open_urandom(void)
155 {
156 	return (pkcs11_open_common(&urandom_fd, &urandom_mutex,
157 	    URANDOM_DEVICE, O_RDONLY));
158 }
159 
160 static int
161 pkcs11_open_random_seed(void)
162 {
163 	return (pkcs11_open_common(&random_seed_fd, &random_seed_mutex,
164 	    RANDOM_DEVICE, O_WRONLY));
165 }
166 
167 static int
168 pkcs11_open_urandom_seed(void)
169 {
170 	return (pkcs11_open_common(&urandom_seed_fd, &urandom_seed_mutex,
171 	    URANDOM_DEVICE, O_WRONLY));
172 }
173 
174 /*
175  * Close the random number generator devices if already open.
176  */
177 static void
178 pkcs11_close_common(int *fd, pthread_mutex_t *mtx)
179 {
180 	(void) pthread_mutex_lock(mtx);
181 	(void) close(*fd);
182 	*fd = -1;
183 	(void) pthread_mutex_unlock(mtx);
184 }
185 
186 static void
187 pkcs11_close_random(void)
188 {
189 	pkcs11_close_common(&random_fd, &random_mutex);
190 }
191 
192 static void
193 pkcs11_close_urandom(void)
194 {
195 	pkcs11_close_common(&urandom_fd, &urandom_mutex);
196 }
197 
198 static void
199 pkcs11_close_random_seed(void)
200 {
201 	pkcs11_close_common(&random_seed_fd, &random_seed_mutex);
202 }
203 
204 static void
205 pkcs11_close_urandom_seed(void)
206 {
207 	pkcs11_close_common(&urandom_seed_fd, &urandom_seed_mutex);
208 }
209 
210 /*
211  * Read from the random number generator devices.
212  */
213 static size_t
214 pkcs11_read_common(int *fd, pthread_mutex_t *mtx, void *dbuf, size_t dlen)
215 {
216 	size_t	n;
217 
218 	(void) pthread_mutex_lock(mtx);
219 	n = readn_nointr(*fd, dbuf, dlen);
220 	(void) pthread_mutex_unlock(mtx);
221 
222 	return (n);
223 }
224 
225 static size_t
226 pkcs11_read_random(void *dbuf, size_t dlen)
227 {
228 	return (pkcs11_read_common(&random_fd, &random_mutex, dbuf, dlen));
229 }
230 
231 static size_t
232 pkcs11_read_urandom(void *dbuf, size_t dlen)
233 {
234 	return (pkcs11_read_common(&urandom_fd, &urandom_mutex, dbuf, dlen));
235 }
236 
237 /*
238  * Write to the random number generator devices.
239  */
240 static size_t
241 pkcs11_write_common(int *fd, pthread_mutex_t *mtx, void *dbuf, size_t dlen)
242 {
243 	size_t	n;
244 
245 	(void) pthread_mutex_lock(mtx);
246 	n = writen_nointr(*fd, dbuf, dlen);
247 	(void) pthread_mutex_unlock(mtx);
248 
249 	return (n);
250 }
251 
252 static size_t
253 pkcs11_write_random_seed(void *dbuf, size_t dlen)
254 {
255 	return (pkcs11_write_common(&random_seed_fd, &random_seed_mutex,
256 	    dbuf, dlen));
257 }
258 
259 static size_t
260 pkcs11_write_urandom_seed(void *dbuf, size_t dlen)
261 {
262 	return (pkcs11_write_common(&urandom_seed_fd, &urandom_seed_mutex,
263 	    dbuf, dlen));
264 }
265 
266 /*
267  * Seed /dev/random with the data in the buffer.
268  */
269 int
270 pkcs11_seed_random(void *sbuf, size_t slen)
271 {
272 	int	rv;
273 
274 	if (sbuf == NULL || slen == 0)
275 		return (0);
276 
277 	/* Seeding error could mean it's not supported (errno = EACCES) */
278 	if (pkcs11_open_random_seed() < 0)
279 		return (-1);
280 
281 	rv = -1;
282 	if (pkcs11_write_random_seed(sbuf, slen) == slen)
283 		rv = 0;
284 
285 	pkcs11_close_random_seed();
286 	return (rv);
287 }
288 
289 /*
290  * Seed /dev/urandom with the data in the buffer.
291  */
292 int
293 pkcs11_seed_urandom(void *sbuf, size_t slen)
294 {
295 	int	rv;
296 
297 	if (sbuf == NULL || slen == 0)
298 		return (0);
299 
300 	/* Seeding error could mean it's not supported (errno = EACCES) */
301 	if (pkcs11_open_urandom_seed() < 0)
302 		return (-1);
303 
304 	rv = -1;
305 	if (pkcs11_write_urandom_seed(sbuf, slen) == slen)
306 		rv = 0;
307 
308 	pkcs11_close_urandom_seed();
309 	return (rv);
310 }
311 
312 /*
313  * Put the requested amount of random data into a preallocated buffer.
314  * Good for token key data, persistent objects.
315  */
316 int
317 pkcs11_get_random(void *dbuf, size_t dlen)
318 {
319 	if (dbuf == NULL || dlen == 0)
320 		return (0);
321 
322 	/* Read random data directly from /dev/random */
323 	if (pkcs11_open_random() < 0)
324 		return (-1);
325 
326 	if (pkcs11_read_random(dbuf, dlen) == dlen)
327 		return (0);
328 	return (-1);
329 }
330 
331 /*
332  * Put the requested amount of random data into a preallocated buffer.
333  * Good for passphrase salts, initialization vectors.
334  */
335 int
336 pkcs11_get_urandom(void *dbuf, size_t dlen)
337 {
338 	if (dbuf == NULL || dlen == 0)
339 		return (0);
340 
341 	/* Read random data directly from /dev/urandom */
342 	if (pkcs11_open_urandom() < 0)
343 		return (-1);
344 
345 	if (pkcs11_read_urandom(dbuf, dlen) == dlen)
346 		return (0);
347 	return (-1);
348 }
349 
350 /*
351  * Same as pkcs11_get_urandom but ensures non zero data.
352  */
353 int
354 pkcs11_get_nzero_urandom(void *dbuf, size_t dlen)
355 {
356 	char	extrarand[32];
357 	size_t	bytesleft = 0;
358 	size_t	i = 0;
359 
360 	/* Start with some random data */
361 	if (pkcs11_get_urandom(dbuf, dlen) < 0)
362 		return (-1);
363 
364 	/* Walk through data replacing any 0 bytes with more random data */
365 	while (i < dlen) {
366 		if (((char *)dbuf)[i] != 0) {
367 			i++;
368 			continue;
369 		}
370 
371 		if (bytesleft == 0) {
372 			bytesleft = sizeof (extrarand);
373 			if (pkcs11_get_urandom(extrarand, bytesleft) < 0)
374 				return (-1);
375 		}
376 		bytesleft--;
377 
378 		((char *)dbuf)[i] = extrarand[bytesleft];
379 	}
380 	return (0);
381 }
382 
383 static void
384 pkcs11_random_prepare(void)
385 {
386 	/*
387 	 * NOTE - None of these are acquired more than one at a time.
388 	 * I can therefore acquire all four without fear of deadlock.
389 	 */
390 	(void) pthread_mutex_lock(&random_mutex);
391 	(void) pthread_mutex_lock(&urandom_mutex);
392 	(void) pthread_mutex_lock(&random_seed_mutex);
393 	(void) pthread_mutex_lock(&urandom_seed_mutex);
394 }
395 
396 static void
397 pkcs11_random_parent_post(void)
398 {
399 	/* Drop the mutexes and get back to work! */
400 	(void) pthread_mutex_unlock(&urandom_seed_mutex);
401 	(void) pthread_mutex_unlock(&random_seed_mutex);
402 	(void) pthread_mutex_unlock(&urandom_mutex);
403 	(void) pthread_mutex_unlock(&random_mutex);
404 }
405 
406 static void
407 pkcs11_random_child_post(void)
408 {
409 	pkcs11_random_parent_post();
410 
411 	/* Also, close the FDs, just in case. */
412 	pkcs11_close_random();
413 	pkcs11_close_urandom();
414 	pkcs11_close_random_seed();
415 	pkcs11_close_urandom_seed();
416 }
417 
418 static void
419 pkcs11_random_init(void)
420 {
421 	(void) pthread_atfork(pkcs11_random_prepare, pkcs11_random_parent_post,
422 	    pkcs11_random_child_post);
423 }
424