From: Stefan Metzmacher Date: Wed, 23 Dec 2015 15:17:04 +0000 (+0100) Subject: CVE-2016-2113: s4:lib/tls: implement infrastructure to do peer verification X-Git-Tag: samba-4.2.10~151 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=b8c5862e02542e67b3f5340eedb3df07c634f740;p=thirdparty%2Fsamba.git CVE-2016-2113: s4:lib/tls: implement infrastructure to do peer verification BUG: https://bugzilla.samba.org/show_bug.cgi?id=11752 Signed-off-by: Stefan Metzmacher Reviewed-by: Günther Deschner --- diff --git a/source4/lib/tls/tls.h b/source4/lib/tls/tls.h index e6c27f3e6f5..e600c89cceb 100644 --- a/source4/lib/tls/tls.h +++ b/source4/lib/tls/tls.h @@ -68,10 +68,33 @@ const struct socket_ops *socket_tls_ops(enum socket_type type); struct tstream_context; struct tstream_tls_params; +enum tls_verify_peer_state { + TLS_VERIFY_PEER_NO_CHECK = 0, +#define TLS_VERIFY_PEER_NO_CHECK_STRING "no_check" + + TLS_VERIFY_PEER_CA_ONLY = 10, +#define TLS_VERIFY_PEER_CA_ONLY_STRING "ca_only" + + TLS_VERIFY_PEER_CA_AND_NAME_IF_AVAILABLE = 20, +#define TLS_VERIFY_PEER_CA_AND_NAME_IF_AVAILABLE_STRING \ + "ca_and_name_if_available" + + TLS_VERIFY_PEER_CA_AND_NAME = 30, +#define TLS_VERIFY_PEER_CA_AND_NAME_STRING "ca_and_name" + + TLS_VERIFY_PEER_AS_STRICT_AS_POSSIBLE = 9999, +#define TLS_VERIFY_PEER_AS_STRICT_AS_POSSIBLE_STRING \ + "as_strict_as_possible" +}; + +const char *tls_verify_peer_string(enum tls_verify_peer_state verify_peer); + NTSTATUS tstream_tls_params_client(TALLOC_CTX *mem_ctx, const char *ca_file, const char *crl_file, const char *tls_priority, + enum tls_verify_peer_state verify_peer, + const char *peer_name, struct tstream_tls_params **_tlsp); NTSTATUS tstream_tls_params_server(TALLOC_CTX *mem_ctx, diff --git a/source4/lib/tls/tls_tstream.c b/source4/lib/tls/tls_tstream.c index 255e2cea421..2ccb9090eba 100644 --- a/source4/lib/tls/tls_tstream.c +++ b/source4/lib/tls/tls_tstream.c @@ -20,13 +20,16 @@ #include "includes.h" #include "system/network.h" #include "system/filesys.h" +#include "system/time.h" #include "../util/tevent_unix.h" #include "../lib/tsocket/tsocket.h" #include "../lib/tsocket/tsocket_internal.h" +#include "../lib/util/util_net.h" #include "lib/tls/tls.h" #if ENABLE_GNUTLS #include +#include #define DH_BITS 2048 @@ -34,8 +37,47 @@ typedef gnutls_datum gnutls_datum_t; #endif +/* + * define our own values in a high range + */ +#ifndef HAVE_DECL_GNUTLS_CERT_EXPIRED +#define GNUTLS_CERT_EXPIRED 0x10000000 +#define REQUIRE_CERT_TIME_CHECKS 1 +#endif +#ifndef HAVE_DECL_GNUTLS_CERT_NOT_ACTIVATED +#define GNUTLS_CERT_NOT_ACTIVATED 0x20000000 +#ifndef REQUIRE_CERT_TIME_CHECKS +#define REQUIRE_CERT_TIME_CHECKS 1 +#endif +#endif +#ifndef HAVE_DECL_GNUTLS_CERT_UNEXPECTED_OWNER +#define GNUTLS_CERT_UNEXPECTED_OWNER 0x40000000 +#endif + #endif /* ENABLE_GNUTLS */ +const char *tls_verify_peer_string(enum tls_verify_peer_state verify_peer) +{ + switch (verify_peer) { + case TLS_VERIFY_PEER_NO_CHECK: + return TLS_VERIFY_PEER_NO_CHECK_STRING; + + case TLS_VERIFY_PEER_CA_ONLY: + return TLS_VERIFY_PEER_CA_ONLY_STRING; + + case TLS_VERIFY_PEER_CA_AND_NAME_IF_AVAILABLE: + return TLS_VERIFY_PEER_CA_AND_NAME_IF_AVAILABLE_STRING; + + case TLS_VERIFY_PEER_CA_AND_NAME: + return TLS_VERIFY_PEER_CA_AND_NAME_STRING; + + case TLS_VERIFY_PEER_AS_STRICT_AS_POSSIBLE: + return TLS_VERIFY_PEER_AS_STRICT_AS_POSSIBLE_STRING; + } + + return "unknown tls_verify_peer_state"; +} + static const struct tstream_context_ops tstream_tls_ops; struct tstream_tls { @@ -46,6 +88,9 @@ struct tstream_tls { gnutls_session tls_session; #endif /* ENABLE_GNUTLS */ + enum tls_verify_peer_state verify_peer; + const char *peer_name; + struct tevent_context *current_ev; struct tevent_immediate *retry_im; @@ -871,6 +916,8 @@ struct tstream_tls_params { const char *tls_priority; #endif /* ENABLE_GNUTLS */ bool tls_enabled; + enum tls_verify_peer_state verify_peer; + const char *peer_name; }; static int tstream_tls_params_destructor(struct tstream_tls_params *tlsp) @@ -897,6 +944,8 @@ NTSTATUS tstream_tls_params_client(TALLOC_CTX *mem_ctx, const char *ca_file, const char *crl_file, const char *tls_priority, + enum tls_verify_peer_state verify_peer, + const char *peer_name, struct tstream_tls_params **_tlsp) { #if ENABLE_GNUTLS @@ -914,6 +963,21 @@ NTSTATUS tstream_tls_params_client(TALLOC_CTX *mem_ctx, talloc_set_destructor(tlsp, tstream_tls_params_destructor); + tlsp->verify_peer = verify_peer; + if (peer_name != NULL) { + tlsp->peer_name = talloc_strdup(tlsp, peer_name); + if (tlsp->peer_name == NULL) { + talloc_free(tlsp); + return NT_STATUS_NO_MEMORY; + } + } else if (tlsp->verify_peer >= TLS_VERIFY_PEER_CA_AND_NAME) { + DEBUG(0,("TLS failed to missing peer_name - " + "with 'tls verify peer = %s'\n", + tls_verify_peer_string(tlsp->verify_peer))); + talloc_free(tlsp); + return NT_STATUS_INVALID_PARAMETER_MIX; + } + ret = gnutls_certificate_allocate_credentials(&tlsp->x509_cred); if (ret != GNUTLS_E_SUCCESS) { DEBUG(0,("TLS %s - %s\n", __location__, gnutls_strerror(ret))); @@ -931,6 +995,13 @@ NTSTATUS tstream_tls_params_client(TALLOC_CTX *mem_ctx, talloc_free(tlsp); return NT_STATUS_CANT_ACCESS_DOMAIN_INFO; } + } else if (tlsp->verify_peer >= TLS_VERIFY_PEER_CA_ONLY) { + DEBUG(0,("TLS failed to missing cafile %s - " + "with 'tls verify peer = %s'\n", + ca_file, + tls_verify_peer_string(tlsp->verify_peer))); + talloc_free(tlsp); + return NT_STATUS_INVALID_PARAMETER_MIX; } if (crl_file && *crl_file && file_exist(crl_file)) { @@ -943,6 +1014,13 @@ NTSTATUS tstream_tls_params_client(TALLOC_CTX *mem_ctx, talloc_free(tlsp); return NT_STATUS_CANT_ACCESS_DOMAIN_INFO; } + } else if (tlsp->verify_peer >= TLS_VERIFY_PEER_AS_STRICT_AS_POSSIBLE) { + DEBUG(0,("TLS failed to missing crlfile %s - " + "with 'tls verify peer = %s'\n", + crl_file, + tls_verify_peer_string(tlsp->verify_peer))); + talloc_free(tlsp); + return NT_STATUS_INVALID_PARAMETER_MIX; } tlsp->tls_priority = talloc_strdup(tlsp, tls_priority); @@ -997,6 +1075,13 @@ struct tevent_req *_tstream_tls_connect_send(TALLOC_CTX *mem_ctx, talloc_set_destructor(tlss, tstream_tls_destructor); tlss->plain_stream = plain_stream; + tlss->verify_peer = tls_params->verify_peer; + if (tls_params->peer_name != NULL) { + tlss->peer_name = talloc_strdup(tlss, tls_params->peer_name); + if (tevent_req_nomem(tlss->peer_name, req)) { + return tevent_req_post(req, ev); + } + } tlss->current_ev = ev; tlss->retry_im = tevent_create_immediate(tlss); @@ -1364,6 +1449,170 @@ static void tstream_tls_retry_handshake(struct tstream_context *stream) return; } + if (tlss->verify_peer >= TLS_VERIFY_PEER_CA_ONLY) { + unsigned int status = UINT32_MAX; + bool ip = true; + const char *hostname = NULL; +#ifndef HAVE_GNUTLS_CERTIFICATE_VERIFY_PEERS3 + bool need_crt_checks = false; +#endif + + if (tlss->peer_name != NULL) { + ip = is_ipaddress(tlss->peer_name); + } + + if (!ip) { + hostname = tlss->peer_name; + } + + if (tlss->verify_peer == TLS_VERIFY_PEER_CA_ONLY) { + hostname = NULL; + } + + if (tlss->verify_peer >= TLS_VERIFY_PEER_CA_AND_NAME) { + if (hostname == NULL) { + DEBUG(1,("TLS %s - no hostname available for " + "verify_peer[%s] and peer_name[%s]\n", + __location__, + tls_verify_peer_string(tlss->verify_peer), + tlss->peer_name)); + tlss->error = EINVAL; + tevent_req_error(req, tlss->error); + return; + } + } + +#ifdef HAVE_GNUTLS_CERTIFICATE_VERIFY_PEERS3 + ret = gnutls_certificate_verify_peers3(tlss->tls_session, + hostname, + &status); + if (ret != GNUTLS_E_SUCCESS) { + DEBUG(1,("TLS %s - %s\n", __location__, gnutls_strerror(ret))); + tlss->error = EIO; + tevent_req_error(req, tlss->error); + return; + } +#else /* not HAVE_GNUTLS_CERTIFICATE_VERIFY_PEERS3 */ + ret = gnutls_certificate_verify_peers2(tlss->tls_session, &status); + if (ret != GNUTLS_E_SUCCESS) { + DEBUG(1,("TLS %s - %s\n", __location__, gnutls_strerror(ret))); + tlss->error = EIO; + tevent_req_error(req, tlss->error); + return; + } + + if (status == 0) { + if (hostname != NULL) { + need_crt_checks = true; + } +#ifdef REQUIRE_CERT_TIME_CHECKS + need_crt_checks = true; +#endif + } + + if (need_crt_checks) { + gnutls_x509_crt crt; + const gnutls_datum *cert_list; + unsigned int cert_list_size = 0; +#ifdef REQUIRE_CERT_TIME_CHECKS + time_t now = time(NULL); + time_t tret = -1; +#endif + + cert_list = gnutls_certificate_get_peers(tlss->tls_session, + &cert_list_size); + if (cert_list == NULL) { + cert_list_size = 0; + } + if (cert_list_size == 0) { + DEBUG(1,("TLS %s - cert_list_size == 0\n", + __location__)); + tlss->error = EIO; + tevent_req_error(req, tlss->error); + return; + } + + ret = gnutls_x509_crt_init(&crt); + if (ret != GNUTLS_E_SUCCESS) { + DEBUG(1,("TLS %s - %s\n", __location__, + gnutls_strerror(ret))); + tlss->error = EIO; + tevent_req_error(req, tlss->error); + return; + } + ret = gnutls_x509_crt_import(crt, + &cert_list[0], + GNUTLS_X509_FMT_DER); + if (ret != GNUTLS_E_SUCCESS) { + DEBUG(1,("TLS %s - %s\n", __location__, + gnutls_strerror(ret))); + gnutls_x509_crt_deinit(crt); + tlss->error = EIO; + tevent_req_error(req, tlss->error); + return; + } + + if (hostname != NULL) { + ret = gnutls_x509_crt_check_hostname(crt, + hostname); + if (ret == 0) { + status |= GNUTLS_CERT_INVALID; + status |= GNUTLS_CERT_UNEXPECTED_OWNER; + } + } + +#ifndef HAVE_DECL_GNUTLS_CERT_NOT_ACTIVATED + /* + * GNUTLS_CERT_NOT_ACTIVATED is defined by ourself + */ + tret = gnutls_x509_crt_get_activation_time(crt); + if ((tret == -1) || (now > tret)) { + status |= GNUTLS_CERT_INVALID; + status |= GNUTLS_CERT_NOT_ACTIVATED; + } +#endif +#ifndef HAVE_DECL_GNUTLS_CERT_EXPIRED + /* + * GNUTLS_CERT_EXPIRED is defined by ourself + */ + tret = gnutls_certificate_expiration_time_peers(tlss->tls_session); + if ((tret == -1) || (now > tret)) { + status |= GNUTLS_CERT_INVALID; + status |= GNUTLS_CERT_EXPIRED; + } +#endif + gnutls_x509_crt_deinit(crt); + } +#endif + + if (status != 0) { + DEBUG(1,("TLS %s - check failed for " + "verify_peer[%s] and peer_name[%s] " + "status 0x%x (%s%s%s%s%s%s%s%s)\n", + __location__, + tls_verify_peer_string(tlss->verify_peer), + tlss->peer_name, + status, + status & GNUTLS_CERT_INVALID ? "invalid " : "", + status & GNUTLS_CERT_REVOKED ? "revoked " : "", + status & GNUTLS_CERT_SIGNER_NOT_FOUND ? + "signer_not_found " : "", + status & GNUTLS_CERT_SIGNER_NOT_CA ? + "signer_not_ca " : "", + status & GNUTLS_CERT_INSECURE_ALGORITHM ? + "insecure_algorithm " : "", + status & GNUTLS_CERT_NOT_ACTIVATED ? + "not_activated " : "", + status & GNUTLS_CERT_EXPIRED ? + "expired " : "", + status & GNUTLS_CERT_UNEXPECTED_OWNER ? + "unexptected_owner " : "")); + tlss->error = EINVAL; + tevent_req_error(req, tlss->error); + return; + } + } + tevent_req_done(req); #else /* ENABLE_GNUTLS */ tevent_req_error(req, ENOSYS); diff --git a/source4/lib/tls/wscript b/source4/lib/tls/wscript index 7985c6f43f1..3fd8acaff18 100644 --- a/source4/lib/tls/wscript +++ b/source4/lib/tls/wscript @@ -39,6 +39,11 @@ def configure(conf): conf.CHECK_FUNCS_IN('gnutls_global_init', 'gnutls', headers='gnutls/gnutls.h') + conf.CHECK_FUNCS_IN('gnutls_certificate_verify_peers3', 'gnutls', + headers='gnutls/gnutls.h') + conf.CHECK_DECLS('GNUTLS_CERT_EXPIRED GNUTLS_CERT_NOT_ACTIVATED GNUTLS_CERT_UNEXPECTED_OWNER', + headers='gnutls/gnutls.h gnutls/x509.h') + conf.CHECK_VARIABLE('gnutls_x509_crt_set_version', headers='gnutls/gnutls.h gnutls/x509.h', define='HAVE_GNUTLS_X509_CRT_SET_VERSION', diff --git a/source4/libcli/ldap/ldap_client.c b/source4/libcli/ldap/ldap_client.c index b11490f4041..f585abded56 100644 --- a/source4/libcli/ldap/ldap_client.c +++ b/source4/libcli/ldap/ldap_client.c @@ -475,6 +475,8 @@ _PUBLIC_ struct composite_context *ldap_connect_send(struct ldap_connection *con ca_file, crl_file, tls_priority, + TLS_VERIFY_PEER_NO_CHECK, + NULL, &state->tls_params); if (!NT_STATUS_IS_OK(status)) { composite_error(result, status); diff --git a/source4/librpc/rpc/dcerpc_roh.c b/source4/librpc/rpc/dcerpc_roh.c index 61a22a79944..c4842fb8cb6 100644 --- a/source4/librpc/rpc/dcerpc_roh.c +++ b/source4/librpc/rpc/dcerpc_roh.c @@ -187,6 +187,8 @@ struct tevent_req *dcerpc_pipe_open_roh_send(struct dcecli_connection *conn, if (use_tls) { status = tstream_tls_params_client(state->roh, NULL, NULL, lpcfg_tls_priority(lp_ctx), + TLS_VERIFY_PEER_NO_CHECK, + NULL, &state->tls_params); if (!NT_STATUS_IS_OK(status)) { DEBUG(0,("%s: Failed tstream_tls_params_client - %s\n",