tests/krb5: Use consistent ordering for etypes
[samba.git] / python / samba / tests / krb5 / as_req_tests.py
1 #!/usr/bin/env python3
2 # Unix SMB/CIFS implementation.
3 # Copyright (C) Stefan Metzmacher 2020
4 #
5 # This program is free software; you can redistribute it and/or modify
6 # it under the terms of the GNU General Public License as published by
7 # the Free Software Foundation; either version 3 of the License, or
8 # (at your option) any later version.
9 #
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 # GNU General Public License for more details.
14 #
15 # You should have received a copy of the GNU General Public License
16 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
17 #
18
19 import sys
20 import os
21
22 sys.path.insert(0, "bin/python")
23 os.environ["PYTHONUNBUFFERED"] = "1"
24
25 from samba.tests import DynamicTestCase
26 from samba.tests.krb5.kdc_base_test import KDCBaseTest
27 import samba.tests.krb5.kcrypto as kcrypto
28 import samba.tests.krb5.rfc4120_pyasn1 as krb5_asn1
29 from samba.tests.krb5.rfc4120_constants import (
30     KDC_ERR_C_PRINCIPAL_UNKNOWN,
31     KDC_ERR_S_PRINCIPAL_UNKNOWN,
32     KDC_ERR_ETYPE_NOSUPP,
33     KDC_ERR_PREAUTH_REQUIRED,
34     KU_PA_ENC_TIMESTAMP,
35     NT_ENTERPRISE_PRINCIPAL,
36     NT_PRINCIPAL,
37     NT_SRV_INST,
38     PADATA_ENC_TIMESTAMP
39 )
40
41 global_asn1_print = False
42 global_hexdump = False
43
44
45 class AsReqBaseTest(KDCBaseTest):
46     def _run_as_req_enc_timestamp(self, client_creds, client_account=None,
47                                   expected_cname=None, sname=None,
48                                   name_type=NT_PRINCIPAL, etypes=None,
49                                   expected_error=None, expect_edata=None,
50                                   expected_pa_error=None, expect_pa_edata=None,
51                                   kdc_options=None, till=None):
52         user_name = client_creds.get_username()
53         if client_account is None:
54             client_account = user_name
55         client_as_etypes = self.get_default_enctypes()
56         client_kvno = client_creds.get_kvno()
57         krbtgt_creds = self.get_krbtgt_creds(require_strongest_key=True)
58         krbtgt_account = krbtgt_creds.get_username()
59         krbtgt_supported_etypes = krbtgt_creds.tgs_supported_enctypes
60         realm = krbtgt_creds.get_realm()
61
62         cname = self.PrincipalName_create(name_type=name_type,
63                                           names=client_account.split('/'))
64         if sname is None:
65             sname = self.PrincipalName_create(name_type=NT_SRV_INST,
66                                               names=[krbtgt_account, realm])
67
68         expected_crealm = realm
69         if expected_cname is None:
70             expected_cname = cname
71         expected_srealm = realm
72         expected_sname = sname
73         expected_salt = client_creds.get_salt()
74
75         if till is None:
76             till = self.get_KerberosTime(offset=36000)
77
78         if etypes is None:
79             etypes = client_as_etypes
80         if kdc_options is None:
81             kdc_options = krb5_asn1.KDCOptions('forwardable')
82         if expected_error is not None:
83             initial_error_mode = expected_error
84         else:
85             initial_error_mode = KDC_ERR_PREAUTH_REQUIRED
86
87         rep, kdc_exchange_dict = self._test_as_exchange(
88             cname,
89             realm,
90             sname,
91             till,
92             client_as_etypes,
93             initial_error_mode,
94             expected_crealm,
95             expected_cname,
96             expected_srealm,
97             expected_sname,
98             expected_salt,
99             etypes,
100             None,
101             kdc_options,
102             expected_supported_etypes=krbtgt_supported_etypes,
103             expected_account_name=user_name,
104             pac_request=True,
105             expect_edata=expect_edata)
106
107         if expected_error is not None:
108             return None
109
110         etype_info2 = kdc_exchange_dict['preauth_etype_info2']
111         self.assertIsNotNone(etype_info2)
112
113         preauth_key = self.PasswordKey_from_etype_info2(client_creds,
114                                                         etype_info2[0],
115                                                         kvno=client_kvno)
116
117         (patime, pausec) = self.get_KerberosTimeWithUsec()
118         pa_ts = self.PA_ENC_TS_ENC_create(patime, pausec)
119         pa_ts = self.der_encode(pa_ts, asn1Spec=krb5_asn1.PA_ENC_TS_ENC())
120
121         enc_pa_ts_usage = KU_PA_ENC_TIMESTAMP
122         pa_ts = self.EncryptedData_create(preauth_key, enc_pa_ts_usage, pa_ts)
123         pa_ts = self.der_encode(pa_ts, asn1Spec=krb5_asn1.EncryptedData())
124
125         pa_ts = self.PA_DATA_create(PADATA_ENC_TIMESTAMP, pa_ts)
126
127         preauth_padata = [pa_ts]
128         preauth_error_mode = 0 # AS-REP
129         if expected_pa_error is not None:
130             preauth_error_mode = expected_pa_error
131
132         krbtgt_decryption_key = (
133             self.TicketDecryptionKey_from_creds(krbtgt_creds))
134
135         as_rep, kdc_exchange_dict = self._test_as_exchange(
136             cname,
137             realm,
138             sname,
139             till,
140             client_as_etypes,
141             preauth_error_mode,
142             expected_crealm,
143             expected_cname,
144             expected_srealm,
145             expected_sname,
146             expected_salt,
147             etypes,
148             preauth_padata,
149             kdc_options,
150             expected_supported_etypes=krbtgt_supported_etypes,
151             expected_account_name=user_name,
152             expect_edata=expect_pa_edata,
153             preauth_key=preauth_key,
154             ticket_decryption_key=krbtgt_decryption_key,
155             pac_request=True)
156         self.assertIsNotNone(as_rep)
157
158         return etype_info2
159
160
161 @DynamicTestCase
162 class AsReqKerberosTests(AsReqBaseTest):
163
164     @classmethod
165     def setUpDynamicTestCases(cls):
166         for (name, idx) in cls.etype_test_permutation_name_idx():
167             for pac in [None, True, False]:
168                 tname = "%s_pac_%s" % (name, pac)
169                 targs = (idx, pac)
170                 cls.generate_dynamic_test("test_as_req_no_preauth", tname, *targs)
171
172     def setUp(self):
173         super(AsReqKerberosTests, self).setUp()
174         self.do_asn1_print = global_asn1_print
175         self.do_hexdump = global_hexdump
176
177     def _test_as_req_nopreauth(self,
178                                initial_etypes,
179                                pac=None,
180                                initial_kdc_options=None):
181         client_creds = self.get_client_creds()
182         client_account = client_creds.get_username()
183         client_as_etypes = self.get_default_enctypes()
184         krbtgt_creds = self.get_krbtgt_creds(require_keys=False)
185         krbtgt_account = krbtgt_creds.get_username()
186         realm = krbtgt_creds.get_realm()
187
188         cname = self.PrincipalName_create(name_type=NT_PRINCIPAL,
189                                           names=[client_account])
190         sname = self.PrincipalName_create(name_type=NT_SRV_INST,
191                                           names=[krbtgt_account, realm])
192
193         expected_crealm = realm
194         expected_cname = cname
195         expected_srealm = realm
196         expected_sname = sname
197         expected_salt = client_creds.get_salt()
198
199         if any(etype in client_as_etypes and etype in initial_etypes
200                for etype in (kcrypto.Enctype.AES256,
201                              kcrypto.Enctype.AES128,
202                              kcrypto.Enctype.RC4)):
203             expected_error_mode = KDC_ERR_PREAUTH_REQUIRED
204         else:
205             expected_error_mode = KDC_ERR_ETYPE_NOSUPP
206
207         kdc_exchange_dict = self.as_exchange_dict(
208             expected_crealm=expected_crealm,
209             expected_cname=expected_cname,
210             expected_srealm=expected_srealm,
211             expected_sname=expected_sname,
212             generate_padata_fn=None,
213             check_error_fn=self.generic_check_kdc_error,
214             check_rep_fn=None,
215             expected_error_mode=expected_error_mode,
216             client_as_etypes=client_as_etypes,
217             expected_salt=expected_salt,
218             kdc_options=str(initial_kdc_options),
219             pac_request=pac)
220
221         self._generic_kdc_exchange(kdc_exchange_dict,
222                                    cname=cname,
223                                    realm=realm,
224                                    sname=sname,
225                                    etypes=initial_etypes)
226
227     def _test_as_req_no_preauth_with_args(self, etype_idx, pac):
228         name, etypes = self.etype_test_permutation_by_idx(etype_idx)
229         self._test_as_req_nopreauth(
230                      pac=pac,
231                      initial_etypes=etypes,
232                      initial_kdc_options=krb5_asn1.KDCOptions('forwardable'))
233
234     def test_as_req_enc_timestamp(self):
235         client_creds = self.get_client_creds()
236         self._run_as_req_enc_timestamp(client_creds)
237
238     def test_as_req_enc_timestamp_mac(self):
239         client_creds = self.get_mach_creds()
240         self._run_as_req_enc_timestamp(client_creds)
241
242     def test_as_req_enc_timestamp_rc4(self):
243         client_creds = self.get_client_creds()
244         self._run_as_req_enc_timestamp(
245             client_creds,
246             etypes=(kcrypto.Enctype.RC4,))
247
248     def test_as_req_enc_timestamp_mac_rc4(self):
249         client_creds = self.get_mach_creds()
250         self._run_as_req_enc_timestamp(
251             client_creds,
252             etypes=(kcrypto.Enctype.RC4,))
253
254     def test_as_req_enc_timestamp_rc4_dummy(self):
255         client_creds = self.get_client_creds()
256         self._run_as_req_enc_timestamp(
257             client_creds,
258             etypes=(kcrypto.Enctype.RC4,
259                     -1111))
260
261     def test_as_req_enc_timestamp_mac_rc4_dummy(self):
262         client_creds = self.get_mach_creds()
263         self._run_as_req_enc_timestamp(
264             client_creds,
265             etypes=(kcrypto.Enctype.RC4,
266                     -1111))
267
268     def test_as_req_enc_timestamp_aes128_rc4(self):
269         client_creds = self.get_client_creds()
270         self._run_as_req_enc_timestamp(
271             client_creds,
272             etypes=(kcrypto.Enctype.AES128,
273                     kcrypto.Enctype.RC4))
274
275     def test_as_req_enc_timestamp_mac_aes128_rc4(self):
276         client_creds = self.get_mach_creds()
277         self._run_as_req_enc_timestamp(
278             client_creds,
279             etypes=(kcrypto.Enctype.AES128,
280                     kcrypto.Enctype.RC4))
281
282     def test_as_req_enc_timestamp_spn(self):
283         client_creds = self.get_mach_creds()
284         spn = client_creds.get_spn()
285         self._run_as_req_enc_timestamp(
286             client_creds, client_account=spn,
287             expected_error=KDC_ERR_C_PRINCIPAL_UNKNOWN,
288             expect_edata=False)
289
290     def test_as_req_enc_timestamp_spn_realm(self):
291         samdb = self.get_samdb()
292         realm = samdb.domain_dns_name().upper()
293
294         client_creds = self.get_cached_creds(
295             account_type=self.AccountType.COMPUTER,
296             opts={'upn': f'host/{{account}}.{realm}@{realm}'})
297         spn = client_creds.get_spn()
298         self._run_as_req_enc_timestamp(
299             client_creds, client_account=spn,
300             expected_error=KDC_ERR_C_PRINCIPAL_UNKNOWN,
301             expect_edata=False)
302
303     def test_as_req_enc_timestamp_spn_upn(self):
304         samdb = self.get_samdb()
305         realm = samdb.domain_dns_name().upper()
306
307         client_creds = self.get_cached_creds(
308             account_type=self.AccountType.COMPUTER,
309             opts={'upn': f'host/{{account}}.{realm}@{realm}',
310                   'spn': f'host/{{account}}.{realm}'})
311         spn = client_creds.get_spn()
312         self._run_as_req_enc_timestamp(client_creds, client_account=spn)
313
314     def test_as_req_enc_timestamp_spn_enterprise(self):
315         client_creds = self.get_mach_creds()
316         spn = client_creds.get_spn()
317         self._run_as_req_enc_timestamp(
318             client_creds, client_account=spn,
319             name_type=NT_ENTERPRISE_PRINCIPAL,
320             expected_error=KDC_ERR_C_PRINCIPAL_UNKNOWN,
321             expect_edata=False)
322
323     def test_as_req_enc_timestamp_spn_enterprise_realm(self):
324         samdb = self.get_samdb()
325         realm = samdb.domain_dns_name().upper()
326
327         client_creds = self.get_cached_creds(
328             account_type=self.AccountType.COMPUTER,
329             opts={'upn': f'host/{{account}}.{realm}@{realm}'})
330         spn = client_creds.get_spn()
331         self._run_as_req_enc_timestamp(
332             client_creds,
333             name_type=NT_ENTERPRISE_PRINCIPAL,
334             client_account=spn,
335             expected_error=KDC_ERR_C_PRINCIPAL_UNKNOWN,
336             expect_edata=False)
337
338     def test_as_req_enc_timestamp_spn_upn_enterprise(self):
339         samdb = self.get_samdb()
340         realm = samdb.domain_dns_name().upper()
341
342         client_creds = self.get_cached_creds(
343             account_type=self.AccountType.COMPUTER,
344             opts={'upn': f'host/{{account}}.{realm}@{realm}',
345                   'spn': f'host/{{account}}.{realm}'})
346         spn = client_creds.get_spn()
347         self._run_as_req_enc_timestamp(
348             client_creds,
349             name_type=NT_ENTERPRISE_PRINCIPAL,
350             client_account=spn,
351             expected_error=KDC_ERR_C_PRINCIPAL_UNKNOWN,
352             expect_edata=False)
353
354     def test_as_req_enterprise_canon(self):
355         upn = self.get_new_username()
356         client_creds = self.get_cached_creds(
357             account_type=self.AccountType.USER,
358             opts={'upn': upn})
359
360         user_name = client_creds.get_username()
361         realm = client_creds.get_realm()
362         client_account = f'{user_name}@{realm}'
363
364         expected_cname = self.PrincipalName_create(
365             name_type=NT_PRINCIPAL,
366             names=[user_name])
367
368         self._run_as_req_enc_timestamp(
369             client_creds,
370             client_account=client_account,
371             expected_cname=expected_cname,
372             name_type=NT_ENTERPRISE_PRINCIPAL,
373             kdc_options=krb5_asn1.KDCOptions('canonicalize'))
374
375     def test_as_req_enterprise_canon_case(self):
376         upn = self.get_new_username()
377         client_creds = self.get_cached_creds(
378             account_type=self.AccountType.USER,
379             opts={'upn': upn})
380
381         user_name = client_creds.get_username()
382         realm = client_creds.get_realm().lower()
383         client_account = f'{user_name}@{realm}'
384
385         expected_cname = self.PrincipalName_create(
386             name_type=NT_PRINCIPAL,
387             names=[user_name])
388
389         self._run_as_req_enc_timestamp(
390             client_creds,
391             client_account=client_account,
392             expected_cname=expected_cname,
393             name_type=NT_ENTERPRISE_PRINCIPAL,
394             kdc_options=krb5_asn1.KDCOptions('canonicalize'))
395
396     def test_as_req_enterprise_canon_mac(self):
397         upn = self.get_new_username()
398         client_creds = self.get_cached_creds(
399             account_type=self.AccountType.COMPUTER,
400             opts={'upn': upn})
401
402         user_name = client_creds.get_username()
403         realm = client_creds.get_realm()
404         client_account = f'{user_name}@{realm}'
405
406         expected_cname = self.PrincipalName_create(
407             name_type=NT_PRINCIPAL,
408             names=[user_name])
409
410         self._run_as_req_enc_timestamp(
411             client_creds,
412             client_account=client_account,
413             expected_cname=expected_cname,
414             name_type=NT_ENTERPRISE_PRINCIPAL,
415             kdc_options=krb5_asn1.KDCOptions('canonicalize'))
416
417     def test_as_req_enterprise_canon_mac_case(self):
418         upn = self.get_new_username()
419         client_creds = self.get_cached_creds(
420             account_type=self.AccountType.COMPUTER,
421             opts={'upn': upn})
422
423         user_name = client_creds.get_username()
424         realm = client_creds.get_realm().lower()
425         client_account = f'{user_name}@{realm}'
426
427         expected_cname = self.PrincipalName_create(
428             name_type=NT_PRINCIPAL,
429             names=[user_name])
430
431         self._run_as_req_enc_timestamp(
432             client_creds,
433             client_account=client_account,
434             expected_cname=expected_cname,
435             name_type=NT_ENTERPRISE_PRINCIPAL,
436             kdc_options=krb5_asn1.KDCOptions('canonicalize'))
437
438     def test_as_req_enterprise_no_canon(self):
439         upn = self.get_new_username()
440         client_creds = self.get_cached_creds(
441             account_type=self.AccountType.USER,
442             opts={'upn': upn})
443
444         user_name = client_creds.get_username()
445         realm = client_creds.get_realm()
446         client_account = f'{user_name}@{realm}'
447
448         self._run_as_req_enc_timestamp(
449             client_creds,
450             client_account=client_account,
451             name_type=NT_ENTERPRISE_PRINCIPAL,
452             kdc_options=0)
453
454     def test_as_req_enterprise_no_canon_case(self):
455         upn = self.get_new_username()
456         client_creds = self.get_cached_creds(
457             account_type=self.AccountType.USER,
458             opts={'upn': upn})
459
460         user_name = client_creds.get_username()
461         realm = client_creds.get_realm().lower()
462         client_account = f'{user_name}@{realm}'
463
464         self._run_as_req_enc_timestamp(
465             client_creds,
466             client_account=client_account,
467             name_type=NT_ENTERPRISE_PRINCIPAL,
468             kdc_options=0)
469
470     def test_as_req_enterprise_no_canon_mac(self):
471         upn = self.get_new_username()
472         client_creds = self.get_cached_creds(
473             account_type=self.AccountType.COMPUTER,
474             opts={'upn': upn})
475
476         user_name = client_creds.get_username()
477         realm = client_creds.get_realm()
478         client_account = f'{user_name}@{realm}'
479
480         self._run_as_req_enc_timestamp(
481             client_creds,
482             client_account=client_account,
483             name_type=NT_ENTERPRISE_PRINCIPAL,
484             kdc_options=0)
485
486     def test_as_req_enterprise_no_canon_mac_case(self):
487         upn = self.get_new_username()
488         client_creds = self.get_cached_creds(
489             account_type=self.AccountType.COMPUTER,
490             opts={'upn': upn})
491
492         user_name = client_creds.get_username()
493         realm = client_creds.get_realm().lower()
494         client_account = f'{user_name}@{realm}'
495
496         self._run_as_req_enc_timestamp(
497             client_creds,
498             client_account=client_account,
499             name_type=NT_ENTERPRISE_PRINCIPAL,
500             kdc_options=0)
501
502     # Ensure we can't use truncated well-known principals such as krb@REALM
503     # instead of krbtgt@REALM.
504     def test_krbtgt_wrong_principal(self):
505         client_creds = self.get_client_creds()
506
507         krbtgt_creds = self.get_krbtgt_creds()
508
509         krbtgt_account = krbtgt_creds.get_username()
510         realm = krbtgt_creds.get_realm()
511
512         # Truncate the name of the krbtgt principal.
513         krbtgt_account = krbtgt_account[:3]
514
515         wrong_krbtgt_princ = self.PrincipalName_create(
516             name_type=NT_SRV_INST,
517             names=[krbtgt_account, realm])
518
519         if self.strict_checking:
520             self._run_as_req_enc_timestamp(
521                 client_creds,
522                 sname=wrong_krbtgt_princ,
523                 expected_pa_error=KDC_ERR_S_PRINCIPAL_UNKNOWN,
524                 expect_pa_edata=False)
525         else:
526             self._run_as_req_enc_timestamp(
527                 client_creds,
528                 sname=wrong_krbtgt_princ,
529                 expected_error=KDC_ERR_S_PRINCIPAL_UNKNOWN)
530
531     # Test that we can make a request for a ticket expiring post-2038.
532     def test_future_till(self):
533         client_creds = self.get_client_creds()
534
535         self._run_as_req_enc_timestamp(
536             client_creds,
537             till='99990913024805Z')
538
539
540 if __name__ == "__main__":
541     global_asn1_print = False
542     global_hexdump = False
543     import unittest
544     unittest.main()
545