Skip to content

Commit

Permalink
Added query context attributes
Browse files Browse the repository at this point in the history
  • Loading branch information
jborean93 committed Sep 18, 2023
1 parent becbc62 commit 64c3ac9
Show file tree
Hide file tree
Showing 9 changed files with 555 additions and 4 deletions.
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,11 +51,11 @@ def make_extension(
for e in [
"credential",
"ntstatus",
"query_context",
"security_buffer",
"security_context",
"security_package",
"text",
# ("acquire_credentials_handle", "AcquireCredentialsHandleW"),
]:
name = e
libraries = ["Secur32"]
Expand Down
12 changes: 12 additions & 0 deletions src/sspi/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,13 @@
acquire_credentials_handle,
)
from ._ntstatus import NtStatus
from ._query_context import (
SecPkgBuffer,
SecPkgContextNames,
SecPkgContextSessionKey,
SecPkgContextSizes,
query_context_attributes,
)
from ._security_buffer import (
SECBUFFER_VERSION,
SecBuffer,
Expand Down Expand Up @@ -58,6 +65,10 @@
"SecBufferFlags",
"SecBufferType",
"SecChannelBindings",
"SecPkgBuffer",
"SecPkgContextNames",
"SecPkgContextSessionKey",
"SecPkgContextSizes",
"SecPkgInfo",
"SecurityPackageCapability",
"SecurityContext",
Expand All @@ -69,4 +80,5 @@
"complete_auth_token",
"enumerate_security_packages",
"initialize_security_context",
"query_context_attributes",
]
96 changes: 96 additions & 0 deletions src/sspi/_query_context.pyi
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
# Copyright: (c) 2023 Jordan Borean (@jborean93) <jborean93@gmail.com>
# MIT License (see LICENSE or https://opensource.org/licenses/MIT)

from __future__ import annotations

import typing as t

from ._security_context import SecurityContext

T = t.TypeVar("T", bound=SecPkgBuffer)

class SecPkgBuffer:
"""Base class for context attribute types."""

class SecPkgContextNames(SecPkgBuffer):
"""Security Package Names
The structure indicates the name of the user associated with a security
context.
This wraps the `SecPkgContext_NamesW`_ Win32 structure.
.. _SecPkgContext_NamesW:
https://learn.microsoft.com/en-us/windows/win32/api/sspi/ns-sspi-secpkgcontext_namesw
"""

@property
def username(self) -> str:
"""The user represented by the context."""

class SecPkgContextSessionKey(SecPkgBuffer):
"""Security Package session key.
The structure contains information about the session key used for the
security context.
This wraps the `SecPkgContext_SessionKey`_ Win32 structure.
.. _SecPkgContext_SessionKey:
https://learn.microsoft.com/en-us/windows/win32/api/sspi/ns-sspi-secpkgcontext_sessionkey
"""

@property
def session_key(self) -> bytes:
"""The session key for the security context."""

class SecPkgContextSizes(SecPkgBuffer):
"""Security Package sizes.
The structure indicates the sizes of important structures used in the
message support functions. Use :meth:`query_context_attributes` to generate
this instance.
This wraps the `SecPkgContext_Sizes`_ Win32 structure.
.. _SecPkgContextSizes:
https://learn.microsoft.com/en-us/windows/win32/api/sspi/ns-sspi-secpkgcontext_sizes
"""

@property
def max_token(self) -> int:
"""Maximum security token sizes."""
@property
def max_signature(self) -> int:
"""Maximum signature size."""
@property
def block_size(self) -> int:
"""Preferred integral size of messages."""
@property
def security_trailer(self) -> int:
"""Size of the security trailer appended to messages."""

def query_context_attributes(
context: SecurityContext,
attribute: type[T],
) -> T:
"""Queries an attribute of a security context.
Enables a transport application to query a security package for certain
attributes of a security context.
The attribute must be a type that is a subclass of :class:`SecPkgBuffer`.
The instance is created, populated, and returned by this function.
This wraps the `QueryContextAttributes`_ Win32 function.
Args:
context: The security context to query.
attribute: The attribute type to query and return.
Returns:
The instance of the attribute type provided.
.. _QueryContextAttributes:
https://learn.microsoft.com/en-us/windows/win32/secauthn/querycontextattributes--general
"""
184 changes: 184 additions & 0 deletions src/sspi/_query_context.pyx
Original file line number Diff line number Diff line change
@@ -0,0 +1,184 @@
# Copyright: (c) 2023 Jordan Borean (@jborean93) <jborean93@gmail.com>
# MIT License (see LICENSE or https://opensource.org/licenses/MIT)

from __future__ import annotations

import collections
import enum

from cpython.exc cimport PyErr_SetFromWindowsErr

from sspi._security_buffer cimport FreeContextBuffer
from sspi._security_context cimport PCtxtHandle, SecurityContext
from sspi._text cimport wide_char_to_str
from sspi._win32_types cimport *

from sspi._ntstatus import NtStatus


cdef extern from "Security.h":
unsigned long SECPKG_ATTR_SIZES
unsigned long SECPKG_ATTR_NAMES
unsigned long SECPKG_ATTR_LIFESPAN
unsigned long SECPKG_ATTR_DCE_INFO
unsigned long SECPKG_ATTR_STREAM_SIZES
unsigned long SECPKG_ATTR_KEY_INFO
unsigned long SECPKG_ATTR_AUTHORITY
unsigned long SECPKG_ATTR_PROTO_INFO
unsigned long SECPKG_ATTR_PASSWORD_EXPIRY
unsigned long SECPKG_ATTR_SESSION_KEY
unsigned long SECPKG_ATTR_PACKAGE_INFO
unsigned long SECPKG_ATTR_USER_FLAGS
unsigned long SECPKG_ATTR_NEGOTIATION_INFO
unsigned long SECPKG_ATTR_NATIVE_NAMES
unsigned long SECPKG_ATTR_FLAGS
unsigned long SECPKG_ATTR_USE_VALIDATED
unsigned long SECPKG_ATTR_CREDENTIAL_NAME
unsigned long SECPKG_ATTR_TARGET_INFORMATION
unsigned long SECPKG_ATTR_ACCESS_TOKEN
unsigned long SECPKG_ATTR_TARGET
unsigned long SECPKG_ATTR_AUTHENTICATION_ID
unsigned long SECPKG_ATTR_LOGOFF_TIME
unsigned long SECPKG_ATTR_NEGO_KEYS
unsigned long SECPKG_ATTR_PROMPTING_NEEDED
unsigned long SECPKG_ATTR_UNIQUE_BINDINGS
unsigned long SECPKG_ATTR_ENDPOINT_BINDINGS
unsigned long SECPKG_ATTR_CLIENT_SPECIFIED_TARGET
unsigned long SECPKG_ATTR_LAST_CLIENT_TOKEN_STATUS
unsigned long SECPKG_ATTR_NEGO_PKG_INFO
unsigned long SECPKG_ATTR_NEGO_STATUS
unsigned long SECPKG_ATTR_CONTEXT_DELETED
unsigned long SECPKG_ATTR_DTLS_MTU
unsigned long SECPKG_ATTR_DATAGRAM_SIZES
unsigned long SECPKG_ATTR_SUBJECT_SECURITY_ATTRIBUTES
unsigned long SECPKG_ATTR_APPLICATION_PROTOCOL
unsigned long SECPKG_ATTR_NEGOTIATED_TLS_EXTENSIONS
unsigned long SECPKG_ATTR_IS_LOOPBACK

cdef struct _SecPkgContext_NamesW:
LPWSTR sUserName
ctypedef _SecPkgContext_NamesW SecPkgContext_NamesW
ctypedef SecPkgContext_NamesW *PSecPkgContext_NamesW

cdef struct _SecPkgContext_Sizes:
unsigned long cbMaxToken
unsigned long cbMaxSignature
unsigned long cbBlockSize
unsigned long cbSecurityTrailer
ctypedef _SecPkgContext_Sizes SecPkgContext_Sizes
ctypedef SecPkgContext_Sizes *PSecPkgContext_Sizes

cdef struct _SecPkgContext_SessionKey:
unsigned long SessionKeyLength
unsigned char *SessionKey
ctypedef _SecPkgContext_SessionKey SecPkgContext_SessionKey
ctypedef SecPkgContext_SessionKey *PSecPkgContext_SessionKey

# https://learn.microsoft.com/en-us/windows/win32/api/sspi/nf-sspi-querycontextattributesw
SECURITY_STATUS QueryContextAttributesW(
PCtxtHandle phContext,
unsigned long ulAttribute,
void *pBuffer
) nogil

cdef class SecPkgBuffer:

cdef (unsigned long, void *) __c_value__(SecPkgBuffer self):
return (0, NULL)

cdef class SecPkgContextNames(SecPkgBuffer):
cdef SecPkgContext_NamesW raw

def __dealloc__(SecPkgContextNames self):
if self.raw.sUserName:
FreeContextBuffer(self.raw.sUserName)
self.raw.sUserName = NULL

cdef (unsigned long, void *) __c_value__(SecPkgContextNames self):
return (SECPKG_ATTR_NAMES, &self.raw)

def __repr__(SecPkgContextNames self):
return f"SecPkgContextNames(username={self.username!r})"

@property
def username(SecPkgContextNames self) -> str:
if self.raw.sUserName == NULL:
return ""
else:
return wide_char_to_str(self.raw.sUserName)

cdef class SecPkgContextSessionKey(SecPkgBuffer):
cdef SecPkgContext_SessionKey raw

def __dealloc__(SecPkgContextSessionKey self):
if self.raw.SessionKey:
FreeContextBuffer(self.raw.SessionKey)
self.raw.SessionKeyLength = 0
self.raw.SessionKey = NULL

cdef (unsigned long, void *) __c_value__(SecPkgContextSessionKey self):
return (SECPKG_ATTR_SESSION_KEY, &self.raw)

def __repr__(SecPkgContextSessionKey self):
return f"SecPkgContextSessionKey(session_key={self.session_key!r})"

@property
def session_key(SecPkgContextSessionKey self) -> bytes:
if self.raw.SessionKeyLength and self.raw.SessionKey != NULL:
return (<char *>self.raw.SessionKey)[:self.raw.SessionKeyLength]
else:
return b""

cdef class SecPkgContextSizes(SecPkgBuffer):
cdef SecPkgContext_Sizes raw

cdef (unsigned long, void *) __c_value__(SecPkgContextSizes self):
return (SECPKG_ATTR_SIZES, &self.raw)

def __repr__(SecPkgContextSizes self) -> str:
kwargs = [f"{k}={v}" for k, v in {
'max_token': self.max_token,
'max_signature': self.max_signature,
'block_size': self.block_size,
'security_trailer': self.security_trailer,
}.items()]

return f"SecPkgContextSizes({', '.join(kwargs)})"

@property
def max_token(SecPkgContextSizes self) -> int:
return self.raw.cbMaxToken

@property
def max_signature(SecPkgContextSizes self) -> int:
return self.raw.cbMaxSignature

@property
def block_size(SecPkgContextSizes self) -> int:
return self.raw.cbBlockSize

@property
def security_trailer(SecPkgContextSizes self) -> int:
return self.raw.cbSecurityTrailer

def query_context_attributes(
SecurityContext context not None,
type attribute not None,
) -> SecPkgBuffer:
if not issubclass(attribute, SecPkgBuffer):
raise TypeError("attribute must be a type of SecPkgBuffer")

cdef SecPkgBuffer value = attribute()
cdef (unsigned long, void*) raw = value.__c_value__()

with nogil:
res = QueryContextAttributesW(
&context.raw,
raw[0],
raw[1],
)

if res:
PyErr_SetFromWindowsErr(res)

return value
2 changes: 1 addition & 1 deletion src/sspi/_security_buffer.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ class SecBufferDesc:
) -> None: ...
def __iter__(self) -> list[SecBuffer]:
"""Creates an iterable of the contained buffers."""
def __l0en__(self) -> int:
def __len__(self) -> int:
"""Returns the number of buffers in this structure."""
def __getitem__(self, key: int) -> SecBuffer:
"""Gets the buffer at the specified index."""
Expand Down
3 changes: 1 addition & 2 deletions src/sspi/_security_buffer.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,6 @@ cdef class SecBufferDesc:
return self.raw.cBuffers

def __getitem__(SecBufferDesc self, key: int) -> SecBuffer:
# FIXME: Add checks for the index.
return self._buffers[key]

cdef void mark_as_allocated(SecBufferDesc self):
Expand Down Expand Up @@ -192,7 +191,7 @@ cdef class SecBuffer:

def __repr__(SecBuffer self) -> str:
kwargs = [f"{k}={v}" for k, v in {
'data': repr(self.data),
'data': f"bytearray({self.data!r})",
'buffer_type': self.buffer_type,
'buffer_flags': self.buffer_flags,
}.items()]
Expand Down
Loading

0 comments on commit 64c3ac9

Please sign in to comment.