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