Line data Source code
1 : /*
2 : Unix SMB/CIFS implementation.
3 :
4 : [MS-RPCH] - RPC over HTTP client
5 :
6 : Copyright (C) 2013 Samuel Cabrero <samuelcabrero@kernevil.me>
7 : Copyright (C) Julien Kerihuel <j.kerihuel@openchange.org> 2013
8 :
9 : This program is free software; you can redistribute it and/or modify
10 : it under the terms of the GNU General Public License as published by
11 : the Free Software Foundation; either version 3 of the License, or
12 : (at your option) any later version.
13 :
14 : This program is distributed in the hope that it will be useful,
15 : but WITHOUT ANY WARRANTY; without even the implied warranty of
16 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 : GNU General Public License for more details.
18 :
19 : You should have received a copy of the GNU General Public License
20 : along with this program. If not, see <http://www.gnu.org/licenses/>.
21 : */
22 :
23 : #include "includes.h"
24 : #include <tevent.h>
25 : #include <talloc.h>
26 : #include "lib/tsocket/tsocket.h"
27 : #include "lib/tls/tls.h"
28 : #include "lib/util/tevent_ntstatus.h"
29 : #include "lib/util/util_net.h"
30 : #include "libcli/resolve/resolve.h"
31 : #include "libcli/composite/composite.h"
32 : #include "auth/credentials/credentials.h"
33 : #include "auth/credentials/credentials_internal.h"
34 : #include <gen_ndr/dcerpc.h>
35 : #include <gen_ndr/ndr_dcerpc.h>
36 :
37 : #include "librpc/rpc/dcerpc.h"
38 : #include "librpc/rpc/dcerpc_roh.h"
39 : #include "librpc/rpc/dcerpc_proto.h"
40 : #include "librpc/rpc/dcerpc_util.h"
41 : #include "libcli/http/http.h"
42 :
43 : struct roh_request_state {
44 : struct http_request *request;
45 : struct http_request *response;
46 : };
47 :
48 : static void roh_send_RPC_DATA_OUT_done(struct tevent_req *subreq);
49 0 : struct tevent_req *roh_send_RPC_DATA_OUT_send(TALLOC_CTX *mem_ctx,
50 : struct loadparm_context *lp_ctx,
51 : struct tevent_context *ev,
52 : struct cli_credentials *credentials,
53 : struct roh_connection *roh,
54 : const char *rpc_server,
55 : uint32_t rpc_server_port,
56 : const char *rpc_proxy,
57 : uint8_t http_auth)
58 : {
59 0 : struct tevent_req *req;
60 0 : struct tevent_req *subreq;
61 0 : struct roh_request_state *state;
62 0 : const char *path;
63 0 : char *query;
64 0 : char *uri;
65 :
66 0 : DEBUG(8, ("%s: Sending RPC_OUT_DATA request\n", __func__));
67 :
68 0 : req = tevent_req_create(mem_ctx, &state, struct roh_request_state);
69 0 : if (req == NULL) {
70 0 : return NULL;
71 : }
72 :
73 0 : state->request = talloc_zero(state, struct http_request);
74 0 : if (tevent_req_nomem(state->request, req)) {
75 0 : return tevent_req_post(req, ev);
76 : }
77 :
78 : /* Build URI, as specified in section 2.2.2 */
79 0 : query = talloc_asprintf(state, "%s:%d", rpc_server, rpc_server_port);
80 0 : if (tevent_req_nomem(query, req)) {
81 0 : return tevent_req_post(req, ev);
82 : }
83 :
84 : /*
85 : * TODO This path changes to "/rpcwithcert/rpcproxy.dll" when using
86 : * certificates
87 : */
88 0 : path = "/rpc/rpcproxy.dll";
89 0 : uri = talloc_asprintf(state, "%s?%s", path, query);
90 0 : if (tevent_req_nomem(uri, req)) {
91 0 : tevent_req_nterror(req, NT_STATUS_NO_MEMORY);
92 0 : return tevent_req_post(req, ev);
93 : }
94 0 : TALLOC_FREE(query);
95 :
96 : /*
97 : * Create the HTTP channel OUT request as specified in the
98 : * section 2.1.2.1.2
99 : */
100 0 : state->request->type = HTTP_REQ_RPC_OUT_DATA;
101 0 : state->request->uri = uri;
102 0 : state->request->body.length = 0;
103 0 : state->request->body.data = NULL;
104 0 : state->request->major = '1';
105 0 : state->request->minor = '0';
106 :
107 0 : http_add_header(state, &state->request->headers,
108 : "Accept", "application/rpc");
109 0 : http_add_header(state, &state->request->headers,
110 : "User-Agent", "MSRPC");
111 0 : http_add_header(state, &state->request->headers,
112 : "Host", rpc_proxy);
113 0 : http_add_header(state, &state->request->headers,
114 : "Connection", "keep-alive");
115 0 : http_add_header(state, &state->request->headers,
116 : "Content-Length", "76");
117 0 : http_add_header(state, &state->request->headers,
118 : "Cache-Control", "no-cache");
119 0 : http_add_header(state, &state->request->headers,
120 : "Pragma", "no-cache");
121 :
122 0 : subreq = http_send_auth_request_send(state,
123 : ev,
124 0 : roh->default_channel_out->http_conn,
125 0 : state->request,
126 : credentials,
127 : lp_ctx,
128 : http_auth);
129 0 : if (tevent_req_nomem(subreq, req)) {
130 0 : return tevent_req_post(req, ev);
131 : }
132 0 : tevent_req_set_callback(subreq, roh_send_RPC_DATA_OUT_done, req);
133 :
134 0 : return req;
135 : }
136 :
137 0 : static void roh_send_RPC_DATA_OUT_done(struct tevent_req *subreq)
138 : {
139 0 : NTSTATUS status;
140 0 : struct tevent_req *req;
141 :
142 0 : req = tevent_req_callback_data(subreq, struct tevent_req);
143 :
144 : /* Receive the sent bytes to check if request has been properly sent */
145 0 : status = http_send_auth_request_recv(subreq);
146 0 : TALLOC_FREE(subreq);
147 0 : if (tevent_req_nterror(req, status)) {
148 0 : return;
149 : }
150 :
151 0 : DEBUG(8, ("%s: RPC_OUT_DATA sent\n", __func__));
152 :
153 0 : tevent_req_done(req);
154 : }
155 :
156 0 : NTSTATUS roh_send_RPC_DATA_OUT_recv(struct tevent_req *req)
157 : {
158 0 : NTSTATUS status;
159 :
160 0 : if (tevent_req_is_nterror(req, &status)) {
161 0 : tevent_req_received(req);
162 0 : return status;
163 : }
164 :
165 0 : tevent_req_received(req);
166 0 : return NT_STATUS_OK;
167 : }
168 :
169 : struct roh_send_pdu_state {
170 : DATA_BLOB buffer;
171 : struct iovec iov;
172 : int bytes_written;
173 : int sys_errno;
174 : };
175 :
176 : static void roh_send_CONN_A1_done(struct tevent_req *subreq);
177 0 : struct tevent_req *roh_send_CONN_A1_send(TALLOC_CTX *mem_ctx,
178 : struct tevent_context *ev,
179 : struct roh_connection *roh)
180 : {
181 0 : struct tevent_req *req;
182 0 : struct tevent_req *subreq;
183 0 : struct roh_send_pdu_state *state;
184 0 : struct dcerpc_rts rts;
185 0 : struct ncacn_packet pkt;
186 0 : struct ndr_push *ndr;
187 0 : struct tstream_context *stream = NULL;
188 0 : struct tevent_queue *send_queue = NULL;
189 :
190 0 : DEBUG(8, ("%s: Sending CONN/A1 request\n", __func__));
191 :
192 0 : req = tevent_req_create(mem_ctx, &state, struct roh_send_pdu_state);
193 0 : if (req == NULL) {
194 0 : return NULL;
195 : }
196 :
197 0 : rts.Flags = RTS_FLAG_NONE;
198 0 : rts.NumberOfCommands = 4;
199 0 : rts.Commands = talloc_array(state, struct dcerpc_rts_cmd, 4);
200 :
201 : /* CONN/A1: Version RTS command */
202 0 : rts.Commands[0].CommandType = 0x00000006;
203 0 : rts.Commands[0].Command.Version.Version = 0x00000001;
204 :
205 : /* CONN/A1: VirtualConnectionCookie RTS command */
206 0 : rts.Commands[1].CommandType = 0x00000003;
207 0 : rts.Commands[1].Command.Cookie.Cookie.Cookie = roh->connection_cookie;
208 :
209 : /* CONN/A1: OutChannelCookie RTS command */
210 0 : rts.Commands[2].CommandType = 0x00000003;
211 0 : rts.Commands[2].Command.Cookie.Cookie.Cookie =
212 0 : roh->default_channel_out->channel_cookie;
213 :
214 : /* CONN/A1: ReceiveWindowSize */
215 0 : rts.Commands[3].CommandType = 0x00000000;
216 0 : rts.Commands[3].Command.ReceiveWindowSize.ReceiveWindowSize = 0x40000;
217 :
218 0 : pkt.rpc_vers = 5;
219 0 : pkt.rpc_vers_minor = 0;
220 0 : pkt.ptype = DCERPC_PKT_RTS;
221 0 : pkt.pfc_flags = DCERPC_PFC_FLAG_LAST | DCERPC_PFC_FLAG_FIRST;
222 0 : pkt.drep[0] = DCERPC_DREP_LE;
223 0 : pkt.drep[1] = 0;
224 0 : pkt.drep[2] = 0;
225 0 : pkt.drep[3] = 0;
226 0 : pkt.frag_length = 76;
227 0 : pkt.auth_length = 0;
228 0 : pkt.call_id = 0;
229 0 : pkt.u.rts = rts;
230 :
231 0 : ndr = ndr_push_init_ctx(state);
232 0 : if (ndr == NULL) {
233 0 : return NULL;
234 : }
235 0 : ndr->offset = 0;
236 0 : ndr_push_ncacn_packet(ndr, NDR_SCALARS, &pkt);
237 :
238 0 : state->buffer = ndr_push_blob(ndr);
239 0 : state->iov.iov_base = (char *) state->buffer.data;
240 0 : state->iov.iov_len = state->buffer.length;
241 :
242 0 : stream = http_conn_tstream(roh->default_channel_out->http_conn);
243 0 : send_queue = http_conn_send_queue(roh->default_channel_out->http_conn);
244 :
245 0 : subreq = tstream_writev_queue_send(mem_ctx,
246 : ev,
247 : stream,
248 : send_queue,
249 0 : &state->iov,
250 : 1);
251 0 : if (tevent_req_nomem(subreq, req)) {
252 0 : return tevent_req_post(req, ev);
253 : }
254 0 : tevent_req_set_callback(subreq, roh_send_CONN_A1_done, req);
255 :
256 0 : return req;
257 : }
258 :
259 0 : static void roh_send_CONN_A1_done(struct tevent_req *subreq)
260 : {
261 0 : NTSTATUS status;
262 0 : struct tevent_req *req;
263 0 : struct roh_send_pdu_state *state;
264 0 : int sys_errno;
265 :
266 0 : req = tevent_req_callback_data(subreq, struct tevent_req);
267 0 : state = tevent_req_data(req, struct roh_send_pdu_state);
268 :
269 0 : state->bytes_written = tstream_writev_queue_recv(subreq, &sys_errno);
270 0 : state->sys_errno = sys_errno;
271 0 : TALLOC_FREE(subreq);
272 0 : if (state->bytes_written <= 0 && sys_errno != 0) {
273 0 : status = map_nt_error_from_unix_common(sys_errno);
274 0 : tevent_req_nterror(req, status);
275 0 : return;
276 : }
277 0 : DEBUG(8, ("%s: CONN/A1 sent (%d bytes written)\n",
278 : __func__, state->bytes_written));
279 :
280 0 : tevent_req_done(req);
281 : }
282 :
283 0 : NTSTATUS roh_send_CONN_A1_recv(struct tevent_req *req)
284 : {
285 0 : NTSTATUS status;
286 :
287 0 : if (tevent_req_is_nterror(req, &status)) {
288 0 : tevent_req_received(req);
289 0 : return status;
290 : }
291 :
292 0 : tevent_req_received(req);
293 0 : return NT_STATUS_OK;
294 : }
295 :
296 : struct roh_recv_response_state
297 : {
298 : struct http_request *response;
299 : };
300 :
301 : static void roh_recv_out_channel_response_done(struct tevent_req *);
302 0 : struct tevent_req *roh_recv_out_channel_response_send(TALLOC_CTX *mem_ctx,
303 : struct tevent_context *ev,
304 : struct roh_connection *roh)
305 : {
306 0 : struct tevent_req *req;
307 0 : struct tevent_req *subreq;
308 0 : struct roh_recv_response_state *state;
309 :
310 0 : DEBUG(8, ("%s: Waiting for RPC_OUT_DATA response\n", __func__));
311 :
312 0 : req = tevent_req_create(mem_ctx, &state, struct roh_recv_response_state);
313 0 : if (req == NULL) {
314 0 : return NULL;
315 : }
316 :
317 0 : subreq = http_read_response_send(state, ev,
318 0 : roh->default_channel_out->http_conn,
319 : 0); /* we'll get the content later */
320 0 : if (tevent_req_nomem(subreq, req)) {
321 0 : return tevent_req_post(req, ev);
322 : }
323 0 : tevent_req_set_callback(subreq, roh_recv_out_channel_response_done, req);
324 :
325 0 : return req;
326 : }
327 :
328 0 : static void roh_recv_out_channel_response_done(struct tevent_req *subreq)
329 : {
330 0 : NTSTATUS status;
331 0 : struct tevent_req *req;
332 0 : struct roh_recv_response_state *state;
333 :
334 0 : req = tevent_req_callback_data(subreq, struct tevent_req);
335 0 : state = tevent_req_data(req, struct roh_recv_response_state);
336 0 : status = http_read_response_recv(subreq, state, &state->response);
337 0 : TALLOC_FREE(subreq);
338 0 : if (tevent_req_nterror(req, status)) {
339 0 : return;
340 : }
341 :
342 0 : DEBUG(8, ("%s: RCP_OUT_DATA response received\n", __func__));
343 :
344 : /* TODO Map response code to nt error */
345 0 : switch (state->response->response_code) {
346 0 : case 200:
347 0 : break;
348 0 : case 401:
349 0 : DEBUG(0, ("%s: Server response: Access denied\n", __func__));
350 0 : tevent_req_nterror(req, NT_STATUS_ACCESS_DENIED);
351 0 : return;
352 0 : case 503:
353 : /* TODO Decode error info as specified in section 2.1.2.1.3 */
354 0 : DEBUG(0, ("%s: Server response: RPC error\n", __func__));
355 0 : tevent_req_nterror(req, NT_STATUS_GENERIC_NOT_MAPPED);
356 0 : return;
357 0 : default:
358 0 : DEBUG(0, ("%s: Server response: Unknown error\n", __func__));
359 0 : tevent_req_nterror(req, NT_STATUS_GENERIC_NOT_MAPPED);
360 0 : return;
361 : }
362 :
363 0 : tevent_req_done(req);
364 : }
365 :
366 0 : NTSTATUS roh_recv_out_channel_response_recv(struct tevent_req *req,
367 : TALLOC_CTX *mem_ctx,
368 : char **response_msg)
369 : {
370 0 : NTSTATUS status;
371 :
372 0 : if (tevent_req_is_nterror(req, &status)) {
373 0 : tevent_req_received(req);
374 0 : return status;
375 : }
376 :
377 0 : tevent_req_received(req);
378 0 : return NT_STATUS_OK;
379 : }
380 :
381 : struct roh_recv_pdu_state {
382 : struct roh_connection *roh;
383 : uint32_t connection_timeout;
384 : uint32_t version;
385 : uint32_t recv_window_size;
386 : };
387 :
388 : static void roh_recv_CONN_A3_done(struct tevent_req *subreq);
389 0 : struct tevent_req *roh_recv_CONN_A3_send(TALLOC_CTX *mem_ctx,
390 : struct tevent_context *ev,
391 : struct roh_connection *roh)
392 : {
393 0 : struct tevent_req *req;
394 0 : struct tevent_req *subreq;
395 0 : struct roh_recv_pdu_state *state;
396 0 : struct tstream_context *stream = NULL;
397 :
398 0 : req = tevent_req_create(mem_ctx, &state, struct roh_recv_pdu_state);
399 0 : if (req == NULL) {
400 0 : return NULL;
401 : }
402 :
403 0 : DEBUG(8, ("%s: Waiting for CONN/A3\n", __func__));
404 :
405 0 : stream = http_conn_tstream(roh->default_channel_out->http_conn);
406 :
407 0 : subreq = dcerpc_read_ncacn_packet_send(state, ev, stream);
408 0 : if (tevent_req_nomem(subreq, req)) {
409 0 : return tevent_req_post(req, ev);
410 : }
411 0 : tevent_req_set_callback(subreq, roh_recv_CONN_A3_done, req);
412 :
413 0 : return req;
414 : }
415 :
416 0 : static void roh_recv_CONN_A3_done(struct tevent_req *subreq)
417 : {
418 0 : NTSTATUS status;
419 0 : struct tevent_req *req;
420 0 : struct roh_recv_pdu_state *state;
421 0 : struct ncacn_packet *pkt;
422 0 : DATA_BLOB buffer;
423 0 : struct dcerpc_rts rts;
424 :
425 0 : req = tevent_req_callback_data(subreq, struct tevent_req);
426 0 : state = tevent_req_data(req, struct roh_recv_pdu_state);
427 0 : status = dcerpc_read_ncacn_packet_recv(subreq, state, &pkt, &buffer);
428 0 : TALLOC_FREE(subreq);
429 :
430 0 : if (tevent_req_nterror(req, status)) {
431 0 : DEBUG(0, ("%s: Error receiving PDU\n", __func__));
432 0 : return;
433 : }
434 :
435 : /*
436 : * Check if it is a CONN/A3 (2.2.4.4) packet and get the connection
437 : * timeout
438 : */
439 0 : rts = pkt->u.rts;
440 0 : if (rts.NumberOfCommands != 1) {
441 0 : DEBUG(0, ("%s: Invalid number of commands received\n", __func__));
442 0 : tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
443 0 : return;
444 : }
445 :
446 0 : if (rts.Commands[0].CommandType != ROH_CMD_TYPE_CONNECTION_TIMEOUT) {
447 0 : DEBUG(0, ("%s: Invalid command type received\n", __func__));
448 0 : tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
449 0 : return;
450 : }
451 :
452 : /* Extract connection timeout */
453 0 : state->connection_timeout = rts.Commands[0].Command.ConnectionTimeout.ConnectionTimeout;
454 :
455 0 : DEBUG(8, ("%s: CONN/A3 received, connection timeout is %u\n",
456 : __func__, state->connection_timeout));
457 0 : tevent_req_done(req);
458 : }
459 :
460 0 : NTSTATUS roh_recv_CONN_A3_recv(struct tevent_req *req,
461 : unsigned int *connection_timeout)
462 : {
463 0 : NTSTATUS status;
464 0 : struct roh_recv_pdu_state *state;
465 :
466 0 : state = tevent_req_data(req, struct roh_recv_pdu_state);
467 0 : if (tevent_req_is_nterror(req, &status)) {
468 0 : tevent_req_received(req);
469 0 : return status;
470 : }
471 :
472 0 : *connection_timeout = state->connection_timeout;
473 :
474 0 : tevent_req_received(req);
475 0 : return NT_STATUS_OK;
476 : }
477 :
478 : static void roh_recv_CONN_C2_done(struct tevent_req *subreq);
479 0 : struct tevent_req *roh_recv_CONN_C2_send(TALLOC_CTX *mem_ctx,
480 : struct tevent_context *ev,
481 : struct roh_connection *roh)
482 : {
483 0 : struct tevent_req *req;
484 0 : struct tevent_req *subreq;
485 0 : struct roh_recv_pdu_state *state;
486 0 : struct tstream_context *stream = NULL;
487 :
488 0 : req = tevent_req_create(mem_ctx, &state, struct roh_recv_pdu_state);
489 0 : if (req == NULL) {
490 0 : return NULL;
491 : }
492 :
493 0 : DEBUG(8, ("%s: Waiting for CONN/C2\n", __func__));
494 0 : stream = http_conn_tstream(roh->default_channel_out->http_conn);
495 :
496 0 : subreq = dcerpc_read_ncacn_packet_send(state, ev, stream);
497 0 : if (tevent_req_nomem(subreq, req)) {
498 0 : return tevent_req_post(req, ev);
499 : }
500 0 : tevent_req_set_callback(subreq, roh_recv_CONN_C2_done, req);
501 :
502 0 : return req;
503 : }
504 :
505 0 : static void roh_recv_CONN_C2_done(struct tevent_req *subreq)
506 : {
507 0 : NTSTATUS status;
508 0 : struct tevent_req *req;
509 0 : struct roh_recv_pdu_state *state;
510 0 : struct ncacn_packet *pkt;
511 0 : DATA_BLOB buffer;
512 0 : struct dcerpc_rts rts;
513 :
514 0 : req = tevent_req_callback_data(subreq, struct tevent_req);
515 0 : state = tevent_req_data(req, struct roh_recv_pdu_state);
516 :
517 0 : status = dcerpc_read_ncacn_packet_recv(subreq, state, &pkt, &buffer);
518 0 : TALLOC_FREE(subreq);
519 0 : if (tevent_req_nterror(req, status)) {
520 0 : DEBUG(0, ("%s: Error receiving PDU\n", __func__));
521 0 : return;
522 : }
523 :
524 : /*
525 : * Check if it is a CONN/C2 packet (2.2.4.9), and get the version, the
526 : * receive windows size and the connection timeout for the IN channel
527 : */
528 0 : rts = pkt->u.rts;
529 0 : if (rts.NumberOfCommands != 3) {
530 0 : DEBUG(0, ("%s: Invalid number of commands received\n",
531 : __func__));
532 0 : tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
533 0 : return;
534 : }
535 0 : if (rts.Commands[0].CommandType != ROH_CMD_TYPE_VERSION) {
536 0 : DEBUG(0, ("%s: Invalid command type received\n", __func__));
537 0 : tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
538 0 : return;
539 : }
540 0 : if (rts.Commands[1].CommandType != ROH_CMD_TYPE_RECV_WINDOWS_SIZE) {
541 0 : DEBUG(0, ("%s: Invalid command type received\n", __func__));
542 0 : tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
543 0 : return;
544 : }
545 0 : if (rts.Commands[2].CommandType != ROH_CMD_TYPE_CONNECTION_TIMEOUT) {
546 0 : DEBUG(0, ("%s: Invalid command type received\n", __func__));
547 0 : tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
548 0 : return;
549 : }
550 :
551 : /* Extract data */
552 0 : state->version = rts.Commands[0].Command.Version.Version;
553 0 : state->recv_window_size = rts.Commands[1].Command.ReceiveWindowSize.ReceiveWindowSize;
554 0 : state->connection_timeout = rts.Commands[2].Command.ConnectionTimeout.ConnectionTimeout;
555 :
556 0 : DEBUG(8, ("%s: CONN/C2 received, version is %u, receive windows size is %u, connection timeout is %u\n",
557 : __func__, state->version, state->recv_window_size,
558 : state->connection_timeout));
559 0 : tevent_req_done(req);
560 : }
561 :
562 0 : NTSTATUS roh_recv_CONN_C2_recv(struct tevent_req *req,
563 : unsigned int *version,
564 : unsigned int *recv_window_size,
565 : unsigned int *connection_timeout)
566 : {
567 0 : NTSTATUS status;
568 0 : struct roh_recv_pdu_state *state;
569 :
570 0 : if (tevent_req_is_nterror(req, &status)) {
571 0 : tevent_req_received(req);
572 0 : return status;
573 : }
574 :
575 0 : state = tevent_req_data(req, struct roh_recv_pdu_state);
576 0 : *version = state->version;
577 0 : *recv_window_size = state->recv_window_size;
578 0 : *connection_timeout = state->connection_timeout;
579 :
580 0 : tevent_req_received(req);
581 0 : return NT_STATUS_OK;
582 : }
|