Coverage for /root/GitHubProjects/impacket/impacket/krb5/types.py : 51%

Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1# Impacket - Collection of Python classes for working with network protocols.
2#
3# SECUREAUTH LABS. Copyright (C) 2021 SecureAuth Corporation. All rights reserved.
4#
5# This software is provided under a slightly modified version
6# of the Apache Software License. See the accompanying LICENSE file
7# for more information.
8#
9# Copyright and license note from types.py:
10#
11# Copyright (c) 2013, Marc Horowitz
12# All rights reserved.
13#
14# Redistribution and use in source and binary forms, with or without
15# modification, are permitted provided that the following conditions are
16# met:
17#
18# Redistributions of source code must retain the above copyright notice,
19# this list of conditions and the following disclaimer.
20#
21# Redistributions in binary form must reproduce the above copyright
22# notice, this list of conditions and the following disclaimer in the
23# documentation and/or other materials provided with the distribution.
24#
25# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
26# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
27# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
28# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
29# HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
30# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
31# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
32# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
33# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
34# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
35# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
36#
37import datetime
38import socket
39import re
40import struct
42from pyasn1.codec.der import decoder
44from . import asn1
45from . import constants
48class KerberosException(Exception):
49 pass
51def _asn1_decode(data, asn1Spec):
52 if isinstance(data, str) or isinstance(data,bytes): 52 ↛ 53line 52 didn't jump to line 53, because the condition on line 52 was never true
53 data, substrate = decoder.decode(data, asn1Spec=asn1Spec)
54 if substrate != b'':
55 raise KerberosException("asn1 encoding invalid")
56 return data
58# A principal can be represented as:
60class Principal(object):
61 """The principal's value can be supplied as:
62* a single string
63* a sequence containing a sequence of component strings and a realm string
64* a sequence whose first n-1 elemeents are component strings and whose last
65 component is the realm
67If the value contains no realm, then default_realm will be used."""
68 def __init__(self, value=None, default_realm=None, type=None):
69 self.type = constants.PrincipalNameType.NT_UNKNOWN
70 self.components = []
71 self.realm = None
73 if value is None:
74 return
76 try: # Python 2
77 if isinstance(value, unicode): 77 ↛ 78line 77 didn't jump to line 78, because the condition on line 77 was never true
78 value = value.encode('utf-8')
79 except NameError: # Python 3
80 if isinstance(value, bytes):
81 value = value.decode('utf-8')
83 if isinstance(value, Principal): 83 ↛ 84line 83 didn't jump to line 84, because the condition on line 83 was never true
84 self.type = value.type
85 self.components = value.components[:]
86 self.realm = value.realm
87 elif isinstance(value, str): 87 ↛ 103line 87 didn't jump to line 103, because the condition on line 87 was never false
88 m = re.match(r'((?:[^\\]|\\.)+?)(@((?:[^\\@]|\\.)+))?$', value)
89 if not m: 89 ↛ 90line 89 didn't jump to line 90, because the condition on line 89 was never true
90 raise KerberosException("invalid principal syntax")
92 def unquote_component(comp):
93 return re.sub(r'\\(.)', r'\1', comp)
95 if m.group(2) is not None: 95 ↛ 96line 95 didn't jump to line 96, because the condition on line 95 was never true
96 self.realm = unquote_component(m.group(3))
97 else:
98 self.realm = default_realm
100 self.components = [
101 unquote_component(qc)
102 for qc in re.findall(r'(?:[^\\/]|\\.)+', m.group(1))]
103 elif len(value) == 2:
104 self.components = value[0]
105 self.realm = value[-1]
106 if isinstance(self.components, str):
107 self.components = [self.components]
108 elif len(value) >= 2:
109 self.components = value[0:-1]
110 self.realm = value[-1]
111 else:
112 raise KerberosException("invalid principal value")
114 if type is not None: 114 ↛ exitline 114 didn't return from function '__init__', because the condition on line 114 was never false
115 self.type = type
117 def __eq__(self, other):
118 if isinstance (other, str):
119 other = Principal (other)
121 return (self.type == constants.PrincipalNameType.NT_UNKNOWN.value or
122 other.type == constants.PrincipalNameType.NT_UNKNOWN.value or
123 self.type == other.type) and all (map (lambda a, b: a == b, self.components, other.components)) and \
124 self.realm == other.realm
126 def __str__(self):
127 def quote_component(comp):
128 return re.sub(r'([\\/@])', r'\\\1', comp)
130 ret = "/".join([quote_component(c) for c in self.components])
131 if self.realm is not None:
132 ret += "@" + self.realm
134 return ret
136 def __repr__(self):
137 return "Principal((" + repr(self.components) + ", " + \
138 repr(self.realm) + "), t=" + str(self.type) + ")"
140 def from_asn1(self, data, realm_component, name_component):
141 name = data.getComponentByName(name_component)
142 self.type = constants.PrincipalNameType(
143 name.getComponentByName('name-type')).value
144 self.components = [
145 str(c) for c in name.getComponentByName('name-string')]
146 self.realm = str(data.getComponentByName(realm_component))
147 return self
149 def components_to_asn1(self, name):
150 name.setComponentByName('name-type', int(self.type))
151 strings = name.setComponentByName('name-string'
152 ).getComponentByName('name-string')
153 for i, c in enumerate(self.components):
154 strings.setComponentByPosition(i, c)
156 return name
158class Address(object):
159 DIRECTIONAL_AP_REQ_SENDER = struct.pack('!I', 0)
160 DIRECTIONAL_AP_REQ_RECIPIENT = struct.pack('!I', 1)
162 def __init__(self):
163 self.type = None
164 self.data = None
166 def __str__(self):
167 family = self.family
169 if family is not None:
170 return str((family, self.address))
171 else:
172 return str((self.type, self.value))
174 @property
175 def family(self):
176 if self.type == constants.AddressType.IPv4.value:
177 return socket.AF_INET
178 elif self.type == constants.AddressType.IPv4.value:
179 return socket.AF_INET6
180 else:
181 return None
183 @property
184 def address(self):
185 if self.type == constants.AddressType.IPv4.value:
186 return socket.inet_pton(self.family, self.data)
187 elif self.type == constants.AddressType.IPv4.value:
188 return socket.inet_pton(self.family, self.data)
189 else:
190 return None
192 def encode(self):
193 # ipv4-mapped ipv6 addresses must be encoded as ipv4.
194 pass
196class EncryptedData(object):
197 def __init__(self):
198 self.etype = None
199 self.kvno = None
200 self.ciphertext = None
202 def from_asn1(self, data):
203 data = _asn1_decode(data, asn1.EncryptedData())
204 self.etype = constants.EncryptionTypes(data.getComponentByName('etype')).value
205 kvno = data.getComponentByName('kvno')
206 if (kvno is None) or (kvno.hasValue() is False): 206 ↛ 207line 206 didn't jump to line 207, because the condition on line 206 was never true
207 self.kvno = False
208 else:
209 self.kvno = kvno
210 self.ciphertext = str(data.getComponentByName('cipher'))
211 return self
213 def to_asn1(self, component):
214 component.setComponentByName('etype', int(self.etype))
215 if self.kvno: 215 ↛ 217line 215 didn't jump to line 217, because the condition on line 215 was never false
216 component.setComponentByName('kvno', self.kvno)
217 component.setComponentByName('cipher', self.ciphertext)
218 return component
220class Ticket(object):
221 def __init__(self):
222 # This is the kerberos version, not the service principal key
223 # version number.
224 self.tkt_vno = None
225 self.service_principal = None
226 self.encrypted_part = None
228 def from_asn1(self, data):
229 data = _asn1_decode(data, asn1.Ticket())
230 self.tkt_vno = int(data.getComponentByName('tkt-vno'))
231 self.service_principal = Principal()
232 self.service_principal.from_asn1(data, 'realm', 'sname')
233 self.encrypted_part = EncryptedData()
234 self.encrypted_part.from_asn1(data.getComponentByName('enc-part'))
235 return self
237 def to_asn1(self, component):
238 component.setComponentByName('tkt-vno', 5)
239 component.setComponentByName('realm', self.service_principal.realm)
240 asn1.seq_set(component, 'sname',
241 self.service_principal.components_to_asn1)
242 asn1.seq_set(component, 'enc-part', self.encrypted_part.to_asn1)
243 return component
245 def __str__(self):
246 return "<Ticket for %s vno %s>" % (str(self.service_principal), str(self.encrypted_part.kvno))
248class KerberosTime(object):
249 INDEFINITE = datetime.datetime(1970, 1, 1, 0, 0, 0)
251 @staticmethod
252 def to_asn1(dt):
253 # A KerberosTime is really just a string, so we can return a
254 # string here, and the asn1 library will convert it correctly.
256 return "%04d%02d%02d%02d%02d%02dZ" % (dt.year, dt.month, dt.day, dt.hour, dt.minute, dt.second)
258 @staticmethod
259 def from_asn1(data):
260 data = str(data)
261 year = int(data[0:4])
262 month = int(data[4:6])
263 day = int(data[6:8])
264 hour = int(data[8:10])
265 minute = int(data[10:12])
266 second = int(data[12:14])
267 if data[14] != 'Z':
268 raise KerberosException("timezone in KerberosTime is not Z")
269 return datetime.datetime(year, month, day, hour, minute, second)
271if __name__ == '__main__': 271 ↛ 273line 271 didn't jump to line 273, because the condition on line 271 was never true
272 # TODO marc: turn this into a real test
273 print(Principal("marc"))
274 print(Principal(("marc", None)))
275 print(Principal((("marc",), None)))
276 print(Principal("marc@ATHENA.MIT.EDU"))
277 print(Principal("marc", default_realm="ATHENA.MIT.EDU"))
278 print(Principal("marc@ATHENA.MIT.EDU", default_realm="EXAMPLE.COM"))
279 print(Principal(("marc", "ATHENA.MIT.EDU")))
280 print(Principal((("marc"), "ATHENA.MIT.EDU")))
281 print(Principal("marc/root"))
282 print(Principal(("marc", "root", "ATHENA.MIT.EDU")))
283 print(Principal((("marc", "root"), "ATHENA.MIT.EDU")))
284 print(Principal("marc\\/root"))