1
0
mirror of https://github.com/samba-team/samba.git synced 2024-12-22 13:34:15 +03:00

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 <metze@samba.org>
Reviewed-by: Günther Deschner <gd@samba.org>
This commit is contained in:
Stefan Metzmacher 2015-12-23 16:17:04 +01:00
parent b5681c4125
commit 64a9cd2a38
5 changed files with 281 additions and 0 deletions

View File

@ -61,10 +61,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,

View File

@ -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 <gnutls/gnutls.h>
#include <gnutls/x509.h>
#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);
@ -1362,6 +1447,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);

View File

@ -53,6 +53,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',

View File

@ -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);

View File

@ -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",