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
;
232 a negprot request has completed
234 static NTSTATUS
connect_negprot(struct composite_context
*c
,
235 struct smb_composite_connect
*io
)
237 struct connect_state
*state
= talloc_get_type(c
->private_data
, struct connect_state
);
240 status
= smb_raw_negotiate_recv(state
->subreq
);
241 TALLOC_FREE(state
->subreq
);
242 NT_STATUS_NOT_OK_RETURN(status
);
244 /* next step is a session setup */
245 state
->session
= smbcli_session_init(state
->transport
, state
, true, io
->in
.session_options
);
246 NT_STATUS_HAVE_NO_MEMORY(state
->session
);
248 /* setup for a tconx (or at least have the structure ready to
249 * return, if we won't go that far) */
250 io
->out
.tree
= smbcli_tree_init(state
->session
, state
, true);
251 NT_STATUS_HAVE_NO_MEMORY(io
->out
.tree
);
253 /* If we don't have any credentials then this indicates that
254 * we don't want to do a session setup */
255 if (!io
->in
.credentials
) {
256 state
->stage
= CONNECT_DONE
;
260 state
->io_setup
= talloc(c
, struct smb_composite_sesssetup
);
261 NT_STATUS_HAVE_NO_MEMORY(state
->io_setup
);
263 /* prepare a session setup to establish a security context */
264 state
->io_setup
->in
.sesskey
= state
->transport
->negotiate
.sesskey
;
265 state
->io_setup
->in
.capabilities
= state
->transport
->negotiate
.capabilities
;
266 state
->io_setup
->in
.credentials
= io
->in
.credentials
;
267 state
->io_setup
->in
.workgroup
= io
->in
.workgroup
;
268 state
->io_setup
->in
.gensec_settings
= io
->in
.gensec_settings
;
270 state
->creq
= smb_composite_sesssetup_send(state
->session
, state
->io_setup
);
271 NT_STATUS_HAVE_NO_MEMORY(state
->creq
);
272 if (state
->creq
->state
== COMPOSITE_STATE_ERROR
) {
273 return state
->creq
->status
;
276 state
->creq
->async
.fn
= composite_handler
;
277 state
->creq
->async
.private_data
= c
;
279 state
->stage
= CONNECT_SESSION_SETUP
;
287 static NTSTATUS
connect_send_negprot(struct composite_context
*c
,
288 struct smb_composite_connect
*io
)
290 struct connect_state
*state
= talloc_get_type(c
->private_data
, struct connect_state
);
292 /* the socket is up - we can initialise the smbcli transport layer */
293 state
->transport
= smbcli_transport_init(state
->sock
, state
, true,
295 NT_STATUS_HAVE_NO_MEMORY(state
->transport
);
297 state
->subreq
= smb_raw_negotiate_send(state
,
298 state
->transport
->ev
,
300 state
->transport
->options
.max_protocol
);
301 NT_STATUS_HAVE_NO_MEMORY(state
->subreq
);
302 tevent_req_set_callback(state
->subreq
, subreq_handler
, c
);
303 state
->stage
= CONNECT_NEGPROT
;
309 a socket connection operation has completed
311 static NTSTATUS
connect_socket(struct composite_context
*c
,
312 struct smb_composite_connect
*io
)
314 struct connect_state
*state
= talloc_get_type(c
->private_data
, struct connect_state
);
317 status
= smbcli_sock_connect_recv(state
->creq
, state
, &state
->sock
);
318 NT_STATUS_NOT_OK_RETURN(status
);
320 if (is_ipaddress(state
->sock
->hostname
) &&
321 (state
->io
->in
.called_name
!= NULL
)) {
322 /* If connecting to an IP address, we might want the real name
323 * of the host for later kerberos. The called name is a better
325 state
->sock
->hostname
=
326 talloc_strdup(state
->sock
, io
->in
.called_name
);
327 NT_STATUS_HAVE_NO_MEMORY(state
->sock
->hostname
);
330 /* next step is a negprot */
331 return connect_send_negprot(c
, io
);
336 handle and dispatch state transitions
338 static void state_handler(struct composite_context
*c
)
340 struct connect_state
*state
= talloc_get_type(c
->private_data
, struct connect_state
);
342 switch (state
->stage
) {
344 c
->status
= connect_socket(c
, state
->io
);
346 case CONNECT_NEGPROT
:
347 c
->status
= connect_negprot(c
, state
->io
);
349 case CONNECT_SESSION_SETUP
:
350 c
->status
= connect_session_setup(c
, state
->io
);
352 case CONNECT_SESSION_SETUP_ANON
:
353 c
->status
= connect_session_setup_anon(c
, state
->io
);
356 c
->status
= connect_tcon(c
, state
->io
);
362 if (state
->stage
== CONNECT_DONE
) {
372 handler for completion of a smbcli_request sub-request
374 static void request_handler(struct smbcli_request
*req
)
376 struct composite_context
*c
= talloc_get_type(req
->async
.private_data
,
377 struct composite_context
);
382 handler for completion of a smbcli_composite sub-request
384 static void composite_handler(struct composite_context
*creq
)
386 struct composite_context
*c
= talloc_get_type(creq
->async
.private_data
,
387 struct composite_context
);
392 handler for completion of a tevent_req sub-request
394 static void subreq_handler(struct tevent_req
*subreq
)
396 struct composite_context
*c
=
397 tevent_req_callback_data(subreq
,
398 struct composite_context
);
403 a function to establish a smbcli_tree from scratch
405 struct composite_context
*smb_composite_connect_send(struct smb_composite_connect
*io
,
407 struct resolve_context
*resolve_ctx
,
408 struct tevent_context
*event_ctx
)
410 struct composite_context
*c
;
411 struct connect_state
*state
;
413 c
= talloc_zero(mem_ctx
, struct composite_context
);
414 if (c
== NULL
) goto failed
;
416 c
->event_ctx
= event_ctx
;
417 if (c
->event_ctx
== NULL
) goto failed
;
419 state
= talloc_zero(c
, struct connect_state
);
420 if (state
== NULL
) goto failed
;
422 if (io
->in
.gensec_settings
== NULL
) goto failed
;
425 c
->state
= COMPOSITE_STATE_IN_PROGRESS
;
426 c
->private_data
= state
;
428 make_nbt_name_client(&state
->calling
,
429 cli_credentials_get_workstation(io
->in
.credentials
));
431 nbt_choose_called_name(state
, &state
->called
,
432 io
->in
.called_name
, NBT_NAME_SERVER
);
434 state
->creq
= smbcli_sock_connect_send(state
,
438 resolve_ctx
, c
->event_ctx
,
439 io
->in
.socket_options
,
442 if (state
->creq
== NULL
) goto failed
;
444 state
->stage
= CONNECT_SOCKET
;
445 state
->creq
->async
.private_data
= c
;
446 state
->creq
->async
.fn
= composite_handler
;
455 recv half of async composite connect code
457 NTSTATUS
smb_composite_connect_recv(struct composite_context
*c
, TALLOC_CTX
*mem_ctx
)
461 status
= composite_wait(c
);
463 if (NT_STATUS_IS_OK(status
)) {
464 struct connect_state
*state
= talloc_get_type(c
->private_data
, struct connect_state
);
465 talloc_steal(mem_ctx
, state
->io
->out
.tree
);
473 sync version of smb_composite_connect
475 NTSTATUS
smb_composite_connect(struct smb_composite_connect
*io
, TALLOC_CTX
*mem_ctx
,
476 struct resolve_context
*resolve_ctx
,
477 struct tevent_context
*ev
)
479 struct composite_context
*c
= smb_composite_connect_send(io
, mem_ctx
, resolve_ctx
, ev
);
480 return smb_composite_connect_recv(c
, mem_ctx
);