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

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

## 176 statements

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.

26

27 import datetime

28 import socket

29 import re

30 import struct

31

32 from pyasn1.codec.der import decoder

33

34 from . import asn1

35 from . import constants

36

37

38 class KerberosException(Exception):

39 pass

40

41 def _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

47

48# A principal can be represented as:

49

50 class 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

56

57 If 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

62

63 if value is None:

64 return

65

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')

72

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")

81

82 def unquote_component(comp):

83 return re.sub(r'\\(.)', r'\1', comp)

84

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

89

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")

103

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

106

107 def __eq__(self, other):

108 if isinstance (other, str):

109 other = Principal (other)

110

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

115

116 def __str__(self):

117 def quote_component(comp):

118 return re.sub(r'([\\/@])', r'\\\1', comp)

119

120 ret = "/".join([quote_component(c) for c in self.components])

121 if self.realm is not None:

122 ret += "@" + self.realm

123

124 return ret

125

126 def __repr__(self):

127 return "Principal((" + repr(self.components) + ", " + \

128 repr(self.realm) + "), t=" + str(self.type) + ")"

129

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

138

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)

145

146 return name

147

148 class Address(object):

149 DIRECTIONAL_AP_REQ_SENDER = struct.pack('!I', 0)

150 DIRECTIONAL_AP_REQ_RECIPIENT = struct.pack('!I', 1)

151

152 def __init__(self):

153 self.type = None

154 self.data = None

155

156 def __str__(self):

157 family = self.family

158

159 if family is not None:

160 return str((family, self.address))

161 else:

162 return str((self.type, self.value))

163

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

172

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

181

182 def encode(self):

183 # ipv4-mapped ipv6 addresses must be encoded as ipv4.

184 pass

185

186 class EncryptedData(object):

187 def __init__(self):

188 self.etype = None

189 self.kvno = None

190 self.ciphertext = None

191

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

202

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

209

210 class 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

217

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

226

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

234

235 def __str__(self):

236 return "<Ticket for %s vno %s>" % (str(self.service_principal), str(self.encrypted_part.kvno))

237

238 class KerberosTime(object):

239 INDEFINITE = datetime.datetime(1970, 1, 1, 0, 0, 0)

240

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.

245

246 return "%04d%02d%02d%02d%02d%02dZ" % (dt.year, dt.month, dt.day, dt.hour, dt.minute, dt.second)

247

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)

260

261 if __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"))