xref: /illumos-gate/usr/src/lib/libcryptoutil/common/random.c (revision dbed73cbda2229fd1aa6dc5743993cae7f0a7ee9)
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 2009 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #include <stdio.h>
27 #include <unistd.h>
28 #include <errno.h>
29 #include <string.h>
30 #include <fcntl.h>
31 #include <locale.h>
32 #include <stdarg.h>
33 #include <cryptoutil.h>
34 #include <pthread.h>
35 
36 
37 static pthread_mutex_t	random_mutex = PTHREAD_MUTEX_INITIALIZER;
38 static pthread_mutex_t	urandom_mutex = PTHREAD_MUTEX_INITIALIZER;
39 
40 static pthread_mutex_t	random_seed_mutex = PTHREAD_MUTEX_INITIALIZER;
41 static pthread_mutex_t	urandom_seed_mutex = PTHREAD_MUTEX_INITIALIZER;
42 
43 #define	RANDOM_DEVICE		"/dev/random"	/* random device name */
44 #define	URANDOM_DEVICE		"/dev/urandom"	/* urandom device name */
45 
46 static int	random_fd = -1;
47 static int	urandom_fd = -1;
48 
49 static int	random_seed_fd = -1;
50 static int	urandom_seed_fd = -1;
51 
52 
53 /*
54  * Equivalent of open(2) insulated from EINTR.
55  * Also sets close-on-exec.
56  */
57 int
58 open_nointr(const char *path, int oflag, ...)
59 {
60 	int	fd;
61 	mode_t	pmode;
62 	va_list	alist;
63 
64 	va_start(alist, oflag);
65 	pmode = va_arg(alist, mode_t);
66 	va_end(alist);
67 
68 	do {
69 		if ((fd = open(path, oflag, pmode)) >= 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 ssize_t
82 readn_nointr(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 		} else if (nread == 0) {
97 			break;
98 		}
99 	}
100 	return (err != 0 ? err : dlen - left);
101 }
102 
103 /*
104  * Equivalent of write(2) insulated from EINTR.
105  */
106 ssize_t
107 writen_nointr(int fd, void *dbuf, size_t dlen)
108 {
109 	char	*marker = dbuf;
110 	size_t	left = dlen;
111 	ssize_t	nwrite = 0, err;
112 
113 	for (err = 0; left > 0 && nwrite != -1; marker += nwrite,
114 	    left -= nwrite) {
115 		if ((nwrite = write(fd, marker, left)) < 0) {
116 			if (errno == EINTR) {	/* keep trying */
117 				nwrite = 0;
118 				continue;
119 			}
120 			err = nwrite;		/* hard error */
121 			break;
122 		} else if (nwrite == 0) {
123 			break;
124 		}
125 	}
126 	return (err != 0 ? err : dlen - left);
127 }
128 
129 /*
130  * Opens the random number generator devices if not already open.
131  * Always returns the opened fd of the device, or error.
132  */
133 static int
134 pkcs11_open_common(int *fd, pthread_mutex_t *mtx, const char *dev, int oflag)
135 {
136 	if (*fd < 0) {
137 		(void) pthread_mutex_lock(mtx);
138 		if (*fd < 0)
139 			*fd = open_nointr(dev, oflag);
140 		(void) pthread_mutex_unlock(mtx);
141 	}
142 	return (*fd);
143 }
144 
145 static int
146 pkcs11_open_random(void)
147 {
148 	return (pkcs11_open_common(&random_fd, &random_mutex,
149 	    RANDOM_DEVICE, O_RDONLY));
150 }
151 
152 static int
153 pkcs11_open_urandom(void)
154 {
155 	return (pkcs11_open_common(&urandom_fd, &urandom_mutex,
156 	    URANDOM_DEVICE, O_RDONLY));
157 }
158 
159 static int
160 pkcs11_open_random_seed(void)
161 {
162 	return (pkcs11_open_common(&random_seed_fd, &random_seed_mutex,
163 	    RANDOM_DEVICE, O_WRONLY));
164 }
165 
166 static int
167 pkcs11_open_urandom_seed(void)
168 {
169 	return (pkcs11_open_common(&urandom_seed_fd, &urandom_seed_mutex,
170 	    URANDOM_DEVICE, O_WRONLY));
171 }
172 
173 /*
174  * Close the random number generator devices if already open.
175  */
176 static void
177 pkcs11_close_common(int *fd, pthread_mutex_t *mtx)
178 {
179 	if (*fd < 0)
180 		return;
181 	(void) pthread_mutex_lock(mtx);
182 	(void) close(*fd);
183 	*fd = -1;
184 	(void) pthread_mutex_unlock(mtx);
185 }
186 
187 void
188 pkcs11_close_random(void)
189 {
190 	pkcs11_close_common(&random_fd, &random_mutex);
191 }
192 
193 void
194 pkcs11_close_urandom(void)
195 {
196 	pkcs11_close_common(&urandom_fd, &urandom_mutex);
197 }
198 
199 static void
200 pkcs11_close_random_seed(void)
201 {
202 	pkcs11_close_common(&random_seed_fd, &random_seed_mutex);
203 }
204 
205 void
206 pkcs11_close_urandom_seed(void)
207 {
208 	pkcs11_close_common(&urandom_seed_fd, &urandom_seed_mutex);
209 }
210 
211 /*
212  * Seed /dev/random with the data in the buffer.
213  */
214 int
215 pkcs11_seed_random(void *sbuf, size_t slen)
216 {
217 	if (sbuf == NULL || slen == 0)
218 		return (0);
219 
220 	/* Seeding error could mean it's not supported (errno = EACCES) */
221 	if (pkcs11_open_random_seed() < 0)
222 		return (-1);
223 
224 	if (writen_nointr(random_seed_fd, sbuf, slen) == slen) {
225 		pkcs11_close_random_seed();
226 		return (0);
227 	}
228 	return (-1);
229 }
230 
231 /*
232  * Seed /dev/urandom with the data in the buffer.
233  */
234 int
235 pkcs11_seed_urandom(void *sbuf, size_t slen)
236 {
237 	if (sbuf == NULL || slen == 0)
238 		return (0);
239 
240 	/* Seeding error could mean it's not supported (errno = EACCES) */
241 	if (pkcs11_open_urandom_seed() < 0)
242 		return (-1);
243 
244 	if (writen_nointr(urandom_seed_fd, sbuf, slen) == slen) {
245 		pkcs11_close_urandom_seed();
246 		return (0);
247 	}
248 	return (-1);
249 }
250 
251 /*
252  * Put the requested amount of random data into a preallocated buffer.
253  * Good for token key data, persistent objects.
254  */
255 int
256 pkcs11_get_random(void *dbuf, size_t dlen)
257 {
258 	if (dbuf == NULL || dlen == 0)
259 		return (0);
260 
261 	/* Read random data directly from /dev/random */
262 	if (pkcs11_open_random() < 0)
263 		return (-1);
264 
265 	if (readn_nointr(random_fd, dbuf, dlen) == dlen)
266 		return (0);
267 	return (-1);
268 }
269 
270 /*
271  * Put the requested amount of random data into a preallocated buffer.
272  * Good for passphrase salts, initialization vectors.
273  */
274 int
275 pkcs11_get_urandom(void *dbuf, size_t dlen)
276 {
277 	if (dbuf == NULL || dlen == 0)
278 		return (0);
279 
280 	/* Read random data directly from /dev/urandom */
281 	if (pkcs11_open_urandom() < 0)
282 		return (-1);
283 
284 	if (readn_nointr(urandom_fd, dbuf, dlen) == dlen)
285 		return (0);
286 	return (-1);
287 }
288 
289 /*
290  * Same as pkcs11_get_urandom but ensures non zero data.
291  */
292 int
293 pkcs11_get_nzero_urandom(void *dbuf, size_t dlen)
294 {
295 	char	extrarand[32];
296 	size_t	bytesleft = 0;
297 	size_t	i = 0;
298 
299 	/* Start with some random data */
300 	if (pkcs11_get_urandom(dbuf, dlen) < 0)
301 		return (-1);
302 
303 	/* Walk through data replacing any 0 bytes with more random data */
304 	while (i < dlen) {
305 		if (((char *)dbuf)[i] != 0) {
306 			i++;
307 			continue;
308 		}
309 
310 		if (bytesleft == 0) {
311 			bytesleft = sizeof (extrarand);
312 			if (pkcs11_get_urandom(extrarand, bytesleft) < 0)
313 				return (-1);
314 		}
315 		bytesleft--;
316 
317 		((char *)dbuf)[i] = extrarand[bytesleft];
318 	}
319 	return (0);
320 }
321