]> git.ipfire.org Git - thirdparty/samba.git/commitdiff
CVE-2022-32743 s4/dsdb/util: Add dsdb_msg_get_single_value()
authorJoseph Sutton <josephsutton@catalyst.net.nz>
Tue, 7 Jun 2022 05:36:56 +0000 (17:36 +1200)
committerDouglas Bagnall <dbagnall@samba.org>
Thu, 28 Jul 2022 22:47:37 +0000 (22:47 +0000)
This function simulates an add or modify operation for an ldb message to
determine the final value of a particular single-valued attribute. This
is useful when validating attributes that should stay in sync with other
attributes, such as servicePrincipalName and dNSHostName.

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

Signed-off-by: Joseph Sutton <josephsutton@catalyst.net.nz>
Reviewed-by: Douglas Bagnall <douglas.bagnall@catalyst.net.nz>
source4/dsdb/samdb/ldb_modules/util.c

index e7fe8f855df1376d1697740cb2c27b5e426369a5..42aa9a2d9d38c2fa7db9b76339875660022f07e4 100644 (file)
@@ -1568,6 +1568,113 @@ int dsdb_get_expected_new_values(TALLOC_CTX *mem_ctx,
        return LDB_SUCCESS;
 }
 
+
+/*
+ * Get the value of a single-valued attribute from an ADDed message. 'val' will only live as
+ * long as 'msg' and 'original_val' do, and must not be freed.
+ */
+int dsdb_msg_add_get_single_value(const struct ldb_message *msg,
+                                  const char *attr_name,
+                                  const struct ldb_val **val)
+{
+       const struct ldb_message_element *el = NULL;
+
+       /*
+        * The ldb_msg_normalize() call in ldb_request() ensures that
+        * there is at most one message element for each
+        * attribute. Thus, we don't need a loop to deal with an
+        * LDB_ADD.
+        */
+       el = ldb_msg_find_element(msg, attr_name);
+       if (el == NULL) {
+               *val = NULL;
+               return LDB_SUCCESS;
+       }
+       if (el->num_values != 1) {
+               return LDB_ERR_CONSTRAINT_VIOLATION;
+       }
+
+       *val = &el->values[0];
+       return LDB_SUCCESS;
+}
+
+/*
+ * Get the value of a single-valued attribute after processing a
+ * message. 'operation' is either LDB_ADD or LDB_MODIFY. 'val' will only live as
+ * long as 'msg' and 'original_val' do, and must not be freed.
+ */
+int dsdb_msg_get_single_value(const struct ldb_message *msg,
+                             const char *attr_name,
+                             const struct ldb_val *original_val,
+                             const struct ldb_val **val,
+                             enum ldb_request_type operation)
+{
+       unsigned idx;
+
+       *val = NULL;
+
+       if (operation == LDB_ADD) {
+               if (original_val != NULL) {
+                       /* This is an error on the caller's part. */
+                       return LDB_ERR_CONSTRAINT_VIOLATION;
+               }
+               return dsdb_msg_add_get_single_value(msg, attr_name, val);
+       }
+
+       SMB_ASSERT(operation == LDB_MODIFY);
+
+       *val = original_val;
+
+       for (idx = 0; idx < msg->num_elements; ++idx) {
+               const struct ldb_message_element *el = &msg->elements[idx];
+
+               if (ldb_attr_cmp(el->name, attr_name) != 0) {
+                       continue;
+               }
+
+               switch (el->flags & LDB_FLAG_MOD_MASK) {
+               case LDB_FLAG_MOD_ADD:
+                       if (el->num_values != 1) {
+                               return LDB_ERR_CONSTRAINT_VIOLATION;
+                       }
+                       if (*val != NULL) {
+                               return LDB_ERR_CONSTRAINT_VIOLATION;
+                       }
+
+                       *val = &el->values[0];
+
+                       break;
+
+               case LDB_FLAG_MOD_REPLACE:
+                       if (el->num_values > 1) {
+                               return LDB_ERR_CONSTRAINT_VIOLATION;
+                       }
+
+                       *val = el->num_values ? &el->values[0] : NULL;
+
+                       break;
+
+               case LDB_FLAG_MOD_DELETE:
+                       if (el->num_values > 1) {
+                               return LDB_ERR_CONSTRAINT_VIOLATION;
+                       }
+
+                       /*
+                        * If a value was specified for the delete, we don't
+                        * bother checking it matches the value we currently
+                        * have. Any mismatch will be caught later (e.g. in
+                        * ldb_kv_modify_internal).
+                        */
+
+                       *val = NULL;
+
+                       break;
+               }
+       }
+
+       return LDB_SUCCESS;
+}
+
 /*
  * This function determines the (last) structural or 88 object class of a passed
  * "objectClass" attribute - per MS-ADTS 3.1.1.1.4 this is the last value.