~zulcss/samba/server-dailies-3.4

« back to all changes in this revision

Viewing changes to source3/smbd/msdfs.c

  • Committer: Chuck Short
  • Date: 2010-09-28 20:38:39 UTC
  • Revision ID: zulcss@ubuntu.com-20100928203839-pgjulytsi9ue63x1
Initial version

Show diffs side-by-side

added added

removed removed

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