2 # Unix SMB/CIFS implementation.
3 # Copyright (C) Stefan Metzmacher 2020
4 # Copyright (C) Catalyst.Net Ltd 2022
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/>.
23 sys
.path
.insert(0, 'bin/python')
24 os
.environ
['PYTHONUNBUFFERED'] = '1'
29 from samba
.dcerpc
import claims
, krb5pac
, security
30 from samba
.ndr
import ndr_pack
32 from samba
.tests
import DynamicTestCase
, env_get_var_value
33 from samba
.tests
.krb5
import kcrypto
34 from samba
.tests
.krb5
.kcrypto
import Enctype
35 from samba
.tests
.krb5
.kdc_base_test
import GroupType
, KDCBaseTest
, Principal
36 from samba
.tests
.krb5
.raw_testcase
import Krb5EncryptionKey
, RawKerberosTest
37 from samba
.tests
.krb5
.rfc4120_constants
import (
38 AES256_CTS_HMAC_SHA1_96
,
43 import samba
.tests
.krb5
.rfc4120_pyasn1
as krb5_asn1
45 SidType
= RawKerberosTest
.SidType
47 global_asn1_print
= False
48 global_hexdump
= False
51 class UnorderedList(tuple):
52 def __eq__(self
, other
):
53 if not isinstance(other
, UnorderedList
):
54 raise AssertionError('unexpected comparison attempt')
55 return sorted(self
) == sorted(other
)
58 return hash(tuple(sorted(self
)))
62 class ClaimsTests(KDCBaseTest
):
63 # Placeholder objects that represent accounts undergoing testing.
67 # Constants for group SID attributes.
68 default_attrs
= security
.SE_GROUP_DEFAULT_FLAGS
69 resource_attrs
= default_attrs | security
.SE_GROUP_RESOURCE
71 asserted_identity
= security
.SID_AUTHENTICATION_AUTHORITY_ASSERTED_IDENTITY
72 compounded_auth
= security
.SID_COMPOUNDED_AUTHENTICATION
78 cls
._search
_iterator
= None
82 self
.do_asn1_print
= global_asn1_print
83 self
.do_hexdump
= global_hexdump
85 def get_sample_dn(self
):
86 if self
._search
_iterator
is None:
87 samdb
= self
.get_samdb()
88 type(self
)._search
_iterator
= samdb
.search_iterator()
90 return str(next(self
._search
_iterator
).dn
)
92 def get_binary_dn(self
):
93 return 'B:8:01010101:' + self
.get_sample_dn()
95 def setup_claims(self
, all_claims
):
97 unexpected_claims
= set()
100 mod_msg
= ldb
.Message()
103 for claim
in all_claims
:
104 # Make a copy to avoid modifying the original.
107 claim_id
= self
.get_new_username()
109 expected
= claim
.pop('expected', False)
110 expected_values
= claim
.pop('expected_values', None)
112 self
.assertIsNone(expected_values
,
113 'claim not expected, '
114 'but expected values provided')
116 values
= claim
.pop('values', None)
117 if values
is not None:
118 def get_placeholder(val
):
119 if val
is self
.sample_dn
:
120 return self
.get_sample_dn()
121 elif val
is self
.binary_dn
:
122 return self
.get_binary_dn()
126 def ldb_transform(val
):
131 elif isinstance(val
, int):
136 values_type
= type(values
)
137 values
= values_type(map(get_placeholder
, values
))
138 transformed_values
= values_type(map(ldb_transform
, values
))
140 attribute
= claim
['attribute']
141 if attribute
in details
:
142 self
.assertEqual(details
[attribute
], transformed_values
,
143 'conflicting values set for attribute')
144 details
[attribute
] = transformed_values
146 readable
= claim
.pop('readable', True)
148 if security_desc
is None:
149 security_desc
= security
.descriptor()
151 # Deny all read property access to the attribute.
153 ace
.type = security
.SEC_ACE_TYPE_ACCESS_DENIED_OBJECT
154 ace
.access_mask
= security
.SEC_ADS_READ_PROP
155 ace
.trustee
= security
.dom_sid(security
.SID_WORLD
)
156 ace
.object.flags |
= security
.SEC_ACE_OBJECT_TYPE_PRESENT
157 ace
.object.type = self
.get_schema_id_guid_from_attribute(
160 security_desc
.dacl_add(ace
)
162 if expected_values
is None:
163 expected_values
= values
165 mod_values
= claim
.pop('mod_values', None)
166 if mod_values
is not None:
167 flag
= (ldb
.FLAG_MOD_REPLACE
168 if values
is not None else ldb
.FLAG_MOD_ADD
)
169 mod_msg
[attribute
] = ldb
.MessageElement(mod_values
,
174 self
.assertIsNotNone(expected_values
,
175 'expected claim, but no value(s) set')
176 value_type
= claim
['value_type']
178 expected_claims
[claim_id
] = {
179 'source_type': claims
.CLAIMS_SOURCE_TYPE_AD
,
181 'values': expected_values
,
184 unexpected_claims
.add(claim_id
)
186 self
.create_claim(claim_id
, **claim
)
188 if security_desc
is not None:
189 self
.assertNotIn('nTSecurityDescriptor', details
)
190 details
['nTSecurityDescriptor'] = ndr_pack(security_desc
)
192 return details
, mod_msg
, expected_claims
, unexpected_claims
194 def modify_pac_remove_client_claims(self
, pac
):
195 pac_buffers
= pac
.buffers
196 for pac_buffer
in pac_buffers
:
197 if pac_buffer
.type == krb5pac
.PAC_TYPE_CLIENT_CLAIMS_INFO
:
199 pac_buffers
.remove(pac_buffer
)
203 self
.fail('expected client claims in PAC')
205 pac
.buffers
= pac_buffers
209 def remove_client_claims(self
, ticket
):
210 return self
.modified_ticket(
212 modify_pac_fn
=self
.modify_pac_remove_client_claims
,
213 checksum_keys
=self
.get_krbtgt_checksum_key())
215 def remove_client_claims_tgt_from_rodc(self
, ticket
):
216 rodc_krbtgt_creds
= self
.get_mock_rodc_krbtgt_creds()
217 rodc_krbtgt_key
= self
.TicketDecryptionKey_from_creds(
221 krb5pac
.PAC_TYPE_KDC_CHECKSUM
: rodc_krbtgt_key
224 return self
.modified_ticket(
226 new_ticket_key
=rodc_krbtgt_key
,
227 modify_pac_fn
=self
.modify_pac_remove_client_claims
,
228 checksum_keys
=checksum_keys
)
230 def test_tgs_claims(self
):
231 self
.run_tgs_test(remove_claims
=False, to_krbtgt
=False)
233 def test_tgs_claims_remove_claims(self
):
234 self
.run_tgs_test(remove_claims
=True, to_krbtgt
=False)
236 def test_tgs_claims_to_krbtgt(self
):
237 self
.run_tgs_test(remove_claims
=False, to_krbtgt
=True)
239 def test_tgs_claims_remove_claims_to_krbtgt(self
):
240 self
.run_tgs_test(remove_claims
=True, to_krbtgt
=True)
242 def test_delegation_claims(self
):
243 self
.run_delegation_test(remove_claims
=False)
245 def test_delegation_claims_remove_claims(self
):
246 self
.run_delegation_test(remove_claims
=True)
248 def test_rodc_issued_claims_modify(self
):
249 self
.run_rodc_tgs_test(remove_claims
=False, delete_claim
=False)
251 def test_rodc_issued_claims_delete(self
):
252 self
.run_rodc_tgs_test(remove_claims
=False, delete_claim
=True)
254 def test_rodc_issued_claims_remove_claims_modify(self
):
255 self
.run_rodc_tgs_test(remove_claims
=True, delete_claim
=False)
257 def test_rodc_issued_claims_remove_claims_delete(self
):
258 self
.run_rodc_tgs_test(remove_claims
=True, delete_claim
=True)
260 def test_rodc_issued_device_claims_modify(self
):
261 self
.run_device_rodc_tgs_test(remove_claims
=False, delete_claim
=False)
263 def test_rodc_issued_device_claims_delete(self
):
264 self
.run_device_rodc_tgs_test(remove_claims
=False, delete_claim
=True)
266 def test_rodc_issued_device_claims_remove_claims_modify(self
):
267 self
.run_device_rodc_tgs_test(remove_claims
=True, delete_claim
=False)
269 def test_rodc_issued_device_claims_remove_claims_delete(self
):
270 self
.run_device_rodc_tgs_test(remove_claims
=True, delete_claim
=True)
272 # Create a user account with an applicable claim for the 'middleName'
273 # attribute. After obtaining a TGT, from which we optionally remove the
274 # claims, change the middleName attribute values for the account in the
275 # database to a different value. By which we may observe, when examining
276 # the reply to our following Kerberos TGS request, whether the claims
277 # contained therein are taken directly from the ticket, or obtained fresh
279 def run_tgs_test(self
, remove_claims
, to_krbtgt
):
280 samdb
= self
.get_samdb()
281 user_creds
, user_dn
= self
.create_account(samdb
,
282 self
.get_new_username(),
287 claim_id
= self
.get_new_username()
288 self
.create_claim(claim_id
,
290 attribute
='middleName',
293 for_classes
=['user'],
294 value_type
=claims
.CLAIM_TYPE_STRING
)
298 'source_type': claims
.CLAIMS_SOURCE_TYPE_AD
,
299 'type': claims
.CLAIM_TYPE_STRING
,
304 # Get a TGT for the user.
305 tgt
= self
.get_tgt(user_creds
, expect_pac
=True,
306 expect_client_claims
=True,
307 expected_client_claims
=expected_claims
)
310 tgt
= self
.remove_client_claims(tgt
)
312 # Change the value of the attribute used for the claim.
313 msg
= ldb
.Message(ldb
.Dn(samdb
, user_dn
))
314 msg
['middleName'] = ldb
.MessageElement('bar',
315 ldb
.FLAG_MOD_REPLACE
,
320 target_creds
= self
.get_krbtgt_creds()
321 sname
= self
.get_krbtgt_sname()
323 target_creds
= self
.get_service_creds()
326 # Get a service ticket for the user. The claim value should not have
327 # changed, indicating that the client claims are propagated straight
329 self
.get_service_ticket(
333 expect_client_claims
=not remove_claims
,
334 expected_client_claims
=(expected_claims
335 if not remove_claims
else None))
337 # Perform a test similar to that preceding. This time, create both a user
338 # and a computer account, each having an applicable claim. After obtaining
339 # tickets, from which the claims are optionally removed, change the claim
340 # attribute of each account to a different value. Then perform constrained
341 # delegation with the user's service ticket, verifying that the user's
342 # claims are carried into the resulting ticket.
343 def run_delegation_test(self
, remove_claims
):
344 service_creds
= self
.get_service_creds()
345 service_spn
= service_creds
.get_spn()
347 user_name
= self
.get_new_username()
348 mach_name
= self
.get_new_username()
350 samdb
= self
.get_samdb()
351 user_creds
, user_dn
= self
.create_account(
354 self
.AccountType
.USER
,
356 'middleName': 'user_old',
358 mach_creds
, mach_dn
= self
.create_account(
361 self
.AccountType
.COMPUTER
,
362 spn
=f
'host/{mach_name}',
364 'middleName': 'mach_old',
365 'msDS-AllowedToDelegateTo': service_spn
,
368 claim_id
= self
.get_new_username()
369 self
.create_claim(claim_id
,
371 attribute
='middleName',
374 for_classes
=['user', 'computer'],
375 value_type
=claims
.CLAIM_TYPE_STRING
)
377 options
= 'forwardable'
378 expected_flags
= krb5_asn1
.TicketFlags(options
)
380 expected_claims_user
= {
382 'source_type': claims
.CLAIMS_SOURCE_TYPE_AD
,
383 'type': claims
.CLAIM_TYPE_STRING
,
384 'values': ('user_old',),
387 expected_claims_mach
= {
389 'source_type': claims
.CLAIMS_SOURCE_TYPE_AD
,
390 'type': claims
.CLAIM_TYPE_STRING
,
391 'values': ('mach_old',),
395 user_tgt
= self
.get_tgt(user_creds
,
398 expected_flags
=expected_flags
,
399 expect_client_claims
=True,
400 expected_client_claims
=expected_claims_user
)
401 user_ticket
= self
.get_service_ticket(
406 expected_flags
=expected_flags
,
407 expect_client_claims
=True,
408 expected_client_claims
=expected_claims_user
)
410 mach_tgt
= self
.get_tgt(mach_creds
,
412 expect_client_claims
=True,
413 expected_client_claims
=expected_claims_mach
)
416 user_ticket
= self
.remove_client_claims(user_ticket
)
417 mach_tgt
= self
.remove_client_claims(mach_tgt
)
419 # Change the value of the attribute used for the user claim.
420 msg
= ldb
.Message(ldb
.Dn(samdb
, user_dn
))
421 msg
['middleName'] = ldb
.MessageElement('user_new',
422 ldb
.FLAG_MOD_REPLACE
,
426 # Change the value of the attribute used for the machine claim.
427 msg
= ldb
.Message(ldb
.Dn(samdb
, mach_dn
))
428 msg
['middleName'] = ldb
.MessageElement('mach_new',
429 ldb
.FLAG_MOD_REPLACE
,
433 additional_tickets
= [user_ticket
.ticket
]
434 options
= str(krb5_asn1
.KDCOptions('cname-in-addl-tkt'))
436 user_realm
= user_creds
.get_realm()
437 user_cname
= self
.PrincipalName_create(name_type
=NT_PRINCIPAL
,
440 user_sid
= user_creds
.get_sid()
442 mach_realm
= mach_creds
.get_realm()
444 service_name
= service_creds
.get_username()[:-1]
445 service_realm
= service_creds
.get_realm()
446 service_sname
= self
.PrincipalName_create(name_type
=NT_PRINCIPAL
,
447 names
=['host', service_name
])
448 service_decryption_key
= self
.TicketDecryptionKey_from_creds(
450 service_etypes
= service_creds
.tgs_supported_enctypes
452 expected_proxy_target
= service_creds
.get_spn()
453 expected_transited_services
= [f
'host/{mach_name}@{mach_realm}']
455 authenticator_subkey
= self
.RandomKey(Enctype
.AES256
)
457 etypes
= (AES256_CTS_HMAC_SHA1_96
, ARCFOUR_HMAC_MD5
)
459 # The user's claims are propagated into the new ticket, while the
460 # machine's claims are dispensed with.
461 expected_claims
= expected_claims_user
if not remove_claims
else None
463 # Perform constrained delegation.
464 kdc_exchange_dict
= self
.tgs_exchange_dict(
466 expected_crealm
=user_realm
,
467 expected_cname
=user_cname
,
468 expected_srealm
=service_realm
,
469 expected_sname
=service_sname
,
470 expected_account_name
=user_name
,
471 expected_sid
=user_sid
,
472 expected_supported_etypes
=service_etypes
,
473 ticket_decryption_key
=service_decryption_key
,
474 check_rep_fn
=self
.generic_check_kdc_rep
,
475 check_kdc_private_fn
=self
.generic_check_kdc_private
,
477 authenticator_subkey
=authenticator_subkey
,
479 expected_proxy_target
=expected_proxy_target
,
480 expected_transited_services
=expected_transited_services
,
481 expect_client_claims
=not remove_claims
,
482 expected_client_claims
=expected_claims
,
483 expect_device_claims
=False,
486 rep
= self
._generic
_kdc
_exchange
(kdc_exchange_dict
,
491 additional_tickets
=additional_tickets
)
492 self
.check_reply(rep
, KRB_TGS_REP
)
494 def run_rodc_tgs_test(self
, remove_claims
, delete_claim
):
495 samdb
= self
.get_samdb()
496 # Create a user account permitted to replicate to the RODC.
497 user_creds
= self
.get_cached_creds(
498 account_type
=self
.AccountType
.USER
,
500 # Set the value of the claim attribute.
501 'additional_details': (('middleName', 'foo'),),
502 'allowed_replication_mock': True,
503 'revealed_to_mock_rodc': True,
506 user_dn
= user_creds
.get_dn()
508 # Create a claim that applies to the user.
509 claim_id
= self
.get_new_username()
510 self
.create_claim(claim_id
,
512 attribute
='middleName',
515 for_classes
=['user'],
516 value_type
=claims
.CLAIM_TYPE_STRING
)
520 'source_type': claims
.CLAIMS_SOURCE_TYPE_AD
,
521 'type': claims
.CLAIM_TYPE_STRING
,
526 # Get a TGT for the user.
527 tgt
= self
.get_tgt(user_creds
, expect_pac
=True,
528 expect_client_claims
=True,
529 expected_client_claims
=expected_claims
)
531 # Modify the TGT to be issued by an RODC. Optionally remove the client
534 tgt
= self
.remove_client_claims_tgt_from_rodc(tgt
)
536 tgt
= self
.issued_by_rodc(tgt
)
538 # Modify or delete the value of the attribute used for the claim. Modify
539 # our test expectations accordingly.
540 msg
= ldb
.Message(user_dn
)
542 msg
['middleName'] = ldb
.MessageElement([],
545 expected_claims
= None
546 unexpected_claims
= {claim_id}
548 msg
['middleName'] = ldb
.MessageElement('bar',
549 ldb
.FLAG_MOD_REPLACE
,
553 'source_type': claims
.CLAIMS_SOURCE_TYPE_AD
,
554 'type': claims
.CLAIM_TYPE_STRING
,
558 unexpected_claims
= None
561 target_creds
= self
.get_service_creds()
563 # Get a service ticket for the user. The claim value should have
564 # changed, indicating that the client claims have been regenerated or
565 # removed, depending on whether the corresponding attribute is still
566 # present on the account.
567 self
.get_service_ticket(
570 # Expect the CLIENT_CLAIMS_INFO PAC buffer. It may be empty.
571 expect_client_claims
=True,
572 expected_client_claims
=expected_claims
,
573 unexpected_client_claims
=unexpected_claims
)
575 def run_device_rodc_tgs_test(self
, remove_claims
, delete_claim
):
576 samdb
= self
.get_samdb()
578 # Create the user account.
579 user_creds
= self
.get_cached_creds(
580 account_type
=self
.AccountType
.USER
)
581 user_name
= user_creds
.get_username()
583 # Create a machine account permitted to replicate to the RODC.
584 mach_creds
= self
.get_cached_creds(
585 account_type
=self
.AccountType
.COMPUTER
,
587 # Set the value of the claim attribute.
588 'additional_details': (('middleName', 'foo'),),
589 'allowed_replication_mock': True,
590 'revealed_to_mock_rodc': True,
593 mach_dn
= mach_creds
.get_dn()
595 # Create a claim that applies to the computer.
596 claim_id
= self
.get_new_username()
597 self
.create_claim(claim_id
,
599 attribute
='middleName',
602 for_classes
=['computer'],
603 value_type
=claims
.CLAIM_TYPE_STRING
)
607 'source_type': claims
.CLAIMS_SOURCE_TYPE_AD
,
608 'type': claims
.CLAIM_TYPE_STRING
,
613 # Get a TGT for the user.
614 user_tgt
= self
.get_tgt(user_creds
)
616 # Get a TGT for the computer.
617 mach_tgt
= self
.get_tgt(mach_creds
, expect_pac
=True,
618 expect_client_claims
=True,
619 expected_client_claims
=expected_claims
)
621 # Modify the computer's TGT to be issued by an RODC. Optionally remove
624 mach_tgt
= self
.remove_client_claims_tgt_from_rodc(mach_tgt
)
626 mach_tgt
= self
.issued_by_rodc(mach_tgt
)
628 # Modify or delete the value of the attribute used for the claim. Modify
629 # our test expectations accordingly.
630 msg
= ldb
.Message(mach_dn
)
632 msg
['middleName'] = ldb
.MessageElement([],
635 expected_claims
= None
636 unexpected_claims
= {claim_id}
638 msg
['middleName'] = ldb
.MessageElement('bar',
639 ldb
.FLAG_MOD_REPLACE
,
643 'source_type': claims
.CLAIMS_SOURCE_TYPE_AD
,
644 'type': claims
.CLAIM_TYPE_STRING
,
648 unexpected_claims
= None
651 subkey
= self
.RandomKey(user_tgt
.session_key
.etype
)
653 armor_subkey
= self
.RandomKey(subkey
.etype
)
654 explicit_armor_key
= self
.generate_armor_key(armor_subkey
,
655 mach_tgt
.session_key
)
656 armor_key
= kcrypto
.cf2(explicit_armor_key
.key
,
660 armor_key
= Krb5EncryptionKey(armor_key
, None)
662 target_creds
= self
.get_service_creds()
663 target_name
= target_creds
.get_username()
664 if target_name
[-1] == '$':
665 target_name
= target_name
[:-1]
667 sname
= self
.PrincipalName_create(name_type
=NT_PRINCIPAL
,
668 names
=['host', target_name
])
669 srealm
= target_creds
.get_realm()
671 decryption_key
= self
.TicketDecryptionKey_from_creds(
674 target_supported_etypes
= target_creds
.tgs_supported_enctypes
676 etypes
= (AES256_CTS_HMAC_SHA1_96
, ARCFOUR_HMAC_MD5
)
679 pac_options
= '1' # claims support
681 # Perform a TGS-REQ for the user. The device claim value should have
682 # changed, indicating that the computer's client claims have been
683 # regenerated or removed, depending on whether the corresponding
684 # attribute is still present on the account.
686 kdc_exchange_dict
= self
.tgs_exchange_dict(
688 expected_crealm
=user_tgt
.crealm
,
689 expected_cname
=user_tgt
.cname
,
690 expected_srealm
=srealm
,
691 expected_sname
=sname
,
692 expected_account_name
=user_name
,
693 ticket_decryption_key
=decryption_key
,
694 generate_fast_fn
=self
.generate_simple_fast
,
695 generate_fast_armor_fn
=self
.generate_ap_req
,
696 check_rep_fn
=self
.generic_check_kdc_rep
,
697 check_kdc_private_fn
=self
.generic_check_kdc_private
,
701 armor_subkey
=armor_subkey
,
702 pac_options
=pac_options
,
703 authenticator_subkey
=subkey
,
704 kdc_options
=kdc_options
,
706 expected_supported_etypes
=target_supported_etypes
,
707 # Expect the DEVICE_CLAIMS_INFO PAC buffer. It may be empty.
708 expect_device_claims
=True,
709 expected_device_claims
=expected_claims
,
710 unexpected_device_claims
=unexpected_claims
)
712 rep
= self
._generic
_kdc
_exchange
(kdc_exchange_dict
,
717 self
.check_reply(rep
, KRB_TGS_REP
)
720 def setUpDynamicTestCases(cls
):
721 FILTER
= env_get_var_value('FILTER', allow_missing
=True)
722 for case
in cls
.cases
:
723 name
= case
.pop('name')
724 name
= re
.sub(r
'\W+', '_', name
)
725 if FILTER
and not re
.search(FILTER
, name
):
728 # Run tests making requests both to the krbtgt and to our own
730 cls
.generate_dynamic_test('test_claims', name
,
732 cls
.generate_dynamic_test('test_claims', name
+ '_to_self',
735 for case
in cls
.device_claims_cases
:
736 name
= case
.pop('test')
737 name
= re
.sub(r
'\W+', '_', name
)
738 if FILTER
and not re
.search(FILTER
, name
):
741 cls
.generate_dynamic_test('test_device_claims', name
,
744 def _test_claims_with_args(self
, case
, to_self
):
745 account_class
= case
.pop('class')
746 if account_class
== 'user':
747 account_type
= self
.AccountType
.USER
748 elif account_class
== 'computer':
749 account_type
= self
.AccountType
.COMPUTER
751 self
.fail(f
'Unknown class "{account_class}"')
753 all_claims
= case
.pop('claims')
756 unexpected_claims
) = self
.setup_claims(all_claims
)
757 self
.assertFalse(mod_msg
,
758 'mid-test modifications not supported in this test')
759 creds
= self
.get_cached_creds(
760 account_type
=account_type
,
762 'additional_details': self
.freeze(details
),
765 # Whether to specify claims support in PA-PAC-OPTIONS.
766 pac_options_claims
= case
.pop('pac-options:claims-support', None)
768 self
.assertFalse(case
, 'unexpected parameters in testcase')
770 if pac_options_claims
is None:
771 pac_options_claims
= True
774 service_creds
= self
.get_service_creds()
775 sname
= self
.PrincipalName_create(
776 name_type
=NT_PRINCIPAL
,
777 names
=[service_creds
.get_username()])
778 ticket_etype
= Enctype
.RC4
784 if pac_options_claims
:
785 pac_options
= '1' # claims support
787 pac_options
= '0' # no claims support
791 target_creds
=service_creds
,
792 ticket_etype
=ticket_etype
,
793 pac_options
=pac_options
,
795 expect_client_claims
=True,
796 expected_client_claims
=expected_claims
or None,
797 unexpected_client_claims
=unexpected_claims
or None)
801 security_descriptor
= (b
'\x01\x00\x04\x95\x14\x00\x00\x00\x00\x00\x00\x00'
802 b
'\x00\x00\x00\x00$\x00\x00\x00\x01\x02\x00\x00\x00'
803 b
'\x00\x00\x05 \x00\x00\x00 \x02\x00\x00\x04\x00'
804 b
'\x1c\x00\x01\x00\x00\x00\x00\x1f\x14\x00\xff\x01'
805 b
'\x0f\xf0\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00'
815 'name': 'simple AD-sourced claim',
820 'attribute': 'carLicense',
821 'single_valued': True,
823 'for_classes': ['user'],
824 'value_type': claims
.CLAIM_TYPE_STRING
,
832 'name': 'no claims support in pac options',
837 'attribute': 'carLicense',
838 'single_valued': True,
840 'for_classes': ['user'],
841 'value_type': claims
.CLAIM_TYPE_STRING
,
843 # We still get claims in the PAC even if we don't specify
844 # claims support in PA-PAC-OPTIONS.
849 'pac-options:claims-support': False,
857 'attribute': 'carLicense',
858 'single_valued': True,
860 'for_classes': ['user'],
861 'value_type': claims
.CLAIM_TYPE_STRING
,
863 # Deny read access to the attribute. It still shows up in
872 # Note: The order of these DNs may differ on Windows.
873 'name': 'dn string syntax',
878 'attribute': 'msDS-AuthenticatedAtDC',
879 'single_valued': True,
881 'for_classes': ['user'],
882 'value_type': claims
.CLAIM_TYPE_STRING
,
883 'values': UnorderedList([sample_dn
, sample_dn
, sample_dn
]),
890 'name': 'dn string syntax, wrong value type',
895 'attribute': 'msDS-AuthenticatedAtDC',
896 'single_valued': True,
898 'for_classes': ['user'],
899 'value_type': claims
.CLAIM_TYPE_BOOLEAN
,
900 'values': UnorderedList([sample_dn
, sample_dn
, sample_dn
]),
906 'name': 'oid syntax',
911 'attribute': 'objectClass',
912 'single_valued': True,
914 'for_classes': ['user'],
915 'value_type': claims
.CLAIM_TYPE_UINT64
,
916 'expected_values': [655369, 65543, 65542, 65536],
923 'name': 'oid syntax 2',
928 'attribute': 'objectClass',
929 'single_valued': True,
931 'for_classes': ['computer'],
932 'value_type': claims
.CLAIM_TYPE_UINT64
,
933 'expected_values': [196638, 655369, 65543, 65542, 65536],
940 'name': 'oid syntax, wrong value type',
945 'attribute': 'objectClass',
946 'single_valued': True,
948 'for_classes': ['user'],
949 'value_type': claims
.CLAIM_TYPE_INT64
,
955 'name': 'boolean syntax, true',
960 'attribute': 'msTSAllowLogon',
961 'single_valued': True,
963 'for_classes': ['user'],
964 'value_type': claims
.CLAIM_TYPE_BOOLEAN
,
972 'name': 'boolean syntax, false',
977 'attribute': 'msTSAllowLogon',
978 'single_valued': True,
980 'for_classes': ['user'],
981 'value_type': claims
.CLAIM_TYPE_BOOLEAN
,
989 'name': 'boolean syntax, wrong value type',
994 'attribute': 'msTSAllowLogon',
995 'single_valued': True,
997 'for_classes': ['user'],
998 'value_type': claims
.CLAIM_TYPE_STRING
,
1005 'name': 'integer syntax',
1010 'attribute': 'localeID',
1011 'single_valued': True,
1012 'source_type': 'AD',
1013 'for_classes': ['user'],
1014 'value_type': claims
.CLAIM_TYPE_INT64
,
1015 'values': (3, 42, -999, 1000, 20000),
1022 'name': 'integer syntax, duplicate claim',
1027 'attribute': 'localeID',
1028 'single_valued': True,
1029 'source_type': 'AD',
1030 'for_classes': ['user'],
1031 'value_type': claims
.CLAIM_TYPE_INT64
,
1032 'values': (3, 42, -999, 1000, 20000),
1035 ] * 2, # Create two integer syntax claims.
1039 'name': 'integer syntax, wrong value type',
1044 'attribute': 'localeID',
1045 'single_valued': True,
1046 'source_type': 'AD',
1047 'for_classes': ['user'],
1048 'value_type': claims
.CLAIM_TYPE_UINT64
,
1049 'values': (3, 42, -999, 1000),
1055 'name': 'security descriptor syntax',
1060 'attribute': 'msDS-AllowedToActOnBehalfOfOtherIdentity',
1061 'single_valued': True,
1062 'source_type': 'AD',
1063 'for_classes': ['computer'],
1064 'value_type': claims
.CLAIM_TYPE_STRING
,
1065 'values': (security_descriptor
,),
1066 'expected_values': (
1067 'O:BAD:PARAI(A;OICINPIOID;CCDCLCSWRPWPDTLOCRSDRCWDWOGAGXGWGR;;;S-1-0-0)',
1072 'class': 'computer',
1075 'name': 'security descriptor syntax, wrong value type',
1080 'attribute': 'msDS-AllowedToActOnBehalfOfOtherIdentity',
1081 'single_valued': True,
1082 'source_type': 'AD',
1083 'for_classes': ['computer'],
1084 'value_type': claims
.CLAIM_TYPE_UINT64
,
1085 'values': (security_descriptor
,),
1088 'class': 'computer',
1091 'name': 'case insensitive string syntax (invalid)',
1096 'attribute': 'networkAddress',
1097 'single_valued': True,
1098 'source_type': 'AD',
1099 'for_classes': ['user'],
1100 'value_type': claims
.CLAIM_TYPE_STRING
,
1101 'values': ('foo', 'bar'),
1107 'name': 'printable string syntax (invalid)',
1112 'attribute': 'displayNamePrintable',
1113 'single_valued': True,
1114 'source_type': 'AD',
1115 'for_classes': ['user'],
1116 'value_type': claims
.CLAIM_TYPE_STRING
,
1123 'name': 'numeric string syntax (invalid)',
1128 'attribute': 'internationalISDNNumber',
1129 'single_valued': True,
1130 'source_type': 'AD',
1131 'for_classes': ['user'],
1132 'value_type': claims
.CLAIM_TYPE_STRING
,
1133 'values': ('foo', 'bar'),
1139 'name': 'dn binary syntax (invalid)',
1144 'attribute': 'msDS-RevealedUsers',
1145 'single_valued': True,
1146 'source_type': 'AD',
1147 'for_classes': ['user'],
1148 'value_type': claims
.CLAIM_TYPE_STRING
,
1149 'values': (binary_dn
, binary_dn
, binary_dn
),
1152 'class': 'computer',
1155 'name': 'octet string syntax (invalid)',
1160 'attribute': 'jpegPhoto',
1161 'single_valued': True,
1162 'source_type': 'AD',
1163 'for_classes': ['user'],
1164 'value_type': claims
.CLAIM_TYPE_STRING
,
1165 'values': ('foo', 'bar'),
1171 'name': 'utc time syntax (invalid)',
1176 'attribute': 'msTSExpireDate2',
1177 'single_valued': True,
1178 'source_type': 'AD',
1179 'for_classes': ['user'],
1180 'value_type': claims
.CLAIM_TYPE_STRING
,
1181 'values': ('19700101000000.0Z',),
1187 'name': 'access point syntax (invalid)',
1192 'attribute': 'mS-DS-CreatorSID',
1193 'single_valued': True,
1194 'source_type': 'AD',
1195 'for_classes': ['user'],
1196 'value_type': claims
.CLAIM_TYPE_STRING
,
1202 'name': 'no value set',
1207 'attribute': 'carLicense',
1208 'single_valued': True,
1209 'source_type': 'AD',
1210 'for_classes': ['user'],
1211 'value_type': claims
.CLAIM_TYPE_STRING
,
1217 'name': 'multi-valued claim',
1222 'attribute': 'carLicense',
1223 'single_valued': True,
1224 'source_type': 'AD',
1225 'for_classes': ['user'],
1226 'value_type': claims
.CLAIM_TYPE_STRING
,
1227 'values': ('foo', 'bar', 'baz'),
1234 'name': 'missing attribute',
1238 'single_valued': True,
1239 'source_type': 'AD',
1240 'for_classes': ['user'],
1241 'value_type': claims
.CLAIM_TYPE_STRING
,
1247 'name': 'invalid attribute',
1252 'attribute': 'unicodePwd',
1253 'single_valued': True,
1254 'source_type': 'AD',
1255 'for_classes': ['user'],
1256 'value_type': claims
.CLAIM_TYPE_STRING
,
1262 'name': 'incorrect value type',
1267 'attribute': 'carLicense',
1268 'single_valued': True,
1269 'source_type': 'AD',
1270 'for_classes': ['user'],
1271 'value_type': claims
.CLAIM_TYPE_INT64
,
1278 'name': 'invalid value type',
1283 'attribute': 'carLicense',
1284 'single_valued': True,
1285 'source_type': 'AD',
1286 'for_classes': ['user'],
1294 'name': 'missing value type',
1299 'attribute': 'carLicense',
1300 'single_valued': True,
1301 'source_type': 'AD',
1302 'for_classes': ['user'],
1309 'name': 'string syntax, duplicate claim',
1314 'attribute': 'carLicense',
1315 'single_valued': True,
1316 'source_type': 'AD',
1317 'for_classes': ['user'],
1318 'value_type': claims
.CLAIM_TYPE_STRING
,
1322 ] * 2, # Create two string syntax claims.
1326 'name': 'multiple claims',
1331 'attribute': 'carLicense',
1332 'single_valued': True,
1333 'source_type': 'AD',
1334 'for_classes': ['user'],
1335 'value_type': claims
.CLAIM_TYPE_STRING
,
1336 'values': ('foo', 'bar', 'baz'),
1342 'attribute': 'msTSAllowLogon',
1343 'single_valued': True,
1344 'source_type': 'AD',
1345 'for_classes': ['user'],
1346 'value_type': claims
.CLAIM_TYPE_BOOLEAN
,
1354 'name': 'case difference for source type',
1359 'attribute': 'carLicense',
1360 'single_valued': True,
1361 'source_type': 'ad',
1362 'for_classes': ['user'],
1363 'value_type': claims
.CLAIM_TYPE_STRING
,
1371 'name': 'unhandled source type',
1376 'attribute': 'carLicense',
1377 'single_valued': True,
1378 'source_type': '<unknown>',
1379 'for_classes': ['user'],
1380 'value_type': claims
.CLAIM_TYPE_STRING
,
1387 'name': 'disabled claim',
1392 'attribute': 'carLicense',
1393 'single_valued': True,
1394 'source_type': 'AD',
1395 'for_classes': ['user'],
1396 'value_type': claims
.CLAIM_TYPE_STRING
,
1403 'name': 'not enabled claim',
1407 'attribute': 'carLicense',
1408 'single_valued': True,
1409 'source_type': 'AD',
1410 'for_classes': ['user'],
1411 'value_type': claims
.CLAIM_TYPE_STRING
,
1418 'name': 'not applicable to any class',
1423 'attribute': 'carLicense',
1424 'single_valued': True,
1425 'source_type': 'AD',
1426 'value_type': claims
.CLAIM_TYPE_STRING
,
1433 'name': 'not applicable to class',
1438 'attribute': 'carLicense',
1439 'single_valued': True,
1440 'source_type': 'AD',
1441 'for_classes': ['user'],
1442 'value_type': claims
.CLAIM_TYPE_STRING
,
1446 'class': 'computer',
1449 'name': 'applicable to class',
1454 'attribute': 'carLicense',
1455 'single_valued': True,
1456 'source_type': 'AD',
1457 'for_classes': ['user', 'computer'],
1458 'value_type': claims
.CLAIM_TYPE_STRING
,
1463 'class': 'computer',
1466 'name': 'applicable to base class',
1471 'attribute': 'carLicense',
1472 'single_valued': True,
1473 'source_type': 'AD',
1474 'for_classes': ['top'],
1475 'value_type': claims
.CLAIM_TYPE_STRING
,
1482 'name': 'applicable to base class 2',
1487 'attribute': 'carLicense',
1488 'single_valued': True,
1489 'source_type': 'AD',
1490 'for_classes': ['organizationalPerson'],
1491 'value_type': claims
.CLAIM_TYPE_STRING
,
1498 'name': 'large compressed claim',
1503 'attribute': 'carLicense',
1504 'single_valued': True,
1505 'source_type': 'AD',
1506 'for_classes': ['user'],
1507 'value_type': claims
.CLAIM_TYPE_STRING
,
1508 # a large value that should cause the claim to be
1510 'values': ('a' * 10000,),
1518 def _test_device_claims_with_args(self
, case
):
1519 # The group arrangement for the test.
1520 group_setup
= case
.pop('groups')
1522 # Groups that should be the primary group for the user and machine
1524 primary_group
= case
.pop('primary_group', None)
1525 mach_primary_group
= case
.pop('mach:primary_group', None)
1527 # Whether the TGS-REQ should be directed to the krbtgt.
1528 tgs_to_krbtgt
= case
.pop('tgs:to_krbtgt', None)
1530 # Whether the target server of the TGS-REQ should support compound
1531 # identity or resource SID compression.
1532 tgs_compound_id
= case
.pop('tgs:compound_id', None)
1533 tgs_compression
= case
.pop('tgs:compression', None)
1535 # Optional SIDs to replace those in the machine account PAC prior to a
1537 tgs_mach_sids
= case
.pop('tgs:mach:sids', None)
1539 # Optional machine SID to replace that in the PAC prior to a TGS-REQ.
1540 tgs_mach_sid
= case
.pop('tgs:mach_sid', None)
1542 # User flags that may be set or reset in the PAC prior to a TGS-REQ.
1543 tgs_mach_set_user_flags
= case
.pop('tgs:mach:set_user_flags', None)
1544 tgs_mach_reset_user_flags
= case
.pop('tgs:mach:reset_user_flags', None)
1546 # The SIDs we expect to see in the PAC after a AS-REQ or a TGS-REQ.
1547 as_expected
= case
.pop('as:expected', None)
1548 as_mach_expected
= case
.pop('as:mach:expected', None)
1549 tgs_expected
= case
.pop('tgs:expected', None)
1550 tgs_device_expected
= case
.pop('tgs:device:expected', None)
1552 # Whether to specify claims support in PA-PAC-OPTIONS.
1553 pac_options_claims
= case
.pop('pac-options:claims-support', None)
1555 all_claims
= case
.pop('claims')
1557 # There should be no parameters remaining in the testcase.
1558 self
.assertFalse(case
, 'unexpected parameters in testcase')
1560 if as_expected
is None:
1561 self
.assertIsNotNone(tgs_expected
,
1562 'no set of expected SIDs is provided')
1564 if as_mach_expected
is None:
1565 self
.assertIsNotNone(tgs_expected
,
1566 'no set of expected machine SIDs is provided')
1568 if tgs_to_krbtgt
is None:
1569 tgs_to_krbtgt
= False
1571 if tgs_compound_id
is None and not tgs_to_krbtgt
:
1572 # Assume the service supports compound identity by default.
1573 tgs_compound_id
= True
1576 self
.assertIsNone(tgs_device_expected
,
1577 'device SIDs are not added for a krbtgt request')
1579 self
.assertIsNotNone(tgs_expected
,
1580 'no set of expected TGS SIDs is provided')
1582 if tgs_mach_sid
is not None:
1583 self
.assertIsNotNone(tgs_mach_sids
,
1584 'specified TGS-REQ mach SID, but no '
1585 'accompanying machine SIDs provided')
1587 if tgs_mach_set_user_flags
is None:
1588 tgs_mach_set_user_flags
= 0
1590 self
.assertIsNotNone(tgs_mach_sids
,
1591 'specified TGS-REQ set user flags, but no '
1592 'accompanying machine SIDs provided')
1594 if tgs_mach_reset_user_flags
is None:
1595 tgs_mach_reset_user_flags
= 0
1597 self
.assertIsNotNone(tgs_mach_sids
,
1598 'specified TGS-REQ reset user flags, but no '
1599 'accompanying machine SIDs provided')
1601 if pac_options_claims
is None:
1602 pac_options_claims
= True
1606 unexpected_claims
) = self
.setup_claims(all_claims
)
1608 samdb
= self
.get_samdb()
1610 domain_sid
= samdb
.get_domain_sid()
1612 user_creds
= self
.get_cached_creds(
1613 account_type
=self
.AccountType
.USER
)
1614 user_dn
= user_creds
.get_dn()
1615 user_sid
= user_creds
.get_sid()
1617 mach_name
= self
.get_new_username()
1618 mach_creds
, mach_dn_str
= self
.create_account(
1621 account_type
=self
.AccountType
.COMPUTER
,
1622 additional_details
=details
)
1623 mach_dn
= ldb
.Dn(samdb
, mach_dn_str
)
1624 mach_sid
= mach_creds
.get_sid()
1626 user_principal
= Principal(user_dn
, user_sid
)
1627 mach_principal
= Principal(mach_dn
, mach_sid
)
1628 preexisting_groups
= {
1629 self
.user
: user_principal
,
1630 self
.mach
: mach_principal
,
1633 if primary_group
is not None:
1634 primary_groups
[user_principal
] = primary_group
1635 if mach_primary_group
is not None:
1636 primary_groups
[mach_principal
] = mach_primary_group
1637 groups
= self
.setup_groups(samdb
,
1643 tgs_user_sid
= user_sid
1644 tgs_user_domain_sid
, tgs_user_rid
= tgs_user_sid
.rsplit('-', 1)
1646 if tgs_mach_sid
is None:
1647 tgs_mach_sid
= mach_sid
1648 elif tgs_mach_sid
in groups
:
1649 tgs_mach_sid
= groups
[tgs_mach_sid
].sid
1651 tgs_mach_domain_sid
, tgs_mach_rid
= tgs_mach_sid
.rsplit('-', 1)
1653 expected_groups
= self
.map_sids(as_expected
, groups
,
1655 mach_expected_groups
= self
.map_sids(as_mach_expected
, groups
,
1657 tgs_mach_sids_mapped
= self
.map_sids(tgs_mach_sids
, groups
,
1658 tgs_mach_domain_sid
)
1659 tgs_expected_mapped
= self
.map_sids(tgs_expected
, groups
,
1660 tgs_user_domain_sid
)
1661 tgs_device_expected_mapped
= self
.map_sids(tgs_device_expected
, groups
,
1662 tgs_mach_domain_sid
)
1664 user_tgt
= self
.get_tgt(user_creds
, expected_groups
=expected_groups
)
1666 # Get a TGT for the computer.
1667 mach_tgt
= self
.get_tgt(mach_creds
, expect_pac
=True,
1668 expected_groups
=mach_expected_groups
,
1669 expect_client_claims
=True,
1670 expected_client_claims
=expected_claims
,
1671 unexpected_client_claims
=unexpected_claims
)
1673 if tgs_mach_sids
is not None:
1674 # Replace the SIDs in the PAC with the ones provided by the test.
1675 mach_tgt
= self
.ticket_with_sids(mach_tgt
,
1676 tgs_mach_sids_mapped
,
1677 tgs_mach_domain_sid
,
1679 set_user_flags
=tgs_mach_set_user_flags
,
1680 reset_user_flags
=tgs_mach_reset_user_flags
)
1683 self
.assertFalse(tgs_to_krbtgt
,
1684 'device claims are omitted for a krbtgt request, '
1685 'so specifying mod_values is probably a mistake!')
1687 # Change the value of attributes used for claims.
1688 mod_msg
.dn
= mach_dn
1689 samdb
.modify(mod_msg
)
1691 domain_sid
= samdb
.get_domain_sid()
1693 subkey
= self
.RandomKey(user_tgt
.session_key
.etype
)
1695 armor_subkey
= self
.RandomKey(subkey
.etype
)
1696 explicit_armor_key
= self
.generate_armor_key(armor_subkey
,
1697 mach_tgt
.session_key
)
1698 armor_key
= kcrypto
.cf2(explicit_armor_key
.key
,
1702 armor_key
= Krb5EncryptionKey(armor_key
, None)
1704 target_creds
, sname
= self
.get_target(
1705 to_krbtgt
=tgs_to_krbtgt
,
1706 compound_id
=tgs_compound_id
,
1707 compression
=tgs_compression
)
1708 srealm
= target_creds
.get_realm()
1710 decryption_key
= self
.TicketDecryptionKey_from_creds(
1713 etypes
= (AES256_CTS_HMAC_SHA1_96
, ARCFOUR_HMAC_MD5
)
1716 if pac_options_claims
:
1717 pac_options
= '1' # claims support
1719 pac_options
= '0' # no claims support
1721 requester_sid
= None
1723 requester_sid
= user_sid
1725 if not tgs_compound_id
:
1726 expected_claims
= None
1727 unexpected_claims
= None
1729 # Get a service ticket for the user, using the computer's TGT as an
1730 # armor TGT. The claim value should not have changed.
1732 kdc_exchange_dict
= self
.tgs_exchange_dict(
1734 expected_crealm
=user_tgt
.crealm
,
1735 expected_cname
=user_tgt
.cname
,
1736 expected_srealm
=srealm
,
1737 expected_sname
=sname
,
1738 ticket_decryption_key
=decryption_key
,
1739 generate_fast_fn
=self
.generate_simple_fast
,
1740 generate_fast_armor_fn
=self
.generate_ap_req
,
1741 check_rep_fn
=self
.generic_check_kdc_rep
,
1742 check_kdc_private_fn
=self
.generic_check_kdc_private
,
1744 armor_key
=armor_key
,
1746 armor_subkey
=armor_subkey
,
1747 pac_options
=pac_options
,
1748 authenticator_subkey
=subkey
,
1749 kdc_options
=kdc_options
,
1751 expect_pac_attrs
=tgs_to_krbtgt
,
1752 expect_pac_attrs_pac_request
=tgs_to_krbtgt
,
1753 expected_sid
=tgs_user_sid
,
1754 expected_requester_sid
=requester_sid
,
1755 expected_domain_sid
=tgs_user_domain_sid
,
1756 expected_device_domain_sid
=tgs_mach_domain_sid
,
1757 expected_groups
=tgs_expected_mapped
,
1758 unexpected_groups
=None,
1759 expect_client_claims
=True,
1760 expected_client_claims
=None,
1761 expect_device_info
=bool(tgs_compound_id
),
1762 expected_device_groups
=tgs_device_expected_mapped
,
1763 expect_device_claims
=bool(tgs_compound_id
),
1764 expected_device_claims
=expected_claims
,
1765 unexpected_device_claims
=unexpected_claims
)
1767 rep
= self
._generic
_kdc
_exchange
(kdc_exchange_dict
,
1772 self
.check_reply(rep
, KRB_TGS_REP
)
1774 device_claims_cases
= [
1776 # Make a TGS request containing claims, but omit the Claims Valid
1778 'test': 'device to service no claims valid sid',
1780 # Some groups to test how the device info is generated.
1781 'foo': (GroupType
.DOMAIN_LOCAL
, {mach}
),
1782 'bar': (GroupType
.DOMAIN_LOCAL
, {mach}
),
1788 'attribute': 'middleName',
1789 'single_valued': True,
1790 'source_type': 'AD',
1791 'for_classes': ['computer'],
1792 'value_type': claims
.CLAIM_TYPE_STRING
,
1795 'mod_values': ['bar'],
1799 (asserted_identity
, SidType
.EXTRA_SID
, default_attrs
),
1800 (security
.DOMAIN_RID_USERS
, SidType
.BASE_SID
, default_attrs
),
1801 (security
.DOMAIN_RID_USERS
, SidType
.PRIMARY_GID
, None),
1802 (security
.SID_CLAIMS_VALID
, SidType
.EXTRA_SID
, default_attrs
),
1804 'as:mach:expected': {
1805 (asserted_identity
, SidType
.EXTRA_SID
, default_attrs
),
1806 (security
.DOMAIN_RID_DOMAIN_MEMBERS
, SidType
.BASE_SID
, default_attrs
),
1807 (security
.DOMAIN_RID_DOMAIN_MEMBERS
, SidType
.PRIMARY_GID
, None),
1808 (security
.SID_CLAIMS_VALID
, SidType
.EXTRA_SID
, default_attrs
),
1811 (asserted_identity
, SidType
.EXTRA_SID
, default_attrs
),
1812 (security
.DOMAIN_RID_DOMAIN_MEMBERS
, SidType
.BASE_SID
, default_attrs
),
1813 (security
.DOMAIN_RID_DOMAIN_MEMBERS
, SidType
.PRIMARY_GID
, None),
1814 # Omit the Claims Valid SID, and verify that this doesn't
1815 # affect the propagation of claims into the final ticket.
1817 # Some extra SIDs to show how they are propagated into the
1819 ('S-1-5-22-1-2-3-4', SidType
.EXTRA_SID
, default_attrs
),
1820 ('S-1-5-22-1-2-3-5', SidType
.EXTRA_SID
, default_attrs
),
1822 'tgs:to_krbtgt': False,
1824 (security
.SID_AUTHENTICATION_AUTHORITY_ASSERTED_IDENTITY
, SidType
.EXTRA_SID
, default_attrs
),
1825 (security
.SID_CLAIMS_VALID
, SidType
.EXTRA_SID
, default_attrs
),
1826 (security
.SID_COMPOUNDED_AUTHENTICATION
, SidType
.EXTRA_SID
, default_attrs
),
1827 (security
.DOMAIN_RID_USERS
, SidType
.BASE_SID
, default_attrs
),
1828 (security
.DOMAIN_RID_USERS
, SidType
.PRIMARY_GID
, None),
1830 'tgs:device:expected': {
1831 (security
.DOMAIN_RID_DOMAIN_MEMBERS
, SidType
.BASE_SID
, default_attrs
),
1832 (security
.DOMAIN_RID_DOMAIN_MEMBERS
, SidType
.PRIMARY_GID
, None),
1833 (asserted_identity
, SidType
.EXTRA_SID
, default_attrs
),
1834 ('S-1-5-22-1-2-3-4', SidType
.EXTRA_SID
, default_attrs
),
1835 ('S-1-5-22-1-2-3-5', SidType
.EXTRA_SID
, default_attrs
),
1837 ('foo', SidType
.RESOURCE_SID
, resource_attrs
),
1838 ('bar', SidType
.RESOURCE_SID
, resource_attrs
),
1843 # Make a TGS request containing claims to a service that lacks
1844 # support for compound identity. The claims are not propagated to
1846 'test': 'device to service no compound id',
1848 'foo': (GroupType
.DOMAIN_LOCAL
, {mach}
),
1849 'bar': (GroupType
.DOMAIN_LOCAL
, {mach}
),
1855 'attribute': 'middleName',
1856 'single_valued': True,
1857 'source_type': 'AD',
1858 'for_classes': ['computer'],
1859 'value_type': claims
.CLAIM_TYPE_STRING
,
1862 'mod_values': ['bar'],
1866 (asserted_identity
, SidType
.EXTRA_SID
, default_attrs
),
1867 (security
.DOMAIN_RID_USERS
, SidType
.BASE_SID
, default_attrs
),
1868 (security
.DOMAIN_RID_USERS
, SidType
.PRIMARY_GID
, None),
1869 (security
.SID_CLAIMS_VALID
, SidType
.EXTRA_SID
, default_attrs
),
1871 'as:mach:expected': {
1872 (asserted_identity
, SidType
.EXTRA_SID
, default_attrs
),
1873 (security
.DOMAIN_RID_DOMAIN_MEMBERS
, SidType
.BASE_SID
, default_attrs
),
1874 (security
.DOMAIN_RID_DOMAIN_MEMBERS
, SidType
.PRIMARY_GID
, None),
1875 (security
.SID_CLAIMS_VALID
, SidType
.EXTRA_SID
, default_attrs
),
1877 'tgs:to_krbtgt': False,
1878 # Compound identity is unsupported.
1879 'tgs:compound_id': False,
1881 (security
.SID_AUTHENTICATION_AUTHORITY_ASSERTED_IDENTITY
, SidType
.EXTRA_SID
, default_attrs
),
1882 (security
.SID_CLAIMS_VALID
, SidType
.EXTRA_SID
, default_attrs
),
1883 # The Compounded Authentication SID should not be present.
1884 (security
.DOMAIN_RID_USERS
, SidType
.BASE_SID
, default_attrs
),
1885 (security
.DOMAIN_RID_USERS
, SidType
.PRIMARY_GID
, None),
1889 # Make a TGS request containing claims to a service, but don't
1890 # specify support for claims in PA-PAC-OPTIONS. We still expect the
1891 # final PAC to contain claims.
1892 'test': 'device to service no claims support in pac options',
1894 'foo': (GroupType
.DOMAIN_LOCAL
, {mach}
),
1895 'bar': (GroupType
.DOMAIN_LOCAL
, {mach}
),
1901 'attribute': 'middleName',
1902 'single_valued': True,
1903 'source_type': 'AD',
1904 'for_classes': ['computer'],
1905 'value_type': claims
.CLAIM_TYPE_STRING
,
1908 'mod_values': ['bar'],
1912 (asserted_identity
, SidType
.EXTRA_SID
, default_attrs
),
1913 (security
.DOMAIN_RID_USERS
, SidType
.BASE_SID
, default_attrs
),
1914 (security
.DOMAIN_RID_USERS
, SidType
.PRIMARY_GID
, None),
1915 (security
.SID_CLAIMS_VALID
, SidType
.EXTRA_SID
, default_attrs
),
1917 'as:mach:expected': {
1918 (asserted_identity
, SidType
.EXTRA_SID
, default_attrs
),
1919 (security
.DOMAIN_RID_DOMAIN_MEMBERS
, SidType
.BASE_SID
, default_attrs
),
1920 (security
.DOMAIN_RID_DOMAIN_MEMBERS
, SidType
.PRIMARY_GID
, None),
1921 (security
.SID_CLAIMS_VALID
, SidType
.EXTRA_SID
, default_attrs
),
1923 'tgs:to_krbtgt': False,
1924 # Claims are unsupported.
1925 'pac-options:claims-support': False,
1927 (security
.SID_AUTHENTICATION_AUTHORITY_ASSERTED_IDENTITY
, SidType
.EXTRA_SID
, default_attrs
),
1928 (security
.SID_CLAIMS_VALID
, SidType
.EXTRA_SID
, default_attrs
),
1929 (security
.SID_COMPOUNDED_AUTHENTICATION
, SidType
.EXTRA_SID
, default_attrs
),
1930 (security
.DOMAIN_RID_USERS
, SidType
.BASE_SID
, default_attrs
),
1931 (security
.DOMAIN_RID_USERS
, SidType
.PRIMARY_GID
, None),
1933 'tgs:device:expected': {
1934 (security
.DOMAIN_RID_DOMAIN_MEMBERS
, SidType
.BASE_SID
, default_attrs
),
1935 (security
.DOMAIN_RID_DOMAIN_MEMBERS
, SidType
.PRIMARY_GID
, None),
1937 ('foo', SidType
.RESOURCE_SID
, resource_attrs
),
1938 ('bar', SidType
.RESOURCE_SID
, resource_attrs
),
1940 (asserted_identity
, SidType
.EXTRA_SID
, default_attrs
),
1941 frozenset([(security
.SID_CLAIMS_VALID
, SidType
.RESOURCE_SID
, default_attrs
)]),
1946 def test_auth_silo_claim(self
):
1947 self
.run_auth_silo_claim_test()
1949 def test_auth_silo_claim_unenforced(self
):
1950 # The claim is not present if the silo is unenforced.
1951 self
.run_auth_silo_claim_test(enforced
=False,
1954 def test_auth_silo_claim_not_a_member(self
):
1955 # The claim is not present if the user is not a member of the silo.
1956 self
.run_auth_silo_claim_test(add_to_silo
=False,
1959 def test_auth_silo_claim_unassigned(self
):
1960 # The claim is not present if the user is not assigned to the silo.
1961 self
.run_auth_silo_claim_test(assigned
=False,
1964 def test_auth_silo_claim_assigned_to_wrong_dn(self
):
1965 samdb
= self
.get_samdb()
1967 # The claim is not present if the user is assigned to some other DN.
1968 self
.run_auth_silo_claim_test(assigned
=self
.get_server_dn(samdb
),
1971 def run_auth_silo_claim_test(self
, *,
1976 # Create a new authentication silo.
1977 silo
= self
.create_authn_silo(enforced
=enforced
)
1979 account_options
= None
1980 if assigned
is not False:
1981 if assigned
is True:
1985 'additional_details': self
.freeze({
1986 # The user is assigned to the authentication silo we just
1987 # created, or to some DN specified by a test.
1988 'msDS-AssignedAuthNPolicySilo': str(assigned
),
1992 # Create the user account.
1993 creds
= self
.get_cached_creds(
1994 account_type
=self
.AccountType
.USER
,
1995 opts
=account_options
)
1998 # Add the account to the silo.
1999 self
.add_to_group(str(creds
.get_dn()),
2001 'msDS-AuthNPolicySiloMembers',
2004 claim_id
= self
.create_authn_silo_claim_id()
2009 'source_type': claims
.CLAIMS_SOURCE_TYPE_AD
,
2010 'type': claims
.CLAIM_TYPE_STRING
,
2011 # Expect a claim containing the name of the silo.
2012 'values': (silo
.name
,),
2015 unexpected_claims
= None
2017 expected_claims
= None
2018 unexpected_claims
= {claim_id}
2020 # Get a TGT and check whether the claim is present or missing.
2023 expect_client_claims
=True,
2024 expected_client_claims
=expected_claims
,
2025 unexpected_client_claims
=unexpected_claims
)
2028 if __name__
== '__main__':
2029 global_asn1_print
= False
2030 global_hexdump
= False