s3-build: Fix inotify detection (bug 8580)
[Samba.git] / source3 / smbd / msdfs.c
blobf0f5d0669702dd80cca80b5722811f0f5c40b739
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 goto err_exit;
980 /* We know this is a valid dfs link. Parse the targetpath. */
981 if (!parse_msdfs_symlink(ctx, targetpath,
982 &jucn->referral_list,
983 &jucn->referral_count)) {
984 DEBUG(3,("get_referred_path: failed to parse symlink "
985 "target %s\n", targetpath ));
986 status = NT_STATUS_NOT_FOUND;
987 goto err_exit;
990 status = NT_STATUS_OK;
991 err_exit:
992 vfs_ChDir(conn, oldpath);
993 SMB_VFS_DISCONNECT(conn);
994 conn_free(conn);
995 TALLOC_FREE(pdp);
996 return status;
999 static int setup_ver2_dfs_referral(const char *pathname,
1000 char **ppdata,
1001 struct junction_map *junction,
1002 bool self_referral)
1004 char* pdata = *ppdata;
1006 smb_ucs2_t *uni_requestedpath = NULL;
1007 int uni_reqpathoffset1,uni_reqpathoffset2;
1008 int uni_curroffset;
1009 int requestedpathlen=0;
1010 int offset;
1011 int reply_size = 0;
1012 int i=0;
1014 DEBUG(10,("Setting up version2 referral\nRequested path:\n"));
1016 requestedpathlen = rpcstr_push_talloc(talloc_tos(),
1017 &uni_requestedpath, pathname);
1018 if (uni_requestedpath == NULL || requestedpathlen == 0) {
1019 return -1;
1022 if (DEBUGLVL(10)) {
1023 dump_data(0, (unsigned char *)uni_requestedpath,
1024 requestedpathlen);
1027 DEBUG(10,("ref count = %u\n",junction->referral_count));
1029 uni_reqpathoffset1 = REFERRAL_HEADER_SIZE +
1030 VERSION2_REFERRAL_SIZE * junction->referral_count;
1032 uni_reqpathoffset2 = uni_reqpathoffset1 + requestedpathlen;
1034 uni_curroffset = uni_reqpathoffset2 + requestedpathlen;
1036 reply_size = REFERRAL_HEADER_SIZE +
1037 VERSION2_REFERRAL_SIZE*junction->referral_count +
1038 2 * requestedpathlen;
1039 DEBUG(10,("reply_size: %u\n",reply_size));
1041 /* add up the unicode lengths of all the referral paths */
1042 for(i=0;i<junction->referral_count;i++) {
1043 DEBUG(10,("referral %u : %s\n",
1045 junction->referral_list[i].alternate_path));
1046 reply_size +=
1047 (strlen(junction->referral_list[i].alternate_path)+1)*2;
1050 DEBUG(10,("reply_size = %u\n",reply_size));
1051 /* add the unexplained 0x16 bytes */
1052 reply_size += 0x16;
1054 pdata = (char *)SMB_REALLOC(pdata,reply_size);
1055 if(pdata == NULL) {
1056 DEBUG(0,("Realloc failed!\n"));
1057 return -1;
1059 *ppdata = pdata;
1061 /* copy in the dfs requested paths.. required for offset calculations */
1062 memcpy(pdata+uni_reqpathoffset1,uni_requestedpath,requestedpathlen);
1063 memcpy(pdata+uni_reqpathoffset2,uni_requestedpath,requestedpathlen);
1065 /* create the header */
1066 SSVAL(pdata,0,requestedpathlen - 2); /* UCS2 of path consumed minus
1067 2 byte null */
1068 /* number of referral in this pkt */
1069 SSVAL(pdata,2,junction->referral_count);
1070 if(self_referral) {
1071 SIVAL(pdata,4,DFSREF_REFERRAL_SERVER | DFSREF_STORAGE_SERVER);
1072 } else {
1073 SIVAL(pdata,4,DFSREF_STORAGE_SERVER);
1076 offset = 8;
1077 /* add the referral elements */
1078 for(i=0;i<junction->referral_count;i++) {
1079 struct referral* ref = &junction->referral_list[i];
1080 int unilen;
1082 SSVAL(pdata,offset,2); /* version 2 */
1083 SSVAL(pdata,offset+2,VERSION2_REFERRAL_SIZE);
1084 if(self_referral) {
1085 SSVAL(pdata,offset+4,1);
1086 } else {
1087 SSVAL(pdata,offset+4,0);
1090 /* ref_flags :use path_consumed bytes? */
1091 SSVAL(pdata,offset+6,0);
1092 SIVAL(pdata,offset+8,ref->proximity);
1093 SIVAL(pdata,offset+12,ref->ttl);
1095 SSVAL(pdata,offset+16,uni_reqpathoffset1-offset);
1096 SSVAL(pdata,offset+18,uni_reqpathoffset2-offset);
1097 /* copy referred path into current offset */
1098 unilen = rpcstr_push(pdata+uni_curroffset,
1099 ref->alternate_path,
1100 reply_size - uni_curroffset,
1101 STR_UNICODE);
1103 SSVAL(pdata,offset+20,uni_curroffset-offset);
1105 uni_curroffset += unilen;
1106 offset += VERSION2_REFERRAL_SIZE;
1108 /* add in the unexplained 22 (0x16) bytes at the end */
1109 memset(pdata+uni_curroffset,'\0',0x16);
1110 return reply_size;
1113 static int setup_ver3_dfs_referral(const char *pathname,
1114 char **ppdata,
1115 struct junction_map *junction,
1116 bool self_referral)
1118 char *pdata = *ppdata;
1120 smb_ucs2_t *uni_reqpath = NULL;
1121 int uni_reqpathoffset1, uni_reqpathoffset2;
1122 int uni_curroffset;
1123 int reply_size = 0;
1125 int reqpathlen = 0;
1126 int offset,i=0;
1128 DEBUG(10,("setting up version3 referral\n"));
1130 reqpathlen = rpcstr_push_talloc(talloc_tos(), &uni_reqpath, pathname);
1131 if (uni_reqpath == NULL || reqpathlen == 0) {
1132 return -1;
1135 if (DEBUGLVL(10)) {
1136 dump_data(0, (unsigned char *)uni_reqpath,
1137 reqpathlen);
1140 uni_reqpathoffset1 = REFERRAL_HEADER_SIZE +
1141 VERSION3_REFERRAL_SIZE * junction->referral_count;
1142 uni_reqpathoffset2 = uni_reqpathoffset1 + reqpathlen;
1143 reply_size = uni_curroffset = uni_reqpathoffset2 + reqpathlen;
1145 for(i=0;i<junction->referral_count;i++) {
1146 DEBUG(10,("referral %u : %s\n",
1148 junction->referral_list[i].alternate_path));
1149 reply_size +=
1150 (strlen(junction->referral_list[i].alternate_path)+1)*2;
1153 pdata = (char *)SMB_REALLOC(pdata,reply_size);
1154 if(pdata == NULL) {
1155 DEBUG(0,("version3 referral setup:"
1156 "malloc failed for Realloc!\n"));
1157 return -1;
1159 *ppdata = pdata;
1161 /* create the header */
1162 SSVAL(pdata,0,reqpathlen - 2); /* UCS2 of path consumed minus
1163 2 byte null */
1164 SSVAL(pdata,2,junction->referral_count); /* number of referral */
1165 if(self_referral) {
1166 SIVAL(pdata,4,DFSREF_REFERRAL_SERVER | DFSREF_STORAGE_SERVER);
1167 } else {
1168 SIVAL(pdata,4,DFSREF_STORAGE_SERVER);
1171 /* copy in the reqpaths */
1172 memcpy(pdata+uni_reqpathoffset1,uni_reqpath,reqpathlen);
1173 memcpy(pdata+uni_reqpathoffset2,uni_reqpath,reqpathlen);
1175 offset = 8;
1176 for(i=0;i<junction->referral_count;i++) {
1177 struct referral* ref = &(junction->referral_list[i]);
1178 int unilen;
1180 SSVAL(pdata,offset,3); /* version 3 */
1181 SSVAL(pdata,offset+2,VERSION3_REFERRAL_SIZE);
1182 if(self_referral) {
1183 SSVAL(pdata,offset+4,1);
1184 } else {
1185 SSVAL(pdata,offset+4,0);
1188 /* ref_flags :use path_consumed bytes? */
1189 SSVAL(pdata,offset+6,0);
1190 SIVAL(pdata,offset+8,ref->ttl);
1192 SSVAL(pdata,offset+12,uni_reqpathoffset1-offset);
1193 SSVAL(pdata,offset+14,uni_reqpathoffset2-offset);
1194 /* copy referred path into current offset */
1195 unilen = rpcstr_push(pdata+uni_curroffset,ref->alternate_path,
1196 reply_size - uni_curroffset,
1197 STR_UNICODE | STR_TERMINATE);
1198 SSVAL(pdata,offset+16,uni_curroffset-offset);
1199 /* copy 0x10 bytes of 00's in the ServiceSite GUID */
1200 memset(pdata+offset+18,'\0',16);
1202 uni_curroffset += unilen;
1203 offset += VERSION3_REFERRAL_SIZE;
1205 return reply_size;
1208 /******************************************************************
1209 Set up the DFS referral for the dfs pathname. This call returns
1210 the amount of the path covered by this server, and where the
1211 client should be redirected to. This is the meat of the
1212 TRANS2_GET_DFS_REFERRAL call.
1213 ******************************************************************/
1215 int setup_dfs_referral(connection_struct *orig_conn,
1216 const char *dfs_path,
1217 int max_referral_level,
1218 char **ppdata, NTSTATUS *pstatus)
1220 struct junction_map *junction = NULL;
1221 int consumedcnt = 0;
1222 bool self_referral = False;
1223 int reply_size = 0;
1224 char *pathnamep = NULL;
1225 char *local_dfs_path = NULL;
1226 TALLOC_CTX *ctx;
1228 if (!(ctx=talloc_init("setup_dfs_referral"))) {
1229 *pstatus = NT_STATUS_NO_MEMORY;
1230 return -1;
1233 /* get the junction entry */
1234 if (!dfs_path) {
1235 talloc_destroy(ctx);
1236 *pstatus = NT_STATUS_NOT_FOUND;
1237 return -1;
1241 * Trim pathname sent by client so it begins with only one backslash.
1242 * Two backslashes confuse some dfs clients
1245 local_dfs_path = talloc_strdup(ctx,dfs_path);
1246 if (!local_dfs_path) {
1247 *pstatus = NT_STATUS_NO_MEMORY;
1248 talloc_destroy(ctx);
1249 return -1;
1251 pathnamep = local_dfs_path;
1252 while (IS_DIRECTORY_SEP(pathnamep[0]) &&
1253 IS_DIRECTORY_SEP(pathnamep[1])) {
1254 pathnamep++;
1257 junction = TALLOC_ZERO_P(ctx, struct junction_map);
1258 if (!junction) {
1259 *pstatus = NT_STATUS_NO_MEMORY;
1260 talloc_destroy(ctx);
1261 return -1;
1264 /* The following call can change cwd. */
1265 *pstatus = get_referred_path(ctx, pathnamep, junction,
1266 &consumedcnt, &self_referral);
1267 if (!NT_STATUS_IS_OK(*pstatus)) {
1268 vfs_ChDir(orig_conn,orig_conn->connectpath);
1269 talloc_destroy(ctx);
1270 return -1;
1272 vfs_ChDir(orig_conn,orig_conn->connectpath);
1274 if (!self_referral) {
1275 pathnamep[consumedcnt] = '\0';
1277 if( DEBUGLVL( 3 ) ) {
1278 int i=0;
1279 dbgtext("setup_dfs_referral: Path %s to "
1280 "alternate path(s):",
1281 pathnamep);
1282 for(i=0;i<junction->referral_count;i++)
1283 dbgtext(" %s",
1284 junction->referral_list[i].alternate_path);
1285 dbgtext(".\n");
1289 /* create the referral depeding on version */
1290 DEBUG(10,("max_referral_level :%d\n",max_referral_level));
1292 if (max_referral_level < 2) {
1293 max_referral_level = 2;
1295 if (max_referral_level > 3) {
1296 max_referral_level = 3;
1299 switch(max_referral_level) {
1300 case 2:
1301 reply_size = setup_ver2_dfs_referral(pathnamep,
1302 ppdata, junction,
1303 self_referral);
1304 break;
1305 case 3:
1306 reply_size = setup_ver3_dfs_referral(pathnamep, ppdata,
1307 junction, self_referral);
1308 break;
1309 default:
1310 DEBUG(0,("setup_dfs_referral: Invalid dfs referral "
1311 "version: %d\n",
1312 max_referral_level));
1313 talloc_destroy(ctx);
1314 *pstatus = NT_STATUS_INVALID_LEVEL;
1315 return -1;
1318 if (DEBUGLVL(10)) {
1319 DEBUGADD(0,("DFS Referral pdata:\n"));
1320 dump_data(0,(uint8 *)*ppdata,reply_size);
1323 talloc_destroy(ctx);
1324 *pstatus = NT_STATUS_OK;
1325 return reply_size;
1328 /**********************************************************************
1329 The following functions are called by the NETDFS RPC pipe functions
1330 **********************************************************************/
1332 /*********************************************************************
1333 Creates a junction structure from a DFS pathname
1334 **********************************************************************/
1336 bool create_junction(TALLOC_CTX *ctx,
1337 const char *dfs_path,
1338 struct junction_map *jucn)
1340 int snum;
1341 bool dummy;
1342 struct dfs_path *pdp = TALLOC_P(ctx,struct dfs_path);
1343 NTSTATUS status;
1345 if (!pdp) {
1346 return False;
1348 status = parse_dfs_path(NULL, dfs_path, False, pdp, &dummy);
1349 if (!NT_STATUS_IS_OK(status)) {
1350 return False;
1353 /* check if path is dfs : validate first token */
1354 if (!is_myname_or_ipaddr(pdp->hostname)) {
1355 DEBUG(4,("create_junction: Invalid hostname %s "
1356 "in dfs path %s\n",
1357 pdp->hostname, dfs_path));
1358 TALLOC_FREE(pdp);
1359 return False;
1362 /* Check for a non-DFS share */
1363 snum = lp_servicenumber(pdp->servicename);
1365 if(snum < 0 || !lp_msdfs_root(snum)) {
1366 DEBUG(4,("create_junction: %s is not an msdfs root.\n",
1367 pdp->servicename));
1368 TALLOC_FREE(pdp);
1369 return False;
1372 jucn->service_name = talloc_strdup(ctx, pdp->servicename);
1373 jucn->volume_name = talloc_strdup(ctx, pdp->reqpath);
1374 jucn->comment = talloc_strdup(ctx, lp_comment(snum));
1376 TALLOC_FREE(pdp);
1377 if (!jucn->service_name || !jucn->volume_name || ! jucn->comment) {
1378 return False;
1380 return True;
1383 /**********************************************************************
1384 Forms a valid Unix pathname from the junction
1385 **********************************************************************/
1387 static bool junction_to_local_path(const struct junction_map *jucn,
1388 char **pp_path_out,
1389 connection_struct **conn_out,
1390 char **oldpath)
1392 int snum;
1393 NTSTATUS status;
1395 snum = lp_servicenumber(jucn->service_name);
1396 if(snum < 0) {
1397 return False;
1399 status = create_conn_struct(talloc_tos(), conn_out, snum,
1400 lp_pathname(snum), NULL, oldpath);
1401 if (!NT_STATUS_IS_OK(status)) {
1402 return False;
1405 *pp_path_out = talloc_asprintf(*conn_out,
1406 "%s/%s",
1407 lp_pathname(snum),
1408 jucn->volume_name);
1409 if (!*pp_path_out) {
1410 vfs_ChDir(*conn_out, *oldpath);
1411 SMB_VFS_DISCONNECT(*conn_out);
1412 conn_free(*conn_out);
1413 return False;
1415 return True;
1418 bool create_msdfs_link(const struct junction_map *jucn)
1420 char *path = NULL;
1421 char *cwd;
1422 char *msdfs_link = NULL;
1423 connection_struct *conn;
1424 int i=0;
1425 bool insert_comma = False;
1426 bool ret = False;
1428 if(!junction_to_local_path(jucn, &path, &conn, &cwd)) {
1429 return False;
1432 /* Form the msdfs_link contents */
1433 msdfs_link = talloc_strdup(conn, "msdfs:");
1434 if (!msdfs_link) {
1435 goto out;
1437 for(i=0; i<jucn->referral_count; i++) {
1438 char *refpath = jucn->referral_list[i].alternate_path;
1440 /* Alternate paths always use Windows separators. */
1441 trim_char(refpath, '\\', '\\');
1442 if(*refpath == '\0') {
1443 if (i == 0) {
1444 insert_comma = False;
1446 continue;
1448 if (i > 0 && insert_comma) {
1449 msdfs_link = talloc_asprintf_append_buffer(msdfs_link,
1450 ",%s",
1451 refpath);
1452 } else {
1453 msdfs_link = talloc_asprintf_append_buffer(msdfs_link,
1454 "%s",
1455 refpath);
1458 if (!msdfs_link) {
1459 goto out;
1461 if (!insert_comma) {
1462 insert_comma = True;
1466 DEBUG(5,("create_msdfs_link: Creating new msdfs link: %s -> %s\n",
1467 path, msdfs_link));
1469 if(SMB_VFS_SYMLINK(conn, msdfs_link, path) < 0) {
1470 if (errno == EEXIST) {
1471 struct smb_filename *smb_fname = NULL;
1472 NTSTATUS status;
1474 status = create_synthetic_smb_fname(talloc_tos(), path,
1475 NULL, NULL,
1476 &smb_fname);
1477 if (!NT_STATUS_IS_OK(status)) {
1478 errno = map_errno_from_nt_status(status);
1479 goto out;
1482 if(SMB_VFS_UNLINK(conn, smb_fname)!=0) {
1483 TALLOC_FREE(smb_fname);
1484 goto out;
1486 TALLOC_FREE(smb_fname);
1488 if (SMB_VFS_SYMLINK(conn, msdfs_link, path) < 0) {
1489 DEBUG(1,("create_msdfs_link: symlink failed "
1490 "%s -> %s\nError: %s\n",
1491 path, msdfs_link, strerror(errno)));
1492 goto out;
1496 ret = True;
1498 out:
1499 vfs_ChDir(conn, cwd);
1500 SMB_VFS_DISCONNECT(conn);
1501 conn_free(conn);
1502 return ret;
1505 bool remove_msdfs_link(const struct junction_map *jucn)
1507 char *path = NULL;
1508 char *cwd;
1509 connection_struct *conn;
1510 bool ret = False;
1511 struct smb_filename *smb_fname = NULL;
1512 NTSTATUS status;
1514 if (!junction_to_local_path(jucn, &path, &conn, &cwd)) {
1515 return false;
1518 status = create_synthetic_smb_fname(talloc_tos(), path,
1519 NULL, NULL,
1520 &smb_fname);
1521 if (!NT_STATUS_IS_OK(status)) {
1522 errno = map_errno_from_nt_status(status);
1523 return false;
1526 if( SMB_VFS_UNLINK(conn, smb_fname) == 0 ) {
1527 ret = True;
1530 TALLOC_FREE(smb_fname);
1531 vfs_ChDir(conn, cwd);
1532 SMB_VFS_DISCONNECT(conn);
1533 conn_free(conn);
1534 return ret;
1537 /*********************************************************************
1538 Return the number of DFS links at the root of this share.
1539 *********************************************************************/
1541 static int count_dfs_links(TALLOC_CTX *ctx, int snum)
1543 size_t cnt = 0;
1544 SMB_STRUCT_DIR *dirp = NULL;
1545 const char *dname = NULL;
1546 char *talloced = NULL;
1547 const char *connect_path = lp_pathname(snum);
1548 const char *msdfs_proxy = lp_msdfs_proxy(snum);
1549 connection_struct *conn;
1550 NTSTATUS status;
1551 char *cwd;
1553 if(*connect_path == '\0') {
1554 return 0;
1558 * Fake up a connection struct for the VFS layer.
1561 status = create_conn_struct(talloc_tos(), &conn, snum, connect_path,
1562 NULL, &cwd);
1563 if (!NT_STATUS_IS_OK(status)) {
1564 DEBUG(3, ("create_conn_struct failed: %s\n",
1565 nt_errstr(status)));
1566 return 0;
1569 /* Count a link for the msdfs root - convention */
1570 cnt = 1;
1572 /* No more links if this is an msdfs proxy. */
1573 if (*msdfs_proxy != '\0') {
1574 goto out;
1577 /* Now enumerate all dfs links */
1578 dirp = SMB_VFS_OPENDIR(conn, ".", NULL, 0);
1579 if(!dirp) {
1580 goto out;
1583 while ((dname = vfs_readdirname(conn, dirp, NULL, &talloced))
1584 != NULL) {
1585 if (is_msdfs_link(conn,
1586 dname,
1587 NULL)) {
1588 cnt++;
1590 TALLOC_FREE(talloced);
1593 SMB_VFS_CLOSEDIR(conn,dirp);
1595 out:
1596 vfs_ChDir(conn, cwd);
1597 SMB_VFS_DISCONNECT(conn);
1598 conn_free(conn);
1599 return cnt;
1602 /*********************************************************************
1603 *********************************************************************/
1605 static int form_junctions(TALLOC_CTX *ctx,
1606 int snum,
1607 struct junction_map *jucn,
1608 size_t jn_remain)
1610 size_t cnt = 0;
1611 SMB_STRUCT_DIR *dirp = NULL;
1612 const char *dname = NULL;
1613 char *talloced = NULL;
1614 const char *connect_path = lp_pathname(snum);
1615 char *service_name = lp_servicename(snum);
1616 const char *msdfs_proxy = lp_msdfs_proxy(snum);
1617 connection_struct *conn;
1618 struct referral *ref = NULL;
1619 char *cwd;
1620 NTSTATUS status;
1622 if (jn_remain == 0) {
1623 return 0;
1626 if(*connect_path == '\0') {
1627 return 0;
1631 * Fake up a connection struct for the VFS layer.
1634 status = create_conn_struct(ctx, &conn, snum, connect_path, NULL,
1635 &cwd);
1636 if (!NT_STATUS_IS_OK(status)) {
1637 DEBUG(3, ("create_conn_struct failed: %s\n",
1638 nt_errstr(status)));
1639 return 0;
1642 /* form a junction for the msdfs root - convention
1643 DO NOT REMOVE THIS: NT clients will not work with us
1644 if this is not present
1646 jucn[cnt].service_name = talloc_strdup(ctx,service_name);
1647 jucn[cnt].volume_name = talloc_strdup(ctx, "");
1648 if (!jucn[cnt].service_name || !jucn[cnt].volume_name) {
1649 goto out;
1651 jucn[cnt].comment = "";
1652 jucn[cnt].referral_count = 1;
1654 ref = jucn[cnt].referral_list = TALLOC_ZERO_P(ctx, struct referral);
1655 if (jucn[cnt].referral_list == NULL) {
1656 goto out;
1659 ref->proximity = 0;
1660 ref->ttl = REFERRAL_TTL;
1661 if (*msdfs_proxy != '\0') {
1662 ref->alternate_path = talloc_strdup(ctx,
1663 msdfs_proxy);
1664 } else {
1665 ref->alternate_path = talloc_asprintf(ctx,
1666 "\\\\%s\\%s",
1667 get_local_machine_name(),
1668 service_name);
1671 if (!ref->alternate_path) {
1672 goto out;
1674 cnt++;
1676 /* Don't enumerate if we're an msdfs proxy. */
1677 if (*msdfs_proxy != '\0') {
1678 goto out;
1681 /* Now enumerate all dfs links */
1682 dirp = SMB_VFS_OPENDIR(conn, ".", NULL, 0);
1683 if(!dirp) {
1684 goto out;
1687 while ((dname = vfs_readdirname(conn, dirp, NULL, &talloced))
1688 != NULL) {
1689 char *link_target = NULL;
1690 if (cnt >= jn_remain) {
1691 DEBUG(2, ("form_junctions: ran out of MSDFS "
1692 "junction slots"));
1693 TALLOC_FREE(talloced);
1694 goto out;
1696 if (is_msdfs_link_internal(ctx,
1697 conn,
1698 dname, &link_target,
1699 NULL)) {
1700 if (parse_msdfs_symlink(ctx,
1701 link_target,
1702 &jucn[cnt].referral_list,
1703 &jucn[cnt].referral_count)) {
1705 jucn[cnt].service_name = talloc_strdup(ctx,
1706 service_name);
1707 jucn[cnt].volume_name = talloc_strdup(ctx,
1708 dname);
1709 if (!jucn[cnt].service_name ||
1710 !jucn[cnt].volume_name) {
1711 TALLOC_FREE(talloced);
1712 goto out;
1714 jucn[cnt].comment = "";
1715 cnt++;
1717 TALLOC_FREE(link_target);
1719 TALLOC_FREE(talloced);
1722 out:
1724 if (dirp) {
1725 SMB_VFS_CLOSEDIR(conn,dirp);
1728 vfs_ChDir(conn, cwd);
1729 conn_free(conn);
1730 return cnt;
1733 struct junction_map *enum_msdfs_links(TALLOC_CTX *ctx, size_t *p_num_jn)
1735 struct junction_map *jn = NULL;
1736 int i=0;
1737 size_t jn_count = 0;
1738 int sharecount = 0;
1740 *p_num_jn = 0;
1741 if(!lp_host_msdfs()) {
1742 return NULL;
1745 /* Ensure all the usershares are loaded. */
1746 become_root();
1747 load_registry_shares();
1748 sharecount = load_usershare_shares();
1749 unbecome_root();
1751 for(i=0;i < sharecount;i++) {
1752 if(lp_msdfs_root(i)) {
1753 jn_count += count_dfs_links(ctx, i);
1756 if (jn_count == 0) {
1757 return NULL;
1759 jn = TALLOC_ARRAY(ctx, struct junction_map, jn_count);
1760 if (!jn) {
1761 return NULL;
1763 for(i=0; i < sharecount; i++) {
1764 if (*p_num_jn >= jn_count) {
1765 break;
1767 if(lp_msdfs_root(i)) {
1768 *p_num_jn += form_junctions(ctx, i,
1769 &jn[*p_num_jn],
1770 jn_count - *p_num_jn);
1773 return jn;
1776 /******************************************************************************
1777 Core function to resolve a dfs pathname possibly containing a wildcard. If
1778 ppath_contains_wcard != NULL, it will be set to true if a wildcard is
1779 detected during dfs resolution.
1780 ******************************************************************************/
1782 NTSTATUS resolve_dfspath_wcard(TALLOC_CTX *ctx,
1783 connection_struct *conn,
1784 bool dfs_pathnames,
1785 const char *name_in,
1786 bool allow_wcards,
1787 char **pp_name_out,
1788 bool *ppath_contains_wcard)
1790 bool path_contains_wcard;
1791 NTSTATUS status = NT_STATUS_OK;
1793 if (dfs_pathnames) {
1794 status = dfs_redirect(ctx,
1795 conn,
1796 name_in,
1797 allow_wcards,
1798 pp_name_out,
1799 &path_contains_wcard);
1801 if (NT_STATUS_IS_OK(status) && ppath_contains_wcard != NULL) {
1802 *ppath_contains_wcard = path_contains_wcard;
1804 } else {
1806 * Cheat and just return a copy of the in ptr.
1807 * Once srvstr_get_path() uses talloc it'll
1808 * be a talloced ptr anyway.
1810 *pp_name_out = CONST_DISCARD(char *,name_in);
1812 return status;