From 3c73a609348145db9caf955437880d284615daca Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Mon, 20 Mar 2017 11:21:02 -0700 Subject: [PATCH 01/37] CVE-2017-2619: s3/smbd: re-open directory after dptr_CloseDir() dptr_CloseDir() will close and invalidate the fsp's file descriptor, we have to reopen it. Bug: https://bugzilla.samba.org/show_bug.cgi?id=12496 Signed-off-by: Ralph Bohme --- source3/smbd/smb2_find.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/source3/smbd/smb2_find.c b/source3/smbd/smb2_find.c index 2dab86b5676..4728924e270 100644 --- a/source3/smbd/smb2_find.c +++ b/source3/smbd/smb2_find.c @@ -24,6 +24,7 @@ #include "../libcli/smb/smb_common.h" #include "trans2.h" #include "../lib/util/tevent_ntstatus.h" +#include "system/filesys.h" static struct tevent_req *smbd_smb2_find_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev, @@ -322,7 +323,23 @@ static struct tevent_req *smbd_smb2_find_send(TALLOC_CTX *mem_ctx, } if (in_flags & SMB2_CONTINUE_FLAG_REOPEN) { + int flags; + dptr_CloseDir(fsp); + + /* + * dptr_CloseDir() will close and invalidate the fsp's file + * descriptor, we have to reopen it. + */ + + flags = O_RDONLY; +#ifdef O_DIRECTORY + flags |= O_DIRECTORY; +#endif + status = fd_open(conn, fsp, flags, 0); + if (tevent_req_nterror(req, status)) { + return tevent_req_post(req, ev); + } } wcard_has_wild = ms_has_wild(in_file_name); -- 2.12.0.367.g23dc2f6d3c-goog From 582f4a9fa616bb1a05397ec1777327ba823248a2 Mon Sep 17 00:00:00 2001 From: Ralph Boehme Date: Sun, 19 Mar 2017 18:52:10 +0100 Subject: [PATCH 02/37] CVE-2017-2619: s4/torture: add SMB2_FIND tests with SMB2_CONTINUE_FLAG_REOPEN flag Bug: https://bugzilla.samba.org/show_bug.cgi?id=12496 Signed-off-by: Ralph Boehme Reviewed-by: Uri Simchoni Signed-off-by: Arvid Requate --- source4/torture/smb2/dir.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/source4/torture/smb2/dir.c b/source4/torture/smb2/dir.c index cf96fee1577..f36808198b4 100644 --- a/source4/torture/smb2/dir.c +++ b/source4/torture/smb2/dir.c @@ -671,7 +671,7 @@ bool fill_result(void *private_data, return true; } -enum continue_type {CONT_SINGLE, CONT_INDEX, CONT_RESTART}; +enum continue_type {CONT_SINGLE, CONT_INDEX, CONT_RESTART, CONT_REOPEN}; static NTSTATUS multiple_smb2_search(struct smb2_tree *tree, TALLOC_CTX *tctx, @@ -697,6 +697,9 @@ static NTSTATUS multiple_smb2_search(struct smb2_tree *tree, /* The search should start from the beginning everytime */ f.in.continue_flags = SMB2_CONTINUE_FLAG_RESTART; + if (cont_type == CONT_REOPEN) { + f.in.continue_flags = SMB2_CONTINUE_FLAG_REOPEN; + } do { status = smb2_find_level(tree, tree, &f, &count, &d); @@ -769,18 +772,23 @@ static bool test_many_files(struct torture_context *tctx, {"SMB2_FIND_BOTH_DIRECTORY_INFO", "SINGLE", SMB2_FIND_BOTH_DIRECTORY_INFO, RAW_SEARCH_DATA_BOTH_DIRECTORY_INFO, CONT_SINGLE}, {"SMB2_FIND_BOTH_DIRECTORY_INFO", "INDEX", SMB2_FIND_BOTH_DIRECTORY_INFO, RAW_SEARCH_DATA_BOTH_DIRECTORY_INFO, CONT_INDEX}, {"SMB2_FIND_BOTH_DIRECTORY_INFO", "RESTART", SMB2_FIND_BOTH_DIRECTORY_INFO, RAW_SEARCH_DATA_BOTH_DIRECTORY_INFO, CONT_RESTART}, + {"SMB2_FIND_BOTH_DIRECTORY_INFO", "REOPEN", SMB2_FIND_BOTH_DIRECTORY_INFO, RAW_SEARCH_DATA_BOTH_DIRECTORY_INFO, CONT_REOPEN}, {"SMB2_FIND_DIRECTORY_INFO", "SINGLE", SMB2_FIND_DIRECTORY_INFO, RAW_SEARCH_DATA_DIRECTORY_INFO, CONT_SINGLE}, {"SMB2_FIND_DIRECTORY_INFO", "INDEX", SMB2_FIND_DIRECTORY_INFO, RAW_SEARCH_DATA_DIRECTORY_INFO, CONT_INDEX}, {"SMB2_FIND_DIRECTORY_INFO", "RESTART", SMB2_FIND_DIRECTORY_INFO, RAW_SEARCH_DATA_DIRECTORY_INFO, CONT_RESTART}, + {"SMB2_FIND_DIRECTORY_INFO", "REOPEN", SMB2_FIND_DIRECTORY_INFO, RAW_SEARCH_DATA_DIRECTORY_INFO, CONT_REOPEN}, {"SMB2_FIND_FULL_DIRECTORY_INFO", "SINGLE", SMB2_FIND_FULL_DIRECTORY_INFO, RAW_SEARCH_DATA_FULL_DIRECTORY_INFO, CONT_SINGLE}, {"SMB2_FIND_FULL_DIRECTORY_INFO", "INDEX", SMB2_FIND_FULL_DIRECTORY_INFO, RAW_SEARCH_DATA_FULL_DIRECTORY_INFO, CONT_INDEX}, {"SMB2_FIND_FULL_DIRECTORY_INFO", "RESTART", SMB2_FIND_FULL_DIRECTORY_INFO, RAW_SEARCH_DATA_FULL_DIRECTORY_INFO, CONT_RESTART}, + {"SMB2_FIND_FULL_DIRECTORY_INFO", "REOPEN", SMB2_FIND_FULL_DIRECTORY_INFO, RAW_SEARCH_DATA_FULL_DIRECTORY_INFO, CONT_REOPEN}, {"SMB2_FIND_ID_FULL_DIRECTORY_INFO", "SINGLE", SMB2_FIND_ID_FULL_DIRECTORY_INFO, RAW_SEARCH_DATA_ID_FULL_DIRECTORY_INFO, CONT_SINGLE}, {"SMB2_FIND_ID_FULL_DIRECTORY_INFO", "INDEX", SMB2_FIND_ID_FULL_DIRECTORY_INFO, RAW_SEARCH_DATA_ID_FULL_DIRECTORY_INFO, CONT_INDEX}, {"SMB2_FIND_ID_FULL_DIRECTORY_INFO", "RESTART", SMB2_FIND_ID_FULL_DIRECTORY_INFO, RAW_SEARCH_DATA_ID_FULL_DIRECTORY_INFO, CONT_RESTART}, + {"SMB2_FIND_ID_FULL_DIRECTORY_INFO", "REOPEN", SMB2_FIND_ID_FULL_DIRECTORY_INFO, RAW_SEARCH_DATA_ID_FULL_DIRECTORY_INFO, CONT_REOPEN}, {"SMB2_FIND_ID_BOTH_DIRECTORY_INFO", "SINGLE", SMB2_FIND_ID_BOTH_DIRECTORY_INFO, RAW_SEARCH_DATA_ID_BOTH_DIRECTORY_INFO, CONT_SINGLE}, {"SMB2_FIND_ID_BOTH_DIRECTORY_INFO", "INDEX", SMB2_FIND_ID_BOTH_DIRECTORY_INFO, RAW_SEARCH_DATA_ID_BOTH_DIRECTORY_INFO, CONT_INDEX}, - {"SMB2_FIND_ID_BOTH_DIRECTORY_INFO", "RESTART", SMB2_FIND_ID_BOTH_DIRECTORY_INFO, RAW_SEARCH_DATA_ID_BOTH_DIRECTORY_INFO, CONT_RESTART} + {"SMB2_FIND_ID_BOTH_DIRECTORY_INFO", "RESTART", SMB2_FIND_ID_BOTH_DIRECTORY_INFO, RAW_SEARCH_DATA_ID_BOTH_DIRECTORY_INFO, CONT_RESTART}, + {"SMB2_FIND_ID_BOTH_DIRECTORY_INFO", "REOPEN", SMB2_FIND_ID_BOTH_DIRECTORY_INFO, RAW_SEARCH_DATA_ID_BOTH_DIRECTORY_INFO, CONT_REOPEN}, }; smb2_deltree(tree, DNAME); -- 2.12.0.367.g23dc2f6d3c-goog From b7d6f0d5be5f6e4ccaa21a93b5bb55b525ed3f1a Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Tue, 28 Feb 2017 10:17:18 -0800 Subject: [PATCH 03/37] s3: vfs: dirsort doesn't handle opendir of "." correctly. Needs to store $cwd path for correct sorting. BUG: https://bugzilla.samba.org/show_bug.cgi?id=12499 Signed-off-by: Jeremy Allison --- source3/modules/vfs_dirsort.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/source3/modules/vfs_dirsort.c b/source3/modules/vfs_dirsort.c index d6b33941fad..6a1c798e8d2 100644 --- a/source3/modules/vfs_dirsort.c +++ b/source3/modules/vfs_dirsort.c @@ -137,6 +137,10 @@ static DIR *dirsort_opendir(vfs_handle_struct *handle, return NULL; } + if (ISDOT(data->smb_fname->base_name)) { + data->smb_fname->base_name = vfs_GetWd(data, handle->conn); + } + /* Open the underlying directory and count the number of entries */ data->source_directory = SMB_VFS_NEXT_OPENDIR(handle, fname, mask, attr); -- 2.12.0.367.g23dc2f6d3c-goog From 0b064ed573c0228e370ed0ccb630d08d31d5f2c0 Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Wed, 11 Jan 2017 16:30:38 -0800 Subject: [PATCH 04/37] s3: smbd: Correctly canonicalize any incoming shadow copy path. Converts to: @GMT-token/path/last_component from all incoming path types. Allows shadow_copy modules to work when current directory is changed after removing last component. Ultimately when the VFS ABI is changed to add a timestamp to struct smb_filename, this is where the parsing will be done. BUG: https://bugzilla.samba.org/show_bug.cgi?id=12531 Signed-off-by: Jeremy Allison Reviewed-by: Uri Simchoni (backported from commit 39678ed6af708fb6f2760bfb51051add11e3c498) --- source3/smbd/filename.c | 150 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 150 insertions(+) diff --git a/source3/smbd/filename.c b/source3/smbd/filename.c index 770d5a766e6..3593155ce4e 100644 --- a/source3/smbd/filename.c +++ b/source3/smbd/filename.c @@ -186,6 +186,148 @@ static NTSTATUS check_parent_exists(TALLOC_CTX *ctx, return NT_STATUS_OK; } +/* + * Re-order a known good @GMT-token path. + */ + +static NTSTATUS rearrange_snapshot_path(struct smb_filename *smb_fname, + char *startp, + char *endp) +{ + size_t endlen = 0; + size_t gmt_len = endp - startp; + char gmt_store[gmt_len + 1]; + char *parent = NULL; + const char *last_component = NULL; + char *newstr; + bool ret; + + DEBUG(10,("|%s| -> ", smb_fname->base_name)); + + /* Save off the @GMT-token. */ + memcpy(gmt_store, startp, gmt_len); + gmt_store[gmt_len] = '\0'; + + if (*endp == '/') { + /* Remove any trailing '/' */ + endp++; + } + + if (*endp == '\0') { + /* + * @GMT-token was at end of path. + * Remove any preceeding '/' + */ + if (startp > smb_fname->base_name && startp[-1] == '/') { + startp--; + } + } + + /* Remove @GMT-token from the path. */ + endlen = strlen(endp); + memmove(startp, endp, endlen + 1); + + /* Split the remaining path into components. */ + ret = parent_dirname(smb_fname, + smb_fname->base_name, + &parent, + &last_component); + if (ret == false) { + /* Must terminate debug with \n */ + DEBUG(0,("NT_STATUS_NO_MEMORY\n")); + return NT_STATUS_NO_MEMORY; + } + + if (ISDOT(parent)) { + if (last_component[0] == '\0') { + newstr = talloc_strdup(smb_fname, + gmt_store); + } else { + newstr = talloc_asprintf(smb_fname, + "%s/%s", + gmt_store, + last_component); + } + } else { + newstr = talloc_asprintf(smb_fname, + "%s/%s/%s", + gmt_store, + parent, + last_component); + } + + TALLOC_FREE(parent); + TALLOC_FREE(smb_fname->base_name); + smb_fname->base_name = newstr; + + DEBUG(10,("|%s|\n", newstr)); + + return NT_STATUS_OK; +} + +/* + * Canonicalize any incoming pathname potentially containining + * a @GMT-token into a path that looks like: + * + * @GMT-YYYY-MM-DD-HH-MM-SS/path/name/components/last_component + * + * Leaves single path @GMT-token -component alone: + * + * @GMT-YYYY-MM-DD-HH-MM-SS -> @GMT-YYYY-MM-DD-HH-MM-SS + * + * Eventually when struct smb_filename is updated and the VFS + * ABI is changed this will remove the @GMT-YYYY-MM-DD-HH-MM-SS + * and store in the struct smb_filename as a struct timeval field + * instead. + */ + +static NTSTATUS canonicalize_snapshot_path(struct smb_filename *smb_fname) +{ + char *startp = strchr_m(smb_fname->base_name, '@'); + char *endp = NULL; + struct tm tm; + + if (startp == NULL) { + /* No @ */ + return NT_STATUS_OK; + } + + startp = strstr_m(startp, "@GMT-"); + if (startp == NULL) { + /* No @ */ + return NT_STATUS_OK; + } + + if ((startp > smb_fname->base_name) && (startp[-1] != '/')) { + /* the GMT-token does not start a path-component */ + return NT_STATUS_OK; + } + + endp = strptime(startp, GMT_FORMAT, &tm); + if (endp == NULL) { + /* Not a valid timestring. */ + return NT_STATUS_OK; + } + + if ( endp[0] == '\0') { + return rearrange_snapshot_path(smb_fname, + startp, + endp); + } + + if (endp[0] != '/') { + /* + * It is not a complete path component, i.e. the path + * component continues after the gmt-token. + */ + return NT_STATUS_OK; + } + + return rearrange_snapshot_path(smb_fname, + startp, + endp); +} + /**************************************************************************** This routine is called to convert names from the dos namespace to unix namespace. It needs to handle any case conversions, mangling, format changes, @@ -311,6 +453,14 @@ NTSTATUS unix_convert(TALLOC_CTX *ctx, goto err; } + /* Canonicalize any @GMT- paths. */ + if (posix_pathnames == false) { + status = canonicalize_snapshot_path(smb_fname); + if (!NT_STATUS_IS_OK(status)) { + goto err; + } + } + /* * Large directory fix normalization. If we're case sensitive, and * the case preserving parameters are set to "no", normalize the case of -- 2.12.0.367.g23dc2f6d3c-goog From 82a1225346b24fc31950f1ab1bbcf4e4fba83a47 Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Tue, 17 Jan 2017 11:33:18 -0800 Subject: [PATCH 05/37] s3: lib: Add canonicalize_absolute_path(). Resolves any invalid path components (.) (..) in an absolute POSIX path. We will be re-using this in several places. BUG: https://bugzilla.samba.org/show_bug.cgi?id=12531 Signed-off-by: Jeremy Allison Reviewed-by: Uri Simchoni (backported from commit 02599c39337c3049762a6b0bd6290577817ee5a5) --- source3/include/proto.h | 1 + source3/lib/util.c | 139 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 140 insertions(+) diff --git a/source3/include/proto.h b/source3/include/proto.h index be900246da4..f8a5b181b6f 100644 --- a/source3/include/proto.h +++ b/source3/include/proto.h @@ -465,6 +465,7 @@ bool map_open_params_to_ntcreate(const char *smb_base_fname, uint32_t *pprivate_flags); struct security_unix_token *copy_unix_token(TALLOC_CTX *ctx, const struct security_unix_token *tok); bool dir_check_ftype(uint32_t mode, uint32_t dirtype); +char *canonicalize_absolute_path(TALLOC_CTX *ctx, const char *abs_path); void init_modules(void); /* The following definitions come from lib/util_builtin.c */ diff --git a/source3/lib/util.c b/source3/lib/util.c index 087ea6d2bed..d8f755a56d3 100644 --- a/source3/lib/util.c +++ b/source3/lib/util.c @@ -2419,3 +2419,142 @@ bool dir_check_ftype(uint32_t mode, uint32_t dirtype) return true; } + +/** + * @brief Removes any invalid path components in an absolute POSIX path. + * + * @param ctx Talloc context to return string. + * + * @param abs_path Absolute path string to process. + * + * @retval Pointer to a talloc'ed string containing the absolute full path. + **/ + +char *canonicalize_absolute_path(TALLOC_CTX *ctx, const char *abs_path) +{ + char *destname; + char *d; + const char *s = abs_path; + bool start_of_name_component = true; + + /* Allocate for strlen + '\0' + possible leading '/' */ + destname = (char *)talloc_size(ctx, strlen(abs_path) + 2); + if (destname == NULL) { + return NULL; + } + d = destname; + + *d++ = '/'; /* Always start with root. */ + + while (*s) { + if (*s == '/') { + /* Eat multiple '/' */ + while (*s == '/') { + s++; + } + if ((d > destname + 1) && (*s != '\0')) { + *d++ = '/'; + } + start_of_name_component = true; + continue; + } + + if (start_of_name_component) { + if ((s[0] == '.') && (s[1] == '.') && + (s[2] == '/' || s[2] == '\0')) { + /* Uh oh - "/../" or "/..\0" ! */ + + /* Go past the .. leaving us on the / or '\0' */ + s += 2; + + /* If we just added a '/' - delete it */ + if ((d > destname) && (*(d-1) == '/')) { + *(d-1) = '\0'; + d--; + } + + /* + * Are we at the start ? + * Can't go back further if so. + */ + if (d <= destname) { + *d++ = '/'; /* Can't delete root */ + continue; + } + /* Go back one level... */ + /* + * Decrement d first as d points to + * the *next* char to write into. + */ + for (d--; d > destname; d--) { + if (*d == '/') { + break; + } + } + + /* + * Are we at the start ? + * Can't go back further if so. + */ + if (d <= destname) { + *d++ = '/'; /* Can't delete root */ + continue; + } + + /* + * We're still at the start of a name + * component, just the previous one. + */ + continue; + } else if ((s[0] == '.') && + ((s[1] == '\0') || s[1] == '/')) { + /* + * Component of pathname can't be "." only. + * Skip the '.' . + */ + if (s[1] == '/') { + s += 2; + } else { + s++; + } + continue; + } + } + + if (!(*s & 0x80)) { + *d++ = *s++; + } else { + size_t siz; + /* Get the size of the next MB character. */ + next_codepoint(s,&siz); + switch(siz) { + case 5: + *d++ = *s++; + /*fall through*/ + case 4: + *d++ = *s++; + /*fall through*/ + case 3: + *d++ = *s++; + /*fall through*/ + case 2: + *d++ = *s++; + /*fall through*/ + case 1: + *d++ = *s++; + break; + default: + break; + } + } + start_of_name_component = false; + } + *d = '\0'; + + /* And must not end in '/' */ + if (d > destname + 1 && (*(d-1) == '/')) { + *(d-1) = '\0'; + } + + return destname; +} -- 2.12.0.367.g23dc2f6d3c-goog From 6f15f8d2cbfb2b58b0267fa7e1734e7c99028ac3 Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Tue, 17 Jan 2017 11:35:52 -0800 Subject: [PATCH 06/37] s3: smbd: Make set_conn_connectpath() call canonicalize_absolute_path(). BUG: https://bugzilla.samba.org/show_bug.cgi?id=12531 Signed-off-by: Jeremy Allison Reviewed-by: Uri Simchoni (backported from commit d650d65488761b30fa34d42cb1ab400618a78c33) --- source3/smbd/service.c | 102 +------------------------------------------------ 1 file changed, 2 insertions(+), 100 deletions(-) diff --git a/source3/smbd/service.c b/source3/smbd/service.c index d11987e63ee..19be912ec7e 100644 --- a/source3/smbd/service.c +++ b/source3/smbd/service.c @@ -47,118 +47,20 @@ static bool canonicalize_connect_path(connection_struct *conn) /**************************************************************************** Ensure when setting connectpath it is a canonicalized (no ./ // or ../) absolute path stating in / and not ending in /. - Observent people will notice a similarity between this and check_path_syntax :-). ****************************************************************************/ bool set_conn_connectpath(connection_struct *conn, const char *connectpath) { char *destname; - char *d; - const char *s = connectpath; - bool start_of_name_component = true; if (connectpath == NULL || connectpath[0] == '\0') { return false; } - /* Allocate for strlen + '\0' + possible leading '/' */ - destname = (char *)talloc_size(conn, strlen(connectpath) + 2); - if (!destname) { + destname = canonicalize_absolute_path(conn, connectpath); + if (destname == NULL) { return false; } - d = destname; - - *d++ = '/'; /* Always start with root. */ - - while (*s) { - if (*s == '/') { - /* Eat multiple '/' */ - while (*s == '/') { - s++; - } - if ((d > destname + 1) && (*s != '\0')) { - *d++ = '/'; - } - start_of_name_component = True; - continue; - } - - if (start_of_name_component) { - if ((s[0] == '.') && (s[1] == '.') && (s[2] == '/' || s[2] == '\0')) { - /* Uh oh - "/../" or "/..\0" ! */ - - /* Go past the ../ or .. */ - if (s[2] == '/') { - s += 3; - } else { - s += 2; /* Go past the .. */ - } - - /* If we just added a '/' - delete it */ - if ((d > destname) && (*(d-1) == '/')) { - *(d-1) = '\0'; - d--; - } - - /* Are we at the start ? Can't go back further if so. */ - if (d <= destname) { - *d++ = '/'; /* Can't delete root */ - continue; - } - /* Go back one level... */ - /* Decrement d first as d points to the *next* char to write into. */ - for (d--; d > destname; d--) { - if (*d == '/') { - break; - } - } - /* We're still at the start of a name component, just the previous one. */ - continue; - } else if ((s[0] == '.') && ((s[1] == '\0') || s[1] == '/')) { - /* Component of pathname can't be "." only - skip the '.' . */ - if (s[1] == '/') { - s += 2; - } else { - s++; - } - continue; - } - } - - if (!(*s & 0x80)) { - *d++ = *s++; - } else { - size_t siz; - /* Get the size of the next MB character. */ - next_codepoint(s,&siz); - switch(siz) { - case 5: - *d++ = *s++; - /*fall through*/ - case 4: - *d++ = *s++; - /*fall through*/ - case 3: - *d++ = *s++; - /*fall through*/ - case 2: - *d++ = *s++; - /*fall through*/ - case 1: - *d++ = *s++; - break; - default: - break; - } - } - start_of_name_component = false; - } - *d = '\0'; - - /* And must not end in '/' */ - if (d > destname + 1 && (*(d-1) == '/')) { - *(d-1) = '\0'; - } DEBUG(10,("set_conn_connectpath: service %s, connectpath = %s\n", lp_servicename(talloc_tos(), SNUM(conn)), destname )); -- 2.12.0.367.g23dc2f6d3c-goog From 799085157990a9b2381e7afc59326d34fef10432 Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Fri, 20 Jan 2017 11:42:39 -0800 Subject: [PATCH 07/37] s3: VFS: shadow_copy2: Correctly initialize timestamp and stripped variables. Allow the called functions to be fixed to not touch them on error. BUG: https://bugzilla.samba.org/show_bug.cgi?id=12531 Signed-off-by: Jeremy Allison Reviewed-by: Uri Simchoni (backported from commit 0a190f4dd950c947d47c42163d11ea4bd6e6e508) --- source3/modules/vfs_shadow_copy2.c | 112 +++++++++++++++++++------------------ 1 file changed, 59 insertions(+), 53 deletions(-) diff --git a/source3/modules/vfs_shadow_copy2.c b/source3/modules/vfs_shadow_copy2.c index c5c2015795e..355181c4c85 100644 --- a/source3/modules/vfs_shadow_copy2.c +++ b/source3/modules/vfs_shadow_copy2.c @@ -230,10 +230,10 @@ static bool shadow_copy2_strip_snapshot(TALLOC_CTX *mem_ctx, char **pstripped) { struct tm tm; - time_t timestamp; + time_t timestamp = 0; const char *p; char *q; - char *stripped; + char *stripped = NULL; size_t rest_len, dst_len; struct shadow_copy2_config *config; const char *snapdir; @@ -663,8 +663,8 @@ static DIR *shadow_copy2_opendir(vfs_handle_struct *handle, const char *mask, uint32 attr) { - time_t timestamp; - char *stripped; + time_t timestamp = 0; + char *stripped = NULL; DIR *ret; int saved_errno; char *conv; @@ -692,7 +692,8 @@ static int shadow_copy2_rename(vfs_handle_struct *handle, const struct smb_filename *smb_fname_src, const struct smb_filename *smb_fname_dst) { - time_t timestamp_src, timestamp_dst; + time_t timestamp_src = 0; + time_t timestamp_dst = 0; if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, smb_fname_src->base_name, @@ -718,7 +719,8 @@ static int shadow_copy2_rename(vfs_handle_struct *handle, static int shadow_copy2_symlink(vfs_handle_struct *handle, const char *oldname, const char *newname) { - time_t timestamp_old, timestamp_new; + time_t timestamp_old = 0; + time_t timestamp_new = 0; if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, oldname, ×tamp_old, NULL)) { @@ -738,7 +740,8 @@ static int shadow_copy2_symlink(vfs_handle_struct *handle, static int shadow_copy2_link(vfs_handle_struct *handle, const char *oldname, const char *newname) { - time_t timestamp_old, timestamp_new; + time_t timestamp_old = 0; + time_t timestamp_new = 0; if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, oldname, ×tamp_old, NULL)) { @@ -758,8 +761,9 @@ static int shadow_copy2_link(vfs_handle_struct *handle, static int shadow_copy2_stat(vfs_handle_struct *handle, struct smb_filename *smb_fname) { - time_t timestamp; - char *stripped, *tmp; + time_t timestamp = 0; + char *stripped = NULL; + char *tmp; int ret, saved_errno; if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, @@ -797,8 +801,9 @@ static int shadow_copy2_stat(vfs_handle_struct *handle, static int shadow_copy2_lstat(vfs_handle_struct *handle, struct smb_filename *smb_fname) { - time_t timestamp; - char *stripped, *tmp; + time_t timestamp = 0; + char *stripped = NULL; + char *tmp; int ret, saved_errno; if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, @@ -836,7 +841,7 @@ static int shadow_copy2_lstat(vfs_handle_struct *handle, static int shadow_copy2_fstat(vfs_handle_struct *handle, files_struct *fsp, SMB_STRUCT_STAT *sbuf) { - time_t timestamp; + time_t timestamp = 0; int ret; ret = SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf); @@ -858,8 +863,9 @@ static int shadow_copy2_open(vfs_handle_struct *handle, struct smb_filename *smb_fname, files_struct *fsp, int flags, mode_t mode) { - time_t timestamp; - char *stripped, *tmp; + time_t timestamp = 0; + char *stripped = NULL; + char *tmp; int ret, saved_errno; if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, @@ -894,8 +900,8 @@ static int shadow_copy2_open(vfs_handle_struct *handle, static int shadow_copy2_unlink(vfs_handle_struct *handle, const struct smb_filename *smb_fname) { - time_t timestamp; - char *stripped; + time_t timestamp = 0; + char *stripped = NULL; int ret, saved_errno; struct smb_filename *conv; @@ -928,8 +934,8 @@ static int shadow_copy2_unlink(vfs_handle_struct *handle, static int shadow_copy2_chmod(vfs_handle_struct *handle, const char *fname, mode_t mode) { - time_t timestamp; - char *stripped; + time_t timestamp = 0; + char *stripped = NULL; int ret, saved_errno; char *conv; @@ -955,8 +961,8 @@ static int shadow_copy2_chmod(vfs_handle_struct *handle, const char *fname, static int shadow_copy2_chown(vfs_handle_struct *handle, const char *fname, uid_t uid, gid_t gid) { - time_t timestamp; - char *stripped; + time_t timestamp = 0; + char *stripped = NULL; int ret, saved_errno; char *conv; @@ -982,8 +988,8 @@ static int shadow_copy2_chown(vfs_handle_struct *handle, const char *fname, static int shadow_copy2_chdir(vfs_handle_struct *handle, const char *fname) { - time_t timestamp; - char *stripped; + time_t timestamp = 0; + char *stripped = NULL; int ret, saved_errno; char *conv; @@ -1010,8 +1016,8 @@ static int shadow_copy2_ntimes(vfs_handle_struct *handle, const struct smb_filename *smb_fname, struct smb_file_time *ft) { - time_t timestamp; - char *stripped; + time_t timestamp = 0; + char *stripped = NULL; int ret, saved_errno; struct smb_filename *conv; @@ -1044,8 +1050,8 @@ static int shadow_copy2_ntimes(vfs_handle_struct *handle, static int shadow_copy2_readlink(vfs_handle_struct *handle, const char *fname, char *buf, size_t bufsiz) { - time_t timestamp; - char *stripped; + time_t timestamp = 0; + char *stripped = NULL; int ret, saved_errno; char *conv; @@ -1071,8 +1077,8 @@ static int shadow_copy2_readlink(vfs_handle_struct *handle, static int shadow_copy2_mknod(vfs_handle_struct *handle, const char *fname, mode_t mode, SMB_DEV_T dev) { - time_t timestamp; - char *stripped; + time_t timestamp = 0; + char *stripped = NULL; int ret, saved_errno; char *conv; @@ -1098,7 +1104,7 @@ static int shadow_copy2_mknod(vfs_handle_struct *handle, static char *shadow_copy2_realpath(vfs_handle_struct *handle, const char *fname) { - time_t timestamp; + time_t timestamp = 0; char *stripped = NULL; char *tmp = NULL; char *result = NULL; @@ -1453,8 +1459,8 @@ static NTSTATUS shadow_copy2_fget_nt_acl(vfs_handle_struct *handle, TALLOC_CTX *mem_ctx, struct security_descriptor **ppdesc) { - time_t timestamp; - char *stripped; + time_t timestamp = 0; + char *stripped = NULL; NTSTATUS status; char *conv; @@ -1485,8 +1491,8 @@ static NTSTATUS shadow_copy2_get_nt_acl(vfs_handle_struct *handle, TALLOC_CTX *mem_ctx, struct security_descriptor **ppdesc) { - time_t timestamp; - char *stripped; + time_t timestamp = 0; + char *stripped = NULL; NTSTATUS status; char *conv; @@ -1512,8 +1518,8 @@ static NTSTATUS shadow_copy2_get_nt_acl(vfs_handle_struct *handle, static int shadow_copy2_mkdir(vfs_handle_struct *handle, const char *fname, mode_t mode) { - time_t timestamp; - char *stripped; + time_t timestamp = 0; + char *stripped = NULL; int ret, saved_errno; char *conv; @@ -1538,8 +1544,8 @@ static int shadow_copy2_mkdir(vfs_handle_struct *handle, static int shadow_copy2_rmdir(vfs_handle_struct *handle, const char *fname) { - time_t timestamp; - char *stripped; + time_t timestamp = 0; + char *stripped = NULL; int ret, saved_errno; char *conv; @@ -1565,8 +1571,8 @@ static int shadow_copy2_rmdir(vfs_handle_struct *handle, const char *fname) static int shadow_copy2_chflags(vfs_handle_struct *handle, const char *fname, unsigned int flags) { - time_t timestamp; - char *stripped; + time_t timestamp = 0; + char *stripped = NULL; int ret, saved_errno; char *conv; @@ -1593,8 +1599,8 @@ static ssize_t shadow_copy2_getxattr(vfs_handle_struct *handle, const char *fname, const char *aname, void *value, size_t size) { - time_t timestamp; - char *stripped; + time_t timestamp = 0; + char *stripped = NULL; ssize_t ret; int saved_errno; char *conv; @@ -1623,8 +1629,8 @@ static ssize_t shadow_copy2_listxattr(struct vfs_handle_struct *handle, const char *fname, char *list, size_t size) { - time_t timestamp; - char *stripped; + time_t timestamp = 0; + char *stripped = NULL; ssize_t ret; int saved_errno; char *conv; @@ -1651,8 +1657,8 @@ static ssize_t shadow_copy2_listxattr(struct vfs_handle_struct *handle, static int shadow_copy2_removexattr(vfs_handle_struct *handle, const char *fname, const char *aname) { - time_t timestamp; - char *stripped; + time_t timestamp = 0; + char *stripped = NULL; int ret, saved_errno; char *conv; @@ -1680,8 +1686,8 @@ static int shadow_copy2_setxattr(struct vfs_handle_struct *handle, const char *aname, const void *value, size_t size, int flags) { - time_t timestamp; - char *stripped; + time_t timestamp = 0; + char *stripped = NULL; ssize_t ret; int saved_errno; char *conv; @@ -1709,8 +1715,8 @@ static int shadow_copy2_setxattr(struct vfs_handle_struct *handle, static int shadow_copy2_chmod_acl(vfs_handle_struct *handle, const char *fname, mode_t mode) { - time_t timestamp; - char *stripped; + time_t timestamp = 0; + char *stripped = NULL; ssize_t ret; int saved_errno; char *conv; @@ -1740,8 +1746,8 @@ static int shadow_copy2_get_real_filename(struct vfs_handle_struct *handle, TALLOC_CTX *mem_ctx, char **found_name) { - time_t timestamp; - char *stripped; + time_t timestamp = 0; + char *stripped = NULL; ssize_t ret; int saved_errno; char *conv; @@ -1781,8 +1787,8 @@ static uint64_t shadow_copy2_disk_free(vfs_handle_struct *handle, uint64_t *bsize, uint64_t *dfree, uint64_t *dsize) { - time_t timestamp; - char *stripped; + time_t timestamp = 0; + char *stripped = NULL; ssize_t ret; int saved_errno; char *conv; -- 2.12.0.367.g23dc2f6d3c-goog From 4cc88d4654691c3d6bc593ab5f0b6e69ce209d76 Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Fri, 20 Jan 2017 11:45:54 -0800 Subject: [PATCH 08/37] s3: VFS: shadow_copy2: Ensure pathnames for parameters are correctly relative and terminated. BUG: https://bugzilla.samba.org/show_bug.cgi?id=12531 Signed-off-by: Jeremy Allison Reviewed-by: Uri Simchoni (backported from commit 979e39252bcc88e8aacb543b8bf322dd6f17fe7f) --- source3/modules/vfs_shadow_copy2.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/source3/modules/vfs_shadow_copy2.c b/source3/modules/vfs_shadow_copy2.c index 355181c4c85..b6297d4334f 100644 --- a/source3/modules/vfs_shadow_copy2.c +++ b/source3/modules/vfs_shadow_copy2.c @@ -2017,6 +2017,11 @@ static int shadow_copy2_connect(struct vfs_handle_struct *handle, } } + trim_string(config->mount_point, NULL, "/"); + trim_string(config->rel_connectpath, "/", "/"); + trim_string(config->snapdir, NULL, "/"); + trim_string(config->snapshot_basepath, NULL, "/"); + DEBUG(10, ("shadow_copy2_connect: configuration:\n" " share root: '%s'\n" " basedir: '%s'\n" -- 2.12.0.367.g23dc2f6d3c-goog From 04bdc8dcd9c868bf69f090b4363365d12083a030 Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Fri, 20 Jan 2017 11:48:40 -0800 Subject: [PATCH 09/37] s3: VFS: shadow_copy2: Fix length comparison to ensure we don't overstep a length. BUG: https://bugzilla.samba.org/show_bug.cgi?id=12531 Signed-off-by: Jeremy Allison Reviewed-by: Uri Simchoni (backported from commit 37ef8d3f65bd1215717eb51b2e1cdb84a7bed348) --- source3/modules/vfs_shadow_copy2.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/source3/modules/vfs_shadow_copy2.c b/source3/modules/vfs_shadow_copy2.c index b6297d4334f..d0a130c0e0b 100644 --- a/source3/modules/vfs_shadow_copy2.c +++ b/source3/modules/vfs_shadow_copy2.c @@ -1979,7 +1979,8 @@ static int shadow_copy2_connect(struct vfs_handle_struct *handle, config->basedir = config->mount_point; } - if (strlen(config->basedir) != strlen(handle->conn->connectpath)) { + if (config->rel_connectpath == NULL && + strlen(config->basedir) < strlen(handle->conn->connectpath)) { config->rel_connectpath = talloc_strdup(config, handle->conn->connectpath + strlen(config->basedir)); if (config->rel_connectpath == NULL) { -- 2.12.0.367.g23dc2f6d3c-goog From 9c363509a342c793185fb023a4ae620afe13a845 Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Fri, 20 Jan 2017 11:50:49 -0800 Subject: [PATCH 10/37] s3: VFS: shadow_copy2: Add two new variables to the config data. Not yet used. BUG: https://bugzilla.samba.org/show_bug.cgi?id=12531 Signed-off-by: Jeremy Allison Reviewed-by: Uri Simchoni (backported from commit 72fe2b62e3ee7462e5be855b01943f28b26c36c1) --- source3/modules/vfs_shadow_copy2.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/source3/modules/vfs_shadow_copy2.c b/source3/modules/vfs_shadow_copy2.c index d0a130c0e0b..b342b5a9d25 100644 --- a/source3/modules/vfs_shadow_copy2.c +++ b/source3/modules/vfs_shadow_copy2.c @@ -50,6 +50,9 @@ struct shadow_copy2_config { char *mount_point; char *rel_connectpath; /* share root, relative to the basedir */ char *snapshot_basepath; /* the absolute version of snapdir */ + char *shadow_cwd; /* Absolute $cwd path. */ + /* Absolute connectpath - can vary depending on $cwd. */ + char *shadow_connectpath; }; static bool shadow_copy2_find_slashes(TALLOC_CTX *mem_ctx, const char *str, -- 2.12.0.367.g23dc2f6d3c-goog From 9b09f870701c0f2ccea182e9966c20926be326a2 Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Fri, 20 Jan 2017 11:54:56 -0800 Subject: [PATCH 11/37] s3: VFS: shadow_copy2: Add a wrapper function to call the original shadow_copy2_strip_snapshot(). BUG: https://bugzilla.samba.org/show_bug.cgi?id=12531 Allows an extra (currently unused) parameter to be added. Signed-off-by: Jeremy Allison Reviewed-by: Uri Simchoni (backported from commit 5aa1ea95157475dfd2d056f0158b14b2b90895a9) --- source3/modules/vfs_shadow_copy2.c | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/source3/modules/vfs_shadow_copy2.c b/source3/modules/vfs_shadow_copy2.c index b342b5a9d25..2cd8d3a667e 100644 --- a/source3/modules/vfs_shadow_copy2.c +++ b/source3/modules/vfs_shadow_copy2.c @@ -226,11 +226,12 @@ static char *shadow_copy2_snapshot_path(TALLOC_CTX *mem_ctx, * handed in via the smb layer. * Returns the parsed timestamp and the stripped filename. */ -static bool shadow_copy2_strip_snapshot(TALLOC_CTX *mem_ctx, +static bool shadow_copy2_strip_snapshot_internal(TALLOC_CTX *mem_ctx, struct vfs_handle_struct *handle, const char *name, time_t *ptimestamp, - char **pstripped) + char **pstripped, + char **psnappath) { struct tm tm; time_t timestamp = 0; @@ -404,6 +405,20 @@ no_snapshot: return true; } +static bool shadow_copy2_strip_snapshot(TALLOC_CTX *mem_ctx, + struct vfs_handle_struct *handle, + const char *orig_name, + time_t *ptimestamp, + char **pstripped) +{ + return shadow_copy2_strip_snapshot_internal(mem_ctx, + handle, + orig_name, + ptimestamp, + pstripped, + NULL); +} + static char *shadow_copy2_find_mount_point(TALLOC_CTX *mem_ctx, vfs_handle_struct *handle) { -- 2.12.0.367.g23dc2f6d3c-goog From e414daecb2451bc47be4efc58403705b56ba7a79 Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Fri, 20 Jan 2017 11:56:21 -0800 Subject: [PATCH 12/37] s3: VFS: shadow_copy2: Change a parameter name. BUG: https://bugzilla.samba.org/show_bug.cgi?id=12531 Allows easy substitution later. Signed-off-by: Jeremy Allison Reviewed-by: Uri Simchoni (backported from commit 2887465108aef5e2e7c64417437ecb86c7460e16) --- source3/modules/vfs_shadow_copy2.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/source3/modules/vfs_shadow_copy2.c b/source3/modules/vfs_shadow_copy2.c index 2cd8d3a667e..2e5be958df4 100644 --- a/source3/modules/vfs_shadow_copy2.c +++ b/source3/modules/vfs_shadow_copy2.c @@ -228,7 +228,7 @@ static char *shadow_copy2_snapshot_path(TALLOC_CTX *mem_ctx, */ static bool shadow_copy2_strip_snapshot_internal(TALLOC_CTX *mem_ctx, struct vfs_handle_struct *handle, - const char *name, + const char *orig_name, time_t *ptimestamp, char **pstripped, char **psnappath) @@ -243,6 +243,7 @@ static bool shadow_copy2_strip_snapshot_internal(TALLOC_CTX *mem_ctx, const char *snapdir; ssize_t snapdirlen; ptrdiff_t len_before_gmt; + const char *name = orig_name; SMB_VFS_HANDLE_GET_DATA(handle, config, struct shadow_copy2_config, return false); -- 2.12.0.367.g23dc2f6d3c-goog From 2860989740fee34439b540b233a3b9687bb53308 Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Fri, 20 Jan 2017 12:00:08 -0800 Subject: [PATCH 13/37] s3: VFS: shadow_copy2: Add two currently unused functions to make pathnames absolute or relative to $cwd. BUG: https://bugzilla.samba.org/show_bug.cgi?id=12531 Signed-off-by: Jeremy Allison Reviewed-by: Uri Simchoni (backported from commit 9d65107b8f2864dba8d41b3316c483b3f36d0697) --- source3/modules/vfs_shadow_copy2.c | 44 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/source3/modules/vfs_shadow_copy2.c b/source3/modules/vfs_shadow_copy2.c index 2e5be958df4..6647c68009e 100644 --- a/source3/modules/vfs_shadow_copy2.c +++ b/source3/modules/vfs_shadow_copy2.c @@ -221,6 +221,50 @@ static char *shadow_copy2_snapshot_path(TALLOC_CTX *mem_ctx, return result; } +static char *make_path_absolute(TALLOC_CTX *mem_ctx, + struct shadow_copy2_config *config, + const char *name) +{ + char *newpath = NULL; + char *abs_path = NULL; + + if (name[0] != '/') { + newpath = talloc_asprintf(mem_ctx, + "%s/%s", + config->shadow_cwd, + name); + if (newpath == NULL) { + return NULL; + } + name = newpath; + } + abs_path = canonicalize_absolute_path(mem_ctx, name); + TALLOC_FREE(newpath); + return abs_path; +} + +/* Return a $cwd-relative path. */ +static bool make_relative_path(const char *cwd, char *abs_path) +{ + size_t cwd_len = strlen(cwd); + size_t abs_len = strlen(abs_path); + + if (abs_len < cwd_len) { + return false; + } + if (memcmp(abs_path, cwd, cwd_len) != 0) { + return false; + } + if (abs_path[cwd_len] != '/' && abs_path[cwd_len] != '\0') { + return false; + } + if (abs_path[cwd_len] == '/') { + cwd_len++; + } + memmove(abs_path, &abs_path[cwd_len], abs_len + 1 - cwd_len); + return true; +} + /** * Strip a snapshot component from a filename as * handed in via the smb layer. -- 2.12.0.367.g23dc2f6d3c-goog From 1b992273ef749df5a50ae5b3dc7ca73cfba6e32e Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Fri, 20 Jan 2017 12:06:55 -0800 Subject: [PATCH 14/37] s3: VFS: shadow_copy2: Fix chdir to store off the needed private variables. BUG: https://bugzilla.samba.org/show_bug.cgi?id=12531 This is not yet used, the users of this will be added later. Signed-off-by: Jeremy Allison Reviewed-by: Uri Simchoni (backported from commit 27340df4b52e4341f134667c59d71656a7a1fdae) --- source3/modules/vfs_shadow_copy2.c | 83 +++++++++++++++++++++++++++++++------- 1 file changed, 69 insertions(+), 14 deletions(-) diff --git a/source3/modules/vfs_shadow_copy2.c b/source3/modules/vfs_shadow_copy2.c index 6647c68009e..a96aecbef91 100644 --- a/source3/modules/vfs_shadow_copy2.c +++ b/source3/modules/vfs_shadow_copy2.c @@ -1048,30 +1048,85 @@ static int shadow_copy2_chown(vfs_handle_struct *handle, const char *fname, return ret; } +static void store_cwd_data(vfs_handle_struct *handle, + const char *connectpath) +{ + struct shadow_copy2_config *config = NULL; + char *cwd = NULL; + + SMB_VFS_HANDLE_GET_DATA(handle, config, struct shadow_copy2_config, + return); + + TALLOC_FREE(config->shadow_cwd); + cwd = SMB_VFS_NEXT_GETWD(handle); + if (cwd == NULL) { + smb_panic("getwd failed\n"); + } + DEBUG(10,("shadow cwd = %s\n", cwd)); + config->shadow_cwd = talloc_strdup(config, cwd); + SAFE_FREE(cwd); + if (config->shadow_cwd == NULL) { + smb_panic("talloc failed\n"); + } + TALLOC_FREE(config->shadow_connectpath); + if (connectpath) { + DEBUG(10,("shadow conectpath = %s\n", connectpath)); + config->shadow_connectpath = talloc_strdup(config, connectpath); + if (config->shadow_connectpath == NULL) { + smb_panic("talloc failed\n"); + } + } +} + static int shadow_copy2_chdir(vfs_handle_struct *handle, const char *fname) { time_t timestamp = 0; char *stripped = NULL; - int ret, saved_errno; - char *conv; - - if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname, - ×tamp, &stripped)) { + char *snappath = NULL; + int ret = -1; + int saved_errno = 0; + char *conv = NULL; + size_t rootpath_len = 0; + + if (!shadow_copy2_strip_snapshot_internal(talloc_tos(), handle, fname, + ×tamp, &stripped, &snappath)) { return -1; } - if (timestamp == 0) { - return SMB_VFS_NEXT_CHDIR(handle, fname); + if (stripped != NULL) { + conv = shadow_copy2_do_convert(talloc_tos(), + handle, + stripped, + timestamp, + &rootpath_len); + TALLOC_FREE(stripped); + if (conv == NULL) { + return -1; + } + fname = conv; } - conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp); - TALLOC_FREE(stripped); - if (conv == NULL) { - return -1; + + ret = SMB_VFS_NEXT_CHDIR(handle, fname); + if (ret == -1) { + saved_errno = errno; } - ret = SMB_VFS_NEXT_CHDIR(handle, conv); - saved_errno = errno; + + if (ret == 0) { + if (conv != NULL && rootpath_len != 0) { + conv[rootpath_len] = '\0'; + } else if (snappath != 0) { + TALLOC_FREE(conv); + conv = snappath; + } + store_cwd_data(handle, conv); + } + + TALLOC_FREE(stripped); TALLOC_FREE(conv); - errno = saved_errno; + + if (saved_errno != 0) { + errno = saved_errno; + } return ret; } -- 2.12.0.367.g23dc2f6d3c-goog From fd596d8853e31b664ffa96021fed34ca608f98b6 Mon Sep 17 00:00:00 2001 From: Uri Simchoni Date: Sun, 1 Nov 2015 22:28:46 +0200 Subject: [PATCH 15/37] vfs_shadow_copy2: add shadow_copy2_do_convert() Add a new routine shadow_copy2_do_convert() which is like shadow_copy2_convert(), but beside calculating the path of the snapshot file, it also returns the minimum length of the subpath which is both inside the share and inside the same snapshot as the file in question, i.e. (at least in the classical case) the subdirectory of the the snapshot file's snapshot directory that corresponds to the file's share root. BUG: https://bugzilla.samba.org/show_bug.cgi?id=11580 Signed-off-by: Uri Simchoni Reviewed-by: Michael Adam --- source3/modules/vfs_shadow_copy2.c | 39 ++++++++++++++++++++++++++++++++++---- 1 file changed, 35 insertions(+), 4 deletions(-) diff --git a/source3/modules/vfs_shadow_copy2.c b/source3/modules/vfs_shadow_copy2.c index a96aecbef91..fd91858f090 100644 --- a/source3/modules/vfs_shadow_copy2.c +++ b/source3/modules/vfs_shadow_copy2.c @@ -498,10 +498,13 @@ static char *shadow_copy2_find_mount_point(TALLOC_CTX *mem_ctx, * Convert from a name as handed in via the SMB layer * and a timestamp into the local path of the snapshot * of the provided file at the provided time. + * Also return the path in the snapshot corresponding + * to the file's share root. */ -static char *shadow_copy2_convert(TALLOC_CTX *mem_ctx, - struct vfs_handle_struct *handle, - const char *name, time_t timestamp) +static char *shadow_copy2_do_convert(TALLOC_CTX *mem_ctx, + struct vfs_handle_struct *handle, + const char *name, time_t timestamp, + size_t *snaproot_len) { struct smb_filename converted_fname; char *result = NULL; @@ -511,10 +514,11 @@ static char *shadow_copy2_convert(TALLOC_CTX *mem_ctx, size_t pathlen; char *insert = NULL; char *converted = NULL; - size_t insertlen; + size_t insertlen, connectlen = 0; int i, saved_errno; size_t min_offset; struct shadow_copy2_config *config; + size_t in_share_offset = 0; SMB_VFS_HANDLE_GET_DATA(handle, config, struct shadow_copy2_config, return NULL); @@ -556,6 +560,13 @@ static char *shadow_copy2_convert(TALLOC_CTX *mem_ctx, DEBUG(10, ("Found %s\n", converted)); result = converted; converted = NULL; + if (snaproot_len != NULL) { + *snaproot_len = strlen(snapshot_path); + if (config->rel_connectpath != NULL) { + *snaproot_len += + strlen(config->rel_connectpath) + 1; + } + } goto fail; } else { errno = ENOENT; @@ -564,6 +575,7 @@ static char *shadow_copy2_convert(TALLOC_CTX *mem_ctx, /* never reached ... */ } + connectlen = strlen(handle->conn->connectpath); if (name[0] == 0) { path = talloc_strdup(mem_ctx, handle->conn->connectpath); } else { @@ -639,6 +651,10 @@ static char *shadow_copy2_convert(TALLOC_CTX *mem_ctx, goto fail; } + if (offset >= connectlen) { + in_share_offset = offset; + } + memcpy(converted+offset, insert, insertlen); offset += insertlen; @@ -652,6 +668,9 @@ static char *shadow_copy2_convert(TALLOC_CTX *mem_ctx, ret, ret == 0 ? "ok" : strerror(errno))); if (ret == 0) { /* success */ + if (snaproot_len != NULL) { + *snaproot_len = in_share_offset + insertlen; + } break; } if (errno == ENOTDIR) { @@ -688,6 +707,18 @@ fail: return result; } +/** + * Convert from a name as handed in via the SMB layer + * and a timestamp into the local path of the snapshot + * of the provided file at the provided time. + */ +static char *shadow_copy2_convert(TALLOC_CTX *mem_ctx, + struct vfs_handle_struct *handle, + const char *name, time_t timestamp) +{ + return shadow_copy2_do_convert(mem_ctx, handle, name, timestamp, NULL); +} + /* modify a sbuf return to ensure that inodes in the shadow directory are different from those in the main directory -- 2.12.0.367.g23dc2f6d3c-goog From 8febc22b45785efb006cc524790123bed4903b67 Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Tue, 28 Feb 2017 13:57:23 -0800 Subject: [PATCH 16/37] vfs_shadow_copy2: fix case where snapshots are outside the share Adjust the connect path to the root of the share in the snapshot, or to the root of the snapshot if the snapshot is "inside" the share. This way snapshot symlink regarded as "wide links" if and only if they point outside the snapshot or they were wide links when the snapshot was taken. This allows mounting the snapshots outside the share's root. BUG: https://bugzilla.samba.org/show_bug.cgi?id=11580 Signed-off-by: Jeremy Allison --- source3/modules/vfs_shadow_copy2.c | 68 ++++++++++++++++++++++++++------------ 1 file changed, 46 insertions(+), 22 deletions(-) diff --git a/source3/modules/vfs_shadow_copy2.c b/source3/modules/vfs_shadow_copy2.c index fd91858f090..7e4913afa22 100644 --- a/source3/modules/vfs_shadow_copy2.c +++ b/source3/modules/vfs_shadow_copy2.c @@ -1257,8 +1257,6 @@ static char *shadow_copy2_realpath(vfs_handle_struct *handle, char *stripped = NULL; char *tmp = NULL; char *result = NULL; - char *inserted = NULL; - char *inserted_to, *inserted_end; int saved_errno; if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname, @@ -1275,29 +1273,9 @@ static char *shadow_copy2_realpath(vfs_handle_struct *handle, } result = SMB_VFS_NEXT_REALPATH(handle, tmp); - if (result == NULL) { - goto done; - } - - /* - * Take away what we've inserted. This removes the @GMT-thingy - * completely, but will give a path under the share root. - */ - inserted = shadow_copy2_insert_string(talloc_tos(), handle, timestamp); - if (inserted == NULL) { - goto done; - } - inserted_to = strstr_m(result, inserted); - if (inserted_to == NULL) { - DEBUG(2, ("SMB_VFS_NEXT_REALPATH removed %s\n", inserted)); - goto done; - } - inserted_end = inserted_to + talloc_get_size(inserted) - 1; - memmove(inserted_to, inserted_end, strlen(inserted_end)+1); done: saved_errno = errno; - TALLOC_FREE(inserted); TALLOC_FREE(tmp); TALLOC_FREE(stripped); errno = saved_errno; @@ -1931,6 +1909,51 @@ static int shadow_copy2_get_real_filename(struct vfs_handle_struct *handle, return ret; } +static const char *shadow_copy2_connectpath(struct vfs_handle_struct *handle, + const char *fname) +{ + time_t timestamp; + char *stripped = NULL; + char *tmp = NULL; + char *result = NULL; + int saved_errno; + size_t rootpath_len = 0; + + DEBUG(10,("Calc connect path for [%s]\n", fname)); + + if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname, + ×tamp, &stripped)) { + goto done; + } + if (timestamp == 0) { + return SMB_VFS_NEXT_CONNECTPATH(handle, fname); + } + + tmp = shadow_copy2_do_convert(talloc_tos(), handle, stripped, timestamp, + &rootpath_len); + if (tmp == NULL) { + goto done; + } + + DEBUG(10,("converted path is [%s] root path is [%.*s]\n", tmp, + (int)rootpath_len, tmp)); + + tmp[rootpath_len] = '\0'; + result = SMB_VFS_NEXT_REALPATH(handle, tmp); + if (result == NULL) { + goto done; + } + + DEBUG(10,("connect path is [%s]\n", result)); + +done: + saved_errno = errno; + TALLOC_FREE(tmp); + TALLOC_FREE(stripped); + errno = saved_errno; + return result; +} + static uint64_t shadow_copy2_disk_free(vfs_handle_struct *handle, const char *path, bool small_query, uint64_t *bsize, uint64_t *dfree, @@ -2239,6 +2262,7 @@ static struct vfs_fn_pointers vfs_shadow_copy2_fns = { .chmod_acl_fn = shadow_copy2_chmod_acl, .chflags_fn = shadow_copy2_chflags, .get_real_filename_fn = shadow_copy2_get_real_filename, + .connectpath_fn = shadow_copy2_connectpath, }; NTSTATUS vfs_shadow_copy2_init(void); -- 2.12.0.367.g23dc2f6d3c-goog From bf1577442b686a7ccd81944dac4fa32305a823b9 Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Fri, 20 Jan 2017 12:09:08 -0800 Subject: [PATCH 17/37] s3: VFS: Allow shadow_copy2_connectpath() to return the cached path derived from $cwd. BUG: https://bugzilla.samba.org/show_bug.cgi?id=12531 Signed-off-by: Jeremy Allison Reviewed-by: Uri Simchoni (backported from commit 42bd1acad75a6b5ea81fe4b30c067dd82623c042) --- source3/modules/vfs_shadow_copy2.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/source3/modules/vfs_shadow_copy2.c b/source3/modules/vfs_shadow_copy2.c index 7e4913afa22..00f7a040855 100644 --- a/source3/modules/vfs_shadow_copy2.c +++ b/source3/modules/vfs_shadow_copy2.c @@ -1918,9 +1918,19 @@ static const char *shadow_copy2_connectpath(struct vfs_handle_struct *handle, char *result = NULL; int saved_errno; size_t rootpath_len = 0; + struct shadow_copy2_config *config = NULL; + + SMB_VFS_HANDLE_GET_DATA(handle, config, struct shadow_copy2_config, + return NULL); DEBUG(10,("Calc connect path for [%s]\n", fname)); + if (config->shadow_connectpath != NULL) { + DEBUG(10,("cached connect path is [%s]\n", + config->shadow_connectpath)); + return config->shadow_connectpath; + } + if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname, ×tamp, &stripped)) { goto done; -- 2.12.0.367.g23dc2f6d3c-goog From b2fa866e6d53210257afb33c7dfb6cda25fb8be2 Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Thu, 26 Jan 2017 10:24:52 -0800 Subject: [PATCH 18/37] s3: VFS: Ensure shadow:format cannot contain a / path separator. BUG: https://bugzilla.samba.org/show_bug.cgi?id=12531 Signed-off-by: Jeremy Allison Reviewed-by: Uri Simchoni (backported from commit cd4f940162b17e4f7345d392326a31ae478230fa) --- source3/modules/vfs_shadow_copy2.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/source3/modules/vfs_shadow_copy2.c b/source3/modules/vfs_shadow_copy2.c index 00f7a040855..808d37a7dbf 100644 --- a/source3/modules/vfs_shadow_copy2.c +++ b/source3/modules/vfs_shadow_copy2.c @@ -2037,6 +2037,15 @@ static int shadow_copy2_connect(struct vfs_handle_struct *handle, return -1; } + /* config->gmt_format must not contain a path separator. */ + if (strchr(config->gmt_format, '/') != NULL) { + DEBUG(0, ("shadow:format %s must not contain a /" + "character. Unable to initialize module.\n", + config->gmt_format)); + errno = EINVAL; + return -1; + } + config->use_sscanf = lp_parm_bool(SNUM(handle->conn), "shadow", "sscanf", false); -- 2.12.0.367.g23dc2f6d3c-goog From a0afe8dd01a55ad4c67df54e7771eb5a21a0c954 Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Thu, 26 Jan 2017 10:35:50 -0800 Subject: [PATCH 19/37] s3: VFS: Add utility function check_for_converted_path(). Detects an already converted path. Not yet used. BUG: https://bugzilla.samba.org/show_bug.cgi?id=12531 Signed-off-by: Jeremy Allison Reviewed-by: Uri Simchoni (backported from commit b94dc85d339c9a10496edd07b85bdd7808d2e332) --- source3/modules/vfs_shadow_copy2.c | 108 +++++++++++++++++++++++++++++++++++++ 1 file changed, 108 insertions(+) diff --git a/source3/modules/vfs_shadow_copy2.c b/source3/modules/vfs_shadow_copy2.c index 808d37a7dbf..f1d4619a03d 100644 --- a/source3/modules/vfs_shadow_copy2.c +++ b/source3/modules/vfs_shadow_copy2.c @@ -265,6 +265,114 @@ static bool make_relative_path(const char *cwd, char *abs_path) return true; } +static bool shadow_copy2_snapshot_to_gmt(vfs_handle_struct *handle, + const char *name, + char *gmt, size_t gmt_len); + +/* + * Check if an incoming filename is already a snapshot converted pathname. + * + * If so, it returns the pathname truncated at the snapshot point which + * will be used as the connectpath. + */ + +static int check_for_converted_path(TALLOC_CTX *mem_ctx, + struct vfs_handle_struct *handle, + struct shadow_copy2_config *config, + char *abs_path, + bool *ppath_already_converted, + char **pconnectpath) +{ + size_t snapdirlen = 0; + char *p = strstr_m(abs_path, config->snapdir); + char *q = NULL; + char *connect_path = NULL; + char snapshot[GMT_NAME_LEN+1]; + + *ppath_already_converted = false; + + if (p == NULL) { + /* Must at least contain shadow:snapdir. */ + return 0; + } + + if (config->snapdir[0] == '/' && + p != abs_path) { + /* Absolute shadow:snapdir must be at the start. */ + return 0; + } + + snapdirlen = strlen(config->snapdir); + if (p[snapdirlen] != '/') { + /* shadow:snapdir must end as a separate component. */ + return 0; + } + + if (p > abs_path && p[-1] != '/') { + /* shadow:snapdir must start as a separate component. */ + return 0; + } + + p += snapdirlen; + p++; /* Move past the / */ + + /* + * Need to return up to the next path + * component after the time. + * This will be used as the connectpath. + */ + q = strchr(p, '/'); + if (q == NULL) { + /* + * No next path component. + * Use entire string. + */ + connect_path = talloc_strdup(mem_ctx, + abs_path); + } else { + connect_path = talloc_strndup(mem_ctx, + abs_path, + q - abs_path); + } + if (connect_path == NULL) { + return ENOMEM; + } + + /* + * Point p at the same offset in connect_path as + * it is in abs_path. + */ + + p = &connect_path[p - abs_path]; + + /* + * Now ensure there is a time string at p. + * The SMB-format @GMT-token string is returned + * in snapshot. + */ + + if (!shadow_copy2_snapshot_to_gmt(handle, + p, + snapshot, + sizeof(snapshot))) { + TALLOC_FREE(connect_path); + return 0; + } + + if (pconnectpath != NULL) { + *pconnectpath = connect_path; + } + + *ppath_already_converted = true; + + DEBUG(10,("path |%s| is already converted. " + "connect path = |%s|\n", + abs_path, + connect_path)); + + return 0; +} + /** * Strip a snapshot component from a filename as * handed in via the smb layer. -- 2.12.0.367.g23dc2f6d3c-goog From eacbaa67116fc6d881c9b9b59c48a41aad97b97e Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Thu, 26 Jan 2017 10:49:51 -0800 Subject: [PATCH 20/37] s3: VFS: shadow_copy2: Fix module to work with variable current working directory. Completely cleans up the horrible shadow_copy2_strip_snapshot() and adds an explaination of what it's actually trying to do. * This function does two things. * * 1). Checks if an incoming filename is already a * snapshot converted pathname. * If so, it returns the pathname truncated * at the snapshot point which will be used * as the connectpath, and then does an early return. * * 2). Checks if an incoming filename contains an * SMB-layer @GMT- style timestamp. * If so, it strips the timestamp, and returns * both the timestamp and the stripped path * (making it cwd-relative). BUG: https://bugzilla.samba.org/show_bug.cgi?id=12531 Signed-off-by: Jeremy Allison Reviewed-by: Uri Simchoni (backported from commit 128d5f27cd42b0c7efcbe3d28fe3eee881e0734b) --- source3/modules/vfs_shadow_copy2.c | 189 ++++++++++++++++++------------------- 1 file changed, 92 insertions(+), 97 deletions(-) diff --git a/source3/modules/vfs_shadow_copy2.c b/source3/modules/vfs_shadow_copy2.c index f1d4619a03d..edf7ba23001 100644 --- a/source3/modules/vfs_shadow_copy2.c +++ b/source3/modules/vfs_shadow_copy2.c @@ -374,10 +374,21 @@ static int check_for_converted_path(TALLOC_CTX *mem_ctx, } /** - * Strip a snapshot component from a filename as - * handed in via the smb layer. - * Returns the parsed timestamp and the stripped filename. + * This function does two things. + * + * 1). Checks if an incoming filename is already a + * snapshot converted pathname. + * If so, it returns the pathname truncated + * at the snapshot point which will be used + * as the connectpath, and then does an early return. + * + * 2). Checks if an incoming filename contains an + * SMB-layer @GMT- style timestamp. + * If so, it strips the timestamp, and returns + * both the timestamp and the stripped path + * (making it cwd-relative). */ + static bool shadow_copy2_strip_snapshot_internal(TALLOC_CTX *mem_ctx, struct vfs_handle_struct *handle, const char *orig_name, @@ -392,62 +403,72 @@ static bool shadow_copy2_strip_snapshot_internal(TALLOC_CTX *mem_ctx, char *stripped = NULL; size_t rest_len, dst_len; struct shadow_copy2_config *config; - const char *snapdir; - ssize_t snapdirlen; ptrdiff_t len_before_gmt; const char *name = orig_name; + char *abs_path = NULL; + bool ret = true; + bool already_converted = false; + int err = 0; SMB_VFS_HANDLE_GET_DATA(handle, config, struct shadow_copy2_config, return false); DEBUG(10, (__location__ ": enter path '%s'\n", name)); + abs_path = make_path_absolute(mem_ctx, config, name); + if (abs_path == NULL) { + ret = false; + goto out; + } + name = abs_path; + + DEBUG(10, (__location__ ": abs path '%s'\n", name)); + + err = check_for_converted_path(mem_ctx, + handle, + config, + abs_path, + &already_converted, + psnappath); + if (err != 0) { + /* error in conversion. */ + ret = false; + goto out; + } + + if (already_converted) { + goto out; + } + + /* + * From here we're only looking to strip an + * SMB-layer @GMT- token. + */ + p = strstr_m(name, "@GMT-"); if (p == NULL) { DEBUG(11, ("@GMT not found\n")); - goto no_snapshot; + goto out; } if ((p > name) && (p[-1] != '/')) { /* the GMT-token does not start a path-component */ DEBUG(10, ("not at start, p=%p, name=%p, p[-1]=%d\n", p, name, (int)p[-1])); - goto no_snapshot; + goto out; } - /* - * Figure out whether we got an already converted string. One - * case where this happens is in a smb2 create call with the - * mxac create blob set. We do the get_acl call on - * fsp->fsp_name, which is already converted. We are converted - * if we got a file name of the form ".snapshots/@GMT-", - * i.e. ".snapshots/" precedes "p". - */ - - snapdir = lp_parm_const_string(SNUM(handle->conn), "shadow", "snapdir", - ".snapshots"); - snapdirlen = strlen(snapdir); len_before_gmt = p - name; - if ((len_before_gmt >= (snapdirlen + 1)) && (p[-1] == '/')) { - const char *parent_snapdir = p - (snapdirlen+1); - - DEBUG(10, ("parent_snapdir = %s\n", parent_snapdir)); - - if (strncmp(parent_snapdir, snapdir, snapdirlen) == 0) { - DEBUG(10, ("name=%s is already converted\n", name)); - goto no_snapshot; - } - } q = strptime(p, GMT_FORMAT, &tm); if (q == NULL) { DEBUG(10, ("strptime failed\n")); - goto no_snapshot; + goto out; } tm.tm_isdst = -1; timestamp = timegm(&tm); if (timestamp == (time_t)-1) { DEBUG(10, ("timestamp==-1\n")); - goto no_snapshot; + goto out; } if (q[0] == '\0') { /* @@ -457,14 +478,33 @@ static bool shadow_copy2_strip_snapshot_internal(TALLOC_CTX *mem_ctx, * with a path prefix. */ if (pstripped != NULL) { + if (len_before_gmt > 0) { + /* + * There is a slash before + * the @GMT-. Remove it. + */ + len_before_gmt -= 1; + } stripped = talloc_strndup(mem_ctx, name, p - name); if (stripped == NULL) { - return false; + ret = false; + goto out; + } + if (orig_name[0] != '/') { + if (make_relative_path(config->shadow_cwd, + stripped) == false) { + DEBUG(10, (__location__ ": path '%s' " + "doesn't start with cwd '%s\n", + stripped, config->shadow_cwd)); + ret = false; + errno = ENOENT; + goto out; + } } *pstripped = stripped; } *ptimestamp = timestamp; - return true; + goto out; } if (q[0] != '/') { /* @@ -472,75 +512,18 @@ static bool shadow_copy2_strip_snapshot_internal(TALLOC_CTX *mem_ctx, * component continues after the gmt-token. */ DEBUG(10, ("q[0] = %d\n", (int)q[0])); - goto no_snapshot; + goto out; } q += 1; rest_len = strlen(q); dst_len = (p-name) + rest_len; - if (config->snapdirseverywhere) { - char *insert; - bool have_insert; - insert = shadow_copy2_insert_string(talloc_tos(), handle, - timestamp); - if (insert == NULL) { - errno = ENOMEM; - return false; - } - - DEBUG(10, (__location__ ": snapdirseverywhere mode.\n" - "path '%s'.\n" - "insert string '%s'\n", name, insert)); - - have_insert = (strstr(name, insert+1) != NULL); - DEBUG(10, ("have_insert=%d, name=%s, insert+1=%s\n", - (int)have_insert, name, insert+1)); - if (have_insert) { - DEBUG(10, (__location__ ": insert string '%s' found in " - "path '%s' found in snapdirseverywhere mode " - "==> already converted\n", insert, name)); - TALLOC_FREE(insert); - goto no_snapshot; - } - TALLOC_FREE(insert); - } else { - char *snapshot_path; - char *s; - - snapshot_path = shadow_copy2_snapshot_path(talloc_tos(), - handle, - timestamp); - if (snapshot_path == NULL) { - errno = ENOMEM; - return false; - } - - DEBUG(10, (__location__ " path: '%s'.\n" - "snapshot path: '%s'\n", name, snapshot_path)); - - s = strstr(name, snapshot_path); - if (s == name) { - /* - * this starts with "snapshot_basepath/GMT-Token" - * so it is already a converted absolute - * path. Don't process further. - */ - DEBUG(10, (__location__ ": path '%s' starts with " - "snapshot path '%s' (not in " - "snapdirseverywhere mode) ==> " - "already converted\n", name, snapshot_path)); - talloc_free(snapshot_path); - goto no_snapshot; - } - talloc_free(snapshot_path); - } - if (pstripped != NULL) { stripped = talloc_array(mem_ctx, char, dst_len+1); if (stripped == NULL) { - errno = ENOMEM; - return false; + ret = false; + goto out; } if (p > name) { memcpy(stripped, name, p-name); @@ -549,13 +532,25 @@ static bool shadow_copy2_strip_snapshot_internal(TALLOC_CTX *mem_ctx, memcpy(stripped + (p-name), q, rest_len); } stripped[dst_len] = '\0'; + if (orig_name[0] != '/') { + if (make_relative_path(config->shadow_cwd, + stripped) == false) { + DEBUG(10, (__location__ ": path '%s' " + "doesn't start with cwd '%s\n", + stripped, config->shadow_cwd)); + ret = false; + errno = ENOENT; + goto out; + } + } *pstripped = stripped; } *ptimestamp = timestamp; - return true; -no_snapshot: - *ptimestamp = 0; - return true; + ret = true; + + out: + TALLOC_FREE(abs_path); + return ret; } static bool shadow_copy2_strip_snapshot(TALLOC_CTX *mem_ctx, -- 2.12.0.367.g23dc2f6d3c-goog From fcbddbc5985f6f7bde467a2c8fcd78953600ff91 Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Mon, 23 Jan 2017 10:06:44 -0800 Subject: [PATCH 21/37] s3: VFS: shadow_copy2: Fix a memory leak in the connectpath function. BUG: https://bugzilla.samba.org/show_bug.cgi?id=12531 Signed-off-by: Jeremy Allison Reviewed-by: Uri Simchoni (backported from commit 4d339a88851f601fae195ac8ff0691cbd3504f41) --- source3/modules/vfs_shadow_copy2.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/source3/modules/vfs_shadow_copy2.c b/source3/modules/vfs_shadow_copy2.c index edf7ba23001..184397fb976 100644 --- a/source3/modules/vfs_shadow_copy2.c +++ b/source3/modules/vfs_shadow_copy2.c @@ -53,6 +53,8 @@ struct shadow_copy2_config { char *shadow_cwd; /* Absolute $cwd path. */ /* Absolute connectpath - can vary depending on $cwd. */ char *shadow_connectpath; + /* malloc'ed realpath return. */ + char *shadow_realpath; }; static bool shadow_copy2_find_slashes(TALLOC_CTX *mem_ctx, const char *str, @@ -2057,6 +2059,13 @@ static const char *shadow_copy2_connectpath(struct vfs_handle_struct *handle, goto done; } + /* + * SMB_VFS_NEXT_REALPATH returns a malloc'ed string. + * Don't leak memory. + */ + SAFE_FREE(config->shadow_realpath); + config->shadow_realpath = result; + DEBUG(10,("connect path is [%s]\n", result)); done: @@ -2103,6 +2112,12 @@ static uint64_t shadow_copy2_disk_free(vfs_handle_struct *handle, return ret; } +static int shadow_copy2_config_destructor(struct shadow_copy2_config *config) +{ + SAFE_FREE(config->shadow_realpath); + return 0; +} + static int shadow_copy2_connect(struct vfs_handle_struct *handle, const char *service, const char *user) { @@ -2130,6 +2145,8 @@ static int shadow_copy2_connect(struct vfs_handle_struct *handle, return -1; } + talloc_set_destructor(config, shadow_copy2_config_destructor); + gmt_format = lp_parm_const_string(SNUM(handle->conn), "shadow", "format", GMT_FORMAT); -- 2.12.0.367.g23dc2f6d3c-goog From 7c15669445d5f8ea750b320684f2f73c915720eb Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Mon, 23 Jan 2017 10:20:13 -0800 Subject: [PATCH 22/37] s3: VFS: shadow_copy2: Fix usage of saved_errno to only set errno on error. Rationale: VFS calls must act like their POSIX equivalents, and the POSIX versions *only* set errno on a failure. There is actually code in the upper smbd layers that depends on errno being correct on a fail return from a VFS call. For a compound VFS module like this, a common pattern is : SMB_VFS_CALL_X() { int ret; syscall1(); ret = syscall2(); syscall3(); return ret; } Where if *any* of the contained syscallX()'s fail, they'll set errno. However, the actual errno we should return is *only* the one returned if syscall2() fails (the others are lstat's checking for existence etc.). So what we should do to correctly return only the errno from syscall2() is: SMB_VFS_CALL_X() { int ret; int saved_errno = 0; syscall1() ret = syscall2(); if (ret == -1) { saved_errno = errno; } syscall3() if (saved_errno != 0) { errno = saved_errno; } return ret; } BUG: https://bugzilla.samba.org/show_bug.cgi?id=12531 Signed-off-by: Jeremy Allison Reviewed-by: Uri Simchoni (backported from commit cda6764f1a8db96182bfd1855440bc6a1ba1abee) --- source3/modules/vfs_shadow_copy2.c | 244 ++++++++++++++++++++++++++----------- 1 file changed, 175 insertions(+), 69 deletions(-) diff --git a/source3/modules/vfs_shadow_copy2.c b/source3/modules/vfs_shadow_copy2.c index 184397fb976..b2cc0b90884 100644 --- a/source3/modules/vfs_shadow_copy2.c +++ b/source3/modules/vfs_shadow_copy2.c @@ -620,7 +620,8 @@ static char *shadow_copy2_do_convert(TALLOC_CTX *mem_ctx, char *insert = NULL; char *converted = NULL; size_t insertlen, connectlen = 0; - int i, saved_errno; + int saved_errno = 0; + int i; size_t min_offset; struct shadow_copy2_config *config; size_t in_share_offset = 0; @@ -803,12 +804,16 @@ static char *shadow_copy2_do_convert(TALLOC_CTX *mem_ctx, errno = ENOENT; } fail: - saved_errno = errno; + if (result == NULL) { + saved_errno = errno; + } TALLOC_FREE(converted); TALLOC_FREE(insert); TALLOC_FREE(slashes); TALLOC_FREE(path); - errno = saved_errno; + if (saved_errno != 0) { + errno = saved_errno; + } return result; } @@ -865,7 +870,7 @@ static DIR *shadow_copy2_opendir(vfs_handle_struct *handle, time_t timestamp = 0; char *stripped = NULL; DIR *ret; - int saved_errno; + int saved_errno = 0; char *conv; if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname, @@ -881,9 +886,13 @@ static DIR *shadow_copy2_opendir(vfs_handle_struct *handle, return NULL; } ret = SMB_VFS_NEXT_OPENDIR(handle, conv, mask, attr); - saved_errno = errno; + if (ret == NULL) { + saved_errno = errno; + } TALLOC_FREE(conv); - errno = saved_errno; + if (saved_errno != 0) { + errno = saved_errno; + } return ret; } @@ -963,7 +972,8 @@ static int shadow_copy2_stat(vfs_handle_struct *handle, time_t timestamp = 0; char *stripped = NULL; char *tmp; - int ret, saved_errno; + int saved_errno = 0; + int ret; if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, smb_fname->base_name, @@ -985,7 +995,9 @@ static int shadow_copy2_stat(vfs_handle_struct *handle, } ret = SMB_VFS_NEXT_STAT(handle, smb_fname); - saved_errno = errno; + if (ret == -1) { + saved_errno = errno; + } TALLOC_FREE(smb_fname->base_name); smb_fname->base_name = tmp; @@ -993,7 +1005,9 @@ static int shadow_copy2_stat(vfs_handle_struct *handle, if (ret == 0) { convert_sbuf(handle, smb_fname->base_name, &smb_fname->st); } - errno = saved_errno; + if (saved_errno != 0) { + errno = saved_errno; + } return ret; } @@ -1003,7 +1017,8 @@ static int shadow_copy2_lstat(vfs_handle_struct *handle, time_t timestamp = 0; char *stripped = NULL; char *tmp; - int ret, saved_errno; + int saved_errno = 0; + int ret; if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, smb_fname->base_name, @@ -1025,7 +1040,9 @@ static int shadow_copy2_lstat(vfs_handle_struct *handle, } ret = SMB_VFS_NEXT_LSTAT(handle, smb_fname); - saved_errno = errno; + if (ret == -1) { + saved_errno = errno; + } TALLOC_FREE(smb_fname->base_name); smb_fname->base_name = tmp; @@ -1033,7 +1050,9 @@ static int shadow_copy2_lstat(vfs_handle_struct *handle, if (ret == 0) { convert_sbuf(handle, smb_fname->base_name, &smb_fname->st); } - errno = saved_errno; + if (saved_errno != 0) { + errno = saved_errno; + } return ret; } @@ -1065,7 +1084,8 @@ static int shadow_copy2_open(vfs_handle_struct *handle, time_t timestamp = 0; char *stripped = NULL; char *tmp; - int ret, saved_errno; + int saved_errno = 0; + int ret; if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, smb_fname->base_name, @@ -1087,12 +1107,16 @@ static int shadow_copy2_open(vfs_handle_struct *handle, } ret = SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode); - saved_errno = errno; + if (ret == -1) { + saved_errno = errno; + } TALLOC_FREE(smb_fname->base_name); smb_fname->base_name = tmp; - errno = saved_errno; + if (saved_errno != 0) { + errno = saved_errno; + } return ret; } @@ -1101,7 +1125,8 @@ static int shadow_copy2_unlink(vfs_handle_struct *handle, { time_t timestamp = 0; char *stripped = NULL; - int ret, saved_errno; + int saved_errno = 0; + int ret; struct smb_filename *conv; if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, @@ -1124,9 +1149,13 @@ static int shadow_copy2_unlink(vfs_handle_struct *handle, return -1; } ret = SMB_VFS_NEXT_UNLINK(handle, conv); - saved_errno = errno; + if (ret == -1) { + saved_errno = errno; + } TALLOC_FREE(conv); - errno = saved_errno; + if (saved_errno != 0) { + errno = saved_errno; + } return ret; } @@ -1135,7 +1164,8 @@ static int shadow_copy2_chmod(vfs_handle_struct *handle, const char *fname, { time_t timestamp = 0; char *stripped = NULL; - int ret, saved_errno; + int saved_errno = 0; + int ret; char *conv; if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname, @@ -1151,9 +1181,13 @@ static int shadow_copy2_chmod(vfs_handle_struct *handle, const char *fname, return -1; } ret = SMB_VFS_NEXT_CHMOD(handle, conv, mode); - saved_errno = errno; + if (ret == -1) { + saved_errno = errno; + } TALLOC_FREE(conv); - errno = saved_errno; + if (saved_errno != 0) { + errno = saved_errno; + } return ret; } @@ -1162,7 +1196,8 @@ static int shadow_copy2_chown(vfs_handle_struct *handle, const char *fname, { time_t timestamp = 0; char *stripped = NULL; - int ret, saved_errno; + int saved_errno = 0; + int ret; char *conv; if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname, @@ -1178,9 +1213,13 @@ static int shadow_copy2_chown(vfs_handle_struct *handle, const char *fname, return -1; } ret = SMB_VFS_NEXT_CHOWN(handle, conv, uid, gid); - saved_errno = errno; + if (ret == -1) { + saved_errno = errno; + } TALLOC_FREE(conv); - errno = saved_errno; + if (saved_errno != 0) { + errno = saved_errno; + } return ret; } @@ -1272,7 +1311,8 @@ static int shadow_copy2_ntimes(vfs_handle_struct *handle, { time_t timestamp = 0; char *stripped = NULL; - int ret, saved_errno; + int saved_errno = 0; + int ret; struct smb_filename *conv; if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, @@ -1295,9 +1335,13 @@ static int shadow_copy2_ntimes(vfs_handle_struct *handle, return -1; } ret = SMB_VFS_NEXT_NTIMES(handle, conv, ft); - saved_errno = errno; + if (ret == -1) { + saved_errno = errno; + } TALLOC_FREE(conv); - errno = saved_errno; + if (saved_errno != 0) { + errno = saved_errno; + } return ret; } @@ -1306,7 +1350,8 @@ static int shadow_copy2_readlink(vfs_handle_struct *handle, { time_t timestamp = 0; char *stripped = NULL; - int ret, saved_errno; + int saved_errno = 0; + int ret; char *conv; if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname, @@ -1322,9 +1367,13 @@ static int shadow_copy2_readlink(vfs_handle_struct *handle, return -1; } ret = SMB_VFS_NEXT_READLINK(handle, conv, buf, bufsiz); - saved_errno = errno; + if (ret == -1) { + saved_errno = errno; + } TALLOC_FREE(conv); - errno = saved_errno; + if (saved_errno != 0) { + errno = saved_errno; + } return ret; } @@ -1333,7 +1382,8 @@ static int shadow_copy2_mknod(vfs_handle_struct *handle, { time_t timestamp = 0; char *stripped = NULL; - int ret, saved_errno; + int saved_errno = 0; + int ret; char *conv; if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname, @@ -1349,9 +1399,13 @@ static int shadow_copy2_mknod(vfs_handle_struct *handle, return -1; } ret = SMB_VFS_NEXT_MKNOD(handle, conv, mode, dev); - saved_errno = errno; + if (ret == -1) { + saved_errno = errno; + } TALLOC_FREE(conv); - errno = saved_errno; + if (saved_errno != 0) { + errno = saved_errno; + } return ret; } @@ -1362,7 +1416,7 @@ static char *shadow_copy2_realpath(vfs_handle_struct *handle, char *stripped = NULL; char *tmp = NULL; char *result = NULL; - int saved_errno; + int saved_errno = 0; if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname, ×tamp, &stripped)) { @@ -1380,10 +1434,14 @@ static char *shadow_copy2_realpath(vfs_handle_struct *handle, result = SMB_VFS_NEXT_REALPATH(handle, tmp); done: - saved_errno = errno; + if (result == NULL) { + saved_errno = errno; + } TALLOC_FREE(tmp); TALLOC_FREE(stripped); - errno = saved_errno; + if (saved_errno != 0) { + errno = saved_errno; + } return result; } @@ -1752,7 +1810,8 @@ static int shadow_copy2_mkdir(vfs_handle_struct *handle, { time_t timestamp = 0; char *stripped = NULL; - int ret, saved_errno; + int ret; + int saved_errno = 0; char *conv; if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname, @@ -1768,9 +1827,13 @@ static int shadow_copy2_mkdir(vfs_handle_struct *handle, return -1; } ret = SMB_VFS_NEXT_MKDIR(handle, conv, mode); - saved_errno = errno; + if (ret == -1) { + saved_errno = errno; + } TALLOC_FREE(conv); - errno = saved_errno; + if (saved_errno != 0) { + errno = saved_errno; + } return ret; } @@ -1778,7 +1841,8 @@ static int shadow_copy2_rmdir(vfs_handle_struct *handle, const char *fname) { time_t timestamp = 0; char *stripped = NULL; - int ret, saved_errno; + int saved_errno = 0; + int ret; char *conv; if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname, @@ -1794,9 +1858,13 @@ static int shadow_copy2_rmdir(vfs_handle_struct *handle, const char *fname) return -1; } ret = SMB_VFS_NEXT_RMDIR(handle, conv); - saved_errno = errno; + if (ret == -1) { + saved_errno = errno; + } TALLOC_FREE(conv); - errno = saved_errno; + if (saved_errno != 0) { + errno = saved_errno; + } return ret; } @@ -1805,7 +1873,8 @@ static int shadow_copy2_chflags(vfs_handle_struct *handle, const char *fname, { time_t timestamp = 0; char *stripped = NULL; - int ret, saved_errno; + int saved_errno = 0; + int ret; char *conv; if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname, @@ -1821,9 +1890,13 @@ static int shadow_copy2_chflags(vfs_handle_struct *handle, const char *fname, return -1; } ret = SMB_VFS_NEXT_CHFLAGS(handle, conv, flags); - saved_errno = errno; + if (ret == -1) { + saved_errno = errno; + } TALLOC_FREE(conv); - errno = saved_errno; + if (saved_errno != 0) { + errno = saved_errno; + } return ret; } @@ -1834,7 +1907,7 @@ static ssize_t shadow_copy2_getxattr(vfs_handle_struct *handle, time_t timestamp = 0; char *stripped = NULL; ssize_t ret; - int saved_errno; + int saved_errno = 0; char *conv; if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname, @@ -1851,9 +1924,13 @@ static ssize_t shadow_copy2_getxattr(vfs_handle_struct *handle, return -1; } ret = SMB_VFS_NEXT_GETXATTR(handle, conv, aname, value, size); - saved_errno = errno; + if (ret == -1) { + saved_errno = errno; + } TALLOC_FREE(conv); - errno = saved_errno; + if (saved_errno != 0) { + errno = saved_errno; + } return ret; } @@ -1864,7 +1941,7 @@ static ssize_t shadow_copy2_listxattr(struct vfs_handle_struct *handle, time_t timestamp = 0; char *stripped = NULL; ssize_t ret; - int saved_errno; + int saved_errno = 0; char *conv; if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname, @@ -1880,9 +1957,13 @@ static ssize_t shadow_copy2_listxattr(struct vfs_handle_struct *handle, return -1; } ret = SMB_VFS_NEXT_LISTXATTR(handle, conv, list, size); - saved_errno = errno; + if (ret == -1) { + saved_errno = errno; + } TALLOC_FREE(conv); - errno = saved_errno; + if (saved_errno != 0) { + errno = saved_errno; + } return ret; } @@ -1891,7 +1972,8 @@ static int shadow_copy2_removexattr(vfs_handle_struct *handle, { time_t timestamp = 0; char *stripped = NULL; - int ret, saved_errno; + int saved_errno = 0; + int ret; char *conv; if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname, @@ -1907,9 +1989,13 @@ static int shadow_copy2_removexattr(vfs_handle_struct *handle, return -1; } ret = SMB_VFS_NEXT_REMOVEXATTR(handle, conv, aname); - saved_errno = errno; + if (ret == -1) { + saved_errno = errno; + } TALLOC_FREE(conv); - errno = saved_errno; + if (saved_errno != 0) { + errno = saved_errno; + } return ret; } @@ -1921,7 +2007,7 @@ static int shadow_copy2_setxattr(struct vfs_handle_struct *handle, time_t timestamp = 0; char *stripped = NULL; ssize_t ret; - int saved_errno; + int saved_errno = 0; char *conv; if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname, @@ -1938,9 +2024,13 @@ static int shadow_copy2_setxattr(struct vfs_handle_struct *handle, return -1; } ret = SMB_VFS_NEXT_SETXATTR(handle, conv, aname, value, size, flags); - saved_errno = errno; + if (ret == -1) { + saved_errno = errno; + } TALLOC_FREE(conv); - errno = saved_errno; + if (saved_errno != 0) { + errno = saved_errno; + } return ret; } @@ -1950,7 +2040,7 @@ static int shadow_copy2_chmod_acl(vfs_handle_struct *handle, time_t timestamp = 0; char *stripped = NULL; ssize_t ret; - int saved_errno; + int saved_errno = 0; char *conv; if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname, @@ -1966,9 +2056,13 @@ static int shadow_copy2_chmod_acl(vfs_handle_struct *handle, return -1; } ret = SMB_VFS_NEXT_CHMOD_ACL(handle, conv, mode); - saved_errno = errno; + if (ret == -1) { + saved_errno = errno; + } TALLOC_FREE(conv); - errno = saved_errno; + if (saved_errno != 0) { + errno = saved_errno; + } return ret; } @@ -1981,7 +2075,7 @@ static int shadow_copy2_get_real_filename(struct vfs_handle_struct *handle, time_t timestamp = 0; char *stripped = NULL; ssize_t ret; - int saved_errno; + int saved_errno = 0; char *conv; DEBUG(10, ("shadow_copy2_get_real_filename called for path=[%s], " @@ -2007,10 +2101,14 @@ static int shadow_copy2_get_real_filename(struct vfs_handle_struct *handle, "name=[%s]\n", conv, name)); ret = SMB_VFS_NEXT_GET_REAL_FILENAME(handle, conv, name, mem_ctx, found_name); + if (ret == -1) { + saved_errno = errno; + } DEBUG(10, ("NEXT_REAL_FILE_NAME returned %d\n", (int)ret)); - saved_errno = errno; TALLOC_FREE(conv); - errno = saved_errno; + if (saved_errno != 0) { + errno = saved_errno; + } return ret; } @@ -2021,7 +2119,7 @@ static const char *shadow_copy2_connectpath(struct vfs_handle_struct *handle, char *stripped = NULL; char *tmp = NULL; char *result = NULL; - int saved_errno; + int saved_errno = 0; size_t rootpath_len = 0; struct shadow_copy2_config *config = NULL; @@ -2069,10 +2167,14 @@ static const char *shadow_copy2_connectpath(struct vfs_handle_struct *handle, DEBUG(10,("connect path is [%s]\n", result)); done: - saved_errno = errno; + if (result == NULL) { + saved_errno = errno; + } TALLOC_FREE(tmp); TALLOC_FREE(stripped); - errno = saved_errno; + if (saved_errno != 0) { + errno = saved_errno; + } return result; } @@ -2084,7 +2186,7 @@ static uint64_t shadow_copy2_disk_free(vfs_handle_struct *handle, time_t timestamp = 0; char *stripped = NULL; ssize_t ret; - int saved_errno; + int saved_errno = 0; char *conv; if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, path, @@ -2105,9 +2207,13 @@ static uint64_t shadow_copy2_disk_free(vfs_handle_struct *handle, ret = SMB_VFS_NEXT_DISK_FREE(handle, conv, small_query, bsize, dfree, dsize); - saved_errno = errno; + if (ret == -1) { + saved_errno = errno; + } TALLOC_FREE(conv); - errno = saved_errno; + if (saved_errno != 0) { + errno = saved_errno; + } return ret; } -- 2.12.0.367.g23dc2f6d3c-goog From b44fe0299641a5880718de49228f9a072dbab23b Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Thu, 26 Jan 2017 17:19:24 -0800 Subject: [PATCH 23/37] s3: VFS: Don't allow symlink, link or rename on already converted paths. Snapshot paths are a read-only filesystem. BUG: https://bugzilla.samba.org/show_bug.cgi?id=12531 Signed-off-by: Jeremy Allison Reviewed-by: Uri Simchoni Autobuild-User(master): Jeremy Allison Autobuild-Date(master): Mon Jan 30 22:26:29 CET 2017 on sn-devel-144 (backported from commit 0e1deb77f2b310ad7e5dd784174207adacf1c981) --- source3/modules/vfs_shadow_copy2.c | 55 +++++++++++++++++++++++++++++--------- 1 file changed, 43 insertions(+), 12 deletions(-) diff --git a/source3/modules/vfs_shadow_copy2.c b/source3/modules/vfs_shadow_copy2.c index b2cc0b90884..c6fffabf68b 100644 --- a/source3/modules/vfs_shadow_copy2.c +++ b/source3/modules/vfs_shadow_copy2.c @@ -902,15 +902,17 @@ static int shadow_copy2_rename(vfs_handle_struct *handle, { time_t timestamp_src = 0; time_t timestamp_dst = 0; + char *snappath_src = NULL; + char *snappath_dst = NULL; - if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, + if (!shadow_copy2_strip_snapshot_internal(talloc_tos(), handle, smb_fname_src->base_name, - ×tamp_src, NULL)) { + ×tamp_src, NULL, &snappath_src)) { return -1; } - if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, + if (!shadow_copy2_strip_snapshot_internal(talloc_tos(), handle, smb_fname_dst->base_name, - ×tamp_dst, NULL)) { + ×tamp_dst, NULL, &snappath_dst)) { return -1; } if (timestamp_src != 0) { @@ -921,6 +923,17 @@ static int shadow_copy2_rename(vfs_handle_struct *handle, errno = EROFS; return -1; } + /* + * Don't allow rename on already converted paths. + */ + if (snappath_src != NULL) { + errno = EXDEV; + return -1; + } + if (snappath_dst != NULL) { + errno = EROFS; + return -1; + } return SMB_VFS_NEXT_RENAME(handle, smb_fname_src, smb_fname_dst); } @@ -929,19 +942,28 @@ static int shadow_copy2_symlink(vfs_handle_struct *handle, { time_t timestamp_old = 0; time_t timestamp_new = 0; + char *snappath_old = NULL; + char *snappath_new = NULL; - if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, oldname, - ×tamp_old, NULL)) { + if (!shadow_copy2_strip_snapshot_internal(talloc_tos(), handle, oldname, + ×tamp_old, NULL, &snappath_old)) { return -1; } - if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, newname, - ×tamp_new, NULL)) { + if (!shadow_copy2_strip_snapshot_internal(talloc_tos(), handle, newname, + ×tamp_new, NULL, &snappath_new)) { return -1; } if ((timestamp_old != 0) || (timestamp_new != 0)) { errno = EROFS; return -1; } + /* + * Don't allow symlinks on already converted paths. + */ + if ((snappath_old != NULL) || (snappath_new != NULL)) { + errno = EROFS; + return -1; + } return SMB_VFS_NEXT_SYMLINK(handle, oldname, newname); } @@ -950,19 +972,28 @@ static int shadow_copy2_link(vfs_handle_struct *handle, { time_t timestamp_old = 0; time_t timestamp_new = 0; + char *snappath_old = NULL; + char *snappath_new = NULL; - if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, oldname, - ×tamp_old, NULL)) { + if (!shadow_copy2_strip_snapshot_internal(talloc_tos(), handle, oldname, + ×tamp_old, NULL, &snappath_old)) { return -1; } - if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, newname, - ×tamp_new, NULL)) { + if (!shadow_copy2_strip_snapshot_internal(talloc_tos(), handle, newname, + ×tamp_new, NULL, &snappath_new)) { return -1; } if ((timestamp_old != 0) || (timestamp_new != 0)) { errno = EROFS; return -1; } + /* + * Don't allow links on already converted paths. + */ + if ((snappath_old != NULL) || (snappath_new != NULL)) { + errno = EROFS; + return -1; + } return SMB_VFS_NEXT_LINK(handle, oldname, newname); } -- 2.12.0.367.g23dc2f6d3c-goog From 9ff0ff2bcf7d45da73f7ad7f7b11e6740626ad75 Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Wed, 1 Feb 2017 11:36:25 -0800 Subject: [PATCH 24/37] s3: VFS: vfs_streams_xattr.c: Make streams_xattr_open() store the same path as streams_xattr_recheck(). MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If the open is changing directories, fsp->fsp_name->base_name will be the full path from the share root, whilst smb_fname will be relative to the $cwd. BUG: https://bugzilla.samba.org/show_bug.cgi?id=12546 Signed-off-by: Jeremy Allison Reviewed-by: Ralph Böhme Autobuild-User(master): Jeremy Allison Autobuild-Date(master): Thu Feb 2 01:55:42 CET 2017 on sn-devel-144 (cherry picked from commit a24ba3e4083200ec9885363efc5769f43183fb6b) Autobuild-User(v4-4-test): Karolin Seeger Autobuild-Date(v4-4-test): Tue Feb 7 13:05:34 CET 2017 on sn-devel-144 --- source3/modules/vfs_streams_xattr.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/source3/modules/vfs_streams_xattr.c b/source3/modules/vfs_streams_xattr.c index 7ef42799819..4f8a9dddd9c 100644 --- a/source3/modules/vfs_streams_xattr.c +++ b/source3/modules/vfs_streams_xattr.c @@ -521,8 +521,15 @@ static int streams_xattr_open(vfs_handle_struct *handle, sio->xattr_name = talloc_strdup(VFS_MEMCTX_FSP_EXTENSION(handle, fsp), xattr_name); + /* + * so->base needs to be a copy of fsp->fsp_name->base_name, + * making it identical to streams_xattr_recheck(). If the + * open is changing directories, fsp->fsp_name->base_name + * will be the full path from the share root, whilst + * smb_fname will be relative to the $cwd. + */ sio->base = talloc_strdup(VFS_MEMCTX_FSP_EXTENSION(handle, fsp), - smb_fname->base_name); + fsp->fsp_name->base_name); sio->fsp_name_ptr = fsp->fsp_name; sio->handle = handle; sio->fsp = fsp; -- 2.12.0.367.g23dc2f6d3c-goog From b9d7739a6d4265f5b869480559a2db0baf18ed7d Mon Sep 17 00:00:00 2001 From: Ralph Boehme Date: Fri, 17 Feb 2017 08:10:53 +0100 Subject: [PATCH 25/37] vfs_streams_xattr: use fsp, not base_fsp The base_fsp's fd is always -1 as it's closed after being openend in create_file_unixpath(). Additionally in streams_xattr_open force using of SMB_VFS_FSETXATTR() by sticking the just created fd into the fsp (and removing it afterwards). Bug: https://bugzilla.samba.org/show_bug.cgi?id=12591 Signed-off-by: Ralph Boehme Reviewed-by: Jeremy Allison Autobuild-User(master): Jeremy Allison Autobuild-Date(master): Wed Feb 22 08:25:46 CET 2017 on sn-devel-144 (cherry picked from commit 021189e32ba507832b5e821e5cda8a2889225955) Autobuild-User(v4-4-test): Stefan Metzmacher Autobuild-Date(v4-4-test): Sat Feb 25 05:08:00 CET 2017 on sn-devel-144 --- source3/modules/vfs_streams_xattr.c | 41 +++++++++++++++---------------------- 1 file changed, 17 insertions(+), 24 deletions(-) diff --git a/source3/modules/vfs_streams_xattr.c b/source3/modules/vfs_streams_xattr.c index 4f8a9dddd9c..20024caa30d 100644 --- a/source3/modules/vfs_streams_xattr.c +++ b/source3/modules/vfs_streams_xattr.c @@ -261,7 +261,7 @@ static int streams_xattr_fstat(vfs_handle_struct *handle, files_struct *fsp, return -1; } - sbuf->st_ex_size = get_xattr_size(handle->conn, fsp->base_fsp, + sbuf->st_ex_size = get_xattr_size(handle->conn, fsp, io->base, io->xattr_name); if (sbuf->st_ex_size == -1) { return -1; @@ -396,6 +396,7 @@ static int streams_xattr_open(vfs_handle_struct *handle, char *xattr_name = NULL; int baseflags; int hostfd = -1; + int ret; DEBUG(10, ("streams_xattr_open called for %s with flags 0x%x\n", smb_fname_str_dbg(smb_fname), flags)); @@ -407,7 +408,6 @@ static int streams_xattr_open(vfs_handle_struct *handle, /* If the default stream is requested, just open the base file. */ if (is_ntfs_default_stream_smb_fname(smb_fname)) { char *tmp_stream_name; - int ret; tmp_stream_name = smb_fname->stream_name; smb_fname->stream_name = NULL; @@ -494,20 +494,13 @@ static int streams_xattr_open(vfs_handle_struct *handle, DEBUG(10, ("creating or truncating attribute %s on file %s\n", xattr_name, smb_fname->base_name)); - if (fsp->base_fsp->fh->fd != -1) { - if (SMB_VFS_FSETXATTR( - fsp->base_fsp, xattr_name, + fsp->fh->fd = hostfd; + ret = SMB_VFS_FSETXATTR(fsp, xattr_name, &null, sizeof(null), - flags & O_EXCL ? XATTR_CREATE : 0) == -1) { - goto fail; - } - } else { - if (SMB_VFS_SETXATTR( - handle->conn, smb_fname->base_name, - xattr_name, &null, sizeof(null), - flags & O_EXCL ? XATTR_CREATE : 0) == -1) { - goto fail; - } + flags & O_EXCL ? XATTR_CREATE : 0); + fsp->fh->fd = -1; + if (ret != 0) { + goto fail; } } @@ -957,7 +950,7 @@ static ssize_t streams_xattr_pwrite(vfs_handle_struct *handle, return -1; } - status = get_ea_value(talloc_tos(), handle->conn, fsp->base_fsp, + status = get_ea_value(talloc_tos(), handle->conn, fsp, sio->base, sio->xattr_name, &ea); if (!NT_STATUS_IS_OK(status)) { return -1; @@ -981,13 +974,13 @@ static ssize_t streams_xattr_pwrite(vfs_handle_struct *handle, memcpy(ea.value.data + offset, data, n); - if (fsp->base_fsp->fh->fd != -1) { - ret = SMB_VFS_FSETXATTR(fsp->base_fsp, + if (fsp->fh->fd != -1) { + ret = SMB_VFS_FSETXATTR(fsp, sio->xattr_name, ea.value.data, ea.value.length, 0); } else { ret = SMB_VFS_SETXATTR(fsp->conn, - fsp->base_fsp->fsp_name->base_name, + fsp->fsp_name->base_name, sio->xattr_name, ea.value.data, ea.value.length, 0); } @@ -1021,7 +1014,7 @@ static ssize_t streams_xattr_pread(vfs_handle_struct *handle, return -1; } - status = get_ea_value(talloc_tos(), handle->conn, fsp->base_fsp, + status = get_ea_value(talloc_tos(), handle->conn, fsp, sio->base, sio->xattr_name, &ea); if (!NT_STATUS_IS_OK(status)) { return -1; @@ -1066,7 +1059,7 @@ static int streams_xattr_ftruncate(struct vfs_handle_struct *handle, return -1; } - status = get_ea_value(talloc_tos(), handle->conn, fsp->base_fsp, + status = get_ea_value(talloc_tos(), handle->conn, fsp, sio->base, sio->xattr_name, &ea); if (!NT_STATUS_IS_OK(status)) { return -1; @@ -1091,13 +1084,13 @@ static int streams_xattr_ftruncate(struct vfs_handle_struct *handle, ea.value.length = offset + 1; ea.value.data[offset] = 0; - if (fsp->base_fsp->fh->fd != -1) { - ret = SMB_VFS_FSETXATTR(fsp->base_fsp, + if (fsp->fh->fd != -1) { + ret = SMB_VFS_FSETXATTR(fsp, sio->xattr_name, ea.value.data, ea.value.length, 0); } else { ret = SMB_VFS_SETXATTR(fsp->conn, - fsp->base_fsp->fsp_name->base_name, + fsp->fsp_name->base_name, sio->xattr_name, ea.value.data, ea.value.length, 0); } -- 2.12.0.367.g23dc2f6d3c-goog From 584f7c7e307573c35d1d71e1f89812403e2edcd3 Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Fri, 21 Oct 2016 11:04:02 -0700 Subject: [PATCH 26/37] s3: vfs: streams_depot. Use conn->connectpath not conn->cwd. conn->cwd can change over the life of the connection, conn->connectpath remains static. BUG: https://bugzilla.samba.org/show_bug.cgi?id=12387 Signed-off-by: Jeremy Allison Reviewed-by: Uri Simchoni Autobuild-User(master): Uri Simchoni Autobuild-Date(master): Mon Oct 24 23:52:48 CEST 2016 on sn-devel-144 --- source3/modules/vfs_streams_depot.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/source3/modules/vfs_streams_depot.c b/source3/modules/vfs_streams_depot.c index 8ba07fc64d6..995d1a7277d 100644 --- a/source3/modules/vfs_streams_depot.c +++ b/source3/modules/vfs_streams_depot.c @@ -128,7 +128,8 @@ static char *stream_dir(vfs_handle_struct *handle, check_valid = lp_parm_bool(SNUM(handle->conn), "streams_depot", "check_valid", true); - tmp = talloc_asprintf(talloc_tos(), "%s/.streams", handle->conn->cwd); + tmp = talloc_asprintf(talloc_tos(), "%s/.streams", + handle->conn->connectpath); if (tmp == NULL) { errno = ENOMEM; -- 2.12.0.367.g23dc2f6d3c-goog From c29a0c927cc40300a1db6671014de112452a5645 Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Mon, 19 Dec 2016 11:55:56 -0800 Subject: [PATCH 27/37] s3: smbd: Create wrapper function for OpenDir in preparation for making robust. CVE-2017-2619 BUG: https://bugzilla.samba.org/show_bug.cgi?id=12496 Signed-off-by: Jeremy Allison --- source3/smbd/dir.c | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/source3/smbd/dir.c b/source3/smbd/dir.c index 55313db80da..fe8f4f5f2f0 100644 --- a/source3/smbd/dir.c +++ b/source3/smbd/dir.c @@ -1582,7 +1582,8 @@ static int smb_Dir_destructor(struct smb_Dir *dirp) Open a directory. ********************************************************************/ -struct smb_Dir *OpenDir(TALLOC_CTX *mem_ctx, connection_struct *conn, +static struct smb_Dir *OpenDir_internal(TALLOC_CTX *mem_ctx, + connection_struct *conn, const char *name, const char *mask, uint32 attr) @@ -1622,6 +1623,18 @@ struct smb_Dir *OpenDir(TALLOC_CTX *mem_ctx, connection_struct *conn, return NULL; } +struct smb_Dir *OpenDir(TALLOC_CTX *mem_ctx, connection_struct *conn, + const char *name, + const char *mask, + uint32_t attr) +{ + return OpenDir_internal(mem_ctx, + conn, + name, + mask, + attr); +} + /******************************************************************* Open a directory from an fsp. ********************************************************************/ -- 2.12.0.367.g23dc2f6d3c-goog From e78196f5d79c289d7e3359dfc0f4451a8660e116 Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Mon, 19 Dec 2016 16:25:26 -0800 Subject: [PATCH 28/37] s3: smbd: Opendir_internal() early return if SMB_VFS_OPENDIR failed. CVE-2017-2619 BUG: https://bugzilla.samba.org/show_bug.cgi?id=12496 Signed-off-by: Jeremy Allison --- source3/smbd/dir.c | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/source3/smbd/dir.c b/source3/smbd/dir.c index fe8f4f5f2f0..d3d4dc6721b 100644 --- a/source3/smbd/dir.c +++ b/source3/smbd/dir.c @@ -1595,20 +1595,12 @@ static struct smb_Dir *OpenDir_internal(TALLOC_CTX *mem_ctx, return NULL; } - dirp->conn = conn; - dirp->name_cache_size = lp_directory_name_cache_size(SNUM(conn)); - dirp->dir_path = talloc_strdup(dirp, name); if (!dirp->dir_path) { errno = ENOMEM; goto fail; } - if (sconn && !sconn->using_smb2) { - sconn->searches.dirhandles_open++; - } - talloc_set_destructor(dirp, smb_Dir_destructor); - dirp->dir = SMB_VFS_OPENDIR(conn, dirp->dir_path, mask, attr); if (!dirp->dir) { DEBUG(5,("OpenDir: Can't open %s. %s\n", dirp->dir_path, @@ -1616,6 +1608,14 @@ static struct smb_Dir *OpenDir_internal(TALLOC_CTX *mem_ctx, goto fail; } + dirp->conn = conn; + dirp->name_cache_size = lp_directory_name_cache_size(SNUM(conn)); + + if (sconn && !sconn->using_smb2) { + sconn->searches.dirhandles_open++; + } + talloc_set_destructor(dirp, smb_Dir_destructor); + return dirp; fail: @@ -1651,6 +1651,15 @@ static struct smb_Dir *OpenDir_fsp(TALLOC_CTX *mem_ctx, connection_struct *conn, return NULL; } + dirp->dir = SMB_VFS_OPENDIR(conn, smb_dname, mask, attr); + + if (!dirp->dir) { + DEBUG(5,("OpenDir: Can't open %s. %s\n", + smb_dname->base_name, + strerror(errno) )); + goto fail; + } + dirp->conn = conn; dirp->name_cache_size = lp_directory_name_cache_size(SNUM(conn)); -- 2.12.0.367.g23dc2f6d3c-goog From 83537564703285d5b6d04ff62e879dde76de665d Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Mon, 19 Dec 2016 16:35:00 -0800 Subject: [PATCH 29/37] s3: smbd: Create and use open_dir_safely(). Use from OpenDir(). Hardens OpenDir against TOC/TOU races. CVE-2017-2619 BUG: https://bugzilla.samba.org/show_bug.cgi?id=12496 Signed-off-by: Jeremy Allison --- source3/smbd/dir.c | 70 +++++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 61 insertions(+), 9 deletions(-) diff --git a/source3/smbd/dir.c b/source3/smbd/dir.c index d3d4dc6721b..e14bf9b9b14 100644 --- a/source3/smbd/dir.c +++ b/source3/smbd/dir.c @@ -1595,15 +1595,9 @@ static struct smb_Dir *OpenDir_internal(TALLOC_CTX *mem_ctx, return NULL; } - dirp->dir_path = talloc_strdup(dirp, name); - if (!dirp->dir_path) { - errno = ENOMEM; - goto fail; - } - - dirp->dir = SMB_VFS_OPENDIR(conn, dirp->dir_path, mask, attr); + dirp->dir = SMB_VFS_OPENDIR(conn, name, mask, attr); if (!dirp->dir) { - DEBUG(5,("OpenDir: Can't open %s. %s\n", dirp->dir_path, + DEBUG(5,("OpenDir: Can't open %s. %s\n", name, strerror(errno) )); goto fail; } @@ -1623,12 +1617,70 @@ static struct smb_Dir *OpenDir_internal(TALLOC_CTX *mem_ctx, return NULL; } +/**************************************************************************** + Open a directory handle by pathname, ensuring it's under the share path. +****************************************************************************/ + +static struct smb_Dir *open_dir_safely(TALLOC_CTX *ctx, + connection_struct *conn, + const char *name, + const char *wcard, + uint32_t attr) +{ + struct smb_Dir *dir_hnd = NULL; + char *saved_dir = vfs_GetWd(ctx, conn); + NTSTATUS status; + + if (saved_dir == NULL) { + return NULL; + } + + if (vfs_ChDir(conn, name) == -1) { + goto out; + } + + /* + * Now the directory is pinned, use + * REALPATH to ensure we can access it. + */ + status = check_name(conn, "."); + if (!NT_STATUS_IS_OK(status)) { + goto out; + } + + dir_hnd = OpenDir_internal(ctx, + conn, + ".", + wcard, + attr); + + if (dir_hnd == NULL) { + goto out; + } + + /* + * OpenDir_internal only gets "." as the dir name. + * Store the real dir name here. + */ + + dir_hnd->dir_path = talloc_strdup(dir_hnd, name); + if (!dir_hnd->dir_path) { + errno = ENOMEM; + } + + out: + + vfs_ChDir(conn, saved_dir); + TALLOC_FREE(saved_dir); + return dir_hnd; +} + struct smb_Dir *OpenDir(TALLOC_CTX *mem_ctx, connection_struct *conn, const char *name, const char *mask, uint32_t attr) { - return OpenDir_internal(mem_ctx, + return open_dir_safely(mem_ctx, conn, name, mask, -- 2.12.0.367.g23dc2f6d3c-goog From ccddf24b5c4a4992f3de71cbb9454addf9a41121 Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Mon, 19 Dec 2016 12:13:20 -0800 Subject: [PATCH 30/37] s3: smbd: OpenDir_fsp() use early returns. CVE-2017-2619 BUG: https://bugzilla.samba.org/show_bug.cgi?id=12496 Signed-off-by: Jeremy Allison --- source3/smbd/dir.c | 35 +++++++++++++++++------------------ 1 file changed, 17 insertions(+), 18 deletions(-) diff --git a/source3/smbd/dir.c b/source3/smbd/dir.c index e14bf9b9b14..2f63342e39a 100644 --- a/source3/smbd/dir.c +++ b/source3/smbd/dir.c @@ -1700,15 +1700,16 @@ static struct smb_Dir *OpenDir_fsp(TALLOC_CTX *mem_ctx, connection_struct *conn, struct smbd_server_connection *sconn = conn->sconn; if (!dirp) { - return NULL; + goto fail; } - dirp->dir = SMB_VFS_OPENDIR(conn, smb_dname, mask, attr); + if (!fsp->is_directory) { + errno = EBADF; + goto fail; + } - if (!dirp->dir) { - DEBUG(5,("OpenDir: Can't open %s. %s\n", - smb_dname->base_name, - strerror(errno) )); + if (fsp->fh->fd == -1) { + errno = EBADF; goto fail; } @@ -1726,18 +1727,16 @@ static struct smb_Dir *OpenDir_fsp(TALLOC_CTX *mem_ctx, connection_struct *conn, } talloc_set_destructor(dirp, smb_Dir_destructor); - if (fsp->is_directory && fsp->fh->fd != -1) { - dirp->dir = SMB_VFS_FDOPENDIR(fsp, mask, attr); - if (dirp->dir != NULL) { - dirp->fsp = fsp; - } else { - DEBUG(10,("OpenDir_fsp: SMB_VFS_FDOPENDIR on %s returned " - "NULL (%s)\n", - dirp->dir_path, - strerror(errno))); - if (errno != ENOSYS) { - return NULL; - } + dirp->dir = SMB_VFS_FDOPENDIR(fsp, mask, attr); + if (dirp->dir != NULL) { + dirp->fsp = fsp; + } else { + DEBUG(10,("OpenDir_fsp: SMB_VFS_FDOPENDIR on %s returned " + "NULL (%s)\n", + dirp->dir_path, + strerror(errno))); + if (errno != ENOSYS) { + return NULL; } } -- 2.12.0.367.g23dc2f6d3c-goog From 1b64bec88c74060b7e15ebd464208fb77eb65f6e Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Mon, 19 Dec 2016 12:15:59 -0800 Subject: [PATCH 31/37] s3: smbd: OpenDir_fsp() - Fix memory leak on error. CVE-2017-2619 BUG: https://bugzilla.samba.org/show_bug.cgi?id=12496 Signed-off-by: Jeremy Allison --- source3/smbd/dir.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source3/smbd/dir.c b/source3/smbd/dir.c index 2f63342e39a..833865b58f9 100644 --- a/source3/smbd/dir.c +++ b/source3/smbd/dir.c @@ -1736,7 +1736,7 @@ static struct smb_Dir *OpenDir_fsp(TALLOC_CTX *mem_ctx, connection_struct *conn, dirp->dir_path, strerror(errno))); if (errno != ENOSYS) { - return NULL; + goto fail; } } -- 2.12.0.367.g23dc2f6d3c-goog From 84ae898fc84e2538e6fafa8f30d89c231f59fe47 Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Mon, 19 Dec 2016 12:32:07 -0800 Subject: [PATCH 32/37] s3: smbd: Move the reference counting and destructor setup to just before retuning success. CVE-2017-2619 BUG: https://bugzilla.samba.org/show_bug.cgi?id=12496 Signed-off-by: Jeremy Allison --- source3/smbd/dir.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/source3/smbd/dir.c b/source3/smbd/dir.c index 833865b58f9..4bab6716af8 100644 --- a/source3/smbd/dir.c +++ b/source3/smbd/dir.c @@ -1722,11 +1722,6 @@ static struct smb_Dir *OpenDir_fsp(TALLOC_CTX *mem_ctx, connection_struct *conn, goto fail; } - if (sconn && !sconn->using_smb2) { - sconn->searches.dirhandles_open++; - } - talloc_set_destructor(dirp, smb_Dir_destructor); - dirp->dir = SMB_VFS_FDOPENDIR(fsp, mask, attr); if (dirp->dir != NULL) { dirp->fsp = fsp; @@ -1751,6 +1746,11 @@ static struct smb_Dir *OpenDir_fsp(TALLOC_CTX *mem_ctx, connection_struct *conn, goto fail; } + if (sconn && !sconn->using_smb2) { + sconn->searches.dirhandles_open++; + } + talloc_set_destructor(dirp, smb_Dir_destructor); + return dirp; fail: -- 2.12.0.367.g23dc2f6d3c-goog From 1d775cf1ffb05ed0f0fcdd5c303bf4075ce612f0 Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Mon, 19 Dec 2016 12:35:32 -0800 Subject: [PATCH 33/37] s3: smbd: Correctly fallback to open_dir_safely if FDOPENDIR not supported on system. CVE-2017-2619 BUG: https://bugzilla.samba.org/show_bug.cgi?id=12496 Signed-off-by: Jeremy Allison --- source3/smbd/dir.c | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/source3/smbd/dir.c b/source3/smbd/dir.c index 4bab6716af8..b8764636334 100644 --- a/source3/smbd/dir.c +++ b/source3/smbd/dir.c @@ -1736,14 +1736,13 @@ static struct smb_Dir *OpenDir_fsp(TALLOC_CTX *mem_ctx, connection_struct *conn, } if (dirp->dir == NULL) { - /* FDOPENDIR didn't work. Use OPENDIR instead. */ - dirp->dir = SMB_VFS_OPENDIR(conn, dirp->dir_path, mask, attr); - } - - if (!dirp->dir) { - DEBUG(5,("OpenDir_fsp: Can't open %s. %s\n", dirp->dir_path, - strerror(errno) )); - goto fail; + /* FDOPENDIR is not supported. Use OPENDIR instead. */ + TALLOC_FREE(dirp); + return open_dir_safely(mem_ctx, + conn, + fsp->fsp_name->base_name, + mask, + attr); } if (sconn && !sconn->using_smb2) { -- 2.12.0.367.g23dc2f6d3c-goog From 9820d02202eae9cf8df4d4d54ff550f483225d1b Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Thu, 15 Dec 2016 12:52:13 -0800 Subject: [PATCH 34/37] s3: smbd: Remove O_NOFOLLOW guards. We insist on O_NOFOLLOW existing. CVE-2017-2619 BUG: https://bugzilla.samba.org/show_bug.cgi?id=12496 Signed-off-by: Jeremy Allison --- source3/smbd/open.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/source3/smbd/open.c b/source3/smbd/open.c index c4489b81391..35a17d32bca 100644 --- a/source3/smbd/open.c +++ b/source3/smbd/open.c @@ -356,8 +356,7 @@ NTSTATUS fd_open(struct connection_struct *conn, struct smb_filename *smb_fname = fsp->fsp_name; NTSTATUS status = NT_STATUS_OK; -#ifdef O_NOFOLLOW - /* + /* * Never follow symlinks on a POSIX client. The * client should be doing this. */ @@ -365,12 +364,10 @@ NTSTATUS fd_open(struct connection_struct *conn, if ((fsp->posix_flags & FSP_POSIX_FLAGS_OPEN) || !lp_follow_symlinks(SNUM(conn))) { flags |= O_NOFOLLOW; } -#endif fsp->fh->fd = SMB_VFS_OPEN(conn, smb_fname, fsp, flags, mode); if (fsp->fh->fd == -1) { int posix_errno = errno; -#ifdef O_NOFOLLOW #if defined(ENOTSUP) && defined(OSF1) /* handle special Tru64 errno */ if (errno == ENOTSUP) { @@ -387,7 +384,6 @@ NTSTATUS fd_open(struct connection_struct *conn, if (errno == EMLINK) { posix_errno = ELOOP; } -#endif /* O_NOFOLLOW */ status = map_nt_error_from_unix(posix_errno); if (errno == EMFILE) { static time_t last_warned = 0L; -- 2.12.0.367.g23dc2f6d3c-goog From ac5bb952a9b01256dec01b64e39a45fedefd4eb2 Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Thu, 15 Dec 2016 12:56:08 -0800 Subject: [PATCH 35/37] s3: smbd: Move special handling of symlink errno's into a utility function. CVE-2017-2619 BUG: https://bugzilla.samba.org/show_bug.cgi?id=12496 Signed-off-by: Jeremy Allison --- source3/smbd/open.c | 43 ++++++++++++++++++++++++++----------------- 1 file changed, 26 insertions(+), 17 deletions(-) diff --git a/source3/smbd/open.c b/source3/smbd/open.c index 35a17d32bca..3aa94846e5d 100644 --- a/source3/smbd/open.c +++ b/source3/smbd/open.c @@ -345,6 +345,31 @@ static NTSTATUS check_base_file_access(struct connection_struct *conn, } /**************************************************************************** + Handle differing symlink errno's +****************************************************************************/ + +static int link_errno_convert(int err) +{ +#if defined(ENOTSUP) && defined(OSF1) + /* handle special Tru64 errno */ + if (err == ENOTSUP) { + err = ELOOP; + } +#endif /* ENOTSUP */ +#ifdef EFTYPE + /* fix broken NetBSD errno */ + if (err == EFTYPE) { + err = ELOOP; + } +#endif /* EFTYPE */ + /* fix broken FreeBSD errno */ + if (err == EMLINK) { + err = ELOOP; + } + return err; +} + +/**************************************************************************** fd support routines - attempt to do a dos_open. ****************************************************************************/ @@ -367,23 +392,7 @@ NTSTATUS fd_open(struct connection_struct *conn, fsp->fh->fd = SMB_VFS_OPEN(conn, smb_fname, fsp, flags, mode); if (fsp->fh->fd == -1) { - int posix_errno = errno; -#if defined(ENOTSUP) && defined(OSF1) - /* handle special Tru64 errno */ - if (errno == ENOTSUP) { - posix_errno = ELOOP; - } -#endif /* ENOTSUP */ -#ifdef EFTYPE - /* fix broken NetBSD errno */ - if (errno == EFTYPE) { - posix_errno = ELOOP; - } -#endif /* EFTYPE */ - /* fix broken FreeBSD errno */ - if (errno == EMLINK) { - posix_errno = ELOOP; - } + int posix_errno = link_errno_convert(errno); status = map_nt_error_from_unix(posix_errno); if (errno == EMFILE) { static time_t last_warned = 0L; -- 2.12.0.367.g23dc2f6d3c-goog From 4a2c899453a3ada50bb59a17ce617798f97e2de6 Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Thu, 15 Dec 2016 13:04:46 -0800 Subject: [PATCH 36/37] s3: smbd: Add the core functions to prevent symlink open races. CVE-2017-2619 BUG: https://bugzilla.samba.org/show_bug.cgi?id=12496 Signed-off-by: Jeremy Allison --- source3/smbd/open.c | 237 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 237 insertions(+) diff --git a/source3/smbd/open.c b/source3/smbd/open.c index 3aa94846e5d..a90bf634358 100644 --- a/source3/smbd/open.c +++ b/source3/smbd/open.c @@ -369,6 +369,243 @@ static int link_errno_convert(int err) return err; } +static int non_widelink_open(struct connection_struct *conn, + const char *conn_rootdir, + files_struct *fsp, + struct smb_filename *smb_fname, + int flags, + mode_t mode, + unsigned int link_depth); + +/**************************************************************************** + Follow a symlink in userspace. +****************************************************************************/ + +static int process_symlink_open(struct connection_struct *conn, + const char *conn_rootdir, + files_struct *fsp, + struct smb_filename *smb_fname, + int flags, + mode_t mode, + unsigned int link_depth) +{ + int fd = -1; + char *link_target = NULL; + int link_len = -1; + char *oldwd = NULL; + size_t rootdir_len = 0; + char *resolved_name = NULL; + bool matched = false; + int saved_errno = 0; + + /* + * Ensure we don't get stuck in a symlink loop. + */ + link_depth++; + if (link_depth >= 20) { + errno = ELOOP; + goto out; + } + + /* Allocate space for the link target. */ + link_target = talloc_array(talloc_tos(), char, PATH_MAX); + if (link_target == NULL) { + errno = ENOMEM; + goto out; + } + + /* Read the link target. */ + link_len = SMB_VFS_READLINK(conn, + smb_fname->base_name, + link_target, + PATH_MAX - 1); + if (link_len == -1) { + goto out; + } + + /* Ensure it's at least null terminated. */ + link_target[link_len] = '\0'; + + /* Convert to an absolute path. */ + resolved_name = SMB_VFS_REALPATH(conn, link_target); + if (resolved_name == NULL) { + goto out; + } + + /* + * We know conn_rootdir starts with '/' and + * does not end in '/'. FIXME ! Should we + * smb_assert this ? + */ + rootdir_len = strlen(conn_rootdir); + + matched = (strncmp(conn_rootdir, resolved_name, rootdir_len) == 0); + if (!matched) { + errno = EACCES; + goto out; + } + + /* + * Turn into a path relative to the share root. + */ + if (resolved_name[rootdir_len] == '\0') { + /* Link to the root of the share. */ + smb_fname->base_name = talloc_strdup(talloc_tos(), "."); + if (smb_fname->base_name == NULL) { + errno = ENOMEM; + goto out; + } + } else if (resolved_name[rootdir_len] == '/') { + smb_fname->base_name = &resolved_name[rootdir_len+1]; + } else { + errno = EACCES; + goto out; + } + + oldwd = vfs_GetWd(talloc_tos(), conn); + if (oldwd == NULL) { + goto out; + } + + /* Ensure we operate from the root of the share. */ + if (vfs_ChDir(conn, conn_rootdir) == -1) { + goto out; + } + + /* And do it all again.. */ + fd = non_widelink_open(conn, + conn_rootdir, + fsp, + smb_fname, + flags, + mode, + link_depth); + if (fd == -1) { + saved_errno = errno; + } + + out: + + SAFE_FREE(resolved_name); + TALLOC_FREE(link_target); + if (oldwd != NULL) { + int ret = vfs_ChDir(conn, oldwd); + if (ret == -1) { + smb_panic("unable to get back to old directory\n"); + } + TALLOC_FREE(oldwd); + } + if (saved_errno != 0) { + errno = saved_errno; + } + return fd; +} + +/**************************************************************************** + Non-widelink open. +****************************************************************************/ + +static int non_widelink_open(struct connection_struct *conn, + const char *conn_rootdir, + files_struct *fsp, + struct smb_filename *smb_fname, + int flags, + mode_t mode, + unsigned int link_depth) +{ + NTSTATUS status; + int fd = -1; + struct smb_filename *smb_fname_rel = NULL; + int saved_errno = 0; + char *oldwd = NULL; + char *parent_dir = NULL; + const char *final_component = NULL; + + if (!parent_dirname(talloc_tos(), + smb_fname->base_name, + &parent_dir, + &final_component)) { + goto out; + } + + oldwd = vfs_GetWd(talloc_tos(), conn); + if (oldwd == NULL) { + goto out; + } + + /* Pin parent directory in place. */ + if (vfs_ChDir(conn, parent_dir) == -1) { + goto out; + } + + /* Ensure the relative path is below the share. */ + status = check_reduced_name(conn, final_component); + if (!NT_STATUS_IS_OK(status)) { + saved_errno = map_errno_from_nt_status(status); + goto out; + } + + smb_fname_rel = synthetic_smb_fname(talloc_tos(), + final_component, + smb_fname->stream_name, + &smb_fname->st); + + flags |= O_NOFOLLOW; + + { + struct smb_filename *tmp_name = fsp->fsp_name; + fsp->fsp_name = smb_fname_rel; + fd = SMB_VFS_OPEN(conn, smb_fname_rel, fsp, flags, mode); + fsp->fsp_name = tmp_name; + } + + if (fd == -1) { + saved_errno = link_errno_convert(errno); + if (saved_errno == ELOOP) { + if (fsp->posix_flags & FSP_POSIX_FLAGS_OPEN) { + /* Never follow symlinks on posix open. */ + goto out; + } + if (!lp_follow_symlinks(SNUM(conn))) { + /* Explicitly no symlinks. */ + goto out; + } + /* + * We have a symlink. Follow in userspace + * to ensure it's under the share definition. + */ + fd = process_symlink_open(conn, + conn_rootdir, + fsp, + smb_fname_rel, + flags, + mode, + link_depth); + if (fd == -1) { + saved_errno = + link_errno_convert(errno); + } + } + } + + out: + + TALLOC_FREE(parent_dir); + TALLOC_FREE(smb_fname_rel); + + if (oldwd != NULL) { + int ret = vfs_ChDir(conn, oldwd); + if (ret == -1) { + smb_panic("unable to get back to old directory\n"); + } + TALLOC_FREE(oldwd); + } + if (saved_errno != 0) { + errno = saved_errno; + } + return fd; +} + /**************************************************************************** fd support routines - attempt to do a dos_open. ****************************************************************************/ -- 2.12.0.367.g23dc2f6d3c-goog From c2f00c8de3b04a7855b2fdc0a9a89091fab8b743 Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Thu, 15 Dec 2016 13:06:31 -0800 Subject: [PATCH 37/37] s3: smbd: Use the new non_widelink_open() function. CVE-2017-2619 BUG: https://bugzilla.samba.org/show_bug.cgi?id=12496 Signed-off-by: Jeremy Allison --- source3/smbd/open.c | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/source3/smbd/open.c b/source3/smbd/open.c index a90bf634358..84177528511 100644 --- a/source3/smbd/open.c +++ b/source3/smbd/open.c @@ -627,7 +627,28 @@ NTSTATUS fd_open(struct connection_struct *conn, flags |= O_NOFOLLOW; } - fsp->fh->fd = SMB_VFS_OPEN(conn, smb_fname, fsp, flags, mode); + /* Ensure path is below share definition. */ + if (!lp_widelinks(SNUM(conn))) { + const char *conn_rootdir = SMB_VFS_CONNECTPATH(conn, + smb_fname->base_name); + if (conn_rootdir == NULL) { + return NT_STATUS_NO_MEMORY; + } + /* + * Only follow symlinks within a share + * definition. + */ + fsp->fh->fd = non_widelink_open(conn, + conn_rootdir, + fsp, + smb_fname, + flags, + mode, + 0); + } else { + fsp->fh->fd = SMB_VFS_OPEN(conn, smb_fname, fsp, flags, mode); + } + if (fsp->fh->fd == -1) { int posix_errno = link_errno_convert(errno); status = map_nt_error_from_unix(posix_errno); -- 2.12.0.367.g23dc2f6d3c-goog