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 "libcli/raw/libcliraw.h"
25 #include "libcli/composite/composite.h"
26 #include "librpc/rpc/dcerpc.h"
27 #include "librpc/rpc/dcerpc_proto.h"
28 #include "librpc/rpc/rpc_common.h"
29 #include "../libcli/smb/smbXcli_base.h"
31 /* transport private information used by SMB pipe transport */
34 struct smbcli_tree
*tree
;
35 DATA_BLOB session_key
;
36 const char *server_name
;
42 tell the dcerpc layer that the transport is dead
44 static void pipe_dead(struct dcecli_connection
*c
, NTSTATUS status
)
46 struct smb_private
*smb
= (struct smb_private
*)c
->transport
.private_data
;
54 if (NT_STATUS_EQUAL(NT_STATUS_UNSUCCESSFUL
, status
)) {
55 status
= NT_STATUS_UNEXPECTED_NETWORK_ERROR
;
58 if (NT_STATUS_EQUAL(NT_STATUS_OK
, status
)) {
59 status
= NT_STATUS_END_OF_FILE
;
62 if (c
->transport
.recv_data
) {
63 c
->transport
.recv_data(c
, NULL
, status
);
69 this holds the state of an in-flight call
71 struct smb_read_state
{
72 struct dcecli_connection
*c
;
73 struct smbcli_request
*req
;
80 called when a read request has completed
82 static void smb_read_callback(struct smbcli_request
*req
)
84 struct dcecli_connection
*c
;
85 struct smb_private
*smb
;
86 struct smb_read_state
*state
;
91 state
= talloc_get_type(req
->async
.private_data
, struct smb_read_state
);
92 smb
= talloc_get_type(state
->c
->transport
.private_data
, struct smb_private
);
96 status
= smb_raw_read_recv(state
->req
, io
);
97 if (NT_STATUS_IS_ERR(status
)) {
103 state
->received
+= io
->readx
.out
.nread
;
105 if (state
->received
< 16) {
106 DEBUG(0,("dcerpc_smb: short packet (length %d) in read callback!\n",
107 (int)state
->received
));
109 pipe_dead(c
, NT_STATUS_INFO_LENGTH_MISMATCH
);
113 frag_length
= dcerpc_get_frag_length(&state
->data
);
115 if (frag_length
<= state
->received
) {
116 DATA_BLOB data
= state
->data
;
117 data
.length
= state
->received
;
118 talloc_steal(state
->c
, data
.data
);
120 c
->transport
.recv_data(c
, &data
, NT_STATUS_OK
);
124 /* initiate another read request, as we only got part of a fragment */
125 state
->data
.data
= talloc_realloc(state
, state
->data
.data
, uint8_t, frag_length
);
127 io
->readx
.in
.mincnt
= MIN(state
->c
->srv_max_xmit_frag
,
128 frag_length
- state
->received
);
129 io
->readx
.in
.maxcnt
= io
->readx
.in
.mincnt
;
130 io
->readx
.out
.data
= state
->data
.data
+ state
->received
;
132 state
->req
= smb_raw_read_send(smb
->tree
, io
);
133 if (state
->req
== NULL
) {
135 pipe_dead(c
, NT_STATUS_NO_MEMORY
);
139 state
->req
->async
.fn
= smb_read_callback
;
140 state
->req
->async
.private_data
= state
;
144 trigger a read request from the server, possibly with some initial
145 data in the read buffer
147 static NTSTATUS
send_read_request_continue(struct dcecli_connection
*c
, DATA_BLOB
*blob
)
149 struct smb_private
*smb
= (struct smb_private
*)c
->transport
.private_data
;
151 struct smb_read_state
*state
;
152 struct smbcli_request
*req
;
154 state
= talloc(smb
, struct smb_read_state
);
156 return NT_STATUS_NO_MEMORY
;
162 state
->data
= data_blob_talloc(state
, NULL
, 0x2000);
164 uint32_t frag_length
= blob
->length
>=16?
165 dcerpc_get_frag_length(blob
):0x2000;
167 if (frag_length
< state
->data
.length
) {
169 return NT_STATUS_RPC_PROTOCOL_ERROR
;
172 state
->received
= blob
->length
;
173 state
->data
= data_blob_talloc(state
, NULL
, frag_length
);
174 if (!state
->data
.data
) {
176 return NT_STATUS_NO_MEMORY
;
178 memcpy(state
->data
.data
, blob
->data
, blob
->length
);
181 state
->io
= talloc(state
, union smb_read
);
184 io
->generic
.level
= RAW_READ_READX
;
185 io
->readx
.in
.file
.fnum
= smb
->fnum
;
186 io
->readx
.in
.mincnt
= state
->data
.length
- state
->received
;
187 io
->readx
.in
.maxcnt
= io
->readx
.in
.mincnt
;
188 io
->readx
.in
.offset
= 0;
189 io
->readx
.in
.remaining
= 0;
190 io
->readx
.in
.read_for_execute
= false;
191 io
->readx
.out
.data
= state
->data
.data
+ state
->received
;
192 req
= smb_raw_read_send(smb
->tree
, io
);
194 return NT_STATUS_NO_MEMORY
;
197 req
->async
.fn
= smb_read_callback
;
198 req
->async
.private_data
= state
;
207 trigger a read request from the server
209 static NTSTATUS
send_read_request(struct dcecli_connection
*c
)
211 struct smb_private
*smb
= (struct smb_private
*)c
->transport
.private_data
;
214 return NT_STATUS_CONNECTION_DISCONNECTED
;
217 return send_read_request_continue(c
, NULL
);
221 this holds the state of an in-flight trans call
223 struct smb_trans_state
{
224 struct dcecli_connection
*c
;
225 struct smbcli_request
*req
;
226 struct smb_trans2
*trans
;
230 called when a trans request has completed
232 static void smb_trans_callback(struct smbcli_request
*req
)
234 struct smb_trans_state
*state
= (struct smb_trans_state
*)req
->async
.private_data
;
235 struct dcecli_connection
*c
= state
->c
;
238 status
= smb_raw_trans_recv(req
, state
, state
->trans
);
240 if (NT_STATUS_IS_ERR(status
)) {
241 pipe_dead(c
, status
);
245 if (!NT_STATUS_EQUAL(status
, STATUS_BUFFER_OVERFLOW
)) {
246 DATA_BLOB data
= state
->trans
->out
.data
;
247 talloc_steal(c
, data
.data
);
249 c
->transport
.recv_data(c
, &data
, NT_STATUS_OK
);
253 /* there is more to receive - setup a readx */
254 send_read_request_continue(c
, &state
->trans
->out
.data
);
259 send a SMBtrans style request
261 static NTSTATUS
smb_send_trans_request(struct dcecli_connection
*c
, DATA_BLOB
*blob
)
263 struct smb_private
*smb
= (struct smb_private
*)c
->transport
.private_data
;
264 struct smb_trans2
*trans
;
266 struct smb_trans_state
*state
;
269 state
= talloc(c
, struct smb_trans_state
);
271 return NT_STATUS_NO_MEMORY
;
275 state
->trans
= talloc(state
, struct smb_trans2
);
276 trans
= state
->trans
;
278 trans
->in
.data
= *blob
;
279 trans
->in
.params
= data_blob(NULL
, 0);
281 setup
[0] = TRANSACT_DCERPCCMD
;
282 setup
[1] = smb
->fnum
;
284 if (c
->srv_max_xmit_frag
> 0) {
285 max_data
= MIN(UINT16_MAX
, c
->srv_max_xmit_frag
);
287 max_data
= UINT16_MAX
;
290 trans
->in
.max_param
= 0;
291 trans
->in
.max_data
= max_data
;
292 trans
->in
.max_setup
= 0;
293 trans
->in
.setup_count
= 2;
295 trans
->in
.timeout
= 0;
296 trans
->in
.setup
= setup
;
297 trans
->in
.trans_name
= "\\PIPE\\";
299 state
->req
= smb_raw_trans_send(smb
->tree
, trans
);
300 if (state
->req
== NULL
) {
302 return NT_STATUS_NO_MEMORY
;
305 state
->req
->async
.fn
= smb_trans_callback
;
306 state
->req
->async
.private_data
= state
;
308 talloc_steal(state
, state
->req
);
314 called when a write request has completed
316 static void smb_write_callback(struct smbcli_request
*req
)
318 struct dcecli_connection
*c
= (struct dcecli_connection
*)req
->async
.private_data
;
323 io
.generic
.level
= RAW_WRITE_WRITEX
;
325 status
= smb_raw_write_recv(req
, &io
);
326 if (!NT_STATUS_IS_OK(status
)) {
327 DEBUG(0,("dcerpc_smb: write callback error: %s\n",
329 pipe_dead(c
, status
);
334 send a packet to the server
336 static NTSTATUS
smb_send_request(struct dcecli_connection
*c
, DATA_BLOB
*blob
,
339 struct smb_private
*smb
= (struct smb_private
*)c
->transport
.private_data
;
341 struct smbcli_request
*req
;
343 if (!smb
|| smb
->dead
) {
344 return NT_STATUS_CONNECTION_DISCONNECTED
;
348 return smb_send_trans_request(c
, blob
);
351 io
.generic
.level
= RAW_WRITE_WRITEX
;
352 io
.writex
.in
.file
.fnum
= smb
->fnum
;
353 io
.writex
.in
.offset
= 0;
354 io
.writex
.in
.wmode
= PIPE_START_MESSAGE
;
355 io
.writex
.in
.remaining
= blob
->length
;
356 io
.writex
.in
.count
= blob
->length
;
357 io
.writex
.in
.data
= blob
->data
;
359 /* we must not timeout at the smb level for rpc requests, as otherwise
360 signing/sealing can be messed up */
361 smb
->tree
->session
->transport
->options
.request_timeout
= 0;
363 req
= smb_raw_write_send(smb
->tree
, &io
);
365 return NT_STATUS_NO_MEMORY
;
368 req
->async
.fn
= smb_write_callback
;
369 req
->async
.private_data
= c
;
375 static void free_request(struct smbcli_request
*req
)
381 shutdown SMB pipe connection
383 static NTSTATUS
smb_shutdown_pipe(struct dcecli_connection
*c
, NTSTATUS status
)
385 struct smb_private
*smb
= (struct smb_private
*)c
->transport
.private_data
;
387 struct smbcli_request
*req
;
389 /* maybe we're still starting up */
390 if (!smb
) return status
;
392 io
.close
.level
= RAW_CLOSE_CLOSE
;
393 io
.close
.in
.file
.fnum
= smb
->fnum
;
394 io
.close
.in
.write_time
= 0;
395 req
= smb_raw_close_send(smb
->tree
, &io
);
397 /* we don't care if this fails, so just free it if it succeeds */
398 req
->async
.fn
= free_request
;
402 c
->transport
.private_data
= NULL
;
408 return SMB server name (called name)
410 static const char *smb_peer_name(struct dcecli_connection
*c
)
412 struct smb_private
*smb
= (struct smb_private
*)c
->transport
.private_data
;
413 if (smb
== NULL
) return "";
414 return smb
->server_name
;
418 return remote name we make the actual connection (good for kerberos)
420 static const char *smb_target_hostname(struct dcecli_connection
*c
)
422 struct smb_private
*smb
= talloc_get_type(c
->transport
.private_data
, struct smb_private
);
423 if (smb
== NULL
) return "";
424 return smbXcli_conn_remote_name(smb
->tree
->session
->transport
->conn
);
428 fetch the user session key
430 static NTSTATUS
smb_session_key(struct dcecli_connection
*c
, DATA_BLOB
*session_key
)
432 struct smb_private
*smb
= (struct smb_private
*)c
->transport
.private_data
;
434 if (smb
== NULL
) return NT_STATUS_CONNECTION_DISCONNECTED
;
436 if (smb
->session_key
.length
== 0) {
437 return NT_STATUS_NO_USER_SESSION_KEY
;
440 *session_key
= smb
->session_key
;
444 struct pipe_open_smb_state
{
445 union smb_open
*open
;
446 struct dcecli_connection
*c
;
447 struct smbcli_tree
*tree
;
448 struct composite_context
*ctx
;
451 static void pipe_open_recv(struct smbcli_request
*req
);
453 struct composite_context
*dcerpc_pipe_open_smb_send(struct dcerpc_pipe
*p
,
454 struct smbcli_tree
*tree
,
455 const char *pipe_name
)
457 struct composite_context
*ctx
;
458 struct pipe_open_smb_state
*state
;
459 struct smbcli_request
*req
;
460 struct dcecli_connection
*c
= p
->conn
;
462 /* if we don't have a binding on this pipe yet, then create one */
463 if (p
->binding
== NULL
) {
465 const char *r
= smbXcli_conn_remote_name(tree
->session
->transport
->conn
);
467 SMB_ASSERT(r
!= NULL
);
468 s
= talloc_asprintf(p
, "ncacn_np:%s", r
);
469 if (s
== NULL
) return NULL
;
470 status
= dcerpc_parse_binding(p
, s
, &p
->binding
);
472 if (!NT_STATUS_IS_OK(status
)) {
477 ctx
= composite_create(c
, c
->event_ctx
);
478 if (ctx
== NULL
) return NULL
;
480 state
= talloc(ctx
, struct pipe_open_smb_state
);
481 if (composite_nomem(state
, ctx
)) return ctx
;
482 ctx
->private_data
= state
;
488 state
->open
= talloc(state
, union smb_open
);
489 if (composite_nomem(state
->open
, ctx
)) return ctx
;
491 state
->open
->ntcreatex
.level
= RAW_OPEN_NTCREATEX
;
492 state
->open
->ntcreatex
.in
.flags
= 0;
493 state
->open
->ntcreatex
.in
.root_fid
.fnum
= 0;
494 state
->open
->ntcreatex
.in
.access_mask
=
495 SEC_STD_READ_CONTROL
|
496 SEC_FILE_WRITE_ATTRIBUTE
|
500 state
->open
->ntcreatex
.in
.file_attr
= 0;
501 state
->open
->ntcreatex
.in
.alloc_size
= 0;
502 state
->open
->ntcreatex
.in
.share_access
=
503 NTCREATEX_SHARE_ACCESS_READ
|
504 NTCREATEX_SHARE_ACCESS_WRITE
;
505 state
->open
->ntcreatex
.in
.open_disposition
= NTCREATEX_DISP_OPEN
;
506 state
->open
->ntcreatex
.in
.create_options
= 0;
507 state
->open
->ntcreatex
.in
.impersonation
=
508 NTCREATEX_IMPERSONATION_IMPERSONATION
;
509 state
->open
->ntcreatex
.in
.security_flags
= 0;
511 if ((strncasecmp(pipe_name
, "/pipe/", 6) == 0) ||
512 (strncasecmp(pipe_name
, "\\pipe\\", 6) == 0)) {
515 state
->open
->ntcreatex
.in
.fname
=
516 (pipe_name
[0] == '\\') ?
517 talloc_strdup(state
->open
, pipe_name
) :
518 talloc_asprintf(state
->open
, "\\%s", pipe_name
);
519 if (composite_nomem(state
->open
->ntcreatex
.in
.fname
, ctx
)) return ctx
;
521 req
= smb_raw_open_send(tree
, state
->open
);
522 composite_continue_smb(ctx
, req
, pipe_open_recv
, state
);
526 static void pipe_open_recv(struct smbcli_request
*req
)
528 struct pipe_open_smb_state
*state
= talloc_get_type(req
->async
.private_data
,
529 struct pipe_open_smb_state
);
530 struct composite_context
*ctx
= state
->ctx
;
531 struct dcecli_connection
*c
= state
->c
;
532 struct smb_private
*smb
;
534 ctx
->status
= smb_raw_open_recv(req
, state
, state
->open
);
535 if (!composite_is_ok(ctx
)) return;
538 fill in the transport methods
540 c
->transport
.transport
= NCACN_NP
;
541 c
->transport
.private_data
= NULL
;
542 c
->transport
.shutdown_pipe
= smb_shutdown_pipe
;
543 c
->transport
.peer_name
= smb_peer_name
;
544 c
->transport
.target_hostname
= smb_target_hostname
;
546 c
->transport
.send_request
= smb_send_request
;
547 c
->transport
.send_read
= send_read_request
;
548 c
->transport
.recv_data
= NULL
;
550 /* Over-ride the default session key with the SMB session key */
551 c
->security_state
.session_key
= smb_session_key
;
553 smb
= talloc(c
, struct smb_private
);
554 if (composite_nomem(smb
, ctx
)) return;
556 smb
->fnum
= state
->open
->ntcreatex
.out
.file
.fnum
;
557 smb
->tree
= talloc_reference(smb
, state
->tree
);
558 smb
->server_name
= strupper_talloc(smb
,
559 smbXcli_conn_remote_name(state
->tree
->session
->transport
->conn
));
560 if (composite_nomem(smb
->server_name
, ctx
)) return;
563 ctx
->status
= smbXcli_session_application_key(state
->tree
->session
->smbXcli
,
564 smb
, &smb
->session_key
);
565 if (NT_STATUS_EQUAL(ctx
->status
, NT_STATUS_NO_USER_SESSION_KEY
)) {
566 smb
->session_key
= data_blob_null
;
567 ctx
->status
= NT_STATUS_OK
;
569 if (!composite_is_ok(ctx
)) return;
571 c
->transport
.private_data
= smb
;
576 NTSTATUS
dcerpc_pipe_open_smb_recv(struct composite_context
*c
)
578 NTSTATUS status
= composite_wait(c
);
583 _PUBLIC_ NTSTATUS
dcerpc_pipe_open_smb(struct dcerpc_pipe
*p
,
584 struct smbcli_tree
*tree
,
585 const char *pipe_name
)
587 struct composite_context
*ctx
= dcerpc_pipe_open_smb_send(p
, tree
,
589 return dcerpc_pipe_open_smb_recv(ctx
);
593 return the SMB tree used for a dcerpc over SMB pipe
595 _PUBLIC_
struct smbcli_tree
*dcerpc_smb_tree(struct dcecli_connection
*c
)
597 struct smb_private
*smb
;
599 if (c
->transport
.transport
!= NCACN_NP
) return NULL
;
601 smb
= talloc_get_type(c
->transport
.private_data
, struct smb_private
);
602 if (!smb
) return NULL
;
608 return the SMB fnum used for a dcerpc over SMB pipe (hack for torture operations)
610 _PUBLIC_
uint16_t dcerpc_smb_fnum(struct dcecli_connection
*c
)
612 struct smb_private
*smb
;
614 if (c
->transport
.transport
!= NCACN_NP
) return 0;
616 smb
= talloc_get_type(c
->transport
.private_data
, struct smb_private
);