From f2dedc629ca0d15a59de38812abb0b290fed0a37 Mon Sep 17 00:00:00 2001 From: Volker Lendecke Date: Sun, 20 Nov 2005 16:28:39 +0000 Subject: r11809: Make dcerpc_bind_auth async. This also removes dcerpc_bind_auth_password, the only user of dcerpc_bind_auth. And this was not only passwords anyway. Andrew Bartlett, as usual: Please take a close look. Thanks, Volker (This used to be commit 2ff2dae3d035af6cb0c131573cfd983fc9a58eee) --- source4/librpc/rpc/dcerpc.c | 9 +- source4/librpc/rpc/dcerpc_auth.c | 332 ++++++++++++++++++++--------------- source4/librpc/rpc/dcerpc_schannel.c | 6 +- source4/librpc/rpc/dcerpc_util.c | 6 +- 4 files changed, 203 insertions(+), 150 deletions(-) (limited to 'source4/librpc/rpc') diff --git a/source4/librpc/rpc/dcerpc.c b/source4/librpc/rpc/dcerpc.c index c7f337de99..ee06d6e2be 100644 --- a/source4/librpc/rpc/dcerpc.c +++ b/source4/librpc/rpc/dcerpc.c @@ -743,10 +743,9 @@ NTSTATUS dcerpc_auth3(struct dcerpc_connection *c, } -NTSTATUS dcerpc_init_syntaxes(const char *uuid, +NTSTATUS dcerpc_init_syntaxes(const char *uuid, uint_t version, struct dcerpc_syntax_id *syntax, - struct dcerpc_syntax_id *transfer_syntax, - uint_t version) + struct dcerpc_syntax_id *transfer_syntax) { NTSTATUS status; @@ -772,8 +771,8 @@ NTSTATUS dcerpc_bind_byuuid(struct dcerpc_pipe *p, struct dcerpc_syntax_id transfer_syntax; NTSTATUS status; - status = dcerpc_init_syntaxes(uuid, &syntax, &transfer_syntax, - version); + status = dcerpc_init_syntaxes(uuid, version, + &syntax, &transfer_syntax); if (!NT_STATUS_IS_OK(status)) { DEBUG(2,("Invalid uuid string in dcerpc_bind_byuuid\n")); return status; diff --git a/source4/librpc/rpc/dcerpc_auth.c b/source4/librpc/rpc/dcerpc_auth.c index 117112c197..29ab80da7a 100644 --- a/source4/librpc/rpc/dcerpc_auth.c +++ b/source4/librpc/rpc/dcerpc_auth.c @@ -42,8 +42,8 @@ struct composite_context *dcerpc_bind_auth_none_send(TALLOC_CTX *mem_ctx, c = talloc_zero(mem_ctx, struct composite_context); if (c == NULL) return NULL; - c->status = dcerpc_init_syntaxes(uuid, &syntax, &transfer_syntax, - version); + c->status = dcerpc_init_syntaxes(uuid, version, + &syntax, &transfer_syntax); if (!NT_STATUS_IS_OK(c->status)) { DEBUG(2,("Invalid uuid string in " "dcerpc_bind_auth_none_send\n")); @@ -70,173 +70,227 @@ NTSTATUS dcerpc_bind_auth_none(struct dcerpc_pipe *p, return dcerpc_bind_auth_none_recv(ctx); } -/* - perform a multi-part authenticated bind -*/ -static NTSTATUS dcerpc_bind_auth(struct dcerpc_pipe *p, uint8_t auth_type, uint8_t auth_level, - const char *uuid, uint_t version) -{ - NTSTATUS status; - TALLOC_CTX *tmp_ctx = talloc_new(p); +struct bind_auth_state { + struct dcerpc_pipe *pipe; DATA_BLOB credentials; - DATA_BLOB null_data_blob = data_blob(NULL, 0); + BOOL more_processing; +}; - int num_passes = 0; +static void bind_auth_recv_alter(struct composite_context *creq); - if (!p->conn->security_state.generic_state) { - status = gensec_client_start(p, &p->conn->security_state.generic_state, - p->conn->event_ctx); - if (!NT_STATUS_IS_OK(status)) goto done; +static void bind_auth_next_step(struct composite_context *c) +{ + struct bind_auth_state *state = + talloc_get_type(c->private_data, struct bind_auth_state); + struct dcerpc_security *sec = &state->pipe->conn->security_state; + struct composite_context *creq; + BOOL more_processing = False; + + c->status = gensec_update(sec->generic_state, state, + sec->auth_info->credentials, + &state->credentials); + + if (NT_STATUS_EQUAL(c->status, NT_STATUS_MORE_PROCESSING_REQUIRED)) { + more_processing = True; + c->status = NT_STATUS_OK; + } + + if (!composite_is_ok(c)) return; - status = gensec_start_mech_by_authtype(p->conn->security_state.generic_state, - auth_type, auth_level); - if (!NT_STATUS_IS_OK(status)) goto done; + if (state->credentials.length == 0) { + composite_done(c); + return; } - p->conn->security_state.auth_info = talloc(p, struct dcerpc_auth); - if (!p->conn->security_state.auth_info) { - status = NT_STATUS_NO_MEMORY; - goto done; + sec->auth_info->credentials = state->credentials; + + if (!more_processing) { + /* NO reply expected, so just send it */ + c->status = dcerpc_auth3(state->pipe->conn, state); + if (!composite_is_ok(c)) return; + composite_done(c); + return; } - p->conn->security_state.auth_info->auth_type = auth_type; - p->conn->security_state.auth_info->auth_level = auth_level; - p->conn->security_state.auth_info->auth_pad_length = 0; - p->conn->security_state.auth_info->auth_reserved = 0; - p->conn->security_state.auth_info->auth_context_id = random(); - p->conn->security_state.auth_info->credentials = null_data_blob; - - while (1) { - num_passes++; - status = gensec_update(p->conn->security_state.generic_state, tmp_ctx, - p->conn->security_state.auth_info->credentials, - &credentials); - - /* The status value here, from GENSEC is vital to the security - * of the system. Even if the other end accepts, if GENSEC - * claims 'MORE_PROCESSING_REQUIRED' then you must keep - * feeding it blobs, or else the remote host/attacker might - * avoid mutal authentication requirements. - * - * Likewise, you must not feed GENSEC too much (after the OK), - * it doesn't like that either - */ - - if (!NT_STATUS_IS_OK(status) - && !NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) { - DEBUG(1, ("Failed DCERPC client gensec_update with mechanism %s: %s\n", - gensec_get_name_by_authtype(auth_type), nt_errstr(status))); - - break; - } + creq = dcerpc_alter_context_send(state->pipe, state, + &state->pipe->syntax, + &state->pipe->transfer_syntax); + composite_continue(c, creq, bind_auth_recv_alter, c); +} - if (!credentials.length) { - break; - } +static void bind_auth_recv_alter(struct composite_context *creq) +{ + struct composite_context *c = + talloc_get_type(creq->async.private_data, + struct composite_context); - p->conn->security_state.auth_info->credentials = credentials; - - if (NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) { - if (num_passes == 1) { - status = dcerpc_bind_byuuid(p, tmp_ctx, uuid, version); - } else { - /* We are demanding a reply, so use a request that will get us one */ - status = dcerpc_alter_context(p, tmp_ctx, &p->syntax, &p->transfer_syntax); - } - if (!NT_STATUS_IS_OK(status)) { - break; - } - } else if (NT_STATUS_IS_OK(status)) { - /* NO reply expected, so just send it */ - if (num_passes == 1) { - status = dcerpc_bind_byuuid(p, tmp_ctx, uuid, version); - } else { - status = dcerpc_auth3(p->conn, tmp_ctx); - } - break; - } else { - break; - } - }; + c->status = dcerpc_alter_context_recv(creq); + if (!composite_is_ok(c)) return; -done: - talloc_free(tmp_ctx); + bind_auth_next_step(c); +} - if (!NT_STATUS_IS_OK(status)) { - talloc_free(p->conn->security_state.generic_state); - ZERO_STRUCT(p->conn->security_state); - } else { - /* Authenticated connections use the generic session key */ - p->conn->security_state.session_key = dcerpc_generic_session_key; +static void bind_auth_recv_bindreply(struct composite_context *creq) +{ + struct composite_context *c = + talloc_get_type(creq->async.private_data, + struct composite_context); + struct bind_auth_state *state = + talloc_get_type(c->private_data, struct bind_auth_state); + + c->status = dcerpc_bind_recv(creq); + if (!composite_is_ok(c)) return; + + if (!state->more_processing) { + composite_done(c); + return; } - return status; + bind_auth_next_step(c); } -/* - setup GENSEC on a DCE-RPC pipe -*/ -NTSTATUS dcerpc_bind_auth_password(struct dcerpc_pipe *p, - const char *uuid, uint_t version, - struct cli_credentials *credentials, - uint8_t auth_type, - const char *service) +static struct composite_context *dcerpc_bind_auth_send(struct dcerpc_pipe *p, + TALLOC_CTX *mem_ctx, + const char *uuid, uint_t version, + struct cli_credentials *credentials, + uint8_t auth_type, + const char *service) { - NTSTATUS status; + struct composite_context *c, *creq; + struct bind_auth_state *state; + struct dcerpc_security *sec; + + struct dcerpc_syntax_id syntax, transfer_syntax; + + c = talloc_zero(mem_ctx, struct composite_context); + if (c == NULL) return NULL; - if (!(p->conn->flags & (DCERPC_SIGN | DCERPC_SEAL))) { - p->conn->flags |= DCERPC_CONNECT; + state = talloc(c, struct bind_auth_state); + if (state == NULL) { + c->status = NT_STATUS_NO_MEMORY; + goto failed; } - status = gensec_client_start(p, &p->conn->security_state.generic_state, - p->conn->event_ctx); - if (!NT_STATUS_IS_OK(status)) { - DEBUG(1, ("Failed to start GENSEC client mode: %s\n", nt_errstr(status))); - return status; + c->state = COMPOSITE_STATE_IN_PROGRESS; + c->private_data = state; + c->event_ctx = p->conn->event_ctx; + + state->pipe = p; + + c->status = dcerpc_init_syntaxes(uuid, version, + &syntax, + &transfer_syntax); + if (!NT_STATUS_IS_OK(c->status)) goto failed; + + sec = &p->conn->security_state; + + c->status = gensec_client_start(p, &sec->generic_state, + p->conn->event_ctx); + if (!NT_STATUS_IS_OK(c->status)) { + DEBUG(1, ("Failed to start GENSEC client mode: %s\n", + nt_errstr(c->status))); + goto failed; } - status = gensec_set_credentials(p->conn->security_state.generic_state, - credentials); - if (!NT_STATUS_IS_OK(status)) { - DEBUG(1, ("Failed to start set GENSEC client credentails: %s\n", - nt_errstr(status))); - return status; + c->status = gensec_set_credentials(sec->generic_state, credentials); + if (!NT_STATUS_IS_OK(c->status)) { + DEBUG(1, ("Failed to set GENSEC client credentails: %s\n", + nt_errstr(c->status))); + goto failed; } - status = gensec_set_target_hostname(p->conn->security_state.generic_state, - p->conn->transport.peer_name(p->conn)); - if (!NT_STATUS_IS_OK(status)) { - DEBUG(1, ("Failed to start set GENSEC target hostname: %s\n", - nt_errstr(status))); - return status; + c->status = gensec_set_target_hostname( + sec->generic_state, p->conn->transport.peer_name(p->conn)); + if (!NT_STATUS_IS_OK(c->status)) { + DEBUG(1, ("Failed to set GENSEC target hostname: %s\n", + nt_errstr(c->status))); + goto failed; } - if (service) { - status = gensec_set_target_service(p->conn->security_state.generic_state, service); - if (!NT_STATUS_IS_OK(status)) { - DEBUG(1, ("Failed to start set GENSEC target service: %s\n", - nt_errstr(status))); - return status; + if (service != NULL) { + c->status = gensec_set_target_service(sec->generic_state, + service); + if (!NT_STATUS_IS_OK(c->status)) { + DEBUG(1, ("Failed to set GENSEC target service: %s\n", + nt_errstr(c->status))); + goto failed; } } - status = gensec_start_mech_by_authtype(p->conn->security_state.generic_state, - auth_type, - dcerpc_auth_level(p->conn)); - if (!NT_STATUS_IS_OK(status)) { - DEBUG(1, ("Failed to start set GENSEC client mechanism %s: %s\n", - gensec_get_name_by_authtype(auth_type), nt_errstr(status))); - return status; + c->status = gensec_start_mech_by_authtype(sec->generic_state, + auth_type, + dcerpc_auth_level(p->conn)); + if (!NT_STATUS_IS_OK(c->status)) { + DEBUG(1, ("Failed to start GENSEC client mechanism %s: %s\n", + gensec_get_name_by_authtype(auth_type), + nt_errstr(c->status))); + goto failed; + } + + sec->auth_info = talloc(p, struct dcerpc_auth); + if (sec->auth_info == NULL) { + c->status = NT_STATUS_NO_MEMORY; + goto failed; + } + + sec->auth_info->auth_type = auth_type; + sec->auth_info->auth_level = dcerpc_auth_level(p->conn); + sec->auth_info->auth_pad_length = 0; + sec->auth_info->auth_reserved = 0; + sec->auth_info->auth_context_id = random(); + sec->auth_info->credentials = data_blob(NULL, 0); + + c->status = gensec_update(sec->generic_state, state, + sec->auth_info->credentials, + &state->credentials); + if (!NT_STATUS_IS_OK(c->status) && + !NT_STATUS_EQUAL(c->status, NT_STATUS_MORE_PROCESSING_REQUIRED)) { + goto failed; + } + + state->more_processing = + NT_STATUS_EQUAL(c->status, NT_STATUS_MORE_PROCESSING_REQUIRED); + + if (state->credentials.length == 0) { + composite_trigger_done(c); + return c; } - - status = dcerpc_bind_auth(p, auth_type, - dcerpc_auth_level(p->conn), - uuid, version); - if (!NT_STATUS_IS_OK(status)) { - DEBUG(2, ("Failed to bind to pipe with %s: %s\n", - gensec_get_name_by_authtype(auth_type), nt_errstr(status))); - return status; + + sec->auth_info->credentials = state->credentials; + + creq = dcerpc_bind_send(p, state, &syntax, &transfer_syntax); + if (creq == NULL) { + c->status = NT_STATUS_NO_MEMORY; + goto failed; } - return status; + creq->async.fn = bind_auth_recv_bindreply; + creq->async.private_data = c; + return c; + + failed: + composite_trigger_error(c); + return c; +} + +static NTSTATUS dcerpc_bind_auth_recv(struct composite_context *creq) +{ + NTSTATUS result = composite_wait(creq); + talloc_free(creq); + return result; +} + +/* + setup GENSEC on a DCE-RPC pipe +*/ +NTSTATUS dcerpc_bind_auth(struct dcerpc_pipe *p, + const char *uuid, uint_t version, + struct cli_credentials *credentials, + uint8_t auth_type, + const char *service) +{ + struct composite_context *creq; + creq = dcerpc_bind_auth_send(p, p, uuid, version, credentials, + auth_type, service); + return dcerpc_bind_auth_recv(creq); } diff --git a/source4/librpc/rpc/dcerpc_schannel.c b/source4/librpc/rpc/dcerpc_schannel.c index ae4ce94269..e9e31f294f 100644 --- a/source4/librpc/rpc/dcerpc_schannel.c +++ b/source4/librpc/rpc/dcerpc_schannel.c @@ -158,8 +158,8 @@ NTSTATUS dcerpc_bind_auth_schannel(TALLOC_CTX *tmp_ctx, return status; } - return dcerpc_bind_auth_password(p, uuid, version, - credentials, DCERPC_AUTH_TYPE_SCHANNEL, - NULL); + return dcerpc_bind_auth(p, uuid, version, + credentials, DCERPC_AUTH_TYPE_SCHANNEL, + NULL); } diff --git a/source4/librpc/rpc/dcerpc_util.c b/source4/librpc/rpc/dcerpc_util.c index ef510f4afc..400a8b5e21 100644 --- a/source4/librpc/rpc/dcerpc_util.c +++ b/source4/librpc/rpc/dcerpc_util.c @@ -985,9 +985,9 @@ NTSTATUS dcerpc_pipe_auth(struct dcerpc_pipe *p, auth_type = DCERPC_AUTH_TYPE_NTLMSSP; } - status = dcerpc_bind_auth_password(p, pipe_uuid, pipe_version, - credentials, auth_type, - binding->authservice); + status = dcerpc_bind_auth(p, pipe_uuid, pipe_version, + credentials, auth_type, + binding->authservice); } else { status = dcerpc_bind_auth_none(p, pipe_uuid, pipe_version); } -- cgit