tests/krb5: Test Windows 2000 variant of PK-INIT
authorJoseph Sutton <josephsutton@catalyst.net.nz>
Tue, 4 Jul 2023 03:28:04 +0000 (15:28 +1200)
committerAndrew Bartlett <abartlet@samba.org>
Wed, 19 Jul 2023 01:47:33 +0000 (01:47 +0000)
Signed-off-by: Joseph Sutton <josephsutton@catalyst.net.nz>
Reviewed-by: Andrew Bartlett <abartlet@samba.org>
python/samba/tests/krb5/pkinit_tests.py
python/samba/tests/krb5/raw_testcase.py
selftest/knownfail_heimdal_kdc
selftest/knownfail_mit_kdc_1_20

index 1e1ff1af4548be68ff30826dd0fc9c9e4eb689d3..66f056b9abd78b7fdd0eacc5af88a370153214cc 100755 (executable)
@@ -49,6 +49,7 @@ from samba.tests.krb5.rfc4120_constants import (
     NT_PRINCIPAL,
     PADATA_AS_FRESHNESS,
     PADATA_ENC_TIMESTAMP,
+    PADATA_PK_AS_REP_19,
     PADATA_PK_AS_REQ,
 )
 import samba.tests.krb5.rfc4120_pyasn1 as krb5_asn1
@@ -92,6 +93,13 @@ class PkInitTests(KDCBaseTest):
         self._pkinit_req(client_creds, target_creds,
                          using_pkinit=PkInit.DIFFIE_HELLMAN)
 
+    def test_pkinit_win2k(self):
+        """Test public-key Windows 2000 PK-INIT."""
+        client_creds = self._get_creds()
+        target_creds = self.get_service_creds()
+
+        self._pkinit_req(client_creds, target_creds, win2k_variant=True)
+
     def test_pkinit_no_des3(self):
         """Test public-key PK-INIT without specifying the DES3 encryption
         type. It should fail."""
@@ -172,6 +180,13 @@ class PkInitTests(KDCBaseTest):
         self._pkinit_req(client_creds, target_creds,
                          using_pkinit=PkInit.DIFFIE_HELLMAN)
 
+    def test_pkinit_computer_win2k(self):
+        """Test public-key Windows 2000 PK-INIT with a computer account."""
+        client_creds = self._get_creds(self.AccountType.COMPUTER)
+        target_creds = self.get_service_creds()
+
+        self._pkinit_req(client_creds, target_creds, win2k_variant=True)
+
     def test_pkinit_service(self):
         """Test public-key PK-INIT with a service account."""
         client_creds = self._get_creds(self.AccountType.MANAGED_SERVICE)
@@ -187,6 +202,13 @@ class PkInitTests(KDCBaseTest):
         self._pkinit_req(client_creds, target_creds,
                          using_pkinit=PkInit.DIFFIE_HELLMAN)
 
+    def test_pkinit_service_win2k(self):
+        """Test public-key Windows 2000 PK-INIT with a service account."""
+        client_creds = self._get_creds(self.AccountType.MANAGED_SERVICE)
+        target_creds = self.get_service_creds()
+
+        self._pkinit_req(client_creds, target_creds, win2k_variant=True)
+
     def test_pkinit_no_supported_cms_types(self):
         """Test public-key PK-INIT, excluding the supportedCmsTypes field. This
         causes Windows to reply with differently-encoded ASN.1."""
@@ -243,6 +265,16 @@ class PkInitTests(KDCBaseTest):
             using_pkinit=PkInit.DIFFIE_HELLMAN,
             signature_algorithm=krb5_asn1.id_pkcs1_sha256WithRSAEncryption)
 
+    def test_pkinit_sha256_signature_win2k(self):
+        """Test public-key Windows 2000 PK-INIT with a SHA256 signature."""
+        client_creds = self._get_creds()
+        target_creds = self.get_service_creds()
+
+        self._pkinit_req(
+            client_creds, target_creds,
+            signature_algorithm=krb5_asn1.id_pkcs1_sha256WithRSAEncryption,
+            win2k_variant=True)
+
     def test_pkinit_sha256_certificate_signature(self):
         """Test public-key PK-INIT with a SHA256 certificate signature."""
         client_creds = self._get_creds()
@@ -262,6 +294,17 @@ class PkInitTests(KDCBaseTest):
             using_pkinit=PkInit.DIFFIE_HELLMAN,
             certificate_signature=hashes.SHA256)
 
+    def test_pkinit_sha256_certificate_signature_win2k(self):
+        """Test public-key Windows 2000 PK-INIT with a SHA256 certificate
+        signature."""
+        client_creds = self._get_creds()
+        target_creds = self.get_service_creds()
+
+        self._pkinit_req(
+            client_creds, target_creds,
+            certificate_signature=hashes.SHA256,
+            win2k_variant=True)
+
     def test_pkinit_freshness(self):
         """Test public-key PK-INIT with the PKINIT Freshness Extension."""
         client_creds = self._get_creds()
@@ -632,6 +675,7 @@ class PkInitTests(KDCBaseTest):
                     signature_algorithm=None,
                     certificate_signature=None,
                     freshness_token=None,
+                    win2k_variant=False,
                     ):
         self.assertIsNot(using_pkinit, PkInit.NOT_USED)
 
@@ -843,18 +887,28 @@ class PkInitTests(KDCBaseTest):
         def generate_pk_padata(_kdc_exchange_dict,
                                _callback_dict,
                                req_body):
-            checksum_blob = self.der_encode(
-                req_body,
-                asn1Spec=krb5_asn1.KDC_REQ_BODY())
+            if win2k_variant:
+                digest = None
+            else:
+                checksum_blob = self.der_encode(
+                    req_body,
+                    asn1Spec=krb5_asn1.KDC_REQ_BODY())
 
-            # Calculate the SHA1 checksum over the KDC-REQ-BODY. This checksum
-            # is required to be present in the authenticator, and must be SHA1.
-            digest = hashes.Hash(hashes.SHA1(), default_backend())
-            digest.update(checksum_blob)
-            digest = digest.finalize()
+                # Calculate the SHA1 checksum over the KDC-REQ-BODY. This checksum
+                # is required to be present in the authenticator, and must be SHA1.
+                digest = hashes.Hash(hashes.SHA1(), default_backend())
+                digest.update(checksum_blob)
+                digest = digest.finalize()
 
             ctime, cusec = self.get_KerberosTimeWithUsec()
 
+            if win2k_variant:
+                krbtgt_sname = self.get_krbtgt_sname()
+                krbtgt_realm = self.get_krbtgt_creds().get_realm()
+            else:
+                krbtgt_sname = None
+                krbtgt_realm = None
+
             # Create the authenticator, which shows that we had possession of
             # the private key at some point.
             authenticator_obj = self.PKAuthenticator_create(
@@ -862,7 +916,10 @@ class PkInitTests(KDCBaseTest):
                 ctime,
                 pk_nonce,
                 pa_checksum=digest,
-                freshness_token=freshness_token)
+                freshness_token=freshness_token,
+                kdc_name=krbtgt_sname,
+                kdc_realm=krbtgt_realm,
+                win2k_variant=win2k_variant)
 
             if using_pkinit is PkInit.DIFFIE_HELLMAN:
                 dh_public_key = dh_private_key.public_key()
@@ -899,7 +956,9 @@ class PkInitTests(KDCBaseTest):
             # differently encoded ReplyKeyPack, wrapping it first in a
             # ContentInfo structure.
             nonlocal supported_cms_types
-            if supported_cms_types is False:
+            if win2k_variant:
+                self.assertIsNone(supported_cms_types)
+            elif supported_cms_types is False:
                 # Exclude this field.
                 supported_cms_types = None
             elif supported_cms_types is None:
@@ -916,10 +975,13 @@ class PkInitTests(KDCBaseTest):
                 authenticator_obj,
                 client_public_value=client_public_value,
                 supported_cms_types=supported_cms_types,
-                client_dh_nonce=client_dh_nonce)
+                client_dh_nonce=client_dh_nonce,
+                win2k_variant=win2k_variant)
 
-            auth_pack = self.der_encode(auth_pack_obj,
-                                        asn1Spec=krb5_asn1.AuthPack())
+            asn1_spec = (krb5_asn1.AuthPack_Win2k
+                         if win2k_variant
+                         else krb5_asn1.AuthPack)
+            auth_pack = self.der_encode(auth_pack_obj, asn1Spec=asn1_spec())
 
             signature_hash = self.hash_from_algorithm(signature_algorithm)
 
@@ -964,9 +1026,13 @@ class PkInitTests(KDCBaseTest):
                                               # trusted by the client, that can
                                               # be used to certify the KDC.
                                               trusted_certifiers=None,
-                                              kdc_pk_id=None)
+                                              kdc_pk_id=None,
+                                              win2k_variant=win2k_variant)
 
-            padata = [self.PA_DATA_create(PADATA_PK_AS_REQ, pk_as_req)]
+            pa_type = (PADATA_PK_AS_REP_19
+                       if win2k_variant
+                       else PADATA_PK_AS_REQ)
+            padata = [self.PA_DATA_create(pa_type, pk_as_req)]
 
             return padata, req_body
 
index b7d2d2a42e54db2e956e6d583849a3701f7a45c8..17a0fe906ac0a8009b83f5bbec5db1408c39cf7b 100644 (file)
@@ -1842,7 +1842,19 @@ class RawKerberosTest(TestCase):
                                nonce,
                                *,
                                pa_checksum=None,
-                               freshness_token=None):
+                               freshness_token=None,
+                               kdc_name=None,
+                               kdc_realm=None,
+                               win2k_variant=False):
+        if win2k_variant:
+            self.assertIsNone(pa_checksum)
+            self.assertIsNone(freshness_token)
+            self.assertIsNotNone(kdc_name)
+            self.assertIsNotNone(kdc_realm)
+        else:
+            self.assertIsNone(kdc_name)
+            self.assertIsNone(kdc_realm)
+
         pk_authenticator_obj = {
             'cusec': cusec,
             'ctime': ctime,
@@ -1852,6 +1864,10 @@ class RawKerberosTest(TestCase):
             pk_authenticator_obj['paChecksum'] = pa_checksum
         if freshness_token is not None:
             pk_authenticator_obj['freshnessToken'] = freshness_token
+        if kdc_name is not None:
+            pk_authenticator_obj['kdcName'] = kdc_name
+        if kdc_realm is not None:
+            pk_authenticator_obj['kdcRealm'] = kdc_realm
 
         return pk_authenticator_obj
 
@@ -2143,7 +2159,12 @@ class RawKerberosTest(TestCase):
                         *,
                         client_public_value=None,
                         supported_cms_types=None,
-                        client_dh_nonce=None):
+                        client_dh_nonce=None,
+                        win2k_variant=False):
+        if win2k_variant:
+            self.assertIsNone(supported_cms_types)
+            self.assertIsNone(client_dh_nonce)
+
         auth_pack_obj = {
             'pkAuthenticator': pk_authenticator,
         }
@@ -2161,7 +2182,18 @@ class RawKerberosTest(TestCase):
                          signed_auth_pack,
                          *,
                          trusted_certifiers=None,
-                         kdc_pk_id=None):
+                         kdc_pk_id=None,
+                         kdc_cert=None,
+                         encryption_cert=None,
+                         win2k_variant=False):
+        if win2k_variant:
+            self.assertIsNone(kdc_pk_id)
+            asn1_spec = krb5_asn1.PA_PK_AS_REQ_Win2k
+        else:
+            self.assertIsNone(kdc_cert)
+            self.assertIsNone(encryption_cert)
+            asn1_spec = krb5_asn1.PA_PK_AS_REQ
+
         content_info_obj = self.ContentInfo_create(
             krb5_asn1.id_signedData, signed_auth_pack)
         content_info = self.der_encode(content_info_obj,
@@ -2175,9 +2207,12 @@ class RawKerberosTest(TestCase):
             pk_as_req_obj['trustedCertifiers'] = trusted_certifiers
         if kdc_pk_id is not None:
             pk_as_req_obj['kdcPkId'] = kdc_pk_id
+        if kdc_cert is not None:
+            pk_as_req_obj['kdcCert'] = kdc_cert
+        if encryption_cert is not None:
+            pk_as_req_obj['encryptionCert'] = encryption_cert
 
-        return self.der_encode(pk_as_req_obj,
-                               asn1Spec=krb5_asn1.PA_PK_AS_REQ())
+        return self.der_encode(pk_as_req_obj, asn1Spec=asn1_spec())
 
     def SignerInfo_create(self,
                           signer_id,
@@ -3326,10 +3361,19 @@ class RawKerberosTest(TestCase):
 
         pa_dict = self.get_pa_dict(padata)
 
-        if PADATA_PK_AS_REP in pa_dict:
-            pk_as_rep = pa_dict[PADATA_PK_AS_REP]
+        pk_as_rep = pa_dict.get(PADATA_PK_AS_REP)
+        if pk_as_rep is not None:
+            pk_as_rep_asn1_spec = krb5_asn1.PA_PK_AS_REP
+            reply_key_pack_asn1_spec = krb5_asn1.ReplyKeyPack
+            pk_win2k = False
+        else:
+            pk_as_rep = pa_dict.get(PADATA_PK_AS_REP_19)
+            pk_as_rep_asn1_spec = krb5_asn1.PA_PK_AS_REP_Win2k
+            reply_key_pack_asn1_spec = krb5_asn1.ReplyKeyPack_Win2k
+            pk_win2k = True
+        if pk_as_rep is not None:
             pk_as_rep = self.der_decode(pk_as_rep,
-                                        asn1Spec=krb5_asn1.PA_PK_AS_REP())
+                                        asn1Spec=pk_as_rep_asn1_spec())
 
             using_pkinit = kdc_exchange_dict['using_pkinit']
             if using_pkinit is PkInit.PUBLIC_KEY:
@@ -3490,9 +3534,7 @@ class RawKerberosTest(TestCase):
                 self.assertEqual(str(krb5_asn1.id_pkinit_rkeyData),
                                  content_type)
                 reply_key_pack = self.der_decode(
-                    content, asn1Spec=krb5_asn1.ReplyKeyPack())
-
-                as_checksum = reply_key_pack['asChecksum']
+                    content, asn1Spec=reply_key_pack_asn1_spec())
 
                 req_obj = kdc_exchange_dict['req_obj']
                 req_asn1Spec = kdc_exchange_dict['req_asn1Spec']
@@ -3508,12 +3550,15 @@ class RawKerberosTest(TestCase):
                     contents=reply_key['keyvalue'],
                     kvno=None)
 
-                # Verify the checksum over the AS request body.
-                kcrypto.verify_checksum(as_checksum['cksumtype'],
-                                        encpart_decryption_key.key,
-                                        KU_PKINIT_AS_REQ,
-                                        req_obj,
-                                        as_checksum['checksum'])
+                if not pk_win2k:
+                    as_checksum = reply_key_pack['asChecksum']
+
+                    # Verify the checksum over the AS request body.
+                    kcrypto.verify_checksum(as_checksum['cksumtype'],
+                                            encpart_decryption_key.key,
+                                            KU_PKINIT_AS_REQ,
+                                            req_obj,
+                                            as_checksum['checksum'])
             elif using_pkinit is PkInit.DIFFIE_HELLMAN:
                 content_info = self.der_decode(
                     pk_as_rep['dhInfo']['dhSignedData'],
@@ -4387,7 +4432,8 @@ class RawKerberosTest(TestCase):
         if expect_requester_sid:
             expected_types.append(krb5pac.PAC_TYPE_REQUESTER_SID)
 
-        sent_pk_as_req = self.sent_pk_as_req(kdc_exchange_dict)
+        sent_pk_as_req = self.sent_pk_as_req(kdc_exchange_dict) or (
+            self.sent_pk_as_req_win2k(kdc_exchange_dict))
         if sent_pk_as_req:
             expected_types.append(krb5pac.PAC_TYPE_CREDENTIAL_INFO)
 
@@ -4797,13 +4843,16 @@ class RawKerberosTest(TestCase):
         expected_patypes = ()
 
         sent_fast = self.sent_fast(kdc_exchange_dict)
-        using_pkinit = kdc_exchange_dict.get('using_pkinit', PkInit.NOT_USED)
         rep_msg_type = kdc_exchange_dict['rep_msg_type']
 
         if sent_fast:
             expected_patypes += (PADATA_FX_FAST,)
         elif rep_msg_type == KRB_AS_REP:
-            if using_pkinit is PkInit.NOT_USED:
+            if self.sent_pk_as_req(kdc_exchange_dict):
+                expected_patypes += PADATA_PK_AS_REP,
+            elif self.sent_pk_as_req_win2k(kdc_exchange_dict):
+                expected_patypes += PADATA_PK_AS_REP_19,
+            else:
                 chosen_etype = self.getElementValue(encpart, 'etype')
                 self.assertIsNotNone(chosen_etype)
 
@@ -4815,8 +4864,6 @@ class RawKerberosTest(TestCase):
                 self.assertIsInstance(preauth_key, Krb5EncryptionKey)
                 if preauth_key.etype == kcrypto.Enctype.RC4 and rep_padata is None:
                     rep_padata = ()
-            else:
-                expected_patypes += PADATA_PK_AS_REP,
         elif rep_msg_type == KRB_TGS_REP:
             if expected_patypes == () and rep_padata is None:
                 rep_padata = ()
@@ -5867,6 +5914,11 @@ class RawKerberosTest(TestCase):
 
         return PADATA_PK_AS_REQ in fast_pa_dict
 
+    def sent_pk_as_req_win2k(self, kdc_exchange_dict):
+        fast_pa_dict = self.get_fast_pa_dict(kdc_exchange_dict)
+
+        return PADATA_PK_AS_REP_19 in fast_pa_dict
+
     def sent_freshness(self, kdc_exchange_dict):
         fast_pa_dict = self.get_fast_pa_dict(kdc_exchange_dict)
 
index 2a8fa66e03729f85dba7a26f1efdb992feb87033..d2c2619f1439d5d4eb6e963a19bed6f40e0dd050 100644 (file)
 ^samba.tests.krb5.pkinit_tests.samba.tests.krb5.pkinit_tests.PkInitTests.test_pkinit_freshness_rodc_ts.ad_dc
 ^samba.tests.krb5.pkinit_tests.samba.tests.krb5.pkinit_tests.PkInitTests.test_pkinit_freshness_wrong_header.ad_dc
 ^samba.tests.krb5.pkinit_tests.samba.tests.krb5.pkinit_tests.PkInitTests.test_pkinit_freshness_wrong_header_dh.ad_dc
+#
+# Windows 2000 PK-INIT tests
+#
+^samba.tests.krb5.pkinit_tests.samba.tests.krb5.pkinit_tests.PkInitTests.test_pkinit_computer_win2k.ad_dc
+^samba.tests.krb5.pkinit_tests.samba.tests.krb5.pkinit_tests.PkInitTests.test_pkinit_service_win2k.ad_dc
+^samba.tests.krb5.pkinit_tests.samba.tests.krb5.pkinit_tests.PkInitTests.test_pkinit_sha256_certificate_signature_win2k.ad_dc
+^samba.tests.krb5.pkinit_tests.samba.tests.krb5.pkinit_tests.PkInitTests.test_pkinit_sha256_signature_win2k.ad_dc
+^samba.tests.krb5.pkinit_tests.samba.tests.krb5.pkinit_tests.PkInitTests.test_pkinit_win2k.ad_dc
index 5c24275f82a13369c034ed49ec8965549f420507..14d522416d033abbc4ee4e9dfed9241e157e0ab2 100644 (file)
 ^samba.tests.krb5.pkinit_tests.samba.tests.krb5.pkinit_tests.PkInitTests.test_pkinit_freshness_with_enc_ts.ad_dc
 ^samba.tests.krb5.pkinit_tests.samba.tests.krb5.pkinit_tests.PkInitTests.test_pkinit_freshness_wrong_header.ad_dc
 ^samba.tests.krb5.pkinit_tests.samba.tests.krb5.pkinit_tests.PkInitTests.test_pkinit_freshness_wrong_header_dh.ad_dc
+#
+# Windows 2000 PK-INIT tests
+#
+^samba.tests.krb5.pkinit_tests.samba.tests.krb5.pkinit_tests.PkInitTests.test_pkinit_computer_win2k.ad_dc
+^samba.tests.krb5.pkinit_tests.samba.tests.krb5.pkinit_tests.PkInitTests.test_pkinit_service_win2k.ad_dc
+^samba.tests.krb5.pkinit_tests.samba.tests.krb5.pkinit_tests.PkInitTests.test_pkinit_sha256_certificate_signature_win2k.ad_dc
+^samba.tests.krb5.pkinit_tests.samba.tests.krb5.pkinit_tests.PkInitTests.test_pkinit_sha256_signature_win2k.ad_dc
+^samba.tests.krb5.pkinit_tests.samba.tests.krb5.pkinit_tests.PkInitTests.test_pkinit_win2k.ad_dc