tests/krb5: Add more encryption type constants
[Samba.git] / source3 / smbd / msdfs.c
blob86dc3f4dd74294f52c5783b8f171ea73ab7beae2
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 \hostname\service\reqpath
41 into the dfs_path structure.
42 If POSIX pathnames is true, the pathname may also be of the
43 form /hostname/service/reqpath.
44 We cope with either here.
46 Unfortunately, due to broken clients who might set the
47 SVAL(inbuf,smb_flg2) & FLAGS2_DFS_PATHNAMES bit and then
48 send a local path, we have to cope with that too....
50 If conn != NULL then ensure the provided service is
51 the one pointed to by the connection.
53 This version does everything using pointers within one copy of the
54 pathname string, talloced on the struct dfs_path pointer (which
55 must be talloced). This may be too clever to live....
56 JRA.
57 **********************************************************************/
59 static NTSTATUS parse_dfs_path(connection_struct *conn,
60 const char *pathname,
61 bool allow_wcards,
62 bool allow_broken_path,
63 struct dfs_path *pdp) /* MUST BE TALLOCED */
65 const struct loadparm_substitution *lp_sub =
66 loadparm_s3_global_substitution();
67 char *pathname_local;
68 char *p,*temp;
69 char *servicename;
70 char *eos_ptr;
71 NTSTATUS status = NT_STATUS_OK;
72 char sepchar;
74 ZERO_STRUCTP(pdp);
77 * This is the only talloc we should need to do
78 * on the struct dfs_path. All the pointers inside
79 * it should point to offsets within this string.
82 pathname_local = talloc_strdup(pdp, pathname);
83 if (!pathname_local) {
84 return NT_STATUS_NO_MEMORY;
86 /* Get a pointer to the terminating '\0' */
87 eos_ptr = &pathname_local[strlen(pathname_local)];
88 p = temp = pathname_local;
91 * Non-broken DFS paths *must* start with the
92 * path separator. For Windows this is always '\\',
93 * for posix paths this is always '/'.
96 if (*pathname == '/') {
97 pdp->posix_path = true;
98 sepchar = '/';
99 } else {
100 pdp->posix_path = false;
101 sepchar = '\\';
104 if (allow_broken_path && (*pathname != sepchar)) {
105 DEBUG(10,("parse_dfs_path: path %s doesn't start with %c\n",
106 pathname, sepchar ));
108 * Possibly client sent a local path by mistake.
109 * Try and convert to a local path.
110 * Note that this is an SMB1-only fallback
111 * to cope with known broken SMB1 clients.
114 pdp->hostname = eos_ptr; /* "" */
115 pdp->servicename = eos_ptr; /* "" */
117 /* We've got no info about separators. */
118 pdp->posix_path = lp_posix_pathnames();
119 p = temp;
120 DEBUG(10,("parse_dfs_path: trying to convert %s to a "
121 "local path\n",
122 temp));
123 goto local_path;
127 * Safe to use on talloc'ed string as it only shrinks.
128 * It also doesn't affect the eos_ptr.
130 trim_char(temp,sepchar,sepchar);
132 DEBUG(10,("parse_dfs_path: temp = |%s| after trimming %c's\n",
133 temp, sepchar));
135 /* Now tokenize. */
136 /* Parse out hostname. */
137 p = strchr_m(temp,sepchar);
138 if(p == NULL) {
139 DEBUG(10,("parse_dfs_path: can't parse hostname from path %s\n",
140 temp));
142 * Possibly client sent a local path by mistake.
143 * Try and convert to a local path.
146 pdp->hostname = eos_ptr; /* "" */
147 pdp->servicename = eos_ptr; /* "" */
149 p = temp;
150 DEBUG(10,("parse_dfs_path: trying to convert %s "
151 "to a local path\n",
152 temp));
153 goto local_path;
155 *p = '\0';
156 pdp->hostname = temp;
158 DEBUG(10,("parse_dfs_path: hostname: %s\n",pdp->hostname));
160 /* Parse out servicename. */
161 servicename = p+1;
162 p = strchr_m(servicename,sepchar);
163 if (p) {
164 *p = '\0';
167 /* Is this really our servicename ? */
168 if (conn && !( strequal(servicename, lp_servicename(talloc_tos(), lp_sub, SNUM(conn)))
169 || (strequal(servicename, HOMES_NAME)
170 && strequal(lp_servicename(talloc_tos(), lp_sub, SNUM(conn)),
171 get_current_username()) )) ) {
172 DEBUG(10,("parse_dfs_path: %s is not our servicename\n",
173 servicename));
176 * Possibly client sent a local path by mistake.
177 * Try and convert to a local path.
180 pdp->hostname = eos_ptr; /* "" */
181 pdp->servicename = eos_ptr; /* "" */
183 /* Repair the path - replace the sepchar's
184 we nulled out */
185 servicename--;
186 *servicename = sepchar;
187 if (p) {
188 *p = sepchar;
191 p = temp;
192 DEBUG(10,("parse_dfs_path: trying to convert %s "
193 "to a local path\n",
194 temp));
195 goto local_path;
198 pdp->servicename = servicename;
200 DEBUG(10,("parse_dfs_path: servicename: %s\n",pdp->servicename));
202 if(p == NULL) {
203 /* Client sent self referral \server\share. */
204 pdp->reqpath = eos_ptr; /* "" */
205 return NT_STATUS_OK;
208 p++;
210 local_path:
212 pdp->reqpath = p;
214 /* Rest is reqpath. */
215 if (pdp->posix_path) {
216 status = check_path_syntax_posix(pdp->reqpath);
217 } else {
218 status = check_path_syntax(pdp->reqpath);
221 if (!NT_STATUS_IS_OK(status)) {
222 DEBUG(10,("parse_dfs_path: '%s' failed with %s\n",
223 p, nt_errstr(status) ));
224 return status;
227 DEBUG(10,("parse_dfs_path: rest of the path: %s\n",pdp->reqpath));
228 return NT_STATUS_OK;
231 /********************************************************
232 Fake up a connection struct for the VFS layer, for use in
233 applications (such as the python bindings), that do not want the
234 global working directory changed under them.
236 SMB_VFS_CONNECT requires root privileges.
237 *********************************************************/
239 static NTSTATUS create_conn_struct_as_root(TALLOC_CTX *ctx,
240 struct tevent_context *ev,
241 struct messaging_context *msg,
242 connection_struct **pconn,
243 int snum,
244 const char *path,
245 const struct auth_session_info *session_info)
247 connection_struct *conn;
248 char *connpath;
249 const char *vfs_user;
250 struct smbd_server_connection *sconn;
251 const char *servicename = lp_const_servicename(snum);
252 bool ok;
254 sconn = talloc_zero(ctx, struct smbd_server_connection);
255 if (sconn == NULL) {
256 return NT_STATUS_NO_MEMORY;
259 sconn->ev_ctx = ev;
260 sconn->msg_ctx = msg;
262 conn = conn_new(sconn);
263 if (conn == NULL) {
264 TALLOC_FREE(sconn);
265 return NT_STATUS_NO_MEMORY;
268 /* Now we have conn, we need to make sconn a child of conn,
269 * for a proper talloc tree */
270 talloc_steal(conn, sconn);
272 if (snum == -1 && servicename == NULL) {
273 servicename = "Unknown Service (snum == -1)";
276 connpath = talloc_strdup(conn, path);
277 if (!connpath) {
278 TALLOC_FREE(conn);
279 return NT_STATUS_NO_MEMORY;
281 connpath = talloc_string_sub(conn,
282 connpath,
283 "%S",
284 servicename);
285 if (!connpath) {
286 TALLOC_FREE(conn);
287 return NT_STATUS_NO_MEMORY;
290 /* needed for smbd_vfs_init() */
292 conn->params->service = snum;
293 conn->cnum = TID_FIELD_INVALID;
295 SMB_ASSERT(session_info != NULL);
297 conn->session_info = copy_session_info(conn, session_info);
298 if (conn->session_info == NULL) {
299 DBG_ERR("copy_serverinfo failed\n");
300 TALLOC_FREE(conn);
301 return NT_STATUS_NO_MEMORY;
304 /* unix_info could be NULL in session_info */
305 if (conn->session_info->unix_info != NULL) {
306 vfs_user = conn->session_info->unix_info->unix_name;
307 } else {
308 vfs_user = get_current_username();
311 conn_setup_case_options(conn);
313 set_conn_connectpath(conn, connpath);
316 * New code to check if there's a share security descriptor
317 * added from NT server manager. This is done after the
318 * smb.conf checks are done as we need a uid and token. JRA.
321 share_access_check(conn->session_info->security_token,
322 servicename,
323 MAXIMUM_ALLOWED_ACCESS,
324 &conn->share_access);
326 if ((conn->share_access & FILE_WRITE_DATA) == 0) {
327 if ((conn->share_access & FILE_READ_DATA) == 0) {
328 /* No access, read or write. */
329 DBG_WARNING("connection to %s "
330 "denied due to security "
331 "descriptor.\n",
332 servicename);
333 conn_free(conn);
334 return NT_STATUS_ACCESS_DENIED;
336 conn->read_only = true;
339 if (!smbd_vfs_init(conn)) {
340 NTSTATUS status = map_nt_error_from_unix(errno);
341 DEBUG(0,("create_conn_struct: smbd_vfs_init failed.\n"));
342 conn_free(conn);
343 return status;
346 /* this must be the first filesystem operation that we do */
347 if (SMB_VFS_CONNECT(conn, servicename, vfs_user) < 0) {
348 DEBUG(0,("VFS connect failed!\n"));
349 conn_free(conn);
350 return NT_STATUS_UNSUCCESSFUL;
353 ok = canonicalize_connect_path(conn);
354 if (!ok) {
355 DBG_ERR("Failed to canonicalize sharepath\n");
356 conn_free(conn);
357 return NT_STATUS_ACCESS_DENIED;
360 conn->fs_capabilities = SMB_VFS_FS_CAPABILITIES(conn, &conn->ts_res);
361 conn->tcon_done = true;
362 *pconn = talloc_move(ctx, &conn);
364 return NT_STATUS_OK;
367 static int conn_struct_tos_destructor(struct conn_struct_tos *c)
369 if (c->oldcwd_fname != NULL) {
370 vfs_ChDir(c->conn, c->oldcwd_fname);
371 TALLOC_FREE(c->oldcwd_fname);
373 SMB_VFS_DISCONNECT(c->conn);
374 conn_free(c->conn);
375 return 0;
378 /********************************************************
379 Fake up a connection struct for the VFS layer, for use in
380 applications (such as the python bindings), that do not want the
381 global working directory changed under them.
383 SMB_VFS_CONNECT requires root privileges.
384 This temporary uses become_root() and unbecome_root().
386 But further impersonation has to be cone by the caller.
387 *********************************************************/
388 NTSTATUS create_conn_struct_tos(struct messaging_context *msg,
389 int snum,
390 const char *path,
391 const struct auth_session_info *session_info,
392 struct conn_struct_tos **_c)
394 struct conn_struct_tos *c = NULL;
395 struct tevent_context *ev = NULL;
396 NTSTATUS status;
398 *_c = NULL;
400 c = talloc_zero(talloc_tos(), struct conn_struct_tos);
401 if (c == NULL) {
402 return NT_STATUS_NO_MEMORY;
405 ev = samba_tevent_context_init(c);
406 if (ev == NULL) {
407 TALLOC_FREE(c);
408 return NT_STATUS_NO_MEMORY;
411 become_root();
412 status = create_conn_struct_as_root(c,
414 msg,
415 &c->conn,
416 snum,
417 path,
418 session_info);
419 unbecome_root();
420 if (!NT_STATUS_IS_OK(status)) {
421 TALLOC_FREE(c);
422 return status;
425 talloc_set_destructor(c, conn_struct_tos_destructor);
427 *_c = c;
428 return NT_STATUS_OK;
431 /********************************************************
432 Fake up a connection struct for the VFS layer.
433 Note: this performs a vfs connect and CHANGES CWD !!!! JRA.
435 See also the comment for create_conn_struct_tos() above!
437 The CWD change is reverted by the destructor of
438 conn_struct_tos when the current talloc_tos() is destroyed.
439 *********************************************************/
440 NTSTATUS create_conn_struct_tos_cwd(struct messaging_context *msg,
441 int snum,
442 const char *path,
443 const struct auth_session_info *session_info,
444 struct conn_struct_tos **_c)
446 struct conn_struct_tos *c = NULL;
447 struct smb_filename smb_fname_connectpath = {0};
448 NTSTATUS status;
450 *_c = NULL;
452 status = create_conn_struct_tos(msg,
453 snum,
454 path,
455 session_info,
456 &c);
457 if (!NT_STATUS_IS_OK(status)) {
458 return status;
462 * Windows seems to insist on doing trans2getdfsreferral() calls on
463 * the IPC$ share as the anonymous user. If we try to chdir as that
464 * user we will fail.... WTF ? JRA.
467 c->oldcwd_fname = vfs_GetWd(c, c->conn);
468 if (c->oldcwd_fname == NULL) {
469 status = map_nt_error_from_unix(errno);
470 DEBUG(3, ("vfs_GetWd failed: %s\n", strerror(errno)));
471 TALLOC_FREE(c);
472 return status;
475 smb_fname_connectpath = (struct smb_filename) {
476 .base_name = c->conn->connectpath
479 if (vfs_ChDir(c->conn, &smb_fname_connectpath) != 0) {
480 status = map_nt_error_from_unix(errno);
481 DBG_NOTICE("Can't ChDir to new conn path %s. "
482 "Error was %s\n",
483 c->conn->connectpath, strerror(errno));
484 TALLOC_FREE(c->oldcwd_fname);
485 TALLOC_FREE(c);
486 return status;
489 *_c = c;
490 return NT_STATUS_OK;
493 /********************************************************
494 Fake up a connection struct for the VFS layer.
495 This takes an TALLOC_CTX and tevent_context from the
496 caller and the resulting connection_struct is stable
497 across the lifetime of mem_ctx and ev.
499 Note: this performs a vfs connect and changes cwd.
501 See also the comment for create_conn_struct_tos() above!
502 *********************************************************/
504 NTSTATUS create_conn_struct_cwd(TALLOC_CTX *mem_ctx,
505 struct tevent_context *ev,
506 struct messaging_context *msg,
507 const struct auth_session_info *session_info,
508 int snum,
509 const char *path,
510 struct connection_struct **c)
512 NTSTATUS status;
514 become_root();
515 status = create_conn_struct_as_root(mem_ctx,
517 msg,
519 snum,
520 path,
521 session_info);
522 unbecome_root();
523 return status;
526 static void shuffle_strlist(char **list, int count)
528 int i;
529 uint32_t r;
530 char *tmp;
532 for (i = count; i > 1; i--) {
533 r = generate_random() % i;
535 tmp = list[i-1];
536 list[i-1] = list[r];
537 list[r] = tmp;
541 /**********************************************************************
542 Parse the contents of a symlink to verify if it is an msdfs referral
543 A valid referral is of the form:
545 msdfs:server1\share1,server2\share2
546 msdfs:server1\share1\pathname,server2\share2\pathname
547 msdfs:server1/share1,server2/share2
548 msdfs:server1/share1/pathname,server2/share2/pathname.
550 Note that the alternate paths returned here must be of the canonicalized
551 form:
553 \server\share or
554 \server\share\path\to\file,
556 even in posix path mode. This is because we have no knowledge if the
557 server we're referring to understands posix paths.
558 **********************************************************************/
560 bool parse_msdfs_symlink(TALLOC_CTX *ctx,
561 bool shuffle_referrals,
562 const char *target,
563 struct referral **ppreflist,
564 size_t *prefcount)
566 char *temp = NULL;
567 char *prot;
568 char **alt_path = NULL;
569 size_t count = 0, i;
570 struct referral *reflist = NULL;
571 char *saveptr;
573 temp = talloc_strdup(ctx, target);
574 if (!temp) {
575 return false;
577 prot = strtok_r(temp, ":", &saveptr);
578 if (!prot) {
579 DEBUG(0,("parse_msdfs_symlink: invalid path !\n"));
580 TALLOC_FREE(temp);
581 return false;
584 alt_path = talloc_array(ctx, char *, MAX_REFERRAL_COUNT);
585 if (!alt_path) {
586 TALLOC_FREE(temp);
587 return false;
590 /* parse out the alternate paths */
591 while((count<MAX_REFERRAL_COUNT) &&
592 ((alt_path[count] = strtok_r(NULL, ",", &saveptr)) != NULL)) {
593 count++;
596 /* shuffle alternate paths */
597 if (shuffle_referrals) {
598 shuffle_strlist(alt_path, count);
601 DBG_DEBUG("count=%zu\n", count);
603 if (count) {
604 reflist = talloc_zero_array(ctx,
605 struct referral, count);
606 if(reflist == NULL) {
607 TALLOC_FREE(temp);
608 TALLOC_FREE(alt_path);
609 return false;
611 } else {
612 reflist = NULL;
615 for(i=0;i<count;i++) {
616 char *p;
618 /* Canonicalize link target.
619 * Replace all /'s in the path by a \ */
620 string_replace(alt_path[i], '/', '\\');
622 /* Remove leading '\\'s */
623 p = alt_path[i];
624 while (*p && (*p == '\\')) {
625 p++;
628 reflist[i].alternate_path = talloc_asprintf(reflist,
629 "\\%s",
631 if (!reflist[i].alternate_path) {
632 TALLOC_FREE(temp);
633 TALLOC_FREE(alt_path);
634 TALLOC_FREE(reflist);
635 return false;
638 reflist[i].proximity = 0;
639 reflist[i].ttl = REFERRAL_TTL;
640 DBG_DEBUG("Created alt path: %s\n",
641 reflist[i].alternate_path);
644 if (ppreflist != NULL) {
645 *ppreflist = reflist;
646 } else {
647 TALLOC_FREE(reflist);
649 if (prefcount != NULL) {
650 *prefcount = count;
652 TALLOC_FREE(temp);
653 TALLOC_FREE(alt_path);
654 return true;
657 /**********************************************************************
658 Returns true if the unix path is a valid msdfs symlink.
659 **********************************************************************/
661 bool is_msdfs_link(struct files_struct *dirfsp,
662 struct smb_filename *atname)
664 NTSTATUS status = SMB_VFS_READ_DFS_PATHAT(dirfsp->conn,
665 talloc_tos(),
666 dirfsp,
667 atname,
668 NULL,
669 NULL);
670 return (NT_STATUS_IS_OK(status));
673 /*****************************************************************
674 Used by other functions to decide if a dfs path is remote,
675 and to get the list of referred locations for that remote path.
677 consumedcntp: how much of the dfs path is being redirected. the client
678 should try the remaining path on the redirected server.
680 If this returns NT_STATUS_PATH_NOT_COVERED the contents of the msdfs
681 link redirect are in targetpath.
682 *****************************************************************/
684 static NTSTATUS dfs_path_lookup(TALLOC_CTX *ctx,
685 connection_struct *conn,
686 const char *dfspath, /* Incoming complete dfs path */
687 const struct dfs_path *pdp, /* Parsed out
688 server+share+extrapath. */
689 uint32_t ucf_flags,
690 NTTIME *_twrp,
691 int *consumedcntp,
692 struct referral **ppreflist,
693 size_t *preferral_count)
695 char *p = NULL;
696 char *q = NULL;
697 NTSTATUS status;
698 struct smb_filename *smb_fname = NULL;
699 struct smb_filename *parent_fname = NULL;
700 struct smb_filename *atname = NULL;
701 char *canon_dfspath = NULL; /* Canonicalized dfs path. (only '/'
702 components). */
704 DEBUG(10,("dfs_path_lookup: Conn path = %s reqpath = %s\n",
705 conn->connectpath, pdp->reqpath));
708 * Note the unix path conversion here we're doing we
709 * throw away. We're looking for a symlink for a dfs
710 * resolution, if we don't find it we'll do another
711 * unix_convert later in the codepath.
714 status = unix_convert(ctx, conn, pdp->reqpath, 0, &smb_fname,
715 ucf_flags);
717 if (!NT_STATUS_IS_OK(status)) {
718 if (!NT_STATUS_EQUAL(status,
719 NT_STATUS_OBJECT_PATH_NOT_FOUND)) {
720 return status;
722 if (smb_fname == NULL || smb_fname->base_name == NULL) {
723 return status;
727 /* Optimization - check if we can redirect the whole path. */
728 status = parent_pathref(ctx,
729 conn->cwd_fsp,
730 smb_fname,
731 &parent_fname,
732 &atname);
733 if (NT_STATUS_IS_OK(status)) {
735 * We must have a parent_fname->fsp before
736 * we can call SMB_VFS_READ_DFS_PATHAT().
738 status = SMB_VFS_READ_DFS_PATHAT(conn,
739 ctx,
740 parent_fname->fsp,
741 atname,
742 ppreflist,
743 preferral_count);
744 /* We're now done with parent_fname and atname. */
745 TALLOC_FREE(parent_fname);
747 if (NT_STATUS_IS_OK(status)) {
748 DBG_INFO("%s resolves to a valid dfs link\n",
749 dfspath);
751 if (consumedcntp) {
752 *consumedcntp = strlen(dfspath);
754 status = NT_STATUS_PATH_NOT_COVERED;
755 goto out;
759 /* Prepare to test only for '/' components in the given path,
760 * so if a Windows path replace all '\\' characters with '/'.
761 * For a POSIX DFS path we know all separators are already '/'. */
763 canon_dfspath = talloc_strdup(ctx, dfspath);
764 if (!canon_dfspath) {
765 status = NT_STATUS_NO_MEMORY;
766 goto out;
768 if (!pdp->posix_path) {
769 string_replace(canon_dfspath, '\\', '/');
773 * localpath comes out of unix_convert, so it has
774 * no trailing backslash. Make sure that canon_dfspath hasn't either.
775 * Fix for bug #4860 from Jan Martin <Jan.Martin@rwedea.com>.
778 trim_char(canon_dfspath,0,'/');
781 * Redirect if any component in the path is a link.
782 * We do this by walking backwards through the
783 * local path, chopping off the last component
784 * in both the local path and the canonicalized
785 * DFS path. If we hit a DFS link then we're done.
788 p = strrchr_m(smb_fname->base_name, '/');
789 if (consumedcntp) {
790 q = strrchr_m(canon_dfspath, '/');
793 while (p) {
794 *p = '\0';
795 if (q) {
796 *q = '\0';
800 * Ensure parent_pathref() calls vfs_stat() on
801 * the newly truncated path.
803 SET_STAT_INVALID(smb_fname->st);
804 status = parent_pathref(ctx,
805 conn->cwd_fsp,
806 smb_fname,
807 &parent_fname,
808 &atname);
809 if (NT_STATUS_IS_OK(status)) {
811 * We must have a parent_fname->fsp before
812 * we can call SMB_VFS_READ_DFS_PATHAT().
814 status = SMB_VFS_READ_DFS_PATHAT(conn,
815 ctx,
816 parent_fname->fsp,
817 atname,
818 ppreflist,
819 preferral_count);
821 /* We're now done with parent_fname and atname. */
822 TALLOC_FREE(parent_fname);
824 if (NT_STATUS_IS_OK(status)) {
825 DBG_INFO("Redirecting %s because "
826 "parent %s is a dfs link\n",
827 dfspath,
828 smb_fname_str_dbg(smb_fname));
830 if (consumedcntp) {
831 *consumedcntp = strlen(canon_dfspath);
832 DBG_DEBUG("Path consumed: %s "
833 "(%d)\n",
834 canon_dfspath,
835 *consumedcntp);
838 status = NT_STATUS_PATH_NOT_COVERED;
839 goto out;
843 /* Step back on the filesystem. */
844 p = strrchr_m(smb_fname->base_name, '/');
846 if (consumedcntp) {
847 /* And in the canonicalized dfs path. */
848 q = strrchr_m(canon_dfspath, '/');
852 if ((ucf_flags & UCF_GMT_PATHNAME) && _twrp != NULL) {
853 *_twrp = smb_fname->twrp;
856 status = NT_STATUS_OK;
857 out:
859 /* This should already be free, but make sure. */
860 TALLOC_FREE(parent_fname);
861 TALLOC_FREE(smb_fname);
862 return status;
865 /*****************************************************************
866 Decides if a dfs pathname should be redirected or not.
867 If not, the pathname is converted to a tcon-relative local unix path
869 search_wcard_flag: this flag performs 2 functions both related
870 to searches. See resolve_dfs_path() and parse_dfs_path_XX()
871 for details.
873 This function can return NT_STATUS_OK, meaning use the returned path as-is
874 (mapped into a local path).
875 or NT_STATUS_NOT_COVERED meaning return a DFS redirect, or
876 any other NT_STATUS error which is a genuine error to be
877 returned to the client.
878 *****************************************************************/
880 NTSTATUS dfs_redirect(TALLOC_CTX *ctx,
881 connection_struct *conn,
882 const char *path_in,
883 uint32_t ucf_flags,
884 bool allow_broken_path,
885 NTTIME *_twrp,
886 char **pp_path_out)
888 const struct loadparm_substitution *lp_sub =
889 loadparm_s3_global_substitution();
890 NTSTATUS status;
891 struct dfs_path *pdp = talloc(ctx, struct dfs_path);
893 if (!pdp) {
894 return NT_STATUS_NO_MEMORY;
897 status = parse_dfs_path(conn, path_in, false,
898 allow_broken_path, pdp);
899 if (!NT_STATUS_IS_OK(status)) {
900 TALLOC_FREE(pdp);
901 return status;
904 if (pdp->reqpath[0] == '\0') {
905 TALLOC_FREE(pdp);
906 *pp_path_out = talloc_strdup(ctx, "");
907 if (!*pp_path_out) {
908 return NT_STATUS_NO_MEMORY;
910 DEBUG(5,("dfs_redirect: self-referral.\n"));
911 return NT_STATUS_OK;
914 /* If dfs pathname for a non-dfs share, convert to tcon-relative
915 path and return OK */
917 if (!lp_msdfs_root(SNUM(conn))) {
918 *pp_path_out = talloc_strdup(ctx, pdp->reqpath);
919 TALLOC_FREE(pdp);
920 if (!*pp_path_out) {
921 return NT_STATUS_NO_MEMORY;
923 return NT_STATUS_OK;
926 /* If it looked like a local path (zero hostname/servicename)
927 * just treat as a tcon-relative path. */
929 if (pdp->hostname[0] == '\0' && pdp->servicename[0] == '\0') {
930 *pp_path_out = talloc_strdup(ctx, pdp->reqpath);
931 TALLOC_FREE(pdp);
932 if (!*pp_path_out) {
933 return NT_STATUS_NO_MEMORY;
935 return NT_STATUS_OK;
938 if (!( strequal(pdp->servicename, lp_servicename(talloc_tos(), lp_sub, SNUM(conn)))
939 || (strequal(pdp->servicename, HOMES_NAME)
940 && strequal(lp_servicename(talloc_tos(), lp_sub, SNUM(conn)),
941 conn->session_info->unix_info->sanitized_username) )) ) {
943 /* The given sharename doesn't match this connection. */
944 TALLOC_FREE(pdp);
946 return NT_STATUS_OBJECT_PATH_NOT_FOUND;
949 status = dfs_path_lookup(ctx,
950 conn,
951 path_in,
952 pdp,
953 ucf_flags,
954 _twrp, /* twrp. */
955 NULL, /* int *consumedcntp */
956 NULL, /* struct referral **ppreflist */
957 NULL); /* size_t *preferral_count */
958 if (!NT_STATUS_IS_OK(status)) {
959 if (NT_STATUS_EQUAL(status, NT_STATUS_PATH_NOT_COVERED)) {
960 DEBUG(3,("dfs_redirect: Redirecting %s\n", path_in));
961 } else {
962 DEBUG(10,("dfs_redirect: dfs_path_lookup "
963 "failed for %s with %s\n",
964 path_in, nt_errstr(status) ));
966 return status;
969 DEBUG(3,("dfs_redirect: Not redirecting %s.\n", path_in));
971 /* Form non-dfs tcon-relative path */
972 *pp_path_out = talloc_strdup(ctx, pdp->reqpath);
973 TALLOC_FREE(pdp);
974 if (!*pp_path_out) {
975 return NT_STATUS_NO_MEMORY;
978 DEBUG(3,("dfs_redirect: Path %s converted to non-dfs path %s\n",
979 path_in,
980 *pp_path_out));
982 return NT_STATUS_OK;
985 /**********************************************************************
986 Return a self referral.
987 **********************************************************************/
989 static NTSTATUS self_ref(TALLOC_CTX *ctx,
990 const char *dfs_path,
991 struct junction_map *jucn,
992 int *consumedcntp,
993 bool *self_referralp)
995 struct referral *ref;
997 *self_referralp = True;
999 jucn->referral_count = 1;
1000 if((ref = talloc_zero(ctx, struct referral)) == NULL) {
1001 return NT_STATUS_NO_MEMORY;
1004 ref->alternate_path = talloc_strdup(ctx, dfs_path);
1005 if (!ref->alternate_path) {
1006 TALLOC_FREE(ref);
1007 return NT_STATUS_NO_MEMORY;
1009 ref->proximity = 0;
1010 ref->ttl = REFERRAL_TTL;
1011 jucn->referral_list = ref;
1012 *consumedcntp = strlen(dfs_path);
1013 return NT_STATUS_OK;
1016 /**********************************************************************
1017 Gets valid referrals for a dfs path and fills up the
1018 junction_map structure.
1019 **********************************************************************/
1021 NTSTATUS get_referred_path(TALLOC_CTX *ctx,
1022 struct auth_session_info *session_info,
1023 const char *dfs_path,
1024 const struct tsocket_address *remote_address,
1025 const struct tsocket_address *local_address,
1026 bool allow_broken_path,
1027 struct junction_map *jucn,
1028 int *consumedcntp,
1029 bool *self_referralp)
1031 TALLOC_CTX *frame = talloc_stackframe();
1032 const struct loadparm_substitution *lp_sub =
1033 loadparm_s3_global_substitution();
1034 struct conn_struct_tos *c = NULL;
1035 struct connection_struct *conn = NULL;
1036 int snum;
1037 NTSTATUS status = NT_STATUS_NOT_FOUND;
1038 struct dfs_path *pdp = talloc_zero(frame, struct dfs_path);
1040 if (!pdp) {
1041 TALLOC_FREE(frame);
1042 return NT_STATUS_NO_MEMORY;
1045 *self_referralp = False;
1047 status = parse_dfs_path(NULL, dfs_path, False, allow_broken_path, pdp);
1048 if (!NT_STATUS_IS_OK(status)) {
1049 TALLOC_FREE(frame);
1050 return status;
1053 jucn->service_name = talloc_strdup(ctx, pdp->servicename);
1054 jucn->volume_name = talloc_strdup(ctx, pdp->reqpath);
1055 if (!jucn->service_name || !jucn->volume_name) {
1056 TALLOC_FREE(frame);
1057 return NT_STATUS_NO_MEMORY;
1060 /* Verify the share is a dfs root */
1061 snum = lp_servicenumber(jucn->service_name);
1062 if(snum < 0) {
1063 char *service_name = NULL;
1064 if ((snum = find_service(ctx, jucn->service_name, &service_name)) < 0) {
1065 TALLOC_FREE(frame);
1066 return NT_STATUS_NOT_FOUND;
1068 if (!service_name) {
1069 TALLOC_FREE(frame);
1070 return NT_STATUS_NO_MEMORY;
1072 TALLOC_FREE(jucn->service_name);
1073 jucn->service_name = talloc_strdup(ctx, service_name);
1074 if (!jucn->service_name) {
1075 TALLOC_FREE(frame);
1076 return NT_STATUS_NO_MEMORY;
1080 if (!lp_msdfs_root(snum) && (*lp_msdfs_proxy(talloc_tos(), lp_sub, snum) == '\0')) {
1081 DEBUG(3,("get_referred_path: |%s| in dfs path %s is not "
1082 "a dfs root.\n",
1083 pdp->servicename, dfs_path));
1084 TALLOC_FREE(frame);
1085 return NT_STATUS_NOT_FOUND;
1089 * Self referrals are tested with a anonymous IPC connection and
1090 * a GET_DFS_REFERRAL call to \\server\share. (which means
1091 * dp.reqpath[0] points to an empty string). create_conn_struct cd's
1092 * into the directory and will fail if it cannot (as the anonymous
1093 * user). Cope with this.
1096 if (pdp->reqpath[0] == '\0') {
1097 char *tmp;
1098 struct referral *ref;
1099 size_t refcount;
1101 if (*lp_msdfs_proxy(talloc_tos(), lp_sub, snum) == '\0') {
1102 TALLOC_FREE(frame);
1103 return self_ref(ctx,
1104 dfs_path,
1105 jucn,
1106 consumedcntp,
1107 self_referralp);
1111 * It's an msdfs proxy share. Redirect to
1112 * the configured target share.
1115 tmp = talloc_asprintf(frame, "msdfs:%s",
1116 lp_msdfs_proxy(frame, lp_sub, snum));
1117 if (tmp == NULL) {
1118 TALLOC_FREE(frame);
1119 return NT_STATUS_NO_MEMORY;
1122 if (!parse_msdfs_symlink(ctx,
1123 lp_msdfs_shuffle_referrals(snum),
1124 tmp,
1125 &ref,
1126 &refcount)) {
1127 TALLOC_FREE(frame);
1128 return NT_STATUS_INVALID_PARAMETER;
1130 jucn->referral_count = refcount;
1131 jucn->referral_list = ref;
1132 *consumedcntp = strlen(dfs_path);
1133 TALLOC_FREE(frame);
1134 return NT_STATUS_OK;
1137 status = create_conn_struct_tos_cwd(global_messaging_context(),
1138 snum,
1139 lp_path(frame, lp_sub, snum),
1140 session_info,
1141 &c);
1142 if (!NT_STATUS_IS_OK(status)) {
1143 TALLOC_FREE(frame);
1144 return status;
1146 conn = c->conn;
1149 * TODO
1151 * The remote and local address should be passed down to
1152 * create_conn_struct_cwd.
1154 if (conn->sconn->remote_address == NULL) {
1155 conn->sconn->remote_address =
1156 tsocket_address_copy(remote_address, conn->sconn);
1157 if (conn->sconn->remote_address == NULL) {
1158 TALLOC_FREE(frame);
1159 return NT_STATUS_NO_MEMORY;
1162 if (conn->sconn->local_address == NULL) {
1163 conn->sconn->local_address =
1164 tsocket_address_copy(local_address, conn->sconn);
1165 if (conn->sconn->local_address == NULL) {
1166 TALLOC_FREE(frame);
1167 return NT_STATUS_NO_MEMORY;
1171 /* If this is a DFS path dfs_lookup should return
1172 * NT_STATUS_PATH_NOT_COVERED. */
1174 status = dfs_path_lookup(ctx,
1175 conn,
1176 dfs_path,
1177 pdp,
1178 0, /* ucf_flags */
1179 NULL,
1180 consumedcntp,
1181 &jucn->referral_list,
1182 &jucn->referral_count);
1184 if (!NT_STATUS_EQUAL(status, NT_STATUS_PATH_NOT_COVERED)) {
1185 DEBUG(3,("get_referred_path: No valid referrals for path %s\n",
1186 dfs_path));
1187 if (NT_STATUS_IS_OK(status)) {
1189 * We are in an error path here (we
1190 * know it's not a DFS path), but
1191 * dfs_path_lookup() can return
1192 * NT_STATUS_OK. Ensure we always
1193 * return a valid error code.
1195 * #9588 - ACLs are not inherited to directories
1196 * for DFS shares.
1198 status = NT_STATUS_NOT_FOUND;
1200 goto err_exit;
1203 status = NT_STATUS_OK;
1204 err_exit:
1205 TALLOC_FREE(frame);
1206 return status;
1209 /******************************************************************
1210 Set up the DFS referral for the dfs pathname. This call returns
1211 the amount of the path covered by this server, and where the
1212 client should be redirected to. This is the meat of the
1213 TRANS2_GET_DFS_REFERRAL call.
1214 ******************************************************************/
1216 int setup_dfs_referral(connection_struct *orig_conn,
1217 const char *dfs_path,
1218 int max_referral_level,
1219 char **ppdata, NTSTATUS *pstatus)
1221 char *pdata = *ppdata;
1222 int reply_size = 0;
1223 struct dfs_GetDFSReferral *r;
1224 DATA_BLOB blob = data_blob_null;
1225 NTSTATUS status;
1226 enum ndr_err_code ndr_err;
1228 r = talloc_zero(talloc_tos(), struct dfs_GetDFSReferral);
1229 if (r == NULL) {
1230 *pstatus = NT_STATUS_NO_MEMORY;
1231 return -1;
1234 r->in.req.max_referral_level = max_referral_level;
1235 r->in.req.servername = talloc_strdup(r, dfs_path);
1236 if (r->in.req.servername == NULL) {
1237 talloc_free(r);
1238 *pstatus = NT_STATUS_NO_MEMORY;
1239 return -1;
1242 status = SMB_VFS_GET_DFS_REFERRALS(orig_conn, r);
1243 if (!NT_STATUS_IS_OK(status)) {
1244 talloc_free(r);
1245 *pstatus = status;
1246 return -1;
1249 ndr_err = ndr_push_struct_blob(&blob, r,
1250 r->out.resp,
1251 (ndr_push_flags_fn_t)ndr_push_dfs_referral_resp);
1252 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1253 TALLOC_FREE(r);
1254 *pstatus = NT_STATUS_INVALID_PARAMETER;
1255 return -1;
1258 pdata = (char *)SMB_REALLOC(pdata, blob.length);
1259 if(pdata == NULL) {
1260 TALLOC_FREE(r);
1261 DEBUG(0,("referral setup:"
1262 "malloc failed for Realloc!\n"));
1263 return -1;
1265 *ppdata = pdata;
1266 reply_size = blob.length;
1267 memcpy(pdata, blob.data, blob.length);
1268 TALLOC_FREE(r);
1270 *pstatus = NT_STATUS_OK;
1271 return reply_size;
1274 /**********************************************************************
1275 The following functions are called by the NETDFS RPC pipe functions
1276 **********************************************************************/
1278 /*********************************************************************
1279 Creates a junction structure from a DFS pathname
1280 **********************************************************************/
1282 bool create_junction(TALLOC_CTX *ctx,
1283 const char *dfs_path,
1284 bool allow_broken_path,
1285 struct junction_map *jucn)
1287 const struct loadparm_substitution *lp_sub =
1288 loadparm_s3_global_substitution();
1289 int snum;
1290 struct dfs_path *pdp = talloc(ctx,struct dfs_path);
1291 NTSTATUS status;
1293 if (!pdp) {
1294 return False;
1296 status = parse_dfs_path(NULL, dfs_path, False, allow_broken_path, pdp);
1297 if (!NT_STATUS_IS_OK(status)) {
1298 return False;
1301 /* check if path is dfs : validate first token */
1302 if (!is_myname_or_ipaddr(pdp->hostname)) {
1303 DEBUG(4,("create_junction: Invalid hostname %s "
1304 "in dfs path %s\n",
1305 pdp->hostname, dfs_path));
1306 TALLOC_FREE(pdp);
1307 return False;
1310 /* Check for a non-DFS share */
1311 snum = lp_servicenumber(pdp->servicename);
1313 if(snum < 0 || !lp_msdfs_root(snum)) {
1314 DEBUG(4,("create_junction: %s is not an msdfs root.\n",
1315 pdp->servicename));
1316 TALLOC_FREE(pdp);
1317 return False;
1320 jucn->service_name = talloc_strdup(ctx, pdp->servicename);
1321 jucn->volume_name = talloc_strdup(ctx, pdp->reqpath);
1322 jucn->comment = lp_comment(ctx, lp_sub, snum);
1324 TALLOC_FREE(pdp);
1325 if (!jucn->service_name || !jucn->volume_name || ! jucn->comment) {
1326 return False;
1328 return True;
1331 /**********************************************************************
1332 Forms a valid Unix pathname from the junction
1333 **********************************************************************/
1335 static bool junction_to_local_path_tos(const struct junction_map *jucn,
1336 struct auth_session_info *session_info,
1337 char **pp_path_out,
1338 connection_struct **conn_out)
1340 const struct loadparm_substitution *lp_sub =
1341 loadparm_s3_global_substitution();
1342 struct conn_struct_tos *c = NULL;
1343 int snum;
1344 char *path_out = NULL;
1345 NTSTATUS status;
1347 snum = lp_servicenumber(jucn->service_name);
1348 if(snum < 0) {
1349 return False;
1351 status = create_conn_struct_tos_cwd(global_messaging_context(),
1352 snum,
1353 lp_path(talloc_tos(), lp_sub, snum),
1354 session_info,
1355 &c);
1356 if (!NT_STATUS_IS_OK(status)) {
1357 return False;
1360 path_out = talloc_asprintf(c,
1361 "%s/%s",
1362 lp_path(talloc_tos(), lp_sub, snum),
1363 jucn->volume_name);
1364 if (path_out == NULL) {
1365 TALLOC_FREE(c);
1366 return False;
1368 *pp_path_out = path_out;
1369 *conn_out = c->conn;
1370 return True;
1374 * Create a msdfs string in Samba format we can store
1375 * in a filesystem object (currently a symlink).
1378 char *msdfs_link_string(TALLOC_CTX *ctx,
1379 const struct referral *reflist,
1380 size_t referral_count)
1382 char *refpath = NULL;
1383 bool insert_comma = false;
1384 char *msdfs_link = NULL;
1385 size_t i;
1387 /* Form the msdfs_link contents */
1388 msdfs_link = talloc_strdup(ctx, "msdfs:");
1389 if (msdfs_link == NULL) {
1390 goto err;
1393 for( i= 0; i < referral_count; i++) {
1394 refpath = talloc_strdup(ctx, reflist[i].alternate_path);
1396 if (refpath == NULL) {
1397 goto err;
1400 /* Alternate paths always use Windows separators. */
1401 trim_char(refpath, '\\', '\\');
1402 if (*refpath == '\0') {
1403 if (i == 0) {
1404 insert_comma = false;
1406 continue;
1408 if (i > 0 && insert_comma) {
1409 msdfs_link = talloc_asprintf_append_buffer(msdfs_link,
1410 ",%s",
1411 refpath);
1412 } else {
1413 msdfs_link = talloc_asprintf_append_buffer(msdfs_link,
1414 "%s",
1415 refpath);
1418 if (msdfs_link == NULL) {
1419 goto err;
1422 if (!insert_comma) {
1423 insert_comma = true;
1426 TALLOC_FREE(refpath);
1429 return msdfs_link;
1431 err:
1433 TALLOC_FREE(refpath);
1434 TALLOC_FREE(msdfs_link);
1435 return NULL;
1438 bool create_msdfs_link(const struct junction_map *jucn,
1439 struct auth_session_info *session_info)
1441 TALLOC_CTX *frame = talloc_stackframe();
1442 char *path = NULL;
1443 connection_struct *conn;
1444 struct smb_filename *smb_fname = NULL;
1445 struct smb_filename *parent_fname = NULL;
1446 struct smb_filename *at_fname = NULL;
1447 bool ok;
1448 NTSTATUS status;
1449 bool ret = false;
1451 ok = junction_to_local_path_tos(jucn, session_info, &path, &conn);
1452 if (!ok) {
1453 goto out;
1456 if (!CAN_WRITE(conn)) {
1457 const struct loadparm_substitution *lp_sub =
1458 loadparm_s3_global_substitution();
1459 int snum = lp_servicenumber(jucn->service_name);
1461 DBG_WARNING("Can't create DFS entry on read-only share %s\n",
1462 lp_servicename(frame, lp_sub, snum));
1463 goto out;
1466 smb_fname = synthetic_smb_fname(frame,
1467 path,
1468 NULL,
1469 NULL,
1472 if (smb_fname == NULL) {
1473 goto out;
1476 status = parent_pathref(frame,
1477 conn->cwd_fsp,
1478 smb_fname,
1479 &parent_fname,
1480 &at_fname);
1481 if (!NT_STATUS_IS_OK(status)) {
1482 goto out;
1485 status = SMB_VFS_CREATE_DFS_PATHAT(conn,
1486 parent_fname->fsp,
1487 at_fname,
1488 jucn->referral_list,
1489 jucn->referral_count);
1490 if (!NT_STATUS_IS_OK(status)) {
1491 if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_COLLISION)) {
1492 int retval = SMB_VFS_UNLINKAT(conn,
1493 parent_fname->fsp,
1494 at_fname,
1496 if (retval != 0) {
1497 goto out;
1500 status = SMB_VFS_CREATE_DFS_PATHAT(conn,
1501 parent_fname->fsp,
1502 at_fname,
1503 jucn->referral_list,
1504 jucn->referral_count);
1505 if (!NT_STATUS_IS_OK(status)) {
1506 DBG_WARNING("SMB_VFS_CREATE_DFS_PATHAT failed "
1507 "%s - Error: %s\n",
1508 path,
1509 nt_errstr(status));
1510 goto out;
1514 ret = true;
1516 out:
1517 TALLOC_FREE(frame);
1518 return ret;
1521 bool remove_msdfs_link(const struct junction_map *jucn,
1522 struct auth_session_info *session_info)
1524 TALLOC_CTX *frame = talloc_stackframe();
1525 char *path = NULL;
1526 connection_struct *conn;
1527 bool ret = False;
1528 struct smb_filename *smb_fname;
1529 struct smb_filename *parent_fname = NULL;
1530 struct smb_filename *at_fname = NULL;
1531 NTSTATUS status;
1532 bool ok;
1533 int retval;
1535 ok = junction_to_local_path_tos(jucn, session_info, &path, &conn);
1536 if (!ok) {
1537 TALLOC_FREE(frame);
1538 return false;
1541 if (!CAN_WRITE(conn)) {
1542 const struct loadparm_substitution *lp_sub =
1543 loadparm_s3_global_substitution();
1544 int snum = lp_servicenumber(jucn->service_name);
1546 DBG_WARNING("Can't remove DFS entry on read-only share %s\n",
1547 lp_servicename(frame, lp_sub, snum));
1548 TALLOC_FREE(frame);
1549 return false;
1552 smb_fname = synthetic_smb_fname(frame,
1553 path,
1554 NULL,
1555 NULL,
1558 if (smb_fname == NULL) {
1559 TALLOC_FREE(frame);
1560 errno = ENOMEM;
1561 return false;
1564 status = parent_pathref(frame,
1565 conn->cwd_fsp,
1566 smb_fname,
1567 &parent_fname,
1568 &at_fname);
1569 if (!NT_STATUS_IS_OK(status)) {
1570 TALLOC_FREE(frame);
1571 return false;
1574 retval = SMB_VFS_UNLINKAT(conn,
1575 parent_fname->fsp,
1576 at_fname,
1578 if (retval == 0) {
1579 ret = True;
1582 TALLOC_FREE(frame);
1583 return ret;
1586 /*********************************************************************
1587 Return the number of DFS links at the root of this share.
1588 *********************************************************************/
1590 static size_t count_dfs_links(TALLOC_CTX *ctx,
1591 struct auth_session_info *session_info,
1592 int snum)
1594 TALLOC_CTX *frame = talloc_stackframe();
1595 const struct loadparm_substitution *lp_sub =
1596 loadparm_s3_global_substitution();
1597 size_t cnt = 0;
1598 const char *dname = NULL;
1599 char *talloced = NULL;
1600 const char *connect_path = lp_path(frame, lp_sub, snum);
1601 const char *msdfs_proxy = lp_msdfs_proxy(frame, lp_sub, snum);
1602 struct conn_struct_tos *c = NULL;
1603 connection_struct *conn = NULL;
1604 NTSTATUS status;
1605 struct smb_filename *smb_fname = NULL;
1606 struct smb_Dir *dir_hnd = NULL;
1607 long offset = 0;
1609 if(*connect_path == '\0') {
1610 TALLOC_FREE(frame);
1611 return 0;
1615 * Fake up a connection struct for the VFS layer.
1618 status = create_conn_struct_tos_cwd(global_messaging_context(),
1619 snum,
1620 connect_path,
1621 session_info,
1622 &c);
1623 if (!NT_STATUS_IS_OK(status)) {
1624 DEBUG(3, ("create_conn_struct failed: %s\n",
1625 nt_errstr(status)));
1626 TALLOC_FREE(frame);
1627 return 0;
1629 conn = c->conn;
1631 /* Count a link for the msdfs root - convention */
1632 cnt = 1;
1634 /* No more links if this is an msdfs proxy. */
1635 if (*msdfs_proxy != '\0') {
1636 goto out;
1639 smb_fname = synthetic_smb_fname(frame,
1640 ".",
1641 NULL,
1642 NULL,
1645 if (smb_fname == NULL) {
1646 goto out;
1649 /* Now enumerate all dfs links */
1650 status = OpenDir(frame,
1651 conn,
1652 smb_fname,
1653 NULL,
1655 &dir_hnd);
1656 if (!NT_STATUS_IS_OK(status)) {
1657 errno = map_errno_from_nt_status(status);
1658 goto out;
1661 while ((dname = ReadDirName(dir_hnd, &offset, NULL, &talloced))
1662 != NULL)
1664 struct smb_filename *smb_dname =
1665 synthetic_smb_fname(frame,
1666 dname,
1667 NULL,
1668 NULL,
1671 if (smb_dname == NULL) {
1672 goto out;
1674 if (is_msdfs_link(dir_hnd_fetch_fsp(dir_hnd), smb_dname)) {
1675 if (cnt + 1 < cnt) {
1676 cnt = 0;
1677 goto out;
1679 cnt++;
1681 TALLOC_FREE(talloced);
1682 TALLOC_FREE(smb_dname);
1685 out:
1686 TALLOC_FREE(frame);
1687 return cnt;
1690 /*********************************************************************
1691 *********************************************************************/
1693 static int form_junctions(TALLOC_CTX *ctx,
1694 struct auth_session_info *session_info,
1695 int snum,
1696 struct junction_map *jucn,
1697 size_t jn_remain)
1699 TALLOC_CTX *frame = talloc_stackframe();
1700 const struct loadparm_substitution *lp_sub =
1701 loadparm_s3_global_substitution();
1702 size_t cnt = 0;
1703 const char *dname = NULL;
1704 char *talloced = NULL;
1705 const char *connect_path = lp_path(frame, lp_sub, snum);
1706 char *service_name = lp_servicename(frame, lp_sub, snum);
1707 const char *msdfs_proxy = lp_msdfs_proxy(frame, lp_sub, snum);
1708 struct conn_struct_tos *c = NULL;
1709 connection_struct *conn = NULL;
1710 struct referral *ref = NULL;
1711 struct smb_filename *smb_fname = NULL;
1712 struct smb_Dir *dir_hnd = NULL;
1713 long offset = 0;
1714 NTSTATUS status;
1716 if (jn_remain == 0) {
1717 TALLOC_FREE(frame);
1718 return 0;
1721 if(*connect_path == '\0') {
1722 TALLOC_FREE(frame);
1723 return 0;
1727 * Fake up a connection struct for the VFS layer.
1730 status = create_conn_struct_tos_cwd(global_messaging_context(),
1731 snum,
1732 connect_path,
1733 session_info,
1734 &c);
1735 if (!NT_STATUS_IS_OK(status)) {
1736 DEBUG(3, ("create_conn_struct failed: %s\n",
1737 nt_errstr(status)));
1738 TALLOC_FREE(frame);
1739 return 0;
1741 conn = c->conn;
1743 /* form a junction for the msdfs root - convention
1744 DO NOT REMOVE THIS: NT clients will not work with us
1745 if this is not present
1747 jucn[cnt].service_name = talloc_strdup(ctx,service_name);
1748 jucn[cnt].volume_name = talloc_strdup(ctx, "");
1749 if (!jucn[cnt].service_name || !jucn[cnt].volume_name) {
1750 goto out;
1752 jucn[cnt].comment = "";
1753 jucn[cnt].referral_count = 1;
1755 ref = jucn[cnt].referral_list = talloc_zero(ctx, struct referral);
1756 if (jucn[cnt].referral_list == NULL) {
1757 goto out;
1760 ref->proximity = 0;
1761 ref->ttl = REFERRAL_TTL;
1762 if (*msdfs_proxy != '\0') {
1763 ref->alternate_path = talloc_strdup(ctx,
1764 msdfs_proxy);
1765 } else {
1766 ref->alternate_path = talloc_asprintf(ctx,
1767 "\\\\%s\\%s",
1768 get_local_machine_name(),
1769 service_name);
1772 if (!ref->alternate_path) {
1773 goto out;
1775 cnt++;
1777 /* Don't enumerate if we're an msdfs proxy. */
1778 if (*msdfs_proxy != '\0') {
1779 goto out;
1782 smb_fname = synthetic_smb_fname(frame,
1783 ".",
1784 NULL,
1785 NULL,
1788 if (smb_fname == NULL) {
1789 goto out;
1792 /* Now enumerate all dfs links */
1793 status = OpenDir(frame,
1794 conn,
1795 smb_fname,
1796 NULL,
1798 &dir_hnd);
1799 if (!NT_STATUS_IS_OK(status)) {
1800 errno = map_errno_from_nt_status(status);
1801 goto out;
1804 while ((dname = ReadDirName(dir_hnd, &offset, NULL, &talloced))
1805 != NULL)
1807 struct smb_filename *smb_dname = NULL;
1809 if (cnt >= jn_remain) {
1810 DEBUG(2, ("form_junctions: ran out of MSDFS "
1811 "junction slots"));
1812 TALLOC_FREE(talloced);
1813 goto out;
1815 smb_dname = synthetic_smb_fname(talloc_tos(),
1816 dname,
1817 NULL,
1818 NULL,
1821 if (smb_dname == NULL) {
1822 TALLOC_FREE(talloced);
1823 goto out;
1826 status = SMB_VFS_READ_DFS_PATHAT(conn,
1827 ctx,
1828 conn->cwd_fsp,
1829 smb_dname,
1830 &jucn[cnt].referral_list,
1831 &jucn[cnt].referral_count);
1833 if (NT_STATUS_IS_OK(status)) {
1834 jucn[cnt].service_name = talloc_strdup(ctx,
1835 service_name);
1836 jucn[cnt].volume_name = talloc_strdup(ctx, dname);
1837 if (!jucn[cnt].service_name || !jucn[cnt].volume_name) {
1838 TALLOC_FREE(talloced);
1839 goto out;
1841 jucn[cnt].comment = "";
1842 cnt++;
1844 TALLOC_FREE(talloced);
1845 TALLOC_FREE(smb_dname);
1848 out:
1849 TALLOC_FREE(frame);
1850 return cnt;
1853 struct junction_map *enum_msdfs_links(TALLOC_CTX *ctx,
1854 struct auth_session_info *session_info,
1855 size_t *p_num_jn)
1857 struct junction_map *jn = NULL;
1858 int i=0;
1859 size_t jn_count = 0;
1860 int sharecount = 0;
1862 *p_num_jn = 0;
1863 if(!lp_host_msdfs()) {
1864 return NULL;
1867 /* Ensure all the usershares are loaded. */
1868 become_root();
1869 load_registry_shares();
1870 sharecount = load_usershare_shares(NULL, connections_snum_used);
1871 unbecome_root();
1873 for(i=0;i < sharecount;i++) {
1874 if(lp_msdfs_root(i)) {
1875 jn_count += count_dfs_links(ctx, session_info, i);
1878 if (jn_count == 0) {
1879 return NULL;
1881 jn = talloc_array(ctx, struct junction_map, jn_count);
1882 if (!jn) {
1883 return NULL;
1885 for(i=0; i < sharecount; i++) {
1886 if (*p_num_jn >= jn_count) {
1887 break;
1889 if(lp_msdfs_root(i)) {
1890 *p_num_jn += form_junctions(ctx,
1891 session_info,
1893 &jn[*p_num_jn],
1894 jn_count - *p_num_jn);
1897 return jn;