summaryrefslogtreecommitdiff
path: root/source4
diff options
context:
space:
mode:
authorStefan Metzmacher <metze@samba.org>2018-11-08 14:59:58 +0100
committerJeremy Allison <jra@samba.org>2019-01-12 03:13:41 +0100
commit7bc6ec81c85eb51f5d6948b9266a7db2918892d4 (patch)
treea4d633e0509ba03e13a3cc2d356eadeb1a4cf22e /source4
parenta0b230631bcb0fd9b0299aa41711af08cc2594c3 (diff)
downloadsamba-7bc6ec81c85eb51f5d6948b9266a7db2918892d4.tar.gz
s4:rpc_server: implement security context multiplexing
There're some systems like Cisco ISE use security multiplexing without checking (via bind time feature negotiation) the server supports it. Others like VMWare View, fallback to NT4 style netlogon connections without using netlogon secure channel, which then triggers an error, with "server schannel = yes", see https://bugzilla.samba.org/show_bug.cgi?id=13464. BUG: https://bugzilla.samba.org/show_bug.cgi?id=7113 BUG: https://bugzilla.samba.org/show_bug.cgi?id=11892 Signed-off-by: Stefan Metzmacher <metze@samba.org> Reviewed-by: Jeremy Allison <jra@samba.org>
Diffstat (limited to 'source4')
-rw-r--r--source4/rpc_server/dcerpc_server.c98
-rw-r--r--source4/rpc_server/dcerpc_server.h6
-rw-r--r--source4/rpc_server/dcesrv_auth.c108
3 files changed, 199 insertions, 13 deletions
diff --git a/source4/rpc_server/dcerpc_server.c b/source4/rpc_server/dcerpc_server.c
index 75e1dfea1c7..9c5bb4f6026 100644
--- a/source4/rpc_server/dcerpc_server.c
+++ b/source4/rpc_server/dcerpc_server.c
@@ -643,6 +643,11 @@ static NTSTATUS dcesrv_endpoint_connect(struct dcesrv_context *dce_ctx,
"dcesrv",
"header signing",
true);
+ p->max_auth_states = lpcfg_parm_ulong(dce_ctx->lp_ctx,
+ NULL,
+ "dcesrv",
+ "max auth states",
+ 2049);
auth = dcesrv_auth_create(p);
if (auth == NULL) {
@@ -707,6 +712,8 @@ static void dcesrv_call_set_list(struct dcesrv_call_state *call,
static void dcesrv_call_disconnect_after(struct dcesrv_call_state *call,
const char *reason)
{
+ struct dcesrv_auth *a = NULL;
+
if (call->conn->terminate != NULL) {
return;
}
@@ -716,6 +723,10 @@ static void dcesrv_call_disconnect_after(struct dcesrv_call_state *call,
call->conn->default_auth_state->auth_invalid = true;
+ for (a = call->conn->auth_states; a != NULL; a = a->next) {
+ a->auth_invalid = true;
+ }
+
call->terminate_reason = talloc_strdup(call, reason);
if (call->terminate_reason == NULL) {
call->terminate_reason = __location__;
@@ -1126,7 +1137,10 @@ static NTSTATUS dcesrv_bind(struct dcesrv_call_state *call)
a->result = DCERPC_BIND_ACK_RESULT_NEGOTIATE_ACK;
a->reason.negotiate = 0;
if (features & DCERPC_BIND_TIME_SECURITY_CONTEXT_MULTIPLEXING) {
- /* not supported yet */
+ if (call->conn->max_auth_states != 0) {
+ a->reason.negotiate |=
+ DCERPC_BIND_TIME_SECURITY_CONTEXT_MULTIPLEXING;
+ }
}
if (features & DCERPC_BIND_TIME_KEEP_CONNECTION_ON_ORPHAN) {
a->reason.negotiate |=
@@ -2018,6 +2032,10 @@ static NTSTATUS dcesrv_process_ncacn_packet(struct dcesrv_connection *dce_conn,
NTSTATUS status;
struct dcesrv_call_state *call;
struct dcesrv_call_state *existing = NULL;
+ size_t num_auth_ctx = 0;
+ enum dcerpc_AuthType auth_type = 0;
+ enum dcerpc_AuthLevel auth_level = 0;
+ uint32_t auth_context_id = 0;
call = talloc_zero(dce_conn, struct dcesrv_call_state);
if (!call) {
@@ -2036,7 +2054,73 @@ static NTSTATUS dcesrv_process_ncacn_packet(struct dcesrv_connection *dce_conn,
talloc_steal(call, blob.data);
call->pkt = *pkt;
- call->auth_state = dce_conn->default_auth_state;
+ if (dce_conn->max_auth_states == 0) {
+ call->auth_state = dce_conn->default_auth_state;
+ } else if (call->pkt.auth_length == 0) {
+ if (call->pkt.ptype == DCERPC_PKT_REQUEST &&
+ dce_conn->default_auth_level_connect != NULL)
+ {
+ call->auth_state = dce_conn->default_auth_level_connect;
+ } else {
+ call->auth_state = dce_conn->default_auth_state;
+ }
+ }
+
+ if (call->auth_state == NULL) {
+ struct dcesrv_auth *a = NULL;
+
+ auth_type = dcerpc_get_auth_type(&blob);
+ auth_level = dcerpc_get_auth_level(&blob);
+ auth_context_id = dcerpc_get_auth_context_id(&blob);
+
+ if (call->pkt.ptype == DCERPC_PKT_REQUEST) {
+ dce_conn->default_auth_level_connect = NULL;
+ if (auth_level == DCERPC_AUTH_LEVEL_CONNECT) {
+ dce_conn->got_explicit_auth_level_connect = true;
+ }
+ }
+
+ for (a = dce_conn->auth_states; a != NULL; a = a->next) {
+ num_auth_ctx++;
+
+ if (a->auth_type != auth_type) {
+ continue;
+ }
+ if (a->auth_finished && a->auth_level != auth_level) {
+ continue;
+ }
+ if (a->auth_context_id != auth_context_id) {
+ continue;
+ }
+
+ DLIST_PROMOTE(dce_conn->auth_states, a);
+ call->auth_state = a;
+ break;
+ }
+ }
+
+ if (call->auth_state == NULL) {
+ struct dcesrv_auth *a = NULL;
+
+ if (num_auth_ctx >= dce_conn->max_auth_states) {
+ return dcesrv_fault_disconnect(call,
+ DCERPC_NCA_S_PROTO_ERROR);
+ }
+
+ a = dcesrv_auth_create(dce_conn);
+ if (a == NULL) {
+ talloc_free(call);
+ return NT_STATUS_NO_MEMORY;
+ }
+ DLIST_ADD(dce_conn->auth_states, a);
+ if (call->pkt.ptype == DCERPC_PKT_REQUEST) {
+ /*
+ * This can never be valid.
+ */
+ a->auth_invalid = true;
+ }
+ call->auth_state = a;
+ }
talloc_set_destructor(call, dcesrv_call_dequeue);
@@ -2051,7 +2135,10 @@ static NTSTATUS dcesrv_process_ncacn_packet(struct dcesrv_connection *dce_conn,
/* we have to check the signing here, before combining the
pdus */
if (call->pkt.ptype == DCERPC_PKT_REQUEST) {
- if (!call->auth_state->auth_finished) {
+ dcesrv_default_auth_state_prepare_request(call);
+
+ if (call->auth_state->auth_started &&
+ !call->auth_state->auth_finished) {
return dcesrv_fault_disconnect(call,
DCERPC_NCA_S_PROTO_ERROR);
}
@@ -2479,6 +2566,7 @@ const struct dcesrv_critical_sizes *dcerpc_module_version(void)
static void dcesrv_terminate_connection(struct dcesrv_connection *dce_conn, const char *reason)
{
struct dcesrv_context *dce_ctx = dce_conn->dce_ctx;
+ struct dcesrv_auth *a = NULL;
struct stream_connection *srv_conn;
srv_conn = talloc_get_type(dce_conn->transport.private_data,
struct stream_connection);
@@ -2492,6 +2580,10 @@ static void dcesrv_terminate_connection(struct dcesrv_connection *dce_conn, cons
dce_conn->default_auth_state->auth_invalid = true;
+ for (a = dce_conn->auth_states; a != NULL; a = a->next) {
+ a->auth_invalid = true;
+ }
+
if (dce_conn->pending_call_list == NULL) {
char *full_reason = talloc_asprintf(dce_conn, "dcesrv: %s", reason);
diff --git a/source4/rpc_server/dcerpc_server.h b/source4/rpc_server/dcerpc_server.h
index 25a3aedc2e1..50b07462cfd 100644
--- a/source4/rpc_server/dcerpc_server.h
+++ b/source4/rpc_server/dcerpc_server.h
@@ -197,6 +197,7 @@ struct dcesrv_handle {
/* hold the authentication state information */
struct dcesrv_auth {
+ struct dcesrv_auth *prev, *next;
enum dcerpc_AuthType auth_type;
enum dcerpc_AuthLevel auth_level;
uint32_t auth_context_id;
@@ -205,6 +206,7 @@ struct dcesrv_auth {
NTSTATUS (*session_key_fn)(struct dcesrv_auth *, DATA_BLOB *session_key);
bool auth_started;
bool auth_finished;
+ bool auth_audited;
bool auth_invalid;
};
@@ -288,6 +290,10 @@ struct dcesrv_connection {
/* the current authentication state */
struct dcesrv_auth *default_auth_state;
+ size_t max_auth_states;
+ struct dcesrv_auth *auth_states;
+ bool got_explicit_auth_level_connect;
+ struct dcesrv_auth *default_auth_level_connect;
bool client_hdr_signing;
bool support_hdr_signing;
bool negotiated_hdr_signing;
diff --git a/source4/rpc_server/dcesrv_auth.c b/source4/rpc_server/dcesrv_auth.c
index 192d90ba759..c71e4868436 100644
--- a/source4/rpc_server/dcesrv_auth.c
+++ b/source4/rpc_server/dcesrv_auth.c
@@ -36,7 +36,7 @@ static NTSTATUS dcesrv_auth_negotiate_hdr_signing(struct dcesrv_call_state *call
struct ncacn_packet *pkt)
{
struct dcesrv_connection *dce_conn = call->conn;
- struct dcesrv_auth *auth = call->auth_state;
+ struct dcesrv_auth *a = NULL;
if (!(call->pkt.pfc_flags & DCERPC_PFC_FLAG_SUPPORT_HEADER_SIGN)) {
return NT_STATUS_OK;
@@ -60,12 +60,20 @@ static NTSTATUS dcesrv_auth_negotiate_hdr_signing(struct dcesrv_call_state *call
pkt->pfc_flags |= DCERPC_PFC_FLAG_SUPPORT_HEADER_SIGN;
}
- if (auth->gensec_security == NULL) {
- return NT_STATUS_OK;
+ a = call->conn->default_auth_state;
+ if (a->gensec_security != NULL) {
+ gensec_want_feature(a->gensec_security,
+ GENSEC_FEATURE_SIGN_PKT_HEADER);
}
- gensec_want_feature(auth->gensec_security,
- GENSEC_FEATURE_SIGN_PKT_HEADER);
+ for (a = call->conn->auth_states; a != NULL; a = a->next) {
+ if (a->gensec_security == NULL) {
+ continue;
+ }
+
+ gensec_want_feature(a->gensec_security,
+ GENSEC_FEATURE_SIGN_PKT_HEADER);
+ }
return NT_STATUS_OK;
}
@@ -250,6 +258,68 @@ static void log_successful_dcesrv_authz_event(struct dcesrv_call_state *call)
auth_type,
transport_protection,
auth->session_info);
+
+ auth->auth_audited = true;
+}
+
+static void dcesrv_default_auth_state_finish_bind(struct dcesrv_call_state *call)
+{
+ SMB_ASSERT(call->pkt.ptype == DCERPC_PKT_BIND);
+
+ if (call->auth_state == call->conn->default_auth_state) {
+ return;
+ }
+
+ if (call->conn->default_auth_state->auth_started) {
+ return;
+ }
+
+ if (call->conn->default_auth_state->auth_invalid) {
+ return;
+ }
+
+ call->conn->default_auth_state->auth_type = DCERPC_AUTH_TYPE_NONE;
+ call->conn->default_auth_state->auth_level = DCERPC_AUTH_LEVEL_NONE;
+ call->conn->default_auth_state->auth_context_id = 0;
+ call->conn->default_auth_state->auth_started = true;
+ call->conn->default_auth_state->auth_finished = true;
+
+ /*
+ *
+ * We defer log_successful_dcesrv_authz_event()
+ * to dcesrv_default_auth_state_prepare_request()
+ *
+ * As we don't want to trigger authz_events
+ * just for alter_context requests without authentication
+ */
+}
+
+void dcesrv_default_auth_state_prepare_request(struct dcesrv_call_state *call)
+{
+ struct dcesrv_connection *dce_conn = call->conn;
+ struct dcesrv_auth *auth = call->auth_state;
+
+ if (auth->auth_audited) {
+ return;
+ }
+
+ if (call->pkt.ptype != DCERPC_PKT_REQUEST) {
+ return;
+ }
+
+ if (auth != dce_conn->default_auth_state) {
+ return;
+ }
+
+ if (auth->auth_invalid) {
+ return;
+ }
+
+ if (!auth->auth_finished) {
+ return;
+ }
+
+ log_successful_dcesrv_authz_event(call);
}
/*
@@ -338,6 +408,12 @@ NTSTATUS dcesrv_auth_complete(struct dcesrv_call_state *call, NTSTATUS status)
}
auth->auth_finished = true;
+ if (auth->auth_level == DCERPC_AUTH_LEVEL_CONNECT &&
+ !call->conn->got_explicit_auth_level_connect)
+ {
+ call->conn->default_auth_level_connect = auth;
+ }
+
if (call->pkt.ptype != DCERPC_PKT_AUTH3) {
return NT_STATUS_OK;
}
@@ -367,6 +443,7 @@ NTSTATUS dcesrv_auth_prepare_bind_ack(struct dcesrv_call_state *call, struct nca
}
dce_conn->allow_alter = true;
+ dcesrv_default_auth_state_finish_bind(call);
if (call->pkt.auth_length == 0) {
auth->auth_finished = true;
@@ -467,11 +544,6 @@ bool dcesrv_auth_alter(struct dcesrv_call_state *call)
return false;
}
- /* We can't work without an existing gensec state */
- if (auth->gensec_security == NULL) {
- return false;
- }
-
status = dcerpc_pull_auth_trailer(pkt, call, &pkt->u.alter.auth_info,
&call->in_auth_info, NULL, true);
if (!NT_STATUS_IS_OK(status)) {
@@ -479,6 +551,18 @@ bool dcesrv_auth_alter(struct dcesrv_call_state *call)
return false;
}
+ if (!auth->auth_started) {
+ bool ok;
+
+ ok = dcesrv_auth_prepare_gensec(call);
+ if (!ok) {
+ call->fault_code = DCERPC_FAULT_ACCESS_DENIED;
+ return false;
+ }
+
+ return true;
+ }
+
if (call->in_auth_info.auth_type == DCERPC_AUTH_TYPE_NONE) {
call->fault_code = DCERPC_FAULT_ACCESS_DENIED;
return false;
@@ -552,6 +636,10 @@ bool dcesrv_auth_pkt_pull(struct dcesrv_call_state *call,
};
NTSTATUS status;
+ if (!auth->auth_started) {
+ return false;
+ }
+
if (!auth->auth_finished) {
call->fault_code = DCERPC_NCA_S_PROTO_ERROR;
return false;