/* Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You 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 #if APR_HAVE_SYS_TYPES_H #include #endif #if APR_HAVE_SYS_SOCKET_H #include #endif #if APR_HAVE_FCNTL_H #include #endif #if APR_HAVE_UNISTD_H #include #endif #if APR_HAVE_SYS_UN_H #include #endif #if defined(HAVE_UUID_H) #include #elif defined(HAVE_UUID_UUID_H) #include #elif defined(HAVE_SYS_UUID_H) #include #endif #if defined(SYS_RANDOM) #if defined(HAVE_SYS_RANDOM_H) && \ defined(HAVE_GETRANDOM) #include #define USE_GETRANDOM #elif defined(HAVE_SYS_SYSCALL_H) && \ defined(HAVE_LINUX_RANDOM_H) && \ defined(HAVE_DECL_SYS_GETRANDOM) && \ HAVE_DECL_SYS_GETRANDOM #ifndef _GNU_SOURCE #define _GNU_SOURCE #endif #include #include #include #define getrandom(buf, buflen, flags) \ syscall(SYS_getrandom, (buf), (buflen), (flags)) #define USE_GETRANDOM #endif /* HAVE_SYS_RANDOM_H */ #endif /* SYS_RANDOM */ #ifndef SHUT_RDWR #define SHUT_RDWR 2 #endif #if APR_HAS_OS_UUID #if defined(HAVE_UUID_CREATE) APR_DECLARE(apr_status_t) apr_os_uuid_get(unsigned char *uuid_data) { uint32_t rv; uuid_t g; uuid_create(&g, &rv); if (rv != uuid_s_ok) return APR_EGENERAL; memcpy(uuid_data, &g, sizeof(uuid_t)); return APR_SUCCESS; } #elif defined(HAVE_UUID_GENERATE) APR_DECLARE(apr_status_t) apr_os_uuid_get(unsigned char *uuid_data) { uuid_t g; uuid_generate(g); memcpy(uuid_data, g, sizeof(uuid_t)); return APR_SUCCESS; } #endif #endif /* APR_HAS_OS_UUID */ #if APR_HAS_RANDOM APR_DECLARE(apr_status_t) apr_generate_random_bytes(unsigned char *buf, apr_size_t length) { #if 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(SYS_RANDOM) && defined(USE_GETRANDOM) do { int rc; rc = getrandom(buf, length, 0); if (rc == -1) { if (errno == EINTR) { continue; } return errno; } buf += rc; length -= rc; } while (length > 0); #elif defined(SYS_RANDOM) && defined(HAVE_ARC4RANDOM_BUF) arc4random_buf(buf, length); #elif defined(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; do { rc = read(fd, buf, length); } while (rc == -1 && errno == EINTR); 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