xref: /titanic_52/usr/src/lib/libcryptoutil/common/random.c (revision 19193bb63b10fe65b6e01f1ce7232407a18a917a)
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  */
211c9bd843Sdinak /*
22*19193bb6SDina K Nimeh  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
231c9bd843Sdinak  * Use is subject to license terms.
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>
32*19193bb6SDina K Nimeh #include <stdarg.h>
331c9bd843Sdinak #include <cryptoutil.h>
341c9bd843Sdinak 
3526ff1ce9Sdinak #ifdef	_REENTRANT
3626ff1ce9Sdinak 
3726ff1ce9Sdinak #include <pthread.h>
3826ff1ce9Sdinak 
3926ff1ce9Sdinak static pthread_mutex_t	random_mutex = PTHREAD_MUTEX_INITIALIZER;
4026ff1ce9Sdinak static pthread_mutex_t	urandom_mutex = PTHREAD_MUTEX_INITIALIZER;
4126ff1ce9Sdinak 
4226ff1ce9Sdinak #define	RAND_LOCK(x)	(void) pthread_mutex_lock(x)
4326ff1ce9Sdinak #define	RAND_UNLOCK(x)	(void) pthread_mutex_unlock(x)
4426ff1ce9Sdinak 
4526ff1ce9Sdinak #else
4626ff1ce9Sdinak 
4726ff1ce9Sdinak #define	RAND_LOCK(x)
4826ff1ce9Sdinak #define	RAND_UNLOCK(x)
4926ff1ce9Sdinak 
5026ff1ce9Sdinak #endif
5126ff1ce9Sdinak 
5226ff1ce9Sdinak #define	RANDOM_DEVICE		"/dev/random"	/* random device name */
5326ff1ce9Sdinak #define	URANDOM_DEVICE		"/dev/urandom"	/* urandom device name */
5426ff1ce9Sdinak 
5526ff1ce9Sdinak static int	random_fd = -1;
5626ff1ce9Sdinak static int	urandom_fd = -1;
5726ff1ce9Sdinak 
5826ff1ce9Sdinak /*
5926ff1ce9Sdinak  * Equivalent of open(2) insulated from EINTR.
6026ff1ce9Sdinak  * Also sets close-on-exec.
6126ff1ce9Sdinak  */
62*19193bb6SDina K Nimeh int
63*19193bb6SDina K Nimeh open_nointr(const char *path, int oflag, ...)
6426ff1ce9Sdinak {
6526ff1ce9Sdinak 	int	fd;
66*19193bb6SDina K Nimeh 	mode_t	pmode;
67*19193bb6SDina K Nimeh 	va_list	alist;
68*19193bb6SDina K Nimeh 
69*19193bb6SDina K Nimeh 	va_start(alist, oflag);
70*19193bb6SDina K Nimeh 	pmode = va_arg(alist, mode_t);
71*19193bb6SDina K Nimeh 	va_end(alist);
7226ff1ce9Sdinak 
7326ff1ce9Sdinak 	do {
74*19193bb6SDina K Nimeh 		if ((fd = open(path, oflag, pmode)) >= 0) {
7526ff1ce9Sdinak 			(void) fcntl(fd, F_SETFD, FD_CLOEXEC);
7626ff1ce9Sdinak 			break;
7726ff1ce9Sdinak 		}
7826ff1ce9Sdinak 		/* errno definitely set by failed open() */
7926ff1ce9Sdinak 	} while (errno == EINTR);
8026ff1ce9Sdinak 	return (fd);
8126ff1ce9Sdinak }
8226ff1ce9Sdinak 
8326ff1ce9Sdinak /*
8426ff1ce9Sdinak  * Equivalent of read(2) insulated from EINTR.
8526ff1ce9Sdinak  */
86*19193bb6SDina K Nimeh ssize_t
87*19193bb6SDina K Nimeh readn_nointr(int fd, void *dbuf, size_t dlen)
8826ff1ce9Sdinak {
8926ff1ce9Sdinak 	char	*marker = dbuf;
9026ff1ce9Sdinak 	size_t	left = dlen;
9126ff1ce9Sdinak 	ssize_t	nread = 0, err;
9226ff1ce9Sdinak 
9326ff1ce9Sdinak 	for (err = 0; left > 0 && nread != -1; marker += nread, left -= nread) {
9426ff1ce9Sdinak 		if ((nread = read(fd, marker, left)) < 0) {
9526ff1ce9Sdinak 			if (errno == EINTR) {	/* keep trying */
9626ff1ce9Sdinak 				nread = 0;
9726ff1ce9Sdinak 				continue;
9826ff1ce9Sdinak 			}
9926ff1ce9Sdinak 			err = nread;		/* hard error */
10026ff1ce9Sdinak 			break;
101*19193bb6SDina K Nimeh 		} else if (nread == 0) {
102*19193bb6SDina K Nimeh 			break;
103*19193bb6SDina K Nimeh 		}
104*19193bb6SDina K Nimeh 	}
105*19193bb6SDina K Nimeh 	return (err != 0 ? err : dlen - left);
106*19193bb6SDina K Nimeh }
107*19193bb6SDina K Nimeh 
108*19193bb6SDina K Nimeh /*
109*19193bb6SDina K Nimeh  * Equivalent of write(2) insulated from EINTR.
110*19193bb6SDina K Nimeh  */
111*19193bb6SDina K Nimeh ssize_t
112*19193bb6SDina K Nimeh writen_nointr(int fd, void *dbuf, size_t dlen)
113*19193bb6SDina K Nimeh {
114*19193bb6SDina K Nimeh 	char	*marker = dbuf;
115*19193bb6SDina K Nimeh 	size_t	left = dlen;
116*19193bb6SDina K Nimeh 	ssize_t	nwrite = 0, err;
117*19193bb6SDina K Nimeh 
118*19193bb6SDina K Nimeh 	for (err = 0; left > 0 && nwrite != -1; marker += nwrite,
119*19193bb6SDina K Nimeh 	    left -= nwrite) {
120*19193bb6SDina K Nimeh 		if ((nwrite = write(fd, marker, left)) < 0) {
121*19193bb6SDina K Nimeh 			if (errno == EINTR) {	/* keep trying */
122*19193bb6SDina K Nimeh 				nwrite = 0;
123*19193bb6SDina K Nimeh 				continue;
124*19193bb6SDina K Nimeh 			}
125*19193bb6SDina K Nimeh 			err = nwrite;		/* hard error */
126*19193bb6SDina K Nimeh 			break;
127*19193bb6SDina K Nimeh 		} else if (nwrite == 0) {
128*19193bb6SDina K Nimeh 			break;
12926ff1ce9Sdinak 		}
13026ff1ce9Sdinak 	}
13126ff1ce9Sdinak 	return (err != 0 ? err : dlen - left);
13226ff1ce9Sdinak }
13326ff1ce9Sdinak 
13426ff1ce9Sdinak /*
13526ff1ce9Sdinak  * Opens the random number generator devices if not already open.
13626ff1ce9Sdinak  * Always returns the opened fd of the device, or error.
13726ff1ce9Sdinak  */
13826ff1ce9Sdinak int
13926ff1ce9Sdinak pkcs11_open_random(void)
14026ff1ce9Sdinak {
14126ff1ce9Sdinak 	RAND_LOCK(&random_mutex);
14226ff1ce9Sdinak 	if (random_fd < 0)
143*19193bb6SDina K Nimeh 		random_fd = open_nointr(RANDOM_DEVICE, O_RDONLY);
14426ff1ce9Sdinak 	RAND_UNLOCK(&random_mutex);
14526ff1ce9Sdinak 	return (random_fd);
14626ff1ce9Sdinak }
14726ff1ce9Sdinak 
14826ff1ce9Sdinak int
14926ff1ce9Sdinak pkcs11_open_urandom(void)
15026ff1ce9Sdinak {
15126ff1ce9Sdinak 	RAND_LOCK(&urandom_mutex);
15226ff1ce9Sdinak 	if (urandom_fd < 0)
153*19193bb6SDina K Nimeh 		urandom_fd = open_nointr(URANDOM_DEVICE, O_RDONLY);
15426ff1ce9Sdinak 	RAND_UNLOCK(&urandom_mutex);
15526ff1ce9Sdinak 	return (urandom_fd);
15626ff1ce9Sdinak }
15726ff1ce9Sdinak 
15826ff1ce9Sdinak /*
15926ff1ce9Sdinak  * Close the random number generator devices if already open.
16026ff1ce9Sdinak  */
16126ff1ce9Sdinak void
16226ff1ce9Sdinak pkcs11_close_random(void)
16326ff1ce9Sdinak {
16426ff1ce9Sdinak 	if (random_fd < 0)
16526ff1ce9Sdinak 		return;
16626ff1ce9Sdinak 	RAND_LOCK(&random_mutex);
16726ff1ce9Sdinak 	(void) close(random_fd);
16826ff1ce9Sdinak 	random_fd = -1;
16926ff1ce9Sdinak 	RAND_UNLOCK(&random_mutex);
17026ff1ce9Sdinak }
17126ff1ce9Sdinak 
17226ff1ce9Sdinak void
17326ff1ce9Sdinak pkcs11_close_urandom(void)
17426ff1ce9Sdinak {
17526ff1ce9Sdinak 	if (urandom_fd < 0)
17626ff1ce9Sdinak 		return;
17726ff1ce9Sdinak 	RAND_LOCK(&urandom_mutex);
17826ff1ce9Sdinak 	(void) close(urandom_fd);
17926ff1ce9Sdinak 	urandom_fd = -1;
18026ff1ce9Sdinak 	RAND_UNLOCK(&urandom_mutex);
18126ff1ce9Sdinak }
1821c9bd843Sdinak 
1831c9bd843Sdinak /*
1841c9bd843Sdinak  * Put the requested amount of random data into a preallocated buffer.
1851c9bd843Sdinak  * Good for passphrase salts, initialization vectors.
1861c9bd843Sdinak  */
1871c9bd843Sdinak int
1881c9bd843Sdinak pkcs11_random_data(void *dbuf, size_t dlen)
1891c9bd843Sdinak {
1901c9bd843Sdinak 	if (dbuf == NULL || dlen == 0)
1911c9bd843Sdinak 		return (0);
1921c9bd843Sdinak 
1931c9bd843Sdinak 	/* Read random data directly from /dev/urandom */
19426ff1ce9Sdinak 	if (pkcs11_open_urandom() < 0)
1951c9bd843Sdinak 		return (-1);
19626ff1ce9Sdinak 
197*19193bb6SDina K Nimeh 	if (readn_nointr(urandom_fd, dbuf, dlen) == dlen)
19826ff1ce9Sdinak 		return (0);
19926ff1ce9Sdinak 	return (-1);
20026ff1ce9Sdinak }
20126ff1ce9Sdinak 
20226ff1ce9Sdinak /*
20326ff1ce9Sdinak  * Same as pkcs11_random_data but ensures non zero data.
20426ff1ce9Sdinak  */
20526ff1ce9Sdinak int
20626ff1ce9Sdinak pkcs11_nzero_random_data(void *dbuf, size_t dlen)
20726ff1ce9Sdinak {
20826ff1ce9Sdinak 	char	extrarand[32];
20926ff1ce9Sdinak 	size_t	bytesleft = 0;
21026ff1ce9Sdinak 	size_t	i = 0;
21126ff1ce9Sdinak 
21226ff1ce9Sdinak 	/* Start with some random data */
21326ff1ce9Sdinak 	if (pkcs11_random_data(dbuf, dlen) < 0)
21426ff1ce9Sdinak 		return (-1);
21526ff1ce9Sdinak 
21626ff1ce9Sdinak 	/* Walk through data replacing any 0 bytes with more random data */
21726ff1ce9Sdinak 	while (i < dlen) {
21826ff1ce9Sdinak 		if (((char *)dbuf)[i] != 0) {
21926ff1ce9Sdinak 			i++;
22026ff1ce9Sdinak 			continue;
22126ff1ce9Sdinak 		}
22226ff1ce9Sdinak 
22326ff1ce9Sdinak 		if (bytesleft == 0) {
22426ff1ce9Sdinak 			bytesleft = sizeof (extrarand);
22526ff1ce9Sdinak 			if (pkcs11_random_data(extrarand, bytesleft) < 0)
22626ff1ce9Sdinak 				return (-1);
22726ff1ce9Sdinak 		}
22826ff1ce9Sdinak 		bytesleft--;
22926ff1ce9Sdinak 
23026ff1ce9Sdinak 		((char *)dbuf)[i] = extrarand[bytesleft];
23126ff1ce9Sdinak 	}
23226ff1ce9Sdinak 	return (0);
2331c9bd843Sdinak }
234