1# Copyright: (c) 2020, Jordan Borean (@jborean93) <jborean93@gmail.com> 2# MIT License (see LICENSE or https://opensource.org/licenses/MIT) 3 4import enum 5import struct 6import typing 7 8 9def _pack_value(addr_type: typing.Optional["AddressType"], b: typing.Optional[bytes]) -> bytes: 10 """ Packs an type/data entry into the byte structure required. """ 11 if not b: 12 b = b"" 13 14 return (struct.pack("<I", addr_type) if addr_type is not None else b"") + struct.pack("<I", len(b)) + b 15 16 17def _unpack_value(b_mem: memoryview, offset: int) -> typing.Tuple[bytes, int]: 18 """ Unpacks a raw C struct value to a byte string. """ 19 length = struct.unpack("<I", b_mem[offset:offset + 4].tobytes())[0] 20 new_offset = offset + length + 4 21 22 data = b"" 23 if length: 24 data = b_mem[offset + 4:offset + 4 + length].tobytes() 25 26 return data, new_offset 27 28 29class AddressType(enum.IntEnum): 30 unspecified = 0 # GSS_C_AF_UNSPEC 31 local = 1 # GSS_C_AF_LOCAL 32 inet = 2 # GSS_C_AF_INET 33 implink = 3 # GSS_C_AF_IMPLINK 34 pup = 4 # GSS_C_AF_PUP 35 chaos = 5 # GSS_C_AF_CHAOS 36 ns = 6 # GSS_C_AF_NS 37 nbs = 8 # GSS_C_AF_NBS 38 ecma = 8 # GSS_C_AF_ECMA 39 datakit = 9 # GSS_C_AF_DATAKIT 40 ccitt = 10 # GSS_C_AF_CCITT 41 sna = 11 # GSS_C_AF_SNA 42 decnet = 12 # GSS_C_AF_DECnet 43 dli = 13 # GSS_C_AF_DLI 44 lat = 14 # GSS_C_AF_LAT 45 hylink = 15 # GSS_C_AF_HYLINK 46 appletalk = 16 # GSS_C_AF_APPLETALK 47 bsc = 17 # GSS_C_AF_BSC 48 dss = 18 # GSS_C_AF_DSS 49 osi = 19 # GSS_C_AF_OSI 50 x25 = 21 # GSS_C_AF_X25 51 inet6 = 24 # GSS_C_AF_INET6 52 nulladdr = 255 # GSS_C_AF_NULLADDR 53 54 55class GssChannelBindings: 56 """Python representation of a GSSAPI Channel Binding data structure. 57 58 A common representation for a GSSAPI Channel Binding data structure that can be passed into a context to bind 59 against that security context. Channel bindings are tags that identify the particular data channel that is used. 60 Because these tags are specific to the originator and recipient applications, they offer more proof of a valid 61 identity. Most HTTPS based authentications just set the application data to b'tls-server-end-point:<cert hash>'. 62 63 Args: 64 initiator_addrtype: The address type of the initiator address. 65 initiator_address: The initiator's address. 66 acceptor_addrtype: The address type of the acceptor address. 67 acceptor_address: The acceptor's address. 68 application_data: Any extra application data to set on the bindings struct. 69 """ 70 71 def __init__( 72 self, 73 initiator_addrtype: AddressType = AddressType.unspecified, 74 initiator_address: typing.Optional[bytes] = None, 75 acceptor_addrtype: AddressType = AddressType.unspecified, 76 acceptor_address: typing.Optional[bytes] = None, 77 application_data: typing.Optional[bytes] = None, 78 ) -> None: 79 self.initiator_addrtype = AddressType(initiator_addrtype) 80 self.initiator_address = initiator_address 81 self.acceptor_addrtype = AddressType(acceptor_addrtype) 82 self.acceptor_address = acceptor_address 83 self.application_data = application_data 84 85 def __repr__(self) -> str: 86 return "{0}.{1} initiator_addrtype={2}|initiator_address={3}|acceptor_addrtype={4}|acceptor_address={5}|" \ 87 "application_data={6}".format(type(self).__module__, type(self).__name__, repr(self.initiator_addrtype), 88 repr(self.initiator_address), repr(self.acceptor_addrtype), 89 repr(self.acceptor_address), repr(self.application_data)) 90 91 def __str__(self) -> str: 92 return "{0} initiator_addr({1}|{2!r}) | acceptor_addr({3}|{4!r}) | application_data({5!r})".format( 93 type(self).__name__, str(self.initiator_addrtype), self.initiator_address, str(self.acceptor_addrtype), 94 self.acceptor_address, self.application_data 95 ) 96 97 def __eq__(self, other: object) -> bool: 98 if not isinstance(other, (bytes, GssChannelBindings)): 99 return False 100 101 if isinstance(other, GssChannelBindings): 102 other = other.pack() 103 104 return self.pack() == other 105 106 def pack(self) -> bytes: 107 """ Pack struct into a byte string. """ 108 return b"".join([ 109 _pack_value(self.initiator_addrtype, self.initiator_address), 110 _pack_value(self.acceptor_addrtype, self.acceptor_address), 111 _pack_value(None, self.application_data) 112 ]) 113 114 @staticmethod 115 def unpack(b_data: bytes) -> "GssChannelBindings": 116 b_mem = memoryview(b_data) 117 118 initiator_addrtype = struct.unpack("<I", b_mem[:4].tobytes())[0] 119 initiator_address, offset = _unpack_value(b_mem, 4) 120 121 acceptor_addrtype = struct.unpack("<I", b_mem[offset:offset + 4].tobytes())[0] 122 acceptor_address, offset = _unpack_value(b_mem, offset + 4) 123 124 application_data = _unpack_value(b_mem, offset)[0] 125 126 return GssChannelBindings(initiator_addrtype=initiator_addrtype, initiator_address=initiator_address, 127 acceptor_addrtype=acceptor_addrtype, acceptor_address=acceptor_address, 128 application_data=application_data) 129