51f21397f7c25691c9ff51ced4bbed9e20530ea6
[obnox/samba/samba-obnox.git] / source / libsmb / clidfs.c
1 /* 
2    Unix SMB/CIFS implementation.
3    client connect/disconnect routines
4    Copyright (C) Andrew Tridgell                  1994-1998
5    Copyright (C) Gerald (Jerry) Carter            2004
6       
7    This program is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 2 of the License, or
10    (at your option) any later version.
11    
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16    
17    You should have received a copy of the GNU General Public License
18    along with this program; if not, write to the Free Software
19    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 */
21
22 #include "includes.h"
23
24
25 struct client_connection {
26         struct client_connection *prev, *next;
27         struct cli_state *cli;
28         pstring mount;
29 };
30
31 /* global state....globals reek! */
32
33 static pstring username;
34 static pstring password;
35 static BOOL use_kerberos;
36 static BOOL got_pass;
37 static int signing_state;
38 int max_protocol = PROTOCOL_NT1;
39
40 static int port;
41 static int name_type = 0x20;
42 static BOOL have_ip;
43 static struct in_addr dest_ip;
44
45 static struct client_connection *connections;
46
47 /********************************************************************
48  Return a connection to a server.
49 ********************************************************************/
50
51 static struct cli_state *do_connect( const char *server, const char *share,
52                                      BOOL show_sessetup )
53 {
54         struct cli_state *c;
55         struct nmb_name called, calling;
56         const char *server_n;
57         struct in_addr ip;
58         pstring servicename;
59         char *sharename;
60         fstring newserver, newshare;
61         
62         /* make a copy so we don't modify the global string 'service' */
63         pstrcpy(servicename, share);
64         sharename = servicename;
65         if (*sharename == '\\') {
66                 server = sharename+2;
67                 sharename = strchr_m(server,'\\');
68                 if (!sharename) return NULL;
69                 *sharename = 0;
70                 sharename++;
71         }
72
73         server_n = server;
74         
75         zero_ip(&ip);
76
77         make_nmb_name(&calling, global_myname(), 0x0);
78         make_nmb_name(&called , server, name_type);
79
80  again:
81         zero_ip(&ip);
82         if (have_ip) 
83                 ip = dest_ip;
84
85         /* have to open a new connection */
86         if (!(c=cli_initialise(NULL)) || (cli_set_port(c, port) != port) ||
87             !cli_connect(c, server_n, &ip)) {
88                 d_printf("Connection to %s failed\n", server_n);
89                 return NULL;
90         }
91
92         c->protocol = max_protocol;
93         c->use_kerberos = use_kerberos;
94         cli_setup_signing_state(c, signing_state);
95                 
96
97         if (!cli_session_request(c, &calling, &called)) {
98                 char *p;
99                 d_printf("session request to %s failed (%s)\n", 
100                          called.name, cli_errstr(c));
101                 cli_shutdown(c);
102                 if ((p=strchr_m(called.name, '.'))) {
103                         *p = 0;
104                         goto again;
105                 }
106                 if (strcmp(called.name, "*SMBSERVER")) {
107                         make_nmb_name(&called , "*SMBSERVER", 0x20);
108                         goto again;
109                 }
110                 return NULL;
111         }
112
113         DEBUG(4,(" session request ok\n"));
114
115         if (!cli_negprot(c)) {
116                 d_printf("protocol negotiation failed\n");
117                 cli_shutdown(c);
118                 return NULL;
119         }
120
121         if (!got_pass) {
122                 char *pass = getpass("Password: ");
123                 if (pass) {
124                         pstrcpy(password, pass);
125                         got_pass = 1;
126                 }
127         }
128
129         if (!cli_session_setup(c, username, 
130                                password, strlen(password),
131                                password, strlen(password),
132                                lp_workgroup())) {
133                 /* if a password was not supplied then try again with a null username */
134                 if (password[0] || !username[0] || use_kerberos ||
135                     !cli_session_setup(c, "", "", 0, "", 0, lp_workgroup())) { 
136                         d_printf("session setup failed: %s\n", cli_errstr(c));
137                         if (NT_STATUS_V(cli_nt_error(c)) == 
138                             NT_STATUS_V(NT_STATUS_MORE_PROCESSING_REQUIRED))
139                                 d_printf("did you forget to run kinit?\n");
140                         cli_shutdown(c);
141                         return NULL;
142                 }
143                 d_printf("Anonymous login successful\n");
144         }
145
146         if ( show_sessetup ) {
147                 if (*c->server_domain) {
148                         DEBUG(0,("Domain=[%s] OS=[%s] Server=[%s]\n",
149                                 c->server_domain,c->server_os,c->server_type));
150                 } else if (*c->server_os || *c->server_type){
151                         DEBUG(0,("OS=[%s] Server=[%s]\n",
152                                  c->server_os,c->server_type));
153                 }               
154         }
155         DEBUG(4,(" session setup ok\n"));
156
157         /* here's the fun part....to support 'msdfs proxy' shares
158            (on Samba or windows) we have to issues a TRANS_GET_DFS_REFERRAL 
159            here before trying to connect to the original share.
160            check_dfs_proxy() will fail if it is a normal share. */
161
162         if ( (c->capabilities & CAP_DFS) && cli_check_msdfs_proxy( c, sharename, newserver, newshare ) ) {
163                 cli_shutdown(c);
164                 return do_connect( newserver, newshare, False );
165         }
166
167         /* must be a normal share */
168
169         if (!cli_send_tconX(c, sharename, "?????", password, strlen(password)+1)) {
170                 d_printf("tree connect failed: %s\n", cli_errstr(c));
171                 cli_shutdown(c);
172                 return NULL;
173         }
174
175         DEBUG(4,(" tconx ok\n"));
176
177         return c;
178 }
179
180 /****************************************************************************
181 ****************************************************************************/
182
183 static void cli_cm_set_mntpoint( struct cli_state *c, const char *mnt )
184 {
185         struct client_connection *p;
186         int i;
187
188         for ( p=connections,i=0; p; p=p->next,i++ ) {
189                 if ( strequal(p->cli->desthost, c->desthost) && strequal(p->cli->share, c->share) )
190                         break;
191         }
192         
193         if ( p ) {
194                 pstrcpy( p->mount, mnt );
195                 dos_clean_name( p->mount );
196         }
197 }
198
199 /****************************************************************************
200 ****************************************************************************/
201
202 const char * cli_cm_get_mntpoint( struct cli_state *c )
203 {
204         struct client_connection *p;
205         int i;
206
207         for ( p=connections,i=0; p; p=p->next,i++ ) {
208                 if ( strequal(p->cli->desthost, c->desthost) && strequal(p->cli->share, c->share) )
209                         break;
210         }
211         
212         if ( p )
213                 return p->mount;
214                 
215         return NULL;
216 }
217
218 /********************************************************************
219  Add a new connection to the list
220 ********************************************************************/
221
222 static struct cli_state* cli_cm_connect( const char *server, const char *share,
223                                          BOOL show_hdr )
224 {
225         struct client_connection *node;
226         
227         node = SMB_XMALLOC_P( struct client_connection );
228         
229         node->cli = do_connect( server, share, show_hdr );
230
231         if ( !node->cli ) {
232                 SAFE_FREE( node );
233                 return NULL;
234         }
235
236         DLIST_ADD( connections, node );
237
238         cli_cm_set_mntpoint( node->cli, "" );
239
240         return node->cli;
241
242 }
243
244 /********************************************************************
245  Return a connection to a server.
246 ********************************************************************/
247
248 static struct cli_state* cli_cm_find( const char *server, const char *share )
249 {
250         struct client_connection *p;
251
252         for ( p=connections; p; p=p->next ) {
253                 if ( strequal(server, p->cli->desthost) && strequal(share,p->cli->share) )
254                         return p->cli;
255         }
256
257         return NULL;
258 }
259
260 /****************************************************************************
261  open a client connection to a \\server\share.  Set's the current *cli 
262  global variable as a side-effect (but only if the connection is successful).
263 ****************************************************************************/
264
265 struct cli_state* cli_cm_open( const char *server, const char *share, BOOL show_hdr )
266 {
267         struct cli_state *c;
268         
269         /* try to reuse an existing connection */
270
271         c = cli_cm_find( server, share );
272         
273         if ( !c )
274                 c = cli_cm_connect( server, share, show_hdr );
275
276         return c;
277 }
278
279 /****************************************************************************
280 ****************************************************************************/
281
282 void cli_cm_shutdown( void )
283 {
284
285         struct client_connection *p, *x;
286
287         for ( p=connections; p; ) {
288                 cli_shutdown( p->cli );
289                 x = p;
290                 p = p->next;
291
292                 SAFE_FREE( x );
293         }
294
295         connections = NULL;
296
297         return;
298 }
299
300 /****************************************************************************
301 ****************************************************************************/
302
303 void cli_cm_display(void)
304 {
305         struct client_connection *p;
306         int i;
307
308         for ( p=connections,i=0; p; p=p->next,i++ ) {
309                 d_printf("%d:\tserver=%s, share=%s\n", 
310                         i, p->cli->desthost, p->cli->share );
311         }
312 }
313
314 /****************************************************************************
315 ****************************************************************************/
316
317 void cli_cm_set_credentials( struct user_auth_info *user )
318 {
319         pstrcpy( username, user->username );
320         
321         if ( user->got_pass ) {
322                 pstrcpy( password, user->password );
323                 got_pass = True;
324         }
325         
326         use_kerberos = user->use_kerberos;      
327         signing_state = user->signing_state;
328 }
329
330 /****************************************************************************
331 ****************************************************************************/
332
333 void cli_cm_set_port( int port_number )
334 {
335         port = port_number;
336 }
337
338 /****************************************************************************
339 ****************************************************************************/
340
341 void cli_cm_set_dest_name_type( int type )
342 {
343         name_type = type;
344 }
345
346 /****************************************************************************
347 ****************************************************************************/
348
349 void cli_cm_set_dest_ip(struct in_addr ip )
350 {
351         dest_ip = ip;
352         have_ip = True;
353 }
354
355 /********************************************************************
356  split a dfs path into the server and share name components
357 ********************************************************************/
358
359 static void split_dfs_path( const char *nodepath, fstring server, fstring share )
360 {
361         char *p;
362         pstring path;
363
364         pstrcpy( path, nodepath );
365
366         if ( path[0] != '\\' )
367                 return;
368
369         p = strrchr_m( path, '\\' );
370
371         if ( !p )
372                 return;
373
374         *p = '\0';
375         p++;
376
377         fstrcpy( share, p );
378         fstrcpy( server, &path[1] );
379 }
380
381 /****************************************************************************
382  return the original path truncated at the first wildcard character
383  (also strips trailing \'s).  Trust the caller to provide a NULL 
384  terminated string
385 ****************************************************************************/
386
387 static void clean_path( pstring clean, const char *path )
388 {
389         int len;
390         char *p;
391         pstring newpath;
392                 
393         pstrcpy( newpath, path );
394         p = newpath;
395         
396         while ( p ) {
397                 /* first check for '*' */
398                 
399                 p = strrchr_m( newpath, '*' );
400                 if ( p ) {
401                         *p = '\0';
402                         p = newpath;
403                         continue;
404                 }
405         
406                 /* first check for '?' */
407                 
408                 p = strrchr_m( newpath, '?' );
409                 if ( p ) {
410                         *p = '\0';
411                         p = newpath;
412                 }
413         }
414         
415         /* strip a trailing backslash */
416         
417         len = strlen( newpath );
418         if ( newpath[len-1] == '\\' )
419                 newpath[len-1] = '\0';
420                 
421         pstrcpy( clean, newpath );
422 }
423
424 /****************************************************************************
425 ****************************************************************************/
426
427 BOOL cli_dfs_make_full_path( pstring path, const char *server, const char *share,
428                             const char *dir )
429 {
430         pstring servicename;
431         char *sharename;
432         const char *directory;
433
434         
435         /* make a copy so we don't modify the global string 'service' */
436         
437         pstrcpy(servicename, share);
438         sharename = servicename;
439         
440         if (*sharename == '\\') {
441         
442                 server = sharename+2;
443                 sharename = strchr_m(server,'\\');
444                 
445                 if (!sharename) 
446                         return False;
447                         
448                 *sharename = 0;
449                 sharename++;
450         }
451
452         directory = dir;
453         if ( *directory == '\\' )
454                 directory++;
455         
456         pstr_sprintf( path, "\\%s\\%s\\%s", server, sharename, directory );
457
458         return True;
459 }
460
461 /********************************************************************
462  check for dfs referral
463 ********************************************************************/
464
465 static BOOL cli_dfs_check_error( struct cli_state *cli, NTSTATUS status )
466 {
467         uint32 flgs2 = SVAL(cli->inbuf,smb_flg2);
468
469         /* only deal with DS when we negotiated NT_STATUS codes and UNICODE */
470
471         if ( !( (flgs2&FLAGS2_32_BIT_ERROR_CODES) && (flgs2&FLAGS2_UNICODE_STRINGS) ) )
472                 return False;
473
474         if ( NT_STATUS_EQUAL( status, NT_STATUS(IVAL(cli->inbuf,smb_rcls)) ) )
475                 return True;
476
477         return False;
478 }
479
480 /********************************************************************
481  get the dfs referral link
482 ********************************************************************/
483
484 BOOL cli_dfs_get_referral( struct cli_state *cli, const char *path, 
485                            CLIENT_DFS_REFERRAL**refs, size_t *num_refs,
486                            uint16 *consumed)
487 {
488         unsigned int data_len = 0;
489         unsigned int param_len = 0;
490         uint16 setup = TRANSACT2_GET_DFS_REFERRAL;
491         char param[sizeof(pstring)+2];
492         pstring data;
493         char *rparam=NULL, *rdata=NULL;
494         char *p;
495         size_t pathlen = 2*(strlen(path)+1);
496         uint16 num_referrals;
497         CLIENT_DFS_REFERRAL *referrals = NULL;
498         
499         memset(param, 0, sizeof(param));
500         SSVAL(param, 0, 0x03);  /* max referral level */
501         p = &param[2];
502
503         p += clistr_push(cli, p, path, MIN(pathlen, sizeof(param)-2), STR_TERMINATE);
504         param_len = PTR_DIFF(p, param);
505
506         if (!cli_send_trans(cli, SMBtrans2,
507                 NULL,                        /* name */
508                 -1, 0,                          /* fid, flags */
509                 &setup, 1, 0,                   /* setup, length, max */
510                 param, param_len, 2,            /* param, length, max */
511                 (char *)&data,  data_len, cli->max_xmit /* data, length, max */
512                 )) {
513                         return False;
514         }
515
516         if (!cli_receive_trans(cli, SMBtrans2,
517                 &rparam, &param_len,
518                 &rdata, &data_len)) {
519                         return False;
520         }
521         
522         *consumed     = SVAL( rdata, 0 );
523         num_referrals = SVAL( rdata, 2 );
524         
525         if ( num_referrals != 0 ) {
526                 uint16 ref_version;
527                 uint16 ref_size;
528                 int i;
529                 uint16 node_offset;
530                 
531                 
532                 referrals = SMB_XMALLOC_ARRAY( CLIENT_DFS_REFERRAL, num_referrals );
533         
534                 /* start at the referrals array */
535         
536                 p = rdata+8;
537                 for ( i=0; i<num_referrals; i++ ) {
538                         ref_version = SVAL( p, 0 );
539                         ref_size    = SVAL( p, 2 );
540                         node_offset = SVAL( p, 16 );
541                         
542                         if ( ref_version != 3 ) {
543                                 p += ref_size;
544                                 continue;
545                         }
546                         
547                         referrals[i].proximity = SVAL( p, 8 );
548                         referrals[i].ttl       = SVAL( p, 10 );
549
550                         clistr_pull( cli, referrals[i].dfspath, p+node_offset, 
551                                 sizeof(referrals[i].dfspath), -1, STR_TERMINATE|STR_UNICODE );
552
553                         p += ref_size;
554                 }
555         
556         }
557         
558         *num_refs = num_referrals;
559         *refs = referrals;
560
561         SAFE_FREE(rdata);
562         SAFE_FREE(rparam);
563
564         return True;
565 }
566
567 /********************************************************************
568 ********************************************************************/
569
570 BOOL cli_resolve_path( const char *mountpt, struct cli_state *rootcli, const char *path,
571                        struct cli_state **targetcli, pstring targetpath )
572 {
573         CLIENT_DFS_REFERRAL *refs = NULL;
574         size_t num_refs;
575         uint16 consumed;
576         struct cli_state *cli_ipc;
577         pstring fullpath, cleanpath;
578         int pathlen;
579         fstring server, share;
580         struct cli_state *newcli;
581         pstring newpath;
582         pstring newmount;
583         char *ppath;
584         
585         SMB_STRUCT_STAT sbuf;
586         uint32 attributes;
587         
588         *targetcli = NULL;
589         
590         if ( !rootcli || !path || !targetcli )
591                 return False;
592                 
593         /* send a trans2_query_path_info to check for a referral */
594         
595         clean_path( cleanpath,  path );
596         cli_dfs_make_full_path( fullpath, rootcli->desthost, rootcli->share, cleanpath );
597
598         /* don't bother continuing if this is not a dfs root */
599         
600         if ( !rootcli->dfsroot || cli_qpathinfo_basic( rootcli, cleanpath, &sbuf, &attributes ) ) {
601                 *targetcli = rootcli;
602                 pstrcpy( targetpath, path );
603                 return True;
604         }
605
606         /* special case where client asked for a path that does not exist */
607
608         if ( cli_dfs_check_error(rootcli, NT_STATUS_OBJECT_NAME_NOT_FOUND) ) {
609                 *targetcli = rootcli;
610                 pstrcpy( targetpath, path );
611                 return True;
612         }
613
614         /* we got an error, check for DFS referral */
615                         
616         if ( !cli_dfs_check_error(rootcli, NT_STATUS_PATH_NOT_COVERED) ) 
617                 return False;
618
619         /* check for the referral */
620
621         if ( !(cli_ipc = cli_cm_open( rootcli->desthost, "IPC$", False )) )
622                 return False;
623         
624         if ( !cli_dfs_get_referral(cli_ipc, fullpath, &refs, &num_refs, &consumed) 
625                 || !num_refs )
626         {
627                 return False;
628         }
629         
630         /* just store the first referral for now
631            Make sure to recreate the original string including any wildcards */
632         
633         cli_dfs_make_full_path( fullpath, rootcli->desthost, rootcli->share, path );
634         pathlen = strlen( fullpath )*2;
635         consumed = MIN(pathlen, consumed );
636         pstrcpy( targetpath, &fullpath[consumed/2] );
637
638         split_dfs_path( refs[0].dfspath, server, share );
639         SAFE_FREE( refs );
640         
641         /* open the connection to the target path */
642         
643         if ( (*targetcli = cli_cm_open(server, share, False)) == NULL ) {
644                 d_printf("Unable to follow dfs referral [//%s/%s]\n",
645                         server, share );
646                         
647                 return False;
648         }
649         
650         /* parse out the consumed mount path */
651         /* trim off the \server\share\ */
652
653         fullpath[consumed/2] = '\0';
654         dos_clean_name( fullpath );
655         ppath = strchr_m( fullpath, '\\' );
656         ppath = strchr_m( ppath+1, '\\' );
657         ppath = strchr_m( ppath+1, '\\' );
658         ppath++;
659         
660         pstr_sprintf( newmount, "%s\\%s", mountpt, ppath );
661         cli_cm_set_mntpoint( *targetcli, newmount );
662
663         /* check for another dfs referral, note that we are not 
664            checking for loops here */
665
666         if ( !strequal( targetpath, "\\" ) ) {
667                 if ( cli_resolve_path( newmount, *targetcli, targetpath, &newcli, newpath ) ) {
668                         *targetcli = newcli;
669                         pstrcpy( targetpath, newpath );
670                 }
671         }
672
673         return True;
674 }
675
676 /********************************************************************
677 ********************************************************************/
678
679 BOOL cli_check_msdfs_proxy( struct cli_state *cli, const char *sharename,
680                             fstring newserver, fstring newshare )
681 {
682         CLIENT_DFS_REFERRAL *refs = NULL;
683         size_t num_refs;
684         uint16 consumed;
685         struct cli_state *cli_ipc;
686         pstring fullpath;
687         
688         if ( !cli || !sharename )
689                 return False;
690
691         /* special case.  never check for a referral on the IPC$ share */
692
693         if ( strequal( sharename, "IPC$" ) )
694                 return False;
695                 
696         /* send a trans2_query_path_info to check for a referral */
697         
698         pstr_sprintf( fullpath, "\\%s\\%s", cli->desthost, sharename );
699
700         /* check for the referral */
701
702         if ( !(cli_ipc = cli_cm_open( cli->desthost, "IPC$", False )) )
703                 return False;
704         
705         if ( !cli_dfs_get_referral(cli_ipc, fullpath, &refs, &num_refs, &consumed) 
706                 || !num_refs )
707         {
708                 return False;
709         }
710         
711         split_dfs_path( refs[0].dfspath, newserver, newshare );
712
713         /* check that this is not a self-referral */
714
715         if ( strequal( cli->desthost, newserver ) && strequal( sharename, newshare ) )
716                 return False;
717         
718         SAFE_FREE( refs );
719         
720         return True;
721 }