r5995: merging more changes from 3.0 tree.
[Samba.git] / source / libsmb / clidfs.c
blob21046cd380bc7d4760ba5642a3f7f401a9a12d2e
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
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.
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.
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.
22 #define NO_SYSLOG
24 #include "includes.h"
27 struct client_connection {
28 struct client_connection *prev, *next;
29 struct cli_state *cli;
30 pstring mount;
33 /* global state....globals reek! */
35 static pstring username;
36 static pstring password;
37 static BOOL use_kerberos;
38 static BOOL got_pass;
39 static int signing_state;
40 int max_protocol = PROTOCOL_NT1;
42 static int port;
43 static int name_type = 0x20;
44 static BOOL have_ip;
45 static struct in_addr dest_ip;
47 static struct client_connection *connections;
49 /********************************************************************
50 Return a connection to a server.
51 ********************************************************************/
53 static struct cli_state *do_connect( const char *server, const char *share,
54 BOOL show_sessetup )
56 struct cli_state *c;
57 struct nmb_name called, calling;
58 const char *server_n;
59 struct in_addr ip;
60 pstring servicename;
61 char *sharename;
63 /* make a copy so we don't modify the global string 'service' */
64 pstrcpy(servicename, share);
65 sharename = servicename;
66 if (*sharename == '\\') {
67 server = sharename+2;
68 sharename = strchr_m(server,'\\');
69 if (!sharename) return NULL;
70 *sharename = 0;
71 sharename++;
74 server_n = server;
76 zero_ip(&ip);
78 make_nmb_name(&calling, global_myname(), 0x0);
79 make_nmb_name(&called , server, name_type);
81 again:
82 zero_ip(&ip);
83 if (have_ip)
84 ip = dest_ip;
86 /* have to open a new connection */
87 if (!(c=cli_initialise(NULL)) || (cli_set_port(c, port) != port) ||
88 !cli_connect(c, server_n, &ip)) {
89 d_printf("Connection to %s failed\n", server_n);
90 return NULL;
93 c->protocol = max_protocol;
94 c->use_kerberos = use_kerberos;
95 cli_setup_signing_state(c, signing_state);
98 if (!cli_session_request(c, &calling, &called)) {
99 char *p;
100 d_printf("session request to %s failed (%s)\n",
101 called.name, cli_errstr(c));
102 cli_shutdown(c);
103 if ((p=strchr_m(called.name, '.'))) {
104 *p = 0;
105 goto again;
107 if (strcmp(called.name, "*SMBSERVER")) {
108 make_nmb_name(&called , "*SMBSERVER", 0x20);
109 goto again;
111 return NULL;
114 DEBUG(4,(" session request ok\n"));
116 if (!cli_negprot(c)) {
117 d_printf("protocol negotiation failed\n");
118 cli_shutdown(c);
119 return NULL;
122 if (!got_pass) {
123 char *pass = getpass("Password: ");
124 if (pass) {
125 pstrcpy(password, pass);
126 got_pass = 1;
130 if (!cli_session_setup(c, username,
131 password, strlen(password),
132 password, strlen(password),
133 lp_workgroup())) {
134 /* if a password was not supplied then try again with a null username */
135 if (password[0] || !username[0] || use_kerberos ||
136 !cli_session_setup(c, "", "", 0, "", 0, lp_workgroup())) {
137 d_printf("session setup failed: %s\n", cli_errstr(c));
138 if (NT_STATUS_V(cli_nt_error(c)) ==
139 NT_STATUS_V(NT_STATUS_MORE_PROCESSING_REQUIRED))
140 d_printf("did you forget to run kinit?\n");
141 cli_shutdown(c);
142 return NULL;
144 d_printf("Anonymous login successful\n");
147 if ( show_sessetup ) {
148 if (*c->server_domain) {
149 DEBUG(0,("Domain=[%s] OS=[%s] Server=[%s]\n",
150 c->server_domain,c->server_os,c->server_type));
151 } else if (*c->server_os || *c->server_type){
152 DEBUG(0,("OS=[%s] Server=[%s]\n",
153 c->server_os,c->server_type));
156 DEBUG(4,(" session setup ok\n"));
158 if (!cli_send_tconX(c, sharename, "?????",
159 password, strlen(password)+1)) {
160 d_printf("tree connect failed: %s\n", cli_errstr(c));
161 cli_shutdown(c);
162 return NULL;
165 DEBUG(4,(" tconx ok\n"));
167 return c;
170 /****************************************************************************
171 ****************************************************************************/
173 static void cli_cm_set_mntpoint( struct cli_state *c, const char *mnt )
175 struct client_connection *p;
176 int i;
178 for ( p=connections,i=0; p; p=p->next,i++ ) {
179 if ( strequal(p->cli->desthost, c->desthost) && strequal(p->cli->share, c->share) )
180 break;
183 if ( p ) {
184 pstrcpy( p->mount, mnt );
185 dos_clean_name( p->mount );
189 /****************************************************************************
190 ****************************************************************************/
192 const char * cli_cm_get_mntpoint( struct cli_state *c )
194 struct client_connection *p;
195 int i;
197 for ( p=connections,i=0; p; p=p->next,i++ ) {
198 if ( strequal(p->cli->desthost, c->desthost) && strequal(p->cli->share, c->share) )
199 break;
202 if ( p )
203 return p->mount;
205 return NULL;
208 /********************************************************************
209 Add a new connection to the list
210 ********************************************************************/
212 static struct cli_state* cli_cm_connect( const char *server, const char *share,
213 BOOL show_hdr )
215 struct client_connection *node;
217 node = SMB_XMALLOC_P( struct client_connection );
219 node->cli = do_connect( server, share, show_hdr );
221 if ( !node->cli ) {
222 SAFE_FREE( node );
223 return NULL;
226 DLIST_ADD( connections, node );
228 cli_cm_set_mntpoint( node->cli, "" );
230 return node->cli;
234 /********************************************************************
235 Return a connection to a server.
236 ********************************************************************/
238 static struct cli_state* cli_cm_find( const char *server, const char *share )
240 struct client_connection *p;
242 for ( p=connections; p; p=p->next ) {
243 if ( strequal(server, p->cli->desthost) && strequal(share,p->cli->share) )
244 return p->cli;
247 return NULL;
250 /****************************************************************************
251 open a client connection to a \\server\share. Set's the current *cli
252 global variable as a side-effect (but only if the connection is successful).
253 ****************************************************************************/
255 struct cli_state* cli_cm_open( const char *server, const char *share, BOOL show_hdr )
257 struct cli_state *c;
259 /* try to reuse an existing connection */
261 c = cli_cm_find( server, share );
263 if ( !c )
264 c = cli_cm_connect( server, share, show_hdr );
266 return c;
269 /****************************************************************************
270 ****************************************************************************/
272 void cli_cm_shutdown( void )
275 struct client_connection *p, *x;
277 for ( p=connections; p; ) {
278 cli_shutdown( p->cli );
279 x = p;
280 p = p->next;
282 SAFE_FREE( x );
285 connections = NULL;
287 return;
290 /****************************************************************************
291 ****************************************************************************/
293 void cli_cm_display(void)
295 struct client_connection *p;
296 int i;
298 for ( p=connections,i=0; p; p=p->next,i++ ) {
299 d_printf("%d:\tserver=%s, share=%s\n",
300 i, p->cli->desthost, p->cli->share );
304 /****************************************************************************
305 ****************************************************************************/
307 void cli_cm_set_credentials( struct user_auth_info *user )
309 pstrcpy( username, user->username );
311 if ( user->got_pass ) {
312 pstrcpy( password, user->password );
313 got_pass = True;
316 use_kerberos = user->use_kerberos;
317 signing_state = user->signing_state;
320 /****************************************************************************
321 ****************************************************************************/
323 void cli_cm_set_port( int port_number )
325 port = port_number;
328 /****************************************************************************
329 ****************************************************************************/
331 void cli_cm_set_dest_name_type( int type )
333 name_type = type;
336 /****************************************************************************
337 ****************************************************************************/
339 void cli_cm_set_dest_ip(struct in_addr ip )
341 dest_ip = ip;
342 have_ip = True;
345 /********************************************************************
346 split a dfs path into the server and share name components
347 ********************************************************************/
349 static void split_dfs_path( const char *nodepath, fstring server, fstring share )
351 char *p;
352 pstring path;
354 pstrcpy( path, nodepath );
356 if ( path[0] != '\\' )
357 return;
359 p = strrchr_m( path, '\\' );
361 if ( !p )
362 return;
364 *p = '\0';
365 p++;
367 fstrcpy( share, p );
368 fstrcpy( server, &path[1] );
371 /****************************************************************************
372 return the original path truncated at the first wildcard character
373 (also strips trailing \'s). Trust the caller to provide a NULL
374 terminated string
375 ****************************************************************************/
377 static void clean_path( pstring clean, const char *path )
379 int len;
380 char *p;
381 pstring newpath;
383 pstrcpy( newpath, path );
384 p = newpath;
386 while ( p ) {
387 /* first check for '*' */
389 p = strrchr_m( newpath, '*' );
390 if ( p ) {
391 *p = '\0';
392 p = newpath;
393 continue;
396 /* first check for '?' */
398 p = strrchr_m( newpath, '?' );
399 if ( p ) {
400 *p = '\0';
401 p = newpath;
405 /* strip a trailing backslash */
407 len = strlen( newpath );
408 if ( newpath[len-1] == '\\' )
409 newpath[len-1] = '\0';
411 pstrcpy( clean, newpath );
414 /****************************************************************************
415 ****************************************************************************/
417 static BOOL make_full_path( pstring path, const char *server, const char *share,
418 const char *dir )
420 pstring servicename;
421 char *sharename;
422 const char *directory;
425 /* make a copy so we don't modify the global string 'service' */
427 pstrcpy(servicename, share);
428 sharename = servicename;
430 if (*sharename == '\\') {
432 server = sharename+2;
433 sharename = strchr_m(server,'\\');
435 if (!sharename)
436 return False;
438 *sharename = 0;
439 sharename++;
442 directory = dir;
443 if ( *directory == '\\' )
444 directory++;
446 pstr_sprintf( path, "\\%s\\%s\\%s", server, sharename, directory );
448 return True;
451 /********************************************************************
452 check for dfs referral
453 ********************************************************************/
455 static BOOL cli_dfs_check_error( struct cli_state *cli )
457 uint32 flgs2 = SVAL(cli->inbuf,smb_flg2);
459 /* only deal with DS when we negotiated NT_STATUS codes and UNICODE */
461 if ( !( (flgs2&FLAGS2_32_BIT_ERROR_CODES) && (flgs2&FLAGS2_UNICODE_STRINGS) ) )
462 return False;
464 if ( NT_STATUS_EQUAL( NT_STATUS_PATH_NOT_COVERED, NT_STATUS(IVAL(cli->inbuf,smb_rcls)) ) )
465 return True;
467 return False;
470 /********************************************************************
471 get the dfs referral link
472 ********************************************************************/
474 BOOL cli_dfs_get_referral( struct cli_state *cli, const char *path,
475 CLIENT_DFS_REFERRAL**refs, size_t *num_refs,
476 uint16 *consumed)
478 unsigned int data_len = 0;
479 unsigned int param_len = 0;
480 uint16 setup = TRANSACT2_GET_DFS_REFERRAL;
481 char param[sizeof(pstring)+2];
482 pstring data;
483 char *rparam=NULL, *rdata=NULL;
484 char *p;
485 size_t pathlen = 2*(strlen(path)+1);
486 uint16 num_referrals;
487 CLIENT_DFS_REFERRAL *referrals = NULL;
489 memset(param, 0, sizeof(param));
490 SSVAL(param, 0, 0x03); /* max referral level */
491 p = &param[2];
493 p += clistr_push(cli, p, path, MIN(pathlen, sizeof(param)-2), STR_TERMINATE);
494 param_len = PTR_DIFF(p, param);
496 if (!cli_send_trans(cli, SMBtrans2,
497 NULL, /* name */
498 -1, 0, /* fid, flags */
499 &setup, 1, 0, /* setup, length, max */
500 param, param_len, 2, /* param, length, max */
501 (char *)&data, data_len, cli->max_xmit /* data, length, max */
502 )) {
503 return False;
506 if (!cli_receive_trans(cli, SMBtrans2,
507 &rparam, &param_len,
508 &rdata, &data_len)) {
509 return False;
512 *consumed = SVAL( rdata, 0 );
513 num_referrals = SVAL( rdata, 2 );
515 if ( num_referrals != 0 ) {
516 uint16 ref_version;
517 uint16 ref_size;
518 int i;
519 uint16 node_offset;
522 referrals = SMB_XMALLOC_ARRAY( CLIENT_DFS_REFERRAL, num_referrals );
524 /* start at the referrals array */
526 p = rdata+8;
527 for ( i=0; i<num_referrals; i++ ) {
528 ref_version = SVAL( p, 0 );
529 ref_size = SVAL( p, 2 );
530 node_offset = SVAL( p, 16 );
532 if ( ref_version != 3 ) {
533 p += ref_size;
534 continue;
537 referrals[i].proximity = SVAL( p, 8 );
538 referrals[i].ttl = SVAL( p, 10 );
540 clistr_pull( cli, referrals[i].dfspath, p+node_offset,
541 sizeof(referrals[i].dfspath), -1, STR_TERMINATE|STR_UNICODE );
543 p += ref_size;
548 *num_refs = num_referrals;
549 *refs = referrals;
551 SAFE_FREE(rdata);
552 SAFE_FREE(rparam);
554 return True;
557 /********************************************************************
558 ********************************************************************/
560 BOOL cli_resolve_path( const char *mountpt, struct cli_state *rootcli, const char *path,
561 struct cli_state **targetcli, pstring targetpath )
563 CLIENT_DFS_REFERRAL *refs = NULL;
564 size_t num_refs;
565 uint16 consumed;
566 struct cli_state *cli_ipc;
567 pstring fullpath, cleanpath;
568 int pathlen;
569 fstring server, share;
570 struct cli_state *newcli;
571 pstring newpath;
572 pstring newmount;
573 char *ppath;
575 SMB_STRUCT_STAT sbuf;
576 uint32 attributes;
578 *targetcli = NULL;
580 if ( !rootcli || !path || !targetcli )
581 return False;
583 /* send a trans2_query_path_info to check for a referral */
585 clean_path( cleanpath, path );
586 make_full_path( fullpath, rootcli->desthost, rootcli->share, cleanpath );
588 /* don't bother continuing if this is not a dfs root */
590 if ( !rootcli->dfsroot || cli_qpathinfo_basic( rootcli, cleanpath, &sbuf, &attributes ) ) {
591 *targetcli = rootcli;
592 pstrcpy( targetpath, path );
593 return True;
596 /* we got an error, check for DFS referral */
598 if ( !cli_dfs_check_error(rootcli) )
599 return False;
601 /* check for the referral */
603 if ( !(cli_ipc = cli_cm_open( rootcli->desthost, "IPC$", False )) )
604 return False;
606 if ( !cli_dfs_get_referral(cli_ipc, fullpath, &refs, &num_refs, &consumed)
607 || !num_refs )
609 return False;
612 /* just store the first referral for now
613 Make sure to recreate the original string including any wildcards */
615 make_full_path( fullpath, rootcli->desthost, rootcli->share, path );
616 pathlen = strlen( fullpath )*2;
617 consumed = MIN(pathlen, consumed );
618 pstrcpy( targetpath, &fullpath[consumed/2] );
620 split_dfs_path( refs[0].dfspath, server, share );
621 SAFE_FREE( refs );
623 /* open the connection to the target path */
625 if ( (*targetcli = cli_cm_open(server, share, False)) == NULL ) {
626 d_printf("Unable to follow dfs referral [//%s/%s]\n",
627 server, share );
629 return False;
632 /* parse out the consumed mount path */
633 /* trim off the \server\share\ */
635 fullpath[consumed/2] = '\0';
636 dos_clean_name( fullpath );
637 ppath = strchr_m( fullpath, '\\' );
638 ppath = strchr_m( ppath+1, '\\' );
639 ppath = strchr_m( ppath+1, '\\' );
640 ppath++;
642 pstr_sprintf( newmount, "%s\\%s", mountpt, ppath );
643 cli_cm_set_mntpoint( *targetcli, newmount );
645 /* check for another dfs referral, note that we are not
646 checking for loops here */
648 if ( !strequal( targetpath, "\\" ) ) {
649 if ( cli_resolve_path( newmount, *targetcli, targetpath, &newcli, newpath ) ) {
650 *targetcli = newcli;
651 pstrcpy( targetpath, newpath );
655 return True;