source: trunk/samba/source/smbd/msdfs.c @ 26

Last change on this file since 26 was 26, checked in by Paul Smedley, 14 years ago

Updated source to 3.0.25rc1

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