1/*
2   Unix SMB/Netbios implementation.
3   Version 3.0
4   MSDFS services for Samba
5   Copyright (C) Shirish Kalele 2000
6   Copyright (C) Jeremy Allison 2007
7
8   This program is free software; you can redistribute it and/or modify
9   it under the terms of the GNU General Public License as published by
10   the Free Software Foundation; either version 3 of the License, or
11   (at your option) any later version.
12
13   This program is distributed in the hope that it will be useful,
14   but WITHOUT ANY WARRANTY; without even the implied warranty of
15   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16   GNU General Public License for more details.
17
18   You should have received a copy of the GNU General Public License
19   along with this program.  If not, see <http://www.gnu.org/licenses/>.
20
21*/
22
23#define DBGC_CLASS DBGC_MSDFS
24#include "includes.h"
25#include "smbd/globals.h"
26
27/**********************************************************************
28 Parse a DFS pathname of the form \hostname\service\reqpath
29 into the dfs_path structure.
30 If POSIX pathnames is true, the pathname may also be of the
31 form /hostname/service/reqpath.
32 We cope with either here.
33
34 Unfortunately, due to broken clients who might set the
35 SVAL(inbuf,smb_flg2) & FLAGS2_DFS_PATHNAMES bit and then
36 send a local path, we have to cope with that too....
37
38 If conn != NULL then ensure the provided service is
39 the one pointed to by the connection.
40
41 This version does everything using pointers within one copy of the
42 pathname string, talloced on the struct dfs_path pointer (which
43 must be talloced). This may be too clever to live....
44 JRA.
45**********************************************************************/
46
47static NTSTATUS parse_dfs_path(connection_struct *conn,
48				const char *pathname,
49				bool allow_wcards,
50				struct dfs_path *pdp, /* MUST BE TALLOCED */
51				bool *ppath_contains_wcard)
52{
53	char *pathname_local;
54	char *p,*temp;
55	char *servicename;
56	char *eos_ptr;
57	NTSTATUS status = NT_STATUS_OK;
58	char sepchar;
59
60	ZERO_STRUCTP(pdp);
61
62	/*
63	 * This is the only talloc we should need to do
64	 * on the struct dfs_path. All the pointers inside
65	 * it should point to offsets within this string.
66	 */
67
68	pathname_local = talloc_strdup(pdp, pathname);
69	if (!pathname_local) {
70		return NT_STATUS_NO_MEMORY;
71	}
72	/* Get a pointer to the terminating '\0' */
73	eos_ptr = &pathname_local[strlen(pathname_local)];
74	p = temp = pathname_local;
75
76	pdp->posix_path = (lp_posix_pathnames() && *pathname == '/');
77
78	sepchar = pdp->posix_path ? '/' : '\\';
79
80	if (*pathname != sepchar) {
81		DEBUG(10,("parse_dfs_path: path %s doesn't start with %c\n",
82			pathname, sepchar ));
83		/*
84		 * Possibly client sent a local path by mistake.
85		 * Try and convert to a local path.
86		 */
87
88		pdp->hostname = eos_ptr; /* "" */
89		pdp->servicename = eos_ptr; /* "" */
90
91		/* We've got no info about separators. */
92		pdp->posix_path = lp_posix_pathnames();
93		p = temp;
94		DEBUG(10,("parse_dfs_path: trying to convert %s to a "
95			"local path\n",
96			temp));
97		goto local_path;
98	}
99
100	/*
101	 * Safe to use on talloc'ed string as it only shrinks.
102	 * It also doesn't affect the eos_ptr.
103	 */
104	trim_char(temp,sepchar,sepchar);
105
106	DEBUG(10,("parse_dfs_path: temp = |%s| after trimming %c's\n",
107		temp, sepchar));
108
109	/* Now tokenize. */
110	/* Parse out hostname. */
111	p = strchr_m(temp,sepchar);
112	if(p == NULL) {
113		DEBUG(10,("parse_dfs_path: can't parse hostname from path %s\n",
114			temp));
115		/*
116		 * Possibly client sent a local path by mistake.
117		 * Try and convert to a local path.
118		 */
119
120		pdp->hostname = eos_ptr; /* "" */
121		pdp->servicename = eos_ptr; /* "" */
122
123		p = temp;
124		DEBUG(10,("parse_dfs_path: trying to convert %s "
125			"to a local path\n",
126			temp));
127		goto local_path;
128	}
129	*p = '\0';
130	pdp->hostname = temp;
131
132	DEBUG(10,("parse_dfs_path: hostname: %s\n",pdp->hostname));
133
134	/* Parse out servicename. */
135	servicename = p+1;
136	p = strchr_m(servicename,sepchar);
137	if (p) {
138		*p = '\0';
139	}
140
141	/* Is this really our servicename ? */
142	if (conn && !( strequal(servicename, lp_servicename(SNUM(conn)))
143			|| (strequal(servicename, HOMES_NAME)
144			&& strequal(lp_servicename(SNUM(conn)),
145				get_current_username()) )) ) {
146		DEBUG(10,("parse_dfs_path: %s is not our servicename\n",
147			servicename));
148
149		/*
150		 * Possibly client sent a local path by mistake.
151		 * Try and convert to a local path.
152		 */
153
154		pdp->hostname = eos_ptr; /* "" */
155		pdp->servicename = eos_ptr; /* "" */
156
157		/* Repair the path - replace the sepchar's
158		   we nulled out */
159		servicename--;
160		*servicename = sepchar;
161		if (p) {
162			*p = sepchar;
163		}
164
165		p = temp;
166		DEBUG(10,("parse_dfs_path: trying to convert %s "
167			"to a local path\n",
168			temp));
169		goto local_path;
170	}
171
172	pdp->servicename = servicename;
173
174	DEBUG(10,("parse_dfs_path: servicename: %s\n",pdp->servicename));
175
176	if(p == NULL) {
177		/* Client sent self referral \server\share. */
178		pdp->reqpath = eos_ptr; /* "" */
179		return NT_STATUS_OK;
180	}
181
182	p++;
183
184  local_path:
185
186	*ppath_contains_wcard = False;
187
188	pdp->reqpath = p;
189
190	/* Rest is reqpath. */
191	if (pdp->posix_path) {
192		status = check_path_syntax_posix(pdp->reqpath);
193	} else {
194		if (allow_wcards) {
195			status = check_path_syntax_wcard(pdp->reqpath,
196					ppath_contains_wcard);
197		} else {
198			status = check_path_syntax(pdp->reqpath);
199		}
200	}
201
202	if (!NT_STATUS_IS_OK(status)) {
203		DEBUG(10,("parse_dfs_path: '%s' failed with %s\n",
204			p, nt_errstr(status) ));
205		return status;
206	}
207
208	DEBUG(10,("parse_dfs_path: rest of the path: %s\n",pdp->reqpath));
209	return NT_STATUS_OK;
210}
211
212/********************************************************
213 Fake up a connection struct for the VFS layer.
214 Note this CHANGES CWD !!!! JRA.
215*********************************************************/
216
217NTSTATUS create_conn_struct(TALLOC_CTX *ctx,
218				connection_struct **pconn,
219				int snum,
220				const char *path,
221				struct auth_serversupplied_info *server_info,
222				char **poldcwd)
223{
224	connection_struct *conn;
225	char *connpath;
226	char *oldcwd;
227
228	conn = TALLOC_ZERO_P(ctx, connection_struct);
229	if (conn == NULL) {
230		return NT_STATUS_NO_MEMORY;
231	}
232
233	connpath = talloc_strdup(conn, path);
234	if (!connpath) {
235		TALLOC_FREE(conn);
236		return NT_STATUS_NO_MEMORY;
237	}
238	connpath = talloc_string_sub(conn,
239				connpath,
240				"%S",
241				lp_servicename(snum));
242	if (!connpath) {
243		TALLOC_FREE(conn);
244		return NT_STATUS_NO_MEMORY;
245	}
246
247	/* needed for smbd_vfs_init() */
248
249	if (!(conn->params = TALLOC_ZERO_P(conn, struct share_params))) {
250		DEBUG(0, ("TALLOC failed\n"));
251		TALLOC_FREE(conn);
252		return NT_STATUS_NO_MEMORY;
253	}
254
255	conn->params->service = snum;
256
257	if (server_info != NULL) {
258		conn->server_info = copy_serverinfo(conn, server_info);
259		if (conn->server_info == NULL) {
260			DEBUG(0, ("copy_serverinfo failed\n"));
261			TALLOC_FREE(conn);
262			return NT_STATUS_NO_MEMORY;
263		}
264	}
265
266	set_conn_connectpath(conn, connpath);
267
268	if (!smbd_vfs_init(conn)) {
269		NTSTATUS status = map_nt_error_from_unix(errno);
270		DEBUG(0,("create_conn_struct: smbd_vfs_init failed.\n"));
271		conn_free(conn);
272		return status;
273	}
274
275	conn->fs_capabilities = SMB_VFS_FS_CAPABILITIES(conn, &conn->ts_res);
276
277	/*
278	 * Windows seems to insist on doing trans2getdfsreferral() calls on
279	 * the IPC$ share as the anonymous user. If we try to chdir as that
280	 * user we will fail.... WTF ? JRA.
281	 */
282
283	oldcwd = vfs_GetWd(ctx, conn);
284	if (oldcwd == NULL) {
285		NTSTATUS status = map_nt_error_from_unix(errno);
286		DEBUG(3, ("vfs_GetWd failed: %s\n", strerror(errno)));
287		conn_free(conn);
288		return status;
289	}
290
291	if (vfs_ChDir(conn,conn->connectpath) != 0) {
292		NTSTATUS status = map_nt_error_from_unix(errno);
293		DEBUG(3,("create_conn_struct: Can't ChDir to new conn path %s. "
294			"Error was %s\n",
295			conn->connectpath, strerror(errno) ));
296		conn_free(conn);
297		return status;
298	}
299
300	*pconn = conn;
301	*poldcwd = oldcwd;
302
303	return NT_STATUS_OK;
304}
305
306/**********************************************************************
307 Parse the contents of a symlink to verify if it is an msdfs referral
308 A valid referral is of the form:
309
310 msdfs:server1\share1,server2\share2
311 msdfs:server1\share1\pathname,server2\share2\pathname
312 msdfs:server1/share1,server2/share2
313 msdfs:server1/share1/pathname,server2/share2/pathname.
314
315 Note that the alternate paths returned here must be of the canonicalized
316 form:
317
318 \server\share or
319 \server\share\path\to\file,
320
321 even in posix path mode. This is because we have no knowledge if the
322 server we're referring to understands posix paths.
323 **********************************************************************/
324
325static bool parse_msdfs_symlink(TALLOC_CTX *ctx,
326				const char *target,
327				struct referral **preflist,
328				int *refcount)
329{
330	char *temp = NULL;
331	char *prot;
332	char **alt_path = NULL;
333	int count = 0, i;
334	struct referral *reflist;
335	char *saveptr;
336
337	temp = talloc_strdup(ctx, target);
338	if (!temp) {
339		return False;
340	}
341	prot = strtok_r(temp, ":", &saveptr);
342	if (!prot) {
343		DEBUG(0,("parse_msdfs_symlink: invalid path !\n"));
344		return False;
345	}
346
347	alt_path = TALLOC_ARRAY(ctx, char *, MAX_REFERRAL_COUNT);
348	if (!alt_path) {
349		return False;
350	}
351
352	/* parse out the alternate paths */
353	while((count<MAX_REFERRAL_COUNT) &&
354	      ((alt_path[count] = strtok_r(NULL, ",", &saveptr)) != NULL)) {
355		count++;
356	}
357
358	DEBUG(10,("parse_msdfs_symlink: count=%d\n", count));
359
360	if (count) {
361		reflist = *preflist = TALLOC_ZERO_ARRAY(ctx,
362				struct referral, count);
363		if(reflist == NULL) {
364			TALLOC_FREE(alt_path);
365			return False;
366		}
367	} else {
368		reflist = *preflist = NULL;
369	}
370
371	for(i=0;i<count;i++) {
372		char *p;
373
374		/* Canonicalize link target.
375		 * Replace all /'s in the path by a \ */
376		string_replace(alt_path[i], '/', '\\');
377
378		/* Remove leading '\\'s */
379		p = alt_path[i];
380		while (*p && (*p == '\\')) {
381			p++;
382		}
383
384		reflist[i].alternate_path = talloc_asprintf(ctx,
385				"\\%s",
386				p);
387		if (!reflist[i].alternate_path) {
388			return False;
389		}
390
391		reflist[i].proximity = 0;
392		reflist[i].ttl = REFERRAL_TTL;
393		DEBUG(10, ("parse_msdfs_symlink: Created alt path: %s\n",
394					reflist[i].alternate_path));
395	}
396
397	*refcount = count;
398
399	TALLOC_FREE(alt_path);
400	return True;
401}
402
403/**********************************************************************
404 Returns true if the unix path is a valid msdfs symlink and also
405 returns the target string from inside the link.
406**********************************************************************/
407
408static bool is_msdfs_link_internal(TALLOC_CTX *ctx,
409			connection_struct *conn,
410			const char *path,
411			char **pp_link_target,
412			SMB_STRUCT_STAT *sbufp)
413{
414	int referral_len = 0;
415#if defined(HAVE_BROKEN_READLINK)
416	char link_target_buf[PATH_MAX];
417#else
418	char link_target_buf[7];
419#endif
420	size_t bufsize = 0;
421	char *link_target = NULL;
422	struct smb_filename smb_fname;
423
424	if (pp_link_target) {
425		bufsize = 1024;
426		link_target = TALLOC_ARRAY(ctx, char, bufsize);
427		if (!link_target) {
428			return False;
429		}
430		*pp_link_target = link_target;
431	} else {
432		bufsize = sizeof(link_target_buf);
433		link_target = link_target_buf;
434	}
435
436	ZERO_STRUCT(smb_fname);
437	smb_fname.base_name = discard_const_p(char, path);
438
439	if (SMB_VFS_LSTAT(conn, &smb_fname) != 0) {
440		DEBUG(5,("is_msdfs_link_read_target: %s does not exist.\n",
441			path));
442		goto err;
443	}
444	if (!S_ISLNK(smb_fname.st.st_ex_mode)) {
445		DEBUG(5,("is_msdfs_link_read_target: %s is not a link.\n",
446					path));
447		goto err;
448	}
449	if (sbufp != NULL) {
450		*sbufp = smb_fname.st;
451	}
452
453	referral_len = SMB_VFS_READLINK(conn, path, link_target, bufsize - 1);
454	if (referral_len == -1) {
455		DEBUG(0,("is_msdfs_link_read_target: Error reading "
456			"msdfs link %s: %s\n",
457			path, strerror(errno)));
458		goto err;
459	}
460	link_target[referral_len] = '\0';
461
462	DEBUG(5,("is_msdfs_link_internal: %s -> %s\n",path,
463				link_target));
464
465	if (!strnequal(link_target, "msdfs:", 6)) {
466		goto err;
467	}
468	return True;
469
470  err:
471
472	if (link_target != link_target_buf) {
473		TALLOC_FREE(link_target);
474	}
475	return False;
476}
477
478/**********************************************************************
479 Returns true if the unix path is a valid msdfs symlink.
480**********************************************************************/
481
482bool is_msdfs_link(connection_struct *conn,
483		const char *path,
484		SMB_STRUCT_STAT *sbufp)
485{
486	return is_msdfs_link_internal(talloc_tos(),
487					conn,
488					path,
489					NULL,
490					sbufp);
491}
492
493/*****************************************************************
494 Used by other functions to decide if a dfs path is remote,
495 and to get the list of referred locations for that remote path.
496
497 search_flag: For findfirsts, dfs links themselves are not
498 redirected, but paths beyond the links are. For normal smb calls,
499 even dfs links need to be redirected.
500
501 consumedcntp: how much of the dfs path is being redirected. the client
502 should try the remaining path on the redirected server.
503
504 If this returns NT_STATUS_PATH_NOT_COVERED the contents of the msdfs
505 link redirect are in targetpath.
506*****************************************************************/
507
508static NTSTATUS dfs_path_lookup(TALLOC_CTX *ctx,
509		connection_struct *conn,
510		const char *dfspath, /* Incoming complete dfs path */
511		const struct dfs_path *pdp, /* Parsed out
512					       server+share+extrapath. */
513		bool search_flag, /* Called from a findfirst ? */
514		int *consumedcntp,
515		char **pp_targetpath)
516{
517	char *p = NULL;
518	char *q = NULL;
519	NTSTATUS status;
520	struct smb_filename *smb_fname = NULL;
521	char *canon_dfspath = NULL; /* Canonicalized dfs path. (only '/'
522				  components). */
523
524	DEBUG(10,("dfs_path_lookup: Conn path = %s reqpath = %s\n",
525		conn->connectpath, pdp->reqpath));
526
527	/*
528 	 * Note the unix path conversion here we're doing we can
529	 * throw away. We're looking for a symlink for a dfs
530	 * resolution, if we don't find it we'll do another
531	 * unix_convert later in the codepath.
532	 * If we needed to remember what we'd resolved in
533	 * dp->reqpath (as the original code did) we'd
534	 * copy (localhost, dp->reqpath) on any code
535	 * path below that returns True - but I don't
536	 * think this is needed. JRA.
537	 */
538
539	status = unix_convert(ctx, conn, pdp->reqpath, &smb_fname,
540			      search_flag ? UCF_ALWAYS_ALLOW_WCARD_LCOMP : 0);
541
542	if (!NT_STATUS_IS_OK(status)) {
543		if (!NT_STATUS_EQUAL(status,
544				     NT_STATUS_OBJECT_PATH_NOT_FOUND)) {
545			return status;
546		}
547
548		/* Create an smb_fname to use below. */
549		status = create_synthetic_smb_fname(ctx, pdp->reqpath, NULL,
550						    NULL, &smb_fname);
551		if (!NT_STATUS_IS_OK(status)) {
552			return status;
553		}
554	}
555
556	/* Optimization - check if we can redirect the whole path. */
557
558	if (is_msdfs_link_internal(ctx, conn, smb_fname->base_name,
559				   pp_targetpath, NULL)) {
560		if (search_flag) {
561			DEBUG(6,("dfs_path_lookup (FindFirst) No redirection "
562				 "for dfs link %s.\n", dfspath));
563			status = NT_STATUS_OK;
564			goto out;
565		}
566
567		DEBUG(6,("dfs_path_lookup: %s resolves to a "
568			"valid dfs link %s.\n", dfspath,
569			pp_targetpath ? *pp_targetpath : ""));
570
571		if (consumedcntp) {
572			*consumedcntp = strlen(dfspath);
573		}
574		status = NT_STATUS_PATH_NOT_COVERED;
575		goto out;
576	}
577
578	/* Prepare to test only for '/' components in the given path,
579	 * so if a Windows path replace all '\\' characters with '/'.
580	 * For a POSIX DFS path we know all separators are already '/'. */
581
582	canon_dfspath = talloc_strdup(ctx, dfspath);
583	if (!canon_dfspath) {
584		status = NT_STATUS_NO_MEMORY;
585		goto out;
586	}
587	if (!pdp->posix_path) {
588		string_replace(canon_dfspath, '\\', '/');
589	}
590
591	/*
592	 * localpath comes out of unix_convert, so it has
593	 * no trailing backslash. Make sure that canon_dfspath hasn't either.
594	 * Fix for bug #4860 from Jan Martin <Jan.Martin@rwedea.com>.
595	 */
596
597	trim_char(canon_dfspath,0,'/');
598
599	/*
600	 * Redirect if any component in the path is a link.
601	 * We do this by walking backwards through the
602	 * local path, chopping off the last component
603	 * in both the local path and the canonicalized
604	 * DFS path. If we hit a DFS link then we're done.
605	 */
606
607	p = strrchr_m(smb_fname->base_name, '/');
608	if (consumedcntp) {
609		q = strrchr_m(canon_dfspath, '/');
610	}
611
612	while (p) {
613		*p = '\0';
614		if (q) {
615			*q = '\0';
616		}
617
618		if (is_msdfs_link_internal(ctx, conn,
619					   smb_fname->base_name, pp_targetpath,
620					   NULL)) {
621			DEBUG(4, ("dfs_path_lookup: Redirecting %s because "
622				  "parent %s is dfs link\n", dfspath,
623				  smb_fname_str_dbg(smb_fname)));
624
625			if (consumedcntp) {
626				*consumedcntp = strlen(canon_dfspath);
627				DEBUG(10, ("dfs_path_lookup: Path consumed: %s "
628					"(%d)\n",
629					canon_dfspath,
630					*consumedcntp));
631			}
632
633			status = NT_STATUS_PATH_NOT_COVERED;
634			goto out;
635		}
636
637		/* Step back on the filesystem. */
638		p = strrchr_m(smb_fname->base_name, '/');
639
640		if (consumedcntp) {
641			/* And in the canonicalized dfs path. */
642			q = strrchr_m(canon_dfspath, '/');
643		}
644	}
645
646	status = NT_STATUS_OK;
647 out:
648	TALLOC_FREE(smb_fname);
649	return status;
650}
651
652/*****************************************************************
653 Decides if a dfs pathname should be redirected or not.
654 If not, the pathname is converted to a tcon-relative local unix path
655
656 search_wcard_flag: this flag performs 2 functions both related
657 to searches.  See resolve_dfs_path() and parse_dfs_path_XX()
658 for details.
659
660 This function can return NT_STATUS_OK, meaning use the returned path as-is
661 (mapped into a local path).
662 or NT_STATUS_NOT_COVERED meaning return a DFS redirect, or
663 any other NT_STATUS error which is a genuine error to be
664 returned to the client.
665*****************************************************************/
666
667static NTSTATUS dfs_redirect(TALLOC_CTX *ctx,
668			connection_struct *conn,
669			const char *path_in,
670			bool search_wcard_flag,
671			char **pp_path_out,
672			bool *ppath_contains_wcard)
673{
674	NTSTATUS status;
675	struct dfs_path *pdp = TALLOC_P(ctx, struct dfs_path);
676
677	if (!pdp) {
678		return NT_STATUS_NO_MEMORY;
679	}
680
681	status = parse_dfs_path(conn, path_in, search_wcard_flag, pdp,
682			ppath_contains_wcard);
683	if (!NT_STATUS_IS_OK(status)) {
684		TALLOC_FREE(pdp);
685		return status;
686	}
687
688	if (pdp->reqpath[0] == '\0') {
689		TALLOC_FREE(pdp);
690		*pp_path_out = talloc_strdup(ctx, "");
691		if (!*pp_path_out) {
692			return NT_STATUS_NO_MEMORY;
693		}
694		DEBUG(5,("dfs_redirect: self-referral.\n"));
695		return NT_STATUS_OK;
696	}
697
698	/* If dfs pathname for a non-dfs share, convert to tcon-relative
699	   path and return OK */
700
701	if (!lp_msdfs_root(SNUM(conn))) {
702		*pp_path_out = talloc_strdup(ctx, pdp->reqpath);
703		TALLOC_FREE(pdp);
704		if (!*pp_path_out) {
705			return NT_STATUS_NO_MEMORY;
706		}
707		return NT_STATUS_OK;
708	}
709
710	/* If it looked like a local path (zero hostname/servicename)
711	 * just treat as a tcon-relative path. */
712
713	if (pdp->hostname[0] == '\0' && pdp->servicename[0] == '\0') {
714		*pp_path_out = talloc_strdup(ctx, pdp->reqpath);
715		TALLOC_FREE(pdp);
716		if (!*pp_path_out) {
717			return NT_STATUS_NO_MEMORY;
718		}
719		return NT_STATUS_OK;
720	}
721
722	if (!( strequal(pdp->servicename, lp_servicename(SNUM(conn)))
723			|| (strequal(pdp->servicename, HOMES_NAME)
724			&& strequal(lp_servicename(SNUM(conn)),
725				conn->server_info->sanitized_username) )) ) {
726
727		/* The given sharename doesn't match this connection. */
728		TALLOC_FREE(pdp);
729
730		return NT_STATUS_OBJECT_PATH_NOT_FOUND;
731	}
732
733	status = dfs_path_lookup(ctx, conn, path_in, pdp,
734			search_wcard_flag, NULL, NULL);
735	if (!NT_STATUS_IS_OK(status)) {
736		if (NT_STATUS_EQUAL(status, NT_STATUS_PATH_NOT_COVERED)) {
737			DEBUG(3,("dfs_redirect: Redirecting %s\n", path_in));
738		} else {
739			DEBUG(10,("dfs_redirect: dfs_path_lookup "
740				"failed for %s with %s\n",
741				path_in, nt_errstr(status) ));
742		}
743		return status;
744	}
745
746	DEBUG(3,("dfs_redirect: Not redirecting %s.\n", path_in));
747
748	/* Form non-dfs tcon-relative path */
749	*pp_path_out = talloc_strdup(ctx, pdp->reqpath);
750	TALLOC_FREE(pdp);
751	if (!*pp_path_out) {
752		return NT_STATUS_NO_MEMORY;
753	}
754
755	DEBUG(3,("dfs_redirect: Path %s converted to non-dfs path %s\n",
756				path_in,
757				*pp_path_out));
758
759	return NT_STATUS_OK;
760}
761
762/**********************************************************************
763 Return a self referral.
764**********************************************************************/
765
766static NTSTATUS self_ref(TALLOC_CTX *ctx,
767			const char *dfs_path,
768			struct junction_map *jucn,
769			int *consumedcntp,
770			bool *self_referralp)
771{
772	struct referral *ref;
773
774	*self_referralp = True;
775
776	jucn->referral_count = 1;
777	if((ref = TALLOC_ZERO_P(ctx, struct referral)) == NULL) {
778		return NT_STATUS_NO_MEMORY;
779	}
780
781	ref->alternate_path = talloc_strdup(ctx, dfs_path);
782	if (!ref->alternate_path) {
783		return NT_STATUS_NO_MEMORY;
784	}
785	ref->proximity = 0;
786	ref->ttl = REFERRAL_TTL;
787	jucn->referral_list = ref;
788	*consumedcntp = strlen(dfs_path);
789	return NT_STATUS_OK;
790}
791
792/**********************************************************************
793 Gets valid referrals for a dfs path and fills up the
794 junction_map structure.
795**********************************************************************/
796
797NTSTATUS get_referred_path(TALLOC_CTX *ctx,
798			struct auth_serversupplied_info *server_info,
799			const char *dfs_path,
800			struct junction_map *jucn,
801			int *consumedcntp,
802			bool *self_referralp)
803{
804	struct connection_struct *conn;
805	char *targetpath = NULL;
806	int snum;
807	NTSTATUS status = NT_STATUS_NOT_FOUND;
808	bool dummy;
809	struct dfs_path *pdp = TALLOC_P(ctx, struct dfs_path);
810	char *oldpath;
811
812	if (!pdp) {
813		return NT_STATUS_NO_MEMORY;
814	}
815
816	*self_referralp = False;
817
818	status = parse_dfs_path(NULL, dfs_path, False, pdp, &dummy);
819	if (!NT_STATUS_IS_OK(status)) {
820		return status;
821	}
822
823	jucn->service_name = talloc_strdup(ctx, pdp->servicename);
824	jucn->volume_name = talloc_strdup(ctx, pdp->reqpath);
825	if (!jucn->service_name || !jucn->volume_name) {
826		TALLOC_FREE(pdp);
827		return NT_STATUS_NO_MEMORY;
828	}
829
830	/* Verify the share is a dfs root */
831	snum = lp_servicenumber(jucn->service_name);
832	if(snum < 0) {
833		fstring service_name;
834		fstrcpy(service_name, jucn->service_name);
835		if ((snum = find_service(service_name)) < 0) {
836			return NT_STATUS_NOT_FOUND;
837		}
838		TALLOC_FREE(jucn->service_name);
839		jucn->service_name = talloc_strdup(ctx, service_name);
840		if (!jucn->service_name) {
841			TALLOC_FREE(pdp);
842			return NT_STATUS_NO_MEMORY;
843		}
844	}
845
846	if (!lp_msdfs_root(snum) && (*lp_msdfs_proxy(snum) == '\0')) {
847		DEBUG(3,("get_referred_path: |%s| in dfs path %s is not "
848			"a dfs root.\n",
849			pdp->servicename, dfs_path));
850		TALLOC_FREE(pdp);
851		return NT_STATUS_NOT_FOUND;
852	}
853
854	/*
855	 * Self referrals are tested with a anonymous IPC connection and
856	 * a GET_DFS_REFERRAL call to \\server\share. (which means
857	 * dp.reqpath[0] points to an empty string). create_conn_struct cd's
858	 * into the directory and will fail if it cannot (as the anonymous
859	 * user). Cope with this.
860	 */
861
862	if (pdp->reqpath[0] == '\0') {
863		char *tmp;
864		struct referral *ref;
865
866		if (*lp_msdfs_proxy(snum) == '\0') {
867			TALLOC_FREE(pdp);
868			return self_ref(ctx,
869					dfs_path,
870					jucn,
871					consumedcntp,
872					self_referralp);
873		}
874
875		/*
876		 * It's an msdfs proxy share. Redirect to
877 		 * the configured target share.
878 		 */
879
880		jucn->referral_count = 1;
881		if ((ref = TALLOC_ZERO_P(ctx, struct referral)) == NULL) {
882			TALLOC_FREE(pdp);
883			return NT_STATUS_NO_MEMORY;
884		}
885
886		if (!(tmp = talloc_strdup(ctx, lp_msdfs_proxy(snum)))) {
887			TALLOC_FREE(pdp);
888			return NT_STATUS_NO_MEMORY;
889		}
890
891		trim_string(tmp, "\\", 0);
892
893		ref->alternate_path = talloc_asprintf(ctx, "\\%s", tmp);
894		TALLOC_FREE(tmp);
895
896		if (!ref->alternate_path) {
897			TALLOC_FREE(pdp);
898			return NT_STATUS_NO_MEMORY;
899		}
900
901		if (pdp->reqpath[0] != '\0') {
902			ref->alternate_path = talloc_asprintf_append(
903					ref->alternate_path,
904					"%s",
905					pdp->reqpath);
906			if (!ref->alternate_path) {
907				TALLOC_FREE(pdp);
908				return NT_STATUS_NO_MEMORY;
909			}
910		}
911		ref->proximity = 0;
912		ref->ttl = REFERRAL_TTL;
913		jucn->referral_list = ref;
914		*consumedcntp = strlen(dfs_path);
915		TALLOC_FREE(pdp);
916		return NT_STATUS_OK;
917	}
918
919	status = create_conn_struct(ctx, &conn, snum, lp_pathname(snum),
920				    server_info, &oldpath);
921	if (!NT_STATUS_IS_OK(status)) {
922		TALLOC_FREE(pdp);
923		return status;
924	}
925
926	/* If this is a DFS path dfs_lookup should return
927	 * NT_STATUS_PATH_NOT_COVERED. */
928
929	status = dfs_path_lookup(ctx, conn, dfs_path, pdp,
930			False, consumedcntp, &targetpath);
931
932	if (!NT_STATUS_EQUAL(status, NT_STATUS_PATH_NOT_COVERED)) {
933		DEBUG(3,("get_referred_path: No valid referrals for path %s\n",
934			dfs_path));
935		vfs_ChDir(conn, oldpath);
936		conn_free(conn);
937		TALLOC_FREE(pdp);
938		return status;
939	}
940
941	/* We know this is a valid dfs link. Parse the targetpath. */
942	if (!parse_msdfs_symlink(ctx, targetpath,
943				&jucn->referral_list,
944				&jucn->referral_count)) {
945		DEBUG(3,("get_referred_path: failed to parse symlink "
946			"target %s\n", targetpath ));
947		vfs_ChDir(conn, oldpath);
948		conn_free(conn);
949		TALLOC_FREE(pdp);
950		return NT_STATUS_NOT_FOUND;
951	}
952
953	vfs_ChDir(conn, oldpath);
954	conn_free(conn);
955	TALLOC_FREE(pdp);
956	return NT_STATUS_OK;
957}
958
959static int setup_ver2_dfs_referral(const char *pathname,
960				char **ppdata,
961				struct junction_map *junction,
962				bool self_referral)
963{
964	char* pdata = *ppdata;
965
966	smb_ucs2_t *uni_requestedpath = NULL;
967	int uni_reqpathoffset1,uni_reqpathoffset2;
968	int uni_curroffset;
969	int requestedpathlen=0;
970	int offset;
971	int reply_size = 0;
972	int i=0;
973
974	DEBUG(10,("Setting up version2 referral\nRequested path:\n"));
975
976	requestedpathlen = rpcstr_push_talloc(talloc_tos(),
977					&uni_requestedpath, pathname);
978	if (uni_requestedpath == NULL || requestedpathlen == 0) {
979		return -1;
980	}
981
982	if (DEBUGLVL(10)) {
983		dump_data(0, (unsigned char *)uni_requestedpath,
984			requestedpathlen);
985	}
986
987	DEBUG(10,("ref count = %u\n",junction->referral_count));
988
989	uni_reqpathoffset1 = REFERRAL_HEADER_SIZE +
990			VERSION2_REFERRAL_SIZE * junction->referral_count;
991
992	uni_reqpathoffset2 = uni_reqpathoffset1 + requestedpathlen;
993
994	uni_curroffset = uni_reqpathoffset2 + requestedpathlen;
995
996	reply_size = REFERRAL_HEADER_SIZE +
997			VERSION2_REFERRAL_SIZE*junction->referral_count +
998			2 * requestedpathlen;
999	DEBUG(10,("reply_size: %u\n",reply_size));
1000
1001	/* add up the unicode lengths of all the referral paths */
1002	for(i=0;i<junction->referral_count;i++) {
1003		DEBUG(10,("referral %u : %s\n",
1004			i,
1005			junction->referral_list[i].alternate_path));
1006		reply_size +=
1007			(strlen(junction->referral_list[i].alternate_path)+1)*2;
1008	}
1009
1010	DEBUG(10,("reply_size = %u\n",reply_size));
1011	/* add the unexplained 0x16 bytes */
1012	reply_size += 0x16;
1013
1014	pdata = (char *)SMB_REALLOC(pdata,reply_size);
1015	if(pdata == NULL) {
1016		DEBUG(0,("Realloc failed!\n"));
1017		return -1;
1018	}
1019	*ppdata = pdata;
1020
1021	/* copy in the dfs requested paths.. required for offset calculations */
1022	memcpy(pdata+uni_reqpathoffset1,uni_requestedpath,requestedpathlen);
1023	memcpy(pdata+uni_reqpathoffset2,uni_requestedpath,requestedpathlen);
1024
1025	/* create the header */
1026	SSVAL(pdata,0,requestedpathlen - 2); /* UCS2 of path consumed minus
1027						2 byte null */
1028	/* number of referral in this pkt */
1029	SSVAL(pdata,2,junction->referral_count);
1030	if(self_referral) {
1031		SIVAL(pdata,4,DFSREF_REFERRAL_SERVER | DFSREF_STORAGE_SERVER);
1032	} else {
1033		SIVAL(pdata,4,DFSREF_STORAGE_SERVER);
1034	}
1035
1036	offset = 8;
1037	/* add the referral elements */
1038	for(i=0;i<junction->referral_count;i++) {
1039		struct referral* ref = &junction->referral_list[i];
1040		int unilen;
1041
1042		SSVAL(pdata,offset,2); /* version 2 */
1043		SSVAL(pdata,offset+2,VERSION2_REFERRAL_SIZE);
1044		if(self_referral) {
1045			SSVAL(pdata,offset+4,1);
1046		} else {
1047			SSVAL(pdata,offset+4,0);
1048		}
1049
1050		/* ref_flags :use path_consumed bytes? */
1051		SSVAL(pdata,offset+6,0);
1052		SIVAL(pdata,offset+8,ref->proximity);
1053		SIVAL(pdata,offset+12,ref->ttl);
1054
1055		SSVAL(pdata,offset+16,uni_reqpathoffset1-offset);
1056		SSVAL(pdata,offset+18,uni_reqpathoffset2-offset);
1057		/* copy referred path into current offset */
1058		unilen = rpcstr_push(pdata+uni_curroffset,
1059					ref->alternate_path,
1060					reply_size - uni_curroffset,
1061					STR_UNICODE);
1062
1063		SSVAL(pdata,offset+20,uni_curroffset-offset);
1064
1065		uni_curroffset += unilen;
1066		offset += VERSION2_REFERRAL_SIZE;
1067	}
1068	/* add in the unexplained 22 (0x16) bytes at the end */
1069	memset(pdata+uni_curroffset,'\0',0x16);
1070	return reply_size;
1071}
1072
1073static int setup_ver3_dfs_referral(const char *pathname,
1074				char **ppdata,
1075				struct junction_map *junction,
1076				bool self_referral)
1077{
1078	char *pdata = *ppdata;
1079
1080	smb_ucs2_t *uni_reqpath = NULL;
1081	int uni_reqpathoffset1, uni_reqpathoffset2;
1082	int uni_curroffset;
1083	int reply_size = 0;
1084
1085	int reqpathlen = 0;
1086	int offset,i=0;
1087
1088	DEBUG(10,("setting up version3 referral\n"));
1089
1090	reqpathlen = rpcstr_push_talloc(talloc_tos(), &uni_reqpath, pathname);
1091	if (uni_reqpath == NULL || reqpathlen == 0) {
1092		return -1;
1093	}
1094
1095	if (DEBUGLVL(10)) {
1096		dump_data(0, (unsigned char *)uni_reqpath,
1097			reqpathlen);
1098	}
1099
1100	uni_reqpathoffset1 = REFERRAL_HEADER_SIZE +
1101			VERSION3_REFERRAL_SIZE * junction->referral_count;
1102	uni_reqpathoffset2 = uni_reqpathoffset1 + reqpathlen;
1103	reply_size = uni_curroffset = uni_reqpathoffset2 + reqpathlen;
1104
1105	for(i=0;i<junction->referral_count;i++) {
1106		DEBUG(10,("referral %u : %s\n",
1107			i,
1108			junction->referral_list[i].alternate_path));
1109		reply_size +=
1110			(strlen(junction->referral_list[i].alternate_path)+1)*2;
1111	}
1112
1113	pdata = (char *)SMB_REALLOC(pdata,reply_size);
1114	if(pdata == NULL) {
1115		DEBUG(0,("version3 referral setup:"
1116			"malloc failed for Realloc!\n"));
1117		return -1;
1118	}
1119	*ppdata = pdata;
1120
1121	/* create the header */
1122	SSVAL(pdata,0,reqpathlen - 2); /* UCS2 of path consumed minus
1123					  2 byte null */
1124	SSVAL(pdata,2,junction->referral_count); /* number of referral */
1125	if(self_referral) {
1126		SIVAL(pdata,4,DFSREF_REFERRAL_SERVER | DFSREF_STORAGE_SERVER);
1127	} else {
1128		SIVAL(pdata,4,DFSREF_STORAGE_SERVER);
1129	}
1130
1131	/* copy in the reqpaths */
1132	memcpy(pdata+uni_reqpathoffset1,uni_reqpath,reqpathlen);
1133	memcpy(pdata+uni_reqpathoffset2,uni_reqpath,reqpathlen);
1134
1135	offset = 8;
1136	for(i=0;i<junction->referral_count;i++) {
1137		struct referral* ref = &(junction->referral_list[i]);
1138		int unilen;
1139
1140		SSVAL(pdata,offset,3); /* version 3 */
1141		SSVAL(pdata,offset+2,VERSION3_REFERRAL_SIZE);
1142		if(self_referral) {
1143			SSVAL(pdata,offset+4,1);
1144		} else {
1145			SSVAL(pdata,offset+4,0);
1146		}
1147
1148		/* ref_flags :use path_consumed bytes? */
1149		SSVAL(pdata,offset+6,0);
1150		SIVAL(pdata,offset+8,ref->ttl);
1151
1152		SSVAL(pdata,offset+12,uni_reqpathoffset1-offset);
1153		SSVAL(pdata,offset+14,uni_reqpathoffset2-offset);
1154		/* copy referred path into current offset */
1155		unilen = rpcstr_push(pdata+uni_curroffset,ref->alternate_path,
1156					reply_size - uni_curroffset,
1157					STR_UNICODE | STR_TERMINATE);
1158		SSVAL(pdata,offset+16,uni_curroffset-offset);
1159		/* copy 0x10 bytes of 00's in the ServiceSite GUID */
1160		memset(pdata+offset+18,'\0',16);
1161
1162		uni_curroffset += unilen;
1163		offset += VERSION3_REFERRAL_SIZE;
1164	}
1165	return reply_size;
1166}
1167
1168/******************************************************************
1169 Set up the DFS referral for the dfs pathname. This call returns
1170 the amount of the path covered by this server, and where the
1171 client should be redirected to. This is the meat of the
1172 TRANS2_GET_DFS_REFERRAL call.
1173******************************************************************/
1174
1175int setup_dfs_referral(connection_struct *orig_conn,
1176			const char *dfs_path,
1177			int max_referral_level,
1178			char **ppdata, NTSTATUS *pstatus)
1179{
1180	struct junction_map *junction = NULL;
1181	int consumedcnt = 0;
1182	bool self_referral = False;
1183	int reply_size = 0;
1184	char *pathnamep = NULL;
1185	char *local_dfs_path = NULL;
1186	TALLOC_CTX *ctx;
1187
1188	if (!(ctx=talloc_init("setup_dfs_referral"))) {
1189		*pstatus = NT_STATUS_NO_MEMORY;
1190		return -1;
1191	}
1192
1193	/* get the junction entry */
1194	if (!dfs_path) {
1195		talloc_destroy(ctx);
1196		*pstatus = NT_STATUS_NOT_FOUND;
1197		return -1;
1198	}
1199
1200	/*
1201	 * Trim pathname sent by client so it begins with only one backslash.
1202	 * Two backslashes confuse some dfs clients
1203	 */
1204
1205	local_dfs_path = talloc_strdup(ctx,dfs_path);
1206	if (!local_dfs_path) {
1207		*pstatus = NT_STATUS_NO_MEMORY;
1208		talloc_destroy(ctx);
1209		return -1;
1210	}
1211	pathnamep = local_dfs_path;
1212	while (IS_DIRECTORY_SEP(pathnamep[0]) &&
1213			IS_DIRECTORY_SEP(pathnamep[1])) {
1214		pathnamep++;
1215	}
1216
1217	junction = TALLOC_ZERO_P(ctx, struct junction_map);
1218	if (!junction) {
1219		*pstatus = NT_STATUS_NO_MEMORY;
1220		talloc_destroy(ctx);
1221		return -1;
1222	}
1223
1224	/* The following call can change cwd. */
1225	*pstatus = get_referred_path(ctx, orig_conn->server_info,
1226				     pathnamep, junction,
1227				     &consumedcnt, &self_referral);
1228	if (!NT_STATUS_IS_OK(*pstatus)) {
1229		vfs_ChDir(orig_conn,orig_conn->connectpath);
1230		talloc_destroy(ctx);
1231		return -1;
1232	}
1233	vfs_ChDir(orig_conn,orig_conn->connectpath);
1234
1235	if (!self_referral) {
1236		pathnamep[consumedcnt] = '\0';
1237
1238		if( DEBUGLVL( 3 ) ) {
1239			int i=0;
1240			dbgtext("setup_dfs_referral: Path %s to "
1241				"alternate path(s):",
1242				pathnamep);
1243			for(i=0;i<junction->referral_count;i++)
1244				dbgtext(" %s",
1245				junction->referral_list[i].alternate_path);
1246			dbgtext(".\n");
1247		}
1248	}
1249
1250	/* create the referral depeding on version */
1251	DEBUG(10,("max_referral_level :%d\n",max_referral_level));
1252
1253	if (max_referral_level < 2) {
1254		max_referral_level = 2;
1255	}
1256	if (max_referral_level > 3) {
1257		max_referral_level = 3;
1258	}
1259
1260	switch(max_referral_level) {
1261	case 2:
1262		reply_size = setup_ver2_dfs_referral(pathnamep,
1263					ppdata, junction,
1264					self_referral);
1265		break;
1266	case 3:
1267		reply_size = setup_ver3_dfs_referral(pathnamep, ppdata,
1268					junction, self_referral);
1269		break;
1270	default:
1271		DEBUG(0,("setup_dfs_referral: Invalid dfs referral "
1272			"version: %d\n",
1273			max_referral_level));
1274		talloc_destroy(ctx);
1275		*pstatus = NT_STATUS_INVALID_LEVEL;
1276		return -1;
1277	}
1278
1279	if (DEBUGLVL(10)) {
1280		DEBUGADD(0,("DFS Referral pdata:\n"));
1281		dump_data(0,(uint8 *)*ppdata,reply_size);
1282	}
1283
1284	talloc_destroy(ctx);
1285	*pstatus = NT_STATUS_OK;
1286	return reply_size;
1287}
1288
1289/**********************************************************************
1290 The following functions are called by the NETDFS RPC pipe functions
1291 **********************************************************************/
1292
1293/*********************************************************************
1294 Creates a junction structure from a DFS pathname
1295**********************************************************************/
1296
1297bool create_junction(TALLOC_CTX *ctx,
1298		const char *dfs_path,
1299		struct junction_map *jucn)
1300{
1301	int snum;
1302	bool dummy;
1303	struct dfs_path *pdp = TALLOC_P(ctx,struct dfs_path);
1304	NTSTATUS status;
1305
1306	if (!pdp) {
1307		return False;
1308	}
1309	status = parse_dfs_path(NULL, dfs_path, False, pdp, &dummy);
1310	if (!NT_STATUS_IS_OK(status)) {
1311		return False;
1312	}
1313
1314	/* check if path is dfs : validate first token */
1315	if (!is_myname_or_ipaddr(pdp->hostname)) {
1316		DEBUG(4,("create_junction: Invalid hostname %s "
1317			"in dfs path %s\n",
1318			pdp->hostname, dfs_path));
1319		TALLOC_FREE(pdp);
1320		return False;
1321	}
1322
1323	/* Check for a non-DFS share */
1324	snum = lp_servicenumber(pdp->servicename);
1325
1326	if(snum < 0 || !lp_msdfs_root(snum)) {
1327		DEBUG(4,("create_junction: %s is not an msdfs root.\n",
1328			pdp->servicename));
1329		TALLOC_FREE(pdp);
1330		return False;
1331	}
1332
1333	jucn->service_name = talloc_strdup(ctx, pdp->servicename);
1334	jucn->volume_name = talloc_strdup(ctx, pdp->reqpath);
1335	jucn->comment = talloc_strdup(ctx, lp_comment(snum));
1336
1337	TALLOC_FREE(pdp);
1338	if (!jucn->service_name || !jucn->volume_name || ! jucn->comment) {
1339		return False;
1340	}
1341	return True;
1342}
1343
1344/**********************************************************************
1345 Forms a valid Unix pathname from the junction
1346 **********************************************************************/
1347
1348static bool junction_to_local_path(const struct junction_map *jucn,
1349				   char **pp_path_out,
1350				   connection_struct **conn_out,
1351				   char **oldpath)
1352{
1353	int snum;
1354	NTSTATUS status;
1355
1356	snum = lp_servicenumber(jucn->service_name);
1357	if(snum < 0) {
1358		return False;
1359	}
1360	status = create_conn_struct(talloc_tos(), conn_out, snum,
1361				    lp_pathname(snum), NULL, oldpath);
1362	if (!NT_STATUS_IS_OK(status)) {
1363		return False;
1364	}
1365
1366	*pp_path_out = talloc_asprintf(*conn_out,
1367			"%s/%s",
1368			lp_pathname(snum),
1369			jucn->volume_name);
1370	if (!*pp_path_out) {
1371		vfs_ChDir(*conn_out, *oldpath);
1372		conn_free(*conn_out);
1373		return False;
1374	}
1375	return True;
1376}
1377
1378bool create_msdfs_link(const struct junction_map *jucn)
1379{
1380	char *path = NULL;
1381	char *cwd;
1382	char *msdfs_link = NULL;
1383	connection_struct *conn;
1384	int i=0;
1385	bool insert_comma = False;
1386	bool ret = False;
1387
1388	if(!junction_to_local_path(jucn, &path, &conn, &cwd)) {
1389		return False;
1390	}
1391
1392	/* Form the msdfs_link contents */
1393	msdfs_link = talloc_strdup(conn, "msdfs:");
1394	if (!msdfs_link) {
1395		goto out;
1396	}
1397	for(i=0; i<jucn->referral_count; i++) {
1398		char *refpath = jucn->referral_list[i].alternate_path;
1399
1400		/* Alternate paths always use Windows separators. */
1401		trim_char(refpath, '\\', '\\');
1402		if(*refpath == '\0') {
1403			if (i == 0) {
1404				insert_comma = False;
1405			}
1406			continue;
1407		}
1408		if (i > 0 && insert_comma) {
1409			msdfs_link = talloc_asprintf_append_buffer(msdfs_link,
1410					",%s",
1411					refpath);
1412		} else {
1413			msdfs_link = talloc_asprintf_append_buffer(msdfs_link,
1414					"%s",
1415					refpath);
1416		}
1417
1418		if (!msdfs_link) {
1419			goto out;
1420		}
1421		if (!insert_comma) {
1422			insert_comma = True;
1423		}
1424	}
1425
1426	DEBUG(5,("create_msdfs_link: Creating new msdfs link: %s -> %s\n",
1427		path, msdfs_link));
1428
1429	if(SMB_VFS_SYMLINK(conn, msdfs_link, path) < 0) {
1430		if (errno == EEXIST) {
1431			struct smb_filename *smb_fname = NULL;
1432			NTSTATUS status;
1433
1434			status = create_synthetic_smb_fname(talloc_tos(), path,
1435							    NULL, NULL,
1436							    &smb_fname);
1437			if (!NT_STATUS_IS_OK(status)) {
1438				errno = map_errno_from_nt_status(status);
1439				goto out;
1440			}
1441
1442			if(SMB_VFS_UNLINK(conn, smb_fname)!=0) {
1443				TALLOC_FREE(smb_fname);
1444				goto out;
1445			}
1446			TALLOC_FREE(smb_fname);
1447		}
1448		if (SMB_VFS_SYMLINK(conn, msdfs_link, path) < 0) {
1449			DEBUG(1,("create_msdfs_link: symlink failed "
1450				 "%s -> %s\nError: %s\n",
1451				 path, msdfs_link, strerror(errno)));
1452			goto out;
1453		}
1454	}
1455
1456	ret = True;
1457
1458out:
1459	vfs_ChDir(conn, cwd);
1460	conn_free(conn);
1461	return ret;
1462}
1463
1464bool remove_msdfs_link(const struct junction_map *jucn)
1465{
1466	char *path = NULL;
1467	char *cwd;
1468	connection_struct *conn;
1469	bool ret = False;
1470	struct smb_filename *smb_fname = NULL;
1471	NTSTATUS status;
1472
1473	if (!junction_to_local_path(jucn, &path, &conn, &cwd)) {
1474		return false;
1475	}
1476
1477	status = create_synthetic_smb_fname(talloc_tos(), path,
1478					    NULL, NULL,
1479					    &smb_fname);
1480	if (!NT_STATUS_IS_OK(status)) {
1481		errno = map_errno_from_nt_status(status);
1482		return false;
1483	}
1484
1485	if( SMB_VFS_UNLINK(conn, smb_fname) == 0 ) {
1486		ret = True;
1487	}
1488
1489	TALLOC_FREE(smb_fname);
1490	vfs_ChDir(conn, cwd);
1491	conn_free(conn);
1492	return ret;
1493}
1494
1495/*********************************************************************
1496 Return the number of DFS links at the root of this share.
1497*********************************************************************/
1498
1499static int count_dfs_links(TALLOC_CTX *ctx, int snum)
1500{
1501	size_t cnt = 0;
1502	SMB_STRUCT_DIR *dirp = NULL;
1503	const char *dname = NULL;
1504	char *talloced = NULL;
1505	const char *connect_path = lp_pathname(snum);
1506	const char *msdfs_proxy = lp_msdfs_proxy(snum);
1507	connection_struct *conn;
1508	NTSTATUS status;
1509	char *cwd;
1510
1511	if(*connect_path == '\0') {
1512		return 0;
1513	}
1514
1515	/*
1516	 * Fake up a connection struct for the VFS layer.
1517	 */
1518
1519	status = create_conn_struct(talloc_tos(), &conn, snum, connect_path,
1520				    NULL, &cwd);
1521	if (!NT_STATUS_IS_OK(status)) {
1522		DEBUG(3, ("create_conn_struct failed: %s\n",
1523			  nt_errstr(status)));
1524		return 0;
1525	}
1526
1527	/* Count a link for the msdfs root - convention */
1528	cnt = 1;
1529
1530	/* No more links if this is an msdfs proxy. */
1531	if (*msdfs_proxy != '\0') {
1532		goto out;
1533	}
1534
1535	/* Now enumerate all dfs links */
1536	dirp = SMB_VFS_OPENDIR(conn, ".", NULL, 0);
1537	if(!dirp) {
1538		goto out;
1539	}
1540
1541	while ((dname = vfs_readdirname(conn, dirp, NULL, &talloced))
1542	       != NULL) {
1543		if (is_msdfs_link(conn,
1544				dname,
1545				NULL)) {
1546			cnt++;
1547		}
1548		TALLOC_FREE(talloced);
1549	}
1550
1551	SMB_VFS_CLOSEDIR(conn,dirp);
1552
1553out:
1554	vfs_ChDir(conn, cwd);
1555	conn_free(conn);
1556	return cnt;
1557}
1558
1559/*********************************************************************
1560*********************************************************************/
1561
1562static int form_junctions(TALLOC_CTX *ctx,
1563				int snum,
1564				struct junction_map *jucn,
1565				size_t jn_remain)
1566{
1567	size_t cnt = 0;
1568	SMB_STRUCT_DIR *dirp = NULL;
1569	const char *dname = NULL;
1570	char *talloced = NULL;
1571	const char *connect_path = lp_pathname(snum);
1572	char *service_name = lp_servicename(snum);
1573	const char *msdfs_proxy = lp_msdfs_proxy(snum);
1574	connection_struct *conn;
1575	struct referral *ref = NULL;
1576	char *cwd;
1577	NTSTATUS status;
1578
1579	if (jn_remain == 0) {
1580		return 0;
1581	}
1582
1583	if(*connect_path == '\0') {
1584		return 0;
1585	}
1586
1587	/*
1588	 * Fake up a connection struct for the VFS layer.
1589	 */
1590
1591	status = create_conn_struct(ctx, &conn, snum, connect_path, NULL,
1592				    &cwd);
1593	if (!NT_STATUS_IS_OK(status)) {
1594		DEBUG(3, ("create_conn_struct failed: %s\n",
1595			  nt_errstr(status)));
1596		return 0;
1597	}
1598
1599	/* form a junction for the msdfs root - convention
1600	   DO NOT REMOVE THIS: NT clients will not work with us
1601	   if this is not present
1602	*/
1603	jucn[cnt].service_name = talloc_strdup(ctx,service_name);
1604	jucn[cnt].volume_name = talloc_strdup(ctx, "");
1605	if (!jucn[cnt].service_name || !jucn[cnt].volume_name) {
1606		goto out;
1607	}
1608	jucn[cnt].comment = "";
1609	jucn[cnt].referral_count = 1;
1610
1611	ref = jucn[cnt].referral_list = TALLOC_ZERO_P(ctx, struct referral);
1612	if (jucn[cnt].referral_list == NULL) {
1613		goto out;
1614	}
1615
1616	ref->proximity = 0;
1617	ref->ttl = REFERRAL_TTL;
1618	if (*msdfs_proxy != '\0') {
1619		ref->alternate_path = talloc_strdup(ctx,
1620						msdfs_proxy);
1621	} else {
1622		ref->alternate_path = talloc_asprintf(ctx,
1623			"\\\\%s\\%s",
1624			get_local_machine_name(),
1625			service_name);
1626	}
1627
1628	if (!ref->alternate_path) {
1629		goto out;
1630	}
1631	cnt++;
1632
1633	/* Don't enumerate if we're an msdfs proxy. */
1634	if (*msdfs_proxy != '\0') {
1635		goto out;
1636	}
1637
1638	/* Now enumerate all dfs links */
1639	dirp = SMB_VFS_OPENDIR(conn, ".", NULL, 0);
1640	if(!dirp) {
1641		goto out;
1642	}
1643
1644	while ((dname = vfs_readdirname(conn, dirp, NULL, &talloced))
1645	       != NULL) {
1646		char *link_target = NULL;
1647		if (cnt >= jn_remain) {
1648			DEBUG(2, ("form_junctions: ran out of MSDFS "
1649				"junction slots"));
1650			TALLOC_FREE(talloced);
1651			goto out;
1652		}
1653		if (is_msdfs_link_internal(ctx,
1654					conn,
1655					dname, &link_target,
1656					NULL)) {
1657			if (parse_msdfs_symlink(ctx,
1658					link_target,
1659					&jucn[cnt].referral_list,
1660					&jucn[cnt].referral_count)) {
1661
1662				jucn[cnt].service_name = talloc_strdup(ctx,
1663								service_name);
1664				jucn[cnt].volume_name = talloc_strdup(ctx,
1665								dname);
1666				if (!jucn[cnt].service_name ||
1667						!jucn[cnt].volume_name) {
1668					TALLOC_FREE(talloced);
1669					goto out;
1670				}
1671				jucn[cnt].comment = "";
1672				cnt++;
1673			}
1674			TALLOC_FREE(link_target);
1675		}
1676		TALLOC_FREE(talloced);
1677	}
1678
1679out:
1680
1681	if (dirp) {
1682		SMB_VFS_CLOSEDIR(conn,dirp);
1683	}
1684
1685	vfs_ChDir(conn, cwd);
1686	conn_free(conn);
1687	return cnt;
1688}
1689
1690struct junction_map *enum_msdfs_links(TALLOC_CTX *ctx, size_t *p_num_jn)
1691{
1692	struct junction_map *jn = NULL;
1693	int i=0;
1694	size_t jn_count = 0;
1695	int sharecount = 0;
1696
1697	*p_num_jn = 0;
1698	if(!lp_host_msdfs()) {
1699		return NULL;
1700	}
1701
1702	/* Ensure all the usershares are loaded. */
1703	become_root();
1704	load_registry_shares();
1705	sharecount = load_usershare_shares();
1706	unbecome_root();
1707
1708	for(i=0;i < sharecount;i++) {
1709		if(lp_msdfs_root(i)) {
1710			jn_count += count_dfs_links(ctx, i);
1711		}
1712	}
1713	if (jn_count == 0) {
1714		return NULL;
1715	}
1716	jn = TALLOC_ARRAY(ctx,  struct junction_map, jn_count);
1717	if (!jn) {
1718		return NULL;
1719	}
1720	for(i=0; i < sharecount; i++) {
1721		if (*p_num_jn >= jn_count) {
1722			break;
1723		}
1724		if(lp_msdfs_root(i)) {
1725			*p_num_jn += form_junctions(ctx, i,
1726					&jn[*p_num_jn],
1727					jn_count - *p_num_jn);
1728		}
1729	}
1730	return jn;
1731}
1732
1733/******************************************************************************
1734 Core function to resolve a dfs pathname possibly containing a wildcard.  If
1735 ppath_contains_wcard != NULL, it will be set to true if a wildcard is
1736 detected during dfs resolution.
1737******************************************************************************/
1738
1739NTSTATUS resolve_dfspath_wcard(TALLOC_CTX *ctx,
1740				connection_struct *conn,
1741				bool dfs_pathnames,
1742				const char *name_in,
1743				bool allow_wcards,
1744				char **pp_name_out,
1745				bool *ppath_contains_wcard)
1746{
1747	bool path_contains_wcard;
1748	NTSTATUS status = NT_STATUS_OK;
1749
1750	if (dfs_pathnames) {
1751		status = dfs_redirect(ctx,
1752					conn,
1753					name_in,
1754					allow_wcards,
1755					pp_name_out,
1756					&path_contains_wcard);
1757
1758		if (NT_STATUS_IS_OK(status) && ppath_contains_wcard != NULL) {
1759			*ppath_contains_wcard = path_contains_wcard;
1760		}
1761	} else {
1762		/*
1763		 * Cheat and just return a copy of the in ptr.
1764		 * Once srvstr_get_path() uses talloc it'll
1765		 * be a talloced ptr anyway.
1766		 */
1767		*pp_name_out = CONST_DISCARD(char *,name_in);
1768	}
1769	return status;
1770}
1771