"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) {
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;
}
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__;
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 |=
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) {
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);
/* 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);
}
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);
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);
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;
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;
}
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);
}
/*
}
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;
}
}
dce_conn->allow_alter = true;
+ dcesrv_default_auth_state_finish_bind(call);
if (call->pkt.auth_length == 0) {
auth->auth_finished = true;
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)) {
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;
};
NTSTATUS status;
+ if (!auth->auth_started) {
+ return false;
+ }
+
if (!auth->auth_finished) {
call->fault_code = DCERPC_NCA_S_PROTO_ERROR;
return false;