]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/apr/misc/unix/rand.c
THIS BRANCH IS OBSOLETE, PLEASE READ:
[FreeBSD/FreeBSD.git] / contrib / apr / misc / unix / rand.c
1 /* Licensed to the Apache Software Foundation (ASF) under one or more
2  * contributor license agreements.  See the NOTICE file distributed with
3  * this work for additional information regarding copyright ownership.
4  * The ASF licenses this file to You under the Apache License, Version 2.0
5  * (the "License"); you may not use this file except in compliance with
6  * the License.  You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 #define APR_WANT_MEMFUNC
18 #include "apr_want.h"
19 #include "apr_general.h"
20
21 #include "apr_arch_misc.h"
22 #include <sys/stat.h>
23 #if APR_HAVE_SYS_TYPES_H
24 #include <sys/types.h>
25 #endif
26 #if APR_HAVE_SYS_SOCKET_H
27 #include <sys/socket.h>
28 #endif
29 #if APR_HAVE_FCNTL_H
30 #include <fcntl.h>
31 #endif
32 #if APR_HAVE_UNISTD_H
33 #include <unistd.h>
34 #endif
35 #if APR_HAVE_SYS_UN_H
36 #include <sys/un.h>
37 #endif
38 #if defined(HAVE_UUID_H)
39 #include <uuid.h>
40 #elif defined(HAVE_UUID_UUID_H)
41 #include <uuid/uuid.h>
42 #elif defined(HAVE_SYS_UUID_H)
43 #include <sys/uuid.h>
44 #endif
45
46 #if defined(SYS_RANDOM)
47 #if defined(HAVE_SYS_RANDOM_H) && \
48     defined(HAVE_GETRANDOM)
49
50 #include <sys/random.h>
51 #define USE_GETRANDOM
52
53 #elif defined(HAVE_SYS_SYSCALL_H) && \
54       defined(HAVE_LINUX_RANDOM_H) && \
55       defined(HAVE_DECL_SYS_GETRANDOM) && \
56       HAVE_DECL_SYS_GETRANDOM
57
58 #ifndef _GNU_SOURCE
59 #define _GNU_SOURCE
60 #endif
61 #include <unistd.h>
62 #include <sys/syscall.h>
63 #include <linux/random.h>
64 #define getrandom(buf, buflen, flags) \
65     syscall(SYS_getrandom, (buf), (buflen), (flags))
66 #define USE_GETRANDOM
67
68 #endif /* HAVE_SYS_RANDOM_H */
69 #endif /* SYS_RANDOM */
70
71 #ifndef SHUT_RDWR
72 #define SHUT_RDWR 2
73 #endif
74
75 #if APR_HAS_OS_UUID
76
77 #if defined(HAVE_UUID_CREATE)
78
79 APR_DECLARE(apr_status_t) apr_os_uuid_get(unsigned char *uuid_data)
80 {
81     uint32_t rv;
82     uuid_t g;
83
84     uuid_create(&g, &rv);
85
86     if (rv != uuid_s_ok)
87         return APR_EGENERAL;
88
89     memcpy(uuid_data, &g, sizeof(uuid_t));
90
91     return APR_SUCCESS;
92 }
93
94 #elif defined(HAVE_UUID_GENERATE)
95
96 APR_DECLARE(apr_status_t) apr_os_uuid_get(unsigned char *uuid_data)
97 {
98     uuid_t g;
99
100     uuid_generate(g);
101
102     memcpy(uuid_data, g, sizeof(uuid_t));
103
104     return APR_SUCCESS;
105 }
106 #endif 
107
108 #endif /* APR_HAS_OS_UUID */
109
110 #if APR_HAS_RANDOM
111
112 APR_DECLARE(apr_status_t) apr_generate_random_bytes(unsigned char *buf, 
113                                                     apr_size_t length)
114 {
115 #if defined(HAVE_EGD)
116     /* use EGD-compatible socket daemon (such as EGD or PRNGd).
117      * message format:
118      * 0x00 (get entropy level)
119      *   0xMM (msb) 0xmm 0xll 0xLL (lsb)
120      * 0x01 (read entropy nonblocking) 0xNN (bytes requested)
121      *   0xMM (bytes granted) MM bytes
122      * 0x02 (read entropy blocking) 0xNN (bytes desired)
123      *   [block] NN bytes
124      * 0x03 (write entropy) 0xMM 0xLL (bits of entropy) 0xNN (bytes of data) 
125      *      NN bytes
126      * (no response - write only) 
127      * 0x04 (report PID)
128      *   0xMM (length of PID string, not null-terminated) MM chars
129      */
130     static const char *egd_sockets[] = { EGD_DEFAULT_SOCKET, NULL };
131     const char **egdsockname = NULL;
132
133     int egd_socket, egd_path_len, rv, bad_errno;
134     struct sockaddr_un addr;
135     apr_socklen_t egd_addr_len;
136     apr_size_t resp_expected;
137     unsigned char req[2], resp[255];
138     unsigned char *curbuf = buf;
139
140     for (egdsockname = egd_sockets; *egdsockname && length > 0; egdsockname++) {
141         egd_path_len = strlen(*egdsockname);
142         
143         if (egd_path_len > sizeof(addr.sun_path)) {
144             return APR_EINVAL;
145         }
146
147         memset(&addr, 0, sizeof(struct sockaddr_un));
148         addr.sun_family = AF_UNIX;
149         memcpy(addr.sun_path, *egdsockname, egd_path_len);
150         egd_addr_len = APR_OFFSETOF(struct sockaddr_un, sun_path) + 
151           egd_path_len; 
152
153         egd_socket = socket(PF_UNIX, SOCK_STREAM, 0);
154
155         if (egd_socket == -1) {
156             return errno;
157         }
158
159         rv = connect(egd_socket, (struct sockaddr*)&addr, egd_addr_len);
160
161         if (rv == -1) {
162             bad_errno = errno;
163             continue;
164         }
165
166         /* EGD can only return 255 bytes of data at a time.  Silly.  */ 
167         while (length > 0) {
168             apr_ssize_t srv;
169             req[0] = 2; /* We'll block for now. */
170             req[1] = length > 255 ? 255: length;
171
172             srv = write(egd_socket, req, 2);
173             if (srv == -1) {
174                 bad_errno = errno;
175                 shutdown(egd_socket, SHUT_RDWR);
176                 close(egd_socket);
177                 break;
178             }
179
180             if (srv != 2) {
181                 shutdown(egd_socket, SHUT_RDWR);
182                 close(egd_socket);
183                 return APR_EGENERAL;
184             }
185             
186             resp_expected = req[1];
187             srv = read(egd_socket, resp, resp_expected);
188             if (srv == -1) {
189                 bad_errno = errno;
190                 shutdown(egd_socket, SHUT_RDWR);
191                 close(egd_socket);
192                 return bad_errno;
193             }
194             
195             memcpy(curbuf, resp, srv);
196             curbuf += srv;
197             length -= srv;
198         }
199         
200         shutdown(egd_socket, SHUT_RDWR);
201         close(egd_socket);
202     }
203
204     if (length > 0) {
205         /* We must have iterated through the list of sockets,
206          * and no go. Return the errno.
207          */
208         return bad_errno;
209     }
210
211 #elif defined(SYS_RANDOM) && defined(USE_GETRANDOM)
212
213     do {
214         int rc;
215
216         rc = getrandom(buf, length, 0);
217         if (rc == -1) {
218             if (errno == EINTR) {
219                 continue;
220             }
221             return errno;
222         }
223
224         buf += rc;
225         length -= rc;
226     } while (length > 0);
227
228 #elif defined(SYS_RANDOM) && defined(HAVE_ARC4RANDOM_BUF)
229
230     arc4random_buf(buf, length);
231
232 #elif defined(DEV_RANDOM)
233
234     int fd = -1;
235
236     /* On BSD/OS 4.1, /dev/random gives out 8 bytes at a time, then
237      * gives EOF, so reading 'length' bytes may require opening the
238      * device several times. */
239     do {
240         apr_ssize_t rc;
241
242         if (fd == -1)
243             if ((fd = open(DEV_RANDOM, O_RDONLY)) == -1)
244                 return errno;
245         
246         do {
247             rc = read(fd, buf, length);
248         } while (rc == -1 && errno == EINTR);
249
250         if (rc < 0) {
251             int errnum = errno;
252             close(fd);
253             return errnum;
254         }
255         else if (rc == 0) {
256             close(fd);
257             fd = -1; /* force open() again */
258         }
259         else {
260             buf += rc;
261             length -= rc;
262         }
263     } while (length > 0);
264     
265     close(fd);
266
267 #elif defined(OS2)
268
269     static UCHAR randbyte();
270     unsigned int idx;
271
272     for (idx=0; idx<length; idx++)
273         buf[idx] = randbyte();
274
275 #elif defined(HAVE_TRUERAND) /* use truerand */
276
277     extern int randbyte(void);  /* from the truerand library */
278     unsigned int idx;
279
280     /* this will increase the startup time of the server, unfortunately...
281      * (generating 20 bytes takes about 8 seconds)
282      */
283     for (idx=0; idx<length; idx++)
284         buf[idx] = (unsigned char) randbyte();
285
286 #else
287
288 #error APR_HAS_RANDOM defined with no implementation
289
290 #endif  /* DEV_RANDOM */
291
292     return APR_SUCCESS;
293 }
294
295 #undef  STR
296 #undef  XSTR
297
298 #ifdef OS2
299 #include "randbyte_os2.inc"
300 #endif
301
302 #endif /* APR_HAS_RANDOM */