Generic Authentication Interface
Copyright (C) Andrew Tridgell 2003
- Copyright (C) Andrew Bartlett <abartlet@samba.org> 2004
-
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 2004-2005
+ Copyright (C) Stefan Metzmacher 2004
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
*/
#include "includes.h"
+#include "libcli/composite/composite.h"
+#include "auth/gensec/gensec.h"
/*
do a non-athenticated dcerpc bind
*/
-NTSTATUS dcerpc_bind_auth_none(struct dcerpc_pipe *p,
- const char *uuid, uint_t version)
+
+struct composite_context *dcerpc_bind_auth_none_send(TALLOC_CTX *mem_ctx,
+ struct dcerpc_pipe *p,
+ const struct dcerpc_interface_table *table)
{
- TALLOC_CTX *mem_ctx;
- NTSTATUS status;
+ struct dcerpc_syntax_id syntax;
+ struct dcerpc_syntax_id transfer_syntax;
+
+ struct composite_context *c;
- mem_ctx = talloc_init("dcerpc_bind_auth_ntlm");
- if (!mem_ctx) {
- return NT_STATUS_NO_MEMORY;
+ c = talloc_zero(mem_ctx, struct composite_context);
+ if (c == NULL) return NULL;
+
+ c->status = dcerpc_init_syntaxes(table,
+ &syntax, &transfer_syntax);
+ if (!NT_STATUS_IS_OK(c->status)) {
+ DEBUG(2,("Invalid uuid string in "
+ "dcerpc_bind_auth_none_send\n"));
+ composite_error(c, c->status);
+ return c;
}
- status = dcerpc_bind_byuuid(p, mem_ctx, uuid, version);
- talloc_destroy(mem_ctx);
+ /* c was only allocated as a container for a possible error */
+ talloc_free(c);
- return status;
+ return dcerpc_bind_send(p, mem_ctx, &syntax, &transfer_syntax);
}
-NTSTATUS dcerpc_bind_auth3(struct dcerpc_pipe *p, uint8_t auth_type, uint8_t auth_level,
- const char *uuid, uint_t version)
+NTSTATUS dcerpc_bind_auth_none_recv(struct composite_context *ctx)
{
- NTSTATUS status;
- TALLOC_CTX *mem_ctx;
- DATA_BLOB credentials;
- DATA_BLOB null_data_blob = data_blob(NULL, 0);
+ return dcerpc_bind_recv(ctx);
+}
- mem_ctx = talloc_init("dcerpc_bind_auth");
- if (!mem_ctx) {
- return NT_STATUS_NO_MEMORY;
- }
-
- if (!p->security_state.generic_state) {
- status = gensec_client_start(p, &p->security_state.generic_state);
- if (!NT_STATUS_IS_OK(status)) {
- return status;
- }
+NTSTATUS dcerpc_bind_auth_none(struct dcerpc_pipe *p,
+ const struct dcerpc_interface_table *table)
+{
+ struct composite_context *ctx;
+ ctx = dcerpc_bind_auth_none_send(p, p, table);
+ return dcerpc_bind_auth_none_recv(ctx);
+}
- status = gensec_start_mech_by_authtype(p->security_state.generic_state, auth_type, auth_level);
+struct bind_auth_state {
+ struct dcerpc_pipe *pipe;
+ DATA_BLOB credentials;
+ BOOL more_processing; /* Is there anything more to do after the
+ * first bind itself received? */
+};
- if (!NT_STATUS_IS_OK(status)) {
- return status;
- }
- }
+static void bind_auth_recv_alter(struct composite_context *creq);
- p->security_state.auth_info = talloc(p, sizeof(*p->security_state.auth_info));
- if (!p->security_state.auth_info) {
- status = NT_STATUS_NO_MEMORY;
- 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;
+
+ /* 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
+ */
+
+ 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;
}
- p->security_state.auth_info->auth_type = auth_type;
- p->security_state.auth_info->auth_level = auth_level;
- p->security_state.auth_info->auth_pad_length = 0;
- p->security_state.auth_info->auth_reserved = 0;
- p->security_state.auth_info->auth_context_id = random();
- p->security_state.auth_info->credentials = null_data_blob;
+ if (!composite_is_ok(c)) return;
- status = gensec_update(p->security_state.generic_state, mem_ctx,
- null_data_blob,
- &credentials);
-
- if (!NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
- goto done;
+ if (state->credentials.length == 0) {
+ composite_done(c);
+ return;
}
- p->security_state.auth_info->credentials = credentials;
+ sec->auth_info->credentials = state->credentials;
- status = dcerpc_bind_byuuid(p, mem_ctx, uuid, version);
- if (!NT_STATUS_IS_OK(status)) {
- goto done;
+ 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;
}
- status = gensec_update(p->security_state.generic_state, mem_ctx,
- p->security_state.auth_info->credentials,
- &credentials);
+ /* We are demanding a reply, so use a request that will get us one */
- if (!NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
- goto done;
- }
+ creq = dcerpc_alter_context_send(state->pipe, state,
+ &state->pipe->syntax,
+ &state->pipe->transfer_syntax);
+ composite_continue(c, creq, bind_auth_recv_alter, c);
+}
- p->security_state.auth_info->credentials = credentials;
-
- status = dcerpc_auth3(p, mem_ctx);
-done:
- talloc_destroy(mem_ctx);
+static void bind_auth_recv_alter(struct composite_context *creq)
+{
+ struct composite_context *c =
+ talloc_get_type(creq->async.private_data,
+ struct composite_context);
+
+ c->status = dcerpc_alter_context_recv(creq);
+ if (!composite_is_ok(c)) return;
- if (!NT_STATUS_IS_OK(status)) {
- gensec_end(&p->security_state.generic_state);
- ZERO_STRUCT(p->security_state);
- } else {
- /* Authenticated connections use the generic session key */
- p->security_state.session_key = dcerpc_generic_session_key;
+ bind_auth_next_step(c);
+}
+
+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) {
+ /* The first gensec_update has not requested a second run, so
+ * we're done here. */
+ composite_done(c);
+ return;
}
- return status;
+ bind_auth_next_step(c);
}
-NTSTATUS dcerpc_bind_alter(struct dcerpc_pipe *p, uint8_t auth_type, uint8_t auth_level,
- const char *uuid, uint_t version)
+struct composite_context *dcerpc_bind_auth_send(TALLOC_CTX *mem_ctx,
+ struct dcerpc_pipe *p,
+ const struct dcerpc_interface_table *table,
+ struct cli_credentials *credentials,
+ uint8_t auth_type, uint8_t auth_level,
+ const char *service)
{
- NTSTATUS status;
- TALLOC_CTX *mem_ctx;
- DATA_BLOB credentials;
- DATA_BLOB null_data_blob = data_blob(NULL, 0);
+ struct composite_context *c, *creq;
+ struct bind_auth_state *state;
+ struct dcerpc_security *sec;
+
+ struct dcerpc_syntax_id syntax, transfer_syntax;
- mem_ctx = talloc_init("dcerpc_bind_auth");
- if (!mem_ctx) {
- return NT_STATUS_NO_MEMORY;
+ c = talloc_zero(mem_ctx, struct composite_context);
+ if (c == NULL) return NULL;
+
+ state = talloc(c, struct bind_auth_state);
+ if (state == NULL) {
+ c->status = NT_STATUS_NO_MEMORY;
+ goto failed;
}
-
- if (!p->security_state.generic_state) {
- status = gensec_client_start(p, &p->security_state.generic_state);
- if (!NT_STATUS_IS_OK(status)) {
- return status;
- }
- status = gensec_start_mech_by_authtype(p->security_state.generic_state,
- auth_type, auth_level);
+ c->state = COMPOSITE_STATE_IN_PROGRESS;
+ c->private_data = state;
+ c->event_ctx = p->conn->event_ctx;
- if (!NT_STATUS_IS_OK(status)) {
- return status;
- }
+ state->pipe = p;
+
+ c->status = dcerpc_init_syntaxes(table,
+ &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;
}
- p->security_state.auth_info = talloc(p, sizeof(*p->security_state.auth_info));
- if (!p->security_state.auth_info) {
- status = NT_STATUS_NO_MEMORY;
- goto done;
+ 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;
}
- p->security_state.auth_info->auth_type = auth_type;
- p->security_state.auth_info->auth_level = auth_level;
- p->security_state.auth_info->auth_pad_length = 0;
- p->security_state.auth_info->auth_reserved = 0;
- p->security_state.auth_info->auth_context_id = random();
- p->security_state.auth_info->credentials = null_data_blob;
+ 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;
+ }
- status = gensec_update(p->security_state.generic_state, mem_ctx,
- null_data_blob,
- &credentials);
-
- if (!NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
- goto done;
+ 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;
+ }
+ }
+
+ c->status = gensec_start_mech_by_authtype(sec->generic_state,
+ auth_type, auth_level);
+ 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;
}
- p->security_state.auth_info->credentials = credentials;
+ sec->auth_info = talloc(p, struct dcerpc_auth);
+ if (sec->auth_info == NULL) {
+ c->status = NT_STATUS_NO_MEMORY;
+ goto failed;
+ }
- status = dcerpc_bind_byuuid(p, mem_ctx, uuid, version);
- if (!NT_STATUS_IS_OK(status)) {
- goto done;
+ sec->auth_info->auth_type = auth_type;
+ sec->auth_info->auth_level = auth_level,
+ 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);
+
+ /* 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
+ */
+
+ 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;
}
- while(1) {
- status = gensec_update(p->security_state.generic_state, mem_ctx,
- p->security_state.auth_info->credentials,
- &credentials);
+ state->more_processing =
+ NT_STATUS_EQUAL(c->status, NT_STATUS_MORE_PROCESSING_REQUIRED);
- if (!NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
- goto done;
- }
+ if (state->credentials.length == 0) {
+ composite_done(c);
+ return c;
+ }
- p->security_state.auth_info->credentials = credentials;
+ sec->auth_info->credentials = state->credentials;
- status = dcerpc_alter(p, mem_ctx);
- if (!NT_STATUS_IS_OK(status)) {
- goto done;
- }
+ /* The first request always is a dcerpc_bind. The subsequent ones
+ * depend on gensec results */
+ creq = dcerpc_bind_send(p, state, &syntax, &transfer_syntax);
+ if (creq == NULL) {
+ c->status = NT_STATUS_NO_MEMORY;
+ goto failed;
}
-done:
- talloc_destroy(mem_ctx);
+ creq->async.fn = bind_auth_recv_bindreply;
+ creq->async.private_data = c;
+ return c;
+
+ failed:
+ composite_error(c, c->status);
+ return c;
+}
- if (!NT_STATUS_IS_OK(status)) {
- ZERO_STRUCT(p->security_state);
- } else {
- /* Authenticated connections use the generic session key */
- p->security_state.session_key = dcerpc_generic_session_key;
+NTSTATUS dcerpc_bind_auth_recv(struct composite_context *creq)
+{
+ NTSTATUS result = composite_wait(creq);
+ struct bind_auth_state *state = talloc_get_type(creq->private_data, struct bind_auth_state);
+
+ if (NT_STATUS_IS_OK(result)) {
+ /*
+ after a successful authenticated bind the session
+ key reverts to the generic session key
+ */
+ state->pipe->conn->security_state.session_key = dcerpc_generic_session_key;
}
+
+ talloc_free(creq);
+ return result;
+}
- return status;
+/*
+ setup GENSEC on a DCE-RPC pipe
+*/
+NTSTATUS dcerpc_bind_auth(struct dcerpc_pipe *p,
+ const struct dcerpc_interface_table *table,
+ struct cli_credentials *credentials,
+ uint8_t auth_type, uint8_t auth_level,
+ const char *service)
+{
+ struct composite_context *creq;
+ creq = dcerpc_bind_auth_send(p, p, table, credentials,
+ auth_type, auth_level, service);
+ return dcerpc_bind_auth_recv(creq);
}