From 2b0153d4f076d501b21de2e54937a5bb1b139635 Mon Sep 17 00:00:00 2001 From: jsing <> Date: Sat, 12 Jul 2014 01:20:25 +0000 Subject: 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. --- src/lib/libressl/Makefile | 28 ++++ src/lib/libressl/ressl.c | 335 ++++++++++++++++++++++++++++++++++++++ src/lib/libressl/ressl.h | 39 +++++ src/lib/libressl/ressl_config.c | 88 ++++++++++ src/lib/libressl/ressl_config.h | 33 ++++ src/lib/libressl/ressl_internal.h | 52 ++++++ src/lib/libressl/ressl_util.c | 80 +++++++++ src/lib/libressl/ressl_verify.c | 190 +++++++++++++++++++++ src/lib/libressl/shlib_version | 2 + 9 files changed, 847 insertions(+) create mode 100644 src/lib/libressl/Makefile create mode 100644 src/lib/libressl/ressl.c create mode 100644 src/lib/libressl/ressl.h create mode 100644 src/lib/libressl/ressl_config.c create mode 100644 src/lib/libressl/ressl_config.h create mode 100644 src/lib/libressl/ressl_internal.h create mode 100644 src/lib/libressl/ressl_util.c create mode 100644 src/lib/libressl/ressl_verify.c create mode 100644 src/lib/libressl/shlib_version diff --git a/src/lib/libressl/Makefile b/src/lib/libressl/Makefile new file mode 100644 index 0000000000..c6f4328b79 --- /dev/null +++ b/src/lib/libressl/Makefile @@ -0,0 +1,28 @@ +# $OpenBSD: Makefile,v 1.1 2014/07/12 01:20:24 jsing Exp $ + +CFLAGS+= -Wall -Werror -Wimplicit +CFLAGS+= -DLIBRESSL_INTERNAL + +LIB= ressl + +DPADD= ${LIBCRYPTO} ${LIBSSL} + +HDRS= ressl.h ressl_config.h + +SRCS= ressl.c \ + ressl_config.c \ + ressl_util.c \ + ressl_verify.c + +includes: + @test -d ${DESTDIR}/usr/include/ressl || \ + mkdir ${DESTDIR}/usr/include/ressl + @cd ${.CURDIR}; for i in $(HDRS); do \ + j="cmp -s $$i ${DESTDIR}/usr/include/ressl/$$i || \ + ${INSTALL} ${INSTALL_COPY} -o ${BINOWN} -g ${BINGRP} -m 444 $$i\ + ${DESTDIR}/usr/include/ressl"; \ + echo $$j; \ + eval "$$j"; \ + done; + +.include 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 @@ +/* + * Copyright (c) 2014 Joel Sing + * + * 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 +#include + +#include + +#include +#include +#include +#include + +#include + +#include + +#include "ressl_internal.h" + +extern struct ressl_config ressl_config_default; + +int +ressl_init(void) +{ + static int ressl_initialised = 0; + + if (ressl_initialised) + return (0); + + SSL_load_error_strings(); + SSL_library_init(); + + ressl_initialised = 1; + + return (0); +} + +const char * +ressl_error(struct ressl *ctx) +{ + return ctx->errmsg; +} + +int +ressl_set_error(struct ressl *ctx, char *fmt, ...) +{ + va_list ap; + int rv; + + ctx->err = errno; + free(ctx->errmsg); + ctx->errmsg = NULL; + + va_start(ap, fmt); + rv = vasprintf(&ctx->errmsg, fmt, ap); + va_end(ap); + + return (rv); +} + +struct ressl * +ressl_new(struct ressl_config *config) +{ + struct ressl *ctx; + + if ((ctx = calloc(1, sizeof(*ctx))) == NULL) + return (NULL); + + if (config == NULL) + config = &ressl_config_default; + + ctx->config = config; + + ressl_reset(ctx); + + return (ctx); +} + +void +ressl_free(struct ressl *ctx) +{ + if (ctx == NULL) + return; + ressl_reset(ctx); + free(ctx); +} + +void +ressl_reset(struct ressl *ctx) +{ + /* SSL_free frees the SSL context. */ + if (ctx->ssl_conn != NULL) + SSL_free(ctx->ssl_conn); + else + SSL_CTX_free(ctx->ssl_ctx); + + ctx->ssl_conn = NULL; + ctx->ssl_ctx = NULL; + + ctx->socket = -1; + + ctx->err = 0; + free(ctx->errmsg); + ctx->errmsg = NULL; +} + +int +ressl_connect(struct ressl *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 (host == NULL) { + ressl_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 = ressl_host_port(host, &hs, &ps); + if (ret == -1) { + ressl_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) { + ressl_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) { + ressl_set_error(ctx, "socket"); + continue; + } + if (connect(s, res->ai_addr, res->ai_addrlen) == -1) { + ressl_set_error(ctx, "connect"); + close(s); + s = -1; + continue; + } + + break; /* Connected. */ + } + freeaddrinfo(res0); + + if (s == -1) + goto err; + + if (ressl_connect_socket(ctx, s, h) != 0) { + close(s); + goto err; + } + + rv = 0; + +err: + + free(hs); + free(ps); + + return (rv); +} + +int +ressl_connect_socket(struct ressl *ctx, int socket, const char *hostname) +{ + union { struct in_addr ip4; struct in6_addr ip6; } addrbuf; + X509 *cert = NULL; + int ret; + + ctx->socket = socket; + + /* XXX - add a configuration option to control versions. */ + if ((ctx->ssl_ctx = SSL_CTX_new(SSLv23_client_method())) == NULL) { + ressl_set_error(ctx, "ssl context failure"); + goto err; + } + if (ctx->config->verify) { + if (hostname == NULL) { + ressl_set_error(ctx, "server name not specified"); + goto err; + } + + 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) { + ressl_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) { + ressl_set_error(ctx, "ssl connection failure"); + goto err; + } + if (SSL_set_fd(ctx->ssl_conn, ctx->socket) != 1) { + ressl_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) { + ressl_set_error(ctx, "SNI host name failed"); + goto err; + } + } + + if ((ret = SSL_connect(ctx->ssl_conn)) != 1) { + ressl_set_error(ctx, "SSL connect failed: %i", + SSL_get_error(ctx->ssl_conn, ret)); + goto err; + } + + if (ctx->config->verify) { + cert = SSL_get_peer_certificate(ctx->ssl_conn); + if (cert == NULL) { + ressl_set_error(ctx, "no server certificate"); + goto err; + } + if (ressl_check_hostname(cert, hostname) != 0) { + ressl_set_error(ctx, "host `%s' not present in" + " server certificate", hostname); + goto err; + } + } + + return (0); + +err: + X509_free(cert); + + return (-1); +} + +int +ressl_read(struct ressl *ctx, char *buf, size_t buflen, size_t *outlen) +{ + int ret; + + /* XXX - handle async/non-blocking. */ + ret = SSL_read(ctx->ssl_conn, buf, buflen); + if (ret <= 0) { + ret = SSL_get_error(ctx->ssl_conn, ret); + if (ret == SSL_ERROR_WANT_READ) + return (-2); + ressl_set_error(ctx, "read failed: %i", ret); + return (-1); + } + *outlen = (size_t)ret; + return (0); +} + +int +ressl_write(struct ressl *ctx, const char *buf, size_t buflen, size_t *outlen) +{ + int ret; + + /* XXX - handle async/non-blocking. */ + ret = SSL_write(ctx->ssl_conn, buf, buflen); + if (ret < 0) { + ressl_set_error(ctx, "write failed %d", + SSL_get_error(ctx->ssl_conn, ret)); + return (-1); + } + *outlen = (size_t)ret; + return (0); +} + +int +ressl_close(struct ressl *ctx) +{ + /* XXX - handle case where multiple calls are required. */ + if (ctx->ssl_conn != NULL) { + if (SSL_shutdown(ctx->ssl_conn) == -1) { + ressl_set_error(ctx, "SSL shutdown failed"); + goto err; + } + } + + if (ctx->socket != -1) { + if (shutdown(ctx->socket, SHUT_RDWR) != 0) { + ressl_set_error(ctx, "shutdown"); + goto err; + } + if (close(ctx->socket) != 0) { + ressl_set_error(ctx, "close"); + goto err; + } + ctx->socket = -1; + } + + return (0); + +err: + return (-1); +} diff --git a/src/lib/libressl/ressl.h b/src/lib/libressl/ressl.h new file mode 100644 index 0000000000..5217a69c15 --- /dev/null +++ b/src/lib/libressl/ressl.h @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2014 Joel Sing + * + * 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. + */ + +#ifndef HEADER_RESSL_H +#define HEADER_RESSL_H + +#include + +struct ressl; + +int ressl_init(void); + +const char *ressl_error(struct ressl *ctx); + +struct ressl *ressl_new(struct ressl_config *config); +void ressl_reset(struct ressl *ctx); +void ressl_free(struct ressl *ctx); + +int ressl_connect(struct ressl *ctx, const char *host, const char *port); +int ressl_connect_socket(struct ressl *ctx, int s, const char *hostname); +int ressl_read(struct ressl *ctx, char *buf, size_t buflen, size_t *outlen); +int ressl_write(struct ressl *ctx, const char *buf, size_t buflen, + size_t *outlen); +int ressl_close(struct ressl *ctx); + +#endif /* HEADER_RESSL_H */ diff --git a/src/lib/libressl/ressl_config.c b/src/lib/libressl/ressl_config.c new file mode 100644 index 0000000000..a84de591bc --- /dev/null +++ b/src/lib/libressl/ressl_config.c @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2014 Joel Sing + * + * 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 +#include + +#include + +#include "ressl_internal.h" + +/* + * Default configuration. + */ +struct ressl_config ressl_config_default = { + .ca_file = _PATH_SSL_CA_FILE, + .ca_path = NULL, + .ciphers = NULL, + .verify = 1, + .verify_depth = 6, +}; + +struct ressl_config * +ressl_config_new(void) +{ + struct ressl_config *config; + + if ((config = malloc(sizeof(*config))) == NULL) + return (NULL); + + memcpy(config, &ressl_config_default, sizeof(*config)); + + return (config); +} + +void +ressl_config_free(struct ressl_config *config) +{ + free(config); +} + +void +ressl_config_ca_file(struct ressl_config *config, char *ca_file) +{ + config->ca_file = ca_file; +} + +void +ressl_config_ca_path(struct ressl_config *config, char *ca_path) +{ + config->ca_path = ca_path; +} + +void +ressl_config_ciphers(struct ressl_config *config, char *ciphers) +{ + config->ciphers = ciphers; +} + +void +ressl_config_insecure(struct ressl_config *config) +{ + config->verify = 0; +} + +void +ressl_config_secure(struct ressl_config *config) +{ + config->verify = 1; +} + +void +ressl_config_verify_depth(struct ressl_config *config, int verify_depth) +{ + config->verify_depth = verify_depth; +} diff --git a/src/lib/libressl/ressl_config.h b/src/lib/libressl/ressl_config.h new file mode 100644 index 0000000000..da13d91efa --- /dev/null +++ b/src/lib/libressl/ressl_config.h @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2014 Joel Sing + * + * 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. + */ + +#ifndef HEADER_RESSL_CONFIG_H +#define HEADER_RESSL_CONFIG_H + +struct ressl_config; + +struct ressl_config *ressl_config_new(void); +void ressl_config_free(struct ressl_config *config); + +void ressl_config_ca_file(struct ressl_config *config, char *ca_file); +void ressl_config_ca_path(struct ressl_config *config, char *ca_path); +void ressl_config_ciphers(struct ressl_config *config, char *ciphers); +void ressl_config_verify_depth(struct ressl_config *config, int verify_depth); + +void ressl_config_insecure(struct ressl_config *config); +void ressl_config_secure(struct ressl_config *config); + +#endif /* HEADER_RESSL_H */ diff --git a/src/lib/libressl/ressl_internal.h b/src/lib/libressl/ressl_internal.h new file mode 100644 index 0000000000..f4eec10e63 --- /dev/null +++ b/src/lib/libressl/ressl_internal.h @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2014 Jeremie Courreges-Anglas + * Copyright (c) 2014 Joel Sing + * + * 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. + */ + +#ifndef HEADER_RESSL_INTERNAL_H +#define HEADER_RESSL_INTERNAL_H + +#include + +#define HTTPS_PORT "443" + +#define _PATH_SSL_CA_FILE "/etc/ssl/cert.pem" + +struct ressl_config { + const char *ca_file; + const char *ca_path; + const char *ciphers; + const char *server_name; + int verify; + int verify_depth; +}; + +struct ressl { + struct ressl_config *config; + + int err; + char *errmsg; + + int socket; + + SSL *ssl_conn; + SSL_CTX *ssl_ctx; +}; + +int ressl_check_hostname(X509 *cert, const char *host); +int ressl_host_port(const char *hostport, char **host, char **port); +int ressl_set_error(struct ressl *ctx, char *fmt, ...); + +#endif /* HEADER_RESSL_INTERNAL_H */ diff --git a/src/lib/libressl/ressl_util.c b/src/lib/libressl/ressl_util.c new file mode 100644 index 0000000000..ee7b1edbd3 --- /dev/null +++ b/src/lib/libressl/ressl_util.c @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2014 Joel Sing + * + * 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 + +#include "ressl_internal.h" + +/* + * Extract the host and port from a colon separated value. For a literal IPv6 + * address the address must be contained with square braces. If a host and + * port are successfully extracted, the function will return 0 and the + * caller is responsible for freeing the host and port. If no port is found + * then the function will return 1, with both host and port being NULL. + * On memory allocation failure -1 will be returned. + */ +int +ressl_host_port(const char *hostport, char **host, char **port) +{ + char *h, *p, *s; + int rv = 1; + + *host = NULL; + *port = NULL; + + if ((s = strdup(hostport)) == NULL) + goto fail; + + h = p = s; + + /* See if this is an IPv6 literal with square braces. */ + if (p[0] == '[') { + h++; + if ((p = strchr(s, ']')) == NULL) + goto done; + *p++ = '\0'; + } + + /* Find the port seperator. */ + if ((p = strchr(p, ':')) == NULL) + goto done; + + /* If there is another separator then we have issues. */ + if (strchr(p + 1, ':') != NULL) + goto done; + + *p++ = '\0'; + + if (asprintf(host, "%s", h) == -1) + goto fail; + if (asprintf(port, "%s", p) == -1) + goto fail; + + rv = 0; + goto done; + +fail: + free(*host); + *host = NULL; + free(*port); + *port = NULL; + rv = -1; + +done: + free(s); + + return (rv); +} diff --git a/src/lib/libressl/ressl_verify.c b/src/lib/libressl/ressl_verify.c new file mode 100644 index 0000000000..e98a264f4f --- /dev/null +++ b/src/lib/libressl/ressl_verify.c @@ -0,0 +1,190 @@ +/* + * Copyright (c) 2014 Jeremie Courreges-Anglas + * + * 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 + +#include +#include + +#include + +#include + +#include "ressl_internal.h" + +int ressl_match_hostname(const char *cert_hostname, const char *hostname); +int ressl_check_subject_altname(X509 *cert, const char *host); +int ressl_check_common_name(X509 *cert, const char *host); + +int +ressl_match_hostname(const char *cert_hostname, const char *hostname) +{ + const char *cert_domain, *domain; + + if (strcasecmp(cert_hostname, hostname) == 0) + return 0; + + /* Wildcard match? */ + if (cert_hostname[0] == '*') { + cert_domain = &cert_hostname[1]; + if (cert_domain[0] != '.') + return -1; + if (strlen(cert_domain) == 1) + return -1; + + domain = strchr(hostname, '.'); + + /* No wildcard match against a hostname with no domain part. */ + if (domain == NULL || strlen(domain) == 1) + return -1; + + if (strcasecmp(cert_domain, domain) == 0) + return 0; + } + + return -1; +} + +int +ressl_check_subject_altname(X509 *cert, const char *host) +{ + STACK_OF(GENERAL_NAME) *altname_stack = NULL; + union { struct in_addr ip4; struct in6_addr ip6; } addrbuf; + int addrlen, type; + int count, i; + int rv = -1; + + altname_stack = X509_get_ext_d2i(cert, NID_subject_alt_name, + NULL, NULL); + if (altname_stack == NULL) + return -1; + + if (inet_pton(AF_INET, host, &addrbuf) == 1) { + type = GEN_IPADD; + addrlen = 4; + } else if (inet_pton(AF_INET6, host, &addrbuf) == 1) { + type = GEN_IPADD; + addrlen = 16; + } else { + type = GEN_DNS; + addrlen = 0; + } + + count = sk_GENERAL_NAME_num(altname_stack); + for (i = 0; i < count; i++) { + GENERAL_NAME *altname; + + altname = sk_GENERAL_NAME_value(altname_stack, i); + + if (altname->type != type) + continue; + + if (type == GEN_DNS) { + unsigned char *data; + int format; + + format = ASN1_STRING_type(altname->d.dNSName); + if (format == V_ASN1_IA5STRING) { + data = ASN1_STRING_data(altname->d.dNSName); + + if (ASN1_STRING_length(altname->d.dNSName) != + (int)strlen(data)) { + fprintf(stdout, "%s: NUL byte in " + "subjectAltName, probably a " + "malicious certificate.\n", + getprogname()); + rv = -2; + break; + } + + if (ressl_match_hostname(data, host) == 0) { + rv = 0; + break; + } + } else + fprintf(stdout, "%s: unhandled subjectAltName " + "dNSName encoding (%d)\n", getprogname(), + format); + + } else if (type == GEN_IPADD) { + unsigned char *data; + int datalen; + + datalen = ASN1_STRING_length(altname->d.iPAddress); + data = ASN1_STRING_data(altname->d.iPAddress); + + if (datalen == addrlen && + memcmp(data, &addrbuf, addrlen) == 0) { + rv = 0; + break; + } + } + } + + sk_GENERAL_NAME_free(altname_stack); + return rv; +} + +int +ressl_check_common_name(X509 *cert, const char *host) +{ + X509_NAME *name; + char *common_name = NULL; + size_t common_name_len; + int rv = -1; + + name = X509_get_subject_name(cert); + if (name == NULL) + goto out; + + common_name_len = X509_NAME_get_text_by_NID(name, NID_commonName, + NULL, 0); + if (common_name_len < 0) + goto out; + + common_name = calloc(common_name_len + 1, 1); + if (common_name == NULL) + goto out; + + X509_NAME_get_text_by_NID(name, NID_commonName, common_name, + common_name_len + 1); + + /* NUL bytes in CN? */ + if (common_name_len != (int)strlen(common_name)) { + fprintf(stdout, "%s: NUL byte in Common Name field, " + "probably a malicious certificate.\n", getprogname()); + rv = -2; + goto out; + } + + if (ressl_match_hostname(common_name, host) == 0) + rv = 0; +out: + free(common_name); + return rv; +} + +int +ressl_check_hostname(X509 *cert, const char *host) +{ + int rv; + + rv = ressl_check_subject_altname(cert, host); + if (rv == 0 || rv == -2) + return rv; + + return ressl_check_common_name(cert, host); +} diff --git a/src/lib/libressl/shlib_version b/src/lib/libressl/shlib_version new file mode 100644 index 0000000000..1edea46de9 --- /dev/null +++ b/src/lib/libressl/shlib_version @@ -0,0 +1,2 @@ +major=1 +minor=0 -- cgit v1.2.3-55-g6feb