s3-torture/denytest.c: replace cli_read_old() with cli_read()
[Samba/gebeck_regimport.git] / source3 / smbd / msdfs.c
blob702dd1d28d0da55c102d96ccdc3dc323f7e68ad7
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 struct dfs_path *pdp, /* MUST BE TALLOCED */
57 bool *ppath_contains_wcard)
59 struct smbd_server_connection *sconn = smbd_server_conn;
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 (!sconn->using_smb2 && (*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 connection_struct **pconn,
226 int snum,
227 const char *path,
228 const struct auth_session_info *session_info,
229 char **poldcwd)
231 connection_struct *conn;
232 char *connpath;
233 char *oldcwd;
234 const char *vfs_user;
236 conn = talloc_zero(ctx, connection_struct);
237 if (conn == NULL) {
238 return NT_STATUS_NO_MEMORY;
241 connpath = talloc_strdup(conn, path);
242 if (!connpath) {
243 TALLOC_FREE(conn);
244 return NT_STATUS_NO_MEMORY;
246 connpath = talloc_string_sub(conn,
247 connpath,
248 "%S",
249 lp_servicename(snum));
250 if (!connpath) {
251 TALLOC_FREE(conn);
252 return NT_STATUS_NO_MEMORY;
255 /* needed for smbd_vfs_init() */
257 if (!(conn->params = talloc_zero(conn, struct share_params))) {
258 DEBUG(0, ("TALLOC failed\n"));
259 TALLOC_FREE(conn);
260 return NT_STATUS_NO_MEMORY;
263 conn->params->service = snum;
265 conn->sconn = smbd_server_conn;
266 conn->sconn->num_tcons_open++;
268 if (session_info != NULL) {
269 conn->session_info = copy_session_info(conn, session_info);
270 if (conn->session_info == NULL) {
271 DEBUG(0, ("copy_serverinfo failed\n"));
272 TALLOC_FREE(conn);
273 return NT_STATUS_NO_MEMORY;
275 vfs_user = conn->session_info->unix_info->unix_name;
276 } else {
277 /* use current authenticated user in absence of session_info */
278 vfs_user = get_current_username();
281 set_conn_connectpath(conn, connpath);
284 * New code to check if there's a share security descripter
285 * added from NT server manager. This is done after the
286 * smb.conf checks are done as we need a uid and token. JRA.
289 if (conn->session_info) {
290 share_access_check(conn->session_info->security_token,
291 lp_servicename(snum), MAXIMUM_ALLOWED_ACCESS,
292 &conn->share_access);
294 if ((conn->share_access & FILE_WRITE_DATA) == 0) {
295 if ((conn->share_access & FILE_READ_DATA) == 0) {
296 /* No access, read or write. */
297 DEBUG(0,("create_conn_struct: connection to %s "
298 "denied due to security "
299 "descriptor.\n",
300 lp_servicename(snum)));
301 conn_free(conn);
302 return NT_STATUS_ACCESS_DENIED;
303 } else {
304 conn->read_only = true;
307 } else {
308 conn->share_access = 0;
309 conn->read_only = true;
312 if (!smbd_vfs_init(conn)) {
313 NTSTATUS status = map_nt_error_from_unix(errno);
314 DEBUG(0,("create_conn_struct: smbd_vfs_init failed.\n"));
315 conn_free(conn);
316 return status;
319 /* this must be the first filesystem operation that we do */
320 if (SMB_VFS_CONNECT(conn, lp_servicename(snum), vfs_user) < 0) {
321 DEBUG(0,("VFS connect failed!\n"));
322 conn_free(conn);
323 return NT_STATUS_UNSUCCESSFUL;
326 conn->fs_capabilities = SMB_VFS_FS_CAPABILITIES(conn, &conn->ts_res);
329 * Windows seems to insist on doing trans2getdfsreferral() calls on
330 * the IPC$ share as the anonymous user. If we try to chdir as that
331 * user we will fail.... WTF ? JRA.
334 oldcwd = vfs_GetWd(ctx, conn);
335 if (oldcwd == NULL) {
336 NTSTATUS status = map_nt_error_from_unix(errno);
337 DEBUG(3, ("vfs_GetWd failed: %s\n", strerror(errno)));
338 conn_free(conn);
339 return status;
342 if (vfs_ChDir(conn,conn->connectpath) != 0) {
343 NTSTATUS status = map_nt_error_from_unix(errno);
344 DEBUG(3,("create_conn_struct: Can't ChDir to new conn path %s. "
345 "Error was %s\n",
346 conn->connectpath, strerror(errno) ));
347 conn_free(conn);
348 return status;
351 *pconn = conn;
352 *poldcwd = oldcwd;
354 return NT_STATUS_OK;
357 /**********************************************************************
358 Parse the contents of a symlink to verify if it is an msdfs referral
359 A valid referral is of the form:
361 msdfs:server1\share1,server2\share2
362 msdfs:server1\share1\pathname,server2\share2\pathname
363 msdfs:server1/share1,server2/share2
364 msdfs:server1/share1/pathname,server2/share2/pathname.
366 Note that the alternate paths returned here must be of the canonicalized
367 form:
369 \server\share or
370 \server\share\path\to\file,
372 even in posix path mode. This is because we have no knowledge if the
373 server we're referring to understands posix paths.
374 **********************************************************************/
376 static bool parse_msdfs_symlink(TALLOC_CTX *ctx,
377 const char *target,
378 struct referral **preflist,
379 int *refcount)
381 char *temp = NULL;
382 char *prot;
383 char **alt_path = NULL;
384 int count = 0, i;
385 struct referral *reflist;
386 char *saveptr;
388 temp = talloc_strdup(ctx, target);
389 if (!temp) {
390 return False;
392 prot = strtok_r(temp, ":", &saveptr);
393 if (!prot) {
394 DEBUG(0,("parse_msdfs_symlink: invalid path !\n"));
395 return False;
398 alt_path = talloc_array(ctx, char *, MAX_REFERRAL_COUNT);
399 if (!alt_path) {
400 return False;
403 /* parse out the alternate paths */
404 while((count<MAX_REFERRAL_COUNT) &&
405 ((alt_path[count] = strtok_r(NULL, ",", &saveptr)) != NULL)) {
406 count++;
409 DEBUG(10,("parse_msdfs_symlink: count=%d\n", count));
411 if (count) {
412 reflist = *preflist = talloc_zero_array(ctx,
413 struct referral, count);
414 if(reflist == NULL) {
415 TALLOC_FREE(alt_path);
416 return False;
418 } else {
419 reflist = *preflist = NULL;
422 for(i=0;i<count;i++) {
423 char *p;
425 /* Canonicalize link target.
426 * Replace all /'s in the path by a \ */
427 string_replace(alt_path[i], '/', '\\');
429 /* Remove leading '\\'s */
430 p = alt_path[i];
431 while (*p && (*p == '\\')) {
432 p++;
435 reflist[i].alternate_path = talloc_asprintf(ctx,
436 "\\%s",
438 if (!reflist[i].alternate_path) {
439 return False;
442 reflist[i].proximity = 0;
443 reflist[i].ttl = REFERRAL_TTL;
444 DEBUG(10, ("parse_msdfs_symlink: Created alt path: %s\n",
445 reflist[i].alternate_path));
448 *refcount = count;
450 TALLOC_FREE(alt_path);
451 return True;
454 /**********************************************************************
455 Returns true if the unix path is a valid msdfs symlink and also
456 returns the target string from inside the link.
457 **********************************************************************/
459 static bool is_msdfs_link_internal(TALLOC_CTX *ctx,
460 connection_struct *conn,
461 const char *path,
462 char **pp_link_target,
463 SMB_STRUCT_STAT *sbufp)
465 int referral_len = 0;
466 #if defined(HAVE_BROKEN_READLINK)
467 char link_target_buf[PATH_MAX];
468 #else
469 char link_target_buf[7];
470 #endif
471 size_t bufsize = 0;
472 char *link_target = NULL;
473 struct smb_filename smb_fname;
475 if (pp_link_target) {
476 bufsize = 1024;
477 link_target = talloc_array(ctx, char, bufsize);
478 if (!link_target) {
479 return False;
481 *pp_link_target = link_target;
482 } else {
483 bufsize = sizeof(link_target_buf);
484 link_target = link_target_buf;
487 ZERO_STRUCT(smb_fname);
488 smb_fname.base_name = discard_const_p(char, path);
490 if (SMB_VFS_LSTAT(conn, &smb_fname) != 0) {
491 DEBUG(5,("is_msdfs_link_read_target: %s does not exist.\n",
492 path));
493 goto err;
495 if (!S_ISLNK(smb_fname.st.st_ex_mode)) {
496 DEBUG(5,("is_msdfs_link_read_target: %s is not a link.\n",
497 path));
498 goto err;
500 if (sbufp != NULL) {
501 *sbufp = smb_fname.st;
504 referral_len = SMB_VFS_READLINK(conn, path, link_target, bufsize - 1);
505 if (referral_len == -1) {
506 DEBUG(0,("is_msdfs_link_read_target: Error reading "
507 "msdfs link %s: %s\n",
508 path, strerror(errno)));
509 goto err;
511 link_target[referral_len] = '\0';
513 DEBUG(5,("is_msdfs_link_internal: %s -> %s\n",path,
514 link_target));
516 if (!strnequal(link_target, "msdfs:", 6)) {
517 goto err;
519 return True;
521 err:
523 if (link_target != link_target_buf) {
524 TALLOC_FREE(link_target);
526 return False;
529 /**********************************************************************
530 Returns true if the unix path is a valid msdfs symlink.
531 **********************************************************************/
533 bool is_msdfs_link(connection_struct *conn,
534 const char *path,
535 SMB_STRUCT_STAT *sbufp)
537 return is_msdfs_link_internal(talloc_tos(),
538 conn,
539 path,
540 NULL,
541 sbufp);
544 /*****************************************************************
545 Used by other functions to decide if a dfs path is remote,
546 and to get the list of referred locations for that remote path.
548 search_flag: For findfirsts, dfs links themselves are not
549 redirected, but paths beyond the links are. For normal smb calls,
550 even dfs links need to be redirected.
552 consumedcntp: how much of the dfs path is being redirected. the client
553 should try the remaining path on the redirected server.
555 If this returns NT_STATUS_PATH_NOT_COVERED the contents of the msdfs
556 link redirect are in targetpath.
557 *****************************************************************/
559 static NTSTATUS dfs_path_lookup(TALLOC_CTX *ctx,
560 connection_struct *conn,
561 const char *dfspath, /* Incoming complete dfs path */
562 const struct dfs_path *pdp, /* Parsed out
563 server+share+extrapath. */
564 bool search_flag, /* Called from a findfirst ? */
565 int *consumedcntp,
566 char **pp_targetpath)
568 char *p = NULL;
569 char *q = NULL;
570 NTSTATUS status;
571 struct smb_filename *smb_fname = NULL;
572 char *canon_dfspath = NULL; /* Canonicalized dfs path. (only '/'
573 components). */
575 DEBUG(10,("dfs_path_lookup: Conn path = %s reqpath = %s\n",
576 conn->connectpath, pdp->reqpath));
579 * Note the unix path conversion here we're doing we can
580 * throw away. We're looking for a symlink for a dfs
581 * resolution, if we don't find it we'll do another
582 * unix_convert later in the codepath.
583 * If we needed to remember what we'd resolved in
584 * dp->reqpath (as the original code did) we'd
585 * copy (localhost, dp->reqpath) on any code
586 * path below that returns True - but I don't
587 * think this is needed. JRA.
590 status = unix_convert(ctx, conn, pdp->reqpath, &smb_fname,
591 search_flag ? UCF_ALWAYS_ALLOW_WCARD_LCOMP : 0);
593 if (!NT_STATUS_IS_OK(status)) {
594 if (!NT_STATUS_EQUAL(status,
595 NT_STATUS_OBJECT_PATH_NOT_FOUND)) {
596 return status;
599 /* Create an smb_fname to use below. */
600 status = create_synthetic_smb_fname(ctx, pdp->reqpath, NULL,
601 NULL, &smb_fname);
602 if (!NT_STATUS_IS_OK(status)) {
603 return status;
607 /* Optimization - check if we can redirect the whole path. */
609 if (is_msdfs_link_internal(ctx, conn, smb_fname->base_name,
610 pp_targetpath, NULL)) {
611 if (search_flag) {
612 DEBUG(6,("dfs_path_lookup (FindFirst) No redirection "
613 "for dfs link %s.\n", dfspath));
614 status = NT_STATUS_OK;
615 goto out;
618 DEBUG(6,("dfs_path_lookup: %s resolves to a "
619 "valid dfs link %s.\n", dfspath,
620 pp_targetpath ? *pp_targetpath : ""));
622 if (consumedcntp) {
623 *consumedcntp = strlen(dfspath);
625 status = NT_STATUS_PATH_NOT_COVERED;
626 goto out;
629 /* Prepare to test only for '/' components in the given path,
630 * so if a Windows path replace all '\\' characters with '/'.
631 * For a POSIX DFS path we know all separators are already '/'. */
633 canon_dfspath = talloc_strdup(ctx, dfspath);
634 if (!canon_dfspath) {
635 status = NT_STATUS_NO_MEMORY;
636 goto out;
638 if (!pdp->posix_path) {
639 string_replace(canon_dfspath, '\\', '/');
643 * localpath comes out of unix_convert, so it has
644 * no trailing backslash. Make sure that canon_dfspath hasn't either.
645 * Fix for bug #4860 from Jan Martin <Jan.Martin@rwedea.com>.
648 trim_char(canon_dfspath,0,'/');
651 * Redirect if any component in the path is a link.
652 * We do this by walking backwards through the
653 * local path, chopping off the last component
654 * in both the local path and the canonicalized
655 * DFS path. If we hit a DFS link then we're done.
658 p = strrchr_m(smb_fname->base_name, '/');
659 if (consumedcntp) {
660 q = strrchr_m(canon_dfspath, '/');
663 while (p) {
664 *p = '\0';
665 if (q) {
666 *q = '\0';
669 if (is_msdfs_link_internal(ctx, conn,
670 smb_fname->base_name, pp_targetpath,
671 NULL)) {
672 DEBUG(4, ("dfs_path_lookup: Redirecting %s because "
673 "parent %s is dfs link\n", dfspath,
674 smb_fname_str_dbg(smb_fname)));
676 if (consumedcntp) {
677 *consumedcntp = strlen(canon_dfspath);
678 DEBUG(10, ("dfs_path_lookup: Path consumed: %s "
679 "(%d)\n",
680 canon_dfspath,
681 *consumedcntp));
684 status = NT_STATUS_PATH_NOT_COVERED;
685 goto out;
688 /* Step back on the filesystem. */
689 p = strrchr_m(smb_fname->base_name, '/');
691 if (consumedcntp) {
692 /* And in the canonicalized dfs path. */
693 q = strrchr_m(canon_dfspath, '/');
697 status = NT_STATUS_OK;
698 out:
699 TALLOC_FREE(smb_fname);
700 return status;
703 /*****************************************************************
704 Decides if a dfs pathname should be redirected or not.
705 If not, the pathname is converted to a tcon-relative local unix path
707 search_wcard_flag: this flag performs 2 functions both related
708 to searches. See resolve_dfs_path() and parse_dfs_path_XX()
709 for details.
711 This function can return NT_STATUS_OK, meaning use the returned path as-is
712 (mapped into a local path).
713 or NT_STATUS_NOT_COVERED meaning return a DFS redirect, or
714 any other NT_STATUS error which is a genuine error to be
715 returned to the client.
716 *****************************************************************/
718 static NTSTATUS dfs_redirect(TALLOC_CTX *ctx,
719 connection_struct *conn,
720 const char *path_in,
721 bool search_wcard_flag,
722 char **pp_path_out,
723 bool *ppath_contains_wcard)
725 NTSTATUS status;
726 struct dfs_path *pdp = talloc(ctx, struct dfs_path);
728 if (!pdp) {
729 return NT_STATUS_NO_MEMORY;
732 status = parse_dfs_path(conn, path_in, search_wcard_flag, pdp,
733 ppath_contains_wcard);
734 if (!NT_STATUS_IS_OK(status)) {
735 TALLOC_FREE(pdp);
736 return status;
739 if (pdp->reqpath[0] == '\0') {
740 TALLOC_FREE(pdp);
741 *pp_path_out = talloc_strdup(ctx, "");
742 if (!*pp_path_out) {
743 return NT_STATUS_NO_MEMORY;
745 DEBUG(5,("dfs_redirect: self-referral.\n"));
746 return NT_STATUS_OK;
749 /* If dfs pathname for a non-dfs share, convert to tcon-relative
750 path and return OK */
752 if (!lp_msdfs_root(SNUM(conn))) {
753 *pp_path_out = talloc_strdup(ctx, pdp->reqpath);
754 TALLOC_FREE(pdp);
755 if (!*pp_path_out) {
756 return NT_STATUS_NO_MEMORY;
758 return NT_STATUS_OK;
761 /* If it looked like a local path (zero hostname/servicename)
762 * just treat as a tcon-relative path. */
764 if (pdp->hostname[0] == '\0' && pdp->servicename[0] == '\0') {
765 *pp_path_out = talloc_strdup(ctx, pdp->reqpath);
766 TALLOC_FREE(pdp);
767 if (!*pp_path_out) {
768 return NT_STATUS_NO_MEMORY;
770 return NT_STATUS_OK;
773 if (!( strequal(pdp->servicename, lp_servicename(SNUM(conn)))
774 || (strequal(pdp->servicename, HOMES_NAME)
775 && strequal(lp_servicename(SNUM(conn)),
776 conn->session_info->unix_info->sanitized_username) )) ) {
778 /* The given sharename doesn't match this connection. */
779 TALLOC_FREE(pdp);
781 return NT_STATUS_OBJECT_PATH_NOT_FOUND;
784 status = dfs_path_lookup(ctx, conn, path_in, pdp,
785 search_wcard_flag, NULL, NULL);
786 if (!NT_STATUS_IS_OK(status)) {
787 if (NT_STATUS_EQUAL(status, NT_STATUS_PATH_NOT_COVERED)) {
788 DEBUG(3,("dfs_redirect: Redirecting %s\n", path_in));
789 } else {
790 DEBUG(10,("dfs_redirect: dfs_path_lookup "
791 "failed for %s with %s\n",
792 path_in, nt_errstr(status) ));
794 return status;
797 DEBUG(3,("dfs_redirect: Not redirecting %s.\n", path_in));
799 /* Form non-dfs tcon-relative path */
800 *pp_path_out = talloc_strdup(ctx, pdp->reqpath);
801 TALLOC_FREE(pdp);
802 if (!*pp_path_out) {
803 return NT_STATUS_NO_MEMORY;
806 DEBUG(3,("dfs_redirect: Path %s converted to non-dfs path %s\n",
807 path_in,
808 *pp_path_out));
810 return NT_STATUS_OK;
813 /**********************************************************************
814 Return a self referral.
815 **********************************************************************/
817 static NTSTATUS self_ref(TALLOC_CTX *ctx,
818 const char *dfs_path,
819 struct junction_map *jucn,
820 int *consumedcntp,
821 bool *self_referralp)
823 struct referral *ref;
825 *self_referralp = True;
827 jucn->referral_count = 1;
828 if((ref = talloc_zero(ctx, struct referral)) == NULL) {
829 return NT_STATUS_NO_MEMORY;
832 ref->alternate_path = talloc_strdup(ctx, dfs_path);
833 if (!ref->alternate_path) {
834 return NT_STATUS_NO_MEMORY;
836 ref->proximity = 0;
837 ref->ttl = REFERRAL_TTL;
838 jucn->referral_list = ref;
839 *consumedcntp = strlen(dfs_path);
840 return NT_STATUS_OK;
843 /**********************************************************************
844 Gets valid referrals for a dfs path and fills up the
845 junction_map structure.
846 **********************************************************************/
848 NTSTATUS get_referred_path(TALLOC_CTX *ctx,
849 const char *dfs_path,
850 struct junction_map *jucn,
851 int *consumedcntp,
852 bool *self_referralp)
854 struct connection_struct *conn;
855 char *targetpath = NULL;
856 int snum;
857 NTSTATUS status = NT_STATUS_NOT_FOUND;
858 bool dummy;
859 struct dfs_path *pdp = talloc(ctx, struct dfs_path);
860 char *oldpath;
862 if (!pdp) {
863 return NT_STATUS_NO_MEMORY;
866 *self_referralp = False;
868 status = parse_dfs_path(NULL, dfs_path, False, pdp, &dummy);
869 if (!NT_STATUS_IS_OK(status)) {
870 return status;
873 jucn->service_name = talloc_strdup(ctx, pdp->servicename);
874 jucn->volume_name = talloc_strdup(ctx, pdp->reqpath);
875 if (!jucn->service_name || !jucn->volume_name) {
876 TALLOC_FREE(pdp);
877 return NT_STATUS_NO_MEMORY;
880 /* Verify the share is a dfs root */
881 snum = lp_servicenumber(jucn->service_name);
882 if(snum < 0) {
883 char *service_name = NULL;
884 if ((snum = find_service(ctx, jucn->service_name, &service_name)) < 0) {
885 return NT_STATUS_NOT_FOUND;
887 if (!service_name) {
888 return NT_STATUS_NO_MEMORY;
890 TALLOC_FREE(jucn->service_name);
891 jucn->service_name = talloc_strdup(ctx, service_name);
892 if (!jucn->service_name) {
893 TALLOC_FREE(pdp);
894 return NT_STATUS_NO_MEMORY;
898 if (!lp_msdfs_root(snum) && (*lp_msdfs_proxy(snum) == '\0')) {
899 DEBUG(3,("get_referred_path: |%s| in dfs path %s is not "
900 "a dfs root.\n",
901 pdp->servicename, dfs_path));
902 TALLOC_FREE(pdp);
903 return NT_STATUS_NOT_FOUND;
907 * Self referrals are tested with a anonymous IPC connection and
908 * a GET_DFS_REFERRAL call to \\server\share. (which means
909 * dp.reqpath[0] points to an empty string). create_conn_struct cd's
910 * into the directory and will fail if it cannot (as the anonymous
911 * user). Cope with this.
914 if (pdp->reqpath[0] == '\0') {
915 char *tmp;
916 struct referral *ref;
918 if (*lp_msdfs_proxy(snum) == '\0') {
919 TALLOC_FREE(pdp);
920 return self_ref(ctx,
921 dfs_path,
922 jucn,
923 consumedcntp,
924 self_referralp);
928 * It's an msdfs proxy share. Redirect to
929 * the configured target share.
932 jucn->referral_count = 1;
933 if ((ref = talloc_zero(ctx, struct referral)) == NULL) {
934 TALLOC_FREE(pdp);
935 return NT_STATUS_NO_MEMORY;
938 if (!(tmp = talloc_strdup(ctx, lp_msdfs_proxy(snum)))) {
939 TALLOC_FREE(pdp);
940 return NT_STATUS_NO_MEMORY;
943 trim_string(tmp, "\\", 0);
945 ref->alternate_path = talloc_asprintf(ctx, "\\%s", tmp);
946 TALLOC_FREE(tmp);
948 if (!ref->alternate_path) {
949 TALLOC_FREE(pdp);
950 return NT_STATUS_NO_MEMORY;
953 if (pdp->reqpath[0] != '\0') {
954 ref->alternate_path = talloc_asprintf_append(
955 ref->alternate_path,
956 "%s",
957 pdp->reqpath);
958 if (!ref->alternate_path) {
959 TALLOC_FREE(pdp);
960 return NT_STATUS_NO_MEMORY;
963 ref->proximity = 0;
964 ref->ttl = REFERRAL_TTL;
965 jucn->referral_list = ref;
966 *consumedcntp = strlen(dfs_path);
967 TALLOC_FREE(pdp);
968 return NT_STATUS_OK;
971 status = create_conn_struct(ctx, &conn, snum, lp_pathname(snum),
972 NULL, &oldpath);
973 if (!NT_STATUS_IS_OK(status)) {
974 TALLOC_FREE(pdp);
975 return status;
978 /* If this is a DFS path dfs_lookup should return
979 * NT_STATUS_PATH_NOT_COVERED. */
981 status = dfs_path_lookup(ctx, conn, dfs_path, pdp,
982 False, consumedcntp, &targetpath);
984 if (!NT_STATUS_EQUAL(status, NT_STATUS_PATH_NOT_COVERED)) {
985 DEBUG(3,("get_referred_path: No valid referrals for path %s\n",
986 dfs_path));
987 goto err_exit;
990 /* We know this is a valid dfs link. Parse the targetpath. */
991 if (!parse_msdfs_symlink(ctx, targetpath,
992 &jucn->referral_list,
993 &jucn->referral_count)) {
994 DEBUG(3,("get_referred_path: failed to parse symlink "
995 "target %s\n", targetpath ));
996 status = NT_STATUS_NOT_FOUND;
997 goto err_exit;
1000 status = NT_STATUS_OK;
1001 err_exit:
1002 vfs_ChDir(conn, oldpath);
1003 SMB_VFS_DISCONNECT(conn);
1004 conn_free(conn);
1005 TALLOC_FREE(pdp);
1006 return status;
1009 static int setup_ver2_dfs_referral(const char *pathname,
1010 char **ppdata,
1011 struct junction_map *junction,
1012 bool self_referral)
1014 char* pdata = *ppdata;
1016 smb_ucs2_t *uni_requestedpath = NULL;
1017 int uni_reqpathoffset1,uni_reqpathoffset2;
1018 int uni_curroffset;
1019 int requestedpathlen=0;
1020 int offset;
1021 int reply_size = 0;
1022 int i=0;
1024 DEBUG(10,("Setting up version2 referral\nRequested path:\n"));
1026 requestedpathlen = rpcstr_push_talloc(talloc_tos(),
1027 &uni_requestedpath, pathname);
1028 if (uni_requestedpath == NULL || requestedpathlen == 0) {
1029 return -1;
1032 if (DEBUGLVL(10)) {
1033 dump_data(0, (unsigned char *)uni_requestedpath,
1034 requestedpathlen);
1037 DEBUG(10,("ref count = %u\n",junction->referral_count));
1039 uni_reqpathoffset1 = REFERRAL_HEADER_SIZE +
1040 VERSION2_REFERRAL_SIZE * junction->referral_count;
1042 uni_reqpathoffset2 = uni_reqpathoffset1 + requestedpathlen;
1044 uni_curroffset = uni_reqpathoffset2 + requestedpathlen;
1046 reply_size = REFERRAL_HEADER_SIZE +
1047 VERSION2_REFERRAL_SIZE*junction->referral_count +
1048 2 * requestedpathlen;
1049 DEBUG(10,("reply_size: %u\n",reply_size));
1051 /* add up the unicode lengths of all the referral paths */
1052 for(i=0;i<junction->referral_count;i++) {
1053 DEBUG(10,("referral %u : %s\n",
1055 junction->referral_list[i].alternate_path));
1056 reply_size +=
1057 (strlen(junction->referral_list[i].alternate_path)+1)*2;
1060 DEBUG(10,("reply_size = %u\n",reply_size));
1061 /* add the unexplained 0x16 bytes */
1062 reply_size += 0x16;
1064 pdata = (char *)SMB_REALLOC(pdata,reply_size);
1065 if(pdata == NULL) {
1066 DEBUG(0,("Realloc failed!\n"));
1067 return -1;
1069 *ppdata = pdata;
1071 /* copy in the dfs requested paths.. required for offset calculations */
1072 memcpy(pdata+uni_reqpathoffset1,uni_requestedpath,requestedpathlen);
1073 memcpy(pdata+uni_reqpathoffset2,uni_requestedpath,requestedpathlen);
1075 /* create the header */
1076 SSVAL(pdata,0,requestedpathlen - 2); /* UCS2 of path consumed minus
1077 2 byte null */
1078 /* number of referral in this pkt */
1079 SSVAL(pdata,2,junction->referral_count);
1080 if(self_referral) {
1081 SIVAL(pdata,4,DFSREF_REFERRAL_SERVER | DFSREF_STORAGE_SERVER);
1082 } else {
1083 SIVAL(pdata,4,DFSREF_STORAGE_SERVER);
1086 offset = 8;
1087 /* add the referral elements */
1088 for(i=0;i<junction->referral_count;i++) {
1089 struct referral* ref = &junction->referral_list[i];
1090 int unilen;
1092 SSVAL(pdata,offset,2); /* version 2 */
1093 SSVAL(pdata,offset+2,VERSION2_REFERRAL_SIZE);
1094 if(self_referral) {
1095 SSVAL(pdata,offset+4,1);
1096 } else {
1097 SSVAL(pdata,offset+4,0);
1100 /* ref_flags :use path_consumed bytes? */
1101 SSVAL(pdata,offset+6,0);
1102 SIVAL(pdata,offset+8,ref->proximity);
1103 SIVAL(pdata,offset+12,ref->ttl);
1105 SSVAL(pdata,offset+16,uni_reqpathoffset1-offset);
1106 SSVAL(pdata,offset+18,uni_reqpathoffset2-offset);
1107 /* copy referred path into current offset */
1108 unilen = rpcstr_push(pdata+uni_curroffset,
1109 ref->alternate_path,
1110 reply_size - uni_curroffset,
1111 STR_UNICODE);
1113 SSVAL(pdata,offset+20,uni_curroffset-offset);
1115 uni_curroffset += unilen;
1116 offset += VERSION2_REFERRAL_SIZE;
1118 /* add in the unexplained 22 (0x16) bytes at the end */
1119 memset(pdata+uni_curroffset,'\0',0x16);
1120 return reply_size;
1123 static int setup_ver3_dfs_referral(const char *pathname,
1124 char **ppdata,
1125 struct junction_map *junction,
1126 bool self_referral)
1128 char *pdata = *ppdata;
1130 smb_ucs2_t *uni_reqpath = NULL;
1131 int uni_reqpathoffset1, uni_reqpathoffset2;
1132 int uni_curroffset;
1133 int reply_size = 0;
1135 int reqpathlen = 0;
1136 int offset,i=0;
1138 DEBUG(10,("setting up version3 referral\n"));
1140 reqpathlen = rpcstr_push_talloc(talloc_tos(), &uni_reqpath, pathname);
1141 if (uni_reqpath == NULL || reqpathlen == 0) {
1142 return -1;
1145 if (DEBUGLVL(10)) {
1146 dump_data(0, (unsigned char *)uni_reqpath,
1147 reqpathlen);
1150 uni_reqpathoffset1 = REFERRAL_HEADER_SIZE +
1151 VERSION3_REFERRAL_SIZE * junction->referral_count;
1152 uni_reqpathoffset2 = uni_reqpathoffset1 + reqpathlen;
1153 reply_size = uni_curroffset = uni_reqpathoffset2 + reqpathlen;
1155 for(i=0;i<junction->referral_count;i++) {
1156 DEBUG(10,("referral %u : %s\n",
1158 junction->referral_list[i].alternate_path));
1159 reply_size +=
1160 (strlen(junction->referral_list[i].alternate_path)+1)*2;
1163 pdata = (char *)SMB_REALLOC(pdata,reply_size);
1164 if(pdata == NULL) {
1165 DEBUG(0,("version3 referral setup:"
1166 "malloc failed for Realloc!\n"));
1167 return -1;
1169 *ppdata = pdata;
1171 /* create the header */
1172 SSVAL(pdata,0,reqpathlen - 2); /* UCS2 of path consumed minus
1173 2 byte null */
1174 SSVAL(pdata,2,junction->referral_count); /* number of referral */
1175 if(self_referral) {
1176 SIVAL(pdata,4,DFSREF_REFERRAL_SERVER | DFSREF_STORAGE_SERVER);
1177 } else {
1178 SIVAL(pdata,4,DFSREF_STORAGE_SERVER);
1181 /* copy in the reqpaths */
1182 memcpy(pdata+uni_reqpathoffset1,uni_reqpath,reqpathlen);
1183 memcpy(pdata+uni_reqpathoffset2,uni_reqpath,reqpathlen);
1185 offset = 8;
1186 for(i=0;i<junction->referral_count;i++) {
1187 struct referral* ref = &(junction->referral_list[i]);
1188 int unilen;
1190 SSVAL(pdata,offset,3); /* version 3 */
1191 SSVAL(pdata,offset+2,VERSION3_REFERRAL_SIZE);
1192 if(self_referral) {
1193 SSVAL(pdata,offset+4,1);
1194 } else {
1195 SSVAL(pdata,offset+4,0);
1198 /* ref_flags :use path_consumed bytes? */
1199 SSVAL(pdata,offset+6,0);
1200 SIVAL(pdata,offset+8,ref->ttl);
1202 SSVAL(pdata,offset+12,uni_reqpathoffset1-offset);
1203 SSVAL(pdata,offset+14,uni_reqpathoffset2-offset);
1204 /* copy referred path into current offset */
1205 unilen = rpcstr_push(pdata+uni_curroffset,ref->alternate_path,
1206 reply_size - uni_curroffset,
1207 STR_UNICODE | STR_TERMINATE);
1208 SSVAL(pdata,offset+16,uni_curroffset-offset);
1209 /* copy 0x10 bytes of 00's in the ServiceSite GUID */
1210 memset(pdata+offset+18,'\0',16);
1212 uni_curroffset += unilen;
1213 offset += VERSION3_REFERRAL_SIZE;
1215 return reply_size;
1218 /******************************************************************
1219 Set up the DFS referral for the dfs pathname. This call returns
1220 the amount of the path covered by this server, and where the
1221 client should be redirected to. This is the meat of the
1222 TRANS2_GET_DFS_REFERRAL call.
1223 ******************************************************************/
1225 int setup_dfs_referral(connection_struct *orig_conn,
1226 const char *dfs_path,
1227 int max_referral_level,
1228 char **ppdata, NTSTATUS *pstatus)
1230 struct junction_map *junction = NULL;
1231 int consumedcnt = 0;
1232 bool self_referral = False;
1233 int reply_size = 0;
1234 char *pathnamep = NULL;
1235 char *local_dfs_path = NULL;
1236 TALLOC_CTX *ctx;
1238 if (!(ctx=talloc_init("setup_dfs_referral"))) {
1239 *pstatus = NT_STATUS_NO_MEMORY;
1240 return -1;
1243 /* get the junction entry */
1244 if (!dfs_path) {
1245 talloc_destroy(ctx);
1246 *pstatus = NT_STATUS_NOT_FOUND;
1247 return -1;
1251 * Trim pathname sent by client so it begins with only one backslash.
1252 * Two backslashes confuse some dfs clients
1255 local_dfs_path = talloc_strdup(ctx,dfs_path);
1256 if (!local_dfs_path) {
1257 *pstatus = NT_STATUS_NO_MEMORY;
1258 talloc_destroy(ctx);
1259 return -1;
1261 pathnamep = local_dfs_path;
1262 while (IS_DIRECTORY_SEP(pathnamep[0]) &&
1263 IS_DIRECTORY_SEP(pathnamep[1])) {
1264 pathnamep++;
1267 junction = talloc_zero(ctx, struct junction_map);
1268 if (!junction) {
1269 *pstatus = NT_STATUS_NO_MEMORY;
1270 talloc_destroy(ctx);
1271 return -1;
1274 /* The following call can change cwd. */
1275 *pstatus = get_referred_path(ctx, pathnamep, junction,
1276 &consumedcnt, &self_referral);
1277 if (!NT_STATUS_IS_OK(*pstatus)) {
1278 vfs_ChDir(orig_conn,orig_conn->connectpath);
1279 talloc_destroy(ctx);
1280 return -1;
1282 vfs_ChDir(orig_conn,orig_conn->connectpath);
1284 if (!self_referral) {
1285 pathnamep[consumedcnt] = '\0';
1287 if( DEBUGLVL( 3 ) ) {
1288 int i=0;
1289 dbgtext("setup_dfs_referral: Path %s to "
1290 "alternate path(s):",
1291 pathnamep);
1292 for(i=0;i<junction->referral_count;i++)
1293 dbgtext(" %s",
1294 junction->referral_list[i].alternate_path);
1295 dbgtext(".\n");
1299 /* create the referral depeding on version */
1300 DEBUG(10,("max_referral_level :%d\n",max_referral_level));
1302 if (max_referral_level < 2) {
1303 max_referral_level = 2;
1305 if (max_referral_level > 3) {
1306 max_referral_level = 3;
1309 switch(max_referral_level) {
1310 case 2:
1311 reply_size = setup_ver2_dfs_referral(pathnamep,
1312 ppdata, junction,
1313 self_referral);
1314 break;
1315 case 3:
1316 reply_size = setup_ver3_dfs_referral(pathnamep, ppdata,
1317 junction, self_referral);
1318 break;
1319 default:
1320 DEBUG(0,("setup_dfs_referral: Invalid dfs referral "
1321 "version: %d\n",
1322 max_referral_level));
1323 talloc_destroy(ctx);
1324 *pstatus = NT_STATUS_INVALID_LEVEL;
1325 return -1;
1328 if (DEBUGLVL(10)) {
1329 DEBUGADD(0,("DFS Referral pdata:\n"));
1330 dump_data(0,(uint8 *)*ppdata,reply_size);
1333 talloc_destroy(ctx);
1334 *pstatus = NT_STATUS_OK;
1335 return reply_size;
1338 /**********************************************************************
1339 The following functions are called by the NETDFS RPC pipe functions
1340 **********************************************************************/
1342 /*********************************************************************
1343 Creates a junction structure from a DFS pathname
1344 **********************************************************************/
1346 bool create_junction(TALLOC_CTX *ctx,
1347 const char *dfs_path,
1348 struct junction_map *jucn)
1350 int snum;
1351 bool dummy;
1352 struct dfs_path *pdp = talloc(ctx,struct dfs_path);
1353 NTSTATUS status;
1355 if (!pdp) {
1356 return False;
1358 status = parse_dfs_path(NULL, dfs_path, False, pdp, &dummy);
1359 if (!NT_STATUS_IS_OK(status)) {
1360 return False;
1363 /* check if path is dfs : validate first token */
1364 if (!is_myname_or_ipaddr(pdp->hostname)) {
1365 DEBUG(4,("create_junction: Invalid hostname %s "
1366 "in dfs path %s\n",
1367 pdp->hostname, dfs_path));
1368 TALLOC_FREE(pdp);
1369 return False;
1372 /* Check for a non-DFS share */
1373 snum = lp_servicenumber(pdp->servicename);
1375 if(snum < 0 || !lp_msdfs_root(snum)) {
1376 DEBUG(4,("create_junction: %s is not an msdfs root.\n",
1377 pdp->servicename));
1378 TALLOC_FREE(pdp);
1379 return False;
1382 jucn->service_name = talloc_strdup(ctx, pdp->servicename);
1383 jucn->volume_name = talloc_strdup(ctx, pdp->reqpath);
1384 jucn->comment = talloc_strdup(ctx, lp_comment(snum));
1386 TALLOC_FREE(pdp);
1387 if (!jucn->service_name || !jucn->volume_name || ! jucn->comment) {
1388 return False;
1390 return True;
1393 /**********************************************************************
1394 Forms a valid Unix pathname from the junction
1395 **********************************************************************/
1397 static bool junction_to_local_path(const struct junction_map *jucn,
1398 char **pp_path_out,
1399 connection_struct **conn_out,
1400 char **oldpath)
1402 int snum;
1403 NTSTATUS status;
1405 snum = lp_servicenumber(jucn->service_name);
1406 if(snum < 0) {
1407 return False;
1409 status = create_conn_struct(talloc_tos(), conn_out, snum,
1410 lp_pathname(snum), NULL, oldpath);
1411 if (!NT_STATUS_IS_OK(status)) {
1412 return False;
1415 *pp_path_out = talloc_asprintf(*conn_out,
1416 "%s/%s",
1417 lp_pathname(snum),
1418 jucn->volume_name);
1419 if (!*pp_path_out) {
1420 vfs_ChDir(*conn_out, *oldpath);
1421 SMB_VFS_DISCONNECT(*conn_out);
1422 conn_free(*conn_out);
1423 return False;
1425 return True;
1428 bool create_msdfs_link(const struct junction_map *jucn)
1430 char *path = NULL;
1431 char *cwd;
1432 char *msdfs_link = NULL;
1433 connection_struct *conn;
1434 int i=0;
1435 bool insert_comma = False;
1436 bool ret = False;
1438 if(!junction_to_local_path(jucn, &path, &conn, &cwd)) {
1439 return False;
1442 /* Form the msdfs_link contents */
1443 msdfs_link = talloc_strdup(conn, "msdfs:");
1444 if (!msdfs_link) {
1445 goto out;
1447 for(i=0; i<jucn->referral_count; i++) {
1448 char *refpath = jucn->referral_list[i].alternate_path;
1450 /* Alternate paths always use Windows separators. */
1451 trim_char(refpath, '\\', '\\');
1452 if(*refpath == '\0') {
1453 if (i == 0) {
1454 insert_comma = False;
1456 continue;
1458 if (i > 0 && insert_comma) {
1459 msdfs_link = talloc_asprintf_append_buffer(msdfs_link,
1460 ",%s",
1461 refpath);
1462 } else {
1463 msdfs_link = talloc_asprintf_append_buffer(msdfs_link,
1464 "%s",
1465 refpath);
1468 if (!msdfs_link) {
1469 goto out;
1471 if (!insert_comma) {
1472 insert_comma = True;
1476 DEBUG(5,("create_msdfs_link: Creating new msdfs link: %s -> %s\n",
1477 path, msdfs_link));
1479 if(SMB_VFS_SYMLINK(conn, msdfs_link, path) < 0) {
1480 if (errno == EEXIST) {
1481 struct smb_filename *smb_fname = NULL;
1482 NTSTATUS status;
1484 status = create_synthetic_smb_fname(talloc_tos(), path,
1485 NULL, NULL,
1486 &smb_fname);
1487 if (!NT_STATUS_IS_OK(status)) {
1488 errno = map_errno_from_nt_status(status);
1489 goto out;
1492 if(SMB_VFS_UNLINK(conn, smb_fname)!=0) {
1493 TALLOC_FREE(smb_fname);
1494 goto out;
1496 TALLOC_FREE(smb_fname);
1498 if (SMB_VFS_SYMLINK(conn, msdfs_link, path) < 0) {
1499 DEBUG(1,("create_msdfs_link: symlink failed "
1500 "%s -> %s\nError: %s\n",
1501 path, msdfs_link, strerror(errno)));
1502 goto out;
1506 ret = True;
1508 out:
1509 vfs_ChDir(conn, cwd);
1510 SMB_VFS_DISCONNECT(conn);
1511 conn_free(conn);
1512 return ret;
1515 bool remove_msdfs_link(const struct junction_map *jucn)
1517 char *path = NULL;
1518 char *cwd;
1519 connection_struct *conn;
1520 bool ret = False;
1521 struct smb_filename *smb_fname = NULL;
1522 NTSTATUS status;
1524 if (!junction_to_local_path(jucn, &path, &conn, &cwd)) {
1525 return false;
1528 status = create_synthetic_smb_fname(talloc_tos(), path,
1529 NULL, NULL,
1530 &smb_fname);
1531 if (!NT_STATUS_IS_OK(status)) {
1532 errno = map_errno_from_nt_status(status);
1533 return false;
1536 if( SMB_VFS_UNLINK(conn, smb_fname) == 0 ) {
1537 ret = True;
1540 TALLOC_FREE(smb_fname);
1541 vfs_ChDir(conn, cwd);
1542 SMB_VFS_DISCONNECT(conn);
1543 conn_free(conn);
1544 return ret;
1547 /*********************************************************************
1548 Return the number of DFS links at the root of this share.
1549 *********************************************************************/
1551 static int count_dfs_links(TALLOC_CTX *ctx, int snum)
1553 size_t cnt = 0;
1554 SMB_STRUCT_DIR *dirp = NULL;
1555 const char *dname = NULL;
1556 char *talloced = NULL;
1557 const char *connect_path = lp_pathname(snum);
1558 const char *msdfs_proxy = lp_msdfs_proxy(snum);
1559 connection_struct *conn;
1560 NTSTATUS status;
1561 char *cwd;
1563 if(*connect_path == '\0') {
1564 return 0;
1568 * Fake up a connection struct for the VFS layer.
1571 status = create_conn_struct(talloc_tos(), &conn, snum, connect_path,
1572 NULL, &cwd);
1573 if (!NT_STATUS_IS_OK(status)) {
1574 DEBUG(3, ("create_conn_struct failed: %s\n",
1575 nt_errstr(status)));
1576 return 0;
1579 /* Count a link for the msdfs root - convention */
1580 cnt = 1;
1582 /* No more links if this is an msdfs proxy. */
1583 if (*msdfs_proxy != '\0') {
1584 goto out;
1587 /* Now enumerate all dfs links */
1588 dirp = SMB_VFS_OPENDIR(conn, ".", NULL, 0);
1589 if(!dirp) {
1590 goto out;
1593 while ((dname = vfs_readdirname(conn, dirp, NULL, &talloced))
1594 != NULL) {
1595 if (is_msdfs_link(conn,
1596 dname,
1597 NULL)) {
1598 cnt++;
1600 TALLOC_FREE(talloced);
1603 SMB_VFS_CLOSEDIR(conn,dirp);
1605 out:
1606 vfs_ChDir(conn, cwd);
1607 SMB_VFS_DISCONNECT(conn);
1608 conn_free(conn);
1609 return cnt;
1612 /*********************************************************************
1613 *********************************************************************/
1615 static int form_junctions(TALLOC_CTX *ctx,
1616 int snum,
1617 struct junction_map *jucn,
1618 size_t jn_remain)
1620 size_t cnt = 0;
1621 SMB_STRUCT_DIR *dirp = NULL;
1622 const char *dname = NULL;
1623 char *talloced = NULL;
1624 const char *connect_path = lp_pathname(snum);
1625 char *service_name = lp_servicename(snum);
1626 const char *msdfs_proxy = lp_msdfs_proxy(snum);
1627 connection_struct *conn;
1628 struct referral *ref = NULL;
1629 char *cwd;
1630 NTSTATUS status;
1632 if (jn_remain == 0) {
1633 return 0;
1636 if(*connect_path == '\0') {
1637 return 0;
1641 * Fake up a connection struct for the VFS layer.
1644 status = create_conn_struct(ctx, &conn, snum, connect_path, NULL,
1645 &cwd);
1646 if (!NT_STATUS_IS_OK(status)) {
1647 DEBUG(3, ("create_conn_struct failed: %s\n",
1648 nt_errstr(status)));
1649 return 0;
1652 /* form a junction for the msdfs root - convention
1653 DO NOT REMOVE THIS: NT clients will not work with us
1654 if this is not present
1656 jucn[cnt].service_name = talloc_strdup(ctx,service_name);
1657 jucn[cnt].volume_name = talloc_strdup(ctx, "");
1658 if (!jucn[cnt].service_name || !jucn[cnt].volume_name) {
1659 goto out;
1661 jucn[cnt].comment = "";
1662 jucn[cnt].referral_count = 1;
1664 ref = jucn[cnt].referral_list = talloc_zero(ctx, struct referral);
1665 if (jucn[cnt].referral_list == NULL) {
1666 goto out;
1669 ref->proximity = 0;
1670 ref->ttl = REFERRAL_TTL;
1671 if (*msdfs_proxy != '\0') {
1672 ref->alternate_path = talloc_strdup(ctx,
1673 msdfs_proxy);
1674 } else {
1675 ref->alternate_path = talloc_asprintf(ctx,
1676 "\\\\%s\\%s",
1677 get_local_machine_name(),
1678 service_name);
1681 if (!ref->alternate_path) {
1682 goto out;
1684 cnt++;
1686 /* Don't enumerate if we're an msdfs proxy. */
1687 if (*msdfs_proxy != '\0') {
1688 goto out;
1691 /* Now enumerate all dfs links */
1692 dirp = SMB_VFS_OPENDIR(conn, ".", NULL, 0);
1693 if(!dirp) {
1694 goto out;
1697 while ((dname = vfs_readdirname(conn, dirp, NULL, &talloced))
1698 != NULL) {
1699 char *link_target = NULL;
1700 if (cnt >= jn_remain) {
1701 DEBUG(2, ("form_junctions: ran out of MSDFS "
1702 "junction slots"));
1703 TALLOC_FREE(talloced);
1704 goto out;
1706 if (is_msdfs_link_internal(ctx,
1707 conn,
1708 dname, &link_target,
1709 NULL)) {
1710 if (parse_msdfs_symlink(ctx,
1711 link_target,
1712 &jucn[cnt].referral_list,
1713 &jucn[cnt].referral_count)) {
1715 jucn[cnt].service_name = talloc_strdup(ctx,
1716 service_name);
1717 jucn[cnt].volume_name = talloc_strdup(ctx,
1718 dname);
1719 if (!jucn[cnt].service_name ||
1720 !jucn[cnt].volume_name) {
1721 TALLOC_FREE(talloced);
1722 goto out;
1724 jucn[cnt].comment = "";
1725 cnt++;
1727 TALLOC_FREE(link_target);
1729 TALLOC_FREE(talloced);
1732 out:
1734 if (dirp) {
1735 SMB_VFS_CLOSEDIR(conn,dirp);
1738 vfs_ChDir(conn, cwd);
1739 conn_free(conn);
1740 return cnt;
1743 struct junction_map *enum_msdfs_links(struct smbd_server_connection *sconn,
1744 TALLOC_CTX *ctx, size_t *p_num_jn)
1746 struct junction_map *jn = NULL;
1747 int i=0;
1748 size_t jn_count = 0;
1749 int sharecount = 0;
1751 *p_num_jn = 0;
1752 if(!lp_host_msdfs()) {
1753 return NULL;
1756 /* Ensure all the usershares are loaded. */
1757 become_root();
1758 load_registry_shares();
1759 sharecount = load_usershare_shares(sconn);
1760 unbecome_root();
1762 for(i=0;i < sharecount;i++) {
1763 if(lp_msdfs_root(i)) {
1764 jn_count += count_dfs_links(ctx, i);
1767 if (jn_count == 0) {
1768 return NULL;
1770 jn = talloc_array(ctx, struct junction_map, jn_count);
1771 if (!jn) {
1772 return NULL;
1774 for(i=0; i < sharecount; i++) {
1775 if (*p_num_jn >= jn_count) {
1776 break;
1778 if(lp_msdfs_root(i)) {
1779 *p_num_jn += form_junctions(ctx, i,
1780 &jn[*p_num_jn],
1781 jn_count - *p_num_jn);
1784 return jn;
1787 /******************************************************************************
1788 Core function to resolve a dfs pathname possibly containing a wildcard. If
1789 ppath_contains_wcard != NULL, it will be set to true if a wildcard is
1790 detected during dfs resolution.
1791 ******************************************************************************/
1793 NTSTATUS resolve_dfspath_wcard(TALLOC_CTX *ctx,
1794 connection_struct *conn,
1795 bool dfs_pathnames,
1796 const char *name_in,
1797 bool allow_wcards,
1798 char **pp_name_out,
1799 bool *ppath_contains_wcard)
1801 bool path_contains_wcard;
1802 NTSTATUS status = NT_STATUS_OK;
1804 if (dfs_pathnames) {
1805 status = dfs_redirect(ctx,
1806 conn,
1807 name_in,
1808 allow_wcards,
1809 pp_name_out,
1810 &path_contains_wcard);
1812 if (NT_STATUS_IS_OK(status) && ppath_contains_wcard != NULL) {
1813 *ppath_contains_wcard = path_contains_wcard;
1815 } else {
1817 * Cheat and just return a copy of the in ptr.
1818 * Once srvstr_get_path() uses talloc it'll
1819 * be a talloced ptr anyway.
1821 *pp_name_out = discard_const_p(char, name_in);
1823 return status;