Skip to content
Merged

Aes #40

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
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v9
- uses: actions/checkout@v6
- name: Set up Python 3.12
uses: actions/setup-python@v6
with:
Expand Down
95 changes: 45 additions & 50 deletions aiowmi/connection.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import asyncio
from typing import Optional, Callable
from Crypto.Cipher import ARC4
from .exceptions import AccessDenied, NoNewActiveKey, BindNak
from .exceptions import AccessDenied, BindNak
from .kerberos.ap_req import build_ap_req, wrap_gss_kerberos, get_active_key
from .kerberos.cache import KerberosCache
from .kerberos.krb5_pdu import get_neg_token, build_alter_context
from .kerberos.tgs import get_tgs
from .kerberos.tgt import get_tgt
from .kerberos.tools import gss_unwrap_kerberos
from .kerberos.tools import sign_func_kerberos, seal_func_kerberos
from .kerberos.wrappers import sign_func_kerberos, seal_func_kerberos
from .kerberos.wrappers import gss_unwrap_kerberos
from .ntlm.auth_authenticate import NTLMAuthAuthenticate
from .ntlm.auth_challange import NTLMAuthChallenge
from .ntlm.auth_negotiate import NTLMAuthNegotiate
Expand Down Expand Up @@ -206,51 +206,37 @@ async def _negotiate_kerberos(self):

self._kerberos_cache.write(self._tgt, self._tgs, logger)

async def negotiate_kerberos(self, max_retry: int = 3) -> Protocol:
async def negotiate_kerberos(self) -> Protocol:
if not self._domain:
raise Exception('domain is required for Kerberos authentication')
attempt = 1
has_keys = self._tgt is not None and self._tgt is not None
while True:
if self._protocol is None:
await self.connect(timeout=self._timeout)
assert self._protocol
proto = self._protocol
proto._auth_level = RPC_C_AUTHN_LEVEL_PKT_PRIVACY
proto._context_id = 0x0001357f

if not has_keys:
logger.info('Start Kerberos negotion for TGT/TGS')
await self._negotiate_kerberos()
has_keys = True
else:
logger.info('Using Kerberos TGT/TGS from cache')
try:
await self._bind_kerberos(IID_IRemoteSCMActivator, proto)
iface = await self._if_binding(proto,
bind_func=self._bind_kerberos,
m_auth_level=proto._auth_level)
except (AccessDenied, NoNewActiveKey, BindNak) as e:
msg = str(e) or type(e).__name__
if attempt <= max_retry:
logger.info(f'{msg} (attempt {attempt}/{max_retry})')
if attempt % 2 == 0:
has_keys = False # attemp to get new keys
self.close()
await asyncio.sleep(0.1 * attempt)
attempt += 1
continue
raise
else:
break
has_keys = self._tgt is not None and self._tgs is not None
if self._protocol is None:
await self.connect(timeout=self._timeout)
assert self._protocol
proto = self._protocol
proto._auth_level = RPC_C_AUTHN_LEVEL_PKT_PRIVACY
proto._context_id = 79231

if not has_keys:
logger.info('Start Kerberos negotiation for TGT/TGS')
await self._negotiate_kerberos()
has_keys = True
else:
logger.info('Using Kerberos TGT/TGS from cache')

await self._bind_kerberos(IID_IRemoteSCMActivator, proto)
iface = await self._if_binding(proto,
bind_func=self._bind_kerberos,
m_auth_level=proto._auth_level)
return iface

async def _bind_kerberos(self, iid: bytes, proto: Protocol):
ticket, service_session_key = self._tgs
ticket, service_session_key, etype = self._tgs
ap_req = build_ap_req(self._username,
self._domain,
ticket, service_session_key)
ticket,
service_session_key,
etype)
auth_value = wrap_gss_kerberos(ap_req)
bind_pkg = proto._dcom.get_bind_kerberos_pkg(
iid,
Expand All @@ -260,13 +246,22 @@ async def _bind_kerberos(self, iid: bytes, proto: Protocol):
)
rpc_bind_ack = await proto.get_dcom_response(bind_pkg)
active_key, seq_number = get_active_key(rpc_bind_ack.auth.auth_value,
service_session_key)
service_session_key,
etype)
if active_key is None:
raise NoNewActiveKey()
logger.warning("No new active key, use existing service key")
active_key = service_session_key

if seq_number is None:
logger.warning(
"No new seq_number; "
f"proto._dcom._seq_num = {proto._dcom._seq_num}")
seq_number = proto._dcom.get_seq_num()

proto._auth_type = rpc_bind_ack.auth.auth_type
proto._auth_level = rpc_bind_ack.auth.auth_level

neg_token = get_neg_token(service_session_key, seq_number)
neg_token = get_neg_token(service_session_key, seq_number, etype)
alter_context_pkg = build_alter_context(
iid,
proto._dcom.get_new_call_id(),
Expand All @@ -275,11 +270,10 @@ async def _bind_kerberos(self, iid: bytes, proto: Protocol):
neg_token,
)
_ = await proto.get_dcom_response(alter_context_pkg)

if proto._auth_level >= RPC_C_AUTHN_LEVEL_PKT_INTEGRITY:
proto._client_sign = sign_func_kerberos(active_key)
proto._client_seal = seal_func_kerberos(active_key)
proto._server_seal = gss_unwrap_kerberos(active_key)
if active_key and proto._auth_level >= RPC_C_AUTHN_LEVEL_PKT_INTEGRITY:
proto._client_sign = sign_func_kerberos(active_key, etype)
proto._client_seal = seal_func_kerberos(active_key, etype)
proto._server_seal = gss_unwrap_kerberos(active_key, etype)

async def negotiate_ntlm(self) -> Protocol:
proto = self._protocol
Expand All @@ -304,7 +298,7 @@ async def login_ntlm(
request.set_pdu_data(ntlm_login_pkg)

request_pkg = request.sign_data(proto)

# print(request_pkg)
rpc_response: RpcResponse = \
await proto.get_dcom_response(
request_pkg,
Expand All @@ -329,6 +323,7 @@ async def _if_binding(self,
IID_IWbemLevel1Login)

rci_pkg = remote_create_instance.get_data()
context_id = proto._context_id

request = RpcRequest(op_num=4)
request.set_pdu_data(rci_pkg)
Expand Down Expand Up @@ -376,6 +371,6 @@ async def _if_binding(self,
proto._auth_level = max(
interface.scm_reply_info_data.authn_hint,
m_auth_level)

proto._context_id = context_id + 1
await bind_func(IID_IWbemLevel1Login, proto)
return proto
Loading