charset/tests: add more str[n]casecmp_m() tests to demonstrate the bug
[Samba.git] / source3 / smbd / msdfs.c
blob3c23d745eee15c23d29f9210a27fa1fda21c8e6e
1 /*
2 Unix SMB/Netbios implementation.
3 Version 3.0
4 MSDFS services for Samba
5 Copyright (C) Shirish Kalele 2000
6 Copyright (C) Jeremy Allison 2007
7 Copyright (C) Robin McCorkell 2015
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 3 of the License, or
12 (at your option) any later version.
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
19 You should have received a copy of the GNU General Public License
20 along with this program. If not, see <http://www.gnu.org/licenses/>.
24 #define DBGC_CLASS DBGC_MSDFS
25 #include "includes.h"
26 #include "system/filesys.h"
27 #include "smbd/smbd.h"
28 #include "smbd/globals.h"
29 #include "msdfs.h"
30 #include "auth.h"
31 #include "lib/param/loadparm.h"
32 #include "libcli/security/security.h"
33 #include "librpc/gen_ndr/ndr_dfsblobs.h"
34 #include "lib/tsocket/tsocket.h"
36 /**********************************************************************
37 Parse a DFS pathname of the form \hostname\service\reqpath
38 into the dfs_path structure.
39 If POSIX pathnames is true, the pathname may also be of the
40 form /hostname/service/reqpath.
41 We cope with either here.
43 Unfortunately, due to broken clients who might set the
44 SVAL(inbuf,smb_flg2) & FLAGS2_DFS_PATHNAMES bit and then
45 send a local path, we have to cope with that too....
47 If conn != NULL then ensure the provided service is
48 the one pointed to by the connection.
50 This version does everything using pointers within one copy of the
51 pathname string, talloced on the struct dfs_path pointer (which
52 must be talloced). This may be too clever to live....
53 JRA.
54 **********************************************************************/
56 static NTSTATUS parse_dfs_path(connection_struct *conn,
57 const char *pathname,
58 bool allow_wcards,
59 bool allow_broken_path,
60 struct dfs_path *pdp, /* MUST BE TALLOCED */
61 bool *ppath_contains_wcard)
63 char *pathname_local;
64 char *p,*temp;
65 char *servicename;
66 char *eos_ptr;
67 NTSTATUS status = NT_STATUS_OK;
68 char sepchar;
70 ZERO_STRUCTP(pdp);
73 * This is the only talloc we should need to do
74 * on the struct dfs_path. All the pointers inside
75 * it should point to offsets within this string.
78 pathname_local = talloc_strdup(pdp, pathname);
79 if (!pathname_local) {
80 return NT_STATUS_NO_MEMORY;
82 /* Get a pointer to the terminating '\0' */
83 eos_ptr = &pathname_local[strlen(pathname_local)];
84 p = temp = pathname_local;
87 * Non-broken DFS paths *must* start with the
88 * path separator. For Windows this is always '\\',
89 * for posix paths this is always '/'.
92 if (*pathname == '/') {
93 pdp->posix_path = true;
94 sepchar = '/';
95 } else {
96 pdp->posix_path = false;
97 sepchar = '\\';
100 if (allow_broken_path && (*pathname != sepchar)) {
101 DEBUG(10,("parse_dfs_path: path %s doesn't start with %c\n",
102 pathname, sepchar ));
104 * Possibly client sent a local path by mistake.
105 * Try and convert to a local path.
106 * Note that this is an SMB1-only fallback
107 * to cope with known broken SMB1 clients.
110 pdp->hostname = eos_ptr; /* "" */
111 pdp->servicename = eos_ptr; /* "" */
113 /* We've got no info about separators. */
114 pdp->posix_path = lp_posix_pathnames();
115 p = temp;
116 DEBUG(10,("parse_dfs_path: trying to convert %s to a "
117 "local path\n",
118 temp));
119 goto local_path;
123 * Safe to use on talloc'ed string as it only shrinks.
124 * It also doesn't affect the eos_ptr.
126 trim_char(temp,sepchar,sepchar);
128 DEBUG(10,("parse_dfs_path: temp = |%s| after trimming %c's\n",
129 temp, sepchar));
131 /* Now tokenize. */
132 /* Parse out hostname. */
133 p = strchr_m(temp,sepchar);
134 if(p == NULL) {
135 DEBUG(10,("parse_dfs_path: can't parse hostname from path %s\n",
136 temp));
138 * Possibly client sent a local path by mistake.
139 * Try and convert to a local path.
142 pdp->hostname = eos_ptr; /* "" */
143 pdp->servicename = eos_ptr; /* "" */
145 p = temp;
146 DEBUG(10,("parse_dfs_path: trying to convert %s "
147 "to a local path\n",
148 temp));
149 goto local_path;
151 *p = '\0';
152 pdp->hostname = temp;
154 DEBUG(10,("parse_dfs_path: hostname: %s\n",pdp->hostname));
156 /* Parse out servicename. */
157 servicename = p+1;
158 p = strchr_m(servicename,sepchar);
159 if (p) {
160 *p = '\0';
163 /* Is this really our servicename ? */
164 if (conn && !( strequal(servicename, lp_servicename(talloc_tos(), SNUM(conn)))
165 || (strequal(servicename, HOMES_NAME)
166 && strequal(lp_servicename(talloc_tos(), SNUM(conn)),
167 get_current_username()) )) ) {
168 DEBUG(10,("parse_dfs_path: %s is not our servicename\n",
169 servicename));
172 * Possibly client sent a local path by mistake.
173 * Try and convert to a local path.
176 pdp->hostname = eos_ptr; /* "" */
177 pdp->servicename = eos_ptr; /* "" */
179 /* Repair the path - replace the sepchar's
180 we nulled out */
181 servicename--;
182 *servicename = sepchar;
183 if (p) {
184 *p = sepchar;
187 p = temp;
188 DEBUG(10,("parse_dfs_path: trying to convert %s "
189 "to a local path\n",
190 temp));
191 goto local_path;
194 pdp->servicename = servicename;
196 DEBUG(10,("parse_dfs_path: servicename: %s\n",pdp->servicename));
198 if(p == NULL) {
199 /* Client sent self referral \server\share. */
200 pdp->reqpath = eos_ptr; /* "" */
201 return NT_STATUS_OK;
204 p++;
206 local_path:
208 *ppath_contains_wcard = False;
210 pdp->reqpath = p;
212 /* Rest is reqpath. */
213 if (pdp->posix_path) {
214 status = check_path_syntax_posix(pdp->reqpath);
215 } else {
216 if (allow_wcards) {
217 status = check_path_syntax_wcard(pdp->reqpath,
218 ppath_contains_wcard);
219 } else {
220 status = check_path_syntax(pdp->reqpath);
224 if (!NT_STATUS_IS_OK(status)) {
225 DEBUG(10,("parse_dfs_path: '%s' failed with %s\n",
226 p, nt_errstr(status) ));
227 return status;
230 DEBUG(10,("parse_dfs_path: rest of the path: %s\n",pdp->reqpath));
231 return NT_STATUS_OK;
234 /********************************************************
235 Fake up a connection struct for the VFS layer, for use in
236 applications (such as the python bindings), that do not want the
237 global working directory changed under them.
239 SMB_VFS_CONNECT requires root privileges.
240 *********************************************************/
242 static NTSTATUS create_conn_struct_as_root(TALLOC_CTX *ctx,
243 struct tevent_context *ev,
244 struct messaging_context *msg,
245 connection_struct **pconn,
246 int snum,
247 const char *path,
248 const struct auth_session_info *session_info)
250 connection_struct *conn;
251 char *connpath;
252 const char *vfs_user;
253 struct smbd_server_connection *sconn;
254 const char *servicename = lp_const_servicename(snum);
256 sconn = talloc_zero(ctx, struct smbd_server_connection);
257 if (sconn == NULL) {
258 return NT_STATUS_NO_MEMORY;
261 sconn->ev_ctx = ev;
262 sconn->msg_ctx = msg;
264 conn = conn_new(sconn);
265 if (conn == NULL) {
266 TALLOC_FREE(sconn);
267 return NT_STATUS_NO_MEMORY;
270 /* Now we have conn, we need to make sconn a child of conn,
271 * for a proper talloc tree */
272 talloc_steal(conn, sconn);
274 if (snum == -1 && servicename == NULL) {
275 servicename = "Unknown Service (snum == -1)";
278 connpath = talloc_strdup(conn, path);
279 if (!connpath) {
280 TALLOC_FREE(conn);
281 return NT_STATUS_NO_MEMORY;
283 connpath = talloc_string_sub(conn,
284 connpath,
285 "%S",
286 servicename);
287 if (!connpath) {
288 TALLOC_FREE(conn);
289 return NT_STATUS_NO_MEMORY;
292 /* needed for smbd_vfs_init() */
294 conn->params->service = snum;
295 conn->cnum = TID_FIELD_INVALID;
297 if (session_info != NULL) {
298 conn->session_info = copy_session_info(conn, session_info);
299 if (conn->session_info == NULL) {
300 DEBUG(0, ("copy_serverinfo failed\n"));
301 TALLOC_FREE(conn);
302 return NT_STATUS_NO_MEMORY;
304 vfs_user = conn->session_info->unix_info->unix_name;
305 } else {
306 /* use current authenticated user in absence of session_info */
307 vfs_user = get_current_username();
310 set_conn_connectpath(conn, connpath);
313 * New code to check if there's a share security descriptor
314 * added from NT server manager. This is done after the
315 * smb.conf checks are done as we need a uid and token. JRA.
318 if (conn->session_info) {
319 share_access_check(conn->session_info->security_token,
320 servicename,
321 MAXIMUM_ALLOWED_ACCESS,
322 &conn->share_access);
324 if ((conn->share_access & FILE_WRITE_DATA) == 0) {
325 if ((conn->share_access & FILE_READ_DATA) == 0) {
326 /* No access, read or write. */
327 DEBUG(3,("create_conn_struct: connection to %s "
328 "denied due to security "
329 "descriptor.\n",
330 servicename));
331 conn_free(conn);
332 return NT_STATUS_ACCESS_DENIED;
333 } else {
334 conn->read_only = true;
337 } else {
338 conn->share_access = 0;
339 conn->read_only = true;
342 if (!smbd_vfs_init(conn)) {
343 NTSTATUS status = map_nt_error_from_unix(errno);
344 DEBUG(0,("create_conn_struct: smbd_vfs_init failed.\n"));
345 conn_free(conn);
346 return status;
349 /* this must be the first filesystem operation that we do */
350 if (SMB_VFS_CONNECT(conn, servicename, vfs_user) < 0) {
351 DEBUG(0,("VFS connect failed!\n"));
352 conn_free(conn);
353 return NT_STATUS_UNSUCCESSFUL;
356 conn->fs_capabilities = SMB_VFS_FS_CAPABILITIES(conn, &conn->ts_res);
357 *pconn = conn;
359 return NT_STATUS_OK;
362 /********************************************************
363 Fake up a connection struct for the VFS layer, for use in
364 applications (such as the python bindings), that do not want the
365 global working directory changed under them.
367 SMB_VFS_CONNECT requires root privileges.
368 *********************************************************/
370 NTSTATUS create_conn_struct(TALLOC_CTX *ctx,
371 struct tevent_context *ev,
372 struct messaging_context *msg,
373 connection_struct **pconn,
374 int snum,
375 const char *path,
376 const struct auth_session_info *session_info)
378 NTSTATUS status;
379 become_root();
380 status = create_conn_struct_as_root(ctx, ev,
381 msg, pconn,
382 snum, path,
383 session_info);
384 unbecome_root();
386 return status;
389 /********************************************************
390 Fake up a connection struct for the VFS layer.
391 Note: this performs a vfs connect and CHANGES CWD !!!! JRA.
393 The old working directory is returned on *poldcwd, allocated on ctx.
394 *********************************************************/
396 NTSTATUS create_conn_struct_cwd(TALLOC_CTX *ctx,
397 struct tevent_context *ev,
398 struct messaging_context *msg,
399 connection_struct **pconn,
400 int snum,
401 const char *path,
402 const struct auth_session_info *session_info,
403 struct smb_filename **poldcwd_fname)
405 connection_struct *conn;
406 struct smb_filename *oldcwd_fname = NULL;
407 struct smb_filename smb_fname_connectpath = {0};
409 NTSTATUS status = create_conn_struct(ctx, ev,
410 msg, &conn,
411 snum, path,
412 session_info);
413 if (!NT_STATUS_IS_OK(status)) {
414 return status;
418 * Windows seems to insist on doing trans2getdfsreferral() calls on
419 * the IPC$ share as the anonymous user. If we try to chdir as that
420 * user we will fail.... WTF ? JRA.
423 oldcwd_fname = vfs_GetWd(ctx, conn);
424 if (oldcwd_fname == NULL) {
425 status = map_nt_error_from_unix(errno);
426 DEBUG(3, ("vfs_GetWd failed: %s\n", strerror(errno)));
427 conn_free(conn);
428 return status;
431 smb_fname_connectpath = (struct smb_filename) {
432 .base_name = conn->connectpath
435 if (vfs_ChDir(conn, &smb_fname_connectpath) != 0) {
436 status = map_nt_error_from_unix(errno);
437 DEBUG(3,("create_conn_struct: Can't ChDir to new conn path %s. "
438 "Error was %s\n",
439 conn->connectpath, strerror(errno) ));
440 TALLOC_FREE(oldcwd_fname);
441 conn_free(conn);
442 return status;
445 *pconn = conn;
446 *poldcwd_fname = oldcwd_fname;
448 return NT_STATUS_OK;
451 static void shuffle_strlist(char **list, int count)
453 int i;
454 uint32_t r;
455 char *tmp;
457 for (i = count; i > 1; i--) {
458 r = generate_random() % i;
460 tmp = list[i-1];
461 list[i-1] = list[r];
462 list[r] = tmp;
466 /**********************************************************************
467 Parse the contents of a symlink to verify if it is an msdfs referral
468 A valid referral is of the form:
470 msdfs:server1\share1,server2\share2
471 msdfs:server1\share1\pathname,server2\share2\pathname
472 msdfs:server1/share1,server2/share2
473 msdfs:server1/share1/pathname,server2/share2/pathname.
475 Note that the alternate paths returned here must be of the canonicalized
476 form:
478 \server\share or
479 \server\share\path\to\file,
481 even in posix path mode. This is because we have no knowledge if the
482 server we're referring to understands posix paths.
483 **********************************************************************/
485 static bool parse_msdfs_symlink(TALLOC_CTX *ctx,
486 int snum,
487 const char *target,
488 struct referral **preflist,
489 int *refcount)
491 char *temp = NULL;
492 char *prot;
493 char **alt_path = NULL;
494 int count = 0, i;
495 struct referral *reflist;
496 char *saveptr;
498 temp = talloc_strdup(ctx, target);
499 if (!temp) {
500 return False;
502 prot = strtok_r(temp, ":", &saveptr);
503 if (!prot) {
504 DEBUG(0,("parse_msdfs_symlink: invalid path !\n"));
505 return False;
508 alt_path = talloc_array(ctx, char *, MAX_REFERRAL_COUNT);
509 if (!alt_path) {
510 return False;
513 /* parse out the alternate paths */
514 while((count<MAX_REFERRAL_COUNT) &&
515 ((alt_path[count] = strtok_r(NULL, ",", &saveptr)) != NULL)) {
516 count++;
519 /* shuffle alternate paths */
520 if (lp_msdfs_shuffle_referrals(snum)) {
521 shuffle_strlist(alt_path, count);
524 DEBUG(10,("parse_msdfs_symlink: count=%d\n", count));
526 if (count) {
527 reflist = *preflist = talloc_zero_array(ctx,
528 struct referral, count);
529 if(reflist == NULL) {
530 TALLOC_FREE(alt_path);
531 return False;
533 } else {
534 reflist = *preflist = NULL;
537 for(i=0;i<count;i++) {
538 char *p;
540 /* Canonicalize link target.
541 * Replace all /'s in the path by a \ */
542 string_replace(alt_path[i], '/', '\\');
544 /* Remove leading '\\'s */
545 p = alt_path[i];
546 while (*p && (*p == '\\')) {
547 p++;
550 reflist[i].alternate_path = talloc_asprintf(ctx,
551 "\\%s",
553 if (!reflist[i].alternate_path) {
554 return False;
557 reflist[i].proximity = 0;
558 reflist[i].ttl = REFERRAL_TTL;
559 DEBUG(10, ("parse_msdfs_symlink: Created alt path: %s\n",
560 reflist[i].alternate_path));
563 *refcount = count;
565 TALLOC_FREE(alt_path);
566 return True;
569 /**********************************************************************
570 Returns true if the unix path is a valid msdfs symlink and also
571 returns the target string from inside the link.
572 **********************************************************************/
574 static bool is_msdfs_link_internal(TALLOC_CTX *ctx,
575 connection_struct *conn,
576 struct smb_filename *smb_fname,
577 char **pp_link_target)
579 int referral_len = 0;
580 #if defined(HAVE_BROKEN_READLINK)
581 char link_target_buf[PATH_MAX];
582 #else
583 char link_target_buf[7];
584 #endif
585 size_t bufsize = 0;
586 char *link_target = NULL;
588 if (pp_link_target) {
589 bufsize = 1024;
590 link_target = talloc_array(ctx, char, bufsize);
591 if (!link_target) {
592 return False;
594 *pp_link_target = link_target;
595 } else {
596 bufsize = sizeof(link_target_buf);
597 link_target = link_target_buf;
600 if (SMB_VFS_LSTAT(conn, smb_fname) != 0) {
601 DEBUG(5,("is_msdfs_link_read_target: %s does not exist.\n",
602 smb_fname->base_name));
603 goto err;
605 if (!S_ISLNK(smb_fname->st.st_ex_mode)) {
606 DEBUG(5,("is_msdfs_link_read_target: %s is not a link.\n",
607 smb_fname->base_name));
608 goto err;
611 referral_len = SMB_VFS_READLINK(conn, smb_fname,
612 link_target, bufsize - 1);
613 if (referral_len == -1) {
614 DEBUG(0,("is_msdfs_link_read_target: Error reading "
615 "msdfs link %s: %s\n",
616 smb_fname->base_name, strerror(errno)));
617 goto err;
619 link_target[referral_len] = '\0';
621 DEBUG(5,("is_msdfs_link_internal: %s -> %s\n", smb_fname->base_name,
622 link_target));
624 if (!strnequal(link_target, "msdfs:", 6)) {
625 goto err;
627 return True;
629 err:
631 if (link_target != link_target_buf) {
632 TALLOC_FREE(link_target);
634 return False;
637 /**********************************************************************
638 Returns true if the unix path is a valid msdfs symlink.
639 **********************************************************************/
641 bool is_msdfs_link(connection_struct *conn,
642 struct smb_filename *smb_fname)
644 return is_msdfs_link_internal(talloc_tos(),
645 conn,
646 smb_fname,
647 NULL);
650 /*****************************************************************
651 Used by other functions to decide if a dfs path is remote,
652 and to get the list of referred locations for that remote path.
654 search_flag: For findfirsts, dfs links themselves are not
655 redirected, but paths beyond the links are. For normal smb calls,
656 even dfs links need to be redirected.
658 consumedcntp: how much of the dfs path is being redirected. the client
659 should try the remaining path on the redirected server.
661 If this returns NT_STATUS_PATH_NOT_COVERED the contents of the msdfs
662 link redirect are in targetpath.
663 *****************************************************************/
665 static NTSTATUS dfs_path_lookup(TALLOC_CTX *ctx,
666 connection_struct *conn,
667 const char *dfspath, /* Incoming complete dfs path */
668 const struct dfs_path *pdp, /* Parsed out
669 server+share+extrapath. */
670 uint32_t ucf_flags,
671 int *consumedcntp,
672 char **pp_targetpath)
674 char *p = NULL;
675 char *q = NULL;
676 NTSTATUS status;
677 struct smb_filename *smb_fname = NULL;
678 char *canon_dfspath = NULL; /* Canonicalized dfs path. (only '/'
679 components). */
681 DEBUG(10,("dfs_path_lookup: Conn path = %s reqpath = %s\n",
682 conn->connectpath, pdp->reqpath));
685 * Note the unix path conversion here we're doing we
686 * throw away. We're looking for a symlink for a dfs
687 * resolution, if we don't find it we'll do another
688 * unix_convert later in the codepath.
691 status = unix_convert(ctx, conn, pdp->reqpath, &smb_fname,
692 ucf_flags);
694 if (!NT_STATUS_IS_OK(status)) {
695 if (!NT_STATUS_EQUAL(status,
696 NT_STATUS_OBJECT_PATH_NOT_FOUND)) {
697 return status;
699 if (smb_fname == NULL || smb_fname->base_name == NULL) {
700 return status;
704 /* Optimization - check if we can redirect the whole path. */
706 if (is_msdfs_link_internal(ctx, conn, smb_fname, pp_targetpath)) {
707 /* XX_ALLOW_WCARD_XXX is called from search functions. */
708 if (ucf_flags &
709 (UCF_COND_ALLOW_WCARD_LCOMP|
710 UCF_ALWAYS_ALLOW_WCARD_LCOMP)) {
711 DEBUG(6,("dfs_path_lookup (FindFirst) No redirection "
712 "for dfs link %s.\n", dfspath));
713 status = NT_STATUS_OK;
714 goto out;
717 DEBUG(6,("dfs_path_lookup: %s resolves to a "
718 "valid dfs link %s.\n", dfspath,
719 pp_targetpath ? *pp_targetpath : ""));
721 if (consumedcntp) {
722 *consumedcntp = strlen(dfspath);
724 status = NT_STATUS_PATH_NOT_COVERED;
725 goto out;
728 /* Prepare to test only for '/' components in the given path,
729 * so if a Windows path replace all '\\' characters with '/'.
730 * For a POSIX DFS path we know all separators are already '/'. */
732 canon_dfspath = talloc_strdup(ctx, dfspath);
733 if (!canon_dfspath) {
734 status = NT_STATUS_NO_MEMORY;
735 goto out;
737 if (!pdp->posix_path) {
738 string_replace(canon_dfspath, '\\', '/');
742 * localpath comes out of unix_convert, so it has
743 * no trailing backslash. Make sure that canon_dfspath hasn't either.
744 * Fix for bug #4860 from Jan Martin <Jan.Martin@rwedea.com>.
747 trim_char(canon_dfspath,0,'/');
750 * Redirect if any component in the path is a link.
751 * We do this by walking backwards through the
752 * local path, chopping off the last component
753 * in both the local path and the canonicalized
754 * DFS path. If we hit a DFS link then we're done.
757 p = strrchr_m(smb_fname->base_name, '/');
758 if (consumedcntp) {
759 q = strrchr_m(canon_dfspath, '/');
762 while (p) {
763 *p = '\0';
764 if (q) {
765 *q = '\0';
768 if (is_msdfs_link_internal(ctx, conn,
769 smb_fname, pp_targetpath)) {
770 DEBUG(4, ("dfs_path_lookup: Redirecting %s because "
771 "parent %s is dfs link\n", dfspath,
772 smb_fname_str_dbg(smb_fname)));
774 if (consumedcntp) {
775 *consumedcntp = strlen(canon_dfspath);
776 DEBUG(10, ("dfs_path_lookup: Path consumed: %s "
777 "(%d)\n",
778 canon_dfspath,
779 *consumedcntp));
782 status = NT_STATUS_PATH_NOT_COVERED;
783 goto out;
786 /* Step back on the filesystem. */
787 p = strrchr_m(smb_fname->base_name, '/');
789 if (consumedcntp) {
790 /* And in the canonicalized dfs path. */
791 q = strrchr_m(canon_dfspath, '/');
795 status = NT_STATUS_OK;
796 out:
797 TALLOC_FREE(smb_fname);
798 return status;
801 /*****************************************************************
802 Decides if a dfs pathname should be redirected or not.
803 If not, the pathname is converted to a tcon-relative local unix path
805 search_wcard_flag: this flag performs 2 functions both related
806 to searches. See resolve_dfs_path() and parse_dfs_path_XX()
807 for details.
809 This function can return NT_STATUS_OK, meaning use the returned path as-is
810 (mapped into a local path).
811 or NT_STATUS_NOT_COVERED meaning return a DFS redirect, or
812 any other NT_STATUS error which is a genuine error to be
813 returned to the client.
814 *****************************************************************/
816 static NTSTATUS dfs_redirect(TALLOC_CTX *ctx,
817 connection_struct *conn,
818 const char *path_in,
819 uint32_t ucf_flags,
820 bool allow_broken_path,
821 char **pp_path_out,
822 bool *ppath_contains_wcard)
824 NTSTATUS status;
825 bool search_wcard_flag = (ucf_flags &
826 (UCF_COND_ALLOW_WCARD_LCOMP|UCF_ALWAYS_ALLOW_WCARD_LCOMP));
827 struct dfs_path *pdp = talloc(ctx, struct dfs_path);
829 if (!pdp) {
830 return NT_STATUS_NO_MEMORY;
833 status = parse_dfs_path(conn, path_in, search_wcard_flag,
834 allow_broken_path, pdp,
835 ppath_contains_wcard);
836 if (!NT_STATUS_IS_OK(status)) {
837 TALLOC_FREE(pdp);
838 return status;
841 if (pdp->reqpath[0] == '\0') {
842 TALLOC_FREE(pdp);
843 *pp_path_out = talloc_strdup(ctx, "");
844 if (!*pp_path_out) {
845 return NT_STATUS_NO_MEMORY;
847 DEBUG(5,("dfs_redirect: self-referral.\n"));
848 return NT_STATUS_OK;
851 /* If dfs pathname for a non-dfs share, convert to tcon-relative
852 path and return OK */
854 if (!lp_msdfs_root(SNUM(conn))) {
855 *pp_path_out = talloc_strdup(ctx, pdp->reqpath);
856 TALLOC_FREE(pdp);
857 if (!*pp_path_out) {
858 return NT_STATUS_NO_MEMORY;
860 return NT_STATUS_OK;
863 /* If it looked like a local path (zero hostname/servicename)
864 * just treat as a tcon-relative path. */
866 if (pdp->hostname[0] == '\0' && pdp->servicename[0] == '\0') {
867 *pp_path_out = talloc_strdup(ctx, pdp->reqpath);
868 TALLOC_FREE(pdp);
869 if (!*pp_path_out) {
870 return NT_STATUS_NO_MEMORY;
872 return NT_STATUS_OK;
875 if (!( strequal(pdp->servicename, lp_servicename(talloc_tos(), SNUM(conn)))
876 || (strequal(pdp->servicename, HOMES_NAME)
877 && strequal(lp_servicename(talloc_tos(), SNUM(conn)),
878 conn->session_info->unix_info->sanitized_username) )) ) {
880 /* The given sharename doesn't match this connection. */
881 TALLOC_FREE(pdp);
883 return NT_STATUS_OBJECT_PATH_NOT_FOUND;
886 status = dfs_path_lookup(ctx, conn, path_in, pdp,
887 ucf_flags, NULL, NULL);
888 if (!NT_STATUS_IS_OK(status)) {
889 if (NT_STATUS_EQUAL(status, NT_STATUS_PATH_NOT_COVERED)) {
890 DEBUG(3,("dfs_redirect: Redirecting %s\n", path_in));
891 } else {
892 DEBUG(10,("dfs_redirect: dfs_path_lookup "
893 "failed for %s with %s\n",
894 path_in, nt_errstr(status) ));
896 return status;
899 DEBUG(3,("dfs_redirect: Not redirecting %s.\n", path_in));
901 /* Form non-dfs tcon-relative path */
902 *pp_path_out = talloc_strdup(ctx, pdp->reqpath);
903 TALLOC_FREE(pdp);
904 if (!*pp_path_out) {
905 return NT_STATUS_NO_MEMORY;
908 DEBUG(3,("dfs_redirect: Path %s converted to non-dfs path %s\n",
909 path_in,
910 *pp_path_out));
912 return NT_STATUS_OK;
915 /**********************************************************************
916 Return a self referral.
917 **********************************************************************/
919 static NTSTATUS self_ref(TALLOC_CTX *ctx,
920 const char *dfs_path,
921 struct junction_map *jucn,
922 int *consumedcntp,
923 bool *self_referralp)
925 struct referral *ref;
927 *self_referralp = True;
929 jucn->referral_count = 1;
930 if((ref = talloc_zero(ctx, struct referral)) == NULL) {
931 return NT_STATUS_NO_MEMORY;
934 ref->alternate_path = talloc_strdup(ctx, dfs_path);
935 if (!ref->alternate_path) {
936 TALLOC_FREE(ref);
937 return NT_STATUS_NO_MEMORY;
939 ref->proximity = 0;
940 ref->ttl = REFERRAL_TTL;
941 jucn->referral_list = ref;
942 *consumedcntp = strlen(dfs_path);
943 return NT_STATUS_OK;
946 /**********************************************************************
947 Gets valid referrals for a dfs path and fills up the
948 junction_map structure.
949 **********************************************************************/
951 NTSTATUS get_referred_path(TALLOC_CTX *ctx,
952 const char *dfs_path,
953 const struct tsocket_address *remote_address,
954 const struct tsocket_address *local_address,
955 bool allow_broken_path,
956 struct junction_map *jucn,
957 int *consumedcntp,
958 bool *self_referralp)
960 struct connection_struct *conn;
961 char *targetpath = NULL;
962 int snum;
963 NTSTATUS status = NT_STATUS_NOT_FOUND;
964 bool dummy;
965 struct dfs_path *pdp = talloc(ctx, struct dfs_path);
966 struct smb_filename *oldcwd_fname = NULL;
968 if (!pdp) {
969 return NT_STATUS_NO_MEMORY;
972 *self_referralp = False;
974 status = parse_dfs_path(NULL, dfs_path, False, allow_broken_path,
975 pdp, &dummy);
976 if (!NT_STATUS_IS_OK(status)) {
977 return status;
980 jucn->service_name = talloc_strdup(ctx, pdp->servicename);
981 jucn->volume_name = talloc_strdup(ctx, pdp->reqpath);
982 if (!jucn->service_name || !jucn->volume_name) {
983 TALLOC_FREE(pdp);
984 return NT_STATUS_NO_MEMORY;
987 /* Verify the share is a dfs root */
988 snum = lp_servicenumber(jucn->service_name);
989 if(snum < 0) {
990 char *service_name = NULL;
991 if ((snum = find_service(ctx, jucn->service_name, &service_name)) < 0) {
992 return NT_STATUS_NOT_FOUND;
994 if (!service_name) {
995 return NT_STATUS_NO_MEMORY;
997 TALLOC_FREE(jucn->service_name);
998 jucn->service_name = talloc_strdup(ctx, service_name);
999 if (!jucn->service_name) {
1000 TALLOC_FREE(pdp);
1001 return NT_STATUS_NO_MEMORY;
1005 if (!lp_msdfs_root(snum) && (*lp_msdfs_proxy(talloc_tos(), snum) == '\0')) {
1006 DEBUG(3,("get_referred_path: |%s| in dfs path %s is not "
1007 "a dfs root.\n",
1008 pdp->servicename, dfs_path));
1009 TALLOC_FREE(pdp);
1010 return NT_STATUS_NOT_FOUND;
1014 * Self referrals are tested with a anonymous IPC connection and
1015 * a GET_DFS_REFERRAL call to \\server\share. (which means
1016 * dp.reqpath[0] points to an empty string). create_conn_struct cd's
1017 * into the directory and will fail if it cannot (as the anonymous
1018 * user). Cope with this.
1021 if (pdp->reqpath[0] == '\0') {
1022 char *tmp;
1023 struct referral *ref;
1024 int refcount;
1026 if (*lp_msdfs_proxy(talloc_tos(), snum) == '\0') {
1027 TALLOC_FREE(pdp);
1028 return self_ref(ctx,
1029 dfs_path,
1030 jucn,
1031 consumedcntp,
1032 self_referralp);
1036 * It's an msdfs proxy share. Redirect to
1037 * the configured target share.
1040 tmp = talloc_asprintf(talloc_tos(), "msdfs:%s",
1041 lp_msdfs_proxy(talloc_tos(), snum));
1042 if (tmp == NULL) {
1043 TALLOC_FREE(pdp);
1044 return NT_STATUS_NO_MEMORY;
1047 if (!parse_msdfs_symlink(ctx, snum, tmp, &ref, &refcount)) {
1048 TALLOC_FREE(tmp);
1049 TALLOC_FREE(pdp);
1050 return NT_STATUS_INVALID_PARAMETER;
1052 TALLOC_FREE(tmp);
1053 jucn->referral_count = refcount;
1054 jucn->referral_list = ref;
1055 *consumedcntp = strlen(dfs_path);
1056 TALLOC_FREE(pdp);
1057 return NT_STATUS_OK;
1060 status = create_conn_struct_cwd(ctx,
1061 server_event_context(),
1062 server_messaging_context(),
1063 &conn,
1064 snum,
1065 lp_path(talloc_tos(), snum),
1066 NULL,
1067 &oldcwd_fname);
1068 if (!NT_STATUS_IS_OK(status)) {
1069 TALLOC_FREE(pdp);
1070 return status;
1074 * TODO
1076 * The remote and local address should be passed down to
1077 * create_conn_struct_cwd.
1079 if (conn->sconn->remote_address == NULL) {
1080 conn->sconn->remote_address =
1081 tsocket_address_copy(remote_address, conn->sconn);
1082 if (conn->sconn->remote_address == NULL) {
1083 TALLOC_FREE(pdp);
1084 return NT_STATUS_NO_MEMORY;
1087 if (conn->sconn->local_address == NULL) {
1088 conn->sconn->local_address =
1089 tsocket_address_copy(local_address, conn->sconn);
1090 if (conn->sconn->local_address == NULL) {
1091 TALLOC_FREE(pdp);
1092 return NT_STATUS_NO_MEMORY;
1096 /* If this is a DFS path dfs_lookup should return
1097 * NT_STATUS_PATH_NOT_COVERED. */
1099 status = dfs_path_lookup(ctx, conn, dfs_path, pdp,
1100 0, consumedcntp, &targetpath);
1102 if (!NT_STATUS_EQUAL(status, NT_STATUS_PATH_NOT_COVERED)) {
1103 DEBUG(3,("get_referred_path: No valid referrals for path %s\n",
1104 dfs_path));
1105 if (NT_STATUS_IS_OK(status)) {
1107 * We are in an error path here (we
1108 * know it's not a DFS path), but
1109 * dfs_path_lookup() can return
1110 * NT_STATUS_OK. Ensure we always
1111 * return a valid error code.
1113 * #9588 - ACLs are not inherited to directories
1114 * for DFS shares.
1116 status = NT_STATUS_NOT_FOUND;
1118 goto err_exit;
1121 /* We know this is a valid dfs link. Parse the targetpath. */
1122 if (!parse_msdfs_symlink(ctx, snum, targetpath,
1123 &jucn->referral_list,
1124 &jucn->referral_count)) {
1125 DEBUG(3,("get_referred_path: failed to parse symlink "
1126 "target %s\n", targetpath ));
1127 status = NT_STATUS_NOT_FOUND;
1128 goto err_exit;
1131 status = NT_STATUS_OK;
1132 err_exit:
1133 vfs_ChDir(conn, oldcwd_fname);
1134 TALLOC_FREE(oldcwd_fname);
1135 SMB_VFS_DISCONNECT(conn);
1136 conn_free(conn);
1137 TALLOC_FREE(pdp);
1138 return status;
1141 /******************************************************************
1142 Set up the DFS referral for the dfs pathname. This call returns
1143 the amount of the path covered by this server, and where the
1144 client should be redirected to. This is the meat of the
1145 TRANS2_GET_DFS_REFERRAL call.
1146 ******************************************************************/
1148 int setup_dfs_referral(connection_struct *orig_conn,
1149 const char *dfs_path,
1150 int max_referral_level,
1151 char **ppdata, NTSTATUS *pstatus)
1153 char *pdata = *ppdata;
1154 int reply_size = 0;
1155 struct dfs_GetDFSReferral *r;
1156 DATA_BLOB blob = data_blob_null;
1157 NTSTATUS status;
1158 enum ndr_err_code ndr_err;
1160 r = talloc_zero(talloc_tos(), struct dfs_GetDFSReferral);
1161 if (r == NULL) {
1162 *pstatus = NT_STATUS_NO_MEMORY;
1163 return -1;
1166 r->in.req.max_referral_level = max_referral_level;
1167 r->in.req.servername = talloc_strdup(r, dfs_path);
1168 if (r->in.req.servername == NULL) {
1169 talloc_free(r);
1170 *pstatus = NT_STATUS_NO_MEMORY;
1171 return -1;
1174 status = SMB_VFS_GET_DFS_REFERRALS(orig_conn, r);
1175 if (!NT_STATUS_IS_OK(status)) {
1176 talloc_free(r);
1177 *pstatus = status;
1178 return -1;
1181 ndr_err = ndr_push_struct_blob(&blob, r,
1182 r->out.resp,
1183 (ndr_push_flags_fn_t)ndr_push_dfs_referral_resp);
1184 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1185 TALLOC_FREE(r);
1186 *pstatus = NT_STATUS_INVALID_PARAMETER;
1187 return -1;
1190 pdata = (char *)SMB_REALLOC(pdata, blob.length);
1191 if(pdata == NULL) {
1192 TALLOC_FREE(r);
1193 DEBUG(0,("referral setup:"
1194 "malloc failed for Realloc!\n"));
1195 return -1;
1197 *ppdata = pdata;
1198 reply_size = blob.length;
1199 memcpy(pdata, blob.data, blob.length);
1200 TALLOC_FREE(r);
1202 *pstatus = NT_STATUS_OK;
1203 return reply_size;
1206 /**********************************************************************
1207 The following functions are called by the NETDFS RPC pipe functions
1208 **********************************************************************/
1210 /*********************************************************************
1211 Creates a junction structure from a DFS pathname
1212 **********************************************************************/
1214 bool create_junction(TALLOC_CTX *ctx,
1215 const char *dfs_path,
1216 bool allow_broken_path,
1217 struct junction_map *jucn)
1219 int snum;
1220 bool dummy;
1221 struct dfs_path *pdp = talloc(ctx,struct dfs_path);
1222 NTSTATUS status;
1224 if (!pdp) {
1225 return False;
1227 status = parse_dfs_path(NULL, dfs_path, False, allow_broken_path,
1228 pdp, &dummy);
1229 if (!NT_STATUS_IS_OK(status)) {
1230 return False;
1233 /* check if path is dfs : validate first token */
1234 if (!is_myname_or_ipaddr(pdp->hostname)) {
1235 DEBUG(4,("create_junction: Invalid hostname %s "
1236 "in dfs path %s\n",
1237 pdp->hostname, dfs_path));
1238 TALLOC_FREE(pdp);
1239 return False;
1242 /* Check for a non-DFS share */
1243 snum = lp_servicenumber(pdp->servicename);
1245 if(snum < 0 || !lp_msdfs_root(snum)) {
1246 DEBUG(4,("create_junction: %s is not an msdfs root.\n",
1247 pdp->servicename));
1248 TALLOC_FREE(pdp);
1249 return False;
1252 jucn->service_name = talloc_strdup(ctx, pdp->servicename);
1253 jucn->volume_name = talloc_strdup(ctx, pdp->reqpath);
1254 jucn->comment = lp_comment(ctx, snum);
1256 TALLOC_FREE(pdp);
1257 if (!jucn->service_name || !jucn->volume_name || ! jucn->comment) {
1258 return False;
1260 return True;
1263 /**********************************************************************
1264 Forms a valid Unix pathname from the junction
1265 **********************************************************************/
1267 static bool junction_to_local_path(const struct junction_map *jucn,
1268 char **pp_path_out,
1269 connection_struct **conn_out,
1270 struct smb_filename **oldpath_fname)
1272 int snum;
1273 NTSTATUS status;
1275 snum = lp_servicenumber(jucn->service_name);
1276 if(snum < 0) {
1277 return False;
1279 status = create_conn_struct_cwd(talloc_tos(),
1280 server_event_context(),
1281 server_messaging_context(),
1282 conn_out,
1283 snum,
1284 lp_path(talloc_tos(), snum),
1285 NULL,
1286 oldpath_fname);
1287 if (!NT_STATUS_IS_OK(status)) {
1288 return False;
1291 *pp_path_out = talloc_asprintf(*conn_out,
1292 "%s/%s",
1293 lp_path(talloc_tos(), snum),
1294 jucn->volume_name);
1295 if (!*pp_path_out) {
1296 vfs_ChDir(*conn_out, *oldpath_fname);
1297 SMB_VFS_DISCONNECT(*conn_out);
1298 conn_free(*conn_out);
1299 return False;
1301 return True;
1304 bool create_msdfs_link(const struct junction_map *jucn)
1306 char *path = NULL;
1307 struct smb_filename *cwd_fname = NULL;
1308 char *msdfs_link = NULL;
1309 connection_struct *conn;
1310 int i=0;
1311 bool insert_comma = False;
1312 bool ret = False;
1313 struct smb_filename *smb_fname = NULL;
1315 if(!junction_to_local_path(jucn, &path, &conn, &cwd_fname)) {
1316 return False;
1319 /* Form the msdfs_link contents */
1320 msdfs_link = talloc_strdup(conn, "msdfs:");
1321 if (!msdfs_link) {
1322 goto out;
1324 for(i=0; i<jucn->referral_count; i++) {
1325 char *refpath = jucn->referral_list[i].alternate_path;
1327 /* Alternate paths always use Windows separators. */
1328 trim_char(refpath, '\\', '\\');
1329 if(*refpath == '\0') {
1330 if (i == 0) {
1331 insert_comma = False;
1333 continue;
1335 if (i > 0 && insert_comma) {
1336 msdfs_link = talloc_asprintf_append_buffer(msdfs_link,
1337 ",%s",
1338 refpath);
1339 } else {
1340 msdfs_link = talloc_asprintf_append_buffer(msdfs_link,
1341 "%s",
1342 refpath);
1345 if (!msdfs_link) {
1346 goto out;
1348 if (!insert_comma) {
1349 insert_comma = True;
1353 DEBUG(5,("create_msdfs_link: Creating new msdfs link: %s -> %s\n",
1354 path, msdfs_link));
1356 smb_fname = synthetic_smb_fname(talloc_tos(),
1357 path,
1358 NULL,
1359 NULL,
1361 if (smb_fname == NULL) {
1362 errno = ENOMEM;
1363 goto out;
1366 if(SMB_VFS_SYMLINK(conn, msdfs_link, smb_fname) < 0) {
1367 if (errno == EEXIST) {
1368 if(SMB_VFS_UNLINK(conn, smb_fname)!=0) {
1369 TALLOC_FREE(smb_fname);
1370 goto out;
1373 if (SMB_VFS_SYMLINK(conn, msdfs_link, smb_fname) < 0) {
1374 DEBUG(1,("create_msdfs_link: symlink failed "
1375 "%s -> %s\nError: %s\n",
1376 path, msdfs_link, strerror(errno)));
1377 goto out;
1381 ret = True;
1383 out:
1384 TALLOC_FREE(smb_fname);
1385 vfs_ChDir(conn, cwd_fname);
1386 TALLOC_FREE(cwd_fname);
1387 SMB_VFS_DISCONNECT(conn);
1388 conn_free(conn);
1389 return ret;
1392 bool remove_msdfs_link(const struct junction_map *jucn)
1394 char *path = NULL;
1395 struct smb_filename *cwd_fname = NULL;
1396 connection_struct *conn;
1397 bool ret = False;
1398 struct smb_filename *smb_fname;
1400 if (!junction_to_local_path(jucn, &path, &conn, &cwd_fname)) {
1401 return false;
1404 smb_fname = synthetic_smb_fname(talloc_tos(),
1405 path,
1406 NULL,
1407 NULL,
1409 if (smb_fname == NULL) {
1410 errno = ENOMEM;
1411 return false;
1414 if( SMB_VFS_UNLINK(conn, smb_fname) == 0 ) {
1415 ret = True;
1418 TALLOC_FREE(smb_fname);
1419 vfs_ChDir(conn, cwd_fname);
1420 TALLOC_FREE(cwd_fname);
1421 SMB_VFS_DISCONNECT(conn);
1422 conn_free(conn);
1423 return ret;
1426 /*********************************************************************
1427 Return the number of DFS links at the root of this share.
1428 *********************************************************************/
1430 static int count_dfs_links(TALLOC_CTX *ctx, int snum)
1432 size_t cnt = 0;
1433 DIR *dirp = NULL;
1434 const char *dname = NULL;
1435 char *talloced = NULL;
1436 const char *connect_path = lp_path(talloc_tos(), snum);
1437 const char *msdfs_proxy = lp_msdfs_proxy(talloc_tos(), snum);
1438 connection_struct *conn;
1439 NTSTATUS status;
1440 struct smb_filename *cwd_fname = NULL;
1441 struct smb_filename *smb_fname = NULL;
1443 if(*connect_path == '\0') {
1444 return 0;
1448 * Fake up a connection struct for the VFS layer.
1451 status = create_conn_struct_cwd(talloc_tos(),
1452 server_event_context(),
1453 server_messaging_context(),
1454 &conn,
1455 snum,
1456 connect_path,
1457 NULL,
1458 &cwd_fname);
1459 if (!NT_STATUS_IS_OK(status)) {
1460 DEBUG(3, ("create_conn_struct failed: %s\n",
1461 nt_errstr(status)));
1462 return 0;
1465 /* Count a link for the msdfs root - convention */
1466 cnt = 1;
1468 /* No more links if this is an msdfs proxy. */
1469 if (*msdfs_proxy != '\0') {
1470 goto out;
1473 smb_fname = synthetic_smb_fname(talloc_tos(),
1474 ".",
1475 NULL,
1476 NULL,
1478 if (smb_fname == NULL) {
1479 goto out;
1482 /* Now enumerate all dfs links */
1483 dirp = SMB_VFS_OPENDIR(conn, smb_fname, NULL, 0);
1484 if(!dirp) {
1485 goto out;
1488 while ((dname = vfs_readdirname(conn, dirp, NULL, &talloced))
1489 != NULL) {
1490 struct smb_filename *smb_dname =
1491 synthetic_smb_fname(talloc_tos(),
1492 dname,
1493 NULL,
1494 NULL,
1496 if (smb_dname == NULL) {
1497 goto out;
1499 if (is_msdfs_link(conn, smb_dname)) {
1500 cnt++;
1502 TALLOC_FREE(talloced);
1503 TALLOC_FREE(smb_dname);
1506 SMB_VFS_CLOSEDIR(conn,dirp);
1508 out:
1509 TALLOC_FREE(smb_fname);
1510 vfs_ChDir(conn, cwd_fname);
1511 TALLOC_FREE(cwd_fname);
1512 SMB_VFS_DISCONNECT(conn);
1513 conn_free(conn);
1514 return cnt;
1517 /*********************************************************************
1518 *********************************************************************/
1520 static int form_junctions(TALLOC_CTX *ctx,
1521 int snum,
1522 struct junction_map *jucn,
1523 size_t jn_remain)
1525 size_t cnt = 0;
1526 DIR *dirp = NULL;
1527 const char *dname = NULL;
1528 char *talloced = NULL;
1529 const char *connect_path = lp_path(talloc_tos(), snum);
1530 char *service_name = lp_servicename(talloc_tos(), snum);
1531 const char *msdfs_proxy = lp_msdfs_proxy(talloc_tos(), snum);
1532 connection_struct *conn;
1533 struct referral *ref = NULL;
1534 struct smb_filename *cwd_fname = NULL;
1535 struct smb_filename *smb_fname = NULL;
1536 NTSTATUS status;
1538 if (jn_remain == 0) {
1539 return 0;
1542 if(*connect_path == '\0') {
1543 return 0;
1547 * Fake up a connection struct for the VFS layer.
1550 status = create_conn_struct_cwd(ctx,
1551 server_event_context(),
1552 server_messaging_context(),
1553 &conn,
1554 snum,
1555 connect_path,
1556 NULL,
1557 &cwd_fname);
1558 if (!NT_STATUS_IS_OK(status)) {
1559 DEBUG(3, ("create_conn_struct failed: %s\n",
1560 nt_errstr(status)));
1561 return 0;
1564 /* form a junction for the msdfs root - convention
1565 DO NOT REMOVE THIS: NT clients will not work with us
1566 if this is not present
1568 jucn[cnt].service_name = talloc_strdup(ctx,service_name);
1569 jucn[cnt].volume_name = talloc_strdup(ctx, "");
1570 if (!jucn[cnt].service_name || !jucn[cnt].volume_name) {
1571 goto out;
1573 jucn[cnt].comment = "";
1574 jucn[cnt].referral_count = 1;
1576 ref = jucn[cnt].referral_list = talloc_zero(ctx, struct referral);
1577 if (jucn[cnt].referral_list == NULL) {
1578 goto out;
1581 ref->proximity = 0;
1582 ref->ttl = REFERRAL_TTL;
1583 if (*msdfs_proxy != '\0') {
1584 ref->alternate_path = talloc_strdup(ctx,
1585 msdfs_proxy);
1586 } else {
1587 ref->alternate_path = talloc_asprintf(ctx,
1588 "\\\\%s\\%s",
1589 get_local_machine_name(),
1590 service_name);
1593 if (!ref->alternate_path) {
1594 goto out;
1596 cnt++;
1598 /* Don't enumerate if we're an msdfs proxy. */
1599 if (*msdfs_proxy != '\0') {
1600 goto out;
1603 smb_fname = synthetic_smb_fname(talloc_tos(),
1604 ".",
1605 NULL,
1606 NULL,
1608 if (smb_fname == NULL) {
1609 goto out;
1612 /* Now enumerate all dfs links */
1613 dirp = SMB_VFS_OPENDIR(conn, smb_fname, NULL, 0);
1614 if(!dirp) {
1615 goto out;
1618 while ((dname = vfs_readdirname(conn, dirp, NULL, &talloced))
1619 != NULL) {
1620 char *link_target = NULL;
1621 struct smb_filename *smb_dname = NULL;
1623 if (cnt >= jn_remain) {
1624 DEBUG(2, ("form_junctions: ran out of MSDFS "
1625 "junction slots"));
1626 TALLOC_FREE(talloced);
1627 goto out;
1629 smb_dname = synthetic_smb_fname(talloc_tos(),
1630 dname,
1631 NULL,
1632 NULL,
1634 if (smb_dname == NULL) {
1635 TALLOC_FREE(talloced);
1636 goto out;
1638 if (is_msdfs_link_internal(ctx,
1639 conn,
1640 smb_dname, &link_target)) {
1641 if (parse_msdfs_symlink(ctx, snum,
1642 link_target,
1643 &jucn[cnt].referral_list,
1644 &jucn[cnt].referral_count)) {
1646 jucn[cnt].service_name = talloc_strdup(ctx,
1647 service_name);
1648 jucn[cnt].volume_name = talloc_strdup(ctx,
1649 dname);
1650 if (!jucn[cnt].service_name ||
1651 !jucn[cnt].volume_name) {
1652 TALLOC_FREE(talloced);
1653 goto out;
1655 jucn[cnt].comment = "";
1656 cnt++;
1658 TALLOC_FREE(link_target);
1660 TALLOC_FREE(talloced);
1661 TALLOC_FREE(smb_dname);
1664 out:
1666 if (dirp) {
1667 SMB_VFS_CLOSEDIR(conn,dirp);
1670 TALLOC_FREE(smb_fname);
1671 vfs_ChDir(conn, cwd_fname);
1672 TALLOC_FREE(cwd_fname);
1673 conn_free(conn);
1674 return cnt;
1677 struct junction_map *enum_msdfs_links(TALLOC_CTX *ctx, size_t *p_num_jn)
1679 struct junction_map *jn = NULL;
1680 int i=0;
1681 size_t jn_count = 0;
1682 int sharecount = 0;
1684 *p_num_jn = 0;
1685 if(!lp_host_msdfs()) {
1686 return NULL;
1689 /* Ensure all the usershares are loaded. */
1690 become_root();
1691 load_registry_shares();
1692 sharecount = load_usershare_shares(NULL, connections_snum_used);
1693 unbecome_root();
1695 for(i=0;i < sharecount;i++) {
1696 if(lp_msdfs_root(i)) {
1697 jn_count += count_dfs_links(ctx, i);
1700 if (jn_count == 0) {
1701 return NULL;
1703 jn = talloc_array(ctx, struct junction_map, jn_count);
1704 if (!jn) {
1705 return NULL;
1707 for(i=0; i < sharecount; i++) {
1708 if (*p_num_jn >= jn_count) {
1709 break;
1711 if(lp_msdfs_root(i)) {
1712 *p_num_jn += form_junctions(ctx, i,
1713 &jn[*p_num_jn],
1714 jn_count - *p_num_jn);
1717 return jn;
1720 /******************************************************************************
1721 Core function to resolve a dfs pathname possibly containing a wildcard. If
1722 ppath_contains_wcard != NULL, it will be set to true if a wildcard is
1723 detected during dfs resolution.
1724 ******************************************************************************/
1726 NTSTATUS resolve_dfspath_wcard(TALLOC_CTX *ctx,
1727 connection_struct *conn,
1728 const char *name_in,
1729 uint32_t ucf_flags,
1730 bool allow_broken_path,
1731 char **pp_name_out,
1732 bool *ppath_contains_wcard)
1734 bool path_contains_wcard = false;
1735 NTSTATUS status = NT_STATUS_OK;
1737 status = dfs_redirect(ctx,
1738 conn,
1739 name_in,
1740 ucf_flags,
1741 allow_broken_path,
1742 pp_name_out,
1743 &path_contains_wcard);
1745 if (NT_STATUS_IS_OK(status) &&
1746 ppath_contains_wcard != NULL &&
1747 path_contains_wcard) {
1748 *ppath_contains_wcard = path_contains_wcard;
1750 return status;