The branch, master has been updated via 5ec660160e4 smbclient3: Get all reparse data for allinfo via a0edab50920 libsmb: Retry with OPEN_REPARSE_POINT on IO_REPARSE_TAG_NOT_HANDLED via eb3e9315fc6 libsmb: Factor out cli_get_reparse_data() from cli_readlink() via 8ad55c382ac libsmb: Move symlink_reparse_buffer_parse() to reparse.c via e99e676bd29 libsmb: Some README.Coding for symlink_reparse_buffer_parse() via e71a6ab5dde pylibsmb: Use reparse_data_buffer_parse() via e20919af5b6 libsmb: Use reparse_data_buffer_parse() to get symlink error resp via 2e20e984e5f libsmb: Use reparse_data_buffer_parse() in cli_readlink() via 97ba7b681f3 libcli: Add general reparse point data parsing via 9831fbeb8f0 libcli: Make symlink_reparse_buffer_parse() more flexible via 874c693b581 smbd: Don't crash in cli_fsctl_send() from f348b84fbcf s3:smbd: fix multichannel connection passing race
https://git.samba.org/?p=samba.git;a=shortlog;h=master - Log ----------------------------------------------------------------- commit 5ec660160e414c18a6ea0e61ef9e7c970dc3d7a1 Author: Volker Lendecke <v...@samba.org> Date: Thu Jul 6 17:53:35 2023 +0200 smbclient3: Get all reparse data for allinfo If we hit a reparse point in point, it might be something but a symlink. Signed-off-by: Volker Lendecke <v...@samba.org> Reviewed-by: Jeremy Allison <j...@samba.org> Autobuild-User(master): Stefan Metzmacher <me...@samba.org> Autobuild-Date(master): Thu Aug 10 14:36:40 UTC 2023 on atb-devel-224 commit a0edab509206bb0d4aa3ccd87542181bac486207 Author: Volker Lendecke <v...@samba.org> Date: Wed Jul 5 16:38:32 2023 +0200 libsmb: Retry with OPEN_REPARSE_POINT on IO_REPARSE_TAG_NOT_HANDLED Eventually we'll have to make STOPPED_ON_SYMLINK special to handle the symlink response, but for now they are the same. STOPPED_ON_SYMLINK will tell us where the symlink is, REPARSE_TAG_NOT_HANDLED won't. So if there's an unhandled reparse point somewhere in the path, there's no really good way to handle this. We'll get the REPARSE_TAG_NOT_HANDLED the second time as well. Even SMB1 QPATHINFO gets this when you try to cross a NFS reparse point. Signed-off-by: Volker Lendecke <v...@samba.org> Reviewed-by: Jeremy Allison <j...@samba.org> commit eb3e9315fc6eca6139a89ea25a367aa9d2559565 Author: Volker Lendecke <v...@samba.org> Date: Thu Jul 6 17:34:31 2023 +0200 libsmb: Factor out cli_get_reparse_data() from cli_readlink() Will be used in smbclient's allinfo command: Reparse points are more than just symlinks. Signed-off-by: Volker Lendecke <v...@samba.org> Reviewed-by: Jeremy Allison <j...@samba.org> commit 8ad55c382ac7b76996936adcc73856eaef86b0fb Author: Volker Lendecke <v...@samba.org> Date: Tue Aug 1 15:57:50 2023 +0200 libsmb: Move symlink_reparse_buffer_parse() to reparse.c The goal of this is to eventually remove reparse_symlink.c once we have marshalling routines for symlinks in reparse.c Signed-off-by: Volker Lendecke <v...@samba.org> Reviewed-by: Jeremy Allison <j...@samba.org> commit e99e676bd29950c3c7806d7c8e1a5931ee0640a7 Author: Volker Lendecke <v...@samba.org> Date: Tue Aug 1 15:36:15 2023 +0200 libsmb: Some README.Coding for symlink_reparse_buffer_parse() Signed-off-by: Volker Lendecke <v...@samba.org> Reviewed-by: Jeremy Allison <j...@samba.org> commit e71a6ab5ddef9bdfff85f677a086e4ab1e03b232 Author: Volker Lendecke <v...@samba.org> Date: Tue Aug 1 15:26:49 2023 +0200 pylibsmb: Use reparse_data_buffer_parse() Remove the last direct caller of symlink_reparse_buffer_parse() Signed-off-by: Volker Lendecke <v...@samba.org> Reviewed-by: Jeremy Allison <j...@samba.org> commit e20919af5b65f6e056e1b2b01f58e19cc7f35a33 Author: Volker Lendecke <v...@samba.org> Date: Fri Jul 7 11:55:50 2023 +0200 libsmb: Use reparse_data_buffer_parse() to get symlink error resp Gets a nicer error message Signed-off-by: Volker Lendecke <v...@samba.org> Reviewed-by: Jeremy Allison <j...@samba.org> commit 2e20e984e5fee41e66b03552fdd921fa4fb7ed2e Author: Volker Lendecke <v...@samba.org> Date: Fri Jul 7 11:40:19 2023 +0200 libsmb: Use reparse_data_buffer_parse() in cli_readlink() Gives the chance of better debug higher up (not used yet) Signed-off-by: Volker Lendecke <v...@samba.org> Reviewed-by: Jeremy Allison <j...@samba.org> commit 97ba7b681f38793d59d5753830f0cac942120ed8 Author: Volker Lendecke <v...@samba.org> Date: Thu Jul 6 11:51:07 2023 +0200 libcli: Add general reparse point data parsing When we retrieve reparse point data, we don't know before what we get. Right now all we do is expect a symlink, but we could get other types as well. Signed-off-by: Volker Lendecke <v...@samba.org> Reviewed-by: Jeremy Allison <j...@samba.org> commit 9831fbeb8f08587a36372da653bc78ed2ff0493c Author: Volker Lendecke <v...@samba.org> Date: Thu Jul 6 16:19:06 2023 +0200 libcli: Make symlink_reparse_buffer_parse() more flexible Allow the destination struct to be preallocated Signed-off-by: Volker Lendecke <v...@samba.org> Reviewed-by: Jeremy Allison <j...@samba.org> commit 874c693b5817f7512cf435be498764fbe329e507 Author: Volker Lendecke <v...@samba.org> Date: Wed Jul 5 14:07:11 2023 +0200 smbd: Don't crash in cli_fsctl_send() If you run "allinfo" on a symlink with NT1, cli_readlink_send sends a NULL "in" blob. Do the same as smb2cli_ioctl_send() does, just send NULL/0 in that case and don't crash. Signed-off-by: Volker Lendecke <v...@samba.org> Reviewed-by: Jeremy Allison <j...@samba.org> ----------------------------------------------------------------------- Summary of changes: libcli/smb/py_reparse_symlink.c | 35 ++- libcli/smb/reparse.c | 348 ++++++++++++++++++++++++++++ libcli/smb/{reparse_symlink.h => reparse.h} | 71 +++--- libcli/smb/reparse_symlink.c | 101 +------- libcli/smb/reparse_symlink.h | 9 - libcli/smb/smb2cli_create.c | 30 ++- libcli/smb/wscript | 1 + source3/client/client.c | 39 +++- source3/libsmb/cli_smb2_fnum.c | 3 +- source3/libsmb/clifile.c | 36 ++- source3/libsmb/clisymlink.c | 274 ++++++++++++++++------ source3/libsmb/proto.h | 14 ++ source3/libsmb/pylibsmb.c | 1 + source3/smbd/dir.c | 2 +- source3/smbd/filename.c | 2 +- source3/smbd/files.c | 2 +- 16 files changed, 721 insertions(+), 247 deletions(-) create mode 100644 libcli/smb/reparse.c copy libcli/smb/{reparse_symlink.h => reparse.h} (50%) Changeset truncated at 500 lines: diff --git a/libcli/smb/py_reparse_symlink.c b/libcli/smb/py_reparse_symlink.c index 57dc6032f99..7aecc4fd38a 100644 --- a/libcli/smb/py_reparse_symlink.c +++ b/libcli/smb/py_reparse_symlink.c @@ -20,7 +20,10 @@ #include "replace.h" #include "python/modules.h" #include "python/py3compat.h" +#include "libcli/util/pyerrors.h" +#include "reparse.h" #include "reparse_symlink.h" +#include "smb_constants.h" static PyObject *py_reparse_put(PyObject *module, PyObject *args) { @@ -104,8 +107,10 @@ static PyObject *py_reparse_symlink_get(PyObject *module, PyObject *args) { char *buf = NULL; Py_ssize_t buflen; - struct symlink_reparse_struct *syml = NULL; + struct reparse_data_buffer *syml = NULL; + struct symlink_reparse_struct *lnk = NULL; PyObject *result = NULL; + NTSTATUS status; bool ok; ok = PyArg_ParseTuple(args, PYARG_BYTES_LEN ":get", &buf, &buflen); @@ -113,18 +118,32 @@ static PyObject *py_reparse_symlink_get(PyObject *module, PyObject *args) return NULL; } - syml = symlink_reparse_buffer_parse(NULL, (uint8_t *)buf, buflen); + syml = talloc(NULL, struct reparse_data_buffer); if (syml == NULL) { PyErr_NoMemory(); return NULL; } - result = Py_BuildValue( - "ssII", - syml->substitute_name, - syml->print_name, - (unsigned)syml->unparsed_path_length, - (unsigned)syml->flags); + status = reparse_data_buffer_parse(syml, syml, (uint8_t *)buf, buflen); + if (!NT_STATUS_IS_OK(status)) { + TALLOC_FREE(syml); + PyErr_SetNTSTATUS(status); + return NULL; + } + + if (syml->tag != IO_REPARSE_TAG_SYMLINK) { + TALLOC_FREE(syml); + PyErr_SetNTSTATUS(NT_STATUS_INVALID_NETWORK_RESPONSE); + return NULL; + } + lnk = &syml->parsed.lnk; + + result = Py_BuildValue("ssII", + lnk->substitute_name, + lnk->print_name, + (unsigned)lnk->unparsed_path_length, + (unsigned)lnk->flags); + TALLOC_FREE(syml); return result; } diff --git a/libcli/smb/reparse.c b/libcli/smb/reparse.c new file mode 100644 index 00000000000..61b893123a4 --- /dev/null +++ b/libcli/smb/reparse.c @@ -0,0 +1,348 @@ +/* + * Unix SMB/CIFS implementation. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "replace.h" +#include "libcli/smb/reparse.h" +#include "libcli/smb/smb_constants.h" +#include "libcli/util/error.h" +#include "lib/util/debug.h" +#include "lib/util/bytearray.h" +#include "lib/util/charset/charset.h" +#include "smb_util.h" + +static NTSTATUS reparse_buffer_check(const uint8_t *in_data, + size_t in_len, + uint32_t *reparse_tag, + const uint8_t **_reparse_data, + size_t *_reparse_data_length) +{ + uint16_t reparse_data_length; + + if (in_len == 0) { + DBG_DEBUG("in_len=0\n"); + return NT_STATUS_INVALID_BUFFER_SIZE; + } + if (in_len < 8) { + DBG_DEBUG("in_len=%zu\n", in_len); + return NT_STATUS_IO_REPARSE_DATA_INVALID; + } + + reparse_data_length = PULL_LE_U16(in_data, 4); + + if (reparse_data_length > (in_len - 8)) { + DBG_DEBUG("in_len=%zu, reparse_data_length=%" PRIu16 "\n", + in_len, + reparse_data_length); + return NT_STATUS_IO_REPARSE_DATA_INVALID; + } + + *reparse_tag = PULL_LE_U32(in_data, 0); + *_reparse_data = in_data + 8; + *_reparse_data_length = reparse_data_length; + + return NT_STATUS_OK; +} + +static int nfs_reparse_buffer_parse(TALLOC_CTX *mem_ctx, + struct nfs_reparse_data_buffer *dst, + const uint8_t *src, + size_t srclen) +{ + uint64_t type; + + if (srclen < 8) { + DBG_DEBUG("srclen=%zu too short\n", srclen); + return EINVAL; + } + + type = PULL_LE_U64(src, 0); + + switch (type) { + case NFS_SPECFILE_CHR: + FALL_THROUGH; + case NFS_SPECFILE_BLK: + if (srclen < 16) { + DBG_DEBUG("srclen %zu too short for type %" PRIx64 "\n", + srclen, + type); + return EINVAL; + } + dst->data.dev.major = PULL_LE_U32(src, 8); + dst->data.dev.minor = PULL_LE_U32(src, 12); + break; + case NFS_SPECFILE_LNK: { + bool ok; + + ok = convert_string_talloc(mem_ctx, + CH_UTF16, + CH_UNIX, + src + 8, + srclen - 8, + &dst->data.lnk_target, + NULL); + if (!ok) { + return errno; + } + break; + } + case NFS_SPECFILE_FIFO: + break; /* empty, no data */ + case NFS_SPECFILE_SOCK: + break; /* empty, no data */ + default: + DBG_DEBUG("Unknown NFS reparse type %" PRIx64 "\n", type); + return EINVAL; + } + + dst->type = type; + + return 0; +} + +static int symlink_reparse_buffer_parse(TALLOC_CTX *mem_ctx, + struct symlink_reparse_struct *dst, + const uint8_t *src, + size_t srclen) +{ + uint16_t reparse_data_length; + uint16_t substitute_name_offset, substitute_name_length; + uint16_t print_name_offset, print_name_length; + bool ok; + + if (srclen < 20) { + DBG_DEBUG("srclen = %zu, expected >= 20\n", srclen); + return EINVAL; + } + if (PULL_LE_U32(src, 0) != IO_REPARSE_TAG_SYMLINK) { + DBG_DEBUG("Got ReparseTag %8.8x, expected %8.8x\n", + PULL_LE_U32(src, 0), + IO_REPARSE_TAG_SYMLINK); + return EINVAL; + } + + reparse_data_length = PULL_LE_U16(src, 4); + substitute_name_offset = PULL_LE_U16(src, 8); + substitute_name_length = PULL_LE_U16(src, 10); + print_name_offset = PULL_LE_U16(src, 12); + print_name_length = PULL_LE_U16(src, 14); + + if (reparse_data_length < 12) { + DBG_DEBUG("reparse_data_length = %"PRIu16", expected >= 12\n", + reparse_data_length); + return EINVAL; + } + if (smb_buffer_oob(srclen - 8, reparse_data_length, 0)) { + DBG_DEBUG("reparse_data_length (%"PRIu16") too large for " + "src_len (%zu)\n", + reparse_data_length, + srclen); + return EINVAL; + } + if (smb_buffer_oob(reparse_data_length - 12, substitute_name_offset, + substitute_name_length)) { + DBG_DEBUG("substitute_name (%"PRIu16"/%"PRIu16") does not fit " + "in reparse_data_length (%"PRIu16")\n", + substitute_name_offset, + substitute_name_length, + reparse_data_length - 12); + return EINVAL; + } + if (smb_buffer_oob(reparse_data_length - 12, print_name_offset, + print_name_length)) { + DBG_DEBUG("print_name (%"PRIu16"/%"PRIu16") does not fit in " + "reparse_data_length (%"PRIu16")\n", + print_name_offset, + print_name_length, + reparse_data_length - 12); + return EINVAL; + } + + *dst = (struct symlink_reparse_struct) { + .unparsed_path_length = PULL_LE_U16(src, 6), + .flags = PULL_LE_U32(src, 16), + }; + + ok = convert_string_talloc(mem_ctx, + CH_UTF16, + CH_UNIX, + src + 20 + substitute_name_offset, + substitute_name_length, + &dst->substitute_name, + NULL); + if (!ok) { + int ret = errno; + DBG_DEBUG("convert_string_talloc for substitute_name " + "failed\n"); + return ret; + } + + ok = convert_string_talloc(mem_ctx, + CH_UTF16, + CH_UNIX, + src + 20 + print_name_offset, + print_name_length, + &dst->print_name, + NULL); + if (!ok) { + int ret = errno; + DBG_DEBUG("convert_string_talloc for print_name failed\n"); + TALLOC_FREE(dst->substitute_name); + return ret; + } + + return 0; +} + +NTSTATUS reparse_data_buffer_parse(TALLOC_CTX *mem_ctx, + struct reparse_data_buffer *dst, + const uint8_t *buf, + size_t buflen) +{ + const uint8_t *reparse_data; + size_t reparse_data_length; + NTSTATUS status; + int ret; + + status = reparse_buffer_check(buf, + buflen, + &dst->tag, + &reparse_data, + &reparse_data_length); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + switch (dst->tag) { + case IO_REPARSE_TAG_SYMLINK: + ret = symlink_reparse_buffer_parse(mem_ctx, + &dst->parsed.lnk, + reparse_data, + reparse_data_length); + if (ret != 0) { + return map_nt_error_from_unix_common(ret); + } + break; + case IO_REPARSE_TAG_NFS: + ret = nfs_reparse_buffer_parse(mem_ctx, + &dst->parsed.nfs, + reparse_data, + reparse_data_length); + if (ret != 0) { + return map_nt_error_from_unix_common(ret); + } + break; + default: + dst->parsed.raw.data = talloc_memdup(mem_ctx, + reparse_data, + reparse_data_length); + if (dst->parsed.raw.data == NULL) { + return NT_STATUS_NO_MEMORY; + } + dst->parsed.raw.length = reparse_data_length; + dst->parsed.raw.reserved = PULL_LE_U16(buf, 6); + break; + } + + return NT_STATUS_OK; +} + +char *reparse_data_buffer_str(TALLOC_CTX *mem_ctx, + const struct reparse_data_buffer *dst) +{ + char *s = talloc_strdup(mem_ctx, ""); + + switch (dst->tag) { + case IO_REPARSE_TAG_SYMLINK: { + const struct symlink_reparse_struct *lnk = &dst->parsed.lnk; + talloc_asprintf_addbuf(&s, + "0x%" PRIx32 + " (IO_REPARSE_TAG_SYMLINK)\n", + dst->tag); + talloc_asprintf_addbuf(&s, + "unparsed=%" PRIu16 "\n", + lnk->unparsed_path_length); + talloc_asprintf_addbuf(&s, + "substitute_name=%s\n", + lnk->substitute_name); + talloc_asprintf_addbuf(&s, "print_name=%s\n", lnk->print_name); + talloc_asprintf_addbuf(&s, "flags=%" PRIu32 "\n", lnk->flags); + break; + } + case IO_REPARSE_TAG_NFS: { + const struct nfs_reparse_data_buffer *nfs = &dst->parsed.nfs; + + talloc_asprintf_addbuf(&s, + "0x%" PRIx32 " (IO_REPARSE_TAG_NFS)\n", + dst->tag); + + switch (nfs->type) { + case NFS_SPECFILE_FIFO: + talloc_asprintf_addbuf(&s, + " 0x%" PRIx64 + " (NFS_SPECFILE_FIFO)\n", + nfs->type); + break; + case NFS_SPECFILE_SOCK: + talloc_asprintf_addbuf(&s, + " 0x%" PRIx64 + " (NFS_SPECFILE_SOCK)\n", + nfs->type); + break; + case NFS_SPECFILE_LNK: + talloc_asprintf_addbuf(&s, + " 0x%" PRIx64 + " (NFS_SPECFILE_LNK)\n", + nfs->type); + talloc_asprintf_addbuf(&s, + " -> %s\n ", + nfs->data.lnk_target); + break; + case NFS_SPECFILE_BLK: + talloc_asprintf_addbuf(&s, + " 0x%" PRIx64 + " (NFS_SPECFILE_BLK)\n", + nfs->type); + talloc_asprintf_addbuf(&s, + " %" PRIu32 "/%" PRIu32 "\n", + nfs->data.dev.major, + nfs->data.dev.minor); + break; + case NFS_SPECFILE_CHR: + talloc_asprintf_addbuf(&s, + " 0x%" PRIx64 + " (NFS_SPECFILE_CHR)\n", + nfs->type); + talloc_asprintf_addbuf(&s, + " %" PRIu32 "/%" PRIu32 "\n", + nfs->data.dev.major, + nfs->data.dev.minor); + break; + default: + talloc_asprintf_addbuf(&s, + " 0x%" PRIu64 + " (Unknown type)\n", + nfs->type); + break; + } + break; + } + default: + talloc_asprintf_addbuf(&s, "%" PRIu32 "\n", dst->tag); + break; + } + return s; +} diff --git a/libcli/smb/reparse_symlink.h b/libcli/smb/reparse.h similarity index 50% copy from libcli/smb/reparse_symlink.h copy to libcli/smb/reparse.h index 2f57a592eec..aeeaf116eba 100644 --- a/libcli/smb/reparse_symlink.h +++ b/libcli/smb/reparse.h @@ -1,11 +1,6 @@ /* * Unix SMB/CIFS implementation. * - * Implementation of - * http://msdn.microsoft.com/en-us/library/cc232006%28v=PROT.13%29.aspx - * - * Copyright (C) Volker Lendecke 2011 - * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or @@ -20,12 +15,12 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#ifndef __REPARSE_SYMLINK_H__ -#define __REPARSE_SYMLINK_H__ +#ifndef __UTIL_REPARSE_H__ +#define __UTIL_REPARSE_H__ -#include "replace.h" #include <talloc.h> -#include "lib/util/iov_buf.h" +#include "replace.h" +#include "libcli/util/ntstatus.h" struct symlink_reparse_struct { uint16_t unparsed_path_length; /* reserved for the reparse point */ @@ -34,23 +29,45 @@ struct symlink_reparse_struct { uint32_t flags; }; -ssize_t reparse_buffer_marshall( - uint32_t reparse_tag, - uint16_t reserved, - const struct iovec *iov, - int iovlen, - uint8_t *buf, - size_t buflen); - -bool symlink_reparse_buffer_marshall( - const char *substitute, - const char *printname, - uint16_t unparsed_path_length, - uint32_t flags, - TALLOC_CTX *mem_ctx, - uint8_t **pdst, - size_t *pdstlen); -struct symlink_reparse_struct *symlink_reparse_buffer_parse( - TALLOC_CTX *mem_ctx, const uint8_t *src, size_t srclen); +struct nfs_reparse_data_buffer { + uint64_t type; + + union { + char *lnk_target; /* NFS_SPECFILE_LNK */ + struct { + uint32_t major; + uint32_t minor; + } dev; /* NFS_SPECFILE_[CHR|BLK] */ + + /* NFS_SPECFILE_[FIFO|SOCK] have no data */ + } data; +}; + +struct reparse_data_buffer { + uint32_t tag; + + union { + /* IO_REPARSE_TAG_NFS */ + struct nfs_reparse_data_buffer nfs; + -- Samba Shared Repository