#include "system/network.h"
#include "system/filesys.h"
#include "system/time.h"
+#include "lib/util/util_file.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"
+#include "lib/param/param.h"
-#if ENABLE_GNUTLS
#include <gnutls/gnutls.h>
#include <gnutls/x509.h>
+#include "lib/crypto/gnutls_helpers.h"
#define DH_BITS 2048
-#if defined(HAVE_GNUTLS_DATUM) && !defined(HAVE_GNUTLS_DATUM_T)
-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) {
struct tstream_context *plain_stream;
int error;
-#if ENABLE_GNUTLS
gnutls_session_t tls_session;
-#endif /* ENABLE_GNUTLS */
+
+ bool is_server;
enum tls_verify_peer_state verify_peer;
const char *peer_name;
+ DATA_BLOB channel_bindings;
+
struct tevent_context *current_ev;
struct tevent_immediate *retry_im;
+ struct {
+ struct tevent_req *mgmt_req;
+ } waiting_flush;
+
struct {
uint8_t *buf;
off_t ofs;
struct iovec iov;
struct tevent_req *subreq;
- struct tevent_immediate *im;
} push;
struct {
tstream_context_data(stream,
struct tstream_tls);
+ if (tlss->push.subreq == NULL && tlss->pull.subreq == NULL) {
+ if (tlss->waiting_flush.mgmt_req != NULL) {
+ struct tevent_req *req = tlss->waiting_flush.mgmt_req;
+
+ tlss->waiting_flush.mgmt_req = NULL;
+
+ tevent_req_done(req);
+ return;
+ }
+ }
+
if (tlss->disconnect.req) {
tstream_tls_retry_disconnect(stream);
return;
tstream_tls_retry(stream, true);
}
-#if ENABLE_GNUTLS
-static void tstream_tls_push_trigger_write(struct tevent_context *ev,
- struct tevent_immediate *im,
- void *private_data);
+static void tstream_tls_push_done(struct tevent_req *subreq);
static ssize_t tstream_tls_push_function(gnutls_transport_ptr_t ptr,
const void *buf, size_t size)
struct tstream_tls *tlss =
tstream_context_data(stream,
struct tstream_tls);
+ struct tevent_req *subreq = NULL;
uint8_t *nbuf;
size_t len;
tlss->push.buf = nbuf;
memcpy(tlss->push.buf + tlss->push.ofs, buf, len);
-
- if (tlss->push.im == NULL) {
- tlss->push.im = tevent_create_immediate(tlss);
- if (tlss->push.im == NULL) {
- errno = ENOMEM;
- return -1;
- }
- }
-
- if (tlss->push.ofs == 0) {
- /*
- * We'll do start the tstream_writev
- * in the next event cycle.
- *
- * This way we can batch all push requests,
- * if they fit into a UINT16_MAX buffer.
- *
- * This is important as gnutls_handshake()
- * had a bug in some versions e.g. 2.4.1
- * and others (See bug #7218) and it doesn't
- * handle EAGAIN.
- */
- tevent_schedule_immediate(tlss->push.im,
- tlss->current_ev,
- tstream_tls_push_trigger_write,
- stream);
- }
-
tlss->push.ofs += len;
- return len;
-}
-
-static void tstream_tls_push_done(struct tevent_req *subreq);
-
-static void tstream_tls_push_trigger_write(struct tevent_context *ev,
- struct tevent_immediate *im,
- void *private_data)
-{
- struct tstream_context *stream =
- talloc_get_type_abort(private_data,
- struct tstream_context);
- struct tstream_tls *tlss =
- tstream_context_data(stream,
- struct tstream_tls);
- struct tevent_req *subreq;
-
- if (tlss->push.subreq) {
- /* nothing todo */
- return;
- }
tlss->push.iov.iov_base = (char *)tlss->push.buf;
tlss->push.iov.iov_len = tlss->push.ofs;
tlss->plain_stream,
&tlss->push.iov, 1);
if (subreq == NULL) {
- tlss->error = ENOMEM;
- tstream_tls_retry(stream, false);
- return;
+ errno = ENOMEM;
+ return -1;
}
tevent_req_set_callback(subreq, tstream_tls_push_done, stream);
tlss->push.subreq = subreq;
+ return len;
}
static void tstream_tls_push_done(struct tevent_req *subreq)
tstream_tls_retry(stream, false);
}
-#endif /* ENABLE_GNUTLS */
static int tstream_tls_destructor(struct tstream_tls *tlss)
{
-#if ENABLE_GNUTLS
if (tlss->tls_session) {
gnutls_deinit(tlss->tls_session);
tlss->tls_session = NULL;
}
-#endif /* ENABLE_GNUTLS */
+
return 0;
}
return -1;
}
-#if ENABLE_GNUTLS
ret = gnutls_record_check_pending(tlss->tls_session);
ret += tlss->read.left;
-#else /* ENABLE_GNUTLS */
- errno = ENOSYS;
- ret = -1;
-#endif /* ENABLE_GNUTLS */
+
return ret;
}
struct tstream_tls_readv_state *state;
tlss->read.req = NULL;
+
+ if (tlss->current_ev != ev) {
+ SMB_ASSERT(tlss->push.subreq == NULL);
+ SMB_ASSERT(tlss->pull.subreq == NULL);
+ }
+
tlss->current_ev = ev;
req = tevent_req_create(mem_ctx, &state,
tstream_context_data(stream,
struct tstream_tls);
struct tevent_req *req = tlss->read.req;
-#if ENABLE_GNUTLS
int ret;
if (tlss->error != 0) {
tlss->read.left = ret;
tstream_tls_readv_crypt_next(req);
-#else /* ENABLE_GNUTLS */
- tevent_req_error(req, ENOSYS);
-#endif /* ENABLE_GNUTLS */
}
static int tstream_tls_readv_recv(struct tevent_req *req,
struct tstream_tls_writev_state *state;
tlss->write.req = NULL;
+
+ if (tlss->current_ev != ev) {
+ SMB_ASSERT(tlss->push.subreq == NULL);
+ SMB_ASSERT(tlss->pull.subreq == NULL);
+ }
+
tlss->current_ev = ev;
req = tevent_req_create(mem_ctx, &state,
tstream_context_data(stream,
struct tstream_tls);
struct tevent_req *req = tlss->write.req;
-#if ENABLE_GNUTLS
int ret;
if (tlss->error != 0) {
}
tstream_tls_writev_crypt_next(req);
-#else /* ENABLE_GNUTLS */
- tevent_req_error(req, ENOSYS);
-#endif /* ENABLE_GNUTLS */
}
static int tstream_tls_writev_recv(struct tevent_req *req,
struct tstream_tls_disconnect_state *state;
tlss->disconnect.req = NULL;
+
+ if (tlss->current_ev != ev) {
+ SMB_ASSERT(tlss->push.subreq == NULL);
+ SMB_ASSERT(tlss->pull.subreq == NULL);
+ }
+
tlss->current_ev = ev;
req = tevent_req_create(mem_ctx, &state,
tstream_context_data(stream,
struct tstream_tls);
struct tevent_req *req = tlss->disconnect.req;
-#if ENABLE_GNUTLS
int ret;
if (tlss->error != 0) {
return;
}
+ if (tlss->push.subreq != NULL || tlss->pull.subreq != NULL) {
+ tlss->waiting_flush.mgmt_req = req;
+ return;
+ }
+
tevent_req_done(req);
-#else /* ENABLE_GNUTLS */
- tevent_req_error(req, ENOSYS);
-#endif /* ENABLE_GNUTLS */
}
static int tstream_tls_disconnect_recv(struct tevent_req *req,
.disconnect_recv = tstream_tls_disconnect_recv,
};
-struct tstream_tls_params {
-#if ENABLE_GNUTLS
+struct tstream_tls_params_internal {
gnutls_certificate_credentials_t x509_cred;
gnutls_dh_params_t dh_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)
+struct tstream_tls_params {
+ struct tstream_tls_params_internal *internal;
+};
+
+static int tstream_tls_params_internal_destructor(struct tstream_tls_params_internal *tlsp)
{
-#if ENABLE_GNUTLS
if (tlsp->x509_cred) {
gnutls_certificate_free_credentials(tlsp->x509_cred);
tlsp->x509_cred = NULL;
gnutls_dh_params_deinit(tlsp->dh_params);
tlsp->dh_params = NULL;
}
-#endif /* ENABLE_GNUTLS */
+
return 0;
}
-bool tstream_tls_params_enabled(struct tstream_tls_params *tlsp)
+bool tstream_tls_params_enabled(struct tstream_tls_params *tls_params)
{
+ struct tstream_tls_params_internal *tlsp = tls_params->internal;
+
return tlsp->tls_enabled;
}
+static NTSTATUS tstream_tls_setup_channel_bindings(struct tstream_tls *tlss)
+{
+ gnutls_datum_t cb = { .size = 0 };
+ int ret;
+
+#ifdef HAVE_GNUTLS_CB_TLS_SERVER_END_POINT
+ ret = gnutls_session_channel_binding(tlss->tls_session,
+ GNUTLS_CB_TLS_SERVER_END_POINT,
+ &cb);
+#else /* not HAVE_GNUTLS_CB_TLS_SERVER_END_POINT */
+ ret = legacy_gnutls_server_end_point_cb(tlss->tls_session,
+ tlss->is_server,
+ &cb);
+#endif /* not HAVE_GNUTLS_CB_TLS_SERVER_END_POINT */
+ if (ret != GNUTLS_E_SUCCESS) {
+ return gnutls_error_to_ntstatus(ret,
+ NT_STATUS_CRYPTO_SYSTEM_INVALID);
+ }
+
+ if (cb.size != 0) {
+ /*
+ * Looking at the OpenLDAP implementation
+ * for LDAP_OPT_X_SASL_CBINDING_TLS_ENDPOINT
+ * revealed that we need to prefix it with
+ * 'tls-server-end-point:'
+ */
+ const char endpoint_prefix[] = "tls-server-end-point:";
+ size_t prefix_size = strlen(endpoint_prefix);
+ size_t size = prefix_size + cb.size;
+
+ tlss->channel_bindings = data_blob_talloc_named(tlss, NULL, size,
+ "tls_channel_bindings");
+ if (tlss->channel_bindings.data == NULL) {
+ gnutls_free(cb.data);
+ return NT_STATUS_NO_MEMORY;
+ }
+ memcpy(tlss->channel_bindings.data, endpoint_prefix, prefix_size);
+ memcpy(tlss->channel_bindings.data + prefix_size, cb.data, cb.size);
+ gnutls_free(cb.data);
+ }
+
+ return NT_STATUS_OK;
+}
+
+const DATA_BLOB *tstream_tls_channel_bindings(struct tstream_context *tls_tstream)
+{
+ struct tstream_tls *tlss =
+ talloc_get_type(_tstream_context_data(tls_tstream),
+ struct tstream_tls);
+
+ if (tlss == NULL) {
+ return NULL;
+ }
+
+ return &tlss->channel_bindings;
+}
+
NTSTATUS tstream_tls_params_client(TALLOC_CTX *mem_ctx,
+ bool system_cas,
+ const char * const *ca_dirs,
const char *ca_file,
const char *crl_file,
const char *tls_priority,
const char *peer_name,
struct tstream_tls_params **_tlsp)
{
-#if ENABLE_GNUTLS
- struct tstream_tls_params *tlsp;
+ struct tstream_tls_params *__tlsp = NULL;
+ struct tstream_tls_params_internal *tlsp = NULL;
+ bool got_ca = false;
+ size_t i;
int ret;
- ret = gnutls_global_init();
- if (ret != GNUTLS_E_SUCCESS) {
- DEBUG(0,("TLS %s - %s\n", __location__, gnutls_strerror(ret)));
- return NT_STATUS_NOT_SUPPORTED;
+ __tlsp = talloc_zero(mem_ctx, struct tstream_tls_params);
+ if (__tlsp == NULL) {
+ return NT_STATUS_NO_MEMORY;
}
- tlsp = talloc_zero(mem_ctx, struct tstream_tls_params);
- NT_STATUS_HAVE_NO_MEMORY(tlsp);
-
- talloc_set_destructor(tlsp, tstream_tls_params_destructor);
+ tlsp = talloc_zero(__tlsp, struct tstream_tls_params_internal);
+ if (tlsp == NULL) {
+ TALLOC_FREE(__tlsp);
+ return NT_STATUS_NO_MEMORY;
+ }
+ talloc_set_destructor(tlsp, tstream_tls_params_internal_destructor);
+ __tlsp->internal = tlsp;
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);
+ 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);
+ 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);
+ TALLOC_FREE(__tlsp);
return NT_STATUS_NO_MEMORY;
}
+ if (system_cas) {
+ ret = gnutls_certificate_set_x509_system_trust(tlsp->x509_cred);
+ if (ret < 0) {
+ DBG_ERR("gnutls_certificate_set_x509_system_trust() - %s\n",
+ gnutls_strerror(ret));
+ TALLOC_FREE(__tlsp);
+ return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
+ }
+ if (ret > 0) {
+ got_ca = true;
+ }
+ }
+
+ for (i = 0; ca_dirs != NULL && ca_dirs[i] != NULL; i++) {
+ const char *ca_dir = ca_dirs[i];
+
+ if (!directory_exist(ca_dir)) {
+ continue;
+ }
+
+ ret = gnutls_certificate_set_x509_trust_dir(tlsp->x509_cred,
+ ca_dir,
+ GNUTLS_X509_FMT_PEM);
+ if (ret < 0) {
+ DBG_ERR("gnutls_certificate_set_x509_trust_dir(%s) - %s\n",
+ ca_dir, gnutls_strerror(ret));
+ TALLOC_FREE(__tlsp);
+ return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
+ }
+ if (ret > 0) {
+ got_ca = true;
+ }
+ }
+
if (ca_file && *ca_file && file_exist(ca_file)) {
ret = gnutls_certificate_set_x509_trust_file(tlsp->x509_cred,
ca_file,
if (ret < 0) {
DEBUG(0,("TLS failed to initialise cafile %s - %s\n",
ca_file, gnutls_strerror(ret)));
- talloc_free(tlsp);
+ 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);
+ if (ret > 0) {
+ got_ca = true;
+ }
+ }
+
+ if (!got_ca && tlsp->verify_peer >= TLS_VERIFY_PEER_CA_ONLY) {
+ D_ERR("TLS: 'tls verify peer = %s' requires "
+ "'tls trust system cas', "
+ "'tls ca directories' or "
+ "'tls cafile'\n",
+ tls_verify_peer_string(tlsp->verify_peer));
+ TALLOC_FREE(__tlsp);
return NT_STATUS_INVALID_PARAMETER_MIX;
}
if (ret < 0) {
DEBUG(0,("TLS failed to initialise crlfile %s - %s\n",
crl_file, gnutls_strerror(ret)));
- talloc_free(tlsp);
+ TALLOC_FREE(__tlsp);
return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
}
} else if (tlsp->verify_peer >= TLS_VERIFY_PEER_AS_STRICT_AS_POSSIBLE) {
"with 'tls verify peer = %s'\n",
crl_file,
tls_verify_peer_string(tlsp->verify_peer)));
- talloc_free(tlsp);
+ TALLOC_FREE(__tlsp);
return NT_STATUS_INVALID_PARAMETER_MIX;
}
tlsp->tls_priority = talloc_strdup(tlsp, tls_priority);
if (tlsp->tls_priority == NULL) {
- talloc_free(tlsp);
+ TALLOC_FREE(__tlsp);
return NT_STATUS_NO_MEMORY;
}
tlsp->tls_enabled = true;
- *_tlsp = tlsp;
+ *_tlsp = __tlsp;
+ return NT_STATUS_OK;
+}
+
+NTSTATUS tstream_tls_params_client_lpcfg(TALLOC_CTX *mem_ctx,
+ struct loadparm_context *lp_ctx,
+ const char *peer_name,
+ struct tstream_tls_params **tlsp)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ bool system_cas = false;
+ const char * const *ca_dirs = NULL;
+ const char *ptr = NULL;
+ char *ca_file = NULL;
+ char *crl_file = NULL;
+ const char *tls_priority = NULL;
+ enum tls_verify_peer_state verify_peer =
+ TLS_VERIFY_PEER_AS_STRICT_AS_POSSIBLE;
+ NTSTATUS status;
+
+ system_cas = lpcfg_tls_trust_system_cas(lp_ctx);
+ ca_dirs = lpcfg_tls_ca_directories(lp_ctx);
+
+ ptr = lpcfg__tls_cafile(lp_ctx);
+ if (ptr != NULL) {
+ ca_file = lpcfg_tls_cafile(frame, lp_ctx);
+ if (ca_file == NULL) {
+ TALLOC_FREE(frame);
+ return NT_STATUS_NO_MEMORY;
+ }
+ }
+
+ ptr = lpcfg__tls_crlfile(lp_ctx);
+ if (ptr != NULL) {
+ crl_file = lpcfg_tls_crlfile(frame, lp_ctx);
+ if (crl_file == NULL) {
+ TALLOC_FREE(frame);
+ return NT_STATUS_NO_MEMORY;
+ }
+ }
+
+ tls_priority = lpcfg_tls_priority(lp_ctx);
+ verify_peer = lpcfg_tls_verify_peer(lp_ctx);
+
+ status = tstream_tls_params_client(mem_ctx,
+ system_cas,
+ ca_dirs,
+ ca_file,
+ crl_file,
+ tls_priority,
+ verify_peer,
+ peer_name,
+ tlsp);
+ TALLOC_FREE(frame);
+ return status;
+}
+
+static NTSTATUS tstream_tls_prepare_gnutls(struct tstream_tls_params *_tlsp,
+ struct tstream_tls *tlss)
+{
+ struct tstream_tls_params_internal *tlsp = NULL;
+ int ret;
+ unsigned int flags;
+ const char *hostname = NULL;
+
+ if (tlss->is_server) {
+ flags = GNUTLS_SERVER;
+ } else {
+ flags = GNUTLS_CLIENT;
+ /*
+ * tls_tstream can't properly handle 'New Session Ticket'
+ * messages sent 'after' the client sends the 'Finished'
+ * message. GNUTLS_NO_TICKETS was introduced in GnuTLS 3.5.6.
+ * This flag is to indicate the session Flag session should not
+ * use resumption with session tickets.
+ */
+ flags |= GNUTLS_NO_TICKETS;
+ }
+
+ /*
+ * Note we need to make sure x509_cred and dh_params
+ * from tstream_tls_params_internal stay alive for
+ * the whole lifetime of this session!
+ *
+ * See 'man gnutls_credentials_set' and
+ * 'man gnutls_certificate_set_dh_params'.
+ *
+ * Note: here we use talloc_reference() in a way
+ * that does not expose it to the caller.
+ */
+ tlsp = talloc_reference(tlss, _tlsp->internal);
+ if (tlsp == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ tlss->verify_peer = tlsp->verify_peer;
+ if (tlsp->peer_name != NULL) {
+ bool ip = is_ipaddress(tlsp->peer_name);
+
+ tlss->peer_name = talloc_strdup(tlss, tlsp->peer_name);
+ if (tlss->peer_name == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ if (!ip) {
+ hostname = tlss->peer_name;
+ }
+
+ if (tlss->verify_peer < TLS_VERIFY_PEER_CA_AND_NAME) {
+ hostname = NULL;
+ }
+ }
+
+ if (tlss->current_ev != NULL) {
+ tlss->retry_im = tevent_create_immediate(tlss);
+ if (tlss->retry_im == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ }
+
+ ret = gnutls_init(&tlss->tls_session, flags);
+ if (ret != GNUTLS_E_SUCCESS) {
+ return gnutls_error_to_ntstatus(ret,
+ NT_STATUS_CRYPTO_SYSTEM_INVALID);
+ }
+
+ ret = gnutls_set_default_priority(tlss->tls_session);
+ if (ret != GNUTLS_E_SUCCESS) {
+ return gnutls_error_to_ntstatus(ret,
+ NT_STATUS_CRYPTO_SYSTEM_INVALID);
+ }
+
+ if (strlen(tlsp->tls_priority) > 0) {
+ const char *error_pos = NULL;
+
+ ret = gnutls_priority_set_direct(tlss->tls_session,
+ tlsp->tls_priority,
+ &error_pos);
+ if (ret != GNUTLS_E_SUCCESS) {
+ return gnutls_error_to_ntstatus(ret,
+ NT_STATUS_CRYPTO_SYSTEM_INVALID);
+ }
+ }
+
+ ret = gnutls_credentials_set(tlss->tls_session,
+ GNUTLS_CRD_CERTIFICATE,
+ tlsp->x509_cred);
+ if (ret != GNUTLS_E_SUCCESS) {
+ return gnutls_error_to_ntstatus(ret,
+ NT_STATUS_CRYPTO_SYSTEM_INVALID);
+ }
+
+ if (hostname != NULL) {
+ ret = gnutls_server_name_set(tlss->tls_session,
+ GNUTLS_NAME_DNS,
+ hostname,
+ strlen(hostname));
+ if (ret != GNUTLS_E_SUCCESS) {
+ return gnutls_error_to_ntstatus(ret,
+ NT_STATUS_CRYPTO_SYSTEM_INVALID);
+ }
+ }
+
+ if (tlss->is_server) {
+ gnutls_certificate_server_set_request(tlss->tls_session,
+ GNUTLS_CERT_REQUEST);
+ gnutls_dh_set_prime_bits(tlss->tls_session, DH_BITS);
+ }
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS tstream_tls_verify_peer(struct tstream_tls *tlss)
+{
+ unsigned int status = UINT32_MAX;
+ bool ip = true;
+ const char *hostname = NULL;
+ int ret;
+
+ if (tlss->verify_peer == TLS_VERIFY_PEER_NO_CHECK) {
+ return NT_STATUS_OK;
+ }
+
+ 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));
+ return NT_STATUS_IMAGE_CERT_REVOKED;
+ }
+ }
+
+ ret = gnutls_certificate_verify_peers3(tlss->tls_session,
+ hostname,
+ &status);
+ if (ret != GNUTLS_E_SUCCESS) {
+ return gnutls_error_to_ntstatus(ret,
+ NT_STATUS_CRYPTO_SYSTEM_INVALID);
+ }
+
+ 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 ?
+ "unexpected_owner " : ""));
+ return NT_STATUS_IMAGE_CERT_REVOKED;
+ }
+
return NT_STATUS_OK;
-#else /* ENABLE_GNUTLS */
- return NT_STATUS_NOT_IMPLEMENTED;
-#endif /* ENABLE_GNUTLS */
}
struct tstream_tls_connect_state {
struct tevent_req *_tstream_tls_connect_send(TALLOC_CTX *mem_ctx,
struct tevent_context *ev,
struct tstream_context *plain_stream,
- struct tstream_tls_params *tls_params,
+ struct tstream_tls_params *_tls_params,
const char *location)
{
struct tevent_req *req;
struct tstream_tls_connect_state *state;
-#if ENABLE_GNUTLS
- const char *error_pos;
struct tstream_tls *tlss;
- int ret;
-#endif /* ENABLE_GNUTLS */
+ NTSTATUS status;
req = tevent_req_create(mem_ctx, &state,
struct tstream_tls_connect_state);
return NULL;
}
-#if ENABLE_GNUTLS
state->tls_stream = tstream_context_create(state,
&tstream_tls_ops,
&tlss,
}
ZERO_STRUCTP(tlss);
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->is_server = false;
tlss->current_ev = ev;
- tlss->retry_im = tevent_create_immediate(tlss);
- if (tevent_req_nomem(tlss->retry_im, req)) {
- return tevent_req_post(req, ev);
- }
- ret = gnutls_init(&tlss->tls_session, GNUTLS_CLIENT);
- if (ret != GNUTLS_E_SUCCESS) {
- DEBUG(0,("TLS %s - %s\n", __location__, gnutls_strerror(ret)));
- tevent_req_error(req, EINVAL);
- return tevent_req_post(req, ev);
- }
-
- ret = gnutls_priority_set_direct(tlss->tls_session,
- tls_params->tls_priority,
- &error_pos);
- if (ret != GNUTLS_E_SUCCESS) {
- DEBUG(0,("TLS %s - %s. Check 'tls priority' option at '%s'\n",
- __location__, gnutls_strerror(ret), error_pos));
- tevent_req_error(req, EINVAL);
+ status = tstream_tls_prepare_gnutls(_tls_params, tlss);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NO_MEMORY)) {
+ tevent_req_oom(req);
return tevent_req_post(req, ev);
}
-
- ret = gnutls_credentials_set(tlss->tls_session,
- GNUTLS_CRD_CERTIFICATE,
- tls_params->x509_cred);
- if (ret != GNUTLS_E_SUCCESS) {
- DEBUG(0,("TLS %s - %s\n", __location__, gnutls_strerror(ret)));
+ if (!NT_STATUS_IS_OK(status)) {
tevent_req_error(req, EINVAL);
return tevent_req_post(req, ev);
}
(gnutls_pull_func)tstream_tls_pull_function);
gnutls_transport_set_push_function(tlss->tls_session,
(gnutls_push_func)tstream_tls_push_function);
-#if GNUTLS_VERSION_MAJOR < 3
- gnutls_transport_set_lowat(tlss->tls_session, 0);
-#endif
tlss->handshake.req = req;
tstream_tls_retry_handshake(state->tls_stream);
}
return req;
-#else /* ENABLE_GNUTLS */
- tevent_req_error(req, ENOSYS);
- return tevent_req_post(req, ev);
-#endif /* ENABLE_GNUTLS */
}
int tstream_tls_connect_recv(struct tevent_req *req,
const char *tls_priority,
struct tstream_tls_params **_tlsp)
{
- struct tstream_tls_params *tlsp;
-#if ENABLE_GNUTLS
+ struct tstream_tls_params *__tlsp = NULL;
+ struct tstream_tls_params_internal *tlsp = NULL;
int ret;
struct stat st;
if (!enabled || key_file == NULL || *key_file == 0) {
- tlsp = talloc_zero(mem_ctx, struct tstream_tls_params);
- NT_STATUS_HAVE_NO_MEMORY(tlsp);
- talloc_set_destructor(tlsp, tstream_tls_params_destructor);
+ __tlsp = talloc_zero(mem_ctx, struct tstream_tls_params);
+ if (__tlsp == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ tlsp = talloc_zero(__tlsp, struct tstream_tls_params_internal);
+ if (tlsp == NULL) {
+ TALLOC_FREE(__tlsp);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ talloc_set_destructor(tlsp, tstream_tls_params_internal_destructor);
+ __tlsp->internal = tlsp;
tlsp->tls_enabled = false;
- *_tlsp = tlsp;
+ *_tlsp = __tlsp;
return NT_STATUS_OK;
}
- ret = gnutls_global_init();
- if (ret != GNUTLS_E_SUCCESS) {
- DEBUG(0,("TLS %s - %s\n", __location__, gnutls_strerror(ret)));
- return NT_STATUS_NOT_SUPPORTED;
+ __tlsp = talloc_zero(mem_ctx, struct tstream_tls_params);
+ if (__tlsp == NULL) {
+ return NT_STATUS_NO_MEMORY;
}
- tlsp = talloc_zero(mem_ctx, struct tstream_tls_params);
- NT_STATUS_HAVE_NO_MEMORY(tlsp);
+ tlsp = talloc_zero(__tlsp, struct tstream_tls_params_internal);
+ if (tlsp == NULL) {
+ TALLOC_FREE(__tlsp);
+ return NT_STATUS_NO_MEMORY;
+ }
- talloc_set_destructor(tlsp, tstream_tls_params_destructor);
+ talloc_set_destructor(tlsp, tstream_tls_params_internal_destructor);
+ __tlsp->internal = tlsp;
if (!file_exist(ca_file)) {
tls_cert_generate(tlsp, dns_host_name,
key_file,
(unsigned int)st.st_uid, geteuid(),
(unsigned int)(st.st_mode & 0777), 0600));
+ TALLOC_FREE(__tlsp);
return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
}
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);
+ TALLOC_FREE(__tlsp);
return NT_STATUS_NO_MEMORY;
}
if (ret < 0) {
DEBUG(0,("TLS failed to initialise cafile %s - %s\n",
ca_file, gnutls_strerror(ret)));
- talloc_free(tlsp);
+ TALLOC_FREE(__tlsp);
return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
}
}
if (ret < 0) {
DEBUG(0,("TLS failed to initialise crlfile %s - %s\n",
crl_file, gnutls_strerror(ret)));
- talloc_free(tlsp);
+ TALLOC_FREE(__tlsp);
return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
}
}
if (ret != GNUTLS_E_SUCCESS) {
DEBUG(0,("TLS failed to initialise certfile %s and keyfile %s - %s\n",
cert_file, key_file, gnutls_strerror(ret)));
- talloc_free(tlsp);
+ TALLOC_FREE(__tlsp);
return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
}
ret = gnutls_dh_params_init(&tlsp->dh_params);
if (ret != GNUTLS_E_SUCCESS) {
DEBUG(0,("TLS %s - %s\n", __location__, gnutls_strerror(ret)));
- talloc_free(tlsp);
+ TALLOC_FREE(__tlsp);
return NT_STATUS_NO_MEMORY;
}
if (!dhparms.data) {
DEBUG(0,("TLS failed to read DH Parms from %s - %d:%s\n",
dhp_file, errno, strerror(errno)));
- talloc_free(tlsp);
+ TALLOC_FREE(__tlsp);
return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
}
dhparms.size = size;
if (ret != GNUTLS_E_SUCCESS) {
DEBUG(0,("TLS failed to import pkcs3 %s - %s\n",
dhp_file, gnutls_strerror(ret)));
- talloc_free(tlsp);
+ TALLOC_FREE(__tlsp);
return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
}
} else {
if (ret != GNUTLS_E_SUCCESS) {
DEBUG(0,("TLS failed to generate dh_params - %s\n",
gnutls_strerror(ret)));
- talloc_free(tlsp);
+ TALLOC_FREE(__tlsp);
return NT_STATUS_INTERNAL_ERROR;
}
}
tlsp->tls_priority = talloc_strdup(tlsp, tls_priority);
if (tlsp->tls_priority == NULL) {
- talloc_free(tlsp);
+ TALLOC_FREE(__tlsp);
return NT_STATUS_NO_MEMORY;
}
tlsp->tls_enabled = true;
-#else /* ENABLE_GNUTLS */
- tlsp = talloc_zero(mem_ctx, struct tstream_tls_params);
- NT_STATUS_HAVE_NO_MEMORY(tlsp);
- talloc_set_destructor(tlsp, tstream_tls_params_destructor);
- tlsp->tls_enabled = false;
-#endif /* ENABLE_GNUTLS */
-
- *_tlsp = tlsp;
+ *_tlsp = __tlsp;
return NT_STATUS_OK;
}
struct tevent_req *_tstream_tls_accept_send(TALLOC_CTX *mem_ctx,
struct tevent_context *ev,
struct tstream_context *plain_stream,
- struct tstream_tls_params *tlsp,
+ struct tstream_tls_params *_tlsp,
const char *location)
{
struct tevent_req *req;
struct tstream_tls_accept_state *state;
struct tstream_tls *tlss;
-#if ENABLE_GNUTLS
- const char *error_pos;
- int ret;
-#endif /* ENABLE_GNUTLS */
+ NTSTATUS status;
req = tevent_req_create(mem_ctx, &state,
struct tstream_tls_accept_state);
}
ZERO_STRUCTP(tlss);
talloc_set_destructor(tlss, tstream_tls_destructor);
-
-#if ENABLE_GNUTLS
tlss->plain_stream = plain_stream;
-
+ tlss->is_server = true;
tlss->current_ev = ev;
- tlss->retry_im = tevent_create_immediate(tlss);
- if (tevent_req_nomem(tlss->retry_im, req)) {
- return tevent_req_post(req, ev);
- }
-
- ret = gnutls_init(&tlss->tls_session, GNUTLS_SERVER);
- if (ret != GNUTLS_E_SUCCESS) {
- DEBUG(0,("TLS %s - %s\n", __location__, gnutls_strerror(ret)));
- tevent_req_error(req, EINVAL);
- return tevent_req_post(req, ev);
- }
- ret = gnutls_priority_set_direct(tlss->tls_session,
- tlsp->tls_priority,
- &error_pos);
- if (ret != GNUTLS_E_SUCCESS) {
- DEBUG(0,("TLS %s - %s. Check 'tls priority' option at '%s'\n",
- __location__, gnutls_strerror(ret), error_pos));
- tevent_req_error(req, EINVAL);
+ status = tstream_tls_prepare_gnutls(_tlsp, tlss);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NO_MEMORY)) {
+ tevent_req_oom(req);
return tevent_req_post(req, ev);
}
-
- ret = gnutls_credentials_set(tlss->tls_session, GNUTLS_CRD_CERTIFICATE,
- tlsp->x509_cred);
- if (ret != GNUTLS_E_SUCCESS) {
- DEBUG(0,("TLS %s - %s\n", __location__, gnutls_strerror(ret)));
+ if (!NT_STATUS_IS_OK(status)) {
tevent_req_error(req, EINVAL);
return tevent_req_post(req, ev);
}
- gnutls_certificate_server_set_request(tlss->tls_session,
- GNUTLS_CERT_REQUEST);
- gnutls_dh_set_prime_bits(tlss->tls_session, DH_BITS);
-
gnutls_transport_set_ptr(tlss->tls_session,
(gnutls_transport_ptr_t)state->tls_stream);
gnutls_transport_set_pull_function(tlss->tls_session,
(gnutls_pull_func)tstream_tls_pull_function);
gnutls_transport_set_push_function(tlss->tls_session,
(gnutls_push_func)tstream_tls_push_function);
-#if GNUTLS_VERSION_MAJOR < 3
- gnutls_transport_set_lowat(tlss->tls_session, 0);
-#endif
tlss->handshake.req = req;
tstream_tls_retry_handshake(state->tls_stream);
}
return req;
-#else /* ENABLE_GNUTLS */
- tevent_req_error(req, ENOSYS);
- return tevent_req_post(req, ev);
-#endif /* ENABLE_GNUTLS */
}
static void tstream_tls_retry_handshake(struct tstream_context *stream)
tstream_context_data(stream,
struct tstream_tls);
struct tevent_req *req = tlss->handshake.req;
-#if ENABLE_GNUTLS
+ NTSTATUS status;
int ret;
if (tlss->error != 0) {
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
- }
+ status = tstream_tls_verify_peer(tlss);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_IMAGE_CERT_REVOKED)) {
+ tlss->error = EINVAL;
+ tevent_req_error(req, tlss->error);
+ return;
+ }
+ if (!NT_STATUS_IS_OK(status)) {
+ tlss->error = EIO;
+ tevent_req_error(req, tlss->error);
+ return;
+ }
- 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
+ status = tstream_tls_setup_channel_bindings(tlss);
+ if (!NT_STATUS_IS_OK(status)) {
+ tlss->error = EIO;
+ tevent_req_error(req, tlss->error);
+ return;
+ }
- 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;
- }
+ if (tlss->push.subreq != NULL || tlss->pull.subreq != NULL) {
+ tlss->waiting_flush.mgmt_req = req;
+ return;
}
tevent_req_done(req);
-#else /* ENABLE_GNUTLS */
- tevent_req_error(req, ENOSYS);
-#endif /* ENABLE_GNUTLS */
}
int tstream_tls_accept_recv(struct tevent_req *req,