summaryrefslogtreecommitdiff
path: root/source4/rpc_server/dcerpc_server.c
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/rpc_server/dcerpc_server.c
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/rpc_server/dcerpc_server.c')
-rw-r--r--source4/rpc_server/dcerpc_server.c98
1 files changed, 95 insertions, 3 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);