summaryrefslogtreecommitdiff
path: root/src/lib/libressl/ressl.c
diff options
context:
space:
mode:
authorjsing <>2014-07-12 01:20:25 +0000
committerjsing <>2014-07-12 01:20:25 +0000
commit2b0153d4f076d501b21de2e54937a5bb1b139635 (patch)
treecb217fd8fb935cea61fc26de366b7e407229df65 /src/lib/libressl/ressl.c
parentc95157e4b6c5e281cb496ef41f9969df25abef91 (diff)
downloadopenbsd-2b0153d4f076d501b21de2e54937a5bb1b139635.tar.gz
openbsd-2b0153d4f076d501b21de2e54937a5bb1b139635.tar.bz2
openbsd-2b0153d4f076d501b21de2e54937a5bb1b139635.zip
Initial version of libressl - a library that provides a clean, simple,
consistent and secure-by-default API for SSL clients (and soon servers). This is a long way from complete and the interface will likely change substantially - committing now so that further work can happen in the tree. Initiated by tedu@ and inspired by discussions with tedu@, beck@ and other developers.
Diffstat (limited to 'src/lib/libressl/ressl.c')
-rw-r--r--src/lib/libressl/ressl.c335
1 files changed, 335 insertions, 0 deletions
diff --git a/src/lib/libressl/ressl.c b/src/lib/libressl/ressl.c
new file mode 100644
index 0000000000..21b7c0ead0
--- /dev/null
+++ b/src/lib/libressl/ressl.c
@@ -0,0 +1,335 @@
1/*
2 * Copyright (c) 2014 Joel Sing <jsing@openbsd.org>
3 *
4 * Permission to use, copy, modify, and distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
7 *
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 */
16
17#include <sys/types.h>
18#include <sys/socket.h>
19
20#include <arpa/inet.h>
21
22#include <errno.h>
23#include <netdb.h>
24#include <stdlib.h>
25#include <unistd.h>
26
27#include <openssl/x509.h>
28
29#include <ressl/ressl.h>
30
31#include "ressl_internal.h"
32
33extern struct ressl_config ressl_config_default;
34
35int
36ressl_init(void)
37{
38 static int ressl_initialised = 0;
39
40 if (ressl_initialised)
41 return (0);
42
43 SSL_load_error_strings();
44 SSL_library_init();
45
46 ressl_initialised = 1;
47
48 return (0);
49}
50
51const char *
52ressl_error(struct ressl *ctx)
53{
54 return ctx->errmsg;
55}
56
57int
58ressl_set_error(struct ressl *ctx, char *fmt, ...)
59{
60 va_list ap;
61 int rv;
62
63 ctx->err = errno;
64 free(ctx->errmsg);
65 ctx->errmsg = NULL;
66
67 va_start(ap, fmt);
68 rv = vasprintf(&ctx->errmsg, fmt, ap);
69 va_end(ap);
70
71 return (rv);
72}
73
74struct ressl *
75ressl_new(struct ressl_config *config)
76{
77 struct ressl *ctx;
78
79 if ((ctx = calloc(1, sizeof(*ctx))) == NULL)
80 return (NULL);
81
82 if (config == NULL)
83 config = &ressl_config_default;
84
85 ctx->config = config;
86
87 ressl_reset(ctx);
88
89 return (ctx);
90}
91
92void
93ressl_free(struct ressl *ctx)
94{
95 if (ctx == NULL)
96 return;
97 ressl_reset(ctx);
98 free(ctx);
99}
100
101void
102ressl_reset(struct ressl *ctx)
103{
104 /* SSL_free frees the SSL context. */
105 if (ctx->ssl_conn != NULL)
106 SSL_free(ctx->ssl_conn);
107 else
108 SSL_CTX_free(ctx->ssl_ctx);
109
110 ctx->ssl_conn = NULL;
111 ctx->ssl_ctx = NULL;
112
113 ctx->socket = -1;
114
115 ctx->err = 0;
116 free(ctx->errmsg);
117 ctx->errmsg = NULL;
118}
119
120int
121ressl_connect(struct ressl *ctx, const char *host, const char *port)
122{
123 struct addrinfo hints, *res, *res0;
124 const char *h = NULL, *p = NULL;
125 char *hs = NULL, *ps = NULL;
126 int rv = -1, s = -1, ret;
127
128 if (host == NULL) {
129 ressl_set_error(ctx, "host not specified");
130 goto err;
131 }
132
133 /*
134 * If port is NULL try to extract a port from the specified host,
135 * otherwise use the default.
136 */
137 if ((p = (char *)port) == NULL) {
138 ret = ressl_host_port(host, &hs, &ps);
139 if (ret == -1) {
140 ressl_set_error(ctx, "memory allocation failure");
141 goto err;
142 }
143 if (ret != 0)
144 port = HTTPS_PORT;
145 }
146
147 h = (hs != NULL) ? hs : host;
148 p = (ps != NULL) ? ps : port;
149
150 memset(&hints, 0, sizeof(hints));
151 hints.ai_family = AF_UNSPEC;
152 hints.ai_socktype = SOCK_STREAM;
153
154 if ((ret = getaddrinfo(h, p, &hints, &res0)) != 0) {
155 ressl_set_error(ctx, "%s", gai_strerror(ret));
156 goto err;
157 }
158 for (res = res0; res; res = res->ai_next) {
159 s = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
160 if (s == -1) {
161 ressl_set_error(ctx, "socket");
162 continue;
163 }
164 if (connect(s, res->ai_addr, res->ai_addrlen) == -1) {
165 ressl_set_error(ctx, "connect");
166 close(s);
167 s = -1;
168 continue;
169 }
170
171 break; /* Connected. */
172 }
173 freeaddrinfo(res0);
174
175 if (s == -1)
176 goto err;
177
178 if (ressl_connect_socket(ctx, s, h) != 0) {
179 close(s);
180 goto err;
181 }
182
183 rv = 0;
184
185err:
186
187 free(hs);
188 free(ps);
189
190 return (rv);
191}
192
193int
194ressl_connect_socket(struct ressl *ctx, int socket, const char *hostname)
195{
196 union { struct in_addr ip4; struct in6_addr ip6; } addrbuf;
197 X509 *cert = NULL;
198 int ret;
199
200 ctx->socket = socket;
201
202 /* XXX - add a configuration option to control versions. */
203 if ((ctx->ssl_ctx = SSL_CTX_new(SSLv23_client_method())) == NULL) {
204 ressl_set_error(ctx, "ssl context failure");
205 goto err;
206 }
207 if (ctx->config->verify) {
208 if (hostname == NULL) {
209 ressl_set_error(ctx, "server name not specified");
210 goto err;
211 }
212
213 SSL_CTX_set_verify(ctx->ssl_ctx, SSL_VERIFY_PEER, NULL);
214
215 if (SSL_CTX_load_verify_locations(ctx->ssl_ctx,
216 ctx->config->ca_file, ctx->config->ca_path) != 1) {
217 ressl_set_error(ctx, "ssl verify setup failure");
218 goto err;
219 }
220 if (ctx->config->verify_depth >= 0)
221 SSL_CTX_set_verify_depth(ctx->ssl_ctx,
222 ctx->config->verify_depth);
223 }
224
225 if ((ctx->ssl_conn = SSL_new(ctx->ssl_ctx)) == NULL) {
226 ressl_set_error(ctx, "ssl connection failure");
227 goto err;
228 }
229 if (SSL_set_fd(ctx->ssl_conn, ctx->socket) != 1) {
230 ressl_set_error(ctx, "ssl file descriptor failure");
231 goto err;
232 }
233
234 /*
235 * RFC4366 (SNI): Literal IPv4 and IPv6 addresses are not
236 * permitted in "HostName".
237 */
238 if (hostname != NULL &&
239 inet_pton(AF_INET, hostname, &addrbuf) != 1 &&
240 inet_pton(AF_INET6, hostname, &addrbuf) != 1) {
241 if (SSL_set_tlsext_host_name(ctx->ssl_conn, hostname) == 0) {
242 ressl_set_error(ctx, "SNI host name failed");
243 goto err;
244 }
245 }
246
247 if ((ret = SSL_connect(ctx->ssl_conn)) != 1) {
248 ressl_set_error(ctx, "SSL connect failed: %i",
249 SSL_get_error(ctx->ssl_conn, ret));
250 goto err;
251 }
252
253 if (ctx->config->verify) {
254 cert = SSL_get_peer_certificate(ctx->ssl_conn);
255 if (cert == NULL) {
256 ressl_set_error(ctx, "no server certificate");
257 goto err;
258 }
259 if (ressl_check_hostname(cert, hostname) != 0) {
260 ressl_set_error(ctx, "host `%s' not present in"
261 " server certificate", hostname);
262 goto err;
263 }
264 }
265
266 return (0);
267
268err:
269 X509_free(cert);
270
271 return (-1);
272}
273
274int
275ressl_read(struct ressl *ctx, char *buf, size_t buflen, size_t *outlen)
276{
277 int ret;
278
279 /* XXX - handle async/non-blocking. */
280 ret = SSL_read(ctx->ssl_conn, buf, buflen);
281 if (ret <= 0) {
282 ret = SSL_get_error(ctx->ssl_conn, ret);
283 if (ret == SSL_ERROR_WANT_READ)
284 return (-2);
285 ressl_set_error(ctx, "read failed: %i", ret);
286 return (-1);
287 }
288 *outlen = (size_t)ret;
289 return (0);
290}
291
292int
293ressl_write(struct ressl *ctx, const char *buf, size_t buflen, size_t *outlen)
294{
295 int ret;
296
297 /* XXX - handle async/non-blocking. */
298 ret = SSL_write(ctx->ssl_conn, buf, buflen);
299 if (ret < 0) {
300 ressl_set_error(ctx, "write failed %d",
301 SSL_get_error(ctx->ssl_conn, ret));
302 return (-1);
303 }
304 *outlen = (size_t)ret;
305 return (0);
306}
307
308int
309ressl_close(struct ressl *ctx)
310{
311 /* XXX - handle case where multiple calls are required. */
312 if (ctx->ssl_conn != NULL) {
313 if (SSL_shutdown(ctx->ssl_conn) == -1) {
314 ressl_set_error(ctx, "SSL shutdown failed");
315 goto err;
316 }
317 }
318
319 if (ctx->socket != -1) {
320 if (shutdown(ctx->socket, SHUT_RDWR) != 0) {
321 ressl_set_error(ctx, "shutdown");
322 goto err;
323 }
324 if (close(ctx->socket) != 0) {
325 ressl_set_error(ctx, "close");
326 goto err;
327 }
328 ctx->socket = -1;
329 }
330
331 return (0);
332
333err:
334 return (-1);
335}