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

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

Import Samba 3.3 branch at 3.0.0 level (psmedley's port)

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