From 75954f9bb4f69b60964eb06ab185a69684b2afda 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 --- source3/smbd/filename.c | 150 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 150 insertions(+) diff --git a/source3/smbd/filename.c b/source3/smbd/filename.c index a8cfb55b0f0..efe52a04328 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, @@ -356,6 +498,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 ad8dc0dcc9a9f1a4843d86c207a0d901746c463e 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 --- 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 6428479e2fb475861af1b0d6db470fa94d6cfa25 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 --- 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 e5890e99fc020ec3b6df13ec4d72a031c0ef5bd6 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 --- 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 0b5a0ce564b..32fa898bfc4 100755 --- a/source3/selftest/tests.py +++ b/source3/selftest/tests.py @@ -125,6 +125,7 @@ local_tests = [ "LOCAL-MESSAGING-FDPASS2a", "LOCAL-MESSAGING-FDPASS2b", "LOCAL-PTHREADPOOL-TEVENT", + "LOCAL-CANONICALIZE-PATH", "LOCAL-hex_encode_buf", "LOCAL-remove_duplicate_addrs2"] diff --git a/source3/torture/torture.c b/source3/torture/torture.c index 073931e097e..1bd7d6e1393 100644 --- a/source3/torture/torture.c +++ b/source3/torture/torture.c @@ -11020,6 +11020,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; @@ -11256,6 +11299,7 @@ static struct { { "LOCAL-DBWRAP-CTDB", run_local_dbwrap_ctdb, 0 }, { "LOCAL-BENCH-PTHREADPOOL", run_bench_pthreadpool, 0 }, { "LOCAL-PTHREADPOOL-TEVENT", run_pthreadpool_tevent, 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 c6c3f816e9d7042fb15d28d791552ed13c12570c 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 --- 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 3308e9dce97..a0ac9dfa2af 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 35487c01ee11135e7a1ec7c20b89cb5e021502e2 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 --- 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 6a25c87b7f7..ed98dd4f8ae 100644 --- a/source3/modules/vfs_shadow_copy2.c +++ b/source3/modules/vfs_shadow_copy2.c @@ -416,10 +416,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_private *priv; const char *snapdir; @@ -893,8 +893,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; @@ -936,7 +936,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, @@ -962,7 +963,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)) { @@ -982,7 +984,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)) { @@ -1002,8 +1005,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, @@ -1041,8 +1045,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, @@ -1080,7 +1085,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); @@ -1102,8 +1107,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, @@ -1138,8 +1144,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; @@ -1173,7 +1179,7 @@ static int shadow_copy2_chmod(vfs_handle_struct *handle, const struct smb_filename *smb_fname, mode_t mode) { - time_t timestamp; + time_t timestamp = 0; char *stripped = NULL; int ret, saved_errno; char *conv = NULL; @@ -1219,8 +1225,8 @@ static int shadow_copy2_chown(vfs_handle_struct *handle, uid_t uid, gid_t gid) { - time_t timestamp; - char *stripped; + time_t timestamp = 0; + char *stripped = NULL; int ret, saved_errno; char *conv = NULL; struct smb_filename *conv_smb_fname = NULL; @@ -1261,8 +1267,8 @@ static int shadow_copy2_chown(vfs_handle_struct *handle, 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; @@ -1289,8 +1295,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; @@ -1323,8 +1329,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; @@ -1350,8 +1356,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; @@ -1377,7 +1383,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; @@ -1805,8 +1811,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; struct smb_filename *smb_fname = NULL; @@ -1849,8 +1855,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; struct smb_filename *conv_smb_fname = NULL; @@ -1891,8 +1897,8 @@ static int shadow_copy2_mkdir(vfs_handle_struct *handle, const struct smb_filename *smb_fname, mode_t mode) { - time_t timestamp; - char *stripped; + time_t timestamp = 0; + char *stripped = NULL; int ret, saved_errno; char *conv; struct smb_filename *conv_smb_fname = NULL; @@ -1932,8 +1938,8 @@ static int shadow_copy2_mkdir(vfs_handle_struct *handle, static int shadow_copy2_rmdir(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; char *conv; struct smb_filename *conv_smb_fname = NULL; @@ -1973,8 +1979,8 @@ static int shadow_copy2_rmdir(vfs_handle_struct *handle, 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; @@ -2001,8 +2007,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; @@ -2031,8 +2037,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; @@ -2059,8 +2065,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; @@ -2088,8 +2094,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; @@ -2118,8 +2124,8 @@ static int shadow_copy2_chmod_acl(vfs_handle_struct *handle, const struct smb_filename *smb_fname, mode_t mode) { - time_t timestamp; - char *stripped; + time_t timestamp = 0; + char *stripped = NULL; ssize_t ret; int saved_errno; char *conv = NULL; @@ -2164,8 +2170,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; @@ -2203,7 +2209,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; @@ -2278,8 +2284,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; @@ -2312,8 +2318,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 95c7832eb6b50dd71dad20fe2f724518eceaaa6b 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 --- 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 ed98dd4f8ae..41d2a1fc875 100644 --- a/source3/modules/vfs_shadow_copy2.c +++ b/source3/modules/vfs_shadow_copy2.c @@ -2626,6 +2626,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 1319445859230ddcc8f275b4bb673457ba4e9dda 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 --- 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 41d2a1fc875..6896a163530 100644 --- a/source3/modules/vfs_shadow_copy2.c +++ b/source3/modules/vfs_shadow_copy2.c @@ -2588,7 +2588,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 a51932d8b6c7fd3f85c03c8534312d5c0075eba2 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 private data. Not yet used. BUG: https://bugzilla.samba.org/show_bug.cgi?id=12531 Signed-off-by: Jeremy Allison --- 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 6896a163530..df49a705079 100644 --- a/source3/modules/vfs_shadow_copy2.c +++ b/source3/modules/vfs_shadow_copy2.c @@ -74,6 +74,9 @@ struct shadow_copy2_snaplist_info { struct shadow_copy2_private { struct shadow_copy2_config *config; struct shadow_copy2_snaplist_info *snaps; + char *shadow_cwd; /* Absolute $cwd path. */ + /* Absolute connectpath - can vary depending on $cwd. */ + char *shadow_connectpath; }; static int shadow_copy2_get_shadow_copy_data( -- 2.11.0.483.g087da7b7c-goog From 0463f7e97794849f8904b6f3d06a0b1884ed9085 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 --- 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 df49a705079..2afc2b102bc 100644 --- a/source3/modules/vfs_shadow_copy2.c +++ b/source3/modules/vfs_shadow_copy2.c @@ -412,11 +412,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; @@ -598,6 +599,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 1ac72c24670cf912a863f2232824cee21ac7c74d 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 --- 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 2afc2b102bc..e1e219141eb 100644 --- a/source3/modules/vfs_shadow_copy2.c +++ b/source3/modules/vfs_shadow_copy2.c @@ -414,7 +414,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) @@ -429,6 +429,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, priv, struct shadow_copy2_private, return false); -- 2.11.0.483.g087da7b7c-goog From 64976b2fb0d3ba786efdb93b033f1cb0d87f8e46 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 --- 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 e1e219141eb..d3179de9928 100644 --- a/source3/modules/vfs_shadow_copy2.c +++ b/source3/modules/vfs_shadow_copy2.c @@ -35,6 +35,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; @@ -407,6 +408,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_private *priv, + const char *name) +{ + char *newpath = NULL; + char *abs_path = NULL; + + if (name[0] != '/') { + newpath = talloc_asprintf(mem_ctx, + "%s/%s", + priv->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 1ad325c49537b13d4fbd7f4a7e29178555e8bace 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 --- 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 d3179de9928..2d1b1e67fdb 100644 --- a/source3/modules/vfs_shadow_copy2.c +++ b/source3/modules/vfs_shadow_copy2.c @@ -1328,30 +1328,85 @@ static int shadow_copy2_chown(vfs_handle_struct *handle, return ret; } +static void store_cwd_data(vfs_handle_struct *handle, + const char *connectpath) +{ + struct shadow_copy2_private *priv = NULL; + char *cwd = NULL; + + SMB_VFS_HANDLE_GET_DATA(handle, priv, struct shadow_copy2_private, + return); + + TALLOC_FREE(priv->shadow_cwd); + cwd = SMB_VFS_NEXT_GETWD(handle); + if (cwd == NULL) { + smb_panic("getwd failed\n"); + } + DBG_DEBUG("shadow cwd = %s\n", cwd); + priv->shadow_cwd = talloc_strdup(priv, cwd); + SAFE_FREE(cwd); + if (priv->shadow_cwd == NULL) { + smb_panic("talloc failed\n"); + } + TALLOC_FREE(priv->shadow_connectpath); + if (connectpath) { + DBG_DEBUG("shadow conectpath = %s\n", connectpath); + priv->shadow_connectpath = talloc_strdup(priv, connectpath); + if (priv->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 ef90fa442cfad66d94aa782d1f0c16dea4530ac5 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 --- 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 2d1b1e67fdb..31f36a533f9 100644 --- a/source3/modules/vfs_shadow_copy2.c +++ b/source3/modules/vfs_shadow_copy2.c @@ -2335,9 +2335,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_private *priv = NULL; + + SMB_VFS_HANDLE_GET_DATA(handle, priv, struct shadow_copy2_private, + return NULL); DBG_DEBUG("Calc connect path for [%s]\n", fname); + if (priv->shadow_connectpath != NULL) { + DBG_DEBUG("cached connect path is [%s]\n", + priv->shadow_connectpath); + return priv->shadow_connectpath; + } + if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname, ×tamp, &stripped)) { goto done; -- 2.11.0.483.g087da7b7c-goog From 0f96a95aa09d2145c60904a7ff1a91484cd2998c 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 --- 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 31f36a533f9..2206edefccc 100644 --- a/source3/modules/vfs_shadow_copy2.c +++ b/source3/modules/vfs_shadow_copy2.c @@ -2533,6 +2533,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 8562cf621e5360c25d5b6f7fe4cd32122c33d1a4 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 --- 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 2206edefccc..d6100ac5bb2 100644 --- a/source3/modules/vfs_shadow_copy2.c +++ b/source3/modules/vfs_shadow_copy2.c @@ -452,6 +452,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_private *priv, + char *abs_path, + bool *ppath_already_converted, + char **pconnectpath) +{ + size_t snapdirlen = 0; + char *p = strstr_m(abs_path, priv->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 (priv->config->snapdir[0] == '/' && + p != abs_path) { + /* Absolute shadow:snapdir must be at the start. */ + return 0; + } + + snapdirlen = strlen(priv->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 c378e450dd3ec8cdfbae689f99bc2b9a003f772a 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 --- source3/modules/vfs_shadow_copy2.c | 182 +++++++++++++++++-------------------- 1 file changed, 85 insertions(+), 97 deletions(-) diff --git a/source3/modules/vfs_shadow_copy2.c b/source3/modules/vfs_shadow_copy2.c index d6100ac5bb2..f116c553ac4 100644 --- a/source3/modules/vfs_shadow_copy2.c +++ b/source3/modules/vfs_shadow_copy2.c @@ -561,10 +561,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, @@ -579,62 +590,72 @@ static bool shadow_copy2_strip_snapshot_internal(TALLOC_CTX *mem_ctx, char *stripped = NULL; size_t rest_len, dst_len; struct shadow_copy2_private *priv; - 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, priv, struct shadow_copy2_private, return false); DEBUG(10, (__location__ ": enter path '%s'\n", name)); + abs_path = make_path_absolute(mem_ctx, priv, 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, + priv, + 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') { /* @@ -654,12 +675,24 @@ static bool shadow_copy2_strip_snapshot_internal(TALLOC_CTX *mem_ctx, stripped = talloc_strndup(mem_ctx, name, len_before_gmt); if (stripped == NULL) { - return false; + ret = false; + goto out; + } + if (orig_name[0] != '/') { + if (make_relative_path(priv->shadow_cwd, + stripped) == false) { + DEBUG(10, (__location__ ": path '%s' " + "doesn't start with cwd '%s\n", + stripped, priv->shadow_cwd)); + ret = false; + errno = ENOENT; + goto out; + } } *pstripped = stripped; } *ptimestamp = timestamp; - return true; + goto out; } if (q[0] != '/') { /* @@ -667,75 +700,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 = len_before_gmt + rest_len; - if (priv->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, len_before_gmt); @@ -744,13 +720,25 @@ static bool shadow_copy2_strip_snapshot_internal(TALLOC_CTX *mem_ctx, memcpy(stripped + len_before_gmt, q, rest_len); } stripped[dst_len] = '\0'; + if (orig_name[0] != '/') { + if (make_relative_path(priv->shadow_cwd, + stripped) == false) { + DEBUG(10, (__location__ ": path '%s' " + "doesn't start with cwd '%s\n", + stripped, priv->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 4075bffaa092f6854640909a1320255cadefbfe5 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 --- 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 f116c553ac4..137dad8bf4f 100644 --- a/source3/modules/vfs_shadow_copy2.c +++ b/source3/modules/vfs_shadow_copy2.c @@ -78,6 +78,8 @@ struct shadow_copy2_private { char *shadow_cwd; /* Absolute $cwd path. */ /* Absolute connectpath - can vary depending on $cwd. */ char *shadow_connectpath; + /* malloc'ed realpath return. */ + char *shadow_realpath; }; static int shadow_copy2_get_shadow_copy_data( @@ -2494,6 +2496,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(priv->shadow_realpath); + priv->shadow_realpath = result; + DBG_DEBUG("connect path is [%s]\n", result); done: @@ -2572,6 +2581,12 @@ static int shadow_copy2_get_quota(vfs_handle_struct *handle, const char *path, return ret; } +static int shadow_copy2_private_destructor(struct shadow_copy2_private *priv) +{ + SAFE_FREE(priv->shadow_realpath); + return 0; +} + static int shadow_copy2_connect(struct vfs_handle_struct *handle, const char *service, const char *user) { @@ -2603,6 +2618,8 @@ static int shadow_copy2_connect(struct vfs_handle_struct *handle, return -1; } + talloc_set_destructor(priv, shadow_copy2_private_destructor); + priv->snaps = talloc_zero(priv, struct shadow_copy2_snaplist_info); if (priv->snaps == NULL) { DBG_ERR("talloc_zero() failed\n"); -- 2.11.0.483.g087da7b7c-goog From bb5c3fa06f54f46de334789de7e49a2aba776b5e 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 --- 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 137dad8bf4f..f2a80f4b456 100644 --- a/source3/modules/vfs_shadow_copy2.c +++ b/source3/modules/vfs_shadow_copy2.c @@ -808,7 +808,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; struct shadow_copy2_private *priv; @@ -994,12 +995,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; } @@ -1058,7 +1063,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; struct smb_filename *conv_smb_fname = NULL; @@ -1087,10 +1092,14 @@ static DIR *shadow_copy2_opendir(vfs_handle_struct *handle, return NULL; } ret = SMB_VFS_NEXT_OPENDIR(handle, conv_smb_fname, mask, attr); - saved_errno = errno; + if (ret == NULL) { + saved_errno = errno; + } TALLOC_FREE(conv); TALLOC_FREE(conv_smb_fname); - errno = saved_errno; + if (saved_errno != 0) { + errno = saved_errno; + } return ret; } @@ -1170,7 +1179,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, @@ -1192,7 +1202,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; @@ -1200,7 +1212,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; } @@ -1210,7 +1224,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, @@ -1232,7 +1247,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; @@ -1240,7 +1257,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; } @@ -1272,7 +1291,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, @@ -1294,12 +1314,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; } @@ -1308,7 +1332,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, @@ -1331,9 +1356,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; } @@ -1343,7 +1372,8 @@ static int shadow_copy2_chmod(vfs_handle_struct *handle, { time_t timestamp = 0; char *stripped = NULL; - int ret, saved_errno; + int saved_errno = 0; + int ret; char *conv = NULL; struct smb_filename *conv_smb_fname; @@ -1375,10 +1405,14 @@ static int shadow_copy2_chmod(vfs_handle_struct *handle, } ret = SMB_VFS_NEXT_CHMOD(handle, conv_smb_fname, mode); - saved_errno = errno; + if (ret == -1) { + saved_errno = errno; + } TALLOC_FREE(conv); TALLOC_FREE(conv_smb_fname); - errno = saved_errno; + if (saved_errno != 0) { + errno = saved_errno; + } return ret; } @@ -1389,7 +1423,8 @@ static int shadow_copy2_chown(vfs_handle_struct *handle, { time_t timestamp = 0; char *stripped = NULL; - int ret, saved_errno; + int saved_errno = 0; + int ret; char *conv = NULL; struct smb_filename *conv_smb_fname = NULL; @@ -1419,10 +1454,14 @@ static int shadow_copy2_chown(vfs_handle_struct *handle, return -1; } ret = SMB_VFS_NEXT_CHOWN(handle, conv_smb_fname, uid, gid); - saved_errno = errno; + if (ret == -1) { + saved_errno = errno; + } TALLOC_FREE(conv); TALLOC_FREE(conv_smb_fname); - errno = saved_errno; + if (saved_errno != 0) { + errno = saved_errno; + } return ret; } @@ -1514,7 +1553,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, @@ -1537,9 +1577,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; } @@ -1548,7 +1592,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, @@ -1564,9 +1609,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; } @@ -1575,7 +1624,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, @@ -1591,9 +1641,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; } @@ -1604,7 +1658,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)) { @@ -1622,10 +1676,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; } @@ -2116,7 +2174,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; struct smb_filename *conv_smb_fname = NULL; @@ -2145,10 +2204,14 @@ static int shadow_copy2_mkdir(vfs_handle_struct *handle, return -1; } ret = SMB_VFS_NEXT_MKDIR(handle, conv_smb_fname, mode); - saved_errno = errno; + if (ret == -1) { + saved_errno = errno; + } TALLOC_FREE(conv); TALLOC_FREE(conv_smb_fname); - errno = saved_errno; + if (saved_errno != 0) { + errno = saved_errno; + } return ret; } @@ -2157,7 +2220,8 @@ static int shadow_copy2_rmdir(vfs_handle_struct *handle, { time_t timestamp = 0; char *stripped = NULL; - int ret, saved_errno; + int saved_errno = 0; + int ret; char *conv; struct smb_filename *conv_smb_fname = NULL; @@ -2186,10 +2250,14 @@ static int shadow_copy2_rmdir(vfs_handle_struct *handle, return -1; } ret = SMB_VFS_NEXT_RMDIR(handle, conv_smb_fname); - saved_errno = errno; + if (ret == -1) { + saved_errno = errno; + } TALLOC_FREE(conv_smb_fname); TALLOC_FREE(conv); - errno = saved_errno; + if (saved_errno != 0) { + errno = saved_errno; + } return ret; } @@ -2198,7 +2266,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, @@ -2214,9 +2283,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; } @@ -2227,7 +2300,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, @@ -2244,9 +2317,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; } @@ -2257,7 +2334,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, @@ -2273,9 +2350,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; } @@ -2284,7 +2365,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, @@ -2300,9 +2382,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; } @@ -2314,7 +2400,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, @@ -2331,9 +2417,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; } @@ -2344,7 +2434,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 = NULL; struct smb_filename *conv_smb_fname = NULL; @@ -2374,10 +2464,14 @@ static int shadow_copy2_chmod_acl(vfs_handle_struct *handle, return -1; } ret = SMB_VFS_NEXT_CHMOD_ACL(handle, conv_smb_fname, mode); - saved_errno = errno; + if (ret == -1) { + saved_errno = errno; + } TALLOC_FREE(conv); TALLOC_FREE(conv_smb_fname); - errno = saved_errno; + if (saved_errno != 0) { + errno = saved_errno; + } return ret; } @@ -2390,7 +2484,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], " @@ -2417,9 +2511,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; } @@ -2431,7 +2529,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_private *priv = NULL; @@ -2506,11 +2604,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; } @@ -2521,7 +2623,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, @@ -2541,9 +2643,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; } @@ -2555,7 +2661,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, @@ -2574,9 +2680,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 697b697882d5453185699869d2e6c3fe4eed2b1e 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 --- 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 f2a80f4b456..e26e553abfd 100644 --- a/source3/modules/vfs_shadow_copy2.c +++ b/source3/modules/vfs_shadow_copy2.c @@ -1109,15 +1109,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) { @@ -1128,6 +1130,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 = EXDEV; + return -1; + } return SMB_VFS_NEXT_RENAME(handle, smb_fname_src, smb_fname_dst); } @@ -1136,19 +1149,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); } @@ -1157,19 +1179,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