s3: Remove the smbd_server_conn ref from setup_dfs_referral
[Samba/gbeck.git] / source3 / smbd / msdfs.c
blob66258a22fd9e43e8f84e41bcfdc64f3a978b93c9
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"
30 #include "lib/param/loadparm.h"
31 #include "libcli/security/security.h"
33 /**********************************************************************
34 Parse a DFS pathname of the form \hostname\service\reqpath
35 into the dfs_path structure.
36 If POSIX pathnames is true, the pathname may also be of the
37 form /hostname/service/reqpath.
38 We cope with either here.
40 Unfortunately, due to broken clients who might set the
41 SVAL(inbuf,smb_flg2) & FLAGS2_DFS_PATHNAMES bit and then
42 send a local path, we have to cope with that too....
44 If conn != NULL then ensure the provided service is
45 the one pointed to by the connection.
47 This version does everything using pointers within one copy of the
48 pathname string, talloced on the struct dfs_path pointer (which
49 must be talloced). This may be too clever to live....
50 JRA.
51 **********************************************************************/
53 static NTSTATUS parse_dfs_path(connection_struct *conn,
54 const char *pathname,
55 bool allow_wcards,
56 bool allow_broken_path,
57 struct dfs_path *pdp, /* MUST BE TALLOCED */
58 bool *ppath_contains_wcard)
60 char *pathname_local;
61 char *p,*temp;
62 char *servicename;
63 char *eos_ptr;
64 NTSTATUS status = NT_STATUS_OK;
65 char sepchar;
67 ZERO_STRUCTP(pdp);
70 * This is the only talloc we should need to do
71 * on the struct dfs_path. All the pointers inside
72 * it should point to offsets within this string.
75 pathname_local = talloc_strdup(pdp, pathname);
76 if (!pathname_local) {
77 return NT_STATUS_NO_MEMORY;
79 /* Get a pointer to the terminating '\0' */
80 eos_ptr = &pathname_local[strlen(pathname_local)];
81 p = temp = pathname_local;
83 pdp->posix_path = (lp_posix_pathnames() && *pathname == '/');
85 sepchar = pdp->posix_path ? '/' : '\\';
87 if (allow_broken_path && (*pathname != sepchar)) {
88 DEBUG(10,("parse_dfs_path: path %s doesn't start with %c\n",
89 pathname, sepchar ));
91 * Possibly client sent a local path by mistake.
92 * Try and convert to a local path.
95 pdp->hostname = eos_ptr; /* "" */
96 pdp->servicename = eos_ptr; /* "" */
98 /* We've got no info about separators. */
99 pdp->posix_path = lp_posix_pathnames();
100 p = temp;
101 DEBUG(10,("parse_dfs_path: trying to convert %s to a "
102 "local path\n",
103 temp));
104 goto local_path;
108 * Safe to use on talloc'ed string as it only shrinks.
109 * It also doesn't affect the eos_ptr.
111 trim_char(temp,sepchar,sepchar);
113 DEBUG(10,("parse_dfs_path: temp = |%s| after trimming %c's\n",
114 temp, sepchar));
116 /* Now tokenize. */
117 /* Parse out hostname. */
118 p = strchr_m(temp,sepchar);
119 if(p == NULL) {
120 DEBUG(10,("parse_dfs_path: can't parse hostname from path %s\n",
121 temp));
123 * Possibly client sent a local path by mistake.
124 * Try and convert to a local path.
127 pdp->hostname = eos_ptr; /* "" */
128 pdp->servicename = eos_ptr; /* "" */
130 p = temp;
131 DEBUG(10,("parse_dfs_path: trying to convert %s "
132 "to a local path\n",
133 temp));
134 goto local_path;
136 *p = '\0';
137 pdp->hostname = temp;
139 DEBUG(10,("parse_dfs_path: hostname: %s\n",pdp->hostname));
141 /* Parse out servicename. */
142 servicename = p+1;
143 p = strchr_m(servicename,sepchar);
144 if (p) {
145 *p = '\0';
148 /* Is this really our servicename ? */
149 if (conn && !( strequal(servicename, lp_servicename(SNUM(conn)))
150 || (strequal(servicename, HOMES_NAME)
151 && strequal(lp_servicename(SNUM(conn)),
152 get_current_username()) )) ) {
153 DEBUG(10,("parse_dfs_path: %s is not our servicename\n",
154 servicename));
157 * Possibly client sent a local path by mistake.
158 * Try and convert to a local path.
161 pdp->hostname = eos_ptr; /* "" */
162 pdp->servicename = eos_ptr; /* "" */
164 /* Repair the path - replace the sepchar's
165 we nulled out */
166 servicename--;
167 *servicename = sepchar;
168 if (p) {
169 *p = sepchar;
172 p = temp;
173 DEBUG(10,("parse_dfs_path: trying to convert %s "
174 "to a local path\n",
175 temp));
176 goto local_path;
179 pdp->servicename = servicename;
181 DEBUG(10,("parse_dfs_path: servicename: %s\n",pdp->servicename));
183 if(p == NULL) {
184 /* Client sent self referral \server\share. */
185 pdp->reqpath = eos_ptr; /* "" */
186 return NT_STATUS_OK;
189 p++;
191 local_path:
193 *ppath_contains_wcard = False;
195 pdp->reqpath = p;
197 /* Rest is reqpath. */
198 if (pdp->posix_path) {
199 status = check_path_syntax_posix(pdp->reqpath);
200 } else {
201 if (allow_wcards) {
202 status = check_path_syntax_wcard(pdp->reqpath,
203 ppath_contains_wcard);
204 } else {
205 status = check_path_syntax(pdp->reqpath);
209 if (!NT_STATUS_IS_OK(status)) {
210 DEBUG(10,("parse_dfs_path: '%s' failed with %s\n",
211 p, nt_errstr(status) ));
212 return status;
215 DEBUG(10,("parse_dfs_path: rest of the path: %s\n",pdp->reqpath));
216 return NT_STATUS_OK;
219 /********************************************************
220 Fake up a connection struct for the VFS layer.
221 Note: this performs a vfs connect and CHANGES CWD !!!! JRA.
222 *********************************************************/
224 NTSTATUS create_conn_struct(TALLOC_CTX *ctx,
225 struct smbd_server_connection *sconn,
226 connection_struct **pconn,
227 int snum,
228 const char *path,
229 const struct auth_session_info *session_info,
230 char **poldcwd)
232 connection_struct *conn;
233 char *connpath;
234 char *oldcwd;
235 const char *vfs_user;
237 conn = talloc_zero(ctx, connection_struct);
238 if (conn == NULL) {
239 return NT_STATUS_NO_MEMORY;
242 connpath = talloc_strdup(conn, path);
243 if (!connpath) {
244 TALLOC_FREE(conn);
245 return NT_STATUS_NO_MEMORY;
247 connpath = talloc_string_sub(conn,
248 connpath,
249 "%S",
250 lp_servicename(snum));
251 if (!connpath) {
252 TALLOC_FREE(conn);
253 return NT_STATUS_NO_MEMORY;
256 /* needed for smbd_vfs_init() */
258 if (!(conn->params = talloc_zero(conn, struct share_params))) {
259 DEBUG(0, ("TALLOC failed\n"));
260 TALLOC_FREE(conn);
261 return NT_STATUS_NO_MEMORY;
264 conn->params->service = snum;
266 conn->sconn = sconn;
267 conn->sconn->num_tcons_open++;
269 if (session_info != NULL) {
270 conn->session_info = copy_session_info(conn, session_info);
271 if (conn->session_info == NULL) {
272 DEBUG(0, ("copy_serverinfo failed\n"));
273 TALLOC_FREE(conn);
274 return NT_STATUS_NO_MEMORY;
276 vfs_user = conn->session_info->unix_info->unix_name;
277 } else {
278 /* use current authenticated user in absence of session_info */
279 vfs_user = get_current_username();
282 set_conn_connectpath(conn, connpath);
285 * New code to check if there's a share security descripter
286 * added from NT server manager. This is done after the
287 * smb.conf checks are done as we need a uid and token. JRA.
290 if (conn->session_info) {
291 share_access_check(conn->session_info->security_token,
292 lp_servicename(snum), MAXIMUM_ALLOWED_ACCESS,
293 &conn->share_access);
295 if ((conn->share_access & FILE_WRITE_DATA) == 0) {
296 if ((conn->share_access & FILE_READ_DATA) == 0) {
297 /* No access, read or write. */
298 DEBUG(0,("create_conn_struct: connection to %s "
299 "denied due to security "
300 "descriptor.\n",
301 lp_servicename(snum)));
302 conn_free(conn);
303 return NT_STATUS_ACCESS_DENIED;
304 } else {
305 conn->read_only = true;
308 } else {
309 conn->share_access = 0;
310 conn->read_only = true;
313 if (!smbd_vfs_init(conn)) {
314 NTSTATUS status = map_nt_error_from_unix(errno);
315 DEBUG(0,("create_conn_struct: smbd_vfs_init failed.\n"));
316 conn_free(conn);
317 return status;
320 /* this must be the first filesystem operation that we do */
321 if (SMB_VFS_CONNECT(conn, lp_servicename(snum), vfs_user) < 0) {
322 DEBUG(0,("VFS connect failed!\n"));
323 conn_free(conn);
324 return NT_STATUS_UNSUCCESSFUL;
327 conn->fs_capabilities = SMB_VFS_FS_CAPABILITIES(conn, &conn->ts_res);
330 * Windows seems to insist on doing trans2getdfsreferral() calls on
331 * the IPC$ share as the anonymous user. If we try to chdir as that
332 * user we will fail.... WTF ? JRA.
335 oldcwd = vfs_GetWd(ctx, conn);
336 if (oldcwd == NULL) {
337 NTSTATUS status = map_nt_error_from_unix(errno);
338 DEBUG(3, ("vfs_GetWd failed: %s\n", strerror(errno)));
339 conn_free(conn);
340 return status;
343 if (vfs_ChDir(conn,conn->connectpath) != 0) {
344 NTSTATUS status = map_nt_error_from_unix(errno);
345 DEBUG(3,("create_conn_struct: Can't ChDir to new conn path %s. "
346 "Error was %s\n",
347 conn->connectpath, strerror(errno) ));
348 conn_free(conn);
349 return status;
352 *pconn = conn;
353 *poldcwd = oldcwd;
355 return NT_STATUS_OK;
358 /**********************************************************************
359 Parse the contents of a symlink to verify if it is an msdfs referral
360 A valid referral is of the form:
362 msdfs:server1\share1,server2\share2
363 msdfs:server1\share1\pathname,server2\share2\pathname
364 msdfs:server1/share1,server2/share2
365 msdfs:server1/share1/pathname,server2/share2/pathname.
367 Note that the alternate paths returned here must be of the canonicalized
368 form:
370 \server\share or
371 \server\share\path\to\file,
373 even in posix path mode. This is because we have no knowledge if the
374 server we're referring to understands posix paths.
375 **********************************************************************/
377 static bool parse_msdfs_symlink(TALLOC_CTX *ctx,
378 const char *target,
379 struct referral **preflist,
380 int *refcount)
382 char *temp = NULL;
383 char *prot;
384 char **alt_path = NULL;
385 int count = 0, i;
386 struct referral *reflist;
387 char *saveptr;
389 temp = talloc_strdup(ctx, target);
390 if (!temp) {
391 return False;
393 prot = strtok_r(temp, ":", &saveptr);
394 if (!prot) {
395 DEBUG(0,("parse_msdfs_symlink: invalid path !\n"));
396 return False;
399 alt_path = talloc_array(ctx, char *, MAX_REFERRAL_COUNT);
400 if (!alt_path) {
401 return False;
404 /* parse out the alternate paths */
405 while((count<MAX_REFERRAL_COUNT) &&
406 ((alt_path[count] = strtok_r(NULL, ",", &saveptr)) != NULL)) {
407 count++;
410 DEBUG(10,("parse_msdfs_symlink: count=%d\n", count));
412 if (count) {
413 reflist = *preflist = talloc_zero_array(ctx,
414 struct referral, count);
415 if(reflist == NULL) {
416 TALLOC_FREE(alt_path);
417 return False;
419 } else {
420 reflist = *preflist = NULL;
423 for(i=0;i<count;i++) {
424 char *p;
426 /* Canonicalize link target.
427 * Replace all /'s in the path by a \ */
428 string_replace(alt_path[i], '/', '\\');
430 /* Remove leading '\\'s */
431 p = alt_path[i];
432 while (*p && (*p == '\\')) {
433 p++;
436 reflist[i].alternate_path = talloc_asprintf(ctx,
437 "\\%s",
439 if (!reflist[i].alternate_path) {
440 return False;
443 reflist[i].proximity = 0;
444 reflist[i].ttl = REFERRAL_TTL;
445 DEBUG(10, ("parse_msdfs_symlink: Created alt path: %s\n",
446 reflist[i].alternate_path));
449 *refcount = count;
451 TALLOC_FREE(alt_path);
452 return True;
455 /**********************************************************************
456 Returns true if the unix path is a valid msdfs symlink and also
457 returns the target string from inside the link.
458 **********************************************************************/
460 static bool is_msdfs_link_internal(TALLOC_CTX *ctx,
461 connection_struct *conn,
462 const char *path,
463 char **pp_link_target,
464 SMB_STRUCT_STAT *sbufp)
466 int referral_len = 0;
467 #if defined(HAVE_BROKEN_READLINK)
468 char link_target_buf[PATH_MAX];
469 #else
470 char link_target_buf[7];
471 #endif
472 size_t bufsize = 0;
473 char *link_target = NULL;
474 struct smb_filename smb_fname;
476 if (pp_link_target) {
477 bufsize = 1024;
478 link_target = talloc_array(ctx, char, bufsize);
479 if (!link_target) {
480 return False;
482 *pp_link_target = link_target;
483 } else {
484 bufsize = sizeof(link_target_buf);
485 link_target = link_target_buf;
488 ZERO_STRUCT(smb_fname);
489 smb_fname.base_name = discard_const_p(char, path);
491 if (SMB_VFS_LSTAT(conn, &smb_fname) != 0) {
492 DEBUG(5,("is_msdfs_link_read_target: %s does not exist.\n",
493 path));
494 goto err;
496 if (!S_ISLNK(smb_fname.st.st_ex_mode)) {
497 DEBUG(5,("is_msdfs_link_read_target: %s is not a link.\n",
498 path));
499 goto err;
501 if (sbufp != NULL) {
502 *sbufp = smb_fname.st;
505 referral_len = SMB_VFS_READLINK(conn, path, link_target, bufsize - 1);
506 if (referral_len == -1) {
507 DEBUG(0,("is_msdfs_link_read_target: Error reading "
508 "msdfs link %s: %s\n",
509 path, strerror(errno)));
510 goto err;
512 link_target[referral_len] = '\0';
514 DEBUG(5,("is_msdfs_link_internal: %s -> %s\n",path,
515 link_target));
517 if (!strnequal(link_target, "msdfs:", 6)) {
518 goto err;
520 return True;
522 err:
524 if (link_target != link_target_buf) {
525 TALLOC_FREE(link_target);
527 return False;
530 /**********************************************************************
531 Returns true if the unix path is a valid msdfs symlink.
532 **********************************************************************/
534 bool is_msdfs_link(connection_struct *conn,
535 const char *path,
536 SMB_STRUCT_STAT *sbufp)
538 return is_msdfs_link_internal(talloc_tos(),
539 conn,
540 path,
541 NULL,
542 sbufp);
545 /*****************************************************************
546 Used by other functions to decide if a dfs path is remote,
547 and to get the list of referred locations for that remote path.
549 search_flag: For findfirsts, dfs links themselves are not
550 redirected, but paths beyond the links are. For normal smb calls,
551 even dfs links need to be redirected.
553 consumedcntp: how much of the dfs path is being redirected. the client
554 should try the remaining path on the redirected server.
556 If this returns NT_STATUS_PATH_NOT_COVERED the contents of the msdfs
557 link redirect are in targetpath.
558 *****************************************************************/
560 static NTSTATUS dfs_path_lookup(TALLOC_CTX *ctx,
561 connection_struct *conn,
562 const char *dfspath, /* Incoming complete dfs path */
563 const struct dfs_path *pdp, /* Parsed out
564 server+share+extrapath. */
565 bool search_flag, /* Called from a findfirst ? */
566 int *consumedcntp,
567 char **pp_targetpath)
569 char *p = NULL;
570 char *q = NULL;
571 NTSTATUS status;
572 struct smb_filename *smb_fname = NULL;
573 char *canon_dfspath = NULL; /* Canonicalized dfs path. (only '/'
574 components). */
576 DEBUG(10,("dfs_path_lookup: Conn path = %s reqpath = %s\n",
577 conn->connectpath, pdp->reqpath));
580 * Note the unix path conversion here we're doing we can
581 * throw away. We're looking for a symlink for a dfs
582 * resolution, if we don't find it we'll do another
583 * unix_convert later in the codepath.
584 * If we needed to remember what we'd resolved in
585 * dp->reqpath (as the original code did) we'd
586 * copy (localhost, dp->reqpath) on any code
587 * path below that returns True - but I don't
588 * think this is needed. JRA.
591 status = unix_convert(ctx, conn, pdp->reqpath, &smb_fname,
592 search_flag ? UCF_ALWAYS_ALLOW_WCARD_LCOMP : 0);
594 if (!NT_STATUS_IS_OK(status)) {
595 if (!NT_STATUS_EQUAL(status,
596 NT_STATUS_OBJECT_PATH_NOT_FOUND)) {
597 return status;
600 /* Create an smb_fname to use below. */
601 status = create_synthetic_smb_fname(ctx, pdp->reqpath, NULL,
602 NULL, &smb_fname);
603 if (!NT_STATUS_IS_OK(status)) {
604 return status;
608 /* Optimization - check if we can redirect the whole path. */
610 if (is_msdfs_link_internal(ctx, conn, smb_fname->base_name,
611 pp_targetpath, NULL)) {
612 if (search_flag) {
613 DEBUG(6,("dfs_path_lookup (FindFirst) No redirection "
614 "for dfs link %s.\n", dfspath));
615 status = NT_STATUS_OK;
616 goto out;
619 DEBUG(6,("dfs_path_lookup: %s resolves to a "
620 "valid dfs link %s.\n", dfspath,
621 pp_targetpath ? *pp_targetpath : ""));
623 if (consumedcntp) {
624 *consumedcntp = strlen(dfspath);
626 status = NT_STATUS_PATH_NOT_COVERED;
627 goto out;
630 /* Prepare to test only for '/' components in the given path,
631 * so if a Windows path replace all '\\' characters with '/'.
632 * For a POSIX DFS path we know all separators are already '/'. */
634 canon_dfspath = talloc_strdup(ctx, dfspath);
635 if (!canon_dfspath) {
636 status = NT_STATUS_NO_MEMORY;
637 goto out;
639 if (!pdp->posix_path) {
640 string_replace(canon_dfspath, '\\', '/');
644 * localpath comes out of unix_convert, so it has
645 * no trailing backslash. Make sure that canon_dfspath hasn't either.
646 * Fix for bug #4860 from Jan Martin <Jan.Martin@rwedea.com>.
649 trim_char(canon_dfspath,0,'/');
652 * Redirect if any component in the path is a link.
653 * We do this by walking backwards through the
654 * local path, chopping off the last component
655 * in both the local path and the canonicalized
656 * DFS path. If we hit a DFS link then we're done.
659 p = strrchr_m(smb_fname->base_name, '/');
660 if (consumedcntp) {
661 q = strrchr_m(canon_dfspath, '/');
664 while (p) {
665 *p = '\0';
666 if (q) {
667 *q = '\0';
670 if (is_msdfs_link_internal(ctx, conn,
671 smb_fname->base_name, pp_targetpath,
672 NULL)) {
673 DEBUG(4, ("dfs_path_lookup: Redirecting %s because "
674 "parent %s is dfs link\n", dfspath,
675 smb_fname_str_dbg(smb_fname)));
677 if (consumedcntp) {
678 *consumedcntp = strlen(canon_dfspath);
679 DEBUG(10, ("dfs_path_lookup: Path consumed: %s "
680 "(%d)\n",
681 canon_dfspath,
682 *consumedcntp));
685 status = NT_STATUS_PATH_NOT_COVERED;
686 goto out;
689 /* Step back on the filesystem. */
690 p = strrchr_m(smb_fname->base_name, '/');
692 if (consumedcntp) {
693 /* And in the canonicalized dfs path. */
694 q = strrchr_m(canon_dfspath, '/');
698 status = NT_STATUS_OK;
699 out:
700 TALLOC_FREE(smb_fname);
701 return status;
704 /*****************************************************************
705 Decides if a dfs pathname should be redirected or not.
706 If not, the pathname is converted to a tcon-relative local unix path
708 search_wcard_flag: this flag performs 2 functions both related
709 to searches. See resolve_dfs_path() and parse_dfs_path_XX()
710 for details.
712 This function can return NT_STATUS_OK, meaning use the returned path as-is
713 (mapped into a local path).
714 or NT_STATUS_NOT_COVERED meaning return a DFS redirect, or
715 any other NT_STATUS error which is a genuine error to be
716 returned to the client.
717 *****************************************************************/
719 static NTSTATUS dfs_redirect(TALLOC_CTX *ctx,
720 connection_struct *conn,
721 const char *path_in,
722 bool search_wcard_flag,
723 bool allow_broken_path,
724 char **pp_path_out,
725 bool *ppath_contains_wcard)
727 NTSTATUS status;
728 struct dfs_path *pdp = talloc(ctx, struct dfs_path);
730 if (!pdp) {
731 return NT_STATUS_NO_MEMORY;
734 status = parse_dfs_path(conn, path_in, search_wcard_flag,
735 allow_broken_path, pdp,
736 ppath_contains_wcard);
737 if (!NT_STATUS_IS_OK(status)) {
738 TALLOC_FREE(pdp);
739 return status;
742 if (pdp->reqpath[0] == '\0') {
743 TALLOC_FREE(pdp);
744 *pp_path_out = talloc_strdup(ctx, "");
745 if (!*pp_path_out) {
746 return NT_STATUS_NO_MEMORY;
748 DEBUG(5,("dfs_redirect: self-referral.\n"));
749 return NT_STATUS_OK;
752 /* If dfs pathname for a non-dfs share, convert to tcon-relative
753 path and return OK */
755 if (!lp_msdfs_root(SNUM(conn))) {
756 *pp_path_out = talloc_strdup(ctx, pdp->reqpath);
757 TALLOC_FREE(pdp);
758 if (!*pp_path_out) {
759 return NT_STATUS_NO_MEMORY;
761 return NT_STATUS_OK;
764 /* If it looked like a local path (zero hostname/servicename)
765 * just treat as a tcon-relative path. */
767 if (pdp->hostname[0] == '\0' && pdp->servicename[0] == '\0') {
768 *pp_path_out = talloc_strdup(ctx, pdp->reqpath);
769 TALLOC_FREE(pdp);
770 if (!*pp_path_out) {
771 return NT_STATUS_NO_MEMORY;
773 return NT_STATUS_OK;
776 if (!( strequal(pdp->servicename, lp_servicename(SNUM(conn)))
777 || (strequal(pdp->servicename, HOMES_NAME)
778 && strequal(lp_servicename(SNUM(conn)),
779 conn->session_info->unix_info->sanitized_username) )) ) {
781 /* The given sharename doesn't match this connection. */
782 TALLOC_FREE(pdp);
784 return NT_STATUS_OBJECT_PATH_NOT_FOUND;
787 status = dfs_path_lookup(ctx, conn, path_in, pdp,
788 search_wcard_flag, NULL, NULL);
789 if (!NT_STATUS_IS_OK(status)) {
790 if (NT_STATUS_EQUAL(status, NT_STATUS_PATH_NOT_COVERED)) {
791 DEBUG(3,("dfs_redirect: Redirecting %s\n", path_in));
792 } else {
793 DEBUG(10,("dfs_redirect: dfs_path_lookup "
794 "failed for %s with %s\n",
795 path_in, nt_errstr(status) ));
797 return status;
800 DEBUG(3,("dfs_redirect: Not redirecting %s.\n", path_in));
802 /* Form non-dfs tcon-relative path */
803 *pp_path_out = talloc_strdup(ctx, pdp->reqpath);
804 TALLOC_FREE(pdp);
805 if (!*pp_path_out) {
806 return NT_STATUS_NO_MEMORY;
809 DEBUG(3,("dfs_redirect: Path %s converted to non-dfs path %s\n",
810 path_in,
811 *pp_path_out));
813 return NT_STATUS_OK;
816 /**********************************************************************
817 Return a self referral.
818 **********************************************************************/
820 static NTSTATUS self_ref(TALLOC_CTX *ctx,
821 const char *dfs_path,
822 struct junction_map *jucn,
823 int *consumedcntp,
824 bool *self_referralp)
826 struct referral *ref;
828 *self_referralp = True;
830 jucn->referral_count = 1;
831 if((ref = talloc_zero(ctx, struct referral)) == NULL) {
832 return NT_STATUS_NO_MEMORY;
835 ref->alternate_path = talloc_strdup(ctx, dfs_path);
836 if (!ref->alternate_path) {
837 return NT_STATUS_NO_MEMORY;
839 ref->proximity = 0;
840 ref->ttl = REFERRAL_TTL;
841 jucn->referral_list = ref;
842 *consumedcntp = strlen(dfs_path);
843 return NT_STATUS_OK;
846 /**********************************************************************
847 Gets valid referrals for a dfs path and fills up the
848 junction_map structure.
849 **********************************************************************/
851 NTSTATUS get_referred_path(TALLOC_CTX *ctx,
852 const char *dfs_path,
853 struct smbd_server_connection *sconn,
854 struct junction_map *jucn,
855 int *consumedcntp,
856 bool *self_referralp)
858 struct connection_struct *conn;
859 char *targetpath = NULL;
860 int snum;
861 NTSTATUS status = NT_STATUS_NOT_FOUND;
862 bool dummy;
863 struct dfs_path *pdp = talloc(ctx, struct dfs_path);
864 char *oldpath;
866 if (!pdp) {
867 return NT_STATUS_NO_MEMORY;
870 *self_referralp = False;
872 status = parse_dfs_path(NULL, dfs_path, False, !sconn->using_smb2,
873 pdp, &dummy);
874 if (!NT_STATUS_IS_OK(status)) {
875 return status;
878 jucn->service_name = talloc_strdup(ctx, pdp->servicename);
879 jucn->volume_name = talloc_strdup(ctx, pdp->reqpath);
880 if (!jucn->service_name || !jucn->volume_name) {
881 TALLOC_FREE(pdp);
882 return NT_STATUS_NO_MEMORY;
885 /* Verify the share is a dfs root */
886 snum = lp_servicenumber(jucn->service_name);
887 if(snum < 0) {
888 char *service_name = NULL;
889 if ((snum = find_service(ctx, jucn->service_name, &service_name)) < 0) {
890 return NT_STATUS_NOT_FOUND;
892 if (!service_name) {
893 return NT_STATUS_NO_MEMORY;
895 TALLOC_FREE(jucn->service_name);
896 jucn->service_name = talloc_strdup(ctx, service_name);
897 if (!jucn->service_name) {
898 TALLOC_FREE(pdp);
899 return NT_STATUS_NO_MEMORY;
903 if (!lp_msdfs_root(snum) && (*lp_msdfs_proxy(snum) == '\0')) {
904 DEBUG(3,("get_referred_path: |%s| in dfs path %s is not "
905 "a dfs root.\n",
906 pdp->servicename, dfs_path));
907 TALLOC_FREE(pdp);
908 return NT_STATUS_NOT_FOUND;
912 * Self referrals are tested with a anonymous IPC connection and
913 * a GET_DFS_REFERRAL call to \\server\share. (which means
914 * dp.reqpath[0] points to an empty string). create_conn_struct cd's
915 * into the directory and will fail if it cannot (as the anonymous
916 * user). Cope with this.
919 if (pdp->reqpath[0] == '\0') {
920 char *tmp;
921 struct referral *ref;
923 if (*lp_msdfs_proxy(snum) == '\0') {
924 TALLOC_FREE(pdp);
925 return self_ref(ctx,
926 dfs_path,
927 jucn,
928 consumedcntp,
929 self_referralp);
933 * It's an msdfs proxy share. Redirect to
934 * the configured target share.
937 jucn->referral_count = 1;
938 if ((ref = talloc_zero(ctx, struct referral)) == NULL) {
939 TALLOC_FREE(pdp);
940 return NT_STATUS_NO_MEMORY;
943 if (!(tmp = talloc_strdup(ctx, lp_msdfs_proxy(snum)))) {
944 TALLOC_FREE(pdp);
945 return NT_STATUS_NO_MEMORY;
948 trim_string(tmp, "\\", 0);
950 ref->alternate_path = talloc_asprintf(ctx, "\\%s", tmp);
951 TALLOC_FREE(tmp);
953 if (!ref->alternate_path) {
954 TALLOC_FREE(pdp);
955 return NT_STATUS_NO_MEMORY;
958 if (pdp->reqpath[0] != '\0') {
959 ref->alternate_path = talloc_asprintf_append(
960 ref->alternate_path,
961 "%s",
962 pdp->reqpath);
963 if (!ref->alternate_path) {
964 TALLOC_FREE(pdp);
965 return NT_STATUS_NO_MEMORY;
968 ref->proximity = 0;
969 ref->ttl = REFERRAL_TTL;
970 jucn->referral_list = ref;
971 *consumedcntp = strlen(dfs_path);
972 TALLOC_FREE(pdp);
973 return NT_STATUS_OK;
976 status = create_conn_struct(ctx, sconn, &conn, snum,
977 lp_pathname(snum), NULL, &oldpath);
978 if (!NT_STATUS_IS_OK(status)) {
979 TALLOC_FREE(pdp);
980 return status;
983 /* If this is a DFS path dfs_lookup should return
984 * NT_STATUS_PATH_NOT_COVERED. */
986 status = dfs_path_lookup(ctx, conn, dfs_path, pdp,
987 False, consumedcntp, &targetpath);
989 if (!NT_STATUS_EQUAL(status, NT_STATUS_PATH_NOT_COVERED)) {
990 DEBUG(3,("get_referred_path: No valid referrals for path %s\n",
991 dfs_path));
992 goto err_exit;
995 /* We know this is a valid dfs link. Parse the targetpath. */
996 if (!parse_msdfs_symlink(ctx, targetpath,
997 &jucn->referral_list,
998 &jucn->referral_count)) {
999 DEBUG(3,("get_referred_path: failed to parse symlink "
1000 "target %s\n", targetpath ));
1001 status = NT_STATUS_NOT_FOUND;
1002 goto err_exit;
1005 status = NT_STATUS_OK;
1006 err_exit:
1007 vfs_ChDir(conn, oldpath);
1008 SMB_VFS_DISCONNECT(conn);
1009 conn_free(conn);
1010 TALLOC_FREE(pdp);
1011 return status;
1014 static int setup_ver2_dfs_referral(const char *pathname,
1015 char **ppdata,
1016 struct junction_map *junction,
1017 bool self_referral)
1019 char* pdata = *ppdata;
1021 smb_ucs2_t *uni_requestedpath = NULL;
1022 int uni_reqpathoffset1,uni_reqpathoffset2;
1023 int uni_curroffset;
1024 int requestedpathlen=0;
1025 int offset;
1026 int reply_size = 0;
1027 int i=0;
1029 DEBUG(10,("Setting up version2 referral\nRequested path:\n"));
1031 requestedpathlen = rpcstr_push_talloc(talloc_tos(),
1032 &uni_requestedpath, pathname);
1033 if (uni_requestedpath == NULL || requestedpathlen == 0) {
1034 return -1;
1037 if (DEBUGLVL(10)) {
1038 dump_data(0, (unsigned char *)uni_requestedpath,
1039 requestedpathlen);
1042 DEBUG(10,("ref count = %u\n",junction->referral_count));
1044 uni_reqpathoffset1 = REFERRAL_HEADER_SIZE +
1045 VERSION2_REFERRAL_SIZE * junction->referral_count;
1047 uni_reqpathoffset2 = uni_reqpathoffset1 + requestedpathlen;
1049 uni_curroffset = uni_reqpathoffset2 + requestedpathlen;
1051 reply_size = REFERRAL_HEADER_SIZE +
1052 VERSION2_REFERRAL_SIZE*junction->referral_count +
1053 2 * requestedpathlen;
1054 DEBUG(10,("reply_size: %u\n",reply_size));
1056 /* add up the unicode lengths of all the referral paths */
1057 for(i=0;i<junction->referral_count;i++) {
1058 DEBUG(10,("referral %u : %s\n",
1060 junction->referral_list[i].alternate_path));
1061 reply_size +=
1062 (strlen(junction->referral_list[i].alternate_path)+1)*2;
1065 DEBUG(10,("reply_size = %u\n",reply_size));
1066 /* add the unexplained 0x16 bytes */
1067 reply_size += 0x16;
1069 pdata = (char *)SMB_REALLOC(pdata,reply_size);
1070 if(pdata == NULL) {
1071 DEBUG(0,("Realloc failed!\n"));
1072 return -1;
1074 *ppdata = pdata;
1076 /* copy in the dfs requested paths.. required for offset calculations */
1077 memcpy(pdata+uni_reqpathoffset1,uni_requestedpath,requestedpathlen);
1078 memcpy(pdata+uni_reqpathoffset2,uni_requestedpath,requestedpathlen);
1080 /* create the header */
1081 SSVAL(pdata,0,requestedpathlen - 2); /* UCS2 of path consumed minus
1082 2 byte null */
1083 /* number of referral in this pkt */
1084 SSVAL(pdata,2,junction->referral_count);
1085 if(self_referral) {
1086 SIVAL(pdata,4,DFSREF_REFERRAL_SERVER | DFSREF_STORAGE_SERVER);
1087 } else {
1088 SIVAL(pdata,4,DFSREF_STORAGE_SERVER);
1091 offset = 8;
1092 /* add the referral elements */
1093 for(i=0;i<junction->referral_count;i++) {
1094 struct referral* ref = &junction->referral_list[i];
1095 int unilen;
1097 SSVAL(pdata,offset,2); /* version 2 */
1098 SSVAL(pdata,offset+2,VERSION2_REFERRAL_SIZE);
1099 if(self_referral) {
1100 SSVAL(pdata,offset+4,1);
1101 } else {
1102 SSVAL(pdata,offset+4,0);
1105 /* ref_flags :use path_consumed bytes? */
1106 SSVAL(pdata,offset+6,0);
1107 SIVAL(pdata,offset+8,ref->proximity);
1108 SIVAL(pdata,offset+12,ref->ttl);
1110 SSVAL(pdata,offset+16,uni_reqpathoffset1-offset);
1111 SSVAL(pdata,offset+18,uni_reqpathoffset2-offset);
1112 /* copy referred path into current offset */
1113 unilen = rpcstr_push(pdata+uni_curroffset,
1114 ref->alternate_path,
1115 reply_size - uni_curroffset,
1116 STR_UNICODE);
1118 SSVAL(pdata,offset+20,uni_curroffset-offset);
1120 uni_curroffset += unilen;
1121 offset += VERSION2_REFERRAL_SIZE;
1123 /* add in the unexplained 22 (0x16) bytes at the end */
1124 memset(pdata+uni_curroffset,'\0',0x16);
1125 return reply_size;
1128 static int setup_ver3_dfs_referral(const char *pathname,
1129 char **ppdata,
1130 struct junction_map *junction,
1131 bool self_referral)
1133 char *pdata = *ppdata;
1135 smb_ucs2_t *uni_reqpath = NULL;
1136 int uni_reqpathoffset1, uni_reqpathoffset2;
1137 int uni_curroffset;
1138 int reply_size = 0;
1140 int reqpathlen = 0;
1141 int offset,i=0;
1143 DEBUG(10,("setting up version3 referral\n"));
1145 reqpathlen = rpcstr_push_talloc(talloc_tos(), &uni_reqpath, pathname);
1146 if (uni_reqpath == NULL || reqpathlen == 0) {
1147 return -1;
1150 if (DEBUGLVL(10)) {
1151 dump_data(0, (unsigned char *)uni_reqpath,
1152 reqpathlen);
1155 uni_reqpathoffset1 = REFERRAL_HEADER_SIZE +
1156 VERSION3_REFERRAL_SIZE * junction->referral_count;
1157 uni_reqpathoffset2 = uni_reqpathoffset1 + reqpathlen;
1158 reply_size = uni_curroffset = uni_reqpathoffset2 + reqpathlen;
1160 for(i=0;i<junction->referral_count;i++) {
1161 DEBUG(10,("referral %u : %s\n",
1163 junction->referral_list[i].alternate_path));
1164 reply_size +=
1165 (strlen(junction->referral_list[i].alternate_path)+1)*2;
1168 pdata = (char *)SMB_REALLOC(pdata,reply_size);
1169 if(pdata == NULL) {
1170 DEBUG(0,("version3 referral setup:"
1171 "malloc failed for Realloc!\n"));
1172 return -1;
1174 *ppdata = pdata;
1176 /* create the header */
1177 SSVAL(pdata,0,reqpathlen - 2); /* UCS2 of path consumed minus
1178 2 byte null */
1179 SSVAL(pdata,2,junction->referral_count); /* number of referral */
1180 if(self_referral) {
1181 SIVAL(pdata,4,DFSREF_REFERRAL_SERVER | DFSREF_STORAGE_SERVER);
1182 } else {
1183 SIVAL(pdata,4,DFSREF_STORAGE_SERVER);
1186 /* copy in the reqpaths */
1187 memcpy(pdata+uni_reqpathoffset1,uni_reqpath,reqpathlen);
1188 memcpy(pdata+uni_reqpathoffset2,uni_reqpath,reqpathlen);
1190 offset = 8;
1191 for(i=0;i<junction->referral_count;i++) {
1192 struct referral* ref = &(junction->referral_list[i]);
1193 int unilen;
1195 SSVAL(pdata,offset,3); /* version 3 */
1196 SSVAL(pdata,offset+2,VERSION3_REFERRAL_SIZE);
1197 if(self_referral) {
1198 SSVAL(pdata,offset+4,1);
1199 } else {
1200 SSVAL(pdata,offset+4,0);
1203 /* ref_flags :use path_consumed bytes? */
1204 SSVAL(pdata,offset+6,0);
1205 SIVAL(pdata,offset+8,ref->ttl);
1207 SSVAL(pdata,offset+12,uni_reqpathoffset1-offset);
1208 SSVAL(pdata,offset+14,uni_reqpathoffset2-offset);
1209 /* copy referred path into current offset */
1210 unilen = rpcstr_push(pdata+uni_curroffset,ref->alternate_path,
1211 reply_size - uni_curroffset,
1212 STR_UNICODE | STR_TERMINATE);
1213 SSVAL(pdata,offset+16,uni_curroffset-offset);
1214 /* copy 0x10 bytes of 00's in the ServiceSite GUID */
1215 memset(pdata+offset+18,'\0',16);
1217 uni_curroffset += unilen;
1218 offset += VERSION3_REFERRAL_SIZE;
1220 return reply_size;
1223 /******************************************************************
1224 Set up the DFS referral for the dfs pathname. This call returns
1225 the amount of the path covered by this server, and where the
1226 client should be redirected to. This is the meat of the
1227 TRANS2_GET_DFS_REFERRAL call.
1228 ******************************************************************/
1230 int setup_dfs_referral(connection_struct *orig_conn,
1231 const char *dfs_path,
1232 int max_referral_level,
1233 char **ppdata, NTSTATUS *pstatus)
1235 struct junction_map *junction = NULL;
1236 int consumedcnt = 0;
1237 bool self_referral = False;
1238 int reply_size = 0;
1239 char *pathnamep = NULL;
1240 char *local_dfs_path = NULL;
1241 TALLOC_CTX *ctx;
1243 if (!(ctx=talloc_init("setup_dfs_referral"))) {
1244 *pstatus = NT_STATUS_NO_MEMORY;
1245 return -1;
1248 /* get the junction entry */
1249 if (!dfs_path) {
1250 talloc_destroy(ctx);
1251 *pstatus = NT_STATUS_NOT_FOUND;
1252 return -1;
1256 * Trim pathname sent by client so it begins with only one backslash.
1257 * Two backslashes confuse some dfs clients
1260 local_dfs_path = talloc_strdup(ctx,dfs_path);
1261 if (!local_dfs_path) {
1262 *pstatus = NT_STATUS_NO_MEMORY;
1263 talloc_destroy(ctx);
1264 return -1;
1266 pathnamep = local_dfs_path;
1267 while (IS_DIRECTORY_SEP(pathnamep[0]) &&
1268 IS_DIRECTORY_SEP(pathnamep[1])) {
1269 pathnamep++;
1272 junction = talloc_zero(ctx, struct junction_map);
1273 if (!junction) {
1274 *pstatus = NT_STATUS_NO_MEMORY;
1275 talloc_destroy(ctx);
1276 return -1;
1279 /* The following call can change cwd. */
1280 *pstatus = get_referred_path(ctx, pathnamep, orig_conn->sconn,
1281 junction, &consumedcnt, &self_referral);
1282 if (!NT_STATUS_IS_OK(*pstatus)) {
1283 vfs_ChDir(orig_conn,orig_conn->connectpath);
1284 talloc_destroy(ctx);
1285 return -1;
1287 vfs_ChDir(orig_conn,orig_conn->connectpath);
1289 if (!self_referral) {
1290 pathnamep[consumedcnt] = '\0';
1292 if( DEBUGLVL( 3 ) ) {
1293 int i=0;
1294 dbgtext("setup_dfs_referral: Path %s to "
1295 "alternate path(s):",
1296 pathnamep);
1297 for(i=0;i<junction->referral_count;i++)
1298 dbgtext(" %s",
1299 junction->referral_list[i].alternate_path);
1300 dbgtext(".\n");
1304 /* create the referral depeding on version */
1305 DEBUG(10,("max_referral_level :%d\n",max_referral_level));
1307 if (max_referral_level < 2) {
1308 max_referral_level = 2;
1310 if (max_referral_level > 3) {
1311 max_referral_level = 3;
1314 switch(max_referral_level) {
1315 case 2:
1316 reply_size = setup_ver2_dfs_referral(pathnamep,
1317 ppdata, junction,
1318 self_referral);
1319 break;
1320 case 3:
1321 reply_size = setup_ver3_dfs_referral(pathnamep, ppdata,
1322 junction, self_referral);
1323 break;
1324 default:
1325 DEBUG(0,("setup_dfs_referral: Invalid dfs referral "
1326 "version: %d\n",
1327 max_referral_level));
1328 talloc_destroy(ctx);
1329 *pstatus = NT_STATUS_INVALID_LEVEL;
1330 return -1;
1333 if (DEBUGLVL(10)) {
1334 DEBUGADD(0,("DFS Referral pdata:\n"));
1335 dump_data(0,(uint8 *)*ppdata,reply_size);
1338 talloc_destroy(ctx);
1339 *pstatus = NT_STATUS_OK;
1340 return reply_size;
1343 /**********************************************************************
1344 The following functions are called by the NETDFS RPC pipe functions
1345 **********************************************************************/
1347 /*********************************************************************
1348 Creates a junction structure from a DFS pathname
1349 **********************************************************************/
1351 bool create_junction(TALLOC_CTX *ctx,
1352 const char *dfs_path,
1353 struct junction_map *jucn)
1355 int snum;
1356 bool dummy;
1357 struct dfs_path *pdp = talloc(ctx,struct dfs_path);
1358 NTSTATUS status;
1360 if (!pdp) {
1361 return False;
1363 status = parse_dfs_path(NULL, dfs_path, False,
1364 !smbd_server_conn->using_smb2, pdp, &dummy);
1365 if (!NT_STATUS_IS_OK(status)) {
1366 return False;
1369 /* check if path is dfs : validate first token */
1370 if (!is_myname_or_ipaddr(pdp->hostname)) {
1371 DEBUG(4,("create_junction: Invalid hostname %s "
1372 "in dfs path %s\n",
1373 pdp->hostname, dfs_path));
1374 TALLOC_FREE(pdp);
1375 return False;
1378 /* Check for a non-DFS share */
1379 snum = lp_servicenumber(pdp->servicename);
1381 if(snum < 0 || !lp_msdfs_root(snum)) {
1382 DEBUG(4,("create_junction: %s is not an msdfs root.\n",
1383 pdp->servicename));
1384 TALLOC_FREE(pdp);
1385 return False;
1388 jucn->service_name = talloc_strdup(ctx, pdp->servicename);
1389 jucn->volume_name = talloc_strdup(ctx, pdp->reqpath);
1390 jucn->comment = talloc_strdup(ctx, lp_comment(snum));
1392 TALLOC_FREE(pdp);
1393 if (!jucn->service_name || !jucn->volume_name || ! jucn->comment) {
1394 return False;
1396 return True;
1399 /**********************************************************************
1400 Forms a valid Unix pathname from the junction
1401 **********************************************************************/
1403 static bool junction_to_local_path(const struct junction_map *jucn,
1404 char **pp_path_out,
1405 connection_struct **conn_out,
1406 char **oldpath)
1408 int snum;
1409 NTSTATUS status;
1411 snum = lp_servicenumber(jucn->service_name);
1412 if(snum < 0) {
1413 return False;
1415 status = create_conn_struct(talloc_tos(), smbd_server_conn, conn_out,
1416 snum, lp_pathname(snum), NULL, oldpath);
1417 if (!NT_STATUS_IS_OK(status)) {
1418 return False;
1421 *pp_path_out = talloc_asprintf(*conn_out,
1422 "%s/%s",
1423 lp_pathname(snum),
1424 jucn->volume_name);
1425 if (!*pp_path_out) {
1426 vfs_ChDir(*conn_out, *oldpath);
1427 SMB_VFS_DISCONNECT(*conn_out);
1428 conn_free(*conn_out);
1429 return False;
1431 return True;
1434 bool create_msdfs_link(const struct junction_map *jucn)
1436 char *path = NULL;
1437 char *cwd;
1438 char *msdfs_link = NULL;
1439 connection_struct *conn;
1440 int i=0;
1441 bool insert_comma = False;
1442 bool ret = False;
1444 if(!junction_to_local_path(jucn, &path, &conn, &cwd)) {
1445 return False;
1448 /* Form the msdfs_link contents */
1449 msdfs_link = talloc_strdup(conn, "msdfs:");
1450 if (!msdfs_link) {
1451 goto out;
1453 for(i=0; i<jucn->referral_count; i++) {
1454 char *refpath = jucn->referral_list[i].alternate_path;
1456 /* Alternate paths always use Windows separators. */
1457 trim_char(refpath, '\\', '\\');
1458 if(*refpath == '\0') {
1459 if (i == 0) {
1460 insert_comma = False;
1462 continue;
1464 if (i > 0 && insert_comma) {
1465 msdfs_link = talloc_asprintf_append_buffer(msdfs_link,
1466 ",%s",
1467 refpath);
1468 } else {
1469 msdfs_link = talloc_asprintf_append_buffer(msdfs_link,
1470 "%s",
1471 refpath);
1474 if (!msdfs_link) {
1475 goto out;
1477 if (!insert_comma) {
1478 insert_comma = True;
1482 DEBUG(5,("create_msdfs_link: Creating new msdfs link: %s -> %s\n",
1483 path, msdfs_link));
1485 if(SMB_VFS_SYMLINK(conn, msdfs_link, path) < 0) {
1486 if (errno == EEXIST) {
1487 struct smb_filename *smb_fname = NULL;
1488 NTSTATUS status;
1490 status = create_synthetic_smb_fname(talloc_tos(), path,
1491 NULL, NULL,
1492 &smb_fname);
1493 if (!NT_STATUS_IS_OK(status)) {
1494 errno = map_errno_from_nt_status(status);
1495 goto out;
1498 if(SMB_VFS_UNLINK(conn, smb_fname)!=0) {
1499 TALLOC_FREE(smb_fname);
1500 goto out;
1502 TALLOC_FREE(smb_fname);
1504 if (SMB_VFS_SYMLINK(conn, msdfs_link, path) < 0) {
1505 DEBUG(1,("create_msdfs_link: symlink failed "
1506 "%s -> %s\nError: %s\n",
1507 path, msdfs_link, strerror(errno)));
1508 goto out;
1512 ret = True;
1514 out:
1515 vfs_ChDir(conn, cwd);
1516 SMB_VFS_DISCONNECT(conn);
1517 conn_free(conn);
1518 return ret;
1521 bool remove_msdfs_link(const struct junction_map *jucn)
1523 char *path = NULL;
1524 char *cwd;
1525 connection_struct *conn;
1526 bool ret = False;
1527 struct smb_filename *smb_fname = NULL;
1528 NTSTATUS status;
1530 if (!junction_to_local_path(jucn, &path, &conn, &cwd)) {
1531 return false;
1534 status = create_synthetic_smb_fname(talloc_tos(), path,
1535 NULL, NULL,
1536 &smb_fname);
1537 if (!NT_STATUS_IS_OK(status)) {
1538 errno = map_errno_from_nt_status(status);
1539 return false;
1542 if( SMB_VFS_UNLINK(conn, smb_fname) == 0 ) {
1543 ret = True;
1546 TALLOC_FREE(smb_fname);
1547 vfs_ChDir(conn, cwd);
1548 SMB_VFS_DISCONNECT(conn);
1549 conn_free(conn);
1550 return ret;
1553 /*********************************************************************
1554 Return the number of DFS links at the root of this share.
1555 *********************************************************************/
1557 static int count_dfs_links(TALLOC_CTX *ctx, int snum)
1559 size_t cnt = 0;
1560 SMB_STRUCT_DIR *dirp = NULL;
1561 const char *dname = NULL;
1562 char *talloced = NULL;
1563 const char *connect_path = lp_pathname(snum);
1564 const char *msdfs_proxy = lp_msdfs_proxy(snum);
1565 connection_struct *conn;
1566 NTSTATUS status;
1567 char *cwd;
1569 if(*connect_path == '\0') {
1570 return 0;
1574 * Fake up a connection struct for the VFS layer.
1577 status = create_conn_struct(talloc_tos(), smbd_server_conn, &conn,
1578 snum, connect_path, NULL, &cwd);
1579 if (!NT_STATUS_IS_OK(status)) {
1580 DEBUG(3, ("create_conn_struct failed: %s\n",
1581 nt_errstr(status)));
1582 return 0;
1585 /* Count a link for the msdfs root - convention */
1586 cnt = 1;
1588 /* No more links if this is an msdfs proxy. */
1589 if (*msdfs_proxy != '\0') {
1590 goto out;
1593 /* Now enumerate all dfs links */
1594 dirp = SMB_VFS_OPENDIR(conn, ".", NULL, 0);
1595 if(!dirp) {
1596 goto out;
1599 while ((dname = vfs_readdirname(conn, dirp, NULL, &talloced))
1600 != NULL) {
1601 if (is_msdfs_link(conn,
1602 dname,
1603 NULL)) {
1604 cnt++;
1606 TALLOC_FREE(talloced);
1609 SMB_VFS_CLOSEDIR(conn,dirp);
1611 out:
1612 vfs_ChDir(conn, cwd);
1613 SMB_VFS_DISCONNECT(conn);
1614 conn_free(conn);
1615 return cnt;
1618 /*********************************************************************
1619 *********************************************************************/
1621 static int form_junctions(TALLOC_CTX *ctx,
1622 int snum,
1623 struct junction_map *jucn,
1624 size_t jn_remain)
1626 size_t cnt = 0;
1627 SMB_STRUCT_DIR *dirp = NULL;
1628 const char *dname = NULL;
1629 char *talloced = NULL;
1630 const char *connect_path = lp_pathname(snum);
1631 char *service_name = lp_servicename(snum);
1632 const char *msdfs_proxy = lp_msdfs_proxy(snum);
1633 connection_struct *conn;
1634 struct referral *ref = NULL;
1635 char *cwd;
1636 NTSTATUS status;
1638 if (jn_remain == 0) {
1639 return 0;
1642 if(*connect_path == '\0') {
1643 return 0;
1647 * Fake up a connection struct for the VFS layer.
1650 status = create_conn_struct(ctx, smbd_server_conn, &conn, snum, connect_path, NULL,
1651 &cwd);
1652 if (!NT_STATUS_IS_OK(status)) {
1653 DEBUG(3, ("create_conn_struct failed: %s\n",
1654 nt_errstr(status)));
1655 return 0;
1658 /* form a junction for the msdfs root - convention
1659 DO NOT REMOVE THIS: NT clients will not work with us
1660 if this is not present
1662 jucn[cnt].service_name = talloc_strdup(ctx,service_name);
1663 jucn[cnt].volume_name = talloc_strdup(ctx, "");
1664 if (!jucn[cnt].service_name || !jucn[cnt].volume_name) {
1665 goto out;
1667 jucn[cnt].comment = "";
1668 jucn[cnt].referral_count = 1;
1670 ref = jucn[cnt].referral_list = talloc_zero(ctx, struct referral);
1671 if (jucn[cnt].referral_list == NULL) {
1672 goto out;
1675 ref->proximity = 0;
1676 ref->ttl = REFERRAL_TTL;
1677 if (*msdfs_proxy != '\0') {
1678 ref->alternate_path = talloc_strdup(ctx,
1679 msdfs_proxy);
1680 } else {
1681 ref->alternate_path = talloc_asprintf(ctx,
1682 "\\\\%s\\%s",
1683 get_local_machine_name(),
1684 service_name);
1687 if (!ref->alternate_path) {
1688 goto out;
1690 cnt++;
1692 /* Don't enumerate if we're an msdfs proxy. */
1693 if (*msdfs_proxy != '\0') {
1694 goto out;
1697 /* Now enumerate all dfs links */
1698 dirp = SMB_VFS_OPENDIR(conn, ".", NULL, 0);
1699 if(!dirp) {
1700 goto out;
1703 while ((dname = vfs_readdirname(conn, dirp, NULL, &talloced))
1704 != NULL) {
1705 char *link_target = NULL;
1706 if (cnt >= jn_remain) {
1707 DEBUG(2, ("form_junctions: ran out of MSDFS "
1708 "junction slots"));
1709 TALLOC_FREE(talloced);
1710 goto out;
1712 if (is_msdfs_link_internal(ctx,
1713 conn,
1714 dname, &link_target,
1715 NULL)) {
1716 if (parse_msdfs_symlink(ctx,
1717 link_target,
1718 &jucn[cnt].referral_list,
1719 &jucn[cnt].referral_count)) {
1721 jucn[cnt].service_name = talloc_strdup(ctx,
1722 service_name);
1723 jucn[cnt].volume_name = talloc_strdup(ctx,
1724 dname);
1725 if (!jucn[cnt].service_name ||
1726 !jucn[cnt].volume_name) {
1727 TALLOC_FREE(talloced);
1728 goto out;
1730 jucn[cnt].comment = "";
1731 cnt++;
1733 TALLOC_FREE(link_target);
1735 TALLOC_FREE(talloced);
1738 out:
1740 if (dirp) {
1741 SMB_VFS_CLOSEDIR(conn,dirp);
1744 vfs_ChDir(conn, cwd);
1745 conn_free(conn);
1746 return cnt;
1749 struct junction_map *enum_msdfs_links(struct smbd_server_connection *sconn,
1750 TALLOC_CTX *ctx, size_t *p_num_jn)
1752 struct junction_map *jn = NULL;
1753 int i=0;
1754 size_t jn_count = 0;
1755 int sharecount = 0;
1757 *p_num_jn = 0;
1758 if(!lp_host_msdfs()) {
1759 return NULL;
1762 /* Ensure all the usershares are loaded. */
1763 become_root();
1764 load_registry_shares();
1765 sharecount = load_usershare_shares(sconn);
1766 unbecome_root();
1768 for(i=0;i < sharecount;i++) {
1769 if(lp_msdfs_root(i)) {
1770 jn_count += count_dfs_links(ctx, i);
1773 if (jn_count == 0) {
1774 return NULL;
1776 jn = talloc_array(ctx, struct junction_map, jn_count);
1777 if (!jn) {
1778 return NULL;
1780 for(i=0; i < sharecount; i++) {
1781 if (*p_num_jn >= jn_count) {
1782 break;
1784 if(lp_msdfs_root(i)) {
1785 *p_num_jn += form_junctions(ctx, i,
1786 &jn[*p_num_jn],
1787 jn_count - *p_num_jn);
1790 return jn;
1793 /******************************************************************************
1794 Core function to resolve a dfs pathname possibly containing a wildcard. If
1795 ppath_contains_wcard != NULL, it will be set to true if a wildcard is
1796 detected during dfs resolution.
1797 ******************************************************************************/
1799 NTSTATUS resolve_dfspath_wcard(TALLOC_CTX *ctx,
1800 connection_struct *conn,
1801 bool dfs_pathnames,
1802 const char *name_in,
1803 bool allow_wcards,
1804 char **pp_name_out,
1805 bool *ppath_contains_wcard)
1807 bool path_contains_wcard;
1808 NTSTATUS status = NT_STATUS_OK;
1810 if (dfs_pathnames) {
1811 status = dfs_redirect(ctx,
1812 conn,
1813 name_in,
1814 allow_wcards,
1815 !smbd_server_conn->using_smb2,
1816 pp_name_out,
1817 &path_contains_wcard);
1819 if (NT_STATUS_IS_OK(status) && ppath_contains_wcard != NULL) {
1820 *ppath_contains_wcard = path_contains_wcard;
1822 } else {
1824 * Cheat and just return a copy of the in ptr.
1825 * Once srvstr_get_path() uses talloc it'll
1826 * be a talloced ptr anyway.
1828 *pp_name_out = discard_const_p(char, name_in);
1830 return status;