Fix bug 6382: Case insensitive access to DFS links broken
[Samba.git] / source / smbd / msdfs.c
blob8f18f77eeb99fcf7b333977857d36fffc63bc877
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"
26 extern uint32 global_client_caps;
28 /**********************************************************************
29 Parse a DFS pathname of the form \hostname\service\reqpath
30 into the dfs_path structure.
31 If POSIX pathnames is true, the pathname may also be of the
32 form /hostname/service/reqpath.
33 We cope with either here.
35 Unfortunately, due to broken clients who might set the
36 SVAL(inbuf,smb_flg2) & FLAGS2_DFS_PATHNAMES bit and then
37 send a local path, we have to cope with that too....
39 If conn != NULL then ensure the provided service is
40 the one pointed to by the connection.
42 This version does everything using pointers within one copy of the
43 pathname string, talloced on the struct dfs_path pointer (which
44 must be talloced). This may be too clever to live....
45 JRA.
46 **********************************************************************/
48 static NTSTATUS parse_dfs_path(connection_struct *conn,
49 const char *pathname,
50 bool allow_wcards,
51 struct dfs_path *pdp, /* MUST BE TALLOCED */
52 bool *ppath_contains_wcard)
54 char *pathname_local;
55 char *p,*temp;
56 char *servicename;
57 char *eos_ptr;
58 NTSTATUS status = NT_STATUS_OK;
59 char sepchar;
61 ZERO_STRUCTP(pdp);
64 * This is the only talloc we should need to do
65 * on the struct dfs_path. All the pointers inside
66 * it should point to offsets within this string.
69 pathname_local = talloc_strdup(pdp, pathname);
70 if (!pathname_local) {
71 return NT_STATUS_NO_MEMORY;
73 /* Get a pointer to the terminating '\0' */
74 eos_ptr = &pathname_local[strlen(pathname_local)];
75 p = temp = pathname_local;
77 pdp->posix_path = (lp_posix_pathnames() && *pathname == '/');
79 sepchar = pdp->posix_path ? '/' : '\\';
81 if (*pathname != sepchar) {
82 DEBUG(10,("parse_dfs_path: path %s doesn't start with %c\n",
83 pathname, sepchar ));
85 * Possibly client sent a local path by mistake.
86 * Try and convert to a local path.
89 pdp->hostname = eos_ptr; /* "" */
90 pdp->servicename = eos_ptr; /* "" */
92 /* We've got no info about separators. */
93 pdp->posix_path = lp_posix_pathnames();
94 p = temp;
95 DEBUG(10,("parse_dfs_path: trying to convert %s to a "
96 "local path\n",
97 temp));
98 goto local_path;
102 * Safe to use on talloc'ed string as it only shrinks.
103 * It also doesn't affect the eos_ptr.
105 trim_char(temp,sepchar,sepchar);
107 DEBUG(10,("parse_dfs_path: temp = |%s| after trimming %c's\n",
108 temp, sepchar));
110 /* Now tokenize. */
111 /* Parse out hostname. */
112 p = strchr_m(temp,sepchar);
113 if(p == NULL) {
114 DEBUG(10,("parse_dfs_path: can't parse hostname from path %s\n",
115 temp));
117 * Possibly client sent a local path by mistake.
118 * Try and convert to a local path.
121 pdp->hostname = eos_ptr; /* "" */
122 pdp->servicename = eos_ptr; /* "" */
124 p = temp;
125 DEBUG(10,("parse_dfs_path: trying to convert %s "
126 "to a local path\n",
127 temp));
128 goto local_path;
130 *p = '\0';
131 pdp->hostname = temp;
133 DEBUG(10,("parse_dfs_path: hostname: %s\n",pdp->hostname));
135 /* Parse out servicename. */
136 servicename = p+1;
137 p = strchr_m(servicename,sepchar);
138 if (p) {
139 *p = '\0';
142 /* Is this really our servicename ? */
143 if (conn && !( strequal(servicename, lp_servicename(SNUM(conn)))
144 || (strequal(servicename, HOMES_NAME)
145 && strequal(lp_servicename(SNUM(conn)),
146 get_current_username()) )) ) {
147 DEBUG(10,("parse_dfs_path: %s is not our servicename\n",
148 servicename));
151 * Possibly client sent a local path by mistake.
152 * Try and convert to a local path.
155 pdp->hostname = eos_ptr; /* "" */
156 pdp->servicename = eos_ptr; /* "" */
158 /* Repair the path - replace the sepchar's
159 we nulled out */
160 servicename--;
161 *servicename = sepchar;
162 if (p) {
163 *p = sepchar;
166 p = temp;
167 DEBUG(10,("parse_dfs_path: trying to convert %s "
168 "to a local path\n",
169 temp));
170 goto local_path;
173 pdp->servicename = servicename;
175 DEBUG(10,("parse_dfs_path: servicename: %s\n",pdp->servicename));
177 if(p == NULL) {
178 /* Client sent self referral \server\share. */
179 pdp->reqpath = eos_ptr; /* "" */
180 return NT_STATUS_OK;
183 p++;
185 local_path:
187 *ppath_contains_wcard = False;
189 pdp->reqpath = p;
191 /* Rest is reqpath. */
192 if (pdp->posix_path) {
193 status = check_path_syntax_posix(pdp->reqpath);
194 } else {
195 if (allow_wcards) {
196 status = check_path_syntax_wcard(pdp->reqpath,
197 ppath_contains_wcard);
198 } else {
199 status = check_path_syntax(pdp->reqpath);
203 if (!NT_STATUS_IS_OK(status)) {
204 DEBUG(10,("parse_dfs_path: '%s' failed with %s\n",
205 p, nt_errstr(status) ));
206 return status;
209 DEBUG(10,("parse_dfs_path: rest of the path: %s\n",pdp->reqpath));
210 return NT_STATUS_OK;
213 /********************************************************
214 Fake up a connection struct for the VFS layer.
215 Note this CHANGES CWD !!!! JRA.
216 *********************************************************/
218 NTSTATUS create_conn_struct(TALLOC_CTX *ctx,
219 connection_struct **pconn,
220 int snum,
221 const char *path,
222 char **poldcwd)
224 connection_struct *conn;
225 char *connpath;
226 char *oldcwd;
228 conn = TALLOC_ZERO_P(ctx, connection_struct);
229 if (conn == NULL) {
230 return NT_STATUS_NO_MEMORY;
233 connpath = talloc_strdup(conn, path);
234 if (!connpath) {
235 TALLOC_FREE(conn);
236 return NT_STATUS_NO_MEMORY;
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;
247 /* needed for smbd_vfs_init() */
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;
255 conn->params->service = snum;
257 set_conn_connectpath(conn, connpath);
259 if (!smbd_vfs_init(conn)) {
260 NTSTATUS status = map_nt_error_from_unix(errno);
261 DEBUG(0,("create_conn_struct: smbd_vfs_init failed.\n"));
262 conn_free_internal(conn);
263 return status;
266 conn->fs_capabilities = SMB_VFS_FS_CAPABILITIES(conn);
269 * Windows seems to insist on doing trans2getdfsreferral() calls on
270 * the IPC$ share as the anonymous user. If we try to chdir as that
271 * user we will fail.... WTF ? JRA.
274 oldcwd = vfs_GetWd(ctx, conn);
275 if (oldcwd == NULL) {
276 NTSTATUS status = map_nt_error_from_unix(errno);
277 DEBUG(3, ("vfs_GetWd failed: %s\n", strerror(errno)));
278 conn_free_internal(conn);
279 return status;
282 if (vfs_ChDir(conn,conn->connectpath) != 0) {
283 NTSTATUS status = map_nt_error_from_unix(errno);
284 DEBUG(3,("create_conn_struct: Can't ChDir to new conn path %s. "
285 "Error was %s\n",
286 conn->connectpath, strerror(errno) ));
287 conn_free_internal(conn);
288 return status;
291 *pconn = conn;
292 *poldcwd = oldcwd;
294 return NT_STATUS_OK;
297 /**********************************************************************
298 Parse the contents of a symlink to verify if it is an msdfs referral
299 A valid referral is of the form:
301 msdfs:server1\share1,server2\share2
302 msdfs:server1\share1\pathname,server2\share2\pathname
303 msdfs:server1/share1,server2/share2
304 msdfs:server1/share1/pathname,server2/share2/pathname.
306 Note that the alternate paths returned here must be of the canonicalized
307 form:
309 \server\share or
310 \server\share\path\to\file,
312 even in posix path mode. This is because we have no knowledge if the
313 server we're referring to understands posix paths.
314 **********************************************************************/
316 static bool parse_msdfs_symlink(TALLOC_CTX *ctx,
317 const char *target,
318 struct referral **preflist,
319 int *refcount)
321 char *temp = NULL;
322 char *prot;
323 char **alt_path = NULL;
324 int count = 0, i;
325 struct referral *reflist;
326 char *saveptr;
328 temp = talloc_strdup(ctx, target);
329 if (!temp) {
330 return False;
332 prot = strtok_r(temp, ":", &saveptr);
333 if (!prot) {
334 DEBUG(0,("parse_msdfs_symlink: invalid path !\n"));
335 return False;
338 alt_path = TALLOC_ARRAY(ctx, char *, MAX_REFERRAL_COUNT);
339 if (!alt_path) {
340 return False;
343 /* parse out the alternate paths */
344 while((count<MAX_REFERRAL_COUNT) &&
345 ((alt_path[count] = strtok_r(NULL, ",", &saveptr)) != NULL)) {
346 count++;
349 DEBUG(10,("parse_msdfs_symlink: count=%d\n", count));
351 if (count) {
352 reflist = *preflist = TALLOC_ZERO_ARRAY(ctx,
353 struct referral, count);
354 if(reflist == NULL) {
355 TALLOC_FREE(alt_path);
356 return False;
358 } else {
359 reflist = *preflist = NULL;
362 for(i=0;i<count;i++) {
363 char *p;
365 /* Canonicalize link target.
366 * Replace all /'s in the path by a \ */
367 string_replace(alt_path[i], '/', '\\');
369 /* Remove leading '\\'s */
370 p = alt_path[i];
371 while (*p && (*p == '\\')) {
372 p++;
375 reflist[i].alternate_path = talloc_asprintf(ctx,
376 "\\%s",
378 if (!reflist[i].alternate_path) {
379 return False;
382 reflist[i].proximity = 0;
383 reflist[i].ttl = REFERRAL_TTL;
384 DEBUG(10, ("parse_msdfs_symlink: Created alt path: %s\n",
385 reflist[i].alternate_path));
388 *refcount = count;
390 TALLOC_FREE(alt_path);
391 return True;
394 /**********************************************************************
395 Returns true if the unix path is a valid msdfs symlink and also
396 returns the target string from inside the link.
397 **********************************************************************/
399 static bool is_msdfs_link_internal(TALLOC_CTX *ctx,
400 connection_struct *conn,
401 const char *path,
402 char **pp_link_target,
403 SMB_STRUCT_STAT *sbufp)
405 SMB_STRUCT_STAT st;
406 int referral_len = 0;
407 #if defined(HAVE_BROKEN_READLINK)
408 char link_target_buf[PATH_MAX];
409 #else
410 char link_target_buf[7];
411 #endif
412 size_t bufsize = 0;
413 char *link_target = NULL;
415 if (pp_link_target) {
416 bufsize = 1024;
417 link_target = TALLOC_ARRAY(ctx, char, bufsize);
418 if (!link_target) {
419 return False;
421 *pp_link_target = link_target;
422 } else {
423 bufsize = sizeof(link_target_buf);
424 link_target = link_target_buf;
427 if (sbufp == NULL) {
428 sbufp = &st;
431 if (SMB_VFS_LSTAT(conn, path, sbufp) != 0) {
432 DEBUG(5,("is_msdfs_link_read_target: %s does not exist.\n",
433 path));
434 goto err;
437 if (!S_ISLNK(sbufp->st_mode)) {
438 DEBUG(5,("is_msdfs_link_read_target: %s is not a link.\n",
439 path));
440 goto err;
443 referral_len = SMB_VFS_READLINK(conn, path, link_target, bufsize - 1);
444 if (referral_len == -1) {
445 DEBUG(0,("is_msdfs_link_read_target: Error reading "
446 "msdfs link %s: %s\n",
447 path, strerror(errno)));
448 goto err;
450 link_target[referral_len] = '\0';
452 DEBUG(5,("is_msdfs_link_internal: %s -> %s\n",path,
453 link_target));
455 if (!strnequal(link_target, "msdfs:", 6)) {
456 goto err;
458 return True;
460 err:
462 if (link_target != link_target_buf) {
463 TALLOC_FREE(link_target);
465 return False;
468 /**********************************************************************
469 Returns true if the unix path is a valid msdfs symlink.
470 **********************************************************************/
472 bool is_msdfs_link(connection_struct *conn,
473 const char *path,
474 SMB_STRUCT_STAT *sbufp)
476 return is_msdfs_link_internal(talloc_tos(),
477 conn,
478 path,
479 NULL,
480 sbufp);
483 /*****************************************************************
484 Used by other functions to decide if a dfs path is remote,
485 and to get the list of referred locations for that remote path.
487 search_flag: For findfirsts, dfs links themselves are not
488 redirected, but paths beyond the links are. For normal smb calls,
489 even dfs links need to be redirected.
491 consumedcntp: how much of the dfs path is being redirected. the client
492 should try the remaining path on the redirected server.
494 If this returns NT_STATUS_PATH_NOT_COVERED the contents of the msdfs
495 link redirect are in targetpath.
496 *****************************************************************/
498 static NTSTATUS dfs_path_lookup(TALLOC_CTX *ctx,
499 connection_struct *conn,
500 const char *dfspath, /* Incoming complete dfs path */
501 const struct dfs_path *pdp, /* Parsed out
502 server+share+extrapath. */
503 bool search_flag, /* Called from a findfirst ? */
504 int *consumedcntp,
505 char **pp_targetpath)
507 char *p = NULL;
508 char *q = NULL;
509 SMB_STRUCT_STAT sbuf;
510 NTSTATUS status;
511 char *localpath = NULL;
512 char *canon_dfspath = NULL; /* Canonicalized dfs path. (only '/'
513 components). */
515 DEBUG(10,("dfs_path_lookup: Conn path = %s reqpath = %s\n",
516 conn->connectpath, pdp->reqpath));
519 * Note the unix path conversion here we're doing we can
520 * throw away. We're looking for a symlink for a dfs
521 * resolution, if we don't find it we'll do another
522 * unix_convert later in the codepath.
523 * If we needed to remember what we'd resolved in
524 * dp->reqpath (as the original code did) we'd
525 * copy (localhost, dp->reqpath) on any code
526 * path below that returns True - but I don't
527 * think this is needed. JRA.
530 status = unix_convert(ctx, conn, pdp->reqpath, search_flag, &localpath,
531 NULL, &sbuf);
532 if (!NT_STATUS_IS_OK(status) && !NT_STATUS_EQUAL(status,
533 NT_STATUS_OBJECT_PATH_NOT_FOUND)) {
534 return status;
537 /* Optimization - check if we can redirect the whole path. */
539 if (is_msdfs_link_internal(ctx, conn, localpath, pp_targetpath, NULL)) {
540 if (search_flag) {
541 DEBUG(6,("dfs_path_lookup (FindFirst) No redirection "
542 "for dfs link %s.\n", dfspath));
543 return NT_STATUS_OK;
546 DEBUG(6,("dfs_path_lookup: %s resolves to a "
547 "valid dfs link %s.\n", dfspath,
548 pp_targetpath ? *pp_targetpath : ""));
550 if (consumedcntp) {
551 *consumedcntp = strlen(dfspath);
553 return NT_STATUS_PATH_NOT_COVERED;
556 /* Prepare to test only for '/' components in the given path,
557 * so if a Windows path replace all '\\' characters with '/'.
558 * For a POSIX DFS path we know all separators are already '/'. */
560 canon_dfspath = talloc_strdup(ctx, dfspath);
561 if (!canon_dfspath) {
562 return NT_STATUS_NO_MEMORY;
564 if (!pdp->posix_path) {
565 string_replace(canon_dfspath, '\\', '/');
569 * localpath comes out of unix_convert, so it has
570 * no trailing backslash. Make sure that canon_dfspath hasn't either.
571 * Fix for bug #4860 from Jan Martin <Jan.Martin@rwedea.com>.
574 trim_char(canon_dfspath,0,'/');
577 * Redirect if any component in the path is a link.
578 * We do this by walking backwards through the
579 * local path, chopping off the last component
580 * in both the local path and the canonicalized
581 * DFS path. If we hit a DFS link then we're done.
584 p = strrchr_m(localpath, '/');
585 if (consumedcntp) {
586 q = strrchr_m(canon_dfspath, '/');
589 while (p) {
590 *p = '\0';
591 if (q) {
592 *q = '\0';
595 if (is_msdfs_link_internal(ctx, conn,
596 localpath, pp_targetpath, NULL)) {
597 DEBUG(4, ("dfs_path_lookup: Redirecting %s because "
598 "parent %s is dfs link\n", dfspath, localpath));
600 if (consumedcntp) {
601 *consumedcntp = strlen(canon_dfspath);
602 DEBUG(10, ("dfs_path_lookup: Path consumed: %s "
603 "(%d)\n",
604 canon_dfspath,
605 *consumedcntp));
608 return NT_STATUS_PATH_NOT_COVERED;
611 /* Step back on the filesystem. */
612 p = strrchr_m(localpath, '/');
614 if (consumedcntp) {
615 /* And in the canonicalized dfs path. */
616 q = strrchr_m(canon_dfspath, '/');
620 return NT_STATUS_OK;
623 /*****************************************************************
624 Decides if a dfs pathname should be redirected or not.
625 If not, the pathname is converted to a tcon-relative local unix path
627 search_wcard_flag: this flag performs 2 functions both related
628 to searches. See resolve_dfs_path() and parse_dfs_path_XX()
629 for details.
631 This function can return NT_STATUS_OK, meaning use the returned path as-is
632 (mapped into a local path).
633 or NT_STATUS_NOT_COVERED meaning return a DFS redirect, or
634 any other NT_STATUS error which is a genuine error to be
635 returned to the client.
636 *****************************************************************/
638 static NTSTATUS dfs_redirect(TALLOC_CTX *ctx,
639 connection_struct *conn,
640 const char *path_in,
641 bool search_wcard_flag,
642 char **pp_path_out,
643 bool *ppath_contains_wcard)
645 NTSTATUS status;
646 struct dfs_path *pdp = TALLOC_P(ctx, struct dfs_path);
648 if (!pdp) {
649 return NT_STATUS_NO_MEMORY;
652 status = parse_dfs_path(conn, path_in, search_wcard_flag, pdp,
653 ppath_contains_wcard);
654 if (!NT_STATUS_IS_OK(status)) {
655 TALLOC_FREE(pdp);
656 return status;
659 if (pdp->reqpath[0] == '\0') {
660 TALLOC_FREE(pdp);
661 *pp_path_out = talloc_strdup(ctx, "");
662 if (!*pp_path_out) {
663 return NT_STATUS_NO_MEMORY;
665 DEBUG(5,("dfs_redirect: self-referral.\n"));
666 return NT_STATUS_OK;
669 /* If dfs pathname for a non-dfs share, convert to tcon-relative
670 path and return OK */
672 if (!lp_msdfs_root(SNUM(conn))) {
673 *pp_path_out = talloc_strdup(ctx, pdp->reqpath);
674 TALLOC_FREE(pdp);
675 if (!*pp_path_out) {
676 return NT_STATUS_NO_MEMORY;
678 return NT_STATUS_OK;
681 /* If it looked like a local path (zero hostname/servicename)
682 * just treat as a tcon-relative path. */
684 if (pdp->hostname[0] == '\0' && pdp->servicename[0] == '\0') {
685 *pp_path_out = talloc_strdup(ctx, pdp->reqpath);
686 TALLOC_FREE(pdp);
687 if (!*pp_path_out) {
688 return NT_STATUS_NO_MEMORY;
690 return NT_STATUS_OK;
693 if (!( strequal(pdp->servicename, lp_servicename(SNUM(conn)))
694 || (strequal(pdp->servicename, HOMES_NAME)
695 && strequal(lp_servicename(SNUM(conn)),
696 conn->server_info->sanitized_username) )) ) {
698 /* The given sharename doesn't match this connection. */
699 TALLOC_FREE(pdp);
701 return NT_STATUS_OBJECT_PATH_NOT_FOUND;
704 status = dfs_path_lookup(ctx, conn, path_in, pdp,
705 search_wcard_flag, NULL, NULL);
706 if (!NT_STATUS_IS_OK(status)) {
707 if (NT_STATUS_EQUAL(status, NT_STATUS_PATH_NOT_COVERED)) {
708 DEBUG(3,("dfs_redirect: Redirecting %s\n", path_in));
709 } else {
710 DEBUG(10,("dfs_redirect: dfs_path_lookup "
711 "failed for %s with %s\n",
712 path_in, nt_errstr(status) ));
714 return status;
717 DEBUG(3,("dfs_redirect: Not redirecting %s.\n", path_in));
719 /* Form non-dfs tcon-relative path */
720 *pp_path_out = talloc_strdup(ctx, pdp->reqpath);
721 TALLOC_FREE(pdp);
722 if (!*pp_path_out) {
723 return NT_STATUS_NO_MEMORY;
726 DEBUG(3,("dfs_redirect: Path %s converted to non-dfs path %s\n",
727 path_in,
728 *pp_path_out));
730 return NT_STATUS_OK;
733 /**********************************************************************
734 Return a self referral.
735 **********************************************************************/
737 static NTSTATUS self_ref(TALLOC_CTX *ctx,
738 const char *dfs_path,
739 struct junction_map *jucn,
740 int *consumedcntp,
741 bool *self_referralp)
743 struct referral *ref;
745 *self_referralp = True;
747 jucn->referral_count = 1;
748 if((ref = TALLOC_ZERO_P(ctx, struct referral)) == NULL) {
749 return NT_STATUS_NO_MEMORY;
752 ref->alternate_path = talloc_strdup(ctx, dfs_path);
753 if (!ref->alternate_path) {
754 return NT_STATUS_NO_MEMORY;
756 ref->proximity = 0;
757 ref->ttl = REFERRAL_TTL;
758 jucn->referral_list = ref;
759 *consumedcntp = strlen(dfs_path);
760 return NT_STATUS_OK;
763 /**********************************************************************
764 Gets valid referrals for a dfs path and fills up the
765 junction_map structure.
766 **********************************************************************/
768 NTSTATUS get_referred_path(TALLOC_CTX *ctx,
769 const char *dfs_path,
770 struct junction_map *jucn,
771 int *consumedcntp,
772 bool *self_referralp)
774 struct connection_struct *conn;
775 char *targetpath = NULL;
776 int snum;
777 NTSTATUS status = NT_STATUS_NOT_FOUND;
778 bool dummy;
779 struct dfs_path *pdp = TALLOC_P(ctx, struct dfs_path);
780 char *oldpath;
782 if (!pdp) {
783 return NT_STATUS_NO_MEMORY;
786 *self_referralp = False;
788 status = parse_dfs_path(NULL, dfs_path, False, pdp, &dummy);
789 if (!NT_STATUS_IS_OK(status)) {
790 return status;
793 jucn->service_name = talloc_strdup(ctx, pdp->servicename);
794 jucn->volume_name = talloc_strdup(ctx, pdp->reqpath);
795 if (!jucn->service_name || !jucn->volume_name) {
796 TALLOC_FREE(pdp);
797 return NT_STATUS_NO_MEMORY;
800 /* Verify the share is a dfs root */
801 snum = lp_servicenumber(jucn->service_name);
802 if(snum < 0) {
803 fstring service_name;
804 fstrcpy(service_name, jucn->service_name);
805 if ((snum = find_service(service_name)) < 0) {
806 return NT_STATUS_NOT_FOUND;
808 TALLOC_FREE(jucn->service_name);
809 jucn->service_name = talloc_strdup(ctx, service_name);
810 if (!jucn->service_name) {
811 TALLOC_FREE(pdp);
812 return NT_STATUS_NO_MEMORY;
816 if (!lp_msdfs_root(snum) && (*lp_msdfs_proxy(snum) == '\0')) {
817 DEBUG(3,("get_referred_path: |%s| in dfs path %s is not "
818 "a dfs root.\n",
819 pdp->servicename, dfs_path));
820 TALLOC_FREE(pdp);
821 return NT_STATUS_NOT_FOUND;
825 * Self referrals are tested with a anonymous IPC connection and
826 * a GET_DFS_REFERRAL call to \\server\share. (which means
827 * dp.reqpath[0] points to an empty string). create_conn_struct cd's
828 * into the directory and will fail if it cannot (as the anonymous
829 * user). Cope with this.
832 if (pdp->reqpath[0] == '\0') {
833 char *tmp;
834 struct referral *ref;
836 if (*lp_msdfs_proxy(snum) == '\0') {
837 TALLOC_FREE(pdp);
838 return self_ref(ctx,
839 dfs_path,
840 jucn,
841 consumedcntp,
842 self_referralp);
846 * It's an msdfs proxy share. Redirect to
847 * the configured target share.
850 jucn->referral_count = 1;
851 if ((ref = TALLOC_ZERO_P(ctx, struct referral)) == NULL) {
852 TALLOC_FREE(pdp);
853 return NT_STATUS_NO_MEMORY;
856 if (!(tmp = talloc_strdup(ctx, lp_msdfs_proxy(snum)))) {
857 TALLOC_FREE(pdp);
858 return NT_STATUS_NO_MEMORY;
861 trim_string(tmp, "\\", 0);
863 ref->alternate_path = talloc_asprintf(ctx, "\\%s", tmp);
864 TALLOC_FREE(tmp);
866 if (!ref->alternate_path) {
867 TALLOC_FREE(pdp);
868 return NT_STATUS_NO_MEMORY;
871 if (pdp->reqpath[0] != '\0') {
872 ref->alternate_path = talloc_asprintf_append(
873 ref->alternate_path,
874 "%s",
875 pdp->reqpath);
876 if (!ref->alternate_path) {
877 TALLOC_FREE(pdp);
878 return NT_STATUS_NO_MEMORY;
881 ref->proximity = 0;
882 ref->ttl = REFERRAL_TTL;
883 jucn->referral_list = ref;
884 *consumedcntp = strlen(dfs_path);
885 TALLOC_FREE(pdp);
886 return NT_STATUS_OK;
889 status = create_conn_struct(ctx, &conn, snum, lp_pathname(snum),
890 &oldpath);
891 if (!NT_STATUS_IS_OK(status)) {
892 TALLOC_FREE(pdp);
893 return status;
896 /* If this is a DFS path dfs_lookup should return
897 * NT_STATUS_PATH_NOT_COVERED. */
899 status = dfs_path_lookup(ctx, conn, dfs_path, pdp,
900 False, consumedcntp, &targetpath);
902 if (!NT_STATUS_EQUAL(status, NT_STATUS_PATH_NOT_COVERED)) {
903 DEBUG(3,("get_referred_path: No valid referrals for path %s\n",
904 dfs_path));
905 vfs_ChDir(conn, oldpath);
906 conn_free_internal(conn);
907 TALLOC_FREE(pdp);
908 return status;
911 /* We know this is a valid dfs link. Parse the targetpath. */
912 if (!parse_msdfs_symlink(ctx, targetpath,
913 &jucn->referral_list,
914 &jucn->referral_count)) {
915 DEBUG(3,("get_referred_path: failed to parse symlink "
916 "target %s\n", targetpath ));
917 vfs_ChDir(conn, oldpath);
918 conn_free_internal(conn);
919 TALLOC_FREE(pdp);
920 return NT_STATUS_NOT_FOUND;
923 vfs_ChDir(conn, oldpath);
924 conn_free_internal(conn);
925 TALLOC_FREE(pdp);
926 return NT_STATUS_OK;
929 static int setup_ver2_dfs_referral(const char *pathname,
930 char **ppdata,
931 struct junction_map *junction,
932 bool self_referral)
934 char* pdata = *ppdata;
936 smb_ucs2_t *uni_requestedpath = NULL;
937 int uni_reqpathoffset1,uni_reqpathoffset2;
938 int uni_curroffset;
939 int requestedpathlen=0;
940 int offset;
941 int reply_size = 0;
942 int i=0;
944 DEBUG(10,("Setting up version2 referral\nRequested path:\n"));
946 requestedpathlen = rpcstr_push_talloc(talloc_tos(),
947 &uni_requestedpath, pathname);
948 if (uni_requestedpath == NULL || requestedpathlen == 0) {
949 return -1;
952 if (DEBUGLVL(10)) {
953 dump_data(0, (unsigned char *)uni_requestedpath,
954 requestedpathlen);
957 DEBUG(10,("ref count = %u\n",junction->referral_count));
959 uni_reqpathoffset1 = REFERRAL_HEADER_SIZE +
960 VERSION2_REFERRAL_SIZE * junction->referral_count;
962 uni_reqpathoffset2 = uni_reqpathoffset1 + requestedpathlen;
964 uni_curroffset = uni_reqpathoffset2 + requestedpathlen;
966 reply_size = REFERRAL_HEADER_SIZE +
967 VERSION2_REFERRAL_SIZE*junction->referral_count +
968 2 * requestedpathlen;
969 DEBUG(10,("reply_size: %u\n",reply_size));
971 /* add up the unicode lengths of all the referral paths */
972 for(i=0;i<junction->referral_count;i++) {
973 DEBUG(10,("referral %u : %s\n",
975 junction->referral_list[i].alternate_path));
976 reply_size +=
977 (strlen(junction->referral_list[i].alternate_path)+1)*2;
980 DEBUG(10,("reply_size = %u\n",reply_size));
981 /* add the unexplained 0x16 bytes */
982 reply_size += 0x16;
984 pdata = (char *)SMB_REALLOC(pdata,reply_size);
985 if(pdata == NULL) {
986 DEBUG(0,("Realloc failed!\n"));
987 return -1;
989 *ppdata = pdata;
991 /* copy in the dfs requested paths.. required for offset calculations */
992 memcpy(pdata+uni_reqpathoffset1,uni_requestedpath,requestedpathlen);
993 memcpy(pdata+uni_reqpathoffset2,uni_requestedpath,requestedpathlen);
995 /* create the header */
996 SSVAL(pdata,0,requestedpathlen - 2); /* UCS2 of path consumed minus
997 2 byte null */
998 /* number of referral in this pkt */
999 SSVAL(pdata,2,junction->referral_count);
1000 if(self_referral) {
1001 SIVAL(pdata,4,DFSREF_REFERRAL_SERVER | DFSREF_STORAGE_SERVER);
1002 } else {
1003 SIVAL(pdata,4,DFSREF_STORAGE_SERVER);
1006 offset = 8;
1007 /* add the referral elements */
1008 for(i=0;i<junction->referral_count;i++) {
1009 struct referral* ref = &junction->referral_list[i];
1010 int unilen;
1012 SSVAL(pdata,offset,2); /* version 2 */
1013 SSVAL(pdata,offset+2,VERSION2_REFERRAL_SIZE);
1014 if(self_referral) {
1015 SSVAL(pdata,offset+4,1);
1016 } else {
1017 SSVAL(pdata,offset+4,0);
1020 /* ref_flags :use path_consumed bytes? */
1021 SSVAL(pdata,offset+6,0);
1022 SIVAL(pdata,offset+8,ref->proximity);
1023 SIVAL(pdata,offset+12,ref->ttl);
1025 SSVAL(pdata,offset+16,uni_reqpathoffset1-offset);
1026 SSVAL(pdata,offset+18,uni_reqpathoffset2-offset);
1027 /* copy referred path into current offset */
1028 unilen = rpcstr_push(pdata+uni_curroffset,
1029 ref->alternate_path,
1030 reply_size - uni_curroffset,
1031 STR_UNICODE);
1033 SSVAL(pdata,offset+20,uni_curroffset-offset);
1035 uni_curroffset += unilen;
1036 offset += VERSION2_REFERRAL_SIZE;
1038 /* add in the unexplained 22 (0x16) bytes at the end */
1039 memset(pdata+uni_curroffset,'\0',0x16);
1040 return reply_size;
1043 static int setup_ver3_dfs_referral(const char *pathname,
1044 char **ppdata,
1045 struct junction_map *junction,
1046 bool self_referral)
1048 char *pdata = *ppdata;
1050 smb_ucs2_t *uni_reqpath = NULL;
1051 int uni_reqpathoffset1, uni_reqpathoffset2;
1052 int uni_curroffset;
1053 int reply_size = 0;
1055 int reqpathlen = 0;
1056 int offset,i=0;
1058 DEBUG(10,("setting up version3 referral\n"));
1060 reqpathlen = rpcstr_push_talloc(talloc_tos(), &uni_reqpath, pathname);
1061 if (uni_reqpath == NULL || reqpathlen == 0) {
1062 return -1;
1065 if (DEBUGLVL(10)) {
1066 dump_data(0, (unsigned char *)uni_reqpath,
1067 reqpathlen);
1070 uni_reqpathoffset1 = REFERRAL_HEADER_SIZE +
1071 VERSION3_REFERRAL_SIZE * junction->referral_count;
1072 uni_reqpathoffset2 = uni_reqpathoffset1 + reqpathlen;
1073 reply_size = uni_curroffset = uni_reqpathoffset2 + reqpathlen;
1075 for(i=0;i<junction->referral_count;i++) {
1076 DEBUG(10,("referral %u : %s\n",
1078 junction->referral_list[i].alternate_path));
1079 reply_size +=
1080 (strlen(junction->referral_list[i].alternate_path)+1)*2;
1083 pdata = (char *)SMB_REALLOC(pdata,reply_size);
1084 if(pdata == NULL) {
1085 DEBUG(0,("version3 referral setup:"
1086 "malloc failed for Realloc!\n"));
1087 return -1;
1089 *ppdata = pdata;
1091 /* create the header */
1092 SSVAL(pdata,0,reqpathlen - 2); /* UCS2 of path consumed minus
1093 2 byte null */
1094 SSVAL(pdata,2,junction->referral_count); /* number of referral */
1095 if(self_referral) {
1096 SIVAL(pdata,4,DFSREF_REFERRAL_SERVER | DFSREF_STORAGE_SERVER);
1097 } else {
1098 SIVAL(pdata,4,DFSREF_STORAGE_SERVER);
1101 /* copy in the reqpaths */
1102 memcpy(pdata+uni_reqpathoffset1,uni_reqpath,reqpathlen);
1103 memcpy(pdata+uni_reqpathoffset2,uni_reqpath,reqpathlen);
1105 offset = 8;
1106 for(i=0;i<junction->referral_count;i++) {
1107 struct referral* ref = &(junction->referral_list[i]);
1108 int unilen;
1110 SSVAL(pdata,offset,3); /* version 3 */
1111 SSVAL(pdata,offset+2,VERSION3_REFERRAL_SIZE);
1112 if(self_referral) {
1113 SSVAL(pdata,offset+4,1);
1114 } else {
1115 SSVAL(pdata,offset+4,0);
1118 /* ref_flags :use path_consumed bytes? */
1119 SSVAL(pdata,offset+6,0);
1120 SIVAL(pdata,offset+8,ref->ttl);
1122 SSVAL(pdata,offset+12,uni_reqpathoffset1-offset);
1123 SSVAL(pdata,offset+14,uni_reqpathoffset2-offset);
1124 /* copy referred path into current offset */
1125 unilen = rpcstr_push(pdata+uni_curroffset,ref->alternate_path,
1126 reply_size - uni_curroffset,
1127 STR_UNICODE | STR_TERMINATE);
1128 SSVAL(pdata,offset+16,uni_curroffset-offset);
1129 /* copy 0x10 bytes of 00's in the ServiceSite GUID */
1130 memset(pdata+offset+18,'\0',16);
1132 uni_curroffset += unilen;
1133 offset += VERSION3_REFERRAL_SIZE;
1135 return reply_size;
1138 /******************************************************************
1139 Set up the DFS referral for the dfs pathname. This call returns
1140 the amount of the path covered by this server, and where the
1141 client should be redirected to. This is the meat of the
1142 TRANS2_GET_DFS_REFERRAL call.
1143 ******************************************************************/
1145 int setup_dfs_referral(connection_struct *orig_conn,
1146 const char *dfs_path,
1147 int max_referral_level,
1148 char **ppdata, NTSTATUS *pstatus)
1150 struct junction_map *junction = NULL;
1151 int consumedcnt = 0;
1152 bool self_referral = False;
1153 int reply_size = 0;
1154 char *pathnamep = NULL;
1155 char *local_dfs_path = NULL;
1156 TALLOC_CTX *ctx;
1158 if (!(ctx=talloc_init("setup_dfs_referral"))) {
1159 *pstatus = NT_STATUS_NO_MEMORY;
1160 return -1;
1163 /* get the junction entry */
1164 if (!dfs_path) {
1165 talloc_destroy(ctx);
1166 *pstatus = NT_STATUS_NOT_FOUND;
1167 return -1;
1171 * Trim pathname sent by client so it begins with only one backslash.
1172 * Two backslashes confuse some dfs clients
1175 local_dfs_path = talloc_strdup(ctx,dfs_path);
1176 if (!local_dfs_path) {
1177 *pstatus = NT_STATUS_NO_MEMORY;
1178 talloc_destroy(ctx);
1179 return -1;
1181 pathnamep = local_dfs_path;
1182 while (IS_DIRECTORY_SEP(pathnamep[0]) &&
1183 IS_DIRECTORY_SEP(pathnamep[1])) {
1184 pathnamep++;
1187 junction = TALLOC_ZERO_P(ctx, struct junction_map);
1188 if (!junction) {
1189 *pstatus = NT_STATUS_NO_MEMORY;
1190 talloc_destroy(ctx);
1191 return -1;
1194 /* The following call can change cwd. */
1195 *pstatus = get_referred_path(ctx, pathnamep, junction,
1196 &consumedcnt, &self_referral);
1197 if (!NT_STATUS_IS_OK(*pstatus)) {
1198 vfs_ChDir(orig_conn,orig_conn->connectpath);
1199 talloc_destroy(ctx);
1200 return -1;
1202 vfs_ChDir(orig_conn,orig_conn->connectpath);
1204 if (!self_referral) {
1205 pathnamep[consumedcnt] = '\0';
1207 if( DEBUGLVL( 3 ) ) {
1208 int i=0;
1209 dbgtext("setup_dfs_referral: Path %s to "
1210 "alternate path(s):",
1211 pathnamep);
1212 for(i=0;i<junction->referral_count;i++)
1213 dbgtext(" %s",
1214 junction->referral_list[i].alternate_path);
1215 dbgtext(".\n");
1219 /* create the referral depeding on version */
1220 DEBUG(10,("max_referral_level :%d\n",max_referral_level));
1222 if (max_referral_level < 2) {
1223 max_referral_level = 2;
1225 if (max_referral_level > 3) {
1226 max_referral_level = 3;
1229 switch(max_referral_level) {
1230 case 2:
1231 reply_size = setup_ver2_dfs_referral(pathnamep,
1232 ppdata, junction,
1233 self_referral);
1234 break;
1235 case 3:
1236 reply_size = setup_ver3_dfs_referral(pathnamep, ppdata,
1237 junction, self_referral);
1238 break;
1239 default:
1240 DEBUG(0,("setup_dfs_referral: Invalid dfs referral "
1241 "version: %d\n",
1242 max_referral_level));
1243 talloc_destroy(ctx);
1244 *pstatus = NT_STATUS_INVALID_LEVEL;
1245 return -1;
1248 if (DEBUGLVL(10)) {
1249 DEBUGADD(0,("DFS Referral pdata:\n"));
1250 dump_data(0,(uint8 *)*ppdata,reply_size);
1253 talloc_destroy(ctx);
1254 *pstatus = NT_STATUS_OK;
1255 return reply_size;
1258 /**********************************************************************
1259 The following functions are called by the NETDFS RPC pipe functions
1260 **********************************************************************/
1262 /*********************************************************************
1263 Creates a junction structure from a DFS pathname
1264 **********************************************************************/
1266 bool create_junction(TALLOC_CTX *ctx,
1267 const char *dfs_path,
1268 struct junction_map *jucn)
1270 int snum;
1271 bool dummy;
1272 struct dfs_path *pdp = TALLOC_P(ctx,struct dfs_path);
1273 NTSTATUS status;
1275 if (!pdp) {
1276 return False;
1278 status = parse_dfs_path(NULL, dfs_path, False, pdp, &dummy);
1279 if (!NT_STATUS_IS_OK(status)) {
1280 return False;
1283 /* check if path is dfs : validate first token */
1284 if (!is_myname_or_ipaddr(pdp->hostname)) {
1285 DEBUG(4,("create_junction: Invalid hostname %s "
1286 "in dfs path %s\n",
1287 pdp->hostname, dfs_path));
1288 TALLOC_FREE(pdp);
1289 return False;
1292 /* Check for a non-DFS share */
1293 snum = lp_servicenumber(pdp->servicename);
1295 if(snum < 0 || !lp_msdfs_root(snum)) {
1296 DEBUG(4,("create_junction: %s is not an msdfs root.\n",
1297 pdp->servicename));
1298 TALLOC_FREE(pdp);
1299 return False;
1302 jucn->service_name = talloc_strdup(ctx, pdp->servicename);
1303 jucn->volume_name = talloc_strdup(ctx, pdp->reqpath);
1304 jucn->comment = talloc_strdup(ctx, lp_comment(snum));
1306 TALLOC_FREE(pdp);
1307 if (!jucn->service_name || !jucn->volume_name || ! jucn->comment) {
1308 return False;
1310 return True;
1313 /**********************************************************************
1314 Forms a valid Unix pathname from the junction
1315 **********************************************************************/
1317 static bool junction_to_local_path(const struct junction_map *jucn,
1318 char **pp_path_out,
1319 connection_struct **conn_out,
1320 char **oldpath)
1322 int snum;
1323 NTSTATUS status;
1325 snum = lp_servicenumber(jucn->service_name);
1326 if(snum < 0) {
1327 return False;
1329 status = create_conn_struct(talloc_tos(), conn_out, snum,
1330 lp_pathname(snum), oldpath);
1331 if (!NT_STATUS_IS_OK(status)) {
1332 return False;
1335 *pp_path_out = talloc_asprintf(*conn_out,
1336 "%s/%s",
1337 lp_pathname(snum),
1338 jucn->volume_name);
1339 if (!*pp_path_out) {
1340 vfs_ChDir(*conn_out, *oldpath);
1341 conn_free_internal(*conn_out);
1342 return False;
1344 return True;
1347 bool create_msdfs_link(const struct junction_map *jucn)
1349 char *path = NULL;
1350 char *cwd;
1351 char *msdfs_link = NULL;
1352 connection_struct *conn;
1353 int i=0;
1354 bool insert_comma = False;
1355 bool ret = False;
1357 if(!junction_to_local_path(jucn, &path, &conn, &cwd)) {
1358 return False;
1361 /* Form the msdfs_link contents */
1362 msdfs_link = talloc_strdup(conn, "msdfs:");
1363 if (!msdfs_link) {
1364 goto out;
1366 for(i=0; i<jucn->referral_count; i++) {
1367 char *refpath = jucn->referral_list[i].alternate_path;
1369 /* Alternate paths always use Windows separators. */
1370 trim_char(refpath, '\\', '\\');
1371 if(*refpath == '\0') {
1372 if (i == 0) {
1373 insert_comma = False;
1375 continue;
1377 if (i > 0 && insert_comma) {
1378 msdfs_link = talloc_asprintf_append_buffer(msdfs_link,
1379 ",%s",
1380 refpath);
1381 } else {
1382 msdfs_link = talloc_asprintf_append_buffer(msdfs_link,
1383 "%s",
1384 refpath);
1387 if (!msdfs_link) {
1388 goto out;
1390 if (!insert_comma) {
1391 insert_comma = True;
1395 DEBUG(5,("create_msdfs_link: Creating new msdfs link: %s -> %s\n",
1396 path, msdfs_link));
1398 if(SMB_VFS_SYMLINK(conn, msdfs_link, path) < 0) {
1399 if (errno == EEXIST) {
1400 if(SMB_VFS_UNLINK(conn,path)!=0) {
1401 goto out;
1404 if (SMB_VFS_SYMLINK(conn, msdfs_link, path) < 0) {
1405 DEBUG(1,("create_msdfs_link: symlink failed "
1406 "%s -> %s\nError: %s\n",
1407 path, msdfs_link, strerror(errno)));
1408 goto out;
1412 ret = True;
1414 out:
1415 vfs_ChDir(conn, cwd);
1416 conn_free_internal(conn);
1417 return ret;
1420 bool remove_msdfs_link(const struct junction_map *jucn)
1422 char *path = NULL;
1423 char *cwd;
1424 connection_struct *conn;
1425 bool ret = False;
1427 if (!junction_to_local_path(jucn, &path, &conn, &cwd)) {
1428 return false;
1431 if( SMB_VFS_UNLINK(conn, path) == 0 ) {
1432 ret = True;
1435 vfs_ChDir(conn, cwd);
1436 conn_free_internal(conn);
1437 return ret;
1440 /*********************************************************************
1441 Return the number of DFS links at the root of this share.
1442 *********************************************************************/
1444 static int count_dfs_links(TALLOC_CTX *ctx, int snum)
1446 size_t cnt = 0;
1447 SMB_STRUCT_DIR *dirp = NULL;
1448 char *dname = NULL;
1449 const char *connect_path = lp_pathname(snum);
1450 const char *msdfs_proxy = lp_msdfs_proxy(snum);
1451 connection_struct *conn;
1452 NTSTATUS status;
1453 char *cwd;
1455 if(*connect_path == '\0') {
1456 return 0;
1460 * Fake up a connection struct for the VFS layer.
1463 status = create_conn_struct(talloc_tos(), &conn, snum, connect_path,
1464 &cwd);
1465 if (!NT_STATUS_IS_OK(status)) {
1466 DEBUG(3, ("create_conn_struct failed: %s\n",
1467 nt_errstr(status)));
1468 return 0;
1471 /* Count a link for the msdfs root - convention */
1472 cnt = 1;
1474 /* No more links if this is an msdfs proxy. */
1475 if (*msdfs_proxy != '\0') {
1476 goto out;
1479 /* Now enumerate all dfs links */
1480 dirp = SMB_VFS_OPENDIR(conn, ".", NULL, 0);
1481 if(!dirp) {
1482 goto out;
1485 while ((dname = vfs_readdirname(conn, dirp)) != NULL) {
1486 if (is_msdfs_link(conn,
1487 dname,
1488 NULL)) {
1489 cnt++;
1493 SMB_VFS_CLOSEDIR(conn,dirp);
1495 out:
1496 vfs_ChDir(conn, cwd);
1497 conn_free_internal(conn);
1498 return cnt;
1501 /*********************************************************************
1502 *********************************************************************/
1504 static int form_junctions(TALLOC_CTX *ctx,
1505 int snum,
1506 struct junction_map *jucn,
1507 size_t jn_remain)
1509 size_t cnt = 0;
1510 SMB_STRUCT_DIR *dirp = NULL;
1511 char *dname = NULL;
1512 const char *connect_path = lp_pathname(snum);
1513 char *service_name = lp_servicename(snum);
1514 const char *msdfs_proxy = lp_msdfs_proxy(snum);
1515 connection_struct *conn;
1516 struct referral *ref = NULL;
1517 char *cwd;
1518 NTSTATUS status;
1520 if (jn_remain == 0) {
1521 return 0;
1524 if(*connect_path == '\0') {
1525 return 0;
1529 * Fake up a connection struct for the VFS layer.
1532 status = create_conn_struct(ctx, &conn, snum, connect_path, &cwd);
1533 if (!NT_STATUS_IS_OK(status)) {
1534 DEBUG(3, ("create_conn_struct failed: %s\n",
1535 nt_errstr(status)));
1536 return 0;
1539 /* form a junction for the msdfs root - convention
1540 DO NOT REMOVE THIS: NT clients will not work with us
1541 if this is not present
1543 jucn[cnt].service_name = talloc_strdup(ctx,service_name);
1544 jucn[cnt].volume_name = talloc_strdup(ctx, "");
1545 if (!jucn[cnt].service_name || !jucn[cnt].volume_name) {
1546 goto out;
1548 jucn[cnt].comment = "";
1549 jucn[cnt].referral_count = 1;
1551 ref = jucn[cnt].referral_list = TALLOC_ZERO_P(ctx, struct referral);
1552 if (jucn[cnt].referral_list == NULL) {
1553 goto out;
1556 ref->proximity = 0;
1557 ref->ttl = REFERRAL_TTL;
1558 if (*msdfs_proxy != '\0') {
1559 ref->alternate_path = talloc_strdup(ctx,
1560 msdfs_proxy);
1561 } else {
1562 ref->alternate_path = talloc_asprintf(ctx,
1563 "\\\\%s\\%s",
1564 get_local_machine_name(),
1565 service_name);
1568 if (!ref->alternate_path) {
1569 goto out;
1571 cnt++;
1573 /* Don't enumerate if we're an msdfs proxy. */
1574 if (*msdfs_proxy != '\0') {
1575 goto out;
1578 /* Now enumerate all dfs links */
1579 dirp = SMB_VFS_OPENDIR(conn, ".", NULL, 0);
1580 if(!dirp) {
1581 goto out;
1584 while ((dname = vfs_readdirname(conn, dirp)) != NULL) {
1585 char *link_target = NULL;
1586 if (cnt >= jn_remain) {
1587 DEBUG(2, ("form_junctions: ran out of MSDFS "
1588 "junction slots"));
1589 goto out;
1591 if (is_msdfs_link_internal(ctx,
1592 conn,
1593 dname, &link_target,
1594 NULL)) {
1595 if (parse_msdfs_symlink(ctx,
1596 link_target,
1597 &jucn[cnt].referral_list,
1598 &jucn[cnt].referral_count)) {
1600 jucn[cnt].service_name = talloc_strdup(ctx,
1601 service_name);
1602 jucn[cnt].volume_name = talloc_strdup(ctx,
1603 dname);
1604 if (!jucn[cnt].service_name ||
1605 !jucn[cnt].volume_name) {
1606 goto out;
1608 jucn[cnt].comment = "";
1609 cnt++;
1611 TALLOC_FREE(link_target);
1615 out:
1617 if (dirp) {
1618 SMB_VFS_CLOSEDIR(conn,dirp);
1621 vfs_ChDir(conn, cwd);
1622 conn_free_internal(conn);
1623 return cnt;
1626 struct junction_map *enum_msdfs_links(TALLOC_CTX *ctx, size_t *p_num_jn)
1628 struct junction_map *jn = NULL;
1629 int i=0;
1630 size_t jn_count = 0;
1631 int sharecount = 0;
1633 *p_num_jn = 0;
1634 if(!lp_host_msdfs()) {
1635 return NULL;
1638 /* Ensure all the usershares are loaded. */
1639 become_root();
1640 load_registry_shares();
1641 sharecount = load_usershare_shares();
1642 unbecome_root();
1644 for(i=0;i < sharecount;i++) {
1645 if(lp_msdfs_root(i)) {
1646 jn_count += count_dfs_links(ctx, i);
1649 if (jn_count == 0) {
1650 return NULL;
1652 jn = TALLOC_ARRAY(ctx, struct junction_map, jn_count);
1653 if (!jn) {
1654 return NULL;
1656 for(i=0; i < sharecount; i++) {
1657 if (*p_num_jn >= jn_count) {
1658 break;
1660 if(lp_msdfs_root(i)) {
1661 *p_num_jn += form_junctions(ctx, i,
1662 &jn[*p_num_jn],
1663 jn_count - *p_num_jn);
1666 return jn;
1669 /******************************************************************************
1670 Core function to resolve a dfs pathname.
1671 ******************************************************************************/
1673 NTSTATUS resolve_dfspath(TALLOC_CTX *ctx,
1674 connection_struct *conn,
1675 bool dfs_pathnames,
1676 const char *name_in,
1677 char **pp_name_out)
1679 NTSTATUS status = NT_STATUS_OK;
1680 bool dummy;
1681 if (dfs_pathnames) {
1682 status = dfs_redirect(ctx,
1683 conn,
1684 name_in,
1685 False,
1686 pp_name_out,
1687 &dummy);
1688 } else {
1690 * Cheat and just return a copy of the in ptr.
1691 * Once srvstr_get_path() uses talloc it'll
1692 * be a talloced ptr anyway.
1694 *pp_name_out = CONST_DISCARD(char *,name_in);
1696 return status;
1699 /******************************************************************************
1700 Core function to resolve a dfs pathname possibly containing a wildcard.
1701 This function is identical to the above except for the bool param to
1702 dfs_redirect but I need this to be separate so it's really clear when
1703 we're allowing wildcards and when we're not. JRA.
1704 ******************************************************************************/
1706 NTSTATUS resolve_dfspath_wcard(TALLOC_CTX *ctx,
1707 connection_struct *conn,
1708 bool dfs_pathnames,
1709 const char *name_in,
1710 char **pp_name_out,
1711 bool *ppath_contains_wcard)
1713 NTSTATUS status = NT_STATUS_OK;
1714 if (dfs_pathnames) {
1715 status = dfs_redirect(ctx,
1716 conn,
1717 name_in,
1718 True,
1719 pp_name_out,
1720 ppath_contains_wcard);
1721 } else {
1723 * Cheat and just return a copy of the in ptr.
1724 * Once srvstr_get_path() uses talloc it'll
1725 * be a talloced ptr anyway.
1727 *pp_name_out = CONST_DISCARD(char *,name_in);
1729 return status;