Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rename sspi dep to sspic #70

Merged
merged 1 commit into from
Sep 29, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
# Changelog

## 0.10.0 - 2023-09-27
## 0.10.1 - 2023-09-29

* Rename `sspi` package dependency to `sspic` to avoid conflicts with pywin32

## 0.10.0 - 2023-09-27 - Has been yanked

* Drop support for Python 3.7 - new minimum is 3.8+
* Moved SSPI bindings out into a separate package called `sspi`
Expand Down
4 changes: 2 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ classifiers = [
]
dependencies = [
"cryptography",
"sspi >= 0.1.0; sys_platform == 'win32'"
"sspic >= 0.1.0; sys_platform == 'win32'"
]
dynamic = ["version"]

Expand Down Expand Up @@ -122,7 +122,7 @@ module = "spnego._sspi"
disallow_any_unimported = false

[[tool.mypy.overrides]]
module = "sspi.*"
module = "sspic.*"
ignore_missing_imports = true

# These types are used in tests, too much effort to create stubs
Expand Down
138 changes: 69 additions & 69 deletions src/spnego/_sspi.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@
log = logging.getLogger(__name__)

if os.name == "nt":
import sspi
import sspic

Check warning on line 39 in src/spnego/_sspi.py

View check run for this annotation

Codecov / codecov/patch

src/spnego/_sspi.py#L39

Added line #L39 was not covered by tests

HAS_SSPI = True
else:
Expand All @@ -51,14 +51,14 @@
return []


def _create_iov_result(iov: sspi.raw.SecBufferDesc) -> tuple[IOVResBuffer, ...]:
def _create_iov_result(iov: sspic.raw.SecBufferDesc) -> tuple[IOVResBuffer, ...]:
"""Converts SSPI IOV buffer to generic IOVBuffer result."""
buffers = []
for i in iov:
buffer_type = int(i.buffer_type)
if i.buffer_flags & sspi.raw.SecBufferFlags.SECBUFFER_READONLY_WITH_CHECKSUM:
if i.buffer_flags & sspic.raw.SecBufferFlags.SECBUFFER_READONLY_WITH_CHECKSUM:

Check warning on line 59 in src/spnego/_sspi.py

View check run for this annotation

Codecov / codecov/patch

src/spnego/_sspi.py#L59

Added line #L59 was not covered by tests
buffer_type = BufferType.sign_only
elif i.buffer_flags & sspi.raw.SecBufferFlags.SECBUFFER_READONLY:
elif i.buffer_flags & sspic.raw.SecBufferFlags.SECBUFFER_READONLY:

Check warning on line 61 in src/spnego/_sspi.py

View check run for this annotation

Codecov / codecov/patch

src/spnego/_sspi.py#L61

Added line #L61 was not covered by tests
buffer_type = BufferType.data_readonly

buffer_entry = IOVResBuffer(type=BufferType(buffer_type), data=i.data)
Expand All @@ -72,7 +72,7 @@
protocol: str,
usage: str,
credentials: list[Credential],
) -> sspi.raw.CredHandle:
) -> sspic.raw.CredHandle:
"""Get the SSPI credential.

Will get an SSPI credential for the protocol specified. Currently only
Expand All @@ -86,31 +86,31 @@
credentials: List of credentials to retrieve from.

Returns:
sspi.raw.CredHandle: The handle to the SSPI credential to use.
sspic.raw.CredHandle: The handle to the SSPI credential to use.
"""
credential_kwargs: dict[str, t.Any] = {
"package": protocol,
"principal": principal,
"credential_use": (
sspi.raw.CredentialUse.SECPKG_CRED_OUTBOUND
sspic.raw.CredentialUse.SECPKG_CRED_OUTBOUND
if usage == "initiate"
else sspi.raw.CredentialUse.SECPKG_CRED_INBOUND
else sspic.raw.CredentialUse.SECPKG_CRED_INBOUND
),
}

for cred in credentials:
if isinstance(cred, Password):
domain, username = split_username(cred.username)
auth_data = sspi.raw.WinNTAuthIdentity(
auth_data = sspic.raw.WinNTAuthIdentity(

Check warning on line 104 in src/spnego/_sspi.py

View check run for this annotation

Codecov / codecov/patch

src/spnego/_sspi.py#L104

Added line #L104 was not covered by tests
username=username,
domain=domain,
password=cred.password,
)

return sspi.raw.acquire_credentials_handle(**credential_kwargs, auth_data=auth_data).credential
return sspic.raw.acquire_credentials_handle(**credential_kwargs, auth_data=auth_data).credential

Check warning on line 110 in src/spnego/_sspi.py

View check run for this annotation

Codecov / codecov/patch

src/spnego/_sspi.py#L110

Added line #L110 was not covered by tests

elif isinstance(cred, CredentialCache):
return sspi.raw.acquire_credentials_handle(**credential_kwargs).credential
return sspic.raw.acquire_credentials_handle(**credential_kwargs).credential

Check warning on line 113 in src/spnego/_sspi.py

View check run for this annotation

Codecov / codecov/patch

src/spnego/_sspi.py#L113

Added line #L113 was not covered by tests

raise InvalidCredentialError(context_msg="No applicable credentials available")

Expand Down Expand Up @@ -138,14 +138,14 @@
) -> None:

if not HAS_SSPI:
raise ImportError("SSPIProxy requires the Windows only sspi python package")
raise ImportError("SSPIProxy requires the Windows only sspic python package")

credentials = unify_credentials(username, password)
super(SSPIProxy, self).__init__(
credentials, hostname, service, channel_bindings, context_req, usage, protocol, options
)

self._native_channel_bindings: sspi.SecChannelBindings | None
self._native_channel_bindings: sspic.SecChannelBindings | None

Check warning on line 148 in src/spnego/_sspi.py

View check run for this annotation

Codecov / codecov/patch

src/spnego/_sspi.py#L148

Added line #L148 was not covered by tests
if channel_bindings:
self._native_channel_bindings = self._get_native_bindings(channel_bindings)
else:
Expand All @@ -156,7 +156,7 @@
self._security_trailer = 0

self._complete = False
self._context: sspi.raw.CtxtHandle | None = None
self._context: sspic.raw.CtxtHandle | None = None

Check warning on line 159 in src/spnego/_sspi.py

View check run for this annotation

Codecov / codecov/patch

src/spnego/_sspi.py#L159

Added line #L159 was not covered by tests
self.__seq_num = 0

sspi_credential = kwargs.get("_sspi_credential", None)
Expand All @@ -179,9 +179,9 @@
@property
def client_principal(self) -> str | None:
if self.usage == "accept":
names = sspi.raw.query_context_attributes(
t.cast(sspi.raw.CtxtHandle, self._context),
sspi.raw.SecPkgContextNames,
names = sspic.raw.query_context_attributes(

Check warning on line 182 in src/spnego/_sspi.py

View check run for this annotation

Codecov / codecov/patch

src/spnego/_sspi.py#L182

Added line #L182 was not covered by tests
t.cast(sspic.raw.CtxtHandle, self._context),
sspic.raw.SecPkgContextNames,
)
return names.username
else:
Expand All @@ -195,18 +195,18 @@
def negotiated_protocol(self) -> str | None:
# FIXME: Try and replicate GSSAPI. Will return None for acceptor until the first token is returned. Negotiate
# for both iniator and acceptor until the context is established.
package_info = sspi.raw.query_context_attributes(
t.cast(sspi.raw.CtxtHandle, self._context),
sspi.raw.SecPkgContextPackageInfo,
package_info = sspic.raw.query_context_attributes(

Check warning on line 198 in src/spnego/_sspi.py

View check run for this annotation

Codecov / codecov/patch

src/spnego/_sspi.py#L198

Added line #L198 was not covered by tests
t.cast(sspic.raw.CtxtHandle, self._context),
sspic.raw.SecPkgContextPackageInfo,
)
return package_info.name.lower()

@property
@wrap_system_error(NativeError, "Retrieving session key")
def session_key(self) -> bytes:
session_key = sspi.raw.query_context_attributes(
t.cast(sspi.raw.CtxtHandle, self._context),
sspi.raw.SecPkgContextSessionKey,
session_key = sspic.raw.query_context_attributes(

Check warning on line 207 in src/spnego/_sspi.py

View check run for this annotation

Codecov / codecov/patch

src/spnego/_sspi.py#L207

Added line #L207 was not covered by tests
t.cast(sspic.raw.CtxtHandle, self._context),
sspic.raw.SecPkgContextSessionKey,
)
return session_key.session_key

Expand All @@ -232,12 +232,12 @@
if not self._is_wrapped:
log.debug("SSPI step input: %s", base64.b64encode(in_token or b"").decode())

sec_tokens: list[sspi.raw.SecBuffer] = []
sec_tokens: list[sspic.raw.SecBuffer] = []

Check warning on line 235 in src/spnego/_sspi.py

View check run for this annotation

Codecov / codecov/patch

src/spnego/_sspi.py#L235

Added line #L235 was not covered by tests
if in_token:
in_token = bytearray(in_token)
sec_tokens.append(sspi.raw.SecBuffer(in_token, sspi.raw.SecBufferType.SECBUFFER_TOKEN))
sec_tokens.append(sspic.raw.SecBuffer(in_token, sspic.raw.SecBufferType.SECBUFFER_TOKEN))

Check warning on line 238 in src/spnego/_sspi.py

View check run for this annotation

Codecov / codecov/patch

src/spnego/_sspi.py#L238

Added line #L238 was not covered by tests

native_channel_bindings: sspi.SecChannelBindings | None
native_channel_bindings: sspic.SecChannelBindings | None
if channel_bindings:
native_channel_bindings = self._get_native_bindings(channel_bindings)
else:
Expand All @@ -246,39 +246,39 @@
if native_channel_bindings:
sec_tokens.append(native_channel_bindings.dangerous_get_sec_buffer())

in_buffer: sspi.raw.SecBufferDesc | None = None
in_buffer: sspic.raw.SecBufferDesc | None = None

Check warning on line 249 in src/spnego/_sspi.py

View check run for this annotation

Codecov / codecov/patch

src/spnego/_sspi.py#L249

Added line #L249 was not covered by tests
if sec_tokens:
in_buffer = sspi.raw.SecBufferDesc(sec_tokens)
in_buffer = sspic.raw.SecBufferDesc(sec_tokens)

Check warning on line 251 in src/spnego/_sspi.py

View check run for this annotation

Codecov / codecov/patch

src/spnego/_sspi.py#L251

Added line #L251 was not covered by tests

out_buffer = sspi.raw.SecBufferDesc(
out_buffer = sspic.raw.SecBufferDesc(

Check warning on line 253 in src/spnego/_sspi.py

View check run for this annotation

Codecov / codecov/patch

src/spnego/_sspi.py#L253

Added line #L253 was not covered by tests
[
sspi.raw.SecBuffer(None, sspi.raw.SecBufferType.SECBUFFER_TOKEN),
sspic.raw.SecBuffer(None, sspic.raw.SecBufferType.SECBUFFER_TOKEN),
]
)

context_req: int
res: sspi.raw.InitializeContextResult | sspi.raw.AcceptContextResult
res: sspic.raw.InitializeContextResult | sspic.raw.AcceptContextResult
if self.usage == "initiate":
context_req = self._context_req | sspi.IscReq.ISC_REQ_ALLOCATE_MEMORY
res = sspi.raw.initialize_security_context(
context_req = self._context_req | sspic.IscReq.ISC_REQ_ALLOCATE_MEMORY
res = sspic.raw.initialize_security_context(

Check warning on line 263 in src/spnego/_sspi.py

View check run for this annotation

Codecov / codecov/patch

src/spnego/_sspi.py#L262-L263

Added lines #L262 - L263 were not covered by tests
credential=self._credential,
context=self._context,
target_name=self.spn or "",
context_req=context_req,
target_data_rep=sspi.raw.TargetDataRep.SECURITY_NATIVE_DREP,
target_data_rep=sspic.raw.TargetDataRep.SECURITY_NATIVE_DREP,
input_buffers=in_buffer,
output_buffers=out_buffer,
)
status = res.status
self._context = res.context
else:
context_req = self._context_req | sspi.AscReq.ASC_REQ_ALLOCATE_MEMORY
res = sspi.raw.accept_security_context(
context_req = self._context_req | sspic.AscReq.ASC_REQ_ALLOCATE_MEMORY
res = sspic.raw.accept_security_context(

Check warning on line 276 in src/spnego/_sspi.py

View check run for this annotation

Codecov / codecov/patch

src/spnego/_sspi.py#L275-L276

Added lines #L275 - L276 were not covered by tests
credential=self._credential,
context=self._context,
input_buffers=in_buffer,
context_req=context_req,
target_data_rep=sspi.raw.TargetDataRep.SECURITY_NATIVE_DREP,
target_data_rep=sspic.raw.TargetDataRep.SECURITY_NATIVE_DREP,
output_buffers=out_buffer,
)
status = res.status
Expand All @@ -288,10 +288,10 @@

self._context_attr = int(res.attributes)

if status == sspi.raw.NtStatus.SEC_E_OK:
if status == sspic.raw.NtStatus.SEC_E_OK:

Check warning on line 291 in src/spnego/_sspi.py

View check run for this annotation

Codecov / codecov/patch

src/spnego/_sspi.py#L291

Added line #L291 was not covered by tests
self._complete = True

attr_sizes = sspi.raw.query_context_attributes(self._context, sspi.raw.SecPkgContextSizes)
attr_sizes = sspic.raw.query_context_attributes(self._context, sspic.raw.SecPkgContextSizes)

Check warning on line 294 in src/spnego/_sspi.py

View check run for this annotation

Codecov / codecov/patch

src/spnego/_sspi.py#L294

Added line #L294 was not covered by tests
self._block_size = attr_sizes.block_size
self._max_signature = attr_sizes.max_signature
self._security_trailer = attr_sizes.security_trailer
Expand Down Expand Up @@ -324,16 +324,16 @@
qop: int | None = None,
) -> IOVWrapResult:
qop = qop or 0
if encrypt and qop & sspi.raw.QopFlags.SECQOP_WRAP_NO_ENCRYPT:
if encrypt and qop & sspic.raw.QopFlags.SECQOP_WRAP_NO_ENCRYPT:

Check warning on line 327 in src/spnego/_sspi.py

View check run for this annotation

Codecov / codecov/patch

src/spnego/_sspi.py#L327

Added line #L327 was not covered by tests
raise ValueError("Cannot set qop with SECQOP_WRAP_NO_ENCRYPT and encrypt=True")
elif not encrypt:
qop |= sspi.raw.QopFlags.SECQOP_WRAP_NO_ENCRYPT
qop |= sspic.raw.QopFlags.SECQOP_WRAP_NO_ENCRYPT

Check warning on line 330 in src/spnego/_sspi.py

View check run for this annotation

Codecov / codecov/patch

src/spnego/_sspi.py#L330

Added line #L330 was not covered by tests

buffers = self._build_iov_list(iov, self._convert_iov_buffer)
iov_buffer = sspi.raw.SecBufferDesc(buffers)
iov_buffer = sspic.raw.SecBufferDesc(buffers)

Check warning on line 333 in src/spnego/_sspi.py

View check run for this annotation

Codecov / codecov/patch

src/spnego/_sspi.py#L333

Added line #L333 was not covered by tests

sspi.raw.encrypt_message(
t.cast(sspi.raw.CtxtHandle, self._context),
sspic.raw.encrypt_message(

Check warning on line 335 in src/spnego/_sspi.py

View check run for this annotation

Codecov / codecov/patch

src/spnego/_sspi.py#L335

Added line #L335 was not covered by tests
t.cast(sspic.raw.CtxtHandle, self._context),
qop=qop,
message=iov_buffer,
seq_no=self._seq_num,
Expand All @@ -360,14 +360,14 @@
iov: collections.abc.Iterable[IOV],
) -> IOVUnwrapResult:
buffers = self._build_iov_list(iov, self._convert_iov_buffer)
iov_buffer = sspi.raw.SecBufferDesc(buffers)
iov_buffer = sspic.raw.SecBufferDesc(buffers)

Check warning on line 363 in src/spnego/_sspi.py

View check run for this annotation

Codecov / codecov/patch

src/spnego/_sspi.py#L363

Added line #L363 was not covered by tests

qop = sspi.raw.decrypt_message(
t.cast(sspi.raw.CtxtHandle, self._context),
qop = sspic.raw.decrypt_message(

Check warning on line 365 in src/spnego/_sspi.py

View check run for this annotation

Codecov / codecov/patch

src/spnego/_sspi.py#L365

Added line #L365 was not covered by tests
t.cast(sspic.raw.CtxtHandle, self._context),
iov_buffer,
seq_no=self._seq_num,
)
encrypted = qop & sspi.raw.QopFlags.SECQOP_WRAP_NO_ENCRYPT == 0
encrypted = qop & sspic.raw.QopFlags.SECQOP_WRAP_NO_ENCRYPT == 0

Check warning on line 370 in src/spnego/_sspi.py

View check run for this annotation

Codecov / codecov/patch

src/spnego/_sspi.py#L370

Added line #L370 was not covered by tests

return IOVUnwrapResult(buffers=_create_iov_result(iov_buffer), encrypted=encrypted, qop=qop)

Expand All @@ -383,14 +383,14 @@
) -> bytes:
data = bytearray(data)
signature = bytearray(self._max_signature)
iov = sspi.raw.SecBufferDesc(
iov = sspic.raw.SecBufferDesc(

Check warning on line 386 in src/spnego/_sspi.py

View check run for this annotation

Codecov / codecov/patch

src/spnego/_sspi.py#L386

Added line #L386 was not covered by tests
[
sspi.raw.SecBuffer(data, sspi.raw.SecBufferType.SECBUFFER_DATA),
sspi.raw.SecBuffer(signature, sspi.raw.SecBufferType.SECBUFFER_TOKEN),
sspic.raw.SecBuffer(data, sspic.raw.SecBufferType.SECBUFFER_DATA),
sspic.raw.SecBuffer(signature, sspic.raw.SecBufferType.SECBUFFER_TOKEN),
]
)
sspi.raw.make_signature(
t.cast(sspi.raw.CtxtHandle, self._context),
sspic.raw.make_signature(

Check warning on line 392 in src/spnego/_sspi.py

View check run for this annotation

Codecov / codecov/patch

src/spnego/_sspi.py#L392

Added line #L392 was not covered by tests
t.cast(sspic.raw.CtxtHandle, self._context),
qop or 0,
iov,
self._seq_num,
Expand All @@ -402,15 +402,15 @@
def verify(self, data: bytes, mic: bytes) -> int:
data = bytearray(data)
mic = bytearray(mic)
iov = sspi.raw.SecBufferDesc(
iov = sspic.raw.SecBufferDesc(

Check warning on line 405 in src/spnego/_sspi.py

View check run for this annotation

Codecov / codecov/patch

src/spnego/_sspi.py#L405

Added line #L405 was not covered by tests
[
sspi.raw.SecBuffer(data, sspi.raw.SecBufferType.SECBUFFER_DATA),
sspi.raw.SecBuffer(mic, sspi.raw.SecBufferType.SECBUFFER_TOKEN),
sspic.raw.SecBuffer(data, sspic.raw.SecBufferType.SECBUFFER_DATA),
sspic.raw.SecBuffer(mic, sspic.raw.SecBufferType.SECBUFFER_TOKEN),
]
)

return sspi.raw.verify_signature(
t.cast(sspi.raw.CtxtHandle, self._context),
return sspic.raw.verify_signature(

Check warning on line 412 in src/spnego/_sspi.py

View check run for this annotation

Codecov / codecov/patch

src/spnego/_sspi.py#L412

Added line #L412 was not covered by tests
t.cast(sspic.raw.CtxtHandle, self._context),
iov,
self._seq_num,
)
Expand All @@ -423,10 +423,10 @@
sspi_req: type[int] | None
if self.usage == "initiate":
attr_map.append((ContextReq.no_integrity, "REQ_NO_INTEGRITY"))
sspi_req = sspi.IscReq
sspi_req = sspic.IscReq

Check warning on line 426 in src/spnego/_sspi.py

View check run for this annotation

Codecov / codecov/patch

src/spnego/_sspi.py#L426

Added line #L426 was not covered by tests
sspi_prefix = "ISC"
else:
sspi_req = sspi.AscReq
sspi_req = sspic.AscReq

Check warning on line 429 in src/spnego/_sspi.py

View check run for this annotation

Codecov / codecov/patch

src/spnego/_sspi.py#L429

Added line #L429 was not covered by tests
sspi_prefix = "ASC"

attr_map.extend(
Expand Down Expand Up @@ -456,7 +456,7 @@
self.__seq_num += 1
return num

def _convert_iov_buffer(self, buffer: IOVBuffer) -> sspi.raw.SecBuffer:
def _convert_iov_buffer(self, buffer: IOVBuffer) -> sspic.raw.SecBuffer:
data = bytearray()

if isinstance(buffer.data, bytes):
Expand Down Expand Up @@ -484,24 +484,24 @@
data = bytearray(auto_alloc_size[buffer.type])

# This buffer types need manual mapping from the generic value to the
# one understood by SSPI.
# one understood by sspic.
buffer_type = int(buffer.type)
buffer_flags = 0
if buffer_type == BufferType.sign_only:
buffer_type = sspi.raw.SecBufferType.SECBUFFER_DATA
buffer_flags = sspi.raw.SecBufferFlags.SECBUFFER_READONLY_WITH_CHECKSUM
buffer_type = sspic.raw.SecBufferType.SECBUFFER_DATA
buffer_flags = sspic.raw.SecBufferFlags.SECBUFFER_READONLY_WITH_CHECKSUM

Check warning on line 492 in src/spnego/_sspi.py

View check run for this annotation

Codecov / codecov/patch

src/spnego/_sspi.py#L491-L492

Added lines #L491 - L492 were not covered by tests
elif buffer_type == BufferType.data_readonly:
buffer_type = sspi.raw.SecBufferType.SECBUFFER_DATA
buffer_flags = sspi.raw.SecBufferFlags.SECBUFFER_READONLY
buffer_type = sspic.raw.SecBufferType.SECBUFFER_DATA
buffer_flags = sspic.raw.SecBufferFlags.SECBUFFER_READONLY

Check warning on line 495 in src/spnego/_sspi.py

View check run for this annotation

Codecov / codecov/patch

src/spnego/_sspi.py#L494-L495

Added lines #L494 - L495 were not covered by tests

return sspi.raw.SecBuffer(data, buffer_type, buffer_flags)
return sspic.raw.SecBuffer(data, buffer_type, buffer_flags)

Check warning on line 497 in src/spnego/_sspi.py

View check run for this annotation

Codecov / codecov/patch

src/spnego/_sspi.py#L497

Added line #L497 was not covered by tests

def _get_native_bindings(
self,
channel_bindings: GssChannelBindings,
) -> sspi.SecChannelBindings:
) -> sspic.SecChannelBindings:
"""Gets the raw byte value of the SEC_CHANNEL_BINDINGS structure."""
return sspi.SecChannelBindings(
return sspic.SecChannelBindings(

Check warning on line 504 in src/spnego/_sspi.py

View check run for this annotation

Codecov / codecov/patch

src/spnego/_sspi.py#L504

Added line #L504 was not covered by tests
initiator_addr_type=int(channel_bindings.initiator_addrtype),
initiator_addr=channel_bindings.initiator_address,
acceptor_addr_type=int(channel_bindings.acceptor_addrtype),
Expand Down
2 changes: 1 addition & 1 deletion tests/test_sspi.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ def test_build_iov_list_fail_auto_alloc(ntlm_cred):
def test_no_sspi_library(monkeypatch):
monkeypatch.setattr(spnego._sspi, "HAS_SSPI", False)

with pytest.raises(ImportError, match="SSPIProxy requires the Windows only sspi python package"):
with pytest.raises(ImportError, match="SSPIProxy requires the Windows only sspic python package"):
spnego._sspi.SSPIProxy()


Expand Down