summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--selftest/knownfail.d/security_context_multiplexing4
-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
4 files changed, 199 insertions, 17 deletions
diff --git a/selftest/knownfail.d/security_context_multiplexing b/selftest/knownfail.d/security_context_multiplexing
deleted file mode 100644
index b5b9658af88..00000000000
--- a/selftest/knownfail.d/security_context_multiplexing
+++ /dev/null
@@ -1,4 +0,0 @@
-^samba.tests.dcerpc.raw_protocol.*.TestDCERPC_BIND.test_lsa_multi_auth.*
-^samba.tests.dcerpc.raw_protocol.*.TestDCERPC_BIND.test_spnego_multiple_auth_hdr_signing
-^samba.tests.dcerpc.raw_protocol.*.TestDCERPC_BIND.test_multiple_auth_limit
-^samba.tests.dcerpc.raw_protocol.*.TestDCERPC_BIND.test_no_auth_bind_time_sec_ctx_ignore_additional
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;