s3-printing: use winreg interface for migration, instead of spoolss.
[Samba.git] / source3 / smbd / msdfs.c
blobab67ac8596d9074719cde71346562df366e5a719
1 /*
2 Unix SMB/Netbios implementation.
3 Version 3.0
4 MSDFS services for Samba
5 Copyright (C) Shirish Kalele 2000
6 Copyright (C) Jeremy Allison 2007
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 3 of the License, or
11 (at your option) any later version.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program. If not, see <http://www.gnu.org/licenses/>.
23 #define DBGC_CLASS DBGC_MSDFS
24 #include "includes.h"
25 #include "system/filesys.h"
26 #include "smbd/smbd.h"
27 #include "smbd/globals.h"
28 #include "msdfs.h"
29 #include "auth.h"
31 /**********************************************************************
32 Parse a DFS pathname of the form \hostname\service\reqpath
33 into the dfs_path structure.
34 If POSIX pathnames is true, the pathname may also be of the
35 form /hostname/service/reqpath.
36 We cope with either here.
38 Unfortunately, due to broken clients who might set the
39 SVAL(inbuf,smb_flg2) & FLAGS2_DFS_PATHNAMES bit and then
40 send a local path, we have to cope with that too....
42 If conn != NULL then ensure the provided service is
43 the one pointed to by the connection.
45 This version does everything using pointers within one copy of the
46 pathname string, talloced on the struct dfs_path pointer (which
47 must be talloced). This may be too clever to live....
48 JRA.
49 **********************************************************************/
51 static NTSTATUS parse_dfs_path(connection_struct *conn,
52 const char *pathname,
53 bool allow_wcards,
54 struct dfs_path *pdp, /* MUST BE TALLOCED */
55 bool *ppath_contains_wcard)
57 struct smbd_server_connection *sconn = smbd_server_conn;
58 char *pathname_local;
59 char *p,*temp;
60 char *servicename;
61 char *eos_ptr;
62 NTSTATUS status = NT_STATUS_OK;
63 char sepchar;
65 ZERO_STRUCTP(pdp);
68 * This is the only talloc we should need to do
69 * on the struct dfs_path. All the pointers inside
70 * it should point to offsets within this string.
73 pathname_local = talloc_strdup(pdp, pathname);
74 if (!pathname_local) {
75 return NT_STATUS_NO_MEMORY;
77 /* Get a pointer to the terminating '\0' */
78 eos_ptr = &pathname_local[strlen(pathname_local)];
79 p = temp = pathname_local;
81 pdp->posix_path = (lp_posix_pathnames() && *pathname == '/');
83 sepchar = pdp->posix_path ? '/' : '\\';
85 if (!sconn->using_smb2 && (*pathname != sepchar)) {
86 DEBUG(10,("parse_dfs_path: path %s doesn't start with %c\n",
87 pathname, sepchar ));
89 * Possibly client sent a local path by mistake.
90 * Try and convert to a local path.
93 pdp->hostname = eos_ptr; /* "" */
94 pdp->servicename = eos_ptr; /* "" */
96 /* We've got no info about separators. */
97 pdp->posix_path = lp_posix_pathnames();
98 p = temp;
99 DEBUG(10,("parse_dfs_path: trying to convert %s to a "
100 "local path\n",
101 temp));
102 goto local_path;
106 * Safe to use on talloc'ed string as it only shrinks.
107 * It also doesn't affect the eos_ptr.
109 trim_char(temp,sepchar,sepchar);
111 DEBUG(10,("parse_dfs_path: temp = |%s| after trimming %c's\n",
112 temp, sepchar));
114 /* Now tokenize. */
115 /* Parse out hostname. */
116 p = strchr_m(temp,sepchar);
117 if(p == NULL) {
118 DEBUG(10,("parse_dfs_path: can't parse hostname from path %s\n",
119 temp));
121 * Possibly client sent a local path by mistake.
122 * Try and convert to a local path.
125 pdp->hostname = eos_ptr; /* "" */
126 pdp->servicename = eos_ptr; /* "" */
128 p = temp;
129 DEBUG(10,("parse_dfs_path: trying to convert %s "
130 "to a local path\n",
131 temp));
132 goto local_path;
134 *p = '\0';
135 pdp->hostname = temp;
137 DEBUG(10,("parse_dfs_path: hostname: %s\n",pdp->hostname));
139 /* Parse out servicename. */
140 servicename = p+1;
141 p = strchr_m(servicename,sepchar);
142 if (p) {
143 *p = '\0';
146 /* Is this really our servicename ? */
147 if (conn && !( strequal(servicename, lp_servicename(SNUM(conn)))
148 || (strequal(servicename, HOMES_NAME)
149 && strequal(lp_servicename(SNUM(conn)),
150 get_current_username()) )) ) {
151 DEBUG(10,("parse_dfs_path: %s is not our servicename\n",
152 servicename));
155 * Possibly client sent a local path by mistake.
156 * Try and convert to a local path.
159 pdp->hostname = eos_ptr; /* "" */
160 pdp->servicename = eos_ptr; /* "" */
162 /* Repair the path - replace the sepchar's
163 we nulled out */
164 servicename--;
165 *servicename = sepchar;
166 if (p) {
167 *p = sepchar;
170 p = temp;
171 DEBUG(10,("parse_dfs_path: trying to convert %s "
172 "to a local path\n",
173 temp));
174 goto local_path;
177 pdp->servicename = servicename;
179 DEBUG(10,("parse_dfs_path: servicename: %s\n",pdp->servicename));
181 if(p == NULL) {
182 /* Client sent self referral \server\share. */
183 pdp->reqpath = eos_ptr; /* "" */
184 return NT_STATUS_OK;
187 p++;
189 local_path:
191 *ppath_contains_wcard = False;
193 pdp->reqpath = p;
195 /* Rest is reqpath. */
196 if (pdp->posix_path) {
197 status = check_path_syntax_posix(pdp->reqpath);
198 } else {
199 if (allow_wcards) {
200 status = check_path_syntax_wcard(pdp->reqpath,
201 ppath_contains_wcard);
202 } else {
203 status = check_path_syntax(pdp->reqpath);
207 if (!NT_STATUS_IS_OK(status)) {
208 DEBUG(10,("parse_dfs_path: '%s' failed with %s\n",
209 p, nt_errstr(status) ));
210 return status;
213 DEBUG(10,("parse_dfs_path: rest of the path: %s\n",pdp->reqpath));
214 return NT_STATUS_OK;
217 /********************************************************
218 Fake up a connection struct for the VFS layer.
219 Note: this performs a vfs connect and CHANGES CWD !!!! JRA.
220 *********************************************************/
222 NTSTATUS create_conn_struct(TALLOC_CTX *ctx,
223 connection_struct **pconn,
224 int snum,
225 const char *path,
226 const struct auth_serversupplied_info *session_info,
227 char **poldcwd)
229 connection_struct *conn;
230 char *connpath;
231 char *oldcwd;
232 const char *vfs_user;
234 conn = TALLOC_ZERO_P(ctx, connection_struct);
235 if (conn == NULL) {
236 return NT_STATUS_NO_MEMORY;
239 connpath = talloc_strdup(conn, path);
240 if (!connpath) {
241 TALLOC_FREE(conn);
242 return NT_STATUS_NO_MEMORY;
244 connpath = talloc_string_sub(conn,
245 connpath,
246 "%S",
247 lp_servicename(snum));
248 if (!connpath) {
249 TALLOC_FREE(conn);
250 return NT_STATUS_NO_MEMORY;
253 /* needed for smbd_vfs_init() */
255 if (!(conn->params = TALLOC_ZERO_P(conn, struct share_params))) {
256 DEBUG(0, ("TALLOC failed\n"));
257 TALLOC_FREE(conn);
258 return NT_STATUS_NO_MEMORY;
261 conn->params->service = snum;
263 conn->sconn = smbd_server_conn;
264 conn->sconn->num_tcons_open++;
266 if (session_info != NULL) {
267 conn->session_info = copy_serverinfo(conn, session_info);
268 if (conn->session_info == NULL) {
269 DEBUG(0, ("copy_serverinfo failed\n"));
270 TALLOC_FREE(conn);
271 return NT_STATUS_NO_MEMORY;
273 vfs_user = conn->session_info->unix_name;
274 } else {
275 /* use current authenticated user in absence of session_info */
276 vfs_user = get_current_username();
279 set_conn_connectpath(conn, connpath);
281 if (!smbd_vfs_init(conn)) {
282 NTSTATUS status = map_nt_error_from_unix(errno);
283 DEBUG(0,("create_conn_struct: smbd_vfs_init failed.\n"));
284 conn_free(conn);
285 return status;
288 /* this must be the first filesystem operation that we do */
289 if (SMB_VFS_CONNECT(conn, lp_servicename(snum), vfs_user) < 0) {
290 DEBUG(0,("VFS connect failed!\n"));
291 conn_free(conn);
292 return NT_STATUS_UNSUCCESSFUL;
295 conn->fs_capabilities = SMB_VFS_FS_CAPABILITIES(conn, &conn->ts_res);
298 * Windows seems to insist on doing trans2getdfsreferral() calls on
299 * the IPC$ share as the anonymous user. If we try to chdir as that
300 * user we will fail.... WTF ? JRA.
303 oldcwd = vfs_GetWd(ctx, conn);
304 if (oldcwd == NULL) {
305 NTSTATUS status = map_nt_error_from_unix(errno);
306 DEBUG(3, ("vfs_GetWd failed: %s\n", strerror(errno)));
307 conn_free(conn);
308 return status;
311 if (vfs_ChDir(conn,conn->connectpath) != 0) {
312 NTSTATUS status = map_nt_error_from_unix(errno);
313 DEBUG(3,("create_conn_struct: Can't ChDir to new conn path %s. "
314 "Error was %s\n",
315 conn->connectpath, strerror(errno) ));
316 conn_free(conn);
317 return status;
320 *pconn = conn;
321 *poldcwd = oldcwd;
323 return NT_STATUS_OK;
326 /**********************************************************************
327 Parse the contents of a symlink to verify if it is an msdfs referral
328 A valid referral is of the form:
330 msdfs:server1\share1,server2\share2
331 msdfs:server1\share1\pathname,server2\share2\pathname
332 msdfs:server1/share1,server2/share2
333 msdfs:server1/share1/pathname,server2/share2/pathname.
335 Note that the alternate paths returned here must be of the canonicalized
336 form:
338 \server\share or
339 \server\share\path\to\file,
341 even in posix path mode. This is because we have no knowledge if the
342 server we're referring to understands posix paths.
343 **********************************************************************/
345 static bool parse_msdfs_symlink(TALLOC_CTX *ctx,
346 const char *target,
347 struct referral **preflist,
348 int *refcount)
350 char *temp = NULL;
351 char *prot;
352 char **alt_path = NULL;
353 int count = 0, i;
354 struct referral *reflist;
355 char *saveptr;
357 temp = talloc_strdup(ctx, target);
358 if (!temp) {
359 return False;
361 prot = strtok_r(temp, ":", &saveptr);
362 if (!prot) {
363 DEBUG(0,("parse_msdfs_symlink: invalid path !\n"));
364 return False;
367 alt_path = TALLOC_ARRAY(ctx, char *, MAX_REFERRAL_COUNT);
368 if (!alt_path) {
369 return False;
372 /* parse out the alternate paths */
373 while((count<MAX_REFERRAL_COUNT) &&
374 ((alt_path[count] = strtok_r(NULL, ",", &saveptr)) != NULL)) {
375 count++;
378 DEBUG(10,("parse_msdfs_symlink: count=%d\n", count));
380 if (count) {
381 reflist = *preflist = TALLOC_ZERO_ARRAY(ctx,
382 struct referral, count);
383 if(reflist == NULL) {
384 TALLOC_FREE(alt_path);
385 return False;
387 } else {
388 reflist = *preflist = NULL;
391 for(i=0;i<count;i++) {
392 char *p;
394 /* Canonicalize link target.
395 * Replace all /'s in the path by a \ */
396 string_replace(alt_path[i], '/', '\\');
398 /* Remove leading '\\'s */
399 p = alt_path[i];
400 while (*p && (*p == '\\')) {
401 p++;
404 reflist[i].alternate_path = talloc_asprintf(ctx,
405 "\\%s",
407 if (!reflist[i].alternate_path) {
408 return False;
411 reflist[i].proximity = 0;
412 reflist[i].ttl = REFERRAL_TTL;
413 DEBUG(10, ("parse_msdfs_symlink: Created alt path: %s\n",
414 reflist[i].alternate_path));
417 *refcount = count;
419 TALLOC_FREE(alt_path);
420 return True;
423 /**********************************************************************
424 Returns true if the unix path is a valid msdfs symlink and also
425 returns the target string from inside the link.
426 **********************************************************************/
428 static bool is_msdfs_link_internal(TALLOC_CTX *ctx,
429 connection_struct *conn,
430 const char *path,
431 char **pp_link_target,
432 SMB_STRUCT_STAT *sbufp)
434 int referral_len = 0;
435 #if defined(HAVE_BROKEN_READLINK)
436 char link_target_buf[PATH_MAX];
437 #else
438 char link_target_buf[7];
439 #endif
440 size_t bufsize = 0;
441 char *link_target = NULL;
442 struct smb_filename smb_fname;
444 if (pp_link_target) {
445 bufsize = 1024;
446 link_target = TALLOC_ARRAY(ctx, char, bufsize);
447 if (!link_target) {
448 return False;
450 *pp_link_target = link_target;
451 } else {
452 bufsize = sizeof(link_target_buf);
453 link_target = link_target_buf;
456 ZERO_STRUCT(smb_fname);
457 smb_fname.base_name = discard_const_p(char, path);
459 if (SMB_VFS_LSTAT(conn, &smb_fname) != 0) {
460 DEBUG(5,("is_msdfs_link_read_target: %s does not exist.\n",
461 path));
462 goto err;
464 if (!S_ISLNK(smb_fname.st.st_ex_mode)) {
465 DEBUG(5,("is_msdfs_link_read_target: %s is not a link.\n",
466 path));
467 goto err;
469 if (sbufp != NULL) {
470 *sbufp = smb_fname.st;
473 referral_len = SMB_VFS_READLINK(conn, path, link_target, bufsize - 1);
474 if (referral_len == -1) {
475 DEBUG(0,("is_msdfs_link_read_target: Error reading "
476 "msdfs link %s: %s\n",
477 path, strerror(errno)));
478 goto err;
480 link_target[referral_len] = '\0';
482 DEBUG(5,("is_msdfs_link_internal: %s -> %s\n",path,
483 link_target));
485 if (!strnequal(link_target, "msdfs:", 6)) {
486 goto err;
488 return True;
490 err:
492 if (link_target != link_target_buf) {
493 TALLOC_FREE(link_target);
495 return False;
498 /**********************************************************************
499 Returns true if the unix path is a valid msdfs symlink.
500 **********************************************************************/
502 bool is_msdfs_link(connection_struct *conn,
503 const char *path,
504 SMB_STRUCT_STAT *sbufp)
506 return is_msdfs_link_internal(talloc_tos(),
507 conn,
508 path,
509 NULL,
510 sbufp);
513 /*****************************************************************
514 Used by other functions to decide if a dfs path is remote,
515 and to get the list of referred locations for that remote path.
517 search_flag: For findfirsts, dfs links themselves are not
518 redirected, but paths beyond the links are. For normal smb calls,
519 even dfs links need to be redirected.
521 consumedcntp: how much of the dfs path is being redirected. the client
522 should try the remaining path on the redirected server.
524 If this returns NT_STATUS_PATH_NOT_COVERED the contents of the msdfs
525 link redirect are in targetpath.
526 *****************************************************************/
528 static NTSTATUS dfs_path_lookup(TALLOC_CTX *ctx,
529 connection_struct *conn,
530 const char *dfspath, /* Incoming complete dfs path */
531 const struct dfs_path *pdp, /* Parsed out
532 server+share+extrapath. */
533 bool search_flag, /* Called from a findfirst ? */
534 int *consumedcntp,
535 char **pp_targetpath)
537 char *p = NULL;
538 char *q = NULL;
539 NTSTATUS status;
540 struct smb_filename *smb_fname = NULL;
541 char *canon_dfspath = NULL; /* Canonicalized dfs path. (only '/'
542 components). */
544 DEBUG(10,("dfs_path_lookup: Conn path = %s reqpath = %s\n",
545 conn->connectpath, pdp->reqpath));
548 * Note the unix path conversion here we're doing we can
549 * throw away. We're looking for a symlink for a dfs
550 * resolution, if we don't find it we'll do another
551 * unix_convert later in the codepath.
552 * If we needed to remember what we'd resolved in
553 * dp->reqpath (as the original code did) we'd
554 * copy (localhost, dp->reqpath) on any code
555 * path below that returns True - but I don't
556 * think this is needed. JRA.
559 status = unix_convert(ctx, conn, pdp->reqpath, &smb_fname,
560 search_flag ? UCF_ALWAYS_ALLOW_WCARD_LCOMP : 0);
562 if (!NT_STATUS_IS_OK(status)) {
563 if (!NT_STATUS_EQUAL(status,
564 NT_STATUS_OBJECT_PATH_NOT_FOUND)) {
565 return status;
568 /* Create an smb_fname to use below. */
569 status = create_synthetic_smb_fname(ctx, pdp->reqpath, NULL,
570 NULL, &smb_fname);
571 if (!NT_STATUS_IS_OK(status)) {
572 return status;
576 /* Optimization - check if we can redirect the whole path. */
578 if (is_msdfs_link_internal(ctx, conn, smb_fname->base_name,
579 pp_targetpath, NULL)) {
580 if (search_flag) {
581 DEBUG(6,("dfs_path_lookup (FindFirst) No redirection "
582 "for dfs link %s.\n", dfspath));
583 status = NT_STATUS_OK;
584 goto out;
587 DEBUG(6,("dfs_path_lookup: %s resolves to a "
588 "valid dfs link %s.\n", dfspath,
589 pp_targetpath ? *pp_targetpath : ""));
591 if (consumedcntp) {
592 *consumedcntp = strlen(dfspath);
594 status = NT_STATUS_PATH_NOT_COVERED;
595 goto out;
598 /* Prepare to test only for '/' components in the given path,
599 * so if a Windows path replace all '\\' characters with '/'.
600 * For a POSIX DFS path we know all separators are already '/'. */
602 canon_dfspath = talloc_strdup(ctx, dfspath);
603 if (!canon_dfspath) {
604 status = NT_STATUS_NO_MEMORY;
605 goto out;
607 if (!pdp->posix_path) {
608 string_replace(canon_dfspath, '\\', '/');
612 * localpath comes out of unix_convert, so it has
613 * no trailing backslash. Make sure that canon_dfspath hasn't either.
614 * Fix for bug #4860 from Jan Martin <Jan.Martin@rwedea.com>.
617 trim_char(canon_dfspath,0,'/');
620 * Redirect if any component in the path is a link.
621 * We do this by walking backwards through the
622 * local path, chopping off the last component
623 * in both the local path and the canonicalized
624 * DFS path. If we hit a DFS link then we're done.
627 p = strrchr_m(smb_fname->base_name, '/');
628 if (consumedcntp) {
629 q = strrchr_m(canon_dfspath, '/');
632 while (p) {
633 *p = '\0';
634 if (q) {
635 *q = '\0';
638 if (is_msdfs_link_internal(ctx, conn,
639 smb_fname->base_name, pp_targetpath,
640 NULL)) {
641 DEBUG(4, ("dfs_path_lookup: Redirecting %s because "
642 "parent %s is dfs link\n", dfspath,
643 smb_fname_str_dbg(smb_fname)));
645 if (consumedcntp) {
646 *consumedcntp = strlen(canon_dfspath);
647 DEBUG(10, ("dfs_path_lookup: Path consumed: %s "
648 "(%d)\n",
649 canon_dfspath,
650 *consumedcntp));
653 status = NT_STATUS_PATH_NOT_COVERED;
654 goto out;
657 /* Step back on the filesystem. */
658 p = strrchr_m(smb_fname->base_name, '/');
660 if (consumedcntp) {
661 /* And in the canonicalized dfs path. */
662 q = strrchr_m(canon_dfspath, '/');
666 status = NT_STATUS_OK;
667 out:
668 TALLOC_FREE(smb_fname);
669 return status;
672 /*****************************************************************
673 Decides if a dfs pathname should be redirected or not.
674 If not, the pathname is converted to a tcon-relative local unix path
676 search_wcard_flag: this flag performs 2 functions both related
677 to searches. See resolve_dfs_path() and parse_dfs_path_XX()
678 for details.
680 This function can return NT_STATUS_OK, meaning use the returned path as-is
681 (mapped into a local path).
682 or NT_STATUS_NOT_COVERED meaning return a DFS redirect, or
683 any other NT_STATUS error which is a genuine error to be
684 returned to the client.
685 *****************************************************************/
687 static NTSTATUS dfs_redirect(TALLOC_CTX *ctx,
688 connection_struct *conn,
689 const char *path_in,
690 bool search_wcard_flag,
691 char **pp_path_out,
692 bool *ppath_contains_wcard)
694 NTSTATUS status;
695 struct dfs_path *pdp = TALLOC_P(ctx, struct dfs_path);
697 if (!pdp) {
698 return NT_STATUS_NO_MEMORY;
701 status = parse_dfs_path(conn, path_in, search_wcard_flag, pdp,
702 ppath_contains_wcard);
703 if (!NT_STATUS_IS_OK(status)) {
704 TALLOC_FREE(pdp);
705 return status;
708 if (pdp->reqpath[0] == '\0') {
709 TALLOC_FREE(pdp);
710 *pp_path_out = talloc_strdup(ctx, "");
711 if (!*pp_path_out) {
712 return NT_STATUS_NO_MEMORY;
714 DEBUG(5,("dfs_redirect: self-referral.\n"));
715 return NT_STATUS_OK;
718 /* If dfs pathname for a non-dfs share, convert to tcon-relative
719 path and return OK */
721 if (!lp_msdfs_root(SNUM(conn))) {
722 *pp_path_out = talloc_strdup(ctx, pdp->reqpath);
723 TALLOC_FREE(pdp);
724 if (!*pp_path_out) {
725 return NT_STATUS_NO_MEMORY;
727 return NT_STATUS_OK;
730 /* If it looked like a local path (zero hostname/servicename)
731 * just treat as a tcon-relative path. */
733 if (pdp->hostname[0] == '\0' && pdp->servicename[0] == '\0') {
734 *pp_path_out = talloc_strdup(ctx, pdp->reqpath);
735 TALLOC_FREE(pdp);
736 if (!*pp_path_out) {
737 return NT_STATUS_NO_MEMORY;
739 return NT_STATUS_OK;
742 if (!( strequal(pdp->servicename, lp_servicename(SNUM(conn)))
743 || (strequal(pdp->servicename, HOMES_NAME)
744 && strequal(lp_servicename(SNUM(conn)),
745 conn->session_info->sanitized_username) )) ) {
747 /* The given sharename doesn't match this connection. */
748 TALLOC_FREE(pdp);
750 return NT_STATUS_OBJECT_PATH_NOT_FOUND;
753 status = dfs_path_lookup(ctx, conn, path_in, pdp,
754 search_wcard_flag, NULL, NULL);
755 if (!NT_STATUS_IS_OK(status)) {
756 if (NT_STATUS_EQUAL(status, NT_STATUS_PATH_NOT_COVERED)) {
757 DEBUG(3,("dfs_redirect: Redirecting %s\n", path_in));
758 } else {
759 DEBUG(10,("dfs_redirect: dfs_path_lookup "
760 "failed for %s with %s\n",
761 path_in, nt_errstr(status) ));
763 return status;
766 DEBUG(3,("dfs_redirect: Not redirecting %s.\n", path_in));
768 /* Form non-dfs tcon-relative path */
769 *pp_path_out = talloc_strdup(ctx, pdp->reqpath);
770 TALLOC_FREE(pdp);
771 if (!*pp_path_out) {
772 return NT_STATUS_NO_MEMORY;
775 DEBUG(3,("dfs_redirect: Path %s converted to non-dfs path %s\n",
776 path_in,
777 *pp_path_out));
779 return NT_STATUS_OK;
782 /**********************************************************************
783 Return a self referral.
784 **********************************************************************/
786 static NTSTATUS self_ref(TALLOC_CTX *ctx,
787 const char *dfs_path,
788 struct junction_map *jucn,
789 int *consumedcntp,
790 bool *self_referralp)
792 struct referral *ref;
794 *self_referralp = True;
796 jucn->referral_count = 1;
797 if((ref = TALLOC_ZERO_P(ctx, struct referral)) == NULL) {
798 return NT_STATUS_NO_MEMORY;
801 ref->alternate_path = talloc_strdup(ctx, dfs_path);
802 if (!ref->alternate_path) {
803 return NT_STATUS_NO_MEMORY;
805 ref->proximity = 0;
806 ref->ttl = REFERRAL_TTL;
807 jucn->referral_list = ref;
808 *consumedcntp = strlen(dfs_path);
809 return NT_STATUS_OK;
812 /**********************************************************************
813 Gets valid referrals for a dfs path and fills up the
814 junction_map structure.
815 **********************************************************************/
817 NTSTATUS get_referred_path(TALLOC_CTX *ctx,
818 const char *dfs_path,
819 struct junction_map *jucn,
820 int *consumedcntp,
821 bool *self_referralp)
823 struct connection_struct *conn;
824 char *targetpath = NULL;
825 int snum;
826 NTSTATUS status = NT_STATUS_NOT_FOUND;
827 bool dummy;
828 struct dfs_path *pdp = TALLOC_P(ctx, struct dfs_path);
829 char *oldpath;
831 if (!pdp) {
832 return NT_STATUS_NO_MEMORY;
835 *self_referralp = False;
837 status = parse_dfs_path(NULL, dfs_path, False, pdp, &dummy);
838 if (!NT_STATUS_IS_OK(status)) {
839 return status;
842 jucn->service_name = talloc_strdup(ctx, pdp->servicename);
843 jucn->volume_name = talloc_strdup(ctx, pdp->reqpath);
844 if (!jucn->service_name || !jucn->volume_name) {
845 TALLOC_FREE(pdp);
846 return NT_STATUS_NO_MEMORY;
849 /* Verify the share is a dfs root */
850 snum = lp_servicenumber(jucn->service_name);
851 if(snum < 0) {
852 char *service_name = NULL;
853 if ((snum = find_service(ctx, jucn->service_name, &service_name)) < 0) {
854 return NT_STATUS_NOT_FOUND;
856 if (!service_name) {
857 return NT_STATUS_NO_MEMORY;
859 TALLOC_FREE(jucn->service_name);
860 jucn->service_name = talloc_strdup(ctx, service_name);
861 if (!jucn->service_name) {
862 TALLOC_FREE(pdp);
863 return NT_STATUS_NO_MEMORY;
867 if (!lp_msdfs_root(snum) && (*lp_msdfs_proxy(snum) == '\0')) {
868 DEBUG(3,("get_referred_path: |%s| in dfs path %s is not "
869 "a dfs root.\n",
870 pdp->servicename, dfs_path));
871 TALLOC_FREE(pdp);
872 return NT_STATUS_NOT_FOUND;
876 * Self referrals are tested with a anonymous IPC connection and
877 * a GET_DFS_REFERRAL call to \\server\share. (which means
878 * dp.reqpath[0] points to an empty string). create_conn_struct cd's
879 * into the directory and will fail if it cannot (as the anonymous
880 * user). Cope with this.
883 if (pdp->reqpath[0] == '\0') {
884 char *tmp;
885 struct referral *ref;
887 if (*lp_msdfs_proxy(snum) == '\0') {
888 TALLOC_FREE(pdp);
889 return self_ref(ctx,
890 dfs_path,
891 jucn,
892 consumedcntp,
893 self_referralp);
897 * It's an msdfs proxy share. Redirect to
898 * the configured target share.
901 jucn->referral_count = 1;
902 if ((ref = TALLOC_ZERO_P(ctx, struct referral)) == NULL) {
903 TALLOC_FREE(pdp);
904 return NT_STATUS_NO_MEMORY;
907 if (!(tmp = talloc_strdup(ctx, lp_msdfs_proxy(snum)))) {
908 TALLOC_FREE(pdp);
909 return NT_STATUS_NO_MEMORY;
912 trim_string(tmp, "\\", 0);
914 ref->alternate_path = talloc_asprintf(ctx, "\\%s", tmp);
915 TALLOC_FREE(tmp);
917 if (!ref->alternate_path) {
918 TALLOC_FREE(pdp);
919 return NT_STATUS_NO_MEMORY;
922 if (pdp->reqpath[0] != '\0') {
923 ref->alternate_path = talloc_asprintf_append(
924 ref->alternate_path,
925 "%s",
926 pdp->reqpath);
927 if (!ref->alternate_path) {
928 TALLOC_FREE(pdp);
929 return NT_STATUS_NO_MEMORY;
932 ref->proximity = 0;
933 ref->ttl = REFERRAL_TTL;
934 jucn->referral_list = ref;
935 *consumedcntp = strlen(dfs_path);
936 TALLOC_FREE(pdp);
937 return NT_STATUS_OK;
940 status = create_conn_struct(ctx, &conn, snum, lp_pathname(snum),
941 NULL, &oldpath);
942 if (!NT_STATUS_IS_OK(status)) {
943 TALLOC_FREE(pdp);
944 return status;
947 /* If this is a DFS path dfs_lookup should return
948 * NT_STATUS_PATH_NOT_COVERED. */
950 status = dfs_path_lookup(ctx, conn, dfs_path, pdp,
951 False, consumedcntp, &targetpath);
953 if (!NT_STATUS_EQUAL(status, NT_STATUS_PATH_NOT_COVERED)) {
954 DEBUG(3,("get_referred_path: No valid referrals for path %s\n",
955 dfs_path));
956 goto err_exit;
959 /* We know this is a valid dfs link. Parse the targetpath. */
960 if (!parse_msdfs_symlink(ctx, targetpath,
961 &jucn->referral_list,
962 &jucn->referral_count)) {
963 DEBUG(3,("get_referred_path: failed to parse symlink "
964 "target %s\n", targetpath ));
965 status = NT_STATUS_NOT_FOUND;
966 goto err_exit;
969 status = NT_STATUS_OK;
970 err_exit:
971 vfs_ChDir(conn, oldpath);
972 SMB_VFS_DISCONNECT(conn);
973 conn_free(conn);
974 TALLOC_FREE(pdp);
975 return status;
978 static int setup_ver2_dfs_referral(const char *pathname,
979 char **ppdata,
980 struct junction_map *junction,
981 bool self_referral)
983 char* pdata = *ppdata;
985 smb_ucs2_t *uni_requestedpath = NULL;
986 int uni_reqpathoffset1,uni_reqpathoffset2;
987 int uni_curroffset;
988 int requestedpathlen=0;
989 int offset;
990 int reply_size = 0;
991 int i=0;
993 DEBUG(10,("Setting up version2 referral\nRequested path:\n"));
995 requestedpathlen = rpcstr_push_talloc(talloc_tos(),
996 &uni_requestedpath, pathname);
997 if (uni_requestedpath == NULL || requestedpathlen == 0) {
998 return -1;
1001 if (DEBUGLVL(10)) {
1002 dump_data(0, (unsigned char *)uni_requestedpath,
1003 requestedpathlen);
1006 DEBUG(10,("ref count = %u\n",junction->referral_count));
1008 uni_reqpathoffset1 = REFERRAL_HEADER_SIZE +
1009 VERSION2_REFERRAL_SIZE * junction->referral_count;
1011 uni_reqpathoffset2 = uni_reqpathoffset1 + requestedpathlen;
1013 uni_curroffset = uni_reqpathoffset2 + requestedpathlen;
1015 reply_size = REFERRAL_HEADER_SIZE +
1016 VERSION2_REFERRAL_SIZE*junction->referral_count +
1017 2 * requestedpathlen;
1018 DEBUG(10,("reply_size: %u\n",reply_size));
1020 /* add up the unicode lengths of all the referral paths */
1021 for(i=0;i<junction->referral_count;i++) {
1022 DEBUG(10,("referral %u : %s\n",
1024 junction->referral_list[i].alternate_path));
1025 reply_size +=
1026 (strlen(junction->referral_list[i].alternate_path)+1)*2;
1029 DEBUG(10,("reply_size = %u\n",reply_size));
1030 /* add the unexplained 0x16 bytes */
1031 reply_size += 0x16;
1033 pdata = (char *)SMB_REALLOC(pdata,reply_size);
1034 if(pdata == NULL) {
1035 DEBUG(0,("Realloc failed!\n"));
1036 return -1;
1038 *ppdata = pdata;
1040 /* copy in the dfs requested paths.. required for offset calculations */
1041 memcpy(pdata+uni_reqpathoffset1,uni_requestedpath,requestedpathlen);
1042 memcpy(pdata+uni_reqpathoffset2,uni_requestedpath,requestedpathlen);
1044 /* create the header */
1045 SSVAL(pdata,0,requestedpathlen - 2); /* UCS2 of path consumed minus
1046 2 byte null */
1047 /* number of referral in this pkt */
1048 SSVAL(pdata,2,junction->referral_count);
1049 if(self_referral) {
1050 SIVAL(pdata,4,DFSREF_REFERRAL_SERVER | DFSREF_STORAGE_SERVER);
1051 } else {
1052 SIVAL(pdata,4,DFSREF_STORAGE_SERVER);
1055 offset = 8;
1056 /* add the referral elements */
1057 for(i=0;i<junction->referral_count;i++) {
1058 struct referral* ref = &junction->referral_list[i];
1059 int unilen;
1061 SSVAL(pdata,offset,2); /* version 2 */
1062 SSVAL(pdata,offset+2,VERSION2_REFERRAL_SIZE);
1063 if(self_referral) {
1064 SSVAL(pdata,offset+4,1);
1065 } else {
1066 SSVAL(pdata,offset+4,0);
1069 /* ref_flags :use path_consumed bytes? */
1070 SSVAL(pdata,offset+6,0);
1071 SIVAL(pdata,offset+8,ref->proximity);
1072 SIVAL(pdata,offset+12,ref->ttl);
1074 SSVAL(pdata,offset+16,uni_reqpathoffset1-offset);
1075 SSVAL(pdata,offset+18,uni_reqpathoffset2-offset);
1076 /* copy referred path into current offset */
1077 unilen = rpcstr_push(pdata+uni_curroffset,
1078 ref->alternate_path,
1079 reply_size - uni_curroffset,
1080 STR_UNICODE);
1082 SSVAL(pdata,offset+20,uni_curroffset-offset);
1084 uni_curroffset += unilen;
1085 offset += VERSION2_REFERRAL_SIZE;
1087 /* add in the unexplained 22 (0x16) bytes at the end */
1088 memset(pdata+uni_curroffset,'\0',0x16);
1089 return reply_size;
1092 static int setup_ver3_dfs_referral(const char *pathname,
1093 char **ppdata,
1094 struct junction_map *junction,
1095 bool self_referral)
1097 char *pdata = *ppdata;
1099 smb_ucs2_t *uni_reqpath = NULL;
1100 int uni_reqpathoffset1, uni_reqpathoffset2;
1101 int uni_curroffset;
1102 int reply_size = 0;
1104 int reqpathlen = 0;
1105 int offset,i=0;
1107 DEBUG(10,("setting up version3 referral\n"));
1109 reqpathlen = rpcstr_push_talloc(talloc_tos(), &uni_reqpath, pathname);
1110 if (uni_reqpath == NULL || reqpathlen == 0) {
1111 return -1;
1114 if (DEBUGLVL(10)) {
1115 dump_data(0, (unsigned char *)uni_reqpath,
1116 reqpathlen);
1119 uni_reqpathoffset1 = REFERRAL_HEADER_SIZE +
1120 VERSION3_REFERRAL_SIZE * junction->referral_count;
1121 uni_reqpathoffset2 = uni_reqpathoffset1 + reqpathlen;
1122 reply_size = uni_curroffset = uni_reqpathoffset2 + reqpathlen;
1124 for(i=0;i<junction->referral_count;i++) {
1125 DEBUG(10,("referral %u : %s\n",
1127 junction->referral_list[i].alternate_path));
1128 reply_size +=
1129 (strlen(junction->referral_list[i].alternate_path)+1)*2;
1132 pdata = (char *)SMB_REALLOC(pdata,reply_size);
1133 if(pdata == NULL) {
1134 DEBUG(0,("version3 referral setup:"
1135 "malloc failed for Realloc!\n"));
1136 return -1;
1138 *ppdata = pdata;
1140 /* create the header */
1141 SSVAL(pdata,0,reqpathlen - 2); /* UCS2 of path consumed minus
1142 2 byte null */
1143 SSVAL(pdata,2,junction->referral_count); /* number of referral */
1144 if(self_referral) {
1145 SIVAL(pdata,4,DFSREF_REFERRAL_SERVER | DFSREF_STORAGE_SERVER);
1146 } else {
1147 SIVAL(pdata,4,DFSREF_STORAGE_SERVER);
1150 /* copy in the reqpaths */
1151 memcpy(pdata+uni_reqpathoffset1,uni_reqpath,reqpathlen);
1152 memcpy(pdata+uni_reqpathoffset2,uni_reqpath,reqpathlen);
1154 offset = 8;
1155 for(i=0;i<junction->referral_count;i++) {
1156 struct referral* ref = &(junction->referral_list[i]);
1157 int unilen;
1159 SSVAL(pdata,offset,3); /* version 3 */
1160 SSVAL(pdata,offset+2,VERSION3_REFERRAL_SIZE);
1161 if(self_referral) {
1162 SSVAL(pdata,offset+4,1);
1163 } else {
1164 SSVAL(pdata,offset+4,0);
1167 /* ref_flags :use path_consumed bytes? */
1168 SSVAL(pdata,offset+6,0);
1169 SIVAL(pdata,offset+8,ref->ttl);
1171 SSVAL(pdata,offset+12,uni_reqpathoffset1-offset);
1172 SSVAL(pdata,offset+14,uni_reqpathoffset2-offset);
1173 /* copy referred path into current offset */
1174 unilen = rpcstr_push(pdata+uni_curroffset,ref->alternate_path,
1175 reply_size - uni_curroffset,
1176 STR_UNICODE | STR_TERMINATE);
1177 SSVAL(pdata,offset+16,uni_curroffset-offset);
1178 /* copy 0x10 bytes of 00's in the ServiceSite GUID */
1179 memset(pdata+offset+18,'\0',16);
1181 uni_curroffset += unilen;
1182 offset += VERSION3_REFERRAL_SIZE;
1184 return reply_size;
1187 /******************************************************************
1188 Set up the DFS referral for the dfs pathname. This call returns
1189 the amount of the path covered by this server, and where the
1190 client should be redirected to. This is the meat of the
1191 TRANS2_GET_DFS_REFERRAL call.
1192 ******************************************************************/
1194 int setup_dfs_referral(connection_struct *orig_conn,
1195 const char *dfs_path,
1196 int max_referral_level,
1197 char **ppdata, NTSTATUS *pstatus)
1199 struct junction_map *junction = NULL;
1200 int consumedcnt = 0;
1201 bool self_referral = False;
1202 int reply_size = 0;
1203 char *pathnamep = NULL;
1204 char *local_dfs_path = NULL;
1205 TALLOC_CTX *ctx;
1207 if (!(ctx=talloc_init("setup_dfs_referral"))) {
1208 *pstatus = NT_STATUS_NO_MEMORY;
1209 return -1;
1212 /* get the junction entry */
1213 if (!dfs_path) {
1214 talloc_destroy(ctx);
1215 *pstatus = NT_STATUS_NOT_FOUND;
1216 return -1;
1220 * Trim pathname sent by client so it begins with only one backslash.
1221 * Two backslashes confuse some dfs clients
1224 local_dfs_path = talloc_strdup(ctx,dfs_path);
1225 if (!local_dfs_path) {
1226 *pstatus = NT_STATUS_NO_MEMORY;
1227 talloc_destroy(ctx);
1228 return -1;
1230 pathnamep = local_dfs_path;
1231 while (IS_DIRECTORY_SEP(pathnamep[0]) &&
1232 IS_DIRECTORY_SEP(pathnamep[1])) {
1233 pathnamep++;
1236 junction = TALLOC_ZERO_P(ctx, struct junction_map);
1237 if (!junction) {
1238 *pstatus = NT_STATUS_NO_MEMORY;
1239 talloc_destroy(ctx);
1240 return -1;
1243 /* The following call can change cwd. */
1244 *pstatus = get_referred_path(ctx, pathnamep, junction,
1245 &consumedcnt, &self_referral);
1246 if (!NT_STATUS_IS_OK(*pstatus)) {
1247 vfs_ChDir(orig_conn,orig_conn->connectpath);
1248 talloc_destroy(ctx);
1249 return -1;
1251 vfs_ChDir(orig_conn,orig_conn->connectpath);
1253 if (!self_referral) {
1254 pathnamep[consumedcnt] = '\0';
1256 if( DEBUGLVL( 3 ) ) {
1257 int i=0;
1258 dbgtext("setup_dfs_referral: Path %s to "
1259 "alternate path(s):",
1260 pathnamep);
1261 for(i=0;i<junction->referral_count;i++)
1262 dbgtext(" %s",
1263 junction->referral_list[i].alternate_path);
1264 dbgtext(".\n");
1268 /* create the referral depeding on version */
1269 DEBUG(10,("max_referral_level :%d\n",max_referral_level));
1271 if (max_referral_level < 2) {
1272 max_referral_level = 2;
1274 if (max_referral_level > 3) {
1275 max_referral_level = 3;
1278 switch(max_referral_level) {
1279 case 2:
1280 reply_size = setup_ver2_dfs_referral(pathnamep,
1281 ppdata, junction,
1282 self_referral);
1283 break;
1284 case 3:
1285 reply_size = setup_ver3_dfs_referral(pathnamep, ppdata,
1286 junction, self_referral);
1287 break;
1288 default:
1289 DEBUG(0,("setup_dfs_referral: Invalid dfs referral "
1290 "version: %d\n",
1291 max_referral_level));
1292 talloc_destroy(ctx);
1293 *pstatus = NT_STATUS_INVALID_LEVEL;
1294 return -1;
1297 if (DEBUGLVL(10)) {
1298 DEBUGADD(0,("DFS Referral pdata:\n"));
1299 dump_data(0,(uint8 *)*ppdata,reply_size);
1302 talloc_destroy(ctx);
1303 *pstatus = NT_STATUS_OK;
1304 return reply_size;
1307 /**********************************************************************
1308 The following functions are called by the NETDFS RPC pipe functions
1309 **********************************************************************/
1311 /*********************************************************************
1312 Creates a junction structure from a DFS pathname
1313 **********************************************************************/
1315 bool create_junction(TALLOC_CTX *ctx,
1316 const char *dfs_path,
1317 struct junction_map *jucn)
1319 int snum;
1320 bool dummy;
1321 struct dfs_path *pdp = TALLOC_P(ctx,struct dfs_path);
1322 NTSTATUS status;
1324 if (!pdp) {
1325 return False;
1327 status = parse_dfs_path(NULL, dfs_path, False, pdp, &dummy);
1328 if (!NT_STATUS_IS_OK(status)) {
1329 return False;
1332 /* check if path is dfs : validate first token */
1333 if (!is_myname_or_ipaddr(pdp->hostname)) {
1334 DEBUG(4,("create_junction: Invalid hostname %s "
1335 "in dfs path %s\n",
1336 pdp->hostname, dfs_path));
1337 TALLOC_FREE(pdp);
1338 return False;
1341 /* Check for a non-DFS share */
1342 snum = lp_servicenumber(pdp->servicename);
1344 if(snum < 0 || !lp_msdfs_root(snum)) {
1345 DEBUG(4,("create_junction: %s is not an msdfs root.\n",
1346 pdp->servicename));
1347 TALLOC_FREE(pdp);
1348 return False;
1351 jucn->service_name = talloc_strdup(ctx, pdp->servicename);
1352 jucn->volume_name = talloc_strdup(ctx, pdp->reqpath);
1353 jucn->comment = talloc_strdup(ctx, lp_comment(snum));
1355 TALLOC_FREE(pdp);
1356 if (!jucn->service_name || !jucn->volume_name || ! jucn->comment) {
1357 return False;
1359 return True;
1362 /**********************************************************************
1363 Forms a valid Unix pathname from the junction
1364 **********************************************************************/
1366 static bool junction_to_local_path(const struct junction_map *jucn,
1367 char **pp_path_out,
1368 connection_struct **conn_out,
1369 char **oldpath)
1371 int snum;
1372 NTSTATUS status;
1374 snum = lp_servicenumber(jucn->service_name);
1375 if(snum < 0) {
1376 return False;
1378 status = create_conn_struct(talloc_tos(), conn_out, snum,
1379 lp_pathname(snum), NULL, oldpath);
1380 if (!NT_STATUS_IS_OK(status)) {
1381 return False;
1384 *pp_path_out = talloc_asprintf(*conn_out,
1385 "%s/%s",
1386 lp_pathname(snum),
1387 jucn->volume_name);
1388 if (!*pp_path_out) {
1389 vfs_ChDir(*conn_out, *oldpath);
1390 SMB_VFS_DISCONNECT(*conn_out);
1391 conn_free(*conn_out);
1392 return False;
1394 return True;
1397 bool create_msdfs_link(const struct junction_map *jucn)
1399 char *path = NULL;
1400 char *cwd;
1401 char *msdfs_link = NULL;
1402 connection_struct *conn;
1403 int i=0;
1404 bool insert_comma = False;
1405 bool ret = False;
1407 if(!junction_to_local_path(jucn, &path, &conn, &cwd)) {
1408 return False;
1411 /* Form the msdfs_link contents */
1412 msdfs_link = talloc_strdup(conn, "msdfs:");
1413 if (!msdfs_link) {
1414 goto out;
1416 for(i=0; i<jucn->referral_count; i++) {
1417 char *refpath = jucn->referral_list[i].alternate_path;
1419 /* Alternate paths always use Windows separators. */
1420 trim_char(refpath, '\\', '\\');
1421 if(*refpath == '\0') {
1422 if (i == 0) {
1423 insert_comma = False;
1425 continue;
1427 if (i > 0 && insert_comma) {
1428 msdfs_link = talloc_asprintf_append_buffer(msdfs_link,
1429 ",%s",
1430 refpath);
1431 } else {
1432 msdfs_link = talloc_asprintf_append_buffer(msdfs_link,
1433 "%s",
1434 refpath);
1437 if (!msdfs_link) {
1438 goto out;
1440 if (!insert_comma) {
1441 insert_comma = True;
1445 DEBUG(5,("create_msdfs_link: Creating new msdfs link: %s -> %s\n",
1446 path, msdfs_link));
1448 if(SMB_VFS_SYMLINK(conn, msdfs_link, path) < 0) {
1449 if (errno == EEXIST) {
1450 struct smb_filename *smb_fname = NULL;
1451 NTSTATUS status;
1453 status = create_synthetic_smb_fname(talloc_tos(), path,
1454 NULL, NULL,
1455 &smb_fname);
1456 if (!NT_STATUS_IS_OK(status)) {
1457 errno = map_errno_from_nt_status(status);
1458 goto out;
1461 if(SMB_VFS_UNLINK(conn, smb_fname)!=0) {
1462 TALLOC_FREE(smb_fname);
1463 goto out;
1465 TALLOC_FREE(smb_fname);
1467 if (SMB_VFS_SYMLINK(conn, msdfs_link, path) < 0) {
1468 DEBUG(1,("create_msdfs_link: symlink failed "
1469 "%s -> %s\nError: %s\n",
1470 path, msdfs_link, strerror(errno)));
1471 goto out;
1475 ret = True;
1477 out:
1478 vfs_ChDir(conn, cwd);
1479 SMB_VFS_DISCONNECT(conn);
1480 conn_free(conn);
1481 return ret;
1484 bool remove_msdfs_link(const struct junction_map *jucn)
1486 char *path = NULL;
1487 char *cwd;
1488 connection_struct *conn;
1489 bool ret = False;
1490 struct smb_filename *smb_fname = NULL;
1491 NTSTATUS status;
1493 if (!junction_to_local_path(jucn, &path, &conn, &cwd)) {
1494 return false;
1497 status = create_synthetic_smb_fname(talloc_tos(), path,
1498 NULL, NULL,
1499 &smb_fname);
1500 if (!NT_STATUS_IS_OK(status)) {
1501 errno = map_errno_from_nt_status(status);
1502 return false;
1505 if( SMB_VFS_UNLINK(conn, smb_fname) == 0 ) {
1506 ret = True;
1509 TALLOC_FREE(smb_fname);
1510 vfs_ChDir(conn, cwd);
1511 SMB_VFS_DISCONNECT(conn);
1512 conn_free(conn);
1513 return ret;
1516 /*********************************************************************
1517 Return the number of DFS links at the root of this share.
1518 *********************************************************************/
1520 static int count_dfs_links(TALLOC_CTX *ctx, int snum)
1522 size_t cnt = 0;
1523 SMB_STRUCT_DIR *dirp = NULL;
1524 const char *dname = NULL;
1525 char *talloced = NULL;
1526 const char *connect_path = lp_pathname(snum);
1527 const char *msdfs_proxy = lp_msdfs_proxy(snum);
1528 connection_struct *conn;
1529 NTSTATUS status;
1530 char *cwd;
1532 if(*connect_path == '\0') {
1533 return 0;
1537 * Fake up a connection struct for the VFS layer.
1540 status = create_conn_struct(talloc_tos(), &conn, snum, connect_path,
1541 NULL, &cwd);
1542 if (!NT_STATUS_IS_OK(status)) {
1543 DEBUG(3, ("create_conn_struct failed: %s\n",
1544 nt_errstr(status)));
1545 return 0;
1548 /* Count a link for the msdfs root - convention */
1549 cnt = 1;
1551 /* No more links if this is an msdfs proxy. */
1552 if (*msdfs_proxy != '\0') {
1553 goto out;
1556 /* Now enumerate all dfs links */
1557 dirp = SMB_VFS_OPENDIR(conn, ".", NULL, 0);
1558 if(!dirp) {
1559 goto out;
1562 while ((dname = vfs_readdirname(conn, dirp, NULL, &talloced))
1563 != NULL) {
1564 if (is_msdfs_link(conn,
1565 dname,
1566 NULL)) {
1567 cnt++;
1569 TALLOC_FREE(talloced);
1572 SMB_VFS_CLOSEDIR(conn,dirp);
1574 out:
1575 vfs_ChDir(conn, cwd);
1576 SMB_VFS_DISCONNECT(conn);
1577 conn_free(conn);
1578 return cnt;
1581 /*********************************************************************
1582 *********************************************************************/
1584 static int form_junctions(TALLOC_CTX *ctx,
1585 int snum,
1586 struct junction_map *jucn,
1587 size_t jn_remain)
1589 size_t cnt = 0;
1590 SMB_STRUCT_DIR *dirp = NULL;
1591 const char *dname = NULL;
1592 char *talloced = NULL;
1593 const char *connect_path = lp_pathname(snum);
1594 char *service_name = lp_servicename(snum);
1595 const char *msdfs_proxy = lp_msdfs_proxy(snum);
1596 connection_struct *conn;
1597 struct referral *ref = NULL;
1598 char *cwd;
1599 NTSTATUS status;
1601 if (jn_remain == 0) {
1602 return 0;
1605 if(*connect_path == '\0') {
1606 return 0;
1610 * Fake up a connection struct for the VFS layer.
1613 status = create_conn_struct(ctx, &conn, snum, connect_path, NULL,
1614 &cwd);
1615 if (!NT_STATUS_IS_OK(status)) {
1616 DEBUG(3, ("create_conn_struct failed: %s\n",
1617 nt_errstr(status)));
1618 return 0;
1621 /* form a junction for the msdfs root - convention
1622 DO NOT REMOVE THIS: NT clients will not work with us
1623 if this is not present
1625 jucn[cnt].service_name = talloc_strdup(ctx,service_name);
1626 jucn[cnt].volume_name = talloc_strdup(ctx, "");
1627 if (!jucn[cnt].service_name || !jucn[cnt].volume_name) {
1628 goto out;
1630 jucn[cnt].comment = "";
1631 jucn[cnt].referral_count = 1;
1633 ref = jucn[cnt].referral_list = TALLOC_ZERO_P(ctx, struct referral);
1634 if (jucn[cnt].referral_list == NULL) {
1635 goto out;
1638 ref->proximity = 0;
1639 ref->ttl = REFERRAL_TTL;
1640 if (*msdfs_proxy != '\0') {
1641 ref->alternate_path = talloc_strdup(ctx,
1642 msdfs_proxy);
1643 } else {
1644 ref->alternate_path = talloc_asprintf(ctx,
1645 "\\\\%s\\%s",
1646 get_local_machine_name(),
1647 service_name);
1650 if (!ref->alternate_path) {
1651 goto out;
1653 cnt++;
1655 /* Don't enumerate if we're an msdfs proxy. */
1656 if (*msdfs_proxy != '\0') {
1657 goto out;
1660 /* Now enumerate all dfs links */
1661 dirp = SMB_VFS_OPENDIR(conn, ".", NULL, 0);
1662 if(!dirp) {
1663 goto out;
1666 while ((dname = vfs_readdirname(conn, dirp, NULL, &talloced))
1667 != NULL) {
1668 char *link_target = NULL;
1669 if (cnt >= jn_remain) {
1670 DEBUG(2, ("form_junctions: ran out of MSDFS "
1671 "junction slots"));
1672 TALLOC_FREE(talloced);
1673 goto out;
1675 if (is_msdfs_link_internal(ctx,
1676 conn,
1677 dname, &link_target,
1678 NULL)) {
1679 if (parse_msdfs_symlink(ctx,
1680 link_target,
1681 &jucn[cnt].referral_list,
1682 &jucn[cnt].referral_count)) {
1684 jucn[cnt].service_name = talloc_strdup(ctx,
1685 service_name);
1686 jucn[cnt].volume_name = talloc_strdup(ctx,
1687 dname);
1688 if (!jucn[cnt].service_name ||
1689 !jucn[cnt].volume_name) {
1690 TALLOC_FREE(talloced);
1691 goto out;
1693 jucn[cnt].comment = "";
1694 cnt++;
1696 TALLOC_FREE(link_target);
1698 TALLOC_FREE(talloced);
1701 out:
1703 if (dirp) {
1704 SMB_VFS_CLOSEDIR(conn,dirp);
1707 vfs_ChDir(conn, cwd);
1708 conn_free(conn);
1709 return cnt;
1712 struct junction_map *enum_msdfs_links(TALLOC_CTX *ctx, size_t *p_num_jn)
1714 struct junction_map *jn = NULL;
1715 int i=0;
1716 size_t jn_count = 0;
1717 int sharecount = 0;
1719 *p_num_jn = 0;
1720 if(!lp_host_msdfs()) {
1721 return NULL;
1724 /* Ensure all the usershares are loaded. */
1725 become_root();
1726 load_registry_shares();
1727 sharecount = load_usershare_shares();
1728 unbecome_root();
1730 for(i=0;i < sharecount;i++) {
1731 if(lp_msdfs_root(i)) {
1732 jn_count += count_dfs_links(ctx, i);
1735 if (jn_count == 0) {
1736 return NULL;
1738 jn = TALLOC_ARRAY(ctx, struct junction_map, jn_count);
1739 if (!jn) {
1740 return NULL;
1742 for(i=0; i < sharecount; i++) {
1743 if (*p_num_jn >= jn_count) {
1744 break;
1746 if(lp_msdfs_root(i)) {
1747 *p_num_jn += form_junctions(ctx, i,
1748 &jn[*p_num_jn],
1749 jn_count - *p_num_jn);
1752 return jn;
1755 /******************************************************************************
1756 Core function to resolve a dfs pathname possibly containing a wildcard. If
1757 ppath_contains_wcard != NULL, it will be set to true if a wildcard is
1758 detected during dfs resolution.
1759 ******************************************************************************/
1761 NTSTATUS resolve_dfspath_wcard(TALLOC_CTX *ctx,
1762 connection_struct *conn,
1763 bool dfs_pathnames,
1764 const char *name_in,
1765 bool allow_wcards,
1766 char **pp_name_out,
1767 bool *ppath_contains_wcard)
1769 bool path_contains_wcard;
1770 NTSTATUS status = NT_STATUS_OK;
1772 if (dfs_pathnames) {
1773 status = dfs_redirect(ctx,
1774 conn,
1775 name_in,
1776 allow_wcards,
1777 pp_name_out,
1778 &path_contains_wcard);
1780 if (NT_STATUS_IS_OK(status) && ppath_contains_wcard != NULL) {
1781 *ppath_contains_wcard = path_contains_wcard;
1783 } else {
1785 * Cheat and just return a copy of the in ptr.
1786 * Once srvstr_get_path() uses talloc it'll
1787 * be a talloced ptr anyway.
1789 *pp_name_out = CONST_DISCARD(char *,name_in);
1791 return status;