2 # Unix SMB/CIFS implementation.
3 # Copyright (C) Stefan Metzmacher 2020
5 # This program is free software; you can redistribute it and/or modify
6 # it under the terms of the GNU General Public License as published by
7 # the Free Software Foundation; either version 3 of the License, or
8 # (at your option) any later version.
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # GNU General Public License for more details.
15 # You should have received a copy of the GNU General Public License
16 # along with this program. If not, see <http://www.gnu.org/licenses/>.
22 sys
.path
.insert(0, "bin/python")
23 os
.environ
["PYTHONUNBUFFERED"] = "1"
25 from samba
.tests
import env_get_var_value
26 from samba
.tests
.krb5
.kcrypto
import Cksumtype
27 from samba
.tests
.krb5
.raw_testcase
import RawKerberosTest
28 from samba
.tests
.krb5
.rfc4120_constants
import (
31 KU_TGS_REP_ENC_PART_SUB_KEY
,
33 import samba
.tests
.krb5
.rfc4120_pyasn1
as krb5_asn1
35 global_asn1_print
= False
36 global_hexdump
= False
38 class S4UKerberosTests(RawKerberosTest
):
41 super(S4UKerberosTests
, self
).setUp()
42 self
.do_asn1_print
= global_asn1_print
43 self
.do_hexdump
= global_hexdump
45 def _test_s4u2self(self
, pa_s4u2self_ctype
=None):
46 service_creds
= self
.get_service_creds()
47 service
= service_creds
.get_username()
48 realm
= service_creds
.get_realm()
50 cname
= self
.PrincipalName_create(name_type
=1, names
=[service
])
51 sname
= self
.PrincipalName_create(name_type
=2, names
=["krbtgt", realm
])
53 till
= self
.get_KerberosTime(offset
=36000)
55 kdc_options
= krb5_asn1
.KDCOptions('forwardable')
60 req
= self
.AS_REQ_create(padata
=padata
,
61 kdc_options
=str(kdc_options
),
71 EncAuthorizationData
=None,
72 EncAuthorizationData_key
=None,
73 additional_tickets
=None)
74 rep
= self
.send_recv_transaction(req
)
75 self
.assertIsNotNone(rep
)
77 self
.assertEqual(rep
['msg-type'], 30)
78 self
.assertEqual(rep
['error-code'], 25)
79 rep_padata
= self
.der_decode(rep
['e-data'], asn1Spec
=krb5_asn1
.METHOD_DATA())
82 if pa
['padata-type'] == 19:
83 etype_info2
= pa
['padata-value']
86 etype_info2
= self
.der_decode(etype_info2
, asn1Spec
=krb5_asn1
.ETYPE_INFO2())
88 key
= self
.PasswordKey_from_etype_info2(service_creds
, etype_info2
[0])
90 (patime
, pausec
) = self
.get_KerberosTimeWithUsec()
91 pa_ts
= self
.PA_ENC_TS_ENC_create(patime
, pausec
)
92 pa_ts
= self
.der_encode(pa_ts
, asn1Spec
=krb5_asn1
.PA_ENC_TS_ENC())
94 pa_ts
= self
.EncryptedData_create(key
, KU_PA_ENC_TIMESTAMP
, pa_ts
)
95 pa_ts
= self
.der_encode(pa_ts
, asn1Spec
=krb5_asn1
.EncryptedData())
97 pa_ts
= self
.PA_DATA_create(2, pa_ts
)
99 kdc_options
= krb5_asn1
.KDCOptions('forwardable')
102 req
= self
.AS_REQ_create(padata
=padata
,
103 kdc_options
=str(kdc_options
),
113 EncAuthorizationData
=None,
114 EncAuthorizationData_key
=None,
115 additional_tickets
=None)
116 rep
= self
.send_recv_transaction(req
)
117 self
.assertIsNotNone(rep
)
119 msg_type
= rep
['msg-type']
120 self
.assertEqual(msg_type
, 11)
122 enc_part2
= key
.decrypt(KU_AS_REP_ENC_PART
, rep
['enc-part']['cipher'])
123 enc_part2
= self
.der_decode(enc_part2
, asn1Spec
=krb5_asn1
.EncASRepPart())
128 for_user_name
= env_get_var_value('FOR_USER')
129 uname
= self
.PrincipalName_create(name_type
=1, names
=[for_user_name
])
131 kdc_options
= krb5_asn1
.KDCOptions('forwardable')
132 till
= self
.get_KerberosTime(offset
=36000)
133 ticket
= rep
['ticket']
134 ticket_session_key
= self
.EncryptionKey_import(enc_part2
['key'])
135 pa_s4u
= self
.PA_S4U2Self_create(name
=uname
, realm
=realm
,
136 tgt_session_key
=ticket_session_key
,
137 ctype
=pa_s4u2self_ctype
)
140 subkey
= self
.RandomKey(ticket_session_key
.etype
)
142 (ctime
, cusec
) = self
.get_KerberosTimeWithUsec()
144 req
= self
.TGS_REQ_create(padata
=padata
,
148 kdc_options
=str(kdc_options
),
158 EncAuthorizationData
=None,
159 EncAuthorizationData_key
=None,
160 additional_tickets
=None,
161 ticket_session_key
=ticket_session_key
,
162 authenticator_subkey
=subkey
)
163 rep
= self
.send_recv_transaction(req
)
164 self
.assertIsNotNone(rep
)
166 msg_type
= rep
['msg-type']
168 enc_part2
= subkey
.decrypt(
169 KU_TGS_REP_ENC_PART_SUB_KEY
, rep
['enc-part']['cipher'])
170 enc_part2
= self
.der_decode(enc_part2
, asn1Spec
=krb5_asn1
.EncTGSRepPart())
174 # Using the checksum type from the tgt_session_key happens to work everywhere
175 def test_s4u2self(self
):
176 msg_type
= self
._test
_s
4u2self
()
177 self
.assertEqual(msg_type
, 13)
179 # Per spec, the checksum of PA-FOR-USER is HMAC_MD5, see [MS-SFU] 2.2.1
180 def test_s4u2self_hmac_md5_checksum(self
):
181 msg_type
= self
._test
_s
4u2self
(pa_s4u2self_ctype
=Cksumtype
.HMAC_MD5
)
182 self
.assertEqual(msg_type
, 13)
184 def test_s4u2self_md5_unkeyed_checksum(self
):
185 msg_type
= self
._test
_s
4u2self
(pa_s4u2self_ctype
=Cksumtype
.MD5
)
186 self
.assertEqual(msg_type
, 30)
188 def test_s4u2self_sha1_unkeyed_checksum(self
):
189 msg_type
= self
._test
_s
4u2self
(pa_s4u2self_ctype
=Cksumtype
.SHA1
)
190 self
.assertEqual(msg_type
, 30)
192 def test_s4u2self_crc32_unkeyed_checksum(self
):
193 msg_type
= self
._test
_s
4u2self
(pa_s4u2self_ctype
=Cksumtype
.CRC32
)
194 self
.assertEqual(msg_type
, 30)
196 if __name__
== "__main__":
197 global_asn1_print
= True
198 global_hexdump
= True