11c9bd843Sdinak /* 21c9bd843Sdinak * CDDL HEADER START 31c9bd843Sdinak * 41c9bd843Sdinak * The contents of this file are subject to the terms of the 51c9bd843Sdinak * Common Development and Distribution License (the "License"). 61c9bd843Sdinak * You may not use this file except in compliance with the License. 71c9bd843Sdinak * 81c9bd843Sdinak * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 91c9bd843Sdinak * or http://www.opensolaris.org/os/licensing. 101c9bd843Sdinak * See the License for the specific language governing permissions 111c9bd843Sdinak * and limitations under the License. 121c9bd843Sdinak * 131c9bd843Sdinak * When distributing Covered Code, include this CDDL HEADER in each 141c9bd843Sdinak * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 151c9bd843Sdinak * If applicable, add the following below this CDDL HEADER, with the 161c9bd843Sdinak * fields enclosed by brackets "[]" replaced with your own identifying 171c9bd843Sdinak * information: Portions Copyright [yyyy] [name of copyright owner] 181c9bd843Sdinak * 191c9bd843Sdinak * CDDL HEADER END 201c9bd843Sdinak */ 211c9bd843Sdinak /* 2219193bb6SDina K Nimeh * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 231c9bd843Sdinak * Use is subject to license terms. 241c9bd843Sdinak */ 251c9bd843Sdinak 261c9bd843Sdinak #include <stdio.h> 2726ff1ce9Sdinak #include <unistd.h> 2826ff1ce9Sdinak #include <errno.h> 291c9bd843Sdinak #include <string.h> 301c9bd843Sdinak #include <fcntl.h> 311c9bd843Sdinak #include <locale.h> 3219193bb6SDina K Nimeh #include <stdarg.h> 331c9bd843Sdinak #include <cryptoutil.h> 3426ff1ce9Sdinak #include <pthread.h> 3526ff1ce9Sdinak 36*7b79d846SDina K Nimeh 3726ff1ce9Sdinak static pthread_mutex_t random_mutex = PTHREAD_MUTEX_INITIALIZER; 3826ff1ce9Sdinak static pthread_mutex_t urandom_mutex = PTHREAD_MUTEX_INITIALIZER; 3926ff1ce9Sdinak 40*7b79d846SDina K Nimeh static pthread_mutex_t random_seed_mutex = PTHREAD_MUTEX_INITIALIZER; 41*7b79d846SDina K Nimeh static pthread_mutex_t urandom_seed_mutex = PTHREAD_MUTEX_INITIALIZER; 4226ff1ce9Sdinak 4326ff1ce9Sdinak #define RANDOM_DEVICE "/dev/random" /* random device name */ 4426ff1ce9Sdinak #define URANDOM_DEVICE "/dev/urandom" /* urandom device name */ 4526ff1ce9Sdinak 4626ff1ce9Sdinak static int random_fd = -1; 4726ff1ce9Sdinak static int urandom_fd = -1; 4826ff1ce9Sdinak 49*7b79d846SDina K Nimeh static int random_seed_fd = -1; 50*7b79d846SDina K Nimeh static int urandom_seed_fd = -1; 51*7b79d846SDina K Nimeh 52*7b79d846SDina K Nimeh 5326ff1ce9Sdinak /* 5426ff1ce9Sdinak * Equivalent of open(2) insulated from EINTR. 5526ff1ce9Sdinak * Also sets close-on-exec. 5626ff1ce9Sdinak */ 5719193bb6SDina K Nimeh int 5819193bb6SDina K Nimeh open_nointr(const char *path, int oflag, ...) 5926ff1ce9Sdinak { 6026ff1ce9Sdinak int fd; 6119193bb6SDina K Nimeh mode_t pmode; 6219193bb6SDina K Nimeh va_list alist; 6319193bb6SDina K Nimeh 6419193bb6SDina K Nimeh va_start(alist, oflag); 6519193bb6SDina K Nimeh pmode = va_arg(alist, mode_t); 6619193bb6SDina K Nimeh va_end(alist); 6726ff1ce9Sdinak 6826ff1ce9Sdinak do { 6919193bb6SDina K Nimeh if ((fd = open(path, oflag, pmode)) >= 0) { 7026ff1ce9Sdinak (void) fcntl(fd, F_SETFD, FD_CLOEXEC); 7126ff1ce9Sdinak break; 7226ff1ce9Sdinak } 7326ff1ce9Sdinak /* errno definitely set by failed open() */ 7426ff1ce9Sdinak } while (errno == EINTR); 7526ff1ce9Sdinak return (fd); 7626ff1ce9Sdinak } 7726ff1ce9Sdinak 7826ff1ce9Sdinak /* 7926ff1ce9Sdinak * Equivalent of read(2) insulated from EINTR. 8026ff1ce9Sdinak */ 8119193bb6SDina K Nimeh ssize_t 8219193bb6SDina K Nimeh readn_nointr(int fd, void *dbuf, size_t dlen) 8326ff1ce9Sdinak { 8426ff1ce9Sdinak char *marker = dbuf; 8526ff1ce9Sdinak size_t left = dlen; 8626ff1ce9Sdinak ssize_t nread = 0, err; 8726ff1ce9Sdinak 8826ff1ce9Sdinak for (err = 0; left > 0 && nread != -1; marker += nread, left -= nread) { 8926ff1ce9Sdinak if ((nread = read(fd, marker, left)) < 0) { 9026ff1ce9Sdinak if (errno == EINTR) { /* keep trying */ 9126ff1ce9Sdinak nread = 0; 9226ff1ce9Sdinak continue; 9326ff1ce9Sdinak } 9426ff1ce9Sdinak err = nread; /* hard error */ 9526ff1ce9Sdinak break; 9619193bb6SDina K Nimeh } else if (nread == 0) { 9719193bb6SDina K Nimeh break; 9819193bb6SDina K Nimeh } 9919193bb6SDina K Nimeh } 10019193bb6SDina K Nimeh return (err != 0 ? err : dlen - left); 10119193bb6SDina K Nimeh } 10219193bb6SDina K Nimeh 10319193bb6SDina K Nimeh /* 10419193bb6SDina K Nimeh * Equivalent of write(2) insulated from EINTR. 10519193bb6SDina K Nimeh */ 10619193bb6SDina K Nimeh ssize_t 10719193bb6SDina K Nimeh writen_nointr(int fd, void *dbuf, size_t dlen) 10819193bb6SDina K Nimeh { 10919193bb6SDina K Nimeh char *marker = dbuf; 11019193bb6SDina K Nimeh size_t left = dlen; 11119193bb6SDina K Nimeh ssize_t nwrite = 0, err; 11219193bb6SDina K Nimeh 11319193bb6SDina K Nimeh for (err = 0; left > 0 && nwrite != -1; marker += nwrite, 11419193bb6SDina K Nimeh left -= nwrite) { 11519193bb6SDina K Nimeh if ((nwrite = write(fd, marker, left)) < 0) { 11619193bb6SDina K Nimeh if (errno == EINTR) { /* keep trying */ 11719193bb6SDina K Nimeh nwrite = 0; 11819193bb6SDina K Nimeh continue; 11919193bb6SDina K Nimeh } 12019193bb6SDina K Nimeh err = nwrite; /* hard error */ 12119193bb6SDina K Nimeh break; 12219193bb6SDina K Nimeh } else if (nwrite == 0) { 12319193bb6SDina K Nimeh break; 12426ff1ce9Sdinak } 12526ff1ce9Sdinak } 12626ff1ce9Sdinak return (err != 0 ? err : dlen - left); 12726ff1ce9Sdinak } 12826ff1ce9Sdinak 12926ff1ce9Sdinak /* 13026ff1ce9Sdinak * Opens the random number generator devices if not already open. 13126ff1ce9Sdinak * Always returns the opened fd of the device, or error. 13226ff1ce9Sdinak */ 133*7b79d846SDina K Nimeh static int 134*7b79d846SDina K Nimeh pkcs11_open_common(int *fd, pthread_mutex_t *mtx, const char *dev, int oflag) 13526ff1ce9Sdinak { 136*7b79d846SDina K Nimeh if (*fd < 0) { 137*7b79d846SDina K Nimeh (void) pthread_mutex_lock(mtx); 138*7b79d846SDina K Nimeh if (*fd < 0) 139*7b79d846SDina K Nimeh *fd = open_nointr(dev, oflag); 140*7b79d846SDina K Nimeh (void) pthread_mutex_unlock(mtx); 141*7b79d846SDina K Nimeh } 142*7b79d846SDina K Nimeh return (*fd); 14326ff1ce9Sdinak } 14426ff1ce9Sdinak 145*7b79d846SDina K Nimeh static int 146*7b79d846SDina K Nimeh pkcs11_open_random(void) 147*7b79d846SDina K Nimeh { 148*7b79d846SDina K Nimeh return (pkcs11_open_common(&random_fd, &random_mutex, 149*7b79d846SDina K Nimeh RANDOM_DEVICE, O_RDONLY)); 150*7b79d846SDina K Nimeh } 151*7b79d846SDina K Nimeh 152*7b79d846SDina K Nimeh static int 15326ff1ce9Sdinak pkcs11_open_urandom(void) 15426ff1ce9Sdinak { 155*7b79d846SDina K Nimeh return (pkcs11_open_common(&urandom_fd, &urandom_mutex, 156*7b79d846SDina K Nimeh URANDOM_DEVICE, O_RDONLY)); 157*7b79d846SDina K Nimeh } 158*7b79d846SDina K Nimeh 159*7b79d846SDina K Nimeh static int 160*7b79d846SDina K Nimeh pkcs11_open_random_seed(void) 161*7b79d846SDina K Nimeh { 162*7b79d846SDina K Nimeh return (pkcs11_open_common(&random_seed_fd, &random_seed_mutex, 163*7b79d846SDina K Nimeh RANDOM_DEVICE, O_WRONLY)); 164*7b79d846SDina K Nimeh } 165*7b79d846SDina K Nimeh 166*7b79d846SDina K Nimeh static int 167*7b79d846SDina K Nimeh pkcs11_open_urandom_seed(void) 168*7b79d846SDina K Nimeh { 169*7b79d846SDina K Nimeh return (pkcs11_open_common(&urandom_seed_fd, &urandom_seed_mutex, 170*7b79d846SDina K Nimeh URANDOM_DEVICE, O_WRONLY)); 17126ff1ce9Sdinak } 17226ff1ce9Sdinak 17326ff1ce9Sdinak /* 17426ff1ce9Sdinak * Close the random number generator devices if already open. 17526ff1ce9Sdinak */ 176*7b79d846SDina K Nimeh static void 177*7b79d846SDina K Nimeh pkcs11_close_common(int *fd, pthread_mutex_t *mtx) 178*7b79d846SDina K Nimeh { 179*7b79d846SDina K Nimeh if (*fd < 0) 180*7b79d846SDina K Nimeh return; 181*7b79d846SDina K Nimeh (void) pthread_mutex_lock(mtx); 182*7b79d846SDina K Nimeh (void) close(*fd); 183*7b79d846SDina K Nimeh *fd = -1; 184*7b79d846SDina K Nimeh (void) pthread_mutex_unlock(mtx); 185*7b79d846SDina K Nimeh } 186*7b79d846SDina K Nimeh 18726ff1ce9Sdinak void 18826ff1ce9Sdinak pkcs11_close_random(void) 18926ff1ce9Sdinak { 190*7b79d846SDina K Nimeh pkcs11_close_common(&random_fd, &random_mutex); 19126ff1ce9Sdinak } 19226ff1ce9Sdinak 19326ff1ce9Sdinak void 19426ff1ce9Sdinak pkcs11_close_urandom(void) 19526ff1ce9Sdinak { 196*7b79d846SDina K Nimeh pkcs11_close_common(&urandom_fd, &urandom_mutex); 197*7b79d846SDina K Nimeh } 198*7b79d846SDina K Nimeh 199*7b79d846SDina K Nimeh static void 200*7b79d846SDina K Nimeh pkcs11_close_random_seed(void) 201*7b79d846SDina K Nimeh { 202*7b79d846SDina K Nimeh pkcs11_close_common(&random_seed_fd, &random_seed_mutex); 203*7b79d846SDina K Nimeh } 204*7b79d846SDina K Nimeh 205*7b79d846SDina K Nimeh void 206*7b79d846SDina K Nimeh pkcs11_close_urandom_seed(void) 207*7b79d846SDina K Nimeh { 208*7b79d846SDina K Nimeh pkcs11_close_common(&urandom_seed_fd, &urandom_seed_mutex); 209*7b79d846SDina K Nimeh } 210*7b79d846SDina K Nimeh 211*7b79d846SDina K Nimeh /* 212*7b79d846SDina K Nimeh * Seed /dev/random with the data in the buffer. 213*7b79d846SDina K Nimeh */ 214*7b79d846SDina K Nimeh int 215*7b79d846SDina K Nimeh pkcs11_seed_random(void *sbuf, size_t slen) 216*7b79d846SDina K Nimeh { 217*7b79d846SDina K Nimeh if (sbuf == NULL || slen == 0) 218*7b79d846SDina K Nimeh return (0); 219*7b79d846SDina K Nimeh 220*7b79d846SDina K Nimeh /* Seeding error could mean it's not supported (errno = EACCES) */ 221*7b79d846SDina K Nimeh if (pkcs11_open_random_seed() < 0) 222*7b79d846SDina K Nimeh return (-1); 223*7b79d846SDina K Nimeh 224*7b79d846SDina K Nimeh if (writen_nointr(random_seed_fd, sbuf, slen) == slen) { 225*7b79d846SDina K Nimeh pkcs11_close_random_seed(); 226*7b79d846SDina K Nimeh return (0); 227*7b79d846SDina K Nimeh } 228*7b79d846SDina K Nimeh return (-1); 229*7b79d846SDina K Nimeh } 230*7b79d846SDina K Nimeh 231*7b79d846SDina K Nimeh /* 232*7b79d846SDina K Nimeh * Seed /dev/urandom with the data in the buffer. 233*7b79d846SDina K Nimeh */ 234*7b79d846SDina K Nimeh int 235*7b79d846SDina K Nimeh pkcs11_seed_urandom(void *sbuf, size_t slen) 236*7b79d846SDina K Nimeh { 237*7b79d846SDina K Nimeh if (sbuf == NULL || slen == 0) 238*7b79d846SDina K Nimeh return (0); 239*7b79d846SDina K Nimeh 240*7b79d846SDina K Nimeh /* Seeding error could mean it's not supported (errno = EACCES) */ 241*7b79d846SDina K Nimeh if (pkcs11_open_urandom_seed() < 0) 242*7b79d846SDina K Nimeh return (-1); 243*7b79d846SDina K Nimeh 244*7b79d846SDina K Nimeh if (writen_nointr(urandom_seed_fd, sbuf, slen) == slen) { 245*7b79d846SDina K Nimeh pkcs11_close_urandom_seed(); 246*7b79d846SDina K Nimeh return (0); 247*7b79d846SDina K Nimeh } 248*7b79d846SDina K Nimeh return (-1); 249*7b79d846SDina K Nimeh } 250*7b79d846SDina K Nimeh 251*7b79d846SDina K Nimeh /* 252*7b79d846SDina K Nimeh * Put the requested amount of random data into a preallocated buffer. 253*7b79d846SDina K Nimeh * Good for token key data, persistent objects. 254*7b79d846SDina K Nimeh */ 255*7b79d846SDina K Nimeh int 256*7b79d846SDina K Nimeh pkcs11_get_random(void *dbuf, size_t dlen) 257*7b79d846SDina K Nimeh { 258*7b79d846SDina K Nimeh if (dbuf == NULL || dlen == 0) 259*7b79d846SDina K Nimeh return (0); 260*7b79d846SDina K Nimeh 261*7b79d846SDina K Nimeh /* Read random data directly from /dev/random */ 262*7b79d846SDina K Nimeh if (pkcs11_open_random() < 0) 263*7b79d846SDina K Nimeh return (-1); 264*7b79d846SDina K Nimeh 265*7b79d846SDina K Nimeh if (readn_nointr(random_fd, dbuf, dlen) == dlen) 266*7b79d846SDina K Nimeh return (0); 267*7b79d846SDina K Nimeh return (-1); 26826ff1ce9Sdinak } 2691c9bd843Sdinak 2701c9bd843Sdinak /* 2711c9bd843Sdinak * Put the requested amount of random data into a preallocated buffer. 2721c9bd843Sdinak * Good for passphrase salts, initialization vectors. 2731c9bd843Sdinak */ 2741c9bd843Sdinak int 275*7b79d846SDina K Nimeh pkcs11_get_urandom(void *dbuf, size_t dlen) 2761c9bd843Sdinak { 2771c9bd843Sdinak if (dbuf == NULL || dlen == 0) 2781c9bd843Sdinak return (0); 2791c9bd843Sdinak 2801c9bd843Sdinak /* Read random data directly from /dev/urandom */ 28126ff1ce9Sdinak if (pkcs11_open_urandom() < 0) 2821c9bd843Sdinak return (-1); 28326ff1ce9Sdinak 28419193bb6SDina K Nimeh if (readn_nointr(urandom_fd, dbuf, dlen) == dlen) 28526ff1ce9Sdinak return (0); 28626ff1ce9Sdinak return (-1); 28726ff1ce9Sdinak } 28826ff1ce9Sdinak 28926ff1ce9Sdinak /* 290*7b79d846SDina K Nimeh * Same as pkcs11_get_urandom but ensures non zero data. 29126ff1ce9Sdinak */ 29226ff1ce9Sdinak int 293*7b79d846SDina K Nimeh pkcs11_get_nzero_urandom(void *dbuf, size_t dlen) 29426ff1ce9Sdinak { 29526ff1ce9Sdinak char extrarand[32]; 29626ff1ce9Sdinak size_t bytesleft = 0; 29726ff1ce9Sdinak size_t i = 0; 29826ff1ce9Sdinak 29926ff1ce9Sdinak /* Start with some random data */ 300*7b79d846SDina K Nimeh if (pkcs11_get_urandom(dbuf, dlen) < 0) 30126ff1ce9Sdinak return (-1); 30226ff1ce9Sdinak 30326ff1ce9Sdinak /* Walk through data replacing any 0 bytes with more random data */ 30426ff1ce9Sdinak while (i < dlen) { 30526ff1ce9Sdinak if (((char *)dbuf)[i] != 0) { 30626ff1ce9Sdinak i++; 30726ff1ce9Sdinak continue; 30826ff1ce9Sdinak } 30926ff1ce9Sdinak 31026ff1ce9Sdinak if (bytesleft == 0) { 31126ff1ce9Sdinak bytesleft = sizeof (extrarand); 312*7b79d846SDina K Nimeh if (pkcs11_get_urandom(extrarand, bytesleft) < 0) 31326ff1ce9Sdinak return (-1); 31426ff1ce9Sdinak } 31526ff1ce9Sdinak bytesleft--; 31626ff1ce9Sdinak 31726ff1ce9Sdinak ((char *)dbuf)[i] = extrarand[bytesleft]; 31826ff1ce9Sdinak } 31926ff1ce9Sdinak return (0); 3201c9bd843Sdinak } 321