s4-smbtorture: add ndr test for nbt_netlogon_packet to avoid future regressions.
[Samba/gebeck_regimport.git] / source3 / smbd / msdfs.c
blobde700423aeef4cc8c6fa78d62b7a90104944cc7c
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;
267 conn->sconn = sconn;
268 conn->sconn->num_tcons_open++;
270 if (session_info != NULL) {
271 conn->session_info = copy_session_info(conn, session_info);
272 if (conn->session_info == NULL) {
273 DEBUG(0, ("copy_serverinfo failed\n"));
274 TALLOC_FREE(conn);
275 return NT_STATUS_NO_MEMORY;
277 vfs_user = conn->session_info->unix_info->unix_name;
278 } else {
279 /* use current authenticated user in absence of session_info */
280 vfs_user = get_current_username();
283 set_conn_connectpath(conn, connpath);
286 * New code to check if there's a share security descripter
287 * added from NT server manager. This is done after the
288 * smb.conf checks are done as we need a uid and token. JRA.
291 if (conn->session_info) {
292 share_access_check(conn->session_info->security_token,
293 lp_servicename(snum), MAXIMUM_ALLOWED_ACCESS,
294 &conn->share_access);
296 if ((conn->share_access & FILE_WRITE_DATA) == 0) {
297 if ((conn->share_access & FILE_READ_DATA) == 0) {
298 /* No access, read or write. */
299 DEBUG(0,("create_conn_struct: connection to %s "
300 "denied due to security "
301 "descriptor.\n",
302 lp_servicename(snum)));
303 conn_free(conn);
304 return NT_STATUS_ACCESS_DENIED;
305 } else {
306 conn->read_only = true;
309 } else {
310 conn->share_access = 0;
311 conn->read_only = true;
314 if (!smbd_vfs_init(conn)) {
315 NTSTATUS status = map_nt_error_from_unix(errno);
316 DEBUG(0,("create_conn_struct: smbd_vfs_init failed.\n"));
317 conn_free(conn);
318 return status;
321 /* this must be the first filesystem operation that we do */
322 if (SMB_VFS_CONNECT(conn, lp_servicename(snum), vfs_user) < 0) {
323 DEBUG(0,("VFS connect failed!\n"));
324 conn_free(conn);
325 return NT_STATUS_UNSUCCESSFUL;
328 conn->fs_capabilities = SMB_VFS_FS_CAPABILITIES(conn, &conn->ts_res);
331 * Windows seems to insist on doing trans2getdfsreferral() calls on
332 * the IPC$ share as the anonymous user. If we try to chdir as that
333 * user we will fail.... WTF ? JRA.
336 oldcwd = vfs_GetWd(ctx, conn);
337 if (oldcwd == NULL) {
338 NTSTATUS status = map_nt_error_from_unix(errno);
339 DEBUG(3, ("vfs_GetWd failed: %s\n", strerror(errno)));
340 conn_free(conn);
341 return status;
344 if (vfs_ChDir(conn,conn->connectpath) != 0) {
345 NTSTATUS status = map_nt_error_from_unix(errno);
346 DEBUG(3,("create_conn_struct: Can't ChDir to new conn path %s. "
347 "Error was %s\n",
348 conn->connectpath, strerror(errno) ));
349 conn_free(conn);
350 return status;
353 *pconn = conn;
354 *poldcwd = oldcwd;
356 return NT_STATUS_OK;
359 /**********************************************************************
360 Parse the contents of a symlink to verify if it is an msdfs referral
361 A valid referral is of the form:
363 msdfs:server1\share1,server2\share2
364 msdfs:server1\share1\pathname,server2\share2\pathname
365 msdfs:server1/share1,server2/share2
366 msdfs:server1/share1/pathname,server2/share2/pathname.
368 Note that the alternate paths returned here must be of the canonicalized
369 form:
371 \server\share or
372 \server\share\path\to\file,
374 even in posix path mode. This is because we have no knowledge if the
375 server we're referring to understands posix paths.
376 **********************************************************************/
378 static bool parse_msdfs_symlink(TALLOC_CTX *ctx,
379 const char *target,
380 struct referral **preflist,
381 int *refcount)
383 char *temp = NULL;
384 char *prot;
385 char **alt_path = NULL;
386 int count = 0, i;
387 struct referral *reflist;
388 char *saveptr;
390 temp = talloc_strdup(ctx, target);
391 if (!temp) {
392 return False;
394 prot = strtok_r(temp, ":", &saveptr);
395 if (!prot) {
396 DEBUG(0,("parse_msdfs_symlink: invalid path !\n"));
397 return False;
400 alt_path = talloc_array(ctx, char *, MAX_REFERRAL_COUNT);
401 if (!alt_path) {
402 return False;
405 /* parse out the alternate paths */
406 while((count<MAX_REFERRAL_COUNT) &&
407 ((alt_path[count] = strtok_r(NULL, ",", &saveptr)) != NULL)) {
408 count++;
411 DEBUG(10,("parse_msdfs_symlink: count=%d\n", count));
413 if (count) {
414 reflist = *preflist = talloc_zero_array(ctx,
415 struct referral, count);
416 if(reflist == NULL) {
417 TALLOC_FREE(alt_path);
418 return False;
420 } else {
421 reflist = *preflist = NULL;
424 for(i=0;i<count;i++) {
425 char *p;
427 /* Canonicalize link target.
428 * Replace all /'s in the path by a \ */
429 string_replace(alt_path[i], '/', '\\');
431 /* Remove leading '\\'s */
432 p = alt_path[i];
433 while (*p && (*p == '\\')) {
434 p++;
437 reflist[i].alternate_path = talloc_asprintf(ctx,
438 "\\%s",
440 if (!reflist[i].alternate_path) {
441 return False;
444 reflist[i].proximity = 0;
445 reflist[i].ttl = REFERRAL_TTL;
446 DEBUG(10, ("parse_msdfs_symlink: Created alt path: %s\n",
447 reflist[i].alternate_path));
450 *refcount = count;
452 TALLOC_FREE(alt_path);
453 return True;
456 /**********************************************************************
457 Returns true if the unix path is a valid msdfs symlink and also
458 returns the target string from inside the link.
459 **********************************************************************/
461 static bool is_msdfs_link_internal(TALLOC_CTX *ctx,
462 connection_struct *conn,
463 const char *path,
464 char **pp_link_target,
465 SMB_STRUCT_STAT *sbufp)
467 int referral_len = 0;
468 #if defined(HAVE_BROKEN_READLINK)
469 char link_target_buf[PATH_MAX];
470 #else
471 char link_target_buf[7];
472 #endif
473 size_t bufsize = 0;
474 char *link_target = NULL;
475 struct smb_filename smb_fname;
477 if (pp_link_target) {
478 bufsize = 1024;
479 link_target = talloc_array(ctx, char, bufsize);
480 if (!link_target) {
481 return False;
483 *pp_link_target = link_target;
484 } else {
485 bufsize = sizeof(link_target_buf);
486 link_target = link_target_buf;
489 ZERO_STRUCT(smb_fname);
490 smb_fname.base_name = discard_const_p(char, path);
492 if (SMB_VFS_LSTAT(conn, &smb_fname) != 0) {
493 DEBUG(5,("is_msdfs_link_read_target: %s does not exist.\n",
494 path));
495 goto err;
497 if (!S_ISLNK(smb_fname.st.st_ex_mode)) {
498 DEBUG(5,("is_msdfs_link_read_target: %s is not a link.\n",
499 path));
500 goto err;
502 if (sbufp != NULL) {
503 *sbufp = smb_fname.st;
506 referral_len = SMB_VFS_READLINK(conn, path, link_target, bufsize - 1);
507 if (referral_len == -1) {
508 DEBUG(0,("is_msdfs_link_read_target: Error reading "
509 "msdfs link %s: %s\n",
510 path, strerror(errno)));
511 goto err;
513 link_target[referral_len] = '\0';
515 DEBUG(5,("is_msdfs_link_internal: %s -> %s\n",path,
516 link_target));
518 if (!strnequal(link_target, "msdfs:", 6)) {
519 goto err;
521 return True;
523 err:
525 if (link_target != link_target_buf) {
526 TALLOC_FREE(link_target);
528 return False;
531 /**********************************************************************
532 Returns true if the unix path is a valid msdfs symlink.
533 **********************************************************************/
535 bool is_msdfs_link(connection_struct *conn,
536 const char *path,
537 SMB_STRUCT_STAT *sbufp)
539 return is_msdfs_link_internal(talloc_tos(),
540 conn,
541 path,
542 NULL,
543 sbufp);
546 /*****************************************************************
547 Used by other functions to decide if a dfs path is remote,
548 and to get the list of referred locations for that remote path.
550 search_flag: For findfirsts, dfs links themselves are not
551 redirected, but paths beyond the links are. For normal smb calls,
552 even dfs links need to be redirected.
554 consumedcntp: how much of the dfs path is being redirected. the client
555 should try the remaining path on the redirected server.
557 If this returns NT_STATUS_PATH_NOT_COVERED the contents of the msdfs
558 link redirect are in targetpath.
559 *****************************************************************/
561 static NTSTATUS dfs_path_lookup(TALLOC_CTX *ctx,
562 connection_struct *conn,
563 const char *dfspath, /* Incoming complete dfs path */
564 const struct dfs_path *pdp, /* Parsed out
565 server+share+extrapath. */
566 bool search_flag, /* Called from a findfirst ? */
567 int *consumedcntp,
568 char **pp_targetpath)
570 char *p = NULL;
571 char *q = NULL;
572 NTSTATUS status;
573 struct smb_filename *smb_fname = NULL;
574 char *canon_dfspath = NULL; /* Canonicalized dfs path. (only '/'
575 components). */
577 DEBUG(10,("dfs_path_lookup: Conn path = %s reqpath = %s\n",
578 conn->connectpath, pdp->reqpath));
581 * Note the unix path conversion here we're doing we
582 * throw away. We're looking for a symlink for a dfs
583 * resolution, if we don't find it we'll do another
584 * unix_convert later in the codepath.
587 status = unix_convert(ctx, conn, pdp->reqpath, &smb_fname,
588 search_flag ? UCF_ALWAYS_ALLOW_WCARD_LCOMP : 0);
590 if (!NT_STATUS_IS_OK(status)) {
591 if (!NT_STATUS_EQUAL(status,
592 NT_STATUS_OBJECT_PATH_NOT_FOUND)) {
593 return status;
595 if (smb_fname == NULL || smb_fname->base_name == NULL) {
596 return status;
600 /* Optimization - check if we can redirect the whole path. */
602 if (is_msdfs_link_internal(ctx, conn, smb_fname->base_name,
603 pp_targetpath, NULL)) {
604 if (search_flag) {
605 DEBUG(6,("dfs_path_lookup (FindFirst) No redirection "
606 "for dfs link %s.\n", dfspath));
607 status = NT_STATUS_OK;
608 goto out;
611 DEBUG(6,("dfs_path_lookup: %s resolves to a "
612 "valid dfs link %s.\n", dfspath,
613 pp_targetpath ? *pp_targetpath : ""));
615 if (consumedcntp) {
616 *consumedcntp = strlen(dfspath);
618 status = NT_STATUS_PATH_NOT_COVERED;
619 goto out;
622 /* Prepare to test only for '/' components in the given path,
623 * so if a Windows path replace all '\\' characters with '/'.
624 * For a POSIX DFS path we know all separators are already '/'. */
626 canon_dfspath = talloc_strdup(ctx, dfspath);
627 if (!canon_dfspath) {
628 status = NT_STATUS_NO_MEMORY;
629 goto out;
631 if (!pdp->posix_path) {
632 string_replace(canon_dfspath, '\\', '/');
636 * localpath comes out of unix_convert, so it has
637 * no trailing backslash. Make sure that canon_dfspath hasn't either.
638 * Fix for bug #4860 from Jan Martin <Jan.Martin@rwedea.com>.
641 trim_char(canon_dfspath,0,'/');
644 * Redirect if any component in the path is a link.
645 * We do this by walking backwards through the
646 * local path, chopping off the last component
647 * in both the local path and the canonicalized
648 * DFS path. If we hit a DFS link then we're done.
651 p = strrchr_m(smb_fname->base_name, '/');
652 if (consumedcntp) {
653 q = strrchr_m(canon_dfspath, '/');
656 while (p) {
657 *p = '\0';
658 if (q) {
659 *q = '\0';
662 if (is_msdfs_link_internal(ctx, conn,
663 smb_fname->base_name, pp_targetpath,
664 NULL)) {
665 DEBUG(4, ("dfs_path_lookup: Redirecting %s because "
666 "parent %s is dfs link\n", dfspath,
667 smb_fname_str_dbg(smb_fname)));
669 if (consumedcntp) {
670 *consumedcntp = strlen(canon_dfspath);
671 DEBUG(10, ("dfs_path_lookup: Path consumed: %s "
672 "(%d)\n",
673 canon_dfspath,
674 *consumedcntp));
677 status = NT_STATUS_PATH_NOT_COVERED;
678 goto out;
681 /* Step back on the filesystem. */
682 p = strrchr_m(smb_fname->base_name, '/');
684 if (consumedcntp) {
685 /* And in the canonicalized dfs path. */
686 q = strrchr_m(canon_dfspath, '/');
690 status = NT_STATUS_OK;
691 out:
692 TALLOC_FREE(smb_fname);
693 return status;
696 /*****************************************************************
697 Decides if a dfs pathname should be redirected or not.
698 If not, the pathname is converted to a tcon-relative local unix path
700 search_wcard_flag: this flag performs 2 functions both related
701 to searches. See resolve_dfs_path() and parse_dfs_path_XX()
702 for details.
704 This function can return NT_STATUS_OK, meaning use the returned path as-is
705 (mapped into a local path).
706 or NT_STATUS_NOT_COVERED meaning return a DFS redirect, or
707 any other NT_STATUS error which is a genuine error to be
708 returned to the client.
709 *****************************************************************/
711 static NTSTATUS dfs_redirect(TALLOC_CTX *ctx,
712 connection_struct *conn,
713 const char *path_in,
714 bool search_wcard_flag,
715 bool allow_broken_path,
716 char **pp_path_out,
717 bool *ppath_contains_wcard)
719 NTSTATUS status;
720 struct dfs_path *pdp = talloc(ctx, struct dfs_path);
722 if (!pdp) {
723 return NT_STATUS_NO_MEMORY;
726 status = parse_dfs_path(conn, path_in, search_wcard_flag,
727 allow_broken_path, pdp,
728 ppath_contains_wcard);
729 if (!NT_STATUS_IS_OK(status)) {
730 TALLOC_FREE(pdp);
731 return status;
734 if (pdp->reqpath[0] == '\0') {
735 TALLOC_FREE(pdp);
736 *pp_path_out = talloc_strdup(ctx, "");
737 if (!*pp_path_out) {
738 return NT_STATUS_NO_MEMORY;
740 DEBUG(5,("dfs_redirect: self-referral.\n"));
741 return NT_STATUS_OK;
744 /* If dfs pathname for a non-dfs share, convert to tcon-relative
745 path and return OK */
747 if (!lp_msdfs_root(SNUM(conn))) {
748 *pp_path_out = talloc_strdup(ctx, pdp->reqpath);
749 TALLOC_FREE(pdp);
750 if (!*pp_path_out) {
751 return NT_STATUS_NO_MEMORY;
753 return NT_STATUS_OK;
756 /* If it looked like a local path (zero hostname/servicename)
757 * just treat as a tcon-relative path. */
759 if (pdp->hostname[0] == '\0' && pdp->servicename[0] == '\0') {
760 *pp_path_out = talloc_strdup(ctx, pdp->reqpath);
761 TALLOC_FREE(pdp);
762 if (!*pp_path_out) {
763 return NT_STATUS_NO_MEMORY;
765 return NT_STATUS_OK;
768 if (!( strequal(pdp->servicename, lp_servicename(SNUM(conn)))
769 || (strequal(pdp->servicename, HOMES_NAME)
770 && strequal(lp_servicename(SNUM(conn)),
771 conn->session_info->unix_info->sanitized_username) )) ) {
773 /* The given sharename doesn't match this connection. */
774 TALLOC_FREE(pdp);
776 return NT_STATUS_OBJECT_PATH_NOT_FOUND;
779 status = dfs_path_lookup(ctx, conn, path_in, pdp,
780 search_wcard_flag, NULL, NULL);
781 if (!NT_STATUS_IS_OK(status)) {
782 if (NT_STATUS_EQUAL(status, NT_STATUS_PATH_NOT_COVERED)) {
783 DEBUG(3,("dfs_redirect: Redirecting %s\n", path_in));
784 } else {
785 DEBUG(10,("dfs_redirect: dfs_path_lookup "
786 "failed for %s with %s\n",
787 path_in, nt_errstr(status) ));
789 return status;
792 DEBUG(3,("dfs_redirect: Not redirecting %s.\n", path_in));
794 /* Form non-dfs tcon-relative path */
795 *pp_path_out = talloc_strdup(ctx, pdp->reqpath);
796 TALLOC_FREE(pdp);
797 if (!*pp_path_out) {
798 return NT_STATUS_NO_MEMORY;
801 DEBUG(3,("dfs_redirect: Path %s converted to non-dfs path %s\n",
802 path_in,
803 *pp_path_out));
805 return NT_STATUS_OK;
808 /**********************************************************************
809 Return a self referral.
810 **********************************************************************/
812 static NTSTATUS self_ref(TALLOC_CTX *ctx,
813 const char *dfs_path,
814 struct junction_map *jucn,
815 int *consumedcntp,
816 bool *self_referralp)
818 struct referral *ref;
820 *self_referralp = True;
822 jucn->referral_count = 1;
823 if((ref = talloc_zero(ctx, struct referral)) == NULL) {
824 return NT_STATUS_NO_MEMORY;
827 ref->alternate_path = talloc_strdup(ctx, dfs_path);
828 if (!ref->alternate_path) {
829 return NT_STATUS_NO_MEMORY;
831 ref->proximity = 0;
832 ref->ttl = REFERRAL_TTL;
833 jucn->referral_list = ref;
834 *consumedcntp = strlen(dfs_path);
835 return NT_STATUS_OK;
838 /**********************************************************************
839 Gets valid referrals for a dfs path and fills up the
840 junction_map structure.
841 **********************************************************************/
843 NTSTATUS get_referred_path(TALLOC_CTX *ctx,
844 const char *dfs_path,
845 struct smbd_server_connection *sconn,
846 struct junction_map *jucn,
847 int *consumedcntp,
848 bool *self_referralp)
850 struct connection_struct *conn;
851 char *targetpath = NULL;
852 int snum;
853 NTSTATUS status = NT_STATUS_NOT_FOUND;
854 bool dummy;
855 struct dfs_path *pdp = talloc(ctx, struct dfs_path);
856 char *oldpath;
858 if (!pdp) {
859 return NT_STATUS_NO_MEMORY;
862 *self_referralp = False;
864 status = parse_dfs_path(NULL, dfs_path, False, !sconn->using_smb2,
865 pdp, &dummy);
866 if (!NT_STATUS_IS_OK(status)) {
867 return status;
870 jucn->service_name = talloc_strdup(ctx, pdp->servicename);
871 jucn->volume_name = talloc_strdup(ctx, pdp->reqpath);
872 if (!jucn->service_name || !jucn->volume_name) {
873 TALLOC_FREE(pdp);
874 return NT_STATUS_NO_MEMORY;
877 /* Verify the share is a dfs root */
878 snum = lp_servicenumber(jucn->service_name);
879 if(snum < 0) {
880 char *service_name = NULL;
881 if ((snum = find_service(ctx, jucn->service_name, &service_name)) < 0) {
882 return NT_STATUS_NOT_FOUND;
884 if (!service_name) {
885 return NT_STATUS_NO_MEMORY;
887 TALLOC_FREE(jucn->service_name);
888 jucn->service_name = talloc_strdup(ctx, service_name);
889 if (!jucn->service_name) {
890 TALLOC_FREE(pdp);
891 return NT_STATUS_NO_MEMORY;
895 if (!lp_msdfs_root(snum) && (*lp_msdfs_proxy(snum) == '\0')) {
896 DEBUG(3,("get_referred_path: |%s| in dfs path %s is not "
897 "a dfs root.\n",
898 pdp->servicename, dfs_path));
899 TALLOC_FREE(pdp);
900 return NT_STATUS_NOT_FOUND;
904 * Self referrals are tested with a anonymous IPC connection and
905 * a GET_DFS_REFERRAL call to \\server\share. (which means
906 * dp.reqpath[0] points to an empty string). create_conn_struct cd's
907 * into the directory and will fail if it cannot (as the anonymous
908 * user). Cope with this.
911 if (pdp->reqpath[0] == '\0') {
912 char *tmp;
913 struct referral *ref;
915 if (*lp_msdfs_proxy(snum) == '\0') {
916 TALLOC_FREE(pdp);
917 return self_ref(ctx,
918 dfs_path,
919 jucn,
920 consumedcntp,
921 self_referralp);
925 * It's an msdfs proxy share. Redirect to
926 * the configured target share.
929 jucn->referral_count = 1;
930 if ((ref = talloc_zero(ctx, struct referral)) == NULL) {
931 TALLOC_FREE(pdp);
932 return NT_STATUS_NO_MEMORY;
935 if (!(tmp = talloc_strdup(ctx, lp_msdfs_proxy(snum)))) {
936 TALLOC_FREE(pdp);
937 return NT_STATUS_NO_MEMORY;
940 trim_string(tmp, "\\", 0);
942 ref->alternate_path = talloc_asprintf(ctx, "\\%s", tmp);
943 TALLOC_FREE(tmp);
945 if (!ref->alternate_path) {
946 TALLOC_FREE(pdp);
947 return NT_STATUS_NO_MEMORY;
950 if (pdp->reqpath[0] != '\0') {
951 ref->alternate_path = talloc_asprintf_append(
952 ref->alternate_path,
953 "%s",
954 pdp->reqpath);
955 if (!ref->alternate_path) {
956 TALLOC_FREE(pdp);
957 return NT_STATUS_NO_MEMORY;
960 ref->proximity = 0;
961 ref->ttl = REFERRAL_TTL;
962 jucn->referral_list = ref;
963 *consumedcntp = strlen(dfs_path);
964 TALLOC_FREE(pdp);
965 return NT_STATUS_OK;
968 status = create_conn_struct(ctx, sconn, &conn, snum,
969 lp_pathname(snum), NULL, &oldpath);
970 if (!NT_STATUS_IS_OK(status)) {
971 TALLOC_FREE(pdp);
972 return status;
975 /* If this is a DFS path dfs_lookup should return
976 * NT_STATUS_PATH_NOT_COVERED. */
978 status = dfs_path_lookup(ctx, conn, dfs_path, pdp,
979 False, consumedcntp, &targetpath);
981 if (!NT_STATUS_EQUAL(status, NT_STATUS_PATH_NOT_COVERED)) {
982 DEBUG(3,("get_referred_path: No valid referrals for path %s\n",
983 dfs_path));
984 goto err_exit;
987 /* We know this is a valid dfs link. Parse the targetpath. */
988 if (!parse_msdfs_symlink(ctx, targetpath,
989 &jucn->referral_list,
990 &jucn->referral_count)) {
991 DEBUG(3,("get_referred_path: failed to parse symlink "
992 "target %s\n", targetpath ));
993 status = NT_STATUS_NOT_FOUND;
994 goto err_exit;
997 status = NT_STATUS_OK;
998 err_exit:
999 vfs_ChDir(conn, oldpath);
1000 SMB_VFS_DISCONNECT(conn);
1001 conn_free(conn);
1002 TALLOC_FREE(pdp);
1003 return status;
1006 /******************************************************************
1007 Set up the DFS referral for the dfs pathname. This call returns
1008 the amount of the path covered by this server, and where the
1009 client should be redirected to. This is the meat of the
1010 TRANS2_GET_DFS_REFERRAL call.
1011 ******************************************************************/
1013 int setup_dfs_referral(connection_struct *orig_conn,
1014 const char *dfs_path,
1015 int max_referral_level,
1016 char **ppdata, NTSTATUS *pstatus)
1018 char *pdata = *ppdata;
1019 int reply_size = 0;
1020 struct dfs_GetDFSReferral *r;
1021 DATA_BLOB blob = data_blob_null;
1022 NTSTATUS status;
1023 enum ndr_err_code ndr_err;
1025 r = talloc_zero(talloc_tos(), struct dfs_GetDFSReferral);
1026 if (r == NULL) {
1027 *pstatus = NT_STATUS_NO_MEMORY;
1028 return -1;
1031 r->in.req.max_referral_level = max_referral_level;
1032 r->in.req.servername = talloc_strdup(r, dfs_path);
1033 if (r->in.req.servername == NULL) {
1034 talloc_free(r);
1035 *pstatus = NT_STATUS_NO_MEMORY;
1036 return -1;
1039 status = SMB_VFS_GET_DFS_REFERRALS(orig_conn, r);
1040 if (!NT_STATUS_IS_OK(status)) {
1041 talloc_free(r);
1042 *pstatus = status;
1043 return -1;
1046 ndr_err = ndr_push_struct_blob(&blob, r,
1047 r->out.resp,
1048 (ndr_push_flags_fn_t)ndr_push_dfs_referral_resp);
1049 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1050 TALLOC_FREE(r);
1051 *pstatus = NT_STATUS_INVALID_PARAMETER;
1052 return -1;
1055 pdata = (char *)SMB_REALLOC(pdata, blob.length);
1056 if(pdata == NULL) {
1057 TALLOC_FREE(r);
1058 DEBUG(0,("referral setup:"
1059 "malloc failed for Realloc!\n"));
1060 return -1;
1062 *ppdata = pdata;
1063 reply_size = blob.length;
1064 memcpy(pdata, blob.data, blob.length);
1065 TALLOC_FREE(r);
1067 *pstatus = NT_STATUS_OK;
1068 return reply_size;
1071 /**********************************************************************
1072 The following functions are called by the NETDFS RPC pipe functions
1073 **********************************************************************/
1075 /*********************************************************************
1076 Creates a junction structure from a DFS pathname
1077 **********************************************************************/
1079 bool create_junction(TALLOC_CTX *ctx,
1080 const char *dfs_path,
1081 bool allow_broken_path,
1082 struct junction_map *jucn)
1084 int snum;
1085 bool dummy;
1086 struct dfs_path *pdp = talloc(ctx,struct dfs_path);
1087 NTSTATUS status;
1089 if (!pdp) {
1090 return False;
1092 status = parse_dfs_path(NULL, dfs_path, False, allow_broken_path,
1093 pdp, &dummy);
1094 if (!NT_STATUS_IS_OK(status)) {
1095 return False;
1098 /* check if path is dfs : validate first token */
1099 if (!is_myname_or_ipaddr(pdp->hostname)) {
1100 DEBUG(4,("create_junction: Invalid hostname %s "
1101 "in dfs path %s\n",
1102 pdp->hostname, dfs_path));
1103 TALLOC_FREE(pdp);
1104 return False;
1107 /* Check for a non-DFS share */
1108 snum = lp_servicenumber(pdp->servicename);
1110 if(snum < 0 || !lp_msdfs_root(snum)) {
1111 DEBUG(4,("create_junction: %s is not an msdfs root.\n",
1112 pdp->servicename));
1113 TALLOC_FREE(pdp);
1114 return False;
1117 jucn->service_name = talloc_strdup(ctx, pdp->servicename);
1118 jucn->volume_name = talloc_strdup(ctx, pdp->reqpath);
1119 jucn->comment = talloc_strdup(ctx, lp_comment(snum));
1121 TALLOC_FREE(pdp);
1122 if (!jucn->service_name || !jucn->volume_name || ! jucn->comment) {
1123 return False;
1125 return True;
1128 /**********************************************************************
1129 Forms a valid Unix pathname from the junction
1130 **********************************************************************/
1132 static bool junction_to_local_path(const struct junction_map *jucn,
1133 char **pp_path_out,
1134 connection_struct **conn_out,
1135 char **oldpath)
1137 int snum;
1138 NTSTATUS status;
1140 snum = lp_servicenumber(jucn->service_name);
1141 if(snum < 0) {
1142 return False;
1144 status = create_conn_struct(talloc_tos(), smbd_server_conn, conn_out,
1145 snum, lp_pathname(snum), NULL, oldpath);
1146 if (!NT_STATUS_IS_OK(status)) {
1147 return False;
1150 *pp_path_out = talloc_asprintf(*conn_out,
1151 "%s/%s",
1152 lp_pathname(snum),
1153 jucn->volume_name);
1154 if (!*pp_path_out) {
1155 vfs_ChDir(*conn_out, *oldpath);
1156 SMB_VFS_DISCONNECT(*conn_out);
1157 conn_free(*conn_out);
1158 return False;
1160 return True;
1163 bool create_msdfs_link(const struct junction_map *jucn)
1165 char *path = NULL;
1166 char *cwd;
1167 char *msdfs_link = NULL;
1168 connection_struct *conn;
1169 int i=0;
1170 bool insert_comma = False;
1171 bool ret = False;
1173 if(!junction_to_local_path(jucn, &path, &conn, &cwd)) {
1174 return False;
1177 /* Form the msdfs_link contents */
1178 msdfs_link = talloc_strdup(conn, "msdfs:");
1179 if (!msdfs_link) {
1180 goto out;
1182 for(i=0; i<jucn->referral_count; i++) {
1183 char *refpath = jucn->referral_list[i].alternate_path;
1185 /* Alternate paths always use Windows separators. */
1186 trim_char(refpath, '\\', '\\');
1187 if(*refpath == '\0') {
1188 if (i == 0) {
1189 insert_comma = False;
1191 continue;
1193 if (i > 0 && insert_comma) {
1194 msdfs_link = talloc_asprintf_append_buffer(msdfs_link,
1195 ",%s",
1196 refpath);
1197 } else {
1198 msdfs_link = talloc_asprintf_append_buffer(msdfs_link,
1199 "%s",
1200 refpath);
1203 if (!msdfs_link) {
1204 goto out;
1206 if (!insert_comma) {
1207 insert_comma = True;
1211 DEBUG(5,("create_msdfs_link: Creating new msdfs link: %s -> %s\n",
1212 path, msdfs_link));
1214 if(SMB_VFS_SYMLINK(conn, msdfs_link, path) < 0) {
1215 if (errno == EEXIST) {
1216 struct smb_filename *smb_fname = NULL;
1217 NTSTATUS status;
1219 status = create_synthetic_smb_fname(talloc_tos(), path,
1220 NULL, NULL,
1221 &smb_fname);
1222 if (!NT_STATUS_IS_OK(status)) {
1223 errno = map_errno_from_nt_status(status);
1224 goto out;
1227 if(SMB_VFS_UNLINK(conn, smb_fname)!=0) {
1228 TALLOC_FREE(smb_fname);
1229 goto out;
1231 TALLOC_FREE(smb_fname);
1233 if (SMB_VFS_SYMLINK(conn, msdfs_link, path) < 0) {
1234 DEBUG(1,("create_msdfs_link: symlink failed "
1235 "%s -> %s\nError: %s\n",
1236 path, msdfs_link, strerror(errno)));
1237 goto out;
1241 ret = True;
1243 out:
1244 vfs_ChDir(conn, cwd);
1245 SMB_VFS_DISCONNECT(conn);
1246 conn_free(conn);
1247 return ret;
1250 bool remove_msdfs_link(const struct junction_map *jucn)
1252 char *path = NULL;
1253 char *cwd;
1254 connection_struct *conn;
1255 bool ret = False;
1256 struct smb_filename *smb_fname = NULL;
1257 NTSTATUS status;
1259 if (!junction_to_local_path(jucn, &path, &conn, &cwd)) {
1260 return false;
1263 status = create_synthetic_smb_fname(talloc_tos(), path,
1264 NULL, NULL,
1265 &smb_fname);
1266 if (!NT_STATUS_IS_OK(status)) {
1267 errno = map_errno_from_nt_status(status);
1268 return false;
1271 if( SMB_VFS_UNLINK(conn, smb_fname) == 0 ) {
1272 ret = True;
1275 TALLOC_FREE(smb_fname);
1276 vfs_ChDir(conn, cwd);
1277 SMB_VFS_DISCONNECT(conn);
1278 conn_free(conn);
1279 return ret;
1282 /*********************************************************************
1283 Return the number of DFS links at the root of this share.
1284 *********************************************************************/
1286 static int count_dfs_links(TALLOC_CTX *ctx, int snum)
1288 size_t cnt = 0;
1289 SMB_STRUCT_DIR *dirp = NULL;
1290 const char *dname = NULL;
1291 char *talloced = NULL;
1292 const char *connect_path = lp_pathname(snum);
1293 const char *msdfs_proxy = lp_msdfs_proxy(snum);
1294 connection_struct *conn;
1295 NTSTATUS status;
1296 char *cwd;
1298 if(*connect_path == '\0') {
1299 return 0;
1303 * Fake up a connection struct for the VFS layer.
1306 status = create_conn_struct(talloc_tos(), smbd_server_conn, &conn,
1307 snum, connect_path, NULL, &cwd);
1308 if (!NT_STATUS_IS_OK(status)) {
1309 DEBUG(3, ("create_conn_struct failed: %s\n",
1310 nt_errstr(status)));
1311 return 0;
1314 /* Count a link for the msdfs root - convention */
1315 cnt = 1;
1317 /* No more links if this is an msdfs proxy. */
1318 if (*msdfs_proxy != '\0') {
1319 goto out;
1322 /* Now enumerate all dfs links */
1323 dirp = SMB_VFS_OPENDIR(conn, ".", NULL, 0);
1324 if(!dirp) {
1325 goto out;
1328 while ((dname = vfs_readdirname(conn, dirp, NULL, &talloced))
1329 != NULL) {
1330 if (is_msdfs_link(conn,
1331 dname,
1332 NULL)) {
1333 cnt++;
1335 TALLOC_FREE(talloced);
1338 SMB_VFS_CLOSEDIR(conn,dirp);
1340 out:
1341 vfs_ChDir(conn, cwd);
1342 SMB_VFS_DISCONNECT(conn);
1343 conn_free(conn);
1344 return cnt;
1347 /*********************************************************************
1348 *********************************************************************/
1350 static int form_junctions(TALLOC_CTX *ctx,
1351 int snum,
1352 struct junction_map *jucn,
1353 size_t jn_remain)
1355 size_t cnt = 0;
1356 SMB_STRUCT_DIR *dirp = NULL;
1357 const char *dname = NULL;
1358 char *talloced = NULL;
1359 const char *connect_path = lp_pathname(snum);
1360 char *service_name = lp_servicename(snum);
1361 const char *msdfs_proxy = lp_msdfs_proxy(snum);
1362 connection_struct *conn;
1363 struct referral *ref = NULL;
1364 char *cwd;
1365 NTSTATUS status;
1367 if (jn_remain == 0) {
1368 return 0;
1371 if(*connect_path == '\0') {
1372 return 0;
1376 * Fake up a connection struct for the VFS layer.
1379 status = create_conn_struct(ctx, smbd_server_conn, &conn, snum, connect_path, NULL,
1380 &cwd);
1381 if (!NT_STATUS_IS_OK(status)) {
1382 DEBUG(3, ("create_conn_struct failed: %s\n",
1383 nt_errstr(status)));
1384 return 0;
1387 /* form a junction for the msdfs root - convention
1388 DO NOT REMOVE THIS: NT clients will not work with us
1389 if this is not present
1391 jucn[cnt].service_name = talloc_strdup(ctx,service_name);
1392 jucn[cnt].volume_name = talloc_strdup(ctx, "");
1393 if (!jucn[cnt].service_name || !jucn[cnt].volume_name) {
1394 goto out;
1396 jucn[cnt].comment = "";
1397 jucn[cnt].referral_count = 1;
1399 ref = jucn[cnt].referral_list = talloc_zero(ctx, struct referral);
1400 if (jucn[cnt].referral_list == NULL) {
1401 goto out;
1404 ref->proximity = 0;
1405 ref->ttl = REFERRAL_TTL;
1406 if (*msdfs_proxy != '\0') {
1407 ref->alternate_path = talloc_strdup(ctx,
1408 msdfs_proxy);
1409 } else {
1410 ref->alternate_path = talloc_asprintf(ctx,
1411 "\\\\%s\\%s",
1412 get_local_machine_name(),
1413 service_name);
1416 if (!ref->alternate_path) {
1417 goto out;
1419 cnt++;
1421 /* Don't enumerate if we're an msdfs proxy. */
1422 if (*msdfs_proxy != '\0') {
1423 goto out;
1426 /* Now enumerate all dfs links */
1427 dirp = SMB_VFS_OPENDIR(conn, ".", NULL, 0);
1428 if(!dirp) {
1429 goto out;
1432 while ((dname = vfs_readdirname(conn, dirp, NULL, &talloced))
1433 != NULL) {
1434 char *link_target = NULL;
1435 if (cnt >= jn_remain) {
1436 DEBUG(2, ("form_junctions: ran out of MSDFS "
1437 "junction slots"));
1438 TALLOC_FREE(talloced);
1439 goto out;
1441 if (is_msdfs_link_internal(ctx,
1442 conn,
1443 dname, &link_target,
1444 NULL)) {
1445 if (parse_msdfs_symlink(ctx,
1446 link_target,
1447 &jucn[cnt].referral_list,
1448 &jucn[cnt].referral_count)) {
1450 jucn[cnt].service_name = talloc_strdup(ctx,
1451 service_name);
1452 jucn[cnt].volume_name = talloc_strdup(ctx,
1453 dname);
1454 if (!jucn[cnt].service_name ||
1455 !jucn[cnt].volume_name) {
1456 TALLOC_FREE(talloced);
1457 goto out;
1459 jucn[cnt].comment = "";
1460 cnt++;
1462 TALLOC_FREE(link_target);
1464 TALLOC_FREE(talloced);
1467 out:
1469 if (dirp) {
1470 SMB_VFS_CLOSEDIR(conn,dirp);
1473 vfs_ChDir(conn, cwd);
1474 conn_free(conn);
1475 return cnt;
1478 struct junction_map *enum_msdfs_links(struct smbd_server_connection *sconn,
1479 TALLOC_CTX *ctx, size_t *p_num_jn)
1481 struct junction_map *jn = NULL;
1482 int i=0;
1483 size_t jn_count = 0;
1484 int sharecount = 0;
1486 *p_num_jn = 0;
1487 if(!lp_host_msdfs()) {
1488 return NULL;
1491 /* Ensure all the usershares are loaded. */
1492 become_root();
1493 load_registry_shares();
1494 sharecount = load_usershare_shares(sconn);
1495 unbecome_root();
1497 for(i=0;i < sharecount;i++) {
1498 if(lp_msdfs_root(i)) {
1499 jn_count += count_dfs_links(ctx, i);
1502 if (jn_count == 0) {
1503 return NULL;
1505 jn = talloc_array(ctx, struct junction_map, jn_count);
1506 if (!jn) {
1507 return NULL;
1509 for(i=0; i < sharecount; i++) {
1510 if (*p_num_jn >= jn_count) {
1511 break;
1513 if(lp_msdfs_root(i)) {
1514 *p_num_jn += form_junctions(ctx, i,
1515 &jn[*p_num_jn],
1516 jn_count - *p_num_jn);
1519 return jn;
1522 /******************************************************************************
1523 Core function to resolve a dfs pathname possibly containing a wildcard. If
1524 ppath_contains_wcard != NULL, it will be set to true if a wildcard is
1525 detected during dfs resolution.
1526 ******************************************************************************/
1528 NTSTATUS resolve_dfspath_wcard(TALLOC_CTX *ctx,
1529 connection_struct *conn,
1530 bool dfs_pathnames,
1531 const char *name_in,
1532 bool allow_wcards,
1533 char **pp_name_out,
1534 bool *ppath_contains_wcard)
1536 bool path_contains_wcard;
1537 NTSTATUS status = NT_STATUS_OK;
1539 if (dfs_pathnames) {
1540 status = dfs_redirect(ctx,
1541 conn,
1542 name_in,
1543 allow_wcards,
1544 !smbd_server_conn->using_smb2,
1545 pp_name_out,
1546 &path_contains_wcard);
1548 if (NT_STATUS_IS_OK(status) && ppath_contains_wcard != NULL) {
1549 *ppath_contains_wcard = path_contains_wcard;
1551 } else {
1553 * Cheat and just return a copy of the in ptr.
1554 * Once srvstr_get_path() uses talloc it'll
1555 * be a talloced ptr anyway.
1557 *pp_name_out = discard_const_p(char, name_in);
1559 return status;