s3-docs: Fix typos.
[Samba/gbeck.git] / source3 / smbd / msdfs.c
blob64a6b40820d88aafce938d83f4131f1a5e1436c2
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
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.
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.
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/>.
23 #define DBGC_CLASS DBGC_MSDFS
24 #include "includes.h"
25 #include "system/filesys.h"
26 #include "smbd/smbd.h"
27 #include "smbd/globals.h"
28 #include "msdfs.h"
29 #include "auth.h"
30 #include "lib/param/loadparm.h"
31 #include "libcli/security/security.h"
33 /**********************************************************************
34 Parse a DFS pathname of the form \hostname\service\reqpath
35 into the dfs_path structure.
36 If POSIX pathnames is true, the pathname may also be of the
37 form /hostname/service/reqpath.
38 We cope with either here.
40 Unfortunately, due to broken clients who might set the
41 SVAL(inbuf,smb_flg2) & FLAGS2_DFS_PATHNAMES bit and then
42 send a local path, we have to cope with that too....
44 If conn != NULL then ensure the provided service is
45 the one pointed to by the connection.
47 This version does everything using pointers within one copy of the
48 pathname string, talloced on the struct dfs_path pointer (which
49 must be talloced). This may be too clever to live....
50 JRA.
51 **********************************************************************/
53 static NTSTATUS parse_dfs_path(connection_struct *conn,
54 const char *pathname,
55 bool allow_wcards,
56 bool allow_broken_path,
57 struct dfs_path *pdp, /* MUST BE TALLOCED */
58 bool *ppath_contains_wcard)
60 char *pathname_local;
61 char *p,*temp;
62 char *servicename;
63 char *eos_ptr;
64 NTSTATUS status = NT_STATUS_OK;
65 char sepchar;
67 ZERO_STRUCTP(pdp);
70 * This is the only talloc we should need to do
71 * on the struct dfs_path. All the pointers inside
72 * it should point to offsets within this string.
75 pathname_local = talloc_strdup(pdp, pathname);
76 if (!pathname_local) {
77 return NT_STATUS_NO_MEMORY;
79 /* Get a pointer to the terminating '\0' */
80 eos_ptr = &pathname_local[strlen(pathname_local)];
81 p = temp = pathname_local;
83 pdp->posix_path = (lp_posix_pathnames() && *pathname == '/');
85 sepchar = pdp->posix_path ? '/' : '\\';
87 if (allow_broken_path && (*pathname != sepchar)) {
88 DEBUG(10,("parse_dfs_path: path %s doesn't start with %c\n",
89 pathname, sepchar ));
91 * Possibly client sent a local path by mistake.
92 * Try and convert to a local path.
95 pdp->hostname = eos_ptr; /* "" */
96 pdp->servicename = eos_ptr; /* "" */
98 /* We've got no info about separators. */
99 pdp->posix_path = lp_posix_pathnames();
100 p = temp;
101 DEBUG(10,("parse_dfs_path: trying to convert %s to a "
102 "local path\n",
103 temp));
104 goto local_path;
108 * Safe to use on talloc'ed string as it only shrinks.
109 * It also doesn't affect the eos_ptr.
111 trim_char(temp,sepchar,sepchar);
113 DEBUG(10,("parse_dfs_path: temp = |%s| after trimming %c's\n",
114 temp, sepchar));
116 /* Now tokenize. */
117 /* Parse out hostname. */
118 p = strchr_m(temp,sepchar);
119 if(p == NULL) {
120 DEBUG(10,("parse_dfs_path: can't parse hostname from path %s\n",
121 temp));
123 * Possibly client sent a local path by mistake.
124 * Try and convert to a local path.
127 pdp->hostname = eos_ptr; /* "" */
128 pdp->servicename = eos_ptr; /* "" */
130 p = temp;
131 DEBUG(10,("parse_dfs_path: trying to convert %s "
132 "to a local path\n",
133 temp));
134 goto local_path;
136 *p = '\0';
137 pdp->hostname = temp;
139 DEBUG(10,("parse_dfs_path: hostname: %s\n",pdp->hostname));
141 /* Parse out servicename. */
142 servicename = p+1;
143 p = strchr_m(servicename,sepchar);
144 if (p) {
145 *p = '\0';
148 /* Is this really our servicename ? */
149 if (conn && !( strequal(servicename, lp_servicename(SNUM(conn)))
150 || (strequal(servicename, HOMES_NAME)
151 && strequal(lp_servicename(SNUM(conn)),
152 get_current_username()) )) ) {
153 DEBUG(10,("parse_dfs_path: %s is not our servicename\n",
154 servicename));
157 * Possibly client sent a local path by mistake.
158 * Try and convert to a local path.
161 pdp->hostname = eos_ptr; /* "" */
162 pdp->servicename = eos_ptr; /* "" */
164 /* Repair the path - replace the sepchar's
165 we nulled out */
166 servicename--;
167 *servicename = sepchar;
168 if (p) {
169 *p = sepchar;
172 p = temp;
173 DEBUG(10,("parse_dfs_path: trying to convert %s "
174 "to a local path\n",
175 temp));
176 goto local_path;
179 pdp->servicename = servicename;
181 DEBUG(10,("parse_dfs_path: servicename: %s\n",pdp->servicename));
183 if(p == NULL) {
184 /* Client sent self referral \server\share. */
185 pdp->reqpath = eos_ptr; /* "" */
186 return NT_STATUS_OK;
189 p++;
191 local_path:
193 *ppath_contains_wcard = False;
195 pdp->reqpath = p;
197 /* Rest is reqpath. */
198 if (pdp->posix_path) {
199 status = check_path_syntax_posix(pdp->reqpath);
200 } else {
201 if (allow_wcards) {
202 status = check_path_syntax_wcard(pdp->reqpath,
203 ppath_contains_wcard);
204 } else {
205 status = check_path_syntax(pdp->reqpath);
209 if (!NT_STATUS_IS_OK(status)) {
210 DEBUG(10,("parse_dfs_path: '%s' failed with %s\n",
211 p, nt_errstr(status) ));
212 return status;
215 DEBUG(10,("parse_dfs_path: rest of the path: %s\n",pdp->reqpath));
216 return NT_STATUS_OK;
219 /********************************************************
220 Fake up a connection struct for the VFS layer.
221 Note: this performs a vfs connect and CHANGES CWD !!!! JRA.
222 *********************************************************/
224 NTSTATUS create_conn_struct(TALLOC_CTX *ctx,
225 struct smbd_server_connection *sconn,
226 connection_struct **pconn,
227 int snum,
228 const char *path,
229 const struct auth_session_info *session_info,
230 char **poldcwd)
232 connection_struct *conn;
233 char *connpath;
234 char *oldcwd;
235 const char *vfs_user;
237 conn = talloc_zero(ctx, connection_struct);
238 if (conn == NULL) {
239 return NT_STATUS_NO_MEMORY;
242 connpath = talloc_strdup(conn, path);
243 if (!connpath) {
244 TALLOC_FREE(conn);
245 return NT_STATUS_NO_MEMORY;
247 connpath = talloc_string_sub(conn,
248 connpath,
249 "%S",
250 lp_servicename(snum));
251 if (!connpath) {
252 TALLOC_FREE(conn);
253 return NT_STATUS_NO_MEMORY;
256 /* needed for smbd_vfs_init() */
258 if (!(conn->params = talloc_zero(conn, struct share_params))) {
259 DEBUG(0, ("TALLOC failed\n"));
260 TALLOC_FREE(conn);
261 return NT_STATUS_NO_MEMORY;
264 conn->params->service = snum;
266 conn->sconn = sconn;
267 conn->sconn->num_tcons_open++;
269 if (session_info != NULL) {
270 conn->session_info = copy_session_info(conn, session_info);
271 if (conn->session_info == NULL) {
272 DEBUG(0, ("copy_serverinfo failed\n"));
273 TALLOC_FREE(conn);
274 return NT_STATUS_NO_MEMORY;
276 vfs_user = conn->session_info->unix_info->unix_name;
277 } else {
278 /* use current authenticated user in absence of session_info */
279 vfs_user = get_current_username();
282 set_conn_connectpath(conn, connpath);
285 * New code to check if there's a share security descripter
286 * added from NT server manager. This is done after the
287 * smb.conf checks are done as we need a uid and token. JRA.
290 if (conn->session_info) {
291 share_access_check(conn->session_info->security_token,
292 lp_servicename(snum), MAXIMUM_ALLOWED_ACCESS,
293 &conn->share_access);
295 if ((conn->share_access & FILE_WRITE_DATA) == 0) {
296 if ((conn->share_access & FILE_READ_DATA) == 0) {
297 /* No access, read or write. */
298 DEBUG(0,("create_conn_struct: connection to %s "
299 "denied due to security "
300 "descriptor.\n",
301 lp_servicename(snum)));
302 conn_free(conn);
303 return NT_STATUS_ACCESS_DENIED;
304 } else {
305 conn->read_only = true;
308 } else {
309 conn->share_access = 0;
310 conn->read_only = true;
313 if (!smbd_vfs_init(conn)) {
314 NTSTATUS status = map_nt_error_from_unix(errno);
315 DEBUG(0,("create_conn_struct: smbd_vfs_init failed.\n"));
316 conn_free(conn);
317 return status;
320 /* this must be the first filesystem operation that we do */
321 if (SMB_VFS_CONNECT(conn, lp_servicename(snum), vfs_user) < 0) {
322 DEBUG(0,("VFS connect failed!\n"));
323 conn_free(conn);
324 return NT_STATUS_UNSUCCESSFUL;
327 conn->fs_capabilities = SMB_VFS_FS_CAPABILITIES(conn, &conn->ts_res);
330 * Windows seems to insist on doing trans2getdfsreferral() calls on
331 * the IPC$ share as the anonymous user. If we try to chdir as that
332 * user we will fail.... WTF ? JRA.
335 oldcwd = vfs_GetWd(ctx, conn);
336 if (oldcwd == NULL) {
337 NTSTATUS status = map_nt_error_from_unix(errno);
338 DEBUG(3, ("vfs_GetWd failed: %s\n", strerror(errno)));
339 conn_free(conn);
340 return status;
343 if (vfs_ChDir(conn,conn->connectpath) != 0) {
344 NTSTATUS status = map_nt_error_from_unix(errno);
345 DEBUG(3,("create_conn_struct: Can't ChDir to new conn path %s. "
346 "Error was %s\n",
347 conn->connectpath, strerror(errno) ));
348 conn_free(conn);
349 return status;
352 *pconn = conn;
353 *poldcwd = oldcwd;
355 return NT_STATUS_OK;
358 /**********************************************************************
359 Parse the contents of a symlink to verify if it is an msdfs referral
360 A valid referral is of the form:
362 msdfs:server1\share1,server2\share2
363 msdfs:server1\share1\pathname,server2\share2\pathname
364 msdfs:server1/share1,server2/share2
365 msdfs:server1/share1/pathname,server2/share2/pathname.
367 Note that the alternate paths returned here must be of the canonicalized
368 form:
370 \server\share or
371 \server\share\path\to\file,
373 even in posix path mode. This is because we have no knowledge if the
374 server we're referring to understands posix paths.
375 **********************************************************************/
377 static bool parse_msdfs_symlink(TALLOC_CTX *ctx,
378 const char *target,
379 struct referral **preflist,
380 int *refcount)
382 char *temp = NULL;
383 char *prot;
384 char **alt_path = NULL;
385 int count = 0, i;
386 struct referral *reflist;
387 char *saveptr;
389 temp = talloc_strdup(ctx, target);
390 if (!temp) {
391 return False;
393 prot = strtok_r(temp, ":", &saveptr);
394 if (!prot) {
395 DEBUG(0,("parse_msdfs_symlink: invalid path !\n"));
396 return False;
399 alt_path = talloc_array(ctx, char *, MAX_REFERRAL_COUNT);
400 if (!alt_path) {
401 return False;
404 /* parse out the alternate paths */
405 while((count<MAX_REFERRAL_COUNT) &&
406 ((alt_path[count] = strtok_r(NULL, ",", &saveptr)) != NULL)) {
407 count++;
410 DEBUG(10,("parse_msdfs_symlink: count=%d\n", count));
412 if (count) {
413 reflist = *preflist = talloc_zero_array(ctx,
414 struct referral, count);
415 if(reflist == NULL) {
416 TALLOC_FREE(alt_path);
417 return False;
419 } else {
420 reflist = *preflist = NULL;
423 for(i=0;i<count;i++) {
424 char *p;
426 /* Canonicalize link target.
427 * Replace all /'s in the path by a \ */
428 string_replace(alt_path[i], '/', '\\');
430 /* Remove leading '\\'s */
431 p = alt_path[i];
432 while (*p && (*p == '\\')) {
433 p++;
436 reflist[i].alternate_path = talloc_asprintf(ctx,
437 "\\%s",
439 if (!reflist[i].alternate_path) {
440 return False;
443 reflist[i].proximity = 0;
444 reflist[i].ttl = REFERRAL_TTL;
445 DEBUG(10, ("parse_msdfs_symlink: Created alt path: %s\n",
446 reflist[i].alternate_path));
449 *refcount = count;
451 TALLOC_FREE(alt_path);
452 return True;
455 /**********************************************************************
456 Returns true if the unix path is a valid msdfs symlink and also
457 returns the target string from inside the link.
458 **********************************************************************/
460 static bool is_msdfs_link_internal(TALLOC_CTX *ctx,
461 connection_struct *conn,
462 const char *path,
463 char **pp_link_target,
464 SMB_STRUCT_STAT *sbufp)
466 int referral_len = 0;
467 #if defined(HAVE_BROKEN_READLINK)
468 char link_target_buf[PATH_MAX];
469 #else
470 char link_target_buf[7];
471 #endif
472 size_t bufsize = 0;
473 char *link_target = NULL;
474 struct smb_filename smb_fname;
476 if (pp_link_target) {
477 bufsize = 1024;
478 link_target = talloc_array(ctx, char, bufsize);
479 if (!link_target) {
480 return False;
482 *pp_link_target = link_target;
483 } else {
484 bufsize = sizeof(link_target_buf);
485 link_target = link_target_buf;
488 ZERO_STRUCT(smb_fname);
489 smb_fname.base_name = discard_const_p(char, path);
491 if (SMB_VFS_LSTAT(conn, &smb_fname) != 0) {
492 DEBUG(5,("is_msdfs_link_read_target: %s does not exist.\n",
493 path));
494 goto err;
496 if (!S_ISLNK(smb_fname.st.st_ex_mode)) {
497 DEBUG(5,("is_msdfs_link_read_target: %s is not a link.\n",
498 path));
499 goto err;
501 if (sbufp != NULL) {
502 *sbufp = smb_fname.st;
505 referral_len = SMB_VFS_READLINK(conn, path, link_target, bufsize - 1);
506 if (referral_len == -1) {
507 DEBUG(0,("is_msdfs_link_read_target: Error reading "
508 "msdfs link %s: %s\n",
509 path, strerror(errno)));
510 goto err;
512 link_target[referral_len] = '\0';
514 DEBUG(5,("is_msdfs_link_internal: %s -> %s\n",path,
515 link_target));
517 if (!strnequal(link_target, "msdfs:", 6)) {
518 goto err;
520 return True;
522 err:
524 if (link_target != link_target_buf) {
525 TALLOC_FREE(link_target);
527 return False;
530 /**********************************************************************
531 Returns true if the unix path is a valid msdfs symlink.
532 **********************************************************************/
534 bool is_msdfs_link(connection_struct *conn,
535 const char *path,
536 SMB_STRUCT_STAT *sbufp)
538 return is_msdfs_link_internal(talloc_tos(),
539 conn,
540 path,
541 NULL,
542 sbufp);
545 /*****************************************************************
546 Used by other functions to decide if a dfs path is remote,
547 and to get the list of referred locations for that remote path.
549 search_flag: For findfirsts, dfs links themselves are not
550 redirected, but paths beyond the links are. For normal smb calls,
551 even dfs links need to be redirected.
553 consumedcntp: how much of the dfs path is being redirected. the client
554 should try the remaining path on the redirected server.
556 If this returns NT_STATUS_PATH_NOT_COVERED the contents of the msdfs
557 link redirect are in targetpath.
558 *****************************************************************/
560 static NTSTATUS dfs_path_lookup(TALLOC_CTX *ctx,
561 connection_struct *conn,
562 const char *dfspath, /* Incoming complete dfs path */
563 const struct dfs_path *pdp, /* Parsed out
564 server+share+extrapath. */
565 bool search_flag, /* Called from a findfirst ? */
566 int *consumedcntp,
567 char **pp_targetpath)
569 char *p = NULL;
570 char *q = NULL;
571 NTSTATUS status;
572 struct smb_filename *smb_fname = NULL;
573 char *canon_dfspath = NULL; /* Canonicalized dfs path. (only '/'
574 components). */
576 DEBUG(10,("dfs_path_lookup: Conn path = %s reqpath = %s\n",
577 conn->connectpath, pdp->reqpath));
580 * Note the unix path conversion here we're doing we can
581 * throw away. We're looking for a symlink for a dfs
582 * resolution, if we don't find it we'll do another
583 * unix_convert later in the codepath.
584 * If we needed to remember what we'd resolved in
585 * dp->reqpath (as the original code did) we'd
586 * copy (localhost, dp->reqpath) on any code
587 * path below that returns True - but I don't
588 * think this is needed. JRA.
591 status = unix_convert(ctx, conn, pdp->reqpath, &smb_fname,
592 search_flag ? UCF_ALWAYS_ALLOW_WCARD_LCOMP : 0);
594 if (!NT_STATUS_IS_OK(status)) {
595 if (!NT_STATUS_EQUAL(status,
596 NT_STATUS_OBJECT_PATH_NOT_FOUND)) {
597 return status;
600 /* Create an smb_fname to use below. */
601 status = create_synthetic_smb_fname(ctx, pdp->reqpath, NULL,
602 NULL, &smb_fname);
603 if (!NT_STATUS_IS_OK(status)) {
604 return status;
608 /* Optimization - check if we can redirect the whole path. */
610 if (is_msdfs_link_internal(ctx, conn, smb_fname->base_name,
611 pp_targetpath, NULL)) {
612 if (search_flag) {
613 DEBUG(6,("dfs_path_lookup (FindFirst) No redirection "
614 "for dfs link %s.\n", dfspath));
615 status = NT_STATUS_OK;
616 goto out;
619 DEBUG(6,("dfs_path_lookup: %s resolves to a "
620 "valid dfs link %s.\n", dfspath,
621 pp_targetpath ? *pp_targetpath : ""));
623 if (consumedcntp) {
624 *consumedcntp = strlen(dfspath);
626 status = NT_STATUS_PATH_NOT_COVERED;
627 goto out;
630 /* Prepare to test only for '/' components in the given path,
631 * so if a Windows path replace all '\\' characters with '/'.
632 * For a POSIX DFS path we know all separators are already '/'. */
634 canon_dfspath = talloc_strdup(ctx, dfspath);
635 if (!canon_dfspath) {
636 status = NT_STATUS_NO_MEMORY;
637 goto out;
639 if (!pdp->posix_path) {
640 string_replace(canon_dfspath, '\\', '/');
644 * localpath comes out of unix_convert, so it has
645 * no trailing backslash. Make sure that canon_dfspath hasn't either.
646 * Fix for bug #4860 from Jan Martin <Jan.Martin@rwedea.com>.
649 trim_char(canon_dfspath,0,'/');
652 * Redirect if any component in the path is a link.
653 * We do this by walking backwards through the
654 * local path, chopping off the last component
655 * in both the local path and the canonicalized
656 * DFS path. If we hit a DFS link then we're done.
659 p = strrchr_m(smb_fname->base_name, '/');
660 if (consumedcntp) {
661 q = strrchr_m(canon_dfspath, '/');
664 while (p) {
665 *p = '\0';
666 if (q) {
667 *q = '\0';
670 if (is_msdfs_link_internal(ctx, conn,
671 smb_fname->base_name, pp_targetpath,
672 NULL)) {
673 DEBUG(4, ("dfs_path_lookup: Redirecting %s because "
674 "parent %s is dfs link\n", dfspath,
675 smb_fname_str_dbg(smb_fname)));
677 if (consumedcntp) {
678 *consumedcntp = strlen(canon_dfspath);
679 DEBUG(10, ("dfs_path_lookup: Path consumed: %s "
680 "(%d)\n",
681 canon_dfspath,
682 *consumedcntp));
685 status = NT_STATUS_PATH_NOT_COVERED;
686 goto out;
689 /* Step back on the filesystem. */
690 p = strrchr_m(smb_fname->base_name, '/');
692 if (consumedcntp) {
693 /* And in the canonicalized dfs path. */
694 q = strrchr_m(canon_dfspath, '/');
698 status = NT_STATUS_OK;
699 out:
700 TALLOC_FREE(smb_fname);
701 return status;
704 /*****************************************************************
705 Decides if a dfs pathname should be redirected or not.
706 If not, the pathname is converted to a tcon-relative local unix path
708 search_wcard_flag: this flag performs 2 functions both related
709 to searches. See resolve_dfs_path() and parse_dfs_path_XX()
710 for details.
712 This function can return NT_STATUS_OK, meaning use the returned path as-is
713 (mapped into a local path).
714 or NT_STATUS_NOT_COVERED meaning return a DFS redirect, or
715 any other NT_STATUS error which is a genuine error to be
716 returned to the client.
717 *****************************************************************/
719 static NTSTATUS dfs_redirect(TALLOC_CTX *ctx,
720 connection_struct *conn,
721 const char *path_in,
722 bool search_wcard_flag,
723 bool allow_broken_path,
724 char **pp_path_out,
725 bool *ppath_contains_wcard)
727 NTSTATUS status;
728 struct dfs_path *pdp = talloc(ctx, struct dfs_path);
730 if (!pdp) {
731 return NT_STATUS_NO_MEMORY;
734 status = parse_dfs_path(conn, path_in, search_wcard_flag,
735 allow_broken_path, pdp,
736 ppath_contains_wcard);
737 if (!NT_STATUS_IS_OK(status)) {
738 TALLOC_FREE(pdp);
739 return status;
742 if (pdp->reqpath[0] == '\0') {
743 TALLOC_FREE(pdp);
744 *pp_path_out = talloc_strdup(ctx, "");
745 if (!*pp_path_out) {
746 return NT_STATUS_NO_MEMORY;
748 DEBUG(5,("dfs_redirect: self-referral.\n"));
749 return NT_STATUS_OK;
752 /* If dfs pathname for a non-dfs share, convert to tcon-relative
753 path and return OK */
755 if (!lp_msdfs_root(SNUM(conn))) {
756 *pp_path_out = talloc_strdup(ctx, pdp->reqpath);
757 TALLOC_FREE(pdp);
758 if (!*pp_path_out) {
759 return NT_STATUS_NO_MEMORY;
761 return NT_STATUS_OK;
764 /* If it looked like a local path (zero hostname/servicename)
765 * just treat as a tcon-relative path. */
767 if (pdp->hostname[0] == '\0' && pdp->servicename[0] == '\0') {
768 *pp_path_out = talloc_strdup(ctx, pdp->reqpath);
769 TALLOC_FREE(pdp);
770 if (!*pp_path_out) {
771 return NT_STATUS_NO_MEMORY;
773 return NT_STATUS_OK;
776 if (!( strequal(pdp->servicename, lp_servicename(SNUM(conn)))
777 || (strequal(pdp->servicename, HOMES_NAME)
778 && strequal(lp_servicename(SNUM(conn)),
779 conn->session_info->unix_info->sanitized_username) )) ) {
781 /* The given sharename doesn't match this connection. */
782 TALLOC_FREE(pdp);
784 return NT_STATUS_OBJECT_PATH_NOT_FOUND;
787 status = dfs_path_lookup(ctx, conn, path_in, pdp,
788 search_wcard_flag, NULL, NULL);
789 if (!NT_STATUS_IS_OK(status)) {
790 if (NT_STATUS_EQUAL(status, NT_STATUS_PATH_NOT_COVERED)) {
791 DEBUG(3,("dfs_redirect: Redirecting %s\n", path_in));
792 } else {
793 DEBUG(10,("dfs_redirect: dfs_path_lookup "
794 "failed for %s with %s\n",
795 path_in, nt_errstr(status) ));
797 return status;
800 DEBUG(3,("dfs_redirect: Not redirecting %s.\n", path_in));
802 /* Form non-dfs tcon-relative path */
803 *pp_path_out = talloc_strdup(ctx, pdp->reqpath);
804 TALLOC_FREE(pdp);
805 if (!*pp_path_out) {
806 return NT_STATUS_NO_MEMORY;
809 DEBUG(3,("dfs_redirect: Path %s converted to non-dfs path %s\n",
810 path_in,
811 *pp_path_out));
813 return NT_STATUS_OK;
816 /**********************************************************************
817 Return a self referral.
818 **********************************************************************/
820 static NTSTATUS self_ref(TALLOC_CTX *ctx,
821 const char *dfs_path,
822 struct junction_map *jucn,
823 int *consumedcntp,
824 bool *self_referralp)
826 struct referral *ref;
828 *self_referralp = True;
830 jucn->referral_count = 1;
831 if((ref = talloc_zero(ctx, struct referral)) == NULL) {
832 return NT_STATUS_NO_MEMORY;
835 ref->alternate_path = talloc_strdup(ctx, dfs_path);
836 if (!ref->alternate_path) {
837 return NT_STATUS_NO_MEMORY;
839 ref->proximity = 0;
840 ref->ttl = REFERRAL_TTL;
841 jucn->referral_list = ref;
842 *consumedcntp = strlen(dfs_path);
843 return NT_STATUS_OK;
846 /**********************************************************************
847 Gets valid referrals for a dfs path and fills up the
848 junction_map structure.
849 **********************************************************************/
851 NTSTATUS get_referred_path(TALLOC_CTX *ctx,
852 const char *dfs_path,
853 struct smbd_server_connection *sconn,
854 struct junction_map *jucn,
855 int *consumedcntp,
856 bool *self_referralp)
858 struct connection_struct *conn;
859 char *targetpath = NULL;
860 int snum;
861 NTSTATUS status = NT_STATUS_NOT_FOUND;
862 bool dummy;
863 struct dfs_path *pdp = talloc(ctx, struct dfs_path);
864 char *oldpath;
866 if (!pdp) {
867 return NT_STATUS_NO_MEMORY;
870 *self_referralp = False;
872 status = parse_dfs_path(NULL, dfs_path, False, !sconn->using_smb2,
873 pdp, &dummy);
874 if (!NT_STATUS_IS_OK(status)) {
875 return status;
878 jucn->service_name = talloc_strdup(ctx, pdp->servicename);
879 jucn->volume_name = talloc_strdup(ctx, pdp->reqpath);
880 if (!jucn->service_name || !jucn->volume_name) {
881 TALLOC_FREE(pdp);
882 return NT_STATUS_NO_MEMORY;
885 /* Verify the share is a dfs root */
886 snum = lp_servicenumber(jucn->service_name);
887 if(snum < 0) {
888 char *service_name = NULL;
889 if ((snum = find_service(ctx, jucn->service_name, &service_name)) < 0) {
890 return NT_STATUS_NOT_FOUND;
892 if (!service_name) {
893 return NT_STATUS_NO_MEMORY;
895 TALLOC_FREE(jucn->service_name);
896 jucn->service_name = talloc_strdup(ctx, service_name);
897 if (!jucn->service_name) {
898 TALLOC_FREE(pdp);
899 return NT_STATUS_NO_MEMORY;
903 if (!lp_msdfs_root(snum) && (*lp_msdfs_proxy(snum) == '\0')) {
904 DEBUG(3,("get_referred_path: |%s| in dfs path %s is not "
905 "a dfs root.\n",
906 pdp->servicename, dfs_path));
907 TALLOC_FREE(pdp);
908 return NT_STATUS_NOT_FOUND;
912 * Self referrals are tested with a anonymous IPC connection and
913 * a GET_DFS_REFERRAL call to \\server\share. (which means
914 * dp.reqpath[0] points to an empty string). create_conn_struct cd's
915 * into the directory and will fail if it cannot (as the anonymous
916 * user). Cope with this.
919 if (pdp->reqpath[0] == '\0') {
920 char *tmp;
921 struct referral *ref;
923 if (*lp_msdfs_proxy(snum) == '\0') {
924 TALLOC_FREE(pdp);
925 return self_ref(ctx,
926 dfs_path,
927 jucn,
928 consumedcntp,
929 self_referralp);
933 * It's an msdfs proxy share. Redirect to
934 * the configured target share.
937 jucn->referral_count = 1;
938 if ((ref = talloc_zero(ctx, struct referral)) == NULL) {
939 TALLOC_FREE(pdp);
940 return NT_STATUS_NO_MEMORY;
943 if (!(tmp = talloc_strdup(ctx, lp_msdfs_proxy(snum)))) {
944 TALLOC_FREE(pdp);
945 return NT_STATUS_NO_MEMORY;
948 trim_string(tmp, "\\", 0);
950 ref->alternate_path = talloc_asprintf(ctx, "\\%s", tmp);
951 TALLOC_FREE(tmp);
953 if (!ref->alternate_path) {
954 TALLOC_FREE(pdp);
955 return NT_STATUS_NO_MEMORY;
958 if (pdp->reqpath[0] != '\0') {
959 ref->alternate_path = talloc_asprintf_append(
960 ref->alternate_path,
961 "%s",
962 pdp->reqpath);
963 if (!ref->alternate_path) {
964 TALLOC_FREE(pdp);
965 return NT_STATUS_NO_MEMORY;
968 ref->proximity = 0;
969 ref->ttl = REFERRAL_TTL;
970 jucn->referral_list = ref;
971 *consumedcntp = strlen(dfs_path);
972 TALLOC_FREE(pdp);
973 return NT_STATUS_OK;
976 status = create_conn_struct(ctx, sconn, &conn, snum,
977 lp_pathname(snum), NULL, &oldpath);
978 if (!NT_STATUS_IS_OK(status)) {
979 TALLOC_FREE(pdp);
980 return status;
983 /* If this is a DFS path dfs_lookup should return
984 * NT_STATUS_PATH_NOT_COVERED. */
986 status = dfs_path_lookup(ctx, conn, dfs_path, pdp,
987 False, consumedcntp, &targetpath);
989 if (!NT_STATUS_EQUAL(status, NT_STATUS_PATH_NOT_COVERED)) {
990 DEBUG(3,("get_referred_path: No valid referrals for path %s\n",
991 dfs_path));
992 goto err_exit;
995 /* We know this is a valid dfs link. Parse the targetpath. */
996 if (!parse_msdfs_symlink(ctx, targetpath,
997 &jucn->referral_list,
998 &jucn->referral_count)) {
999 DEBUG(3,("get_referred_path: failed to parse symlink "
1000 "target %s\n", targetpath ));
1001 status = NT_STATUS_NOT_FOUND;
1002 goto err_exit;
1005 status = NT_STATUS_OK;
1006 err_exit:
1007 vfs_ChDir(conn, oldpath);
1008 SMB_VFS_DISCONNECT(conn);
1009 conn_free(conn);
1010 TALLOC_FREE(pdp);
1011 return status;
1014 static int setup_ver2_dfs_referral(const char *pathname,
1015 char **ppdata,
1016 struct junction_map *junction,
1017 bool self_referral)
1019 char* pdata = *ppdata;
1021 smb_ucs2_t *uni_requestedpath = NULL;
1022 int uni_reqpathoffset1,uni_reqpathoffset2;
1023 int uni_curroffset;
1024 int requestedpathlen=0;
1025 int offset;
1026 int reply_size = 0;
1027 int i=0;
1029 DEBUG(10,("Setting up version2 referral\nRequested path:\n"));
1031 requestedpathlen = rpcstr_push_talloc(talloc_tos(),
1032 &uni_requestedpath, pathname);
1033 if (uni_requestedpath == NULL || requestedpathlen == 0) {
1034 return -1;
1037 if (DEBUGLVL(10)) {
1038 dump_data(0, (unsigned char *)uni_requestedpath,
1039 requestedpathlen);
1042 DEBUG(10,("ref count = %u\n",junction->referral_count));
1044 uni_reqpathoffset1 = REFERRAL_HEADER_SIZE +
1045 VERSION2_REFERRAL_SIZE * junction->referral_count;
1047 uni_reqpathoffset2 = uni_reqpathoffset1 + requestedpathlen;
1049 uni_curroffset = uni_reqpathoffset2 + requestedpathlen;
1051 reply_size = REFERRAL_HEADER_SIZE +
1052 VERSION2_REFERRAL_SIZE*junction->referral_count +
1053 2 * requestedpathlen;
1054 DEBUG(10,("reply_size: %u\n",reply_size));
1056 /* add up the unicode lengths of all the referral paths */
1057 for(i=0;i<junction->referral_count;i++) {
1058 DEBUG(10,("referral %u : %s\n",
1060 junction->referral_list[i].alternate_path));
1061 reply_size +=
1062 (strlen(junction->referral_list[i].alternate_path)+1)*2;
1065 DEBUG(10,("reply_size = %u\n",reply_size));
1066 /* add the unexplained 0x16 bytes */
1067 reply_size += 0x16;
1069 pdata = (char *)SMB_REALLOC(pdata,reply_size);
1070 if(pdata == NULL) {
1071 DEBUG(0,("Realloc failed!\n"));
1072 return -1;
1074 *ppdata = pdata;
1076 /* copy in the dfs requested paths.. required for offset calculations */
1077 memcpy(pdata+uni_reqpathoffset1,uni_requestedpath,requestedpathlen);
1078 memcpy(pdata+uni_reqpathoffset2,uni_requestedpath,requestedpathlen);
1080 /* create the header */
1081 SSVAL(pdata,0,requestedpathlen - 2); /* UCS2 of path consumed minus
1082 2 byte null */
1083 /* number of referral in this pkt */
1084 SSVAL(pdata,2,junction->referral_count);
1085 if(self_referral) {
1086 SIVAL(pdata,4,DFSREF_REFERRAL_SERVER | DFSREF_STORAGE_SERVER);
1087 } else {
1088 SIVAL(pdata,4,DFSREF_STORAGE_SERVER);
1091 offset = 8;
1092 /* add the referral elements */
1093 for(i=0;i<junction->referral_count;i++) {
1094 struct referral* ref = &junction->referral_list[i];
1095 int unilen;
1097 SSVAL(pdata,offset,2); /* version 2 */
1098 SSVAL(pdata,offset+2,VERSION2_REFERRAL_SIZE);
1099 if(self_referral) {
1100 SSVAL(pdata,offset+4,1);
1101 } else {
1102 SSVAL(pdata,offset+4,0);
1105 /* ref_flags :use path_consumed bytes? */
1106 SSVAL(pdata,offset+6,0);
1107 SIVAL(pdata,offset+8,ref->proximity);
1108 SIVAL(pdata,offset+12,ref->ttl);
1110 SSVAL(pdata,offset+16,uni_reqpathoffset1-offset);
1111 SSVAL(pdata,offset+18,uni_reqpathoffset2-offset);
1112 /* copy referred path into current offset */
1113 unilen = rpcstr_push(pdata+uni_curroffset,
1114 ref->alternate_path,
1115 reply_size - uni_curroffset,
1116 STR_UNICODE);
1118 SSVAL(pdata,offset+20,uni_curroffset-offset);
1120 uni_curroffset += unilen;
1121 offset += VERSION2_REFERRAL_SIZE;
1123 /* add in the unexplained 22 (0x16) bytes at the end */
1124 memset(pdata+uni_curroffset,'\0',0x16);
1125 return reply_size;
1128 static int setup_ver3_dfs_referral(const char *pathname,
1129 char **ppdata,
1130 struct junction_map *junction,
1131 bool self_referral)
1133 char *pdata = *ppdata;
1135 smb_ucs2_t *uni_reqpath = NULL;
1136 int uni_reqpathoffset1, uni_reqpathoffset2;
1137 int uni_curroffset;
1138 int reply_size = 0;
1140 int reqpathlen = 0;
1141 int offset,i=0;
1143 DEBUG(10,("setting up version3 referral\n"));
1145 reqpathlen = rpcstr_push_talloc(talloc_tos(), &uni_reqpath, pathname);
1146 if (uni_reqpath == NULL || reqpathlen == 0) {
1147 return -1;
1150 if (DEBUGLVL(10)) {
1151 dump_data(0, (unsigned char *)uni_reqpath,
1152 reqpathlen);
1155 uni_reqpathoffset1 = REFERRAL_HEADER_SIZE +
1156 VERSION3_REFERRAL_SIZE * junction->referral_count;
1157 uni_reqpathoffset2 = uni_reqpathoffset1 + reqpathlen;
1158 reply_size = uni_curroffset = uni_reqpathoffset2 + reqpathlen;
1160 for(i=0;i<junction->referral_count;i++) {
1161 DEBUG(10,("referral %u : %s\n",
1163 junction->referral_list[i].alternate_path));
1164 reply_size +=
1165 (strlen(junction->referral_list[i].alternate_path)+1)*2;
1168 pdata = (char *)SMB_REALLOC(pdata,reply_size);
1169 if(pdata == NULL) {
1170 DEBUG(0,("version3 referral setup:"
1171 "malloc failed for Realloc!\n"));
1172 return -1;
1174 *ppdata = pdata;
1176 /* create the header */
1177 SSVAL(pdata,0,reqpathlen - 2); /* UCS2 of path consumed minus
1178 2 byte null */
1179 SSVAL(pdata,2,junction->referral_count); /* number of referral */
1180 if(self_referral) {
1181 SIVAL(pdata,4,DFSREF_REFERRAL_SERVER | DFSREF_STORAGE_SERVER);
1182 } else {
1183 SIVAL(pdata,4,DFSREF_STORAGE_SERVER);
1186 /* copy in the reqpaths */
1187 memcpy(pdata+uni_reqpathoffset1,uni_reqpath,reqpathlen);
1188 memcpy(pdata+uni_reqpathoffset2,uni_reqpath,reqpathlen);
1190 offset = 8;
1191 for(i=0;i<junction->referral_count;i++) {
1192 struct referral* ref = &(junction->referral_list[i]);
1193 int unilen;
1195 SSVAL(pdata,offset,3); /* version 3 */
1196 SSVAL(pdata,offset+2,VERSION3_REFERRAL_SIZE);
1197 if(self_referral) {
1198 SSVAL(pdata,offset+4,1);
1199 } else {
1200 SSVAL(pdata,offset+4,0);
1203 /* ref_flags :use path_consumed bytes? */
1204 SSVAL(pdata,offset+6,0);
1205 SIVAL(pdata,offset+8,ref->ttl);
1207 SSVAL(pdata,offset+12,uni_reqpathoffset1-offset);
1208 SSVAL(pdata,offset+14,uni_reqpathoffset2-offset);
1209 /* copy referred path into current offset */
1210 unilen = rpcstr_push(pdata+uni_curroffset,ref->alternate_path,
1211 reply_size - uni_curroffset,
1212 STR_UNICODE | STR_TERMINATE);
1213 SSVAL(pdata,offset+16,uni_curroffset-offset);
1214 /* copy 0x10 bytes of 00's in the ServiceSite GUID */
1215 memset(pdata+offset+18,'\0',16);
1217 uni_curroffset += unilen;
1218 offset += VERSION3_REFERRAL_SIZE;
1220 return reply_size;
1223 /******************************************************************
1224 Set up the DFS referral for the dfs pathname. This call returns
1225 the amount of the path covered by this server, and where the
1226 client should be redirected to. This is the meat of the
1227 TRANS2_GET_DFS_REFERRAL call.
1228 ******************************************************************/
1230 int setup_dfs_referral(connection_struct *orig_conn,
1231 const char *dfs_path,
1232 int max_referral_level,
1233 char **ppdata, NTSTATUS *pstatus)
1235 struct junction_map *junction = NULL;
1236 int consumedcnt = 0;
1237 bool self_referral = False;
1238 int reply_size = 0;
1239 char *pathnamep = NULL;
1240 char *local_dfs_path = NULL;
1241 TALLOC_CTX *ctx;
1243 if (!(ctx=talloc_init("setup_dfs_referral"))) {
1244 *pstatus = NT_STATUS_NO_MEMORY;
1245 return -1;
1248 /* get the junction entry */
1249 if (!dfs_path) {
1250 talloc_destroy(ctx);
1251 *pstatus = NT_STATUS_NOT_FOUND;
1252 return -1;
1256 * Trim pathname sent by client so it begins with only one backslash.
1257 * Two backslashes confuse some dfs clients
1260 local_dfs_path = talloc_strdup(ctx,dfs_path);
1261 if (!local_dfs_path) {
1262 *pstatus = NT_STATUS_NO_MEMORY;
1263 talloc_destroy(ctx);
1264 return -1;
1266 pathnamep = local_dfs_path;
1267 while (IS_DIRECTORY_SEP(pathnamep[0]) &&
1268 IS_DIRECTORY_SEP(pathnamep[1])) {
1269 pathnamep++;
1272 junction = talloc_zero(ctx, struct junction_map);
1273 if (!junction) {
1274 *pstatus = NT_STATUS_NO_MEMORY;
1275 talloc_destroy(ctx);
1276 return -1;
1279 /* The following call can change cwd. */
1280 *pstatus = get_referred_path(ctx, pathnamep, orig_conn->sconn,
1281 junction, &consumedcnt, &self_referral);
1282 if (!NT_STATUS_IS_OK(*pstatus)) {
1283 vfs_ChDir(orig_conn,orig_conn->connectpath);
1284 talloc_destroy(ctx);
1285 return -1;
1287 vfs_ChDir(orig_conn,orig_conn->connectpath);
1289 if (!self_referral) {
1290 pathnamep[consumedcnt] = '\0';
1292 if( DEBUGLVL( 3 ) ) {
1293 int i=0;
1294 dbgtext("setup_dfs_referral: Path %s to "
1295 "alternate path(s):",
1296 pathnamep);
1297 for(i=0;i<junction->referral_count;i++)
1298 dbgtext(" %s",
1299 junction->referral_list[i].alternate_path);
1300 dbgtext(".\n");
1304 /* create the referral depeding on version */
1305 DEBUG(10,("max_referral_level :%d\n",max_referral_level));
1307 if (max_referral_level < 2) {
1308 max_referral_level = 2;
1310 if (max_referral_level > 3) {
1311 max_referral_level = 3;
1314 switch(max_referral_level) {
1315 case 2:
1316 reply_size = setup_ver2_dfs_referral(pathnamep,
1317 ppdata, junction,
1318 self_referral);
1319 break;
1320 case 3:
1321 reply_size = setup_ver3_dfs_referral(pathnamep, ppdata,
1322 junction, self_referral);
1323 break;
1324 default:
1325 DEBUG(0,("setup_dfs_referral: Invalid dfs referral "
1326 "version: %d\n",
1327 max_referral_level));
1328 talloc_destroy(ctx);
1329 *pstatus = NT_STATUS_INVALID_LEVEL;
1330 return -1;
1333 if (DEBUGLVL(10)) {
1334 DEBUGADD(0,("DFS Referral pdata:\n"));
1335 dump_data(0,(uint8 *)*ppdata,reply_size);
1338 talloc_destroy(ctx);
1339 *pstatus = NT_STATUS_OK;
1340 return reply_size;
1343 /**********************************************************************
1344 The following functions are called by the NETDFS RPC pipe functions
1345 **********************************************************************/
1347 /*********************************************************************
1348 Creates a junction structure from a DFS pathname
1349 **********************************************************************/
1351 bool create_junction(TALLOC_CTX *ctx,
1352 const char *dfs_path,
1353 bool allow_broken_path,
1354 struct junction_map *jucn)
1356 int snum;
1357 bool dummy;
1358 struct dfs_path *pdp = talloc(ctx,struct dfs_path);
1359 NTSTATUS status;
1361 if (!pdp) {
1362 return False;
1364 status = parse_dfs_path(NULL, dfs_path, False, allow_broken_path,
1365 pdp, &dummy);
1366 if (!NT_STATUS_IS_OK(status)) {
1367 return False;
1370 /* check if path is dfs : validate first token */
1371 if (!is_myname_or_ipaddr(pdp->hostname)) {
1372 DEBUG(4,("create_junction: Invalid hostname %s "
1373 "in dfs path %s\n",
1374 pdp->hostname, dfs_path));
1375 TALLOC_FREE(pdp);
1376 return False;
1379 /* Check for a non-DFS share */
1380 snum = lp_servicenumber(pdp->servicename);
1382 if(snum < 0 || !lp_msdfs_root(snum)) {
1383 DEBUG(4,("create_junction: %s is not an msdfs root.\n",
1384 pdp->servicename));
1385 TALLOC_FREE(pdp);
1386 return False;
1389 jucn->service_name = talloc_strdup(ctx, pdp->servicename);
1390 jucn->volume_name = talloc_strdup(ctx, pdp->reqpath);
1391 jucn->comment = talloc_strdup(ctx, lp_comment(snum));
1393 TALLOC_FREE(pdp);
1394 if (!jucn->service_name || !jucn->volume_name || ! jucn->comment) {
1395 return False;
1397 return True;
1400 /**********************************************************************
1401 Forms a valid Unix pathname from the junction
1402 **********************************************************************/
1404 static bool junction_to_local_path(const struct junction_map *jucn,
1405 char **pp_path_out,
1406 connection_struct **conn_out,
1407 char **oldpath)
1409 int snum;
1410 NTSTATUS status;
1412 snum = lp_servicenumber(jucn->service_name);
1413 if(snum < 0) {
1414 return False;
1416 status = create_conn_struct(talloc_tos(), smbd_server_conn, conn_out,
1417 snum, lp_pathname(snum), NULL, oldpath);
1418 if (!NT_STATUS_IS_OK(status)) {
1419 return False;
1422 *pp_path_out = talloc_asprintf(*conn_out,
1423 "%s/%s",
1424 lp_pathname(snum),
1425 jucn->volume_name);
1426 if (!*pp_path_out) {
1427 vfs_ChDir(*conn_out, *oldpath);
1428 SMB_VFS_DISCONNECT(*conn_out);
1429 conn_free(*conn_out);
1430 return False;
1432 return True;
1435 bool create_msdfs_link(const struct junction_map *jucn)
1437 char *path = NULL;
1438 char *cwd;
1439 char *msdfs_link = NULL;
1440 connection_struct *conn;
1441 int i=0;
1442 bool insert_comma = False;
1443 bool ret = False;
1445 if(!junction_to_local_path(jucn, &path, &conn, &cwd)) {
1446 return False;
1449 /* Form the msdfs_link contents */
1450 msdfs_link = talloc_strdup(conn, "msdfs:");
1451 if (!msdfs_link) {
1452 goto out;
1454 for(i=0; i<jucn->referral_count; i++) {
1455 char *refpath = jucn->referral_list[i].alternate_path;
1457 /* Alternate paths always use Windows separators. */
1458 trim_char(refpath, '\\', '\\');
1459 if(*refpath == '\0') {
1460 if (i == 0) {
1461 insert_comma = False;
1463 continue;
1465 if (i > 0 && insert_comma) {
1466 msdfs_link = talloc_asprintf_append_buffer(msdfs_link,
1467 ",%s",
1468 refpath);
1469 } else {
1470 msdfs_link = talloc_asprintf_append_buffer(msdfs_link,
1471 "%s",
1472 refpath);
1475 if (!msdfs_link) {
1476 goto out;
1478 if (!insert_comma) {
1479 insert_comma = True;
1483 DEBUG(5,("create_msdfs_link: Creating new msdfs link: %s -> %s\n",
1484 path, msdfs_link));
1486 if(SMB_VFS_SYMLINK(conn, msdfs_link, path) < 0) {
1487 if (errno == EEXIST) {
1488 struct smb_filename *smb_fname = NULL;
1489 NTSTATUS status;
1491 status = create_synthetic_smb_fname(talloc_tos(), path,
1492 NULL, NULL,
1493 &smb_fname);
1494 if (!NT_STATUS_IS_OK(status)) {
1495 errno = map_errno_from_nt_status(status);
1496 goto out;
1499 if(SMB_VFS_UNLINK(conn, smb_fname)!=0) {
1500 TALLOC_FREE(smb_fname);
1501 goto out;
1503 TALLOC_FREE(smb_fname);
1505 if (SMB_VFS_SYMLINK(conn, msdfs_link, path) < 0) {
1506 DEBUG(1,("create_msdfs_link: symlink failed "
1507 "%s -> %s\nError: %s\n",
1508 path, msdfs_link, strerror(errno)));
1509 goto out;
1513 ret = True;
1515 out:
1516 vfs_ChDir(conn, cwd);
1517 SMB_VFS_DISCONNECT(conn);
1518 conn_free(conn);
1519 return ret;
1522 bool remove_msdfs_link(const struct junction_map *jucn)
1524 char *path = NULL;
1525 char *cwd;
1526 connection_struct *conn;
1527 bool ret = False;
1528 struct smb_filename *smb_fname = NULL;
1529 NTSTATUS status;
1531 if (!junction_to_local_path(jucn, &path, &conn, &cwd)) {
1532 return false;
1535 status = create_synthetic_smb_fname(talloc_tos(), path,
1536 NULL, NULL,
1537 &smb_fname);
1538 if (!NT_STATUS_IS_OK(status)) {
1539 errno = map_errno_from_nt_status(status);
1540 return false;
1543 if( SMB_VFS_UNLINK(conn, smb_fname) == 0 ) {
1544 ret = True;
1547 TALLOC_FREE(smb_fname);
1548 vfs_ChDir(conn, cwd);
1549 SMB_VFS_DISCONNECT(conn);
1550 conn_free(conn);
1551 return ret;
1554 /*********************************************************************
1555 Return the number of DFS links at the root of this share.
1556 *********************************************************************/
1558 static int count_dfs_links(TALLOC_CTX *ctx, int snum)
1560 size_t cnt = 0;
1561 SMB_STRUCT_DIR *dirp = NULL;
1562 const char *dname = NULL;
1563 char *talloced = NULL;
1564 const char *connect_path = lp_pathname(snum);
1565 const char *msdfs_proxy = lp_msdfs_proxy(snum);
1566 connection_struct *conn;
1567 NTSTATUS status;
1568 char *cwd;
1570 if(*connect_path == '\0') {
1571 return 0;
1575 * Fake up a connection struct for the VFS layer.
1578 status = create_conn_struct(talloc_tos(), smbd_server_conn, &conn,
1579 snum, connect_path, NULL, &cwd);
1580 if (!NT_STATUS_IS_OK(status)) {
1581 DEBUG(3, ("create_conn_struct failed: %s\n",
1582 nt_errstr(status)));
1583 return 0;
1586 /* Count a link for the msdfs root - convention */
1587 cnt = 1;
1589 /* No more links if this is an msdfs proxy. */
1590 if (*msdfs_proxy != '\0') {
1591 goto out;
1594 /* Now enumerate all dfs links */
1595 dirp = SMB_VFS_OPENDIR(conn, ".", NULL, 0);
1596 if(!dirp) {
1597 goto out;
1600 while ((dname = vfs_readdirname(conn, dirp, NULL, &talloced))
1601 != NULL) {
1602 if (is_msdfs_link(conn,
1603 dname,
1604 NULL)) {
1605 cnt++;
1607 TALLOC_FREE(talloced);
1610 SMB_VFS_CLOSEDIR(conn,dirp);
1612 out:
1613 vfs_ChDir(conn, cwd);
1614 SMB_VFS_DISCONNECT(conn);
1615 conn_free(conn);
1616 return cnt;
1619 /*********************************************************************
1620 *********************************************************************/
1622 static int form_junctions(TALLOC_CTX *ctx,
1623 int snum,
1624 struct junction_map *jucn,
1625 size_t jn_remain)
1627 size_t cnt = 0;
1628 SMB_STRUCT_DIR *dirp = NULL;
1629 const char *dname = NULL;
1630 char *talloced = NULL;
1631 const char *connect_path = lp_pathname(snum);
1632 char *service_name = lp_servicename(snum);
1633 const char *msdfs_proxy = lp_msdfs_proxy(snum);
1634 connection_struct *conn;
1635 struct referral *ref = NULL;
1636 char *cwd;
1637 NTSTATUS status;
1639 if (jn_remain == 0) {
1640 return 0;
1643 if(*connect_path == '\0') {
1644 return 0;
1648 * Fake up a connection struct for the VFS layer.
1651 status = create_conn_struct(ctx, smbd_server_conn, &conn, snum, connect_path, NULL,
1652 &cwd);
1653 if (!NT_STATUS_IS_OK(status)) {
1654 DEBUG(3, ("create_conn_struct failed: %s\n",
1655 nt_errstr(status)));
1656 return 0;
1659 /* form a junction for the msdfs root - convention
1660 DO NOT REMOVE THIS: NT clients will not work with us
1661 if this is not present
1663 jucn[cnt].service_name = talloc_strdup(ctx,service_name);
1664 jucn[cnt].volume_name = talloc_strdup(ctx, "");
1665 if (!jucn[cnt].service_name || !jucn[cnt].volume_name) {
1666 goto out;
1668 jucn[cnt].comment = "";
1669 jucn[cnt].referral_count = 1;
1671 ref = jucn[cnt].referral_list = talloc_zero(ctx, struct referral);
1672 if (jucn[cnt].referral_list == NULL) {
1673 goto out;
1676 ref->proximity = 0;
1677 ref->ttl = REFERRAL_TTL;
1678 if (*msdfs_proxy != '\0') {
1679 ref->alternate_path = talloc_strdup(ctx,
1680 msdfs_proxy);
1681 } else {
1682 ref->alternate_path = talloc_asprintf(ctx,
1683 "\\\\%s\\%s",
1684 get_local_machine_name(),
1685 service_name);
1688 if (!ref->alternate_path) {
1689 goto out;
1691 cnt++;
1693 /* Don't enumerate if we're an msdfs proxy. */
1694 if (*msdfs_proxy != '\0') {
1695 goto out;
1698 /* Now enumerate all dfs links */
1699 dirp = SMB_VFS_OPENDIR(conn, ".", NULL, 0);
1700 if(!dirp) {
1701 goto out;
1704 while ((dname = vfs_readdirname(conn, dirp, NULL, &talloced))
1705 != NULL) {
1706 char *link_target = NULL;
1707 if (cnt >= jn_remain) {
1708 DEBUG(2, ("form_junctions: ran out of MSDFS "
1709 "junction slots"));
1710 TALLOC_FREE(talloced);
1711 goto out;
1713 if (is_msdfs_link_internal(ctx,
1714 conn,
1715 dname, &link_target,
1716 NULL)) {
1717 if (parse_msdfs_symlink(ctx,
1718 link_target,
1719 &jucn[cnt].referral_list,
1720 &jucn[cnt].referral_count)) {
1722 jucn[cnt].service_name = talloc_strdup(ctx,
1723 service_name);
1724 jucn[cnt].volume_name = talloc_strdup(ctx,
1725 dname);
1726 if (!jucn[cnt].service_name ||
1727 !jucn[cnt].volume_name) {
1728 TALLOC_FREE(talloced);
1729 goto out;
1731 jucn[cnt].comment = "";
1732 cnt++;
1734 TALLOC_FREE(link_target);
1736 TALLOC_FREE(talloced);
1739 out:
1741 if (dirp) {
1742 SMB_VFS_CLOSEDIR(conn,dirp);
1745 vfs_ChDir(conn, cwd);
1746 conn_free(conn);
1747 return cnt;
1750 struct junction_map *enum_msdfs_links(struct smbd_server_connection *sconn,
1751 TALLOC_CTX *ctx, size_t *p_num_jn)
1753 struct junction_map *jn = NULL;
1754 int i=0;
1755 size_t jn_count = 0;
1756 int sharecount = 0;
1758 *p_num_jn = 0;
1759 if(!lp_host_msdfs()) {
1760 return NULL;
1763 /* Ensure all the usershares are loaded. */
1764 become_root();
1765 load_registry_shares();
1766 sharecount = load_usershare_shares(sconn);
1767 unbecome_root();
1769 for(i=0;i < sharecount;i++) {
1770 if(lp_msdfs_root(i)) {
1771 jn_count += count_dfs_links(ctx, i);
1774 if (jn_count == 0) {
1775 return NULL;
1777 jn = talloc_array(ctx, struct junction_map, jn_count);
1778 if (!jn) {
1779 return NULL;
1781 for(i=0; i < sharecount; i++) {
1782 if (*p_num_jn >= jn_count) {
1783 break;
1785 if(lp_msdfs_root(i)) {
1786 *p_num_jn += form_junctions(ctx, i,
1787 &jn[*p_num_jn],
1788 jn_count - *p_num_jn);
1791 return jn;
1794 /******************************************************************************
1795 Core function to resolve a dfs pathname possibly containing a wildcard. If
1796 ppath_contains_wcard != NULL, it will be set to true if a wildcard is
1797 detected during dfs resolution.
1798 ******************************************************************************/
1800 NTSTATUS resolve_dfspath_wcard(TALLOC_CTX *ctx,
1801 connection_struct *conn,
1802 bool dfs_pathnames,
1803 const char *name_in,
1804 bool allow_wcards,
1805 char **pp_name_out,
1806 bool *ppath_contains_wcard)
1808 bool path_contains_wcard;
1809 NTSTATUS status = NT_STATUS_OK;
1811 if (dfs_pathnames) {
1812 status = dfs_redirect(ctx,
1813 conn,
1814 name_in,
1815 allow_wcards,
1816 !smbd_server_conn->using_smb2,
1817 pp_name_out,
1818 &path_contains_wcard);
1820 if (NT_STATUS_IS_OK(status) && ppath_contains_wcard != NULL) {
1821 *ppath_contains_wcard = path_contains_wcard;
1823 } else {
1825 * Cheat and just return a copy of the in ptr.
1826 * Once srvstr_get_path() uses talloc it'll
1827 * be a talloced ptr anyway.
1829 *pp_name_out = discard_const_p(char, name_in);
1831 return status;