bool drbg_getentropy()

in csrc/aes_ctr_drbg.cpp [48:128]


bool drbg_getentropy(void *buffer, size_t length) {
#ifdef HAVE_GETENTROPY
    int rv = getentropy(buffer, length);
    if (likely(rv == 0) || errno != ENOSYS) {
        // The getentropy call was available; return success if it
        // gave us a successful return.
        return !rv;
    }
#endif

    // Fallback path for when getentropy is unavailable
    bool success = false;
    int mutex_error = 0;
    uint8_t *bufp = reinterpret_cast<uint8_t *>(buffer);

    if (unlikely(mutex_error = pthread_mutex_lock(&getentropy_mutex))) {
        errno = mutex_error;
        perror("pthread_mutex_lock");
        abort(); // mutex failure - don't even try to recover
    }

    if (unlikely(urandom_fd < 0)) {
        if (O_CLOEXEC) {
            // If O_CLOEXEC is undefined, make sure we go into the fallback path
            // which will do a workaround using fcntl.
            urandom_fd = open("/dev/urandom", O_RDONLY | O_CLOEXEC);
        }

        if (unlikely(urandom_fd < 0)) {
            if (errno == EINVAL || errno == ENOSYS) {
                // We might be on an old kernel that doesn't support O_CLOEXEC.
                // Try again and use fcntl instead. This is subject to an unavoidable
                // race condition in which the process forks before we can set the CLOEXEC
                // flag, which is why O_CLOEXEC was introduced in the first place.
                int flags;

                urandom_fd = open("/dev/urandom", O_RDONLY);
                if (urandom_fd >= 0) {
                    flags = fcntl(F_GETFD, urandom_fd);
                    if (flags == -1) {
                        close(urandom_fd);
                        urandom_fd = -1;
                    } else {
                        if (-1 == fcntl(F_SETFD, urandom_fd, flags | FD_CLOEXEC)) {
                            close(urandom_fd);
                            urandom_fd = -1;
                        }
                    }
                }
            }
        }
        // If our workaround failed we'll still have urandom_fd = -1

        if (unlikely(urandom_fd < 0)) {
            goto out;
        }
    }

    while (length) {
        ssize_t rv = read(urandom_fd, bufp, length);
        if (rv < 0) {
            goto out;
        }
        length -= rv;
        bufp += rv;
    }

    success = true;
out:
    if (!success) {
        secureZero(buffer, length);
    }

    if (unlikely(mutex_error = pthread_mutex_unlock(&getentropy_mutex))) {
        errno = mutex_error;
        perror("pthread_mutex_unlock");
        abort(); // unexpected mutex failure - can't recover
    }

    return success;
}