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"
35 /* the stages of this call */
36 enum connect_stage
{CONNECT_SOCKET
,
37 CONNECT_SESSION_REQUEST
,
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
;
58 static void request_handler(struct smbcli_request
*);
59 static void composite_handler(struct composite_context
*);
62 a tree connect request has completed
64 static NTSTATUS
connect_tcon(struct composite_context
*c
,
65 struct smb_composite_connect
*io
)
67 struct connect_state
*state
= talloc_get_type(c
->private_data
, struct connect_state
);
70 status
= smb_raw_tcon_recv(state
->req
, c
, state
->io_tcon
);
71 NT_STATUS_NOT_OK_RETURN(status
);
73 io
->out
.tree
->tid
= state
->io_tcon
->tconx
.out
.tid
;
74 if (state
->io_tcon
->tconx
.out
.dev_type
) {
75 io
->out
.tree
->device
= talloc_strdup(io
->out
.tree
,
76 state
->io_tcon
->tconx
.out
.dev_type
);
78 if (state
->io_tcon
->tconx
.out
.fs_type
) {
79 io
->out
.tree
->fs_type
= talloc_strdup(io
->out
.tree
,
80 state
->io_tcon
->tconx
.out
.fs_type
);
83 state
->stage
= CONNECT_DONE
;
90 a session setup request with anonymous fallback has completed
92 static NTSTATUS
connect_session_setup_anon(struct composite_context
*c
,
93 struct smb_composite_connect
*io
)
95 struct connect_state
*state
= talloc_get_type(c
->private_data
, struct connect_state
);
98 status
= smb_composite_sesssetup_recv(state
->creq
);
99 NT_STATUS_NOT_OK_RETURN(status
);
101 io
->out
.anonymous_fallback_done
= true;
103 state
->session
->vuid
= state
->io_setup
->out
.vuid
;
105 /* setup for a tconx */
106 state
->io_tcon
= talloc(c
, union smb_tcon
);
107 NT_STATUS_HAVE_NO_MEMORY(state
->io_tcon
);
109 /* connect to a share using a tree connect */
110 state
->io_tcon
->generic
.level
= RAW_TCON_TCONX
;
111 state
->io_tcon
->tconx
.in
.flags
= 0;
112 state
->io_tcon
->tconx
.in
.password
= data_blob(NULL
, 0);
114 state
->io_tcon
->tconx
.in
.path
= talloc_asprintf(state
->io_tcon
,
118 NT_STATUS_HAVE_NO_MEMORY(state
->io_tcon
->tconx
.in
.path
);
119 if (!io
->in
.service_type
) {
120 state
->io_tcon
->tconx
.in
.device
= "?????";
122 state
->io_tcon
->tconx
.in
.device
= io
->in
.service_type
;
125 state
->req
= smb_raw_tcon_send(io
->out
.tree
, state
->io_tcon
);
126 NT_STATUS_HAVE_NO_MEMORY(state
->req
);
127 if (state
->req
->state
== SMBCLI_REQUEST_ERROR
) {
128 return state
->req
->status
;
131 state
->req
->async
.fn
= request_handler
;
132 state
->req
->async
.private_data
= c
;
133 state
->stage
= CONNECT_TCON
;
139 a session setup request has completed
141 static NTSTATUS
connect_session_setup(struct composite_context
*c
,
142 struct smb_composite_connect
*io
)
144 struct connect_state
*state
= talloc_get_type(c
->private_data
, struct connect_state
);
147 status
= smb_composite_sesssetup_recv(state
->creq
);
149 if (!NT_STATUS_IS_OK(status
) &&
150 !cli_credentials_is_anonymous(state
->io
->in
.credentials
) &&
151 io
->in
.fallback_to_anonymous
) {
153 state
->io_setup
->in
.credentials
= cli_credentials_init(state
);
154 NT_STATUS_HAVE_NO_MEMORY(state
->io_setup
->in
.credentials
);
155 cli_credentials_set_workstation(state
->io_setup
->in
.credentials
,
156 cli_credentials_get_workstation(state
->io
->in
.credentials
),
158 cli_credentials_set_anonymous(state
->io_setup
->in
.credentials
);
160 /* If the preceding attempt was with extended security, we
161 * have been given a uid in the NTLMSSP_CHALLENGE reply. This
162 * would lead to an invalid uid in the anonymous fallback */
163 state
->session
->vuid
= 0;
164 data_blob_free(&state
->session
->user_session_key
);
165 talloc_free(state
->session
->gensec
);
166 state
->session
->gensec
= NULL
;
168 state
->creq
= smb_composite_sesssetup_send(state
->session
,
170 NT_STATUS_HAVE_NO_MEMORY(state
->creq
);
171 if (state
->creq
->state
== COMPOSITE_STATE_ERROR
) {
172 return state
->creq
->status
;
174 state
->creq
->async
.fn
= composite_handler
;
175 state
->creq
->async
.private_data
= c
;
176 state
->stage
= CONNECT_SESSION_SETUP_ANON
;
181 NT_STATUS_NOT_OK_RETURN(status
);
183 state
->session
->vuid
= state
->io_setup
->out
.vuid
;
185 /* If we don't have a remote share name then this indicates that
186 * we don't want to do a tree connect */
187 if (!io
->in
.service
) {
188 state
->stage
= CONNECT_DONE
;
192 state
->io_tcon
= talloc(c
, union smb_tcon
);
193 NT_STATUS_HAVE_NO_MEMORY(state
->io_tcon
);
195 /* connect to a share using a tree connect */
196 state
->io_tcon
->generic
.level
= RAW_TCON_TCONX
;
197 state
->io_tcon
->tconx
.in
.flags
= 0;
198 state
->io_tcon
->tconx
.in
.password
= data_blob(NULL
, 0);
200 state
->io_tcon
->tconx
.in
.path
= talloc_asprintf(state
->io_tcon
,
204 NT_STATUS_HAVE_NO_MEMORY(state
->io_tcon
->tconx
.in
.path
);
205 if (!io
->in
.service_type
) {
206 state
->io_tcon
->tconx
.in
.device
= "?????";
208 state
->io_tcon
->tconx
.in
.device
= io
->in
.service_type
;
211 state
->req
= smb_raw_tcon_send(io
->out
.tree
, state
->io_tcon
);
212 NT_STATUS_HAVE_NO_MEMORY(state
->req
);
213 if (state
->req
->state
== SMBCLI_REQUEST_ERROR
) {
214 return state
->req
->status
;
217 state
->req
->async
.fn
= request_handler
;
218 state
->req
->async
.private_data
= c
;
219 state
->stage
= CONNECT_TCON
;
225 a negprot request has completed
227 static NTSTATUS
connect_negprot(struct composite_context
*c
,
228 struct smb_composite_connect
*io
)
230 struct connect_state
*state
= talloc_get_type(c
->private_data
, struct connect_state
);
233 status
= smb_raw_negotiate_recv(state
->req
);
234 NT_STATUS_NOT_OK_RETURN(status
);
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
;
279 static NTSTATUS
connect_send_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
);
284 state
->req
= smb_raw_negotiate_send(state
->transport
, io
->in
.options
.unicode
, io
->in
.options
.max_protocol
);
285 NT_STATUS_HAVE_NO_MEMORY(state
->req
);
287 state
->req
->async
.fn
= request_handler
;
288 state
->req
->async
.private_data
= c
;
289 state
->stage
= CONNECT_NEGPROT
;
296 a session request operation has completed
298 static NTSTATUS
connect_session_request(struct composite_context
*c
,
299 struct smb_composite_connect
*io
)
301 struct connect_state
*state
= talloc_get_type(c
->private_data
, struct connect_state
);
304 status
= smbcli_transport_connect_recv(state
->req
);
305 NT_STATUS_NOT_OK_RETURN(status
);
307 /* next step is a negprot */
308 return connect_send_negprot(c
, io
);
312 a socket connection operation has completed
314 static NTSTATUS
connect_socket(struct composite_context
*c
,
315 struct smb_composite_connect
*io
)
317 struct connect_state
*state
= talloc_get_type(c
->private_data
, struct connect_state
);
319 struct nbt_name calling
, called
;
321 status
= smbcli_sock_connect_recv(state
->creq
, state
, &state
->sock
);
322 NT_STATUS_NOT_OK_RETURN(status
);
324 /* the socket is up - we can initialise the smbcli transport layer */
325 state
->transport
= smbcli_transport_init(state
->sock
, state
, true,
327 NT_STATUS_HAVE_NO_MEMORY(state
->transport
);
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 make_nbt_name_client(&calling
, cli_credentials_get_workstation(io
->in
.credentials
));
341 nbt_choose_called_name(state
, &called
, io
->in
.called_name
, NBT_NAME_SERVER
);
343 /* we have a connected socket - next step is a session
344 request, if needed. Port 445 doesn't need it, so it goes
345 straight to the negprot */
346 if (state
->sock
->port
== 445) {
347 status
= nbt_name_dup(state
->transport
, &called
,
348 &state
->transport
->called
);
349 NT_STATUS_NOT_OK_RETURN(status
);
350 return connect_send_negprot(c
, io
);
353 state
->req
= smbcli_transport_connect_send(state
->transport
, &calling
, &called
);
354 NT_STATUS_HAVE_NO_MEMORY(state
->req
);
356 state
->req
->async
.fn
= request_handler
;
357 state
->req
->async
.private_data
= c
;
358 state
->stage
= CONNECT_SESSION_REQUEST
;
365 handle and dispatch state transitions
367 static void state_handler(struct composite_context
*c
)
369 struct connect_state
*state
= talloc_get_type(c
->private_data
, struct connect_state
);
371 switch (state
->stage
) {
373 c
->status
= connect_socket(c
, state
->io
);
375 case CONNECT_SESSION_REQUEST
:
376 c
->status
= connect_session_request(c
, state
->io
);
378 case CONNECT_NEGPROT
:
379 c
->status
= connect_negprot(c
, state
->io
);
381 case CONNECT_SESSION_SETUP
:
382 c
->status
= connect_session_setup(c
, state
->io
);
384 case CONNECT_SESSION_SETUP_ANON
:
385 c
->status
= connect_session_setup_anon(c
, state
->io
);
388 c
->status
= connect_tcon(c
, state
->io
);
394 if (state
->stage
== CONNECT_DONE
) {
404 handler for completion of a smbcli_request sub-request
406 static void request_handler(struct smbcli_request
*req
)
408 struct composite_context
*c
= talloc_get_type(req
->async
.private_data
,
409 struct composite_context
);
414 handler for completion of a smbcli_composite sub-request
416 static void composite_handler(struct composite_context
*creq
)
418 struct composite_context
*c
= talloc_get_type(creq
->async
.private_data
,
419 struct composite_context
);
424 a function to establish a smbcli_tree from scratch
426 struct composite_context
*smb_composite_connect_send(struct smb_composite_connect
*io
,
428 struct resolve_context
*resolve_ctx
,
429 struct tevent_context
*event_ctx
)
431 struct composite_context
*c
;
432 struct connect_state
*state
;
434 c
= talloc_zero(mem_ctx
, struct composite_context
);
435 if (c
== NULL
) goto failed
;
437 c
->event_ctx
= event_ctx
;
438 if (c
->event_ctx
== NULL
) goto failed
;
440 state
= talloc_zero(c
, struct connect_state
);
441 if (state
== NULL
) goto failed
;
443 if (io
->in
.gensec_settings
== NULL
) goto failed
;
446 c
->state
= COMPOSITE_STATE_IN_PROGRESS
;
447 c
->private_data
= state
;
449 state
->creq
= smbcli_sock_connect_send(state
,
453 resolve_ctx
, c
->event_ctx
,
454 io
->in
.socket_options
);
455 if (state
->creq
== NULL
) goto failed
;
457 state
->stage
= CONNECT_SOCKET
;
458 state
->creq
->async
.private_data
= c
;
459 state
->creq
->async
.fn
= composite_handler
;
468 recv half of async composite connect code
470 NTSTATUS
smb_composite_connect_recv(struct composite_context
*c
, TALLOC_CTX
*mem_ctx
)
474 status
= composite_wait(c
);
476 if (NT_STATUS_IS_OK(status
)) {
477 struct connect_state
*state
= talloc_get_type(c
->private_data
, struct connect_state
);
478 talloc_steal(mem_ctx
, state
->io
->out
.tree
);
486 sync version of smb_composite_connect
488 NTSTATUS
smb_composite_connect(struct smb_composite_connect
*io
, TALLOC_CTX
*mem_ctx
,
489 struct resolve_context
*resolve_ctx
,
490 struct tevent_context
*ev
)
492 struct composite_context
*c
= smb_composite_connect_send(io
, mem_ctx
, resolve_ctx
, ev
);
493 return smb_composite_connect_recv(c
, mem_ctx
);