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 2741 : static NTSTATUS connect_tcon(struct composite_context *c,
68 : struct smb_composite_connect *io)
69 : {
70 2741 : struct connect_state *state = talloc_get_type(c->private_data, struct connect_state);
71 133 : NTSTATUS status;
72 :
73 2741 : status = smb_raw_tcon_recv(state->req, c, state->io_tcon);
74 2741 : NT_STATUS_NOT_OK_RETURN(status);
75 :
76 2741 : if (state->io_tcon->tconx.out.options & SMB_EXTENDED_SIGNATURES) {
77 1879 : smb1cli_session_protect_session_key(io->out.tree->session->smbXcli);
78 : }
79 :
80 2741 : io->out.tree->tid = state->io_tcon->tconx.out.tid;
81 2741 : if (state->io_tcon->tconx.out.dev_type) {
82 2741 : io->out.tree->device = talloc_strdup(io->out.tree,
83 2608 : state->io_tcon->tconx.out.dev_type);
84 : }
85 2741 : if (state->io_tcon->tconx.out.fs_type) {
86 2741 : io->out.tree->fs_type = talloc_strdup(io->out.tree,
87 2608 : state->io_tcon->tconx.out.fs_type);
88 : }
89 :
90 2741 : state->stage = CONNECT_DONE;
91 :
92 2741 : 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 0 : 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 2747 : static NTSTATUS connect_session_setup(struct composite_context *c,
149 : struct smb_composite_connect *io)
150 : {
151 2747 : struct connect_state *state = talloc_get_type(c->private_data, struct connect_state);
152 133 : NTSTATUS status;
153 :
154 2747 : status = smb_composite_sesssetup_recv(state->creq);
155 :
156 2747 : if (!NT_STATUS_IS_OK(status) &&
157 6 : !cli_credentials_is_anonymous(state->io->in.credentials) &&
158 6 : 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 2747 : NT_STATUS_NOT_OK_RETURN(status);
188 :
189 2741 : 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 2741 : if (!io->in.service) {
194 0 : state->stage = CONNECT_DONE;
195 0 : return NT_STATUS_OK;
196 : }
197 :
198 2741 : state->io_tcon = talloc(c, union smb_tcon);
199 2741 : NT_STATUS_HAVE_NO_MEMORY(state->io_tcon);
200 :
201 : /* connect to a share using a tree connect */
202 2741 : state->io_tcon->generic.level = RAW_TCON_TCONX;
203 2741 : state->io_tcon->tconx.in.flags = TCONX_FLAG_EXTENDED_RESPONSE;
204 2741 : state->io_tcon->tconx.in.flags |= TCONX_FLAG_EXTENDED_SIGNATURES;
205 2741 : state->io_tcon->tconx.in.password = data_blob(NULL, 0);
206 :
207 2741 : state->io_tcon->tconx.in.path = talloc_asprintf(state->io_tcon,
208 : "\\\\%s\\%s",
209 : io->in.called_name,
210 : io->in.service);
211 2741 : NT_STATUS_HAVE_NO_MEMORY(state->io_tcon->tconx.in.path);
212 2741 : if (!io->in.service_type) {
213 2481 : state->io_tcon->tconx.in.device = "?????";
214 : } else {
215 260 : state->io_tcon->tconx.in.device = io->in.service_type;
216 : }
217 :
218 2741 : state->req = smb_raw_tcon_send(io->out.tree, state->io_tcon);
219 2741 : NT_STATUS_HAVE_NO_MEMORY(state->req);
220 2741 : if (state->req->state == SMBCLI_REQUEST_ERROR) {
221 0 : return state->req->status;
222 : }
223 :
224 2741 : state->req->async.fn = request_handler;
225 2741 : state->req->async.private_data = c;
226 2741 : state->stage = CONNECT_TCON;
227 :
228 2741 : return NT_STATUS_OK;
229 : }
230 :
231 2749 : static NTSTATUS connect_send_session(struct composite_context *c,
232 : struct smb_composite_connect *io)
233 : {
234 2749 : struct connect_state *state = talloc_get_type(c->private_data, struct connect_state);
235 :
236 : /* next step is a session setup */
237 2749 : state->session = smbcli_session_init(state->transport, state, true, io->in.session_options);
238 2749 : 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 2749 : io->out.tree = smbcli_tree_init(state->session, state, true);
243 2749 : 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 2749 : if (!io->in.credentials) {
248 0 : state->stage = CONNECT_DONE;
249 0 : return NT_STATUS_OK;
250 : }
251 :
252 2749 : state->io_setup = talloc(c, struct smb_composite_sesssetup);
253 2749 : NT_STATUS_HAVE_NO_MEMORY(state->io_setup);
254 :
255 : /* prepare a session setup to establish a security context */
256 2749 : state->io_setup->in.sesskey = state->transport->negotiate.sesskey;
257 2749 : state->io_setup->in.capabilities = state->transport->negotiate.capabilities;
258 2749 : state->io_setup->in.credentials = io->in.credentials;
259 2749 : state->io_setup->in.workgroup = io->in.workgroup;
260 2749 : state->io_setup->in.gensec_settings = io->in.gensec_settings;
261 :
262 2749 : state->creq = smb_composite_sesssetup_send(state->session, state->io_setup);
263 2749 : NT_STATUS_HAVE_NO_MEMORY(state->creq);
264 2749 : if (state->creq->state == COMPOSITE_STATE_ERROR) {
265 2 : return state->creq->status;
266 : }
267 :
268 2747 : state->creq->async.fn = composite_handler;
269 2747 : state->creq->async.private_data = c;
270 :
271 2747 : state->stage = CONNECT_SESSION_SETUP;
272 :
273 2747 : return NT_STATUS_OK;
274 : }
275 :
276 : /*
277 : a negprot request has completed
278 : */
279 2831 : static NTSTATUS connect_negprot(struct composite_context *c,
280 : struct smb_composite_connect *io)
281 : {
282 2831 : struct connect_state *state = talloc_get_type(c->private_data, struct connect_state);
283 141 : NTSTATUS status;
284 :
285 2831 : status = smb_raw_negotiate_recv(state->subreq);
286 2831 : TALLOC_FREE(state->subreq);
287 2831 : NT_STATUS_NOT_OK_RETURN(status);
288 :
289 2723 : return connect_send_session(c, io);
290 : }
291 :
292 : /*
293 : setup a negprot send
294 : */
295 2831 : static NTSTATUS connect_send_negprot(struct composite_context *c,
296 : struct smb_composite_connect *io)
297 : {
298 2831 : 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 2831 : state->transport = smbcli_transport_init(state->sock, state, true,
302 : &io->in.options);
303 2831 : NT_STATUS_HAVE_NO_MEMORY(state->transport);
304 :
305 5521 : state->subreq = smb_raw_negotiate_send(state,
306 2690 : state->transport->ev,
307 : state->transport,
308 2690 : state->transport->options.min_protocol,
309 2690 : state->transport->options.max_protocol);
310 2831 : NT_STATUS_HAVE_NO_MEMORY(state->subreq);
311 2831 : tevent_req_set_callback(state->subreq, subreq_handler, c);
312 2831 : state->stage = CONNECT_NEGPROT;
313 :
314 2831 : return NT_STATUS_OK;
315 : }
316 :
317 : /*
318 : a socket connection operation has completed
319 : */
320 2831 : static NTSTATUS connect_socket(struct composite_context *c,
321 : struct smb_composite_connect *io)
322 : {
323 2831 : struct connect_state *state = talloc_get_type(c->private_data, struct connect_state);
324 141 : NTSTATUS status;
325 :
326 2831 : status = smbcli_sock_connect_recv(state->creq, state, &state->sock);
327 2831 : NT_STATUS_NOT_OK_RETURN(status);
328 :
329 2831 : if (is_ipaddress(state->sock->hostname) &&
330 1104 : (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 2208 : state->sock->hostname =
335 1104 : talloc_strdup(state->sock, io->in.called_name);
336 1104 : NT_STATUS_HAVE_NO_MEMORY(state->sock->hostname);
337 : }
338 :
339 : /* next step is a negprot */
340 2831 : return connect_send_negprot(c, io);
341 : }
342 :
343 :
344 : /*
345 : handle and dispatch state transitions
346 : */
347 11150 : static void state_handler(struct composite_context *c)
348 : {
349 11150 : struct connect_state *state = talloc_get_type(c->private_data, struct connect_state);
350 :
351 11150 : switch (state->stage) {
352 2831 : case CONNECT_SOCKET:
353 2831 : c->status = connect_socket(c, state->io);
354 2831 : break;
355 2831 : case CONNECT_NEGPROT:
356 2831 : c->status = connect_negprot(c, state->io);
357 2831 : break;
358 2747 : case CONNECT_SESSION_SETUP:
359 2747 : c->status = connect_session_setup(c, state->io);
360 2747 : break;
361 0 : case CONNECT_SESSION_SETUP_ANON:
362 0 : c->status = connect_session_setup_anon(c, state->io);
363 0 : break;
364 2741 : case CONNECT_TCON:
365 2741 : c->status = connect_tcon(c, state->io);
366 2741 : break;
367 0 : case CONNECT_DONE:
368 0 : break;
369 : }
370 :
371 11150 : if (state->stage == CONNECT_DONE) {
372 : /* all done! */
373 2741 : composite_done(c);
374 : } else {
375 8409 : composite_is_ok(c);
376 : }
377 11150 : }
378 :
379 :
380 : /*
381 : handler for completion of a smbcli_request sub-request
382 : */
383 2741 : static void request_handler(struct smbcli_request *req)
384 : {
385 2741 : struct composite_context *c = talloc_get_type(req->async.private_data,
386 : struct composite_context);
387 2741 : state_handler(c);
388 2741 : }
389 :
390 : /*
391 : handler for completion of a smbcli_composite sub-request
392 : */
393 5578 : static void composite_handler(struct composite_context *creq)
394 : {
395 5578 : struct composite_context *c = talloc_get_type(creq->async.private_data,
396 : struct composite_context);
397 5578 : state_handler(c);
398 5578 : }
399 :
400 : /*
401 : handler for completion of a tevent_req sub-request
402 : */
403 2831 : static void subreq_handler(struct tevent_req *subreq)
404 : {
405 141 : struct composite_context *c =
406 2831 : tevent_req_callback_data(subreq,
407 : struct composite_context);
408 2831 : state_handler(c);
409 2831 : }
410 :
411 : /*
412 : a function to establish a smbcli_tree from scratch
413 : */
414 2857 : 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 141 : struct composite_context *c;
420 141 : struct connect_state *state;
421 :
422 2857 : c = talloc_zero(mem_ctx, struct composite_context);
423 2857 : if (c == NULL) {
424 0 : goto nomem;
425 : }
426 :
427 2857 : state = talloc_zero(c, struct connect_state);
428 2857 : if (state == NULL) {
429 0 : goto nomem;
430 : }
431 :
432 2857 : c->event_ctx = event_ctx;
433 2857 : if (c->event_ctx == NULL) {
434 0 : composite_error(c, NT_STATUS_INVALID_PARAMETER_MIX);
435 0 : return c;
436 : }
437 :
438 2857 : if (io->in.gensec_settings == NULL) {
439 0 : composite_error(c, NT_STATUS_INVALID_PARAMETER_MIX);
440 0 : return c;
441 : }
442 2857 : state->io = io;
443 :
444 2857 : c->state = COMPOSITE_STATE_IN_PROGRESS;
445 2857 : c->private_data = state;
446 :
447 2857 : make_nbt_name_client(&state->calling,
448 : cli_credentials_get_workstation(io->in.credentials));
449 :
450 2857 : nbt_choose_called_name(state, &state->called,
451 : io->in.called_name, NBT_NAME_SERVER);
452 :
453 2857 : if (io->in.existing_conn != NULL) {
454 0 : NTSTATUS status;
455 :
456 26 : status = smbcli_transport_raw_init(state,
457 : c->event_ctx,
458 : &io->in.existing_conn,
459 26 : &io->in.options,
460 : &state->transport);
461 26 : if (!NT_STATUS_IS_OK(status)) {
462 0 : composite_error(c, status);
463 0 : return c;
464 : }
465 :
466 26 : status = connect_send_session(c, io);
467 26 : if (!NT_STATUS_IS_OK(status)) {
468 2 : composite_error(c, status);
469 2 : return c;
470 : }
471 :
472 24 : return c;
473 : }
474 :
475 2831 : 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 2831 : if (state->creq == NULL) {
484 0 : composite_error(c, NT_STATUS_NO_MEMORY);
485 0 : return c;
486 : }
487 :
488 2831 : state->stage = CONNECT_SOCKET;
489 2831 : state->creq->async.private_data = c;
490 2831 : state->creq->async.fn = composite_handler;
491 :
492 2831 : 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 2857 : NTSTATUS smb_composite_connect_recv(struct composite_context *c, TALLOC_CTX *mem_ctx)
502 : {
503 141 : NTSTATUS status;
504 :
505 2857 : status = composite_wait(c);
506 :
507 2857 : if (NT_STATUS_IS_OK(status)) {
508 2741 : struct connect_state *state = talloc_get_type(c->private_data, struct connect_state);
509 2741 : talloc_steal(mem_ctx, state->io->out.tree);
510 : }
511 :
512 2857 : talloc_free(c);
513 2857 : return status;
514 : }
515 :
516 : /*
517 : sync version of smb_composite_connect
518 : */
519 2575 : 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 2575 : struct composite_context *c = smb_composite_connect_send(io, mem_ctx, resolve_ctx, ev);
524 2575 : if (c == NULL) {
525 0 : return NT_STATUS_NO_MEMORY;
526 : }
527 2575 : return smb_composite_connect_recv(c, mem_ctx);
528 : }
|