/src/samba/source4/librpc/rpc/dcerpc_secondary.c
Line | Count | Source |
1 | | /* |
2 | | Unix SMB/CIFS implementation. |
3 | | |
4 | | dcerpc connect functions |
5 | | |
6 | | Copyright (C) Andrew Tridgell 2003 |
7 | | Copyright (C) Jelmer Vernooij 2004 |
8 | | Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005-2007 |
9 | | Copyright (C) Rafal Szczesniak 2005 |
10 | | |
11 | | This program is free software; you can redistribute it and/or modify |
12 | | it under the terms of the GNU General Public License as published by |
13 | | the Free Software Foundation; either version 3 of the License, or |
14 | | (at your option) any later version. |
15 | | |
16 | | This program is distributed in the hope that it will be useful, |
17 | | but WITHOUT ANY WARRANTY; without even the implied warranty of |
18 | | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
19 | | GNU General Public License for more details. |
20 | | |
21 | | You should have received a copy of the GNU General Public License |
22 | | along with this program. If not, see <http://www.gnu.org/licenses/>. |
23 | | */ |
24 | | |
25 | | #define SOURCE4_LIBRPC_INTERNALS 1 |
26 | | |
27 | | #include "includes.h" |
28 | | #include "libcli/composite/composite.h" |
29 | | #include "lib/events/events.h" |
30 | | #include "librpc/rpc/dcerpc.h" |
31 | | #include "librpc/rpc/dcerpc_proto.h" |
32 | | #include "auth/credentials/credentials.h" |
33 | | #include "param/param.h" |
34 | | #include "libcli/resolve/resolve.h" |
35 | | #include "lib/util/util_net.h" |
36 | | |
37 | | struct sec_conn_state { |
38 | | struct dcerpc_pipe *pipe; |
39 | | struct dcerpc_pipe *pipe2; |
40 | | struct dcerpc_binding *binding; |
41 | | }; |
42 | | |
43 | | |
44 | | static void continue_open_smb(struct composite_context *ctx); |
45 | | static void continue_open_tcp(struct composite_context *ctx); |
46 | | static void continue_open_ncalrpc(struct composite_context *ctx); |
47 | | static void continue_open_ncacn_unix(struct composite_context *ctx); |
48 | | static void continue_pipe_open(struct composite_context *c); |
49 | | |
50 | | |
51 | | /* |
52 | | Send request to create a secondary dcerpc connection from a primary |
53 | | connection |
54 | | */ |
55 | | _PUBLIC_ struct composite_context* dcerpc_secondary_connection_send(struct dcerpc_pipe *p, |
56 | | const struct dcerpc_binding *b) |
57 | 0 | { |
58 | 0 | struct composite_context *c; |
59 | 0 | struct sec_conn_state *s; |
60 | 0 | struct composite_context *pipe_smb_req; |
61 | 0 | struct composite_context *pipe_tcp_req; |
62 | 0 | const char *localaddress = NULL; |
63 | 0 | struct composite_context *pipe_ncalrpc_req; |
64 | 0 | const char *ncalrpc_dir = NULL; |
65 | 0 | struct composite_context *pipe_unix_req; |
66 | 0 | const char *host; |
67 | 0 | const char *target_hostname; |
68 | 0 | const char *endpoint; |
69 | | |
70 | | /* composite context allocation and setup */ |
71 | 0 | c = composite_create(p, p->conn->event_ctx); |
72 | 0 | if (c == NULL) return NULL; |
73 | | |
74 | 0 | s = talloc_zero(c, struct sec_conn_state); |
75 | 0 | if (composite_nomem(s, c)) return c; |
76 | 0 | c->private_data = s; |
77 | |
|
78 | 0 | s->pipe = p; |
79 | 0 | s->binding = dcerpc_binding_dup(s, b); |
80 | 0 | if (composite_nomem(s->binding, c)) return c; |
81 | | |
82 | | /* initialise second dcerpc pipe based on primary pipe's event context */ |
83 | 0 | s->pipe2 = dcerpc_pipe_init(c, s->pipe->conn->event_ctx); |
84 | 0 | if (composite_nomem(s->pipe2, c)) return c; |
85 | | |
86 | 0 | if (DEBUGLEVEL >= 10) |
87 | 0 | s->pipe2->conn->packet_log_dir = s->pipe->conn->packet_log_dir; |
88 | |
|
89 | 0 | host = dcerpc_binding_get_string_option(s->binding, "host"); |
90 | 0 | if (host == NULL) { |
91 | | /* |
92 | | * We may fallback to the host of the given connection |
93 | | */ |
94 | 0 | host = dcerpc_binding_get_string_option(s->pipe->binding, |
95 | 0 | "host"); |
96 | 0 | } |
97 | 0 | target_hostname = dcerpc_binding_get_string_option(s->binding, "target_hostname"); |
98 | 0 | if (target_hostname == NULL) { |
99 | | /* |
100 | | * We may fallback to the target_hostname of the given connection |
101 | | */ |
102 | 0 | target_hostname = dcerpc_binding_get_string_option(s->pipe->binding, |
103 | 0 | "target_hostname"); |
104 | 0 | } |
105 | 0 | endpoint = dcerpc_binding_get_string_option(s->binding, "endpoint"); |
106 | 0 | if (endpoint == NULL) { |
107 | | /* |
108 | | * We may fallback to the endpoint of the given connection |
109 | | */ |
110 | 0 | endpoint = dcerpc_binding_get_string_option(s->pipe->binding, "endpoint"); |
111 | 0 | } |
112 | 0 | if (endpoint == NULL) { |
113 | 0 | composite_error(c, NT_STATUS_INVALID_PARAMETER_MIX); |
114 | 0 | return c; |
115 | 0 | } |
116 | | |
117 | | /* open second dcerpc pipe using the same transport as for primary pipe */ |
118 | 0 | switch (s->pipe->conn->transport.transport) { |
119 | 0 | case NCACN_NP: |
120 | 0 | pipe_smb_req = dcerpc_secondary_smb_send(s->pipe->conn, |
121 | 0 | s->pipe2->conn, |
122 | 0 | endpoint); |
123 | 0 | composite_continue(c, pipe_smb_req, continue_open_smb, c); |
124 | 0 | return c; |
125 | | |
126 | 0 | case NCACN_IP_TCP: |
127 | 0 | if (host == NULL) { |
128 | 0 | composite_error(c, NT_STATUS_INVALID_PARAMETER_MIX); |
129 | 0 | return c; |
130 | 0 | } |
131 | | |
132 | 0 | if (!is_ipaddress(host)) { |
133 | | /* |
134 | | * We may fallback to the host of the given connection |
135 | | */ |
136 | 0 | host = dcerpc_binding_get_string_option(s->pipe->binding, |
137 | 0 | "host"); |
138 | 0 | if (host == NULL) { |
139 | 0 | composite_error(c, NT_STATUS_INVALID_PARAMETER_MIX); |
140 | 0 | return c; |
141 | 0 | } |
142 | 0 | if (!is_ipaddress(host)) { |
143 | 0 | composite_error(c, NT_STATUS_INVALID_PARAMETER_MIX); |
144 | 0 | return c; |
145 | 0 | } |
146 | 0 | } |
147 | | |
148 | 0 | localaddress = dcerpc_binding_get_string_option(s->binding, |
149 | 0 | "localaddress"); |
150 | 0 | if (localaddress == NULL) { |
151 | | /* |
152 | | * We may fallback to the localaddress of the given connection |
153 | | */ |
154 | 0 | localaddress = dcerpc_binding_get_string_option(s->pipe->binding, |
155 | 0 | "localaddress"); |
156 | 0 | } |
157 | |
|
158 | 0 | pipe_tcp_req = dcerpc_pipe_open_tcp_send(s->pipe2->conn, |
159 | 0 | localaddress, |
160 | 0 | host, |
161 | 0 | target_hostname, |
162 | 0 | atoi(endpoint), |
163 | 0 | resolve_context_init(s)); |
164 | 0 | composite_continue(c, pipe_tcp_req, continue_open_tcp, c); |
165 | 0 | return c; |
166 | | |
167 | 0 | case NCALRPC: |
168 | 0 | ncalrpc_dir = dcerpc_binding_get_string_option(s->binding, |
169 | 0 | "ncalrpc_dir"); |
170 | 0 | if (ncalrpc_dir == NULL) { |
171 | 0 | ncalrpc_dir = dcerpc_binding_get_string_option(s->pipe->binding, |
172 | 0 | "ncalrpc_dir"); |
173 | 0 | } |
174 | 0 | if (ncalrpc_dir == NULL) { |
175 | 0 | composite_error(c, NT_STATUS_INVALID_PARAMETER_MIX); |
176 | 0 | return c; |
177 | 0 | } |
178 | | |
179 | 0 | pipe_ncalrpc_req = dcerpc_pipe_open_pipe_send(s->pipe2->conn, |
180 | 0 | ncalrpc_dir, |
181 | 0 | endpoint); |
182 | 0 | composite_continue(c, pipe_ncalrpc_req, continue_open_ncalrpc, c); |
183 | 0 | return c; |
184 | | |
185 | 0 | case NCACN_UNIX_STREAM: |
186 | 0 | pipe_unix_req = dcerpc_pipe_open_unix_stream_send(s->pipe2->conn, |
187 | 0 | endpoint); |
188 | 0 | composite_continue(c, pipe_unix_req, continue_open_ncacn_unix, c); |
189 | 0 | return c; |
190 | | |
191 | 0 | default: |
192 | | /* looks like a transport we don't support */ |
193 | 0 | composite_error(c, NT_STATUS_NOT_SUPPORTED); |
194 | 0 | } |
195 | | |
196 | 0 | return c; |
197 | 0 | } |
198 | | |
199 | | |
200 | | /* |
201 | | Stage 2 of secondary_connection: Receive result of pipe open request on smb |
202 | | */ |
203 | | static void continue_open_smb(struct composite_context *ctx) |
204 | 0 | { |
205 | 0 | struct composite_context *c = talloc_get_type(ctx->async.private_data, |
206 | 0 | struct composite_context); |
207 | | |
208 | 0 | c->status = dcerpc_secondary_smb_recv(ctx); |
209 | 0 | if (!composite_is_ok(c)) return; |
210 | | |
211 | 0 | continue_pipe_open(c); |
212 | 0 | } |
213 | | |
214 | | |
215 | | /* |
216 | | Stage 2 of secondary_connection: Receive result of pipe open request on tcp/ip |
217 | | */ |
218 | | static void continue_open_tcp(struct composite_context *ctx) |
219 | 0 | { |
220 | 0 | struct composite_context *c = talloc_get_type(ctx->async.private_data, |
221 | 0 | struct composite_context); |
222 | 0 | struct sec_conn_state *s = talloc_get_type_abort(c->private_data, |
223 | 0 | struct sec_conn_state); |
224 | 0 | char *localaddr = NULL; |
225 | 0 | char *remoteaddr = NULL; |
226 | |
|
227 | 0 | c->status = dcerpc_pipe_open_tcp_recv(ctx, s, &localaddr, &remoteaddr); |
228 | 0 | if (!composite_is_ok(c)) return; |
229 | | |
230 | 0 | c->status = dcerpc_binding_set_string_option(s->binding, |
231 | 0 | "localaddress", |
232 | 0 | localaddr); |
233 | 0 | if (!composite_is_ok(c)) return; |
234 | | |
235 | 0 | c->status = dcerpc_binding_set_string_option(s->binding, |
236 | 0 | "host", |
237 | 0 | remoteaddr); |
238 | 0 | if (!composite_is_ok(c)) return; |
239 | | |
240 | 0 | continue_pipe_open(c); |
241 | 0 | } |
242 | | |
243 | | /* |
244 | | Stage 2 of secondary_connection: Receive result of pipe open request on ncalrpc |
245 | | */ |
246 | | static void continue_open_ncalrpc(struct composite_context *ctx) |
247 | 0 | { |
248 | 0 | struct composite_context *c = talloc_get_type(ctx->async.private_data, |
249 | 0 | struct composite_context); |
250 | |
|
251 | 0 | c->status = dcerpc_pipe_open_pipe_recv(ctx); |
252 | 0 | if (!composite_is_ok(c)) return; |
253 | | |
254 | 0 | continue_pipe_open(c); |
255 | 0 | } |
256 | | |
257 | | /* |
258 | | Stage 2 of secondary_connection: Receive result of pipe open request on ncacn_unix |
259 | | */ |
260 | | static void continue_open_ncacn_unix(struct composite_context *ctx) |
261 | 0 | { |
262 | 0 | struct composite_context *c = talloc_get_type(ctx->async.private_data, |
263 | 0 | struct composite_context); |
264 | |
|
265 | 0 | c->status = dcerpc_pipe_open_unix_stream_recv(ctx); |
266 | 0 | if (!composite_is_ok(c)) return; |
267 | | |
268 | 0 | continue_pipe_open(c); |
269 | 0 | } |
270 | | |
271 | | |
272 | | /* |
273 | | Stage 3 of secondary_connection: Get binding data and flags from primary pipe |
274 | | and say if we're done ok. |
275 | | */ |
276 | | static void continue_pipe_open(struct composite_context *c) |
277 | 0 | { |
278 | 0 | struct sec_conn_state *s; |
279 | |
|
280 | 0 | s = talloc_get_type(c->private_data, struct sec_conn_state); |
281 | |
|
282 | 0 | s->pipe2->conn->flags = s->pipe->conn->flags; |
283 | 0 | s->pipe2->binding = dcerpc_binding_dup(s->pipe2, s->binding); |
284 | 0 | if (composite_nomem(s->pipe2->binding, c)) { |
285 | 0 | return; |
286 | 0 | } |
287 | | |
288 | 0 | composite_done(c); |
289 | 0 | } |
290 | | |
291 | | |
292 | | /* |
293 | | Receive result of secondary rpc connection request and return |
294 | | second dcerpc pipe. |
295 | | */ |
296 | | _PUBLIC_ NTSTATUS dcerpc_secondary_connection_recv(struct composite_context *c, |
297 | | struct dcerpc_pipe **p2) |
298 | 0 | { |
299 | 0 | NTSTATUS status = composite_wait(c); |
300 | 0 | struct sec_conn_state *s; |
301 | |
|
302 | 0 | s = talloc_get_type(c->private_data, struct sec_conn_state); |
303 | |
|
304 | 0 | if (NT_STATUS_IS_OK(status)) { |
305 | 0 | *p2 = talloc_steal(s->pipe, s->pipe2); |
306 | 0 | } |
307 | |
|
308 | 0 | talloc_free(c); |
309 | 0 | return status; |
310 | 0 | } |
311 | | |
312 | | /* |
313 | | Create a secondary DCERPC connection, then bind (and possibly |
314 | | authenticate) using the supplied credentials. |
315 | | |
316 | | This creates a second connection, to the same host (and on ncacn_np on the same connection) as the first |
317 | | */ |
318 | | struct sec_auth_conn_state { |
319 | | struct dcerpc_pipe *pipe2; |
320 | | const struct dcerpc_binding *binding; |
321 | | const struct ndr_interface_table *table; |
322 | | struct cli_credentials *credentials; |
323 | | struct composite_context *ctx; |
324 | | struct loadparm_context *lp_ctx; |
325 | | }; |
326 | | |
327 | | static void dcerpc_secondary_auth_connection_bind(struct composite_context *ctx); |
328 | | static void dcerpc_secondary_auth_connection_continue(struct composite_context *ctx); |
329 | | |
330 | | _PUBLIC_ struct composite_context* dcerpc_secondary_auth_connection_send(struct dcerpc_pipe *p, |
331 | | const struct dcerpc_binding *binding, |
332 | | const struct ndr_interface_table *table, |
333 | | struct cli_credentials *credentials, |
334 | | struct loadparm_context *lp_ctx) |
335 | 0 | { |
336 | |
|
337 | 0 | struct composite_context *c, *secondary_conn_ctx; |
338 | 0 | struct sec_auth_conn_state *s; |
339 | | |
340 | | /* composite context allocation and setup */ |
341 | 0 | c = composite_create(p, p->conn->event_ctx); |
342 | 0 | if (c == NULL) return NULL; |
343 | | |
344 | 0 | s = talloc_zero(c, struct sec_auth_conn_state); |
345 | 0 | if (composite_nomem(s, c)) return c; |
346 | 0 | c->private_data = s; |
347 | 0 | s->ctx = c; |
348 | |
|
349 | 0 | s->binding = binding; |
350 | 0 | s->table = table; |
351 | 0 | s->credentials = credentials; |
352 | 0 | s->lp_ctx = lp_ctx; |
353 | | |
354 | 0 | secondary_conn_ctx = dcerpc_secondary_connection_send(p, binding); |
355 | | |
356 | 0 | if (composite_nomem(secondary_conn_ctx, s->ctx)) { |
357 | 0 | talloc_free(c); |
358 | 0 | return NULL; |
359 | 0 | } |
360 | | |
361 | 0 | composite_continue(s->ctx, secondary_conn_ctx, dcerpc_secondary_auth_connection_bind, |
362 | 0 | s); |
363 | 0 | return c; |
364 | 0 | } |
365 | | |
366 | | /* |
367 | | Stage 2 of secondary_auth_connection: |
368 | | Having made the secondary connection, we will need to do an (authenticated) bind |
369 | | */ |
370 | | static void dcerpc_secondary_auth_connection_bind(struct composite_context *ctx) |
371 | 0 | { |
372 | 0 | struct composite_context *secondary_auth_ctx; |
373 | 0 | struct sec_auth_conn_state *s = talloc_get_type(ctx->async.private_data, |
374 | 0 | struct sec_auth_conn_state); |
375 | | |
376 | 0 | s->ctx->status = dcerpc_secondary_connection_recv(ctx, &s->pipe2); |
377 | 0 | if (!composite_is_ok(s->ctx)) return; |
378 | | |
379 | 0 | secondary_auth_ctx = dcerpc_pipe_auth_send(s->pipe2, s->binding, s->table, s->credentials, |
380 | 0 | s->lp_ctx); |
381 | 0 | composite_continue(s->ctx, secondary_auth_ctx, dcerpc_secondary_auth_connection_continue, s); |
382 | | |
383 | 0 | } |
384 | | |
385 | | /* |
386 | | Stage 3 of secondary_auth_connection: Receive result of authenticated bind request |
387 | | */ |
388 | | static void dcerpc_secondary_auth_connection_continue(struct composite_context *ctx) |
389 | 0 | { |
390 | 0 | struct sec_auth_conn_state *s = talloc_get_type(ctx->async.private_data, |
391 | 0 | struct sec_auth_conn_state); |
392 | |
|
393 | 0 | s->ctx->status = dcerpc_pipe_auth_recv(ctx, s, &s->pipe2); |
394 | 0 | if (!composite_is_ok(s->ctx)) return; |
395 | | |
396 | 0 | composite_done(s->ctx); |
397 | 0 | } |
398 | | |
399 | | /* |
400 | | Receive an authenticated pipe, created as a secondary connection |
401 | | */ |
402 | | _PUBLIC_ NTSTATUS dcerpc_secondary_auth_connection_recv(struct composite_context *c, |
403 | | TALLOC_CTX *mem_ctx, |
404 | | struct dcerpc_pipe **p) |
405 | 0 | { |
406 | 0 | NTSTATUS status = composite_wait(c); |
407 | 0 | struct sec_auth_conn_state *s; |
408 | |
|
409 | 0 | s = talloc_get_type(c->private_data, struct sec_auth_conn_state); |
410 | |
|
411 | 0 | if (NT_STATUS_IS_OK(status)) { |
412 | 0 | *p = talloc_steal(mem_ctx, s->pipe2); |
413 | 0 | } |
414 | |
|
415 | 0 | talloc_free(c); |
416 | 0 | return status; |
417 | 0 | } |
418 | | |
419 | | _PUBLIC_ NTSTATUS dcerpc_secondary_auth_connection(struct dcerpc_pipe *p, |
420 | | const struct dcerpc_binding *binding, |
421 | | const struct ndr_interface_table *table, |
422 | | struct cli_credentials *credentials, |
423 | | struct loadparm_context *lp_ctx, |
424 | | TALLOC_CTX *mem_ctx, |
425 | | struct dcerpc_pipe **p2) |
426 | 0 | { |
427 | 0 | struct composite_context *c; |
428 | |
|
429 | 0 | c = dcerpc_secondary_auth_connection_send(p, binding, table, |
430 | 0 | credentials, lp_ctx); |
431 | 0 | return dcerpc_secondary_auth_connection_recv(c, mem_ctx, p2); |
432 | 0 | } |