winreg: Use the ntstatus return code for client side errors
[Samba/gebeck_regimport.git] / source3 / smbd / msdfs.c
blob31c5a2d07a6a9c318e764b4568bc2d45822d6f8a
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
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 3 of the License, or
11 (at your option) any later version.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program. If not, see <http://www.gnu.org/licenses/>.
23 #define DBGC_CLASS DBGC_MSDFS
24 #include "includes.h"
25 #include "system/filesys.h"
26 #include "smbd/smbd.h"
27 #include "smbd/globals.h"
28 #include "msdfs.h"
29 #include "auth.h"
30 #include "lib/param/loadparm.h"
32 /**********************************************************************
33 Parse a DFS pathname of the form \hostname\service\reqpath
34 into the dfs_path structure.
35 If POSIX pathnames is true, the pathname may also be of the
36 form /hostname/service/reqpath.
37 We cope with either here.
39 Unfortunately, due to broken clients who might set the
40 SVAL(inbuf,smb_flg2) & FLAGS2_DFS_PATHNAMES bit and then
41 send a local path, we have to cope with that too....
43 If conn != NULL then ensure the provided service is
44 the one pointed to by the connection.
46 This version does everything using pointers within one copy of the
47 pathname string, talloced on the struct dfs_path pointer (which
48 must be talloced). This may be too clever to live....
49 JRA.
50 **********************************************************************/
52 static NTSTATUS parse_dfs_path(connection_struct *conn,
53 const char *pathname,
54 bool allow_wcards,
55 struct dfs_path *pdp, /* MUST BE TALLOCED */
56 bool *ppath_contains_wcard)
58 struct smbd_server_connection *sconn = smbd_server_conn;
59 char *pathname_local;
60 char *p,*temp;
61 char *servicename;
62 char *eos_ptr;
63 NTSTATUS status = NT_STATUS_OK;
64 char sepchar;
66 ZERO_STRUCTP(pdp);
69 * This is the only talloc we should need to do
70 * on the struct dfs_path. All the pointers inside
71 * it should point to offsets within this string.
74 pathname_local = talloc_strdup(pdp, pathname);
75 if (!pathname_local) {
76 return NT_STATUS_NO_MEMORY;
78 /* Get a pointer to the terminating '\0' */
79 eos_ptr = &pathname_local[strlen(pathname_local)];
80 p = temp = pathname_local;
82 pdp->posix_path = (lp_posix_pathnames() && *pathname == '/');
84 sepchar = pdp->posix_path ? '/' : '\\';
86 if (!sconn->using_smb2 && (*pathname != sepchar)) {
87 DEBUG(10,("parse_dfs_path: path %s doesn't start with %c\n",
88 pathname, sepchar ));
90 * Possibly client sent a local path by mistake.
91 * Try and convert to a local path.
94 pdp->hostname = eos_ptr; /* "" */
95 pdp->servicename = eos_ptr; /* "" */
97 /* We've got no info about separators. */
98 pdp->posix_path = lp_posix_pathnames();
99 p = temp;
100 DEBUG(10,("parse_dfs_path: trying to convert %s to a "
101 "local path\n",
102 temp));
103 goto local_path;
107 * Safe to use on talloc'ed string as it only shrinks.
108 * It also doesn't affect the eos_ptr.
110 trim_char(temp,sepchar,sepchar);
112 DEBUG(10,("parse_dfs_path: temp = |%s| after trimming %c's\n",
113 temp, sepchar));
115 /* Now tokenize. */
116 /* Parse out hostname. */
117 p = strchr_m(temp,sepchar);
118 if(p == NULL) {
119 DEBUG(10,("parse_dfs_path: can't parse hostname from path %s\n",
120 temp));
122 * Possibly client sent a local path by mistake.
123 * Try and convert to a local path.
126 pdp->hostname = eos_ptr; /* "" */
127 pdp->servicename = eos_ptr; /* "" */
129 p = temp;
130 DEBUG(10,("parse_dfs_path: trying to convert %s "
131 "to a local path\n",
132 temp));
133 goto local_path;
135 *p = '\0';
136 pdp->hostname = temp;
138 DEBUG(10,("parse_dfs_path: hostname: %s\n",pdp->hostname));
140 /* Parse out servicename. */
141 servicename = p+1;
142 p = strchr_m(servicename,sepchar);
143 if (p) {
144 *p = '\0';
147 /* Is this really our servicename ? */
148 if (conn && !( strequal(servicename, lp_servicename(SNUM(conn)))
149 || (strequal(servicename, HOMES_NAME)
150 && strequal(lp_servicename(SNUM(conn)),
151 get_current_username()) )) ) {
152 DEBUG(10,("parse_dfs_path: %s is not our servicename\n",
153 servicename));
156 * Possibly client sent a local path by mistake.
157 * Try and convert to a local path.
160 pdp->hostname = eos_ptr; /* "" */
161 pdp->servicename = eos_ptr; /* "" */
163 /* Repair the path - replace the sepchar's
164 we nulled out */
165 servicename--;
166 *servicename = sepchar;
167 if (p) {
168 *p = sepchar;
171 p = temp;
172 DEBUG(10,("parse_dfs_path: trying to convert %s "
173 "to a local path\n",
174 temp));
175 goto local_path;
178 pdp->servicename = servicename;
180 DEBUG(10,("parse_dfs_path: servicename: %s\n",pdp->servicename));
182 if(p == NULL) {
183 /* Client sent self referral \server\share. */
184 pdp->reqpath = eos_ptr; /* "" */
185 return NT_STATUS_OK;
188 p++;
190 local_path:
192 *ppath_contains_wcard = False;
194 pdp->reqpath = p;
196 /* Rest is reqpath. */
197 if (pdp->posix_path) {
198 status = check_path_syntax_posix(pdp->reqpath);
199 } else {
200 if (allow_wcards) {
201 status = check_path_syntax_wcard(pdp->reqpath,
202 ppath_contains_wcard);
203 } else {
204 status = check_path_syntax(pdp->reqpath);
208 if (!NT_STATUS_IS_OK(status)) {
209 DEBUG(10,("parse_dfs_path: '%s' failed with %s\n",
210 p, nt_errstr(status) ));
211 return status;
214 DEBUG(10,("parse_dfs_path: rest of the path: %s\n",pdp->reqpath));
215 return NT_STATUS_OK;
218 /********************************************************
219 Fake up a connection struct for the VFS layer.
220 Note: this performs a vfs connect and CHANGES CWD !!!! JRA.
221 *********************************************************/
223 NTSTATUS create_conn_struct(TALLOC_CTX *ctx,
224 connection_struct **pconn,
225 int snum,
226 const char *path,
227 const struct auth_serversupplied_info *session_info,
228 char **poldcwd)
230 connection_struct *conn;
231 char *connpath;
232 char *oldcwd;
233 const char *vfs_user;
235 conn = talloc_zero(ctx, connection_struct);
236 if (conn == NULL) {
237 return NT_STATUS_NO_MEMORY;
240 connpath = talloc_strdup(conn, path);
241 if (!connpath) {
242 TALLOC_FREE(conn);
243 return NT_STATUS_NO_MEMORY;
245 connpath = talloc_string_sub(conn,
246 connpath,
247 "%S",
248 lp_servicename(snum));
249 if (!connpath) {
250 TALLOC_FREE(conn);
251 return NT_STATUS_NO_MEMORY;
254 /* needed for smbd_vfs_init() */
256 if (!(conn->params = talloc_zero(conn, struct share_params))) {
257 DEBUG(0, ("TALLOC failed\n"));
258 TALLOC_FREE(conn);
259 return NT_STATUS_NO_MEMORY;
262 conn->params->service = snum;
264 conn->sconn = smbd_server_conn;
265 conn->sconn->num_tcons_open++;
267 if (session_info != NULL) {
268 conn->session_info = copy_serverinfo(conn, session_info);
269 if (conn->session_info == NULL) {
270 DEBUG(0, ("copy_serverinfo failed\n"));
271 TALLOC_FREE(conn);
272 return NT_STATUS_NO_MEMORY;
274 vfs_user = conn->session_info->unix_name;
275 } else {
276 /* use current authenticated user in absence of session_info */
277 vfs_user = get_current_username();
280 set_conn_connectpath(conn, connpath);
282 if (!smbd_vfs_init(conn)) {
283 NTSTATUS status = map_nt_error_from_unix(errno);
284 DEBUG(0,("create_conn_struct: smbd_vfs_init failed.\n"));
285 conn_free(conn);
286 return status;
289 /* this must be the first filesystem operation that we do */
290 if (SMB_VFS_CONNECT(conn, lp_servicename(snum), vfs_user) < 0) {
291 DEBUG(0,("VFS connect failed!\n"));
292 conn_free(conn);
293 return NT_STATUS_UNSUCCESSFUL;
296 conn->fs_capabilities = SMB_VFS_FS_CAPABILITIES(conn, &conn->ts_res);
299 * Windows seems to insist on doing trans2getdfsreferral() calls on
300 * the IPC$ share as the anonymous user. If we try to chdir as that
301 * user we will fail.... WTF ? JRA.
304 oldcwd = vfs_GetWd(ctx, conn);
305 if (oldcwd == NULL) {
306 NTSTATUS status = map_nt_error_from_unix(errno);
307 DEBUG(3, ("vfs_GetWd failed: %s\n", strerror(errno)));
308 conn_free(conn);
309 return status;
312 if (vfs_ChDir(conn,conn->connectpath) != 0) {
313 NTSTATUS status = map_nt_error_from_unix(errno);
314 DEBUG(3,("create_conn_struct: Can't ChDir to new conn path %s. "
315 "Error was %s\n",
316 conn->connectpath, strerror(errno) ));
317 conn_free(conn);
318 return status;
321 *pconn = conn;
322 *poldcwd = oldcwd;
324 return NT_STATUS_OK;
327 /**********************************************************************
328 Parse the contents of a symlink to verify if it is an msdfs referral
329 A valid referral is of the form:
331 msdfs:server1\share1,server2\share2
332 msdfs:server1\share1\pathname,server2\share2\pathname
333 msdfs:server1/share1,server2/share2
334 msdfs:server1/share1/pathname,server2/share2/pathname.
336 Note that the alternate paths returned here must be of the canonicalized
337 form:
339 \server\share or
340 \server\share\path\to\file,
342 even in posix path mode. This is because we have no knowledge if the
343 server we're referring to understands posix paths.
344 **********************************************************************/
346 static bool parse_msdfs_symlink(TALLOC_CTX *ctx,
347 const char *target,
348 struct referral **preflist,
349 int *refcount)
351 char *temp = NULL;
352 char *prot;
353 char **alt_path = NULL;
354 int count = 0, i;
355 struct referral *reflist;
356 char *saveptr;
358 temp = talloc_strdup(ctx, target);
359 if (!temp) {
360 return False;
362 prot = strtok_r(temp, ":", &saveptr);
363 if (!prot) {
364 DEBUG(0,("parse_msdfs_symlink: invalid path !\n"));
365 return False;
368 alt_path = talloc_array(ctx, char *, MAX_REFERRAL_COUNT);
369 if (!alt_path) {
370 return False;
373 /* parse out the alternate paths */
374 while((count<MAX_REFERRAL_COUNT) &&
375 ((alt_path[count] = strtok_r(NULL, ",", &saveptr)) != NULL)) {
376 count++;
379 DEBUG(10,("parse_msdfs_symlink: count=%d\n", count));
381 if (count) {
382 reflist = *preflist = talloc_zero_array(ctx,
383 struct referral, count);
384 if(reflist == NULL) {
385 TALLOC_FREE(alt_path);
386 return False;
388 } else {
389 reflist = *preflist = NULL;
392 for(i=0;i<count;i++) {
393 char *p;
395 /* Canonicalize link target.
396 * Replace all /'s in the path by a \ */
397 string_replace(alt_path[i], '/', '\\');
399 /* Remove leading '\\'s */
400 p = alt_path[i];
401 while (*p && (*p == '\\')) {
402 p++;
405 reflist[i].alternate_path = talloc_asprintf(ctx,
406 "\\%s",
408 if (!reflist[i].alternate_path) {
409 return False;
412 reflist[i].proximity = 0;
413 reflist[i].ttl = REFERRAL_TTL;
414 DEBUG(10, ("parse_msdfs_symlink: Created alt path: %s\n",
415 reflist[i].alternate_path));
418 *refcount = count;
420 TALLOC_FREE(alt_path);
421 return True;
424 /**********************************************************************
425 Returns true if the unix path is a valid msdfs symlink and also
426 returns the target string from inside the link.
427 **********************************************************************/
429 static bool is_msdfs_link_internal(TALLOC_CTX *ctx,
430 connection_struct *conn,
431 const char *path,
432 char **pp_link_target,
433 SMB_STRUCT_STAT *sbufp)
435 int referral_len = 0;
436 #if defined(HAVE_BROKEN_READLINK)
437 char link_target_buf[PATH_MAX];
438 #else
439 char link_target_buf[7];
440 #endif
441 size_t bufsize = 0;
442 char *link_target = NULL;
443 struct smb_filename smb_fname;
445 if (pp_link_target) {
446 bufsize = 1024;
447 link_target = talloc_array(ctx, char, bufsize);
448 if (!link_target) {
449 return False;
451 *pp_link_target = link_target;
452 } else {
453 bufsize = sizeof(link_target_buf);
454 link_target = link_target_buf;
457 ZERO_STRUCT(smb_fname);
458 smb_fname.base_name = discard_const_p(char, path);
460 if (SMB_VFS_LSTAT(conn, &smb_fname) != 0) {
461 DEBUG(5,("is_msdfs_link_read_target: %s does not exist.\n",
462 path));
463 goto err;
465 if (!S_ISLNK(smb_fname.st.st_ex_mode)) {
466 DEBUG(5,("is_msdfs_link_read_target: %s is not a link.\n",
467 path));
468 goto err;
470 if (sbufp != NULL) {
471 *sbufp = smb_fname.st;
474 referral_len = SMB_VFS_READLINK(conn, path, link_target, bufsize - 1);
475 if (referral_len == -1) {
476 DEBUG(0,("is_msdfs_link_read_target: Error reading "
477 "msdfs link %s: %s\n",
478 path, strerror(errno)));
479 goto err;
481 link_target[referral_len] = '\0';
483 DEBUG(5,("is_msdfs_link_internal: %s -> %s\n",path,
484 link_target));
486 if (!strnequal(link_target, "msdfs:", 6)) {
487 goto err;
489 return True;
491 err:
493 if (link_target != link_target_buf) {
494 TALLOC_FREE(link_target);
496 return False;
499 /**********************************************************************
500 Returns true if the unix path is a valid msdfs symlink.
501 **********************************************************************/
503 bool is_msdfs_link(connection_struct *conn,
504 const char *path,
505 SMB_STRUCT_STAT *sbufp)
507 return is_msdfs_link_internal(talloc_tos(),
508 conn,
509 path,
510 NULL,
511 sbufp);
514 /*****************************************************************
515 Used by other functions to decide if a dfs path is remote,
516 and to get the list of referred locations for that remote path.
518 search_flag: For findfirsts, dfs links themselves are not
519 redirected, but paths beyond the links are. For normal smb calls,
520 even dfs links need to be redirected.
522 consumedcntp: how much of the dfs path is being redirected. the client
523 should try the remaining path on the redirected server.
525 If this returns NT_STATUS_PATH_NOT_COVERED the contents of the msdfs
526 link redirect are in targetpath.
527 *****************************************************************/
529 static NTSTATUS dfs_path_lookup(TALLOC_CTX *ctx,
530 connection_struct *conn,
531 const char *dfspath, /* Incoming complete dfs path */
532 const struct dfs_path *pdp, /* Parsed out
533 server+share+extrapath. */
534 bool search_flag, /* Called from a findfirst ? */
535 int *consumedcntp,
536 char **pp_targetpath)
538 char *p = NULL;
539 char *q = NULL;
540 NTSTATUS status;
541 struct smb_filename *smb_fname = NULL;
542 char *canon_dfspath = NULL; /* Canonicalized dfs path. (only '/'
543 components). */
545 DEBUG(10,("dfs_path_lookup: Conn path = %s reqpath = %s\n",
546 conn->connectpath, pdp->reqpath));
549 * Note the unix path conversion here we're doing we can
550 * throw away. We're looking for a symlink for a dfs
551 * resolution, if we don't find it we'll do another
552 * unix_convert later in the codepath.
553 * If we needed to remember what we'd resolved in
554 * dp->reqpath (as the original code did) we'd
555 * copy (localhost, dp->reqpath) on any code
556 * path below that returns True - but I don't
557 * think this is needed. JRA.
560 status = unix_convert(ctx, conn, pdp->reqpath, &smb_fname,
561 search_flag ? UCF_ALWAYS_ALLOW_WCARD_LCOMP : 0);
563 if (!NT_STATUS_IS_OK(status)) {
564 if (!NT_STATUS_EQUAL(status,
565 NT_STATUS_OBJECT_PATH_NOT_FOUND)) {
566 return status;
569 /* Create an smb_fname to use below. */
570 status = create_synthetic_smb_fname(ctx, pdp->reqpath, NULL,
571 NULL, &smb_fname);
572 if (!NT_STATUS_IS_OK(status)) {
573 return status;
577 /* Optimization - check if we can redirect the whole path. */
579 if (is_msdfs_link_internal(ctx, conn, smb_fname->base_name,
580 pp_targetpath, NULL)) {
581 if (search_flag) {
582 DEBUG(6,("dfs_path_lookup (FindFirst) No redirection "
583 "for dfs link %s.\n", dfspath));
584 status = NT_STATUS_OK;
585 goto out;
588 DEBUG(6,("dfs_path_lookup: %s resolves to a "
589 "valid dfs link %s.\n", dfspath,
590 pp_targetpath ? *pp_targetpath : ""));
592 if (consumedcntp) {
593 *consumedcntp = strlen(dfspath);
595 status = NT_STATUS_PATH_NOT_COVERED;
596 goto out;
599 /* Prepare to test only for '/' components in the given path,
600 * so if a Windows path replace all '\\' characters with '/'.
601 * For a POSIX DFS path we know all separators are already '/'. */
603 canon_dfspath = talloc_strdup(ctx, dfspath);
604 if (!canon_dfspath) {
605 status = NT_STATUS_NO_MEMORY;
606 goto out;
608 if (!pdp->posix_path) {
609 string_replace(canon_dfspath, '\\', '/');
613 * localpath comes out of unix_convert, so it has
614 * no trailing backslash. Make sure that canon_dfspath hasn't either.
615 * Fix for bug #4860 from Jan Martin <Jan.Martin@rwedea.com>.
618 trim_char(canon_dfspath,0,'/');
621 * Redirect if any component in the path is a link.
622 * We do this by walking backwards through the
623 * local path, chopping off the last component
624 * in both the local path and the canonicalized
625 * DFS path. If we hit a DFS link then we're done.
628 p = strrchr_m(smb_fname->base_name, '/');
629 if (consumedcntp) {
630 q = strrchr_m(canon_dfspath, '/');
633 while (p) {
634 *p = '\0';
635 if (q) {
636 *q = '\0';
639 if (is_msdfs_link_internal(ctx, conn,
640 smb_fname->base_name, pp_targetpath,
641 NULL)) {
642 DEBUG(4, ("dfs_path_lookup: Redirecting %s because "
643 "parent %s is dfs link\n", dfspath,
644 smb_fname_str_dbg(smb_fname)));
646 if (consumedcntp) {
647 *consumedcntp = strlen(canon_dfspath);
648 DEBUG(10, ("dfs_path_lookup: Path consumed: %s "
649 "(%d)\n",
650 canon_dfspath,
651 *consumedcntp));
654 status = NT_STATUS_PATH_NOT_COVERED;
655 goto out;
658 /* Step back on the filesystem. */
659 p = strrchr_m(smb_fname->base_name, '/');
661 if (consumedcntp) {
662 /* And in the canonicalized dfs path. */
663 q = strrchr_m(canon_dfspath, '/');
667 status = NT_STATUS_OK;
668 out:
669 TALLOC_FREE(smb_fname);
670 return status;
673 /*****************************************************************
674 Decides if a dfs pathname should be redirected or not.
675 If not, the pathname is converted to a tcon-relative local unix path
677 search_wcard_flag: this flag performs 2 functions both related
678 to searches. See resolve_dfs_path() and parse_dfs_path_XX()
679 for details.
681 This function can return NT_STATUS_OK, meaning use the returned path as-is
682 (mapped into a local path).
683 or NT_STATUS_NOT_COVERED meaning return a DFS redirect, or
684 any other NT_STATUS error which is a genuine error to be
685 returned to the client.
686 *****************************************************************/
688 static NTSTATUS dfs_redirect(TALLOC_CTX *ctx,
689 connection_struct *conn,
690 const char *path_in,
691 bool search_wcard_flag,
692 char **pp_path_out,
693 bool *ppath_contains_wcard)
695 NTSTATUS status;
696 struct dfs_path *pdp = talloc(ctx, struct dfs_path);
698 if (!pdp) {
699 return NT_STATUS_NO_MEMORY;
702 status = parse_dfs_path(conn, path_in, search_wcard_flag, pdp,
703 ppath_contains_wcard);
704 if (!NT_STATUS_IS_OK(status)) {
705 TALLOC_FREE(pdp);
706 return status;
709 if (pdp->reqpath[0] == '\0') {
710 TALLOC_FREE(pdp);
711 *pp_path_out = talloc_strdup(ctx, "");
712 if (!*pp_path_out) {
713 return NT_STATUS_NO_MEMORY;
715 DEBUG(5,("dfs_redirect: self-referral.\n"));
716 return NT_STATUS_OK;
719 /* If dfs pathname for a non-dfs share, convert to tcon-relative
720 path and return OK */
722 if (!lp_msdfs_root(SNUM(conn))) {
723 *pp_path_out = talloc_strdup(ctx, pdp->reqpath);
724 TALLOC_FREE(pdp);
725 if (!*pp_path_out) {
726 return NT_STATUS_NO_MEMORY;
728 return NT_STATUS_OK;
731 /* If it looked like a local path (zero hostname/servicename)
732 * just treat as a tcon-relative path. */
734 if (pdp->hostname[0] == '\0' && pdp->servicename[0] == '\0') {
735 *pp_path_out = talloc_strdup(ctx, pdp->reqpath);
736 TALLOC_FREE(pdp);
737 if (!*pp_path_out) {
738 return NT_STATUS_NO_MEMORY;
740 return NT_STATUS_OK;
743 if (!( strequal(pdp->servicename, lp_servicename(SNUM(conn)))
744 || (strequal(pdp->servicename, HOMES_NAME)
745 && strequal(lp_servicename(SNUM(conn)),
746 conn->session_info->sanitized_username) )) ) {
748 /* The given sharename doesn't match this connection. */
749 TALLOC_FREE(pdp);
751 return NT_STATUS_OBJECT_PATH_NOT_FOUND;
754 status = dfs_path_lookup(ctx, conn, path_in, pdp,
755 search_wcard_flag, NULL, NULL);
756 if (!NT_STATUS_IS_OK(status)) {
757 if (NT_STATUS_EQUAL(status, NT_STATUS_PATH_NOT_COVERED)) {
758 DEBUG(3,("dfs_redirect: Redirecting %s\n", path_in));
759 } else {
760 DEBUG(10,("dfs_redirect: dfs_path_lookup "
761 "failed for %s with %s\n",
762 path_in, nt_errstr(status) ));
764 return status;
767 DEBUG(3,("dfs_redirect: Not redirecting %s.\n", path_in));
769 /* Form non-dfs tcon-relative path */
770 *pp_path_out = talloc_strdup(ctx, pdp->reqpath);
771 TALLOC_FREE(pdp);
772 if (!*pp_path_out) {
773 return NT_STATUS_NO_MEMORY;
776 DEBUG(3,("dfs_redirect: Path %s converted to non-dfs path %s\n",
777 path_in,
778 *pp_path_out));
780 return NT_STATUS_OK;
783 /**********************************************************************
784 Return a self referral.
785 **********************************************************************/
787 static NTSTATUS self_ref(TALLOC_CTX *ctx,
788 const char *dfs_path,
789 struct junction_map *jucn,
790 int *consumedcntp,
791 bool *self_referralp)
793 struct referral *ref;
795 *self_referralp = True;
797 jucn->referral_count = 1;
798 if((ref = talloc_zero(ctx, struct referral)) == NULL) {
799 return NT_STATUS_NO_MEMORY;
802 ref->alternate_path = talloc_strdup(ctx, dfs_path);
803 if (!ref->alternate_path) {
804 return NT_STATUS_NO_MEMORY;
806 ref->proximity = 0;
807 ref->ttl = REFERRAL_TTL;
808 jucn->referral_list = ref;
809 *consumedcntp = strlen(dfs_path);
810 return NT_STATUS_OK;
813 /**********************************************************************
814 Gets valid referrals for a dfs path and fills up the
815 junction_map structure.
816 **********************************************************************/
818 NTSTATUS get_referred_path(TALLOC_CTX *ctx,
819 const char *dfs_path,
820 struct junction_map *jucn,
821 int *consumedcntp,
822 bool *self_referralp)
824 struct connection_struct *conn;
825 char *targetpath = NULL;
826 int snum;
827 NTSTATUS status = NT_STATUS_NOT_FOUND;
828 bool dummy;
829 struct dfs_path *pdp = talloc(ctx, struct dfs_path);
830 char *oldpath;
832 if (!pdp) {
833 return NT_STATUS_NO_MEMORY;
836 *self_referralp = False;
838 status = parse_dfs_path(NULL, dfs_path, False, pdp, &dummy);
839 if (!NT_STATUS_IS_OK(status)) {
840 return status;
843 jucn->service_name = talloc_strdup(ctx, pdp->servicename);
844 jucn->volume_name = talloc_strdup(ctx, pdp->reqpath);
845 if (!jucn->service_name || !jucn->volume_name) {
846 TALLOC_FREE(pdp);
847 return NT_STATUS_NO_MEMORY;
850 /* Verify the share is a dfs root */
851 snum = lp_servicenumber(jucn->service_name);
852 if(snum < 0) {
853 char *service_name = NULL;
854 if ((snum = find_service(ctx, jucn->service_name, &service_name)) < 0) {
855 return NT_STATUS_NOT_FOUND;
857 if (!service_name) {
858 return NT_STATUS_NO_MEMORY;
860 TALLOC_FREE(jucn->service_name);
861 jucn->service_name = talloc_strdup(ctx, service_name);
862 if (!jucn->service_name) {
863 TALLOC_FREE(pdp);
864 return NT_STATUS_NO_MEMORY;
868 if (!lp_msdfs_root(snum) && (*lp_msdfs_proxy(snum) == '\0')) {
869 DEBUG(3,("get_referred_path: |%s| in dfs path %s is not "
870 "a dfs root.\n",
871 pdp->servicename, dfs_path));
872 TALLOC_FREE(pdp);
873 return NT_STATUS_NOT_FOUND;
877 * Self referrals are tested with a anonymous IPC connection and
878 * a GET_DFS_REFERRAL call to \\server\share. (which means
879 * dp.reqpath[0] points to an empty string). create_conn_struct cd's
880 * into the directory and will fail if it cannot (as the anonymous
881 * user). Cope with this.
884 if (pdp->reqpath[0] == '\0') {
885 char *tmp;
886 struct referral *ref;
888 if (*lp_msdfs_proxy(snum) == '\0') {
889 TALLOC_FREE(pdp);
890 return self_ref(ctx,
891 dfs_path,
892 jucn,
893 consumedcntp,
894 self_referralp);
898 * It's an msdfs proxy share. Redirect to
899 * the configured target share.
902 jucn->referral_count = 1;
903 if ((ref = talloc_zero(ctx, struct referral)) == NULL) {
904 TALLOC_FREE(pdp);
905 return NT_STATUS_NO_MEMORY;
908 if (!(tmp = talloc_strdup(ctx, lp_msdfs_proxy(snum)))) {
909 TALLOC_FREE(pdp);
910 return NT_STATUS_NO_MEMORY;
913 trim_string(tmp, "\\", 0);
915 ref->alternate_path = talloc_asprintf(ctx, "\\%s", tmp);
916 TALLOC_FREE(tmp);
918 if (!ref->alternate_path) {
919 TALLOC_FREE(pdp);
920 return NT_STATUS_NO_MEMORY;
923 if (pdp->reqpath[0] != '\0') {
924 ref->alternate_path = talloc_asprintf_append(
925 ref->alternate_path,
926 "%s",
927 pdp->reqpath);
928 if (!ref->alternate_path) {
929 TALLOC_FREE(pdp);
930 return NT_STATUS_NO_MEMORY;
933 ref->proximity = 0;
934 ref->ttl = REFERRAL_TTL;
935 jucn->referral_list = ref;
936 *consumedcntp = strlen(dfs_path);
937 TALLOC_FREE(pdp);
938 return NT_STATUS_OK;
941 status = create_conn_struct(ctx, &conn, snum, lp_pathname(snum),
942 NULL, &oldpath);
943 if (!NT_STATUS_IS_OK(status)) {
944 TALLOC_FREE(pdp);
945 return status;
948 /* If this is a DFS path dfs_lookup should return
949 * NT_STATUS_PATH_NOT_COVERED. */
951 status = dfs_path_lookup(ctx, conn, dfs_path, pdp,
952 False, consumedcntp, &targetpath);
954 if (!NT_STATUS_EQUAL(status, NT_STATUS_PATH_NOT_COVERED)) {
955 DEBUG(3,("get_referred_path: No valid referrals for path %s\n",
956 dfs_path));
957 goto err_exit;
960 /* We know this is a valid dfs link. Parse the targetpath. */
961 if (!parse_msdfs_symlink(ctx, targetpath,
962 &jucn->referral_list,
963 &jucn->referral_count)) {
964 DEBUG(3,("get_referred_path: failed to parse symlink "
965 "target %s\n", targetpath ));
966 status = NT_STATUS_NOT_FOUND;
967 goto err_exit;
970 status = NT_STATUS_OK;
971 err_exit:
972 vfs_ChDir(conn, oldpath);
973 SMB_VFS_DISCONNECT(conn);
974 conn_free(conn);
975 TALLOC_FREE(pdp);
976 return status;
979 static int setup_ver2_dfs_referral(const char *pathname,
980 char **ppdata,
981 struct junction_map *junction,
982 bool self_referral)
984 char* pdata = *ppdata;
986 smb_ucs2_t *uni_requestedpath = NULL;
987 int uni_reqpathoffset1,uni_reqpathoffset2;
988 int uni_curroffset;
989 int requestedpathlen=0;
990 int offset;
991 int reply_size = 0;
992 int i=0;
994 DEBUG(10,("Setting up version2 referral\nRequested path:\n"));
996 requestedpathlen = rpcstr_push_talloc(talloc_tos(),
997 &uni_requestedpath, pathname);
998 if (uni_requestedpath == NULL || requestedpathlen == 0) {
999 return -1;
1002 if (DEBUGLVL(10)) {
1003 dump_data(0, (unsigned char *)uni_requestedpath,
1004 requestedpathlen);
1007 DEBUG(10,("ref count = %u\n",junction->referral_count));
1009 uni_reqpathoffset1 = REFERRAL_HEADER_SIZE +
1010 VERSION2_REFERRAL_SIZE * junction->referral_count;
1012 uni_reqpathoffset2 = uni_reqpathoffset1 + requestedpathlen;
1014 uni_curroffset = uni_reqpathoffset2 + requestedpathlen;
1016 reply_size = REFERRAL_HEADER_SIZE +
1017 VERSION2_REFERRAL_SIZE*junction->referral_count +
1018 2 * requestedpathlen;
1019 DEBUG(10,("reply_size: %u\n",reply_size));
1021 /* add up the unicode lengths of all the referral paths */
1022 for(i=0;i<junction->referral_count;i++) {
1023 DEBUG(10,("referral %u : %s\n",
1025 junction->referral_list[i].alternate_path));
1026 reply_size +=
1027 (strlen(junction->referral_list[i].alternate_path)+1)*2;
1030 DEBUG(10,("reply_size = %u\n",reply_size));
1031 /* add the unexplained 0x16 bytes */
1032 reply_size += 0x16;
1034 pdata = (char *)SMB_REALLOC(pdata,reply_size);
1035 if(pdata == NULL) {
1036 DEBUG(0,("Realloc failed!\n"));
1037 return -1;
1039 *ppdata = pdata;
1041 /* copy in the dfs requested paths.. required for offset calculations */
1042 memcpy(pdata+uni_reqpathoffset1,uni_requestedpath,requestedpathlen);
1043 memcpy(pdata+uni_reqpathoffset2,uni_requestedpath,requestedpathlen);
1045 /* create the header */
1046 SSVAL(pdata,0,requestedpathlen - 2); /* UCS2 of path consumed minus
1047 2 byte null */
1048 /* number of referral in this pkt */
1049 SSVAL(pdata,2,junction->referral_count);
1050 if(self_referral) {
1051 SIVAL(pdata,4,DFSREF_REFERRAL_SERVER | DFSREF_STORAGE_SERVER);
1052 } else {
1053 SIVAL(pdata,4,DFSREF_STORAGE_SERVER);
1056 offset = 8;
1057 /* add the referral elements */
1058 for(i=0;i<junction->referral_count;i++) {
1059 struct referral* ref = &junction->referral_list[i];
1060 int unilen;
1062 SSVAL(pdata,offset,2); /* version 2 */
1063 SSVAL(pdata,offset+2,VERSION2_REFERRAL_SIZE);
1064 if(self_referral) {
1065 SSVAL(pdata,offset+4,1);
1066 } else {
1067 SSVAL(pdata,offset+4,0);
1070 /* ref_flags :use path_consumed bytes? */
1071 SSVAL(pdata,offset+6,0);
1072 SIVAL(pdata,offset+8,ref->proximity);
1073 SIVAL(pdata,offset+12,ref->ttl);
1075 SSVAL(pdata,offset+16,uni_reqpathoffset1-offset);
1076 SSVAL(pdata,offset+18,uni_reqpathoffset2-offset);
1077 /* copy referred path into current offset */
1078 unilen = rpcstr_push(pdata+uni_curroffset,
1079 ref->alternate_path,
1080 reply_size - uni_curroffset,
1081 STR_UNICODE);
1083 SSVAL(pdata,offset+20,uni_curroffset-offset);
1085 uni_curroffset += unilen;
1086 offset += VERSION2_REFERRAL_SIZE;
1088 /* add in the unexplained 22 (0x16) bytes at the end */
1089 memset(pdata+uni_curroffset,'\0',0x16);
1090 return reply_size;
1093 static int setup_ver3_dfs_referral(const char *pathname,
1094 char **ppdata,
1095 struct junction_map *junction,
1096 bool self_referral)
1098 char *pdata = *ppdata;
1100 smb_ucs2_t *uni_reqpath = NULL;
1101 int uni_reqpathoffset1, uni_reqpathoffset2;
1102 int uni_curroffset;
1103 int reply_size = 0;
1105 int reqpathlen = 0;
1106 int offset,i=0;
1108 DEBUG(10,("setting up version3 referral\n"));
1110 reqpathlen = rpcstr_push_talloc(talloc_tos(), &uni_reqpath, pathname);
1111 if (uni_reqpath == NULL || reqpathlen == 0) {
1112 return -1;
1115 if (DEBUGLVL(10)) {
1116 dump_data(0, (unsigned char *)uni_reqpath,
1117 reqpathlen);
1120 uni_reqpathoffset1 = REFERRAL_HEADER_SIZE +
1121 VERSION3_REFERRAL_SIZE * junction->referral_count;
1122 uni_reqpathoffset2 = uni_reqpathoffset1 + reqpathlen;
1123 reply_size = uni_curroffset = uni_reqpathoffset2 + reqpathlen;
1125 for(i=0;i<junction->referral_count;i++) {
1126 DEBUG(10,("referral %u : %s\n",
1128 junction->referral_list[i].alternate_path));
1129 reply_size +=
1130 (strlen(junction->referral_list[i].alternate_path)+1)*2;
1133 pdata = (char *)SMB_REALLOC(pdata,reply_size);
1134 if(pdata == NULL) {
1135 DEBUG(0,("version3 referral setup:"
1136 "malloc failed for Realloc!\n"));
1137 return -1;
1139 *ppdata = pdata;
1141 /* create the header */
1142 SSVAL(pdata,0,reqpathlen - 2); /* UCS2 of path consumed minus
1143 2 byte null */
1144 SSVAL(pdata,2,junction->referral_count); /* number of referral */
1145 if(self_referral) {
1146 SIVAL(pdata,4,DFSREF_REFERRAL_SERVER | DFSREF_STORAGE_SERVER);
1147 } else {
1148 SIVAL(pdata,4,DFSREF_STORAGE_SERVER);
1151 /* copy in the reqpaths */
1152 memcpy(pdata+uni_reqpathoffset1,uni_reqpath,reqpathlen);
1153 memcpy(pdata+uni_reqpathoffset2,uni_reqpath,reqpathlen);
1155 offset = 8;
1156 for(i=0;i<junction->referral_count;i++) {
1157 struct referral* ref = &(junction->referral_list[i]);
1158 int unilen;
1160 SSVAL(pdata,offset,3); /* version 3 */
1161 SSVAL(pdata,offset+2,VERSION3_REFERRAL_SIZE);
1162 if(self_referral) {
1163 SSVAL(pdata,offset+4,1);
1164 } else {
1165 SSVAL(pdata,offset+4,0);
1168 /* ref_flags :use path_consumed bytes? */
1169 SSVAL(pdata,offset+6,0);
1170 SIVAL(pdata,offset+8,ref->ttl);
1172 SSVAL(pdata,offset+12,uni_reqpathoffset1-offset);
1173 SSVAL(pdata,offset+14,uni_reqpathoffset2-offset);
1174 /* copy referred path into current offset */
1175 unilen = rpcstr_push(pdata+uni_curroffset,ref->alternate_path,
1176 reply_size - uni_curroffset,
1177 STR_UNICODE | STR_TERMINATE);
1178 SSVAL(pdata,offset+16,uni_curroffset-offset);
1179 /* copy 0x10 bytes of 00's in the ServiceSite GUID */
1180 memset(pdata+offset+18,'\0',16);
1182 uni_curroffset += unilen;
1183 offset += VERSION3_REFERRAL_SIZE;
1185 return reply_size;
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 struct junction_map *junction = NULL;
1201 int consumedcnt = 0;
1202 bool self_referral = False;
1203 int reply_size = 0;
1204 char *pathnamep = NULL;
1205 char *local_dfs_path = NULL;
1206 TALLOC_CTX *ctx;
1208 if (!(ctx=talloc_init("setup_dfs_referral"))) {
1209 *pstatus = NT_STATUS_NO_MEMORY;
1210 return -1;
1213 /* get the junction entry */
1214 if (!dfs_path) {
1215 talloc_destroy(ctx);
1216 *pstatus = NT_STATUS_NOT_FOUND;
1217 return -1;
1221 * Trim pathname sent by client so it begins with only one backslash.
1222 * Two backslashes confuse some dfs clients
1225 local_dfs_path = talloc_strdup(ctx,dfs_path);
1226 if (!local_dfs_path) {
1227 *pstatus = NT_STATUS_NO_MEMORY;
1228 talloc_destroy(ctx);
1229 return -1;
1231 pathnamep = local_dfs_path;
1232 while (IS_DIRECTORY_SEP(pathnamep[0]) &&
1233 IS_DIRECTORY_SEP(pathnamep[1])) {
1234 pathnamep++;
1237 junction = talloc_zero(ctx, struct junction_map);
1238 if (!junction) {
1239 *pstatus = NT_STATUS_NO_MEMORY;
1240 talloc_destroy(ctx);
1241 return -1;
1244 /* The following call can change cwd. */
1245 *pstatus = get_referred_path(ctx, pathnamep, junction,
1246 &consumedcnt, &self_referral);
1247 if (!NT_STATUS_IS_OK(*pstatus)) {
1248 vfs_ChDir(orig_conn,orig_conn->connectpath);
1249 talloc_destroy(ctx);
1250 return -1;
1252 vfs_ChDir(orig_conn,orig_conn->connectpath);
1254 if (!self_referral) {
1255 pathnamep[consumedcnt] = '\0';
1257 if( DEBUGLVL( 3 ) ) {
1258 int i=0;
1259 dbgtext("setup_dfs_referral: Path %s to "
1260 "alternate path(s):",
1261 pathnamep);
1262 for(i=0;i<junction->referral_count;i++)
1263 dbgtext(" %s",
1264 junction->referral_list[i].alternate_path);
1265 dbgtext(".\n");
1269 /* create the referral depeding on version */
1270 DEBUG(10,("max_referral_level :%d\n",max_referral_level));
1272 if (max_referral_level < 2) {
1273 max_referral_level = 2;
1275 if (max_referral_level > 3) {
1276 max_referral_level = 3;
1279 switch(max_referral_level) {
1280 case 2:
1281 reply_size = setup_ver2_dfs_referral(pathnamep,
1282 ppdata, junction,
1283 self_referral);
1284 break;
1285 case 3:
1286 reply_size = setup_ver3_dfs_referral(pathnamep, ppdata,
1287 junction, self_referral);
1288 break;
1289 default:
1290 DEBUG(0,("setup_dfs_referral: Invalid dfs referral "
1291 "version: %d\n",
1292 max_referral_level));
1293 talloc_destroy(ctx);
1294 *pstatus = NT_STATUS_INVALID_LEVEL;
1295 return -1;
1298 if (DEBUGLVL(10)) {
1299 DEBUGADD(0,("DFS Referral pdata:\n"));
1300 dump_data(0,(uint8 *)*ppdata,reply_size);
1303 talloc_destroy(ctx);
1304 *pstatus = NT_STATUS_OK;
1305 return reply_size;
1308 /**********************************************************************
1309 The following functions are called by the NETDFS RPC pipe functions
1310 **********************************************************************/
1312 /*********************************************************************
1313 Creates a junction structure from a DFS pathname
1314 **********************************************************************/
1316 bool create_junction(TALLOC_CTX *ctx,
1317 const char *dfs_path,
1318 struct junction_map *jucn)
1320 int snum;
1321 bool dummy;
1322 struct dfs_path *pdp = talloc(ctx,struct dfs_path);
1323 NTSTATUS status;
1325 if (!pdp) {
1326 return False;
1328 status = parse_dfs_path(NULL, dfs_path, False, pdp, &dummy);
1329 if (!NT_STATUS_IS_OK(status)) {
1330 return False;
1333 /* check if path is dfs : validate first token */
1334 if (!is_myname_or_ipaddr(pdp->hostname)) {
1335 DEBUG(4,("create_junction: Invalid hostname %s "
1336 "in dfs path %s\n",
1337 pdp->hostname, dfs_path));
1338 TALLOC_FREE(pdp);
1339 return False;
1342 /* Check for a non-DFS share */
1343 snum = lp_servicenumber(pdp->servicename);
1345 if(snum < 0 || !lp_msdfs_root(snum)) {
1346 DEBUG(4,("create_junction: %s is not an msdfs root.\n",
1347 pdp->servicename));
1348 TALLOC_FREE(pdp);
1349 return False;
1352 jucn->service_name = talloc_strdup(ctx, pdp->servicename);
1353 jucn->volume_name = talloc_strdup(ctx, pdp->reqpath);
1354 jucn->comment = talloc_strdup(ctx, lp_comment(snum));
1356 TALLOC_FREE(pdp);
1357 if (!jucn->service_name || !jucn->volume_name || ! jucn->comment) {
1358 return False;
1360 return True;
1363 /**********************************************************************
1364 Forms a valid Unix pathname from the junction
1365 **********************************************************************/
1367 static bool junction_to_local_path(const struct junction_map *jucn,
1368 char **pp_path_out,
1369 connection_struct **conn_out,
1370 char **oldpath)
1372 int snum;
1373 NTSTATUS status;
1375 snum = lp_servicenumber(jucn->service_name);
1376 if(snum < 0) {
1377 return False;
1379 status = create_conn_struct(talloc_tos(), conn_out, snum,
1380 lp_pathname(snum), NULL, oldpath);
1381 if (!NT_STATUS_IS_OK(status)) {
1382 return False;
1385 *pp_path_out = talloc_asprintf(*conn_out,
1386 "%s/%s",
1387 lp_pathname(snum),
1388 jucn->volume_name);
1389 if (!*pp_path_out) {
1390 vfs_ChDir(*conn_out, *oldpath);
1391 SMB_VFS_DISCONNECT(*conn_out);
1392 conn_free(*conn_out);
1393 return False;
1395 return True;
1398 bool create_msdfs_link(const struct junction_map *jucn)
1400 char *path = NULL;
1401 char *cwd;
1402 char *msdfs_link = NULL;
1403 connection_struct *conn;
1404 int i=0;
1405 bool insert_comma = False;
1406 bool ret = False;
1408 if(!junction_to_local_path(jucn, &path, &conn, &cwd)) {
1409 return False;
1412 /* Form the msdfs_link contents */
1413 msdfs_link = talloc_strdup(conn, "msdfs:");
1414 if (!msdfs_link) {
1415 goto out;
1417 for(i=0; i<jucn->referral_count; i++) {
1418 char *refpath = jucn->referral_list[i].alternate_path;
1420 /* Alternate paths always use Windows separators. */
1421 trim_char(refpath, '\\', '\\');
1422 if(*refpath == '\0') {
1423 if (i == 0) {
1424 insert_comma = False;
1426 continue;
1428 if (i > 0 && insert_comma) {
1429 msdfs_link = talloc_asprintf_append_buffer(msdfs_link,
1430 ",%s",
1431 refpath);
1432 } else {
1433 msdfs_link = talloc_asprintf_append_buffer(msdfs_link,
1434 "%s",
1435 refpath);
1438 if (!msdfs_link) {
1439 goto out;
1441 if (!insert_comma) {
1442 insert_comma = True;
1446 DEBUG(5,("create_msdfs_link: Creating new msdfs link: %s -> %s\n",
1447 path, msdfs_link));
1449 if(SMB_VFS_SYMLINK(conn, msdfs_link, path) < 0) {
1450 if (errno == EEXIST) {
1451 struct smb_filename *smb_fname = NULL;
1452 NTSTATUS status;
1454 status = create_synthetic_smb_fname(talloc_tos(), path,
1455 NULL, NULL,
1456 &smb_fname);
1457 if (!NT_STATUS_IS_OK(status)) {
1458 errno = map_errno_from_nt_status(status);
1459 goto out;
1462 if(SMB_VFS_UNLINK(conn, smb_fname)!=0) {
1463 TALLOC_FREE(smb_fname);
1464 goto out;
1466 TALLOC_FREE(smb_fname);
1468 if (SMB_VFS_SYMLINK(conn, msdfs_link, path) < 0) {
1469 DEBUG(1,("create_msdfs_link: symlink failed "
1470 "%s -> %s\nError: %s\n",
1471 path, msdfs_link, strerror(errno)));
1472 goto out;
1476 ret = True;
1478 out:
1479 vfs_ChDir(conn, cwd);
1480 SMB_VFS_DISCONNECT(conn);
1481 conn_free(conn);
1482 return ret;
1485 bool remove_msdfs_link(const struct junction_map *jucn)
1487 char *path = NULL;
1488 char *cwd;
1489 connection_struct *conn;
1490 bool ret = False;
1491 struct smb_filename *smb_fname = NULL;
1492 NTSTATUS status;
1494 if (!junction_to_local_path(jucn, &path, &conn, &cwd)) {
1495 return false;
1498 status = create_synthetic_smb_fname(talloc_tos(), path,
1499 NULL, NULL,
1500 &smb_fname);
1501 if (!NT_STATUS_IS_OK(status)) {
1502 errno = map_errno_from_nt_status(status);
1503 return false;
1506 if( SMB_VFS_UNLINK(conn, smb_fname) == 0 ) {
1507 ret = True;
1510 TALLOC_FREE(smb_fname);
1511 vfs_ChDir(conn, cwd);
1512 SMB_VFS_DISCONNECT(conn);
1513 conn_free(conn);
1514 return ret;
1517 /*********************************************************************
1518 Return the number of DFS links at the root of this share.
1519 *********************************************************************/
1521 static int count_dfs_links(TALLOC_CTX *ctx, int snum)
1523 size_t cnt = 0;
1524 SMB_STRUCT_DIR *dirp = NULL;
1525 const char *dname = NULL;
1526 char *talloced = NULL;
1527 const char *connect_path = lp_pathname(snum);
1528 const char *msdfs_proxy = lp_msdfs_proxy(snum);
1529 connection_struct *conn;
1530 NTSTATUS status;
1531 char *cwd;
1533 if(*connect_path == '\0') {
1534 return 0;
1538 * Fake up a connection struct for the VFS layer.
1541 status = create_conn_struct(talloc_tos(), &conn, snum, connect_path,
1542 NULL, &cwd);
1543 if (!NT_STATUS_IS_OK(status)) {
1544 DEBUG(3, ("create_conn_struct failed: %s\n",
1545 nt_errstr(status)));
1546 return 0;
1549 /* Count a link for the msdfs root - convention */
1550 cnt = 1;
1552 /* No more links if this is an msdfs proxy. */
1553 if (*msdfs_proxy != '\0') {
1554 goto out;
1557 /* Now enumerate all dfs links */
1558 dirp = SMB_VFS_OPENDIR(conn, ".", NULL, 0);
1559 if(!dirp) {
1560 goto out;
1563 while ((dname = vfs_readdirname(conn, dirp, NULL, &talloced))
1564 != NULL) {
1565 if (is_msdfs_link(conn,
1566 dname,
1567 NULL)) {
1568 cnt++;
1570 TALLOC_FREE(talloced);
1573 SMB_VFS_CLOSEDIR(conn,dirp);
1575 out:
1576 vfs_ChDir(conn, cwd);
1577 SMB_VFS_DISCONNECT(conn);
1578 conn_free(conn);
1579 return cnt;
1582 /*********************************************************************
1583 *********************************************************************/
1585 static int form_junctions(TALLOC_CTX *ctx,
1586 int snum,
1587 struct junction_map *jucn,
1588 size_t jn_remain)
1590 size_t cnt = 0;
1591 SMB_STRUCT_DIR *dirp = NULL;
1592 const char *dname = NULL;
1593 char *talloced = NULL;
1594 const char *connect_path = lp_pathname(snum);
1595 char *service_name = lp_servicename(snum);
1596 const char *msdfs_proxy = lp_msdfs_proxy(snum);
1597 connection_struct *conn;
1598 struct referral *ref = NULL;
1599 char *cwd;
1600 NTSTATUS status;
1602 if (jn_remain == 0) {
1603 return 0;
1606 if(*connect_path == '\0') {
1607 return 0;
1611 * Fake up a connection struct for the VFS layer.
1614 status = create_conn_struct(ctx, &conn, snum, connect_path, NULL,
1615 &cwd);
1616 if (!NT_STATUS_IS_OK(status)) {
1617 DEBUG(3, ("create_conn_struct failed: %s\n",
1618 nt_errstr(status)));
1619 return 0;
1622 /* form a junction for the msdfs root - convention
1623 DO NOT REMOVE THIS: NT clients will not work with us
1624 if this is not present
1626 jucn[cnt].service_name = talloc_strdup(ctx,service_name);
1627 jucn[cnt].volume_name = talloc_strdup(ctx, "");
1628 if (!jucn[cnt].service_name || !jucn[cnt].volume_name) {
1629 goto out;
1631 jucn[cnt].comment = "";
1632 jucn[cnt].referral_count = 1;
1634 ref = jucn[cnt].referral_list = talloc_zero(ctx, struct referral);
1635 if (jucn[cnt].referral_list == NULL) {
1636 goto out;
1639 ref->proximity = 0;
1640 ref->ttl = REFERRAL_TTL;
1641 if (*msdfs_proxy != '\0') {
1642 ref->alternate_path = talloc_strdup(ctx,
1643 msdfs_proxy);
1644 } else {
1645 ref->alternate_path = talloc_asprintf(ctx,
1646 "\\\\%s\\%s",
1647 get_local_machine_name(),
1648 service_name);
1651 if (!ref->alternate_path) {
1652 goto out;
1654 cnt++;
1656 /* Don't enumerate if we're an msdfs proxy. */
1657 if (*msdfs_proxy != '\0') {
1658 goto out;
1661 /* Now enumerate all dfs links */
1662 dirp = SMB_VFS_OPENDIR(conn, ".", NULL, 0);
1663 if(!dirp) {
1664 goto out;
1667 while ((dname = vfs_readdirname(conn, dirp, NULL, &talloced))
1668 != NULL) {
1669 char *link_target = NULL;
1670 if (cnt >= jn_remain) {
1671 DEBUG(2, ("form_junctions: ran out of MSDFS "
1672 "junction slots"));
1673 TALLOC_FREE(talloced);
1674 goto out;
1676 if (is_msdfs_link_internal(ctx,
1677 conn,
1678 dname, &link_target,
1679 NULL)) {
1680 if (parse_msdfs_symlink(ctx,
1681 link_target,
1682 &jucn[cnt].referral_list,
1683 &jucn[cnt].referral_count)) {
1685 jucn[cnt].service_name = talloc_strdup(ctx,
1686 service_name);
1687 jucn[cnt].volume_name = talloc_strdup(ctx,
1688 dname);
1689 if (!jucn[cnt].service_name ||
1690 !jucn[cnt].volume_name) {
1691 TALLOC_FREE(talloced);
1692 goto out;
1694 jucn[cnt].comment = "";
1695 cnt++;
1697 TALLOC_FREE(link_target);
1699 TALLOC_FREE(talloced);
1702 out:
1704 if (dirp) {
1705 SMB_VFS_CLOSEDIR(conn,dirp);
1708 vfs_ChDir(conn, cwd);
1709 conn_free(conn);
1710 return cnt;
1713 struct junction_map *enum_msdfs_links(struct smbd_server_connection *sconn,
1714 TALLOC_CTX *ctx, size_t *p_num_jn)
1716 struct junction_map *jn = NULL;
1717 int i=0;
1718 size_t jn_count = 0;
1719 int sharecount = 0;
1721 *p_num_jn = 0;
1722 if(!lp_host_msdfs()) {
1723 return NULL;
1726 /* Ensure all the usershares are loaded. */
1727 become_root();
1728 load_registry_shares();
1729 sharecount = load_usershare_shares(sconn);
1730 unbecome_root();
1732 for(i=0;i < sharecount;i++) {
1733 if(lp_msdfs_root(i)) {
1734 jn_count += count_dfs_links(ctx, i);
1737 if (jn_count == 0) {
1738 return NULL;
1740 jn = talloc_array(ctx, struct junction_map, jn_count);
1741 if (!jn) {
1742 return NULL;
1744 for(i=0; i < sharecount; i++) {
1745 if (*p_num_jn >= jn_count) {
1746 break;
1748 if(lp_msdfs_root(i)) {
1749 *p_num_jn += form_junctions(ctx, i,
1750 &jn[*p_num_jn],
1751 jn_count - *p_num_jn);
1754 return jn;
1757 /******************************************************************************
1758 Core function to resolve a dfs pathname possibly containing a wildcard. If
1759 ppath_contains_wcard != NULL, it will be set to true if a wildcard is
1760 detected during dfs resolution.
1761 ******************************************************************************/
1763 NTSTATUS resolve_dfspath_wcard(TALLOC_CTX *ctx,
1764 connection_struct *conn,
1765 bool dfs_pathnames,
1766 const char *name_in,
1767 bool allow_wcards,
1768 char **pp_name_out,
1769 bool *ppath_contains_wcard)
1771 bool path_contains_wcard;
1772 NTSTATUS status = NT_STATUS_OK;
1774 if (dfs_pathnames) {
1775 status = dfs_redirect(ctx,
1776 conn,
1777 name_in,
1778 allow_wcards,
1779 pp_name_out,
1780 &path_contains_wcard);
1782 if (NT_STATUS_IS_OK(status) && ppath_contains_wcard != NULL) {
1783 *ppath_contains_wcard = path_contains_wcard;
1785 } else {
1787 * Cheat and just return a copy of the in ptr.
1788 * Once srvstr_get_path() uses talloc it'll
1789 * be a talloced ptr anyway.
1791 *pp_name_out = discard_const_p(char, name_in);
1793 return status;