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 
35 #ifdef	_REENTRANT
36 
37 #include <pthread.h>
38 
39 static pthread_mutex_t	random_mutex = PTHREAD_MUTEX_INITIALIZER;
40 static pthread_mutex_t	urandom_mutex = PTHREAD_MUTEX_INITIALIZER;
41 
42 #define	RAND_LOCK(x)	(void) pthread_mutex_lock(x)
43 #define	RAND_UNLOCK(x)	(void) pthread_mutex_unlock(x)
44 
45 #else
46 
47 #define	RAND_LOCK(x)
48 #define	RAND_UNLOCK(x)
49 
50 #endif
51 
52 #define	RANDOM_DEVICE		"/dev/random"	/* random device name */
53 #define	URANDOM_DEVICE		"/dev/urandom"	/* urandom device name */
54 
55 static int	random_fd = -1;
56 static int	urandom_fd = -1;
57 
58 /*
59  * Equivalent of open(2) insulated from EINTR.
60  * Also sets close-on-exec.
61  */
62 int
63 open_nointr(const char *path, int oflag, ...)
64 {
65 	int	fd;
66 	mode_t	pmode;
67 	va_list	alist;
68 
69 	va_start(alist, oflag);
70 	pmode = va_arg(alist, mode_t);
71 	va_end(alist);
72 
73 	do {
74 		if ((fd = open(path, oflag, pmode)) >= 0) {
75 			(void) fcntl(fd, F_SETFD, FD_CLOEXEC);
76 			break;
77 		}
78 		/* errno definitely set by failed open() */
79 	} while (errno == EINTR);
80 	return (fd);
81 }
82 
83 /*
84  * Equivalent of read(2) insulated from EINTR.
85  */
86 ssize_t
87 readn_nointr(int fd, void *dbuf, size_t dlen)
88 {
89 	char	*marker = dbuf;
90 	size_t	left = dlen;
91 	ssize_t	nread = 0, err;
92 
93 	for (err = 0; left > 0 && nread != -1; marker += nread, left -= nread) {
94 		if ((nread = read(fd, marker, left)) < 0) {
95 			if (errno == EINTR) {	/* keep trying */
96 				nread = 0;
97 				continue;
98 			}
99 			err = nread;		/* hard error */
100 			break;
101 		} else if (nread == 0) {
102 			break;
103 		}
104 	}
105 	return (err != 0 ? err : dlen - left);
106 }
107 
108 /*
109  * Equivalent of write(2) insulated from EINTR.
110  */
111 ssize_t
112 writen_nointr(int fd, void *dbuf, size_t dlen)
113 {
114 	char	*marker = dbuf;
115 	size_t	left = dlen;
116 	ssize_t	nwrite = 0, err;
117 
118 	for (err = 0; left > 0 && nwrite != -1; marker += nwrite,
119 	    left -= nwrite) {
120 		if ((nwrite = write(fd, marker, left)) < 0) {
121 			if (errno == EINTR) {	/* keep trying */
122 				nwrite = 0;
123 				continue;
124 			}
125 			err = nwrite;		/* hard error */
126 			break;
127 		} else if (nwrite == 0) {
128 			break;
129 		}
130 	}
131 	return (err != 0 ? err : dlen - left);
132 }
133 
134 /*
135  * Opens the random number generator devices if not already open.
136  * Always returns the opened fd of the device, or error.
137  */
138 int
139 pkcs11_open_random(void)
140 {
141 	RAND_LOCK(&random_mutex);
142 	if (random_fd < 0)
143 		random_fd = open_nointr(RANDOM_DEVICE, O_RDONLY);
144 	RAND_UNLOCK(&random_mutex);
145 	return (random_fd);
146 }
147 
148 int
149 pkcs11_open_urandom(void)
150 {
151 	RAND_LOCK(&urandom_mutex);
152 	if (urandom_fd < 0)
153 		urandom_fd = open_nointr(URANDOM_DEVICE, O_RDONLY);
154 	RAND_UNLOCK(&urandom_mutex);
155 	return (urandom_fd);
156 }
157 
158 /*
159  * Close the random number generator devices if already open.
160  */
161 void
162 pkcs11_close_random(void)
163 {
164 	if (random_fd < 0)
165 		return;
166 	RAND_LOCK(&random_mutex);
167 	(void) close(random_fd);
168 	random_fd = -1;
169 	RAND_UNLOCK(&random_mutex);
170 }
171 
172 void
173 pkcs11_close_urandom(void)
174 {
175 	if (urandom_fd < 0)
176 		return;
177 	RAND_LOCK(&urandom_mutex);
178 	(void) close(urandom_fd);
179 	urandom_fd = -1;
180 	RAND_UNLOCK(&urandom_mutex);
181 }
182 
183 /*
184  * Put the requested amount of random data into a preallocated buffer.
185  * Good for passphrase salts, initialization vectors.
186  */
187 int
188 pkcs11_random_data(void *dbuf, size_t dlen)
189 {
190 	if (dbuf == NULL || dlen == 0)
191 		return (0);
192 
193 	/* Read random data directly from /dev/urandom */
194 	if (pkcs11_open_urandom() < 0)
195 		return (-1);
196 
197 	if (readn_nointr(urandom_fd, dbuf, dlen) == dlen)
198 		return (0);
199 	return (-1);
200 }
201 
202 /*
203  * Same as pkcs11_random_data but ensures non zero data.
204  */
205 int
206 pkcs11_nzero_random_data(void *dbuf, size_t dlen)
207 {
208 	char	extrarand[32];
209 	size_t	bytesleft = 0;
210 	size_t	i = 0;
211 
212 	/* Start with some random data */
213 	if (pkcs11_random_data(dbuf, dlen) < 0)
214 		return (-1);
215 
216 	/* Walk through data replacing any 0 bytes with more random data */
217 	while (i < dlen) {
218 		if (((char *)dbuf)[i] != 0) {
219 			i++;
220 			continue;
221 		}
222 
223 		if (bytesleft == 0) {
224 			bytesleft = sizeof (extrarand);
225 			if (pkcs11_random_data(extrarand, bytesleft) < 0)
226 				return (-1);
227 		}
228 		bytesleft--;
229 
230 		((char *)dbuf)[i] = extrarand[bytesleft];
231 	}
232 	return (0);
233 }
234