]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
lib-storage: Add support for "validated attributes"
authorTimo Sirainen <timo.sirainen@open-xchange.com>
Wed, 24 Jul 2019 11:42:21 +0000 (14:42 +0300)
committerTimo Sirainen <timo.sirainen@open-xchange.com>
Tue, 30 Jul 2019 08:19:58 +0000 (08:19 +0000)
If an attribute is marked with MAIL_ATTRIBUTE_INTERNAL_FLAG_VALIDATED, it's
assumed to be "validated". This means that it has a set() callback that
validates the value, which at minimum means that its size isn't excessively
large.

MAIL_ATTRIBUTE_TYPE_FLAG_VALIDATED can be used with set/get/iterate to allow
access only to these validated attributes. Trying to access non-validated
attributes will result in error.

src/lib-storage/index/index-attribute.c
src/lib-storage/mailbox-attribute.c
src/lib-storage/mailbox-attribute.h

index aed651f04ee6583f011a5d5474d61205fd083a29..15feb5503e564a11d1e4b041afbb4253da53d5ed 100644 (file)
@@ -88,6 +88,14 @@ index_storage_get_dict(struct mailbox *box, enum mail_attribute_type type_flags,
        struct dict_settings set;
        const char *error;
 
+       if ((type_flags & MAIL_ATTRIBUTE_TYPE_FLAG_VALIDATED) != 0) {
+               /* IMAP METADATA support isn't enabled, so don't allow using
+                  mail_attribute_dict. */
+               mail_storage_set_error(storage, MAIL_ERROR_NOTPOSSIBLE,
+                                      "Generic mailbox attributes not enabled");
+               return -1;
+       }
+
        if (mailbox_get_metadata(box, MAILBOX_METADATA_GUID, &metadata) < 0)
                return -1;
        *mailbox_prefix_r = guid_128_to_string(metadata.guid);
@@ -177,8 +185,11 @@ index_storage_attribute_get_dict_trans(struct mailbox_transaction_context *t,
        }
        i_assert(dtransp != NULL);
 
-       if (*dtransp != NULL) {
-               /* transaction already created */
+       if (*dtransp != NULL &&
+           (type_flags & MAIL_ATTRIBUTE_TYPE_FLAG_VALIDATED) == 0) {
+               /* Transaction already created. Even if it was, don't use it
+                  if _FLAG_VALIDATED is being used. It'll be handled below by
+                  returning failure. */
                if (mailbox_get_metadata(t->box, MAILBOX_METADATA_GUID,
                                         &metadata) < 0)
                        return -1;
@@ -189,6 +200,7 @@ index_storage_attribute_get_dict_trans(struct mailbox_transaction_context *t,
 
        if (index_storage_get_dict(t->box, type_flags, &dict, mailbox_prefix_r) < 0)
                return -1;
+       i_assert(*dtransp == NULL);
        *dtransp = *dtrans_r = dict_transaction_begin(dict);
        return 0;
 }
index 51455b885184e8699e7ad20a3b7a64b995d8f2f4..bb52376cf5f3756777a0b56acb329fb3dcf327bf 100644 (file)
@@ -47,6 +47,13 @@ void mailbox_attribute_register_internal(
        struct mailbox_attribute_internal ireg;
        unsigned int insert_idx;
 
+       /* Validated attributes must have a set() callback that validates the
+          provided values. Also read-only _RANK_AUTHORITY attributes don't
+          need validation. */
+       i_assert((iattr->flags & MAIL_ATTRIBUTE_INTERNAL_FLAG_VALIDATED) == 0 ||
+                iattr->set != NULL ||
+                iattr->rank == MAIL_ATTRIBUTE_INTERNAL_RANK_AUTHORITY);
+
        (void)array_bsearch_insert_pos(&mailbox_internal_attributes,
                iattr, mailbox_attribute_internal_cmp, &insert_idx);
 
@@ -88,8 +95,8 @@ void mailbox_attribute_unregister_internals(
 }
 
 static const struct mailbox_attribute_internal *
-mailbox_internal_attribute_get(enum mail_attribute_type type_flags,
-                              const char *key)
+mailbox_internal_attribute_get_int(enum mail_attribute_type type_flags,
+                                  const char *key)
 {
        const struct mailbox_attribute_internal *iattr;
        struct mailbox_attribute_internal dreg;
@@ -121,6 +128,22 @@ mailbox_internal_attribute_get(enum mail_attribute_type type_flags,
        }
 }
 
+static const struct mailbox_attribute_internal *
+mailbox_internal_attribute_get(enum mail_attribute_type type_flags,
+                              const char *key)
+{
+       const struct mailbox_attribute_internal *iattr;
+
+       iattr = mailbox_internal_attribute_get_int(type_flags, key);
+       if ((type_flags & MAIL_ATTRIBUTE_TYPE_FLAG_VALIDATED) != 0 &&
+           iattr != NULL &&
+           (iattr->flags & MAIL_ATTRIBUTE_INTERNAL_FLAG_VALIDATED) == 0) {
+               /* only validated attributes can be accessed */
+               iattr = NULL;
+       }
+       return iattr;
+}
+
 static void
 mailbox_internal_attributes_get(enum mail_attribute_type type_flags,
        const char *prefix, bool have_dict, ARRAY_TYPE(const_string) *attrs)
@@ -151,6 +174,10 @@ mailbox_internal_attributes_get(enum mail_attribute_type type_flags,
 
                if (regs[i].type != dreg.type)
                        return;
+               if ((type_flags & MAIL_ATTRIBUTE_TYPE_FLAG_VALIDATED) != 0 &&
+                   (regs[i].flags & MAIL_ATTRIBUTE_INTERNAL_FLAG_VALIDATED) == 0)
+                       continue;
+
                if (plen > 0) {
                        if (strncmp(key, bare_prefix, plen) != 0)
                                return;
@@ -214,6 +241,8 @@ mailbox_attribute_set_common(struct mailbox_transaction_context *t,
                default:
                        i_unreached();
                }
+               /* the value was validated. */
+               type_flags &= ~MAIL_ATTRIBUTE_TYPE_FLAG_VALIDATED;
        }
 
        ret = t->box->v.attribute_set(t, type_flags, key, value);
@@ -290,6 +319,12 @@ mailbox_attribute_get_common(struct mailbox *box,
        if (iattr != NULL) {
                switch (iattr->rank) {
                case MAIL_ATTRIBUTE_INTERNAL_RANK_OVERRIDE:
+                       /* we already checked that this attribute has
+                          validated-flag */
+                       type_flags &= ~MAIL_ATTRIBUTE_TYPE_FLAG_VALIDATED;
+
+                       if (iattr->get == NULL)
+                               break;
                        if ((ret = iattr->get(box, key, value_r)) != 0) {
                                if (ret < 0)
                                        return -1;
index 76428dd185884dde6074f87aee66fb3165a367de..093158a7f9234af7c866ae6395ff00c8110821b2 100644 (file)
@@ -183,6 +183,9 @@ enum mail_attribute_type {
        MAIL_ATTRIBUTE_TYPE_SHARED
 };
 #define MAIL_ATTRIBUTE_TYPE_MASK               0x0f
+/* Allow accessing only attributes with
+   MAIL_ATTRIBUTE_INTERNAL_FLAG_VALIDATED. */
+#define MAIL_ATTRIBUTE_TYPE_FLAG_VALIDATED     0x80
 
 enum mail_attribute_value_flags {
        MAIL_ATTRIBUTE_VALUE_FLAG_READONLY      = 0x01,
@@ -234,7 +237,11 @@ enum mail_attribute_internal_rank {
 
 enum mail_attribute_internal_flags {
        /* Apply this attribute to the given key and its children. */
-       MAIL_ATTRIBUTE_INTERNAL_FLAG_CHILDREN   = 0x01
+       MAIL_ATTRIBUTE_INTERNAL_FLAG_CHILDREN   = 0x01,
+       /* This attribute can be set/get even without generic METADATA support.
+          These attributes don't count towards any quotas either, so the set()
+          callback should validate that the value isn't excessively large. */
+       MAIL_ATTRIBUTE_INTERNAL_FLAG_VALIDATED  = 0x02,
 };
 
 struct mailbox_attribute_internal {