From 7368e0051a320fce48c1f303914b62985a40beb0 Mon Sep 17 00:00:00 2001
From: Joseph Sutton <josephsutton@catalyst.net.nz>
Date: Tue, 18 Jan 2022 11:56:38 +1300
Subject: [PATCH 1/7] CVE-2022-0336: pytest: Add a test for an SPN conflict
 with a re-added SPN

This test currently fails, as re-adding an SPN means that later checks
do not run.

BUG: https://bugzilla.samba.org/show_bug.cgi?id=14950

Signed-off-by: Joseph Sutton <josephsutton@catalyst.net.nz>
Reviewed-by: Douglas Bagnall <douglas.bagnall@catalyst.net.nz>
---
 python/samba/tests/ldap_spn.py | 7 +++++++
 selftest/knownfail.d/ldap_spn  | 1 +
 2 files changed, 8 insertions(+)

diff --git a/python/samba/tests/ldap_spn.py b/python/samba/tests/ldap_spn.py
index 8a398ffaa49..6ebdf8f9a32 100644
--- a/python/samba/tests/ldap_spn.py
+++ b/python/samba/tests/ldap_spn.py
@@ -268,6 +268,8 @@ class LdapSpnTestBase(TestCase):
             for k in ('dNSHostName', 'servicePrincipalName'):
                 if isinstance(m.get(k), str):
                     m[k] = m[k].format(dnsname=f"x.{REALM}")
+                elif isinstance(m.get(k), list):
+                    m[k] = [x.format(dnsname=f"x.{REALM}") for x in m[k]]
 
             msg = ldb.Message.from_dict(samdb, m, op)
 
@@ -727,6 +729,11 @@ class LdapSpnSambaOnlyTest(LdapSpnTestBase):
          ('user:C', 'host/{dnsname}', '*', ok),
          ('user:D', 'www/{dnsname}', 'D', denied),
         ),
+        ("add a conflict, along with a re-added SPN",
+         ('A', 'cifs/{dnsname}', '*', ok),
+         ('B', 'cifs/heeble.example.net', 'B', ok),
+         ('B', ['cifs/heeble.example.net', 'host/{dnsname}'], 'B', constraint),
+        ),
 
         ("changing dNSHostName after host",
          ('A', {'dNSHostName': '{dnsname}'}, '*', ok),
diff --git a/selftest/knownfail.d/ldap_spn b/selftest/knownfail.d/ldap_spn
index 63f9fe02ef7..16dafa91b66 100644
--- a/selftest/knownfail.d/ldap_spn
+++ b/selftest/knownfail.d/ldap_spn
@@ -1 +1,2 @@
 samba.tests.ldap_spn.+LdapSpnTest.test_spn_dodgy_spns
+samba.tests.ldap_spn.+LdapSpnSambaOnlyTest.test_spn_add_a_conflict_along_with_a_re_added_SPN
-- 
2.25.1


From 2802b7d8f3f77a639d0d69bced528f328655750b Mon Sep 17 00:00:00 2001
From: Joseph Sutton <josephsutton@catalyst.net.nz>
Date: Tue, 18 Jan 2022 12:02:45 +1300
Subject: [PATCH 2/7] CVE-2022-0336: s4/dsdb/samldb: Don't return early when an
 SPN is re-added to an object

If an added SPN already exists on an object, we still want to check the
rest of the element values for conflicts.

BUG: https://bugzilla.samba.org/show_bug.cgi?id=14950

Signed-off-by: Joseph Sutton <josephsutton@catalyst.net.nz>
Reviewed-by: Douglas Bagnall <douglas.bagnall@catalyst.net.nz>
---
 selftest/knownfail.d/ldap_spn           | 1 -
 source4/dsdb/samdb/ldb_modules/samldb.c | 3 +--
 2 files changed, 1 insertion(+), 3 deletions(-)

diff --git a/selftest/knownfail.d/ldap_spn b/selftest/knownfail.d/ldap_spn
index 16dafa91b66..63f9fe02ef7 100644
--- a/selftest/knownfail.d/ldap_spn
+++ b/selftest/knownfail.d/ldap_spn
@@ -1,2 +1 @@
 samba.tests.ldap_spn.+LdapSpnTest.test_spn_dodgy_spns
-samba.tests.ldap_spn.+LdapSpnSambaOnlyTest.test_spn_add_a_conflict_along_with_a_re_added_SPN
diff --git a/source4/dsdb/samdb/ldb_modules/samldb.c b/source4/dsdb/samdb/ldb_modules/samldb.c
index f0227411ccd..a219446bba7 100644
--- a/source4/dsdb/samdb/ldb_modules/samldb.c
+++ b/source4/dsdb/samdb/ldb_modules/samldb.c
@@ -4001,8 +4001,7 @@ static int samldb_spn_uniqueness_check(struct samldb_ctx *ac,
 						 ac->msg->dn);
 		if (ret == LDB_ERR_COMPARE_TRUE) {
 			DBG_INFO("SPN %s re-added to the same object\n", spn);
-			talloc_free(tmp_ctx);
-			return LDB_SUCCESS;
+			continue;
 		}
 		if (ret != LDB_SUCCESS) {
 			DBG_ERR("SPN %s failed direct uniqueness check\n", spn);
-- 
2.25.1


From 57d9afe5d71da5360501f43f1c69208bca3b0769 Mon Sep 17 00:00:00 2001
From: Ralph Boehme <slow@samba.org>
Date: Thu, 13 Jan 2022 16:48:01 +0100
Subject: [PATCH 3/7] CVE-2021-44142: libadouble: add defines for icon lengths

From https://www.ietf.org/rfc/rfc1740.txt

BUG: https://bugzilla.samba.org/show_bug.cgi?id=14914

Signed-off-by: Ralph Boehme <slow@samba.org>
Reviewed-by: Jeremy Allison <jra@samba.org>
---
 source3/lib/adouble.h | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/source3/lib/adouble.h b/source3/lib/adouble.h
index 90a825c502e..e3b9263a1f9 100644
--- a/source3/lib/adouble.h
+++ b/source3/lib/adouble.h
@@ -101,6 +101,8 @@ typedef enum {ADOUBLE_META, ADOUBLE_RSRC} adouble_type_t;
 #define ADEDLEN_MACFILEI        4
 #define ADEDLEN_PRODOSFILEI     8
 #define ADEDLEN_MSDOSFILEI      2
+#define ADEDLEN_ICONBW          128
+#define ADEDLEN_ICONCOL         1024
 #define ADEDLEN_DID             4
 #define ADEDLEN_PRIVDEV         8
 #define ADEDLEN_PRIVINO         8
-- 
2.25.1


From 9eba87ce4b47488f1422248b8d4ad9b37ae26899 Mon Sep 17 00:00:00 2001
From: Ralph Boehme <slow@samba.org>
Date: Sat, 20 Nov 2021 16:36:42 +0100
Subject: [PATCH 4/7] CVE-2021-44142: smbd: add Netatalk xattr used by
 vfs_fruit to the list of private Samba xattrs

This is an internal xattr that should not be user visible.

BUG: https://bugzilla.samba.org/show_bug.cgi?id=14914

Signed-off-by: Ralph Boehme <slow@samba.org>
Reviewed-by: Jeremy Allison <jra@samba.org>
[slow@samba.org: conflict due to changed includes in source3/smbd/trans2.c]
---
 source3/smbd/trans2.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/source3/smbd/trans2.c b/source3/smbd/trans2.c
index 7acde285a90..673d7165c04 100644
--- a/source3/smbd/trans2.c
+++ b/source3/smbd/trans2.c
@@ -44,6 +44,7 @@
 #include "messages.h"
 #include "smb1_utils.h"
 #include "libcli/smb/smb2_posix.h"
+#include "source3/lib/adouble.h"
 
 #define DIR_ENTRY_SAFETY_MARGIN 4096
 
@@ -240,6 +241,7 @@ bool samba_private_attr_name(const char *unix_ea_name)
 		SAMBA_XATTR_DOS_ATTRIB,
 		SAMBA_XATTR_MARKER,
 		XATTR_NTACL_NAME,
+		AFPINFO_EA_NETATALK,
 		NULL
 	};
 
-- 
2.25.1


From b3b76222cfba5d162843b14b55570a02573c8ce8 Mon Sep 17 00:00:00 2001
From: Ralph Boehme <slow@samba.org>
Date: Fri, 26 Nov 2021 07:19:32 +0100
Subject: [PATCH 5/7] CVE-2021-44142: libadouble: harden ad_unpack_xattrs()

This ensures ad_unpack_xattrs() is only called for an ad_type of ADOUBLE_RSRC,
which is used for parsing ._ AppleDouble sidecar files, and the buffer
ad->ad_data is AD_XATTR_MAX_HDR_SIZE bytes large which is a prerequisite for all
buffer out-of-bounds access checks in ad_unpack_xattrs().

BUG: https://bugzilla.samba.org/show_bug.cgi?id=14914

Signed-off-by: Ralph Boehme <slow@samba.org>
Reviewed-by: Jeremy Allison <jra@samba.org>
---
 source3/lib/adouble.c | 21 ++++++++++++++++++---
 1 file changed, 18 insertions(+), 3 deletions(-)

diff --git a/source3/lib/adouble.c b/source3/lib/adouble.c
index 42b2e808d66..48621feb530 100644
--- a/source3/lib/adouble.c
+++ b/source3/lib/adouble.c
@@ -707,14 +707,27 @@ static bool ad_pack(struct vfs_handle_struct *handle,
 static bool ad_unpack_xattrs(struct adouble *ad)
 {
 	struct ad_xattr_header *h = &ad->adx_header;
+	size_t bufsize = talloc_get_size(ad->ad_data);
 	const char *p = ad->ad_data;
 	uint32_t hoff;
 	uint32_t i;
 
+	if (ad->ad_type != ADOUBLE_RSRC) {
+		return false;
+	}
+
 	if (ad_getentrylen(ad, ADEID_FINDERI) <= ADEDLEN_FINDERI) {
 		return true;
 	}
 
+	/*
+	 * Ensure the buffer ad->ad_data was allocated by ad_alloc() for an
+	 * ADOUBLE_RSRC type (._ AppleDouble file on-disk).
+	 */
+	if (bufsize != AD_XATTR_MAX_HDR_SIZE) {
+		return false;
+	}
+
 	/* 2 bytes padding */
 	hoff = ad_getentryoff(ad, ADEID_FINDERI) + ADEDLEN_FINDERI + 2;
 
@@ -964,9 +977,11 @@ static bool ad_unpack(struct adouble *ad, const size_t nentries,
 		ad->ad_eid[eid].ade_len = len;
 	}
 
-	ok = ad_unpack_xattrs(ad);
-	if (!ok) {
-		return false;
+	if (ad->ad_type == ADOUBLE_RSRC) {
+		ok = ad_unpack_xattrs(ad);
+		if (!ok) {
+			return false;
+		}
 	}
 
 	return true;
-- 
2.25.1


From ffa40d4acb5f2d57443670be31dca05caacb1cfd Mon Sep 17 00:00:00 2001
From: Ralph Boehme <slow@samba.org>
Date: Thu, 25 Nov 2021 15:04:03 +0100
Subject: [PATCH 6/7] CVE-2021-44142: libadouble: add basic cmocka tests

BUG: https://bugzilla.samba.org/show_bug.cgi?id=14914

Signed-off-by: Ralph Boehme <slow@samba.org>
Reviewed-by: Jeremy Allison <jra@samba.org>
[slow@samba.org: conflict due to missing test in selftest/tests.py]
---
 selftest/knownfail.d/samba.unittests.adouble |   3 +
 selftest/tests.py                            |   2 +
 source3/lib/test_adouble.c                   | 389 +++++++++++++++++++
 source3/wscript_build                        |   5 +
 4 files changed, 399 insertions(+)
 create mode 100644 selftest/knownfail.d/samba.unittests.adouble
 create mode 100644 source3/lib/test_adouble.c

diff --git a/selftest/knownfail.d/samba.unittests.adouble b/selftest/knownfail.d/samba.unittests.adouble
new file mode 100644
index 00000000000..8b0314f2fae
--- /dev/null
+++ b/selftest/knownfail.d/samba.unittests.adouble
@@ -0,0 +1,3 @@
+^samba.unittests.adouble.parse_abouble_finderinfo2\(none\)
+^samba.unittests.adouble.parse_abouble_finderinfo3\(none\)
+^samba.unittests.adouble.parse_abouble_date2\(none\)
diff --git a/selftest/tests.py b/selftest/tests.py
index a2b8bf5c4d5..ac5070f7e5b 100644
--- a/selftest/tests.py
+++ b/selftest/tests.py
@@ -417,3 +417,5 @@ plantestsuite("samba.unittests.test_oLschema2ldif", "none",
 if with_elasticsearch_backend:
     plantestsuite("samba.unittests.mdsparser_es", "none",
                   [os.path.join(bindir(), "default/source3/test_mdsparser_es")] + [configuration])
+plantestsuite("samba.unittests.adouble", "none",
+              [os.path.join(bindir(), "test_adouble")])
diff --git a/source3/lib/test_adouble.c b/source3/lib/test_adouble.c
new file mode 100644
index 00000000000..615c22469c9
--- /dev/null
+++ b/source3/lib/test_adouble.c
@@ -0,0 +1,389 @@
+/*
+ * Unix SMB/CIFS implementation.
+ *
+ * Copyright (C) 2021 Ralph Boehme <slow@samba.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "adouble.c"
+#include <cmocka.h>
+
+static int setup_talloc_context(void **state)
+{
+	TALLOC_CTX *frame = talloc_stackframe();
+
+	*state = frame;
+	return 0;
+}
+
+static int teardown_talloc_context(void **state)
+{
+	TALLOC_CTX *frame = *state;
+
+	TALLOC_FREE(frame);
+	return 0;
+}
+
+/*
+ * Basic and sane buffer.
+ */
+static uint8_t ad_basic[] = {
+	0x00, 0x05, 0x16, 0x07, /* Magic */
+	0x00, 0x02, 0x00, 0x00, /* Version */
+	0x00, 0x00, 0x00, 0x00, /* Filler */
+	0x00, 0x00, 0x00, 0x00, /* Filler */
+	0x00, 0x00, 0x00, 0x00, /* Filler */
+	0x00, 0x00, 0x00, 0x00, /* Filler */
+	0x00, 0x02,             /* Count */
+	/* adentry 1: FinderInfo */
+	0x00, 0x00, 0x00, 0x09,	/* eid: FinderInfo */
+	0x00, 0x00, 0x00, 0x32,	/* offset */
+	0x00, 0x00, 0x00, 0x20,	/* length */
+	/* adentry 2: Resourcefork */
+	0x00, 0x00, 0x00, 0x02,	/* eid: Resourcefork */
+	0x00, 0x00, 0x00, 0x52,	/* offset */
+	0xff, 0xff, 0xff, 0x00,	/* length */
+	/* FinderInfo data: 32 bytes */
+	0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00,
+};
+
+/*
+ * An empty FinderInfo entry.
+ */
+static uint8_t ad_finderinfo1[] = {
+	0x00, 0x05, 0x16, 0x07, /* Magic */
+	0x00, 0x02, 0x00, 0x00, /* Version */
+	0x00, 0x00, 0x00, 0x00, /* Filler */
+	0x00, 0x00, 0x00, 0x00, /* Filler */
+	0x00, 0x00, 0x00, 0x00, /* Filler */
+	0x00, 0x00, 0x00, 0x00, /* Filler */
+	0x00, 0x02,             /* Count */
+	/* adentry 1: FinderInfo */
+	0x00, 0x00, 0x00, 0x09,	/* eid: FinderInfo */
+	0x00, 0x00, 0x00, 0x52,	/* off: points at end of buffer */
+	0x00, 0x00, 0x00, 0x00,	/* len: 0, so off+len don't exceed bufferlen */
+	/* adentry 2: Resourcefork */
+	0x00, 0x00, 0x00, 0x02,	/* eid: Resourcefork */
+	0x00, 0x00, 0x00, 0x52,	/* offset */
+	0xff, 0xff, 0xff, 0x00,	/* length */
+	/* FinderInfo data: 32 bytes */
+	0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00,
+};
+
+/*
+ * A dangerous FinderInfo with correct length exceeding buffer by one byte.
+ */
+static uint8_t ad_finderinfo2[] = {
+	0x00, 0x05, 0x16, 0x07, /* Magic */
+	0x00, 0x02, 0x00, 0x00, /* Version */
+	0x00, 0x00, 0x00, 0x00, /* Filler */
+	0x00, 0x00, 0x00, 0x00, /* Filler */
+	0x00, 0x00, 0x00, 0x00, /* Filler */
+	0x00, 0x00, 0x00, 0x00, /* Filler */
+	0x00, 0x02,             /* Count */
+	/* adentry 1: FinderInfo */
+	0x00, 0x00, 0x00, 0x09,	/* eid: FinderInfo */
+	0x00, 0x00, 0x00, 0x33,	/* off: points at beginng of data + 1 */
+	0x00, 0x00, 0x00, 0x20,	/* len: 32, so off+len exceeds bufferlen by 1 */
+	/* adentry 2: Resourcefork */
+	0x00, 0x00, 0x00, 0x02,	/* eid: Resourcefork */
+	0x00, 0x00, 0x00, 0x52,	/* offset */
+	0xff, 0xff, 0xff, 0x00,	/* length */
+	/* FinderInfo data: 32 bytes */
+	0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00,
+};
+
+static uint8_t ad_finderinfo3[] = {
+	0x00, 0x05, 0x16, 0x07, /* Magic */
+	0x00, 0x02, 0x00, 0x00, /* Version */
+	0x00, 0x00, 0x00, 0x00, /* Filler */
+	0x00, 0x00, 0x00, 0x00, /* Filler */
+	0x00, 0x00, 0x00, 0x00, /* Filler */
+	0x00, 0x00, 0x00, 0x00, /* Filler */
+	0x00, 0x02,             /* Count */
+	/* adentry 1: FinderInfo */
+	0x00, 0x00, 0x00, 0x09,	/* eid: FinderInfo */
+	0x00, 0x00, 0x00, 0x33,	/* off: points at beginng of data + 1 */
+	0x00, 0x00, 0x00, 0x1f,	/* len: 31, so off+len don't exceed buf */
+	/* adentry 2: Resourcefork */
+	0x00, 0x00, 0x00, 0x02,	/* eid: Resourcefork */
+	0x00, 0x00, 0x00, 0x52,	/* offset */
+	0xff, 0xff, 0xff, 0x00,	/* length */
+	/* FinderInfo data: 32 bytes */
+	0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00,
+};
+
+/*
+ * A dangerous name entry.
+ */
+static uint8_t ad_name[] = {
+	0x00, 0x05, 0x16, 0x07, /* Magic */
+	0x00, 0x02, 0x00, 0x00, /* Version */
+	0x00, 0x00, 0x00, 0x00, /* Filler */
+	0x00, 0x00, 0x00, 0x00, /* Filler */
+	0x00, 0x00, 0x00, 0x00, /* Filler */
+	0x00, 0x00, 0x00, 0x00, /* Filler */
+	0x00, 0x02,             /* Count */
+	/* adentry 1: FinderInfo */
+	0x00, 0x00, 0x00, 0x09,	/* eid: FinderInfo */
+	0x00, 0x00, 0x00, 0x32,	/* offset */
+	0x00, 0x00, 0x00, 0x20,	/* length */
+	/* adentry 2: Name */
+	0x00, 0x00, 0x00, 0x03,	/* eid: Name */
+	0x00, 0x00, 0x00, 0x52,	/* off: points at end of buffer */
+	0x00, 0x00, 0x00, 0x01,	/* len: 1, so off+len exceeds bufferlen */
+	/* FinderInfo data: 32 bytes */
+	0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00,
+};
+
+/*
+ * A empty ADEID_FILEDATESI entry.
+ */
+static uint8_t ad_date1[] = {
+	0x00, 0x05, 0x16, 0x07, /* Magic */
+	0x00, 0x02, 0x00, 0x00, /* Version */
+	0x00, 0x00, 0x00, 0x00, /* Filler */
+	0x00, 0x00, 0x00, 0x00, /* Filler */
+	0x00, 0x00, 0x00, 0x00, /* Filler */
+	0x00, 0x00, 0x00, 0x00, /* Filler */
+	0x00, 0x02,             /* Count */
+	/* adentry 1: FinderInfo */
+	0x00, 0x00, 0x00, 0x09,	/* eid: FinderInfo */
+	0x00, 0x00, 0x00, 0x32,	/* offset */
+	0x00, 0x00, 0x00, 0x20,	/* length */
+	/* adentry 2: Dates */
+	0x00, 0x00, 0x00, 0x08,	/* eid: dates */
+	0x00, 0x00, 0x00, 0x52,	/* off: end of buffer */
+	0x00, 0x00, 0x00, 0x00,	/* len: 0, empty entry, valid */
+	/* FinderInfo data: 32 bytes */
+	0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00,
+};
+
+/*
+ * A dangerous ADEID_FILEDATESI entry, invalid length.
+ */
+static uint8_t ad_date2[] = {
+	0x00, 0x05, 0x16, 0x07, /* Magic */
+	0x00, 0x02, 0x00, 0x00, /* Version */
+	0x00, 0x00, 0x00, 0x00, /* Filler */
+	0x00, 0x00, 0x00, 0x00, /* Filler */
+	0x00, 0x00, 0x00, 0x00, /* Filler */
+	0x00, 0x00, 0x00, 0x00, /* Filler */
+	0x00, 0x02,             /* Count */
+	/* adentry 1: FinderInfo */
+	0x00, 0x00, 0x00, 0x09,	/* eid: FinderInfo */
+	0x00, 0x00, 0x00, 0x32,	/* offset */
+	0x00, 0x00, 0x00, 0x20,	/* length */
+	/* adentry 2: Dates */
+	0x00, 0x00, 0x00, 0x08,	/* eid: dates */
+	0x00, 0x00, 0x00, 0x43,	/* off: FinderInfo buf but one byte short */
+	0x00, 0x00, 0x00, 0x0f,	/* len: 15, so off+len don't exceed bufferlen */
+	/* FinderInfo data: 32 bytes */
+	0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00,
+};
+
+static struct adouble *parse_adouble(TALLOC_CTX *mem_ctx,
+				     uint8_t *adbuf,
+				     size_t adsize,
+				     off_t filesize)
+{
+	struct adouble *ad = NULL;
+	bool ok;
+
+	ad = talloc_zero(mem_ctx, struct adouble);
+	ad->ad_data = talloc_zero_size(ad, adsize);
+	assert_non_null(ad);
+
+	memcpy(ad->ad_data, adbuf, adsize);
+
+	ok = ad_unpack(ad, 2, filesize);
+	if (!ok) {
+		return NULL;
+	}
+
+	return ad;
+}
+
+static void parse_abouble_basic(void **state)
+{
+	TALLOC_CTX *frame = *state;
+	struct adouble *ad = NULL;
+	char *p = NULL;
+
+	ad = parse_adouble(frame, ad_basic, sizeof(ad_basic), 0xffffff52);
+	assert_non_null(ad);
+
+	p = ad_get_entry(ad, ADEID_FINDERI);
+	assert_non_null(p);
+
+	return;
+}
+
+static void parse_abouble_finderinfo1(void **state)
+{
+	TALLOC_CTX *frame = *state;
+	struct adouble *ad = NULL;
+	char *p = NULL;
+
+	ad = parse_adouble(frame,
+			   ad_finderinfo1,
+			   sizeof(ad_finderinfo1),
+			   0xffffff52);
+	assert_non_null(ad);
+
+	p = ad_get_entry(ad, ADEID_FINDERI);
+	assert_null(p);
+
+	return;
+}
+
+static void parse_abouble_finderinfo2(void **state)
+{
+	TALLOC_CTX *frame = *state;
+	struct adouble *ad = NULL;
+
+	ad = parse_adouble(frame,
+			   ad_finderinfo2,
+			   sizeof(ad_finderinfo2),
+			   0xffffff52);
+	assert_null(ad);
+
+	return;
+}
+
+static void parse_abouble_finderinfo3(void **state)
+{
+	TALLOC_CTX *frame = *state;
+	struct adouble *ad = NULL;
+
+	ad = parse_adouble(frame,
+			   ad_finderinfo3,
+			   sizeof(ad_finderinfo3),
+			   0xffffff52);
+	assert_null(ad);
+
+	return;
+}
+
+static void parse_abouble_name(void **state)
+{
+	TALLOC_CTX *frame = *state;
+	struct adouble *ad = NULL;
+
+	ad = parse_adouble(frame, ad_name, sizeof(ad_name), 0x52);
+	assert_null(ad);
+
+	return;
+}
+
+static void parse_abouble_date1(void **state)
+{
+	TALLOC_CTX *frame = *state;
+	struct adouble *ad = NULL;
+	char *p = NULL;
+
+	ad = parse_adouble(frame, ad_date1, sizeof(ad_date1), 0x52);
+	assert_non_null(ad);
+
+	p = ad_get_entry(ad, ADEID_FILEDATESI);
+	assert_null(p);
+
+	return;
+}
+
+static void parse_abouble_date2(void **state)
+{
+	TALLOC_CTX *frame = *state;
+	struct adouble *ad = NULL;
+
+	ad = parse_adouble(frame, ad_date2, sizeof(ad_date2), 0x52);
+	assert_null(ad);
+
+	return;
+}
+
+int main(int argc, char *argv[])
+{
+	int rc;
+	const struct CMUnitTest tests[] = {
+		cmocka_unit_test(parse_abouble_basic),
+		cmocka_unit_test(parse_abouble_finderinfo1),
+		cmocka_unit_test(parse_abouble_finderinfo2),
+		cmocka_unit_test(parse_abouble_finderinfo3),
+		cmocka_unit_test(parse_abouble_name),
+		cmocka_unit_test(parse_abouble_date1),
+		cmocka_unit_test(parse_abouble_date2),
+	};
+
+	if (argc == 2) {
+		cmocka_set_test_filter(argv[1]);
+	}
+	cmocka_set_message_output(CM_OUTPUT_SUBUNIT);
+
+	rc = cmocka_run_group_tests(tests,
+				    setup_talloc_context,
+				    teardown_talloc_context);
+
+	return rc;
+}
diff --git a/source3/wscript_build b/source3/wscript_build
index 46c914c7b22..6ac99e81b7f 100644
--- a/source3/wscript_build
+++ b/source3/wscript_build
@@ -1086,6 +1086,11 @@ bld.SAMBA3_SUBSYSTEM('ADOUBLE',
                     source='lib/adouble.c',
                     deps='STRING_REPLACE')
 
+bld.SAMBA3_BINARY('test_adouble',
+                 source='lib/test_adouble.c',
+                 deps='smbd_base STRING_REPLACE cmocka',
+                 for_selftest=True)
+
 bld.SAMBA3_SUBSYSTEM('STRING_REPLACE',
                     source='lib/string_replace.c')
 
-- 
2.25.1


From edf661e40b3c17681563613bf58b2e757a6ebc12 Mon Sep 17 00:00:00 2001
From: Ralph Boehme <slow@samba.org>
Date: Thu, 13 Jan 2022 17:03:02 +0100
Subject: [PATCH 7/7] CVE-2021-44142: libadouble: harden parsing code

BUG: https://bugzilla.samba.org/show_bug.cgi?id=14914

Signed-off-by: Ralph Boehme <slow@samba.org>
Reviewed-by: Jeremy Allison <jra@samba.org>
---
 selftest/knownfail.d/samba.unittests.adouble |   3 -
 source3/lib/adouble.c                        | 115 ++++++++++++++++---
 2 files changed, 101 insertions(+), 17 deletions(-)
 delete mode 100644 selftest/knownfail.d/samba.unittests.adouble

diff --git a/selftest/knownfail.d/samba.unittests.adouble b/selftest/knownfail.d/samba.unittests.adouble
deleted file mode 100644
index 8b0314f2fae..00000000000
--- a/selftest/knownfail.d/samba.unittests.adouble
+++ /dev/null
@@ -1,3 +0,0 @@
-^samba.unittests.adouble.parse_abouble_finderinfo2\(none\)
-^samba.unittests.adouble.parse_abouble_finderinfo3\(none\)
-^samba.unittests.adouble.parse_abouble_date2\(none\)
diff --git a/source3/lib/adouble.c b/source3/lib/adouble.c
index 48621feb530..bb94a45e2de 100644
--- a/source3/lib/adouble.c
+++ b/source3/lib/adouble.c
@@ -269,6 +269,95 @@ size_t ad_setentryoff(struct adouble *ad, int eid, size_t off)
 	return ad->ad_eid[eid].ade_off = off;
 }
 
+/*
+ * All entries besides FinderInfo and resource fork must fit into the
+ * buffer. FinderInfo is special as it may be larger then the default 32 bytes
+ * if it contains marshalled xattrs, which we will fixup that in
+ * ad_convert(). The first 32 bytes however must also be part of the buffer.
+ *
+ * The resource fork is never accessed directly by the ad_data buf.
+ */
+static bool ad_entry_check_size(uint32_t eid,
+				size_t bufsize,
+				uint32_t off,
+				uint32_t got_len)
+{
+	struct {
+		off_t expected_len;
+		bool fixed_size;
+		bool minimum_size;
+	} ad_checks[] = {
+		[ADEID_DFORK] = {-1, false, false}, /* not applicable */
+		[ADEID_RFORK] = {-1, false, false}, /* no limit */
+		[ADEID_NAME] = {ADEDLEN_NAME, false, false},
+		[ADEID_COMMENT] = {ADEDLEN_COMMENT, false, false},
+		[ADEID_ICONBW] = {ADEDLEN_ICONBW, true, false},
+		[ADEID_ICONCOL] = {ADEDLEN_ICONCOL, false, false},
+		[ADEID_FILEI] = {ADEDLEN_FILEI, true, false},
+		[ADEID_FILEDATESI] = {ADEDLEN_FILEDATESI, true, false},
+		[ADEID_FINDERI] = {ADEDLEN_FINDERI, false, true},
+		[ADEID_MACFILEI] = {ADEDLEN_MACFILEI, true, false},
+		[ADEID_PRODOSFILEI] = {ADEDLEN_PRODOSFILEI, true, false},
+		[ADEID_MSDOSFILEI] = {ADEDLEN_MSDOSFILEI, true, false},
+		[ADEID_SHORTNAME] = {ADEDLEN_SHORTNAME, false, false},
+		[ADEID_AFPFILEI] = {ADEDLEN_AFPFILEI, true, false},
+		[ADEID_DID] = {ADEDLEN_DID, true, false},
+		[ADEID_PRIVDEV] = {ADEDLEN_PRIVDEV, true, false},
+		[ADEID_PRIVINO] = {ADEDLEN_PRIVINO, true, false},
+		[ADEID_PRIVSYN] = {ADEDLEN_PRIVSYN, true, false},
+		[ADEID_PRIVID] = {ADEDLEN_PRIVID, true, false},
+	};
+
+	if (eid >= ADEID_MAX) {
+		return false;
+	}
+	if (got_len == 0) {
+		/* Entry present, but empty, allow */
+		return true;
+	}
+	if (ad_checks[eid].expected_len == 0) {
+		/*
+		 * Shouldn't happen: implicitly initialized to zero because
+		 * explicit initializer missing.
+		 */
+		return false;
+	}
+	if (ad_checks[eid].expected_len == -1) {
+		/* Unused or no limit */
+		return true;
+	}
+	if (ad_checks[eid].fixed_size) {
+		if (ad_checks[eid].expected_len != got_len) {
+			/* Wrong size fo fixed size entry. */
+			return false;
+		}
+	} else {
+		if (ad_checks[eid].minimum_size) {
+			if (got_len < ad_checks[eid].expected_len) {
+				/*
+				 * Too small for variable sized entry with
+				 * minimum size.
+				 */
+				return false;
+			}
+		} else {
+			if (got_len > ad_checks[eid].expected_len) {
+				/* Too big for variable sized entry. */
+				return false;
+			}
+		}
+	}
+	if (off + got_len < off) {
+		/* wrap around */
+		return false;
+	}
+	if (off + got_len > bufsize) {
+		/* overflow */
+		return false;
+	}
+	return true;
+}
+
 /**
  * Return a pointer to an AppleDouble entry
  *
@@ -276,8 +365,15 @@ size_t ad_setentryoff(struct adouble *ad, int eid, size_t off)
  **/
 char *ad_get_entry(const struct adouble *ad, int eid)
 {
+	size_t bufsize = talloc_get_size(ad->ad_data);
 	off_t off = ad_getentryoff(ad, eid);
 	size_t len = ad_getentrylen(ad, eid);
+	bool valid;
+
+	valid = ad_entry_check_size(eid, bufsize, off, len);
+	if (!valid) {
+		return NULL;
+	}
 
 	if (off == 0 || len == 0) {
 		return NULL;
@@ -914,20 +1010,11 @@ static bool ad_unpack(struct adouble *ad, const size_t nentries,
 			return false;
 		}
 
-		/*
-		 * All entries besides FinderInfo and resource fork
-		 * must fit into the buffer. FinderInfo is special as
-		 * it may be larger then the default 32 bytes (if it
-		 * contains marshalled xattrs), but we will fixup that
-		 * in ad_convert(). And the resource fork is never
-		 * accessed directly by the ad_data buf (also see
-		 * comment above) anyway.
-		 */
-		if ((eid != ADEID_RFORK) &&
-		    (eid != ADEID_FINDERI) &&
-		    ((off + len) > bufsize)) {
-			DEBUG(1, ("bogus eid %d: off: %" PRIu32 ", len: %" PRIu32 "\n",
-				  eid, off, len));
+		ok = ad_entry_check_size(eid, bufsize, off, len);
+		if (!ok) {
+			DBG_ERR("bogus eid [%"PRIu32"] bufsize [%zu] "
+				"off [%"PRIu32"] len [%"PRIu32"]\n",
+				eid, bufsize, off, len);
 			return false;
 		}
 
-- 
2.25.1