s4:lib/http: use http_conn in http_send_request_send() and http_read_response_send()
[Samba.git] / source4 / librpc / rpc / dcerpc_roh_channel_out.c
blob491bbadfa693be5616919fc8f410ecd0f4262612
1 /*
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/>.
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>
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,
56 bool tls,
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);
68 if (req == NULL) {
69 return NULL;
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);
79 state->ev = ev;
80 state->credentials = credentials;
81 state->roh = roh;
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,
93 ev,
94 rpcproxy_ip_address,
95 rpcproxy_port,
96 credentials,
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);
103 return 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;
110 NTSTATUS status;
111 int ret;
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);
119 TALLOC_FREE(subreq);
120 if (ret != 0) {
121 status = map_nt_error_from_unix_common(ret);
122 tevent_req_nterror(req, status);
123 return;
126 DBG_DEBUG("HTTP connected\n");
127 tevent_req_done(req);
130 NTSTATUS roh_connect_channel_out_recv(struct tevent_req *req)
132 NTSTATUS status;
134 if (tevent_req_is_nterror(req, &status)) {
135 tevent_req_received(req);
136 return status;
139 tevent_req_received(req);
140 return NT_STATUS_OK;
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,
157 uint8_t http_auth)
159 struct tevent_req *req;
160 struct tevent_req *subreq;
161 struct roh_request_state *state;
162 const char *path;
163 char *query;
164 char *uri;
166 DEBUG(8, ("%s: Sending RPC_OUT_DATA request\n", __func__));
168 req = tevent_req_create(mem_ctx, &state, struct roh_request_state);
169 if (req == NULL) {
170 return NULL;
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
186 * certificates
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);
194 TALLOC_FREE(query);
197 * Create the HTTP channel OUT request as specified in the
198 * section 2.1.2.1.2
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,
212 "Host", rpc_proxy);
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,
225 state->request,
226 credentials,
227 lp_ctx,
228 http_auth);
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);
234 return req;
237 static void roh_send_RPC_DATA_OUT_done(struct tevent_req *subreq)
239 NTSTATUS status;
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);
246 TALLOC_FREE(subreq);
247 if (tevent_req_nterror(req, status)) {
248 return;
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)
258 NTSTATUS status;
260 if (tevent_req_is_nterror(req, &status)) {
261 tevent_req_received(req);
262 return status;
265 tevent_req_received(req);
266 return NT_STATUS_OK;
269 struct roh_send_pdu_state {
270 DATA_BLOB buffer;
271 struct iovec iov;
272 int bytes_written;
273 int sys_errno;
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);
293 if (req == NULL) {
294 return NULL;
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;
318 pkt.rpc_vers = 5;
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;
323 pkt.drep[1] = 0;
324 pkt.drep[2] = 0;
325 pkt.drep[3] = 0;
326 pkt.frag_length = 76;
327 pkt.auth_length = 0;
328 pkt.call_id = 0;
329 pkt.u.rts = rts;
331 ndr = ndr_push_init_ctx(state);
332 if (ndr == NULL) {
333 return NULL;
335 ndr->offset = 0;
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,
347 stream,
348 send_queue,
349 &state->iov,
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);
356 return req;
359 static void roh_send_CONN_A1_done(struct tevent_req *subreq)
361 NTSTATUS status;
362 struct tevent_req *req;
363 struct roh_send_pdu_state *state;
364 int sys_errno;
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;
371 TALLOC_FREE(subreq);
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);
375 return;
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)
385 NTSTATUS status;
387 if (tevent_req_is_nterror(req, &status)) {
388 tevent_req_received(req);
389 return status;
392 tevent_req_received(req);
393 return NT_STATUS_OK;
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);
413 if (req == NULL) {
414 return NULL;
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);
425 return req;
428 static void roh_recv_out_channel_response_done(struct tevent_req *subreq)
430 NTSTATUS status;
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);
437 TALLOC_FREE(subreq);
438 if (tevent_req_nterror(req, status)) {
439 return;
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) {
446 case 200:
447 break;
448 case 401:
449 DEBUG(0, ("%s: Server response: Access denied\n", __func__));
450 tevent_req_nterror(req, NT_STATUS_ACCESS_DENIED);
451 return;
452 case 503:
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);
456 return;
457 default:
458 DEBUG(0, ("%s: Server response: Unknown error\n", __func__));
459 tevent_req_nterror(req, NT_STATUS_GENERIC_NOT_MAPPED);
460 return;
463 tevent_req_done(req);
466 NTSTATUS roh_recv_out_channel_response_recv(struct tevent_req *req,
467 TALLOC_CTX *mem_ctx,
468 char **response_msg)
470 NTSTATUS status;
472 if (tevent_req_is_nterror(req, &status)) {
473 tevent_req_received(req);
474 return status;
477 tevent_req_received(req);
478 return NT_STATUS_OK;
481 struct roh_recv_pdu_state {
482 struct roh_connection *roh;
483 uint32_t connection_timeout;
484 uint32_t version;
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);
499 if (req == NULL) {
500 return NULL;
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);
513 return req;
516 static void roh_recv_CONN_A3_done(struct tevent_req *subreq)
518 NTSTATUS status;
519 struct tevent_req *req;
520 struct roh_recv_pdu_state *state;
521 struct ncacn_packet *pkt;
522 DATA_BLOB buffer;
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);
528 TALLOC_FREE(subreq);
530 if (tevent_req_nterror(req, status)) {
531 DEBUG(0, ("%s: Error receiving PDU\n", __func__));
532 return;
536 * Check if it is a CONN/A3 (2.2.4.4) packet and get the connection
537 * timeout
539 rts = pkt->u.rts;
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);
543 return;
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);
549 return;
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)
563 NTSTATUS status;
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);
569 return status;
572 *connection_timeout = state->connection_timeout;
574 tevent_req_received(req);
575 return NT_STATUS_OK;
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);
589 if (req == NULL) {
590 return NULL;
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);
602 return req;
605 static void roh_recv_CONN_C2_done(struct tevent_req *subreq)
607 NTSTATUS status;
608 struct tevent_req *req;
609 struct roh_recv_pdu_state *state;
610 struct ncacn_packet *pkt;
611 DATA_BLOB buffer;
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);
618 TALLOC_FREE(subreq);
619 if (tevent_req_nterror(req, status)) {
620 DEBUG(0, ("%s: Error receiving PDU\n", __func__));
621 return;
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
628 rts = pkt->u.rts;
629 if (rts.NumberOfCommands != 3) {
630 DEBUG(0, ("%s: Invalid number of commands received\n",
631 __func__));
632 tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
633 return;
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);
638 return;
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);
643 return;
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);
648 return;
651 /* Extract data */
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)
667 NTSTATUS status;
668 struct roh_recv_pdu_state *state;
670 if (tevent_req_is_nterror(req, &status)) {
671 tevent_req_received(req);
672 return status;
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);
681 return NT_STATUS_OK;