diff --git a/source3/smbd/filename.c b/source3/smbd/filename.c index e378bb72b32..d53046bf394 100644 --- a/source3/smbd/filename.c +++ b/source3/smbd/filename.c @@ -1932,7 +1932,6 @@ NTSTATUS filename_convert_smb1_search_path(TALLOC_CTX *ctx, char *p = NULL; char *mask = NULL; struct smb_filename *smb_fname = NULL; - bool posix_pathnames = (ucf_flags & UCF_POSIX_PATHNAMES); NTTIME twrp = 0; *_smb_fname_out = NULL; @@ -1949,10 +1948,7 @@ NTSTATUS filename_convert_smb1_search_path(TALLOC_CTX *ctx, if (ucf_flags & UCF_DFS_PATHNAME) { /* * We've been given a raw DFS pathname. - * In Windows mode this is separated by '\' - * characters, in POSIX by '/' characters. */ - char path_sep = posix_pathnames ? '/' : '\\'; char *fname = NULL; char *name_in_copy = NULL; char *last_component = NULL; @@ -1967,7 +1963,7 @@ NTSTATUS filename_convert_smb1_search_path(TALLOC_CTX *ctx, * Now we know that the last component is the * wildcard. Copy it and truncate to remove it. */ - p = strrchr_m(name_in_copy, path_sep); + p = strrchr(name_in_copy, '/'); if (p == NULL) { last_component = talloc_strdup(ctx, name_in_copy); name_in_copy[0] = '\0'; diff --git a/source3/smbd/msdfs.c b/source3/smbd/msdfs.c index 86dc3f4dd74..565c2a37b3b 100644 --- a/source3/smbd/msdfs.c +++ b/source3/smbd/msdfs.c @@ -37,11 +37,13 @@ #include "source3/lib/substitute.h" /********************************************************************** - Parse a DFS pathname of the form \hostname\service\reqpath + Parse a DFS pathname of the form /hostname/service/reqpath into the dfs_path structure. - If POSIX pathnames is true, the pathname may also be of the - form /hostname/service/reqpath. - We cope with either here. + + NB. srvstr_get_path_internal() now *always* calls + check_path_syntax_XXX() on an incoming name, so + the path separator is now always '/', even from + Windows clients. Unfortunately, due to broken clients who might set the SVAL(inbuf,smb_flg2) & FLAGS2_DFS_PATHNAMES bit and then @@ -65,11 +67,9 @@ static NTSTATUS parse_dfs_path(connection_struct *conn, const struct loadparm_substitution *lp_sub = loadparm_s3_global_substitution(); char *pathname_local; - char *p,*temp; + char *p; char *servicename; char *eos_ptr; - NTSTATUS status = NT_STATUS_OK; - char sepchar; ZERO_STRUCTP(pdp); @@ -80,30 +80,28 @@ static NTSTATUS parse_dfs_path(connection_struct *conn, */ pathname_local = talloc_strdup(pdp, pathname); - if (!pathname_local) { + if (pathname_local == NULL) { return NT_STATUS_NO_MEMORY; } + /* + * parse_dfs_path() can be called from + * get_referred_path() and create_junction() + * which use Windows DFS paths of \server\share. + * Ensure we only have to cope with '/' separators. + */ + string_replace(pathname_local, '\\', '/'); + /* Get a pointer to the terminating '\0' */ eos_ptr = &pathname_local[strlen(pathname_local)]; - p = temp = pathname_local; + p = pathname_local; /* * Non-broken DFS paths *must* start with the - * path separator. For Windows this is always '\\', - * for posix paths this is always '/'. + * path separator '/'. */ - if (*pathname == '/') { - pdp->posix_path = true; - sepchar = '/'; - } else { - pdp->posix_path = false; - sepchar = '\\'; - } - - if (allow_broken_path && (*pathname != sepchar)) { - DEBUG(10,("parse_dfs_path: path %s doesn't start with %c\n", - pathname, sepchar )); + if (allow_broken_path && (*p != '/')) { + DBG_ERR("path %s doesn't start with /\n", p); /* * Possibly client sent a local path by mistake. * Try and convert to a local path. @@ -114,12 +112,7 @@ static NTSTATUS parse_dfs_path(connection_struct *conn, pdp->hostname = eos_ptr; /* "" */ pdp->servicename = eos_ptr; /* "" */ - /* We've got no info about separators. */ - pdp->posix_path = lp_posix_pathnames(); - p = temp; - DEBUG(10,("parse_dfs_path: trying to convert %s to a " - "local path\n", - temp)); + DBG_ERR("trying to convert %s to a local path\n", p); goto local_path; } @@ -127,17 +120,15 @@ static NTSTATUS parse_dfs_path(connection_struct *conn, * Safe to use on talloc'ed string as it only shrinks. * It also doesn't affect the eos_ptr. */ - trim_char(temp,sepchar,sepchar); + trim_char(p, '/', '/'); - DEBUG(10,("parse_dfs_path: temp = |%s| after trimming %c's\n", - temp, sepchar)); + DBG_ERR("p = |%s| after trimming /'s\n", p); /* Now tokenize. */ /* Parse out hostname. */ - p = strchr_m(temp,sepchar); + p = strchr(p,'/'); if(p == NULL) { - DEBUG(10,("parse_dfs_path: can't parse hostname from path %s\n", - temp)); + DBG_ERR("can't parse hostname from path %s\n", pathname_local); /* * Possibly client sent a local path by mistake. * Try and convert to a local path. @@ -146,20 +137,18 @@ static NTSTATUS parse_dfs_path(connection_struct *conn, pdp->hostname = eos_ptr; /* "" */ pdp->servicename = eos_ptr; /* "" */ - p = temp; - DEBUG(10,("parse_dfs_path: trying to convert %s " - "to a local path\n", - temp)); + p = pathname_local; + DBG_ERR("trying to convert %s to a local path\n", p); goto local_path; } *p = '\0'; - pdp->hostname = temp; + pdp->hostname = pathname_local; - DEBUG(10,("parse_dfs_path: hostname: %s\n",pdp->hostname)); + DBG_ERR("hostname: %s\n",pdp->hostname); /* Parse out servicename. */ servicename = p+1; - p = strchr_m(servicename,sepchar); + p = strchr(servicename, '/'); if (p) { *p = '\0'; } @@ -169,8 +158,7 @@ static NTSTATUS parse_dfs_path(connection_struct *conn, || (strequal(servicename, HOMES_NAME) && strequal(lp_servicename(talloc_tos(), lp_sub, SNUM(conn)), get_current_username()) )) ) { - DEBUG(10,("parse_dfs_path: %s is not our servicename\n", - servicename)); + DBG_ERR("%s is not our servicename\n", servicename); /* * Possibly client sent a local path by mistake. @@ -183,21 +171,20 @@ static NTSTATUS parse_dfs_path(connection_struct *conn, /* Repair the path - replace the sepchar's we nulled out */ servicename--; - *servicename = sepchar; + *servicename = '/'; if (p) { - *p = sepchar; + *p = '/'; } - p = temp; - DEBUG(10,("parse_dfs_path: trying to convert %s " - "to a local path\n", - temp)); + p = pathname_local; + DBG_ERR("trying to convert %s to a local path\n", + pathname_local); goto local_path; } pdp->servicename = servicename; - DEBUG(10,("parse_dfs_path: servicename: %s\n",pdp->servicename)); + DBG_ERR("servicename: %s\n", pdp->servicename); if(p == NULL) { /* Client sent self referral \server\share. */ @@ -209,22 +196,14 @@ static NTSTATUS parse_dfs_path(connection_struct *conn, local_path: + /* + * As check_path_syntax_XXX() has already been + * called we know this is a normal path containing + * '/' separators. + */ + pdp->reqpath = p; - - /* Rest is reqpath. */ - if (pdp->posix_path) { - status = check_path_syntax_posix(pdp->reqpath); - } else { - status = check_path_syntax(pdp->reqpath); - } - - if (!NT_STATUS_IS_OK(status)) { - DEBUG(10,("parse_dfs_path: '%s' failed with %s\n", - p, nt_errstr(status) )); - return status; - } - - DEBUG(10,("parse_dfs_path: rest of the path: %s\n",pdp->reqpath)); + DBG_ERR("rest of the path: %s\n", pdp->reqpath); return NT_STATUS_OK; } @@ -765,9 +744,7 @@ static NTSTATUS dfs_path_lookup(TALLOC_CTX *ctx, status = NT_STATUS_NO_MEMORY; goto out; } - if (!pdp->posix_path) { - string_replace(canon_dfspath, '\\', '/'); - } + string_replace(canon_dfspath, '\\', '/'); /* * localpath comes out of unix_convert, so it has diff --git a/source3/smbd/smb2_reply.c b/source3/smbd/smb2_reply.c index 6f3a2c214f5..42601879c09 100644 --- a/source3/smbd/smb2_reply.c +++ b/source3/smbd/smb2_reply.c @@ -259,6 +259,7 @@ static size_t srvstr_get_path_internal(TALLOC_CTX *ctx, NTSTATUS *err) { size_t ret; + char *dst = NULL; *pp_dest = NULL; @@ -270,19 +271,84 @@ static size_t srvstr_get_path_internal(TALLOC_CTX *ctx, return ret; } + dst = *pp_dest; + if (smb_flags2 & FLAGS2_DFS_PATHNAMES) { /* - * For a DFS path the function parse_dfs_path() - * will do the path processing, just make a copy. + * A valid DFS path looks either like + * /server/share + * \server\share + * (there may be more components after). + * Either way it must have at least two separators. + * + * Ensure we end up as /server/share + * so we don't need to special case + * separator characters elsewhere in + * the code. */ - *err = NT_STATUS_OK; - return ret; + char *server = NULL; + char *share = NULL; + char *remaining_path = NULL; + char path_sep = 0; + + if (posix_pathnames && (dst[0] == '/')) { + path_sep = dst[0]; + } else if (dst[0] == '\\') { + path_sep = dst[0]; + } + + if (path_sep == 0) { + goto local_path; + } + /* + * May be a DFS path. + * We need some heuristics here, + * as clients differ on what constitutes + * a well-formed DFS path. If the path + * appears malformed, just fall back to + * processing as a local path. + */ + server = dst; + + /* + * Look to see if we also have /share following. + */ + share = strchr(server+1, path_sep); + if (share == NULL) { + goto local_path; + } + /* + * It's a well formed DFS path with + * at least server and share components. + * Replace the slashes with '/' and + * pass the remainder to local_path. + */ + *server = '/'; + *share = '/'; + /* + * Skip past share so we don't pass the + * sharename into check_path_syntax(). + */ + remaining_path = strchr(share+1, path_sep); + if (remaining_path == NULL) { + /* + * If no remaining path this was + * a bare /server/share path. Just return. + */ + *err = NT_STATUS_OK; + return ret; + } + *remaining_path = '/'; + dst = remaining_path + 1; + /* dst now points at any following components. */ } + local_path: + if (posix_pathnames) { - *err = check_path_syntax_posix(*pp_dest); + *err = check_path_syntax_posix(dst); } else { - *err = check_path_syntax(*pp_dest); + *err = check_path_syntax(dst); } return ret;