0aa3309b81494c469d56887f0f17fe9d7291ffa6
[samba.git] / python / samba / tests / krb5 / ms_kile_client_principal_lookup_tests.py
1 #!/usr/bin/env python3
2 # Unix SMB/CIFS implementation.
3 # Copyright (C) Stefan Metzmacher 2020
4 # Copyright (C) 2020 Catalyst.Net Ltd
5 #
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.
10 #
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.
15 #
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/>.
18 #
19
20 import sys
21 import os
22
23 sys.path.insert(0, "bin/python")
24 os.environ["PYTHONUNBUFFERED"] = "1"
25
26 from samba.dsdb import UF_NORMAL_ACCOUNT, UF_DONT_REQUIRE_PREAUTH
27 from samba.tests.krb5.kdc_base_test import KDCBaseTest
28 from samba.tests.krb5.rfc4120_constants import (
29     AES256_CTS_HMAC_SHA1_96,
30     ARCFOUR_HMAC_MD5,
31     NT_ENTERPRISE_PRINCIPAL,
32     NT_PRINCIPAL,
33     NT_SRV_INST,
34     KDC_ERR_C_PRINCIPAL_UNKNOWN,
35 )
36
37 global_asn1_print = False
38 global_hexdump = False
39
40
41 class MS_Kile_Client_Principal_Lookup_Tests(KDCBaseTest):
42     ''' Tests for MS-KILE client principal look-up
43         See [MS-KILE]: Kerberos Protocol Extensions
44             secion 3.3.5.6.1 Client Principal Lookup
45     '''
46
47     def setUp(self):
48         super().setUp()
49         self.do_asn1_print = global_asn1_print
50         self.do_hexdump = global_hexdump
51
52     def check_pac(self, samdb, auth_data, dn, uc, name, upn=None):
53
54         pac_data = self.get_pac_data(auth_data)
55         sid = self.get_objectSid(samdb, dn)
56         if upn is None:
57             upn = "%s@%s" % (name, uc.get_realm().lower())
58         if name.endswith('$'):
59             name = name[:-1]
60
61         self.assertEqual(
62             uc.get_username(),
63             str(pac_data.account_name),
64             "pac_data = {%s}" % str(pac_data))
65         self.assertEqual(
66             name,
67             pac_data.logon_name,
68             "pac_data = {%s}" % str(pac_data))
69         self.assertEqual(
70             uc.get_realm(),
71             pac_data.domain_name,
72             "pac_data = {%s}" % str(pac_data))
73         self.assertEqual(
74             upn,
75             pac_data.upn,
76             "pac_data = {%s}" % str(pac_data))
77         self.assertEqual(
78             sid,
79             pac_data.account_sid,
80             "pac_data = {%s}" % str(pac_data))
81
82     def test_nt_principal_step_1(self):
83         ''' Step 1
84             For an NT_PRINCIPAL cname with no realm or the realm matches the
85             DC's domain
86                 search for an account with the
87                     sAMAccountName matching the cname.
88         '''
89
90         # Create user and machine accounts for the test.
91         #
92         samdb = self.get_samdb()
93         user_name = "mskileusr"
94         (uc, dn) = self.create_account(samdb, user_name)
95         realm = uc.get_realm().lower()
96
97         mach_name = "mskilemac"
98         (mc, _) = self.create_account(samdb, mach_name,
99                                       account_type=self.AccountType.COMPUTER)
100
101         # Do the initial AS-REQ, should get a pre-authentication required
102         # response
103         etype = (AES256_CTS_HMAC_SHA1_96, ARCFOUR_HMAC_MD5)
104         cname = self.PrincipalName_create(
105             name_type=NT_PRINCIPAL, names=[user_name])
106         sname = self.PrincipalName_create(
107             name_type=NT_SRV_INST, names=["krbtgt", realm])
108
109         rep = self.as_req(cname, sname, realm, etype)
110         self.check_pre_authentication(rep)
111
112         # Do the next AS-REQ
113         padata = self.get_enc_timestamp_pa_data(uc, rep)
114         key = self.get_as_rep_key(uc, rep)
115         rep = self.as_req(cname, sname, realm, etype, padata=[padata])
116         self.check_as_reply(rep)
117
118         # Request a ticket to the host service on the machine account
119         ticket = rep['ticket']
120         enc_part2 = self.get_as_rep_enc_data(key, rep)
121         key = self.EncryptionKey_import(enc_part2['key'])
122         cname = self.PrincipalName_create(
123             name_type=NT_PRINCIPAL,
124             names=[user_name])
125         sname = self.PrincipalName_create(
126             name_type=NT_PRINCIPAL,
127             names=[mc.get_username()])
128
129         (rep, enc_part) = self.tgs_req(
130             cname, sname, uc.get_realm(), ticket, key, etype,
131             service_creds=mc)
132         self.check_tgs_reply(rep)
133
134         # Check the contents of the pac, and the ticket
135         ticket = rep['ticket']
136         enc_part = self.decode_service_ticket(mc, ticket)
137         self.check_pac(samdb, enc_part['authorization-data'], dn, uc, user_name)
138         # check the crealm and cname
139         cname = enc_part['cname']
140         self.assertEqual(NT_PRINCIPAL, cname['name-type'])
141         self.assertEqual(user_name.encode('UTF8'), cname['name-string'][0])
142         self.assertEqual(realm.upper().encode('UTF8'), enc_part['crealm'])
143
144     def test_nt_principal_step_2(self):
145         ''' Step 2
146             If not found
147                 search for sAMAccountName equal to the cname + "$"
148
149         '''
150
151         # Create a machine account for the test.
152         #
153         samdb = self.get_samdb()
154         mach_name = "mskilemac"
155         (mc, dn) = self.create_account(samdb, mach_name,
156                                        account_type=self.AccountType.COMPUTER)
157         realm = mc.get_realm().lower()
158
159         # Do the initial AS-REQ, should get a pre-authentication required
160         # response
161         etype = (AES256_CTS_HMAC_SHA1_96, ARCFOUR_HMAC_MD5)
162         cname = self.PrincipalName_create(
163             name_type=NT_PRINCIPAL, names=[mach_name])
164         sname = self.PrincipalName_create(
165             name_type=NT_SRV_INST, names=["krbtgt", realm])
166
167         rep = self.as_req(cname, sname, realm, etype)
168         self.check_pre_authentication(rep)
169
170         # Do the next AS-REQ
171         padata = self.get_enc_timestamp_pa_data(mc, rep)
172         key = self.get_as_rep_key(mc, rep)
173         rep = self.as_req(cname, sname, realm, etype, padata=[padata])
174         self.check_as_reply(rep)
175
176         # Request a ticket to the host service on the machine account
177         ticket = rep['ticket']
178         enc_part2 = self.get_as_rep_enc_data(key, rep)
179         key = self.EncryptionKey_import(enc_part2['key'])
180         cname = self.PrincipalName_create(
181             name_type=NT_PRINCIPAL,
182             names=[mach_name])
183         sname = self.PrincipalName_create(
184             name_type=NT_PRINCIPAL,
185             names=[mc.get_username()])
186
187         (rep, enc_part) = self.tgs_req(
188             cname, sname, mc.get_realm(), ticket, key, etype,
189             service_creds=mc)
190         self.check_tgs_reply(rep)
191
192         # Check the contents of the pac, and the ticket
193         ticket = rep['ticket']
194         enc_part = self.decode_service_ticket(mc, ticket)
195         self.check_pac(samdb, enc_part['authorization-data'], dn, mc, mach_name + '$')
196         # check the crealm and cname
197         cname = enc_part['cname']
198         self.assertEqual(NT_PRINCIPAL, cname['name-type'])
199         self.assertEqual(mach_name.encode('UTF8'), cname['name-string'][0])
200         self.assertEqual(realm.upper().encode('UTF8'), enc_part['crealm'])
201
202     def test_nt_principal_step_3(self):
203         ''' Step 3
204
205             If not found
206                 search for a matching UPN name where the UPN is set to
207                     cname@realm or cname@DC's domain name
208
209         '''
210         # Create a user account for the test.
211         #
212         samdb = self.get_samdb()
213         user_name = "mskileusr"
214         upn_name = "mskileupn"
215         upn = upn_name + "@" + self.get_user_creds().get_realm().lower()
216         (uc, dn) = self.create_account(samdb, user_name, upn=upn)
217         realm = uc.get_realm().lower()
218
219         mach_name = "mskilemac"
220         (mc, _) = self.create_account(samdb, mach_name,
221                                       account_type=self.AccountType.COMPUTER)
222
223         # Do the initial AS-REQ, should get a pre-authentication required
224         # response
225         etype = (AES256_CTS_HMAC_SHA1_96, ARCFOUR_HMAC_MD5)
226         cname = self.PrincipalName_create(
227             name_type=NT_PRINCIPAL, names=[upn_name])
228         sname = self.PrincipalName_create(
229             name_type=NT_SRV_INST, names=["krbtgt", realm])
230
231         rep = self.as_req(cname, sname, realm, etype)
232         self.check_pre_authentication(rep)
233
234         # Do the next AS-REQ
235         padata = self.get_enc_timestamp_pa_data(uc, rep)
236         key = self.get_as_rep_key(uc, rep)
237         rep = self.as_req(cname, sname, realm, etype, padata=[padata])
238         self.check_as_reply(rep)
239
240         # Request a ticket to the host service on the machine account
241         ticket = rep['ticket']
242         enc_part2 = self.get_as_rep_enc_data(key, rep)
243         key = self.EncryptionKey_import(enc_part2['key'])
244         cname = self.PrincipalName_create(
245             name_type=NT_PRINCIPAL,
246             names=[upn_name])
247         sname = self.PrincipalName_create(
248             name_type=NT_PRINCIPAL,
249             names=[mc.get_username()])
250
251         (rep, enc_part) = self.tgs_req(
252             cname, sname, uc.get_realm(), ticket, key, etype,
253             service_creds=mc)
254         self.check_tgs_reply(rep)
255
256         # Check the contents of the service ticket
257         ticket = rep['ticket']
258         enc_part = self.decode_service_ticket(mc, ticket)
259         self.check_pac(samdb, enc_part['authorization-data'], dn, uc, upn_name)
260         # check the crealm and cname
261         cname = enc_part['cname']
262         self.assertEqual(NT_PRINCIPAL, cname['name-type'])
263         self.assertEqual(upn_name.encode('UTF8'), cname['name-string'][0])
264         self.assertEqual(realm.upper().encode('UTF8'), enc_part['crealm'])
265
266     def test_nt_principal_step_4_a(self):
267         ''' Step 4, no pre-authentication
268             If not found and no pre-authentication
269                 search for a matching altSecurityIdentity
270         '''
271         # Create a user account for the test.
272         # with an altSecurityIdentity, and with UF_DONT_REQUIRE_PREAUTH
273         # set.
274         #
275         #   note that in this case IDL_DRSCrackNames is called with
276         #        pmsgIn.formatOffered set to
277         #           DS_USER_PRINCIPAL_NAME_AND_ALTSECID
278         #
279         # setting UF_DONT_REQUIRE_PREAUTH seems to be the only way
280         # to trigger the no pre-auth step
281
282         samdb = self.get_samdb()
283         user_name = "mskileusr"
284         alt_name = "mskilealtsec"
285         (uc, dn) = self.create_account(samdb, user_name,
286                                        account_control=UF_DONT_REQUIRE_PREAUTH)
287         realm = uc.get_realm().lower()
288         alt_sec = "Kerberos:%s@%s" % (alt_name, realm)
289         self.add_attribute(samdb, dn, "altSecurityIdentities", alt_sec)
290
291         mach_name = "mskilemac"
292         (mc, _) = self.create_account(samdb, mach_name,
293                                       account_type=self.AccountType.COMPUTER)
294
295         # Do the initial AS-REQ, as we've set UF_DONT_REQUIRE_PREAUTH
296         # we should get a valid AS-RESP
297         # response
298         etype = (AES256_CTS_HMAC_SHA1_96, ARCFOUR_HMAC_MD5)
299         cname = self.PrincipalName_create(
300             name_type=NT_PRINCIPAL, names=[alt_name])
301         sname = self.PrincipalName_create(
302             name_type=NT_SRV_INST, names=["krbtgt", realm])
303
304         rep = self.as_req(cname, sname, realm, etype)
305         self.check_as_reply(rep)
306         salt = "%s%s" % (realm.upper(), user_name)
307         key = self.PasswordKey_create(
308             rep['enc-part']['etype'],
309             uc.get_password(),
310             salt.encode('UTF8'),
311             rep['enc-part']['kvno'])
312
313         # Request a ticket to the host service on the machine account
314         ticket = rep['ticket']
315         enc_part2 = self.get_as_rep_enc_data(key, rep)
316         key = self.EncryptionKey_import(enc_part2['key'])
317         cname = self.PrincipalName_create(
318             name_type=NT_PRINCIPAL, names=[alt_name])
319         sname = self.PrincipalName_create(
320             name_type=NT_PRINCIPAL,
321             names=[mc.get_username()])
322
323         (rep, enc_part) = self.tgs_req(
324             cname, sname, uc.get_realm(), ticket, key, etype,
325             service_creds=mc, expect_pac=False)
326         self.check_tgs_reply(rep)
327
328         # Check the contents of the service ticket
329         ticket = rep['ticket']
330         enc_part = self.decode_service_ticket(mc, ticket)
331         #
332         # We get an empty authorization-data element in the ticket.
333         # i.e. no PAC
334         self.assertEqual([], enc_part['authorization-data'])
335         # check the crealm and cname
336         cname = enc_part['cname']
337         self.assertEqual(NT_PRINCIPAL, cname['name-type'])
338         self.assertEqual(alt_name.encode('UTF8'), cname['name-string'][0])
339         self.assertEqual(realm.upper().encode('UTF8'), enc_part['crealm'])
340
341     def test_nt_principal_step_4_b(self):
342         ''' Step 4, pre-authentication
343             If not found and pre-authentication
344                 search for a matching user principal name
345         '''
346
347         # Create user and machine accounts for the test.
348         #
349         samdb = self.get_samdb()
350         user_name = "mskileusr"
351         alt_name = "mskilealtsec"
352         (uc, dn) = self.create_account(samdb, user_name)
353         realm = uc.get_realm().lower()
354         alt_sec = "Kerberos:%s@%s" % (alt_name, realm)
355         self.add_attribute(samdb, dn, "altSecurityIdentities", alt_sec)
356
357         mach_name = "mskilemac"
358         (mc, _) = self.create_account(samdb, mach_name,
359                                       account_type=self.AccountType.COMPUTER)
360
361         # Do the initial AS-REQ, should get a pre-authentication required
362         # response
363         etype = (AES256_CTS_HMAC_SHA1_96, ARCFOUR_HMAC_MD5)
364         cname = self.PrincipalName_create(
365             name_type=NT_PRINCIPAL, names=[alt_name])
366         sname = self.PrincipalName_create(
367             name_type=NT_SRV_INST, names=["krbtgt", realm])
368
369         rep = self.as_req(cname, sname, realm, etype)
370         self.check_pre_authentication(rep)
371
372         # Do the next AS-REQ
373         padata = self.get_enc_timestamp_pa_data(uc, rep)
374         key = self.get_as_rep_key(uc, rep)
375         # Note: although we used the alt security id for the pre-auth
376         #       we need to use the username for the auth
377         cname = self.PrincipalName_create(
378             name_type=NT_PRINCIPAL, names=[user_name])
379         rep = self.as_req(cname, sname, realm, etype, padata=[padata])
380         self.check_as_reply(rep)
381
382         # Request a ticket to the host service on the machine account
383         ticket = rep['ticket']
384         enc_part2 = self.get_as_rep_enc_data(key, rep)
385         key = self.EncryptionKey_import(enc_part2['key'])
386         cname = self.PrincipalName_create(
387             name_type=NT_PRINCIPAL,
388             names=[user_name])
389         sname = self.PrincipalName_create(
390             name_type=NT_PRINCIPAL,
391             names=[mc.get_username()])
392
393         (rep, enc_part) = self.tgs_req(
394             cname, sname, uc.get_realm(), ticket, key, etype,
395             service_creds=mc)
396         self.check_tgs_reply(rep)
397
398         # Check the contents of the pac, and the ticket
399         ticket = rep['ticket']
400         enc_part = self.decode_service_ticket(mc, ticket)
401         self.check_pac(samdb,
402                        enc_part['authorization-data'], dn, uc, user_name)
403         # check the crealm and cname
404         cname = enc_part['cname']
405         self.assertEqual(NT_PRINCIPAL, cname['name-type'])
406         self.assertEqual(user_name.encode('UTF8'), cname['name-string'][0])
407         self.assertEqual(realm.upper().encode('UTF8'), enc_part['crealm'])
408
409     def test_nt_principal_step_4_c(self):
410         ''' Step 4, pre-authentication
411             If not found and pre-authentication
412                 search for a matching user principal name
413
414             This test uses the altsecid, so the AS-REQ should fail.
415         '''
416
417         # Create user and machine accounts for the test.
418         #
419         samdb = self.get_samdb()
420         user_name = "mskileusr"
421         alt_name = "mskilealtsec"
422         (uc, dn) = self.create_account(samdb, user_name)
423         realm = uc.get_realm().lower()
424         alt_sec = "Kerberos:%s@%s" % (alt_name, realm)
425         self.add_attribute(samdb, dn, "altSecurityIdentities", alt_sec)
426
427         mach_name = "mskilemac"
428         (mc, _) = self.create_account(samdb, mach_name,
429                                       account_type=self.AccountType.COMPUTER)
430
431         # Do the initial AS-REQ, should get a pre-authentication required
432         # response
433         etype = (AES256_CTS_HMAC_SHA1_96, ARCFOUR_HMAC_MD5)
434         cname = self.PrincipalName_create(
435             name_type=NT_PRINCIPAL, names=[alt_name])
436         sname = self.PrincipalName_create(
437             name_type=NT_SRV_INST, names=["krbtgt", realm])
438
439         rep = self.as_req(cname, sname, realm, etype)
440         self.check_pre_authentication(rep)
441
442         # Do the next AS-REQ
443         padata = self.get_enc_timestamp_pa_data(uc, rep)
444         # Use the alternate security identifier
445         #     this should fail
446         cname = self.PrincipalName_create(
447             name_type=NT_PRINCIPAL, names=[alt_sec])
448         rep = self.as_req(cname, sname, realm, etype, padata=[padata])
449         self.check_error_rep(rep, KDC_ERR_C_PRINCIPAL_UNKNOWN)
450
451     def test_enterprise_principal_step_1_3(self):
452         ''' Steps 1-3
453             For an NT_ENTERPRISE_PRINCIPAL cname
454                 search for a user principal name matching the cname
455
456         '''
457
458         # Create a user account for the test.
459         #
460         samdb = self.get_samdb()
461         user_name = "mskileusr"
462         upn_name = "mskileupn"
463         upn = upn_name + "@" + self.get_user_creds().get_realm().lower()
464         (uc, dn) = self.create_account(samdb, user_name, upn=upn)
465         realm = uc.get_realm().lower()
466
467         mach_name = "mskilemac"
468         (mc, _) = self.create_account(samdb, mach_name,
469                                       account_type=self.AccountType.COMPUTER)
470
471         # Do the initial AS-REQ, should get a pre-authentication required
472         # response
473         etype = (AES256_CTS_HMAC_SHA1_96, ARCFOUR_HMAC_MD5)
474         cname = self.PrincipalName_create(
475             name_type=NT_ENTERPRISE_PRINCIPAL, names=[upn])
476         sname = self.PrincipalName_create(
477             name_type=NT_SRV_INST, names=["krbtgt", realm])
478
479         rep = self.as_req(cname, sname, realm, etype)
480         self.check_pre_authentication(rep)
481
482         # Do the next AS-REQ
483         padata = self.get_enc_timestamp_pa_data(uc, rep)
484         key = self.get_as_rep_key(uc, rep)
485         rep = self.as_req(cname, sname, realm, etype, padata=[padata])
486         self.check_as_reply(rep)
487
488         # Request a ticket to the host service on the machine account
489         ticket = rep['ticket']
490         enc_part2 = self.get_as_rep_enc_data(key, rep)
491         key = self.EncryptionKey_import(enc_part2['key'])
492         cname = self.PrincipalName_create(
493             name_type=NT_ENTERPRISE_PRINCIPAL, names=[upn])
494         sname = self.PrincipalName_create(
495             name_type=NT_PRINCIPAL,
496             names=[mc.get_username()])
497
498         (rep, enc_part) = self.tgs_req(
499             cname, sname, uc.get_realm(), ticket, key, etype,
500             service_creds=mc)
501         self.check_tgs_reply(rep)
502
503         # Check the contents of the pac, and the ticket
504         ticket = rep['ticket']
505         enc_part = self.decode_service_ticket(mc, ticket)
506         self.check_pac(
507             samdb, enc_part['authorization-data'], dn, uc, upn, upn=upn)
508         # check the crealm and cname
509         cname = enc_part['cname']
510         crealm = enc_part['crealm']
511         self.assertEqual(NT_ENTERPRISE_PRINCIPAL, cname['name-type'])
512         self.assertEqual(upn.encode('UTF8'), cname['name-string'][0])
513         self.assertEqual(realm.upper().encode('UTF8'), crealm)
514
515     def test_enterprise_principal_step_4(self):
516         ''' Step 4
517
518             If that fails
519                 search for an account where the sAMAccountName matches
520                 the name before the @
521
522         '''
523
524         # Create a user account for the test.
525         #
526         samdb = self.get_samdb()
527         user_name = "mskileusr"
528         (uc, dn) = self.create_account(samdb, user_name)
529         realm = uc.get_realm().lower()
530         ename = user_name + "@" + realm
531
532         mach_name = "mskilemac"
533         (mc, _) = self.create_account(samdb, mach_name,
534                                       account_type=self.AccountType.COMPUTER)
535
536         # Do the initial AS-REQ, should get a pre-authentication required
537         # response
538         etype = (AES256_CTS_HMAC_SHA1_96, ARCFOUR_HMAC_MD5)
539         cname = self.PrincipalName_create(
540             name_type=NT_ENTERPRISE_PRINCIPAL, names=[ename])
541         sname = self.PrincipalName_create(
542             name_type=NT_SRV_INST, names=["krbtgt", realm])
543
544         rep = self.as_req(cname, sname, realm, etype)
545         self.check_pre_authentication(rep)
546
547         # Do the next AS-REQ
548         padata = self.get_enc_timestamp_pa_data(uc, rep)
549         key = self.get_as_rep_key(uc, rep)
550         rep = self.as_req(cname, sname, realm, etype, padata=[padata])
551         self.check_as_reply(rep)
552
553         # Request a ticket to the host service on the machine account
554         ticket = rep['ticket']
555         enc_part2 = self.get_as_rep_enc_data(key, rep)
556         key = self.EncryptionKey_import(enc_part2['key'])
557         cname = self.PrincipalName_create(
558             name_type=NT_ENTERPRISE_PRINCIPAL, names=[ename])
559         sname = self.PrincipalName_create(
560             name_type=NT_PRINCIPAL,
561             names=[mc.get_username()])
562
563         (rep, enc_part) = self.tgs_req(
564             cname, sname, uc.get_realm(), ticket, key, etype,
565             service_creds=mc)
566         self.check_tgs_reply(rep)
567
568         # Check the contents of the pac, and the ticket
569         ticket = rep['ticket']
570         enc_part = self.decode_service_ticket(mc, ticket)
571         self.check_pac(
572             samdb, enc_part['authorization-data'], dn, uc, ename, upn=ename)
573         # check the crealm and cname
574         cname = enc_part['cname']
575         crealm = enc_part['crealm']
576         self.assertEqual(NT_ENTERPRISE_PRINCIPAL, cname['name-type'])
577         self.assertEqual(ename.encode('UTF8'), cname['name-string'][0])
578         self.assertEqual(realm.upper().encode('UTF8'), crealm)
579
580     def test_enterprise_principal_step_5(self):
581         ''' Step 5
582
583             If that fails
584                 search for an account where the sAMAccountName matches
585                 the name before the @ with a $ appended.
586
587         '''
588
589         # Create a user account for the test.
590         #
591         samdb = self.get_samdb()
592         user_name = "mskileusr"
593         (uc, _) = self.create_account(samdb, user_name)
594         realm = uc.get_realm().lower()
595
596         mach_name = "mskilemac"
597         (mc, dn) = self.create_account(samdb, mach_name,
598                                       account_type=self.AccountType.COMPUTER)
599         ename = mach_name + "@" + realm
600         uname = mach_name + "$@" + realm
601
602         # Do the initial AS-REQ, should get a pre-authentication required
603         # response
604         etype = (AES256_CTS_HMAC_SHA1_96, ARCFOUR_HMAC_MD5)
605         cname = self.PrincipalName_create(
606             name_type=NT_ENTERPRISE_PRINCIPAL, names=[ename])
607         sname = self.PrincipalName_create(
608             name_type=NT_SRV_INST, names=["krbtgt", realm])
609
610         rep = self.as_req(cname, sname, realm, etype)
611         self.check_pre_authentication(rep)
612
613         # Do the next AS-REQ
614         padata = self.get_enc_timestamp_pa_data(mc, rep)
615         key = self.get_as_rep_key(mc, rep)
616         rep = self.as_req(cname, sname, realm, etype, padata=[padata])
617         self.check_as_reply(rep)
618
619         # Request a ticket to the host service on the machine account
620         ticket = rep['ticket']
621         enc_part2 = self.get_as_rep_enc_data(key, rep)
622         key = self.EncryptionKey_import(enc_part2['key'])
623         cname = self.PrincipalName_create(
624             name_type=NT_ENTERPRISE_PRINCIPAL, names=[ename])
625         sname = self.PrincipalName_create(
626             name_type=NT_PRINCIPAL,
627             names=[mc.get_username()])
628
629         (rep, enc_part) = self.tgs_req(
630             cname, sname, uc.get_realm(), ticket, key, etype,
631             service_creds=mc)
632         self.check_tgs_reply(rep)
633
634         # Check the contents of the pac, and the ticket
635         ticket = rep['ticket']
636         enc_part = self.decode_service_ticket(mc, ticket)
637         self.check_pac(
638             samdb, enc_part['authorization-data'], dn, mc, ename, upn=uname)
639         # check the crealm and cname
640         cname = enc_part['cname']
641         crealm = enc_part['crealm']
642         self.assertEqual(NT_ENTERPRISE_PRINCIPAL, cname['name-type'])
643         self.assertEqual(ename.encode('UTF8'), cname['name-string'][0])
644         self.assertEqual(realm.upper().encode('UTF8'), crealm)
645
646     def test_enterprise_principal_step_6_a(self):
647         ''' Step 6, no pre-authentication
648             If not found and no pre-authentication
649                 search for a matching altSecurityIdentity
650         '''
651         # Create a user account for the test.
652         # with an altSecurityIdentity, and with UF_DONT_REQUIRE_PREAUTH
653         # set.
654         #
655         #   note that in this case IDL_DRSCrackNames is called with
656         #        pmsgIn.formatOffered set to
657         #           DS_USER_PRINCIPAL_NAME_AND_ALTSECID
658         #
659         # setting UF_DONT_REQUIRE_PREAUTH seems to be the only way
660         # to trigger the no pre-auth step
661
662         samdb = self.get_samdb()
663         user_name = "mskileusr"
664         alt_name = "mskilealtsec"
665         (uc, dn) = self.create_account(samdb, user_name,
666                                        account_control=UF_DONT_REQUIRE_PREAUTH)
667         realm = uc.get_realm().lower()
668         alt_sec = "Kerberos:%s@%s" % (alt_name, realm)
669         self.add_attribute(samdb, dn, "altSecurityIdentities", alt_sec)
670         ename = alt_name + "@" + realm
671
672         mach_name = "mskilemac"
673         (mc, _) = self.create_account(samdb, mach_name,
674                                       account_type=self.AccountType.COMPUTER)
675
676         # Do the initial AS-REQ, as we've set UF_DONT_REQUIRE_PREAUTH
677         # we should get a valid AS-RESP
678         # response
679         etype = (AES256_CTS_HMAC_SHA1_96, ARCFOUR_HMAC_MD5)
680         cname = self.PrincipalName_create(
681             name_type=NT_ENTERPRISE_PRINCIPAL, names=[ename])
682         sname = self.PrincipalName_create(
683             name_type=NT_SRV_INST, names=["krbtgt", realm])
684
685         rep = self.as_req(cname, sname, realm, etype)
686         self.check_as_reply(rep)
687         salt = "%s%s" % (realm.upper(), user_name)
688         key = self.PasswordKey_create(
689             rep['enc-part']['etype'],
690             uc.get_password(),
691             salt.encode('UTF8'),
692             rep['enc-part']['kvno'])
693
694         # Request a ticket to the host service on the machine account
695         ticket = rep['ticket']
696         enc_part2 = self.get_as_rep_enc_data(key, rep)
697         key = self.EncryptionKey_import(enc_part2['key'])
698         cname = self.PrincipalName_create(
699             name_type=NT_ENTERPRISE_PRINCIPAL, names=[ename])
700         sname = self.PrincipalName_create(
701             name_type=NT_PRINCIPAL,
702             names=[mc.get_username()])
703
704         (rep, enc_part) = self.tgs_req(
705             cname, sname, uc.get_realm(), ticket, key, etype,
706             service_creds=mc, expect_pac=False)
707         self.check_tgs_reply(rep)
708
709         # Check the contents of the service ticket
710         ticket = rep['ticket']
711         enc_part = self.decode_service_ticket(mc, ticket)
712         #
713         # We get an empty authorization-data element in the ticket.
714         # i.e. no PAC
715         self.assertEqual([], enc_part['authorization-data'])
716         # check the crealm and cname
717         cname = enc_part['cname']
718         self.assertEqual(NT_ENTERPRISE_PRINCIPAL, cname['name-type'])
719         self.assertEqual(ename.encode('UTF8'), cname['name-string'][0])
720         self.assertEqual(realm.upper().encode('UTF8'), enc_part['crealm'])
721
722     def test_nt_enterprise_principal_step_6_b(self):
723         ''' Step 4, pre-authentication
724             If not found and pre-authentication
725                 search for a matching user principal name
726         '''
727
728         # Create user and machine accounts for the test.
729         #
730         samdb = self.get_samdb()
731         user_name = "mskileusr"
732         alt_name = "mskilealtsec"
733         (uc, dn) = self.create_account(samdb, user_name)
734         realm = uc.get_realm().lower()
735         alt_sec = "Kerberos:%s@%s" % (alt_name, realm)
736         self.add_attribute(samdb, dn, "altSecurityIdentities", alt_sec)
737         ename = alt_name + "@" + realm
738         uname = user_name + "@" + realm
739
740         mach_name = "mskilemac"
741         (mc, _) = self.create_account(samdb, mach_name,
742                                       account_type=self.AccountType.COMPUTER)
743
744         # Do the initial AS-REQ, should get a pre-authentication required
745         # response
746         etype = (AES256_CTS_HMAC_SHA1_96, ARCFOUR_HMAC_MD5)
747         cname = self.PrincipalName_create(
748             name_type=NT_ENTERPRISE_PRINCIPAL, names=[ename])
749         sname = self.PrincipalName_create(
750             name_type=NT_SRV_INST, names=["krbtgt", realm])
751
752         rep = self.as_req(cname, sname, realm, etype)
753         self.check_pre_authentication(rep)
754
755         # Do the next AS-REQ
756         padata = self.get_enc_timestamp_pa_data(uc, rep)
757         key = self.get_as_rep_key(uc, rep)
758         # Note: although we used the alt security id for the pre-auth
759         #       we need to use the username for the auth
760         cname = self.PrincipalName_create(
761             name_type=NT_ENTERPRISE_PRINCIPAL, names=[uname])
762         rep = self.as_req(cname, sname, realm, etype, padata=[padata])
763         self.check_as_reply(rep)
764
765         # Request a ticket to the host service on the machine account
766         ticket = rep['ticket']
767         enc_part2 = self.get_as_rep_enc_data(key, rep)
768         key = self.EncryptionKey_import(enc_part2['key'])
769         cname = self.PrincipalName_create(
770             name_type=NT_ENTERPRISE_PRINCIPAL,
771             names=[uname])
772         sname = self.PrincipalName_create(
773             name_type=NT_PRINCIPAL,
774             names=[mc.get_username()])
775
776         (rep, enc_part) = self.tgs_req(
777             cname, sname, uc.get_realm(), ticket, key, etype,
778             service_creds=mc)
779         self.check_tgs_reply(rep)
780
781         # Check the contents of the pac, and the ticket
782         ticket = rep['ticket']
783         enc_part = self.decode_service_ticket(mc, ticket)
784         self.check_pac(
785             samdb, enc_part['authorization-data'], dn, uc, uname, upn=uname)
786         # check the crealm and cname
787         cname = enc_part['cname']
788         self.assertEqual(NT_ENTERPRISE_PRINCIPAL, cname['name-type'])
789         self.assertEqual(uname.encode('UTF8'), cname['name-string'][0])
790         self.assertEqual(realm.upper().encode('UTF8'), enc_part['crealm'])
791
792     def test_nt_principal_step_6_c(self):
793         ''' Step 4, pre-authentication
794             If not found and pre-authentication
795                 search for a matching user principal name
796
797             This test uses the altsecid, so the AS-REQ should fail.
798         '''
799
800         # Create user and machine accounts for the test.
801         #
802         samdb = self.get_samdb()
803         user_name = "mskileusr"
804         alt_name = "mskilealtsec"
805         (uc, dn) = self.create_account(samdb, user_name)
806         realm = uc.get_realm().lower()
807         alt_sec = "Kerberos:%s@%s" % (alt_name, realm)
808         self.add_attribute(samdb, dn, "altSecurityIdentities", alt_sec)
809         ename = alt_name + "@" + realm
810
811         mach_name = "mskilemac"
812         (mc, _) = self.create_account(samdb, mach_name,
813                                       account_type=self.AccountType.COMPUTER)
814
815         # Do the initial AS-REQ, should get a pre-authentication required
816         # response
817         etype = (AES256_CTS_HMAC_SHA1_96, ARCFOUR_HMAC_MD5)
818         cname = self.PrincipalName_create(
819             name_type=NT_ENTERPRISE_PRINCIPAL, names=[ename])
820         sname = self.PrincipalName_create(
821             name_type=NT_SRV_INST, names=["krbtgt", realm])
822
823         rep = self.as_req(cname, sname, realm, etype)
824         self.check_pre_authentication(rep)
825
826         # Do the next AS-REQ
827         padata = self.get_enc_timestamp_pa_data(uc, rep)
828         # Use the alternate security identifier
829         #     this should fail
830         cname = self.PrincipalName_create(
831             name_type=NT_ENTERPRISE_PRINCIPAL, names=[ename])
832         rep = self.as_req(cname, sname, realm, etype, padata=[padata])
833         self.check_error_rep(rep, KDC_ERR_C_PRINCIPAL_UNKNOWN)
834
835
836 if __name__ == "__main__":
837     global_asn1_print = False
838     global_hexdump = False
839     import unittest
840     unittest.main()