15252Sdinak /*
25252Sdinak * CDDL HEADER START
35252Sdinak *
45252Sdinak * The contents of this file are subject to the terms of the
55252Sdinak * Common Development and Distribution License (the "License").
65252Sdinak * You may not use this file except in compliance with the License.
75252Sdinak *
85252Sdinak * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
95252Sdinak * or http://www.opensolaris.org/os/licensing.
105252Sdinak * See the License for the specific language governing permissions
115252Sdinak * and limitations under the License.
125252Sdinak *
135252Sdinak * When distributing Covered Code, include this CDDL HEADER in each
145252Sdinak * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
155252Sdinak * If applicable, add the following below this CDDL HEADER, with the
165252Sdinak * fields enclosed by brackets "[]" replaced with your own identifying
175252Sdinak * information: Portions Copyright [yyyy] [name of copyright owner]
185252Sdinak *
195252Sdinak * CDDL HEADER END
205252Sdinak */
21*12657SDina.Nimeh@Sun.COM
225252Sdinak /*
23*12657SDina.Nimeh@Sun.COM * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
245252Sdinak */
255252Sdinak
265252Sdinak #include <stdio.h>
276104Sdinak #include <unistd.h>
286104Sdinak #include <errno.h>
295252Sdinak #include <string.h>
305252Sdinak #include <fcntl.h>
315252Sdinak #include <locale.h>
328932SDina.Nimeh@Sun.COM #include <stdarg.h>
335252Sdinak #include <cryptoutil.h>
349127SDina.Nimeh@Sun.COM #include <pthread.h>
355252Sdinak
366104Sdinak
376104Sdinak static pthread_mutex_t random_mutex = PTHREAD_MUTEX_INITIALIZER;
386104Sdinak static pthread_mutex_t urandom_mutex = PTHREAD_MUTEX_INITIALIZER;
396104Sdinak
409127SDina.Nimeh@Sun.COM static pthread_mutex_t random_seed_mutex = PTHREAD_MUTEX_INITIALIZER;
419127SDina.Nimeh@Sun.COM static pthread_mutex_t urandom_seed_mutex = PTHREAD_MUTEX_INITIALIZER;
426104Sdinak
436104Sdinak #define RANDOM_DEVICE "/dev/random" /* random device name */
446104Sdinak #define URANDOM_DEVICE "/dev/urandom" /* urandom device name */
456104Sdinak
466104Sdinak static int random_fd = -1;
476104Sdinak static int urandom_fd = -1;
486104Sdinak
499127SDina.Nimeh@Sun.COM static int random_seed_fd = -1;
509127SDina.Nimeh@Sun.COM static int urandom_seed_fd = -1;
519127SDina.Nimeh@Sun.COM
529127SDina.Nimeh@Sun.COM
536104Sdinak /*
546104Sdinak * Equivalent of open(2) insulated from EINTR.
556104Sdinak * Also sets close-on-exec.
566104Sdinak */
578932SDina.Nimeh@Sun.COM int
open_nointr(const char * path,int oflag,...)588932SDina.Nimeh@Sun.COM open_nointr(const char *path, int oflag, ...)
596104Sdinak {
606104Sdinak int fd;
618932SDina.Nimeh@Sun.COM mode_t pmode;
628932SDina.Nimeh@Sun.COM va_list alist;
638932SDina.Nimeh@Sun.COM
648932SDina.Nimeh@Sun.COM va_start(alist, oflag);
658932SDina.Nimeh@Sun.COM pmode = va_arg(alist, mode_t);
668932SDina.Nimeh@Sun.COM va_end(alist);
676104Sdinak
686104Sdinak do {
698932SDina.Nimeh@Sun.COM if ((fd = open(path, oflag, pmode)) >= 0) {
706104Sdinak (void) fcntl(fd, F_SETFD, FD_CLOEXEC);
716104Sdinak break;
726104Sdinak }
736104Sdinak /* errno definitely set by failed open() */
746104Sdinak } while (errno == EINTR);
756104Sdinak return (fd);
766104Sdinak }
776104Sdinak
786104Sdinak /*
796104Sdinak * Equivalent of read(2) insulated from EINTR.
806104Sdinak */
818932SDina.Nimeh@Sun.COM ssize_t
readn_nointr(int fd,void * dbuf,size_t dlen)828932SDina.Nimeh@Sun.COM readn_nointr(int fd, void *dbuf, size_t dlen)
836104Sdinak {
846104Sdinak char *marker = dbuf;
856104Sdinak size_t left = dlen;
866104Sdinak ssize_t nread = 0, err;
876104Sdinak
886104Sdinak for (err = 0; left > 0 && nread != -1; marker += nread, left -= nread) {
896104Sdinak if ((nread = read(fd, marker, left)) < 0) {
906104Sdinak if (errno == EINTR) { /* keep trying */
916104Sdinak nread = 0;
926104Sdinak continue;
936104Sdinak }
946104Sdinak err = nread; /* hard error */
956104Sdinak break;
968932SDina.Nimeh@Sun.COM } else if (nread == 0) {
978932SDina.Nimeh@Sun.COM break;
988932SDina.Nimeh@Sun.COM }
998932SDina.Nimeh@Sun.COM }
1008932SDina.Nimeh@Sun.COM return (err != 0 ? err : dlen - left);
1018932SDina.Nimeh@Sun.COM }
1028932SDina.Nimeh@Sun.COM
1038932SDina.Nimeh@Sun.COM /*
1048932SDina.Nimeh@Sun.COM * Equivalent of write(2) insulated from EINTR.
1058932SDina.Nimeh@Sun.COM */
1068932SDina.Nimeh@Sun.COM ssize_t
writen_nointr(int fd,void * dbuf,size_t dlen)1078932SDina.Nimeh@Sun.COM writen_nointr(int fd, void *dbuf, size_t dlen)
1088932SDina.Nimeh@Sun.COM {
1098932SDina.Nimeh@Sun.COM char *marker = dbuf;
1108932SDina.Nimeh@Sun.COM size_t left = dlen;
1118932SDina.Nimeh@Sun.COM ssize_t nwrite = 0, err;
1128932SDina.Nimeh@Sun.COM
1138932SDina.Nimeh@Sun.COM for (err = 0; left > 0 && nwrite != -1; marker += nwrite,
1148932SDina.Nimeh@Sun.COM left -= nwrite) {
1158932SDina.Nimeh@Sun.COM if ((nwrite = write(fd, marker, left)) < 0) {
1168932SDina.Nimeh@Sun.COM if (errno == EINTR) { /* keep trying */
1178932SDina.Nimeh@Sun.COM nwrite = 0;
1188932SDina.Nimeh@Sun.COM continue;
1198932SDina.Nimeh@Sun.COM }
1208932SDina.Nimeh@Sun.COM err = nwrite; /* hard error */
1218932SDina.Nimeh@Sun.COM break;
1228932SDina.Nimeh@Sun.COM } else if (nwrite == 0) {
1238932SDina.Nimeh@Sun.COM break;
1246104Sdinak }
1256104Sdinak }
1266104Sdinak return (err != 0 ? err : dlen - left);
1276104Sdinak }
1286104Sdinak
1296104Sdinak /*
1306104Sdinak * Opens the random number generator devices if not already open.
1316104Sdinak * Always returns the opened fd of the device, or error.
1326104Sdinak */
1339127SDina.Nimeh@Sun.COM static int
pkcs11_open_common(int * fd,pthread_mutex_t * mtx,const char * dev,int oflag)1349127SDina.Nimeh@Sun.COM pkcs11_open_common(int *fd, pthread_mutex_t *mtx, const char *dev, int oflag)
1359127SDina.Nimeh@Sun.COM {
136*12657SDina.Nimeh@Sun.COM (void) pthread_mutex_lock(mtx);
137*12657SDina.Nimeh@Sun.COM if (*fd < 0)
138*12657SDina.Nimeh@Sun.COM *fd = open_nointr(dev, oflag);
139*12657SDina.Nimeh@Sun.COM (void) pthread_mutex_unlock(mtx);
140*12657SDina.Nimeh@Sun.COM
1419127SDina.Nimeh@Sun.COM return (*fd);
1429127SDina.Nimeh@Sun.COM }
1439127SDina.Nimeh@Sun.COM
1449127SDina.Nimeh@Sun.COM static int
pkcs11_open_random(void)1456104Sdinak pkcs11_open_random(void)
1466104Sdinak {
1479127SDina.Nimeh@Sun.COM return (pkcs11_open_common(&random_fd, &random_mutex,
1489127SDina.Nimeh@Sun.COM RANDOM_DEVICE, O_RDONLY));
1499127SDina.Nimeh@Sun.COM }
1509127SDina.Nimeh@Sun.COM
1519127SDina.Nimeh@Sun.COM static int
pkcs11_open_urandom(void)1529127SDina.Nimeh@Sun.COM pkcs11_open_urandom(void)
1539127SDina.Nimeh@Sun.COM {
1549127SDina.Nimeh@Sun.COM return (pkcs11_open_common(&urandom_fd, &urandom_mutex,
1559127SDina.Nimeh@Sun.COM URANDOM_DEVICE, O_RDONLY));
1566104Sdinak }
1576104Sdinak
1589127SDina.Nimeh@Sun.COM static int
pkcs11_open_random_seed(void)1599127SDina.Nimeh@Sun.COM pkcs11_open_random_seed(void)
1606104Sdinak {
1619127SDina.Nimeh@Sun.COM return (pkcs11_open_common(&random_seed_fd, &random_seed_mutex,
1629127SDina.Nimeh@Sun.COM RANDOM_DEVICE, O_WRONLY));
1639127SDina.Nimeh@Sun.COM }
1649127SDina.Nimeh@Sun.COM
1659127SDina.Nimeh@Sun.COM static int
pkcs11_open_urandom_seed(void)1669127SDina.Nimeh@Sun.COM pkcs11_open_urandom_seed(void)
1679127SDina.Nimeh@Sun.COM {
1689127SDina.Nimeh@Sun.COM return (pkcs11_open_common(&urandom_seed_fd, &urandom_seed_mutex,
1699127SDina.Nimeh@Sun.COM URANDOM_DEVICE, O_WRONLY));
1706104Sdinak }
1716104Sdinak
1726104Sdinak /*
1736104Sdinak * Close the random number generator devices if already open.
1746104Sdinak */
1759127SDina.Nimeh@Sun.COM static void
pkcs11_close_common(int * fd,pthread_mutex_t * mtx)1769127SDina.Nimeh@Sun.COM pkcs11_close_common(int *fd, pthread_mutex_t *mtx)
1779127SDina.Nimeh@Sun.COM {
1789127SDina.Nimeh@Sun.COM (void) pthread_mutex_lock(mtx);
1799127SDina.Nimeh@Sun.COM (void) close(*fd);
1809127SDina.Nimeh@Sun.COM *fd = -1;
1819127SDina.Nimeh@Sun.COM (void) pthread_mutex_unlock(mtx);
1829127SDina.Nimeh@Sun.COM }
1839127SDina.Nimeh@Sun.COM
1846104Sdinak void
pkcs11_close_random(void)1856104Sdinak pkcs11_close_random(void)
1866104Sdinak {
1879127SDina.Nimeh@Sun.COM pkcs11_close_common(&random_fd, &random_mutex);
1886104Sdinak }
1896104Sdinak
1906104Sdinak void
pkcs11_close_urandom(void)1916104Sdinak pkcs11_close_urandom(void)
1926104Sdinak {
1939127SDina.Nimeh@Sun.COM pkcs11_close_common(&urandom_fd, &urandom_mutex);
1949127SDina.Nimeh@Sun.COM }
1959127SDina.Nimeh@Sun.COM
1969127SDina.Nimeh@Sun.COM static void
pkcs11_close_random_seed(void)1979127SDina.Nimeh@Sun.COM pkcs11_close_random_seed(void)
1989127SDina.Nimeh@Sun.COM {
1999127SDina.Nimeh@Sun.COM pkcs11_close_common(&random_seed_fd, &random_seed_mutex);
2009127SDina.Nimeh@Sun.COM }
2019127SDina.Nimeh@Sun.COM
2029127SDina.Nimeh@Sun.COM void
pkcs11_close_urandom_seed(void)2039127SDina.Nimeh@Sun.COM pkcs11_close_urandom_seed(void)
2049127SDina.Nimeh@Sun.COM {
2059127SDina.Nimeh@Sun.COM pkcs11_close_common(&urandom_seed_fd, &urandom_seed_mutex);
2069127SDina.Nimeh@Sun.COM }
2079127SDina.Nimeh@Sun.COM
2089127SDina.Nimeh@Sun.COM /*
209*12657SDina.Nimeh@Sun.COM * Read from the random number generator devices.
210*12657SDina.Nimeh@Sun.COM */
211*12657SDina.Nimeh@Sun.COM static size_t
pkcs11_read_common(int * fd,pthread_mutex_t * mtx,void * dbuf,size_t dlen)212*12657SDina.Nimeh@Sun.COM pkcs11_read_common(int *fd, pthread_mutex_t *mtx, void *dbuf, size_t dlen)
213*12657SDina.Nimeh@Sun.COM {
214*12657SDina.Nimeh@Sun.COM size_t n;
215*12657SDina.Nimeh@Sun.COM
216*12657SDina.Nimeh@Sun.COM (void) pthread_mutex_lock(mtx);
217*12657SDina.Nimeh@Sun.COM n = readn_nointr(*fd, dbuf, dlen);
218*12657SDina.Nimeh@Sun.COM (void) pthread_mutex_unlock(mtx);
219*12657SDina.Nimeh@Sun.COM
220*12657SDina.Nimeh@Sun.COM return (n);
221*12657SDina.Nimeh@Sun.COM }
222*12657SDina.Nimeh@Sun.COM
223*12657SDina.Nimeh@Sun.COM static size_t
pkcs11_read_random(void * dbuf,size_t dlen)224*12657SDina.Nimeh@Sun.COM pkcs11_read_random(void *dbuf, size_t dlen)
225*12657SDina.Nimeh@Sun.COM {
226*12657SDina.Nimeh@Sun.COM return (pkcs11_read_common(&random_fd, &random_mutex, dbuf, dlen));
227*12657SDina.Nimeh@Sun.COM }
228*12657SDina.Nimeh@Sun.COM
229*12657SDina.Nimeh@Sun.COM static size_t
pkcs11_read_urandom(void * dbuf,size_t dlen)230*12657SDina.Nimeh@Sun.COM pkcs11_read_urandom(void *dbuf, size_t dlen)
231*12657SDina.Nimeh@Sun.COM {
232*12657SDina.Nimeh@Sun.COM return (pkcs11_read_common(&urandom_fd, &urandom_mutex, dbuf, dlen));
233*12657SDina.Nimeh@Sun.COM }
234*12657SDina.Nimeh@Sun.COM
235*12657SDina.Nimeh@Sun.COM /*
236*12657SDina.Nimeh@Sun.COM * Write to the random number generator devices.
237*12657SDina.Nimeh@Sun.COM */
238*12657SDina.Nimeh@Sun.COM static size_t
pkcs11_write_common(int * fd,pthread_mutex_t * mtx,void * dbuf,size_t dlen)239*12657SDina.Nimeh@Sun.COM pkcs11_write_common(int *fd, pthread_mutex_t *mtx, void *dbuf, size_t dlen)
240*12657SDina.Nimeh@Sun.COM {
241*12657SDina.Nimeh@Sun.COM size_t n;
242*12657SDina.Nimeh@Sun.COM
243*12657SDina.Nimeh@Sun.COM (void) pthread_mutex_lock(mtx);
244*12657SDina.Nimeh@Sun.COM n = writen_nointr(*fd, dbuf, dlen);
245*12657SDina.Nimeh@Sun.COM (void) pthread_mutex_unlock(mtx);
246*12657SDina.Nimeh@Sun.COM
247*12657SDina.Nimeh@Sun.COM return (n);
248*12657SDina.Nimeh@Sun.COM }
249*12657SDina.Nimeh@Sun.COM
250*12657SDina.Nimeh@Sun.COM static size_t
pkcs11_write_random_seed(void * dbuf,size_t dlen)251*12657SDina.Nimeh@Sun.COM pkcs11_write_random_seed(void *dbuf, size_t dlen)
252*12657SDina.Nimeh@Sun.COM {
253*12657SDina.Nimeh@Sun.COM return (pkcs11_write_common(&random_seed_fd, &random_seed_mutex,
254*12657SDina.Nimeh@Sun.COM dbuf, dlen));
255*12657SDina.Nimeh@Sun.COM }
256*12657SDina.Nimeh@Sun.COM
257*12657SDina.Nimeh@Sun.COM static size_t
pkcs11_write_urandom_seed(void * dbuf,size_t dlen)258*12657SDina.Nimeh@Sun.COM pkcs11_write_urandom_seed(void *dbuf, size_t dlen)
259*12657SDina.Nimeh@Sun.COM {
260*12657SDina.Nimeh@Sun.COM return (pkcs11_write_common(&urandom_seed_fd, &urandom_seed_mutex,
261*12657SDina.Nimeh@Sun.COM dbuf, dlen));
262*12657SDina.Nimeh@Sun.COM }
263*12657SDina.Nimeh@Sun.COM
264*12657SDina.Nimeh@Sun.COM /*
2659127SDina.Nimeh@Sun.COM * Seed /dev/random with the data in the buffer.
2669127SDina.Nimeh@Sun.COM */
2679127SDina.Nimeh@Sun.COM int
pkcs11_seed_random(void * sbuf,size_t slen)2689127SDina.Nimeh@Sun.COM pkcs11_seed_random(void *sbuf, size_t slen)
2699127SDina.Nimeh@Sun.COM {
270*12657SDina.Nimeh@Sun.COM int rv;
271*12657SDina.Nimeh@Sun.COM
2729127SDina.Nimeh@Sun.COM if (sbuf == NULL || slen == 0)
2739127SDina.Nimeh@Sun.COM return (0);
2749127SDina.Nimeh@Sun.COM
2759127SDina.Nimeh@Sun.COM /* Seeding error could mean it's not supported (errno = EACCES) */
2769127SDina.Nimeh@Sun.COM if (pkcs11_open_random_seed() < 0)
2779127SDina.Nimeh@Sun.COM return (-1);
2789127SDina.Nimeh@Sun.COM
279*12657SDina.Nimeh@Sun.COM rv = -1;
280*12657SDina.Nimeh@Sun.COM if (pkcs11_write_random_seed(sbuf, slen) == slen)
281*12657SDina.Nimeh@Sun.COM rv = 0;
282*12657SDina.Nimeh@Sun.COM
283*12657SDina.Nimeh@Sun.COM pkcs11_close_random_seed();
284*12657SDina.Nimeh@Sun.COM return (rv);
2859127SDina.Nimeh@Sun.COM }
2869127SDina.Nimeh@Sun.COM
2879127SDina.Nimeh@Sun.COM /*
2889127SDina.Nimeh@Sun.COM * Seed /dev/urandom with the data in the buffer.
2899127SDina.Nimeh@Sun.COM */
2909127SDina.Nimeh@Sun.COM int
pkcs11_seed_urandom(void * sbuf,size_t slen)2919127SDina.Nimeh@Sun.COM pkcs11_seed_urandom(void *sbuf, size_t slen)
2929127SDina.Nimeh@Sun.COM {
293*12657SDina.Nimeh@Sun.COM int rv;
294*12657SDina.Nimeh@Sun.COM
2959127SDina.Nimeh@Sun.COM if (sbuf == NULL || slen == 0)
2969127SDina.Nimeh@Sun.COM return (0);
2979127SDina.Nimeh@Sun.COM
2989127SDina.Nimeh@Sun.COM /* Seeding error could mean it's not supported (errno = EACCES) */
2999127SDina.Nimeh@Sun.COM if (pkcs11_open_urandom_seed() < 0)
3009127SDina.Nimeh@Sun.COM return (-1);
3019127SDina.Nimeh@Sun.COM
302*12657SDina.Nimeh@Sun.COM rv = -1;
303*12657SDina.Nimeh@Sun.COM if (pkcs11_write_urandom_seed(sbuf, slen) == slen)
304*12657SDina.Nimeh@Sun.COM rv = 0;
305*12657SDina.Nimeh@Sun.COM
306*12657SDina.Nimeh@Sun.COM pkcs11_close_urandom_seed();
307*12657SDina.Nimeh@Sun.COM return (rv);
3089127SDina.Nimeh@Sun.COM }
3099127SDina.Nimeh@Sun.COM
3109127SDina.Nimeh@Sun.COM /*
3119127SDina.Nimeh@Sun.COM * Put the requested amount of random data into a preallocated buffer.
3129127SDina.Nimeh@Sun.COM * Good for token key data, persistent objects.
3139127SDina.Nimeh@Sun.COM */
3149127SDina.Nimeh@Sun.COM int
pkcs11_get_random(void * dbuf,size_t dlen)3159127SDina.Nimeh@Sun.COM pkcs11_get_random(void *dbuf, size_t dlen)
3169127SDina.Nimeh@Sun.COM {
3179127SDina.Nimeh@Sun.COM if (dbuf == NULL || dlen == 0)
3189127SDina.Nimeh@Sun.COM return (0);
3199127SDina.Nimeh@Sun.COM
3209127SDina.Nimeh@Sun.COM /* Read random data directly from /dev/random */
3219127SDina.Nimeh@Sun.COM if (pkcs11_open_random() < 0)
3229127SDina.Nimeh@Sun.COM return (-1);
3239127SDina.Nimeh@Sun.COM
324*12657SDina.Nimeh@Sun.COM if (pkcs11_read_random(dbuf, dlen) == dlen)
3259127SDina.Nimeh@Sun.COM return (0);
3269127SDina.Nimeh@Sun.COM return (-1);
3276104Sdinak }
3285252Sdinak
3295252Sdinak /*
3305252Sdinak * Put the requested amount of random data into a preallocated buffer.
3315252Sdinak * Good for passphrase salts, initialization vectors.
3325252Sdinak */
3335252Sdinak int
pkcs11_get_urandom(void * dbuf,size_t dlen)3349127SDina.Nimeh@Sun.COM pkcs11_get_urandom(void *dbuf, size_t dlen)
3355252Sdinak {
3365252Sdinak if (dbuf == NULL || dlen == 0)
3375252Sdinak return (0);
3385252Sdinak
3395252Sdinak /* Read random data directly from /dev/urandom */
3406104Sdinak if (pkcs11_open_urandom() < 0)
3416104Sdinak return (-1);
3426104Sdinak
343*12657SDina.Nimeh@Sun.COM if (pkcs11_read_urandom(dbuf, dlen) == dlen)
3446104Sdinak return (0);
3455252Sdinak return (-1);
3465252Sdinak }
3476104Sdinak
3486104Sdinak /*
3499127SDina.Nimeh@Sun.COM * Same as pkcs11_get_urandom but ensures non zero data.
3506104Sdinak */
3516104Sdinak int
pkcs11_get_nzero_urandom(void * dbuf,size_t dlen)3529127SDina.Nimeh@Sun.COM pkcs11_get_nzero_urandom(void *dbuf, size_t dlen)
3536104Sdinak {
3546104Sdinak char extrarand[32];
3556104Sdinak size_t bytesleft = 0;
3566104Sdinak size_t i = 0;
3576104Sdinak
3586104Sdinak /* Start with some random data */
3599127SDina.Nimeh@Sun.COM if (pkcs11_get_urandom(dbuf, dlen) < 0)
3606104Sdinak return (-1);
3616104Sdinak
3626104Sdinak /* Walk through data replacing any 0 bytes with more random data */
3636104Sdinak while (i < dlen) {
3646104Sdinak if (((char *)dbuf)[i] != 0) {
3656104Sdinak i++;
3666104Sdinak continue;
3676104Sdinak }
3686104Sdinak
3696104Sdinak if (bytesleft == 0) {
3706104Sdinak bytesleft = sizeof (extrarand);
3719127SDina.Nimeh@Sun.COM if (pkcs11_get_urandom(extrarand, bytesleft) < 0)
3726104Sdinak return (-1);
3736104Sdinak }
3746104Sdinak bytesleft--;
3756104Sdinak
3766104Sdinak ((char *)dbuf)[i] = extrarand[bytesleft];
3776104Sdinak }
3786104Sdinak return (0);
3796104Sdinak }
380