2 Unix SMB/CIFS implementation.
4 Generic Authentication Interface
6 Copyright (C) Andrew Tridgell 2003
7 Copyright (C) Andrew Bartlett <abartlet@samba.org> 2004-2005
8 Copyright (C) Stefan Metzmacher 2004
10 This program is free software; you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation; either version 3 of the License, or
13 (at your option) any later version.
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
20 You should have received a copy of the GNU General Public License
21 along with this program. If not, see <http://www.gnu.org/licenses/>.
26 #include "libcli/composite/composite.h"
27 #include "auth/gensec/gensec.h"
28 #include "librpc/rpc/dcerpc.h"
29 #include "librpc/rpc/dcerpc_proto.h"
30 #include "param/param.h"
33 return the rpc syntax and transfer syntax given the pipe uuid and version
35 static NTSTATUS dcerpc_init_syntaxes(struct dcerpc_pipe *p,
36 const struct ndr_interface_table *table,
37 struct ndr_syntax_id *syntax,
38 struct ndr_syntax_id *transfer_syntax)
40 struct GUID *object = NULL;
42 p->object = dcerpc_binding_get_object(p->binding);
43 if (!GUID_all_zero(&p->object)) {
47 p->binding_handle = dcerpc_pipe_binding_handle(p, object, table);
48 if (p->binding_handle == NULL) {
49 return NT_STATUS_NO_MEMORY;
52 syntax->uuid = table->syntax_id.uuid;
53 syntax->if_version = table->syntax_id.if_version;
55 if (p->conn->flags & DCERPC_NDR64) {
56 *transfer_syntax = ndr_transfer_syntax_ndr64;
58 *transfer_syntax = ndr_transfer_syntax_ndr;
66 Send request to do a non-authenticated dcerpc bind
68 static void dcerpc_bind_auth_none_done(struct tevent_req *subreq);
70 struct composite_context *dcerpc_bind_auth_none_send(TALLOC_CTX *mem_ctx,
71 struct dcerpc_pipe *p,
72 const struct ndr_interface_table *table)
74 struct ndr_syntax_id syntax;
75 struct ndr_syntax_id transfer_syntax;
77 struct composite_context *c;
78 struct tevent_req *subreq;
80 c = composite_create(mem_ctx, p->conn->event_ctx);
81 if (c == NULL) return NULL;
83 c->status = dcerpc_init_syntaxes(p, table,
84 &syntax, &transfer_syntax);
85 if (!NT_STATUS_IS_OK(c->status)) {
86 DEBUG(2,("Invalid uuid string in "
87 "dcerpc_bind_auth_none_send\n"));
88 composite_error(c, c->status);
92 subreq = dcerpc_bind_send(mem_ctx, p->conn->event_ctx, p,
93 &syntax, &transfer_syntax);
94 if (composite_nomem(subreq, c)) return c;
95 tevent_req_set_callback(subreq, dcerpc_bind_auth_none_done, c);
100 static void dcerpc_bind_auth_none_done(struct tevent_req *subreq)
102 struct composite_context *ctx =
103 tevent_req_callback_data(subreq,
104 struct composite_context);
106 ctx->status = dcerpc_bind_recv(subreq);
108 if (!composite_is_ok(ctx)) return;
114 Receive result of a non-authenticated dcerpc bind
116 NTSTATUS dcerpc_bind_auth_none_recv(struct composite_context *ctx)
118 NTSTATUS result = composite_wait(ctx);
125 Perform sync non-authenticated dcerpc bind
127 _PUBLIC_ NTSTATUS dcerpc_bind_auth_none(struct dcerpc_pipe *p,
128 const struct ndr_interface_table *table)
130 struct composite_context *ctx;
132 ctx = dcerpc_bind_auth_none_send(p, p, table);
133 return dcerpc_bind_auth_none_recv(ctx);
137 struct bind_auth_state {
138 struct dcerpc_pipe *pipe;
139 struct ndr_syntax_id syntax;
140 struct ndr_syntax_id transfer_syntax;
141 struct dcerpc_auth out_auth_info;
142 struct dcerpc_auth in_auth_info;
143 bool more_processing; /* Is there anything more to do after the
144 * first bind itself received? */
147 static void bind_auth_next_gensec_done(struct tevent_req *subreq);
148 static void bind_auth_recv_alter(struct tevent_req *subreq);
150 static void bind_auth_next_step(struct composite_context *c)
152 struct bind_auth_state *state;
153 struct dcecli_security *sec;
154 struct tevent_req *subreq;
156 state = talloc_get_type(c->private_data, struct bind_auth_state);
157 sec = &state->pipe->conn->security_state;
159 if (state->in_auth_info.auth_type != sec->auth_type) {
160 composite_error(c, NT_STATUS_RPC_PROTOCOL_ERROR);
164 if (state->in_auth_info.auth_level != sec->auth_level) {
165 composite_error(c, NT_STATUS_RPC_PROTOCOL_ERROR);
169 if (state->in_auth_info.auth_context_id != sec->auth_context_id) {
170 composite_error(c, NT_STATUS_RPC_PROTOCOL_ERROR);
174 state->out_auth_info = (struct dcerpc_auth) {
175 .auth_type = sec->auth_type,
176 .auth_level = sec->auth_level,
177 .auth_context_id = sec->auth_context_id,
180 /* The status value here, from GENSEC is vital to the security
181 * of the system. Even if the other end accepts, if GENSEC
182 * claims 'MORE_PROCESSING_REQUIRED' then you must keep
183 * feeding it blobs, or else the remote host/attacker might
184 * avoid mutal authentication requirements.
186 * Likewise, you must not feed GENSEC too much (after the OK),
187 * it doesn't like that either
190 subreq = gensec_update_send(state,
191 state->pipe->conn->event_ctx,
193 state->in_auth_info.credentials);
194 if (composite_nomem(subreq, c)) return;
195 tevent_req_set_callback(subreq, bind_auth_next_gensec_done, c);
198 static void bind_auth_next_gensec_done(struct tevent_req *subreq)
200 struct composite_context *c =
201 tevent_req_callback_data(subreq,
202 struct composite_context);
203 struct bind_auth_state *state =
204 talloc_get_type_abort(c->private_data,
205 struct bind_auth_state);
206 struct dcerpc_pipe *p = state->pipe;
207 struct dcecli_security *sec = &p->conn->security_state;
208 bool more_processing = false;
210 c->status = gensec_update_recv(subreq, state,
211 &state->out_auth_info.credentials);
214 if (NT_STATUS_EQUAL(c->status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
215 more_processing = true;
216 c->status = NT_STATUS_OK;
219 if (!composite_is_ok(c)) return;
221 if (!more_processing) {
222 if (state->pipe->conn->flags & DCERPC_HEADER_SIGNING) {
223 gensec_want_feature(sec->generic_state,
224 GENSEC_FEATURE_SIGN_PKT_HEADER);
228 if (state->out_auth_info.credentials.length == 0) {
233 state->in_auth_info = (struct dcerpc_auth) {
234 .auth_type = DCERPC_AUTH_TYPE_NONE,
236 sec->tmp_auth_info.in = &state->in_auth_info;
237 sec->tmp_auth_info.mem = state;
238 sec->tmp_auth_info.out = &state->out_auth_info;
240 if (!more_processing) {
241 /* NO reply expected, so just send it */
242 c->status = dcerpc_auth3(state->pipe, state);
243 if (!composite_is_ok(c)) return;
249 /* We are demanding a reply, so use a request that will get us one */
251 subreq = dcerpc_alter_context_send(state, state->pipe->conn->event_ctx,
253 &state->pipe->syntax,
254 &state->pipe->transfer_syntax);
255 if (composite_nomem(subreq, c)) return;
256 tevent_req_set_callback(subreq, bind_auth_recv_alter, c);
260 static void bind_auth_recv_alter(struct tevent_req *subreq)
262 struct composite_context *c =
263 tevent_req_callback_data(subreq,
264 struct composite_context);
265 struct bind_auth_state *state = talloc_get_type(c->private_data,
266 struct bind_auth_state);
267 struct dcecli_security *sec = &state->pipe->conn->security_state;
269 ZERO_STRUCT(sec->tmp_auth_info);
271 c->status = dcerpc_alter_context_recv(subreq);
273 if (!composite_is_ok(c)) return;
275 bind_auth_next_step(c);
279 static void bind_auth_recv_bindreply(struct tevent_req *subreq)
281 struct composite_context *c =
282 tevent_req_callback_data(subreq,
283 struct composite_context);
284 struct bind_auth_state *state = talloc_get_type(c->private_data,
285 struct bind_auth_state);
286 struct dcecli_security *sec = &state->pipe->conn->security_state;
288 ZERO_STRUCT(sec->tmp_auth_info);
290 c->status = dcerpc_bind_recv(subreq);
292 if (!composite_is_ok(c)) return;
294 if (!state->more_processing) {
295 /* The first gensec_update has not requested a second run, so
296 * we're done here. */
301 bind_auth_next_step(c);
305 static void dcerpc_bind_auth_gensec_done(struct tevent_req *subreq);
308 Bind to a DCE/RPC pipe, send async request
309 @param mem_ctx TALLOC_CTX for the allocation of the composite_context
310 @param p The dcerpc_pipe to bind (must already be connected)
311 @param table The interface table to use (the DCE/RPC bind both selects and interface and authenticates)
312 @param credentials The credentials of the account to connect with
313 @param auth_type Select the authentication scheme to use
314 @param auth_level Chooses between unprotected (connect), signed or sealed
315 @param service The service (used by Kerberos to select the service principal to contact)
316 @retval A composite context describing the partial state of the bind
319 struct composite_context *dcerpc_bind_auth_send(TALLOC_CTX *mem_ctx,
320 struct dcerpc_pipe *p,
321 const struct ndr_interface_table *table,
322 struct cli_credentials *credentials,
323 struct gensec_settings *gensec_settings,
324 uint8_t auth_type, uint8_t auth_level,
327 struct composite_context *c;
328 struct bind_auth_state *state;
329 struct dcecli_security *sec;
330 struct tevent_req *subreq;
331 const char *target_principal = NULL;
333 /* composite context allocation and setup */
334 c = composite_create(mem_ctx, p->conn->event_ctx);
335 if (c == NULL) return NULL;
337 state = talloc(c, struct bind_auth_state);
338 if (composite_nomem(state, c)) return c;
339 c->private_data = state;
343 c->status = dcerpc_init_syntaxes(p, table,
345 &state->transfer_syntax);
346 if (!composite_is_ok(c)) return c;
348 sec = &p->conn->security_state;
350 c->status = gensec_client_start(p, &sec->generic_state,
352 if (!NT_STATUS_IS_OK(c->status)) {
353 DEBUG(1, ("Failed to start GENSEC client mode: %s\n",
354 nt_errstr(c->status)));
355 composite_error(c, c->status);
359 c->status = gensec_set_credentials(sec->generic_state, credentials);
360 if (!NT_STATUS_IS_OK(c->status)) {
361 DEBUG(1, ("Failed to set GENSEC client credentials: %s\n",
362 nt_errstr(c->status)));
363 composite_error(c, c->status);
367 c->status = gensec_set_target_hostname(sec->generic_state,
368 dcerpc_server_name(p));
369 if (!NT_STATUS_IS_OK(c->status)) {
370 DEBUG(1, ("Failed to set GENSEC target hostname: %s\n",
371 nt_errstr(c->status)));
372 composite_error(c, c->status);
376 if (service != NULL) {
377 c->status = gensec_set_target_service(sec->generic_state,
379 if (!NT_STATUS_IS_OK(c->status)) {
380 DEBUG(1, ("Failed to set GENSEC target service: %s\n",
381 nt_errstr(c->status)));
382 composite_error(c, c->status);
387 if (p->binding != NULL) {
388 target_principal = dcerpc_binding_get_string_option(p->binding,
391 if (target_principal != NULL) {
392 c->status = gensec_set_target_principal(sec->generic_state,
394 if (!NT_STATUS_IS_OK(c->status)) {
395 DEBUG(1, ("Failed to set GENSEC target principal to %s: %s\n",
396 target_principal, nt_errstr(c->status)));
397 composite_error(c, c->status);
402 c->status = gensec_start_mech_by_authtype(sec->generic_state,
403 auth_type, auth_level);
404 if (!NT_STATUS_IS_OK(c->status)) {
405 DEBUG(1, ("Failed to start GENSEC client mechanism %s: %s\n",
406 gensec_get_name_by_authtype(sec->generic_state, auth_type),
407 nt_errstr(c->status)));
408 composite_error(c, c->status);
412 sec->auth_type = auth_type;
413 sec->auth_level = auth_level,
415 * We use auth_context_id = 1 as some older
416 * Samba versions (<= 4.2.3) use that value hardcoded
419 sec->auth_context_id = 1;
421 state->out_auth_info = (struct dcerpc_auth) {
422 .auth_type = sec->auth_type,
423 .auth_level = sec->auth_level,
424 .auth_context_id = sec->auth_context_id,
427 /* The status value here, from GENSEC is vital to the security
428 * of the system. Even if the other end accepts, if GENSEC
429 * claims 'MORE_PROCESSING_REQUIRED' then you must keep
430 * feeding it blobs, or else the remote host/attacker might
431 * avoid mutal authentication requirements.
433 * Likewise, you must not feed GENSEC too much (after the OK),
434 * it doesn't like that either
437 subreq = gensec_update_send(state,
441 if (composite_nomem(subreq, c)) return c;
442 tevent_req_set_callback(subreq, dcerpc_bind_auth_gensec_done, c);
447 static void dcerpc_bind_auth_gensec_done(struct tevent_req *subreq)
449 struct composite_context *c =
450 tevent_req_callback_data(subreq,
451 struct composite_context);
452 struct bind_auth_state *state =
453 talloc_get_type_abort(c->private_data,
454 struct bind_auth_state);
455 struct dcerpc_pipe *p = state->pipe;
456 struct dcecli_security *sec = &p->conn->security_state;
458 c->status = gensec_update_recv(subreq, state,
459 &state->out_auth_info.credentials);
461 if (!NT_STATUS_IS_OK(c->status) &&
462 !NT_STATUS_EQUAL(c->status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
463 composite_error(c, c->status);
467 state->more_processing = NT_STATUS_EQUAL(c->status,
468 NT_STATUS_MORE_PROCESSING_REQUIRED);
470 if (state->out_auth_info.credentials.length == 0) {
475 if (gensec_have_feature(sec->generic_state, GENSEC_FEATURE_SIGN_PKT_HEADER)) {
476 if (sec->auth_level >= DCERPC_AUTH_LEVEL_PACKET) {
477 state->pipe->conn->flags |= DCERPC_PROPOSE_HEADER_SIGNING;
481 state->in_auth_info = (struct dcerpc_auth) {
482 .auth_type = DCERPC_AUTH_TYPE_NONE,
484 sec->tmp_auth_info.in = &state->in_auth_info;
485 sec->tmp_auth_info.mem = state;
486 sec->tmp_auth_info.out = &state->out_auth_info;
488 /* The first request always is a dcerpc_bind. The subsequent ones
489 * depend on gensec results */
490 subreq = dcerpc_bind_send(state, p->conn->event_ctx, p,
491 &state->syntax, &state->transfer_syntax);
492 if (composite_nomem(subreq, c)) return;
493 tevent_req_set_callback(subreq, bind_auth_recv_bindreply, c);
500 Bind to a DCE/RPC pipe, receive result
501 @param creq A composite context describing state of async call
502 @retval NTSTATUS code
505 NTSTATUS dcerpc_bind_auth_recv(struct composite_context *creq)
507 NTSTATUS result = composite_wait(creq);
508 struct bind_auth_state *state = talloc_get_type(creq->private_data,
509 struct bind_auth_state);
511 if (NT_STATUS_IS_OK(result)) {
513 after a successful authenticated bind the session
514 key reverts to the generic session key
516 state->pipe->conn->security_state.session_key = dcerpc_generic_session_key;
525 Perform a GENSEC authenticated bind to a DCE/RPC pipe, sync
526 @param p The dcerpc_pipe to bind (must already be connected)
527 @param table The interface table to use (the DCE/RPC bind both selects and interface and authenticates)
528 @param credentials The credentials of the account to connect with
529 @param auth_type Select the authentication scheme to use
530 @param auth_level Chooses between unprotected (connect), signed or sealed
531 @param service The service (used by Kerberos to select the service principal to contact)
532 @retval NTSTATUS status code
535 _PUBLIC_ NTSTATUS dcerpc_bind_auth(struct dcerpc_pipe *p,
536 const struct ndr_interface_table *table,
537 struct cli_credentials *credentials,
538 struct gensec_settings *gensec_settings,
539 uint8_t auth_type, uint8_t auth_level,
542 struct composite_context *creq;
543 creq = dcerpc_bind_auth_send(p, p, table, credentials, gensec_settings,
544 auth_type, auth_level, service);
545 return dcerpc_bind_auth_recv(creq);