summaryrefslogtreecommitdiff
path: root/source4/librpc
diff options
context:
space:
mode:
authorAndrew Bartlett <abartlet@samba.org>2006-05-04 10:03:41 +0000
committerGerald (Jerry) Carter <jerry@samba.org>2007-10-10 14:05:36 -0500
commit5f4d86f955d939e96ec9b81c8a9d080aab4354b6 (patch)
treeb785a6c2aa092fea9bd64391cc45915788b31692 /source4/librpc
parent086c9cc5f4a9145ee93060db2eebb3badc325e44 (diff)
downloadsamba-5f4d86f955d939e96ec9b81c8a9d080aab4354b6.tar.gz
samba-5f4d86f955d939e96ec9b81c8a9d080aab4354b6.tar.bz2
samba-5f4d86f955d939e96ec9b81c8a9d080aab4354b6.zip
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)
Diffstat (limited to 'source4/librpc')
-rw-r--r--source4/librpc/rpc/dcerpc.h9
-rw-r--r--source4/librpc/rpc/dcerpc_connect.c51
-rw-r--r--source4/librpc/rpc/dcerpc_util.c156
3 files changed, 178 insertions, 38 deletions
diff --git a/source4/librpc/rpc/dcerpc.h b/source4/librpc/rpc/dcerpc.h
index 4c8a615ce5..39de9fcaa8 100644
--- a/source4/librpc/rpc/dcerpc.h
+++ b/source4/librpc/rpc/dcerpc.h
@@ -144,16 +144,19 @@ struct dcerpc_pipe {
/* set LIBNDR_FLAG_REF_ALLOC flag when decoding NDR */
#define DCERPC_NDR_REF_ALLOC (1<<14)
-#define DCERPC_AUTH_OPTIONS (DCERPC_SEAL|DCERPC_SIGN|DCERPC_SCHANNEL|DCERPC_AUTH_SPNEGO|DCERPC_AUTH_KRB5)
+#define DCERPC_AUTH_OPTIONS (DCERPC_SEAL|DCERPC_SIGN|DCERPC_SCHANNEL|DCERPC_AUTH_SPNEGO|DCERPC_AUTH_KRB5|DCERPC_AUTH_NTLM)
-/* enable spnego auth */
+/* select spnego auth */
#define DCERPC_AUTH_SPNEGO (1<<15)
-/* enable krb5 auth */
+/* select krb5 auth */
#define DCERPC_AUTH_KRB5 (1<<16)
#define DCERPC_SMB2 (1<<17)
+/* select NTLM auth */
+#define DCERPC_AUTH_NTLM (1<<18)
+
/*
this is used to find pointers to calls
*/
diff --git a/source4/librpc/rpc/dcerpc_connect.c b/source4/librpc/rpc/dcerpc_connect.c
index 2b40935be6..64bb78ec60 100644
--- a/source4/librpc/rpc/dcerpc_connect.c
+++ b/source4/librpc/rpc/dcerpc_connect.c
@@ -28,6 +28,7 @@
#include "smb.h"
#include "libcli/composite/composite.h"
#include "libcli/smb_composite/smb_composite.h"
+#include "lib/events/events.h"
#include "libcli/smb2/smb2.h"
#include "libcli/smb2/smb2_calls.h"
#include "auth/credentials/credentials.h"
@@ -762,8 +763,9 @@ static void continue_pipe_auth(struct composite_context *ctx)
{
struct composite_context *c = talloc_get_type(ctx->async.private_data,
struct composite_context);
+ struct pipe_connect_state *s = talloc_get_type(c->private_data, struct pipe_connect_state);
- c->status = dcerpc_pipe_auth_recv(ctx);
+ c->status = dcerpc_pipe_auth_recv(ctx, s, &s->pipe);
if (!composite_is_ok(c)) return;
composite_done(c);
@@ -775,7 +777,6 @@ static void continue_pipe_auth(struct composite_context *ctx)
specified binding structure to determine the endpoint and options
*/
struct composite_context* dcerpc_pipe_connect_b_send(TALLOC_CTX *parent_ctx,
- struct dcerpc_pipe **pp,
struct dcerpc_binding *binding,
const struct dcerpc_interface_table *table,
struct cli_credentials *credentials,
@@ -796,16 +797,19 @@ struct composite_context* dcerpc_pipe_connect_b_send(TALLOC_CTX *parent_ctx,
c->state = COMPOSITE_STATE_IN_PROGRESS;
c->private_data = s;
- (*pp) = NULL;
+ if (ev == NULL) {
+ ev = event_context_init(c);
+ if (ev == NULL) {
+ talloc_free(c);
+ return NULL;
+ }
+ }
+ c->event_ctx = ev;
/* initialise dcerpc pipe structure */
s->pipe = dcerpc_pipe_init(c, ev);
if (composite_nomem(s->pipe, c)) return c;
- /* get event context from initialised dcerpc pipe */
- c->event_ctx = s->pipe->conn->event_ctx;
- (*pp) = s->pipe;
-
/* store parameters in state structure */
s->binding = binding;
s->table = table;
@@ -842,12 +846,10 @@ NTSTATUS dcerpc_pipe_connect_b_recv(struct composite_context *c, TALLOC_CTX *mem
status = composite_wait(c);
- if (NT_STATUS_IS_OK(status) && p) {
- s = talloc_get_type(c->private_data, struct pipe_connect_state);
- talloc_steal(mem_ctx, s->pipe);
- *p = s->pipe;
- }
-
+ s = talloc_get_type(c->private_data, struct pipe_connect_state);
+ talloc_steal(mem_ctx, s->pipe);
+ *p = s->pipe;
+
talloc_free(c);
return status;
}
@@ -866,7 +868,7 @@ NTSTATUS dcerpc_pipe_connect_b(TALLOC_CTX *parent_ctx,
{
struct composite_context *c;
- c = dcerpc_pipe_connect_b_send(parent_ctx, pp, binding, table,
+ c = dcerpc_pipe_connect_b_send(parent_ctx, binding, table,
credentials, ev);
return dcerpc_pipe_connect_b_recv(c, parent_ctx, pp);
}
@@ -908,6 +910,15 @@ struct composite_context* dcerpc_pipe_connect_send(TALLOC_CTX *parent_ctx,
c->state = COMPOSITE_STATE_IN_PROGRESS;
c->private_data = s;
+ if (ev == NULL) {
+ ev = event_context_init(c);
+ if (ev == NULL) {
+ talloc_free(c);
+ return NULL;
+ }
+ }
+ c->event_ctx = ev;
+
/* parse binding string to the structure */
status = dcerpc_parse_binding(c, binding, &b);
if (!NT_STATUS_IS_OK(status)) {
@@ -922,12 +933,9 @@ struct composite_context* dcerpc_pipe_connect_send(TALLOC_CTX *parent_ctx,
start connecting to a rpc pipe after binding structure
is established
*/
- pipe_conn_req = dcerpc_pipe_connect_b_send(c, &s->pipe, b, table,
+ pipe_conn_req = dcerpc_pipe_connect_b_send(c, b, table,
credentials, ev);
- /* event context for created dcerpc_pipe would be useful... */
- c->event_ctx = s->pipe->conn->event_ctx;
-
if (composite_nomem(pipe_conn_req, c)) return c;
composite_continue(c, pipe_conn_req, continue_pipe_connect_b, c);
@@ -947,6 +955,7 @@ static void continue_pipe_connect_b(struct composite_context *ctx)
struct pipe_conn_state);
c->status = dcerpc_pipe_connect_b_recv(ctx, c, &s->pipe);
+ talloc_steal(s, s->pipe);
if (!composite_is_ok(c)) return;
composite_done(c);
@@ -965,10 +974,8 @@ NTSTATUS dcerpc_pipe_connect_recv(struct composite_context *c,
struct pipe_conn_state *s;
status = composite_wait(c);
- if (NT_STATUS_IS_OK(status) && pp) {
- s = talloc_get_type(c->private_data, struct pipe_conn_state);
- *pp = talloc_steal(mem_ctx, s->pipe);
- }
+ s = talloc_get_type(c->private_data, struct pipe_conn_state);
+ *pp = talloc_steal(mem_ctx, s->pipe);
talloc_free(c);
return status;
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);
}