xref: /titanic_52/usr/src/lib/libcryptoutil/common/random.c (revision 41cf421d9c6400a15b7c05a8f75a59121e759f6c)
11c9bd843Sdinak /*
21c9bd843Sdinak  * CDDL HEADER START
31c9bd843Sdinak  *
41c9bd843Sdinak  * The contents of this file are subject to the terms of the
51c9bd843Sdinak  * Common Development and Distribution License (the "License").
61c9bd843Sdinak  * You may not use this file except in compliance with the License.
71c9bd843Sdinak  *
81c9bd843Sdinak  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
91c9bd843Sdinak  * or http://www.opensolaris.org/os/licensing.
101c9bd843Sdinak  * See the License for the specific language governing permissions
111c9bd843Sdinak  * and limitations under the License.
121c9bd843Sdinak  *
131c9bd843Sdinak  * When distributing Covered Code, include this CDDL HEADER in each
141c9bd843Sdinak  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
151c9bd843Sdinak  * If applicable, add the following below this CDDL HEADER, with the
161c9bd843Sdinak  * fields enclosed by brackets "[]" replaced with your own identifying
171c9bd843Sdinak  * information: Portions Copyright [yyyy] [name of copyright owner]
181c9bd843Sdinak  *
191c9bd843Sdinak  * CDDL HEADER END
201c9bd843Sdinak  */
21*41cf421dSDina K Nimeh 
221c9bd843Sdinak /*
23*41cf421dSDina K Nimeh  * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
241c9bd843Sdinak  */
251c9bd843Sdinak 
261c9bd843Sdinak #include <stdio.h>
2726ff1ce9Sdinak #include <unistd.h>
2826ff1ce9Sdinak #include <errno.h>
291c9bd843Sdinak #include <string.h>
301c9bd843Sdinak #include <fcntl.h>
311c9bd843Sdinak #include <locale.h>
3219193bb6SDina K Nimeh #include <stdarg.h>
331c9bd843Sdinak #include <cryptoutil.h>
3426ff1ce9Sdinak #include <pthread.h>
3526ff1ce9Sdinak 
367b79d846SDina K Nimeh 
3726ff1ce9Sdinak static pthread_mutex_t	random_mutex = PTHREAD_MUTEX_INITIALIZER;
3826ff1ce9Sdinak static pthread_mutex_t	urandom_mutex = PTHREAD_MUTEX_INITIALIZER;
3926ff1ce9Sdinak 
407b79d846SDina K Nimeh static pthread_mutex_t	random_seed_mutex = PTHREAD_MUTEX_INITIALIZER;
417b79d846SDina K Nimeh static pthread_mutex_t	urandom_seed_mutex = PTHREAD_MUTEX_INITIALIZER;
4226ff1ce9Sdinak 
4326ff1ce9Sdinak #define	RANDOM_DEVICE		"/dev/random"	/* random device name */
4426ff1ce9Sdinak #define	URANDOM_DEVICE		"/dev/urandom"	/* urandom device name */
4526ff1ce9Sdinak 
4626ff1ce9Sdinak static int	random_fd = -1;
4726ff1ce9Sdinak static int	urandom_fd = -1;
4826ff1ce9Sdinak 
497b79d846SDina K Nimeh static int	random_seed_fd = -1;
507b79d846SDina K Nimeh static int	urandom_seed_fd = -1;
517b79d846SDina K Nimeh 
527b79d846SDina K Nimeh 
5326ff1ce9Sdinak /*
5426ff1ce9Sdinak  * Equivalent of open(2) insulated from EINTR.
5526ff1ce9Sdinak  * Also sets close-on-exec.
5626ff1ce9Sdinak  */
5719193bb6SDina K Nimeh int
5819193bb6SDina K Nimeh open_nointr(const char *path, int oflag, ...)
5926ff1ce9Sdinak {
6026ff1ce9Sdinak 	int	fd;
6119193bb6SDina K Nimeh 	mode_t	pmode;
6219193bb6SDina K Nimeh 	va_list	alist;
6319193bb6SDina K Nimeh 
6419193bb6SDina K Nimeh 	va_start(alist, oflag);
6519193bb6SDina K Nimeh 	pmode = va_arg(alist, mode_t);
6619193bb6SDina K Nimeh 	va_end(alist);
6726ff1ce9Sdinak 
6826ff1ce9Sdinak 	do {
6919193bb6SDina K Nimeh 		if ((fd = open(path, oflag, pmode)) >= 0) {
7026ff1ce9Sdinak 			(void) fcntl(fd, F_SETFD, FD_CLOEXEC);
7126ff1ce9Sdinak 			break;
7226ff1ce9Sdinak 		}
7326ff1ce9Sdinak 		/* errno definitely set by failed open() */
7426ff1ce9Sdinak 	} while (errno == EINTR);
7526ff1ce9Sdinak 	return (fd);
7626ff1ce9Sdinak }
7726ff1ce9Sdinak 
7826ff1ce9Sdinak /*
7926ff1ce9Sdinak  * Equivalent of read(2) insulated from EINTR.
8026ff1ce9Sdinak  */
8119193bb6SDina K Nimeh ssize_t
8219193bb6SDina K Nimeh readn_nointr(int fd, void *dbuf, size_t dlen)
8326ff1ce9Sdinak {
8426ff1ce9Sdinak 	char	*marker = dbuf;
8526ff1ce9Sdinak 	size_t	left = dlen;
8626ff1ce9Sdinak 	ssize_t	nread = 0, err;
8726ff1ce9Sdinak 
8826ff1ce9Sdinak 	for (err = 0; left > 0 && nread != -1; marker += nread, left -= nread) {
8926ff1ce9Sdinak 		if ((nread = read(fd, marker, left)) < 0) {
9026ff1ce9Sdinak 			if (errno == EINTR) {	/* keep trying */
9126ff1ce9Sdinak 				nread = 0;
9226ff1ce9Sdinak 				continue;
9326ff1ce9Sdinak 			}
9426ff1ce9Sdinak 			err = nread;		/* hard error */
9526ff1ce9Sdinak 			break;
9619193bb6SDina K Nimeh 		} else if (nread == 0) {
9719193bb6SDina K Nimeh 			break;
9819193bb6SDina K Nimeh 		}
9919193bb6SDina K Nimeh 	}
10019193bb6SDina K Nimeh 	return (err != 0 ? err : dlen - left);
10119193bb6SDina K Nimeh }
10219193bb6SDina K Nimeh 
10319193bb6SDina K Nimeh /*
10419193bb6SDina K Nimeh  * Equivalent of write(2) insulated from EINTR.
10519193bb6SDina K Nimeh  */
10619193bb6SDina K Nimeh ssize_t
10719193bb6SDina K Nimeh writen_nointr(int fd, void *dbuf, size_t dlen)
10819193bb6SDina K Nimeh {
10919193bb6SDina K Nimeh 	char	*marker = dbuf;
11019193bb6SDina K Nimeh 	size_t	left = dlen;
11119193bb6SDina K Nimeh 	ssize_t	nwrite = 0, err;
11219193bb6SDina K Nimeh 
11319193bb6SDina K Nimeh 	for (err = 0; left > 0 && nwrite != -1; marker += nwrite,
11419193bb6SDina K Nimeh 	    left -= nwrite) {
11519193bb6SDina K Nimeh 		if ((nwrite = write(fd, marker, left)) < 0) {
11619193bb6SDina K Nimeh 			if (errno == EINTR) {	/* keep trying */
11719193bb6SDina K Nimeh 				nwrite = 0;
11819193bb6SDina K Nimeh 				continue;
11919193bb6SDina K Nimeh 			}
12019193bb6SDina K Nimeh 			err = nwrite;		/* hard error */
12119193bb6SDina K Nimeh 			break;
12219193bb6SDina K Nimeh 		} else if (nwrite == 0) {
12319193bb6SDina K Nimeh 			break;
12426ff1ce9Sdinak 		}
12526ff1ce9Sdinak 	}
12626ff1ce9Sdinak 	return (err != 0 ? err : dlen - left);
12726ff1ce9Sdinak }
12826ff1ce9Sdinak 
12926ff1ce9Sdinak /*
13026ff1ce9Sdinak  * Opens the random number generator devices if not already open.
13126ff1ce9Sdinak  * Always returns the opened fd of the device, or error.
13226ff1ce9Sdinak  */
1337b79d846SDina K Nimeh static int
1347b79d846SDina K Nimeh pkcs11_open_common(int *fd, pthread_mutex_t *mtx, const char *dev, int oflag)
13526ff1ce9Sdinak {
1367b79d846SDina K Nimeh 	(void) pthread_mutex_lock(mtx);
1377b79d846SDina K Nimeh 	if (*fd < 0)
1387b79d846SDina K Nimeh 		*fd = open_nointr(dev, oflag);
1397b79d846SDina K Nimeh 	(void) pthread_mutex_unlock(mtx);
140*41cf421dSDina K Nimeh 
1417b79d846SDina K Nimeh 	return (*fd);
14226ff1ce9Sdinak }
14326ff1ce9Sdinak 
1447b79d846SDina K Nimeh static int
1457b79d846SDina K Nimeh pkcs11_open_random(void)
1467b79d846SDina K Nimeh {
1477b79d846SDina K Nimeh 	return (pkcs11_open_common(&random_fd, &random_mutex,
1487b79d846SDina K Nimeh 	    RANDOM_DEVICE, O_RDONLY));
1497b79d846SDina K Nimeh }
1507b79d846SDina K Nimeh 
1517b79d846SDina K Nimeh static int
15226ff1ce9Sdinak pkcs11_open_urandom(void)
15326ff1ce9Sdinak {
1547b79d846SDina K Nimeh 	return (pkcs11_open_common(&urandom_fd, &urandom_mutex,
1557b79d846SDina K Nimeh 	    URANDOM_DEVICE, O_RDONLY));
1567b79d846SDina K Nimeh }
1577b79d846SDina K Nimeh 
1587b79d846SDina K Nimeh static int
1597b79d846SDina K Nimeh pkcs11_open_random_seed(void)
1607b79d846SDina K Nimeh {
1617b79d846SDina K Nimeh 	return (pkcs11_open_common(&random_seed_fd, &random_seed_mutex,
1627b79d846SDina K Nimeh 	    RANDOM_DEVICE, O_WRONLY));
1637b79d846SDina K Nimeh }
1647b79d846SDina K Nimeh 
1657b79d846SDina K Nimeh static int
1667b79d846SDina K Nimeh pkcs11_open_urandom_seed(void)
1677b79d846SDina K Nimeh {
1687b79d846SDina K Nimeh 	return (pkcs11_open_common(&urandom_seed_fd, &urandom_seed_mutex,
1697b79d846SDina K Nimeh 	    URANDOM_DEVICE, O_WRONLY));
17026ff1ce9Sdinak }
17126ff1ce9Sdinak 
17226ff1ce9Sdinak /*
17326ff1ce9Sdinak  * Close the random number generator devices if already open.
17426ff1ce9Sdinak  */
1757b79d846SDina K Nimeh static void
1767b79d846SDina K Nimeh pkcs11_close_common(int *fd, pthread_mutex_t *mtx)
1777b79d846SDina K Nimeh {
1787b79d846SDina K Nimeh 	(void) pthread_mutex_lock(mtx);
1797b79d846SDina K Nimeh 	(void) close(*fd);
1807b79d846SDina K Nimeh 	*fd = -1;
1817b79d846SDina K Nimeh 	(void) pthread_mutex_unlock(mtx);
1827b79d846SDina K Nimeh }
1837b79d846SDina K Nimeh 
18426ff1ce9Sdinak void
18526ff1ce9Sdinak pkcs11_close_random(void)
18626ff1ce9Sdinak {
1877b79d846SDina K Nimeh 	pkcs11_close_common(&random_fd, &random_mutex);
18826ff1ce9Sdinak }
18926ff1ce9Sdinak 
19026ff1ce9Sdinak void
19126ff1ce9Sdinak pkcs11_close_urandom(void)
19226ff1ce9Sdinak {
1937b79d846SDina K Nimeh 	pkcs11_close_common(&urandom_fd, &urandom_mutex);
1947b79d846SDina K Nimeh }
1957b79d846SDina K Nimeh 
1967b79d846SDina K Nimeh static void
1977b79d846SDina K Nimeh pkcs11_close_random_seed(void)
1987b79d846SDina K Nimeh {
1997b79d846SDina K Nimeh 	pkcs11_close_common(&random_seed_fd, &random_seed_mutex);
2007b79d846SDina K Nimeh }
2017b79d846SDina K Nimeh 
2027b79d846SDina K Nimeh void
2037b79d846SDina K Nimeh pkcs11_close_urandom_seed(void)
2047b79d846SDina K Nimeh {
2057b79d846SDina K Nimeh 	pkcs11_close_common(&urandom_seed_fd, &urandom_seed_mutex);
2067b79d846SDina K Nimeh }
2077b79d846SDina K Nimeh 
2087b79d846SDina K Nimeh /*
209*41cf421dSDina K Nimeh  * Read from the random number generator devices.
210*41cf421dSDina K Nimeh  */
211*41cf421dSDina K Nimeh static size_t
212*41cf421dSDina K Nimeh pkcs11_read_common(int *fd, pthread_mutex_t *mtx, void *dbuf, size_t dlen)
213*41cf421dSDina K Nimeh {
214*41cf421dSDina K Nimeh 	size_t	n;
215*41cf421dSDina K Nimeh 
216*41cf421dSDina K Nimeh 	(void) pthread_mutex_lock(mtx);
217*41cf421dSDina K Nimeh 	n = readn_nointr(*fd, dbuf, dlen);
218*41cf421dSDina K Nimeh 	(void) pthread_mutex_unlock(mtx);
219*41cf421dSDina K Nimeh 
220*41cf421dSDina K Nimeh 	return (n);
221*41cf421dSDina K Nimeh }
222*41cf421dSDina K Nimeh 
223*41cf421dSDina K Nimeh static size_t
224*41cf421dSDina K Nimeh pkcs11_read_random(void *dbuf, size_t dlen)
225*41cf421dSDina K Nimeh {
226*41cf421dSDina K Nimeh 	return (pkcs11_read_common(&random_fd, &random_mutex, dbuf, dlen));
227*41cf421dSDina K Nimeh }
228*41cf421dSDina K Nimeh 
229*41cf421dSDina K Nimeh static size_t
230*41cf421dSDina K Nimeh pkcs11_read_urandom(void *dbuf, size_t dlen)
231*41cf421dSDina K Nimeh {
232*41cf421dSDina K Nimeh 	return (pkcs11_read_common(&urandom_fd, &urandom_mutex, dbuf, dlen));
233*41cf421dSDina K Nimeh }
234*41cf421dSDina K Nimeh 
235*41cf421dSDina K Nimeh /*
236*41cf421dSDina K Nimeh  * Write to the random number generator devices.
237*41cf421dSDina K Nimeh  */
238*41cf421dSDina K Nimeh static size_t
239*41cf421dSDina K Nimeh pkcs11_write_common(int *fd, pthread_mutex_t *mtx, void *dbuf, size_t dlen)
240*41cf421dSDina K Nimeh {
241*41cf421dSDina K Nimeh 	size_t	n;
242*41cf421dSDina K Nimeh 
243*41cf421dSDina K Nimeh 	(void) pthread_mutex_lock(mtx);
244*41cf421dSDina K Nimeh 	n = writen_nointr(*fd, dbuf, dlen);
245*41cf421dSDina K Nimeh 	(void) pthread_mutex_unlock(mtx);
246*41cf421dSDina K Nimeh 
247*41cf421dSDina K Nimeh 	return (n);
248*41cf421dSDina K Nimeh }
249*41cf421dSDina K Nimeh 
250*41cf421dSDina K Nimeh static size_t
251*41cf421dSDina K Nimeh pkcs11_write_random_seed(void *dbuf, size_t dlen)
252*41cf421dSDina K Nimeh {
253*41cf421dSDina K Nimeh 	return (pkcs11_write_common(&random_seed_fd, &random_seed_mutex,
254*41cf421dSDina K Nimeh 	    dbuf, dlen));
255*41cf421dSDina K Nimeh }
256*41cf421dSDina K Nimeh 
257*41cf421dSDina K Nimeh static size_t
258*41cf421dSDina K Nimeh pkcs11_write_urandom_seed(void *dbuf, size_t dlen)
259*41cf421dSDina K Nimeh {
260*41cf421dSDina K Nimeh 	return (pkcs11_write_common(&urandom_seed_fd, &urandom_seed_mutex,
261*41cf421dSDina K Nimeh 	    dbuf, dlen));
262*41cf421dSDina K Nimeh }
263*41cf421dSDina K Nimeh 
264*41cf421dSDina K Nimeh /*
2657b79d846SDina K Nimeh  * Seed /dev/random with the data in the buffer.
2667b79d846SDina K Nimeh  */
2677b79d846SDina K Nimeh int
2687b79d846SDina K Nimeh pkcs11_seed_random(void *sbuf, size_t slen)
2697b79d846SDina K Nimeh {
270*41cf421dSDina K Nimeh 	int	rv;
271*41cf421dSDina K Nimeh 
2727b79d846SDina K Nimeh 	if (sbuf == NULL || slen == 0)
2737b79d846SDina K Nimeh 		return (0);
2747b79d846SDina K Nimeh 
2757b79d846SDina K Nimeh 	/* Seeding error could mean it's not supported (errno = EACCES) */
2767b79d846SDina K Nimeh 	if (pkcs11_open_random_seed() < 0)
2777b79d846SDina K Nimeh 		return (-1);
2787b79d846SDina K Nimeh 
279*41cf421dSDina K Nimeh 	rv = -1;
280*41cf421dSDina K Nimeh 	if (pkcs11_write_random_seed(sbuf, slen) == slen)
281*41cf421dSDina K Nimeh 		rv = 0;
282*41cf421dSDina K Nimeh 
2837b79d846SDina K Nimeh 	pkcs11_close_random_seed();
284*41cf421dSDina K Nimeh 	return (rv);
2857b79d846SDina K Nimeh }
2867b79d846SDina K Nimeh 
2877b79d846SDina K Nimeh /*
2887b79d846SDina K Nimeh  * Seed /dev/urandom with the data in the buffer.
2897b79d846SDina K Nimeh  */
2907b79d846SDina K Nimeh int
2917b79d846SDina K Nimeh pkcs11_seed_urandom(void *sbuf, size_t slen)
2927b79d846SDina K Nimeh {
293*41cf421dSDina K Nimeh 	int	rv;
294*41cf421dSDina K Nimeh 
2957b79d846SDina K Nimeh 	if (sbuf == NULL || slen == 0)
2967b79d846SDina K Nimeh 		return (0);
2977b79d846SDina K Nimeh 
2987b79d846SDina K Nimeh 	/* Seeding error could mean it's not supported (errno = EACCES) */
2997b79d846SDina K Nimeh 	if (pkcs11_open_urandom_seed() < 0)
3007b79d846SDina K Nimeh 		return (-1);
3017b79d846SDina K Nimeh 
302*41cf421dSDina K Nimeh 	rv = -1;
303*41cf421dSDina K Nimeh 	if (pkcs11_write_urandom_seed(sbuf, slen) == slen)
304*41cf421dSDina K Nimeh 		rv = 0;
305*41cf421dSDina K Nimeh 
3067b79d846SDina K Nimeh 	pkcs11_close_urandom_seed();
307*41cf421dSDina K Nimeh 	return (rv);
3087b79d846SDina K Nimeh }
3097b79d846SDina K Nimeh 
3107b79d846SDina K Nimeh /*
3117b79d846SDina K Nimeh  * Put the requested amount of random data into a preallocated buffer.
3127b79d846SDina K Nimeh  * Good for token key data, persistent objects.
3137b79d846SDina K Nimeh  */
3147b79d846SDina K Nimeh int
3157b79d846SDina K Nimeh pkcs11_get_random(void *dbuf, size_t dlen)
3167b79d846SDina K Nimeh {
3177b79d846SDina K Nimeh 	if (dbuf == NULL || dlen == 0)
3187b79d846SDina K Nimeh 		return (0);
3197b79d846SDina K Nimeh 
3207b79d846SDina K Nimeh 	/* Read random data directly from /dev/random */
3217b79d846SDina K Nimeh 	if (pkcs11_open_random() < 0)
3227b79d846SDina K Nimeh 		return (-1);
3237b79d846SDina K Nimeh 
324*41cf421dSDina K Nimeh 	if (pkcs11_read_random(dbuf, dlen) == dlen)
3257b79d846SDina K Nimeh 		return (0);
3267b79d846SDina K Nimeh 	return (-1);
32726ff1ce9Sdinak }
3281c9bd843Sdinak 
3291c9bd843Sdinak /*
3301c9bd843Sdinak  * Put the requested amount of random data into a preallocated buffer.
3311c9bd843Sdinak  * Good for passphrase salts, initialization vectors.
3321c9bd843Sdinak  */
3331c9bd843Sdinak int
3347b79d846SDina K Nimeh pkcs11_get_urandom(void *dbuf, size_t dlen)
3351c9bd843Sdinak {
3361c9bd843Sdinak 	if (dbuf == NULL || dlen == 0)
3371c9bd843Sdinak 		return (0);
3381c9bd843Sdinak 
3391c9bd843Sdinak 	/* Read random data directly from /dev/urandom */
34026ff1ce9Sdinak 	if (pkcs11_open_urandom() < 0)
3411c9bd843Sdinak 		return (-1);
34226ff1ce9Sdinak 
343*41cf421dSDina K Nimeh 	if (pkcs11_read_urandom(dbuf, dlen) == dlen)
34426ff1ce9Sdinak 		return (0);
34526ff1ce9Sdinak 	return (-1);
34626ff1ce9Sdinak }
34726ff1ce9Sdinak 
34826ff1ce9Sdinak /*
3497b79d846SDina K Nimeh  * Same as pkcs11_get_urandom but ensures non zero data.
35026ff1ce9Sdinak  */
35126ff1ce9Sdinak int
3527b79d846SDina K Nimeh pkcs11_get_nzero_urandom(void *dbuf, size_t dlen)
35326ff1ce9Sdinak {
35426ff1ce9Sdinak 	char	extrarand[32];
35526ff1ce9Sdinak 	size_t	bytesleft = 0;
35626ff1ce9Sdinak 	size_t	i = 0;
35726ff1ce9Sdinak 
35826ff1ce9Sdinak 	/* Start with some random data */
3597b79d846SDina K Nimeh 	if (pkcs11_get_urandom(dbuf, dlen) < 0)
36026ff1ce9Sdinak 		return (-1);
36126ff1ce9Sdinak 
36226ff1ce9Sdinak 	/* Walk through data replacing any 0 bytes with more random data */
36326ff1ce9Sdinak 	while (i < dlen) {
36426ff1ce9Sdinak 		if (((char *)dbuf)[i] != 0) {
36526ff1ce9Sdinak 			i++;
36626ff1ce9Sdinak 			continue;
36726ff1ce9Sdinak 		}
36826ff1ce9Sdinak 
36926ff1ce9Sdinak 		if (bytesleft == 0) {
37026ff1ce9Sdinak 			bytesleft = sizeof (extrarand);
3717b79d846SDina K Nimeh 			if (pkcs11_get_urandom(extrarand, bytesleft) < 0)
37226ff1ce9Sdinak 				return (-1);
37326ff1ce9Sdinak 		}
37426ff1ce9Sdinak 		bytesleft--;
37526ff1ce9Sdinak 
37626ff1ce9Sdinak 		((char *)dbuf)[i] = extrarand[bytesleft];
37726ff1ce9Sdinak 	}
37826ff1ce9Sdinak 	return (0);
3791c9bd843Sdinak }
380