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