s3: smbd: Remove now unused dfs_filename_convert().
[Samba.git] / source3 / smbd / msdfs.c
bloba0b59da1fcf4fbfd832c2b6db8d7ddd8c1025230
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 Parse a DFS pathname of the form(s)
42 \hostname\service - self referral
43 \hostname\service\remainingpath - Windows referral path
45 FIXME! Should we also parse:
46 \hostname\service/remainingpath - POSIX referral path
47 as currently nothing uses this ?
49 into the dfs_path components. Strict form.
51 Checks DFS path starts with separator.
52 Checks hostname is ours.
53 Ensures servicename (share) is sent, and
54 if so, terminates the name or is followed by
55 \pathname.
57 If returned, remainingpath is untouched. Caller must call
58 check_path_syntaxXXX() on it.
60 Called by all non-fileserver processing (DFS RPC, FSCTL_DFS_GET_REFERRALS)
61 etc. Errors out on any inconsistency in the path.
62 **********************************************************************/
64 static NTSTATUS parse_dfs_path_strict(TALLOC_CTX *ctx,
65 const char *pathname,
66 char **_hostname,
67 char **_servicename,
68 char **_remaining_path)
70 char *pathname_local = NULL;
71 char *p = NULL;
72 const char *hostname = NULL;
73 const char *servicename = NULL;
74 const char *reqpath = NULL;
75 bool my_hostname = false;
76 NTSTATUS status;
78 DBG_DEBUG("path = |%s|\n", pathname);
80 pathname_local = talloc_strdup(talloc_tos(), pathname);
81 if (pathname_local == NULL) {
82 return NT_STATUS_NO_MEMORY;
85 * parse_dfs_path_strict() is called from
86 * get_referred_path() and create_junction()
87 * which use Windows DFS paths of \server\share.
91 * Strict DFS paths *must* start with the
92 * path separator '\\'.
95 if (pathname_local[0] != '\\') {
96 DBG_ERR("path %s doesn't start with \\\n",
97 pathname_local);
98 status = NT_STATUS_NOT_FOUND;
99 goto out;
102 /* Now tokenize. */
103 /* Parse out hostname. */
104 p = strchr(pathname_local + 1, '\\');
105 if (p == NULL) {
106 DBG_ERR("can't parse hostname from path %s\n",
107 pathname_local);
108 status = NT_STATUS_NOT_FOUND;
109 goto out;
111 *p = '\0';
112 hostname = &pathname_local[1];
114 DBG_DEBUG("hostname: %s\n", hostname);
116 /* Is this really our hostname ? */
117 my_hostname = is_myname_or_ipaddr(hostname);
118 if (!my_hostname) {
119 DBG_ERR("Hostname %s is not ours.\n",
120 hostname);
121 status = NT_STATUS_NOT_FOUND;
122 goto out;
125 servicename = p + 1;
128 * Find the end of servicename by looking for
129 * a directory separator character. The character
130 * should be '\\' for a Windows path.
131 * If there is no separator, then this is a self-referral
132 * of "\server\share".
135 p = strchr(servicename, '\\');
136 if (p != NULL) {
137 *p = '\0';
140 DBG_DEBUG("servicename: %s\n", servicename);
142 if (p == NULL) {
143 /* Client sent self referral "\server\share". */
144 reqpath = "";
145 } else {
146 /* Step past the '\0' we just replaced '\\' with. */
147 reqpath = p + 1;
150 DBG_DEBUG("rest of the path: %s\n", reqpath);
152 if (_hostname != NULL) {
153 *_hostname = talloc_strdup(ctx, hostname);
154 if (*_hostname == NULL) {
155 status = NT_STATUS_NO_MEMORY;
156 goto out;
159 if (_servicename != NULL) {
160 *_servicename = talloc_strdup(ctx, servicename);
161 if (*_servicename == NULL) {
162 status = NT_STATUS_NO_MEMORY;
163 goto out;
166 if (_remaining_path != NULL) {
167 *_remaining_path = talloc_strdup(ctx, reqpath);
168 if (*_remaining_path == NULL) {
169 status = NT_STATUS_NO_MEMORY;
170 goto out;
174 status = NT_STATUS_OK;
175 out:
176 TALLOC_FREE(pathname_local);
177 return status;
180 /********************************************************
181 Fake up a connection struct for the VFS layer, for use in
182 applications (such as the python bindings), that do not want the
183 global working directory changed under them.
185 SMB_VFS_CONNECT requires root privileges.
186 *********************************************************/
188 static NTSTATUS create_conn_struct_as_root(TALLOC_CTX *ctx,
189 struct tevent_context *ev,
190 struct messaging_context *msg,
191 connection_struct **pconn,
192 int snum,
193 const char *path,
194 const struct auth_session_info *session_info)
196 connection_struct *conn;
197 char *connpath;
198 const char *vfs_user;
199 struct smbd_server_connection *sconn;
200 const char *servicename = lp_const_servicename(snum);
201 bool ok;
203 sconn = talloc_zero(ctx, struct smbd_server_connection);
204 if (sconn == NULL) {
205 return NT_STATUS_NO_MEMORY;
208 sconn->ev_ctx = ev;
209 sconn->msg_ctx = msg;
211 conn = conn_new(sconn);
212 if (conn == NULL) {
213 TALLOC_FREE(sconn);
214 return NT_STATUS_NO_MEMORY;
217 /* Now we have conn, we need to make sconn a child of conn,
218 * for a proper talloc tree */
219 talloc_steal(conn, sconn);
221 if (snum == -1 && servicename == NULL) {
222 servicename = "Unknown Service (snum == -1)";
225 connpath = talloc_strdup(conn, path);
226 if (!connpath) {
227 TALLOC_FREE(conn);
228 return NT_STATUS_NO_MEMORY;
230 connpath = talloc_string_sub(conn,
231 connpath,
232 "%S",
233 servicename);
234 if (!connpath) {
235 TALLOC_FREE(conn);
236 return NT_STATUS_NO_MEMORY;
239 /* needed for smbd_vfs_init() */
241 conn->params->service = snum;
242 conn->cnum = TID_FIELD_INVALID;
244 SMB_ASSERT(session_info != NULL);
246 conn->session_info = copy_session_info(conn, session_info);
247 if (conn->session_info == NULL) {
248 DBG_ERR("copy_serverinfo failed\n");
249 TALLOC_FREE(conn);
250 return NT_STATUS_NO_MEMORY;
253 /* unix_info could be NULL in session_info */
254 if (conn->session_info->unix_info != NULL) {
255 vfs_user = conn->session_info->unix_info->unix_name;
256 } else {
257 vfs_user = get_current_username();
260 conn_setup_case_options(conn);
262 set_conn_connectpath(conn, connpath);
265 * New code to check if there's a share security descriptor
266 * added from NT server manager. This is done after the
267 * smb.conf checks are done as we need a uid and token. JRA.
270 share_access_check(conn->session_info->security_token,
271 servicename,
272 MAXIMUM_ALLOWED_ACCESS,
273 &conn->share_access);
275 if ((conn->share_access & FILE_WRITE_DATA) == 0) {
276 if ((conn->share_access & FILE_READ_DATA) == 0) {
277 /* No access, read or write. */
278 DBG_WARNING("connection to %s "
279 "denied due to security "
280 "descriptor.\n",
281 servicename);
282 conn_free(conn);
283 return NT_STATUS_ACCESS_DENIED;
285 conn->read_only = true;
288 if (!smbd_vfs_init(conn)) {
289 NTSTATUS status = map_nt_error_from_unix(errno);
290 DEBUG(0,("create_conn_struct: smbd_vfs_init failed.\n"));
291 conn_free(conn);
292 return status;
295 /* this must be the first filesystem operation that we do */
296 if (SMB_VFS_CONNECT(conn, servicename, vfs_user) < 0) {
297 DEBUG(0,("VFS connect failed!\n"));
298 conn_free(conn);
299 return NT_STATUS_UNSUCCESSFUL;
302 ok = canonicalize_connect_path(conn);
303 if (!ok) {
304 DBG_ERR("Failed to canonicalize sharepath\n");
305 conn_free(conn);
306 return NT_STATUS_ACCESS_DENIED;
309 conn->fs_capabilities = SMB_VFS_FS_CAPABILITIES(conn, &conn->ts_res);
310 conn->tcon_done = true;
311 *pconn = talloc_move(ctx, &conn);
313 return NT_STATUS_OK;
316 static int conn_struct_tos_destructor(struct conn_struct_tos *c)
318 if (c->oldcwd_fname != NULL) {
319 vfs_ChDir(c->conn, c->oldcwd_fname);
320 TALLOC_FREE(c->oldcwd_fname);
322 SMB_VFS_DISCONNECT(c->conn);
323 conn_free(c->conn);
324 return 0;
327 /********************************************************
328 Fake up a connection struct for the VFS layer, for use in
329 applications (such as the python bindings), that do not want the
330 global working directory changed under them.
332 SMB_VFS_CONNECT requires root privileges.
333 This temporary uses become_root() and unbecome_root().
335 But further impersonation has to be cone by the caller.
336 *********************************************************/
337 NTSTATUS create_conn_struct_tos(struct messaging_context *msg,
338 int snum,
339 const char *path,
340 const struct auth_session_info *session_info,
341 struct conn_struct_tos **_c)
343 struct conn_struct_tos *c = NULL;
344 struct tevent_context *ev = NULL;
345 NTSTATUS status;
347 *_c = NULL;
349 c = talloc_zero(talloc_tos(), struct conn_struct_tos);
350 if (c == NULL) {
351 return NT_STATUS_NO_MEMORY;
354 ev = samba_tevent_context_init(c);
355 if (ev == NULL) {
356 TALLOC_FREE(c);
357 return NT_STATUS_NO_MEMORY;
360 become_root();
361 status = create_conn_struct_as_root(c,
363 msg,
364 &c->conn,
365 snum,
366 path,
367 session_info);
368 unbecome_root();
369 if (!NT_STATUS_IS_OK(status)) {
370 TALLOC_FREE(c);
371 return status;
374 talloc_set_destructor(c, conn_struct_tos_destructor);
376 *_c = c;
377 return NT_STATUS_OK;
380 /********************************************************
381 Fake up a connection struct for the VFS layer.
382 Note: this performs a vfs connect and CHANGES CWD !!!! JRA.
384 See also the comment for create_conn_struct_tos() above!
386 The CWD change is reverted by the destructor of
387 conn_struct_tos when the current talloc_tos() is destroyed.
388 *********************************************************/
389 NTSTATUS create_conn_struct_tos_cwd(struct messaging_context *msg,
390 int snum,
391 const char *path,
392 const struct auth_session_info *session_info,
393 struct conn_struct_tos **_c)
395 struct conn_struct_tos *c = NULL;
396 struct smb_filename smb_fname_connectpath = {0};
397 NTSTATUS status;
399 *_c = NULL;
401 status = create_conn_struct_tos(msg,
402 snum,
403 path,
404 session_info,
405 &c);
406 if (!NT_STATUS_IS_OK(status)) {
407 return status;
411 * Windows seems to insist on doing trans2getdfsreferral() calls on
412 * the IPC$ share as the anonymous user. If we try to chdir as that
413 * user we will fail.... WTF ? JRA.
416 c->oldcwd_fname = vfs_GetWd(c, c->conn);
417 if (c->oldcwd_fname == NULL) {
418 status = map_nt_error_from_unix(errno);
419 DEBUG(3, ("vfs_GetWd failed: %s\n", strerror(errno)));
420 TALLOC_FREE(c);
421 return status;
424 smb_fname_connectpath = (struct smb_filename) {
425 .base_name = c->conn->connectpath
428 if (vfs_ChDir(c->conn, &smb_fname_connectpath) != 0) {
429 status = map_nt_error_from_unix(errno);
430 DBG_NOTICE("Can't ChDir to new conn path %s. "
431 "Error was %s\n",
432 c->conn->connectpath, strerror(errno));
433 TALLOC_FREE(c->oldcwd_fname);
434 TALLOC_FREE(c);
435 return status;
438 *_c = c;
439 return NT_STATUS_OK;
442 /********************************************************
443 Fake up a connection struct for the VFS layer.
444 This takes an TALLOC_CTX and tevent_context from the
445 caller and the resulting connection_struct is stable
446 across the lifetime of mem_ctx and ev.
448 Note: this performs a vfs connect and changes cwd.
450 See also the comment for create_conn_struct_tos() above!
451 *********************************************************/
453 NTSTATUS create_conn_struct_cwd(TALLOC_CTX *mem_ctx,
454 struct tevent_context *ev,
455 struct messaging_context *msg,
456 const struct auth_session_info *session_info,
457 int snum,
458 const char *path,
459 struct connection_struct **c)
461 NTSTATUS status;
463 become_root();
464 status = create_conn_struct_as_root(mem_ctx,
466 msg,
468 snum,
469 path,
470 session_info);
471 unbecome_root();
472 return status;
475 static void shuffle_strlist(char **list, int count)
477 int i;
478 uint32_t r;
479 char *tmp;
481 for (i = count; i > 1; i--) {
482 r = generate_random() % i;
484 tmp = list[i-1];
485 list[i-1] = list[r];
486 list[r] = tmp;
490 /**********************************************************************
491 Parse the contents of a symlink to verify if it is an msdfs referral
492 A valid referral is of the form:
494 msdfs:server1\share1,server2\share2
495 msdfs:server1\share1\pathname,server2\share2\pathname
496 msdfs:server1/share1,server2/share2
497 msdfs:server1/share1/pathname,server2/share2/pathname.
499 Note that the alternate paths returned here must be of the canonicalized
500 form:
502 \server\share or
503 \server\share\path\to\file,
505 even in posix path mode. This is because we have no knowledge if the
506 server we're referring to understands posix paths.
507 **********************************************************************/
509 bool parse_msdfs_symlink(TALLOC_CTX *ctx,
510 bool shuffle_referrals,
511 const char *target,
512 struct referral **ppreflist,
513 size_t *prefcount)
515 char *temp = NULL;
516 char *prot;
517 char **alt_path = NULL;
518 size_t count = 0, i;
519 struct referral *reflist = NULL;
520 char *saveptr;
522 temp = talloc_strdup(ctx, target);
523 if (!temp) {
524 return false;
526 prot = strtok_r(temp, ":", &saveptr);
527 if (!prot) {
528 DEBUG(0,("parse_msdfs_symlink: invalid path !\n"));
529 TALLOC_FREE(temp);
530 return false;
533 alt_path = talloc_array(ctx, char *, MAX_REFERRAL_COUNT);
534 if (!alt_path) {
535 TALLOC_FREE(temp);
536 return false;
539 /* parse out the alternate paths */
540 while((count<MAX_REFERRAL_COUNT) &&
541 ((alt_path[count] = strtok_r(NULL, ",", &saveptr)) != NULL)) {
542 count++;
545 /* shuffle alternate paths */
546 if (shuffle_referrals) {
547 shuffle_strlist(alt_path, count);
550 DBG_DEBUG("count=%zu\n", count);
552 if (count) {
553 reflist = talloc_zero_array(ctx,
554 struct referral, count);
555 if(reflist == NULL) {
556 TALLOC_FREE(temp);
557 TALLOC_FREE(alt_path);
558 return false;
560 } else {
561 reflist = NULL;
564 for(i=0;i<count;i++) {
565 char *p;
567 /* Canonicalize link target.
568 * Replace all /'s in the path by a \ */
569 string_replace(alt_path[i], '/', '\\');
571 /* Remove leading '\\'s */
572 p = alt_path[i];
573 while (*p && (*p == '\\')) {
574 p++;
577 reflist[i].alternate_path = talloc_asprintf(reflist,
578 "\\%s",
580 if (!reflist[i].alternate_path) {
581 TALLOC_FREE(temp);
582 TALLOC_FREE(alt_path);
583 TALLOC_FREE(reflist);
584 return false;
587 reflist[i].proximity = 0;
588 reflist[i].ttl = REFERRAL_TTL;
589 DBG_DEBUG("Created alt path: %s\n",
590 reflist[i].alternate_path);
593 if (ppreflist != NULL) {
594 *ppreflist = reflist;
595 } else {
596 TALLOC_FREE(reflist);
598 if (prefcount != NULL) {
599 *prefcount = count;
601 TALLOC_FREE(temp);
602 TALLOC_FREE(alt_path);
603 return true;
606 /**********************************************************************
607 Returns true if the unix path is a valid msdfs symlink.
608 **********************************************************************/
610 bool is_msdfs_link(struct files_struct *dirfsp,
611 struct smb_filename *atname)
613 NTSTATUS status = SMB_VFS_READ_DFS_PATHAT(dirfsp->conn,
614 talloc_tos(),
615 dirfsp,
616 atname,
617 NULL,
618 NULL);
619 return (NT_STATUS_IS_OK(status));
622 /*****************************************************************
623 Used by other functions to decide if a dfs path is remote,
624 and to get the list of referred locations for that remote path.
626 consumedcntp: how much of the dfs path is being redirected. the client
627 should try the remaining path on the redirected server.
628 *****************************************************************/
630 static NTSTATUS dfs_path_lookup(TALLOC_CTX *ctx,
631 connection_struct *conn,
632 const char *dfspath, /* Incoming complete dfs path */
633 const char *reqpath, /* Parsed out remaining path. */
634 uint32_t ucf_flags,
635 size_t *consumedcntp,
636 struct referral **ppreflist,
637 size_t *preferral_count)
639 NTSTATUS status;
640 struct smb_filename *parent_smb_fname = NULL;
641 struct smb_filename *smb_fname_rel = NULL;
642 NTTIME twrp = 0;
643 char *local_pathname = NULL;
644 char *last_component = NULL;
645 char *atname = NULL;
646 size_t removed_components = 0;
647 bool posix = (ucf_flags & UCF_POSIX_PATHNAMES);
648 char *p = NULL;
649 char *canon_dfspath = NULL;
651 DBG_DEBUG("Conn path = %s reqpath = %s\n", conn->connectpath, reqpath);
653 local_pathname = talloc_strdup(ctx, reqpath);
654 if (local_pathname == NULL) {
655 status = NT_STATUS_NO_MEMORY;
656 goto out;
659 /* We know reqpath isn't a DFS path. */
660 ucf_flags &= ~UCF_DFS_PATHNAME;
662 if (ucf_flags & UCF_GMT_PATHNAME) {
663 extract_snapshot_token(local_pathname, &twrp);
664 ucf_flags &= ~UCF_GMT_PATHNAME;
668 * We should have been given a DFS path to resolve.
669 * This should return NT_STATUS_PATH_NOT_COVERED.
671 * Do a pathname walk, stripping off components
672 * until we get NT_STATUS_OK instead of
673 * NT_STATUS_PATH_NOT_COVERED.
675 * Fail on any other error.
678 for (;;) {
679 TALLOC_CTX *frame = NULL;
680 struct files_struct *dirfsp = NULL;
681 struct smb_filename *smb_fname_walk = NULL;
683 TALLOC_FREE(parent_smb_fname);
686 * Use a local stackframe as filename_convert_dirfsp()
687 * opens handles on the last two components in the path.
688 * Allow these to be freed as we step back through
689 * the local_pathname.
691 frame = talloc_stackframe();
692 status = filename_convert_dirfsp(frame,
693 conn,
694 local_pathname,
695 ucf_flags,
696 twrp,
697 &dirfsp,
698 &smb_fname_walk);
699 /* If we got a name, save it. */
700 if (smb_fname_walk != NULL) {
701 parent_smb_fname = talloc_move(ctx, &smb_fname_walk);
703 TALLOC_FREE(frame);
705 if (!NT_STATUS_EQUAL(status, NT_STATUS_PATH_NOT_COVERED)) {
707 * For any other status than NT_STATUS_PATH_NOT_COVERED
708 * (including NT_STATUS_OK) we exit the walk.
709 * If it's an error we catch it outside the loop.
711 break;
714 /* Step back one component and save it off as last_component. */
715 TALLOC_FREE(last_component);
716 p = strrchr(local_pathname, '/');
717 if (p == NULL) {
719 * We removed all components.
720 * Go around once more to make
721 * sure we can open the root '\0'.
723 last_component = talloc_strdup(ctx, local_pathname);
724 *local_pathname = '\0';
725 } else {
726 last_component = talloc_strdup(ctx, p+1);
727 *p = '\0';
729 if (last_component == NULL) {
730 status = NT_STATUS_NO_MEMORY;
731 goto out;
733 /* Integer wrap check. */
734 if (removed_components + 1 < removed_components) {
735 status = NT_STATUS_INVALID_PARAMETER;
736 goto out;
738 removed_components++;
741 if (!NT_STATUS_IS_OK(status)) {
742 DBG_DEBUG("dfspath = %s. reqpath = %s. Error %s.\n",
743 dfspath,
744 reqpath,
745 nt_errstr(status));
746 goto out;
749 if (parent_smb_fname->fsp == NULL) {
750 /* Unable to open parent. */
751 DBG_DEBUG("dfspath = %s. reqpath = %s. "
752 "Unable to open parent directory (%s).\n",
753 dfspath,
754 reqpath,
755 smb_fname_str_dbg(parent_smb_fname));
756 status = NT_STATUS_OBJECT_PATH_NOT_FOUND;
757 goto out;
760 if (removed_components == 0) {
762 * We never got NT_STATUS_PATH_NOT_COVERED.
763 * There was no DFS redirect.
765 DBG_DEBUG("dfspath = %s. reqpath = %s. "
766 "No removed components.\n",
767 dfspath,
768 reqpath);
769 status = NT_STATUS_OBJECT_PATH_NOT_FOUND;
770 goto out;
774 * One of the removed_components was the MSDFS link
775 * at the end. We need to count this in the resolved
776 * path below, so remove one from removed_components.
778 removed_components--;
781 * Now parent_smb_fname->fsp is the parent directory dirfsp,
782 * last_component is the untranslated MS-DFS link name.
783 * Search for it in the parent directory to get the real
784 * filename on disk.
786 status = get_real_filename_at(parent_smb_fname->fsp,
787 last_component,
788 ctx,
789 &atname);
791 if (!NT_STATUS_IS_OK(status)) {
792 DBG_DEBUG("dfspath = %s. reqpath = %s "
793 "get_real_filename_at(%s, %s) error (%s)\n",
794 dfspath,
795 reqpath,
796 smb_fname_str_dbg(parent_smb_fname),
797 last_component,
798 nt_errstr(status));
799 goto out;
802 smb_fname_rel = synthetic_smb_fname(ctx,
803 atname,
804 NULL,
805 NULL,
806 twrp,
807 posix ? SMB_FILENAME_POSIX_PATH : 0);
808 if (smb_fname_rel == NULL) {
809 status = NT_STATUS_NO_MEMORY;
810 goto out;
813 /* Get the referral to return. */
814 status = SMB_VFS_READ_DFS_PATHAT(conn,
815 ctx,
816 parent_smb_fname->fsp,
817 smb_fname_rel,
818 ppreflist,
819 preferral_count);
820 if (!NT_STATUS_IS_OK(status)) {
821 DBG_DEBUG("dfspath = %s. reqpath = %s. "
822 "SMB_VFS_READ_DFS_PATHAT(%s, %s) error (%s)\n",
823 dfspath,
824 reqpath,
825 smb_fname_str_dbg(parent_smb_fname),
826 smb_fname_str_dbg(smb_fname_rel),
827 nt_errstr(status));
828 goto out;
832 * Now we must work out how much of the
833 * given pathname we consumed.
835 canon_dfspath = talloc_strdup(ctx, dfspath);
836 if (!canon_dfspath) {
837 status = NT_STATUS_NO_MEMORY;
838 goto out;
840 /* Canonicalize the raw dfspath. */
841 string_replace(canon_dfspath, '\\', '/');
844 * reqpath comes out of parse_dfs_path(), so it has
845 * no trailing backslash. Make sure that canon_dfspath hasn't either.
847 trim_char(canon_dfspath, 0, '/');
849 DBG_DEBUG("Unconsumed path: %s\n", canon_dfspath);
851 while (removed_components > 0) {
852 p = strrchr(canon_dfspath, '/');
853 if (p != NULL) {
854 *p = '\0';
856 removed_components--;
857 if (p == NULL && removed_components != 0) {
858 DBG_ERR("Component mismatch. path = %s, "
859 "%zu components left\n",
860 canon_dfspath,
861 removed_components);
862 status = NT_STATUS_OBJECT_PATH_NOT_FOUND;
863 goto out;
866 *consumedcntp = strlen(canon_dfspath);
867 DBG_DEBUG("Path consumed: %s (%zu)\n", canon_dfspath, *consumedcntp);
868 status = NT_STATUS_OK;
870 out:
872 TALLOC_FREE(parent_smb_fname);
873 TALLOC_FREE(local_pathname);
874 TALLOC_FREE(last_component);
875 TALLOC_FREE(atname);
876 TALLOC_FREE(smb_fname_rel);
877 TALLOC_FREE(canon_dfspath);
878 return status;
881 /**********************************************************************
882 Return a self referral.
883 **********************************************************************/
885 static NTSTATUS self_ref(TALLOC_CTX *ctx,
886 const char *dfs_path,
887 struct junction_map *jucn,
888 size_t *consumedcntp,
889 bool *self_referralp)
891 struct referral *ref;
893 *self_referralp = True;
895 jucn->referral_count = 1;
896 if((ref = talloc_zero(ctx, struct referral)) == NULL) {
897 return NT_STATUS_NO_MEMORY;
900 ref->alternate_path = talloc_strdup(ctx, dfs_path);
901 if (!ref->alternate_path) {
902 TALLOC_FREE(ref);
903 return NT_STATUS_NO_MEMORY;
905 ref->proximity = 0;
906 ref->ttl = REFERRAL_TTL;
907 jucn->referral_list = ref;
908 *consumedcntp = strlen(dfs_path);
909 return NT_STATUS_OK;
912 /**********************************************************************
913 Gets valid referrals for a dfs path and fills up the
914 junction_map structure.
915 **********************************************************************/
917 NTSTATUS get_referred_path(TALLOC_CTX *ctx,
918 struct auth_session_info *session_info,
919 const char *dfs_path,
920 const struct tsocket_address *remote_address,
921 const struct tsocket_address *local_address,
922 struct junction_map *jucn,
923 size_t *consumedcntp,
924 bool *self_referralp)
926 TALLOC_CTX *frame = talloc_stackframe();
927 const struct loadparm_substitution *lp_sub =
928 loadparm_s3_global_substitution();
929 struct conn_struct_tos *c = NULL;
930 struct connection_struct *conn = NULL;
931 char *servicename = NULL;
932 char *reqpath = NULL;
933 int snum;
934 NTSTATUS status = NT_STATUS_NOT_FOUND;
936 *self_referralp = False;
938 status = parse_dfs_path_strict(
939 frame,
940 dfs_path,
941 NULL, /* hostname */
942 &servicename,
943 &reqpath);
944 if (!NT_STATUS_IS_OK(status)) {
945 TALLOC_FREE(frame);
946 return status;
949 /* Path referrals are always non-POSIX. */
950 status = check_path_syntax(reqpath);
951 if (!NT_STATUS_IS_OK(status)) {
952 TALLOC_FREE(frame);
953 return status;
956 jucn->service_name = talloc_strdup(ctx, servicename);
957 jucn->volume_name = talloc_strdup(ctx, reqpath);
958 if (!jucn->service_name || !jucn->volume_name) {
959 TALLOC_FREE(frame);
960 return NT_STATUS_NO_MEMORY;
963 /* Verify the share is a dfs root */
964 snum = lp_servicenumber(jucn->service_name);
965 if(snum < 0) {
966 char *service_name = NULL;
967 if ((snum = find_service(ctx, jucn->service_name, &service_name)) < 0) {
968 TALLOC_FREE(frame);
969 return NT_STATUS_NOT_FOUND;
971 if (!service_name) {
972 TALLOC_FREE(frame);
973 return NT_STATUS_NO_MEMORY;
975 TALLOC_FREE(jucn->service_name);
976 jucn->service_name = talloc_strdup(ctx, service_name);
977 if (!jucn->service_name) {
978 TALLOC_FREE(frame);
979 return NT_STATUS_NO_MEMORY;
983 if (!lp_msdfs_root(snum) && (*lp_msdfs_proxy(talloc_tos(), lp_sub, snum) == '\0')) {
984 DEBUG(3,("get_referred_path: |%s| in dfs path %s is not "
985 "a dfs root.\n",
986 servicename, dfs_path));
987 TALLOC_FREE(frame);
988 return NT_STATUS_NOT_FOUND;
992 * Self referrals are tested with a anonymous IPC connection and
993 * a GET_DFS_REFERRAL call to \\server\share. (which means
994 * dp.reqpath[0] points to an empty string). create_conn_struct cd's
995 * into the directory and will fail if it cannot (as the anonymous
996 * user). Cope with this.
999 if (reqpath[0] == '\0') {
1000 char *tmp;
1001 struct referral *ref;
1002 size_t refcount;
1004 if (*lp_msdfs_proxy(talloc_tos(), lp_sub, snum) == '\0') {
1005 TALLOC_FREE(frame);
1006 return self_ref(ctx,
1007 dfs_path,
1008 jucn,
1009 consumedcntp,
1010 self_referralp);
1014 * It's an msdfs proxy share. Redirect to
1015 * the configured target share.
1018 tmp = talloc_asprintf(frame, "msdfs:%s",
1019 lp_msdfs_proxy(frame, lp_sub, snum));
1020 if (tmp == NULL) {
1021 TALLOC_FREE(frame);
1022 return NT_STATUS_NO_MEMORY;
1025 if (!parse_msdfs_symlink(ctx,
1026 lp_msdfs_shuffle_referrals(snum),
1027 tmp,
1028 &ref,
1029 &refcount)) {
1030 TALLOC_FREE(frame);
1031 return NT_STATUS_INVALID_PARAMETER;
1033 jucn->referral_count = refcount;
1034 jucn->referral_list = ref;
1035 *consumedcntp = strlen(dfs_path);
1036 TALLOC_FREE(frame);
1037 return NT_STATUS_OK;
1040 status = create_conn_struct_tos_cwd(global_messaging_context(),
1041 snum,
1042 lp_path(frame, lp_sub, snum),
1043 session_info,
1044 &c);
1045 if (!NT_STATUS_IS_OK(status)) {
1046 TALLOC_FREE(frame);
1047 return status;
1049 conn = c->conn;
1052 * TODO
1054 * The remote and local address should be passed down to
1055 * create_conn_struct_cwd.
1057 if (conn->sconn->remote_address == NULL) {
1058 conn->sconn->remote_address =
1059 tsocket_address_copy(remote_address, conn->sconn);
1060 if (conn->sconn->remote_address == NULL) {
1061 TALLOC_FREE(frame);
1062 return NT_STATUS_NO_MEMORY;
1065 if (conn->sconn->local_address == NULL) {
1066 conn->sconn->local_address =
1067 tsocket_address_copy(local_address, conn->sconn);
1068 if (conn->sconn->local_address == NULL) {
1069 TALLOC_FREE(frame);
1070 return NT_STATUS_NO_MEMORY;
1074 status = dfs_path_lookup(ctx,
1075 conn,
1076 dfs_path,
1077 reqpath,
1078 0, /* ucf_flags */
1079 consumedcntp,
1080 &jucn->referral_list,
1081 &jucn->referral_count);
1083 if (!NT_STATUS_IS_OK(status)) {
1084 DBG_NOTICE("No valid referrals for path %s (%s)\n",
1085 dfs_path,
1086 nt_errstr(status));
1089 TALLOC_FREE(frame);
1090 return status;
1093 /******************************************************************
1094 Set up the DFS referral for the dfs pathname. This call returns
1095 the amount of the path covered by this server, and where the
1096 client should be redirected to. This is the meat of the
1097 TRANS2_GET_DFS_REFERRAL call.
1098 ******************************************************************/
1100 int setup_dfs_referral(connection_struct *orig_conn,
1101 const char *dfs_path,
1102 int max_referral_level,
1103 char **ppdata, NTSTATUS *pstatus)
1105 char *pdata = *ppdata;
1106 int reply_size = 0;
1107 struct dfs_GetDFSReferral *r;
1108 DATA_BLOB blob = data_blob_null;
1109 NTSTATUS status;
1110 enum ndr_err_code ndr_err;
1112 r = talloc_zero(talloc_tos(), struct dfs_GetDFSReferral);
1113 if (r == NULL) {
1114 *pstatus = NT_STATUS_NO_MEMORY;
1115 return -1;
1118 r->in.req.max_referral_level = max_referral_level;
1119 r->in.req.servername = talloc_strdup(r, dfs_path);
1120 if (r->in.req.servername == NULL) {
1121 talloc_free(r);
1122 *pstatus = NT_STATUS_NO_MEMORY;
1123 return -1;
1126 status = SMB_VFS_GET_DFS_REFERRALS(orig_conn, r);
1127 if (!NT_STATUS_IS_OK(status)) {
1128 talloc_free(r);
1129 *pstatus = status;
1130 return -1;
1133 ndr_err = ndr_push_struct_blob(&blob, r,
1134 r->out.resp,
1135 (ndr_push_flags_fn_t)ndr_push_dfs_referral_resp);
1136 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1137 TALLOC_FREE(r);
1138 *pstatus = NT_STATUS_INVALID_PARAMETER;
1139 return -1;
1142 pdata = (char *)SMB_REALLOC(pdata, blob.length);
1143 if(pdata == NULL) {
1144 TALLOC_FREE(r);
1145 DEBUG(0,("referral setup:"
1146 "malloc failed for Realloc!\n"));
1147 return -1;
1149 *ppdata = pdata;
1150 reply_size = blob.length;
1151 memcpy(pdata, blob.data, blob.length);
1152 TALLOC_FREE(r);
1154 *pstatus = NT_STATUS_OK;
1155 return reply_size;
1158 /**********************************************************************
1159 The following functions are called by the NETDFS RPC pipe functions
1160 **********************************************************************/
1162 /*********************************************************************
1163 Creates a junction structure from a DFS pathname
1164 **********************************************************************/
1166 bool create_junction(TALLOC_CTX *ctx,
1167 const char *dfs_path,
1168 struct junction_map *jucn)
1170 const struct loadparm_substitution *lp_sub =
1171 loadparm_s3_global_substitution();
1172 int snum;
1173 char *servicename = NULL;
1174 char *reqpath = NULL;
1175 NTSTATUS status;
1177 status = parse_dfs_path_strict(
1178 ctx,
1179 dfs_path,
1180 NULL,
1181 &servicename,
1182 &reqpath);
1183 if (!NT_STATUS_IS_OK(status)) {
1184 return False;
1187 /* Check for a non-DFS share */
1188 snum = lp_servicenumber(servicename);
1190 if(snum < 0 || !lp_msdfs_root(snum)) {
1191 DEBUG(4,("create_junction: %s is not an msdfs root.\n",
1192 servicename));
1193 return False;
1196 /* Junction create paths are always non-POSIX. */
1197 status = check_path_syntax(reqpath);
1198 if (!NT_STATUS_IS_OK(status)) {
1199 return false;
1202 jucn->service_name = talloc_strdup(ctx, servicename);
1203 jucn->volume_name = talloc_strdup(ctx, reqpath);
1204 jucn->comment = lp_comment(ctx, lp_sub, snum);
1206 if (!jucn->service_name || !jucn->volume_name || ! jucn->comment) {
1207 return False;
1209 return True;
1212 /**********************************************************************
1213 Forms a valid Unix pathname from the junction
1214 **********************************************************************/
1216 static bool junction_to_local_path_tos(const struct junction_map *jucn,
1217 struct auth_session_info *session_info,
1218 char **pp_path_out,
1219 connection_struct **conn_out)
1221 const struct loadparm_substitution *lp_sub =
1222 loadparm_s3_global_substitution();
1223 struct conn_struct_tos *c = NULL;
1224 int snum;
1225 char *path_out = NULL;
1226 NTSTATUS status;
1228 snum = lp_servicenumber(jucn->service_name);
1229 if(snum < 0) {
1230 return False;
1232 status = create_conn_struct_tos_cwd(global_messaging_context(),
1233 snum,
1234 lp_path(talloc_tos(), lp_sub, snum),
1235 session_info,
1236 &c);
1237 if (!NT_STATUS_IS_OK(status)) {
1238 return False;
1241 path_out = talloc_asprintf(c,
1242 "%s/%s",
1243 lp_path(talloc_tos(), lp_sub, snum),
1244 jucn->volume_name);
1245 if (path_out == NULL) {
1246 TALLOC_FREE(c);
1247 return False;
1249 *pp_path_out = path_out;
1250 *conn_out = c->conn;
1251 return True;
1255 * Create a msdfs string in Samba format we can store
1256 * in a filesystem object (currently a symlink).
1259 char *msdfs_link_string(TALLOC_CTX *ctx,
1260 const struct referral *reflist,
1261 size_t referral_count)
1263 char *refpath = NULL;
1264 bool insert_comma = false;
1265 char *msdfs_link = NULL;
1266 size_t i;
1268 /* Form the msdfs_link contents */
1269 msdfs_link = talloc_strdup(ctx, "msdfs:");
1270 if (msdfs_link == NULL) {
1271 goto err;
1274 for( i= 0; i < referral_count; i++) {
1275 refpath = talloc_strdup(ctx, reflist[i].alternate_path);
1277 if (refpath == NULL) {
1278 goto err;
1281 /* Alternate paths always use Windows separators. */
1282 trim_char(refpath, '\\', '\\');
1283 if (*refpath == '\0') {
1284 if (i == 0) {
1285 insert_comma = false;
1287 continue;
1289 if (i > 0 && insert_comma) {
1290 msdfs_link = talloc_asprintf_append_buffer(msdfs_link,
1291 ",%s",
1292 refpath);
1293 } else {
1294 msdfs_link = talloc_asprintf_append_buffer(msdfs_link,
1295 "%s",
1296 refpath);
1299 if (msdfs_link == NULL) {
1300 goto err;
1303 if (!insert_comma) {
1304 insert_comma = true;
1307 TALLOC_FREE(refpath);
1310 return msdfs_link;
1312 err:
1314 TALLOC_FREE(refpath);
1315 TALLOC_FREE(msdfs_link);
1316 return NULL;
1319 bool create_msdfs_link(const struct junction_map *jucn,
1320 struct auth_session_info *session_info)
1322 TALLOC_CTX *frame = talloc_stackframe();
1323 char *path = NULL;
1324 connection_struct *conn;
1325 struct smb_filename *smb_fname = NULL;
1326 struct smb_filename *parent_fname = NULL;
1327 struct smb_filename *at_fname = NULL;
1328 bool ok;
1329 NTSTATUS status;
1330 bool ret = false;
1332 ok = junction_to_local_path_tos(jucn, session_info, &path, &conn);
1333 if (!ok) {
1334 goto out;
1337 if (!CAN_WRITE(conn)) {
1338 const struct loadparm_substitution *lp_sub =
1339 loadparm_s3_global_substitution();
1340 int snum = lp_servicenumber(jucn->service_name);
1342 DBG_WARNING("Can't create DFS entry on read-only share %s\n",
1343 lp_servicename(frame, lp_sub, snum));
1344 goto out;
1347 smb_fname = synthetic_smb_fname(frame,
1348 path,
1349 NULL,
1350 NULL,
1353 if (smb_fname == NULL) {
1354 goto out;
1357 status = parent_pathref(frame,
1358 conn->cwd_fsp,
1359 smb_fname,
1360 &parent_fname,
1361 &at_fname);
1362 if (!NT_STATUS_IS_OK(status)) {
1363 goto out;
1366 status = SMB_VFS_CREATE_DFS_PATHAT(conn,
1367 parent_fname->fsp,
1368 at_fname,
1369 jucn->referral_list,
1370 jucn->referral_count);
1371 if (!NT_STATUS_IS_OK(status)) {
1372 if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_COLLISION)) {
1373 int retval = SMB_VFS_UNLINKAT(conn,
1374 parent_fname->fsp,
1375 at_fname,
1377 if (retval != 0) {
1378 goto out;
1381 status = SMB_VFS_CREATE_DFS_PATHAT(conn,
1382 parent_fname->fsp,
1383 at_fname,
1384 jucn->referral_list,
1385 jucn->referral_count);
1386 if (!NT_STATUS_IS_OK(status)) {
1387 DBG_WARNING("SMB_VFS_CREATE_DFS_PATHAT failed "
1388 "%s - Error: %s\n",
1389 path,
1390 nt_errstr(status));
1391 goto out;
1395 ret = true;
1397 out:
1398 TALLOC_FREE(frame);
1399 return ret;
1402 bool remove_msdfs_link(const struct junction_map *jucn,
1403 struct auth_session_info *session_info)
1405 TALLOC_CTX *frame = talloc_stackframe();
1406 char *path = NULL;
1407 connection_struct *conn;
1408 bool ret = False;
1409 struct smb_filename *smb_fname;
1410 struct smb_filename *parent_fname = NULL;
1411 struct smb_filename *at_fname = NULL;
1412 NTSTATUS status;
1413 bool ok;
1414 int retval;
1416 ok = junction_to_local_path_tos(jucn, session_info, &path, &conn);
1417 if (!ok) {
1418 TALLOC_FREE(frame);
1419 return false;
1422 if (!CAN_WRITE(conn)) {
1423 const struct loadparm_substitution *lp_sub =
1424 loadparm_s3_global_substitution();
1425 int snum = lp_servicenumber(jucn->service_name);
1427 DBG_WARNING("Can't remove DFS entry on read-only share %s\n",
1428 lp_servicename(frame, lp_sub, snum));
1429 TALLOC_FREE(frame);
1430 return false;
1433 smb_fname = synthetic_smb_fname(frame,
1434 path,
1435 NULL,
1436 NULL,
1439 if (smb_fname == NULL) {
1440 TALLOC_FREE(frame);
1441 errno = ENOMEM;
1442 return false;
1445 status = parent_pathref(frame,
1446 conn->cwd_fsp,
1447 smb_fname,
1448 &parent_fname,
1449 &at_fname);
1450 if (!NT_STATUS_IS_OK(status)) {
1451 TALLOC_FREE(frame);
1452 return false;
1455 retval = SMB_VFS_UNLINKAT(conn,
1456 parent_fname->fsp,
1457 at_fname,
1459 if (retval == 0) {
1460 ret = True;
1463 TALLOC_FREE(frame);
1464 return ret;
1467 /*********************************************************************
1468 Return the number of DFS links at the root of this share.
1469 *********************************************************************/
1471 static size_t count_dfs_links(TALLOC_CTX *ctx,
1472 struct auth_session_info *session_info,
1473 int snum)
1475 TALLOC_CTX *frame = talloc_stackframe();
1476 const struct loadparm_substitution *lp_sub =
1477 loadparm_s3_global_substitution();
1478 size_t cnt = 0;
1479 const char *dname = NULL;
1480 char *talloced = NULL;
1481 const char *connect_path = lp_path(frame, lp_sub, snum);
1482 const char *msdfs_proxy = lp_msdfs_proxy(frame, lp_sub, snum);
1483 struct conn_struct_tos *c = NULL;
1484 connection_struct *conn = NULL;
1485 NTSTATUS status;
1486 struct smb_filename *smb_fname = NULL;
1487 struct smb_Dir *dir_hnd = NULL;
1488 long offset = 0;
1490 if(*connect_path == '\0') {
1491 TALLOC_FREE(frame);
1492 return 0;
1496 * Fake up a connection struct for the VFS layer.
1499 status = create_conn_struct_tos_cwd(global_messaging_context(),
1500 snum,
1501 connect_path,
1502 session_info,
1503 &c);
1504 if (!NT_STATUS_IS_OK(status)) {
1505 DEBUG(3, ("create_conn_struct failed: %s\n",
1506 nt_errstr(status)));
1507 TALLOC_FREE(frame);
1508 return 0;
1510 conn = c->conn;
1512 /* Count a link for the msdfs root - convention */
1513 cnt = 1;
1515 /* No more links if this is an msdfs proxy. */
1516 if (*msdfs_proxy != '\0') {
1517 goto out;
1520 smb_fname = synthetic_smb_fname(frame,
1521 ".",
1522 NULL,
1523 NULL,
1526 if (smb_fname == NULL) {
1527 goto out;
1530 /* Now enumerate all dfs links */
1531 status = OpenDir(frame,
1532 conn,
1533 smb_fname,
1534 NULL,
1536 &dir_hnd);
1537 if (!NT_STATUS_IS_OK(status)) {
1538 errno = map_errno_from_nt_status(status);
1539 goto out;
1542 while ((dname = ReadDirName(dir_hnd, &offset, NULL, &talloced))
1543 != NULL)
1545 struct smb_filename *smb_dname =
1546 synthetic_smb_fname(frame,
1547 dname,
1548 NULL,
1549 NULL,
1552 if (smb_dname == NULL) {
1553 goto out;
1555 if (is_msdfs_link(dir_hnd_fetch_fsp(dir_hnd), smb_dname)) {
1556 if (cnt + 1 < cnt) {
1557 cnt = 0;
1558 goto out;
1560 cnt++;
1562 TALLOC_FREE(talloced);
1563 TALLOC_FREE(smb_dname);
1566 out:
1567 TALLOC_FREE(frame);
1568 return cnt;
1571 /*********************************************************************
1572 *********************************************************************/
1574 static int form_junctions(TALLOC_CTX *ctx,
1575 struct auth_session_info *session_info,
1576 int snum,
1577 struct junction_map *jucn,
1578 size_t jn_remain)
1580 TALLOC_CTX *frame = talloc_stackframe();
1581 const struct loadparm_substitution *lp_sub =
1582 loadparm_s3_global_substitution();
1583 size_t cnt = 0;
1584 const char *dname = NULL;
1585 char *talloced = NULL;
1586 const char *connect_path = lp_path(frame, lp_sub, snum);
1587 char *service_name = lp_servicename(frame, lp_sub, snum);
1588 const char *msdfs_proxy = lp_msdfs_proxy(frame, lp_sub, snum);
1589 struct conn_struct_tos *c = NULL;
1590 connection_struct *conn = NULL;
1591 struct referral *ref = NULL;
1592 struct smb_filename *smb_fname = NULL;
1593 struct smb_Dir *dir_hnd = NULL;
1594 long offset = 0;
1595 NTSTATUS status;
1597 if (jn_remain == 0) {
1598 TALLOC_FREE(frame);
1599 return 0;
1602 if(*connect_path == '\0') {
1603 TALLOC_FREE(frame);
1604 return 0;
1608 * Fake up a connection struct for the VFS layer.
1611 status = create_conn_struct_tos_cwd(global_messaging_context(),
1612 snum,
1613 connect_path,
1614 session_info,
1615 &c);
1616 if (!NT_STATUS_IS_OK(status)) {
1617 DEBUG(3, ("create_conn_struct failed: %s\n",
1618 nt_errstr(status)));
1619 TALLOC_FREE(frame);
1620 return 0;
1622 conn = c->conn;
1624 /* form a junction for the msdfs root - convention
1625 DO NOT REMOVE THIS: NT clients will not work with us
1626 if this is not present
1628 jucn[cnt].service_name = talloc_strdup(ctx,service_name);
1629 jucn[cnt].volume_name = talloc_strdup(ctx, "");
1630 if (!jucn[cnt].service_name || !jucn[cnt].volume_name) {
1631 goto out;
1633 jucn[cnt].comment = "";
1634 jucn[cnt].referral_count = 1;
1636 ref = jucn[cnt].referral_list = talloc_zero(ctx, struct referral);
1637 if (jucn[cnt].referral_list == NULL) {
1638 goto out;
1641 ref->proximity = 0;
1642 ref->ttl = REFERRAL_TTL;
1643 if (*msdfs_proxy != '\0') {
1644 ref->alternate_path = talloc_strdup(ctx,
1645 msdfs_proxy);
1646 } else {
1647 ref->alternate_path = talloc_asprintf(ctx,
1648 "\\\\%s\\%s",
1649 get_local_machine_name(),
1650 service_name);
1653 if (!ref->alternate_path) {
1654 goto out;
1656 cnt++;
1658 /* Don't enumerate if we're an msdfs proxy. */
1659 if (*msdfs_proxy != '\0') {
1660 goto out;
1663 smb_fname = synthetic_smb_fname(frame,
1664 ".",
1665 NULL,
1666 NULL,
1669 if (smb_fname == NULL) {
1670 goto out;
1673 /* Now enumerate all dfs links */
1674 status = OpenDir(frame,
1675 conn,
1676 smb_fname,
1677 NULL,
1679 &dir_hnd);
1680 if (!NT_STATUS_IS_OK(status)) {
1681 errno = map_errno_from_nt_status(status);
1682 goto out;
1685 while ((dname = ReadDirName(dir_hnd, &offset, NULL, &talloced))
1686 != NULL)
1688 struct smb_filename *smb_dname = NULL;
1690 if (cnt >= jn_remain) {
1691 DEBUG(2, ("form_junctions: ran out of MSDFS "
1692 "junction slots"));
1693 TALLOC_FREE(talloced);
1694 goto out;
1696 smb_dname = synthetic_smb_fname(talloc_tos(),
1697 dname,
1698 NULL,
1699 NULL,
1702 if (smb_dname == NULL) {
1703 TALLOC_FREE(talloced);
1704 goto out;
1707 status = SMB_VFS_READ_DFS_PATHAT(conn,
1708 ctx,
1709 conn->cwd_fsp,
1710 smb_dname,
1711 &jucn[cnt].referral_list,
1712 &jucn[cnt].referral_count);
1714 if (NT_STATUS_IS_OK(status)) {
1715 jucn[cnt].service_name = talloc_strdup(ctx,
1716 service_name);
1717 jucn[cnt].volume_name = talloc_strdup(ctx, dname);
1718 if (!jucn[cnt].service_name || !jucn[cnt].volume_name) {
1719 TALLOC_FREE(talloced);
1720 goto out;
1722 jucn[cnt].comment = "";
1723 cnt++;
1725 TALLOC_FREE(talloced);
1726 TALLOC_FREE(smb_dname);
1729 out:
1730 TALLOC_FREE(frame);
1731 return cnt;
1734 struct junction_map *enum_msdfs_links(TALLOC_CTX *ctx,
1735 struct auth_session_info *session_info,
1736 size_t *p_num_jn)
1738 struct junction_map *jn = NULL;
1739 int i=0;
1740 size_t jn_count = 0;
1741 int sharecount = 0;
1743 *p_num_jn = 0;
1744 if(!lp_host_msdfs()) {
1745 return NULL;
1748 /* Ensure all the usershares are loaded. */
1749 become_root();
1750 load_registry_shares();
1751 sharecount = load_usershare_shares(NULL, connections_snum_used);
1752 unbecome_root();
1754 for(i=0;i < sharecount;i++) {
1755 if(lp_msdfs_root(i)) {
1756 jn_count += count_dfs_links(ctx, session_info, i);
1759 if (jn_count == 0) {
1760 return NULL;
1762 jn = talloc_array(ctx, struct junction_map, jn_count);
1763 if (!jn) {
1764 return NULL;
1766 for(i=0; i < sharecount; i++) {
1767 if (*p_num_jn >= jn_count) {
1768 break;
1770 if(lp_msdfs_root(i)) {
1771 *p_num_jn += form_junctions(ctx,
1772 session_info,
1774 &jn[*p_num_jn],
1775 jn_count - *p_num_jn);
1778 return jn;