xref: /openbsd-src/lib/libtls/tls_client.c (revision 5b859c19fe53bbea08f5c342e0a4470e99f883e1)
1 /* $OpenBSD: tls_client.c,v 1.2 2014/11/02 14:45:05 jsing Exp $ */
2 /*
3  * Copyright (c) 2014 Joel Sing <jsing@openbsd.org>
4  *
5  * Permission to use, copy, modify, and distribute this software for any
6  * purpose with or without fee is hereby granted, provided that the above
7  * copyright notice and this permission notice appear in all copies.
8  *
9  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16  */
17 
18 #include <sys/types.h>
19 #include <sys/socket.h>
20 
21 #include <arpa/inet.h>
22 
23 #include <netdb.h>
24 #include <stdlib.h>
25 #include <unistd.h>
26 
27 #include <openssl/x509.h>
28 
29 #include <tls.h>
30 #include "tls_internal.h"
31 
32 struct tls *
33 tls_client(void)
34 {
35 	struct tls *ctx;
36 
37 	if ((ctx = tls_new()) == NULL)
38 		return (NULL);
39 
40 	ctx->flags |= TLS_CLIENT;
41 
42 	return (ctx);
43 }
44 
45 int
46 tls_connect(struct tls *ctx, const char *host, const char *port)
47 {
48 	struct addrinfo hints, *res, *res0;
49 	const char *h = NULL, *p = NULL;
50 	char *hs = NULL, *ps = NULL;
51 	int rv = -1, s = -1, ret;
52 
53 	if ((ctx->flags & TLS_CLIENT) == 0) {
54 		tls_set_error(ctx, "not a client context");
55 		goto err;
56 	}
57 
58 	if (host == NULL) {
59 		tls_set_error(ctx, "host not specified");
60 		goto err;
61 	}
62 
63 	/*
64 	 * If port is NULL try to extract a port from the specified host,
65 	 * otherwise use the default.
66 	 */
67 	if ((p = (char *)port) == NULL) {
68 		ret = tls_host_port(host, &hs, &ps);
69 		if (ret == -1) {
70 			tls_set_error(ctx, "memory allocation failure");
71 			goto err;
72 		}
73 		if (ret != 0)
74 			port = HTTPS_PORT;
75 	}
76 
77 	h = (hs != NULL) ? hs : host;
78 	p = (ps != NULL) ? ps : port;
79 
80 	memset(&hints, 0, sizeof(hints));
81 	hints.ai_family = AF_UNSPEC;
82 	hints.ai_socktype = SOCK_STREAM;
83 
84 	if ((ret = getaddrinfo(h, p, &hints, &res0)) != 0) {
85 		tls_set_error(ctx, "%s", gai_strerror(ret));
86 		goto err;
87 	}
88 	for (res = res0; res; res = res->ai_next) {
89 		s = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
90 		if (s == -1) {
91 			tls_set_error(ctx, "socket");
92 			continue;
93 		}
94 		if (connect(s, res->ai_addr, res->ai_addrlen) == -1) {
95 			tls_set_error(ctx, "connect");
96 			close(s);
97 			s = -1;
98 			continue;
99 		}
100 
101 		break;  /* Connected. */
102 	}
103 	freeaddrinfo(res0);
104 
105 	if (s == -1)
106 		goto err;
107 
108 	if (tls_connect_socket(ctx, s, h) != 0) {
109 		close(s);
110 		goto err;
111 	}
112 
113 	rv = 0;
114 
115 err:
116 
117 	free(hs);
118 	free(ps);
119 
120 	return (rv);
121 }
122 
123 int
124 tls_connect_socket(struct tls *ctx, int socket, const char *hostname)
125 {
126 	ctx->socket = socket;
127 
128 	return tls_connect_fds(ctx, socket, socket, hostname);
129 }
130 
131 int
132 tls_connect_fds(struct tls *ctx, int fd_read, int fd_write,
133     const char *hostname)
134 {
135 	union { struct in_addr ip4; struct in6_addr ip6; } addrbuf;
136 	X509 *cert = NULL;
137 	int ret;
138 
139 	if ((ctx->flags & TLS_CLIENT) == 0) {
140 		tls_set_error(ctx, "not a client context");
141 		goto err;
142 	}
143 
144 	if (fd_read < 0 || fd_write < 0) {
145 		tls_set_error(ctx, "invalid file descriptors");
146 		return (-1);
147 	}
148 
149 	if ((ctx->ssl_ctx = SSL_CTX_new(SSLv23_client_method())) == NULL) {
150 		tls_set_error(ctx, "ssl context failure");
151 		goto err;
152 	}
153 
154 	if (tls_configure_ssl(ctx) != 0)
155 		goto err;
156 
157 	if (ctx->config->verify_host) {
158 		if (hostname == NULL) {
159 			tls_set_error(ctx, "server name not specified");
160 			goto err;
161 		}
162 	}
163 
164 	if (ctx->config->verify_cert) {
165 		SSL_CTX_set_verify(ctx->ssl_ctx, SSL_VERIFY_PEER, NULL);
166 
167 		if (SSL_CTX_load_verify_locations(ctx->ssl_ctx,
168 		    ctx->config->ca_file, ctx->config->ca_path) != 1) {
169 			tls_set_error(ctx, "ssl verify setup failure");
170 			goto err;
171 		}
172 		if (ctx->config->verify_depth >= 0)
173 			SSL_CTX_set_verify_depth(ctx->ssl_ctx,
174 			    ctx->config->verify_depth);
175 	}
176 
177 	if ((ctx->ssl_conn = SSL_new(ctx->ssl_ctx)) == NULL) {
178 		tls_set_error(ctx, "ssl connection failure");
179 		goto err;
180 	}
181 	if (SSL_set_rfd(ctx->ssl_conn, fd_read) != 1 ||
182 	    SSL_set_wfd(ctx->ssl_conn, fd_write) != 1) {
183 		tls_set_error(ctx, "ssl file descriptor failure");
184 		goto err;
185 	}
186 
187 	/*
188 	 * RFC4366 (SNI): Literal IPv4 and IPv6 addresses are not
189 	 * permitted in "HostName".
190 	 */
191 	if (hostname != NULL &&
192 	    inet_pton(AF_INET, hostname, &addrbuf) != 1 &&
193 	    inet_pton(AF_INET6, hostname, &addrbuf) != 1) {
194 		if (SSL_set_tlsext_host_name(ctx->ssl_conn, hostname) == 0) {
195 			tls_set_error(ctx, "SNI host name failed");
196 			goto err;
197 		}
198 	}
199 
200 	if ((ret = SSL_connect(ctx->ssl_conn)) != 1) {
201 		tls_set_error(ctx, "SSL connect failed: %i",
202 		    SSL_get_error(ctx->ssl_conn, ret));
203 		goto err;
204 	}
205 
206 	if (ctx->config->verify_host) {
207 		cert = SSL_get_peer_certificate(ctx->ssl_conn);
208 		if (cert == NULL) {
209 			tls_set_error(ctx, "no server certificate");
210 			goto err;
211 		}
212 		if (tls_check_hostname(cert, hostname) != 0) {
213 			tls_set_error(ctx, "host `%s' not present in"
214 			    " server certificate", hostname);
215 			goto err;
216 		}
217 	}
218 
219 	return (0);
220 
221 err:
222 	X509_free(cert);
223 
224 	return (-1);
225 }
226