xref: /titanic_52/usr/src/lib/libcryptoutil/common/random.c (revision 7b79d84636ec82b45f00c982cf6810db81852d17)
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 /*
2219193bb6SDina 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>
3219193bb6SDina K Nimeh #include <stdarg.h>
331c9bd843Sdinak #include <cryptoutil.h>
3426ff1ce9Sdinak #include <pthread.h>
3526ff1ce9Sdinak 
36*7b79d846SDina K Nimeh 
3726ff1ce9Sdinak static pthread_mutex_t	random_mutex = PTHREAD_MUTEX_INITIALIZER;
3826ff1ce9Sdinak static pthread_mutex_t	urandom_mutex = PTHREAD_MUTEX_INITIALIZER;
3926ff1ce9Sdinak 
40*7b79d846SDina K Nimeh static pthread_mutex_t	random_seed_mutex = PTHREAD_MUTEX_INITIALIZER;
41*7b79d846SDina 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 
49*7b79d846SDina K Nimeh static int	random_seed_fd = -1;
50*7b79d846SDina K Nimeh static int	urandom_seed_fd = -1;
51*7b79d846SDina K Nimeh 
52*7b79d846SDina 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  */
133*7b79d846SDina K Nimeh static int
134*7b79d846SDina K Nimeh pkcs11_open_common(int *fd, pthread_mutex_t *mtx, const char *dev, int oflag)
13526ff1ce9Sdinak {
136*7b79d846SDina K Nimeh 	if (*fd < 0) {
137*7b79d846SDina K Nimeh 		(void) pthread_mutex_lock(mtx);
138*7b79d846SDina K Nimeh 		if (*fd < 0)
139*7b79d846SDina K Nimeh 			*fd = open_nointr(dev, oflag);
140*7b79d846SDina K Nimeh 		(void) pthread_mutex_unlock(mtx);
141*7b79d846SDina K Nimeh 	}
142*7b79d846SDina K Nimeh 	return (*fd);
14326ff1ce9Sdinak }
14426ff1ce9Sdinak 
145*7b79d846SDina K Nimeh static int
146*7b79d846SDina K Nimeh pkcs11_open_random(void)
147*7b79d846SDina K Nimeh {
148*7b79d846SDina K Nimeh 	return (pkcs11_open_common(&random_fd, &random_mutex,
149*7b79d846SDina K Nimeh 	    RANDOM_DEVICE, O_RDONLY));
150*7b79d846SDina K Nimeh }
151*7b79d846SDina K Nimeh 
152*7b79d846SDina K Nimeh static int
15326ff1ce9Sdinak pkcs11_open_urandom(void)
15426ff1ce9Sdinak {
155*7b79d846SDina K Nimeh 	return (pkcs11_open_common(&urandom_fd, &urandom_mutex,
156*7b79d846SDina K Nimeh 	    URANDOM_DEVICE, O_RDONLY));
157*7b79d846SDina K Nimeh }
158*7b79d846SDina K Nimeh 
159*7b79d846SDina K Nimeh static int
160*7b79d846SDina K Nimeh pkcs11_open_random_seed(void)
161*7b79d846SDina K Nimeh {
162*7b79d846SDina K Nimeh 	return (pkcs11_open_common(&random_seed_fd, &random_seed_mutex,
163*7b79d846SDina K Nimeh 	    RANDOM_DEVICE, O_WRONLY));
164*7b79d846SDina K Nimeh }
165*7b79d846SDina K Nimeh 
166*7b79d846SDina K Nimeh static int
167*7b79d846SDina K Nimeh pkcs11_open_urandom_seed(void)
168*7b79d846SDina K Nimeh {
169*7b79d846SDina K Nimeh 	return (pkcs11_open_common(&urandom_seed_fd, &urandom_seed_mutex,
170*7b79d846SDina K Nimeh 	    URANDOM_DEVICE, O_WRONLY));
17126ff1ce9Sdinak }
17226ff1ce9Sdinak 
17326ff1ce9Sdinak /*
17426ff1ce9Sdinak  * Close the random number generator devices if already open.
17526ff1ce9Sdinak  */
176*7b79d846SDina K Nimeh static void
177*7b79d846SDina K Nimeh pkcs11_close_common(int *fd, pthread_mutex_t *mtx)
178*7b79d846SDina K Nimeh {
179*7b79d846SDina K Nimeh 	if (*fd < 0)
180*7b79d846SDina K Nimeh 		return;
181*7b79d846SDina K Nimeh 	(void) pthread_mutex_lock(mtx);
182*7b79d846SDina K Nimeh 	(void) close(*fd);
183*7b79d846SDina K Nimeh 	*fd = -1;
184*7b79d846SDina K Nimeh 	(void) pthread_mutex_unlock(mtx);
185*7b79d846SDina K Nimeh }
186*7b79d846SDina K Nimeh 
18726ff1ce9Sdinak void
18826ff1ce9Sdinak pkcs11_close_random(void)
18926ff1ce9Sdinak {
190*7b79d846SDina K Nimeh 	pkcs11_close_common(&random_fd, &random_mutex);
19126ff1ce9Sdinak }
19226ff1ce9Sdinak 
19326ff1ce9Sdinak void
19426ff1ce9Sdinak pkcs11_close_urandom(void)
19526ff1ce9Sdinak {
196*7b79d846SDina K Nimeh 	pkcs11_close_common(&urandom_fd, &urandom_mutex);
197*7b79d846SDina K Nimeh }
198*7b79d846SDina K Nimeh 
199*7b79d846SDina K Nimeh static void
200*7b79d846SDina K Nimeh pkcs11_close_random_seed(void)
201*7b79d846SDina K Nimeh {
202*7b79d846SDina K Nimeh 	pkcs11_close_common(&random_seed_fd, &random_seed_mutex);
203*7b79d846SDina K Nimeh }
204*7b79d846SDina K Nimeh 
205*7b79d846SDina K Nimeh void
206*7b79d846SDina K Nimeh pkcs11_close_urandom_seed(void)
207*7b79d846SDina K Nimeh {
208*7b79d846SDina K Nimeh 	pkcs11_close_common(&urandom_seed_fd, &urandom_seed_mutex);
209*7b79d846SDina K Nimeh }
210*7b79d846SDina K Nimeh 
211*7b79d846SDina K Nimeh /*
212*7b79d846SDina K Nimeh  * Seed /dev/random with the data in the buffer.
213*7b79d846SDina K Nimeh  */
214*7b79d846SDina K Nimeh int
215*7b79d846SDina K Nimeh pkcs11_seed_random(void *sbuf, size_t slen)
216*7b79d846SDina K Nimeh {
217*7b79d846SDina K Nimeh 	if (sbuf == NULL || slen == 0)
218*7b79d846SDina K Nimeh 		return (0);
219*7b79d846SDina K Nimeh 
220*7b79d846SDina K Nimeh 	/* Seeding error could mean it's not supported (errno = EACCES) */
221*7b79d846SDina K Nimeh 	if (pkcs11_open_random_seed() < 0)
222*7b79d846SDina K Nimeh 		return (-1);
223*7b79d846SDina K Nimeh 
224*7b79d846SDina K Nimeh 	if (writen_nointr(random_seed_fd, sbuf, slen) == slen) {
225*7b79d846SDina K Nimeh 		pkcs11_close_random_seed();
226*7b79d846SDina K Nimeh 		return (0);
227*7b79d846SDina K Nimeh 	}
228*7b79d846SDina K Nimeh 	return (-1);
229*7b79d846SDina K Nimeh }
230*7b79d846SDina K Nimeh 
231*7b79d846SDina K Nimeh /*
232*7b79d846SDina K Nimeh  * Seed /dev/urandom with the data in the buffer.
233*7b79d846SDina K Nimeh  */
234*7b79d846SDina K Nimeh int
235*7b79d846SDina K Nimeh pkcs11_seed_urandom(void *sbuf, size_t slen)
236*7b79d846SDina K Nimeh {
237*7b79d846SDina K Nimeh 	if (sbuf == NULL || slen == 0)
238*7b79d846SDina K Nimeh 		return (0);
239*7b79d846SDina K Nimeh 
240*7b79d846SDina K Nimeh 	/* Seeding error could mean it's not supported (errno = EACCES) */
241*7b79d846SDina K Nimeh 	if (pkcs11_open_urandom_seed() < 0)
242*7b79d846SDina K Nimeh 		return (-1);
243*7b79d846SDina K Nimeh 
244*7b79d846SDina K Nimeh 	if (writen_nointr(urandom_seed_fd, sbuf, slen) == slen) {
245*7b79d846SDina K Nimeh 		pkcs11_close_urandom_seed();
246*7b79d846SDina K Nimeh 		return (0);
247*7b79d846SDina K Nimeh 	}
248*7b79d846SDina K Nimeh 	return (-1);
249*7b79d846SDina K Nimeh }
250*7b79d846SDina K Nimeh 
251*7b79d846SDina K Nimeh /*
252*7b79d846SDina K Nimeh  * Put the requested amount of random data into a preallocated buffer.
253*7b79d846SDina K Nimeh  * Good for token key data, persistent objects.
254*7b79d846SDina K Nimeh  */
255*7b79d846SDina K Nimeh int
256*7b79d846SDina K Nimeh pkcs11_get_random(void *dbuf, size_t dlen)
257*7b79d846SDina K Nimeh {
258*7b79d846SDina K Nimeh 	if (dbuf == NULL || dlen == 0)
259*7b79d846SDina K Nimeh 		return (0);
260*7b79d846SDina K Nimeh 
261*7b79d846SDina K Nimeh 	/* Read random data directly from /dev/random */
262*7b79d846SDina K Nimeh 	if (pkcs11_open_random() < 0)
263*7b79d846SDina K Nimeh 		return (-1);
264*7b79d846SDina K Nimeh 
265*7b79d846SDina K Nimeh 	if (readn_nointr(random_fd, dbuf, dlen) == dlen)
266*7b79d846SDina K Nimeh 		return (0);
267*7b79d846SDina K Nimeh 	return (-1);
26826ff1ce9Sdinak }
2691c9bd843Sdinak 
2701c9bd843Sdinak /*
2711c9bd843Sdinak  * Put the requested amount of random data into a preallocated buffer.
2721c9bd843Sdinak  * Good for passphrase salts, initialization vectors.
2731c9bd843Sdinak  */
2741c9bd843Sdinak int
275*7b79d846SDina K Nimeh pkcs11_get_urandom(void *dbuf, size_t dlen)
2761c9bd843Sdinak {
2771c9bd843Sdinak 	if (dbuf == NULL || dlen == 0)
2781c9bd843Sdinak 		return (0);
2791c9bd843Sdinak 
2801c9bd843Sdinak 	/* Read random data directly from /dev/urandom */
28126ff1ce9Sdinak 	if (pkcs11_open_urandom() < 0)
2821c9bd843Sdinak 		return (-1);
28326ff1ce9Sdinak 
28419193bb6SDina K Nimeh 	if (readn_nointr(urandom_fd, dbuf, dlen) == dlen)
28526ff1ce9Sdinak 		return (0);
28626ff1ce9Sdinak 	return (-1);
28726ff1ce9Sdinak }
28826ff1ce9Sdinak 
28926ff1ce9Sdinak /*
290*7b79d846SDina K Nimeh  * Same as pkcs11_get_urandom but ensures non zero data.
29126ff1ce9Sdinak  */
29226ff1ce9Sdinak int
293*7b79d846SDina K Nimeh pkcs11_get_nzero_urandom(void *dbuf, size_t dlen)
29426ff1ce9Sdinak {
29526ff1ce9Sdinak 	char	extrarand[32];
29626ff1ce9Sdinak 	size_t	bytesleft = 0;
29726ff1ce9Sdinak 	size_t	i = 0;
29826ff1ce9Sdinak 
29926ff1ce9Sdinak 	/* Start with some random data */
300*7b79d846SDina K Nimeh 	if (pkcs11_get_urandom(dbuf, dlen) < 0)
30126ff1ce9Sdinak 		return (-1);
30226ff1ce9Sdinak 
30326ff1ce9Sdinak 	/* Walk through data replacing any 0 bytes with more random data */
30426ff1ce9Sdinak 	while (i < dlen) {
30526ff1ce9Sdinak 		if (((char *)dbuf)[i] != 0) {
30626ff1ce9Sdinak 			i++;
30726ff1ce9Sdinak 			continue;
30826ff1ce9Sdinak 		}
30926ff1ce9Sdinak 
31026ff1ce9Sdinak 		if (bytesleft == 0) {
31126ff1ce9Sdinak 			bytesleft = sizeof (extrarand);
312*7b79d846SDina K Nimeh 			if (pkcs11_get_urandom(extrarand, bytesleft) < 0)
31326ff1ce9Sdinak 				return (-1);
31426ff1ce9Sdinak 		}
31526ff1ce9Sdinak 		bytesleft--;
31626ff1ce9Sdinak 
31726ff1ce9Sdinak 		((char *)dbuf)[i] = extrarand[bytesleft];
31826ff1ce9Sdinak 	}
31926ff1ce9Sdinak 	return (0);
3201c9bd843Sdinak }
321