1231990Smp/* crypto/rand/randfile.c */
259243Sobrien/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
359243Sobrien * All rights reserved.
459243Sobrien *
559243Sobrien * This package is an SSL implementation written
659243Sobrien * by Eric Young (eay@cryptsoft.com).
759243Sobrien * The implementation was written so as to conform with Netscapes SSL.
859243Sobrien *
959243Sobrien * This library is free for commercial and non-commercial use as long as
1059243Sobrien * the following conditions are aheared to.  The following conditions
1159243Sobrien * apply to all code found in this distribution, be it the RC4, RSA,
1259243Sobrien * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
1359243Sobrien * included with this distribution is covered by the same copyright terms
1459243Sobrien * except that the holder is Tim Hudson (tjh@cryptsoft.com).
1559243Sobrien *
1659243Sobrien * Copyright remains Eric Young's, and as such any Copyright notices in
17100616Smp * the code are not to be removed.
1859243Sobrien * If this package is used in a product, Eric Young should be given attribution
1959243Sobrien * as the author of the parts of the library used.
2059243Sobrien * This can be in the form of a textual message at program startup or
2159243Sobrien * in documentation (online or textual) provided with the package.
2259243Sobrien *
2359243Sobrien * Redistribution and use in source and binary forms, with or without
2459243Sobrien * modification, are permitted provided that the following conditions
2559243Sobrien * are met:
2659243Sobrien * 1. Redistributions of source code must retain the copyright
2759243Sobrien *    notice, this list of conditions and the following disclaimer.
2859243Sobrien * 2. Redistributions in binary form must reproduce the above copyright
2959243Sobrien *    notice, this list of conditions and the following disclaimer in the
3059243Sobrien *    documentation and/or other materials provided with the distribution.
3159243Sobrien * 3. All advertising materials mentioning features or use of this software
3259243Sobrien *    must display the following acknowledgement:
3359243Sobrien *    "This product includes cryptographic software written by
3459243Sobrien *     Eric Young (eay@cryptsoft.com)"
35231990Smp *    The word 'cryptographic' can be left out if the rouines from the library
3659243Sobrien *    being used are not cryptographic related :-).
3759243Sobrien * 4. If you include any Windows specific code (or a derivative thereof) from
3859243Sobrien *    the apps directory (application code) you must include an acknowledgement:
3959243Sobrien *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
4059243Sobrien *
4159243Sobrien * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
4259243Sobrien * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
4359243Sobrien * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
4459243Sobrien * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
4559243Sobrien * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
4659243Sobrien * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
4759243Sobrien * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
4859243Sobrien * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
4959243Sobrien * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
50231990Smp * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
51145479Smp * SUCH DAMAGE.
52167465Smp *
5359243Sobrien * The licence and distribution terms for any publically available version or
54145479Smp * derivative of this code cannot be changed.  i.e. this code cannot simply be
5559243Sobrien * copied and put under another distribution licence
5659243Sobrien * [including the GNU Public Licence.]
5759243Sobrien */
5859243Sobrien
5959243Sobrien/* We need to define this to get macros like S_IFBLK and S_IFCHR */
6059243Sobrien#if !defined(OPENSSL_SYS_VXWORKS)
6159243Sobrien# define _XOPEN_SOURCE 500
6259243Sobrien#endif
6359243Sobrien
6459243Sobrien#include <errno.h>
6559243Sobrien#include <stdio.h>
6659243Sobrien#include <stdlib.h>
6759243Sobrien#include <string.h>
6859243Sobrien
69167465Smp#include "e_os.h"
70167465Smp#include <openssl/crypto.h>
71167465Smp#include <openssl/rand.h>
7259243Sobrien#include <openssl/buffer.h>
73167465Smp
74167465Smp#ifdef OPENSSL_SYS_VMS
75167465Smp# include <unixio.h>
76167465Smp#endif
77167465Smp#ifndef NO_SYS_TYPES_H
78167465Smp# include <sys/types.h>
79167465Smp#endif
80167465Smp#ifndef OPENSSL_NO_POSIX_IO
81167465Smp# include <sys/stat.h>
82167465Smp# include <fcntl.h>
83167465Smp#endif
8459243Sobrien
8559243Sobrien#ifdef _WIN32
8659243Sobrien# define stat    _stat
8759243Sobrien# define chmod   _chmod
8859243Sobrien# define open    _open
8959243Sobrien# define fdopen  _fdopen
90167465Smp#endif
9159243Sobrien
92145479Smp#undef BUFSIZE
93145479Smp#define BUFSIZE 1024
9459243Sobrien#define RAND_DATA 1024
9559243Sobrien
9659243Sobrien#ifdef OPENSSL_SYS_VMS
9759243Sobrien/*
9859243Sobrien * This declaration is a nasty hack to get around vms' extension to fopen for
9959243Sobrien * passing in sharing options being disabled by our /STANDARD=ANSI89
10059243Sobrien */
101167465Smpstatic FILE *(*const vms_fopen)(const char *, const char *, ...) =
102167465Smp    (FILE *(*)(const char *, const char *, ...))fopen;
103167465Smp# define VMS_OPEN_ATTRS "shr=get,put,upd,del","ctx=bin,stm","rfm=stm","rat=none","mrs=0"
10459243Sobrien#endif
105167465Smp
10659243Sobrien/* #define RFILE ".rnd" - defined in ../../e_os.h */
10759243Sobrien
10859243Sobrien/*
10959243Sobrien * Note that these functions are intended for seed files only. Entropy
11059243Sobrien * devices and EGD sockets are handled in rand_unix.c
11159243Sobrien */
11259243Sobrien
11359243Sobrienint RAND_load_file(const char *file, long bytes)
11459243Sobrien{
11559243Sobrien    /*-
116167465Smp     * If bytes >= 0, read up to 'bytes' bytes.
11759243Sobrien     * if bytes == -1, read complete file.
118167465Smp     */
11959243Sobrien
12059243Sobrien    MS_STATIC unsigned char buf[BUFSIZE];
12159243Sobrien#ifndef OPENSSL_NO_POSIX_IO
12259243Sobrien    struct stat sb;
12359243Sobrien#endif
124167465Smp    int i, ret = 0, n;
125167465Smp    FILE *in;
126167465Smp
12759243Sobrien    if (file == NULL)
12859243Sobrien        return (0);
12959243Sobrien
130167465Smp#ifndef OPENSSL_NO_POSIX_IO
131167465Smp# ifdef PURIFY
13259243Sobrien    /*
13359243Sobrien     * struct stat can have padding and unused fields that may not be
13459243Sobrien     * initialized in the call to stat(). We need to clear the entire
13559243Sobrien     * structure before calling RAND_add() to avoid complaints from
13659243Sobrien     * applications such as Valgrind.
13759243Sobrien     */
138167465Smp    memset(&sb, 0, sizeof(sb));
139167465Smp# endif
14059243Sobrien    if (stat(file, &sb) < 0)
141195609Smp        return (0);
142195609Smp    RAND_add(&sb, sizeof(sb), 0.0);
143167465Smp#endif
14459243Sobrien    if (bytes == 0)
14559243Sobrien        return (ret);
14659243Sobrien
14759243Sobrien#ifdef OPENSSL_SYS_VMS
14859243Sobrien    in = vms_fopen(file, "rb", VMS_OPEN_ATTRS);
14959243Sobrien#else
150195609Smp    in = fopen(file, "rb");
151195609Smp#endif
15259243Sobrien    if (in == NULL)
153195609Smp        goto err;
154195609Smp#if defined(S_IFBLK) && defined(S_IFCHR) && !defined(OPENSSL_NO_POSIX_IO)
155195609Smp    if (sb.st_mode & (S_IFBLK | S_IFCHR)) {
156195609Smp        /*
157195609Smp         * this file is a device. we don't want read an infinite number of
15859243Sobrien         * bytes from a random device, nor do we want to use buffered I/O
15959243Sobrien         * because we will waste system entropy.
16059243Sobrien         */
16159243Sobrien        bytes = (bytes == -1) ? 2048 : bytes; /* ok, is 2048 enough? */
16259243Sobrien# ifndef OPENSSL_NO_SETVBUF_IONBF
163167465Smp        setvbuf(in, NULL, _IONBF, 0); /* don't do buffered reads */
164167465Smp# endif                         /* ndef OPENSSL_NO_SETVBUF_IONBF */
16559243Sobrien    }
166145479Smp#endif
16759243Sobrien    for (;;) {
16859243Sobrien        if (bytes > 0)
16959243Sobrien            n = (bytes < BUFSIZE) ? (int)bytes : BUFSIZE;
17059243Sobrien        else
17159243Sobrien            n = BUFSIZE;
17259243Sobrien        i = fread(buf, 1, n, in);
17359243Sobrien        if (i <= 0)
174167465Smp            break;
17559243Sobrien#ifdef PURIFY
17659243Sobrien        RAND_add(buf, i, (double)i);
17759243Sobrien#else
17859243Sobrien        /* even if n != i, use the full array */
17959243Sobrien        RAND_add(buf, n, (double)i);
18059243Sobrien#endif
18159243Sobrien        ret += i;
18259243Sobrien        if (bytes > 0) {
183167465Smp            bytes -= n;
18459243Sobrien            if (bytes <= 0)
18559243Sobrien                break;
18659243Sobrien        }
18759243Sobrien    }
188167465Smp    fclose(in);
189167465Smp    OPENSSL_cleanse(buf, BUFSIZE);
19059243Sobrien err:
191167465Smp    return (ret);
19259243Sobrien}
19359243Sobrien
19459243Sobrienint RAND_write_file(const char *file)
19559243Sobrien{
19659243Sobrien    unsigned char buf[BUFSIZE];
19759243Sobrien    int i, ret = 0, rand_err = 0;
19859243Sobrien    FILE *out = NULL;
19959243Sobrien    int n;
20059243Sobrien#ifndef OPENSSL_NO_POSIX_IO
20159243Sobrien    struct stat sb;
202167465Smp
20359243Sobrien    i = stat(file, &sb);
204145479Smp    if (i != -1) {
205195609Smp# if defined(S_ISBLK) && defined(S_ISCHR)
206145479Smp        if (S_ISBLK(sb.st_mode) || S_ISCHR(sb.st_mode)) {
207167465Smp            /*
208195609Smp             * this file is a device. we don't write back to it. we
20959243Sobrien             * "succeed" on the assumption this is some sort of random
210195609Smp             * device. Otherwise attempting to write to and chmod the device
211167465Smp             * causes problems.
21259243Sobrien             */
21359243Sobrien            return (1);
21459243Sobrien        }
21559243Sobrien# endif
216167465Smp    }
217195609Smp#endif
21859243Sobrien
219167465Smp#if defined(O_CREAT) && !defined(OPENSSL_NO_POSIX_IO) && !defined(OPENSSL_SYS_VMS)
22059243Sobrien    {
22159243Sobrien# ifndef O_BINARY
22259243Sobrien#  define O_BINARY 0
22359243Sobrien# endif
22459243Sobrien        /*
225167465Smp         * chmod(..., 0600) is too late to protect the file, permissions
22659243Sobrien         * should be restrictive from the start
22759243Sobrien         */
22859243Sobrien        int fd = open(file, O_WRONLY | O_CREAT | O_BINARY, 0600);
229167465Smp        if (fd != -1)
23059243Sobrien            out = fdopen(fd, "wb");
23159243Sobrien    }
23259243Sobrien#endif
233195609Smp
23459243Sobrien#ifdef OPENSSL_SYS_VMS
23559243Sobrien    /*
23659243Sobrien     * VMS NOTE: Prior versions of this routine created a _new_ version of
23759243Sobrien     * the rand file for each call into this routine, then deleted all
23859243Sobrien     * existing versions named ;-1, and finally renamed the current version
23959243Sobrien     * as ';1'. Under concurrent usage, this resulted in an RMS race
24059243Sobrien     * condition in rename() which could orphan files (see vms message help
24159243Sobrien     * for RMS$_REENT). With the fopen() calls below, openssl/VMS now shares
24259243Sobrien     * the top-level version of the rand file. Note that there may still be
24359243Sobrien     * conditions where the top-level rand file is locked. If so, this code
24459243Sobrien     * will then create a new version of the rand file. Without the delete
24559243Sobrien     * and rename code, this can result in ascending file versions that stop
24659243Sobrien     * at version 32767, and this routine will then return an error. The
247195609Smp     * remedy for this is to recode the calling application to avoid
248195609Smp     * concurrent use of the rand file, or synchronize usage at the
249145479Smp     * application level. Also consider whether or not you NEED a persistent
250195609Smp     * rand file in a concurrent use situation.
25159243Sobrien     */
252195609Smp
253195609Smp    out = vms_fopen(file, "rb+", VMS_OPEN_ATTRS);
25459243Sobrien    if (out == NULL)
25559243Sobrien        out = vms_fopen(file, "wb", VMS_OPEN_ATTRS);
25659243Sobrien#else
25759243Sobrien    if (out == NULL)
25859243Sobrien        out = fopen(file, "wb");
25959243Sobrien#endif
26059243Sobrien    if (out == NULL)
26159243Sobrien        goto err;
262195609Smp
26359243Sobrien#ifndef NO_CHMOD
26459243Sobrien    chmod(file, 0600);
26559243Sobrien#endif
26659243Sobrien    n = RAND_DATA;
267195609Smp    for (;;) {
26859243Sobrien        i = (n > BUFSIZE) ? BUFSIZE : n;
26959243Sobrien        n -= BUFSIZE;
27059243Sobrien        if (RAND_bytes(buf, i) <= 0)
27159243Sobrien            rand_err = 1;
272195609Smp        i = fwrite(buf, 1, i, out);
27359243Sobrien        if (i <= 0) {
27459243Sobrien            ret = 0;
27559243Sobrien            break;
27659243Sobrien        }
27759243Sobrien        ret += i;
27859243Sobrien        if (n <= 0)
27959243Sobrien            break;
280195609Smp    }
28159243Sobrien
282195609Smp    fclose(out);
283167465Smp    OPENSSL_cleanse(buf, BUFSIZE);
284167465Smp err:
28559243Sobrien    return (rand_err ? -1 : ret);
28659243Sobrien}
28759243Sobrien
288167465Smpconst char *RAND_file_name(char *buf, size_t size)
289167465Smp{
29059243Sobrien    char *s = NULL;
29159243Sobrien#ifdef __OpenBSD__
29259243Sobrien    struct stat sb;
29359243Sobrien#endif
29459243Sobrien
29559243Sobrien    if (OPENSSL_issetugid() == 0)
296167465Smp        s = getenv("RANDFILE");
297167465Smp    if (s != NULL && *s && strlen(s) + 1 < size) {
298195609Smp        if (BUF_strlcpy(buf, s, size) >= size)
299167465Smp            return NULL;
30059243Sobrien    } else {
301167465Smp        if (OPENSSL_issetugid() == 0)
302167465Smp            s = getenv("HOME");
303195609Smp#ifdef DEFAULT_HOME
304195609Smp        if (s == NULL) {
305195609Smp            s = DEFAULT_HOME;
306195609Smp        }
307195609Smp#endif
308167465Smp        if (s && *s && strlen(s) + strlen(RFILE) + 2 < size) {
30959243Sobrien            BUF_strlcpy(buf, s, size);
31059243Sobrien#ifndef OPENSSL_SYS_VMS
31159243Sobrien            BUF_strlcat(buf, "/", size);
31259243Sobrien#endif
31359243Sobrien            BUF_strlcat(buf, RFILE, size);
31459243Sobrien        } else
31559243Sobrien            buf[0] = '\0';      /* no file name */
31659243Sobrien    }
317145479Smp
318167465Smp#ifdef __OpenBSD__
31959243Sobrien    /*
320231990Smp     * given that all random loads just fail if the file can't be seen on a
32159243Sobrien     * stat, we stat the file we're returning, if it fails, use /dev/arandom
32259243Sobrien     * instead. this allows the user to use their own source for good random
32359243Sobrien     * data, but defaults to something hopefully decent if that isn't
32459243Sobrien     * available.
32559243Sobrien     */
32659243Sobrien
327167465Smp    if (!buf[0])
328167465Smp        if (BUF_strlcpy(buf, "/dev/arandom", size) >= size) {
32959243Sobrien            return (NULL);
33059243Sobrien        }
33159243Sobrien    if (stat(buf, &sb) == -1)
33259243Sobrien        if (BUF_strlcpy(buf, "/dev/arandom", size) >= size) {
33359243Sobrien            return (NULL);
33459243Sobrien        }
33559243Sobrien#endif
33659243Sobrien    return (buf);
33759243Sobrien}
33859243Sobrien