]> git.ipfire.org Git - thirdparty/samba.git/commitdiff
librpc:bcrypt_rsakey_blob: exponent and modulus lengths can't be zero
authorDouglas Bagnall <douglas.bagnall@catalyst.net.nz>
Wed, 30 Jul 2025 09:18:09 +0000 (21:18 +1200)
committerDouglas Bagnall <dbagnall@samba.org>
Thu, 31 Jul 2025 05:45:07 +0000 (05:45 +0000)
Apart from it making no sense, without these ranges we end up
allocating a NULL buffer and aborting.

We also put a maximum size on the RSA key, in case we could get
tricked into a DoS by pulling a large buffer and trying crypto maths
on it.

 6 0x572ebce2749a in talloc_abort samba/lib/talloc/talloc.c:506:3
 7 0x572ebce271d4 in talloc_chunk_from_ptr samba/lib/talloc/talloc.c:0
 8 0x572ebce271d4 in __talloc_with_prefix samba/lib/talloc/talloc.c:762:12
 9 0x572ebce235f9 in __talloc samba/lib/talloc/talloc.c:825:9
10 0x572ebce235f9 in _talloc_named_const samba/lib/talloc/talloc.c:982:8
11 0x572ebce235f9 in _talloc_memdup samba/lib/talloc/talloc.c:2441:9
12 0x572ebc8f6a4f in data_blob_talloc_named samba/lib/util/data_blob.c:56:25
13 0x572ebc7d23bd in pull_BCRYPT_RSAPUBLIC_BLOB samba/librpc/ndr/ndr_keycredlink.c:878:17
14 0x572ebc7d23bd in ndr_pull_KeyMaterialInternal samba/librpc/ndr/ndr_keycredlink.c:959:10
15 0x572ebc788e90 in LLVMFuzzerTestOneInput samba/bin/default/lib/fuzzing/fuzz_ndr_keycredlink_TYPE_STRUCT.c:282:13

REF: https://issues.oss-fuzz.com/issues/435039896

Signed-off-by: Douglas Bagnall <douglas.bagnall@catalyst.net.nz>
Reviewed-by: Gary Lockyer <gary@catalyst.net.nz>
Autobuild-User(master): Douglas Bagnall <dbagnall@samba.org>
Autobuild-Date(master): Thu Jul 31 05:45:07 UTC 2025 on atb-devel-224

librpc/idl/bcrypt_rsakey_blob.idl
python/samba/tests/bcrypt_rsakey_blob.py

index fd5f6134e3434096d4c73605d5722092ca25a1d4..6bd071cf4d4c008537278da7150f0b4031e7bda3 100644 (file)
@@ -21,13 +21,22 @@ interface bcrypt_rsakey_blob
                /* Currently only handle RSA Public Key blobs */
                [value(0x31415352), range(0x31415352, 0x31415352)]
                        uint32 magic; /* RSA1 */
-               uint32 bit_length;
+               /*
+                * In key_credential_links we expect bit_length to be
+                * 2048, but we accept a wider range in part because
+                * testing is much easier with small numbers.
+                */
+               [range(1,65536)]uint32 bit_length;
                /*
                 * As of Windows 10 version 1903, public exponents larger
                 * than (2^64 - 1) are no longer supported.
                 */
-               [range(0x0,0x8)] uint32 public_exponent_len;
-               uint32 modulus_len;
+               [range(0x1,0x8)] uint32 public_exponent_len;
+               /*
+                * modulus_len is the key size in bytes, more or less
+                * bit_length / 8.
+                */
+               [range(0x1, 0x2001)] uint32 modulus_len;
                /*
                 * We're only supporting public keys, so the private
                 * key prime lengths should be zero
index ea984445d24a5b6e2e16c917a3f2d325c028db70..59e446deec2f4d91ce05b7fa5a740232346e6d35 100755 (executable)
@@ -35,7 +35,8 @@ class BcryptRsaKeyBlobTests(TestCase):
     def test_unpack_empty_key_blob(self):
         """
         Ensure that a minimal header only BCRYPT_RSAPUBLIC_BLOB
-        can be unpacked, then packed into identical bytes
+        can't be unpacked, because it would imply zero length modulus
+        and exponent numbers, which is meaningless.
         """
         empty_key_blob = bytes.fromhex(
             "52 53 41 31"  # Magic value RSA1
@@ -45,20 +46,11 @@ class BcryptRsaKeyBlobTests(TestCase):
             "00 00 00 00"  # prime one length"
             "00 00 00 00"  # prime two length"
         )
-        blob = ndr_unpack(
-            bcrypt_rsakey_blob.BCRYPT_RSAPUBLIC_BLOB, empty_key_blob)
-
-        self.assertEqual(blob.magic, 0x31415352)
-        self.assertEqual(blob.bit_length, 0)
-        self.assertEqual(blob.public_exponent_len, 0)
-        self.assertEqual(blob.modulus_len, 0)
-        self.assertEqual(blob.prime1_len_unused, 0)
-        self.assertEqual(blob.prime2_len_unused, 0)
-        self.assertEqual(len(blob.public_exponent), 0)
-        self.assertEqual(len(blob.modulus), 0)
-
-        packed = ndr_pack(blob)
-        self.assertEqual(empty_key_blob, packed)
+        with self.assertRaises(RuntimeError) as e:
+            ndr_unpack(bcrypt_rsakey_blob.BCRYPT_RSAPUBLIC_BLOB,
+                       empty_key_blob)
+        self.assertEqual(e.exception.args[0], 13)
+        self.assertEqual(e.exception.args[1], "Range Error")
 
     def test_unpack_invalid_magic(self):
         """
@@ -67,11 +59,12 @@ class BcryptRsaKeyBlobTests(TestCase):
         """
         invalid_magic_key_blob = bytes.fromhex(
             "52 53 41 30"  # Magic value RSA0
-            "00 00 00 00"  # bit length
-            "00 00 00 00"  # public exponent length
-            "00 00 00 00"  # modulus length
+            "04 00 00 00"  # bit length
+            "01 00 00 00"  # public exponent length
+            "01 00 00 00"  # modulus length
             "00 00 00 00"  # prime one length
             "00 00 00 00"  # prime two length"
+            "01 02"        # exponent and modulus, one byte each
         )
         with self.assertRaises(RuntimeError) as e:
             ndr_unpack(bcrypt_rsakey_blob.BCRYPT_RSAPUBLIC_BLOB,
@@ -87,11 +80,12 @@ class BcryptRsaKeyBlobTests(TestCase):
         """
         extra_data_key_blob = bytes.fromhex(
             "52 53 41 31"  # Magic value RSA1
-            "00 00 00 00"  # bit length
-            "00 00 00 00"  # public exponent length
-            "00 00 00 00"  # modulus length
+            "04 00 00 00"  # bit length
+            "01 00 00 00"  # public exponent length
+            "01 00 00 00"  # modulus length
             "00 00 00 00"  # prime one length
             "00 00 00 00"  # prime two length
+            "01 02"        # exponent and modulus, one byte each
             "01"           # a trailing byte of data
         )
         with self.assertRaises(RuntimeError) as e:
@@ -127,9 +121,9 @@ class BcryptRsaKeyBlobTests(TestCase):
         """
         invalid_magic_key_blob = bytes.fromhex(
             "52 53 41 31"  # Magic value RSA1
-            "00 00 00 00"  # bit length
+            "08 00 00 00"  # bit length
             "09 00 00 00"  # public exponent length, 9 bytes
-            "00 00 00 00"  # modulus length
+            "01 00 00 00"  # modulus length
             "00 00 00 00"  # prime one length
             "00 00 00 00"  # prime two length"
         )
@@ -147,11 +141,12 @@ class BcryptRsaKeyBlobTests(TestCase):
         """
         invalid_prime1_key_blob = bytes.fromhex(
             "52 53 41 31"  # Magic value RSA1
-            "00 00 00 00"  # bit length
-            "00 00 00 00"  # public exponent length, 9 bytes
-            "00 00 00 00"  # modulus length
+            "04 00 00 00"  # bit length
+            "01 00 00 00"  # public exponent length
+            "01 00 00 00"  # modulus length
             "01 00 00 00"  # prime one length
             "00 00 00 00"  # prime two length"
+            "01 02"        # exponent and modulus, one byte each
         )
         with self.assertRaises(RuntimeError) as e:
             ndr_unpack(bcrypt_rsakey_blob.BCRYPT_RSAPUBLIC_BLOB,
@@ -168,8 +163,8 @@ class BcryptRsaKeyBlobTests(TestCase):
         invalid_prime2_key_blob = bytes.fromhex(
             "52 53 41 31"  # Magic value RSA1
             "00 00 00 00"  # bit length
-            "00 00 00 00"  # public exponent length, 9 bytes
-            "00 00 00 00"  # modulus length
+            "01 00 00 00"  # public exponent length
+            "01 00 00 00"  # modulus length
             "00 00 00 00"  # prime one length
             "01 00 00 00"  # prime two length"
         )