Fix bug #8493 - DFS breaks zip file extracting unless "follow symlinks = no" set
[Samba/id10ts.git] / source3 / smbd / msdfs.c
blobc49cb61f6cc647fe5a6b6d2daee0bc92119bed9c
1 /*
2 Unix SMB/Netbios implementation.
3 Version 3.0
4 MSDFS services for Samba
5 Copyright (C) Shirish Kalele 2000
6 Copyright (C) Jeremy Allison 2007
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 3 of the License, or
11 (at your option) any later version.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program. If not, see <http://www.gnu.org/licenses/>.
23 #define DBGC_CLASS DBGC_MSDFS
24 #include "includes.h"
25 #include "system/filesys.h"
26 #include "smbd/smbd.h"
27 #include "smbd/globals.h"
28 #include "msdfs.h"
29 #include "auth.h"
30 #include "lib/param/loadparm.h"
31 #include "libcli/security/security.h"
33 /**********************************************************************
34 Parse a DFS pathname of the form \hostname\service\reqpath
35 into the dfs_path structure.
36 If POSIX pathnames is true, the pathname may also be of the
37 form /hostname/service/reqpath.
38 We cope with either here.
40 Unfortunately, due to broken clients who might set the
41 SVAL(inbuf,smb_flg2) & FLAGS2_DFS_PATHNAMES bit and then
42 send a local path, we have to cope with that too....
44 If conn != NULL then ensure the provided service is
45 the one pointed to by the connection.
47 This version does everything using pointers within one copy of the
48 pathname string, talloced on the struct dfs_path pointer (which
49 must be talloced). This may be too clever to live....
50 JRA.
51 **********************************************************************/
53 static NTSTATUS parse_dfs_path(connection_struct *conn,
54 const char *pathname,
55 bool allow_wcards,
56 bool allow_broken_path,
57 struct dfs_path *pdp, /* MUST BE TALLOCED */
58 bool *ppath_contains_wcard)
60 char *pathname_local;
61 char *p,*temp;
62 char *servicename;
63 char *eos_ptr;
64 NTSTATUS status = NT_STATUS_OK;
65 char sepchar;
67 ZERO_STRUCTP(pdp);
70 * This is the only talloc we should need to do
71 * on the struct dfs_path. All the pointers inside
72 * it should point to offsets within this string.
75 pathname_local = talloc_strdup(pdp, pathname);
76 if (!pathname_local) {
77 return NT_STATUS_NO_MEMORY;
79 /* Get a pointer to the terminating '\0' */
80 eos_ptr = &pathname_local[strlen(pathname_local)];
81 p = temp = pathname_local;
83 pdp->posix_path = (lp_posix_pathnames() && *pathname == '/');
85 sepchar = pdp->posix_path ? '/' : '\\';
87 if (allow_broken_path && (*pathname != sepchar)) {
88 DEBUG(10,("parse_dfs_path: path %s doesn't start with %c\n",
89 pathname, sepchar ));
91 * Possibly client sent a local path by mistake.
92 * Try and convert to a local path.
95 pdp->hostname = eos_ptr; /* "" */
96 pdp->servicename = eos_ptr; /* "" */
98 /* We've got no info about separators. */
99 pdp->posix_path = lp_posix_pathnames();
100 p = temp;
101 DEBUG(10,("parse_dfs_path: trying to convert %s to a "
102 "local path\n",
103 temp));
104 goto local_path;
108 * Safe to use on talloc'ed string as it only shrinks.
109 * It also doesn't affect the eos_ptr.
111 trim_char(temp,sepchar,sepchar);
113 DEBUG(10,("parse_dfs_path: temp = |%s| after trimming %c's\n",
114 temp, sepchar));
116 /* Now tokenize. */
117 /* Parse out hostname. */
118 p = strchr_m(temp,sepchar);
119 if(p == NULL) {
120 DEBUG(10,("parse_dfs_path: can't parse hostname from path %s\n",
121 temp));
123 * Possibly client sent a local path by mistake.
124 * Try and convert to a local path.
127 pdp->hostname = eos_ptr; /* "" */
128 pdp->servicename = eos_ptr; /* "" */
130 p = temp;
131 DEBUG(10,("parse_dfs_path: trying to convert %s "
132 "to a local path\n",
133 temp));
134 goto local_path;
136 *p = '\0';
137 pdp->hostname = temp;
139 DEBUG(10,("parse_dfs_path: hostname: %s\n",pdp->hostname));
141 /* Parse out servicename. */
142 servicename = p+1;
143 p = strchr_m(servicename,sepchar);
144 if (p) {
145 *p = '\0';
148 /* Is this really our servicename ? */
149 if (conn && !( strequal(servicename, lp_servicename(SNUM(conn)))
150 || (strequal(servicename, HOMES_NAME)
151 && strequal(lp_servicename(SNUM(conn)),
152 get_current_username()) )) ) {
153 DEBUG(10,("parse_dfs_path: %s is not our servicename\n",
154 servicename));
157 * Possibly client sent a local path by mistake.
158 * Try and convert to a local path.
161 pdp->hostname = eos_ptr; /* "" */
162 pdp->servicename = eos_ptr; /* "" */
164 /* Repair the path - replace the sepchar's
165 we nulled out */
166 servicename--;
167 *servicename = sepchar;
168 if (p) {
169 *p = sepchar;
172 p = temp;
173 DEBUG(10,("parse_dfs_path: trying to convert %s "
174 "to a local path\n",
175 temp));
176 goto local_path;
179 pdp->servicename = servicename;
181 DEBUG(10,("parse_dfs_path: servicename: %s\n",pdp->servicename));
183 if(p == NULL) {
184 /* Client sent self referral \server\share. */
185 pdp->reqpath = eos_ptr; /* "" */
186 return NT_STATUS_OK;
189 p++;
191 local_path:
193 *ppath_contains_wcard = False;
195 pdp->reqpath = p;
197 /* Rest is reqpath. */
198 if (pdp->posix_path) {
199 status = check_path_syntax_posix(pdp->reqpath);
200 } else {
201 if (allow_wcards) {
202 status = check_path_syntax_wcard(pdp->reqpath,
203 ppath_contains_wcard);
204 } else {
205 status = check_path_syntax(pdp->reqpath);
209 if (!NT_STATUS_IS_OK(status)) {
210 DEBUG(10,("parse_dfs_path: '%s' failed with %s\n",
211 p, nt_errstr(status) ));
212 return status;
215 DEBUG(10,("parse_dfs_path: rest of the path: %s\n",pdp->reqpath));
216 return NT_STATUS_OK;
219 /********************************************************
220 Fake up a connection struct for the VFS layer.
221 Note: this performs a vfs connect and CHANGES CWD !!!! JRA.
222 *********************************************************/
224 NTSTATUS create_conn_struct(TALLOC_CTX *ctx,
225 struct smbd_server_connection *sconn,
226 connection_struct **pconn,
227 int snum,
228 const char *path,
229 const struct auth_session_info *session_info,
230 char **poldcwd)
232 connection_struct *conn;
233 char *connpath;
234 char *oldcwd;
235 const char *vfs_user;
237 conn = talloc_zero(ctx, connection_struct);
238 if (conn == NULL) {
239 return NT_STATUS_NO_MEMORY;
242 connpath = talloc_strdup(conn, path);
243 if (!connpath) {
244 TALLOC_FREE(conn);
245 return NT_STATUS_NO_MEMORY;
247 connpath = talloc_string_sub(conn,
248 connpath,
249 "%S",
250 lp_servicename(snum));
251 if (!connpath) {
252 TALLOC_FREE(conn);
253 return NT_STATUS_NO_MEMORY;
256 /* needed for smbd_vfs_init() */
258 if (!(conn->params = talloc_zero(conn, struct share_params))) {
259 DEBUG(0, ("TALLOC failed\n"));
260 TALLOC_FREE(conn);
261 return NT_STATUS_NO_MEMORY;
264 conn->params->service = snum;
266 conn->sconn = sconn;
267 conn->sconn->num_tcons_open++;
269 if (session_info != NULL) {
270 conn->session_info = copy_session_info(conn, session_info);
271 if (conn->session_info == NULL) {
272 DEBUG(0, ("copy_serverinfo failed\n"));
273 TALLOC_FREE(conn);
274 return NT_STATUS_NO_MEMORY;
276 vfs_user = conn->session_info->unix_info->unix_name;
277 } else {
278 /* use current authenticated user in absence of session_info */
279 vfs_user = get_current_username();
282 set_conn_connectpath(conn, connpath);
285 * New code to check if there's a share security descripter
286 * added from NT server manager. This is done after the
287 * smb.conf checks are done as we need a uid and token. JRA.
290 if (conn->session_info) {
291 share_access_check(conn->session_info->security_token,
292 lp_servicename(snum), MAXIMUM_ALLOWED_ACCESS,
293 &conn->share_access);
295 if ((conn->share_access & FILE_WRITE_DATA) == 0) {
296 if ((conn->share_access & FILE_READ_DATA) == 0) {
297 /* No access, read or write. */
298 DEBUG(0,("create_conn_struct: connection to %s "
299 "denied due to security "
300 "descriptor.\n",
301 lp_servicename(snum)));
302 conn_free(conn);
303 return NT_STATUS_ACCESS_DENIED;
304 } else {
305 conn->read_only = true;
308 } else {
309 conn->share_access = 0;
310 conn->read_only = true;
313 if (!smbd_vfs_init(conn)) {
314 NTSTATUS status = map_nt_error_from_unix(errno);
315 DEBUG(0,("create_conn_struct: smbd_vfs_init failed.\n"));
316 conn_free(conn);
317 return status;
320 /* this must be the first filesystem operation that we do */
321 if (SMB_VFS_CONNECT(conn, lp_servicename(snum), vfs_user) < 0) {
322 DEBUG(0,("VFS connect failed!\n"));
323 conn_free(conn);
324 return NT_STATUS_UNSUCCESSFUL;
327 conn->fs_capabilities = SMB_VFS_FS_CAPABILITIES(conn, &conn->ts_res);
330 * Windows seems to insist on doing trans2getdfsreferral() calls on
331 * the IPC$ share as the anonymous user. If we try to chdir as that
332 * user we will fail.... WTF ? JRA.
335 oldcwd = vfs_GetWd(ctx, conn);
336 if (oldcwd == NULL) {
337 NTSTATUS status = map_nt_error_from_unix(errno);
338 DEBUG(3, ("vfs_GetWd failed: %s\n", strerror(errno)));
339 conn_free(conn);
340 return status;
343 if (vfs_ChDir(conn,conn->connectpath) != 0) {
344 NTSTATUS status = map_nt_error_from_unix(errno);
345 DEBUG(3,("create_conn_struct: Can't ChDir to new conn path %s. "
346 "Error was %s\n",
347 conn->connectpath, strerror(errno) ));
348 conn_free(conn);
349 return status;
352 *pconn = conn;
353 *poldcwd = oldcwd;
355 return NT_STATUS_OK;
358 /**********************************************************************
359 Parse the contents of a symlink to verify if it is an msdfs referral
360 A valid referral is of the form:
362 msdfs:server1\share1,server2\share2
363 msdfs:server1\share1\pathname,server2\share2\pathname
364 msdfs:server1/share1,server2/share2
365 msdfs:server1/share1/pathname,server2/share2/pathname.
367 Note that the alternate paths returned here must be of the canonicalized
368 form:
370 \server\share or
371 \server\share\path\to\file,
373 even in posix path mode. This is because we have no knowledge if the
374 server we're referring to understands posix paths.
375 **********************************************************************/
377 static bool parse_msdfs_symlink(TALLOC_CTX *ctx,
378 const char *target,
379 struct referral **preflist,
380 int *refcount)
382 char *temp = NULL;
383 char *prot;
384 char **alt_path = NULL;
385 int count = 0, i;
386 struct referral *reflist;
387 char *saveptr;
389 temp = talloc_strdup(ctx, target);
390 if (!temp) {
391 return False;
393 prot = strtok_r(temp, ":", &saveptr);
394 if (!prot) {
395 DEBUG(0,("parse_msdfs_symlink: invalid path !\n"));
396 return False;
399 alt_path = talloc_array(ctx, char *, MAX_REFERRAL_COUNT);
400 if (!alt_path) {
401 return False;
404 /* parse out the alternate paths */
405 while((count<MAX_REFERRAL_COUNT) &&
406 ((alt_path[count] = strtok_r(NULL, ",", &saveptr)) != NULL)) {
407 count++;
410 DEBUG(10,("parse_msdfs_symlink: count=%d\n", count));
412 if (count) {
413 reflist = *preflist = talloc_zero_array(ctx,
414 struct referral, count);
415 if(reflist == NULL) {
416 TALLOC_FREE(alt_path);
417 return False;
419 } else {
420 reflist = *preflist = NULL;
423 for(i=0;i<count;i++) {
424 char *p;
426 /* Canonicalize link target.
427 * Replace all /'s in the path by a \ */
428 string_replace(alt_path[i], '/', '\\');
430 /* Remove leading '\\'s */
431 p = alt_path[i];
432 while (*p && (*p == '\\')) {
433 p++;
436 reflist[i].alternate_path = talloc_asprintf(ctx,
437 "\\%s",
439 if (!reflist[i].alternate_path) {
440 return False;
443 reflist[i].proximity = 0;
444 reflist[i].ttl = REFERRAL_TTL;
445 DEBUG(10, ("parse_msdfs_symlink: Created alt path: %s\n",
446 reflist[i].alternate_path));
449 *refcount = count;
451 TALLOC_FREE(alt_path);
452 return True;
455 /**********************************************************************
456 Returns true if the unix path is a valid msdfs symlink and also
457 returns the target string from inside the link.
458 **********************************************************************/
460 static bool is_msdfs_link_internal(TALLOC_CTX *ctx,
461 connection_struct *conn,
462 const char *path,
463 char **pp_link_target,
464 SMB_STRUCT_STAT *sbufp)
466 int referral_len = 0;
467 #if defined(HAVE_BROKEN_READLINK)
468 char link_target_buf[PATH_MAX];
469 #else
470 char link_target_buf[7];
471 #endif
472 size_t bufsize = 0;
473 char *link_target = NULL;
474 struct smb_filename smb_fname;
476 if (pp_link_target) {
477 bufsize = 1024;
478 link_target = talloc_array(ctx, char, bufsize);
479 if (!link_target) {
480 return False;
482 *pp_link_target = link_target;
483 } else {
484 bufsize = sizeof(link_target_buf);
485 link_target = link_target_buf;
488 ZERO_STRUCT(smb_fname);
489 smb_fname.base_name = discard_const_p(char, path);
491 if (SMB_VFS_LSTAT(conn, &smb_fname) != 0) {
492 DEBUG(5,("is_msdfs_link_read_target: %s does not exist.\n",
493 path));
494 goto err;
496 if (!S_ISLNK(smb_fname.st.st_ex_mode)) {
497 DEBUG(5,("is_msdfs_link_read_target: %s is not a link.\n",
498 path));
499 goto err;
501 if (sbufp != NULL) {
502 *sbufp = smb_fname.st;
505 referral_len = SMB_VFS_READLINK(conn, path, link_target, bufsize - 1);
506 if (referral_len == -1) {
507 DEBUG(0,("is_msdfs_link_read_target: Error reading "
508 "msdfs link %s: %s\n",
509 path, strerror(errno)));
510 goto err;
512 link_target[referral_len] = '\0';
514 DEBUG(5,("is_msdfs_link_internal: %s -> %s\n",path,
515 link_target));
517 if (!strnequal(link_target, "msdfs:", 6)) {
518 goto err;
520 return True;
522 err:
524 if (link_target != link_target_buf) {
525 TALLOC_FREE(link_target);
527 return False;
530 /**********************************************************************
531 Returns true if the unix path is a valid msdfs symlink.
532 **********************************************************************/
534 bool is_msdfs_link(connection_struct *conn,
535 const char *path,
536 SMB_STRUCT_STAT *sbufp)
538 return is_msdfs_link_internal(talloc_tos(),
539 conn,
540 path,
541 NULL,
542 sbufp);
545 /*****************************************************************
546 Used by other functions to decide if a dfs path is remote,
547 and to get the list of referred locations for that remote path.
549 search_flag: For findfirsts, dfs links themselves are not
550 redirected, but paths beyond the links are. For normal smb calls,
551 even dfs links need to be redirected.
553 consumedcntp: how much of the dfs path is being redirected. the client
554 should try the remaining path on the redirected server.
556 If this returns NT_STATUS_PATH_NOT_COVERED the contents of the msdfs
557 link redirect are in targetpath.
558 *****************************************************************/
560 static NTSTATUS dfs_path_lookup(TALLOC_CTX *ctx,
561 connection_struct *conn,
562 const char *dfspath, /* Incoming complete dfs path */
563 const struct dfs_path *pdp, /* Parsed out
564 server+share+extrapath. */
565 bool search_flag, /* Called from a findfirst ? */
566 int *consumedcntp,
567 char **pp_targetpath)
569 char *p = NULL;
570 char *q = NULL;
571 NTSTATUS status;
572 struct smb_filename *smb_fname = NULL;
573 char *canon_dfspath = NULL; /* Canonicalized dfs path. (only '/'
574 components). */
576 DEBUG(10,("dfs_path_lookup: Conn path = %s reqpath = %s\n",
577 conn->connectpath, pdp->reqpath));
580 * Note the unix path conversion here we're doing we
581 * throw away. We're looking for a symlink for a dfs
582 * resolution, if we don't find it we'll do another
583 * unix_convert later in the codepath.
586 status = unix_convert(ctx, conn, pdp->reqpath, &smb_fname,
587 search_flag ? UCF_ALWAYS_ALLOW_WCARD_LCOMP : 0);
589 if (!NT_STATUS_IS_OK(status)) {
590 if (!NT_STATUS_EQUAL(status,
591 NT_STATUS_OBJECT_PATH_NOT_FOUND)) {
592 return status;
594 if (smb_fname == NULL || smb_fname->base_name == NULL) {
595 return status;
599 /* Optimization - check if we can redirect the whole path. */
601 if (is_msdfs_link_internal(ctx, conn, smb_fname->base_name,
602 pp_targetpath, NULL)) {
603 if (search_flag) {
604 DEBUG(6,("dfs_path_lookup (FindFirst) No redirection "
605 "for dfs link %s.\n", dfspath));
606 status = NT_STATUS_OK;
607 goto out;
610 DEBUG(6,("dfs_path_lookup: %s resolves to a "
611 "valid dfs link %s.\n", dfspath,
612 pp_targetpath ? *pp_targetpath : ""));
614 if (consumedcntp) {
615 *consumedcntp = strlen(dfspath);
617 status = NT_STATUS_PATH_NOT_COVERED;
618 goto out;
621 /* Prepare to test only for '/' components in the given path,
622 * so if a Windows path replace all '\\' characters with '/'.
623 * For a POSIX DFS path we know all separators are already '/'. */
625 canon_dfspath = talloc_strdup(ctx, dfspath);
626 if (!canon_dfspath) {
627 status = NT_STATUS_NO_MEMORY;
628 goto out;
630 if (!pdp->posix_path) {
631 string_replace(canon_dfspath, '\\', '/');
635 * localpath comes out of unix_convert, so it has
636 * no trailing backslash. Make sure that canon_dfspath hasn't either.
637 * Fix for bug #4860 from Jan Martin <Jan.Martin@rwedea.com>.
640 trim_char(canon_dfspath,0,'/');
643 * Redirect if any component in the path is a link.
644 * We do this by walking backwards through the
645 * local path, chopping off the last component
646 * in both the local path and the canonicalized
647 * DFS path. If we hit a DFS link then we're done.
650 p = strrchr_m(smb_fname->base_name, '/');
651 if (consumedcntp) {
652 q = strrchr_m(canon_dfspath, '/');
655 while (p) {
656 *p = '\0';
657 if (q) {
658 *q = '\0';
661 if (is_msdfs_link_internal(ctx, conn,
662 smb_fname->base_name, pp_targetpath,
663 NULL)) {
664 DEBUG(4, ("dfs_path_lookup: Redirecting %s because "
665 "parent %s is dfs link\n", dfspath,
666 smb_fname_str_dbg(smb_fname)));
668 if (consumedcntp) {
669 *consumedcntp = strlen(canon_dfspath);
670 DEBUG(10, ("dfs_path_lookup: Path consumed: %s "
671 "(%d)\n",
672 canon_dfspath,
673 *consumedcntp));
676 status = NT_STATUS_PATH_NOT_COVERED;
677 goto out;
680 /* Step back on the filesystem. */
681 p = strrchr_m(smb_fname->base_name, '/');
683 if (consumedcntp) {
684 /* And in the canonicalized dfs path. */
685 q = strrchr_m(canon_dfspath, '/');
689 status = NT_STATUS_OK;
690 out:
691 TALLOC_FREE(smb_fname);
692 return status;
695 /*****************************************************************
696 Decides if a dfs pathname should be redirected or not.
697 If not, the pathname is converted to a tcon-relative local unix path
699 search_wcard_flag: this flag performs 2 functions both related
700 to searches. See resolve_dfs_path() and parse_dfs_path_XX()
701 for details.
703 This function can return NT_STATUS_OK, meaning use the returned path as-is
704 (mapped into a local path).
705 or NT_STATUS_NOT_COVERED meaning return a DFS redirect, or
706 any other NT_STATUS error which is a genuine error to be
707 returned to the client.
708 *****************************************************************/
710 static NTSTATUS dfs_redirect(TALLOC_CTX *ctx,
711 connection_struct *conn,
712 const char *path_in,
713 bool search_wcard_flag,
714 bool allow_broken_path,
715 char **pp_path_out,
716 bool *ppath_contains_wcard)
718 NTSTATUS status;
719 struct dfs_path *pdp = talloc(ctx, struct dfs_path);
721 if (!pdp) {
722 return NT_STATUS_NO_MEMORY;
725 status = parse_dfs_path(conn, path_in, search_wcard_flag,
726 allow_broken_path, pdp,
727 ppath_contains_wcard);
728 if (!NT_STATUS_IS_OK(status)) {
729 TALLOC_FREE(pdp);
730 return status;
733 if (pdp->reqpath[0] == '\0') {
734 TALLOC_FREE(pdp);
735 *pp_path_out = talloc_strdup(ctx, "");
736 if (!*pp_path_out) {
737 return NT_STATUS_NO_MEMORY;
739 DEBUG(5,("dfs_redirect: self-referral.\n"));
740 return NT_STATUS_OK;
743 /* If dfs pathname for a non-dfs share, convert to tcon-relative
744 path and return OK */
746 if (!lp_msdfs_root(SNUM(conn))) {
747 *pp_path_out = talloc_strdup(ctx, pdp->reqpath);
748 TALLOC_FREE(pdp);
749 if (!*pp_path_out) {
750 return NT_STATUS_NO_MEMORY;
752 return NT_STATUS_OK;
755 /* If it looked like a local path (zero hostname/servicename)
756 * just treat as a tcon-relative path. */
758 if (pdp->hostname[0] == '\0' && pdp->servicename[0] == '\0') {
759 *pp_path_out = talloc_strdup(ctx, pdp->reqpath);
760 TALLOC_FREE(pdp);
761 if (!*pp_path_out) {
762 return NT_STATUS_NO_MEMORY;
764 return NT_STATUS_OK;
767 if (!( strequal(pdp->servicename, lp_servicename(SNUM(conn)))
768 || (strequal(pdp->servicename, HOMES_NAME)
769 && strequal(lp_servicename(SNUM(conn)),
770 conn->session_info->unix_info->sanitized_username) )) ) {
772 /* The given sharename doesn't match this connection. */
773 TALLOC_FREE(pdp);
775 return NT_STATUS_OBJECT_PATH_NOT_FOUND;
778 status = dfs_path_lookup(ctx, conn, path_in, pdp,
779 search_wcard_flag, NULL, NULL);
780 if (!NT_STATUS_IS_OK(status)) {
781 if (NT_STATUS_EQUAL(status, NT_STATUS_PATH_NOT_COVERED)) {
782 DEBUG(3,("dfs_redirect: Redirecting %s\n", path_in));
783 } else {
784 DEBUG(10,("dfs_redirect: dfs_path_lookup "
785 "failed for %s with %s\n",
786 path_in, nt_errstr(status) ));
788 return status;
791 DEBUG(3,("dfs_redirect: Not redirecting %s.\n", path_in));
793 /* Form non-dfs tcon-relative path */
794 *pp_path_out = talloc_strdup(ctx, pdp->reqpath);
795 TALLOC_FREE(pdp);
796 if (!*pp_path_out) {
797 return NT_STATUS_NO_MEMORY;
800 DEBUG(3,("dfs_redirect: Path %s converted to non-dfs path %s\n",
801 path_in,
802 *pp_path_out));
804 return NT_STATUS_OK;
807 /**********************************************************************
808 Return a self referral.
809 **********************************************************************/
811 static NTSTATUS self_ref(TALLOC_CTX *ctx,
812 const char *dfs_path,
813 struct junction_map *jucn,
814 int *consumedcntp,
815 bool *self_referralp)
817 struct referral *ref;
819 *self_referralp = True;
821 jucn->referral_count = 1;
822 if((ref = talloc_zero(ctx, struct referral)) == NULL) {
823 return NT_STATUS_NO_MEMORY;
826 ref->alternate_path = talloc_strdup(ctx, dfs_path);
827 if (!ref->alternate_path) {
828 return NT_STATUS_NO_MEMORY;
830 ref->proximity = 0;
831 ref->ttl = REFERRAL_TTL;
832 jucn->referral_list = ref;
833 *consumedcntp = strlen(dfs_path);
834 return NT_STATUS_OK;
837 /**********************************************************************
838 Gets valid referrals for a dfs path and fills up the
839 junction_map structure.
840 **********************************************************************/
842 NTSTATUS get_referred_path(TALLOC_CTX *ctx,
843 const char *dfs_path,
844 struct smbd_server_connection *sconn,
845 struct junction_map *jucn,
846 int *consumedcntp,
847 bool *self_referralp)
849 struct connection_struct *conn;
850 char *targetpath = NULL;
851 int snum;
852 NTSTATUS status = NT_STATUS_NOT_FOUND;
853 bool dummy;
854 struct dfs_path *pdp = talloc(ctx, struct dfs_path);
855 char *oldpath;
857 if (!pdp) {
858 return NT_STATUS_NO_MEMORY;
861 *self_referralp = False;
863 status = parse_dfs_path(NULL, dfs_path, False, !sconn->using_smb2,
864 pdp, &dummy);
865 if (!NT_STATUS_IS_OK(status)) {
866 return status;
869 jucn->service_name = talloc_strdup(ctx, pdp->servicename);
870 jucn->volume_name = talloc_strdup(ctx, pdp->reqpath);
871 if (!jucn->service_name || !jucn->volume_name) {
872 TALLOC_FREE(pdp);
873 return NT_STATUS_NO_MEMORY;
876 /* Verify the share is a dfs root */
877 snum = lp_servicenumber(jucn->service_name);
878 if(snum < 0) {
879 char *service_name = NULL;
880 if ((snum = find_service(ctx, jucn->service_name, &service_name)) < 0) {
881 return NT_STATUS_NOT_FOUND;
883 if (!service_name) {
884 return NT_STATUS_NO_MEMORY;
886 TALLOC_FREE(jucn->service_name);
887 jucn->service_name = talloc_strdup(ctx, service_name);
888 if (!jucn->service_name) {
889 TALLOC_FREE(pdp);
890 return NT_STATUS_NO_MEMORY;
894 if (!lp_msdfs_root(snum) && (*lp_msdfs_proxy(snum) == '\0')) {
895 DEBUG(3,("get_referred_path: |%s| in dfs path %s is not "
896 "a dfs root.\n",
897 pdp->servicename, dfs_path));
898 TALLOC_FREE(pdp);
899 return NT_STATUS_NOT_FOUND;
903 * Self referrals are tested with a anonymous IPC connection and
904 * a GET_DFS_REFERRAL call to \\server\share. (which means
905 * dp.reqpath[0] points to an empty string). create_conn_struct cd's
906 * into the directory and will fail if it cannot (as the anonymous
907 * user). Cope with this.
910 if (pdp->reqpath[0] == '\0') {
911 char *tmp;
912 struct referral *ref;
914 if (*lp_msdfs_proxy(snum) == '\0') {
915 TALLOC_FREE(pdp);
916 return self_ref(ctx,
917 dfs_path,
918 jucn,
919 consumedcntp,
920 self_referralp);
924 * It's an msdfs proxy share. Redirect to
925 * the configured target share.
928 jucn->referral_count = 1;
929 if ((ref = talloc_zero(ctx, struct referral)) == NULL) {
930 TALLOC_FREE(pdp);
931 return NT_STATUS_NO_MEMORY;
934 if (!(tmp = talloc_strdup(ctx, lp_msdfs_proxy(snum)))) {
935 TALLOC_FREE(pdp);
936 return NT_STATUS_NO_MEMORY;
939 trim_string(tmp, "\\", 0);
941 ref->alternate_path = talloc_asprintf(ctx, "\\%s", tmp);
942 TALLOC_FREE(tmp);
944 if (!ref->alternate_path) {
945 TALLOC_FREE(pdp);
946 return NT_STATUS_NO_MEMORY;
949 if (pdp->reqpath[0] != '\0') {
950 ref->alternate_path = talloc_asprintf_append(
951 ref->alternate_path,
952 "%s",
953 pdp->reqpath);
954 if (!ref->alternate_path) {
955 TALLOC_FREE(pdp);
956 return NT_STATUS_NO_MEMORY;
959 ref->proximity = 0;
960 ref->ttl = REFERRAL_TTL;
961 jucn->referral_list = ref;
962 *consumedcntp = strlen(dfs_path);
963 TALLOC_FREE(pdp);
964 return NT_STATUS_OK;
967 status = create_conn_struct(ctx, sconn, &conn, snum,
968 lp_pathname(snum), NULL, &oldpath);
969 if (!NT_STATUS_IS_OK(status)) {
970 TALLOC_FREE(pdp);
971 return status;
974 /* If this is a DFS path dfs_lookup should return
975 * NT_STATUS_PATH_NOT_COVERED. */
977 status = dfs_path_lookup(ctx, conn, dfs_path, pdp,
978 False, consumedcntp, &targetpath);
980 if (!NT_STATUS_EQUAL(status, NT_STATUS_PATH_NOT_COVERED)) {
981 DEBUG(3,("get_referred_path: No valid referrals for path %s\n",
982 dfs_path));
983 goto err_exit;
986 /* We know this is a valid dfs link. Parse the targetpath. */
987 if (!parse_msdfs_symlink(ctx, targetpath,
988 &jucn->referral_list,
989 &jucn->referral_count)) {
990 DEBUG(3,("get_referred_path: failed to parse symlink "
991 "target %s\n", targetpath ));
992 status = NT_STATUS_NOT_FOUND;
993 goto err_exit;
996 status = NT_STATUS_OK;
997 err_exit:
998 vfs_ChDir(conn, oldpath);
999 SMB_VFS_DISCONNECT(conn);
1000 conn_free(conn);
1001 TALLOC_FREE(pdp);
1002 return status;
1005 static int setup_ver2_dfs_referral(const char *pathname,
1006 char **ppdata,
1007 struct junction_map *junction,
1008 bool self_referral)
1010 char* pdata = *ppdata;
1012 smb_ucs2_t *uni_requestedpath = NULL;
1013 int uni_reqpathoffset1,uni_reqpathoffset2;
1014 int uni_curroffset;
1015 int requestedpathlen=0;
1016 int offset;
1017 int reply_size = 0;
1018 int i=0;
1020 DEBUG(10,("Setting up version2 referral\nRequested path:\n"));
1022 requestedpathlen = rpcstr_push_talloc(talloc_tos(),
1023 &uni_requestedpath, pathname);
1024 if (uni_requestedpath == NULL || requestedpathlen == 0) {
1025 return -1;
1028 if (DEBUGLVL(10)) {
1029 dump_data(0, (unsigned char *)uni_requestedpath,
1030 requestedpathlen);
1033 DEBUG(10,("ref count = %u\n",junction->referral_count));
1035 uni_reqpathoffset1 = REFERRAL_HEADER_SIZE +
1036 VERSION2_REFERRAL_SIZE * junction->referral_count;
1038 uni_reqpathoffset2 = uni_reqpathoffset1 + requestedpathlen;
1040 uni_curroffset = uni_reqpathoffset2 + requestedpathlen;
1042 reply_size = REFERRAL_HEADER_SIZE +
1043 VERSION2_REFERRAL_SIZE*junction->referral_count +
1044 2 * requestedpathlen;
1045 DEBUG(10,("reply_size: %u\n",reply_size));
1047 /* add up the unicode lengths of all the referral paths */
1048 for(i=0;i<junction->referral_count;i++) {
1049 DEBUG(10,("referral %u : %s\n",
1051 junction->referral_list[i].alternate_path));
1052 reply_size +=
1053 (strlen(junction->referral_list[i].alternate_path)+1)*2;
1056 DEBUG(10,("reply_size = %u\n",reply_size));
1057 /* add the unexplained 0x16 bytes */
1058 reply_size += 0x16;
1060 pdata = (char *)SMB_REALLOC(pdata,reply_size);
1061 if(pdata == NULL) {
1062 DEBUG(0,("Realloc failed!\n"));
1063 return -1;
1065 *ppdata = pdata;
1067 /* copy in the dfs requested paths.. required for offset calculations */
1068 memcpy(pdata+uni_reqpathoffset1,uni_requestedpath,requestedpathlen);
1069 memcpy(pdata+uni_reqpathoffset2,uni_requestedpath,requestedpathlen);
1071 /* create the header */
1072 SSVAL(pdata,0,requestedpathlen - 2); /* UCS2 of path consumed minus
1073 2 byte null */
1074 /* number of referral in this pkt */
1075 SSVAL(pdata,2,junction->referral_count);
1076 if(self_referral) {
1077 SIVAL(pdata,4,DFSREF_REFERRAL_SERVER | DFSREF_STORAGE_SERVER);
1078 } else {
1079 SIVAL(pdata,4,DFSREF_STORAGE_SERVER);
1082 offset = 8;
1083 /* add the referral elements */
1084 for(i=0;i<junction->referral_count;i++) {
1085 struct referral* ref = &junction->referral_list[i];
1086 int unilen;
1088 SSVAL(pdata,offset,2); /* version 2 */
1089 SSVAL(pdata,offset+2,VERSION2_REFERRAL_SIZE);
1090 if(self_referral) {
1091 SSVAL(pdata,offset+4,1);
1092 } else {
1093 SSVAL(pdata,offset+4,0);
1096 /* ref_flags :use path_consumed bytes? */
1097 SSVAL(pdata,offset+6,0);
1098 SIVAL(pdata,offset+8,ref->proximity);
1099 SIVAL(pdata,offset+12,ref->ttl);
1101 SSVAL(pdata,offset+16,uni_reqpathoffset1-offset);
1102 SSVAL(pdata,offset+18,uni_reqpathoffset2-offset);
1103 /* copy referred path into current offset */
1104 unilen = rpcstr_push(pdata+uni_curroffset,
1105 ref->alternate_path,
1106 reply_size - uni_curroffset,
1107 STR_UNICODE);
1109 SSVAL(pdata,offset+20,uni_curroffset-offset);
1111 uni_curroffset += unilen;
1112 offset += VERSION2_REFERRAL_SIZE;
1114 /* add in the unexplained 22 (0x16) bytes at the end */
1115 memset(pdata+uni_curroffset,'\0',0x16);
1116 return reply_size;
1119 static int setup_ver3_dfs_referral(const char *pathname,
1120 char **ppdata,
1121 struct junction_map *junction,
1122 bool self_referral)
1124 char *pdata = *ppdata;
1126 smb_ucs2_t *uni_reqpath = NULL;
1127 int uni_reqpathoffset1, uni_reqpathoffset2;
1128 int uni_curroffset;
1129 int reply_size = 0;
1131 int reqpathlen = 0;
1132 int offset,i=0;
1134 DEBUG(10,("setting up version3 referral\n"));
1136 reqpathlen = rpcstr_push_talloc(talloc_tos(), &uni_reqpath, pathname);
1137 if (uni_reqpath == NULL || reqpathlen == 0) {
1138 return -1;
1141 if (DEBUGLVL(10)) {
1142 dump_data(0, (unsigned char *)uni_reqpath,
1143 reqpathlen);
1146 uni_reqpathoffset1 = REFERRAL_HEADER_SIZE +
1147 VERSION3_REFERRAL_SIZE * junction->referral_count;
1148 uni_reqpathoffset2 = uni_reqpathoffset1 + reqpathlen;
1149 reply_size = uni_curroffset = uni_reqpathoffset2 + reqpathlen;
1151 for(i=0;i<junction->referral_count;i++) {
1152 DEBUG(10,("referral %u : %s\n",
1154 junction->referral_list[i].alternate_path));
1155 reply_size +=
1156 (strlen(junction->referral_list[i].alternate_path)+1)*2;
1159 pdata = (char *)SMB_REALLOC(pdata,reply_size);
1160 if(pdata == NULL) {
1161 DEBUG(0,("version3 referral setup:"
1162 "malloc failed for Realloc!\n"));
1163 return -1;
1165 *ppdata = pdata;
1167 /* create the header */
1168 SSVAL(pdata,0,reqpathlen - 2); /* UCS2 of path consumed minus
1169 2 byte null */
1170 SSVAL(pdata,2,junction->referral_count); /* number of referral */
1171 if(self_referral) {
1172 SIVAL(pdata,4,DFSREF_REFERRAL_SERVER | DFSREF_STORAGE_SERVER);
1173 } else {
1174 SIVAL(pdata,4,DFSREF_STORAGE_SERVER);
1177 /* copy in the reqpaths */
1178 memcpy(pdata+uni_reqpathoffset1,uni_reqpath,reqpathlen);
1179 memcpy(pdata+uni_reqpathoffset2,uni_reqpath,reqpathlen);
1181 offset = 8;
1182 for(i=0;i<junction->referral_count;i++) {
1183 struct referral* ref = &(junction->referral_list[i]);
1184 int unilen;
1186 SSVAL(pdata,offset,3); /* version 3 */
1187 SSVAL(pdata,offset+2,VERSION3_REFERRAL_SIZE);
1188 if(self_referral) {
1189 SSVAL(pdata,offset+4,1);
1190 } else {
1191 SSVAL(pdata,offset+4,0);
1194 /* ref_flags :use path_consumed bytes? */
1195 SSVAL(pdata,offset+6,0);
1196 SIVAL(pdata,offset+8,ref->ttl);
1198 SSVAL(pdata,offset+12,uni_reqpathoffset1-offset);
1199 SSVAL(pdata,offset+14,uni_reqpathoffset2-offset);
1200 /* copy referred path into current offset */
1201 unilen = rpcstr_push(pdata+uni_curroffset,ref->alternate_path,
1202 reply_size - uni_curroffset,
1203 STR_UNICODE | STR_TERMINATE);
1204 SSVAL(pdata,offset+16,uni_curroffset-offset);
1205 /* copy 0x10 bytes of 00's in the ServiceSite GUID */
1206 memset(pdata+offset+18,'\0',16);
1208 uni_curroffset += unilen;
1209 offset += VERSION3_REFERRAL_SIZE;
1211 return reply_size;
1214 /******************************************************************
1215 Set up the DFS referral for the dfs pathname. This call returns
1216 the amount of the path covered by this server, and where the
1217 client should be redirected to. This is the meat of the
1218 TRANS2_GET_DFS_REFERRAL call.
1219 ******************************************************************/
1221 int setup_dfs_referral(connection_struct *orig_conn,
1222 const char *dfs_path,
1223 int max_referral_level,
1224 char **ppdata, NTSTATUS *pstatus)
1226 struct junction_map *junction = NULL;
1227 int consumedcnt = 0;
1228 bool self_referral = False;
1229 int reply_size = 0;
1230 char *pathnamep = NULL;
1231 char *local_dfs_path = NULL;
1232 TALLOC_CTX *ctx;
1234 if (!(ctx=talloc_init("setup_dfs_referral"))) {
1235 *pstatus = NT_STATUS_NO_MEMORY;
1236 return -1;
1239 /* get the junction entry */
1240 if (!dfs_path) {
1241 talloc_destroy(ctx);
1242 *pstatus = NT_STATUS_NOT_FOUND;
1243 return -1;
1247 * Trim pathname sent by client so it begins with only one backslash.
1248 * Two backslashes confuse some dfs clients
1251 local_dfs_path = talloc_strdup(ctx,dfs_path);
1252 if (!local_dfs_path) {
1253 *pstatus = NT_STATUS_NO_MEMORY;
1254 talloc_destroy(ctx);
1255 return -1;
1257 pathnamep = local_dfs_path;
1258 while (IS_DIRECTORY_SEP(pathnamep[0]) &&
1259 IS_DIRECTORY_SEP(pathnamep[1])) {
1260 pathnamep++;
1263 junction = talloc_zero(ctx, struct junction_map);
1264 if (!junction) {
1265 *pstatus = NT_STATUS_NO_MEMORY;
1266 talloc_destroy(ctx);
1267 return -1;
1270 /* The following call can change cwd. */
1271 *pstatus = get_referred_path(ctx, pathnamep, orig_conn->sconn,
1272 junction, &consumedcnt, &self_referral);
1273 if (!NT_STATUS_IS_OK(*pstatus)) {
1274 vfs_ChDir(orig_conn,orig_conn->connectpath);
1275 talloc_destroy(ctx);
1276 return -1;
1278 vfs_ChDir(orig_conn,orig_conn->connectpath);
1280 if (!self_referral) {
1281 pathnamep[consumedcnt] = '\0';
1283 if( DEBUGLVL( 3 ) ) {
1284 int i=0;
1285 dbgtext("setup_dfs_referral: Path %s to "
1286 "alternate path(s):",
1287 pathnamep);
1288 for(i=0;i<junction->referral_count;i++)
1289 dbgtext(" %s",
1290 junction->referral_list[i].alternate_path);
1291 dbgtext(".\n");
1295 /* create the referral depeding on version */
1296 DEBUG(10,("max_referral_level :%d\n",max_referral_level));
1298 if (max_referral_level < 2) {
1299 max_referral_level = 2;
1301 if (max_referral_level > 3) {
1302 max_referral_level = 3;
1305 switch(max_referral_level) {
1306 case 2:
1307 reply_size = setup_ver2_dfs_referral(pathnamep,
1308 ppdata, junction,
1309 self_referral);
1310 break;
1311 case 3:
1312 reply_size = setup_ver3_dfs_referral(pathnamep, ppdata,
1313 junction, self_referral);
1314 break;
1315 default:
1316 DEBUG(0,("setup_dfs_referral: Invalid dfs referral "
1317 "version: %d\n",
1318 max_referral_level));
1319 talloc_destroy(ctx);
1320 *pstatus = NT_STATUS_INVALID_LEVEL;
1321 return -1;
1324 if (DEBUGLVL(10)) {
1325 DEBUGADD(0,("DFS Referral pdata:\n"));
1326 dump_data(0,(uint8 *)*ppdata,reply_size);
1329 talloc_destroy(ctx);
1330 *pstatus = NT_STATUS_OK;
1331 return reply_size;
1334 /**********************************************************************
1335 The following functions are called by the NETDFS RPC pipe functions
1336 **********************************************************************/
1338 /*********************************************************************
1339 Creates a junction structure from a DFS pathname
1340 **********************************************************************/
1342 bool create_junction(TALLOC_CTX *ctx,
1343 const char *dfs_path,
1344 bool allow_broken_path,
1345 struct junction_map *jucn)
1347 int snum;
1348 bool dummy;
1349 struct dfs_path *pdp = talloc(ctx,struct dfs_path);
1350 NTSTATUS status;
1352 if (!pdp) {
1353 return False;
1355 status = parse_dfs_path(NULL, dfs_path, False, allow_broken_path,
1356 pdp, &dummy);
1357 if (!NT_STATUS_IS_OK(status)) {
1358 return False;
1361 /* check if path is dfs : validate first token */
1362 if (!is_myname_or_ipaddr(pdp->hostname)) {
1363 DEBUG(4,("create_junction: Invalid hostname %s "
1364 "in dfs path %s\n",
1365 pdp->hostname, dfs_path));
1366 TALLOC_FREE(pdp);
1367 return False;
1370 /* Check for a non-DFS share */
1371 snum = lp_servicenumber(pdp->servicename);
1373 if(snum < 0 || !lp_msdfs_root(snum)) {
1374 DEBUG(4,("create_junction: %s is not an msdfs root.\n",
1375 pdp->servicename));
1376 TALLOC_FREE(pdp);
1377 return False;
1380 jucn->service_name = talloc_strdup(ctx, pdp->servicename);
1381 jucn->volume_name = talloc_strdup(ctx, pdp->reqpath);
1382 jucn->comment = talloc_strdup(ctx, lp_comment(snum));
1384 TALLOC_FREE(pdp);
1385 if (!jucn->service_name || !jucn->volume_name || ! jucn->comment) {
1386 return False;
1388 return True;
1391 /**********************************************************************
1392 Forms a valid Unix pathname from the junction
1393 **********************************************************************/
1395 static bool junction_to_local_path(const struct junction_map *jucn,
1396 char **pp_path_out,
1397 connection_struct **conn_out,
1398 char **oldpath)
1400 int snum;
1401 NTSTATUS status;
1403 snum = lp_servicenumber(jucn->service_name);
1404 if(snum < 0) {
1405 return False;
1407 status = create_conn_struct(talloc_tos(), smbd_server_conn, conn_out,
1408 snum, lp_pathname(snum), NULL, oldpath);
1409 if (!NT_STATUS_IS_OK(status)) {
1410 return False;
1413 *pp_path_out = talloc_asprintf(*conn_out,
1414 "%s/%s",
1415 lp_pathname(snum),
1416 jucn->volume_name);
1417 if (!*pp_path_out) {
1418 vfs_ChDir(*conn_out, *oldpath);
1419 SMB_VFS_DISCONNECT(*conn_out);
1420 conn_free(*conn_out);
1421 return False;
1423 return True;
1426 bool create_msdfs_link(const struct junction_map *jucn)
1428 char *path = NULL;
1429 char *cwd;
1430 char *msdfs_link = NULL;
1431 connection_struct *conn;
1432 int i=0;
1433 bool insert_comma = False;
1434 bool ret = False;
1436 if(!junction_to_local_path(jucn, &path, &conn, &cwd)) {
1437 return False;
1440 /* Form the msdfs_link contents */
1441 msdfs_link = talloc_strdup(conn, "msdfs:");
1442 if (!msdfs_link) {
1443 goto out;
1445 for(i=0; i<jucn->referral_count; i++) {
1446 char *refpath = jucn->referral_list[i].alternate_path;
1448 /* Alternate paths always use Windows separators. */
1449 trim_char(refpath, '\\', '\\');
1450 if(*refpath == '\0') {
1451 if (i == 0) {
1452 insert_comma = False;
1454 continue;
1456 if (i > 0 && insert_comma) {
1457 msdfs_link = talloc_asprintf_append_buffer(msdfs_link,
1458 ",%s",
1459 refpath);
1460 } else {
1461 msdfs_link = talloc_asprintf_append_buffer(msdfs_link,
1462 "%s",
1463 refpath);
1466 if (!msdfs_link) {
1467 goto out;
1469 if (!insert_comma) {
1470 insert_comma = True;
1474 DEBUG(5,("create_msdfs_link: Creating new msdfs link: %s -> %s\n",
1475 path, msdfs_link));
1477 if(SMB_VFS_SYMLINK(conn, msdfs_link, path) < 0) {
1478 if (errno == EEXIST) {
1479 struct smb_filename *smb_fname = NULL;
1480 NTSTATUS status;
1482 status = create_synthetic_smb_fname(talloc_tos(), path,
1483 NULL, NULL,
1484 &smb_fname);
1485 if (!NT_STATUS_IS_OK(status)) {
1486 errno = map_errno_from_nt_status(status);
1487 goto out;
1490 if(SMB_VFS_UNLINK(conn, smb_fname)!=0) {
1491 TALLOC_FREE(smb_fname);
1492 goto out;
1494 TALLOC_FREE(smb_fname);
1496 if (SMB_VFS_SYMLINK(conn, msdfs_link, path) < 0) {
1497 DEBUG(1,("create_msdfs_link: symlink failed "
1498 "%s -> %s\nError: %s\n",
1499 path, msdfs_link, strerror(errno)));
1500 goto out;
1504 ret = True;
1506 out:
1507 vfs_ChDir(conn, cwd);
1508 SMB_VFS_DISCONNECT(conn);
1509 conn_free(conn);
1510 return ret;
1513 bool remove_msdfs_link(const struct junction_map *jucn)
1515 char *path = NULL;
1516 char *cwd;
1517 connection_struct *conn;
1518 bool ret = False;
1519 struct smb_filename *smb_fname = NULL;
1520 NTSTATUS status;
1522 if (!junction_to_local_path(jucn, &path, &conn, &cwd)) {
1523 return false;
1526 status = create_synthetic_smb_fname(talloc_tos(), path,
1527 NULL, NULL,
1528 &smb_fname);
1529 if (!NT_STATUS_IS_OK(status)) {
1530 errno = map_errno_from_nt_status(status);
1531 return false;
1534 if( SMB_VFS_UNLINK(conn, smb_fname) == 0 ) {
1535 ret = True;
1538 TALLOC_FREE(smb_fname);
1539 vfs_ChDir(conn, cwd);
1540 SMB_VFS_DISCONNECT(conn);
1541 conn_free(conn);
1542 return ret;
1545 /*********************************************************************
1546 Return the number of DFS links at the root of this share.
1547 *********************************************************************/
1549 static int count_dfs_links(TALLOC_CTX *ctx, int snum)
1551 size_t cnt = 0;
1552 SMB_STRUCT_DIR *dirp = NULL;
1553 const char *dname = NULL;
1554 char *talloced = NULL;
1555 const char *connect_path = lp_pathname(snum);
1556 const char *msdfs_proxy = lp_msdfs_proxy(snum);
1557 connection_struct *conn;
1558 NTSTATUS status;
1559 char *cwd;
1561 if(*connect_path == '\0') {
1562 return 0;
1566 * Fake up a connection struct for the VFS layer.
1569 status = create_conn_struct(talloc_tos(), smbd_server_conn, &conn,
1570 snum, connect_path, NULL, &cwd);
1571 if (!NT_STATUS_IS_OK(status)) {
1572 DEBUG(3, ("create_conn_struct failed: %s\n",
1573 nt_errstr(status)));
1574 return 0;
1577 /* Count a link for the msdfs root - convention */
1578 cnt = 1;
1580 /* No more links if this is an msdfs proxy. */
1581 if (*msdfs_proxy != '\0') {
1582 goto out;
1585 /* Now enumerate all dfs links */
1586 dirp = SMB_VFS_OPENDIR(conn, ".", NULL, 0);
1587 if(!dirp) {
1588 goto out;
1591 while ((dname = vfs_readdirname(conn, dirp, NULL, &talloced))
1592 != NULL) {
1593 if (is_msdfs_link(conn,
1594 dname,
1595 NULL)) {
1596 cnt++;
1598 TALLOC_FREE(talloced);
1601 SMB_VFS_CLOSEDIR(conn,dirp);
1603 out:
1604 vfs_ChDir(conn, cwd);
1605 SMB_VFS_DISCONNECT(conn);
1606 conn_free(conn);
1607 return cnt;
1610 /*********************************************************************
1611 *********************************************************************/
1613 static int form_junctions(TALLOC_CTX *ctx,
1614 int snum,
1615 struct junction_map *jucn,
1616 size_t jn_remain)
1618 size_t cnt = 0;
1619 SMB_STRUCT_DIR *dirp = NULL;
1620 const char *dname = NULL;
1621 char *talloced = NULL;
1622 const char *connect_path = lp_pathname(snum);
1623 char *service_name = lp_servicename(snum);
1624 const char *msdfs_proxy = lp_msdfs_proxy(snum);
1625 connection_struct *conn;
1626 struct referral *ref = NULL;
1627 char *cwd;
1628 NTSTATUS status;
1630 if (jn_remain == 0) {
1631 return 0;
1634 if(*connect_path == '\0') {
1635 return 0;
1639 * Fake up a connection struct for the VFS layer.
1642 status = create_conn_struct(ctx, smbd_server_conn, &conn, snum, connect_path, NULL,
1643 &cwd);
1644 if (!NT_STATUS_IS_OK(status)) {
1645 DEBUG(3, ("create_conn_struct failed: %s\n",
1646 nt_errstr(status)));
1647 return 0;
1650 /* form a junction for the msdfs root - convention
1651 DO NOT REMOVE THIS: NT clients will not work with us
1652 if this is not present
1654 jucn[cnt].service_name = talloc_strdup(ctx,service_name);
1655 jucn[cnt].volume_name = talloc_strdup(ctx, "");
1656 if (!jucn[cnt].service_name || !jucn[cnt].volume_name) {
1657 goto out;
1659 jucn[cnt].comment = "";
1660 jucn[cnt].referral_count = 1;
1662 ref = jucn[cnt].referral_list = talloc_zero(ctx, struct referral);
1663 if (jucn[cnt].referral_list == NULL) {
1664 goto out;
1667 ref->proximity = 0;
1668 ref->ttl = REFERRAL_TTL;
1669 if (*msdfs_proxy != '\0') {
1670 ref->alternate_path = talloc_strdup(ctx,
1671 msdfs_proxy);
1672 } else {
1673 ref->alternate_path = talloc_asprintf(ctx,
1674 "\\\\%s\\%s",
1675 get_local_machine_name(),
1676 service_name);
1679 if (!ref->alternate_path) {
1680 goto out;
1682 cnt++;
1684 /* Don't enumerate if we're an msdfs proxy. */
1685 if (*msdfs_proxy != '\0') {
1686 goto out;
1689 /* Now enumerate all dfs links */
1690 dirp = SMB_VFS_OPENDIR(conn, ".", NULL, 0);
1691 if(!dirp) {
1692 goto out;
1695 while ((dname = vfs_readdirname(conn, dirp, NULL, &talloced))
1696 != NULL) {
1697 char *link_target = NULL;
1698 if (cnt >= jn_remain) {
1699 DEBUG(2, ("form_junctions: ran out of MSDFS "
1700 "junction slots"));
1701 TALLOC_FREE(talloced);
1702 goto out;
1704 if (is_msdfs_link_internal(ctx,
1705 conn,
1706 dname, &link_target,
1707 NULL)) {
1708 if (parse_msdfs_symlink(ctx,
1709 link_target,
1710 &jucn[cnt].referral_list,
1711 &jucn[cnt].referral_count)) {
1713 jucn[cnt].service_name = talloc_strdup(ctx,
1714 service_name);
1715 jucn[cnt].volume_name = talloc_strdup(ctx,
1716 dname);
1717 if (!jucn[cnt].service_name ||
1718 !jucn[cnt].volume_name) {
1719 TALLOC_FREE(talloced);
1720 goto out;
1722 jucn[cnt].comment = "";
1723 cnt++;
1725 TALLOC_FREE(link_target);
1727 TALLOC_FREE(talloced);
1730 out:
1732 if (dirp) {
1733 SMB_VFS_CLOSEDIR(conn,dirp);
1736 vfs_ChDir(conn, cwd);
1737 conn_free(conn);
1738 return cnt;
1741 struct junction_map *enum_msdfs_links(struct smbd_server_connection *sconn,
1742 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(sconn);
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 !smbd_server_conn->using_smb2,
1808 pp_name_out,
1809 &path_contains_wcard);
1811 if (NT_STATUS_IS_OK(status) && ppath_contains_wcard != NULL) {
1812 *ppath_contains_wcard = path_contains_wcard;
1814 } else {
1816 * Cheat and just return a copy of the in ptr.
1817 * Once srvstr_get_path() uses talloc it'll
1818 * be a talloced ptr anyway.
1820 *pp_name_out = discard_const_p(char, name_in);
1822 return status;