2 # Unix SMB/CIFS implementation.
3 # Copyright (C) Stefan Metzmacher 2020
4 # Copyright (C) 2022 Catalyst.Net Ltd
6 # This program is free software; you can redistribute it and/or modify
7 # it under the terms of the GNU General Public License as published by
8 # the Free Software Foundation; either version 3 of the License, or
9 # (at your option) any later version.
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
16 # You should have received a copy of the GNU General Public License
17 # along with this program. If not, see <http://www.gnu.org/licenses/>.
24 from samba.dcerpc import security
26 from samba.tests import DynamicTestCase
27 from samba.tests.krb5.kdc_tgs_tests import KdcTgsBaseTests
28 from samba.tests.krb5.raw_testcase import KerberosCredentials
29 from samba.tests.krb5.rfc4120_constants import (
30 AES128_CTS_HMAC_SHA1_96,
31 AES256_CTS_HMAC_SHA1_96,
36 sys.path.insert(0, "bin/python")
37 os.environ["PYTHONUNBUFFERED"] = "1"
39 global_asn1_print = False
40 global_hexdump = False
42 rc4_bit = security.KERB_ENCTYPE_RC4_HMAC_MD5
43 aes128_bit = security.KERB_ENCTYPE_AES128_CTS_HMAC_SHA1_96
44 aes256_bit = security.KERB_ENCTYPE_AES256_CTS_HMAC_SHA1_96
45 aes256_sk_bit = security.KERB_ENCTYPE_AES256_CTS_HMAC_SHA1_96_SK
46 fast_bit = security.KERB_ENCTYPE_FAST_SUPPORTED
48 etype_bits = rc4_bit | aes128_bit | aes256_bit
49 extra_bits = aes256_sk_bit | fast_bit
53 class EtypeTests(KdcTgsBaseTests):
56 self.do_asn1_print = global_asn1_print
57 self.do_hexdump = global_hexdump
59 self.default_supported_enctypes = self.default_etypes
60 if self.default_supported_enctypes is None:
62 self.default_supported_enctypes = lp.get(
63 'kdc default domain supported enctypes')
65 def _server_creds(self, supported=None):
66 return self.get_cached_creds(
67 account_type=self.AccountType.COMPUTER,
69 'supported_enctypes': supported,
72 def only_non_etype_bits_set(self, bits):
73 return bits is not None and (
75 not (bits & etype_bits))
78 def setUpDynamicTestCases(cls):
79 all_etypes = (AES256_CTS_HMAC_SHA1_96,
80 AES128_CTS_HMAC_SHA1_96,
83 # An iterator yielding all permutations consisting of at least one
85 requested_etype_cases = itertools.chain.from_iterable(
86 itertools.permutations(all_etypes, x)
87 for x in range(1, len(all_etypes) + 1))
89 # Some combinations of msDS-SupportedEncryptionTypes bits to be set on
91 supported_etype_cases = (
94 # Every possible combination of RC4, AES128, AES256, and AES256-SK.
98 aes256_sk_bit | rc4_bit,
100 aes256_bit | rc4_bit,
101 aes256_bit | aes256_sk_bit,
102 aes256_bit | aes256_sk_bit | rc4_bit,
104 aes128_bit | rc4_bit,
105 aes128_bit | aes256_sk_bit,
106 aes128_bit | aes256_sk_bit | rc4_bit,
107 aes128_bit | aes256_bit,
108 aes128_bit | aes256_bit | rc4_bit,
109 aes128_bit | aes256_bit | aes256_sk_bit,
110 aes128_bit | aes256_bit | aes256_sk_bit | rc4_bit,
111 # Some combinations with an extra bit (the FAST-supported bit) set.
114 fast_bit | aes256_sk_bit,
115 fast_bit | aes256_bit,
118 for requested_etypes in requested_etype_cases:
119 for supported_etypes in supported_etype_cases:
120 tname = (f'{supported_etypes}_supported_'
121 f'{requested_etypes}_requested')
122 targs = supported_etypes, requested_etypes
123 cls.generate_dynamic_test('test_etype_as', tname, *targs)
125 def _test_etype_as_with_args(self, supported_bits, requested_etypes):
126 # The ticket will be encrypted with the strongest enctype for which the
127 # server explicitly declares support, falling back to RC4 if the server
128 # has no declared supported encryption types. The enctype of the
129 # session key is the first enctype listed in the request that the
130 # server supports, taking the AES-SK bit as an indication of support
131 # for both AES types.
133 # If none of the enctypes in the request are supported by the target
134 # server, implicitly or explicitly, return ETYPE_NOSUPP.
138 if not supported_bits:
139 # If msDS-SupportedEncryptionTypes is missing or set to zero, the
140 # default value, provided by smb.conf, is assumed.
141 supported_bits = self.default_supported_enctypes
143 # If msDS-SupportedEncryptionTypes specifies only non-etype bits, we
145 if self.only_non_etype_bits_set(supported_bits):
146 expected_error = KDC_ERR_ETYPE_NOSUPP
148 virtual_bits = supported_bits
150 if self.forced_rc4 and not (virtual_bits & rc4_bit):
151 # If our fallback smb.conf option is set, force in RC4 support.
152 virtual_bits |= rc4_bit
154 if virtual_bits & aes256_sk_bit:
155 # If strong session keys are enabled, force in the AES bits.
156 virtual_bits |= aes256_bit | aes128_bit
158 virtual_etypes = KerberosCredentials.bits_to_etypes(virtual_bits)
160 # The enctype of the session key is the first listed in the request
161 # that the server supports, implicitly or explicitly.
162 for requested_etype in requested_etypes:
163 if requested_etype in virtual_etypes:
164 expected_session_etype = requested_etype
167 # If there is no such enctype, expect an error.
168 expected_error = KDC_ERR_ETYPE_NOSUPP
170 # Get the credentials of the client and server accounts.
171 creds = self.get_client_creds()
172 target_creds = self._server_creds(supported=supported_bits)
174 # Perform the TGS-REQ.
175 ticket = self._as_req(creds, expected_error=expected_error,
176 target_creds=target_creds,
177 etype=requested_etypes)
179 # There's no more to check. Return.
182 # We expect the ticket etype to be the strongest the server claims to
183 # support, with a fallback to RC4.
184 expected_etype = ARCFOUR_HMAC_MD5
185 if supported_bits is not None:
186 if supported_bits & aes256_bit:
187 expected_etype = AES256_CTS_HMAC_SHA1_96
188 elif supported_bits & aes128_bit:
189 expected_etype = AES128_CTS_HMAC_SHA1_96
191 # Check the etypes of the ticket and session key.
192 self.assertEqual(expected_etype, ticket.decryption_key.etype)
193 self.assertEqual(expected_session_etype, ticket.session_key.etype)
195 # Perform an AS-REQ for a service ticket, specifying AES, when the target
196 # service only supports AES. The resulting ticket should be encrypted with
197 # AES, with an AES session key.
198 def test_as_aes_supported_aes_requested(self):
199 creds = self.get_client_creds()
200 target_creds = self._server_creds(supported=aes256_bit)
202 ticket = self._as_req(creds, expected_error=0,
203 target_creds=target_creds,
204 etype=(AES256_CTS_HMAC_SHA1_96,))
206 self.assertEqual(AES256_CTS_HMAC_SHA1_96, ticket.decryption_key.etype)
207 self.assertEqual(AES256_CTS_HMAC_SHA1_96, ticket.session_key.etype)
209 # Perform an AS-REQ for a service ticket, specifying RC4, when the target
210 # service only supports AES. The request should fail with an error.
211 def test_as_aes_supported_rc4_requested(self):
212 creds = self.get_client_creds()
213 target_creds = self._server_creds(supported=aes256_bit)
217 expected_session_etype = ARCFOUR_HMAC_MD5
219 expected_error = KDC_ERR_ETYPE_NOSUPP
220 expected_session_etype = AES256_CTS_HMAC_SHA1_96
222 ticket = self._as_req(creds, expected_error=expected_error,
223 target_creds=target_creds,
224 etype=(ARCFOUR_HMAC_MD5,))
226 if not self.forced_rc4:
229 self.assertEqual(AES256_CTS_HMAC_SHA1_96, ticket.decryption_key.etype)
230 self.assertEqual(expected_session_etype, ticket.session_key.etype)
232 # Perform an AS-REQ for a service ticket, specifying AES, when the target
233 # service only supports AES, and supports AES256 session keys. The
234 # resulting ticket should be encrypted with AES, with an AES session key.
235 def test_as_aes_supported_aes_session_aes_requested(self):
236 creds = self.get_client_creds()
237 target_creds = self._server_creds(supported=aes256_bit | aes256_sk_bit)
239 ticket = self._as_req(creds, expected_error=0,
240 target_creds=target_creds,
241 etype=(AES256_CTS_HMAC_SHA1_96,))
243 self.assertEqual(AES256_CTS_HMAC_SHA1_96, ticket.decryption_key.etype)
244 self.assertEqual(AES256_CTS_HMAC_SHA1_96, ticket.session_key.etype)
246 # Perform an AS-REQ for a service ticket, specifying RC4, when the target
247 # service only supports AES, and supports AES256 session keys. The request
248 # should fail with an error.
249 def test_as_aes_supported_aes_session_rc4_requested(self):
250 creds = self.get_client_creds()
251 target_creds = self._server_creds(supported=aes256_bit | aes256_sk_bit)
255 expected_session_etype = ARCFOUR_HMAC_MD5
257 expected_error = KDC_ERR_ETYPE_NOSUPP
258 expected_session_etype = AES256_CTS_HMAC_SHA1_96
260 ticket = self._as_req(creds, expected_error=expected_error,
261 target_creds=target_creds,
262 etype=(ARCFOUR_HMAC_MD5,))
264 if not self.forced_rc4:
267 self.assertEqual(AES256_CTS_HMAC_SHA1_96, ticket.decryption_key.etype)
268 self.assertEqual(expected_session_etype, ticket.session_key.etype)
270 # Perform an AS-REQ for a service ticket, specifying AES, when the target
271 # service only supports RC4. The request should fail with an error.
272 def test_as_rc4_supported_aes_requested(self):
273 creds = self.get_client_creds()
274 target_creds = self._server_creds(supported=rc4_bit)
276 self._as_req(creds, expected_error=KDC_ERR_ETYPE_NOSUPP,
277 target_creds=target_creds,
278 etype=(AES256_CTS_HMAC_SHA1_96,))
280 # Perform an AS-REQ for a service ticket, specifying RC4, when the target
281 # service only supports RC4. The resulting ticket should be encrypted with
282 # RC4, with an RC4 session key.
283 def test_as_rc4_supported_rc4_requested(self):
284 creds = self.get_client_creds()
285 target_creds = self._server_creds(supported=rc4_bit)
287 ticket = self._as_req(creds, expected_error=0,
288 target_creds=target_creds,
289 etype=(ARCFOUR_HMAC_MD5,))
291 self.assertEqual(ARCFOUR_HMAC_MD5, ticket.decryption_key.etype)
292 self.assertEqual(ARCFOUR_HMAC_MD5, ticket.session_key.etype)
294 # Perform an AS-REQ for a service ticket, specifying AES, when the target
295 # service only supports RC4, but supports AES256 session keys. The
296 # resulting ticket should be encrypted with RC4, with an AES256 session
298 def test_as_rc4_supported_aes_session_aes_requested(self):
299 creds = self.get_client_creds()
300 target_creds = self._server_creds(supported=rc4_bit | aes256_sk_bit)
302 ticket = self._as_req(creds, expected_error=0,
303 target_creds=target_creds,
304 etype=(AES256_CTS_HMAC_SHA1_96,))
306 self.assertEqual(ARCFOUR_HMAC_MD5, ticket.decryption_key.etype)
307 self.assertEqual(AES256_CTS_HMAC_SHA1_96, ticket.session_key.etype)
309 # Perform an AS-REQ for a service ticket, specifying RC4, when the target
310 # service only supports RC4, but supports AES256 session keys. The
311 # resulting ticket should be encrypted with RC4, with an RC4 session key.
312 def test_as_rc4_supported_aes_session_rc4_requested(self):
313 creds = self.get_client_creds()
314 target_creds = self._server_creds(supported=rc4_bit | aes256_sk_bit)
316 ticket = self._as_req(creds, expected_error=0,
317 target_creds=target_creds,
318 etype=(ARCFOUR_HMAC_MD5,))
320 self.assertEqual(ARCFOUR_HMAC_MD5, ticket.decryption_key.etype)
321 self.assertEqual(ARCFOUR_HMAC_MD5, ticket.session_key.etype)
323 # Perform a TGS-REQ for a service ticket, specifying AES, when the target
324 # service only supports AES. The resulting ticket should be encrypted with
325 # AES, with an AES session key.
326 def test_tgs_aes_supported_aes_requested(self):
327 creds = self.get_client_creds()
328 tgt = self.get_tgt(creds)
330 target_creds = self._server_creds(supported=aes256_bit)
332 ticket = self._tgs_req(tgt, expected_error=0,
333 target_creds=target_creds,
334 etypes=(AES256_CTS_HMAC_SHA1_96,))
336 self.assertEqual(AES256_CTS_HMAC_SHA1_96, ticket.decryption_key.etype)
337 self.assertEqual(AES256_CTS_HMAC_SHA1_96, ticket.session_key.etype)
339 # Perform a TGS-REQ for a service ticket, specifying RC4, when the target
340 # service only supports AES. The request should fail with an error.
341 def test_tgs_aes_supported_rc4_requested(self):
342 creds = self.get_client_creds()
343 tgt = self.get_tgt(creds)
345 target_creds = self._server_creds(supported=aes256_bit)
350 expected_error = KDC_ERR_ETYPE_NOSUPP
352 ticket = self._tgs_req(tgt, expected_error=expected_error,
353 target_creds=target_creds,
354 etypes=(ARCFOUR_HMAC_MD5,))
356 if not self.forced_rc4:
359 self.assertEqual(AES256_CTS_HMAC_SHA1_96, ticket.decryption_key.etype)
360 self.assertEqual(ARCFOUR_HMAC_MD5, ticket.session_key.etype)
362 # Perform a TGS-REQ for a service ticket, specifying AES, when the target
363 # service only supports AES, and supports AES256 session keys. The
364 # resulting ticket should be encrypted with AES, with an AES session key.
365 def test_tgs_aes_supported_aes_session_aes_requested(self):
366 creds = self.get_client_creds()
367 tgt = self.get_tgt(creds)
369 target_creds = self._server_creds(supported=aes256_bit | aes256_sk_bit)
371 ticket = self._tgs_req(tgt, expected_error=0,
372 target_creds=target_creds,
373 etypes=(AES256_CTS_HMAC_SHA1_96,))
375 self.assertEqual(AES256_CTS_HMAC_SHA1_96, ticket.decryption_key.etype)
376 self.assertEqual(AES256_CTS_HMAC_SHA1_96, ticket.session_key.etype)
378 # Perform a TGS-REQ for a service ticket, specifying RC4, when the target
379 # service only supports AES, and supports AES256 session keys. The request
380 # should fail with an error.
381 def test_tgs_aes_supported_aes_session_rc4_requested(self):
382 creds = self.get_client_creds()
383 tgt = self.get_tgt(creds)
385 target_creds = self._server_creds(supported=aes256_bit | aes256_sk_bit)
390 expected_error = KDC_ERR_ETYPE_NOSUPP
392 ticket = self._tgs_req(tgt, expected_error=expected_error,
393 target_creds=target_creds,
394 etypes=(ARCFOUR_HMAC_MD5,))
396 if not self.forced_rc4:
399 self.assertEqual(AES256_CTS_HMAC_SHA1_96, ticket.decryption_key.etype)
400 self.assertEqual(ARCFOUR_HMAC_MD5, ticket.session_key.etype)
402 # Perform a TGS-REQ for a service ticket, specifying AES, when the target
403 # service only supports RC4. The request should fail with an error.
404 def test_tgs_rc4_supported_aes_requested(self):
405 creds = self.get_client_creds()
406 tgt = self.get_tgt(creds)
408 target_creds = self._server_creds(supported=rc4_bit)
410 self._tgs_req(tgt, expected_error=KDC_ERR_ETYPE_NOSUPP,
411 target_creds=target_creds,
412 etypes=(AES256_CTS_HMAC_SHA1_96,))
414 # Perform a TGS-REQ for a service ticket, specifying RC4, when the target
415 # service only supports RC4. The resulting ticket should be encrypted with
416 # RC4, with an RC4 session key.
417 def test_tgs_rc4_supported_rc4_requested(self):
418 creds = self.get_client_creds()
419 tgt = self.get_tgt(creds)
421 target_creds = self._server_creds(supported=rc4_bit)
423 ticket = self._tgs_req(tgt, expected_error=0,
424 target_creds=target_creds,
425 etypes=(ARCFOUR_HMAC_MD5,))
427 self.assertEqual(ARCFOUR_HMAC_MD5, ticket.decryption_key.etype)
428 self.assertEqual(ARCFOUR_HMAC_MD5, ticket.session_key.etype)
430 # Perform a TGS-REQ for a service ticket, specifying AES, when the target
431 # service only supports RC4, but supports AES256 session keys. The
432 # resulting ticket should be encrypted with RC4, with an AES256 session
434 def test_tgs_rc4_supported_aes_session_aes_requested(self):
435 creds = self.get_client_creds()
436 tgt = self.get_tgt(creds)
438 target_creds = self._server_creds(supported=rc4_bit | aes256_sk_bit)
440 ticket = self._tgs_req(tgt, expected_error=0,
441 target_creds=target_creds,
442 etypes=(AES256_CTS_HMAC_SHA1_96,))
444 self.assertEqual(ARCFOUR_HMAC_MD5, ticket.decryption_key.etype)
445 self.assertEqual(AES256_CTS_HMAC_SHA1_96, ticket.session_key.etype)
447 # Perform a TGS-REQ for a service ticket, specifying RC4, when the target
448 # service only supports RC4, but supports AES256 session keys. The
449 # resulting ticket should be encrypted with RC4, with an RC4 session key.
450 def test_tgs_rc4_supported_aes_session_rc4_requested(self):
451 creds = self.get_client_creds()
452 tgt = self.get_tgt(creds)
454 target_creds = self._server_creds(supported=rc4_bit | aes256_sk_bit)
456 ticket = self._tgs_req(tgt, expected_error=0,
457 target_creds=target_creds,
458 etypes=(ARCFOUR_HMAC_MD5,))
460 self.assertEqual(ARCFOUR_HMAC_MD5, ticket.decryption_key.etype)
461 self.assertEqual(ARCFOUR_HMAC_MD5, ticket.session_key.etype)
464 if __name__ == "__main__":
465 global_asn1_print = False
466 global_hexdump = False