s3:smbd/files: remove unused VALID_FNUM()
[Samba/gebeck_regimport.git] / source3 / smbd / msdfs.c
blobb7a505284e7d1f9a83c9924da9636bf9cfa29374
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"
32 #include "librpc/gen_ndr/ndr_dfsblobs.h"
34 /**********************************************************************
35 Parse a DFS pathname of the form \hostname\service\reqpath
36 into the dfs_path structure.
37 If POSIX pathnames is true, the pathname may also be of the
38 form /hostname/service/reqpath.
39 We cope with either here.
41 Unfortunately, due to broken clients who might set the
42 SVAL(inbuf,smb_flg2) & FLAGS2_DFS_PATHNAMES bit and then
43 send a local path, we have to cope with that too....
45 If conn != NULL then ensure the provided service is
46 the one pointed to by the connection.
48 This version does everything using pointers within one copy of the
49 pathname string, talloced on the struct dfs_path pointer (which
50 must be talloced). This may be too clever to live....
51 JRA.
52 **********************************************************************/
54 static NTSTATUS parse_dfs_path(connection_struct *conn,
55 const char *pathname,
56 bool allow_wcards,
57 bool allow_broken_path,
58 struct dfs_path *pdp, /* MUST BE TALLOCED */
59 bool *ppath_contains_wcard)
61 char *pathname_local;
62 char *p,*temp;
63 char *servicename;
64 char *eos_ptr;
65 NTSTATUS status = NT_STATUS_OK;
66 char sepchar;
68 ZERO_STRUCTP(pdp);
71 * This is the only talloc we should need to do
72 * on the struct dfs_path. All the pointers inside
73 * it should point to offsets within this string.
76 pathname_local = talloc_strdup(pdp, pathname);
77 if (!pathname_local) {
78 return NT_STATUS_NO_MEMORY;
80 /* Get a pointer to the terminating '\0' */
81 eos_ptr = &pathname_local[strlen(pathname_local)];
82 p = temp = pathname_local;
84 pdp->posix_path = (lp_posix_pathnames() && *pathname == '/');
86 sepchar = pdp->posix_path ? '/' : '\\';
88 if (allow_broken_path && (*pathname != sepchar)) {
89 DEBUG(10,("parse_dfs_path: path %s doesn't start with %c\n",
90 pathname, sepchar ));
92 * Possibly client sent a local path by mistake.
93 * Try and convert to a local path.
96 pdp->hostname = eos_ptr; /* "" */
97 pdp->servicename = eos_ptr; /* "" */
99 /* We've got no info about separators. */
100 pdp->posix_path = lp_posix_pathnames();
101 p = temp;
102 DEBUG(10,("parse_dfs_path: trying to convert %s to a "
103 "local path\n",
104 temp));
105 goto local_path;
109 * Safe to use on talloc'ed string as it only shrinks.
110 * It also doesn't affect the eos_ptr.
112 trim_char(temp,sepchar,sepchar);
114 DEBUG(10,("parse_dfs_path: temp = |%s| after trimming %c's\n",
115 temp, sepchar));
117 /* Now tokenize. */
118 /* Parse out hostname. */
119 p = strchr_m(temp,sepchar);
120 if(p == NULL) {
121 DEBUG(10,("parse_dfs_path: can't parse hostname from path %s\n",
122 temp));
124 * Possibly client sent a local path by mistake.
125 * Try and convert to a local path.
128 pdp->hostname = eos_ptr; /* "" */
129 pdp->servicename = eos_ptr; /* "" */
131 p = temp;
132 DEBUG(10,("parse_dfs_path: trying to convert %s "
133 "to a local path\n",
134 temp));
135 goto local_path;
137 *p = '\0';
138 pdp->hostname = temp;
140 DEBUG(10,("parse_dfs_path: hostname: %s\n",pdp->hostname));
142 /* Parse out servicename. */
143 servicename = p+1;
144 p = strchr_m(servicename,sepchar);
145 if (p) {
146 *p = '\0';
149 /* Is this really our servicename ? */
150 if (conn && !( strequal(servicename, lp_servicename(SNUM(conn)))
151 || (strequal(servicename, HOMES_NAME)
152 && strequal(lp_servicename(SNUM(conn)),
153 get_current_username()) )) ) {
154 DEBUG(10,("parse_dfs_path: %s is not our servicename\n",
155 servicename));
158 * Possibly client sent a local path by mistake.
159 * Try and convert to a local path.
162 pdp->hostname = eos_ptr; /* "" */
163 pdp->servicename = eos_ptr; /* "" */
165 /* Repair the path - replace the sepchar's
166 we nulled out */
167 servicename--;
168 *servicename = sepchar;
169 if (p) {
170 *p = sepchar;
173 p = temp;
174 DEBUG(10,("parse_dfs_path: trying to convert %s "
175 "to a local path\n",
176 temp));
177 goto local_path;
180 pdp->servicename = servicename;
182 DEBUG(10,("parse_dfs_path: servicename: %s\n",pdp->servicename));
184 if(p == NULL) {
185 /* Client sent self referral \server\share. */
186 pdp->reqpath = eos_ptr; /* "" */
187 return NT_STATUS_OK;
190 p++;
192 local_path:
194 *ppath_contains_wcard = False;
196 pdp->reqpath = p;
198 /* Rest is reqpath. */
199 if (pdp->posix_path) {
200 status = check_path_syntax_posix(pdp->reqpath);
201 } else {
202 if (allow_wcards) {
203 status = check_path_syntax_wcard(pdp->reqpath,
204 ppath_contains_wcard);
205 } else {
206 status = check_path_syntax(pdp->reqpath);
210 if (!NT_STATUS_IS_OK(status)) {
211 DEBUG(10,("parse_dfs_path: '%s' failed with %s\n",
212 p, nt_errstr(status) ));
213 return status;
216 DEBUG(10,("parse_dfs_path: rest of the path: %s\n",pdp->reqpath));
217 return NT_STATUS_OK;
220 /********************************************************
221 Fake up a connection struct for the VFS layer.
222 Note: this performs a vfs connect and CHANGES CWD !!!! JRA.
223 *********************************************************/
225 NTSTATUS create_conn_struct(TALLOC_CTX *ctx,
226 struct smbd_server_connection *sconn,
227 connection_struct **pconn,
228 int snum,
229 const char *path,
230 const struct auth_session_info *session_info,
231 char **poldcwd)
233 connection_struct *conn;
234 char *connpath;
235 char *oldcwd;
236 const char *vfs_user;
238 conn = talloc_zero(ctx, connection_struct);
239 if (conn == NULL) {
240 return NT_STATUS_NO_MEMORY;
243 connpath = talloc_strdup(conn, path);
244 if (!connpath) {
245 TALLOC_FREE(conn);
246 return NT_STATUS_NO_MEMORY;
248 connpath = talloc_string_sub(conn,
249 connpath,
250 "%S",
251 lp_servicename(snum));
252 if (!connpath) {
253 TALLOC_FREE(conn);
254 return NT_STATUS_NO_MEMORY;
257 /* needed for smbd_vfs_init() */
259 if (!(conn->params = talloc_zero(conn, struct share_params))) {
260 DEBUG(0, ("TALLOC failed\n"));
261 TALLOC_FREE(conn);
262 return NT_STATUS_NO_MEMORY;
265 conn->params->service = snum;
266 conn->cnum = (unsigned)-1;
268 conn->sconn = sconn;
269 DLIST_ADD(sconn->connections, conn);
270 conn->sconn->num_connections++;
272 if (session_info != NULL) {
273 conn->session_info = copy_session_info(conn, session_info);
274 if (conn->session_info == NULL) {
275 DEBUG(0, ("copy_serverinfo failed\n"));
276 TALLOC_FREE(conn);
277 return NT_STATUS_NO_MEMORY;
279 vfs_user = conn->session_info->unix_info->unix_name;
280 } else {
281 /* use current authenticated user in absence of session_info */
282 vfs_user = get_current_username();
285 set_conn_connectpath(conn, connpath);
288 * New code to check if there's a share security descripter
289 * added from NT server manager. This is done after the
290 * smb.conf checks are done as we need a uid and token. JRA.
293 if (conn->session_info) {
294 share_access_check(conn->session_info->security_token,
295 lp_servicename(snum), MAXIMUM_ALLOWED_ACCESS,
296 &conn->share_access);
298 if ((conn->share_access & FILE_WRITE_DATA) == 0) {
299 if ((conn->share_access & FILE_READ_DATA) == 0) {
300 /* No access, read or write. */
301 DEBUG(0,("create_conn_struct: connection to %s "
302 "denied due to security "
303 "descriptor.\n",
304 lp_servicename(snum)));
305 conn_free(conn);
306 return NT_STATUS_ACCESS_DENIED;
307 } else {
308 conn->read_only = true;
311 } else {
312 conn->share_access = 0;
313 conn->read_only = true;
316 if (!smbd_vfs_init(conn)) {
317 NTSTATUS status = map_nt_error_from_unix(errno);
318 DEBUG(0,("create_conn_struct: smbd_vfs_init failed.\n"));
319 conn_free(conn);
320 return status;
323 /* this must be the first filesystem operation that we do */
324 if (SMB_VFS_CONNECT(conn, lp_servicename(snum), vfs_user) < 0) {
325 DEBUG(0,("VFS connect failed!\n"));
326 conn_free(conn);
327 return NT_STATUS_UNSUCCESSFUL;
330 conn->fs_capabilities = SMB_VFS_FS_CAPABILITIES(conn, &conn->ts_res);
333 * Windows seems to insist on doing trans2getdfsreferral() calls on
334 * the IPC$ share as the anonymous user. If we try to chdir as that
335 * user we will fail.... WTF ? JRA.
338 oldcwd = vfs_GetWd(ctx, conn);
339 if (oldcwd == NULL) {
340 NTSTATUS status = map_nt_error_from_unix(errno);
341 DEBUG(3, ("vfs_GetWd failed: %s\n", strerror(errno)));
342 conn_free(conn);
343 return status;
346 if (vfs_ChDir(conn,conn->connectpath) != 0) {
347 NTSTATUS status = map_nt_error_from_unix(errno);
348 DEBUG(3,("create_conn_struct: Can't ChDir to new conn path %s. "
349 "Error was %s\n",
350 conn->connectpath, strerror(errno) ));
351 conn_free(conn);
352 return status;
355 *pconn = conn;
356 *poldcwd = oldcwd;
358 return NT_STATUS_OK;
361 /**********************************************************************
362 Parse the contents of a symlink to verify if it is an msdfs referral
363 A valid referral is of the form:
365 msdfs:server1\share1,server2\share2
366 msdfs:server1\share1\pathname,server2\share2\pathname
367 msdfs:server1/share1,server2/share2
368 msdfs:server1/share1/pathname,server2/share2/pathname.
370 Note that the alternate paths returned here must be of the canonicalized
371 form:
373 \server\share or
374 \server\share\path\to\file,
376 even in posix path mode. This is because we have no knowledge if the
377 server we're referring to understands posix paths.
378 **********************************************************************/
380 static bool parse_msdfs_symlink(TALLOC_CTX *ctx,
381 const char *target,
382 struct referral **preflist,
383 int *refcount)
385 char *temp = NULL;
386 char *prot;
387 char **alt_path = NULL;
388 int count = 0, i;
389 struct referral *reflist;
390 char *saveptr;
392 temp = talloc_strdup(ctx, target);
393 if (!temp) {
394 return False;
396 prot = strtok_r(temp, ":", &saveptr);
397 if (!prot) {
398 DEBUG(0,("parse_msdfs_symlink: invalid path !\n"));
399 return False;
402 alt_path = talloc_array(ctx, char *, MAX_REFERRAL_COUNT);
403 if (!alt_path) {
404 return False;
407 /* parse out the alternate paths */
408 while((count<MAX_REFERRAL_COUNT) &&
409 ((alt_path[count] = strtok_r(NULL, ",", &saveptr)) != NULL)) {
410 count++;
413 DEBUG(10,("parse_msdfs_symlink: count=%d\n", count));
415 if (count) {
416 reflist = *preflist = talloc_zero_array(ctx,
417 struct referral, count);
418 if(reflist == NULL) {
419 TALLOC_FREE(alt_path);
420 return False;
422 } else {
423 reflist = *preflist = NULL;
426 for(i=0;i<count;i++) {
427 char *p;
429 /* Canonicalize link target.
430 * Replace all /'s in the path by a \ */
431 string_replace(alt_path[i], '/', '\\');
433 /* Remove leading '\\'s */
434 p = alt_path[i];
435 while (*p && (*p == '\\')) {
436 p++;
439 reflist[i].alternate_path = talloc_asprintf(ctx,
440 "\\%s",
442 if (!reflist[i].alternate_path) {
443 return False;
446 reflist[i].proximity = 0;
447 reflist[i].ttl = REFERRAL_TTL;
448 DEBUG(10, ("parse_msdfs_symlink: Created alt path: %s\n",
449 reflist[i].alternate_path));
452 *refcount = count;
454 TALLOC_FREE(alt_path);
455 return True;
458 /**********************************************************************
459 Returns true if the unix path is a valid msdfs symlink and also
460 returns the target string from inside the link.
461 **********************************************************************/
463 static bool is_msdfs_link_internal(TALLOC_CTX *ctx,
464 connection_struct *conn,
465 const char *path,
466 char **pp_link_target,
467 SMB_STRUCT_STAT *sbufp)
469 int referral_len = 0;
470 #if defined(HAVE_BROKEN_READLINK)
471 char link_target_buf[PATH_MAX];
472 #else
473 char link_target_buf[7];
474 #endif
475 size_t bufsize = 0;
476 char *link_target = NULL;
477 struct smb_filename smb_fname;
479 if (pp_link_target) {
480 bufsize = 1024;
481 link_target = talloc_array(ctx, char, bufsize);
482 if (!link_target) {
483 return False;
485 *pp_link_target = link_target;
486 } else {
487 bufsize = sizeof(link_target_buf);
488 link_target = link_target_buf;
491 ZERO_STRUCT(smb_fname);
492 smb_fname.base_name = discard_const_p(char, path);
494 if (SMB_VFS_LSTAT(conn, &smb_fname) != 0) {
495 DEBUG(5,("is_msdfs_link_read_target: %s does not exist.\n",
496 path));
497 goto err;
499 if (!S_ISLNK(smb_fname.st.st_ex_mode)) {
500 DEBUG(5,("is_msdfs_link_read_target: %s is not a link.\n",
501 path));
502 goto err;
504 if (sbufp != NULL) {
505 *sbufp = smb_fname.st;
508 referral_len = SMB_VFS_READLINK(conn, path, link_target, bufsize - 1);
509 if (referral_len == -1) {
510 DEBUG(0,("is_msdfs_link_read_target: Error reading "
511 "msdfs link %s: %s\n",
512 path, strerror(errno)));
513 goto err;
515 link_target[referral_len] = '\0';
517 DEBUG(5,("is_msdfs_link_internal: %s -> %s\n",path,
518 link_target));
520 if (!strnequal(link_target, "msdfs:", 6)) {
521 goto err;
523 return True;
525 err:
527 if (link_target != link_target_buf) {
528 TALLOC_FREE(link_target);
530 return False;
533 /**********************************************************************
534 Returns true if the unix path is a valid msdfs symlink.
535 **********************************************************************/
537 bool is_msdfs_link(connection_struct *conn,
538 const char *path,
539 SMB_STRUCT_STAT *sbufp)
541 return is_msdfs_link_internal(talloc_tos(),
542 conn,
543 path,
544 NULL,
545 sbufp);
548 /*****************************************************************
549 Used by other functions to decide if a dfs path is remote,
550 and to get the list of referred locations for that remote path.
552 search_flag: For findfirsts, dfs links themselves are not
553 redirected, but paths beyond the links are. For normal smb calls,
554 even dfs links need to be redirected.
556 consumedcntp: how much of the dfs path is being redirected. the client
557 should try the remaining path on the redirected server.
559 If this returns NT_STATUS_PATH_NOT_COVERED the contents of the msdfs
560 link redirect are in targetpath.
561 *****************************************************************/
563 static NTSTATUS dfs_path_lookup(TALLOC_CTX *ctx,
564 connection_struct *conn,
565 const char *dfspath, /* Incoming complete dfs path */
566 const struct dfs_path *pdp, /* Parsed out
567 server+share+extrapath. */
568 bool search_flag, /* Called from a findfirst ? */
569 int *consumedcntp,
570 char **pp_targetpath)
572 char *p = NULL;
573 char *q = NULL;
574 NTSTATUS status;
575 struct smb_filename *smb_fname = NULL;
576 char *canon_dfspath = NULL; /* Canonicalized dfs path. (only '/'
577 components). */
579 DEBUG(10,("dfs_path_lookup: Conn path = %s reqpath = %s\n",
580 conn->connectpath, pdp->reqpath));
583 * Note the unix path conversion here we're doing we
584 * throw away. We're looking for a symlink for a dfs
585 * resolution, if we don't find it we'll do another
586 * unix_convert later in the codepath.
589 status = unix_convert(ctx, conn, pdp->reqpath, &smb_fname,
590 search_flag ? UCF_ALWAYS_ALLOW_WCARD_LCOMP : 0);
592 if (!NT_STATUS_IS_OK(status)) {
593 if (!NT_STATUS_EQUAL(status,
594 NT_STATUS_OBJECT_PATH_NOT_FOUND)) {
595 return status;
597 if (smb_fname == NULL || smb_fname->base_name == NULL) {
598 return status;
602 /* Optimization - check if we can redirect the whole path. */
604 if (is_msdfs_link_internal(ctx, conn, smb_fname->base_name,
605 pp_targetpath, NULL)) {
606 if (search_flag) {
607 DEBUG(6,("dfs_path_lookup (FindFirst) No redirection "
608 "for dfs link %s.\n", dfspath));
609 status = NT_STATUS_OK;
610 goto out;
613 DEBUG(6,("dfs_path_lookup: %s resolves to a "
614 "valid dfs link %s.\n", dfspath,
615 pp_targetpath ? *pp_targetpath : ""));
617 if (consumedcntp) {
618 *consumedcntp = strlen(dfspath);
620 status = NT_STATUS_PATH_NOT_COVERED;
621 goto out;
624 /* Prepare to test only for '/' components in the given path,
625 * so if a Windows path replace all '\\' characters with '/'.
626 * For a POSIX DFS path we know all separators are already '/'. */
628 canon_dfspath = talloc_strdup(ctx, dfspath);
629 if (!canon_dfspath) {
630 status = NT_STATUS_NO_MEMORY;
631 goto out;
633 if (!pdp->posix_path) {
634 string_replace(canon_dfspath, '\\', '/');
638 * localpath comes out of unix_convert, so it has
639 * no trailing backslash. Make sure that canon_dfspath hasn't either.
640 * Fix for bug #4860 from Jan Martin <Jan.Martin@rwedea.com>.
643 trim_char(canon_dfspath,0,'/');
646 * Redirect if any component in the path is a link.
647 * We do this by walking backwards through the
648 * local path, chopping off the last component
649 * in both the local path and the canonicalized
650 * DFS path. If we hit a DFS link then we're done.
653 p = strrchr_m(smb_fname->base_name, '/');
654 if (consumedcntp) {
655 q = strrchr_m(canon_dfspath, '/');
658 while (p) {
659 *p = '\0';
660 if (q) {
661 *q = '\0';
664 if (is_msdfs_link_internal(ctx, conn,
665 smb_fname->base_name, pp_targetpath,
666 NULL)) {
667 DEBUG(4, ("dfs_path_lookup: Redirecting %s because "
668 "parent %s is dfs link\n", dfspath,
669 smb_fname_str_dbg(smb_fname)));
671 if (consumedcntp) {
672 *consumedcntp = strlen(canon_dfspath);
673 DEBUG(10, ("dfs_path_lookup: Path consumed: %s "
674 "(%d)\n",
675 canon_dfspath,
676 *consumedcntp));
679 status = NT_STATUS_PATH_NOT_COVERED;
680 goto out;
683 /* Step back on the filesystem. */
684 p = strrchr_m(smb_fname->base_name, '/');
686 if (consumedcntp) {
687 /* And in the canonicalized dfs path. */
688 q = strrchr_m(canon_dfspath, '/');
692 status = NT_STATUS_OK;
693 out:
694 TALLOC_FREE(smb_fname);
695 return status;
698 /*****************************************************************
699 Decides if a dfs pathname should be redirected or not.
700 If not, the pathname is converted to a tcon-relative local unix path
702 search_wcard_flag: this flag performs 2 functions both related
703 to searches. See resolve_dfs_path() and parse_dfs_path_XX()
704 for details.
706 This function can return NT_STATUS_OK, meaning use the returned path as-is
707 (mapped into a local path).
708 or NT_STATUS_NOT_COVERED meaning return a DFS redirect, or
709 any other NT_STATUS error which is a genuine error to be
710 returned to the client.
711 *****************************************************************/
713 static NTSTATUS dfs_redirect(TALLOC_CTX *ctx,
714 connection_struct *conn,
715 const char *path_in,
716 bool search_wcard_flag,
717 bool allow_broken_path,
718 char **pp_path_out,
719 bool *ppath_contains_wcard)
721 NTSTATUS status;
722 struct dfs_path *pdp = talloc(ctx, struct dfs_path);
724 if (!pdp) {
725 return NT_STATUS_NO_MEMORY;
728 status = parse_dfs_path(conn, path_in, search_wcard_flag,
729 allow_broken_path, pdp,
730 ppath_contains_wcard);
731 if (!NT_STATUS_IS_OK(status)) {
732 TALLOC_FREE(pdp);
733 return status;
736 if (pdp->reqpath[0] == '\0') {
737 TALLOC_FREE(pdp);
738 *pp_path_out = talloc_strdup(ctx, "");
739 if (!*pp_path_out) {
740 return NT_STATUS_NO_MEMORY;
742 DEBUG(5,("dfs_redirect: self-referral.\n"));
743 return NT_STATUS_OK;
746 /* If dfs pathname for a non-dfs share, convert to tcon-relative
747 path and return OK */
749 if (!lp_msdfs_root(SNUM(conn))) {
750 *pp_path_out = talloc_strdup(ctx, pdp->reqpath);
751 TALLOC_FREE(pdp);
752 if (!*pp_path_out) {
753 return NT_STATUS_NO_MEMORY;
755 return NT_STATUS_OK;
758 /* If it looked like a local path (zero hostname/servicename)
759 * just treat as a tcon-relative path. */
761 if (pdp->hostname[0] == '\0' && pdp->servicename[0] == '\0') {
762 *pp_path_out = talloc_strdup(ctx, pdp->reqpath);
763 TALLOC_FREE(pdp);
764 if (!*pp_path_out) {
765 return NT_STATUS_NO_MEMORY;
767 return NT_STATUS_OK;
770 if (!( strequal(pdp->servicename, lp_servicename(SNUM(conn)))
771 || (strequal(pdp->servicename, HOMES_NAME)
772 && strequal(lp_servicename(SNUM(conn)),
773 conn->session_info->unix_info->sanitized_username) )) ) {
775 /* The given sharename doesn't match this connection. */
776 TALLOC_FREE(pdp);
778 return NT_STATUS_OBJECT_PATH_NOT_FOUND;
781 status = dfs_path_lookup(ctx, conn, path_in, pdp,
782 search_wcard_flag, NULL, NULL);
783 if (!NT_STATUS_IS_OK(status)) {
784 if (NT_STATUS_EQUAL(status, NT_STATUS_PATH_NOT_COVERED)) {
785 DEBUG(3,("dfs_redirect: Redirecting %s\n", path_in));
786 } else {
787 DEBUG(10,("dfs_redirect: dfs_path_lookup "
788 "failed for %s with %s\n",
789 path_in, nt_errstr(status) ));
791 return status;
794 DEBUG(3,("dfs_redirect: Not redirecting %s.\n", path_in));
796 /* Form non-dfs tcon-relative path */
797 *pp_path_out = talloc_strdup(ctx, pdp->reqpath);
798 TALLOC_FREE(pdp);
799 if (!*pp_path_out) {
800 return NT_STATUS_NO_MEMORY;
803 DEBUG(3,("dfs_redirect: Path %s converted to non-dfs path %s\n",
804 path_in,
805 *pp_path_out));
807 return NT_STATUS_OK;
810 /**********************************************************************
811 Return a self referral.
812 **********************************************************************/
814 static NTSTATUS self_ref(TALLOC_CTX *ctx,
815 const char *dfs_path,
816 struct junction_map *jucn,
817 int *consumedcntp,
818 bool *self_referralp)
820 struct referral *ref;
822 *self_referralp = True;
824 jucn->referral_count = 1;
825 if((ref = talloc_zero(ctx, struct referral)) == NULL) {
826 return NT_STATUS_NO_MEMORY;
829 ref->alternate_path = talloc_strdup(ctx, dfs_path);
830 if (!ref->alternate_path) {
831 return NT_STATUS_NO_MEMORY;
833 ref->proximity = 0;
834 ref->ttl = REFERRAL_TTL;
835 jucn->referral_list = ref;
836 *consumedcntp = strlen(dfs_path);
837 return NT_STATUS_OK;
840 /**********************************************************************
841 Gets valid referrals for a dfs path and fills up the
842 junction_map structure.
843 **********************************************************************/
845 NTSTATUS get_referred_path(TALLOC_CTX *ctx,
846 const char *dfs_path,
847 struct smbd_server_connection *sconn,
848 struct junction_map *jucn,
849 int *consumedcntp,
850 bool *self_referralp)
852 struct connection_struct *conn;
853 char *targetpath = NULL;
854 int snum;
855 NTSTATUS status = NT_STATUS_NOT_FOUND;
856 bool dummy;
857 struct dfs_path *pdp = talloc(ctx, struct dfs_path);
858 char *oldpath;
860 if (!pdp) {
861 return NT_STATUS_NO_MEMORY;
864 *self_referralp = False;
866 status = parse_dfs_path(NULL, dfs_path, False, !sconn->using_smb2,
867 pdp, &dummy);
868 if (!NT_STATUS_IS_OK(status)) {
869 return status;
872 jucn->service_name = talloc_strdup(ctx, pdp->servicename);
873 jucn->volume_name = talloc_strdup(ctx, pdp->reqpath);
874 if (!jucn->service_name || !jucn->volume_name) {
875 TALLOC_FREE(pdp);
876 return NT_STATUS_NO_MEMORY;
879 /* Verify the share is a dfs root */
880 snum = lp_servicenumber(jucn->service_name);
881 if(snum < 0) {
882 char *service_name = NULL;
883 if ((snum = find_service(ctx, jucn->service_name, &service_name)) < 0) {
884 return NT_STATUS_NOT_FOUND;
886 if (!service_name) {
887 return NT_STATUS_NO_MEMORY;
889 TALLOC_FREE(jucn->service_name);
890 jucn->service_name = talloc_strdup(ctx, service_name);
891 if (!jucn->service_name) {
892 TALLOC_FREE(pdp);
893 return NT_STATUS_NO_MEMORY;
897 if (!lp_msdfs_root(snum) && (*lp_msdfs_proxy(snum) == '\0')) {
898 DEBUG(3,("get_referred_path: |%s| in dfs path %s is not "
899 "a dfs root.\n",
900 pdp->servicename, dfs_path));
901 TALLOC_FREE(pdp);
902 return NT_STATUS_NOT_FOUND;
906 * Self referrals are tested with a anonymous IPC connection and
907 * a GET_DFS_REFERRAL call to \\server\share. (which means
908 * dp.reqpath[0] points to an empty string). create_conn_struct cd's
909 * into the directory and will fail if it cannot (as the anonymous
910 * user). Cope with this.
913 if (pdp->reqpath[0] == '\0') {
914 char *tmp;
915 struct referral *ref;
917 if (*lp_msdfs_proxy(snum) == '\0') {
918 TALLOC_FREE(pdp);
919 return self_ref(ctx,
920 dfs_path,
921 jucn,
922 consumedcntp,
923 self_referralp);
927 * It's an msdfs proxy share. Redirect to
928 * the configured target share.
931 jucn->referral_count = 1;
932 if ((ref = talloc_zero(ctx, struct referral)) == NULL) {
933 TALLOC_FREE(pdp);
934 return NT_STATUS_NO_MEMORY;
937 if (!(tmp = talloc_strdup(ctx, lp_msdfs_proxy(snum)))) {
938 TALLOC_FREE(pdp);
939 return NT_STATUS_NO_MEMORY;
942 trim_string(tmp, "\\", 0);
944 ref->alternate_path = talloc_asprintf(ctx, "\\%s", tmp);
945 TALLOC_FREE(tmp);
947 if (!ref->alternate_path) {
948 TALLOC_FREE(pdp);
949 return NT_STATUS_NO_MEMORY;
952 if (pdp->reqpath[0] != '\0') {
953 ref->alternate_path = talloc_asprintf_append(
954 ref->alternate_path,
955 "%s",
956 pdp->reqpath);
957 if (!ref->alternate_path) {
958 TALLOC_FREE(pdp);
959 return NT_STATUS_NO_MEMORY;
962 ref->proximity = 0;
963 ref->ttl = REFERRAL_TTL;
964 jucn->referral_list = ref;
965 *consumedcntp = strlen(dfs_path);
966 TALLOC_FREE(pdp);
967 return NT_STATUS_OK;
970 status = create_conn_struct(ctx, sconn, &conn, snum,
971 lp_pathname(snum), NULL, &oldpath);
972 if (!NT_STATUS_IS_OK(status)) {
973 TALLOC_FREE(pdp);
974 return status;
977 /* If this is a DFS path dfs_lookup should return
978 * NT_STATUS_PATH_NOT_COVERED. */
980 status = dfs_path_lookup(ctx, conn, dfs_path, pdp,
981 False, consumedcntp, &targetpath);
983 if (!NT_STATUS_EQUAL(status, NT_STATUS_PATH_NOT_COVERED)) {
984 DEBUG(3,("get_referred_path: No valid referrals for path %s\n",
985 dfs_path));
986 goto err_exit;
989 /* We know this is a valid dfs link. Parse the targetpath. */
990 if (!parse_msdfs_symlink(ctx, targetpath,
991 &jucn->referral_list,
992 &jucn->referral_count)) {
993 DEBUG(3,("get_referred_path: failed to parse symlink "
994 "target %s\n", targetpath ));
995 status = NT_STATUS_NOT_FOUND;
996 goto err_exit;
999 status = NT_STATUS_OK;
1000 err_exit:
1001 vfs_ChDir(conn, oldpath);
1002 SMB_VFS_DISCONNECT(conn);
1003 conn_free(conn);
1004 TALLOC_FREE(pdp);
1005 return status;
1008 /******************************************************************
1009 Set up the DFS referral for the dfs pathname. This call returns
1010 the amount of the path covered by this server, and where the
1011 client should be redirected to. This is the meat of the
1012 TRANS2_GET_DFS_REFERRAL call.
1013 ******************************************************************/
1015 int setup_dfs_referral(connection_struct *orig_conn,
1016 const char *dfs_path,
1017 int max_referral_level,
1018 char **ppdata, NTSTATUS *pstatus)
1020 char *pdata = *ppdata;
1021 int reply_size = 0;
1022 struct dfs_GetDFSReferral *r;
1023 DATA_BLOB blob = data_blob_null;
1024 NTSTATUS status;
1025 enum ndr_err_code ndr_err;
1027 r = talloc_zero(talloc_tos(), struct dfs_GetDFSReferral);
1028 if (r == NULL) {
1029 *pstatus = NT_STATUS_NO_MEMORY;
1030 return -1;
1033 r->in.req.max_referral_level = max_referral_level;
1034 r->in.req.servername = talloc_strdup(r, dfs_path);
1035 if (r->in.req.servername == NULL) {
1036 talloc_free(r);
1037 *pstatus = NT_STATUS_NO_MEMORY;
1038 return -1;
1041 status = SMB_VFS_GET_DFS_REFERRALS(orig_conn, r);
1042 if (!NT_STATUS_IS_OK(status)) {
1043 talloc_free(r);
1044 *pstatus = status;
1045 return -1;
1048 ndr_err = ndr_push_struct_blob(&blob, r,
1049 r->out.resp,
1050 (ndr_push_flags_fn_t)ndr_push_dfs_referral_resp);
1051 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1052 TALLOC_FREE(r);
1053 *pstatus = NT_STATUS_INVALID_PARAMETER;
1054 return -1;
1057 pdata = (char *)SMB_REALLOC(pdata, blob.length);
1058 if(pdata == NULL) {
1059 TALLOC_FREE(r);
1060 DEBUG(0,("referral setup:"
1061 "malloc failed for Realloc!\n"));
1062 return -1;
1064 *ppdata = pdata;
1065 reply_size = blob.length;
1066 memcpy(pdata, blob.data, blob.length);
1067 TALLOC_FREE(r);
1069 *pstatus = NT_STATUS_OK;
1070 return reply_size;
1073 /**********************************************************************
1074 The following functions are called by the NETDFS RPC pipe functions
1075 **********************************************************************/
1077 /*********************************************************************
1078 Creates a junction structure from a DFS pathname
1079 **********************************************************************/
1081 bool create_junction(TALLOC_CTX *ctx,
1082 const char *dfs_path,
1083 bool allow_broken_path,
1084 struct junction_map *jucn)
1086 int snum;
1087 bool dummy;
1088 struct dfs_path *pdp = talloc(ctx,struct dfs_path);
1089 NTSTATUS status;
1091 if (!pdp) {
1092 return False;
1094 status = parse_dfs_path(NULL, dfs_path, False, allow_broken_path,
1095 pdp, &dummy);
1096 if (!NT_STATUS_IS_OK(status)) {
1097 return False;
1100 /* check if path is dfs : validate first token */
1101 if (!is_myname_or_ipaddr(pdp->hostname)) {
1102 DEBUG(4,("create_junction: Invalid hostname %s "
1103 "in dfs path %s\n",
1104 pdp->hostname, dfs_path));
1105 TALLOC_FREE(pdp);
1106 return False;
1109 /* Check for a non-DFS share */
1110 snum = lp_servicenumber(pdp->servicename);
1112 if(snum < 0 || !lp_msdfs_root(snum)) {
1113 DEBUG(4,("create_junction: %s is not an msdfs root.\n",
1114 pdp->servicename));
1115 TALLOC_FREE(pdp);
1116 return False;
1119 jucn->service_name = talloc_strdup(ctx, pdp->servicename);
1120 jucn->volume_name = talloc_strdup(ctx, pdp->reqpath);
1121 jucn->comment = talloc_strdup(ctx, lp_comment(snum));
1123 TALLOC_FREE(pdp);
1124 if (!jucn->service_name || !jucn->volume_name || ! jucn->comment) {
1125 return False;
1127 return True;
1130 /**********************************************************************
1131 Forms a valid Unix pathname from the junction
1132 **********************************************************************/
1134 static bool junction_to_local_path(const struct junction_map *jucn,
1135 char **pp_path_out,
1136 connection_struct **conn_out,
1137 char **oldpath)
1139 int snum;
1140 NTSTATUS status;
1142 snum = lp_servicenumber(jucn->service_name);
1143 if(snum < 0) {
1144 return False;
1146 status = create_conn_struct(talloc_tos(), smbd_server_conn, conn_out,
1147 snum, lp_pathname(snum), NULL, oldpath);
1148 if (!NT_STATUS_IS_OK(status)) {
1149 return False;
1152 *pp_path_out = talloc_asprintf(*conn_out,
1153 "%s/%s",
1154 lp_pathname(snum),
1155 jucn->volume_name);
1156 if (!*pp_path_out) {
1157 vfs_ChDir(*conn_out, *oldpath);
1158 SMB_VFS_DISCONNECT(*conn_out);
1159 conn_free(*conn_out);
1160 return False;
1162 return True;
1165 bool create_msdfs_link(const struct junction_map *jucn)
1167 char *path = NULL;
1168 char *cwd;
1169 char *msdfs_link = NULL;
1170 connection_struct *conn;
1171 int i=0;
1172 bool insert_comma = False;
1173 bool ret = False;
1175 if(!junction_to_local_path(jucn, &path, &conn, &cwd)) {
1176 return False;
1179 /* Form the msdfs_link contents */
1180 msdfs_link = talloc_strdup(conn, "msdfs:");
1181 if (!msdfs_link) {
1182 goto out;
1184 for(i=0; i<jucn->referral_count; i++) {
1185 char *refpath = jucn->referral_list[i].alternate_path;
1187 /* Alternate paths always use Windows separators. */
1188 trim_char(refpath, '\\', '\\');
1189 if(*refpath == '\0') {
1190 if (i == 0) {
1191 insert_comma = False;
1193 continue;
1195 if (i > 0 && insert_comma) {
1196 msdfs_link = talloc_asprintf_append_buffer(msdfs_link,
1197 ",%s",
1198 refpath);
1199 } else {
1200 msdfs_link = talloc_asprintf_append_buffer(msdfs_link,
1201 "%s",
1202 refpath);
1205 if (!msdfs_link) {
1206 goto out;
1208 if (!insert_comma) {
1209 insert_comma = True;
1213 DEBUG(5,("create_msdfs_link: Creating new msdfs link: %s -> %s\n",
1214 path, msdfs_link));
1216 if(SMB_VFS_SYMLINK(conn, msdfs_link, path) < 0) {
1217 if (errno == EEXIST) {
1218 struct smb_filename *smb_fname = NULL;
1219 NTSTATUS status;
1221 status = create_synthetic_smb_fname(talloc_tos(), path,
1222 NULL, NULL,
1223 &smb_fname);
1224 if (!NT_STATUS_IS_OK(status)) {
1225 errno = map_errno_from_nt_status(status);
1226 goto out;
1229 if(SMB_VFS_UNLINK(conn, smb_fname)!=0) {
1230 TALLOC_FREE(smb_fname);
1231 goto out;
1233 TALLOC_FREE(smb_fname);
1235 if (SMB_VFS_SYMLINK(conn, msdfs_link, path) < 0) {
1236 DEBUG(1,("create_msdfs_link: symlink failed "
1237 "%s -> %s\nError: %s\n",
1238 path, msdfs_link, strerror(errno)));
1239 goto out;
1243 ret = True;
1245 out:
1246 vfs_ChDir(conn, cwd);
1247 SMB_VFS_DISCONNECT(conn);
1248 conn_free(conn);
1249 return ret;
1252 bool remove_msdfs_link(const struct junction_map *jucn)
1254 char *path = NULL;
1255 char *cwd;
1256 connection_struct *conn;
1257 bool ret = False;
1258 struct smb_filename *smb_fname = NULL;
1259 NTSTATUS status;
1261 if (!junction_to_local_path(jucn, &path, &conn, &cwd)) {
1262 return false;
1265 status = create_synthetic_smb_fname(talloc_tos(), path,
1266 NULL, NULL,
1267 &smb_fname);
1268 if (!NT_STATUS_IS_OK(status)) {
1269 errno = map_errno_from_nt_status(status);
1270 return false;
1273 if( SMB_VFS_UNLINK(conn, smb_fname) == 0 ) {
1274 ret = True;
1277 TALLOC_FREE(smb_fname);
1278 vfs_ChDir(conn, cwd);
1279 SMB_VFS_DISCONNECT(conn);
1280 conn_free(conn);
1281 return ret;
1284 /*********************************************************************
1285 Return the number of DFS links at the root of this share.
1286 *********************************************************************/
1288 static int count_dfs_links(TALLOC_CTX *ctx, int snum)
1290 size_t cnt = 0;
1291 DIR *dirp = NULL;
1292 const char *dname = NULL;
1293 char *talloced = NULL;
1294 const char *connect_path = lp_pathname(snum);
1295 const char *msdfs_proxy = lp_msdfs_proxy(snum);
1296 connection_struct *conn;
1297 NTSTATUS status;
1298 char *cwd;
1300 if(*connect_path == '\0') {
1301 return 0;
1305 * Fake up a connection struct for the VFS layer.
1308 status = create_conn_struct(talloc_tos(), smbd_server_conn, &conn,
1309 snum, connect_path, NULL, &cwd);
1310 if (!NT_STATUS_IS_OK(status)) {
1311 DEBUG(3, ("create_conn_struct failed: %s\n",
1312 nt_errstr(status)));
1313 return 0;
1316 /* Count a link for the msdfs root - convention */
1317 cnt = 1;
1319 /* No more links if this is an msdfs proxy. */
1320 if (*msdfs_proxy != '\0') {
1321 goto out;
1324 /* Now enumerate all dfs links */
1325 dirp = SMB_VFS_OPENDIR(conn, ".", NULL, 0);
1326 if(!dirp) {
1327 goto out;
1330 while ((dname = vfs_readdirname(conn, dirp, NULL, &talloced))
1331 != NULL) {
1332 if (is_msdfs_link(conn,
1333 dname,
1334 NULL)) {
1335 cnt++;
1337 TALLOC_FREE(talloced);
1340 SMB_VFS_CLOSEDIR(conn,dirp);
1342 out:
1343 vfs_ChDir(conn, cwd);
1344 SMB_VFS_DISCONNECT(conn);
1345 conn_free(conn);
1346 return cnt;
1349 /*********************************************************************
1350 *********************************************************************/
1352 static int form_junctions(TALLOC_CTX *ctx,
1353 int snum,
1354 struct junction_map *jucn,
1355 size_t jn_remain)
1357 size_t cnt = 0;
1358 DIR *dirp = NULL;
1359 const char *dname = NULL;
1360 char *talloced = NULL;
1361 const char *connect_path = lp_pathname(snum);
1362 char *service_name = lp_servicename(snum);
1363 const char *msdfs_proxy = lp_msdfs_proxy(snum);
1364 connection_struct *conn;
1365 struct referral *ref = NULL;
1366 char *cwd;
1367 NTSTATUS status;
1369 if (jn_remain == 0) {
1370 return 0;
1373 if(*connect_path == '\0') {
1374 return 0;
1378 * Fake up a connection struct for the VFS layer.
1381 status = create_conn_struct(ctx, smbd_server_conn, &conn, snum, connect_path, NULL,
1382 &cwd);
1383 if (!NT_STATUS_IS_OK(status)) {
1384 DEBUG(3, ("create_conn_struct failed: %s\n",
1385 nt_errstr(status)));
1386 return 0;
1389 /* form a junction for the msdfs root - convention
1390 DO NOT REMOVE THIS: NT clients will not work with us
1391 if this is not present
1393 jucn[cnt].service_name = talloc_strdup(ctx,service_name);
1394 jucn[cnt].volume_name = talloc_strdup(ctx, "");
1395 if (!jucn[cnt].service_name || !jucn[cnt].volume_name) {
1396 goto out;
1398 jucn[cnt].comment = "";
1399 jucn[cnt].referral_count = 1;
1401 ref = jucn[cnt].referral_list = talloc_zero(ctx, struct referral);
1402 if (jucn[cnt].referral_list == NULL) {
1403 goto out;
1406 ref->proximity = 0;
1407 ref->ttl = REFERRAL_TTL;
1408 if (*msdfs_proxy != '\0') {
1409 ref->alternate_path = talloc_strdup(ctx,
1410 msdfs_proxy);
1411 } else {
1412 ref->alternate_path = talloc_asprintf(ctx,
1413 "\\\\%s\\%s",
1414 get_local_machine_name(),
1415 service_name);
1418 if (!ref->alternate_path) {
1419 goto out;
1421 cnt++;
1423 /* Don't enumerate if we're an msdfs proxy. */
1424 if (*msdfs_proxy != '\0') {
1425 goto out;
1428 /* Now enumerate all dfs links */
1429 dirp = SMB_VFS_OPENDIR(conn, ".", NULL, 0);
1430 if(!dirp) {
1431 goto out;
1434 while ((dname = vfs_readdirname(conn, dirp, NULL, &talloced))
1435 != NULL) {
1436 char *link_target = NULL;
1437 if (cnt >= jn_remain) {
1438 DEBUG(2, ("form_junctions: ran out of MSDFS "
1439 "junction slots"));
1440 TALLOC_FREE(talloced);
1441 goto out;
1443 if (is_msdfs_link_internal(ctx,
1444 conn,
1445 dname, &link_target,
1446 NULL)) {
1447 if (parse_msdfs_symlink(ctx,
1448 link_target,
1449 &jucn[cnt].referral_list,
1450 &jucn[cnt].referral_count)) {
1452 jucn[cnt].service_name = talloc_strdup(ctx,
1453 service_name);
1454 jucn[cnt].volume_name = talloc_strdup(ctx,
1455 dname);
1456 if (!jucn[cnt].service_name ||
1457 !jucn[cnt].volume_name) {
1458 TALLOC_FREE(talloced);
1459 goto out;
1461 jucn[cnt].comment = "";
1462 cnt++;
1464 TALLOC_FREE(link_target);
1466 TALLOC_FREE(talloced);
1469 out:
1471 if (dirp) {
1472 SMB_VFS_CLOSEDIR(conn,dirp);
1475 vfs_ChDir(conn, cwd);
1476 conn_free(conn);
1477 return cnt;
1480 struct junction_map *enum_msdfs_links(TALLOC_CTX *ctx, size_t *p_num_jn)
1482 struct junction_map *jn = NULL;
1483 int i=0;
1484 size_t jn_count = 0;
1485 int sharecount = 0;
1487 *p_num_jn = 0;
1488 if(!lp_host_msdfs()) {
1489 return NULL;
1492 /* Ensure all the usershares are loaded. */
1493 become_root();
1494 load_registry_shares();
1495 sharecount = load_usershare_shares(NULL, connections_snum_used);
1496 unbecome_root();
1498 for(i=0;i < sharecount;i++) {
1499 if(lp_msdfs_root(i)) {
1500 jn_count += count_dfs_links(ctx, i);
1503 if (jn_count == 0) {
1504 return NULL;
1506 jn = talloc_array(ctx, struct junction_map, jn_count);
1507 if (!jn) {
1508 return NULL;
1510 for(i=0; i < sharecount; i++) {
1511 if (*p_num_jn >= jn_count) {
1512 break;
1514 if(lp_msdfs_root(i)) {
1515 *p_num_jn += form_junctions(ctx, i,
1516 &jn[*p_num_jn],
1517 jn_count - *p_num_jn);
1520 return jn;
1523 /******************************************************************************
1524 Core function to resolve a dfs pathname possibly containing a wildcard. If
1525 ppath_contains_wcard != NULL, it will be set to true if a wildcard is
1526 detected during dfs resolution.
1527 ******************************************************************************/
1529 NTSTATUS resolve_dfspath_wcard(TALLOC_CTX *ctx,
1530 connection_struct *conn,
1531 bool dfs_pathnames,
1532 const char *name_in,
1533 bool allow_wcards,
1534 char **pp_name_out,
1535 bool *ppath_contains_wcard)
1537 bool path_contains_wcard;
1538 NTSTATUS status = NT_STATUS_OK;
1540 if (dfs_pathnames) {
1541 status = dfs_redirect(ctx,
1542 conn,
1543 name_in,
1544 allow_wcards,
1545 !smbd_server_conn->using_smb2,
1546 pp_name_out,
1547 &path_contains_wcard);
1549 if (NT_STATUS_IS_OK(status) && ppath_contains_wcard != NULL) {
1550 *ppath_contains_wcard = path_contains_wcard;
1552 } else {
1554 * Cheat and just return a copy of the in ptr.
1555 * Once srvstr_get_path() uses talloc it'll
1556 * be a talloced ptr anyway.
1558 *pp_name_out = discard_const_p(char, name_in);
1560 return status;