X-Git-Url: http://www.project-moonshot.org/gitweb/?p=radsecproxy.git;a=blobdiff_plain;f=tls.c;h=ba3cab5e89f612f6fd6d2fb4dfa64abf39570088;hp=d49e94bd4bc1fe33d3161dbd47a7c90922186ed6;hb=HEAD;hpb=6068df237753e20c504c954504858585d5105a46 diff --git a/tls.c b/tls.c index d49e94b..ba3cab5 100644 --- a/tls.c +++ b/tls.c @@ -1,477 +1,372 @@ -/* - * Copyright (C) 2006-2008 Stig Venaas - * - * 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. - */ +/* Copyright 2010-2013 NORDUnet A/S. All rights reserved. + See LICENSE for licensing information. */ + +#if defined HAVE_CONFIG_H +#include +#endif -#include -#include -#include -#include -#include +#include #include -#include -#ifdef SYS_SOLARIS9 +#include #include -#endif -#include -#include -#include -#include -#include -#include -#include +#include +#if defined HAVE_PTHREAD_H #include +#endif #include #include -#include "list.h" -#include "radsecproxy.h" - -#ifdef RADPROT_TLS -#include "debug.h" -#include "util.h" - -static void setprotoopts(struct commonprotoopts *opts); -static char **getlistenerargs(); -void *tlslistener(void *arg); -int tlsconnect(struct server *server, struct timeval *when, int timeout, char *text); -void *tlsclientrd(void *arg); -int clientradputtls(struct server *server, unsigned char *rad); -void tlssetsrcres(); - -static const struct protodefs protodefs = { - "tls", - "mysecret", /* secretdefault */ - SOCK_STREAM, /* socktype */ - "2083", /* portdefault */ - 0, /* retrycountdefault */ - 0, /* retrycountmax */ - REQUEST_RETRY_INTERVAL * REQUEST_RETRY_COUNT, /* retryintervaldefault */ - 60, /* retryintervalmax */ - DUPLICATE_INTERVAL, /* duplicateintervaldefault */ - setprotoopts, /* setprotoopts */ - getlistenerargs, /* getlistenerargs */ - tlslistener, /* listener */ - tlsconnect, /* connecter */ - tlsclientrd, /* clientconnreader */ - clientradputtls, /* clientradput */ - NULL, /* addclient */ - NULL, /* addserverextra */ - tlssetsrcres, /* setsrcres */ - NULL /* initextra */ -}; - -static struct addrinfo *srcres = NULL; -static uint8_t handle; -static struct commonprotoopts *protoopts = NULL; - -const struct protodefs *tlsinit(uint8_t h) { - handle = h; - return &protodefs; -} - -static void setprotoopts(struct commonprotoopts *opts) { - protoopts = opts; -} +#include +#include +#include +#include +#include +#include -static char **getlistenerargs() { - return protoopts ? protoopts->listenargs : NULL; -} +#include +#include "radsecproxy/list.h" +#include "radsecproxy/radsecproxy.h" + +#include "tls.h" + +static struct tls * +_get_tlsconf (struct rs_connection *conn, const struct rs_realm *realm) +{ + struct tls *c = rs_malloc (conn->ctx, sizeof (struct tls)); + + if (c) + { + memset (c, 0, sizeof (struct tls)); + /* TODO: Make sure old radsecproxy code doesn't free these all + of a sudden, or strdup them. */ + c->name = realm->name; + c->cacertfile = realm->cacertfile; + c->cacertpath = NULL; /* NYI */ + c->certfile = realm->certfile; + c->certkeyfile = realm->certkeyfile; + c->certkeypwd = NULL; /* NYI */ + c->cacheexpiry = 0; /* NYI */ + c->crlcheck = 0; /* NYI */ + c->policyoids = (char **) NULL; /* NYI */ + } + else + rs_err_conn_push_fl (conn, RSE_NOMEM, __FILE__, __LINE__, NULL); -void tlssetsrcres() { - if (!srcres) - srcres = resolve_hostport_addrinfo(handle, protoopts ? protoopts->sourcearg : NULL); - + return c; } -int tlsconnect(struct server *server, struct timeval *when, int timeout, char *text) { - struct timeval now; - time_t elapsed; - X509 *cert; - SSL_CTX *ctx = NULL; - unsigned long error; - - debug(DBG_DBG, "tlsconnect: called from %s", text); - pthread_mutex_lock(&server->lock); - if (when && memcmp(&server->lastconnecttry, when, sizeof(struct timeval))) { - /* already reconnected, nothing to do */ - debug(DBG_DBG, "tlsconnect(%s): seems already reconnected", text); - pthread_mutex_unlock(&server->lock); - return 1; +#if defined RS_ENABLE_TLS_PSK +static unsigned int +psk_client_cb (SSL *ssl, + const char *hint, + char *identity, + unsigned int max_identity_len, + unsigned char *psk, + unsigned int max_psk_len) +{ + struct rs_connection *conn = NULL; + struct rs_credentials *cred = NULL; + + conn = SSL_get_ex_data (ssl, 0); + assert (conn != NULL); + cred = conn->active_peer->realm->transport_cred; + assert (cred != NULL); + /* NOTE: Ignoring identity hint from server. */ + + if (strlen (cred->identity) + 1 > max_identity_len) + { + rs_err_conn_push (conn, RSE_CRED, "PSK identity longer than max %d", + max_identity_len - 1); + return 0; } - - for (;;) { - gettimeofday(&now, NULL); - elapsed = now.tv_sec - server->lastconnecttry.tv_sec; - if (timeout && server->lastconnecttry.tv_sec && elapsed > timeout) { - debug(DBG_DBG, "tlsconnect: timeout"); - if (server->sock >= 0) - close(server->sock); - SSL_free(server->ssl); - server->ssl = NULL; - pthread_mutex_unlock(&server->lock); - return 0; - } - if (server->connectionok) { - server->connectionok = 0; - sleep(2); - } else if (elapsed < 1) - sleep(2); - else if (elapsed < 60) { - debug(DBG_INFO, "tlsconnect: sleeping %lds", elapsed); - sleep(elapsed); - } else if (elapsed < 100000) { - debug(DBG_INFO, "tlsconnect: sleeping %ds", 60); - sleep(60); - } else - server->lastconnecttry.tv_sec = now.tv_sec; /* no sleep at startup */ - debug(DBG_WARN, "tlsconnect: trying to open TLS connection to %s port %s", server->conf->host, server->conf->port); - if (server->sock >= 0) - close(server->sock); - if ((server->sock = connecttcp(server->conf->addrinfo, srcres)) < 0) { - debug(DBG_ERR, "tlsconnect: connecttcp failed"); - continue; - } - - SSL_free(server->ssl); - server->ssl = NULL; - ctx = tlsgetctx(handle, server->conf->tlsconf); - if (!ctx) - continue; - server->ssl = SSL_new(ctx); - if (!server->ssl) - continue; - - SSL_set_fd(server->ssl, server->sock); - if (SSL_connect(server->ssl) <= 0) { - while ((error = ERR_get_error())) - debug(DBG_ERR, "tlsconnect: TLS: %s", ERR_error_string(error, NULL)); - continue; - } - cert = verifytlscert(server->ssl); - if (!cert) - continue; - if (verifyconfcert(cert, server->conf)) { - X509_free(cert); - break; - } - X509_free(cert); + strcpy (identity, cred->identity); + + switch (cred->secret_encoding) + { + case RS_KEY_ENCODING_UTF8: + cred->secret_len = strlen (cred->secret); + if (cred->secret_len > max_psk_len) + { + rs_err_conn_push (conn, RSE_CRED, "PSK secret longer than max %d", + max_psk_len); + return 0; + } + memcpy (psk, cred->secret, cred->secret_len); + break; + case RS_KEY_ENCODING_ASCII_HEX: + { + BIGNUM *bn = NULL; + + if (BN_hex2bn (&bn, cred->secret) == 0) + { + rs_err_conn_push (conn, RSE_CRED, "Unable to convert pskhexstr"); + if (bn != NULL) + BN_clear_free (bn); + return 0; + } + if ((unsigned int) BN_num_bytes (bn) > max_psk_len) + { + rs_err_conn_push (conn, RSE_CRED, "PSK secret longer than max %d", + max_psk_len); + BN_clear_free (bn); + return 0; + } + cred->secret_len = BN_bn2bin (bn, psk); + BN_clear_free (bn); + } + break; + default: + assert (!"unknown psk encoding"); } - debug(DBG_WARN, "tlsconnect: TLS connection to %s port %s up", server->conf->host, server->conf->port); - server->connectionok = 1; - gettimeofday(&server->lastconnecttry, NULL); - pthread_mutex_unlock(&server->lock); - return 1; -} -/* timeout in seconds, 0 means no timeout (blocking), returns when num bytes have been read, or timeout */ -/* returns 0 on timeout, -1 on error and num if ok */ -int sslreadtimeout(SSL *ssl, unsigned char *buf, int num, int timeout) { - int s, ndesc, cnt, len; - fd_set readfds, writefds; - struct timeval timer; - - s = SSL_get_fd(ssl); - if (s < 0) - return -1; - /* make socket non-blocking? */ - for (len = 0; len < num; len += cnt) { - FD_ZERO(&readfds); - FD_SET(s, &readfds); - writefds = readfds; - if (timeout) { - timer.tv_sec = timeout; - timer.tv_usec = 0; - } - ndesc = select(s + 1, &readfds, &writefds, NULL, timeout ? &timer : NULL); - if (ndesc < 1) - return ndesc; - - cnt = SSL_read(ssl, buf + len, num - len); - if (cnt <= 0) - switch (SSL_get_error(ssl, cnt)) { - case SSL_ERROR_WANT_READ: - case SSL_ERROR_WANT_WRITE: - cnt = 0; - continue; - case SSL_ERROR_ZERO_RETURN: - /* remote end sent close_notify, send one back */ - SSL_shutdown(ssl); - return -1; - default: - return -1; - } - } - return num; + return cred->secret_len; } - -/* timeout in seconds, 0 means no timeout (blocking) */ -unsigned char *radtlsget(SSL *ssl, int timeout) { - int cnt, len; - unsigned char buf[4], *rad; - - for (;;) { - cnt = sslreadtimeout(ssl, buf, 4, timeout); - if (cnt < 1) { - debug(DBG_DBG, cnt ? "radtlsget: connection lost" : "radtlsget: timeout"); - return NULL; - } - - len = RADLEN(buf); - rad = malloc(len); - if (!rad) { - debug(DBG_ERR, "radtlsget: malloc failed"); - continue; - } - memcpy(rad, buf, 4); - - cnt = sslreadtimeout(ssl, rad + 4, len - 4, timeout); - if (cnt < 1) { - debug(DBG_DBG, cnt ? "radtlsget: connection lost" : "radtlsget: timeout"); - free(rad); - return NULL; - } - - if (len >= 20) - break; - - free(rad); - debug(DBG_WARN, "radtlsget: packet smaller than minimum radius size"); +#endif /* RS_ENABLE_TLS_PSK */ + +/** Read \a buf_len bytes from one of the random devices into \a + buf. Return 0 on success and -1 on failure. */ +static int +load_rand_ (uint8_t *buf, size_t buf_len) +{ + static const char *fns[] = {"/dev/urandom", "/dev/random", NULL}; + int i; + + if (buf_len > SSIZE_MAX) + return -1; + + for (i = 0; fns[i] != NULL; i++) + { + size_t nread = 0; + int fd = open (fns[i], O_RDONLY); + if (fd < 0) + continue; + while (nread != buf_len) + { + ssize_t r = read (fd, buf + nread, buf_len - nread); + if (r < 0) + return -1; + if (r == 0) + break; + nread += r; + } + close (fd); + if (nread != buf_len) + return -1; + return 0; } - - debug(DBG_DBG, "radtlsget: got %d bytes", len); - return rad; + return -1; } -int clientradputtls(struct server *server, unsigned char *rad) { - int cnt; - size_t len; - unsigned long error; - struct clsrvconf *conf = server->conf; - - if (!server->connectionok) - return 0; - len = RADLEN(rad); - if ((cnt = SSL_write(server->ssl, rad, len)) <= 0) { - while ((error = ERR_get_error())) - debug(DBG_ERR, "clientradputtls: TLS: %s", ERR_error_string(error, NULL)); - return 0; - } - - debug(DBG_DBG, "clientradputtls: Sent %d bytes, Radius packet of length %d to TLS peer %s", cnt, len, conf->host); - return 1; +/** Initialise OpenSSL's PRNG by possibly invoking RAND_poll() and by + feeding RAND_seed() data from one of the random devices. If either + succeeds, we're happy and return 0. */ +static int +init_openssl_rand_ (void) +{ + long openssl_version = 0; + int openssl_random_init_flag = 0; + int our_random_init_flag = 0; + uint8_t buf[32]; + + /* Older OpenSSL has a crash bug in RAND_poll (when a file it opens + gets a file descriptor with a number higher than FD_SETSIZE) so + use it only for newer versions. */ + openssl_version = SSLeay (); + if (openssl_version >= OPENSSL_V (0,9,8,'c')) + openssl_random_init_flag = RAND_poll (); + + our_random_init_flag = !load_rand_ (buf, sizeof(buf)); + if (our_random_init_flag) + RAND_seed (buf, sizeof(buf)); + memset (buf, 0, sizeof(buf)); /* FIXME: What if memset() is optimised out? */ + + if (!openssl_random_init_flag && !our_random_init_flag) + return -1; + if (!RAND_bytes (buf, sizeof(buf))) + return -1; + return 0; } -void *tlsclientrd(void *arg) { - struct server *server = (struct server *)arg; - unsigned char *buf; - struct timeval now, lastconnecttry; - - for (;;) { - /* yes, lastconnecttry is really necessary */ - lastconnecttry = server->lastconnecttry; - buf = radtlsget(server->ssl, server->dynamiclookuparg ? IDLE_TIMEOUT : 0); - if (!buf) { - if (server->dynamiclookuparg) - break; - tlsconnect(server, &lastconnecttry, 0, "tlsclientrd"); - continue; - } - - replyh(server, buf); - - if (server->dynamiclookuparg) { - gettimeofday(&now, NULL); - if (now.tv_sec - server->lastreply.tv_sec > IDLE_TIMEOUT) { - debug(DBG_INFO, "tlsclientrd: idle timeout for %s", server->conf->name); - break; - } - } - } - ERR_remove_state(0); - server->clientrdgone = 1; - return NULL; +#if defined HAVE_PTHREADS +/** Array of pthread_mutex_t for OpenSSL. Allocated and initialised in + \a init_locking_ and never freed. */ +static pthread_mutex_t *s_openssl_mutexes = NULL; +/** Number of pthread_mutex_t's allocated at s_openssl_mutexes. */ +static int s_openssl_mutexes_count = 0; + +/** Callback for OpenSSL when a lock is to be held or released. */ +static void +openssl_locking_cb_ (int mode, int i, const char *file, int line) +{ + if (s_openssl_mutexes == NULL || i >= s_openssl_mutexes_count) + return; + if (mode & CRYPTO_LOCK) + pthread_mutex_lock (&s_openssl_mutexes[i]); + else + pthread_mutex_unlock (&s_openssl_mutexes[i]); } -void *tlsserverwr(void *arg) { - int cnt; - unsigned long error; - struct client *client = (struct client *)arg; - struct queue *replyq; - struct request *reply; - - debug(DBG_DBG, "tlsserverwr: starting for %s", addr2string(client->addr)); - replyq = client->replyq; - for (;;) { - pthread_mutex_lock(&replyq->mutex); - while (!list_first(replyq->entries)) { - if (client->ssl) { - debug(DBG_DBG, "tlsserverwr: waiting for signal"); - pthread_cond_wait(&replyq->cond, &replyq->mutex); - debug(DBG_DBG, "tlsserverwr: got signal"); - } - if (!client->ssl) { - /* ssl might have changed while waiting */ - pthread_mutex_unlock(&replyq->mutex); - debug(DBG_DBG, "tlsserverwr: exiting as requested"); - ERR_remove_state(0); - pthread_exit(NULL); - } - } - reply = (struct request *)list_shift(replyq->entries); - pthread_mutex_unlock(&replyq->mutex); - cnt = SSL_write(client->ssl, reply->replybuf, RADLEN(reply->replybuf)); - if (cnt > 0) - debug(DBG_DBG, "tlsserverwr: sent %d bytes, Radius packet of length %d to %s", - cnt, RADLEN(reply->replybuf), addr2string(client->addr)); - else - while ((error = ERR_get_error())) - debug(DBG_ERR, "tlsserverwr: SSL: %s", ERR_error_string(error, NULL)); - freerq(reply); - } +/** Initialise any locking needed for being thread safe. Libradsec has + all its own state in one or more struct rs_context and doesn't + need locks but libraries used by libradsec may need protection. */ +static int +init_locking_ () +{ + int i, n; + n = CRYPTO_num_locks (); + + s_openssl_mutexes = calloc (n, sizeof(pthread_mutex_t)); + if (s_openssl_mutexes == NULL) + return -RSE_NOMEM; + for (i = 0; i < n; i++) + pthread_mutex_init (&s_openssl_mutexes[i], NULL); + s_openssl_mutexes_count = n; + + return 0; } - -void tlsserverrd(struct client *client) { - struct request *rq; - uint8_t *buf; - pthread_t tlsserverwrth; - - debug(DBG_DBG, "tlsserverrd: starting for %s", addr2string(client->addr)); - - if (pthread_create(&tlsserverwrth, NULL, tlsserverwr, (void *)client)) { - debug(DBG_ERR, "tlsserverrd: pthread_create failed"); - return; - } - - for (;;) { - buf = radtlsget(client->ssl, 0); - if (!buf) { - debug(DBG_ERR, "tlsserverrd: connection from %s lost", addr2string(client->addr)); - break; - } - debug(DBG_DBG, "tlsserverrd: got Radius message from %s", addr2string(client->addr)); - rq = newrequest(); - if (!rq) { - free(buf); - continue; - } - rq->buf = buf; - rq->from = client; - if (!radsrv(rq)) { - debug(DBG_ERR, "tlsserverrd: message authentication/validation failed, closing connection from %s", addr2string(client->addr)); - break; - } +#endif /* HAVE_PTHREADS */ + +/** Initialise the TLS library. Return 0 on success, -1 on failure. */ +int +tls_init () +{ + SSL_load_error_strings (); +#if defined HAVE_PTHREADS + if (CRYPTO_get_locking_callback () == NULL) + { + assert (s_openssl_mutexes_count == 0); + /* Allocate and initialise mutexes. We will never free + these. FIXME: Is there a portable way of having a function + invoked when a solib is unloaded? -ln */ + if (init_locking_ ()) + return -1; + CRYPTO_set_locking_callback (openssl_locking_cb_); } - - /* stop writer by setting ssl to NULL and give signal in case waiting for data */ - client->ssl = NULL; - pthread_mutex_lock(&client->replyq->mutex); - pthread_cond_signal(&client->replyq->cond); - pthread_mutex_unlock(&client->replyq->mutex); - debug(DBG_DBG, "tlsserverrd: waiting for writer to end"); - pthread_join(tlsserverwrth, NULL); - debug(DBG_DBG, "tlsserverrd: reader for %s exiting", addr2string(client->addr)); +#endif /* HAVE_PTHREADS */ + SSL_library_init (); + return init_openssl_rand_ (); } -void *tlsservernew(void *arg) { - int s; - struct sockaddr_storage from; - socklen_t fromlen = sizeof(from); - struct clsrvconf *conf; - struct list_node *cur = NULL; - SSL *ssl = NULL; - X509 *cert = NULL; - SSL_CTX *ctx = NULL; - unsigned long error; - struct client *client; - - s = *(int *)arg; - if (getpeername(s, (struct sockaddr *)&from, &fromlen)) { - debug(DBG_DBG, "tlsservernew: getpeername failed, exiting"); - goto exit; - } - debug(DBG_WARN, "tlsservernew: incoming TLS connection from %s", addr2string((struct sockaddr *)&from)); - - conf = find_clconf(handle, (struct sockaddr *)&from, &cur); - if (conf) { - ctx = tlsgetctx(handle, conf->tlsconf); - if (!ctx) - goto exit; - ssl = SSL_new(ctx); - if (!ssl) - goto exit; - SSL_set_fd(ssl, s); - - if (SSL_accept(ssl) <= 0) { - while ((error = ERR_get_error())) - debug(DBG_ERR, "tlsservernew: SSL: %s", ERR_error_string(error, NULL)); - debug(DBG_ERR, "tlsservernew: SSL_accept failed"); - goto exit; - } - cert = verifytlscert(ssl); - if (!cert) - goto exit; +int +tls_init_conn (struct rs_connection *conn) +{ + struct rs_context *ctx = NULL; + struct tls *tlsconf = NULL; + SSL_CTX *ssl_ctx = NULL; + SSL *ssl = NULL; + unsigned long sslerr = 0; + + assert (conn->ctx); + ctx = conn->ctx; + + tlsconf = _get_tlsconf (conn, conn->active_peer->realm); + if (!tlsconf) + return -1; + ssl_ctx = tlsgetctx (RAD_TLS, tlsconf); + if (!ssl_ctx) + { + for (sslerr = ERR_get_error (); sslerr; sslerr = ERR_get_error ()) + rs_err_conn_push_fl (conn, RSE_SSLERR, __FILE__, __LINE__, + ERR_error_string (sslerr, NULL)); + return -1; } - - while (conf) { - if (verifyconfcert(cert, conf)) { - X509_free(cert); - client = addclient(conf, 1); - if (client) { - client->ssl = ssl; - client->addr = addr_copy((struct sockaddr *)&from); - tlsserverrd(client); - removeclient(client); - } else - debug(DBG_WARN, "tlsservernew: failed to create new client instance"); - goto exit; - } - conf = find_clconf(handle, (struct sockaddr *)&from, &cur); + ssl = SSL_new (ssl_ctx); + if (!ssl) + { + for (sslerr = ERR_get_error (); sslerr; sslerr = ERR_get_error ()) + rs_err_conn_push_fl (conn, RSE_SSLERR, __FILE__, __LINE__, + ERR_error_string (sslerr, NULL)); + return -1; } - debug(DBG_WARN, "tlsservernew: ignoring request, no matching TLS client"); - if (cert) - X509_free(cert); - - exit: - if (ssl) { - SSL_shutdown(ssl); - SSL_free(ssl); + +#if defined RS_ENABLE_TLS_PSK + if (conn->active_peer->realm->transport_cred != NULL) + { + SSL_set_psk_client_callback (ssl, psk_client_cb); + SSL_set_ex_data (ssl, 0, conn); } - ERR_remove_state(0); - shutdown(s, SHUT_RDWR); - close(s); - pthread_exit(NULL); +#endif /* RS_ENABLE_TLS_PSK */ + + conn->tls_ctx = ssl_ctx; + conn->tls_ssl = ssl; + rs_free (ctx, tlsconf); + return RSE_OK; } -void *tlslistener(void *arg) { - pthread_t tlsserverth; - int s, *sp = (int *)arg; - struct sockaddr_storage from; - socklen_t fromlen = sizeof(from); - - listen(*sp, 0); - - for (;;) { - s = accept(*sp, (struct sockaddr *)&from, &fromlen); - if (s < 0) { - debug(DBG_WARN, "accept failed"); - continue; - } - if (pthread_create(&tlsserverth, NULL, tlsservernew, (void *)&s)) { - debug(DBG_ERR, "tlslistener: pthread_create failed"); - shutdown(s, SHUT_RDWR); - close(s); - continue; - } - pthread_detach(tlsserverth); +/* draft-ietf-radext-radsec-11.txt + + * Certificate validation MUST include the verification rules as + per [RFC5280]. + + * Implementations SHOULD indicate their acceptable Certification + Authorities as per section 7.4.4 (server side) and x.y.z + ["Trusted CA Indication"] (client side) of [RFC5246] (see + Section 3.2) + + * Implementations SHOULD allow to configure a list of acceptable + certificates, identified via certificate fingerprint. When a + fingerprint configured, the fingerprint is prepended with an + ASCII label identifying the hash function followed by a colon. + Implementations MUST support SHA-1 as the hash algorithm and + use the ASCII label "sha-1" to identify the SHA-1 algorithm. + The length of a SHA-1 hash is 20 bytes and the length of the + corresponding fingerprint string is 65 characters. An example + certificate fingerprint is: sha- + 1:E1:2D:53:2B:7C:6B:8A:29:A2:76:C8:64:36:0B:08:4B:7A:F1:9E:9D + + * Peer validation always includes a check on whether the locally + configured expected DNS name or IP address of the server that + is contacted matches its presented certificate. DNS names and + IP addresses can be contained in the Common Name (CN) or + subjectAltName entries. For verification, only one of these + entries is to be considered. The following precedence + applies: for DNS name validation, subjectAltName:DNS has + precedence over CN; for IP address validation, subjectAltName: + iPAddr has precedence over CN. + + * Implementations SHOULD allow to configure a set of acceptable + values for subjectAltName:URI. + */ +int +tls_verify_cert (struct rs_connection *conn) +{ + int err = 0; + int success = 0; + X509 *peer_cert = NULL; + struct in6_addr addr; + const char *hostname = NULL; + + assert (conn->active_peer->conn == conn); + assert (conn->active_peer->hostname != NULL); + hostname = conn->active_peer->hostname; + + /* verifytlscert() performs basic verification as described by + OpenSSL VERIFY(1), i.e. verification of the certificate chain. */ + peer_cert = verifytlscert (conn->tls_ssl); + if (peer_cert == NULL) + { + err = rs_err_conn_push (conn, RSE_SSLERR, + "basic certificate validation failed"); + goto out; } - free(sp); - return NULL; -} -#else -const struct protodefs *tlsinit(uint8_t h) { - return NULL; + + if (inet_pton (AF_INET, hostname, &addr)) + success = (subjectaltnameaddr (peer_cert, AF_INET, &addr) == 1); + else if (inet_pton (AF_INET6, hostname, &addr)) + success = (subjectaltnameaddr (peer_cert, AF_INET6, &addr) == 1); + else + success = (subjectaltnameregexp (peer_cert, GEN_DNS, hostname, NULL) == 1); + + if (!success) + success = (cnregexp (peer_cert, hostname, NULL) == 1); + + if (conn->realm->disable_hostname_check) + success = 1; + if (!success) + err = rs_err_conn_push (conn, RSE_CERT, "server certificate doesn't " + "match configured hostname \"%s\"", hostname); + + out: + if (peer_cert != NULL) + X509_free (peer_cert); + return err; } -#endif