2 Unix SMB/CIFS implementation.
4 Copyright (C) Andrew Tridgell 2005
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 3 of the License, or
9 (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program. If not, see <http://www.gnu.org/licenses/>.
20 a composite API for making a full SMB connection
24 #include "libcli/raw/libcliraw.h"
25 #include "libcli/raw/raw_proto.h"
26 #include "libcli/composite/composite.h"
27 #include "libcli/smb_composite/smb_composite.h"
28 #include "lib/events/events.h"
29 #include "libcli/resolve/resolve.h"
30 #include "auth/credentials/credentials.h"
31 #include "librpc/gen_ndr/ndr_nbt.h"
32 #include "param/param.h"
33 #include "lib/util/util_net.h"
34 #include "libcli/smb/smbXcli_base.h"
36 /* the stages of this call */
37 enum connect_stage
{CONNECT_SOCKET
,
39 CONNECT_SESSION_SETUP
,
40 CONNECT_SESSION_SETUP_ANON
,
45 struct connect_state
{
46 enum connect_stage stage
;
47 struct smbcli_socket
*sock
;
48 struct smbcli_transport
*transport
;
49 struct smbcli_session
*session
;
50 struct smb_composite_connect
*io
;
51 union smb_tcon
*io_tcon
;
52 struct smb_composite_sesssetup
*io_setup
;
53 struct smbcli_request
*req
;
54 struct composite_context
*creq
;
55 struct tevent_req
*subreq
;
56 struct nbt_name calling
, called
;
60 static void request_handler(struct smbcli_request
*);
61 static void composite_handler(struct composite_context
*);
62 static void subreq_handler(struct tevent_req
*subreq
);
65 a tree connect request has completed
67 static NTSTATUS
connect_tcon(struct composite_context
*c
,
68 struct smb_composite_connect
*io
)
70 struct connect_state
*state
= talloc_get_type(c
->private_data
, struct connect_state
);
73 status
= smb_raw_tcon_recv(state
->req
, c
, state
->io_tcon
);
74 NT_STATUS_NOT_OK_RETURN(status
);
76 if (state
->io_tcon
->tconx
.out
.options
& SMB_EXTENDED_SIGNATURES
) {
77 smb1cli_session_protect_session_key(io
->out
.tree
->session
->smbXcli
);
80 io
->out
.tree
->tid
= state
->io_tcon
->tconx
.out
.tid
;
81 if (state
->io_tcon
->tconx
.out
.dev_type
) {
82 io
->out
.tree
->device
= talloc_strdup(io
->out
.tree
,
83 state
->io_tcon
->tconx
.out
.dev_type
);
85 if (state
->io_tcon
->tconx
.out
.fs_type
) {
86 io
->out
.tree
->fs_type
= talloc_strdup(io
->out
.tree
,
87 state
->io_tcon
->tconx
.out
.fs_type
);
90 state
->stage
= CONNECT_DONE
;
97 a session setup request with anonymous fallback has completed
99 static NTSTATUS
connect_session_setup_anon(struct composite_context
*c
,
100 struct smb_composite_connect
*io
)
102 struct connect_state
*state
= talloc_get_type(c
->private_data
, struct connect_state
);
105 status
= smb_composite_sesssetup_recv(state
->creq
);
106 NT_STATUS_NOT_OK_RETURN(status
);
108 io
->out
.anonymous_fallback_done
= true;
110 state
->session
->vuid
= state
->io_setup
->out
.vuid
;
112 /* setup for a tconx */
113 state
->io_tcon
= talloc(c
, union smb_tcon
);
114 NT_STATUS_HAVE_NO_MEMORY(state
->io_tcon
);
116 /* connect to a share using a tree connect */
117 state
->io_tcon
->generic
.level
= RAW_TCON_TCONX
;
118 state
->io_tcon
->tconx
.in
.flags
= TCONX_FLAG_EXTENDED_RESPONSE
;
119 state
->io_tcon
->tconx
.in
.password
= data_blob(NULL
, 0);
121 state
->io_tcon
->tconx
.in
.path
= talloc_asprintf(state
->io_tcon
,
125 NT_STATUS_HAVE_NO_MEMORY(state
->io_tcon
->tconx
.in
.path
);
126 if (!io
->in
.service_type
) {
127 state
->io_tcon
->tconx
.in
.device
= "?????";
129 state
->io_tcon
->tconx
.in
.device
= io
->in
.service_type
;
132 state
->req
= smb_raw_tcon_send(io
->out
.tree
, state
->io_tcon
);
133 NT_STATUS_HAVE_NO_MEMORY(state
->req
);
134 if (state
->req
->state
== SMBCLI_REQUEST_ERROR
) {
135 return state
->req
->status
;
138 state
->req
->async
.fn
= request_handler
;
139 state
->req
->async
.private_data
= c
;
140 state
->stage
= CONNECT_TCON
;
146 a session setup request has completed
148 static NTSTATUS
connect_session_setup(struct composite_context
*c
,
149 struct smb_composite_connect
*io
)
151 struct connect_state
*state
= talloc_get_type(c
->private_data
, struct connect_state
);
154 status
= smb_composite_sesssetup_recv(state
->creq
);
156 if (!NT_STATUS_IS_OK(status
) &&
157 !cli_credentials_is_anonymous(state
->io
->in
.credentials
) &&
158 io
->in
.fallback_to_anonymous
) {
160 state
->io_setup
->in
.credentials
= cli_credentials_init(state
);
161 NT_STATUS_HAVE_NO_MEMORY(state
->io_setup
->in
.credentials
);
162 cli_credentials_set_workstation(state
->io_setup
->in
.credentials
,
163 cli_credentials_get_workstation(state
->io
->in
.credentials
),
165 cli_credentials_set_anonymous(state
->io_setup
->in
.credentials
);
167 /* If the preceding attempt was with extended security, we
168 * have been given a uid in the NTLMSSP_CHALLENGE reply. This
169 * would lead to an invalid uid in the anonymous fallback */
170 state
->session
->vuid
= 0;
171 talloc_free(state
->session
->gensec
);
172 state
->session
->gensec
= NULL
;
174 state
->creq
= smb_composite_sesssetup_send(state
->session
,
176 NT_STATUS_HAVE_NO_MEMORY(state
->creq
);
177 if (state
->creq
->state
== COMPOSITE_STATE_ERROR
) {
178 return state
->creq
->status
;
180 state
->creq
->async
.fn
= composite_handler
;
181 state
->creq
->async
.private_data
= c
;
182 state
->stage
= CONNECT_SESSION_SETUP_ANON
;
187 NT_STATUS_NOT_OK_RETURN(status
);
189 state
->session
->vuid
= state
->io_setup
->out
.vuid
;
191 /* If we don't have a remote share name then this indicates that
192 * we don't want to do a tree connect */
193 if (!io
->in
.service
) {
194 state
->stage
= CONNECT_DONE
;
198 state
->io_tcon
= talloc(c
, union smb_tcon
);
199 NT_STATUS_HAVE_NO_MEMORY(state
->io_tcon
);
201 /* connect to a share using a tree connect */
202 state
->io_tcon
->generic
.level
= RAW_TCON_TCONX
;
203 state
->io_tcon
->tconx
.in
.flags
= TCONX_FLAG_EXTENDED_RESPONSE
;
204 state
->io_tcon
->tconx
.in
.flags
|= TCONX_FLAG_EXTENDED_SIGNATURES
;
205 state
->io_tcon
->tconx
.in
.password
= data_blob(NULL
, 0);
207 state
->io_tcon
->tconx
.in
.path
= talloc_asprintf(state
->io_tcon
,
211 NT_STATUS_HAVE_NO_MEMORY(state
->io_tcon
->tconx
.in
.path
);
212 if (!io
->in
.service_type
) {
213 state
->io_tcon
->tconx
.in
.device
= "?????";
215 state
->io_tcon
->tconx
.in
.device
= io
->in
.service_type
;
218 state
->req
= smb_raw_tcon_send(io
->out
.tree
, state
->io_tcon
);
219 NT_STATUS_HAVE_NO_MEMORY(state
->req
);
220 if (state
->req
->state
== SMBCLI_REQUEST_ERROR
) {
221 return state
->req
->status
;
224 state
->req
->async
.fn
= request_handler
;
225 state
->req
->async
.private_data
= c
;
226 state
->stage
= CONNECT_TCON
;
231 static NTSTATUS
connect_send_session(struct composite_context
*c
,
232 struct smb_composite_connect
*io
)
234 struct connect_state
*state
= talloc_get_type(c
->private_data
, struct connect_state
);
236 /* next step is a session setup */
237 state
->session
= smbcli_session_init(state
->transport
, state
, true, io
->in
.session_options
);
238 NT_STATUS_HAVE_NO_MEMORY(state
->session
);
240 /* setup for a tconx (or at least have the structure ready to
241 * return, if we won't go that far) */
242 io
->out
.tree
= smbcli_tree_init(state
->session
, state
, true);
243 NT_STATUS_HAVE_NO_MEMORY(io
->out
.tree
);
245 /* If we don't have any credentials then this indicates that
246 * we don't want to do a session setup */
247 if (!io
->in
.credentials
) {
248 state
->stage
= CONNECT_DONE
;
252 state
->io_setup
= talloc(c
, struct smb_composite_sesssetup
);
253 NT_STATUS_HAVE_NO_MEMORY(state
->io_setup
);
255 /* prepare a session setup to establish a security context */
256 state
->io_setup
->in
.sesskey
= state
->transport
->negotiate
.sesskey
;
257 state
->io_setup
->in
.capabilities
= state
->transport
->negotiate
.capabilities
;
258 state
->io_setup
->in
.credentials
= io
->in
.credentials
;
259 state
->io_setup
->in
.workgroup
= io
->in
.workgroup
;
260 state
->io_setup
->in
.gensec_settings
= io
->in
.gensec_settings
;
262 state
->creq
= smb_composite_sesssetup_send(state
->session
, state
->io_setup
);
263 NT_STATUS_HAVE_NO_MEMORY(state
->creq
);
264 if (state
->creq
->state
== COMPOSITE_STATE_ERROR
) {
265 return state
->creq
->status
;
268 state
->creq
->async
.fn
= composite_handler
;
269 state
->creq
->async
.private_data
= c
;
271 state
->stage
= CONNECT_SESSION_SETUP
;
277 a negprot request has completed
279 static NTSTATUS
connect_negprot(struct composite_context
*c
,
280 struct smb_composite_connect
*io
)
282 struct connect_state
*state
= talloc_get_type(c
->private_data
, struct connect_state
);
285 status
= smb_raw_negotiate_recv(state
->subreq
);
286 TALLOC_FREE(state
->subreq
);
287 NT_STATUS_NOT_OK_RETURN(status
);
289 return connect_send_session(c
, io
);
295 static NTSTATUS
connect_send_negprot(struct composite_context
*c
,
296 struct smb_composite_connect
*io
)
298 struct connect_state
*state
= talloc_get_type(c
->private_data
, struct connect_state
);
300 /* the socket is up - we can initialise the smbcli transport layer */
301 state
->transport
= smbcli_transport_init(state
->sock
, state
, true,
303 NT_STATUS_HAVE_NO_MEMORY(state
->transport
);
305 state
->subreq
= smb_raw_negotiate_send(state
,
306 state
->transport
->ev
,
308 state
->transport
->options
.min_protocol
,
309 state
->transport
->options
.max_protocol
);
310 NT_STATUS_HAVE_NO_MEMORY(state
->subreq
);
311 tevent_req_set_callback(state
->subreq
, subreq_handler
, c
);
312 state
->stage
= CONNECT_NEGPROT
;
318 a socket connection operation has completed
320 static NTSTATUS
connect_socket(struct composite_context
*c
,
321 struct smb_composite_connect
*io
)
323 struct connect_state
*state
= talloc_get_type(c
->private_data
, struct connect_state
);
326 status
= smbcli_sock_connect_recv(state
->creq
, state
, &state
->sock
);
327 NT_STATUS_NOT_OK_RETURN(status
);
329 if (is_ipaddress(state
->sock
->hostname
) &&
330 (state
->io
->in
.called_name
!= NULL
)) {
331 /* If connecting to an IP address, we might want the real name
332 * of the host for later kerberos. The called name is a better
334 state
->sock
->hostname
=
335 talloc_strdup(state
->sock
, io
->in
.called_name
);
336 NT_STATUS_HAVE_NO_MEMORY(state
->sock
->hostname
);
339 /* next step is a negprot */
340 return connect_send_negprot(c
, io
);
345 handle and dispatch state transitions
347 static void state_handler(struct composite_context
*c
)
349 struct connect_state
*state
= talloc_get_type(c
->private_data
, struct connect_state
);
351 switch (state
->stage
) {
353 c
->status
= connect_socket(c
, state
->io
);
355 case CONNECT_NEGPROT
:
356 c
->status
= connect_negprot(c
, state
->io
);
358 case CONNECT_SESSION_SETUP
:
359 c
->status
= connect_session_setup(c
, state
->io
);
361 case CONNECT_SESSION_SETUP_ANON
:
362 c
->status
= connect_session_setup_anon(c
, state
->io
);
365 c
->status
= connect_tcon(c
, state
->io
);
371 if (state
->stage
== CONNECT_DONE
) {
381 handler for completion of a smbcli_request sub-request
383 static void request_handler(struct smbcli_request
*req
)
385 struct composite_context
*c
= talloc_get_type(req
->async
.private_data
,
386 struct composite_context
);
391 handler for completion of a smbcli_composite sub-request
393 static void composite_handler(struct composite_context
*creq
)
395 struct composite_context
*c
= talloc_get_type(creq
->async
.private_data
,
396 struct composite_context
);
401 handler for completion of a tevent_req sub-request
403 static void subreq_handler(struct tevent_req
*subreq
)
405 struct composite_context
*c
=
406 tevent_req_callback_data(subreq
,
407 struct composite_context
);
412 a function to establish a smbcli_tree from scratch
414 struct composite_context
*smb_composite_connect_send(struct smb_composite_connect
*io
,
416 struct resolve_context
*resolve_ctx
,
417 struct tevent_context
*event_ctx
)
419 struct composite_context
*c
;
420 struct connect_state
*state
;
422 c
= talloc_zero(mem_ctx
, struct composite_context
);
427 state
= talloc_zero(c
, struct connect_state
);
432 c
->event_ctx
= event_ctx
;
433 if (c
->event_ctx
== NULL
) {
434 composite_error(c
, NT_STATUS_INVALID_PARAMETER_MIX
);
438 if (io
->in
.gensec_settings
== NULL
) {
439 composite_error(c
, NT_STATUS_INVALID_PARAMETER_MIX
);
444 c
->state
= COMPOSITE_STATE_IN_PROGRESS
;
445 c
->private_data
= state
;
447 make_nbt_name_client(&state
->calling
,
448 cli_credentials_get_workstation(io
->in
.credentials
));
450 nbt_choose_called_name(state
, &state
->called
,
451 io
->in
.called_name
, NBT_NAME_SERVER
);
453 if (io
->in
.existing_conn
!= NULL
) {
456 status
= smbcli_transport_raw_init(state
,
458 &io
->in
.existing_conn
,
461 if (!NT_STATUS_IS_OK(status
)) {
462 composite_error(c
, status
);
466 status
= connect_send_session(c
, io
);
467 if (!NT_STATUS_IS_OK(status
)) {
468 composite_error(c
, status
);
475 state
->creq
= smbcli_sock_connect_send(state
,
479 resolve_ctx
, c
->event_ctx
,
480 io
->in
.socket_options
,
483 if (state
->creq
== NULL
) {
484 composite_error(c
, NT_STATUS_NO_MEMORY
);
488 state
->stage
= CONNECT_SOCKET
;
489 state
->creq
->async
.private_data
= c
;
490 state
->creq
->async
.fn
= composite_handler
;
499 recv half of async composite connect code
501 NTSTATUS
smb_composite_connect_recv(struct composite_context
*c
, TALLOC_CTX
*mem_ctx
)
505 status
= composite_wait(c
);
507 if (NT_STATUS_IS_OK(status
)) {
508 struct connect_state
*state
= talloc_get_type(c
->private_data
, struct connect_state
);
509 talloc_steal(mem_ctx
, state
->io
->out
.tree
);
517 sync version of smb_composite_connect
519 NTSTATUS
smb_composite_connect(struct smb_composite_connect
*io
, TALLOC_CTX
*mem_ctx
,
520 struct resolve_context
*resolve_ctx
,
521 struct tevent_context
*ev
)
523 struct composite_context
*c
= smb_composite_connect_send(io
, mem_ctx
, resolve_ctx
, ev
);
525 return NT_STATUS_NO_MEMORY
;
527 return smb_composite_connect_recv(c
, mem_ctx
);