xref: /freebsd/crypto/krb5/src/lib/crypto/krb/prng.c (revision 4b15965daa99044daf184221b7c283bf7f2d7e66)
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
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
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 __linux__
60 #include <sys/syscall.h>
61 #endif /* __linux__ */
62 
63 /* Open device, ensure that it is not a regular file, and read entropy.  Return
64  * true on success, false on failure. */
65 static krb5_boolean
66 read_entropy_from_device(const char *device, unsigned char *buf, size_t len)
67 {
68     struct stat sb;
69     int fd;
70     unsigned char *bp;
71     size_t left;
72     ssize_t count;
73     krb5_boolean result = FALSE;
74 
75     fd = open(device, O_RDONLY);
76     if (fd == -1)
77         return FALSE;
78     set_cloexec_fd(fd);
79     if (fstat(fd, &sb) == -1 || S_ISREG(sb.st_mode))
80         goto cleanup;
81 
82     for (bp = buf, left = len; left > 0;) {
83         count = read(fd, bp, left);
84         if (count <= 0)
85             goto cleanup;
86         left -= count;
87         bp += count;
88     }
89     result = TRUE;
90 
91 cleanup:
92     close(fd);
93     return result;
94 }
95 
96 static krb5_boolean
97 get_os_entropy(unsigned char *buf, size_t len)
98 {
99 #if defined(__linux__) && defined(SYS_getrandom)
100     int r;
101 
102     while (len > 0) {
103         /*
104          * Pull from the /dev/urandom pool, but require it to have been seeded.
105          * This ensures strong randomness while only blocking during first
106          * system boot.
107          *
108          * glibc does not currently provide a binding for getrandom:
109          * https://sourceware.org/bugzilla/show_bug.cgi?id=17252
110          */
111         errno = 0;
112         r = syscall(SYS_getrandom, buf, len, 0);
113         if (r <= 0) {
114             if (errno == EINTR)
115                 continue;
116 
117             /* ENOSYS or other unrecoverable failure */
118             break;
119         }
120         len -= r;
121         buf += r;
122     }
123     if (len == 0)
124         return TRUE;
125 #endif /* defined(__linux__) && defined(SYS_getrandom) */
126 
127     return read_entropy_from_device("/dev/urandom", buf, len);
128 }
129 
130 #endif /* not Windows */
131 
132 krb5_error_code KRB5_CALLCONV
133 krb5_c_random_make_octets(krb5_context context, krb5_data *outdata)
134 {
135     krb5_boolean res;
136 
137     res = get_os_entropy((uint8_t *)outdata->data, outdata->length);
138     return res ? 0 : KRB5_CRYPTO_INTERNAL;
139 }
140 
141 krb5_error_code KRB5_CALLCONV
142 krb5_c_random_add_entropy(krb5_context context, unsigned int randsource,
143                           const krb5_data *indata)
144 {
145     return 0;
146 }
147 
148 krb5_error_code KRB5_CALLCONV
149 krb5_c_random_os_entropy(krb5_context context, int strong, int *success)
150 {
151     *success = 0;
152     return 0;
153 }
154