#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
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 {
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;
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)
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
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)));
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)) {
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);
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);
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);