2N/A/*
2N/A * CDDL HEADER START
2N/A *
2N/A * The contents of this file are subject to the terms of the
2N/A * Common Development and Distribution License (the "License").
2N/A * You may not use this file except in compliance with the License.
2N/A *
2N/A * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
2N/A * or http://www.opensolaris.org/os/licensing.
2N/A * See the License for the specific language governing permissions
2N/A * and limitations under the License.
2N/A *
2N/A * When distributing Covered Code, include this CDDL HEADER in each
2N/A * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
2N/A * If applicable, add the following below this CDDL HEADER, with the
2N/A * fields enclosed by brackets "[]" replaced with your own identifying
2N/A * information: Portions Copyright [yyyy] [name of copyright owner]
2N/A *
2N/A * CDDL HEADER END
2N/A */
2N/A
2N/A/*
2N/A * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
2N/A */
2N/A
2N/A#include <openssl/ssl.h>
2N/A#include <openssl/err.h>
2N/A#include <sys/time.h>
2N/A#include <stdlib.h>
2N/A#include <stdio.h>
2N/A#include <string.h>
2N/A#include <assert.h>
2N/A#include <stdarg.h>
2N/A#include <stddef.h>
2N/A#include <unistd.h>
2N/A#include <fcntl.h>
2N/A#include <pthread.h>
2N/A#include <poll.h>
2N/A
2N/A#include "adr_stream.h"
2N/A
2N/A/*
2N/A * Common adr_stream implementation
2N/A */
2N/A
2N/Astruct adr_stream {
2N/A void *astr_data;
2N/A ssize_t (*astr_read)(void *, char *, size_t);
2N/A ssize_t (*astr_write)(void *, const char *, size_t);
2N/A void (*astr_close)(void *);
2N/A void (*astr_free)(void *);
2N/A};
2N/A
2N/Aadr_stream_t *
2N/Aadr_stream_create(ssize_t (*readf)(void *, char *, size_t),
2N/A ssize_t (*writef)(void *, const char *, size_t),
2N/A void (*closef)(void *), void (*freef)(void *), void *data)
2N/A{
2N/A adr_stream_t *result = malloc(sizeof (adr_stream_t));
2N/A if (result == NULL) {
2N/A closef(data);
2N/A freef(data);
2N/A return (NULL);
2N/A }
2N/A result->astr_data = data;
2N/A result->astr_read = readf;
2N/A result->astr_write = writef;
2N/A result->astr_close = closef;
2N/A result->astr_free = freef;
2N/A return (result);
2N/A}
2N/A
2N/Assize_t
2N/Aadr_stream_read(adr_stream_t *str, char *buf, size_t len)
2N/A{
2N/A return (str->astr_read(str->astr_data, buf, len));
2N/A}
2N/A
2N/Assize_t
2N/Aadr_stream_write(adr_stream_t *str, const char *buf, size_t len)
2N/A{
2N/A return (str->astr_write(str->astr_data, buf, len));
2N/A}
2N/A
2N/Avoid
2N/Aadr_stream_close(adr_stream_t *str)
2N/A{
2N/A str->astr_close(str->astr_data);
2N/A}
2N/A
2N/Avoid
2N/Aadr_stream_free(adr_stream_t *str)
2N/A{
2N/A if (str->astr_free)
2N/A str->astr_free(str->astr_data);
2N/A free(str);
2N/A}
2N/A
2N/A
2N/A/*
2N/A * File descriptor stream implementation.
2N/A */
2N/A
2N/Atypedef struct adr_fdstream {
2N/A int infd;
2N/A int outfd;
2N/A boolean_t closed;
2N/A} adr_fdstream_t;
2N/A
2N/Astatic ssize_t
2N/Aadr_fdstream_read(void *data, char *buf, size_t len)
2N/A{
2N/A adr_fdstream_t *fdstr = data;
2N/A return (read(fdstr->infd, buf, len));
2N/A}
2N/A
2N/Astatic ssize_t
2N/Aadr_fdstream_write(void *data, const char *buf, size_t len)
2N/A{
2N/A adr_fdstream_t *fdstr = data;
2N/A return (write(fdstr->outfd, buf, len));
2N/A}
2N/A
2N/Astatic void
2N/Aadr_fdstream_close(void *data)
2N/A{
2N/A adr_fdstream_t *fdstr = data;
2N/A int fd;
2N/A
2N/A if ((fd = open("/dev/null", O_RDWR)) != -1) {
2N/A (void) dup2(fd, fdstr->infd);
2N/A if (fdstr->infd != fdstr->outfd)
2N/A (void) dup2(fd, fdstr->outfd);
2N/A (void) close(fd);
2N/A }
2N/A fdstr->closed = B_TRUE;
2N/A}
2N/A
2N/Astatic void
2N/Aadr_fdstream_free(void *data)
2N/A{
2N/A adr_fdstream_t *fdstr = data;
2N/A (void) close(fdstr->infd);
2N/A if (fdstr->infd != fdstr->outfd)
2N/A (void) close(fdstr->outfd);
2N/A free(data);
2N/A}
2N/A
2N/Aadr_stream_t *
2N/Aadr_stream_create_fds(int infd, int outfd)
2N/A{
2N/A adr_stream_t *result;
2N/A adr_fdstream_t *fdstr = malloc(sizeof (adr_fdstream_t));
2N/A
2N/A if (fdstr == NULL) {
2N/A (void) close(infd);
2N/A if (infd != outfd)
2N/A (void) close(outfd);
2N/A return (NULL);
2N/A }
2N/A fdstr->infd = infd;
2N/A fdstr->outfd = outfd;
2N/A fdstr->closed = B_FALSE;
2N/A
2N/A if ((result = adr_stream_create(adr_fdstream_read, adr_fdstream_write,
2N/A adr_fdstream_close, adr_fdstream_free, fdstr)) == NULL)
2N/A return (NULL);
2N/A
2N/A return (result);
2N/A}
2N/A
2N/Aadr_stream_t *
2N/Aadr_stream_create_fd(int fd)
2N/A{
2N/A return (adr_stream_create_fds(fd, fd));
2N/A}
2N/A
2N/A
2N/Astatic pthread_mutex_t *crypto_locks;
2N/A
2N/Astatic unsigned long
2N/Aadr_ssl_id_function(void)
2N/A{
2N/A return (pthread_self());
2N/A}
2N/A
2N/A/* ARGSUSED */
2N/Astatic void
2N/Aadr_ssl_locking_function(int mode, int n, const char *file, int line)
2N/A{
2N/A if (mode & CRYPTO_LOCK)
2N/A (void) pthread_mutex_lock(&crypto_locks[n]);
2N/A else
2N/A (void) pthread_mutex_unlock(&crypto_locks[n]);
2N/A}
2N/A
2N/Avoid
2N/Aadr_ssl_init(void)
2N/A{
2N/A crypto_locks = malloc(CRYPTO_num_locks() * sizeof (pthread_mutex_t));
2N/A if (crypto_locks == NULL)
2N/A abort();
2N/A for (int i = 0; i < CRYPTO_num_locks(); i++)
2N/A (void) pthread_mutex_init(&crypto_locks[i], NULL);
2N/A CRYPTO_set_locking_callback(adr_ssl_locking_function);
2N/A CRYPTO_set_id_callback(adr_ssl_id_function);
2N/A}
2N/A
2N/A/*
2N/A * SSL stream implementation
2N/A *
2N/A * Neither documentation nor internet chatter is clear on whether it is
2N/A * legal to call SSL_write after an SSL_read that returns
2N/A * SSL_ERROR_WANT_* before completing the SSL_read (and vice versa),
2N/A * but I don't see how the interfaces are otherwise usable without
2N/A * introducing cross-connection deadlocks.
2N/A *
2N/A * To avoid the single-side deadlock situation where one thread reads
2N/A * socket data between when the other drops its lock and starts to poll
2N/A * for it, we call poll with a 5 second timeout. This limits the time
2N/A * spent polling (in the traditional sense) the socket, and lets us to
2N/A * check every once in a while in case this rare situation arises, all
2N/A * without the per-connection overhead more precise solutions require.
2N/A */
2N/A
2N/Atypedef struct adr_sslstream {
2N/A SSL *ssl;
2N/A int fd;
2N/A boolean_t closed;
2N/A pthread_mutex_t lock;
2N/A} adr_sslstream_t;
2N/A
2N/Astatic void
2N/Aadr_sslstream_close_locked(adr_sslstream_t *sslstr)
2N/A{
2N/A if (!sslstr->closed) {
2N/A int fd;
2N/A
2N/A if ((fd = open("/dev/null", O_RDWR)) != -1) {
2N/A (void) dup2(fd, sslstr->fd);
2N/A (void) close(fd);
2N/A }
2N/A sslstr->closed = B_TRUE;
2N/A }
2N/A}
2N/A
2N/Astatic boolean_t
2N/Aadr_sslstream_wait(adr_sslstream_t *sslstr, int res)
2N/A{
2N/A if (res == -1) {
2N/A int err = SSL_get_error(sslstr->ssl, res);
2N/A
2N/A struct pollfd pfd;
2N/A pfd.fd = sslstr->fd;
2N/A if (err == SSL_ERROR_WANT_READ)
2N/A pfd.events = POLLIN | POLLRDNORM;
2N/A else if (err == SSL_ERROR_WANT_WRITE)
2N/A pfd.events = POLLOUT | POLLWRNORM;
2N/A else
2N/A goto out;
2N/A
2N/A /*
2N/A * We weren't able to read/write everything required.
2N/A * Poll for the ability to do so, and retry.
2N/A *
2N/A * Use a timeout in case a renegotiation caused another
2N/A * thread to read/write the data we were polling for.
2N/A * Much simpler than implementing a precise wakeup.
2N/A */
2N/A (void) pthread_mutex_unlock(&sslstr->lock);
2N/A if (poll(&pfd, 1, 5 * MILLISEC) >= 0)
2N/A return (B_TRUE);
2N/A (void) pthread_mutex_lock(&sslstr->lock);
2N/A }
2N/A
2N/Aout:
2N/A adr_sslstream_close_locked(sslstr);
2N/A (void) pthread_mutex_unlock(&sslstr->lock);
2N/A return (B_FALSE);
2N/A}
2N/A
2N/Astatic ssize_t
2N/Aadr_sslstream_read(void *data, char *buf, size_t len)
2N/A{
2N/A int res;
2N/A adr_sslstream_t *sslstr = data;
2N/A do {
2N/A (void) pthread_mutex_lock(&sslstr->lock);
2N/A /*
2N/A * We're done.
2N/A */
2N/A if (sslstr->closed) {
2N/A (void) pthread_mutex_unlock(&sslstr->lock);
2N/A return (0);
2N/A }
2N/A
2N/A ERR_clear_error();
2N/A res = SSL_read(sslstr->ssl, buf, len);
2N/A if (res > 0) {
2N/A (void) pthread_mutex_unlock(&sslstr->lock);
2N/A return (res);
2N/A }
2N/A
2N/A /* drops lock */
2N/A } while (adr_sslstream_wait(sslstr, res));
2N/A
2N/A return (res);
2N/A}
2N/A
2N/Astatic ssize_t
2N/Aadr_sslstream_write(void *data, const char *buf, size_t len)
2N/A{
2N/A int res;
2N/A adr_sslstream_t *sslstr = data;
2N/A do {
2N/A (void) pthread_mutex_lock(&sslstr->lock);
2N/A /*
2N/A * We're done.
2N/A */
2N/A if (sslstr->closed) {
2N/A (void) pthread_mutex_unlock(&sslstr->lock);
2N/A return (0);
2N/A }
2N/A
2N/A ERR_clear_error();
2N/A res = SSL_write(sslstr->ssl, buf, len);
2N/A if (res > 0) {
2N/A (void) pthread_mutex_unlock(&sslstr->lock);
2N/A return (res);
2N/A }
2N/A
2N/A /* drops lock */
2N/A } while (adr_sslstream_wait(sslstr, res));
2N/A
2N/A return (res);
2N/A}
2N/A
2N/Astatic void
2N/Aadr_sslstream_close(void *data)
2N/A{
2N/A adr_sslstream_t *sslstr = data;
2N/A (void) pthread_mutex_lock(&sslstr->lock);
2N/A adr_sslstream_close_locked(sslstr);
2N/A (void) pthread_mutex_unlock(&sslstr->lock);
2N/A}
2N/A
2N/Astatic void
2N/Aadr_sslstream_free(void *data)
2N/A{
2N/A adr_sslstream_t *sslstr = data;
2N/A SSL_free(sslstr->ssl);
2N/A (void) close(sslstr->fd);
2N/A free(data);
2N/A}
2N/A
2N/Aadr_stream_t *
2N/Aadr_stream_create_ssl(SSL *ssl, int fd)
2N/A{
2N/A adr_stream_t *result;
2N/A adr_sslstream_t *sslstr = malloc(sizeof (adr_sslstream_t));
2N/A
2N/A if (sslstr == NULL) {
2N/A SSL_free(ssl);
2N/A (void) close(fd);
2N/A return (NULL);
2N/A }
2N/A sslstr->ssl = ssl;
2N/A sslstr->fd = fd;
2N/A sslstr->closed = B_FALSE;
2N/A (void) pthread_mutex_init(&sslstr->lock, NULL);
2N/A
2N/A int flags = fcntl(fd, F_GETFL, 0);
2N/A (void) fcntl(fd, F_SETFL, flags | O_NONBLOCK);
2N/A
2N/A if ((result = adr_stream_create(adr_sslstream_read, adr_sslstream_write,
2N/A adr_sslstream_close, adr_sslstream_free, sslstr)) == NULL)
2N/A return (NULL);
2N/A
2N/A return (result);
2N/A}