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
,
38 CONNECT_SESSION_SETUP
,
39 CONNECT_SESSION_SETUP_ANON
,
44 struct connect_state
{
45 enum connect_stage stage
;
46 struct smbcli_socket
*sock
;
47 struct smbcli_transport
*transport
;
48 struct smbcli_session
*session
;
49 struct smb_composite_connect
*io
;
50 union smb_tcon
*io_tcon
;
51 struct smb_composite_sesssetup
*io_setup
;
52 struct smbcli_request
*req
;
53 struct composite_context
*creq
;
54 struct tevent_req
*subreq
;
55 struct nbt_name calling
, called
;
59 static void request_handler(struct smbcli_request
*);
60 static void composite_handler(struct composite_context
*);
61 static void subreq_handler(struct tevent_req
*subreq
);
64 a tree connect request has completed
66 static NTSTATUS
connect_tcon(struct composite_context
*c
,
67 struct smb_composite_connect
*io
)
69 struct connect_state
*state
= talloc_get_type(c
->private_data
, struct connect_state
);
72 status
= smb_raw_tcon_recv(state
->req
, c
, state
->io_tcon
);
73 NT_STATUS_NOT_OK_RETURN(status
);
75 io
->out
.tree
->tid
= state
->io_tcon
->tconx
.out
.tid
;
76 if (state
->io_tcon
->tconx
.out
.dev_type
) {
77 io
->out
.tree
->device
= talloc_strdup(io
->out
.tree
,
78 state
->io_tcon
->tconx
.out
.dev_type
);
80 if (state
->io_tcon
->tconx
.out
.fs_type
) {
81 io
->out
.tree
->fs_type
= talloc_strdup(io
->out
.tree
,
82 state
->io_tcon
->tconx
.out
.fs_type
);
85 state
->stage
= CONNECT_DONE
;
92 a session setup request with anonymous fallback has completed
94 static NTSTATUS
connect_session_setup_anon(struct composite_context
*c
,
95 struct smb_composite_connect
*io
)
97 struct connect_state
*state
= talloc_get_type(c
->private_data
, struct connect_state
);
100 status
= smb_composite_sesssetup_recv(state
->creq
);
101 NT_STATUS_NOT_OK_RETURN(status
);
103 io
->out
.anonymous_fallback_done
= true;
105 state
->session
->vuid
= state
->io_setup
->out
.vuid
;
107 /* setup for a tconx */
108 state
->io_tcon
= talloc(c
, union smb_tcon
);
109 NT_STATUS_HAVE_NO_MEMORY(state
->io_tcon
);
111 /* connect to a share using a tree connect */
112 state
->io_tcon
->generic
.level
= RAW_TCON_TCONX
;
113 state
->io_tcon
->tconx
.in
.flags
= 0;
114 state
->io_tcon
->tconx
.in
.password
= data_blob(NULL
, 0);
116 state
->io_tcon
->tconx
.in
.path
= talloc_asprintf(state
->io_tcon
,
120 NT_STATUS_HAVE_NO_MEMORY(state
->io_tcon
->tconx
.in
.path
);
121 if (!io
->in
.service_type
) {
122 state
->io_tcon
->tconx
.in
.device
= "?????";
124 state
->io_tcon
->tconx
.in
.device
= io
->in
.service_type
;
127 state
->req
= smb_raw_tcon_send(io
->out
.tree
, state
->io_tcon
);
128 NT_STATUS_HAVE_NO_MEMORY(state
->req
);
129 if (state
->req
->state
== SMBCLI_REQUEST_ERROR
) {
130 return state
->req
->status
;
133 state
->req
->async
.fn
= request_handler
;
134 state
->req
->async
.private_data
= c
;
135 state
->stage
= CONNECT_TCON
;
141 a session setup request has completed
143 static NTSTATUS
connect_session_setup(struct composite_context
*c
,
144 struct smb_composite_connect
*io
)
146 struct connect_state
*state
= talloc_get_type(c
->private_data
, struct connect_state
);
149 status
= smb_composite_sesssetup_recv(state
->creq
);
151 if (!NT_STATUS_IS_OK(status
) &&
152 !cli_credentials_is_anonymous(state
->io
->in
.credentials
) &&
153 io
->in
.fallback_to_anonymous
) {
155 state
->io_setup
->in
.credentials
= cli_credentials_init(state
);
156 NT_STATUS_HAVE_NO_MEMORY(state
->io_setup
->in
.credentials
);
157 cli_credentials_set_workstation(state
->io_setup
->in
.credentials
,
158 cli_credentials_get_workstation(state
->io
->in
.credentials
),
160 cli_credentials_set_anonymous(state
->io_setup
->in
.credentials
);
162 /* If the preceding attempt was with extended security, we
163 * have been given a uid in the NTLMSSP_CHALLENGE reply. This
164 * would lead to an invalid uid in the anonymous fallback */
165 state
->session
->vuid
= 0;
166 data_blob_free(&state
->session
->user_session_key
);
167 talloc_free(state
->session
->gensec
);
168 state
->session
->gensec
= NULL
;
170 state
->creq
= smb_composite_sesssetup_send(state
->session
,
172 NT_STATUS_HAVE_NO_MEMORY(state
->creq
);
173 if (state
->creq
->state
== COMPOSITE_STATE_ERROR
) {
174 return state
->creq
->status
;
176 state
->creq
->async
.fn
= composite_handler
;
177 state
->creq
->async
.private_data
= c
;
178 state
->stage
= CONNECT_SESSION_SETUP_ANON
;
183 NT_STATUS_NOT_OK_RETURN(status
);
185 state
->session
->vuid
= state
->io_setup
->out
.vuid
;
187 /* If we don't have a remote share name then this indicates that
188 * we don't want to do a tree connect */
189 if (!io
->in
.service
) {
190 state
->stage
= CONNECT_DONE
;
194 state
->io_tcon
= talloc(c
, union smb_tcon
);
195 NT_STATUS_HAVE_NO_MEMORY(state
->io_tcon
);
197 /* connect to a share using a tree connect */
198 state
->io_tcon
->generic
.level
= RAW_TCON_TCONX
;
199 state
->io_tcon
->tconx
.in
.flags
= 0;
200 state
->io_tcon
->tconx
.in
.password
= data_blob(NULL
, 0);
202 state
->io_tcon
->tconx
.in
.path
= talloc_asprintf(state
->io_tcon
,
206 NT_STATUS_HAVE_NO_MEMORY(state
->io_tcon
->tconx
.in
.path
);
207 if (!io
->in
.service_type
) {
208 state
->io_tcon
->tconx
.in
.device
= "?????";
210 state
->io_tcon
->tconx
.in
.device
= io
->in
.service_type
;
213 state
->req
= smb_raw_tcon_send(io
->out
.tree
, state
->io_tcon
);
214 NT_STATUS_HAVE_NO_MEMORY(state
->req
);
215 if (state
->req
->state
== SMBCLI_REQUEST_ERROR
) {
216 return state
->req
->status
;
219 state
->req
->async
.fn
= request_handler
;
220 state
->req
->async
.private_data
= c
;
221 state
->stage
= CONNECT_TCON
;
227 a negprot request has completed
229 static NTSTATUS
connect_negprot(struct composite_context
*c
,
230 struct smb_composite_connect
*io
)
232 struct connect_state
*state
= talloc_get_type(c
->private_data
, struct connect_state
);
235 status
= smb_raw_negotiate_recv(state
->subreq
);
236 TALLOC_FREE(state
->subreq
);
237 NT_STATUS_NOT_OK_RETURN(status
);
239 /* next step is a session setup */
240 state
->session
= smbcli_session_init(state
->transport
, state
, true, io
->in
.session_options
);
241 NT_STATUS_HAVE_NO_MEMORY(state
->session
);
243 /* setup for a tconx (or at least have the structure ready to
244 * return, if we won't go that far) */
245 io
->out
.tree
= smbcli_tree_init(state
->session
, state
, true);
246 NT_STATUS_HAVE_NO_MEMORY(io
->out
.tree
);
248 /* If we don't have any credentials then this indicates that
249 * we don't want to do a session setup */
250 if (!io
->in
.credentials
) {
251 state
->stage
= CONNECT_DONE
;
255 state
->io_setup
= talloc(c
, struct smb_composite_sesssetup
);
256 NT_STATUS_HAVE_NO_MEMORY(state
->io_setup
);
258 /* prepare a session setup to establish a security context */
259 state
->io_setup
->in
.sesskey
= state
->transport
->negotiate
.sesskey
;
260 state
->io_setup
->in
.capabilities
= state
->transport
->negotiate
.capabilities
;
261 state
->io_setup
->in
.credentials
= io
->in
.credentials
;
262 state
->io_setup
->in
.workgroup
= io
->in
.workgroup
;
263 state
->io_setup
->in
.gensec_settings
= io
->in
.gensec_settings
;
265 state
->creq
= smb_composite_sesssetup_send(state
->session
, state
->io_setup
);
266 NT_STATUS_HAVE_NO_MEMORY(state
->creq
);
267 if (state
->creq
->state
== COMPOSITE_STATE_ERROR
) {
268 return state
->creq
->status
;
271 state
->creq
->async
.fn
= composite_handler
;
272 state
->creq
->async
.private_data
= c
;
274 state
->stage
= CONNECT_SESSION_SETUP
;
282 static NTSTATUS
connect_send_negprot(struct composite_context
*c
,
283 struct smb_composite_connect
*io
)
285 struct connect_state
*state
= talloc_get_type(c
->private_data
, struct connect_state
);
287 /* the socket is up - we can initialise the smbcli transport layer */
288 state
->transport
= smbcli_transport_init(state
->sock
, state
, true,
290 NT_STATUS_HAVE_NO_MEMORY(state
->transport
);
292 state
->subreq
= smb_raw_negotiate_send(state
,
293 state
->transport
->ev
,
295 io
->in
.options
.max_protocol
);
296 NT_STATUS_HAVE_NO_MEMORY(state
->subreq
);
297 tevent_req_set_callback(state
->subreq
, subreq_handler
, c
);
298 state
->stage
= CONNECT_NEGPROT
;
304 a socket connection operation has completed
306 static NTSTATUS
connect_socket(struct composite_context
*c
,
307 struct smb_composite_connect
*io
)
309 struct connect_state
*state
= talloc_get_type(c
->private_data
, struct connect_state
);
312 status
= smbcli_sock_connect_recv(state
->creq
, state
, &state
->sock
);
313 NT_STATUS_NOT_OK_RETURN(status
);
315 if (is_ipaddress(state
->sock
->hostname
) &&
316 (state
->io
->in
.called_name
!= NULL
)) {
317 /* If connecting to an IP address, we might want the real name
318 * of the host for later kerberos. The called name is a better
320 state
->sock
->hostname
=
321 talloc_strdup(state
->sock
, io
->in
.called_name
);
322 NT_STATUS_HAVE_NO_MEMORY(state
->sock
->hostname
);
325 /* next step is a negprot */
326 return connect_send_negprot(c
, io
);
331 handle and dispatch state transitions
333 static void state_handler(struct composite_context
*c
)
335 struct connect_state
*state
= talloc_get_type(c
->private_data
, struct connect_state
);
337 switch (state
->stage
) {
339 c
->status
= connect_socket(c
, state
->io
);
341 case CONNECT_NEGPROT
:
342 c
->status
= connect_negprot(c
, state
->io
);
344 case CONNECT_SESSION_SETUP
:
345 c
->status
= connect_session_setup(c
, state
->io
);
347 case CONNECT_SESSION_SETUP_ANON
:
348 c
->status
= connect_session_setup_anon(c
, state
->io
);
351 c
->status
= connect_tcon(c
, state
->io
);
357 if (state
->stage
== CONNECT_DONE
) {
367 handler for completion of a smbcli_request sub-request
369 static void request_handler(struct smbcli_request
*req
)
371 struct composite_context
*c
= talloc_get_type(req
->async
.private_data
,
372 struct composite_context
);
377 handler for completion of a smbcli_composite sub-request
379 static void composite_handler(struct composite_context
*creq
)
381 struct composite_context
*c
= talloc_get_type(creq
->async
.private_data
,
382 struct composite_context
);
387 handler for completion of a tevent_req sub-request
389 static void subreq_handler(struct tevent_req
*subreq
)
391 struct composite_context
*c
=
392 tevent_req_callback_data(subreq
,
393 struct composite_context
);
398 a function to establish a smbcli_tree from scratch
400 struct composite_context
*smb_composite_connect_send(struct smb_composite_connect
*io
,
402 struct resolve_context
*resolve_ctx
,
403 struct tevent_context
*event_ctx
)
405 struct composite_context
*c
;
406 struct connect_state
*state
;
408 c
= talloc_zero(mem_ctx
, struct composite_context
);
409 if (c
== NULL
) goto failed
;
411 c
->event_ctx
= event_ctx
;
412 if (c
->event_ctx
== NULL
) goto failed
;
414 state
= talloc_zero(c
, struct connect_state
);
415 if (state
== NULL
) goto failed
;
417 if (io
->in
.gensec_settings
== NULL
) goto failed
;
420 c
->state
= COMPOSITE_STATE_IN_PROGRESS
;
421 c
->private_data
= state
;
423 make_nbt_name_client(&state
->calling
,
424 cli_credentials_get_workstation(io
->in
.credentials
));
426 nbt_choose_called_name(state
, &state
->called
,
427 io
->in
.called_name
, NBT_NAME_SERVER
);
429 state
->creq
= smbcli_sock_connect_send(state
,
433 resolve_ctx
, c
->event_ctx
,
434 io
->in
.socket_options
,
437 if (state
->creq
== NULL
) goto failed
;
439 state
->stage
= CONNECT_SOCKET
;
440 state
->creq
->async
.private_data
= c
;
441 state
->creq
->async
.fn
= composite_handler
;
450 recv half of async composite connect code
452 NTSTATUS
smb_composite_connect_recv(struct composite_context
*c
, TALLOC_CTX
*mem_ctx
)
456 status
= composite_wait(c
);
458 if (NT_STATUS_IS_OK(status
)) {
459 struct connect_state
*state
= talloc_get_type(c
->private_data
, struct connect_state
);
460 talloc_steal(mem_ctx
, state
->io
->out
.tree
);
468 sync version of smb_composite_connect
470 NTSTATUS
smb_composite_connect(struct smb_composite_connect
*io
, TALLOC_CTX
*mem_ctx
,
471 struct resolve_context
*resolve_ctx
,
472 struct tevent_context
*ev
)
474 struct composite_context
*c
= smb_composite_connect_send(io
, mem_ctx
, resolve_ctx
, ev
);
475 return smb_composite_connect_recv(c
, mem_ctx
);