From e3f5becc2cae4b12667ea2b6701dff9b011aa308 Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Wed, 11 Jan 2017 16:30:38 -0800 Subject: [PATCH 01/20] 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 ce40c0a502f..046ce9c8391 100644 --- a/source3/smbd/filename.c +++ b/source3/smbd/filename.c @@ -220,6 +220,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; + + DBG_DEBUG("|%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 */ + DBG_DEBUG("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; + + DBG_DEBUG("|%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, @@ -354,6 +496,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.11.0.483.g087da7b7c-goog From 6704a8fcfb35c9f7c0910cce08f478168a6b8d9b Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Tue, 17 Jan 2017 11:33:18 -0800 Subject: [PATCH 02/20] 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/lib/util_path.c | 133 ++++++++++++++++++++++++++++++++++++++++++++++++ source3/lib/util_path.h | 1 + 2 files changed, 134 insertions(+) diff --git a/source3/lib/util_path.c b/source3/lib/util_path.c index 509ba5ff727..cbad2e15d48 100644 --- a/source3/lib/util_path.c +++ b/source3/lib/util_path.c @@ -93,3 +93,136 @@ char *cache_path(const char *name) { return xx_path(name, lp_cache_directory()); } + +/** + * @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 ../ 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'; + } + + return destname; +} diff --git a/source3/lib/util_path.h b/source3/lib/util_path.h index 118a4bed524..16e27926084 100644 --- a/source3/lib/util_path.h +++ b/source3/lib/util_path.h @@ -27,5 +27,6 @@ char *lock_path(const char *name); char *state_path(const char *name); char *cache_path(const char *name); +char *canonicalize_absolute_path(TALLOC_CTX *ctx, const char *abs_path); #endif -- 2.11.0.483.g087da7b7c-goog From ea19e0b889f506f5d9b42c5d3c28f7e88a4284a3 Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Thu, 19 Jan 2017 15:18:41 -0800 Subject: [PATCH 03/20] s3: lib: Fix two old, old bugs in set_conn_connectpath(), now in canonicalize_absolute_path(). Canonicalizing a path of /foo/bar/../baz would return /foo/barbaz as moving forward 3 characters would delete the / character. Canonicalizing /foo/.. would end up as '\0'. Test to follow. BUG: https://bugzilla.samba.org/show_bug.cgi?id=12531 Signed-off-by: Jeremy Allison Reviewed-by: Uri Simchoni (backported from commit 82979afc46cc5e466bdd999a94080e7a5df95518) --- source3/lib/util_path.c | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/source3/lib/util_path.c b/source3/lib/util_path.c index cbad2e15d48..6f58a03ae58 100644 --- a/source3/lib/util_path.c +++ b/source3/lib/util_path.c @@ -138,12 +138,8 @@ char *canonicalize_absolute_path(TALLOC_CTX *ctx, const char *abs_path) (s[2] == '/' || s[2] == '\0')) { /* Uh oh - "/../" or "/..\0" ! */ - /* Go past the ../ or .. */ - if (s[2] == '/') { - s += 3; - } else { - s += 2; /* Go past the .. */ - } + /* Go past the .. leaving us on the / or '\0' */ + s += 2; /* If we just added a '/' - delete it */ if ((d > destname) && (*(d-1) == '/')) { @@ -169,6 +165,16 @@ char *canonicalize_absolute_path(TALLOC_CTX *ctx, const char *abs_path) 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. -- 2.11.0.483.g087da7b7c-goog From b1a120ce1569c4fbff7f6f9f524f700af63e0360 Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Thu, 26 Jan 2017 16:08:42 -0800 Subject: [PATCH 04/20] s3: smbtorture: Add new local test LOCAL-CANONICALIZE-PATH Tests new canonicalize_absolute_path() function. BUG: https://bugzilla.samba.org/show_bug.cgi?id=12531 Signed-off-by: Jeremy Allison Reviewed-by: Uri Simchoni (backported from commit a51363309a4330b65e34ae941ec99d180bdbab56) --- source3/selftest/tests.py | 1 + source3/torture/torture.c | 44 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 45 insertions(+) diff --git a/source3/selftest/tests.py b/source3/selftest/tests.py index e389fae9ae4..b3df98c86d6 100755 --- a/source3/selftest/tests.py +++ b/source3/selftest/tests.py @@ -113,6 +113,7 @@ local_tests = [ "LOCAL-MESSAGING-FDPASS2", "LOCAL-MESSAGING-FDPASS2a", "LOCAL-MESSAGING-FDPASS2b", + "LOCAL-CANONICALIZE-PATH", "LOCAL-hex_encode_buf", "LOCAL-sprintf_append", "LOCAL-remove_duplicate_addrs2"] diff --git a/source3/torture/torture.c b/source3/torture/torture.c index cfe33a81b61..0341c85547b 100644 --- a/source3/torture/torture.c +++ b/source3/torture/torture.c @@ -9878,6 +9878,49 @@ static bool run_local_tdb_writer(int dummy) return true; } +static bool run_local_canonicalize_path(int dummy) +{ + const char *src[] = { + "/foo/..", + "/..", + "/foo/bar/../baz", + "/foo/././", + "/../foo", + ".././././", + ".././././../../../boo", + "./..", + NULL + }; + const char *dst[] = { + "/", + "/", + "/foo/baz", + "/foo", + "/foo", + "/", + "/boo", + "/", + NULL + }; + unsigned int i; + + for (i = 0; src[i] != NULL; i++) { + char *d = canonicalize_absolute_path(talloc_tos(), src[i]); + if (d == NULL) { + perror("talloc fail\n"); + return false; + } + if (strcmp(d, dst[i]) != 0) { + d_fprintf(stderr, + "canonicalize missmatch %s -> %s != %s", + src[i], d, dst[i]); + return false; + } + talloc_free(d); + } + return true; +} + static double create_procs(bool (*fn)(int), bool *result) { int i, status; @@ -10108,6 +10151,7 @@ static struct { { "local-tdb-writer", run_local_tdb_writer, 0 }, { "LOCAL-DBWRAP-CTDB", run_local_dbwrap_ctdb, 0 }, { "LOCAL-BENCH-PTHREADPOOL", run_bench_pthreadpool, 0 }, + { "LOCAL-CANONICALIZE-PATH", run_local_canonicalize_path, 0 }, { "qpathinfo-bufsize", run_qpathinfo_bufsize, 0 }, {NULL, NULL, 0}}; -- 2.11.0.483.g087da7b7c-goog From c581e75c692d4c8093a9e9480993de4194688f78 Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Tue, 17 Jan 2017 11:35:52 -0800 Subject: [PATCH 05/20] 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 | 103 ++----------------------------------------------- 1 file changed, 3 insertions(+), 100 deletions(-) diff --git a/source3/smbd/service.c b/source3/smbd/service.c index 0c70250e309..ebcdda5ec12 100644 --- a/source3/smbd/service.c +++ b/source3/smbd/service.c @@ -31,6 +31,7 @@ #include "lib/param/loadparm.h" #include "messages.h" #include "lib/afs/afs_funcs.h" +#include "lib/util_path.h" static bool canonicalize_connect_path(connection_struct *conn) { @@ -47,118 +48,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.11.0.483.g087da7b7c-goog From a4388dc88591f256a2000a06802618a8f7c3ee74 Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Fri, 20 Jan 2017 11:42:39 -0800 Subject: [PATCH 06/20] 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 | 116 +++++++++++++++++++------------------ 1 file changed, 61 insertions(+), 55 deletions(-) diff --git a/source3/modules/vfs_shadow_copy2.c b/source3/modules/vfs_shadow_copy2.c index 5750b65ccae..80f544d90b7 100644 --- a/source3/modules/vfs_shadow_copy2.c +++ b/source3/modules/vfs_shadow_copy2.c @@ -228,10 +228,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; @@ -694,8 +694,8 @@ static DIR *shadow_copy2_opendir(vfs_handle_struct *handle, const char *mask, uint32_t attr) { - time_t timestamp; - char *stripped; + time_t timestamp = 0; + char *stripped = NULL; DIR *ret; int saved_errno; char *conv; @@ -723,7 +723,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, @@ -749,7 +750,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)) { @@ -769,7 +771,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)) { @@ -789,8 +792,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, @@ -828,8 +832,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, @@ -867,7 +872,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); @@ -889,8 +894,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, @@ -925,8 +931,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; @@ -959,8 +965,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; @@ -986,8 +992,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; @@ -1013,8 +1019,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; @@ -1041,8 +1047,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; @@ -1075,7 +1081,7 @@ 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; + time_t timestamp = 0; char *stripped; int ret, saved_errno; char *conv; @@ -1102,8 +1108,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; @@ -1129,7 +1135,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; @@ -1462,8 +1468,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; @@ -1494,8 +1500,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; @@ -1521,8 +1527,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; @@ -1547,8 +1553,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; @@ -1574,8 +1580,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; @@ -1602,8 +1608,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; @@ -1632,8 +1638,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; @@ -1660,8 +1666,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; @@ -1689,8 +1695,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; @@ -1718,8 +1724,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; @@ -1749,8 +1755,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; @@ -1788,7 +1794,7 @@ static int shadow_copy2_get_real_filename(struct vfs_handle_struct *handle, static const char *shadow_copy2_connectpath(struct vfs_handle_struct *handle, const char *fname) { - time_t timestamp; + time_t timestamp = 0; char *stripped = NULL; char *tmp = NULL; char *result = NULL; @@ -1863,8 +1869,8 @@ static uint64_t shadow_copy2_disk_free(vfs_handle_struct *handle, const char *path, 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; @@ -1897,8 +1903,8 @@ static int shadow_copy2_get_quota(vfs_handle_struct *handle, const char *path, enum SMB_QUOTA_TYPE qtype, unid_t id, SMB_DISK_QUOTA *dq) { - time_t timestamp; - char *stripped; + time_t timestamp = 0; + char *stripped = NULL; int ret; int saved_errno; char *conv; -- 2.11.0.483.g087da7b7c-goog From 964e382c90baba6bc4328aa2d7c3541f47f59a2d Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Fri, 20 Jan 2017 11:45:54 -0800 Subject: [PATCH 07/20] 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 80f544d90b7..562b4c400c0 100644 --- a/source3/modules/vfs_shadow_copy2.c +++ b/source3/modules/vfs_shadow_copy2.c @@ -2161,6 +2161,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" " mountpoint: '%s'\n" -- 2.11.0.483.g087da7b7c-goog From 015db3c97b368bba51c2cbc4d0db1c66686456e5 Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Fri, 20 Jan 2017 11:48:40 -0800 Subject: [PATCH 08/20] 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 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source3/modules/vfs_shadow_copy2.c b/source3/modules/vfs_shadow_copy2.c index 562b4c400c0..711d8ac1ac0 100644 --- a/source3/modules/vfs_shadow_copy2.c +++ b/source3/modules/vfs_shadow_copy2.c @@ -2123,7 +2123,7 @@ static int shadow_copy2_connect(struct vfs_handle_struct *handle, } if (config->rel_connectpath == NULL && - strlen(basedir) != strlen(handle->conn->connectpath)) { + strlen(basedir) < strlen(handle->conn->connectpath)) { config->rel_connectpath = talloc_strdup(config, handle->conn->connectpath + strlen(basedir)); if (config->rel_connectpath == NULL) { -- 2.11.0.483.g087da7b7c-goog From 47d1697932f3c310936e5c95d10900a4536ff21a Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Fri, 20 Jan 2017 11:50:49 -0800 Subject: [PATCH 09/20] 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 711d8ac1ac0..7355b629e18 100644 --- a/source3/modules/vfs_shadow_copy2.c +++ b/source3/modules/vfs_shadow_copy2.c @@ -48,6 +48,9 @@ struct shadow_copy2_config { char *mount_point; char *rel_connectpath; /* share root, relative to a snapshot root */ 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.11.0.483.g087da7b7c-goog From 95fdc3a2e3f8f3e6bd65540cdb0e657385940bd7 Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Fri, 20 Jan 2017 11:54:56 -0800 Subject: [PATCH 10/20] 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 7355b629e18..a0ada76b597 100644 --- a/source3/modules/vfs_shadow_copy2.c +++ b/source3/modules/vfs_shadow_copy2.c @@ -224,11 +224,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; @@ -402,6 +403,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.11.0.483.g087da7b7c-goog From c0363a1a1647cec779cb1fdc3ef4dad58cce8b0f Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Fri, 20 Jan 2017 11:56:21 -0800 Subject: [PATCH 11/20] 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 a0ada76b597..0a6b10d22d6 100644 --- a/source3/modules/vfs_shadow_copy2.c +++ b/source3/modules/vfs_shadow_copy2.c @@ -226,7 +226,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) @@ -241,6 +241,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.11.0.483.g087da7b7c-goog From e4d2225099f82629697df224668a8152c2017b38 Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Fri, 20 Jan 2017 12:00:08 -0800 Subject: [PATCH 12/20] 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 | 45 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/source3/modules/vfs_shadow_copy2.c b/source3/modules/vfs_shadow_copy2.c index 0a6b10d22d6..7b4502a9eb6 100644 --- a/source3/modules/vfs_shadow_copy2.c +++ b/source3/modules/vfs_shadow_copy2.c @@ -34,6 +34,7 @@ #include "system/filesys.h" #include "include/ntioctl.h" #include "util_tdb.h" +#include "lib/util_path.h" struct shadow_copy2_config { char *gmt_format; @@ -219,6 +220,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.11.0.483.g087da7b7c-goog From 1ef467d2dd2358252381279d186a55e417ef3e7d Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Fri, 20 Jan 2017 12:06:55 -0800 Subject: [PATCH 13/20] 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 | 81 ++++++++++++++++++++++++++++++++------ 1 file changed, 68 insertions(+), 13 deletions(-) diff --git a/source3/modules/vfs_shadow_copy2.c b/source3/modules/vfs_shadow_copy2.c index 7b4502a9eb6..81cd9f45987 100644 --- a/source3/modules/vfs_shadow_copy2.c +++ b/source3/modules/vfs_shadow_copy2.c @@ -1080,30 +1080,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"); + } + DBG_DEBUG("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) { + DBG_DEBUG("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; + char *snappath = NULL; + int ret = -1; + int saved_errno = 0; + char *conv = NULL; + size_t rootpath_len = 0; - if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname, - ×tamp, &stripped)) { + 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.11.0.483.g087da7b7c-goog From 4e4ac5f5e4602e1f7590b3e704d109ab6aef0f06 Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Fri, 20 Jan 2017 12:09:08 -0800 Subject: [PATCH 14/20] 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 81cd9f45987..59cc65644af 100644 --- a/source3/modules/vfs_shadow_copy2.c +++ b/source3/modules/vfs_shadow_copy2.c @@ -1920,9 +1920,19 @@ static const char *shadow_copy2_connectpath(struct vfs_handle_struct *handle, char *parent_dir = 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); DBG_DEBUG("Calc connect path for [%s]\n", fname); + if (config->shadow_connectpath != NULL) { + DBG_DEBUG("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.11.0.483.g087da7b7c-goog From 3ff4c9003cc09327c6731aa5268699204db6584e Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Thu, 26 Jan 2017 10:24:52 -0800 Subject: [PATCH 15/20] 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 59cc65644af..2ef7662ab8d 100644 --- a/source3/modules/vfs_shadow_copy2.c +++ b/source3/modules/vfs_shadow_copy2.c @@ -2099,6 +2099,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.11.0.483.g087da7b7c-goog From c8d58c9fe58a482417c6698e2b81cc794bfb9650 Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Thu, 26 Jan 2017 10:35:50 -0800 Subject: [PATCH 16/20] 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 2ef7662ab8d..3e3aef4deaf 100644 --- a/source3/modules/vfs_shadow_copy2.c +++ b/source3/modules/vfs_shadow_copy2.c @@ -264,6 +264,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; + + DBG_DEBUG("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.11.0.483.g087da7b7c-goog From f79a82f1dd568c2e5c3c849722d55dff07fb41ee Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Thu, 26 Jan 2017 10:49:51 -0800 Subject: [PATCH 17/20] 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 3e3aef4deaf..d213866a425 100644 --- a/source3/modules/vfs_shadow_copy2.c +++ b/source3/modules/vfs_shadow_copy2.c @@ -373,10 +373,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, @@ -391,62 +402,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') { /* @@ -456,14 +477,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] != '/') { /* @@ -471,75 +511,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); @@ -548,13 +531,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.11.0.483.g087da7b7c-goog From 7bdedb8aa9fe0c48ba1dc6e4a85dab285dad1fe7 Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Mon, 23 Jan 2017 10:06:44 -0800 Subject: [PATCH 18/20] 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 d213866a425..df4cebe2858 100644 --- a/source3/modules/vfs_shadow_copy2.c +++ b/source3/modules/vfs_shadow_copy2.c @@ -52,6 +52,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, @@ -2086,6 +2088,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; + DBG_DEBUG("connect path is [%s]\n", result); done: @@ -2164,6 +2173,12 @@ static int shadow_copy2_get_quota(vfs_handle_struct *handle, const char *path, 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) { @@ -2192,6 +2207,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.11.0.483.g087da7b7c-goog From 0a310cf2ce84ca028969bae7ae9c15e63d01db90 Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Mon, 23 Jan 2017 10:20:13 -0800 Subject: [PATCH 19/20] 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 | 254 ++++++++++++++++++++++++++----------- 1 file changed, 182 insertions(+), 72 deletions(-) diff --git a/source3/modules/vfs_shadow_copy2.c b/source3/modules/vfs_shadow_copy2.c index df4cebe2858..8c07fa7fde3 100644 --- a/source3/modules/vfs_shadow_copy2.c +++ b/source3/modules/vfs_shadow_copy2.c @@ -619,7 +619,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; @@ -802,12 +803,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; } @@ -866,7 +871,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, @@ -882,9 +887,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; } @@ -964,7 +973,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, @@ -986,7 +996,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; @@ -994,7 +1006,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; } @@ -1004,7 +1018,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, @@ -1026,7 +1041,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; @@ -1034,7 +1051,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; } @@ -1066,7 +1085,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, @@ -1088,12 +1108,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; } @@ -1102,7 +1126,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, @@ -1125,9 +1150,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; } @@ -1136,7 +1165,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, @@ -1152,9 +1182,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; } @@ -1163,7 +1197,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, @@ -1179,9 +1214,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; } @@ -1273,7 +1312,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, @@ -1296,9 +1336,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; } @@ -1307,7 +1351,8 @@ static int shadow_copy2_readlink(vfs_handle_struct *handle, { time_t timestamp = 0; char *stripped; - int ret, saved_errno; + int saved_errno = 0; + int ret; char *conv; if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname, @@ -1323,9 +1368,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; } @@ -1334,7 +1383,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, @@ -1350,9 +1400,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; } @@ -1363,7 +1417,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)) { @@ -1381,10 +1435,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; } @@ -1753,7 +1811,8 @@ static int shadow_copy2_mkdir(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, @@ -1769,9 +1828,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; } @@ -1779,7 +1842,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, @@ -1795,9 +1859,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; } @@ -1806,7 +1874,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, @@ -1822,9 +1891,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; } @@ -1835,7 +1908,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, @@ -1852,9 +1925,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; } @@ -1865,7 +1942,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, @@ -1881,9 +1958,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; } @@ -1892,7 +1973,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, @@ -1908,9 +1990,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; } @@ -1922,7 +2008,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, @@ -1939,9 +2025,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; } @@ -1951,7 +2041,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, @@ -1967,9 +2057,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; } @@ -1982,7 +2076,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], " @@ -2009,9 +2103,13 @@ static int shadow_copy2_get_real_filename(struct vfs_handle_struct *handle, ret = SMB_VFS_NEXT_GET_REAL_FILENAME(handle, conv, name, mem_ctx, found_name); DEBUG(10, ("NEXT_REAL_FILE_NAME returned %d\n", (int)ret)); - saved_errno = errno; + if (ret == -1) { + saved_errno = errno; + } TALLOC_FREE(conv); - errno = saved_errno; + if (saved_errno != 0) { + errno = saved_errno; + } return ret; } @@ -2023,7 +2121,7 @@ static const char *shadow_copy2_connectpath(struct vfs_handle_struct *handle, char *tmp = NULL; char *result = NULL; char *parent_dir = NULL; - int saved_errno; + int saved_errno = 0; size_t rootpath_len = 0; struct shadow_copy2_config *config = NULL; @@ -2098,11 +2196,15 @@ static const char *shadow_copy2_connectpath(struct vfs_handle_struct *handle, DBG_DEBUG("connect path is [%s]\n", result); done: - saved_errno = errno; + if (result == NULL) { + saved_errno = errno; + } TALLOC_FREE(tmp); TALLOC_FREE(stripped); TALLOC_FREE(parent_dir); - errno = saved_errno; + if (saved_errno != 0) { + errno = saved_errno; + } return result; } @@ -2113,7 +2215,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, @@ -2133,9 +2235,13 @@ static uint64_t shadow_copy2_disk_free(vfs_handle_struct *handle, ret = SMB_VFS_NEXT_DISK_FREE(handle, conv, 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; } @@ -2147,7 +2253,7 @@ static int shadow_copy2_get_quota(vfs_handle_struct *handle, const char *path, time_t timestamp = 0; char *stripped = NULL; int ret; - int saved_errno; + int saved_errno = 0; char *conv; if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, path, ×tamp, @@ -2166,9 +2272,13 @@ static int shadow_copy2_get_quota(vfs_handle_struct *handle, const char *path, ret = SMB_VFS_NEXT_GET_QUOTA(handle, conv, qtype, id, dq); - 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.11.0.483.g087da7b7c-goog From 2e6b0608db5a392a632a0f7b4b861a6dab914c19 Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Thu, 26 Jan 2017 17:19:24 -0800 Subject: [PATCH 20/20] 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 8c07fa7fde3..b7b2935962f 100644 --- a/source3/modules/vfs_shadow_copy2.c +++ b/source3/modules/vfs_shadow_copy2.c @@ -903,15 +903,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) { @@ -922,6 +924,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); } @@ -930,19 +943,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); } @@ -951,19 +973,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.11.0.483.g087da7b7c-goog