2
Unix SMB/Netbios implementation.
4
MSDFS services for Samba
5
Copyright (C) Shirish Kalele 2000
6
Copyright (C) Jeremy Allison 2007
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.
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.
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/>.
23
#define DBGC_CLASS DBGC_MSDFS
25
#include "smbd/globals.h"
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.
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....
38
If conn != NULL then ensure the provided service is
39
the one pointed to by the connection.
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....
45
**********************************************************************/
47
static NTSTATUS parse_dfs_path(connection_struct *conn,
50
struct dfs_path *pdp, /* MUST BE TALLOCED */
51
bool *ppath_contains_wcard)
57
NTSTATUS status = NT_STATUS_OK;
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.
68
pathname_local = talloc_strdup(pdp, pathname);
69
if (!pathname_local) {
70
return NT_STATUS_NO_MEMORY;
72
/* Get a pointer to the terminating '\0' */
73
eos_ptr = &pathname_local[strlen(pathname_local)];
74
p = temp = pathname_local;
76
pdp->posix_path = (lp_posix_pathnames() && *pathname == '/');
78
sepchar = pdp->posix_path ? '/' : '\\';
80
if (*pathname != sepchar) {
81
DEBUG(10,("parse_dfs_path: path %s doesn't start with %c\n",
84
* Possibly client sent a local path by mistake.
85
* Try and convert to a local path.
88
pdp->hostname = eos_ptr; /* "" */
89
pdp->servicename = eos_ptr; /* "" */
91
/* We've got no info about separators. */
92
pdp->posix_path = lp_posix_pathnames();
94
DEBUG(10,("parse_dfs_path: trying to convert %s to a "
101
* Safe to use on talloc'ed string as it only shrinks.
102
* It also doesn't affect the eos_ptr.
104
trim_char(temp,sepchar,sepchar);
106
DEBUG(10,("parse_dfs_path: temp = |%s| after trimming %c's\n",
110
/* Parse out hostname. */
111
p = strchr_m(temp,sepchar);
113
DEBUG(10,("parse_dfs_path: can't parse hostname from path %s\n",
116
* Possibly client sent a local path by mistake.
117
* Try and convert to a local path.
120
pdp->hostname = eos_ptr; /* "" */
121
pdp->servicename = eos_ptr; /* "" */
124
DEBUG(10,("parse_dfs_path: trying to convert %s "
130
pdp->hostname = temp;
132
DEBUG(10,("parse_dfs_path: hostname: %s\n",pdp->hostname));
134
/* Parse out servicename. */
136
p = strchr_m(servicename,sepchar);
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",
150
* Possibly client sent a local path by mistake.
151
* Try and convert to a local path.
154
pdp->hostname = eos_ptr; /* "" */
155
pdp->servicename = eos_ptr; /* "" */
157
/* Repair the path - replace the sepchar's
160
*servicename = sepchar;
166
DEBUG(10,("parse_dfs_path: trying to convert %s "
172
pdp->servicename = servicename;
174
DEBUG(10,("parse_dfs_path: servicename: %s\n",pdp->servicename));
177
/* Client sent self referral \server\share. */
178
pdp->reqpath = eos_ptr; /* "" */
186
*ppath_contains_wcard = False;
190
/* Rest is reqpath. */
191
if (pdp->posix_path) {
192
status = check_path_syntax_posix(pdp->reqpath);
195
status = check_path_syntax_wcard(pdp->reqpath,
196
ppath_contains_wcard);
198
status = check_path_syntax(pdp->reqpath);
202
if (!NT_STATUS_IS_OK(status)) {
203
DEBUG(10,("parse_dfs_path: '%s' failed with %s\n",
204
p, nt_errstr(status) ));
208
DEBUG(10,("parse_dfs_path: rest of the path: %s\n",pdp->reqpath));
212
/********************************************************
213
Fake up a connection struct for the VFS layer.
214
Note this CHANGES CWD !!!! JRA.
215
*********************************************************/
217
NTSTATUS create_conn_struct(TALLOC_CTX *ctx,
218
connection_struct **pconn,
221
struct auth_serversupplied_info *server_info,
224
connection_struct *conn;
228
conn = TALLOC_ZERO_P(ctx, connection_struct);
230
return NT_STATUS_NO_MEMORY;
233
connpath = talloc_strdup(conn, path);
236
return NT_STATUS_NO_MEMORY;
238
connpath = talloc_string_sub(conn,
241
lp_servicename(snum));
244
return NT_STATUS_NO_MEMORY;
247
/* needed for smbd_vfs_init() */
249
if (!(conn->params = TALLOC_ZERO_P(conn, struct share_params))) {
250
DEBUG(0, ("TALLOC failed\n"));
252
return NT_STATUS_NO_MEMORY;
255
conn->params->service = snum;
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"));
262
return NT_STATUS_NO_MEMORY;
266
set_conn_connectpath(conn, connpath);
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);
275
conn->fs_capabilities = SMB_VFS_FS_CAPABILITIES(conn);
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.
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);
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. "
295
conn->connectpath, strerror(errno) ));
296
conn_free_internal(conn);
306
/**********************************************************************
307
Parse the contents of a symlink to verify if it is an msdfs referral
308
A valid referral is of the form:
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.
315
Note that the alternate paths returned here must be of the canonicalized
319
\server\share\path\to\file,
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
**********************************************************************/
325
static bool parse_msdfs_symlink(TALLOC_CTX *ctx,
327
struct referral **preflist,
332
char **alt_path = NULL;
334
struct referral *reflist;
337
temp = talloc_strdup(ctx, target);
341
prot = strtok_r(temp, ":", &saveptr);
343
DEBUG(0,("parse_msdfs_symlink: invalid path !\n"));
347
alt_path = TALLOC_ARRAY(ctx, char *, MAX_REFERRAL_COUNT);
352
/* parse out the alternate paths */
353
while((count<MAX_REFERRAL_COUNT) &&
354
((alt_path[count] = strtok_r(NULL, ",", &saveptr)) != NULL)) {
358
DEBUG(10,("parse_msdfs_symlink: count=%d\n", count));
361
reflist = *preflist = TALLOC_ZERO_ARRAY(ctx,
362
struct referral, count);
363
if(reflist == NULL) {
364
TALLOC_FREE(alt_path);
368
reflist = *preflist = NULL;
371
for(i=0;i<count;i++) {
374
/* Canonicalize link target.
375
* Replace all /'s in the path by a \ */
376
string_replace(alt_path[i], '/', '\\');
378
/* Remove leading '\\'s */
380
while (*p && (*p == '\\')) {
384
reflist[i].alternate_path = talloc_asprintf(ctx,
387
if (!reflist[i].alternate_path) {
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));
399
TALLOC_FREE(alt_path);
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
**********************************************************************/
408
static bool is_msdfs_link_internal(TALLOC_CTX *ctx,
409
connection_struct *conn,
411
char **pp_link_target,
412
SMB_STRUCT_STAT *sbufp)
415
int referral_len = 0;
416
#if defined(HAVE_BROKEN_READLINK)
417
char link_target_buf[PATH_MAX];
419
char link_target_buf[7];
422
char *link_target = NULL;
424
if (pp_link_target) {
426
link_target = TALLOC_ARRAY(ctx, char, bufsize);
430
*pp_link_target = link_target;
432
bufsize = sizeof(link_target_buf);
433
link_target = link_target_buf;
440
if (SMB_VFS_LSTAT(conn, path, sbufp) != 0) {
441
DEBUG(5,("is_msdfs_link_read_target: %s does not exist.\n",
446
if (!S_ISLNK(sbufp->st_mode)) {
447
DEBUG(5,("is_msdfs_link_read_target: %s is not a link.\n",
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)));
459
link_target[referral_len] = '\0';
461
DEBUG(5,("is_msdfs_link_internal: %s -> %s\n",path,
464
if (!strnequal(link_target, "msdfs:", 6)) {
471
if (link_target != link_target_buf) {
472
TALLOC_FREE(link_target);
477
/**********************************************************************
478
Returns true if the unix path is a valid msdfs symlink.
479
**********************************************************************/
481
bool is_msdfs_link(connection_struct *conn,
483
SMB_STRUCT_STAT *sbufp)
485
return is_msdfs_link_internal(talloc_tos(),
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.
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.
500
consumedcntp: how much of the dfs path is being redirected. the client
501
should try the remaining path on the redirected server.
503
If this returns NT_STATUS_PATH_NOT_COVERED the contents of the msdfs
504
link redirect are in targetpath.
505
*****************************************************************/
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 ? */
514
char **pp_targetpath)
518
SMB_STRUCT_STAT sbuf;
520
char *localpath = NULL;
521
char *canon_dfspath = NULL; /* Canonicalized dfs path. (only '/'
524
DEBUG(10,("dfs_path_lookup: Conn path = %s reqpath = %s\n",
525
conn->connectpath, pdp->reqpath));
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.
539
status = unix_convert(ctx, conn, pdp->reqpath, search_flag, &localpath,
541
if (!NT_STATUS_IS_OK(status) && !NT_STATUS_EQUAL(status,
542
NT_STATUS_OBJECT_PATH_NOT_FOUND)) {
546
/* Optimization - check if we can redirect the whole path. */
548
if (is_msdfs_link_internal(ctx, conn, localpath, pp_targetpath, NULL)) {
550
DEBUG(6,("dfs_path_lookup (FindFirst) No redirection "
551
"for dfs link %s.\n", dfspath));
555
DEBUG(6,("dfs_path_lookup: %s resolves to a "
556
"valid dfs link %s.\n", dfspath,
557
pp_targetpath ? *pp_targetpath : ""));
560
*consumedcntp = strlen(dfspath);
562
return NT_STATUS_PATH_NOT_COVERED;
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 '/'. */
569
canon_dfspath = talloc_strdup(ctx, dfspath);
570
if (!canon_dfspath) {
571
return NT_STATUS_NO_MEMORY;
573
if (!pdp->posix_path) {
574
string_replace(canon_dfspath, '\\', '/');
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>.
583
trim_char(canon_dfspath,0,'/');
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.
593
p = strrchr_m(localpath, '/');
595
q = strrchr_m(canon_dfspath, '/');
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));
610
*consumedcntp = strlen(canon_dfspath);
611
DEBUG(10, ("dfs_path_lookup: Path consumed: %s "
617
return NT_STATUS_PATH_NOT_COVERED;
620
/* Step back on the filesystem. */
621
p = strrchr_m(localpath, '/');
624
/* And in the canonicalized dfs path. */
625
q = strrchr_m(canon_dfspath, '/');
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
636
search_wcard_flag: this flag performs 2 functions both related
637
to searches. See resolve_dfs_path() and parse_dfs_path_XX()
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
*****************************************************************/
647
static NTSTATUS dfs_redirect(TALLOC_CTX *ctx,
648
connection_struct *conn,
650
bool search_wcard_flag,
652
bool *ppath_contains_wcard)
655
struct dfs_path *pdp = TALLOC_P(ctx, struct dfs_path);
658
return NT_STATUS_NO_MEMORY;
661
status = parse_dfs_path(conn, path_in, search_wcard_flag, pdp,
662
ppath_contains_wcard);
663
if (!NT_STATUS_IS_OK(status)) {
668
if (pdp->reqpath[0] == '\0') {
670
*pp_path_out = talloc_strdup(ctx, "");
672
return NT_STATUS_NO_MEMORY;
674
DEBUG(5,("dfs_redirect: self-referral.\n"));
678
/* If dfs pathname for a non-dfs share, convert to tcon-relative
679
path and return OK */
681
if (!lp_msdfs_root(SNUM(conn))) {
682
*pp_path_out = talloc_strdup(ctx, pdp->reqpath);
685
return NT_STATUS_NO_MEMORY;
690
/* If it looked like a local path (zero hostname/servicename)
691
* just treat as a tcon-relative path. */
693
if (pdp->hostname[0] == '\0' && pdp->servicename[0] == '\0') {
694
*pp_path_out = talloc_strdup(ctx, pdp->reqpath);
697
return NT_STATUS_NO_MEMORY;
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) )) ) {
707
/* The given sharename doesn't match this connection. */
710
return NT_STATUS_OBJECT_PATH_NOT_FOUND;
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));
719
DEBUG(10,("dfs_redirect: dfs_path_lookup "
720
"failed for %s with %s\n",
721
path_in, nt_errstr(status) ));
726
DEBUG(3,("dfs_redirect: Not redirecting %s.\n", path_in));
728
/* Form non-dfs tcon-relative path */
729
*pp_path_out = talloc_strdup(ctx, pdp->reqpath);
732
return NT_STATUS_NO_MEMORY;
735
DEBUG(3,("dfs_redirect: Path %s converted to non-dfs path %s\n",
742
/**********************************************************************
743
Return a self referral.
744
**********************************************************************/
746
static NTSTATUS self_ref(TALLOC_CTX *ctx,
747
const char *dfs_path,
748
struct junction_map *jucn,
750
bool *self_referralp)
752
struct referral *ref;
754
*self_referralp = True;
756
jucn->referral_count = 1;
757
if((ref = TALLOC_ZERO_P(ctx, struct referral)) == NULL) {
758
return NT_STATUS_NO_MEMORY;
761
ref->alternate_path = talloc_strdup(ctx, dfs_path);
762
if (!ref->alternate_path) {
763
return NT_STATUS_NO_MEMORY;
766
ref->ttl = REFERRAL_TTL;
767
jucn->referral_list = ref;
768
*consumedcntp = strlen(dfs_path);
772
/**********************************************************************
773
Gets valid referrals for a dfs path and fills up the
774
junction_map structure.
775
**********************************************************************/
777
NTSTATUS get_referred_path(TALLOC_CTX *ctx,
778
const char *dfs_path,
779
struct junction_map *jucn,
781
bool *self_referralp)
783
struct connection_struct *conn;
784
char *targetpath = NULL;
786
NTSTATUS status = NT_STATUS_NOT_FOUND;
788
struct dfs_path *pdp = TALLOC_P(ctx, struct dfs_path);
792
return NT_STATUS_NO_MEMORY;
795
*self_referralp = False;
797
status = parse_dfs_path(NULL, dfs_path, False, pdp, &dummy);
798
if (!NT_STATUS_IS_OK(status)) {
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) {
806
return NT_STATUS_NO_MEMORY;
809
/* Verify the share is a dfs root */
810
snum = lp_servicenumber(jucn->service_name);
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;
817
TALLOC_FREE(jucn->service_name);
818
jucn->service_name = talloc_strdup(ctx, service_name);
819
if (!jucn->service_name) {
821
return NT_STATUS_NO_MEMORY;
825
if (!lp_msdfs_root(snum) && (*lp_msdfs_proxy(snum) == '\0')) {
826
DEBUG(3,("get_referred_path: |%s| in dfs path %s is not "
828
pdp->servicename, dfs_path));
830
return NT_STATUS_NOT_FOUND;
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.
841
if (pdp->reqpath[0] == '\0') {
843
struct referral *ref;
845
if (*lp_msdfs_proxy(snum) == '\0') {
855
* It's an msdfs proxy share. Redirect to
856
* the configured target share.
859
jucn->referral_count = 1;
860
if ((ref = TALLOC_ZERO_P(ctx, struct referral)) == NULL) {
862
return NT_STATUS_NO_MEMORY;
865
if (!(tmp = talloc_strdup(ctx, lp_msdfs_proxy(snum)))) {
867
return NT_STATUS_NO_MEMORY;
870
trim_string(tmp, "\\", 0);
872
ref->alternate_path = talloc_asprintf(ctx, "\\%s", tmp);
875
if (!ref->alternate_path) {
877
return NT_STATUS_NO_MEMORY;
880
if (pdp->reqpath[0] != '\0') {
881
ref->alternate_path = talloc_asprintf_append(
885
if (!ref->alternate_path) {
887
return NT_STATUS_NO_MEMORY;
891
ref->ttl = REFERRAL_TTL;
892
jucn->referral_list = ref;
893
*consumedcntp = strlen(dfs_path);
898
status = create_conn_struct(ctx, &conn, snum, lp_pathname(snum),
900
if (!NT_STATUS_IS_OK(status)) {
905
/* If this is a DFS path dfs_lookup should return
906
* NT_STATUS_PATH_NOT_COVERED. */
908
status = dfs_path_lookup(ctx, conn, dfs_path, pdp,
909
False, consumedcntp, &targetpath);
911
if (!NT_STATUS_EQUAL(status, NT_STATUS_PATH_NOT_COVERED)) {
912
DEBUG(3,("get_referred_path: No valid referrals for path %s\n",
914
vfs_ChDir(conn, oldpath);
915
conn_free_internal(conn);
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);
929
return NT_STATUS_NOT_FOUND;
932
vfs_ChDir(conn, oldpath);
933
conn_free_internal(conn);
938
static int setup_ver2_dfs_referral(const char *pathname,
940
struct junction_map *junction,
943
char* pdata = *ppdata;
945
smb_ucs2_t *uni_requestedpath = NULL;
946
int uni_reqpathoffset1,uni_reqpathoffset2;
948
int requestedpathlen=0;
953
DEBUG(10,("Setting up version2 referral\nRequested path:\n"));
955
requestedpathlen = rpcstr_push_talloc(talloc_tos(),
956
&uni_requestedpath, pathname);
957
if (uni_requestedpath == NULL || requestedpathlen == 0) {
962
dump_data(0, (unsigned char *)uni_requestedpath,
966
DEBUG(10,("ref count = %u\n",junction->referral_count));
968
uni_reqpathoffset1 = REFERRAL_HEADER_SIZE +
969
VERSION2_REFERRAL_SIZE * junction->referral_count;
971
uni_reqpathoffset2 = uni_reqpathoffset1 + requestedpathlen;
973
uni_curroffset = uni_reqpathoffset2 + requestedpathlen;
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));
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",
984
junction->referral_list[i].alternate_path));
986
(strlen(junction->referral_list[i].alternate_path)+1)*2;
989
DEBUG(10,("reply_size = %u\n",reply_size));
990
/* add the unexplained 0x16 bytes */
993
pdata = (char *)SMB_REALLOC(pdata,reply_size);
995
DEBUG(0,("Realloc failed!\n"));
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);
1004
/* create the header */
1005
SSVAL(pdata,0,requestedpathlen - 2); /* UCS2 of path consumed minus
1007
/* number of referral in this pkt */
1008
SSVAL(pdata,2,junction->referral_count);
1010
SIVAL(pdata,4,DFSREF_REFERRAL_SERVER | DFSREF_STORAGE_SERVER);
1012
SIVAL(pdata,4,DFSREF_STORAGE_SERVER);
1016
/* add the referral elements */
1017
for(i=0;i<junction->referral_count;i++) {
1018
struct referral* ref = &junction->referral_list[i];
1021
SSVAL(pdata,offset,2); /* version 2 */
1022
SSVAL(pdata,offset+2,VERSION2_REFERRAL_SIZE);
1024
SSVAL(pdata,offset+4,1);
1026
SSVAL(pdata,offset+4,0);
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);
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,
1042
SSVAL(pdata,offset+20,uni_curroffset-offset);
1044
uni_curroffset += unilen;
1045
offset += VERSION2_REFERRAL_SIZE;
1047
/* add in the unexplained 22 (0x16) bytes at the end */
1048
memset(pdata+uni_curroffset,'\0',0x16);
1052
static int setup_ver3_dfs_referral(const char *pathname,
1054
struct junction_map *junction,
1057
char *pdata = *ppdata;
1059
smb_ucs2_t *uni_reqpath = NULL;
1060
int uni_reqpathoffset1, uni_reqpathoffset2;
1067
DEBUG(10,("setting up version3 referral\n"));
1069
reqpathlen = rpcstr_push_talloc(talloc_tos(), &uni_reqpath, pathname);
1070
if (uni_reqpath == NULL || reqpathlen == 0) {
1075
dump_data(0, (unsigned char *)uni_reqpath,
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;
1084
for(i=0;i<junction->referral_count;i++) {
1085
DEBUG(10,("referral %u : %s\n",
1087
junction->referral_list[i].alternate_path));
1089
(strlen(junction->referral_list[i].alternate_path)+1)*2;
1092
pdata = (char *)SMB_REALLOC(pdata,reply_size);
1094
DEBUG(0,("version3 referral setup:"
1095
"malloc failed for Realloc!\n"));
1100
/* create the header */
1101
SSVAL(pdata,0,reqpathlen - 2); /* UCS2 of path consumed minus
1103
SSVAL(pdata,2,junction->referral_count); /* number of referral */
1105
SIVAL(pdata,4,DFSREF_REFERRAL_SERVER | DFSREF_STORAGE_SERVER);
1107
SIVAL(pdata,4,DFSREF_STORAGE_SERVER);
1110
/* copy in the reqpaths */
1111
memcpy(pdata+uni_reqpathoffset1,uni_reqpath,reqpathlen);
1112
memcpy(pdata+uni_reqpathoffset2,uni_reqpath,reqpathlen);
1115
for(i=0;i<junction->referral_count;i++) {
1116
struct referral* ref = &(junction->referral_list[i]);
1119
SSVAL(pdata,offset,3); /* version 3 */
1120
SSVAL(pdata,offset+2,VERSION3_REFERRAL_SIZE);
1122
SSVAL(pdata,offset+4,1);
1124
SSVAL(pdata,offset+4,0);
1127
/* ref_flags :use path_consumed bytes? */
1128
SSVAL(pdata,offset+6,0);
1129
SIVAL(pdata,offset+8,ref->ttl);
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);
1141
uni_curroffset += unilen;
1142
offset += VERSION3_REFERRAL_SIZE;
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
******************************************************************/
1154
int setup_dfs_referral(connection_struct *orig_conn,
1155
const char *dfs_path,
1156
int max_referral_level,
1157
char **ppdata, NTSTATUS *pstatus)
1159
struct junction_map *junction = NULL;
1160
int consumedcnt = 0;
1161
bool self_referral = False;
1163
char *pathnamep = NULL;
1164
char *local_dfs_path = NULL;
1167
if (!(ctx=talloc_init("setup_dfs_referral"))) {
1168
*pstatus = NT_STATUS_NO_MEMORY;
1172
/* get the junction entry */
1174
talloc_destroy(ctx);
1175
*pstatus = NT_STATUS_NOT_FOUND;
1180
* Trim pathname sent by client so it begins with only one backslash.
1181
* Two backslashes confuse some dfs clients
1184
local_dfs_path = talloc_strdup(ctx,dfs_path);
1185
if (!local_dfs_path) {
1186
*pstatus = NT_STATUS_NO_MEMORY;
1187
talloc_destroy(ctx);
1190
pathnamep = local_dfs_path;
1191
while (IS_DIRECTORY_SEP(pathnamep[0]) &&
1192
IS_DIRECTORY_SEP(pathnamep[1])) {
1196
junction = TALLOC_ZERO_P(ctx, struct junction_map);
1198
*pstatus = NT_STATUS_NO_MEMORY;
1199
talloc_destroy(ctx);
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);
1211
vfs_ChDir(orig_conn,orig_conn->connectpath);
1213
if (!self_referral) {
1214
pathnamep[consumedcnt] = '\0';
1216
if( DEBUGLVL( 3 ) ) {
1218
dbgtext("setup_dfs_referral: Path %s to "
1219
"alternate path(s):",
1221
for(i=0;i<junction->referral_count;i++)
1223
junction->referral_list[i].alternate_path);
1228
/* create the referral depeding on version */
1229
DEBUG(10,("max_referral_level :%d\n",max_referral_level));
1231
if (max_referral_level < 2) {
1232
max_referral_level = 2;
1234
if (max_referral_level > 3) {
1235
max_referral_level = 3;
1238
switch(max_referral_level) {
1240
reply_size = setup_ver2_dfs_referral(pathnamep,
1245
reply_size = setup_ver3_dfs_referral(pathnamep, ppdata,
1246
junction, self_referral);
1249
DEBUG(0,("setup_dfs_referral: Invalid dfs referral "
1251
max_referral_level));
1252
talloc_destroy(ctx);
1253
*pstatus = NT_STATUS_INVALID_LEVEL;
1258
DEBUGADD(0,("DFS Referral pdata:\n"));
1259
dump_data(0,(uint8 *)*ppdata,reply_size);
1262
talloc_destroy(ctx);
1263
*pstatus = NT_STATUS_OK;
1267
/**********************************************************************
1268
The following functions are called by the NETDFS RPC pipe functions
1269
**********************************************************************/
1271
/*********************************************************************
1272
Creates a junction structure from a DFS pathname
1273
**********************************************************************/
1275
bool create_junction(TALLOC_CTX *ctx,
1276
const char *dfs_path,
1277
struct junction_map *jucn)
1281
struct dfs_path *pdp = TALLOC_P(ctx,struct dfs_path);
1287
status = parse_dfs_path(NULL, dfs_path, False, pdp, &dummy);
1288
if (!NT_STATUS_IS_OK(status)) {
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 "
1296
pdp->hostname, dfs_path));
1301
/* Check for a non-DFS share */
1302
snum = lp_servicenumber(pdp->servicename);
1304
if(snum < 0 || !lp_msdfs_root(snum)) {
1305
DEBUG(4,("create_junction: %s is not an msdfs root.\n",
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));
1316
if (!jucn->service_name || !jucn->volume_name || ! jucn->comment) {
1322
/**********************************************************************
1323
Forms a valid Unix pathname from the junction
1324
**********************************************************************/
1326
static bool junction_to_local_path(const struct junction_map *jucn,
1328
connection_struct **conn_out,
1334
snum = lp_servicenumber(jucn->service_name);
1338
status = create_conn_struct(talloc_tos(), conn_out, snum,
1339
lp_pathname(snum), NULL, oldpath);
1340
if (!NT_STATUS_IS_OK(status)) {
1344
*pp_path_out = talloc_asprintf(*conn_out,
1348
if (!*pp_path_out) {
1349
vfs_ChDir(*conn_out, *oldpath);
1350
conn_free_internal(*conn_out);
1356
bool create_msdfs_link(const struct junction_map *jucn)
1360
char *msdfs_link = NULL;
1361
connection_struct *conn;
1363
bool insert_comma = False;
1366
if(!junction_to_local_path(jucn, &path, &conn, &cwd)) {
1370
/* Form the msdfs_link contents */
1371
msdfs_link = talloc_strdup(conn, "msdfs:");
1375
for(i=0; i<jucn->referral_count; i++) {
1376
char *refpath = jucn->referral_list[i].alternate_path;
1378
/* Alternate paths always use Windows separators. */
1379
trim_char(refpath, '\\', '\\');
1380
if(*refpath == '\0') {
1382
insert_comma = False;
1386
if (i > 0 && insert_comma) {
1387
msdfs_link = talloc_asprintf_append_buffer(msdfs_link,
1391
msdfs_link = talloc_asprintf_append_buffer(msdfs_link,
1399
if (!insert_comma) {
1400
insert_comma = True;
1404
DEBUG(5,("create_msdfs_link: Creating new msdfs link: %s -> %s\n",
1407
if(SMB_VFS_SYMLINK(conn, msdfs_link, path) < 0) {
1408
if (errno == EEXIST) {
1409
if(SMB_VFS_UNLINK(conn,path)!=0) {
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)));
1424
vfs_ChDir(conn, cwd);
1425
conn_free_internal(conn);
1429
bool remove_msdfs_link(const struct junction_map *jucn)
1433
connection_struct *conn;
1436
if (!junction_to_local_path(jucn, &path, &conn, &cwd)) {
1440
if( SMB_VFS_UNLINK(conn, path) == 0 ) {
1444
vfs_ChDir(conn, cwd);
1445
conn_free_internal(conn);
1449
/*********************************************************************
1450
Return the number of DFS links at the root of this share.
1451
*********************************************************************/
1453
static int count_dfs_links(TALLOC_CTX *ctx, int snum)
1456
SMB_STRUCT_DIR *dirp = NULL;
1458
const char *connect_path = lp_pathname(snum);
1459
const char *msdfs_proxy = lp_msdfs_proxy(snum);
1460
connection_struct *conn;
1464
if(*connect_path == '\0') {
1469
* Fake up a connection struct for the VFS layer.
1472
status = create_conn_struct(talloc_tos(), &conn, snum, connect_path,
1474
if (!NT_STATUS_IS_OK(status)) {
1475
DEBUG(3, ("create_conn_struct failed: %s\n",
1476
nt_errstr(status)));
1480
/* Count a link for the msdfs root - convention */
1483
/* No more links if this is an msdfs proxy. */
1484
if (*msdfs_proxy != '\0') {
1488
/* Now enumerate all dfs links */
1489
dirp = SMB_VFS_OPENDIR(conn, ".", NULL, 0);
1494
while ((dname = vfs_readdirname(conn, dirp, NULL)) != NULL) {
1495
if (is_msdfs_link(conn,
1502
SMB_VFS_CLOSEDIR(conn,dirp);
1505
vfs_ChDir(conn, cwd);
1506
conn_free_internal(conn);
1510
/*********************************************************************
1511
*********************************************************************/
1513
static int form_junctions(TALLOC_CTX *ctx,
1515
struct junction_map *jucn,
1519
SMB_STRUCT_DIR *dirp = 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;
1529
if (jn_remain == 0) {
1533
if(*connect_path == '\0') {
1538
* Fake up a connection struct for the VFS layer.
1541
status = create_conn_struct(ctx, &conn, snum, connect_path, NULL,
1543
if (!NT_STATUS_IS_OK(status)) {
1544
DEBUG(3, ("create_conn_struct failed: %s\n",
1545
nt_errstr(status)));
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
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) {
1558
jucn[cnt].comment = "";
1559
jucn[cnt].referral_count = 1;
1561
ref = jucn[cnt].referral_list = TALLOC_ZERO_P(ctx, struct referral);
1562
if (jucn[cnt].referral_list == NULL) {
1567
ref->ttl = REFERRAL_TTL;
1568
if (*msdfs_proxy != '\0') {
1569
ref->alternate_path = talloc_strdup(ctx,
1572
ref->alternate_path = talloc_asprintf(ctx,
1574
get_local_machine_name(),
1578
if (!ref->alternate_path) {
1583
/* Don't enumerate if we're an msdfs proxy. */
1584
if (*msdfs_proxy != '\0') {
1588
/* Now enumerate all dfs links */
1589
dirp = SMB_VFS_OPENDIR(conn, ".", NULL, 0);
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 "
1601
if (is_msdfs_link_internal(ctx,
1603
dname, &link_target,
1605
if (parse_msdfs_symlink(ctx,
1607
&jucn[cnt].referral_list,
1608
&jucn[cnt].referral_count)) {
1610
jucn[cnt].service_name = talloc_strdup(ctx,
1612
jucn[cnt].volume_name = talloc_strdup(ctx,
1614
if (!jucn[cnt].service_name ||
1615
!jucn[cnt].volume_name) {
1618
jucn[cnt].comment = "";
1621
TALLOC_FREE(link_target);
1628
SMB_VFS_CLOSEDIR(conn,dirp);
1631
vfs_ChDir(conn, cwd);
1632
conn_free_internal(conn);
1636
struct junction_map *enum_msdfs_links(TALLOC_CTX *ctx, size_t *p_num_jn)
1638
struct junction_map *jn = NULL;
1640
size_t jn_count = 0;
1644
if(!lp_host_msdfs()) {
1648
/* Ensure all the usershares are loaded. */
1650
load_registry_shares();
1651
sharecount = load_usershare_shares();
1654
for(i=0;i < sharecount;i++) {
1655
if(lp_msdfs_root(i)) {
1656
jn_count += count_dfs_links(ctx, i);
1659
if (jn_count == 0) {
1662
jn = TALLOC_ARRAY(ctx, struct junction_map, jn_count);
1666
for(i=0; i < sharecount; i++) {
1667
if (*p_num_jn >= jn_count) {
1670
if(lp_msdfs_root(i)) {
1671
*p_num_jn += form_junctions(ctx, i,
1673
jn_count - *p_num_jn);
1679
/******************************************************************************
1680
Core function to resolve a dfs pathname.
1681
******************************************************************************/
1683
NTSTATUS resolve_dfspath(TALLOC_CTX *ctx,
1684
connection_struct *conn,
1686
const char *name_in,
1689
NTSTATUS status = NT_STATUS_OK;
1691
if (dfs_pathnames) {
1692
status = dfs_redirect(ctx,
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.
1704
*pp_name_out = CONST_DISCARD(char *,name_in);
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
******************************************************************************/
1716
NTSTATUS resolve_dfspath_wcard(TALLOC_CTX *ctx,
1717
connection_struct *conn,
1719
const char *name_in,
1721
bool *ppath_contains_wcard)
1723
NTSTATUS status = NT_STATUS_OK;
1724
if (dfs_pathnames) {
1725
status = dfs_redirect(ctx,
1730
ppath_contains_wcard);
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.
1737
*pp_name_out = CONST_DISCARD(char *,name_in);