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 2 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, write to the Free Software
18    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 */
20 /*
21   a composite API for making a full SMB connection
22 */
23 
24 #include "includes.h"
25 #include "libcli/raw/libcliraw.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 
33 /* the stages of this call */
34 enum connect_stage {CONNECT_RESOLVE,
35 		    CONNECT_SOCKET,
36 		    CONNECT_SESSION_REQUEST,
37 		    CONNECT_NEGPROT,
38 		    CONNECT_SESSION_SETUP,
39 		    CONNECT_SESSION_SETUP_ANON,
40 		    CONNECT_TCON};
41 
42 struct connect_state {
43 	enum connect_stage stage;
44 	struct smbcli_socket *sock;
45 	struct smbcli_transport *transport;
46 	struct smbcli_session *session;
47 	struct smb_composite_connect *io;
48 	union smb_tcon *io_tcon;
49 	struct smb_composite_sesssetup *io_setup;
50 	struct smbcli_request *req;
51 	struct composite_context *creq;
52 };
53 
54 
55 static void request_handler(struct smbcli_request *);
56 static void composite_handler(struct composite_context *);
57 
58 /*
59   setup a negprot send
60 */
connect_send_negprot(struct composite_context * c,struct smb_composite_connect * io)61 static NTSTATUS connect_send_negprot(struct composite_context *c,
62 				     struct smb_composite_connect *io)
63 {
64 	struct connect_state *state = talloc_get_type(c->private_data, struct connect_state);
65 
66 	state->req = smb_raw_negotiate_send(state->transport, lp_cli_maxprotocol());
67 	NT_STATUS_HAVE_NO_MEMORY(state->req);
68 
69 	state->req->async.fn = request_handler;
70 	state->req->async.private = c;
71 	state->stage = CONNECT_NEGPROT;
72 
73 	return NT_STATUS_OK;
74 }
75 
76 
77 /*
78   a tree connect request has completed
79 */
connect_tcon(struct composite_context * c,struct smb_composite_connect * io)80 static NTSTATUS connect_tcon(struct composite_context *c,
81 			     struct smb_composite_connect *io)
82 {
83 	struct connect_state *state = talloc_get_type(c->private_data, struct connect_state);
84 	NTSTATUS status;
85 
86 	status = smb_raw_tcon_recv(state->req, c, state->io_tcon);
87 	NT_STATUS_NOT_OK_RETURN(status);
88 
89 	io->out.tree->tid = state->io_tcon->tconx.out.tid;
90 	if (state->io_tcon->tconx.out.dev_type) {
91 		io->out.tree->device = talloc_strdup(io->out.tree,
92 						     state->io_tcon->tconx.out.dev_type);
93 	}
94 	if (state->io_tcon->tconx.out.fs_type) {
95 		io->out.tree->fs_type = talloc_strdup(io->out.tree,
96 						      state->io_tcon->tconx.out.fs_type);
97 	}
98 
99 	/* all done! */
100 	c->state = COMPOSITE_STATE_DONE;
101 
102 	return NT_STATUS_OK;
103 }
104 
105 
106 /*
107   a session setup request with anonymous fallback has completed
108 */
connect_session_setup_anon(struct composite_context * c,struct smb_composite_connect * io)109 static NTSTATUS connect_session_setup_anon(struct composite_context *c,
110 					   struct smb_composite_connect *io)
111 {
112 	struct connect_state *state = talloc_get_type(c->private_data, struct connect_state);
113 	NTSTATUS status;
114 
115 	status = smb_composite_sesssetup_recv(state->creq);
116 	NT_STATUS_NOT_OK_RETURN(status);
117 
118 	io->out.anonymous_fallback_done = True;
119 
120 	state->session->vuid = state->io_setup->out.vuid;
121 
122 	/* setup for a tconx */
123 	io->out.tree = smbcli_tree_init(state->session, state, True);
124 	NT_STATUS_HAVE_NO_MEMORY(io->out.tree);
125 
126 	state->io_tcon = talloc(c, union smb_tcon);
127 	NT_STATUS_HAVE_NO_MEMORY(state->io_tcon);
128 
129 	/* connect to a share using a tree connect */
130 	state->io_tcon->generic.level = RAW_TCON_TCONX;
131 	state->io_tcon->tconx.in.flags = 0;
132 	state->io_tcon->tconx.in.password = data_blob(NULL, 0);
133 
134 	state->io_tcon->tconx.in.path = talloc_asprintf(state->io_tcon,
135 						 "\\\\%s\\%s",
136 						 io->in.called_name,
137 						 io->in.service);
138 	NT_STATUS_HAVE_NO_MEMORY(state->io_tcon->tconx.in.path);
139 	if (!io->in.service_type) {
140 		state->io_tcon->tconx.in.device = "?????";
141 	} else {
142 		state->io_tcon->tconx.in.device = io->in.service_type;
143 	}
144 
145 	state->req = smb_raw_tcon_send(io->out.tree, state->io_tcon);
146 	NT_STATUS_HAVE_NO_MEMORY(state->req);
147 	if (state->req->state == SMBCLI_REQUEST_ERROR) {
148 		return state->req->status;
149 	}
150 
151 	state->req->async.fn = request_handler;
152 	state->req->async.private = c;
153 	state->stage = CONNECT_TCON;
154 
155 	return NT_STATUS_OK;
156 }
157 
158 /*
159   a session setup request has completed
160 */
connect_session_setup(struct composite_context * c,struct smb_composite_connect * io)161 static NTSTATUS connect_session_setup(struct composite_context *c,
162 				      struct smb_composite_connect *io)
163 {
164 	struct connect_state *state = talloc_get_type(c->private_data, struct connect_state);
165 	NTSTATUS status;
166 
167 	status = smb_composite_sesssetup_recv(state->creq);
168 
169 	if (!NT_STATUS_IS_OK(status) &&
170 	    !cli_credentials_is_anonymous(state->io->in.credentials) &&
171 	    io->in.fallback_to_anonymous) {
172 
173 		state->io_setup->in.credentials = cli_credentials_init(state);
174 		NT_STATUS_HAVE_NO_MEMORY(state->io_setup->in.credentials);
175 		cli_credentials_set_conf(state->io_setup->in.credentials);
176 		cli_credentials_set_anonymous(state->io_setup->in.credentials);
177 
178 		/* If the preceding attempt was with extended security, we
179 		 * have been given a uid in the NTLMSSP_CHALLENGE reply. This
180 		 * would lead to an invalid uid in the anonymous fallback */
181 		state->session->vuid = 0;
182 		data_blob_free(&state->session->user_session_key);
183 		talloc_free(state->session->gensec);
184 		state->session->gensec = NULL;
185 
186 		state->creq = smb_composite_sesssetup_send(state->session,
187 							   state->io_setup);
188 		NT_STATUS_HAVE_NO_MEMORY(state->creq);
189 		if (state->creq->state == COMPOSITE_STATE_ERROR) {
190 			return state->creq->status;
191 		}
192 		state->creq->async.fn = composite_handler;
193 		state->creq->async.private_data = c;
194 		state->stage = CONNECT_SESSION_SETUP_ANON;
195 
196 		return NT_STATUS_OK;
197 	}
198 
199 	NT_STATUS_NOT_OK_RETURN(status);
200 
201 	state->session->vuid = state->io_setup->out.vuid;
202 
203 	/* setup for a tconx */
204 	io->out.tree = smbcli_tree_init(state->session, state, True);
205 	NT_STATUS_HAVE_NO_MEMORY(io->out.tree);
206 
207 	state->io_tcon = talloc(c, union smb_tcon);
208 	NT_STATUS_HAVE_NO_MEMORY(state->io_tcon);
209 
210 	/* connect to a share using a tree connect */
211 	state->io_tcon->generic.level = RAW_TCON_TCONX;
212 	state->io_tcon->tconx.in.flags = 0;
213 	state->io_tcon->tconx.in.password = data_blob(NULL, 0);
214 
215 	state->io_tcon->tconx.in.path = talloc_asprintf(state->io_tcon,
216 						 "\\\\%s\\%s",
217 						 io->in.called_name,
218 						 io->in.service);
219 	NT_STATUS_HAVE_NO_MEMORY(state->io_tcon->tconx.in.path);
220 	if (!io->in.service_type) {
221 		state->io_tcon->tconx.in.device = "?????";
222 	} else {
223 		state->io_tcon->tconx.in.device = io->in.service_type;
224 	}
225 
226 	state->req = smb_raw_tcon_send(io->out.tree, state->io_tcon);
227 	NT_STATUS_HAVE_NO_MEMORY(state->req);
228 	if (state->req->state == SMBCLI_REQUEST_ERROR) {
229 		return state->req->status;
230 	}
231 
232 	state->req->async.fn = request_handler;
233 	state->req->async.private = c;
234 	state->stage = CONNECT_TCON;
235 
236 	return NT_STATUS_OK;
237 }
238 
239 /*
240   a negprot request has completed
241 */
connect_negprot(struct composite_context * c,struct smb_composite_connect * io)242 static NTSTATUS connect_negprot(struct composite_context *c,
243 				struct smb_composite_connect *io)
244 {
245 	struct connect_state *state = talloc_get_type(c->private_data, struct connect_state);
246 	NTSTATUS status;
247 
248 	status = smb_raw_negotiate_recv(state->req);
249 	NT_STATUS_NOT_OK_RETURN(status);
250 
251 	/* next step is a session setup */
252 	state->session = smbcli_session_init(state->transport, state, True);
253 	NT_STATUS_HAVE_NO_MEMORY(state->session);
254 
255 	state->io_setup = talloc(c, struct smb_composite_sesssetup);
256 	NT_STATUS_HAVE_NO_MEMORY(state->io_setup);
257 
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 
264 	state->creq = smb_composite_sesssetup_send(state->session, state->io_setup);
265 	NT_STATUS_HAVE_NO_MEMORY(state->creq);
266 	if (state->creq->state == COMPOSITE_STATE_ERROR) {
267 		return state->creq->status;
268 	}
269 
270 	state->creq->async.fn = composite_handler;
271 	state->creq->async.private_data = c;
272 	state->stage = CONNECT_SESSION_SETUP;
273 
274 	return NT_STATUS_OK;
275 }
276 
277 
278 /*
279   a session request operation has completed
280 */
connect_session_request(struct composite_context * c,struct smb_composite_connect * io)281 static NTSTATUS connect_session_request(struct composite_context *c,
282 					struct smb_composite_connect *io)
283 {
284 	struct connect_state *state = talloc_get_type(c->private_data, struct connect_state);
285 	NTSTATUS status;
286 
287 	status = smbcli_transport_connect_recv(state->req);
288 	NT_STATUS_NOT_OK_RETURN(status);
289 
290 	/* next step is a negprot */
291 	return connect_send_negprot(c, io);
292 }
293 
294 /*
295   a socket connection operation has completed
296 */
connect_socket(struct composite_context * c,struct smb_composite_connect * io)297 static NTSTATUS connect_socket(struct composite_context *c,
298 			       struct smb_composite_connect *io)
299 {
300 	struct connect_state *state = talloc_get_type(c->private_data, struct connect_state);
301 	NTSTATUS status;
302 	struct nbt_name calling, called;
303 
304 	status = smbcli_sock_connect_recv(state->creq, state, &state->sock);
305 	NT_STATUS_NOT_OK_RETURN(status);
306 
307 	/* the socket is up - we can initialise the smbcli transport layer */
308 	state->transport = smbcli_transport_init(state->sock, state, True);
309 	NT_STATUS_HAVE_NO_MEMORY(state->transport);
310 
311 	if (is_ipaddress(state->sock->hostname) &&
312 	    (state->io->in.called_name != NULL)) {
313 		/* If connecting to an IP address, we might want the real name
314 		 * of the host for later kerberos. The called name is a better
315 		 * approximation */
316 		state->sock->hostname =
317 			talloc_strdup(state->sock, io->in.called_name);
318 		NT_STATUS_HAVE_NO_MEMORY(state->sock->hostname);
319 	}
320 
321 	make_nbt_name_client(&calling, cli_credentials_get_workstation(io->in.credentials));
322 
323 	nbt_choose_called_name(state, &called, io->in.called_name, NBT_NAME_SERVER);
324 
325 	/* we have a connected socket - next step is a session
326 	   request, if needed. Port 445 doesn't need it, so it goes
327 	   straight to the negprot */
328 	if (state->sock->port == 445) {
329 		status = nbt_name_dup(state->transport, &called,
330 				      &state->transport->called);
331 		NT_STATUS_NOT_OK_RETURN(status);
332 		return connect_send_negprot(c, io);
333 	}
334 
335 	state->req = smbcli_transport_connect_send(state->transport, &calling, &called);
336 	NT_STATUS_HAVE_NO_MEMORY(state->req);
337 
338 	state->req->async.fn = request_handler;
339 	state->req->async.private = c;
340 	state->stage = CONNECT_SESSION_REQUEST;
341 
342 	return NT_STATUS_OK;
343 }
344 
345 
346 /*
347   called when name resolution is finished
348 */
connect_resolve(struct composite_context * c,struct smb_composite_connect * io)349 static NTSTATUS connect_resolve(struct composite_context *c,
350 				struct smb_composite_connect *io)
351 {
352 	struct connect_state *state = talloc_get_type(c->private_data, struct connect_state);
353 	NTSTATUS status;
354 	const char *address;
355 
356 	status = resolve_name_recv(state->creq, state, &address);
357 	NT_STATUS_NOT_OK_RETURN(status);
358 
359 	state->creq = smbcli_sock_connect_send(state, address, io->in.port,
360 					       io->in.dest_host, c->event_ctx);
361 	NT_STATUS_HAVE_NO_MEMORY(state->creq);
362 
363 	state->stage = CONNECT_SOCKET;
364 	state->creq->async.private_data = c;
365 	state->creq->async.fn = composite_handler;
366 
367 	return NT_STATUS_OK;
368 }
369 
370 
371 /*
372   handle and dispatch state transitions
373 */
state_handler(struct composite_context * c)374 static void state_handler(struct composite_context *c)
375 {
376 	struct connect_state *state = talloc_get_type(c->private_data, struct connect_state);
377 
378 	switch (state->stage) {
379 	case CONNECT_RESOLVE:
380 		c->status = connect_resolve(c, state->io);
381 		break;
382 	case CONNECT_SOCKET:
383 		c->status = connect_socket(c, state->io);
384 		break;
385 	case CONNECT_SESSION_REQUEST:
386 		c->status = connect_session_request(c, state->io);
387 		break;
388 	case CONNECT_NEGPROT:
389 		c->status = connect_negprot(c, state->io);
390 		break;
391 	case CONNECT_SESSION_SETUP:
392 		c->status = connect_session_setup(c, state->io);
393 		break;
394 	case CONNECT_SESSION_SETUP_ANON:
395 		c->status = connect_session_setup_anon(c, state->io);
396 		break;
397 	case CONNECT_TCON:
398 		c->status = connect_tcon(c, state->io);
399 		break;
400 	}
401 
402 	if (!NT_STATUS_IS_OK(c->status)) {
403 		c->state = COMPOSITE_STATE_ERROR;
404 	}
405 
406 	if (c->state >= COMPOSITE_STATE_DONE &&
407 	    c->async.fn) {
408 		c->async.fn(c);
409 	}
410 }
411 
412 
413 /*
414   handler for completion of a smbcli_request sub-request
415 */
request_handler(struct smbcli_request * req)416 static void request_handler(struct smbcli_request *req)
417 {
418 	struct composite_context *c = talloc_get_type(req->async.private,
419 						     struct composite_context);
420 	state_handler(c);
421 }
422 
423 /*
424   handler for completion of a smbcli_composite sub-request
425 */
composite_handler(struct composite_context * creq)426 static void composite_handler(struct composite_context *creq)
427 {
428 	struct composite_context *c = talloc_get_type(creq->async.private_data,
429 						     struct composite_context);
430 	state_handler(c);
431 }
432 
433 /*
434   a function to establish a smbcli_tree from scratch
435 */
smb_composite_connect_send(struct smb_composite_connect * io,TALLOC_CTX * mem_ctx,struct event_context * event_ctx)436 struct composite_context *smb_composite_connect_send(struct smb_composite_connect *io,
437 						     TALLOC_CTX *mem_ctx,
438 						     struct event_context *event_ctx)
439 {
440 	struct composite_context *c;
441 	struct connect_state *state;
442 	struct nbt_name name;
443 
444 	c = talloc_zero(mem_ctx, struct composite_context);
445 	if (c == NULL) goto failed;
446 
447 	state = talloc_zero(c, struct connect_state);
448 	if (state == NULL) goto failed;
449 
450 	if (event_ctx == NULL) {
451 		event_ctx = event_context_init(mem_ctx);
452 	}
453 
454 	state->io = io;
455 
456 	c->state = COMPOSITE_STATE_IN_PROGRESS;
457 	c->event_ctx = talloc_reference(c, event_ctx);
458 	c->private_data = state;
459 
460 	state->stage = CONNECT_RESOLVE;
461 	make_nbt_name_server(&name, io->in.dest_host);
462 	state->creq = resolve_name_send(&name, c->event_ctx, lp_name_resolve_order());
463 
464 	if (state->creq == NULL) goto failed;
465 	state->creq->async.private_data = c;
466 	state->creq->async.fn = composite_handler;
467 
468 	return c;
469 failed:
470 	talloc_free(c);
471 	return NULL;
472 }
473 
474 /*
475   recv half of async composite connect code
476 */
smb_composite_connect_recv(struct composite_context * c,TALLOC_CTX * mem_ctx)477 NTSTATUS smb_composite_connect_recv(struct composite_context *c, TALLOC_CTX *mem_ctx)
478 {
479 	NTSTATUS status;
480 
481 	status = composite_wait(c);
482 
483 	if (NT_STATUS_IS_OK(status)) {
484 		struct connect_state *state = talloc_get_type(c->private_data, struct connect_state);
485 		talloc_steal(mem_ctx, state->io->out.tree);
486 	}
487 
488 	talloc_free(c);
489 	return status;
490 }
491 
492 /*
493   sync version of smb_composite_connect
494 */
smb_composite_connect(struct smb_composite_connect * io,TALLOC_CTX * mem_ctx,struct event_context * ev)495 NTSTATUS smb_composite_connect(struct smb_composite_connect *io, TALLOC_CTX *mem_ctx,
496 			       struct event_context *ev)
497 {
498 	struct composite_context *c = smb_composite_connect_send(io, mem_ctx, ev);
499 	return smb_composite_connect_recv(c, mem_ctx);
500 }
501