Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package python-smbprotocol for openSUSE:Factory checked in at 2024-11-12 19:21:52 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-smbprotocol (Old) and /work/SRC/openSUSE:Factory/.python-smbprotocol.new.2017 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-smbprotocol" Tue Nov 12 19:21:52 2024 rev:23 rq:1223468 version:1.15.0 Changes: -------- --- /work/SRC/openSUSE:Factory/python-smbprotocol/python-smbprotocol.changes 2024-11-11 14:58:22.195025652 +0100 +++ /work/SRC/openSUSE:Factory/.python-smbprotocol.new.2017/python-smbprotocol.changes 2024-11-12 19:22:43.614748554 +0100 @@ -1,0 +2,10 @@ +Mon Nov 11 22:08:22 UTC 2024 - Martin Hauke <mar...@gmx.de> + +- Update to version 1.15.0 + * Fix Impacket logoff session id check. + * Update session id logic. + * Add smbclient.liststreams to enumerate ADS streams. + * Add some type annotations, most notably to smbclient.open_file + * Add Python 3.13 support. + +------------------------------------------------------------------- Old: ---- python-smbprotocol-1.14.0.tar.gz New: ---- python-smbprotocol-1.15.0.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-smbprotocol.spec ++++++ --- /var/tmp/diff_new_pack.0Jal0i/_old 2024-11-12 19:22:45.054808814 +0100 +++ /var/tmp/diff_new_pack.0Jal0i/_new 2024-11-12 19:22:45.070809483 +0100 @@ -17,13 +17,12 @@ Name: python-smbprotocol -Version: 1.14.0 +Version: 1.15.0 Release: 0 Summary: SMBv2/v3 client for Python 2 and 3 License: MIT Group: Development/Languages/Python URL: https://github.com/jborean93/smbprotocol -#Source: https://files.pythonhosted.org/packages/source/s/smbprotocol/smbprotocol-%%{version}.tar.gz Source: https://github.com/jborean93/smbprotocol/archive/v%{version}.tar.gz#/%{name}-%{version}.tar.gz BuildRequires: %{python_module cryptography >= 2.0} BuildRequires: %{python_module pip} ++++++ python-smbprotocol-1.14.0.tar.gz -> python-smbprotocol-1.15.0.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/smbprotocol-1.14.0/.github/workflows/ci.yml new/smbprotocol-1.15.0/.github/workflows/ci.yml --- old/smbprotocol-1.14.0/.github/workflows/ci.yml 2024-08-25 23:16:35.000000000 +0200 +++ new/smbprotocol-1.15.0/.github/workflows/ci.yml 2024-11-11 22:24:12.000000000 +0100 @@ -60,23 +60,32 @@ - '3.10' - '3.11' - '3.12' + - '3.13' python-arch: - x86 - x64 + - arm64 exclude: + # Exclude OS and arch combinations that are not supported - os: ubuntu-latest python-arch: x86 - - os: macOS-latest + - os: ubuntu-latest + python-arch: arm64 + - os: windows-2019 python-arch: x86 + - os: windows-2019 + python-arch: arm64 + - os: windows-2022 + python-arch: arm64 - os: macOS-latest - python-version: 3.8 + python-arch: x86 - os: macOS-latest - python-version: 3.9 + python-arch: x64 + + # macOS arm64 is supported from 3.9 - os: macOS-latest - python-version: '3.10' - - os: windows-2019 - python-arch: x86 + python-version: 3.8 - os: windows-2019 python-version: 3.8 - os: windows-2019 @@ -84,6 +93,8 @@ - os: windows-2019 python-version: '3.10' - os: windows-2019 + python-version: '3.11' + - os: windows-2019 python-version: '3.12' steps: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/smbprotocol-1.14.0/CHANGELOG.md new/smbprotocol-1.15.0/CHANGELOG.md --- old/smbprotocol-1.14.0/CHANGELOG.md 2024-08-25 23:16:35.000000000 +0200 +++ new/smbprotocol-1.15.0/CHANGELOG.md 2024-11-11 22:24:12.000000000 +0100 @@ -1,5 +1,12 @@ # Changelog +## 1.15.0 - 2024-11-12 + +* Update session id lookup logic to comply with MS-SMB2 spec +* Remove connection from global connection cache even if failing to close it +* Added `smbclient.liststreams` as a way to list the Alternate Data Streams in a file/directory +* Added official support for Python 3.13 + ## 1.14.0 - 2024-08-26 * Dropped support for Python 3.7 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/smbprotocol-1.14.0/README.md new/smbprotocol-1.15.0/README.md --- old/smbprotocol-1.14.0/README.md 2024-08-25 23:16:35.000000000 +0200 +++ new/smbprotocol-1.15.0/README.md 2024-11-11 22:24:12.000000000 +0100 @@ -226,18 +226,12 @@ # Recommend to have virtual environment installed at .venv path. pip install -r requirements-dev.txt pip install -e . - -# you can also run tox by installing tox -pip install tox ``` From there to run the basic tests run; ```bash -py.test -v --cov smbprotocol --cov-report term-missing - -# or with tox for dedicated virtual environments and multiple Python versions. -tox +python -m pytest -v --cov smbprotocol --cov-report term-missing ``` Before sending the code for review, besides making sure all the test pass, @@ -258,7 +252,7 @@ * `SMB_PORT`: The port the SMB server is listening on, default is `445` * `SMB_SHARE`: The name of the share to connect to, a share with this name must exist as well as a share with the name`$SMB_SHARE-encrypted` must also exist that forces encryption -From here running `tox` or `py.test` with these environment variables set will +From here running `pytest` with these environment variables set will activate the integration tests. This requires either Windows 10 or Server 2016 as they support Dialect 3.1.1 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/smbprotocol-1.14.0/pyproject.toml new/smbprotocol-1.15.0/pyproject.toml --- old/smbprotocol-1.14.0/pyproject.toml 2024-08-25 23:16:35.000000000 +0200 +++ new/smbprotocol-1.15.0/pyproject.toml 2024-11-11 22:24:12.000000000 +0100 @@ -6,7 +6,7 @@ [project] name = "smbprotocol" -version = "1.14.0" +version = "1.15.0" description = "Interact with a server using the SMB 2/3 Protocol" readme = "README.md" requires-python = ">=3.8" @@ -23,7 +23,8 @@ "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", - "Programming Language :: Python :: 3.12" + "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: 3.13" ] dependencies = [ "cryptography >= 2.0", @@ -62,26 +63,3 @@ [tool.pytest.ini_options] testpaths = "tests" addopts = "--import-mode=importlib" - -[tool.tox] -legacy_tox_ini = """ -[tox] -envlist = sanity,py38,py39,py310,py311,py312 -skip_missing_interpreters = true -isolated_build = true - -[testenv] -deps = - -r{toxinidir}/requirements-dev.txt - -commands = - python -m pytest -v --cov smbclient --cov smbprotocol --cov-report term-missing - -passenv = - SMB_* - -[testenv:sanity] -commands = - python -m black . --check - python -m isort . --check-only -""" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/smbprotocol-1.14.0/requirements-dev.txt new/smbprotocol-1.15.0/requirements-dev.txt --- old/smbprotocol-1.14.0/requirements-dev.txt 2024-08-25 23:16:35.000000000 +0200 +++ new/smbprotocol-1.15.0/requirements-dev.txt 2024-11-11 22:24:12.000000000 +0100 @@ -7,4 +7,3 @@ pytest pytest-cov pytest-mock -tox diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/smbprotocol-1.14.0/src/smbclient/__init__.py new/smbprotocol-1.15.0/src/smbclient/__init__.py --- old/smbprotocol-1.14.0/src/smbclient/__init__.py 2024-08-25 23:16:35.000000000 +0200 +++ new/smbprotocol-1.15.0/src/smbclient/__init__.py 2024-11-11 22:24:12.000000000 +0100 @@ -14,6 +14,7 @@ getxattr, link, listdir, + liststreams, listxattr, lstat, makedirs, diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/smbprotocol-1.14.0/src/smbclient/_os.py new/smbprotocol-1.15.0/src/smbclient/_os.py --- old/smbprotocol-1.14.0/src/smbclient/_os.py 2024-08-25 23:16:35.000000000 +0200 +++ new/smbprotocol-1.15.0/src/smbclient/_os.py 2024-11-11 22:24:12.000000000 +0100 @@ -1,6 +1,8 @@ # Copyright: (c) 2019, Jordan Borean (@jborean93) <jborea...@gmail.com> # MIT License (see LICENSE or https://opensource.org/licenses/MIT) +from __future__ import annotations + import collections import datetime import errno @@ -37,6 +39,7 @@ FileLinkInformation, FileRenameInformation, FileStandardInformation, + FileStreamInformation, ) from smbprotocol.header import NtStatus from smbprotocol.ioctl import ( @@ -102,6 +105,12 @@ ) +class SMBFileStreamInformation(t.NamedTuple): + name: str + size: int + allocation_size: int + + def is_remote_path(path: str) -> bool: """ Returns True iff the given path is a remote SMB path (rather than a local path). @@ -306,6 +315,140 @@ create_queue.pop(-1) +# Taken from stdlib typeshed but removed the unused 'U' flag +OpenTextModeUpdating: t.TypeAlias = t.Literal[ + "r+", + "+r", + "rt+", + "r+t", + "+rt", + "tr+", + "t+r", + "+tr", + "w+", + "+w", + "wt+", + "w+t", + "+wt", + "tw+", + "t+w", + "+tw", + "a+", + "+a", + "at+", + "a+t", + "+at", + "ta+", + "t+a", + "+ta", + "x+", + "+x", + "xt+", + "x+t", + "+xt", + "tx+", + "t+x", + "+tx", +] +OpenTextModeWriting: t.TypeAlias = t.Literal["w", "wt", "tw", "a", "at", "ta", "x", "xt", "tx"] +OpenTextModeReading: t.TypeAlias = t.Literal["r", "rt", "tr"] +OpenTextMode: t.TypeAlias = t.Literal[OpenTextModeUpdating, OpenTextModeWriting, OpenTextModeReading] +OpenBinaryModeUpdating: t.TypeAlias = t.Literal[ + "rb+", + "r+b", + "+rb", + "br+", + "b+r", + "+br", + "wb+", + "w+b", + "+wb", + "bw+", + "b+w", + "+bw", + "ab+", + "a+b", + "+ab", + "ba+", + "b+a", + "+ba", + "xb+", + "x+b", + "+xb", + "bx+", + "b+x", + "+bx", +] +OpenBinaryModeWriting: t.TypeAlias = t.Literal["wb", "bw", "ab", "ba", "xb", "bx"] +OpenBinaryModeReading: t.TypeAlias = t.Literal["rb", "br"] +OpenBinaryMode: t.TypeAlias = t.Literal[OpenBinaryModeUpdating, OpenBinaryModeReading, OpenBinaryModeWriting] +FileType: t.TypeAlias = t.Literal["file", "dir", "pipe"] + + +# Text mode: always returns a TextIOWrapper +@t.overload +def open_file( + path, + mode: OpenTextMode = "r", + buffering=-1, + file_type: FileType = "file", + encoding=None, + errors=None, + newline=None, + share_access=None, + desired_access=None, + file_attributes=None, + **kwargs, +) -> io.TextIOWrapper[io.BufferedRandom | io.BufferedReader | io.BufferedWriter]: ... + + +# Otherwise return BufferedRandom, BufferedReader, or BufferedWriter +# NOTE: This incorrectly returns unbuffered opens as Buffered types, due to difficulties +# in annotating that case +@t.overload +def open_file( + path, + mode: OpenBinaryModeUpdating, + buffering=-1, + encoding=None, + errors=None, + newline=None, + share_access=None, + desired_access=None, + file_attributes=None, + file_type: FileType = "file", + **kwargs, +) -> io.BufferedRandom: ... +@t.overload +def open_file( + path, + mode: OpenBinaryModeReading, + buffering=-1, + encoding=None, + errors=None, + newline=None, + share_access=None, + desired_access=None, + file_attributes=None, + file_type: FileType = "file", + **kwargs, +) -> io.BufferedReader: ... +@t.overload +def open_file( + path, + mode: OpenBinaryModeWriting, + buffering=-1, + encoding=None, + errors=None, + newline=None, + share_access=None, + desired_access=None, + file_attributes=None, + file_type: FileType = "file", + **kwargs, +) -> io.BufferedWriter: ... + + def open_file( path, mode="r", @@ -316,7 +459,7 @@ share_access=None, desired_access=None, file_attributes=None, - file_type="file", + file_type: t.Literal["file", "dir", "pipe"] = "file", **kwargs, ): """ @@ -1019,6 +1162,43 @@ set_info(transaction, ea_info) +def liststreams(path: str, follow_symlinks=True, **kwargs: t.Any) -> list[SMBFileStreamInformation]: + """ + Return a list of the alternative data streams on a path. Listed streams can + be opened by appending their name to the original path. An example call for + a file with a single extra stream may return: + + ``` + [ + SMBFileStreamInformation(name=':extra_stream:$DATA', size=8, allocation_size=8), + SMBFileStreamInformation(name='::$DATA', size=103472, allocation_size=131072), + ] + ``` + + :param path: The full UNC path to the file to get the list of streams for. + :param follow_symlinks: Whether to follow the symlink at path if encountered. + :param kwargs: Common SMB Session arguments for smbclient. + :return: List of streams on the file with each entry being a string. + """ + + raw = SMBRawIO( + path, + desired_access=FilePipePrinterAccessMask.FILE_READ_ATTRIBUTES, + create_options=0 if follow_symlinks else CreateOptions.FILE_OPEN_REPARSE_POINT, + **kwargs, + ) + + with SMBFileTransaction(raw) as transaction: + query_info(transaction, FileStreamInformation, output_buffer_length=MAX_PAYLOAD_SIZE) + + return [ + SMBFileStreamInformation( + s["stream_name"].get_value(), s["stream_size"].get_value(), s["stream_allocation_size"].get_value() + ) + for s in transaction.results[0] + ] + + def _delete(raw_type, path, **kwargs): # Ensures we delete the symlink (if present) and don't follow it down. co = CreateOptions.FILE_OPEN_REPARSE_POINT diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/smbprotocol-1.14.0/src/smbclient/_pool.py new/smbprotocol-1.15.0/src/smbclient/_pool.py --- old/smbprotocol-1.14.0/src/smbclient/_pool.py 2024-08-25 23:16:35.000000000 +0200 +++ new/smbprotocol-1.15.0/src/smbclient/_pool.py 2024-11-11 22:24:12.000000000 +0100 @@ -7,6 +7,7 @@ import logging import ntpath import uuid +from typing import Literal, Optional from smbprotocol._text import to_text from smbprotocol.connection import Capabilities, Connection @@ -366,14 +367,14 @@ def register_session( - server, - username=None, - password=None, + server: str, + username: Optional[str] = None, + password: Optional[str] = None, port=445, - encrypt=None, + encrypt: Optional[bool] = None, connection_timeout=60, connection_cache=None, - auth_protocol="negotiate", + auth_protocol: Literal["negotiate", "ntlm", "kerberos"] = "negotiate", require_signing=True, ): """ @@ -440,10 +441,10 @@ if connection_cache is None: connection_cache = _SMB_CONNECTIONS - for name, connection in list(connection_cache.items()): + for name in list(connection_cache.keys()): + connection = connection_cache.pop(name) try: connection.disconnect() - del connection_cache[name] except Exception as e: if fail_on_error: raise diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/smbprotocol-1.14.0/src/smbprotocol/connection.py new/smbprotocol-1.15.0/src/smbprotocol/connection.py --- old/smbprotocol-1.14.0/src/smbprotocol/connection.py 2024-08-25 23:16:35.000000000 +0200 +++ new/smbprotocol-1.15.0/src/smbprotocol/connection.py 2024-11-11 22:24:12.000000000 +0100 @@ -1356,12 +1356,14 @@ message_id = header["message_id"].get_value() request = self.outstanding_requests[message_id] - # Typically you want to get the Session Id from the first message in a compound request but that is - # unreliable for async responses. Instead get the Session Id from the original request object if - # the Session Id is 0xFFFFFFFFFFFFFFFF. - # https://social.msdn.microsoft.com/Forums/en-US/a580f7bc-6746-4876-83db-6ac209b202c4/mssmb2-change-notify-response-sessionid?forum=os_fileservices - session_id = header["session_id"].get_value() - if session_id == 0xFFFFFFFFFFFFFFFF: + # For SMB2 SESSION_SETUP, the client MUST retrieve SessionId + # from SMB2 header of the response. For all other messages, + # the client MUST retrieve SessionId from the corresponding + # Request.Message. + command = header["command"].get_value() + if command == Commands.SMB2_SESSION_SETUP: + session_id = header["session_id"].get_value() + else: session_id = request.session_id # No need to waste CPU cycles to verify the signature if we already decrypted the header. @@ -1378,7 +1380,6 @@ with self.sequence_lock: self.sequence_window["high"] += credit_response - command = header["command"].get_value() status = header["status"].get_value() if command == Commands.SMB2_NEGOTIATE: self.preauth_integrity_hash_value.append(b_header) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/smbprotocol-1.14.0/src/smbprotocol/header.py new/smbprotocol-1.15.0/src/smbprotocol/header.py --- old/smbprotocol-1.14.0/src/smbprotocol/header.py 2024-08-25 23:16:35.000000000 +0200 +++ new/smbprotocol-1.15.0/src/smbprotocol/header.py 2024-11-11 22:24:12.000000000 +0100 @@ -58,6 +58,7 @@ STATUS_STOPPED_ON_SYMLINK = 0x8000002D STATUS_INVALID_INFO_CLASS = 0xC0000003 STATUS_INFO_LENGTH_MISMATCH = 0xC0000004 + STATUS_INVALID_HANDLE = 0xC0000008 STATUS_INVALID_PARAMETER = 0xC000000D STATUS_NO_SUCH_FILE = 0xC000000F STATUS_INVALID_DEVICE_REQUEST = 0xC0000010 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/smbprotocol-1.14.0/src/smbprotocol/session.py new/smbprotocol-1.15.0/src/smbprotocol/session.py --- old/smbprotocol-1.14.0/src/smbprotocol/session.py 2024-08-25 23:16:35.000000000 +0200 +++ new/smbprotocol-1.15.0/src/smbprotocol/session.py 2024-11-11 22:24:12.000000000 +0100 @@ -5,6 +5,7 @@ import logging import random from collections import OrderedDict +from typing import Literal, Optional import spnego from cryptography.hazmat.backends import default_backend @@ -170,7 +171,14 @@ class Session: - def __init__(self, connection, username=None, password=None, require_encryption=True, auth_protocol="negotiate"): + def __init__( + self, + connection, + username: Optional[str] = None, + password: Optional[str] = None, + require_encryption=True, + auth_protocol: Literal["negotiate", "ntlm", "kerberos"] = "negotiate", + ): """ [MS-SMB2] v53.0 2017-09-15 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/smbprotocol-1.14.0/tests/test_file_info.py new/smbprotocol-1.15.0/tests/test_file_info.py --- old/smbprotocol-1.14.0/tests/test_file_info.py 2024-08-25 23:16:35.000000000 +0200 +++ new/smbprotocol-1.15.0/tests/test_file_info.py 2024-11-11 22:24:12.000000000 +0100 @@ -6,6 +6,7 @@ from smbprotocol.file_info import ( FileAllInformation, + FileBasicInformation, FileBothDirectoryInformation, FileDirectoryInformation, FileDispositionInformation, @@ -27,6 +28,31 @@ from smbprotocol.structure import DateTimeField +class TestFileBasicInformation: + + def test_parse_message(self): + data = ( + b"\x00\xf2\xc4\x22\x2d\x1c\xdb\x01" + b"\x00\xf2\xc4\x22\x2d\x1c\xdb\x01" + b"\x00\xf2\xc4\x22\x2d\x1c\xdb\x01" + b"\x00\xf2\xc4\x22\x2d\x1c\xdb\x01" + b"\x10\x00\x00\x00" + b"\x00\x00\x00\x00" + ) + + actual = FileBasicInformation() + data = actual.unpack(data) + assert data == b"" + assert len(actual) == 40 + + assert actual["creation_time"].get_value() == 133731594120000000 + assert actual["last_access_time"].get_value() == 133731594120000000 + assert actual["last_write_time"].get_value() == 133731594120000000 + assert actual["change_time"].get_value() == 133731594120000000 + assert actual["file_attributes"].get_value() == 0x10 + assert actual["reserved"].get_value() == 0 + + class TestFileNameInformation: DATA = b"\x08\x00\x00\x00" b"\x63\x00\x61\x00\x66\x00\xe9\x00" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/smbprotocol-1.14.0/tests/test_smbclient_os.py new/smbprotocol-1.15.0/tests/test_smbclient_os.py --- old/smbprotocol-1.14.0/tests/test_smbclient_os.py 2024-08-25 23:16:35.000000000 +0200 +++ new/smbprotocol-1.15.0/tests/test_smbclient_os.py 2024-11-11 22:24:12.000000000 +0100 @@ -834,10 +834,7 @@ assert smbclient.listdir(smb_share) == ["file.txt"] - with smbclient.open_file(filename, buffering=0, mode="rb") as fd, SMBFileTransaction(fd) as trans: - query_info(trans, FileStreamInformation, output_buffer_length=1024) - - actual = sorted([s["stream_name"].get_value() for s in trans.results[0]]) + actual = sorted([s.name for s in smbclient.liststreams(filename)]) assert actual == ["::$DATA", ":ads:$DATA"] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/smbprotocol-1.14.0/tests/test_smbclient_shutil.py new/smbprotocol-1.15.0/tests/test_smbclient_shutil.py --- old/smbprotocol-1.14.0/tests/test_smbclient_shutil.py 2024-08-25 23:16:35.000000000 +0200 +++ new/smbprotocol-1.15.0/tests/test_smbclient_shutil.py 2024-11-11 22:24:12.000000000 +0100 @@ -640,9 +640,12 @@ os.symlink(src_filename, src_link) os.symlink(dst_filename, dst_link) - expected = "chmod: follow_symlinks unavailable on this platform" - with pytest.raises(NotImplementedError, match=re.escape(expected)): + if os.name == "nt" and sys.version_info >= (3, 13): copymode(src_link, dst_link, follow_symlinks=False) + else: + expected = "chmod: follow_symlinks unavailable on this platform" + with pytest.raises(NotImplementedError, match=re.escape(expected)): + copymode(src_link, dst_link, follow_symlinks=False) def test_copymode_missing_src(smb_share):