Ejemplo n.º 1
0
/* Return -1 on hard error (abort), 0 on timeout, >= 1 on successful wakeup */
static int
_BIO_wait(BIO *cbio, int msecs)
{
        if (!BIO_should_retry(cbio)) {
                return (-1);
        }

        struct pollfd pfd;
        BIO_get_fd(cbio, &pfd.fd);
        pfd.events = 0;
        pfd.revents = 0;

        if (BIO_should_io_special(cbio)) {
                pfd.events = POLLOUT | POLLWRBAND;
        } else if (BIO_should_read(cbio)) {
                pfd.events = POLLIN | POLLPRI | POLLRDBAND;
        } else if (BIO_should_write(cbio)) {
                pfd.events = POLLOUT | POLLWRBAND;
        } else {
                return (-1);
        }

        if (msecs < 0) {
            /* POSIX requires -1 for "no timeout" although some libcs
               accept any negative value. */
            msecs = -1;
        }
        int result = poll(&pfd, 1, msecs);

        /* Timeout or poll internal error */
        if (result <= 0) {
                return (result);
        }

        /* Return 1 if the event was not an error */
        return (pfd.revents & pfd.events ? 1 : -1);
}
Ejemplo n.º 2
0
/**
 * oh_ssl_write
 * @bio:        pointer to a BIO as returned by oh_ssl_connect()
 * @buf:        buffer to write to the connection
 * @size:       number of bytes to be written
 * @timeout:    maximum number of seconds to wait for the remote host to
 *              accept the data, or zero to wait forever
 *
 * Write data to an existing SSL connection.
 *
 * Note that oh_ssl_read() and oh_ssl_write() have some subtle differences
 * in behavior.  While oh_ssl_read() returns as soon as it has data for the
 * caller, oh_ssl_write() does not return until all the bytes have been
 * written to the remote host.
 *
 * Return value: (as follows)
 *    0:        success
 *   -1:        error
 *   -2:        timeout
 **/
int             oh_ssl_write(BIO *bio, char *buf, int size, long timeout)
{
        SSL             *ssl;
        int             bytes;
        fd_set          readfds;
        fd_set          writefds;
        struct          timeval tv;
        int             write_wait;
        int             done;
        int             err;
        int             fd;
        int             sent;

        if (bio == NULL) {
                err("NULL bio in oh_ssl_write()");
                return(-1);
        }
        if (buf == NULL) {
                err("NULL buf in oh_ssl_write()");
                return(-1);
        }
        if (size <= 0) {
                err("inappropriate size in oh_ssl_write()");
                return(-1);
        }
        if (timeout < 0) {
                err("inappropriate timeout in oh_ssl_write()");
                return(-1);
        }

        /* Get underlying file descriptor, needed for select call */
        fd = BIO_get_fd(bio, NULL);
        if (fd == -1) {
                err("BIO doesn't seem to be initialized in oh_ssl_write()");
                return(-1);
        }

        /* We also need the SSL connection pointer */
        BIO_get_ssl(bio, &ssl);
        if (ssl == NULL) {
                err("BIO_get_ssl() failed");
                return(-1);
        }

        /* Because of SSL renegotiations, we may have to wait on a socket
         * read even though we're trying to do a write.  The initial value
         * of write_wait indicates that we're trying to write, but it can
         * be set to 0 if we end up waiting for a socket read.
         */
        write_wait = 1;
        done = 0;
        sent = 0;

        /* We have to loop on the write call, until everything gets written */
        while (! done) {
                /* First, we need to wait until something happens on the
                 * underlying socket.  We are either waiting for a read
                 * or a write (but not both).
                 */
                FD_ZERO(&readfds);
                FD_ZERO(&writefds);
                if (write_wait) {
                        FD_SET(fd, &writefds);
                }
                else {
                        FD_SET(fd, &readfds);
                }
                if (timeout) {
                        tv.tv_sec = timeout;
                        tv.tv_usec = 0;
                        err = select(fd + 1, &readfds, &writefds, NULL, &tv);
                }
                else {                  /* No timeout */
                        err = select(fd + 1, &readfds, &writefds, NULL, NULL);
                }

                /* Evaluate select() return code */
                if (err < 0) {
                        err("error during select()");
                        return(-1);
                }
                if (err == 0) {
                        return(-2);     /* Timeout */
                }

                /* The socket is ready.  Ready to try (or re-try) the write
                 * call.
                 */
                bytes = SSL_write(ssl, buf + sent, size - sent);
                switch (SSL_get_error(ssl, bytes)) {
                        case SSL_ERROR_NONE:
                                /* No error */
                                sent += bytes;
                                if (sent == size) {
                                        done = 1;
                                }
                                break;
                        case SSL_ERROR_ZERO_RETURN:
                                /* Connection was closed.  Since we're trying
                                 * to write, this is an error condition.
                                 */
                                err("remote host unexpectedly closed"
                                    " the connection");
                                return(-1);
                        case SSL_ERROR_WANT_READ:
                                write_wait = 0;
                                break;
                        case SSL_ERROR_WANT_WRITE:
                                write_wait = 1;
                                break;
                        default:
                                /* Some other sort of error */
                                err("error %d from SSL_write", bytes);
                                return(-1);
                }
        }

        return(0);
}
Ejemplo n.º 3
0
/**
 * oh_ssl_read
 * @bio:        pointer to a BIO as returned by oh_ssl_connect()
 * @buf:        buffer for the data which is read from the connection
 * @size:       maximum number of bytes to be read into buf
 * @timeout:    maximum number of seconds to wait for input to be available,
 *              or zero to wait forever
 *
 * Read from an existing SSL connection.  The data and number of bytes read
 * are returned.
 *
 * Note that oh_ssl_read() and oh_ssl_write() have some subtle differences
 * in behavior.  While oh_ssl_write() will try to write all the bytes,
 * oh_ssl_read() will return as soon as it has read some data.
 *
 * Return value: (as follows)
 *   >0:        number of bytes read
 *    0:        nothing more to read; remote host closed the connection
 *   -1:        SSL or other error
 *   -2:        Timeout
 **/
int             oh_ssl_read(BIO *bio, char *buf, int size, long timeout)
{
        SSL             *ssl;
        int             bytes = 0;
        fd_set          readfds;
        fd_set          writefds;
        struct          timeval tv;
        int             read_wait;
        int             done;
        int             err;
        int             fd;

        if (bio == NULL) {
                err("NULL bio in oh_ssl_read()");
                return(-1);
        }
        if (buf == NULL) {
                err("NULL buf in oh_ssl_read()");
                return(-1);
        }
        if (size <= 0) {
                err("inappropriate size in oh_ssl_read()");
                return(-1);
        }
        if (timeout < 0) {
                err("inappropriate timeout in oh_ssl_read()");
                return(-1);
        }

        /* Get underlying file descriptor, needed for select call */
        fd = BIO_get_fd(bio, NULL);
        if (fd == -1) {
                err("BIO doesn't seem to be initialized in oh_ssl_read()");
                return(-1);
        }

        /* We also need the SSL connection pointer */
        BIO_get_ssl(bio, &ssl);
        if (ssl == NULL) {
                err("BIO_get_ssl() failed");
                return(-1);
        }

        /* Because of SSL renegotiations, we may have to wait on a socket
         * write even though we're trying to do a read.  The initial value
         * of read_wait indicates that we're trying to read, but it can be
         * set to 0 if we end up waiting for a socket write.
         */
        read_wait = 1;
        done = 0;

        /* We have to loop on the read call, until we get something we
         * can return to the user.
         */
        while (! done) {
                /* First, we need to wait until something happens on the
                 * underlying socket.  We are either waiting for a read
                 * or a write (but not both).
                 */
                FD_ZERO(&readfds);
                FD_ZERO(&writefds);
                if (read_wait) {
                        FD_SET(fd, &readfds);
                }
                else {
                        FD_SET(fd, &writefds);
                }
                if (timeout) {
                        tv.tv_sec = timeout;
                        tv.tv_usec = 0;
                        err = select(fd + 1, &readfds, &writefds, NULL, &tv);
                }
                else {                  /* No timeout */
                        err = select(fd + 1, &readfds, &writefds, NULL, NULL);
                }

                /* Evaluate select() return code */
                if (err < 0) {
                        err("error during select()");
                        return(-1);
                }
                if (err == 0) {
                        return(-2);     /* Timeout */
                }

                /* The socket has something.  Ready to try (or re-try)
                 * the read call.
                 */
                bytes = SSL_read(ssl, buf, size);
                switch (SSL_get_error(ssl, bytes)) {
                        case SSL_ERROR_NONE:
                                /* No error */
                                if (bytes) {
                                        done = 1;
                                }
                                break;
                        case SSL_ERROR_ZERO_RETURN:
                                /* Connection was closed.  For this case,
                                 * since it's normal for the remote host
                                 * to close when it's done, we'll not signal
                                 * any error, but will return zero bytes.
                                 */
                                return(0);
                        case SSL_ERROR_WANT_READ:
                                read_wait = 1;
                                break;
                        case SSL_ERROR_WANT_WRITE:
                                read_wait = 0;
                                break;
                        default:
                                /* Some other sort of error */
                                err("error %d from SSL_read", bytes);
                                return(-1);
                }
        }

        return(bytes);
}
Ejemplo n.º 4
0
/**
 * oh_ssl_connect
 * @hostname:   Name of target host.  Format:
 *                  "hostname:port" or "IPaddress:port"
 * @ctx:        pointer to SSL_CTX as returned by oh_ssl_ctx_init()
 * @timeout:    maximum number of seconds to wait for a connection to
 *              hostname, or zero to wait forever
 *
 * Create and open a new ssl conection to the specified host.
 *
 * Return value: pointer to BIO, or NULL for failure
 **/
BIO             *oh_ssl_connect(char *hostname, SSL_CTX *ctx, long timeout)
{
        BIO             *bio;
        SSL             *ssl;
        fd_set          readfds;
        fd_set          writefds;
        struct timeval  tv;
        int             fd;
        int             err;

        if (hostname == NULL) {
                err("NULL hostname in oh_ssl_connect()");
                return(NULL);
        }
        if (ctx == NULL) {
                err("NULL ctx in oh_ssl_connect()");
                return(NULL);
        }
        if (timeout < 0) {
                err("inappropriate timeout in oh_ssl_connect()");
                return(NULL);
        }

        /* Start with a new SSL BIO */
        bio = BIO_new_ssl_connect(ctx);
        if (bio == NULL) {
                err("BIO_new_ssl_connect() failed");
                return(NULL);
        }

        /* Set up connection parameters for this BIO */
        BIO_set_conn_hostname(bio, hostname);
        BIO_set_nbio(bio, 1);           /* Set underlying socket to
                                         * non-blocking mode
                                         */

        /* Set up SSL session parameters */
        BIO_get_ssl(bio, &ssl);
        if (ssl == NULL) {
                err("BIO_get_ssl() failed");
                BIO_free_all(bio);
                return(NULL);
        }
        SSL_set_mode(ssl, SSL_MODE_ENABLE_PARTIAL_WRITE);

        /* Ready to open the connection.  Note that this call will probably
         * take a while, so we need to retry it, watching for a timeout.
         */
        while (1) {
                if (BIO_do_connect(bio) == 1) {
                        break;          /* Connection established */
                }
                if (! BIO_should_retry(bio)) { /* Hard error? */
                        err("BIO_do_connect() failed");
                        err("SSL error: %s",
                            ERR_reason_error_string(ERR_get_error()));
                        BIO_free_all(bio);
                        return(NULL);
                }

                /* Wait until there's a change in the socket's status or until
                 * the timeout period.
                 *
                 * Get underlying file descriptor, needed for the select call.
                 */
                fd = BIO_get_fd(bio, NULL);
                if (fd == -1) {
                        err("BIO isn't initialized in oh_ssl_connect()");
                        BIO_free_all(bio);
                        return(NULL);
                }

                FD_ZERO(&readfds);
                FD_ZERO(&writefds);
                if (BIO_should_read(bio)) {
                        FD_SET(fd, &readfds);
                }
                else if (BIO_should_write(bio)) {
                        FD_SET(fd, &writefds);
                }
                else {                  /* This is BIO_should_io_special().
                                         * Not sure what "special" needs to
                                         * wait for, but both read and write
                                         * seems to work without unnecessary
                                         * retries.
                                         */
                        FD_SET(fd, &readfds);
                        FD_SET(fd, &writefds);
                }
                if (timeout) {
                        tv.tv_sec = timeout;
                        tv.tv_usec = 0;
                        err = select(fd + 1, &readfds, &writefds, NULL, &tv);
                }
                else {                  /* No timeout */
                        err = select(fd + 1, &readfds, &writefds, NULL, NULL);
                }

                /* Evaluate select() return code */
                if (err < 0) {
                        err("error during select()");
                        BIO_free_all(bio);
                        return(NULL);
                }
                if (err == 0) {
                        err("connection timeout to %s", hostname);
                        BIO_free_all(bio);
                        return(NULL);   /* Timeout */
                }
        }

        /* TODO: Do I need to set the client or server mode here?  I don't
         * think so.
         */

        return(bio);
}
Ejemplo n.º 5
0
int tls_do_handshake(rdpTls* tls, BOOL clientMode)
{
	CryptoCert cert;
	int verify_status, status;

	do
	{
#ifdef HAVE_POLL_H
		struct pollfd pollfds;
#else
		struct timeval tv;
		fd_set rset;
#endif
		int fd;

		status = BIO_do_handshake(tls->bio);

		if (status == 1)
			break;

		if (!BIO_should_retry(tls->bio))
			return -1;

		/* we select() only for read even if we should test both read and write
		 * depending of what have blocked */
		fd = BIO_get_fd(tls->bio, NULL);

		if (fd < 0)
		{
			DEBUG_WARN( "%s: unable to retrieve BIO fd\n", __FUNCTION__);
			return -1;
		}

#ifdef HAVE_POLL_H
		pollfds.fd = fd;
		pollfds.events = POLLIN;
		pollfds.revents = 0;

		do
		{
			status = poll(&pollfds, 1, 10 * 1000);
		}
		while ((status < 0) && (errno == EINTR));
#else
		FD_ZERO(&rset);
		FD_SET(fd, &rset);
		tv.tv_sec = 0;
		tv.tv_usec = 10 * 1000; /* 10ms */

		status = _select(fd + 1, &rset, NULL, NULL, &tv);
#endif
		if (status < 0)
		{
			DEBUG_WARN( "%s: error during select()\n", __FUNCTION__);
			return -1;
		}
	}
	while (TRUE);

	cert = tls_get_certificate(tls, clientMode);
	if (!cert)
	{
		DEBUG_WARN( "%s: tls_get_certificate failed to return the server certificate.\n", __FUNCTION__);
		return -1;
	}

	tls->Bindings = tls_get_channel_bindings(cert->px509);
	if (!tls->Bindings)
	{
		DEBUG_WARN( "%s: unable to retrieve bindings\n", __FUNCTION__);
		verify_status = -1;
		goto out;
	}

	if (!crypto_cert_get_public_key(cert, &tls->PublicKey, &tls->PublicKeyLength))
	{
		DEBUG_WARN( "%s: crypto_cert_get_public_key failed to return the server public key.\n", __FUNCTION__);
		verify_status = -1;
		goto out;
	}

	/* Note: server-side NLA needs public keys (keys from us, the server) but no
	 * 		certificate verify
	 */
	verify_status = 1;
	if (clientMode)
	{
		verify_status = tls_verify_certificate(tls, cert, tls->hostname, tls->port);

		if (verify_status < 1)
		{
			DEBUG_WARN( "%s: certificate not trusted, aborting.\n", __FUNCTION__);
			tls_disconnect(tls);
			verify_status = 0;
		}
	}

out:
	tls_free_certificate(cert);

	return verify_status;
}
Ejemplo n.º 6
0
//--------------------------------------------------------------------------------------------------
le_result_t secSocket_Connect
(
    secSocket_Ctx_t* ctxPtr,     ///< [INOUT] Secure socket context pointer
    char*            hostPtr,    ///< [IN] Host to connect on
    uint16_t         port,       ///< [IN] Port to connect on
    SocketType_t     type,       ///< [IN] Socket type (TCP, UDP)
    int*             fdPtr       ///< [OUT] Socket file descriptor
)
{
    SSL* sslPtr = NULL;
    BIO* bioPtr = NULL;
    char hostAndPort[HOST_ADDR_LEN + PORT_STR_LEN + 1];
    le_result_t status = LE_FAULT;
    unsigned long code;

    if ((!ctxPtr) || (!hostPtr) || (!fdPtr))
    {
        LE_ERROR("Invalid argument: ctxPtr %p, hostPtr %p fdPtr %p", ctxPtr, hostPtr, fdPtr);
        return LE_BAD_PARAMETER;
    }

    OpensslCtx_t* contextPtr = GetContext(ctxPtr);
    if (!contextPtr)
    {
        return LE_BAD_PARAMETER;
    }

    // Start the connection
    snprintf(hostAndPort, sizeof(hostAndPort), "%s:%d", hostPtr, port);
    LE_INFO("Connecting to %d/%s:%d - %s...", type, hostPtr, port, hostAndPort);

    // Clear the current thread's OpenSSL error queue
    ERR_clear_error();

    // Setting up the BIO abstraction layer
    bioPtr = BIO_new_ssl_connect(contextPtr->sslCtxPtr);
    if (!bioPtr)
    {
        LE_ERROR("Unable to allocate and connect BIO");
        goto err;
    }

    BIO_get_ssl(bioPtr, &sslPtr);
    if (!sslPtr)
    {
        LE_ERROR("Unable to locate SSL pointer");
        goto err;
    }

    // Set the SSL_MODE_AUTO_RETRY flag: it will cause read/write operations to only return after
    // the handshake and successful completion
    SSL_set_mode(sslPtr, SSL_MODE_AUTO_RETRY);

    BIO_set_conn_hostname(bioPtr, hostAndPort);

    // Attempt to connect the supplied BIO and perform the handshake.
    // This function returns 1 if the connection was successfully established and 0 or -1 if the
    // connection failed.
    if (BIO_do_connect(bioPtr) != 1)
    {
        LE_ERROR("Unable to connect BIO to %s", hostAndPort);
        goto err;
    }

    // Get the FD linked to the BIO
    BIO_get_fd(bioPtr, fdPtr);
    BIO_socket_nbio(*fdPtr, 1);

    contextPtr->bioPtr = bioPtr;
    return LE_OK;

err:
    code = ERR_peek_last_error();

    if ((ERR_GET_LIB(code) == ERR_LIB_BIO) || (ERR_GET_LIB(code) == ERR_LIB_SSL))
    {
        switch (ERR_GET_REASON(code))
        {
            case ERR_R_MALLOC_FAILURE:
                status = LE_NO_MEMORY;
                break;

            case BIO_R_NULL_PARAMETER:
                status = LE_BAD_PARAMETER;
                break;

#if defined(BIO_R_BAD_HOSTNAME_LOOKUP)
            case BIO_R_BAD_HOSTNAME_LOOKUP:
                status = LE_UNAVAILABLE;
                break;
#endif

            case BIO_R_CONNECT_ERROR:
                status = LE_COMM_ERROR;
                break;

#if defined(BIO_R_EOF_ON_MEMORY_BIO)
            case BIO_R_EOF_ON_MEMORY_BIO:
                status = LE_CLOSED;
                break;
#endif

            default:
                status = LE_FAULT;
                break;
        }
     }

    return status;
}
Ejemplo n.º 7
0
void *netConnectHttpsThread(void *threadParam)
/* use a thread to run socket back to user */
{
/* child */

struct netConnectHttpsParams *params = threadParam;

pthread_detach(params->thread);  // this thread will never join back with it's progenitor

int fd=0;

char hostnameProto[256];

BIO *sbio;
SSL_CTX *ctx;
SSL *ssl;

openSslInit();

ctx = SSL_CTX_new(SSLv23_client_method());

fd_set readfds;
fd_set writefds;
int err;
struct timeval tv;


/* TODO checking certificates 

char *certFile = NULL;
char *certPath = NULL;
if (certFile || certPath)
    {
    SSL_CTX_load_verify_locations(ctx,certFile,certPath);
#if (OPENSSL_VERSION_NUMBER < 0x0090600fL)
    SSL_CTX_set_verify_depth(ctx,1);
#endif
    }

// verify paths and mode.

*/


sbio = BIO_new_ssl_connect(ctx);

BIO_get_ssl(sbio, &ssl);
if(!ssl) 
    {
    xerr("Can't locate SSL pointer");
    goto cleanup;
    }

/* Don't want any retries since we are non-blocking bio now */
//SSL_set_mode(ssl, SSL_MODE_AUTO_RETRY);


safef(hostnameProto,sizeof(hostnameProto),"%s:%d",params->hostName,params->port);
BIO_set_conn_hostname(sbio, hostnameProto);

BIO_set_nbio(sbio, 1);     /* non-blocking mode */

while (1) 
    {
    if (BIO_do_connect(sbio) == 1) 
	{
	break;  /* Connected */
	}
    if (! BIO_should_retry(sbio)) 
	{
	xerr("BIO_do_connect() failed");
	char s[256];	
	safef(s, sizeof s, "SSL error: %s", ERR_reason_error_string(ERR_get_error()));
	xerr(s);
	goto cleanup;
	}

    fd = BIO_get_fd(sbio, NULL);
    if (fd == -1) 
	{
	xerr("unable to get BIO descriptor");
	goto cleanup;
	}
    FD_ZERO(&readfds);
    FD_ZERO(&writefds);
    if (BIO_should_read(sbio)) 
	{
	FD_SET(fd, &readfds);
	}
    else if (BIO_should_write(sbio)) 
	{
	FD_SET(fd, &writefds);
	}
    else 
	{  /* BIO_should_io_special() */
	FD_SET(fd, &readfds);
	FD_SET(fd, &writefds);
	}
    tv.tv_sec = 10;  // timeout
    tv.tv_usec = 0;

    err = select(fd + 1, &readfds, &writefds, NULL, &tv);
    if (err < 0) 
	{
	xerr("select() error");
	goto cleanup;
	}

    if (err == 0) 
	{
	char s[256];	
	safef(s, sizeof s, "connection timeout to %s", params->hostName);
	xerr(s);
	goto cleanup;
	}
    }


/* TODO checking certificates 

if (certFile || certPath)
    if (!check_cert(ssl, host))
	return -1;

*/

/* we need to wait on both the user's socket and the BIO SSL socket 
 * to see if we need to ferry data from one to the other */


char sbuf[32768];  // socket buffer sv[1] to user
char bbuf[32768];  // bio buffer
int srd = 0;
int swt = 0;
int brd = 0;
int bwt = 0;
while (1) 
    {

    // Do NOT move this outside the while loop. 
    /* Get underlying file descriptor, needed for select call */
    fd = BIO_get_fd(sbio, NULL);
    if (fd == -1) 
	{
	xerr("BIO doesn't seem to be initialized in https, unable to get descriptor.");
	goto cleanup;
	}


    FD_ZERO(&readfds);
    FD_ZERO(&writefds);

    if (brd == 0)
	FD_SET(fd, &readfds);
    if (swt < srd)
	FD_SET(fd, &writefds);
    if (srd == 0)
	FD_SET(params->sv[1], &readfds);

    tv.tv_sec = 10;   // timeout
    tv.tv_usec = 0;

    err = select(max(fd,params->sv[1]) + 1, &readfds, &writefds, NULL, &tv);

    /* Evaluate select() return code */
    if (err < 0) 
	{
	xerr("error during select()");
	goto cleanup;
	}
    else if (err == 0) 
	{
	/* Timed out - just quit */
	xerr("https timeout expired");
	goto cleanup;
	}

    else 
	{
	if (FD_ISSET(params->sv[1], &readfds))
	    {
	    swt = 0;
	    srd = read(params->sv[1], sbuf, 32768);
	    if (srd == -1)
		{
		if (errno != 104) // udcCache often closes causing "Connection reset by peer"
		    xerrno("error reading https socket");
		goto cleanup;
		}
	    if (srd == 0) 
		break;  // user closed socket, we are done
	    }

	if (FD_ISSET(fd, &writefds))
	    {
	    int swtx = BIO_write(sbio, sbuf+swt, srd-swt);
	    if (swtx <= 0)
		{
		if (!BIO_should_write(sbio))
		    {
		    ERR_print_errors_fp(stderr);
		    xerr("Error writing SSL connection");
		    goto cleanup;
		    }
		}
	    else
		{
		swt += swtx;
		if (swt >= srd)
		    {
		    swt = 0;
		    srd = 0;
		    }
		}
	    }

	if (FD_ISSET(fd, &readfds))
	    {
	    bwt = 0;
	    brd = BIO_read(sbio, bbuf, 32768);

	    if (brd <= 0)
		{
		if (BIO_should_read(sbio))
		    {
		    brd = 0;
		    continue;
		    }
		else
		    {
		    if (brd == 0) break;
		    ERR_print_errors_fp(stderr);
		    xerr("Error reading SSL connection");
		    goto cleanup;
		    }
		}
	    // write the https data received immediately back on socket to user, and it's ok if it blocks.
	    while(bwt < brd)
		{
		int bwtx = write(params->sv[1], bbuf+bwt, brd-bwt);
		if (bwtx == -1)
		    {
		    if ((errno != 104)  // udcCache often closes causing "Connection reset by peer"
		     && (errno !=  32)) // udcCache often closes causing "Broken pipe"
			xerrno("error writing https data back to user socket");
		    goto cleanup;
		    }
		bwt += bwtx;
		}
	    brd = 0;
	    bwt = 0;
	    }
	}
    }

cleanup:

BIO_free_all(sbio);
close(params->sv[1]);  /* we are done with it */

return NULL;
}
Ejemplo n.º 8
0
static int
netsnmp_tlstcp_accept(netsnmp_transport *t)
{
    BIO            *accepted_bio;
    int             rc;
    SSL_CTX *ctx;
    SSL     *ssl;
    _netsnmpTLSBaseData *tlsdata = NULL;

    DEBUGMSGTL(("tlstcp", "netsnmp_tlstcp_accept called\n"));

    tlsdata = (_netsnmpTLSBaseData *) t->data;

    rc = BIO_do_accept(tlsdata->accept_bio);

    if (rc <= 0) {
        snmp_log(LOG_ERR, "BIO_do_accept failed\n");
        _openssl_log_error(rc, NULL, "BIO_do_accept");
        /* XXX: need to close the listening connection here? */
        return -1;
    }

    tlsdata->accepted_bio = accepted_bio = BIO_pop(tlsdata->accept_bio);
    if (!accepted_bio) {
        snmp_log(LOG_ERR, "Failed to pop an accepted bio off the bio staack\n");
        /* XXX: need to close the listening connection here? */
        return -1;
    }

    /* create the OpenSSL TLS context */
    ctx = tlsdata->ssl_context;

    /* create the server's main SSL bio */
    ssl = tlsdata->ssl = SSL_new(ctx);
    if (!tlsdata->ssl) {
        snmp_log(LOG_ERR, "TLSTCP: Failed to create a SSL BIO\n");
        BIO_free(accepted_bio);
        tlsdata->accepted_bio = NULL;
        return -1;
    }

    SSL_set_bio(ssl, accepted_bio, accepted_bio);

    if ((rc = SSL_accept(ssl)) <= 0) {
        snmp_log(LOG_ERR, "TLSTCP: Failed SSL_accept\n");
        _openssl_log_error(rc, ssl, "SSL_accept");
        SSL_shutdown(tlsdata->ssl);
        SSL_free(tlsdata->ssl);
        tlsdata->accepted_bio = NULL; /* freed by SSL_free */
        tlsdata->ssl = NULL;
        return -1;
    }

    /*
     * currently netsnmp_tlsbase_wrapup_recv is where we check for
     * algorithm compliance, but for tls we know the algorithms
     * at this point, so we could bail earlier...
     */
#if 0 /* moved checks to netsnmp_tlsbase_wrapup_recv */
    netsnmp_openssl_null_checks(tlsdata->ssl, &no_auth, NULL);
    if (no_auth != 0) { /* null/unknown authentication */
        /* xxx-rks: snmp_increment_statistic(STAT_???); */
        snmp_log(LOG_ERR, "tlstcp: connection with NULL authentication\n");
        SSL_shutdown(tlsdata->ssl);
        SSL_free(tlsdata->ssl);
        tlsdata->accepted_bio = NULL; /* freed by SSL_free */
        tlsdata->ssl = NULL;
        return -1;
    }
#endif

    /* RFC5953 Section 5.3.2: Accepting a Session as a Server
       A (D)TLS server should accept new session connections from any client
       that it is able to verify the client's credentials for.  This is done
       by authenticating the client's presented certificate through a
       certificate path validation process (e.g.  [RFC5280]) or through
       certificate fingerprint verification using fingerprints configured in
       the snmpTlstmCertToTSNTable.  Afterward the server will determine the
       identity of the remote entity using the following procedures.

       The (D)TLS server identifies the authenticated identity from the
       (D)TLS client's principal certificate using configuration information
       from the snmpTlstmCertToTSNTable mapping table.  The (D)TLS server
       MUST request and expect a certificate from the client and MUST NOT
       accept SNMP messages over the (D)TLS connection until the client has
       sent a certificate and it has been authenticated.  The resulting
       derived tmSecurityName is recorded in the tmStateReference cache as
       tmSecurityName.  The details of the lookup process are fully
       described in the DESCRIPTION clause of the snmpTlstmCertToTSNTable
       MIB object.  If any verification fails in any way (for example
       because of failures in cryptographic verification or because of the
       lack of an appropriate row in the snmpTlstmCertToTSNTable) then the
       session establishment MUST fail, and the
       snmpTlstmSessionInvalidClientCertificates object is incremented.  If
       the session can not be opened for any reason at all, including
       cryptographic verification failures, then the
       snmpTlstmSessionOpenErrors counter is incremented and processing
       stops.

       Servers that wish to support multiple principals at a particular port
       SHOULD make use of a (D)TLS extension that allows server-side
       principal selection like the Server Name Indication extension defined
       in Section 3.1 of [RFC4366].  Supporting this will allow, for
       example, sending notifications to a specific principal at a given TCP
       or UDP port.
    */
    /* Implementation notes:
       - we expect fingerprints to be stored in the transport config
       - we do not currently support mulitple principals and only offer one
    */
    if ((rc = netsnmp_tlsbase_verify_client_cert(ssl, tlsdata))
            != SNMPERR_SUCCESS) {
        /* XXX: free needed memory */
        snmp_log(LOG_ERR, "TLSTCP: Falied checking client certificate\n");
        snmp_increment_statistic(STAT_TLSTM_SNMPTLSTMSESSIONINVALIDCLIENTCERTIFICATES);
        SSL_shutdown(tlsdata->ssl);
        SSL_free(tlsdata->ssl);
        tlsdata->accepted_bio = NULL; /* freed by SSL_free */
        tlsdata->ssl = NULL;
        return -1;
    }


    /* XXX: check acceptance criteria here */

    DEBUGMSGTL(("tlstcp", "accept succeeded on sock %d\n", t->sock));

    /* RFC5953 Section 5.1.2 step 1, part2::
     * If this is the first message received through this session and
     the session does not have an assigned tlstmSessionID yet then the
     snmpTlstmSessionAccepts counter is incremented and a
     tlstmSessionID for the session is created.  This will only happen
     on the server side of a connection because a client would have
     already assigned a tlstmSessionID during the openSession()
     invocation.  Implementations may have performed the procedures
     described in Section 5.3.2 prior to this point or they may
     perform them now, but the procedures described in Section 5.3.2
     MUST be performed before continuing beyond this point.
    */
    /* We're taking option 2 and incrementing the session accepts here
       rather than upon receiving the first packet */
    snmp_increment_statistic(STAT_TLSTM_SNMPTLSTMSESSIONACCEPTS);

    /* XXX: check that it returns something so we can free stuff? */
    return BIO_get_fd(tlsdata->accepted_bio, NULL);
}
Ejemplo n.º 9
0
netsnmp_transport *
netsnmp_tlstcp_open(netsnmp_transport *t)
{
    _netsnmpTLSBaseData *tlsdata;
    BIO *bio;
    SSL_CTX *ctx;
    SSL *ssl;
    int rc = 0;
    _netsnmp_verify_info *verify_info;

    netsnmp_assert_or_return(t != NULL, NULL);
    netsnmp_assert_or_return(t->data != NULL, NULL);
    netsnmp_assert_or_return(sizeof(_netsnmpTLSBaseData) == t->data_length,
                             NULL);

    tlsdata = t->data;

    if (tlsdata->flags & NETSNMP_TLSBASE_IS_CLIENT) {
        /* Is the client */

        /* RFC5953 Section 5.3.1:  Establishing a Session as a Client
         *    1)  The snmpTlstmSessionOpens counter is incremented.
         */
        snmp_increment_statistic(STAT_TLSTM_SNMPTLSTMSESSIONOPENS);

        /* RFC5953 Section 5.3.1:  Establishing a Session as a Client
          2)  The client selects the appropriate certificate and cipher_suites
              for the key agreement based on the tmSecurityName and the
              tmRequestedSecurityLevel for the session.  For sessions being
              established as a result of a SNMP-TARGET-MIB based operation, the
              certificate will potentially have been identified via the
              snmpTlstmParamsTable mapping and the cipher_suites will have to
              be taken from system-wide or implementation-specific
              configuration.  If no row in the snmpTlstmParamsTable exists then
              implementations MAY choose to establish the connection using a
              default client certificate available to the application.
              Otherwise, the certificate and appropriate cipher_suites will
              need to be passed to the openSession() ASI as supplemental
              information or configured through an implementation-dependent
              mechanism.  It is also implementation-dependent and possibly
              policy-dependent how tmRequestedSecurityLevel will be used to
              influence the security capabilities provided by the (D)TLS
              connection.  However this is done, the security capabilities
              provided by (D)TLS MUST be at least as high as the level of
              security indicated by the tmRequestedSecurityLevel parameter.
              The actual security level of the session is reported in the
              tmStateReference cache as tmSecurityLevel.  For (D)TLS to provide
              strong authentication, each principal acting as a command
              generator SHOULD have its own certificate.
        */
        /*
          Implementation notes: we do most of this in the
          sslctx_client_setup The transport should have been
          f_config()ed with the proper fingerprints to use (which is
          stored in tlsdata), or we'll use the default identity
          fingerprint if that can be found.
        */

        /* XXX: check securityLevel and ensure no NULL fingerprints are used */

        /* set up the needed SSL context */
        tlsdata->ssl_context = ctx =
                                   sslctx_client_setup(TLSv1_method(), tlsdata);
        if (!ctx) {
            snmp_log(LOG_ERR, "failed to create TLS context\n");
            return NULL;
        }

        /* RFC5953 Section 5.3.1:  Establishing a Session as a Client
           3)  Using the destTransportDomain and destTransportAddress values,
               the client will initiate the (D)TLS handshake protocol to
               establish session keys for message integrity and encryption.
        */
        /* Implementation note:
           The transport domain and address are pre-processed by this point
        */

        /* Create a BIO connection for it */
        DEBUGMSGTL(("tlstcp", "connecting to tlstcp %s\n",
                    tlsdata->addr_string));
        t->remote = (void *) strdup(tlsdata->addr_string);
        t->remote_length = strlen(tlsdata->addr_string) + 1;

        bio = BIO_new_connect(tlsdata->addr_string);

        /* RFC5953 Section 5.3.1:  Establishing a Session as a Client
           3) continued:
              If the attempt to establish a session is unsuccessful, then
              snmpTlstmSessionOpenErrors is incremented, an error indication is
              returned, and processing stops.
        */

        if (NULL == bio) {
            snmp_increment_statistic(STAT_TLSTM_SNMPTLSTMSESSIONOPENERRORS);
            snmp_log(LOG_ERR, "tlstcp: failed to create bio\n");
            _openssl_log_error(rc, NULL, "BIO creation");
            return NULL;
        }


        /* Tell the BIO to actually do the connection */
        if ((rc = BIO_do_connect(bio)) <= 0) {
            snmp_increment_statistic(STAT_TLSTM_SNMPTLSTMSESSIONOPENERRORS);
            snmp_log(LOG_ERR, "tlstcp: failed to connect to %s\n",
                     tlsdata->addr_string);
            _openssl_log_error(rc, NULL, "BIO_do_connect");
            BIO_free(bio);
            return NULL;
        }

        /* Create the SSL layer on top of the socket bio */
        ssl = tlsdata->ssl = SSL_new(ctx);
        if (NULL == ssl) {
            snmp_increment_statistic(STAT_TLSTM_SNMPTLSTMSESSIONOPENERRORS);
            snmp_log(LOG_ERR, "tlstcp: failed to create a SSL connection\n");
            BIO_free(bio);
            return NULL;
        }

        /* Bind the SSL layer to the BIO */
        SSL_set_bio(ssl, bio, bio);
        SSL_set_mode(ssl, SSL_MODE_AUTO_RETRY);

        verify_info = SNMP_MALLOC_TYPEDEF(_netsnmp_verify_info);
        if (NULL == verify_info) {
            snmp_increment_statistic(STAT_TLSTM_SNMPTLSTMSESSIONOPENERRORS);
            snmp_log(LOG_ERR, "tlstcp: failed to create a SSL connection\n");
            SSL_shutdown(ssl);
            BIO_free(bio);
            return NULL;
        }

        SSL_set_ex_data(ssl, tls_get_verify_info_index(), verify_info);

        /* Then have SSL do it's connection over the BIO */
        if ((rc = SSL_connect(ssl)) <= 0) {
            snmp_increment_statistic(STAT_TLSTM_SNMPTLSTMSESSIONOPENERRORS);
            snmp_log(LOG_ERR, "tlstcp: failed to ssl_connect\n");
            BIO_free(bio);
            return NULL;
        }

        /* RFC5953 Section 5.3.1: Establishing a Session as a Client
           3) continued:
              If the session failed to open because the presented
              server certificate was unknown or invalid then the
              snmpTlstmSessionUnknownServerCertificate or
              snmpTlstmSessionInvalidServerCertificates MUST be
              incremented and a snmpTlstmServerCertificateUnknown or
              snmpTlstmServerInvalidCertificate notification SHOULD be
              sent as appropriate.  Reasons for server certificate
              invalidation includes, but is not limited to,
              cryptographic validation failures and an unexpected
              presented certificate identity.
        */

        /* RFC5953 Section 5.3.1: Establishing a Session as a Client
           4)  The (D)TLS client MUST then verify that the (D)TLS server's
               presented certificate is the expected certificate.  The (D)TLS
               client MUST NOT transmit SNMP messages until the server
               certificate has been authenticated, the client certificate has
               been transmitted and the TLS connection has been fully
               established.

               If the connection is being established from configuration based
               on SNMP-TARGET-MIB configuration, then the snmpTlstmAddrTable
               DESCRIPTION clause describes how the verification is done (using
               either a certificate fingerprint, or an identity authenticated
               via certification path validation).

               If the connection is being established for reasons other than
               configuration found in the SNMP-TARGET-MIB then configuration and
               procedures outside the scope of this document should be followed.
               Configuration mechanisms SHOULD be similar in nature to those
               defined in the snmpTlstmAddrTable to ensure consistency across
               management configuration systems.  For example, a command-line
               tool for generating SNMP GETs might support specifying either the
               server's certificate fingerprint or the expected host name as a
               command line argument.
        */

        /* Implementation notes:
           - All remote certificate fingerprints are expected to be
             stored in the transport's config information.  This is
             true both for CLI clients and TARGET-MIB sessions.
           - netsnmp_tlsbase_verify_server_cert implements these checks
        */
        if (netsnmp_tlsbase_verify_server_cert(ssl, tlsdata) != SNMPERR_SUCCESS) {
            /* XXX: unknown vs invalid; two counters */
            snmp_increment_statistic(STAT_TLSTM_SNMPTLSTMSESSIONUNKNOWNSERVERCERTIFICATE);
            snmp_log(LOG_ERR, "tlstcp: failed to verify ssl certificate\n");
            SSL_shutdown(ssl);
            BIO_free(bio);
            return NULL;
        }

        /* RFC5953 Section 5.3.1: Establishing a Session as a Client
           5)  (D)TLS provides assurance that the authenticated identity has
               been signed by a trusted configured certification authority.  If
               verification of the server's certificate fails in any way (for
               example because of failures in cryptographic verification or the
               presented identity did not match the expected named entity) then
               the session establishment MUST fail, the
               snmpTlstmSessionInvalidServerCertificates object is incremented.
               If the session can not be opened for any reason at all, including
               cryptographic verification failures, then the
               snmpTlstmSessionOpenErrors counter is incremented and processing
               stops.

        */
        /* XXX: add snmpTlstmSessionInvalidServerCertificates on
           crypto failure */

        /* RFC5953 Section 5.3.1: Establishing a Session as a Client
           6)  The TLSTM-specific session identifier (tlstmSessionID) is set in
           the tmSessionID of the tmStateReference passed to the TLS
           Transport Model to indicate that the session has been established
           successfully and to point to a specific (D)TLS connection for
           future use.  The tlstmSessionID is also stored in the LCD for
           later lookup during processing of incoming messages
           (Section 5.1.2).
        */
        /* Implementation notes:
           - the tlsdata pointer is used as our session identifier, as
             noted in the netsnmp_tlstcp_recv() function comments.
        */

        t->sock = BIO_get_fd(bio, NULL);

    } else {
#ifndef NETSNMP_NO_LISTEN_SUPPORT
        /* Is the server */

        /* Create the socket bio */
        DEBUGMSGTL(("tlstcp", "listening on tlstcp port %s\n",
                    tlsdata->addr_string));
        tlsdata->accept_bio = BIO_new_accept(tlsdata->addr_string);
        t->local = (void *) strdup(tlsdata->addr_string);
        t->local_length = strlen(tlsdata->addr_string)+1;
        if (NULL == tlsdata->accept_bio) {
            snmp_log(LOG_ERR, "TLSTCP: Falied to create a accept BIO\n");
            return NULL;
        }

        /* openssl requires an initial accept to bind() the socket */
        if (BIO_do_accept(tlsdata->accept_bio) <= 0) {
            _openssl_log_error(rc, tlsdata->ssl, "BIO_do__accept");
            snmp_log(LOG_ERR, "TLSTCP: Falied to do first accept on the TLS accept BIO\n");
            return NULL;
        }

        /* create the OpenSSL TLS context */
        tlsdata->ssl_context =
            sslctx_server_setup(TLSv1_method());

        t->sock = BIO_get_fd(tlsdata->accept_bio, NULL);
        t->flags |= NETSNMP_TRANSPORT_FLAG_LISTEN;
#else /* NETSNMP_NO_LISTEN_SUPPORT */
        return NULL;
#endif /* NETSNMP_NO_LISTEN_SUPPORT */
    }
    return t;
}
Ejemplo n.º 10
0
int redisContextConnectSSL(redisContext *c, const char *addr, int port, char* certfile, char* certdir, struct timeval *timeout) {
    struct timeval  start_time;
    int             has_timeout = 0;
    int             is_nonblocking = 0;

    c->ssl.sd = -1;
    c->ssl.ctx = NULL;
    c->ssl.ssl = NULL;
    c->ssl.bio = NULL;

    // Set up a SSL_CTX object, which will tell our BIO object how to do its work
    SSL_CTX* ctx = SSL_CTX_new(TLSv1_1_client_method());
    c->ssl.ctx = ctx;

    // Create a SSL object pointer, which our BIO object will provide.
    SSL* ssl;

    // Create our BIO object for SSL connections.
    BIO* bio = BIO_new_ssl_connect(ctx);
    c->ssl.bio = bio;

    // Failure?
    if (bio == NULL) {
        char errorbuf[1024];
        __redisSetError(c,REDIS_ERR_OTHER,"SSL Error: Error creating BIO!\n");

        ERR_error_string(1024,errorbuf);
        __redisSetError(c,REDIS_ERR_OTHER,errorbuf);

        // We need to free up the SSL_CTX before we leave.
        cleanupSSL( &c->ssl );
        return REDIS_ERR;
    }

    // Makes ssl point to bio's SSL object.
    BIO_get_ssl(bio, &ssl);
    c->ssl.ssl = ssl;

    // Set the SSL to automatically retry on failure.
    SSL_set_mode(ssl, SSL_MODE_AUTO_RETRY);

    char* connect_str = (char *)calloc( 1, strlen( addr ) + 10 );
    sprintf( connect_str, "%s:%d", addr, port );
    c->ssl.conn_str = connect_str;

    // We're connection to the REDIS server at host:port
    BIO_set_conn_hostname(bio, connect_str);

    SSL_CTX_load_verify_locations(ctx, certfile, certdir);

    // Are we supposed to be blocking or non-blocking?
    if( (c->flags & REDIS_BLOCK) == 0) {
        is_nonblocking = 1;
        BIO_set_nbio(bio,1);
    }

    // What about a timeout?
    // If we're blocking and have a timeout, we need to handle that specially
    if( NULL != timeout ) {
        BIO_set_nbio(bio,1);
        has_timeout = 1;
        gettimeofday(&start_time, NULL);
    }

    while(1) {
        struct timeval	cur_time,
                    elapsed_time;

        if (has_timeout) {
            gettimeofday(&cur_time, NULL);
            elapsed_time = subtractTimeval( cur_time, start_time );

            if (compareTimeval( elapsed_time, *timeout) > 0) {
                char errorbuf[1024];

                __redisSetError(c,REDIS_ERR_OTHER,"SSL Error: Connection timed out.");
                ERR_error_string(1024,errorbuf);
                __redisSetError(c,REDIS_ERR_OTHER,errorbuf);
                cleanupSSL( &(c->ssl) );
                return REDIS_ERR;
            }
        }

        // Same as before, try to connect.
        if (BIO_do_connect(bio) <= 0 ) {
            if( BIO_should_retry( bio ) ) {
                // We need to retry.
            } else {
                char errorbuf[1024];
                __redisSetError(c,REDIS_ERR_OTHER,"SSL Error: Failed to connect");
                ERR_error_string(1024,errorbuf);
                __redisSetError(c,REDIS_ERR_OTHER,errorbuf);
                cleanupSSL( &(c->ssl) );
                return REDIS_ERR;
            }
        } else {
            // connect is done...
            break;
        }

        if( has_timeout ) {
            // Do select and seelct on it
            int result;
            int fd = BIO_get_fd( bio, NULL );
            struct timeval time_left;

            if (has_timeout) {
                time_left = subtractTimeval( *timeout, elapsed_time );
            }

            fd_set readset, writeset;
            FD_ZERO(&readset);
            FD_SET(fd, &readset);
            FD_ZERO(&writeset);
            FD_SET(fd, &writeset);

            do {
                result = select( fd+1, &readset, &writeset, NULL, &time_left);
            } while( result == -1 && errno == EINTR );
        }

    }

    while(1) {
        struct timeval	cur_time,
                    elapsed_time;

        if (has_timeout) {
            gettimeofday(&cur_time, NULL);
            elapsed_time = subtractTimeval( cur_time, start_time );

            if (compareTimeval( elapsed_time, *timeout) > 0) {
                char errorbuf[1024];

                __redisSetError(c,REDIS_ERR_OTHER,"SSL Error: Connection timed out.");
                ERR_error_string(1024,errorbuf);
                __redisSetError(c,REDIS_ERR_OTHER,errorbuf);
                cleanupSSL( &(c->ssl) );
                return REDIS_ERR;
            }
        }

        // Now we need to do the SSL handshake, so we can communicate.
        if (BIO_do_handshake(bio) <= 0) {
            if( BIO_should_retry( bio ) ) {
                // We need to retry.
            } else {
                char errorbuf[1024];
                __redisSetError(c,REDIS_ERR_OTHER,"SSL Error: handshake failure");
                ERR_error_string(1024,errorbuf);
                __redisSetError(c,REDIS_ERR_OTHER,errorbuf);
                cleanupSSL( &(c->ssl) );
                return REDIS_ERR;
            }
        } else {
            // handshake is done...
            break;
        }

        if( has_timeout ) {
            // Do select and seelct on it
            int result;
            int fd = BIO_get_fd( bio, NULL );
            struct timeval time_left;

            if (has_timeout) {
                time_left = subtractTimeval( *timeout, elapsed_time );
            }

            fd_set readset, writeset;
            FD_ZERO(&readset);
            FD_SET(fd, &readset);
            FD_ZERO(&writeset);
            FD_SET(fd, &writeset);

            do {
                result = select( fd+1, &readset, &writeset, NULL, &time_left);
            } while( result == -1 && errno == EINTR );
        }

    }

    long verify_result = SSL_get_verify_result(ssl);
    if( verify_result == X509_V_OK) {
        X509* peerCertificate = SSL_get_peer_certificate(ssl);

        char commonName [512];
        X509_NAME * name = X509_get_subject_name(peerCertificate);
        X509_NAME_get_text_by_NID(name, NID_commonName, commonName, 512);

        // TODO: Add a redis config parameter for the common name to check for.
        //       Since Redis connections are generally done with an IP address directly,
        //       we can't just assume that the name we get on the addr is an actual name.
        //
        //    if(strcasecmp(commonName, "BradBroerman") != 0) {
        //      __redisSetError(c,REDIS_ERR_OTHER,"SSL Error: Error validating cert common name.\n\n" );
        //      cleanupSSL( &(c->ssl) );
        //      return REDIS_ERR;

    } else {
        char errorbuf[1024];
        __redisSetError(c,REDIS_ERR_OTHER,"SSL Error: Error retrieving peer certificate.\n" );
        ERR_error_string(1024,errorbuf);
        __redisSetError(c,REDIS_ERR_OTHER,errorbuf);
        cleanupSSL( &(c->ssl) );
        return REDIS_ERR;
    }

    BIO_set_nbio(bio,is_nonblocking);

    return REDIS_OK;
}
Ejemplo n.º 11
0
BOOL tcp_connect(rdpTcp* tcp, const char* hostname, int port, int timeout)
{
	int status;
	UINT32 option_value;
	socklen_t option_len;

	if (!hostname)
		return FALSE;

	if (hostname[0] == '/')
		tcp->ipcSocket = TRUE;

	if (tcp->ipcSocket)
	{
		tcp->sockfd = freerdp_uds_connect(hostname);

		if (tcp->sockfd < 0)
			return FALSE;

		tcp->socketBio = BIO_new(BIO_s_simple_socket());

		if (!tcp->socketBio)
			return FALSE;

		BIO_set_fd(tcp->socketBio, tcp->sockfd, BIO_CLOSE);
	}
	else
	{
#ifdef HAVE_POLL_H
		struct pollfd pollfds;
#else
		fd_set cfds;
		struct timeval tv;
#endif

#ifdef NO_IPV6
		tcp->socketBio = BIO_new(BIO_s_connect());

		if (!tcp->socketBio)
			return FALSE;

		if (BIO_set_conn_hostname(tcp->socketBio, hostname) < 0 || BIO_set_conn_int_port(tcp->socketBio, &port) < 0)
			return FALSE;

		BIO_set_nbio(tcp->socketBio, 1);

		status = BIO_do_connect(tcp->socketBio);

		if ((status <= 0) && !BIO_should_retry(tcp->socketBio))
			return FALSE;

		tcp->sockfd = BIO_get_fd(tcp->socketBio, NULL);

		if (tcp->sockfd < 0)
			return FALSE;
#else /* NO_IPV6 */
		struct addrinfo hints = {0};
		struct addrinfo *result;
		struct addrinfo *tmp;
		char port_str[11];

		//ZeroMemory(&hints, sizeof(struct addrinfo));
		hints.ai_family = AF_UNSPEC;    /* Allow IPv4 or IPv6 */
		hints.ai_socktype = SOCK_STREAM;
		/*
		 * FIXME: the following is a nasty workaround. Find a cleaner way:
		 * Either set port manually afterwards or get it passed as string?
		 */
		sprintf_s(port_str, 11, "%u", port);

		status = getaddrinfo(hostname, port_str, &hints, &result);
		if (status) {
			fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(status));
			return FALSE;
		}

		/* For now prefer IPv4 over IPv6. */
		tmp = result;
		if (tmp->ai_family == AF_INET6 && tmp->ai_next != 0)
		{
			while ((tmp = tmp->ai_next))
			{
				if (tmp->ai_family == AF_INET)
					break;
			}
			if (!tmp)
				tmp = result;
		}
		tcp->sockfd = socket(tmp->ai_family, tmp->ai_socktype, tmp->ai_protocol);

		if (tcp->sockfd  < 0) {
			freeaddrinfo(result);
			return FALSE;
		}

		if (connect(tcp->sockfd, tmp->ai_addr, tmp->ai_addrlen) < 0) {
			fprintf(stderr, "connect: %s\n", strerror(errno));
			freeaddrinfo(result);
			return FALSE;
		}
		freeaddrinfo(result);
		tcp->socketBio = BIO_new_socket(tcp->sockfd, BIO_NOCLOSE);

		/* TODO: make sure the handshake is done by querying the bio */
		//		if (BIO_should_retry(tcp->socketBio))
		//          return FALSE;
#endif /* NO_IPV6 */

		if (status <= 0)
		{
#ifdef HAVE_POLL_H
			pollfds.fd = tcp->sockfd;
			pollfds.events = POLLOUT;
			pollfds.revents = 0;
			do
			{
				status = poll(&pollfds, 1, timeout * 1000);
			}
			while ((status < 0) && (errno == EINTR));
#else
			FD_ZERO(&cfds);
			FD_SET(tcp->sockfd, &cfds);

			tv.tv_sec = timeout;
			tv.tv_usec = 0;

			status = _select(tcp->sockfd + 1, NULL, &cfds, NULL, &tv);
#endif
			if (status == 0)
			{
				return FALSE; /* timeout */
			}
		}

		(void)BIO_set_close(tcp->socketBio, BIO_NOCLOSE);
		BIO_free(tcp->socketBio);

		tcp->socketBio = BIO_new(BIO_s_simple_socket());

		if (!tcp->socketBio)
			return FALSE;

		BIO_set_fd(tcp->socketBio, tcp->sockfd, BIO_CLOSE);
	}

	SetEventFileDescriptor(tcp->event, tcp->sockfd);

	tcp_get_ip_address(tcp);
	tcp_get_mac_address(tcp);

	option_value = 1;
	option_len = sizeof(option_value);

	if (!tcp->ipcSocket)
	{
		if (setsockopt(tcp->sockfd, IPPROTO_TCP, TCP_NODELAY, (void*) &option_value, option_len) < 0)
			WLog_ERR(TAG,  "unable to set TCP_NODELAY");
	}

	/* receive buffer must be a least 32 K */
	if (getsockopt(tcp->sockfd, SOL_SOCKET, SO_RCVBUF, (void*) &option_value, &option_len) == 0)
	{
		if (option_value < (1024 * 32))
		{
			option_value = 1024 * 32;
			option_len = sizeof(option_value);

			if (setsockopt(tcp->sockfd, SOL_SOCKET, SO_RCVBUF, (void*) &option_value, option_len) < 0)
			{
				WLog_ERR(TAG,  "unable to set receive buffer len");
				return FALSE;
			}
		}
	}

	if (!tcp->ipcSocket)
	{
		if (!tcp_set_keep_alive_mode(tcp))
			return FALSE;
	}

	tcp->bufferedBio = BIO_new(BIO_s_buffered_socket());

	if (!tcp->bufferedBio)
		return FALSE;

	tcp->bufferedBio->ptr = tcp;

	tcp->bufferedBio = BIO_push(tcp->bufferedBio, tcp->socketBio);

	return TRUE;
}
int ssl_transport_insecure_send(option_block *opts, void *d, int i)
{
  FILE *log = stdout;
  char spec[2048] = {0};
  struct timeval tv;
  int sockfd;
  fd_set fds;
  unsigned long int to = MAX(100, opts->time_out);
  int ret;

  if(opts->fp_log)
    log = opts->fp_log;

  if(ssl_bio == NULL)
    {
      snprintf(spec, 2048, "%s:%d", opts->host_spec, opts->port);
      ssl_bio = BIO_new_connect(spec);
      if(ssl_bio == NULL)
	{
	  fprintf(stderr, "<ssl_transport:i-send> failure to acquire BIO: [%s]\n",
		  spec);
	  return -1;
	}
      
      if(BIO_do_connect(ssl_bio) <= 0)
	{
	  fprintf(stderr, "<ssl_transport:i-send> failure to simple connect to: [%s]\n",
		  spec);
	  return -1;
	}
    }
  
 retx:
  if(BIO_write(ssl_bio, d, i) <= 0)
    {
      if(!BIO_should_retry(ssl_bio))
	{
	  fprintf(stderr, "<ssl_transport:i-send> failure to transmit!\n");
	  ssl_transport_close();
	}
      goto retx;
    }
  
  if(opts->verbosity != QUIET)
    fprintf(log, "[%s] <ssl_transport:send> tx fuzz - scanning for reply.\n",
	    get_time_as_log());

    BIO_get_fd(ssl_bio, &sockfd);

    FD_ZERO(&fds);
    FD_SET(sockfd, &fds);

    tv.tv_sec  = to / 1000;
    tv.tv_usec = (to % 1000) * 1000; /*time out*/

    ret = select(sockfd+1, &fds, NULL, NULL, &tv);
    if(ret > 0)
    {
        if(FD_ISSET(sockfd, &fds))
        {
            char buf[8192] = {0};
            int r_len = 0;

	    ret = BIO_read(ssl_bio, buf, 8192);
	    if(ret == 0)
	      {
		fprintf(stderr, "<ssl_transport:send> remote closed xon\n");
		ssl_transport_close();
	      }
	    else if(ret > 0)
	      {
		if(opts->verbosity != QUIET)
		  fprintf(log, 
			  "[%s] read:\n%s\n===============================================================================\n", 
			  get_time_as_log(),
			  buf);
	      }
	}
    }
    
    if((opts->close_conn) || ((opts->close_conn) && (!opts->forget_conn)))
      {
	ssl_transport_close();
      }
    
    mssleep(opts->reqw_inms);
    return 0;
}
Ejemplo n.º 13
0
int main(int argc, char** argv){

    int sock = 0; // declaración del socket e inicializado a 0
    int error = 0; /** declaramos una variable que nos servirá para detectar
                    * errores
                    */
    int serverTalk = 0;
    //socklen_t length = (socklen_t) sizeof (struct sockaddr_in); // tamaño del paquete
    //struct sockaddr_in addr; // definimos el contenedor de la dirección
    unsigned int port = 5678; /** creamos la variable que identifica el puerto
                               * de conexión, siendo el puerto por defecto 5678
                               */
    char dir[DIM] = "localhost"; /** definimos la cadena que contendrá a la
                                  * dirección del servidor, la cual, será por
                                  * defecto localhost
                                  */
    //struct hostent*  server; // estructura utilizada para la gestión de direcciones
    sms auxMsj;
    char name[DIM] = {0};
    int nbytes = 0; // contador de bytes leidos y escritos
    char aux[DIM] = {0};
    // inicializamos las variables de SSL
    BIO* bio = NULL;
    SSL_CTX* ctx = NULL;
    SSL* ssl = NULL;
    char cert[DIM] = "/usr/share/doc/libssl-dev/demos/sign/cert.pem";

    //analizamos los parámetros de entrada
    int i = 0;
    for(; i < argc; i++){
        if(strcmp(argv[i], "-p") == 0){ // leemos el puerto
            if(argc <= i + 1 || isNum(argv[i+1]) == 0){
                perror("Se esperaba un número después de -p");
                exit(-1);
            }else{
                PDEBUG("INFO: Puerto identificado\n");
                i++;
                port = atoi(argv[i]);
            }
            continue;
        }else if(strcmp(argv[i], "-d") == 0){ // dirección de destino
            if(argc <= i + 1){
                perror("Se esperaba una dirección después de -d");
                exit(-1);
            }else{
                PDEBUG("INFO: Destino identificado");
                i++;
                strcpy(dir, argv[i]);
            }
            continue;
        }else if(strcmp(argv[i], "-cert") == 0){ // dirección de destino
            if(argc <= i + 1){
                perror("Se esperaba una ruta de certificado después de -cert");
                exit(-1);
            }else{
                PDEBUG("INFO: Destino identificado");
                i++;
                strcpy(cert, argv[i]);
            }
            continue;
        }
    }

    /***********************************SSL************************************/
    PDEBUG("INFO: Iniciamos la librería SSL\n");
    SSL_load_error_strings(); // strings de error
    SSL_library_init(); // iniciams la libreria en sí
    ERR_load_BIO_strings(); // strings de error de BIO
    OpenSSL_add_all_algorithms(); // inicializamos los algoritmos de la librería

    PDEBUG("INFO: Conectando...\n");
    PDEBUG("INFO: Inicializando los punteros\n");
    ctx = SSL_CTX_new(SSLv23_client_method());
    ssl = NULL;
    PDEBUG("INFO: Cargamos el certificado\n");
    if(SSL_CTX_load_verify_locations(ctx, cert, NULL) == 0){
        char aux[] = "ERROR: No se pudo comprobar el certificado\n";
        write(WRITE, aux, strlen(aux));
        exit(-1);
    }
    PDEBUG("INFO: Inicializando BIO\n");
    bio = BIO_new_ssl_connect(ctx);
    BIO_get_ssl(bio, &ssl);
    if(ssl == 0){
        char aux[] = "ERROR: Error al crear el objeto ssl\n";
        write(WRITE, aux, strlen(aux));
        exit(-1);
    }
    PDEBUG("INFO: Estableciendo el modo de trabajo, no queremos reintentos\n");
    SSL_set_mode(ssl, SSL_MODE_AUTO_RETRY);
    PDEBUG("INFO: Intentando realizar la conexión\n");
    PDEBUG("INFO: Conectando a -> ");
    sprintf(aux, "%s:%i", dir, port);
    PDEBUG(aux);PDEBUG("\n");
    BIO_set_conn_hostname(bio, aux);
    PDEBUG("INFO: Verificando la conexión\n");
    if (BIO_do_connect(bio) < 1){
        char aux[] = "ERROR: al conectar el BIO\n";
        write(WRITE, aux, strlen(aux));
        exit(-1);
    }
    //PDEBUG("INFO: Verificando el resultado de la conexión\n");
    //printf("--%i-%i--\n", X509_V_OK, SSL_get_verify_result(ssl));
    //if (SSL_get_verify_result(ssl) != X509_V_OK) {
    //    char aux[] = "ERROR: verificar el resultado de la conexión\n";
    //    write(WRITE, aux, strlen(aux));
    //    exit(-1);
    //}

    PDEBUG("INFO: Conectado\n");
    /***********************************SSL************************************/

    ////Creamos el socket
    //PDEBUG("INFO: Creando el socket\n");
    //sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
    ////Comprobamos si ha ocurrido un error al crear el socket
    //if(sock < 0){
    //    char aux[] = "ERROR: creación del socket {{socket()}}:\n";
    //    write(WRITE, aux, strlen(aux));
    //    exit(-1);
    //}
    //
    //
    //addr.sin_family = AF_INET; // familia AF_INET
    //addr.sin_port = htons(port); // definimos el puerto de conexión
    //PDEBUG("INFO: Traducimos la dirección del servidor\n");
    //server = gethostbyname(dir); /** convertimos la dirección dada
    //                              * en una dirección válida para el
    //                              * equipo y la metemos en addr
    //                              */
    ////Comprobamos si ha ocurrido un error obtener el host
    //if(server == NULL){
    //    write(WRITE, "ERROR: Host inválido\n", DIM);
    //    // terminamos la ejecución del programa
    //    exit(-1);
    //}
    //// copiamos el contenido correspondiente a la dirección de server en addr
    //bcopy(server->h_addr, &(addr.sin_addr.s_addr), server->h_length);
    //
    //// realizamos la conexión al servidor
    //PDEBUG("INFO: Nos conectamos al servidor\n");
    //error = connect(sock, (struct sockaddr*) &addr, length);
    //if(error < 0){
    //    char aux[] = "ERROR: al establecer la conexion con el servidor {{connect()}}: \n";
    //    write(WRITE, aux, strlen(aux));
    //    // terminamos la ejecución del programa
    //    exit(-1);
    //}
    //
    //PDEBUG("INFO: Conexión establecida\n");
    PDEBUG("INFO: Esperando mensaje de bienvenida\n");

    memset(&auxMsj, 0, sizeof(sms));
    if(client_read(bio, &auxMsj, sizeof(sms)) <= 0){
        PDEBUG("INFO: El socket está cerrado\n");
        perror("Error al leer contenido del socket porque está cerrado\n");
        perror("El cliente se parará\n");
        exit(-1);
    }


    // atendemos la autentificación del cliente
    do{
        bzero(aux, DIM);
        sprintf(aux, "%s ~$> %s\n", auxMsj.name, auxMsj.text);
        write(WRITE, aux, DIM);
        bzero(&auxMsj.text, SMS_LEN);

        if(auxMsj.flag == MSJ){ // si es un mensaje, solo imprimimos por la salida estandar

            memset(&auxMsj, 0, sizeof(sms));
            if(client_read(bio, &auxMsj, sizeof(sms)) <= 0){
                PDEBUG("INFO: El socket está cerrado\n");
                perror("Error al leer contenido del socket porque está cerrado\n");
                perror("El cliente se parará\n");
                exit(-1);
            }
            continue;

        }

        if(auxMsj.flag == REQ_PASS){ // si es password desactivamos el echo
            echo_off();
        }
        nbytes = read(READ, &auxMsj.text, SMS_LEN);
        if(auxMsj.flag == REQ_PASS){// si es password activamos el echo
            echo_on();
        }
        auxMsj.text[nbytes - 1] = '\0'; // eliminamos el retorno de carro
        
        // nos salimos?
        if(strcmp(auxMsj.text, "-x") == 0){
            PDEBUG("EXIT: Cerrando el cliente, avisando al servidor...\n");
            auxMsj.flag = CLI_EXIT;
            client_write(bio, &auxMsj, sizeof(sms));
            PDEBUG("EXIT: Cerrando el socket\n");
            shutdown(sock, 2);
            PDEBUG("EXIT: Cerrando el cliente\n");
            exit(0);
        }else{ // es un mensaje
            strcpy(name, auxMsj.text); // hacemos una copia del nombre introducido para no perderlo
            if(auxMsj.flag == REQ_TEXT){
                auxMsj.flag = REQ_AUTH;
            }else if(auxMsj.flag == REQ_PASS){ // entonces REQ_PASS
                auxMsj.flag = CHECK_PASS;
            }else if(auxMsj.flag == REQ_ROOM){ // entonces REQ_ROOM
                auxMsj.flag = CHECK_ROOM;
            }
            client_write(bio, &auxMsj, sizeof(sms));
            memset(&auxMsj, 0, sizeof(sms));
            if(client_read(bio, &auxMsj, sizeof(sms)) <= 0){
                PDEBUG("INFO: El socket está cerrado\n");
                perror("Error al leer contenido del socket porque está cerrado\n");
                perror("El cliente se parará\n");
                exit(-1);
            }
        }
    }while(auxMsj.flag != OK);

    PDEBUG("INFO: Usuario conectado\n");
    printf("Usuario conectado...\n");
    
    fd_set desc, descCopy; // definimos un descriptor que contendrá nuestros descriptores
    //inicializamos la lista de conexiones
    FD_ZERO(&desc);
    // Inicio del bit descriptor sock con el valor de sock
    int fd;
	if(BIO_get_fd(bio, &fd) < 0){
            write(WRITE, "ERROR: crear le descriptor %s\n", DIM);
            // terminamos la ejecución del programa
            exit(-1);
	}
    FD_SET (fd, &desc);
    // Inicio del bit descriptor connList con el valor del descriptor de entrada estándar
    FD_SET (READ, &desc);

    while(1){
        // hacemos una copia de seguridad para asegurarnos de no perder los datos
        descCopy = desc;
        // ¿Hay algún socket listo para leer?
        PDEBUG("INFO: ¿Hay algún socket listo para leer?\n");
        error = select(fd + 1, &descCopy, NULL, NULL, NULL);
        //Comprobamos si ha ocurrido un error al ponernos a escuchar
        if(error < 0){
            write(WRITE, "ERROR: al realizar la selección {{select()}}: %s\n", DIM);
            // terminamos la ejecución del programa
            exit(-1);
        }

        // recorriendo los sockets para ver los que están activos
        PDEBUG("INFO: recorriendo los sockets para ver los que están activos\n");
        if(FD_ISSET(fd, &descCopy)){
            PDEBUG("INFO: Nuevo mensaje recibido\n");
            if(client_read(bio, &auxMsj, sizeof(sms)) <= 0){
                PDEBUG("INFO: El socket está cerrado\n");
                perror("Error al leer contenido del socket porque está cerrado\n");
                perror("El cliente se parará\n");
                exit(-1);
            }

            switch(auxMsj.flag){
                case OK: // mensaje de aceptacion, mismo comportamiento que msj
                case MSJ:  // mensaje recibido
                    if(serverTalk != 1 || strcmp(auxMsj.name, SERVER) == 0){
                        PDEBUG("INFO: Recibido mensaje\n");
                        sprintf(aux, "%s ~$> %s\n", auxMsj.name, auxMsj.text);
                        write(WRITE, aux, strlen(aux));
                        sync();
                    }
                    break;
                case SERV_EXIT: // el servidor se va a cerrar
                    PDEBUG("EXIT: El servidor se está cerrando, se dejará de leer\n");
                    shutdown(sock, SHUT_RDWR);
                    sprintf(aux, "%s ~$> Servidor desconectado\n", SERVER);
                    write(WRITE, aux, strlen(aux));
                    sprintf(aux, "El proceso cliente se cerrará\n");
                    write(WRITE, aux, strlen(aux));
                    exit(0);
                    break;
                default:
                    sprintf(aux, "Recibido un mensaje mal formado\n");
                    write(WRITE, aux, strlen(aux));
                    sync();
                    break;
            }
        }else if(FD_ISSET(READ, &descCopy)){
            PDEBUG("INFO: Nuevo mensaje escrito\n");

            bzero(&auxMsj.text, SMS_LEN); // inicializamos el array
            nbytes = read(READ, &auxMsj.text, SMS_LEN); // leemos de la entrada estándar
            auxMsj.text[nbytes - 1] = 0; // eliminamos el retorno de carro

            // nos salimos?
            if(strcmp(auxMsj.text, "-x") == 0 && serverTalk != 1){

                PDEBUG("EXIT: Cerrando el cliente, avisando al servidor...\n");
                auxMsj.flag = CLI_EXIT;

            }else if(strcmp(auxMsj.text, "--serv") == 0){ // queremos hablar con el servidor

                PDEBUG("SERV_ADMIN: Iniciando la comunicación directa con el servidor\n");
                sprintf(aux, "%s ~$> Iniciada conversación con el servidor\n", SERVER);
                write(WRITE, aux, strlen(aux));
                serverTalk = 1;
                continue;

            }else if(sscanf(auxMsj.text, "--mp %s", aux) == 1){ // queremos hablar con el servidor

                PDEBUG("MP: Mensaje privado detectado\n");
                strcpy(auxMsj.to, aux);
                sprintf(aux, "%s ~$> Inserte el mensaje privado\n", SERVER);
                write(WRITE, aux, strlen(aux));
                auxMsj.flag = MP;
                nbytes = read(READ, &auxMsj.text, SMS_LEN); // leemos de la entrada estándar
                auxMsj.text[nbytes - 1] = 0; // eliminamos el retorno de carro

            }else{ // es un mensaje

                if(serverTalk == 1){

                    PDEBUG("SERV_ADMIN: Enviando mensaje al servidor\n");
                    auxMsj.flag = SERV_ADMIN;

                    if(strcmp(auxMsj.text, "exit") == 0){

                        serverTalk = 0;
                        sprintf(aux, "%s ~$> Envio de mensajes de configuración terminada:\n", SERVER);
                        write(WRITE, aux, strlen(aux));
                        continue;

                    }

                }else{

                    auxMsj.flag = MSJ;

                }
            }

            strcpy(auxMsj.name, name); // hacemos una copia del nombre introducido para no perderlo
            PDEBUG("INFO: Enviando mensaje...\n");
            client_write(bio, &auxMsj, sizeof(sms));
            PDEBUG("INFO: Mensaje Enviado\n");

            // nos salimos?
            if(auxMsj.flag == CLI_EXIT){

                PDEBUG("EXIT: Cerrando el socket\n");
                shutdown(sock, SHUT_RDWR);
                PDEBUG("EXIT: Cerrando el cliente\n");
                exit(0);
                
            }
        }
    }
    
    return 0;
}
Ejemplo n.º 14
0
//--------------------------------------------------------------------------------------------------
le_result_t secSocket_Read
(
    secSocket_Ctx_t* ctxPtr,       ///< [INOUT] Secure socket context pointer
    char*            dataPtr,      ///< [INOUT] Data pointer
    size_t*          dataLenPtr,   ///< [IN] Data length pointer
    uint32_t         timeout       ///< [IN] Read timeout in milliseconds.
)
{
    fd_set set;
    int rv, fd;

    if ((!ctxPtr) || (!dataPtr) || (!dataLenPtr))
    {
        return LE_BAD_PARAMETER;
    }

    OpensslCtx_t* contextPtr = GetContext(ctxPtr);
    if (!contextPtr)
    {
        return LE_BAD_PARAMETER;
    }

    if (!contextPtr->bioPtr)
    {
        LE_ERROR("Socket not connected");
        return LE_FAULT;
    }

    if (!BIO_pending(contextPtr->bioPtr))
    {
        struct timeval time = {.tv_sec = timeout / 1000, .tv_usec = (timeout % 1000) * 1000};

        // Get file descriptor of the current BIO
        BIO_get_fd(contextPtr->bioPtr, &fd);

        do
        {
           FD_ZERO(&set);
           FD_SET(fd, &set);
           rv = select(fd + 1, &set, NULL, NULL, &time);
        }
        while (rv == -1 && errno == EINTR);

        if (rv > 0)
        {
            if (!FD_ISSET(fd, &set))
            {
                LE_ERROR("Nothing to read");
                return LE_FAULT;
            }
        }
        else if (rv == 0)
        {
            return LE_TIMEOUT;
        }
        else
        {
            return LE_FAULT;
        }
    }

    // At this point, there is something available for reading from BIO
    rv = BIO_read(contextPtr->bioPtr, dataPtr, *dataLenPtr);
    if (rv <= 0)
    {
        if (BIO_should_retry(contextPtr->bioPtr))
        {
             return LE_WOULD_BLOCK;
        }
        else
        {
            LE_ERROR("Read failed. Error code: %d", rv);
            return LE_FAULT;
        }
    }

    *dataLenPtr = rv;
    return LE_OK;
}

//--------------------------------------------------------------------------------------------------
/**
 * Check if data is available to be read
 *
 * @return
 *  - True if data is available to be read, false otherwise
 */
//--------------------------------------------------------------------------------------------------
bool secSocket_IsDataAvailable
(
    secSocket_Ctx_t* ctxPtr       ///< [INOUT] Secure socket context pointer
)
{
    if (!ctxPtr)
    {
        return false;
    }

    OpensslCtx_t* contextPtr = GetContext(ctxPtr);
    if (!contextPtr)
    {
        return false;
    }

    return (BIO_pending(contextPtr->bioPtr) ? true: false);
}
Ejemplo n.º 15
0
int proxyhandler(BIO *cbio)
{
   BIO *mbio = NULL, *sbio = NULL;
   char *mptr = NULL;
   long mlen;
   int cfd, sfd, len = 0, found = 0;
   fd_set rfds;
   char buf[1024];
   struct sockaddr_in caddr;
   char auth[1024] = {0};
   int cl = 0;

   mbio = BIO_new(BIO_s_mem());

   for(len = 0; ; len = 0) {
      while(len < sizeof(buf)) {
         if(BIO_read(cbio, buf + len, 1) != 1) return -1;
         if(buf[len++] == '\n') break;
      }
      buf[--len] = '\0';
      if(len && (buf[len - 1] == '\r')) buf[len - 1] = '\0';
      if(!buf[0]) break;

      if(!strncasecmp(buf, "X-Forwarded-For: ", strlen("X-Forwarded-For: "))) found |= FOUND_XFF;
      if(!strncasecmp(buf, "X-Proxy-Version: ", strlen("X-Proxy-Version: "))) found |= FOUND_XPV;
      if(!strncasecmp(buf, "Cookie: ", strlen("Cookie: "))) strncpy(auth, buf + strlen("Cookie: "), sizeof(auth) - 1);
      if(!strncasecmp(buf, "Content-Length: ", strlen("Content-Length: "))) cl = atoi(buf + strlen("Content-Length: "));
      if(BIO_printf(mbio, "%s\r\n", buf) <= 0) return -1;
   }

   logme(LOGMSG_DEBUG, "Cookie: %s", auth);

   if(!strcmp(auth, conf.cookie)) return commandhandler(cbio, cl);

   sbio = BIO_new_connect(conf.nexthop);

   if(BIO_do_connect(sbio) != 1) {
      logme(LOGMSG_STATUSERROR, "Unable to connect to %s", conf.nexthop);

      return -1;
   }
   logme(LOGMSG_STATUSOK, "Running");
   logme(LOGMSG_DEBUG, "Connected to %s", conf.nexthop);
   sfd = BIO_get_fd(sbio, NULL);

   cfd = BIO_get_fd(cbio, NULL);
   len = sizeof(caddr);
   getpeername(cfd, (struct sockaddr *)&caddr, (socklen_t *)&len);

   if(!(found & FOUND_COOKIE)) logme(LOGMSG_DEBUG, "New session forwarded for %s", inet_ntoa(caddr.sin_addr));

   if((mlen = BIO_get_mem_data(mbio, &mptr)) > 0) BIO_write(sbio, mptr, mlen);
   if(!(found & FOUND_XFF)) if(BIO_printf(sbio, "X-Forwarded-For: %s\r\n", inet_ntoa(caddr.sin_addr)) <= 0) return -1;
   if(!(found & FOUND_XPV)) if(BIO_printf(sbio, "X-Proxy-Version: %s\r\n", conf.version) <= 0) return -1;
   if(BIO_puts(sbio, "\r\n") <= 0) return -1;

   do {
      FD_ZERO(&rfds);
      FD_SET(sfd, &rfds);
      FD_SET(cfd, &rfds);
      if(select(((sfd > cfd) ? sfd : cfd) + 1, &rfds, NULL, NULL, NULL) == -1) return -1;

      if(FD_ISSET(sfd, &rfds)) {
         if((len = BIO_read(sbio, buf, sizeof(buf))) > 0) if(BIO_write(cbio, buf, len) <= 0) return -1;
      } else if(FD_ISSET(cfd, &rfds)) {
         if((len = BIO_read(cbio, buf, sizeof(buf))) > 0) if(BIO_write(sbio, buf, len) <= 0) return -1;
      }
   } while(len > 0);

   return 0;
}
Ejemplo n.º 16
0
bool TLS_SOCKET_CLASS::accept(BASE_SOCKET_CLASS** acceptedSocket_ptr_ptr)

//  DESCRIPTION     : Accept connection from listen socket.
//  PRECONDITIONS   :
//  POSTCONDITIONS  :
//  EXCEPTIONS      : 
//  NOTES           : The returned socket was new'ed so it must be deleted by the caller
//<<===========================================================================
{
	*acceptedSocket_ptr_ptr = NULL;
	BIO* acceptedBio_ptr;
	SSL* acceptedSsl_ptr;
	long error;
	int fileDesc;
	struct fd_set fds;
	struct timeval tv = {1, 0}; // always timeout in 1 second
	int sel;

	if (terminatingM)
	{
		if (loggerM_ptr) 
		{
			loggerM_ptr->text(LOG_ERROR, 1, "Secure Socket - In process of terminating.  Cannot accept.");
		}

		// return - in process of termintating
		return false;
	}

	// make sure we are listening to the port
	if (!listeningM)
	{
		if (loggerM_ptr)
		{
			loggerM_ptr->text(LOG_ERROR, 1, "Secure Socket - Socket is not listening to port %d.  Cannot accept a connection.", 
				localListenPortM);
		}
		return false;
	}
	if (acceptBioM_ptr == NULL)
	{
		if (loggerM_ptr)
		{
			loggerM_ptr->text(LOG_ERROR, 1, "Secure Socket - Socket is listening to port %d, but not bound.", 
				localListenPortM);
		}
		listeningM = false;
		return false;
	}

	if (loggerM_ptr) 
	{
		loggerM_ptr->text(LOG_DEBUG, 1, "Secure Socket - tls::accept()");
	}

	// get the file descriptor and set up the file descriptor set for select()
	fileDesc = BIO_get_fd(acceptBioM_ptr, NULL);
	if (fileDesc == -1)
	{
		openSslError("getting listen socket file descriptor");
		return false;
	}

	// wait for a connection
	do
	{
		FD_ZERO(&fds);
		FD_SET(fileDesc, &fds);

		sel = select(fileDesc + 1, &fds, NULL, NULL, &tv);
		if (sel == 0)
		{
			// no data at the end of the timeout - check for terminating
			if (terminatingM)
			{
				return false;
			}
		}
		else if (sel == SOCKET_ERROR)
		{
			// socket error
			if (loggerM_ptr)
			{
				loggerM_ptr->text(LOG_ERROR, 2, "Secure Socket - Error waiting to accept connection (error code %d)", WSAGetLastError());
			}
			return false;
		}
		else if (sel != 1)
		{
			// unknown error
			if (loggerM_ptr)
			{
				loggerM_ptr->text(LOG_ERROR, 2, "Secure Socket - Unknown error while waiting to accept connection (select returned %d)", sel);
			}
			return false;
		}

	} while (sel != 1);

	// accept a connection
	if (BIO_do_accept(acceptBioM_ptr) <= 0)
	{
		openSslError(LOG_DEBUG, "accepting connection on port %d", localListenPortM);
		close();
		return false;
	}

	// get the new connection
	acceptedBio_ptr = BIO_pop(acceptBioM_ptr);

	// setup the socket structure
	acceptedSsl_ptr = SSL_new(ctxM_ptr);
	if (acceptedSsl_ptr == NULL)
	{
		openSslError("creating accepted secure socket object");
		BIO_free(acceptedBio_ptr);
		return false;
	}

	// set the 'this' pointer for the callbacks openSslMsgCallback and openSslVerifyCallback
	SSL_set_msg_callback_arg(acceptedSsl_ptr, static_cast<void*>(this));

	SSL_set_accept_state(acceptedSsl_ptr);
	SSL_set_bio(acceptedSsl_ptr, acceptedBio_ptr, acceptedBio_ptr); // the ssl takes over the BIO memory
	if (SSL_accept(acceptedSsl_ptr) <= 0)
	{
		openSslError("accepting secure connection to port %d", localListenPortM);
		SSL_free(acceptedSsl_ptr);
		return false;
	}

	// make sure everything is OK
	error = postConnectionCheck(acceptedSsl_ptr);
	if (error != X509_V_OK)
	{
		openSslError("checking connection parameters after accepting from port %d", localListenPortM);
		SSL_free(acceptedSsl_ptr);
		return false;
	}

	// create a socket for the new connection
	*acceptedSocket_ptr_ptr = new TLS_SOCKET_CLASS(*this, acceptedSsl_ptr);

	if (loggerM_ptr && (loggerM_ptr->getLogMask() & LOG_DEBUG))
	{
		char buffer[128];

		SSL_CIPHER_description(SSL_get_current_cipher(acceptedSsl_ptr), buffer, 128);
		loggerM_ptr->text(LOG_DEBUG, 1, "Secure Socket - %s connection opened using cipher %s",
			SSL_get_version(acceptedSsl_ptr), buffer);
	}

	return true;
}
Ejemplo n.º 17
0
BOOL tcp_connect(rdpTcp* tcp, const char* hostname, int port, int timeout)
{
	int status;
	UINT32 option_value;
	socklen_t option_len;

	if (!hostname)
		return FALSE;

	if (hostname[0] == '/')
	{
		tcp->sockfd = freerdp_uds_connect(hostname);

		if (tcp->sockfd < 0)
			return FALSE;

		tcp->socketBio = BIO_new_fd(tcp->sockfd, 1);

		if (!tcp->socketBio)
			return FALSE;
	}
	else
	{
		fd_set cfds;
		struct timeval tv;

		tcp->socketBio = BIO_new(BIO_s_connect());

		if (!tcp->socketBio)
			return FALSE;

		if (BIO_set_conn_hostname(tcp->socketBio, hostname) < 0 || BIO_set_conn_int_port(tcp->socketBio, &port) < 0)
			return FALSE;

		BIO_set_nbio(tcp->socketBio, 1);

		status = BIO_do_connect(tcp->socketBio);

		if ((status <= 0) && !BIO_should_retry(tcp->socketBio))
			return FALSE;

		tcp->sockfd = BIO_get_fd(tcp->socketBio, NULL);

		if (tcp->sockfd < 0)
			return FALSE;

		if (status <= 0)
		{
			FD_ZERO(&cfds);
			FD_SET(tcp->sockfd, &cfds);

			tv.tv_sec = timeout;
			tv.tv_usec = 0;

			status = select(tcp->sockfd + 1, NULL, &cfds, NULL, &tv);

			if (status == 0)
			{
				return FALSE; /* timeout */
			}
		}

		BIO_set_close(tcp->socketBio, BIO_NOCLOSE);
		BIO_free(tcp->socketBio);

		tcp->socketBio = BIO_new(BIO_s_simple_socket());

		if (!tcp->socketBio)
			return -1;

		BIO_set_fd(tcp->socketBio, tcp->sockfd, BIO_CLOSE);
	}

	SetEventFileDescriptor(tcp->event, tcp->sockfd);

	tcp_get_ip_address(tcp);
	tcp_get_mac_address(tcp);

	option_value = 1;
	option_len = sizeof(option_value);

	if (setsockopt(tcp->sockfd, IPPROTO_TCP, TCP_NODELAY, (void*) &option_value, option_len) < 0)
		fprintf(stderr, "%s: unable to set TCP_NODELAY\n", __FUNCTION__);

	/* receive buffer must be a least 32 K */
	if (getsockopt(tcp->sockfd, SOL_SOCKET, SO_RCVBUF, (void*) &option_value, &option_len) == 0)
	{
		if (option_value < (1024 * 32))
		{
			option_value = 1024 * 32;
			option_len = sizeof(option_value);

			if (setsockopt(tcp->sockfd, SOL_SOCKET, SO_RCVBUF, (void*) &option_value, option_len) < 0)
			{
				fprintf(stderr, "%s: unable to set receive buffer len\n", __FUNCTION__);
				return FALSE;
			}
		}
	}

	if (!tcp_set_keep_alive_mode(tcp))
		return FALSE;

	tcp->bufferedBio = BIO_new(BIO_s_buffered_socket());

	if (!tcp->bufferedBio)
		return FALSE;

	tcp->bufferedBio->ptr = tcp;

	tcp->bufferedBio = BIO_push(tcp->bufferedBio, tcp->socketBio);

	return TRUE;
}
Ejemplo n.º 18
0
Archivo: pop.c Proyecto: kusune/from
/* otherwise return POP_OK             */
int pop_readline(POP_SESSION *psp)
{
    int len = 0;

#ifdef WITH_OPENSSL
    assert(psp != NULL && psp->bio != NULL);
#else /* WITH_OPENSSL */
    assert(psp != NULL && psp->fr != NULL);
#endif /* WITH_OPENSSL */

    psp->resp = NULL;
    if (psp->rest != NULL && psp->restlen > 0) {
	memmove(psp->rbuf, psp->rest, psp->restlen);
	len = psp->restlen;
    }
    psp->rest = NULL;
    psp->restlen = 0;

    do {
	char *newline = memchr(psp->rbuf, '\n', len);

	if (newline != NULL) {
	    *(newline++) = NUL;
	    if (newline - psp->rbuf >= len) {
		psp->rest = newline;
		psp->restlen = len - (psp->rest - psp->rbuf);
	    }

	    return POP_OK;
	}

	{
	    fd_set fdr;
	    int fd;
	    int c;

#ifdef WITH_OPENSSL
	    fd = BIO_get_fd(psp->bio, NULL);
#else /* WITH_OPENSSL */
	    fd = psp->sr;
#endif /* WITH_OPENSSL */

	    do {
		FD_ZERO(&fdr);
		FD_SET(fd, &fdr);
		c = select(fd + 1, &fdr, NULL, NULL, NULL);
	    } while (!FD_ISSET(fd, &fdr));
	}

	{
	    int n;

#ifdef WITH_OPENSSL
	    n = BIO_gets(psp->bio, psp->rbuf + len, POP_MAXRESPLEN - len);
#else /* WITH_OPENSSL */
	    if (fgets(psp->rbuf + len, POP_MAXRESPLEN - len, psp->fr) == NULL) {
		n = -1;
	    } else {
		n = strlen(psp->rbuf + len);
	    }
#endif /* WITH_OPENSSL */

	    if (n < 0) {
		if (errno == EAGAIN) {
		    continue;
		}
#ifdef WITH_OPENSSL
		__pop_set_error_openssl(psp, "BIO_gets(): ");
#else /* WITH_OPENSSL */
		__pop_set_error_errno(psp, "read(): ");
#endif /* WITH_OPENSSL */
		return POP_ERR;
	    }
	    len += n;
	}
    } while (len < POP_MAXRESPLEN);

    *(psp->rbuf + len) = NUL;
    return POP_CONT;
}
Ejemplo n.º 19
0
int tls_do_handshake(rdpTls* tls, BOOL clientMode)
{
	CryptoCert cert;
	int verify_status;

	do
	{
#ifdef HAVE_POLL_H
		int fd;
		int status;
		struct pollfd pollfds;
#elif !defined(_WIN32)
		int fd;
		int status;
		fd_set rset;
		struct timeval tv;
#else
		HANDLE event;
		DWORD status;
#endif
		status = BIO_do_handshake(tls->bio);

		if (status == 1)
			break;

		if (!BIO_should_retry(tls->bio))
			return -1;

#ifndef _WIN32
		/* we select() only for read even if we should test both read and write
		 * depending of what have blocked */
		fd = BIO_get_fd(tls->bio, NULL);

		if (fd < 0)
		{
			WLog_ERR(TAG, "unable to retrieve BIO fd");
			return -1;
		}

#else
		BIO_get_event(tls->bio, &event);

		if (!event)
		{
			WLog_ERR(TAG, "unable to retrieve BIO event");
			return -1;
		}

#endif
#ifdef HAVE_POLL_H
		pollfds.fd = fd;
		pollfds.events = POLLIN;
		pollfds.revents = 0;

		do
		{
			status = poll(&pollfds, 1, 10 * 1000);
		}
		while ((status < 0) && (errno == EINTR));

#elif !defined(_WIN32)
		FD_ZERO(&rset);
		FD_SET(fd, &rset);
		tv.tv_sec = 0;
		tv.tv_usec = 10 * 1000; /* 10ms */
		status = _select(fd + 1, &rset, NULL, NULL, &tv);
#else
		status = WaitForSingleObject(event, 10);
#endif
#ifndef _WIN32

		if (status < 0)
		{
			WLog_ERR(TAG, "error during select()");
			return -1;
		}

#else

		if ((status != WAIT_OBJECT_0) && (status != WAIT_TIMEOUT))
		{
			WLog_ERR(TAG, "error during WaitForSingleObject(): 0x%04X", status);
			return -1;
		}

#endif
	}
	while (TRUE);

	cert = tls_get_certificate(tls, clientMode);

	if (!cert)
	{
		WLog_ERR(TAG, "tls_get_certificate failed to return the server certificate.");
		return -1;
	}

	tls->Bindings = tls_get_channel_bindings(cert->px509);

	if (!tls->Bindings)
	{
		WLog_ERR(TAG, "unable to retrieve bindings");
		verify_status = -1;
		goto out;
	}

	if (!crypto_cert_get_public_key(cert, &tls->PublicKey, &tls->PublicKeyLength))
	{
		WLog_ERR(TAG,
		         "crypto_cert_get_public_key failed to return the server public key.");
		verify_status = -1;
		goto out;
	}

	/* server-side NLA needs public keys (keys from us, the server) but no certificate verify */
	verify_status = 1;

	if (clientMode)
	{
		verify_status = tls_verify_certificate(tls, cert, tls->hostname, tls->port);

		if (verify_status < 1)
		{
			WLog_ERR(TAG, "certificate not trusted, aborting.");
			tls_send_alert(tls);
			verify_status = 0;
		}
	}

out:
	tls_free_certificate(cert);
	return verify_status;
}
Ejemplo n.º 20
0
int SSLBufferConnect(struct sslbuffer_t *sslbuffer, const char *host,
	const char *port)
{
	int res;
	int fd;
	struct event *ev_write = NULL;
	struct event *ev_read = NULL;
	BIO *bio = NULL;
	unsigned long error;

	bio = BIO_new(BIO_s_connect( ));
	if (bio == NULL)
		return 0;

	BIO_set_conn_hostname(bio, host);
	BIO_set_conn_port(bio, port);
	BIO_set_nbio(bio, 1);

	SSL_set_bio(sslbuffer->ssl, bio, bio);

	res = SSL_connect(sslbuffer->ssl);
	if (res <= 0)
	{
		error = SSL_get_error(sslbuffer->ssl, res);
		sslbuffer->fl_connecting = 1;
		if ( (error != SSL_ERROR_WANT_CONNECT) &&
		     (error != SSL_ERROR_WANT_READ) &&
		     (error != SSL_ERROR_WANT_WRITE) )
		{
			printf("%s:%d: unknown error %lu\n", __FILE__, __LINE__, error);
			print_errors();
			goto Error;
		}
	}

	fd = BIO_get_fd(bio, NULL);

	ev_read = event_new(sslbuffer->base, fd, EV_READ|EV_PERSIST, SSLBuffer_func, sslbuffer);
	if (ev_read == NULL)
		goto Error;
	res = event_add(ev_read, NULL);
	if (res != 0)
		goto Error;
	sslbuffer->ev_read = ev_read;

	ev_write = event_new(sslbuffer->base, fd, EV_WRITE|EV_PERSIST, SSLBuffer_func, sslbuffer);
	if (ev_write == NULL)
		goto Error;

    res = event_add(ev_write, NULL);
    if (res != 0)
        goto Error;
	sslbuffer->ev_write = ev_write;

	return 1;

Error:
	if (ev_write != NULL)
		event_free(ev_write);
	if (ev_read != NULL)
		event_free(ev_read);
	return 0;
}
Ejemplo n.º 21
0
int anetSSLGenericConnect( char* err, char* addr, int port, int flags, anetSSLConnection* sslctn, char* certFilePath, char* certDirPath, char* checkCommonName ) {
  sslctn->sd = -1;
  sslctn->ctx = NULL;
  sslctn->ssl = NULL;
  sslctn->bio = NULL;
  sslctn->conn_str = NULL;

  // Set up a SSL_CTX object, which will tell our BIO object how to do its work
  SSL_CTX* ctx = SSL_CTX_new(TLSv1_1_client_method());
  sslctn->ctx = ctx;

  // Create a SSL object pointer, which our BIO object will provide.
  SSL* ssl;

  // Create our BIO object for SSL connections.
  BIO* bio = BIO_new_ssl_connect(ctx);
  sslctn->bio = bio;

  // Failure?
  if (bio == NULL) {
     char errorbuf[1024];

     ERR_error_string(1024,errorbuf);
     anetSetError(err, "SSL Error: Error creating BIO: %s\n", errorbuf);

     // We need to free up the SSL_CTX before we leave.
     anetCleanupSSL( sslctn );
     return ANET_ERR;
  }

  // Makes ssl point to bio's SSL object.
  BIO_get_ssl(bio, &ssl);
  sslctn->ssl = ssl;

  // Set the SSL to automatically retry on failure.
  SSL_set_mode(ssl, SSL_MODE_AUTO_RETRY);

  char* connect_str = (char *)calloc( 1, strlen( addr ) + 10 );
  sprintf( connect_str, "%s:%d", addr, port );
  sslctn->conn_str = connect_str;

  // We're connection to to the redis server at IP:port.
  BIO_set_conn_hostname(bio, connect_str);

  SSL_CTX_load_verify_locations(ctx, certFilePath, certDirPath);

  // Same as before, try to connect.
  if (BIO_do_connect(bio) <= 0) {
    char errorbuf[1024];
    ERR_error_string(1024,errorbuf);
    anetSetError(err, "SSL Error: Failed to connect: %s\n", errorbuf);
    anetCleanupSSL( sslctn );
    return ANET_ERR;
  }

  // Now we need to do the SSL handshake, so we can communicate.
  if (BIO_do_handshake(bio) <= 0) {
    char errorbuf[1024];
    ERR_error_string(1024,errorbuf);
    anetSetError(err, "SSL Error: handshake failure: %s\n", errorbuf);
    anetCleanupSSL( sslctn );
    return ANET_ERR;
  }

  long verify_result = SSL_get_verify_result(ssl);
  if( verify_result == X509_V_OK) {
    X509* peerCertificate = SSL_get_peer_certificate(ssl);

    char commonName [512];
    X509_NAME * name = X509_get_subject_name(peerCertificate);
    X509_NAME_get_text_by_NID(name, NID_commonName, commonName, 512);
    if( checkCommonName != NULL && strlen( checkCommonName ) > 0 ) {
      if(wildcmp(commonName, checkCommonName, strlen(checkCommonName)) == 0) {
        anetSetError(err, "SSL Error: Error validating peer common name: %s\n", commonName);
        anetCleanupSSL( sslctn );
        return ANET_ERR;
      }
    }
  } else {
     char errorbuf[1024];
     ERR_error_string(1024,errorbuf);
     anetSetError(err, "SSL Error: Error retrieving peer certificate: %s\n", errorbuf);
     anetCleanupSSL( sslctn );
     return ANET_ERR;
  }

  int s = BIO_get_fd( bio, NULL );

  if (flags & ANET_CONNECT_NONBLOCK) {
       if (anetNonBlock(err,s) != ANET_OK)
         return ANET_ERR;
  }

  return s;
}
enum pbpal_resolv_n_connect_result pbpal_resolv_and_connect(pubnub_t *pb)
{
    SSL *ssl = NULL;
    int rslt;
    char const* origin = PUBNUB_ORIGIN_SETTABLE ? pb->origin : PUBNUB_ORIGIN;

    PUBNUB_ASSERT(pb_valid_ctx_ptr(pb));
    PUBNUB_ASSERT_OPT((pb->state == PBS_READY) || (pb->state == PBS_WAIT_CONNECT));

    if (!pb->options.useSSL) {
        return resolv_and_connect_wout_SSL(pb);
    }

    if (NULL == pb->pal.ctx) {
        PUBNUB_LOG_TRACE("pb=%p: Don't have SSL_CTX\n", pb);
        pb->pal.ctx = SSL_CTX_new(SSLv23_client_method());
        if (NULL == pb->pal.ctx) {
            ERR_print_errors_cb(print_to_pubnub_log, NULL);
            PUBNUB_LOG_ERROR("pb=%p SSL_CTX_new failed\n", pb);
            return pbpal_resolv_resource_failure;
        }
        PUBNUB_LOG_TRACE("pb=%p: Got SSL_CTX\n", pb);
        add_pubnub_cert(pb->pal.ctx);
    }

    if (NULL == pb->pal.socket) {
        PUBNUB_LOG_TRACE("pb=%p: Don't have BIO\n", pb);
        pb->pal.socket = BIO_new_ssl_connect(pb->pal.ctx);
        if (PUBNUB_TIMERS_API) {
            pb->pal.connect_timeout = time(NULL)  + pb->transaction_timeout_ms/1000;
        }
    }
    else {
        BIO_get_ssl(pb->pal.socket, &ssl);
        if (NULL == ssl) {
            return resolv_and_connect_wout_SSL(pb);
        }
        ssl = NULL;
    }
    if (NULL == pb->pal.socket) {
        ERR_print_errors_cb(print_to_pubnub_log, NULL);
        return pbpal_resolv_resource_failure;
    }

    PUBNUB_LOG_TRACE("pb=%p: Using BIO == %p\n", pb, pb->pal.socket);

    BIO_get_ssl(pb->pal.socket, &ssl);
    PUBNUB_ASSERT(NULL != ssl);
    SSL_set_mode(ssl, SSL_MODE_AUTO_RETRY); /* maybe not auto_retry? */
    if (pb->pal.session != NULL) {
        SSL_set_session(ssl, pb->pal.session);
    }

    BIO_set_conn_hostname(pb->pal.socket, origin);
    BIO_set_conn_port(pb->pal.socket, "https");
    if (pb->pal.ip_timeout != 0) {
        if (pb->pal.ip_timeout < time(NULL)) {
            pb->pal.ip_timeout = 0;
        }
        else {
            PUBNUB_LOG_TRACE("SSL re-connect to: %d.%d.%d.%d\n", pb->pal.ip[0], pb->pal.ip[1], pb->pal.ip[2], pb->pal.ip[3]);
            BIO_set_conn_ip(pb->pal.socket, pb->pal.ip);
        }
    }
    
    BIO_set_nbio(pb->pal.socket, !pb->options.use_blocking_io);
    
    WATCH_ENUM(pb->options.use_blocking_io);
    if (BIO_do_connect(pb->pal.socket) <= 0) {
        if (BIO_should_retry(pb->pal.socket) && PUBNUB_TIMERS_API && (pb->pal.connect_timeout > time(NULL))) {
            PUBNUB_LOG_TRACE("pb=%p: BIO_should_retry\n", pb);
            return pbpal_connect_wouldblock;
        }
        /* Expire the IP for the next connect */
        pb->pal.ip_timeout = 0;
        ERR_print_errors_cb(print_to_pubnub_log, NULL);
        if (pb->pal.session != NULL) {
            SSL_SESSION_free(pb->pal.session);
            pb->pal.session = NULL;
        }
        PUBNUB_LOG_ERROR("pb=%p: BIO_do_connect failed\n", pb);
        return pbpal_connect_failed;
    }

    PUBNUB_LOG_TRACE("pb=%p: BIO connected\n", pb);
    {
        int fd = BIO_get_fd(pb->pal.socket, NULL);
        socket_set_rcv_timeout(fd, pb->transaction_timeout_ms);
    }

    rslt = SSL_get_verify_result(ssl);
    if (rslt != X509_V_OK) {
        PUBNUB_LOG_WARNING("pb=%p: SSL_get_verify_result() failed == %d(%s)\n", pb, rslt, X509_verify_cert_error_string(rslt));
        ERR_print_errors_cb(print_to_pubnub_log, NULL);
        if (pb->options.fallbackSSL) {
            BIO_free_all(pb->pal.socket);
            pb->pal.socket = NULL;
            return resolv_and_connect_wout_SSL(pb);
        }
        return pbpal_connect_failed;
    }

    PUBNUB_LOG_INFO("pb=%p: SSL session reused: %s\n", pb, SSL_session_reused(ssl) ? "yes" : "no");
    if (pb->pal.session != NULL) {
        SSL_SESSION_free(pb->pal.session);
    }
    pb->pal.session = SSL_get1_session(ssl);
    if (0 == pb->pal.ip_timeout) {
        pb->pal.ip_timeout = SSL_SESSION_get_time(pb->pal.session) + SSL_SESSION_get_timeout(pb->pal.session);
        memcpy(pb->pal.ip, BIO_get_conn_ip(pb->pal.socket), 4);
    }
    PUBNUB_LOG_TRACE("pb=%p: SSL connected to IP: %d.%d.%d.%d\n", pb, pb->pal.ip[0], pb->pal.ip[1], pb->pal.ip[2], pb->pal.ip[3]);

    return pbpal_connect_success;
}