source: trunk/samba-3.0.25pre1/source/smbd/msdfs.c@ 7

Last change on this file since 7 was 1, checked in by Paul Smedley, 18 years ago

Initial code import

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