xref: /illumos-gate/usr/src/lib/libcryptoutil/common/random.c (revision a38ddfee9c8c6b6c5a2947ff52fd2338362a4444)
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  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #pragma ident	"%Z%%M%	%I%	%E% SMI"
27 
28 #include <stdio.h>
29 #include <unistd.h>
30 #include <errno.h>
31 #include <string.h>
32 #include <fcntl.h>
33 #include <locale.h>
34 #include <cryptoutil.h>
35 
36 #ifdef	_REENTRANT
37 
38 #include <pthread.h>
39 
40 static pthread_mutex_t	random_mutex = PTHREAD_MUTEX_INITIALIZER;
41 static pthread_mutex_t	urandom_mutex = PTHREAD_MUTEX_INITIALIZER;
42 
43 #define	RAND_LOCK(x)	(void) pthread_mutex_lock(x)
44 #define	RAND_UNLOCK(x)	(void) pthread_mutex_unlock(x)
45 
46 #else
47 
48 #define	RAND_LOCK(x)
49 #define	RAND_UNLOCK(x)
50 
51 #endif
52 
53 #define	RANDOM_DEVICE		"/dev/random"	/* random device name */
54 #define	URANDOM_DEVICE		"/dev/urandom"	/* urandom device name */
55 
56 static int	random_fd = -1;
57 static int	urandom_fd = -1;
58 
59 /*
60  * Equivalent of open(2) insulated from EINTR.
61  * Also sets close-on-exec.
62  */
63 static int
64 OPEN(const char *path, int oflag)
65 {
66 	int	fd;
67 
68 	do {
69 		if ((fd = open(path, oflag)) >= 0) {
70 			(void) fcntl(fd, F_SETFD, FD_CLOEXEC);
71 			break;
72 		}
73 		/* errno definitely set by failed open() */
74 	} while (errno == EINTR);
75 	return (fd);
76 }
77 
78 /*
79  * Equivalent of read(2) insulated from EINTR.
80  */
81 static ssize_t
82 READ(int fd, void *dbuf, size_t dlen)
83 {
84 	char	*marker = dbuf;
85 	size_t	left = dlen;
86 	ssize_t	nread = 0, err;
87 
88 	for (err = 0; left > 0 && nread != -1; marker += nread, left -= nread) {
89 		if ((nread = read(fd, marker, left)) < 0) {
90 			if (errno == EINTR) {	/* keep trying */
91 				nread = 0;
92 				continue;
93 			}
94 			err = nread;		/* hard error */
95 			break;
96 		}
97 	}
98 	return (err != 0 ? err : dlen - left);
99 }
100 
101 /*
102  * Opens the random number generator devices if not already open.
103  * Always returns the opened fd of the device, or error.
104  */
105 int
106 pkcs11_open_random(void)
107 {
108 	RAND_LOCK(&random_mutex);
109 	if (random_fd < 0)
110 		random_fd = OPEN(RANDOM_DEVICE, O_RDONLY);
111 	RAND_UNLOCK(&random_mutex);
112 	return (random_fd);
113 }
114 
115 int
116 pkcs11_open_urandom(void)
117 {
118 	RAND_LOCK(&urandom_mutex);
119 	if (urandom_fd < 0)
120 		urandom_fd = OPEN(URANDOM_DEVICE, O_RDONLY);
121 	RAND_UNLOCK(&urandom_mutex);
122 	return (urandom_fd);
123 }
124 
125 /*
126  * Close the random number generator devices if already open.
127  */
128 void
129 pkcs11_close_random(void)
130 {
131 	if (random_fd < 0)
132 		return;
133 	RAND_LOCK(&random_mutex);
134 	(void) close(random_fd);
135 	random_fd = -1;
136 	RAND_UNLOCK(&random_mutex);
137 }
138 
139 void
140 pkcs11_close_urandom(void)
141 {
142 	if (urandom_fd < 0)
143 		return;
144 	RAND_LOCK(&urandom_mutex);
145 	(void) close(urandom_fd);
146 	urandom_fd = -1;
147 	RAND_UNLOCK(&urandom_mutex);
148 }
149 
150 /*
151  * Put the requested amount of random data into a preallocated buffer.
152  * Good for passphrase salts, initialization vectors.
153  */
154 int
155 pkcs11_random_data(void *dbuf, size_t dlen)
156 {
157 	if (dbuf == NULL || dlen == 0)
158 		return (0);
159 
160 	/* Read random data directly from /dev/urandom */
161 	if (pkcs11_open_urandom() < 0)
162 		return (-1);
163 
164 	if (READ(urandom_fd, dbuf, dlen) == dlen)
165 		return (0);
166 	return (-1);
167 }
168 
169 /*
170  * Same as pkcs11_random_data but ensures non zero data.
171  */
172 int
173 pkcs11_nzero_random_data(void *dbuf, size_t dlen)
174 {
175 	char	extrarand[32];
176 	size_t	bytesleft = 0;
177 	size_t	i = 0;
178 
179 	/* Start with some random data */
180 	if (pkcs11_random_data(dbuf, dlen) < 0)
181 		return (-1);
182 
183 	/* Walk through data replacing any 0 bytes with more random data */
184 	while (i < dlen) {
185 		if (((char *)dbuf)[i] != 0) {
186 			i++;
187 			continue;
188 		}
189 
190 		if (bytesleft == 0) {
191 			bytesleft = sizeof (extrarand);
192 			if (pkcs11_random_data(extrarand, bytesleft) < 0)
193 				return (-1);
194 		}
195 		bytesleft--;
196 
197 		((char *)dbuf)[i] = extrarand[bytesleft];
198 	}
199 	return (0);
200 }
201