dnsp: Parse TXT records
[Samba/gebeck_regimport.git] / source3 / smbd / msdfs.c
blob481958c8ae8eaa768eff9286b11efe437a7bd74e
1 /*
2 Unix SMB/Netbios implementation.
3 Version 3.0
4 MSDFS services for Samba
5 Copyright (C) Shirish Kalele 2000
6 Copyright (C) Jeremy Allison 2007
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 3 of the License, or
11 (at your option) any later version.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program. If not, see <http://www.gnu.org/licenses/>.
23 #define DBGC_CLASS DBGC_MSDFS
24 #include "includes.h"
25 #include "smbd/globals.h"
27 /**********************************************************************
28 Parse a DFS pathname of the form \hostname\service\reqpath
29 into the dfs_path structure.
30 If POSIX pathnames is true, the pathname may also be of the
31 form /hostname/service/reqpath.
32 We cope with either here.
34 Unfortunately, due to broken clients who might set the
35 SVAL(inbuf,smb_flg2) & FLAGS2_DFS_PATHNAMES bit and then
36 send a local path, we have to cope with that too....
38 If conn != NULL then ensure the provided service is
39 the one pointed to by the connection.
41 This version does everything using pointers within one copy of the
42 pathname string, talloced on the struct dfs_path pointer (which
43 must be talloced). This may be too clever to live....
44 JRA.
45 **********************************************************************/
47 static NTSTATUS parse_dfs_path(connection_struct *conn,
48 const char *pathname,
49 bool allow_wcards,
50 struct dfs_path *pdp, /* MUST BE TALLOCED */
51 bool *ppath_contains_wcard)
53 struct smbd_server_connection *sconn = smbd_server_conn;
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 (!sconn->using_smb2 && (*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 const struct auth_serversupplied_info *server_info,
223 char **poldcwd)
225 connection_struct *conn;
226 char *connpath;
227 char *oldcwd;
229 conn = TALLOC_ZERO_P(ctx, connection_struct);
230 if (conn == NULL) {
231 return NT_STATUS_NO_MEMORY;
234 connpath = talloc_strdup(conn, path);
235 if (!connpath) {
236 TALLOC_FREE(conn);
237 return NT_STATUS_NO_MEMORY;
239 connpath = talloc_string_sub(conn,
240 connpath,
241 "%S",
242 lp_servicename(snum));
243 if (!connpath) {
244 TALLOC_FREE(conn);
245 return NT_STATUS_NO_MEMORY;
248 /* needed for smbd_vfs_init() */
250 if (!(conn->params = TALLOC_ZERO_P(conn, struct share_params))) {
251 DEBUG(0, ("TALLOC failed\n"));
252 TALLOC_FREE(conn);
253 return NT_STATUS_NO_MEMORY;
256 conn->params->service = snum;
258 conn->sconn = smbd_server_conn;
259 conn->sconn->smb1.tcons.num_open += 1;
261 if (server_info != NULL) {
262 conn->server_info = copy_serverinfo(conn, server_info);
263 if (conn->server_info == NULL) {
264 DEBUG(0, ("copy_serverinfo failed\n"));
265 TALLOC_FREE(conn);
266 return NT_STATUS_NO_MEMORY;
270 set_conn_connectpath(conn, connpath);
272 if (!smbd_vfs_init(conn)) {
273 NTSTATUS status = map_nt_error_from_unix(errno);
274 DEBUG(0,("create_conn_struct: smbd_vfs_init failed.\n"));
275 conn_free(conn);
276 return status;
279 conn->fs_capabilities = SMB_VFS_FS_CAPABILITIES(conn, &conn->ts_res);
282 * Windows seems to insist on doing trans2getdfsreferral() calls on
283 * the IPC$ share as the anonymous user. If we try to chdir as that
284 * user we will fail.... WTF ? JRA.
287 oldcwd = vfs_GetWd(ctx, conn);
288 if (oldcwd == NULL) {
289 NTSTATUS status = map_nt_error_from_unix(errno);
290 DEBUG(3, ("vfs_GetWd failed: %s\n", strerror(errno)));
291 conn_free(conn);
292 return status;
295 if (vfs_ChDir(conn,conn->connectpath) != 0) {
296 NTSTATUS status = map_nt_error_from_unix(errno);
297 DEBUG(3,("create_conn_struct: Can't ChDir to new conn path %s. "
298 "Error was %s\n",
299 conn->connectpath, strerror(errno) ));
300 conn_free(conn);
301 return status;
304 *pconn = conn;
305 *poldcwd = oldcwd;
307 return NT_STATUS_OK;
310 /**********************************************************************
311 Parse the contents of a symlink to verify if it is an msdfs referral
312 A valid referral is of the form:
314 msdfs:server1\share1,server2\share2
315 msdfs:server1\share1\pathname,server2\share2\pathname
316 msdfs:server1/share1,server2/share2
317 msdfs:server1/share1/pathname,server2/share2/pathname.
319 Note that the alternate paths returned here must be of the canonicalized
320 form:
322 \server\share or
323 \server\share\path\to\file,
325 even in posix path mode. This is because we have no knowledge if the
326 server we're referring to understands posix paths.
327 **********************************************************************/
329 static bool parse_msdfs_symlink(TALLOC_CTX *ctx,
330 const char *target,
331 struct referral **preflist,
332 int *refcount)
334 char *temp = NULL;
335 char *prot;
336 char **alt_path = NULL;
337 int count = 0, i;
338 struct referral *reflist;
339 char *saveptr;
341 temp = talloc_strdup(ctx, target);
342 if (!temp) {
343 return False;
345 prot = strtok_r(temp, ":", &saveptr);
346 if (!prot) {
347 DEBUG(0,("parse_msdfs_symlink: invalid path !\n"));
348 return False;
351 alt_path = TALLOC_ARRAY(ctx, char *, MAX_REFERRAL_COUNT);
352 if (!alt_path) {
353 return False;
356 /* parse out the alternate paths */
357 while((count<MAX_REFERRAL_COUNT) &&
358 ((alt_path[count] = strtok_r(NULL, ",", &saveptr)) != NULL)) {
359 count++;
362 DEBUG(10,("parse_msdfs_symlink: count=%d\n", count));
364 if (count) {
365 reflist = *preflist = TALLOC_ZERO_ARRAY(ctx,
366 struct referral, count);
367 if(reflist == NULL) {
368 TALLOC_FREE(alt_path);
369 return False;
371 } else {
372 reflist = *preflist = NULL;
375 for(i=0;i<count;i++) {
376 char *p;
378 /* Canonicalize link target.
379 * Replace all /'s in the path by a \ */
380 string_replace(alt_path[i], '/', '\\');
382 /* Remove leading '\\'s */
383 p = alt_path[i];
384 while (*p && (*p == '\\')) {
385 p++;
388 reflist[i].alternate_path = talloc_asprintf(ctx,
389 "\\%s",
391 if (!reflist[i].alternate_path) {
392 return False;
395 reflist[i].proximity = 0;
396 reflist[i].ttl = REFERRAL_TTL;
397 DEBUG(10, ("parse_msdfs_symlink: Created alt path: %s\n",
398 reflist[i].alternate_path));
401 *refcount = count;
403 TALLOC_FREE(alt_path);
404 return True;
407 /**********************************************************************
408 Returns true if the unix path is a valid msdfs symlink and also
409 returns the target string from inside the link.
410 **********************************************************************/
412 static bool is_msdfs_link_internal(TALLOC_CTX *ctx,
413 connection_struct *conn,
414 const char *path,
415 char **pp_link_target,
416 SMB_STRUCT_STAT *sbufp)
418 int referral_len = 0;
419 #if defined(HAVE_BROKEN_READLINK)
420 char link_target_buf[PATH_MAX];
421 #else
422 char link_target_buf[7];
423 #endif
424 size_t bufsize = 0;
425 char *link_target = NULL;
426 struct smb_filename smb_fname;
428 if (pp_link_target) {
429 bufsize = 1024;
430 link_target = TALLOC_ARRAY(ctx, char, bufsize);
431 if (!link_target) {
432 return False;
434 *pp_link_target = link_target;
435 } else {
436 bufsize = sizeof(link_target_buf);
437 link_target = link_target_buf;
440 ZERO_STRUCT(smb_fname);
441 smb_fname.base_name = discard_const_p(char, path);
443 if (SMB_VFS_LSTAT(conn, &smb_fname) != 0) {
444 DEBUG(5,("is_msdfs_link_read_target: %s does not exist.\n",
445 path));
446 goto err;
448 if (!S_ISLNK(smb_fname.st.st_ex_mode)) {
449 DEBUG(5,("is_msdfs_link_read_target: %s is not a link.\n",
450 path));
451 goto err;
453 if (sbufp != NULL) {
454 *sbufp = smb_fname.st;
457 referral_len = SMB_VFS_READLINK(conn, path, link_target, bufsize - 1);
458 if (referral_len == -1) {
459 DEBUG(0,("is_msdfs_link_read_target: Error reading "
460 "msdfs link %s: %s\n",
461 path, strerror(errno)));
462 goto err;
464 link_target[referral_len] = '\0';
466 DEBUG(5,("is_msdfs_link_internal: %s -> %s\n",path,
467 link_target));
469 if (!strnequal(link_target, "msdfs:", 6)) {
470 goto err;
472 return True;
474 err:
476 if (link_target != link_target_buf) {
477 TALLOC_FREE(link_target);
479 return False;
482 /**********************************************************************
483 Returns true if the unix path is a valid msdfs symlink.
484 **********************************************************************/
486 bool is_msdfs_link(connection_struct *conn,
487 const char *path,
488 SMB_STRUCT_STAT *sbufp)
490 return is_msdfs_link_internal(talloc_tos(),
491 conn,
492 path,
493 NULL,
494 sbufp);
497 /*****************************************************************
498 Used by other functions to decide if a dfs path is remote,
499 and to get the list of referred locations for that remote path.
501 search_flag: For findfirsts, dfs links themselves are not
502 redirected, but paths beyond the links are. For normal smb calls,
503 even dfs links need to be redirected.
505 consumedcntp: how much of the dfs path is being redirected. the client
506 should try the remaining path on the redirected server.
508 If this returns NT_STATUS_PATH_NOT_COVERED the contents of the msdfs
509 link redirect are in targetpath.
510 *****************************************************************/
512 static NTSTATUS dfs_path_lookup(TALLOC_CTX *ctx,
513 connection_struct *conn,
514 const char *dfspath, /* Incoming complete dfs path */
515 const struct dfs_path *pdp, /* Parsed out
516 server+share+extrapath. */
517 bool search_flag, /* Called from a findfirst ? */
518 int *consumedcntp,
519 char **pp_targetpath)
521 char *p = NULL;
522 char *q = NULL;
523 NTSTATUS status;
524 struct smb_filename *smb_fname = NULL;
525 char *canon_dfspath = NULL; /* Canonicalized dfs path. (only '/'
526 components). */
528 DEBUG(10,("dfs_path_lookup: Conn path = %s reqpath = %s\n",
529 conn->connectpath, pdp->reqpath));
532 * Note the unix path conversion here we're doing we can
533 * throw away. We're looking for a symlink for a dfs
534 * resolution, if we don't find it we'll do another
535 * unix_convert later in the codepath.
536 * If we needed to remember what we'd resolved in
537 * dp->reqpath (as the original code did) we'd
538 * copy (localhost, dp->reqpath) on any code
539 * path below that returns True - but I don't
540 * think this is needed. JRA.
543 status = unix_convert(ctx, conn, pdp->reqpath, &smb_fname,
544 search_flag ? UCF_ALWAYS_ALLOW_WCARD_LCOMP : 0);
546 if (!NT_STATUS_IS_OK(status)) {
547 if (!NT_STATUS_EQUAL(status,
548 NT_STATUS_OBJECT_PATH_NOT_FOUND)) {
549 return status;
552 /* Create an smb_fname to use below. */
553 status = create_synthetic_smb_fname(ctx, pdp->reqpath, NULL,
554 NULL, &smb_fname);
555 if (!NT_STATUS_IS_OK(status)) {
556 return status;
560 /* Optimization - check if we can redirect the whole path. */
562 if (is_msdfs_link_internal(ctx, conn, smb_fname->base_name,
563 pp_targetpath, NULL)) {
564 if (search_flag) {
565 DEBUG(6,("dfs_path_lookup (FindFirst) No redirection "
566 "for dfs link %s.\n", dfspath));
567 status = NT_STATUS_OK;
568 goto out;
571 DEBUG(6,("dfs_path_lookup: %s resolves to a "
572 "valid dfs link %s.\n", dfspath,
573 pp_targetpath ? *pp_targetpath : ""));
575 if (consumedcntp) {
576 *consumedcntp = strlen(dfspath);
578 status = NT_STATUS_PATH_NOT_COVERED;
579 goto out;
582 /* Prepare to test only for '/' components in the given path,
583 * so if a Windows path replace all '\\' characters with '/'.
584 * For a POSIX DFS path we know all separators are already '/'. */
586 canon_dfspath = talloc_strdup(ctx, dfspath);
587 if (!canon_dfspath) {
588 status = NT_STATUS_NO_MEMORY;
589 goto out;
591 if (!pdp->posix_path) {
592 string_replace(canon_dfspath, '\\', '/');
596 * localpath comes out of unix_convert, so it has
597 * no trailing backslash. Make sure that canon_dfspath hasn't either.
598 * Fix for bug #4860 from Jan Martin <Jan.Martin@rwedea.com>.
601 trim_char(canon_dfspath,0,'/');
604 * Redirect if any component in the path is a link.
605 * We do this by walking backwards through the
606 * local path, chopping off the last component
607 * in both the local path and the canonicalized
608 * DFS path. If we hit a DFS link then we're done.
611 p = strrchr_m(smb_fname->base_name, '/');
612 if (consumedcntp) {
613 q = strrchr_m(canon_dfspath, '/');
616 while (p) {
617 *p = '\0';
618 if (q) {
619 *q = '\0';
622 if (is_msdfs_link_internal(ctx, conn,
623 smb_fname->base_name, pp_targetpath,
624 NULL)) {
625 DEBUG(4, ("dfs_path_lookup: Redirecting %s because "
626 "parent %s is dfs link\n", dfspath,
627 smb_fname_str_dbg(smb_fname)));
629 if (consumedcntp) {
630 *consumedcntp = strlen(canon_dfspath);
631 DEBUG(10, ("dfs_path_lookup: Path consumed: %s "
632 "(%d)\n",
633 canon_dfspath,
634 *consumedcntp));
637 status = NT_STATUS_PATH_NOT_COVERED;
638 goto out;
641 /* Step back on the filesystem. */
642 p = strrchr_m(smb_fname->base_name, '/');
644 if (consumedcntp) {
645 /* And in the canonicalized dfs path. */
646 q = strrchr_m(canon_dfspath, '/');
650 status = NT_STATUS_OK;
651 out:
652 TALLOC_FREE(smb_fname);
653 return status;
656 /*****************************************************************
657 Decides if a dfs pathname should be redirected or not.
658 If not, the pathname is converted to a tcon-relative local unix path
660 search_wcard_flag: this flag performs 2 functions both related
661 to searches. See resolve_dfs_path() and parse_dfs_path_XX()
662 for details.
664 This function can return NT_STATUS_OK, meaning use the returned path as-is
665 (mapped into a local path).
666 or NT_STATUS_NOT_COVERED meaning return a DFS redirect, or
667 any other NT_STATUS error which is a genuine error to be
668 returned to the client.
669 *****************************************************************/
671 static NTSTATUS dfs_redirect(TALLOC_CTX *ctx,
672 connection_struct *conn,
673 const char *path_in,
674 bool search_wcard_flag,
675 char **pp_path_out,
676 bool *ppath_contains_wcard)
678 NTSTATUS status;
679 struct dfs_path *pdp = TALLOC_P(ctx, struct dfs_path);
681 if (!pdp) {
682 return NT_STATUS_NO_MEMORY;
685 status = parse_dfs_path(conn, path_in, search_wcard_flag, pdp,
686 ppath_contains_wcard);
687 if (!NT_STATUS_IS_OK(status)) {
688 TALLOC_FREE(pdp);
689 return status;
692 if (pdp->reqpath[0] == '\0') {
693 TALLOC_FREE(pdp);
694 *pp_path_out = talloc_strdup(ctx, "");
695 if (!*pp_path_out) {
696 return NT_STATUS_NO_MEMORY;
698 DEBUG(5,("dfs_redirect: self-referral.\n"));
699 return NT_STATUS_OK;
702 /* If dfs pathname for a non-dfs share, convert to tcon-relative
703 path and return OK */
705 if (!lp_msdfs_root(SNUM(conn))) {
706 *pp_path_out = talloc_strdup(ctx, pdp->reqpath);
707 TALLOC_FREE(pdp);
708 if (!*pp_path_out) {
709 return NT_STATUS_NO_MEMORY;
711 return NT_STATUS_OK;
714 /* If it looked like a local path (zero hostname/servicename)
715 * just treat as a tcon-relative path. */
717 if (pdp->hostname[0] == '\0' && pdp->servicename[0] == '\0') {
718 *pp_path_out = talloc_strdup(ctx, pdp->reqpath);
719 TALLOC_FREE(pdp);
720 if (!*pp_path_out) {
721 return NT_STATUS_NO_MEMORY;
723 return NT_STATUS_OK;
726 if (!( strequal(pdp->servicename, lp_servicename(SNUM(conn)))
727 || (strequal(pdp->servicename, HOMES_NAME)
728 && strequal(lp_servicename(SNUM(conn)),
729 conn->server_info->sanitized_username) )) ) {
731 /* The given sharename doesn't match this connection. */
732 TALLOC_FREE(pdp);
734 return NT_STATUS_OBJECT_PATH_NOT_FOUND;
737 status = dfs_path_lookup(ctx, conn, path_in, pdp,
738 search_wcard_flag, NULL, NULL);
739 if (!NT_STATUS_IS_OK(status)) {
740 if (NT_STATUS_EQUAL(status, NT_STATUS_PATH_NOT_COVERED)) {
741 DEBUG(3,("dfs_redirect: Redirecting %s\n", path_in));
742 } else {
743 DEBUG(10,("dfs_redirect: dfs_path_lookup "
744 "failed for %s with %s\n",
745 path_in, nt_errstr(status) ));
747 return status;
750 DEBUG(3,("dfs_redirect: Not redirecting %s.\n", path_in));
752 /* Form non-dfs tcon-relative path */
753 *pp_path_out = talloc_strdup(ctx, pdp->reqpath);
754 TALLOC_FREE(pdp);
755 if (!*pp_path_out) {
756 return NT_STATUS_NO_MEMORY;
759 DEBUG(3,("dfs_redirect: Path %s converted to non-dfs path %s\n",
760 path_in,
761 *pp_path_out));
763 return NT_STATUS_OK;
766 /**********************************************************************
767 Return a self referral.
768 **********************************************************************/
770 static NTSTATUS self_ref(TALLOC_CTX *ctx,
771 const char *dfs_path,
772 struct junction_map *jucn,
773 int *consumedcntp,
774 bool *self_referralp)
776 struct referral *ref;
778 *self_referralp = True;
780 jucn->referral_count = 1;
781 if((ref = TALLOC_ZERO_P(ctx, struct referral)) == NULL) {
782 return NT_STATUS_NO_MEMORY;
785 ref->alternate_path = talloc_strdup(ctx, dfs_path);
786 if (!ref->alternate_path) {
787 return NT_STATUS_NO_MEMORY;
789 ref->proximity = 0;
790 ref->ttl = REFERRAL_TTL;
791 jucn->referral_list = ref;
792 *consumedcntp = strlen(dfs_path);
793 return NT_STATUS_OK;
796 /**********************************************************************
797 Gets valid referrals for a dfs path and fills up the
798 junction_map structure.
799 **********************************************************************/
801 NTSTATUS get_referred_path(TALLOC_CTX *ctx,
802 const char *dfs_path,
803 struct junction_map *jucn,
804 int *consumedcntp,
805 bool *self_referralp)
807 struct connection_struct *conn;
808 char *targetpath = NULL;
809 int snum;
810 NTSTATUS status = NT_STATUS_NOT_FOUND;
811 bool dummy;
812 struct dfs_path *pdp = TALLOC_P(ctx, struct dfs_path);
813 char *oldpath;
815 if (!pdp) {
816 return NT_STATUS_NO_MEMORY;
819 *self_referralp = False;
821 status = parse_dfs_path(NULL, dfs_path, False, pdp, &dummy);
822 if (!NT_STATUS_IS_OK(status)) {
823 return status;
826 jucn->service_name = talloc_strdup(ctx, pdp->servicename);
827 jucn->volume_name = talloc_strdup(ctx, pdp->reqpath);
828 if (!jucn->service_name || !jucn->volume_name) {
829 TALLOC_FREE(pdp);
830 return NT_STATUS_NO_MEMORY;
833 /* Verify the share is a dfs root */
834 snum = lp_servicenumber(jucn->service_name);
835 if(snum < 0) {
836 fstring service_name;
837 fstrcpy(service_name, jucn->service_name);
838 if ((snum = find_service(service_name)) < 0) {
839 return NT_STATUS_NOT_FOUND;
841 TALLOC_FREE(jucn->service_name);
842 jucn->service_name = talloc_strdup(ctx, service_name);
843 if (!jucn->service_name) {
844 TALLOC_FREE(pdp);
845 return NT_STATUS_NO_MEMORY;
849 if (!lp_msdfs_root(snum) && (*lp_msdfs_proxy(snum) == '\0')) {
850 DEBUG(3,("get_referred_path: |%s| in dfs path %s is not "
851 "a dfs root.\n",
852 pdp->servicename, dfs_path));
853 TALLOC_FREE(pdp);
854 return NT_STATUS_NOT_FOUND;
858 * Self referrals are tested with a anonymous IPC connection and
859 * a GET_DFS_REFERRAL call to \\server\share. (which means
860 * dp.reqpath[0] points to an empty string). create_conn_struct cd's
861 * into the directory and will fail if it cannot (as the anonymous
862 * user). Cope with this.
865 if (pdp->reqpath[0] == '\0') {
866 char *tmp;
867 struct referral *ref;
869 if (*lp_msdfs_proxy(snum) == '\0') {
870 TALLOC_FREE(pdp);
871 return self_ref(ctx,
872 dfs_path,
873 jucn,
874 consumedcntp,
875 self_referralp);
879 * It's an msdfs proxy share. Redirect to
880 * the configured target share.
883 jucn->referral_count = 1;
884 if ((ref = TALLOC_ZERO_P(ctx, struct referral)) == NULL) {
885 TALLOC_FREE(pdp);
886 return NT_STATUS_NO_MEMORY;
889 if (!(tmp = talloc_strdup(ctx, lp_msdfs_proxy(snum)))) {
890 TALLOC_FREE(pdp);
891 return NT_STATUS_NO_MEMORY;
894 trim_string(tmp, "\\", 0);
896 ref->alternate_path = talloc_asprintf(ctx, "\\%s", tmp);
897 TALLOC_FREE(tmp);
899 if (!ref->alternate_path) {
900 TALLOC_FREE(pdp);
901 return NT_STATUS_NO_MEMORY;
904 if (pdp->reqpath[0] != '\0') {
905 ref->alternate_path = talloc_asprintf_append(
906 ref->alternate_path,
907 "%s",
908 pdp->reqpath);
909 if (!ref->alternate_path) {
910 TALLOC_FREE(pdp);
911 return NT_STATUS_NO_MEMORY;
914 ref->proximity = 0;
915 ref->ttl = REFERRAL_TTL;
916 jucn->referral_list = ref;
917 *consumedcntp = strlen(dfs_path);
918 TALLOC_FREE(pdp);
919 return NT_STATUS_OK;
922 status = create_conn_struct(ctx, &conn, snum, lp_pathname(snum),
923 NULL, &oldpath);
924 if (!NT_STATUS_IS_OK(status)) {
925 TALLOC_FREE(pdp);
926 return status;
929 /* If this is a DFS path dfs_lookup should return
930 * NT_STATUS_PATH_NOT_COVERED. */
932 status = dfs_path_lookup(ctx, conn, dfs_path, pdp,
933 False, consumedcntp, &targetpath);
935 if (!NT_STATUS_EQUAL(status, NT_STATUS_PATH_NOT_COVERED)) {
936 DEBUG(3,("get_referred_path: No valid referrals for path %s\n",
937 dfs_path));
938 vfs_ChDir(conn, oldpath);
939 conn_free(conn);
940 TALLOC_FREE(pdp);
941 return status;
944 /* We know this is a valid dfs link. Parse the targetpath. */
945 if (!parse_msdfs_symlink(ctx, targetpath,
946 &jucn->referral_list,
947 &jucn->referral_count)) {
948 DEBUG(3,("get_referred_path: failed to parse symlink "
949 "target %s\n", targetpath ));
950 vfs_ChDir(conn, oldpath);
951 conn_free(conn);
952 TALLOC_FREE(pdp);
953 return NT_STATUS_NOT_FOUND;
956 vfs_ChDir(conn, oldpath);
957 conn_free(conn);
958 TALLOC_FREE(pdp);
959 return NT_STATUS_OK;
962 static int setup_ver2_dfs_referral(const char *pathname,
963 char **ppdata,
964 struct junction_map *junction,
965 bool self_referral)
967 char* pdata = *ppdata;
969 smb_ucs2_t *uni_requestedpath = NULL;
970 int uni_reqpathoffset1,uni_reqpathoffset2;
971 int uni_curroffset;
972 int requestedpathlen=0;
973 int offset;
974 int reply_size = 0;
975 int i=0;
977 DEBUG(10,("Setting up version2 referral\nRequested path:\n"));
979 requestedpathlen = rpcstr_push_talloc(talloc_tos(),
980 &uni_requestedpath, pathname);
981 if (uni_requestedpath == NULL || requestedpathlen == 0) {
982 return -1;
985 if (DEBUGLVL(10)) {
986 dump_data(0, (unsigned char *)uni_requestedpath,
987 requestedpathlen);
990 DEBUG(10,("ref count = %u\n",junction->referral_count));
992 uni_reqpathoffset1 = REFERRAL_HEADER_SIZE +
993 VERSION2_REFERRAL_SIZE * junction->referral_count;
995 uni_reqpathoffset2 = uni_reqpathoffset1 + requestedpathlen;
997 uni_curroffset = uni_reqpathoffset2 + requestedpathlen;
999 reply_size = REFERRAL_HEADER_SIZE +
1000 VERSION2_REFERRAL_SIZE*junction->referral_count +
1001 2 * requestedpathlen;
1002 DEBUG(10,("reply_size: %u\n",reply_size));
1004 /* add up the unicode lengths of all the referral paths */
1005 for(i=0;i<junction->referral_count;i++) {
1006 DEBUG(10,("referral %u : %s\n",
1008 junction->referral_list[i].alternate_path));
1009 reply_size +=
1010 (strlen(junction->referral_list[i].alternate_path)+1)*2;
1013 DEBUG(10,("reply_size = %u\n",reply_size));
1014 /* add the unexplained 0x16 bytes */
1015 reply_size += 0x16;
1017 pdata = (char *)SMB_REALLOC(pdata,reply_size);
1018 if(pdata == NULL) {
1019 DEBUG(0,("Realloc failed!\n"));
1020 return -1;
1022 *ppdata = pdata;
1024 /* copy in the dfs requested paths.. required for offset calculations */
1025 memcpy(pdata+uni_reqpathoffset1,uni_requestedpath,requestedpathlen);
1026 memcpy(pdata+uni_reqpathoffset2,uni_requestedpath,requestedpathlen);
1028 /* create the header */
1029 SSVAL(pdata,0,requestedpathlen - 2); /* UCS2 of path consumed minus
1030 2 byte null */
1031 /* number of referral in this pkt */
1032 SSVAL(pdata,2,junction->referral_count);
1033 if(self_referral) {
1034 SIVAL(pdata,4,DFSREF_REFERRAL_SERVER | DFSREF_STORAGE_SERVER);
1035 } else {
1036 SIVAL(pdata,4,DFSREF_STORAGE_SERVER);
1039 offset = 8;
1040 /* add the referral elements */
1041 for(i=0;i<junction->referral_count;i++) {
1042 struct referral* ref = &junction->referral_list[i];
1043 int unilen;
1045 SSVAL(pdata,offset,2); /* version 2 */
1046 SSVAL(pdata,offset+2,VERSION2_REFERRAL_SIZE);
1047 if(self_referral) {
1048 SSVAL(pdata,offset+4,1);
1049 } else {
1050 SSVAL(pdata,offset+4,0);
1053 /* ref_flags :use path_consumed bytes? */
1054 SSVAL(pdata,offset+6,0);
1055 SIVAL(pdata,offset+8,ref->proximity);
1056 SIVAL(pdata,offset+12,ref->ttl);
1058 SSVAL(pdata,offset+16,uni_reqpathoffset1-offset);
1059 SSVAL(pdata,offset+18,uni_reqpathoffset2-offset);
1060 /* copy referred path into current offset */
1061 unilen = rpcstr_push(pdata+uni_curroffset,
1062 ref->alternate_path,
1063 reply_size - uni_curroffset,
1064 STR_UNICODE);
1066 SSVAL(pdata,offset+20,uni_curroffset-offset);
1068 uni_curroffset += unilen;
1069 offset += VERSION2_REFERRAL_SIZE;
1071 /* add in the unexplained 22 (0x16) bytes at the end */
1072 memset(pdata+uni_curroffset,'\0',0x16);
1073 return reply_size;
1076 static int setup_ver3_dfs_referral(const char *pathname,
1077 char **ppdata,
1078 struct junction_map *junction,
1079 bool self_referral)
1081 char *pdata = *ppdata;
1083 smb_ucs2_t *uni_reqpath = NULL;
1084 int uni_reqpathoffset1, uni_reqpathoffset2;
1085 int uni_curroffset;
1086 int reply_size = 0;
1088 int reqpathlen = 0;
1089 int offset,i=0;
1091 DEBUG(10,("setting up version3 referral\n"));
1093 reqpathlen = rpcstr_push_talloc(talloc_tos(), &uni_reqpath, pathname);
1094 if (uni_reqpath == NULL || reqpathlen == 0) {
1095 return -1;
1098 if (DEBUGLVL(10)) {
1099 dump_data(0, (unsigned char *)uni_reqpath,
1100 reqpathlen);
1103 uni_reqpathoffset1 = REFERRAL_HEADER_SIZE +
1104 VERSION3_REFERRAL_SIZE * junction->referral_count;
1105 uni_reqpathoffset2 = uni_reqpathoffset1 + reqpathlen;
1106 reply_size = uni_curroffset = uni_reqpathoffset2 + reqpathlen;
1108 for(i=0;i<junction->referral_count;i++) {
1109 DEBUG(10,("referral %u : %s\n",
1111 junction->referral_list[i].alternate_path));
1112 reply_size +=
1113 (strlen(junction->referral_list[i].alternate_path)+1)*2;
1116 pdata = (char *)SMB_REALLOC(pdata,reply_size);
1117 if(pdata == NULL) {
1118 DEBUG(0,("version3 referral setup:"
1119 "malloc failed for Realloc!\n"));
1120 return -1;
1122 *ppdata = pdata;
1124 /* create the header */
1125 SSVAL(pdata,0,reqpathlen - 2); /* UCS2 of path consumed minus
1126 2 byte null */
1127 SSVAL(pdata,2,junction->referral_count); /* number of referral */
1128 if(self_referral) {
1129 SIVAL(pdata,4,DFSREF_REFERRAL_SERVER | DFSREF_STORAGE_SERVER);
1130 } else {
1131 SIVAL(pdata,4,DFSREF_STORAGE_SERVER);
1134 /* copy in the reqpaths */
1135 memcpy(pdata+uni_reqpathoffset1,uni_reqpath,reqpathlen);
1136 memcpy(pdata+uni_reqpathoffset2,uni_reqpath,reqpathlen);
1138 offset = 8;
1139 for(i=0;i<junction->referral_count;i++) {
1140 struct referral* ref = &(junction->referral_list[i]);
1141 int unilen;
1143 SSVAL(pdata,offset,3); /* version 3 */
1144 SSVAL(pdata,offset+2,VERSION3_REFERRAL_SIZE);
1145 if(self_referral) {
1146 SSVAL(pdata,offset+4,1);
1147 } else {
1148 SSVAL(pdata,offset+4,0);
1151 /* ref_flags :use path_consumed bytes? */
1152 SSVAL(pdata,offset+6,0);
1153 SIVAL(pdata,offset+8,ref->ttl);
1155 SSVAL(pdata,offset+12,uni_reqpathoffset1-offset);
1156 SSVAL(pdata,offset+14,uni_reqpathoffset2-offset);
1157 /* copy referred path into current offset */
1158 unilen = rpcstr_push(pdata+uni_curroffset,ref->alternate_path,
1159 reply_size - uni_curroffset,
1160 STR_UNICODE | STR_TERMINATE);
1161 SSVAL(pdata,offset+16,uni_curroffset-offset);
1162 /* copy 0x10 bytes of 00's in the ServiceSite GUID */
1163 memset(pdata+offset+18,'\0',16);
1165 uni_curroffset += unilen;
1166 offset += VERSION3_REFERRAL_SIZE;
1168 return reply_size;
1171 /******************************************************************
1172 Set up the DFS referral for the dfs pathname. This call returns
1173 the amount of the path covered by this server, and where the
1174 client should be redirected to. This is the meat of the
1175 TRANS2_GET_DFS_REFERRAL call.
1176 ******************************************************************/
1178 int setup_dfs_referral(connection_struct *orig_conn,
1179 const char *dfs_path,
1180 int max_referral_level,
1181 char **ppdata, NTSTATUS *pstatus)
1183 struct junction_map *junction = NULL;
1184 int consumedcnt = 0;
1185 bool self_referral = False;
1186 int reply_size = 0;
1187 char *pathnamep = NULL;
1188 char *local_dfs_path = NULL;
1189 TALLOC_CTX *ctx;
1191 if (!(ctx=talloc_init("setup_dfs_referral"))) {
1192 *pstatus = NT_STATUS_NO_MEMORY;
1193 return -1;
1196 /* get the junction entry */
1197 if (!dfs_path) {
1198 talloc_destroy(ctx);
1199 *pstatus = NT_STATUS_NOT_FOUND;
1200 return -1;
1204 * Trim pathname sent by client so it begins with only one backslash.
1205 * Two backslashes confuse some dfs clients
1208 local_dfs_path = talloc_strdup(ctx,dfs_path);
1209 if (!local_dfs_path) {
1210 *pstatus = NT_STATUS_NO_MEMORY;
1211 talloc_destroy(ctx);
1212 return -1;
1214 pathnamep = local_dfs_path;
1215 while (IS_DIRECTORY_SEP(pathnamep[0]) &&
1216 IS_DIRECTORY_SEP(pathnamep[1])) {
1217 pathnamep++;
1220 junction = TALLOC_ZERO_P(ctx, struct junction_map);
1221 if (!junction) {
1222 *pstatus = NT_STATUS_NO_MEMORY;
1223 talloc_destroy(ctx);
1224 return -1;
1227 /* The following call can change cwd. */
1228 *pstatus = get_referred_path(ctx, pathnamep, junction,
1229 &consumedcnt, &self_referral);
1230 if (!NT_STATUS_IS_OK(*pstatus)) {
1231 vfs_ChDir(orig_conn,orig_conn->connectpath);
1232 talloc_destroy(ctx);
1233 return -1;
1235 vfs_ChDir(orig_conn,orig_conn->connectpath);
1237 if (!self_referral) {
1238 pathnamep[consumedcnt] = '\0';
1240 if( DEBUGLVL( 3 ) ) {
1241 int i=0;
1242 dbgtext("setup_dfs_referral: Path %s to "
1243 "alternate path(s):",
1244 pathnamep);
1245 for(i=0;i<junction->referral_count;i++)
1246 dbgtext(" %s",
1247 junction->referral_list[i].alternate_path);
1248 dbgtext(".\n");
1252 /* create the referral depeding on version */
1253 DEBUG(10,("max_referral_level :%d\n",max_referral_level));
1255 if (max_referral_level < 2) {
1256 max_referral_level = 2;
1258 if (max_referral_level > 3) {
1259 max_referral_level = 3;
1262 switch(max_referral_level) {
1263 case 2:
1264 reply_size = setup_ver2_dfs_referral(pathnamep,
1265 ppdata, junction,
1266 self_referral);
1267 break;
1268 case 3:
1269 reply_size = setup_ver3_dfs_referral(pathnamep, ppdata,
1270 junction, self_referral);
1271 break;
1272 default:
1273 DEBUG(0,("setup_dfs_referral: Invalid dfs referral "
1274 "version: %d\n",
1275 max_referral_level));
1276 talloc_destroy(ctx);
1277 *pstatus = NT_STATUS_INVALID_LEVEL;
1278 return -1;
1281 if (DEBUGLVL(10)) {
1282 DEBUGADD(0,("DFS Referral pdata:\n"));
1283 dump_data(0,(uint8 *)*ppdata,reply_size);
1286 talloc_destroy(ctx);
1287 *pstatus = NT_STATUS_OK;
1288 return reply_size;
1291 /**********************************************************************
1292 The following functions are called by the NETDFS RPC pipe functions
1293 **********************************************************************/
1295 /*********************************************************************
1296 Creates a junction structure from a DFS pathname
1297 **********************************************************************/
1299 bool create_junction(TALLOC_CTX *ctx,
1300 const char *dfs_path,
1301 struct junction_map *jucn)
1303 int snum;
1304 bool dummy;
1305 struct dfs_path *pdp = TALLOC_P(ctx,struct dfs_path);
1306 NTSTATUS status;
1308 if (!pdp) {
1309 return False;
1311 status = parse_dfs_path(NULL, dfs_path, False, pdp, &dummy);
1312 if (!NT_STATUS_IS_OK(status)) {
1313 return False;
1316 /* check if path is dfs : validate first token */
1317 if (!is_myname_or_ipaddr(pdp->hostname)) {
1318 DEBUG(4,("create_junction: Invalid hostname %s "
1319 "in dfs path %s\n",
1320 pdp->hostname, dfs_path));
1321 TALLOC_FREE(pdp);
1322 return False;
1325 /* Check for a non-DFS share */
1326 snum = lp_servicenumber(pdp->servicename);
1328 if(snum < 0 || !lp_msdfs_root(snum)) {
1329 DEBUG(4,("create_junction: %s is not an msdfs root.\n",
1330 pdp->servicename));
1331 TALLOC_FREE(pdp);
1332 return False;
1335 jucn->service_name = talloc_strdup(ctx, pdp->servicename);
1336 jucn->volume_name = talloc_strdup(ctx, pdp->reqpath);
1337 jucn->comment = talloc_strdup(ctx, lp_comment(snum));
1339 TALLOC_FREE(pdp);
1340 if (!jucn->service_name || !jucn->volume_name || ! jucn->comment) {
1341 return False;
1343 return True;
1346 /**********************************************************************
1347 Forms a valid Unix pathname from the junction
1348 **********************************************************************/
1350 static bool junction_to_local_path(const struct junction_map *jucn,
1351 char **pp_path_out,
1352 connection_struct **conn_out,
1353 char **oldpath)
1355 int snum;
1356 NTSTATUS status;
1358 snum = lp_servicenumber(jucn->service_name);
1359 if(snum < 0) {
1360 return False;
1362 status = create_conn_struct(talloc_tos(), conn_out, snum,
1363 lp_pathname(snum), NULL, oldpath);
1364 if (!NT_STATUS_IS_OK(status)) {
1365 return False;
1368 *pp_path_out = talloc_asprintf(*conn_out,
1369 "%s/%s",
1370 lp_pathname(snum),
1371 jucn->volume_name);
1372 if (!*pp_path_out) {
1373 vfs_ChDir(*conn_out, *oldpath);
1374 conn_free(*conn_out);
1375 return False;
1377 return True;
1380 bool create_msdfs_link(const struct junction_map *jucn)
1382 char *path = NULL;
1383 char *cwd;
1384 char *msdfs_link = NULL;
1385 connection_struct *conn;
1386 int i=0;
1387 bool insert_comma = False;
1388 bool ret = False;
1390 if(!junction_to_local_path(jucn, &path, &conn, &cwd)) {
1391 return False;
1394 /* Form the msdfs_link contents */
1395 msdfs_link = talloc_strdup(conn, "msdfs:");
1396 if (!msdfs_link) {
1397 goto out;
1399 for(i=0; i<jucn->referral_count; i++) {
1400 char *refpath = jucn->referral_list[i].alternate_path;
1402 /* Alternate paths always use Windows separators. */
1403 trim_char(refpath, '\\', '\\');
1404 if(*refpath == '\0') {
1405 if (i == 0) {
1406 insert_comma = False;
1408 continue;
1410 if (i > 0 && insert_comma) {
1411 msdfs_link = talloc_asprintf_append_buffer(msdfs_link,
1412 ",%s",
1413 refpath);
1414 } else {
1415 msdfs_link = talloc_asprintf_append_buffer(msdfs_link,
1416 "%s",
1417 refpath);
1420 if (!msdfs_link) {
1421 goto out;
1423 if (!insert_comma) {
1424 insert_comma = True;
1428 DEBUG(5,("create_msdfs_link: Creating new msdfs link: %s -> %s\n",
1429 path, msdfs_link));
1431 if(SMB_VFS_SYMLINK(conn, msdfs_link, path) < 0) {
1432 if (errno == EEXIST) {
1433 struct smb_filename *smb_fname = NULL;
1434 NTSTATUS status;
1436 status = create_synthetic_smb_fname(talloc_tos(), path,
1437 NULL, NULL,
1438 &smb_fname);
1439 if (!NT_STATUS_IS_OK(status)) {
1440 errno = map_errno_from_nt_status(status);
1441 goto out;
1444 if(SMB_VFS_UNLINK(conn, smb_fname)!=0) {
1445 TALLOC_FREE(smb_fname);
1446 goto out;
1448 TALLOC_FREE(smb_fname);
1450 if (SMB_VFS_SYMLINK(conn, msdfs_link, path) < 0) {
1451 DEBUG(1,("create_msdfs_link: symlink failed "
1452 "%s -> %s\nError: %s\n",
1453 path, msdfs_link, strerror(errno)));
1454 goto out;
1458 ret = True;
1460 out:
1461 vfs_ChDir(conn, cwd);
1462 conn_free(conn);
1463 return ret;
1466 bool remove_msdfs_link(const struct junction_map *jucn)
1468 char *path = NULL;
1469 char *cwd;
1470 connection_struct *conn;
1471 bool ret = False;
1472 struct smb_filename *smb_fname = NULL;
1473 NTSTATUS status;
1475 if (!junction_to_local_path(jucn, &path, &conn, &cwd)) {
1476 return false;
1479 status = create_synthetic_smb_fname(talloc_tos(), path,
1480 NULL, NULL,
1481 &smb_fname);
1482 if (!NT_STATUS_IS_OK(status)) {
1483 errno = map_errno_from_nt_status(status);
1484 return false;
1487 if( SMB_VFS_UNLINK(conn, smb_fname) == 0 ) {
1488 ret = True;
1491 TALLOC_FREE(smb_fname);
1492 vfs_ChDir(conn, cwd);
1493 conn_free(conn);
1494 return ret;
1497 /*********************************************************************
1498 Return the number of DFS links at the root of this share.
1499 *********************************************************************/
1501 static int count_dfs_links(TALLOC_CTX *ctx, int snum)
1503 size_t cnt = 0;
1504 SMB_STRUCT_DIR *dirp = NULL;
1505 const char *dname = NULL;
1506 char *talloced = NULL;
1507 const char *connect_path = lp_pathname(snum);
1508 const char *msdfs_proxy = lp_msdfs_proxy(snum);
1509 connection_struct *conn;
1510 NTSTATUS status;
1511 char *cwd;
1513 if(*connect_path == '\0') {
1514 return 0;
1518 * Fake up a connection struct for the VFS layer.
1521 status = create_conn_struct(talloc_tos(), &conn, snum, connect_path,
1522 NULL, &cwd);
1523 if (!NT_STATUS_IS_OK(status)) {
1524 DEBUG(3, ("create_conn_struct failed: %s\n",
1525 nt_errstr(status)));
1526 return 0;
1529 /* Count a link for the msdfs root - convention */
1530 cnt = 1;
1532 /* No more links if this is an msdfs proxy. */
1533 if (*msdfs_proxy != '\0') {
1534 goto out;
1537 /* Now enumerate all dfs links */
1538 dirp = SMB_VFS_OPENDIR(conn, ".", NULL, 0);
1539 if(!dirp) {
1540 goto out;
1543 while ((dname = vfs_readdirname(conn, dirp, NULL, &talloced))
1544 != NULL) {
1545 if (is_msdfs_link(conn,
1546 dname,
1547 NULL)) {
1548 cnt++;
1550 TALLOC_FREE(talloced);
1553 SMB_VFS_CLOSEDIR(conn,dirp);
1555 out:
1556 vfs_ChDir(conn, cwd);
1557 conn_free(conn);
1558 return cnt;
1561 /*********************************************************************
1562 *********************************************************************/
1564 static int form_junctions(TALLOC_CTX *ctx,
1565 int snum,
1566 struct junction_map *jucn,
1567 size_t jn_remain)
1569 size_t cnt = 0;
1570 SMB_STRUCT_DIR *dirp = NULL;
1571 const char *dname = NULL;
1572 char *talloced = NULL;
1573 const char *connect_path = lp_pathname(snum);
1574 char *service_name = lp_servicename(snum);
1575 const char *msdfs_proxy = lp_msdfs_proxy(snum);
1576 connection_struct *conn;
1577 struct referral *ref = NULL;
1578 char *cwd;
1579 NTSTATUS status;
1581 if (jn_remain == 0) {
1582 return 0;
1585 if(*connect_path == '\0') {
1586 return 0;
1590 * Fake up a connection struct for the VFS layer.
1593 status = create_conn_struct(ctx, &conn, snum, connect_path, NULL,
1594 &cwd);
1595 if (!NT_STATUS_IS_OK(status)) {
1596 DEBUG(3, ("create_conn_struct failed: %s\n",
1597 nt_errstr(status)));
1598 return 0;
1601 /* form a junction for the msdfs root - convention
1602 DO NOT REMOVE THIS: NT clients will not work with us
1603 if this is not present
1605 jucn[cnt].service_name = talloc_strdup(ctx,service_name);
1606 jucn[cnt].volume_name = talloc_strdup(ctx, "");
1607 if (!jucn[cnt].service_name || !jucn[cnt].volume_name) {
1608 goto out;
1610 jucn[cnt].comment = "";
1611 jucn[cnt].referral_count = 1;
1613 ref = jucn[cnt].referral_list = TALLOC_ZERO_P(ctx, struct referral);
1614 if (jucn[cnt].referral_list == NULL) {
1615 goto out;
1618 ref->proximity = 0;
1619 ref->ttl = REFERRAL_TTL;
1620 if (*msdfs_proxy != '\0') {
1621 ref->alternate_path = talloc_strdup(ctx,
1622 msdfs_proxy);
1623 } else {
1624 ref->alternate_path = talloc_asprintf(ctx,
1625 "\\\\%s\\%s",
1626 get_local_machine_name(),
1627 service_name);
1630 if (!ref->alternate_path) {
1631 goto out;
1633 cnt++;
1635 /* Don't enumerate if we're an msdfs proxy. */
1636 if (*msdfs_proxy != '\0') {
1637 goto out;
1640 /* Now enumerate all dfs links */
1641 dirp = SMB_VFS_OPENDIR(conn, ".", NULL, 0);
1642 if(!dirp) {
1643 goto out;
1646 while ((dname = vfs_readdirname(conn, dirp, NULL, &talloced))
1647 != NULL) {
1648 char *link_target = NULL;
1649 if (cnt >= jn_remain) {
1650 DEBUG(2, ("form_junctions: ran out of MSDFS "
1651 "junction slots"));
1652 TALLOC_FREE(talloced);
1653 goto out;
1655 if (is_msdfs_link_internal(ctx,
1656 conn,
1657 dname, &link_target,
1658 NULL)) {
1659 if (parse_msdfs_symlink(ctx,
1660 link_target,
1661 &jucn[cnt].referral_list,
1662 &jucn[cnt].referral_count)) {
1664 jucn[cnt].service_name = talloc_strdup(ctx,
1665 service_name);
1666 jucn[cnt].volume_name = talloc_strdup(ctx,
1667 dname);
1668 if (!jucn[cnt].service_name ||
1669 !jucn[cnt].volume_name) {
1670 TALLOC_FREE(talloced);
1671 goto out;
1673 jucn[cnt].comment = "";
1674 cnt++;
1676 TALLOC_FREE(link_target);
1678 TALLOC_FREE(talloced);
1681 out:
1683 if (dirp) {
1684 SMB_VFS_CLOSEDIR(conn,dirp);
1687 vfs_ChDir(conn, cwd);
1688 conn_free(conn);
1689 return cnt;
1692 struct junction_map *enum_msdfs_links(TALLOC_CTX *ctx, size_t *p_num_jn)
1694 struct junction_map *jn = NULL;
1695 int i=0;
1696 size_t jn_count = 0;
1697 int sharecount = 0;
1699 *p_num_jn = 0;
1700 if(!lp_host_msdfs()) {
1701 return NULL;
1704 /* Ensure all the usershares are loaded. */
1705 become_root();
1706 load_registry_shares();
1707 sharecount = load_usershare_shares();
1708 unbecome_root();
1710 for(i=0;i < sharecount;i++) {
1711 if(lp_msdfs_root(i)) {
1712 jn_count += count_dfs_links(ctx, i);
1715 if (jn_count == 0) {
1716 return NULL;
1718 jn = TALLOC_ARRAY(ctx, struct junction_map, jn_count);
1719 if (!jn) {
1720 return NULL;
1722 for(i=0; i < sharecount; i++) {
1723 if (*p_num_jn >= jn_count) {
1724 break;
1726 if(lp_msdfs_root(i)) {
1727 *p_num_jn += form_junctions(ctx, i,
1728 &jn[*p_num_jn],
1729 jn_count - *p_num_jn);
1732 return jn;
1735 /******************************************************************************
1736 Core function to resolve a dfs pathname possibly containing a wildcard. If
1737 ppath_contains_wcard != NULL, it will be set to true if a wildcard is
1738 detected during dfs resolution.
1739 ******************************************************************************/
1741 NTSTATUS resolve_dfspath_wcard(TALLOC_CTX *ctx,
1742 connection_struct *conn,
1743 bool dfs_pathnames,
1744 const char *name_in,
1745 bool allow_wcards,
1746 char **pp_name_out,
1747 bool *ppath_contains_wcard)
1749 bool path_contains_wcard;
1750 NTSTATUS status = NT_STATUS_OK;
1752 if (dfs_pathnames) {
1753 status = dfs_redirect(ctx,
1754 conn,
1755 name_in,
1756 allow_wcards,
1757 pp_name_out,
1758 &path_contains_wcard);
1760 if (NT_STATUS_IS_OK(status) && ppath_contains_wcard != NULL) {
1761 *ppath_contains_wcard = path_contains_wcard;
1763 } else {
1765 * Cheat and just return a copy of the in ptr.
1766 * Once srvstr_get_path() uses talloc it'll
1767 * be a talloced ptr anyway.
1769 *pp_name_out = CONST_DISCARD(char *,name_in);
1771 return status;