From: Volker Lendecke Date: Wed, 3 Jul 2024 13:55:46 +0000 (+0200) Subject: libcli: New routine symlink_target_path for [MS-SMB2] 2.2.2.2.1.1 X-Git-Tag: tdb-1.4.11~192 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=b91e257f565797849d384cce8f8a200755f71700;p=thirdparty%2Fsamba.git libcli: New routine symlink_target_path for [MS-SMB2] 2.2.2.2.1.1 Right now the only user is the user-space symlink following in smbd. We will use it in libsmb as well to correctly handle STOPPED_ON_SYMLINK. When trying to upstream that code I found the previous_slash function incredibly hard to understand. This new routine makes copy of "const char *_name_in", so that we can replace previous_slash with a simple strrchr_m. If that's too slow (which I doubt, this is "only" chasing symlinks) we can always do something smarter again. Signed-off-by: Volker Lendecke Reviewed-by: Andreas Schneider --- diff --git a/libcli/smb/reparse.c b/libcli/smb/reparse.c index 08071ca85d7..ffadf1c35af 100644 --- a/libcli/smb/reparse.c +++ b/libcli/smb/reparse.c @@ -565,3 +565,97 @@ ssize_t reparse_data_buffer_marshall(const struct reparse_data_buffer *src, return ret; } + +/* + * Implement [MS-SMB2] 2.2.2.2.1.1 Handling the Symbolic Link Error Response + */ + +int symlink_target_path(TALLOC_CTX *ctx, + const char *_name_in, + size_t num_unparsed, + const char *substitute, + bool relative, + char separator, + char **_target) +{ + size_t name_in_len = strlen(_name_in); + size_t num_parsed; + char name_in[name_in_len + 1]; + char *unparsed = NULL; + char *syml = NULL; + char *target = NULL; + + if (num_unparsed > name_in_len) { + return EINVAL; + } + num_parsed = name_in_len - num_unparsed; + + /* + * We need to NULL out separators in name_in. Make a copy of + * _name_in, which is a const char *. + */ + memcpy(name_in, _name_in, sizeof(name_in)); + + unparsed = name_in + num_parsed; + + if ((num_unparsed != 0) && (unparsed[0] != separator)) { + /* + * Symlinks in the middle of name_in must end in a separator + */ + return EINVAL; + } + + if (!relative) { + /* + * From [MS-SMB2] 2.2.2.2.1.1: + * + * If the SYMLINK_FLAG_RELATIVE flag is not set in the Flags + * field of the symbolic link error response, the unparsed + * portion of the file name MUST be appended to the substitute + * name to create the new target path name. + */ + target = talloc_asprintf(ctx, "%s%s", substitute, unparsed); + goto done; + } + + /* + * From [MS-SMB2] 2.2.2.2.1.1: + * + * If the SYMLINK_FLAG_RELATIVE flag is set in the Flags field + * of the symbolic link error response, the symbolic link name + * MUST be identified by backing up one path name element from + * the unparsed portion of the path name. The symbolic link + * MUST be replaced with the substitute name to create the new + * target path name. + */ + + { + char symlink_end_char = unparsed[0]; /* '\0' or a separator */ + + unparsed[0] = '\0'; + syml = strrchr_m(name_in, separator); + unparsed[0] = symlink_end_char; + } + + if (syml == NULL) { + /* + * Nothing to back up to, the symlink was the first + * path component. + */ + name_in[0] = '\0'; + } else { + /* + * Make "name_in" up to the symlink usable for asprintf + */ + syml[1] = '\0'; + } + + target = talloc_asprintf(ctx, "%s%s%s", name_in, substitute, unparsed); + +done: + if (target == NULL) { + return ENOMEM; + } + *_target = target; + return 0; +} diff --git a/libcli/smb/reparse.h b/libcli/smb/reparse.h index e4410d974e4..78c55ca3245 100644 --- a/libcli/smb/reparse.h +++ b/libcli/smb/reparse.h @@ -79,4 +79,12 @@ ssize_t reparse_data_buffer_marshall(const struct reparse_data_buffer *src, uint8_t *buf, size_t buflen); +int symlink_target_path(TALLOC_CTX *ctx, + const char *_name_in, + size_t num_unparsed, + const char *substitute, + bool relative, + char separator, + char **_target); + #endif