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#ifndef SHUT_RDWR
47#define SHUT_RDWR 2
48#endif
49
50#if APR_HAS_OS_UUID
51
52#if defined(HAVE_UUID_CREATE)
53
54APR_DECLARE(apr_status_t) apr_os_uuid_get(unsigned char *uuid_data)
55{
56    uint32_t rv;
57    uuid_t g;
58
59    uuid_create(&g, &rv);
60
61    if (rv != uuid_s_ok)
62        return APR_EGENERAL;
63
64    memcpy(uuid_data, &g, sizeof(uuid_t));
65
66    return APR_SUCCESS;
67}
68
69#elif defined(HAVE_UUID_GENERATE)
70
71APR_DECLARE(apr_status_t) apr_os_uuid_get(unsigned char *uuid_data)
72{
73    uuid_t g;
74
75    uuid_generate(g);
76
77    memcpy(uuid_data, g, sizeof(uuid_t));
78
79    return APR_SUCCESS;
80}
81#endif
82
83#endif /* APR_HAS_OS_UUID */
84
85#if APR_HAS_RANDOM
86
87APR_DECLARE(apr_status_t) apr_generate_random_bytes(unsigned char *buf,
88                                                    apr_size_t length)
89{
90#ifdef DEV_RANDOM
91
92    int fd = -1;
93
94    /* On BSD/OS 4.1, /dev/random gives out 8 bytes at a time, then
95     * gives EOF, so reading 'length' bytes may require opening the
96     * device several times. */
97    do {
98        apr_ssize_t rc;
99
100        if (fd == -1)
101            if ((fd = open(DEV_RANDOM, O_RDONLY)) == -1)
102                return errno;
103
104        do {
105            rc = read(fd, buf, length);
106        } while (rc == -1 && errno == EINTR);
107
108        if (rc < 0) {
109            int errnum = errno;
110            close(fd);
111            return errnum;
112        }
113        else if (rc == 0) {
114            close(fd);
115            fd = -1; /* force open() again */
116        }
117        else {
118            buf += rc;
119            length -= rc;
120        }
121    } while (length > 0);
122
123    close(fd);
124#elif defined(OS2)
125    static UCHAR randbyte();
126    unsigned int idx;
127
128    for (idx=0; idx<length; idx++)
129	buf[idx] = randbyte();
130
131#elif defined(HAVE_EGD)
132    /* use EGD-compatible socket daemon (such as EGD or PRNGd).
133     * message format:
134     * 0x00 (get entropy level)
135     *   0xMM (msb) 0xmm 0xll 0xLL (lsb)
136     * 0x01 (read entropy nonblocking) 0xNN (bytes requested)
137     *   0xMM (bytes granted) MM bytes
138     * 0x02 (read entropy blocking) 0xNN (bytes desired)
139     *   [block] NN bytes
140     * 0x03 (write entropy) 0xMM 0xLL (bits of entropy) 0xNN (bytes of data)
141     *      NN bytes
142     * (no response - write only)
143     * 0x04 (report PID)
144     *   0xMM (length of PID string, not null-terminated) MM chars
145     */
146    static const char *egd_sockets[] = { EGD_DEFAULT_SOCKET, NULL };
147    const char **egdsockname = NULL;
148
149    int egd_socket, egd_path_len, rv, bad_errno;
150    struct sockaddr_un addr;
151    apr_socklen_t egd_addr_len;
152    apr_size_t resp_expected;
153    unsigned char req[2], resp[255];
154    unsigned char *curbuf = buf;
155
156    for (egdsockname = egd_sockets; *egdsockname && length > 0; egdsockname++) {
157        egd_path_len = strlen(*egdsockname);
158
159        if (egd_path_len > sizeof(addr.sun_path)) {
160            return APR_EINVAL;
161        }
162
163        memset(&addr, 0, sizeof(struct sockaddr_un));
164        addr.sun_family = AF_UNIX;
165        memcpy(addr.sun_path, *egdsockname, egd_path_len);
166        egd_addr_len = APR_OFFSETOF(struct sockaddr_un, sun_path) +
167          egd_path_len;
168
169        egd_socket = socket(PF_UNIX, SOCK_STREAM, 0);
170
171        if (egd_socket == -1) {
172            return errno;
173        }
174
175        rv = connect(egd_socket, (struct sockaddr*)&addr, egd_addr_len);
176
177        if (rv == -1) {
178            bad_errno = errno;
179            continue;
180        }
181
182        /* EGD can only return 255 bytes of data at a time.  Silly.  */
183        while (length > 0) {
184            apr_ssize_t srv;
185            req[0] = 2; /* We'll block for now. */
186            req[1] = length > 255 ? 255: length;
187
188            srv = write(egd_socket, req, 2);
189            if (srv == -1) {
190                bad_errno = errno;
191                shutdown(egd_socket, SHUT_RDWR);
192                close(egd_socket);
193                break;
194            }
195
196            if (srv != 2) {
197                shutdown(egd_socket, SHUT_RDWR);
198                close(egd_socket);
199                return APR_EGENERAL;
200            }
201
202            resp_expected = req[1];
203            srv = read(egd_socket, resp, resp_expected);
204            if (srv == -1) {
205                bad_errno = errno;
206                shutdown(egd_socket, SHUT_RDWR);
207                close(egd_socket);
208                return bad_errno;
209            }
210
211            memcpy(curbuf, resp, srv);
212            curbuf += srv;
213            length -= srv;
214        }
215
216        shutdown(egd_socket, SHUT_RDWR);
217        close(egd_socket);
218    }
219
220    if (length > 0) {
221        /* We must have iterated through the list of sockets,
222         * and no go. Return the errno.
223         */
224        return bad_errno;
225    }
226
227#elif defined(HAVE_TRUERAND) /* use truerand */
228
229    extern int randbyte(void);	/* from the truerand library */
230    unsigned int idx;
231
232    /* this will increase the startup time of the server, unfortunately...
233     * (generating 20 bytes takes about 8 seconds)
234     */
235    for (idx=0; idx<length; idx++)
236	buf[idx] = (unsigned char) randbyte();
237
238#endif	/* DEV_RANDOM */
239
240    return APR_SUCCESS;
241}
242
243#undef	STR
244#undef	XSTR
245
246#ifdef OS2
247#include "randbyte_os2.inc"
248#endif
249
250#endif /* APR_HAS_RANDOM */
251