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

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