s4:libcli: split out smb_raw_negotiate_fill_transport()
[Samba.git] / source3 / smbd / msdfs.c
blobf0ec6b8489258b28f1dd2b3344e1cdbce1bf8e0e
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 "lib/param/loadparm.h"
32 #include "libcli/security/security.h"
33 #include "librpc/gen_ndr/ndr_dfsblobs.h"
34 #include "lib/tsocket/tsocket.h"
36 /**********************************************************************
37 Parse a DFS pathname of the form \hostname\service\reqpath
38 into the dfs_path structure.
39 If POSIX pathnames is true, the pathname may also be of the
40 form /hostname/service/reqpath.
41 We cope with either here.
43 Unfortunately, due to broken clients who might set the
44 SVAL(inbuf,smb_flg2) & FLAGS2_DFS_PATHNAMES bit and then
45 send a local path, we have to cope with that too....
47 If conn != NULL then ensure the provided service is
48 the one pointed to by the connection.
50 This version does everything using pointers within one copy of the
51 pathname string, talloced on the struct dfs_path pointer (which
52 must be talloced). This may be too clever to live....
53 JRA.
54 **********************************************************************/
56 static NTSTATUS parse_dfs_path(connection_struct *conn,
57 const char *pathname,
58 bool allow_wcards,
59 bool allow_broken_path,
60 struct dfs_path *pdp, /* MUST BE TALLOCED */
61 bool *ppath_contains_wcard)
63 char *pathname_local;
64 char *p,*temp;
65 char *servicename;
66 char *eos_ptr;
67 NTSTATUS status = NT_STATUS_OK;
68 char sepchar;
70 ZERO_STRUCTP(pdp);
73 * This is the only talloc we should need to do
74 * on the struct dfs_path. All the pointers inside
75 * it should point to offsets within this string.
78 pathname_local = talloc_strdup(pdp, pathname);
79 if (!pathname_local) {
80 return NT_STATUS_NO_MEMORY;
82 /* Get a pointer to the terminating '\0' */
83 eos_ptr = &pathname_local[strlen(pathname_local)];
84 p = temp = pathname_local;
87 * Non-broken DFS paths *must* start with the
88 * path separator. For Windows this is always '\\',
89 * for posix paths this is always '/'.
92 if (*pathname == '/') {
93 pdp->posix_path = true;
94 sepchar = '/';
95 } else {
96 pdp->posix_path = false;
97 sepchar = '\\';
100 if (allow_broken_path && (*pathname != sepchar)) {
101 DEBUG(10,("parse_dfs_path: path %s doesn't start with %c\n",
102 pathname, sepchar ));
104 * Possibly client sent a local path by mistake.
105 * Try and convert to a local path.
106 * Note that this is an SMB1-only fallback
107 * to cope with known broken SMB1 clients.
110 pdp->hostname = eos_ptr; /* "" */
111 pdp->servicename = eos_ptr; /* "" */
113 /* We've got no info about separators. */
114 pdp->posix_path = lp_posix_pathnames();
115 p = temp;
116 DEBUG(10,("parse_dfs_path: trying to convert %s to a "
117 "local path\n",
118 temp));
119 goto local_path;
123 * Safe to use on talloc'ed string as it only shrinks.
124 * It also doesn't affect the eos_ptr.
126 trim_char(temp,sepchar,sepchar);
128 DEBUG(10,("parse_dfs_path: temp = |%s| after trimming %c's\n",
129 temp, sepchar));
131 /* Now tokenize. */
132 /* Parse out hostname. */
133 p = strchr_m(temp,sepchar);
134 if(p == NULL) {
135 DEBUG(10,("parse_dfs_path: can't parse hostname from path %s\n",
136 temp));
138 * Possibly client sent a local path by mistake.
139 * Try and convert to a local path.
142 pdp->hostname = eos_ptr; /* "" */
143 pdp->servicename = eos_ptr; /* "" */
145 p = temp;
146 DEBUG(10,("parse_dfs_path: trying to convert %s "
147 "to a local path\n",
148 temp));
149 goto local_path;
151 *p = '\0';
152 pdp->hostname = temp;
154 DEBUG(10,("parse_dfs_path: hostname: %s\n",pdp->hostname));
156 /* Parse out servicename. */
157 servicename = p+1;
158 p = strchr_m(servicename,sepchar);
159 if (p) {
160 *p = '\0';
163 /* Is this really our servicename ? */
164 if (conn && !( strequal(servicename, lp_servicename(talloc_tos(), SNUM(conn)))
165 || (strequal(servicename, HOMES_NAME)
166 && strequal(lp_servicename(talloc_tos(), SNUM(conn)),
167 get_current_username()) )) ) {
168 DEBUG(10,("parse_dfs_path: %s is not our servicename\n",
169 servicename));
172 * Possibly client sent a local path by mistake.
173 * Try and convert to a local path.
176 pdp->hostname = eos_ptr; /* "" */
177 pdp->servicename = eos_ptr; /* "" */
179 /* Repair the path - replace the sepchar's
180 we nulled out */
181 servicename--;
182 *servicename = sepchar;
183 if (p) {
184 *p = sepchar;
187 p = temp;
188 DEBUG(10,("parse_dfs_path: trying to convert %s "
189 "to a local path\n",
190 temp));
191 goto local_path;
194 pdp->servicename = servicename;
196 DEBUG(10,("parse_dfs_path: servicename: %s\n",pdp->servicename));
198 if(p == NULL) {
199 /* Client sent self referral \server\share. */
200 pdp->reqpath = eos_ptr; /* "" */
201 return NT_STATUS_OK;
204 p++;
206 local_path:
208 *ppath_contains_wcard = False;
210 pdp->reqpath = p;
212 /* Rest is reqpath. */
213 if (pdp->posix_path) {
214 status = check_path_syntax_posix(pdp->reqpath);
215 } else {
216 if (allow_wcards) {
217 status = check_path_syntax_wcard(pdp->reqpath,
218 ppath_contains_wcard);
219 } else {
220 status = check_path_syntax(pdp->reqpath);
224 if (!NT_STATUS_IS_OK(status)) {
225 DEBUG(10,("parse_dfs_path: '%s' failed with %s\n",
226 p, nt_errstr(status) ));
227 return status;
230 DEBUG(10,("parse_dfs_path: rest of the path: %s\n",pdp->reqpath));
231 return NT_STATUS_OK;
234 /********************************************************
235 Fake up a connection struct for the VFS layer, for use in
236 applications (such as the python bindings), that do not want the
237 global working directory changed under them.
239 SMB_VFS_CONNECT requires root privileges.
240 *********************************************************/
242 static NTSTATUS create_conn_struct_as_root(TALLOC_CTX *ctx,
243 struct messaging_context *msg,
244 connection_struct **pconn,
245 int snum,
246 const char *path,
247 const struct auth_session_info *session_info)
249 connection_struct *conn;
250 char *connpath;
251 const char *vfs_user;
252 struct smbd_server_connection *sconn;
253 const char *servicename = lp_const_servicename(snum);
255 sconn = talloc_zero(ctx, struct smbd_server_connection);
256 if (sconn == NULL) {
257 return NT_STATUS_NO_MEMORY;
260 sconn->raw_ev_ctx = samba_tevent_context_init(sconn);
261 if (sconn->raw_ev_ctx == NULL) {
262 TALLOC_FREE(sconn);
263 return NT_STATUS_NO_MEMORY;
266 sconn->root_ev_ctx = sconn->raw_ev_ctx;
267 sconn->guest_ev_ctx = sconn->raw_ev_ctx;
268 sconn->msg_ctx = msg;
270 conn = conn_new(sconn);
271 if (conn == NULL) {
272 TALLOC_FREE(sconn);
273 return NT_STATUS_NO_MEMORY;
276 /* Now we have conn, we need to make sconn a child of conn,
277 * for a proper talloc tree */
278 talloc_steal(conn, sconn);
280 if (snum == -1 && servicename == NULL) {
281 servicename = "Unknown Service (snum == -1)";
284 connpath = talloc_strdup(conn, path);
285 if (!connpath) {
286 TALLOC_FREE(conn);
287 return NT_STATUS_NO_MEMORY;
289 connpath = talloc_string_sub(conn,
290 connpath,
291 "%S",
292 servicename);
293 if (!connpath) {
294 TALLOC_FREE(conn);
295 return NT_STATUS_NO_MEMORY;
298 /* needed for smbd_vfs_init() */
300 conn->params->service = snum;
301 conn->cnum = TID_FIELD_INVALID;
303 if (session_info != NULL) {
304 conn->session_info = copy_session_info(conn, session_info);
305 if (conn->session_info == NULL) {
306 DEBUG(0, ("copy_serverinfo failed\n"));
307 TALLOC_FREE(conn);
308 return NT_STATUS_NO_MEMORY;
310 /* unix_info could be NULL in session_info */
311 if (conn->session_info->unix_info != NULL) {
312 vfs_user = conn->session_info->unix_info->unix_name;
313 } else {
314 vfs_user = get_current_username();
316 } else {
317 /* use current authenticated user in absence of session_info */
318 vfs_user = get_current_username();
321 conn->user_ev_ctx = sconn->raw_ev_ctx;
323 set_conn_connectpath(conn, connpath);
326 * New code to check if there's a share security descriptor
327 * added from NT server manager. This is done after the
328 * smb.conf checks are done as we need a uid and token. JRA.
331 if (conn->session_info) {
332 share_access_check(conn->session_info->security_token,
333 servicename,
334 MAXIMUM_ALLOWED_ACCESS,
335 &conn->share_access);
337 if ((conn->share_access & FILE_WRITE_DATA) == 0) {
338 if ((conn->share_access & FILE_READ_DATA) == 0) {
339 /* No access, read or write. */
340 DEBUG(3,("create_conn_struct: connection to %s "
341 "denied due to security "
342 "descriptor.\n",
343 servicename));
344 conn_free(conn);
345 return NT_STATUS_ACCESS_DENIED;
346 } else {
347 conn->read_only = true;
350 } else {
351 conn->share_access = 0;
352 conn->read_only = true;
355 if (!smbd_vfs_init(conn)) {
356 NTSTATUS status = map_nt_error_from_unix(errno);
357 DEBUG(0,("create_conn_struct: smbd_vfs_init failed.\n"));
358 conn_free(conn);
359 return status;
362 /* this must be the first filesystem operation that we do */
363 if (SMB_VFS_CONNECT(conn, servicename, vfs_user) < 0) {
364 DEBUG(0,("VFS connect failed!\n"));
365 conn_free(conn);
366 return NT_STATUS_UNSUCCESSFUL;
369 talloc_free(conn->origpath);
370 conn->origpath = talloc_strdup(conn, conn->connectpath);
371 if (conn->origpath == NULL) {
372 conn_free(conn);
373 return NT_STATUS_NO_MEMORY;
376 conn->fs_capabilities = SMB_VFS_FS_CAPABILITIES(conn, &conn->ts_res);
377 conn->tcon_done = true;
378 *pconn = talloc_move(ctx, &conn);
380 return NT_STATUS_OK;
383 static int conn_struct_tos_destructor(struct conn_struct_tos *c)
385 if (c->oldcwd_fname != NULL) {
386 vfs_ChDir(c->conn, c->oldcwd_fname);
387 TALLOC_FREE(c->oldcwd_fname);
389 SMB_VFS_DISCONNECT(c->conn);
390 conn_free(c->conn);
391 return 0;
394 /********************************************************
395 Fake up a connection struct for the VFS layer, for use in
396 applications (such as the python bindings), that do not want the
397 global working directory changed under them.
399 SMB_VFS_CONNECT requires root privileges.
400 This temporary uses become_root() and unbecome_root().
402 But further impersonation has to be cone by the caller.
403 *********************************************************/
404 NTSTATUS create_conn_struct_tos(struct messaging_context *msg,
405 int snum,
406 const char *path,
407 const struct auth_session_info *session_info,
408 struct conn_struct_tos **_c)
410 struct conn_struct_tos *c = NULL;
411 NTSTATUS status;
413 *_c = NULL;
415 c = talloc_zero(talloc_tos(), struct conn_struct_tos);
416 if (c == NULL) {
417 return NT_STATUS_NO_MEMORY;
420 become_root();
421 status = create_conn_struct_as_root(c,
422 msg,
423 &c->conn,
424 snum,
425 path,
426 session_info);
427 unbecome_root();
428 if (!NT_STATUS_IS_OK(status)) {
429 TALLOC_FREE(c);
430 return status;
433 talloc_set_destructor(c, conn_struct_tos_destructor);
435 *_c = c;
436 return NT_STATUS_OK;
439 /********************************************************
440 Fake up a connection struct for the VFS layer.
441 Note: this performs a vfs connect and CHANGES CWD !!!! JRA.
443 See also the comment for create_conn_struct_tos() above!
445 The CWD change is reverted by the destructor of
446 conn_struct_tos when the current talloc_tos() is destroyed.
447 *********************************************************/
448 NTSTATUS create_conn_struct_tos_cwd(struct messaging_context *msg,
449 int snum,
450 const char *path,
451 const struct auth_session_info *session_info,
452 struct conn_struct_tos **_c)
454 struct conn_struct_tos *c = NULL;
455 struct smb_filename smb_fname_connectpath = {0};
456 NTSTATUS status;
458 *_c = NULL;
460 status = create_conn_struct_tos(msg,
461 snum,
462 path,
463 session_info,
464 &c);
465 if (!NT_STATUS_IS_OK(status)) {
466 return status;
470 * Windows seems to insist on doing trans2getdfsreferral() calls on
471 * the IPC$ share as the anonymous user. If we try to chdir as that
472 * user we will fail.... WTF ? JRA.
475 c->oldcwd_fname = vfs_GetWd(c, c->conn);
476 if (c->oldcwd_fname == NULL) {
477 status = map_nt_error_from_unix(errno);
478 DEBUG(3, ("vfs_GetWd failed: %s\n", strerror(errno)));
479 TALLOC_FREE(c);
480 return status;
483 smb_fname_connectpath = (struct smb_filename) {
484 .base_name = c->conn->connectpath
487 if (vfs_ChDir(c->conn, &smb_fname_connectpath) != 0) {
488 status = map_nt_error_from_unix(errno);
489 DBG_NOTICE("Can't ChDir to new conn path %s. "
490 "Error was %s\n",
491 c->conn->connectpath, strerror(errno));
492 TALLOC_FREE(c->oldcwd_fname);
493 TALLOC_FREE(c);
494 return status;
497 *_c = c;
498 return NT_STATUS_OK;
501 static void shuffle_strlist(char **list, int count)
503 int i;
504 uint32_t r;
505 char *tmp;
507 for (i = count; i > 1; i--) {
508 r = generate_random() % i;
510 tmp = list[i-1];
511 list[i-1] = list[r];
512 list[r] = tmp;
516 /**********************************************************************
517 Parse the contents of a symlink to verify if it is an msdfs referral
518 A valid referral is of the form:
520 msdfs:server1\share1,server2\share2
521 msdfs:server1\share1\pathname,server2\share2\pathname
522 msdfs:server1/share1,server2/share2
523 msdfs:server1/share1/pathname,server2/share2/pathname.
525 Note that the alternate paths returned here must be of the canonicalized
526 form:
528 \server\share or
529 \server\share\path\to\file,
531 even in posix path mode. This is because we have no knowledge if the
532 server we're referring to understands posix paths.
533 **********************************************************************/
535 static bool parse_msdfs_symlink(TALLOC_CTX *ctx,
536 int snum,
537 const char *target,
538 struct referral **preflist,
539 int *refcount)
541 char *temp = NULL;
542 char *prot;
543 char **alt_path = NULL;
544 int count = 0, i;
545 struct referral *reflist;
546 char *saveptr;
548 temp = talloc_strdup(ctx, target);
549 if (!temp) {
550 return False;
552 prot = strtok_r(temp, ":", &saveptr);
553 if (!prot) {
554 DEBUG(0,("parse_msdfs_symlink: invalid path !\n"));
555 return False;
558 alt_path = talloc_array(ctx, char *, MAX_REFERRAL_COUNT);
559 if (!alt_path) {
560 return False;
563 /* parse out the alternate paths */
564 while((count<MAX_REFERRAL_COUNT) &&
565 ((alt_path[count] = strtok_r(NULL, ",", &saveptr)) != NULL)) {
566 count++;
569 /* shuffle alternate paths */
570 if (lp_msdfs_shuffle_referrals(snum)) {
571 shuffle_strlist(alt_path, count);
574 DEBUG(10,("parse_msdfs_symlink: count=%d\n", count));
576 if (count) {
577 reflist = *preflist = talloc_zero_array(ctx,
578 struct referral, count);
579 if(reflist == NULL) {
580 TALLOC_FREE(alt_path);
581 return False;
583 } else {
584 reflist = *preflist = NULL;
587 for(i=0;i<count;i++) {
588 char *p;
590 /* Canonicalize link target.
591 * Replace all /'s in the path by a \ */
592 string_replace(alt_path[i], '/', '\\');
594 /* Remove leading '\\'s */
595 p = alt_path[i];
596 while (*p && (*p == '\\')) {
597 p++;
600 reflist[i].alternate_path = talloc_asprintf(ctx,
601 "\\%s",
603 if (!reflist[i].alternate_path) {
604 return False;
607 reflist[i].proximity = 0;
608 reflist[i].ttl = REFERRAL_TTL;
609 DEBUG(10, ("parse_msdfs_symlink: Created alt path: %s\n",
610 reflist[i].alternate_path));
613 *refcount = count;
615 TALLOC_FREE(alt_path);
616 return True;
619 /**********************************************************************
620 Returns true if the unix path is a valid msdfs symlink and also
621 returns the target string from inside the link.
622 **********************************************************************/
624 static bool is_msdfs_link_internal(TALLOC_CTX *ctx,
625 connection_struct *conn,
626 struct smb_filename *smb_fname,
627 char **pp_link_target)
629 int referral_len = 0;
630 #if defined(HAVE_BROKEN_READLINK)
631 char link_target_buf[PATH_MAX];
632 #else
633 char link_target_buf[7];
634 #endif
635 size_t bufsize = 0;
636 char *link_target = NULL;
638 if (pp_link_target) {
639 bufsize = 1024;
640 link_target = talloc_array(ctx, char, bufsize);
641 if (!link_target) {
642 return False;
644 *pp_link_target = link_target;
645 } else {
646 bufsize = sizeof(link_target_buf);
647 link_target = link_target_buf;
650 if (SMB_VFS_LSTAT(conn, smb_fname) != 0) {
651 DEBUG(5,("is_msdfs_link_read_target: %s does not exist.\n",
652 smb_fname->base_name));
653 goto err;
655 if (!S_ISLNK(smb_fname->st.st_ex_mode)) {
656 DEBUG(5,("is_msdfs_link_read_target: %s is not a link.\n",
657 smb_fname->base_name));
658 goto err;
661 referral_len = SMB_VFS_READLINK(conn, smb_fname,
662 link_target, bufsize - 1);
663 if (referral_len == -1) {
664 DEBUG(0,("is_msdfs_link_read_target: Error reading "
665 "msdfs link %s: %s\n",
666 smb_fname->base_name, strerror(errno)));
667 goto err;
669 link_target[referral_len] = '\0';
671 DEBUG(5,("is_msdfs_link_internal: %s -> %s\n", smb_fname->base_name,
672 link_target));
674 if (!strnequal(link_target, "msdfs:", 6)) {
675 goto err;
677 return True;
679 err:
681 if (link_target != link_target_buf) {
682 TALLOC_FREE(link_target);
684 return False;
687 /**********************************************************************
688 Returns true if the unix path is a valid msdfs symlink.
689 **********************************************************************/
691 bool is_msdfs_link(connection_struct *conn,
692 struct smb_filename *smb_fname)
694 return is_msdfs_link_internal(talloc_tos(),
695 conn,
696 smb_fname,
697 NULL);
700 /*****************************************************************
701 Used by other functions to decide if a dfs path is remote,
702 and to get the list of referred locations for that remote path.
704 search_flag: For findfirsts, dfs links themselves are not
705 redirected, but paths beyond the links are. For normal smb calls,
706 even dfs links need to be redirected.
708 consumedcntp: how much of the dfs path is being redirected. the client
709 should try the remaining path on the redirected server.
711 If this returns NT_STATUS_PATH_NOT_COVERED the contents of the msdfs
712 link redirect are in targetpath.
713 *****************************************************************/
715 static NTSTATUS dfs_path_lookup(TALLOC_CTX *ctx,
716 connection_struct *conn,
717 const char *dfspath, /* Incoming complete dfs path */
718 const struct dfs_path *pdp, /* Parsed out
719 server+share+extrapath. */
720 uint32_t ucf_flags,
721 int *consumedcntp,
722 char **pp_targetpath)
724 char *p = NULL;
725 char *q = NULL;
726 NTSTATUS status;
727 struct smb_filename *smb_fname = NULL;
728 char *canon_dfspath = NULL; /* Canonicalized dfs path. (only '/'
729 components). */
731 DEBUG(10,("dfs_path_lookup: Conn path = %s reqpath = %s\n",
732 conn->connectpath, pdp->reqpath));
735 * Note the unix path conversion here we're doing we
736 * throw away. We're looking for a symlink for a dfs
737 * resolution, if we don't find it we'll do another
738 * unix_convert later in the codepath.
741 status = unix_convert(ctx, conn, pdp->reqpath, &smb_fname,
742 ucf_flags);
744 if (!NT_STATUS_IS_OK(status)) {
745 if (!NT_STATUS_EQUAL(status,
746 NT_STATUS_OBJECT_PATH_NOT_FOUND)) {
747 return status;
749 if (smb_fname == NULL || smb_fname->base_name == NULL) {
750 return status;
754 /* Optimization - check if we can redirect the whole path. */
756 if (is_msdfs_link_internal(ctx, conn, smb_fname, pp_targetpath)) {
757 /* XX_ALLOW_WCARD_XXX is called from search functions. */
758 if (ucf_flags &
759 (UCF_COND_ALLOW_WCARD_LCOMP|
760 UCF_ALWAYS_ALLOW_WCARD_LCOMP)) {
761 DEBUG(6,("dfs_path_lookup (FindFirst) No redirection "
762 "for dfs link %s.\n", dfspath));
763 status = NT_STATUS_OK;
764 goto out;
767 DEBUG(6,("dfs_path_lookup: %s resolves to a "
768 "valid dfs link %s.\n", dfspath,
769 pp_targetpath ? *pp_targetpath : ""));
771 if (consumedcntp) {
772 *consumedcntp = strlen(dfspath);
774 status = NT_STATUS_PATH_NOT_COVERED;
775 goto out;
778 /* Prepare to test only for '/' components in the given path,
779 * so if a Windows path replace all '\\' characters with '/'.
780 * For a POSIX DFS path we know all separators are already '/'. */
782 canon_dfspath = talloc_strdup(ctx, dfspath);
783 if (!canon_dfspath) {
784 status = NT_STATUS_NO_MEMORY;
785 goto out;
787 if (!pdp->posix_path) {
788 string_replace(canon_dfspath, '\\', '/');
792 * localpath comes out of unix_convert, so it has
793 * no trailing backslash. Make sure that canon_dfspath hasn't either.
794 * Fix for bug #4860 from Jan Martin <Jan.Martin@rwedea.com>.
797 trim_char(canon_dfspath,0,'/');
800 * Redirect if any component in the path is a link.
801 * We do this by walking backwards through the
802 * local path, chopping off the last component
803 * in both the local path and the canonicalized
804 * DFS path. If we hit a DFS link then we're done.
807 p = strrchr_m(smb_fname->base_name, '/');
808 if (consumedcntp) {
809 q = strrchr_m(canon_dfspath, '/');
812 while (p) {
813 *p = '\0';
814 if (q) {
815 *q = '\0';
818 if (is_msdfs_link_internal(ctx, conn,
819 smb_fname, pp_targetpath)) {
820 DEBUG(4, ("dfs_path_lookup: Redirecting %s because "
821 "parent %s is dfs link\n", dfspath,
822 smb_fname_str_dbg(smb_fname)));
824 if (consumedcntp) {
825 *consumedcntp = strlen(canon_dfspath);
826 DEBUG(10, ("dfs_path_lookup: Path consumed: %s "
827 "(%d)\n",
828 canon_dfspath,
829 *consumedcntp));
832 status = NT_STATUS_PATH_NOT_COVERED;
833 goto out;
836 /* Step back on the filesystem. */
837 p = strrchr_m(smb_fname->base_name, '/');
839 if (consumedcntp) {
840 /* And in the canonicalized dfs path. */
841 q = strrchr_m(canon_dfspath, '/');
845 status = NT_STATUS_OK;
846 out:
847 TALLOC_FREE(smb_fname);
848 return status;
851 /*****************************************************************
852 Decides if a dfs pathname should be redirected or not.
853 If not, the pathname is converted to a tcon-relative local unix path
855 search_wcard_flag: this flag performs 2 functions both related
856 to searches. See resolve_dfs_path() and parse_dfs_path_XX()
857 for details.
859 This function can return NT_STATUS_OK, meaning use the returned path as-is
860 (mapped into a local path).
861 or NT_STATUS_NOT_COVERED meaning return a DFS redirect, or
862 any other NT_STATUS error which is a genuine error to be
863 returned to the client.
864 *****************************************************************/
866 static NTSTATUS dfs_redirect(TALLOC_CTX *ctx,
867 connection_struct *conn,
868 const char *path_in,
869 uint32_t ucf_flags,
870 bool allow_broken_path,
871 char **pp_path_out,
872 bool *ppath_contains_wcard)
874 NTSTATUS status;
875 bool search_wcard_flag = (ucf_flags &
876 (UCF_COND_ALLOW_WCARD_LCOMP|UCF_ALWAYS_ALLOW_WCARD_LCOMP));
877 struct dfs_path *pdp = talloc(ctx, struct dfs_path);
879 if (!pdp) {
880 return NT_STATUS_NO_MEMORY;
883 status = parse_dfs_path(conn, path_in, search_wcard_flag,
884 allow_broken_path, pdp,
885 ppath_contains_wcard);
886 if (!NT_STATUS_IS_OK(status)) {
887 TALLOC_FREE(pdp);
888 return status;
891 if (pdp->reqpath[0] == '\0') {
892 TALLOC_FREE(pdp);
893 *pp_path_out = talloc_strdup(ctx, "");
894 if (!*pp_path_out) {
895 return NT_STATUS_NO_MEMORY;
897 DEBUG(5,("dfs_redirect: self-referral.\n"));
898 return NT_STATUS_OK;
901 /* If dfs pathname for a non-dfs share, convert to tcon-relative
902 path and return OK */
904 if (!lp_msdfs_root(SNUM(conn))) {
905 *pp_path_out = talloc_strdup(ctx, pdp->reqpath);
906 TALLOC_FREE(pdp);
907 if (!*pp_path_out) {
908 return NT_STATUS_NO_MEMORY;
910 return NT_STATUS_OK;
913 /* If it looked like a local path (zero hostname/servicename)
914 * just treat as a tcon-relative path. */
916 if (pdp->hostname[0] == '\0' && pdp->servicename[0] == '\0') {
917 *pp_path_out = talloc_strdup(ctx, pdp->reqpath);
918 TALLOC_FREE(pdp);
919 if (!*pp_path_out) {
920 return NT_STATUS_NO_MEMORY;
922 return NT_STATUS_OK;
925 if (!( strequal(pdp->servicename, lp_servicename(talloc_tos(), SNUM(conn)))
926 || (strequal(pdp->servicename, HOMES_NAME)
927 && strequal(lp_servicename(talloc_tos(), SNUM(conn)),
928 conn->session_info->unix_info->sanitized_username) )) ) {
930 /* The given sharename doesn't match this connection. */
931 TALLOC_FREE(pdp);
933 return NT_STATUS_OBJECT_PATH_NOT_FOUND;
936 status = dfs_path_lookup(ctx, conn, path_in, pdp,
937 ucf_flags, NULL, NULL);
938 if (!NT_STATUS_IS_OK(status)) {
939 if (NT_STATUS_EQUAL(status, NT_STATUS_PATH_NOT_COVERED)) {
940 DEBUG(3,("dfs_redirect: Redirecting %s\n", path_in));
941 } else {
942 DEBUG(10,("dfs_redirect: dfs_path_lookup "
943 "failed for %s with %s\n",
944 path_in, nt_errstr(status) ));
946 return status;
949 DEBUG(3,("dfs_redirect: Not redirecting %s.\n", path_in));
951 /* Form non-dfs tcon-relative path */
952 *pp_path_out = talloc_strdup(ctx, pdp->reqpath);
953 TALLOC_FREE(pdp);
954 if (!*pp_path_out) {
955 return NT_STATUS_NO_MEMORY;
958 DEBUG(3,("dfs_redirect: Path %s converted to non-dfs path %s\n",
959 path_in,
960 *pp_path_out));
962 return NT_STATUS_OK;
965 /**********************************************************************
966 Return a self referral.
967 **********************************************************************/
969 static NTSTATUS self_ref(TALLOC_CTX *ctx,
970 const char *dfs_path,
971 struct junction_map *jucn,
972 int *consumedcntp,
973 bool *self_referralp)
975 struct referral *ref;
977 *self_referralp = True;
979 jucn->referral_count = 1;
980 if((ref = talloc_zero(ctx, struct referral)) == NULL) {
981 return NT_STATUS_NO_MEMORY;
984 ref->alternate_path = talloc_strdup(ctx, dfs_path);
985 if (!ref->alternate_path) {
986 TALLOC_FREE(ref);
987 return NT_STATUS_NO_MEMORY;
989 ref->proximity = 0;
990 ref->ttl = REFERRAL_TTL;
991 jucn->referral_list = ref;
992 *consumedcntp = strlen(dfs_path);
993 return NT_STATUS_OK;
996 /**********************************************************************
997 Gets valid referrals for a dfs path and fills up the
998 junction_map structure.
999 **********************************************************************/
1001 NTSTATUS get_referred_path(TALLOC_CTX *ctx,
1002 const char *dfs_path,
1003 const struct tsocket_address *remote_address,
1004 const struct tsocket_address *local_address,
1005 bool allow_broken_path,
1006 struct junction_map *jucn,
1007 int *consumedcntp,
1008 bool *self_referralp)
1010 TALLOC_CTX *frame = talloc_stackframe();
1011 struct conn_struct_tos *c = NULL;
1012 struct connection_struct *conn = NULL;
1013 char *targetpath = NULL;
1014 int snum;
1015 NTSTATUS status = NT_STATUS_NOT_FOUND;
1016 bool dummy;
1017 struct dfs_path *pdp = talloc_zero(frame, struct dfs_path);
1019 if (!pdp) {
1020 TALLOC_FREE(frame);
1021 return NT_STATUS_NO_MEMORY;
1024 *self_referralp = False;
1026 status = parse_dfs_path(NULL, dfs_path, False, allow_broken_path,
1027 pdp, &dummy);
1028 if (!NT_STATUS_IS_OK(status)) {
1029 TALLOC_FREE(frame);
1030 return status;
1033 jucn->service_name = talloc_strdup(ctx, pdp->servicename);
1034 jucn->volume_name = talloc_strdup(ctx, pdp->reqpath);
1035 if (!jucn->service_name || !jucn->volume_name) {
1036 TALLOC_FREE(frame);
1037 return NT_STATUS_NO_MEMORY;
1040 /* Verify the share is a dfs root */
1041 snum = lp_servicenumber(jucn->service_name);
1042 if(snum < 0) {
1043 char *service_name = NULL;
1044 if ((snum = find_service(ctx, jucn->service_name, &service_name)) < 0) {
1045 TALLOC_FREE(frame);
1046 return NT_STATUS_NOT_FOUND;
1048 if (!service_name) {
1049 TALLOC_FREE(frame);
1050 return NT_STATUS_NO_MEMORY;
1052 TALLOC_FREE(jucn->service_name);
1053 jucn->service_name = talloc_strdup(ctx, service_name);
1054 if (!jucn->service_name) {
1055 TALLOC_FREE(frame);
1056 return NT_STATUS_NO_MEMORY;
1060 if (!lp_msdfs_root(snum) && (*lp_msdfs_proxy(talloc_tos(), snum) == '\0')) {
1061 DEBUG(3,("get_referred_path: |%s| in dfs path %s is not "
1062 "a dfs root.\n",
1063 pdp->servicename, dfs_path));
1064 TALLOC_FREE(frame);
1065 return NT_STATUS_NOT_FOUND;
1069 * Self referrals are tested with a anonymous IPC connection and
1070 * a GET_DFS_REFERRAL call to \\server\share. (which means
1071 * dp.reqpath[0] points to an empty string). create_conn_struct cd's
1072 * into the directory and will fail if it cannot (as the anonymous
1073 * user). Cope with this.
1076 if (pdp->reqpath[0] == '\0') {
1077 char *tmp;
1078 struct referral *ref;
1079 int refcount;
1081 if (*lp_msdfs_proxy(talloc_tos(), snum) == '\0') {
1082 TALLOC_FREE(frame);
1083 return self_ref(ctx,
1084 dfs_path,
1085 jucn,
1086 consumedcntp,
1087 self_referralp);
1091 * It's an msdfs proxy share. Redirect to
1092 * the configured target share.
1095 tmp = talloc_asprintf(frame, "msdfs:%s",
1096 lp_msdfs_proxy(frame, snum));
1097 if (tmp == NULL) {
1098 TALLOC_FREE(frame);
1099 return NT_STATUS_NO_MEMORY;
1102 if (!parse_msdfs_symlink(ctx, snum, tmp, &ref, &refcount)) {
1103 TALLOC_FREE(frame);
1104 return NT_STATUS_INVALID_PARAMETER;
1106 jucn->referral_count = refcount;
1107 jucn->referral_list = ref;
1108 *consumedcntp = strlen(dfs_path);
1109 TALLOC_FREE(frame);
1110 return NT_STATUS_OK;
1113 status = create_conn_struct_tos_cwd(server_messaging_context(),
1114 snum,
1115 lp_path(frame, snum),
1116 NULL,
1117 &c);
1118 if (!NT_STATUS_IS_OK(status)) {
1119 TALLOC_FREE(frame);
1120 return status;
1122 conn = c->conn;
1125 * TODO
1127 * The remote and local address should be passed down to
1128 * create_conn_struct_cwd.
1130 if (conn->sconn->remote_address == NULL) {
1131 conn->sconn->remote_address =
1132 tsocket_address_copy(remote_address, conn->sconn);
1133 if (conn->sconn->remote_address == NULL) {
1134 TALLOC_FREE(frame);
1135 return NT_STATUS_NO_MEMORY;
1138 if (conn->sconn->local_address == NULL) {
1139 conn->sconn->local_address =
1140 tsocket_address_copy(local_address, conn->sconn);
1141 if (conn->sconn->local_address == NULL) {
1142 TALLOC_FREE(frame);
1143 return NT_STATUS_NO_MEMORY;
1147 /* If this is a DFS path dfs_lookup should return
1148 * NT_STATUS_PATH_NOT_COVERED. */
1150 status = dfs_path_lookup(ctx, conn, dfs_path, pdp,
1151 0, consumedcntp, &targetpath);
1153 if (!NT_STATUS_EQUAL(status, NT_STATUS_PATH_NOT_COVERED)) {
1154 DEBUG(3,("get_referred_path: No valid referrals for path %s\n",
1155 dfs_path));
1156 if (NT_STATUS_IS_OK(status)) {
1158 * We are in an error path here (we
1159 * know it's not a DFS path), but
1160 * dfs_path_lookup() can return
1161 * NT_STATUS_OK. Ensure we always
1162 * return a valid error code.
1164 * #9588 - ACLs are not inherited to directories
1165 * for DFS shares.
1167 status = NT_STATUS_NOT_FOUND;
1169 goto err_exit;
1172 /* We know this is a valid dfs link. Parse the targetpath. */
1173 if (!parse_msdfs_symlink(ctx, snum, targetpath,
1174 &jucn->referral_list,
1175 &jucn->referral_count)) {
1176 DEBUG(3,("get_referred_path: failed to parse symlink "
1177 "target %s\n", targetpath ));
1178 status = NT_STATUS_NOT_FOUND;
1179 goto err_exit;
1182 status = NT_STATUS_OK;
1183 err_exit:
1184 TALLOC_FREE(frame);
1185 return status;
1188 /******************************************************************
1189 Set up the DFS referral for the dfs pathname. This call returns
1190 the amount of the path covered by this server, and where the
1191 client should be redirected to. This is the meat of the
1192 TRANS2_GET_DFS_REFERRAL call.
1193 ******************************************************************/
1195 int setup_dfs_referral(connection_struct *orig_conn,
1196 const char *dfs_path,
1197 int max_referral_level,
1198 char **ppdata, NTSTATUS *pstatus)
1200 char *pdata = *ppdata;
1201 int reply_size = 0;
1202 struct dfs_GetDFSReferral *r;
1203 DATA_BLOB blob = data_blob_null;
1204 NTSTATUS status;
1205 enum ndr_err_code ndr_err;
1207 r = talloc_zero(talloc_tos(), struct dfs_GetDFSReferral);
1208 if (r == NULL) {
1209 *pstatus = NT_STATUS_NO_MEMORY;
1210 return -1;
1213 r->in.req.max_referral_level = max_referral_level;
1214 r->in.req.servername = talloc_strdup(r, dfs_path);
1215 if (r->in.req.servername == NULL) {
1216 talloc_free(r);
1217 *pstatus = NT_STATUS_NO_MEMORY;
1218 return -1;
1221 status = SMB_VFS_GET_DFS_REFERRALS(orig_conn, r);
1222 if (!NT_STATUS_IS_OK(status)) {
1223 talloc_free(r);
1224 *pstatus = status;
1225 return -1;
1228 ndr_err = ndr_push_struct_blob(&blob, r,
1229 r->out.resp,
1230 (ndr_push_flags_fn_t)ndr_push_dfs_referral_resp);
1231 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1232 TALLOC_FREE(r);
1233 *pstatus = NT_STATUS_INVALID_PARAMETER;
1234 return -1;
1237 pdata = (char *)SMB_REALLOC(pdata, blob.length);
1238 if(pdata == NULL) {
1239 TALLOC_FREE(r);
1240 DEBUG(0,("referral setup:"
1241 "malloc failed for Realloc!\n"));
1242 return -1;
1244 *ppdata = pdata;
1245 reply_size = blob.length;
1246 memcpy(pdata, blob.data, blob.length);
1247 TALLOC_FREE(r);
1249 *pstatus = NT_STATUS_OK;
1250 return reply_size;
1253 /**********************************************************************
1254 The following functions are called by the NETDFS RPC pipe functions
1255 **********************************************************************/
1257 /*********************************************************************
1258 Creates a junction structure from a DFS pathname
1259 **********************************************************************/
1261 bool create_junction(TALLOC_CTX *ctx,
1262 const char *dfs_path,
1263 bool allow_broken_path,
1264 struct junction_map *jucn)
1266 int snum;
1267 bool dummy;
1268 struct dfs_path *pdp = talloc(ctx,struct dfs_path);
1269 NTSTATUS status;
1271 if (!pdp) {
1272 return False;
1274 status = parse_dfs_path(NULL, dfs_path, False, allow_broken_path,
1275 pdp, &dummy);
1276 if (!NT_STATUS_IS_OK(status)) {
1277 return False;
1280 /* check if path is dfs : validate first token */
1281 if (!is_myname_or_ipaddr(pdp->hostname)) {
1282 DEBUG(4,("create_junction: Invalid hostname %s "
1283 "in dfs path %s\n",
1284 pdp->hostname, dfs_path));
1285 TALLOC_FREE(pdp);
1286 return False;
1289 /* Check for a non-DFS share */
1290 snum = lp_servicenumber(pdp->servicename);
1292 if(snum < 0 || !lp_msdfs_root(snum)) {
1293 DEBUG(4,("create_junction: %s is not an msdfs root.\n",
1294 pdp->servicename));
1295 TALLOC_FREE(pdp);
1296 return False;
1299 jucn->service_name = talloc_strdup(ctx, pdp->servicename);
1300 jucn->volume_name = talloc_strdup(ctx, pdp->reqpath);
1301 jucn->comment = lp_comment(ctx, snum);
1303 TALLOC_FREE(pdp);
1304 if (!jucn->service_name || !jucn->volume_name || ! jucn->comment) {
1305 return False;
1307 return True;
1310 /**********************************************************************
1311 Forms a valid Unix pathname from the junction
1312 **********************************************************************/
1314 static bool junction_to_local_path_tos(const struct junction_map *jucn,
1315 char **pp_path_out,
1316 connection_struct **conn_out)
1318 struct conn_struct_tos *c = NULL;
1319 int snum;
1320 char *path_out = NULL;
1321 NTSTATUS status;
1323 snum = lp_servicenumber(jucn->service_name);
1324 if(snum < 0) {
1325 return False;
1327 status = create_conn_struct_tos_cwd(server_messaging_context(),
1328 snum,
1329 lp_path(talloc_tos(), snum),
1330 NULL,
1331 &c);
1332 if (!NT_STATUS_IS_OK(status)) {
1333 return False;
1336 path_out = talloc_asprintf(c,
1337 "%s/%s",
1338 lp_path(talloc_tos(), snum),
1339 jucn->volume_name);
1340 if (path_out == NULL) {
1341 TALLOC_FREE(c);
1342 return False;
1344 *pp_path_out = path_out;
1345 *conn_out = c->conn;
1346 return True;
1349 bool create_msdfs_link(const struct junction_map *jucn)
1351 TALLOC_CTX *frame = talloc_stackframe();
1352 char *path = NULL;
1353 char *msdfs_link = NULL;
1354 connection_struct *conn;
1355 int i=0;
1356 bool insert_comma = False;
1357 bool ret = False;
1358 struct smb_filename *smb_fname = NULL;
1359 bool ok;
1361 ok = junction_to_local_path_tos(jucn, &path, &conn);
1362 if (!ok) {
1363 TALLOC_FREE(frame);
1364 return False;
1367 /* Form the msdfs_link contents */
1368 msdfs_link = talloc_strdup(conn, "msdfs:");
1369 if (!msdfs_link) {
1370 goto out;
1372 for(i=0; i<jucn->referral_count; i++) {
1373 char *refpath = jucn->referral_list[i].alternate_path;
1375 /* Alternate paths always use Windows separators. */
1376 trim_char(refpath, '\\', '\\');
1377 if(*refpath == '\0') {
1378 if (i == 0) {
1379 insert_comma = False;
1381 continue;
1383 if (i > 0 && insert_comma) {
1384 msdfs_link = talloc_asprintf_append_buffer(msdfs_link,
1385 ",%s",
1386 refpath);
1387 } else {
1388 msdfs_link = talloc_asprintf_append_buffer(msdfs_link,
1389 "%s",
1390 refpath);
1393 if (!msdfs_link) {
1394 goto out;
1396 if (!insert_comma) {
1397 insert_comma = True;
1401 DEBUG(5,("create_msdfs_link: Creating new msdfs link: %s -> %s\n",
1402 path, msdfs_link));
1404 smb_fname = synthetic_smb_fname(frame,
1405 path,
1406 NULL,
1407 NULL,
1409 if (smb_fname == NULL) {
1410 errno = ENOMEM;
1411 goto out;
1414 if(SMB_VFS_SYMLINK(conn, msdfs_link, smb_fname) < 0) {
1415 if (errno == EEXIST) {
1416 if(SMB_VFS_UNLINK(conn, smb_fname)!=0) {
1417 TALLOC_FREE(smb_fname);
1418 goto out;
1421 if (SMB_VFS_SYMLINK(conn, msdfs_link, smb_fname) < 0) {
1422 DEBUG(1,("create_msdfs_link: symlink failed "
1423 "%s -> %s\nError: %s\n",
1424 path, msdfs_link, strerror(errno)));
1425 goto out;
1429 ret = True;
1431 out:
1432 TALLOC_FREE(frame);
1433 return ret;
1436 bool remove_msdfs_link(const struct junction_map *jucn)
1438 TALLOC_CTX *frame = talloc_stackframe();
1439 char *path = NULL;
1440 connection_struct *conn;
1441 bool ret = False;
1442 struct smb_filename *smb_fname;
1443 bool ok;
1445 ok = junction_to_local_path_tos(jucn, &path, &conn);
1446 if (!ok) {
1447 TALLOC_FREE(frame);
1448 return false;
1451 smb_fname = synthetic_smb_fname(frame,
1452 path,
1453 NULL,
1454 NULL,
1456 if (smb_fname == NULL) {
1457 TALLOC_FREE(frame);
1458 errno = ENOMEM;
1459 return false;
1462 if( SMB_VFS_UNLINK(conn, smb_fname) == 0 ) {
1463 ret = True;
1466 TALLOC_FREE(frame);
1467 return ret;
1470 /*********************************************************************
1471 Return the number of DFS links at the root of this share.
1472 *********************************************************************/
1474 static int count_dfs_links(TALLOC_CTX *ctx, int snum)
1476 TALLOC_CTX *frame = talloc_stackframe();
1477 size_t cnt = 0;
1478 DIR *dirp = NULL;
1479 const char *dname = NULL;
1480 char *talloced = NULL;
1481 const char *connect_path = lp_path(frame, snum);
1482 const char *msdfs_proxy = lp_msdfs_proxy(frame, snum);
1483 struct conn_struct_tos *c = NULL;
1484 connection_struct *conn = NULL;
1485 NTSTATUS status;
1486 struct smb_filename *smb_fname = NULL;
1488 if(*connect_path == '\0') {
1489 TALLOC_FREE(frame);
1490 return 0;
1494 * Fake up a connection struct for the VFS layer.
1497 status = create_conn_struct_tos_cwd(server_messaging_context(),
1498 snum,
1499 connect_path,
1500 NULL,
1501 &c);
1502 if (!NT_STATUS_IS_OK(status)) {
1503 DEBUG(3, ("create_conn_struct failed: %s\n",
1504 nt_errstr(status)));
1505 TALLOC_FREE(frame);
1506 return 0;
1508 conn = c->conn;
1510 /* Count a link for the msdfs root - convention */
1511 cnt = 1;
1513 /* No more links if this is an msdfs proxy. */
1514 if (*msdfs_proxy != '\0') {
1515 goto out;
1518 smb_fname = synthetic_smb_fname(frame,
1519 ".",
1520 NULL,
1521 NULL,
1523 if (smb_fname == NULL) {
1524 goto out;
1527 /* Now enumerate all dfs links */
1528 dirp = SMB_VFS_OPENDIR(conn, smb_fname, NULL, 0);
1529 if(!dirp) {
1530 goto out;
1533 while ((dname = vfs_readdirname(conn, dirp, NULL, &talloced))
1534 != NULL) {
1535 struct smb_filename *smb_dname =
1536 synthetic_smb_fname(frame,
1537 dname,
1538 NULL,
1539 NULL,
1541 if (smb_dname == NULL) {
1542 goto out;
1544 if (is_msdfs_link(conn, smb_dname)) {
1545 cnt++;
1547 TALLOC_FREE(talloced);
1548 TALLOC_FREE(smb_dname);
1551 SMB_VFS_CLOSEDIR(conn,dirp);
1553 out:
1554 TALLOC_FREE(frame);
1555 return cnt;
1558 /*********************************************************************
1559 *********************************************************************/
1561 static int form_junctions(TALLOC_CTX *ctx,
1562 int snum,
1563 struct junction_map *jucn,
1564 size_t jn_remain)
1566 TALLOC_CTX *frame = talloc_stackframe();
1567 size_t cnt = 0;
1568 DIR *dirp = NULL;
1569 const char *dname = NULL;
1570 char *talloced = NULL;
1571 const char *connect_path = lp_path(frame, snum);
1572 char *service_name = lp_servicename(frame, snum);
1573 const char *msdfs_proxy = lp_msdfs_proxy(frame, snum);
1574 struct conn_struct_tos *c = NULL;
1575 connection_struct *conn = NULL;
1576 struct referral *ref = NULL;
1577 struct smb_filename *smb_fname = NULL;
1578 NTSTATUS status;
1580 if (jn_remain == 0) {
1581 TALLOC_FREE(frame);
1582 return 0;
1585 if(*connect_path == '\0') {
1586 TALLOC_FREE(frame);
1587 return 0;
1591 * Fake up a connection struct for the VFS layer.
1594 status = create_conn_struct_tos_cwd(server_messaging_context(),
1595 snum,
1596 connect_path,
1597 NULL,
1598 &c);
1599 if (!NT_STATUS_IS_OK(status)) {
1600 DEBUG(3, ("create_conn_struct failed: %s\n",
1601 nt_errstr(status)));
1602 TALLOC_FREE(frame);
1603 return 0;
1605 conn = c->conn;
1607 /* form a junction for the msdfs root - convention
1608 DO NOT REMOVE THIS: NT clients will not work with us
1609 if this is not present
1611 jucn[cnt].service_name = talloc_strdup(ctx,service_name);
1612 jucn[cnt].volume_name = talloc_strdup(ctx, "");
1613 if (!jucn[cnt].service_name || !jucn[cnt].volume_name) {
1614 goto out;
1616 jucn[cnt].comment = "";
1617 jucn[cnt].referral_count = 1;
1619 ref = jucn[cnt].referral_list = talloc_zero(ctx, struct referral);
1620 if (jucn[cnt].referral_list == NULL) {
1621 goto out;
1624 ref->proximity = 0;
1625 ref->ttl = REFERRAL_TTL;
1626 if (*msdfs_proxy != '\0') {
1627 ref->alternate_path = talloc_strdup(ctx,
1628 msdfs_proxy);
1629 } else {
1630 ref->alternate_path = talloc_asprintf(ctx,
1631 "\\\\%s\\%s",
1632 get_local_machine_name(),
1633 service_name);
1636 if (!ref->alternate_path) {
1637 goto out;
1639 cnt++;
1641 /* Don't enumerate if we're an msdfs proxy. */
1642 if (*msdfs_proxy != '\0') {
1643 goto out;
1646 smb_fname = synthetic_smb_fname(frame,
1647 ".",
1648 NULL,
1649 NULL,
1651 if (smb_fname == NULL) {
1652 goto out;
1655 /* Now enumerate all dfs links */
1656 dirp = SMB_VFS_OPENDIR(conn, smb_fname, NULL, 0);
1657 if(!dirp) {
1658 goto out;
1661 while ((dname = vfs_readdirname(conn, dirp, NULL, &talloced))
1662 != NULL) {
1663 char *link_target = NULL;
1664 struct smb_filename *smb_dname = NULL;
1666 if (cnt >= jn_remain) {
1667 DEBUG(2, ("form_junctions: ran out of MSDFS "
1668 "junction slots"));
1669 TALLOC_FREE(talloced);
1670 goto out;
1672 smb_dname = synthetic_smb_fname(talloc_tos(),
1673 dname,
1674 NULL,
1675 NULL,
1677 if (smb_dname == NULL) {
1678 TALLOC_FREE(talloced);
1679 goto out;
1681 if (is_msdfs_link_internal(ctx,
1682 conn,
1683 smb_dname, &link_target)) {
1684 if (parse_msdfs_symlink(ctx, snum,
1685 link_target,
1686 &jucn[cnt].referral_list,
1687 &jucn[cnt].referral_count)) {
1689 jucn[cnt].service_name = talloc_strdup(ctx,
1690 service_name);
1691 jucn[cnt].volume_name = talloc_strdup(ctx,
1692 dname);
1693 if (!jucn[cnt].service_name ||
1694 !jucn[cnt].volume_name) {
1695 TALLOC_FREE(talloced);
1696 goto out;
1698 jucn[cnt].comment = "";
1699 cnt++;
1701 TALLOC_FREE(link_target);
1703 TALLOC_FREE(talloced);
1704 TALLOC_FREE(smb_dname);
1707 out:
1709 if (dirp) {
1710 SMB_VFS_CLOSEDIR(conn,dirp);
1713 TALLOC_FREE(frame);
1714 return cnt;
1717 struct junction_map *enum_msdfs_links(TALLOC_CTX *ctx, size_t *p_num_jn)
1719 struct junction_map *jn = NULL;
1720 int i=0;
1721 size_t jn_count = 0;
1722 int sharecount = 0;
1724 *p_num_jn = 0;
1725 if(!lp_host_msdfs()) {
1726 return NULL;
1729 /* Ensure all the usershares are loaded. */
1730 become_root();
1731 load_registry_shares();
1732 sharecount = load_usershare_shares(NULL, connections_snum_used);
1733 unbecome_root();
1735 for(i=0;i < sharecount;i++) {
1736 if(lp_msdfs_root(i)) {
1737 jn_count += count_dfs_links(ctx, i);
1740 if (jn_count == 0) {
1741 return NULL;
1743 jn = talloc_array(ctx, struct junction_map, jn_count);
1744 if (!jn) {
1745 return NULL;
1747 for(i=0; i < sharecount; i++) {
1748 if (*p_num_jn >= jn_count) {
1749 break;
1751 if(lp_msdfs_root(i)) {
1752 *p_num_jn += form_junctions(ctx, i,
1753 &jn[*p_num_jn],
1754 jn_count - *p_num_jn);
1757 return jn;
1760 /******************************************************************************
1761 Core function to resolve a dfs pathname possibly containing a wildcard. If
1762 ppath_contains_wcard != NULL, it will be set to true if a wildcard is
1763 detected during dfs resolution.
1764 ******************************************************************************/
1766 NTSTATUS resolve_dfspath_wcard(TALLOC_CTX *ctx,
1767 connection_struct *conn,
1768 const char *name_in,
1769 uint32_t ucf_flags,
1770 bool allow_broken_path,
1771 char **pp_name_out,
1772 bool *ppath_contains_wcard)
1774 bool path_contains_wcard = false;
1775 NTSTATUS status = NT_STATUS_OK;
1777 status = dfs_redirect(ctx,
1778 conn,
1779 name_in,
1780 ucf_flags,
1781 allow_broken_path,
1782 pp_name_out,
1783 &path_contains_wcard);
1785 if (NT_STATUS_IS_OK(status) &&
1786 ppath_contains_wcard != NULL &&
1787 path_contains_wcard) {
1788 *ppath_contains_wcard = path_contains_wcard;
1790 return status;