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