r23784: use the GPLv3 boilerplate as recommended by the FSF and the license text
[Samba/bb.git] / source / smbd / msdfs.c
blobd4c08848d7c2a1d0d30617687d2e841e17293d7c
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"
26 extern uint32 global_client_caps;
28 /**********************************************************************
29 Parse a DFS pathname of the form \hostname\service\reqpath
30 into the dfs_path structure.
31 If POSIX pathnames is true, the pathname may also be of the
32 form /hostname/service/reqpath.
33 We cope with either here.
35 Unfortunately, due to broken clients who might set the
36 SVAL(inbuf,smb_flg2) & FLAGS2_DFS_PATHNAMES bit and then
37 send a local path, we have to cope with that too....
39 JRA.
40 **********************************************************************/
42 static NTSTATUS parse_dfs_path(const char *pathname,
43 BOOL allow_wcards,
44 struct dfs_path *pdp,
45 BOOL *ppath_contains_wcard)
47 pstring pathname_local;
48 char *p,*temp;
49 NTSTATUS status = NT_STATUS_OK;
50 char sepchar;
52 ZERO_STRUCTP(pdp);
54 pstrcpy(pathname_local,pathname);
55 p = temp = pathname_local;
57 pdp->posix_path = (lp_posix_pathnames() && *pathname == '/');
59 sepchar = pdp->posix_path ? '/' : '\\';
61 if (*pathname != sepchar) {
62 DEBUG(10,("parse_dfs_path: path %s doesn't start with %c\n",
63 pathname, sepchar ));
65 * Possibly client sent a local path by mistake.
66 * Try and convert to a local path.
69 pdp->hostname[0] = '\0';
70 pdp->servicename[0] = '\0';
72 /* We've got no info about separators. */
73 pdp->posix_path = lp_posix_pathnames();
74 p = temp;
75 DEBUG(10,("parse_dfs_path: trying to convert %s to a local path\n",
76 temp));
77 goto local_path;
80 trim_char(temp,sepchar,sepchar);
82 DEBUG(10,("parse_dfs_path: temp = |%s| after trimming %c's\n",
83 temp, sepchar));
85 /* Now tokenize. */
86 /* Parse out hostname. */
87 p = strchr_m(temp,sepchar);
88 if(p == NULL) {
89 DEBUG(10,("parse_dfs_path: can't parse hostname from path %s\n",
90 temp));
92 * Possibly client sent a local path by mistake.
93 * Try and convert to a local path.
96 pdp->hostname[0] = '\0';
97 pdp->servicename[0] = '\0';
99 p = temp;
100 DEBUG(10,("parse_dfs_path: trying to convert %s to a local path\n",
101 temp));
102 goto local_path;
104 *p = '\0';
105 fstrcpy(pdp->hostname,temp);
106 DEBUG(10,("parse_dfs_path: hostname: %s\n",pdp->hostname));
108 /* If we got a hostname, is it ours (or an IP address) ? */
109 if (!is_myname_or_ipaddr(pdp->hostname)) {
110 /* Repair path. */
111 *p = sepchar;
112 DEBUG(10,("parse_dfs_path: hostname %s isn't ours. Try local path from path %s\n",
113 pdp->hostname, temp));
115 * Possibly client sent a local path by mistake.
116 * Try and convert to a local path.
119 pdp->hostname[0] = '\0';
120 pdp->servicename[0] = '\0';
122 p = temp;
123 DEBUG(10,("parse_dfs_path: trying to convert %s to a local path\n",
124 temp));
125 goto local_path;
128 /* Parse out servicename. */
129 temp = p+1;
130 p = strchr_m(temp,sepchar);
131 if(p == NULL) {
132 fstrcpy(pdp->servicename,temp);
133 pdp->reqpath[0] = '\0';
134 return NT_STATUS_OK;
136 *p = '\0';
137 fstrcpy(pdp->servicename,temp);
138 DEBUG(10,("parse_dfs_path: servicename: %s\n",pdp->servicename));
140 p++;
142 local_path:
144 *ppath_contains_wcard = False;
146 pstrcpy(pdp->reqpath, p);
148 /* Rest is reqpath. */
149 if (pdp->posix_path) {
150 status = check_path_syntax_posix(pdp->reqpath);
151 } else {
152 if (allow_wcards) {
153 status = check_path_syntax_wcard(pdp->reqpath, ppath_contains_wcard);
154 } else {
155 status = check_path_syntax(pdp->reqpath);
159 if (!NT_STATUS_IS_OK(status)) {
160 DEBUG(10,("parse_dfs_path: '%s' failed with %s\n",
161 p, nt_errstr(status) ));
162 return status;
165 DEBUG(10,("parse_dfs_path: rest of the path: %s\n",pdp->reqpath));
166 return NT_STATUS_OK;
169 /********************************************************
170 Fake up a connection struct for the VFS layer.
171 Note this CHANGES CWD !!!! JRA.
172 *********************************************************/
174 static NTSTATUS create_conn_struct(connection_struct *conn, int snum, const char *path)
176 pstring connpath;
178 ZERO_STRUCTP(conn);
180 pstrcpy(connpath, path);
181 pstring_sub(connpath , "%S", lp_servicename(snum));
183 /* needed for smbd_vfs_init() */
185 if ((conn->mem_ctx=talloc_init("connection_struct")) == NULL) {
186 DEBUG(0,("talloc_init(connection_struct) failed!\n"));
187 return NT_STATUS_NO_MEMORY;
190 if (!(conn->params = TALLOC_ZERO_P(conn->mem_ctx, struct share_params))) {
191 DEBUG(0, ("TALLOC failed\n"));
192 return NT_STATUS_NO_MEMORY;
195 conn->params->service = snum;
197 set_conn_connectpath(conn, connpath);
199 if (!smbd_vfs_init(conn)) {
200 NTSTATUS status = map_nt_error_from_unix(errno);
201 DEBUG(0,("create_conn_struct: smbd_vfs_init failed.\n"));
202 conn_free_internal(conn);
203 return status;
207 * Windows seems to insist on doing trans2getdfsreferral() calls on the IPC$
208 * share as the anonymous user. If we try to chdir as that user we will
209 * fail.... WTF ? JRA.
212 if (vfs_ChDir(conn,conn->connectpath) != 0) {
213 NTSTATUS status = map_nt_error_from_unix(errno);
214 DEBUG(3,("create_conn_struct: Can't ChDir to new conn path %s. Error was %s\n",
215 conn->connectpath, strerror(errno) ));
216 conn_free_internal(conn);
217 return status;
220 return NT_STATUS_OK;
223 /**********************************************************************
224 Parse the contents of a symlink to verify if it is an msdfs referral
225 A valid referral is of the form:
227 msdfs:server1\share1,server2\share2
228 msdfs:server1\share1\pathname,server2\share2\pathname
229 msdfs:server1/share1,server2/share2
230 msdfs:server1/share1/pathname,server2/share2/pathname.
232 Note that the alternate paths returned here must be of the canonicalized
233 form:
235 \server\share or
236 \server\share\path\to\file,
238 even in posix path mode. This is because we have no knowledge if the
239 server we're referring to understands posix paths.
240 **********************************************************************/
242 static BOOL parse_msdfs_symlink(TALLOC_CTX *ctx,
243 char *target,
244 struct referral **preflist,
245 int *refcount)
247 pstring temp;
248 char *prot;
249 char *alt_path[MAX_REFERRAL_COUNT];
250 int count = 0, i;
251 struct referral *reflist;
253 pstrcpy(temp,target);
254 prot = strtok(temp,":");
255 if (!prot) {
256 DEBUG(0,("parse_msdfs_symlink: invalid path !\n"));
257 return False;
260 /* parse out the alternate paths */
261 while((count<MAX_REFERRAL_COUNT) &&
262 ((alt_path[count] = strtok(NULL,",")) != NULL)) {
263 count++;
266 DEBUG(10,("parse_msdfs_symlink: count=%d\n", count));
268 if (count) {
269 reflist = *preflist = TALLOC_ZERO_ARRAY(ctx, struct referral, count);
270 if(reflist == NULL) {
271 DEBUG(0,("parse_msdfs_symlink: talloc failed!\n"));
272 return False;
274 } else {
275 reflist = *preflist = NULL;
278 for(i=0;i<count;i++) {
279 char *p;
281 /* Canonicalize link target. Replace all /'s in the path by a \ */
282 string_replace(alt_path[i], '/', '\\');
284 /* Remove leading '\\'s */
285 p = alt_path[i];
286 while (*p && (*p == '\\')) {
287 p++;
290 pstrcpy(reflist[i].alternate_path, "\\");
291 pstrcat(reflist[i].alternate_path, p);
293 reflist[i].proximity = 0;
294 reflist[i].ttl = REFERRAL_TTL;
295 DEBUG(10, ("parse_msdfs_symlink: Created alt path: %s\n", reflist[i].alternate_path));
296 *refcount += 1;
299 return True;
302 /**********************************************************************
303 Returns true if the unix path is a valid msdfs symlink and also
304 returns the target string from inside the link.
305 **********************************************************************/
307 BOOL is_msdfs_link(connection_struct *conn,
308 const char *path,
309 pstring link_target,
310 SMB_STRUCT_STAT *sbufp)
312 SMB_STRUCT_STAT st;
313 int referral_len = 0;
315 if (sbufp == NULL) {
316 sbufp = &st;
319 if (SMB_VFS_LSTAT(conn, path, sbufp) != 0) {
320 DEBUG(5,("is_msdfs_link: %s does not exist.\n",path));
321 return False;
324 if (!S_ISLNK(sbufp->st_mode)) {
325 DEBUG(5,("is_msdfs_link: %s is not a link.\n",path));
326 return False;
329 /* open the link and read it */
330 referral_len = SMB_VFS_READLINK(conn, path, link_target, sizeof(pstring)-1);
331 if (referral_len == -1) {
332 DEBUG(0,("is_msdfs_link: Error reading msdfs link %s: %s\n",
333 path, strerror(errno)));
334 return False;
336 link_target[referral_len] = '\0';
338 DEBUG(5,("is_msdfs_link: %s -> %s\n",path, link_target));
340 if (!strnequal(link_target, "msdfs:", 6)) {
341 return False;
343 return True;
346 /*****************************************************************
347 Used by other functions to decide if a dfs path is remote,
348 and to get the list of referred locations for that remote path.
350 search_flag: For findfirsts, dfs links themselves are not
351 redirected, but paths beyond the links are. For normal smb calls,
352 even dfs links need to be redirected.
354 consumedcntp: how much of the dfs path is being redirected. the client
355 should try the remaining path on the redirected server.
357 If this returns NT_STATUS_PATH_NOT_COVERED the contents of the msdfs
358 link redirect are in targetpath.
359 *****************************************************************/
361 static NTSTATUS dfs_path_lookup(connection_struct *conn,
362 const char *dfspath, /* Incoming complete dfs path */
363 const struct dfs_path *pdp, /* Parsed out server+share+extrapath. */
364 BOOL search_flag, /* Called from a findfirst ? */
365 int *consumedcntp,
366 pstring targetpath)
368 char *p = NULL;
369 char *q = NULL;
370 SMB_STRUCT_STAT sbuf;
371 NTSTATUS status;
372 pstring localpath;
373 pstring canon_dfspath; /* Canonicalized dfs path. (only '/' components). */
375 DEBUG(10,("dfs_path_lookup: Conn path = %s reqpath = %s\n",
376 conn->connectpath, pdp->reqpath));
379 * Note the unix path conversion here we're doing we can
380 * throw away. We're looking for a symlink for a dfs
381 * resolution, if we don't find it we'll do another
382 * unix_convert later in the codepath.
383 * If we needed to remember what we'd resolved in
384 * dp->reqpath (as the original code did) we'd
385 * pstrcpy(localhost, dp->reqpath) on any code
386 * path below that returns True - but I don't
387 * think this is needed. JRA.
390 pstrcpy(localpath, pdp->reqpath);
391 status = unix_convert(conn, localpath, search_flag, NULL, &sbuf);
392 if (!NT_STATUS_IS_OK(status)) {
393 return status;
396 /* Optimization - check if we can redirect the whole path. */
398 if (is_msdfs_link(conn, localpath, targetpath, NULL)) {
399 if (search_flag) {
400 DEBUG(6,("dfs_path_lookup (FindFirst) No redirection "
401 "for dfs link %s.\n", dfspath));
402 return NT_STATUS_OK;
405 DEBUG(6,("dfs_path_lookup: %s resolves to a "
406 "valid dfs link %s.\n", dfspath, targetpath));
408 if (consumedcntp) {
409 *consumedcntp = strlen(dfspath);
411 return NT_STATUS_PATH_NOT_COVERED;
414 /* Prepare to test only for '/' components in the given path,
415 * so if a Windows path replace all '\\' characters with '/'.
416 * For a POSIX DFS path we know all separators are already '/'. */
418 pstrcpy(canon_dfspath, dfspath);
419 if (!pdp->posix_path) {
420 string_replace(canon_dfspath, '\\', '/');
424 * Redirect if any component in the path is a link.
425 * We do this by walking backwards through the
426 * local path, chopping off the last component
427 * in both the local path and the canonicalized
428 * DFS path. If we hit a DFS link then we're done.
431 p = strrchr_m(localpath, '/');
432 if (consumedcntp) {
433 q = strrchr_m(canon_dfspath, '/');
436 while (p) {
437 *p = '\0';
438 if (q) {
439 *q = '\0';
442 if (is_msdfs_link(conn, localpath, targetpath, NULL)) {
443 DEBUG(4, ("dfs_path_lookup: Redirecting %s because "
444 "parent %s is dfs link\n", dfspath, localpath));
446 if (consumedcntp) {
447 *consumedcntp = strlen(canon_dfspath);
448 DEBUG(10, ("dfs_path_lookup: Path consumed: %s "
449 "(%d)\n", canon_dfspath, *consumedcntp));
452 return NT_STATUS_PATH_NOT_COVERED;
455 /* Step back on the filesystem. */
456 p = strrchr_m(localpath, '/');
458 if (consumedcntp) {
459 /* And in the canonicalized dfs path. */
460 q = strrchr_m(canon_dfspath, '/');
464 return NT_STATUS_OK;
467 /*****************************************************************
468 Decides if a dfs pathname should be redirected or not.
469 If not, the pathname is converted to a tcon-relative local unix path
471 search_wcard_flag: this flag performs 2 functions bother related
472 to searches. See resolve_dfs_path() and parse_dfs_path_XX()
473 for details.
475 This function can return NT_STATUS_OK, meaning use the returned path as-is
476 (mapped into a local path).
477 or NT_STATUS_NOT_COVERED meaning return a DFS redirect, or
478 any other NT_STATUS error which is a genuine error to be
479 returned to the client.
480 *****************************************************************/
482 static NTSTATUS dfs_redirect( connection_struct *conn,
483 pstring dfs_path,
484 BOOL search_wcard_flag,
485 BOOL *ppath_contains_wcard)
487 NTSTATUS status;
488 struct dfs_path dp;
489 pstring targetpath;
491 status = parse_dfs_path(dfs_path, search_wcard_flag, &dp, ppath_contains_wcard);
492 if (!NT_STATUS_IS_OK(status)) {
493 return status;
496 if (dp.reqpath[0] == '\0') {
497 pstrcpy(dfs_path, dp.reqpath);
498 DEBUG(5,("dfs_redirect: self-referral.\n"));
499 return NT_STATUS_OK;
502 /* If dfs pathname for a non-dfs share, convert to tcon-relative
503 path and return OK */
505 if (!lp_msdfs_root(SNUM(conn))) {
506 pstrcpy(dfs_path, dp.reqpath);
507 return NT_STATUS_OK;
510 /* If it looked like a local path (zero hostname/servicename)
511 * just treat as a tcon-relative path. */
513 if (dp.hostname[0] == '\0' && dp.servicename[0] == '\0') {
514 pstrcpy(dfs_path, dp.reqpath);
515 return NT_STATUS_OK;
518 if (!( strequal(dp.servicename, lp_servicename(SNUM(conn)))
519 || (strequal(dp.servicename, HOMES_NAME)
520 && strequal(lp_servicename(SNUM(conn)), get_current_username()) )) ) {
522 /* The given sharename doesn't match this connection. */
524 return NT_STATUS_OBJECT_PATH_NOT_FOUND;
527 status = dfs_path_lookup(conn, dfs_path, &dp,
528 search_wcard_flag, NULL, targetpath);
529 if (!NT_STATUS_IS_OK(status)) {
530 if (NT_STATUS_EQUAL(status, NT_STATUS_PATH_NOT_COVERED)) {
531 DEBUG(3,("dfs_redirect: Redirecting %s\n", dfs_path));
532 } else {
533 DEBUG(10,("dfs_redirect: dfs_path_lookup failed for %s with %s\n",
534 dfs_path, nt_errstr(status) ));
536 return status;
539 DEBUG(3,("dfs_redirect: Not redirecting %s.\n", dfs_path));
541 /* Form non-dfs tcon-relative path */
542 pstrcpy(dfs_path, dp.reqpath);
544 DEBUG(3,("dfs_redirect: Path converted to non-dfs path %s\n", dfs_path));
545 return NT_STATUS_OK;
548 /**********************************************************************
549 Return a self referral.
550 **********************************************************************/
552 static NTSTATUS self_ref(TALLOC_CTX *ctx,
553 const char *dfs_path,
554 struct junction_map *jucn,
555 int *consumedcntp,
556 BOOL *self_referralp)
558 struct referral *ref;
560 *self_referralp = True;
562 jucn->referral_count = 1;
563 if((ref = TALLOC_ZERO_P(ctx, struct referral)) == NULL) {
564 DEBUG(0,("self_ref: talloc failed for referral\n"));
565 return NT_STATUS_NO_MEMORY;
568 pstrcpy(ref->alternate_path,dfs_path);
569 ref->proximity = 0;
570 ref->ttl = REFERRAL_TTL;
571 jucn->referral_list = ref;
572 *consumedcntp = strlen(dfs_path);
573 return NT_STATUS_OK;
576 /**********************************************************************
577 Gets valid referrals for a dfs path and fills up the
578 junction_map structure.
579 **********************************************************************/
581 NTSTATUS get_referred_path(TALLOC_CTX *ctx,
582 const char *dfs_path,
583 struct junction_map *jucn,
584 int *consumedcntp,
585 BOOL *self_referralp)
587 struct connection_struct conns;
588 struct connection_struct *conn = &conns;
589 struct dfs_path dp;
590 pstring conn_path;
591 pstring targetpath;
592 int snum;
593 NTSTATUS status = NT_STATUS_NOT_FOUND;
594 BOOL dummy;
596 ZERO_STRUCT(conns);
598 *self_referralp = False;
600 status = parse_dfs_path(dfs_path, False, &dp, &dummy);
601 if (!NT_STATUS_IS_OK(status)) {
602 return status;
605 /* Verify hostname in path */
606 if (!is_myname_or_ipaddr(dp.hostname)) {
607 DEBUG(3, ("get_referred_path: Invalid hostname %s in path %s\n",
608 dp.hostname, dfs_path));
609 return NT_STATUS_NOT_FOUND;
612 fstrcpy(jucn->service_name, dp.servicename);
613 pstrcpy(jucn->volume_name, dp.reqpath);
615 /* Verify the share is a dfs root */
616 snum = lp_servicenumber(jucn->service_name);
617 if(snum < 0) {
618 if ((snum = find_service(jucn->service_name)) < 0) {
619 return NT_STATUS_NOT_FOUND;
623 if (!lp_msdfs_root(snum)) {
624 DEBUG(3,("get_referred_path: |%s| in dfs path %s is not a dfs root.\n",
625 dp.servicename, dfs_path));
626 return NT_STATUS_NOT_FOUND;
630 * Self referrals are tested with a anonymous IPC connection and
631 * a GET_DFS_REFERRAL call to \\server\share. (which means dp.reqpath[0] points
632 * to an empty string). create_conn_struct cd's into the directory and will
633 * fail if it cannot (as the anonymous user). Cope with this.
636 if (dp.reqpath[0] == '\0') {
637 struct referral *ref;
639 if (*lp_msdfs_proxy(snum) == '\0') {
640 return self_ref(ctx,
641 dfs_path,
642 jucn,
643 consumedcntp,
644 self_referralp);
648 * It's an msdfs proxy share. Redirect to
649 * the configured target share.
652 jucn->referral_count = 1;
653 if ((ref = TALLOC_ZERO_P(ctx, struct referral)) == NULL) {
654 DEBUG(0, ("malloc failed for referral\n"));
655 return NT_STATUS_NO_MEMORY;
658 pstrcpy(ref->alternate_path, lp_msdfs_proxy(snum));
659 if (dp.reqpath[0] != '\0') {
660 pstrcat(ref->alternate_path, dp.reqpath);
662 ref->proximity = 0;
663 ref->ttl = REFERRAL_TTL;
664 jucn->referral_list = ref;
665 *consumedcntp = strlen(dfs_path);
666 return NT_STATUS_OK;
669 pstrcpy(conn_path, lp_pathname(snum));
670 status = create_conn_struct(conn, snum, conn_path);
671 if (!NT_STATUS_IS_OK(status)) {
672 return status;
675 /* If this is a DFS path dfs_lookup should return
676 * NT_STATUS_PATH_NOT_COVERED. */
678 status = dfs_path_lookup(conn, dfs_path, &dp,
679 False, consumedcntp, targetpath);
681 if (!NT_STATUS_EQUAL(status, NT_STATUS_PATH_NOT_COVERED)) {
682 DEBUG(3,("get_referred_path: No valid referrals for path %s\n",
683 dfs_path));
684 conn_free_internal(conn);
685 return status;
688 /* We know this is a valid dfs link. Parse the targetpath. */
689 if (!parse_msdfs_symlink(ctx, targetpath,
690 &jucn->referral_list,
691 &jucn->referral_count)) {
692 DEBUG(3,("get_referred_path: failed to parse symlink "
693 "target %s\n", targetpath ));
694 conn_free_internal(conn);
695 return NT_STATUS_NOT_FOUND;
698 conn_free_internal(conn);
699 return NT_STATUS_OK;
702 static int setup_ver2_dfs_referral(const char *pathname,
703 char **ppdata,
704 struct junction_map *junction,
705 int consumedcnt,
706 BOOL self_referral)
708 char* pdata = *ppdata;
710 unsigned char uni_requestedpath[1024];
711 int uni_reqpathoffset1,uni_reqpathoffset2;
712 int uni_curroffset;
713 int requestedpathlen=0;
714 int offset;
715 int reply_size = 0;
716 int i=0;
718 DEBUG(10,("Setting up version2 referral\nRequested path:\n"));
720 requestedpathlen = rpcstr_push(uni_requestedpath, pathname, sizeof(pstring),
721 STR_TERMINATE);
723 if (DEBUGLVL(10)) {
724 dump_data(0, uni_requestedpath,requestedpathlen);
727 DEBUG(10,("ref count = %u\n",junction->referral_count));
729 uni_reqpathoffset1 = REFERRAL_HEADER_SIZE +
730 VERSION2_REFERRAL_SIZE * junction->referral_count;
732 uni_reqpathoffset2 = uni_reqpathoffset1 + requestedpathlen;
734 uni_curroffset = uni_reqpathoffset2 + requestedpathlen;
736 reply_size = REFERRAL_HEADER_SIZE + VERSION2_REFERRAL_SIZE*junction->referral_count +
737 2 * requestedpathlen;
738 DEBUG(10,("reply_size: %u\n",reply_size));
740 /* add up the unicode lengths of all the referral paths */
741 for(i=0;i<junction->referral_count;i++) {
742 DEBUG(10,("referral %u : %s\n",i,junction->referral_list[i].alternate_path));
743 reply_size += (strlen(junction->referral_list[i].alternate_path)+1)*2;
746 DEBUG(10,("reply_size = %u\n",reply_size));
747 /* add the unexplained 0x16 bytes */
748 reply_size += 0x16;
750 pdata = (char *)SMB_REALLOC(pdata,reply_size);
751 if(pdata == NULL) {
752 DEBUG(0,("Realloc failed!\n"));
753 return -1;
755 *ppdata = pdata;
757 /* copy in the dfs requested paths.. required for offset calculations */
758 memcpy(pdata+uni_reqpathoffset1,uni_requestedpath,requestedpathlen);
759 memcpy(pdata+uni_reqpathoffset2,uni_requestedpath,requestedpathlen);
761 /* create the header */
762 SSVAL(pdata,0,consumedcnt * 2); /* path consumed */
763 SSVAL(pdata,2,junction->referral_count); /* number of referral in this pkt */
764 if(self_referral) {
765 SIVAL(pdata,4,DFSREF_REFERRAL_SERVER | DFSREF_STORAGE_SERVER);
766 } else {
767 SIVAL(pdata,4,DFSREF_STORAGE_SERVER);
770 offset = 8;
771 /* add the referral elements */
772 for(i=0;i<junction->referral_count;i++) {
773 struct referral* ref = &junction->referral_list[i];
774 int unilen;
776 SSVAL(pdata,offset,2); /* version 2 */
777 SSVAL(pdata,offset+2,VERSION2_REFERRAL_SIZE);
778 if(self_referral) {
779 SSVAL(pdata,offset+4,1);
780 } else {
781 SSVAL(pdata,offset+4,0);
783 SSVAL(pdata,offset+6,0); /* ref_flags :use path_consumed bytes? */
784 SIVAL(pdata,offset+8,ref->proximity);
785 SIVAL(pdata,offset+12,ref->ttl);
787 SSVAL(pdata,offset+16,uni_reqpathoffset1-offset);
788 SSVAL(pdata,offset+18,uni_reqpathoffset2-offset);
789 /* copy referred path into current offset */
790 unilen = rpcstr_push(pdata+uni_curroffset, ref->alternate_path,
791 sizeof(pstring), STR_UNICODE);
793 SSVAL(pdata,offset+20,uni_curroffset-offset);
795 uni_curroffset += unilen;
796 offset += VERSION2_REFERRAL_SIZE;
798 /* add in the unexplained 22 (0x16) bytes at the end */
799 memset(pdata+uni_curroffset,'\0',0x16);
800 return reply_size;
803 static int setup_ver3_dfs_referral(const char *pathname,
804 char **ppdata,
805 struct junction_map *junction,
806 int consumedcnt,
807 BOOL self_referral)
809 char* pdata = *ppdata;
811 unsigned char uni_reqpath[1024];
812 int uni_reqpathoffset1, uni_reqpathoffset2;
813 int uni_curroffset;
814 int reply_size = 0;
816 int reqpathlen = 0;
817 int offset,i=0;
819 DEBUG(10,("setting up version3 referral\n"));
821 reqpathlen = rpcstr_push(uni_reqpath, pathname, sizeof(pstring), STR_TERMINATE);
823 if (DEBUGLVL(10)) {
824 dump_data(0, uni_reqpath,reqpathlen);
827 uni_reqpathoffset1 = REFERRAL_HEADER_SIZE + VERSION3_REFERRAL_SIZE * junction->referral_count;
828 uni_reqpathoffset2 = uni_reqpathoffset1 + reqpathlen;
829 reply_size = uni_curroffset = uni_reqpathoffset2 + reqpathlen;
831 for(i=0;i<junction->referral_count;i++) {
832 DEBUG(10,("referral %u : %s\n",i,junction->referral_list[i].alternate_path));
833 reply_size += (strlen(junction->referral_list[i].alternate_path)+1)*2;
836 pdata = (char *)SMB_REALLOC(pdata,reply_size);
837 if(pdata == NULL) {
838 DEBUG(0,("version3 referral setup: malloc failed for Realloc!\n"));
839 return -1;
841 *ppdata = pdata;
843 /* create the header */
844 SSVAL(pdata,0,consumedcnt * 2); /* path consumed */
845 SSVAL(pdata,2,junction->referral_count); /* number of referral */
846 if(self_referral) {
847 SIVAL(pdata,4,DFSREF_REFERRAL_SERVER | DFSREF_STORAGE_SERVER);
848 } else {
849 SIVAL(pdata,4,DFSREF_STORAGE_SERVER);
852 /* copy in the reqpaths */
853 memcpy(pdata+uni_reqpathoffset1,uni_reqpath,reqpathlen);
854 memcpy(pdata+uni_reqpathoffset2,uni_reqpath,reqpathlen);
856 offset = 8;
857 for(i=0;i<junction->referral_count;i++) {
858 struct referral* ref = &(junction->referral_list[i]);
859 int unilen;
861 SSVAL(pdata,offset,3); /* version 3 */
862 SSVAL(pdata,offset+2,VERSION3_REFERRAL_SIZE);
863 if(self_referral) {
864 SSVAL(pdata,offset+4,1);
865 } else {
866 SSVAL(pdata,offset+4,0);
869 SSVAL(pdata,offset+6,0); /* ref_flags :use path_consumed bytes? */
870 SIVAL(pdata,offset+8,ref->ttl);
872 SSVAL(pdata,offset+12,uni_reqpathoffset1-offset);
873 SSVAL(pdata,offset+14,uni_reqpathoffset2-offset);
874 /* copy referred path into current offset */
875 unilen = rpcstr_push(pdata+uni_curroffset,ref->alternate_path,
876 sizeof(pstring), STR_UNICODE | STR_TERMINATE);
877 SSVAL(pdata,offset+16,uni_curroffset-offset);
878 /* copy 0x10 bytes of 00's in the ServiceSite GUID */
879 memset(pdata+offset+18,'\0',16);
881 uni_curroffset += unilen;
882 offset += VERSION3_REFERRAL_SIZE;
884 return reply_size;
887 /******************************************************************
888 Set up the DFS referral for the dfs pathname. This call returns
889 the amount of the path covered by this server, and where the
890 client should be redirected to. This is the meat of the
891 TRANS2_GET_DFS_REFERRAL call.
892 ******************************************************************/
894 int setup_dfs_referral(connection_struct *orig_conn,
895 const char *dfs_path,
896 int max_referral_level,
897 char **ppdata, NTSTATUS *pstatus)
899 struct junction_map junction;
900 int consumedcnt = 0;
901 BOOL self_referral = False;
902 int reply_size = 0;
903 char *pathnamep = NULL;
904 pstring local_dfs_path;
905 TALLOC_CTX *ctx;
907 if (!(ctx=talloc_init("setup_dfs_referral"))) {
908 *pstatus = NT_STATUS_NO_MEMORY;
909 return -1;
912 ZERO_STRUCT(junction);
914 /* get the junction entry */
915 if (!dfs_path) {
916 talloc_destroy(ctx);
917 *pstatus = NT_STATUS_NOT_FOUND;
918 return -1;
922 * Trim pathname sent by client so it begins with only one backslash.
923 * Two backslashes confuse some dfs clients
926 pstrcpy(local_dfs_path, dfs_path);
927 pathnamep = local_dfs_path;
928 while (IS_DIRECTORY_SEP(pathnamep[0]) && IS_DIRECTORY_SEP(pathnamep[1])) {
929 pathnamep++;
932 /* The following call can change cwd. */
933 *pstatus = get_referred_path(ctx, pathnamep, &junction, &consumedcnt, &self_referral);
934 if (!NT_STATUS_IS_OK(*pstatus)) {
935 vfs_ChDir(orig_conn,orig_conn->connectpath);
936 talloc_destroy(ctx);
937 return -1;
939 vfs_ChDir(orig_conn,orig_conn->connectpath);
941 if (!self_referral) {
942 pathnamep[consumedcnt] = '\0';
944 if( DEBUGLVL( 3 ) ) {
945 int i=0;
946 dbgtext("setup_dfs_referral: Path %s to alternate path(s):",pathnamep);
947 for(i=0;i<junction.referral_count;i++)
948 dbgtext(" %s",junction.referral_list[i].alternate_path);
949 dbgtext(".\n");
953 /* create the referral depeding on version */
954 DEBUG(10,("max_referral_level :%d\n",max_referral_level));
956 if (max_referral_level < 2) {
957 max_referral_level = 2;
959 if (max_referral_level > 3) {
960 max_referral_level = 3;
963 switch(max_referral_level) {
964 case 2:
965 reply_size = setup_ver2_dfs_referral(pathnamep, ppdata, &junction,
966 consumedcnt, self_referral);
967 break;
968 case 3:
969 reply_size = setup_ver3_dfs_referral(pathnamep, ppdata, &junction,
970 consumedcnt, self_referral);
971 break;
972 default:
973 DEBUG(0,("setup_dfs_referral: Invalid dfs referral version: %d\n", max_referral_level));
974 talloc_destroy(ctx);
975 *pstatus = NT_STATUS_INVALID_LEVEL;
976 return -1;
979 if (DEBUGLVL(10)) {
980 DEBUGADD(0,("DFS Referral pdata:\n"));
981 dump_data(0,(uint8 *)*ppdata,reply_size);
984 talloc_destroy(ctx);
985 *pstatus = NT_STATUS_OK;
986 return reply_size;
989 /**********************************************************************
990 The following functions are called by the NETDFS RPC pipe functions
991 **********************************************************************/
993 /*********************************************************************
994 Creates a junction structure from a DFS pathname
995 **********************************************************************/
997 BOOL create_junction(const char *dfs_path, struct junction_map *jucn)
999 int snum;
1000 BOOL dummy;
1001 struct dfs_path dp;
1003 NTSTATUS status = parse_dfs_path(dfs_path, False, &dp, &dummy);
1005 if (!NT_STATUS_IS_OK(status)) {
1006 return False;
1009 /* check if path is dfs : validate first token */
1010 if (!is_myname_or_ipaddr(dp.hostname)) {
1011 DEBUG(4,("create_junction: Invalid hostname %s in dfs path %s\n",
1012 dp.hostname, dfs_path));
1013 return False;
1016 /* Check for a non-DFS share */
1017 snum = lp_servicenumber(dp.servicename);
1019 if(snum < 0 || !lp_msdfs_root(snum)) {
1020 DEBUG(4,("create_junction: %s is not an msdfs root.\n",
1021 dp.servicename));
1022 return False;
1025 fstrcpy(jucn->service_name,dp.servicename);
1026 pstrcpy(jucn->volume_name,dp.reqpath);
1027 pstrcpy(jucn->comment, lp_comment(snum));
1028 return True;
1031 /**********************************************************************
1032 Forms a valid Unix pathname from the junction
1033 **********************************************************************/
1035 static BOOL junction_to_local_path(struct junction_map *jucn,
1036 char *path,
1037 int max_pathlen,
1038 connection_struct *conn_out)
1040 int snum;
1041 pstring conn_path;
1043 snum = lp_servicenumber(jucn->service_name);
1044 if(snum < 0) {
1045 return False;
1048 safe_strcpy(path, lp_pathname(snum), max_pathlen-1);
1049 safe_strcat(path, "/", max_pathlen-1);
1050 safe_strcat(path, jucn->volume_name, max_pathlen-1);
1052 pstrcpy(conn_path, lp_pathname(snum));
1053 if (!NT_STATUS_IS_OK(create_conn_struct(conn_out, snum, conn_path))) {
1054 return False;
1057 return True;
1060 BOOL create_msdfs_link(struct junction_map *jucn, BOOL exists)
1062 pstring path;
1063 pstring msdfs_link;
1064 connection_struct conns;
1065 connection_struct *conn = &conns;
1066 int i=0;
1067 BOOL insert_comma = False;
1068 BOOL ret = False;
1070 ZERO_STRUCT(conns);
1072 if(!junction_to_local_path(jucn, path, sizeof(path), conn)) {
1073 return False;
1076 /* Form the msdfs_link contents */
1077 pstrcpy(msdfs_link, "msdfs:");
1078 for(i=0; i<jucn->referral_count; i++) {
1079 char* refpath = jucn->referral_list[i].alternate_path;
1081 /* Alternate paths always use Windows separators. */
1082 trim_char(refpath, '\\', '\\');
1083 if(*refpath == '\0') {
1084 if (i == 0) {
1085 insert_comma = False;
1087 continue;
1089 if (i > 0 && insert_comma) {
1090 pstrcat(msdfs_link, ",");
1093 pstrcat(msdfs_link, refpath);
1094 if (!insert_comma) {
1095 insert_comma = True;
1099 DEBUG(5,("create_msdfs_link: Creating new msdfs link: %s -> %s\n",
1100 path, msdfs_link));
1102 if(exists) {
1103 if(SMB_VFS_UNLINK(conn,path)!=0) {
1104 goto out;
1108 if(SMB_VFS_SYMLINK(conn, msdfs_link, path) < 0) {
1109 DEBUG(1,("create_msdfs_link: symlink failed %s -> %s\nError: %s\n",
1110 path, msdfs_link, strerror(errno)));
1111 goto out;
1115 ret = True;
1117 out:
1119 conn_free_internal(conn);
1120 return ret;
1123 BOOL remove_msdfs_link(struct junction_map *jucn)
1125 pstring path;
1126 connection_struct conns;
1127 connection_struct *conn = &conns;
1128 BOOL ret = False;
1130 ZERO_STRUCT(conns);
1132 if( junction_to_local_path(jucn, path, sizeof(path), conn) ) {
1133 if( SMB_VFS_UNLINK(conn, path) == 0 ) {
1134 ret = True;
1136 talloc_destroy( conn->mem_ctx );
1139 conn_free_internal(conn);
1140 return ret;
1143 static int form_junctions(TALLOC_CTX *ctx,
1144 int snum,
1145 struct junction_map *jucn,
1146 int jn_remain)
1148 int cnt = 0;
1149 SMB_STRUCT_DIR *dirp;
1150 char *dname;
1151 pstring connect_path;
1152 char *service_name = lp_servicename(snum);
1153 connection_struct conn;
1154 struct referral *ref = NULL;
1156 ZERO_STRUCT(conn);
1158 if (jn_remain <= 0) {
1159 return 0;
1162 pstrcpy(connect_path,lp_pathname(snum));
1164 if(*connect_path == '\0') {
1165 return 0;
1169 * Fake up a connection struct for the VFS layer.
1172 if (!NT_STATUS_IS_OK(create_conn_struct(&conn, snum, connect_path))) {
1173 return 0;
1176 /* form a junction for the msdfs root - convention
1177 DO NOT REMOVE THIS: NT clients will not work with us
1178 if this is not present
1180 fstrcpy(jucn[cnt].service_name, service_name);
1181 jucn[cnt].volume_name[0] = '\0';
1182 jucn[cnt].referral_count = 1;
1184 ref = jucn[cnt].referral_list = TALLOC_ZERO_P(ctx, struct referral);
1185 if (jucn[cnt].referral_list == NULL) {
1186 DEBUG(0, ("talloc failed!\n"));
1187 goto out;
1190 ref->proximity = 0;
1191 ref->ttl = REFERRAL_TTL;
1192 if (*lp_msdfs_proxy(snum) != '\0') {
1193 pstrcpy(ref->alternate_path, lp_msdfs_proxy(snum));
1194 cnt++;
1195 goto out;
1198 pstr_sprintf(ref->alternate_path, "\\\\%s\\%s",
1199 get_local_machine_name(),
1200 service_name);
1201 cnt++;
1203 /* Now enumerate all dfs links */
1204 dirp = SMB_VFS_OPENDIR(&conn, ".", NULL, 0);
1205 if(!dirp) {
1206 goto out;
1209 while ((dname = vfs_readdirname(&conn, dirp)) != NULL) {
1210 pstring link_target;
1211 if (cnt >= jn_remain) {
1212 SMB_VFS_CLOSEDIR(&conn,dirp);
1213 DEBUG(2, ("ran out of MSDFS junction slots"));
1214 goto out;
1216 if (is_msdfs_link(&conn, dname, link_target, NULL)) {
1217 if (parse_msdfs_symlink(ctx,
1218 link_target,
1219 &jucn[cnt].referral_list,
1220 &jucn[cnt].referral_count)) {
1222 fstrcpy(jucn[cnt].service_name, service_name);
1223 pstrcpy(jucn[cnt].volume_name, dname);
1224 cnt++;
1229 SMB_VFS_CLOSEDIR(&conn,dirp);
1231 out:
1233 conn_free_internal(&conn);
1234 return cnt;
1237 int enum_msdfs_links(TALLOC_CTX *ctx, struct junction_map *jucn, int jn_max)
1239 int i=0;
1240 int sharecount = 0;
1241 int jn_count = 0;
1243 if(!lp_host_msdfs()) {
1244 return 0;
1247 /* Ensure all the usershares are loaded. */
1248 become_root();
1249 load_registry_shares();
1250 sharecount = load_usershare_shares();
1251 unbecome_root();
1253 for(i=0;i < sharecount && (jn_max - jn_count) > 0;i++) {
1254 if(lp_msdfs_root(i)) {
1255 jn_count += form_junctions(ctx, i,jucn,jn_max - jn_count);
1258 return jn_count;
1261 /******************************************************************************
1262 Core function to resolve a dfs pathname.
1263 ******************************************************************************/
1265 NTSTATUS resolve_dfspath(connection_struct *conn, BOOL dfs_pathnames, pstring name)
1267 NTSTATUS status = NT_STATUS_OK;
1268 BOOL dummy;
1269 if (dfs_pathnames) {
1270 status = dfs_redirect(conn, name, False, &dummy);
1272 return status;
1275 /******************************************************************************
1276 Core function to resolve a dfs pathname possibly containing a wildcard.
1277 This function is identical to the above except for the BOOL param to
1278 dfs_redirect but I need this to be separate so it's really clear when
1279 we're allowing wildcards and when we're not. JRA.
1280 ******************************************************************************/
1282 NTSTATUS resolve_dfspath_wcard(connection_struct *conn, BOOL dfs_pathnames, pstring name, BOOL *ppath_contains_wcard)
1284 NTSTATUS status = NT_STATUS_OK;
1285 if (dfs_pathnames) {
1286 status = dfs_redirect(conn, name, True, ppath_contains_wcard);
1288 return status;