diff options
Diffstat (limited to 'src/lib/libtls')
| -rw-r--r-- | src/lib/libtls/Makefile | 58 | ||||
| -rw-r--r-- | src/lib/libtls/shlib_version | 2 | ||||
| -rw-r--r-- | src/lib/libtls/tls.c | 300 | ||||
| -rw-r--r-- | src/lib/libtls/tls.h | 74 | ||||
| -rw-r--r-- | src/lib/libtls/tls_client.c | 212 | ||||
| -rw-r--r-- | src/lib/libtls/tls_config.c | 201 | ||||
| -rw-r--r-- | src/lib/libtls/tls_init.3 | 316 | ||||
| -rw-r--r-- | src/lib/libtls/tls_internal.h | 72 | ||||
| -rw-r--r-- | src/lib/libtls/tls_server.c | 134 | ||||
| -rw-r--r-- | src/lib/libtls/tls_util.c | 81 | ||||
| -rw-r--r-- | src/lib/libtls/tls_verify.c | 225 |
11 files changed, 1675 insertions, 0 deletions
diff --git a/src/lib/libtls/Makefile b/src/lib/libtls/Makefile new file mode 100644 index 0000000000..b83a6de2ce --- /dev/null +++ b/src/lib/libtls/Makefile | |||
| @@ -0,0 +1,58 @@ | |||
| 1 | # $OpenBSD: Makefile,v 1.1 2014/10/31 13:46:17 jsing Exp $ | ||
| 2 | |||
| 3 | CFLAGS+= -Wall -Werror -Wimplicit | ||
| 4 | CFLAGS+= -DLIBRESSL_INTERNAL | ||
| 5 | |||
| 6 | LIB= tls | ||
| 7 | |||
| 8 | DPADD= ${LIBCRYPTO} ${LIBSSL} | ||
| 9 | |||
| 10 | HDRS= tls.h | ||
| 11 | |||
| 12 | SRCS= tls.c \ | ||
| 13 | tls_client.c \ | ||
| 14 | tls_config.c \ | ||
| 15 | tls_server.c \ | ||
| 16 | tls_util.c \ | ||
| 17 | tls_verify.c | ||
| 18 | |||
| 19 | MAN= tls_init.3 | ||
| 20 | |||
| 21 | MLINKS+=tls_init.3 tls_config_new.3 | ||
| 22 | MLINKS+=tls_init.3 tls_config_free.3 | ||
| 23 | MLINKS+=tls_init.3 tls_config_set_ca_file.3 | ||
| 24 | MLINKS+=tls_init.3 tls_config_set_ca_path.3 | ||
| 25 | MLINKS+=tls_init.3 tls_config_set_cert_file.3 | ||
| 26 | MLINKS+=tls_init.3 tls_config_set_cert_mem.3 | ||
| 27 | MLINKS+=tls_init.3 tls_config_set_ciphers.3 | ||
| 28 | MLINKS+=tls_init.3 tls_config_set_ecdhcurve.3 | ||
| 29 | MLINKS+=tls_init.3 tls_config_set_key_file.3 | ||
| 30 | MLINKS+=tls_init.3 tls_config_set_key_mem.3 | ||
| 31 | MLINKS+=tls_init.3 tls_config_set_protocols.3 | ||
| 32 | MLINKS+=tls_init.3 tls_config_set_verify_depth.3 | ||
| 33 | MLINKS+=tls_init.3 tls_config_clear_keys.3 | ||
| 34 | MLINKS+=tls_init.3 tls_config_insecure_noverifyhost.3 | ||
| 35 | MLINKS+=tls_init.3 tls_config_insecure_noverifycert.3 | ||
| 36 | MLINKS+=tls_init.3 tls_config_verify.3 | ||
| 37 | MLINKS+=tls_init.3 tls_client.3 | ||
| 38 | MLINKS+=tls_init.3 tls_server.3 | ||
| 39 | MLINKS+=tls_init.3 tls_configure.3 | ||
| 40 | MLINKS+=tls_init.3 tls_error.3 | ||
| 41 | MLINKS+=tls_init.3 tls_reset.3 | ||
| 42 | MLINKS+=tls_init.3 tls_free.3 | ||
| 43 | MLINKS+=tls_init.3 tls_close.3 | ||
| 44 | MLINKS+=tls_init.3 tls_connect.3 | ||
| 45 | MLINKS+=tls_init.3 tls_connect_socket.3 | ||
| 46 | MLINKS+=tls_init.3 tls_read.3 | ||
| 47 | MLINKS+=tls_init.3 tls_write.3 | ||
| 48 | |||
| 49 | includes: | ||
| 50 | @cd ${.CURDIR}; for i in $(HDRS); do \ | ||
| 51 | j="cmp -s $$i ${DESTDIR}/usr/include/$$i || \ | ||
| 52 | ${INSTALL} ${INSTALL_COPY} -o ${BINOWN} -g ${BINGRP} -m 444 $$i\ | ||
| 53 | ${DESTDIR}/usr/include/"; \ | ||
| 54 | echo $$j; \ | ||
| 55 | eval "$$j"; \ | ||
| 56 | done; | ||
| 57 | |||
| 58 | .include <bsd.lib.mk> | ||
diff --git a/src/lib/libtls/shlib_version b/src/lib/libtls/shlib_version new file mode 100644 index 0000000000..1edea46de9 --- /dev/null +++ b/src/lib/libtls/shlib_version | |||
| @@ -0,0 +1,2 @@ | |||
| 1 | major=1 | ||
| 2 | minor=0 | ||
diff --git a/src/lib/libtls/tls.c b/src/lib/libtls/tls.c new file mode 100644 index 0000000000..a7f612e40b --- /dev/null +++ b/src/lib/libtls/tls.c | |||
| @@ -0,0 +1,300 @@ | |||
| 1 | /* $OpenBSD: tls.c,v 1.1 2014/10/31 13:46:17 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/socket.h> | ||
| 19 | |||
| 20 | #include <errno.h> | ||
| 21 | #include <stdlib.h> | ||
| 22 | #include <unistd.h> | ||
| 23 | |||
| 24 | #include <openssl/bio.h> | ||
| 25 | #include <openssl/evp.h> | ||
| 26 | #include <openssl/pem.h> | ||
| 27 | #include <openssl/x509.h> | ||
| 28 | |||
| 29 | #include <tls.h> | ||
| 30 | #include "tls_internal.h" | ||
| 31 | |||
| 32 | static struct tls_config *tls_config_default; | ||
| 33 | |||
| 34 | int | ||
| 35 | tls_init(void) | ||
| 36 | { | ||
| 37 | static int tls_initialised = 0; | ||
| 38 | |||
| 39 | if (tls_initialised) | ||
| 40 | return (0); | ||
| 41 | |||
| 42 | SSL_load_error_strings(); | ||
| 43 | SSL_library_init(); | ||
| 44 | |||
| 45 | if ((tls_config_default = tls_config_new()) == NULL) | ||
| 46 | return (-1); | ||
| 47 | |||
| 48 | tls_initialised = 1; | ||
| 49 | |||
| 50 | return (0); | ||
| 51 | } | ||
| 52 | |||
| 53 | const char * | ||
| 54 | tls_error(struct tls *ctx) | ||
| 55 | { | ||
| 56 | return ctx->errmsg; | ||
| 57 | } | ||
| 58 | |||
| 59 | int | ||
| 60 | tls_set_error(struct tls *ctx, char *fmt, ...) | ||
| 61 | { | ||
| 62 | va_list ap; | ||
| 63 | int rv; | ||
| 64 | |||
| 65 | ctx->err = errno; | ||
| 66 | free(ctx->errmsg); | ||
| 67 | ctx->errmsg = NULL; | ||
| 68 | |||
| 69 | va_start(ap, fmt); | ||
| 70 | rv = vasprintf(&ctx->errmsg, fmt, ap); | ||
| 71 | va_end(ap); | ||
| 72 | |||
| 73 | return (rv); | ||
| 74 | } | ||
| 75 | |||
| 76 | struct tls * | ||
| 77 | tls_new(void) | ||
| 78 | { | ||
| 79 | struct tls *ctx; | ||
| 80 | |||
| 81 | if ((ctx = calloc(1, sizeof(*ctx))) == NULL) | ||
| 82 | return (NULL); | ||
| 83 | |||
| 84 | ctx->config = tls_config_default; | ||
| 85 | |||
| 86 | tls_reset(ctx); | ||
| 87 | |||
| 88 | return (ctx); | ||
| 89 | } | ||
| 90 | |||
| 91 | int | ||
| 92 | tls_configure(struct tls *ctx, struct tls_config *config) | ||
| 93 | { | ||
| 94 | if (config == NULL) | ||
| 95 | config = tls_config_default; | ||
| 96 | |||
| 97 | ctx->config = config; | ||
| 98 | |||
| 99 | if ((ctx->flags & TLS_SERVER) != 0) | ||
| 100 | return (tls_configure_server(ctx)); | ||
| 101 | |||
| 102 | return (0); | ||
| 103 | } | ||
| 104 | |||
| 105 | int | ||
| 106 | tls_configure_keypair(struct tls *ctx) | ||
| 107 | { | ||
| 108 | EVP_PKEY *pkey = NULL; | ||
| 109 | X509 *cert = NULL; | ||
| 110 | BIO *bio = NULL; | ||
| 111 | |||
| 112 | if (ctx->config->cert_mem != NULL) { | ||
| 113 | if (SSL_CTX_use_certificate_chain(ctx->ssl_ctx, | ||
| 114 | ctx->config->cert_mem, ctx->config->cert_len) != 1) { | ||
| 115 | tls_set_error(ctx, "failed to load certificate"); | ||
| 116 | goto err; | ||
| 117 | } | ||
| 118 | cert = NULL; | ||
| 119 | } | ||
| 120 | if (ctx->config->key_mem != NULL) { | ||
| 121 | if ((bio = BIO_new_mem_buf(ctx->config->key_mem, | ||
| 122 | ctx->config->key_len)) == NULL) { | ||
| 123 | tls_set_error(ctx, "failed to create buffer"); | ||
| 124 | goto err; | ||
| 125 | } | ||
| 126 | if ((pkey = PEM_read_bio_PrivateKey(bio, NULL, NULL, | ||
| 127 | NULL)) == NULL) { | ||
| 128 | tls_set_error(ctx, "failed to read private key"); | ||
| 129 | goto err; | ||
| 130 | } | ||
| 131 | if (SSL_CTX_use_PrivateKey(ctx->ssl_ctx, pkey) != 1) { | ||
| 132 | tls_set_error(ctx, "failed to load private key"); | ||
| 133 | goto err; | ||
| 134 | } | ||
| 135 | BIO_free(bio); | ||
| 136 | bio = NULL; | ||
| 137 | EVP_PKEY_free(pkey); | ||
| 138 | pkey = NULL; | ||
| 139 | } | ||
| 140 | |||
| 141 | if (ctx->config->cert_file != NULL) { | ||
| 142 | if (SSL_CTX_use_certificate_chain_file(ctx->ssl_ctx, | ||
| 143 | ctx->config->cert_file) != 1) { | ||
| 144 | tls_set_error(ctx, "failed to load certificate file"); | ||
| 145 | goto err; | ||
| 146 | } | ||
| 147 | } | ||
| 148 | if (ctx->config->key_file != NULL) { | ||
| 149 | if (SSL_CTX_use_PrivateKey_file(ctx->ssl_ctx, | ||
| 150 | ctx->config->key_file, SSL_FILETYPE_PEM) != 1) { | ||
| 151 | tls_set_error(ctx, "failed to load private key file"); | ||
| 152 | goto err; | ||
| 153 | } | ||
| 154 | } | ||
| 155 | |||
| 156 | if (SSL_CTX_check_private_key(ctx->ssl_ctx) != 1) { | ||
| 157 | tls_set_error(ctx, "private/public key mismatch"); | ||
| 158 | goto err; | ||
| 159 | } | ||
| 160 | |||
| 161 | return (0); | ||
| 162 | |||
| 163 | err: | ||
| 164 | EVP_PKEY_free(pkey); | ||
| 165 | X509_free(cert); | ||
| 166 | BIO_free(bio); | ||
| 167 | |||
| 168 | return (1); | ||
| 169 | } | ||
| 170 | |||
| 171 | int | ||
| 172 | tls_configure_ssl(struct tls *ctx) | ||
| 173 | { | ||
| 174 | SSL_CTX_set_options(ctx->ssl_ctx, SSL_OP_NO_SSLv2); | ||
| 175 | SSL_CTX_set_options(ctx->ssl_ctx, SSL_OP_NO_SSLv3); | ||
| 176 | |||
| 177 | SSL_CTX_clear_options(ctx->ssl_ctx, SSL_OP_NO_TLSv1); | ||
| 178 | SSL_CTX_clear_options(ctx->ssl_ctx, SSL_OP_NO_TLSv1_1); | ||
| 179 | SSL_CTX_clear_options(ctx->ssl_ctx, SSL_OP_NO_TLSv1_2); | ||
| 180 | |||
| 181 | if ((ctx->config->protocols & TLS_PROTOCOL_TLSv1_0) == 0) | ||
| 182 | SSL_CTX_set_options(ctx->ssl_ctx, SSL_OP_NO_TLSv1); | ||
| 183 | if ((ctx->config->protocols & TLS_PROTOCOL_TLSv1_1) == 0) | ||
| 184 | SSL_CTX_set_options(ctx->ssl_ctx, SSL_OP_NO_TLSv1_1); | ||
| 185 | if ((ctx->config->protocols & TLS_PROTOCOL_TLSv1_2) == 0) | ||
| 186 | SSL_CTX_set_options(ctx->ssl_ctx, SSL_OP_NO_TLSv1_2); | ||
| 187 | |||
| 188 | if (ctx->config->ciphers != NULL) { | ||
| 189 | if (SSL_CTX_set_cipher_list(ctx->ssl_ctx, | ||
| 190 | ctx->config->ciphers) != 1) { | ||
| 191 | tls_set_error(ctx, "failed to set ciphers"); | ||
| 192 | goto err; | ||
| 193 | } | ||
| 194 | } | ||
| 195 | |||
| 196 | return (0); | ||
| 197 | |||
| 198 | err: | ||
| 199 | return (-1); | ||
| 200 | } | ||
| 201 | |||
| 202 | void | ||
| 203 | tls_free(struct tls *ctx) | ||
| 204 | { | ||
| 205 | if (ctx == NULL) | ||
| 206 | return; | ||
| 207 | tls_reset(ctx); | ||
| 208 | free(ctx); | ||
| 209 | } | ||
| 210 | |||
| 211 | void | ||
| 212 | tls_reset(struct tls *ctx) | ||
| 213 | { | ||
| 214 | SSL_CTX_free(ctx->ssl_ctx); | ||
| 215 | SSL_free(ctx->ssl_conn); | ||
| 216 | |||
| 217 | ctx->ssl_conn = NULL; | ||
| 218 | ctx->ssl_ctx = NULL; | ||
| 219 | |||
| 220 | ctx->socket = -1; | ||
| 221 | |||
| 222 | ctx->err = 0; | ||
| 223 | free(ctx->errmsg); | ||
| 224 | ctx->errmsg = NULL; | ||
| 225 | } | ||
| 226 | |||
| 227 | int | ||
| 228 | tls_read(struct tls *ctx, void *buf, size_t buflen, size_t *outlen) | ||
| 229 | { | ||
| 230 | int ret, ssl_err; | ||
| 231 | |||
| 232 | ret = SSL_read(ctx->ssl_conn, buf, buflen); | ||
| 233 | if (ret > 0) { | ||
| 234 | *outlen = (size_t)ret; | ||
| 235 | return (0); | ||
| 236 | } | ||
| 237 | |||
| 238 | ssl_err = SSL_get_error(ctx->ssl_conn, ret); | ||
| 239 | switch (ssl_err) { | ||
| 240 | case SSL_ERROR_WANT_READ: | ||
| 241 | return (TLS_READ_AGAIN); | ||
| 242 | case SSL_ERROR_WANT_WRITE: | ||
| 243 | return (TLS_WRITE_AGAIN); | ||
| 244 | default: | ||
| 245 | tls_set_error(ctx, "read failed (%i)", ssl_err); | ||
| 246 | return (-1); | ||
| 247 | } | ||
| 248 | } | ||
| 249 | |||
| 250 | int | ||
| 251 | tls_write(struct tls *ctx, const void *buf, size_t buflen, size_t *outlen) | ||
| 252 | { | ||
| 253 | int ret, ssl_err; | ||
| 254 | |||
| 255 | ret = SSL_write(ctx->ssl_conn, buf, buflen); | ||
| 256 | if (ret > 0) { | ||
| 257 | *outlen = (size_t)ret; | ||
| 258 | return (0); | ||
| 259 | } | ||
| 260 | |||
| 261 | ssl_err = SSL_get_error(ctx->ssl_conn, ret); | ||
| 262 | switch (ssl_err) { | ||
| 263 | case SSL_ERROR_WANT_READ: | ||
| 264 | return (TLS_READ_AGAIN); | ||
| 265 | case SSL_ERROR_WANT_WRITE: | ||
| 266 | return (TLS_WRITE_AGAIN); | ||
| 267 | default: | ||
| 268 | tls_set_error(ctx, "write failed (%i)", ssl_err); | ||
| 269 | return (-1); | ||
| 270 | } | ||
| 271 | } | ||
| 272 | |||
| 273 | int | ||
| 274 | tls_close(struct tls *ctx) | ||
| 275 | { | ||
| 276 | /* XXX - handle case where multiple calls are required. */ | ||
| 277 | if (ctx->ssl_conn != NULL) { | ||
| 278 | if (SSL_shutdown(ctx->ssl_conn) == -1) { | ||
| 279 | tls_set_error(ctx, "SSL shutdown failed"); | ||
| 280 | goto err; | ||
| 281 | } | ||
| 282 | } | ||
| 283 | |||
| 284 | if (ctx->socket != -1) { | ||
| 285 | if (shutdown(ctx->socket, SHUT_RDWR) != 0) { | ||
| 286 | tls_set_error(ctx, "shutdown"); | ||
| 287 | goto err; | ||
| 288 | } | ||
| 289 | if (close(ctx->socket) != 0) { | ||
| 290 | tls_set_error(ctx, "close"); | ||
| 291 | goto err; | ||
| 292 | } | ||
| 293 | ctx->socket = -1; | ||
| 294 | } | ||
| 295 | |||
| 296 | return (0); | ||
| 297 | |||
| 298 | err: | ||
| 299 | return (-1); | ||
| 300 | } | ||
diff --git a/src/lib/libtls/tls.h b/src/lib/libtls/tls.h new file mode 100644 index 0000000000..0fa776e584 --- /dev/null +++ b/src/lib/libtls/tls.h | |||
| @@ -0,0 +1,74 @@ | |||
| 1 | /* $OpenBSD: tls.h,v 1.1 2014/10/31 13:46:17 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 | #ifndef HEADER_TLS_H | ||
| 19 | #define HEADER_TLS_H | ||
| 20 | |||
| 21 | #define TLS_API 20141031 | ||
| 22 | |||
| 23 | #define TLS_PROTOCOL_TLSv1_0 (1 << 1) | ||
| 24 | #define TLS_PROTOCOL_TLSv1_1 (1 << 2) | ||
| 25 | #define TLS_PROTOCOL_TLSv1_2 (1 << 3) | ||
| 26 | #define TLS_PROTOCOL_TLSv1 \ | ||
| 27 | (TLS_PROTOCOL_TLSv1_0|TLS_PROTOCOL_TLSv1_1|TLS_PROTOCOL_TLSv1_2) | ||
| 28 | #define TLS_PROTOCOLS_DEFAULT TLS_PROTOCOL_TLSv1 | ||
| 29 | |||
| 30 | #define TLS_READ_AGAIN -2 | ||
| 31 | #define TLS_WRITE_AGAIN -3 | ||
| 32 | |||
| 33 | struct tls; | ||
| 34 | struct tls_config; | ||
| 35 | |||
| 36 | int tls_init(void); | ||
| 37 | |||
| 38 | const char *tls_error(struct tls *ctx); | ||
| 39 | |||
| 40 | struct tls_config *tls_config_new(void); | ||
| 41 | void tls_config_free(struct tls_config *config); | ||
| 42 | |||
| 43 | int tls_config_set_ca_file(struct tls_config *config, const char *ca_file); | ||
| 44 | int tls_config_set_ca_path(struct tls_config *config, const char *ca_path); | ||
| 45 | int tls_config_set_cert_file(struct tls_config *config, const char *cert_file); | ||
| 46 | int tls_config_set_cert_mem(struct tls_config *config, const uint8_t *cert, | ||
| 47 | size_t len); | ||
| 48 | int tls_config_set_ciphers(struct tls_config *config, const char *ciphers); | ||
| 49 | int tls_config_set_ecdhcurve(struct tls_config *config, const char *name); | ||
| 50 | int tls_config_set_key_file(struct tls_config *config, const char *key_file); | ||
| 51 | int tls_config_set_key_mem(struct tls_config *config, const uint8_t *key, | ||
| 52 | size_t len); | ||
| 53 | void tls_config_set_protocols(struct tls_config *config, uint32_t protocols); | ||
| 54 | void tls_config_set_verify_depth(struct tls_config *config, int verify_depth); | ||
| 55 | |||
| 56 | void tls_config_clear_keys(struct tls_config *config); | ||
| 57 | void tls_config_insecure_noverifyhost(struct tls_config *config); | ||
| 58 | void tls_config_insecure_noverifycert(struct tls_config *config); | ||
| 59 | void tls_config_verify(struct tls_config *config); | ||
| 60 | |||
| 61 | struct tls *tls_client(void); | ||
| 62 | struct tls *tls_server(void); | ||
| 63 | int tls_configure(struct tls *ctx, struct tls_config *config); | ||
| 64 | void tls_reset(struct tls *ctx); | ||
| 65 | void tls_free(struct tls *ctx); | ||
| 66 | |||
| 67 | int tls_accept_socket(struct tls *ctx, struct tls **cctx, int socket); | ||
| 68 | int tls_connect(struct tls *ctx, const char *host, const char *port); | ||
| 69 | int tls_connect_socket(struct tls *ctx, int s, const char *hostname); | ||
| 70 | int tls_read(struct tls *ctx, void *buf, size_t buflen, size_t *outlen); | ||
| 71 | int tls_write(struct tls *ctx, const void *buf, size_t buflen, size_t *outlen); | ||
| 72 | int tls_close(struct tls *ctx); | ||
| 73 | |||
| 74 | #endif /* HEADER_TLS_H */ | ||
diff --git a/src/lib/libtls/tls_client.c b/src/lib/libtls/tls_client.c new file mode 100644 index 0000000000..853766f87b --- /dev/null +++ b/src/lib/libtls/tls_client.c | |||
| @@ -0,0 +1,212 @@ | |||
| 1 | /* $OpenBSD: tls_client.c,v 1.1 2014/10/31 13:46:17 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 | union { struct in_addr ip4; struct in6_addr ip6; } addrbuf; | ||
| 127 | X509 *cert = NULL; | ||
| 128 | int ret; | ||
| 129 | |||
| 130 | if ((ctx->flags & TLS_CLIENT) == 0) { | ||
| 131 | tls_set_error(ctx, "not a client context"); | ||
| 132 | goto err; | ||
| 133 | } | ||
| 134 | |||
| 135 | ctx->socket = socket; | ||
| 136 | |||
| 137 | if ((ctx->ssl_ctx = SSL_CTX_new(SSLv23_client_method())) == NULL) { | ||
| 138 | tls_set_error(ctx, "ssl context failure"); | ||
| 139 | goto err; | ||
| 140 | } | ||
| 141 | |||
| 142 | if (tls_configure_ssl(ctx) != 0) | ||
| 143 | goto err; | ||
| 144 | |||
| 145 | if (ctx->config->verify_host) { | ||
| 146 | if (hostname == NULL) { | ||
| 147 | tls_set_error(ctx, "server name not specified"); | ||
| 148 | goto err; | ||
| 149 | } | ||
| 150 | } | ||
| 151 | |||
| 152 | if (ctx->config->verify_cert) { | ||
| 153 | SSL_CTX_set_verify(ctx->ssl_ctx, SSL_VERIFY_PEER, NULL); | ||
| 154 | |||
| 155 | if (SSL_CTX_load_verify_locations(ctx->ssl_ctx, | ||
| 156 | ctx->config->ca_file, ctx->config->ca_path) != 1) { | ||
| 157 | tls_set_error(ctx, "ssl verify setup failure"); | ||
| 158 | goto err; | ||
| 159 | } | ||
| 160 | if (ctx->config->verify_depth >= 0) | ||
| 161 | SSL_CTX_set_verify_depth(ctx->ssl_ctx, | ||
| 162 | ctx->config->verify_depth); | ||
| 163 | } | ||
| 164 | |||
| 165 | if ((ctx->ssl_conn = SSL_new(ctx->ssl_ctx)) == NULL) { | ||
| 166 | tls_set_error(ctx, "ssl connection failure"); | ||
| 167 | goto err; | ||
| 168 | } | ||
| 169 | if (SSL_set_fd(ctx->ssl_conn, ctx->socket) != 1) { | ||
| 170 | tls_set_error(ctx, "ssl file descriptor failure"); | ||
| 171 | goto err; | ||
| 172 | } | ||
| 173 | |||
| 174 | /* | ||
| 175 | * RFC4366 (SNI): Literal IPv4 and IPv6 addresses are not | ||
| 176 | * permitted in "HostName". | ||
| 177 | */ | ||
| 178 | if (hostname != NULL && | ||
| 179 | inet_pton(AF_INET, hostname, &addrbuf) != 1 && | ||
| 180 | inet_pton(AF_INET6, hostname, &addrbuf) != 1) { | ||
| 181 | if (SSL_set_tlsext_host_name(ctx->ssl_conn, hostname) == 0) { | ||
| 182 | tls_set_error(ctx, "SNI host name failed"); | ||
| 183 | goto err; | ||
| 184 | } | ||
| 185 | } | ||
| 186 | |||
| 187 | if ((ret = SSL_connect(ctx->ssl_conn)) != 1) { | ||
| 188 | tls_set_error(ctx, "SSL connect failed: %i", | ||
| 189 | SSL_get_error(ctx->ssl_conn, ret)); | ||
| 190 | goto err; | ||
| 191 | } | ||
| 192 | |||
| 193 | if (ctx->config->verify_host) { | ||
| 194 | cert = SSL_get_peer_certificate(ctx->ssl_conn); | ||
| 195 | if (cert == NULL) { | ||
| 196 | tls_set_error(ctx, "no server certificate"); | ||
| 197 | goto err; | ||
| 198 | } | ||
| 199 | if (tls_check_hostname(cert, hostname) != 0) { | ||
| 200 | tls_set_error(ctx, "host `%s' not present in" | ||
| 201 | " server certificate", hostname); | ||
| 202 | goto err; | ||
| 203 | } | ||
| 204 | } | ||
| 205 | |||
| 206 | return (0); | ||
| 207 | |||
| 208 | err: | ||
| 209 | X509_free(cert); | ||
| 210 | |||
| 211 | return (-1); | ||
| 212 | } | ||
diff --git a/src/lib/libtls/tls_config.c b/src/lib/libtls/tls_config.c new file mode 100644 index 0000000000..0e435f616a --- /dev/null +++ b/src/lib/libtls/tls_config.c | |||
| @@ -0,0 +1,201 @@ | |||
| 1 | /* $OpenBSD: tls_config.c,v 1.1 2014/10/31 13:46:17 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 <errno.h> | ||
| 19 | #include <stdlib.h> | ||
| 20 | |||
| 21 | #include <tls.h> | ||
| 22 | #include "tls_internal.h" | ||
| 23 | |||
| 24 | static int | ||
| 25 | set_string(const char **dest, const char *src) | ||
| 26 | { | ||
| 27 | free((char *)*dest); | ||
| 28 | *dest = NULL; | ||
| 29 | if (src != NULL) | ||
| 30 | if ((*dest = strdup(src)) == NULL) | ||
| 31 | return -1; | ||
| 32 | return 0; | ||
| 33 | } | ||
| 34 | |||
| 35 | static void * | ||
| 36 | memdup(const void *in, size_t len) | ||
| 37 | { | ||
| 38 | void *out; | ||
| 39 | |||
| 40 | if ((out = malloc(len)) == NULL) | ||
| 41 | return NULL; | ||
| 42 | memcpy(out, in, len); | ||
| 43 | return out; | ||
| 44 | } | ||
| 45 | |||
| 46 | static int | ||
| 47 | set_mem(char **dest, size_t *destlen, const void *src, size_t srclen) | ||
| 48 | { | ||
| 49 | free(*dest); | ||
| 50 | *dest = NULL; | ||
| 51 | *destlen = 0; | ||
| 52 | if (src != NULL) | ||
| 53 | if ((*dest = memdup(src, srclen)) == NULL) | ||
| 54 | return -1; | ||
| 55 | *destlen = srclen; | ||
| 56 | return 0; | ||
| 57 | } | ||
| 58 | |||
| 59 | struct tls_config * | ||
| 60 | tls_config_new(void) | ||
| 61 | { | ||
| 62 | struct tls_config *config; | ||
| 63 | |||
| 64 | if ((config = calloc(1, sizeof(*config))) == NULL) | ||
| 65 | return (NULL); | ||
| 66 | |||
| 67 | /* | ||
| 68 | * Default configuration. | ||
| 69 | */ | ||
| 70 | if (tls_config_set_ca_file(config, _PATH_SSL_CA_FILE) != 0) { | ||
| 71 | tls_config_free(config); | ||
| 72 | return (NULL); | ||
| 73 | } | ||
| 74 | tls_config_set_ecdhcurve(config, "auto"); | ||
| 75 | tls_config_set_protocols(config, TLS_PROTOCOLS_DEFAULT); | ||
| 76 | tls_config_set_verify_depth(config, 6); | ||
| 77 | |||
| 78 | tls_config_verify(config); | ||
| 79 | |||
| 80 | return (config); | ||
| 81 | } | ||
| 82 | |||
| 83 | void | ||
| 84 | tls_config_free(struct tls_config *config) | ||
| 85 | { | ||
| 86 | if (config == NULL) | ||
| 87 | return; | ||
| 88 | |||
| 89 | tls_config_clear_keys(config); | ||
| 90 | |||
| 91 | free((char *)config->ca_file); | ||
| 92 | free((char *)config->ca_path); | ||
| 93 | free((char *)config->cert_file); | ||
| 94 | free(config->cert_mem); | ||
| 95 | free((char *)config->ciphers); | ||
| 96 | free((char *)config->key_file); | ||
| 97 | free(config->key_mem); | ||
| 98 | |||
| 99 | free(config); | ||
| 100 | } | ||
| 101 | |||
| 102 | void | ||
| 103 | tls_config_clear_keys(struct tls_config *config) | ||
| 104 | { | ||
| 105 | tls_config_set_cert_mem(config, NULL, 0); | ||
| 106 | tls_config_set_key_mem(config, NULL, 0); | ||
| 107 | } | ||
| 108 | |||
| 109 | int | ||
| 110 | tls_config_set_ca_file(struct tls_config *config, const char *ca_file) | ||
| 111 | { | ||
| 112 | return set_string(&config->ca_file, ca_file); | ||
| 113 | } | ||
| 114 | |||
| 115 | int | ||
| 116 | tls_config_set_ca_path(struct tls_config *config, const char *ca_path) | ||
| 117 | { | ||
| 118 | return set_string(&config->ca_path, ca_path); | ||
| 119 | } | ||
| 120 | |||
| 121 | int | ||
| 122 | tls_config_set_cert_file(struct tls_config *config, const char *cert_file) | ||
| 123 | { | ||
| 124 | return set_string(&config->cert_file, cert_file); | ||
| 125 | } | ||
| 126 | |||
| 127 | int | ||
| 128 | tls_config_set_cert_mem(struct tls_config *config, const uint8_t *cert, | ||
| 129 | size_t len) | ||
| 130 | { | ||
| 131 | return set_mem(&config->cert_mem, &config->cert_len, cert, len); | ||
| 132 | } | ||
| 133 | |||
| 134 | int | ||
| 135 | tls_config_set_ciphers(struct tls_config *config, const char *ciphers) | ||
| 136 | { | ||
| 137 | return set_string(&config->ciphers, ciphers); | ||
| 138 | } | ||
| 139 | |||
| 140 | int | ||
| 141 | tls_config_set_ecdhcurve(struct tls_config *config, const char *name) | ||
| 142 | { | ||
| 143 | int nid; | ||
| 144 | |||
| 145 | if (name == NULL) | ||
| 146 | nid = NID_undef; | ||
| 147 | else if (strcasecmp(name, "auto") == 0) | ||
| 148 | nid = -1; | ||
| 149 | else if ((nid = OBJ_txt2nid(name)) == NID_undef) | ||
| 150 | return (-1); | ||
| 151 | |||
| 152 | config->ecdhcurve = nid; | ||
| 153 | |||
| 154 | return (0); | ||
| 155 | } | ||
| 156 | |||
| 157 | int | ||
| 158 | tls_config_set_key_file(struct tls_config *config, const char *key_file) | ||
| 159 | { | ||
| 160 | return set_string(&config->key_file, key_file); | ||
| 161 | } | ||
| 162 | |||
| 163 | int | ||
| 164 | tls_config_set_key_mem(struct tls_config *config, const uint8_t *key, | ||
| 165 | size_t len) | ||
| 166 | { | ||
| 167 | if (config->key_mem) | ||
| 168 | explicit_bzero(config->key_mem, config->key_len); | ||
| 169 | return set_mem(&config->key_mem, &config->key_len, key, len); | ||
| 170 | } | ||
| 171 | |||
| 172 | void | ||
| 173 | tls_config_set_protocols(struct tls_config *config, uint32_t protocols) | ||
| 174 | { | ||
| 175 | config->protocols = protocols; | ||
| 176 | } | ||
| 177 | |||
| 178 | void | ||
| 179 | tls_config_set_verify_depth(struct tls_config *config, int verify_depth) | ||
| 180 | { | ||
| 181 | config->verify_depth = verify_depth; | ||
| 182 | } | ||
| 183 | |||
| 184 | void | ||
| 185 | tls_config_insecure_noverifyhost(struct tls_config *config) | ||
| 186 | { | ||
| 187 | config->verify_host = 0; | ||
| 188 | } | ||
| 189 | |||
| 190 | void | ||
| 191 | tls_config_insecure_noverifycert(struct tls_config *config) | ||
| 192 | { | ||
| 193 | config->verify_cert = 0; | ||
| 194 | } | ||
| 195 | |||
| 196 | void | ||
| 197 | tls_config_verify(struct tls_config *config) | ||
| 198 | { | ||
| 199 | config->verify_host = 1; | ||
| 200 | config->verify_cert = 1; | ||
| 201 | } | ||
diff --git a/src/lib/libtls/tls_init.3 b/src/lib/libtls/tls_init.3 new file mode 100644 index 0000000000..faa9b99539 --- /dev/null +++ b/src/lib/libtls/tls_init.3 | |||
| @@ -0,0 +1,316 @@ | |||
| 1 | .\" $OpenBSD: tls_init.3,v 1.1 2014/10/31 13:46:17 jsing Exp $ | ||
| 2 | .\" | ||
| 3 | .\" Copyright (c) 2014 Ted Unangst <tedu@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 | .Dd $Mdocdate: October 31 2014 $ | ||
| 18 | .Dt TLS 3 | ||
| 19 | .Os | ||
| 20 | .Sh NAME | ||
| 21 | .Nm tls_init , | ||
| 22 | .Nm tls_error , | ||
| 23 | .Nm tls_config_new , | ||
| 24 | .Nm tls_config_free , | ||
| 25 | .Nm tls_config_set_ca_file , | ||
| 26 | .Nm tls_config_set_ca_path , | ||
| 27 | .Nm tls_config_set_cert_file , | ||
| 28 | .Nm tls_config_set_cert_mem , | ||
| 29 | .Nm tls_config_set_ciphers , | ||
| 30 | .Nm tls_config_set_ecdhcurve , | ||
| 31 | .Nm tls_config_set_key_file , | ||
| 32 | .Nm tls_config_set_key_mem , | ||
| 33 | .Nm tls_config_set_protocols , | ||
| 34 | .Nm tls_config_set_verify_depth , | ||
| 35 | .Nm tls_config_clear_keys , | ||
| 36 | .Nm tls_config_insecure_noverifyhost , | ||
| 37 | .Nm tls_config_insecure_noverifycert , | ||
| 38 | .Nm tls_config_verify , | ||
| 39 | .Nm tls_client , | ||
| 40 | .Nm tls_server , | ||
| 41 | .Nm tls_configure , | ||
| 42 | .Nm tls_reset , | ||
| 43 | .Nm tls_close , | ||
| 44 | .Nm tls_free , | ||
| 45 | .Nm tls_connect , | ||
| 46 | .Nm tls_connect_socket , | ||
| 47 | .Nm tls_read , | ||
| 48 | .Nm tls_write , | ||
| 49 | .Nd tls TLS client and server API | ||
| 50 | .Sh SYNOPSIS | ||
| 51 | .In tls.h | ||
| 52 | .Ft "int" | ||
| 53 | .Fn tls_init "void" | ||
| 54 | .Ft "const char *" | ||
| 55 | .Fn tls_error "struct tls *ctx" | ||
| 56 | .Ft "struct tls_config *" | ||
| 57 | .Fn tls_config_new "void" | ||
| 58 | .Ft "void" | ||
| 59 | .Fn tls_config_free "struct tls_config *config" | ||
| 60 | .Ft "int" | ||
| 61 | .Fn tls_config_set_ca_file "struct tls_config *config" "const char *ca_file" | ||
| 62 | .Ft "int" | ||
| 63 | .Fn tls_config_set_ca_path "struct tls_config *config" "const char *ca_path" | ||
| 64 | .Ft "int" | ||
| 65 | .Fn tls_config_set_cert_file "struct tls_config *config" "const char *cert_file" | ||
| 66 | .Ft "int" | ||
| 67 | .Fn tls_config_set_cert_mem "struct tls_config *config" "const uint8_t *cert" "size_t len" | ||
| 68 | .Ft "int" | ||
| 69 | .Fn tls_config_set_ciphers "struct tls_config *config" "const char *ciphers" | ||
| 70 | .Ft "int" | ||
| 71 | .Fn tls_config_set_ecdhcurve "struct tls_config *config" "const char *name" | ||
| 72 | .Ft "int" | ||
| 73 | .Fn tls_config_set_key_file "struct tls_config *config" "const char *key_file" | ||
| 74 | .Ft "int" | ||
| 75 | .Fn tls_config_set_key_mem "struct tls_config *config" "const uint8_t *key" "size_t len" | ||
| 76 | .Ft "int" | ||
| 77 | .Fn tls_config_set_protocols "struct tls_config *config" "uint32_t protocols" | ||
| 78 | .Ft "int" | ||
| 79 | .Fn tls_config_set_verify_depth "struct tls_config *config" "int verify_depth" | ||
| 80 | .Ft "void" | ||
| 81 | .Fn tls_config_clear_keys "struct tls_config *config" | ||
| 82 | .Ft "void" | ||
| 83 | .Fn tls_config_insecure_noverifyhost "struct tls_config *config" | ||
| 84 | .Ft "void" | ||
| 85 | .Fn tls_config_insecure_noverifycert "struct tls_config *config" | ||
| 86 | .Ft "void" | ||
| 87 | .Fn tls_config_verify "struct tls_config *config" | ||
| 88 | .Ft "struct tls *" | ||
| 89 | .Fn tls_client void | ||
| 90 | .Ft "struct tls *" | ||
| 91 | .Fn tls_server void | ||
| 92 | .Ft "int" | ||
| 93 | .Fn tls_configure "struct tls *ctx" "struct tls_config *config" | ||
| 94 | .Ft "void" | ||
| 95 | .Fn tls_reset "struct tls *ctx" | ||
| 96 | .Ft "int" | ||
| 97 | .Fn tls_close "struct tls *ctx" | ||
| 98 | .Ft "void" | ||
| 99 | .Fn tls_free "struct tls *ctx" | ||
| 100 | .Ft "int" | ||
| 101 | .Fn tls_connect "struct tls *ctx" "const char *host" "const char *port" | ||
| 102 | .Ft "int" | ||
| 103 | .Fn tls_connect_socket "struct tls *ctx" "int s" "const char *hostname" | ||
| 104 | .Ft "int" | ||
| 105 | .Fn tls_read "struct tls *ctx" "void *buf" "size_t buflen" "size_t *outlen" | ||
| 106 | .Ft "int" | ||
| 107 | .Fn tls_write "struct tls *ctx" "const void *buf" "size_t buflen" | ||
| 108 | .Sh DESCRIPTION | ||
| 109 | The | ||
| 110 | .Nm tls | ||
| 111 | family of functions establishes a secure communications channel | ||
| 112 | using the TLS socket protocol. | ||
| 113 | Both clients and servers are supported. | ||
| 114 | .Pp | ||
| 115 | The | ||
| 116 | .Fn tls_init | ||
| 117 | function should be called once before any function is used. | ||
| 118 | .Pp | ||
| 119 | Before a connection is created, a configuration must be created. | ||
| 120 | The | ||
| 121 | .Fn tls_config_new | ||
| 122 | function returns a new default configuration that can be used for future | ||
| 123 | connections. | ||
| 124 | Several functions exist to change the options of the configuration; see below. | ||
| 125 | .Pp | ||
| 126 | A | ||
| 127 | .Em tls | ||
| 128 | connection is represented as a | ||
| 129 | .Em context . | ||
| 130 | A new | ||
| 131 | .Em context | ||
| 132 | is created by either the | ||
| 133 | .Fn tls_client | ||
| 134 | or | ||
| 135 | .Fn tls_server | ||
| 136 | functions. | ||
| 137 | The context can then be configured with the function | ||
| 138 | .Fn tls_configure . | ||
| 139 | The same | ||
| 140 | .Em tls_config | ||
| 141 | object can be used to configure multiple contexts. | ||
| 142 | .Pp | ||
| 143 | A client connection is initiated after configuration by calling | ||
| 144 | .Fn tls_connect . | ||
| 145 | This function will create a new socket, connect to the specified host and | ||
| 146 | port, and then establish a secure connection. | ||
| 147 | An already existing socket can be upgraded to a secure connection by calling | ||
| 148 | .Fn tls_connect_socket . | ||
| 149 | .Pp | ||
| 150 | Two functions are provided for input and output, | ||
| 151 | .Fn tls_read | ||
| 152 | and | ||
| 153 | .Fn tls_write . | ||
| 154 | .Pp | ||
| 155 | After use, a tls | ||
| 156 | .Em context | ||
| 157 | should be closed with | ||
| 158 | .Fn tls_close , | ||
| 159 | and then freed by calling | ||
| 160 | .Fn tls_free . | ||
| 161 | When no more contexts are to be created, the | ||
| 162 | .Em tls_config | ||
| 163 | object should be freed by calling | ||
| 164 | .Fn tls_config_free . | ||
| 165 | .Sh FUNCTIONS | ||
| 166 | The | ||
| 167 | .Fn tls_init | ||
| 168 | function initializes global data structures. | ||
| 169 | It should be called once before any other functions. | ||
| 170 | .Pp | ||
| 171 | The following functions create and free configuration objects. | ||
| 172 | .Bl -bullet -offset four | ||
| 173 | .It | ||
| 174 | .Fn tls_config_new | ||
| 175 | allocates a new default configuration object. | ||
| 176 | .It | ||
| 177 | .Fn tls_config_free | ||
| 178 | frees a configuration object. | ||
| 179 | .El | ||
| 180 | .Pp | ||
| 181 | The following functions modify a configuration by setting parameters. | ||
| 182 | Configuration options may apply to only clients or only servers or both. | ||
| 183 | .Bl -bullet -offset four | ||
| 184 | .It | ||
| 185 | .Fn tls_config_set_ca_file | ||
| 186 | sets the filename used to load a file | ||
| 187 | containing the root certificates. | ||
| 188 | .Em (Client) | ||
| 189 | .It | ||
| 190 | .Fn tls_config_set_ca_path | ||
| 191 | sets the path (directory) which should be searched for root | ||
| 192 | certificates. | ||
| 193 | .Em (Client) | ||
| 194 | .It | ||
| 195 | .Fn tls_config_set_cert_file | ||
| 196 | sets file from which the public certificate will be read. | ||
| 197 | .Em (Client and server) | ||
| 198 | .It | ||
| 199 | .Fn tls_config_set_cert_mem | ||
| 200 | sets the public certificate directly from memory. | ||
| 201 | .Em (Client and server) | ||
| 202 | .It | ||
| 203 | .Fn tls_config_set_ciphers | ||
| 204 | sets the list of ciphers that may be used. | ||
| 205 | .Em (Client and server) | ||
| 206 | .It | ||
| 207 | .Fn tls_config_set_key_file | ||
| 208 | sets the file from which the private key will be read. | ||
| 209 | .Em (Server) | ||
| 210 | .It | ||
| 211 | .Fn tls_config_set_key_mem | ||
| 212 | directly sets the private key from memory. | ||
| 213 | .Em (Server) | ||
| 214 | .It | ||
| 215 | .Fn tls_config_set_protocols | ||
| 216 | sets which versions of the protocol may be used. | ||
| 217 | Possible values are the bitwise OR of: | ||
| 218 | .Pp | ||
| 219 | .Bl -tag -width "TLS_PROTOCOL_TLSv1_2" -offset indent -compact | ||
| 220 | .It Dv TLS_PROTOCOL_TLSv1_0 | ||
| 221 | .It Dv TLS_PROTOCOL_TLSv1_1 | ||
| 222 | .It Dv TLS_PROTOCOL_TLSv1_2 | ||
| 223 | .El | ||
| 224 | .Pp | ||
| 225 | Additionally, the values | ||
| 226 | .Dv TLS_PROTOCOL_TLSv1 | ||
| 227 | (all TLS versions) and | ||
| 228 | .Dv TLS_PROTOCOLS_DEFAULT | ||
| 229 | (currently all TLS versions) may be used. | ||
| 230 | .Em (Client and server) | ||
| 231 | .It | ||
| 232 | .Fn tls_config_clear_keys | ||
| 233 | clears any secret keys from memory. | ||
| 234 | .Em (Server) | ||
| 235 | .It | ||
| 236 | .Fn tls_config_insecure_noverifyhost | ||
| 237 | disables hostname verification. | ||
| 238 | Be careful when using this option. | ||
| 239 | .Em (Client) | ||
| 240 | .It | ||
| 241 | .Fn tls_config_insecure_noverifycert | ||
| 242 | disables certificate verification. | ||
| 243 | Be extremely careful when using this option. | ||
| 244 | .Em (Client) | ||
| 245 | .It | ||
| 246 | .Fn tls_config_verify | ||
| 247 | reenables hostname and certificate verification. | ||
| 248 | .Em (Client) | ||
| 249 | .El | ||
| 250 | .Pp | ||
| 251 | The following functions create, prepare, and free a connection context. | ||
| 252 | .Bl -bullet -offset four | ||
| 253 | .It | ||
| 254 | .Fn tls_client | ||
| 255 | creates a new tls context for client connections. | ||
| 256 | .It | ||
| 257 | .Fn tls_server | ||
| 258 | creates a new tls context for server connections. | ||
| 259 | .It | ||
| 260 | .Fn tls_configure | ||
| 261 | readies a tls context for use by applying the configuration | ||
| 262 | options. | ||
| 263 | .It | ||
| 264 | .Fn tls_close | ||
| 265 | closes a connection after use. | ||
| 266 | .It | ||
| 267 | .Fn tls_free | ||
| 268 | frees a tls context after use. | ||
| 269 | .El | ||
| 270 | .Pp | ||
| 271 | The following functions initiate a connection and perform input and output | ||
| 272 | operations. | ||
| 273 | .Bl -bullet -offset four | ||
| 274 | .It | ||
| 275 | .Fn tls_connect | ||
| 276 | connects a client context to the server named by | ||
| 277 | .Fa host . | ||
| 278 | The | ||
| 279 | .Fa port | ||
| 280 | may be numeric or a service name. | ||
| 281 | If it is NULL then a host of the format "hostname:port" is permitted. | ||
| 282 | .It | ||
| 283 | .Fn tls_connect_socket | ||
| 284 | connects a client context to an already established socket connection. | ||
| 285 | .It | ||
| 286 | .Fn tls_read | ||
| 287 | reads | ||
| 288 | .Fa buflen | ||
| 289 | bytes of data from the socket into | ||
| 290 | .Fa buf . | ||
| 291 | The amount of data read is returned in | ||
| 292 | .Fa outlen . | ||
| 293 | .It | ||
| 294 | .Fn tls_write | ||
| 295 | writes | ||
| 296 | .Fa buflen | ||
| 297 | bytes of data from | ||
| 298 | .Fa buf | ||
| 299 | to the socket. | ||
| 300 | The amount of data written is returned in | ||
| 301 | .Fa outlen . | ||
| 302 | .El | ||
| 303 | .Sh RETURN VALUES | ||
| 304 | Functions that return | ||
| 305 | .Vt int | ||
| 306 | will return 0 on success and -1 on error. | ||
| 307 | Functions that return a pointer will return NULL on error. | ||
| 308 | .\" .Sh ERRORS | ||
| 309 | .\" .Sh SEE ALSO | ||
| 310 | .Sh HISTORY | ||
| 311 | The | ||
| 312 | .Nm tls | ||
| 313 | API first appeared in | ||
| 314 | .Ox 5.6 | ||
| 315 | as a response to the unnecessary challenges other APIs present in | ||
| 316 | order to use them safely. | ||
diff --git a/src/lib/libtls/tls_internal.h b/src/lib/libtls/tls_internal.h new file mode 100644 index 0000000000..da696e228d --- /dev/null +++ b/src/lib/libtls/tls_internal.h | |||
| @@ -0,0 +1,72 @@ | |||
| 1 | /* $OpenBSD: tls_internal.h,v 1.1 2014/10/31 13:46:17 jsing Exp $ */ | ||
| 2 | /* | ||
| 3 | * Copyright (c) 2014 Jeremie Courreges-Anglas <jca@openbsd.org> | ||
| 4 | * Copyright (c) 2014 Joel Sing <jsing@openbsd.org> | ||
| 5 | * | ||
| 6 | * Permission to use, copy, modify, and distribute this software for any | ||
| 7 | * purpose with or without fee is hereby granted, provided that the above | ||
| 8 | * copyright notice and this permission notice appear in all copies. | ||
| 9 | * | ||
| 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | ||
| 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | ||
| 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | ||
| 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | ||
| 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | ||
| 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | ||
| 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | ||
| 17 | */ | ||
| 18 | |||
| 19 | #ifndef HEADER_TLS_INTERNAL_H | ||
| 20 | #define HEADER_TLS_INTERNAL_H | ||
| 21 | |||
| 22 | #include <openssl/ssl.h> | ||
| 23 | |||
| 24 | #define HTTPS_PORT "443" | ||
| 25 | |||
| 26 | #define _PATH_SSL_CA_FILE "/etc/ssl/cert.pem" | ||
| 27 | |||
| 28 | struct tls_config { | ||
| 29 | const char *ca_file; | ||
| 30 | const char *ca_path; | ||
| 31 | const char *cert_file; | ||
| 32 | char *cert_mem; | ||
| 33 | size_t cert_len; | ||
| 34 | const char *ciphers; | ||
| 35 | int ecdhcurve; | ||
| 36 | const char *key_file; | ||
| 37 | char *key_mem; | ||
| 38 | size_t key_len; | ||
| 39 | uint32_t protocols; | ||
| 40 | int verify_cert; | ||
| 41 | int verify_host; | ||
| 42 | int verify_depth; | ||
| 43 | }; | ||
| 44 | |||
| 45 | #define TLS_CLIENT (1 << 0) | ||
| 46 | #define TLS_SERVER (1 << 1) | ||
| 47 | #define TLS_SERVER_CONN (1 << 2) | ||
| 48 | |||
| 49 | struct tls { | ||
| 50 | struct tls_config *config; | ||
| 51 | uint64_t flags; | ||
| 52 | |||
| 53 | int err; | ||
| 54 | char *errmsg; | ||
| 55 | |||
| 56 | int socket; | ||
| 57 | |||
| 58 | SSL *ssl_conn; | ||
| 59 | SSL_CTX *ssl_ctx; | ||
| 60 | }; | ||
| 61 | |||
| 62 | struct tls *tls_new(void); | ||
| 63 | struct tls *tls_server_conn(struct tls *ctx); | ||
| 64 | |||
| 65 | int tls_check_hostname(X509 *cert, const char *host); | ||
| 66 | int tls_configure_keypair(struct tls *ctx); | ||
| 67 | int tls_configure_server(struct tls *ctx); | ||
| 68 | int tls_configure_ssl(struct tls *ctx); | ||
| 69 | int tls_host_port(const char *hostport, char **host, char **port); | ||
| 70 | int tls_set_error(struct tls *ctx, char *fmt, ...); | ||
| 71 | |||
| 72 | #endif /* HEADER_TLS_INTERNAL_H */ | ||
diff --git a/src/lib/libtls/tls_server.c b/src/lib/libtls/tls_server.c new file mode 100644 index 0000000000..001f19ded4 --- /dev/null +++ b/src/lib/libtls/tls_server.c | |||
| @@ -0,0 +1,134 @@ | |||
| 1 | /* $OpenBSD: tls_server.c,v 1.1 2014/10/31 13:46:17 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 <openssl/ec.h> | ||
| 19 | #include <openssl/ssl.h> | ||
| 20 | |||
| 21 | #include <tls.h> | ||
| 22 | #include "tls_internal.h" | ||
| 23 | |||
| 24 | struct tls * | ||
| 25 | tls_server(void) | ||
| 26 | { | ||
| 27 | struct tls *ctx; | ||
| 28 | |||
| 29 | if ((ctx = tls_new()) == NULL) | ||
| 30 | return (NULL); | ||
| 31 | |||
| 32 | ctx->flags |= TLS_SERVER; | ||
| 33 | |||
| 34 | return (ctx); | ||
| 35 | } | ||
| 36 | |||
| 37 | struct tls * | ||
| 38 | tls_server_conn(struct tls *ctx) | ||
| 39 | { | ||
| 40 | struct tls *conn_ctx; | ||
| 41 | |||
| 42 | if ((conn_ctx = tls_new()) == NULL) | ||
| 43 | return (NULL); | ||
| 44 | |||
| 45 | conn_ctx->flags |= TLS_SERVER_CONN; | ||
| 46 | |||
| 47 | return (conn_ctx); | ||
| 48 | } | ||
| 49 | |||
| 50 | int | ||
| 51 | tls_configure_server(struct tls *ctx) | ||
| 52 | { | ||
| 53 | EC_KEY *ecdh_key; | ||
| 54 | |||
| 55 | if ((ctx->ssl_ctx = SSL_CTX_new(SSLv23_server_method())) == NULL) { | ||
| 56 | tls_set_error(ctx, "ssl context failure"); | ||
| 57 | goto err; | ||
| 58 | } | ||
| 59 | |||
| 60 | if (tls_configure_ssl(ctx) != 0) | ||
| 61 | goto err; | ||
| 62 | if (tls_configure_keypair(ctx) != 0) | ||
| 63 | goto err; | ||
| 64 | |||
| 65 | if (ctx->config->ecdhcurve == -1) { | ||
| 66 | SSL_CTX_set_ecdh_auto(ctx->ssl_ctx, 1); | ||
| 67 | } else if (ctx->config->ecdhcurve != NID_undef) { | ||
| 68 | if ((ecdh_key = EC_KEY_new_by_curve_name( | ||
| 69 | ctx->config->ecdhcurve)) == NULL) { | ||
| 70 | tls_set_error(ctx, "failed to set ECDH curve"); | ||
| 71 | goto err; | ||
| 72 | } | ||
| 73 | SSL_CTX_set_options(ctx->ssl_ctx, SSL_OP_SINGLE_ECDH_USE); | ||
| 74 | SSL_CTX_set_tmp_ecdh(ctx->ssl_ctx, ecdh_key); | ||
| 75 | EC_KEY_free(ecdh_key); | ||
| 76 | } | ||
| 77 | |||
| 78 | return (0); | ||
| 79 | |||
| 80 | err: | ||
| 81 | return (-1); | ||
| 82 | } | ||
| 83 | |||
| 84 | int | ||
| 85 | tls_accept_socket(struct tls *ctx, struct tls **cctx, int socket) | ||
| 86 | { | ||
| 87 | struct tls *conn_ctx = *cctx; | ||
| 88 | int ret, ssl_err; | ||
| 89 | |||
| 90 | if ((ctx->flags & TLS_SERVER) == 0) { | ||
| 91 | tls_set_error(ctx, "not a server context"); | ||
| 92 | goto err; | ||
| 93 | } | ||
| 94 | |||
| 95 | if (conn_ctx == NULL) { | ||
| 96 | if ((conn_ctx = tls_server_conn(ctx)) == NULL) { | ||
| 97 | tls_set_error(ctx, "connection context failure"); | ||
| 98 | goto err; | ||
| 99 | } | ||
| 100 | *cctx = conn_ctx; | ||
| 101 | |||
| 102 | conn_ctx->socket = socket; | ||
| 103 | |||
| 104 | if ((conn_ctx->ssl_conn = SSL_new(ctx->ssl_ctx)) == NULL) { | ||
| 105 | tls_set_error(ctx, "ssl failure"); | ||
| 106 | goto err; | ||
| 107 | } | ||
| 108 | |||
| 109 | if (SSL_set_fd(conn_ctx->ssl_conn, socket) != 1) { | ||
| 110 | tls_set_error(ctx, "ssl set fd failure"); | ||
| 111 | goto err; | ||
| 112 | } | ||
| 113 | SSL_set_app_data(conn_ctx->ssl_conn, conn_ctx); | ||
| 114 | } | ||
| 115 | |||
| 116 | if ((ret = SSL_accept(conn_ctx->ssl_conn)) != 1) { | ||
| 117 | ssl_err = SSL_get_error(conn_ctx->ssl_conn, ret); | ||
| 118 | switch (ssl_err) { | ||
| 119 | case SSL_ERROR_WANT_READ: | ||
| 120 | return (TLS_READ_AGAIN); | ||
| 121 | case SSL_ERROR_WANT_WRITE: | ||
| 122 | return (TLS_WRITE_AGAIN); | ||
| 123 | default: | ||
| 124 | tls_set_error(ctx, "ssl accept failure (%i)", | ||
| 125 | ssl_err); | ||
| 126 | goto err; | ||
| 127 | } | ||
| 128 | } | ||
| 129 | |||
| 130 | return (0); | ||
| 131 | |||
| 132 | err: | ||
| 133 | return (-1); | ||
| 134 | } | ||
diff --git a/src/lib/libtls/tls_util.c b/src/lib/libtls/tls_util.c new file mode 100644 index 0000000000..2adfb674b8 --- /dev/null +++ b/src/lib/libtls/tls_util.c | |||
| @@ -0,0 +1,81 @@ | |||
| 1 | /* $OpenBSD: tls_util.c,v 1.1 2014/10/31 13:46:17 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 <stdlib.h> | ||
| 19 | |||
| 20 | #include "tls_internal.h" | ||
| 21 | |||
| 22 | /* | ||
| 23 | * Extract the host and port from a colon separated value. For a literal IPv6 | ||
| 24 | * address the address must be contained with square braces. If a host and | ||
| 25 | * port are successfully extracted, the function will return 0 and the | ||
| 26 | * caller is responsible for freeing the host and port. If no port is found | ||
| 27 | * then the function will return 1, with both host and port being NULL. | ||
| 28 | * On memory allocation failure -1 will be returned. | ||
| 29 | */ | ||
| 30 | int | ||
| 31 | tls_host_port(const char *hostport, char **host, char **port) | ||
| 32 | { | ||
| 33 | char *h, *p, *s; | ||
| 34 | int rv = 1; | ||
| 35 | |||
| 36 | *host = NULL; | ||
| 37 | *port = NULL; | ||
| 38 | |||
| 39 | if ((s = strdup(hostport)) == NULL) | ||
| 40 | goto fail; | ||
| 41 | |||
| 42 | h = p = s; | ||
| 43 | |||
| 44 | /* See if this is an IPv6 literal with square braces. */ | ||
| 45 | if (p[0] == '[') { | ||
| 46 | h++; | ||
| 47 | if ((p = strchr(s, ']')) == NULL) | ||
| 48 | goto done; | ||
| 49 | *p++ = '\0'; | ||
| 50 | } | ||
| 51 | |||
| 52 | /* Find the port seperator. */ | ||
| 53 | if ((p = strchr(p, ':')) == NULL) | ||
| 54 | goto done; | ||
| 55 | |||
| 56 | /* If there is another separator then we have issues. */ | ||
| 57 | if (strchr(p + 1, ':') != NULL) | ||
| 58 | goto done; | ||
| 59 | |||
| 60 | *p++ = '\0'; | ||
| 61 | |||
| 62 | if (asprintf(host, "%s", h) == -1) | ||
| 63 | goto fail; | ||
| 64 | if (asprintf(port, "%s", p) == -1) | ||
| 65 | goto fail; | ||
| 66 | |||
| 67 | rv = 0; | ||
| 68 | goto done; | ||
| 69 | |||
| 70 | fail: | ||
| 71 | free(*host); | ||
| 72 | *host = NULL; | ||
| 73 | free(*port); | ||
| 74 | *port = NULL; | ||
| 75 | rv = -1; | ||
| 76 | |||
| 77 | done: | ||
| 78 | free(s); | ||
| 79 | |||
| 80 | return (rv); | ||
| 81 | } | ||
diff --git a/src/lib/libtls/tls_verify.c b/src/lib/libtls/tls_verify.c new file mode 100644 index 0000000000..fa0010922f --- /dev/null +++ b/src/lib/libtls/tls_verify.c | |||
| @@ -0,0 +1,225 @@ | |||
| 1 | /* $OpenBSD: tls_verify.c,v 1.1 2014/10/31 13:46:17 jsing Exp $ */ | ||
| 2 | /* | ||
| 3 | * Copyright (c) 2014 Jeremie Courreges-Anglas <jca@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/socket.h> | ||
| 19 | |||
| 20 | #include <arpa/inet.h> | ||
| 21 | #include <netinet/in.h> | ||
| 22 | |||
| 23 | #include <string.h> | ||
| 24 | |||
| 25 | #include <openssl/x509v3.h> | ||
| 26 | |||
| 27 | #include "tls_internal.h" | ||
| 28 | |||
| 29 | int tls_match_hostname(const char *cert_hostname, const char *hostname); | ||
| 30 | int tls_check_subject_altname(X509 *cert, const char *host); | ||
| 31 | int tls_check_common_name(X509 *cert, const char *host); | ||
| 32 | |||
| 33 | int | ||
| 34 | tls_match_hostname(const char *cert_hostname, const char *hostname) | ||
| 35 | { | ||
| 36 | const char *cert_domain, *domain, *next_dot; | ||
| 37 | |||
| 38 | if (strcasecmp(cert_hostname, hostname) == 0) | ||
| 39 | return 0; | ||
| 40 | |||
| 41 | /* Wildcard match? */ | ||
| 42 | if (cert_hostname[0] == '*') { | ||
| 43 | /* | ||
| 44 | * Valid wildcards: | ||
| 45 | * - "*.domain.tld" | ||
| 46 | * - "*.sub.domain.tld" | ||
| 47 | * - etc. | ||
| 48 | * Reject "*.tld". | ||
| 49 | * No attempt to prevent the use of eg. "*.co.uk". | ||
| 50 | */ | ||
| 51 | cert_domain = &cert_hostname[1]; | ||
| 52 | /* Disallow "*" */ | ||
| 53 | if (cert_domain[0] == '\0') | ||
| 54 | return -1; | ||
| 55 | /* Disallow "*foo" */ | ||
| 56 | if (cert_domain[0] != '.') | ||
| 57 | return -1; | ||
| 58 | /* Disallow "*.." */ | ||
| 59 | if (cert_domain[1] == '.') | ||
| 60 | return -1; | ||
| 61 | next_dot = strchr(&cert_domain[1], '.'); | ||
| 62 | /* Disallow "*.bar" */ | ||
| 63 | if (next_dot == NULL) | ||
| 64 | return -1; | ||
| 65 | /* Disallow "*.bar.." */ | ||
| 66 | if (next_dot[1] == '.') | ||
| 67 | return -1; | ||
| 68 | |||
| 69 | domain = strchr(hostname, '.'); | ||
| 70 | |||
| 71 | /* No wildcard match against a hostname with no domain part. */ | ||
| 72 | if (domain == NULL || strlen(domain) == 1) | ||
| 73 | return -1; | ||
| 74 | |||
| 75 | if (strcasecmp(cert_domain, domain) == 0) | ||
| 76 | return 0; | ||
| 77 | } | ||
| 78 | |||
| 79 | return -1; | ||
| 80 | } | ||
| 81 | |||
| 82 | int | ||
| 83 | tls_check_subject_altname(X509 *cert, const char *host) | ||
| 84 | { | ||
| 85 | STACK_OF(GENERAL_NAME) *altname_stack = NULL; | ||
| 86 | union { struct in_addr ip4; struct in6_addr ip6; } addrbuf; | ||
| 87 | int addrlen, type; | ||
| 88 | int count, i; | ||
| 89 | int rv = -1; | ||
| 90 | |||
| 91 | altname_stack = X509_get_ext_d2i(cert, NID_subject_alt_name, | ||
| 92 | NULL, NULL); | ||
| 93 | if (altname_stack == NULL) | ||
| 94 | return -1; | ||
| 95 | |||
| 96 | if (inet_pton(AF_INET, host, &addrbuf) == 1) { | ||
| 97 | type = GEN_IPADD; | ||
| 98 | addrlen = 4; | ||
| 99 | } else if (inet_pton(AF_INET6, host, &addrbuf) == 1) { | ||
| 100 | type = GEN_IPADD; | ||
| 101 | addrlen = 16; | ||
| 102 | } else { | ||
| 103 | type = GEN_DNS; | ||
| 104 | addrlen = 0; | ||
| 105 | } | ||
| 106 | |||
| 107 | count = sk_GENERAL_NAME_num(altname_stack); | ||
| 108 | for (i = 0; i < count; i++) { | ||
| 109 | GENERAL_NAME *altname; | ||
| 110 | |||
| 111 | altname = sk_GENERAL_NAME_value(altname_stack, i); | ||
| 112 | |||
| 113 | if (altname->type != type) | ||
| 114 | continue; | ||
| 115 | |||
| 116 | if (type == GEN_DNS) { | ||
| 117 | unsigned char *data; | ||
| 118 | int format; | ||
| 119 | |||
| 120 | format = ASN1_STRING_type(altname->d.dNSName); | ||
| 121 | if (format == V_ASN1_IA5STRING) { | ||
| 122 | data = ASN1_STRING_data(altname->d.dNSName); | ||
| 123 | |||
| 124 | if (ASN1_STRING_length(altname->d.dNSName) != | ||
| 125 | (int)strlen(data)) { | ||
| 126 | fprintf(stdout, "%s: NUL byte in " | ||
| 127 | "subjectAltName, probably a " | ||
| 128 | "malicious certificate.\n", | ||
| 129 | getprogname()); | ||
| 130 | rv = -2; | ||
| 131 | break; | ||
| 132 | } | ||
| 133 | |||
| 134 | if (tls_match_hostname(data, host) == 0) { | ||
| 135 | rv = 0; | ||
| 136 | break; | ||
| 137 | } | ||
| 138 | } else | ||
| 139 | fprintf(stdout, "%s: unhandled subjectAltName " | ||
| 140 | "dNSName encoding (%d)\n", getprogname(), | ||
| 141 | format); | ||
| 142 | |||
| 143 | } else if (type == GEN_IPADD) { | ||
| 144 | unsigned char *data; | ||
| 145 | int datalen; | ||
| 146 | |||
| 147 | datalen = ASN1_STRING_length(altname->d.iPAddress); | ||
| 148 | data = ASN1_STRING_data(altname->d.iPAddress); | ||
| 149 | |||
| 150 | if (datalen == addrlen && | ||
| 151 | memcmp(data, &addrbuf, addrlen) == 0) { | ||
| 152 | rv = 0; | ||
| 153 | break; | ||
| 154 | } | ||
| 155 | } | ||
| 156 | } | ||
| 157 | |||
| 158 | sk_GENERAL_NAME_free(altname_stack); | ||
| 159 | return rv; | ||
| 160 | } | ||
| 161 | |||
| 162 | int | ||
| 163 | tls_check_common_name(X509 *cert, const char *host) | ||
| 164 | { | ||
| 165 | X509_NAME *name; | ||
| 166 | char *common_name = NULL; | ||
| 167 | int common_name_len; | ||
| 168 | int rv = -1; | ||
| 169 | union { struct in_addr ip4; struct in6_addr ip6; } addrbuf; | ||
| 170 | |||
| 171 | name = X509_get_subject_name(cert); | ||
| 172 | if (name == NULL) | ||
| 173 | goto out; | ||
| 174 | |||
| 175 | common_name_len = X509_NAME_get_text_by_NID(name, NID_commonName, | ||
| 176 | NULL, 0); | ||
| 177 | if (common_name_len < 0) | ||
| 178 | goto out; | ||
| 179 | |||
| 180 | common_name = calloc(common_name_len + 1, 1); | ||
| 181 | if (common_name == NULL) | ||
| 182 | goto out; | ||
| 183 | |||
| 184 | X509_NAME_get_text_by_NID(name, NID_commonName, common_name, | ||
| 185 | common_name_len + 1); | ||
| 186 | |||
| 187 | /* NUL bytes in CN? */ | ||
| 188 | if (common_name_len != (int)strlen(common_name)) { | ||
| 189 | fprintf(stdout, "%s: NUL byte in Common Name field, " | ||
| 190 | "probably a malicious certificate.\n", getprogname()); | ||
| 191 | rv = -2; | ||
| 192 | goto out; | ||
| 193 | } | ||
| 194 | |||
| 195 | if (inet_pton(AF_INET, host, &addrbuf) == 1 || | ||
| 196 | inet_pton(AF_INET6, host, &addrbuf) == 1) { | ||
| 197 | /* | ||
| 198 | * We don't want to attempt wildcard matching against IP | ||
| 199 | * addresses, so perform a simple comparison here. | ||
| 200 | */ | ||
| 201 | if (strcmp(common_name, host) == 0) | ||
| 202 | rv = 0; | ||
| 203 | else | ||
| 204 | rv = -1; | ||
| 205 | goto out; | ||
| 206 | } | ||
| 207 | |||
| 208 | if (tls_match_hostname(common_name, host) == 0) | ||
| 209 | rv = 0; | ||
| 210 | out: | ||
| 211 | free(common_name); | ||
| 212 | return rv; | ||
| 213 | } | ||
| 214 | |||
| 215 | int | ||
| 216 | tls_check_hostname(X509 *cert, const char *host) | ||
| 217 | { | ||
| 218 | int rv; | ||
| 219 | |||
| 220 | rv = tls_check_subject_altname(cert, host); | ||
| 221 | if (rv == 0 || rv == -2) | ||
| 222 | return rv; | ||
| 223 | |||
| 224 | return tls_check_common_name(cert, host); | ||
| 225 | } | ||
