s3: libsmb: Make a comment note that cli_set_ea() needs some internal changes before...
[Samba.git] / source3 / smbd / msdfs.c
blob51e3dff26787d57ff38ece466975d9bb20d53c47
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"
35 /**********************************************************************
36 Parse a DFS pathname of the form \hostname\service\reqpath
37 into the dfs_path structure.
38 If POSIX pathnames is true, the pathname may also be of the
39 form /hostname/service/reqpath.
40 We cope with either here.
42 Unfortunately, due to broken clients who might set the
43 SVAL(inbuf,smb_flg2) & FLAGS2_DFS_PATHNAMES bit and then
44 send a local path, we have to cope with that too....
46 If conn != NULL then ensure the provided service is
47 the one pointed to by the connection.
49 This version does everything using pointers within one copy of the
50 pathname string, talloced on the struct dfs_path pointer (which
51 must be talloced). This may be too clever to live....
52 JRA.
53 **********************************************************************/
55 static NTSTATUS parse_dfs_path(connection_struct *conn,
56 const char *pathname,
57 bool allow_wcards,
58 bool allow_broken_path,
59 struct dfs_path *pdp, /* MUST BE TALLOCED */
60 bool *ppath_contains_wcard)
62 char *pathname_local;
63 char *p,*temp;
64 char *servicename;
65 char *eos_ptr;
66 NTSTATUS status = NT_STATUS_OK;
67 char sepchar;
69 ZERO_STRUCTP(pdp);
72 * This is the only talloc we should need to do
73 * on the struct dfs_path. All the pointers inside
74 * it should point to offsets within this string.
77 pathname_local = talloc_strdup(pdp, pathname);
78 if (!pathname_local) {
79 return NT_STATUS_NO_MEMORY;
81 /* Get a pointer to the terminating '\0' */
82 eos_ptr = &pathname_local[strlen(pathname_local)];
83 p = temp = pathname_local;
86 * Non-broken DFS paths *must* start with the
87 * path separator. For Windows this is always '\\',
88 * for posix paths this is always '/'.
91 if (*pathname == '/') {
92 pdp->posix_path = true;
93 sepchar = '/';
94 } else {
95 pdp->posix_path = false;
96 sepchar = '\\';
99 if (allow_broken_path && (*pathname != sepchar)) {
100 DEBUG(10,("parse_dfs_path: path %s doesn't start with %c\n",
101 pathname, sepchar ));
103 * Possibly client sent a local path by mistake.
104 * Try and convert to a local path.
105 * Note that this is an SMB1-only fallback
106 * to cope with known broken SMB1 clients.
109 pdp->hostname = eos_ptr; /* "" */
110 pdp->servicename = eos_ptr; /* "" */
112 /* We've got no info about separators. */
113 pdp->posix_path = lp_posix_pathnames();
114 p = temp;
115 DEBUG(10,("parse_dfs_path: trying to convert %s to a "
116 "local path\n",
117 temp));
118 goto local_path;
122 * Safe to use on talloc'ed string as it only shrinks.
123 * It also doesn't affect the eos_ptr.
125 trim_char(temp,sepchar,sepchar);
127 DEBUG(10,("parse_dfs_path: temp = |%s| after trimming %c's\n",
128 temp, sepchar));
130 /* Now tokenize. */
131 /* Parse out hostname. */
132 p = strchr_m(temp,sepchar);
133 if(p == NULL) {
134 DEBUG(10,("parse_dfs_path: can't parse hostname from path %s\n",
135 temp));
137 * Possibly client sent a local path by mistake.
138 * Try and convert to a local path.
141 pdp->hostname = eos_ptr; /* "" */
142 pdp->servicename = eos_ptr; /* "" */
144 p = temp;
145 DEBUG(10,("parse_dfs_path: trying to convert %s "
146 "to a local path\n",
147 temp));
148 goto local_path;
150 *p = '\0';
151 pdp->hostname = temp;
153 DEBUG(10,("parse_dfs_path: hostname: %s\n",pdp->hostname));
155 /* Parse out servicename. */
156 servicename = p+1;
157 p = strchr_m(servicename,sepchar);
158 if (p) {
159 *p = '\0';
162 /* Is this really our servicename ? */
163 if (conn && !( strequal(servicename, lp_servicename(talloc_tos(), SNUM(conn)))
164 || (strequal(servicename, HOMES_NAME)
165 && strequal(lp_servicename(talloc_tos(), SNUM(conn)),
166 get_current_username()) )) ) {
167 DEBUG(10,("parse_dfs_path: %s is not our servicename\n",
168 servicename));
171 * Possibly client sent a local path by mistake.
172 * Try and convert to a local path.
175 pdp->hostname = eos_ptr; /* "" */
176 pdp->servicename = eos_ptr; /* "" */
178 /* Repair the path - replace the sepchar's
179 we nulled out */
180 servicename--;
181 *servicename = sepchar;
182 if (p) {
183 *p = sepchar;
186 p = temp;
187 DEBUG(10,("parse_dfs_path: trying to convert %s "
188 "to a local path\n",
189 temp));
190 goto local_path;
193 pdp->servicename = servicename;
195 DEBUG(10,("parse_dfs_path: servicename: %s\n",pdp->servicename));
197 if(p == NULL) {
198 /* Client sent self referral \server\share. */
199 pdp->reqpath = eos_ptr; /* "" */
200 return NT_STATUS_OK;
203 p++;
205 local_path:
207 *ppath_contains_wcard = False;
209 pdp->reqpath = p;
211 /* Rest is reqpath. */
212 if (pdp->posix_path) {
213 status = check_path_syntax_posix(pdp->reqpath);
214 } else {
215 if (allow_wcards) {
216 status = check_path_syntax_wcard(pdp->reqpath,
217 ppath_contains_wcard);
218 } else {
219 status = check_path_syntax(pdp->reqpath);
223 if (!NT_STATUS_IS_OK(status)) {
224 DEBUG(10,("parse_dfs_path: '%s' failed with %s\n",
225 p, nt_errstr(status) ));
226 return status;
229 DEBUG(10,("parse_dfs_path: rest of the path: %s\n",pdp->reqpath));
230 return NT_STATUS_OK;
233 /********************************************************
234 Fake up a connection struct for the VFS layer, for use in
235 applications (such as the python bindings), that do not want the
236 global working directory changed under them.
238 SMB_VFS_CONNECT requires root privileges.
239 *********************************************************/
241 static NTSTATUS create_conn_struct_as_root(TALLOC_CTX *ctx,
242 struct tevent_context *ev,
243 struct messaging_context *msg,
244 connection_struct **pconn,
245 int snum,
246 const char *path,
247 const struct auth_session_info *session_info)
249 connection_struct *conn;
250 char *connpath;
251 const char *vfs_user;
252 struct smbd_server_connection *sconn;
253 const char *servicename = lp_const_servicename(snum);
255 sconn = talloc_zero(ctx, struct smbd_server_connection);
256 if (sconn == NULL) {
257 return NT_STATUS_NO_MEMORY;
260 sconn->ev_ctx = ev;
261 sconn->msg_ctx = msg;
263 conn = conn_new(sconn);
264 if (conn == NULL) {
265 TALLOC_FREE(sconn);
266 return NT_STATUS_NO_MEMORY;
269 /* Now we have conn, we need to make sconn a child of conn,
270 * for a proper talloc tree */
271 talloc_steal(conn, sconn);
273 if (snum == -1 && servicename == NULL) {
274 servicename = "Unknown Service (snum == -1)";
277 connpath = talloc_strdup(conn, path);
278 if (!connpath) {
279 TALLOC_FREE(conn);
280 return NT_STATUS_NO_MEMORY;
282 connpath = talloc_string_sub(conn,
283 connpath,
284 "%S",
285 servicename);
286 if (!connpath) {
287 TALLOC_FREE(conn);
288 return NT_STATUS_NO_MEMORY;
291 /* needed for smbd_vfs_init() */
293 conn->params->service = snum;
294 conn->cnum = TID_FIELD_INVALID;
296 if (session_info != NULL) {
297 conn->session_info = copy_session_info(conn, session_info);
298 if (conn->session_info == NULL) {
299 DEBUG(0, ("copy_serverinfo failed\n"));
300 TALLOC_FREE(conn);
301 return NT_STATUS_NO_MEMORY;
303 vfs_user = conn->session_info->unix_info->unix_name;
304 } else {
305 /* use current authenticated user in absence of session_info */
306 vfs_user = get_current_username();
309 set_conn_connectpath(conn, connpath);
312 * New code to check if there's a share security descriptor
313 * added from NT server manager. This is done after the
314 * smb.conf checks are done as we need a uid and token. JRA.
317 if (conn->session_info) {
318 share_access_check(conn->session_info->security_token,
319 servicename,
320 MAXIMUM_ALLOWED_ACCESS,
321 &conn->share_access);
323 if ((conn->share_access & FILE_WRITE_DATA) == 0) {
324 if ((conn->share_access & FILE_READ_DATA) == 0) {
325 /* No access, read or write. */
326 DEBUG(3,("create_conn_struct: connection to %s "
327 "denied due to security "
328 "descriptor.\n",
329 servicename));
330 conn_free(conn);
331 return NT_STATUS_ACCESS_DENIED;
332 } else {
333 conn->read_only = true;
336 } else {
337 conn->share_access = 0;
338 conn->read_only = true;
341 if (!smbd_vfs_init(conn)) {
342 NTSTATUS status = map_nt_error_from_unix(errno);
343 DEBUG(0,("create_conn_struct: smbd_vfs_init failed.\n"));
344 conn_free(conn);
345 return status;
348 /* this must be the first filesystem operation that we do */
349 if (SMB_VFS_CONNECT(conn, servicename, vfs_user) < 0) {
350 DEBUG(0,("VFS connect failed!\n"));
351 conn_free(conn);
352 return NT_STATUS_UNSUCCESSFUL;
355 conn->fs_capabilities = SMB_VFS_FS_CAPABILITIES(conn, &conn->ts_res);
356 *pconn = conn;
358 return NT_STATUS_OK;
361 /********************************************************
362 Fake up a connection struct for the VFS layer, for use in
363 applications (such as the python bindings), that do not want the
364 global working directory changed under them.
366 SMB_VFS_CONNECT requires root privileges.
367 *********************************************************/
369 NTSTATUS create_conn_struct(TALLOC_CTX *ctx,
370 struct tevent_context *ev,
371 struct messaging_context *msg,
372 connection_struct **pconn,
373 int snum,
374 const char *path,
375 const struct auth_session_info *session_info)
377 NTSTATUS status;
378 become_root();
379 status = create_conn_struct_as_root(ctx, ev,
380 msg, pconn,
381 snum, path,
382 session_info);
383 unbecome_root();
385 return status;
388 /********************************************************
389 Fake up a connection struct for the VFS layer.
390 Note: this performs a vfs connect and CHANGES CWD !!!! JRA.
392 The old working directory is returned on *poldcwd, allocated on ctx.
393 *********************************************************/
395 NTSTATUS create_conn_struct_cwd(TALLOC_CTX *ctx,
396 struct tevent_context *ev,
397 struct messaging_context *msg,
398 connection_struct **pconn,
399 int snum,
400 const char *path,
401 const struct auth_session_info *session_info,
402 char **poldcwd)
404 connection_struct *conn;
405 char *oldcwd;
407 NTSTATUS status = create_conn_struct(ctx, ev,
408 msg, &conn,
409 snum, path,
410 session_info);
411 if (!NT_STATUS_IS_OK(status)) {
412 return status;
416 * Windows seems to insist on doing trans2getdfsreferral() calls on
417 * the IPC$ share as the anonymous user. If we try to chdir as that
418 * user we will fail.... WTF ? JRA.
421 oldcwd = vfs_GetWd(ctx, conn);
422 if (oldcwd == NULL) {
423 status = map_nt_error_from_unix(errno);
424 DEBUG(3, ("vfs_GetWd failed: %s\n", strerror(errno)));
425 conn_free(conn);
426 return status;
429 if (vfs_ChDir(conn,conn->connectpath) != 0) {
430 status = map_nt_error_from_unix(errno);
431 DEBUG(3,("create_conn_struct: Can't ChDir to new conn path %s. "
432 "Error was %s\n",
433 conn->connectpath, strerror(errno) ));
434 conn_free(conn);
435 return status;
438 *pconn = conn;
439 *poldcwd = oldcwd;
441 return NT_STATUS_OK;
444 static void shuffle_strlist(char **list, int count)
446 int i;
447 uint32_t r;
448 char *tmp;
450 for (i = count; i > 1; i--) {
451 r = generate_random() % i;
453 tmp = list[i-1];
454 list[i-1] = list[r];
455 list[r] = tmp;
459 /**********************************************************************
460 Parse the contents of a symlink to verify if it is an msdfs referral
461 A valid referral is of the form:
463 msdfs:server1\share1,server2\share2
464 msdfs:server1\share1\pathname,server2\share2\pathname
465 msdfs:server1/share1,server2/share2
466 msdfs:server1/share1/pathname,server2/share2/pathname.
468 Note that the alternate paths returned here must be of the canonicalized
469 form:
471 \server\share or
472 \server\share\path\to\file,
474 even in posix path mode. This is because we have no knowledge if the
475 server we're referring to understands posix paths.
476 **********************************************************************/
478 static bool parse_msdfs_symlink(TALLOC_CTX *ctx,
479 int snum,
480 const char *target,
481 struct referral **preflist,
482 int *refcount)
484 char *temp = NULL;
485 char *prot;
486 char **alt_path = NULL;
487 int count = 0, i;
488 struct referral *reflist;
489 char *saveptr;
491 temp = talloc_strdup(ctx, target);
492 if (!temp) {
493 return False;
495 prot = strtok_r(temp, ":", &saveptr);
496 if (!prot) {
497 DEBUG(0,("parse_msdfs_symlink: invalid path !\n"));
498 return False;
501 alt_path = talloc_array(ctx, char *, MAX_REFERRAL_COUNT);
502 if (!alt_path) {
503 return False;
506 /* parse out the alternate paths */
507 while((count<MAX_REFERRAL_COUNT) &&
508 ((alt_path[count] = strtok_r(NULL, ",", &saveptr)) != NULL)) {
509 count++;
512 /* shuffle alternate paths */
513 if (lp_msdfs_shuffle_referrals(snum)) {
514 shuffle_strlist(alt_path, count);
517 DEBUG(10,("parse_msdfs_symlink: count=%d\n", count));
519 if (count) {
520 reflist = *preflist = talloc_zero_array(ctx,
521 struct referral, count);
522 if(reflist == NULL) {
523 TALLOC_FREE(alt_path);
524 return False;
526 } else {
527 reflist = *preflist = NULL;
530 for(i=0;i<count;i++) {
531 char *p;
533 /* Canonicalize link target.
534 * Replace all /'s in the path by a \ */
535 string_replace(alt_path[i], '/', '\\');
537 /* Remove leading '\\'s */
538 p = alt_path[i];
539 while (*p && (*p == '\\')) {
540 p++;
543 reflist[i].alternate_path = talloc_asprintf(ctx,
544 "\\%s",
546 if (!reflist[i].alternate_path) {
547 return False;
550 reflist[i].proximity = 0;
551 reflist[i].ttl = REFERRAL_TTL;
552 DEBUG(10, ("parse_msdfs_symlink: Created alt path: %s\n",
553 reflist[i].alternate_path));
556 *refcount = count;
558 TALLOC_FREE(alt_path);
559 return True;
562 /**********************************************************************
563 Returns true if the unix path is a valid msdfs symlink and also
564 returns the target string from inside the link.
565 **********************************************************************/
567 static bool is_msdfs_link_internal(TALLOC_CTX *ctx,
568 connection_struct *conn,
569 const char *path,
570 char **pp_link_target,
571 SMB_STRUCT_STAT *sbufp)
573 int referral_len = 0;
574 #if defined(HAVE_BROKEN_READLINK)
575 char link_target_buf[PATH_MAX];
576 #else
577 char link_target_buf[7];
578 #endif
579 size_t bufsize = 0;
580 char *link_target = NULL;
581 struct smb_filename smb_fname;
583 if (pp_link_target) {
584 bufsize = 1024;
585 link_target = talloc_array(ctx, char, bufsize);
586 if (!link_target) {
587 return False;
589 *pp_link_target = link_target;
590 } else {
591 bufsize = sizeof(link_target_buf);
592 link_target = link_target_buf;
595 ZERO_STRUCT(smb_fname);
596 smb_fname.base_name = discard_const_p(char, path);
598 if (SMB_VFS_LSTAT(conn, &smb_fname) != 0) {
599 DEBUG(5,("is_msdfs_link_read_target: %s does not exist.\n",
600 path));
601 goto err;
603 if (!S_ISLNK(smb_fname.st.st_ex_mode)) {
604 DEBUG(5,("is_msdfs_link_read_target: %s is not a link.\n",
605 path));
606 goto err;
608 if (sbufp != NULL) {
609 *sbufp = smb_fname.st;
612 referral_len = SMB_VFS_READLINK(conn, path, 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 path, strerror(errno)));
617 goto err;
619 link_target[referral_len] = '\0';
621 DEBUG(5,("is_msdfs_link_internal: %s -> %s\n",path,
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 const char *path,
643 SMB_STRUCT_STAT *sbufp)
645 return is_msdfs_link_internal(talloc_tos(),
646 conn,
647 path,
648 NULL,
649 sbufp);
652 /*****************************************************************
653 Used by other functions to decide if a dfs path is remote,
654 and to get the list of referred locations for that remote path.
656 search_flag: For findfirsts, dfs links themselves are not
657 redirected, but paths beyond the links are. For normal smb calls,
658 even dfs links need to be redirected.
660 consumedcntp: how much of the dfs path is being redirected. the client
661 should try the remaining path on the redirected server.
663 If this returns NT_STATUS_PATH_NOT_COVERED the contents of the msdfs
664 link redirect are in targetpath.
665 *****************************************************************/
667 static NTSTATUS dfs_path_lookup(TALLOC_CTX *ctx,
668 connection_struct *conn,
669 const char *dfspath, /* Incoming complete dfs path */
670 const struct dfs_path *pdp, /* Parsed out
671 server+share+extrapath. */
672 uint32_t ucf_flags,
673 int *consumedcntp,
674 char **pp_targetpath)
676 char *p = NULL;
677 char *q = NULL;
678 NTSTATUS status;
679 struct smb_filename *smb_fname = NULL;
680 char *canon_dfspath = NULL; /* Canonicalized dfs path. (only '/'
681 components). */
683 DEBUG(10,("dfs_path_lookup: Conn path = %s reqpath = %s\n",
684 conn->connectpath, pdp->reqpath));
687 * Note the unix path conversion here we're doing we
688 * throw away. We're looking for a symlink for a dfs
689 * resolution, if we don't find it we'll do another
690 * unix_convert later in the codepath.
693 status = unix_convert(ctx, conn, pdp->reqpath, &smb_fname,
694 ucf_flags);
696 if (!NT_STATUS_IS_OK(status)) {
697 if (!NT_STATUS_EQUAL(status,
698 NT_STATUS_OBJECT_PATH_NOT_FOUND)) {
699 return status;
701 if (smb_fname == NULL || smb_fname->base_name == NULL) {
702 return status;
706 /* Optimization - check if we can redirect the whole path. */
708 if (is_msdfs_link_internal(ctx, conn, smb_fname->base_name,
709 pp_targetpath, NULL)) {
710 /* XX_ALLOW_WCARD_XXX is called from search functions. */
711 if (ucf_flags &
712 (UCF_COND_ALLOW_WCARD_LCOMP|
713 UCF_ALWAYS_ALLOW_WCARD_LCOMP)) {
714 DEBUG(6,("dfs_path_lookup (FindFirst) No redirection "
715 "for dfs link %s.\n", dfspath));
716 status = NT_STATUS_OK;
717 goto out;
720 DEBUG(6,("dfs_path_lookup: %s resolves to a "
721 "valid dfs link %s.\n", dfspath,
722 pp_targetpath ? *pp_targetpath : ""));
724 if (consumedcntp) {
725 *consumedcntp = strlen(dfspath);
727 status = NT_STATUS_PATH_NOT_COVERED;
728 goto out;
731 /* Prepare to test only for '/' components in the given path,
732 * so if a Windows path replace all '\\' characters with '/'.
733 * For a POSIX DFS path we know all separators are already '/'. */
735 canon_dfspath = talloc_strdup(ctx, dfspath);
736 if (!canon_dfspath) {
737 status = NT_STATUS_NO_MEMORY;
738 goto out;
740 if (!pdp->posix_path) {
741 string_replace(canon_dfspath, '\\', '/');
745 * localpath comes out of unix_convert, so it has
746 * no trailing backslash. Make sure that canon_dfspath hasn't either.
747 * Fix for bug #4860 from Jan Martin <Jan.Martin@rwedea.com>.
750 trim_char(canon_dfspath,0,'/');
753 * Redirect if any component in the path is a link.
754 * We do this by walking backwards through the
755 * local path, chopping off the last component
756 * in both the local path and the canonicalized
757 * DFS path. If we hit a DFS link then we're done.
760 p = strrchr_m(smb_fname->base_name, '/');
761 if (consumedcntp) {
762 q = strrchr_m(canon_dfspath, '/');
765 while (p) {
766 *p = '\0';
767 if (q) {
768 *q = '\0';
771 if (is_msdfs_link_internal(ctx, conn,
772 smb_fname->base_name, pp_targetpath,
773 NULL)) {
774 DEBUG(4, ("dfs_path_lookup: Redirecting %s because "
775 "parent %s is dfs link\n", dfspath,
776 smb_fname_str_dbg(smb_fname)));
778 if (consumedcntp) {
779 *consumedcntp = strlen(canon_dfspath);
780 DEBUG(10, ("dfs_path_lookup: Path consumed: %s "
781 "(%d)\n",
782 canon_dfspath,
783 *consumedcntp));
786 status = NT_STATUS_PATH_NOT_COVERED;
787 goto out;
790 /* Step back on the filesystem. */
791 p = strrchr_m(smb_fname->base_name, '/');
793 if (consumedcntp) {
794 /* And in the canonicalized dfs path. */
795 q = strrchr_m(canon_dfspath, '/');
799 status = NT_STATUS_OK;
800 out:
801 TALLOC_FREE(smb_fname);
802 return status;
805 /*****************************************************************
806 Decides if a dfs pathname should be redirected or not.
807 If not, the pathname is converted to a tcon-relative local unix path
809 search_wcard_flag: this flag performs 2 functions both related
810 to searches. See resolve_dfs_path() and parse_dfs_path_XX()
811 for details.
813 This function can return NT_STATUS_OK, meaning use the returned path as-is
814 (mapped into a local path).
815 or NT_STATUS_NOT_COVERED meaning return a DFS redirect, or
816 any other NT_STATUS error which is a genuine error to be
817 returned to the client.
818 *****************************************************************/
820 static NTSTATUS dfs_redirect(TALLOC_CTX *ctx,
821 connection_struct *conn,
822 const char *path_in,
823 uint32_t ucf_flags,
824 bool allow_broken_path,
825 char **pp_path_out,
826 bool *ppath_contains_wcard)
828 NTSTATUS status;
829 bool search_wcard_flag = (ucf_flags &
830 (UCF_COND_ALLOW_WCARD_LCOMP|UCF_ALWAYS_ALLOW_WCARD_LCOMP));
831 struct dfs_path *pdp = talloc(ctx, struct dfs_path);
833 if (!pdp) {
834 return NT_STATUS_NO_MEMORY;
837 status = parse_dfs_path(conn, path_in, search_wcard_flag,
838 allow_broken_path, pdp,
839 ppath_contains_wcard);
840 if (!NT_STATUS_IS_OK(status)) {
841 TALLOC_FREE(pdp);
842 return status;
845 if (pdp->reqpath[0] == '\0') {
846 TALLOC_FREE(pdp);
847 *pp_path_out = talloc_strdup(ctx, "");
848 if (!*pp_path_out) {
849 return NT_STATUS_NO_MEMORY;
851 DEBUG(5,("dfs_redirect: self-referral.\n"));
852 return NT_STATUS_OK;
855 /* If dfs pathname for a non-dfs share, convert to tcon-relative
856 path and return OK */
858 if (!lp_msdfs_root(SNUM(conn))) {
859 *pp_path_out = talloc_strdup(ctx, pdp->reqpath);
860 TALLOC_FREE(pdp);
861 if (!*pp_path_out) {
862 return NT_STATUS_NO_MEMORY;
864 return NT_STATUS_OK;
867 /* If it looked like a local path (zero hostname/servicename)
868 * just treat as a tcon-relative path. */
870 if (pdp->hostname[0] == '\0' && pdp->servicename[0] == '\0') {
871 *pp_path_out = talloc_strdup(ctx, pdp->reqpath);
872 TALLOC_FREE(pdp);
873 if (!*pp_path_out) {
874 return NT_STATUS_NO_MEMORY;
876 return NT_STATUS_OK;
879 if (!( strequal(pdp->servicename, lp_servicename(talloc_tos(), SNUM(conn)))
880 || (strequal(pdp->servicename, HOMES_NAME)
881 && strequal(lp_servicename(talloc_tos(), SNUM(conn)),
882 conn->session_info->unix_info->sanitized_username) )) ) {
884 /* The given sharename doesn't match this connection. */
885 TALLOC_FREE(pdp);
887 return NT_STATUS_OBJECT_PATH_NOT_FOUND;
890 status = dfs_path_lookup(ctx, conn, path_in, pdp,
891 search_wcard_flag, NULL, NULL);
892 if (!NT_STATUS_IS_OK(status)) {
893 if (NT_STATUS_EQUAL(status, NT_STATUS_PATH_NOT_COVERED)) {
894 DEBUG(3,("dfs_redirect: Redirecting %s\n", path_in));
895 } else {
896 DEBUG(10,("dfs_redirect: dfs_path_lookup "
897 "failed for %s with %s\n",
898 path_in, nt_errstr(status) ));
900 return status;
903 DEBUG(3,("dfs_redirect: Not redirecting %s.\n", path_in));
905 /* Form non-dfs tcon-relative path */
906 *pp_path_out = talloc_strdup(ctx, pdp->reqpath);
907 TALLOC_FREE(pdp);
908 if (!*pp_path_out) {
909 return NT_STATUS_NO_MEMORY;
912 DEBUG(3,("dfs_redirect: Path %s converted to non-dfs path %s\n",
913 path_in,
914 *pp_path_out));
916 return NT_STATUS_OK;
919 /**********************************************************************
920 Return a self referral.
921 **********************************************************************/
923 static NTSTATUS self_ref(TALLOC_CTX *ctx,
924 const char *dfs_path,
925 struct junction_map *jucn,
926 int *consumedcntp,
927 bool *self_referralp)
929 struct referral *ref;
931 *self_referralp = True;
933 jucn->referral_count = 1;
934 if((ref = talloc_zero(ctx, struct referral)) == NULL) {
935 return NT_STATUS_NO_MEMORY;
938 ref->alternate_path = talloc_strdup(ctx, dfs_path);
939 if (!ref->alternate_path) {
940 TALLOC_FREE(ref);
941 return NT_STATUS_NO_MEMORY;
943 ref->proximity = 0;
944 ref->ttl = REFERRAL_TTL;
945 jucn->referral_list = ref;
946 *consumedcntp = strlen(dfs_path);
947 return NT_STATUS_OK;
950 /**********************************************************************
951 Gets valid referrals for a dfs path and fills up the
952 junction_map structure.
953 **********************************************************************/
955 NTSTATUS get_referred_path(TALLOC_CTX *ctx,
956 const char *dfs_path,
957 bool allow_broken_path,
958 struct junction_map *jucn,
959 int *consumedcntp,
960 bool *self_referralp)
962 struct connection_struct *conn;
963 char *targetpath = NULL;
964 int snum;
965 NTSTATUS status = NT_STATUS_NOT_FOUND;
966 bool dummy;
967 struct dfs_path *pdp = talloc(ctx, struct dfs_path);
968 char *oldpath;
970 if (!pdp) {
971 return NT_STATUS_NO_MEMORY;
974 *self_referralp = False;
976 status = parse_dfs_path(NULL, dfs_path, False, allow_broken_path,
977 pdp, &dummy);
978 if (!NT_STATUS_IS_OK(status)) {
979 return status;
982 jucn->service_name = talloc_strdup(ctx, pdp->servicename);
983 jucn->volume_name = talloc_strdup(ctx, pdp->reqpath);
984 if (!jucn->service_name || !jucn->volume_name) {
985 TALLOC_FREE(pdp);
986 return NT_STATUS_NO_MEMORY;
989 /* Verify the share is a dfs root */
990 snum = lp_servicenumber(jucn->service_name);
991 if(snum < 0) {
992 char *service_name = NULL;
993 if ((snum = find_service(ctx, jucn->service_name, &service_name)) < 0) {
994 return NT_STATUS_NOT_FOUND;
996 if (!service_name) {
997 return NT_STATUS_NO_MEMORY;
999 TALLOC_FREE(jucn->service_name);
1000 jucn->service_name = talloc_strdup(ctx, service_name);
1001 if (!jucn->service_name) {
1002 TALLOC_FREE(pdp);
1003 return NT_STATUS_NO_MEMORY;
1007 if (!lp_msdfs_root(snum) && (*lp_msdfs_proxy(talloc_tos(), snum) == '\0')) {
1008 DEBUG(3,("get_referred_path: |%s| in dfs path %s is not "
1009 "a dfs root.\n",
1010 pdp->servicename, dfs_path));
1011 TALLOC_FREE(pdp);
1012 return NT_STATUS_NOT_FOUND;
1016 * Self referrals are tested with a anonymous IPC connection and
1017 * a GET_DFS_REFERRAL call to \\server\share. (which means
1018 * dp.reqpath[0] points to an empty string). create_conn_struct cd's
1019 * into the directory and will fail if it cannot (as the anonymous
1020 * user). Cope with this.
1023 if (pdp->reqpath[0] == '\0') {
1024 char *tmp;
1025 struct referral *ref;
1026 int refcount;
1028 if (*lp_msdfs_proxy(talloc_tos(), snum) == '\0') {
1029 TALLOC_FREE(pdp);
1030 return self_ref(ctx,
1031 dfs_path,
1032 jucn,
1033 consumedcntp,
1034 self_referralp);
1038 * It's an msdfs proxy share. Redirect to
1039 * the configured target share.
1042 tmp = talloc_asprintf(talloc_tos(), "msdfs:%s",
1043 lp_msdfs_proxy(talloc_tos(), snum));
1044 if (tmp == NULL) {
1045 TALLOC_FREE(pdp);
1046 return NT_STATUS_NO_MEMORY;
1049 if (!parse_msdfs_symlink(ctx, snum, tmp, &ref, &refcount)) {
1050 TALLOC_FREE(tmp);
1051 TALLOC_FREE(pdp);
1052 return NT_STATUS_INVALID_PARAMETER;
1054 TALLOC_FREE(tmp);
1055 jucn->referral_count = refcount;
1056 jucn->referral_list = ref;
1057 *consumedcntp = strlen(dfs_path);
1058 TALLOC_FREE(pdp);
1059 return NT_STATUS_OK;
1062 status = create_conn_struct_cwd(ctx,
1063 server_event_context(),
1064 server_messaging_context(),
1065 &conn, snum,
1066 lp_path(talloc_tos(), snum), NULL, &oldpath);
1067 if (!NT_STATUS_IS_OK(status)) {
1068 TALLOC_FREE(pdp);
1069 return status;
1072 /* If this is a DFS path dfs_lookup should return
1073 * NT_STATUS_PATH_NOT_COVERED. */
1075 status = dfs_path_lookup(ctx, conn, dfs_path, pdp,
1076 False, consumedcntp, &targetpath);
1078 if (!NT_STATUS_EQUAL(status, NT_STATUS_PATH_NOT_COVERED)) {
1079 DEBUG(3,("get_referred_path: No valid referrals for path %s\n",
1080 dfs_path));
1081 if (NT_STATUS_IS_OK(status)) {
1083 * We are in an error path here (we
1084 * know it's not a DFS path), but
1085 * dfs_path_lookup() can return
1086 * NT_STATUS_OK. Ensure we always
1087 * return a valid error code.
1089 * #9588 - ACLs are not inherited to directories
1090 * for DFS shares.
1092 status = NT_STATUS_NOT_FOUND;
1094 goto err_exit;
1097 /* We know this is a valid dfs link. Parse the targetpath. */
1098 if (!parse_msdfs_symlink(ctx, snum, targetpath,
1099 &jucn->referral_list,
1100 &jucn->referral_count)) {
1101 DEBUG(3,("get_referred_path: failed to parse symlink "
1102 "target %s\n", targetpath ));
1103 status = NT_STATUS_NOT_FOUND;
1104 goto err_exit;
1107 status = NT_STATUS_OK;
1108 err_exit:
1109 vfs_ChDir(conn, oldpath);
1110 SMB_VFS_DISCONNECT(conn);
1111 conn_free(conn);
1112 TALLOC_FREE(pdp);
1113 return status;
1116 /******************************************************************
1117 Set up the DFS referral for the dfs pathname. This call returns
1118 the amount of the path covered by this server, and where the
1119 client should be redirected to. This is the meat of the
1120 TRANS2_GET_DFS_REFERRAL call.
1121 ******************************************************************/
1123 int setup_dfs_referral(connection_struct *orig_conn,
1124 const char *dfs_path,
1125 int max_referral_level,
1126 char **ppdata, NTSTATUS *pstatus)
1128 char *pdata = *ppdata;
1129 int reply_size = 0;
1130 struct dfs_GetDFSReferral *r;
1131 DATA_BLOB blob = data_blob_null;
1132 NTSTATUS status;
1133 enum ndr_err_code ndr_err;
1135 r = talloc_zero(talloc_tos(), struct dfs_GetDFSReferral);
1136 if (r == NULL) {
1137 *pstatus = NT_STATUS_NO_MEMORY;
1138 return -1;
1141 r->in.req.max_referral_level = max_referral_level;
1142 r->in.req.servername = talloc_strdup(r, dfs_path);
1143 if (r->in.req.servername == NULL) {
1144 talloc_free(r);
1145 *pstatus = NT_STATUS_NO_MEMORY;
1146 return -1;
1149 status = SMB_VFS_GET_DFS_REFERRALS(orig_conn, r);
1150 if (!NT_STATUS_IS_OK(status)) {
1151 talloc_free(r);
1152 *pstatus = status;
1153 return -1;
1156 ndr_err = ndr_push_struct_blob(&blob, r,
1157 r->out.resp,
1158 (ndr_push_flags_fn_t)ndr_push_dfs_referral_resp);
1159 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1160 TALLOC_FREE(r);
1161 *pstatus = NT_STATUS_INVALID_PARAMETER;
1162 return -1;
1165 pdata = (char *)SMB_REALLOC(pdata, blob.length);
1166 if(pdata == NULL) {
1167 TALLOC_FREE(r);
1168 DEBUG(0,("referral setup:"
1169 "malloc failed for Realloc!\n"));
1170 return -1;
1172 *ppdata = pdata;
1173 reply_size = blob.length;
1174 memcpy(pdata, blob.data, blob.length);
1175 TALLOC_FREE(r);
1177 *pstatus = NT_STATUS_OK;
1178 return reply_size;
1181 /**********************************************************************
1182 The following functions are called by the NETDFS RPC pipe functions
1183 **********************************************************************/
1185 /*********************************************************************
1186 Creates a junction structure from a DFS pathname
1187 **********************************************************************/
1189 bool create_junction(TALLOC_CTX *ctx,
1190 const char *dfs_path,
1191 bool allow_broken_path,
1192 struct junction_map *jucn)
1194 int snum;
1195 bool dummy;
1196 struct dfs_path *pdp = talloc(ctx,struct dfs_path);
1197 NTSTATUS status;
1199 if (!pdp) {
1200 return False;
1202 status = parse_dfs_path(NULL, dfs_path, False, allow_broken_path,
1203 pdp, &dummy);
1204 if (!NT_STATUS_IS_OK(status)) {
1205 return False;
1208 /* check if path is dfs : validate first token */
1209 if (!is_myname_or_ipaddr(pdp->hostname)) {
1210 DEBUG(4,("create_junction: Invalid hostname %s "
1211 "in dfs path %s\n",
1212 pdp->hostname, dfs_path));
1213 TALLOC_FREE(pdp);
1214 return False;
1217 /* Check for a non-DFS share */
1218 snum = lp_servicenumber(pdp->servicename);
1220 if(snum < 0 || !lp_msdfs_root(snum)) {
1221 DEBUG(4,("create_junction: %s is not an msdfs root.\n",
1222 pdp->servicename));
1223 TALLOC_FREE(pdp);
1224 return False;
1227 jucn->service_name = talloc_strdup(ctx, pdp->servicename);
1228 jucn->volume_name = talloc_strdup(ctx, pdp->reqpath);
1229 jucn->comment = lp_comment(ctx, snum);
1231 TALLOC_FREE(pdp);
1232 if (!jucn->service_name || !jucn->volume_name || ! jucn->comment) {
1233 return False;
1235 return True;
1238 /**********************************************************************
1239 Forms a valid Unix pathname from the junction
1240 **********************************************************************/
1242 static bool junction_to_local_path(const struct junction_map *jucn,
1243 char **pp_path_out,
1244 connection_struct **conn_out,
1245 char **oldpath)
1247 int snum;
1248 NTSTATUS status;
1250 snum = lp_servicenumber(jucn->service_name);
1251 if(snum < 0) {
1252 return False;
1254 status = create_conn_struct_cwd(talloc_tos(),
1255 server_event_context(),
1256 server_messaging_context(),
1257 conn_out,
1258 snum, lp_path(talloc_tos(), snum), NULL, oldpath);
1259 if (!NT_STATUS_IS_OK(status)) {
1260 return False;
1263 *pp_path_out = talloc_asprintf(*conn_out,
1264 "%s/%s",
1265 lp_path(talloc_tos(), snum),
1266 jucn->volume_name);
1267 if (!*pp_path_out) {
1268 vfs_ChDir(*conn_out, *oldpath);
1269 SMB_VFS_DISCONNECT(*conn_out);
1270 conn_free(*conn_out);
1271 return False;
1273 return True;
1276 bool create_msdfs_link(const struct junction_map *jucn)
1278 char *path = NULL;
1279 char *cwd;
1280 char *msdfs_link = NULL;
1281 connection_struct *conn;
1282 int i=0;
1283 bool insert_comma = False;
1284 bool ret = False;
1286 if(!junction_to_local_path(jucn, &path, &conn, &cwd)) {
1287 return False;
1290 /* Form the msdfs_link contents */
1291 msdfs_link = talloc_strdup(conn, "msdfs:");
1292 if (!msdfs_link) {
1293 goto out;
1295 for(i=0; i<jucn->referral_count; i++) {
1296 char *refpath = jucn->referral_list[i].alternate_path;
1298 /* Alternate paths always use Windows separators. */
1299 trim_char(refpath, '\\', '\\');
1300 if(*refpath == '\0') {
1301 if (i == 0) {
1302 insert_comma = False;
1304 continue;
1306 if (i > 0 && insert_comma) {
1307 msdfs_link = talloc_asprintf_append_buffer(msdfs_link,
1308 ",%s",
1309 refpath);
1310 } else {
1311 msdfs_link = talloc_asprintf_append_buffer(msdfs_link,
1312 "%s",
1313 refpath);
1316 if (!msdfs_link) {
1317 goto out;
1319 if (!insert_comma) {
1320 insert_comma = True;
1324 DEBUG(5,("create_msdfs_link: Creating new msdfs link: %s -> %s\n",
1325 path, msdfs_link));
1327 if(SMB_VFS_SYMLINK(conn, msdfs_link, path) < 0) {
1328 if (errno == EEXIST) {
1329 struct smb_filename *smb_fname;
1331 smb_fname = synthetic_smb_fname(talloc_tos(),
1332 path,
1333 NULL,
1334 NULL,
1336 if (smb_fname == NULL) {
1337 errno = ENOMEM;
1338 goto out;
1341 if(SMB_VFS_UNLINK(conn, smb_fname)!=0) {
1342 TALLOC_FREE(smb_fname);
1343 goto out;
1345 TALLOC_FREE(smb_fname);
1347 if (SMB_VFS_SYMLINK(conn, msdfs_link, path) < 0) {
1348 DEBUG(1,("create_msdfs_link: symlink failed "
1349 "%s -> %s\nError: %s\n",
1350 path, msdfs_link, strerror(errno)));
1351 goto out;
1355 ret = True;
1357 out:
1358 vfs_ChDir(conn, cwd);
1359 SMB_VFS_DISCONNECT(conn);
1360 conn_free(conn);
1361 return ret;
1364 bool remove_msdfs_link(const struct junction_map *jucn)
1366 char *path = NULL;
1367 char *cwd;
1368 connection_struct *conn;
1369 bool ret = False;
1370 struct smb_filename *smb_fname;
1372 if (!junction_to_local_path(jucn, &path, &conn, &cwd)) {
1373 return false;
1376 smb_fname = synthetic_smb_fname(talloc_tos(),
1377 path,
1378 NULL,
1379 NULL,
1381 if (smb_fname == NULL) {
1382 errno = ENOMEM;
1383 return false;
1386 if( SMB_VFS_UNLINK(conn, smb_fname) == 0 ) {
1387 ret = True;
1390 TALLOC_FREE(smb_fname);
1391 vfs_ChDir(conn, cwd);
1392 SMB_VFS_DISCONNECT(conn);
1393 conn_free(conn);
1394 return ret;
1397 /*********************************************************************
1398 Return the number of DFS links at the root of this share.
1399 *********************************************************************/
1401 static int count_dfs_links(TALLOC_CTX *ctx, int snum)
1403 size_t cnt = 0;
1404 DIR *dirp = NULL;
1405 const char *dname = NULL;
1406 char *talloced = NULL;
1407 const char *connect_path = lp_path(talloc_tos(), snum);
1408 const char *msdfs_proxy = lp_msdfs_proxy(talloc_tos(), snum);
1409 connection_struct *conn;
1410 NTSTATUS status;
1411 char *cwd;
1412 struct smb_filename *smb_fname = NULL;
1414 if(*connect_path == '\0') {
1415 return 0;
1419 * Fake up a connection struct for the VFS layer.
1422 status = create_conn_struct_cwd(talloc_tos(),
1423 server_event_context(),
1424 server_messaging_context(),
1425 &conn,
1426 snum, connect_path, NULL, &cwd);
1427 if (!NT_STATUS_IS_OK(status)) {
1428 DEBUG(3, ("create_conn_struct failed: %s\n",
1429 nt_errstr(status)));
1430 return 0;
1433 /* Count a link for the msdfs root - convention */
1434 cnt = 1;
1436 /* No more links if this is an msdfs proxy. */
1437 if (*msdfs_proxy != '\0') {
1438 goto out;
1441 smb_fname = synthetic_smb_fname(talloc_tos(),
1442 ".",
1443 NULL,
1444 NULL,
1446 if (smb_fname == NULL) {
1447 goto out;
1450 /* Now enumerate all dfs links */
1451 dirp = SMB_VFS_OPENDIR(conn, smb_fname, NULL, 0);
1452 if(!dirp) {
1453 goto out;
1456 while ((dname = vfs_readdirname(conn, dirp, NULL, &talloced))
1457 != NULL) {
1458 if (is_msdfs_link(conn,
1459 dname,
1460 NULL)) {
1461 cnt++;
1463 TALLOC_FREE(talloced);
1466 SMB_VFS_CLOSEDIR(conn,dirp);
1468 out:
1469 TALLOC_FREE(smb_fname);
1470 vfs_ChDir(conn, cwd);
1471 SMB_VFS_DISCONNECT(conn);
1472 conn_free(conn);
1473 return cnt;
1476 /*********************************************************************
1477 *********************************************************************/
1479 static int form_junctions(TALLOC_CTX *ctx,
1480 int snum,
1481 struct junction_map *jucn,
1482 size_t jn_remain)
1484 size_t cnt = 0;
1485 DIR *dirp = NULL;
1486 const char *dname = NULL;
1487 char *talloced = NULL;
1488 const char *connect_path = lp_path(talloc_tos(), snum);
1489 char *service_name = lp_servicename(talloc_tos(), snum);
1490 const char *msdfs_proxy = lp_msdfs_proxy(talloc_tos(), snum);
1491 connection_struct *conn;
1492 struct referral *ref = NULL;
1493 char *cwd;
1494 struct smb_filename *smb_fname = NULL;
1495 NTSTATUS status;
1497 if (jn_remain == 0) {
1498 return 0;
1501 if(*connect_path == '\0') {
1502 return 0;
1506 * Fake up a connection struct for the VFS layer.
1509 status = create_conn_struct_cwd(ctx,
1510 server_event_context(),
1511 server_messaging_context(),
1512 &conn, snum, connect_path, NULL,
1513 &cwd);
1514 if (!NT_STATUS_IS_OK(status)) {
1515 DEBUG(3, ("create_conn_struct failed: %s\n",
1516 nt_errstr(status)));
1517 return 0;
1520 /* form a junction for the msdfs root - convention
1521 DO NOT REMOVE THIS: NT clients will not work with us
1522 if this is not present
1524 jucn[cnt].service_name = talloc_strdup(ctx,service_name);
1525 jucn[cnt].volume_name = talloc_strdup(ctx, "");
1526 if (!jucn[cnt].service_name || !jucn[cnt].volume_name) {
1527 goto out;
1529 jucn[cnt].comment = "";
1530 jucn[cnt].referral_count = 1;
1532 ref = jucn[cnt].referral_list = talloc_zero(ctx, struct referral);
1533 if (jucn[cnt].referral_list == NULL) {
1534 goto out;
1537 ref->proximity = 0;
1538 ref->ttl = REFERRAL_TTL;
1539 if (*msdfs_proxy != '\0') {
1540 ref->alternate_path = talloc_strdup(ctx,
1541 msdfs_proxy);
1542 } else {
1543 ref->alternate_path = talloc_asprintf(ctx,
1544 "\\\\%s\\%s",
1545 get_local_machine_name(),
1546 service_name);
1549 if (!ref->alternate_path) {
1550 goto out;
1552 cnt++;
1554 /* Don't enumerate if we're an msdfs proxy. */
1555 if (*msdfs_proxy != '\0') {
1556 goto out;
1559 smb_fname = synthetic_smb_fname(talloc_tos(),
1560 ".",
1561 NULL,
1562 NULL,
1564 if (smb_fname == NULL) {
1565 goto out;
1568 /* Now enumerate all dfs links */
1569 dirp = SMB_VFS_OPENDIR(conn, smb_fname, NULL, 0);
1570 if(!dirp) {
1571 goto out;
1574 while ((dname = vfs_readdirname(conn, dirp, NULL, &talloced))
1575 != NULL) {
1576 char *link_target = NULL;
1577 if (cnt >= jn_remain) {
1578 DEBUG(2, ("form_junctions: ran out of MSDFS "
1579 "junction slots"));
1580 TALLOC_FREE(talloced);
1581 goto out;
1583 if (is_msdfs_link_internal(ctx,
1584 conn,
1585 dname, &link_target,
1586 NULL)) {
1587 if (parse_msdfs_symlink(ctx, snum,
1588 link_target,
1589 &jucn[cnt].referral_list,
1590 &jucn[cnt].referral_count)) {
1592 jucn[cnt].service_name = talloc_strdup(ctx,
1593 service_name);
1594 jucn[cnt].volume_name = talloc_strdup(ctx,
1595 dname);
1596 if (!jucn[cnt].service_name ||
1597 !jucn[cnt].volume_name) {
1598 TALLOC_FREE(talloced);
1599 goto out;
1601 jucn[cnt].comment = "";
1602 cnt++;
1604 TALLOC_FREE(link_target);
1606 TALLOC_FREE(talloced);
1609 out:
1611 if (dirp) {
1612 SMB_VFS_CLOSEDIR(conn,dirp);
1615 TALLOC_FREE(smb_fname);
1616 vfs_ChDir(conn, cwd);
1617 conn_free(conn);
1618 return cnt;
1621 struct junction_map *enum_msdfs_links(TALLOC_CTX *ctx, size_t *p_num_jn)
1623 struct junction_map *jn = NULL;
1624 int i=0;
1625 size_t jn_count = 0;
1626 int sharecount = 0;
1628 *p_num_jn = 0;
1629 if(!lp_host_msdfs()) {
1630 return NULL;
1633 /* Ensure all the usershares are loaded. */
1634 become_root();
1635 load_registry_shares();
1636 sharecount = load_usershare_shares(NULL, connections_snum_used);
1637 unbecome_root();
1639 for(i=0;i < sharecount;i++) {
1640 if(lp_msdfs_root(i)) {
1641 jn_count += count_dfs_links(ctx, i);
1644 if (jn_count == 0) {
1645 return NULL;
1647 jn = talloc_array(ctx, struct junction_map, jn_count);
1648 if (!jn) {
1649 return NULL;
1651 for(i=0; i < sharecount; i++) {
1652 if (*p_num_jn >= jn_count) {
1653 break;
1655 if(lp_msdfs_root(i)) {
1656 *p_num_jn += form_junctions(ctx, i,
1657 &jn[*p_num_jn],
1658 jn_count - *p_num_jn);
1661 return jn;
1664 /******************************************************************************
1665 Core function to resolve a dfs pathname possibly containing a wildcard. If
1666 ppath_contains_wcard != NULL, it will be set to true if a wildcard is
1667 detected during dfs resolution.
1668 ******************************************************************************/
1670 NTSTATUS resolve_dfspath_wcard(TALLOC_CTX *ctx,
1671 connection_struct *conn,
1672 bool dfs_pathnames,
1673 const char *name_in,
1674 uint32_t ucf_flags,
1675 bool allow_broken_path,
1676 char **pp_name_out,
1677 bool *ppath_contains_wcard)
1679 bool path_contains_wcard;
1680 NTSTATUS status = NT_STATUS_OK;
1682 if (dfs_pathnames) {
1683 status = dfs_redirect(ctx,
1684 conn,
1685 name_in,
1686 ucf_flags,
1687 allow_broken_path,
1688 pp_name_out,
1689 &path_contains_wcard);
1691 if (NT_STATUS_IS_OK(status) && ppath_contains_wcard != NULL) {
1692 *ppath_contains_wcard = path_contains_wcard;
1694 } else {
1696 * Cheat and just return a copy of the in ptr.
1697 * Once srvstr_get_path() uses talloc it'll
1698 * be a talloced ptr anyway.
1700 *pp_name_out = discard_const_p(char, name_in);
1702 return status;