]> granicus.if.org Git - postgresql/blob - src/port/pg_strong_random.c
Introduce convenience macros to hide JsonbContainer header accesses better.
[postgresql] / src / port / pg_strong_random.c
1 /*-------------------------------------------------------------------------
2  *
3  * pg_strong_random.c
4  *        generate a cryptographically secure random number
5  *
6  * Our definition of "strong" is that it's suitable for generating random
7  * salts and query cancellation keys, during authentication.
8  *
9  * Copyright (c) 1996-2017, PostgreSQL Global Development Group
10  *
11  * IDENTIFICATION
12  *        src/port/pg_strong_random.c
13  *
14  *-------------------------------------------------------------------------
15  */
16
17 #ifndef FRONTEND
18 #include "postgres.h"
19 #else
20 #include "postgres_fe.h"
21 #endif
22
23 #include <fcntl.h>
24 #include <unistd.h>
25 #include <sys/time.h>
26
27 #ifdef USE_OPENSSL
28 #include <openssl/rand.h>
29 #endif
30 #ifdef WIN32
31 #include <Wincrypt.h>
32 #endif
33
34 #ifdef WIN32
35 /*
36  * Cache a global crypto provider that only gets freed when the process
37  * exits, in case we need random numbers more than once.
38  */
39 static HCRYPTPROV hProvider = 0;
40 #endif
41
42 #if defined(USE_DEV_URANDOM)
43 /*
44  * Read (random) bytes from a file.
45  */
46 static bool
47 random_from_file(char *filename, void *buf, size_t len)
48 {
49         int                     f;
50         char       *p = buf;
51         ssize_t         res;
52
53         f = open(filename, O_RDONLY, 0);
54         if (f == -1)
55                 return false;
56
57         while (len)
58         {
59                 res = read(f, p, len);
60                 if (res <= 0)
61                 {
62                         if (errno == EINTR)
63                                 continue;               /* interrupted by signal, just retry */
64
65                         close(f);
66                         return false;
67                 }
68
69                 p += res;
70                 len -= res;
71         }
72
73         close(f);
74         return true;
75 }
76 #endif
77
78 /*
79  * pg_strong_random
80  *
81  * Generate requested number of random bytes. The returned bytes are
82  * cryptographically secure, suitable for use e.g. in authentication.
83  *
84  * We rely on system facilities for actually generating the numbers.
85  * We support a number of sources:
86  *
87  * 1. OpenSSL's RAND_bytes()
88  * 2. Windows' CryptGenRandom() function
89  * 3. /dev/urandom
90  *
91  * The configure script will choose which one to use, and set
92  * a USE_*_RANDOM flag accordingly.
93  *
94  * Returns true on success, and false if none of the sources
95  * were available. NB: It is important to check the return value!
96  * Proceeding with key generation when no random data was available
97  * would lead to predictable keys and security issues.
98  */
99 bool
100 pg_strong_random(void *buf, size_t len)
101 {
102         /*
103          * When built with OpenSSL, use OpenSSL's RAND_bytes function.
104          */
105 #if defined(USE_OPENSSL_RANDOM)
106         if (RAND_bytes(buf, len) == 1)
107                 return true;
108         return false;
109
110         /*
111          * Windows has CryptoAPI for strong cryptographic numbers.
112          */
113 #elif defined(USE_WIN32_RANDOM)
114         if (hProvider == 0)
115         {
116                 if (!CryptAcquireContext(&hProvider,
117                                                                  NULL,
118                                                                  MS_DEF_PROV,
119                                                                  PROV_RSA_FULL,
120                                                                  CRYPT_VERIFYCONTEXT | CRYPT_SILENT))
121                 {
122                         /*
123                          * On failure, set back to 0 in case the value was for some reason
124                          * modified.
125                          */
126                         hProvider = 0;
127                 }
128         }
129         /* Re-check in case we just retrieved the provider */
130         if (hProvider != 0)
131         {
132                 if (CryptGenRandom(hProvider, len, buf))
133                         return true;
134         }
135         return false;
136
137         /*
138          * Read /dev/urandom ourselves.
139          */
140 #elif defined(USE_DEV_URANDOM)
141         if (random_from_file("/dev/urandom", buf, len))
142                 return true;
143         return false;
144
145 #else
146         /* The autoconf script should not have allowed this */
147 #error no source of random numbers configured
148 #endif
149 }