source: branches/samba-3.3.x/source/smbd/msdfs.c@ 407

Last change on this file since 407 was 274, checked in by Herwig Bauernfeind, 16 years ago

Update 3.3 branch to 3.3.5

File size: 43.8 KB
Line 
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
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.
12
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.
17
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/>.
20
21*/
22
23#define DBGC_CLASS DBGC_MSDFS
24#include "includes.h"
25
26extern uint32 global_client_caps;
27
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.
34
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....
38
39 If conn != NULL then ensure the provided service is
40 the one pointed to by the connection.
41
42 This version does everything using pointers within one copy of the
43 pathname string, talloced on the struct dfs_path pointer (which
44 must be talloced). This may be too clever to live....
45 JRA.
46**********************************************************************/
47
48static NTSTATUS parse_dfs_path(connection_struct *conn,
49 const char *pathname,
50 bool allow_wcards,
51 struct dfs_path *pdp, /* MUST BE TALLOCED */
52 bool *ppath_contains_wcard)
53{
54 char *pathname_local;
55 char *p,*temp;
56 char *servicename;
57 char *eos_ptr;
58 NTSTATUS status = NT_STATUS_OK;
59 char sepchar;
60
61 ZERO_STRUCTP(pdp);
62
63 /*
64 * This is the only talloc we should need to do
65 * on the struct dfs_path. All the pointers inside
66 * it should point to offsets within this string.
67 */
68
69 pathname_local = talloc_strdup(pdp, pathname);
70 if (!pathname_local) {
71 return NT_STATUS_NO_MEMORY;
72 }
73 /* Get a pointer to the terminating '\0' */
74 eos_ptr = &pathname_local[strlen(pathname_local)];
75 p = temp = pathname_local;
76
77 pdp->posix_path = (lp_posix_pathnames() && *pathname == '/');
78
79 sepchar = pdp->posix_path ? '/' : '\\';
80
81 if (*pathname != sepchar) {
82 DEBUG(10,("parse_dfs_path: path %s doesn't start with %c\n",
83 pathname, sepchar ));
84 /*
85 * Possibly client sent a local path by mistake.
86 * Try and convert to a local path.
87 */
88
89 pdp->hostname = eos_ptr; /* "" */
90 pdp->servicename = eos_ptr; /* "" */
91
92 /* We've got no info about separators. */
93 pdp->posix_path = lp_posix_pathnames();
94 p = temp;
95 DEBUG(10,("parse_dfs_path: trying to convert %s to a "
96 "local path\n",
97 temp));
98 goto local_path;
99 }
100
101 /*
102 * Safe to use on talloc'ed string as it only shrinks.
103 * It also doesn't affect the eos_ptr.
104 */
105 trim_char(temp,sepchar,sepchar);
106
107 DEBUG(10,("parse_dfs_path: temp = |%s| after trimming %c's\n",
108 temp, sepchar));
109
110 /* Now tokenize. */
111 /* Parse out hostname. */
112 p = strchr_m(temp,sepchar);
113 if(p == NULL) {
114 DEBUG(10,("parse_dfs_path: can't parse hostname from path %s\n",
115 temp));
116 /*
117 * Possibly client sent a local path by mistake.
118 * Try and convert to a local path.
119 */
120
121 pdp->hostname = eos_ptr; /* "" */
122 pdp->servicename = eos_ptr; /* "" */
123
124 p = temp;
125 DEBUG(10,("parse_dfs_path: trying to convert %s "
126 "to a local path\n",
127 temp));
128 goto local_path;
129 }
130 *p = '\0';
131 pdp->hostname = temp;
132
133 DEBUG(10,("parse_dfs_path: hostname: %s\n",pdp->hostname));
134
135 /* Parse out servicename. */
136 servicename = p+1;
137 p = strchr_m(servicename,sepchar);
138 if (p) {
139 *p = '\0';
140 }
141
142 /* Is this really our servicename ? */
143 if (conn && !( strequal(servicename, lp_servicename(SNUM(conn)))
144 || (strequal(servicename, HOMES_NAME)
145 && strequal(lp_servicename(SNUM(conn)),
146 get_current_username()) )) ) {
147 DEBUG(10,("parse_dfs_path: %s is not our servicename\n",
148 servicename));
149
150 /*
151 * Possibly client sent a local path by mistake.
152 * Try and convert to a local path.
153 */
154
155 pdp->hostname = eos_ptr; /* "" */
156 pdp->servicename = eos_ptr; /* "" */
157
158 /* Repair the path - replace the sepchar's
159 we nulled out */
160 servicename--;
161 *servicename = sepchar;
162 if (p) {
163 *p = sepchar;
164 }
165
166 p = temp;
167 DEBUG(10,("parse_dfs_path: trying to convert %s "
168 "to a local path\n",
169 temp));
170 goto local_path;
171 }
172
173 pdp->servicename = servicename;
174
175 DEBUG(10,("parse_dfs_path: servicename: %s\n",pdp->servicename));
176
177 if(p == NULL) {
178 /* Client sent self referral \server\share. */
179 pdp->reqpath = eos_ptr; /* "" */
180 return NT_STATUS_OK;
181 }
182
183 p++;
184
185 local_path:
186
187 *ppath_contains_wcard = False;
188
189 pdp->reqpath = p;
190
191 /* Rest is reqpath. */
192 if (pdp->posix_path) {
193 status = check_path_syntax_posix(pdp->reqpath);
194 } else {
195 if (allow_wcards) {
196 status = check_path_syntax_wcard(pdp->reqpath,
197 ppath_contains_wcard);
198 } else {
199 status = check_path_syntax(pdp->reqpath);
200 }
201 }
202
203 if (!NT_STATUS_IS_OK(status)) {
204 DEBUG(10,("parse_dfs_path: '%s' failed with %s\n",
205 p, nt_errstr(status) ));
206 return status;
207 }
208
209 DEBUG(10,("parse_dfs_path: rest of the path: %s\n",pdp->reqpath));
210 return NT_STATUS_OK;
211}
212
213/********************************************************
214 Fake up a connection struct for the VFS layer.
215 Note this CHANGES CWD !!!! JRA.
216*********************************************************/
217
218NTSTATUS create_conn_struct(TALLOC_CTX *ctx,
219 connection_struct **pconn,
220 int snum,
221 const char *path,
222 char **poldcwd)
223{
224 connection_struct *conn;
225 char *connpath;
226 char *oldcwd;
227
228 conn = TALLOC_ZERO_P(ctx, connection_struct);
229 if (conn == NULL) {
230 return NT_STATUS_NO_MEMORY;
231 }
232
233 connpath = talloc_strdup(conn, path);
234 if (!connpath) {
235 TALLOC_FREE(conn);
236 return NT_STATUS_NO_MEMORY;
237 }
238 connpath = talloc_string_sub(conn,
239 connpath,
240 "%S",
241 lp_servicename(snum));
242 if (!connpath) {
243 TALLOC_FREE(conn);
244 return NT_STATUS_NO_MEMORY;
245 }
246
247 /* needed for smbd_vfs_init() */
248
249 if (!(conn->params = TALLOC_ZERO_P(conn, struct share_params))) {
250 DEBUG(0, ("TALLOC failed\n"));
251 TALLOC_FREE(conn);
252 return NT_STATUS_NO_MEMORY;
253 }
254
255 conn->params->service = snum;
256
257 set_conn_connectpath(conn, connpath);
258
259 if (!smbd_vfs_init(conn)) {
260 NTSTATUS status = map_nt_error_from_unix(errno);
261 DEBUG(0,("create_conn_struct: smbd_vfs_init failed.\n"));
262 conn_free_internal(conn);
263 return status;
264 }
265
266 conn->fs_capabilities = SMB_VFS_FS_CAPABILITIES(conn);
267
268 /*
269 * Windows seems to insist on doing trans2getdfsreferral() calls on
270 * the IPC$ share as the anonymous user. If we try to chdir as that
271 * user we will fail.... WTF ? JRA.
272 */
273
274 oldcwd = vfs_GetWd(ctx, conn);
275 if (oldcwd == NULL) {
276 NTSTATUS status = map_nt_error_from_unix(errno);
277 DEBUG(3, ("vfs_GetWd failed: %s\n", strerror(errno)));
278 conn_free_internal(conn);
279 return status;
280 }
281
282 if (vfs_ChDir(conn,conn->connectpath) != 0) {
283 NTSTATUS status = map_nt_error_from_unix(errno);
284 DEBUG(3,("create_conn_struct: Can't ChDir to new conn path %s. "
285 "Error was %s\n",
286 conn->connectpath, strerror(errno) ));
287 conn_free_internal(conn);
288 return status;
289 }
290
291 *pconn = conn;
292 *poldcwd = oldcwd;
293
294 return NT_STATUS_OK;
295}
296
297/**********************************************************************
298 Parse the contents of a symlink to verify if it is an msdfs referral
299 A valid referral is of the form:
300
301 msdfs:server1\share1,server2\share2
302 msdfs:server1\share1\pathname,server2\share2\pathname
303 msdfs:server1/share1,server2/share2
304 msdfs:server1/share1/pathname,server2/share2/pathname.
305
306 Note that the alternate paths returned here must be of the canonicalized
307 form:
308
309 \server\share or
310 \server\share\path\to\file,
311
312 even in posix path mode. This is because we have no knowledge if the
313 server we're referring to understands posix paths.
314 **********************************************************************/
315
316static bool parse_msdfs_symlink(TALLOC_CTX *ctx,
317 const char *target,
318 struct referral **preflist,
319 int *refcount)
320{
321 char *temp = NULL;
322 char *prot;
323 char **alt_path = NULL;
324 int count = 0, i;
325 struct referral *reflist;
326 char *saveptr;
327
328 temp = talloc_strdup(ctx, target);
329 if (!temp) {
330 return False;
331 }
332 prot = strtok_r(temp, ":", &saveptr);
333 if (!prot) {
334 DEBUG(0,("parse_msdfs_symlink: invalid path !\n"));
335 return False;
336 }
337
338 alt_path = TALLOC_ARRAY(ctx, char *, MAX_REFERRAL_COUNT);
339 if (!alt_path) {
340 return False;
341 }
342
343 /* parse out the alternate paths */
344 while((count<MAX_REFERRAL_COUNT) &&
345 ((alt_path[count] = strtok_r(NULL, ",", &saveptr)) != NULL)) {
346 count++;
347 }
348
349 DEBUG(10,("parse_msdfs_symlink: count=%d\n", count));
350
351 if (count) {
352 reflist = *preflist = TALLOC_ZERO_ARRAY(ctx,
353 struct referral, count);
354 if(reflist == NULL) {
355 TALLOC_FREE(alt_path);
356 return False;
357 }
358 } else {
359 reflist = *preflist = NULL;
360 }
361
362 for(i=0;i<count;i++) {
363 char *p;
364
365 /* Canonicalize link target.
366 * Replace all /'s in the path by a \ */
367 string_replace(alt_path[i], '/', '\\');
368
369 /* Remove leading '\\'s */
370 p = alt_path[i];
371 while (*p && (*p == '\\')) {
372 p++;
373 }
374
375 reflist[i].alternate_path = talloc_asprintf(ctx,
376 "\\%s",
377 p);
378 if (!reflist[i].alternate_path) {
379 return False;
380 }
381
382 reflist[i].proximity = 0;
383 reflist[i].ttl = REFERRAL_TTL;
384 DEBUG(10, ("parse_msdfs_symlink: Created alt path: %s\n",
385 reflist[i].alternate_path));
386 }
387
388 *refcount = count;
389
390 TALLOC_FREE(alt_path);
391 return True;
392}
393
394/**********************************************************************
395 Returns true if the unix path is a valid msdfs symlink and also
396 returns the target string from inside the link.
397**********************************************************************/
398
399static bool is_msdfs_link_internal(TALLOC_CTX *ctx,
400 connection_struct *conn,
401 const char *path,
402 char **pp_link_target,
403 SMB_STRUCT_STAT *sbufp)
404{
405 SMB_STRUCT_STAT st;
406 int referral_len = 0;
407#if defined(HAVE_BROKEN_READLINK)
408 char link_target_buf[PATH_MAX];
409#else
410 char link_target_buf[7];
411#endif
412 size_t bufsize = 0;
413 char *link_target = NULL;
414
415 if (pp_link_target) {
416 bufsize = 1024;
417 link_target = TALLOC_ARRAY(ctx, char, bufsize);
418 if (!link_target) {
419 return False;
420 }
421 *pp_link_target = link_target;
422 } else {
423 bufsize = sizeof(link_target_buf);
424 link_target = link_target_buf;
425 }
426
427 if (sbufp == NULL) {
428 sbufp = &st;
429 }
430
431 if (SMB_VFS_LSTAT(conn, path, sbufp) != 0) {
432 DEBUG(5,("is_msdfs_link_read_target: %s does not exist.\n",
433 path));
434 goto err;
435 }
436
437 if (!S_ISLNK(sbufp->st_mode)) {
438 DEBUG(5,("is_msdfs_link_read_target: %s is not a link.\n",
439 path));
440 goto err;
441 }
442
443 referral_len = SMB_VFS_READLINK(conn, path, link_target, bufsize - 1);
444 if (referral_len == -1) {
445 DEBUG(0,("is_msdfs_link_read_target: Error reading "
446 "msdfs link %s: %s\n",
447 path, strerror(errno)));
448 goto err;
449 }
450 link_target[referral_len] = '\0';
451
452 DEBUG(5,("is_msdfs_link_internal: %s -> %s\n",path,
453 link_target));
454
455 if (!strnequal(link_target, "msdfs:", 6)) {
456 goto err;
457 }
458 return True;
459
460 err:
461
462 if (link_target != link_target_buf) {
463 TALLOC_FREE(link_target);
464 }
465 return False;
466}
467
468/**********************************************************************
469 Returns true if the unix path is a valid msdfs symlink.
470**********************************************************************/
471
472bool is_msdfs_link(connection_struct *conn,
473 const char *path,
474 SMB_STRUCT_STAT *sbufp)
475{
476 return is_msdfs_link_internal(talloc_tos(),
477 conn,
478 path,
479 NULL,
480 sbufp);
481}
482
483/*****************************************************************
484 Used by other functions to decide if a dfs path is remote,
485 and to get the list of referred locations for that remote path.
486
487 search_flag: For findfirsts, dfs links themselves are not
488 redirected, but paths beyond the links are. For normal smb calls,
489 even dfs links need to be redirected.
490
491 consumedcntp: how much of the dfs path is being redirected. the client
492 should try the remaining path on the redirected server.
493
494 If this returns NT_STATUS_PATH_NOT_COVERED the contents of the msdfs
495 link redirect are in targetpath.
496*****************************************************************/
497
498static NTSTATUS dfs_path_lookup(TALLOC_CTX *ctx,
499 connection_struct *conn,
500 const char *dfspath, /* Incoming complete dfs path */
501 const struct dfs_path *pdp, /* Parsed out
502 server+share+extrapath. */
503 bool search_flag, /* Called from a findfirst ? */
504 int *consumedcntp,
505 char **pp_targetpath)
506{
507 char *p = NULL;
508 char *q = NULL;
509 SMB_STRUCT_STAT sbuf;
510 NTSTATUS status;
511 char *localpath = NULL;
512 char *canon_dfspath = NULL; /* Canonicalized dfs path. (only '/'
513 components). */
514
515 DEBUG(10,("dfs_path_lookup: Conn path = %s reqpath = %s\n",
516 conn->connectpath, pdp->reqpath));
517
518 /*
519 * Note the unix path conversion here we're doing we can
520 * throw away. We're looking for a symlink for a dfs
521 * resolution, if we don't find it we'll do another
522 * unix_convert later in the codepath.
523 * If we needed to remember what we'd resolved in
524 * dp->reqpath (as the original code did) we'd
525 * copy (localhost, dp->reqpath) on any code
526 * path below that returns True - but I don't
527 * think this is needed. JRA.
528 */
529
530 status = unix_convert(ctx, conn, pdp->reqpath, search_flag, &localpath,
531 NULL, &sbuf);
532 if (!NT_STATUS_IS_OK(status) && !NT_STATUS_EQUAL(status,
533 NT_STATUS_OBJECT_PATH_NOT_FOUND)) {
534 return status;
535 }
536
537 /* Optimization - check if we can redirect the whole path. */
538
539 if (is_msdfs_link_internal(ctx, conn, localpath, pp_targetpath, NULL)) {
540 if (search_flag) {
541 DEBUG(6,("dfs_path_lookup (FindFirst) No redirection "
542 "for dfs link %s.\n", dfspath));
543 return NT_STATUS_OK;
544 }
545
546 DEBUG(6,("dfs_path_lookup: %s resolves to a "
547 "valid dfs link %s.\n", dfspath,
548 pp_targetpath ? *pp_targetpath : ""));
549
550 if (consumedcntp) {
551 *consumedcntp = strlen(dfspath);
552 }
553 return NT_STATUS_PATH_NOT_COVERED;
554 }
555
556 /* Prepare to test only for '/' components in the given path,
557 * so if a Windows path replace all '\\' characters with '/'.
558 * For a POSIX DFS path we know all separators are already '/'. */
559
560 canon_dfspath = talloc_strdup(ctx, dfspath);
561 if (!canon_dfspath) {
562 return NT_STATUS_NO_MEMORY;
563 }
564 if (!pdp->posix_path) {
565 string_replace(canon_dfspath, '\\', '/');
566 }
567
568 /*
569 * localpath comes out of unix_convert, so it has
570 * no trailing backslash. Make sure that canon_dfspath hasn't either.
571 * Fix for bug #4860 from Jan Martin <Jan.Martin@rwedea.com>.
572 */
573
574 trim_char(canon_dfspath,0,'/');
575
576 /*
577 * Redirect if any component in the path is a link.
578 * We do this by walking backwards through the
579 * local path, chopping off the last component
580 * in both the local path and the canonicalized
581 * DFS path. If we hit a DFS link then we're done.
582 */
583
584 p = strrchr_m(localpath, '/');
585 if (consumedcntp) {
586 q = strrchr_m(canon_dfspath, '/');
587 }
588
589 while (p) {
590 *p = '\0';
591 if (q) {
592 *q = '\0';
593 }
594
595 if (is_msdfs_link_internal(ctx, conn,
596 localpath, pp_targetpath, NULL)) {
597 DEBUG(4, ("dfs_path_lookup: Redirecting %s because "
598 "parent %s is dfs link\n", dfspath, localpath));
599
600 if (consumedcntp) {
601 *consumedcntp = strlen(canon_dfspath);
602 DEBUG(10, ("dfs_path_lookup: Path consumed: %s "
603 "(%d)\n",
604 canon_dfspath,
605 *consumedcntp));
606 }
607
608 return NT_STATUS_PATH_NOT_COVERED;
609 }
610
611 /* Step back on the filesystem. */
612 p = strrchr_m(localpath, '/');
613
614 if (consumedcntp) {
615 /* And in the canonicalized dfs path. */
616 q = strrchr_m(canon_dfspath, '/');
617 }
618 }
619
620 return NT_STATUS_OK;
621}
622
623/*****************************************************************
624 Decides if a dfs pathname should be redirected or not.
625 If not, the pathname is converted to a tcon-relative local unix path
626
627 search_wcard_flag: this flag performs 2 functions both related
628 to searches. See resolve_dfs_path() and parse_dfs_path_XX()
629 for details.
630
631 This function can return NT_STATUS_OK, meaning use the returned path as-is
632 (mapped into a local path).
633 or NT_STATUS_NOT_COVERED meaning return a DFS redirect, or
634 any other NT_STATUS error which is a genuine error to be
635 returned to the client.
636*****************************************************************/
637
638static NTSTATUS dfs_redirect(TALLOC_CTX *ctx,
639 connection_struct *conn,
640 const char *path_in,
641 bool search_wcard_flag,
642 char **pp_path_out,
643 bool *ppath_contains_wcard)
644{
645 NTSTATUS status;
646 struct dfs_path *pdp = TALLOC_P(ctx, struct dfs_path);
647
648 if (!pdp) {
649 return NT_STATUS_NO_MEMORY;
650 }
651
652 status = parse_dfs_path(conn, path_in, search_wcard_flag, pdp,
653 ppath_contains_wcard);
654 if (!NT_STATUS_IS_OK(status)) {
655 TALLOC_FREE(pdp);
656 return status;
657 }
658
659 if (pdp->reqpath[0] == '\0') {
660 TALLOC_FREE(pdp);
661 *pp_path_out = talloc_strdup(ctx, "");
662 if (!*pp_path_out) {
663 return NT_STATUS_NO_MEMORY;
664 }
665 DEBUG(5,("dfs_redirect: self-referral.\n"));
666 return NT_STATUS_OK;
667 }
668
669 /* If dfs pathname for a non-dfs share, convert to tcon-relative
670 path and return OK */
671
672 if (!lp_msdfs_root(SNUM(conn))) {
673 *pp_path_out = talloc_strdup(ctx, pdp->reqpath);
674 TALLOC_FREE(pdp);
675 if (!*pp_path_out) {
676 return NT_STATUS_NO_MEMORY;
677 }
678 return NT_STATUS_OK;
679 }
680
681 /* If it looked like a local path (zero hostname/servicename)
682 * just treat as a tcon-relative path. */
683
684 if (pdp->hostname[0] == '\0' && pdp->servicename[0] == '\0') {
685 *pp_path_out = talloc_strdup(ctx, pdp->reqpath);
686 TALLOC_FREE(pdp);
687 if (!*pp_path_out) {
688 return NT_STATUS_NO_MEMORY;
689 }
690 return NT_STATUS_OK;
691 }
692
693 if (!( strequal(pdp->servicename, lp_servicename(SNUM(conn)))
694 || (strequal(pdp->servicename, HOMES_NAME)
695 && strequal(lp_servicename(SNUM(conn)),
696 conn->server_info->sanitized_username) )) ) {
697
698 /* The given sharename doesn't match this connection. */
699 TALLOC_FREE(pdp);
700
701 return NT_STATUS_OBJECT_PATH_NOT_FOUND;
702 }
703
704 status = dfs_path_lookup(ctx, conn, path_in, pdp,
705 search_wcard_flag, NULL, NULL);
706 if (!NT_STATUS_IS_OK(status)) {
707 if (NT_STATUS_EQUAL(status, NT_STATUS_PATH_NOT_COVERED)) {
708 DEBUG(3,("dfs_redirect: Redirecting %s\n", path_in));
709 } else {
710 DEBUG(10,("dfs_redirect: dfs_path_lookup "
711 "failed for %s with %s\n",
712 path_in, nt_errstr(status) ));
713 }
714 return status;
715 }
716
717 DEBUG(3,("dfs_redirect: Not redirecting %s.\n", path_in));
718
719 /* Form non-dfs tcon-relative path */
720 *pp_path_out = talloc_strdup(ctx, pdp->reqpath);
721 TALLOC_FREE(pdp);
722 if (!*pp_path_out) {
723 return NT_STATUS_NO_MEMORY;
724 }
725
726 DEBUG(3,("dfs_redirect: Path %s converted to non-dfs path %s\n",
727 path_in,
728 *pp_path_out));
729
730 return NT_STATUS_OK;
731}
732
733/**********************************************************************
734 Return a self referral.
735**********************************************************************/
736
737static NTSTATUS self_ref(TALLOC_CTX *ctx,
738 const char *dfs_path,
739 struct junction_map *jucn,
740 int *consumedcntp,
741 bool *self_referralp)
742{
743 struct referral *ref;
744
745 *self_referralp = True;
746
747 jucn->referral_count = 1;
748 if((ref = TALLOC_ZERO_P(ctx, struct referral)) == NULL) {
749 return NT_STATUS_NO_MEMORY;
750 }
751
752 ref->alternate_path = talloc_strdup(ctx, dfs_path);
753 if (!ref->alternate_path) {
754 return NT_STATUS_NO_MEMORY;
755 }
756 ref->proximity = 0;
757 ref->ttl = REFERRAL_TTL;
758 jucn->referral_list = ref;
759 *consumedcntp = strlen(dfs_path);
760 return NT_STATUS_OK;
761}
762
763/**********************************************************************
764 Gets valid referrals for a dfs path and fills up the
765 junction_map structure.
766**********************************************************************/
767
768NTSTATUS get_referred_path(TALLOC_CTX *ctx,
769 const char *dfs_path,
770 struct junction_map *jucn,
771 int *consumedcntp,
772 bool *self_referralp)
773{
774 struct connection_struct *conn;
775 char *targetpath = NULL;
776 int snum;
777 NTSTATUS status = NT_STATUS_NOT_FOUND;
778 bool dummy;
779 struct dfs_path *pdp = TALLOC_P(ctx, struct dfs_path);
780 char *oldpath;
781
782 if (!pdp) {
783 return NT_STATUS_NO_MEMORY;
784 }
785
786 *self_referralp = False;
787
788 status = parse_dfs_path(NULL, dfs_path, False, pdp, &dummy);
789 if (!NT_STATUS_IS_OK(status)) {
790 return status;
791 }
792
793 jucn->service_name = talloc_strdup(ctx, pdp->servicename);
794 jucn->volume_name = talloc_strdup(ctx, pdp->reqpath);
795 if (!jucn->service_name || !jucn->volume_name) {
796 TALLOC_FREE(pdp);
797 return NT_STATUS_NO_MEMORY;
798 }
799
800 /* Verify the share is a dfs root */
801 snum = lp_servicenumber(jucn->service_name);
802 if(snum < 0) {
803 fstring service_name;
804 fstrcpy(service_name, jucn->service_name);
805 if ((snum = find_service(service_name)) < 0) {
806 return NT_STATUS_NOT_FOUND;
807 }
808 TALLOC_FREE(jucn->service_name);
809 jucn->service_name = talloc_strdup(ctx, service_name);
810 if (!jucn->service_name) {
811 TALLOC_FREE(pdp);
812 return NT_STATUS_NO_MEMORY;
813 }
814 }
815
816 if (!lp_msdfs_root(snum) && (*lp_msdfs_proxy(snum) == '\0')) {
817 DEBUG(3,("get_referred_path: |%s| in dfs path %s is not "
818 "a dfs root.\n",
819 pdp->servicename, dfs_path));
820 TALLOC_FREE(pdp);
821 return NT_STATUS_NOT_FOUND;
822 }
823
824 /*
825 * Self referrals are tested with a anonymous IPC connection and
826 * a GET_DFS_REFERRAL call to \\server\share. (which means
827 * dp.reqpath[0] points to an empty string). create_conn_struct cd's
828 * into the directory and will fail if it cannot (as the anonymous
829 * user). Cope with this.
830 */
831
832 if (pdp->reqpath[0] == '\0') {
833 char *tmp;
834 struct referral *ref;
835
836 if (*lp_msdfs_proxy(snum) == '\0') {
837 TALLOC_FREE(pdp);
838 return self_ref(ctx,
839 dfs_path,
840 jucn,
841 consumedcntp,
842 self_referralp);
843 }
844
845 /*
846 * It's an msdfs proxy share. Redirect to
847 * the configured target share.
848 */
849
850 jucn->referral_count = 1;
851 if ((ref = TALLOC_ZERO_P(ctx, struct referral)) == NULL) {
852 TALLOC_FREE(pdp);
853 return NT_STATUS_NO_MEMORY;
854 }
855
856 if (!(tmp = talloc_strdup(ctx, lp_msdfs_proxy(snum)))) {
857 TALLOC_FREE(pdp);
858 return NT_STATUS_NO_MEMORY;
859 }
860
861 trim_string(tmp, "\\", 0);
862
863 ref->alternate_path = talloc_asprintf(ctx, "\\%s", tmp);
864 TALLOC_FREE(tmp);
865
866 if (!ref->alternate_path) {
867 TALLOC_FREE(pdp);
868 return NT_STATUS_NO_MEMORY;
869 }
870
871 if (pdp->reqpath[0] != '\0') {
872 ref->alternate_path = talloc_asprintf_append(
873 ref->alternate_path,
874 "%s",
875 pdp->reqpath);
876 if (!ref->alternate_path) {
877 TALLOC_FREE(pdp);
878 return NT_STATUS_NO_MEMORY;
879 }
880 }
881 ref->proximity = 0;
882 ref->ttl = REFERRAL_TTL;
883 jucn->referral_list = ref;
884 *consumedcntp = strlen(dfs_path);
885 TALLOC_FREE(pdp);
886 return NT_STATUS_OK;
887 }
888
889 status = create_conn_struct(ctx, &conn, snum, lp_pathname(snum),
890 &oldpath);
891 if (!NT_STATUS_IS_OK(status)) {
892 TALLOC_FREE(pdp);
893 return status;
894 }
895
896 /* If this is a DFS path dfs_lookup should return
897 * NT_STATUS_PATH_NOT_COVERED. */
898
899 status = dfs_path_lookup(ctx, conn, dfs_path, pdp,
900 False, consumedcntp, &targetpath);
901
902 if (!NT_STATUS_EQUAL(status, NT_STATUS_PATH_NOT_COVERED)) {
903 DEBUG(3,("get_referred_path: No valid referrals for path %s\n",
904 dfs_path));
905 vfs_ChDir(conn, oldpath);
906 conn_free_internal(conn);
907 TALLOC_FREE(pdp);
908 return status;
909 }
910
911 /* We know this is a valid dfs link. Parse the targetpath. */
912 if (!parse_msdfs_symlink(ctx, targetpath,
913 &jucn->referral_list,
914 &jucn->referral_count)) {
915 DEBUG(3,("get_referred_path: failed to parse symlink "
916 "target %s\n", targetpath ));
917 vfs_ChDir(conn, oldpath);
918 conn_free_internal(conn);
919 TALLOC_FREE(pdp);
920 return NT_STATUS_NOT_FOUND;
921 }
922
923 vfs_ChDir(conn, oldpath);
924 conn_free_internal(conn);
925 TALLOC_FREE(pdp);
926 return NT_STATUS_OK;
927}
928
929static int setup_ver2_dfs_referral(const char *pathname,
930 char **ppdata,
931 struct junction_map *junction,
932 bool self_referral)
933{
934 char* pdata = *ppdata;
935
936 smb_ucs2_t *uni_requestedpath = NULL;
937 int uni_reqpathoffset1,uni_reqpathoffset2;
938 int uni_curroffset;
939 int requestedpathlen=0;
940 int offset;
941 int reply_size = 0;
942 int i=0;
943
944 DEBUG(10,("Setting up version2 referral\nRequested path:\n"));
945
946 requestedpathlen = rpcstr_push_talloc(talloc_tos(),
947 &uni_requestedpath, pathname);
948 if (uni_requestedpath == NULL || requestedpathlen == 0) {
949 return -1;
950 }
951
952 if (DEBUGLVL(10)) {
953 dump_data(0, (unsigned char *)uni_requestedpath,
954 requestedpathlen);
955 }
956
957 DEBUG(10,("ref count = %u\n",junction->referral_count));
958
959 uni_reqpathoffset1 = REFERRAL_HEADER_SIZE +
960 VERSION2_REFERRAL_SIZE * junction->referral_count;
961
962 uni_reqpathoffset2 = uni_reqpathoffset1 + requestedpathlen;
963
964 uni_curroffset = uni_reqpathoffset2 + requestedpathlen;
965
966 reply_size = REFERRAL_HEADER_SIZE +
967 VERSION2_REFERRAL_SIZE*junction->referral_count +
968 2 * requestedpathlen;
969 DEBUG(10,("reply_size: %u\n",reply_size));
970
971 /* add up the unicode lengths of all the referral paths */
972 for(i=0;i<junction->referral_count;i++) {
973 DEBUG(10,("referral %u : %s\n",
974 i,
975 junction->referral_list[i].alternate_path));
976 reply_size +=
977 (strlen(junction->referral_list[i].alternate_path)+1)*2;
978 }
979
980 DEBUG(10,("reply_size = %u\n",reply_size));
981 /* add the unexplained 0x16 bytes */
982 reply_size += 0x16;
983
984 pdata = (char *)SMB_REALLOC(pdata,reply_size);
985 if(pdata == NULL) {
986 DEBUG(0,("Realloc failed!\n"));
987 return -1;
988 }
989 *ppdata = pdata;
990
991 /* copy in the dfs requested paths.. required for offset calculations */
992 memcpy(pdata+uni_reqpathoffset1,uni_requestedpath,requestedpathlen);
993 memcpy(pdata+uni_reqpathoffset2,uni_requestedpath,requestedpathlen);
994
995 /* create the header */
996 SSVAL(pdata,0,requestedpathlen - 2); /* UCS2 of path consumed minus
997 2 byte null */
998 /* number of referral in this pkt */
999 SSVAL(pdata,2,junction->referral_count);
1000 if(self_referral) {
1001 SIVAL(pdata,4,DFSREF_REFERRAL_SERVER | DFSREF_STORAGE_SERVER);
1002 } else {
1003 SIVAL(pdata,4,DFSREF_STORAGE_SERVER);
1004 }
1005
1006 offset = 8;
1007 /* add the referral elements */
1008 for(i=0;i<junction->referral_count;i++) {
1009 struct referral* ref = &junction->referral_list[i];
1010 int unilen;
1011
1012 SSVAL(pdata,offset,2); /* version 2 */
1013 SSVAL(pdata,offset+2,VERSION2_REFERRAL_SIZE);
1014 if(self_referral) {
1015 SSVAL(pdata,offset+4,1);
1016 } else {
1017 SSVAL(pdata,offset+4,0);
1018 }
1019
1020 /* ref_flags :use path_consumed bytes? */
1021 SSVAL(pdata,offset+6,0);
1022 SIVAL(pdata,offset+8,ref->proximity);
1023 SIVAL(pdata,offset+12,ref->ttl);
1024
1025 SSVAL(pdata,offset+16,uni_reqpathoffset1-offset);
1026 SSVAL(pdata,offset+18,uni_reqpathoffset2-offset);
1027 /* copy referred path into current offset */
1028 unilen = rpcstr_push(pdata+uni_curroffset,
1029 ref->alternate_path,
1030 reply_size - uni_curroffset,
1031 STR_UNICODE);
1032
1033 SSVAL(pdata,offset+20,uni_curroffset-offset);
1034
1035 uni_curroffset += unilen;
1036 offset += VERSION2_REFERRAL_SIZE;
1037 }
1038 /* add in the unexplained 22 (0x16) bytes at the end */
1039 memset(pdata+uni_curroffset,'\0',0x16);
1040 return reply_size;
1041}
1042
1043static int setup_ver3_dfs_referral(const char *pathname,
1044 char **ppdata,
1045 struct junction_map *junction,
1046 bool self_referral)
1047{
1048 char *pdata = *ppdata;
1049
1050 smb_ucs2_t *uni_reqpath = NULL;
1051 int uni_reqpathoffset1, uni_reqpathoffset2;
1052 int uni_curroffset;
1053 int reply_size = 0;
1054
1055 int reqpathlen = 0;
1056 int offset,i=0;
1057
1058 DEBUG(10,("setting up version3 referral\n"));
1059
1060 reqpathlen = rpcstr_push_talloc(talloc_tos(), &uni_reqpath, pathname);
1061 if (uni_reqpath == NULL || reqpathlen == 0) {
1062 return -1;
1063 }
1064
1065 if (DEBUGLVL(10)) {
1066 dump_data(0, (unsigned char *)uni_reqpath,
1067 reqpathlen);
1068 }
1069
1070 uni_reqpathoffset1 = REFERRAL_HEADER_SIZE +
1071 VERSION3_REFERRAL_SIZE * junction->referral_count;
1072 uni_reqpathoffset2 = uni_reqpathoffset1 + reqpathlen;
1073 reply_size = uni_curroffset = uni_reqpathoffset2 + reqpathlen;
1074
1075 for(i=0;i<junction->referral_count;i++) {
1076 DEBUG(10,("referral %u : %s\n",
1077 i,
1078 junction->referral_list[i].alternate_path));
1079 reply_size +=
1080 (strlen(junction->referral_list[i].alternate_path)+1)*2;
1081 }
1082
1083 pdata = (char *)SMB_REALLOC(pdata,reply_size);
1084 if(pdata == NULL) {
1085 DEBUG(0,("version3 referral setup:"
1086 "malloc failed for Realloc!\n"));
1087 return -1;
1088 }
1089 *ppdata = pdata;
1090
1091 /* create the header */
1092 SSVAL(pdata,0,reqpathlen - 2); /* UCS2 of path consumed minus
1093 2 byte null */
1094
1095 SSVAL(pdata,2,junction->referral_count); /* number of referral */
1096 if(self_referral) {
1097 SIVAL(pdata,4,DFSREF_REFERRAL_SERVER | DFSREF_STORAGE_SERVER);
1098 } else {
1099 SIVAL(pdata,4,DFSREF_STORAGE_SERVER);
1100 }
1101
1102 /* copy in the reqpaths */
1103 memcpy(pdata+uni_reqpathoffset1,uni_reqpath,reqpathlen);
1104 memcpy(pdata+uni_reqpathoffset2,uni_reqpath,reqpathlen);
1105
1106 offset = 8;
1107 for(i=0;i<junction->referral_count;i++) {
1108 struct referral* ref = &(junction->referral_list[i]);
1109 int unilen;
1110
1111 SSVAL(pdata,offset,3); /* version 3 */
1112 SSVAL(pdata,offset+2,VERSION3_REFERRAL_SIZE);
1113 if(self_referral) {
1114 SSVAL(pdata,offset+4,1);
1115 } else {
1116 SSVAL(pdata,offset+4,0);
1117 }
1118
1119 /* ref_flags :use path_consumed bytes? */
1120 SSVAL(pdata,offset+6,0);
1121 SIVAL(pdata,offset+8,ref->ttl);
1122
1123 SSVAL(pdata,offset+12,uni_reqpathoffset1-offset);
1124 SSVAL(pdata,offset+14,uni_reqpathoffset2-offset);
1125 /* copy referred path into current offset */
1126 unilen = rpcstr_push(pdata+uni_curroffset,ref->alternate_path,
1127 reply_size - uni_curroffset,
1128 STR_UNICODE | STR_TERMINATE);
1129 SSVAL(pdata,offset+16,uni_curroffset-offset);
1130 /* copy 0x10 bytes of 00's in the ServiceSite GUID */
1131 memset(pdata+offset+18,'\0',16);
1132
1133 uni_curroffset += unilen;
1134 offset += VERSION3_REFERRAL_SIZE;
1135 }
1136 return reply_size;
1137}
1138
1139/******************************************************************
1140 Set up the DFS referral for the dfs pathname. This call returns
1141 the amount of the path covered by this server, and where the
1142 client should be redirected to. This is the meat of the
1143 TRANS2_GET_DFS_REFERRAL call.
1144******************************************************************/
1145
1146int setup_dfs_referral(connection_struct *orig_conn,
1147 const char *dfs_path,
1148 int max_referral_level,
1149 char **ppdata, NTSTATUS *pstatus)
1150{
1151 struct junction_map *junction = NULL;
1152 int consumedcnt = 0;
1153 bool self_referral = False;
1154 int reply_size = 0;
1155 char *pathnamep = NULL;
1156 char *local_dfs_path = NULL;
1157 TALLOC_CTX *ctx;
1158
1159 if (!(ctx=talloc_init("setup_dfs_referral"))) {
1160 *pstatus = NT_STATUS_NO_MEMORY;
1161 return -1;
1162 }
1163
1164 /* get the junction entry */
1165 if (!dfs_path) {
1166 talloc_destroy(ctx);
1167 *pstatus = NT_STATUS_NOT_FOUND;
1168 return -1;
1169 }
1170
1171 /*
1172 * Trim pathname sent by client so it begins with only one backslash.
1173 * Two backslashes confuse some dfs clients
1174 */
1175
1176 local_dfs_path = talloc_strdup(ctx,dfs_path);
1177 if (!local_dfs_path) {
1178 *pstatus = NT_STATUS_NO_MEMORY;
1179 talloc_destroy(ctx);
1180 return -1;
1181 }
1182 pathnamep = local_dfs_path;
1183 while (IS_DIRECTORY_SEP(pathnamep[0]) &&
1184 IS_DIRECTORY_SEP(pathnamep[1])) {
1185 pathnamep++;
1186 }
1187
1188 junction = TALLOC_ZERO_P(ctx, struct junction_map);
1189 if (!junction) {
1190 *pstatus = NT_STATUS_NO_MEMORY;
1191 talloc_destroy(ctx);
1192 return -1;
1193 }
1194
1195 /* The following call can change cwd. */
1196 *pstatus = get_referred_path(ctx, pathnamep, junction,
1197 &consumedcnt, &self_referral);
1198 if (!NT_STATUS_IS_OK(*pstatus)) {
1199 vfs_ChDir(orig_conn,orig_conn->connectpath);
1200 talloc_destroy(ctx);
1201 return -1;
1202 }
1203 vfs_ChDir(orig_conn,orig_conn->connectpath);
1204
1205 if (!self_referral) {
1206 pathnamep[consumedcnt] = '\0';
1207
1208 if( DEBUGLVL( 3 ) ) {
1209 int i=0;
1210 dbgtext("setup_dfs_referral: Path %s to "
1211 "alternate path(s):",
1212 pathnamep);
1213 for(i=0;i<junction->referral_count;i++)
1214 dbgtext(" %s",
1215 junction->referral_list[i].alternate_path);
1216 dbgtext(".\n");
1217 }
1218 }
1219
1220 /* create the referral depeding on version */
1221 DEBUG(10,("max_referral_level :%d\n",max_referral_level));
1222
1223 if (max_referral_level < 2) {
1224 max_referral_level = 2;
1225 }
1226 if (max_referral_level > 3) {
1227 max_referral_level = 3;
1228 }
1229
1230 switch(max_referral_level) {
1231 case 2:
1232 reply_size = setup_ver2_dfs_referral(pathnamep,
1233 ppdata, junction,
1234 self_referral);
1235 break;
1236 case 3:
1237 reply_size = setup_ver3_dfs_referral(pathnamep, ppdata,
1238 junction, self_referral);
1239 break;
1240 default:
1241 DEBUG(0,("setup_dfs_referral: Invalid dfs referral "
1242 "version: %d\n",
1243 max_referral_level));
1244 talloc_destroy(ctx);
1245 *pstatus = NT_STATUS_INVALID_LEVEL;
1246 return -1;
1247 }
1248
1249 if (DEBUGLVL(10)) {
1250 DEBUGADD(0,("DFS Referral pdata:\n"));
1251 dump_data(0,(uint8 *)*ppdata,reply_size);
1252 }
1253
1254 talloc_destroy(ctx);
1255 *pstatus = NT_STATUS_OK;
1256 return reply_size;
1257}
1258
1259/**********************************************************************
1260 The following functions are called by the NETDFS RPC pipe functions
1261 **********************************************************************/
1262
1263/*********************************************************************
1264 Creates a junction structure from a DFS pathname
1265**********************************************************************/
1266
1267bool create_junction(TALLOC_CTX *ctx,
1268 const char *dfs_path,
1269 struct junction_map *jucn)
1270{
1271 int snum;
1272 bool dummy;
1273 struct dfs_path *pdp = TALLOC_P(ctx,struct dfs_path);
1274 NTSTATUS status;
1275
1276 if (!pdp) {
1277 return False;
1278 }
1279 status = parse_dfs_path(NULL, dfs_path, False, pdp, &dummy);
1280 if (!NT_STATUS_IS_OK(status)) {
1281 return False;
1282 }
1283
1284 /* check if path is dfs : validate first token */
1285 if (!is_myname_or_ipaddr(pdp->hostname)) {
1286 DEBUG(4,("create_junction: Invalid hostname %s "
1287 "in dfs path %s\n",
1288 pdp->hostname, dfs_path));
1289 TALLOC_FREE(pdp);
1290 return False;
1291 }
1292
1293 /* Check for a non-DFS share */
1294 snum = lp_servicenumber(pdp->servicename);
1295
1296 if(snum < 0 || !lp_msdfs_root(snum)) {
1297 DEBUG(4,("create_junction: %s is not an msdfs root.\n",
1298 pdp->servicename));
1299 TALLOC_FREE(pdp);
1300 return False;
1301 }
1302
1303 jucn->service_name = talloc_strdup(ctx, pdp->servicename);
1304 jucn->volume_name = talloc_strdup(ctx, pdp->reqpath);
1305 jucn->comment = talloc_strdup(ctx, lp_comment(snum));
1306
1307 TALLOC_FREE(pdp);
1308 if (!jucn->service_name || !jucn->volume_name || ! jucn->comment) {
1309 return False;
1310 }
1311 return True;
1312}
1313
1314/**********************************************************************
1315 Forms a valid Unix pathname from the junction
1316 **********************************************************************/
1317
1318static bool junction_to_local_path(const struct junction_map *jucn,
1319 char **pp_path_out,
1320 connection_struct **conn_out,
1321 char **oldpath)
1322{
1323 int snum;
1324 NTSTATUS status;
1325
1326 snum = lp_servicenumber(jucn->service_name);
1327 if(snum < 0) {
1328 return False;
1329 }
1330 status = create_conn_struct(talloc_tos(), conn_out, snum,
1331 lp_pathname(snum), oldpath);
1332 if (!NT_STATUS_IS_OK(status)) {
1333 return False;
1334 }
1335
1336 *pp_path_out = talloc_asprintf(*conn_out,
1337 "%s/%s",
1338 lp_pathname(snum),
1339 jucn->volume_name);
1340 if (!*pp_path_out) {
1341 vfs_ChDir(*conn_out, *oldpath);
1342 conn_free_internal(*conn_out);
1343 return False;
1344 }
1345 return True;
1346}
1347
1348bool create_msdfs_link(const struct junction_map *jucn)
1349{
1350 char *path = NULL;
1351 char *cwd;
1352 char *msdfs_link = NULL;
1353 connection_struct *conn;
1354 int i=0;
1355 bool insert_comma = False;
1356 bool ret = False;
1357
1358 if(!junction_to_local_path(jucn, &path, &conn, &cwd)) {
1359 return False;
1360 }
1361
1362 /* Form the msdfs_link contents */
1363 msdfs_link = talloc_strdup(conn, "msdfs:");
1364 if (!msdfs_link) {
1365 goto out;
1366 }
1367 for(i=0; i<jucn->referral_count; i++) {
1368 char *refpath = jucn->referral_list[i].alternate_path;
1369
1370 /* Alternate paths always use Windows separators. */
1371 trim_char(refpath, '\\', '\\');
1372 if(*refpath == '\0') {
1373 if (i == 0) {
1374 insert_comma = False;
1375 }
1376 continue;
1377 }
1378 if (i > 0 && insert_comma) {
1379 msdfs_link = talloc_asprintf_append_buffer(msdfs_link,
1380 ",%s",
1381 refpath);
1382 } else {
1383 msdfs_link = talloc_asprintf_append_buffer(msdfs_link,
1384 "%s",
1385 refpath);
1386 }
1387
1388 if (!msdfs_link) {
1389 goto out;
1390 }
1391 if (!insert_comma) {
1392 insert_comma = True;
1393 }
1394 }
1395
1396 DEBUG(5,("create_msdfs_link: Creating new msdfs link: %s -> %s\n",
1397 path, msdfs_link));
1398
1399 if(SMB_VFS_SYMLINK(conn, msdfs_link, path) < 0) {
1400 if (errno == EEXIST) {
1401 if(SMB_VFS_UNLINK(conn,path)!=0) {
1402 goto out;
1403 }
1404 }
1405 if (SMB_VFS_SYMLINK(conn, msdfs_link, path) < 0) {
1406 DEBUG(1,("create_msdfs_link: symlink failed "
1407 "%s -> %s\nError: %s\n",
1408 path, msdfs_link, strerror(errno)));
1409 goto out;
1410 }
1411 }
1412
1413 ret = True;
1414
1415out:
1416 vfs_ChDir(conn, cwd);
1417 conn_free_internal(conn);
1418 return ret;
1419}
1420
1421bool remove_msdfs_link(const struct junction_map *jucn)
1422{
1423 char *path = NULL;
1424 char *cwd;
1425 connection_struct *conn;
1426 bool ret = False;
1427
1428 if (!junction_to_local_path(jucn, &path, &conn, &cwd)) {
1429 return false;
1430 }
1431
1432 if( SMB_VFS_UNLINK(conn, path) == 0 ) {
1433 ret = True;
1434 }
1435
1436 vfs_ChDir(conn, cwd);
1437 conn_free_internal(conn);
1438 return ret;
1439}
1440
1441/*********************************************************************
1442 Return the number of DFS links at the root of this share.
1443*********************************************************************/
1444
1445static int count_dfs_links(TALLOC_CTX *ctx, int snum)
1446{
1447 size_t cnt = 0;
1448 SMB_STRUCT_DIR *dirp = NULL;
1449 char *dname = NULL;
1450 const char *connect_path = lp_pathname(snum);
1451 const char *msdfs_proxy = lp_msdfs_proxy(snum);
1452 connection_struct *conn;
1453 NTSTATUS status;
1454 char *cwd;
1455
1456 if(*connect_path == '\0') {
1457 return 0;
1458 }
1459
1460 /*
1461 * Fake up a connection struct for the VFS layer.
1462 */
1463
1464 status = create_conn_struct(talloc_tos(), &conn, snum, connect_path,
1465 &cwd);
1466 if (!NT_STATUS_IS_OK(status)) {
1467 DEBUG(3, ("create_conn_struct failed: %s\n",
1468 nt_errstr(status)));
1469 return 0;
1470 }
1471
1472 /* Count a link for the msdfs root - convention */
1473 cnt = 1;
1474
1475 /* No more links if this is an msdfs proxy. */
1476 if (*msdfs_proxy != '\0') {
1477 goto out;
1478 }
1479
1480 /* Now enumerate all dfs links */
1481 dirp = SMB_VFS_OPENDIR(conn, ".", NULL, 0);
1482 if(!dirp) {
1483 goto out;
1484 }
1485
1486 while ((dname = vfs_readdirname(conn, dirp)) != NULL) {
1487 if (is_msdfs_link(conn,
1488 dname,
1489 NULL)) {
1490 cnt++;
1491 }
1492 }
1493
1494 SMB_VFS_CLOSEDIR(conn,dirp);
1495
1496out:
1497 vfs_ChDir(conn, cwd);
1498 conn_free_internal(conn);
1499 return cnt;
1500}
1501
1502/*********************************************************************
1503*********************************************************************/
1504
1505static int form_junctions(TALLOC_CTX *ctx,
1506 int snum,
1507 struct junction_map *jucn,
1508 size_t jn_remain)
1509{
1510 size_t cnt = 0;
1511 SMB_STRUCT_DIR *dirp = NULL;
1512 char *dname = NULL;
1513 const char *connect_path = lp_pathname(snum);
1514 char *service_name = lp_servicename(snum);
1515 const char *msdfs_proxy = lp_msdfs_proxy(snum);
1516 connection_struct *conn;
1517 struct referral *ref = NULL;
1518 char *cwd;
1519 NTSTATUS status;
1520
1521 if (jn_remain == 0) {
1522 return 0;
1523 }
1524
1525 if(*connect_path == '\0') {
1526 return 0;
1527 }
1528
1529 /*
1530 * Fake up a connection struct for the VFS layer.
1531 */
1532
1533 status = create_conn_struct(ctx, &conn, snum, connect_path, &cwd);
1534 if (!NT_STATUS_IS_OK(status)) {
1535 DEBUG(3, ("create_conn_struct failed: %s\n",
1536 nt_errstr(status)));
1537 return 0;
1538 }
1539
1540 /* form a junction for the msdfs root - convention
1541 DO NOT REMOVE THIS: NT clients will not work with us
1542 if this is not present
1543 */
1544 jucn[cnt].service_name = talloc_strdup(ctx,service_name);
1545 jucn[cnt].volume_name = talloc_strdup(ctx, "");
1546 if (!jucn[cnt].service_name || !jucn[cnt].volume_name) {
1547 goto out;
1548 }
1549 jucn[cnt].comment = "";
1550 jucn[cnt].referral_count = 1;
1551
1552 ref = jucn[cnt].referral_list = TALLOC_ZERO_P(ctx, struct referral);
1553 if (jucn[cnt].referral_list == NULL) {
1554 goto out;
1555 }
1556
1557 ref->proximity = 0;
1558 ref->ttl = REFERRAL_TTL;
1559 if (*msdfs_proxy != '\0') {
1560 ref->alternate_path = talloc_strdup(ctx,
1561 msdfs_proxy);
1562 } else {
1563 ref->alternate_path = talloc_asprintf(ctx,
1564 "\\\\%s\\%s",
1565 get_local_machine_name(),
1566 service_name);
1567 }
1568
1569 if (!ref->alternate_path) {
1570 goto out;
1571 }
1572 cnt++;
1573
1574 /* Don't enumerate if we're an msdfs proxy. */
1575 if (*msdfs_proxy != '\0') {
1576 goto out;
1577 }
1578
1579 /* Now enumerate all dfs links */
1580 dirp = SMB_VFS_OPENDIR(conn, ".", NULL, 0);
1581 if(!dirp) {
1582 goto out;
1583 }
1584
1585 while ((dname = vfs_readdirname(conn, dirp)) != NULL) {
1586 char *link_target = NULL;
1587 if (cnt >= jn_remain) {
1588 DEBUG(2, ("form_junctions: ran out of MSDFS "
1589 "junction slots"));
1590 goto out;
1591 }
1592 if (is_msdfs_link_internal(ctx,
1593 conn,
1594 dname, &link_target,
1595 NULL)) {
1596 if (parse_msdfs_symlink(ctx,
1597 link_target,
1598 &jucn[cnt].referral_list,
1599 &jucn[cnt].referral_count)) {
1600
1601 jucn[cnt].service_name = talloc_strdup(ctx,
1602 service_name);
1603 jucn[cnt].volume_name = talloc_strdup(ctx,
1604 dname);
1605 if (!jucn[cnt].service_name ||
1606 !jucn[cnt].volume_name) {
1607 goto out;
1608 }
1609 jucn[cnt].comment = "";
1610 cnt++;
1611 }
1612 TALLOC_FREE(link_target);
1613 }
1614 }
1615
1616out:
1617
1618 if (dirp) {
1619 SMB_VFS_CLOSEDIR(conn,dirp);
1620 }
1621
1622 vfs_ChDir(conn, cwd);
1623 conn_free_internal(conn);
1624 return cnt;
1625}
1626
1627struct junction_map *enum_msdfs_links(TALLOC_CTX *ctx, size_t *p_num_jn)
1628{
1629 struct junction_map *jn = NULL;
1630 int i=0;
1631 size_t jn_count = 0;
1632 int sharecount = 0;
1633
1634 *p_num_jn = 0;
1635 if(!lp_host_msdfs()) {
1636 return NULL;
1637 }
1638
1639 /* Ensure all the usershares are loaded. */
1640 become_root();
1641 load_registry_shares();
1642 sharecount = load_usershare_shares();
1643 unbecome_root();
1644
1645 for(i=0;i < sharecount;i++) {
1646 if(lp_msdfs_root(i)) {
1647 jn_count += count_dfs_links(ctx, i);
1648 }
1649 }
1650 if (jn_count == 0) {
1651 return NULL;
1652 }
1653 jn = TALLOC_ARRAY(ctx, struct junction_map, jn_count);
1654 if (!jn) {
1655 return NULL;
1656 }
1657 for(i=0; i < sharecount; i++) {
1658 if (*p_num_jn >= jn_count) {
1659 break;
1660 }
1661 if(lp_msdfs_root(i)) {
1662 *p_num_jn += form_junctions(ctx, i,
1663 &jn[*p_num_jn],
1664 jn_count - *p_num_jn);
1665 }
1666 }
1667 return jn;
1668}
1669
1670/******************************************************************************
1671 Core function to resolve a dfs pathname.
1672******************************************************************************/
1673
1674NTSTATUS resolve_dfspath(TALLOC_CTX *ctx,
1675 connection_struct *conn,
1676 bool dfs_pathnames,
1677 const char *name_in,
1678 char **pp_name_out)
1679{
1680 NTSTATUS status = NT_STATUS_OK;
1681 bool dummy;
1682 if (dfs_pathnames) {
1683 status = dfs_redirect(ctx,
1684 conn,
1685 name_in,
1686 False,
1687 pp_name_out,
1688 &dummy);
1689 } else {
1690 /*
1691 * Cheat and just return a copy of the in ptr.
1692 * Once srvstr_get_path() uses talloc it'll
1693 * be a talloced ptr anyway.
1694 */
1695 *pp_name_out = CONST_DISCARD(char *,name_in);
1696 }
1697 return status;
1698}
1699
1700/******************************************************************************
1701 Core function to resolve a dfs pathname possibly containing a wildcard.
1702 This function is identical to the above except for the bool param to
1703 dfs_redirect but I need this to be separate so it's really clear when
1704 we're allowing wildcards and when we're not. JRA.
1705******************************************************************************/
1706
1707NTSTATUS resolve_dfspath_wcard(TALLOC_CTX *ctx,
1708 connection_struct *conn,
1709 bool dfs_pathnames,
1710 const char *name_in,
1711 char **pp_name_out,
1712 bool *ppath_contains_wcard)
1713{
1714 NTSTATUS status = NT_STATUS_OK;
1715 if (dfs_pathnames) {
1716 status = dfs_redirect(ctx,
1717 conn,
1718 name_in,
1719 True,
1720 pp_name_out,
1721 ppath_contains_wcard);
1722 } else {
1723 /*
1724 * Cheat and just return a copy of the in ptr.
1725 * Once srvstr_get_path() uses talloc it'll
1726 * be a talloced ptr anyway.
1727 */
1728 *pp_name_out = CONST_DISCARD(char *,name_in);
1729 }
1730 return status;
1731}
Note: See TracBrowser for help on using the repository browser.