xref: /freebsd/crypto/krb5/src/lib/crypto/krb/prng.c (revision f1c4c3daccbaf3820f0e2224de53df12fc952fcc)
1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /*
3  * Copyright (C) 2001, 2002, 2004, 2007, 2008, 2010 by the Massachusetts Institute of Technology.
4  * All rights reserved.
5  *
6  *
7  * Export of this software from the United States of America may require
8  * a specific license from the United States Government.  It is the
9  * responsibility of any person or organization contemplating export to
10  * obtain such a license before exporting.
11  *
12  * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
13  * distribute this software and its documentation for any purpose and
14  * without fee is hereby granted, provided that the above copyright
15  * notice appear in all copies and that both that copyright notice and
16  * this permission notice appear in supporting documentation, and that
17  * the name of M.I.T. not be used in advertising or publicity pertaining
18  * to distribution of the software without specific, written prior
19  * permission.  Furthermore if you modify this software you must label
20  * your software as modified software and not distribute it in such a
21  * fashion that it might be confused with the original M.I.T. software.
22  * M.I.T. makes no representations about the suitability of
23  * this software for any purpose.  It is provided "as is" without express
24  * or implied warranty.
25  */
26 
27 #include "crypto_int.h"
28 
29 krb5_error_code KRB5_CALLCONV
krb5_c_random_seed(krb5_context context,krb5_data * data)30 krb5_c_random_seed(krb5_context context, krb5_data *data)
31 {
32     return krb5_c_random_add_entropy(context, KRB5_C_RANDSOURCE_OLDAPI, data);
33 }
34 
35 /* Routines to get entropy from the OS. */
36 #if defined(_WIN32)
37 
38 static krb5_boolean
get_os_entropy(unsigned char * buf,size_t len)39 get_os_entropy(unsigned char *buf, size_t len)
40 {
41     krb5_boolean result;
42     HCRYPTPROV provider;
43 
44     if (!CryptAcquireContext(&provider, NULL, NULL, PROV_RSA_FULL,
45                              CRYPT_VERIFYCONTEXT))
46         return FALSE;
47     result = CryptGenRandom(provider, len, buf);
48     (void)CryptReleaseContext(provider, 0);
49     return result;
50 }
51 
52 #else /* not Windows */
53 #ifdef HAVE_UNISTD_H
54 #include <unistd.h>
55 #endif
56 #ifdef HAVE_SYS_STAT_H
57 #include <sys/stat.h>
58 #endif
59 #ifdef HAVE_SYS_RANDOM_H
60 #include <sys/random.h>
61 #endif
62 #ifdef __linux__
63 #include <sys/syscall.h>
64 #endif /* __linux__ */
65 
66 /* Open device, ensure that it is not a regular file, and read entropy.  Return
67  * true on success, false on failure. */
68 static krb5_boolean
read_entropy_from_device(const char * device,unsigned char * buf,size_t len)69 read_entropy_from_device(const char *device, unsigned char *buf, size_t len)
70 {
71     struct stat sb;
72     int fd;
73     unsigned char *bp;
74     size_t left;
75     ssize_t count;
76     krb5_boolean result = FALSE;
77 
78     fd = open(device, O_RDONLY);
79     if (fd == -1)
80         return FALSE;
81     set_cloexec_fd(fd);
82     if (fstat(fd, &sb) == -1 || S_ISREG(sb.st_mode))
83         goto cleanup;
84 
85     for (bp = buf, left = len; left > 0;) {
86         count = read(fd, bp, left);
87         if (count <= 0)
88             goto cleanup;
89         left -= count;
90         bp += count;
91     }
92     result = TRUE;
93 
94 cleanup:
95     close(fd);
96     return result;
97 }
98 
99 static krb5_boolean
get_os_entropy(unsigned char * buf,size_t len)100 get_os_entropy(unsigned char *buf, size_t len)
101 {
102 #if defined(HAVE_GETENTROPY)
103     int r;
104     size_t seg;
105 
106     /* getentropy() has a maximum length of 256. */
107     while (len > 0) {
108         seg = (len > 256) ? 256 : len;
109         r = getentropy(buf, seg);
110         if (r != 0)
111             break;
112         len -= seg;
113         buf += seg;
114     }
115     if (len == 0)
116         return TRUE;
117 #elif defined(__linux__) && defined(SYS_getrandom)
118     /*
119      * Linux added SYS_getrandom in 3.17 (2014), but glibc did not have a
120      * wrapper until 2.25 (2017).  This block can be deleted when that interval
121      * is far in the past, along with the conditional include of
122      * <sys/syscall.h> above.
123      */
124     int r;
125 
126     while (len > 0) {
127         /*
128          * Pull from the /dev/urandom pool, but require it to have been seeded.
129          * This ensures strong randomness while only blocking during first
130          * system boot.
131          */
132         errno = 0;
133         r = syscall(SYS_getrandom, buf, len, 0);
134         if (r <= 0) {
135             if (errno == EINTR)
136                 continue;
137 
138             /* ENOSYS or other unrecoverable failure */
139             break;
140         }
141         len -= r;
142         buf += r;
143     }
144     if (len == 0)
145         return TRUE;
146 #endif /* defined(__linux__) && defined(SYS_getrandom) */
147 
148     return read_entropy_from_device("/dev/urandom", buf, len);
149 }
150 
151 #endif /* not Windows */
152 
153 krb5_error_code KRB5_CALLCONV
krb5_c_random_make_octets(krb5_context context,krb5_data * outdata)154 krb5_c_random_make_octets(krb5_context context, krb5_data *outdata)
155 {
156     krb5_boolean res;
157 
158     res = get_os_entropy((uint8_t *)outdata->data, outdata->length);
159     return res ? 0 : KRB5_CRYPTO_INTERNAL;
160 }
161 
162 krb5_error_code KRB5_CALLCONV
krb5_c_random_add_entropy(krb5_context context,unsigned int randsource,const krb5_data * indata)163 krb5_c_random_add_entropy(krb5_context context, unsigned int randsource,
164                           const krb5_data *indata)
165 {
166     return 0;
167 }
168 
169 krb5_error_code KRB5_CALLCONV
krb5_c_random_os_entropy(krb5_context context,int strong,int * success)170 krb5_c_random_os_entropy(krb5_context context, int strong, int *success)
171 {
172     *success = 0;
173     return 0;
174 }
175