Revert "s3: vfs: add user_vfs_evg to connection_struct"
[Samba.git] / source3 / smbd / msdfs.c
blob9b0b2de27caa199138363811a7631f5d377d39df
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/pthreadpool/pthreadpool_tevent.h"
38 /**********************************************************************
39 Parse a DFS pathname of the form \hostname\service\reqpath
40 into the dfs_path structure.
41 If POSIX pathnames is true, the pathname may also be of the
42 form /hostname/service/reqpath.
43 We cope with either here.
45 Unfortunately, due to broken clients who might set the
46 SVAL(inbuf,smb_flg2) & FLAGS2_DFS_PATHNAMES bit and then
47 send a local path, we have to cope with that too....
49 If conn != NULL then ensure the provided service is
50 the one pointed to by the connection.
52 This version does everything using pointers within one copy of the
53 pathname string, talloced on the struct dfs_path pointer (which
54 must be talloced). This may be too clever to live....
55 JRA.
56 **********************************************************************/
58 static NTSTATUS parse_dfs_path(connection_struct *conn,
59 const char *pathname,
60 bool allow_wcards,
61 bool allow_broken_path,
62 struct dfs_path *pdp, /* MUST BE TALLOCED */
63 bool *ppath_contains_wcard)
65 char *pathname_local;
66 char *p,*temp;
67 char *servicename;
68 char *eos_ptr;
69 NTSTATUS status = NT_STATUS_OK;
70 char sepchar;
72 ZERO_STRUCTP(pdp);
75 * This is the only talloc we should need to do
76 * on the struct dfs_path. All the pointers inside
77 * it should point to offsets within this string.
80 pathname_local = talloc_strdup(pdp, pathname);
81 if (!pathname_local) {
82 return NT_STATUS_NO_MEMORY;
84 /* Get a pointer to the terminating '\0' */
85 eos_ptr = &pathname_local[strlen(pathname_local)];
86 p = temp = pathname_local;
89 * Non-broken DFS paths *must* start with the
90 * path separator. For Windows this is always '\\',
91 * for posix paths this is always '/'.
94 if (*pathname == '/') {
95 pdp->posix_path = true;
96 sepchar = '/';
97 } else {
98 pdp->posix_path = false;
99 sepchar = '\\';
102 if (allow_broken_path && (*pathname != sepchar)) {
103 DEBUG(10,("parse_dfs_path: path %s doesn't start with %c\n",
104 pathname, sepchar ));
106 * Possibly client sent a local path by mistake.
107 * Try and convert to a local path.
108 * Note that this is an SMB1-only fallback
109 * to cope with known broken SMB1 clients.
112 pdp->hostname = eos_ptr; /* "" */
113 pdp->servicename = eos_ptr; /* "" */
115 /* We've got no info about separators. */
116 pdp->posix_path = lp_posix_pathnames();
117 p = temp;
118 DEBUG(10,("parse_dfs_path: trying to convert %s to a "
119 "local path\n",
120 temp));
121 goto local_path;
125 * Safe to use on talloc'ed string as it only shrinks.
126 * It also doesn't affect the eos_ptr.
128 trim_char(temp,sepchar,sepchar);
130 DEBUG(10,("parse_dfs_path: temp = |%s| after trimming %c's\n",
131 temp, sepchar));
133 /* Now tokenize. */
134 /* Parse out hostname. */
135 p = strchr_m(temp,sepchar);
136 if(p == NULL) {
137 DEBUG(10,("parse_dfs_path: can't parse hostname from path %s\n",
138 temp));
140 * Possibly client sent a local path by mistake.
141 * Try and convert to a local path.
144 pdp->hostname = eos_ptr; /* "" */
145 pdp->servicename = eos_ptr; /* "" */
147 p = temp;
148 DEBUG(10,("parse_dfs_path: trying to convert %s "
149 "to a local path\n",
150 temp));
151 goto local_path;
153 *p = '\0';
154 pdp->hostname = temp;
156 DEBUG(10,("parse_dfs_path: hostname: %s\n",pdp->hostname));
158 /* Parse out servicename. */
159 servicename = p+1;
160 p = strchr_m(servicename,sepchar);
161 if (p) {
162 *p = '\0';
165 /* Is this really our servicename ? */
166 if (conn && !( strequal(servicename, lp_servicename(talloc_tos(), SNUM(conn)))
167 || (strequal(servicename, HOMES_NAME)
168 && strequal(lp_servicename(talloc_tos(), SNUM(conn)),
169 get_current_username()) )) ) {
170 DEBUG(10,("parse_dfs_path: %s is not our servicename\n",
171 servicename));
174 * Possibly client sent a local path by mistake.
175 * Try and convert to a local path.
178 pdp->hostname = eos_ptr; /* "" */
179 pdp->servicename = eos_ptr; /* "" */
181 /* Repair the path - replace the sepchar's
182 we nulled out */
183 servicename--;
184 *servicename = sepchar;
185 if (p) {
186 *p = sepchar;
189 p = temp;
190 DEBUG(10,("parse_dfs_path: trying to convert %s "
191 "to a local path\n",
192 temp));
193 goto local_path;
196 pdp->servicename = servicename;
198 DEBUG(10,("parse_dfs_path: servicename: %s\n",pdp->servicename));
200 if(p == NULL) {
201 /* Client sent self referral \server\share. */
202 pdp->reqpath = eos_ptr; /* "" */
203 return NT_STATUS_OK;
206 p++;
208 local_path:
210 *ppath_contains_wcard = False;
212 pdp->reqpath = p;
214 /* Rest is reqpath. */
215 if (pdp->posix_path) {
216 status = check_path_syntax_posix(pdp->reqpath);
217 } else {
218 if (allow_wcards) {
219 status = check_path_syntax_wcard(pdp->reqpath,
220 ppath_contains_wcard);
221 } else {
222 status = check_path_syntax(pdp->reqpath);
226 if (!NT_STATUS_IS_OK(status)) {
227 DEBUG(10,("parse_dfs_path: '%s' failed with %s\n",
228 p, nt_errstr(status) ));
229 return status;
232 DEBUG(10,("parse_dfs_path: rest of the path: %s\n",pdp->reqpath));
233 return NT_STATUS_OK;
236 /********************************************************
237 Fake up a connection struct for the VFS layer, for use in
238 applications (such as the python bindings), that do not want the
239 global working directory changed under them.
241 SMB_VFS_CONNECT requires root privileges.
242 *********************************************************/
244 static NTSTATUS create_conn_struct_as_root(TALLOC_CTX *ctx,
245 struct messaging_context *msg,
246 connection_struct **pconn,
247 int snum,
248 const char *path,
249 const struct auth_session_info *session_info)
251 connection_struct *conn;
252 char *connpath;
253 const char *vfs_user;
254 struct smbd_server_connection *sconn;
255 const char *servicename = lp_const_servicename(snum);
256 int ret;
258 sconn = talloc_zero(ctx, struct smbd_server_connection);
259 if (sconn == NULL) {
260 return NT_STATUS_NO_MEMORY;
263 sconn->raw_ev_ctx = samba_tevent_context_init(sconn);
264 if (sconn->raw_ev_ctx == NULL) {
265 TALLOC_FREE(sconn);
266 return NT_STATUS_NO_MEMORY;
269 sconn->root_ev_ctx = smbd_impersonate_root_create(sconn->raw_ev_ctx);
270 if (sconn->root_ev_ctx == NULL) {
271 TALLOC_FREE(sconn);
272 return NT_STATUS_NO_MEMORY;
274 sconn->guest_ev_ctx = smbd_impersonate_guest_create(sconn->raw_ev_ctx);
275 if (sconn->guest_ev_ctx == NULL) {
276 TALLOC_FREE(sconn);
277 return NT_STATUS_NO_MEMORY;
281 * We only provide sync threadpools.
283 ret = pthreadpool_tevent_init(sconn, 0, &sconn->sync_thread_pool);
284 if (ret != 0) {
285 TALLOC_FREE(sconn);
286 return NT_STATUS_NO_MEMORY;
288 sconn->raw_thread_pool = sconn->sync_thread_pool;
290 sconn->msg_ctx = msg;
292 conn = conn_new(sconn);
293 if (conn == NULL) {
294 TALLOC_FREE(sconn);
295 return NT_STATUS_NO_MEMORY;
298 /* Now we have conn, we need to make sconn a child of conn,
299 * for a proper talloc tree */
300 talloc_steal(conn, sconn);
302 if (snum == -1 && servicename == NULL) {
303 servicename = "Unknown Service (snum == -1)";
306 connpath = talloc_strdup(conn, path);
307 if (!connpath) {
308 TALLOC_FREE(conn);
309 return NT_STATUS_NO_MEMORY;
311 connpath = talloc_string_sub(conn,
312 connpath,
313 "%S",
314 servicename);
315 if (!connpath) {
316 TALLOC_FREE(conn);
317 return NT_STATUS_NO_MEMORY;
320 /* needed for smbd_vfs_init() */
322 conn->params->service = snum;
323 conn->cnum = TID_FIELD_INVALID;
325 if (session_info != NULL) {
326 conn->session_info = copy_session_info(conn, session_info);
327 if (conn->session_info == NULL) {
328 DEBUG(0, ("copy_serverinfo failed\n"));
329 TALLOC_FREE(conn);
330 return NT_STATUS_NO_MEMORY;
332 /* unix_info could be NULL in session_info */
333 if (conn->session_info->unix_info != NULL) {
334 vfs_user = conn->session_info->unix_info->unix_name;
335 } else {
336 vfs_user = get_current_username();
338 } else {
339 /* use current authenticated user in absence of session_info */
340 vfs_user = get_current_username();
344 * The impersonation has to be done by the caller
345 * of create_conn_struct_tos[_cwd]().
347 * Note: the context can't be changed anyway
348 * as we're using our own tevent_context
349 * and not a global one were other requests
350 * could change the current unix token.
352 * We just use a wrapper tevent_context in order
353 * to avoid crashes because TALLOC_FREE(conn->user_ev_ctx)
354 * would also remove sconn->raw_ev_ctx.
356 conn->user_ev_ctx = smbd_impersonate_debug_create(sconn->raw_ev_ctx,
357 "FAKE impersonation",
358 DBGLVL_DEBUG);
359 if (conn->user_ev_ctx == NULL) {
360 TALLOC_FREE(conn);
361 return NT_STATUS_NO_MEMORY;
364 set_conn_connectpath(conn, connpath);
367 * New code to check if there's a share security descriptor
368 * added from NT server manager. This is done after the
369 * smb.conf checks are done as we need a uid and token. JRA.
372 if (conn->session_info) {
373 share_access_check(conn->session_info->security_token,
374 servicename,
375 MAXIMUM_ALLOWED_ACCESS,
376 &conn->share_access);
378 if ((conn->share_access & FILE_WRITE_DATA) == 0) {
379 if ((conn->share_access & FILE_READ_DATA) == 0) {
380 /* No access, read or write. */
381 DEBUG(3,("create_conn_struct: connection to %s "
382 "denied due to security "
383 "descriptor.\n",
384 servicename));
385 conn_free(conn);
386 return NT_STATUS_ACCESS_DENIED;
387 } else {
388 conn->read_only = true;
391 } else {
392 conn->share_access = 0;
393 conn->read_only = true;
396 if (!smbd_vfs_init(conn)) {
397 NTSTATUS status = map_nt_error_from_unix(errno);
398 DEBUG(0,("create_conn_struct: smbd_vfs_init failed.\n"));
399 conn_free(conn);
400 return status;
403 /* this must be the first filesystem operation that we do */
404 if (SMB_VFS_CONNECT(conn, servicename, vfs_user) < 0) {
405 DEBUG(0,("VFS connect failed!\n"));
406 conn_free(conn);
407 return NT_STATUS_UNSUCCESSFUL;
410 talloc_free(conn->origpath);
411 conn->origpath = talloc_strdup(conn, conn->connectpath);
412 if (conn->origpath == NULL) {
413 conn_free(conn);
414 return NT_STATUS_NO_MEMORY;
417 conn->fs_capabilities = SMB_VFS_FS_CAPABILITIES(conn, &conn->ts_res);
418 conn->tcon_done = true;
419 *pconn = talloc_move(ctx, &conn);
421 return NT_STATUS_OK;
424 static int conn_struct_tos_destructor(struct conn_struct_tos *c)
426 if (c->oldcwd_fname != NULL) {
427 vfs_ChDir(c->conn, c->oldcwd_fname);
428 TALLOC_FREE(c->oldcwd_fname);
430 SMB_VFS_DISCONNECT(c->conn);
431 conn_free(c->conn);
432 return 0;
435 /********************************************************
436 Fake up a connection struct for the VFS layer, for use in
437 applications (such as the python bindings), that do not want the
438 global working directory changed under them.
440 SMB_VFS_CONNECT requires root privileges.
441 This temporary uses become_root() and unbecome_root().
443 But further impersonation has to be cone by the caller.
444 *********************************************************/
445 NTSTATUS create_conn_struct_tos(struct messaging_context *msg,
446 int snum,
447 const char *path,
448 const struct auth_session_info *session_info,
449 struct conn_struct_tos **_c)
451 struct conn_struct_tos *c = NULL;
452 NTSTATUS status;
454 *_c = NULL;
456 c = talloc_zero(talloc_tos(), struct conn_struct_tos);
457 if (c == NULL) {
458 return NT_STATUS_NO_MEMORY;
461 become_root();
462 status = create_conn_struct_as_root(c,
463 msg,
464 &c->conn,
465 snum,
466 path,
467 session_info);
468 unbecome_root();
469 if (!NT_STATUS_IS_OK(status)) {
470 TALLOC_FREE(c);
471 return status;
474 talloc_set_destructor(c, conn_struct_tos_destructor);
476 *_c = c;
477 return NT_STATUS_OK;
480 /********************************************************
481 Fake up a connection struct for the VFS layer.
482 Note: this performs a vfs connect and CHANGES CWD !!!! JRA.
484 See also the comment for create_conn_struct_tos() above!
486 The CWD change is reverted by the destructor of
487 conn_struct_tos when the current talloc_tos() is destroyed.
488 *********************************************************/
489 NTSTATUS create_conn_struct_tos_cwd(struct messaging_context *msg,
490 int snum,
491 const char *path,
492 const struct auth_session_info *session_info,
493 struct conn_struct_tos **_c)
495 struct conn_struct_tos *c = NULL;
496 struct smb_filename smb_fname_connectpath = {0};
497 NTSTATUS status;
499 *_c = NULL;
501 status = create_conn_struct_tos(msg,
502 snum,
503 path,
504 session_info,
505 &c);
506 if (!NT_STATUS_IS_OK(status)) {
507 return status;
511 * Windows seems to insist on doing trans2getdfsreferral() calls on
512 * the IPC$ share as the anonymous user. If we try to chdir as that
513 * user we will fail.... WTF ? JRA.
516 c->oldcwd_fname = vfs_GetWd(c, c->conn);
517 if (c->oldcwd_fname == NULL) {
518 status = map_nt_error_from_unix(errno);
519 DEBUG(3, ("vfs_GetWd failed: %s\n", strerror(errno)));
520 TALLOC_FREE(c);
521 return status;
524 smb_fname_connectpath = (struct smb_filename) {
525 .base_name = c->conn->connectpath
528 if (vfs_ChDir(c->conn, &smb_fname_connectpath) != 0) {
529 status = map_nt_error_from_unix(errno);
530 DBG_NOTICE("Can't ChDir to new conn path %s. "
531 "Error was %s\n",
532 c->conn->connectpath, strerror(errno));
533 TALLOC_FREE(c->oldcwd_fname);
534 TALLOC_FREE(c);
535 return status;
538 *_c = c;
539 return NT_STATUS_OK;
542 static void shuffle_strlist(char **list, int count)
544 int i;
545 uint32_t r;
546 char *tmp;
548 for (i = count; i > 1; i--) {
549 r = generate_random() % i;
551 tmp = list[i-1];
552 list[i-1] = list[r];
553 list[r] = tmp;
557 /**********************************************************************
558 Parse the contents of a symlink to verify if it is an msdfs referral
559 A valid referral is of the form:
561 msdfs:server1\share1,server2\share2
562 msdfs:server1\share1\pathname,server2\share2\pathname
563 msdfs:server1/share1,server2/share2
564 msdfs:server1/share1/pathname,server2/share2/pathname.
566 Note that the alternate paths returned here must be of the canonicalized
567 form:
569 \server\share or
570 \server\share\path\to\file,
572 even in posix path mode. This is because we have no knowledge if the
573 server we're referring to understands posix paths.
574 **********************************************************************/
576 static bool parse_msdfs_symlink(TALLOC_CTX *ctx,
577 int snum,
578 const char *target,
579 struct referral **preflist,
580 int *refcount)
582 char *temp = NULL;
583 char *prot;
584 char **alt_path = NULL;
585 int count = 0, i;
586 struct referral *reflist;
587 char *saveptr;
589 temp = talloc_strdup(ctx, target);
590 if (!temp) {
591 return False;
593 prot = strtok_r(temp, ":", &saveptr);
594 if (!prot) {
595 DEBUG(0,("parse_msdfs_symlink: invalid path !\n"));
596 return False;
599 alt_path = talloc_array(ctx, char *, MAX_REFERRAL_COUNT);
600 if (!alt_path) {
601 return False;
604 /* parse out the alternate paths */
605 while((count<MAX_REFERRAL_COUNT) &&
606 ((alt_path[count] = strtok_r(NULL, ",", &saveptr)) != NULL)) {
607 count++;
610 /* shuffle alternate paths */
611 if (lp_msdfs_shuffle_referrals(snum)) {
612 shuffle_strlist(alt_path, count);
615 DEBUG(10,("parse_msdfs_symlink: count=%d\n", count));
617 if (count) {
618 reflist = *preflist = talloc_zero_array(ctx,
619 struct referral, count);
620 if(reflist == NULL) {
621 TALLOC_FREE(alt_path);
622 return False;
624 } else {
625 reflist = *preflist = NULL;
628 for(i=0;i<count;i++) {
629 char *p;
631 /* Canonicalize link target.
632 * Replace all /'s in the path by a \ */
633 string_replace(alt_path[i], '/', '\\');
635 /* Remove leading '\\'s */
636 p = alt_path[i];
637 while (*p && (*p == '\\')) {
638 p++;
641 reflist[i].alternate_path = talloc_asprintf(ctx,
642 "\\%s",
644 if (!reflist[i].alternate_path) {
645 return False;
648 reflist[i].proximity = 0;
649 reflist[i].ttl = REFERRAL_TTL;
650 DEBUG(10, ("parse_msdfs_symlink: Created alt path: %s\n",
651 reflist[i].alternate_path));
654 *refcount = count;
656 TALLOC_FREE(alt_path);
657 return True;
660 /**********************************************************************
661 Returns true if the unix path is a valid msdfs symlink and also
662 returns the target string from inside the link.
663 **********************************************************************/
665 static bool is_msdfs_link_internal(TALLOC_CTX *ctx,
666 connection_struct *conn,
667 struct smb_filename *smb_fname,
668 char **pp_link_target)
670 int referral_len = 0;
671 #if defined(HAVE_BROKEN_READLINK)
672 char link_target_buf[PATH_MAX];
673 #else
674 char link_target_buf[7];
675 #endif
676 size_t bufsize = 0;
677 char *link_target = NULL;
679 if (pp_link_target) {
680 bufsize = 1024;
681 link_target = talloc_array(ctx, char, bufsize);
682 if (!link_target) {
683 return False;
685 *pp_link_target = link_target;
686 } else {
687 bufsize = sizeof(link_target_buf);
688 link_target = link_target_buf;
691 if (SMB_VFS_LSTAT(conn, smb_fname) != 0) {
692 DEBUG(5,("is_msdfs_link_read_target: %s does not exist.\n",
693 smb_fname->base_name));
694 goto err;
696 if (!S_ISLNK(smb_fname->st.st_ex_mode)) {
697 DEBUG(5,("is_msdfs_link_read_target: %s is not a link.\n",
698 smb_fname->base_name));
699 goto err;
702 referral_len = SMB_VFS_READLINK(conn, smb_fname,
703 link_target, bufsize - 1);
704 if (referral_len == -1) {
705 DEBUG(0,("is_msdfs_link_read_target: Error reading "
706 "msdfs link %s: %s\n",
707 smb_fname->base_name, strerror(errno)));
708 goto err;
710 link_target[referral_len] = '\0';
712 DEBUG(5,("is_msdfs_link_internal: %s -> %s\n", smb_fname->base_name,
713 link_target));
715 if (!strnequal(link_target, "msdfs:", 6)) {
716 goto err;
718 return True;
720 err:
722 if (link_target != link_target_buf) {
723 TALLOC_FREE(link_target);
725 return False;
728 /**********************************************************************
729 Returns true if the unix path is a valid msdfs symlink.
730 **********************************************************************/
732 bool is_msdfs_link(connection_struct *conn,
733 struct smb_filename *smb_fname)
735 return is_msdfs_link_internal(talloc_tos(),
736 conn,
737 smb_fname,
738 NULL);
741 /*****************************************************************
742 Used by other functions to decide if a dfs path is remote,
743 and to get the list of referred locations for that remote path.
745 search_flag: For findfirsts, dfs links themselves are not
746 redirected, but paths beyond the links are. For normal smb calls,
747 even dfs links need to be redirected.
749 consumedcntp: how much of the dfs path is being redirected. the client
750 should try the remaining path on the redirected server.
752 If this returns NT_STATUS_PATH_NOT_COVERED the contents of the msdfs
753 link redirect are in targetpath.
754 *****************************************************************/
756 static NTSTATUS dfs_path_lookup(TALLOC_CTX *ctx,
757 connection_struct *conn,
758 const char *dfspath, /* Incoming complete dfs path */
759 const struct dfs_path *pdp, /* Parsed out
760 server+share+extrapath. */
761 uint32_t ucf_flags,
762 int *consumedcntp,
763 char **pp_targetpath)
765 char *p = NULL;
766 char *q = NULL;
767 NTSTATUS status;
768 struct smb_filename *smb_fname = NULL;
769 char *canon_dfspath = NULL; /* Canonicalized dfs path. (only '/'
770 components). */
772 DEBUG(10,("dfs_path_lookup: Conn path = %s reqpath = %s\n",
773 conn->connectpath, pdp->reqpath));
776 * Note the unix path conversion here we're doing we
777 * throw away. We're looking for a symlink for a dfs
778 * resolution, if we don't find it we'll do another
779 * unix_convert later in the codepath.
782 status = unix_convert(ctx, conn, pdp->reqpath, &smb_fname,
783 ucf_flags);
785 if (!NT_STATUS_IS_OK(status)) {
786 if (!NT_STATUS_EQUAL(status,
787 NT_STATUS_OBJECT_PATH_NOT_FOUND)) {
788 return status;
790 if (smb_fname == NULL || smb_fname->base_name == NULL) {
791 return status;
795 /* Optimization - check if we can redirect the whole path. */
797 if (is_msdfs_link_internal(ctx, conn, smb_fname, pp_targetpath)) {
798 /* XX_ALLOW_WCARD_XXX is called from search functions. */
799 if (ucf_flags &
800 (UCF_COND_ALLOW_WCARD_LCOMP|
801 UCF_ALWAYS_ALLOW_WCARD_LCOMP)) {
802 DEBUG(6,("dfs_path_lookup (FindFirst) No redirection "
803 "for dfs link %s.\n", dfspath));
804 status = NT_STATUS_OK;
805 goto out;
808 DEBUG(6,("dfs_path_lookup: %s resolves to a "
809 "valid dfs link %s.\n", dfspath,
810 pp_targetpath ? *pp_targetpath : ""));
812 if (consumedcntp) {
813 *consumedcntp = strlen(dfspath);
815 status = NT_STATUS_PATH_NOT_COVERED;
816 goto out;
819 /* Prepare to test only for '/' components in the given path,
820 * so if a Windows path replace all '\\' characters with '/'.
821 * For a POSIX DFS path we know all separators are already '/'. */
823 canon_dfspath = talloc_strdup(ctx, dfspath);
824 if (!canon_dfspath) {
825 status = NT_STATUS_NO_MEMORY;
826 goto out;
828 if (!pdp->posix_path) {
829 string_replace(canon_dfspath, '\\', '/');
833 * localpath comes out of unix_convert, so it has
834 * no trailing backslash. Make sure that canon_dfspath hasn't either.
835 * Fix for bug #4860 from Jan Martin <Jan.Martin@rwedea.com>.
838 trim_char(canon_dfspath,0,'/');
841 * Redirect if any component in the path is a link.
842 * We do this by walking backwards through the
843 * local path, chopping off the last component
844 * in both the local path and the canonicalized
845 * DFS path. If we hit a DFS link then we're done.
848 p = strrchr_m(smb_fname->base_name, '/');
849 if (consumedcntp) {
850 q = strrchr_m(canon_dfspath, '/');
853 while (p) {
854 *p = '\0';
855 if (q) {
856 *q = '\0';
859 if (is_msdfs_link_internal(ctx, conn,
860 smb_fname, pp_targetpath)) {
861 DEBUG(4, ("dfs_path_lookup: Redirecting %s because "
862 "parent %s is dfs link\n", dfspath,
863 smb_fname_str_dbg(smb_fname)));
865 if (consumedcntp) {
866 *consumedcntp = strlen(canon_dfspath);
867 DEBUG(10, ("dfs_path_lookup: Path consumed: %s "
868 "(%d)\n",
869 canon_dfspath,
870 *consumedcntp));
873 status = NT_STATUS_PATH_NOT_COVERED;
874 goto out;
877 /* Step back on the filesystem. */
878 p = strrchr_m(smb_fname->base_name, '/');
880 if (consumedcntp) {
881 /* And in the canonicalized dfs path. */
882 q = strrchr_m(canon_dfspath, '/');
886 status = NT_STATUS_OK;
887 out:
888 TALLOC_FREE(smb_fname);
889 return status;
892 /*****************************************************************
893 Decides if a dfs pathname should be redirected or not.
894 If not, the pathname is converted to a tcon-relative local unix path
896 search_wcard_flag: this flag performs 2 functions both related
897 to searches. See resolve_dfs_path() and parse_dfs_path_XX()
898 for details.
900 This function can return NT_STATUS_OK, meaning use the returned path as-is
901 (mapped into a local path).
902 or NT_STATUS_NOT_COVERED meaning return a DFS redirect, or
903 any other NT_STATUS error which is a genuine error to be
904 returned to the client.
905 *****************************************************************/
907 static NTSTATUS dfs_redirect(TALLOC_CTX *ctx,
908 connection_struct *conn,
909 const char *path_in,
910 uint32_t ucf_flags,
911 bool allow_broken_path,
912 char **pp_path_out,
913 bool *ppath_contains_wcard)
915 NTSTATUS status;
916 bool search_wcard_flag = (ucf_flags &
917 (UCF_COND_ALLOW_WCARD_LCOMP|UCF_ALWAYS_ALLOW_WCARD_LCOMP));
918 struct dfs_path *pdp = talloc(ctx, struct dfs_path);
920 if (!pdp) {
921 return NT_STATUS_NO_MEMORY;
924 status = parse_dfs_path(conn, path_in, search_wcard_flag,
925 allow_broken_path, pdp,
926 ppath_contains_wcard);
927 if (!NT_STATUS_IS_OK(status)) {
928 TALLOC_FREE(pdp);
929 return status;
932 if (pdp->reqpath[0] == '\0') {
933 TALLOC_FREE(pdp);
934 *pp_path_out = talloc_strdup(ctx, "");
935 if (!*pp_path_out) {
936 return NT_STATUS_NO_MEMORY;
938 DEBUG(5,("dfs_redirect: self-referral.\n"));
939 return NT_STATUS_OK;
942 /* If dfs pathname for a non-dfs share, convert to tcon-relative
943 path and return OK */
945 if (!lp_msdfs_root(SNUM(conn))) {
946 *pp_path_out = talloc_strdup(ctx, pdp->reqpath);
947 TALLOC_FREE(pdp);
948 if (!*pp_path_out) {
949 return NT_STATUS_NO_MEMORY;
951 return NT_STATUS_OK;
954 /* If it looked like a local path (zero hostname/servicename)
955 * just treat as a tcon-relative path. */
957 if (pdp->hostname[0] == '\0' && pdp->servicename[0] == '\0') {
958 *pp_path_out = talloc_strdup(ctx, pdp->reqpath);
959 TALLOC_FREE(pdp);
960 if (!*pp_path_out) {
961 return NT_STATUS_NO_MEMORY;
963 return NT_STATUS_OK;
966 if (!( strequal(pdp->servicename, lp_servicename(talloc_tos(), SNUM(conn)))
967 || (strequal(pdp->servicename, HOMES_NAME)
968 && strequal(lp_servicename(talloc_tos(), SNUM(conn)),
969 conn->session_info->unix_info->sanitized_username) )) ) {
971 /* The given sharename doesn't match this connection. */
972 TALLOC_FREE(pdp);
974 return NT_STATUS_OBJECT_PATH_NOT_FOUND;
977 status = dfs_path_lookup(ctx, conn, path_in, pdp,
978 ucf_flags, NULL, NULL);
979 if (!NT_STATUS_IS_OK(status)) {
980 if (NT_STATUS_EQUAL(status, NT_STATUS_PATH_NOT_COVERED)) {
981 DEBUG(3,("dfs_redirect: Redirecting %s\n", path_in));
982 } else {
983 DEBUG(10,("dfs_redirect: dfs_path_lookup "
984 "failed for %s with %s\n",
985 path_in, nt_errstr(status) ));
987 return status;
990 DEBUG(3,("dfs_redirect: Not redirecting %s.\n", path_in));
992 /* Form non-dfs tcon-relative path */
993 *pp_path_out = talloc_strdup(ctx, pdp->reqpath);
994 TALLOC_FREE(pdp);
995 if (!*pp_path_out) {
996 return NT_STATUS_NO_MEMORY;
999 DEBUG(3,("dfs_redirect: Path %s converted to non-dfs path %s\n",
1000 path_in,
1001 *pp_path_out));
1003 return NT_STATUS_OK;
1006 /**********************************************************************
1007 Return a self referral.
1008 **********************************************************************/
1010 static NTSTATUS self_ref(TALLOC_CTX *ctx,
1011 const char *dfs_path,
1012 struct junction_map *jucn,
1013 int *consumedcntp,
1014 bool *self_referralp)
1016 struct referral *ref;
1018 *self_referralp = True;
1020 jucn->referral_count = 1;
1021 if((ref = talloc_zero(ctx, struct referral)) == NULL) {
1022 return NT_STATUS_NO_MEMORY;
1025 ref->alternate_path = talloc_strdup(ctx, dfs_path);
1026 if (!ref->alternate_path) {
1027 TALLOC_FREE(ref);
1028 return NT_STATUS_NO_MEMORY;
1030 ref->proximity = 0;
1031 ref->ttl = REFERRAL_TTL;
1032 jucn->referral_list = ref;
1033 *consumedcntp = strlen(dfs_path);
1034 return NT_STATUS_OK;
1037 /**********************************************************************
1038 Gets valid referrals for a dfs path and fills up the
1039 junction_map structure.
1040 **********************************************************************/
1042 NTSTATUS get_referred_path(TALLOC_CTX *ctx,
1043 const char *dfs_path,
1044 const struct tsocket_address *remote_address,
1045 const struct tsocket_address *local_address,
1046 bool allow_broken_path,
1047 struct junction_map *jucn,
1048 int *consumedcntp,
1049 bool *self_referralp)
1051 TALLOC_CTX *frame = talloc_stackframe();
1052 struct conn_struct_tos *c = NULL;
1053 struct connection_struct *conn = NULL;
1054 char *targetpath = NULL;
1055 int snum;
1056 NTSTATUS status = NT_STATUS_NOT_FOUND;
1057 bool dummy;
1058 struct dfs_path *pdp = talloc_zero(frame, struct dfs_path);
1060 if (!pdp) {
1061 TALLOC_FREE(frame);
1062 return NT_STATUS_NO_MEMORY;
1065 *self_referralp = False;
1067 status = parse_dfs_path(NULL, dfs_path, False, allow_broken_path,
1068 pdp, &dummy);
1069 if (!NT_STATUS_IS_OK(status)) {
1070 TALLOC_FREE(frame);
1071 return status;
1074 jucn->service_name = talloc_strdup(ctx, pdp->servicename);
1075 jucn->volume_name = talloc_strdup(ctx, pdp->reqpath);
1076 if (!jucn->service_name || !jucn->volume_name) {
1077 TALLOC_FREE(frame);
1078 return NT_STATUS_NO_MEMORY;
1081 /* Verify the share is a dfs root */
1082 snum = lp_servicenumber(jucn->service_name);
1083 if(snum < 0) {
1084 char *service_name = NULL;
1085 if ((snum = find_service(ctx, jucn->service_name, &service_name)) < 0) {
1086 TALLOC_FREE(frame);
1087 return NT_STATUS_NOT_FOUND;
1089 if (!service_name) {
1090 TALLOC_FREE(frame);
1091 return NT_STATUS_NO_MEMORY;
1093 TALLOC_FREE(jucn->service_name);
1094 jucn->service_name = talloc_strdup(ctx, service_name);
1095 if (!jucn->service_name) {
1096 TALLOC_FREE(frame);
1097 return NT_STATUS_NO_MEMORY;
1101 if (!lp_msdfs_root(snum) && (*lp_msdfs_proxy(talloc_tos(), snum) == '\0')) {
1102 DEBUG(3,("get_referred_path: |%s| in dfs path %s is not "
1103 "a dfs root.\n",
1104 pdp->servicename, dfs_path));
1105 TALLOC_FREE(frame);
1106 return NT_STATUS_NOT_FOUND;
1110 * Self referrals are tested with a anonymous IPC connection and
1111 * a GET_DFS_REFERRAL call to \\server\share. (which means
1112 * dp.reqpath[0] points to an empty string). create_conn_struct cd's
1113 * into the directory and will fail if it cannot (as the anonymous
1114 * user). Cope with this.
1117 if (pdp->reqpath[0] == '\0') {
1118 char *tmp;
1119 struct referral *ref;
1120 int refcount;
1122 if (*lp_msdfs_proxy(talloc_tos(), snum) == '\0') {
1123 TALLOC_FREE(frame);
1124 return self_ref(ctx,
1125 dfs_path,
1126 jucn,
1127 consumedcntp,
1128 self_referralp);
1132 * It's an msdfs proxy share. Redirect to
1133 * the configured target share.
1136 tmp = talloc_asprintf(frame, "msdfs:%s",
1137 lp_msdfs_proxy(frame, snum));
1138 if (tmp == NULL) {
1139 TALLOC_FREE(frame);
1140 return NT_STATUS_NO_MEMORY;
1143 if (!parse_msdfs_symlink(ctx, snum, tmp, &ref, &refcount)) {
1144 TALLOC_FREE(frame);
1145 return NT_STATUS_INVALID_PARAMETER;
1147 jucn->referral_count = refcount;
1148 jucn->referral_list = ref;
1149 *consumedcntp = strlen(dfs_path);
1150 TALLOC_FREE(frame);
1151 return NT_STATUS_OK;
1154 status = create_conn_struct_tos_cwd(global_messaging_context(),
1155 snum,
1156 lp_path(frame, snum),
1157 NULL,
1158 &c);
1159 if (!NT_STATUS_IS_OK(status)) {
1160 TALLOC_FREE(frame);
1161 return status;
1163 conn = c->conn;
1166 * TODO
1168 * The remote and local address should be passed down to
1169 * create_conn_struct_cwd.
1171 if (conn->sconn->remote_address == NULL) {
1172 conn->sconn->remote_address =
1173 tsocket_address_copy(remote_address, conn->sconn);
1174 if (conn->sconn->remote_address == NULL) {
1175 TALLOC_FREE(frame);
1176 return NT_STATUS_NO_MEMORY;
1179 if (conn->sconn->local_address == NULL) {
1180 conn->sconn->local_address =
1181 tsocket_address_copy(local_address, conn->sconn);
1182 if (conn->sconn->local_address == NULL) {
1183 TALLOC_FREE(frame);
1184 return NT_STATUS_NO_MEMORY;
1188 /* If this is a DFS path dfs_lookup should return
1189 * NT_STATUS_PATH_NOT_COVERED. */
1191 status = dfs_path_lookup(ctx, conn, dfs_path, pdp,
1192 0, consumedcntp, &targetpath);
1194 if (!NT_STATUS_EQUAL(status, NT_STATUS_PATH_NOT_COVERED)) {
1195 DEBUG(3,("get_referred_path: No valid referrals for path %s\n",
1196 dfs_path));
1197 if (NT_STATUS_IS_OK(status)) {
1199 * We are in an error path here (we
1200 * know it's not a DFS path), but
1201 * dfs_path_lookup() can return
1202 * NT_STATUS_OK. Ensure we always
1203 * return a valid error code.
1205 * #9588 - ACLs are not inherited to directories
1206 * for DFS shares.
1208 status = NT_STATUS_NOT_FOUND;
1210 goto err_exit;
1213 /* We know this is a valid dfs link. Parse the targetpath. */
1214 if (!parse_msdfs_symlink(ctx, snum, targetpath,
1215 &jucn->referral_list,
1216 &jucn->referral_count)) {
1217 DEBUG(3,("get_referred_path: failed to parse symlink "
1218 "target %s\n", targetpath ));
1219 status = NT_STATUS_NOT_FOUND;
1220 goto err_exit;
1223 status = NT_STATUS_OK;
1224 err_exit:
1225 TALLOC_FREE(frame);
1226 return status;
1229 /******************************************************************
1230 Set up the DFS referral for the dfs pathname. This call returns
1231 the amount of the path covered by this server, and where the
1232 client should be redirected to. This is the meat of the
1233 TRANS2_GET_DFS_REFERRAL call.
1234 ******************************************************************/
1236 int setup_dfs_referral(connection_struct *orig_conn,
1237 const char *dfs_path,
1238 int max_referral_level,
1239 char **ppdata, NTSTATUS *pstatus)
1241 char *pdata = *ppdata;
1242 int reply_size = 0;
1243 struct dfs_GetDFSReferral *r;
1244 DATA_BLOB blob = data_blob_null;
1245 NTSTATUS status;
1246 enum ndr_err_code ndr_err;
1248 r = talloc_zero(talloc_tos(), struct dfs_GetDFSReferral);
1249 if (r == NULL) {
1250 *pstatus = NT_STATUS_NO_MEMORY;
1251 return -1;
1254 r->in.req.max_referral_level = max_referral_level;
1255 r->in.req.servername = talloc_strdup(r, dfs_path);
1256 if (r->in.req.servername == NULL) {
1257 talloc_free(r);
1258 *pstatus = NT_STATUS_NO_MEMORY;
1259 return -1;
1262 status = SMB_VFS_GET_DFS_REFERRALS(orig_conn, r);
1263 if (!NT_STATUS_IS_OK(status)) {
1264 talloc_free(r);
1265 *pstatus = status;
1266 return -1;
1269 ndr_err = ndr_push_struct_blob(&blob, r,
1270 r->out.resp,
1271 (ndr_push_flags_fn_t)ndr_push_dfs_referral_resp);
1272 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1273 TALLOC_FREE(r);
1274 *pstatus = NT_STATUS_INVALID_PARAMETER;
1275 return -1;
1278 pdata = (char *)SMB_REALLOC(pdata, blob.length);
1279 if(pdata == NULL) {
1280 TALLOC_FREE(r);
1281 DEBUG(0,("referral setup:"
1282 "malloc failed for Realloc!\n"));
1283 return -1;
1285 *ppdata = pdata;
1286 reply_size = blob.length;
1287 memcpy(pdata, blob.data, blob.length);
1288 TALLOC_FREE(r);
1290 *pstatus = NT_STATUS_OK;
1291 return reply_size;
1294 /**********************************************************************
1295 The following functions are called by the NETDFS RPC pipe functions
1296 **********************************************************************/
1298 /*********************************************************************
1299 Creates a junction structure from a DFS pathname
1300 **********************************************************************/
1302 bool create_junction(TALLOC_CTX *ctx,
1303 const char *dfs_path,
1304 bool allow_broken_path,
1305 struct junction_map *jucn)
1307 int snum;
1308 bool dummy;
1309 struct dfs_path *pdp = talloc(ctx,struct dfs_path);
1310 NTSTATUS status;
1312 if (!pdp) {
1313 return False;
1315 status = parse_dfs_path(NULL, dfs_path, False, allow_broken_path,
1316 pdp, &dummy);
1317 if (!NT_STATUS_IS_OK(status)) {
1318 return False;
1321 /* check if path is dfs : validate first token */
1322 if (!is_myname_or_ipaddr(pdp->hostname)) {
1323 DEBUG(4,("create_junction: Invalid hostname %s "
1324 "in dfs path %s\n",
1325 pdp->hostname, dfs_path));
1326 TALLOC_FREE(pdp);
1327 return False;
1330 /* Check for a non-DFS share */
1331 snum = lp_servicenumber(pdp->servicename);
1333 if(snum < 0 || !lp_msdfs_root(snum)) {
1334 DEBUG(4,("create_junction: %s is not an msdfs root.\n",
1335 pdp->servicename));
1336 TALLOC_FREE(pdp);
1337 return False;
1340 jucn->service_name = talloc_strdup(ctx, pdp->servicename);
1341 jucn->volume_name = talloc_strdup(ctx, pdp->reqpath);
1342 jucn->comment = lp_comment(ctx, snum);
1344 TALLOC_FREE(pdp);
1345 if (!jucn->service_name || !jucn->volume_name || ! jucn->comment) {
1346 return False;
1348 return True;
1351 /**********************************************************************
1352 Forms a valid Unix pathname from the junction
1353 **********************************************************************/
1355 static bool junction_to_local_path_tos(const struct junction_map *jucn,
1356 char **pp_path_out,
1357 connection_struct **conn_out)
1359 struct conn_struct_tos *c = NULL;
1360 int snum;
1361 char *path_out = NULL;
1362 NTSTATUS status;
1364 snum = lp_servicenumber(jucn->service_name);
1365 if(snum < 0) {
1366 return False;
1368 status = create_conn_struct_tos_cwd(global_messaging_context(),
1369 snum,
1370 lp_path(talloc_tos(), snum),
1371 NULL,
1372 &c);
1373 if (!NT_STATUS_IS_OK(status)) {
1374 return False;
1377 path_out = talloc_asprintf(c,
1378 "%s/%s",
1379 lp_path(talloc_tos(), snum),
1380 jucn->volume_name);
1381 if (path_out == NULL) {
1382 TALLOC_FREE(c);
1383 return False;
1385 *pp_path_out = path_out;
1386 *conn_out = c->conn;
1387 return True;
1390 bool create_msdfs_link(const struct junction_map *jucn)
1392 TALLOC_CTX *frame = talloc_stackframe();
1393 char *path = NULL;
1394 char *msdfs_link = NULL;
1395 connection_struct *conn;
1396 int i=0;
1397 bool insert_comma = False;
1398 bool ret = False;
1399 struct smb_filename *smb_fname = NULL;
1400 bool ok;
1402 ok = junction_to_local_path_tos(jucn, &path, &conn);
1403 if (!ok) {
1404 TALLOC_FREE(frame);
1405 return False;
1408 /* Form the msdfs_link contents */
1409 msdfs_link = talloc_strdup(conn, "msdfs:");
1410 if (!msdfs_link) {
1411 goto out;
1413 for(i=0; i<jucn->referral_count; i++) {
1414 char *refpath = jucn->referral_list[i].alternate_path;
1416 /* Alternate paths always use Windows separators. */
1417 trim_char(refpath, '\\', '\\');
1418 if(*refpath == '\0') {
1419 if (i == 0) {
1420 insert_comma = False;
1422 continue;
1424 if (i > 0 && insert_comma) {
1425 msdfs_link = talloc_asprintf_append_buffer(msdfs_link,
1426 ",%s",
1427 refpath);
1428 } else {
1429 msdfs_link = talloc_asprintf_append_buffer(msdfs_link,
1430 "%s",
1431 refpath);
1434 if (!msdfs_link) {
1435 goto out;
1437 if (!insert_comma) {
1438 insert_comma = True;
1442 DEBUG(5,("create_msdfs_link: Creating new msdfs link: %s -> %s\n",
1443 path, msdfs_link));
1445 smb_fname = synthetic_smb_fname(frame,
1446 path,
1447 NULL,
1448 NULL,
1450 if (smb_fname == NULL) {
1451 errno = ENOMEM;
1452 goto out;
1455 if(SMB_VFS_SYMLINK(conn, msdfs_link, smb_fname) < 0) {
1456 if (errno == EEXIST) {
1457 if(SMB_VFS_UNLINK(conn, smb_fname)!=0) {
1458 TALLOC_FREE(smb_fname);
1459 goto out;
1462 if (SMB_VFS_SYMLINK(conn, msdfs_link, smb_fname) < 0) {
1463 DEBUG(1,("create_msdfs_link: symlink failed "
1464 "%s -> %s\nError: %s\n",
1465 path, msdfs_link, strerror(errno)));
1466 goto out;
1470 ret = True;
1472 out:
1473 TALLOC_FREE(frame);
1474 return ret;
1477 bool remove_msdfs_link(const struct junction_map *jucn)
1479 TALLOC_CTX *frame = talloc_stackframe();
1480 char *path = NULL;
1481 connection_struct *conn;
1482 bool ret = False;
1483 struct smb_filename *smb_fname;
1484 bool ok;
1486 ok = junction_to_local_path_tos(jucn, &path, &conn);
1487 if (!ok) {
1488 TALLOC_FREE(frame);
1489 return false;
1492 smb_fname = synthetic_smb_fname(frame,
1493 path,
1494 NULL,
1495 NULL,
1497 if (smb_fname == NULL) {
1498 TALLOC_FREE(frame);
1499 errno = ENOMEM;
1500 return false;
1503 if( SMB_VFS_UNLINK(conn, smb_fname) == 0 ) {
1504 ret = True;
1507 TALLOC_FREE(frame);
1508 return ret;
1511 /*********************************************************************
1512 Return the number of DFS links at the root of this share.
1513 *********************************************************************/
1515 static int count_dfs_links(TALLOC_CTX *ctx, int snum)
1517 TALLOC_CTX *frame = talloc_stackframe();
1518 size_t cnt = 0;
1519 DIR *dirp = NULL;
1520 const char *dname = NULL;
1521 char *talloced = NULL;
1522 const char *connect_path = lp_path(frame, snum);
1523 const char *msdfs_proxy = lp_msdfs_proxy(frame, snum);
1524 struct conn_struct_tos *c = NULL;
1525 connection_struct *conn = NULL;
1526 NTSTATUS status;
1527 struct smb_filename *smb_fname = NULL;
1529 if(*connect_path == '\0') {
1530 TALLOC_FREE(frame);
1531 return 0;
1535 * Fake up a connection struct for the VFS layer.
1538 status = create_conn_struct_tos_cwd(global_messaging_context(),
1539 snum,
1540 connect_path,
1541 NULL,
1542 &c);
1543 if (!NT_STATUS_IS_OK(status)) {
1544 DEBUG(3, ("create_conn_struct failed: %s\n",
1545 nt_errstr(status)));
1546 TALLOC_FREE(frame);
1547 return 0;
1549 conn = c->conn;
1551 /* Count a link for the msdfs root - convention */
1552 cnt = 1;
1554 /* No more links if this is an msdfs proxy. */
1555 if (*msdfs_proxy != '\0') {
1556 goto out;
1559 smb_fname = synthetic_smb_fname(frame,
1560 ".",
1561 NULL,
1562 NULL,
1564 if (smb_fname == NULL) {
1565 goto out;
1568 /* Now enumerate all dfs links */
1569 dirp = SMB_VFS_OPENDIR(conn, smb_fname, NULL, 0);
1570 if(!dirp) {
1571 goto out;
1574 while ((dname = vfs_readdirname(conn, dirp, NULL, &talloced))
1575 != NULL) {
1576 struct smb_filename *smb_dname =
1577 synthetic_smb_fname(frame,
1578 dname,
1579 NULL,
1580 NULL,
1582 if (smb_dname == NULL) {
1583 goto out;
1585 if (is_msdfs_link(conn, smb_dname)) {
1586 cnt++;
1588 TALLOC_FREE(talloced);
1589 TALLOC_FREE(smb_dname);
1592 SMB_VFS_CLOSEDIR(conn,dirp);
1594 out:
1595 TALLOC_FREE(frame);
1596 return cnt;
1599 /*********************************************************************
1600 *********************************************************************/
1602 static int form_junctions(TALLOC_CTX *ctx,
1603 int snum,
1604 struct junction_map *jucn,
1605 size_t jn_remain)
1607 TALLOC_CTX *frame = talloc_stackframe();
1608 size_t cnt = 0;
1609 DIR *dirp = NULL;
1610 const char *dname = NULL;
1611 char *talloced = NULL;
1612 const char *connect_path = lp_path(frame, snum);
1613 char *service_name = lp_servicename(frame, snum);
1614 const char *msdfs_proxy = lp_msdfs_proxy(frame, snum);
1615 struct conn_struct_tos *c = NULL;
1616 connection_struct *conn = NULL;
1617 struct referral *ref = NULL;
1618 struct smb_filename *smb_fname = NULL;
1619 NTSTATUS status;
1621 if (jn_remain == 0) {
1622 TALLOC_FREE(frame);
1623 return 0;
1626 if(*connect_path == '\0') {
1627 TALLOC_FREE(frame);
1628 return 0;
1632 * Fake up a connection struct for the VFS layer.
1635 status = create_conn_struct_tos_cwd(global_messaging_context(),
1636 snum,
1637 connect_path,
1638 NULL,
1639 &c);
1640 if (!NT_STATUS_IS_OK(status)) {
1641 DEBUG(3, ("create_conn_struct failed: %s\n",
1642 nt_errstr(status)));
1643 TALLOC_FREE(frame);
1644 return 0;
1646 conn = c->conn;
1648 /* form a junction for the msdfs root - convention
1649 DO NOT REMOVE THIS: NT clients will not work with us
1650 if this is not present
1652 jucn[cnt].service_name = talloc_strdup(ctx,service_name);
1653 jucn[cnt].volume_name = talloc_strdup(ctx, "");
1654 if (!jucn[cnt].service_name || !jucn[cnt].volume_name) {
1655 goto out;
1657 jucn[cnt].comment = "";
1658 jucn[cnt].referral_count = 1;
1660 ref = jucn[cnt].referral_list = talloc_zero(ctx, struct referral);
1661 if (jucn[cnt].referral_list == NULL) {
1662 goto out;
1665 ref->proximity = 0;
1666 ref->ttl = REFERRAL_TTL;
1667 if (*msdfs_proxy != '\0') {
1668 ref->alternate_path = talloc_strdup(ctx,
1669 msdfs_proxy);
1670 } else {
1671 ref->alternate_path = talloc_asprintf(ctx,
1672 "\\\\%s\\%s",
1673 get_local_machine_name(),
1674 service_name);
1677 if (!ref->alternate_path) {
1678 goto out;
1680 cnt++;
1682 /* Don't enumerate if we're an msdfs proxy. */
1683 if (*msdfs_proxy != '\0') {
1684 goto out;
1687 smb_fname = synthetic_smb_fname(frame,
1688 ".",
1689 NULL,
1690 NULL,
1692 if (smb_fname == NULL) {
1693 goto out;
1696 /* Now enumerate all dfs links */
1697 dirp = SMB_VFS_OPENDIR(conn, smb_fname, NULL, 0);
1698 if(!dirp) {
1699 goto out;
1702 while ((dname = vfs_readdirname(conn, dirp, NULL, &talloced))
1703 != NULL) {
1704 char *link_target = NULL;
1705 struct smb_filename *smb_dname = NULL;
1707 if (cnt >= jn_remain) {
1708 DEBUG(2, ("form_junctions: ran out of MSDFS "
1709 "junction slots"));
1710 TALLOC_FREE(talloced);
1711 goto out;
1713 smb_dname = synthetic_smb_fname(talloc_tos(),
1714 dname,
1715 NULL,
1716 NULL,
1718 if (smb_dname == NULL) {
1719 TALLOC_FREE(talloced);
1720 goto out;
1722 if (is_msdfs_link_internal(ctx,
1723 conn,
1724 smb_dname, &link_target)) {
1725 if (parse_msdfs_symlink(ctx, snum,
1726 link_target,
1727 &jucn[cnt].referral_list,
1728 &jucn[cnt].referral_count)) {
1730 jucn[cnt].service_name = talloc_strdup(ctx,
1731 service_name);
1732 jucn[cnt].volume_name = talloc_strdup(ctx,
1733 dname);
1734 if (!jucn[cnt].service_name ||
1735 !jucn[cnt].volume_name) {
1736 TALLOC_FREE(talloced);
1737 goto out;
1739 jucn[cnt].comment = "";
1740 cnt++;
1742 TALLOC_FREE(link_target);
1744 TALLOC_FREE(talloced);
1745 TALLOC_FREE(smb_dname);
1748 out:
1750 if (dirp) {
1751 SMB_VFS_CLOSEDIR(conn,dirp);
1754 TALLOC_FREE(frame);
1755 return cnt;
1758 struct junction_map *enum_msdfs_links(TALLOC_CTX *ctx, size_t *p_num_jn)
1760 struct junction_map *jn = NULL;
1761 int i=0;
1762 size_t jn_count = 0;
1763 int sharecount = 0;
1765 *p_num_jn = 0;
1766 if(!lp_host_msdfs()) {
1767 return NULL;
1770 /* Ensure all the usershares are loaded. */
1771 become_root();
1772 load_registry_shares();
1773 sharecount = load_usershare_shares(NULL, connections_snum_used);
1774 unbecome_root();
1776 for(i=0;i < sharecount;i++) {
1777 if(lp_msdfs_root(i)) {
1778 jn_count += count_dfs_links(ctx, i);
1781 if (jn_count == 0) {
1782 return NULL;
1784 jn = talloc_array(ctx, struct junction_map, jn_count);
1785 if (!jn) {
1786 return NULL;
1788 for(i=0; i < sharecount; i++) {
1789 if (*p_num_jn >= jn_count) {
1790 break;
1792 if(lp_msdfs_root(i)) {
1793 *p_num_jn += form_junctions(ctx, i,
1794 &jn[*p_num_jn],
1795 jn_count - *p_num_jn);
1798 return jn;
1801 /******************************************************************************
1802 Core function to resolve a dfs pathname possibly containing a wildcard. If
1803 ppath_contains_wcard != NULL, it will be set to true if a wildcard is
1804 detected during dfs resolution.
1805 ******************************************************************************/
1807 NTSTATUS resolve_dfspath_wcard(TALLOC_CTX *ctx,
1808 connection_struct *conn,
1809 const char *name_in,
1810 uint32_t ucf_flags,
1811 bool allow_broken_path,
1812 char **pp_name_out,
1813 bool *ppath_contains_wcard)
1815 bool path_contains_wcard = false;
1816 NTSTATUS status = NT_STATUS_OK;
1818 status = dfs_redirect(ctx,
1819 conn,
1820 name_in,
1821 ucf_flags,
1822 allow_broken_path,
1823 pp_name_out,
1824 &path_contains_wcard);
1826 if (NT_STATUS_IS_OK(status) &&
1827 ppath_contains_wcard != NULL &&
1828 path_contains_wcard) {
1829 *ppath_contains_wcard = path_contains_wcard;
1831 return status;