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