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#define NO_SYSLOG
23
24#include "includes.h"
25
26
27struct client_connection {
28	struct client_connection *prev, *next;
29	struct cli_state *cli;
30	pstring mount;
31};
32
33/* global state....globals reek! */
34
35static pstring username;
36static pstring password;
37static BOOL use_kerberos;
38static BOOL got_pass;
39static int signing_state;
40int max_protocol = PROTOCOL_NT1;
41
42static int port;
43static int name_type = 0x20;
44static BOOL have_ip;
45static struct in_addr dest_ip;
46
47static struct client_connection *connections;
48
49/********************************************************************
50 Return a connection to a server.
51********************************************************************/
52
53static struct cli_state *do_connect( const char *server, const char *share,
54                                     BOOL show_sessetup )
55{
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;
62
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++;
72	}
73
74	server_n = server;
75
76	zero_ip(&ip);
77
78	make_nmb_name(&calling, global_myname(), 0x0);
79	make_nmb_name(&called , server, name_type);
80
81 again:
82	zero_ip(&ip);
83	if (have_ip)
84		ip = dest_ip;
85
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;
91	}
92
93	c->protocol = max_protocol;
94	c->use_kerberos = use_kerberos;
95	cli_setup_signing_state(c, signing_state);
96
97
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;
106		}
107		if (strcmp(called.name, "*SMBSERVER")) {
108			make_nmb_name(&called , "*SMBSERVER", 0x20);
109			goto again;
110		}
111		return NULL;
112	}
113
114	DEBUG(4,(" session request ok\n"));
115
116	if (!cli_negprot(c)) {
117		d_printf("protocol negotiation failed\n");
118		cli_shutdown(c);
119		return NULL;
120	}
121
122	if (!got_pass) {
123		char *pass = getpass("Password: ");
124		if (pass) {
125			pstrcpy(password, pass);
126			got_pass = 1;
127		}
128	}
129
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;
143		}
144		d_printf("Anonymous login successful\n");
145	}
146
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));
154		}
155	}
156	DEBUG(4,(" session setup ok\n"));
157
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;
163	}
164
165	DEBUG(4,(" tconx ok\n"));
166
167	return c;
168}
169
170/****************************************************************************
171****************************************************************************/
172
173static void cli_cm_set_mntpoint( struct cli_state *c, const char *mnt )
174{
175	struct client_connection *p;
176	int i;
177
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;
181	}
182
183	if ( p ) {
184		pstrcpy( p->mount, mnt );
185		dos_clean_name( p->mount );
186	}
187}
188
189/****************************************************************************
190****************************************************************************/
191
192const char * cli_cm_get_mntpoint( struct cli_state *c )
193{
194	struct client_connection *p;
195	int i;
196
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;
200	}
201
202	if ( p )
203		return p->mount;
204
205	return NULL;
206}
207
208/********************************************************************
209 Add a new connection to the list
210********************************************************************/
211
212static struct cli_state* cli_cm_connect( const char *server, const char *share,
213                                         BOOL show_hdr )
214{
215	struct client_connection *node;
216
217	node = SMB_XMALLOC_P( struct client_connection );
218
219	node->cli = do_connect( server, share, show_hdr );
220
221	if ( !node->cli ) {
222		SAFE_FREE( node );
223		return NULL;
224	}
225
226	DLIST_ADD( connections, node );
227
228	cli_cm_set_mntpoint( node->cli, "" );
229
230	return node->cli;
231
232}
233
234/********************************************************************
235 Return a connection to a server.
236********************************************************************/
237
238static struct cli_state* cli_cm_find( const char *server, const char *share )
239{
240	struct client_connection *p;
241
242	for ( p=connections; p; p=p->next ) {
243		if ( strequal(server, p->cli->desthost) && strequal(share,p->cli->share) )
244			return p->cli;
245	}
246
247	return NULL;
248}
249
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****************************************************************************/
254
255struct cli_state* cli_cm_open( const char *server, const char *share, BOOL show_hdr )
256{
257	struct cli_state *c;
258
259	/* try to reuse an existing connection */
260
261	c = cli_cm_find( server, share );
262
263	if ( !c )
264		c = cli_cm_connect( server, share, show_hdr );
265
266	return c;
267}
268
269/****************************************************************************
270****************************************************************************/
271
272void cli_cm_shutdown( void )
273{
274
275	struct client_connection *p, *x;
276
277	for ( p=connections; p; ) {
278		cli_shutdown( p->cli );
279		x = p;
280		p = p->next;
281
282		SAFE_FREE( x );
283	}
284
285	connections = NULL;
286
287	return;
288}
289
290/****************************************************************************
291****************************************************************************/
292
293void cli_cm_display(void)
294{
295	struct client_connection *p;
296	int i;
297
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 );
301	}
302}
303
304/****************************************************************************
305****************************************************************************/
306
307void cli_cm_set_credentials( struct user_auth_info *user )
308{
309	pstrcpy( username, user->username );
310
311	if ( user->got_pass ) {
312		pstrcpy( password, user->password );
313		got_pass = True;
314	}
315
316	use_kerberos = user->use_kerberos;
317	signing_state = user->signing_state;
318}
319
320/****************************************************************************
321****************************************************************************/
322
323void cli_cm_set_port( int port_number )
324{
325	port = port_number;
326}
327
328/****************************************************************************
329****************************************************************************/
330
331void cli_cm_set_dest_name_type( int type )
332{
333	name_type = type;
334}
335
336/****************************************************************************
337****************************************************************************/
338
339void cli_cm_set_dest_ip(struct in_addr ip )
340{
341	dest_ip = ip;
342	have_ip = True;
343}
344
345/********************************************************************
346 split a dfs path into the server and share name components
347********************************************************************/
348
349static void split_dfs_path( const char *nodepath, fstring server, fstring share )
350{
351	char *p;
352	pstring path;
353
354	pstrcpy( path, nodepath );
355
356	if ( path[0] != '\\' )
357		return;
358
359	p = strrchr_m( path, '\\' );
360
361	if ( !p )
362		return;
363
364	*p = '\0';
365	p++;
366
367	fstrcpy( share, p );
368	fstrcpy( server, &path[1] );
369}
370
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****************************************************************************/
376
377static void clean_path( pstring clean, const char *path )
378{
379	int len;
380	char *p;
381	pstring newpath;
382
383	pstrcpy( newpath, path );
384	p = newpath;
385
386	while ( p ) {
387		/* first check for '*' */
388
389		p = strrchr_m( newpath, '*' );
390		if ( p ) {
391			*p = '\0';
392			p = newpath;
393			continue;
394		}
395
396		/* first check for '?' */
397
398		p = strrchr_m( newpath, '?' );
399		if ( p ) {
400			*p = '\0';
401			p = newpath;
402		}
403	}
404
405	/* strip a trailing backslash */
406
407	len = strlen( newpath );
408	if ( newpath[len-1] == '\\' )
409		newpath[len-1] = '\0';
410
411	pstrcpy( clean, newpath );
412}
413
414/****************************************************************************
415****************************************************************************/
416
417static BOOL make_full_path( pstring path, const char *server, const char *share,
418                            const char *dir )
419{
420	pstring servicename;
421	char *sharename;
422	const char *directory;
423
424
425	/* make a copy so we don't modify the global string 'service' */
426
427	pstrcpy(servicename, share);
428	sharename = servicename;
429
430	if (*sharename == '\\') {
431
432		server = sharename+2;
433		sharename = strchr_m(server,'\\');
434
435		if (!sharename)
436			return False;
437
438		*sharename = 0;
439		sharename++;
440	}
441
442	directory = dir;
443	if ( *directory == '\\' )
444		directory++;
445
446	pstr_sprintf( path, "\\%s\\%s\\%s", server, sharename, directory );
447
448	return True;
449}
450
451/********************************************************************
452 check for dfs referral
453********************************************************************/
454
455static BOOL cli_dfs_check_error( struct cli_state *cli )
456{
457	uint32 flgs2 = SVAL(cli->inbuf,smb_flg2);
458
459	/* only deal with DS when we negotiated NT_STATUS codes and UNICODE */
460
461	if ( !( (flgs2&FLAGS2_32_BIT_ERROR_CODES) && (flgs2&FLAGS2_UNICODE_STRINGS) ) )
462		return False;
463
464	if ( NT_STATUS_EQUAL( NT_STATUS_PATH_NOT_COVERED, NT_STATUS(IVAL(cli->inbuf,smb_rcls)) ) )
465		return True;
466
467	return False;
468}
469
470/********************************************************************
471 get the dfs referral link
472********************************************************************/
473
474BOOL cli_dfs_get_referral( struct cli_state *cli, const char *path,
475                           CLIENT_DFS_REFERRAL**refs, size_t *num_refs,
476			   uint16 *consumed)
477{
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;
488
489	memset(param, 0, sizeof(param));
490	SSVAL(param, 0, 0x03);	/* max referral level */
491	p = &param[2];
492
493	p += clistr_push(cli, p, path, MIN(pathlen, sizeof(param)-2), STR_TERMINATE);
494	param_len = PTR_DIFF(p, param);
495
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;
504	}
505
506	if (!cli_receive_trans(cli, SMBtrans2,
507		&rparam, &param_len,
508		&rdata, &data_len)) {
509			return False;
510	}
511
512	*consumed     = SVAL( rdata, 0 );
513	num_referrals = SVAL( rdata, 2 );
514
515	if ( num_referrals != 0 ) {
516		uint16 ref_version;
517		uint16 ref_size;
518		int i;
519		uint16 node_offset;
520
521
522		referrals = SMB_XMALLOC_ARRAY( CLIENT_DFS_REFERRAL, num_referrals );
523
524		/* start at the referrals array */
525
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 );
531
532			if ( ref_version != 3 ) {
533				p += ref_size;
534				continue;
535			}
536
537			referrals[i].proximity = SVAL( p, 8 );
538			referrals[i].ttl       = SVAL( p, 10 );
539
540			clistr_pull( cli, referrals[i].dfspath, p+node_offset,
541				sizeof(referrals[i].dfspath), -1, STR_TERMINATE|STR_UNICODE );
542
543			p += ref_size;
544		}
545
546	}
547
548	*num_refs = num_referrals;
549	*refs = referrals;
550
551	SAFE_FREE(rdata);
552	SAFE_FREE(rparam);
553
554	return True;
555}
556
557/********************************************************************
558********************************************************************/
559
560BOOL cli_resolve_path( const char *mountpt, struct cli_state *rootcli, const char *path,
561                       struct cli_state **targetcli, pstring targetpath )
562{
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;
574
575	SMB_STRUCT_STAT sbuf;
576	uint32 attributes;
577
578	*targetcli = NULL;
579
580	if ( !rootcli || !path || !targetcli )
581		return False;
582
583	/* send a trans2_query_path_info to check for a referral */
584
585	clean_path( cleanpath, 	path );
586	make_full_path( fullpath, rootcli->desthost, rootcli->share, cleanpath );
587
588	/* don't bother continuing if this is not a dfs root */
589
590	if ( !rootcli->dfsroot || cli_qpathinfo_basic( rootcli, cleanpath, &sbuf, &attributes ) ) {
591		*targetcli = rootcli;
592		pstrcpy( targetpath, path );
593		return True;
594	}
595
596	/* we got an error, check for DFS referral */
597
598	if ( !cli_dfs_check_error(rootcli) )
599		return False;
600
601	/* check for the referral */
602
603	if ( !(cli_ipc = cli_cm_open( rootcli->desthost, "IPC$", False )) )
604		return False;
605
606	if ( !cli_dfs_get_referral(cli_ipc, fullpath, &refs, &num_refs, &consumed)
607		|| !num_refs )
608	{
609		return False;
610	}
611
612	/* just store the first referral for now
613	   Make sure to recreate the original string including any wildcards */
614
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] );
619
620	split_dfs_path( refs[0].dfspath, server, share );
621	SAFE_FREE( refs );
622
623	/* open the connection to the target path */
624
625	if ( (*targetcli = cli_cm_open(server, share, False)) == NULL ) {
626		d_printf("Unable to follow dfs referral [//%s/%s]\n",
627			server, share );
628
629		return False;
630	}
631
632	/* parse out the consumed mount path */
633	/* trim off the \server\share\ */
634
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++;
641
642	pstr_sprintf( newmount, "%s\\%s", mountpt, ppath );
643	cli_cm_set_mntpoint( *targetcli, newmount );
644
645	/* check for another dfs referral, note that we are not
646	   checking for loops here */
647
648	if ( !strequal( targetpath, "\\" ) ) {
649		if ( cli_resolve_path( newmount, *targetcli, targetpath, &newcli, newpath ) ) {
650			*targetcli = newcli;
651			pstrcpy( targetpath, newpath );
652		}
653	}
654
655	return True;
656}
657