randfile.c revision 194206
155714Skris/* crypto/rand/randfile.c */
255714Skris/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
355714Skris * All rights reserved.
455714Skris *
555714Skris * This package is an SSL implementation written
655714Skris * by Eric Young (eay@cryptsoft.com).
755714Skris * The implementation was written so as to conform with Netscapes SSL.
855714Skris *
955714Skris * This library is free for commercial and non-commercial use as long as
1055714Skris * the following conditions are aheared to.  The following conditions
1155714Skris * apply to all code found in this distribution, be it the RC4, RSA,
1255714Skris * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
1355714Skris * included with this distribution is covered by the same copyright terms
1455714Skris * except that the holder is Tim Hudson (tjh@cryptsoft.com).
1555714Skris *
1655714Skris * Copyright remains Eric Young's, and as such any Copyright notices in
1755714Skris * the code are not to be removed.
1855714Skris * If this package is used in a product, Eric Young should be given attribution
1955714Skris * as the author of the parts of the library used.
2055714Skris * This can be in the form of a textual message at program startup or
2155714Skris * in documentation (online or textual) provided with the package.
2255714Skris *
2355714Skris * Redistribution and use in source and binary forms, with or without
2455714Skris * modification, are permitted provided that the following conditions
2555714Skris * are met:
2655714Skris * 1. Redistributions of source code must retain the copyright
2755714Skris *    notice, this list of conditions and the following disclaimer.
2855714Skris * 2. Redistributions in binary form must reproduce the above copyright
2955714Skris *    notice, this list of conditions and the following disclaimer in the
3055714Skris *    documentation and/or other materials provided with the distribution.
3155714Skris * 3. All advertising materials mentioning features or use of this software
3255714Skris *    must display the following acknowledgement:
3355714Skris *    "This product includes cryptographic software written by
3455714Skris *     Eric Young (eay@cryptsoft.com)"
3555714Skris *    The word 'cryptographic' can be left out if the rouines from the library
3655714Skris *    being used are not cryptographic related :-).
3755714Skris * 4. If you include any Windows specific code (or a derivative thereof) from
3855714Skris *    the apps directory (application code) you must include an acknowledgement:
3955714Skris *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
4055714Skris *
4155714Skris * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
4255714Skris * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
4355714Skris * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
4455714Skris * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
4555714Skris * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
4655714Skris * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
4755714Skris * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
4855714Skris * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
4955714Skris * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
5055714Skris * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
5155714Skris * SUCH DAMAGE.
5255714Skris *
5355714Skris * The licence and distribution terms for any publically available version or
5455714Skris * derivative of this code cannot be changed.  i.e. this code cannot simply be
5555714Skris * copied and put under another distribution licence
5655714Skris * [including the GNU Public Licence.]
5755714Skris */
5855714Skris
59127128Snectar/* We need to define this to get macros like S_IFBLK and S_IFCHR */
60160814Ssimon#define _XOPEN_SOURCE 500
61127128Snectar
6255714Skris#include <errno.h>
6355714Skris#include <stdio.h>
6455714Skris#include <stdlib.h>
6555714Skris#include <string.h>
6655714Skris
67109998Smarkm#include "e_os.h"
68109998Smarkm#include <openssl/crypto.h>
69109998Smarkm#include <openssl/rand.h>
70127128Snectar#include <openssl/buffer.h>
71109998Smarkm
72109998Smarkm#ifdef OPENSSL_SYS_VMS
7359191Skris#include <unixio.h>
7459191Skris#endif
7559191Skris#ifndef NO_SYS_TYPES_H
7659191Skris# include <sys/types.h>
7759191Skris#endif
7859191Skris#ifdef MAC_OS_pre_X
7959191Skris# include <stat.h>
8059191Skris#else
8159191Skris# include <sys/stat.h>
8259191Skris#endif
8359191Skris
84194206Ssimon#ifdef _WIN32
85194206Ssimon#define stat	_stat
86194206Ssimon#define chmod	_chmod
87194206Ssimon#define open	_open
88194206Ssimon#define fdopen	_fdopen
89194206Ssimon#endif
90194206Ssimon
9155714Skris#undef BUFSIZE
9255714Skris#define BUFSIZE	1024
9355714Skris#define RAND_DATA 1024
9455714Skris
95194206Ssimon#ifdef OPENSSL_SYS_VMS
96194206Ssimon/* This declaration is a nasty hack to get around vms' extension to fopen
97194206Ssimon * for passing in sharing options being disabled by our /STANDARD=ANSI89 */
98194206Ssimonstatic FILE *(*const vms_fopen)(const char *, const char *, ...) =
99194206Ssimon    (FILE *(*)(const char *, const char *, ...))fopen;
100194206Ssimon#define VMS_OPEN_ATTRS "shr=get,put,upd,del","ctx=bin,stm","rfm=stm","rat=none","mrs=0"
101194206Ssimon#endif
102194206Ssimon
10359191Skris/* #define RFILE ".rnd" - defined in ../../e_os.h */
10455714Skris
10572613Skris/* Note that these functions are intended for seed files only.
10672613Skris * Entropy devices and EGD sockets are handled in rand_unix.c */
10772613Skris
10855714Skrisint RAND_load_file(const char *file, long bytes)
10955714Skris	{
11059191Skris	/* If bytes >= 0, read up to 'bytes' bytes.
11159191Skris	 * if bytes == -1, read complete file. */
11259191Skris
11355714Skris	MS_STATIC unsigned char buf[BUFSIZE];
11455714Skris	struct stat sb;
11555714Skris	int i,ret=0,n;
11655714Skris	FILE *in;
11755714Skris
11855714Skris	if (file == NULL) return(0);
11955714Skris
120194206Ssimon	if (stat(file,&sb) < 0) return(0);
121160814Ssimon	RAND_add(&sb,sizeof(sb),0.0);
12259191Skris	if (bytes == 0) return(ret);
12355714Skris
124194206Ssimon#ifdef OPENSSL_SYS_VMS
125194206Ssimon	in=vms_fopen(file,"rb",VMS_OPEN_ATTRS);
126194206Ssimon#else
12755714Skris	in=fopen(file,"rb");
128194206Ssimon#endif
12955714Skris	if (in == NULL) goto err;
130127128Snectar#if defined(S_IFBLK) && defined(S_IFCHR)
131127128Snectar	if (sb.st_mode & (S_IFBLK | S_IFCHR)) {
132127128Snectar	  /* this file is a device. we don't want read an infinite number
133127128Snectar	   * of bytes from a random device, nor do we want to use buffered
134127128Snectar	   * I/O because we will waste system entropy.
135127128Snectar	   */
136127128Snectar	  bytes = (bytes == -1) ? 2048 : bytes; /* ok, is 2048 enough? */
137127128Snectar	  setvbuf(in, NULL, _IONBF, 0); /* don't do buffered reads */
138127128Snectar	}
139127128Snectar#endif
14055714Skris	for (;;)
14155714Skris		{
14259191Skris		if (bytes > 0)
14359191Skris			n = (bytes < BUFSIZE)?(int)bytes:BUFSIZE;
14459191Skris		else
14559191Skris			n = BUFSIZE;
14655714Skris		i=fread(buf,1,n,in);
14755714Skris		if (i <= 0) break;
148194206Ssimon#ifdef PURIFY
149194206Ssimon		RAND_add(buf,i,(double)i);
150194206Ssimon#else
15155714Skris		/* even if n != i, use the full array */
152160814Ssimon		RAND_add(buf,n,(double)i);
153194206Ssimon#endif
15455714Skris		ret+=i;
15559191Skris		if (bytes > 0)
15659191Skris			{
15759191Skris			bytes-=n;
15872613Skris			if (bytes <= 0) break;
15959191Skris			}
16055714Skris		}
16155714Skris	fclose(in);
162109998Smarkm	OPENSSL_cleanse(buf,BUFSIZE);
16355714Skriserr:
16455714Skris	return(ret);
16555714Skris	}
16655714Skris
16755714Skrisint RAND_write_file(const char *file)
16855714Skris	{
16955714Skris	unsigned char buf[BUFSIZE];
17068651Skris	int i,ret=0,rand_err=0;
17159191Skris	FILE *out = NULL;
17255714Skris	int n;
173127128Snectar	struct stat sb;
17459191Skris
175127128Snectar	i=stat(file,&sb);
176127128Snectar	if (i != -1) {
177127128Snectar#if defined(S_IFBLK) && defined(S_IFCHR)
178127128Snectar	  if (sb.st_mode & (S_IFBLK | S_IFCHR)) {
179127128Snectar	    /* this file is a device. we don't write back to it.
180127128Snectar	     * we "succeed" on the assumption this is some sort
181127128Snectar	     * of random device. Otherwise attempting to write to
182127128Snectar	     * and chmod the device causes problems.
183127128Snectar	     */
184127128Snectar	    return(1);
185127128Snectar	  }
186127128Snectar#endif
187127128Snectar	}
188127128Snectar
189194206Ssimon#if defined(O_CREAT) && !defined(OPENSSL_SYS_WIN32) && !defined(OPENSSL_SYS_VMS)
190142425Snectar	{
19159191Skris	/* For some reason Win32 can't write to files created this way */
19268651Skris
19368651Skris	/* chmod(..., 0600) is too late to protect the file,
19468651Skris	 * permissions should be restrictive from the start */
19568651Skris	int fd = open(file, O_CREAT, 0600);
19668651Skris	if (fd != -1)
19768651Skris		out = fdopen(fd, "wb");
198142425Snectar	}
19959191Skris#endif
200194206Ssimon
201194206Ssimon#ifdef OPENSSL_SYS_VMS
202194206Ssimon	/* VMS NOTE: Prior versions of this routine created a _new_
203194206Ssimon	 * version of the rand file for each call into this routine, then
204194206Ssimon	 * deleted all existing versions named ;-1, and finally renamed
205194206Ssimon	 * the current version as ';1'. Under concurrent usage, this
206194206Ssimon	 * resulted in an RMS race condition in rename() which could
207194206Ssimon	 * orphan files (see vms message help for RMS$_REENT). With the
208194206Ssimon	 * fopen() calls below, openssl/VMS now shares the top-level
209194206Ssimon	 * version of the rand file. Note that there may still be
210194206Ssimon	 * conditions where the top-level rand file is locked. If so, this
211194206Ssimon	 * code will then create a new version of the rand file. Without
212194206Ssimon	 * the delete and rename code, this can result in ascending file
213194206Ssimon	 * versions that stop at version 32767, and this routine will then
214194206Ssimon	 * return an error. The remedy for this is to recode the calling
215194206Ssimon	 * application to avoid concurrent use of the rand file, or
216194206Ssimon	 * synchronize usage at the application level. Also consider
217194206Ssimon	 * whether or not you NEED a persistent rand file in a concurrent
218194206Ssimon	 * use situation.
219194206Ssimon	 */
220194206Ssimon
221194206Ssimon	out = vms_fopen(file,"rb+",VMS_OPEN_ATTRS);
22268651Skris	if (out == NULL)
223194206Ssimon		out = vms_fopen(file,"wb",VMS_OPEN_ATTRS);
224194206Ssimon#else
225194206Ssimon	if (out == NULL)
22668651Skris		out = fopen(file,"wb");
227194206Ssimon#endif
22868651Skris	if (out == NULL) goto err;
22959191Skris
23059191Skris#ifndef NO_CHMOD
23155714Skris	chmod(file,0600);
23259191Skris#endif
23355714Skris	n=RAND_DATA;
23455714Skris	for (;;)
23555714Skris		{
23655714Skris		i=(n > BUFSIZE)?BUFSIZE:n;
23755714Skris		n-=BUFSIZE;
23859191Skris		if (RAND_bytes(buf,i) <= 0)
23968651Skris			rand_err=1;
24055714Skris		i=fwrite(buf,1,i,out);
24155714Skris		if (i <= 0)
24255714Skris			{
24355714Skris			ret=0;
24455714Skris			break;
24555714Skris			}
24655714Skris		ret+=i;
24755714Skris		if (n <= 0) break;
24859191Skris                }
24959191Skris
25055714Skris	fclose(out);
251109998Smarkm	OPENSSL_cleanse(buf,BUFSIZE);
25255714Skriserr:
25368651Skris	return (rand_err ? -1 : ret);
25455714Skris	}
25555714Skris
25676866Skrisconst char *RAND_file_name(char *buf, size_t size)
25755714Skris	{
25876866Skris	char *s=NULL;
259127128Snectar	int ok = 0;
260127128Snectar#ifdef __OpenBSD__
261127128Snectar	struct stat sb;
262127128Snectar#endif
26355714Skris
26476866Skris	if (OPENSSL_issetugid() == 0)
26576866Skris		s=getenv("RANDFILE");
266127128Snectar	if (s != NULL && *s && strlen(s) + 1 < size)
26755714Skris		{
268127128Snectar		if (BUF_strlcpy(buf,s,size) >= size)
269109998Smarkm			return NULL;
27055714Skris		}
27155714Skris	else
27255714Skris		{
27376866Skris		if (OPENSSL_issetugid() == 0)
27476866Skris			s=getenv("HOME");
27579998Skris#ifdef DEFAULT_HOME
27679998Skris		if (s == NULL)
27779998Skris			{
27879998Skris			s = DEFAULT_HOME;
27979998Skris			}
28079998Skris#endif
281127128Snectar		if (s && *s && strlen(s)+strlen(RFILE)+2 < size)
28276866Skris			{
283127128Snectar			BUF_strlcpy(buf,s,size);
284109998Smarkm#ifndef OPENSSL_SYS_VMS
285127128Snectar			BUF_strlcat(buf,"/",size);
28655714Skris#endif
287127128Snectar			BUF_strlcat(buf,RFILE,size);
288127128Snectar			ok = 1;
28976866Skris			}
29079998Skris		else
29176866Skris		  	buf[0] = '\0'; /* no file name */
29255714Skris		}
293127128Snectar
294127128Snectar#ifdef __OpenBSD__
295127128Snectar	/* given that all random loads just fail if the file can't be
296127128Snectar	 * seen on a stat, we stat the file we're returning, if it
297127128Snectar	 * fails, use /dev/arandom instead. this allows the user to
298127128Snectar	 * use their own source for good random data, but defaults
299127128Snectar	 * to something hopefully decent if that isn't available.
300127128Snectar	 */
301127128Snectar
302127128Snectar	if (!ok)
303127128Snectar		if (BUF_strlcpy(buf,"/dev/arandom",size) >= size) {
304127128Snectar			return(NULL);
305127128Snectar		}
306127128Snectar	if (stat(buf,&sb) == -1)
307127128Snectar		if (BUF_strlcpy(buf,"/dev/arandom",size) >= size) {
308127128Snectar			return(NULL);
309127128Snectar		}
310127128Snectar
311127128Snectar#endif
312127128Snectar	return(buf);
31355714Skris	}
314