Line data Source code
1 : /*
2 : Unix SMB/CIFS implementation.
3 :
4 : Copyright (C) Andrew Tridgell 2005
5 :
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.
10 :
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.
15 :
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/>.
18 : */
19 : /*
20 : a composite API for making a full SMB connection
21 : */
22 :
23 : #include "includes.h"
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"
35 :
36 : /* the stages of this call */
37 : enum connect_stage {CONNECT_SOCKET,
38 : CONNECT_NEGPROT,
39 : CONNECT_SESSION_SETUP,
40 : CONNECT_SESSION_SETUP_ANON,
41 : CONNECT_TCON,
42 : CONNECT_DONE
43 : };
44 :
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;
57 : };
58 :
59 :
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);
63 :
64 : /*
65 : a tree connect request has completed
66 : */
67 890 : static NTSTATUS connect_tcon(struct composite_context *c,
68 : struct smb_composite_connect *io)
69 : {
70 890 : struct connect_state *state = talloc_get_type(c->private_data, struct connect_state);
71 : NTSTATUS status;
72 :
73 890 : status = smb_raw_tcon_recv(state->req, c, state->io_tcon);
74 890 : NT_STATUS_NOT_OK_RETURN(status);
75 :
76 890 : if (state->io_tcon->tconx.out.options & SMB_EXTENDED_SIGNATURES) {
77 18 : smb1cli_session_protect_session_key(io->out.tree->session->smbXcli);
78 : }
79 :
80 890 : io->out.tree->tid = state->io_tcon->tconx.out.tid;
81 890 : if (state->io_tcon->tconx.out.dev_type) {
82 890 : io->out.tree->device = talloc_strdup(io->out.tree,
83 890 : state->io_tcon->tconx.out.dev_type);
84 : }
85 890 : if (state->io_tcon->tconx.out.fs_type) {
86 890 : io->out.tree->fs_type = talloc_strdup(io->out.tree,
87 890 : state->io_tcon->tconx.out.fs_type);
88 : }
89 :
90 890 : state->stage = CONNECT_DONE;
91 :
92 890 : return NT_STATUS_OK;
93 : }
94 :
95 :
96 : /*
97 : a session setup request with anonymous fallback has completed
98 : */
99 0 : static NTSTATUS connect_session_setup_anon(struct composite_context *c,
100 : struct smb_composite_connect *io)
101 : {
102 0 : struct connect_state *state = talloc_get_type(c->private_data, struct connect_state);
103 : NTSTATUS status;
104 :
105 0 : status = smb_composite_sesssetup_recv(state->creq);
106 0 : NT_STATUS_NOT_OK_RETURN(status);
107 :
108 0 : io->out.anonymous_fallback_done = true;
109 :
110 0 : state->session->vuid = state->io_setup->out.vuid;
111 :
112 : /* setup for a tconx */
113 0 : state->io_tcon = talloc(c, union smb_tcon);
114 0 : NT_STATUS_HAVE_NO_MEMORY(state->io_tcon);
115 :
116 : /* connect to a share using a tree connect */
117 0 : state->io_tcon->generic.level = RAW_TCON_TCONX;
118 0 : state->io_tcon->tconx.in.flags = TCONX_FLAG_EXTENDED_RESPONSE;
119 0 : state->io_tcon->tconx.in.password = data_blob(NULL, 0);
120 :
121 0 : state->io_tcon->tconx.in.path = talloc_asprintf(state->io_tcon,
122 : "\\\\%s\\%s",
123 : io->in.called_name,
124 : io->in.service);
125 0 : NT_STATUS_HAVE_NO_MEMORY(state->io_tcon->tconx.in.path);
126 0 : if (!io->in.service_type) {
127 0 : state->io_tcon->tconx.in.device = "?????";
128 : } else {
129 0 : state->io_tcon->tconx.in.device = io->in.service_type;
130 : }
131 :
132 0 : state->req = smb_raw_tcon_send(io->out.tree, state->io_tcon);
133 0 : NT_STATUS_HAVE_NO_MEMORY(state->req);
134 0 : if (state->req->state == SMBCLI_REQUEST_ERROR) {
135 0 : return state->req->status;
136 : }
137 :
138 0 : state->req->async.fn = request_handler;
139 0 : state->req->async.private_data = c;
140 0 : state->stage = CONNECT_TCON;
141 :
142 0 : return NT_STATUS_OK;
143 : }
144 :
145 : /*
146 : a session setup request has completed
147 : */
148 894 : static NTSTATUS connect_session_setup(struct composite_context *c,
149 : struct smb_composite_connect *io)
150 : {
151 894 : struct connect_state *state = talloc_get_type(c->private_data, struct connect_state);
152 : NTSTATUS status;
153 :
154 894 : status = smb_composite_sesssetup_recv(state->creq);
155 :
156 896 : if (!NT_STATUS_IS_OK(status) &&
157 6 : !cli_credentials_is_anonymous(state->io->in.credentials) &&
158 4 : io->in.fallback_to_anonymous) {
159 :
160 0 : state->io_setup->in.credentials = cli_credentials_init(state);
161 0 : NT_STATUS_HAVE_NO_MEMORY(state->io_setup->in.credentials);
162 0 : cli_credentials_set_workstation(state->io_setup->in.credentials,
163 0 : cli_credentials_get_workstation(state->io->in.credentials),
164 : CRED_SPECIFIED);
165 0 : cli_credentials_set_anonymous(state->io_setup->in.credentials);
166 :
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 0 : state->session->vuid = 0;
171 0 : talloc_free(state->session->gensec);
172 0 : state->session->gensec = NULL;
173 :
174 0 : state->creq = smb_composite_sesssetup_send(state->session,
175 : state->io_setup);
176 0 : NT_STATUS_HAVE_NO_MEMORY(state->creq);
177 0 : if (state->creq->state == COMPOSITE_STATE_ERROR) {
178 0 : return state->creq->status;
179 : }
180 0 : state->creq->async.fn = composite_handler;
181 0 : state->creq->async.private_data = c;
182 0 : state->stage = CONNECT_SESSION_SETUP_ANON;
183 :
184 0 : return NT_STATUS_OK;
185 : }
186 :
187 894 : NT_STATUS_NOT_OK_RETURN(status);
188 :
189 890 : state->session->vuid = state->io_setup->out.vuid;
190 :
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 890 : if (!io->in.service) {
194 0 : state->stage = CONNECT_DONE;
195 0 : return NT_STATUS_OK;
196 : }
197 :
198 890 : state->io_tcon = talloc(c, union smb_tcon);
199 890 : NT_STATUS_HAVE_NO_MEMORY(state->io_tcon);
200 :
201 : /* connect to a share using a tree connect */
202 890 : state->io_tcon->generic.level = RAW_TCON_TCONX;
203 890 : state->io_tcon->tconx.in.flags = TCONX_FLAG_EXTENDED_RESPONSE;
204 890 : state->io_tcon->tconx.in.flags |= TCONX_FLAG_EXTENDED_SIGNATURES;
205 890 : state->io_tcon->tconx.in.password = data_blob(NULL, 0);
206 :
207 890 : state->io_tcon->tconx.in.path = talloc_asprintf(state->io_tcon,
208 : "\\\\%s\\%s",
209 : io->in.called_name,
210 : io->in.service);
211 890 : NT_STATUS_HAVE_NO_MEMORY(state->io_tcon->tconx.in.path);
212 890 : if (!io->in.service_type) {
213 710 : state->io_tcon->tconx.in.device = "?????";
214 : } else {
215 180 : state->io_tcon->tconx.in.device = io->in.service_type;
216 : }
217 :
218 890 : state->req = smb_raw_tcon_send(io->out.tree, state->io_tcon);
219 890 : NT_STATUS_HAVE_NO_MEMORY(state->req);
220 890 : if (state->req->state == SMBCLI_REQUEST_ERROR) {
221 0 : return state->req->status;
222 : }
223 :
224 890 : state->req->async.fn = request_handler;
225 890 : state->req->async.private_data = c;
226 890 : state->stage = CONNECT_TCON;
227 :
228 890 : return NT_STATUS_OK;
229 : }
230 :
231 894 : static NTSTATUS connect_send_session(struct composite_context *c,
232 : struct smb_composite_connect *io)
233 : {
234 894 : struct connect_state *state = talloc_get_type(c->private_data, struct connect_state);
235 :
236 : /* next step is a session setup */
237 894 : state->session = smbcli_session_init(state->transport, state, true, io->in.session_options);
238 894 : NT_STATUS_HAVE_NO_MEMORY(state->session);
239 :
240 : /* setup for a tconx (or at least have the structure ready to
241 : * return, if we won't go that far) */
242 894 : io->out.tree = smbcli_tree_init(state->session, state, true);
243 894 : NT_STATUS_HAVE_NO_MEMORY(io->out.tree);
244 :
245 : /* If we don't have any credentials then this indicates that
246 : * we don't want to do a session setup */
247 894 : if (!io->in.credentials) {
248 0 : state->stage = CONNECT_DONE;
249 0 : return NT_STATUS_OK;
250 : }
251 :
252 894 : state->io_setup = talloc(c, struct smb_composite_sesssetup);
253 894 : NT_STATUS_HAVE_NO_MEMORY(state->io_setup);
254 :
255 : /* prepare a session setup to establish a security context */
256 894 : state->io_setup->in.sesskey = state->transport->negotiate.sesskey;
257 894 : state->io_setup->in.capabilities = state->transport->negotiate.capabilities;
258 894 : state->io_setup->in.credentials = io->in.credentials;
259 894 : state->io_setup->in.workgroup = io->in.workgroup;
260 894 : state->io_setup->in.gensec_settings = io->in.gensec_settings;
261 :
262 894 : state->creq = smb_composite_sesssetup_send(state->session, state->io_setup);
263 894 : NT_STATUS_HAVE_NO_MEMORY(state->creq);
264 894 : if (state->creq->state == COMPOSITE_STATE_ERROR) {
265 0 : return state->creq->status;
266 : }
267 :
268 894 : state->creq->async.fn = composite_handler;
269 894 : state->creq->async.private_data = c;
270 :
271 894 : state->stage = CONNECT_SESSION_SETUP;
272 :
273 894 : return NT_STATUS_OK;
274 : }
275 :
276 : /*
277 : a negprot request has completed
278 : */
279 924 : static NTSTATUS connect_negprot(struct composite_context *c,
280 : struct smb_composite_connect *io)
281 : {
282 924 : struct connect_state *state = talloc_get_type(c->private_data, struct connect_state);
283 : NTSTATUS status;
284 :
285 924 : status = smb_raw_negotiate_recv(state->subreq);
286 924 : TALLOC_FREE(state->subreq);
287 924 : NT_STATUS_NOT_OK_RETURN(status);
288 :
289 894 : return connect_send_session(c, io);
290 : }
291 :
292 : /*
293 : setup a negprot send
294 : */
295 924 : static NTSTATUS connect_send_negprot(struct composite_context *c,
296 : struct smb_composite_connect *io)
297 : {
298 924 : struct connect_state *state = talloc_get_type(c->private_data, struct connect_state);
299 :
300 : /* the socket is up - we can initialise the smbcli transport layer */
301 924 : state->transport = smbcli_transport_init(state->sock, state, true,
302 : &io->in.options);
303 924 : NT_STATUS_HAVE_NO_MEMORY(state->transport);
304 :
305 2714 : state->subreq = smb_raw_negotiate_send(state,
306 924 : state->transport->ev,
307 : state->transport,
308 924 : state->transport->options.min_protocol,
309 924 : state->transport->options.max_protocol);
310 924 : NT_STATUS_HAVE_NO_MEMORY(state->subreq);
311 924 : tevent_req_set_callback(state->subreq, subreq_handler, c);
312 924 : state->stage = CONNECT_NEGPROT;
313 :
314 924 : return NT_STATUS_OK;
315 : }
316 :
317 : /*
318 : a socket connection operation has completed
319 : */
320 924 : static NTSTATUS connect_socket(struct composite_context *c,
321 : struct smb_composite_connect *io)
322 : {
323 924 : struct connect_state *state = talloc_get_type(c->private_data, struct connect_state);
324 : NTSTATUS status;
325 :
326 924 : status = smbcli_sock_connect_recv(state->creq, state, &state->sock);
327 924 : NT_STATUS_NOT_OK_RETURN(status);
328 :
329 924 : if (is_ipaddress(state->sock->hostname) &&
330 0 : (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
333 : * approximation */
334 0 : state->sock->hostname =
335 0 : talloc_strdup(state->sock, io->in.called_name);
336 0 : NT_STATUS_HAVE_NO_MEMORY(state->sock->hostname);
337 : }
338 :
339 : /* next step is a negprot */
340 924 : return connect_send_negprot(c, io);
341 : }
342 :
343 :
344 : /*
345 : handle and dispatch state transitions
346 : */
347 3632 : static void state_handler(struct composite_context *c)
348 : {
349 3632 : struct connect_state *state = talloc_get_type(c->private_data, struct connect_state);
350 :
351 3632 : switch (state->stage) {
352 924 : case CONNECT_SOCKET:
353 924 : c->status = connect_socket(c, state->io);
354 924 : break;
355 924 : case CONNECT_NEGPROT:
356 924 : c->status = connect_negprot(c, state->io);
357 924 : break;
358 894 : case CONNECT_SESSION_SETUP:
359 894 : c->status = connect_session_setup(c, state->io);
360 894 : break;
361 0 : case CONNECT_SESSION_SETUP_ANON:
362 0 : c->status = connect_session_setup_anon(c, state->io);
363 0 : break;
364 890 : case CONNECT_TCON:
365 890 : c->status = connect_tcon(c, state->io);
366 890 : break;
367 0 : case CONNECT_DONE:
368 0 : break;
369 : }
370 :
371 3632 : if (state->stage == CONNECT_DONE) {
372 : /* all done! */
373 890 : composite_done(c);
374 : } else {
375 2742 : composite_is_ok(c);
376 : }
377 3632 : }
378 :
379 :
380 : /*
381 : handler for completion of a smbcli_request sub-request
382 : */
383 890 : static void request_handler(struct smbcli_request *req)
384 : {
385 890 : struct composite_context *c = talloc_get_type(req->async.private_data,
386 : struct composite_context);
387 890 : state_handler(c);
388 890 : }
389 :
390 : /*
391 : handler for completion of a smbcli_composite sub-request
392 : */
393 1818 : static void composite_handler(struct composite_context *creq)
394 : {
395 1818 : struct composite_context *c = talloc_get_type(creq->async.private_data,
396 : struct composite_context);
397 1818 : state_handler(c);
398 1818 : }
399 :
400 : /*
401 : handler for completion of a tevent_req sub-request
402 : */
403 924 : static void subreq_handler(struct tevent_req *subreq)
404 : {
405 866 : struct composite_context *c =
406 924 : tevent_req_callback_data(subreq,
407 : struct composite_context);
408 924 : state_handler(c);
409 924 : }
410 :
411 : /*
412 : a function to establish a smbcli_tree from scratch
413 : */
414 932 : struct composite_context *smb_composite_connect_send(struct smb_composite_connect *io,
415 : TALLOC_CTX *mem_ctx,
416 : struct resolve_context *resolve_ctx,
417 : struct tevent_context *event_ctx)
418 : {
419 : struct composite_context *c;
420 : struct connect_state *state;
421 :
422 932 : c = talloc_zero(mem_ctx, struct composite_context);
423 932 : if (c == NULL) {
424 0 : goto nomem;
425 : }
426 :
427 932 : state = talloc_zero(c, struct connect_state);
428 932 : if (state == NULL) {
429 0 : goto nomem;
430 : }
431 :
432 932 : c->event_ctx = event_ctx;
433 932 : if (c->event_ctx == NULL) {
434 0 : composite_error(c, NT_STATUS_INVALID_PARAMETER_MIX);
435 0 : return c;
436 : }
437 :
438 932 : if (io->in.gensec_settings == NULL) {
439 0 : composite_error(c, NT_STATUS_INVALID_PARAMETER_MIX);
440 0 : return c;
441 : }
442 932 : state->io = io;
443 :
444 932 : c->state = COMPOSITE_STATE_IN_PROGRESS;
445 932 : c->private_data = state;
446 :
447 932 : make_nbt_name_client(&state->calling,
448 : cli_credentials_get_workstation(io->in.credentials));
449 :
450 932 : nbt_choose_called_name(state, &state->called,
451 : io->in.called_name, NBT_NAME_SERVER);
452 :
453 932 : if (io->in.existing_conn != NULL) {
454 : NTSTATUS status;
455 :
456 0 : status = smbcli_transport_raw_init(state,
457 : c->event_ctx,
458 : &io->in.existing_conn,
459 0 : &io->in.options,
460 : &state->transport);
461 0 : if (!NT_STATUS_IS_OK(status)) {
462 0 : composite_error(c, status);
463 0 : return c;
464 : }
465 :
466 0 : status = connect_send_session(c, io);
467 0 : if (!NT_STATUS_IS_OK(status)) {
468 0 : composite_error(c, status);
469 0 : return c;
470 : }
471 :
472 0 : return c;
473 : }
474 :
475 932 : state->creq = smbcli_sock_connect_send(state,
476 : NULL,
477 : io->in.dest_ports,
478 : io->in.dest_host,
479 : resolve_ctx, c->event_ctx,
480 : io->in.socket_options,
481 : &state->calling,
482 : &state->called);
483 932 : if (state->creq == NULL) {
484 0 : composite_error(c, NT_STATUS_NO_MEMORY);
485 0 : return c;
486 : }
487 :
488 932 : state->stage = CONNECT_SOCKET;
489 932 : state->creq->async.private_data = c;
490 932 : state->creq->async.fn = composite_handler;
491 :
492 932 : return c;
493 0 : nomem:
494 0 : TALLOC_FREE(c);
495 0 : return NULL;
496 : }
497 :
498 : /*
499 : recv half of async composite connect code
500 : */
501 932 : NTSTATUS smb_composite_connect_recv(struct composite_context *c, TALLOC_CTX *mem_ctx)
502 : {
503 : NTSTATUS status;
504 :
505 932 : status = composite_wait(c);
506 :
507 924 : if (NT_STATUS_IS_OK(status)) {
508 890 : struct connect_state *state = talloc_get_type(c->private_data, struct connect_state);
509 890 : talloc_steal(mem_ctx, state->io->out.tree);
510 : }
511 :
512 924 : talloc_free(c);
513 924 : return status;
514 : }
515 :
516 : /*
517 : sync version of smb_composite_connect
518 : */
519 756 : NTSTATUS smb_composite_connect(struct smb_composite_connect *io, TALLOC_CTX *mem_ctx,
520 : struct resolve_context *resolve_ctx,
521 : struct tevent_context *ev)
522 : {
523 756 : struct composite_context *c = smb_composite_connect_send(io, mem_ctx, resolve_ctx, ev);
524 756 : if (c == NULL) {
525 0 : return NT_STATUS_NO_MEMORY;
526 : }
527 756 : return smb_composite_connect_recv(c, mem_ctx);
528 : }
|