2 Unix SMB/CIFS implementation.
4 dcerpc over SMB transport
6 Copyright (C) Tim Potter 2003
7 Copyright (C) Andrew Tridgell 2003
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/>.
24 #include "system/filesys.h"
26 #include "lib/tsocket/tsocket.h"
27 #include "libcli/smb/smb_constants.h"
28 #include "libcli/smb/smbXcli_base.h"
29 #include "libcli/smb/tstream_smbXcli_np.h"
30 #include "libcli/raw/libcliraw.h"
31 #include "librpc/rpc/dcerpc.h"
32 #include "librpc/rpc/dcerpc_proto.h"
33 #include "libcli/composite/composite.h"
35 /* transport private information used by SMB pipe transport */
37 DATA_BLOB session_key;
38 const char *server_name;
40 struct tstream_context *stream;
41 struct tevent_queue *write_queue;
42 struct tevent_req *read_subreq;
43 uint32_t pending_reads;
46 * these are needed to open a secondary connection
48 struct smbXcli_conn *conn;
49 struct smbXcli_session *session;
50 struct smbXcli_tcon *tcon;
51 uint32_t timeout_msec;
56 tell the dcerpc layer that the transport is dead
58 static void pipe_dead(struct dcecli_connection *c, NTSTATUS status)
60 struct smb_private *smb = talloc_get_type_abort(
61 c->transport.private_data, struct smb_private);
63 if (smb->stream == NULL) {
67 tevent_queue_stop(smb->write_queue);
68 TALLOC_FREE(smb->read_subreq);
69 TALLOC_FREE(smb->stream);
71 if (NT_STATUS_EQUAL(NT_STATUS_UNSUCCESSFUL, status)) {
72 status = NT_STATUS_UNEXPECTED_NETWORK_ERROR;
75 if (NT_STATUS_EQUAL(NT_STATUS_OK, status)) {
76 status = NT_STATUS_END_OF_FILE;
79 if (c->transport.recv_data) {
80 c->transport.recv_data(c, NULL, status);
84 struct smb_send_read_state {
85 struct dcecli_connection *p;
88 static int smb_send_read_state_destructor(struct smb_send_read_state *state)
90 struct dcecli_connection *p = state->p;
91 struct smb_private *sock = talloc_get_type_abort(
92 p->transport.private_data, struct smb_private);
94 sock->read_subreq = NULL;
99 static void smb_send_read_done(struct tevent_req *subreq);
101 static NTSTATUS smb_send_read(struct dcecli_connection *p)
103 struct smb_private *sock = talloc_get_type_abort(
104 p->transport.private_data, struct smb_private);
105 struct smb_send_read_state *state;
107 if (sock->read_subreq != NULL) {
108 sock->pending_reads++;
112 state = talloc_zero(sock, struct smb_send_read_state);
114 return NT_STATUS_NO_MEMORY;
118 talloc_set_destructor(state, smb_send_read_state_destructor);
120 sock->read_subreq = dcerpc_read_ncacn_packet_send(state,
123 if (sock->read_subreq == NULL) {
124 return NT_STATUS_NO_MEMORY;
126 tevent_req_set_callback(sock->read_subreq, smb_send_read_done, state);
131 static void smb_send_read_done(struct tevent_req *subreq)
133 struct smb_send_read_state *state =
134 tevent_req_callback_data(subreq,
135 struct smb_send_read_state);
136 struct dcecli_connection *p = state->p;
137 struct smb_private *sock = talloc_get_type_abort(
138 p->transport.private_data, struct smb_private);
140 struct ncacn_packet *pkt;
143 status = dcerpc_read_ncacn_packet_recv(subreq, state,
146 if (!NT_STATUS_IS_OK(status)) {
148 pipe_dead(p, status);
153 * here we steal into thet connection context,
154 * but p->transport.recv_data() will steal or free it again
156 talloc_steal(p, blob.data);
159 if (sock->pending_reads > 0) {
160 sock->pending_reads--;
162 status = smb_send_read(p);
163 if (!NT_STATUS_IS_OK(status)) {
164 pipe_dead(p, status);
169 if (p->transport.recv_data) {
170 p->transport.recv_data(p, &blob, NT_STATUS_OK);
175 send an initial pdu in a multi-pdu sequence
178 struct smb_send_request_state {
179 struct dcecli_connection *p;
184 static int smb_send_request_state_destructor(struct smb_send_request_state *state)
186 struct dcecli_connection *p = state->p;
187 struct smb_private *sock = talloc_get_type_abort(
188 p->transport.private_data, struct smb_private);
190 sock->read_subreq = NULL;
195 static void smb_send_request_wait_done(struct tevent_req *subreq);
196 static void smb_send_request_done(struct tevent_req *subreq);
198 static NTSTATUS smb_send_request(struct dcecli_connection *p, DATA_BLOB *data,
201 struct smb_private *sock = talloc_get_type_abort(
202 p->transport.private_data, struct smb_private);
203 struct smb_send_request_state *state;
204 struct tevent_req *subreq;
205 bool use_trans = trigger_read;
207 if (sock->stream == NULL) {
208 return NT_STATUS_CONNECTION_DISCONNECTED;
211 state = talloc_zero(sock, struct smb_send_request_state);
213 return NT_STATUS_NO_MEMORY;
217 state->blob = data_blob_talloc(state, data->data, data->length);
218 if (state->blob.data == NULL) {
220 return NT_STATUS_NO_MEMORY;
222 state->iov.iov_base = (void *)state->blob.data;
223 state->iov.iov_len = state->blob.length;
225 if (sock->read_subreq != NULL) {
231 * we need to block reads until our write is
232 * the next in the write queue.
234 sock->read_subreq = tevent_queue_wait_send(state, p->event_ctx,
236 if (sock->read_subreq == NULL) {
238 return NT_STATUS_NO_MEMORY;
240 tevent_req_set_callback(sock->read_subreq,
241 smb_send_request_wait_done,
244 talloc_set_destructor(state, smb_send_request_state_destructor);
246 trigger_read = false;
249 subreq = tstream_writev_queue_send(state, p->event_ctx,
253 if (subreq == NULL) {
255 return NT_STATUS_NO_MEMORY;
257 tevent_req_set_callback(subreq, smb_send_request_done, state);
266 static void smb_send_request_wait_done(struct tevent_req *subreq)
268 struct smb_send_request_state *state =
269 tevent_req_callback_data(subreq,
270 struct smb_send_request_state);
271 struct dcecli_connection *p = state->p;
272 struct smb_private *sock = talloc_get_type_abort(
273 p->transport.private_data, struct smb_private);
277 sock->read_subreq = NULL;
278 talloc_set_destructor(state, NULL);
280 ok = tevent_queue_wait_recv(subreq);
283 pipe_dead(p, NT_STATUS_NO_MEMORY);
287 if (tevent_queue_length(sock->write_queue) <= 2) {
288 status = tstream_smbXcli_np_use_trans(sock->stream);
289 if (!NT_STATUS_IS_OK(status)) {
291 pipe_dead(p, status);
296 /* we free subreq after tstream_smbXcli_np_use_trans */
302 static void smb_send_request_done(struct tevent_req *subreq)
304 struct smb_send_request_state *state =
305 tevent_req_callback_data(subreq,
306 struct smb_send_request_state);
310 ret = tstream_writev_queue_recv(subreq, &error);
313 struct dcecli_connection *p = state->p;
314 NTSTATUS status = map_nt_error_from_unix_common(error);
317 pipe_dead(p, status);
325 shutdown SMB pipe connection
327 struct smb_shutdown_pipe_state {
328 struct dcecli_connection *c;
332 static void smb_shutdown_pipe_done(struct tevent_req *subreq);
334 static NTSTATUS smb_shutdown_pipe(struct dcecli_connection *c, NTSTATUS status)
336 struct smb_private *smb = talloc_get_type_abort(
337 c->transport.private_data, struct smb_private);
338 struct smb_shutdown_pipe_state *state;
339 struct tevent_req *subreq;
341 if (smb->stream == NULL) {
345 state = talloc_zero(smb, struct smb_shutdown_pipe_state);
347 return NT_STATUS_NO_MEMORY;
350 state->status = status;
352 subreq = tstream_disconnect_send(state, c->event_ctx, smb->stream);
353 if (subreq == NULL) {
354 return NT_STATUS_NO_MEMORY;
356 tevent_req_set_callback(subreq, smb_shutdown_pipe_done, state);
361 static void smb_shutdown_pipe_done(struct tevent_req *subreq)
363 struct smb_shutdown_pipe_state *state =
364 tevent_req_callback_data(subreq, struct smb_shutdown_pipe_state);
365 struct dcecli_connection *c = state->c;
366 NTSTATUS status = state->status;
370 * here we ignore the return values...
372 tstream_disconnect_recv(subreq, &error);
377 pipe_dead(c, status);
381 return SMB server name (called name)
383 static const char *smb_peer_name(struct dcecli_connection *c)
385 struct smb_private *smb = talloc_get_type_abort(
386 c->transport.private_data, struct smb_private);
387 if (smb == NULL) return "";
388 return smb->server_name;
392 return remote name we make the actual connection (good for kerberos)
394 static const char *smb_target_hostname(struct dcecli_connection *c)
396 struct smb_private *smb = talloc_get_type_abort(
397 c->transport.private_data, struct smb_private);
398 if (smb == NULL) return "";
399 return smb->server_name;
403 fetch the user session key
405 static NTSTATUS smb_session_key(struct dcecli_connection *c, DATA_BLOB *session_key)
407 struct smb_private *smb = talloc_get_type_abort(
408 c->transport.private_data, struct smb_private);
410 if (smb == NULL) return NT_STATUS_CONNECTION_DISCONNECTED;
412 if (smb->session_key.length == 0) {
413 return NT_STATUS_NO_USER_SESSION_KEY;
416 *session_key = smb->session_key;
420 struct dcerpc_pipe_open_smb_state {
421 struct dcecli_connection *c;
422 struct composite_context *ctx;
426 struct smb_private *smb;
429 static void dcerpc_pipe_open_smb_done(struct tevent_req *subreq);
431 struct composite_context *dcerpc_pipe_open_smb_send(struct dcecli_connection *c,
432 struct smbXcli_conn *conn,
433 struct smbXcli_session *session,
434 struct smbXcli_tcon *tcon,
435 uint32_t timeout_msec,
436 const char *pipe_name)
438 struct composite_context *ctx;
439 struct dcerpc_pipe_open_smb_state *state;
441 struct tevent_req *subreq;
443 ctx = composite_create(c, c->event_ctx);
444 if (ctx == NULL) return NULL;
446 state = talloc(ctx, struct dcerpc_pipe_open_smb_state);
447 if (composite_nomem(state, ctx)) return ctx;
448 ctx->private_data = state;
453 if ((strncasecmp(pipe_name, "/pipe/", 6) == 0) ||
454 (strncasecmp(pipe_name, "\\pipe\\", 6) == 0)) {
457 if ((strncasecmp(pipe_name, "/", 1) == 0) ||
458 (strncasecmp(pipe_name, "\\", 1) == 0)) {
461 state->fname = talloc_strdup(state, pipe_name);
462 if (composite_nomem(state->fname, ctx)) return ctx;
464 state->smb = talloc_zero(state, struct smb_private);
465 if (composite_nomem(state->smb, ctx)) return ctx;
467 state->smb->conn = conn;
468 state->smb->session = session;
469 state->smb->tcon = tcon;
470 state->smb->timeout_msec = timeout_msec;
472 state->smb->server_name = strupper_talloc(state->smb,
473 smbXcli_conn_remote_name(conn));
474 if (composite_nomem(state->smb->server_name, ctx)) return ctx;
476 ctx->status = smbXcli_session_application_key(session,
478 &state->smb->session_key);
479 if (NT_STATUS_EQUAL(ctx->status, NT_STATUS_NO_USER_SESSION_KEY)) {
480 state->smb->session_key = data_blob_null;
481 ctx->status = NT_STATUS_OK;
483 if (!composite_is_ok(ctx)) return ctx;
485 subreq = tstream_smbXcli_np_open_send(state, c->event_ctx,
486 conn, session, tcon, pid,
487 timeout_msec, state->fname);
488 if (composite_nomem(subreq, ctx)) return ctx;
489 tevent_req_set_callback(subreq, dcerpc_pipe_open_smb_done, state);
494 static void dcerpc_pipe_open_smb_done(struct tevent_req *subreq)
496 struct dcerpc_pipe_open_smb_state *state =
497 tevent_req_callback_data(subreq,
498 struct dcerpc_pipe_open_smb_state);
499 struct composite_context *ctx = state->ctx;
500 struct dcecli_connection *c = state->c;
502 ctx->status = tstream_smbXcli_np_open_recv(subreq,
504 &state->smb->stream);
506 if (!composite_is_ok(ctx)) return;
508 state->smb->write_queue = tevent_queue_create(state->smb,
509 "dcerpc_smb write queue");
510 if (composite_nomem(state->smb->write_queue, ctx)) return;
513 fill in the transport methods
515 c->transport.transport = NCACN_NP;
516 c->transport.private_data = NULL;
517 c->transport.shutdown_pipe = smb_shutdown_pipe;
518 c->transport.peer_name = smb_peer_name;
519 c->transport.target_hostname = smb_target_hostname;
521 c->transport.send_request = smb_send_request;
522 c->transport.send_read = smb_send_read;
523 c->transport.recv_data = NULL;
526 * Windows uses 4280 for ncacn_np,
527 * so we also use it, this is what our
528 * tstream_smbXcli_np code relies on.
530 c->srv_max_xmit_frag = 4280;
531 c->srv_max_recv_frag = 4280;
533 /* Over-ride the default session key with the SMB session key */
534 c->security_state.session_key = smb_session_key;
536 c->transport.private_data = talloc_move(c, &state->smb);
541 NTSTATUS dcerpc_pipe_open_smb_recv(struct composite_context *c)
543 NTSTATUS status = composite_wait(c);
548 _PUBLIC_ NTSTATUS dcerpc_pipe_open_smb(struct dcerpc_pipe *p,
549 struct smbcli_tree *t,
550 const char *pipe_name)
552 struct smbXcli_conn *conn;
553 struct smbXcli_session *session;
554 struct smbXcli_tcon *tcon;
555 uint32_t timeout_msec;
556 struct composite_context *ctx;
558 conn = t->session->transport->conn;
559 session = t->session->smbXcli;
561 smb1cli_tcon_set_id(tcon, t->tid);
562 timeout_msec = t->session->transport->options.request_timeout * 1000;
564 /* if we don't have a binding on this pipe yet, then create one */
565 if (p->binding == NULL) {
567 const char *r = smbXcli_conn_remote_name(conn);
569 SMB_ASSERT(r != NULL);
570 str = talloc_asprintf(p, "ncacn_np:%s", r);
572 return NT_STATUS_NO_MEMORY;
574 status = dcerpc_parse_binding(p, str,
577 if (!NT_STATUS_IS_OK(status)) {
582 ctx = dcerpc_pipe_open_smb_send(p->conn,
587 return NT_STATUS_NO_MEMORY;
590 return dcerpc_pipe_open_smb_recv(ctx);
593 struct composite_context *dcerpc_secondary_smb_send(struct dcecli_connection *c1,
594 struct dcerpc_pipe *p2,
595 const char *pipe_name)
597 struct smb_private *smb;
599 if (c1->transport.transport != NCACN_NP) return NULL;
601 smb = talloc_get_type(c1->transport.private_data, struct smb_private);
602 if (!smb) return NULL;
604 return dcerpc_pipe_open_smb_send(p2->conn,
612 NTSTATUS dcerpc_secondary_smb_recv(struct composite_context *c)
614 return dcerpc_pipe_open_smb_recv(c);