/src/samba/source4/librpc/rpc/dcerpc_util.c
Line | Count | Source |
1 | | /* |
2 | | Unix SMB/CIFS implementation. |
3 | | |
4 | | dcerpc utility functions |
5 | | |
6 | | Copyright (C) Andrew Tridgell 2003 |
7 | | Copyright (C) Jelmer Vernooij 2004 |
8 | | Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005 |
9 | | Copyright (C) Rafal Szczesniak 2006 |
10 | | |
11 | | This program is free software; you can redistribute it and/or modify |
12 | | it under the terms of the GNU General Public License as published by |
13 | | the Free Software Foundation; either version 3 of the License, or |
14 | | (at your option) any later version. |
15 | | |
16 | | This program is distributed in the hope that it will be useful, |
17 | | but WITHOUT ANY WARRANTY; without even the implied warranty of |
18 | | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
19 | | GNU General Public License for more details. |
20 | | |
21 | | You should have received a copy of the GNU General Public License |
22 | | along with this program. If not, see <http://www.gnu.org/licenses/>. |
23 | | */ |
24 | | |
25 | | #define SOURCE4_LIBRPC_INTERNALS 1 |
26 | | |
27 | | #include "includes.h" |
28 | | #include "lib/events/events.h" |
29 | | #include "libcli/composite/composite.h" |
30 | | #include "librpc/gen_ndr/ndr_epmapper_c.h" |
31 | | #include "librpc/gen_ndr/ndr_dcerpc.h" |
32 | | #include "librpc/gen_ndr/ndr_misc.h" |
33 | | #include "librpc/gen_ndr/ndr_schannel.h" |
34 | | #include "librpc/rpc/dcerpc_proto.h" |
35 | | #include "auth/credentials/credentials.h" |
36 | | #include "auth/gensec/gensec.h" |
37 | | #include "param/param.h" |
38 | | #include "librpc/rpc/rpc_common.h" |
39 | | |
40 | | /* |
41 | | find a dcerpc call on an interface by name |
42 | | */ |
43 | | const struct ndr_interface_call *dcerpc_iface_find_call(const struct ndr_interface_table *iface, |
44 | | const char *name) |
45 | 0 | { |
46 | 0 | uint32_t i; |
47 | 0 | for (i=0;i<iface->num_calls;i++) { |
48 | 0 | if (strcmp(iface->calls[i].name, name) == 0) { |
49 | 0 | return &iface->calls[i]; |
50 | 0 | } |
51 | 0 | } |
52 | 0 | return NULL; |
53 | 0 | } |
54 | | |
55 | | struct epm_map_binding_state { |
56 | | struct dcerpc_binding *binding; |
57 | | const struct ndr_interface_table *table; |
58 | | struct dcerpc_pipe *pipe; |
59 | | struct policy_handle handle; |
60 | | struct GUID object; |
61 | | struct epm_twr_t twr; |
62 | | struct epm_twr_t *twr_r; |
63 | | uint32_t num_towers; |
64 | | struct epm_Map r; |
65 | | }; |
66 | | |
67 | | |
68 | | static void continue_epm_recv_binding(struct composite_context *ctx); |
69 | | static void continue_epm_map(struct tevent_req *subreq); |
70 | | |
71 | | |
72 | | /* |
73 | | Stage 2 of epm_map_binding: Receive connected rpc pipe and send endpoint |
74 | | mapping rpc request |
75 | | */ |
76 | | static void continue_epm_recv_binding(struct composite_context *ctx) |
77 | 0 | { |
78 | 0 | struct composite_context *c = talloc_get_type(ctx->async.private_data, |
79 | 0 | struct composite_context); |
80 | 0 | struct epm_map_binding_state *s = talloc_get_type(c->private_data, |
81 | 0 | struct epm_map_binding_state); |
82 | 0 | struct tevent_req *subreq; |
83 | | |
84 | | /* receive result of rpc pipe connect request */ |
85 | 0 | c->status = dcerpc_pipe_connect_b_recv(ctx, c, &s->pipe); |
86 | 0 | if (!composite_is_ok(c)) return; |
87 | | |
88 | 0 | c->status = dcerpc_binding_build_tower(s->pipe, s->binding, &s->twr.tower); |
89 | 0 | if (!composite_is_ok(c)) return; |
90 | | |
91 | | /* with some nice pretty paper around it of course */ |
92 | 0 | s->r.in.object = &s->object; |
93 | 0 | s->r.in.map_tower = &s->twr; |
94 | 0 | s->r.in.entry_handle = &s->handle; |
95 | 0 | s->r.in.max_towers = 1; |
96 | 0 | s->r.out.entry_handle = &s->handle; |
97 | 0 | s->r.out.num_towers = &s->num_towers; |
98 | | |
99 | | /* send request for an endpoint mapping - a rpc request on connected pipe */ |
100 | 0 | subreq = dcerpc_epm_Map_r_send(s, c->event_ctx, |
101 | 0 | s->pipe->binding_handle, |
102 | 0 | &s->r); |
103 | 0 | if (composite_nomem(subreq, c)) return; |
104 | | |
105 | 0 | tevent_req_set_callback(subreq, continue_epm_map, c); |
106 | 0 | } |
107 | | |
108 | | |
109 | | /* |
110 | | Stage 3 of epm_map_binding: Receive endpoint mapping and provide binding details |
111 | | */ |
112 | | static void continue_epm_map(struct tevent_req *subreq) |
113 | 0 | { |
114 | 0 | struct composite_context *c = tevent_req_callback_data(subreq, |
115 | 0 | struct composite_context); |
116 | 0 | struct epm_map_binding_state *s = talloc_get_type(c->private_data, |
117 | 0 | struct epm_map_binding_state); |
118 | 0 | const char *endpoint; |
119 | | |
120 | | /* receive result of a rpc request */ |
121 | 0 | c->status = dcerpc_epm_Map_r_recv(subreq, s); |
122 | 0 | TALLOC_FREE(subreq); |
123 | 0 | if (!composite_is_ok(c)) return; |
124 | | |
125 | | /* check the details */ |
126 | 0 | if (s->r.out.result != 0 || *s->r.out.num_towers != 1) { |
127 | 0 | composite_error(c, NT_STATUS_PORT_UNREACHABLE); |
128 | 0 | return; |
129 | 0 | } |
130 | | |
131 | 0 | s->twr_r = s->r.out.towers[0].twr; |
132 | 0 | if (s->twr_r == NULL) { |
133 | 0 | composite_error(c, NT_STATUS_PORT_UNREACHABLE); |
134 | 0 | return; |
135 | 0 | } |
136 | | |
137 | 0 | if (s->twr_r->tower.num_floors != s->twr.tower.num_floors || |
138 | 0 | s->twr_r->tower.floors[3].lhs.protocol != s->twr.tower.floors[3].lhs.protocol) { |
139 | 0 | composite_error(c, NT_STATUS_PORT_UNREACHABLE); |
140 | 0 | return; |
141 | 0 | } |
142 | | |
143 | | /* get received endpoint */ |
144 | 0 | endpoint = dcerpc_floor_get_rhs_data(s, &s->twr_r->tower.floors[3]); |
145 | 0 | if (composite_nomem(endpoint, c)) return; |
146 | | |
147 | 0 | c->status = dcerpc_binding_set_string_option(s->binding, |
148 | 0 | "endpoint", |
149 | 0 | endpoint); |
150 | 0 | if (!composite_is_ok(c)) { |
151 | 0 | return; |
152 | 0 | } |
153 | | |
154 | 0 | composite_done(c); |
155 | 0 | } |
156 | | |
157 | | |
158 | | /* |
159 | | Request for endpoint mapping of dcerpc binding - try to request for endpoint |
160 | | unless there is default one. |
161 | | */ |
162 | | struct composite_context *dcerpc_epm_map_binding_send(TALLOC_CTX *mem_ctx, |
163 | | struct dcerpc_binding *binding, |
164 | | const struct ndr_interface_table *table, |
165 | | struct cli_credentials *creds, |
166 | | struct tevent_context *ev, |
167 | | struct loadparm_context *lp_ctx) |
168 | 0 | { |
169 | 0 | struct composite_context *c; |
170 | 0 | struct epm_map_binding_state *s; |
171 | 0 | struct composite_context *pipe_connect_req; |
172 | 0 | NTSTATUS status; |
173 | 0 | struct dcerpc_binding *epmapper_binding; |
174 | 0 | uint32_t i; |
175 | |
|
176 | 0 | if (ev == NULL) { |
177 | 0 | return NULL; |
178 | 0 | } |
179 | | |
180 | | /* composite context allocation and setup */ |
181 | 0 | c = composite_create(mem_ctx, ev); |
182 | 0 | if (c == NULL) { |
183 | 0 | return NULL; |
184 | 0 | } |
185 | | |
186 | 0 | s = talloc_zero(c, struct epm_map_binding_state); |
187 | 0 | if (composite_nomem(s, c)) return c; |
188 | 0 | c->private_data = s; |
189 | |
|
190 | 0 | s->binding = binding; |
191 | 0 | s->object = dcerpc_binding_get_object(binding); |
192 | 0 | s->table = table; |
193 | |
|
194 | 0 | c->status = dcerpc_binding_set_abstract_syntax(binding, |
195 | 0 | &table->syntax_id); |
196 | 0 | if (!composite_is_ok(c)) { |
197 | 0 | return c; |
198 | 0 | } |
199 | | |
200 | | /* |
201 | | First, check if there is a default endpoint specified in the IDL |
202 | | */ |
203 | 0 | for (i = 0; i < table->endpoints->count; i++) { |
204 | 0 | struct dcerpc_binding *default_binding; |
205 | 0 | enum dcerpc_transport_t transport; |
206 | 0 | enum dcerpc_transport_t dtransport; |
207 | 0 | const char *dendpoint = NULL; |
208 | |
|
209 | 0 | status = dcerpc_parse_binding(s, |
210 | 0 | table->endpoints->names[i], |
211 | 0 | &default_binding); |
212 | 0 | if (!NT_STATUS_IS_OK(status)) { |
213 | 0 | continue; |
214 | 0 | } |
215 | | |
216 | 0 | transport = dcerpc_binding_get_transport(binding); |
217 | 0 | dtransport = dcerpc_binding_get_transport(default_binding); |
218 | 0 | if (transport == NCA_UNKNOWN) { |
219 | 0 | c->status = dcerpc_binding_set_transport(binding, |
220 | 0 | dtransport); |
221 | 0 | if (!composite_is_ok(c)) { |
222 | 0 | return c; |
223 | 0 | } |
224 | 0 | transport = dtransport; |
225 | 0 | } |
226 | | |
227 | 0 | if (transport != dtransport) { |
228 | 0 | TALLOC_FREE(default_binding); |
229 | 0 | continue; |
230 | 0 | } |
231 | | |
232 | 0 | dendpoint = dcerpc_binding_get_string_option(default_binding, |
233 | 0 | "endpoint"); |
234 | 0 | if (dendpoint == NULL) { |
235 | 0 | TALLOC_FREE(default_binding); |
236 | 0 | continue; |
237 | 0 | } |
238 | | |
239 | 0 | c->status = dcerpc_binding_set_string_option(binding, |
240 | 0 | "endpoint", |
241 | 0 | dendpoint); |
242 | 0 | if (!composite_is_ok(c)) { |
243 | 0 | return c; |
244 | 0 | } |
245 | | |
246 | 0 | TALLOC_FREE(default_binding); |
247 | 0 | composite_done(c); |
248 | 0 | return c; |
249 | 0 | } |
250 | | |
251 | 0 | epmapper_binding = dcerpc_binding_dup(s, binding); |
252 | 0 | if (composite_nomem(epmapper_binding, c)) return c; |
253 | | |
254 | | /* basic endpoint mapping data */ |
255 | 0 | c->status = dcerpc_binding_set_string_option(epmapper_binding, |
256 | 0 | "endpoint", NULL); |
257 | 0 | if (!composite_is_ok(c)) { |
258 | 0 | return c; |
259 | 0 | } |
260 | 0 | c->status = dcerpc_binding_set_flags(epmapper_binding, 0, UINT32_MAX); |
261 | 0 | if (!composite_is_ok(c)) { |
262 | 0 | return c; |
263 | 0 | } |
264 | 0 | c->status = dcerpc_binding_set_assoc_group_id(epmapper_binding, 0); |
265 | 0 | if (!composite_is_ok(c)) { |
266 | 0 | return c; |
267 | 0 | } |
268 | 0 | c->status = dcerpc_binding_set_object(epmapper_binding, GUID_zero()); |
269 | 0 | if (!composite_is_ok(c)) { |
270 | 0 | return c; |
271 | 0 | } |
272 | | |
273 | | /* initiate rpc pipe connection */ |
274 | 0 | pipe_connect_req = dcerpc_pipe_connect_b_send(s, epmapper_binding, |
275 | 0 | &ndr_table_epmapper, |
276 | 0 | creds, c->event_ctx, |
277 | 0 | lp_ctx); |
278 | 0 | if (composite_nomem(pipe_connect_req, c)) return c; |
279 | | |
280 | 0 | composite_continue(c, pipe_connect_req, continue_epm_recv_binding, c); |
281 | 0 | return c; |
282 | 0 | } |
283 | | |
284 | | |
285 | | /* |
286 | | Receive result of endpoint mapping request |
287 | | */ |
288 | | NTSTATUS dcerpc_epm_map_binding_recv(struct composite_context *c) |
289 | 0 | { |
290 | 0 | NTSTATUS status = composite_wait(c); |
291 | | |
292 | 0 | talloc_free(c); |
293 | 0 | return status; |
294 | 0 | } |
295 | | |
296 | | |
297 | | /* |
298 | | Get endpoint mapping for rpc connection |
299 | | */ |
300 | | _PUBLIC_ NTSTATUS dcerpc_epm_map_binding(TALLOC_CTX *mem_ctx, struct dcerpc_binding *binding, |
301 | | const struct ndr_interface_table *table, struct tevent_context *ev, |
302 | | struct loadparm_context *lp_ctx) |
303 | 0 | { |
304 | 0 | struct composite_context *c; |
305 | 0 | struct cli_credentials *epm_creds; |
306 | |
|
307 | 0 | epm_creds = cli_credentials_init_anon(mem_ctx); |
308 | 0 | if (epm_creds == NULL) { |
309 | 0 | return NT_STATUS_NO_MEMORY; |
310 | 0 | } |
311 | 0 | c = dcerpc_epm_map_binding_send(mem_ctx, binding, table, epm_creds, ev, lp_ctx); |
312 | 0 | if (c == NULL) { |
313 | 0 | talloc_free(epm_creds); |
314 | 0 | return NT_STATUS_NO_MEMORY; |
315 | 0 | } |
316 | 0 | talloc_steal(c, epm_creds); |
317 | 0 | return dcerpc_epm_map_binding_recv(c); |
318 | 0 | } |
319 | | |
320 | | |
321 | | struct pipe_auth_state { |
322 | | struct dcerpc_pipe *pipe; |
323 | | const struct dcerpc_binding *binding; |
324 | | const struct ndr_interface_table *table; |
325 | | struct loadparm_context *lp_ctx; |
326 | | struct cli_credentials *credentials; |
327 | | unsigned int logon_retries; |
328 | | }; |
329 | | |
330 | | |
331 | | static void continue_auth_schannel(struct composite_context *ctx); |
332 | | static void continue_auth(struct composite_context *ctx); |
333 | | static void continue_auth_none(struct composite_context *ctx); |
334 | | static void continue_ntlmssp_connection(struct composite_context *ctx); |
335 | | static void continue_spnego_after_wrong_pass(struct composite_context *ctx); |
336 | | |
337 | | |
338 | | /* |
339 | | Stage 2 of pipe_auth: Receive result of schannel bind request |
340 | | */ |
341 | | static void continue_auth_schannel(struct composite_context *ctx) |
342 | 0 | { |
343 | 0 | struct composite_context *c = talloc_get_type(ctx->async.private_data, |
344 | 0 | struct composite_context); |
345 | |
|
346 | 0 | c->status = dcerpc_bind_auth_schannel_recv(ctx); |
347 | 0 | if (!composite_is_ok(c)) return; |
348 | | |
349 | 0 | composite_done(c); |
350 | 0 | } |
351 | | |
352 | | |
353 | | /* |
354 | | Stage 2 of pipe_auth: Receive result of authenticated bind request |
355 | | */ |
356 | | static void continue_auth(struct composite_context *ctx) |
357 | 0 | { |
358 | 0 | struct composite_context *c = talloc_get_type(ctx->async.private_data, |
359 | 0 | struct composite_context); |
360 | |
|
361 | 0 | c->status = dcerpc_bind_auth_recv(ctx); |
362 | 0 | if (!composite_is_ok(c)) return; |
363 | | |
364 | 0 | composite_done(c); |
365 | 0 | } |
366 | | /* |
367 | | Stage 2 of pipe_auth: Receive result of authenticated bind request, but handle fallbacks: |
368 | | SPNEGO -> NTLMSSP |
369 | | */ |
370 | | static void continue_auth_auto(struct composite_context *ctx) |
371 | 0 | { |
372 | 0 | struct composite_context *c = talloc_get_type(ctx->async.private_data, |
373 | 0 | struct composite_context); |
374 | 0 | struct pipe_auth_state *s = talloc_get_type(c->private_data, struct pipe_auth_state); |
375 | 0 | struct composite_context *sec_conn_req; |
376 | |
|
377 | 0 | c->status = dcerpc_bind_auth_recv(ctx); |
378 | 0 | if (NT_STATUS_EQUAL(c->status, NT_STATUS_INVALID_PARAMETER)) { |
379 | | /* |
380 | | * Retry with NTLMSSP auth as fallback |
381 | | * send a request for secondary rpc connection |
382 | | */ |
383 | 0 | sec_conn_req = dcerpc_secondary_connection_send(s->pipe, |
384 | 0 | s->binding); |
385 | 0 | composite_continue(c, sec_conn_req, continue_ntlmssp_connection, c); |
386 | 0 | return; |
387 | 0 | } else if (NT_STATUS_EQUAL(c->status, NT_STATUS_LOGON_FAILURE) || |
388 | 0 | NT_STATUS_EQUAL(c->status, NT_STATUS_UNSUCCESSFUL)) { |
389 | | /* |
390 | | try a second time on any error. We don't just do it |
391 | | on LOGON_FAILURE as some servers will give a |
392 | | NT_STATUS_UNSUCCESSFUL on a authentication error on RPC |
393 | | */ |
394 | 0 | const char *principal; |
395 | 0 | const char *endpoint; |
396 | |
|
397 | 0 | principal = gensec_get_target_principal(s->pipe->conn->security_state.generic_state); |
398 | 0 | if (principal == NULL) { |
399 | 0 | const char *hostname = gensec_get_target_hostname(s->pipe->conn->security_state.generic_state); |
400 | 0 | const char *service = gensec_get_target_service(s->pipe->conn->security_state.generic_state); |
401 | 0 | if (hostname != NULL && service != NULL) { |
402 | 0 | principal = talloc_asprintf(c, "%s/%s", service, hostname); |
403 | 0 | } |
404 | 0 | } |
405 | |
|
406 | 0 | endpoint = dcerpc_binding_get_string_option(s->binding, "endpoint"); |
407 | |
|
408 | 0 | if ((cli_credentials_failed_kerberos_login(s->credentials, principal, &s->logon_retries) || |
409 | 0 | cli_credentials_wrong_password(s->credentials)) && |
410 | 0 | endpoint != NULL) { |
411 | | /* |
412 | | * Retry SPNEGO with a better password |
413 | | * send a request for secondary rpc connection |
414 | | */ |
415 | 0 | sec_conn_req = dcerpc_secondary_connection_send(s->pipe, |
416 | 0 | s->binding); |
417 | 0 | composite_continue(c, sec_conn_req, continue_spnego_after_wrong_pass, c); |
418 | 0 | return; |
419 | 0 | } |
420 | 0 | } |
421 | | |
422 | 0 | if (!composite_is_ok(c)) return; |
423 | | |
424 | 0 | composite_done(c); |
425 | 0 | } |
426 | | |
427 | | /* |
428 | | Stage 3 of pipe_auth (fallback to NTLMSSP case): Receive secondary |
429 | | rpc connection (the first one can't be used any more, due to the |
430 | | bind nak) and perform authenticated bind request |
431 | | */ |
432 | | static void continue_ntlmssp_connection(struct composite_context *ctx) |
433 | 0 | { |
434 | 0 | struct composite_context *c; |
435 | 0 | struct pipe_auth_state *s; |
436 | 0 | struct composite_context *auth_req; |
437 | 0 | struct dcerpc_pipe *p2; |
438 | 0 | void *pp; |
439 | |
|
440 | 0 | c = talloc_get_type(ctx->async.private_data, struct composite_context); |
441 | 0 | s = talloc_get_type(c->private_data, struct pipe_auth_state); |
442 | | |
443 | | /* receive secondary rpc connection */ |
444 | 0 | c->status = dcerpc_secondary_connection_recv(ctx, &p2); |
445 | 0 | if (!composite_is_ok(c)) return; |
446 | | |
447 | | |
448 | | /* this is a rather strange situation. When |
449 | | we come into the routine, s is a child of s->pipe, and |
450 | | when we created p2 above, it also became a child of |
451 | | s->pipe. |
452 | | |
453 | | Now we want p2 to be a parent of s->pipe, and we want s to |
454 | | be a parent of both of them! If we don't do this very |
455 | | carefully we end up creating a talloc loop |
456 | | */ |
457 | | |
458 | | /* we need the new contexts to hang off the same context |
459 | | that s->pipe is on, but the only way to get that is |
460 | | via talloc_parent() */ |
461 | 0 | pp = talloc_parent(s->pipe); |
462 | | |
463 | | /* promote s to be at the top */ |
464 | 0 | talloc_steal(pp, s); |
465 | | |
466 | | /* and put p2 under s */ |
467 | 0 | talloc_steal(s, p2); |
468 | | |
469 | | /* now put s->pipe under p2 */ |
470 | 0 | talloc_steal(p2, s->pipe); |
471 | |
|
472 | 0 | s->pipe = p2; |
473 | | |
474 | | /* initiate a authenticated bind */ |
475 | 0 | auth_req = dcerpc_bind_auth_send(c, s->pipe, s->table, |
476 | 0 | s->credentials, |
477 | 0 | lpcfg_gensec_settings(c, s->lp_ctx), |
478 | 0 | DCERPC_AUTH_TYPE_NTLMSSP, |
479 | 0 | dcerpc_auth_level(s->pipe->conn), |
480 | 0 | s->table->authservices->names[0]); |
481 | 0 | composite_continue(c, auth_req, continue_auth, c); |
482 | 0 | } |
483 | | |
484 | | /* |
485 | | Stage 3 of pipe_auth (retry on wrong password): Receive secondary |
486 | | rpc connection (the first one can't be used any more, due to the |
487 | | bind nak) and perform authenticated bind request |
488 | | */ |
489 | | static void continue_spnego_after_wrong_pass(struct composite_context *ctx) |
490 | 0 | { |
491 | 0 | struct composite_context *c; |
492 | 0 | struct pipe_auth_state *s; |
493 | 0 | struct composite_context *auth_req; |
494 | 0 | struct dcerpc_pipe *p2; |
495 | |
|
496 | 0 | c = talloc_get_type(ctx->async.private_data, struct composite_context); |
497 | 0 | s = talloc_get_type(c->private_data, struct pipe_auth_state); |
498 | | |
499 | | /* receive secondary rpc connection */ |
500 | 0 | c->status = dcerpc_secondary_connection_recv(ctx, &p2); |
501 | 0 | if (!composite_is_ok(c)) return; |
502 | | |
503 | 0 | talloc_steal(s, p2); |
504 | 0 | talloc_steal(p2, s->pipe); |
505 | 0 | s->pipe = p2; |
506 | | |
507 | | /* initiate a authenticated bind */ |
508 | 0 | auth_req = dcerpc_bind_auth_send(c, s->pipe, s->table, |
509 | 0 | s->credentials, |
510 | 0 | lpcfg_gensec_settings(c, s->lp_ctx), |
511 | 0 | DCERPC_AUTH_TYPE_SPNEGO, |
512 | 0 | dcerpc_auth_level(s->pipe->conn), |
513 | 0 | s->table->authservices->names[0]); |
514 | 0 | composite_continue(c, auth_req, continue_auth, c); |
515 | 0 | } |
516 | | |
517 | | |
518 | | /* |
519 | | Stage 2 of pipe_auth: Receive result of non-authenticated bind request |
520 | | */ |
521 | | static void continue_auth_none(struct composite_context *ctx) |
522 | 0 | { |
523 | 0 | struct composite_context *c = talloc_get_type(ctx->async.private_data, |
524 | 0 | struct composite_context); |
525 | |
|
526 | 0 | c->status = dcerpc_bind_auth_none_recv(ctx); |
527 | 0 | if (!composite_is_ok(c)) return; |
528 | | |
529 | 0 | composite_done(c); |
530 | 0 | } |
531 | | |
532 | | |
533 | | /* |
534 | | Request to perform an authenticated bind if required. Authentication |
535 | | is determined using credentials passed and binding flags. |
536 | | */ |
537 | | struct composite_context *dcerpc_pipe_auth_send(struct dcerpc_pipe *p, |
538 | | const struct dcerpc_binding *binding, |
539 | | const struct ndr_interface_table *table, |
540 | | struct cli_credentials *credentials, |
541 | | struct loadparm_context *lp_ctx) |
542 | 0 | { |
543 | 0 | struct composite_context *c; |
544 | 0 | struct pipe_auth_state *s; |
545 | 0 | struct composite_context *auth_schannel_req; |
546 | 0 | struct composite_context *auth_req; |
547 | 0 | struct composite_context *auth_none_req; |
548 | 0 | struct dcecli_connection *conn; |
549 | 0 | uint8_t auth_type; |
550 | | |
551 | | /* composite context allocation and setup */ |
552 | 0 | c = composite_create(p, p->conn->event_ctx); |
553 | 0 | if (c == NULL) return NULL; |
554 | | |
555 | 0 | s = talloc_zero(c, struct pipe_auth_state); |
556 | 0 | if (composite_nomem(s, c)) return c; |
557 | 0 | c->private_data = s; |
558 | | |
559 | | /* store parameters in state structure */ |
560 | 0 | s->binding = binding; |
561 | 0 | s->table = table; |
562 | 0 | s->credentials = credentials; |
563 | 0 | s->pipe = p; |
564 | 0 | s->lp_ctx = lp_ctx; |
565 | |
|
566 | 0 | conn = s->pipe->conn; |
567 | 0 | conn->flags = dcerpc_binding_get_flags(binding); |
568 | |
|
569 | 0 | if (DEBUGLVL(100)) { |
570 | 0 | conn->flags |= DCERPC_DEBUG_PRINT_BOTH; |
571 | 0 | } |
572 | |
|
573 | 0 | if (conn->transport.transport == NCALRPC) { |
574 | 0 | const char *v = dcerpc_binding_get_string_option(binding, |
575 | 0 | "auth_type"); |
576 | |
|
577 | 0 | if (v != NULL && strcmp(v, "ncalrpc_as_system") == 0) { |
578 | 0 | auth_req = dcerpc_bind_auth_send(c, s->pipe, s->table, |
579 | 0 | s->credentials, |
580 | 0 | lpcfg_gensec_settings(c, s->lp_ctx), |
581 | 0 | DCERPC_AUTH_TYPE_NCALRPC_AS_SYSTEM, |
582 | 0 | DCERPC_AUTH_LEVEL_CONNECT, |
583 | 0 | s->table->authservices->names[0]); |
584 | 0 | composite_continue(c, auth_req, continue_auth, c); |
585 | 0 | return c; |
586 | 0 | } |
587 | 0 | } |
588 | | |
589 | 0 | if (cli_credentials_is_anonymous(s->credentials)) { |
590 | 0 | auth_none_req = dcerpc_bind_auth_none_send(c, s->pipe, s->table); |
591 | 0 | composite_continue(c, auth_none_req, continue_auth_none, c); |
592 | 0 | return c; |
593 | 0 | } |
594 | | |
595 | 0 | if ((conn->flags & DCERPC_SCHANNEL) && |
596 | 0 | !cli_credentials_get_netlogon_creds(s->credentials)) { |
597 | | /* If we don't already have netlogon credentials for |
598 | | * the schannel bind, then we have to get these |
599 | | * first */ |
600 | 0 | auth_schannel_req = dcerpc_bind_auth_schannel_send(c, s->pipe, s->table, |
601 | 0 | s->credentials, s->lp_ctx, |
602 | 0 | dcerpc_auth_level(conn)); |
603 | 0 | composite_continue(c, auth_schannel_req, continue_auth_schannel, c); |
604 | 0 | return c; |
605 | 0 | } |
606 | | |
607 | | /* |
608 | | * we rely on the already authenticated CIFS connection |
609 | | * if not doing sign or seal |
610 | | */ |
611 | 0 | if (conn->transport.transport == NCACN_NP && |
612 | 0 | !(conn->flags & (DCERPC_PACKET|DCERPC_SIGN|DCERPC_SEAL))) { |
613 | 0 | auth_none_req = dcerpc_bind_auth_none_send(c, s->pipe, s->table); |
614 | 0 | composite_continue(c, auth_none_req, continue_auth_none, c); |
615 | 0 | return c; |
616 | 0 | } |
617 | | |
618 | | |
619 | | /* Perform an authenticated DCE-RPC bind |
620 | | */ |
621 | 0 | if (!(conn->flags & (DCERPC_CONNECT|DCERPC_SEAL|DCERPC_PACKET))) { |
622 | | /* |
623 | | we are doing an authenticated connection, |
624 | | which needs to use [connect], [sign] or [seal]. |
625 | | If nothing is specified, we default to [sign] now. |
626 | | This give roughly the same protection as |
627 | | ncacn_np with smb signing. |
628 | | */ |
629 | 0 | conn->flags |= DCERPC_SIGN; |
630 | 0 | } |
631 | |
|
632 | 0 | if (conn->flags & DCERPC_AUTH_SPNEGO) { |
633 | 0 | auth_type = DCERPC_AUTH_TYPE_SPNEGO; |
634 | |
|
635 | 0 | } else if (conn->flags & DCERPC_AUTH_KRB5) { |
636 | 0 | auth_type = DCERPC_AUTH_TYPE_KRB5; |
637 | |
|
638 | 0 | } else if (conn->flags & DCERPC_SCHANNEL) { |
639 | 0 | struct netlogon_creds_CredentialState *ncreds = NULL; |
640 | |
|
641 | 0 | ncreds = cli_credentials_get_netlogon_creds(s->credentials); |
642 | 0 | if (ncreds->authenticate_kerberos) { |
643 | 0 | conn->flags |= DCERPC_SCHANNEL_KRB5; |
644 | 0 | } |
645 | |
|
646 | 0 | if (conn->flags & DCERPC_SCHANNEL_KRB5) { |
647 | 0 | conn->flags &= ~DCERPC_SCHANNEL_AUTO; |
648 | 0 | conn->flags |= DCERPC_SEAL; |
649 | 0 | auth_type = DCERPC_AUTH_TYPE_KRB5; |
650 | 0 | } else { |
651 | 0 | auth_type = DCERPC_AUTH_TYPE_SCHANNEL; |
652 | 0 | } |
653 | |
|
654 | 0 | } else if (conn->flags & DCERPC_AUTH_NTLM) { |
655 | 0 | auth_type = DCERPC_AUTH_TYPE_NTLMSSP; |
656 | |
|
657 | 0 | } else { |
658 | | /* try SPNEGO with fallback to NTLMSSP */ |
659 | 0 | auth_req = dcerpc_bind_auth_send(c, s->pipe, s->table, |
660 | 0 | s->credentials, |
661 | 0 | lpcfg_gensec_settings(c, s->lp_ctx), |
662 | 0 | DCERPC_AUTH_TYPE_SPNEGO, |
663 | 0 | dcerpc_auth_level(conn), |
664 | 0 | s->table->authservices->names[0]); |
665 | 0 | composite_continue(c, auth_req, continue_auth_auto, c); |
666 | 0 | return c; |
667 | 0 | } |
668 | | |
669 | 0 | auth_req = dcerpc_bind_auth_send(c, s->pipe, s->table, |
670 | 0 | s->credentials, |
671 | 0 | lpcfg_gensec_settings(c, s->lp_ctx), |
672 | 0 | auth_type, |
673 | 0 | dcerpc_auth_level(conn), |
674 | 0 | s->table->authservices->names[0]); |
675 | 0 | composite_continue(c, auth_req, continue_auth, c); |
676 | 0 | return c; |
677 | 0 | } |
678 | | |
679 | | |
680 | | /* |
681 | | Receive result of authenticated bind request on dcerpc pipe |
682 | | |
683 | | This returns *p, which may be different to the one originally |
684 | | supplied, as it rebinds to a new pipe due to authentication fallback |
685 | | |
686 | | */ |
687 | | NTSTATUS dcerpc_pipe_auth_recv(struct composite_context *c, TALLOC_CTX *mem_ctx, |
688 | | struct dcerpc_pipe **p) |
689 | 0 | { |
690 | 0 | NTSTATUS status; |
691 | |
|
692 | 0 | struct pipe_auth_state *s = talloc_get_type(c->private_data, |
693 | 0 | struct pipe_auth_state); |
694 | 0 | status = composite_wait(c); |
695 | 0 | if (!NT_STATUS_IS_OK(status)) { |
696 | 0 | char *uuid_str = GUID_string(s->pipe, &s->table->syntax_id.uuid); |
697 | 0 | DEBUG(0, ("Failed to bind to uuid %s for %s %s\n", uuid_str, |
698 | 0 | dcerpc_binding_string(uuid_str, s->binding), nt_errstr(status))); |
699 | 0 | talloc_free(uuid_str); |
700 | 0 | } else { |
701 | 0 | talloc_steal(mem_ctx, s->pipe); |
702 | 0 | *p = s->pipe; |
703 | 0 | } |
704 | |
|
705 | 0 | talloc_free(c); |
706 | 0 | return status; |
707 | 0 | } |
708 | | |
709 | | |
710 | | /* |
711 | | Perform an authenticated bind if needed - sync version |
712 | | |
713 | | This may change *p, as it rebinds to a new pipe due to authentication fallback |
714 | | */ |
715 | | _PUBLIC_ NTSTATUS dcerpc_pipe_auth(TALLOC_CTX *mem_ctx, |
716 | | struct dcerpc_pipe **p, |
717 | | const struct dcerpc_binding *binding, |
718 | | const struct ndr_interface_table *table, |
719 | | struct cli_credentials *credentials, |
720 | | struct loadparm_context *lp_ctx) |
721 | 0 | { |
722 | 0 | struct composite_context *c; |
723 | |
|
724 | 0 | c = dcerpc_pipe_auth_send(*p, binding, table, credentials, lp_ctx); |
725 | 0 | return dcerpc_pipe_auth_recv(c, mem_ctx, p); |
726 | 0 | } |
727 | | |
728 | | |
729 | | NTSTATUS dcecli_generic_session_key(struct dcecli_connection *c, |
730 | | DATA_BLOB *session_key) |
731 | 0 | { |
732 | 0 | if (c != NULL) { |
733 | 0 | if (c->transport.transport != NCALRPC && |
734 | 0 | c->transport.transport != NCACN_UNIX_STREAM) |
735 | 0 | { |
736 | 0 | return NT_STATUS_LOCAL_USER_SESSION_KEY; |
737 | 0 | } |
738 | 0 | } |
739 | | |
740 | 0 | return dcerpc_generic_session_key(session_key); |
741 | 0 | } |
742 | | |
743 | | /* |
744 | | create a secondary context from a primary connection |
745 | | |
746 | | this uses dcerpc_alter_context() to create a new dcerpc context_id |
747 | | */ |
748 | | _PUBLIC_ NTSTATUS dcerpc_secondary_context(struct dcerpc_pipe *p, |
749 | | struct dcerpc_pipe **pp2, |
750 | | const struct ndr_interface_table *table) |
751 | 0 | { |
752 | 0 | NTSTATUS status; |
753 | 0 | struct dcerpc_pipe *p2; |
754 | 0 | struct GUID *object = NULL; |
755 | | |
756 | 0 | p2 = talloc_zero(p, struct dcerpc_pipe); |
757 | 0 | if (p2 == NULL) { |
758 | 0 | return NT_STATUS_NO_MEMORY; |
759 | 0 | } |
760 | 0 | p2->conn = talloc_reference(p2, p->conn); |
761 | 0 | p2->request_timeout = p->request_timeout; |
762 | |
|
763 | 0 | p2->context_id = ++p->conn->next_context_id; |
764 | |
|
765 | 0 | p2->syntax = table->syntax_id; |
766 | |
|
767 | 0 | p2->transfer_syntax = p->transfer_syntax; |
768 | |
|
769 | 0 | p2->binding = dcerpc_binding_dup(p2, p->binding); |
770 | 0 | if (p2->binding == NULL) { |
771 | 0 | talloc_free(p2); |
772 | 0 | return NT_STATUS_NO_MEMORY; |
773 | 0 | } |
774 | | |
775 | 0 | p2->object = dcerpc_binding_get_object(p2->binding); |
776 | 0 | if (!GUID_all_zero(&p2->object)) { |
777 | 0 | object = &p2->object; |
778 | 0 | } |
779 | |
|
780 | 0 | p2->binding_handle = dcerpc_pipe_binding_handle(p2, object, table); |
781 | 0 | if (p2->binding_handle == NULL) { |
782 | 0 | talloc_free(p2); |
783 | 0 | return NT_STATUS_NO_MEMORY; |
784 | 0 | } |
785 | | |
786 | 0 | status = dcerpc_alter_context(p2, p2, &p2->syntax, &p2->transfer_syntax); |
787 | 0 | if (!NT_STATUS_IS_OK(status)) { |
788 | 0 | talloc_free(p2); |
789 | 0 | return status; |
790 | 0 | } |
791 | | |
792 | 0 | *pp2 = p2; |
793 | |
|
794 | 0 | return NT_STATUS_OK; |
795 | 0 | } |