From 12ab932affea36c8ef305e8c22cd7bc0d3f09f46 Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Fri, 5 Aug 2022 19:27:33 -0700 Subject: [PATCH 01/25] s3: smbd: Fix cosmetic bug logging pathnames from Linux kernel clients using SMB1 DFS calls. The Linux kernel SMB1 client has a bug - it sends DFS pathnames as: \\server\share\path instead of: \server\share\path Causing us to mis-parse server,share,remaining_path here and jump into 'goto local_path' at 'share\path' instead of 'path'. This doesn't cause an error as the limits on share names are similar to those on pathnames. parse_dfs_path() which we call before filename parsing copes with this by calling trim_char on the leading '\' characters before processing. Do the same here so logging of pathnames looks better. How did I find this ? Lots and lots of manual testing with the Linux kernel client to make sure all the recent changes haven't broken Linux SMB1/2/3 DFS :-). BUG: https://bugzilla.samba.org/show_bug.cgi?id=15144 Signed-off-by: Jeremy Allison Reviewed-by: Ralph Boehme (cherry picked from commit 2818fd6910201fd4a18b921933a0b7392a0a8995) --- source3/smbd/smb2_reply.c | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/source3/smbd/smb2_reply.c b/source3/smbd/smb2_reply.c index 42601879c09..870c3bcb898 100644 --- a/source3/smbd/smb2_reply.c +++ b/source3/smbd/smb2_reply.c @@ -310,6 +310,29 @@ static size_t srvstr_get_path_internal(TALLOC_CTX *ctx, */ server = dst; + /* + * Cosmetic fix for Linux-only DFS clients. + * The Linux kernel SMB1 client has a bug - it sends + * DFS pathnames as: + * + * \\server\share\path + * + * Causing us to mis-parse server,share,remaining_path here + * and jump into 'goto local_path' at 'share\path' instead + * of 'path'. + * + * This doesn't cause an error as the limits on share names + * are similar to those on pathnames. + * + * parse_dfs_path() which we call before filename parsing + * copes with this by calling trim_char on the leading '\' + * characters before processing. + * Do the same here so logging of pathnames looks better. + */ + if (server[1] == path_sep) { + trim_char(&server[1], path_sep, '\0'); + } + /* * Look to see if we also have /share following. */ -- 2.34.1 From 5a49e2eafde4d377adf1a1d14e7911389390c843 Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Fri, 5 Aug 2022 12:16:44 -0700 Subject: [PATCH 02/25] s3: smbd: Add new function check_path_syntax_smb2_msdfs() for SMB2 MSDFS paths. #ifdef'ed out as static and not yet used. We can't just call check_path_syntax() on these as they are of the form hostname\share[\extrapath] (where [\extrapath] is optional). hostname here can be an IPv6 ':' separated address, which check_path_syntax() fails on due to the streamname processing. NB. This also has to cope with out existing (broken) libsmbclient libraries that sometimes set the DFS flag and then send a local pathname. Cope by just calling the normal check_path_syntax() on the whole pathname in that case. BUG: https://bugzilla.samba.org/show_bug.cgi?id=15144 Signed-off-by: Jeremy Allison Reviewed-by: Ralph Boehme (cherry picked from commit bcba5502282eb6dcc346d7c63aa3218cda2f9bb0) --- source3/smbd/smb2_reply.c | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/source3/smbd/smb2_reply.c b/source3/smbd/smb2_reply.c index 870c3bcb898..6052f94354a 100644 --- a/source3/smbd/smb2_reply.c +++ b/source3/smbd/smb2_reply.c @@ -243,6 +243,44 @@ NTSTATUS check_path_syntax_posix(char *path) return check_path_syntax_internal(path, true); } +#if 0 +/**************************************************************************** + Check the path for an SMB2 DFS path. + SMB2 DFS paths look like hostname\share (followed by a possible \extrapath. + Path returned from here must look like: + hostname/share (followed by a possible /extrapath). +****************************************************************************/ + +static NTSTATUS check_path_syntax_smb2_msdfs(char *path) +{ + char *share = NULL; + char *remaining_path = NULL; + /* No SMB2 names can start with '\\' */ + if (path[0] == '\\') { + return NT_STATUS_OBJECT_NAME_INVALID; + } + /* + * smbclient libraries sometimes set the DFS flag and send + * local pathnames. Cope with this by just calling + * check_path_syntax() on the whole path if it doesn't + * look like a DFS path, similar to what parse_dfs_path() does. + */ + /* servername should be at path[0] */ + share = strchr(path, '\\'); + if (share == NULL) { + return check_path_syntax(path); + } + *share++ = '/'; + remaining_path = strchr(share, '\\'); + if (remaining_path == NULL) { + /* Only hostname\share. We're done. */ + return NT_STATUS_OK; + } + *remaining_path++ = '/'; + return check_path_syntax(remaining_path); +} +#endif + /**************************************************************************** Pull a string and check the path allowing a wildcard - provide for error return. Passes in posix flag. -- 2.34.1 From de02e12e578eec361c468e4f0ff041ce176b60b4 Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Tue, 9 Aug 2022 10:36:00 -0700 Subject: [PATCH 03/25] s3: smbd: Add helper function check_path_syntax_smb2(). Not yet used, but uses check_path_syntax_smb2_msdfs() so remove the #ifdef's around that. BUG: https://bugzilla.samba.org/show_bug.cgi?id=15144 Signed-off-by: Jeremy Allison Reviewed-by: Ralph Boehme (cherry picked from commit 7bd7fa0a0b46ad6826097a1987595e2ab6f83384) --- source3/smbd/proto.h | 1 + source3/smbd/smb2_reply.c | 11 +++++++++-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/source3/smbd/proto.h b/source3/smbd/proto.h index ceee52d79cd..33fc222311e 100644 --- a/source3/smbd/proto.h +++ b/source3/smbd/proto.h @@ -941,6 +941,7 @@ bool disk_quotas(connection_struct *conn, struct smb_filename *fname, NTSTATUS check_path_syntax(char *path); NTSTATUS check_path_syntax_posix(char *path); +NTSTATUS check_path_syntax_smb2(char *path, bool dfs_path); size_t srvstr_get_path(TALLOC_CTX *ctx, const char *inbuf, uint16_t smb_flags2, diff --git a/source3/smbd/smb2_reply.c b/source3/smbd/smb2_reply.c index 6052f94354a..0303db428f2 100644 --- a/source3/smbd/smb2_reply.c +++ b/source3/smbd/smb2_reply.c @@ -243,7 +243,6 @@ NTSTATUS check_path_syntax_posix(char *path) return check_path_syntax_internal(path, true); } -#if 0 /**************************************************************************** Check the path for an SMB2 DFS path. SMB2 DFS paths look like hostname\share (followed by a possible \extrapath. @@ -279,7 +278,15 @@ static NTSTATUS check_path_syntax_smb2_msdfs(char *path) *remaining_path++ = '/'; return check_path_syntax(remaining_path); } -#endif + +NTSTATUS check_path_syntax_smb2(char *path, bool dfs_path) +{ + if (dfs_path) { + return check_path_syntax_smb2_msdfs(path); + } else { + return check_path_syntax(path); + } +} /**************************************************************************** Pull a string and check the path allowing a wildcard - provide for error return. -- 2.34.1 From ba056f7f7b00befb945df9f048139f78b49a0281 Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Tue, 9 Aug 2022 10:39:41 -0700 Subject: [PATCH 04/25] s3: smbd: In smbd_smb2_create_send() call the helper function check_path_syntax_smb2(). Previously for DFS names we were skipping this. BUG: https://bugzilla.samba.org/show_bug.cgi?id=15144 Signed-off-by: Jeremy Allison Reviewed-by: Ralph Boehme (cherry picked from commit 4fafc3418931de06ea2d91baca1eef8d904cc4e6) --- source3/smbd/smb2_create.c | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/source3/smbd/smb2_create.c b/source3/smbd/smb2_create.c index dbafaf01597..8ef605dea66 100644 --- a/source3/smbd/smb2_create.c +++ b/source3/smbd/smb2_create.c @@ -714,6 +714,7 @@ static struct tevent_req *smbd_smb2_create_send(TALLOC_CTX *mem_ctx, struct files_struct *dirfsp = NULL; struct smb_filename *smb_fname = NULL; uint32_t ucf_flags; + bool is_dfs = false; req = tevent_req_create(mem_ctx, &state, struct smbd_smb2_create_state); @@ -959,18 +960,13 @@ static struct tevent_req *smbd_smb2_create_send(TALLOC_CTX *mem_ctx, state->lease_ptr = NULL; } - /* - * For a DFS path the function parse_dfs_path() - * will do the path processing. - */ + is_dfs = (smb1req->flags2 & FLAGS2_DFS_PATHNAMES); - if (!(smb1req->flags2 & FLAGS2_DFS_PATHNAMES)) { - /* convert '\\' into '/' */ - status = check_path_syntax(state->fname); - if (!NT_STATUS_IS_OK(status)) { - tevent_req_nterror(req, status); - return tevent_req_post(req, state->ev); - } + /* convert '\\' into '/' */ + status = check_path_syntax_smb2(state->fname, is_dfs); + if (!NT_STATUS_IS_OK(status)) { + tevent_req_nterror(req, status); + return tevent_req_post(req, state->ev); } ucf_flags = filename_create_ucf_flags( -- 2.34.1 From 5dcf3dfd09eb75496205acac3538836d5ebb27e2 Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Tue, 9 Aug 2022 10:41:39 -0700 Subject: [PATCH 05/25] s3: smbd: Make sure we have identical check_path_syntax logic in smbd_smb2_create_durable_lease_check(), as for smb2_create. BUG: https://bugzilla.samba.org/show_bug.cgi?id=15144 Signed-off-by: Jeremy Allison Reviewed-by: Ralph Boehme (cherry picked from commit a2a097fc3d6a89fb970c1ea3ea75fde93ddb545e) --- source3/smbd/smb2_create.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/source3/smbd/smb2_create.c b/source3/smbd/smb2_create.c index 8ef605dea66..75b9c7d28ff 100644 --- a/source3/smbd/smb2_create.c +++ b/source3/smbd/smb2_create.c @@ -430,6 +430,7 @@ static NTSTATUS smbd_smb2_create_durable_lease_check(struct smb_request *smb1req uint32_t ucf_flags; NTTIME twrp = fsp->fsp_name->twrp; NTSTATUS status; + bool is_dfs = (smb1req->flags2 & FLAGS2_DFS_PATHNAMES); if (lease_ptr == NULL) { if (fsp->oplock_type != LEASE_OPLOCK) { @@ -459,7 +460,7 @@ static NTSTATUS smbd_smb2_create_durable_lease_check(struct smb_request *smb1req } /* This also converts '\' to '/' */ - status = check_path_syntax(filename); + status = check_path_syntax_smb2(filename, is_dfs); if (!NT_STATUS_IS_OK(status)) { TALLOC_FREE(filename); return status; -- 2.34.1 From 5155d323497d8f9529d5fc7b9a0b14c175cb7ab1 Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Tue, 9 Aug 2022 10:43:45 -0700 Subject: [PATCH 06/25] s3: smbd: Ensure smb2_file_rename_information() uses the SMB2 pathname parsers, not the SMB1 parsers. BUG: https://bugzilla.samba.org/show_bug.cgi?id=15144 Signed-off-by: Jeremy Allison Reviewed-by: Ralph Boehme (cherry picked from commit 0a4a27ce48bc7090aa821eea5e56f8d44c686716) --- source3/smbd/smb2_trans2.c | 30 ++++++++++++------------------ 1 file changed, 12 insertions(+), 18 deletions(-) diff --git a/source3/smbd/smb2_trans2.c b/source3/smbd/smb2_trans2.c index d0f30a782bf..b2a0cc4140a 100644 --- a/source3/smbd/smb2_trans2.c +++ b/source3/smbd/smb2_trans2.c @@ -4957,6 +4957,7 @@ static NTSTATUS smb2_file_rename_information(connection_struct *conn, uint32_t ucf_flags = ucf_flags_from_smb_request(req); NTTIME dst_twrp = 0; NTSTATUS status = NT_STATUS_OK; + bool is_dfs = (req->flags2 & FLAGS2_DFS_PATHNAMES); TALLOC_CTX *ctx = talloc_tos(); if (!fsp) { @@ -4974,25 +4975,18 @@ static NTSTATUS smb2_file_rename_information(connection_struct *conn, return NT_STATUS_INVALID_PARAMETER; } - if (req->posix_pathnames) { - srvstr_get_path_posix(ctx, - pdata, - req->flags2, - &newname, - &pdata[20], - len, - STR_TERMINATE, - &status); - } else { - srvstr_get_path(ctx, - pdata, - req->flags2, - &newname, - &pdata[20], - len, - STR_TERMINATE, - &status); + (void)srvstr_pull_talloc(ctx, + pdata, + req->flags2, + &newname, + &pdata[20], + len, + STR_TERMINATE); + + if (newname == NULL) { + return NT_STATUS_INVALID_PARAMETER; } + status = check_path_syntax_smb2(newname, is_dfs); if (!NT_STATUS_IS_OK(status)) { return status; } -- 2.34.1 From 8c47743593d0b9792db4f864ee976f613047a10e Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Wed, 10 Aug 2022 11:06:47 -0700 Subject: [PATCH 07/25] s3: smbd: Add TALLOC_CTX * parameter to parse_dfs_path(). Not yet used. Preparing to remove 'struct dfs_path'. BUG: https://bugzilla.samba.org/show_bug.cgi?id=15144 Signed-off-by: Jeremy Allison Reviewed-by: Ralph Boehme (cherry picked from commit 2df8a8ab87a1372f2b67880be4454a0285b3104b) --- source3/smbd/msdfs.c | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/source3/smbd/msdfs.c b/source3/smbd/msdfs.c index a52a2449965..abe447dd056 100644 --- a/source3/smbd/msdfs.c +++ b/source3/smbd/msdfs.c @@ -58,7 +58,8 @@ JRA. **********************************************************************/ -static NTSTATUS parse_dfs_path(connection_struct *conn, +static NTSTATUS parse_dfs_path(TALLOC_CTX *ctx, + connection_struct *conn, const char *pathname, bool allow_broken_path, struct dfs_path *pdp) /* MUST BE TALLOCED */ @@ -870,8 +871,11 @@ NTSTATUS dfs_redirect(TALLOC_CTX *ctx, return NT_STATUS_NO_MEMORY; } - status = parse_dfs_path(conn, path_in, - allow_broken_path, pdp); + status = parse_dfs_path(ctx, + conn, + path_in, + allow_broken_path, + pdp); if (!NT_STATUS_IS_OK(status)) { TALLOC_FREE(pdp); return status; @@ -1020,7 +1024,11 @@ NTSTATUS get_referred_path(TALLOC_CTX *ctx, *self_referralp = False; - status = parse_dfs_path(NULL, dfs_path, allow_broken_path, pdp); + status = parse_dfs_path(frame, + NULL, + dfs_path, + allow_broken_path, + pdp); if (!NT_STATUS_IS_OK(status)) { TALLOC_FREE(frame); return status; @@ -1269,7 +1277,11 @@ bool create_junction(TALLOC_CTX *ctx, if (!pdp) { return False; } - status = parse_dfs_path(NULL, dfs_path, allow_broken_path, pdp); + status = parse_dfs_path(ctx, + NULL, + dfs_path, + allow_broken_path, + pdp); if (!NT_STATUS_IS_OK(status)) { return False; } -- 2.34.1 From 4073edd33916d2c29431cad5fb1e0550782a704a Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Wed, 10 Aug 2022 11:17:49 -0700 Subject: [PATCH 08/25] s3: smbd: Remove use of 'struct dfs_path'. Not needed for a (hostname, servicename, path) tuple. BUG: https://bugzilla.samba.org/show_bug.cgi?id=15144 Signed-off-by: Jeremy Allison Reviewed-by: Ralph Boehme (cherry picked from commit f92711f000a3cb658dfb8fffe92ae6bba78b4f91) --- source3/smbd/msdfs.c | 167 ++++++++++++++++++++++--------------------- 1 file changed, 87 insertions(+), 80 deletions(-) diff --git a/source3/smbd/msdfs.c b/source3/smbd/msdfs.c index abe447dd056..21288aaf664 100644 --- a/source3/smbd/msdfs.c +++ b/source3/smbd/msdfs.c @@ -62,24 +62,20 @@ static NTSTATUS parse_dfs_path(TALLOC_CTX *ctx, connection_struct *conn, const char *pathname, bool allow_broken_path, - struct dfs_path *pdp) /* MUST BE TALLOCED */ + char **_hostname, + char **_servicename, + char **_remaining_path) { const struct loadparm_substitution *lp_sub = loadparm_s3_global_substitution(); - char *pathname_local; - char *p; - char *servicename; - char *eos_ptr; - - ZERO_STRUCTP(pdp); - - /* - * This is the only talloc we should need to do - * on the struct dfs_path. All the pointers inside - * it should point to offsets within this string. - */ + char *hostname = NULL; + char *pathname_local = NULL; + char *p = NULL; + char *servicename = NULL; + char *reqpath = NULL; + char *eos_ptr = NULL; - pathname_local = talloc_strdup(pdp, pathname); + pathname_local = talloc_strdup(ctx, pathname); if (pathname_local == NULL) { return NT_STATUS_NO_MEMORY; } @@ -109,8 +105,8 @@ static NTSTATUS parse_dfs_path(TALLOC_CTX *ctx, * to cope with known broken SMB1 clients. */ - pdp->hostname = eos_ptr; /* "" */ - pdp->servicename = eos_ptr; /* "" */ + hostname = eos_ptr; /* "" */ + servicename = eos_ptr; /* "" */ DBG_ERR("trying to convert %s to a local path\n", p); goto local_path; @@ -134,17 +130,17 @@ static NTSTATUS parse_dfs_path(TALLOC_CTX *ctx, * Try and convert to a local path. */ - pdp->hostname = eos_ptr; /* "" */ - pdp->servicename = eos_ptr; /* "" */ + hostname = eos_ptr; /* "" */ + servicename = eos_ptr; /* "" */ p = pathname_local; DBG_ERR("trying to convert %s to a local path\n", p); goto local_path; } *p = '\0'; - pdp->hostname = pathname_local; + hostname = pathname_local; - DBG_DEBUG("hostname: %s\n",pdp->hostname); + DBG_DEBUG("hostname: %s\n", hostname); /* Parse out servicename. */ servicename = p+1; @@ -165,9 +161,6 @@ static NTSTATUS parse_dfs_path(TALLOC_CTX *ctx, * Try and convert to a local path. */ - pdp->hostname = eos_ptr; /* "" */ - pdp->servicename = eos_ptr; /* "" */ - /* Repair the path - replace the sepchar's we nulled out */ servicename--; @@ -176,20 +169,23 @@ static NTSTATUS parse_dfs_path(TALLOC_CTX *ctx, *p = '/'; } + hostname = eos_ptr; /* "" */ + servicename = eos_ptr; /* "" */ + p = pathname_local; DBG_ERR("trying to convert %s to a local path\n", pathname_local); goto local_path; } - pdp->servicename = servicename; + servicename = servicename; - DBG_DEBUG("servicename: %s\n", pdp->servicename); + DBG_DEBUG("servicename: %s\n", servicename); if(p == NULL) { /* Client sent self referral \server\share. */ - pdp->reqpath = eos_ptr; /* "" */ - return NT_STATUS_OK; + reqpath = eos_ptr; /* "" */ + goto out; } p++; @@ -202,8 +198,31 @@ static NTSTATUS parse_dfs_path(TALLOC_CTX *ctx, * '/' separators. */ - pdp->reqpath = p; - DBG_DEBUG("rest of the path: %s\n", pdp->reqpath); + reqpath = p; + + out: + + DBG_DEBUG("rest of the path: %s\n", reqpath); + + if (_hostname != NULL) { + *_hostname = talloc_strdup(ctx, hostname); + if (*_hostname == NULL) { + return NT_STATUS_NO_MEMORY; + } + } + if (_servicename != NULL) { + *_servicename = talloc_strdup(ctx, servicename); + if (*_servicename == NULL) { + return NT_STATUS_NO_MEMORY; + } + } + if (_remaining_path != NULL) { + *_remaining_path = talloc_strdup(ctx, reqpath); + if (*_remaining_path == NULL) { + return NT_STATUS_NO_MEMORY; + } + } + TALLOC_FREE(pathname_local); return NT_STATUS_OK; } @@ -663,8 +682,7 @@ bool is_msdfs_link(struct files_struct *dirfsp, static NTSTATUS dfs_path_lookup(TALLOC_CTX *ctx, connection_struct *conn, const char *dfspath, /* Incoming complete dfs path */ - const struct dfs_path *pdp, /* Parsed out - server+share+extrapath. */ + const char *reqpath, /* Parsed out remaining path. */ uint32_t ucf_flags, NTTIME *_twrp, size_t *consumedcntp, @@ -681,7 +699,7 @@ static NTSTATUS dfs_path_lookup(TALLOC_CTX *ctx, components). */ DEBUG(10,("dfs_path_lookup: Conn path = %s reqpath = %s\n", - conn->connectpath, pdp->reqpath)); + conn->connectpath, reqpath)); /* * Note the unix path conversion here we're doing we @@ -690,7 +708,7 @@ static NTSTATUS dfs_path_lookup(TALLOC_CTX *ctx, * unix_convert later in the codepath. */ - status = unix_convert(ctx, conn, pdp->reqpath, 0, &smb_fname, + status = unix_convert(ctx, conn, reqpath, 0, &smb_fname, ucf_flags); if (!NT_STATUS_IS_OK(status)) { @@ -864,25 +882,23 @@ NTSTATUS dfs_redirect(TALLOC_CTX *ctx, { const struct loadparm_substitution *lp_sub = loadparm_s3_global_substitution(); + char *hostname = NULL; + char *servicename = NULL; + char *reqpath = NULL; NTSTATUS status; - struct dfs_path *pdp = talloc(ctx, struct dfs_path); - - if (!pdp) { - return NT_STATUS_NO_MEMORY; - } status = parse_dfs_path(ctx, conn, path_in, allow_broken_path, - pdp); + &hostname, + &servicename, + &reqpath); if (!NT_STATUS_IS_OK(status)) { - TALLOC_FREE(pdp); return status; } - if (pdp->reqpath[0] == '\0') { - TALLOC_FREE(pdp); + if (reqpath[0] == '\0') { *pp_path_out = talloc_strdup(ctx, ""); if (!*pp_path_out) { return NT_STATUS_NO_MEMORY; @@ -895,8 +911,7 @@ NTSTATUS dfs_redirect(TALLOC_CTX *ctx, path and return OK */ if (!lp_msdfs_root(SNUM(conn))) { - *pp_path_out = talloc_strdup(ctx, pdp->reqpath); - TALLOC_FREE(pdp); + *pp_path_out = talloc_strdup(ctx, reqpath); if (!*pp_path_out) { return NT_STATUS_NO_MEMORY; } @@ -906,30 +921,27 @@ NTSTATUS dfs_redirect(TALLOC_CTX *ctx, /* If it looked like a local path (zero hostname/servicename) * just treat as a tcon-relative path. */ - if (pdp->hostname[0] == '\0' && pdp->servicename[0] == '\0') { - *pp_path_out = talloc_strdup(ctx, pdp->reqpath); - TALLOC_FREE(pdp); + if (hostname[0] == '\0' && servicename[0] == '\0') { + *pp_path_out = talloc_strdup(ctx, reqpath); if (!*pp_path_out) { return NT_STATUS_NO_MEMORY; } return NT_STATUS_OK; } - if (!( strequal(pdp->servicename, lp_servicename(talloc_tos(), lp_sub, SNUM(conn))) - || (strequal(pdp->servicename, HOMES_NAME) + if (!( strequal(servicename, lp_servicename(talloc_tos(), lp_sub, SNUM(conn))) + || (strequal(servicename, HOMES_NAME) && strequal(lp_servicename(talloc_tos(), lp_sub, SNUM(conn)), conn->session_info->unix_info->sanitized_username) )) ) { /* The given sharename doesn't match this connection. */ - TALLOC_FREE(pdp); - return NT_STATUS_OBJECT_PATH_NOT_FOUND; } status = dfs_path_lookup(ctx, conn, path_in, - pdp, + reqpath, ucf_flags, _twrp, /* twrp. */ NULL, /* size_t *consumedcntp */ @@ -949,8 +961,7 @@ NTSTATUS dfs_redirect(TALLOC_CTX *ctx, DEBUG(3,("dfs_redirect: Not redirecting %s.\n", path_in)); /* Form non-dfs tcon-relative path */ - *pp_path_out = talloc_strdup(ctx, pdp->reqpath); - TALLOC_FREE(pdp); + *pp_path_out = talloc_strdup(ctx, reqpath); if (!*pp_path_out) { return NT_STATUS_NO_MEMORY; } @@ -1013,14 +1024,10 @@ NTSTATUS get_referred_path(TALLOC_CTX *ctx, loadparm_s3_global_substitution(); struct conn_struct_tos *c = NULL; struct connection_struct *conn = NULL; + char *servicename = NULL; + char *reqpath = NULL; int snum; NTSTATUS status = NT_STATUS_NOT_FOUND; - struct dfs_path *pdp = talloc_zero(frame, struct dfs_path); - - if (!pdp) { - TALLOC_FREE(frame); - return NT_STATUS_NO_MEMORY; - } *self_referralp = False; @@ -1028,14 +1035,16 @@ NTSTATUS get_referred_path(TALLOC_CTX *ctx, NULL, dfs_path, allow_broken_path, - pdp); + NULL, + &servicename, + &reqpath); if (!NT_STATUS_IS_OK(status)) { TALLOC_FREE(frame); return status; } - jucn->service_name = talloc_strdup(ctx, pdp->servicename); - jucn->volume_name = talloc_strdup(ctx, pdp->reqpath); + jucn->service_name = talloc_strdup(ctx, servicename); + jucn->volume_name = talloc_strdup(ctx, reqpath); if (!jucn->service_name || !jucn->volume_name) { TALLOC_FREE(frame); return NT_STATUS_NO_MEMORY; @@ -1064,7 +1073,7 @@ NTSTATUS get_referred_path(TALLOC_CTX *ctx, if (!lp_msdfs_root(snum) && (*lp_msdfs_proxy(talloc_tos(), lp_sub, snum) == '\0')) { DEBUG(3,("get_referred_path: |%s| in dfs path %s is not " "a dfs root.\n", - pdp->servicename, dfs_path)); + servicename, dfs_path)); TALLOC_FREE(frame); return NT_STATUS_NOT_FOUND; } @@ -1077,7 +1086,7 @@ NTSTATUS get_referred_path(TALLOC_CTX *ctx, * user). Cope with this. */ - if (pdp->reqpath[0] == '\0') { + if (reqpath[0] == '\0') { char *tmp; struct referral *ref; size_t refcount; @@ -1158,7 +1167,7 @@ NTSTATUS get_referred_path(TALLOC_CTX *ctx, status = dfs_path_lookup(ctx, conn, dfs_path, - pdp, + reqpath, 0, /* ucf_flags */ NULL, consumedcntp, @@ -1271,45 +1280,43 @@ bool create_junction(TALLOC_CTX *ctx, const struct loadparm_substitution *lp_sub = loadparm_s3_global_substitution(); int snum; - struct dfs_path *pdp = talloc(ctx,struct dfs_path); + char *hostname = NULL; + char *servicename = NULL; + char *reqpath = NULL; NTSTATUS status; - if (!pdp) { - return False; - } status = parse_dfs_path(ctx, NULL, dfs_path, allow_broken_path, - pdp); + &hostname, + &servicename, + &reqpath); if (!NT_STATUS_IS_OK(status)) { return False; } /* check if path is dfs : validate first token */ - if (!is_myname_or_ipaddr(pdp->hostname)) { + if (!is_myname_or_ipaddr(hostname)) { DEBUG(4,("create_junction: Invalid hostname %s " "in dfs path %s\n", - pdp->hostname, dfs_path)); - TALLOC_FREE(pdp); + hostname, dfs_path)); return False; } /* Check for a non-DFS share */ - snum = lp_servicenumber(pdp->servicename); + snum = lp_servicenumber(servicename); if(snum < 0 || !lp_msdfs_root(snum)) { DEBUG(4,("create_junction: %s is not an msdfs root.\n", - pdp->servicename)); - TALLOC_FREE(pdp); + servicename)); return False; } - jucn->service_name = talloc_strdup(ctx, pdp->servicename); - jucn->volume_name = talloc_strdup(ctx, pdp->reqpath); + jucn->service_name = talloc_strdup(ctx, servicename); + jucn->volume_name = talloc_strdup(ctx, reqpath); jucn->comment = lp_comment(ctx, lp_sub, snum); - TALLOC_FREE(pdp); if (!jucn->service_name || !jucn->volume_name || ! jucn->comment) { return False; } -- 2.34.1 From 58844fa8aee5458feeb666a89fad6f98ca110b3b Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Mon, 8 Aug 2022 10:27:16 -0700 Subject: [PATCH 09/25] s3: smbd: Remove definition of struct dfs_path. BUG: https://bugzilla.samba.org/show_bug.cgi?id=15144 Signed-off-by: Jeremy Allison Reviewed-by: Ralph Boehme (cherry picked from commit 6c83c674bab8e57ecaf6271eb3a403171bbbacca) --- source3/include/msdfs.h | 7 ------- 1 file changed, 7 deletions(-) diff --git a/source3/include/msdfs.h b/source3/include/msdfs.h index 83265174373..892343fdd9a 100644 --- a/source3/include/msdfs.h +++ b/source3/include/msdfs.h @@ -56,11 +56,4 @@ struct junction_map { size_t referral_count; struct referral* referral_list; }; - -struct dfs_path { - char *hostname; - char *servicename; - char *reqpath; -}; - #endif /* _MSDFS_H */ -- 2.34.1 From 9472c84db87e97b725b72c3d243498e6c4b6c521 Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Tue, 9 Aug 2022 10:49:46 -0700 Subject: [PATCH 10/25] s3: smbd: Add helper function msdfs_servicename_matches_connection(). Not yet used so commented out. BUG: https://bugzilla.samba.org/show_bug.cgi?id=15144 Signed-off-by: Jeremy Allison Reviewed-by: Ralph Boehme (cherry picked from commit 4f5d02f8c0efc1520b2113ce656c78483deb7826) --- source3/smbd/msdfs.c | 46 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/source3/smbd/msdfs.c b/source3/smbd/msdfs.c index 21288aaf664..463601cf1ee 100644 --- a/source3/smbd/msdfs.c +++ b/source3/smbd/msdfs.c @@ -36,6 +36,52 @@ #include "lib/global_contexts.h" #include "source3/lib/substitute.h" +#if 0 +/********************************************************************** + Function to determine if a given sharename matches a connection. +**********************************************************************/ + +static bool msdfs_servicename_matches_connection(struct connection_struct *conn, + const char *servicename, + const char *vfs_user) +{ + const struct loadparm_substitution *lp_sub = + loadparm_s3_global_substitution(); + char *conn_servicename = NULL; + int snum; + bool match = false; + + if (conn == NULL) { + /* No connection always matches. */ + return true; + } + + snum = SNUM(conn); + + conn_servicename = lp_servicename(talloc_tos(), lp_sub, snum); + if (conn_servicename == NULL) { + DBG_ERR("lp_servicename() failed, OOM!\n"); + return false; + } + + if (strequal(servicename, conn_servicename)) { + match = true; + goto done; + } + if (strequal(servicename, HOMES_NAME)) { + match = true; + goto done; + } + if (strequal(vfs_user, conn_servicename)) { + match = true; + goto done; + } +done: + TALLOC_FREE(conn_servicename); + return match; +} +#endif + /********************************************************************** Parse a DFS pathname of the form /hostname/service/reqpath into the dfs_path structure. -- 2.34.1 From abce1c91b18a8f2889372549398cd06c3149eb5d Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Tue, 9 Aug 2022 10:53:18 -0700 Subject: [PATCH 11/25] s3: smbd: Use helper function msdfs_servicename_matches_connection() in parse_dfs_path(). Replaces ugly complex logic. BUG: https://bugzilla.samba.org/show_bug.cgi?id=15144 Signed-off-by: Jeremy Allison Reviewed-by: Ralph Boehme (cherry picked from commit c0a1d7c7a8a7f24890e60c7a371498949dec11c2) --- source3/smbd/msdfs.c | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/source3/smbd/msdfs.c b/source3/smbd/msdfs.c index 463601cf1ee..a48ae642ae8 100644 --- a/source3/smbd/msdfs.c +++ b/source3/smbd/msdfs.c @@ -36,7 +36,6 @@ #include "lib/global_contexts.h" #include "source3/lib/substitute.h" -#if 0 /********************************************************************** Function to determine if a given sharename matches a connection. **********************************************************************/ @@ -80,7 +79,6 @@ done: TALLOC_FREE(conn_servicename); return match; } -#endif /********************************************************************** Parse a DFS pathname of the form /hostname/service/reqpath @@ -112,14 +110,13 @@ static NTSTATUS parse_dfs_path(TALLOC_CTX *ctx, char **_servicename, char **_remaining_path) { - const struct loadparm_substitution *lp_sub = - loadparm_s3_global_substitution(); char *hostname = NULL; char *pathname_local = NULL; char *p = NULL; char *servicename = NULL; char *reqpath = NULL; char *eos_ptr = NULL; + bool servicename_matches = false; pathname_local = talloc_strdup(ctx, pathname); if (pathname_local == NULL) { @@ -196,10 +193,12 @@ static NTSTATUS parse_dfs_path(TALLOC_CTX *ctx, } /* Is this really our servicename ? */ - if (conn && !( strequal(servicename, lp_servicename(talloc_tos(), lp_sub, SNUM(conn))) - || (strequal(servicename, HOMES_NAME) - && strequal(lp_servicename(talloc_tos(), lp_sub, SNUM(conn)), - get_current_username()) )) ) { + servicename_matches = msdfs_servicename_matches_connection( + conn, + servicename, + get_current_username()); + + if (!servicename_matches) { DBG_ERR("%s is not our servicename\n", servicename); /* -- 2.34.1 From e6db841b41a22c904c5effdf7c218a6d1c83a85c Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Tue, 9 Aug 2022 10:58:24 -0700 Subject: [PATCH 12/25] s3: smbd: Use helper function msdfs_servicename_matches_connection() in dfs_redirect(). Replaces ugly complex logic. BUG: https://bugzilla.samba.org/show_bug.cgi?id=15144 Signed-off-by: Jeremy Allison Reviewed-by: Ralph Boehme (cherry picked from commit a3c9eb7931cb4da0dd5bc5d600125979dd1a7df5) --- source3/smbd/msdfs.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/source3/smbd/msdfs.c b/source3/smbd/msdfs.c index a48ae642ae8..fce887de9aa 100644 --- a/source3/smbd/msdfs.c +++ b/source3/smbd/msdfs.c @@ -925,11 +925,10 @@ NTSTATUS dfs_redirect(TALLOC_CTX *ctx, NTTIME *_twrp, char **pp_path_out) { - const struct loadparm_substitution *lp_sub = - loadparm_s3_global_substitution(); char *hostname = NULL; char *servicename = NULL; char *reqpath = NULL; + bool servicename_matches = false; NTSTATUS status; status = parse_dfs_path(ctx, @@ -974,11 +973,11 @@ NTSTATUS dfs_redirect(TALLOC_CTX *ctx, return NT_STATUS_OK; } - if (!( strequal(servicename, lp_servicename(talloc_tos(), lp_sub, SNUM(conn))) - || (strequal(servicename, HOMES_NAME) - && strequal(lp_servicename(talloc_tos(), lp_sub, SNUM(conn)), - conn->session_info->unix_info->sanitized_username) )) ) { - + servicename_matches = msdfs_servicename_matches_connection( + conn, + servicename, + conn->session_info->unix_info->sanitized_username); + if (!servicename_matches) { /* The given sharename doesn't match this connection. */ return NT_STATUS_OBJECT_PATH_NOT_FOUND; } -- 2.34.1 From b63693747ab2607debae92228b4caccf0b1a32bc Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Mon, 8 Aug 2022 11:16:17 -0700 Subject: [PATCH 13/25] s3: smbd: Add dfs_filename_convert(). Simple wrapper around parse_dfs_path(). Not yet used. This is what we will use to replace dfs_redirect() in the filename conversion code. Keep as a wrapper for now as we might want to add some error checking around the 'hostname' and 'service' returns. BUG: https://bugzilla.samba.org/show_bug.cgi?id=15144 Signed-off-by: Jeremy Allison Reviewed-by: Ralph Boehme (cherry picked from commit 245d07ab84852b829c029496618e56782d070e83) --- source3/smbd/msdfs.c | 62 ++++++++++++++++++++++++++++++++++++++++++++ source3/smbd/proto.h | 5 ++++ 2 files changed, 67 insertions(+) diff --git a/source3/smbd/msdfs.c b/source3/smbd/msdfs.c index fce887de9aa..1b66911faf0 100644 --- a/source3/smbd/msdfs.c +++ b/source3/smbd/msdfs.c @@ -902,6 +902,68 @@ static NTSTATUS dfs_path_lookup(TALLOC_CTX *ctx, return status; } +/***************************************************************** + Decides if a dfs pathname should be redirected or not. + If not, the pathname is converted to a tcon-relative local unix path + This is now a simple wrapper around parse_dfs_path() + as it does all the required checks. +*****************************************************************/ + +NTSTATUS dfs_filename_convert(TALLOC_CTX *ctx, + connection_struct *conn, + uint32_t ucf_flags, + const char *dfs_path_in, + char **pp_path_out) +{ + char *hostname = NULL; + char *servicename = NULL; + char *reqpath = NULL; + NTSTATUS status; + + status = parse_dfs_path(ctx, + conn, + dfs_path_in, + !conn->sconn->using_smb2, + &hostname, + &servicename, + &reqpath); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + /* + * Caller doesn't care about hostname + * or servicename. + */ + TALLOC_FREE(hostname); + TALLOC_FREE(servicename); + + /* + * If parse_dfs_path fell back to a local path + * after skipping hostname or servicename, ensure + * we still have called check_path_syntax() + * on the full returned local path. check_path_syntax() + * is idempotent so this is safe. + */ + if (ucf_flags & UCF_POSIX_PATHNAMES) { + status = check_path_syntax_posix(reqpath); + } else { + status = check_path_syntax(reqpath); + } + if (!NT_STATUS_IS_OK(status)) { + return status; + } + /* + * Previous (and current logic) just ignores + * the server, share components if a DFS + * path is sent on a non-DFS share except to + * check that they match an existing share. Should + * we tighten this up to return an error here ? + */ + *pp_path_out = reqpath; + return NT_STATUS_OK; +} + /***************************************************************** Decides if a dfs pathname should be redirected or not. If not, the pathname is converted to a tcon-relative local unix path diff --git a/source3/smbd/proto.h b/source3/smbd/proto.h index 33fc222311e..1c10849346e 100644 --- a/source3/smbd/proto.h +++ b/source3/smbd/proto.h @@ -560,6 +560,11 @@ bool remove_msdfs_link(const struct junction_map *jucn, struct junction_map *enum_msdfs_links(TALLOC_CTX *ctx, struct auth_session_info *session_info, size_t *p_num_jn); +NTSTATUS dfs_filename_convert(TALLOC_CTX *ctx, + connection_struct *conn, + uint32_t ucf_flags, + const char *dfs_path_in, + char **pp_path_out); NTSTATUS dfs_redirect(TALLOC_CTX *ctx, connection_struct *conn, const char *name_in, -- 2.34.1 From 4a23b9422dc09ca2e335b62bbc637b4809b16230 Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Thu, 11 Aug 2022 23:55:58 -0700 Subject: [PATCH 14/25] s3: smbd: In get referred_path(), make sure check_path_syntax() is called on returned reqpath. BUG: https://bugzilla.samba.org/show_bug.cgi?id=15144 Signed-off-by: Jeremy Allison Reviewed-by: Ralph Boehme (cherry picked from commit da625e4ab4bc670e44fcb6ad7456aa64d0f1f9d2) --- source3/smbd/msdfs.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/source3/smbd/msdfs.c b/source3/smbd/msdfs.c index 1b66911faf0..19160b03c3c 100644 --- a/source3/smbd/msdfs.c +++ b/source3/smbd/msdfs.c @@ -1149,6 +1149,13 @@ NTSTATUS get_referred_path(TALLOC_CTX *ctx, return status; } + /* Path referrals are always non-POSIX. */ + status = check_path_syntax(reqpath); + if (!NT_STATUS_IS_OK(status)) { + TALLOC_FREE(frame); + return status; + } + jucn->service_name = talloc_strdup(ctx, servicename); jucn->volume_name = talloc_strdup(ctx, reqpath); if (!jucn->service_name || !jucn->volume_name) { -- 2.34.1 From 0ab7eef5ccdaf4e49cfc89970546822aec593751 Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Thu, 11 Aug 2022 23:57:51 -0700 Subject: [PATCH 15/25] s3: smbd: In get create_junction(), make sure check_path_syntax() is called on returned reqpath. BUG: https://bugzilla.samba.org/show_bug.cgi?id=15144 Signed-off-by: Jeremy Allison Reviewed-by: Ralph Boehme (cherry picked from commit a92f4f7af0eaa035deebfb1c930ca0cc12d992d5) --- source3/smbd/msdfs.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/source3/smbd/msdfs.c b/source3/smbd/msdfs.c index 19160b03c3c..5a2413ed8b9 100644 --- a/source3/smbd/msdfs.c +++ b/source3/smbd/msdfs.c @@ -1426,6 +1426,12 @@ bool create_junction(TALLOC_CTX *ctx, return False; } + /* Junction create paths are always non-POSIX. */ + status = check_path_syntax(reqpath); + if (!NT_STATUS_IS_OK(status)) { + return false; + } + jucn->service_name = talloc_strdup(ctx, servicename); jucn->volume_name = talloc_strdup(ctx, reqpath); jucn->comment = lp_comment(ctx, lp_sub, snum); -- 2.34.1 From 8a39ea255a897308bc816f36cbf54dc349a2a2ff Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Mon, 8 Aug 2022 11:31:39 -0700 Subject: [PATCH 16/25] s3: smbd: Allow openat_pathref_dirfsp_nosymlink() to return NT_STATUS_PATH_NOT_COVERED for a DFS link on a DFS share. BUG: https://bugzilla.samba.org/show_bug.cgi?id=15144 Signed-off-by: Jeremy Allison Reviewed-by: Ralph Boehme (cherry picked from commit b5f6809593524e7e9aca1c09ff379e02a1cde61b) --- source3/smbd/files.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/source3/smbd/files.c b/source3/smbd/files.c index 1cd146dedc7..72d1bc7d555 100644 --- a/source3/smbd/files.c +++ b/source3/smbd/files.c @@ -856,6 +856,18 @@ next: *unparsed = len - parsed; } } + /* + * If we're on an MSDFS share, see if this is + * an MSDFS link. + */ + if (lp_host_msdfs() && + lp_msdfs_root(SNUM(conn)) && + (substitute != NULL) && + strnequal(*substitute, "msdfs:", 6) && + is_msdfs_link(dirfsp, &rel_fname)) + { + status = NT_STATUS_PATH_NOT_COVERED; + } } else { DBG_DEBUG("readlink_talloc failed: %s\n", -- 2.34.1 From e26307c6830afc3c3e97519aa0bfef31ea961d43 Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Mon, 8 Aug 2022 13:15:17 -0700 Subject: [PATCH 17/25] s3: smbd: In filename_convert_dirfsp_nosymlink(), allow a NT_STATUS_PATH_NOT_COVERED error to be returned. openat_pathref_dirfsp_nosymlink() can now return NT_STATUS_PATH_NOT_COVERED. Don't convert this automatically into NT_STATUS_OBJECT_PATH_NOT_FOUND. BUG: https://bugzilla.samba.org/show_bug.cgi?id=15144 Signed-off-by: Jeremy Allison Reviewed-by: Ralph Boehme (cherry picked from commit 07ef9e3029b8cca1b92d900d6ed684ca0ac6afe4) --- source3/smbd/filename.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/source3/smbd/filename.c b/source3/smbd/filename.c index 87abc8be376..41144097362 100644 --- a/source3/smbd/filename.c +++ b/source3/smbd/filename.c @@ -2549,6 +2549,11 @@ static NTSTATUS filename_convert_dirfsp_nosymlink( nt_errstr(status)); TALLOC_FREE(dirname); + if (NT_STATUS_EQUAL(status, NT_STATUS_PATH_NOT_COVERED)) { + /* MS-DFS error must propagate back out. */ + goto fail; + } + if (!NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED)) { /* * Except ACCESS_DENIED, everything else leads -- 2.34.1 From 215f7160505f0956ad3753296f80c7e49c613678 Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Mon, 8 Aug 2022 13:18:56 -0700 Subject: [PATCH 18/25] s3: smbd: In filename_convert_dirfsp_nosymlink(), cope with an MS-DFS link as the terminal component. If the terminal component was an MSDFS link, openat_pathref_fsp_case_insensitive() will return NT_STATUS_OBJECT_NAME_NOT_FOUND with a VALID_STAT of a symlink. If this is the case, check if we actually found a terminal MS-DFS link at the end of the pathname and return NT_STATUS_PATH_NOT_COVERED. BUG: https://bugzilla.samba.org/show_bug.cgi?id=15144 Signed-off-by: Jeremy Allison Reviewed-by: Ralph Boehme (cherry picked from commit d80bedc3c418b6839b1bde78ba8d3db06611be2a) --- source3/smbd/filename.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/source3/smbd/filename.c b/source3/smbd/filename.c index 41144097362..3e7909a48c8 100644 --- a/source3/smbd/filename.c +++ b/source3/smbd/filename.c @@ -2635,6 +2635,19 @@ static NTSTATUS filename_convert_dirfsp_nosymlink( char *normalized = NULL; if (VALID_STAT(smb_fname_rel->st)) { + /* + * If we're on an MSDFS share, see if this is + * an MSDFS link. + */ + if (lp_host_msdfs() && + lp_msdfs_root(SNUM(conn)) && + S_ISLNK(smb_fname_rel->st.st_ex_mode) && + is_msdfs_link(smb_dirname->fsp, smb_fname_rel)) + { + status = NT_STATUS_PATH_NOT_COVERED; + goto fail; + } + #if defined(WITH_SMB1SERVER) /* * In SMB1 posix mode, if this is a symlink, -- 2.34.1 From 77ae9545f38f817e6515d117e00648b71887d4ba Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Wed, 10 Aug 2022 11:29:33 -0700 Subject: [PATCH 19/25] s3: smbd: Remove call to dfs_redirect() from filename_convert_smb1_search_path(). Use dfs_filename_convert() instead. Code is now much simpler. BUG: https://bugzilla.samba.org/show_bug.cgi?id=15144 Signed-off-by: Jeremy Allison Reviewed-by: Ralph Boehme (cherry picked from commit fcf19d91c09edc6dfbf5bd7cbeedcd641030eb31) --- source3/smbd/filename.c | 68 ++++++++--------------------------------- 1 file changed, 12 insertions(+), 56 deletions(-) diff --git a/source3/smbd/filename.c b/source3/smbd/filename.c index 3e7909a48c8..cba012acc3e 100644 --- a/source3/smbd/filename.c +++ b/source3/smbd/filename.c @@ -1935,67 +1935,23 @@ NTSTATUS filename_convert_smb1_search_path(TALLOC_CTX *ctx, /* * We've been given a raw DFS pathname. */ - char *fname = NULL; - char *name_in_copy = NULL; - char *last_component = NULL; - - /* Work on a copy of name_in. */ - name_in_copy = talloc_strdup(ctx, name_in); - if (name_in_copy == NULL) { - return NT_STATUS_NO_MEMORY; - } - - /* - * Now we know that the last component is the - * wildcard. Copy it and truncate to remove it. - */ - p = strrchr(name_in_copy, '/'); - if (p == NULL) { - last_component = talloc_strdup(ctx, name_in_copy); - name_in_copy[0] = '\0'; - } else { - last_component = talloc_strdup(ctx, p+1); - *p = '\0'; - } - if (last_component == NULL) { - return NT_STATUS_NO_MEMORY; - } - - DBG_DEBUG("name_in_copy: %s\n", name_in_copy); - - /* - * Now we can call dfs_redirect() - * on the name without wildcard. - */ - status = dfs_redirect(ctx, - conn, - name_in_copy, - ucf_flags, - !conn->sconn->using_smb2, - NULL, - &fname); - if (!NT_STATUS_IS_OK(status)) { - DBG_DEBUG("dfs_redirect " + char *pathname = NULL; + DBG_DEBUG("Before dfs_filename_convert name_in: %s\n", name_in); + status = dfs_filename_convert(ctx, + conn, + ucf_flags, + name_in, + &pathname); + if (!NT_STATUS_IS_OK(status)) { + DBG_DEBUG("dfs_filename_convert " "failed for name %s with %s\n", - name_in_copy, + name_in, nt_errstr(status)); return status; } - /* Add the last component back. */ - if (fname[0] == '\0') { - name_in = talloc_strdup(ctx, last_component); - } else { - name_in = talloc_asprintf(ctx, - "%s/%s", - fname, - last_component); - } - if (name_in == NULL) { - return NT_STATUS_NO_MEMORY; - } ucf_flags &= ~UCF_DFS_PATHNAME; - - DBG_DEBUG("After DFS redirect name_in: %s\n", name_in); + name_in = pathname; + DBG_DEBUG("After dfs_filename_convert name_in: %s\n", name_in); } /* Get the original lcomp. */ -- 2.34.1 From 0ce8a97c18632a3abeda395431fe0d0631259b29 Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Wed, 10 Aug 2022 11:32:30 -0700 Subject: [PATCH 20/25] s3: smbd: Remove call to dfs_redirect() from filename_convert_dirfsp_nosymlink(). Use dfs_filename_convert() instead. There are now no more callers of dfs_redirect(). BUG: https://bugzilla.samba.org/show_bug.cgi?id=15144 Signed-off-by: Jeremy Allison Reviewed-by: Ralph Boehme (cherry picked from commit d20b60c3200b5e1881cdf4b59da154d1af7e3994) --- source3/smbd/filename.c | 28 +++++++++++++--------------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/source3/smbd/filename.c b/source3/smbd/filename.c index cba012acc3e..b64fb908f16 100644 --- a/source3/smbd/filename.c +++ b/source3/smbd/filename.c @@ -2384,28 +2384,26 @@ static NTSTATUS filename_convert_dirfsp_nosymlink( NTSTATUS status = NT_STATUS_UNSUCCESSFUL; if (ucf_flags & UCF_DFS_PATHNAME) { - char *fname = NULL; - NTTIME dfs_twrp = 0; - status = dfs_redirect( - mem_ctx, - conn, - name_in, - ucf_flags, - !conn->sconn->using_smb2, - &dfs_twrp, - &fname); + /* + * We've been given a raw DFS pathname. + */ + char *pathname = NULL; + DBG_DEBUG("Before dfs_filename_convert name_in: %s\n", name_in); + status = dfs_filename_convert(mem_ctx, + conn, + ucf_flags, + name_in, + &pathname); if (!NT_STATUS_IS_OK(status)) { - DBG_DEBUG("dfs_redirect " + DBG_DEBUG("dfs_filename_convert " "failed for name %s with %s\n", name_in, nt_errstr(status)); return status; } - name_in = fname; ucf_flags &= ~UCF_DFS_PATHNAME; - if (twrp == 0 && dfs_twrp != 0) { - twrp = dfs_twrp; - } + name_in = pathname; + DBG_DEBUG("After dfs_filename_convert name_in: %s\n", name_in); } if (is_fake_file_path(name_in)) { -- 2.34.1 From 8c469cafeb9944af7a1882f570f1acc4d52babf3 Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Wed, 10 Aug 2022 11:34:24 -0700 Subject: [PATCH 21/25] s3: smbd: Remove dfs_redirect(). A moment of silence please. BUG: https://bugzilla.samba.org/show_bug.cgi?id=15144 Signed-off-by: Jeremy Allison Reviewed-by: Ralph Boehme (cherry picked from commit 6b1224b22012b54b1ae20b682daf61c877362a7b) --- source3/smbd/msdfs.c | 115 ------------------------------------------- source3/smbd/proto.h | 7 --- 2 files changed, 122 deletions(-) diff --git a/source3/smbd/msdfs.c b/source3/smbd/msdfs.c index 5a2413ed8b9..d48c5d6c906 100644 --- a/source3/smbd/msdfs.c +++ b/source3/smbd/msdfs.c @@ -964,121 +964,6 @@ NTSTATUS dfs_filename_convert(TALLOC_CTX *ctx, return NT_STATUS_OK; } -/***************************************************************** - Decides if a dfs pathname should be redirected or not. - If not, the pathname is converted to a tcon-relative local unix path - - search_wcard_flag: this flag performs 2 functions both related - to searches. See resolve_dfs_path() and parse_dfs_path_XX() - for details. - - This function can return NT_STATUS_OK, meaning use the returned path as-is - (mapped into a local path). - or NT_STATUS_NOT_COVERED meaning return a DFS redirect, or - any other NT_STATUS error which is a genuine error to be - returned to the client. -*****************************************************************/ - -NTSTATUS dfs_redirect(TALLOC_CTX *ctx, - connection_struct *conn, - const char *path_in, - uint32_t ucf_flags, - bool allow_broken_path, - NTTIME *_twrp, - char **pp_path_out) -{ - char *hostname = NULL; - char *servicename = NULL; - char *reqpath = NULL; - bool servicename_matches = false; - NTSTATUS status; - - status = parse_dfs_path(ctx, - conn, - path_in, - allow_broken_path, - &hostname, - &servicename, - &reqpath); - if (!NT_STATUS_IS_OK(status)) { - return status; - } - - if (reqpath[0] == '\0') { - *pp_path_out = talloc_strdup(ctx, ""); - if (!*pp_path_out) { - return NT_STATUS_NO_MEMORY; - } - DEBUG(5,("dfs_redirect: self-referral.\n")); - return NT_STATUS_OK; - } - - /* If dfs pathname for a non-dfs share, convert to tcon-relative - path and return OK */ - - if (!lp_msdfs_root(SNUM(conn))) { - *pp_path_out = talloc_strdup(ctx, reqpath); - if (!*pp_path_out) { - return NT_STATUS_NO_MEMORY; - } - return NT_STATUS_OK; - } - - /* If it looked like a local path (zero hostname/servicename) - * just treat as a tcon-relative path. */ - - if (hostname[0] == '\0' && servicename[0] == '\0') { - *pp_path_out = talloc_strdup(ctx, reqpath); - if (!*pp_path_out) { - return NT_STATUS_NO_MEMORY; - } - return NT_STATUS_OK; - } - - servicename_matches = msdfs_servicename_matches_connection( - conn, - servicename, - conn->session_info->unix_info->sanitized_username); - if (!servicename_matches) { - /* The given sharename doesn't match this connection. */ - return NT_STATUS_OBJECT_PATH_NOT_FOUND; - } - - status = dfs_path_lookup(ctx, - conn, - path_in, - reqpath, - ucf_flags, - _twrp, /* twrp. */ - NULL, /* size_t *consumedcntp */ - NULL, /* struct referral **ppreflist */ - NULL); /* size_t *preferral_count */ - if (!NT_STATUS_IS_OK(status)) { - if (NT_STATUS_EQUAL(status, NT_STATUS_PATH_NOT_COVERED)) { - DEBUG(3,("dfs_redirect: Redirecting %s\n", path_in)); - } else { - DEBUG(10,("dfs_redirect: dfs_path_lookup " - "failed for %s with %s\n", - path_in, nt_errstr(status) )); - } - return status; - } - - DEBUG(3,("dfs_redirect: Not redirecting %s.\n", path_in)); - - /* Form non-dfs tcon-relative path */ - *pp_path_out = talloc_strdup(ctx, reqpath); - if (!*pp_path_out) { - return NT_STATUS_NO_MEMORY; - } - - DEBUG(3,("dfs_redirect: Path %s converted to non-dfs path %s\n", - path_in, - *pp_path_out)); - - return NT_STATUS_OK; -} - /********************************************************************** Return a self referral. **********************************************************************/ diff --git a/source3/smbd/proto.h b/source3/smbd/proto.h index 1c10849346e..bc305bce296 100644 --- a/source3/smbd/proto.h +++ b/source3/smbd/proto.h @@ -565,13 +565,6 @@ NTSTATUS dfs_filename_convert(TALLOC_CTX *ctx, uint32_t ucf_flags, const char *dfs_path_in, char **pp_path_out); -NTSTATUS dfs_redirect(TALLOC_CTX *ctx, - connection_struct *conn, - const char *name_in, - uint32_t ucf_flags, - bool allow_broken_path, - NTTIME *twrp, - char **pp_name_out); struct connection_struct; struct smb_filename; -- 2.34.1 From f1e52f44d3961ed4d3319b158106edc13a0aa918 Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Tue, 9 Aug 2022 12:07:30 -0700 Subject: [PATCH 22/25] s3: smbd: Add new version of dfs_path_lookup() that uses filename_convert_dirfsp(). Commented out as not yet used but it's easier to see the new logic this way. BUG: https://bugzilla.samba.org/show_bug.cgi?id=15144 Signed-off-by: Jeremy Allison Reviewed-by: Ralph Boehme (cherry picked from commit 22d4f62537199d9454be312a546e251f04022497) --- source3/smbd/msdfs.c | 261 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 261 insertions(+) diff --git a/source3/smbd/msdfs.c b/source3/smbd/msdfs.c index d48c5d6c906..ab7f9f0ad78 100644 --- a/source3/smbd/msdfs.c +++ b/source3/smbd/msdfs.c @@ -713,6 +713,267 @@ bool is_msdfs_link(struct files_struct *dirfsp, return (NT_STATUS_IS_OK(status)); } +#if 0 +/***************************************************************** + Used by other functions to decide if a dfs path is remote, + and to get the list of referred locations for that remote path. + + consumedcntp: how much of the dfs path is being redirected. the client + should try the remaining path on the redirected server. +*****************************************************************/ + +static NTSTATUS dfs_path_lookup(TALLOC_CTX *ctx, + connection_struct *conn, + const char *dfspath, /* Incoming complete dfs path */ + const char *reqpath, /* Parsed out remaining path. */ + uint32_t ucf_flags, + size_t *consumedcntp, + struct referral **ppreflist, + size_t *preferral_count) +{ + NTSTATUS status; + struct smb_filename *parent_smb_fname = NULL; + struct smb_filename *smb_fname_rel = NULL; + NTTIME twrp = 0; + char *local_pathname = NULL; + char *last_component = NULL; + char *atname = NULL; + size_t removed_components = 0; + bool posix = (ucf_flags & UCF_POSIX_PATHNAMES); + char *p = NULL; + char *canon_dfspath = NULL; + + DBG_DEBUG("Conn path = %s reqpath = %s\n", conn->connectpath, reqpath); + + local_pathname = talloc_strdup(ctx, reqpath); + if (local_pathname == NULL) { + status = NT_STATUS_NO_MEMORY; + goto out; + } + + /* We know reqpath isn't a DFS path. */ + ucf_flags &= ~UCF_DFS_PATHNAME; + + if (ucf_flags & UCF_GMT_PATHNAME) { + extract_snapshot_token(local_pathname, &twrp); + ucf_flags &= ~UCF_GMT_PATHNAME; + } + + /* + * We should have been given a DFS path to resolve. + * This should return NT_STATUS_PATH_NOT_COVERED. + * + * Do a pathname walk, stripping off components + * until we get NT_STATUS_OK instead of + * NT_STATUS_PATH_NOT_COVERED. + * + * Fail on any other error. + */ + + for (;;) { + TALLOC_CTX *frame = NULL; + struct files_struct *dirfsp = NULL; + struct smb_filename *smb_fname_walk = NULL; + + TALLOC_FREE(parent_smb_fname); + + /* + * Use a local stackframe as filename_convert_dirfsp() + * opens handles on the last two components in the path. + * Allow these to be freed as we step back through + * the local_pathname. + */ + frame = talloc_stackframe(); + status = filename_convert_dirfsp(frame, + conn, + local_pathname, + ucf_flags, + twrp, + &dirfsp, + &smb_fname_walk); + /* If we got a name, save it. */ + if (smb_fname_walk != NULL) { + parent_smb_fname = talloc_move(ctx, &smb_fname_walk); + } + TALLOC_FREE(frame); + + if (!NT_STATUS_EQUAL(status, NT_STATUS_PATH_NOT_COVERED)) { + /* + * For any other status than NT_STATUS_PATH_NOT_COVERED + * (including NT_STATUS_OK) we exit the walk. + * If it's an error we catch it outside the loop. + */ + break; + } + + /* Step back one component and save it off as last_component. */ + TALLOC_FREE(last_component); + p = strrchr(local_pathname, '/'); + if (p == NULL) { + /* + * We removed all components. + * Go around once more to make + * sure we can open the root '\0'. + */ + last_component = talloc_strdup(ctx, local_pathname); + *local_pathname = '\0'; + } else { + last_component = talloc_strdup(ctx, p+1); + *p = '\0'; + } + if (last_component == NULL) { + status = NT_STATUS_NO_MEMORY; + goto out; + } + /* Integer wrap check. */ + if (removed_components + 1 < removed_components) { + status = NT_STATUS_INVALID_PARAMETER; + goto out; + } + removed_components++; + } + + if (!NT_STATUS_IS_OK(status)) { + DBG_DEBUG("dfspath = %s. reqpath = %s. Error %s.\n", + dfspath, + reqpath, + nt_errstr(status)); + goto out; + } + + if (parent_smb_fname->fsp == NULL) { + /* Unable to open parent. */ + DBG_DEBUG("dfspath = %s. reqpath = %s. " + "Unable to open parent directory (%s).\n", + dfspath, + reqpath, + smb_fname_str_dbg(parent_smb_fname)); + status = NT_STATUS_OBJECT_PATH_NOT_FOUND; + goto out; + } + + if (removed_components == 0) { + /* + * We never got NT_STATUS_PATH_NOT_COVERED. + * There was no DFS redirect. + */ + DBG_DEBUG("dfspath = %s. reqpath = %s. " + "No removed components.\n", + dfspath, + reqpath); + status = NT_STATUS_OBJECT_PATH_NOT_FOUND; + goto out; + } + + /* + * One of the removed_components was the MSDFS link + * at the end. We need to count this in the resolved + * path below, so remove one from removed_components. + */ + removed_components--; + + /* + * Now parent_smb_fname->fsp is the parent directory dirfsp, + * last_component is the untranslated MS-DFS link name. + * Search for it in the parent directory to get the real + * filename on disk. + */ + status = get_real_filename_at(parent_smb_fname->fsp, + last_component, + ctx, + &atname); + + if (!NT_STATUS_IS_OK(status)) { + DBG_DEBUG("dfspath = %s. reqpath = %s " + "get_real_filename_at(%s, %s) error (%s)\n", + dfspath, + reqpath, + smb_fname_str_dbg(parent_smb_fname), + last_component, + nt_errstr(status)); + goto out; + } + + smb_fname_rel = synthetic_smb_fname(ctx, + atname, + NULL, + NULL, + twrp, + posix ? SMB_FILENAME_POSIX_PATH : 0); + if (smb_fname_rel == NULL) { + status = NT_STATUS_NO_MEMORY; + goto out; + } + + /* Get the referral to return. */ + status = SMB_VFS_READ_DFS_PATHAT(conn, + ctx, + parent_smb_fname->fsp, + smb_fname_rel, + ppreflist, + preferral_count); + if (!NT_STATUS_IS_OK(status)) { + DBG_DEBUG("dfspath = %s. reqpath = %s. " + "SMB_VFS_READ_DFS_PATHAT(%s, %s) error (%s)\n", + dfspath, + reqpath, + smb_fname_str_dbg(parent_smb_fname), + smb_fname_str_dbg(smb_fname_rel), + nt_errstr(status)); + goto out; + } + + /* + * Now we must work out how much of the + * given pathname we consumed. + */ + canon_dfspath = talloc_strdup(ctx, dfspath); + if (!canon_dfspath) { + status = NT_STATUS_NO_MEMORY; + goto out; + } + /* Canonicalize the raw dfspath. */ + string_replace(canon_dfspath, '\\', '/'); + + /* + * reqpath comes out of parse_dfs_path(), so it has + * no trailing backslash. Make sure that canon_dfspath hasn't either. + */ + trim_char(canon_dfspath, 0, '/'); + + DBG_DEBUG("Unconsumed path: %s\n", canon_dfspath); + + while (removed_components > 0) { + p = strrchr(canon_dfspath, '/'); + if (p != NULL) { + *p = '\0'; + } + removed_components--; + if (p == NULL && removed_components != 0) { + DBG_ERR("Component missmatch. path = %s, " + "%zu components left\n", + canon_dfspath, + removed_components); + status = NT_STATUS_OBJECT_PATH_NOT_FOUND; + goto out; + } + } + *consumedcntp = strlen(canon_dfspath); + DBG_DEBUG("Path consumed: %s (%zu)\n", canon_dfspath, *consumedcntp); + status = NT_STATUS_OK; + + out: + + TALLOC_FREE(parent_smb_fname); + TALLOC_FREE(local_pathname); + TALLOC_FREE(last_component); + TALLOC_FREE(atname); + TALLOC_FREE(smb_fname_rel); + TALLOC_FREE(canon_dfspath); + return status; +} +#endif + /***************************************************************** Used by other functions to decide if a dfs path is remote, and to get the list of referred locations for that remote path. -- 2.34.1 From ce38e08e1449f275a2a98a74d41ec8906f831aa5 Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Tue, 9 Aug 2022 12:11:07 -0700 Subject: [PATCH 23/25] s3: smbd: Switch get_referred_path() over to use the new dfs_path_lookup(). New function doesn't need a TWRP argument and returns NT_STATUS_OK on successful redirect, not NT_STATUS_PATH_NOT_COVERED. Comment out the old dfs_path_lookup(). There are now no more users of unix_convert(). BUG: https://bugzilla.samba.org/show_bug.cgi?id=15144 Signed-off-by: Jeremy Allison Reviewed-by: Ralph Boehme (cherry picked from commit 88e8bfec59412fdc0e83251fef60b45d2cc3a884) --- source3/smbd/msdfs.c | 31 ++++++------------------------- 1 file changed, 6 insertions(+), 25 deletions(-) diff --git a/source3/smbd/msdfs.c b/source3/smbd/msdfs.c index ab7f9f0ad78..55ab12a75e3 100644 --- a/source3/smbd/msdfs.c +++ b/source3/smbd/msdfs.c @@ -713,7 +713,6 @@ bool is_msdfs_link(struct files_struct *dirfsp, return (NT_STATUS_IS_OK(status)); } -#if 0 /***************************************************************** Used by other functions to decide if a dfs path is remote, and to get the list of referred locations for that remote path. @@ -972,8 +971,8 @@ static NTSTATUS dfs_path_lookup(TALLOC_CTX *ctx, TALLOC_FREE(canon_dfspath); return status; } -#endif +#if 0 /***************************************************************** Used by other functions to decide if a dfs path is remote, and to get the list of referred locations for that remote path. @@ -1162,6 +1161,7 @@ static NTSTATUS dfs_path_lookup(TALLOC_CTX *ctx, TALLOC_FREE(smb_fname); return status; } +#endif /***************************************************************** Decides if a dfs pathname should be redirected or not. @@ -1420,40 +1420,21 @@ NTSTATUS get_referred_path(TALLOC_CTX *ctx, } } - /* If this is a DFS path dfs_lookup should return - * NT_STATUS_PATH_NOT_COVERED. */ - status = dfs_path_lookup(ctx, conn, dfs_path, reqpath, 0, /* ucf_flags */ - NULL, consumedcntp, &jucn->referral_list, &jucn->referral_count); - if (!NT_STATUS_EQUAL(status, NT_STATUS_PATH_NOT_COVERED)) { - DEBUG(3,("get_referred_path: No valid referrals for path %s\n", - dfs_path)); - if (NT_STATUS_IS_OK(status)) { - /* - * We are in an error path here (we - * know it's not a DFS path), but - * dfs_path_lookup() can return - * NT_STATUS_OK. Ensure we always - * return a valid error code. - * - * #9588 - ACLs are not inherited to directories - * for DFS shares. - */ - status = NT_STATUS_NOT_FOUND; - } - goto err_exit; + if (!NT_STATUS_IS_OK(status)) { + DBG_NOTICE("No valid referrals for path %s (%s)\n", + dfs_path, + nt_errstr(status)); } - status = NT_STATUS_OK; - err_exit: TALLOC_FREE(frame); return status; } -- 2.34.1 From 3b266f1ff88b10fc62ff12f8f9f374dbb0b1bed1 Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Tue, 9 Aug 2022 12:13:10 -0700 Subject: [PATCH 24/25] s3: smbd: Remove the old dfs_path_lookup() code. BUG: https://bugzilla.samba.org/show_bug.cgi?id=15144 Signed-off-by: Jeremy Allison Reviewed-by: Ralph Boehme (cherry picked from commit cc638c25e0332d366016880d174d9349940cba3f) --- source3/smbd/msdfs.c | 191 ------------------------------------------- 1 file changed, 191 deletions(-) diff --git a/source3/smbd/msdfs.c b/source3/smbd/msdfs.c index 55ab12a75e3..4819df35837 100644 --- a/source3/smbd/msdfs.c +++ b/source3/smbd/msdfs.c @@ -972,197 +972,6 @@ static NTSTATUS dfs_path_lookup(TALLOC_CTX *ctx, return status; } -#if 0 -/***************************************************************** - Used by other functions to decide if a dfs path is remote, - and to get the list of referred locations for that remote path. - - consumedcntp: how much of the dfs path is being redirected. the client - should try the remaining path on the redirected server. - - If this returns NT_STATUS_PATH_NOT_COVERED the contents of the msdfs - link redirect are in targetpath. -*****************************************************************/ - -static NTSTATUS dfs_path_lookup(TALLOC_CTX *ctx, - connection_struct *conn, - const char *dfspath, /* Incoming complete dfs path */ - const char *reqpath, /* Parsed out remaining path. */ - uint32_t ucf_flags, - NTTIME *_twrp, - size_t *consumedcntp, - struct referral **ppreflist, - size_t *preferral_count) -{ - char *p = NULL; - char *q = NULL; - NTSTATUS status; - struct smb_filename *smb_fname = NULL; - struct smb_filename *parent_fname = NULL; - struct smb_filename *atname = NULL; - char *canon_dfspath = NULL; /* Canonicalized dfs path. (only '/' - components). */ - - DEBUG(10,("dfs_path_lookup: Conn path = %s reqpath = %s\n", - conn->connectpath, reqpath)); - - /* - * Note the unix path conversion here we're doing we - * throw away. We're looking for a symlink for a dfs - * resolution, if we don't find it we'll do another - * unix_convert later in the codepath. - */ - - status = unix_convert(ctx, conn, reqpath, 0, &smb_fname, - ucf_flags); - - if (!NT_STATUS_IS_OK(status)) { - if (!NT_STATUS_EQUAL(status, - NT_STATUS_OBJECT_PATH_NOT_FOUND)) { - return status; - } - if (smb_fname == NULL || smb_fname->base_name == NULL) { - return status; - } - } - - /* Optimization - check if we can redirect the whole path. */ - status = parent_pathref(ctx, - conn->cwd_fsp, - smb_fname, - &parent_fname, - &atname); - if (NT_STATUS_IS_OK(status)) { - /* - * We must have a parent_fname->fsp before - * we can call SMB_VFS_READ_DFS_PATHAT(). - */ - status = SMB_VFS_READ_DFS_PATHAT(conn, - ctx, - parent_fname->fsp, - atname, - ppreflist, - preferral_count); - /* We're now done with parent_fname and atname. */ - TALLOC_FREE(parent_fname); - - if (NT_STATUS_IS_OK(status)) { - DBG_INFO("%s resolves to a valid dfs link\n", - dfspath); - - if (consumedcntp) { - *consumedcntp = strlen(dfspath); - } - status = NT_STATUS_PATH_NOT_COVERED; - goto out; - } - } - - /* Prepare to test only for '/' components in the given path, - * so if a Windows path replace all '\\' characters with '/'. - * For a POSIX DFS path we know all separators are already '/'. */ - - canon_dfspath = talloc_strdup(ctx, dfspath); - if (!canon_dfspath) { - status = NT_STATUS_NO_MEMORY; - goto out; - } - string_replace(canon_dfspath, '\\', '/'); - - /* - * localpath comes out of unix_convert, so it has - * no trailing backslash. Make sure that canon_dfspath hasn't either. - * Fix for bug #4860 from Jan Martin . - */ - - trim_char(canon_dfspath,0,'/'); - - /* - * Redirect if any component in the path is a link. - * We do this by walking backwards through the - * local path, chopping off the last component - * in both the local path and the canonicalized - * DFS path. If we hit a DFS link then we're done. - */ - - p = strrchr_m(smb_fname->base_name, '/'); - if (consumedcntp) { - q = strrchr_m(canon_dfspath, '/'); - } - - while (p) { - *p = '\0'; - if (q) { - *q = '\0'; - } - - /* - * Ensure parent_pathref() calls vfs_stat() on - * the newly truncated path. - */ - SET_STAT_INVALID(smb_fname->st); - status = parent_pathref(ctx, - conn->cwd_fsp, - smb_fname, - &parent_fname, - &atname); - if (NT_STATUS_IS_OK(status)) { - /* - * We must have a parent_fname->fsp before - * we can call SMB_VFS_READ_DFS_PATHAT(). - */ - status = SMB_VFS_READ_DFS_PATHAT(conn, - ctx, - parent_fname->fsp, - atname, - ppreflist, - preferral_count); - - /* We're now done with parent_fname and atname. */ - TALLOC_FREE(parent_fname); - - if (NT_STATUS_IS_OK(status)) { - DBG_INFO("Redirecting %s because " - "parent %s is a dfs link\n", - dfspath, - smb_fname_str_dbg(smb_fname)); - - if (consumedcntp) { - *consumedcntp = strlen(canon_dfspath); - DBG_DEBUG("Path consumed: %s " - "(%zu)\n", - canon_dfspath, - *consumedcntp); - } - - status = NT_STATUS_PATH_NOT_COVERED; - goto out; - } - } - - /* Step back on the filesystem. */ - p = strrchr_m(smb_fname->base_name, '/'); - - if (consumedcntp) { - /* And in the canonicalized dfs path. */ - q = strrchr_m(canon_dfspath, '/'); - } - } - - if ((ucf_flags & UCF_GMT_PATHNAME) && _twrp != NULL) { - *_twrp = smb_fname->twrp; - } - - status = NT_STATUS_OK; - out: - - /* This should already be free, but make sure. */ - TALLOC_FREE(parent_fname); - TALLOC_FREE(smb_fname); - return status; -} -#endif - /***************************************************************** Decides if a dfs pathname should be redirected or not. If not, the pathname is converted to a tcon-relative local unix path -- 2.34.1 From 74eb042edab52463ba735af3d590dad244eb4f99 Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Mon, 8 Aug 2022 21:59:14 -0700 Subject: [PATCH 25/25] s3: smbd: Remove unix_convert() and associated functions. All code now uses filename_convert_dirfsp() for race-free filename conversion. Best viewed with: $ git show --patience ---------------- / \ / REST \ / IN \ / PEACE \ / \ | | | unix_convert | | | | | | 9th August | | 2022 | | | | | *| * * * | * _________)/\\_//(\/(/\)/\//\/\///\/|_)_______ BUG: https://bugzilla.samba.org/show_bug.cgi?id=15144 Signed-off-by: Jeremy Allison Reviewed-by: Ralph Boehme Autobuild-User(master): Jeremy Allison Autobuild-Date(master): Fri Aug 12 19:18:25 UTC 2022 on sn-devel-184 (cherry picked from commit 78e4aac76df977cea6cdbcfdf082fd3acdffbd95) --- source3/smbd/filename.c | 1565 +++------------------------------------ source3/smbd/proto.h | 12 - 2 files changed, 107 insertions(+), 1470 deletions(-) diff --git a/source3/smbd/filename.c b/source3/smbd/filename.c index b64fb908f16..f362aee9452 100644 --- a/source3/smbd/filename.c +++ b/source3/smbd/filename.c @@ -31,15 +31,6 @@ #include "smbd/globals.h" #include "lib/util/memcache.h" -static NTSTATUS get_real_filename(connection_struct *conn, - struct smb_filename *path, - const char *name, - TALLOC_CTX *mem_ctx, - char **found_name); - -static NTSTATUS check_name(connection_struct *conn, - const struct smb_filename *smb_fname); - uint32_t ucf_flags_from_smb_request(struct smb_request *req) { uint32_t ucf_flags = 0; @@ -80,10 +71,6 @@ uint32_t filename_create_ucf_flags(struct smb_request *req, uint32_t create_disp return ucf_flags; } -static NTSTATUS build_stream_path(TALLOC_CTX *mem_ctx, - connection_struct *conn, - struct smb_filename *smb_fname); - /**************************************************************************** Mangle the 2nd name and check if it is then equal to the first name. ****************************************************************************/ @@ -100,153 +87,6 @@ static bool mangled_equal(const char *name1, return strequal(name1, mname); } -static NTSTATUS check_for_dot_component(const struct smb_filename *smb_fname) -{ - /* Ensure we catch all names with in "/." - this is disallowed under Windows and - in POSIX they've already been removed. */ - const char *p = strstr(smb_fname->base_name, "/."); /*mb safe*/ - if (p) { - if (p[2] == '/') { - /* Error code within a pathname. */ - return NT_STATUS_OBJECT_PATH_NOT_FOUND; - } else if (p[2] == '\0') { - /* Error code at the end of a pathname. */ - return NT_STATUS_OBJECT_NAME_INVALID; - } - } - return NT_STATUS_OK; -} - -/**************************************************************************** - Optimization for common case where the missing part - is in the last component and the client already - sent the correct case. - Returns NT_STATUS_OK to mean continue the tree walk - (possibly with modified start pointer). - Any other NT_STATUS_XXX error means terminate the path - lookup here. -****************************************************************************/ - -static NTSTATUS check_parent_exists(TALLOC_CTX *ctx, - connection_struct *conn, - bool posix_pathnames, - const struct smb_filename *smb_fname, - char **pp_dirpath, - char **pp_start, - int *p_parent_stat_errno) -{ - char *parent_name = NULL; - struct smb_filename *parent_fname = NULL; - const char *last_component = NULL; - NTSTATUS status; - int ret; - - if (!parent_dirname(ctx, smb_fname->base_name, - &parent_name, - &last_component)) { - return NT_STATUS_NO_MEMORY; - } - - if (!posix_pathnames) { - if (ms_has_wild(parent_name)) { - goto no_optimization_out; - } - } - - /* - * If there was no parent component in - * smb_fname->base_name then don't do this - * optimization. - */ - if (smb_fname->base_name == last_component) { - goto no_optimization_out; - } - - parent_fname = synthetic_smb_fname(ctx, - parent_name, - NULL, - NULL, - smb_fname->twrp, - smb_fname->flags); - if (parent_fname == NULL) { - return NT_STATUS_NO_MEMORY; - } - - ret = vfs_stat(conn, parent_fname); - - /* If the parent stat failed, just continue - with the normal tree walk. */ - - if (ret == -1) { - /* - * Optimization. Preserving the - * errno from the STAT/LSTAT here - * will allow us to save a duplicate - * STAT/LSTAT system call of the parent - * pathname in a hot code path in the caller. - */ - if (p_parent_stat_errno != NULL) { - *p_parent_stat_errno = errno; - } - goto no_optimization_out; - } - - status = check_for_dot_component(parent_fname); - if (!NT_STATUS_IS_OK(status)) { - return status; - } - - /* Parent exists - set "start" to be the - * last component to shorten the tree walk. */ - - /* - * Safe to use discard_const_p - * here as last_component points - * into our smb_fname->base_name. - */ - *pp_start = discard_const_p(char, last_component); - - /* Update dirpath. */ - TALLOC_FREE(*pp_dirpath); - *pp_dirpath = talloc_strdup(ctx, parent_fname->base_name); - if (!*pp_dirpath) { - return NT_STATUS_NO_MEMORY; - } - - DEBUG(5,("check_parent_exists: name " - "= %s, dirpath = %s, " - "start = %s\n", - smb_fname->base_name, - *pp_dirpath, - *pp_start)); - - return NT_STATUS_OK; - - no_optimization_out: - - /* - * We must still return an *pp_dirpath - * initialized to ".", and a *pp_start - * pointing at smb_fname->base_name. - */ - - TALLOC_FREE(parent_name); - TALLOC_FREE(parent_fname); - - *pp_dirpath = talloc_strdup(ctx, "."); - if (*pp_dirpath == NULL) { - return NT_STATUS_NO_MEMORY; - } - /* - * Safe to use discard_const_p - * here as by convention smb_fname->base_name - * is allocated off ctx. - */ - *pp_start = discard_const_p(char, smb_fname->base_name); - return NT_STATUS_OK; -} - static bool find_snapshot_token( const char *filename, const char **_start, @@ -255,1138 +95,148 @@ static bool find_snapshot_token( { const char *start = NULL; const char *end = NULL; - struct tm tm; - time_t t; - - start = strstr_m(filename, "@GMT-"); - - if (start == NULL) { - return false; - } - - if ((start > filename) && (start[-1] != '/')) { - /* the GMT-token does not start a path-component */ - return false; - } - - end = strptime(start, GMT_FORMAT, &tm); - if (end == NULL) { - /* Not a valid timestring. */ - return false; - } - - if ((end[0] != '\0') && (end[0] != '/')) { - /* - * It is not a complete path component, i.e. the path - * component continues after the gmt-token. - */ - return false; - } - - tm.tm_isdst = -1; - t = timegm(&tm); - unix_to_nt_time(twrp, t); - - DBG_DEBUG("Extracted @GMT-Timestamp %s\n", - nt_time_string(talloc_tos(), *twrp)); - - *_start = start; - - if (end[0] == '/') { - end += 1; - } - *_next_component = end; - - return true; -} - -bool extract_snapshot_token(char *fname, NTTIME *twrp) -{ - const char *start = NULL; - const char *next = NULL; - size_t remaining; - bool found; - - found = find_snapshot_token(fname, &start, &next, twrp); - if (!found) { - return false; - } - - remaining = strlen(next); - memmove(discard_const_p(char, start), next, remaining+1); - - return true; -} - -/* - * Strip a valid @GMT-token from any incoming filename path, - * adding any NTTIME encoded in the pathname into the - * twrp field of the passed in smb_fname. - * - * Valid @GMT-tokens look like @GMT-YYYY-MM-DD-HH-MM-SS - * at the *start* of a pathname component. - * - * If twrp is passed in then smb_fname->twrp is set to that - * value, and the @GMT-token part of the filename is removed - * and does not change the stored smb_fname->twrp. - * - */ - -NTSTATUS canonicalize_snapshot_path(struct smb_filename *smb_fname, - uint32_t ucf_flags, - NTTIME twrp) -{ - bool found; - - if (twrp != 0) { - smb_fname->twrp = twrp; - } - - if (!(ucf_flags & UCF_GMT_PATHNAME)) { - return NT_STATUS_OK; - } - - found = extract_snapshot_token(smb_fname->base_name, &twrp); - if (!found) { - return NT_STATUS_OK; - } - - if (smb_fname->twrp == 0) { - smb_fname->twrp = twrp; - } - - return NT_STATUS_OK; -} - -static bool strnorm(char *s, int case_default) -{ - if (case_default == CASE_UPPER) - return strupper_m(s); - else - return strlower_m(s); -} - -/* - * Utility function to normalize case on an incoming client filename - * if required on this connection struct. - * Performs an in-place case conversion guaranteed to stay the same size. - */ - -static NTSTATUS normalize_filename_case(connection_struct *conn, - char *filename, - uint32_t ucf_flags) -{ - bool ok; - - if (ucf_flags & UCF_POSIX_PATHNAMES) { - /* - * POSIX never normalizes filename case. - */ - return NT_STATUS_OK; - } - if (!conn->case_sensitive) { - return NT_STATUS_OK; - } - if (conn->case_preserve) { - return NT_STATUS_OK; - } - if (conn->short_case_preserve) { - return NT_STATUS_OK; - } - ok = strnorm(filename, lp_default_case(SNUM(conn))); - if (!ok) { - return NT_STATUS_INVALID_PARAMETER; - } - return NT_STATUS_OK; -} - -/**************************************************************************** -This routine is called to convert names from the dos namespace to unix -namespace. It needs to handle any case conversions, mangling, format changes, -streams etc. - -We assume that we have already done a chdir() to the right "root" directory -for this service. - -Conversion to basic unix format is already done in check_path_syntax(). - -Names must be relative to the root of the service - any leading /. and -trailing /'s should have been trimmed by check_path_syntax(). - -The function will return an NTSTATUS error if some part of the name except for -the last part cannot be resolved, else NT_STATUS_OK. - -Note NT_STATUS_OK doesn't mean the name exists or is valid, just that we -didn't get any fatal errors that should immediately terminate the calling SMB -processing whilst resolving. - -If the orig_path was a stream, smb_filename->base_name will point to the base -filename, and smb_filename->stream_name will point to the stream name. If -orig_path was not a stream, then smb_filename->stream_name will be NULL. - -On exit from unix_convert, the smb_filename->st stat struct will be populated -if the file exists and was found, if not this stat struct will be filled with -zeros (and this can be detected by checking for nlinks = 0, which can never be -true for any file). -****************************************************************************/ - -struct uc_state { - TALLOC_CTX *mem_ctx; - struct connection_struct *conn; - struct smb_filename *smb_fname; - const char *orig_path; - uint32_t ucf_flags; - char *name; - char *end; - char *dirpath; - char *stream; - bool component_was_mangled; - bool posix_pathnames; - bool done; - bool case_sensitive; - bool case_preserve; - bool short_case_preserve; -}; - -static NTSTATUS unix_convert_step_search_fail( - struct uc_state *state, NTSTATUS status) -{ - char *unmangled; - - if (state->end) { - /* - * An intermediate part of the name - * can't be found. - */ - DBG_DEBUG("Intermediate [%s] missing\n", - state->name); - *state->end = '/'; - - /* - * We need to return the fact that the - * intermediate name resolution failed. - * This is used to return an error of - * ERRbadpath rather than ERRbadfile. - * Some Windows applications depend on - * the difference between these two - * errors. - */ - - /* - * ENOENT, ENOTDIR and ELOOP all map - * to NT_STATUS_OBJECT_PATH_NOT_FOUND - * in the filename walk. - */ - if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND) || - NT_STATUS_EQUAL(status, NT_STATUS_STOPPED_ON_SYMLINK) || - NT_STATUS_EQUAL(status, NT_STATUS_NOT_A_DIRECTORY)) { - status = NT_STATUS_OBJECT_PATH_NOT_FOUND; - } - return status; - } - - /* - * ENOENT/EACCESS are the only valid errors - * here. - */ - - if (NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED)) { - if ((state->ucf_flags & UCF_PREP_CREATEFILE) == 0) { - /* - * Could be a symlink pointing to - * a directory outside the share - * to which we don't have access. - * If so, we need to know that here - * so we can return the correct error code. - * check_name() is never called if we - * error out of filename_convert(). - */ - int ret; - struct smb_filename dname = (struct smb_filename) { - .base_name = state->dirpath, - .twrp = state->smb_fname->twrp, - }; - - /* handle null paths */ - if ((dname.base_name == NULL) || - (dname.base_name[0] == '\0')) { - return NT_STATUS_ACCESS_DENIED; - } - ret = SMB_VFS_LSTAT(state->conn, &dname); - if (ret != 0) { - return NT_STATUS_ACCESS_DENIED; - } - if (!S_ISLNK(dname.st.st_ex_mode)) { - return NT_STATUS_ACCESS_DENIED; - } - status = check_name(state->conn, &dname); - if (!NT_STATUS_IS_OK(status)) { - /* We know this is an intermediate path. */ - return NT_STATUS_OBJECT_PATH_NOT_FOUND; - } - return NT_STATUS_ACCESS_DENIED; - } else { - /* - * This is the dropbox - * behaviour. A dropbox is a - * directory with only -wx - * permissions, so - * get_real_filename fails - * with EACCESS, it needs to - * list the directory. We - * nevertheless want to allow - * users creating a file. - */ - status = NT_STATUS_OK; - } - } - - if (!NT_STATUS_IS_OK(status) && - !NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) { - /* - * ENOTDIR and ELOOP both map to - * NT_STATUS_OBJECT_PATH_NOT_FOUND - * in the filename walk. - */ - if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_A_DIRECTORY) || - NT_STATUS_EQUAL(status, NT_STATUS_STOPPED_ON_SYMLINK)) { - status = NT_STATUS_OBJECT_PATH_NOT_FOUND; - } - return status; - } - - /* - * POSIX pathnames must never call into mangling. - */ - if (state->posix_pathnames) { - goto done; - } - - /* - * Just the last part of the name doesn't exist. - * We need to strupper() or strlower() it as - * this conversion may be used for file creation - * purposes. Fix inspired by - * Thomas Neumann . - */ - if (!state->case_preserve || - (mangle_is_8_3(state->name, false, - state->conn->params) && - !state->short_case_preserve)) { - if (!strnorm(state->name, - lp_default_case(SNUM(state->conn)))) { - DBG_DEBUG("strnorm %s failed\n", - state->name); - return NT_STATUS_INVALID_PARAMETER; - } - } - - /* - * check on the mangled stack to see if we can - * recover the base of the filename. - */ - - if (mangle_is_mangled(state->name, state->conn->params) - && mangle_lookup_name_from_8_3(state->mem_ctx, - state->name, - &unmangled, - state->conn->params)) { - char *tmp; - size_t name_ofs = - state->name - state->smb_fname->base_name; - - if (!ISDOT(state->dirpath)) { - tmp = talloc_asprintf( - state->smb_fname, "%s/%s", - state->dirpath, unmangled); - TALLOC_FREE(unmangled); - } - else { - tmp = unmangled; - } - if (tmp == NULL) { - DBG_ERR("talloc failed\n"); - return NT_STATUS_NO_MEMORY; - } - TALLOC_FREE(state->smb_fname->base_name); - state->smb_fname->base_name = tmp; - state->name = - state->smb_fname->base_name + name_ofs; - state->end = state->name + strlen(state->name); - } - - done: - - DBG_DEBUG("New file [%s]\n", state->name); - state->done = true; - return NT_STATUS_OK; -} - -static NTSTATUS unix_convert_step_stat(struct uc_state *state) -{ - struct smb_filename dname; - char dot[2] = "."; - char *found_name = NULL; - int ret; - NTSTATUS status; - - /* - * Check if the name exists up to this point. - */ - - DBG_DEBUG("smb_fname [%s]\n", smb_fname_str_dbg(state->smb_fname)); - - ret = vfs_stat(state->conn, state->smb_fname); - if (ret == 0) { - /* - * It exists. it must either be a directory or this must - * be the last part of the path for it to be OK. - */ - if (state->end && !S_ISDIR(state->smb_fname->st.st_ex_mode)) { - /* - * An intermediate part of the name isn't - * a directory. - */ - DBG_DEBUG("Not a dir [%s]\n", state->name); - *state->end = '/'; - /* - * We need to return the fact that the - * intermediate name resolution failed. This - * is used to return an error of ERRbadpath - * rather than ERRbadfile. Some Windows - * applications depend on the difference between - * these two errors. - */ - return NT_STATUS_OBJECT_PATH_NOT_FOUND; - } - return NT_STATUS_OK; - } - - /* Stat failed - ensure we don't use it. */ - SET_STAT_INVALID(state->smb_fname->st); - - if (state->posix_pathnames) { - /* - * For posix_pathnames, we're done. - * Don't blunder into the - * get_real_filename() codepath as they may - * be doing case insensitive lookups. So when - * creating a new POSIX directory Foo they might - * match on name foo. - * - * BUG: https://bugzilla.samba.org/show_bug.cgi?id=13803 - */ - if (state->end != NULL) { - const char *morepath = NULL; - /* - * If this is intermediate we must - * restore the full path. - */ - *state->end = '/'; - /* - * If there are any more components - * after the failed LSTAT we cannot - * continue. - */ - morepath = strchr(state->end + 1, '/'); - if (morepath != NULL) { - return NT_STATUS_OBJECT_PATH_NOT_FOUND; - } - } - if (errno == ENOENT) { - /* New file or directory. */ - state->done = true; - return NT_STATUS_OK; - } - if ((errno == EACCES) && - (state->ucf_flags & UCF_PREP_CREATEFILE)) { - /* POSIX Dropbox case. */ - errno = 0; - state->done = true; - return NT_STATUS_OK; - } - return map_nt_error_from_unix(errno); - } - - /* - * Reset errno so we can detect - * directory open errors. - */ - errno = 0; - - /* - * Try to find this part of the path in the directory. - */ - - dname = (struct smb_filename) { - .base_name = state->dirpath, - .twrp = state->smb_fname->twrp, - }; - - /* handle null paths */ - if ((dname.base_name == NULL) || (dname.base_name[0] == '\0')) { - dname.base_name = dot; - } - - status = get_real_filename(state->conn, - &dname, - state->name, - talloc_tos(), - &found_name); - if (!NT_STATUS_IS_OK(status)) { - return unix_convert_step_search_fail(state, status); - } - - /* - * Restore the rest of the string. If the string was - * mangled the size may have changed. - */ - if (state->end) { - char *tmp; - size_t name_ofs = - state->name - state->smb_fname->base_name; - - if (!ISDOT(state->dirpath)) { - tmp = talloc_asprintf(state->smb_fname, - "%s/%s/%s", state->dirpath, - found_name, state->end+1); - } - else { - tmp = talloc_asprintf(state->smb_fname, - "%s/%s", found_name, - state->end+1); - } - if (tmp == NULL) { - DBG_ERR("talloc_asprintf failed\n"); - return NT_STATUS_NO_MEMORY; - } - TALLOC_FREE(state->smb_fname->base_name); - state->smb_fname->base_name = tmp; - state->name = state->smb_fname->base_name + name_ofs; - state->end = state->name + strlen(found_name); - *state->end = '\0'; - } else { - char *tmp; - size_t name_ofs = - state->name - state->smb_fname->base_name; - - if (!ISDOT(state->dirpath)) { - tmp = talloc_asprintf(state->smb_fname, - "%s/%s", state->dirpath, - found_name); - } else { - tmp = talloc_strdup(state->smb_fname, - found_name); - } - if (tmp == NULL) { - DBG_ERR("talloc failed\n"); - return NT_STATUS_NO_MEMORY; - } - TALLOC_FREE(state->smb_fname->base_name); - state->smb_fname->base_name = tmp; - state->name = state->smb_fname->base_name + name_ofs; - - /* - * We just scanned for, and found the end of - * the path. We must return a valid stat struct - * if it exists. JRA. - */ - - ret = vfs_stat(state->conn, state->smb_fname); - if (ret != 0) { - SET_STAT_INVALID(state->smb_fname->st); - } - } - - TALLOC_FREE(found_name); - return NT_STATUS_OK; -} - -static NTSTATUS unix_convert_step(struct uc_state *state) -{ - NTSTATUS status; - - /* - * Pinpoint the end of this section of the filename. - */ - /* mb safe. '/' can't be in any encoded char. */ - state->end = strchr(state->name, '/'); - - /* - * Chop the name at this point. - */ - if (state->end != NULL) { - *state->end = 0; - } - - DBG_DEBUG("dirpath [%s] name [%s]\n", state->dirpath, state->name); - - /* The name cannot have a component of "." */ - - if (ISDOT(state->name)) { - if (state->end == NULL) { - /* Error code at the end of a pathname. */ - return NT_STATUS_OBJECT_NAME_INVALID; - } - return NT_STATUS_OBJECT_PATH_NOT_FOUND; - } - - status = unix_convert_step_stat(state); - if (!NT_STATUS_IS_OK(status)) { - return status; - } - if (state->done) { - return NT_STATUS_OK; - } - - /* - * Add to the dirpath that we have resolved so far. - */ - - if (!ISDOT(state->dirpath)) { - char *tmp = talloc_asprintf(state->mem_ctx, - "%s/%s", state->dirpath, state->name); - if (!tmp) { - DBG_ERR("talloc_asprintf failed\n"); - return NT_STATUS_NO_MEMORY; - } - TALLOC_FREE(state->dirpath); - state->dirpath = tmp; - } - else { - TALLOC_FREE(state->dirpath); - if (!(state->dirpath = talloc_strdup(state->mem_ctx,state->name))) { - DBG_ERR("talloc_strdup failed\n"); - return NT_STATUS_NO_MEMORY; - } - } - - /* - * Cache the dirpath thus far. Don't cache a name with mangled - * components as this can change the size. - */ - if(!state->component_was_mangled) { - stat_cache_add(state->orig_path, - state->dirpath, - state->smb_fname->twrp, - state->case_sensitive); - } - - /* - * Restore the / that we wiped out earlier. - */ - if (state->end != NULL) { - *state->end = '/'; - } - - return NT_STATUS_OK; -} - -NTSTATUS unix_convert(TALLOC_CTX *mem_ctx, - connection_struct *conn, - const char *orig_path, - NTTIME twrp, - struct smb_filename **smb_fname_out, - uint32_t ucf_flags) -{ - struct uc_state uc_state; - struct uc_state *state = &uc_state; - NTSTATUS status; - int ret = -1; - int parent_stat_errno = 0; - - *state = (struct uc_state) { - .mem_ctx = mem_ctx, - .conn = conn, - .orig_path = orig_path, - .ucf_flags = ucf_flags, - .posix_pathnames = (ucf_flags & UCF_POSIX_PATHNAMES), - .case_sensitive = conn->case_sensitive, - .case_preserve = conn->case_preserve, - .short_case_preserve = conn->short_case_preserve, - }; - - *smb_fname_out = NULL; - - if (state->posix_pathnames) { - /* POSIX means ignore case settings on share. */ - state->case_sensitive = true; - state->case_preserve = true; - state->short_case_preserve = true; - } - - state->smb_fname = talloc_zero(state->mem_ctx, struct smb_filename); - if (state->smb_fname == NULL) { - return NT_STATUS_NO_MEMORY; - } - - if (state->conn->printer) { - /* we don't ever use the filenames on a printer share as a - filename - so don't convert them */ - state->smb_fname->base_name = talloc_strdup( - state->smb_fname, state->orig_path); - if (state->smb_fname->base_name == NULL) { - status = NT_STATUS_NO_MEMORY; - goto err; - } - goto done; - } - - state->smb_fname->flags = state->posix_pathnames ? SMB_FILENAME_POSIX_PATH : 0; - - DBG_DEBUG("Called on file [%s]\n", state->orig_path); - - if (state->orig_path[0] == '/') { - DBG_ERR("Path [%s] starts with '/'\n", state->orig_path); - return NT_STATUS_OBJECT_NAME_INVALID; - } - - /* Start with the full orig_path as given by the caller. */ - state->smb_fname->base_name = talloc_strdup( - state->smb_fname, state->orig_path); - if (state->smb_fname->base_name == NULL) { - DBG_ERR("talloc_strdup failed\n"); - status = NT_STATUS_NO_MEMORY; - goto err; - } - - /* Canonicalize any @GMT- paths. */ - status = canonicalize_snapshot_path(state->smb_fname, ucf_flags, twrp); - if (!NT_STATUS_IS_OK(status)) { - goto err; - } - - /* - * If we trimmed down to a single '\0' character - * then we should use the "." directory to avoid - * searching the cache, but not if we are in a - * printing share. - * As we know this is valid we can return true here. - */ - - if (state->smb_fname->base_name[0] == '\0') { - state->smb_fname->base_name = talloc_strdup(state->smb_fname, "."); - if (state->smb_fname->base_name == NULL) { - status = NT_STATUS_NO_MEMORY; - goto err; - } - if (SMB_VFS_STAT(state->conn, state->smb_fname) != 0) { - status = map_nt_error_from_unix(errno); - goto err; - } - DBG_DEBUG("conversion finished [] -> [%s]\n", - state->smb_fname->base_name); - goto done; - } - - if (state->orig_path[0] == '.' && (state->orig_path[1] == '/' || - state->orig_path[1] == '\0')) { - /* Start of pathname can't be "." only. */ - if (state->orig_path[1] == '\0' || state->orig_path[2] == '\0') { - status = NT_STATUS_OBJECT_NAME_INVALID; - } else { - status = NT_STATUS_OBJECT_PATH_NOT_FOUND; - } - goto err; - } - - /* - * Large directory fix normalization. If we're case sensitive, and - * the case preserving parameters are set to "no", normalize the case of - * the incoming filename from the client WHETHER IT EXISTS OR NOT ! - * This is in conflict with the current (3.0.20) man page, but is - * what people expect from the "large directory howto". I'll update - * the man page. Thanks to jht@samba.org for finding this. JRA. - */ - - status = normalize_filename_case(state->conn, - state->smb_fname->base_name, - ucf_flags); - if (!NT_STATUS_IS_OK(status)) { - DBG_ERR("normalize_filename_case %s failed\n", - state->smb_fname->base_name); - goto err; - } - - /* - * Strip off the stream, and add it back when we're done with the - * base_name. - */ - if (!state->posix_pathnames) { - state->stream = strchr_m(state->smb_fname->base_name, ':'); - - if (state->stream != NULL) { - char *tmp = talloc_strdup(state->smb_fname, state->stream); - if (tmp == NULL) { - status = NT_STATUS_NO_MEMORY; - goto err; - } - /* - * Since this is actually pointing into - * smb_fname->base_name this truncates base_name. - */ - *state->stream = '\0'; - state->stream = tmp; - - if (state->smb_fname->base_name[0] == '\0') { - /* - * orig_name was just a stream name. - * This is a stream on the root of - * the share. Replace base_name with - * a "." - */ - state->smb_fname->base_name = - talloc_strdup(state->smb_fname, "."); - if (state->smb_fname->base_name == NULL) { - status = NT_STATUS_NO_MEMORY; - goto err; - } - if (SMB_VFS_STAT(state->conn, state->smb_fname) != 0) { - status = map_nt_error_from_unix(errno); - goto err; - } - /* dirpath must exist. */ - state->dirpath = talloc_strdup(state->mem_ctx,"."); - if (state->dirpath == NULL) { - status = NT_STATUS_NO_MEMORY; - goto err; - } - DBG_INFO("conversion finished [%s] -> [%s]\n", - state->orig_path, - state->smb_fname->base_name); - goto done; - } - } - } - - state->name = state->smb_fname->base_name; - - /* - * If we're providing case insensitive semantics or - * the underlying filesystem is case insensitive, - * then a case-normalized hit in the stat-cache is - * authoritative. JRA. - * - * Note: We're only checking base_name. The stream_name will be - * added and verified in build_stream_path(). - */ - - if (!state->case_sensitive || - !(state->conn->fs_capabilities & FILE_CASE_SENSITIVE_SEARCH)) - { - bool found; - - found = stat_cache_lookup(state->conn, - &state->smb_fname->base_name, - &state->dirpath, - &state->name, - state->smb_fname->twrp, - &state->smb_fname->st); - /* - * stat_cache_lookup() allocates on talloc_tos() even - * when !found, reparent correctly - */ - talloc_steal(state->smb_fname, state->smb_fname->base_name); - talloc_steal(state->mem_ctx, state->dirpath); - - if (found) { - goto done; - } - } - - /* - * Make sure "dirpath" is an allocated string, we use this for - * building the directories with talloc_asprintf and free it. - */ - - if (state->dirpath == NULL) { - state->dirpath = talloc_strdup(state->mem_ctx,"."); - if (state->dirpath == NULL) { - DBG_ERR("talloc_strdup failed\n"); - status = NT_STATUS_NO_MEMORY; - goto err; - } - } - - /* - * If we have a wildcard we must walk the path to - * find where the error is, even if case sensitive - * is true. - */ - - if (!state->posix_pathnames) { - /* POSIX pathnames have no wildcards. */ - bool name_has_wildcard = ms_has_wild(state->smb_fname->base_name); - if (name_has_wildcard) { - /* Wildcard not valid anywhere. */ - status = NT_STATUS_OBJECT_NAME_INVALID; - goto fail; - } - } - - DBG_DEBUG("Begin: name [%s] dirpath [%s] name [%s]\n", - state->smb_fname->base_name, state->dirpath, state->name); - - /* - * stat the name - if it exists then we can add the stream back (if - * there was one) and be done! - */ - - ret = vfs_stat(state->conn, state->smb_fname); - if (ret == 0) { - status = check_for_dot_component(state->smb_fname); - if (!NT_STATUS_IS_OK(status)) { - goto fail; - } - /* Add the path (not including the stream) to the cache. */ - stat_cache_add(state->orig_path, - state->smb_fname->base_name, - state->smb_fname->twrp, - state->case_sensitive); - DBG_DEBUG("Conversion of base_name finished " - "[%s] -> [%s]\n", - state->orig_path, state->smb_fname->base_name); - goto done; - } - - /* Stat failed - ensure we don't use it. */ - SET_STAT_INVALID(state->smb_fname->st); - - /* - * Note: we must continue processing a path if we get EACCES - * from stat. With NFS4 permissions the file might be lacking - * READ_ATTR, but if the parent has LIST permissions we can - * resolve the path in the path traversal loop down below. - */ - - if (errno == ENOENT) { - /* Optimization when creating a new file - only - the last component doesn't exist. - NOTE : check_parent_exists() doesn't preserve errno. - */ - int saved_errno = errno; - status = check_parent_exists(state->mem_ctx, - state->conn, - state->posix_pathnames, - state->smb_fname, - &state->dirpath, - &state->name, - &parent_stat_errno); - errno = saved_errno; - if (!NT_STATUS_IS_OK(status)) { - goto fail; - } - } - - /* - * A special case - if we don't have any wildcards or mangling chars and are case - * sensitive or the underlying filesystem is case insensitive then searching - * won't help. - * - * NB. As POSIX sets state->case_sensitive as - * true we will never call into mangle_is_mangled() here. - */ - - if ((state->case_sensitive || !(state->conn->fs_capabilities & - FILE_CASE_SENSITIVE_SEARCH)) && - !mangle_is_mangled(state->smb_fname->base_name, state->conn->params)) { - - status = check_for_dot_component(state->smb_fname); - if (!NT_STATUS_IS_OK(status)) { - goto fail; - } + struct tm tm; + time_t t; - /* - * The stat failed. Could be ok as it could be - * a new file. - */ + start = strstr_m(filename, "@GMT-"); - if (errno == ENOTDIR || errno == ELOOP) { - status = NT_STATUS_OBJECT_PATH_NOT_FOUND; - goto fail; - } else if (errno == ENOENT) { - /* - * Was it a missing last component ? - * or a missing intermediate component ? - * - * Optimization. - * - * For this code path we can guarantee that - * we have gone through check_parent_exists() - * and it returned NT_STATUS_OK. - * - * Either there was no parent component (".") - * parent_stat_errno == 0 and we have a missing - * last component here. - * - * OR check_parent_exists() called STAT/LSTAT - * and if it failed parent_stat_errno has been - * set telling us if the parent existed or not. - * - * Either way we can avoid another STAT/LSTAT - * system call on the parent here. - */ - if (parent_stat_errno == ENOTDIR || - parent_stat_errno == ENOENT || - parent_stat_errno == ELOOP) { - status = NT_STATUS_OBJECT_PATH_NOT_FOUND; - goto fail; - } + if (start == NULL) { + return false; + } - /* - * Missing last component is ok - new file. - * Also deal with permission denied elsewhere. - * Just drop out to done. - */ - goto done; - } + if ((start > filename) && (start[-1] != '/')) { + /* the GMT-token does not start a path-component */ + return false; } - /* - * is_mangled() was changed to look at an entire pathname, not - * just a component. JRA. - */ + end = strptime(start, GMT_FORMAT, &tm); + if (end == NULL) { + /* Not a valid timestring. */ + return false; + } - if (state->posix_pathnames) { + if ((end[0] != '\0') && (end[0] != '/')) { /* - * POSIX names are never mangled and we must not - * call into mangling functions. + * It is not a complete path component, i.e. the path + * component continues after the gmt-token. */ - state->component_was_mangled = false; - } else if (mangle_is_mangled(state->name, state->conn->params)) { - state->component_was_mangled = true; + return false; } - /* - * Now we need to recursively match the name against the real - * directory structure. - */ + tm.tm_isdst = -1; + t = timegm(&tm); + unix_to_nt_time(twrp, t); - /* - * Match each part of the path name separately, trying the names - * as is first, then trying to scan the directory for matching names. - */ + DBG_DEBUG("Extracted @GMT-Timestamp %s\n", + nt_time_string(talloc_tos(), *twrp)); - for (; state->name ; state->name = (state->end ? state->end + 1:(char *)NULL)) { - status = unix_convert_step(state); - if (!NT_STATUS_IS_OK(status)) { - if (NT_STATUS_EQUAL(status, NT_STATUS_NO_MEMORY)) { - goto err; - } - goto fail; - } - if (state->done) { - goto done; - } + *_start = start; + + if (end[0] == '/') { + end += 1; } + *_next_component = end; - /* - * Cache the full path. Don't cache a name with mangled or wildcard - * components as this can change the size. - */ + return true; +} + +bool extract_snapshot_token(char *fname, NTTIME *twrp) +{ + const char *start = NULL; + const char *next = NULL; + size_t remaining; + bool found; - if(!state->component_was_mangled) { - stat_cache_add(state->orig_path, - state->smb_fname->base_name, - state->smb_fname->twrp, - state->case_sensitive); + found = find_snapshot_token(fname, &start, &next, twrp); + if (!found) { + return false; } - /* - * The name has been resolved. - */ + remaining = strlen(next); + memmove(discard_const_p(char, start), next, remaining+1); - done: - /* Add back the stream if one was stripped off originally. */ - if (state->stream != NULL) { - state->smb_fname->stream_name = state->stream; + return true; +} - /* Check path now that the base_name has been converted. */ - status = build_stream_path(state->mem_ctx, state->conn, state->smb_fname); - if (!NT_STATUS_IS_OK(status)) { - goto fail; - } +/* + * Strip a valid @GMT-token from any incoming filename path, + * adding any NTTIME encoded in the pathname into the + * twrp field of the passed in smb_fname. + * + * Valid @GMT-tokens look like @GMT-YYYY-MM-DD-HH-MM-SS + * at the *start* of a pathname component. + * + * If twrp is passed in then smb_fname->twrp is set to that + * value, and the @GMT-token part of the filename is removed + * and does not change the stored smb_fname->twrp. + * + */ + +NTSTATUS canonicalize_snapshot_path(struct smb_filename *smb_fname, + uint32_t ucf_flags, + NTTIME twrp) +{ + bool found; + + if (twrp != 0) { + smb_fname->twrp = twrp; } - DBG_DEBUG("Conversion finished [%s] -> [%s]\n", - state->orig_path, smb_fname_str_dbg(state->smb_fname)); + if (!(ucf_flags & UCF_GMT_PATHNAME)) { + return NT_STATUS_OK; + } - TALLOC_FREE(state->dirpath); - *smb_fname_out = state->smb_fname; - return NT_STATUS_OK; - fail: - DBG_DEBUG("Conversion failed: dirpath [%s] name [%s]\n", - state->dirpath, state->name); - if ((state->dirpath != NULL) && !ISDOT(state->dirpath)) { - state->smb_fname->base_name = talloc_asprintf( - state->smb_fname, - "%s/%s", - state->dirpath, - state->name); - } else { - state->smb_fname->base_name = talloc_strdup( - state->smb_fname, state->name); + found = extract_snapshot_token(smb_fname->base_name, &twrp); + if (!found) { + return NT_STATUS_OK; } - if (state->smb_fname->base_name == NULL) { - DBG_ERR("talloc_asprintf failed\n"); - status = NT_STATUS_NO_MEMORY; - goto err; + + if (smb_fname->twrp == 0) { + smb_fname->twrp = twrp; } - *smb_fname_out = state->smb_fname; - TALLOC_FREE(state->dirpath); - return status; - err: - TALLOC_FREE(state->smb_fname); - return status; + return NT_STATUS_OK; } -/**************************************************************************** - Ensure a path is not vetoed. -****************************************************************************/ - -static NTSTATUS check_veto_path(connection_struct *conn, - const struct smb_filename *smb_fname) +static bool strnorm(char *s, int case_default) { - const char *name = smb_fname->base_name; - - if (IS_VETO_PATH(conn, name)) { - /* Is it not dot or dot dot. */ - if (!(ISDOT(name) || ISDOTDOT(name))) { - DEBUG(5,("check_veto_path: file path name %s vetoed\n", - name)); - return map_nt_error_from_unix(ENOENT); - } - } - return NT_STATUS_OK; + if (case_default == CASE_UPPER) + return strupper_m(s); + else + return strlower_m(s); } -/**************************************************************************** - Check a filename - possibly calling check_reduced_name. - This is called by every routine before it allows an operation on a filename. - It does any final confirmation necessary to ensure that the filename is - a valid one for the user to access. -****************************************************************************/ +/* + * Utility function to normalize case on an incoming client filename + * if required on this connection struct. + * Performs an in-place case conversion guaranteed to stay the same size. + */ -static NTSTATUS check_name(connection_struct *conn, - const struct smb_filename *smb_fname) +static NTSTATUS normalize_filename_case(connection_struct *conn, + char *filename, + uint32_t ucf_flags) { - NTSTATUS status = check_veto_path(conn, smb_fname); + bool ok; - if (!NT_STATUS_IS_OK(status)) { - return status; + if (ucf_flags & UCF_POSIX_PATHNAMES) { + /* + * POSIX never normalizes filename case. + */ + return NT_STATUS_OK; } - - if (!lp_widelinks(SNUM(conn)) || !lp_follow_symlinks(SNUM(conn))) { - status = check_reduced_name(conn, NULL, smb_fname); - if (!NT_STATUS_IS_OK(status)) { - DEBUG(5,("check_name: name %s failed with %s\n", - smb_fname->base_name, - nt_errstr(status))); - return status; - } + if (!conn->case_sensitive) { + return NT_STATUS_OK; + } + if (conn->case_preserve) { + return NT_STATUS_OK; + } + if (conn->short_case_preserve) { + return NT_STATUS_OK; + } + ok = strnorm(filename, lp_default_case(SNUM(conn))); + if (!ok) { + return NT_STATUS_INVALID_PARAMETER; } - return NT_STATUS_OK; } @@ -1580,41 +430,6 @@ NTSTATUS get_real_filename_full_scan_at(struct files_struct *dirfsp, return NT_STATUS_OBJECT_NAME_NOT_FOUND; } -NTSTATUS get_real_filename_full_scan(connection_struct *conn, - const char *path, - const char *name, - bool mangled, - TALLOC_CTX *mem_ctx, - char **found_name) -{ - struct smb_filename *smb_dname = NULL; - NTSTATUS status; - - /* handle null paths */ - if ((path == NULL) || (*path == 0)) { - path = "."; - } - - status = synthetic_pathref( - talloc_tos(), - conn->cwd_fsp, - path, - NULL, - NULL, - 0, - 0, - &smb_dname); - if (!NT_STATUS_IS_OK(status)) { - return status; - } - - status = get_real_filename_full_scan_at( - smb_dname->fsp, name, mangled, mem_ctx, found_name); - - TALLOC_FREE(smb_dname); - return status; -} - /**************************************************************************** Wrapper around the vfs get_real_filename and the full directory scan fallback. @@ -1703,172 +518,6 @@ static bool get_real_filename_cache_key( return true; } -static NTSTATUS get_real_filename(connection_struct *conn, - struct smb_filename *path, - const char *name, - TALLOC_CTX *mem_ctx, - char **found_name) -{ - struct smb_filename *smb_dname = NULL; - NTSTATUS status; - - smb_dname = cp_smb_filename_nostream(talloc_tos(), path); - if (smb_dname == NULL) { - return NT_STATUS_NO_MEMORY; - } - -again: - status = openat_pathref_fsp(conn->cwd_fsp, smb_dname); - - if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND) && - S_ISLNK(smb_dname->st.st_ex_mode)) { - status = NT_STATUS_STOPPED_ON_SYMLINK; - } - - if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND) && - (smb_dname->twrp != 0)) { - /* - * Retry looking at the non-snapshot path, copying the - * fallback mechanism from vfs_shadow_copy2.c when - * shadow_copy2_convert() fails. This path-based - * routine get_real_filename() should go away and be - * replaced with a fd-based one, so spoiling it with a - * shadow_copy2 specific mechanism should not be too - * bad. - */ - smb_dname->twrp = 0; - goto again; - } - - if (!NT_STATUS_IS_OK(status)) { - DBG_DEBUG("openat_pathref_fsp(%s) failed: %s\n", - smb_fname_str_dbg(smb_dname), - nt_errstr(status)); - - /* - * ENOTDIR and ELOOP both map to - * NT_STATUS_OBJECT_PATH_NOT_FOUND in the filename - * walk. - */ - if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_A_DIRECTORY) || - NT_STATUS_EQUAL(status, NT_STATUS_STOPPED_ON_SYMLINK)) { - status = NT_STATUS_OBJECT_PATH_NOT_FOUND; - } - - return status; - } - - status = get_real_filename_at( - smb_dname->fsp, name, mem_ctx, found_name); - TALLOC_FREE(smb_dname); - return status; -} - -static NTSTATUS build_stream_path(TALLOC_CTX *mem_ctx, - connection_struct *conn, - struct smb_filename *smb_fname) -{ - NTSTATUS status; - unsigned int i, num_streams = 0; - struct stream_struct *streams = NULL; - struct smb_filename *pathref = NULL; - - if (SMB_VFS_STAT(conn, smb_fname) == 0) { - DEBUG(10, ("'%s' exists\n", smb_fname_str_dbg(smb_fname))); - return NT_STATUS_OK; - } - - if (errno != ENOENT) { - DEBUG(10, ("vfs_stat failed: %s\n", strerror(errno))); - status = map_nt_error_from_unix(errno); - goto fail; - } - - if (smb_fname->fsp == NULL) { - status = synthetic_pathref(mem_ctx, - conn->cwd_fsp, - smb_fname->base_name, - NULL, - NULL, - smb_fname->twrp, - smb_fname->flags, - &pathref); - if (!NT_STATUS_IS_OK(status)) { - if (NT_STATUS_EQUAL(status, - NT_STATUS_OBJECT_NAME_NOT_FOUND)) { - TALLOC_FREE(pathref); - SET_STAT_INVALID(smb_fname->st); - return NT_STATUS_OK; - } - DBG_DEBUG("synthetic_pathref failed: %s\n", - nt_errstr(status)); - goto fail; - } - } else { - pathref = smb_fname; - } - - /* Fall back to a case-insensitive scan of all streams on the file. */ - status = vfs_fstreaminfo(pathref->fsp, mem_ctx, - &num_streams, &streams); - if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) { - SET_STAT_INVALID(smb_fname->st); - TALLOC_FREE(pathref); - return NT_STATUS_OK; - } - - if (!NT_STATUS_IS_OK(status)) { - DEBUG(10, ("vfs_fstreaminfo failed: %s\n", nt_errstr(status))); - goto fail; - } - - for (i=0; istream_name, - streams[i].name, - conn->case_sensitive); - - DBG_DEBUG("comparing [%s] and [%s]: %sequal\n", - smb_fname->stream_name, - streams[i].name, - equal ? "" : "not "); - - if (equal) { - break; - } - } - - /* Couldn't find the stream. */ - if (i == num_streams) { - SET_STAT_INVALID(smb_fname->st); - TALLOC_FREE(pathref); - TALLOC_FREE(streams); - return NT_STATUS_OK; - } - - DEBUG(10, ("case insensitive stream. requested: %s, actual: %s\n", - smb_fname->stream_name, streams[i].name)); - - - TALLOC_FREE(smb_fname->stream_name); - smb_fname->stream_name = talloc_strdup(smb_fname, streams[i].name); - if (smb_fname->stream_name == NULL) { - status = NT_STATUS_NO_MEMORY; - goto fail; - } - - SET_STAT_INVALID(smb_fname->st); - - if (SMB_VFS_STAT(conn, smb_fname) == 0) { - DEBUG(10, ("'%s' exists\n", smb_fname_str_dbg(smb_fname))); - } - status = NT_STATUS_OK; - fail: - TALLOC_FREE(pathref); - TALLOC_FREE(streams); - return status; -} - /* * Lightweight function to just get last component * for rename / enumerate directory calls. diff --git a/source3/smbd/proto.h b/source3/smbd/proto.h index bc305bce296..5ac0f713958 100644 --- a/source3/smbd/proto.h +++ b/source3/smbd/proto.h @@ -350,22 +350,10 @@ NTSTATUS sync_file(connection_struct *conn, files_struct *fsp, bool write_throug uint32_t ucf_flags_from_smb_request(struct smb_request *req); uint32_t filename_create_ucf_flags(struct smb_request *req, uint32_t create_disposition); -NTSTATUS unix_convert(TALLOC_CTX *ctx, - connection_struct *conn, - const char *orig_path, - NTTIME twrp, - struct smb_filename **smb_fname, - uint32_t ucf_flags); bool extract_snapshot_token(char *fname, NTTIME *twrp); NTSTATUS canonicalize_snapshot_path(struct smb_filename *smb_fname, uint32_t ucf_flags, NTTIME twrp); -NTSTATUS get_real_filename_full_scan(connection_struct *conn, - const char *path, - const char *name, - bool mangled, - TALLOC_CTX *mem_ctx, - char **found_name); NTSTATUS get_real_filename_full_scan_at(struct files_struct *dirfsp, const char *name, bool mangled, -- 2.34.1