Home | History | Annotate | Download | only in common
      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