diff options
Diffstat (limited to 'source4')
-rw-r--r-- | source4/rpc_server/dcerpc_server.c | 98 | ||||
-rw-r--r-- | source4/rpc_server/dcerpc_server.h | 6 | ||||
-rw-r--r-- | source4/rpc_server/dcesrv_auth.c | 108 |
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; |