s3: Make is_executable() available in lib/
[Samba.git] / source3 / smbd / msdfs.c
blob3bdedb8651eaf5d2cc365fbc5a85cc15fb223bca
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 can
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.
582 * If we needed to remember what we'd resolved in
583 * dp->reqpath (as the original code did) we'd
584 * copy (localhost, dp->reqpath) on any code
585 * path below that returns True - but I don't
586 * think this is needed. JRA.
589 status = unix_convert(ctx, conn, pdp->reqpath, &smb_fname,
590 search_flag ? UCF_ALWAYS_ALLOW_WCARD_LCOMP : 0);
592 if (!NT_STATUS_IS_OK(status)) {
593 if (!NT_STATUS_EQUAL(status,
594 NT_STATUS_OBJECT_PATH_NOT_FOUND)) {
595 return status;
598 /* Create an smb_fname to use below. */
599 status = create_synthetic_smb_fname(ctx, pdp->reqpath, NULL,
600 NULL, &smb_fname);
601 if (!NT_STATUS_IS_OK(status)) {
602 return status;
606 /* Optimization - check if we can redirect the whole path. */
608 if (is_msdfs_link_internal(ctx, conn, smb_fname->base_name,
609 pp_targetpath, NULL)) {
610 if (search_flag) {
611 DEBUG(6,("dfs_path_lookup (FindFirst) No redirection "
612 "for dfs link %s.\n", dfspath));
613 status = NT_STATUS_OK;
614 goto out;
617 DEBUG(6,("dfs_path_lookup: %s resolves to a "
618 "valid dfs link %s.\n", dfspath,
619 pp_targetpath ? *pp_targetpath : ""));
621 if (consumedcntp) {
622 *consumedcntp = strlen(dfspath);
624 status = NT_STATUS_PATH_NOT_COVERED;
625 goto out;
628 /* Prepare to test only for '/' components in the given path,
629 * so if a Windows path replace all '\\' characters with '/'.
630 * For a POSIX DFS path we know all separators are already '/'. */
632 canon_dfspath = talloc_strdup(ctx, dfspath);
633 if (!canon_dfspath) {
634 status = NT_STATUS_NO_MEMORY;
635 goto out;
637 if (!pdp->posix_path) {
638 string_replace(canon_dfspath, '\\', '/');
642 * localpath comes out of unix_convert, so it has
643 * no trailing backslash. Make sure that canon_dfspath hasn't either.
644 * Fix for bug #4860 from Jan Martin <Jan.Martin@rwedea.com>.
647 trim_char(canon_dfspath,0,'/');
650 * Redirect if any component in the path is a link.
651 * We do this by walking backwards through the
652 * local path, chopping off the last component
653 * in both the local path and the canonicalized
654 * DFS path. If we hit a DFS link then we're done.
657 p = strrchr_m(smb_fname->base_name, '/');
658 if (consumedcntp) {
659 q = strrchr_m(canon_dfspath, '/');
662 while (p) {
663 *p = '\0';
664 if (q) {
665 *q = '\0';
668 if (is_msdfs_link_internal(ctx, conn,
669 smb_fname->base_name, pp_targetpath,
670 NULL)) {
671 DEBUG(4, ("dfs_path_lookup: Redirecting %s because "
672 "parent %s is dfs link\n", dfspath,
673 smb_fname_str_dbg(smb_fname)));
675 if (consumedcntp) {
676 *consumedcntp = strlen(canon_dfspath);
677 DEBUG(10, ("dfs_path_lookup: Path consumed: %s "
678 "(%d)\n",
679 canon_dfspath,
680 *consumedcntp));
683 status = NT_STATUS_PATH_NOT_COVERED;
684 goto out;
687 /* Step back on the filesystem. */
688 p = strrchr_m(smb_fname->base_name, '/');
690 if (consumedcntp) {
691 /* And in the canonicalized dfs path. */
692 q = strrchr_m(canon_dfspath, '/');
696 status = NT_STATUS_OK;
697 out:
698 TALLOC_FREE(smb_fname);
699 return status;
702 /*****************************************************************
703 Decides if a dfs pathname should be redirected or not.
704 If not, the pathname is converted to a tcon-relative local unix path
706 search_wcard_flag: this flag performs 2 functions both related
707 to searches. See resolve_dfs_path() and parse_dfs_path_XX()
708 for details.
710 This function can return NT_STATUS_OK, meaning use the returned path as-is
711 (mapped into a local path).
712 or NT_STATUS_NOT_COVERED meaning return a DFS redirect, or
713 any other NT_STATUS error which is a genuine error to be
714 returned to the client.
715 *****************************************************************/
717 static NTSTATUS dfs_redirect(TALLOC_CTX *ctx,
718 connection_struct *conn,
719 const char *path_in,
720 bool search_wcard_flag,
721 char **pp_path_out,
722 bool *ppath_contains_wcard)
724 NTSTATUS status;
725 struct dfs_path *pdp = TALLOC_P(ctx, struct dfs_path);
727 if (!pdp) {
728 return NT_STATUS_NO_MEMORY;
731 status = parse_dfs_path(conn, path_in, search_wcard_flag, pdp,
732 ppath_contains_wcard);
733 if (!NT_STATUS_IS_OK(status)) {
734 TALLOC_FREE(pdp);
735 return status;
738 if (pdp->reqpath[0] == '\0') {
739 TALLOC_FREE(pdp);
740 *pp_path_out = talloc_strdup(ctx, "");
741 if (!*pp_path_out) {
742 return NT_STATUS_NO_MEMORY;
744 DEBUG(5,("dfs_redirect: self-referral.\n"));
745 return NT_STATUS_OK;
748 /* If dfs pathname for a non-dfs share, convert to tcon-relative
749 path and return OK */
751 if (!lp_msdfs_root(SNUM(conn))) {
752 *pp_path_out = talloc_strdup(ctx, pdp->reqpath);
753 TALLOC_FREE(pdp);
754 if (!*pp_path_out) {
755 return NT_STATUS_NO_MEMORY;
757 return NT_STATUS_OK;
760 /* If it looked like a local path (zero hostname/servicename)
761 * just treat as a tcon-relative path. */
763 if (pdp->hostname[0] == '\0' && pdp->servicename[0] == '\0') {
764 *pp_path_out = talloc_strdup(ctx, pdp->reqpath);
765 TALLOC_FREE(pdp);
766 if (!*pp_path_out) {
767 return NT_STATUS_NO_MEMORY;
769 return NT_STATUS_OK;
772 if (!( strequal(pdp->servicename, lp_servicename(SNUM(conn)))
773 || (strequal(pdp->servicename, HOMES_NAME)
774 && strequal(lp_servicename(SNUM(conn)),
775 conn->session_info->sanitized_username) )) ) {
777 /* The given sharename doesn't match this connection. */
778 TALLOC_FREE(pdp);
780 return NT_STATUS_OBJECT_PATH_NOT_FOUND;
783 status = dfs_path_lookup(ctx, conn, path_in, pdp,
784 search_wcard_flag, NULL, NULL);
785 if (!NT_STATUS_IS_OK(status)) {
786 if (NT_STATUS_EQUAL(status, NT_STATUS_PATH_NOT_COVERED)) {
787 DEBUG(3,("dfs_redirect: Redirecting %s\n", path_in));
788 } else {
789 DEBUG(10,("dfs_redirect: dfs_path_lookup "
790 "failed for %s with %s\n",
791 path_in, nt_errstr(status) ));
793 return status;
796 DEBUG(3,("dfs_redirect: Not redirecting %s.\n", path_in));
798 /* Form non-dfs tcon-relative path */
799 *pp_path_out = talloc_strdup(ctx, pdp->reqpath);
800 TALLOC_FREE(pdp);
801 if (!*pp_path_out) {
802 return NT_STATUS_NO_MEMORY;
805 DEBUG(3,("dfs_redirect: Path %s converted to non-dfs path %s\n",
806 path_in,
807 *pp_path_out));
809 return NT_STATUS_OK;
812 /**********************************************************************
813 Return a self referral.
814 **********************************************************************/
816 static NTSTATUS self_ref(TALLOC_CTX *ctx,
817 const char *dfs_path,
818 struct junction_map *jucn,
819 int *consumedcntp,
820 bool *self_referralp)
822 struct referral *ref;
824 *self_referralp = True;
826 jucn->referral_count = 1;
827 if((ref = TALLOC_ZERO_P(ctx, struct referral)) == NULL) {
828 return NT_STATUS_NO_MEMORY;
831 ref->alternate_path = talloc_strdup(ctx, dfs_path);
832 if (!ref->alternate_path) {
833 return NT_STATUS_NO_MEMORY;
835 ref->proximity = 0;
836 ref->ttl = REFERRAL_TTL;
837 jucn->referral_list = ref;
838 *consumedcntp = strlen(dfs_path);
839 return NT_STATUS_OK;
842 /**********************************************************************
843 Gets valid referrals for a dfs path and fills up the
844 junction_map structure.
845 **********************************************************************/
847 NTSTATUS get_referred_path(TALLOC_CTX *ctx,
848 const char *dfs_path,
849 struct junction_map *jucn,
850 int *consumedcntp,
851 bool *self_referralp)
853 struct connection_struct *conn;
854 char *targetpath = NULL;
855 int snum;
856 NTSTATUS status = NT_STATUS_NOT_FOUND;
857 bool dummy;
858 struct dfs_path *pdp = TALLOC_P(ctx, struct dfs_path);
859 char *oldpath;
861 if (!pdp) {
862 return NT_STATUS_NO_MEMORY;
865 *self_referralp = False;
867 status = parse_dfs_path(NULL, dfs_path, False, pdp, &dummy);
868 if (!NT_STATUS_IS_OK(status)) {
869 return status;
872 jucn->service_name = talloc_strdup(ctx, pdp->servicename);
873 jucn->volume_name = talloc_strdup(ctx, pdp->reqpath);
874 if (!jucn->service_name || !jucn->volume_name) {
875 TALLOC_FREE(pdp);
876 return NT_STATUS_NO_MEMORY;
879 /* Verify the share is a dfs root */
880 snum = lp_servicenumber(jucn->service_name);
881 if(snum < 0) {
882 char *service_name = NULL;
883 if ((snum = find_service(ctx, jucn->service_name, &service_name)) < 0) {
884 return NT_STATUS_NOT_FOUND;
886 if (!service_name) {
887 return NT_STATUS_NO_MEMORY;
889 TALLOC_FREE(jucn->service_name);
890 jucn->service_name = talloc_strdup(ctx, service_name);
891 if (!jucn->service_name) {
892 TALLOC_FREE(pdp);
893 return NT_STATUS_NO_MEMORY;
897 if (!lp_msdfs_root(snum) && (*lp_msdfs_proxy(snum) == '\0')) {
898 DEBUG(3,("get_referred_path: |%s| in dfs path %s is not "
899 "a dfs root.\n",
900 pdp->servicename, dfs_path));
901 TALLOC_FREE(pdp);
902 return NT_STATUS_NOT_FOUND;
906 * Self referrals are tested with a anonymous IPC connection and
907 * a GET_DFS_REFERRAL call to \\server\share. (which means
908 * dp.reqpath[0] points to an empty string). create_conn_struct cd's
909 * into the directory and will fail if it cannot (as the anonymous
910 * user). Cope with this.
913 if (pdp->reqpath[0] == '\0') {
914 char *tmp;
915 struct referral *ref;
917 if (*lp_msdfs_proxy(snum) == '\0') {
918 TALLOC_FREE(pdp);
919 return self_ref(ctx,
920 dfs_path,
921 jucn,
922 consumedcntp,
923 self_referralp);
927 * It's an msdfs proxy share. Redirect to
928 * the configured target share.
931 jucn->referral_count = 1;
932 if ((ref = TALLOC_ZERO_P(ctx, struct referral)) == NULL) {
933 TALLOC_FREE(pdp);
934 return NT_STATUS_NO_MEMORY;
937 if (!(tmp = talloc_strdup(ctx, lp_msdfs_proxy(snum)))) {
938 TALLOC_FREE(pdp);
939 return NT_STATUS_NO_MEMORY;
942 trim_string(tmp, "\\", 0);
944 ref->alternate_path = talloc_asprintf(ctx, "\\%s", tmp);
945 TALLOC_FREE(tmp);
947 if (!ref->alternate_path) {
948 TALLOC_FREE(pdp);
949 return NT_STATUS_NO_MEMORY;
952 if (pdp->reqpath[0] != '\0') {
953 ref->alternate_path = talloc_asprintf_append(
954 ref->alternate_path,
955 "%s",
956 pdp->reqpath);
957 if (!ref->alternate_path) {
958 TALLOC_FREE(pdp);
959 return NT_STATUS_NO_MEMORY;
962 ref->proximity = 0;
963 ref->ttl = REFERRAL_TTL;
964 jucn->referral_list = ref;
965 *consumedcntp = strlen(dfs_path);
966 TALLOC_FREE(pdp);
967 return NT_STATUS_OK;
970 status = create_conn_struct(ctx, &conn, snum, lp_pathname(snum),
971 NULL, &oldpath);
972 if (!NT_STATUS_IS_OK(status)) {
973 TALLOC_FREE(pdp);
974 return status;
977 /* If this is a DFS path dfs_lookup should return
978 * NT_STATUS_PATH_NOT_COVERED. */
980 status = dfs_path_lookup(ctx, conn, dfs_path, pdp,
981 False, consumedcntp, &targetpath);
983 if (!NT_STATUS_EQUAL(status, NT_STATUS_PATH_NOT_COVERED)) {
984 DEBUG(3,("get_referred_path: No valid referrals for path %s\n",
985 dfs_path));
986 goto err_exit;
989 /* We know this is a valid dfs link. Parse the targetpath. */
990 if (!parse_msdfs_symlink(ctx, targetpath,
991 &jucn->referral_list,
992 &jucn->referral_count)) {
993 DEBUG(3,("get_referred_path: failed to parse symlink "
994 "target %s\n", targetpath ));
995 status = NT_STATUS_NOT_FOUND;
996 goto err_exit;
999 status = NT_STATUS_OK;
1000 err_exit:
1001 vfs_ChDir(conn, oldpath);
1002 SMB_VFS_DISCONNECT(conn);
1003 conn_free(conn);
1004 TALLOC_FREE(pdp);
1005 return status;
1008 static int setup_ver2_dfs_referral(const char *pathname,
1009 char **ppdata,
1010 struct junction_map *junction,
1011 bool self_referral)
1013 char* pdata = *ppdata;
1015 smb_ucs2_t *uni_requestedpath = NULL;
1016 int uni_reqpathoffset1,uni_reqpathoffset2;
1017 int uni_curroffset;
1018 int requestedpathlen=0;
1019 int offset;
1020 int reply_size = 0;
1021 int i=0;
1023 DEBUG(10,("Setting up version2 referral\nRequested path:\n"));
1025 requestedpathlen = rpcstr_push_talloc(talloc_tos(),
1026 &uni_requestedpath, pathname);
1027 if (uni_requestedpath == NULL || requestedpathlen == 0) {
1028 return -1;
1031 if (DEBUGLVL(10)) {
1032 dump_data(0, (unsigned char *)uni_requestedpath,
1033 requestedpathlen);
1036 DEBUG(10,("ref count = %u\n",junction->referral_count));
1038 uni_reqpathoffset1 = REFERRAL_HEADER_SIZE +
1039 VERSION2_REFERRAL_SIZE * junction->referral_count;
1041 uni_reqpathoffset2 = uni_reqpathoffset1 + requestedpathlen;
1043 uni_curroffset = uni_reqpathoffset2 + requestedpathlen;
1045 reply_size = REFERRAL_HEADER_SIZE +
1046 VERSION2_REFERRAL_SIZE*junction->referral_count +
1047 2 * requestedpathlen;
1048 DEBUG(10,("reply_size: %u\n",reply_size));
1050 /* add up the unicode lengths of all the referral paths */
1051 for(i=0;i<junction->referral_count;i++) {
1052 DEBUG(10,("referral %u : %s\n",
1054 junction->referral_list[i].alternate_path));
1055 reply_size +=
1056 (strlen(junction->referral_list[i].alternate_path)+1)*2;
1059 DEBUG(10,("reply_size = %u\n",reply_size));
1060 /* add the unexplained 0x16 bytes */
1061 reply_size += 0x16;
1063 pdata = (char *)SMB_REALLOC(pdata,reply_size);
1064 if(pdata == NULL) {
1065 DEBUG(0,("Realloc failed!\n"));
1066 return -1;
1068 *ppdata = pdata;
1070 /* copy in the dfs requested paths.. required for offset calculations */
1071 memcpy(pdata+uni_reqpathoffset1,uni_requestedpath,requestedpathlen);
1072 memcpy(pdata+uni_reqpathoffset2,uni_requestedpath,requestedpathlen);
1074 /* create the header */
1075 SSVAL(pdata,0,requestedpathlen - 2); /* UCS2 of path consumed minus
1076 2 byte null */
1077 /* number of referral in this pkt */
1078 SSVAL(pdata,2,junction->referral_count);
1079 if(self_referral) {
1080 SIVAL(pdata,4,DFSREF_REFERRAL_SERVER | DFSREF_STORAGE_SERVER);
1081 } else {
1082 SIVAL(pdata,4,DFSREF_STORAGE_SERVER);
1085 offset = 8;
1086 /* add the referral elements */
1087 for(i=0;i<junction->referral_count;i++) {
1088 struct referral* ref = &junction->referral_list[i];
1089 int unilen;
1091 SSVAL(pdata,offset,2); /* version 2 */
1092 SSVAL(pdata,offset+2,VERSION2_REFERRAL_SIZE);
1093 if(self_referral) {
1094 SSVAL(pdata,offset+4,1);
1095 } else {
1096 SSVAL(pdata,offset+4,0);
1099 /* ref_flags :use path_consumed bytes? */
1100 SSVAL(pdata,offset+6,0);
1101 SIVAL(pdata,offset+8,ref->proximity);
1102 SIVAL(pdata,offset+12,ref->ttl);
1104 SSVAL(pdata,offset+16,uni_reqpathoffset1-offset);
1105 SSVAL(pdata,offset+18,uni_reqpathoffset2-offset);
1106 /* copy referred path into current offset */
1107 unilen = rpcstr_push(pdata+uni_curroffset,
1108 ref->alternate_path,
1109 reply_size - uni_curroffset,
1110 STR_UNICODE);
1112 SSVAL(pdata,offset+20,uni_curroffset-offset);
1114 uni_curroffset += unilen;
1115 offset += VERSION2_REFERRAL_SIZE;
1117 /* add in the unexplained 22 (0x16) bytes at the end */
1118 memset(pdata+uni_curroffset,'\0',0x16);
1119 return reply_size;
1122 static int setup_ver3_dfs_referral(const char *pathname,
1123 char **ppdata,
1124 struct junction_map *junction,
1125 bool self_referral)
1127 char *pdata = *ppdata;
1129 smb_ucs2_t *uni_reqpath = NULL;
1130 int uni_reqpathoffset1, uni_reqpathoffset2;
1131 int uni_curroffset;
1132 int reply_size = 0;
1134 int reqpathlen = 0;
1135 int offset,i=0;
1137 DEBUG(10,("setting up version3 referral\n"));
1139 reqpathlen = rpcstr_push_talloc(talloc_tos(), &uni_reqpath, pathname);
1140 if (uni_reqpath == NULL || reqpathlen == 0) {
1141 return -1;
1144 if (DEBUGLVL(10)) {
1145 dump_data(0, (unsigned char *)uni_reqpath,
1146 reqpathlen);
1149 uni_reqpathoffset1 = REFERRAL_HEADER_SIZE +
1150 VERSION3_REFERRAL_SIZE * junction->referral_count;
1151 uni_reqpathoffset2 = uni_reqpathoffset1 + reqpathlen;
1152 reply_size = uni_curroffset = uni_reqpathoffset2 + reqpathlen;
1154 for(i=0;i<junction->referral_count;i++) {
1155 DEBUG(10,("referral %u : %s\n",
1157 junction->referral_list[i].alternate_path));
1158 reply_size +=
1159 (strlen(junction->referral_list[i].alternate_path)+1)*2;
1162 pdata = (char *)SMB_REALLOC(pdata,reply_size);
1163 if(pdata == NULL) {
1164 DEBUG(0,("version3 referral setup:"
1165 "malloc failed for Realloc!\n"));
1166 return -1;
1168 *ppdata = pdata;
1170 /* create the header */
1171 SSVAL(pdata,0,reqpathlen - 2); /* UCS2 of path consumed minus
1172 2 byte null */
1173 SSVAL(pdata,2,junction->referral_count); /* number of referral */
1174 if(self_referral) {
1175 SIVAL(pdata,4,DFSREF_REFERRAL_SERVER | DFSREF_STORAGE_SERVER);
1176 } else {
1177 SIVAL(pdata,4,DFSREF_STORAGE_SERVER);
1180 /* copy in the reqpaths */
1181 memcpy(pdata+uni_reqpathoffset1,uni_reqpath,reqpathlen);
1182 memcpy(pdata+uni_reqpathoffset2,uni_reqpath,reqpathlen);
1184 offset = 8;
1185 for(i=0;i<junction->referral_count;i++) {
1186 struct referral* ref = &(junction->referral_list[i]);
1187 int unilen;
1189 SSVAL(pdata,offset,3); /* version 3 */
1190 SSVAL(pdata,offset+2,VERSION3_REFERRAL_SIZE);
1191 if(self_referral) {
1192 SSVAL(pdata,offset+4,1);
1193 } else {
1194 SSVAL(pdata,offset+4,0);
1197 /* ref_flags :use path_consumed bytes? */
1198 SSVAL(pdata,offset+6,0);
1199 SIVAL(pdata,offset+8,ref->ttl);
1201 SSVAL(pdata,offset+12,uni_reqpathoffset1-offset);
1202 SSVAL(pdata,offset+14,uni_reqpathoffset2-offset);
1203 /* copy referred path into current offset */
1204 unilen = rpcstr_push(pdata+uni_curroffset,ref->alternate_path,
1205 reply_size - uni_curroffset,
1206 STR_UNICODE | STR_TERMINATE);
1207 SSVAL(pdata,offset+16,uni_curroffset-offset);
1208 /* copy 0x10 bytes of 00's in the ServiceSite GUID */
1209 memset(pdata+offset+18,'\0',16);
1211 uni_curroffset += unilen;
1212 offset += VERSION3_REFERRAL_SIZE;
1214 return reply_size;
1217 /******************************************************************
1218 Set up the DFS referral for the dfs pathname. This call returns
1219 the amount of the path covered by this server, and where the
1220 client should be redirected to. This is the meat of the
1221 TRANS2_GET_DFS_REFERRAL call.
1222 ******************************************************************/
1224 int setup_dfs_referral(connection_struct *orig_conn,
1225 const char *dfs_path,
1226 int max_referral_level,
1227 char **ppdata, NTSTATUS *pstatus)
1229 struct junction_map *junction = NULL;
1230 int consumedcnt = 0;
1231 bool self_referral = False;
1232 int reply_size = 0;
1233 char *pathnamep = NULL;
1234 char *local_dfs_path = NULL;
1235 TALLOC_CTX *ctx;
1237 if (!(ctx=talloc_init("setup_dfs_referral"))) {
1238 *pstatus = NT_STATUS_NO_MEMORY;
1239 return -1;
1242 /* get the junction entry */
1243 if (!dfs_path) {
1244 talloc_destroy(ctx);
1245 *pstatus = NT_STATUS_NOT_FOUND;
1246 return -1;
1250 * Trim pathname sent by client so it begins with only one backslash.
1251 * Two backslashes confuse some dfs clients
1254 local_dfs_path = talloc_strdup(ctx,dfs_path);
1255 if (!local_dfs_path) {
1256 *pstatus = NT_STATUS_NO_MEMORY;
1257 talloc_destroy(ctx);
1258 return -1;
1260 pathnamep = local_dfs_path;
1261 while (IS_DIRECTORY_SEP(pathnamep[0]) &&
1262 IS_DIRECTORY_SEP(pathnamep[1])) {
1263 pathnamep++;
1266 junction = TALLOC_ZERO_P(ctx, struct junction_map);
1267 if (!junction) {
1268 *pstatus = NT_STATUS_NO_MEMORY;
1269 talloc_destroy(ctx);
1270 return -1;
1273 /* The following call can change cwd. */
1274 *pstatus = get_referred_path(ctx, pathnamep, junction,
1275 &consumedcnt, &self_referral);
1276 if (!NT_STATUS_IS_OK(*pstatus)) {
1277 vfs_ChDir(orig_conn,orig_conn->connectpath);
1278 talloc_destroy(ctx);
1279 return -1;
1281 vfs_ChDir(orig_conn,orig_conn->connectpath);
1283 if (!self_referral) {
1284 pathnamep[consumedcnt] = '\0';
1286 if( DEBUGLVL( 3 ) ) {
1287 int i=0;
1288 dbgtext("setup_dfs_referral: Path %s to "
1289 "alternate path(s):",
1290 pathnamep);
1291 for(i=0;i<junction->referral_count;i++)
1292 dbgtext(" %s",
1293 junction->referral_list[i].alternate_path);
1294 dbgtext(".\n");
1298 /* create the referral depeding on version */
1299 DEBUG(10,("max_referral_level :%d\n",max_referral_level));
1301 if (max_referral_level < 2) {
1302 max_referral_level = 2;
1304 if (max_referral_level > 3) {
1305 max_referral_level = 3;
1308 switch(max_referral_level) {
1309 case 2:
1310 reply_size = setup_ver2_dfs_referral(pathnamep,
1311 ppdata, junction,
1312 self_referral);
1313 break;
1314 case 3:
1315 reply_size = setup_ver3_dfs_referral(pathnamep, ppdata,
1316 junction, self_referral);
1317 break;
1318 default:
1319 DEBUG(0,("setup_dfs_referral: Invalid dfs referral "
1320 "version: %d\n",
1321 max_referral_level));
1322 talloc_destroy(ctx);
1323 *pstatus = NT_STATUS_INVALID_LEVEL;
1324 return -1;
1327 if (DEBUGLVL(10)) {
1328 DEBUGADD(0,("DFS Referral pdata:\n"));
1329 dump_data(0,(uint8 *)*ppdata,reply_size);
1332 talloc_destroy(ctx);
1333 *pstatus = NT_STATUS_OK;
1334 return reply_size;
1337 /**********************************************************************
1338 The following functions are called by the NETDFS RPC pipe functions
1339 **********************************************************************/
1341 /*********************************************************************
1342 Creates a junction structure from a DFS pathname
1343 **********************************************************************/
1345 bool create_junction(TALLOC_CTX *ctx,
1346 const char *dfs_path,
1347 struct junction_map *jucn)
1349 int snum;
1350 bool dummy;
1351 struct dfs_path *pdp = TALLOC_P(ctx,struct dfs_path);
1352 NTSTATUS status;
1354 if (!pdp) {
1355 return False;
1357 status = parse_dfs_path(NULL, dfs_path, False, pdp, &dummy);
1358 if (!NT_STATUS_IS_OK(status)) {
1359 return False;
1362 /* check if path is dfs : validate first token */
1363 if (!is_myname_or_ipaddr(pdp->hostname)) {
1364 DEBUG(4,("create_junction: Invalid hostname %s "
1365 "in dfs path %s\n",
1366 pdp->hostname, dfs_path));
1367 TALLOC_FREE(pdp);
1368 return False;
1371 /* Check for a non-DFS share */
1372 snum = lp_servicenumber(pdp->servicename);
1374 if(snum < 0 || !lp_msdfs_root(snum)) {
1375 DEBUG(4,("create_junction: %s is not an msdfs root.\n",
1376 pdp->servicename));
1377 TALLOC_FREE(pdp);
1378 return False;
1381 jucn->service_name = talloc_strdup(ctx, pdp->servicename);
1382 jucn->volume_name = talloc_strdup(ctx, pdp->reqpath);
1383 jucn->comment = talloc_strdup(ctx, lp_comment(snum));
1385 TALLOC_FREE(pdp);
1386 if (!jucn->service_name || !jucn->volume_name || ! jucn->comment) {
1387 return False;
1389 return True;
1392 /**********************************************************************
1393 Forms a valid Unix pathname from the junction
1394 **********************************************************************/
1396 static bool junction_to_local_path(const struct junction_map *jucn,
1397 char **pp_path_out,
1398 connection_struct **conn_out,
1399 char **oldpath)
1401 int snum;
1402 NTSTATUS status;
1404 snum = lp_servicenumber(jucn->service_name);
1405 if(snum < 0) {
1406 return False;
1408 status = create_conn_struct(talloc_tos(), conn_out, snum,
1409 lp_pathname(snum), NULL, oldpath);
1410 if (!NT_STATUS_IS_OK(status)) {
1411 return False;
1414 *pp_path_out = talloc_asprintf(*conn_out,
1415 "%s/%s",
1416 lp_pathname(snum),
1417 jucn->volume_name);
1418 if (!*pp_path_out) {
1419 vfs_ChDir(*conn_out, *oldpath);
1420 SMB_VFS_DISCONNECT(*conn_out);
1421 conn_free(*conn_out);
1422 return False;
1424 return True;
1427 bool create_msdfs_link(const struct junction_map *jucn)
1429 char *path = NULL;
1430 char *cwd;
1431 char *msdfs_link = NULL;
1432 connection_struct *conn;
1433 int i=0;
1434 bool insert_comma = False;
1435 bool ret = False;
1437 if(!junction_to_local_path(jucn, &path, &conn, &cwd)) {
1438 return False;
1441 /* Form the msdfs_link contents */
1442 msdfs_link = talloc_strdup(conn, "msdfs:");
1443 if (!msdfs_link) {
1444 goto out;
1446 for(i=0; i<jucn->referral_count; i++) {
1447 char *refpath = jucn->referral_list[i].alternate_path;
1449 /* Alternate paths always use Windows separators. */
1450 trim_char(refpath, '\\', '\\');
1451 if(*refpath == '\0') {
1452 if (i == 0) {
1453 insert_comma = False;
1455 continue;
1457 if (i > 0 && insert_comma) {
1458 msdfs_link = talloc_asprintf_append_buffer(msdfs_link,
1459 ",%s",
1460 refpath);
1461 } else {
1462 msdfs_link = talloc_asprintf_append_buffer(msdfs_link,
1463 "%s",
1464 refpath);
1467 if (!msdfs_link) {
1468 goto out;
1470 if (!insert_comma) {
1471 insert_comma = True;
1475 DEBUG(5,("create_msdfs_link: Creating new msdfs link: %s -> %s\n",
1476 path, msdfs_link));
1478 if(SMB_VFS_SYMLINK(conn, msdfs_link, path) < 0) {
1479 if (errno == EEXIST) {
1480 struct smb_filename *smb_fname = NULL;
1481 NTSTATUS status;
1483 status = create_synthetic_smb_fname(talloc_tos(), path,
1484 NULL, NULL,
1485 &smb_fname);
1486 if (!NT_STATUS_IS_OK(status)) {
1487 errno = map_errno_from_nt_status(status);
1488 goto out;
1491 if(SMB_VFS_UNLINK(conn, smb_fname)!=0) {
1492 TALLOC_FREE(smb_fname);
1493 goto out;
1495 TALLOC_FREE(smb_fname);
1497 if (SMB_VFS_SYMLINK(conn, msdfs_link, path) < 0) {
1498 DEBUG(1,("create_msdfs_link: symlink failed "
1499 "%s -> %s\nError: %s\n",
1500 path, msdfs_link, strerror(errno)));
1501 goto out;
1505 ret = True;
1507 out:
1508 vfs_ChDir(conn, cwd);
1509 SMB_VFS_DISCONNECT(conn);
1510 conn_free(conn);
1511 return ret;
1514 bool remove_msdfs_link(const struct junction_map *jucn)
1516 char *path = NULL;
1517 char *cwd;
1518 connection_struct *conn;
1519 bool ret = False;
1520 struct smb_filename *smb_fname = NULL;
1521 NTSTATUS status;
1523 if (!junction_to_local_path(jucn, &path, &conn, &cwd)) {
1524 return false;
1527 status = create_synthetic_smb_fname(talloc_tos(), path,
1528 NULL, NULL,
1529 &smb_fname);
1530 if (!NT_STATUS_IS_OK(status)) {
1531 errno = map_errno_from_nt_status(status);
1532 return false;
1535 if( SMB_VFS_UNLINK(conn, smb_fname) == 0 ) {
1536 ret = True;
1539 TALLOC_FREE(smb_fname);
1540 vfs_ChDir(conn, cwd);
1541 SMB_VFS_DISCONNECT(conn);
1542 conn_free(conn);
1543 return ret;
1546 /*********************************************************************
1547 Return the number of DFS links at the root of this share.
1548 *********************************************************************/
1550 static int count_dfs_links(TALLOC_CTX *ctx, int snum)
1552 size_t cnt = 0;
1553 SMB_STRUCT_DIR *dirp = NULL;
1554 const char *dname = NULL;
1555 char *talloced = NULL;
1556 const char *connect_path = lp_pathname(snum);
1557 const char *msdfs_proxy = lp_msdfs_proxy(snum);
1558 connection_struct *conn;
1559 NTSTATUS status;
1560 char *cwd;
1562 if(*connect_path == '\0') {
1563 return 0;
1567 * Fake up a connection struct for the VFS layer.
1570 status = create_conn_struct(talloc_tos(), &conn, snum, connect_path,
1571 NULL, &cwd);
1572 if (!NT_STATUS_IS_OK(status)) {
1573 DEBUG(3, ("create_conn_struct failed: %s\n",
1574 nt_errstr(status)));
1575 return 0;
1578 /* Count a link for the msdfs root - convention */
1579 cnt = 1;
1581 /* No more links if this is an msdfs proxy. */
1582 if (*msdfs_proxy != '\0') {
1583 goto out;
1586 /* Now enumerate all dfs links */
1587 dirp = SMB_VFS_OPENDIR(conn, ".", NULL, 0);
1588 if(!dirp) {
1589 goto out;
1592 while ((dname = vfs_readdirname(conn, dirp, NULL, &talloced))
1593 != NULL) {
1594 if (is_msdfs_link(conn,
1595 dname,
1596 NULL)) {
1597 cnt++;
1599 TALLOC_FREE(talloced);
1602 SMB_VFS_CLOSEDIR(conn,dirp);
1604 out:
1605 vfs_ChDir(conn, cwd);
1606 SMB_VFS_DISCONNECT(conn);
1607 conn_free(conn);
1608 return cnt;
1611 /*********************************************************************
1612 *********************************************************************/
1614 static int form_junctions(TALLOC_CTX *ctx,
1615 int snum,
1616 struct junction_map *jucn,
1617 size_t jn_remain)
1619 size_t cnt = 0;
1620 SMB_STRUCT_DIR *dirp = NULL;
1621 const char *dname = NULL;
1622 char *talloced = NULL;
1623 const char *connect_path = lp_pathname(snum);
1624 char *service_name = lp_servicename(snum);
1625 const char *msdfs_proxy = lp_msdfs_proxy(snum);
1626 connection_struct *conn;
1627 struct referral *ref = NULL;
1628 char *cwd;
1629 NTSTATUS status;
1631 if (jn_remain == 0) {
1632 return 0;
1635 if(*connect_path == '\0') {
1636 return 0;
1640 * Fake up a connection struct for the VFS layer.
1643 status = create_conn_struct(ctx, &conn, snum, connect_path, NULL,
1644 &cwd);
1645 if (!NT_STATUS_IS_OK(status)) {
1646 DEBUG(3, ("create_conn_struct failed: %s\n",
1647 nt_errstr(status)));
1648 return 0;
1651 /* form a junction for the msdfs root - convention
1652 DO NOT REMOVE THIS: NT clients will not work with us
1653 if this is not present
1655 jucn[cnt].service_name = talloc_strdup(ctx,service_name);
1656 jucn[cnt].volume_name = talloc_strdup(ctx, "");
1657 if (!jucn[cnt].service_name || !jucn[cnt].volume_name) {
1658 goto out;
1660 jucn[cnt].comment = "";
1661 jucn[cnt].referral_count = 1;
1663 ref = jucn[cnt].referral_list = TALLOC_ZERO_P(ctx, struct referral);
1664 if (jucn[cnt].referral_list == NULL) {
1665 goto out;
1668 ref->proximity = 0;
1669 ref->ttl = REFERRAL_TTL;
1670 if (*msdfs_proxy != '\0') {
1671 ref->alternate_path = talloc_strdup(ctx,
1672 msdfs_proxy);
1673 } else {
1674 ref->alternate_path = talloc_asprintf(ctx,
1675 "\\\\%s\\%s",
1676 get_local_machine_name(),
1677 service_name);
1680 if (!ref->alternate_path) {
1681 goto out;
1683 cnt++;
1685 /* Don't enumerate if we're an msdfs proxy. */
1686 if (*msdfs_proxy != '\0') {
1687 goto out;
1690 /* Now enumerate all dfs links */
1691 dirp = SMB_VFS_OPENDIR(conn, ".", NULL, 0);
1692 if(!dirp) {
1693 goto out;
1696 while ((dname = vfs_readdirname(conn, dirp, NULL, &talloced))
1697 != NULL) {
1698 char *link_target = NULL;
1699 if (cnt >= jn_remain) {
1700 DEBUG(2, ("form_junctions: ran out of MSDFS "
1701 "junction slots"));
1702 TALLOC_FREE(talloced);
1703 goto out;
1705 if (is_msdfs_link_internal(ctx,
1706 conn,
1707 dname, &link_target,
1708 NULL)) {
1709 if (parse_msdfs_symlink(ctx,
1710 link_target,
1711 &jucn[cnt].referral_list,
1712 &jucn[cnt].referral_count)) {
1714 jucn[cnt].service_name = talloc_strdup(ctx,
1715 service_name);
1716 jucn[cnt].volume_name = talloc_strdup(ctx,
1717 dname);
1718 if (!jucn[cnt].service_name ||
1719 !jucn[cnt].volume_name) {
1720 TALLOC_FREE(talloced);
1721 goto out;
1723 jucn[cnt].comment = "";
1724 cnt++;
1726 TALLOC_FREE(link_target);
1728 TALLOC_FREE(talloced);
1731 out:
1733 if (dirp) {
1734 SMB_VFS_CLOSEDIR(conn,dirp);
1737 vfs_ChDir(conn, cwd);
1738 conn_free(conn);
1739 return cnt;
1742 struct junction_map *enum_msdfs_links(TALLOC_CTX *ctx, size_t *p_num_jn)
1744 struct junction_map *jn = NULL;
1745 int i=0;
1746 size_t jn_count = 0;
1747 int sharecount = 0;
1749 *p_num_jn = 0;
1750 if(!lp_host_msdfs()) {
1751 return NULL;
1754 /* Ensure all the usershares are loaded. */
1755 become_root();
1756 load_registry_shares();
1757 sharecount = load_usershare_shares();
1758 unbecome_root();
1760 for(i=0;i < sharecount;i++) {
1761 if(lp_msdfs_root(i)) {
1762 jn_count += count_dfs_links(ctx, i);
1765 if (jn_count == 0) {
1766 return NULL;
1768 jn = TALLOC_ARRAY(ctx, struct junction_map, jn_count);
1769 if (!jn) {
1770 return NULL;
1772 for(i=0; i < sharecount; i++) {
1773 if (*p_num_jn >= jn_count) {
1774 break;
1776 if(lp_msdfs_root(i)) {
1777 *p_num_jn += form_junctions(ctx, i,
1778 &jn[*p_num_jn],
1779 jn_count - *p_num_jn);
1782 return jn;
1785 /******************************************************************************
1786 Core function to resolve a dfs pathname possibly containing a wildcard. If
1787 ppath_contains_wcard != NULL, it will be set to true if a wildcard is
1788 detected during dfs resolution.
1789 ******************************************************************************/
1791 NTSTATUS resolve_dfspath_wcard(TALLOC_CTX *ctx,
1792 connection_struct *conn,
1793 bool dfs_pathnames,
1794 const char *name_in,
1795 bool allow_wcards,
1796 char **pp_name_out,
1797 bool *ppath_contains_wcard)
1799 bool path_contains_wcard;
1800 NTSTATUS status = NT_STATUS_OK;
1802 if (dfs_pathnames) {
1803 status = dfs_redirect(ctx,
1804 conn,
1805 name_in,
1806 allow_wcards,
1807 pp_name_out,
1808 &path_contains_wcard);
1810 if (NT_STATUS_IS_OK(status) && ppath_contains_wcard != NULL) {
1811 *ppath_contains_wcard = path_contains_wcard;
1813 } else {
1815 * Cheat and just return a copy of the in ptr.
1816 * Once srvstr_get_path() uses talloc it'll
1817 * be a talloced ptr anyway.
1819 *pp_name_out = CONST_DISCARD(char *,name_in);
1821 return status;