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:
parent
b5681c4125
commit
64a9cd2a38
@ -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,
|
||||
|
@ -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);
|
||||
|
@ -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',
|
||||
|
@ -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);
|
||||
|
@ -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",
|
||||
|
Loading…
Reference in New Issue
Block a user