2 Unix SMB/CIFS implementation.
4 [MS-RPCH] - RPC over HTTP client
6 Copyright (C) 2013 Samuel Cabrero <samuelcabrero@kernevil.me>
7 Copyright (C) Julien Kerihuel <j.kerihuel@openchange.org> 2013
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.
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.
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/>.
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>
37 #include "librpc/rpc/dcerpc.h"
38 #include "librpc/rpc/dcerpc_roh.h"
39 #include "librpc/rpc/dcerpc_proto.h"
40 #include "lib/http/http.h"
42 struct roh_connect_channel_state
{
43 struct tevent_context
*ev
;
44 struct cli_credentials
*credentials
;
45 struct roh_connection
*roh
;
46 struct tstream_tls_params
*tls_params
;
49 static void roh_connect_channel_out_done(struct tevent_req
*subreq
);
50 struct tevent_req
*roh_connect_channel_out_send(TALLOC_CTX
*mem_ctx
,
51 struct tevent_context
*ev
,
52 const char *rpcproxy_ip_address
,
53 unsigned int rpcproxy_port
,
54 struct cli_credentials
*credentials
,
55 struct roh_connection
*roh
,
57 struct tstream_tls_params
*tls_params
)
59 struct tevent_req
*req
;
60 struct tevent_req
*subreq
;
61 struct roh_connect_channel_state
*state
;
63 DEBUG(8, ("%s: Connecting channel out socket, RPC proxy is %s:%d (TLS: %s)\n",
64 __func__
, rpcproxy_ip_address
, rpcproxy_port
,
65 (tls
? "true" : "false")));
67 req
= tevent_req_create(mem_ctx
, &state
, struct roh_connect_channel_state
);
72 if (!is_ipaddress(rpcproxy_ip_address
)) {
73 DEBUG(0, ("%s: Invalid host (%s), needs to be an IP address\n",
74 __func__
, rpcproxy_ip_address
));
75 tevent_req_nterror(req
, NT_STATUS_INVALID_PARAMETER
);
76 return tevent_req_post(req
, ev
);
80 state
->credentials
= credentials
;
82 state
->tls_params
= tls_params
;
84 /* Initialize channel structure */
85 state
->roh
->default_channel_out
= talloc_zero(roh
, struct roh_channel
);
86 if (tevent_req_nomem(state
->roh
->default_channel_out
, req
)) {
87 return tevent_req_post(req
, ev
);
90 state
->roh
->default_channel_out
->channel_cookie
= GUID_random();
92 subreq
= http_connect_send(state
,
97 tls
? tls_params
: NULL
);
98 if (tevent_req_nomem(subreq
, req
)) {
99 return tevent_req_post(req
, ev
);
101 tevent_req_set_callback(subreq
, roh_connect_channel_out_done
, req
);
106 static void roh_connect_channel_out_done(struct tevent_req
*subreq
)
108 struct tevent_req
*req
;
109 struct roh_connect_channel_state
*state
;
113 req
= tevent_req_callback_data(subreq
, struct tevent_req
);
114 state
= tevent_req_data(req
, struct roh_connect_channel_state
);
116 ret
= http_connect_recv(subreq
,
117 state
->roh
->default_channel_out
,
118 &state
->roh
->default_channel_out
->http_conn
);
121 status
= map_nt_error_from_unix_common(ret
);
122 tevent_req_nterror(req
, status
);
126 DBG_DEBUG("HTTP connected\n");
127 tevent_req_done(req
);
130 NTSTATUS
roh_connect_channel_out_recv(struct tevent_req
*req
)
134 if (tevent_req_is_nterror(req
, &status
)) {
135 tevent_req_received(req
);
139 tevent_req_received(req
);
143 struct roh_request_state
{
144 struct http_request
*request
;
145 struct http_request
*response
;
148 static void roh_send_RPC_DATA_OUT_done(struct tevent_req
*subreq
);
149 struct tevent_req
*roh_send_RPC_DATA_OUT_send(TALLOC_CTX
*mem_ctx
,
150 struct loadparm_context
*lp_ctx
,
151 struct tevent_context
*ev
,
152 struct cli_credentials
*credentials
,
153 struct roh_connection
*roh
,
154 const char *rpc_server
,
155 uint32_t rpc_server_port
,
156 const char *rpc_proxy
,
159 struct tevent_req
*req
;
160 struct tevent_req
*subreq
;
161 struct roh_request_state
*state
;
166 DEBUG(8, ("%s: Sending RPC_OUT_DATA request\n", __func__
));
168 req
= tevent_req_create(mem_ctx
, &state
, struct roh_request_state
);
173 state
->request
= talloc_zero(state
, struct http_request
);
174 if (tevent_req_nomem(state
->request
, req
)) {
175 return tevent_req_post(req
, ev
);
178 /* Build URI, as specified in section 2.2.2 */
179 query
= talloc_asprintf(state
, "%s:%d", rpc_server
, rpc_server_port
);
180 if (tevent_req_nomem(query
, req
)) {
181 return tevent_req_post(req
, ev
);
185 * TODO This path changes to "/rpcwithcert/rpcproxy.dll" when using
188 path
= "/rpc/rpcproxy.dll";
189 uri
= talloc_asprintf(state
, "%s?%s", path
, query
);
190 if (tevent_req_nomem(uri
, req
)) {
191 tevent_req_nterror(req
, NT_STATUS_NO_MEMORY
);
192 return tevent_req_post(req
, ev
);
197 * Create the HTTP channel OUT request as specified in the
200 state
->request
->type
= HTTP_REQ_RPC_OUT_DATA
;
201 state
->request
->uri
= uri
;
202 state
->request
->body
.length
= 0;
203 state
->request
->body
.data
= NULL
;
204 state
->request
->major
= '1';
205 state
->request
->minor
= '0';
207 http_add_header(state
, &state
->request
->headers
,
208 "Accept", "application/rpc");
209 http_add_header(state
, &state
->request
->headers
,
210 "User-Agent", "MSRPC");
211 http_add_header(state
, &state
->request
->headers
,
213 http_add_header(state
, &state
->request
->headers
,
214 "Connection", "keep-alive");
215 http_add_header(state
, &state
->request
->headers
,
216 "Content-Length", "76");
217 http_add_header(state
, &state
->request
->headers
,
218 "Cache-Control", "no-cache");
219 http_add_header(state
, &state
->request
->headers
,
220 "Pragma", "no-cache");
222 subreq
= http_send_auth_request_send(state
,
224 roh
->default_channel_out
->http_conn
,
229 if (tevent_req_nomem(subreq
, req
)) {
230 return tevent_req_post(req
, ev
);
232 tevent_req_set_callback(subreq
, roh_send_RPC_DATA_OUT_done
, req
);
237 static void roh_send_RPC_DATA_OUT_done(struct tevent_req
*subreq
)
240 struct tevent_req
*req
;
242 req
= tevent_req_callback_data(subreq
, struct tevent_req
);
244 /* Receive the sent bytes to check if request has been properly sent */
245 status
= http_send_auth_request_recv(subreq
);
247 if (tevent_req_nterror(req
, status
)) {
251 DEBUG(8, ("%s: RPC_OUT_DATA sent", __func__
));
253 tevent_req_done(req
);
256 NTSTATUS
roh_send_RPC_DATA_OUT_recv(struct tevent_req
*req
)
260 if (tevent_req_is_nterror(req
, &status
)) {
261 tevent_req_received(req
);
265 tevent_req_received(req
);
269 struct roh_send_pdu_state
{
276 static void roh_send_CONN_A1_done(struct tevent_req
*subreq
);
277 struct tevent_req
*roh_send_CONN_A1_send(TALLOC_CTX
*mem_ctx
,
278 struct tevent_context
*ev
,
279 struct roh_connection
*roh
)
281 struct tevent_req
*req
;
282 struct tevent_req
*subreq
;
283 struct roh_send_pdu_state
*state
;
284 struct dcerpc_rts rts
;
285 struct ncacn_packet pkt
;
286 struct ndr_push
*ndr
;
287 struct tstream_context
*stream
= NULL
;
288 struct tevent_queue
*send_queue
= NULL
;
290 DEBUG(8, ("%s: Sending CONN/A1 request\n", __func__
));
292 req
= tevent_req_create(mem_ctx
, &state
, struct roh_send_pdu_state
);
297 rts
.Flags
= RTS_FLAG_NONE
;
298 rts
.NumberOfCommands
= 4;
299 rts
.Commands
= talloc_array(state
, struct dcerpc_rts_cmd
, 4);
301 /* CONN/A1: Version RTS command */
302 rts
.Commands
[0].CommandType
= 0x00000006;
303 rts
.Commands
[0].Command
.Version
.Version
= 0x00000001;
305 /* CONN/A1: VirtualConnectionCookie RTS command */
306 rts
.Commands
[1].CommandType
= 0x00000003;
307 rts
.Commands
[1].Command
.Cookie
.Cookie
.Cookie
= roh
->connection_cookie
;
309 /* CONN/A1: OutChannelCookie RTS command */
310 rts
.Commands
[2].CommandType
= 0x00000003;
311 rts
.Commands
[2].Command
.Cookie
.Cookie
.Cookie
=
312 roh
->default_channel_out
->channel_cookie
;
314 /* CONN/A1: ReceiveWindowSize */
315 rts
.Commands
[3].CommandType
= 0x00000000;
316 rts
.Commands
[3].Command
.ReceiveWindowSize
.ReceiveWindowSize
= 0x40000;
319 pkt
.rpc_vers_minor
= 0;
320 pkt
.ptype
= DCERPC_PKT_RTS
;
321 pkt
.pfc_flags
= DCERPC_PFC_FLAG_LAST
| DCERPC_PFC_FLAG_FIRST
;
322 pkt
.drep
[0] = DCERPC_DREP_LE
;
326 pkt
.frag_length
= 76;
331 ndr
= ndr_push_init_ctx(state
);
336 ndr_push_ncacn_packet(ndr
, NDR_SCALARS
, &pkt
);
338 state
->buffer
= ndr_push_blob(ndr
);
339 state
->iov
.iov_base
= (char *) state
->buffer
.data
;
340 state
->iov
.iov_len
= state
->buffer
.length
;
342 stream
= http_conn_tstream(roh
->default_channel_out
->http_conn
);
343 send_queue
= http_conn_send_queue(roh
->default_channel_out
->http_conn
);
345 subreq
= tstream_writev_queue_send(mem_ctx
,
351 if (tevent_req_nomem(subreq
, req
)) {
352 return tevent_req_post(req
, ev
);
354 tevent_req_set_callback(subreq
, roh_send_CONN_A1_done
, req
);
359 static void roh_send_CONN_A1_done(struct tevent_req
*subreq
)
362 struct tevent_req
*req
;
363 struct roh_send_pdu_state
*state
;
366 req
= tevent_req_callback_data(subreq
, struct tevent_req
);
367 state
= tevent_req_data(req
, struct roh_send_pdu_state
);
369 state
->bytes_written
= tstream_writev_queue_recv(subreq
, &sys_errno
);
370 state
->sys_errno
= sys_errno
;
372 if (state
->bytes_written
<= 0 && sys_errno
!= 0) {
373 status
= map_nt_error_from_unix_common(sys_errno
);
374 tevent_req_nterror(req
, status
);
377 DEBUG(8, ("%s: CONN/A1 sent (%d bytes written)\n",
378 __func__
, state
->bytes_written
));
380 tevent_req_done(req
);
383 NTSTATUS
roh_send_CONN_A1_recv(struct tevent_req
*req
)
387 if (tevent_req_is_nterror(req
, &status
)) {
388 tevent_req_received(req
);
392 tevent_req_received(req
);
396 struct roh_recv_response_state
398 struct http_request
*response
;
401 static void roh_recv_out_channel_response_done(struct tevent_req
*);
402 struct tevent_req
*roh_recv_out_channel_response_send(TALLOC_CTX
*mem_ctx
,
403 struct tevent_context
*ev
,
404 struct roh_connection
*roh
)
406 struct tevent_req
*req
;
407 struct tevent_req
*subreq
;
408 struct roh_recv_response_state
*state
;
410 DEBUG(8, ("%s: Waiting for RPC_OUT_DATA response\n", __func__
));
412 req
= tevent_req_create(mem_ctx
, &state
, struct roh_recv_response_state
);
417 subreq
= http_read_response_send(state
, ev
,
418 roh
->default_channel_out
->http_conn
,
419 0); /* we'll get the content later */
420 if (tevent_req_nomem(subreq
, req
)) {
421 return tevent_req_post(req
, ev
);
423 tevent_req_set_callback(subreq
, roh_recv_out_channel_response_done
, req
);
428 static void roh_recv_out_channel_response_done(struct tevent_req
*subreq
)
431 struct tevent_req
*req
;
432 struct roh_recv_response_state
*state
;
434 req
= tevent_req_callback_data(subreq
, struct tevent_req
);
435 state
= tevent_req_data(req
, struct roh_recv_response_state
);
436 status
= http_read_response_recv(subreq
, state
, &state
->response
);
438 if (tevent_req_nterror(req
, status
)) {
442 DEBUG(8, ("%s: RCP_OUT_DATA response received\n", __func__
));
444 /* TODO Map response code to nt error */
445 switch (state
->response
->response_code
) {
449 DEBUG(0, ("%s: Server response: Access denied\n", __func__
));
450 tevent_req_nterror(req
, NT_STATUS_ACCESS_DENIED
);
453 /* TODO Decode error info as specified in section 2.1.2.1.3 */
454 DEBUG(0, ("%s: Server response: RPC error\n", __func__
));
455 tevent_req_nterror(req
, NT_STATUS_GENERIC_NOT_MAPPED
);
458 DEBUG(0, ("%s: Server response: Unknown error\n", __func__
));
459 tevent_req_nterror(req
, NT_STATUS_GENERIC_NOT_MAPPED
);
463 tevent_req_done(req
);
466 NTSTATUS
roh_recv_out_channel_response_recv(struct tevent_req
*req
,
472 if (tevent_req_is_nterror(req
, &status
)) {
473 tevent_req_received(req
);
477 tevent_req_received(req
);
481 struct roh_recv_pdu_state
{
482 struct roh_connection
*roh
;
483 uint32_t connection_timeout
;
485 uint32_t recv_window_size
;
488 static void roh_recv_CONN_A3_done(struct tevent_req
*subreq
);
489 struct tevent_req
*roh_recv_CONN_A3_send(TALLOC_CTX
*mem_ctx
,
490 struct tevent_context
*ev
,
491 struct roh_connection
*roh
)
493 struct tevent_req
*req
;
494 struct tevent_req
*subreq
;
495 struct roh_recv_pdu_state
*state
;
496 struct tstream_context
*stream
= NULL
;
498 req
= tevent_req_create(mem_ctx
, &state
, struct roh_recv_pdu_state
);
503 DEBUG(8, ("%s: Waiting for CONN/A3\n", __func__
));
505 stream
= http_conn_tstream(roh
->default_channel_out
->http_conn
);
507 subreq
= dcerpc_read_ncacn_packet_send(state
, ev
, stream
);
508 if (tevent_req_nomem(subreq
, req
)) {
509 return tevent_req_post(req
, ev
);
511 tevent_req_set_callback(subreq
, roh_recv_CONN_A3_done
, req
);
516 static void roh_recv_CONN_A3_done(struct tevent_req
*subreq
)
519 struct tevent_req
*req
;
520 struct roh_recv_pdu_state
*state
;
521 struct ncacn_packet
*pkt
;
523 struct dcerpc_rts rts
;
525 req
= tevent_req_callback_data(subreq
, struct tevent_req
);
526 state
= tevent_req_data(req
, struct roh_recv_pdu_state
);
527 status
= dcerpc_read_ncacn_packet_recv(subreq
, state
, &pkt
, &buffer
);
530 if (tevent_req_nterror(req
, status
)) {
531 DEBUG(0, ("%s: Error receiving PDU\n", __func__
));
536 * Check if it is a CONN/A3 (2.2.4.4) packet and get the connection
540 if (rts
.NumberOfCommands
!= 1) {
541 DEBUG(0, ("%s: Invalid number of commands received\n", __func__
));
542 tevent_req_nterror(req
, NT_STATUS_INVALID_PARAMETER
);
546 if (rts
.Commands
[0].CommandType
!= ROH_CMD_TYPE_CONNECTION_TIMEOUT
) {
547 DEBUG(0, ("%s: Invalid command type received\n", __func__
));
548 tevent_req_nterror(req
, NT_STATUS_INVALID_PARAMETER
);
552 /* Extract connection timeout */
553 state
->connection_timeout
= rts
.Commands
[0].Command
.ConnectionTimeout
.ConnectionTimeout
;
555 DEBUG(8, ("%s: CONN/A3 received, connection timeout is %u\n",
556 __func__
, state
->connection_timeout
));
557 tevent_req_done(req
);
560 NTSTATUS
roh_recv_CONN_A3_recv(struct tevent_req
*req
,
561 unsigned int *connection_timeout
)
564 struct roh_recv_pdu_state
*state
;
566 state
= tevent_req_data(req
, struct roh_recv_pdu_state
);
567 if (tevent_req_is_nterror(req
, &status
)) {
568 tevent_req_received(req
);
572 *connection_timeout
= state
->connection_timeout
;
574 tevent_req_received(req
);
578 static void roh_recv_CONN_C2_done(struct tevent_req
*subreq
);
579 struct tevent_req
*roh_recv_CONN_C2_send(TALLOC_CTX
*mem_ctx
,
580 struct tevent_context
*ev
,
581 struct roh_connection
*roh
)
583 struct tevent_req
*req
;
584 struct tevent_req
*subreq
;
585 struct roh_recv_pdu_state
*state
;
586 struct tstream_context
*stream
= NULL
;
588 req
= tevent_req_create(mem_ctx
, &state
, struct roh_recv_pdu_state
);
593 DEBUG(8, ("%s: Waiting for CONN/C2\n", __func__
));
594 stream
= http_conn_tstream(roh
->default_channel_out
->http_conn
);
596 subreq
= dcerpc_read_ncacn_packet_send(state
, ev
, stream
);
597 if (tevent_req_nomem(subreq
, req
)) {
598 return tevent_req_post(req
, ev
);
600 tevent_req_set_callback(subreq
, roh_recv_CONN_C2_done
, req
);
605 static void roh_recv_CONN_C2_done(struct tevent_req
*subreq
)
608 struct tevent_req
*req
;
609 struct roh_recv_pdu_state
*state
;
610 struct ncacn_packet
*pkt
;
612 struct dcerpc_rts rts
;
614 req
= tevent_req_callback_data(subreq
, struct tevent_req
);
615 state
= tevent_req_data(req
, struct roh_recv_pdu_state
);
617 status
= dcerpc_read_ncacn_packet_recv(subreq
, state
, &pkt
, &buffer
);
619 if (tevent_req_nterror(req
, status
)) {
620 DEBUG(0, ("%s: Error receiving PDU\n", __func__
));
625 * Check if it is a CONN/C2 packet (2.2.4.9), and get the version, the
626 * receive windows size and the connection timeout for the IN channel
629 if (rts
.NumberOfCommands
!= 3) {
630 DEBUG(0, ("%s: Invalid number of commands received\n",
632 tevent_req_nterror(req
, NT_STATUS_INVALID_PARAMETER
);
635 if (rts
.Commands
[0].CommandType
!= ROH_CMD_TYPE_VERSION
) {
636 DEBUG(0, ("%s: Invalid command type received\n", __func__
));
637 tevent_req_nterror(req
, NT_STATUS_INVALID_PARAMETER
);
640 if (rts
.Commands
[1].CommandType
!= ROH_CMD_TYPE_RECV_WINDOWS_SIZE
) {
641 DEBUG(0, ("%s: Invalid command type received\n", __func__
));
642 tevent_req_nterror(req
, NT_STATUS_INVALID_PARAMETER
);
645 if (rts
.Commands
[2].CommandType
!= ROH_CMD_TYPE_CONNECTION_TIMEOUT
) {
646 DEBUG(0, ("%s: Invalid command type received\n", __func__
));
647 tevent_req_nterror(req
, NT_STATUS_INVALID_PARAMETER
);
652 state
->version
= rts
.Commands
[0].Command
.Version
.Version
;
653 state
->recv_window_size
= rts
.Commands
[1].Command
.ReceiveWindowSize
.ReceiveWindowSize
;
654 state
->connection_timeout
= rts
.Commands
[2].Command
.ConnectionTimeout
.ConnectionTimeout
;
656 DEBUG(8, ("%s: CONN/C2 received, version is %u, receive windows size is %u, connection timeout is %u\n",
657 __func__
, state
->version
, state
->recv_window_size
,
658 state
->connection_timeout
));
659 tevent_req_done(req
);
662 NTSTATUS
roh_recv_CONN_C2_recv(struct tevent_req
*req
,
663 unsigned int *version
,
664 unsigned int *recv_window_size
,
665 unsigned int *connection_timeout
)
668 struct roh_recv_pdu_state
*state
;
670 if (tevent_req_is_nterror(req
, &status
)) {
671 tevent_req_received(req
);
675 state
= tevent_req_data(req
, struct roh_recv_pdu_state
);
676 *version
= state
->version
;
677 *recv_window_size
= state
->recv_window_size
;
678 *connection_timeout
= state
->connection_timeout
;
680 tevent_req_received(req
);