diff options
Diffstat (limited to 'open-vm-tools/lib/sslDirect/sslDirect.c')
-rw-r--r-- | open-vm-tools/lib/sslDirect/sslDirect.c | 791 |
1 files changed, 791 insertions, 0 deletions
diff --git a/open-vm-tools/lib/sslDirect/sslDirect.c b/open-vm-tools/lib/sslDirect/sslDirect.c new file mode 100644 index 00000000..a86722f8 --- /dev/null +++ b/open-vm-tools/lib/sslDirect/sslDirect.c @@ -0,0 +1,791 @@ +/********************************************************* + * Copyright (C) 2014-2015 VMware, Inc. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation version 2.1 and no later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the Lesser GNU General Public + * License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + *********************************************************/ + +/* + * sslDirect.c -- + * + * Mostly direct call stubs for AsyncSocket SSL functions. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <errno.h> +#include <stdarg.h> + +#include "str.h" + +#ifdef _WIN32 +#include <winsock2.h> +#else +#include <sys/socket.h> +#include <fcntl.h> +#include <unistd.h> +#endif + +#include "log.h" +#include "debug.h" +#include "err.h" +#include "msg.h" +#include "sslDirect.h" +#include "vm_assert.h" + +#define LOGLEVEL_MODULE SSLDirect +#include "loglevel_user.h" + +#include <openssl/err.h> +#include <openssl/ssl.h> +#include <openssl/rand.h> +#include <openssl/engine.h> + +#define SSL_LOG(x) Debug x + +struct SSLSockStruct { + SSL *sslCnx; + int fd; + Bool encrypted; + Bool closeFdOnShutdown; + Bool connectionFailed; +#ifdef __APPLE__ + Bool loggedKernelReadBug; +#endif + + int sslIOError; +}; + + +static Bool SSLModuleInitialized = FALSE; + +#ifndef SOCKET_ERROR +#define SOCKET_ERROR (-1) +#endif + +enum { + SSL_SOCK_WANT_RETRY, + SSL_SOCK_LOST_CONNECTION, +}; + +/* + *---------------------------------------------------------------------- + * + * SSLPrintErrors + * + * Print out all the errors in the SSL error queue. + * + * Results: + * None + * + * Side effects: + * Clears out the SSL error stack + * + *---------------------------------------------------------------------- + */ + +static void +SSLPrintErrors(uint32 logLevel) // IN: +{ + /* + * Code inspection of the unsafe ERR_error_string function + * implementation in crypto/err.c shows a static buffer of 256 + * characters. Presumably most messages will fit in that buffer. + */ + enum { SSL_ERR_MAX_STRING = 256 }; + + int errNum; + char errString[SSL_ERR_MAX_STRING]; + while ((errNum = ERR_get_error())) { + errString[0] = '\0'; + ERR_error_string_n(errNum, errString, ARRAYSIZE(errString)); + /* TODO: use LogV */ + if (logLevel == VMW_LOG_WARNING) { + Warning("SSL Error: %s\n", errString); + } else { + Log("SSL Error: %s\n", errString); + } + } +} + + +/* + *---------------------------------------------------------------------- + * + * SSLSetSystemError + * + * Maps the ssl error state into an appropriate errno / WSA error. + * + * Results: + * None + * + * Side effects: + * None + *---------------------------------------------------------------------- + */ + +static void +SSLSetSystemError(int err) +{ + switch (err) { + case SSL_SOCK_WANT_RETRY: +#ifdef _WIN32 + WSASetLastError(WSAEWOULDBLOCK); +#else + errno = EAGAIN; +#endif + break; + case SSL_SOCK_LOST_CONNECTION: + /* + * no good way to know what the real error was (could have been + * a failure to load certificates in an accept), so return + * something generic. + */ +#ifdef _WIN32 + WSASetLastError(WSAEACCES); +#else + errno = EPERM; +#endif + break; + default: + NOT_REACHED(); + } +} + + +/* + *---------------------------------------------------------------------- + * + * SSLSetErrorState + * + * Each ssl read / write could result in several reads and writes on + * the underlying socket. In this case the actual value for errno + * will not be correct. Manually setup the error value so that + * clients will do the right thing. + * + * XXX: Mapping the SSL_ERROR_WANT_<something> errors to a single error code + * is not good. Applications using non-blocking IO would not know whether + * they should put the FD in a read wait or a write wait. Note that SSL_read + * can return SSL_ERROR_WANT_WRITE and SSL_write may return + * SSL_ERROR_WANT_READ. + * + * Results: + * None + * + * Side effects: + * errno / windows error might be set. + * + *---------------------------------------------------------------------- + */ + +static int +SSLSetErrorState(SSL *ssl, + int result) +{ + int sslError = SSL_get_error(ssl, result); + switch (sslError) { + case SSL_ERROR_NONE: + SSL_LOG(("SSL: action success, %d bytes\n", result)); + break; + case SSL_ERROR_ZERO_RETURN: + SSL_LOG(("SSL: Zero return\n")); + break; + case SSL_ERROR_WANT_READ: + SSL_LOG(("SSL: Want read\n")); + SSLSetSystemError(SSL_SOCK_WANT_RETRY); + break; + case SSL_ERROR_WANT_WRITE: + SSL_LOG(("SSL: Want write\n")); + SSLSetSystemError(SSL_SOCK_WANT_RETRY); + break; + case SSL_ERROR_WANT_X509_LOOKUP: + SSL_LOG(("SSL: want x509 lookup\n")); + break; + case SSL_ERROR_SYSCALL: + SSL_LOG(("SSL: syscall error\n")); + SSLPrintErrors(VMW_LOG_INFO); + if (result == 0) { + Log("SSL: EOF in violation of protocol\n"); + } else { + Log("SSL: syscall error %d: %s\n", Err_Errno(), Err_ErrString()); + } + break; + case SSL_ERROR_SSL: + Warning("SSL: Unknown SSL Error\n"); + SSLPrintErrors(VMW_LOG_INFO); + break; + } + return sslError; +} + + +/* + *---------------------------------------------------------------------- + * + * SSL_Init -- + * + * Initializes the SSL library and prepares the session context. + * + * Results: + * None. + * + * Side effects: + * Lots. + *---------------------------------------------------------------------- + */ + +void +SSL_Init(SSLLibFn *getLibFn, // Ignored + const char *defaultLib, // Ignored + const char *name) // Ignored +{ + /* + * Silently ignore any attempts to initialize module more than once. + */ + if (SSLModuleInitialized) { + return; + } + + SSL_library_init(); + SSL_load_error_strings(); + + OpenSSL_add_all_algorithms(); + + /* + * Force the PRNG to be initialized early, as opposed to at the + * time when the SSL connection is made. A call to RAND_status + * forces this initialization to happen. Initializing the PRNG + * as early as possible in the process makes it take much less + * time (e.g. 1sec. vs. sometimes 20sec.) compared to + * initializing it later in the process, as may be the case on + * the first SSL_accept() or SSL_connect(). That's because the + * PRNG initialization walks the process heap and the total heap + * is smaller at startup. + * + * If SSL_InitEx could not be called early enough in the + * process, then the caller could just call RAND_status() by + * itself. Only the first call to RAND_status will have the side + * effect of initializing the PRNG, so calling it subsequently + * would be a NOOP. + */ + RAND_status(); + + ENGINE_register_all_ciphers(); + ENGINE_register_all_digests(); + + SSLModuleInitialized = TRUE; +} + + +/* + *---------------------------------------------------------------------- + * + * SSL_New() + * + * Results: + * Returns a freshly allocated SSLSock structure. + * + * Side effects: + * None + * + *---------------------------------------------------------------------- + */ + +SSLSock +SSL_New(int fd, // IN + Bool closeFdOnShutdown) // IN +{ + SSLSock sslConnection; + + sslConnection = (SSLSock)calloc(1, sizeof(struct SSLSockStruct)); + VERIFY(sslConnection); + sslConnection->fd = fd; + sslConnection->closeFdOnShutdown = closeFdOnShutdown; + + return sslConnection; +} + + +/* + *---------------------------------------------------------------------- + * + * SSL_SetupAcceptWithContext() + * + * Setting up the ssl connection and states to do a SSL accept operation + * + * Results: + * Returns TRUE on success, FALSE on failure. + * + * Side effects: + * The server's certificate & private key may be loaded from disk. + * + *---------------------------------------------------------------------- + */ + +Bool SSL_SetupAcceptWithContext(SSLSock sSock, // IN: SSL socket + void *ctx) // IN: OpenSSL context (SSL_CTX *) +{ + Bool ret = TRUE; + + ASSERT(SSLModuleInitialized); + ASSERT(sSock); + ASSERT(ctx); + + sSock->sslCnx = SSL_new(ctx); + if (!sSock->sslCnx) { + SSLPrintErrors(VMW_LOG_WARNING); + Warning("Error Creating SSL connection structure\n"); + sSock->connectionFailed = TRUE; + ret = FALSE; + goto end; + } + SSL_set_accept_state(sSock->sslCnx); + + SSL_LOG(("SSL: ssl created\n")); + if (!SSL_set_fd(sSock->sslCnx, sSock->fd)) { + SSLPrintErrors(VMW_LOG_WARNING); + Warning("Error setting fd for SSL connection\n"); + sSock->connectionFailed = TRUE; + ret = FALSE; + goto end; + } + SSL_LOG(("SSL: fd set done\n")); + + sSock->encrypted = TRUE; + +end: + return ret; +} + + +/* + *---------------------------------------------------------------------- + * + * SSL_Read -- + * + * Functional equivalent of the read() syscall. + * + * Results: + * Returns the number of bytes read, or -1 on error. The + * data read will be placed in buf. + * + * Side effects: + * + * + *---------------------------------------------------------------------- + */ + +ssize_t +SSL_Read(SSLSock ssl, // IN + char *buf, // OUT + size_t num) // IN +{ + int ret; + ASSERT(ssl); + + if (ssl->connectionFailed) { + SSLSetSystemError(SSL_SOCK_LOST_CONNECTION); + ret = SOCKET_ERROR; + goto end; + } + + if (ssl->encrypted) { + int result = SSL_read(ssl->sslCnx, buf, (int)num); + + ssl->sslIOError = SSLSetErrorState(ssl->sslCnx, result); + if (ssl->sslIOError != SSL_ERROR_NONE) { + SSL_LOG(("SSL: Read(%d, %p, %"FMTSZ"u): %d\n", + ssl->fd, buf, num, result)); + result = SOCKET_ERROR; + } + ret = result; + } else { + ret = SSLGeneric_read(ssl->fd, buf, (int)num); + +#ifdef __APPLE__ + /* + * Detect bug 161237 (Apple bug 5202831), which should no longer be + * happening due to a workaround in our code. + * + * There is a bug on Mac OS 10.4 and 10.5 where passing an fd + * over a socket can result in that fd being in an inconsistent state. + * We can detect when this happens when read(2) returns zero + * even if the other end of the socket is not disconnected. + * We verify this by calling write(ssl->fd, "", 0) and + * see if it is okay. (If the socket was really closed, it would + * return -1 with errno==EPIPE.) + */ + if (ret == 0) { + ssize_t writeRet; +#ifdef VMX86_DEBUG + struct stat statBuffer; + + /* + * Make sure we're using a socket. + */ + ASSERT((fstat(ssl->fd, &statBuffer) == 0) && + ((statBuffer.st_mode & S_IFSOCK) == S_IFSOCK)); + +#endif + writeRet = write(ssl->fd, "", 0); + if (writeRet == 0) { + /* + * The socket is still good. read(2) should not have returned zero. + */ + if (! ssl->loggedKernelReadBug) { + Log("Error: Encountered Apple bug #5202831. Disconnecting.\n"); + ssl->loggedKernelReadBug = TRUE; + } + } + } +#endif + } + + end: + return ret; +} + + +/* + *---------------------------------------------------------------------- + * + * SSL_RecvDataAndFd -- + * + * recvmsg wrapper which can receive only file descriptors, not other + * control data. + * + * Results: + * Returns the number of bytes received, or -1 on error. The + * data read will be placed in buf. *fd is either -1 if no fd was + * received, or descriptor... + * + * Side effects: + * + * + *---------------------------------------------------------------------- + */ + +ssize_t +SSL_RecvDataAndFd(SSLSock ssl, // IN/OUT: socket + char *buf, // OUT: buffer + size_t num, // IN: length of buffer + int *fd) // OUT: descriptor received +{ + int ret; + ASSERT(ssl); + ASSERT(fd); + + *fd = -1; + if (ssl->connectionFailed) { + SSLSetSystemError(SSL_SOCK_LOST_CONNECTION); + ret = SOCKET_ERROR; + goto end; + } + + /* + * No fd passing over SSL or Windows. Windows needs different code. + */ +#ifdef _WIN32 + return SSL_Read(ssl, buf, num); +#else + if (ssl->encrypted) { + int result = SSL_read(ssl->sslCnx, buf, (int)num); + + ssl->sslIOError = SSLSetErrorState(ssl->sslCnx, result); + if (ssl->sslIOError != SSL_ERROR_NONE) { + SSL_LOG(("SSL: Read(%d, %p, %"FMTSZ"u): %d\n", + ssl->fd, buf, num, result)); + result = SOCKET_ERROR; + } + ret = result; + } else { + struct iovec iov; + struct msghdr msg = { 0 }; + uint8 cmsgBuf[CMSG_SPACE(sizeof(int))]; + + iov.iov_base = buf; + iov.iov_len = num; + msg.msg_name = NULL; + msg.msg_namelen = 0; + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + msg.msg_control = cmsgBuf; + msg.msg_controllen = sizeof cmsgBuf; + ret = SSLGeneric_recvmsg(ssl->fd, &msg, 0); + if (ret >= 0 && msg.msg_controllen != 0) { + struct cmsghdr *cmsg; + + for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) { + if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) { + int receivedFd = *(int *)CMSG_DATA(cmsg); + + ASSERT(*fd == -1); + *fd = receivedFd; + } + } + } + } +#endif + + end: + return ret; +} + + +/* + *---------------------------------------------------------------------- + * + * SSL_Write() + * + * Functional equivalent of the write() syscall. + * + * Results: + * Returns the number of bytes written, or -1 on error. + * + * Side effects: + * + *---------------------------------------------------------------------- + */ + +ssize_t +SSL_Write(SSLSock ssl, // IN + const char *buf, // IN + size_t num) // IN +{ + int ret; + ASSERT(ssl); + + if (ssl->connectionFailed) { + SSLSetSystemError(SSL_SOCK_LOST_CONNECTION); + ret = SOCKET_ERROR; + goto end; + } + if (ssl->encrypted) { + int result = SSL_write(ssl->sslCnx, buf, (int)num); + + ssl->sslIOError = SSLSetErrorState(ssl->sslCnx, result); + if (ssl->sslIOError != SSL_ERROR_NONE) { + SSL_LOG(("SSL: Write(%d)\n", ssl->fd)); + result = SOCKET_ERROR; + } + ret = result; + } else { + ret = SSLGeneric_write(ssl->fd, buf, (int)num); + } + + end: + return ret; +} + + +/* + *---------------------------------------------------------------------- + * + * SSL_Pending() + * + * Functional equivalent of select when SSL is enabled + * + * Results: + * Obtain number of readable bytes buffered in an SSL object if SSL + * is enabled, otherwise, return 0 + * + * Side effects: + * + *---------------------------------------------------------------------- + */ + +int +SSL_Pending(SSLSock ssl) // IN +{ + int ret; + ASSERT(ssl); + + if (ssl->encrypted) { + ret = SSL_pending(ssl->sslCnx); + } else { + ret = 0; + } + + return ret; +} + + +/* + *---------------------------------------------------------------------- + * + * SSL_SetCloseOnShutdownFlag() + * + * Sets closeFdOnShutdown flag. + * + * Results: + * None. Always succeeds. Do not call close/closesocket on + * the fd after this, call SSL_Shutdown() instead. + * + * Side effects: + * + *---------------------------------------------------------------------- + */ + +void +SSL_SetCloseOnShutdownFlag(SSLSock ssl) // IN +{ + ASSERT(ssl); + ssl->closeFdOnShutdown = TRUE; +} + + +/* + *---------------------------------------------------------------------- + * + * SSL_Shutdown() + * + * Functional equivalent of the close() syscall. Does + * not close the actual fd used for the connection. + * + * + * Results: + * 0 on success, -1 on failure. + * + * Side effects: + * closes the connection, freeing up the memory associated + * with the passed in socket object + * + *---------------------------------------------------------------------- + */ + +int +SSL_Shutdown(SSLSock ssl) // IN +{ + int retVal = 0; + ASSERT(ssl); + + SSL_LOG(("SSL: Starting shutdown for %d\n", ssl->fd)); + if (ssl->encrypted) { + /* since quiet_shutdown is set, SSL_shutdown always succeeds */ + SSL_shutdown(ssl->sslCnx); + } + if (ssl->sslCnx) { + SSL_free(ssl->sslCnx); + } + + if (ssl->closeFdOnShutdown) { + SSL_LOG(("SSL: Trying to close %d\n", ssl->fd)); + /* + * Apparently in the past coverity has complained about the lack + * of shutdown() before close() here. However we only want to + * shut down the SSL layer, not the socket layer since authd may + * handoff the fd to another process. + */ + retVal = SSLGeneric_close(ssl->fd); + } + + free(ssl); + SSL_LOG(("SSL: shutdown done\n")); + + return retVal; +} + + +/* + *---------------------------------------------------------------------- + * + * SSL_GetFd() + * + * Returns an SSL socket's file descriptor or handle. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +int +SSL_GetFd(SSLSock ssl) // IN +{ + ASSERT(ssl); + + return ssl->fd; +} + + +/* + *---------------------------------------------------------------------- + * + * SSL_WantRead() + * + * Wrapper around SSL_want_read. + * + * Results: + * That of SSL_want_read. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +int +SSL_WantRead(const SSLSock ssl) +{ + ASSERT(ssl); + ASSERT(ssl->sslCnx); + + return SSL_want_read(ssl->sslCnx); +} + + +/* + *---------------------------------------------------------------------- + * + * SSL_TryCompleteAccept() + * + * Call SSL_Accept() to start or redrive the SSL accept operation. + * Nonblocking. + * + * Results: + * > 0 if the SSL_accept completed successfully + * = 0 if the SSL_accept need a redrive + * < 0 if an error occurred + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +int +SSL_TryCompleteAccept(SSLSock ssl) // IN +{ + int sslRet; + + ASSERT(ssl); + ASSERT(ssl->sslCnx); + + ERR_clear_error(); + sslRet = SSL_accept(ssl->sslCnx); + ssl->sslIOError = SSL_get_error(ssl->sslCnx, sslRet); + + switch (ssl->sslIOError) { + case SSL_ERROR_NONE: + return 1; + case SSL_ERROR_WANT_READ: + case SSL_ERROR_WANT_WRITE: + return 0; + default: + ssl->connectionFailed = TRUE; + SSLPrintErrors(VMW_LOG_INFO); + return -1; + } +} + |