source: vendor/3.6.0/source3/smbd/msdfs.c

Last change on this file was 740, checked in by Silvan Scherrer, 13 years ago

Samba Server: update vendor to 3.6.0

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