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