| /* Copyright 2000-2004 The Apache Software Foundation |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #define APR_WANT_MEMFUNC |
| #include "apr_want.h" |
| #include "apr_general.h" |
| |
| #include "apr_arch_misc.h" |
| #include <sys/stat.h> |
| #if APR_HAVE_SYS_TYPES_H |
| #include <sys/types.h> |
| #endif |
| #if APR_HAVE_SYS_SOCKET_H |
| #include <sys/socket.h> |
| #endif |
| #if APR_HAVE_FCNTL_H |
| #include <fcntl.h> |
| #endif |
| #if APR_HAVE_UNISTD_H |
| #include <unistd.h> |
| #endif |
| #if APR_HAVE_SYS_UN_H |
| #include <sys/un.h> |
| #endif |
| |
| #ifndef SHUT_RDWR |
| #define SHUT_RDWR 2 |
| #endif |
| |
| #if APR_HAS_RANDOM |
| |
| APR_DECLARE(apr_status_t) apr_generate_random_bytes(unsigned char *buf, |
| apr_size_t length) |
| { |
| #ifdef DEV_RANDOM |
| |
| int fd = -1; |
| |
| /* On BSD/OS 4.1, /dev/random gives out 8 bytes at a time, then |
| * gives EOF, so reading 'length' bytes may require opening the |
| * device several times. */ |
| do { |
| apr_ssize_t rc; |
| |
| if (fd == -1) |
| if ((fd = open(DEV_RANDOM, O_RDONLY)) == -1) |
| return errno; |
| |
| rc = read(fd, buf, length); |
| if (rc < 0) { |
| int errnum = errno; |
| close(fd); |
| return errnum; |
| } |
| else if (rc == 0) { |
| close(fd); |
| fd = -1; /* force open() again */ |
| } |
| else { |
| buf += rc; |
| length -= rc; |
| } |
| } while (length > 0); |
| |
| close(fd); |
| #elif defined(OS2) |
| static UCHAR randbyte(); |
| unsigned int idx; |
| |
| for (idx=0; idx<length; idx++) |
| buf[idx] = randbyte(); |
| |
| #elif defined(HAVE_EGD) |
| /* use EGD-compatible socket daemon (such as EGD or PRNGd). |
| * message format: |
| * 0x00 (get entropy level) |
| * 0xMM (msb) 0xmm 0xll 0xLL (lsb) |
| * 0x01 (read entropy nonblocking) 0xNN (bytes requested) |
| * 0xMM (bytes granted) MM bytes |
| * 0x02 (read entropy blocking) 0xNN (bytes desired) |
| * [block] NN bytes |
| * 0x03 (write entropy) 0xMM 0xLL (bits of entropy) 0xNN (bytes of data) |
| * NN bytes |
| * (no response - write only) |
| * 0x04 (report PID) |
| * 0xMM (length of PID string, not null-terminated) MM chars |
| */ |
| static const char *egd_sockets[] = { EGD_DEFAULT_SOCKET, NULL }; |
| const char **egdsockname = NULL; |
| |
| int egd_socket, egd_path_len, rv, bad_errno; |
| struct sockaddr_un addr; |
| apr_socklen_t egd_addr_len; |
| apr_size_t resp_expected; |
| unsigned char req[2], resp[255]; |
| unsigned char *curbuf = buf; |
| |
| for (egdsockname = egd_sockets; *egdsockname && length > 0; egdsockname++) { |
| egd_path_len = strlen(*egdsockname); |
| |
| if (egd_path_len > sizeof(addr.sun_path)) { |
| return APR_EINVAL; |
| } |
| |
| memset(&addr, 0, sizeof(struct sockaddr_un)); |
| addr.sun_family = AF_UNIX; |
| memcpy(addr.sun_path, *egdsockname, egd_path_len); |
| egd_addr_len = APR_OFFSETOF(struct sockaddr_un, sun_path) + |
| egd_path_len; |
| |
| egd_socket = socket(PF_UNIX, SOCK_STREAM, 0); |
| |
| if (egd_socket == -1) { |
| return errno; |
| } |
| |
| rv = connect(egd_socket, (struct sockaddr*)&addr, egd_addr_len); |
| |
| if (rv == -1) { |
| bad_errno = errno; |
| continue; |
| } |
| |
| /* EGD can only return 255 bytes of data at a time. Silly. */ |
| while (length > 0) { |
| apr_ssize_t srv; |
| req[0] = 2; /* We'll block for now. */ |
| req[1] = length > 255 ? 255: length; |
| |
| srv = write(egd_socket, req, 2); |
| if (srv == -1) { |
| bad_errno = errno; |
| shutdown(egd_socket, SHUT_RDWR); |
| close(egd_socket); |
| break; |
| } |
| |
| if (srv != 2) { |
| shutdown(egd_socket, SHUT_RDWR); |
| close(egd_socket); |
| return APR_EGENERAL; |
| } |
| |
| resp_expected = req[1]; |
| srv = read(egd_socket, resp, resp_expected); |
| if (srv == -1) { |
| bad_errno = errno; |
| shutdown(egd_socket, SHUT_RDWR); |
| close(egd_socket); |
| return bad_errno; |
| } |
| |
| memcpy(curbuf, resp, srv); |
| curbuf += srv; |
| length -= srv; |
| } |
| |
| shutdown(egd_socket, SHUT_RDWR); |
| close(egd_socket); |
| } |
| |
| if (length > 0) { |
| /* We must have iterated through the list of sockets, |
| * and no go. Return the errno. |
| */ |
| return bad_errno; |
| } |
| |
| #elif defined(HAVE_TRUERAND) /* use truerand */ |
| |
| extern int randbyte(void); /* from the truerand library */ |
| unsigned int idx; |
| |
| /* this will increase the startup time of the server, unfortunately... |
| * (generating 20 bytes takes about 8 seconds) |
| */ |
| for (idx=0; idx<length; idx++) |
| buf[idx] = (unsigned char) randbyte(); |
| |
| #endif /* DEV_RANDOM */ |
| |
| return APR_SUCCESS; |
| } |
| |
| #undef STR |
| #undef XSTR |
| |
| #ifdef OS2 |
| #include "randbyte_os2.inc" |
| #endif |
| |
| #endif /* APR_HAS_RANDOM */ |