diff options
author | Joel Sing <jsing@cvs.openbsd.org> | 2014-10-31 13:46:18 +0000 |
---|---|---|
committer | Joel Sing <jsing@cvs.openbsd.org> | 2014-10-31 13:46:18 +0000 |
commit | f44b598f55e4b6375817972d506ad93e63c93bd1 (patch) | |
tree | 853c2c67e6a38e22e1a866a41d78d694c9411397 /lib/libtls/tls_client.c | |
parent | c3c9d9dbad2cd6645037029656c4a7339bda69da (diff) |
Rename libressl to libtls to avoid confusion and to make it easier to
distinguish between LibreSSL (the project) and libressl (the library).
Discussed with many.
Diffstat (limited to 'lib/libtls/tls_client.c')
-rw-r--r-- | lib/libtls/tls_client.c | 212 |
1 files changed, 212 insertions, 0 deletions
diff --git a/lib/libtls/tls_client.c b/lib/libtls/tls_client.c new file mode 100644 index 00000000000..853766f87b6 --- /dev/null +++ b/lib/libtls/tls_client.c @@ -0,0 +1,212 @@ +/* $OpenBSD: tls_client.c,v 1.1 2014/10/31 13:46:17 jsing Exp $ */ +/* + * Copyright (c) 2014 Joel Sing <jsing@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <sys/types.h> +#include <sys/socket.h> + +#include <arpa/inet.h> + +#include <netdb.h> +#include <stdlib.h> +#include <unistd.h> + +#include <openssl/x509.h> + +#include <tls.h> +#include "tls_internal.h" + +struct tls * +tls_client(void) +{ + struct tls *ctx; + + if ((ctx = tls_new()) == NULL) + return (NULL); + + ctx->flags |= TLS_CLIENT; + + return (ctx); +} + +int +tls_connect(struct tls *ctx, const char *host, const char *port) +{ + struct addrinfo hints, *res, *res0; + const char *h = NULL, *p = NULL; + char *hs = NULL, *ps = NULL; + int rv = -1, s = -1, ret; + + if ((ctx->flags & TLS_CLIENT) == 0) { + tls_set_error(ctx, "not a client context"); + goto err; + } + + if (host == NULL) { + tls_set_error(ctx, "host not specified"); + goto err; + } + + /* + * If port is NULL try to extract a port from the specified host, + * otherwise use the default. + */ + if ((p = (char *)port) == NULL) { + ret = tls_host_port(host, &hs, &ps); + if (ret == -1) { + tls_set_error(ctx, "memory allocation failure"); + goto err; + } + if (ret != 0) + port = HTTPS_PORT; + } + + h = (hs != NULL) ? hs : host; + p = (ps != NULL) ? ps : port; + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + + if ((ret = getaddrinfo(h, p, &hints, &res0)) != 0) { + tls_set_error(ctx, "%s", gai_strerror(ret)); + goto err; + } + for (res = res0; res; res = res->ai_next) { + s = socket(res->ai_family, res->ai_socktype, res->ai_protocol); + if (s == -1) { + tls_set_error(ctx, "socket"); + continue; + } + if (connect(s, res->ai_addr, res->ai_addrlen) == -1) { + tls_set_error(ctx, "connect"); + close(s); + s = -1; + continue; + } + + break; /* Connected. */ + } + freeaddrinfo(res0); + + if (s == -1) + goto err; + + if (tls_connect_socket(ctx, s, h) != 0) { + close(s); + goto err; + } + + rv = 0; + +err: + + free(hs); + free(ps); + + return (rv); +} + +int +tls_connect_socket(struct tls *ctx, int socket, const char *hostname) +{ + union { struct in_addr ip4; struct in6_addr ip6; } addrbuf; + X509 *cert = NULL; + int ret; + + if ((ctx->flags & TLS_CLIENT) == 0) { + tls_set_error(ctx, "not a client context"); + goto err; + } + + ctx->socket = socket; + + if ((ctx->ssl_ctx = SSL_CTX_new(SSLv23_client_method())) == NULL) { + tls_set_error(ctx, "ssl context failure"); + goto err; + } + + if (tls_configure_ssl(ctx) != 0) + goto err; + + if (ctx->config->verify_host) { + if (hostname == NULL) { + tls_set_error(ctx, "server name not specified"); + goto err; + } + } + + if (ctx->config->verify_cert) { + SSL_CTX_set_verify(ctx->ssl_ctx, SSL_VERIFY_PEER, NULL); + + if (SSL_CTX_load_verify_locations(ctx->ssl_ctx, + ctx->config->ca_file, ctx->config->ca_path) != 1) { + tls_set_error(ctx, "ssl verify setup failure"); + goto err; + } + if (ctx->config->verify_depth >= 0) + SSL_CTX_set_verify_depth(ctx->ssl_ctx, + ctx->config->verify_depth); + } + + if ((ctx->ssl_conn = SSL_new(ctx->ssl_ctx)) == NULL) { + tls_set_error(ctx, "ssl connection failure"); + goto err; + } + if (SSL_set_fd(ctx->ssl_conn, ctx->socket) != 1) { + tls_set_error(ctx, "ssl file descriptor failure"); + goto err; + } + + /* + * RFC4366 (SNI): Literal IPv4 and IPv6 addresses are not + * permitted in "HostName". + */ + if (hostname != NULL && + inet_pton(AF_INET, hostname, &addrbuf) != 1 && + inet_pton(AF_INET6, hostname, &addrbuf) != 1) { + if (SSL_set_tlsext_host_name(ctx->ssl_conn, hostname) == 0) { + tls_set_error(ctx, "SNI host name failed"); + goto err; + } + } + + if ((ret = SSL_connect(ctx->ssl_conn)) != 1) { + tls_set_error(ctx, "SSL connect failed: %i", + SSL_get_error(ctx->ssl_conn, ret)); + goto err; + } + + if (ctx->config->verify_host) { + cert = SSL_get_peer_certificate(ctx->ssl_conn); + if (cert == NULL) { + tls_set_error(ctx, "no server certificate"); + goto err; + } + if (tls_check_hostname(cert, hostname) != 0) { + tls_set_error(ctx, "host `%s' not present in" + " server certificate", hostname); + goto err; + } + } + + return (0); + +err: + X509_free(cert); + + return (-1); +} |