Ejemplo n.º 1
0
BOOL transport_tsg_connect(rdpTransport* transport, const char* hostname, UINT16 port)
{
	rdpTsg* tsg = tsg_new(transport);

	tsg->transport = transport;
	transport->tsg = tsg;
	transport->SplitInputOutput = TRUE;

	if (transport->TlsIn == NULL)
		transport->TlsIn = tls_new(transport->settings);

	transport->TlsIn->sockfd = transport->TcpIn->sockfd;

	if (transport->TlsOut == NULL)
		transport->TlsOut = tls_new(transport->settings);

	transport->TlsOut->sockfd = transport->TcpOut->sockfd;

	if (tls_connect(transport->TlsIn) != TRUE)
		return FALSE;

	if (tls_connect(transport->TlsOut) != TRUE)
		return FALSE;

	if (!tsg_connect(tsg, hostname, port))
		return FALSE;

	return TRUE;
}
Ejemplo n.º 2
0
boolean transport_tsg_connect(rdpTransport* transport, const char* hostname, uint16 port)
{
	rdpTsg* tsg = tsg_new(transport);

	tsg->transport = transport;
	transport->tsg = tsg;

	if (transport->tls_in == NULL)
		transport->tls_in = tls_new(transport->settings);

	transport->tls_in->sockfd = transport->tcp_in->sockfd;

	if (transport->tls_out == NULL)
		transport->tls_out = tls_new(transport->settings);

	transport->tls_out->sockfd = transport->tcp_out->sockfd;

	if (tls_connect(transport->tls_in) != true)
		return false;

	if (tls_connect(transport->tls_out) != true)
		return false;

	if (!tsg_connect(tsg, hostname, port))
		return false;

	return true;
}
Ejemplo n.º 3
0
BOOL transport_connect_tls(rdpTransport* transport)
{
	if (transport->layer == TRANSPORT_LAYER_TSG)
	{
		transport->TsgTls = tls_new(transport->settings);

		transport->TsgTls->methods = BIO_s_tsg();
		transport->TsgTls->tsg = (void*) transport->tsg;

		transport->layer = TRANSPORT_LAYER_TSG_TLS;

		if (tls_connect(transport->TsgTls) != TRUE)
		{
			if (!connectErrorCode)
				connectErrorCode = TLSCONNECTERROR;

			tls_free(transport->TsgTls);
			transport->TsgTls = NULL;

			return FALSE;
		}

		return TRUE;
	}

	if (transport->TlsIn == NULL)
		transport->TlsIn = tls_new(transport->settings);

	if (transport->TlsOut == NULL)
		transport->TlsOut = transport->TlsIn;

	transport->layer = TRANSPORT_LAYER_TLS;
	transport->TlsIn->sockfd = transport->TcpIn->sockfd;

	if (tls_connect(transport->TlsIn) != TRUE)
	{
		if (!connectErrorCode)
			connectErrorCode = TLSCONNECTERROR;

		tls_free(transport->TlsIn);

		if (transport->TlsIn == transport->TlsOut)
			transport->TlsIn = transport->TlsOut = NULL;
		else
			transport->TlsIn = NULL;

		return FALSE;
	}

	return TRUE;
}
Ejemplo n.º 4
0
boolean transport_connect_nla(rdpTransport* transport)
{
	if (transport->tls == NULL)
		transport->tls = tls_new();

	transport->layer = TRANSPORT_LAYER_TLS;
	transport->tls->sockfd = transport->tcp->sockfd;

	if (tls_connect(transport->tls) != True)
		return False;

	/* Network Level Authentication */

	if (transport->settings->authentication != True)
		return True;

	if (transport->credssp == NULL)
		transport->credssp = credssp_new(transport);

	if (credssp_authenticate(transport->credssp) < 0)
	{
		printf("Authentication failure, check credentials.\n"
			"If credentials are valid, the NTLMSSP implementation may be to blame.\n");

		credssp_free(transport->credssp);
		return False;
	}

	credssp_free(transport->credssp);

	return True;
}
Ejemplo n.º 5
0
/** callback for tls_ct_q_flush().
 *
 * @param *ssl - ssl context.
 * @param *err - error reason (set on exit).
 * @return >0 on success (bytes written), <=0 on ssl error (should be
 *  handled outside).
 * WARNING: the ssl context must have the wbio and rbio previously set!
 */
static int ssl_flush(void* tcp_c, void* error, const void* buf, unsigned size)
{
	int n;
	int ssl_error;
	struct tls_extra_data* tls_c;
	SSL* ssl;
	
	tls_c = ((struct tcp_connection*)tcp_c)->extra_data;
	ssl = tls_c->ssl;
	ssl_error = SSL_ERROR_NONE;
	if (unlikely(tls_c->state == S_TLS_CONNECTING)) {
		n = tls_connect(tcp_c, &ssl_error);
		if (unlikely(n>=1)) {
			n = SSL_write(ssl, buf, size);
			if (unlikely(n <= 0))
				ssl_error = SSL_get_error(ssl, n);
		}
	} else if (unlikely(tls_c->state == S_TLS_ACCEPTING)) {
		n = tls_accept(tcp_c, &ssl_error);
		if (unlikely(n>=1)) {
			n = SSL_write(ssl, buf, size);
			if (unlikely(n <= 0))
				ssl_error = SSL_get_error(ssl, n);
		}
	} else {
		n = SSL_write(ssl, buf, size);
		if (unlikely(n <= 0))
			ssl_error = SSL_get_error(ssl, n);
	}
	
	*(long*)error = ssl_error;
	return n;
}
Ejemplo n.º 6
0
BOOL transport_connect_tls(rdpTransport* transport)
{
	if (transport->layer == TRANSPORT_LAYER_TSG)
		return TRUE;

	if (transport->TlsIn == NULL)
		transport->TlsIn = tls_new(transport->settings);

	if (transport->TlsOut == NULL)
		transport->TlsOut = transport->TlsIn;

	transport->layer = TRANSPORT_LAYER_TLS;
	transport->TlsIn->sockfd = transport->TcpIn->sockfd;

	if (tls_connect(transport->TlsIn) != TRUE)
	{
		if (!connectErrorCode)
			connectErrorCode = TLSCONNECTERROR;

		tls_free(transport->TlsIn);
		if (transport->TlsIn == transport->TlsOut)
			transport->TlsIn = transport->TlsOut = NULL;
		else
			transport->TlsIn = NULL;

		return FALSE;
	}

	return TRUE;
}
Ejemplo n.º 7
0
/*
 * called before tls_read, the this function should attempt tls_accept or
 * tls_connect depending on the state of the connection, if this function
 * does not transit a connection into S_CONN_OK then tcp layer would not
 * call tcp_read
 */
int tls_fix_read_conn(struct tcp_connection *c)
{
	/*
	* no lock acquired
	*/
	int             ret;

	ret = 0;

	/*
	* We have to acquire the lock before testing c->state, otherwise a
	* writer could modify the structure if it gets preempted and has
	* something to write
	*/
	lock_get(&c->write_lock);

	if ( c->proto_flags & F_TLS_DO_ACCEPT ) {
		ret = tls_update_fd(c, c->fd);
		if (!ret)
			ret = tls_accept(c, NULL);
	} else if ( c->proto_flags & F_TLS_DO_CONNECT ) {
		ret = tls_update_fd(c, c->fd);
		if (!ret)
			ret = tls_connect(c, NULL);
	}

	lock_release(&c->write_lock);

	return ret;
}
Ejemplo n.º 8
0
BOOL rdg_tls_out_connect(rdpRdg* rdg, const char* hostname, UINT16 port, int timeout)
{
	int sockfd = 0;
	int status = 0;
	BIO* socketBio = NULL;
	BIO* bufferedBio = NULL;
	rdpSettings* settings = rdg->settings;

	assert(hostname != NULL);

	sockfd = freerdp_tcp_connect(rdg->context, settings, settings->GatewayHostname,
					settings->GatewayPort, timeout);

	if (sockfd < 1)
	{
		return FALSE;
	}

	socketBio = BIO_new(BIO_s_simple_socket());

	if (!socketBio)
	{
		closesocket(sockfd);
		return FALSE;
	}

	BIO_set_fd(socketBio, sockfd, BIO_CLOSE);
	bufferedBio = BIO_new(BIO_s_buffered_socket());

	if (!bufferedBio)
	{
		BIO_free(socketBio);
		return FALSE;
	}

	bufferedBio = BIO_push(bufferedBio, socketBio);
	status = BIO_set_nonblock(bufferedBio, TRUE);

	if (!status)
	{
		BIO_free_all(bufferedBio);
		return FALSE;
	}

	rdg->tlsOut->hostname = settings->GatewayHostname;
	rdg->tlsOut->port = settings->GatewayPort;
	rdg->tlsOut->isGatewayTransport = TRUE;
	status = tls_connect(rdg->tlsOut, bufferedBio);

	if (status < 1)
	{
		return FALSE;
	}

	return TRUE;
}
Ejemplo n.º 9
0
int main(int argc, char *argv[])
{
	struct tls_config *conf;
	struct tls *ctx;
	struct tls_cert_info *cert;
	int res;
	const char *host;

	if (argc < 2)
		errx(1, "give host as arg\n");
	host = argv[1];

	res = tls_init();
	if (res < 0)
		errx(1, "tls_init");

	conf = tls_config_new();
	if (!conf)
		errx(1, "tls_config_new");

	tls_config_set_protocols(conf, TLS_PROTOCOLS_ALL);
	tls_config_set_ciphers(conf, "fast");

	ctx = tls_client();
	if (!ctx)
		errx(1, "tls_client");

	res = tls_configure(ctx, conf);
	if (res < 0)
		errx(1, "tls_configure: %s", tls_error(ctx));

	res = tls_connect(ctx, host, "443");
	if (res < 0)
		errx(1, "tls_connect: %s", tls_error(ctx));

	printf("connect ok\n");

	res = tls_get_peer_cert(ctx, &cert);
	if (res < 0)
		errx(1, "tls_get_peer_cert: %s", tls_error(ctx));

	tls_close(ctx);
	tls_free(ctx);
	tls_config_free(conf);

	printf("  CN='%s'\n", cert->subject.common_name);
	printf("  C='%s'\n", cert->subject.country_name);
	printf("  ST='%s'\n", cert->subject.state_or_province_name);
	printf("  L='%s'\n", cert->subject.locality_name);
	printf("  S='%s'\n", cert->subject.street_address);
	printf("  O='%s'\n", cert->subject.organization_name);
	printf("  OU='%s'\n", cert->subject.organizational_unit_name);

	tls_cert_free(cert);
	return 0;
}
Ejemplo n.º 10
0
BOOL transport_connect_tls(rdpTransport* transport)
{
	int tlsStatus;
	rdpTls* tls = NULL;
	rdpContext* context = transport->context;
	rdpSettings* settings = transport->settings;

	if (!(tls = tls_new(settings)))
		return FALSE;

	transport->tls = tls;

	if (transport->GatewayEnabled)
		transport->layer = TRANSPORT_LAYER_TSG_TLS;
	else
		transport->layer = TRANSPORT_LAYER_TLS;

	tls->hostname = settings->ServerHostname;
	tls->port = settings->ServerPort;

	if (tls->port == 0)
		tls->port = 3389;

	tls->isGatewayTransport = FALSE;
	tlsStatus = tls_connect(tls, transport->frontBio);

	if (tlsStatus < 1)
	{
		if (tlsStatus < 0)
		{
			if (!freerdp_get_last_error(context))
				freerdp_set_last_error(context, FREERDP_ERROR_TLS_CONNECT_FAILED);
		}
		else
		{
			if (!freerdp_get_last_error(context))
				freerdp_set_last_error(context, FREERDP_ERROR_CONNECT_CANCELLED);
		}

		return FALSE;
	}

	transport->frontBio = tls->bio;

	BIO_callback_ctrl(tls->bio, BIO_CTRL_SET_CALLBACK, (bio_info_cb*) transport_ssl_cb);
	SSL_set_app_data(tls->ssl, transport);

	if (!transport->frontBio)
	{
		WLog_ERR(TAG, "unable to prepend a filtering TLS bio");
		return FALSE;
	}

	return TRUE;
}
Ejemplo n.º 11
0
BOOL transport_tsg_connect(rdpTransport* transport, const char* hostname, UINT16 port)
{
	rdpTsg* tsg = tsg_new(transport);

	tsg->transport = transport;
	transport->tsg = tsg;
	transport->SplitInputOutput = TRUE;

	if (!transport->TlsIn)
		transport->TlsIn = tls_new(transport->settings);

	transport->TlsIn->sockfd = transport->TcpIn->sockfd;
	transport->TlsIn->hostname = transport->settings->GatewayHostname;
	transport->TlsIn->port = transport->settings->GatewayPort;

	if (transport->TlsIn->port == 0)
		transport->TlsIn->port = 443;

	if (!transport->TlsOut)
		transport->TlsOut = tls_new(transport->settings);

	transport->TlsOut->sockfd = transport->TcpOut->sockfd;
	transport->TlsOut->hostname = transport->settings->GatewayHostname;
	transport->TlsOut->port = transport->settings->GatewayPort;

	if (transport->TlsOut->port == 0)
		transport->TlsOut->port = 443;

	if (!tls_connect(transport->TlsIn))
		return FALSE;

	if (!tls_connect(transport->TlsOut))
		return FALSE;

	if (!tsg_connect(tsg, hostname, port))
		return FALSE;

	return TRUE;
}
Ejemplo n.º 12
0
tbool transport_connect_tls(rdpTransport* transport)
{
    if (transport->tls == NULL)
        transport->tls = tls_new(transport->settings);

    transport->layer = TRANSPORT_LAYER_TLS;
    transport->tls->sockfd = transport->tcp->sockfd;

    if (tls_connect(transport->tls) == false)
        return false;

    return true;
}
Ejemplo n.º 13
0
boolean transport_connect_tls(rdpTransport* transport)
{
	if (transport->tls == NULL)
		transport->tls = tls_new();

	transport->layer = TRANSPORT_LAYER_TLS;
	transport->tls->sockfd = transport->tcp->sockfd;

	if (tls_connect(transport->tls) != True)
		return False;

	return True;
}
Ejemplo n.º 14
0
void
net_connect_cb(uv_connect_t *conn, int stat) {
  net_t * net = (net_t *) conn->data;
  err_t err;
  int read;

  if (stat < 0) {
    err = uv_last_error(net->loop);
    if (net->error_cb) {
      net->error_cb(net, err, (char *) uv_strerror(err));
    } else {
      printf("error(%s:%d) %s", net->hostname, net->port, (char *) uv_strerror(err));
      net_free(net);
    }
    return;
  }

  /*
   * change the `connected` state
   */
  net->connected = 1;

  /*
   * read buffers via uv
   */
  uv_read_start((uv_stream_t *) net->handle, net_alloc, net_read);

  /*
   * call `conn_cb`, the tcp connection has been 
   *  established in user-land.
   */
  if (net->use_ssl == NOT_SSL && net->conn_cb != NULL) {
    net->conn_cb(net);
  }

  /*
   * Handle TLS Partial
   */
  if (net->use_ssl == USE_SSL && tls_connect(net->tls) == NET_OK) {
    read = 0;
    do {
      read = tls_bio_read(net->tls, 0);
      if (read > 0) {
        uv_write_t req;
        uv_buf_t uvbuf = uv_buf_init(net->tls->buf, read);
        uv_write(&req, (uv_stream_t*)net->handle, &uvbuf, 1, NULL);
      }
    } while (read > 0);
  }
}
Ejemplo n.º 15
0
static bool estab_handler(int *err, bool active, void *arg)
{
	struct tls_conn *tc = arg;

	DEBUG_INFO("tcp established (active=%u)\n", active);

	if (!active)
		return true;

	tc->active = true;
	*err = tls_connect(tc);

	return true;
}
Ejemplo n.º 16
0
boolean transport_connect_nla(rdpTransport* transport)
{
	freerdp* instance;
	rdpSettings* settings;

	if (transport->tls == NULL)
		transport->tls = tls_new(transport->settings);

	transport->layer = TRANSPORT_LAYER_TLS;
	transport->tls->sockfd = transport->tcp->sockfd;

	if (tls_connect(transport->tls) != true)
	{
		if (!connectErrorCode)                    
			connectErrorCode = TLSCONNECTERROR;                      

		tls_free(transport->tls);
		transport->tls = NULL;
		return false;
	}

	/* Network Level Authentication */

	if (transport->settings->authentication != true)
		return true;

	settings = transport->settings;
	instance = (freerdp*) settings->instance;

	if (transport->credssp == NULL)
		transport->credssp = credssp_new(instance, transport->tls, settings);

	if (credssp_authenticate(transport->credssp) < 0)
	{
		if (!connectErrorCode)                    
			connectErrorCode = AUTHENTICATIONERROR;                      

		printf("Authentication failure, check credentials.\n"
			"If credentials are valid, the NTLMSSP implementation may be to blame.\n");

		credssp_free(transport->credssp);
		return false;
	}

	credssp_free(transport->credssp);

	return true;
}
Ejemplo n.º 17
0
VMINT vm_tls_connect(VMINT res_id, vm_sockaddr_ex_struct * faddr)
{
    kal_int32 ret;
    vm_tls_context_t * ctx_p = NULL;
    sockaddr_struct int_faddr = {0};

    MMI_TRACE(TRACE_GROUP_8, TRC_MRE_SSL_S, 3, __LINE__);
    if (NULL == faddr)
    {
        MMI_TRACE(TRACE_GROUP_8, TRC_MRE_SSL_E1, 3, __LINE__);
        return VM_TLS_RET_BASE -3;
    }

    ctx_p = vm_tls_get_ctx_by_res(res_id);
    if (NULL == ctx_p)
    {
        MMI_TRACE(TRACE_GROUP_8, TRC_MRE_SSL_E2, 3, __LINE__);
        return VM_TLS_RET_BASE -2;
    }

    int_faddr.addr_len = faddr->addr_len;
    memcpy(int_faddr.addr, faddr->addr, int_faddr.addr_len);
    int_faddr.port = faddr->port;
    int_faddr.sock_type = faddr->sock_type;
    
    MMI_TRACE(TRACE_GROUP_8, TRC_MRE_DLS_LOG, 
        int_faddr.addr_len,
        int_faddr.addr[0],
        int_faddr.addr[1],
        int_faddr.addr[2],
        int_faddr.addr[3],
        int_faddr.port,
        3, int_faddr.sock_type);
        
    ret = tls_connect(ctx_p->soc_id, &int_faddr);
    
    if (TLS_ERR_NONE != ret)
    {
        MMI_TRACE(TRACE_GROUP_8, TRC_MRE_SSL_E3, 3, ret);
        return ret;
    }
    
    MMI_TRACE(TRACE_GROUP_8, TRC_MRE_SSL_E, 3, __LINE__);
    return ret;
}
Ejemplo n.º 18
0
//*****************************************************************************
//
//! Main 
//!
//! \param  none
//!
//! \return None
//!
//*****************************************************************************
void main()
{
    long lRetVal = -1;
    //
    // Initialize board configuration
    //
    BoardInit();

    PinMuxConfig();
    //Initialize GPIO interrupt
    GPIOIntInit();
    //Initialize Systick interrupt
    SystickIntInit();
    //Initialize Uart interrupt
    UART1IntInit();
    //Initialize SPI
    SPIInit();
    //Initalize Adafruit
    Adafruit_Init();

    InitTerm();
    //Connect the CC3200 to the local access point
    lRetVal = connectToAccessPoint();
    //Set time so that encryption can be used
    lRetVal = set_time();
    if(lRetVal < 0)
    {
        UART_PRINT("Unable to set time in the device");
        LOOP_FOREVER();
    }
    //Connect to the website with TLS encryption
    lRetVal = tls_connect();
    if(lRetVal < 0)
    {
        ERR_PRINT(lRetVal);
    }

    //remote calls sendMessage() which calls http_post() which requires the return value from tls_connect()
    remote(lRetVal);

    //http_post(lRetVal);

    sl_Stop(SL_STOP_TIMEOUT);
    LOOP_FOREVER();
}
Ejemplo n.º 19
0
boolean transport_connect_tls(rdpTransport* transport)
{
	if (transport->tls == NULL)
		transport->tls = tls_new(transport->settings);

	transport->layer = TRANSPORT_LAYER_TLS;
	transport->tls->sockfd = transport->tcp->sockfd;

	if (tls_connect(transport->tls) != true)
	{
		if (!connectErrorCode)                    
			connectErrorCode = TLSCONNECTERROR;                      

		tls_free(transport->tls);
		transport->tls = NULL;
		return false;
	}

	return true;
}
Ejemplo n.º 20
0
/*
 * called before tls_read, the this function should attempt tls_accept or
 * tls_connect depending on the state of the connection, if this function
 * does not transit a connection into S_CONN_OK then tcp layer would not
 * call tcp_read
 */
int
tls_fix_read_conn(struct tcp_connection *c)
{
	/*
	* no lock acquired
	*/
	int             ret;

	ret = 0;

	/*
	* We have to acquire the lock before testing c->state, otherwise a
	* writer could modify the structure if it gets preempted and has
	* something to write
	*/
	lock_get(&c->write_lock);
    switch (c->state) {
		case S_CONN_ACCEPT:
			ret = tls_update_fd(c, c->fd);
			if (!ret)
				ret = tls_accept(c, NULL);
			break;

		case S_CONN_CONNECT:
			ret = tls_update_fd(c, c->fd);
			if (!ret)
				ret = tls_connect(c, NULL);
			break;

		default:	/* fall through */
			break;
	}
	lock_release(&c->write_lock);

	return ret;
}
Ejemplo n.º 21
0
/*
 * fixme: probably does not work correctly
 */
size_t tls_blocking_write(struct tcp_connection *c, int fd, const char *buf,
																	size_t len)
{
	#define MAX_SSL_RETRIES 32
	int             written, n;
	int             timeout, retries;
	struct pollfd   pf;
	pf.fd = fd;

	written = 0;
	retries = 0;

	if (c->state!=S_CONN_OK) {
		LM_ERR("TLS broken connection\n");
		goto error;
	}

	lock_get(&c->write_lock);

	if (tls_update_fd(c, fd) < 0)
		goto error;

	timeout = tls_send_timeout;
again:
	n = 0;
	pf.events = 0;

	if ( c->proto_flags & F_TLS_DO_ACCEPT ) {
		if (tls_accept(c, &(pf.events)) < 0)
			goto error;
		timeout = tls_handshake_timeout;
	} else if ( c->proto_flags & F_TLS_DO_CONNECT ) {
		if (tls_connect(c, &(pf.events)) < 0)
			goto error;
		timeout = tls_handshake_timeout;
	} else {
		n = tls_write(c, fd, buf, len, &(pf.events));
		timeout = tls_send_timeout;
	}

	if (n < 0) {
		LM_ERR("TLS failed to send data\n");
		goto error;
	}

	/* nothing happens */
	if (n==0) {
		retries++;
		/* avoid looping */
		if (retries==MAX_SSL_RETRIES) {
			LM_ERR("too many retries with no operation\n");
			goto error;
		}
	} else {
		/* reset the retries if we succeded in doing something*/
		retries = 0;
	}

	written += n;
	if (n < len) {
		/*
		* partial write
		*/
		buf += n;
		len -= n;
	} else {
		/*
		* successful full write
		*/
		lock_release(&c->write_lock);
		return written;
	}

	if (pf.events == 0)
		pf.events = POLLOUT;

poll_loop:
	while (1) {
		n = poll(&pf, 1, timeout);
		if (n < 0) {
			if (errno == EINTR)
				continue;	/* signal, ignore */
			else if (errno != EAGAIN && errno != EWOULDBLOCK) {
				LM_ERR("TLS poll failed: %s [%d]\n",strerror(errno), errno);
				goto error;
			} else
				goto poll_loop;
		} else if (n == 0) {
			/*
			* timeout
			*/
			LM_ERR("TLS send timeout (%d)\n", timeout);
			goto error;
		}
		if (pf.revents & POLLOUT || pf.revents & POLLIN) {
			/*
			* we can read or write again
			*/
			goto again;
		} else if (pf.revents & (POLLERR | POLLHUP | POLLNVAL)) {
			LM_ERR("TLS bad poll flags %x\n",pf.revents);
			goto error;
		}
		/*
		* if POLLPRI or other non-harmful events happened, continue (
		* although poll should never signal them since we're not
		* interested in them => we should never reach this point)
		*/
	}

error:
	lock_release(&c->write_lock);
	return -1;
}
Ejemplo n.º 22
0
BOOL transport_tsg_connect(rdpTransport* transport, const char* hostname, UINT16 port)
{
	rdpTsg* tsg;
	int tls_status;
	freerdp* instance;
	rdpContext* context;
	rdpSettings *settings = transport->settings;

	instance = (freerdp*) transport->settings->instance;
	context = instance->context;

	tsg = tsg_new(transport);

	if (!tsg)
		return FALSE;

	tsg->transport = transport;
	transport->tsg = tsg;
	transport->SplitInputOutput = TRUE;

	if (!transport->TlsIn)
	{
		transport->TlsIn = tls_new(settings);

		if (!transport->TlsIn)
			return FALSE;
	}

	if (!transport->TlsOut)
	{
		transport->TlsOut = tls_new(settings);

		if (!transport->TlsOut)
			return FALSE;
	}

	/* put a decent default value for gateway port */
	if (!settings->GatewayPort)
		settings->GatewayPort = 443;

	transport->TlsIn->hostname = transport->TlsOut->hostname = settings->GatewayHostname;
	transport->TlsIn->port = transport->TlsOut->port = settings->GatewayPort;

	transport->TlsIn->isGatewayTransport = TRUE;

	tls_status = tls_connect(transport->TlsIn, transport->TcpIn->bufferedBio);

	if (tls_status < 1)
	{
		if (tls_status < 0)
		{
			if (!freerdp_get_last_error(context))
				freerdp_set_last_error(context, FREERDP_ERROR_TLS_CONNECT_FAILED);
		}
		else
		{
			if (!freerdp_get_last_error(context))
				freerdp_set_last_error(context, FREERDP_ERROR_CONNECT_CANCELLED);
		}

		return FALSE;
	}

	transport->TlsOut->isGatewayTransport = TRUE;

	tls_status = tls_connect(transport->TlsOut, transport->TcpOut->bufferedBio);

	if (tls_status < 1)
	{
		if (tls_status < 0)
		{
			if (!freerdp_get_last_error(context))
				freerdp_set_last_error(context, FREERDP_ERROR_TLS_CONNECT_FAILED);
		}
		else
		{
			if (!freerdp_get_last_error(context))
				freerdp_set_last_error(context, FREERDP_ERROR_CONNECT_CANCELLED);
		}

		return FALSE;
	}

	if (!tsg_connect(tsg, hostname, port))
		return FALSE;

	transport->frontBio = BIO_new(BIO_s_tsg());
	transport->frontBio->ptr = tsg;

	return TRUE;
}
Ejemplo n.º 23
0
BOOL transport_connect_tls(rdpTransport* transport)
{
	rdpSettings *settings = transport->settings;
	rdpTls *targetTls;
	BIO *targetBio;
	int tls_status;
	freerdp* instance;
	rdpContext* context;

	instance = (freerdp*) transport->settings->instance;
	context = instance->context;

	if (transport->layer == TRANSPORT_LAYER_TSG)
	{
		transport->TsgTls = tls_new(transport->settings);
		transport->layer = TRANSPORT_LAYER_TSG_TLS;

		targetTls = transport->TsgTls;
		targetBio = transport->frontBio;
	}
	else
	{
		if (!transport->TlsIn)
			transport->TlsIn = tls_new(settings);

		if (!transport->TlsOut)
			transport->TlsOut = transport->TlsIn;

		targetTls = transport->TlsIn;
		targetBio = transport->TcpIn->bufferedBio;

		transport->layer = TRANSPORT_LAYER_TLS;
	}


	targetTls->hostname = settings->ServerHostname;
	targetTls->port = settings->ServerPort;

	if (targetTls->port == 0)
		targetTls->port = 3389;

	targetTls->isGatewayTransport = FALSE;

	tls_status = tls_connect(targetTls, targetBio);

	if (tls_status < 1)
	{
		if (tls_status < 0)
		{
			if (!connectErrorCode)
				connectErrorCode = TLSCONNECTERROR;

			if (!freerdp_get_last_error(context))
				freerdp_set_last_error(context, FREERDP_ERROR_TLS_CONNECT_FAILED);
		}
		else
		{
			if (!freerdp_get_last_error(context))
				freerdp_set_last_error(context, FREERDP_ERROR_CONNECT_CANCELLED);
		}

		return FALSE;
	}

	transport->frontBio = targetTls->bio;
	if (!transport->frontBio)
	{
		fprintf(stderr, "%s: unable to prepend a filtering TLS bio", __FUNCTION__);
		return FALSE;
	}

	return TRUE;
}
Ejemplo n.º 24
0
int main(int argc, char *argv[])
{
	struct tls_config *conf;
	struct tls *ctx, *ocsp;
	struct tls_cert *cert;
	int res;
	const char *host;
	char buf[256];

	if (argc < 2)
		errx(1, "give host as arg\n");
	host = argv[1];

#ifdef USUAL_LIBSSL_FOR_TLS
	printf("libssl: %s\n", SSLeay_version(SSLEAY_VERSION));
#endif
	res = tls_init();
	if (res < 0)
		errx(1, "tls_init");

	conf = tls_config_new();
	if (!conf)
		errx(1, "tls_config_new");

	tls_config_set_protocols(conf, TLS_PROTOCOLS_ALL);
	tls_config_set_ciphers(conf, "fast");

	ctx = tls_client();
	if (!ctx)
		errx(1, "tls_client");

	res = tls_configure(ctx, conf);
	if (res < 0)
		errx(1, "tls_configure: %s", tls_error(ctx));

	res = tls_connect(ctx, host, "443");
	if (res < 0)
		errx(1, "tls_connect: %s", tls_error(ctx));

	res = tls_handshake(ctx);
	if (res < 0)
		errx(1, "tls_handshake: %s", tls_error(ctx));

	res = tls_get_peer_cert(ctx, &cert, NULL);
	if (res < 0)
		errx(1, "tls_get_peer_cert: %s", tls_error(ctx));

	tls_get_connection_info(ctx, buf, sizeof buf);

	printf("Connection: '%s'\n", buf);
	printf("  CN='%s'\n", cert->subject.common_name);
	printf("  C='%s'\n", cert->subject.country_name);
	printf("  ST='%s'\n", cert->subject.state_or_province_name);
	printf("  L='%s'\n", cert->subject.locality_name);
	printf("  S='%s'\n", cert->subject.street_address);
	printf("  O='%s'\n", cert->subject.organization_name);
	printf("  OU='%s'\n", cert->subject.organizational_unit_name);

	show_ocsp_info("OCSP stapling", ctx);

	ocsp = NULL;
	res = tls_ocsp_check_peer(&ocsp, NULL, ctx);
	if (ocsp) {
		show_ocsp_info("OCSP responder", ocsp);
		tls_free(ocsp);
	} else if (res == TLS_NO_OCSP) {
		printf("OCSP responder: No OCSP support in libtls\n");
	}

	if (0) test_context(ctx);

	tls_close(ctx);
	tls_free(ctx);
	tls_config_free(conf);
	tls_cert_free(cert);

	return 0;
}
Ejemplo n.º 25
0
/** tls read.
 * Each modification of ssl data structures has to be protected, another process * might ask for the same connection and attempt write to it which would
 * result in updating the ssl structures.
 * WARNING: must be called whic c->write_lock _unlocked_.
 * @param c - tcp connection pointer. The following flags might be set:
 * @param flags - value/result:
 *                     input: RD_CONN_FORCE_EOF  - force EOF after the first
 *                            successful read (bytes_read >=0 )
 *                     output: RD_CONN_SHORT_READ if the read exhausted
 *                              all the bytes in the socket read buffer.
 *                             RD_CONN_EOF if EOF detected (0 bytes read)
 *                              or forced via RD_CONN_FORCE_EOF.
 *                             RD_CONN_REPEAT_READ  if this function should
 *                              be called again (e.g. has some data
 *                              buffered internally that didn't fit in
 *                              tcp_req).
 *                     Note: RD_CONN_SHORT_READ & RD_CONN_EOF should be cleared
 *                           before calling this function when there is new
 *                           data (e.g. POLLIN), but not if the called is
 *                           retried because of RD_CONN_REPEAT_READ and there
 *                           is no information about the socket having more
 *                           read data available.
 * @return bytes decrypted on success, -1 on error (it also sets some
 *         tcp connection flags and might set c->state and r->error on
 *         EOF or error).
 */
int tls_read_f(struct tcp_connection* c, int* flags)
{
	struct tcp_req* r;
	int bytes_free, bytes_read, read_size, ssl_error, ssl_read;
	SSL* ssl;
	unsigned char rd_buf[TLS_RD_MBUF_SZ];
	unsigned char wr_buf[TLS_WR_MBUF_SZ];
	struct tls_mbuf rd, wr;
	struct tls_extra_data* tls_c;
	struct tls_rd_buf* enc_rd_buf;
	int n, flush_flags;
	char* err_src;
	int x;
	int tls_dbg;
	
	TLS_RD_TRACE("(%p, %p (%d)) start (%s -> %s:%d*)\n",
					c, flags, *flags,
					su2a(&c->rcv.src_su, sizeof(c->rcv.src_su)),
					ip_addr2a(&c->rcv.dst_ip), c->rcv.dst_port);
	ssl_read = 0;
	r = &c->req;
	enc_rd_buf = 0;
	*flags &= ~RD_CONN_REPEAT_READ;
	if (unlikely(tls_fix_connection(c) < 0)) {
		TLS_RD_TRACE("(%p, %p) end: tls_fix_connection failed =>"
						" immediate error exit\n", c, flags);
		return -1;
	}
	/* here it's safe to use c->extra_data in read-only mode.
	   If it's != 0 is changed only on destroy. It's not possible to have
	   parallel reads.*/
	tls_c = c->extra_data;
	bytes_free = c->req.b_size - (int)(r->pos - r->buf);
	if (unlikely(bytes_free == 0)) {
		ERR("Buffer overrun, dropping\n");
		r->error = TCP_REQ_OVERRUN;
		return -1;
	}
redo_read:
	/* if data queued from a previous read(), use it (don't perform
	 * a real read()).
	*/
	if (unlikely(tls_c->enc_rd_buf)) {
		/* use queued data */
		/* safe to use without locks, because only read changes it and
		   there can't be parallel reads on the same connection */
		enc_rd_buf = tls_c->enc_rd_buf;
		tls_c->enc_rd_buf = 0;
		TLS_RD_TRACE("(%p, %p) using queued data (%p: %p %d bytes)\n", c,
					flags, enc_rd_buf, enc_rd_buf->buf + enc_rd_buf->pos,
					enc_rd_buf->size - enc_rd_buf->pos);
		tls_mbuf_init(&rd, enc_rd_buf->buf + enc_rd_buf->pos,
						enc_rd_buf->size - enc_rd_buf->pos);
		rd.used = enc_rd_buf->size - enc_rd_buf->pos;
	} else {
		/* if we were using using queued data before, free & reset the
			the queued read data before performing the real read() */
		if (unlikely(enc_rd_buf)) {
			TLS_RD_TRACE("(%p, %p) reset prev. used enc_rd_buf (%p)\n", c,
							flags, enc_rd_buf);
			shm_free(enc_rd_buf);
			enc_rd_buf = 0;
		}
		/* real read() */
		tls_mbuf_init(&rd, rd_buf, sizeof(rd_buf));
		/* read() only if no previously detected EOF, or previous
		   short read (which means the socket buffer was emptied) */
		if (likely(!(*flags & (RD_CONN_EOF|RD_CONN_SHORT_READ)))) {
			/* don't read more then the free bytes in the tcp req buffer */
			read_size = MIN_unsigned(rd.size, bytes_free);
			bytes_read = tcp_read_data(c->fd, c, (char*)rd.buf, read_size,
										flags);
			TLS_RD_TRACE("(%p, %p) tcp_read_data(..., %d, *%d) => %d bytes\n",
						c, flags, read_size, *flags, bytes_read);
			/* try SSL_read even on 0 bytes read, it might have
			   internally buffered data */
			if (unlikely(bytes_read < 0)) {
					goto error;
			}
			rd.used = bytes_read;
		}
	}
	
continue_ssl_read:
	tls_mbuf_init(&wr, wr_buf, sizeof(wr_buf));
	ssl_error = SSL_ERROR_NONE;
	err_src = "TLS read:";
	/* we have to avoid to run in the same time 
	 * with a tls_write because of the
	 * update bio stuff  (we don't want a write
	 * stealing the wbio or rbio under us or vice versa)
	 * => lock on con->write_lock (ugly hack) */
	lock_get(&c->write_lock);
		tls_set_mbufs(c, &rd, &wr);
		ssl = tls_c->ssl;
		n = 0;
		if (unlikely(tls_write_wants_read(tls_c) &&
						!(*flags & RD_CONN_EOF))) {
			n = tls_ct_wq_flush(c, &tls_c->ct_wq, &flush_flags,
								&ssl_error);
			TLS_RD_TRACE("(%p, %p) tls write on read (WRITE_WANTS_READ):"
							" ct_wq_flush()=> %d (ff=%d ssl_error=%d))\n",
							c, flags, n, flush_flags, ssl_error);
			if (unlikely(n < 0 )) {
				tls_set_mbufs(c, 0, 0);
				lock_release(&c->write_lock);
				ERR("write flush error (%d)\n", n);
				goto error;
			}
			if (likely(flush_flags & F_BUFQ_EMPTY))
				tls_c->flags &= ~F_TLS_CON_WR_WANTS_RD;
			if (unlikely(flush_flags & F_BUFQ_ERROR_FLUSH))
				err_src = "TLS write:";
		}
		if (likely(ssl_error == SSL_ERROR_NONE)) {
			if (unlikely(tls_c->state == S_TLS_CONNECTING)) {
				n = tls_connect(c, &ssl_error);
				TLS_RD_TRACE("(%p, %p) tls_connect() => %d (err=%d)\n",
								c, flags, n, ssl_error);
				if (unlikely(n>=1)) {
					n = SSL_read(ssl, r->pos, bytes_free);
				} else {
					/* tls_connect failed/needs more IO */
					if (unlikely(n < 0 && ssl_error == SSL_ERROR_NONE)) {
						lock_release(&c->write_lock);
						goto error;
					}
					err_src = "TLS connect:";
					goto ssl_read_skipped;
				}
			} else if (unlikely(tls_c->state == S_TLS_ACCEPTING)) {
				n = tls_accept(c, &ssl_error);
				TLS_RD_TRACE("(%p, %p) tls_accept() => %d (err=%d)\n",
								c, flags, n, ssl_error);
				if (unlikely(n>=1)) {
					n = SSL_read(ssl, r->pos, bytes_free);
				} else {
					/* tls_accept failed/needs more IO */
					if (unlikely(n < 0 && ssl_error == SSL_ERROR_NONE)) {
						lock_release(&c->write_lock);
						goto error;
					}
					err_src = "TLS accept:";
					goto ssl_read_skipped;
				}
			} else {
				/* if bytes in then decrypt read buffer into tcpconn req.
				   buffer */
				n = SSL_read(ssl, r->pos, bytes_free);
			}
			/** handle SSL_read() return.
			 *  There are 3 main cases, each with several sub-cases, depending
			 *  on whether or not the output buffer was filled, if there
			 *  is still unconsumed input data in the input buffer (rd)
			 *  and if there is "cached" data in the internal openssl
			 *  buffers.
			 *  0. error (n<=0):
			 *     SSL_ERROR_WANT_READ - input data fully
			 *       consumed, no more returnable cached data inside openssl
			 *       => exit.
			 *     SSL_ERROR_WANT_WRITE - should never happen (the write
			 *       buffer is big enough to handle any re-negociation).
			 *     SSL_ERROR_ZERO_RETURN - ssl level shutdown => exit.
			 *    other errors are unexpected.
			 * 1. output buffer filled (n == bytes_free):
			 *    1i.  - still unconsumed input, nothing buffered by openssl
			 *    1ip. - unconsumed input + buffered data by openssl (pending
			             on the next SSL_read).
			 *    1p.  - completely consumed input, buffered data internally
			 *            by openssl (pending).
			 *           Likely to happen, about the only case when
			 *           SSL_pending() could be used (but only if readahead=0).
			 *    1f.  - consumed input, no buffered data.
			 * 2. output buffer not fully filled (n < bytes_free):
			 *     2i. - still unconsumed input, nothing buffered by openssl.
			 *           This can appear if SSL readahead is 0 (SSL_read()
			 *           tries to get only 1 record from the input).
			 *     2ip. - unconsumed input and buffered data by openssl.
			 *            Unlikely to happen (e.g. readahead is 1, more
			 *            records are buffered internally by openssl, but
			 *            there was not enough space for buffering the whole
			 *            input).
			 *     2p  - consumed input, but buffered data by openssl.
			 *            It happens especially when readahead is 1.
			 *     2f.  - consumed input, no buffered data.
			 *
			 * One should repeat SSL_read() until and error is detected
			 *  (0*) or the input and internal ssl buffers are fully consumed
			 *  (1f or 2f). However in general is not possible to see if
			 *  SSL_read() could return more data. SSL_pending() has very
			 *  limited usability (basically it would return !=0 only if there
			 *  was no enough space in the output buffer and only if this did
			 *  not happen at a record boundary).
			 * The solution is to repeat SSL_read() until error or until
			 *  the output buffer is filled (0* or 1*).
			 *  In the later case, this whole function should be called again
			 *  once there is more output space (set RD_CONN_REPEAT_READ).
			 */

			if (unlikely(tls_c->flags & F_TLS_CON_RENEGOTIATION)) {
				/* Fix CVE-2009-3555 - disable renegotiation if started by client
				 * - simulate SSL EOF to force close connection*/
				tls_dbg = cfg_get(tls, tls_cfg, debug);
				LOG(tls_dbg, "Reading on a renegotiation of connection (n:%d) (%d)\n",
						n, SSL_get_error(ssl, n));
				err_src = "TLS R-N read:";
				ssl_error = SSL_ERROR_ZERO_RETURN;
			} else {
				if (unlikely(n <= 0)) {
					ssl_error = SSL_get_error(ssl, n);
					err_src = "TLS read:";
					/*  errors handled below, outside the lock */
				} else {
					ssl_error = SSL_ERROR_NONE;
					r->pos += n;
					ssl_read += n;
					bytes_free -=n;
				}
			}
			TLS_RD_TRACE("(%p, %p) SSL_read() => %d (err=%d) ssl_read=%d"
							" *flags=%d tls_c->flags=%d\n",
							c, flags, n, ssl_error, ssl_read, *flags,
							tls_c->flags);
ssl_read_skipped:
			;
		}
		if (unlikely(wr.used != 0 && ssl_error != SSL_ERROR_ZERO_RETURN)) {
			TLS_RD_TRACE("(%p, %p) tcpconn_send_unsafe %d bytes\n",
							c, flags, wr.used);
			/* something was written and it's not ssl EOF*/
			if (unlikely(tcpconn_send_unsafe(c->fd, c, (char*)wr.buf,
											wr.used, c->send_flags) < 0)) {
				tls_set_mbufs(c, 0, 0);
				lock_release(&c->write_lock);
				TLS_RD_TRACE("(%p, %p) tcpconn_send_unsafe error\n", c, flags);
				goto error_send;
			}
		}
	/* quickly catch bugs: segfault if accessed and not set */
	tls_set_mbufs(c, 0, 0);
	lock_release(&c->write_lock);
	switch(ssl_error) {
		case SSL_ERROR_NONE:
			if (unlikely(n < 0)) {
				BUG("unexpected SSL_ERROR_NONE for n=%d\n", n);
				goto error;
			}
			break;
		case SSL_ERROR_ZERO_RETURN:
			/* SSL EOF */
			TLS_RD_TRACE("(%p, %p) SSL EOF (fd=%d)\n", c, flags, c->fd);
			goto ssl_eof;
		case SSL_ERROR_WANT_READ:
			TLS_RD_TRACE("(%p, %p) SSL_ERROR_WANT_READ *flags=%d\n",
							c, flags, *flags);
			/* needs to read more data */
			if (unlikely(rd.pos != rd.used)) {
				/* data still in the read buffer */
				BUG("SSL_ERROR_WANT_READ but data still in"
						" the rbio (%p, %d bytes at %d)\n", rd.buf,
						rd.used - rd.pos, rd.pos);
				goto bug;
			}
			if (unlikely((*flags & (RD_CONN_EOF | RD_CONN_SHORT_READ)) == 0) &&
							bytes_free){
				/* there might still be data to read and there is space
				   to decrypt it in tcp_req (no byte has been written into
				    tcp_req in this case) */
				TLS_RD_TRACE("(%p, %p) redo read *flags=%d bytes_free=%d\n",
								c, flags, *flags, bytes_free);
				goto redo_read;
			}
			goto end; /* no more data to read */
		case SSL_ERROR_WANT_WRITE:
			if (wr.used) {
				/* something was written => buffer not big enough to hold
				   everything => reset buffer & retry (the tcp_write already
				   happened if we are here) */
				TLS_RD_TRACE("(%p) SSL_ERROR_WANT_WRITE partial write"
							" (written  %d), retrying\n", c, wr.used);
				goto continue_ssl_read;
			}
			/* else write buffer too small, nothing written */
			BUG("write buffer too small (%d/%d bytes)\n",
						wr.used, wr.size);
			goto bug;
		case SSL_ERROR_SSL:
			/* protocol level error */
			TLS_ERR(err_src);
			goto error;
#if OPENSSL_VERSION_NUMBER >= 0x00907000L /*0.9.7*/
		case SSL_ERROR_WANT_CONNECT:
			/* only if the underlying BIO is not yet connected
			   and the call would block in connect().
			   (not possible in our case) */
			BUG("unexpected SSL_ERROR_WANT_CONNECT\n");
			goto bug;
		case SSL_ERROR_WANT_ACCEPT:
			/* only if the underlying BIO is not yet connected
			   and call would block in accept()
			   (not possible in our case) */
			BUG("unexpected SSL_ERROR_WANT_ACCEPT\n");
			goto bug;
#endif
		case SSL_ERROR_WANT_X509_LOOKUP:
			/* can only appear on client application and it indicates that
			   an installed client cert. callback should be called again
			   (it returned < 0 indicated that it wants to be called
			   later). Not possible in our case */
			BUG("unsupported SSL_ERROR_WANT_X509_LOOKUP");
			goto bug;
		case SSL_ERROR_SYSCALL:
			TLS_ERR_RET(x, err_src);
			if (!x) {
				if (n == 0) {
					WARN("Unexpected EOF\n");
				} else
					/* should never happen */
					BUG("IO error (%d) %s\n", errno, strerror(errno));
			}
			goto error;
		default:
			TLS_ERR(err_src);
			BUG("unexpected SSL error %d\n", ssl_error);
			goto bug;
	}
	if (unlikely(n < 0)) {
		/* here n should always be >= 0 */
		BUG("unexpected value (n = %d)\n", n);
		goto bug;
	}
	if (unlikely(rd.pos != rd.used)) {
		/* encrypted data still in the read buffer (SSL_read() did not
		   consume all of it) */
		if (unlikely(n < 0))
			/* here n should always be >= 0 */
			BUG("unexpected value (n = %d)\n", n);
		else {
			if (unlikely(bytes_free != 0)) {
				/* 2i or 2ip: unconsumed input and output buffer not filled =>
				  retry ssl read (SSL_read() will read will stop at
				  record boundaries, unless readahead==1).
				  No tcp_read() is attempted, since that would reset the
				  current no-yet-consumed input data.
				 */
				TLS_RD_TRACE("(%p, %p) input not fully consumed =>"
								" retry SSL_read"
								" (pos: %d, remaining %d, output free %d)\n",
								c, flags, rd.pos, rd.used-rd.pos, bytes_free);
				goto continue_ssl_read;
			}
			/* 1i or 1ip: bytes_free == 0
			   (unconsumed input, but filled output  buffer) =>
			    queue read data, and exit asking for repeating the call
			    once there is some space in the output buffer.
			 */
			if (likely(!enc_rd_buf)) {
				TLS_RD_TRACE("(%p, %p) creating enc_rd_buf (for %d bytes)\n",
								c, flags, rd.used - rd.pos);
				enc_rd_buf = shm_malloc(sizeof(*enc_rd_buf) -
										sizeof(enc_rd_buf->buf) +
										rd.used - rd.pos);
				if (unlikely(enc_rd_buf == 0)) {
					ERR("memory allocation error (%d bytes requested)\n",
						(int)(sizeof(*enc_rd_buf) + sizeof(enc_rd_buf->buf) +
										rd.used - rd.pos));
					goto error;
				}
				enc_rd_buf->pos = 0;
				enc_rd_buf->size = rd.used - rd.pos;
				memcpy(enc_rd_buf->buf, rd.buf + rd.pos,
										enc_rd_buf->size);
			} else if ((enc_rd_buf->buf + enc_rd_buf->pos) == rd.buf) {
				TLS_RD_TRACE("(%p, %p) enc_rd_buf already in use,"
								" updating pos %d\n",
								c, flags, enc_rd_buf->pos);
				enc_rd_buf->pos += rd.pos;
			} else {
				BUG("enc_rd_buf->buf = %p, pos = %d, rd_buf.buf = %p\n",
						enc_rd_buf->buf, enc_rd_buf->pos, rd.buf);
				goto bug;
			}
			if (unlikely(tls_c->enc_rd_buf))
				BUG("tls_c->enc_rd_buf!=0 (%p)\n", tls_c->enc_rd_buf);
			/* there can't be 2 reads in parallel, so no locking is needed
			   here */
			tls_c->enc_rd_buf = enc_rd_buf;
			enc_rd_buf = 0;
			*flags |= RD_CONN_REPEAT_READ;
		}
	} else if (bytes_free != 0) {
		/*  2f or 2p: input fully consumed (rd.pos == rd.used),
		    output buffer not filled, still possible to have pending
		    data buffered by openssl */
		if (unlikely((*flags & (RD_CONN_EOF|RD_CONN_SHORT_READ)) == 0)) {
			/* still space in the tcp unenc. req. buffer, no SSL_read error,
			   not a short read and not an EOF (possible more data in
			   the socket buffer) => try a new tcp read too */
			TLS_RD_TRACE("(%p, %p) retry read (still space and no short"
							" tcp read: %d)\n", c, flags, *flags);
			goto redo_read;
		} else {
			/* don't tcp_read() anymore, but there might still be data
			   buffered internally by openssl (e.g. if readahead==1) =>
			   retry SSL_read() with the current full input buffer
			   (if no more internally SSL buffered data => WANT_READ => exit).
			 */
			TLS_RD_TRACE("(%p, %p) retry SSL_read only (*flags =%d)\n",
							c, flags, *flags);
			goto continue_ssl_read;
		}
	} else {
		/*   1p or 1f: rd.pos == rd.used && bytes_free == 0
			 (input fully consumed && output buffer filled) */
		/* ask for a repeat when there is more buffer space
		   (there is no definitive way to know if ssl doesn't still have
		    some internal buffered data until we get WANT_READ, see
			SSL_read() comment above) */
		*flags |= RD_CONN_REPEAT_READ;
		TLS_RD_TRACE("(%p, %p) output filled, exit asking to be called again"
						" (*flags =%d)\n", c, flags, *flags);
	}
	
end:
	if (enc_rd_buf)
		shm_free(enc_rd_buf);
	TLS_RD_TRACE("(%p, %p) end => %d (*flags=%d)\n",
					c, flags, ssl_read, *flags);
	return ssl_read;
ssl_eof:
	/* behave as an EOF would have been received at the tcp level */
	if (enc_rd_buf)
		shm_free(enc_rd_buf);
	c->state = S_CONN_EOF;
	*flags |= RD_CONN_EOF;
	TLS_RD_TRACE("(%p, %p) end EOF => %d (*flags=%d)\n",
					c, flags, ssl_read, *flags);
	return ssl_read;
error_send:
error:
bug:
	if (enc_rd_buf)
		shm_free(enc_rd_buf);
	r->error=TCP_READ_ERROR;
	TLS_RD_TRACE("(%p, %p) end error => %d (*flags=%d)\n",
					c, flags, ssl_read, *flags);
	return -1;
}
Ejemplo n.º 26
0
/** tls encrypt before sending function.
 * It is a callback that will be called by the tcp code, before a send
 * on TLS would be attempted. It should replace the input buffer with a
 * new static buffer containing the TLS processed data.
 * If the input buffer could not be fully encoded (e.g. run out of space
 * in the internal static buffer), it should set rest_buf and rest_len to
 * the remaining part, so that it could be called again once the output has
 * been used (sent). The send_flags used are also passed and they can be
 * changed (e.g. to disallow a close() after a partial encode).
 * WARNING: it must always be called with c->write_lock held!
 * @param c - tcp connection
 * @param pbuf - pointer to buffer (value/result, on success it will be
 *               replaced with a static buffer).
 * @param plen - pointer to buffer size (value/result, on success it will be
 *               replaced with the size of the replacement buffer.
 * @param rest_buf - (result) should be filled with a pointer to the
 *                remaining unencoded part of the original buffer if any,
 *                0 otherwise.
 * @param rest_len - (result) should be filled with the length of the
 *                 remaining unencoded part of the original buffer (0 if
 *                 the original buffer was fully encoded).
 * @param send_flags - pointer to the send_flags that will be used for sending
 *                     the message.
 * @return *plen on success (>=0), < 0 on error.
 */
int tls_encode_f(struct tcp_connection *c,
						const char** pbuf, unsigned int* plen,
						const char** rest_buf, unsigned int* rest_len,
						snd_flags_t* send_flags)
{
	int n, offs;
	SSL* ssl;
	struct tls_extra_data* tls_c;
	static unsigned char wr_buf[TLS_WR_MBUF_SZ];
	struct tls_mbuf rd, wr;
	int ssl_error;
	char* err_src;
	const char* buf;
	unsigned int len;
	int x;
	
	buf = *pbuf;
	len = *plen;
	*rest_buf = 0;
	*rest_len = 0;
	TLS_WR_TRACE("(%p, %p, %d, ... 0x%0x) start (%s:%d* -> %s)\n",
					c, buf, len, send_flags->f,
					ip_addr2a(&c->rcv.dst_ip), c->rcv.dst_port,
					su2a(&c->rcv.src_su, sizeof(c->rcv.src_su)));
	n = 0;
	offs = 0;
	ssl_error = SSL_ERROR_NONE;
	err_src = "TLS write:";
	if (unlikely(tls_fix_connection_unsafe(c) < 0)) {
		/* c->extra_data might be null => exit immediately */
		TLS_WR_TRACE("(%p) end: tls_fix_connection_unsafe failed =>"
						" immediate error exit\n", c);
		return -1;
	}
	tls_c = (struct tls_extra_data*)c->extra_data;
	ssl = tls_c->ssl;
	tls_mbuf_init(&rd, 0, 0); /* no read */
	tls_mbuf_init(&wr, wr_buf, sizeof(wr_buf));
	/* clear text already queued (WANTS_READ) queue directly*/
	if (unlikely(tls_write_wants_read(tls_c))) {
		TLS_WR_TRACE("(%p) WANTS_READ queue present => queueing"
						" (%d bytes,  %p + %d)\n", c, len - offs, buf, offs);
		if (unlikely(tls_ct_wq_add(&tls_c->ct_wq, buf+offs, len -offs) < 0)) {
				ERR("ct write buffer full for %p (%d bytes)\n",
						c, tls_c->ct_wq?tls_c->ct_wq->queued:0);
				goto error_wq_full;
		}
		/* buffer queued for a future send attempt, after first reading
		   some data (key exchange) => don't allow immediate closing of
		   the connection */
		send_flags->f &= ~SND_F_CON_CLOSE;
		goto end;
	}
	if (unlikely(tls_set_mbufs(c, &rd, &wr) < 0)) {
		ERR("tls_set_mbufs failed\n");
		goto error;
	}
redo_wr:
	if (unlikely(tls_c->state == S_TLS_CONNECTING)) {
		n = tls_connect(c, &ssl_error);
		TLS_WR_TRACE("(%p) tls_connect() => %d (err=%d)\n", c, n, ssl_error);
		if (unlikely(n>=1)) {
			n = SSL_write(ssl, buf + offs, len - offs);
			if (unlikely(n <= 0))
				ssl_error = SSL_get_error(ssl, n);
		} else {
			/* tls_connect failed/needs more IO */
			if (unlikely(n < 0 && ssl_error == SSL_ERROR_NONE))
				goto error;
			err_src = "TLS connect:";
		}
	} else if (unlikely(tls_c->state == S_TLS_ACCEPTING)) {
		n = tls_accept(c, &ssl_error);
		TLS_WR_TRACE("(%p) tls_accept() => %d (err=%d)\n", c, n, ssl_error);
		if (unlikely(n>=1)) {
			n = SSL_write(ssl, buf + offs, len - offs);
			if (unlikely(n <= 0))
				ssl_error = SSL_get_error(ssl, n);
		} else {
			/* tls_accept failed/needs more IO */
			if (unlikely(n < 0 && ssl_error == SSL_ERROR_NONE))
				goto error;
			err_src = "TLS accept:";
		}
	} else {
		n = SSL_write(ssl, buf + offs, len - offs);
		if (unlikely(n <= 0))
			ssl_error = SSL_get_error(ssl, n);
	}
	TLS_WR_TRACE("(%p) SSL_write(%p + %d, %d) => %d (err=%d)\n",
					c, buf, offs, len - offs, n, ssl_error);
	/* check for possible ssl errors */
	if (unlikely(n <= 0)){
		switch(ssl_error) {
			case SSL_ERROR_NONE:
				BUG("unexpected SSL_ERROR_NONE for n=%d\n", n);
				goto error;
				break;
			case SSL_ERROR_ZERO_RETURN:
				/* SSL EOF */
				ERR("ssl level EOF\n");
				goto ssl_eof;
			case SSL_ERROR_WANT_READ:
				/* queue write buffer */
				TLS_WR_TRACE("(%p) SSL_ERROR_WANT_READ => queueing for read"
								" (%p + %d, %d)\n", c, buf, offs, len -offs);
				if (unlikely(tls_ct_wq_add(&tls_c->ct_wq, buf+offs, len -offs)
								< 0)) {
					ERR("ct write buffer full (%d bytes)\n",
							tls_c->ct_wq?tls_c->ct_wq->queued:0);
					goto error_wq_full;
				}
				tls_c->flags |= F_TLS_CON_WR_WANTS_RD;
				/* buffer queued for a future send attempt, after first
				   reading some data (key exchange) => don't allow immediate
				   closing of the connection */
				send_flags->f &= ~SND_F_CON_CLOSE;
				break; /* or goto end */
			case SSL_ERROR_WANT_WRITE:
				if (unlikely(offs == 0)) {
					/*  error, no record fits in the buffer or
					  no partial write enabled and buffer to small to fit
					  all the records */
					BUG("write buffer too small (%d/%d bytes)\n",
							wr.used, wr.size);
					goto bug;
				} else {
					/* offs != 0 => something was "written"  */
					*rest_buf = buf + offs;
					*rest_len = len - offs;
					/* this function should be called again => disallow
					   immediate closing of the connection */
					send_flags->f &= ~SND_F_CON_CLOSE;
					TLS_WR_TRACE("(%p) SSL_ERROR_WANT_WRITE partial write"
								" (written %p , %d, rest_buf=%p"
								" rest_len=%d))\n", c, buf, offs,
								*rest_buf, *rest_len);
				}
				break; /* or goto end */
			case SSL_ERROR_SSL:
				/* protocol level error */
				TLS_ERR(err_src);
				goto error;
#if OPENSSL_VERSION_NUMBER >= 0x00907000L /*0.9.7*/
			case SSL_ERROR_WANT_CONNECT:
				/* only if the underlying BIO is not yet connected
				   and the call would block in connect().
				   (not possible in our case) */
				BUG("unexpected SSL_ERROR_WANT_CONNECT\n");
				break;
			case SSL_ERROR_WANT_ACCEPT:
				/* only if the underlying BIO is not yet connected
				   and call would block in accept()
				   (not possible in our case) */
				BUG("unexpected SSL_ERROR_WANT_ACCEPT\n");
				break;
#endif
			case SSL_ERROR_WANT_X509_LOOKUP:
				/* can only appear on client application and it indicates that
				   an installed client cert. callback should be called again
				   (it returned < 0 indicated that it wants to be called
				   later). Not possible in our case */
				BUG("unsupported SSL_ERROR_WANT_X509_LOOKUP");
				goto bug;
			case SSL_ERROR_SYSCALL:
				TLS_ERR_RET(x, err_src);
				if (!x) {
					if (n == 0) {
						WARN("Unexpected EOF\n");
					} else
						/* should never happen */
						BUG("IO error (%d) %s\n", errno, strerror(errno));
				}
				goto error;
			default:
				TLS_ERR(err_src);
				BUG("unexpected SSL error %d\n", ssl_error);
				goto bug;
		}
	} else if (unlikely(n < (len - offs))) {
		/* partial ssl write (possible if SSL_MODE_ENABLE_PARTIAL_WRITE) =>
		   retry with the rest */
		TLS_WR_TRACE("(%p) partial write (%d < %d, offset %d), retry\n",
						c, n, len - offs, offs);
		offs += n;
		goto redo_wr;
	}
	tls_set_mbufs(c, 0, 0);
end:
	*pbuf = (const char*)wr.buf;
	*plen = wr.used;
	TLS_WR_TRACE("(%p) end (offs %d, rest_buf=%p rest_len=%d 0x%0x) => %d \n",
					c, offs, *rest_buf, *rest_len, send_flags->f, *plen);
	return *plen;
error:
/*error_send:*/
error_wq_full:
bug:
	tls_set_mbufs(c, 0, 0);
	TLS_WR_TRACE("(%p) end error (offs %d, %d encoded) => -1\n",
					c, offs, wr.used);
	return -1;
ssl_eof:
	c->state = S_CONN_EOF;
	c->flags |= F_CONN_FORCE_EOF;
	*pbuf = (const char*)wr.buf;
	*plen = wr.used;
	DBG("TLS connection has been closed\n");
	TLS_WR_TRACE("(%p) end EOF (offs %d) => (%d\n",
					c, offs, *plen);
	return *plen;
}
Ejemplo n.º 27
0
static BOOL rdg_tls_connect(rdpRdg* rdg, rdpTls* tls, const char* peerAddress, int timeout)
{
	int sockfd = 0;
	int status = 0;
	BIO* socketBio = NULL;
	BIO* bufferedBio = NULL;
	rdpSettings* settings = rdg->settings;
	const char* peerHostname = settings->GatewayHostname;
	UINT16 peerPort = settings->GatewayPort;
	const char* proxyUsername, *proxyPassword;
	BOOL isProxyConnection = proxy_prepare(settings, &peerHostname, &peerPort, &proxyUsername,
	                                       &proxyPassword);
	sockfd = freerdp_tcp_connect(rdg->context, settings,
	                             peerAddress ? peerAddress : peerHostname,
	                             peerPort, timeout);

	if (sockfd < 0)
	{
		return FALSE;
	}

	socketBio = BIO_new(BIO_s_simple_socket());

	if (!socketBio)
	{
		closesocket(sockfd);
		return FALSE;
	}

	BIO_set_fd(socketBio, sockfd, BIO_CLOSE);
	bufferedBio = BIO_new(BIO_s_buffered_socket());

	if (!bufferedBio)
	{
		closesocket(sockfd);
		BIO_free(socketBio);
		return FALSE;
	}

	bufferedBio = BIO_push(bufferedBio, socketBio);
	status = BIO_set_nonblock(bufferedBio, TRUE);

	if (isProxyConnection)
	{
		if (!proxy_connect(settings, bufferedBio, proxyUsername, proxyPassword, settings->GatewayHostname,
		                   settings->GatewayPort))
			return FALSE;
	}

	if (!status)
	{
		BIO_free_all(bufferedBio);
		return FALSE;
	}

	tls->hostname = settings->GatewayHostname;
	tls->port = settings->GatewayPort;
	tls->isGatewayTransport = TRUE;
	status = tls_connect(tls, bufferedBio);
	return (status >= 1);
}
Ejemplo n.º 28
0
/**
 * Send an authentication request to NuAuth. May restart TLS session
 * and/or open TLS connection (if closed).
 *
 * Create the thread authsrv() when opening a new session.
 *
 * Packet maximum size is 512 bytes,
 * and it's structure is ::nufw_to_nuauth_auth_message_t.
 *
 * \param type Type of request (::AUTH_REQUEST, ::AUTH_CONTROL, ...)
 * \param pckt_data A pointer to a queued_pckt:: holding packet information
 * \return If an error occurs returns 0, else return 1.
 */
int auth_request_send(uint8_t type, struct queued_pckt *pckt_data)
{
	unsigned char data[512];
	nuv4_nufw_to_nuauth_auth_message_t *msg_header =
	    (nuv4_nufw_to_nuauth_auth_message_t *) & data;
	unsigned char *msg_content =
	    data + sizeof(nuv4_nufw_to_nuauth_auth_message_t);
	int msg_length;

	/* Drop non-IPv(4|6) packet */
	if ((((struct iphdr *) (pckt_data->payload))->version != 4)
	    && (((struct iphdr *) (pckt_data->payload))->version != 6)) {
		log_area_printf(DEBUG_AREA_PACKET, DEBUG_LEVEL_DEBUG,
				 "Dropping non-IPv4/non-IPv6 packet (version %u)",
				 ((struct iphdr *) (pckt_data->payload))->
				 version);
		return 0;
	}

	/* Truncate packet content if needed */
	if (sizeof(data) <
	    sizeof(nuv4_nufw_to_nuauth_auth_message_t) + pckt_data->payload_len) {
		debug_log_printf(DEBUG_AREA_PACKET, DEBUG_LEVEL_DEBUG,
				 "Very long packet: truncating!");
		pckt_data->payload_len =
		    sizeof(data) -
		    sizeof(nuv4_nufw_to_nuauth_auth_message_t);
	}
	msg_length = sizeof(nuv4_nufw_to_nuauth_auth_message_t) + pckt_data->payload_len;

	/* Fill message header */
	msg_header->protocol_version = PROTO_NUFW_VERSION;
	msg_header->msg_type = type;
	msg_header->msg_length = htons(msg_length);
	msg_header->packet_id = htonl(pckt_data->packet_id);
	msg_header->timestamp = htonl(pckt_data->timestamp);

	/* Add info about interfaces */
	msg_header->mark = pckt_data->mark;
	memcpy(msg_header->indev, pckt_data->indev,
	       IFNAMSIZ * sizeof(char));
	memcpy(msg_header->outdev, pckt_data->outdev,
	       IFNAMSIZ * sizeof(char));
	memcpy(msg_header->physindev, pckt_data->physindev,
	       IFNAMSIZ * sizeof(char));
	memcpy(msg_header->physoutdev, pckt_data->physoutdev,
	       IFNAMSIZ * sizeof(char));

	/* Copy (maybe truncated) packet content */
	memcpy(msg_content, pckt_data->payload, pckt_data->payload_len);

	/* Display message */
	log_area_printf(DEBUG_AREA_PACKET, DEBUG_LEVEL_DEBUG,
			"Sending request for %lu", (long)pckt_data->packet_id);

	/* negotiate TLS connection if needed */
	if (!tls.session) {
		log_area_printf(DEBUG_AREA_GW, DEBUG_LEVEL_INFO,
				"Not connected, trying TLS connection");
		tls_connect();

		if (tls.session) {
			int fd;
			char buf[256];
			buf[0] = '\0';
			nussl_session_get_cipher(tls.session, buf, sizeof(buf));
			log_area_printf(DEBUG_AREA_GW,
					DEBUG_LEVEL_WARNING,
					"[+] TLS connection to nuauth restored (%s:%d), cipher is %s",
					authreq_addr, authreq_port,
					(buf[0] != '\0') ? buf : "none" );

			fd = nussl_session_get_fd(tls.session);
			if (fd >= 0) {
				ev_io_init(&tls.ev_io, tls_activity_cb,
						nussl_session_get_fd(tls.session), EV_READ);
				tls.ev_io.data = &nufw_nfq_watcher;
				ev_io_start(nufw_loop, &tls.ev_io);
			}
		} else {
			log_area_printf(DEBUG_AREA_GW,
					DEBUG_LEVEL_WARNING,
					"[!] TLS connection to nuauth can NOT be restored (%s:%d)",
					authreq_addr, authreq_port);
			return 0;
		}
	}

	if (nussl_write(tls.session, (char*)data, msg_length) < 0) {
		debug_log_printf(DEBUG_AREA_MAIN, DEBUG_LEVEL_DEBUG,
				 "Error during nussl_write (auth_request_send).");
		shutdown_tls();
		log_area_printf(DEBUG_AREA_GW,
				DEBUG_LEVEL_WARNING,
				"[!] TLS send failure");
		return 0;
	}
	return 1;
}
Ejemplo n.º 29
0
int main(int argc, char *argv[])
{
	struct tls_config *conf;
	struct tls *ctx;
	int res;
	const char *host;

	if (argc < 2)
		errx(1, "give host as arg\n");
	host = argv[1];

	res = tls_init();
	if (res < 0)
		errx(1, "tls_init");

	conf = tls_config_new();
	if (!conf)
		errx(1, "tls_config_new");

	tls_config_set_protocols(conf, TLS_PROTOCOLS_ALL);
	tls_config_set_ciphers(conf, "HIGH:+3DES:!aNULL");
	tls_config_set_ca_file(conf, "/etc/ssl/certs/ca-certificates.crt");

	ctx = tls_client();
	if (!ctx)
		errx(1, "tls_client");

	res = tls_configure(ctx, conf);
	if (res < 0)
		errx(1, "tls_configure: %s", tls_error(ctx));

	res = tls_connect(ctx, host, "443");
	if (res < 0)
		errx(1, "tls_connect: %s", tls_error(ctx));

	res = tls_handshake(ctx);
	if (res < 0)
		errx(1, "tls_handshake: %s", tls_error(ctx));

	printf("connect ok\n");

#if 0
	struct tls_cert *cert;
	//res = tls_get_peer_cert(ctx, &cert, NULL);
	//if (res < 0)
	//errx(1, "tls_get_peer_cert: %s", tls_error(ctx));
	printf("  CN='%s'\n", cert->subject.common_name);
	printf("  C='%s'\n", cert->subject.country_name);
	printf("  ST='%s'\n", cert->subject.state_or_province_name);
	printf("  L='%s'\n", cert->subject.locality_name);
	printf("  S='%s'\n", cert->subject.street_address);
	printf("  O='%s'\n", cert->subject.organization_name);
	printf("  OU='%s'\n", cert->subject.organizational_unit_name);

	tls_cert_free(cert);
#endif
	tls_close(ctx);
	tls_free(ctx);
	tls_config_free(conf);

	return 0;
}
Ejemplo n.º 30
0
/**
 * Simple command-line HTTP client.
 */
int main( int argc, char *argv[ ] )
{
  int client_connection;
  char *host, *path;
  char *proxy_host, *proxy_user, *proxy_password;
  int proxy_port;
  struct hostent *host_name;
  struct sockaddr_in host_address;
  int port = HTTPS_PORT;
  int ind;
  int master_secret_length;
  unsigned char *master_secret;
  int session_id_length;
  unsigned char *session_id;
#ifdef WIN32
  WSADATA wsaData;
#endif

  TLSParameters tls_context;

  if ( argc < 2 )
  {
    fprintf( stderr, 
      "Usage: %s: [-p http://[username:password@]proxy-host:proxy-port] <URL>\n", 
      argv[ 0 ] );
    return 1;
  }

  proxy_host = proxy_user = proxy_password = host = path = session_id = master_secret = NULL;
  session_id_length = master_secret_length = 0;

  for ( ind = 1; ind < ( argc - 1 ); ind++ )
  {
    if ( !strcmp( "-p", argv[ ind ] ) )
    {
      if ( !parse_proxy_param( argv[ ++ind ], &proxy_host, &proxy_port,
                               &proxy_user, &proxy_password ) )
      {
        fprintf( stderr, "Error - malformed proxy parameter '%s'.\n", argv[ 2 ] );
        return 2;
      }
    }
    else if ( !strcmp( "-s", argv[ ind ] ) )
    {
      session_id_length = hex_decode( argv[ ++ind ], &session_id );
    }
    else if ( !strcmp( "-m", argv[ ind ] ) )
    {
      master_secret_length = hex_decode( argv[ ++ind ], &master_secret );
    }
  }

  if ( ( ( master_secret_length > 0 ) && ( session_id_length == 0 ) ) ||
       ( ( master_secret_length == 0 ) && ( session_id_length > 0 ) ) )
  {
    fprintf( stderr, "session id and master secret must both be provided.\n" );
    return 3;
  }

  if ( parse_url( argv[ ind ], &host, &path ) == -1 )
  {
    fprintf( stderr, "Error - malformed URL '%s'.\n", argv[ 1 ] );
    return 1;
  }

  printf( "Connecting to host '%s'\n", host );
  // Step 1: open a socket connection on http port with the destination host.
#ifdef WIN32
  if ( WSAStartup( MAKEWORD( 2, 2 ), &wsaData ) != NO_ERROR )
  {
    fprintf( stderr, "Error, unable to initialize winsock.\n" );
    return 2;
  }
#endif

  client_connection = socket( PF_INET, SOCK_STREAM, 0 );

  if ( !client_connection )
  {
    perror( "Unable to create local socket" );
    return 2;
  }

  if ( proxy_host )
  {
    printf( "Connecting to host '%s'\n", proxy_host );
    host_name = gethostbyname( proxy_host );
  } 
  else
  {
    host_name = gethostbyname( host );
  }

  if ( !host_name )
  {
    perror( "Error in name resolution" );
    return 3;
  }

  host_address.sin_family = AF_INET;
  host_address.sin_port = htons( proxy_host ? proxy_port : HTTPS_PORT  );
  memcpy( &host_address.sin_addr, host_name->h_addr_list[ 0 ], 
          sizeof( struct in_addr ) );

  if ( connect( client_connection, ( struct sockaddr * ) &host_address, 
       sizeof( host_address ) ) == -1 )
  {
    perror( "Unable to connect to host" );
    return 4;
  }

  printf( "Connection complete; negotiating TLS parameters\n" );

  if ( proxy_host )
  {
    if ( !http_connect( client_connection, host, port, proxy_user, 
                        proxy_password ) )
    {
      perror( "Unable to establish proxy tunnel" );
      if ( close( client_connection ) == -1 )
      {
        perror( "Error closing client connection" );
        return 2;
      }
      return 3;
    }
  }

  if ( session_id != NULL )
  {
    if ( tls_resume( client_connection, session_id_length,
         session_id, master_secret, &tls_context ) )
    {
      fprintf( stderr, "Error: unable to negotiate SSL connection.\n" );
      if ( close( client_connection ) == -1 )
      {
        perror( "Error closing client connection" );
        return 2;
      }
      return 3;
    }
  }
  else
  {
    if ( tls_connect( client_connection, &tls_context, 0 ) )
    {
      fprintf( stderr, "Error: unable to negotiate TLS connection.\n" );
      return 3;
    }
  }

  printf( "Retrieving document: '%s'\n", path );
  http_get( client_connection, path, host, &tls_context );

  display_result( client_connection, &tls_context );

  tls_shutdown( client_connection, &tls_context );

  printf( "Session ID was: " );
  show_hex( tls_context.session_id, tls_context.session_id_length );
  printf( "Master secret was: " );
  show_hex( tls_context.master_secret, MASTER_SECRET_LENGTH );

  printf( "Shutting down.\n" );

#ifdef WIN32
  if ( closesocket( client_connection ) == -1 )
#else
  if ( close( client_connection ) == -1 )
#endif
  {
    perror( "Error closing client connection" );
    return 5;
  }

  if ( session_id != NULL )
  {
    free( session_id );
  } 
  
  if ( master_secret != NULL )
  {
    free( master_secret );
  } 

#ifdef WIN32
  WSACleanup();
#endif

  return 0;
}