auth/credentials: a temporary MEMORY ccache needs krb5_cc_destroy()
[Samba.git] / source3 / smbd / msdfs.c
blobd8361fb8060c917d740e8d0c760a5e71dedea4ec
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"
38 #include "source3/smbd/dir.h"
40 /**********************************************************************
41 Parse a DFS pathname of the form(s)
43 \hostname\service - self referral
44 \hostname\service\remainingpath - Windows referral path
46 FIXME! Should we also parse:
47 \hostname\service/remainingpath - POSIX referral path
48 as currently nothing uses this ?
50 into the dfs_path components. Strict form.
52 Checks DFS path starts with separator.
53 Checks hostname is ours.
54 Ensures servicename (share) is sent, and
55 if so, terminates the name or is followed by
56 \pathname.
58 If returned, remainingpath is untouched. Caller must call
59 check_path_syntax() on it.
61 Called by all non-fileserver processing (DFS RPC, FSCTL_DFS_GET_REFERRALS)
62 etc. Errors out on any inconsistency in the path.
63 **********************************************************************/
65 static NTSTATUS parse_dfs_path_strict(TALLOC_CTX *ctx,
66 const char *pathname,
67 char **_hostname,
68 char **_servicename,
69 char **_remaining_path)
71 char *pathname_local = NULL;
72 char *p = NULL;
73 const char *hostname = NULL;
74 const char *servicename = NULL;
75 const char *reqpath = NULL;
76 bool my_hostname = false;
77 NTSTATUS status;
79 DBG_DEBUG("path = |%s|\n", pathname);
81 pathname_local = talloc_strdup(talloc_tos(), pathname);
82 if (pathname_local == NULL) {
83 return NT_STATUS_NO_MEMORY;
86 * parse_dfs_path_strict() is called from
87 * get_referred_path() and create_junction()
88 * which use Windows DFS paths of \server\share.
92 * Strict DFS paths *must* start with the
93 * path separator '\\'.
96 if (pathname_local[0] != '\\') {
97 DBG_ERR("path %s doesn't start with \\\n",
98 pathname_local);
99 status = NT_STATUS_NOT_FOUND;
100 goto out;
103 /* Now tokenize. */
104 /* Parse out hostname. */
105 p = strchr(pathname_local + 1, '\\');
106 if (p == NULL) {
107 DBG_ERR("can't parse hostname from path %s\n",
108 pathname_local);
109 status = NT_STATUS_NOT_FOUND;
110 goto out;
112 *p = '\0';
113 hostname = &pathname_local[1];
115 DBG_DEBUG("hostname: %s\n", hostname);
117 /* Is this really our hostname ? */
118 my_hostname = is_myname_or_ipaddr(hostname);
119 if (!my_hostname) {
120 DBG_ERR("Hostname %s is not ours.\n",
121 hostname);
122 status = NT_STATUS_NOT_FOUND;
123 goto out;
126 servicename = p + 1;
129 * Find the end of servicename by looking for
130 * a directory separator character. The character
131 * should be '\\' for a Windows path.
132 * If there is no separator, then this is a self-referral
133 * of "\server\share".
136 p = strchr(servicename, '\\');
137 if (p != NULL) {
138 *p = '\0';
141 DBG_DEBUG("servicename: %s\n", servicename);
143 if (p == NULL) {
144 /* Client sent self referral "\server\share". */
145 reqpath = "";
146 } else {
147 /* Step past the '\0' we just replaced '\\' with. */
148 reqpath = p + 1;
151 DBG_DEBUG("rest of the path: %s\n", reqpath);
153 if (_hostname != NULL) {
154 *_hostname = talloc_strdup(ctx, hostname);
155 if (*_hostname == NULL) {
156 status = NT_STATUS_NO_MEMORY;
157 goto out;
160 if (_servicename != NULL) {
161 *_servicename = talloc_strdup(ctx, servicename);
162 if (*_servicename == NULL) {
163 status = NT_STATUS_NO_MEMORY;
164 goto out;
167 if (_remaining_path != NULL) {
168 *_remaining_path = talloc_strdup(ctx, reqpath);
169 if (*_remaining_path == NULL) {
170 status = NT_STATUS_NO_MEMORY;
171 goto out;
175 status = NT_STATUS_OK;
176 out:
177 TALLOC_FREE(pathname_local);
178 return status;
181 /********************************************************
182 Fake up a connection struct for the VFS layer, for use in
183 applications (such as the python bindings), that do not want the
184 global working directory changed under them.
186 SMB_VFS_CONNECT requires root privileges.
187 *********************************************************/
189 static NTSTATUS create_conn_struct_as_root(TALLOC_CTX *ctx,
190 struct tevent_context *ev,
191 struct messaging_context *msg,
192 connection_struct **pconn,
193 int snum,
194 const char *path,
195 const struct auth_session_info *session_info)
197 connection_struct *conn;
198 char *connpath;
199 const char *vfs_user;
200 struct smbd_server_connection *sconn;
201 const char *servicename = lp_const_servicename(snum);
202 bool ok;
204 sconn = talloc_zero(ctx, struct smbd_server_connection);
205 if (sconn == NULL) {
206 return NT_STATUS_NO_MEMORY;
209 sconn->ev_ctx = ev;
210 sconn->msg_ctx = msg;
212 conn = conn_new(sconn);
213 if (conn == NULL) {
214 TALLOC_FREE(sconn);
215 return NT_STATUS_NO_MEMORY;
218 /* Now we have conn, we need to make sconn a child of conn,
219 * for a proper talloc tree */
220 talloc_steal(conn, sconn);
222 if (snum == -1 && servicename == NULL) {
223 servicename = "Unknown Service (snum == -1)";
226 connpath = talloc_strdup(conn, path);
227 if (!connpath) {
228 TALLOC_FREE(conn);
229 return NT_STATUS_NO_MEMORY;
231 connpath = talloc_string_sub(conn,
232 connpath,
233 "%S",
234 servicename);
235 if (!connpath) {
236 TALLOC_FREE(conn);
237 return NT_STATUS_NO_MEMORY;
240 /* needed for smbd_vfs_init() */
242 conn->params->service = snum;
243 conn->cnum = TID_FIELD_INVALID;
245 SMB_ASSERT(session_info != NULL);
247 conn->session_info = copy_session_info(conn, session_info);
248 if (conn->session_info == NULL) {
249 DBG_ERR("copy_serverinfo failed\n");
250 TALLOC_FREE(conn);
251 return NT_STATUS_NO_MEMORY;
254 /* unix_info could be NULL in session_info */
255 if (conn->session_info->unix_info != NULL) {
256 vfs_user = conn->session_info->unix_info->unix_name;
257 } else {
258 vfs_user = get_current_username();
261 conn_setup_case_options(conn);
263 set_conn_connectpath(conn, connpath);
266 * New code to check if there's a share security descriptor
267 * added from NT server manager. This is done after the
268 * smb.conf checks are done as we need a uid and token. JRA.
271 share_access_check(conn->session_info->security_token,
272 servicename,
273 MAXIMUM_ALLOWED_ACCESS,
274 &conn->share_access);
276 if ((conn->share_access & FILE_WRITE_DATA) == 0) {
277 if ((conn->share_access & FILE_READ_DATA) == 0) {
278 /* No access, read or write. */
279 DBG_WARNING("connection to %s "
280 "denied due to security "
281 "descriptor.\n",
282 servicename);
283 conn_free(conn);
284 return NT_STATUS_ACCESS_DENIED;
286 conn->read_only = true;
289 if (!smbd_vfs_init(conn)) {
290 NTSTATUS status = map_nt_error_from_unix(errno);
291 DEBUG(0,("create_conn_struct: smbd_vfs_init failed.\n"));
292 conn_free(conn);
293 return status;
296 /* this must be the first filesystem operation that we do */
297 if (SMB_VFS_CONNECT(conn, servicename, vfs_user) < 0) {
298 DEBUG(0,("VFS connect failed!\n"));
299 conn_free(conn);
300 return NT_STATUS_UNSUCCESSFUL;
303 ok = canonicalize_connect_path(conn);
304 if (!ok) {
305 DBG_ERR("Failed to canonicalize sharepath\n");
306 conn_free(conn);
307 return NT_STATUS_ACCESS_DENIED;
310 conn->fs_capabilities = SMB_VFS_FS_CAPABILITIES(conn, &conn->ts_res);
311 conn->tcon_done = true;
312 *pconn = talloc_move(ctx, &conn);
314 return NT_STATUS_OK;
317 static int conn_struct_tos_destructor(struct conn_struct_tos *c)
319 if (c->oldcwd_fname != NULL) {
320 vfs_ChDir(c->conn, c->oldcwd_fname);
321 TALLOC_FREE(c->oldcwd_fname);
323 SMB_VFS_DISCONNECT(c->conn);
324 conn_free(c->conn);
325 return 0;
328 /********************************************************
329 Fake up a connection struct for the VFS layer, for use in
330 applications (such as the python bindings), that do not want the
331 global working directory changed under them.
333 SMB_VFS_CONNECT requires root privileges.
334 This temporary uses become_root() and unbecome_root().
336 But further impersonation has to be done by the caller.
337 *********************************************************/
338 NTSTATUS create_conn_struct_tos(struct messaging_context *msg,
339 int snum,
340 const char *path,
341 const struct auth_session_info *session_info,
342 struct conn_struct_tos **_c)
344 struct conn_struct_tos *c = NULL;
345 struct tevent_context *ev = NULL;
346 NTSTATUS status;
348 *_c = NULL;
350 c = talloc_zero(talloc_tos(), struct conn_struct_tos);
351 if (c == NULL) {
352 return NT_STATUS_NO_MEMORY;
355 ev = samba_tevent_context_init(c);
356 if (ev == NULL) {
357 TALLOC_FREE(c);
358 return NT_STATUS_NO_MEMORY;
361 become_root();
362 status = create_conn_struct_as_root(c,
364 msg,
365 &c->conn,
366 snum,
367 path,
368 session_info);
369 unbecome_root();
370 if (!NT_STATUS_IS_OK(status)) {
371 TALLOC_FREE(c);
372 return status;
375 talloc_set_destructor(c, conn_struct_tos_destructor);
377 *_c = c;
378 return NT_STATUS_OK;
381 /********************************************************
382 Fake up a connection struct for the VFS layer.
383 Note: this performs a vfs connect and CHANGES CWD !!!! JRA.
385 See also the comment for create_conn_struct_tos() above!
387 The CWD change is reverted by the destructor of
388 conn_struct_tos when the current talloc_tos() is destroyed.
389 *********************************************************/
390 NTSTATUS create_conn_struct_tos_cwd(struct messaging_context *msg,
391 int snum,
392 const char *path,
393 const struct auth_session_info *session_info,
394 struct conn_struct_tos **_c)
396 struct conn_struct_tos *c = NULL;
397 struct smb_filename smb_fname_connectpath = {0};
398 NTSTATUS status;
400 *_c = NULL;
402 status = create_conn_struct_tos(msg,
403 snum,
404 path,
405 session_info,
406 &c);
407 if (!NT_STATUS_IS_OK(status)) {
408 return status;
412 * Windows seems to insist on doing trans2getdfsreferral() calls on
413 * the IPC$ share as the anonymous user. If we try to chdir as that
414 * user we will fail.... WTF ? JRA.
417 c->oldcwd_fname = vfs_GetWd(c, c->conn);
418 if (c->oldcwd_fname == NULL) {
419 status = map_nt_error_from_unix(errno);
420 DEBUG(3, ("vfs_GetWd failed: %s\n", strerror(errno)));
421 TALLOC_FREE(c);
422 return status;
425 smb_fname_connectpath = (struct smb_filename) {
426 .base_name = c->conn->connectpath
429 if (vfs_ChDir(c->conn, &smb_fname_connectpath) != 0) {
430 status = map_nt_error_from_unix(errno);
431 DBG_NOTICE("Can't ChDir to new conn path %s. "
432 "Error was %s\n",
433 c->conn->connectpath, strerror(errno));
434 TALLOC_FREE(c->oldcwd_fname);
435 TALLOC_FREE(c);
436 return status;
439 *_c = c;
440 return NT_STATUS_OK;
443 /********************************************************
444 Fake up a connection struct for the VFS layer.
445 This takes an TALLOC_CTX and tevent_context from the
446 caller and the resulting connection_struct is stable
447 across the lifetime of mem_ctx and ev.
449 Note: this performs a vfs connect and changes cwd.
451 See also the comment for create_conn_struct_tos() above!
452 *********************************************************/
454 NTSTATUS create_conn_struct_cwd(TALLOC_CTX *mem_ctx,
455 struct tevent_context *ev,
456 struct messaging_context *msg,
457 const struct auth_session_info *session_info,
458 int snum,
459 const char *path,
460 struct connection_struct **c)
462 NTSTATUS status;
464 become_root();
465 status = create_conn_struct_as_root(mem_ctx,
467 msg,
469 snum,
470 path,
471 session_info);
472 unbecome_root();
473 return status;
476 static void shuffle_strlist(char **list, int count)
478 int i;
479 uint32_t r;
480 char *tmp;
482 for (i = count; i > 1; i--) {
483 r = generate_random() % i;
485 tmp = list[i-1];
486 list[i-1] = list[r];
487 list[r] = tmp;
491 /**********************************************************************
492 Parse the contents of a symlink to verify if it is an msdfs referral
493 A valid referral is of the form:
495 msdfs:server1\share1,server2\share2
496 msdfs:server1\share1\pathname,server2\share2\pathname
497 msdfs:server1/share1,server2/share2
498 msdfs:server1/share1/pathname,server2/share2/pathname.
500 Note that the alternate paths returned here must be of the canonicalized
501 form:
503 \server\share or
504 \server\share\path\to\file,
506 even in posix path mode. This is because we have no knowledge if the
507 server we're referring to understands posix paths.
508 **********************************************************************/
510 bool parse_msdfs_symlink(TALLOC_CTX *ctx,
511 bool shuffle_referrals,
512 const char *target,
513 struct referral **ppreflist,
514 size_t *prefcount)
516 char *temp = NULL;
517 char *prot;
518 char **alt_path = NULL;
519 size_t count = 0, i;
520 struct referral *reflist = NULL;
521 char *saveptr;
523 temp = talloc_strdup(ctx, target);
524 if (!temp) {
525 return false;
527 prot = strtok_r(temp, ":", &saveptr);
528 if (!prot) {
529 DEBUG(0,("parse_msdfs_symlink: invalid path !\n"));
530 TALLOC_FREE(temp);
531 return false;
534 alt_path = talloc_array(ctx, char *, MAX_REFERRAL_COUNT);
535 if (!alt_path) {
536 TALLOC_FREE(temp);
537 return false;
540 /* parse out the alternate paths */
541 while((count<MAX_REFERRAL_COUNT) &&
542 ((alt_path[count] = strtok_r(NULL, ",", &saveptr)) != NULL)) {
543 count++;
546 /* shuffle alternate paths */
547 if (shuffle_referrals) {
548 shuffle_strlist(alt_path, count);
551 DBG_DEBUG("count=%zu\n", count);
553 if (count) {
554 reflist = talloc_zero_array(ctx,
555 struct referral, count);
556 if(reflist == NULL) {
557 TALLOC_FREE(temp);
558 TALLOC_FREE(alt_path);
559 return false;
561 } else {
562 reflist = NULL;
565 for(i=0;i<count;i++) {
566 char *p;
568 /* Canonicalize link target.
569 * Replace all /'s in the path by a \ */
570 string_replace(alt_path[i], '/', '\\');
572 /* Remove leading '\\'s */
573 p = alt_path[i];
574 while (*p && (*p == '\\')) {
575 p++;
578 reflist[i].alternate_path = talloc_asprintf(reflist,
579 "\\%s",
581 if (!reflist[i].alternate_path) {
582 TALLOC_FREE(temp);
583 TALLOC_FREE(alt_path);
584 TALLOC_FREE(reflist);
585 return false;
588 reflist[i].proximity = 0;
589 reflist[i].ttl = REFERRAL_TTL;
590 DBG_DEBUG("Created alt path: %s\n",
591 reflist[i].alternate_path);
594 if (ppreflist != NULL) {
595 *ppreflist = reflist;
596 } else {
597 TALLOC_FREE(reflist);
599 if (prefcount != NULL) {
600 *prefcount = count;
602 TALLOC_FREE(temp);
603 TALLOC_FREE(alt_path);
604 return true;
607 /**********************************************************************
608 Returns true if the unix path is a valid msdfs symlink.
609 **********************************************************************/
611 bool is_msdfs_link(struct files_struct *dirfsp,
612 struct smb_filename *atname)
614 NTSTATUS status = SMB_VFS_READ_DFS_PATHAT(dirfsp->conn,
615 talloc_tos(),
616 dirfsp,
617 atname,
618 NULL,
619 NULL);
620 return (NT_STATUS_IS_OK(status));
623 /*****************************************************************
624 Used by other functions to decide if a dfs path is remote,
625 and to get the list of referred locations for that remote path.
627 consumedcntp: how much of the dfs path is being redirected. the client
628 should try the remaining path on the redirected server.
629 *****************************************************************/
631 static NTSTATUS dfs_path_lookup(TALLOC_CTX *ctx,
632 connection_struct *conn,
633 const char *dfspath, /* Incoming complete dfs path */
634 const char *reqpath, /* Parsed out remaining path. */
635 uint32_t ucf_flags,
636 size_t *consumedcntp,
637 struct referral **ppreflist,
638 size_t *preferral_count)
640 NTSTATUS status;
641 struct smb_filename *parent_smb_fname = NULL;
642 struct smb_filename *smb_fname_rel = NULL;
643 NTTIME twrp = 0;
644 char *local_pathname = NULL;
645 char *last_component = NULL;
646 char *atname = NULL;
647 size_t removed_components = 0;
648 bool posix = (ucf_flags & UCF_POSIX_PATHNAMES);
649 char *p = NULL;
650 char *canon_dfspath = NULL;
652 DBG_DEBUG("Conn path = %s reqpath = %s\n", conn->connectpath, reqpath);
654 local_pathname = talloc_strdup(ctx, reqpath);
655 if (local_pathname == NULL) {
656 status = NT_STATUS_NO_MEMORY;
657 goto out;
660 /* We know reqpath isn't a DFS path. */
661 ucf_flags &= ~UCF_DFS_PATHNAME;
663 if (ucf_flags & UCF_GMT_PATHNAME) {
664 extract_snapshot_token(local_pathname, &twrp);
665 ucf_flags &= ~UCF_GMT_PATHNAME;
669 * We should have been given a DFS path to resolve.
670 * This should return NT_STATUS_PATH_NOT_COVERED.
672 * Do a pathname walk, stripping off components
673 * until we get NT_STATUS_OK instead of
674 * NT_STATUS_PATH_NOT_COVERED.
676 * Fail on any other error.
679 for (;;) {
680 TALLOC_CTX *frame = NULL;
681 struct files_struct *dirfsp = NULL;
682 struct smb_filename *smb_fname_walk = NULL;
684 TALLOC_FREE(parent_smb_fname);
687 * Use a local stackframe as filename_convert_dirfsp()
688 * opens handles on the last two components in the path.
689 * Allow these to be freed as we step back through
690 * the local_pathname.
692 frame = talloc_stackframe();
693 status = filename_convert_dirfsp(frame,
694 conn,
695 local_pathname,
696 ucf_flags,
697 twrp,
698 &dirfsp,
699 &smb_fname_walk);
700 /* If we got a name, save it. */
701 if (smb_fname_walk != NULL) {
702 parent_smb_fname = talloc_move(ctx, &smb_fname_walk);
704 TALLOC_FREE(frame);
706 if (!NT_STATUS_EQUAL(status, NT_STATUS_PATH_NOT_COVERED)) {
708 * For any other status than NT_STATUS_PATH_NOT_COVERED
709 * (including NT_STATUS_OK) we exit the walk.
710 * If it's an error we catch it outside the loop.
712 break;
715 /* Step back one component and save it off as last_component. */
716 TALLOC_FREE(last_component);
717 p = strrchr(local_pathname, '/');
718 if (p == NULL) {
720 * We removed all components.
721 * Go around once more to make
722 * sure we can open the root '\0'.
724 last_component = talloc_strdup(ctx, local_pathname);
725 *local_pathname = '\0';
726 } else {
727 last_component = talloc_strdup(ctx, p+1);
728 *p = '\0';
730 if (last_component == NULL) {
731 status = NT_STATUS_NO_MEMORY;
732 goto out;
734 /* Integer wrap check. */
735 if (removed_components + 1 < removed_components) {
736 status = NT_STATUS_INVALID_PARAMETER;
737 goto out;
739 removed_components++;
742 if (!NT_STATUS_IS_OK(status)) {
743 DBG_DEBUG("dfspath = %s. reqpath = %s. Error %s.\n",
744 dfspath,
745 reqpath,
746 nt_errstr(status));
747 goto out;
750 if (parent_smb_fname->fsp == NULL) {
751 /* Unable to open parent. */
752 DBG_DEBUG("dfspath = %s. reqpath = %s. "
753 "Unable to open parent directory (%s).\n",
754 dfspath,
755 reqpath,
756 smb_fname_str_dbg(parent_smb_fname));
757 status = NT_STATUS_OBJECT_PATH_NOT_FOUND;
758 goto out;
761 if (removed_components == 0) {
763 * We never got NT_STATUS_PATH_NOT_COVERED.
764 * There was no DFS redirect.
766 DBG_DEBUG("dfspath = %s. reqpath = %s. "
767 "No removed components.\n",
768 dfspath,
769 reqpath);
770 status = NT_STATUS_OBJECT_PATH_NOT_FOUND;
771 goto out;
775 * One of the removed_components was the MSDFS link
776 * at the end. We need to count this in the resolved
777 * path below, so remove one from removed_components.
779 removed_components--;
782 * Now parent_smb_fname->fsp is the parent directory dirfsp,
783 * last_component is the untranslated MS-DFS link name.
784 * Search for it in the parent directory to get the real
785 * filename on disk.
787 status = get_real_filename_at(parent_smb_fname->fsp,
788 last_component,
789 ctx,
790 &atname);
792 if (!NT_STATUS_IS_OK(status)) {
793 DBG_DEBUG("dfspath = %s. reqpath = %s "
794 "get_real_filename_at(%s, %s) error (%s)\n",
795 dfspath,
796 reqpath,
797 smb_fname_str_dbg(parent_smb_fname),
798 last_component,
799 nt_errstr(status));
800 goto out;
803 smb_fname_rel = synthetic_smb_fname(ctx,
804 atname,
805 NULL,
806 NULL,
807 twrp,
808 posix ? SMB_FILENAME_POSIX_PATH : 0);
809 if (smb_fname_rel == NULL) {
810 status = NT_STATUS_NO_MEMORY;
811 goto out;
814 /* Get the referral to return. */
815 status = SMB_VFS_READ_DFS_PATHAT(conn,
816 ctx,
817 parent_smb_fname->fsp,
818 smb_fname_rel,
819 ppreflist,
820 preferral_count);
821 if (!NT_STATUS_IS_OK(status)) {
822 DBG_DEBUG("dfspath = %s. reqpath = %s. "
823 "SMB_VFS_READ_DFS_PATHAT(%s, %s) error (%s)\n",
824 dfspath,
825 reqpath,
826 smb_fname_str_dbg(parent_smb_fname),
827 smb_fname_str_dbg(smb_fname_rel),
828 nt_errstr(status));
829 goto out;
833 * Now we must work out how much of the
834 * given pathname we consumed.
836 canon_dfspath = talloc_strdup(ctx, dfspath);
837 if (!canon_dfspath) {
838 status = NT_STATUS_NO_MEMORY;
839 goto out;
841 /* Canonicalize the raw dfspath. */
842 string_replace(canon_dfspath, '\\', '/');
845 * reqpath comes out of parse_dfs_path(), so it has
846 * no trailing backslash. Make sure that canon_dfspath hasn't either.
848 trim_char(canon_dfspath, 0, '/');
850 DBG_DEBUG("Unconsumed path: %s\n", canon_dfspath);
852 while (removed_components > 0) {
853 p = strrchr(canon_dfspath, '/');
854 if (p != NULL) {
855 *p = '\0';
857 removed_components--;
858 if (p == NULL && removed_components != 0) {
859 DBG_ERR("Component mismatch. path = %s, "
860 "%zu components left\n",
861 canon_dfspath,
862 removed_components);
863 status = NT_STATUS_OBJECT_PATH_NOT_FOUND;
864 goto out;
867 *consumedcntp = strlen(canon_dfspath);
868 DBG_DEBUG("Path consumed: %s (%zu)\n", canon_dfspath, *consumedcntp);
869 status = NT_STATUS_OK;
871 out:
873 TALLOC_FREE(parent_smb_fname);
874 TALLOC_FREE(local_pathname);
875 TALLOC_FREE(last_component);
876 TALLOC_FREE(atname);
877 TALLOC_FREE(smb_fname_rel);
878 TALLOC_FREE(canon_dfspath);
879 return status;
882 /**********************************************************************
883 Return a self referral.
884 **********************************************************************/
886 static NTSTATUS self_ref(TALLOC_CTX *ctx,
887 const char *dfs_path,
888 struct junction_map *jucn,
889 size_t *consumedcntp,
890 bool *self_referralp)
892 struct referral *ref;
894 *self_referralp = True;
896 jucn->referral_count = 1;
897 if((ref = talloc_zero(ctx, struct referral)) == NULL) {
898 return NT_STATUS_NO_MEMORY;
901 ref->alternate_path = talloc_strdup(ctx, dfs_path);
902 if (!ref->alternate_path) {
903 TALLOC_FREE(ref);
904 return NT_STATUS_NO_MEMORY;
906 ref->proximity = 0;
907 ref->ttl = REFERRAL_TTL;
908 jucn->referral_list = ref;
909 *consumedcntp = strlen(dfs_path);
910 return NT_STATUS_OK;
913 /**********************************************************************
914 Gets valid referrals for a dfs path and fills up the
915 junction_map structure.
916 **********************************************************************/
918 NTSTATUS get_referred_path(TALLOC_CTX *ctx,
919 struct auth_session_info *session_info,
920 const char *dfs_path,
921 const struct tsocket_address *remote_address,
922 const struct tsocket_address *local_address,
923 struct junction_map *jucn,
924 size_t *consumedcntp,
925 bool *self_referralp)
927 TALLOC_CTX *frame = talloc_stackframe();
928 const struct loadparm_substitution *lp_sub =
929 loadparm_s3_global_substitution();
930 struct conn_struct_tos *c = NULL;
931 struct connection_struct *conn = NULL;
932 char *servicename = NULL;
933 char *reqpath = NULL;
934 int snum;
935 NTSTATUS status = NT_STATUS_NOT_FOUND;
937 *self_referralp = False;
939 status = parse_dfs_path_strict(
940 frame,
941 dfs_path,
942 NULL, /* hostname */
943 &servicename,
944 &reqpath);
945 if (!NT_STATUS_IS_OK(status)) {
946 TALLOC_FREE(frame);
947 return status;
950 /* Path referrals are always non-POSIX. */
951 status = check_path_syntax(reqpath, false);
952 if (!NT_STATUS_IS_OK(status)) {
953 TALLOC_FREE(frame);
954 return status;
957 jucn->service_name = talloc_strdup(ctx, servicename);
958 jucn->volume_name = talloc_strdup(ctx, reqpath);
959 if (!jucn->service_name || !jucn->volume_name) {
960 TALLOC_FREE(frame);
961 return NT_STATUS_NO_MEMORY;
964 /* Verify the share is a dfs root */
965 snum = lp_servicenumber(jucn->service_name);
966 if(snum < 0) {
967 char *service_name = NULL;
968 if ((snum = find_service(ctx, jucn->service_name, &service_name)) < 0) {
969 TALLOC_FREE(frame);
970 return NT_STATUS_NOT_FOUND;
972 if (!service_name) {
973 TALLOC_FREE(frame);
974 return NT_STATUS_NO_MEMORY;
976 TALLOC_FREE(jucn->service_name);
977 jucn->service_name = talloc_strdup(ctx, service_name);
978 if (!jucn->service_name) {
979 TALLOC_FREE(frame);
980 return NT_STATUS_NO_MEMORY;
984 if (!lp_msdfs_root(snum) && (*lp_msdfs_proxy(talloc_tos(), lp_sub, snum) == '\0')) {
985 DEBUG(3,("get_referred_path: |%s| in dfs path %s is not "
986 "a dfs root.\n",
987 servicename, dfs_path));
988 TALLOC_FREE(frame);
989 return NT_STATUS_NOT_FOUND;
993 * Self referrals are tested with a anonymous IPC connection and
994 * a GET_DFS_REFERRAL call to \\server\share. (which means
995 * dp.reqpath[0] points to an empty string). create_conn_struct cd's
996 * into the directory and will fail if it cannot (as the anonymous
997 * user). Cope with this.
1000 if (reqpath[0] == '\0') {
1001 char *tmp;
1002 struct referral *ref;
1003 size_t refcount;
1005 if (*lp_msdfs_proxy(talloc_tos(), lp_sub, snum) == '\0') {
1006 TALLOC_FREE(frame);
1007 return self_ref(ctx,
1008 dfs_path,
1009 jucn,
1010 consumedcntp,
1011 self_referralp);
1015 * It's an msdfs proxy share. Redirect to
1016 * the configured target share.
1019 tmp = talloc_asprintf(frame, "msdfs:%s",
1020 lp_msdfs_proxy(frame, lp_sub, snum));
1021 if (tmp == NULL) {
1022 TALLOC_FREE(frame);
1023 return NT_STATUS_NO_MEMORY;
1026 if (!parse_msdfs_symlink(ctx,
1027 lp_msdfs_shuffle_referrals(snum),
1028 tmp,
1029 &ref,
1030 &refcount)) {
1031 TALLOC_FREE(frame);
1032 return NT_STATUS_INVALID_PARAMETER;
1034 jucn->referral_count = refcount;
1035 jucn->referral_list = ref;
1036 *consumedcntp = strlen(dfs_path);
1037 TALLOC_FREE(frame);
1038 return NT_STATUS_OK;
1041 status = create_conn_struct_tos_cwd(global_messaging_context(),
1042 snum,
1043 lp_path(frame, lp_sub, snum),
1044 session_info,
1045 &c);
1046 if (!NT_STATUS_IS_OK(status)) {
1047 TALLOC_FREE(frame);
1048 return status;
1050 conn = c->conn;
1053 * TODO
1055 * The remote and local address should be passed down to
1056 * create_conn_struct_cwd.
1058 if (conn->sconn->remote_address == NULL) {
1059 conn->sconn->remote_address =
1060 tsocket_address_copy(remote_address, conn->sconn);
1061 if (conn->sconn->remote_address == NULL) {
1062 TALLOC_FREE(frame);
1063 return NT_STATUS_NO_MEMORY;
1066 if (conn->sconn->local_address == NULL) {
1067 conn->sconn->local_address =
1068 tsocket_address_copy(local_address, conn->sconn);
1069 if (conn->sconn->local_address == NULL) {
1070 TALLOC_FREE(frame);
1071 return NT_STATUS_NO_MEMORY;
1075 status = dfs_path_lookup(ctx,
1076 conn,
1077 dfs_path,
1078 reqpath,
1079 0, /* ucf_flags */
1080 consumedcntp,
1081 &jucn->referral_list,
1082 &jucn->referral_count);
1084 if (!NT_STATUS_IS_OK(status)) {
1085 DBG_NOTICE("No valid referrals for path %s (%s)\n",
1086 dfs_path,
1087 nt_errstr(status));
1090 TALLOC_FREE(frame);
1091 return status;
1094 /******************************************************************
1095 Set up the DFS referral for the dfs pathname. This call returns
1096 the amount of the path covered by this server, and where the
1097 client should be redirected to. This is the meat of the
1098 TRANS2_GET_DFS_REFERRAL call.
1099 ******************************************************************/
1101 int setup_dfs_referral(connection_struct *orig_conn,
1102 const char *dfs_path,
1103 int max_referral_level,
1104 char **ppdata, NTSTATUS *pstatus)
1106 char *pdata = *ppdata;
1107 int reply_size = 0;
1108 struct dfs_GetDFSReferral *r;
1109 DATA_BLOB blob = data_blob_null;
1110 NTSTATUS status;
1111 enum ndr_err_code ndr_err;
1113 r = talloc_zero(talloc_tos(), struct dfs_GetDFSReferral);
1114 if (r == NULL) {
1115 *pstatus = NT_STATUS_NO_MEMORY;
1116 return -1;
1119 r->in.req.max_referral_level = max_referral_level;
1120 r->in.req.servername = talloc_strdup(r, dfs_path);
1121 if (r->in.req.servername == NULL) {
1122 talloc_free(r);
1123 *pstatus = NT_STATUS_NO_MEMORY;
1124 return -1;
1127 status = SMB_VFS_GET_DFS_REFERRALS(orig_conn, r);
1128 if (!NT_STATUS_IS_OK(status)) {
1129 talloc_free(r);
1130 *pstatus = status;
1131 return -1;
1134 ndr_err = ndr_push_struct_blob(&blob, r,
1135 r->out.resp,
1136 (ndr_push_flags_fn_t)ndr_push_dfs_referral_resp);
1137 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1138 TALLOC_FREE(r);
1139 *pstatus = NT_STATUS_INVALID_PARAMETER;
1140 return -1;
1143 pdata = (char *)SMB_REALLOC(pdata, blob.length);
1144 if(pdata == NULL) {
1145 TALLOC_FREE(r);
1146 DEBUG(0,("referral setup:"
1147 "malloc failed for Realloc!\n"));
1148 return -1;
1150 *ppdata = pdata;
1151 reply_size = blob.length;
1152 memcpy(pdata, blob.data, blob.length);
1153 TALLOC_FREE(r);
1155 *pstatus = NT_STATUS_OK;
1156 return reply_size;
1159 /**********************************************************************
1160 The following functions are called by the NETDFS RPC pipe functions
1161 **********************************************************************/
1163 /*********************************************************************
1164 Creates a junction structure from a DFS pathname
1165 **********************************************************************/
1167 bool create_junction(TALLOC_CTX *ctx,
1168 const char *dfs_path,
1169 struct junction_map *jucn)
1171 const struct loadparm_substitution *lp_sub =
1172 loadparm_s3_global_substitution();
1173 int snum;
1174 char *servicename = NULL;
1175 char *reqpath = NULL;
1176 NTSTATUS status;
1178 status = parse_dfs_path_strict(
1179 ctx,
1180 dfs_path,
1181 NULL,
1182 &servicename,
1183 &reqpath);
1184 if (!NT_STATUS_IS_OK(status)) {
1185 return False;
1188 /* Check for a non-DFS share */
1189 snum = lp_servicenumber(servicename);
1191 if(snum < 0 || !lp_msdfs_root(snum)) {
1192 DEBUG(4,("create_junction: %s is not an msdfs root.\n",
1193 servicename));
1194 return False;
1197 /* Junction create paths are always non-POSIX. */
1198 status = check_path_syntax(reqpath, false);
1199 if (!NT_STATUS_IS_OK(status)) {
1200 return false;
1203 jucn->service_name = talloc_strdup(ctx, servicename);
1204 jucn->volume_name = talloc_strdup(ctx, reqpath);
1205 jucn->comment = lp_comment(ctx, lp_sub, snum);
1207 if (!jucn->service_name || !jucn->volume_name || ! jucn->comment) {
1208 return False;
1210 return True;
1213 /**********************************************************************
1214 Forms a valid Unix pathname from the junction
1215 **********************************************************************/
1217 static bool junction_to_local_path_tos(const struct junction_map *jucn,
1218 struct auth_session_info *session_info,
1219 char **pp_path_out,
1220 connection_struct **conn_out)
1222 const struct loadparm_substitution *lp_sub =
1223 loadparm_s3_global_substitution();
1224 struct conn_struct_tos *c = NULL;
1225 int snum;
1226 char *path_out = NULL;
1227 NTSTATUS status;
1229 snum = lp_servicenumber(jucn->service_name);
1230 if(snum < 0) {
1231 return False;
1233 status = create_conn_struct_tos_cwd(global_messaging_context(),
1234 snum,
1235 lp_path(talloc_tos(), lp_sub, snum),
1236 session_info,
1237 &c);
1238 if (!NT_STATUS_IS_OK(status)) {
1239 return False;
1242 path_out = talloc_asprintf(c,
1243 "%s/%s",
1244 lp_path(talloc_tos(), lp_sub, snum),
1245 jucn->volume_name);
1246 if (path_out == NULL) {
1247 TALLOC_FREE(c);
1248 return False;
1250 *pp_path_out = path_out;
1251 *conn_out = c->conn;
1252 return True;
1256 * Create a msdfs string in Samba format we can store
1257 * in a filesystem object (currently a symlink).
1260 char *msdfs_link_string(TALLOC_CTX *ctx,
1261 const struct referral *reflist,
1262 size_t referral_count)
1264 char *refpath = NULL;
1265 bool insert_comma = false;
1266 char *msdfs_link = NULL;
1267 size_t i;
1269 /* Form the msdfs_link contents */
1270 msdfs_link = talloc_strdup(ctx, "msdfs:");
1271 if (msdfs_link == NULL) {
1272 goto err;
1275 for( i= 0; i < referral_count; i++) {
1276 refpath = talloc_strdup(ctx, reflist[i].alternate_path);
1278 if (refpath == NULL) {
1279 goto err;
1282 /* Alternate paths always use Windows separators. */
1283 trim_char(refpath, '\\', '\\');
1284 if (*refpath == '\0') {
1285 if (i == 0) {
1286 insert_comma = false;
1288 continue;
1290 if (i > 0 && insert_comma) {
1291 msdfs_link = talloc_asprintf_append_buffer(msdfs_link,
1292 ",%s",
1293 refpath);
1294 } else {
1295 msdfs_link = talloc_asprintf_append_buffer(msdfs_link,
1296 "%s",
1297 refpath);
1300 if (msdfs_link == NULL) {
1301 goto err;
1304 if (!insert_comma) {
1305 insert_comma = true;
1308 TALLOC_FREE(refpath);
1311 return msdfs_link;
1313 err:
1315 TALLOC_FREE(refpath);
1316 TALLOC_FREE(msdfs_link);
1317 return NULL;
1320 bool create_msdfs_link(const struct junction_map *jucn,
1321 struct auth_session_info *session_info)
1323 TALLOC_CTX *frame = talloc_stackframe();
1324 char *path = NULL;
1325 connection_struct *conn;
1326 struct smb_filename *smb_fname = NULL;
1327 struct smb_filename *parent_fname = NULL;
1328 struct smb_filename *at_fname = NULL;
1329 bool ok;
1330 NTSTATUS status;
1331 bool ret = false;
1333 ok = junction_to_local_path_tos(jucn, session_info, &path, &conn);
1334 if (!ok) {
1335 goto out;
1338 if (!CAN_WRITE(conn)) {
1339 const struct loadparm_substitution *lp_sub =
1340 loadparm_s3_global_substitution();
1341 int snum = lp_servicenumber(jucn->service_name);
1343 DBG_WARNING("Can't create DFS entry on read-only share %s\n",
1344 lp_servicename(frame, lp_sub, snum));
1345 goto out;
1348 smb_fname = synthetic_smb_fname(frame,
1349 path,
1350 NULL,
1351 NULL,
1354 if (smb_fname == NULL) {
1355 goto out;
1358 status = parent_pathref(frame,
1359 conn->cwd_fsp,
1360 smb_fname,
1361 &parent_fname,
1362 &at_fname);
1363 if (!NT_STATUS_IS_OK(status)) {
1364 goto out;
1367 status = SMB_VFS_CREATE_DFS_PATHAT(conn,
1368 parent_fname->fsp,
1369 at_fname,
1370 jucn->referral_list,
1371 jucn->referral_count);
1372 if (!NT_STATUS_IS_OK(status)) {
1373 if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_COLLISION)) {
1374 int retval = SMB_VFS_UNLINKAT(conn,
1375 parent_fname->fsp,
1376 at_fname,
1378 if (retval != 0) {
1379 goto out;
1382 status = SMB_VFS_CREATE_DFS_PATHAT(conn,
1383 parent_fname->fsp,
1384 at_fname,
1385 jucn->referral_list,
1386 jucn->referral_count);
1387 if (!NT_STATUS_IS_OK(status)) {
1388 DBG_WARNING("SMB_VFS_CREATE_DFS_PATHAT failed "
1389 "%s - Error: %s\n",
1390 path,
1391 nt_errstr(status));
1392 goto out;
1396 ret = true;
1398 out:
1399 TALLOC_FREE(frame);
1400 return ret;
1403 bool remove_msdfs_link(const struct junction_map *jucn,
1404 struct auth_session_info *session_info)
1406 TALLOC_CTX *frame = talloc_stackframe();
1407 char *path = NULL;
1408 connection_struct *conn;
1409 bool ret = False;
1410 struct smb_filename *smb_fname;
1411 struct smb_filename *parent_fname = NULL;
1412 struct smb_filename *at_fname = NULL;
1413 NTSTATUS status;
1414 bool ok;
1415 int retval;
1417 ok = junction_to_local_path_tos(jucn, session_info, &path, &conn);
1418 if (!ok) {
1419 TALLOC_FREE(frame);
1420 return false;
1423 if (!CAN_WRITE(conn)) {
1424 const struct loadparm_substitution *lp_sub =
1425 loadparm_s3_global_substitution();
1426 int snum = lp_servicenumber(jucn->service_name);
1428 DBG_WARNING("Can't remove DFS entry on read-only share %s\n",
1429 lp_servicename(frame, lp_sub, snum));
1430 TALLOC_FREE(frame);
1431 return false;
1434 smb_fname = synthetic_smb_fname(frame,
1435 path,
1436 NULL,
1437 NULL,
1440 if (smb_fname == NULL) {
1441 TALLOC_FREE(frame);
1442 errno = ENOMEM;
1443 return false;
1446 status = parent_pathref(frame,
1447 conn->cwd_fsp,
1448 smb_fname,
1449 &parent_fname,
1450 &at_fname);
1451 if (!NT_STATUS_IS_OK(status)) {
1452 TALLOC_FREE(frame);
1453 return false;
1456 retval = SMB_VFS_UNLINKAT(conn,
1457 parent_fname->fsp,
1458 at_fname,
1460 if (retval == 0) {
1461 ret = True;
1464 TALLOC_FREE(frame);
1465 return ret;
1468 /*********************************************************************
1469 Return the number of DFS links at the root of this share.
1470 *********************************************************************/
1472 static size_t count_dfs_links(TALLOC_CTX *ctx,
1473 struct auth_session_info *session_info,
1474 int snum)
1476 TALLOC_CTX *frame = talloc_stackframe();
1477 const struct loadparm_substitution *lp_sub =
1478 loadparm_s3_global_substitution();
1479 size_t cnt = 0;
1480 const char *dname = NULL;
1481 char *talloced = NULL;
1482 const char *connect_path = lp_path(frame, lp_sub, snum);
1483 const char *msdfs_proxy = lp_msdfs_proxy(frame, lp_sub, snum);
1484 struct conn_struct_tos *c = NULL;
1485 connection_struct *conn = NULL;
1486 NTSTATUS status;
1487 struct smb_filename *smb_fname = NULL;
1488 struct smb_Dir *dir_hnd = NULL;
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, &talloced)) != NULL) {
1543 struct smb_filename *smb_dname =
1544 synthetic_smb_fname(frame,
1545 dname,
1546 NULL,
1547 NULL,
1550 if (smb_dname == NULL) {
1551 goto out;
1553 if (is_msdfs_link(dir_hnd_fetch_fsp(dir_hnd), smb_dname)) {
1554 if (cnt + 1 < cnt) {
1555 cnt = 0;
1556 goto out;
1558 cnt++;
1560 TALLOC_FREE(talloced);
1561 TALLOC_FREE(smb_dname);
1564 out:
1565 TALLOC_FREE(frame);
1566 return cnt;
1569 /*********************************************************************
1570 *********************************************************************/
1572 static int form_junctions(TALLOC_CTX *ctx,
1573 struct auth_session_info *session_info,
1574 int snum,
1575 struct junction_map *jucn,
1576 size_t jn_remain)
1578 TALLOC_CTX *frame = talloc_stackframe();
1579 const struct loadparm_substitution *lp_sub =
1580 loadparm_s3_global_substitution();
1581 size_t cnt = 0;
1582 const char *dname = NULL;
1583 char *talloced = NULL;
1584 const char *connect_path = lp_path(frame, lp_sub, snum);
1585 char *service_name = lp_servicename(frame, lp_sub, snum);
1586 const char *msdfs_proxy = lp_msdfs_proxy(frame, lp_sub, snum);
1587 struct conn_struct_tos *c = NULL;
1588 connection_struct *conn = NULL;
1589 struct referral *ref = NULL;
1590 struct smb_filename *smb_fname = NULL;
1591 struct smb_Dir *dir_hnd = NULL;
1592 NTSTATUS status;
1594 if (jn_remain == 0) {
1595 TALLOC_FREE(frame);
1596 return 0;
1599 if(*connect_path == '\0') {
1600 TALLOC_FREE(frame);
1601 return 0;
1605 * Fake up a connection struct for the VFS layer.
1608 status = create_conn_struct_tos_cwd(global_messaging_context(),
1609 snum,
1610 connect_path,
1611 session_info,
1612 &c);
1613 if (!NT_STATUS_IS_OK(status)) {
1614 DEBUG(3, ("create_conn_struct failed: %s\n",
1615 nt_errstr(status)));
1616 TALLOC_FREE(frame);
1617 return 0;
1619 conn = c->conn;
1621 /* form a junction for the msdfs root - convention
1622 DO NOT REMOVE THIS: NT clients will not work with us
1623 if this is not present
1625 jucn[cnt].service_name = talloc_strdup(ctx,service_name);
1626 jucn[cnt].volume_name = talloc_strdup(ctx, "");
1627 if (!jucn[cnt].service_name || !jucn[cnt].volume_name) {
1628 goto out;
1630 jucn[cnt].comment = "";
1631 jucn[cnt].referral_count = 1;
1633 ref = jucn[cnt].referral_list = talloc_zero(ctx, struct referral);
1634 if (jucn[cnt].referral_list == NULL) {
1635 goto out;
1638 ref->proximity = 0;
1639 ref->ttl = REFERRAL_TTL;
1640 if (*msdfs_proxy != '\0') {
1641 ref->alternate_path = talloc_strdup(ctx,
1642 msdfs_proxy);
1643 } else {
1644 ref->alternate_path = talloc_asprintf(ctx,
1645 "\\\\%s\\%s",
1646 get_local_machine_name(),
1647 service_name);
1650 if (!ref->alternate_path) {
1651 goto out;
1653 cnt++;
1655 /* Don't enumerate if we're an msdfs proxy. */
1656 if (*msdfs_proxy != '\0') {
1657 goto out;
1660 smb_fname = synthetic_smb_fname(frame,
1661 ".",
1662 NULL,
1663 NULL,
1666 if (smb_fname == NULL) {
1667 goto out;
1670 /* Now enumerate all dfs links */
1671 status = OpenDir(frame,
1672 conn,
1673 smb_fname,
1674 NULL,
1676 &dir_hnd);
1677 if (!NT_STATUS_IS_OK(status)) {
1678 errno = map_errno_from_nt_status(status);
1679 goto out;
1682 while ((dname = ReadDirName(dir_hnd, &talloced)) != NULL) {
1683 struct smb_filename *smb_dname = NULL;
1685 if (cnt >= jn_remain) {
1686 DEBUG(2, ("form_junctions: ran out of MSDFS "
1687 "junction slots\n"));
1688 TALLOC_FREE(talloced);
1689 goto out;
1691 smb_dname = synthetic_smb_fname(talloc_tos(),
1692 dname,
1693 NULL,
1694 NULL,
1697 if (smb_dname == NULL) {
1698 TALLOC_FREE(talloced);
1699 goto out;
1702 status = SMB_VFS_READ_DFS_PATHAT(conn,
1703 ctx,
1704 conn->cwd_fsp,
1705 smb_dname,
1706 &jucn[cnt].referral_list,
1707 &jucn[cnt].referral_count);
1709 if (NT_STATUS_IS_OK(status)) {
1710 jucn[cnt].service_name = talloc_strdup(ctx,
1711 service_name);
1712 jucn[cnt].volume_name = talloc_strdup(ctx, dname);
1713 if (!jucn[cnt].service_name || !jucn[cnt].volume_name) {
1714 TALLOC_FREE(talloced);
1715 goto out;
1717 jucn[cnt].comment = "";
1718 cnt++;
1720 TALLOC_FREE(talloced);
1721 TALLOC_FREE(smb_dname);
1724 out:
1725 TALLOC_FREE(frame);
1726 return cnt;
1729 struct junction_map *enum_msdfs_links(TALLOC_CTX *ctx,
1730 struct auth_session_info *session_info,
1731 size_t *p_num_jn)
1733 struct junction_map *jn = NULL;
1734 int i=0;
1735 size_t jn_count = 0;
1736 int sharecount = 0;
1738 *p_num_jn = 0;
1739 if(!lp_host_msdfs()) {
1740 return NULL;
1743 /* Ensure all the usershares are loaded. */
1744 become_root();
1745 load_registry_shares();
1746 sharecount = load_usershare_shares(NULL, connections_snum_used);
1747 unbecome_root();
1749 for(i=0;i < sharecount;i++) {
1750 if(lp_msdfs_root(i)) {
1751 jn_count += count_dfs_links(ctx, session_info, i);
1754 if (jn_count == 0) {
1755 return NULL;
1757 jn = talloc_array(ctx, struct junction_map, jn_count);
1758 if (!jn) {
1759 return NULL;
1761 for(i=0; i < sharecount; i++) {
1762 if (*p_num_jn >= jn_count) {
1763 break;
1765 if(lp_msdfs_root(i)) {
1766 *p_num_jn += form_junctions(ctx,
1767 session_info,
1769 &jn[*p_num_jn],
1770 jn_count - *p_num_jn);
1773 return jn;