libsmb: Use clistr_smb2_extract_snapshot_token() in cli_smb2_create_fnum_send()
[Samba.git] / source3 / smbd / msdfs.c
blobb5fd7608e07605391301bfa510124fe5a831f1a1
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
7 Copyright (C) Robin McCorkell 2015
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 3 of the License, or
12 (at your option) any later version.
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
19 You should have received a copy of the GNU General Public License
20 along with this program. If not, see <http://www.gnu.org/licenses/>.
24 #define DBGC_CLASS DBGC_MSDFS
25 #include "includes.h"
26 #include "system/filesys.h"
27 #include "smbd/smbd.h"
28 #include "smbd/globals.h"
29 #include "msdfs.h"
30 #include "auth.h"
31 #include "../auth/auth_util.h"
32 #include "lib/param/loadparm.h"
33 #include "libcli/security/security.h"
34 #include "librpc/gen_ndr/ndr_dfsblobs.h"
35 #include "lib/tsocket/tsocket.h"
36 #include "lib/global_contexts.h"
37 #include "source3/lib/substitute.h"
39 /**********************************************************************
40 Function to determine if a given sharename matches a connection.
41 **********************************************************************/
43 static bool msdfs_servicename_matches_connection(struct connection_struct *conn,
44 const char *servicename,
45 const char *vfs_user)
47 const struct loadparm_substitution *lp_sub =
48 loadparm_s3_global_substitution();
49 char *conn_servicename = NULL;
50 int snum;
51 bool match = false;
53 snum = SNUM(conn);
55 conn_servicename = lp_servicename(talloc_tos(), lp_sub, snum);
56 if (conn_servicename == NULL) {
57 DBG_ERR("lp_servicename() failed, OOM!\n");
58 return false;
61 if (strequal(servicename, conn_servicename)) {
62 match = true;
63 goto done;
65 if (strequal(servicename, HOMES_NAME)) {
66 match = true;
67 goto done;
69 if (strequal(vfs_user, conn_servicename)) {
70 match = true;
71 goto done;
73 done:
74 TALLOC_FREE(conn_servicename);
75 return match;
78 /**********************************************************************
79 Parse a DFS pathname of the form(s)
81 \hostname\service - self referral
82 \hostname\service\remainingpath - Windows referral path
84 FIXME! Should we also parse:
85 \hostname\service/remainingpath - POSIX referral path
86 as currently nothing uses this ?
88 into the dfs_path components. Strict form.
90 Checks DFS path starts with separator.
91 Checks hostname is ours.
92 Ensures servicename (share) is sent, and
93 if so, terminates the name or is followed by
94 \pathname.
96 If returned, remainingpath is untouched. Caller must call
97 check_path_syntaxXXX() on it.
99 Called by all non-fileserver processing (DFS RPC, FSCTL_DFS_GET_REFERRALS)
100 etc. Errors out on any inconsistency in the path.
101 **********************************************************************/
103 static NTSTATUS parse_dfs_path_strict(TALLOC_CTX *ctx,
104 const char *pathname,
105 char **_hostname,
106 char **_servicename,
107 char **_remaining_path)
109 char *pathname_local = NULL;
110 char *p = NULL;
111 const char *hostname = NULL;
112 const char *servicename = NULL;
113 const char *reqpath = NULL;
114 bool my_hostname = false;
115 NTSTATUS status;
117 DBG_DEBUG("path = |%s|\n", pathname);
119 pathname_local = talloc_strdup(talloc_tos(), pathname);
120 if (pathname_local == NULL) {
121 return NT_STATUS_NO_MEMORY;
124 * parse_dfs_path_strict() is called from
125 * get_referred_path() and create_junction()
126 * which use Windows DFS paths of \server\share.
130 * Strict DFS paths *must* start with the
131 * path separator '\\'.
134 if (pathname_local[0] != '\\') {
135 DBG_ERR("path %s doesn't start with \\\n",
136 pathname_local);
137 status = NT_STATUS_NOT_FOUND;
138 goto out;
141 /* Now tokenize. */
142 /* Parse out hostname. */
143 p = strchr(pathname_local + 1, '\\');
144 if (p == NULL) {
145 DBG_ERR("can't parse hostname from path %s\n",
146 pathname_local);
147 status = NT_STATUS_NOT_FOUND;
148 goto out;
150 *p = '\0';
151 hostname = &pathname_local[1];
153 DBG_DEBUG("hostname: %s\n", hostname);
155 /* Is this really our hostname ? */
156 my_hostname = is_myname_or_ipaddr(hostname);
157 if (!my_hostname) {
158 DBG_ERR("Hostname %s is not ours.\n",
159 hostname);
160 status = NT_STATUS_NOT_FOUND;
161 goto out;
164 servicename = p + 1;
167 * Find the end of servicename by looking for
168 * a directory separator character. The character
169 * should be '\\' for a Windows path.
170 * If there is no separator, then this is a self-referral
171 * of "\server\share".
174 p = strchr(servicename, '\\');
175 if (p != NULL) {
176 *p = '\0';
179 DBG_DEBUG("servicename: %s\n", servicename);
181 if (p == NULL) {
182 /* Client sent self referral "\server\share". */
183 reqpath = "";
184 } else {
185 /* Step past the '\0' we just replaced '\\' with. */
186 reqpath = p + 1;
189 DBG_DEBUG("rest of the path: %s\n", reqpath);
191 if (_hostname != NULL) {
192 *_hostname = talloc_strdup(ctx, hostname);
193 if (*_hostname == NULL) {
194 status = NT_STATUS_NO_MEMORY;
195 goto out;
198 if (_servicename != NULL) {
199 *_servicename = talloc_strdup(ctx, servicename);
200 if (*_servicename == NULL) {
201 status = NT_STATUS_NO_MEMORY;
202 goto out;
205 if (_remaining_path != NULL) {
206 *_remaining_path = talloc_strdup(ctx, reqpath);
207 if (*_remaining_path == NULL) {
208 status = NT_STATUS_NO_MEMORY;
209 goto out;
213 status = NT_STATUS_OK;
214 out:
215 TALLOC_FREE(pathname_local);
216 return status;
219 /**********************************************************************
220 Parse a DFS pathname of the form /hostname/service/reqpath
221 into the dfs_path structure.
223 NB. srvstr_get_path_internal() now *always* calls
224 check_path_syntax_XXX() on an incoming name, so
225 the path separator is now always '/', even from
226 Windows clients.
228 Unfortunately, due to broken clients who might set the
229 SVAL(inbuf,smb_flg2) & FLAGS2_DFS_PATHNAMES bit and then
230 send a local path, we have to cope with that too....
232 If conn != NULL then ensure the provided service is
233 the one pointed to by the connection.
235 This version does everything using pointers within one copy of the
236 pathname string, talloced on the struct dfs_path pointer (which
237 must be talloced). This may be too clever to live....
238 JRA.
239 **********************************************************************/
241 static NTSTATUS parse_dfs_path(TALLOC_CTX *ctx,
242 connection_struct *conn,
243 const char *pathname,
244 char **_hostname,
245 char **_servicename,
246 char **_remaining_path)
248 char *hostname = NULL;
249 char *pathname_local = NULL;
250 char *p = NULL;
251 char *servicename = NULL;
252 char *reqpath = NULL;
253 char *eos_ptr = NULL;
254 bool servicename_matches = false;
255 bool using_smb1 = !conn->sconn->using_smb2;
257 pathname_local = talloc_strdup(ctx, pathname);
258 if (pathname_local == NULL) {
259 return NT_STATUS_NO_MEMORY;
262 * parse_dfs_path() is only called from
263 * dfs_filename_convert() with SMB1/2/3 DFS
264 * names. Ensure we only have to cope with
265 * '/' separators.
267 string_replace(pathname_local, '\\', '/');
269 /* Get a pointer to the terminating '\0' */
270 eos_ptr = &pathname_local[strlen(pathname_local)];
271 p = pathname_local;
274 * SMB1 DFS paths sent to the fileserver should start with
275 * the path separator '/'. However, libsmbclient libraries
276 * will set the DFS bit on SMB1 calls and then send non-DFS
277 * paths. We must cope with this.
279 * Note SMB2 paths sent to the fileserver never start with
280 * the path separator '/'.
283 if (using_smb1 && (*p != '/')) {
284 DBG_ERR("path %s doesn't start with /\n", p);
286 * Possibly client sent a local path by mistake.
287 * Try and convert to a local path.
288 * Note that this is an SMB1-only fallback
289 * to cope with known broken SMB1 clients.
292 hostname = eos_ptr; /* "" */
293 servicename = eos_ptr; /* "" */
295 DBG_ERR("trying to convert %s to a local path\n", p);
296 goto local_path;
300 * Safe to use on talloc'ed string as it only shrinks.
301 * It also doesn't affect the eos_ptr.
303 trim_char(p, '/', '/');
305 DBG_DEBUG("p = |%s| after trimming /'s\n", p);
307 /* Now tokenize. */
308 /* Parse out hostname. */
309 p = strchr(p,'/');
310 if(p == NULL) {
311 DBG_ERR("can't parse hostname from path %s\n", pathname_local);
313 * Possibly client sent a local path by mistake.
314 * Try and convert to a local path.
317 hostname = eos_ptr; /* "" */
318 servicename = eos_ptr; /* "" */
320 p = pathname_local;
321 DBG_ERR("trying to convert %s to a local path\n", p);
322 goto local_path;
324 *p = '\0';
325 hostname = pathname_local;
327 DBG_DEBUG("hostname: %s\n", hostname);
329 /* Parse out servicename. */
330 servicename = p+1;
331 p = strchr(servicename, '/');
332 if (p) {
333 *p = '\0';
336 /* Is this really our servicename ? */
337 servicename_matches = msdfs_servicename_matches_connection(
338 conn,
339 servicename,
340 get_current_username());
342 if (!servicename_matches) {
343 DBG_ERR("%s is not our servicename\n", servicename);
346 * Possibly client sent a local path by mistake.
347 * Try and convert to a local path.
350 /* Repair the path - replace the sepchar's
351 we nulled out */
352 servicename--;
353 *servicename = '/';
354 if (p) {
355 *p = '/';
358 hostname = eos_ptr; /* "" */
359 servicename = eos_ptr; /* "" */
361 p = pathname_local;
362 DBG_ERR("trying to convert %s to a local path\n",
363 pathname_local);
364 goto local_path;
367 servicename = servicename;
369 DBG_DEBUG("servicename: %s\n", servicename);
371 if(p == NULL) {
372 /* Client sent self referral \server\share. */
373 reqpath = eos_ptr; /* "" */
374 goto out;
377 p++;
379 local_path:
382 * As check_path_syntax_XXX() has already been
383 * called we know this is a normal path containing
384 * '/' separators.
387 reqpath = p;
389 out:
391 DBG_DEBUG("rest of the path: %s\n", reqpath);
393 if (_hostname != NULL) {
394 *_hostname = talloc_strdup(ctx, hostname);
395 if (*_hostname == NULL) {
396 return NT_STATUS_NO_MEMORY;
399 if (_servicename != NULL) {
400 *_servicename = talloc_strdup(ctx, servicename);
401 if (*_servicename == NULL) {
402 return NT_STATUS_NO_MEMORY;
405 if (_remaining_path != NULL) {
406 *_remaining_path = talloc_strdup(ctx, reqpath);
407 if (*_remaining_path == NULL) {
408 return NT_STATUS_NO_MEMORY;
411 TALLOC_FREE(pathname_local);
412 return NT_STATUS_OK;
415 /********************************************************
416 Fake up a connection struct for the VFS layer, for use in
417 applications (such as the python bindings), that do not want the
418 global working directory changed under them.
420 SMB_VFS_CONNECT requires root privileges.
421 *********************************************************/
423 static NTSTATUS create_conn_struct_as_root(TALLOC_CTX *ctx,
424 struct tevent_context *ev,
425 struct messaging_context *msg,
426 connection_struct **pconn,
427 int snum,
428 const char *path,
429 const struct auth_session_info *session_info)
431 connection_struct *conn;
432 char *connpath;
433 const char *vfs_user;
434 struct smbd_server_connection *sconn;
435 const char *servicename = lp_const_servicename(snum);
436 bool ok;
438 sconn = talloc_zero(ctx, struct smbd_server_connection);
439 if (sconn == NULL) {
440 return NT_STATUS_NO_MEMORY;
443 sconn->ev_ctx = ev;
444 sconn->msg_ctx = msg;
446 conn = conn_new(sconn);
447 if (conn == NULL) {
448 TALLOC_FREE(sconn);
449 return NT_STATUS_NO_MEMORY;
452 /* Now we have conn, we need to make sconn a child of conn,
453 * for a proper talloc tree */
454 talloc_steal(conn, sconn);
456 if (snum == -1 && servicename == NULL) {
457 servicename = "Unknown Service (snum == -1)";
460 connpath = talloc_strdup(conn, path);
461 if (!connpath) {
462 TALLOC_FREE(conn);
463 return NT_STATUS_NO_MEMORY;
465 connpath = talloc_string_sub(conn,
466 connpath,
467 "%S",
468 servicename);
469 if (!connpath) {
470 TALLOC_FREE(conn);
471 return NT_STATUS_NO_MEMORY;
474 /* needed for smbd_vfs_init() */
476 conn->params->service = snum;
477 conn->cnum = TID_FIELD_INVALID;
479 SMB_ASSERT(session_info != NULL);
481 conn->session_info = copy_session_info(conn, session_info);
482 if (conn->session_info == NULL) {
483 DBG_ERR("copy_serverinfo failed\n");
484 TALLOC_FREE(conn);
485 return NT_STATUS_NO_MEMORY;
488 /* unix_info could be NULL in session_info */
489 if (conn->session_info->unix_info != NULL) {
490 vfs_user = conn->session_info->unix_info->unix_name;
491 } else {
492 vfs_user = get_current_username();
495 conn_setup_case_options(conn);
497 set_conn_connectpath(conn, connpath);
500 * New code to check if there's a share security descriptor
501 * added from NT server manager. This is done after the
502 * smb.conf checks are done as we need a uid and token. JRA.
505 share_access_check(conn->session_info->security_token,
506 servicename,
507 MAXIMUM_ALLOWED_ACCESS,
508 &conn->share_access);
510 if ((conn->share_access & FILE_WRITE_DATA) == 0) {
511 if ((conn->share_access & FILE_READ_DATA) == 0) {
512 /* No access, read or write. */
513 DBG_WARNING("connection to %s "
514 "denied due to security "
515 "descriptor.\n",
516 servicename);
517 conn_free(conn);
518 return NT_STATUS_ACCESS_DENIED;
520 conn->read_only = true;
523 if (!smbd_vfs_init(conn)) {
524 NTSTATUS status = map_nt_error_from_unix(errno);
525 DEBUG(0,("create_conn_struct: smbd_vfs_init failed.\n"));
526 conn_free(conn);
527 return status;
530 /* this must be the first filesystem operation that we do */
531 if (SMB_VFS_CONNECT(conn, servicename, vfs_user) < 0) {
532 DEBUG(0,("VFS connect failed!\n"));
533 conn_free(conn);
534 return NT_STATUS_UNSUCCESSFUL;
537 ok = canonicalize_connect_path(conn);
538 if (!ok) {
539 DBG_ERR("Failed to canonicalize sharepath\n");
540 conn_free(conn);
541 return NT_STATUS_ACCESS_DENIED;
544 conn->fs_capabilities = SMB_VFS_FS_CAPABILITIES(conn, &conn->ts_res);
545 conn->tcon_done = true;
546 *pconn = talloc_move(ctx, &conn);
548 return NT_STATUS_OK;
551 static int conn_struct_tos_destructor(struct conn_struct_tos *c)
553 if (c->oldcwd_fname != NULL) {
554 vfs_ChDir(c->conn, c->oldcwd_fname);
555 TALLOC_FREE(c->oldcwd_fname);
557 SMB_VFS_DISCONNECT(c->conn);
558 conn_free(c->conn);
559 return 0;
562 /********************************************************
563 Fake up a connection struct for the VFS layer, for use in
564 applications (such as the python bindings), that do not want the
565 global working directory changed under them.
567 SMB_VFS_CONNECT requires root privileges.
568 This temporary uses become_root() and unbecome_root().
570 But further impersonation has to be cone by the caller.
571 *********************************************************/
572 NTSTATUS create_conn_struct_tos(struct messaging_context *msg,
573 int snum,
574 const char *path,
575 const struct auth_session_info *session_info,
576 struct conn_struct_tos **_c)
578 struct conn_struct_tos *c = NULL;
579 struct tevent_context *ev = NULL;
580 NTSTATUS status;
582 *_c = NULL;
584 c = talloc_zero(talloc_tos(), struct conn_struct_tos);
585 if (c == NULL) {
586 return NT_STATUS_NO_MEMORY;
589 ev = samba_tevent_context_init(c);
590 if (ev == NULL) {
591 TALLOC_FREE(c);
592 return NT_STATUS_NO_MEMORY;
595 become_root();
596 status = create_conn_struct_as_root(c,
598 msg,
599 &c->conn,
600 snum,
601 path,
602 session_info);
603 unbecome_root();
604 if (!NT_STATUS_IS_OK(status)) {
605 TALLOC_FREE(c);
606 return status;
609 talloc_set_destructor(c, conn_struct_tos_destructor);
611 *_c = c;
612 return NT_STATUS_OK;
615 /********************************************************
616 Fake up a connection struct for the VFS layer.
617 Note: this performs a vfs connect and CHANGES CWD !!!! JRA.
619 See also the comment for create_conn_struct_tos() above!
621 The CWD change is reverted by the destructor of
622 conn_struct_tos when the current talloc_tos() is destroyed.
623 *********************************************************/
624 NTSTATUS create_conn_struct_tos_cwd(struct messaging_context *msg,
625 int snum,
626 const char *path,
627 const struct auth_session_info *session_info,
628 struct conn_struct_tos **_c)
630 struct conn_struct_tos *c = NULL;
631 struct smb_filename smb_fname_connectpath = {0};
632 NTSTATUS status;
634 *_c = NULL;
636 status = create_conn_struct_tos(msg,
637 snum,
638 path,
639 session_info,
640 &c);
641 if (!NT_STATUS_IS_OK(status)) {
642 return status;
646 * Windows seems to insist on doing trans2getdfsreferral() calls on
647 * the IPC$ share as the anonymous user. If we try to chdir as that
648 * user we will fail.... WTF ? JRA.
651 c->oldcwd_fname = vfs_GetWd(c, c->conn);
652 if (c->oldcwd_fname == NULL) {
653 status = map_nt_error_from_unix(errno);
654 DEBUG(3, ("vfs_GetWd failed: %s\n", strerror(errno)));
655 TALLOC_FREE(c);
656 return status;
659 smb_fname_connectpath = (struct smb_filename) {
660 .base_name = c->conn->connectpath
663 if (vfs_ChDir(c->conn, &smb_fname_connectpath) != 0) {
664 status = map_nt_error_from_unix(errno);
665 DBG_NOTICE("Can't ChDir to new conn path %s. "
666 "Error was %s\n",
667 c->conn->connectpath, strerror(errno));
668 TALLOC_FREE(c->oldcwd_fname);
669 TALLOC_FREE(c);
670 return status;
673 *_c = c;
674 return NT_STATUS_OK;
677 /********************************************************
678 Fake up a connection struct for the VFS layer.
679 This takes an TALLOC_CTX and tevent_context from the
680 caller and the resulting connection_struct is stable
681 across the lifetime of mem_ctx and ev.
683 Note: this performs a vfs connect and changes cwd.
685 See also the comment for create_conn_struct_tos() above!
686 *********************************************************/
688 NTSTATUS create_conn_struct_cwd(TALLOC_CTX *mem_ctx,
689 struct tevent_context *ev,
690 struct messaging_context *msg,
691 const struct auth_session_info *session_info,
692 int snum,
693 const char *path,
694 struct connection_struct **c)
696 NTSTATUS status;
698 become_root();
699 status = create_conn_struct_as_root(mem_ctx,
701 msg,
703 snum,
704 path,
705 session_info);
706 unbecome_root();
707 return status;
710 static void shuffle_strlist(char **list, int count)
712 int i;
713 uint32_t r;
714 char *tmp;
716 for (i = count; i > 1; i--) {
717 r = generate_random() % i;
719 tmp = list[i-1];
720 list[i-1] = list[r];
721 list[r] = tmp;
725 /**********************************************************************
726 Parse the contents of a symlink to verify if it is an msdfs referral
727 A valid referral is of the form:
729 msdfs:server1\share1,server2\share2
730 msdfs:server1\share1\pathname,server2\share2\pathname
731 msdfs:server1/share1,server2/share2
732 msdfs:server1/share1/pathname,server2/share2/pathname.
734 Note that the alternate paths returned here must be of the canonicalized
735 form:
737 \server\share or
738 \server\share\path\to\file,
740 even in posix path mode. This is because we have no knowledge if the
741 server we're referring to understands posix paths.
742 **********************************************************************/
744 bool parse_msdfs_symlink(TALLOC_CTX *ctx,
745 bool shuffle_referrals,
746 const char *target,
747 struct referral **ppreflist,
748 size_t *prefcount)
750 char *temp = NULL;
751 char *prot;
752 char **alt_path = NULL;
753 size_t count = 0, i;
754 struct referral *reflist = NULL;
755 char *saveptr;
757 temp = talloc_strdup(ctx, target);
758 if (!temp) {
759 return false;
761 prot = strtok_r(temp, ":", &saveptr);
762 if (!prot) {
763 DEBUG(0,("parse_msdfs_symlink: invalid path !\n"));
764 TALLOC_FREE(temp);
765 return false;
768 alt_path = talloc_array(ctx, char *, MAX_REFERRAL_COUNT);
769 if (!alt_path) {
770 TALLOC_FREE(temp);
771 return false;
774 /* parse out the alternate paths */
775 while((count<MAX_REFERRAL_COUNT) &&
776 ((alt_path[count] = strtok_r(NULL, ",", &saveptr)) != NULL)) {
777 count++;
780 /* shuffle alternate paths */
781 if (shuffle_referrals) {
782 shuffle_strlist(alt_path, count);
785 DBG_DEBUG("count=%zu\n", count);
787 if (count) {
788 reflist = talloc_zero_array(ctx,
789 struct referral, count);
790 if(reflist == NULL) {
791 TALLOC_FREE(temp);
792 TALLOC_FREE(alt_path);
793 return false;
795 } else {
796 reflist = NULL;
799 for(i=0;i<count;i++) {
800 char *p;
802 /* Canonicalize link target.
803 * Replace all /'s in the path by a \ */
804 string_replace(alt_path[i], '/', '\\');
806 /* Remove leading '\\'s */
807 p = alt_path[i];
808 while (*p && (*p == '\\')) {
809 p++;
812 reflist[i].alternate_path = talloc_asprintf(reflist,
813 "\\%s",
815 if (!reflist[i].alternate_path) {
816 TALLOC_FREE(temp);
817 TALLOC_FREE(alt_path);
818 TALLOC_FREE(reflist);
819 return false;
822 reflist[i].proximity = 0;
823 reflist[i].ttl = REFERRAL_TTL;
824 DBG_DEBUG("Created alt path: %s\n",
825 reflist[i].alternate_path);
828 if (ppreflist != NULL) {
829 *ppreflist = reflist;
830 } else {
831 TALLOC_FREE(reflist);
833 if (prefcount != NULL) {
834 *prefcount = count;
836 TALLOC_FREE(temp);
837 TALLOC_FREE(alt_path);
838 return true;
841 /**********************************************************************
842 Returns true if the unix path is a valid msdfs symlink.
843 **********************************************************************/
845 bool is_msdfs_link(struct files_struct *dirfsp,
846 struct smb_filename *atname)
848 NTSTATUS status = SMB_VFS_READ_DFS_PATHAT(dirfsp->conn,
849 talloc_tos(),
850 dirfsp,
851 atname,
852 NULL,
853 NULL);
854 return (NT_STATUS_IS_OK(status));
857 /*****************************************************************
858 Used by other functions to decide if a dfs path is remote,
859 and to get the list of referred locations for that remote path.
861 consumedcntp: how much of the dfs path is being redirected. the client
862 should try the remaining path on the redirected server.
863 *****************************************************************/
865 static NTSTATUS dfs_path_lookup(TALLOC_CTX *ctx,
866 connection_struct *conn,
867 const char *dfspath, /* Incoming complete dfs path */
868 const char *reqpath, /* Parsed out remaining path. */
869 uint32_t ucf_flags,
870 size_t *consumedcntp,
871 struct referral **ppreflist,
872 size_t *preferral_count)
874 NTSTATUS status;
875 struct smb_filename *parent_smb_fname = NULL;
876 struct smb_filename *smb_fname_rel = NULL;
877 NTTIME twrp = 0;
878 char *local_pathname = NULL;
879 char *last_component = NULL;
880 char *atname = NULL;
881 size_t removed_components = 0;
882 bool posix = (ucf_flags & UCF_POSIX_PATHNAMES);
883 char *p = NULL;
884 char *canon_dfspath = NULL;
886 DBG_DEBUG("Conn path = %s reqpath = %s\n", conn->connectpath, reqpath);
888 local_pathname = talloc_strdup(ctx, reqpath);
889 if (local_pathname == NULL) {
890 status = NT_STATUS_NO_MEMORY;
891 goto out;
894 /* We know reqpath isn't a DFS path. */
895 ucf_flags &= ~UCF_DFS_PATHNAME;
897 if (ucf_flags & UCF_GMT_PATHNAME) {
898 extract_snapshot_token(local_pathname, &twrp);
899 ucf_flags &= ~UCF_GMT_PATHNAME;
903 * We should have been given a DFS path to resolve.
904 * This should return NT_STATUS_PATH_NOT_COVERED.
906 * Do a pathname walk, stripping off components
907 * until we get NT_STATUS_OK instead of
908 * NT_STATUS_PATH_NOT_COVERED.
910 * Fail on any other error.
913 for (;;) {
914 TALLOC_CTX *frame = NULL;
915 struct files_struct *dirfsp = NULL;
916 struct smb_filename *smb_fname_walk = NULL;
918 TALLOC_FREE(parent_smb_fname);
921 * Use a local stackframe as filename_convert_dirfsp()
922 * opens handles on the last two components in the path.
923 * Allow these to be freed as we step back through
924 * the local_pathname.
926 frame = talloc_stackframe();
927 status = filename_convert_dirfsp(frame,
928 conn,
929 local_pathname,
930 ucf_flags,
931 twrp,
932 &dirfsp,
933 &smb_fname_walk);
934 /* If we got a name, save it. */
935 if (smb_fname_walk != NULL) {
936 parent_smb_fname = talloc_move(ctx, &smb_fname_walk);
938 TALLOC_FREE(frame);
940 if (!NT_STATUS_EQUAL(status, NT_STATUS_PATH_NOT_COVERED)) {
942 * For any other status than NT_STATUS_PATH_NOT_COVERED
943 * (including NT_STATUS_OK) we exit the walk.
944 * If it's an error we catch it outside the loop.
946 break;
949 /* Step back one component and save it off as last_component. */
950 TALLOC_FREE(last_component);
951 p = strrchr(local_pathname, '/');
952 if (p == NULL) {
954 * We removed all components.
955 * Go around once more to make
956 * sure we can open the root '\0'.
958 last_component = talloc_strdup(ctx, local_pathname);
959 *local_pathname = '\0';
960 } else {
961 last_component = talloc_strdup(ctx, p+1);
962 *p = '\0';
964 if (last_component == NULL) {
965 status = NT_STATUS_NO_MEMORY;
966 goto out;
968 /* Integer wrap check. */
969 if (removed_components + 1 < removed_components) {
970 status = NT_STATUS_INVALID_PARAMETER;
971 goto out;
973 removed_components++;
976 if (!NT_STATUS_IS_OK(status)) {
977 DBG_DEBUG("dfspath = %s. reqpath = %s. Error %s.\n",
978 dfspath,
979 reqpath,
980 nt_errstr(status));
981 goto out;
984 if (parent_smb_fname->fsp == NULL) {
985 /* Unable to open parent. */
986 DBG_DEBUG("dfspath = %s. reqpath = %s. "
987 "Unable to open parent directory (%s).\n",
988 dfspath,
989 reqpath,
990 smb_fname_str_dbg(parent_smb_fname));
991 status = NT_STATUS_OBJECT_PATH_NOT_FOUND;
992 goto out;
995 if (removed_components == 0) {
997 * We never got NT_STATUS_PATH_NOT_COVERED.
998 * There was no DFS redirect.
1000 DBG_DEBUG("dfspath = %s. reqpath = %s. "
1001 "No removed components.\n",
1002 dfspath,
1003 reqpath);
1004 status = NT_STATUS_OBJECT_PATH_NOT_FOUND;
1005 goto out;
1009 * One of the removed_components was the MSDFS link
1010 * at the end. We need to count this in the resolved
1011 * path below, so remove one from removed_components.
1013 removed_components--;
1016 * Now parent_smb_fname->fsp is the parent directory dirfsp,
1017 * last_component is the untranslated MS-DFS link name.
1018 * Search for it in the parent directory to get the real
1019 * filename on disk.
1021 status = get_real_filename_at(parent_smb_fname->fsp,
1022 last_component,
1023 ctx,
1024 &atname);
1026 if (!NT_STATUS_IS_OK(status)) {
1027 DBG_DEBUG("dfspath = %s. reqpath = %s "
1028 "get_real_filename_at(%s, %s) error (%s)\n",
1029 dfspath,
1030 reqpath,
1031 smb_fname_str_dbg(parent_smb_fname),
1032 last_component,
1033 nt_errstr(status));
1034 goto out;
1037 smb_fname_rel = synthetic_smb_fname(ctx,
1038 atname,
1039 NULL,
1040 NULL,
1041 twrp,
1042 posix ? SMB_FILENAME_POSIX_PATH : 0);
1043 if (smb_fname_rel == NULL) {
1044 status = NT_STATUS_NO_MEMORY;
1045 goto out;
1048 /* Get the referral to return. */
1049 status = SMB_VFS_READ_DFS_PATHAT(conn,
1050 ctx,
1051 parent_smb_fname->fsp,
1052 smb_fname_rel,
1053 ppreflist,
1054 preferral_count);
1055 if (!NT_STATUS_IS_OK(status)) {
1056 DBG_DEBUG("dfspath = %s. reqpath = %s. "
1057 "SMB_VFS_READ_DFS_PATHAT(%s, %s) error (%s)\n",
1058 dfspath,
1059 reqpath,
1060 smb_fname_str_dbg(parent_smb_fname),
1061 smb_fname_str_dbg(smb_fname_rel),
1062 nt_errstr(status));
1063 goto out;
1067 * Now we must work out how much of the
1068 * given pathname we consumed.
1070 canon_dfspath = talloc_strdup(ctx, dfspath);
1071 if (!canon_dfspath) {
1072 status = NT_STATUS_NO_MEMORY;
1073 goto out;
1075 /* Canonicalize the raw dfspath. */
1076 string_replace(canon_dfspath, '\\', '/');
1079 * reqpath comes out of parse_dfs_path(), so it has
1080 * no trailing backslash. Make sure that canon_dfspath hasn't either.
1082 trim_char(canon_dfspath, 0, '/');
1084 DBG_DEBUG("Unconsumed path: %s\n", canon_dfspath);
1086 while (removed_components > 0) {
1087 p = strrchr(canon_dfspath, '/');
1088 if (p != NULL) {
1089 *p = '\0';
1091 removed_components--;
1092 if (p == NULL && removed_components != 0) {
1093 DBG_ERR("Component mismatch. path = %s, "
1094 "%zu components left\n",
1095 canon_dfspath,
1096 removed_components);
1097 status = NT_STATUS_OBJECT_PATH_NOT_FOUND;
1098 goto out;
1101 *consumedcntp = strlen(canon_dfspath);
1102 DBG_DEBUG("Path consumed: %s (%zu)\n", canon_dfspath, *consumedcntp);
1103 status = NT_STATUS_OK;
1105 out:
1107 TALLOC_FREE(parent_smb_fname);
1108 TALLOC_FREE(local_pathname);
1109 TALLOC_FREE(last_component);
1110 TALLOC_FREE(atname);
1111 TALLOC_FREE(smb_fname_rel);
1112 TALLOC_FREE(canon_dfspath);
1113 return status;
1116 /*****************************************************************
1117 Decides if a dfs pathname should be redirected or not.
1118 If not, the pathname is converted to a tcon-relative local unix path
1119 This is now a simple wrapper around parse_dfs_path()
1120 as it does all the required checks.
1121 *****************************************************************/
1123 NTSTATUS dfs_filename_convert(TALLOC_CTX *ctx,
1124 connection_struct *conn,
1125 uint32_t ucf_flags,
1126 const char *dfs_path_in,
1127 char **pp_path_out)
1129 char *reqpath = NULL;
1130 NTSTATUS status;
1133 * We must use the non-strict version of parse_dfs_path for
1134 * pathnames sent to the fileserver over SMB1/2/3.
1135 * libsmbclient callers always set the FLAGS2_DFS_PATHNAMES
1136 * but then don't send a DFS path in (for example) FindFirst
1137 * or other calls. This is a problem with our client libraries
1138 * for both SMB1 and SMB2+ and will remain so whilst broken
1139 * versions of libsmbclient are being used.
1142 status = parse_dfs_path(ctx,
1143 conn,
1144 dfs_path_in,
1145 NULL, /* hostname */
1146 NULL, /* servicename */
1147 &reqpath);
1148 if (!NT_STATUS_IS_OK(status)) {
1149 return status;
1153 * If parse_dfs_path fell back to a local path
1154 * after skipping hostname or servicename, ensure
1155 * we still have called check_path_syntax()
1156 * on the full returned local path. check_path_syntax()
1157 * is idempotent so this is safe.
1159 if (ucf_flags & UCF_POSIX_PATHNAMES) {
1160 status = check_path_syntax_posix(reqpath);
1161 } else {
1162 status = check_path_syntax(reqpath);
1164 if (!NT_STATUS_IS_OK(status)) {
1165 return status;
1168 * Previous (and current logic) just ignores
1169 * the server, share components if a DFS
1170 * path is sent on a non-DFS share except to
1171 * check that they match an existing share. Should
1172 * we tighten this up to return an error here ?
1174 *pp_path_out = reqpath;
1175 return NT_STATUS_OK;
1178 /**********************************************************************
1179 Return a self referral.
1180 **********************************************************************/
1182 static NTSTATUS self_ref(TALLOC_CTX *ctx,
1183 const char *dfs_path,
1184 struct junction_map *jucn,
1185 size_t *consumedcntp,
1186 bool *self_referralp)
1188 struct referral *ref;
1190 *self_referralp = True;
1192 jucn->referral_count = 1;
1193 if((ref = talloc_zero(ctx, struct referral)) == NULL) {
1194 return NT_STATUS_NO_MEMORY;
1197 ref->alternate_path = talloc_strdup(ctx, dfs_path);
1198 if (!ref->alternate_path) {
1199 TALLOC_FREE(ref);
1200 return NT_STATUS_NO_MEMORY;
1202 ref->proximity = 0;
1203 ref->ttl = REFERRAL_TTL;
1204 jucn->referral_list = ref;
1205 *consumedcntp = strlen(dfs_path);
1206 return NT_STATUS_OK;
1209 /**********************************************************************
1210 Gets valid referrals for a dfs path and fills up the
1211 junction_map structure.
1212 **********************************************************************/
1214 NTSTATUS get_referred_path(TALLOC_CTX *ctx,
1215 struct auth_session_info *session_info,
1216 const char *dfs_path,
1217 const struct tsocket_address *remote_address,
1218 const struct tsocket_address *local_address,
1219 struct junction_map *jucn,
1220 size_t *consumedcntp,
1221 bool *self_referralp)
1223 TALLOC_CTX *frame = talloc_stackframe();
1224 const struct loadparm_substitution *lp_sub =
1225 loadparm_s3_global_substitution();
1226 struct conn_struct_tos *c = NULL;
1227 struct connection_struct *conn = NULL;
1228 char *servicename = NULL;
1229 char *reqpath = NULL;
1230 int snum;
1231 NTSTATUS status = NT_STATUS_NOT_FOUND;
1233 *self_referralp = False;
1235 status = parse_dfs_path_strict(
1236 frame,
1237 dfs_path,
1238 NULL, /* hostname */
1239 &servicename,
1240 &reqpath);
1241 if (!NT_STATUS_IS_OK(status)) {
1242 TALLOC_FREE(frame);
1243 return status;
1246 /* Path referrals are always non-POSIX. */
1247 status = check_path_syntax(reqpath);
1248 if (!NT_STATUS_IS_OK(status)) {
1249 TALLOC_FREE(frame);
1250 return status;
1253 jucn->service_name = talloc_strdup(ctx, servicename);
1254 jucn->volume_name = talloc_strdup(ctx, reqpath);
1255 if (!jucn->service_name || !jucn->volume_name) {
1256 TALLOC_FREE(frame);
1257 return NT_STATUS_NO_MEMORY;
1260 /* Verify the share is a dfs root */
1261 snum = lp_servicenumber(jucn->service_name);
1262 if(snum < 0) {
1263 char *service_name = NULL;
1264 if ((snum = find_service(ctx, jucn->service_name, &service_name)) < 0) {
1265 TALLOC_FREE(frame);
1266 return NT_STATUS_NOT_FOUND;
1268 if (!service_name) {
1269 TALLOC_FREE(frame);
1270 return NT_STATUS_NO_MEMORY;
1272 TALLOC_FREE(jucn->service_name);
1273 jucn->service_name = talloc_strdup(ctx, service_name);
1274 if (!jucn->service_name) {
1275 TALLOC_FREE(frame);
1276 return NT_STATUS_NO_MEMORY;
1280 if (!lp_msdfs_root(snum) && (*lp_msdfs_proxy(talloc_tos(), lp_sub, snum) == '\0')) {
1281 DEBUG(3,("get_referred_path: |%s| in dfs path %s is not "
1282 "a dfs root.\n",
1283 servicename, dfs_path));
1284 TALLOC_FREE(frame);
1285 return NT_STATUS_NOT_FOUND;
1289 * Self referrals are tested with a anonymous IPC connection and
1290 * a GET_DFS_REFERRAL call to \\server\share. (which means
1291 * dp.reqpath[0] points to an empty string). create_conn_struct cd's
1292 * into the directory and will fail if it cannot (as the anonymous
1293 * user). Cope with this.
1296 if (reqpath[0] == '\0') {
1297 char *tmp;
1298 struct referral *ref;
1299 size_t refcount;
1301 if (*lp_msdfs_proxy(talloc_tos(), lp_sub, snum) == '\0') {
1302 TALLOC_FREE(frame);
1303 return self_ref(ctx,
1304 dfs_path,
1305 jucn,
1306 consumedcntp,
1307 self_referralp);
1311 * It's an msdfs proxy share. Redirect to
1312 * the configured target share.
1315 tmp = talloc_asprintf(frame, "msdfs:%s",
1316 lp_msdfs_proxy(frame, lp_sub, snum));
1317 if (tmp == NULL) {
1318 TALLOC_FREE(frame);
1319 return NT_STATUS_NO_MEMORY;
1322 if (!parse_msdfs_symlink(ctx,
1323 lp_msdfs_shuffle_referrals(snum),
1324 tmp,
1325 &ref,
1326 &refcount)) {
1327 TALLOC_FREE(frame);
1328 return NT_STATUS_INVALID_PARAMETER;
1330 jucn->referral_count = refcount;
1331 jucn->referral_list = ref;
1332 *consumedcntp = strlen(dfs_path);
1333 TALLOC_FREE(frame);
1334 return NT_STATUS_OK;
1337 status = create_conn_struct_tos_cwd(global_messaging_context(),
1338 snum,
1339 lp_path(frame, lp_sub, snum),
1340 session_info,
1341 &c);
1342 if (!NT_STATUS_IS_OK(status)) {
1343 TALLOC_FREE(frame);
1344 return status;
1346 conn = c->conn;
1349 * TODO
1351 * The remote and local address should be passed down to
1352 * create_conn_struct_cwd.
1354 if (conn->sconn->remote_address == NULL) {
1355 conn->sconn->remote_address =
1356 tsocket_address_copy(remote_address, conn->sconn);
1357 if (conn->sconn->remote_address == NULL) {
1358 TALLOC_FREE(frame);
1359 return NT_STATUS_NO_MEMORY;
1362 if (conn->sconn->local_address == NULL) {
1363 conn->sconn->local_address =
1364 tsocket_address_copy(local_address, conn->sconn);
1365 if (conn->sconn->local_address == NULL) {
1366 TALLOC_FREE(frame);
1367 return NT_STATUS_NO_MEMORY;
1371 status = dfs_path_lookup(ctx,
1372 conn,
1373 dfs_path,
1374 reqpath,
1375 0, /* ucf_flags */
1376 consumedcntp,
1377 &jucn->referral_list,
1378 &jucn->referral_count);
1380 if (!NT_STATUS_IS_OK(status)) {
1381 DBG_NOTICE("No valid referrals for path %s (%s)\n",
1382 dfs_path,
1383 nt_errstr(status));
1386 TALLOC_FREE(frame);
1387 return status;
1390 /******************************************************************
1391 Set up the DFS referral for the dfs pathname. This call returns
1392 the amount of the path covered by this server, and where the
1393 client should be redirected to. This is the meat of the
1394 TRANS2_GET_DFS_REFERRAL call.
1395 ******************************************************************/
1397 int setup_dfs_referral(connection_struct *orig_conn,
1398 const char *dfs_path,
1399 int max_referral_level,
1400 char **ppdata, NTSTATUS *pstatus)
1402 char *pdata = *ppdata;
1403 int reply_size = 0;
1404 struct dfs_GetDFSReferral *r;
1405 DATA_BLOB blob = data_blob_null;
1406 NTSTATUS status;
1407 enum ndr_err_code ndr_err;
1409 r = talloc_zero(talloc_tos(), struct dfs_GetDFSReferral);
1410 if (r == NULL) {
1411 *pstatus = NT_STATUS_NO_MEMORY;
1412 return -1;
1415 r->in.req.max_referral_level = max_referral_level;
1416 r->in.req.servername = talloc_strdup(r, dfs_path);
1417 if (r->in.req.servername == NULL) {
1418 talloc_free(r);
1419 *pstatus = NT_STATUS_NO_MEMORY;
1420 return -1;
1423 status = SMB_VFS_GET_DFS_REFERRALS(orig_conn, r);
1424 if (!NT_STATUS_IS_OK(status)) {
1425 talloc_free(r);
1426 *pstatus = status;
1427 return -1;
1430 ndr_err = ndr_push_struct_blob(&blob, r,
1431 r->out.resp,
1432 (ndr_push_flags_fn_t)ndr_push_dfs_referral_resp);
1433 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1434 TALLOC_FREE(r);
1435 *pstatus = NT_STATUS_INVALID_PARAMETER;
1436 return -1;
1439 pdata = (char *)SMB_REALLOC(pdata, blob.length);
1440 if(pdata == NULL) {
1441 TALLOC_FREE(r);
1442 DEBUG(0,("referral setup:"
1443 "malloc failed for Realloc!\n"));
1444 return -1;
1446 *ppdata = pdata;
1447 reply_size = blob.length;
1448 memcpy(pdata, blob.data, blob.length);
1449 TALLOC_FREE(r);
1451 *pstatus = NT_STATUS_OK;
1452 return reply_size;
1455 /**********************************************************************
1456 The following functions are called by the NETDFS RPC pipe functions
1457 **********************************************************************/
1459 /*********************************************************************
1460 Creates a junction structure from a DFS pathname
1461 **********************************************************************/
1463 bool create_junction(TALLOC_CTX *ctx,
1464 const char *dfs_path,
1465 struct junction_map *jucn)
1467 const struct loadparm_substitution *lp_sub =
1468 loadparm_s3_global_substitution();
1469 int snum;
1470 char *servicename = NULL;
1471 char *reqpath = NULL;
1472 NTSTATUS status;
1474 status = parse_dfs_path_strict(
1475 ctx,
1476 dfs_path,
1477 NULL,
1478 &servicename,
1479 &reqpath);
1480 if (!NT_STATUS_IS_OK(status)) {
1481 return False;
1484 /* Check for a non-DFS share */
1485 snum = lp_servicenumber(servicename);
1487 if(snum < 0 || !lp_msdfs_root(snum)) {
1488 DEBUG(4,("create_junction: %s is not an msdfs root.\n",
1489 servicename));
1490 return False;
1493 /* Junction create paths are always non-POSIX. */
1494 status = check_path_syntax(reqpath);
1495 if (!NT_STATUS_IS_OK(status)) {
1496 return false;
1499 jucn->service_name = talloc_strdup(ctx, servicename);
1500 jucn->volume_name = talloc_strdup(ctx, reqpath);
1501 jucn->comment = lp_comment(ctx, lp_sub, snum);
1503 if (!jucn->service_name || !jucn->volume_name || ! jucn->comment) {
1504 return False;
1506 return True;
1509 /**********************************************************************
1510 Forms a valid Unix pathname from the junction
1511 **********************************************************************/
1513 static bool junction_to_local_path_tos(const struct junction_map *jucn,
1514 struct auth_session_info *session_info,
1515 char **pp_path_out,
1516 connection_struct **conn_out)
1518 const struct loadparm_substitution *lp_sub =
1519 loadparm_s3_global_substitution();
1520 struct conn_struct_tos *c = NULL;
1521 int snum;
1522 char *path_out = NULL;
1523 NTSTATUS status;
1525 snum = lp_servicenumber(jucn->service_name);
1526 if(snum < 0) {
1527 return False;
1529 status = create_conn_struct_tos_cwd(global_messaging_context(),
1530 snum,
1531 lp_path(talloc_tos(), lp_sub, snum),
1532 session_info,
1533 &c);
1534 if (!NT_STATUS_IS_OK(status)) {
1535 return False;
1538 path_out = talloc_asprintf(c,
1539 "%s/%s",
1540 lp_path(talloc_tos(), lp_sub, snum),
1541 jucn->volume_name);
1542 if (path_out == NULL) {
1543 TALLOC_FREE(c);
1544 return False;
1546 *pp_path_out = path_out;
1547 *conn_out = c->conn;
1548 return True;
1552 * Create a msdfs string in Samba format we can store
1553 * in a filesystem object (currently a symlink).
1556 char *msdfs_link_string(TALLOC_CTX *ctx,
1557 const struct referral *reflist,
1558 size_t referral_count)
1560 char *refpath = NULL;
1561 bool insert_comma = false;
1562 char *msdfs_link = NULL;
1563 size_t i;
1565 /* Form the msdfs_link contents */
1566 msdfs_link = talloc_strdup(ctx, "msdfs:");
1567 if (msdfs_link == NULL) {
1568 goto err;
1571 for( i= 0; i < referral_count; i++) {
1572 refpath = talloc_strdup(ctx, reflist[i].alternate_path);
1574 if (refpath == NULL) {
1575 goto err;
1578 /* Alternate paths always use Windows separators. */
1579 trim_char(refpath, '\\', '\\');
1580 if (*refpath == '\0') {
1581 if (i == 0) {
1582 insert_comma = false;
1584 continue;
1586 if (i > 0 && insert_comma) {
1587 msdfs_link = talloc_asprintf_append_buffer(msdfs_link,
1588 ",%s",
1589 refpath);
1590 } else {
1591 msdfs_link = talloc_asprintf_append_buffer(msdfs_link,
1592 "%s",
1593 refpath);
1596 if (msdfs_link == NULL) {
1597 goto err;
1600 if (!insert_comma) {
1601 insert_comma = true;
1604 TALLOC_FREE(refpath);
1607 return msdfs_link;
1609 err:
1611 TALLOC_FREE(refpath);
1612 TALLOC_FREE(msdfs_link);
1613 return NULL;
1616 bool create_msdfs_link(const struct junction_map *jucn,
1617 struct auth_session_info *session_info)
1619 TALLOC_CTX *frame = talloc_stackframe();
1620 char *path = NULL;
1621 connection_struct *conn;
1622 struct smb_filename *smb_fname = NULL;
1623 struct smb_filename *parent_fname = NULL;
1624 struct smb_filename *at_fname = NULL;
1625 bool ok;
1626 NTSTATUS status;
1627 bool ret = false;
1629 ok = junction_to_local_path_tos(jucn, session_info, &path, &conn);
1630 if (!ok) {
1631 goto out;
1634 if (!CAN_WRITE(conn)) {
1635 const struct loadparm_substitution *lp_sub =
1636 loadparm_s3_global_substitution();
1637 int snum = lp_servicenumber(jucn->service_name);
1639 DBG_WARNING("Can't create DFS entry on read-only share %s\n",
1640 lp_servicename(frame, lp_sub, snum));
1641 goto out;
1644 smb_fname = synthetic_smb_fname(frame,
1645 path,
1646 NULL,
1647 NULL,
1650 if (smb_fname == NULL) {
1651 goto out;
1654 status = parent_pathref(frame,
1655 conn->cwd_fsp,
1656 smb_fname,
1657 &parent_fname,
1658 &at_fname);
1659 if (!NT_STATUS_IS_OK(status)) {
1660 goto out;
1663 status = SMB_VFS_CREATE_DFS_PATHAT(conn,
1664 parent_fname->fsp,
1665 at_fname,
1666 jucn->referral_list,
1667 jucn->referral_count);
1668 if (!NT_STATUS_IS_OK(status)) {
1669 if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_COLLISION)) {
1670 int retval = SMB_VFS_UNLINKAT(conn,
1671 parent_fname->fsp,
1672 at_fname,
1674 if (retval != 0) {
1675 goto out;
1678 status = SMB_VFS_CREATE_DFS_PATHAT(conn,
1679 parent_fname->fsp,
1680 at_fname,
1681 jucn->referral_list,
1682 jucn->referral_count);
1683 if (!NT_STATUS_IS_OK(status)) {
1684 DBG_WARNING("SMB_VFS_CREATE_DFS_PATHAT failed "
1685 "%s - Error: %s\n",
1686 path,
1687 nt_errstr(status));
1688 goto out;
1692 ret = true;
1694 out:
1695 TALLOC_FREE(frame);
1696 return ret;
1699 bool remove_msdfs_link(const struct junction_map *jucn,
1700 struct auth_session_info *session_info)
1702 TALLOC_CTX *frame = talloc_stackframe();
1703 char *path = NULL;
1704 connection_struct *conn;
1705 bool ret = False;
1706 struct smb_filename *smb_fname;
1707 struct smb_filename *parent_fname = NULL;
1708 struct smb_filename *at_fname = NULL;
1709 NTSTATUS status;
1710 bool ok;
1711 int retval;
1713 ok = junction_to_local_path_tos(jucn, session_info, &path, &conn);
1714 if (!ok) {
1715 TALLOC_FREE(frame);
1716 return false;
1719 if (!CAN_WRITE(conn)) {
1720 const struct loadparm_substitution *lp_sub =
1721 loadparm_s3_global_substitution();
1722 int snum = lp_servicenumber(jucn->service_name);
1724 DBG_WARNING("Can't remove DFS entry on read-only share %s\n",
1725 lp_servicename(frame, lp_sub, snum));
1726 TALLOC_FREE(frame);
1727 return false;
1730 smb_fname = synthetic_smb_fname(frame,
1731 path,
1732 NULL,
1733 NULL,
1736 if (smb_fname == NULL) {
1737 TALLOC_FREE(frame);
1738 errno = ENOMEM;
1739 return false;
1742 status = parent_pathref(frame,
1743 conn->cwd_fsp,
1744 smb_fname,
1745 &parent_fname,
1746 &at_fname);
1747 if (!NT_STATUS_IS_OK(status)) {
1748 TALLOC_FREE(frame);
1749 return false;
1752 retval = SMB_VFS_UNLINKAT(conn,
1753 parent_fname->fsp,
1754 at_fname,
1756 if (retval == 0) {
1757 ret = True;
1760 TALLOC_FREE(frame);
1761 return ret;
1764 /*********************************************************************
1765 Return the number of DFS links at the root of this share.
1766 *********************************************************************/
1768 static size_t count_dfs_links(TALLOC_CTX *ctx,
1769 struct auth_session_info *session_info,
1770 int snum)
1772 TALLOC_CTX *frame = talloc_stackframe();
1773 const struct loadparm_substitution *lp_sub =
1774 loadparm_s3_global_substitution();
1775 size_t cnt = 0;
1776 const char *dname = NULL;
1777 char *talloced = NULL;
1778 const char *connect_path = lp_path(frame, lp_sub, snum);
1779 const char *msdfs_proxy = lp_msdfs_proxy(frame, lp_sub, snum);
1780 struct conn_struct_tos *c = NULL;
1781 connection_struct *conn = NULL;
1782 NTSTATUS status;
1783 struct smb_filename *smb_fname = NULL;
1784 struct smb_Dir *dir_hnd = NULL;
1785 long offset = 0;
1787 if(*connect_path == '\0') {
1788 TALLOC_FREE(frame);
1789 return 0;
1793 * Fake up a connection struct for the VFS layer.
1796 status = create_conn_struct_tos_cwd(global_messaging_context(),
1797 snum,
1798 connect_path,
1799 session_info,
1800 &c);
1801 if (!NT_STATUS_IS_OK(status)) {
1802 DEBUG(3, ("create_conn_struct failed: %s\n",
1803 nt_errstr(status)));
1804 TALLOC_FREE(frame);
1805 return 0;
1807 conn = c->conn;
1809 /* Count a link for the msdfs root - convention */
1810 cnt = 1;
1812 /* No more links if this is an msdfs proxy. */
1813 if (*msdfs_proxy != '\0') {
1814 goto out;
1817 smb_fname = synthetic_smb_fname(frame,
1818 ".",
1819 NULL,
1820 NULL,
1823 if (smb_fname == NULL) {
1824 goto out;
1827 /* Now enumerate all dfs links */
1828 status = OpenDir(frame,
1829 conn,
1830 smb_fname,
1831 NULL,
1833 &dir_hnd);
1834 if (!NT_STATUS_IS_OK(status)) {
1835 errno = map_errno_from_nt_status(status);
1836 goto out;
1839 while ((dname = ReadDirName(dir_hnd, &offset, NULL, &talloced))
1840 != NULL)
1842 struct smb_filename *smb_dname =
1843 synthetic_smb_fname(frame,
1844 dname,
1845 NULL,
1846 NULL,
1849 if (smb_dname == NULL) {
1850 goto out;
1852 if (is_msdfs_link(dir_hnd_fetch_fsp(dir_hnd), smb_dname)) {
1853 if (cnt + 1 < cnt) {
1854 cnt = 0;
1855 goto out;
1857 cnt++;
1859 TALLOC_FREE(talloced);
1860 TALLOC_FREE(smb_dname);
1863 out:
1864 TALLOC_FREE(frame);
1865 return cnt;
1868 /*********************************************************************
1869 *********************************************************************/
1871 static int form_junctions(TALLOC_CTX *ctx,
1872 struct auth_session_info *session_info,
1873 int snum,
1874 struct junction_map *jucn,
1875 size_t jn_remain)
1877 TALLOC_CTX *frame = talloc_stackframe();
1878 const struct loadparm_substitution *lp_sub =
1879 loadparm_s3_global_substitution();
1880 size_t cnt = 0;
1881 const char *dname = NULL;
1882 char *talloced = NULL;
1883 const char *connect_path = lp_path(frame, lp_sub, snum);
1884 char *service_name = lp_servicename(frame, lp_sub, snum);
1885 const char *msdfs_proxy = lp_msdfs_proxy(frame, lp_sub, snum);
1886 struct conn_struct_tos *c = NULL;
1887 connection_struct *conn = NULL;
1888 struct referral *ref = NULL;
1889 struct smb_filename *smb_fname = NULL;
1890 struct smb_Dir *dir_hnd = NULL;
1891 long offset = 0;
1892 NTSTATUS status;
1894 if (jn_remain == 0) {
1895 TALLOC_FREE(frame);
1896 return 0;
1899 if(*connect_path == '\0') {
1900 TALLOC_FREE(frame);
1901 return 0;
1905 * Fake up a connection struct for the VFS layer.
1908 status = create_conn_struct_tos_cwd(global_messaging_context(),
1909 snum,
1910 connect_path,
1911 session_info,
1912 &c);
1913 if (!NT_STATUS_IS_OK(status)) {
1914 DEBUG(3, ("create_conn_struct failed: %s\n",
1915 nt_errstr(status)));
1916 TALLOC_FREE(frame);
1917 return 0;
1919 conn = c->conn;
1921 /* form a junction for the msdfs root - convention
1922 DO NOT REMOVE THIS: NT clients will not work with us
1923 if this is not present
1925 jucn[cnt].service_name = talloc_strdup(ctx,service_name);
1926 jucn[cnt].volume_name = talloc_strdup(ctx, "");
1927 if (!jucn[cnt].service_name || !jucn[cnt].volume_name) {
1928 goto out;
1930 jucn[cnt].comment = "";
1931 jucn[cnt].referral_count = 1;
1933 ref = jucn[cnt].referral_list = talloc_zero(ctx, struct referral);
1934 if (jucn[cnt].referral_list == NULL) {
1935 goto out;
1938 ref->proximity = 0;
1939 ref->ttl = REFERRAL_TTL;
1940 if (*msdfs_proxy != '\0') {
1941 ref->alternate_path = talloc_strdup(ctx,
1942 msdfs_proxy);
1943 } else {
1944 ref->alternate_path = talloc_asprintf(ctx,
1945 "\\\\%s\\%s",
1946 get_local_machine_name(),
1947 service_name);
1950 if (!ref->alternate_path) {
1951 goto out;
1953 cnt++;
1955 /* Don't enumerate if we're an msdfs proxy. */
1956 if (*msdfs_proxy != '\0') {
1957 goto out;
1960 smb_fname = synthetic_smb_fname(frame,
1961 ".",
1962 NULL,
1963 NULL,
1966 if (smb_fname == NULL) {
1967 goto out;
1970 /* Now enumerate all dfs links */
1971 status = OpenDir(frame,
1972 conn,
1973 smb_fname,
1974 NULL,
1976 &dir_hnd);
1977 if (!NT_STATUS_IS_OK(status)) {
1978 errno = map_errno_from_nt_status(status);
1979 goto out;
1982 while ((dname = ReadDirName(dir_hnd, &offset, NULL, &talloced))
1983 != NULL)
1985 struct smb_filename *smb_dname = NULL;
1987 if (cnt >= jn_remain) {
1988 DEBUG(2, ("form_junctions: ran out of MSDFS "
1989 "junction slots"));
1990 TALLOC_FREE(talloced);
1991 goto out;
1993 smb_dname = synthetic_smb_fname(talloc_tos(),
1994 dname,
1995 NULL,
1996 NULL,
1999 if (smb_dname == NULL) {
2000 TALLOC_FREE(talloced);
2001 goto out;
2004 status = SMB_VFS_READ_DFS_PATHAT(conn,
2005 ctx,
2006 conn->cwd_fsp,
2007 smb_dname,
2008 &jucn[cnt].referral_list,
2009 &jucn[cnt].referral_count);
2011 if (NT_STATUS_IS_OK(status)) {
2012 jucn[cnt].service_name = talloc_strdup(ctx,
2013 service_name);
2014 jucn[cnt].volume_name = talloc_strdup(ctx, dname);
2015 if (!jucn[cnt].service_name || !jucn[cnt].volume_name) {
2016 TALLOC_FREE(talloced);
2017 goto out;
2019 jucn[cnt].comment = "";
2020 cnt++;
2022 TALLOC_FREE(talloced);
2023 TALLOC_FREE(smb_dname);
2026 out:
2027 TALLOC_FREE(frame);
2028 return cnt;
2031 struct junction_map *enum_msdfs_links(TALLOC_CTX *ctx,
2032 struct auth_session_info *session_info,
2033 size_t *p_num_jn)
2035 struct junction_map *jn = NULL;
2036 int i=0;
2037 size_t jn_count = 0;
2038 int sharecount = 0;
2040 *p_num_jn = 0;
2041 if(!lp_host_msdfs()) {
2042 return NULL;
2045 /* Ensure all the usershares are loaded. */
2046 become_root();
2047 load_registry_shares();
2048 sharecount = load_usershare_shares(NULL, connections_snum_used);
2049 unbecome_root();
2051 for(i=0;i < sharecount;i++) {
2052 if(lp_msdfs_root(i)) {
2053 jn_count += count_dfs_links(ctx, session_info, i);
2056 if (jn_count == 0) {
2057 return NULL;
2059 jn = talloc_array(ctx, struct junction_map, jn_count);
2060 if (!jn) {
2061 return NULL;
2063 for(i=0; i < sharecount; i++) {
2064 if (*p_num_jn >= jn_count) {
2065 break;
2067 if(lp_msdfs_root(i)) {
2068 *p_num_jn += form_junctions(ctx,
2069 session_info,
2071 &jn[*p_num_jn],
2072 jn_count - *p_num_jn);
2075 return jn;