From 5f4d86f955d939e96ec9b81c8a9d080aab4354b6 Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Thu, 4 May 2006 10:03:41 +0000 Subject: r15426: Implement SPNEGO as the default RPC authentication mechanism. Where this isn't supported, fallback to NTLM. Also, where we get a failure as 'logon failure', try and do a '3 tries' for the password, like we already do for CIFS. (Incomplete: needs a mapping between RPC errors and the logon failure NTSTATUS). Because we don't yet support Kerberos sign/seal to win2k3 SP1 for DCE/RPC, disable this (causing SPNEGO to negotiate NTLM) when kerberos isn't demanded. Andrew Bartlett (This used to be commit b3212d1fb91b26c1d326a289560106dffe1d2e80) --- source4/librpc/rpc/dcerpc_util.c | 156 +++++++++++++++++++++++++++++++++++---- 1 file changed, 143 insertions(+), 13 deletions(-) (limited to 'source4/librpc/rpc/dcerpc_util.c') diff --git a/source4/librpc/rpc/dcerpc_util.c b/source4/librpc/rpc/dcerpc_util.c index dc2eded967..6c8ed7ecd8 100644 --- a/source4/librpc/rpc/dcerpc_util.c +++ b/source4/librpc/rpc/dcerpc_util.c @@ -141,6 +141,7 @@ static const struct { {"seal", DCERPC_SEAL}, {"connect", DCERPC_CONNECT}, {"spnego", DCERPC_AUTH_SPNEGO}, + {"ntlm", DCERPC_AUTH_NTLM}, {"krb5", DCERPC_AUTH_KRB5}, {"validate", DCERPC_DEBUG_VALIDATE_BOTH}, {"print", DCERPC_DEBUG_PRINT_BOTH}, @@ -936,7 +937,7 @@ struct composite_context *dcerpc_epm_map_binding_send(TALLOC_CTX *mem_ctx, epmapper_binding->endpoint = NULL; /* initiate rpc pipe connection */ - pipe_connect_req = dcerpc_pipe_connect_b_send(c, &s->pipe, epmapper_binding, &dcerpc_table_epmapper, + pipe_connect_req = dcerpc_pipe_connect_b_send(c, epmapper_binding, &dcerpc_table_epmapper, anon_creds, c->event_ctx); if (composite_nomem(pipe_connect_req, c)) return c; @@ -981,6 +982,8 @@ struct pipe_auth_state { static void continue_auth_schannel(struct composite_context *ctx); static void continue_auth(struct composite_context *ctx); static void continue_auth_none(struct composite_context *ctx); +static void continue_ntlmssp_connection(struct composite_context *ctx); +static void continue_spnego_after_wrong_pass(struct composite_context *ctx); /* @@ -1011,6 +1014,111 @@ static void continue_auth(struct composite_context *ctx) composite_done(c); } +/* + Stage 2 of pipe_auth: Receive result of authenticated bind request, but handle fallbacks: + SPNEGO -> NTLMSSP +*/ +static void continue_auth_auto(struct composite_context *ctx) +{ + struct composite_context *c = talloc_get_type(ctx->async.private_data, + struct composite_context); + + c->status = dcerpc_bind_auth_recv(ctx); + if (NT_STATUS_EQUAL(c->status, NT_STATUS_INVALID_PARAMETER)) { + struct pipe_auth_state *s = talloc_get_type(c->private_data, struct pipe_auth_state); + struct composite_context *sec_conn_req; + + /* send a request for secondary rpc connection */ + sec_conn_req = dcerpc_secondary_connection_send(s->pipe, + s->binding); + if (composite_nomem(sec_conn_req, c)) return; + + composite_continue(c, sec_conn_req, continue_ntlmssp_connection, c); + + return; + } else if (NT_STATUS_EQUAL(c->status, NT_STATUS_LOGON_FAILURE)) { + struct pipe_auth_state *s = talloc_get_type(c->private_data, struct pipe_auth_state); + struct composite_context *sec_conn_req; + if (cli_credentials_wrong_password(s->credentials)) { + /* send a request for secondary rpc connection */ + sec_conn_req = dcerpc_secondary_connection_send(s->pipe, + s->binding); + if (composite_nomem(sec_conn_req, c)) return; + + composite_continue(c, sec_conn_req, continue_spnego_after_wrong_pass, c); + } + } + + if (!composite_is_ok(c)) return; + + composite_done(c); +} + +/* + Stage 3 of pipe_auth (fallback to NTLMSSP case): Receive secondary + rpc connection (the first one can't be used any more, due to the + bind nak) and perform authenticated bind request +*/ +static void continue_ntlmssp_connection(struct composite_context *ctx) +{ + struct composite_context *c; + struct pipe_auth_state *s; + struct composite_context *auth_req; + struct dcerpc_pipe *p2; + + c = talloc_get_type(ctx->async.private_data, struct composite_context); + s = talloc_get_type(c->private_data, struct pipe_auth_state); + + /* receive secondary rpc connection */ + c->status = dcerpc_secondary_connection_recv(ctx, &p2); + talloc_steal(s, p2); + talloc_steal(p2, s->pipe); + s->pipe = p2; + + if (!composite_is_ok(c)) return; + + /* initiate a authenticated bind */ + auth_req = dcerpc_bind_auth_send(c, s->pipe, s->table, + s->credentials, DCERPC_AUTH_TYPE_NTLMSSP, + dcerpc_auth_level(s->pipe->conn), + s->table->authservices->names[0]); + if (composite_nomem(auth_req, c)) return; + + composite_continue(c, auth_req, continue_auth, c); +} + +/* + Stage 3 of pipe_auth (retry on wrong password): Receive secondary + rpc connection (the first one can't be used any more, due to the + bind nak) and perform authenticated bind request +*/ +static void continue_spnego_after_wrong_pass(struct composite_context *ctx) +{ + struct composite_context *c; + struct pipe_auth_state *s; + struct composite_context *auth_req; + struct dcerpc_pipe *p2; + + c = talloc_get_type(ctx->async.private_data, struct composite_context); + s = talloc_get_type(c->private_data, struct pipe_auth_state); + + /* receive secondary rpc connection */ + c->status = dcerpc_secondary_connection_recv(ctx, &p2); + talloc_steal(s, p2); + talloc_steal(p2, s->pipe); + s->pipe = p2; + + if (!composite_is_ok(c)) return; + + /* initiate a authenticated bind */ + auth_req = dcerpc_bind_auth_send(c, s->pipe, s->table, + s->credentials, DCERPC_AUTH_TYPE_SPNEGO, + dcerpc_auth_level(s->pipe->conn), + s->table->authservices->names[0]); + if (composite_nomem(auth_req, c)) return; + + composite_continue(c, auth_req, continue_auth, c); +} /* @@ -1042,6 +1150,7 @@ struct composite_context *dcerpc_pipe_auth_send(struct dcerpc_pipe *p, struct composite_context *auth_schannel_req; struct composite_context *auth_req; struct composite_context *auth_none_req; + struct dcerpc_connection *conn; /* composite context allocation and setup */ c = talloc_zero(NULL, struct composite_context); @@ -1060,10 +1169,11 @@ struct composite_context *dcerpc_pipe_auth_send(struct dcerpc_pipe *p, s->credentials = credentials; s->pipe = p; - s->pipe->conn->flags = binding->flags; + conn = s->pipe->conn; + conn->flags = binding->flags; /* remember the binding string for possible secondary connections */ - s->pipe->conn->binding_string = dcerpc_binding_string(p, binding); + conn->binding_string = dcerpc_binding_string(p, binding); if (!cli_credentials_is_anonymous(s->credentials) && (binding->flags & DCERPC_SCHANNEL) && @@ -1074,13 +1184,13 @@ struct composite_context *dcerpc_pipe_auth_send(struct dcerpc_pipe *p, * first */ auth_schannel_req = dcerpc_bind_auth_schannel_send(c, s->pipe, s->table, s->credentials, - dcerpc_auth_level(s->pipe->conn)); + dcerpc_auth_level(conn)); if (composite_nomem(auth_schannel_req, c)) return c; composite_continue(c, auth_schannel_req, continue_auth_schannel, c); } else if (!cli_credentials_is_anonymous(s->credentials) && - !(s->pipe->conn->transport.transport == NCACN_NP && + !(conn->transport.transport == NCACN_NP && !(s->binding->flags & DCERPC_SIGN) && !(s->binding->flags & DCERPC_SEAL))) { @@ -1092,7 +1202,7 @@ struct composite_context *dcerpc_pipe_auth_send(struct dcerpc_pipe *p, uint8_t auth_type; - if ((s->pipe->conn->flags & (DCERPC_SIGN|DCERPC_SEAL)) == 0) { + if ((conn->flags & (DCERPC_SIGN|DCERPC_SEAL)) == 0) { /* we are doing an authenticated connection, but not using sign or seal. We must force @@ -1100,7 +1210,7 @@ struct composite_context *dcerpc_pipe_auth_send(struct dcerpc_pipe *p, type doesn't allow authentication information to be passed. */ - s->pipe->conn->flags |= DCERPC_CONNECT; + conn->flags |= DCERPC_CONNECT; } if (s->binding->flags & DCERPC_AUTH_SPNEGO) { @@ -1112,13 +1222,22 @@ struct composite_context *dcerpc_pipe_auth_send(struct dcerpc_pipe *p, } else if (s->binding->flags & DCERPC_SCHANNEL) { auth_type = DCERPC_AUTH_TYPE_SCHANNEL; - } else { + } else if (s->binding->flags & DCERPC_AUTH_NTLM) { auth_type = DCERPC_AUTH_TYPE_NTLMSSP; + } else { + auth_req = dcerpc_bind_auth_send(c, s->pipe, s->table, + s->credentials, DCERPC_AUTH_TYPE_SPNEGO, + dcerpc_auth_level(conn), + s->table->authservices->names[0]); + if (composite_nomem(auth_req, c)) return c; + + composite_continue(c, auth_req, continue_auth_auto, c); + return c; } auth_req = dcerpc_bind_auth_send(c, s->pipe, s->table, s->credentials, auth_type, - dcerpc_auth_level(s->pipe->conn), + dcerpc_auth_level(conn), s->table->authservices->names[0]); if (composite_nomem(auth_req, c)) return c; @@ -1137,8 +1256,13 @@ struct composite_context *dcerpc_pipe_auth_send(struct dcerpc_pipe *p, /* Receive result of authenticated bind request on dcerpc pipe + + This returns *p, which may be different to the one originally + supllied, as it rebinds to a new pipe due to authentication fallback + */ -NTSTATUS dcerpc_pipe_auth_recv(struct composite_context *c) +NTSTATUS dcerpc_pipe_auth_recv(struct composite_context *c, TALLOC_CTX *mem_ctx, + struct dcerpc_pipe **p) { NTSTATUS status; @@ -1149,6 +1273,9 @@ NTSTATUS dcerpc_pipe_auth_recv(struct composite_context *c) char *uuid_str = GUID_string(s->pipe, &s->table->syntax_id.uuid); DEBUG(0, ("Failed to bind to uuid %s - %s\n", uuid_str, nt_errstr(status))); talloc_free(uuid_str); + } else { + talloc_steal(mem_ctx, s->pipe); + *p = s->pipe; } talloc_free(c); @@ -1158,16 +1285,19 @@ NTSTATUS dcerpc_pipe_auth_recv(struct composite_context *c) /* Perform an authenticated bind if needed - sync version + + This may change *p, as it rebinds to a new pipe due to authentication fallback */ -NTSTATUS dcerpc_pipe_auth(struct dcerpc_pipe *p, +NTSTATUS dcerpc_pipe_auth(TALLOC_CTX *mem_ctx, + struct dcerpc_pipe **p, struct dcerpc_binding *binding, const struct dcerpc_interface_table *table, struct cli_credentials *credentials) { struct composite_context *c; - c = dcerpc_pipe_auth_send(p, binding, table, credentials); - return dcerpc_pipe_auth_recv(c); + c = dcerpc_pipe_auth_send(*p, binding, table, credentials); + return dcerpc_pipe_auth_recv(c, mem_ctx, p); } -- cgit