xref: /openbsd-src/lib/libtls/tls_client.c (revision 40ae7d6bc66d03e54ad7f33a82a1e22aa4b447ca)
1*40ae7d6bSjoshua /* $OpenBSD: tls_client.c,v 1.51 2024/03/26 08:54:48 joshua Exp $ */
2b600beedSjsing /*
3b600beedSjsing  * Copyright (c) 2014 Joel Sing <jsing@openbsd.org>
4b600beedSjsing  *
5b600beedSjsing  * Permission to use, copy, modify, and distribute this software for any
6b600beedSjsing  * purpose with or without fee is hereby granted, provided that the above
7b600beedSjsing  * copyright notice and this permission notice appear in all copies.
8b600beedSjsing  *
9b600beedSjsing  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10b600beedSjsing  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11b600beedSjsing  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12b600beedSjsing  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13b600beedSjsing  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14b600beedSjsing  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15b600beedSjsing  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16b600beedSjsing  */
17b600beedSjsing 
18b600beedSjsing #include <sys/types.h>
19b600beedSjsing #include <sys/socket.h>
200dd084b9Sjsing #include <sys/stat.h>
21b600beedSjsing 
22b600beedSjsing #include <arpa/inet.h>
23af0755b1Sbcook #include <netinet/in.h>
24b600beedSjsing 
250dd084b9Sjsing #include <limits.h>
26b600beedSjsing #include <netdb.h>
27b600beedSjsing #include <stdlib.h>
28e6d77be9Sop #include <string.h>
29b600beedSjsing #include <unistd.h>
30b600beedSjsing 
319ba095aaSjsing #include <openssl/err.h>
32b600beedSjsing #include <openssl/x509.h>
33b600beedSjsing 
34b600beedSjsing #include <tls.h>
35b600beedSjsing #include "tls_internal.h"
36b600beedSjsing 
37b600beedSjsing struct tls *
tls_client(void)38b600beedSjsing tls_client(void)
39b600beedSjsing {
40b600beedSjsing 	struct tls *ctx;
41b600beedSjsing 
42b9573a74Sjsing 	if (tls_init() == -1)
43b9573a74Sjsing 		return (NULL);
44b9573a74Sjsing 
45b600beedSjsing 	if ((ctx = tls_new()) == NULL)
46b600beedSjsing 		return (NULL);
47b600beedSjsing 
48b600beedSjsing 	ctx->flags |= TLS_CLIENT;
49b600beedSjsing 
50b600beedSjsing 	return (ctx);
51b600beedSjsing }
52b600beedSjsing 
53b600beedSjsing int
tls_connect(struct tls * ctx,const char * host,const char * port)54b600beedSjsing tls_connect(struct tls *ctx, const char *host, const char *port)
55b600beedSjsing {
56e2dbdfc5Sjsing 	return tls_connect_servername(ctx, host, port, NULL);
57e2dbdfc5Sjsing }
58e2dbdfc5Sjsing 
59e2dbdfc5Sjsing int
tls_connect_servername(struct tls * ctx,const char * host,const char * port,const char * servername)60e2dbdfc5Sjsing tls_connect_servername(struct tls *ctx, const char *host, const char *port,
61e2dbdfc5Sjsing     const char *servername)
62e2dbdfc5Sjsing {
63e3007dfeSguenther 	struct addrinfo hints, *res, *res0;
64b600beedSjsing 	const char *h = NULL, *p = NULL;
65b600beedSjsing 	char *hs = NULL, *ps = NULL;
66b600beedSjsing 	int rv = -1, s = -1, ret;
67b600beedSjsing 
68b600beedSjsing 	if ((ctx->flags & TLS_CLIENT) == 0) {
69*40ae7d6bSjoshua 		tls_set_errorx(ctx, TLS_ERROR_INVALID_CONTEXT,
70*40ae7d6bSjoshua 		    "not a client context");
71b600beedSjsing 		goto err;
72b600beedSjsing 	}
73b600beedSjsing 
74b600beedSjsing 	if (host == NULL) {
757a756d37Sjoshua 		tls_set_errorx(ctx, TLS_ERROR_UNKNOWN, "host not specified");
76b600beedSjsing 		goto err;
77b600beedSjsing 	}
78b600beedSjsing 
791b7b92c5Stb 	/* If port is NULL, try to extract a port from the specified host. */
801b7b92c5Stb 	if (port == NULL) {
81b600beedSjsing 		ret = tls_host_port(host, &hs, &ps);
82b600beedSjsing 		if (ret == -1) {
837a756d37Sjoshua 			tls_set_errorx(ctx, TLS_ERROR_OUT_OF_MEMORY, "out of memory");
84b600beedSjsing 			goto err;
85b600beedSjsing 		}
86abc69f3cSjsing 		if (ret != 0) {
877a756d37Sjoshua 			tls_set_errorx(ctx, TLS_ERROR_UNKNOWN, "no port provided");
88abc69f3cSjsing 			goto err;
89abc69f3cSjsing 		}
90b600beedSjsing 	}
91b600beedSjsing 
92b600beedSjsing 	h = (hs != NULL) ? hs : host;
93b600beedSjsing 	p = (ps != NULL) ? ps : port;
94b600beedSjsing 
958e5d73c6Sreyk 	/*
968e5d73c6Sreyk 	 * First check if the host is specified as a numeric IP address,
978e5d73c6Sreyk 	 * either IPv4 or IPv6, before trying to resolve the host.
988e5d73c6Sreyk 	 * The AI_ADDRCONFIG resolver option will not return IPv4 or IPv6
998e5d73c6Sreyk 	 * records if it is not configured on an interface;  not considering
1008e5d73c6Sreyk 	 * loopback addresses.  Checking the numeric addresses first makes
1018e5d73c6Sreyk 	 * sure that connection attempts to numeric addresses and especially
1028e5d73c6Sreyk 	 * 127.0.0.1 or ::1 loopback addresses are always possible.
1038e5d73c6Sreyk 	 */
104e3007dfeSguenther 	memset(&hints, 0, sizeof(hints));
105e3007dfeSguenther 	hints.ai_socktype = SOCK_STREAM;
106e3007dfeSguenther 
107e3007dfeSguenther 	/* try as an IPv4 literal */
108e3007dfeSguenther 	hints.ai_family = AF_INET;
109e3007dfeSguenther 	hints.ai_flags = AI_NUMERICHOST;
110e3007dfeSguenther 	if (getaddrinfo(h, p, &hints, &res0) != 0) {
111e3007dfeSguenther 		/* try again as an IPv6 literal */
112e3007dfeSguenther 		hints.ai_family = AF_INET6;
113e3007dfeSguenther 		if (getaddrinfo(h, p, &hints, &res0) != 0) {
114e3007dfeSguenther 			/* last try, with name resolution and save the error */
115e3007dfeSguenther 			hints.ai_family = AF_UNSPEC;
116e3007dfeSguenther 			hints.ai_flags = AI_ADDRCONFIG;
117e3007dfeSguenther 			if ((s = getaddrinfo(h, p, &hints, &res0)) != 0) {
1187a756d37Sjoshua 				tls_set_error(ctx, TLS_ERROR_UNKNOWN,
1197a756d37Sjoshua 				    "%s", gai_strerror(s));
120b600beedSjsing 				goto err;
121e3007dfeSguenther 			}
122e3007dfeSguenther 		}
123e3007dfeSguenther 	}
124e3007dfeSguenther 
125e3007dfeSguenther 	/* It was resolved somehow; now try connecting to what we got */
126f053eeaeSderaadt 	s = -1;
127e3007dfeSguenther 	for (res = res0; res; res = res->ai_next) {
128e3007dfeSguenther 		s = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
129e3007dfeSguenther 		if (s == -1) {
1307a756d37Sjoshua 			tls_set_error(ctx, TLS_ERROR_UNKNOWN,
1317a756d37Sjoshua 			    "socket");
132e3007dfeSguenther 			continue;
133e3007dfeSguenther 		}
134e3007dfeSguenther 		if (connect(s, res->ai_addr, res->ai_addrlen) == -1) {
1357a756d37Sjoshua 			tls_set_error(ctx, TLS_ERROR_UNKNOWN,
1367a756d37Sjoshua 			    "connect");
137e3007dfeSguenther 			close(s);
138e3007dfeSguenther 			s = -1;
139e3007dfeSguenther 			continue;
140e3007dfeSguenther 		}
141e3007dfeSguenther 
142e3007dfeSguenther 		break;  /* Connected. */
143e3007dfeSguenther 	}
144e3007dfeSguenther 	freeaddrinfo(res0);
145b600beedSjsing 
146f053eeaeSderaadt 	if (s == -1)
147f053eeaeSderaadt 		goto err;
148f053eeaeSderaadt 
149e2dbdfc5Sjsing 	if (servername == NULL)
150e2dbdfc5Sjsing 		servername = h;
151e2dbdfc5Sjsing 
152e2dbdfc5Sjsing 	if (tls_connect_socket(ctx, s, servername) != 0) {
153b600beedSjsing 		close(s);
154b600beedSjsing 		goto err;
155b600beedSjsing 	}
156b600beedSjsing 
157aa7238e6Sjsing 	ctx->socket = s;
158aa7238e6Sjsing 
159b600beedSjsing 	rv = 0;
160b600beedSjsing 
161b600beedSjsing  err:
162b600beedSjsing 	free(hs);
163b600beedSjsing 	free(ps);
164b600beedSjsing 
165b600beedSjsing 	return (rv);
166b600beedSjsing }
167b600beedSjsing 
168ed19021fSbcook static int
tls_client_read_session(struct tls * ctx)1690dd084b9Sjsing tls_client_read_session(struct tls *ctx)
1700dd084b9Sjsing {
1710dd084b9Sjsing 	int sfd = ctx->config->session_fd;
1720dd084b9Sjsing 	uint8_t *session = NULL;
1730dd084b9Sjsing 	size_t session_len = 0;
1740dd084b9Sjsing 	SSL_SESSION *ss = NULL;
1750dd084b9Sjsing 	BIO *bio = NULL;
1760dd084b9Sjsing 	struct stat sb;
1770dd084b9Sjsing 	ssize_t n;
1780dd084b9Sjsing 	int rv = -1;
1790dd084b9Sjsing 
1800dd084b9Sjsing 	if (fstat(sfd, &sb) == -1) {
1817a756d37Sjoshua 		tls_set_error(ctx, TLS_ERROR_UNKNOWN,
1827a756d37Sjoshua 		    "failed to stat session file");
1830dd084b9Sjsing 		goto err;
1840dd084b9Sjsing 	}
1850dd084b9Sjsing 	if (sb.st_size < 0 || sb.st_size > INT_MAX) {
1867a756d37Sjoshua 		tls_set_errorx(ctx, TLS_ERROR_UNKNOWN,
1877a756d37Sjoshua 		    "invalid session file size");
1880dd084b9Sjsing 		goto err;
1890dd084b9Sjsing 	}
1900dd084b9Sjsing 	session_len = (size_t)sb.st_size;
1910dd084b9Sjsing 
1920dd084b9Sjsing 	/* A zero size file means that we do not yet have a valid session. */
1930dd084b9Sjsing 	if (session_len == 0)
1940dd084b9Sjsing 		goto done;
1950dd084b9Sjsing 
1960dd084b9Sjsing 	if ((session = malloc(session_len)) == NULL)
1970dd084b9Sjsing 		goto err;
1980dd084b9Sjsing 
1990dd084b9Sjsing 	n = pread(sfd, session, session_len, 0);
2000dd084b9Sjsing 	if (n < 0 || (size_t)n != session_len) {
2017a756d37Sjoshua 		tls_set_error(ctx, TLS_ERROR_UNKNOWN,
2027a756d37Sjoshua 		    "failed to read session file");
2030dd084b9Sjsing 		goto err;
2040dd084b9Sjsing 	}
2050dd084b9Sjsing 	if ((bio = BIO_new_mem_buf(session, session_len)) == NULL)
2060dd084b9Sjsing 		goto err;
2070dd084b9Sjsing 	if ((ss = PEM_read_bio_SSL_SESSION(bio, NULL, tls_password_cb,
2080dd084b9Sjsing 	    NULL)) == NULL) {
2097a756d37Sjoshua 		tls_set_errorx(ctx, TLS_ERROR_UNKNOWN,
2107a756d37Sjoshua 		    "failed to parse session");
2110dd084b9Sjsing 		goto err;
2120dd084b9Sjsing 	}
2130dd084b9Sjsing 
2140dd084b9Sjsing 	if (SSL_set_session(ctx->ssl_conn, ss) != 1) {
2157a756d37Sjoshua 		tls_set_errorx(ctx, TLS_ERROR_UNKNOWN,
2167a756d37Sjoshua 		    "failed to set session");
2170dd084b9Sjsing 		goto err;
2180dd084b9Sjsing 	}
2190dd084b9Sjsing 
2200dd084b9Sjsing  done:
2210dd084b9Sjsing 	rv = 0;
2220dd084b9Sjsing 
2230dd084b9Sjsing  err:
2240dd084b9Sjsing 	freezero(session, session_len);
2250dd084b9Sjsing 	SSL_SESSION_free(ss);
2260dd084b9Sjsing 	BIO_free(bio);
2270dd084b9Sjsing 
2280dd084b9Sjsing 	return rv;
2290dd084b9Sjsing }
2300dd084b9Sjsing 
2310dd084b9Sjsing static int
tls_client_write_session(struct tls * ctx)2320dd084b9Sjsing tls_client_write_session(struct tls *ctx)
2330dd084b9Sjsing {
2340dd084b9Sjsing 	int sfd = ctx->config->session_fd;
2350dd084b9Sjsing 	SSL_SESSION *ss = NULL;
2360dd084b9Sjsing 	BIO *bio = NULL;
2370dd084b9Sjsing 	long data_len;
2380dd084b9Sjsing 	char *data;
2390dd084b9Sjsing 	off_t offset;
2400dd084b9Sjsing 	size_t len;
2410dd084b9Sjsing 	ssize_t n;
2420dd084b9Sjsing 	int rv = -1;
2430dd084b9Sjsing 
2440dd084b9Sjsing 	if ((ss = SSL_get1_session(ctx->ssl_conn)) == NULL) {
2450dd084b9Sjsing 		if (ftruncate(sfd, 0) == -1) {
2467a756d37Sjoshua 			tls_set_error(ctx, TLS_ERROR_UNKNOWN,
2477a756d37Sjoshua 			    "failed to truncate session file");
2480dd084b9Sjsing 			goto err;
2490dd084b9Sjsing 		}
2500dd084b9Sjsing 		goto done;
2510dd084b9Sjsing 	}
2520dd084b9Sjsing 
2530dd084b9Sjsing 	if ((bio = BIO_new(BIO_s_mem())) == NULL)
2540dd084b9Sjsing 		goto err;
2550dd084b9Sjsing 	if (PEM_write_bio_SSL_SESSION(bio, ss) == 0)
2560dd084b9Sjsing 		goto err;
2570dd084b9Sjsing 	if ((data_len = BIO_get_mem_data(bio, &data)) <= 0)
2580dd084b9Sjsing 		goto err;
2590dd084b9Sjsing 
2600dd084b9Sjsing 	len = (size_t)data_len;
2610dd084b9Sjsing 	offset = 0;
2620dd084b9Sjsing 
2630dd084b9Sjsing 	if (ftruncate(sfd, len) == -1) {
2647a756d37Sjoshua 		tls_set_error(ctx, TLS_ERROR_UNKNOWN,
2657a756d37Sjoshua 		    "failed to truncate session file");
2660dd084b9Sjsing 		goto err;
2670dd084b9Sjsing 	}
2680dd084b9Sjsing 	while (len > 0) {
2690dd084b9Sjsing 		if ((n = pwrite(sfd, data + offset, len, offset)) == -1) {
2707a756d37Sjoshua 			tls_set_error(ctx, TLS_ERROR_UNKNOWN,
2717a756d37Sjoshua 			    "failed to write session file");
2720dd084b9Sjsing 			goto err;
2730dd084b9Sjsing 		}
2740dd084b9Sjsing 		offset += n;
2750dd084b9Sjsing 		len -= n;
2760dd084b9Sjsing 	}
2770dd084b9Sjsing 
2780dd084b9Sjsing  done:
2790dd084b9Sjsing 	rv = 0;
2800dd084b9Sjsing 
2810dd084b9Sjsing  err:
2820dd084b9Sjsing 	SSL_SESSION_free(ss);
2830dd084b9Sjsing 	BIO_free_all(bio);
2840dd084b9Sjsing 
2850dd084b9Sjsing 	return (rv);
2860dd084b9Sjsing }
2870dd084b9Sjsing 
2880dd084b9Sjsing static int
tls_connect_common(struct tls * ctx,const char * servername)2892233a86cSjsing tls_connect_common(struct tls *ctx, const char *servername)
2904a8c0db6Sjsing {
291fc5c813dSjsing 	union tls_addr addrbuf;
292bac16b51Stb 	size_t servername_len;
293e2b71c11Sjsing 	int rv = -1;
2943d6199ebSbluhm 
295b600beedSjsing 	if ((ctx->flags & TLS_CLIENT) == 0) {
296*40ae7d6bSjoshua 		tls_set_errorx(ctx, TLS_ERROR_INVALID_CONTEXT,
297*40ae7d6bSjoshua 		    "not a client context");
298b600beedSjsing 		goto err;
299b600beedSjsing 	}
300b600beedSjsing 
301e2b71c11Sjsing 	if (servername != NULL) {
302e2b71c11Sjsing 		if ((ctx->servername = strdup(servername)) == NULL) {
3037a756d37Sjoshua 			tls_set_errorx(ctx, TLS_ERROR_OUT_OF_MEMORY,
3047a756d37Sjoshua 			    "out of memory");
305e2b71c11Sjsing 			goto err;
306e2b71c11Sjsing 		}
307bac16b51Stb 
308bac16b51Stb 		/*
309bac16b51Stb 		 * If there's a trailing dot, remove it. While an FQDN includes
310bac16b51Stb 		 * the terminating dot representing the zero-length label of
311bac16b51Stb 		 * the root (RFC 8499, section 2), the SNI explicitly does not
312bac16b51Stb 		 * include it (RFC 6066, section 3).
313bac16b51Stb 		 */
314bac16b51Stb 		servername_len = strlen(ctx->servername);
315bac16b51Stb 		if (servername_len > 0 &&
316bac16b51Stb 		    ctx->servername[servername_len - 1] == '.')
317bac16b51Stb 			ctx->servername[servername_len - 1] = '\0';
3184a8c0db6Sjsing 	}
319b600beedSjsing 
320b600beedSjsing 	if ((ctx->ssl_ctx = SSL_CTX_new(SSLv23_client_method())) == NULL) {
3217a756d37Sjoshua 		tls_set_errorx(ctx, TLS_ERROR_UNKNOWN, "ssl context failure");
322b600beedSjsing 		goto err;
323b600beedSjsing 	}
324b600beedSjsing 
325b7f318e7Sjsing 	if (tls_configure_ssl(ctx, ctx->ssl_ctx) != 0)
326b600beedSjsing 		goto err;
327ed19021fSbcook 
328b7f318e7Sjsing 	if (tls_configure_ssl_keypair(ctx, ctx->ssl_ctx,
329b7f318e7Sjsing 	    ctx->config->keypair, 0) != 0)
33051f3bd3dSbeck 		goto err;
331b600beedSjsing 
3320ca7b9dfSjsing 	if (ctx->config->verify_name) {
333bac16b51Stb 		if (ctx->servername == NULL) {
3347a756d37Sjoshua 			tls_set_errorx(ctx, TLS_ERROR_UNKNOWN,
3357a756d37Sjoshua 			    "server name not specified");
336b600beedSjsing 			goto err;
337b600beedSjsing 		}
338b600beedSjsing 	}
339ed19021fSbcook 
340888c565eSjsing 	if (tls_configure_ssl_verify(ctx, ctx->ssl_ctx, SSL_VERIFY_PEER) == -1)
34199cfb8c5Sreyk 		goto err;
342b600beedSjsing 
3434896de1eSjsing 	if (ctx->config->ecdhecurves != NULL) {
3444896de1eSjsing 		if (SSL_CTX_set1_groups(ctx->ssl_ctx, ctx->config->ecdhecurves,
3454896de1eSjsing 		    ctx->config->ecdhecurves_len) != 1) {
3467a756d37Sjoshua 			tls_set_errorx(ctx, TLS_ERROR_UNKNOWN,
3477a756d37Sjoshua 			    "failed to set ecdhe curves");
3484896de1eSjsing 			goto err;
3494896de1eSjsing 		}
3504896de1eSjsing 	}
3514896de1eSjsing 
3522dc6b4e4Sbeck 	if (SSL_CTX_set_tlsext_status_cb(ctx->ssl_ctx, tls_ocsp_verify_cb) != 1) {
3537a756d37Sjoshua 		tls_set_errorx(ctx, TLS_ERROR_UNKNOWN,
3547a756d37Sjoshua 		    "ssl OCSP verification setup failure");
3552dc6b4e4Sbeck 		goto err;
3562dc6b4e4Sbeck 	}
3572dc6b4e4Sbeck 
358b600beedSjsing 	if ((ctx->ssl_conn = SSL_new(ctx->ssl_ctx)) == NULL) {
3597a756d37Sjoshua 		tls_set_errorx(ctx, TLS_ERROR_UNKNOWN, "ssl connection failure");
360b600beedSjsing 		goto err;
361b600beedSjsing 	}
362ed19021fSbcook 
3638c73da28Sjsing 	if (SSL_set_app_data(ctx->ssl_conn, ctx) != 1) {
3647a756d37Sjoshua 		tls_set_errorx(ctx, TLS_ERROR_UNKNOWN,
3657a756d37Sjoshua 		    "ssl application data failure");
3668c73da28Sjsing 		goto err;
3678c73da28Sjsing 	}
368b600beedSjsing 
3690dd084b9Sjsing 	if (ctx->config->session_fd != -1) {
3700dd084b9Sjsing 		SSL_clear_options(ctx->ssl_conn, SSL_OP_NO_TICKET);
3710dd084b9Sjsing 		if (tls_client_read_session(ctx) == -1)
3720dd084b9Sjsing 			goto err;
3730dd084b9Sjsing 	}
3740dd084b9Sjsing 
3752dc6b4e4Sbeck 	if (SSL_set_tlsext_status_type(ctx->ssl_conn, TLSEXT_STATUSTYPE_ocsp) != 1) {
3767a756d37Sjoshua 		tls_set_errorx(ctx, TLS_ERROR_UNKNOWN,
3777a756d37Sjoshua 		    "ssl OCSP extension setup failure");
3782dc6b4e4Sbeck 		goto err;
3792dc6b4e4Sbeck 	}
3802dc6b4e4Sbeck 
381b600beedSjsing 	/*
3825beec0e2Stb 	 * RFC 6066 (SNI): Literal IPv4 and IPv6 addresses are not
383b600beedSjsing 	 * permitted in "HostName".
384b600beedSjsing 	 */
385bac16b51Stb 	if (ctx->servername != NULL &&
386bac16b51Stb 	    inet_pton(AF_INET, ctx->servername, &addrbuf) != 1 &&
387bac16b51Stb 	    inet_pton(AF_INET6, ctx->servername, &addrbuf) != 1) {
388bac16b51Stb 		if (SSL_set_tlsext_host_name(ctx->ssl_conn,
389bac16b51Stb 		    ctx->servername) == 0) {
3907a756d37Sjoshua 			tls_set_errorx(ctx, TLS_ERROR_UNKNOWN,
3917a756d37Sjoshua 			    "server name indication failure");
392b600beedSjsing 			goto err;
393b600beedSjsing 		}
394b600beedSjsing 	}
395d5f88726Sjsing 
396d5f88726Sjsing 	ctx->state |= TLS_CONNECTED;
397ed19021fSbcook 	rv = 0;
398ed19021fSbcook 
399ed19021fSbcook  err:
400ed19021fSbcook 	return (rv);
401ed19021fSbcook }
402ed19021fSbcook 
403ed19021fSbcook int
tls_connect_socket(struct tls * ctx,int s,const char * servername)404ed19021fSbcook tls_connect_socket(struct tls *ctx, int s, const char *servername)
405ed19021fSbcook {
406ed19021fSbcook 	return tls_connect_fds(ctx, s, s, servername);
407ed19021fSbcook }
408ed19021fSbcook 
409ed19021fSbcook int
tls_connect_fds(struct tls * ctx,int fd_read,int fd_write,const char * servername)410ed19021fSbcook tls_connect_fds(struct tls *ctx, int fd_read, int fd_write,
411ed19021fSbcook     const char *servername)
412ed19021fSbcook {
413ed19021fSbcook 	int rv = -1;
414ed19021fSbcook 
415ed19021fSbcook 	if (fd_read < 0 || fd_write < 0) {
4167a756d37Sjoshua 		tls_set_errorx(ctx, TLS_ERROR_UNKNOWN, "invalid file descriptors");
417ed19021fSbcook 		goto err;
418ed19021fSbcook 	}
419ed19021fSbcook 
4202233a86cSjsing 	if (tls_connect_common(ctx, servername) != 0)
421ed19021fSbcook 		goto err;
422ed19021fSbcook 
423ed19021fSbcook 	if (SSL_set_rfd(ctx->ssl_conn, fd_read) != 1 ||
424ed19021fSbcook 	    SSL_set_wfd(ctx->ssl_conn, fd_write) != 1) {
4257a756d37Sjoshua 		tls_set_errorx(ctx, TLS_ERROR_UNKNOWN,
4267a756d37Sjoshua 		    "ssl file descriptor failure");
427ed19021fSbcook 		goto err;
428ed19021fSbcook 	}
429ed19021fSbcook 
430ed19021fSbcook 	rv = 0;
431ed19021fSbcook  err:
432ed19021fSbcook 	return (rv);
433ed19021fSbcook }
434ed19021fSbcook 
435ed19021fSbcook int
tls_connect_cbs(struct tls * ctx,tls_read_cb read_cb,tls_write_cb write_cb,void * cb_arg,const char * servername)436ed19021fSbcook tls_connect_cbs(struct tls *ctx, tls_read_cb read_cb,
437ed19021fSbcook     tls_write_cb write_cb, void *cb_arg, const char *servername)
438ed19021fSbcook {
439ed19021fSbcook 	int rv = -1;
440ed19021fSbcook 
4412233a86cSjsing 	if (tls_connect_common(ctx, servername) != 0)
442ed19021fSbcook 		goto err;
443ed19021fSbcook 
44459bda8beSjsing 	if (tls_set_cbs(ctx, read_cb, write_cb, cb_arg) != 0)
445ed19021fSbcook 		goto err;
446b600beedSjsing 
447e2b71c11Sjsing 	rv = 0;
448e2b71c11Sjsing 
449e2b71c11Sjsing  err:
450e2b71c11Sjsing 	return (rv);
451b600beedSjsing }
452e2b71c11Sjsing 
453e2b71c11Sjsing int
tls_handshake_client(struct tls * ctx)454e2b71c11Sjsing tls_handshake_client(struct tls *ctx)
455e2b71c11Sjsing {
456e2b71c11Sjsing 	X509 *cert = NULL;
4575f3c5205Sjsing 	int match, ssl_ret;
458e2b71c11Sjsing 	int rv = -1;
459e2b71c11Sjsing 
460e2b71c11Sjsing 	if ((ctx->flags & TLS_CLIENT) == 0) {
461*40ae7d6bSjoshua 		tls_set_errorx(ctx, TLS_ERROR_INVALID_CONTEXT,
462*40ae7d6bSjoshua 		    "not a client context");
463d474f84fSjsing 		goto err;
4643d6199ebSbluhm 	}
465e2b71c11Sjsing 
466d5f88726Sjsing 	if ((ctx->state & TLS_CONNECTED) == 0) {
4677a756d37Sjoshua 		tls_set_errorx(ctx, TLS_ERROR_UNKNOWN, "context not connected");
468d5f88726Sjsing 		goto err;
469d5f88726Sjsing 	}
470d5f88726Sjsing 
4715a752462Sjsing 	ctx->state |= TLS_SSL_NEEDS_SHUTDOWN;
4725a752462Sjsing 
4739ba095aaSjsing 	ERR_clear_error();
474e2b71c11Sjsing 	if ((ssl_ret = SSL_connect(ctx->ssl_conn)) != 1) {
475e2b71c11Sjsing 		rv = tls_ssl_error(ctx, ctx->ssl_conn, ssl_ret, "handshake");
476e2b71c11Sjsing 		goto err;
477e2b71c11Sjsing 	}
478b600beedSjsing 
4790ca7b9dfSjsing 	if (ctx->config->verify_name) {
480b600beedSjsing 		cert = SSL_get_peer_certificate(ctx->ssl_conn);
481b600beedSjsing 		if (cert == NULL) {
4827a756d37Sjoshua 			tls_set_errorx(ctx, TLS_ERROR_UNKNOWN,
4837a756d37Sjoshua 			    "no server certificate");
484b600beedSjsing 			goto err;
485b600beedSjsing 		}
4865f3c5205Sjsing 		if (tls_check_name(ctx, cert, ctx->servername, &match) == -1)
4875f3c5205Sjsing 			goto err;
4885f3c5205Sjsing 		if (!match) {
4897a756d37Sjoshua 			tls_set_errorx(ctx, TLS_ERROR_UNKNOWN,
4907a756d37Sjoshua 			    "name `%s' not present in server certificate",
4917a756d37Sjoshua 			    ctx->servername);
492b600beedSjsing 			goto err;
493b600beedSjsing 		}
494b600beedSjsing 	}
495b600beedSjsing 
496e2b71c11Sjsing 	ctx->state |= TLS_HANDSHAKE_COMPLETE;
4970dd084b9Sjsing 
4980dd084b9Sjsing 	if (ctx->config->session_fd != -1) {
4990dd084b9Sjsing 		if (tls_client_write_session(ctx) == -1)
5000dd084b9Sjsing 			goto err;
5010dd084b9Sjsing 	}
5020dd084b9Sjsing 
503e2b71c11Sjsing 	rv = 0;
504b600beedSjsing 
505b600beedSjsing  err:
506b600beedSjsing 	X509_free(cert);
507b600beedSjsing 
508e2b71c11Sjsing 	return (rv);
509b600beedSjsing }
510