s4:mitkdc: Add support for MIT Kerberos 1.20
[Samba.git] / python / samba / tests / krb5 / compatability_tests.py
blobb862f381bc5bb5c08812c14ec58b42bf24ed318e
1 #!/usr/bin/env python3
2 # Unix SMB/CIFS implementation.
3 # Copyright (C) Stefan Metzmacher 2020
4 # Copyright (C) Catalyst.Net Ltd 2020
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/>.
20 import sys
21 import os
23 sys.path.insert(0, "bin/python")
24 os.environ["PYTHONUNBUFFERED"] = "1"
26 from samba.tests.krb5.kdc_base_test import KDCBaseTest
27 import samba.tests.krb5.rfc4120_pyasn1 as krb5_asn1
28 from samba.tests.krb5.rfc4120_constants import (
29 AES128_CTS_HMAC_SHA1_96,
30 AES256_CTS_HMAC_SHA1_96,
31 ARCFOUR_HMAC_MD5,
32 KDC_ERR_PREAUTH_REQUIRED,
33 KRB_AS_REP,
34 KRB_ERROR,
35 KU_AS_REP_ENC_PART,
36 KU_PA_ENC_TIMESTAMP,
37 PADATA_ENC_TIMESTAMP,
38 PADATA_ETYPE_INFO2,
39 NT_PRINCIPAL,
40 NT_SRV_INST,
43 global_asn1_print = False
44 global_hexdump = False
46 HIEMDAL_ENC_AS_REP_PART_TYPE_TAG = 0x79
47 # MIT uses the EncTGSRepPart tag for the EncASRepPart
48 MIT_ENC_AS_REP_PART_TYPE_TAG = 0x7A
50 ENC_PA_REP_FLAG = 0x00010000
53 class SimpleKerberosTests(KDCBaseTest):
55 def setUp(self):
56 super(SimpleKerberosTests, self).setUp()
57 self.do_asn1_print = global_asn1_print
58 self.do_hexdump = global_hexdump
60 def test_mit_EncASRepPart_tag(self):
61 creds = self.get_user_creds()
62 (enc, _) = self.as_req(creds)
63 self.assertEqual(MIT_ENC_AS_REP_PART_TYPE_TAG, enc[0])
65 def test_heimdal_EncASRepPart_tag(self):
66 creds = self.get_user_creds()
67 (enc, _) = self.as_req(creds)
68 self.assertEqual(HIEMDAL_ENC_AS_REP_PART_TYPE_TAG, enc[0])
70 def test_mit_EncryptedData_kvno(self):
71 creds = self.get_user_creds()
72 (_, enc) = self.as_req(creds)
73 if 'kvno' in enc:
74 self.fail("kvno present in EncryptedData")
76 def test_heimdal_EncryptedData_kvno(self):
77 creds = self.get_user_creds()
78 (_, enc) = self.as_req(creds)
79 if 'kvno' not in enc:
80 self.fail("kvno absent in EncryptedData")
82 def test_mit_EncASRepPart_FAST_support(self):
83 creds = self.get_user_creds()
84 (enc, _) = self.as_req(creds)
85 self.assertEqual(MIT_ENC_AS_REP_PART_TYPE_TAG, enc[0])
86 as_rep = self.der_decode(enc, asn1Spec=krb5_asn1.EncTGSRepPart())
87 flags = int(as_rep['flags'], base=2)
88 # MIT sets enc-pa-rep, flag bit 15
89 # RFC 6806 11. Negotiation of FAST and Detecting Modified Requests
90 self.assertTrue(ENC_PA_REP_FLAG & flags)
92 def test_heimdal_and_windows_EncASRepPart_FAST_support(self):
93 creds = self.get_user_creds()
94 (enc, _) = self.as_req(creds)
95 self.assertEqual(HIEMDAL_ENC_AS_REP_PART_TYPE_TAG, enc[0])
96 as_rep = self.der_decode(enc, asn1Spec=krb5_asn1.EncASRepPart())
97 flags = as_rep['flags']
98 flags = int(as_rep['flags'], base=2)
99 # Heimdal and Windows does set enc-pa-rep, flag bit 15
100 # RFC 6806 11. Negotiation of FAST and Detecting Modified Requests
101 self.assertTrue(ENC_PA_REP_FLAG & flags)
103 def test_mit_arcfour_salt(self):
104 creds = self.get_user_creds()
105 etypes = (ARCFOUR_HMAC_MD5,)
106 (rep, *_) = self.as_pre_auth_req(creds, etypes)
107 self.check_preauth_rep(rep)
108 etype_info2 = self.get_etype_info2(rep)
109 if 'salt' not in etype_info2[0]:
110 self.fail(
111 "(MIT) Salt not populated for ARCFOUR_HMAC_MD5 encryption")
113 def test_heimdal_arcfour_salt(self):
114 creds = self.get_user_creds()
115 etypes = (ARCFOUR_HMAC_MD5,)
116 (rep, *_) = self.as_pre_auth_req(creds, etypes)
117 self.check_preauth_rep(rep)
118 etype_info2 = self.get_etype_info2(rep)
119 if 'salt' in etype_info2[0]:
120 self.fail(
121 "(Heimdal) Salt populated for ARCFOUR_HMAC_MD5 encryption")
123 # This tests also passes again Samba AD built with MIT Kerberos 1.20 which
124 # is not released yet.
126 # FIXME: Should be moved to to a new kdc_tgt_tests.py once MIT KRB5 1.20
127 # is released.
128 def test_ticket_signature(self):
129 # Ensure that a DC correctly issues tickets signed with its krbtgt key.
130 user_creds = self.get_client_creds()
131 target_creds = self.get_service_creds()
133 krbtgt_creds = self.get_krbtgt_creds()
134 key = self.TicketDecryptionKey_from_creds(krbtgt_creds)
136 # Get a TGT from the DC.
137 tgt = self.get_tgt(user_creds)
139 # Ensure the PAC contains the expected checksums.
140 self.verify_ticket(tgt, key, service_ticket=False)
142 # Get a service ticket from the DC.
143 service_ticket = self.get_service_ticket(tgt, target_creds)
145 # Ensure the PAC contains the expected checksums.
146 self.verify_ticket(service_ticket, key, service_ticket=True,
147 expect_ticket_checksum=True)
149 def test_mit_pre_1_20_ticket_signature(self):
150 # Ensure that a DC does not issue tickets signed with its krbtgt key.
151 user_creds = self.get_client_creds()
152 target_creds = self.get_service_creds()
154 krbtgt_creds = self.get_krbtgt_creds()
155 key = self.TicketDecryptionKey_from_creds(krbtgt_creds)
157 # Get a TGT from the DC.
158 tgt = self.get_tgt(user_creds)
160 # Ensure the PAC contains the expected checksums.
161 self.verify_ticket(tgt, key, service_ticket=False)
163 # Get a service ticket from the DC.
164 service_ticket = self.get_service_ticket(tgt, target_creds)
166 # Ensure the PAC does not contain the expected checksums.
167 self.verify_ticket(service_ticket, key, service_ticket=True,
168 expect_ticket_checksum=False)
170 def as_pre_auth_req(self, creds, etypes):
171 user = creds.get_username()
172 realm = creds.get_realm()
174 cname = self.PrincipalName_create(
175 name_type=NT_PRINCIPAL,
176 names=[user])
177 sname = self.PrincipalName_create(
178 name_type=NT_SRV_INST,
179 names=["krbtgt", realm])
181 till = self.get_KerberosTime(offset=36000)
183 kdc_options = krb5_asn1.KDCOptions('forwardable')
184 padata = None
186 req = self.AS_REQ_create(padata=padata,
187 kdc_options=str(kdc_options),
188 cname=cname,
189 realm=realm,
190 sname=sname,
191 from_time=None,
192 till_time=till,
193 renew_time=None,
194 nonce=0x7fffffff,
195 etypes=etypes,
196 addresses=None,
197 additional_tickets=None)
198 rep = self.send_recv_transaction(req)
200 return (rep, cname, sname, realm, till)
202 def check_preauth_rep(self, rep):
203 self.assertIsNotNone(rep)
204 self.assertEqual(rep['msg-type'], KRB_ERROR)
205 self.assertEqual(rep['error-code'], KDC_ERR_PREAUTH_REQUIRED)
207 def get_etype_info2(self, rep):
209 rep_padata = self.der_decode(
210 rep['e-data'],
211 asn1Spec=krb5_asn1.METHOD_DATA())
213 for pa in rep_padata:
214 if pa['padata-type'] == PADATA_ETYPE_INFO2:
215 etype_info2 = pa['padata-value']
216 break
218 etype_info2 = self.der_decode(
219 etype_info2,
220 asn1Spec=krb5_asn1.ETYPE_INFO2())
221 return etype_info2
223 def as_req(self, creds):
224 etypes = (
225 AES256_CTS_HMAC_SHA1_96,
226 AES128_CTS_HMAC_SHA1_96,
227 ARCFOUR_HMAC_MD5)
228 (rep, cname, sname, realm, till) = self.as_pre_auth_req(creds, etypes)
229 self.check_preauth_rep(rep)
231 etype_info2 = self.get_etype_info2(rep)
232 key = self.PasswordKey_from_etype_info2(creds, etype_info2[0])
234 (patime, pausec) = self.get_KerberosTimeWithUsec()
235 pa_ts = self.PA_ENC_TS_ENC_create(patime, pausec)
236 pa_ts = self.der_encode(pa_ts, asn1Spec=krb5_asn1.PA_ENC_TS_ENC())
238 pa_ts = self.EncryptedData_create(key, KU_PA_ENC_TIMESTAMP, pa_ts)
239 pa_ts = self.der_encode(pa_ts, asn1Spec=krb5_asn1.EncryptedData())
241 pa_ts = self.PA_DATA_create(PADATA_ENC_TIMESTAMP, pa_ts)
243 kdc_options = krb5_asn1.KDCOptions('forwardable')
244 padata = [pa_ts]
246 req = self.AS_REQ_create(padata=padata,
247 kdc_options=str(kdc_options),
248 cname=cname,
249 realm=realm,
250 sname=sname,
251 from_time=None,
252 till_time=till,
253 renew_time=None,
254 nonce=0x7fffffff,
255 etypes=etypes,
256 addresses=None,
257 additional_tickets=None)
258 rep = self.send_recv_transaction(req)
259 self.assertIsNotNone(rep)
261 msg_type = rep['msg-type']
262 self.assertEqual(msg_type, KRB_AS_REP)
264 enc_part = rep['enc-part']
265 enc_as_rep_part = key.decrypt(
266 KU_AS_REP_ENC_PART, rep['enc-part']['cipher'])
267 return (enc_as_rep_part, enc_part)
270 if __name__ == "__main__":
271 global_asn1_print = False
272 global_hexdump = False
273 import unittest
274 unittest.main()