s3:smbd/msdfs: pass 'allow_broken_path' to get_referred_path()
[Samba/gebeck_regimport.git] / source3 / smbd / msdfs.c
blob76fcb8cbe3850a8deb1b2707a1e1260e427df77b
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 tevent_context *ev,
227 struct messaging_context *msg,
228 connection_struct **pconn,
229 int snum,
230 const char *path,
231 const struct auth_session_info *session_info,
232 char **poldcwd)
234 connection_struct *conn;
235 char *connpath;
236 char *oldcwd;
237 const char *vfs_user;
239 conn = talloc_zero(ctx, connection_struct);
240 if (conn == NULL) {
241 return NT_STATUS_NO_MEMORY;
244 connpath = talloc_strdup(conn, path);
245 if (!connpath) {
246 TALLOC_FREE(conn);
247 return NT_STATUS_NO_MEMORY;
249 connpath = talloc_string_sub(conn,
250 connpath,
251 "%S",
252 lp_servicename(snum));
253 if (!connpath) {
254 TALLOC_FREE(conn);
255 return NT_STATUS_NO_MEMORY;
258 conn->sconn = talloc_zero(conn, struct smbd_server_connection);
259 if (conn->sconn == NULL) {
260 TALLOC_FREE(conn);
261 return NT_STATUS_NO_MEMORY;
264 conn->sconn->ev_ctx = ev;
265 conn->sconn->msg_ctx = msg;
266 conn->sconn->sock = -1;
267 conn->sconn->smb1.echo_handler.trusted_fd = -1;
268 conn->sconn->smb1.echo_handler.socket_lock_fd = -1;
270 /* needed for smbd_vfs_init() */
272 if (!(conn->params = talloc_zero(conn, struct share_params))) {
273 DEBUG(0, ("TALLOC failed\n"));
274 TALLOC_FREE(conn);
275 return NT_STATUS_NO_MEMORY;
278 conn->params->service = snum;
279 conn->cnum = (unsigned)-1;
281 DLIST_ADD(conn->sconn->connections, conn);
282 conn->sconn->num_connections++;
284 if (session_info != NULL) {
285 conn->session_info = copy_session_info(conn, session_info);
286 if (conn->session_info == NULL) {
287 DEBUG(0, ("copy_serverinfo failed\n"));
288 TALLOC_FREE(conn);
289 return NT_STATUS_NO_MEMORY;
291 vfs_user = conn->session_info->unix_info->unix_name;
292 } else {
293 /* use current authenticated user in absence of session_info */
294 vfs_user = get_current_username();
297 set_conn_connectpath(conn, connpath);
300 * New code to check if there's a share security descripter
301 * added from NT server manager. This is done after the
302 * smb.conf checks are done as we need a uid and token. JRA.
305 if (conn->session_info) {
306 share_access_check(conn->session_info->security_token,
307 lp_servicename(snum), MAXIMUM_ALLOWED_ACCESS,
308 &conn->share_access);
310 if ((conn->share_access & FILE_WRITE_DATA) == 0) {
311 if ((conn->share_access & FILE_READ_DATA) == 0) {
312 /* No access, read or write. */
313 DEBUG(0,("create_conn_struct: connection to %s "
314 "denied due to security "
315 "descriptor.\n",
316 lp_servicename(snum)));
317 conn_free(conn);
318 return NT_STATUS_ACCESS_DENIED;
319 } else {
320 conn->read_only = true;
323 } else {
324 conn->share_access = 0;
325 conn->read_only = true;
328 if (!smbd_vfs_init(conn)) {
329 NTSTATUS status = map_nt_error_from_unix(errno);
330 DEBUG(0,("create_conn_struct: smbd_vfs_init failed.\n"));
331 conn_free(conn);
332 return status;
335 /* this must be the first filesystem operation that we do */
336 if (SMB_VFS_CONNECT(conn, lp_servicename(snum), vfs_user) < 0) {
337 DEBUG(0,("VFS connect failed!\n"));
338 conn_free(conn);
339 return NT_STATUS_UNSUCCESSFUL;
342 conn->fs_capabilities = SMB_VFS_FS_CAPABILITIES(conn, &conn->ts_res);
345 * Windows seems to insist on doing trans2getdfsreferral() calls on
346 * the IPC$ share as the anonymous user. If we try to chdir as that
347 * user we will fail.... WTF ? JRA.
350 oldcwd = vfs_GetWd(ctx, conn);
351 if (oldcwd == NULL) {
352 NTSTATUS status = map_nt_error_from_unix(errno);
353 DEBUG(3, ("vfs_GetWd failed: %s\n", strerror(errno)));
354 conn_free(conn);
355 return status;
358 if (vfs_ChDir(conn,conn->connectpath) != 0) {
359 NTSTATUS status = map_nt_error_from_unix(errno);
360 DEBUG(3,("create_conn_struct: Can't ChDir to new conn path %s. "
361 "Error was %s\n",
362 conn->connectpath, strerror(errno) ));
363 conn_free(conn);
364 return status;
367 *pconn = conn;
368 *poldcwd = oldcwd;
370 return NT_STATUS_OK;
373 /**********************************************************************
374 Parse the contents of a symlink to verify if it is an msdfs referral
375 A valid referral is of the form:
377 msdfs:server1\share1,server2\share2
378 msdfs:server1\share1\pathname,server2\share2\pathname
379 msdfs:server1/share1,server2/share2
380 msdfs:server1/share1/pathname,server2/share2/pathname.
382 Note that the alternate paths returned here must be of the canonicalized
383 form:
385 \server\share or
386 \server\share\path\to\file,
388 even in posix path mode. This is because we have no knowledge if the
389 server we're referring to understands posix paths.
390 **********************************************************************/
392 static bool parse_msdfs_symlink(TALLOC_CTX *ctx,
393 const char *target,
394 struct referral **preflist,
395 int *refcount)
397 char *temp = NULL;
398 char *prot;
399 char **alt_path = NULL;
400 int count = 0, i;
401 struct referral *reflist;
402 char *saveptr;
404 temp = talloc_strdup(ctx, target);
405 if (!temp) {
406 return False;
408 prot = strtok_r(temp, ":", &saveptr);
409 if (!prot) {
410 DEBUG(0,("parse_msdfs_symlink: invalid path !\n"));
411 return False;
414 alt_path = talloc_array(ctx, char *, MAX_REFERRAL_COUNT);
415 if (!alt_path) {
416 return False;
419 /* parse out the alternate paths */
420 while((count<MAX_REFERRAL_COUNT) &&
421 ((alt_path[count] = strtok_r(NULL, ",", &saveptr)) != NULL)) {
422 count++;
425 DEBUG(10,("parse_msdfs_symlink: count=%d\n", count));
427 if (count) {
428 reflist = *preflist = talloc_zero_array(ctx,
429 struct referral, count);
430 if(reflist == NULL) {
431 TALLOC_FREE(alt_path);
432 return False;
434 } else {
435 reflist = *preflist = NULL;
438 for(i=0;i<count;i++) {
439 char *p;
441 /* Canonicalize link target.
442 * Replace all /'s in the path by a \ */
443 string_replace(alt_path[i], '/', '\\');
445 /* Remove leading '\\'s */
446 p = alt_path[i];
447 while (*p && (*p == '\\')) {
448 p++;
451 reflist[i].alternate_path = talloc_asprintf(ctx,
452 "\\%s",
454 if (!reflist[i].alternate_path) {
455 return False;
458 reflist[i].proximity = 0;
459 reflist[i].ttl = REFERRAL_TTL;
460 DEBUG(10, ("parse_msdfs_symlink: Created alt path: %s\n",
461 reflist[i].alternate_path));
464 *refcount = count;
466 TALLOC_FREE(alt_path);
467 return True;
470 /**********************************************************************
471 Returns true if the unix path is a valid msdfs symlink and also
472 returns the target string from inside the link.
473 **********************************************************************/
475 static bool is_msdfs_link_internal(TALLOC_CTX *ctx,
476 connection_struct *conn,
477 const char *path,
478 char **pp_link_target,
479 SMB_STRUCT_STAT *sbufp)
481 int referral_len = 0;
482 #if defined(HAVE_BROKEN_READLINK)
483 char link_target_buf[PATH_MAX];
484 #else
485 char link_target_buf[7];
486 #endif
487 size_t bufsize = 0;
488 char *link_target = NULL;
489 struct smb_filename smb_fname;
491 if (pp_link_target) {
492 bufsize = 1024;
493 link_target = talloc_array(ctx, char, bufsize);
494 if (!link_target) {
495 return False;
497 *pp_link_target = link_target;
498 } else {
499 bufsize = sizeof(link_target_buf);
500 link_target = link_target_buf;
503 ZERO_STRUCT(smb_fname);
504 smb_fname.base_name = discard_const_p(char, path);
506 if (SMB_VFS_LSTAT(conn, &smb_fname) != 0) {
507 DEBUG(5,("is_msdfs_link_read_target: %s does not exist.\n",
508 path));
509 goto err;
511 if (!S_ISLNK(smb_fname.st.st_ex_mode)) {
512 DEBUG(5,("is_msdfs_link_read_target: %s is not a link.\n",
513 path));
514 goto err;
516 if (sbufp != NULL) {
517 *sbufp = smb_fname.st;
520 referral_len = SMB_VFS_READLINK(conn, path, link_target, bufsize - 1);
521 if (referral_len == -1) {
522 DEBUG(0,("is_msdfs_link_read_target: Error reading "
523 "msdfs link %s: %s\n",
524 path, strerror(errno)));
525 goto err;
527 link_target[referral_len] = '\0';
529 DEBUG(5,("is_msdfs_link_internal: %s -> %s\n",path,
530 link_target));
532 if (!strnequal(link_target, "msdfs:", 6)) {
533 goto err;
535 return True;
537 err:
539 if (link_target != link_target_buf) {
540 TALLOC_FREE(link_target);
542 return False;
545 /**********************************************************************
546 Returns true if the unix path is a valid msdfs symlink.
547 **********************************************************************/
549 bool is_msdfs_link(connection_struct *conn,
550 const char *path,
551 SMB_STRUCT_STAT *sbufp)
553 return is_msdfs_link_internal(talloc_tos(),
554 conn,
555 path,
556 NULL,
557 sbufp);
560 /*****************************************************************
561 Used by other functions to decide if a dfs path is remote,
562 and to get the list of referred locations for that remote path.
564 search_flag: For findfirsts, dfs links themselves are not
565 redirected, but paths beyond the links are. For normal smb calls,
566 even dfs links need to be redirected.
568 consumedcntp: how much of the dfs path is being redirected. the client
569 should try the remaining path on the redirected server.
571 If this returns NT_STATUS_PATH_NOT_COVERED the contents of the msdfs
572 link redirect are in targetpath.
573 *****************************************************************/
575 static NTSTATUS dfs_path_lookup(TALLOC_CTX *ctx,
576 connection_struct *conn,
577 const char *dfspath, /* Incoming complete dfs path */
578 const struct dfs_path *pdp, /* Parsed out
579 server+share+extrapath. */
580 bool search_flag, /* Called from a findfirst ? */
581 int *consumedcntp,
582 char **pp_targetpath)
584 char *p = NULL;
585 char *q = NULL;
586 NTSTATUS status;
587 struct smb_filename *smb_fname = NULL;
588 char *canon_dfspath = NULL; /* Canonicalized dfs path. (only '/'
589 components). */
591 DEBUG(10,("dfs_path_lookup: Conn path = %s reqpath = %s\n",
592 conn->connectpath, pdp->reqpath));
595 * Note the unix path conversion here we're doing we
596 * throw away. We're looking for a symlink for a dfs
597 * resolution, if we don't find it we'll do another
598 * unix_convert later in the codepath.
601 status = unix_convert(ctx, conn, pdp->reqpath, &smb_fname,
602 search_flag ? UCF_ALWAYS_ALLOW_WCARD_LCOMP : 0);
604 if (!NT_STATUS_IS_OK(status)) {
605 if (!NT_STATUS_EQUAL(status,
606 NT_STATUS_OBJECT_PATH_NOT_FOUND)) {
607 return status;
609 if (smb_fname == NULL || smb_fname->base_name == NULL) {
610 return status;
614 /* Optimization - check if we can redirect the whole path. */
616 if (is_msdfs_link_internal(ctx, conn, smb_fname->base_name,
617 pp_targetpath, NULL)) {
618 if (search_flag) {
619 DEBUG(6,("dfs_path_lookup (FindFirst) No redirection "
620 "for dfs link %s.\n", dfspath));
621 status = NT_STATUS_OK;
622 goto out;
625 DEBUG(6,("dfs_path_lookup: %s resolves to a "
626 "valid dfs link %s.\n", dfspath,
627 pp_targetpath ? *pp_targetpath : ""));
629 if (consumedcntp) {
630 *consumedcntp = strlen(dfspath);
632 status = NT_STATUS_PATH_NOT_COVERED;
633 goto out;
636 /* Prepare to test only for '/' components in the given path,
637 * so if a Windows path replace all '\\' characters with '/'.
638 * For a POSIX DFS path we know all separators are already '/'. */
640 canon_dfspath = talloc_strdup(ctx, dfspath);
641 if (!canon_dfspath) {
642 status = NT_STATUS_NO_MEMORY;
643 goto out;
645 if (!pdp->posix_path) {
646 string_replace(canon_dfspath, '\\', '/');
650 * localpath comes out of unix_convert, so it has
651 * no trailing backslash. Make sure that canon_dfspath hasn't either.
652 * Fix for bug #4860 from Jan Martin <Jan.Martin@rwedea.com>.
655 trim_char(canon_dfspath,0,'/');
658 * Redirect if any component in the path is a link.
659 * We do this by walking backwards through the
660 * local path, chopping off the last component
661 * in both the local path and the canonicalized
662 * DFS path. If we hit a DFS link then we're done.
665 p = strrchr_m(smb_fname->base_name, '/');
666 if (consumedcntp) {
667 q = strrchr_m(canon_dfspath, '/');
670 while (p) {
671 *p = '\0';
672 if (q) {
673 *q = '\0';
676 if (is_msdfs_link_internal(ctx, conn,
677 smb_fname->base_name, pp_targetpath,
678 NULL)) {
679 DEBUG(4, ("dfs_path_lookup: Redirecting %s because "
680 "parent %s is dfs link\n", dfspath,
681 smb_fname_str_dbg(smb_fname)));
683 if (consumedcntp) {
684 *consumedcntp = strlen(canon_dfspath);
685 DEBUG(10, ("dfs_path_lookup: Path consumed: %s "
686 "(%d)\n",
687 canon_dfspath,
688 *consumedcntp));
691 status = NT_STATUS_PATH_NOT_COVERED;
692 goto out;
695 /* Step back on the filesystem. */
696 p = strrchr_m(smb_fname->base_name, '/');
698 if (consumedcntp) {
699 /* And in the canonicalized dfs path. */
700 q = strrchr_m(canon_dfspath, '/');
704 status = NT_STATUS_OK;
705 out:
706 TALLOC_FREE(smb_fname);
707 return status;
710 /*****************************************************************
711 Decides if a dfs pathname should be redirected or not.
712 If not, the pathname is converted to a tcon-relative local unix path
714 search_wcard_flag: this flag performs 2 functions both related
715 to searches. See resolve_dfs_path() and parse_dfs_path_XX()
716 for details.
718 This function can return NT_STATUS_OK, meaning use the returned path as-is
719 (mapped into a local path).
720 or NT_STATUS_NOT_COVERED meaning return a DFS redirect, or
721 any other NT_STATUS error which is a genuine error to be
722 returned to the client.
723 *****************************************************************/
725 static NTSTATUS dfs_redirect(TALLOC_CTX *ctx,
726 connection_struct *conn,
727 const char *path_in,
728 bool search_wcard_flag,
729 bool allow_broken_path,
730 char **pp_path_out,
731 bool *ppath_contains_wcard)
733 NTSTATUS status;
734 struct dfs_path *pdp = talloc(ctx, struct dfs_path);
736 if (!pdp) {
737 return NT_STATUS_NO_MEMORY;
740 status = parse_dfs_path(conn, path_in, search_wcard_flag,
741 allow_broken_path, pdp,
742 ppath_contains_wcard);
743 if (!NT_STATUS_IS_OK(status)) {
744 TALLOC_FREE(pdp);
745 return status;
748 if (pdp->reqpath[0] == '\0') {
749 TALLOC_FREE(pdp);
750 *pp_path_out = talloc_strdup(ctx, "");
751 if (!*pp_path_out) {
752 return NT_STATUS_NO_MEMORY;
754 DEBUG(5,("dfs_redirect: self-referral.\n"));
755 return NT_STATUS_OK;
758 /* If dfs pathname for a non-dfs share, convert to tcon-relative
759 path and return OK */
761 if (!lp_msdfs_root(SNUM(conn))) {
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 it looked like a local path (zero hostname/servicename)
771 * just treat as a tcon-relative path. */
773 if (pdp->hostname[0] == '\0' && pdp->servicename[0] == '\0') {
774 *pp_path_out = talloc_strdup(ctx, pdp->reqpath);
775 TALLOC_FREE(pdp);
776 if (!*pp_path_out) {
777 return NT_STATUS_NO_MEMORY;
779 return NT_STATUS_OK;
782 if (!( strequal(pdp->servicename, lp_servicename(SNUM(conn)))
783 || (strequal(pdp->servicename, HOMES_NAME)
784 && strequal(lp_servicename(SNUM(conn)),
785 conn->session_info->unix_info->sanitized_username) )) ) {
787 /* The given sharename doesn't match this connection. */
788 TALLOC_FREE(pdp);
790 return NT_STATUS_OBJECT_PATH_NOT_FOUND;
793 status = dfs_path_lookup(ctx, conn, path_in, pdp,
794 search_wcard_flag, NULL, NULL);
795 if (!NT_STATUS_IS_OK(status)) {
796 if (NT_STATUS_EQUAL(status, NT_STATUS_PATH_NOT_COVERED)) {
797 DEBUG(3,("dfs_redirect: Redirecting %s\n", path_in));
798 } else {
799 DEBUG(10,("dfs_redirect: dfs_path_lookup "
800 "failed for %s with %s\n",
801 path_in, nt_errstr(status) ));
803 return status;
806 DEBUG(3,("dfs_redirect: Not redirecting %s.\n", path_in));
808 /* Form non-dfs tcon-relative path */
809 *pp_path_out = talloc_strdup(ctx, pdp->reqpath);
810 TALLOC_FREE(pdp);
811 if (!*pp_path_out) {
812 return NT_STATUS_NO_MEMORY;
815 DEBUG(3,("dfs_redirect: Path %s converted to non-dfs path %s\n",
816 path_in,
817 *pp_path_out));
819 return NT_STATUS_OK;
822 /**********************************************************************
823 Return a self referral.
824 **********************************************************************/
826 static NTSTATUS self_ref(TALLOC_CTX *ctx,
827 const char *dfs_path,
828 struct junction_map *jucn,
829 int *consumedcntp,
830 bool *self_referralp)
832 struct referral *ref;
834 *self_referralp = True;
836 jucn->referral_count = 1;
837 if((ref = talloc_zero(ctx, struct referral)) == NULL) {
838 return NT_STATUS_NO_MEMORY;
841 ref->alternate_path = talloc_strdup(ctx, dfs_path);
842 if (!ref->alternate_path) {
843 return NT_STATUS_NO_MEMORY;
845 ref->proximity = 0;
846 ref->ttl = REFERRAL_TTL;
847 jucn->referral_list = ref;
848 *consumedcntp = strlen(dfs_path);
849 return NT_STATUS_OK;
852 /**********************************************************************
853 Gets valid referrals for a dfs path and fills up the
854 junction_map structure.
855 **********************************************************************/
857 NTSTATUS get_referred_path(TALLOC_CTX *ctx,
858 const char *dfs_path,
859 bool allow_broken_path,
860 struct junction_map *jucn,
861 int *consumedcntp,
862 bool *self_referralp)
864 struct connection_struct *conn;
865 char *targetpath = NULL;
866 int snum;
867 NTSTATUS status = NT_STATUS_NOT_FOUND;
868 bool dummy;
869 struct dfs_path *pdp = talloc(ctx, struct dfs_path);
870 char *oldpath;
872 if (!pdp) {
873 return NT_STATUS_NO_MEMORY;
876 *self_referralp = False;
878 status = parse_dfs_path(NULL, dfs_path, False, allow_broken_path,
879 pdp, &dummy);
880 if (!NT_STATUS_IS_OK(status)) {
881 return status;
884 jucn->service_name = talloc_strdup(ctx, pdp->servicename);
885 jucn->volume_name = talloc_strdup(ctx, pdp->reqpath);
886 if (!jucn->service_name || !jucn->volume_name) {
887 TALLOC_FREE(pdp);
888 return NT_STATUS_NO_MEMORY;
891 /* Verify the share is a dfs root */
892 snum = lp_servicenumber(jucn->service_name);
893 if(snum < 0) {
894 char *service_name = NULL;
895 if ((snum = find_service(ctx, jucn->service_name, &service_name)) < 0) {
896 return NT_STATUS_NOT_FOUND;
898 if (!service_name) {
899 return NT_STATUS_NO_MEMORY;
901 TALLOC_FREE(jucn->service_name);
902 jucn->service_name = talloc_strdup(ctx, service_name);
903 if (!jucn->service_name) {
904 TALLOC_FREE(pdp);
905 return NT_STATUS_NO_MEMORY;
909 if (!lp_msdfs_root(snum) && (*lp_msdfs_proxy(snum) == '\0')) {
910 DEBUG(3,("get_referred_path: |%s| in dfs path %s is not "
911 "a dfs root.\n",
912 pdp->servicename, dfs_path));
913 TALLOC_FREE(pdp);
914 return NT_STATUS_NOT_FOUND;
918 * Self referrals are tested with a anonymous IPC connection and
919 * a GET_DFS_REFERRAL call to \\server\share. (which means
920 * dp.reqpath[0] points to an empty string). create_conn_struct cd's
921 * into the directory and will fail if it cannot (as the anonymous
922 * user). Cope with this.
925 if (pdp->reqpath[0] == '\0') {
926 char *tmp;
927 struct referral *ref;
929 if (*lp_msdfs_proxy(snum) == '\0') {
930 TALLOC_FREE(pdp);
931 return self_ref(ctx,
932 dfs_path,
933 jucn,
934 consumedcntp,
935 self_referralp);
939 * It's an msdfs proxy share. Redirect to
940 * the configured target share.
943 jucn->referral_count = 1;
944 if ((ref = talloc_zero(ctx, struct referral)) == NULL) {
945 TALLOC_FREE(pdp);
946 return NT_STATUS_NO_MEMORY;
949 if (!(tmp = talloc_strdup(ctx, lp_msdfs_proxy(snum)))) {
950 TALLOC_FREE(pdp);
951 return NT_STATUS_NO_MEMORY;
954 trim_string(tmp, "\\", 0);
956 ref->alternate_path = talloc_asprintf(ctx, "\\%s", tmp);
957 TALLOC_FREE(tmp);
959 if (!ref->alternate_path) {
960 TALLOC_FREE(pdp);
961 return NT_STATUS_NO_MEMORY;
964 if (pdp->reqpath[0] != '\0') {
965 ref->alternate_path = talloc_asprintf_append(
966 ref->alternate_path,
967 "%s",
968 pdp->reqpath);
969 if (!ref->alternate_path) {
970 TALLOC_FREE(pdp);
971 return NT_STATUS_NO_MEMORY;
974 ref->proximity = 0;
975 ref->ttl = REFERRAL_TTL;
976 jucn->referral_list = ref;
977 *consumedcntp = strlen(dfs_path);
978 TALLOC_FREE(pdp);
979 return NT_STATUS_OK;
982 status = create_conn_struct(ctx,
983 server_event_context(),
984 server_messaging_context(),
985 &conn, snum,
986 lp_pathname(snum), NULL, &oldpath);
987 if (!NT_STATUS_IS_OK(status)) {
988 TALLOC_FREE(pdp);
989 return status;
992 /* If this is a DFS path dfs_lookup should return
993 * NT_STATUS_PATH_NOT_COVERED. */
995 status = dfs_path_lookup(ctx, conn, dfs_path, pdp,
996 False, consumedcntp, &targetpath);
998 if (!NT_STATUS_EQUAL(status, NT_STATUS_PATH_NOT_COVERED)) {
999 DEBUG(3,("get_referred_path: No valid referrals for path %s\n",
1000 dfs_path));
1001 goto err_exit;
1004 /* We know this is a valid dfs link. Parse the targetpath. */
1005 if (!parse_msdfs_symlink(ctx, targetpath,
1006 &jucn->referral_list,
1007 &jucn->referral_count)) {
1008 DEBUG(3,("get_referred_path: failed to parse symlink "
1009 "target %s\n", targetpath ));
1010 status = NT_STATUS_NOT_FOUND;
1011 goto err_exit;
1014 status = NT_STATUS_OK;
1015 err_exit:
1016 vfs_ChDir(conn, oldpath);
1017 SMB_VFS_DISCONNECT(conn);
1018 conn_free(conn);
1019 TALLOC_FREE(pdp);
1020 return status;
1023 /******************************************************************
1024 Set up the DFS referral for the dfs pathname. This call returns
1025 the amount of the path covered by this server, and where the
1026 client should be redirected to. This is the meat of the
1027 TRANS2_GET_DFS_REFERRAL call.
1028 ******************************************************************/
1030 int setup_dfs_referral(connection_struct *orig_conn,
1031 const char *dfs_path,
1032 int max_referral_level,
1033 char **ppdata, NTSTATUS *pstatus)
1035 char *pdata = *ppdata;
1036 int reply_size = 0;
1037 struct dfs_GetDFSReferral *r;
1038 DATA_BLOB blob = data_blob_null;
1039 NTSTATUS status;
1040 enum ndr_err_code ndr_err;
1042 r = talloc_zero(talloc_tos(), struct dfs_GetDFSReferral);
1043 if (r == NULL) {
1044 *pstatus = NT_STATUS_NO_MEMORY;
1045 return -1;
1048 r->in.req.max_referral_level = max_referral_level;
1049 r->in.req.servername = talloc_strdup(r, dfs_path);
1050 if (r->in.req.servername == NULL) {
1051 talloc_free(r);
1052 *pstatus = NT_STATUS_NO_MEMORY;
1053 return -1;
1056 status = SMB_VFS_GET_DFS_REFERRALS(orig_conn, r);
1057 if (!NT_STATUS_IS_OK(status)) {
1058 talloc_free(r);
1059 *pstatus = status;
1060 return -1;
1063 ndr_err = ndr_push_struct_blob(&blob, r,
1064 r->out.resp,
1065 (ndr_push_flags_fn_t)ndr_push_dfs_referral_resp);
1066 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1067 TALLOC_FREE(r);
1068 *pstatus = NT_STATUS_INVALID_PARAMETER;
1069 return -1;
1072 pdata = (char *)SMB_REALLOC(pdata, blob.length);
1073 if(pdata == NULL) {
1074 TALLOC_FREE(r);
1075 DEBUG(0,("referral setup:"
1076 "malloc failed for Realloc!\n"));
1077 return -1;
1079 *ppdata = pdata;
1080 reply_size = blob.length;
1081 memcpy(pdata, blob.data, blob.length);
1082 TALLOC_FREE(r);
1084 *pstatus = NT_STATUS_OK;
1085 return reply_size;
1088 /**********************************************************************
1089 The following functions are called by the NETDFS RPC pipe functions
1090 **********************************************************************/
1092 /*********************************************************************
1093 Creates a junction structure from a DFS pathname
1094 **********************************************************************/
1096 bool create_junction(TALLOC_CTX *ctx,
1097 const char *dfs_path,
1098 bool allow_broken_path,
1099 struct junction_map *jucn)
1101 int snum;
1102 bool dummy;
1103 struct dfs_path *pdp = talloc(ctx,struct dfs_path);
1104 NTSTATUS status;
1106 if (!pdp) {
1107 return False;
1109 status = parse_dfs_path(NULL, dfs_path, False, allow_broken_path,
1110 pdp, &dummy);
1111 if (!NT_STATUS_IS_OK(status)) {
1112 return False;
1115 /* check if path is dfs : validate first token */
1116 if (!is_myname_or_ipaddr(pdp->hostname)) {
1117 DEBUG(4,("create_junction: Invalid hostname %s "
1118 "in dfs path %s\n",
1119 pdp->hostname, dfs_path));
1120 TALLOC_FREE(pdp);
1121 return False;
1124 /* Check for a non-DFS share */
1125 snum = lp_servicenumber(pdp->servicename);
1127 if(snum < 0 || !lp_msdfs_root(snum)) {
1128 DEBUG(4,("create_junction: %s is not an msdfs root.\n",
1129 pdp->servicename));
1130 TALLOC_FREE(pdp);
1131 return False;
1134 jucn->service_name = talloc_strdup(ctx, pdp->servicename);
1135 jucn->volume_name = talloc_strdup(ctx, pdp->reqpath);
1136 jucn->comment = talloc_strdup(ctx, lp_comment(snum));
1138 TALLOC_FREE(pdp);
1139 if (!jucn->service_name || !jucn->volume_name || ! jucn->comment) {
1140 return False;
1142 return True;
1145 /**********************************************************************
1146 Forms a valid Unix pathname from the junction
1147 **********************************************************************/
1149 static bool junction_to_local_path(const struct junction_map *jucn,
1150 char **pp_path_out,
1151 connection_struct **conn_out,
1152 char **oldpath)
1154 int snum;
1155 NTSTATUS status;
1157 snum = lp_servicenumber(jucn->service_name);
1158 if(snum < 0) {
1159 return False;
1161 status = create_conn_struct(talloc_tos(),
1162 server_event_context(),
1163 server_messaging_context(),
1164 conn_out,
1165 snum, lp_pathname(snum), NULL, oldpath);
1166 if (!NT_STATUS_IS_OK(status)) {
1167 return False;
1170 *pp_path_out = talloc_asprintf(*conn_out,
1171 "%s/%s",
1172 lp_pathname(snum),
1173 jucn->volume_name);
1174 if (!*pp_path_out) {
1175 vfs_ChDir(*conn_out, *oldpath);
1176 SMB_VFS_DISCONNECT(*conn_out);
1177 conn_free(*conn_out);
1178 return False;
1180 return True;
1183 bool create_msdfs_link(const struct junction_map *jucn)
1185 char *path = NULL;
1186 char *cwd;
1187 char *msdfs_link = NULL;
1188 connection_struct *conn;
1189 int i=0;
1190 bool insert_comma = False;
1191 bool ret = False;
1193 if(!junction_to_local_path(jucn, &path, &conn, &cwd)) {
1194 return False;
1197 /* Form the msdfs_link contents */
1198 msdfs_link = talloc_strdup(conn, "msdfs:");
1199 if (!msdfs_link) {
1200 goto out;
1202 for(i=0; i<jucn->referral_count; i++) {
1203 char *refpath = jucn->referral_list[i].alternate_path;
1205 /* Alternate paths always use Windows separators. */
1206 trim_char(refpath, '\\', '\\');
1207 if(*refpath == '\0') {
1208 if (i == 0) {
1209 insert_comma = False;
1211 continue;
1213 if (i > 0 && insert_comma) {
1214 msdfs_link = talloc_asprintf_append_buffer(msdfs_link,
1215 ",%s",
1216 refpath);
1217 } else {
1218 msdfs_link = talloc_asprintf_append_buffer(msdfs_link,
1219 "%s",
1220 refpath);
1223 if (!msdfs_link) {
1224 goto out;
1226 if (!insert_comma) {
1227 insert_comma = True;
1231 DEBUG(5,("create_msdfs_link: Creating new msdfs link: %s -> %s\n",
1232 path, msdfs_link));
1234 if(SMB_VFS_SYMLINK(conn, msdfs_link, path) < 0) {
1235 if (errno == EEXIST) {
1236 struct smb_filename *smb_fname = NULL;
1237 NTSTATUS status;
1239 status = create_synthetic_smb_fname(talloc_tos(), path,
1240 NULL, NULL,
1241 &smb_fname);
1242 if (!NT_STATUS_IS_OK(status)) {
1243 errno = map_errno_from_nt_status(status);
1244 goto out;
1247 if(SMB_VFS_UNLINK(conn, smb_fname)!=0) {
1248 TALLOC_FREE(smb_fname);
1249 goto out;
1251 TALLOC_FREE(smb_fname);
1253 if (SMB_VFS_SYMLINK(conn, msdfs_link, path) < 0) {
1254 DEBUG(1,("create_msdfs_link: symlink failed "
1255 "%s -> %s\nError: %s\n",
1256 path, msdfs_link, strerror(errno)));
1257 goto out;
1261 ret = True;
1263 out:
1264 vfs_ChDir(conn, cwd);
1265 SMB_VFS_DISCONNECT(conn);
1266 conn_free(conn);
1267 return ret;
1270 bool remove_msdfs_link(const struct junction_map *jucn)
1272 char *path = NULL;
1273 char *cwd;
1274 connection_struct *conn;
1275 bool ret = False;
1276 struct smb_filename *smb_fname = NULL;
1277 NTSTATUS status;
1279 if (!junction_to_local_path(jucn, &path, &conn, &cwd)) {
1280 return false;
1283 status = create_synthetic_smb_fname(talloc_tos(), path,
1284 NULL, NULL,
1285 &smb_fname);
1286 if (!NT_STATUS_IS_OK(status)) {
1287 errno = map_errno_from_nt_status(status);
1288 return false;
1291 if( SMB_VFS_UNLINK(conn, smb_fname) == 0 ) {
1292 ret = True;
1295 TALLOC_FREE(smb_fname);
1296 vfs_ChDir(conn, cwd);
1297 SMB_VFS_DISCONNECT(conn);
1298 conn_free(conn);
1299 return ret;
1302 /*********************************************************************
1303 Return the number of DFS links at the root of this share.
1304 *********************************************************************/
1306 static int count_dfs_links(TALLOC_CTX *ctx, int snum)
1308 size_t cnt = 0;
1309 DIR *dirp = NULL;
1310 const char *dname = NULL;
1311 char *talloced = NULL;
1312 const char *connect_path = lp_pathname(snum);
1313 const char *msdfs_proxy = lp_msdfs_proxy(snum);
1314 connection_struct *conn;
1315 NTSTATUS status;
1316 char *cwd;
1318 if(*connect_path == '\0') {
1319 return 0;
1323 * Fake up a connection struct for the VFS layer.
1326 status = create_conn_struct(talloc_tos(),
1327 server_event_context(),
1328 server_messaging_context(),
1329 &conn,
1330 snum, connect_path, NULL, &cwd);
1331 if (!NT_STATUS_IS_OK(status)) {
1332 DEBUG(3, ("create_conn_struct failed: %s\n",
1333 nt_errstr(status)));
1334 return 0;
1337 /* Count a link for the msdfs root - convention */
1338 cnt = 1;
1340 /* No more links if this is an msdfs proxy. */
1341 if (*msdfs_proxy != '\0') {
1342 goto out;
1345 /* Now enumerate all dfs links */
1346 dirp = SMB_VFS_OPENDIR(conn, ".", NULL, 0);
1347 if(!dirp) {
1348 goto out;
1351 while ((dname = vfs_readdirname(conn, dirp, NULL, &talloced))
1352 != NULL) {
1353 if (is_msdfs_link(conn,
1354 dname,
1355 NULL)) {
1356 cnt++;
1358 TALLOC_FREE(talloced);
1361 SMB_VFS_CLOSEDIR(conn,dirp);
1363 out:
1364 vfs_ChDir(conn, cwd);
1365 SMB_VFS_DISCONNECT(conn);
1366 conn_free(conn);
1367 return cnt;
1370 /*********************************************************************
1371 *********************************************************************/
1373 static int form_junctions(TALLOC_CTX *ctx,
1374 int snum,
1375 struct junction_map *jucn,
1376 size_t jn_remain)
1378 size_t cnt = 0;
1379 DIR *dirp = NULL;
1380 const char *dname = NULL;
1381 char *talloced = NULL;
1382 const char *connect_path = lp_pathname(snum);
1383 char *service_name = lp_servicename(snum);
1384 const char *msdfs_proxy = lp_msdfs_proxy(snum);
1385 connection_struct *conn;
1386 struct referral *ref = NULL;
1387 char *cwd;
1388 NTSTATUS status;
1390 if (jn_remain == 0) {
1391 return 0;
1394 if(*connect_path == '\0') {
1395 return 0;
1399 * Fake up a connection struct for the VFS layer.
1402 status = create_conn_struct(ctx,
1403 server_event_context(),
1404 server_messaging_context(),
1405 &conn, snum, connect_path, NULL,
1406 &cwd);
1407 if (!NT_STATUS_IS_OK(status)) {
1408 DEBUG(3, ("create_conn_struct failed: %s\n",
1409 nt_errstr(status)));
1410 return 0;
1413 /* form a junction for the msdfs root - convention
1414 DO NOT REMOVE THIS: NT clients will not work with us
1415 if this is not present
1417 jucn[cnt].service_name = talloc_strdup(ctx,service_name);
1418 jucn[cnt].volume_name = talloc_strdup(ctx, "");
1419 if (!jucn[cnt].service_name || !jucn[cnt].volume_name) {
1420 goto out;
1422 jucn[cnt].comment = "";
1423 jucn[cnt].referral_count = 1;
1425 ref = jucn[cnt].referral_list = talloc_zero(ctx, struct referral);
1426 if (jucn[cnt].referral_list == NULL) {
1427 goto out;
1430 ref->proximity = 0;
1431 ref->ttl = REFERRAL_TTL;
1432 if (*msdfs_proxy != '\0') {
1433 ref->alternate_path = talloc_strdup(ctx,
1434 msdfs_proxy);
1435 } else {
1436 ref->alternate_path = talloc_asprintf(ctx,
1437 "\\\\%s\\%s",
1438 get_local_machine_name(),
1439 service_name);
1442 if (!ref->alternate_path) {
1443 goto out;
1445 cnt++;
1447 /* Don't enumerate if we're an msdfs proxy. */
1448 if (*msdfs_proxy != '\0') {
1449 goto out;
1452 /* Now enumerate all dfs links */
1453 dirp = SMB_VFS_OPENDIR(conn, ".", NULL, 0);
1454 if(!dirp) {
1455 goto out;
1458 while ((dname = vfs_readdirname(conn, dirp, NULL, &talloced))
1459 != NULL) {
1460 char *link_target = NULL;
1461 if (cnt >= jn_remain) {
1462 DEBUG(2, ("form_junctions: ran out of MSDFS "
1463 "junction slots"));
1464 TALLOC_FREE(talloced);
1465 goto out;
1467 if (is_msdfs_link_internal(ctx,
1468 conn,
1469 dname, &link_target,
1470 NULL)) {
1471 if (parse_msdfs_symlink(ctx,
1472 link_target,
1473 &jucn[cnt].referral_list,
1474 &jucn[cnt].referral_count)) {
1476 jucn[cnt].service_name = talloc_strdup(ctx,
1477 service_name);
1478 jucn[cnt].volume_name = talloc_strdup(ctx,
1479 dname);
1480 if (!jucn[cnt].service_name ||
1481 !jucn[cnt].volume_name) {
1482 TALLOC_FREE(talloced);
1483 goto out;
1485 jucn[cnt].comment = "";
1486 cnt++;
1488 TALLOC_FREE(link_target);
1490 TALLOC_FREE(talloced);
1493 out:
1495 if (dirp) {
1496 SMB_VFS_CLOSEDIR(conn,dirp);
1499 vfs_ChDir(conn, cwd);
1500 conn_free(conn);
1501 return cnt;
1504 struct junction_map *enum_msdfs_links(TALLOC_CTX *ctx, size_t *p_num_jn)
1506 struct junction_map *jn = NULL;
1507 int i=0;
1508 size_t jn_count = 0;
1509 int sharecount = 0;
1511 *p_num_jn = 0;
1512 if(!lp_host_msdfs()) {
1513 return NULL;
1516 /* Ensure all the usershares are loaded. */
1517 become_root();
1518 load_registry_shares();
1519 sharecount = load_usershare_shares(NULL, connections_snum_used);
1520 unbecome_root();
1522 for(i=0;i < sharecount;i++) {
1523 if(lp_msdfs_root(i)) {
1524 jn_count += count_dfs_links(ctx, i);
1527 if (jn_count == 0) {
1528 return NULL;
1530 jn = talloc_array(ctx, struct junction_map, jn_count);
1531 if (!jn) {
1532 return NULL;
1534 for(i=0; i < sharecount; i++) {
1535 if (*p_num_jn >= jn_count) {
1536 break;
1538 if(lp_msdfs_root(i)) {
1539 *p_num_jn += form_junctions(ctx, i,
1540 &jn[*p_num_jn],
1541 jn_count - *p_num_jn);
1544 return jn;
1547 /******************************************************************************
1548 Core function to resolve a dfs pathname possibly containing a wildcard. If
1549 ppath_contains_wcard != NULL, it will be set to true if a wildcard is
1550 detected during dfs resolution.
1551 ******************************************************************************/
1553 NTSTATUS resolve_dfspath_wcard(TALLOC_CTX *ctx,
1554 connection_struct *conn,
1555 bool dfs_pathnames,
1556 const char *name_in,
1557 bool allow_wcards,
1558 char **pp_name_out,
1559 bool *ppath_contains_wcard)
1561 bool path_contains_wcard;
1562 NTSTATUS status = NT_STATUS_OK;
1564 if (dfs_pathnames) {
1565 status = dfs_redirect(ctx,
1566 conn,
1567 name_in,
1568 allow_wcards,
1569 !smbd_server_conn->using_smb2,
1570 pp_name_out,
1571 &path_contains_wcard);
1573 if (NT_STATUS_IS_OK(status) && ppath_contains_wcard != NULL) {
1574 *ppath_contains_wcard = path_contains_wcard;
1576 } else {
1578 * Cheat and just return a copy of the in ptr.
1579 * Once srvstr_get_path() uses talloc it'll
1580 * be a talloced ptr anyway.
1582 *pp_name_out = discard_const_p(char, name_in);
1584 return status;