2 # Unix SMB/CIFS implementation.
3 # Copyright (C) Stefan Metzmacher 2020
4 # Copyright (C) 2021 Catalyst.Net Ltd
6 # This program is free software; you can redistribute it and/or modify
7 # it under the terms of the GNU General Public License as published by
8 # the Free Software Foundation; either version 3 of the License, or
9 # (at your option) any later version.
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
16 # You should have received a copy of the GNU General Public License
17 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 from samba
.tests
import delete_force
26 import samba
.tests
.krb5
.kcrypto
as kcrypto
27 from samba
.tests
.krb5
.kdc_base_test
import KDCBaseTest
28 from samba
.tests
.krb5
.rfc4120_constants
import (
29 AES256_CTS_HMAC_SHA1_96
,
31 KDC_ERR_CLIENT_NAME_MISMATCH
,
35 sys
.path
.insert(0, 'bin/python')
36 os
.environ
['PYTHONUNBUFFERED'] = '1'
38 global_asn1_print
= False
39 global_hexdump
= False
42 class AliasTests(KDCBaseTest
):
43 def test_dc_alias_rename(self
):
44 self
._run
_dc
_alias
(action
='rename')
46 def test_dc_alias_delete(self
):
47 self
._run
_dc
_alias
(action
='delete')
49 def _run_dc_alias(self
, action
=None):
50 target_creds
= self
.get_dc_creds()
51 target_name
= target_creds
.get_username()[:-1]
53 self
._run
_alias
(target_name
, lambda: target_creds
, action
=action
)
55 def test_create_alias_rename(self
):
56 self
._run
_create
_alias
(action
='rename')
58 def test_create_alias_delete(self
):
59 self
._run
_create
_alias
(action
='delete')
61 def _run_create_alias(self
, action
=None):
62 target_name
= self
.get_new_username()
65 samdb
= self
.get_samdb()
67 realm
= samdb
.domain_dns_name().lower()
69 hostname
= f
'{target_name}.{realm}'
70 spn
= f
'ldap/{hostname}'
73 'dNSHostName': hostname
76 creds
, fn
= self
.create_account(
79 account_type
=self
.AccountType
.COMPUTER
,
81 additional_details
=details
)
85 self
._run
_alias
(target_name
, create_target
, action
=action
)
87 def _run_alias(self
, target_name
, target_creds_fn
, action
=None):
88 samdb
= self
.get_samdb()
90 mach_name
= self
.get_new_username()
92 # Create a machine account.
93 mach_creds
, mach_dn
= self
.create_account(
94 samdb
, mach_name
, account_type
=self
.AccountType
.COMPUTER
)
95 self
.addCleanup(delete_force
, samdb
, mach_dn
)
97 mach_sid
= self
.get_objectSid(samdb
, mach_dn
)
98 realm
= mach_creds
.get_realm()
100 # The account salt doesn't change when the account is renamed.
101 old_salt
= mach_creds
.get_salt()
102 mach_creds
.set_forced_salt(old_salt
)
104 # Rename the account to alias with the target account.
105 msg
= ldb
.Message(ldb
.Dn(samdb
, mach_dn
))
106 msg
['sAMAccountName'] = ldb
.MessageElement(target_name
,
107 ldb
.FLAG_MOD_REPLACE
,
110 mach_creds
.set_username(target_name
)
112 # Get a TGT for the machine account.
113 tgt
= self
.get_tgt(mach_creds
, kdc_options
='0', fresh
=True)
116 pac_data
= self
.get_pac_data(tgt
.ticket_private
['authorization-data'])
118 upn
= f
'{target_name}@{realm.lower()}'
120 self
.assertEqual(target_name
, str(pac_data
.account_name
))
121 self
.assertEqual(mach_sid
, pac_data
.account_sid
)
122 self
.assertEqual(target_name
, pac_data
.logon_name
)
123 self
.assertEqual(upn
, pac_data
.upn
)
124 self
.assertEqual(realm
, pac_data
.domain_name
)
126 # Rename or delete the machine account.
127 if action
== 'rename':
128 mach_name2
= self
.get_new_username()
130 msg
= ldb
.Message(ldb
.Dn(samdb
, mach_dn
))
131 msg
['sAMAccountName'] = ldb
.MessageElement(mach_name2
,
132 ldb
.FLAG_MOD_REPLACE
,
135 elif action
== 'delete':
136 samdb
.delete(mach_dn
)
140 # Get the credentials for the target account.
141 target_creds
= target_creds_fn()
143 # Look up the DNS host name of the target account.
144 target_dn
= target_creds
.get_dn()
145 res
= samdb
.search(target_dn
,
146 scope
=ldb
.SCOPE_BASE
,
147 attrs
=['dNSHostName'])
148 target_hostname
= str(res
[0].get('dNSHostName', idx
=0))
150 sname
= self
.PrincipalName_create(name_type
=NT_PRINCIPAL
,
151 names
=['ldap', target_hostname
])
152 target_cname
= self
.PrincipalName_create(name_type
=NT_PRINCIPAL
,
155 target_decryption_key
= self
.TicketDecryptionKey_from_creds(
158 authenticator_subkey
= self
.RandomKey(kcrypto
.Enctype
.AES256
)
160 etypes
= (AES256_CTS_HMAC_SHA1_96
, ARCFOUR_HMAC_MD5
)
162 def generate_s4u2self_padata(_kdc_exchange_dict
,
165 padata
= self
.PA_S4U2Self_create(name
=target_cname
,
167 tgt_session_key
=tgt
.session_key
,
169 return [padata
], req_body
171 expected_error_mode
= KDC_ERR_CLIENT_NAME_MISMATCH
173 # Make a request using S4U2Self. The request should fail.
174 kdc_exchange_dict
= self
.tgs_exchange_dict(
175 expected_crealm
=realm
,
176 expected_cname
=target_cname
,
177 expected_srealm
=realm
,
178 expected_sname
=sname
,
179 ticket_decryption_key
=target_decryption_key
,
180 generate_padata_fn
=generate_s4u2self_padata
,
181 expected_error_mode
=expected_error_mode
,
182 check_error_fn
=self
.generic_check_kdc_error
,
183 check_kdc_private_fn
=self
.generic_check_kdc_private
,
185 authenticator_subkey
=authenticator_subkey
,
189 rep
= self
._generic
_kdc
_exchange
(kdc_exchange_dict
,
194 self
.check_error_rep(rep
, expected_error_mode
)
197 if __name__
== '__main__':
198 global_asn1_print
= False
199 global_hexdump
= False