WHATSNEW: Add release notes for Samba 3.6.20.
[Samba.git] / source3 / smbd / msdfs.c
blob1235f0fef38cb49c98c1084168eb6361bcb3181d
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 "libcli/security/security.h"
32 /**********************************************************************
33 Parse a DFS pathname of the form \hostname\service\reqpath
34 into the dfs_path structure.
35 If POSIX pathnames is true, the pathname may also be of the
36 form /hostname/service/reqpath.
37 We cope with either here.
39 Unfortunately, due to broken clients who might set the
40 SVAL(inbuf,smb_flg2) & FLAGS2_DFS_PATHNAMES bit and then
41 send a local path, we have to cope with that too....
43 If conn != NULL then ensure the provided service is
44 the one pointed to by the connection.
46 This version does everything using pointers within one copy of the
47 pathname string, talloced on the struct dfs_path pointer (which
48 must be talloced). This may be too clever to live....
49 JRA.
50 **********************************************************************/
52 static NTSTATUS parse_dfs_path(connection_struct *conn,
53 const char *pathname,
54 bool allow_wcards,
55 struct dfs_path *pdp, /* MUST BE TALLOCED */
56 bool *ppath_contains_wcard)
58 struct smbd_server_connection *sconn = smbd_server_conn;
59 char *pathname_local;
60 char *p,*temp;
61 char *servicename;
62 char *eos_ptr;
63 NTSTATUS status = NT_STATUS_OK;
64 char sepchar;
66 ZERO_STRUCTP(pdp);
69 * This is the only talloc we should need to do
70 * on the struct dfs_path. All the pointers inside
71 * it should point to offsets within this string.
74 pathname_local = talloc_strdup(pdp, pathname);
75 if (!pathname_local) {
76 return NT_STATUS_NO_MEMORY;
78 /* Get a pointer to the terminating '\0' */
79 eos_ptr = &pathname_local[strlen(pathname_local)];
80 p = temp = pathname_local;
82 pdp->posix_path = (lp_posix_pathnames() && *pathname == '/');
84 sepchar = pdp->posix_path ? '/' : '\\';
86 if (!sconn->using_smb2 && (*pathname != sepchar)) {
87 DEBUG(10,("parse_dfs_path: path %s doesn't start with %c\n",
88 pathname, sepchar ));
90 * Possibly client sent a local path by mistake.
91 * Try and convert to a local path.
94 pdp->hostname = eos_ptr; /* "" */
95 pdp->servicename = eos_ptr; /* "" */
97 /* We've got no info about separators. */
98 pdp->posix_path = lp_posix_pathnames();
99 p = temp;
100 DEBUG(10,("parse_dfs_path: trying to convert %s to a "
101 "local path\n",
102 temp));
103 goto local_path;
107 * Safe to use on talloc'ed string as it only shrinks.
108 * It also doesn't affect the eos_ptr.
110 trim_char(temp,sepchar,sepchar);
112 DEBUG(10,("parse_dfs_path: temp = |%s| after trimming %c's\n",
113 temp, sepchar));
115 /* Now tokenize. */
116 /* Parse out hostname. */
117 p = strchr_m(temp,sepchar);
118 if(p == NULL) {
119 DEBUG(10,("parse_dfs_path: can't parse hostname from path %s\n",
120 temp));
122 * Possibly client sent a local path by mistake.
123 * Try and convert to a local path.
126 pdp->hostname = eos_ptr; /* "" */
127 pdp->servicename = eos_ptr; /* "" */
129 p = temp;
130 DEBUG(10,("parse_dfs_path: trying to convert %s "
131 "to a local path\n",
132 temp));
133 goto local_path;
135 *p = '\0';
136 pdp->hostname = temp;
138 DEBUG(10,("parse_dfs_path: hostname: %s\n",pdp->hostname));
140 /* Parse out servicename. */
141 servicename = p+1;
142 p = strchr_m(servicename,sepchar);
143 if (p) {
144 *p = '\0';
147 /* Is this really our servicename ? */
148 if (conn && !( strequal(servicename, lp_servicename(SNUM(conn)))
149 || (strequal(servicename, HOMES_NAME)
150 && strequal(lp_servicename(SNUM(conn)),
151 get_current_username()) )) ) {
152 DEBUG(10,("parse_dfs_path: %s is not our servicename\n",
153 servicename));
156 * Possibly client sent a local path by mistake.
157 * Try and convert to a local path.
160 pdp->hostname = eos_ptr; /* "" */
161 pdp->servicename = eos_ptr; /* "" */
163 /* Repair the path - replace the sepchar's
164 we nulled out */
165 servicename--;
166 *servicename = sepchar;
167 if (p) {
168 *p = sepchar;
171 p = temp;
172 DEBUG(10,("parse_dfs_path: trying to convert %s "
173 "to a local path\n",
174 temp));
175 goto local_path;
178 pdp->servicename = servicename;
180 DEBUG(10,("parse_dfs_path: servicename: %s\n",pdp->servicename));
182 if(p == NULL) {
183 /* Client sent self referral \server\share. */
184 pdp->reqpath = eos_ptr; /* "" */
185 return NT_STATUS_OK;
188 p++;
190 local_path:
192 *ppath_contains_wcard = False;
194 pdp->reqpath = p;
196 /* Rest is reqpath. */
197 if (pdp->posix_path) {
198 status = check_path_syntax_posix(pdp->reqpath);
199 } else {
200 if (allow_wcards) {
201 status = check_path_syntax_wcard(pdp->reqpath,
202 ppath_contains_wcard);
203 } else {
204 status = check_path_syntax(pdp->reqpath);
208 if (!NT_STATUS_IS_OK(status)) {
209 DEBUG(10,("parse_dfs_path: '%s' failed with %s\n",
210 p, nt_errstr(status) ));
211 return status;
214 DEBUG(10,("parse_dfs_path: rest of the path: %s\n",pdp->reqpath));
215 return NT_STATUS_OK;
218 /********************************************************
219 Fake up a connection struct for the VFS layer.
220 Note: this performs a vfs connect and CHANGES CWD !!!! JRA.
221 *********************************************************/
223 NTSTATUS create_conn_struct(TALLOC_CTX *ctx,
224 connection_struct **pconn,
225 int snum,
226 const char *path,
227 const struct auth_serversupplied_info *session_info,
228 char **poldcwd)
230 connection_struct *conn;
231 char *connpath;
232 char *oldcwd;
233 const char *vfs_user;
235 conn = TALLOC_ZERO_P(ctx, connection_struct);
236 if (conn == NULL) {
237 return NT_STATUS_NO_MEMORY;
240 connpath = talloc_strdup(conn, path);
241 if (!connpath) {
242 TALLOC_FREE(conn);
243 return NT_STATUS_NO_MEMORY;
245 connpath = talloc_string_sub(conn,
246 connpath,
247 "%S",
248 lp_servicename(snum));
249 if (!connpath) {
250 TALLOC_FREE(conn);
251 return NT_STATUS_NO_MEMORY;
254 /* needed for smbd_vfs_init() */
256 if (!(conn->params = TALLOC_ZERO_P(conn, struct share_params))) {
257 DEBUG(0, ("TALLOC failed\n"));
258 TALLOC_FREE(conn);
259 return NT_STATUS_NO_MEMORY;
262 conn->params->service = snum;
264 conn->sconn = smbd_server_conn;
265 conn->sconn->num_tcons_open++;
267 if (session_info != NULL) {
268 conn->session_info = copy_serverinfo(conn, session_info);
269 if (conn->session_info == NULL) {
270 DEBUG(0, ("copy_serverinfo failed\n"));
271 TALLOC_FREE(conn);
272 return NT_STATUS_NO_MEMORY;
274 vfs_user = conn->session_info->unix_name;
275 } else {
276 /* use current authenticated user in absence of session_info */
277 vfs_user = get_current_username();
280 set_conn_connectpath(conn, connpath);
283 * New code to check if there's a share security descripter
284 * added from NT server manager. This is done after the
285 * smb.conf checks are done as we need a uid and token. JRA.
288 if (conn->session_info) {
289 share_access_check(conn->session_info->security_token,
290 lp_servicename(snum), MAXIMUM_ALLOWED_ACCESS,
291 &conn->share_access);
293 if ((conn->share_access & FILE_WRITE_DATA) == 0) {
294 if ((conn->share_access & FILE_READ_DATA) == 0) {
295 /* No access, read or write. */
296 DEBUG(0,("create_conn_struct: connection to %s "
297 "denied due to security "
298 "descriptor.\n",
299 lp_servicename(snum)));
300 conn_free(conn);
301 return NT_STATUS_ACCESS_DENIED;
302 } else {
303 conn->read_only = true;
306 } else {
307 conn->share_access = 0;
308 conn->read_only = true;
311 if (!smbd_vfs_init(conn)) {
312 NTSTATUS status = map_nt_error_from_unix(errno);
313 DEBUG(0,("create_conn_struct: smbd_vfs_init failed.\n"));
314 conn_free(conn);
315 return status;
318 /* this must be the first filesystem operation that we do */
319 if (SMB_VFS_CONNECT(conn, lp_servicename(snum), vfs_user) < 0) {
320 DEBUG(0,("VFS connect failed!\n"));
321 conn_free(conn);
322 return NT_STATUS_UNSUCCESSFUL;
325 conn->fs_capabilities = SMB_VFS_FS_CAPABILITIES(conn, &conn->ts_res);
328 * Windows seems to insist on doing trans2getdfsreferral() calls on
329 * the IPC$ share as the anonymous user. If we try to chdir as that
330 * user we will fail.... WTF ? JRA.
333 oldcwd = vfs_GetWd(ctx, conn);
334 if (oldcwd == NULL) {
335 NTSTATUS status = map_nt_error_from_unix(errno);
336 DEBUG(3, ("vfs_GetWd failed: %s\n", strerror(errno)));
337 conn_free(conn);
338 return status;
341 if (vfs_ChDir(conn,conn->connectpath) != 0) {
342 NTSTATUS status = map_nt_error_from_unix(errno);
343 DEBUG(3,("create_conn_struct: Can't ChDir to new conn path %s. "
344 "Error was %s\n",
345 conn->connectpath, strerror(errno) ));
346 conn_free(conn);
347 return status;
350 *pconn = conn;
351 *poldcwd = oldcwd;
353 return NT_STATUS_OK;
356 /**********************************************************************
357 Parse the contents of a symlink to verify if it is an msdfs referral
358 A valid referral is of the form:
360 msdfs:server1\share1,server2\share2
361 msdfs:server1\share1\pathname,server2\share2\pathname
362 msdfs:server1/share1,server2/share2
363 msdfs:server1/share1/pathname,server2/share2/pathname.
365 Note that the alternate paths returned here must be of the canonicalized
366 form:
368 \server\share or
369 \server\share\path\to\file,
371 even in posix path mode. This is because we have no knowledge if the
372 server we're referring to understands posix paths.
373 **********************************************************************/
375 static bool parse_msdfs_symlink(TALLOC_CTX *ctx,
376 const char *target,
377 struct referral **preflist,
378 int *refcount)
380 char *temp = NULL;
381 char *prot;
382 char **alt_path = NULL;
383 int count = 0, i;
384 struct referral *reflist;
385 char *saveptr;
387 temp = talloc_strdup(ctx, target);
388 if (!temp) {
389 return False;
391 prot = strtok_r(temp, ":", &saveptr);
392 if (!prot) {
393 DEBUG(0,("parse_msdfs_symlink: invalid path !\n"));
394 return False;
397 alt_path = TALLOC_ARRAY(ctx, char *, MAX_REFERRAL_COUNT);
398 if (!alt_path) {
399 return False;
402 /* parse out the alternate paths */
403 while((count<MAX_REFERRAL_COUNT) &&
404 ((alt_path[count] = strtok_r(NULL, ",", &saveptr)) != NULL)) {
405 count++;
408 DEBUG(10,("parse_msdfs_symlink: count=%d\n", count));
410 if (count) {
411 reflist = *preflist = TALLOC_ZERO_ARRAY(ctx,
412 struct referral, count);
413 if(reflist == NULL) {
414 TALLOC_FREE(alt_path);
415 return False;
417 } else {
418 reflist = *preflist = NULL;
421 for(i=0;i<count;i++) {
422 char *p;
424 /* Canonicalize link target.
425 * Replace all /'s in the path by a \ */
426 string_replace(alt_path[i], '/', '\\');
428 /* Remove leading '\\'s */
429 p = alt_path[i];
430 while (*p && (*p == '\\')) {
431 p++;
434 reflist[i].alternate_path = talloc_asprintf(ctx,
435 "\\%s",
437 if (!reflist[i].alternate_path) {
438 return False;
441 reflist[i].proximity = 0;
442 reflist[i].ttl = REFERRAL_TTL;
443 DEBUG(10, ("parse_msdfs_symlink: Created alt path: %s\n",
444 reflist[i].alternate_path));
447 *refcount = count;
449 TALLOC_FREE(alt_path);
450 return True;
453 /**********************************************************************
454 Returns true if the unix path is a valid msdfs symlink and also
455 returns the target string from inside the link.
456 **********************************************************************/
458 static bool is_msdfs_link_internal(TALLOC_CTX *ctx,
459 connection_struct *conn,
460 const char *path,
461 char **pp_link_target,
462 SMB_STRUCT_STAT *sbufp)
464 int referral_len = 0;
465 #if defined(HAVE_BROKEN_READLINK)
466 char link_target_buf[PATH_MAX];
467 #else
468 char link_target_buf[7];
469 #endif
470 size_t bufsize = 0;
471 char *link_target = NULL;
472 struct smb_filename smb_fname;
474 if (pp_link_target) {
475 bufsize = 1024;
476 link_target = TALLOC_ARRAY(ctx, char, bufsize);
477 if (!link_target) {
478 return False;
480 *pp_link_target = link_target;
481 } else {
482 bufsize = sizeof(link_target_buf);
483 link_target = link_target_buf;
486 ZERO_STRUCT(smb_fname);
487 smb_fname.base_name = discard_const_p(char, path);
489 if (SMB_VFS_LSTAT(conn, &smb_fname) != 0) {
490 DEBUG(5,("is_msdfs_link_read_target: %s does not exist.\n",
491 path));
492 goto err;
494 if (!S_ISLNK(smb_fname.st.st_ex_mode)) {
495 DEBUG(5,("is_msdfs_link_read_target: %s is not a link.\n",
496 path));
497 goto err;
499 if (sbufp != NULL) {
500 *sbufp = smb_fname.st;
503 referral_len = SMB_VFS_READLINK(conn, path, link_target, bufsize - 1);
504 if (referral_len == -1) {
505 DEBUG(0,("is_msdfs_link_read_target: Error reading "
506 "msdfs link %s: %s\n",
507 path, strerror(errno)));
508 goto err;
510 link_target[referral_len] = '\0';
512 DEBUG(5,("is_msdfs_link_internal: %s -> %s\n",path,
513 link_target));
515 if (!strnequal(link_target, "msdfs:", 6)) {
516 goto err;
518 return True;
520 err:
522 if (link_target != link_target_buf) {
523 TALLOC_FREE(link_target);
525 return False;
528 /**********************************************************************
529 Returns true if the unix path is a valid msdfs symlink.
530 **********************************************************************/
532 bool is_msdfs_link(connection_struct *conn,
533 const char *path,
534 SMB_STRUCT_STAT *sbufp)
536 return is_msdfs_link_internal(talloc_tos(),
537 conn,
538 path,
539 NULL,
540 sbufp);
543 /*****************************************************************
544 Used by other functions to decide if a dfs path is remote,
545 and to get the list of referred locations for that remote path.
547 search_flag: For findfirsts, dfs links themselves are not
548 redirected, but paths beyond the links are. For normal smb calls,
549 even dfs links need to be redirected.
551 consumedcntp: how much of the dfs path is being redirected. the client
552 should try the remaining path on the redirected server.
554 If this returns NT_STATUS_PATH_NOT_COVERED the contents of the msdfs
555 link redirect are in targetpath.
556 *****************************************************************/
558 static NTSTATUS dfs_path_lookup(TALLOC_CTX *ctx,
559 connection_struct *conn,
560 const char *dfspath, /* Incoming complete dfs path */
561 const struct dfs_path *pdp, /* Parsed out
562 server+share+extrapath. */
563 bool search_flag, /* Called from a findfirst ? */
564 int *consumedcntp,
565 char **pp_targetpath)
567 char *p = NULL;
568 char *q = NULL;
569 NTSTATUS status;
570 struct smb_filename *smb_fname = NULL;
571 char *canon_dfspath = NULL; /* Canonicalized dfs path. (only '/'
572 components). */
574 DEBUG(10,("dfs_path_lookup: Conn path = %s reqpath = %s\n",
575 conn->connectpath, pdp->reqpath));
578 * Note the unix path conversion here we're doing we
579 * throw away. We're looking for a symlink for a dfs
580 * resolution, if we don't find it we'll do another
581 * unix_convert later in the codepath.
584 status = unix_convert(ctx, conn, pdp->reqpath, &smb_fname,
585 search_flag ? UCF_ALWAYS_ALLOW_WCARD_LCOMP : 0);
587 if (!NT_STATUS_IS_OK(status)) {
588 if (!NT_STATUS_EQUAL(status,
589 NT_STATUS_OBJECT_PATH_NOT_FOUND)) {
590 return status;
592 if (smb_fname == NULL || smb_fname->base_name == NULL) {
593 return status;
597 /* Optimization - check if we can redirect the whole path. */
599 if (is_msdfs_link_internal(ctx, conn, smb_fname->base_name,
600 pp_targetpath, NULL)) {
601 if (search_flag) {
602 DEBUG(6,("dfs_path_lookup (FindFirst) No redirection "
603 "for dfs link %s.\n", dfspath));
604 status = NT_STATUS_OK;
605 goto out;
608 DEBUG(6,("dfs_path_lookup: %s resolves to a "
609 "valid dfs link %s.\n", dfspath,
610 pp_targetpath ? *pp_targetpath : ""));
612 if (consumedcntp) {
613 *consumedcntp = strlen(dfspath);
615 status = NT_STATUS_PATH_NOT_COVERED;
616 goto out;
619 /* Prepare to test only for '/' components in the given path,
620 * so if a Windows path replace all '\\' characters with '/'.
621 * For a POSIX DFS path we know all separators are already '/'. */
623 canon_dfspath = talloc_strdup(ctx, dfspath);
624 if (!canon_dfspath) {
625 status = NT_STATUS_NO_MEMORY;
626 goto out;
628 if (!pdp->posix_path) {
629 string_replace(canon_dfspath, '\\', '/');
633 * localpath comes out of unix_convert, so it has
634 * no trailing backslash. Make sure that canon_dfspath hasn't either.
635 * Fix for bug #4860 from Jan Martin <Jan.Martin@rwedea.com>.
638 trim_char(canon_dfspath,0,'/');
641 * Redirect if any component in the path is a link.
642 * We do this by walking backwards through the
643 * local path, chopping off the last component
644 * in both the local path and the canonicalized
645 * DFS path. If we hit a DFS link then we're done.
648 p = strrchr_m(smb_fname->base_name, '/');
649 if (consumedcntp) {
650 q = strrchr_m(canon_dfspath, '/');
653 while (p) {
654 *p = '\0';
655 if (q) {
656 *q = '\0';
659 if (is_msdfs_link_internal(ctx, conn,
660 smb_fname->base_name, pp_targetpath,
661 NULL)) {
662 DEBUG(4, ("dfs_path_lookup: Redirecting %s because "
663 "parent %s is dfs link\n", dfspath,
664 smb_fname_str_dbg(smb_fname)));
666 if (consumedcntp) {
667 *consumedcntp = strlen(canon_dfspath);
668 DEBUG(10, ("dfs_path_lookup: Path consumed: %s "
669 "(%d)\n",
670 canon_dfspath,
671 *consumedcntp));
674 status = NT_STATUS_PATH_NOT_COVERED;
675 goto out;
678 /* Step back on the filesystem. */
679 p = strrchr_m(smb_fname->base_name, '/');
681 if (consumedcntp) {
682 /* And in the canonicalized dfs path. */
683 q = strrchr_m(canon_dfspath, '/');
687 status = NT_STATUS_OK;
688 out:
689 TALLOC_FREE(smb_fname);
690 return status;
693 /*****************************************************************
694 Decides if a dfs pathname should be redirected or not.
695 If not, the pathname is converted to a tcon-relative local unix path
697 search_wcard_flag: this flag performs 2 functions both related
698 to searches. See resolve_dfs_path() and parse_dfs_path_XX()
699 for details.
701 This function can return NT_STATUS_OK, meaning use the returned path as-is
702 (mapped into a local path).
703 or NT_STATUS_NOT_COVERED meaning return a DFS redirect, or
704 any other NT_STATUS error which is a genuine error to be
705 returned to the client.
706 *****************************************************************/
708 static NTSTATUS dfs_redirect(TALLOC_CTX *ctx,
709 connection_struct *conn,
710 const char *path_in,
711 bool search_wcard_flag,
712 char **pp_path_out,
713 bool *ppath_contains_wcard)
715 NTSTATUS status;
716 struct dfs_path *pdp = TALLOC_P(ctx, struct dfs_path);
718 if (!pdp) {
719 return NT_STATUS_NO_MEMORY;
722 status = parse_dfs_path(conn, path_in, search_wcard_flag, pdp,
723 ppath_contains_wcard);
724 if (!NT_STATUS_IS_OK(status)) {
725 TALLOC_FREE(pdp);
726 return status;
729 if (pdp->reqpath[0] == '\0') {
730 TALLOC_FREE(pdp);
731 *pp_path_out = talloc_strdup(ctx, "");
732 if (!*pp_path_out) {
733 return NT_STATUS_NO_MEMORY;
735 DEBUG(5,("dfs_redirect: self-referral.\n"));
736 return NT_STATUS_OK;
739 /* If dfs pathname for a non-dfs share, convert to tcon-relative
740 path and return OK */
742 if (!lp_msdfs_root(SNUM(conn))) {
743 *pp_path_out = talloc_strdup(ctx, pdp->reqpath);
744 TALLOC_FREE(pdp);
745 if (!*pp_path_out) {
746 return NT_STATUS_NO_MEMORY;
748 return NT_STATUS_OK;
751 /* If it looked like a local path (zero hostname/servicename)
752 * just treat as a tcon-relative path. */
754 if (pdp->hostname[0] == '\0' && pdp->servicename[0] == '\0') {
755 *pp_path_out = talloc_strdup(ctx, pdp->reqpath);
756 TALLOC_FREE(pdp);
757 if (!*pp_path_out) {
758 return NT_STATUS_NO_MEMORY;
760 return NT_STATUS_OK;
763 if (!( strequal(pdp->servicename, lp_servicename(SNUM(conn)))
764 || (strequal(pdp->servicename, HOMES_NAME)
765 && strequal(lp_servicename(SNUM(conn)),
766 conn->session_info->sanitized_username) )) ) {
768 /* The given sharename doesn't match this connection. */
769 TALLOC_FREE(pdp);
771 return NT_STATUS_OBJECT_PATH_NOT_FOUND;
774 status = dfs_path_lookup(ctx, conn, path_in, pdp,
775 search_wcard_flag, NULL, NULL);
776 if (!NT_STATUS_IS_OK(status)) {
777 if (NT_STATUS_EQUAL(status, NT_STATUS_PATH_NOT_COVERED)) {
778 DEBUG(3,("dfs_redirect: Redirecting %s\n", path_in));
779 } else {
780 DEBUG(10,("dfs_redirect: dfs_path_lookup "
781 "failed for %s with %s\n",
782 path_in, nt_errstr(status) ));
784 return status;
787 DEBUG(3,("dfs_redirect: Not redirecting %s.\n", path_in));
789 /* Form non-dfs tcon-relative path */
790 *pp_path_out = talloc_strdup(ctx, pdp->reqpath);
791 TALLOC_FREE(pdp);
792 if (!*pp_path_out) {
793 return NT_STATUS_NO_MEMORY;
796 DEBUG(3,("dfs_redirect: Path %s converted to non-dfs path %s\n",
797 path_in,
798 *pp_path_out));
800 return NT_STATUS_OK;
803 /**********************************************************************
804 Return a self referral.
805 **********************************************************************/
807 static NTSTATUS self_ref(TALLOC_CTX *ctx,
808 const char *dfs_path,
809 struct junction_map *jucn,
810 int *consumedcntp,
811 bool *self_referralp)
813 struct referral *ref;
815 *self_referralp = True;
817 jucn->referral_count = 1;
818 if((ref = TALLOC_ZERO_P(ctx, struct referral)) == NULL) {
819 return NT_STATUS_NO_MEMORY;
822 ref->alternate_path = talloc_strdup(ctx, dfs_path);
823 if (!ref->alternate_path) {
824 return NT_STATUS_NO_MEMORY;
826 ref->proximity = 0;
827 ref->ttl = REFERRAL_TTL;
828 jucn->referral_list = ref;
829 *consumedcntp = strlen(dfs_path);
830 return NT_STATUS_OK;
833 /**********************************************************************
834 Gets valid referrals for a dfs path and fills up the
835 junction_map structure.
836 **********************************************************************/
838 NTSTATUS get_referred_path(TALLOC_CTX *ctx,
839 const char *dfs_path,
840 struct junction_map *jucn,
841 int *consumedcntp,
842 bool *self_referralp)
844 struct connection_struct *conn;
845 char *targetpath = NULL;
846 int snum;
847 NTSTATUS status = NT_STATUS_NOT_FOUND;
848 bool dummy;
849 struct dfs_path *pdp = TALLOC_P(ctx, struct dfs_path);
850 char *oldpath;
852 if (!pdp) {
853 return NT_STATUS_NO_MEMORY;
856 *self_referralp = False;
858 status = parse_dfs_path(NULL, dfs_path, False, pdp, &dummy);
859 if (!NT_STATUS_IS_OK(status)) {
860 return status;
863 jucn->service_name = talloc_strdup(ctx, pdp->servicename);
864 jucn->volume_name = talloc_strdup(ctx, pdp->reqpath);
865 if (!jucn->service_name || !jucn->volume_name) {
866 TALLOC_FREE(pdp);
867 return NT_STATUS_NO_MEMORY;
870 /* Verify the share is a dfs root */
871 snum = lp_servicenumber(jucn->service_name);
872 if(snum < 0) {
873 char *service_name = NULL;
874 if ((snum = find_service(ctx, jucn->service_name, &service_name)) < 0) {
875 return NT_STATUS_NOT_FOUND;
877 if (!service_name) {
878 return NT_STATUS_NO_MEMORY;
880 TALLOC_FREE(jucn->service_name);
881 jucn->service_name = talloc_strdup(ctx, service_name);
882 if (!jucn->service_name) {
883 TALLOC_FREE(pdp);
884 return NT_STATUS_NO_MEMORY;
888 if (!lp_msdfs_root(snum) && (*lp_msdfs_proxy(snum) == '\0')) {
889 DEBUG(3,("get_referred_path: |%s| in dfs path %s is not "
890 "a dfs root.\n",
891 pdp->servicename, dfs_path));
892 TALLOC_FREE(pdp);
893 return NT_STATUS_NOT_FOUND;
897 * Self referrals are tested with a anonymous IPC connection and
898 * a GET_DFS_REFERRAL call to \\server\share. (which means
899 * dp.reqpath[0] points to an empty string). create_conn_struct cd's
900 * into the directory and will fail if it cannot (as the anonymous
901 * user). Cope with this.
904 if (pdp->reqpath[0] == '\0') {
905 char *tmp;
906 struct referral *ref;
908 if (*lp_msdfs_proxy(snum) == '\0') {
909 TALLOC_FREE(pdp);
910 return self_ref(ctx,
911 dfs_path,
912 jucn,
913 consumedcntp,
914 self_referralp);
918 * It's an msdfs proxy share. Redirect to
919 * the configured target share.
922 jucn->referral_count = 1;
923 if ((ref = TALLOC_ZERO_P(ctx, struct referral)) == NULL) {
924 TALLOC_FREE(pdp);
925 return NT_STATUS_NO_MEMORY;
928 if (!(tmp = talloc_strdup(ctx, lp_msdfs_proxy(snum)))) {
929 TALLOC_FREE(pdp);
930 return NT_STATUS_NO_MEMORY;
933 trim_string(tmp, "\\", 0);
935 ref->alternate_path = talloc_asprintf(ctx, "\\%s", tmp);
936 TALLOC_FREE(tmp);
938 if (!ref->alternate_path) {
939 TALLOC_FREE(pdp);
940 return NT_STATUS_NO_MEMORY;
943 if (pdp->reqpath[0] != '\0') {
944 ref->alternate_path = talloc_asprintf_append(
945 ref->alternate_path,
946 "%s",
947 pdp->reqpath);
948 if (!ref->alternate_path) {
949 TALLOC_FREE(pdp);
950 return NT_STATUS_NO_MEMORY;
953 ref->proximity = 0;
954 ref->ttl = REFERRAL_TTL;
955 jucn->referral_list = ref;
956 *consumedcntp = strlen(dfs_path);
957 TALLOC_FREE(pdp);
958 return NT_STATUS_OK;
961 status = create_conn_struct(ctx, &conn, snum, lp_pathname(snum),
962 NULL, &oldpath);
963 if (!NT_STATUS_IS_OK(status)) {
964 TALLOC_FREE(pdp);
965 return status;
968 /* If this is a DFS path dfs_lookup should return
969 * NT_STATUS_PATH_NOT_COVERED. */
971 status = dfs_path_lookup(ctx, conn, dfs_path, pdp,
972 False, consumedcntp, &targetpath);
974 if (!NT_STATUS_EQUAL(status, NT_STATUS_PATH_NOT_COVERED)) {
975 DEBUG(3,("get_referred_path: No valid referrals for path %s\n",
976 dfs_path));
977 if (NT_STATUS_IS_OK(status)) {
979 * We are in an error path here (we
980 * know it's not a DFS path), but
981 * dfs_path_lookup() can return
982 * NT_STATUS_OK. Ensure we always
983 * return a valid error code.
985 * #9588 - ACLs are not inherited to directories
986 * for DFS shares.
988 status = NT_STATUS_NOT_FOUND;
990 goto err_exit;
993 /* We know this is a valid dfs link. Parse the targetpath. */
994 if (!parse_msdfs_symlink(ctx, targetpath,
995 &jucn->referral_list,
996 &jucn->referral_count)) {
997 DEBUG(3,("get_referred_path: failed to parse symlink "
998 "target %s\n", targetpath ));
999 status = NT_STATUS_NOT_FOUND;
1000 goto err_exit;
1003 status = NT_STATUS_OK;
1004 err_exit:
1005 vfs_ChDir(conn, oldpath);
1006 SMB_VFS_DISCONNECT(conn);
1007 conn_free(conn);
1008 TALLOC_FREE(pdp);
1009 return status;
1012 static int setup_ver2_dfs_referral(const char *pathname,
1013 char **ppdata,
1014 struct junction_map *junction,
1015 bool self_referral)
1017 char* pdata = *ppdata;
1019 smb_ucs2_t *uni_requestedpath = NULL;
1020 int uni_reqpathoffset1,uni_reqpathoffset2;
1021 int uni_curroffset;
1022 int requestedpathlen=0;
1023 int offset;
1024 int reply_size = 0;
1025 int i=0;
1027 DEBUG(10,("Setting up version2 referral\nRequested path:\n"));
1029 requestedpathlen = rpcstr_push_talloc(talloc_tos(),
1030 &uni_requestedpath, pathname);
1031 if (uni_requestedpath == NULL || requestedpathlen == 0) {
1032 return -1;
1035 if (DEBUGLVL(10)) {
1036 dump_data(0, (unsigned char *)uni_requestedpath,
1037 requestedpathlen);
1040 DEBUG(10,("ref count = %u\n",junction->referral_count));
1042 uni_reqpathoffset1 = REFERRAL_HEADER_SIZE +
1043 VERSION2_REFERRAL_SIZE * junction->referral_count;
1045 uni_reqpathoffset2 = uni_reqpathoffset1 + requestedpathlen;
1047 uni_curroffset = uni_reqpathoffset2 + requestedpathlen;
1049 reply_size = REFERRAL_HEADER_SIZE +
1050 VERSION2_REFERRAL_SIZE*junction->referral_count +
1051 2 * requestedpathlen;
1052 DEBUG(10,("reply_size: %u\n",reply_size));
1054 /* add up the unicode lengths of all the referral paths */
1055 for(i=0;i<junction->referral_count;i++) {
1056 DEBUG(10,("referral %u : %s\n",
1058 junction->referral_list[i].alternate_path));
1059 reply_size +=
1060 (strlen(junction->referral_list[i].alternate_path)+1)*2;
1063 DEBUG(10,("reply_size = %u\n",reply_size));
1064 /* add the unexplained 0x16 bytes */
1065 reply_size += 0x16;
1067 pdata = (char *)SMB_REALLOC(pdata,reply_size);
1068 if(pdata == NULL) {
1069 DEBUG(0,("Realloc failed!\n"));
1070 return -1;
1072 *ppdata = pdata;
1074 /* copy in the dfs requested paths.. required for offset calculations */
1075 memcpy(pdata+uni_reqpathoffset1,uni_requestedpath,requestedpathlen);
1076 memcpy(pdata+uni_reqpathoffset2,uni_requestedpath,requestedpathlen);
1078 /* create the header */
1079 SSVAL(pdata,0,requestedpathlen - 2); /* UCS2 of path consumed minus
1080 2 byte null */
1081 /* number of referral in this pkt */
1082 SSVAL(pdata,2,junction->referral_count);
1083 if(self_referral) {
1084 SIVAL(pdata,4,DFSREF_REFERRAL_SERVER | DFSREF_STORAGE_SERVER);
1085 } else {
1086 SIVAL(pdata,4,DFSREF_STORAGE_SERVER);
1089 offset = 8;
1090 /* add the referral elements */
1091 for(i=0;i<junction->referral_count;i++) {
1092 struct referral* ref = &junction->referral_list[i];
1093 int unilen;
1095 SSVAL(pdata,offset,2); /* version 2 */
1096 SSVAL(pdata,offset+2,VERSION2_REFERRAL_SIZE);
1097 if(self_referral) {
1098 SSVAL(pdata,offset+4,1);
1099 } else {
1100 SSVAL(pdata,offset+4,0);
1103 /* ref_flags :use path_consumed bytes? */
1104 SSVAL(pdata,offset+6,0);
1105 SIVAL(pdata,offset+8,ref->proximity);
1106 SIVAL(pdata,offset+12,ref->ttl);
1108 SSVAL(pdata,offset+16,uni_reqpathoffset1-offset);
1109 SSVAL(pdata,offset+18,uni_reqpathoffset2-offset);
1110 /* copy referred path into current offset */
1111 unilen = rpcstr_push(pdata+uni_curroffset,
1112 ref->alternate_path,
1113 reply_size - uni_curroffset,
1114 STR_UNICODE);
1116 SSVAL(pdata,offset+20,uni_curroffset-offset);
1118 uni_curroffset += unilen;
1119 offset += VERSION2_REFERRAL_SIZE;
1121 /* add in the unexplained 22 (0x16) bytes at the end */
1122 memset(pdata+uni_curroffset,'\0',0x16);
1123 return reply_size;
1126 static int setup_ver3_dfs_referral(const char *pathname,
1127 char **ppdata,
1128 struct junction_map *junction,
1129 bool self_referral)
1131 char *pdata = *ppdata;
1133 smb_ucs2_t *uni_reqpath = NULL;
1134 int uni_reqpathoffset1, uni_reqpathoffset2;
1135 int uni_curroffset;
1136 int reply_size = 0;
1138 int reqpathlen = 0;
1139 int offset,i=0;
1141 DEBUG(10,("setting up version3 referral\n"));
1143 reqpathlen = rpcstr_push_talloc(talloc_tos(), &uni_reqpath, pathname);
1144 if (uni_reqpath == NULL || reqpathlen == 0) {
1145 return -1;
1148 if (DEBUGLVL(10)) {
1149 dump_data(0, (unsigned char *)uni_reqpath,
1150 reqpathlen);
1153 uni_reqpathoffset1 = REFERRAL_HEADER_SIZE +
1154 VERSION3_REFERRAL_SIZE * junction->referral_count;
1155 uni_reqpathoffset2 = uni_reqpathoffset1 + reqpathlen;
1156 reply_size = uni_curroffset = uni_reqpathoffset2 + reqpathlen;
1158 for(i=0;i<junction->referral_count;i++) {
1159 DEBUG(10,("referral %u : %s\n",
1161 junction->referral_list[i].alternate_path));
1162 reply_size +=
1163 (strlen(junction->referral_list[i].alternate_path)+1)*2;
1166 pdata = (char *)SMB_REALLOC(pdata,reply_size);
1167 if(pdata == NULL) {
1168 DEBUG(0,("version3 referral setup:"
1169 "malloc failed for Realloc!\n"));
1170 return -1;
1172 *ppdata = pdata;
1174 /* create the header */
1175 SSVAL(pdata,0,reqpathlen - 2); /* UCS2 of path consumed minus
1176 2 byte null */
1177 SSVAL(pdata,2,junction->referral_count); /* number of referral */
1178 if(self_referral) {
1179 SIVAL(pdata,4,DFSREF_REFERRAL_SERVER | DFSREF_STORAGE_SERVER);
1180 } else {
1181 SIVAL(pdata,4,DFSREF_STORAGE_SERVER);
1184 /* copy in the reqpaths */
1185 memcpy(pdata+uni_reqpathoffset1,uni_reqpath,reqpathlen);
1186 memcpy(pdata+uni_reqpathoffset2,uni_reqpath,reqpathlen);
1188 offset = 8;
1189 for(i=0;i<junction->referral_count;i++) {
1190 struct referral* ref = &(junction->referral_list[i]);
1191 int unilen;
1193 SSVAL(pdata,offset,3); /* version 3 */
1194 SSVAL(pdata,offset+2,VERSION3_REFERRAL_SIZE);
1195 if(self_referral) {
1196 SSVAL(pdata,offset+4,1);
1197 } else {
1198 SSVAL(pdata,offset+4,0);
1201 /* ref_flags :use path_consumed bytes? */
1202 SSVAL(pdata,offset+6,0);
1203 SIVAL(pdata,offset+8,ref->ttl);
1205 SSVAL(pdata,offset+12,uni_reqpathoffset1-offset);
1206 SSVAL(pdata,offset+14,uni_reqpathoffset2-offset);
1207 /* copy referred path into current offset */
1208 unilen = rpcstr_push(pdata+uni_curroffset,ref->alternate_path,
1209 reply_size - uni_curroffset,
1210 STR_UNICODE | STR_TERMINATE);
1211 SSVAL(pdata,offset+16,uni_curroffset-offset);
1212 /* copy 0x10 bytes of 00's in the ServiceSite GUID */
1213 memset(pdata+offset+18,'\0',16);
1215 uni_curroffset += unilen;
1216 offset += VERSION3_REFERRAL_SIZE;
1218 return reply_size;
1221 /******************************************************************
1222 Set up the DFS referral for the dfs pathname. This call returns
1223 the amount of the path covered by this server, and where the
1224 client should be redirected to. This is the meat of the
1225 TRANS2_GET_DFS_REFERRAL call.
1226 ******************************************************************/
1228 int setup_dfs_referral(connection_struct *orig_conn,
1229 const char *dfs_path,
1230 int max_referral_level,
1231 char **ppdata, NTSTATUS *pstatus)
1233 struct junction_map *junction = NULL;
1234 int consumedcnt = 0;
1235 bool self_referral = False;
1236 int reply_size = 0;
1237 char *pathnamep = NULL;
1238 char *local_dfs_path = NULL;
1239 TALLOC_CTX *ctx;
1241 if (!(ctx=talloc_init("setup_dfs_referral"))) {
1242 *pstatus = NT_STATUS_NO_MEMORY;
1243 return -1;
1246 /* get the junction entry */
1247 if (!dfs_path) {
1248 talloc_destroy(ctx);
1249 *pstatus = NT_STATUS_NOT_FOUND;
1250 return -1;
1254 * Trim pathname sent by client so it begins with only one backslash.
1255 * Two backslashes confuse some dfs clients
1258 local_dfs_path = talloc_strdup(ctx,dfs_path);
1259 if (!local_dfs_path) {
1260 *pstatus = NT_STATUS_NO_MEMORY;
1261 talloc_destroy(ctx);
1262 return -1;
1264 pathnamep = local_dfs_path;
1265 while (IS_DIRECTORY_SEP(pathnamep[0]) &&
1266 IS_DIRECTORY_SEP(pathnamep[1])) {
1267 pathnamep++;
1270 junction = TALLOC_ZERO_P(ctx, struct junction_map);
1271 if (!junction) {
1272 *pstatus = NT_STATUS_NO_MEMORY;
1273 talloc_destroy(ctx);
1274 return -1;
1277 /* The following call can change cwd. */
1278 *pstatus = get_referred_path(ctx, pathnamep, junction,
1279 &consumedcnt, &self_referral);
1280 if (!NT_STATUS_IS_OK(*pstatus)) {
1281 vfs_ChDir(orig_conn,orig_conn->connectpath);
1282 talloc_destroy(ctx);
1283 return -1;
1285 vfs_ChDir(orig_conn,orig_conn->connectpath);
1287 if (!self_referral) {
1288 pathnamep[consumedcnt] = '\0';
1290 if( DEBUGLVL( 3 ) ) {
1291 int i=0;
1292 dbgtext("setup_dfs_referral: Path %s to "
1293 "alternate path(s):",
1294 pathnamep);
1295 for(i=0;i<junction->referral_count;i++)
1296 dbgtext(" %s",
1297 junction->referral_list[i].alternate_path);
1298 dbgtext(".\n");
1302 /* create the referral depeding on version */
1303 DEBUG(10,("max_referral_level :%d\n",max_referral_level));
1305 if (max_referral_level < 2) {
1306 max_referral_level = 2;
1308 if (max_referral_level > 3) {
1309 max_referral_level = 3;
1312 switch(max_referral_level) {
1313 case 2:
1314 reply_size = setup_ver2_dfs_referral(pathnamep,
1315 ppdata, junction,
1316 self_referral);
1317 break;
1318 case 3:
1319 reply_size = setup_ver3_dfs_referral(pathnamep, ppdata,
1320 junction, self_referral);
1321 break;
1322 default:
1323 DEBUG(0,("setup_dfs_referral: Invalid dfs referral "
1324 "version: %d\n",
1325 max_referral_level));
1326 talloc_destroy(ctx);
1327 *pstatus = NT_STATUS_INVALID_LEVEL;
1328 return -1;
1331 if (DEBUGLVL(10)) {
1332 DEBUGADD(0,("DFS Referral pdata:\n"));
1333 dump_data(0,(uint8 *)*ppdata,reply_size);
1336 talloc_destroy(ctx);
1337 *pstatus = NT_STATUS_OK;
1338 return reply_size;
1341 /**********************************************************************
1342 The following functions are called by the NETDFS RPC pipe functions
1343 **********************************************************************/
1345 /*********************************************************************
1346 Creates a junction structure from a DFS pathname
1347 **********************************************************************/
1349 bool create_junction(TALLOC_CTX *ctx,
1350 const char *dfs_path,
1351 struct junction_map *jucn)
1353 int snum;
1354 bool dummy;
1355 struct dfs_path *pdp = TALLOC_P(ctx,struct dfs_path);
1356 NTSTATUS status;
1358 if (!pdp) {
1359 return False;
1361 status = parse_dfs_path(NULL, dfs_path, False, pdp, &dummy);
1362 if (!NT_STATUS_IS_OK(status)) {
1363 return False;
1366 /* check if path is dfs : validate first token */
1367 if (!is_myname_or_ipaddr(pdp->hostname)) {
1368 DEBUG(4,("create_junction: Invalid hostname %s "
1369 "in dfs path %s\n",
1370 pdp->hostname, dfs_path));
1371 TALLOC_FREE(pdp);
1372 return False;
1375 /* Check for a non-DFS share */
1376 snum = lp_servicenumber(pdp->servicename);
1378 if(snum < 0 || !lp_msdfs_root(snum)) {
1379 DEBUG(4,("create_junction: %s is not an msdfs root.\n",
1380 pdp->servicename));
1381 TALLOC_FREE(pdp);
1382 return False;
1385 jucn->service_name = talloc_strdup(ctx, pdp->servicename);
1386 jucn->volume_name = talloc_strdup(ctx, pdp->reqpath);
1387 jucn->comment = talloc_strdup(ctx, lp_comment(snum));
1389 TALLOC_FREE(pdp);
1390 if (!jucn->service_name || !jucn->volume_name || ! jucn->comment) {
1391 return False;
1393 return True;
1396 /**********************************************************************
1397 Forms a valid Unix pathname from the junction
1398 **********************************************************************/
1400 static bool junction_to_local_path(const struct junction_map *jucn,
1401 char **pp_path_out,
1402 connection_struct **conn_out,
1403 char **oldpath)
1405 int snum;
1406 NTSTATUS status;
1408 snum = lp_servicenumber(jucn->service_name);
1409 if(snum < 0) {
1410 return False;
1412 status = create_conn_struct(talloc_tos(), conn_out, snum,
1413 lp_pathname(snum), NULL, oldpath);
1414 if (!NT_STATUS_IS_OK(status)) {
1415 return False;
1418 *pp_path_out = talloc_asprintf(*conn_out,
1419 "%s/%s",
1420 lp_pathname(snum),
1421 jucn->volume_name);
1422 if (!*pp_path_out) {
1423 vfs_ChDir(*conn_out, *oldpath);
1424 SMB_VFS_DISCONNECT(*conn_out);
1425 conn_free(*conn_out);
1426 return False;
1428 return True;
1431 bool create_msdfs_link(const struct junction_map *jucn)
1433 char *path = NULL;
1434 char *cwd;
1435 char *msdfs_link = NULL;
1436 connection_struct *conn;
1437 int i=0;
1438 bool insert_comma = False;
1439 bool ret = False;
1441 if(!junction_to_local_path(jucn, &path, &conn, &cwd)) {
1442 return False;
1445 /* Form the msdfs_link contents */
1446 msdfs_link = talloc_strdup(conn, "msdfs:");
1447 if (!msdfs_link) {
1448 goto out;
1450 for(i=0; i<jucn->referral_count; i++) {
1451 char *refpath = jucn->referral_list[i].alternate_path;
1453 /* Alternate paths always use Windows separators. */
1454 trim_char(refpath, '\\', '\\');
1455 if(*refpath == '\0') {
1456 if (i == 0) {
1457 insert_comma = False;
1459 continue;
1461 if (i > 0 && insert_comma) {
1462 msdfs_link = talloc_asprintf_append_buffer(msdfs_link,
1463 ",%s",
1464 refpath);
1465 } else {
1466 msdfs_link = talloc_asprintf_append_buffer(msdfs_link,
1467 "%s",
1468 refpath);
1471 if (!msdfs_link) {
1472 goto out;
1474 if (!insert_comma) {
1475 insert_comma = True;
1479 DEBUG(5,("create_msdfs_link: Creating new msdfs link: %s -> %s\n",
1480 path, msdfs_link));
1482 if(SMB_VFS_SYMLINK(conn, msdfs_link, path) < 0) {
1483 if (errno == EEXIST) {
1484 struct smb_filename *smb_fname = NULL;
1485 NTSTATUS status;
1487 status = create_synthetic_smb_fname(talloc_tos(), path,
1488 NULL, NULL,
1489 &smb_fname);
1490 if (!NT_STATUS_IS_OK(status)) {
1491 errno = map_errno_from_nt_status(status);
1492 goto out;
1495 if(SMB_VFS_UNLINK(conn, smb_fname)!=0) {
1496 TALLOC_FREE(smb_fname);
1497 goto out;
1499 TALLOC_FREE(smb_fname);
1501 if (SMB_VFS_SYMLINK(conn, msdfs_link, path) < 0) {
1502 DEBUG(1,("create_msdfs_link: symlink failed "
1503 "%s -> %s\nError: %s\n",
1504 path, msdfs_link, strerror(errno)));
1505 goto out;
1509 ret = True;
1511 out:
1512 vfs_ChDir(conn, cwd);
1513 SMB_VFS_DISCONNECT(conn);
1514 conn_free(conn);
1515 return ret;
1518 bool remove_msdfs_link(const struct junction_map *jucn)
1520 char *path = NULL;
1521 char *cwd;
1522 connection_struct *conn;
1523 bool ret = False;
1524 struct smb_filename *smb_fname = NULL;
1525 NTSTATUS status;
1527 if (!junction_to_local_path(jucn, &path, &conn, &cwd)) {
1528 return false;
1531 status = create_synthetic_smb_fname(talloc_tos(), path,
1532 NULL, NULL,
1533 &smb_fname);
1534 if (!NT_STATUS_IS_OK(status)) {
1535 errno = map_errno_from_nt_status(status);
1536 return false;
1539 if( SMB_VFS_UNLINK(conn, smb_fname) == 0 ) {
1540 ret = True;
1543 TALLOC_FREE(smb_fname);
1544 vfs_ChDir(conn, cwd);
1545 SMB_VFS_DISCONNECT(conn);
1546 conn_free(conn);
1547 return ret;
1550 /*********************************************************************
1551 Return the number of DFS links at the root of this share.
1552 *********************************************************************/
1554 static int count_dfs_links(TALLOC_CTX *ctx, int snum)
1556 size_t cnt = 0;
1557 SMB_STRUCT_DIR *dirp = NULL;
1558 const char *dname = NULL;
1559 char *talloced = NULL;
1560 const char *connect_path = lp_pathname(snum);
1561 const char *msdfs_proxy = lp_msdfs_proxy(snum);
1562 connection_struct *conn;
1563 NTSTATUS status;
1564 char *cwd;
1566 if(*connect_path == '\0') {
1567 return 0;
1571 * Fake up a connection struct for the VFS layer.
1574 status = create_conn_struct(talloc_tos(), &conn, snum, connect_path,
1575 NULL, &cwd);
1576 if (!NT_STATUS_IS_OK(status)) {
1577 DEBUG(3, ("create_conn_struct failed: %s\n",
1578 nt_errstr(status)));
1579 return 0;
1582 /* Count a link for the msdfs root - convention */
1583 cnt = 1;
1585 /* No more links if this is an msdfs proxy. */
1586 if (*msdfs_proxy != '\0') {
1587 goto out;
1590 /* Now enumerate all dfs links */
1591 dirp = SMB_VFS_OPENDIR(conn, ".", NULL, 0);
1592 if(!dirp) {
1593 goto out;
1596 while ((dname = vfs_readdirname(conn, dirp, NULL, &talloced))
1597 != NULL) {
1598 if (is_msdfs_link(conn,
1599 dname,
1600 NULL)) {
1601 cnt++;
1603 TALLOC_FREE(talloced);
1606 SMB_VFS_CLOSEDIR(conn,dirp);
1608 out:
1609 vfs_ChDir(conn, cwd);
1610 SMB_VFS_DISCONNECT(conn);
1611 conn_free(conn);
1612 return cnt;
1615 /*********************************************************************
1616 *********************************************************************/
1618 static int form_junctions(TALLOC_CTX *ctx,
1619 int snum,
1620 struct junction_map *jucn,
1621 size_t jn_remain)
1623 size_t cnt = 0;
1624 SMB_STRUCT_DIR *dirp = NULL;
1625 const char *dname = NULL;
1626 char *talloced = NULL;
1627 const char *connect_path = lp_pathname(snum);
1628 char *service_name = lp_servicename(snum);
1629 const char *msdfs_proxy = lp_msdfs_proxy(snum);
1630 connection_struct *conn;
1631 struct referral *ref = NULL;
1632 char *cwd;
1633 NTSTATUS status;
1635 if (jn_remain == 0) {
1636 return 0;
1639 if(*connect_path == '\0') {
1640 return 0;
1644 * Fake up a connection struct for the VFS layer.
1647 status = create_conn_struct(ctx, &conn, snum, connect_path, NULL,
1648 &cwd);
1649 if (!NT_STATUS_IS_OK(status)) {
1650 DEBUG(3, ("create_conn_struct failed: %s\n",
1651 nt_errstr(status)));
1652 return 0;
1655 /* form a junction for the msdfs root - convention
1656 DO NOT REMOVE THIS: NT clients will not work with us
1657 if this is not present
1659 jucn[cnt].service_name = talloc_strdup(ctx,service_name);
1660 jucn[cnt].volume_name = talloc_strdup(ctx, "");
1661 if (!jucn[cnt].service_name || !jucn[cnt].volume_name) {
1662 goto out;
1664 jucn[cnt].comment = "";
1665 jucn[cnt].referral_count = 1;
1667 ref = jucn[cnt].referral_list = TALLOC_ZERO_P(ctx, struct referral);
1668 if (jucn[cnt].referral_list == NULL) {
1669 goto out;
1672 ref->proximity = 0;
1673 ref->ttl = REFERRAL_TTL;
1674 if (*msdfs_proxy != '\0') {
1675 ref->alternate_path = talloc_strdup(ctx,
1676 msdfs_proxy);
1677 } else {
1678 ref->alternate_path = talloc_asprintf(ctx,
1679 "\\\\%s\\%s",
1680 get_local_machine_name(),
1681 service_name);
1684 if (!ref->alternate_path) {
1685 goto out;
1687 cnt++;
1689 /* Don't enumerate if we're an msdfs proxy. */
1690 if (*msdfs_proxy != '\0') {
1691 goto out;
1694 /* Now enumerate all dfs links */
1695 dirp = SMB_VFS_OPENDIR(conn, ".", NULL, 0);
1696 if(!dirp) {
1697 goto out;
1700 while ((dname = vfs_readdirname(conn, dirp, NULL, &talloced))
1701 != NULL) {
1702 char *link_target = NULL;
1703 if (cnt >= jn_remain) {
1704 DEBUG(2, ("form_junctions: ran out of MSDFS "
1705 "junction slots"));
1706 TALLOC_FREE(talloced);
1707 goto out;
1709 if (is_msdfs_link_internal(ctx,
1710 conn,
1711 dname, &link_target,
1712 NULL)) {
1713 if (parse_msdfs_symlink(ctx,
1714 link_target,
1715 &jucn[cnt].referral_list,
1716 &jucn[cnt].referral_count)) {
1718 jucn[cnt].service_name = talloc_strdup(ctx,
1719 service_name);
1720 jucn[cnt].volume_name = talloc_strdup(ctx,
1721 dname);
1722 if (!jucn[cnt].service_name ||
1723 !jucn[cnt].volume_name) {
1724 TALLOC_FREE(talloced);
1725 goto out;
1727 jucn[cnt].comment = "";
1728 cnt++;
1730 TALLOC_FREE(link_target);
1732 TALLOC_FREE(talloced);
1735 out:
1737 if (dirp) {
1738 SMB_VFS_CLOSEDIR(conn,dirp);
1741 vfs_ChDir(conn, cwd);
1742 conn_free(conn);
1743 return cnt;
1746 struct junction_map *enum_msdfs_links(TALLOC_CTX *ctx, size_t *p_num_jn)
1748 struct junction_map *jn = NULL;
1749 int i=0;
1750 size_t jn_count = 0;
1751 int sharecount = 0;
1753 *p_num_jn = 0;
1754 if(!lp_host_msdfs()) {
1755 return NULL;
1758 /* Ensure all the usershares are loaded. */
1759 become_root();
1760 load_registry_shares();
1761 sharecount = load_usershare_shares();
1762 unbecome_root();
1764 for(i=0;i < sharecount;i++) {
1765 if(lp_msdfs_root(i)) {
1766 jn_count += count_dfs_links(ctx, i);
1769 if (jn_count == 0) {
1770 return NULL;
1772 jn = TALLOC_ARRAY(ctx, struct junction_map, jn_count);
1773 if (!jn) {
1774 return NULL;
1776 for(i=0; i < sharecount; i++) {
1777 if (*p_num_jn >= jn_count) {
1778 break;
1780 if(lp_msdfs_root(i)) {
1781 *p_num_jn += form_junctions(ctx, i,
1782 &jn[*p_num_jn],
1783 jn_count - *p_num_jn);
1786 return jn;
1789 /******************************************************************************
1790 Core function to resolve a dfs pathname possibly containing a wildcard. If
1791 ppath_contains_wcard != NULL, it will be set to true if a wildcard is
1792 detected during dfs resolution.
1793 ******************************************************************************/
1795 NTSTATUS resolve_dfspath_wcard(TALLOC_CTX *ctx,
1796 connection_struct *conn,
1797 bool dfs_pathnames,
1798 const char *name_in,
1799 bool allow_wcards,
1800 char **pp_name_out,
1801 bool *ppath_contains_wcard)
1803 bool path_contains_wcard;
1804 NTSTATUS status = NT_STATUS_OK;
1806 if (dfs_pathnames) {
1807 status = dfs_redirect(ctx,
1808 conn,
1809 name_in,
1810 allow_wcards,
1811 pp_name_out,
1812 &path_contains_wcard);
1814 if (NT_STATUS_IS_OK(status) && ppath_contains_wcard != NULL) {
1815 *ppath_contains_wcard = path_contains_wcard;
1817 } else {
1819 * Cheat and just return a copy of the in ptr.
1820 * Once srvstr_get_path() uses talloc it'll
1821 * be a talloced ptr anyway.
1823 *pp_name_out = CONST_DISCARD(char *,name_in);
1825 return status;