Subject: [PATCH 01/20] s3: smbd: Correctly canonicalize any incoming shadow copy path.

From: Jeremy Allison <[email-masked]>
Date: Wed, 11 Jan 2017 16:30:38 -0800

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 <[email-masked]>
Reviewed-by: Uri Simchoni <[email-masked]>
(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 ce40c0a..046ce9c 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.7.4