Imported Upstream version 1.15.1
[platform/upstream/krb5.git] / src / lib / crypto / krb / prng.c
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 krb5_boolean
39 k5_get_os_entropy(unsigned char *buf, size_t len, int strong)
40 {
41     krb5_boolean result;
42     HCRYPTPROV provider;
43
44     /* CryptGenRandom is always considered strong. */
45
46     if (!CryptAcquireContext(&provider, NULL, NULL, PROV_RSA_FULL,
47                              CRYPT_VERIFYCONTEXT))
48         return FALSE;
49     result = CryptGenRandom(provider, len, buf);
50     (void)CryptReleaseContext(provider, 0);
51     return result;
52 }
53
54 #else /* not Windows */
55 #ifdef HAVE_UNISTD_H
56 #include <unistd.h>
57 #endif
58 #ifdef HAVE_SYS_STAT_H
59 #include <sys/stat.h>
60 #endif
61 #ifdef __linux__
62 #include <sys/syscall.h>
63 #endif /* __linux__ */
64
65 /* Open device, ensure that it is not a regular file, and read entropy.  Return
66  * true on success, false on failure. */
67 static krb5_boolean
68 read_entropy_from_device(const char *device, unsigned char *buf, size_t len)
69 {
70     struct stat sb;
71     int fd;
72     unsigned char *bp;
73     size_t left;
74     ssize_t count;
75     krb5_boolean result = FALSE;
76
77     fd = open(device, O_RDONLY);
78     if (fd == -1)
79         return FALSE;
80     set_cloexec_fd(fd);
81     if (fstat(fd, &sb) == -1 || S_ISREG(sb.st_mode))
82         goto cleanup;
83
84     for (bp = buf, left = len; left > 0;) {
85         count = read(fd, bp, left);
86         if (count <= 0)
87             goto cleanup;
88         left -= count;
89         bp += count;
90     }
91     result = TRUE;
92
93 cleanup:
94     close(fd);
95     return result;
96 }
97
98 krb5_boolean
99 k5_get_os_entropy(unsigned char *buf, size_t len, int strong)
100 {
101     const char *device;
102 #if defined(__linux__) && defined(SYS_getrandom)
103     int r;
104
105     while (len > 0) {
106         /*
107          * Pull from the /dev/urandom pool, but require it to have been seeded.
108          * This ensures strong randomness while only blocking during first
109          * system boot.
110          *
111          * glibc does not currently provide a binding for getrandom:
112          * https://sourceware.org/bugzilla/show_bug.cgi?id=17252
113          */
114         errno = 0;
115         r = syscall(SYS_getrandom, buf, len, 0);
116         if (r <= 0) {
117             if (errno == EINTR)
118                 continue;
119
120             /* ENOSYS or other unrecoverable failure */
121             break;
122         }
123         len -= r;
124         buf += r;
125     }
126     if (len == 0)
127         return TRUE;
128 #endif /* defined(__linux__) && defined(SYS_getrandom) */
129
130     device = strong ? "/dev/random" : "/dev/urandom";
131     return read_entropy_from_device(device, buf, len);
132 }
133
134 #endif /* not Windows */