source: branches/samba-3.0/source/smbd/msdfs.c@ 134

Last change on this file since 134 was 134, checked in by Paul Smedley, 17 years ago

Update source to 3.0.29

File size: 35.0 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 2 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, write to the Free Software
20 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21
22*/
23
24#define DBGC_CLASS DBGC_MSDFS
25#include "includes.h"
26
27extern uint32 global_client_caps;
28
29/**********************************************************************
30 Parse a DFS pathname of the form \hostname\service\reqpath
31 into the dfs_path structure.
32 If POSIX pathnames is true, the pathname may also be of the
33 form /hostname/service/reqpath.
34 We cope with either here.
35
36 If conn != NULL then ensure the provided service is
37 the one pointed to by the connection.
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 JRA.
44**********************************************************************/
45
46static NTSTATUS parse_dfs_path(connection_struct *conn,
47 const char *pathname,
48 BOOL allow_wcards,
49 struct dfs_path *pdp,
50 BOOL *ppath_contains_wcard)
51{
52 pstring pathname_local;
53 char *p,*temp, *servicename;
54 NTSTATUS status = NT_STATUS_OK;
55 char sepchar;
56
57 ZERO_STRUCTP(pdp);
58
59 pstrcpy(pathname_local,pathname);
60 p = temp = pathname_local;
61
62 pdp->posix_path = (lp_posix_pathnames() && *pathname == '/');
63
64 sepchar = pdp->posix_path ? '/' : '\\';
65
66 if (*pathname != sepchar) {
67 DEBUG(10,("parse_dfs_path: path %s doesn't start with %c\n",
68 pathname, sepchar ));
69 /*
70 * Possibly client sent a local path by mistake.
71 * Try and convert to a local path.
72 */
73
74 pdp->hostname[0] = '\0';
75 pdp->servicename[0] = '\0';
76
77 /* We've got no info about separators. */
78 pdp->posix_path = lp_posix_pathnames();
79 p = temp;
80 DEBUG(10,("parse_dfs_path: trying to convert %s to a local path\n",
81 temp));
82 goto local_path;
83 }
84
85 trim_char(temp,sepchar,sepchar);
86
87 DEBUG(10,("parse_dfs_path: temp = |%s| after trimming %c's\n",
88 temp, sepchar));
89
90 /* Now tokenize. */
91 /* Parse out hostname. */
92 p = strchr_m(temp,sepchar);
93 if(p == NULL) {
94 DEBUG(10,("parse_dfs_path: can't parse hostname from path %s\n",
95 temp));
96 /*
97 * Possibly client sent a local path by mistake.
98 * Try and convert to a local path.
99 */
100
101 pdp->hostname[0] = '\0';
102 pdp->servicename[0] = '\0';
103
104 p = temp;
105 DEBUG(10,("parse_dfs_path: trying to convert %s to a local path\n",
106 temp));
107 goto local_path;
108 }
109 *p = '\0';
110 fstrcpy(pdp->hostname,temp);
111 DEBUG(10,("parse_dfs_path: hostname: %s\n",pdp->hostname));
112
113 /* Parse out servicename. */
114 servicename = p+1;
115 p = strchr_m(servicename,sepchar);
116 if (p) {
117 *p = '\0';
118 }
119
120 /* Is this really our servicename ? */
121 if (conn && !( strequal(servicename, lp_servicename(SNUM(conn)))
122 || (strequal(servicename, HOMES_NAME)
123 && strequal(lp_servicename(SNUM(conn)),
124 get_current_username()) )) ) {
125
126 DEBUG(10,("parse_dfs_path: %s is not our servicename\n",
127 servicename));
128
129 /*
130 * Possibly client sent a local path by mistake.
131 * Try and convert to a local path.
132 */
133
134 pdp->hostname[0] = '\0';
135 pdp->servicename[0] = '\0';
136
137 /* Repair the path - replace the sepchar's
138 we nulled out */
139 servicename--;
140 *servicename = sepchar;
141 if (p) {
142 *p = sepchar;
143 }
144
145 p = temp;
146 DEBUG(10,("parse_dfs_path: trying to convert %s "
147 "to a local path\n",
148 temp));
149 goto local_path;
150 }
151
152 fstrcpy(pdp->servicename,servicename);
153
154 DEBUG(10,("parse_dfs_path: servicename: %s\n",pdp->servicename));
155
156 if(p == NULL) {
157 pdp->reqpath[0] = '\0';
158 return NT_STATUS_OK;
159 }
160 p++;
161
162 local_path:
163
164 *ppath_contains_wcard = False;
165
166 /* Rest is reqpath. */
167 if (pdp->posix_path) {
168 status = check_path_syntax_posix(pdp->reqpath, p);
169 } else {
170 if (allow_wcards) {
171 status = check_path_syntax_wcard(pdp->reqpath, p, ppath_contains_wcard);
172 } else {
173 status = check_path_syntax(pdp->reqpath, p);
174 }
175 }
176
177 if (!NT_STATUS_IS_OK(status)) {
178 DEBUG(10,("parse_dfs_path: '%s' failed with %s\n",
179 p, nt_errstr(status) ));
180 return status;
181 }
182
183 DEBUG(10,("parse_dfs_path: rest of the path: %s\n",pdp->reqpath));
184 return NT_STATUS_OK;
185}
186
187/********************************************************
188 Fake up a connection struct for the VFS layer.
189 Note this CHANGES CWD !!!! JRA.
190*********************************************************/
191
192static NTSTATUS create_conn_struct(connection_struct *conn, int snum, const char *path)
193{
194 pstring connpath;
195
196 ZERO_STRUCTP(conn);
197
198 pstrcpy(connpath, path);
199 pstring_sub(connpath , "%S", lp_servicename(snum));
200
201 /* needed for smbd_vfs_init() */
202
203 if ((conn->mem_ctx=talloc_init("connection_struct")) == NULL) {
204 DEBUG(0,("talloc_init(connection_struct) failed!\n"));
205 return NT_STATUS_NO_MEMORY;
206 }
207
208 if (!(conn->params = TALLOC_ZERO_P(conn->mem_ctx, struct share_params))) {
209 DEBUG(0, ("TALLOC failed\n"));
210 return NT_STATUS_NO_MEMORY;
211 }
212
213 conn->params->service = snum;
214
215 set_conn_connectpath(conn, connpath);
216
217 if (!smbd_vfs_init(conn)) {
218 NTSTATUS status = map_nt_error_from_unix(errno);
219 DEBUG(0,("create_conn_struct: smbd_vfs_init failed.\n"));
220 conn_free_internal(conn);
221 return status;
222 }
223
224 /*
225 * Windows seems to insist on doing trans2getdfsreferral() calls on the IPC$
226 * share as the anonymous user. If we try to chdir as that user we will
227 * fail.... WTF ? JRA.
228 */
229
230 if (vfs_ChDir(conn,conn->connectpath) != 0) {
231 NTSTATUS status = map_nt_error_from_unix(errno);
232 DEBUG(3,("create_conn_struct: Can't ChDir to new conn path %s. Error was %s\n",
233 conn->connectpath, strerror(errno) ));
234 conn_free_internal(conn);
235 return status;
236 }
237
238 return NT_STATUS_OK;
239}
240
241/**********************************************************************
242 Parse the contents of a symlink to verify if it is an msdfs referral
243 A valid referral is of the form:
244
245 msdfs:server1\share1,server2\share2
246 msdfs:server1\share1\pathname,server2\share2\pathname
247 msdfs:server1/share1,server2/share2
248 msdfs:server1/share1/pathname,server2/share2/pathname.
249
250 Note that the alternate paths returned here must be of the canonicalized
251 form:
252
253 \server\share or
254 \server\share\path\to\file,
255
256 even in posix path mode. This is because we have no knowledge if the
257 server we're referring to understands posix paths.
258 **********************************************************************/
259
260static BOOL parse_msdfs_symlink(TALLOC_CTX *ctx,
261 char *target,
262 struct referral **preflist,
263 int *refcount)
264{
265 pstring temp;
266 char *prot;
267 char *alt_path[MAX_REFERRAL_COUNT];
268 int count = 0, i;
269 struct referral *reflist;
270
271 pstrcpy(temp,target);
272 prot = strtok(temp,":");
273 if (!prot) {
274 DEBUG(0,("parse_msdfs_symlink: invalid path !\n"));
275 return False;
276 }
277
278 /* parse out the alternate paths */
279 while((count<MAX_REFERRAL_COUNT) &&
280 ((alt_path[count] = strtok(NULL,",")) != NULL)) {
281 count++;
282 }
283
284 DEBUG(10,("parse_msdfs_symlink: count=%d\n", count));
285
286 if (count) {
287 reflist = *preflist = TALLOC_ZERO_ARRAY(ctx, struct referral, count);
288 if(reflist == NULL) {
289 DEBUG(0,("parse_msdfs_symlink: talloc failed!\n"));
290 return False;
291 }
292 } else {
293 reflist = *preflist = NULL;
294 }
295
296 for(i=0;i<count;i++) {
297 char *p;
298
299 /* Canonicalize link target. Replace all /'s in the path by a \ */
300 string_replace(alt_path[i], '/', '\\');
301
302 /* Remove leading '\\'s */
303 p = alt_path[i];
304 while (*p && (*p == '\\')) {
305 p++;
306 }
307
308 pstrcpy(reflist[i].alternate_path, "\\");
309 pstrcat(reflist[i].alternate_path, p);
310
311 reflist[i].proximity = 0;
312 reflist[i].ttl = REFERRAL_TTL;
313 DEBUG(10, ("parse_msdfs_symlink: Created alt path: %s\n", reflist[i].alternate_path));
314 *refcount += 1;
315 }
316
317 return True;
318}
319
320/**********************************************************************
321 Returns true if the unix path is a valid msdfs symlink and also
322 returns the target string from inside the link.
323**********************************************************************/
324
325BOOL is_msdfs_link(connection_struct *conn,
326 const char *path,
327 pstring link_target,
328 SMB_STRUCT_STAT *sbufp)
329{
330 SMB_STRUCT_STAT st;
331 int referral_len = 0;
332
333 if (sbufp == NULL) {
334 sbufp = &st;
335 }
336
337 if (SMB_VFS_LSTAT(conn, path, sbufp) != 0) {
338 DEBUG(5,("is_msdfs_link: %s does not exist.\n",path));
339 return False;
340 }
341
342 if (!S_ISLNK(sbufp->st_mode)) {
343 DEBUG(5,("is_msdfs_link: %s is not a link.\n",path));
344 return False;
345 }
346
347 /* open the link and read it */
348 referral_len = SMB_VFS_READLINK(conn, path, link_target, sizeof(pstring)-1);
349 if (referral_len == -1) {
350 DEBUG(0,("is_msdfs_link: Error reading msdfs link %s: %s\n",
351 path, strerror(errno)));
352 return False;
353 }
354 link_target[referral_len] = '\0';
355
356 DEBUG(5,("is_msdfs_link: %s -> %s\n",path, link_target));
357
358 if (!strnequal(link_target, "msdfs:", 6)) {
359 return False;
360 }
361 return True;
362}
363
364/*****************************************************************
365 Used by other functions to decide if a dfs path is remote,
366 and to get the list of referred locations for that remote path.
367
368 search_flag: For findfirsts, dfs links themselves are not
369 redirected, but paths beyond the links are. For normal smb calls,
370 even dfs links need to be redirected.
371
372 consumedcntp: how much of the dfs path is being redirected. the client
373 should try the remaining path on the redirected server.
374
375 If this returns NT_STATUS_PATH_NOT_COVERED the contents of the msdfs
376 link redirect are in targetpath.
377*****************************************************************/
378
379static NTSTATUS dfs_path_lookup(connection_struct *conn,
380 const char *dfspath, /* Incoming complete dfs path */
381 const struct dfs_path *pdp, /* Parsed out server+share+extrapath. */
382 BOOL search_flag, /* Called from a findfirst ? */
383 int *consumedcntp,
384 pstring targetpath)
385{
386 char *p = NULL;
387 char *q = NULL;
388 SMB_STRUCT_STAT sbuf;
389 NTSTATUS status;
390 pstring localpath;
391 pstring canon_dfspath; /* Canonicalized dfs path. (only '/' components). */
392
393 DEBUG(10,("dfs_path_lookup: Conn path = %s reqpath = %s\n",
394 conn->connectpath, pdp->reqpath));
395
396 /*
397 * Note the unix path conversion here we're doing we can
398 * throw away. We're looking for a symlink for a dfs
399 * resolution, if we don't find it we'll do another
400 * unix_convert later in the codepath.
401 * If we needed to remember what we'd resolved in
402 * dp->reqpath (as the original code did) we'd
403 * pstrcpy(localhost, dp->reqpath) on any code
404 * path below that returns True - but I don't
405 * think this is needed. JRA.
406 */
407
408 pstrcpy(localpath, pdp->reqpath);
409 status = unix_convert(conn, localpath, search_flag, NULL, &sbuf);
410 if (!NT_STATUS_IS_OK(status) && !NT_STATUS_EQUAL(status,
411 NT_STATUS_OBJECT_PATH_NOT_FOUND)) {
412 return status;
413 }
414
415 /* Optimization - check if we can redirect the whole path. */
416
417 if (is_msdfs_link(conn, localpath, targetpath, NULL)) {
418 if (search_flag) {
419 DEBUG(6,("dfs_path_lookup (FindFirst) No redirection "
420 "for dfs link %s.\n", dfspath));
421 return NT_STATUS_OK;
422 }
423
424 DEBUG(6,("dfs_path_lookup: %s resolves to a "
425 "valid dfs link %s.\n", dfspath, targetpath));
426
427 if (consumedcntp) {
428 *consumedcntp = strlen(dfspath);
429 }
430 return NT_STATUS_PATH_NOT_COVERED;
431 }
432
433 /* Prepare to test only for '/' components in the given path,
434 * so if a Windows path replace all '\\' characters with '/'.
435 * For a POSIX DFS path we know all separators are already '/'. */
436
437 pstrcpy(canon_dfspath, dfspath);
438 if (!pdp->posix_path) {
439 string_replace(canon_dfspath, '\\', '/');
440 }
441
442 /*
443 * localpath comes out of unix_convert, so it has
444 * no trailing backslash. Make sure that canon_dfspath hasn't either.
445 * Fix for bug #4860 from Jan Martin <Jan.Martin@rwedea.com>.
446 */
447
448 trim_char(canon_dfspath,0,'/');
449
450 /*
451 * Redirect if any component in the path is a link.
452 * We do this by walking backwards through the
453 * local path, chopping off the last component
454 * in both the local path and the canonicalized
455 * DFS path. If we hit a DFS link then we're done.
456 */
457
458 p = strrchr_m(localpath, '/');
459 if (consumedcntp) {
460 q = strrchr_m(canon_dfspath, '/');
461 }
462
463 while (p) {
464 *p = '\0';
465 if (q) {
466 *q = '\0';
467 }
468
469 if (is_msdfs_link(conn, localpath, targetpath, NULL)) {
470 DEBUG(4, ("dfs_path_lookup: Redirecting %s because "
471 "parent %s is dfs link\n", dfspath, localpath));
472
473 if (consumedcntp) {
474 *consumedcntp = strlen(canon_dfspath);
475 DEBUG(10, ("dfs_path_lookup: Path consumed: %s "
476 "(%d)\n", canon_dfspath, *consumedcntp));
477 }
478
479 return NT_STATUS_PATH_NOT_COVERED;
480 }
481
482 /* Step back on the filesystem. */
483 p = strrchr_m(localpath, '/');
484
485 if (consumedcntp) {
486 /* And in the canonicalized dfs path. */
487 q = strrchr_m(canon_dfspath, '/');
488 }
489 }
490
491 return NT_STATUS_OK;
492}
493
494/*****************************************************************
495 Decides if a dfs pathname should be redirected or not.
496 If not, the pathname is converted to a tcon-relative local unix path
497
498 search_wcard_flag: this flag performs 2 functions bother related
499 to searches. See resolve_dfs_path() and parse_dfs_path_XX()
500 for details.
501
502 This function can return NT_STATUS_OK, meaning use the returned path as-is
503 (mapped into a local path).
504 or NT_STATUS_NOT_COVERED meaning return a DFS redirect, or
505 any other NT_STATUS error which is a genuine error to be
506 returned to the client.
507*****************************************************************/
508
509static NTSTATUS dfs_redirect( connection_struct *conn,
510 pstring dfs_path,
511 BOOL search_wcard_flag,
512 BOOL *ppath_contains_wcard)
513{
514 NTSTATUS status;
515 struct dfs_path dp;
516 pstring targetpath;
517
518 status = parse_dfs_path(conn, dfs_path, search_wcard_flag, &dp, ppath_contains_wcard);
519 if (!NT_STATUS_IS_OK(status)) {
520 return status;
521 }
522
523 if (dp.reqpath[0] == '\0') {
524 pstrcpy(dfs_path, dp.reqpath);
525 DEBUG(5,("dfs_redirect: self-referral.\n"));
526 return NT_STATUS_OK;
527 }
528
529 /* If dfs pathname for a non-dfs share, convert to tcon-relative
530 path and return OK */
531
532 if (!lp_msdfs_root(SNUM(conn))) {
533 pstrcpy(dfs_path, dp.reqpath);
534 return NT_STATUS_OK;
535 }
536
537 /* If it looked like a local path (zero hostname/servicename)
538 * just treat as a tcon-relative path. */
539
540 if (dp.hostname[0] == '\0' && dp.servicename[0] == '\0') {
541 pstrcpy(dfs_path, dp.reqpath);
542 return NT_STATUS_OK;
543 }
544
545 status = dfs_path_lookup(conn, dfs_path, &dp,
546 search_wcard_flag, NULL, targetpath);
547 if (!NT_STATUS_IS_OK(status)) {
548 if (NT_STATUS_EQUAL(status, NT_STATUS_PATH_NOT_COVERED)) {
549 DEBUG(3,("dfs_redirect: Redirecting %s\n", dfs_path));
550 } else {
551 DEBUG(10,("dfs_redirect: dfs_path_lookup failed for %s with %s\n",
552 dfs_path, nt_errstr(status) ));
553 }
554 return status;
555 }
556
557 DEBUG(3,("dfs_redirect: Not redirecting %s.\n", dfs_path));
558
559 /* Form non-dfs tcon-relative path */
560 pstrcpy(dfs_path, dp.reqpath);
561
562 DEBUG(3,("dfs_redirect: Path converted to non-dfs path %s\n", dfs_path));
563 return NT_STATUS_OK;
564}
565
566/**********************************************************************
567 Return a self referral.
568**********************************************************************/
569
570static NTSTATUS self_ref(TALLOC_CTX *ctx,
571 const char *dfs_path,
572 struct junction_map *jucn,
573 int *consumedcntp,
574 BOOL *self_referralp)
575{
576 struct referral *ref;
577
578 *self_referralp = True;
579
580 jucn->referral_count = 1;
581 if((ref = TALLOC_ZERO_P(ctx, struct referral)) == NULL) {
582 DEBUG(0,("self_ref: talloc failed for referral\n"));
583 return NT_STATUS_NO_MEMORY;
584 }
585
586 pstrcpy(ref->alternate_path,dfs_path);
587 ref->proximity = 0;
588 ref->ttl = REFERRAL_TTL;
589 jucn->referral_list = ref;
590 *consumedcntp = strlen(dfs_path);
591 return NT_STATUS_OK;
592}
593
594/**********************************************************************
595 Gets valid referrals for a dfs path and fills up the
596 junction_map structure.
597**********************************************************************/
598
599NTSTATUS get_referred_path(TALLOC_CTX *ctx,
600 const char *dfs_path,
601 struct junction_map *jucn,
602 int *consumedcntp,
603 BOOL *self_referralp)
604{
605 struct connection_struct conns;
606 struct connection_struct *conn = &conns;
607 struct dfs_path dp;
608 pstring conn_path;
609 pstring targetpath;
610 int snum;
611 NTSTATUS status = NT_STATUS_NOT_FOUND;
612 BOOL dummy;
613
614 ZERO_STRUCT(conns);
615
616 *self_referralp = False;
617
618 status = parse_dfs_path(NULL, dfs_path, False, &dp, &dummy);
619 if (!NT_STATUS_IS_OK(status)) {
620 return status;
621 }
622
623 fstrcpy(jucn->service_name, dp.servicename);
624 pstrcpy(jucn->volume_name, dp.reqpath);
625
626 /* Verify the share is a dfs root */
627 snum = lp_servicenumber(jucn->service_name);
628 if(snum < 0) {
629 if ((snum = find_service(jucn->service_name)) < 0) {
630 return NT_STATUS_NOT_FOUND;
631 }
632 }
633
634 if (!lp_msdfs_root(snum)) {
635 DEBUG(3,("get_referred_path: |%s| in dfs path %s is not a dfs root.\n",
636 dp.servicename, dfs_path));
637 return NT_STATUS_NOT_FOUND;
638 }
639
640 /*
641 * Self referrals are tested with a anonymous IPC connection and
642 * a GET_DFS_REFERRAL call to \\server\share. (which means dp.reqpath[0] points
643 * to an empty string). create_conn_struct cd's into the directory and will
644 * fail if it cannot (as the anonymous user). Cope with this.
645 */
646
647 if (dp.reqpath[0] == '\0') {
648 struct referral *ref;
649
650 if (*lp_msdfs_proxy(snum) == '\0') {
651 return self_ref(ctx,
652 dfs_path,
653 jucn,
654 consumedcntp,
655 self_referralp);
656 }
657
658 /*
659 * It's an msdfs proxy share. Redirect to
660 * the configured target share.
661 */
662
663 jucn->referral_count = 1;
664 if ((ref = TALLOC_ZERO_P(ctx, struct referral)) == NULL) {
665 DEBUG(0, ("malloc failed for referral\n"));
666 return NT_STATUS_NO_MEMORY;
667 }
668
669 pstrcpy(ref->alternate_path, lp_msdfs_proxy(snum));
670 if (dp.reqpath[0] != '\0') {
671 pstrcat(ref->alternate_path, dp.reqpath);
672 }
673 ref->proximity = 0;
674 ref->ttl = REFERRAL_TTL;
675 jucn->referral_list = ref;
676 *consumedcntp = strlen(dfs_path);
677 return NT_STATUS_OK;
678 }
679
680 pstrcpy(conn_path, lp_pathname(snum));
681 status = create_conn_struct(conn, snum, conn_path);
682 if (!NT_STATUS_IS_OK(status)) {
683 return status;
684 }
685
686 /* If this is a DFS path dfs_lookup should return
687 * NT_STATUS_PATH_NOT_COVERED. */
688
689 status = dfs_path_lookup(conn, dfs_path, &dp,
690 False, consumedcntp, targetpath);
691
692 if (!NT_STATUS_EQUAL(status, NT_STATUS_PATH_NOT_COVERED)) {
693 DEBUG(3,("get_referred_path: No valid referrals for path %s\n",
694 dfs_path));
695 conn_free_internal(conn);
696 return status;
697 }
698
699 /* We know this is a valid dfs link. Parse the targetpath. */
700 if (!parse_msdfs_symlink(ctx, targetpath,
701 &jucn->referral_list,
702 &jucn->referral_count)) {
703 DEBUG(3,("get_referred_path: failed to parse symlink "
704 "target %s\n", targetpath ));
705 conn_free_internal(conn);
706 return NT_STATUS_NOT_FOUND;
707 }
708
709 conn_free_internal(conn);
710 return NT_STATUS_OK;
711}
712
713static int setup_ver2_dfs_referral(const char *pathname,
714 char **ppdata,
715 struct junction_map *junction,
716 int consumedcnt,
717 BOOL self_referral)
718{
719 char* pdata = *ppdata;
720
721 unsigned char uni_requestedpath[1024];
722 int uni_reqpathoffset1,uni_reqpathoffset2;
723 int uni_curroffset;
724 int requestedpathlen=0;
725 int offset;
726 int reply_size = 0;
727 int i=0;
728
729 DEBUG(10,("Setting up version2 referral\nRequested path:\n"));
730
731 requestedpathlen = rpcstr_push(uni_requestedpath, pathname, sizeof(pstring),
732 STR_TERMINATE);
733
734 if (DEBUGLVL(10)) {
735 dump_data(0, (const char *) uni_requestedpath,requestedpathlen);
736 }
737
738 DEBUG(10,("ref count = %u\n",junction->referral_count));
739
740 uni_reqpathoffset1 = REFERRAL_HEADER_SIZE +
741 VERSION2_REFERRAL_SIZE * junction->referral_count;
742
743 uni_reqpathoffset2 = uni_reqpathoffset1 + requestedpathlen;
744
745 uni_curroffset = uni_reqpathoffset2 + requestedpathlen;
746
747 reply_size = REFERRAL_HEADER_SIZE + VERSION2_REFERRAL_SIZE*junction->referral_count +
748 2 * requestedpathlen;
749 DEBUG(10,("reply_size: %u\n",reply_size));
750
751 /* add up the unicode lengths of all the referral paths */
752 for(i=0;i<junction->referral_count;i++) {
753 DEBUG(10,("referral %u : %s\n",i,junction->referral_list[i].alternate_path));
754 reply_size += (strlen(junction->referral_list[i].alternate_path)+1)*2;
755 }
756
757 DEBUG(10,("reply_size = %u\n",reply_size));
758 /* add the unexplained 0x16 bytes */
759 reply_size += 0x16;
760
761 pdata = (char *)SMB_REALLOC(pdata,reply_size);
762 if(pdata == NULL) {
763 DEBUG(0,("Realloc failed!\n"));
764 return -1;
765 }
766 *ppdata = pdata;
767
768 /* copy in the dfs requested paths.. required for offset calculations */
769 memcpy(pdata+uni_reqpathoffset1,uni_requestedpath,requestedpathlen);
770 memcpy(pdata+uni_reqpathoffset2,uni_requestedpath,requestedpathlen);
771
772 /* create the header */
773 SSVAL(pdata,0,consumedcnt * 2); /* path consumed */
774 SSVAL(pdata,2,junction->referral_count); /* number of referral in this pkt */
775 if(self_referral) {
776 SIVAL(pdata,4,DFSREF_REFERRAL_SERVER | DFSREF_STORAGE_SERVER);
777 } else {
778 SIVAL(pdata,4,DFSREF_STORAGE_SERVER);
779 }
780
781 offset = 8;
782 /* add the referral elements */
783 for(i=0;i<junction->referral_count;i++) {
784 struct referral* ref = &junction->referral_list[i];
785 int unilen;
786
787 SSVAL(pdata,offset,2); /* version 2 */
788 SSVAL(pdata,offset+2,VERSION2_REFERRAL_SIZE);
789 if(self_referral) {
790 SSVAL(pdata,offset+4,1);
791 } else {
792 SSVAL(pdata,offset+4,0);
793 }
794 SSVAL(pdata,offset+6,0); /* ref_flags :use path_consumed bytes? */
795 SIVAL(pdata,offset+8,ref->proximity);
796 SIVAL(pdata,offset+12,ref->ttl);
797
798 SSVAL(pdata,offset+16,uni_reqpathoffset1-offset);
799 SSVAL(pdata,offset+18,uni_reqpathoffset2-offset);
800 /* copy referred path into current offset */
801 unilen = rpcstr_push(pdata+uni_curroffset, ref->alternate_path,
802 sizeof(pstring), STR_UNICODE);
803
804 SSVAL(pdata,offset+20,uni_curroffset-offset);
805
806 uni_curroffset += unilen;
807 offset += VERSION2_REFERRAL_SIZE;
808 }
809 /* add in the unexplained 22 (0x16) bytes at the end */
810 memset(pdata+uni_curroffset,'\0',0x16);
811 return reply_size;
812}
813
814static int setup_ver3_dfs_referral(const char *pathname,
815 char **ppdata,
816 struct junction_map *junction,
817 int consumedcnt,
818 BOOL self_referral)
819{
820 char* pdata = *ppdata;
821
822 unsigned char uni_reqpath[1024];
823 int uni_reqpathoffset1, uni_reqpathoffset2;
824 int uni_curroffset;
825 int reply_size = 0;
826
827 int reqpathlen = 0;
828 int offset,i=0;
829
830 DEBUG(10,("setting up version3 referral\n"));
831
832 reqpathlen = rpcstr_push(uni_reqpath, pathname, sizeof(pstring), STR_TERMINATE);
833
834 if (DEBUGLVL(10)) {
835 dump_data(0, (char *) uni_reqpath,reqpathlen);
836 }
837
838 uni_reqpathoffset1 = REFERRAL_HEADER_SIZE + VERSION3_REFERRAL_SIZE * junction->referral_count;
839 uni_reqpathoffset2 = uni_reqpathoffset1 + reqpathlen;
840 reply_size = uni_curroffset = uni_reqpathoffset2 + reqpathlen;
841
842 for(i=0;i<junction->referral_count;i++) {
843 DEBUG(10,("referral %u : %s\n",i,junction->referral_list[i].alternate_path));
844 reply_size += (strlen(junction->referral_list[i].alternate_path)+1)*2;
845 }
846
847 pdata = (char *)SMB_REALLOC(pdata,reply_size);
848 if(pdata == NULL) {
849 DEBUG(0,("version3 referral setup: malloc failed for Realloc!\n"));
850 return -1;
851 }
852 *ppdata = pdata;
853
854 /* create the header */
855 SSVAL(pdata,0,consumedcnt * 2); /* path consumed */
856 SSVAL(pdata,2,junction->referral_count); /* number of referral */
857 if(self_referral) {
858 SIVAL(pdata,4,DFSREF_REFERRAL_SERVER | DFSREF_STORAGE_SERVER);
859 } else {
860 SIVAL(pdata,4,DFSREF_STORAGE_SERVER);
861 }
862
863 /* copy in the reqpaths */
864 memcpy(pdata+uni_reqpathoffset1,uni_reqpath,reqpathlen);
865 memcpy(pdata+uni_reqpathoffset2,uni_reqpath,reqpathlen);
866
867 offset = 8;
868 for(i=0;i<junction->referral_count;i++) {
869 struct referral* ref = &(junction->referral_list[i]);
870 int unilen;
871
872 SSVAL(pdata,offset,3); /* version 3 */
873 SSVAL(pdata,offset+2,VERSION3_REFERRAL_SIZE);
874 if(self_referral) {
875 SSVAL(pdata,offset+4,1);
876 } else {
877 SSVAL(pdata,offset+4,0);
878 }
879
880 SSVAL(pdata,offset+6,0); /* ref_flags :use path_consumed bytes? */
881 SIVAL(pdata,offset+8,ref->ttl);
882
883 SSVAL(pdata,offset+12,uni_reqpathoffset1-offset);
884 SSVAL(pdata,offset+14,uni_reqpathoffset2-offset);
885 /* copy referred path into current offset */
886 unilen = rpcstr_push(pdata+uni_curroffset,ref->alternate_path,
887 sizeof(pstring), STR_UNICODE | STR_TERMINATE);
888 SSVAL(pdata,offset+16,uni_curroffset-offset);
889 /* copy 0x10 bytes of 00's in the ServiceSite GUID */
890 memset(pdata+offset+18,'\0',16);
891
892 uni_curroffset += unilen;
893 offset += VERSION3_REFERRAL_SIZE;
894 }
895 return reply_size;
896}
897
898/******************************************************************
899 Set up the DFS referral for the dfs pathname. This call returns
900 the amount of the path covered by this server, and where the
901 client should be redirected to. This is the meat of the
902 TRANS2_GET_DFS_REFERRAL call.
903******************************************************************/
904
905int setup_dfs_referral(connection_struct *orig_conn,
906 const char *dfs_path,
907 int max_referral_level,
908 char **ppdata, NTSTATUS *pstatus)
909{
910 struct junction_map junction;
911 int consumedcnt = 0;
912 BOOL self_referral = False;
913 int reply_size = 0;
914 char *pathnamep = NULL;
915 pstring local_dfs_path;
916 TALLOC_CTX *ctx;
917
918 if (!(ctx=talloc_init("setup_dfs_referral"))) {
919 *pstatus = NT_STATUS_NO_MEMORY;
920 return -1;
921 }
922
923 ZERO_STRUCT(junction);
924
925 /* get the junction entry */
926 if (!dfs_path) {
927 talloc_destroy(ctx);
928 *pstatus = NT_STATUS_NOT_FOUND;
929 return -1;
930 }
931
932 /*
933 * Trim pathname sent by client so it begins with only one backslash.
934 * Two backslashes confuse some dfs clients
935 */
936
937 pstrcpy(local_dfs_path, dfs_path);
938 pathnamep = local_dfs_path;
939 while (IS_DIRECTORY_SEP(pathnamep[0]) && IS_DIRECTORY_SEP(pathnamep[1])) {
940 pathnamep++;
941 }
942
943 /* The following call can change cwd. */
944 *pstatus = get_referred_path(ctx, pathnamep, &junction, &consumedcnt, &self_referral);
945 if (!NT_STATUS_IS_OK(*pstatus)) {
946 vfs_ChDir(orig_conn,orig_conn->connectpath);
947 talloc_destroy(ctx);
948 return -1;
949 }
950 vfs_ChDir(orig_conn,orig_conn->connectpath);
951
952 if (!self_referral) {
953 pathnamep[consumedcnt] = '\0';
954
955 if( DEBUGLVL( 3 ) ) {
956 int i=0;
957 dbgtext("setup_dfs_referral: Path %s to alternate path(s):",pathnamep);
958 for(i=0;i<junction.referral_count;i++)
959 dbgtext(" %s",junction.referral_list[i].alternate_path);
960 dbgtext(".\n");
961 }
962 }
963
964 /* create the referral depeding on version */
965 DEBUG(10,("max_referral_level :%d\n",max_referral_level));
966
967 if (max_referral_level < 2) {
968 max_referral_level = 2;
969 }
970 if (max_referral_level > 3) {
971 max_referral_level = 3;
972 }
973
974 switch(max_referral_level) {
975 case 2:
976 reply_size = setup_ver2_dfs_referral(pathnamep, ppdata, &junction,
977 consumedcnt, self_referral);
978 break;
979 case 3:
980 reply_size = setup_ver3_dfs_referral(pathnamep, ppdata, &junction,
981 consumedcnt, self_referral);
982 break;
983 default:
984 DEBUG(0,("setup_dfs_referral: Invalid dfs referral version: %d\n", max_referral_level));
985 talloc_destroy(ctx);
986 *pstatus = NT_STATUS_INVALID_LEVEL;
987 return -1;
988 }
989
990 if (DEBUGLVL(10)) {
991 DEBUGADD(0,("DFS Referral pdata:\n"));
992 dump_data(0,*ppdata,reply_size);
993 }
994
995 talloc_destroy(ctx);
996 *pstatus = NT_STATUS_OK;
997 return reply_size;
998}
999
1000/**********************************************************************
1001 The following functions are called by the NETDFS RPC pipe functions
1002 **********************************************************************/
1003
1004/*********************************************************************
1005 Creates a junction structure from a DFS pathname
1006**********************************************************************/
1007
1008BOOL create_junction(const char *dfs_path, struct junction_map *jucn)
1009{
1010 int snum;
1011 BOOL dummy;
1012 struct dfs_path dp;
1013
1014 NTSTATUS status = parse_dfs_path(NULL, dfs_path, False, &dp, &dummy);
1015
1016 if (!NT_STATUS_IS_OK(status)) {
1017 return False;
1018 }
1019
1020 /* check if path is dfs : validate first token */
1021 if (!is_myname_or_ipaddr(dp.hostname)) {
1022 DEBUG(4,("create_junction: Invalid hostname %s in dfs path %s\n",
1023 dp.hostname, dfs_path));
1024 return False;
1025 }
1026
1027 /* Check for a non-DFS share */
1028 snum = lp_servicenumber(dp.servicename);
1029
1030 if(snum < 0 || !lp_msdfs_root(snum)) {
1031 DEBUG(4,("create_junction: %s is not an msdfs root.\n",
1032 dp.servicename));
1033 return False;
1034 }
1035
1036 fstrcpy(jucn->service_name,dp.servicename);
1037 pstrcpy(jucn->volume_name,dp.reqpath);
1038 pstrcpy(jucn->comment, lp_comment(snum));
1039 return True;
1040}
1041
1042/**********************************************************************
1043 Forms a valid Unix pathname from the junction
1044 **********************************************************************/
1045
1046static BOOL junction_to_local_path(struct junction_map *jucn,
1047 char *path,
1048 int max_pathlen,
1049 connection_struct *conn_out)
1050{
1051 int snum;
1052 pstring conn_path;
1053
1054 snum = lp_servicenumber(jucn->service_name);
1055 if(snum < 0) {
1056 return False;
1057 }
1058
1059 safe_strcpy(path, lp_pathname(snum), max_pathlen-1);
1060 safe_strcat(path, "/", max_pathlen-1);
1061 safe_strcat(path, jucn->volume_name, max_pathlen-1);
1062
1063 pstrcpy(conn_path, lp_pathname(snum));
1064 if (!NT_STATUS_IS_OK(create_conn_struct(conn_out, snum, conn_path))) {
1065 return False;
1066 }
1067
1068 return True;
1069}
1070
1071BOOL create_msdfs_link(struct junction_map *jucn, BOOL exists)
1072{
1073 pstring path;
1074 pstring msdfs_link;
1075 connection_struct conns;
1076 connection_struct *conn = &conns;
1077 int i=0;
1078 BOOL insert_comma = False;
1079 BOOL ret = False;
1080
1081 ZERO_STRUCT(conns);
1082
1083 if(!junction_to_local_path(jucn, path, sizeof(path), conn)) {
1084 return False;
1085 }
1086
1087 /* Form the msdfs_link contents */
1088 pstrcpy(msdfs_link, "msdfs:");
1089 for(i=0; i<jucn->referral_count; i++) {
1090 char* refpath = jucn->referral_list[i].alternate_path;
1091
1092 /* Alternate paths always use Windows separators. */
1093 trim_char(refpath, '\\', '\\');
1094 if(*refpath == '\0') {
1095 if (i == 0) {
1096 insert_comma = False;
1097 }
1098 continue;
1099 }
1100 if (i > 0 && insert_comma) {
1101 pstrcat(msdfs_link, ",");
1102 }
1103
1104 pstrcat(msdfs_link, refpath);
1105 if (!insert_comma) {
1106 insert_comma = True;
1107 }
1108 }
1109
1110 DEBUG(5,("create_msdfs_link: Creating new msdfs link: %s -> %s\n",
1111 path, msdfs_link));
1112
1113 if(exists) {
1114 if(SMB_VFS_UNLINK(conn,path)!=0) {
1115 goto out;
1116 }
1117 }
1118
1119 if(SMB_VFS_SYMLINK(conn, msdfs_link, path) < 0) {
1120 DEBUG(1,("create_msdfs_link: symlink failed %s -> %s\nError: %s\n",
1121 path, msdfs_link, strerror(errno)));
1122 goto out;
1123 }
1124
1125
1126 ret = True;
1127
1128out:
1129
1130 conn_free_internal(conn);
1131 return ret;
1132}
1133
1134BOOL remove_msdfs_link(struct junction_map *jucn)
1135{
1136 pstring path;
1137 connection_struct conns;
1138 connection_struct *conn = &conns;
1139 BOOL ret = False;
1140
1141 ZERO_STRUCT(conns);
1142
1143 if( junction_to_local_path(jucn, path, sizeof(path), conn) ) {
1144 if( SMB_VFS_UNLINK(conn, path) == 0 ) {
1145 ret = True;
1146 }
1147 talloc_destroy( conn->mem_ctx );
1148 }
1149
1150 conn_free_internal(conn);
1151 return ret;
1152}
1153
1154static int form_junctions(TALLOC_CTX *ctx,
1155 int snum,
1156 struct junction_map *jucn,
1157 int jn_remain)
1158{
1159 int cnt = 0;
1160 SMB_STRUCT_DIR *dirp;
1161 char *dname;
1162 pstring connect_path;
1163 char *service_name = lp_servicename(snum);
1164 connection_struct conn;
1165 struct referral *ref = NULL;
1166
1167 ZERO_STRUCT(conn);
1168
1169 if (jn_remain <= 0) {
1170 return 0;
1171 }
1172
1173 pstrcpy(connect_path,lp_pathname(snum));
1174
1175 if(*connect_path == '\0') {
1176 return 0;
1177 }
1178
1179 /*
1180 * Fake up a connection struct for the VFS layer.
1181 */
1182
1183 if (!NT_STATUS_IS_OK(create_conn_struct(&conn, snum, connect_path))) {
1184 return 0;
1185 }
1186
1187 /* form a junction for the msdfs root - convention
1188 DO NOT REMOVE THIS: NT clients will not work with us
1189 if this is not present
1190 */
1191 fstrcpy(jucn[cnt].service_name, service_name);
1192 jucn[cnt].volume_name[0] = '\0';
1193 jucn[cnt].referral_count = 1;
1194
1195 ref = jucn[cnt].referral_list = TALLOC_ZERO_P(ctx, struct referral);
1196 if (jucn[cnt].referral_list == NULL) {
1197 DEBUG(0, ("talloc failed!\n"));
1198 goto out;
1199 }
1200
1201 ref->proximity = 0;
1202 ref->ttl = REFERRAL_TTL;
1203 if (*lp_msdfs_proxy(snum) != '\0') {
1204 pstrcpy(ref->alternate_path, lp_msdfs_proxy(snum));
1205 cnt++;
1206 goto out;
1207 }
1208
1209 pstr_sprintf(ref->alternate_path, "\\\\%s\\%s",
1210 get_local_machine_name(),
1211 service_name);
1212 cnt++;
1213
1214 /* Now enumerate all dfs links */
1215 dirp = SMB_VFS_OPENDIR(&conn, ".", NULL, 0);
1216 if(!dirp) {
1217 goto out;
1218 }
1219
1220 while ((dname = vfs_readdirname(&conn, dirp)) != NULL) {
1221 pstring link_target;
1222 if (cnt >= jn_remain) {
1223 SMB_VFS_CLOSEDIR(&conn,dirp);
1224 DEBUG(2, ("ran out of MSDFS junction slots"));
1225 goto out;
1226 }
1227 if (is_msdfs_link(&conn, dname, link_target, NULL)) {
1228 if (parse_msdfs_symlink(ctx,
1229 link_target,
1230 &jucn[cnt].referral_list,
1231 &jucn[cnt].referral_count)) {
1232
1233 fstrcpy(jucn[cnt].service_name, service_name);
1234 pstrcpy(jucn[cnt].volume_name, dname);
1235 cnt++;
1236 }
1237 }
1238 }
1239
1240 SMB_VFS_CLOSEDIR(&conn,dirp);
1241
1242out:
1243
1244 conn_free_internal(&conn);
1245 return cnt;
1246}
1247
1248int enum_msdfs_links(TALLOC_CTX *ctx, struct junction_map *jucn, int jn_max)
1249{
1250 int i=0;
1251 int sharecount = 0;
1252 int jn_count = 0;
1253
1254 if(!lp_host_msdfs()) {
1255 return 0;
1256 }
1257
1258 /* Ensure all the usershares are loaded. */
1259 become_root();
1260 sharecount = load_usershare_shares();
1261 unbecome_root();
1262
1263 for(i=0;i < sharecount && (jn_max - jn_count) > 0;i++) {
1264 if(lp_msdfs_root(i)) {
1265 jn_count += form_junctions(ctx, i,jucn,jn_max - jn_count);
1266 }
1267 }
1268 return jn_count;
1269}
1270
1271/******************************************************************************
1272 Core function to resolve a dfs pathname.
1273******************************************************************************/
1274
1275NTSTATUS resolve_dfspath(connection_struct *conn, BOOL dfs_pathnames, pstring name)
1276{
1277 NTSTATUS status = NT_STATUS_OK;
1278 BOOL dummy;
1279 if (dfs_pathnames) {
1280 status = dfs_redirect(conn, name, False, &dummy);
1281 }
1282 return status;
1283}
1284
1285/******************************************************************************
1286 Core function to resolve a dfs pathname possibly containing a wildcard.
1287 This function is identical to the above except for the BOOL param to
1288 dfs_redirect but I need this to be separate so it's really clear when
1289 we're allowing wildcards and when we're not. JRA.
1290******************************************************************************/
1291
1292NTSTATUS resolve_dfspath_wcard(connection_struct *conn, BOOL dfs_pathnames, pstring name, BOOL *ppath_contains_wcard)
1293{
1294 NTSTATUS status = NT_STATUS_OK;
1295 if (dfs_pathnames) {
1296 status = dfs_redirect(conn, name, True, ppath_contains_wcard);
1297 }
1298 return status;
1299}
Note: See TracBrowser for help on using the repository browser.