/src/samba/source3/rpc_client/cli_pipe.c
Line | Count | Source |
1 | | /* |
2 | | * Unix SMB/CIFS implementation. |
3 | | * RPC Pipe client routines |
4 | | * Largely rewritten by Jeremy Allison 2005. |
5 | | * Heavily modified by Simo Sorce 2010. |
6 | | * Copyright Andrew Bartlett 2011. |
7 | | * |
8 | | * This program is free software; you can redistribute it and/or modify |
9 | | * it under the terms of the GNU General Public License as published by |
10 | | * the Free Software Foundation; either version 3 of the License, or |
11 | | * (at your option) any later version. |
12 | | * |
13 | | * This program is distributed in the hope that it will be useful, |
14 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
15 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
16 | | * GNU General Public License for more details. |
17 | | * |
18 | | * You should have received a copy of the GNU General Public License |
19 | | * along with this program; if not, see <http://www.gnu.org/licenses/>. |
20 | | */ |
21 | | |
22 | | #define SOURCE3_LIBRPC_INTERNALS 1 |
23 | | |
24 | | #include "includes.h" |
25 | | #include "libsmb/namequery.h" |
26 | | #include "../lib/util/tevent_ntstatus.h" |
27 | | #include "librpc/gen_ndr/ndr_epmapper_c.h" |
28 | | #include "../librpc/gen_ndr/ndr_dssetup.h" |
29 | | #include "../libcli/auth/schannel.h" |
30 | | #include "../libcli/auth/netlogon_creds_cli.h" |
31 | | #include "auth_generic.h" |
32 | | #include "librpc/gen_ndr/ndr_dcerpc.h" |
33 | | #include "librpc/gen_ndr/ndr_netlogon_c.h" |
34 | | #include "librpc/gen_ndr/auth.h" |
35 | | #include "librpc/rpc/dcerpc.h" |
36 | | #include "librpc/rpc/dcerpc_internal.h" |
37 | | #include "librpc/rpc/dcerpc_util.h" |
38 | | #include "rpc_dce.h" |
39 | | #include "cli_pipe.h" |
40 | | #include "source3/include/client.h" |
41 | | #include "source3/libsmb/proto.h" |
42 | | #include "auth/gensec/gensec.h" |
43 | | #include "auth/credentials/credentials.h" |
44 | | #include "auth/auth_util.h" |
45 | | #include "../libcli/smb/smbXcli_base.h" |
46 | | #include "lib/tsocket/tsocket.h" |
47 | | #include "libcli/named_pipe_auth/npa_tstream.h" |
48 | | #include "librpc/gen_ndr/ndr_winreg.h" |
49 | | #include "local_np.h" |
50 | | #include "libcli/smb/tstream_smbXcli_np.h" |
51 | | |
52 | | #undef DBGC_CLASS |
53 | 0 | #define DBGC_CLASS DBGC_RPC_CLI |
54 | | |
55 | | static const uint16_t default_bt_features = |
56 | | DCERPC_BIND_TIME_KEEP_CONNECTION_ON_ORPHAN | |
57 | | DCERPC_BIND_TIME_SECURITY_CONTEXT_MULTIPLEXING; |
58 | | |
59 | | struct rpc_client_association { |
60 | | struct dcerpc_binding *binding; |
61 | | |
62 | | struct { |
63 | | uint16_t client; |
64 | | uint16_t negotiated; |
65 | | bool negotiation_done; |
66 | | } features; |
67 | | |
68 | | struct samba_sockaddr addr; |
69 | | |
70 | | uint32_t next_call_id; |
71 | | }; |
72 | | |
73 | | static NTSTATUS rpc_client_association_create(TALLOC_CTX *mem_ctx, |
74 | | uint16_t client_features, |
75 | | uint32_t flags, |
76 | | const char *target_hostname, |
77 | | enum dcerpc_transport_t transport, |
78 | | const struct samba_sockaddr *addr, |
79 | | const char *endpoint, |
80 | | struct rpc_client_association **passoc) |
81 | 0 | { |
82 | 0 | struct rpc_client_association *assoc = NULL; |
83 | 0 | struct dcerpc_binding *bd = NULL; |
84 | 0 | char addrstr[INET6_ADDRSTRLEN] = { 0, }; |
85 | 0 | NTSTATUS status; |
86 | |
|
87 | 0 | assoc = talloc_zero(mem_ctx, struct rpc_client_association); |
88 | 0 | if (assoc == NULL) { |
89 | 0 | return NT_STATUS_NO_MEMORY; |
90 | 0 | } |
91 | 0 | assoc->features.client = client_features; |
92 | 0 | if (client_features == 0) { |
93 | | /* |
94 | | * Without requested features there |
95 | | * is no point in trying to negotiate |
96 | | * something, so we are done... |
97 | | */ |
98 | 0 | assoc->features.negotiation_done = true; |
99 | 0 | } |
100 | |
|
101 | 0 | status = dcerpc_parse_binding(assoc, "", &bd); |
102 | 0 | if (!NT_STATUS_IS_OK(status)) { |
103 | 0 | TALLOC_FREE(assoc); |
104 | 0 | return status; |
105 | 0 | } |
106 | 0 | status = dcerpc_binding_set_transport(bd, transport); |
107 | 0 | if (!NT_STATUS_IS_OK(status)) { |
108 | 0 | TALLOC_FREE(assoc); |
109 | 0 | return status; |
110 | 0 | } |
111 | 0 | switch (addr->u.sa.sa_family) { |
112 | 0 | case AF_INET: |
113 | 0 | case AF_INET6: |
114 | 0 | print_sockaddr(addrstr, |
115 | 0 | sizeof(addrstr), |
116 | 0 | &addr->u.ss); |
117 | 0 | status = dcerpc_binding_set_string_option(bd, |
118 | 0 | "host", |
119 | 0 | addrstr); |
120 | 0 | if (!NT_STATUS_IS_OK(status)) { |
121 | 0 | TALLOC_FREE(assoc); |
122 | 0 | return status; |
123 | 0 | } |
124 | 0 | break; |
125 | 0 | default: |
126 | 0 | status = dcerpc_binding_set_string_option(bd, |
127 | 0 | "host", |
128 | 0 | target_hostname); |
129 | 0 | if (!NT_STATUS_IS_OK(status)) { |
130 | 0 | TALLOC_FREE(assoc); |
131 | 0 | return status; |
132 | 0 | } |
133 | 0 | break; |
134 | 0 | } |
135 | 0 | status = dcerpc_binding_set_string_option(bd, |
136 | 0 | "target_hostname", |
137 | 0 | target_hostname); |
138 | 0 | if (!NT_STATUS_IS_OK(status)) { |
139 | 0 | TALLOC_FREE(assoc); |
140 | 0 | return status; |
141 | 0 | } |
142 | 0 | status = dcerpc_binding_set_string_option(bd, |
143 | 0 | "endpoint", |
144 | 0 | endpoint); |
145 | 0 | if (!NT_STATUS_IS_OK(status)) { |
146 | 0 | TALLOC_FREE(assoc); |
147 | 0 | return status; |
148 | 0 | } |
149 | 0 | status = dcerpc_binding_set_flags(bd, flags, 0); |
150 | 0 | if (!NT_STATUS_IS_OK(status)) { |
151 | 0 | TALLOC_FREE(assoc); |
152 | 0 | return status; |
153 | 0 | } |
154 | | |
155 | 0 | assoc->binding = bd; |
156 | 0 | assoc->addr = *addr; |
157 | |
|
158 | 0 | *passoc = assoc; |
159 | 0 | return NT_STATUS_OK; |
160 | 0 | } |
161 | | |
162 | | struct rpc_client_connection { |
163 | | DATA_BLOB transport_session_key; |
164 | | struct rpc_cli_transport *transport; |
165 | | struct samba_sockaddr localaddr; |
166 | | |
167 | | struct { |
168 | | uint16_t max_xmit_frag; |
169 | | uint16_t max_recv_frag; |
170 | | bool client_hdr_signing; |
171 | | bool hdr_signing; |
172 | | bool bind_done; |
173 | | } features; |
174 | | |
175 | | uint32_t next_auth_context_id; |
176 | | uint16_t next_pres_context_id; |
177 | | }; |
178 | | |
179 | | static NTSTATUS rpc_client_connection_create(TALLOC_CTX *mem_ctx, |
180 | | const struct rpc_client_association *assoc, |
181 | | uint16_t max_frag, |
182 | | struct rpc_client_connection **pconn) |
183 | 0 | { |
184 | 0 | struct rpc_client_connection *conn = NULL; |
185 | 0 | uint32_t flags = dcerpc_binding_get_flags(assoc->binding); |
186 | 0 | bool client_hdr_signing = (flags & DCERPC_PROPOSE_HEADER_SIGNING); |
187 | |
|
188 | 0 | conn = talloc_zero(mem_ctx, struct rpc_client_connection); |
189 | 0 | if (conn == NULL) { |
190 | 0 | return NT_STATUS_NO_MEMORY; |
191 | 0 | } |
192 | 0 | conn->features.max_xmit_frag = max_frag; |
193 | 0 | conn->features.max_recv_frag = max_frag; |
194 | 0 | conn->features.client_hdr_signing = client_hdr_signing; |
195 | |
|
196 | 0 | *pconn = conn; |
197 | 0 | return NT_STATUS_OK; |
198 | 0 | } |
199 | | |
200 | | static int rpc_pipe_client_wrap_destructor(struct rpc_pipe_client *p) |
201 | 0 | { |
202 | 0 | if (p->np_cli != NULL) { |
203 | 0 | DLIST_REMOVE(p->np_cli->pipe_list, p); |
204 | 0 | p->np_cli = NULL; |
205 | 0 | } |
206 | |
|
207 | 0 | return 0; |
208 | 0 | } |
209 | | |
210 | | static NTSTATUS rpc_pipe_wrap_create( |
211 | | const struct ndr_interface_table *table, |
212 | | struct cli_state *np_cli, |
213 | | struct rpc_client_association **passoc, |
214 | | struct rpc_client_connection **pconn, |
215 | | TALLOC_CTX *mem_ctx, |
216 | | struct rpc_pipe_client **presult) |
217 | 0 | { |
218 | 0 | struct rpc_pipe_client *result = NULL; |
219 | 0 | const char *hostname = NULL; |
220 | |
|
221 | 0 | result = talloc_zero(mem_ctx, struct rpc_pipe_client); |
222 | 0 | if (result == NULL) { |
223 | 0 | return NT_STATUS_NO_MEMORY; |
224 | 0 | } |
225 | 0 | talloc_set_destructor(result, rpc_pipe_client_wrap_destructor); |
226 | |
|
227 | 0 | result->assoc = talloc_move(result, passoc); |
228 | 0 | result->conn = talloc_move(result, pconn); |
229 | | |
230 | | /* rpc_pipe_bind_send should allocate an id... */ |
231 | 0 | result->pres_context_id = UINT16_MAX; |
232 | 0 | result->table = table; |
233 | 0 | result->transfer_syntax = ndr_transfer_syntax_ndr; |
234 | |
|
235 | 0 | hostname = dcerpc_binding_get_string_option(result->assoc->binding, |
236 | 0 | "target_hostname"); |
237 | 0 | result->desthost = talloc_strdup(result, hostname); |
238 | 0 | if (result->desthost == NULL) { |
239 | 0 | TALLOC_FREE(result); |
240 | 0 | return NT_STATUS_NO_MEMORY; |
241 | 0 | } |
242 | | |
243 | 0 | result->srv_name_slash = talloc_asprintf_strupper_m( |
244 | 0 | result, "\\\\%s", result->desthost); |
245 | 0 | if (result->srv_name_slash == NULL) { |
246 | 0 | TALLOC_FREE(result); |
247 | 0 | return NT_STATUS_NO_MEMORY; |
248 | 0 | } |
249 | | |
250 | 0 | if (np_cli != NULL) { |
251 | 0 | result->np_cli = np_cli; |
252 | 0 | DLIST_ADD_END(np_cli->pipe_list, result); |
253 | 0 | } |
254 | |
|
255 | 0 | *presult = result; |
256 | 0 | return NT_STATUS_OK; |
257 | 0 | } |
258 | | |
259 | | /******************************************************************** |
260 | | Pipe description for a DEBUG |
261 | | ********************************************************************/ |
262 | | static const char *rpccli_pipe_txt(TALLOC_CTX *mem_ctx, |
263 | | struct rpc_pipe_client *cli) |
264 | 0 | { |
265 | 0 | char *result = talloc_asprintf(mem_ctx, "host %s", cli->desthost); |
266 | 0 | if (result == NULL) { |
267 | 0 | return "pipe"; |
268 | 0 | } |
269 | 0 | return result; |
270 | 0 | } |
271 | | |
272 | | /******************************************************************* |
273 | | Use SMBreadX to get rest of one fragment's worth of rpc data. |
274 | | Reads the whole size or give an error message |
275 | | ********************************************************************/ |
276 | | |
277 | | struct rpc_read_state { |
278 | | struct tevent_context *ev; |
279 | | struct rpc_cli_transport *transport; |
280 | | uint8_t *data; |
281 | | size_t size; |
282 | | size_t num_read; |
283 | | }; |
284 | | |
285 | | static void rpc_read_done(struct tevent_req *subreq); |
286 | | |
287 | | static struct tevent_req *rpc_read_send(TALLOC_CTX *mem_ctx, |
288 | | struct tevent_context *ev, |
289 | | struct rpc_cli_transport *transport, |
290 | | uint8_t *data, size_t size) |
291 | 0 | { |
292 | 0 | struct tevent_req *req, *subreq; |
293 | 0 | struct rpc_read_state *state; |
294 | |
|
295 | 0 | req = tevent_req_create(mem_ctx, &state, struct rpc_read_state); |
296 | 0 | if (req == NULL) { |
297 | 0 | return NULL; |
298 | 0 | } |
299 | 0 | state->ev = ev; |
300 | 0 | state->transport = transport; |
301 | 0 | state->data = data; |
302 | 0 | state->size = size; |
303 | 0 | state->num_read = 0; |
304 | |
|
305 | 0 | DBG_INFO("data_to_read: %zu\n", size); |
306 | |
|
307 | 0 | subreq = transport->read_send(state, ev, (uint8_t *)data, size, |
308 | 0 | transport->priv); |
309 | 0 | if (subreq == NULL) { |
310 | 0 | goto fail; |
311 | 0 | } |
312 | 0 | tevent_req_set_callback(subreq, rpc_read_done, req); |
313 | 0 | return req; |
314 | | |
315 | 0 | fail: |
316 | 0 | TALLOC_FREE(req); |
317 | 0 | return NULL; |
318 | 0 | } |
319 | | |
320 | | static void rpc_read_done(struct tevent_req *subreq) |
321 | 0 | { |
322 | 0 | struct tevent_req *req = tevent_req_callback_data( |
323 | 0 | subreq, struct tevent_req); |
324 | 0 | struct rpc_read_state *state = tevent_req_data( |
325 | 0 | req, struct rpc_read_state); |
326 | 0 | NTSTATUS status; |
327 | 0 | ssize_t received; |
328 | |
|
329 | 0 | status = state->transport->read_recv(subreq, &received); |
330 | 0 | TALLOC_FREE(subreq); |
331 | 0 | if (tevent_req_nterror(req, status)) { |
332 | 0 | return; |
333 | 0 | } |
334 | | |
335 | 0 | state->num_read += received; |
336 | 0 | if (state->num_read == state->size) { |
337 | 0 | tevent_req_done(req); |
338 | 0 | return; |
339 | 0 | } |
340 | | |
341 | 0 | subreq = state->transport->read_send(state, state->ev, |
342 | 0 | state->data + state->num_read, |
343 | 0 | state->size - state->num_read, |
344 | 0 | state->transport->priv); |
345 | 0 | if (tevent_req_nomem(subreq, req)) { |
346 | 0 | return; |
347 | 0 | } |
348 | 0 | tevent_req_set_callback(subreq, rpc_read_done, req); |
349 | 0 | } |
350 | | |
351 | | static NTSTATUS rpc_read_recv(struct tevent_req *req) |
352 | 0 | { |
353 | 0 | return tevent_req_simple_recv_ntstatus(req); |
354 | 0 | } |
355 | | |
356 | | struct rpc_write_state { |
357 | | struct tevent_context *ev; |
358 | | struct rpc_cli_transport *transport; |
359 | | const uint8_t *data; |
360 | | size_t size; |
361 | | size_t num_written; |
362 | | }; |
363 | | |
364 | | static void rpc_write_done(struct tevent_req *subreq); |
365 | | |
366 | | static struct tevent_req *rpc_write_send(TALLOC_CTX *mem_ctx, |
367 | | struct tevent_context *ev, |
368 | | struct rpc_cli_transport *transport, |
369 | | const uint8_t *data, size_t size) |
370 | 0 | { |
371 | 0 | struct tevent_req *req, *subreq; |
372 | 0 | struct rpc_write_state *state; |
373 | |
|
374 | 0 | req = tevent_req_create(mem_ctx, &state, struct rpc_write_state); |
375 | 0 | if (req == NULL) { |
376 | 0 | return NULL; |
377 | 0 | } |
378 | 0 | state->ev = ev; |
379 | 0 | state->transport = transport; |
380 | 0 | state->data = data; |
381 | 0 | state->size = size; |
382 | 0 | state->num_written = 0; |
383 | |
|
384 | 0 | DBG_INFO("data_to_write: %zu\n", size); |
385 | |
|
386 | 0 | subreq = transport->write_send(state, ev, data, size, transport->priv); |
387 | 0 | if (tevent_req_nomem(subreq, req)) { |
388 | 0 | return tevent_req_post(req, ev); |
389 | 0 | } |
390 | 0 | tevent_req_set_callback(subreq, rpc_write_done, req); |
391 | 0 | return req; |
392 | 0 | } |
393 | | |
394 | | static void rpc_write_done(struct tevent_req *subreq) |
395 | 0 | { |
396 | 0 | struct tevent_req *req = tevent_req_callback_data( |
397 | 0 | subreq, struct tevent_req); |
398 | 0 | struct rpc_write_state *state = tevent_req_data( |
399 | 0 | req, struct rpc_write_state); |
400 | 0 | NTSTATUS status; |
401 | 0 | ssize_t written; |
402 | |
|
403 | 0 | status = state->transport->write_recv(subreq, &written); |
404 | 0 | TALLOC_FREE(subreq); |
405 | 0 | if (tevent_req_nterror(req, status)) { |
406 | 0 | return; |
407 | 0 | } |
408 | | |
409 | 0 | state->num_written += written; |
410 | |
|
411 | 0 | if (state->num_written == state->size) { |
412 | 0 | tevent_req_done(req); |
413 | 0 | return; |
414 | 0 | } |
415 | | |
416 | 0 | subreq = state->transport->write_send(state, state->ev, |
417 | 0 | state->data + state->num_written, |
418 | 0 | state->size - state->num_written, |
419 | 0 | state->transport->priv); |
420 | 0 | if (tevent_req_nomem(subreq, req)) { |
421 | 0 | return; |
422 | 0 | } |
423 | 0 | tevent_req_set_callback(subreq, rpc_write_done, req); |
424 | 0 | } |
425 | | |
426 | | static NTSTATUS rpc_write_recv(struct tevent_req *req) |
427 | 0 | { |
428 | 0 | return tevent_req_simple_recv_ntstatus(req); |
429 | 0 | } |
430 | | |
431 | | |
432 | | /**************************************************************************** |
433 | | Try and get a PDU's worth of data from current_pdu. If not, then read more |
434 | | from the wire. |
435 | | ****************************************************************************/ |
436 | | |
437 | | struct get_complete_frag_state { |
438 | | struct tevent_context *ev; |
439 | | struct rpc_pipe_client *cli; |
440 | | uint16_t frag_len; |
441 | | DATA_BLOB *pdu; |
442 | | }; |
443 | | |
444 | | static void get_complete_frag_got_header(struct tevent_req *subreq); |
445 | | static void get_complete_frag_got_rest(struct tevent_req *subreq); |
446 | | |
447 | | static struct tevent_req *get_complete_frag_send(TALLOC_CTX *mem_ctx, |
448 | | struct tevent_context *ev, |
449 | | struct rpc_pipe_client *cli, |
450 | | DATA_BLOB *pdu) |
451 | 0 | { |
452 | 0 | struct tevent_req *req, *subreq; |
453 | 0 | struct get_complete_frag_state *state; |
454 | 0 | size_t received; |
455 | |
|
456 | 0 | req = tevent_req_create(mem_ctx, &state, |
457 | 0 | struct get_complete_frag_state); |
458 | 0 | if (req == NULL) { |
459 | 0 | return NULL; |
460 | 0 | } |
461 | 0 | state->ev = ev; |
462 | 0 | state->cli = cli; |
463 | 0 | state->frag_len = RPC_HEADER_LEN; |
464 | 0 | state->pdu = pdu; |
465 | |
|
466 | 0 | received = pdu->length; |
467 | 0 | if (received < RPC_HEADER_LEN) { |
468 | 0 | if (!data_blob_realloc(mem_ctx, pdu, RPC_HEADER_LEN)) { |
469 | 0 | tevent_req_oom(req); |
470 | 0 | return tevent_req_post(req, ev); |
471 | 0 | } |
472 | 0 | subreq = rpc_read_send(state, state->ev, |
473 | 0 | state->cli->conn->transport, |
474 | 0 | pdu->data + received, |
475 | 0 | RPC_HEADER_LEN - received); |
476 | 0 | if (tevent_req_nomem(subreq, req)) { |
477 | 0 | return tevent_req_post(req, ev); |
478 | 0 | } |
479 | 0 | tevent_req_set_callback(subreq, get_complete_frag_got_header, |
480 | 0 | req); |
481 | 0 | return req; |
482 | 0 | } |
483 | | |
484 | 0 | state->frag_len = dcerpc_get_frag_length(pdu); |
485 | 0 | if (state->frag_len < RPC_HEADER_LEN) { |
486 | 0 | tevent_req_nterror(req, NT_STATUS_RPC_PROTOCOL_ERROR); |
487 | 0 | return tevent_req_post(req, ev); |
488 | 0 | } |
489 | | |
490 | 0 | if (received >= state->frag_len) { |
491 | | /* |
492 | | * Got the whole fragment |
493 | | */ |
494 | 0 | tevent_req_done(req); |
495 | 0 | return tevent_req_post(req, ev); |
496 | 0 | } |
497 | | |
498 | 0 | if (!data_blob_realloc(NULL, pdu, state->frag_len)) { |
499 | 0 | tevent_req_oom(req); |
500 | 0 | return tevent_req_post(req, ev); |
501 | 0 | } |
502 | | |
503 | 0 | subreq = rpc_read_send( |
504 | 0 | state, |
505 | 0 | state->ev, |
506 | 0 | state->cli->conn->transport, |
507 | 0 | pdu->data + received, |
508 | 0 | state->frag_len - received); |
509 | 0 | if (tevent_req_nomem(subreq, req)) { |
510 | 0 | return tevent_req_post(req, ev); |
511 | 0 | } |
512 | 0 | tevent_req_set_callback(subreq, get_complete_frag_got_rest, req); |
513 | 0 | return req; |
514 | 0 | } |
515 | | |
516 | | static void get_complete_frag_got_header(struct tevent_req *subreq) |
517 | 0 | { |
518 | 0 | struct tevent_req *req = tevent_req_callback_data( |
519 | 0 | subreq, struct tevent_req); |
520 | 0 | struct get_complete_frag_state *state = tevent_req_data( |
521 | 0 | req, struct get_complete_frag_state); |
522 | 0 | NTSTATUS status; |
523 | |
|
524 | 0 | status = rpc_read_recv(subreq); |
525 | 0 | TALLOC_FREE(subreq); |
526 | 0 | if (tevent_req_nterror(req, status)) { |
527 | 0 | return; |
528 | 0 | } |
529 | | |
530 | 0 | state->frag_len = dcerpc_get_frag_length(state->pdu); |
531 | 0 | if (state->frag_len < RPC_HEADER_LEN) { |
532 | 0 | tevent_req_nterror(req, NT_STATUS_RPC_PROTOCOL_ERROR); |
533 | 0 | return; |
534 | 0 | } |
535 | | |
536 | 0 | if (!data_blob_realloc(NULL, state->pdu, state->frag_len)) { |
537 | 0 | tevent_req_oom(req); |
538 | 0 | return; |
539 | 0 | } |
540 | | |
541 | | /* |
542 | | * We're here in this piece of code because we've read exactly |
543 | | * RPC_HEADER_LEN bytes into state->pdu. |
544 | | */ |
545 | | |
546 | 0 | subreq = rpc_read_send(state, state->ev, |
547 | 0 | state->cli->conn->transport, |
548 | 0 | state->pdu->data + RPC_HEADER_LEN, |
549 | 0 | state->frag_len - RPC_HEADER_LEN); |
550 | 0 | if (tevent_req_nomem(subreq, req)) { |
551 | 0 | return; |
552 | 0 | } |
553 | 0 | tevent_req_set_callback(subreq, get_complete_frag_got_rest, req); |
554 | 0 | } |
555 | | |
556 | | static void get_complete_frag_got_rest(struct tevent_req *subreq) |
557 | 0 | { |
558 | 0 | NTSTATUS status = rpc_read_recv(subreq); |
559 | 0 | return tevent_req_simple_finish_ntstatus(subreq, status); |
560 | 0 | } |
561 | | |
562 | | static NTSTATUS get_complete_frag_recv(struct tevent_req *req) |
563 | 0 | { |
564 | 0 | return tevent_req_simple_recv_ntstatus(req); |
565 | 0 | } |
566 | | |
567 | | /**************************************************************************** |
568 | | Do basic authentication checks on an incoming pdu. |
569 | | ****************************************************************************/ |
570 | | |
571 | | static NTSTATUS cli_pipe_validate_current_pdu(TALLOC_CTX *mem_ctx, |
572 | | struct rpc_pipe_client *cli, |
573 | | struct ncacn_packet *pkt, |
574 | | DATA_BLOB *pdu, |
575 | | uint8_t expected_pkt_type, |
576 | | uint32_t call_id, |
577 | | DATA_BLOB *rdata, |
578 | | DATA_BLOB *reply_pdu) |
579 | 0 | { |
580 | 0 | const struct dcerpc_response *r = NULL; |
581 | 0 | DATA_BLOB tmp_stub = { .data = NULL }; |
582 | 0 | NTSTATUS ret; |
583 | | |
584 | | /* |
585 | | * Point the return values at the real data including the RPC |
586 | | * header. Just in case the caller wants it. |
587 | | */ |
588 | 0 | *rdata = *pdu; |
589 | |
|
590 | 0 | if ((pkt->ptype == DCERPC_PKT_BIND_ACK) && |
591 | 0 | !(pkt->pfc_flags & DCERPC_PFC_FLAG_LAST)) { |
592 | | /* |
593 | | * TODO: do we still need this hack which was introduced |
594 | | * in commit a42afcdcc7ab9aa9ed193ae36d3dbb10843447f0. |
595 | | * |
596 | | * I don't even know what AS/U might be... |
597 | | */ |
598 | 0 | DEBUG(5, (__location__ ": bug in server (AS/U?), setting " |
599 | 0 | "fragment first/last ON.\n")); |
600 | 0 | pkt->pfc_flags |= DCERPC_PFC_FLAG_FIRST | DCERPC_PFC_FLAG_LAST; |
601 | 0 | } |
602 | | |
603 | | /* Ensure we have the correct type. */ |
604 | 0 | switch (pkt->ptype) { |
605 | 0 | case DCERPC_PKT_BIND_NAK: |
606 | 0 | DEBUG(1, (__location__ ": Bind NACK received from %s!\n", |
607 | 0 | rpccli_pipe_txt(talloc_tos(), cli))); |
608 | |
|
609 | 0 | ret = dcerpc_verify_ncacn_packet_header(pkt, |
610 | 0 | DCERPC_PKT_BIND_NAK, |
611 | 0 | 0, /* max_auth_info */ |
612 | 0 | DCERPC_PFC_FLAG_FIRST | |
613 | 0 | DCERPC_PFC_FLAG_LAST, |
614 | 0 | 0); /* optional flags */ |
615 | 0 | if (!NT_STATUS_IS_OK(ret)) { |
616 | 0 | DEBUG(1, (__location__ ": Connection to %s got an unexpected " |
617 | 0 | "RPC packet type - %u, expected %u: %s\n", |
618 | 0 | rpccli_pipe_txt(talloc_tos(), cli), |
619 | 0 | pkt->ptype, expected_pkt_type, |
620 | 0 | nt_errstr(ret))); |
621 | 0 | NDR_PRINT_DEBUG(ncacn_packet, pkt); |
622 | 0 | return ret; |
623 | 0 | } |
624 | | |
625 | | /* Use this for now... */ |
626 | 0 | return NT_STATUS_NETWORK_ACCESS_DENIED; |
627 | | |
628 | 0 | case DCERPC_PKT_BIND_ACK: |
629 | 0 | ret = dcerpc_verify_ncacn_packet_header(pkt, |
630 | 0 | expected_pkt_type, |
631 | 0 | pkt->u.bind_ack.auth_info.length, |
632 | 0 | DCERPC_PFC_FLAG_FIRST | |
633 | 0 | DCERPC_PFC_FLAG_LAST, |
634 | 0 | DCERPC_PFC_FLAG_CONC_MPX | |
635 | 0 | DCERPC_PFC_FLAG_SUPPORT_HEADER_SIGN); |
636 | 0 | if (!NT_STATUS_IS_OK(ret)) { |
637 | 0 | DEBUG(1, (__location__ ": Connection to %s got an unexpected " |
638 | 0 | "RPC packet type - %u, expected %u: %s\n", |
639 | 0 | rpccli_pipe_txt(talloc_tos(), cli), |
640 | 0 | pkt->ptype, expected_pkt_type, |
641 | 0 | nt_errstr(ret))); |
642 | 0 | NDR_PRINT_DEBUG(ncacn_packet, pkt); |
643 | 0 | return ret; |
644 | 0 | } |
645 | | |
646 | 0 | break; |
647 | | |
648 | 0 | case DCERPC_PKT_ALTER_RESP: |
649 | 0 | ret = dcerpc_verify_ncacn_packet_header(pkt, |
650 | 0 | expected_pkt_type, |
651 | 0 | pkt->u.alter_resp.auth_info.length, |
652 | 0 | DCERPC_PFC_FLAG_FIRST | |
653 | 0 | DCERPC_PFC_FLAG_LAST, |
654 | 0 | DCERPC_PFC_FLAG_CONC_MPX | |
655 | 0 | DCERPC_PFC_FLAG_SUPPORT_HEADER_SIGN); |
656 | 0 | if (!NT_STATUS_IS_OK(ret)) { |
657 | 0 | DEBUG(1, (__location__ ": Connection to %s got an unexpected " |
658 | 0 | "RPC packet type - %u, expected %u: %s\n", |
659 | 0 | rpccli_pipe_txt(talloc_tos(), cli), |
660 | 0 | pkt->ptype, expected_pkt_type, |
661 | 0 | nt_errstr(ret))); |
662 | 0 | NDR_PRINT_DEBUG(ncacn_packet, pkt); |
663 | 0 | return ret; |
664 | 0 | } |
665 | | |
666 | 0 | break; |
667 | | |
668 | 0 | case DCERPC_PKT_RESPONSE: |
669 | |
|
670 | 0 | r = &pkt->u.response; |
671 | |
|
672 | 0 | ret = dcerpc_verify_ncacn_packet_header(pkt, |
673 | 0 | expected_pkt_type, |
674 | 0 | r->stub_and_verifier.length, |
675 | 0 | 0, /* required_flags */ |
676 | 0 | DCERPC_PFC_FLAG_FIRST | |
677 | 0 | DCERPC_PFC_FLAG_LAST); |
678 | 0 | if (!NT_STATUS_IS_OK(ret)) { |
679 | 0 | DEBUG(1, (__location__ ": Connection to %s got an unexpected " |
680 | 0 | "RPC packet type - %u, expected %u: %s\n", |
681 | 0 | rpccli_pipe_txt(talloc_tos(), cli), |
682 | 0 | pkt->ptype, expected_pkt_type, |
683 | 0 | nt_errstr(ret))); |
684 | 0 | NDR_PRINT_DEBUG(ncacn_packet, pkt); |
685 | 0 | return ret; |
686 | 0 | } |
687 | | |
688 | 0 | tmp_stub.data = r->stub_and_verifier.data; |
689 | 0 | tmp_stub.length = r->stub_and_verifier.length; |
690 | | |
691 | | /* Here's where we deal with incoming sign/seal. */ |
692 | 0 | ret = dcerpc_check_auth(cli->auth, pkt, |
693 | 0 | &tmp_stub, |
694 | 0 | DCERPC_RESPONSE_LENGTH, |
695 | 0 | pdu); |
696 | 0 | if (!NT_STATUS_IS_OK(ret)) { |
697 | 0 | DEBUG(1, (__location__ ": Connection to %s got an unexpected " |
698 | 0 | "RPC packet type - %u, expected %u: %s\n", |
699 | 0 | rpccli_pipe_txt(talloc_tos(), cli), |
700 | 0 | pkt->ptype, expected_pkt_type, |
701 | 0 | nt_errstr(ret))); |
702 | 0 | NDR_PRINT_DEBUG(ncacn_packet, pkt); |
703 | 0 | return ret; |
704 | 0 | } |
705 | | |
706 | | /* Point the return values at the NDR data. */ |
707 | 0 | *rdata = tmp_stub; |
708 | |
|
709 | 0 | DEBUG(10, ("Got pdu len %lu, data_len %lu\n", |
710 | 0 | (long unsigned int)pdu->length, |
711 | 0 | (long unsigned int)rdata->length)); |
712 | | |
713 | | /* |
714 | | * If this is the first reply, and the allocation hint is |
715 | | * reasonable, try and set up the reply_pdu DATA_BLOB to the |
716 | | * correct size. |
717 | | */ |
718 | |
|
719 | 0 | if ((reply_pdu->length == 0) && |
720 | 0 | r->alloc_hint && (r->alloc_hint < 15*1024*1024)) { |
721 | 0 | if (!data_blob_realloc(mem_ctx, reply_pdu, |
722 | 0 | r->alloc_hint)) { |
723 | 0 | DEBUG(0, ("reply alloc hint %d too " |
724 | 0 | "large to allocate\n", |
725 | 0 | (int)r->alloc_hint)); |
726 | 0 | return NT_STATUS_NO_MEMORY; |
727 | 0 | } |
728 | 0 | } |
729 | | |
730 | 0 | break; |
731 | | |
732 | 0 | case DCERPC_PKT_FAULT: |
733 | |
|
734 | 0 | ret = dcerpc_verify_ncacn_packet_header(pkt, |
735 | 0 | DCERPC_PKT_FAULT, |
736 | 0 | 0, /* max_auth_info */ |
737 | 0 | DCERPC_PFC_FLAG_FIRST | |
738 | 0 | DCERPC_PFC_FLAG_LAST, |
739 | 0 | DCERPC_PFC_FLAG_DID_NOT_EXECUTE); |
740 | 0 | if (!NT_STATUS_IS_OK(ret)) { |
741 | 0 | DEBUG(1, (__location__ ": Connection to %s got an unexpected " |
742 | 0 | "RPC packet type - %u, expected %u: %s\n", |
743 | 0 | rpccli_pipe_txt(talloc_tos(), cli), |
744 | 0 | pkt->ptype, expected_pkt_type, |
745 | 0 | nt_errstr(ret))); |
746 | 0 | NDR_PRINT_DEBUG(ncacn_packet, pkt); |
747 | 0 | return ret; |
748 | 0 | } |
749 | | |
750 | 0 | DEBUG(1, (__location__ ": RPC fault code %s received " |
751 | 0 | "from %s!\n", |
752 | 0 | dcerpc_errstr(talloc_tos(), |
753 | 0 | pkt->u.fault.status), |
754 | 0 | rpccli_pipe_txt(talloc_tos(), cli))); |
755 | |
|
756 | 0 | return dcerpc_fault_to_nt_status(pkt->u.fault.status); |
757 | | |
758 | 0 | default: |
759 | 0 | DEBUG(0, (__location__ "Unknown packet type %u received " |
760 | 0 | "from %s!\n", |
761 | 0 | (unsigned int)pkt->ptype, |
762 | 0 | rpccli_pipe_txt(talloc_tos(), cli))); |
763 | 0 | return NT_STATUS_RPC_PROTOCOL_ERROR; |
764 | 0 | } |
765 | | |
766 | | |
767 | 0 | if (pkt->call_id != call_id) { |
768 | 0 | DEBUG(3, (__location__ ": Connection to %s got an unexpected " |
769 | 0 | "RPC call_id - %u, not %u\n", |
770 | 0 | rpccli_pipe_txt(talloc_tos(), cli), |
771 | 0 | pkt->call_id, call_id)); |
772 | 0 | return NT_STATUS_RPC_PROTOCOL_ERROR; |
773 | 0 | } |
774 | | |
775 | 0 | return NT_STATUS_OK; |
776 | 0 | } |
777 | | |
778 | | /**************************************************************************** |
779 | | Call a remote api on an arbitrary pipe. takes param, data and setup buffers. |
780 | | ****************************************************************************/ |
781 | | |
782 | | struct cli_api_pipe_state { |
783 | | struct tevent_context *ev; |
784 | | struct rpc_cli_transport *transport; |
785 | | uint8_t *rdata; |
786 | | uint32_t rdata_len; |
787 | | }; |
788 | | |
789 | | static void cli_api_pipe_trans_done(struct tevent_req *subreq); |
790 | | static void cli_api_pipe_write_done(struct tevent_req *subreq); |
791 | | static void cli_api_pipe_read_done(struct tevent_req *subreq); |
792 | | |
793 | | static struct tevent_req *cli_api_pipe_send(TALLOC_CTX *mem_ctx, |
794 | | struct tevent_context *ev, |
795 | | struct rpc_cli_transport *transport, |
796 | | uint8_t *data, size_t data_len, |
797 | | uint32_t max_rdata_len) |
798 | 0 | { |
799 | 0 | struct tevent_req *req, *subreq; |
800 | 0 | struct cli_api_pipe_state *state; |
801 | |
|
802 | 0 | req = tevent_req_create(mem_ctx, &state, struct cli_api_pipe_state); |
803 | 0 | if (req == NULL) { |
804 | 0 | return NULL; |
805 | 0 | } |
806 | 0 | state->ev = ev; |
807 | 0 | state->transport = transport; |
808 | |
|
809 | 0 | if (max_rdata_len < RPC_HEADER_LEN) { |
810 | | /* |
811 | | * For a RPC reply we always need at least RPC_HEADER_LEN |
812 | | * bytes. We check this here because we will receive |
813 | | * RPC_HEADER_LEN bytes in cli_trans_sock_send_done. |
814 | | */ |
815 | 0 | tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER); |
816 | 0 | return tevent_req_post(req, ev); |
817 | 0 | } |
818 | | |
819 | 0 | if (transport->trans_send != NULL) { |
820 | 0 | subreq = transport->trans_send(state, ev, data, data_len, |
821 | 0 | max_rdata_len, transport->priv); |
822 | 0 | if (tevent_req_nomem(subreq, req)) { |
823 | 0 | return tevent_req_post(req, ev); |
824 | 0 | } |
825 | 0 | tevent_req_set_callback(subreq, cli_api_pipe_trans_done, req); |
826 | 0 | return req; |
827 | 0 | } |
828 | | |
829 | | /* |
830 | | * If the transport does not provide a "trans" routine, i.e. for |
831 | | * example the ncacn_ip_tcp transport, do the write/read step here. |
832 | | */ |
833 | | |
834 | 0 | subreq = rpc_write_send(state, ev, transport, data, data_len); |
835 | 0 | if (tevent_req_nomem(subreq, req)) { |
836 | 0 | return tevent_req_post(req, ev); |
837 | 0 | } |
838 | 0 | tevent_req_set_callback(subreq, cli_api_pipe_write_done, req); |
839 | 0 | return req; |
840 | 0 | } |
841 | | |
842 | | static void cli_api_pipe_trans_done(struct tevent_req *subreq) |
843 | 0 | { |
844 | 0 | struct tevent_req *req = tevent_req_callback_data( |
845 | 0 | subreq, struct tevent_req); |
846 | 0 | struct cli_api_pipe_state *state = tevent_req_data( |
847 | 0 | req, struct cli_api_pipe_state); |
848 | 0 | NTSTATUS status; |
849 | |
|
850 | 0 | status = state->transport->trans_recv(subreq, state, &state->rdata, |
851 | 0 | &state->rdata_len); |
852 | 0 | TALLOC_FREE(subreq); |
853 | 0 | if (tevent_req_nterror(req, status)) { |
854 | 0 | return; |
855 | 0 | } |
856 | 0 | tevent_req_done(req); |
857 | 0 | } |
858 | | |
859 | | static void cli_api_pipe_write_done(struct tevent_req *subreq) |
860 | 0 | { |
861 | 0 | struct tevent_req *req = tevent_req_callback_data( |
862 | 0 | subreq, struct tevent_req); |
863 | 0 | struct cli_api_pipe_state *state = tevent_req_data( |
864 | 0 | req, struct cli_api_pipe_state); |
865 | 0 | NTSTATUS status; |
866 | |
|
867 | 0 | status = rpc_write_recv(subreq); |
868 | 0 | TALLOC_FREE(subreq); |
869 | 0 | if (tevent_req_nterror(req, status)) { |
870 | 0 | return; |
871 | 0 | } |
872 | | |
873 | 0 | state->rdata = talloc_array(state, uint8_t, RPC_HEADER_LEN); |
874 | 0 | if (tevent_req_nomem(state->rdata, req)) { |
875 | 0 | return; |
876 | 0 | } |
877 | | |
878 | | /* |
879 | | * We don't need to use rpc_read_send here, the upper layer will cope |
880 | | * with a short read, transport->trans_send could also return less |
881 | | * than state->max_rdata_len. |
882 | | */ |
883 | 0 | subreq = state->transport->read_send(state, state->ev, state->rdata, |
884 | 0 | RPC_HEADER_LEN, |
885 | 0 | state->transport->priv); |
886 | 0 | if (tevent_req_nomem(subreq, req)) { |
887 | 0 | return; |
888 | 0 | } |
889 | 0 | tevent_req_set_callback(subreq, cli_api_pipe_read_done, req); |
890 | 0 | } |
891 | | |
892 | | static void cli_api_pipe_read_done(struct tevent_req *subreq) |
893 | 0 | { |
894 | 0 | struct tevent_req *req = tevent_req_callback_data( |
895 | 0 | subreq, struct tevent_req); |
896 | 0 | struct cli_api_pipe_state *state = tevent_req_data( |
897 | 0 | req, struct cli_api_pipe_state); |
898 | 0 | NTSTATUS status; |
899 | 0 | ssize_t received; |
900 | |
|
901 | 0 | status = state->transport->read_recv(subreq, &received); |
902 | 0 | TALLOC_FREE(subreq); |
903 | 0 | if (tevent_req_nterror(req, status)) { |
904 | 0 | return; |
905 | 0 | } |
906 | 0 | state->rdata_len = received; |
907 | 0 | tevent_req_done(req); |
908 | 0 | } |
909 | | |
910 | | static NTSTATUS cli_api_pipe_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx, |
911 | | uint8_t **prdata, uint32_t *prdata_len) |
912 | 0 | { |
913 | 0 | struct cli_api_pipe_state *state = tevent_req_data( |
914 | 0 | req, struct cli_api_pipe_state); |
915 | 0 | NTSTATUS status; |
916 | |
|
917 | 0 | if (tevent_req_is_nterror(req, &status)) { |
918 | 0 | return status; |
919 | 0 | } |
920 | | |
921 | 0 | *prdata = talloc_move(mem_ctx, &state->rdata); |
922 | 0 | *prdata_len = state->rdata_len; |
923 | 0 | return NT_STATUS_OK; |
924 | 0 | } |
925 | | |
926 | | /**************************************************************************** |
927 | | Send data on an rpc pipe via trans. The data must be the last |
928 | | pdu fragment of an NDR data stream. |
929 | | |
930 | | Receive response data from an rpc pipe, which may be large... |
931 | | |
932 | | Read the first fragment: unfortunately have to use SMBtrans for the first |
933 | | bit, then SMBreadX for subsequent bits. |
934 | | |
935 | | If first fragment received also wasn't the last fragment, continue |
936 | | getting fragments until we _do_ receive the last fragment. |
937 | | |
938 | | Request/Response PDU's look like the following... |
939 | | |
940 | | |<------------------PDU len----------------------------------------------->| |
941 | | |<-HDR_LEN-->|<--REQ LEN------>|.............|<-AUTH_HDRLEN->|<-AUTH_LEN-->| |
942 | | |
943 | | +------------+-----------------+-------------+---------------+-------------+ |
944 | | | RPC HEADER | REQ/RESP HEADER | DATA ...... | AUTH_HDR | AUTH DATA | |
945 | | +------------+-----------------+-------------+---------------+-------------+ |
946 | | |
947 | | Where the presence of the AUTH_HDR and AUTH DATA are dependent on the |
948 | | signing & sealing being negotiated. |
949 | | |
950 | | ****************************************************************************/ |
951 | | |
952 | | struct rpc_api_pipe_state { |
953 | | struct tevent_context *ev; |
954 | | struct rpc_pipe_client *cli; |
955 | | uint8_t expected_pkt_type; |
956 | | uint32_t call_id; |
957 | | |
958 | | DATA_BLOB incoming_frag; |
959 | | struct ncacn_packet *pkt; |
960 | | |
961 | | /* Incoming reply */ |
962 | | DATA_BLOB reply_pdu; |
963 | | size_t reply_pdu_offset; |
964 | | uint8_t endianness; |
965 | | }; |
966 | | |
967 | | static void rpc_api_pipe_trans_done(struct tevent_req *subreq); |
968 | | static void rpc_api_pipe_got_pdu(struct tevent_req *subreq); |
969 | | static void rpc_api_pipe_auth3_done(struct tevent_req *subreq); |
970 | | |
971 | | static struct tevent_req *rpc_api_pipe_send(TALLOC_CTX *mem_ctx, |
972 | | struct tevent_context *ev, |
973 | | struct rpc_pipe_client *cli, |
974 | | DATA_BLOB *data, /* Outgoing PDU */ |
975 | | uint8_t expected_pkt_type, |
976 | | uint32_t call_id) |
977 | 0 | { |
978 | 0 | struct tevent_req *req, *subreq; |
979 | 0 | struct rpc_api_pipe_state *state; |
980 | 0 | uint16_t max_recv_frag; |
981 | |
|
982 | 0 | req = tevent_req_create(mem_ctx, &state, struct rpc_api_pipe_state); |
983 | 0 | if (req == NULL) { |
984 | 0 | return NULL; |
985 | 0 | } |
986 | 0 | state->ev = ev; |
987 | 0 | state->cli = cli; |
988 | 0 | state->expected_pkt_type = expected_pkt_type; |
989 | 0 | state->call_id = call_id; |
990 | 0 | state->endianness = DCERPC_DREP_LE; |
991 | | |
992 | | /* |
993 | | * Ensure we're not sending too much. |
994 | | */ |
995 | 0 | if (data->length > cli->conn->features.max_xmit_frag) { |
996 | 0 | tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER); |
997 | 0 | return tevent_req_post(req, ev); |
998 | 0 | } |
999 | | |
1000 | 0 | DEBUG(5,("rpc_api_pipe: %s\n", rpccli_pipe_txt(talloc_tos(), cli))); |
1001 | |
|
1002 | 0 | if (state->expected_pkt_type == DCERPC_PKT_AUTH3) { |
1003 | 0 | subreq = rpc_write_send(state, ev, cli->conn->transport, |
1004 | 0 | data->data, data->length); |
1005 | 0 | if (tevent_req_nomem(subreq, req)) { |
1006 | 0 | return tevent_req_post(req, ev); |
1007 | 0 | } |
1008 | 0 | tevent_req_set_callback(subreq, rpc_api_pipe_auth3_done, req); |
1009 | 0 | return req; |
1010 | 0 | } |
1011 | | |
1012 | | /* get the header first, then fetch the rest once we have |
1013 | | * the frag_length available */ |
1014 | 0 | max_recv_frag = RPC_HEADER_LEN; |
1015 | |
|
1016 | 0 | subreq = cli_api_pipe_send(state, ev, cli->conn->transport, |
1017 | 0 | data->data, data->length, max_recv_frag); |
1018 | 0 | if (tevent_req_nomem(subreq, req)) { |
1019 | 0 | return tevent_req_post(req, ev); |
1020 | 0 | } |
1021 | 0 | tevent_req_set_callback(subreq, rpc_api_pipe_trans_done, req); |
1022 | 0 | return req; |
1023 | 0 | } |
1024 | | |
1025 | | static void rpc_api_pipe_auth3_done(struct tevent_req *subreq) |
1026 | 0 | { |
1027 | 0 | NTSTATUS status = rpc_write_recv(subreq); |
1028 | 0 | return tevent_req_simple_finish_ntstatus(subreq, status); |
1029 | 0 | } |
1030 | | |
1031 | | static void rpc_api_pipe_trans_done(struct tevent_req *subreq) |
1032 | 0 | { |
1033 | 0 | struct tevent_req *req = tevent_req_callback_data( |
1034 | 0 | subreq, struct tevent_req); |
1035 | 0 | struct rpc_api_pipe_state *state = tevent_req_data( |
1036 | 0 | req, struct rpc_api_pipe_state); |
1037 | 0 | NTSTATUS status; |
1038 | 0 | uint8_t *rdata = NULL; |
1039 | 0 | uint32_t rdata_len = 0; |
1040 | |
|
1041 | 0 | status = cli_api_pipe_recv(subreq, state, &rdata, &rdata_len); |
1042 | 0 | TALLOC_FREE(subreq); |
1043 | 0 | if (tevent_req_nterror(req, status)) {; |
1044 | 0 | DEBUG(5, ("cli_api_pipe failed: %s\n", nt_errstr(status))); |
1045 | 0 | return; |
1046 | 0 | } |
1047 | | |
1048 | 0 | if (rdata == NULL) { |
1049 | 0 | DEBUG(3,("rpc_api_pipe: %s failed to return data.\n", |
1050 | 0 | rpccli_pipe_txt(talloc_tos(), state->cli))); |
1051 | 0 | tevent_req_done(req); |
1052 | 0 | return; |
1053 | 0 | } |
1054 | | |
1055 | | /* |
1056 | | * Move data on state->incoming_frag. |
1057 | | */ |
1058 | 0 | state->incoming_frag.data = talloc_move(state, &rdata); |
1059 | 0 | state->incoming_frag.length = rdata_len; |
1060 | 0 | if (!state->incoming_frag.data) { |
1061 | 0 | tevent_req_nterror(req, NT_STATUS_NO_MEMORY); |
1062 | 0 | return; |
1063 | 0 | } |
1064 | | |
1065 | | /* Ensure we have enough data for a pdu. */ |
1066 | 0 | subreq = get_complete_frag_send(state, state->ev, state->cli, |
1067 | 0 | &state->incoming_frag); |
1068 | 0 | if (tevent_req_nomem(subreq, req)) { |
1069 | 0 | return; |
1070 | 0 | } |
1071 | 0 | tevent_req_set_callback(subreq, rpc_api_pipe_got_pdu, req); |
1072 | 0 | } |
1073 | | |
1074 | | static void rpc_api_pipe_got_pdu(struct tevent_req *subreq) |
1075 | 0 | { |
1076 | 0 | struct tevent_req *req = tevent_req_callback_data( |
1077 | 0 | subreq, struct tevent_req); |
1078 | 0 | struct rpc_api_pipe_state *state = tevent_req_data( |
1079 | 0 | req, struct rpc_api_pipe_state); |
1080 | 0 | NTSTATUS status; |
1081 | 0 | DATA_BLOB rdata = { .data = NULL }; |
1082 | |
|
1083 | 0 | status = get_complete_frag_recv(subreq); |
1084 | 0 | TALLOC_FREE(subreq); |
1085 | 0 | if (tevent_req_nterror(req, status)) { |
1086 | 0 | DEBUG(5, ("get_complete_frag failed: %s\n", |
1087 | 0 | nt_errstr(status))); |
1088 | 0 | return; |
1089 | 0 | } |
1090 | | |
1091 | 0 | state->pkt = talloc(state, struct ncacn_packet); |
1092 | 0 | if (!state->pkt) { |
1093 | | /* |
1094 | | * TODO: do a real async disconnect ... |
1095 | | * |
1096 | | * For now do it sync... |
1097 | | */ |
1098 | 0 | TALLOC_FREE(state->cli->conn); |
1099 | 0 | tevent_req_oom(req); |
1100 | 0 | return; |
1101 | 0 | } |
1102 | | |
1103 | 0 | status = dcerpc_pull_ncacn_packet(state->pkt, |
1104 | 0 | &state->incoming_frag, |
1105 | 0 | state->pkt); |
1106 | 0 | if (tevent_req_nterror(req, status)) { |
1107 | | /* |
1108 | | * TODO: do a real async disconnect ... |
1109 | | * |
1110 | | * For now do it sync... |
1111 | | */ |
1112 | 0 | TALLOC_FREE(state->cli->conn); |
1113 | 0 | return; |
1114 | 0 | } |
1115 | | |
1116 | 0 | if (DEBUGLEVEL >= 10) { |
1117 | 0 | NDR_PRINT_DEBUG(ncacn_packet, state->pkt); |
1118 | 0 | } |
1119 | |
|
1120 | 0 | status = cli_pipe_validate_current_pdu(state, |
1121 | 0 | state->cli, state->pkt, |
1122 | 0 | &state->incoming_frag, |
1123 | 0 | state->expected_pkt_type, |
1124 | 0 | state->call_id, |
1125 | 0 | &rdata, |
1126 | 0 | &state->reply_pdu); |
1127 | |
|
1128 | 0 | DBG_DEBUG("got frag len of %zu at offset %zu: %s\n", |
1129 | 0 | state->incoming_frag.length, |
1130 | 0 | state->reply_pdu_offset, |
1131 | 0 | nt_errstr(status)); |
1132 | |
|
1133 | 0 | if (state->pkt->ptype != DCERPC_PKT_FAULT && !NT_STATUS_IS_OK(status)) { |
1134 | | /* |
1135 | | * TODO: do a real async disconnect ... |
1136 | | * |
1137 | | * For now do it sync... |
1138 | | */ |
1139 | 0 | TALLOC_FREE(state->cli->conn); |
1140 | 0 | } else if (NT_STATUS_EQUAL(status, NT_STATUS_RPC_PROTOCOL_ERROR)) { |
1141 | | /* |
1142 | | * TODO: do a real async disconnect ... |
1143 | | * |
1144 | | * For now do it sync... |
1145 | | */ |
1146 | 0 | TALLOC_FREE(state->cli->conn); |
1147 | 0 | } else if (NT_STATUS_EQUAL(status, NT_STATUS_RPC_SEC_PKG_ERROR)) { |
1148 | | /* |
1149 | | * TODO: do a real async disconnect ... |
1150 | | * |
1151 | | * For now do it sync... |
1152 | | */ |
1153 | 0 | TALLOC_FREE(state->cli->conn); |
1154 | 0 | } |
1155 | 0 | if (tevent_req_nterror(req, status)) { |
1156 | 0 | return; |
1157 | 0 | } |
1158 | | |
1159 | 0 | if ((state->pkt->pfc_flags & DCERPC_PFC_FLAG_FIRST) |
1160 | 0 | && (state->pkt->drep[0] != DCERPC_DREP_LE)) { |
1161 | | /* |
1162 | | * Set the data type correctly for big-endian data on the |
1163 | | * first packet. |
1164 | | */ |
1165 | 0 | DEBUG(10,("rpc_api_pipe: On %s PDU data format is " |
1166 | 0 | "big-endian.\n", |
1167 | 0 | rpccli_pipe_txt(talloc_tos(), state->cli))); |
1168 | 0 | state->endianness = 0x00; /* BIG ENDIAN */ |
1169 | 0 | } |
1170 | | /* |
1171 | | * Check endianness on subsequent packets. |
1172 | | */ |
1173 | 0 | if (state->endianness != state->pkt->drep[0]) { |
1174 | 0 | DEBUG(0,("rpc_api_pipe: Error : Endianness changed from %s to " |
1175 | 0 | "%s\n", |
1176 | 0 | state->endianness?"little":"big", |
1177 | 0 | state->pkt->drep[0]?"little":"big")); |
1178 | | /* |
1179 | | * TODO: do a real async disconnect ... |
1180 | | * |
1181 | | * For now do it sync... |
1182 | | */ |
1183 | 0 | TALLOC_FREE(state->cli->conn); |
1184 | 0 | tevent_req_nterror(req, NT_STATUS_RPC_PROTOCOL_ERROR); |
1185 | 0 | return; |
1186 | 0 | } |
1187 | | |
1188 | 0 | if (state->reply_pdu_offset + rdata.length > MAX_RPC_DATA_SIZE) { |
1189 | | /* |
1190 | | * TODO: do a real async disconnect ... |
1191 | | * |
1192 | | * For now do it sync... |
1193 | | */ |
1194 | 0 | TALLOC_FREE(state->cli->conn); |
1195 | 0 | tevent_req_nterror(req, NT_STATUS_RPC_PROTOCOL_ERROR); |
1196 | 0 | return; |
1197 | 0 | } |
1198 | | |
1199 | | /* Now copy the data portion out of the pdu into rbuf. */ |
1200 | 0 | if (state->reply_pdu.length < state->reply_pdu_offset + rdata.length) { |
1201 | 0 | if (!data_blob_realloc(NULL, &state->reply_pdu, |
1202 | 0 | state->reply_pdu_offset + rdata.length)) { |
1203 | | /* |
1204 | | * TODO: do a real async disconnect ... |
1205 | | * |
1206 | | * For now do it sync... |
1207 | | */ |
1208 | 0 | TALLOC_FREE(state->cli->conn); |
1209 | 0 | tevent_req_oom(req); |
1210 | 0 | return; |
1211 | 0 | } |
1212 | 0 | } |
1213 | | |
1214 | 0 | memcpy(state->reply_pdu.data + state->reply_pdu_offset, |
1215 | 0 | rdata.data, rdata.length); |
1216 | 0 | state->reply_pdu_offset += rdata.length; |
1217 | | |
1218 | | /* reset state->incoming_frag, there is no need to free it, |
1219 | | * it will be reallocated to the right size the next time |
1220 | | * it is used */ |
1221 | 0 | state->incoming_frag.length = 0; |
1222 | |
|
1223 | 0 | if (state->pkt->pfc_flags & DCERPC_PFC_FLAG_LAST) { |
1224 | | /* make sure the pdu length is right now that we |
1225 | | * have all the data available (alloc hint may |
1226 | | * have allocated more than was actually used) */ |
1227 | 0 | state->reply_pdu.length = state->reply_pdu_offset; |
1228 | 0 | DEBUG(10,("rpc_api_pipe: %s returned %u bytes.\n", |
1229 | 0 | rpccli_pipe_txt(talloc_tos(), state->cli), |
1230 | 0 | (unsigned)state->reply_pdu.length)); |
1231 | 0 | tevent_req_done(req); |
1232 | 0 | return; |
1233 | 0 | } |
1234 | | |
1235 | 0 | subreq = get_complete_frag_send(state, state->ev, state->cli, |
1236 | 0 | &state->incoming_frag); |
1237 | 0 | if (subreq == NULL) { |
1238 | | /* |
1239 | | * TODO: do a real async disconnect ... |
1240 | | * |
1241 | | * For now do it sync... |
1242 | | */ |
1243 | 0 | TALLOC_FREE(state->cli->conn); |
1244 | 0 | } |
1245 | 0 | if (tevent_req_nomem(subreq, req)) { |
1246 | 0 | return; |
1247 | 0 | } |
1248 | 0 | tevent_req_set_callback(subreq, rpc_api_pipe_got_pdu, req); |
1249 | 0 | } |
1250 | | |
1251 | | static NTSTATUS rpc_api_pipe_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx, |
1252 | | struct ncacn_packet **pkt, |
1253 | | DATA_BLOB *reply_pdu) |
1254 | 0 | { |
1255 | 0 | struct rpc_api_pipe_state *state = tevent_req_data( |
1256 | 0 | req, struct rpc_api_pipe_state); |
1257 | 0 | NTSTATUS status; |
1258 | |
|
1259 | 0 | if (tevent_req_is_nterror(req, &status)) { |
1260 | 0 | return status; |
1261 | 0 | } |
1262 | | |
1263 | | /* return data to caller and assign it ownership of memory */ |
1264 | 0 | if (reply_pdu) { |
1265 | 0 | reply_pdu->data = talloc_move(mem_ctx, &state->reply_pdu.data); |
1266 | 0 | reply_pdu->length = state->reply_pdu.length; |
1267 | 0 | state->reply_pdu.length = 0; |
1268 | 0 | } else { |
1269 | 0 | data_blob_free(&state->reply_pdu); |
1270 | 0 | } |
1271 | |
|
1272 | 0 | if (pkt) { |
1273 | 0 | *pkt = talloc_steal(mem_ctx, state->pkt); |
1274 | 0 | } |
1275 | |
|
1276 | 0 | return NT_STATUS_OK; |
1277 | 0 | } |
1278 | | |
1279 | | /******************************************************************* |
1280 | | Creates NTLMSSP auth bind. |
1281 | | ********************************************************************/ |
1282 | | |
1283 | | static NTSTATUS create_generic_auth_rpc_bind_req(struct rpc_pipe_client *cli, |
1284 | | TALLOC_CTX *mem_ctx, |
1285 | | DATA_BLOB *auth_token) |
1286 | 0 | { |
1287 | 0 | struct gensec_security *gensec_security; |
1288 | 0 | DATA_BLOB null_blob = { .data = NULL }; |
1289 | 0 | NTSTATUS status; |
1290 | |
|
1291 | 0 | gensec_security = cli->auth->auth_ctx; |
1292 | |
|
1293 | 0 | DEBUG(5, ("create_generic_auth_rpc_bind_req: generate first token\n")); |
1294 | 0 | status = gensec_update(gensec_security, mem_ctx, null_blob, auth_token); |
1295 | |
|
1296 | 0 | if (!NT_STATUS_IS_OK(status) && |
1297 | 0 | !NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) |
1298 | 0 | { |
1299 | 0 | return status; |
1300 | 0 | } |
1301 | | |
1302 | 0 | return status; |
1303 | 0 | } |
1304 | | |
1305 | | /******************************************************************* |
1306 | | Creates the internals of a DCE/RPC bind request or alter context PDU. |
1307 | | ********************************************************************/ |
1308 | | |
1309 | | static NTSTATUS create_bind_or_alt_ctx_internal(TALLOC_CTX *mem_ctx, |
1310 | | enum dcerpc_pkt_type ptype, |
1311 | | uint32_t rpc_call_id, |
1312 | | struct rpc_client_association *assoc, |
1313 | | struct rpc_client_connection *conn, |
1314 | | uint16_t pres_context_id, |
1315 | | const struct ndr_syntax_id *abstract, |
1316 | | const struct ndr_syntax_id *transfer, |
1317 | | const DATA_BLOB *auth_info, |
1318 | | DATA_BLOB *blob) |
1319 | 0 | { |
1320 | 0 | uint16_t auth_len = auth_info->length; |
1321 | 0 | NTSTATUS status; |
1322 | 0 | struct ndr_syntax_id bind_time_features = dcerpc_construct_bind_time_features( |
1323 | 0 | assoc->features.client); |
1324 | 0 | struct dcerpc_ctx_list ctx_list[2] = { |
1325 | 0 | [0] = { |
1326 | 0 | .context_id = pres_context_id, |
1327 | 0 | .num_transfer_syntaxes = 1, |
1328 | 0 | .abstract_syntax = *abstract, |
1329 | 0 | .transfer_syntaxes = (struct ndr_syntax_id *)discard_const(transfer), |
1330 | 0 | }, |
1331 | 0 | [1] = { |
1332 | | /* |
1333 | | * For now we assume pres_context_id is 0 |
1334 | | * because bind time feature negotiation |
1335 | | * only happens once per association, |
1336 | | * with the first DCERPC Bind. |
1337 | | * |
1338 | | * With that we use pres_context_id + 1, |
1339 | | * but only consume it from conn->next_pres_context_id |
1340 | | * in check_bind_response(). |
1341 | | */ |
1342 | 0 | .context_id = pres_context_id + 1, |
1343 | 0 | .num_transfer_syntaxes = 1, |
1344 | 0 | .abstract_syntax = *abstract, |
1345 | 0 | .transfer_syntaxes = &bind_time_features, |
1346 | 0 | }, |
1347 | 0 | }; |
1348 | 0 | uint32_t assoc_group_id = |
1349 | 0 | dcerpc_binding_get_assoc_group_id(assoc->binding); |
1350 | 0 | union dcerpc_payload u = { |
1351 | 0 | .bind.max_xmit_frag = conn->features.max_xmit_frag, |
1352 | 0 | .bind.max_recv_frag = conn->features.max_recv_frag, |
1353 | 0 | .bind.assoc_group_id = assoc_group_id, |
1354 | 0 | .bind.num_contexts = assoc->features.negotiation_done ? 1 : 2, |
1355 | 0 | .bind.ctx_list = ctx_list, |
1356 | 0 | .bind.auth_info = *auth_info, |
1357 | 0 | }; |
1358 | 0 | uint8_t pfc_flags = DCERPC_PFC_FLAG_FIRST | DCERPC_PFC_FLAG_LAST; |
1359 | |
|
1360 | 0 | if (conn->features.client_hdr_signing && |
1361 | 0 | auth_len != 0 && |
1362 | 0 | !conn->features.hdr_signing) |
1363 | 0 | { |
1364 | | /* |
1365 | | * The first authenticated bind or alter_context |
1366 | | * negotiates header signing |
1367 | | */ |
1368 | 0 | pfc_flags |= DCERPC_PFC_FLAG_SUPPORT_HEADER_SIGN; |
1369 | 0 | } |
1370 | |
|
1371 | 0 | if (auth_len) { |
1372 | 0 | auth_len -= DCERPC_AUTH_TRAILER_LENGTH; |
1373 | 0 | } |
1374 | |
|
1375 | 0 | status = dcerpc_push_ncacn_packet(mem_ctx, |
1376 | 0 | ptype, pfc_flags, |
1377 | 0 | auth_len, |
1378 | 0 | rpc_call_id, |
1379 | 0 | &u, |
1380 | 0 | blob); |
1381 | 0 | if (!NT_STATUS_IS_OK(status)) { |
1382 | 0 | DEBUG(0, ("Failed to marshall bind/alter ncacn_packet.\n")); |
1383 | 0 | return status; |
1384 | 0 | } |
1385 | | |
1386 | 0 | return NT_STATUS_OK; |
1387 | 0 | } |
1388 | | |
1389 | | /******************************************************************* |
1390 | | Creates a DCE/RPC bind request. |
1391 | | ********************************************************************/ |
1392 | | |
1393 | | static NTSTATUS create_rpc_bind_req(TALLOC_CTX *mem_ctx, |
1394 | | struct rpc_pipe_client *cli, |
1395 | | struct pipe_auth_data *auth, |
1396 | | uint32_t rpc_call_id, |
1397 | | const struct ndr_syntax_id *abstract, |
1398 | | const struct ndr_syntax_id *transfer, |
1399 | | DATA_BLOB *rpc_out) |
1400 | 0 | { |
1401 | 0 | enum dcerpc_pkt_type ptype = DCERPC_PKT_BIND; |
1402 | 0 | DATA_BLOB auth_token = { .data = NULL }; |
1403 | 0 | DATA_BLOB auth_info = { .data = NULL }; |
1404 | 0 | NTSTATUS ret; |
1405 | |
|
1406 | 0 | if (cli->conn->features.bind_done) { |
1407 | 0 | ptype = DCERPC_PKT_ALTER; |
1408 | 0 | } |
1409 | |
|
1410 | 0 | if (auth->auth_type != DCERPC_AUTH_TYPE_NONE) { |
1411 | 0 | ret = create_generic_auth_rpc_bind_req( |
1412 | 0 | cli, mem_ctx, &auth_token); |
1413 | |
|
1414 | 0 | if (!NT_STATUS_IS_OK(ret) && |
1415 | 0 | !NT_STATUS_EQUAL(ret, NT_STATUS_MORE_PROCESSING_REQUIRED)) { |
1416 | 0 | return ret; |
1417 | 0 | } |
1418 | 0 | } |
1419 | | |
1420 | 0 | if (auth_token.length != 0) { |
1421 | 0 | ret = dcerpc_push_dcerpc_auth(cli, |
1422 | 0 | auth->auth_type, |
1423 | 0 | auth->auth_level, |
1424 | 0 | 0, /* auth_pad_length */ |
1425 | 0 | auth->auth_context_id, |
1426 | 0 | &auth_token, |
1427 | 0 | &auth_info); |
1428 | 0 | if (!NT_STATUS_IS_OK(ret)) { |
1429 | 0 | return ret; |
1430 | 0 | } |
1431 | 0 | data_blob_free(&auth_token); |
1432 | 0 | } |
1433 | | |
1434 | 0 | ret = create_bind_or_alt_ctx_internal(mem_ctx, |
1435 | 0 | ptype, |
1436 | 0 | rpc_call_id, |
1437 | 0 | cli->assoc, |
1438 | 0 | cli->conn, |
1439 | 0 | cli->pres_context_id, |
1440 | 0 | abstract, |
1441 | 0 | transfer, |
1442 | 0 | &auth_info, |
1443 | 0 | rpc_out); |
1444 | 0 | data_blob_free(&auth_info); |
1445 | |
|
1446 | 0 | return ret; |
1447 | 0 | } |
1448 | | |
1449 | | /******************************************************************* |
1450 | | External interface. |
1451 | | Does an rpc request on a pipe. Incoming data is NDR encoded in in_data. |
1452 | | Reply is NDR encoded in out_data. Splits the data stream into RPC PDU's |
1453 | | and deals with signing/sealing details. |
1454 | | ********************************************************************/ |
1455 | | |
1456 | | struct rpc_api_pipe_req_state { |
1457 | | struct tevent_context *ev; |
1458 | | struct rpc_pipe_client *cli; |
1459 | | uint8_t op_num; |
1460 | | uint32_t call_id; |
1461 | | const DATA_BLOB *req_data; |
1462 | | const struct GUID *object_uuid; |
1463 | | uint32_t req_data_sent; |
1464 | | DATA_BLOB req_trailer; |
1465 | | uint32_t req_trailer_sent; |
1466 | | bool verify_bitmask1; |
1467 | | bool verify_pcontext; |
1468 | | DATA_BLOB rpc_out; |
1469 | | DATA_BLOB reply_pdu; |
1470 | | }; |
1471 | | |
1472 | | static void rpc_api_pipe_req_write_done(struct tevent_req *subreq); |
1473 | | static void rpc_api_pipe_req_done(struct tevent_req *subreq); |
1474 | | static NTSTATUS prepare_verification_trailer(struct rpc_api_pipe_req_state *state); |
1475 | | static NTSTATUS prepare_next_frag(struct rpc_api_pipe_req_state *state, |
1476 | | bool *is_last_frag); |
1477 | | |
1478 | | static struct tevent_req *rpc_api_pipe_req_send(TALLOC_CTX *mem_ctx, |
1479 | | struct tevent_context *ev, |
1480 | | struct rpc_pipe_client *cli, |
1481 | | uint8_t op_num, |
1482 | | const struct GUID *object_uuid, |
1483 | | const DATA_BLOB *req_data) |
1484 | 0 | { |
1485 | 0 | struct tevent_req *req, *subreq; |
1486 | 0 | struct rpc_api_pipe_req_state *state; |
1487 | 0 | NTSTATUS status; |
1488 | 0 | bool is_last_frag; |
1489 | |
|
1490 | 0 | req = tevent_req_create(mem_ctx, &state, |
1491 | 0 | struct rpc_api_pipe_req_state); |
1492 | 0 | if (req == NULL) { |
1493 | 0 | return NULL; |
1494 | 0 | } |
1495 | 0 | state->ev = ev; |
1496 | 0 | state->cli = cli; |
1497 | 0 | state->op_num = op_num; |
1498 | 0 | state->object_uuid = object_uuid; |
1499 | 0 | state->req_data = req_data; |
1500 | 0 | state->call_id = ++cli->assoc->next_call_id; |
1501 | |
|
1502 | 0 | if (cli->conn->features.max_xmit_frag < DCERPC_REQUEST_LENGTH |
1503 | 0 | + RPC_MAX_SIGN_SIZE) { |
1504 | | /* Server is screwed up ! */ |
1505 | 0 | tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER); |
1506 | 0 | return tevent_req_post(req, ev); |
1507 | 0 | } |
1508 | | |
1509 | 0 | status = prepare_verification_trailer(state); |
1510 | 0 | if (tevent_req_nterror(req, status)) { |
1511 | 0 | return tevent_req_post(req, ev); |
1512 | 0 | } |
1513 | | |
1514 | 0 | status = prepare_next_frag(state, &is_last_frag); |
1515 | 0 | if (tevent_req_nterror(req, status)) { |
1516 | 0 | return tevent_req_post(req, ev); |
1517 | 0 | } |
1518 | | |
1519 | 0 | if (is_last_frag) { |
1520 | 0 | subreq = rpc_api_pipe_send(state, ev, state->cli, |
1521 | 0 | &state->rpc_out, |
1522 | 0 | DCERPC_PKT_RESPONSE, |
1523 | 0 | state->call_id); |
1524 | 0 | if (tevent_req_nomem(subreq, req)) { |
1525 | 0 | return tevent_req_post(req, ev); |
1526 | 0 | } |
1527 | 0 | tevent_req_set_callback(subreq, rpc_api_pipe_req_done, req); |
1528 | 0 | } else { |
1529 | 0 | subreq = rpc_write_send(state, ev, cli->conn->transport, |
1530 | 0 | state->rpc_out.data, |
1531 | 0 | state->rpc_out.length); |
1532 | 0 | if (tevent_req_nomem(subreq, req)) { |
1533 | 0 | return tevent_req_post(req, ev); |
1534 | 0 | } |
1535 | 0 | tevent_req_set_callback(subreq, rpc_api_pipe_req_write_done, |
1536 | 0 | req); |
1537 | 0 | } |
1538 | 0 | return req; |
1539 | 0 | } |
1540 | | |
1541 | | static NTSTATUS prepare_verification_trailer(struct rpc_api_pipe_req_state *state) |
1542 | 0 | { |
1543 | 0 | struct pipe_auth_data *a = state->cli->auth; |
1544 | 0 | struct dcerpc_sec_verification_trailer *t; |
1545 | 0 | struct ndr_push *ndr = NULL; |
1546 | 0 | enum ndr_err_code ndr_err; |
1547 | 0 | size_t align = 0; |
1548 | 0 | size_t pad = 0; |
1549 | |
|
1550 | 0 | if (a == NULL) { |
1551 | 0 | return NT_STATUS_OK; |
1552 | 0 | } |
1553 | | |
1554 | 0 | if (a->auth_level < DCERPC_AUTH_LEVEL_PACKET) { |
1555 | 0 | return NT_STATUS_OK; |
1556 | 0 | } |
1557 | | |
1558 | 0 | t = talloc_zero(state, struct dcerpc_sec_verification_trailer); |
1559 | 0 | if (t == NULL) { |
1560 | 0 | return NT_STATUS_NO_MEMORY; |
1561 | 0 | } |
1562 | | |
1563 | 0 | if (!a->verified_bitmask1) { |
1564 | 0 | t->commands = talloc_realloc(t, t->commands, |
1565 | 0 | struct dcerpc_sec_vt, |
1566 | 0 | t->count.count + 1); |
1567 | 0 | if (t->commands == NULL) { |
1568 | 0 | return NT_STATUS_NO_MEMORY; |
1569 | 0 | } |
1570 | 0 | t->commands[t->count.count++] = (struct dcerpc_sec_vt) { |
1571 | 0 | .command = DCERPC_SEC_VT_COMMAND_BITMASK1, |
1572 | 0 | .u.bitmask1 = (state->cli->conn->features.client_hdr_signing) ? |
1573 | 0 | DCERPC_SEC_VT_CLIENT_SUPPORTS_HEADER_SIGNING : |
1574 | 0 | 0, |
1575 | 0 | }; |
1576 | 0 | state->verify_bitmask1 = true; |
1577 | 0 | } |
1578 | | |
1579 | 0 | if (!state->cli->verified_pcontext) { |
1580 | 0 | t->commands = talloc_realloc(t, t->commands, |
1581 | 0 | struct dcerpc_sec_vt, |
1582 | 0 | t->count.count + 1); |
1583 | 0 | if (t->commands == NULL) { |
1584 | 0 | return NT_STATUS_NO_MEMORY; |
1585 | 0 | } |
1586 | 0 | t->commands[t->count.count++] = (struct dcerpc_sec_vt) { |
1587 | 0 | .command = DCERPC_SEC_VT_COMMAND_PCONTEXT, |
1588 | 0 | .u.pcontext.abstract_syntax = |
1589 | 0 | state->cli->table->syntax_id, |
1590 | 0 | .u.pcontext.transfer_syntax = |
1591 | 0 | state->cli->transfer_syntax, |
1592 | 0 | }; |
1593 | 0 | state->verify_pcontext = true; |
1594 | 0 | } |
1595 | | |
1596 | 0 | if (!a->hdr_signing) { |
1597 | 0 | t->commands = talloc_realloc(t, t->commands, |
1598 | 0 | struct dcerpc_sec_vt, |
1599 | 0 | t->count.count + 1); |
1600 | 0 | if (t->commands == NULL) { |
1601 | 0 | return NT_STATUS_NO_MEMORY; |
1602 | 0 | } |
1603 | 0 | t->commands[t->count.count++] = (struct dcerpc_sec_vt) { |
1604 | 0 | .command = DCERPC_SEC_VT_COMMAND_HEADER2, |
1605 | 0 | .u.header2.ptype = DCERPC_PKT_REQUEST, |
1606 | 0 | .u.header2.drep[0] = DCERPC_DREP_LE, |
1607 | 0 | .u.header2.call_id = state->call_id, |
1608 | 0 | .u.header2.context_id = state->cli->pres_context_id, |
1609 | 0 | .u.header2.opnum = state->op_num, |
1610 | 0 | }; |
1611 | 0 | } |
1612 | | |
1613 | 0 | if (t->count.count == 0) { |
1614 | 0 | TALLOC_FREE(t); |
1615 | 0 | return NT_STATUS_OK; |
1616 | 0 | } |
1617 | | |
1618 | 0 | t->commands[t->count.count - 1].command |= DCERPC_SEC_VT_COMMAND_END; |
1619 | |
|
1620 | 0 | if (DEBUGLEVEL >= 10) { |
1621 | 0 | NDR_PRINT_DEBUG(dcerpc_sec_verification_trailer, t); |
1622 | 0 | } |
1623 | |
|
1624 | 0 | ndr = ndr_push_init_ctx(state); |
1625 | 0 | if (ndr == NULL) { |
1626 | 0 | return NT_STATUS_NO_MEMORY; |
1627 | 0 | } |
1628 | | |
1629 | 0 | ndr_err = ndr_push_dcerpc_sec_verification_trailer(ndr, |
1630 | 0 | NDR_SCALARS | NDR_BUFFERS, |
1631 | 0 | t); |
1632 | 0 | TALLOC_FREE(t); |
1633 | 0 | if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { |
1634 | 0 | return ndr_map_error2ntstatus(ndr_err); |
1635 | 0 | } |
1636 | 0 | state->req_trailer = ndr_push_blob(ndr); |
1637 | |
|
1638 | 0 | align = state->req_data->length & 0x3; |
1639 | 0 | if (align > 0) { |
1640 | 0 | pad = 4 - align; |
1641 | 0 | } |
1642 | 0 | if (pad > 0) { |
1643 | 0 | bool ok; |
1644 | 0 | uint8_t *p; |
1645 | 0 | const uint8_t zeros[4] = { 0, }; |
1646 | |
|
1647 | 0 | ok = data_blob_append(ndr, &state->req_trailer, zeros, pad); |
1648 | 0 | if (!ok) { |
1649 | 0 | return NT_STATUS_NO_MEMORY; |
1650 | 0 | } |
1651 | | |
1652 | | /* move the padding to the start */ |
1653 | 0 | p = state->req_trailer.data; |
1654 | 0 | memmove(p + pad, p, state->req_trailer.length - pad); |
1655 | 0 | memset(p, 0, pad); |
1656 | 0 | } |
1657 | | |
1658 | 0 | return NT_STATUS_OK; |
1659 | 0 | } |
1660 | | |
1661 | | static NTSTATUS prepare_next_frag(struct rpc_api_pipe_req_state *state, |
1662 | | bool *is_last_frag) |
1663 | 0 | { |
1664 | 0 | size_t auth_len; |
1665 | 0 | size_t frag_len; |
1666 | 0 | uint8_t flags = 0; |
1667 | 0 | size_t pad_len; |
1668 | 0 | size_t data_left; |
1669 | 0 | size_t data_thistime; |
1670 | 0 | size_t trailer_left; |
1671 | 0 | size_t trailer_thistime = 0; |
1672 | 0 | size_t total_left; |
1673 | 0 | size_t total_thistime; |
1674 | 0 | NTSTATUS status; |
1675 | 0 | bool ok; |
1676 | 0 | union dcerpc_payload u; |
1677 | |
|
1678 | 0 | data_left = state->req_data->length - state->req_data_sent; |
1679 | 0 | trailer_left = state->req_trailer.length - state->req_trailer_sent; |
1680 | 0 | total_left = data_left + trailer_left; |
1681 | 0 | if ((total_left < data_left) || (total_left < trailer_left)) { |
1682 | | /* |
1683 | | * overflow |
1684 | | */ |
1685 | 0 | return NT_STATUS_INVALID_PARAMETER_MIX; |
1686 | 0 | } |
1687 | | |
1688 | 0 | status = dcerpc_guess_sizes(state->cli->auth, |
1689 | 0 | DCERPC_REQUEST_LENGTH, total_left, |
1690 | 0 | state->cli->conn->features.max_xmit_frag, |
1691 | 0 | &total_thistime, |
1692 | 0 | &frag_len, &auth_len, &pad_len); |
1693 | 0 | if (!NT_STATUS_IS_OK(status)) { |
1694 | 0 | return status; |
1695 | 0 | } |
1696 | | |
1697 | 0 | if (state->req_data_sent == 0) { |
1698 | 0 | flags = DCERPC_PFC_FLAG_FIRST; |
1699 | 0 | } |
1700 | |
|
1701 | 0 | if (total_thistime == total_left) { |
1702 | 0 | flags |= DCERPC_PFC_FLAG_LAST; |
1703 | 0 | } |
1704 | |
|
1705 | 0 | data_thistime = MIN(total_thistime, data_left); |
1706 | 0 | if (data_thistime < total_thistime) { |
1707 | 0 | trailer_thistime = total_thistime - data_thistime; |
1708 | 0 | } |
1709 | |
|
1710 | 0 | data_blob_free(&state->rpc_out); |
1711 | |
|
1712 | 0 | u = (union dcerpc_payload) { |
1713 | 0 | .request.alloc_hint = total_left, |
1714 | 0 | .request.context_id = state->cli->pres_context_id, |
1715 | 0 | .request.opnum = state->op_num, |
1716 | 0 | }; |
1717 | |
|
1718 | 0 | if (state->object_uuid) { |
1719 | 0 | flags |= DCERPC_PFC_FLAG_OBJECT_UUID; |
1720 | 0 | u.request.object.object = *state->object_uuid; |
1721 | 0 | frag_len += ndr_size_GUID(state->object_uuid, 0); |
1722 | 0 | } |
1723 | |
|
1724 | 0 | status = dcerpc_push_ncacn_packet(state, |
1725 | 0 | DCERPC_PKT_REQUEST, |
1726 | 0 | flags, |
1727 | 0 | auth_len, |
1728 | 0 | state->call_id, |
1729 | 0 | &u, |
1730 | 0 | &state->rpc_out); |
1731 | 0 | if (!NT_STATUS_IS_OK(status)) { |
1732 | 0 | return status; |
1733 | 0 | } |
1734 | | |
1735 | | /* explicitly set frag_len here as dcerpc_push_ncacn_packet() can't |
1736 | | * compute it right for requests because the auth trailer is missing |
1737 | | * at this stage */ |
1738 | 0 | dcerpc_set_frag_length(&state->rpc_out, frag_len); |
1739 | |
|
1740 | 0 | if (data_thistime > 0) { |
1741 | | /* Copy in the data. */ |
1742 | 0 | ok = data_blob_append(NULL, &state->rpc_out, |
1743 | 0 | state->req_data->data + state->req_data_sent, |
1744 | 0 | data_thistime); |
1745 | 0 | if (!ok) { |
1746 | 0 | return NT_STATUS_NO_MEMORY; |
1747 | 0 | } |
1748 | 0 | state->req_data_sent += data_thistime; |
1749 | 0 | } |
1750 | | |
1751 | 0 | if (trailer_thistime > 0) { |
1752 | | /* Copy in the verification trailer. */ |
1753 | 0 | ok = data_blob_append(NULL, &state->rpc_out, |
1754 | 0 | state->req_trailer.data + state->req_trailer_sent, |
1755 | 0 | trailer_thistime); |
1756 | 0 | if (!ok) { |
1757 | 0 | return NT_STATUS_NO_MEMORY; |
1758 | 0 | } |
1759 | 0 | state->req_trailer_sent += trailer_thistime; |
1760 | 0 | } |
1761 | | |
1762 | 0 | switch (state->cli->auth->auth_level) { |
1763 | 0 | case DCERPC_AUTH_LEVEL_NONE: |
1764 | 0 | case DCERPC_AUTH_LEVEL_CONNECT: |
1765 | 0 | break; |
1766 | 0 | case DCERPC_AUTH_LEVEL_PACKET: |
1767 | 0 | case DCERPC_AUTH_LEVEL_INTEGRITY: |
1768 | 0 | case DCERPC_AUTH_LEVEL_PRIVACY: |
1769 | 0 | status = dcerpc_add_auth_footer(state->cli->auth, pad_len, |
1770 | 0 | &state->rpc_out); |
1771 | 0 | if (!NT_STATUS_IS_OK(status)) { |
1772 | 0 | return status; |
1773 | 0 | } |
1774 | 0 | break; |
1775 | 0 | default: |
1776 | 0 | return NT_STATUS_INVALID_PARAMETER; |
1777 | 0 | } |
1778 | | |
1779 | 0 | *is_last_frag = ((flags & DCERPC_PFC_FLAG_LAST) != 0); |
1780 | |
|
1781 | 0 | return status; |
1782 | 0 | } |
1783 | | |
1784 | | static void rpc_api_pipe_req_write_done(struct tevent_req *subreq) |
1785 | 0 | { |
1786 | 0 | struct tevent_req *req = tevent_req_callback_data( |
1787 | 0 | subreq, struct tevent_req); |
1788 | 0 | struct rpc_api_pipe_req_state *state = tevent_req_data( |
1789 | 0 | req, struct rpc_api_pipe_req_state); |
1790 | 0 | NTSTATUS status; |
1791 | 0 | bool is_last_frag; |
1792 | |
|
1793 | 0 | status = rpc_write_recv(subreq); |
1794 | 0 | TALLOC_FREE(subreq); |
1795 | 0 | if (tevent_req_nterror(req, status)) { |
1796 | 0 | return; |
1797 | 0 | } |
1798 | | |
1799 | 0 | status = prepare_next_frag(state, &is_last_frag); |
1800 | 0 | if (tevent_req_nterror(req, status)) { |
1801 | 0 | return; |
1802 | 0 | } |
1803 | | |
1804 | 0 | if (is_last_frag) { |
1805 | 0 | subreq = rpc_api_pipe_send(state, state->ev, state->cli, |
1806 | 0 | &state->rpc_out, |
1807 | 0 | DCERPC_PKT_RESPONSE, |
1808 | 0 | state->call_id); |
1809 | 0 | if (tevent_req_nomem(subreq, req)) { |
1810 | 0 | return; |
1811 | 0 | } |
1812 | 0 | tevent_req_set_callback(subreq, rpc_api_pipe_req_done, req); |
1813 | 0 | } else { |
1814 | 0 | subreq = rpc_write_send(state, state->ev, |
1815 | 0 | state->cli->conn->transport, |
1816 | 0 | state->rpc_out.data, |
1817 | 0 | state->rpc_out.length); |
1818 | 0 | if (tevent_req_nomem(subreq, req)) { |
1819 | 0 | return; |
1820 | 0 | } |
1821 | 0 | tevent_req_set_callback(subreq, rpc_api_pipe_req_write_done, |
1822 | 0 | req); |
1823 | 0 | } |
1824 | 0 | } |
1825 | | |
1826 | | static void rpc_api_pipe_req_done(struct tevent_req *subreq) |
1827 | 0 | { |
1828 | 0 | struct tevent_req *req = tevent_req_callback_data( |
1829 | 0 | subreq, struct tevent_req); |
1830 | 0 | struct rpc_api_pipe_req_state *state = tevent_req_data( |
1831 | 0 | req, struct rpc_api_pipe_req_state); |
1832 | 0 | NTSTATUS status; |
1833 | |
|
1834 | 0 | status = rpc_api_pipe_recv(subreq, state, NULL, &state->reply_pdu); |
1835 | 0 | TALLOC_FREE(subreq); |
1836 | 0 | if (tevent_req_nterror(req, status)) { |
1837 | 0 | return; |
1838 | 0 | } |
1839 | | |
1840 | 0 | if (state->cli->auth == NULL) { |
1841 | 0 | tevent_req_done(req); |
1842 | 0 | return; |
1843 | 0 | } |
1844 | | |
1845 | 0 | if (state->verify_bitmask1) { |
1846 | 0 | state->cli->auth->verified_bitmask1 = true; |
1847 | 0 | } |
1848 | |
|
1849 | 0 | if (state->verify_pcontext) { |
1850 | 0 | state->cli->verified_pcontext = true; |
1851 | 0 | } |
1852 | |
|
1853 | 0 | tevent_req_done(req); |
1854 | 0 | } |
1855 | | |
1856 | | static NTSTATUS rpc_api_pipe_req_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx, |
1857 | | DATA_BLOB *reply_pdu) |
1858 | 0 | { |
1859 | 0 | struct rpc_api_pipe_req_state *state = tevent_req_data( |
1860 | 0 | req, struct rpc_api_pipe_req_state); |
1861 | 0 | NTSTATUS status; |
1862 | |
|
1863 | 0 | if (tevent_req_is_nterror(req, &status)) { |
1864 | | /* |
1865 | | * We always have to initialize to reply pdu, even if there is |
1866 | | * none. The rpccli_* caller routines expect this. |
1867 | | */ |
1868 | 0 | *reply_pdu = data_blob_null; |
1869 | 0 | return status; |
1870 | 0 | } |
1871 | | |
1872 | | /* return data to caller and assign it ownership of memory */ |
1873 | 0 | reply_pdu->data = talloc_move(mem_ctx, &state->reply_pdu.data); |
1874 | 0 | reply_pdu->length = state->reply_pdu.length; |
1875 | 0 | state->reply_pdu.length = 0; |
1876 | |
|
1877 | 0 | return NT_STATUS_OK; |
1878 | 0 | } |
1879 | | |
1880 | | /**************************************************************************** |
1881 | | Check the rpc bind acknowledge response. |
1882 | | ****************************************************************************/ |
1883 | | |
1884 | | static bool check_bind_response(const struct dcerpc_bind_ack *r, |
1885 | | struct rpc_pipe_client *cli) |
1886 | 0 | { |
1887 | 0 | const struct ndr_syntax_id *transfer = &cli->transfer_syntax; |
1888 | 0 | uint32_t assoc_group_id = |
1889 | 0 | dcerpc_binding_get_assoc_group_id(cli->assoc->binding); |
1890 | 0 | struct dcerpc_ack_ctx ctx; |
1891 | 0 | bool equal; |
1892 | |
|
1893 | 0 | if (r->secondary_address_size == 0) { |
1894 | 0 | DEBUG(4,("Ignoring length check -- ASU bug (server didn't fill in the pipe name correctly)\n")); |
1895 | 0 | } |
1896 | |
|
1897 | 0 | if (assoc_group_id == 0) { |
1898 | 0 | NTSTATUS status; |
1899 | | |
1900 | | /* |
1901 | | * We only capture the first assoc_group_id we're |
1902 | | * getting. |
1903 | | * |
1904 | | * Current samba releases may ignore the client value |
1905 | | * and return a different assoc_group_id if the |
1906 | | * client given one is not found in the preforked |
1907 | | * process. This applies to the source4 netlogon, |
1908 | | * which uses DCESRV_INTERFACE_FLAGS_HANDLES_NOT_USED. |
1909 | | */ |
1910 | |
|
1911 | 0 | status = dcerpc_binding_set_assoc_group_id(cli->assoc->binding, |
1912 | 0 | r->assoc_group_id); |
1913 | 0 | if (!NT_STATUS_IS_OK(status)) { |
1914 | 0 | return false; |
1915 | 0 | } |
1916 | 0 | } |
1917 | | |
1918 | 0 | if (!cli->conn->features.bind_done) { |
1919 | | /* |
1920 | | * DCE-RPC 1.1 (c706) specifies |
1921 | | * CONST_MUST_RCV_FRAG_SIZE as 1432 |
1922 | | */ |
1923 | |
|
1924 | 0 | if (r->max_xmit_frag < 1432) { |
1925 | 0 | return false; |
1926 | 0 | } |
1927 | 0 | if (r->max_recv_frag < 1432) { |
1928 | 0 | return false; |
1929 | 0 | } |
1930 | | |
1931 | 0 | cli->conn->features.max_xmit_frag = |
1932 | 0 | MIN(cli->conn->features.max_xmit_frag, r->max_xmit_frag); |
1933 | 0 | cli->conn->features.max_recv_frag = |
1934 | 0 | MIN(cli->conn->features.max_recv_frag, r->max_recv_frag); |
1935 | |
|
1936 | 0 | cli->conn->features.bind_done = true; |
1937 | 0 | } |
1938 | | |
1939 | 0 | if (r->num_results < 1 || !r->ctx_list) { |
1940 | 0 | return false; |
1941 | 0 | } |
1942 | | |
1943 | 0 | ctx = r->ctx_list[0]; |
1944 | | |
1945 | | /* check the transfer syntax */ |
1946 | 0 | equal = ndr_syntax_id_equal(&ctx.syntax, transfer); |
1947 | 0 | if (!equal) { |
1948 | 0 | DEBUG(2,("bind_rpc_pipe: transfer syntax differs\n")); |
1949 | 0 | return False; |
1950 | 0 | } |
1951 | | |
1952 | 0 | if (ctx.result != DCERPC_BIND_ACK_RESULT_ACCEPTANCE) { |
1953 | 0 | DBG_NOTICE("bind denied result: %d reason: %x\n", |
1954 | 0 | ctx.result, ctx.reason.value); |
1955 | 0 | return false; |
1956 | 0 | } |
1957 | | |
1958 | 0 | if (r->num_results >= 2) { |
1959 | 0 | const struct dcerpc_ack_ctx *neg = &r->ctx_list[1]; |
1960 | |
|
1961 | 0 | if (neg->result == DCERPC_BIND_ACK_RESULT_NEGOTIATE_ACK) { |
1962 | 0 | cli->assoc->features.negotiated = neg->reason.negotiate; |
1963 | 0 | cli->assoc->features.negotiation_done = true; |
1964 | | /* |
1965 | | * consume presentation context used for bind time |
1966 | | * feature negotiation |
1967 | | */ |
1968 | 0 | cli->conn->next_pres_context_id++; |
1969 | 0 | } else { |
1970 | 0 | DBG_DEBUG("bind_time_feature failed - " |
1971 | 0 | "result: %d reason %x\n", |
1972 | 0 | neg->result, neg->reason.value); |
1973 | 0 | } |
1974 | 0 | } |
1975 | |
|
1976 | 0 | DEBUG(5,("check_bind_response: accepted!\n")); |
1977 | 0 | return True; |
1978 | 0 | } |
1979 | | |
1980 | | /******************************************************************* |
1981 | | Creates a DCE/RPC bind authentication response. |
1982 | | This is the packet that is sent back to the server once we |
1983 | | have received a BIND-ACK, to finish the third leg of |
1984 | | the authentication handshake. |
1985 | | ********************************************************************/ |
1986 | | |
1987 | | static NTSTATUS create_rpc_bind_auth3(TALLOC_CTX *mem_ctx, |
1988 | | struct rpc_pipe_client *cli, |
1989 | | struct pipe_auth_data *auth, |
1990 | | uint32_t rpc_call_id, |
1991 | | DATA_BLOB *pauth_blob, |
1992 | | DATA_BLOB *rpc_out) |
1993 | 0 | { |
1994 | 0 | NTSTATUS status; |
1995 | 0 | union dcerpc_payload u = { .auth3._pad = 0, }; |
1996 | |
|
1997 | 0 | status = dcerpc_push_dcerpc_auth(mem_ctx, |
1998 | 0 | auth->auth_type, |
1999 | 0 | auth->auth_level, |
2000 | 0 | 0, /* auth_pad_length */ |
2001 | 0 | auth->auth_context_id, |
2002 | 0 | pauth_blob, |
2003 | 0 | &u.auth3.auth_info); |
2004 | 0 | if (!NT_STATUS_IS_OK(status)) { |
2005 | 0 | return status; |
2006 | 0 | } |
2007 | | |
2008 | 0 | status = dcerpc_push_ncacn_packet(mem_ctx, |
2009 | 0 | DCERPC_PKT_AUTH3, |
2010 | 0 | DCERPC_PFC_FLAG_FIRST | |
2011 | 0 | DCERPC_PFC_FLAG_LAST, |
2012 | 0 | pauth_blob->length, |
2013 | 0 | rpc_call_id, |
2014 | 0 | &u, |
2015 | 0 | rpc_out); |
2016 | 0 | data_blob_free(&u.auth3.auth_info); |
2017 | 0 | if (!NT_STATUS_IS_OK(status)) { |
2018 | 0 | DEBUG(0,("create_bind_or_alt_ctx_internal: failed to marshall RPC_HDR_RB.\n")); |
2019 | 0 | return status; |
2020 | 0 | } |
2021 | | |
2022 | 0 | return NT_STATUS_OK; |
2023 | 0 | } |
2024 | | |
2025 | | /******************************************************************* |
2026 | | Creates a DCE/RPC bind alter context authentication request which |
2027 | | may contain a spnego auth blob |
2028 | | ********************************************************************/ |
2029 | | |
2030 | | static NTSTATUS create_rpc_alter_context(TALLOC_CTX *mem_ctx, |
2031 | | struct pipe_auth_data *auth, |
2032 | | uint32_t rpc_call_id, |
2033 | | struct rpc_client_association *assoc, |
2034 | | struct rpc_client_connection *conn, |
2035 | | uint16_t pres_context_id, |
2036 | | const struct ndr_syntax_id *abstract, |
2037 | | const struct ndr_syntax_id *transfer, |
2038 | | const DATA_BLOB *pauth_blob, /* spnego auth blob already created. */ |
2039 | | DATA_BLOB *rpc_out) |
2040 | 0 | { |
2041 | 0 | DATA_BLOB auth_info; |
2042 | 0 | NTSTATUS status; |
2043 | |
|
2044 | 0 | status = dcerpc_push_dcerpc_auth(mem_ctx, |
2045 | 0 | auth->auth_type, |
2046 | 0 | auth->auth_level, |
2047 | 0 | 0, /* auth_pad_length */ |
2048 | 0 | auth->auth_context_id, |
2049 | 0 | pauth_blob, |
2050 | 0 | &auth_info); |
2051 | 0 | if (!NT_STATUS_IS_OK(status)) { |
2052 | 0 | return status; |
2053 | 0 | } |
2054 | | |
2055 | 0 | status = create_bind_or_alt_ctx_internal(mem_ctx, |
2056 | 0 | DCERPC_PKT_ALTER, |
2057 | 0 | rpc_call_id, |
2058 | 0 | assoc, |
2059 | 0 | conn, |
2060 | 0 | pres_context_id, |
2061 | 0 | abstract, |
2062 | 0 | transfer, |
2063 | 0 | &auth_info, |
2064 | 0 | rpc_out); |
2065 | 0 | data_blob_free(&auth_info); |
2066 | 0 | return status; |
2067 | 0 | } |
2068 | | |
2069 | | /**************************************************************************** |
2070 | | Do an rpc bind. |
2071 | | ****************************************************************************/ |
2072 | | |
2073 | | struct rpc_pipe_bind_state { |
2074 | | struct tevent_context *ev; |
2075 | | struct rpc_pipe_client *cli; |
2076 | | DATA_BLOB rpc_out; |
2077 | | bool auth3; |
2078 | | uint32_t rpc_call_id; |
2079 | | }; |
2080 | | |
2081 | | static void rpc_pipe_bind_step_one_done(struct tevent_req *subreq); |
2082 | | static NTSTATUS rpc_bind_next_send(struct tevent_req *req, |
2083 | | struct rpc_pipe_bind_state *state, |
2084 | | DATA_BLOB *credentials); |
2085 | | static NTSTATUS rpc_bind_finish_send(struct tevent_req *req, |
2086 | | struct rpc_pipe_bind_state *state, |
2087 | | DATA_BLOB *credentials); |
2088 | | |
2089 | | struct tevent_req *rpc_pipe_bind_send(TALLOC_CTX *mem_ctx, |
2090 | | struct tevent_context *ev, |
2091 | | struct rpc_pipe_client *cli, |
2092 | | struct pipe_auth_data *auth) |
2093 | 0 | { |
2094 | 0 | struct tevent_req *req, *subreq; |
2095 | 0 | struct rpc_pipe_bind_state *state; |
2096 | 0 | struct cli_credentials *creds = NULL; |
2097 | 0 | const char *username = NULL; |
2098 | 0 | enum dcerpc_pkt_type rep_ptype = DCERPC_PKT_BIND_ACK; |
2099 | 0 | NTSTATUS status; |
2100 | |
|
2101 | 0 | if (cli->conn->features.bind_done) { |
2102 | 0 | rep_ptype = DCERPC_PKT_ALTER_RESP; |
2103 | 0 | } |
2104 | |
|
2105 | 0 | req = tevent_req_create(mem_ctx, &state, struct rpc_pipe_bind_state); |
2106 | 0 | if (req == NULL) { |
2107 | 0 | return NULL; |
2108 | 0 | } |
2109 | | |
2110 | 0 | DEBUG(5,("Bind RPC Pipe: %s auth_type %u, auth_level %u\n", |
2111 | 0 | rpccli_pipe_txt(talloc_tos(), cli), |
2112 | 0 | (unsigned int)auth->auth_type, |
2113 | 0 | (unsigned int)auth->auth_level )); |
2114 | |
|
2115 | 0 | state->ev = ev; |
2116 | 0 | state->cli = cli; |
2117 | 0 | state->rpc_call_id = ++cli->assoc->next_call_id; |
2118 | |
|
2119 | 0 | cli->auth = talloc_move(cli, &auth); |
2120 | 0 | if (cli->auth->auth_context_id == UINT32_MAX) { |
2121 | 0 | if (cli->conn->next_auth_context_id == UINT32_MAX) { |
2122 | 0 | tevent_req_nterror(req, NT_STATUS_RPC_NO_MORE_BINDINGS); |
2123 | 0 | return tevent_req_post(req, ev); |
2124 | 0 | } |
2125 | 0 | cli->auth->auth_context_id = cli->conn->next_auth_context_id++; |
2126 | 0 | } |
2127 | 0 | if (cli->pres_context_id == UINT16_MAX) { |
2128 | 0 | if (cli->conn->next_pres_context_id == UINT16_MAX) { |
2129 | 0 | tevent_req_nterror(req, NT_STATUS_RPC_NO_MORE_BINDINGS); |
2130 | 0 | return tevent_req_post(req, ev); |
2131 | 0 | } |
2132 | 0 | cli->pres_context_id = cli->conn->next_pres_context_id++; |
2133 | 0 | } |
2134 | | |
2135 | 0 | cli->binding_handle = rpccli_bh_create(cli, NULL, cli->table); |
2136 | 0 | if (tevent_req_nomem(cli->binding_handle, req)) { |
2137 | 0 | return tevent_req_post(req, ev); |
2138 | 0 | } |
2139 | | |
2140 | 0 | creds = gensec_get_credentials(cli->auth->auth_ctx); |
2141 | 0 | username = cli_credentials_get_username(creds); |
2142 | 0 | cli->printer_username = talloc_strdup(cli, username); |
2143 | 0 | if (tevent_req_nomem(cli->printer_username, req)) { |
2144 | 0 | return tevent_req_post(req, ev); |
2145 | 0 | } |
2146 | | |
2147 | | /* Marshall the outgoing data. */ |
2148 | 0 | status = create_rpc_bind_req(state, cli, |
2149 | 0 | cli->auth, |
2150 | 0 | state->rpc_call_id, |
2151 | 0 | &cli->table->syntax_id, |
2152 | 0 | &cli->transfer_syntax, |
2153 | 0 | &state->rpc_out); |
2154 | |
|
2155 | 0 | if (!NT_STATUS_IS_OK(status) && |
2156 | 0 | !NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) { |
2157 | 0 | tevent_req_nterror(req, status); |
2158 | 0 | return tevent_req_post(req, ev); |
2159 | 0 | } |
2160 | | |
2161 | 0 | subreq = rpc_api_pipe_send(state, ev, cli, &state->rpc_out, |
2162 | 0 | rep_ptype, state->rpc_call_id); |
2163 | 0 | if (tevent_req_nomem(subreq, req)) { |
2164 | 0 | return tevent_req_post(req, ev); |
2165 | 0 | } |
2166 | 0 | tevent_req_set_callback(subreq, rpc_pipe_bind_step_one_done, req); |
2167 | 0 | return req; |
2168 | 0 | } |
2169 | | |
2170 | | static void rpc_pipe_bind_step_one_done(struct tevent_req *subreq) |
2171 | 0 | { |
2172 | 0 | struct tevent_req *req = tevent_req_callback_data( |
2173 | 0 | subreq, struct tevent_req); |
2174 | 0 | struct rpc_pipe_bind_state *state = tevent_req_data( |
2175 | 0 | req, struct rpc_pipe_bind_state); |
2176 | 0 | struct pipe_auth_data *pauth = state->cli->auth; |
2177 | 0 | struct gensec_security *gensec_security; |
2178 | 0 | struct ncacn_packet *pkt = NULL; |
2179 | 0 | struct dcerpc_auth auth; |
2180 | 0 | DATA_BLOB auth_token = { .data = NULL }; |
2181 | 0 | NTSTATUS status; |
2182 | |
|
2183 | 0 | status = rpc_api_pipe_recv(subreq, talloc_tos(), &pkt, NULL); |
2184 | 0 | TALLOC_FREE(subreq); |
2185 | 0 | if (tevent_req_nterror(req, status)) { |
2186 | 0 | DEBUG(3, ("rpc_pipe_bind: %s bind request returned %s\n", |
2187 | 0 | rpccli_pipe_txt(talloc_tos(), state->cli), |
2188 | 0 | nt_errstr(status))); |
2189 | 0 | return; |
2190 | 0 | } |
2191 | | |
2192 | 0 | if (state->auth3) { |
2193 | 0 | tevent_req_done(req); |
2194 | 0 | return; |
2195 | 0 | } |
2196 | | |
2197 | 0 | if (!check_bind_response(&pkt->u.bind_ack, state->cli)) { |
2198 | 0 | DEBUG(2, ("rpc_pipe_bind: check_bind_response failed.\n")); |
2199 | 0 | tevent_req_nterror(req, NT_STATUS_BUFFER_TOO_SMALL); |
2200 | 0 | return; |
2201 | 0 | } |
2202 | | |
2203 | 0 | if (state->cli->conn->features.client_hdr_signing && |
2204 | 0 | pkt->pfc_flags & DCERPC_PFC_FLAG_SUPPORT_HEADER_SIGN) |
2205 | 0 | { |
2206 | 0 | state->cli->conn->features.hdr_signing = true; |
2207 | 0 | } |
2208 | |
|
2209 | 0 | if (pauth->auth_type == DCERPC_AUTH_TYPE_NONE) { |
2210 | | /* Bind complete. */ |
2211 | 0 | tevent_req_done(req); |
2212 | 0 | return; |
2213 | 0 | } |
2214 | | |
2215 | 0 | if (pkt->auth_length == 0) { |
2216 | 0 | tevent_req_nterror(req, NT_STATUS_RPC_PROTOCOL_ERROR); |
2217 | 0 | return; |
2218 | 0 | } |
2219 | | |
2220 | 0 | pauth->hdr_signing = state->cli->conn->features.hdr_signing; |
2221 | | |
2222 | | /* get auth credentials */ |
2223 | 0 | status = dcerpc_pull_auth_trailer(pkt, talloc_tos(), |
2224 | 0 | &pkt->u.bind_ack.auth_info, |
2225 | 0 | &auth, NULL, true); |
2226 | 0 | if (tevent_req_nterror(req, status)) { |
2227 | 0 | DEBUG(0, ("Failed to pull dcerpc auth: %s.\n", |
2228 | 0 | nt_errstr(status))); |
2229 | 0 | return; |
2230 | 0 | } |
2231 | | |
2232 | 0 | if (auth.auth_type != pauth->auth_type) { |
2233 | 0 | DBG_ERR("Auth type %u mismatch expected %u.\n", |
2234 | 0 | auth.auth_type, pauth->auth_type); |
2235 | 0 | tevent_req_nterror(req, NT_STATUS_RPC_PROTOCOL_ERROR); |
2236 | 0 | return; |
2237 | 0 | } |
2238 | | |
2239 | 0 | if (auth.auth_level != pauth->auth_level) { |
2240 | 0 | DBG_ERR("Auth level %u mismatch expected %u.\n", |
2241 | 0 | auth.auth_level, pauth->auth_level); |
2242 | 0 | tevent_req_nterror(req, NT_STATUS_RPC_PROTOCOL_ERROR); |
2243 | 0 | return; |
2244 | 0 | } |
2245 | | |
2246 | 0 | if (auth.auth_context_id != pauth->auth_context_id) { |
2247 | 0 | DBG_ERR("Auth context id %"PRIu32" mismatch " |
2248 | 0 | "expected %"PRIu32".\n", |
2249 | 0 | auth.auth_context_id, |
2250 | 0 | pauth->auth_context_id); |
2251 | 0 | tevent_req_nterror(req, NT_STATUS_RPC_PROTOCOL_ERROR); |
2252 | 0 | return; |
2253 | 0 | } |
2254 | | |
2255 | | /* |
2256 | | * For authenticated binds we may need to do 3 or 4 leg binds. |
2257 | | */ |
2258 | | |
2259 | 0 | if (pauth->auth_type == DCERPC_AUTH_TYPE_NONE) { |
2260 | | /* Bind complete. */ |
2261 | 0 | tevent_req_done(req); |
2262 | 0 | return; |
2263 | 0 | } |
2264 | | |
2265 | 0 | gensec_security = pauth->auth_ctx; |
2266 | |
|
2267 | 0 | status = gensec_update(gensec_security, state, |
2268 | 0 | auth.credentials, &auth_token); |
2269 | 0 | if (NT_STATUS_EQUAL(status, |
2270 | 0 | NT_STATUS_MORE_PROCESSING_REQUIRED)) { |
2271 | 0 | status = rpc_bind_next_send(req, state, |
2272 | 0 | &auth_token); |
2273 | 0 | } else if (NT_STATUS_IS_OK(status)) { |
2274 | 0 | if (pauth->hdr_signing) { |
2275 | 0 | gensec_want_feature(gensec_security, |
2276 | 0 | GENSEC_FEATURE_SIGN_PKT_HEADER); |
2277 | 0 | } |
2278 | |
|
2279 | 0 | if (auth_token.length == 0) { |
2280 | | /* Bind complete. */ |
2281 | 0 | tevent_req_done(req); |
2282 | 0 | return; |
2283 | 0 | } |
2284 | 0 | status = rpc_bind_finish_send(req, state, |
2285 | 0 | &auth_token); |
2286 | 0 | } |
2287 | | |
2288 | 0 | if (!NT_STATUS_IS_OK(status)) { |
2289 | 0 | tevent_req_nterror(req, status); |
2290 | 0 | } |
2291 | 0 | return; |
2292 | 0 | } |
2293 | | |
2294 | | static NTSTATUS rpc_bind_next_send(struct tevent_req *req, |
2295 | | struct rpc_pipe_bind_state *state, |
2296 | | DATA_BLOB *auth_token) |
2297 | 0 | { |
2298 | 0 | struct pipe_auth_data *auth = state->cli->auth; |
2299 | 0 | struct tevent_req *subreq; |
2300 | 0 | NTSTATUS status; |
2301 | | |
2302 | | /* Now prepare the alter context pdu. */ |
2303 | 0 | data_blob_free(&state->rpc_out); |
2304 | |
|
2305 | 0 | status = create_rpc_alter_context(state, auth, |
2306 | 0 | state->rpc_call_id, |
2307 | 0 | state->cli->assoc, |
2308 | 0 | state->cli->conn, |
2309 | 0 | state->cli->pres_context_id, |
2310 | 0 | &state->cli->table->syntax_id, |
2311 | 0 | &state->cli->transfer_syntax, |
2312 | 0 | auth_token, |
2313 | 0 | &state->rpc_out); |
2314 | 0 | if (!NT_STATUS_IS_OK(status)) { |
2315 | 0 | return status; |
2316 | 0 | } |
2317 | | |
2318 | 0 | subreq = rpc_api_pipe_send(state, state->ev, state->cli, |
2319 | 0 | &state->rpc_out, DCERPC_PKT_ALTER_RESP, |
2320 | 0 | state->rpc_call_id); |
2321 | 0 | if (subreq == NULL) { |
2322 | 0 | return NT_STATUS_NO_MEMORY; |
2323 | 0 | } |
2324 | 0 | tevent_req_set_callback(subreq, rpc_pipe_bind_step_one_done, req); |
2325 | 0 | return NT_STATUS_OK; |
2326 | 0 | } |
2327 | | |
2328 | | static NTSTATUS rpc_bind_finish_send(struct tevent_req *req, |
2329 | | struct rpc_pipe_bind_state *state, |
2330 | | DATA_BLOB *auth_token) |
2331 | 0 | { |
2332 | 0 | struct pipe_auth_data *auth = state->cli->auth; |
2333 | 0 | struct tevent_req *subreq; |
2334 | 0 | NTSTATUS status; |
2335 | |
|
2336 | 0 | state->auth3 = true; |
2337 | | |
2338 | | /* Now prepare the auth3 context pdu. */ |
2339 | 0 | data_blob_free(&state->rpc_out); |
2340 | |
|
2341 | 0 | status = create_rpc_bind_auth3(state, state->cli, auth, |
2342 | 0 | state->rpc_call_id, |
2343 | 0 | auth_token, |
2344 | 0 | &state->rpc_out); |
2345 | 0 | if (!NT_STATUS_IS_OK(status)) { |
2346 | 0 | return status; |
2347 | 0 | } |
2348 | | |
2349 | 0 | subreq = rpc_api_pipe_send(state, state->ev, state->cli, |
2350 | 0 | &state->rpc_out, DCERPC_PKT_AUTH3, |
2351 | 0 | state->rpc_call_id); |
2352 | 0 | if (subreq == NULL) { |
2353 | 0 | return NT_STATUS_NO_MEMORY; |
2354 | 0 | } |
2355 | 0 | tevent_req_set_callback(subreq, rpc_pipe_bind_step_one_done, req); |
2356 | 0 | return NT_STATUS_OK; |
2357 | 0 | } |
2358 | | |
2359 | | NTSTATUS rpc_pipe_bind_recv(struct tevent_req *req) |
2360 | 0 | { |
2361 | 0 | return tevent_req_simple_recv_ntstatus(req); |
2362 | 0 | } |
2363 | | |
2364 | | NTSTATUS rpc_pipe_bind(struct rpc_pipe_client *cli, |
2365 | | struct pipe_auth_data *auth) |
2366 | 0 | { |
2367 | 0 | TALLOC_CTX *frame = talloc_stackframe(); |
2368 | 0 | struct tevent_context *ev; |
2369 | 0 | struct tevent_req *req; |
2370 | 0 | NTSTATUS status = NT_STATUS_OK; |
2371 | |
|
2372 | 0 | ev = samba_tevent_context_init(frame); |
2373 | 0 | if (ev == NULL) { |
2374 | 0 | status = NT_STATUS_NO_MEMORY; |
2375 | 0 | goto fail; |
2376 | 0 | } |
2377 | | |
2378 | 0 | req = rpc_pipe_bind_send(frame, ev, cli, auth); |
2379 | 0 | if (req == NULL) { |
2380 | 0 | status = NT_STATUS_NO_MEMORY; |
2381 | 0 | goto fail; |
2382 | 0 | } |
2383 | | |
2384 | 0 | if (!tevent_req_poll_ntstatus(req, ev, &status)) { |
2385 | 0 | goto fail; |
2386 | 0 | } |
2387 | | |
2388 | 0 | status = rpc_pipe_bind_recv(req); |
2389 | 0 | fail: |
2390 | 0 | TALLOC_FREE(frame); |
2391 | 0 | return status; |
2392 | 0 | } |
2393 | | |
2394 | 0 | #define RPCCLI_DEFAULT_TIMEOUT 10000 /* 10 seconds. */ |
2395 | | |
2396 | | unsigned int rpccli_set_timeout(struct rpc_pipe_client *rpc_cli, |
2397 | | unsigned int timeout) |
2398 | 0 | { |
2399 | 0 | if (rpc_cli == NULL) { |
2400 | 0 | return RPCCLI_DEFAULT_TIMEOUT; |
2401 | 0 | } |
2402 | | |
2403 | 0 | if (rpc_cli->binding_handle == NULL) { |
2404 | 0 | return RPCCLI_DEFAULT_TIMEOUT; |
2405 | 0 | } |
2406 | | |
2407 | 0 | return dcerpc_binding_handle_set_timeout(rpc_cli->binding_handle, |
2408 | 0 | timeout); |
2409 | 0 | } |
2410 | | |
2411 | | bool rpccli_is_connected(struct rpc_pipe_client *rpc_cli) |
2412 | 0 | { |
2413 | 0 | if (rpc_cli == NULL) { |
2414 | 0 | return false; |
2415 | 0 | } |
2416 | | |
2417 | 0 | if (rpc_cli->binding_handle == NULL) { |
2418 | 0 | return false; |
2419 | 0 | } |
2420 | | |
2421 | 0 | return dcerpc_binding_handle_is_connected(rpc_cli->binding_handle); |
2422 | 0 | } |
2423 | | |
2424 | | struct rpccli_bh_state { |
2425 | | struct rpc_pipe_client *rpc_cli; |
2426 | | struct dcerpc_binding *binding; |
2427 | | }; |
2428 | | |
2429 | | static const struct dcerpc_binding *rpccli_bh_get_binding(struct dcerpc_binding_handle *h) |
2430 | 0 | { |
2431 | 0 | struct rpccli_bh_state *hs = dcerpc_binding_handle_data(h, |
2432 | 0 | struct rpccli_bh_state); |
2433 | |
|
2434 | 0 | return hs->binding; |
2435 | 0 | } |
2436 | | |
2437 | | static bool rpccli_bh_is_connected(struct dcerpc_binding_handle *h) |
2438 | 0 | { |
2439 | 0 | struct rpccli_bh_state *hs = dcerpc_binding_handle_data(h, |
2440 | 0 | struct rpccli_bh_state); |
2441 | 0 | struct rpc_cli_transport *transport = NULL; |
2442 | |
|
2443 | 0 | if (hs->rpc_cli == NULL) { |
2444 | 0 | return false; |
2445 | 0 | } |
2446 | | |
2447 | 0 | if (hs->rpc_cli->conn == NULL) { |
2448 | 0 | return false; |
2449 | 0 | } |
2450 | | |
2451 | 0 | transport = hs->rpc_cli->conn->transport; |
2452 | 0 | if (transport == NULL) { |
2453 | 0 | return false; |
2454 | 0 | } |
2455 | | |
2456 | 0 | if (transport->is_connected == NULL) { |
2457 | 0 | return false; |
2458 | 0 | } |
2459 | | |
2460 | 0 | return transport->is_connected(transport->priv); |
2461 | 0 | } |
2462 | | |
2463 | | static uint32_t rpccli_bh_set_timeout(struct dcerpc_binding_handle *h, |
2464 | | uint32_t timeout) |
2465 | 0 | { |
2466 | 0 | struct rpccli_bh_state *hs = dcerpc_binding_handle_data(h, |
2467 | 0 | struct rpccli_bh_state); |
2468 | 0 | struct rpc_cli_transport *transport = NULL; |
2469 | 0 | unsigned int old; |
2470 | |
|
2471 | 0 | if (hs->rpc_cli->conn == NULL) { |
2472 | 0 | return RPCCLI_DEFAULT_TIMEOUT; |
2473 | 0 | } |
2474 | | |
2475 | 0 | transport = hs->rpc_cli->conn->transport; |
2476 | 0 | if (transport == NULL) { |
2477 | 0 | return RPCCLI_DEFAULT_TIMEOUT; |
2478 | 0 | } |
2479 | | |
2480 | 0 | if (transport->set_timeout == NULL) { |
2481 | 0 | return RPCCLI_DEFAULT_TIMEOUT; |
2482 | 0 | } |
2483 | | |
2484 | 0 | old = transport->set_timeout(transport->priv, timeout); |
2485 | 0 | if (old == 0) { |
2486 | 0 | return RPCCLI_DEFAULT_TIMEOUT; |
2487 | 0 | } |
2488 | | |
2489 | 0 | return old; |
2490 | 0 | } |
2491 | | |
2492 | | static NTSTATUS rpccli_bh_transport_session_key(struct dcerpc_binding_handle *h, |
2493 | | TALLOC_CTX *mem_ctx, |
2494 | | DATA_BLOB *session_key) |
2495 | 0 | { |
2496 | 0 | struct rpccli_bh_state *hs = dcerpc_binding_handle_data(h, |
2497 | 0 | struct rpccli_bh_state); |
2498 | 0 | DATA_BLOB sk = { .length = 0, }; |
2499 | |
|
2500 | 0 | if (hs->rpc_cli == NULL) { |
2501 | 0 | return NT_STATUS_NO_USER_SESSION_KEY; |
2502 | 0 | } |
2503 | | |
2504 | 0 | if (hs->rpc_cli->conn == NULL) { |
2505 | 0 | return NT_STATUS_NO_USER_SESSION_KEY; |
2506 | 0 | } |
2507 | | |
2508 | 0 | if (hs->rpc_cli->conn->transport_session_key.length == 0) { |
2509 | 0 | return NT_STATUS_NO_USER_SESSION_KEY; |
2510 | 0 | } |
2511 | | |
2512 | 0 | sk = hs->rpc_cli->conn->transport_session_key; |
2513 | 0 | sk.length = MIN(sk.length, 16); |
2514 | |
|
2515 | 0 | *session_key = data_blob_dup_talloc_s(mem_ctx, sk); |
2516 | 0 | if (session_key->length != sk.length) { |
2517 | 0 | return NT_STATUS_NO_MEMORY; |
2518 | 0 | } |
2519 | 0 | return NT_STATUS_OK; |
2520 | 0 | } |
2521 | | |
2522 | | static void rpccli_bh_auth_info(struct dcerpc_binding_handle *h, |
2523 | | enum dcerpc_AuthType *auth_type, |
2524 | | enum dcerpc_AuthLevel *auth_level) |
2525 | 0 | { |
2526 | 0 | struct rpccli_bh_state *hs = dcerpc_binding_handle_data(h, |
2527 | 0 | struct rpccli_bh_state); |
2528 | |
|
2529 | 0 | if (hs->rpc_cli == NULL) { |
2530 | 0 | return; |
2531 | 0 | } |
2532 | | |
2533 | 0 | if (hs->rpc_cli->auth == NULL) { |
2534 | 0 | return; |
2535 | 0 | } |
2536 | | |
2537 | 0 | *auth_type = hs->rpc_cli->auth->auth_type; |
2538 | 0 | *auth_level = hs->rpc_cli->auth->auth_level; |
2539 | 0 | } |
2540 | | |
2541 | | static NTSTATUS rpccli_bh_auth_session_key(struct dcerpc_binding_handle *h, |
2542 | | TALLOC_CTX *mem_ctx, |
2543 | | DATA_BLOB *session_key) |
2544 | 0 | { |
2545 | 0 | struct rpccli_bh_state *hs = dcerpc_binding_handle_data(h, |
2546 | 0 | struct rpccli_bh_state); |
2547 | 0 | struct pipe_auth_data *auth = NULL; |
2548 | 0 | NTSTATUS status; |
2549 | |
|
2550 | 0 | if (hs->rpc_cli == NULL) { |
2551 | 0 | return NT_STATUS_NO_USER_SESSION_KEY; |
2552 | 0 | } |
2553 | | |
2554 | 0 | if (hs->rpc_cli->auth == NULL) { |
2555 | 0 | return NT_STATUS_NO_USER_SESSION_KEY; |
2556 | 0 | } |
2557 | | |
2558 | 0 | auth = hs->rpc_cli->auth; |
2559 | |
|
2560 | 0 | if (auth->auth_type == DCERPC_AUTH_TYPE_NONE) { |
2561 | 0 | return NT_STATUS_NO_USER_SESSION_KEY; |
2562 | 0 | } |
2563 | | |
2564 | 0 | if (auth->auth_ctx == NULL) { |
2565 | 0 | return NT_STATUS_NO_USER_SESSION_KEY; |
2566 | 0 | } |
2567 | | |
2568 | 0 | status = gensec_session_key(auth->auth_ctx, |
2569 | 0 | mem_ctx, |
2570 | 0 | session_key); |
2571 | 0 | if (!NT_STATUS_IS_OK(status)) { |
2572 | 0 | return status; |
2573 | 0 | } |
2574 | | |
2575 | 0 | talloc_keep_secret(session_key->data); |
2576 | 0 | return NT_STATUS_OK; |
2577 | 0 | } |
2578 | | |
2579 | | struct rpccli_bh_raw_call_state { |
2580 | | DATA_BLOB in_data; |
2581 | | DATA_BLOB out_data; |
2582 | | uint32_t out_flags; |
2583 | | }; |
2584 | | |
2585 | | static void rpccli_bh_raw_call_done(struct tevent_req *subreq); |
2586 | | |
2587 | | static struct tevent_req *rpccli_bh_raw_call_send(TALLOC_CTX *mem_ctx, |
2588 | | struct tevent_context *ev, |
2589 | | struct dcerpc_binding_handle *h, |
2590 | | const struct GUID *object, |
2591 | | uint32_t opnum, |
2592 | | uint32_t in_flags, |
2593 | | const uint8_t *in_data, |
2594 | | size_t in_length) |
2595 | 0 | { |
2596 | 0 | struct rpccli_bh_state *hs = dcerpc_binding_handle_data(h, |
2597 | 0 | struct rpccli_bh_state); |
2598 | 0 | struct tevent_req *req; |
2599 | 0 | struct rpccli_bh_raw_call_state *state; |
2600 | 0 | bool ok; |
2601 | 0 | struct tevent_req *subreq; |
2602 | |
|
2603 | 0 | req = tevent_req_create(mem_ctx, &state, |
2604 | 0 | struct rpccli_bh_raw_call_state); |
2605 | 0 | if (req == NULL) { |
2606 | 0 | return NULL; |
2607 | 0 | } |
2608 | 0 | state->in_data.data = discard_const_p(uint8_t, in_data); |
2609 | 0 | state->in_data.length = in_length; |
2610 | |
|
2611 | 0 | ok = rpccli_bh_is_connected(h); |
2612 | 0 | if (!ok) { |
2613 | 0 | tevent_req_nterror(req, NT_STATUS_CONNECTION_DISCONNECTED); |
2614 | 0 | return tevent_req_post(req, ev); |
2615 | 0 | } |
2616 | | |
2617 | 0 | subreq = rpc_api_pipe_req_send(state, ev, hs->rpc_cli, |
2618 | 0 | opnum, object, &state->in_data); |
2619 | 0 | if (tevent_req_nomem(subreq, req)) { |
2620 | 0 | return tevent_req_post(req, ev); |
2621 | 0 | } |
2622 | 0 | tevent_req_set_callback(subreq, rpccli_bh_raw_call_done, req); |
2623 | |
|
2624 | 0 | return req; |
2625 | 0 | } |
2626 | | |
2627 | | static void rpccli_bh_raw_call_done(struct tevent_req *subreq) |
2628 | 0 | { |
2629 | 0 | struct tevent_req *req = |
2630 | 0 | tevent_req_callback_data(subreq, |
2631 | 0 | struct tevent_req); |
2632 | 0 | struct rpccli_bh_raw_call_state *state = |
2633 | 0 | tevent_req_data(req, |
2634 | 0 | struct rpccli_bh_raw_call_state); |
2635 | 0 | NTSTATUS status; |
2636 | |
|
2637 | 0 | state->out_flags = 0; |
2638 | | |
2639 | | /* TODO: support bigendian responses */ |
2640 | |
|
2641 | 0 | status = rpc_api_pipe_req_recv(subreq, state, &state->out_data); |
2642 | 0 | TALLOC_FREE(subreq); |
2643 | 0 | if (tevent_req_nterror(req, status)) { |
2644 | 0 | return; |
2645 | 0 | } |
2646 | | |
2647 | 0 | tevent_req_done(req); |
2648 | 0 | } |
2649 | | |
2650 | | static NTSTATUS rpccli_bh_raw_call_recv(struct tevent_req *req, |
2651 | | TALLOC_CTX *mem_ctx, |
2652 | | uint8_t **out_data, |
2653 | | size_t *out_length, |
2654 | | uint32_t *out_flags) |
2655 | 0 | { |
2656 | 0 | struct rpccli_bh_raw_call_state *state = |
2657 | 0 | tevent_req_data(req, |
2658 | 0 | struct rpccli_bh_raw_call_state); |
2659 | 0 | NTSTATUS status; |
2660 | |
|
2661 | 0 | if (tevent_req_is_nterror(req, &status)) { |
2662 | 0 | tevent_req_received(req); |
2663 | 0 | return status; |
2664 | 0 | } |
2665 | | |
2666 | 0 | *out_data = talloc_move(mem_ctx, &state->out_data.data); |
2667 | 0 | *out_length = state->out_data.length; |
2668 | 0 | *out_flags = state->out_flags; |
2669 | 0 | tevent_req_received(req); |
2670 | 0 | return NT_STATUS_OK; |
2671 | 0 | } |
2672 | | |
2673 | | struct rpccli_bh_disconnect_state { |
2674 | | uint8_t _dummy; |
2675 | | }; |
2676 | | |
2677 | | static struct tevent_req *rpccli_bh_disconnect_send(TALLOC_CTX *mem_ctx, |
2678 | | struct tevent_context *ev, |
2679 | | struct dcerpc_binding_handle *h) |
2680 | 0 | { |
2681 | 0 | struct rpccli_bh_state *hs = dcerpc_binding_handle_data(h, |
2682 | 0 | struct rpccli_bh_state); |
2683 | 0 | struct tevent_req *req; |
2684 | 0 | struct rpccli_bh_disconnect_state *state; |
2685 | 0 | bool ok; |
2686 | |
|
2687 | 0 | req = tevent_req_create(mem_ctx, &state, |
2688 | 0 | struct rpccli_bh_disconnect_state); |
2689 | 0 | if (req == NULL) { |
2690 | 0 | return NULL; |
2691 | 0 | } |
2692 | | |
2693 | 0 | ok = rpccli_bh_is_connected(h); |
2694 | 0 | if (!ok) { |
2695 | 0 | tevent_req_nterror(req, NT_STATUS_CONNECTION_DISCONNECTED); |
2696 | 0 | return tevent_req_post(req, ev); |
2697 | 0 | } |
2698 | | |
2699 | | /* |
2700 | | * TODO: do a real async disconnect ... |
2701 | | * |
2702 | | * For now we do it sync... |
2703 | | */ |
2704 | 0 | TALLOC_FREE(hs->rpc_cli->conn); |
2705 | 0 | hs->rpc_cli = NULL; |
2706 | |
|
2707 | 0 | tevent_req_done(req); |
2708 | 0 | return tevent_req_post(req, ev); |
2709 | 0 | } |
2710 | | |
2711 | | static NTSTATUS rpccli_bh_disconnect_recv(struct tevent_req *req) |
2712 | 0 | { |
2713 | 0 | return tevent_req_simple_recv_ntstatus(req); |
2714 | 0 | } |
2715 | | |
2716 | | static bool rpccli_bh_ref_alloc(struct dcerpc_binding_handle *h) |
2717 | 0 | { |
2718 | 0 | return true; |
2719 | 0 | } |
2720 | | |
2721 | | static void rpccli_bh_do_ndr_print(struct dcerpc_binding_handle *h, |
2722 | | ndr_flags_type ndr_flags, |
2723 | | const void *_struct_ptr, |
2724 | | const struct ndr_interface_call *call) |
2725 | 0 | { |
2726 | 0 | void *struct_ptr = discard_const(_struct_ptr); |
2727 | |
|
2728 | 0 | if (!CHECK_DEBUGLVLC(DBGC_RPC_PARSE, 10)) { |
2729 | 0 | return; |
2730 | 0 | } |
2731 | | |
2732 | 0 | if (ndr_flags & NDR_IN) { |
2733 | 0 | ndr_print_function_debug(call->ndr_print, |
2734 | 0 | call->name, |
2735 | 0 | ndr_flags, |
2736 | 0 | struct_ptr); |
2737 | 0 | } |
2738 | 0 | if (ndr_flags & NDR_OUT) { |
2739 | 0 | ndr_print_function_debug(call->ndr_print, |
2740 | 0 | call->name, |
2741 | 0 | ndr_flags, |
2742 | 0 | struct_ptr); |
2743 | 0 | } |
2744 | 0 | } |
2745 | | |
2746 | | static const struct dcerpc_binding_handle_ops rpccli_bh_ops = { |
2747 | | .name = "rpccli", |
2748 | | .get_binding = rpccli_bh_get_binding, |
2749 | | .is_connected = rpccli_bh_is_connected, |
2750 | | .set_timeout = rpccli_bh_set_timeout, |
2751 | | .transport_session_key = rpccli_bh_transport_session_key, |
2752 | | .auth_info = rpccli_bh_auth_info, |
2753 | | .auth_session_key = rpccli_bh_auth_session_key, |
2754 | | .raw_call_send = rpccli_bh_raw_call_send, |
2755 | | .raw_call_recv = rpccli_bh_raw_call_recv, |
2756 | | .disconnect_send = rpccli_bh_disconnect_send, |
2757 | | .disconnect_recv = rpccli_bh_disconnect_recv, |
2758 | | |
2759 | | .ref_alloc = rpccli_bh_ref_alloc, |
2760 | | .do_ndr_print = rpccli_bh_do_ndr_print, |
2761 | | }; |
2762 | | |
2763 | | /* initialise a rpc_pipe_client binding handle */ |
2764 | | struct dcerpc_binding_handle *rpccli_bh_create(struct rpc_pipe_client *c, |
2765 | | const struct GUID *object, |
2766 | | const struct ndr_interface_table *table) |
2767 | 0 | { |
2768 | 0 | struct dcerpc_binding_handle *h = NULL; |
2769 | 0 | struct rpccli_bh_state *hs = NULL; |
2770 | 0 | struct dcerpc_binding *b = NULL; |
2771 | 0 | char localaddr[INET6_ADDRSTRLEN] = { 0, }; |
2772 | 0 | uint32_t a_flags = 0; |
2773 | 0 | uint32_t c_flags = 0; |
2774 | 0 | NTSTATUS status; |
2775 | |
|
2776 | 0 | if (c->conn->features.hdr_signing) { |
2777 | 0 | a_flags |= DCERPC_HEADER_SIGNING; |
2778 | 0 | } else { |
2779 | 0 | c_flags |= DCERPC_HEADER_SIGNING; |
2780 | 0 | } |
2781 | |
|
2782 | 0 | switch (c->auth->auth_type) { |
2783 | 0 | case DCERPC_AUTH_TYPE_KRB5: |
2784 | 0 | a_flags |= DCERPC_AUTH_KRB5; |
2785 | 0 | c_flags |= DCERPC_AUTH_NTLM; |
2786 | 0 | c_flags |= DCERPC_AUTH_SPNEGO; |
2787 | 0 | c_flags |= DCERPC_SCHANNEL; |
2788 | 0 | break; |
2789 | 0 | case DCERPC_AUTH_TYPE_NTLMSSP: |
2790 | 0 | c_flags |= DCERPC_AUTH_KRB5; |
2791 | 0 | a_flags |= DCERPC_AUTH_NTLM; |
2792 | 0 | c_flags |= DCERPC_AUTH_SPNEGO; |
2793 | 0 | c_flags |= DCERPC_SCHANNEL; |
2794 | 0 | break; |
2795 | 0 | case DCERPC_AUTH_TYPE_SPNEGO: |
2796 | 0 | c_flags |= DCERPC_AUTH_KRB5; |
2797 | 0 | c_flags |= DCERPC_AUTH_NTLM; |
2798 | 0 | a_flags |= DCERPC_AUTH_SPNEGO; |
2799 | 0 | c_flags |= DCERPC_SCHANNEL; |
2800 | 0 | break; |
2801 | 0 | case DCERPC_AUTH_TYPE_SCHANNEL: |
2802 | 0 | c_flags |= DCERPC_AUTH_KRB5; |
2803 | 0 | c_flags |= DCERPC_AUTH_NTLM; |
2804 | 0 | c_flags |= DCERPC_AUTH_SPNEGO; |
2805 | 0 | a_flags |= DCERPC_SCHANNEL; |
2806 | 0 | break; |
2807 | 0 | default: |
2808 | 0 | c_flags |= DCERPC_AUTH_KRB5; |
2809 | 0 | c_flags |= DCERPC_AUTH_NTLM; |
2810 | 0 | c_flags |= DCERPC_AUTH_SPNEGO; |
2811 | 0 | c_flags |= DCERPC_SCHANNEL; |
2812 | 0 | break; |
2813 | 0 | } |
2814 | | |
2815 | 0 | if (c->auth->auth_level >= DCERPC_AUTH_LEVEL_PRIVACY) { |
2816 | 0 | a_flags |= DCERPC_SEAL; |
2817 | 0 | c_flags |= DCERPC_SIGN; |
2818 | 0 | c_flags |= DCERPC_CONNECT; |
2819 | 0 | } else if (c->auth->auth_level >= DCERPC_AUTH_LEVEL_INTEGRITY) { |
2820 | 0 | c_flags |= DCERPC_SEAL; |
2821 | 0 | a_flags |= DCERPC_SIGN; |
2822 | 0 | c_flags |= DCERPC_CONNECT; |
2823 | 0 | } else if (c->auth->auth_level >= DCERPC_AUTH_LEVEL_CONNECT) { |
2824 | 0 | c_flags |= DCERPC_SEAL; |
2825 | 0 | c_flags |= DCERPC_SIGN; |
2826 | 0 | a_flags |= DCERPC_CONNECT; |
2827 | 0 | } else { |
2828 | 0 | c_flags |= DCERPC_SEAL; |
2829 | 0 | c_flags |= DCERPC_SIGN; |
2830 | 0 | c_flags |= DCERPC_CONNECT; |
2831 | 0 | } |
2832 | |
|
2833 | 0 | h = dcerpc_binding_handle_create(c, |
2834 | 0 | &rpccli_bh_ops, |
2835 | 0 | object, |
2836 | 0 | table, |
2837 | 0 | &hs, |
2838 | 0 | struct rpccli_bh_state, |
2839 | 0 | __location__); |
2840 | 0 | if (h == NULL) { |
2841 | 0 | return NULL; |
2842 | 0 | } |
2843 | 0 | hs->rpc_cli = c; |
2844 | |
|
2845 | 0 | b = dcerpc_binding_dup(h, c->assoc->binding); |
2846 | 0 | if (b == NULL) { |
2847 | 0 | TALLOC_FREE(h); |
2848 | 0 | return NULL; |
2849 | 0 | } |
2850 | 0 | status = dcerpc_binding_set_abstract_syntax(b, &table->syntax_id); |
2851 | 0 | if (!NT_STATUS_IS_OK(status)) { |
2852 | 0 | TALLOC_FREE(h); |
2853 | 0 | return NULL; |
2854 | 0 | } |
2855 | 0 | status = dcerpc_binding_set_flags(b, a_flags, c_flags); |
2856 | 0 | if (!NT_STATUS_IS_OK(status)) { |
2857 | 0 | TALLOC_FREE(h); |
2858 | 0 | return NULL; |
2859 | 0 | } |
2860 | | |
2861 | 0 | switch (c->conn->localaddr.u.sa.sa_family) { |
2862 | 0 | case AF_INET: |
2863 | 0 | case AF_INET6: |
2864 | 0 | print_sockaddr(localaddr, |
2865 | 0 | sizeof(localaddr), |
2866 | 0 | &c->conn->localaddr.u.ss); |
2867 | 0 | status = dcerpc_binding_set_string_option(b, |
2868 | 0 | "localaddress", |
2869 | 0 | localaddr); |
2870 | 0 | if (!NT_STATUS_IS_OK(status)) { |
2871 | 0 | TALLOC_FREE(h); |
2872 | 0 | return NULL; |
2873 | 0 | } |
2874 | 0 | break; |
2875 | 0 | } |
2876 | | |
2877 | 0 | hs->binding = b; |
2878 | |
|
2879 | 0 | return h; |
2880 | 0 | } |
2881 | | |
2882 | | NTSTATUS rpccli_anon_bind_data(TALLOC_CTX *mem_ctx, |
2883 | | struct pipe_auth_data **presult) |
2884 | 0 | { |
2885 | 0 | struct pipe_auth_data *result; |
2886 | 0 | struct auth_generic_state *auth_generic_ctx; |
2887 | 0 | NTSTATUS status; |
2888 | |
|
2889 | 0 | result = talloc_zero(mem_ctx, struct pipe_auth_data); |
2890 | 0 | if (result == NULL) { |
2891 | 0 | return NT_STATUS_NO_MEMORY; |
2892 | 0 | } |
2893 | | |
2894 | 0 | result->auth_type = DCERPC_AUTH_TYPE_NONE; |
2895 | 0 | result->auth_level = DCERPC_AUTH_LEVEL_NONE; |
2896 | | /* rpc_pipe_bind_send should allocate an id... */ |
2897 | 0 | result->auth_context_id = UINT32_MAX; |
2898 | |
|
2899 | 0 | status = auth_generic_client_prepare(result, |
2900 | 0 | &auth_generic_ctx); |
2901 | 0 | if (!NT_STATUS_IS_OK(status)) { |
2902 | 0 | DEBUG(1, ("Failed to create auth_generic context: %s\n", |
2903 | 0 | nt_errstr(status))); |
2904 | 0 | } |
2905 | |
|
2906 | 0 | status = auth_generic_set_username(auth_generic_ctx, ""); |
2907 | 0 | if (!NT_STATUS_IS_OK(status)) { |
2908 | 0 | DEBUG(1, ("Failed to set username: %s\n", |
2909 | 0 | nt_errstr(status))); |
2910 | 0 | } |
2911 | |
|
2912 | 0 | status = auth_generic_set_domain(auth_generic_ctx, ""); |
2913 | 0 | if (!NT_STATUS_IS_OK(status)) { |
2914 | 0 | DEBUG(1, ("Failed to set domain: %s\n", |
2915 | 0 | nt_errstr(status))); |
2916 | 0 | return status; |
2917 | 0 | } |
2918 | | |
2919 | 0 | status = gensec_set_credentials(auth_generic_ctx->gensec_security, |
2920 | 0 | auth_generic_ctx->credentials); |
2921 | 0 | if (!NT_STATUS_IS_OK(status)) { |
2922 | 0 | DEBUG(1, ("Failed to set GENSEC credentials: %s\n", |
2923 | 0 | nt_errstr(status))); |
2924 | 0 | return status; |
2925 | 0 | } |
2926 | 0 | talloc_unlink(auth_generic_ctx, auth_generic_ctx->credentials); |
2927 | 0 | auth_generic_ctx->credentials = NULL; |
2928 | |
|
2929 | 0 | result->auth_ctx = talloc_move(result, &auth_generic_ctx->gensec_security); |
2930 | 0 | talloc_free(auth_generic_ctx); |
2931 | 0 | *presult = result; |
2932 | 0 | return NT_STATUS_OK; |
2933 | 0 | } |
2934 | | |
2935 | | static NTSTATUS rpccli_generic_bind_data(TALLOC_CTX *mem_ctx, |
2936 | | enum dcerpc_AuthType auth_type, |
2937 | | enum dcerpc_AuthLevel auth_level, |
2938 | | const char *server, |
2939 | | const char *target_service, |
2940 | | const char *domain, |
2941 | | const char *username, |
2942 | | const char *password, |
2943 | | enum credentials_use_kerberos use_kerberos, |
2944 | | struct netlogon_creds_CredentialState *creds, |
2945 | | struct pipe_auth_data **presult) |
2946 | 0 | { |
2947 | 0 | struct auth_generic_state *auth_generic_ctx; |
2948 | 0 | struct pipe_auth_data *result; |
2949 | 0 | NTSTATUS status; |
2950 | |
|
2951 | 0 | result = talloc_zero(mem_ctx, struct pipe_auth_data); |
2952 | 0 | if (result == NULL) { |
2953 | 0 | return NT_STATUS_NO_MEMORY; |
2954 | 0 | } |
2955 | | |
2956 | 0 | result->auth_type = auth_type; |
2957 | 0 | result->auth_level = auth_level; |
2958 | | /* rpc_pipe_bind_send should allocate an id... */ |
2959 | 0 | result->auth_context_id = UINT32_MAX; |
2960 | |
|
2961 | 0 | status = auth_generic_client_prepare(result, |
2962 | 0 | &auth_generic_ctx); |
2963 | 0 | if (!NT_STATUS_IS_OK(status)) { |
2964 | 0 | goto fail; |
2965 | 0 | } |
2966 | | |
2967 | 0 | status = auth_generic_set_username(auth_generic_ctx, username); |
2968 | 0 | if (!NT_STATUS_IS_OK(status)) { |
2969 | 0 | goto fail; |
2970 | 0 | } |
2971 | | |
2972 | 0 | status = auth_generic_set_domain(auth_generic_ctx, domain); |
2973 | 0 | if (!NT_STATUS_IS_OK(status)) { |
2974 | 0 | goto fail; |
2975 | 0 | } |
2976 | | |
2977 | 0 | status = auth_generic_set_password(auth_generic_ctx, password); |
2978 | 0 | if (!NT_STATUS_IS_OK(status)) { |
2979 | 0 | goto fail; |
2980 | 0 | } |
2981 | | |
2982 | 0 | status = gensec_set_target_service(auth_generic_ctx->gensec_security, target_service); |
2983 | 0 | if (!NT_STATUS_IS_OK(status)) { |
2984 | 0 | goto fail; |
2985 | 0 | } |
2986 | | |
2987 | 0 | status = gensec_set_target_hostname(auth_generic_ctx->gensec_security, server); |
2988 | 0 | if (!NT_STATUS_IS_OK(status)) { |
2989 | 0 | goto fail; |
2990 | 0 | } |
2991 | | |
2992 | 0 | cli_credentials_set_kerberos_state(auth_generic_ctx->credentials, |
2993 | 0 | use_kerberos, |
2994 | 0 | CRED_SPECIFIED); |
2995 | 0 | cli_credentials_set_netlogon_creds(auth_generic_ctx->credentials, creds); |
2996 | |
|
2997 | 0 | status = auth_generic_client_start_by_authtype(auth_generic_ctx, auth_type, auth_level); |
2998 | 0 | if (!NT_STATUS_IS_OK(status)) { |
2999 | 0 | goto fail; |
3000 | 0 | } |
3001 | | |
3002 | 0 | result->auth_ctx = talloc_move(result, &auth_generic_ctx->gensec_security); |
3003 | 0 | talloc_free(auth_generic_ctx); |
3004 | 0 | *presult = result; |
3005 | 0 | return NT_STATUS_OK; |
3006 | | |
3007 | 0 | fail: |
3008 | 0 | TALLOC_FREE(result); |
3009 | 0 | return status; |
3010 | 0 | } |
3011 | | |
3012 | | /* This routine steals the creds pointer that is passed in */ |
3013 | | static NTSTATUS rpccli_generic_bind_data_from_creds(TALLOC_CTX *mem_ctx, |
3014 | | enum dcerpc_AuthType auth_type, |
3015 | | enum dcerpc_AuthLevel auth_level, |
3016 | | const char *server, |
3017 | | const char *target_service, |
3018 | | struct cli_credentials *creds, |
3019 | | struct pipe_auth_data **presult) |
3020 | 0 | { |
3021 | 0 | struct auth_generic_state *auth_generic_ctx; |
3022 | 0 | struct pipe_auth_data *result; |
3023 | 0 | NTSTATUS status; |
3024 | |
|
3025 | 0 | result = talloc_zero(mem_ctx, struct pipe_auth_data); |
3026 | 0 | if (result == NULL) { |
3027 | 0 | return NT_STATUS_NO_MEMORY; |
3028 | 0 | } |
3029 | | |
3030 | 0 | result->auth_type = auth_type; |
3031 | 0 | result->auth_level = auth_level; |
3032 | | /* rpc_pipe_bind_send should allocate an id... */ |
3033 | 0 | result->auth_context_id = UINT32_MAX; |
3034 | |
|
3035 | 0 | status = auth_generic_client_prepare(result, |
3036 | 0 | &auth_generic_ctx); |
3037 | 0 | if (!NT_STATUS_IS_OK(status)) { |
3038 | 0 | goto fail; |
3039 | 0 | } |
3040 | | |
3041 | 0 | status = auth_generic_set_creds(auth_generic_ctx, creds); |
3042 | 0 | if (!NT_STATUS_IS_OK(status)) { |
3043 | 0 | goto fail; |
3044 | 0 | } |
3045 | | |
3046 | 0 | status = gensec_set_target_service(auth_generic_ctx->gensec_security, target_service); |
3047 | 0 | if (!NT_STATUS_IS_OK(status)) { |
3048 | 0 | goto fail; |
3049 | 0 | } |
3050 | | |
3051 | 0 | status = gensec_set_target_hostname(auth_generic_ctx->gensec_security, server); |
3052 | 0 | if (!NT_STATUS_IS_OK(status)) { |
3053 | 0 | goto fail; |
3054 | 0 | } |
3055 | | |
3056 | 0 | status = auth_generic_client_start_by_authtype(auth_generic_ctx, auth_type, auth_level); |
3057 | 0 | if (!NT_STATUS_IS_OK(status)) { |
3058 | 0 | goto fail; |
3059 | 0 | } |
3060 | | |
3061 | 0 | result->auth_ctx = talloc_move(result, &auth_generic_ctx->gensec_security); |
3062 | 0 | talloc_free(auth_generic_ctx); |
3063 | 0 | *presult = result; |
3064 | 0 | return NT_STATUS_OK; |
3065 | | |
3066 | 0 | fail: |
3067 | 0 | TALLOC_FREE(result); |
3068 | 0 | return status; |
3069 | 0 | } |
3070 | | |
3071 | | NTSTATUS rpccli_ncalrpc_bind_data(TALLOC_CTX *mem_ctx, |
3072 | | struct pipe_auth_data **presult) |
3073 | 0 | { |
3074 | 0 | return rpccli_generic_bind_data(mem_ctx, |
3075 | 0 | DCERPC_AUTH_TYPE_NCALRPC_AS_SYSTEM, |
3076 | 0 | DCERPC_AUTH_LEVEL_CONNECT, |
3077 | 0 | NULL, /* server */ |
3078 | 0 | "host", /* target_service */ |
3079 | 0 | NAME_NT_AUTHORITY, /* domain */ |
3080 | 0 | "SYSTEM", |
3081 | 0 | NULL, /* password */ |
3082 | 0 | CRED_USE_KERBEROS_DISABLED, |
3083 | 0 | NULL, /* netlogon_creds_CredentialState */ |
3084 | 0 | presult); |
3085 | 0 | } |
3086 | | |
3087 | | /** |
3088 | | * Create an rpc pipe client struct, connecting to a tcp port. |
3089 | | */ |
3090 | | static NTSTATUS rpc_pipe_open_tcp_port(TALLOC_CTX *mem_ctx, |
3091 | | const struct rpc_client_association *assoc, |
3092 | | struct rpc_client_connection **pconn) |
3093 | 0 | { |
3094 | 0 | struct rpc_client_connection *conn = NULL; |
3095 | 0 | enum dcerpc_transport_t transport; |
3096 | 0 | const char *endpoint = NULL; |
3097 | 0 | uint16_t port; |
3098 | 0 | NTSTATUS status; |
3099 | 0 | int fd; |
3100 | 0 | int ret; |
3101 | |
|
3102 | 0 | transport = dcerpc_binding_get_transport(assoc->binding); |
3103 | 0 | if (transport != NCACN_IP_TCP) { |
3104 | 0 | return NT_STATUS_RPC_WRONG_KIND_OF_BINDING; |
3105 | 0 | } |
3106 | | |
3107 | 0 | endpoint = dcerpc_binding_get_string_option(assoc->binding, |
3108 | 0 | "endpoint"); |
3109 | 0 | if (endpoint == NULL) { |
3110 | 0 | return NT_STATUS_RPC_INVALID_ENDPOINT_FORMAT; |
3111 | 0 | } |
3112 | 0 | port = (uint16_t)atoi(endpoint); |
3113 | 0 | if (port == 0) { |
3114 | 0 | return NT_STATUS_RPC_INVALID_ENDPOINT_FORMAT; |
3115 | 0 | } |
3116 | | |
3117 | 0 | status = rpc_client_connection_create(mem_ctx, |
3118 | 0 | assoc, |
3119 | 0 | DCERPC_FRAG_MAX_SIZE, |
3120 | 0 | &conn); |
3121 | 0 | if (!NT_STATUS_IS_OK(status)) { |
3122 | 0 | return status; |
3123 | 0 | } |
3124 | | |
3125 | 0 | status = open_socket_out(&assoc->addr.u.ss, port, 60*1000, &fd); |
3126 | 0 | if (!NT_STATUS_IS_OK(status)) { |
3127 | 0 | TALLOC_FREE(conn); |
3128 | 0 | return status; |
3129 | 0 | } |
3130 | 0 | set_socket_options(fd, lp_socket_options()); |
3131 | |
|
3132 | 0 | conn->localaddr.sa_socklen = sizeof(conn->localaddr.u.ss); |
3133 | 0 | ret = getsockname(fd, &conn->localaddr.u.sa, &conn->localaddr.sa_socklen); |
3134 | 0 | if (ret != 0) { |
3135 | 0 | status = map_nt_error_from_unix_common(errno); |
3136 | 0 | close(fd); |
3137 | 0 | TALLOC_FREE(conn); |
3138 | 0 | return status; |
3139 | 0 | } |
3140 | | |
3141 | 0 | status = rpc_transport_sock_init(conn, fd, &conn->transport); |
3142 | 0 | if (!NT_STATUS_IS_OK(status)) { |
3143 | 0 | close(fd); |
3144 | 0 | TALLOC_FREE(conn); |
3145 | 0 | return status; |
3146 | 0 | } |
3147 | | |
3148 | 0 | conn->transport->transport = NCACN_IP_TCP; |
3149 | |
|
3150 | 0 | *pconn = conn; |
3151 | 0 | return NT_STATUS_OK; |
3152 | 0 | } |
3153 | | |
3154 | | static NTSTATUS rpccli_epm_map_binding( |
3155 | | struct dcerpc_binding_handle *epm_connection, |
3156 | | struct dcerpc_binding *binding, |
3157 | | TALLOC_CTX *mem_ctx, |
3158 | | char **pendpoint) |
3159 | 0 | { |
3160 | 0 | TALLOC_CTX *frame = talloc_stackframe(); |
3161 | 0 | enum dcerpc_transport_t transport = |
3162 | 0 | dcerpc_binding_get_transport(binding); |
3163 | 0 | enum dcerpc_transport_t res_transport; |
3164 | 0 | struct dcerpc_binding *res_binding = NULL; |
3165 | 0 | struct epm_twr_t *map_tower = NULL; |
3166 | 0 | struct epm_twr_p_t res_towers = { .twr = NULL }; |
3167 | 0 | struct policy_handle *entry_handle = NULL; |
3168 | 0 | uint32_t num_towers = 0; |
3169 | 0 | const uint32_t max_towers = 1; |
3170 | 0 | const char *endpoint = NULL; |
3171 | 0 | char *tmp = NULL; |
3172 | 0 | uint32_t result; |
3173 | 0 | NTSTATUS status; |
3174 | |
|
3175 | 0 | map_tower = talloc_zero(frame, struct epm_twr_t); |
3176 | 0 | if (map_tower == NULL) { |
3177 | 0 | goto nomem; |
3178 | 0 | } |
3179 | | |
3180 | 0 | status = dcerpc_binding_build_tower( |
3181 | 0 | frame, binding, &(map_tower->tower)); |
3182 | 0 | if (!NT_STATUS_IS_OK(status)) { |
3183 | 0 | DBG_DEBUG("dcerpc_binding_build_tower failed: %s\n", |
3184 | 0 | nt_errstr(status)); |
3185 | 0 | goto done; |
3186 | 0 | } |
3187 | | |
3188 | 0 | res_towers.twr = talloc_array(frame, struct epm_twr_t, max_towers); |
3189 | 0 | if (res_towers.twr == NULL) { |
3190 | 0 | goto nomem; |
3191 | 0 | } |
3192 | | |
3193 | 0 | entry_handle = talloc_zero(frame, struct policy_handle); |
3194 | 0 | if (entry_handle == NULL) { |
3195 | 0 | goto nomem; |
3196 | 0 | } |
3197 | | |
3198 | 0 | status = dcerpc_epm_Map( |
3199 | 0 | epm_connection, |
3200 | 0 | frame, |
3201 | 0 | NULL, |
3202 | 0 | map_tower, |
3203 | 0 | entry_handle, |
3204 | 0 | max_towers, |
3205 | 0 | &num_towers, |
3206 | 0 | &res_towers, |
3207 | 0 | &result); |
3208 | |
|
3209 | 0 | if (!NT_STATUS_IS_OK(status)) { |
3210 | 0 | DBG_DEBUG("dcerpc_epm_Map failed: %s\n", nt_errstr(status)); |
3211 | 0 | goto done; |
3212 | 0 | } |
3213 | | |
3214 | 0 | if (result != EPMAPPER_STATUS_OK) { |
3215 | 0 | DBG_DEBUG("dcerpc_epm_Map returned %"PRIu32"\n", result); |
3216 | 0 | status = NT_STATUS_NOT_FOUND; |
3217 | 0 | goto done; |
3218 | 0 | } |
3219 | | |
3220 | 0 | if (num_towers != 1) { |
3221 | 0 | DBG_DEBUG("dcerpc_epm_Map returned %"PRIu32" towers\n", |
3222 | 0 | num_towers); |
3223 | 0 | status = NT_STATUS_INVALID_NETWORK_RESPONSE; |
3224 | 0 | goto done; |
3225 | 0 | } |
3226 | | |
3227 | 0 | status = dcerpc_binding_from_tower( |
3228 | 0 | frame, &(res_towers.twr->tower), &res_binding); |
3229 | 0 | if (!NT_STATUS_IS_OK(status)) { |
3230 | 0 | DBG_DEBUG("dcerpc_binding_from_tower failed: %s\n", |
3231 | 0 | nt_errstr(status)); |
3232 | 0 | goto done; |
3233 | 0 | } |
3234 | | |
3235 | 0 | res_transport = dcerpc_binding_get_transport(res_binding); |
3236 | 0 | if (res_transport != transport) { |
3237 | 0 | DBG_DEBUG("dcerpc_epm_Map returned transport %d, " |
3238 | 0 | "expected %d\n", |
3239 | 0 | (int)res_transport, |
3240 | 0 | (int)transport); |
3241 | 0 | status = NT_STATUS_INVALID_NETWORK_RESPONSE; |
3242 | 0 | goto done; |
3243 | 0 | } |
3244 | | |
3245 | 0 | endpoint = dcerpc_binding_get_string_option(res_binding, "endpoint"); |
3246 | 0 | if (endpoint == NULL) { |
3247 | 0 | DBG_DEBUG("dcerpc_epm_Map returned no endpoint\n"); |
3248 | 0 | status = NT_STATUS_INVALID_NETWORK_RESPONSE; |
3249 | 0 | goto done; |
3250 | 0 | } |
3251 | | |
3252 | 0 | tmp = talloc_strdup(mem_ctx, endpoint); |
3253 | 0 | if (tmp == NULL) { |
3254 | 0 | goto nomem; |
3255 | 0 | } |
3256 | 0 | *pendpoint = tmp; |
3257 | |
|
3258 | 0 | status = NT_STATUS_OK; |
3259 | 0 | goto done; |
3260 | | |
3261 | 0 | nomem: |
3262 | 0 | status = NT_STATUS_NO_MEMORY; |
3263 | 0 | done: |
3264 | 0 | TALLOC_FREE(frame); |
3265 | 0 | return status; |
3266 | 0 | } |
3267 | | |
3268 | | static NTSTATUS rpccli_epm_map_interface( |
3269 | | struct dcerpc_binding_handle *epm_connection, |
3270 | | enum dcerpc_transport_t transport, |
3271 | | const struct ndr_syntax_id *iface, |
3272 | | TALLOC_CTX *mem_ctx, |
3273 | | char **pendpoint) |
3274 | 0 | { |
3275 | 0 | struct dcerpc_binding *binding = NULL; |
3276 | 0 | char *endpoint = NULL; |
3277 | 0 | NTSTATUS status; |
3278 | |
|
3279 | 0 | status = dcerpc_parse_binding(mem_ctx, "", &binding); |
3280 | 0 | if (!NT_STATUS_IS_OK(status)) { |
3281 | 0 | DBG_DEBUG("dcerpc_parse_binding failed: %s\n", |
3282 | 0 | nt_errstr(status)); |
3283 | 0 | goto done; |
3284 | 0 | } |
3285 | | |
3286 | 0 | status = dcerpc_binding_set_transport(binding, transport); |
3287 | 0 | if (!NT_STATUS_IS_OK(status)) { |
3288 | 0 | DBG_DEBUG("dcerpc_binding_set_transport failed: %s\n", |
3289 | 0 | nt_errstr(status)); |
3290 | 0 | goto done; |
3291 | 0 | } |
3292 | | |
3293 | 0 | status = dcerpc_binding_set_abstract_syntax(binding, iface); |
3294 | 0 | if (!NT_STATUS_IS_OK(status)) { |
3295 | 0 | DBG_DEBUG("dcerpc_binding_set_abstract_syntax failed: %s\n", |
3296 | 0 | nt_errstr(status)); |
3297 | 0 | goto done; |
3298 | 0 | } |
3299 | | |
3300 | 0 | status = rpccli_epm_map_binding( |
3301 | 0 | epm_connection, binding, mem_ctx, &endpoint); |
3302 | 0 | if (!NT_STATUS_IS_OK(status)) { |
3303 | 0 | DBG_DEBUG("rpccli_epm_map_binding failed: %s\n", |
3304 | 0 | nt_errstr(status)); |
3305 | 0 | goto done; |
3306 | 0 | } |
3307 | 0 | *pendpoint = endpoint; |
3308 | |
|
3309 | 0 | done: |
3310 | 0 | TALLOC_FREE(binding); |
3311 | 0 | return status; |
3312 | 0 | } |
3313 | | |
3314 | | /** |
3315 | | * Determine the tcp port on which a dcerpc interface is listening |
3316 | | * for the ncacn_ip_tcp transport via the endpoint mapper of the |
3317 | | * target host. |
3318 | | */ |
3319 | | static NTSTATUS rpc_pipe_get_tcp_port(const char *host, |
3320 | | const struct samba_sockaddr *saddr, |
3321 | | const struct ndr_interface_table *table, |
3322 | | uint16_t *pport) |
3323 | 0 | { |
3324 | 0 | TALLOC_CTX *frame = talloc_stackframe(); |
3325 | 0 | const char *epm_ep = NULL; |
3326 | 0 | struct rpc_client_association *epm_assoc = NULL; |
3327 | 0 | struct rpc_client_connection *epm_conn = NULL; |
3328 | 0 | struct rpc_pipe_client *epm_pipe = NULL; |
3329 | 0 | struct pipe_auth_data *epm_auth = NULL; |
3330 | 0 | char *endpoint = NULL; |
3331 | 0 | uint16_t port = 0; |
3332 | 0 | NTSTATUS status; |
3333 | |
|
3334 | 0 | endpoint = dcerpc_default_transport_endpoint(frame, |
3335 | 0 | NCACN_IP_TCP, |
3336 | 0 | table); |
3337 | 0 | if (endpoint != NULL) { |
3338 | 0 | port = (uint16_t)atoi(endpoint); |
3339 | 0 | } |
3340 | |
|
3341 | 0 | if (port != 0) { |
3342 | 0 | *pport = port; |
3343 | 0 | TALLOC_FREE(frame); |
3344 | 0 | return NT_STATUS_OK; |
3345 | 0 | } |
3346 | | |
3347 | 0 | epm_ep = dcerpc_default_transport_endpoint(frame, |
3348 | 0 | NCACN_IP_TCP, |
3349 | 0 | &ndr_table_epmapper); |
3350 | 0 | if (epm_ep == NULL) { |
3351 | 0 | TALLOC_FREE(frame); |
3352 | 0 | return NT_STATUS_RPC_INTERNAL_ERROR; |
3353 | 0 | } |
3354 | | |
3355 | 0 | status = rpc_client_association_create(frame, |
3356 | 0 | default_bt_features, |
3357 | 0 | DCERPC_PROPOSE_HEADER_SIGNING, |
3358 | 0 | host, |
3359 | 0 | NCACN_IP_TCP, |
3360 | 0 | saddr, |
3361 | 0 | epm_ep, |
3362 | 0 | &epm_assoc); |
3363 | 0 | if (!NT_STATUS_IS_OK(status)) { |
3364 | 0 | TALLOC_FREE(frame); |
3365 | 0 | return status; |
3366 | 0 | } |
3367 | | |
3368 | | /* open the connection to the endpoint mapper */ |
3369 | 0 | status = rpc_pipe_open_tcp_port(frame, epm_assoc, &epm_conn); |
3370 | 0 | if (!NT_STATUS_IS_OK(status)) { |
3371 | 0 | TALLOC_FREE(frame); |
3372 | 0 | return status; |
3373 | 0 | } |
3374 | | |
3375 | 0 | status = rpccli_anon_bind_data(frame, &epm_auth); |
3376 | 0 | if (!NT_STATUS_IS_OK(status)) { |
3377 | 0 | TALLOC_FREE(frame); |
3378 | 0 | return status; |
3379 | 0 | } |
3380 | | |
3381 | 0 | status = rpc_pipe_wrap_create(&ndr_table_epmapper, |
3382 | 0 | NULL, |
3383 | 0 | &epm_assoc, |
3384 | 0 | &epm_conn, |
3385 | 0 | frame, |
3386 | 0 | &epm_pipe); |
3387 | 0 | if (!NT_STATUS_IS_OK(status)) { |
3388 | 0 | TALLOC_FREE(frame); |
3389 | 0 | return status; |
3390 | 0 | } |
3391 | | |
3392 | 0 | status = rpc_pipe_bind(epm_pipe, epm_auth); |
3393 | 0 | if (!NT_STATUS_IS_OK(status)) { |
3394 | 0 | TALLOC_FREE(frame); |
3395 | 0 | return status; |
3396 | 0 | } |
3397 | | |
3398 | 0 | status = rpccli_epm_map_interface( |
3399 | 0 | epm_pipe->binding_handle, |
3400 | 0 | NCACN_IP_TCP, |
3401 | 0 | &table->syntax_id, |
3402 | 0 | frame, |
3403 | 0 | &endpoint); |
3404 | 0 | if (!NT_STATUS_IS_OK(status)) { |
3405 | 0 | DBG_DEBUG("rpccli_epm_map_interface failed: %s\n", |
3406 | 0 | nt_errstr(status)); |
3407 | 0 | TALLOC_FREE(frame); |
3408 | 0 | return status; |
3409 | 0 | } |
3410 | | |
3411 | 0 | *pport = (uint16_t)atoi(endpoint); |
3412 | 0 | TALLOC_FREE(frame); |
3413 | 0 | return NT_STATUS_OK; |
3414 | 0 | } |
3415 | | |
3416 | | static NTSTATUS rpc_pipe_get_ncalrpc_name( |
3417 | | const struct ndr_syntax_id *iface, |
3418 | | TALLOC_CTX *mem_ctx, |
3419 | | char **psocket_name) |
3420 | 0 | { |
3421 | 0 | TALLOC_CTX *frame = talloc_stackframe(); |
3422 | 0 | struct rpc_pipe_client *epm_pipe = NULL; |
3423 | 0 | struct pipe_auth_data *auth = NULL; |
3424 | 0 | NTSTATUS status = NT_STATUS_OK; |
3425 | 0 | bool is_epm; |
3426 | |
|
3427 | 0 | is_epm = ndr_syntax_id_equal(iface, &ndr_table_epmapper.syntax_id); |
3428 | 0 | if (is_epm) { |
3429 | 0 | char *endpoint = talloc_strdup(mem_ctx, "EPMAPPER"); |
3430 | 0 | if (endpoint == NULL) { |
3431 | 0 | status = NT_STATUS_NO_MEMORY; |
3432 | 0 | goto done; |
3433 | 0 | } |
3434 | 0 | *psocket_name = endpoint; |
3435 | 0 | goto done; |
3436 | 0 | } |
3437 | | |
3438 | 0 | status = rpc_pipe_open_ncalrpc( |
3439 | 0 | frame, &ndr_table_epmapper, &epm_pipe); |
3440 | 0 | if (!NT_STATUS_IS_OK(status)) { |
3441 | 0 | DBG_DEBUG("rpc_pipe_open_ncalrpc failed: %s\n", |
3442 | 0 | nt_errstr(status)); |
3443 | 0 | goto done; |
3444 | 0 | } |
3445 | | |
3446 | 0 | status = rpccli_anon_bind_data(epm_pipe, &auth); |
3447 | 0 | if (!NT_STATUS_IS_OK(status)) { |
3448 | 0 | DBG_DEBUG("rpccli_anon_bind_data failed: %s\n", |
3449 | 0 | nt_errstr(status)); |
3450 | 0 | goto done; |
3451 | 0 | } |
3452 | | |
3453 | 0 | status = rpc_pipe_bind(epm_pipe, auth); |
3454 | 0 | if (!NT_STATUS_IS_OK(status)) { |
3455 | 0 | DBG_DEBUG("rpc_pipe_bind failed: %s\n", nt_errstr(status)); |
3456 | 0 | goto done; |
3457 | 0 | } |
3458 | | |
3459 | 0 | status = rpccli_epm_map_interface( |
3460 | 0 | epm_pipe->binding_handle, |
3461 | 0 | NCALRPC, |
3462 | 0 | iface, |
3463 | 0 | mem_ctx, |
3464 | 0 | psocket_name); |
3465 | 0 | if (!NT_STATUS_IS_OK(status)) { |
3466 | 0 | DBG_DEBUG("rpccli_epm_map_interface failed: %s\n", |
3467 | 0 | nt_errstr(status)); |
3468 | 0 | } |
3469 | |
|
3470 | 0 | done: |
3471 | 0 | TALLOC_FREE(frame); |
3472 | 0 | return status; |
3473 | 0 | } |
3474 | | |
3475 | | /******************************************************************** |
3476 | | Create a rpc pipe client struct, connecting to a unix domain socket |
3477 | | ********************************************************************/ |
3478 | | NTSTATUS rpc_pipe_open_ncalrpc(TALLOC_CTX *mem_ctx, |
3479 | | const struct ndr_interface_table *table, |
3480 | | struct rpc_pipe_client **presult) |
3481 | 0 | { |
3482 | 0 | TALLOC_CTX *frame = talloc_stackframe(); |
3483 | 0 | char *myname = NULL; |
3484 | 0 | char *socket_name = NULL; |
3485 | 0 | struct samba_sockaddr saddr = { |
3486 | 0 | .sa_socklen = sizeof(struct sockaddr_un), |
3487 | 0 | .u = { |
3488 | 0 | .un = { |
3489 | 0 | .sun_family = AF_UNIX, |
3490 | 0 | }, |
3491 | 0 | }, |
3492 | 0 | }; |
3493 | 0 | struct rpc_client_association *assoc = NULL; |
3494 | 0 | struct rpc_client_connection *conn = NULL; |
3495 | 0 | struct rpc_pipe_client *result = NULL; |
3496 | 0 | int pathlen; |
3497 | 0 | NTSTATUS status; |
3498 | 0 | int fd = -1; |
3499 | |
|
3500 | 0 | myname = get_myname(frame); |
3501 | 0 | if (myname == NULL) { |
3502 | 0 | TALLOC_FREE(frame); |
3503 | 0 | return NT_STATUS_NO_MEMORY; |
3504 | 0 | } |
3505 | | |
3506 | 0 | status = rpc_pipe_get_ncalrpc_name(&table->syntax_id, |
3507 | 0 | frame, |
3508 | 0 | &socket_name); |
3509 | 0 | if (!NT_STATUS_IS_OK(status)) { |
3510 | 0 | DBG_DEBUG("rpc_pipe_get_ncalrpc_name failed: %s\n", |
3511 | 0 | nt_errstr(status)); |
3512 | 0 | TALLOC_FREE(frame); |
3513 | 0 | return status; |
3514 | 0 | } |
3515 | | |
3516 | 0 | pathlen = snprintf( |
3517 | 0 | saddr.u.un.sun_path, |
3518 | 0 | sizeof(saddr.u.un.sun_path), |
3519 | 0 | "%s/%s", |
3520 | 0 | lp_ncalrpc_dir(), |
3521 | 0 | socket_name); |
3522 | 0 | if ((pathlen < 0) || ((size_t)pathlen >= sizeof(saddr.u.un.sun_path))) { |
3523 | 0 | DBG_DEBUG("socket_path for %s too long\n", socket_name); |
3524 | 0 | TALLOC_FREE(frame); |
3525 | 0 | return NT_STATUS_NAME_TOO_LONG; |
3526 | 0 | } |
3527 | 0 | TALLOC_FREE(socket_name); |
3528 | |
|
3529 | 0 | status = rpc_client_association_create(mem_ctx, |
3530 | 0 | 0, /* no client_features */ |
3531 | 0 | 0, /* flags */ |
3532 | 0 | myname, |
3533 | 0 | NCALRPC, |
3534 | 0 | &saddr, |
3535 | 0 | socket_name, |
3536 | 0 | &assoc); |
3537 | 0 | if (!NT_STATUS_IS_OK(status)) { |
3538 | 0 | TALLOC_FREE(frame); |
3539 | 0 | return status; |
3540 | 0 | } |
3541 | 0 | talloc_steal(frame, assoc); |
3542 | |
|
3543 | 0 | status = rpc_client_connection_create(mem_ctx, |
3544 | 0 | assoc, |
3545 | 0 | DCERPC_FRAG_MAX_SIZE, |
3546 | 0 | &conn); |
3547 | 0 | if (!NT_STATUS_IS_OK(status)) { |
3548 | 0 | TALLOC_FREE(frame); |
3549 | 0 | return status; |
3550 | 0 | } |
3551 | 0 | talloc_steal(frame, conn); |
3552 | |
|
3553 | 0 | fd = socket(AF_UNIX, SOCK_STREAM, 0); |
3554 | 0 | if (fd == -1) { |
3555 | 0 | status = map_nt_error_from_unix(errno); |
3556 | 0 | TALLOC_FREE(frame); |
3557 | 0 | return status; |
3558 | 0 | } |
3559 | | |
3560 | 0 | if (connect(fd, &saddr.u.sa, saddr.sa_socklen) == -1) { |
3561 | 0 | status = map_nt_error_from_unix(errno); |
3562 | 0 | close(fd); |
3563 | 0 | DBG_WARNING("connect(%s) failed: %s - %s\n", |
3564 | 0 | saddr.u.un.sun_path, |
3565 | 0 | strerror(errno), nt_errstr(status)); |
3566 | 0 | TALLOC_FREE(frame); |
3567 | 0 | return status; |
3568 | 0 | } |
3569 | | |
3570 | 0 | status = rpc_transport_sock_init(conn, fd, &conn->transport); |
3571 | 0 | if (!NT_STATUS_IS_OK(status)) { |
3572 | 0 | close(fd); |
3573 | 0 | TALLOC_FREE(frame); |
3574 | 0 | return status; |
3575 | 0 | } |
3576 | 0 | fd = -1; |
3577 | |
|
3578 | 0 | conn->transport->transport = NCALRPC; |
3579 | |
|
3580 | 0 | status = rpc_pipe_wrap_create(table, |
3581 | 0 | NULL, |
3582 | 0 | &assoc, |
3583 | 0 | &conn, |
3584 | 0 | mem_ctx, |
3585 | 0 | &result); |
3586 | 0 | if (!NT_STATUS_IS_OK(status)) { |
3587 | 0 | TALLOC_FREE(frame); |
3588 | 0 | return status; |
3589 | 0 | } |
3590 | | |
3591 | 0 | *presult = result; |
3592 | 0 | TALLOC_FREE(frame); |
3593 | 0 | return NT_STATUS_OK; |
3594 | 0 | } |
3595 | | |
3596 | | NTSTATUS rpc_pipe_open_local_np( |
3597 | | TALLOC_CTX *mem_ctx, |
3598 | | const struct ndr_interface_table *table, |
3599 | | const char *remote_client_name, |
3600 | | const struct tsocket_address *remote_client_addr, |
3601 | | const char *local_server_name, |
3602 | | const struct tsocket_address *local_server_addr, |
3603 | | const struct auth_session_info *session_info, |
3604 | | struct rpc_pipe_client **presult) |
3605 | 0 | { |
3606 | 0 | TALLOC_CTX *frame = talloc_stackframe(); |
3607 | 0 | struct rpc_client_association *assoc = NULL; |
3608 | 0 | struct rpc_client_connection *conn = NULL; |
3609 | 0 | struct rpc_pipe_client *result = NULL; |
3610 | 0 | struct pipe_auth_data *auth = NULL; |
3611 | 0 | struct samba_sockaddr saddr = { .sa_socklen = 0, }; |
3612 | 0 | const char *pipe_name = NULL; |
3613 | 0 | struct tstream_context *npa_stream = NULL; |
3614 | 0 | NTSTATUS status = NT_STATUS_NO_MEMORY; |
3615 | 0 | int ret; |
3616 | |
|
3617 | 0 | pipe_name = dcerpc_default_transport_endpoint(frame, |
3618 | 0 | NCACN_NP, |
3619 | 0 | table); |
3620 | 0 | if (pipe_name == NULL) { |
3621 | 0 | DBG_DEBUG("dcerpc_default_transport_endpoint failed\n"); |
3622 | 0 | TALLOC_FREE(frame); |
3623 | 0 | return NT_STATUS_OBJECT_NAME_NOT_FOUND; |
3624 | 0 | } |
3625 | | |
3626 | 0 | if (local_server_name == NULL) { |
3627 | 0 | local_server_name = get_myname(result); |
3628 | 0 | } |
3629 | |
|
3630 | 0 | if (local_server_addr != NULL) { |
3631 | 0 | saddr.sa_socklen = tsocket_address_bsd_sockaddr(local_server_addr, |
3632 | 0 | &saddr.u.sa, |
3633 | 0 | sizeof(saddr.u.ss)); |
3634 | 0 | if (saddr.sa_socklen == -1) { |
3635 | 0 | status = map_nt_error_from_unix(errno); |
3636 | 0 | TALLOC_FREE(frame); |
3637 | 0 | return status; |
3638 | 0 | } |
3639 | 0 | } |
3640 | | |
3641 | 0 | status = rpc_client_association_create(mem_ctx, |
3642 | 0 | 0, /* no client_features */ |
3643 | 0 | 0, /* flags */ |
3644 | 0 | local_server_name, |
3645 | 0 | NCACN_NP, |
3646 | 0 | &saddr, |
3647 | 0 | pipe_name, |
3648 | 0 | &assoc); |
3649 | 0 | if (!NT_STATUS_IS_OK(status)) { |
3650 | 0 | TALLOC_FREE(frame); |
3651 | 0 | return status; |
3652 | 0 | } |
3653 | 0 | talloc_steal(frame, assoc); |
3654 | |
|
3655 | 0 | status = rpc_client_connection_create(mem_ctx, |
3656 | 0 | assoc, |
3657 | 0 | DCERPC_FRAG_MAX_SIZE, |
3658 | 0 | &conn); |
3659 | 0 | if (!NT_STATUS_IS_OK(status)) { |
3660 | 0 | TALLOC_FREE(frame); |
3661 | 0 | return status; |
3662 | 0 | } |
3663 | 0 | talloc_steal(frame, conn); |
3664 | |
|
3665 | 0 | ret = local_np_connect( |
3666 | 0 | pipe_name, |
3667 | 0 | NCALRPC, |
3668 | 0 | remote_client_name, |
3669 | 0 | remote_client_addr, |
3670 | 0 | local_server_name, |
3671 | 0 | local_server_addr, |
3672 | 0 | session_info, |
3673 | 0 | true, |
3674 | 0 | conn, |
3675 | 0 | &npa_stream); |
3676 | 0 | if (ret != 0) { |
3677 | 0 | DBG_DEBUG("local_np_connect for %s and " |
3678 | 0 | "user %s\\%s failed: %s\n", |
3679 | 0 | pipe_name, |
3680 | 0 | session_info->info->domain_name, |
3681 | 0 | session_info->info->account_name, |
3682 | 0 | strerror(ret)); |
3683 | 0 | status = map_nt_error_from_unix(ret); |
3684 | 0 | TALLOC_FREE(frame); |
3685 | 0 | return status; |
3686 | 0 | } |
3687 | | |
3688 | 0 | status = rpc_transport_tstream_init(conn, |
3689 | 0 | &npa_stream, |
3690 | 0 | &conn->transport); |
3691 | 0 | if (!NT_STATUS_IS_OK(status)) { |
3692 | 0 | DBG_DEBUG("rpc_transport_tstream_init failed: %s\n", |
3693 | 0 | nt_errstr(status)); |
3694 | 0 | TALLOC_FREE(frame); |
3695 | 0 | return status; |
3696 | 0 | } |
3697 | 0 | conn->transport->transport = NCACN_NP; |
3698 | |
|
3699 | 0 | status = rpc_pipe_wrap_create(table, |
3700 | 0 | NULL, |
3701 | 0 | &assoc, |
3702 | 0 | &conn, |
3703 | 0 | mem_ctx, |
3704 | 0 | &result); |
3705 | 0 | if (!NT_STATUS_IS_OK(status)) { |
3706 | 0 | TALLOC_FREE(frame); |
3707 | 0 | return status; |
3708 | 0 | } |
3709 | 0 | talloc_steal(frame, result); |
3710 | |
|
3711 | 0 | status = rpccli_anon_bind_data(result, &auth); |
3712 | 0 | if (!NT_STATUS_IS_OK(status)) { |
3713 | 0 | DBG_DEBUG("rpccli_anon_bind_data failed: %s\n", |
3714 | 0 | nt_errstr(status)); |
3715 | 0 | TALLOC_FREE(frame); |
3716 | 0 | return status; |
3717 | 0 | } |
3718 | | |
3719 | 0 | status = rpc_pipe_bind(result, auth); |
3720 | 0 | if (!NT_STATUS_IS_OK(status)) { |
3721 | 0 | DBG_DEBUG("rpc_pipe_bind failed: %s\n", nt_errstr(status)); |
3722 | 0 | TALLOC_FREE(frame); |
3723 | 0 | return status; |
3724 | 0 | } |
3725 | | |
3726 | 0 | *presult = talloc_move(mem_ctx, &result); |
3727 | 0 | TALLOC_FREE(frame); |
3728 | 0 | return NT_STATUS_OK; |
3729 | 0 | } |
3730 | | |
3731 | | struct rpc_client_connection_np_state { |
3732 | | struct cli_state *cli; |
3733 | | const char *pipe_name; |
3734 | | struct rpc_client_connection *conn; |
3735 | | }; |
3736 | | |
3737 | | static void rpc_client_connection_np_done(struct tevent_req *subreq); |
3738 | | |
3739 | | static struct tevent_req *rpc_client_connection_np_send( |
3740 | | TALLOC_CTX *mem_ctx, |
3741 | | struct tevent_context *ev, |
3742 | | struct cli_state *cli, |
3743 | | const struct rpc_client_association *assoc) |
3744 | 0 | { |
3745 | 0 | struct tevent_req *req = NULL, *subreq = NULL; |
3746 | 0 | struct rpc_client_connection_np_state *state = NULL; |
3747 | 0 | enum dcerpc_transport_t transport; |
3748 | 0 | const char *endpoint = NULL; |
3749 | 0 | const struct sockaddr_storage *local_sockaddr = NULL; |
3750 | 0 | struct smbXcli_session *session = NULL; |
3751 | 0 | NTSTATUS status; |
3752 | |
|
3753 | 0 | req = tevent_req_create(mem_ctx, &state, |
3754 | 0 | struct rpc_client_connection_np_state); |
3755 | 0 | if (req == NULL) { |
3756 | 0 | return NULL; |
3757 | 0 | } |
3758 | | |
3759 | 0 | transport = dcerpc_binding_get_transport(assoc->binding); |
3760 | 0 | if (transport != NCACN_NP) { |
3761 | 0 | tevent_req_nterror(req, NT_STATUS_RPC_WRONG_KIND_OF_BINDING); |
3762 | 0 | return tevent_req_post(req, ev); |
3763 | 0 | } |
3764 | | |
3765 | 0 | endpoint = dcerpc_binding_get_string_option(assoc->binding, |
3766 | 0 | "endpoint"); |
3767 | 0 | if (endpoint == NULL) { |
3768 | 0 | tevent_req_nterror(req, NT_STATUS_RPC_INVALID_ENDPOINT_FORMAT); |
3769 | 0 | return tevent_req_post(req, ev); |
3770 | 0 | } |
3771 | | |
3772 | 0 | status = rpc_client_connection_create(state, |
3773 | 0 | assoc, |
3774 | 0 | TSTREAM_SMBXCLI_NP_MAX_BUF_SIZE, |
3775 | 0 | &state->conn); |
3776 | 0 | if (tevent_req_nterror(req, status)) { |
3777 | 0 | return tevent_req_post(req, ev); |
3778 | 0 | } |
3779 | | |
3780 | 0 | local_sockaddr = smbXcli_conn_local_sockaddr(cli->conn); |
3781 | 0 | state->conn->localaddr.u.ss = *local_sockaddr; |
3782 | |
|
3783 | 0 | if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) { |
3784 | 0 | session = cli->smb2.session; |
3785 | 0 | } else { |
3786 | 0 | session = cli->smb1.session; |
3787 | 0 | } |
3788 | |
|
3789 | 0 | status = smbXcli_session_application_key(session, state->conn, |
3790 | 0 | &state->conn->transport_session_key); |
3791 | 0 | if (!NT_STATUS_IS_OK(status)) { |
3792 | 0 | state->conn->transport_session_key = data_blob_null; |
3793 | 0 | } |
3794 | |
|
3795 | 0 | subreq = rpc_transport_np_init_send(state, ev, cli, endpoint); |
3796 | 0 | if (tevent_req_nomem(subreq, req)) { |
3797 | 0 | return tevent_req_post(req, ev); |
3798 | 0 | } |
3799 | 0 | tevent_req_set_callback(subreq, rpc_client_connection_np_done, req); |
3800 | 0 | return req; |
3801 | 0 | } |
3802 | | |
3803 | | static void rpc_client_connection_np_done(struct tevent_req *subreq) |
3804 | 0 | { |
3805 | 0 | struct tevent_req *req = tevent_req_callback_data( |
3806 | 0 | subreq, struct tevent_req); |
3807 | 0 | struct rpc_client_connection_np_state *state = tevent_req_data( |
3808 | 0 | req, struct rpc_client_connection_np_state); |
3809 | 0 | NTSTATUS status; |
3810 | |
|
3811 | 0 | status = rpc_transport_np_init_recv(subreq, |
3812 | 0 | state->conn, |
3813 | 0 | &state->conn->transport); |
3814 | 0 | TALLOC_FREE(subreq); |
3815 | 0 | if (tevent_req_nterror(req, status)) { |
3816 | 0 | return; |
3817 | 0 | } |
3818 | | |
3819 | 0 | state->conn->transport->transport = NCACN_NP; |
3820 | |
|
3821 | 0 | tevent_req_done(req); |
3822 | 0 | } |
3823 | | |
3824 | | static NTSTATUS rpc_client_connection_np_recv( |
3825 | | struct tevent_req *req, |
3826 | | TALLOC_CTX *mem_ctx, |
3827 | | struct rpc_client_connection **pconn) |
3828 | 0 | { |
3829 | 0 | struct rpc_client_connection_np_state *state = tevent_req_data( |
3830 | 0 | req, struct rpc_client_connection_np_state); |
3831 | 0 | NTSTATUS status; |
3832 | |
|
3833 | 0 | if (tevent_req_is_nterror(req, &status)) { |
3834 | 0 | tevent_req_received(req); |
3835 | 0 | return status; |
3836 | 0 | } |
3837 | 0 | *pconn = talloc_move(mem_ctx, &state->conn); |
3838 | 0 | tevent_req_received(req); |
3839 | 0 | return NT_STATUS_OK; |
3840 | 0 | } |
3841 | | |
3842 | | static NTSTATUS rpc_client_connection_np(struct cli_state *cli, |
3843 | | const struct rpc_client_association *assoc, |
3844 | | struct rpc_client_connection **pconn) |
3845 | 0 | { |
3846 | 0 | struct tevent_context *ev = NULL; |
3847 | 0 | struct tevent_req *req = NULL; |
3848 | 0 | NTSTATUS status = NT_STATUS_NO_MEMORY; |
3849 | |
|
3850 | 0 | ev = samba_tevent_context_init(cli); |
3851 | 0 | if (ev == NULL) { |
3852 | 0 | goto fail; |
3853 | 0 | } |
3854 | 0 | req = rpc_client_connection_np_send(ev, ev, cli, assoc); |
3855 | 0 | if (req == NULL) { |
3856 | 0 | goto fail; |
3857 | 0 | } |
3858 | 0 | if (!tevent_req_poll_ntstatus(req, ev, &status)) { |
3859 | 0 | goto fail; |
3860 | 0 | } |
3861 | 0 | status = rpc_client_connection_np_recv(req, NULL, pconn); |
3862 | 0 | fail: |
3863 | 0 | TALLOC_FREE(req); |
3864 | 0 | TALLOC_FREE(ev); |
3865 | 0 | return status; |
3866 | 0 | } |
3867 | | |
3868 | | struct rpc_pipe_open_np_state { |
3869 | | struct cli_state *cli; |
3870 | | const struct ndr_interface_table *table; |
3871 | | struct rpc_client_association *assoc; |
3872 | | struct rpc_client_connection *conn; |
3873 | | struct rpc_pipe_client *result; |
3874 | | }; |
3875 | | |
3876 | | static void rpc_pipe_open_np_done(struct tevent_req *subreq); |
3877 | | |
3878 | | struct tevent_req *rpc_pipe_open_np_send( |
3879 | | TALLOC_CTX *mem_ctx, |
3880 | | struct tevent_context *ev, |
3881 | | struct cli_state *cli, |
3882 | | const struct ndr_interface_table *table) |
3883 | 0 | { |
3884 | 0 | struct tevent_req *req = NULL, *subreq = NULL; |
3885 | 0 | struct rpc_pipe_open_np_state *state = NULL; |
3886 | 0 | const char *remote_name = NULL; |
3887 | 0 | const struct sockaddr_storage *remote_sockaddr = NULL; |
3888 | 0 | struct samba_sockaddr saddr = { .sa_socklen = 0, }; |
3889 | 0 | const char *pipe_name = NULL; |
3890 | 0 | NTSTATUS status; |
3891 | |
|
3892 | 0 | req = tevent_req_create( |
3893 | 0 | mem_ctx, &state, struct rpc_pipe_open_np_state); |
3894 | 0 | if (req == NULL) { |
3895 | 0 | return NULL; |
3896 | 0 | } |
3897 | 0 | state->cli = cli; |
3898 | 0 | state->table = table; |
3899 | |
|
3900 | 0 | remote_name = smbXcli_conn_remote_name(cli->conn); |
3901 | 0 | remote_sockaddr = smbXcli_conn_remote_sockaddr(cli->conn); |
3902 | 0 | saddr.u.ss = *remote_sockaddr; |
3903 | |
|
3904 | 0 | pipe_name = dcerpc_default_transport_endpoint(state, |
3905 | 0 | NCACN_NP, |
3906 | 0 | table); |
3907 | 0 | if (tevent_req_nomem(pipe_name, req)) { |
3908 | 0 | return tevent_req_post(req, ev); |
3909 | 0 | } |
3910 | | |
3911 | 0 | status = rpc_client_association_create(state, |
3912 | 0 | default_bt_features, |
3913 | 0 | DCERPC_PROPOSE_HEADER_SIGNING, |
3914 | 0 | remote_name, |
3915 | 0 | NCACN_NP, |
3916 | 0 | &saddr, |
3917 | 0 | pipe_name, |
3918 | 0 | &state->assoc); |
3919 | 0 | if (tevent_req_nterror(req, status)) { |
3920 | 0 | return tevent_req_post(req, ev); |
3921 | 0 | } |
3922 | | |
3923 | 0 | subreq = rpc_client_connection_np_send(state, ev, cli, state->assoc); |
3924 | 0 | if (tevent_req_nomem(subreq, req)) { |
3925 | 0 | return tevent_req_post(req, ev); |
3926 | 0 | } |
3927 | 0 | tevent_req_set_callback(subreq, rpc_pipe_open_np_done, req); |
3928 | 0 | return req; |
3929 | 0 | } |
3930 | | |
3931 | | static void rpc_pipe_open_np_done(struct tevent_req *subreq) |
3932 | 0 | { |
3933 | 0 | struct tevent_req *req = tevent_req_callback_data( |
3934 | 0 | subreq, struct tevent_req); |
3935 | 0 | struct rpc_pipe_open_np_state *state = tevent_req_data( |
3936 | 0 | req, struct rpc_pipe_open_np_state); |
3937 | 0 | NTSTATUS status; |
3938 | |
|
3939 | 0 | status = rpc_client_connection_np_recv(subreq, |
3940 | 0 | state, |
3941 | 0 | &state->conn); |
3942 | 0 | TALLOC_FREE(subreq); |
3943 | 0 | if (tevent_req_nterror(req, status)) { |
3944 | 0 | return; |
3945 | 0 | } |
3946 | | |
3947 | 0 | status = rpc_pipe_wrap_create(state->table, |
3948 | 0 | state->cli, |
3949 | 0 | &state->assoc, |
3950 | 0 | &state->conn, |
3951 | 0 | state, |
3952 | 0 | &state->result); |
3953 | 0 | if (tevent_req_nterror(req, status)) { |
3954 | 0 | return; |
3955 | 0 | } |
3956 | | |
3957 | 0 | tevent_req_done(req); |
3958 | 0 | } |
3959 | | |
3960 | | NTSTATUS rpc_pipe_open_np_recv( |
3961 | | struct tevent_req *req, |
3962 | | TALLOC_CTX *mem_ctx, |
3963 | | struct rpc_pipe_client **_result) |
3964 | 0 | { |
3965 | 0 | struct rpc_pipe_open_np_state *state = tevent_req_data( |
3966 | 0 | req, struct rpc_pipe_open_np_state); |
3967 | 0 | NTSTATUS status; |
3968 | |
|
3969 | 0 | if (tevent_req_is_nterror(req, &status)) { |
3970 | 0 | tevent_req_received(req); |
3971 | 0 | return status; |
3972 | 0 | } |
3973 | 0 | *_result = talloc_move(mem_ctx, &state->result); |
3974 | 0 | tevent_req_received(req); |
3975 | 0 | return NT_STATUS_OK; |
3976 | 0 | } |
3977 | | |
3978 | | /**************************************************************************** |
3979 | | Open a pipe to a remote server. |
3980 | | ****************************************************************************/ |
3981 | | |
3982 | | static NTSTATUS cli_rpc_pipe_open(struct cli_state *cli, |
3983 | | enum dcerpc_transport_t transport, |
3984 | | const struct ndr_interface_table *table, |
3985 | | const char *remote_name, |
3986 | | const struct sockaddr_storage *remote_sockaddr, |
3987 | | struct rpc_pipe_client **presult) |
3988 | 0 | { |
3989 | 0 | TALLOC_CTX *frame = talloc_stackframe(); |
3990 | 0 | struct samba_sockaddr saddr = { .sa_socklen = 0, }; |
3991 | 0 | struct rpc_client_association *assoc = NULL; |
3992 | 0 | struct rpc_client_connection *conn = NULL; |
3993 | 0 | struct rpc_pipe_client *result = NULL; |
3994 | 0 | char _tcp_endpoint[6] = { 0, }; |
3995 | 0 | const char *endpoint = NULL; |
3996 | 0 | NTSTATUS status; |
3997 | |
|
3998 | 0 | if (cli != NULL && remote_name == NULL) { |
3999 | 0 | remote_name = smbXcli_conn_remote_name(cli->conn); |
4000 | 0 | } |
4001 | 0 | if (cli != NULL && remote_sockaddr == NULL) { |
4002 | 0 | remote_sockaddr = smbXcli_conn_remote_sockaddr(cli->conn); |
4003 | 0 | } |
4004 | |
|
4005 | 0 | if (remote_sockaddr != NULL) { |
4006 | 0 | saddr.u.ss = *remote_sockaddr; |
4007 | 0 | } else { |
4008 | 0 | bool ok; |
4009 | |
|
4010 | 0 | ok = resolve_name(remote_name, |
4011 | 0 | &saddr.u.ss, |
4012 | 0 | NBT_NAME_SERVER, |
4013 | 0 | false); |
4014 | 0 | if (!ok) { |
4015 | 0 | TALLOC_FREE(frame); |
4016 | 0 | return NT_STATUS_NOT_FOUND; |
4017 | 0 | } |
4018 | 0 | } |
4019 | | |
4020 | 0 | endpoint = dcerpc_default_transport_endpoint(frame, |
4021 | 0 | transport, |
4022 | 0 | table); |
4023 | 0 | if (endpoint == NULL) { |
4024 | 0 | uint16_t port = 0; |
4025 | |
|
4026 | 0 | if (transport != NCACN_IP_TCP) { |
4027 | 0 | TALLOC_FREE(frame); |
4028 | 0 | return NT_STATUS_RPC_NO_ENDPOINT_FOUND; |
4029 | 0 | } |
4030 | | |
4031 | 0 | status = rpc_pipe_get_tcp_port(remote_name, |
4032 | 0 | &saddr, |
4033 | 0 | table, |
4034 | 0 | &port); |
4035 | 0 | if (!NT_STATUS_IS_OK(status)) { |
4036 | 0 | TALLOC_FREE(frame); |
4037 | 0 | return status; |
4038 | 0 | } |
4039 | | |
4040 | 0 | snprintf(_tcp_endpoint, sizeof(_tcp_endpoint), "%u", port); |
4041 | 0 | endpoint = _tcp_endpoint; |
4042 | 0 | } |
4043 | | |
4044 | 0 | status = rpc_client_association_create(NULL, |
4045 | 0 | default_bt_features, |
4046 | 0 | DCERPC_PROPOSE_HEADER_SIGNING, |
4047 | 0 | remote_name, |
4048 | 0 | transport, |
4049 | 0 | &saddr, |
4050 | 0 | endpoint, |
4051 | 0 | &assoc); |
4052 | 0 | if (!NT_STATUS_IS_OK(status)) { |
4053 | 0 | TALLOC_FREE(frame); |
4054 | 0 | return status; |
4055 | 0 | } |
4056 | 0 | talloc_steal(frame, assoc); |
4057 | |
|
4058 | 0 | switch (transport) { |
4059 | 0 | case NCACN_IP_TCP: |
4060 | 0 | status = rpc_pipe_open_tcp_port(NULL, |
4061 | 0 | assoc, |
4062 | 0 | &conn); |
4063 | 0 | if (!NT_STATUS_IS_OK(status)) { |
4064 | 0 | TALLOC_FREE(frame); |
4065 | 0 | return status; |
4066 | 0 | } |
4067 | 0 | talloc_steal(frame, conn); |
4068 | 0 | break; |
4069 | 0 | case NCACN_NP: |
4070 | 0 | status = rpc_client_connection_np(cli, |
4071 | 0 | assoc, |
4072 | 0 | &conn); |
4073 | 0 | if (!NT_STATUS_IS_OK(status)) { |
4074 | 0 | TALLOC_FREE(frame); |
4075 | 0 | return status; |
4076 | 0 | } |
4077 | 0 | talloc_steal(frame, conn); |
4078 | 0 | break; |
4079 | 0 | default: |
4080 | 0 | TALLOC_FREE(frame); |
4081 | 0 | return NT_STATUS_NOT_IMPLEMENTED; |
4082 | 0 | } |
4083 | 0 | status = rpc_pipe_wrap_create(table, |
4084 | 0 | cli, |
4085 | 0 | &assoc, |
4086 | 0 | &conn, |
4087 | 0 | NULL, |
4088 | 0 | &result); |
4089 | 0 | if (!NT_STATUS_IS_OK(status)) { |
4090 | 0 | TALLOC_FREE(frame); |
4091 | 0 | return status; |
4092 | 0 | } |
4093 | | |
4094 | 0 | *presult = result; |
4095 | 0 | TALLOC_FREE(frame); |
4096 | 0 | return NT_STATUS_OK; |
4097 | 0 | } |
4098 | | |
4099 | | static NTSTATUS cli_rpc_pipe_client_reconnect(struct rpc_pipe_client *p) |
4100 | 0 | { |
4101 | 0 | enum dcerpc_transport_t transport = |
4102 | 0 | dcerpc_binding_get_transport(p->assoc->binding); |
4103 | 0 | NTSTATUS status; |
4104 | |
|
4105 | 0 | switch (transport) { |
4106 | 0 | case NCACN_IP_TCP: |
4107 | 0 | status = rpc_pipe_open_tcp_port(p, |
4108 | 0 | p->assoc, |
4109 | 0 | &p->conn); |
4110 | 0 | if (!NT_STATUS_IS_OK(status)) { |
4111 | 0 | return status; |
4112 | 0 | } |
4113 | 0 | break; |
4114 | 0 | case NCACN_NP: |
4115 | 0 | status = rpc_client_connection_np(p->np_cli, |
4116 | 0 | p->assoc, |
4117 | 0 | &p->conn); |
4118 | 0 | if (!NT_STATUS_IS_OK(status)) { |
4119 | 0 | return status; |
4120 | 0 | } |
4121 | 0 | talloc_steal(p, p->conn); |
4122 | 0 | break; |
4123 | 0 | default: |
4124 | 0 | return NT_STATUS_NOT_IMPLEMENTED; |
4125 | 0 | } |
4126 | | |
4127 | 0 | return NT_STATUS_OK; |
4128 | 0 | } |
4129 | | |
4130 | | NTSTATUS cli_rpc_pipe_client_prepare_alter(struct rpc_pipe_client *p, |
4131 | | bool new_auth_context, |
4132 | | const struct ndr_interface_table *table, |
4133 | | bool new_pres_context) |
4134 | 0 | { |
4135 | 0 | uint32_t f = p->assoc->features.negotiated; |
4136 | 0 | NTSTATUS status; |
4137 | |
|
4138 | 0 | if (!new_auth_context && !new_pres_context) { |
4139 | 0 | return NT_STATUS_INVALID_PARAMETER_MIX; |
4140 | 0 | } |
4141 | | |
4142 | 0 | TALLOC_FREE(p->binding_handle); |
4143 | |
|
4144 | 0 | if (new_auth_context) { |
4145 | 0 | p->auth = NULL; |
4146 | 0 | } |
4147 | |
|
4148 | 0 | if (new_auth_context && |
4149 | 0 | !(f & DCERPC_BIND_TIME_SECURITY_CONTEXT_MULTIPLEXING)) |
4150 | 0 | { |
4151 | | /* |
4152 | | * new auth context without |
4153 | | * security context multiplexing. |
4154 | | * |
4155 | | * We need to create a new transport |
4156 | | * connection is the same association |
4157 | | * group |
4158 | | * |
4159 | | * We need to keep the old connection alive |
4160 | | * in order to connect to the existing association |
4161 | | * group..., so no TALLOC_FREE(p->conn) |
4162 | | */ |
4163 | 0 | p->conn = NULL; |
4164 | 0 | status = cli_rpc_pipe_client_reconnect(p); |
4165 | 0 | if (!NT_STATUS_IS_OK(status)) { |
4166 | 0 | return status; |
4167 | 0 | } |
4168 | 0 | } |
4169 | | |
4170 | 0 | if (new_pres_context) { |
4171 | | /* rpc_pipe_bind_send should allocate an id... */ |
4172 | 0 | p->pres_context_id = UINT16_MAX; |
4173 | 0 | p->table = table; |
4174 | 0 | p->transfer_syntax = ndr_transfer_syntax_ndr; |
4175 | 0 | } |
4176 | |
|
4177 | 0 | return NT_STATUS_OK; |
4178 | 0 | } |
4179 | | |
4180 | | /**************************************************************************** |
4181 | | Open a named pipe to an SMB server and bind anonymously. |
4182 | | ****************************************************************************/ |
4183 | | |
4184 | | NTSTATUS cli_rpc_pipe_open_noauth_transport(struct cli_state *cli, |
4185 | | enum dcerpc_transport_t transport, |
4186 | | const struct ndr_interface_table *table, |
4187 | | const char *remote_name, |
4188 | | const struct sockaddr_storage *remote_sockaddr, |
4189 | | struct rpc_pipe_client **presult) |
4190 | 0 | { |
4191 | 0 | struct rpc_pipe_client *result; |
4192 | 0 | struct pipe_auth_data *auth; |
4193 | 0 | NTSTATUS status; |
4194 | |
|
4195 | 0 | status = cli_rpc_pipe_open(cli, |
4196 | 0 | transport, |
4197 | 0 | table, |
4198 | 0 | remote_name, |
4199 | 0 | remote_sockaddr, |
4200 | 0 | &result); |
4201 | 0 | if (!NT_STATUS_IS_OK(status)) { |
4202 | 0 | return status; |
4203 | 0 | } |
4204 | | |
4205 | 0 | status = rpccli_anon_bind_data(result, &auth); |
4206 | 0 | if (!NT_STATUS_IS_OK(status)) { |
4207 | 0 | DEBUG(0, ("rpccli_anon_bind_data returned %s\n", |
4208 | 0 | nt_errstr(status))); |
4209 | 0 | TALLOC_FREE(result); |
4210 | 0 | return status; |
4211 | 0 | } |
4212 | | |
4213 | 0 | status = rpc_pipe_bind(result, auth); |
4214 | 0 | if (!NT_STATUS_IS_OK(status)) { |
4215 | 0 | int lvl = 0; |
4216 | 0 | if (ndr_syntax_id_equal(&table->syntax_id, |
4217 | 0 | &ndr_table_dssetup.syntax_id)) { |
4218 | | /* non AD domains just don't have this pipe, avoid |
4219 | | * level 0 statement in that case - gd */ |
4220 | 0 | lvl = 3; |
4221 | 0 | } |
4222 | 0 | DEBUG(lvl, ("cli_rpc_pipe_open_noauth: rpc_pipe_bind for pipe " |
4223 | 0 | "%s failed with error %s\n", |
4224 | 0 | table->name, |
4225 | 0 | nt_errstr(status) )); |
4226 | 0 | TALLOC_FREE(result); |
4227 | 0 | return status; |
4228 | 0 | } |
4229 | | |
4230 | 0 | DEBUG(10,("cli_rpc_pipe_open_noauth: opened pipe %s to machine " |
4231 | 0 | "%s and bound anonymously.\n", |
4232 | 0 | table->name, |
4233 | 0 | result->desthost)); |
4234 | |
|
4235 | 0 | *presult = result; |
4236 | 0 | return NT_STATUS_OK; |
4237 | 0 | } |
4238 | | |
4239 | | /**************************************************************************** |
4240 | | ****************************************************************************/ |
4241 | | |
4242 | | NTSTATUS cli_rpc_pipe_open_noauth(struct cli_state *cli, |
4243 | | const struct ndr_interface_table *table, |
4244 | | struct rpc_pipe_client **presult) |
4245 | 0 | { |
4246 | 0 | const char *remote_name = smbXcli_conn_remote_name(cli->conn); |
4247 | 0 | const struct sockaddr_storage *remote_sockaddr = |
4248 | 0 | smbXcli_conn_remote_sockaddr(cli->conn); |
4249 | |
|
4250 | 0 | return cli_rpc_pipe_open_noauth_transport(cli, NCACN_NP, |
4251 | 0 | table, |
4252 | 0 | remote_name, |
4253 | 0 | remote_sockaddr, |
4254 | 0 | presult); |
4255 | 0 | } |
4256 | | |
4257 | | /**************************************************************************** |
4258 | | * Reopen a connection with the same parameters. |
4259 | | * |
4260 | | * This is useful if we try an RPC function the server doesn't know about and |
4261 | | * disconnects us. |
4262 | | ****************************************************************************/ |
4263 | | NTSTATUS cli_rpc_pipe_reopen_np_noauth(struct rpc_pipe_client *rpccli) |
4264 | 0 | { |
4265 | 0 | TALLOC_CTX *frame = talloc_stackframe(); |
4266 | 0 | enum dcerpc_transport_t transport; |
4267 | 0 | struct cli_state *cli = NULL; |
4268 | 0 | struct rpc_client_association *assoc = NULL; |
4269 | 0 | struct rpc_client_connection *new_conn = NULL; |
4270 | 0 | struct pipe_auth_data *new_auth = NULL; |
4271 | 0 | NTSTATUS status; |
4272 | |
|
4273 | 0 | if (rpccli->assoc == NULL) { |
4274 | 0 | TALLOC_FREE(frame); |
4275 | 0 | return NT_STATUS_INVALID_PARAMETER_MIX; |
4276 | 0 | } |
4277 | | |
4278 | 0 | transport = dcerpc_binding_get_transport(rpccli->assoc->binding); |
4279 | 0 | if (transport != NCACN_NP) { |
4280 | 0 | TALLOC_FREE(frame); |
4281 | 0 | return NT_STATUS_INVALID_PARAMETER_MIX; |
4282 | 0 | } |
4283 | | |
4284 | 0 | if (rpccli->np_cli == NULL) { |
4285 | 0 | TALLOC_FREE(frame); |
4286 | 0 | return NT_STATUS_INVALID_PARAMETER_MIX; |
4287 | 0 | } |
4288 | 0 | cli = rpccli->np_cli; |
4289 | | |
4290 | | /* |
4291 | | * close the old connection |
4292 | | */ |
4293 | 0 | TALLOC_FREE(rpccli->conn); |
4294 | | |
4295 | | /* |
4296 | | * Free the auth context |
4297 | | */ |
4298 | 0 | TALLOC_FREE(rpccli->auth); |
4299 | | |
4300 | | /* |
4301 | | * Reset the association |
4302 | | */ |
4303 | 0 | assoc = talloc_move(frame, &rpccli->assoc); |
4304 | 0 | status = dcerpc_binding_set_assoc_group_id(assoc->binding, 0); |
4305 | 0 | if (!NT_STATUS_IS_OK(status)) { |
4306 | 0 | return status; |
4307 | 0 | } |
4308 | 0 | assoc->features.negotiated = 0; |
4309 | 0 | if (assoc->features.client != 0) { |
4310 | 0 | assoc->features.negotiation_done = false; |
4311 | 0 | } |
4312 | 0 | assoc->next_call_id = 0; |
4313 | |
|
4314 | 0 | status = rpc_client_connection_np(cli, |
4315 | 0 | assoc, |
4316 | 0 | &new_conn); |
4317 | 0 | if (!NT_STATUS_IS_OK(status)) { |
4318 | 0 | TALLOC_FREE(frame); |
4319 | 0 | return status; |
4320 | 0 | } |
4321 | | |
4322 | 0 | rpccli->assoc = talloc_move(rpccli, &assoc); |
4323 | 0 | rpccli->conn = talloc_move(rpccli, &new_conn); |
4324 | | |
4325 | | /* rpc_pipe_bind_send should allocate an id... */ |
4326 | 0 | rpccli->pres_context_id = UINT16_MAX; |
4327 | 0 | rpccli->verified_pcontext = false; |
4328 | |
|
4329 | 0 | status = rpccli_anon_bind_data(rpccli, &new_auth); |
4330 | 0 | if (!NT_STATUS_IS_OK(status)) { |
4331 | 0 | TALLOC_FREE(frame); |
4332 | 0 | return status; |
4333 | 0 | } |
4334 | | |
4335 | 0 | status = rpc_pipe_bind(rpccli, new_auth); |
4336 | 0 | if (!NT_STATUS_IS_OK(status)) { |
4337 | 0 | TALLOC_FREE(frame); |
4338 | 0 | return status; |
4339 | 0 | } |
4340 | | |
4341 | 0 | TALLOC_FREE(frame); |
4342 | 0 | return NT_STATUS_OK; |
4343 | 0 | } |
4344 | | |
4345 | | /**************************************************************************** |
4346 | | Open a named pipe to an SMB server and bind using the mech specified |
4347 | | |
4348 | | This routine references the creds pointer that is passed in |
4349 | | ****************************************************************************/ |
4350 | | |
4351 | | NTSTATUS cli_rpc_pipe_open_with_creds(struct cli_state *cli, |
4352 | | const struct ndr_interface_table *table, |
4353 | | enum dcerpc_transport_t transport, |
4354 | | enum dcerpc_AuthType auth_type, |
4355 | | enum dcerpc_AuthLevel auth_level, |
4356 | | const char *target_service, |
4357 | | const char *target_hostname, |
4358 | | const struct sockaddr_storage *remote_sockaddr, |
4359 | | struct cli_credentials *creds, |
4360 | | struct rpc_pipe_client **presult) |
4361 | 0 | { |
4362 | 0 | struct rpc_pipe_client *result; |
4363 | 0 | struct pipe_auth_data *auth = NULL; |
4364 | 0 | NTSTATUS status; |
4365 | |
|
4366 | 0 | if (target_service == NULL) { |
4367 | 0 | target_service = table->authservices->names[0]; |
4368 | 0 | } |
4369 | |
|
4370 | 0 | status = cli_rpc_pipe_open(cli, |
4371 | 0 | transport, |
4372 | 0 | table, |
4373 | 0 | target_hostname, |
4374 | 0 | remote_sockaddr, |
4375 | 0 | &result); |
4376 | 0 | if (!NT_STATUS_IS_OK(status)) { |
4377 | 0 | return status; |
4378 | 0 | } |
4379 | | |
4380 | 0 | status = rpccli_generic_bind_data_from_creds(result, |
4381 | 0 | auth_type, |
4382 | 0 | auth_level, |
4383 | 0 | target_hostname, |
4384 | 0 | target_service, |
4385 | 0 | creds, |
4386 | 0 | &auth); |
4387 | 0 | if (!NT_STATUS_IS_OK(status)) { |
4388 | 0 | DBG_ERR("rpccli_generic_bind_data_from_creds returned %s\n", |
4389 | 0 | nt_errstr(status)); |
4390 | 0 | goto err; |
4391 | 0 | } |
4392 | | |
4393 | 0 | status = rpc_pipe_bind(result, auth); |
4394 | 0 | if (!NT_STATUS_IS_OK(status)) { |
4395 | 0 | DBG_ERR("cli_rpc_pipe_bind failed with error %s\n", |
4396 | 0 | nt_errstr(status)); |
4397 | 0 | goto err; |
4398 | 0 | } |
4399 | | |
4400 | 0 | DBG_DEBUG("opened pipe %s to machine %s and bound as user %s.\n", |
4401 | 0 | table->name, |
4402 | 0 | result->desthost, |
4403 | 0 | cli_credentials_get_unparsed_name(creds, talloc_tos())); |
4404 | |
|
4405 | 0 | *presult = result; |
4406 | 0 | return NT_STATUS_OK; |
4407 | | |
4408 | 0 | err: |
4409 | |
|
4410 | 0 | TALLOC_FREE(result); |
4411 | 0 | return status; |
4412 | 0 | } |
4413 | | |
4414 | | NTSTATUS cli_rpc_pipe_client_auth_schannel( |
4415 | | struct rpc_pipe_client *rpccli, |
4416 | | const struct ndr_interface_table *table, |
4417 | | struct netlogon_creds_cli_context *netlogon_creds) |
4418 | 0 | { |
4419 | 0 | TALLOC_CTX *frame = talloc_stackframe(); |
4420 | 0 | struct pipe_auth_data *rpcauth = NULL; |
4421 | 0 | const char *target_service = table->authservices->names[0]; |
4422 | 0 | struct cli_credentials *cli_creds = NULL; |
4423 | 0 | enum dcerpc_AuthLevel auth_level; |
4424 | 0 | NTSTATUS status; |
4425 | |
|
4426 | 0 | auth_level = netlogon_creds_cli_auth_level(netlogon_creds); |
4427 | |
|
4428 | 0 | status = netlogon_creds_bind_cli_credentials(netlogon_creds, |
4429 | 0 | frame, |
4430 | 0 | &cli_creds); |
4431 | 0 | if (!NT_STATUS_IS_OK(status)) { |
4432 | 0 | DBG_DEBUG("netlogon_creds_bind_cli_credentials failed: %s\n", |
4433 | 0 | nt_errstr(status)); |
4434 | 0 | TALLOC_FREE(frame); |
4435 | 0 | return status; |
4436 | 0 | } |
4437 | | |
4438 | 0 | status = rpccli_generic_bind_data_from_creds(rpccli, |
4439 | 0 | DCERPC_AUTH_TYPE_SCHANNEL, |
4440 | 0 | auth_level, |
4441 | 0 | rpccli->desthost, |
4442 | 0 | target_service, |
4443 | 0 | cli_creds, |
4444 | 0 | &rpcauth); |
4445 | 0 | if (!NT_STATUS_IS_OK(status)) { |
4446 | 0 | DEBUG(0, ("rpccli_generic_bind_data_from_creds returned %s\n", |
4447 | 0 | nt_errstr(status))); |
4448 | 0 | TALLOC_FREE(frame); |
4449 | 0 | return status; |
4450 | 0 | } |
4451 | | |
4452 | 0 | status = rpc_pipe_bind(rpccli, rpcauth); |
4453 | 0 | if (!NT_STATUS_IS_OK(status)) { |
4454 | 0 | DBG_DEBUG("rpc_pipe_bind failed with error %s\n", |
4455 | 0 | nt_errstr(status)); |
4456 | 0 | TALLOC_FREE(frame); |
4457 | 0 | return status; |
4458 | 0 | } |
4459 | | |
4460 | 0 | TALLOC_FREE(frame); |
4461 | 0 | return NT_STATUS_OK; |
4462 | 0 | } |
4463 | | |
4464 | | NTSTATUS cli_rpc_pipe_open_bind_schannel( |
4465 | | struct cli_state *cli, |
4466 | | const struct ndr_interface_table *table, |
4467 | | enum dcerpc_transport_t transport, |
4468 | | struct netlogon_creds_cli_context *netlogon_creds, |
4469 | | const char *remote_name, |
4470 | | const struct sockaddr_storage *remote_sockaddr, |
4471 | | struct rpc_pipe_client **_rpccli) |
4472 | 0 | { |
4473 | 0 | struct rpc_pipe_client *rpccli = NULL; |
4474 | 0 | NTSTATUS status; |
4475 | |
|
4476 | 0 | status = cli_rpc_pipe_open(cli, |
4477 | 0 | transport, |
4478 | 0 | table, |
4479 | 0 | remote_name, |
4480 | 0 | remote_sockaddr, |
4481 | 0 | &rpccli); |
4482 | 0 | if (!NT_STATUS_IS_OK(status)) { |
4483 | 0 | return status; |
4484 | 0 | } |
4485 | | |
4486 | 0 | status = cli_rpc_pipe_client_auth_schannel(rpccli, |
4487 | 0 | table, |
4488 | 0 | netlogon_creds); |
4489 | 0 | if (!NT_STATUS_IS_OK(status)) { |
4490 | 0 | DBG_DEBUG("rpc_pipe_bind failed with error %s\n", |
4491 | 0 | nt_errstr(status)); |
4492 | 0 | TALLOC_FREE(rpccli); |
4493 | 0 | return status; |
4494 | 0 | } |
4495 | | |
4496 | 0 | *_rpccli = rpccli; |
4497 | |
|
4498 | 0 | return NT_STATUS_OK; |
4499 | 0 | } |
4500 | | |
4501 | | NTSTATUS cli_rpc_pipe_open_schannel_with_creds(struct cli_state *cli, |
4502 | | const struct ndr_interface_table *table, |
4503 | | enum dcerpc_transport_t transport, |
4504 | | struct netlogon_creds_cli_context *netlogon_creds, |
4505 | | const char *remote_name, |
4506 | | const struct sockaddr_storage *remote_sockaddr, |
4507 | | struct rpc_pipe_client **_rpccli) |
4508 | 0 | { |
4509 | 0 | TALLOC_CTX *frame = talloc_stackframe(); |
4510 | 0 | struct rpc_pipe_client *rpccli; |
4511 | 0 | struct netlogon_creds_cli_lck *lck; |
4512 | 0 | NTSTATUS status; |
4513 | |
|
4514 | 0 | status = netlogon_creds_cli_lck( |
4515 | 0 | netlogon_creds, NETLOGON_CREDS_CLI_LCK_EXCLUSIVE, |
4516 | 0 | frame, &lck); |
4517 | 0 | if (!NT_STATUS_IS_OK(status)) { |
4518 | 0 | DBG_WARNING("netlogon_creds_cli_lck returned %s\n", |
4519 | 0 | nt_errstr(status)); |
4520 | 0 | TALLOC_FREE(frame); |
4521 | 0 | return status; |
4522 | 0 | } |
4523 | | |
4524 | 0 | status = cli_rpc_pipe_open_bind_schannel(cli, |
4525 | 0 | table, |
4526 | 0 | transport, |
4527 | 0 | netlogon_creds, |
4528 | 0 | remote_name, |
4529 | 0 | remote_sockaddr, |
4530 | 0 | &rpccli); |
4531 | 0 | if (NT_STATUS_EQUAL(status, NT_STATUS_NETWORK_ACCESS_DENIED)) { |
4532 | 0 | netlogon_creds_cli_delete_lck(netlogon_creds); |
4533 | 0 | } |
4534 | 0 | if (!NT_STATUS_IS_OK(status)) { |
4535 | 0 | DBG_DEBUG("cli_rpc_pipe_open_bind_schannel failed: %s\n", |
4536 | 0 | nt_errstr(status)); |
4537 | 0 | TALLOC_FREE(frame); |
4538 | 0 | return status; |
4539 | 0 | } |
4540 | | |
4541 | 0 | if (ndr_syntax_id_equal(&table->syntax_id, |
4542 | 0 | &ndr_table_netlogon.syntax_id)) { |
4543 | 0 | status = netlogon_creds_cli_check(netlogon_creds, |
4544 | 0 | rpccli->binding_handle, |
4545 | 0 | NULL); |
4546 | 0 | if (!NT_STATUS_IS_OK(status)) { |
4547 | 0 | DEBUG(0, ("netlogon_creds_cli_check failed with %s\n", |
4548 | 0 | nt_errstr(status))); |
4549 | 0 | TALLOC_FREE(frame); |
4550 | 0 | return status; |
4551 | 0 | } |
4552 | 0 | } |
4553 | | |
4554 | 0 | DBG_DEBUG("opened pipe %s to machine %s with key %s " |
4555 | 0 | "and bound using schannel.\n", |
4556 | 0 | table->name, rpccli->desthost, |
4557 | 0 | netlogon_creds_cli_debug_string(netlogon_creds, lck)); |
4558 | |
|
4559 | 0 | TALLOC_FREE(frame); |
4560 | |
|
4561 | 0 | *_rpccli = rpccli; |
4562 | 0 | return NT_STATUS_OK; |
4563 | 0 | } |