From 554347e1faa751f0f9cc9fd097a0b0168e5ef746 Mon Sep 17 00:00:00 2001
From: Dmitri Pal <dpal@redhat.com>
Date: Fri, 2 Apr 2010 20:08:14 -0400
Subject: [PATCH] [INI] Acess control and config change checks

1) Fixed the issue that metadata was saved
as numbers. Was supposed to be saved as strings.
2) Added two functions. One is to check permissions
on the config file. Another to check if the file
has changed and thus the cinfiguration needs
to be reread.
3) Added unit test will sample code
and comments how to use the functions.
4) Added doxygen description in the comments.
5) Fixed couple typos and ommisions here and there.
---
 common/ini/ini_config.c    |    4 +-
 common/ini/ini_config.h    |  118 ++++++++++++++++++-
 common/ini/ini_config_ut.c |  260 ++++++++++++++++++++++++++++++++++++++-
 common/ini/ini_metadata.c  |  292 +++++++++++++++++++++++++++++++++++++++++--
 4 files changed, 650 insertions(+), 24 deletions(-)

diff --git a/common/ini/ini_config.c b/common/ini/ini_config.c
index d8e4011..60fffba 100644
--- a/common/ini/ini_config.c
+++ b/common/ini/ini_config.c
@@ -1531,7 +1531,7 @@ static unsigned long long get_ullong_config_value(struct collection_item *item,
     char *endptr;
     unsigned long long val = 0;
 
-    TRACE_FLOW_STRING("get_long_config_value", "Entry");
+    TRACE_FLOW_STRING("get_ullong_config_value", "Entry");
 
     /* Do we have the item ? */
     if ((item == NULL) ||
@@ -1563,7 +1563,7 @@ static unsigned long long get_ullong_config_value(struct collection_item *item,
         return def;
     }
 
-    TRACE_FLOW_NUMBER("get_long_config_value returning", (long)val);
+    TRACE_FLOW_NUMBER("get_ullong_config_value returning", (long)val);
     return val;
 }
 
diff --git a/common/ini/ini_config.h b/common/ini/ini_config.h
index c0f82d0..bf7d157 100644
--- a/common/ini/ini_config.h
+++ b/common/ini/ini_config.h
@@ -264,6 +264,46 @@
  */
 
 /**
+ * @defgroup accesscheck Access control check flags
+ *
+ * @{
+ */
+
+/**
+ * @brief Validate access mode
+ *
+ * If this flag is specified the mode parameter
+ * will be matched against the permissions set on the file
+ * using the provided mask.
+ */
+#define INI_ACCESS_CHECK_MODE   0x00000001
+
+/**
+ * @brief Validate uid
+ *
+ * Provided uid will be checked against uid
+ * of the file. If the provided uid is 0
+ * the function will skip the check
+ * assuming success.
+ */
+#define INI_ACCESS_CHECK_UID   0x00000002
+
+/**
+ * @brief Validate gid
+ *
+ * Provided gid will be checked against gid
+ * of the file. If the provided gid is 0
+ * the function will skip the check
+ * assuming success.
+ */
+#define INI_ACCESS_CHECK_GID   0x00000004
+
+/**
+ * @}
+ */
+
+
+/**
  * @}
  */
 
@@ -485,6 +525,7 @@ const char *parsing_error_str(int parsing_error);
  *
  * @return 0 - Success.
  * @return EINVAL - Invalid parameter.
+ * @return EMOMEM - No memory.
  * @return Any error returned by fopen().
  *
  */
@@ -516,6 +557,7 @@ int config_from_file(const char *application,
  *                                 detected during parsing.
  *
  * @return 0 - Success.
+ * @return EMOMEM - No memory.
  * @return EINVAL - Invalid parameter.
  *
  */
@@ -567,6 +609,7 @@ int config_from_fd(const char *application,
  *
  * @return 0 - Success.
  * @return EINVAL - Invalid parameter.
+ * @return EMOMEM - No memory.
  * @return Any error returned by fopen().
  *
  *
@@ -622,6 +665,7 @@ int config_from_file_with_metadata(
  *
  * @return 0 - Success.
  * @return EINVAL - Invalid parameter.
+ * @return EMOMEM - No memory.
  *
  */
 int config_from_fd_with_metadata(
@@ -660,6 +704,7 @@ int config_from_fd_with_metadata(
  *
  * @return 0 - Success.
  * @return EINVAL - Invalid parameter.
+ * @return EMOMEM - No memory.
  * @return Any error returned by fopen().
  */
 int config_for_app(const char *application,
@@ -715,6 +760,7 @@ int config_for_app(const char *application,
  *
  * @return 0 - Success.
  * @return EINVAL - Invalid parameter.
+ * @return EMOMEM - No memory.
  * @return Any error returned by fopen().
  */
 int config_for_app_with_metadata(
@@ -727,6 +773,76 @@ int config_for_app_with_metadata(
                    uint32_t metaflags,
                    struct collection_item **meta_default,
                    struct collection_item **meta_appini);
+
+
+/**
+ *
+ * @brief Function to check ownership and permissions
+ *
+ * The function allow caller to make decision
+ * if the configuration file is from a trusted source
+ * or not.
+ *
+ * The flags control how to perform check.
+ * See \ref accesscheck "Access control check flags"
+ * section for more information.
+ *
+ * @param[in] metadata     Meta data object.
+ *                         Can't be NULL.
+ * @param[in] flags        How and what to check.
+ *                         Must be nonzero.
+ * @param[in] uid          UID to check.
+ * @param[in] gid          GID to check.
+ * @param[in] mode         Mode to check.
+ *                         Only permission bits
+ *                         are used.
+ * @param[in] mask         Which mode bits to check.
+ *                         If 0 all permision bits
+ *                         are checked.
+ *
+ * @return 0 - Success.
+ * @return EINVAL  - Invalid parameter.
+ * @return EACCESS - File properties do not match provided
+ *                   access parameters.
+ */
+int config_access_check(struct collection_item *metadata,
+                        uint32_t flags,
+                        int32_t uid,
+                        int32_t gid,
+                        uint32_t mode,
+                        uint32_t mask);
+
+
+/**
+ * @brief Function compares two meta data objects
+ *
+ * Function compares two meta data objects
+ * to determine whether the configuration
+ * has changed since last time the meta data
+ * was collected.
+ * The function checks three things about the
+ * file:
+ * - time stamp
+ * - device ID
+ * - i-node
+ * If any of those changes function will indicate
+ * that configuration changed.
+ *
+ * @param[in] metadata        Recent meta data object.
+ * @param[in] saved_metadata  Previously saved meta
+ *                            data object.
+ * @param[out] changed        Will be set to a nonzero value
+ *                            if the configuration has changed.
+ *
+ * @return 0 - No internal error
+ * @return EINVAL - Invalid argument
+ * @return ENOENT - Expected value is missing
+ * @return ENOMEM - No memory
+ */
+int config_changed(struct collection_item *metadata,
+                   struct collection_item *saved_metadata,
+                   int *changed);
+
 /**
  * @brief Function to free configuration object.
  *
@@ -747,7 +863,7 @@ void free_ini_config_errors(struct collection_item *error_set);
 /**
  * @brief Function to free metadata.
  *
- * @param[in] error_set       Configuration meta data object.
+ * @param[in] metadata       Configuration meta data object.
  *
  */
 void free_ini_config_metadata(struct collection_item *metadata);
diff --git a/common/ini/ini_config_ut.c b/common/ini/ini_config_ut.c
index dde1c52..59943eb 100644
--- a/common/ini/ini_config_ut.c
+++ b/common/ini/ini_config_ut.c
@@ -136,8 +136,10 @@ int single_file(void)
                                            &metadata);
     if (error) {
         printf("Attempt to read configuration returned error: %d\n",error);
-        printf("\n\nMetadata\n");
-        col_debug_collection(metadata, COL_TRAVERSE_DEFAULT);
+        if (metadata) {
+            printf("\n\nMeta data\n");
+            col_debug_collection(metadata, COL_TRAVERSE_DEFAULT);
+        }
         free_ini_config_metadata(metadata);
         return error;
     }
@@ -146,7 +148,7 @@ int single_file(void)
         printf("Expected no config but got some.\n");
         col_debug_collection(ini_config, COL_TRAVERSE_DEFAULT);
         free_ini_config(ini_config);
-        printf("\n\nMetadata\n");
+        printf("\n\nMeta data\n");
         col_debug_collection(metadata, COL_TRAVERSE_DEFAULT);
         free_ini_config_metadata(metadata);
         return EINVAL;
@@ -249,8 +251,10 @@ int single_fd(void)
         printf("\n\nErrors\n");
         print_file_parsing_errors(stdout, error_set);
         free_ini_config_errors(error_set);
-        printf("\n\nMetadata\n");
-        col_debug_collection(metadata, COL_TRAVERSE_DEFAULT);
+        if (metadata) {
+            printf("\n\nMeta data\n");
+            col_debug_collection(metadata, COL_TRAVERSE_DEFAULT);
+        }
         free_ini_config_metadata(metadata);
         return error;
     }
@@ -262,8 +266,6 @@ int single_fd(void)
         return EINVAL;
     }
 
-    /* FIXME get elements of the meta data and check them */
-
 
     COLOUT(printf("\n\nMeta data\n"));
     COLOUT(col_debug_collection(metadata, COL_TRAVERSE_DEFAULT));
@@ -1062,6 +1064,248 @@ int get_test(void)
     return EOK;
 }
 
+/* This is an emulation of the case when daemon starts
+ * and one needs to parse the configuration file
+ * for the first time and load configuration
+ */
+int startup_test(void)
+{
+    int error;
+    struct collection_item *ini_config = NULL;
+    struct collection_item *error_set = NULL;
+    struct collection_item *metadata = NULL;
+    uint32_t flags;
+
+
+    /* At startup we can simplify our life by
+     * parsing configuration and then checking
+     * the permissions. It is less optimal from
+     * the performnce point of view but simple to implement.
+     * Since it is the start of the daemon we can
+     * hope that parsing the config file would
+     * usually not a be a wasted effort.
+     * If permission check fails that means we should
+     * exit. Ok so we just parse the INI file for nothing.
+     * Not a big deal, I would say...
+     */
+
+    COLOUT(printf("STARTUP TEST\n"));
+
+
+    flags = INI_META_SEC_ACCESS_FLAG |
+            INI_META_SEC_ERROR_FLAG;
+
+    error = config_from_file_with_metadata("test", "./ini.conf",
+                                           &ini_config, INI_STOP_ON_NONE,
+                                           &error_set,
+                                           flags,
+                                           &metadata);
+    /* Debugging start ==> */
+    if (metadata) {
+        printf("\n\nMeta data\n");
+        col_debug_collection(metadata, COL_TRAVERSE_DEFAULT);
+    }
+    /* <==== end */
+
+    if (error) {
+        printf("Attempt to read configuration returned error: %d\n",error);
+
+        /* If you want to do any specific error checking, do it here.
+         * If you want to get the file error code from the
+         * metadata get it here.
+         */
+        free_ini_config_metadata(metadata);
+
+        /* Error reporting start ==> */
+        if (error_set) {
+            printf("\n\nErrors\n");
+            col_debug_collection(error_set, COL_TRAVERSE_DEFAULT);
+        }
+        /* <==== end */
+        free_ini_config_errors(error_set);
+        return error;
+    }
+
+    free_ini_config_errors(error_set);
+
+    /* So we are here if we successfully got configuration. */
+    /* You can check ownership and permissions here in one call */
+    /* We will check just permissions here. */
+    error = config_access_check(metadata,
+                                INI_ACCESS_CHECK_MODE, /* add uid & gui flags
+                                                        * in real case
+                                                        */
+                                0, /* <- will be real uid in real case */
+                                0, /* <- will be real gid in real case */
+                                0440, /* Checkling for r--r----- */
+                                0);
+    /* This check is expected to fail since
+     * the actual permissions on the test file are: rw-rw-r--
+     */
+
+    if (!error) {
+        printf("Expected error got success!\n");
+        free_ini_config_metadata(metadata);
+        free_ini_config(ini_config);
+        return EACCES;
+    }
+
+    error = config_access_check(metadata,
+                                INI_ACCESS_CHECK_MODE, /* add uid & gui flags
+                                                        * in real case
+                                                        */
+                                0, /* <- will be real uid in real case */
+                                0, /* <- will be real gid in real case */
+                                0664, /* Checkling for rw-rw-r-- */
+                                0);
+
+    if (error) {
+        printf("Access check failed %d!\n", error);
+        free_ini_config_metadata(metadata);
+        free_ini_config(ini_config);
+        return EACCES;
+    }
+
+
+    /* Use configuration */
+
+    COLOUT(printf("\n\nMeta data\n"));
+    COLOUT(col_debug_collection(metadata, COL_TRAVERSE_DEFAULT));
+    free_ini_config_metadata(metadata);
+
+    COLOUT(printf("\n\n----------------------\n"));
+
+    COLOUT(printf("\n\nConfiguration\n"));
+    COLOUT(col_debug_collection(ini_config, COL_TRAVERSE_DEFAULT));
+    free_ini_config(ini_config);
+
+    return 0;
+}
+
+int reload_test(void)
+{
+
+    int error;
+    struct collection_item *ini_config = NULL;
+    struct collection_item *metadata = NULL;
+    struct collection_item *saved_metadata = NULL;
+    uint32_t flags;
+    int changed = 0;
+    int fd;
+
+    COLOUT(printf("RELOAD TEST\n"));
+
+    /* Assume we saved metadata at the beginning
+     * when we opened the file and read configuration
+     * for the first time.
+     * Here we have to emulate it.
+     */
+
+    flags = INI_META_SEC_ACCESS_FLAG |
+            INI_META_ACTION_NOPARSE;
+
+    error = config_from_file_with_metadata("test", "./ini.conf",
+                                           &ini_config,
+                                           0,
+                                           NULL,
+                                           flags,
+                                           &saved_metadata);
+    if (error) {
+        printf("Attempt to read configuration returned error: %d\n",error);
+        free_ini_config_metadata(saved_metadata);
+        return error;
+    }
+
+    /*****************************************/
+
+    /* We are reloading so we probably doing it becuase
+     * we got a signal ot some kind of time out expired
+     * and it might be time for us to check if we need
+     * to reload. So assume it is time to check...
+     */
+
+    /* It is safer to open file first */
+    fd = open("./ini.conf", O_RDONLY);
+    if (fd < 0) {
+        error = errno;
+        printf("Attempt to read configuration returned error: %d\n", error);
+        free_ini_config_metadata(saved_metadata);
+        return error;
+    }
+
+    /* You migth be checking pretty frequently, once in 5 min for example
+     * but the config usually does not change for months
+     * so you do not want to do any extra processing every time you check.
+     */
+
+    /* Do permission check here right away on the file, or... */
+
+
+    flags = INI_META_SEC_ACCESS_FLAG |
+            INI_META_ACTION_NOPARSE;
+
+    error = config_from_fd_with_metadata("test", fd,
+                                         "./ini.conf",
+                                         &ini_config,
+                                         0,
+                                         NULL,
+                                         flags,
+                                         &metadata);
+    if (error) {
+        printf("Attempt to read configuration returned error: %d\n",error);
+        if (metadata) {
+            printf("\n\nMeta data\n");
+            col_debug_collection(metadata, COL_TRAVERSE_DEFAULT);
+        }
+        free_ini_config_metadata(metadata);
+        free_ini_config_metadata(saved_metadata);
+        close(fd);
+        return error;
+    }
+
+    /* ...or you can do permission check here using the metadata
+     * as it is done in the startup test.
+     * For now we skip this part and move on.
+     */
+
+    error = config_changed(metadata, saved_metadata, &changed);
+
+    if (error) {
+        printf("Internal error: %d\n",error);
+        printf("\n\nSaved Meta data\n");
+        col_debug_collection(saved_metadata, COL_TRAVERSE_DEFAULT);
+        printf("\n\nMeta data\n");
+        col_debug_collection(metadata, COL_TRAVERSE_DEFAULT);
+        free_ini_config_metadata(saved_metadata);
+        free_ini_config_metadata(metadata);
+        close(fd);
+        return error;
+
+    }
+
+    if (changed) {
+
+        /* Read the config from the descriptor and use it.
+         * Discard old saved meta data and save
+         * the latest one for future use...
+         */
+
+        /* Here it would be an error if it is different */
+        printf("Meta data is supposed to be same but different.\n");
+        printf("\n\nSaved Meta data\n");
+        col_debug_collection(saved_metadata, COL_TRAVERSE_DEFAULT);
+        printf("\n\nMeta data\n");
+        col_debug_collection(metadata, COL_TRAVERSE_DEFAULT);
+    }
+
+    free_ini_config_metadata(saved_metadata);
+    free_ini_config_metadata(metadata);
+    close(fd);
+
+    return 0;
+}
+
+
 int main(int argc, char *argv[])
 {
     int error = EOK;
@@ -1086,6 +1330,8 @@ int main(int argc, char *argv[])
         (error = real_test(NULL)) ||
          /* This should result in merged configuration */
         (error = real_test("./ini.conf")) ||
+        (error = startup_test()) ||
+        (error = reload_test()) ||
         (error = get_test())) {
         printf("Test failed! Error %d.\n", error);
         return -1;
diff --git a/common/ini/ini_metadata.c b/common/ini/ini_metadata.c
index 0c0fb42..2c42bf1 100644
--- a/common/ini/ini_metadata.c
+++ b/common/ini/ini_metadata.c
@@ -33,6 +33,17 @@
 
 #define INI_METADATA    "meta"
 
+/* Beffer length used for int to string conversions */
+#define CONVERSION_BUFFER 80
+
+/* Invalid file mode */
+#define WRONG_FMODE 0x80000000
+
+/* How many checks we need to perform to
+ * determin is the config file has changed.
+ */
+#define CHEKED_ALL 3
+
 /* Prepare metadata */
 int prepare_metadata(uint32_t metaflags,
                      struct collection_item **metadata,
@@ -73,6 +84,7 @@ int prepare_metadata(uint32_t metaflags,
             TRACE_ERROR_NUMBER("Failed to create error section", error);
             col_destroy_collection(metasec);
             col_destroy_collection(*metadata);
+            *metadata = NULL;
             return error;
         }
         /* If we are here we would have to save file open error */
@@ -96,6 +108,7 @@ int collect_metadata(uint32_t metaflags,
     struct collection_item *metasec = NULL;
     int filedes;
     struct stat file_stats;
+    char buff[CONVERSION_BUFFER];
 
     TRACE_FLOW_STRING("collect_metadata", "Entry");
     /* Check and create section for file error if needed */
@@ -122,10 +135,12 @@ int collect_metadata(uint32_t metaflags,
 
         /* Record statistics */
         /* UID */
-        error = col_add_int_property(metasec,
+        sprintf(buff, "%d", (int)file_stats.st_uid);
+        error = col_add_str_property(metasec,
                                      NULL,
                                      INI_META_KEY_UID,
-                                     file_stats.st_uid);
+                                     buff,
+                                     0);
         if (error) {
             TRACE_ERROR_NUMBER("Failed to save uid", error);
             col_destroy_collection(metasec);
@@ -133,10 +148,12 @@ int collect_metadata(uint32_t metaflags,
         }
 
         /* GID */
-        error = col_add_int_property(metasec,
+        sprintf(buff, "%d", (int)file_stats.st_gid);
+        error = col_add_str_property(metasec,
                                      NULL,
                                      INI_META_KEY_GID,
-                                     file_stats.st_gid);
+                                     buff,
+                                     0);
         if (error) {
             TRACE_ERROR_NUMBER("Failed to save gid", error);
             col_destroy_collection(metasec);
@@ -144,10 +161,12 @@ int collect_metadata(uint32_t metaflags,
         }
 
         /* PERMISSIONS */
-        error = col_add_unsigned_property(metasec,
-                                          NULL,
-                                          INI_META_KEY_PERM,
-                                          file_stats.st_mode);
+        sprintf(buff, "%u", (unsigned)file_stats.st_mode);
+        error = col_add_str_property(metasec,
+                                     NULL,
+                                     INI_META_KEY_PERM,
+                                     buff,
+                                     0);
         if (error) {
             TRACE_ERROR_NUMBER("Failed to save permissions", error);
             col_destroy_collection(metasec);
@@ -155,10 +174,12 @@ int collect_metadata(uint32_t metaflags,
         }
 
         /* Modification time stamp */
-        error = col_add_int_property(metasec,
+        sprintf(buff, "%d", (int)file_stats.st_mtime);
+        error = col_add_str_property(metasec,
                                      NULL,
                                      INI_META_KEY_MODIFIED,
-                                     file_stats.st_mtime);
+                                     buff,
+                                     0);
         if (error) {
             TRACE_ERROR_NUMBER("Failed to save modification time", error);
             col_destroy_collection(metasec);
@@ -178,10 +199,12 @@ int collect_metadata(uint32_t metaflags,
         }
 
         /* Device  ID */
-        error = col_add_int_property(metasec,
+        sprintf(buff, "%d", (int)file_stats.st_dev);
+        error = col_add_str_property(metasec,
                                      NULL,
                                      INI_META_KEY_DEV,
-                                     file_stats.st_dev);
+                                     buff,
+                                     0);
         if (error) {
             TRACE_ERROR_NUMBER("Failed to save inode", error);
             col_destroy_collection(metasec);
@@ -189,10 +212,12 @@ int collect_metadata(uint32_t metaflags,
         }
 
         /* i-node */
-        error = col_add_int_property(metasec,
+        sprintf(buff, "%d", (int)file_stats.st_ino);
+        error = col_add_str_property(metasec,
                                      NULL,
                                      INI_META_KEY_INODE,
-                                     file_stats.st_ino);
+                                     buff,
+                                     0);
         if (error) {
             TRACE_ERROR_NUMBER("Failed to save inode", error);
             col_destroy_collection(metasec);
@@ -226,3 +251,242 @@ void free_ini_config_metadata(struct collection_item *metadata)
     col_destroy_collection(metadata);
     TRACE_FLOW_STRING("free_ini_config_metadata", "Exit");
 }
+
+/* Function to check uid or gid */
+static int check_id(struct collection_item *metadata,
+                    int32_t id,
+                    const char *key)
+{
+    int error = EOK;
+    struct collection_item *item = NULL;
+    int32_t fid;
+
+    TRACE_FLOW_STRING("check_id", "Entry");
+    TRACE_INFO_STRING("Key", key);
+
+    error = get_config_item(INI_META_SEC_ACCESS,
+                            key,
+                            metadata,
+                            &item);
+    if (error) {
+        TRACE_ERROR_NUMBER("Internal collection error.", error);
+        return error;
+    }
+
+    /* Entry is supposed to be there so it is an error
+        * is the item is not found.
+        */
+    if (item == NULL) {
+        TRACE_ERROR_NUMBER("Expected item is not found.", ENOENT);
+        return ENOENT;
+    }
+
+    fid = get_int_config_value(item, 1, -1, &error);
+    if ((error) || (id == -1)) {
+        TRACE_ERROR_NUMBER("Conversion failed", EINVAL);
+        return EINVAL;
+    }
+
+    if (id != fid) {
+        TRACE_ERROR_NUMBER("File ID:", fid);
+        TRACE_ERROR_NUMBER("ID passed in.", id);
+        TRACE_ERROR_NUMBER("Access denied.", EACCES);
+        return EACCES;
+    }
+
+    TRACE_FLOW_STRING("check_id", "Exit");
+    return EOK;
+}
+
+/* Function to check access */
+int config_access_check(struct collection_item *metadata,
+                        uint32_t flags,
+                        int32_t uid,
+                        int32_t gid,
+                        uint32_t mode,
+                        uint32_t mask)
+{
+    int error = EOK;
+    struct collection_item *item = NULL;
+    uint32_t f_mode;
+
+    TRACE_FLOW_STRING("config_access_check", "Entry");
+
+    flags &= INI_ACCESS_CHECK_MODE |
+             INI_ACCESS_CHECK_GID |
+             INI_ACCESS_CHECK_UID;
+
+    if ((metadata == NULL) || (flags == 0)) {
+        TRACE_ERROR_NUMBER("Invalid parameter.", EINVAL);
+        return EINVAL;
+
+    }
+
+    /* Check that metadata is actually metadata */
+    if(!col_is_of_class(metadata, COL_CLASS_INI_META)) {
+        TRACE_ERROR_NUMBER("Invalid collection.", EINVAL);
+        return EINVAL;
+    }
+
+    /* Check mode */
+    if (flags & INI_ACCESS_CHECK_MODE) {
+
+        error = get_config_item(INI_META_SEC_ACCESS,
+                                INI_META_KEY_PERM,
+                                metadata,
+                                &item);
+        if (error) {
+            TRACE_ERROR_NUMBER("Internal collection error.", error);
+            return error;
+        }
+
+        /* Entry is supposed to be there so it is an error
+            * is the item is not found.
+            */
+        if (item == NULL) {
+            TRACE_ERROR_NUMBER("Expected item is not found.", ENOENT);
+            return ENOENT;
+        }
+
+        f_mode = get_unsigned_config_value(item, 1, WRONG_FMODE, &error);
+        if ((error) || (f_mode == WRONG_FMODE)) {
+            TRACE_ERROR_NUMBER("Conversion failed", error);
+            return ENOENT;
+        }
+
+        TRACE_INFO_NUMBER("File mode as saved.", f_mode);
+        f_mode &= S_IRWXU | S_IRWXG | S_IRWXO;
+        TRACE_INFO_NUMBER("File mode adjusted.", f_mode);
+
+        TRACE_INFO_NUMBER("Mode as provided.", mode);
+        mode &= S_IRWXU | S_IRWXG | S_IRWXO;
+        TRACE_INFO_NUMBER("Mode adjusted.", mode);
+
+    	/* Adjust mask */
+        if (mask == 0) mask = S_IRWXU | S_IRWXG | S_IRWXO;
+        else mask &= S_IRWXU | S_IRWXG | S_IRWXO;
+
+        if ((mode & mask) != (f_mode & mask)) {
+            TRACE_INFO_NUMBER("File mode:", (mode & mask));
+            TRACE_INFO_NUMBER("Mode adjusted.", (f_mode & mask));
+            TRACE_ERROR_NUMBER("Access denied.", EACCES);
+            return EACCES;
+        }
+    }
+
+    /* Check uid */
+    if (flags & INI_ACCESS_CHECK_UID) {
+
+        error = check_id(metadata, uid, INI_META_KEY_UID);
+        if (error) {
+            TRACE_ERROR_NUMBER("Check for UID failed.", error);
+            return error;
+        }
+    }
+
+    /* Check gid */
+    if (flags & INI_ACCESS_CHECK_GID) {
+
+        error = check_id(metadata, gid, INI_META_KEY_GID);
+        if (error) {
+            TRACE_ERROR_NUMBER("Check for UID failed.", error);
+            return error;
+        }
+    }
+
+    TRACE_FLOW_STRING("config_access_check", "Exit");
+    return error;
+
+}
+
+static int get_checked_value(struct collection_item *metadata,
+                                  const char *key,
+                                  int *err)
+{
+
+    int error = EOK;
+    struct collection_item *item = NULL;
+    int value;
+
+    TRACE_FLOW_STRING("get_checked_value", "Entry");
+    TRACE_INFO_STRING("Key", key);
+
+    error = get_config_item(INI_META_SEC_ACCESS,
+                            key,
+                            metadata,
+                            &item);
+    if (error) {
+        TRACE_ERROR_NUMBER("Internal collection error.", error);
+        *err = error;
+        return -1;
+    }
+
+    /* Entry is supposed to be there so it is an error
+     * is the item is not found.
+     */
+    if (item == NULL) {
+        TRACE_ERROR_NUMBER("Expected item is not found.", ENOENT);
+        *err = ENOENT;
+        return -1;
+    }
+
+    value = get_int_config_value(item, 1, -1, &error);
+    if ((error) || (value == -1)) {
+        TRACE_ERROR_NUMBER("Conversion failed", EINVAL);
+        *err = EINVAL;
+        return -1;
+    }
+
+    TRACE_FLOW_NUMBER("get_checked_value Returning", value);
+    return value;
+
+}
+
+
+/* Function to check whether the configuration is different */
+int config_changed(struct collection_item *metadata,
+                   struct collection_item *saved_metadata,
+                   int *changed)
+{
+    int error = EOK;
+    struct collection_item *md[2];
+    int value[3][2];
+    const char *key[] = { INI_META_KEY_MODIFIED,
+                          INI_META_KEY_DEV,
+                          INI_META_KEY_INODE };
+    int i, j;
+
+
+    TRACE_FLOW_STRING("config_changed", "Entry");
+
+    if ((!metadata) ||
+        (!saved_metadata) ||
+        (!changed) ||
+        (!col_is_of_class(metadata, COL_CLASS_INI_META)) ||
+        (!col_is_of_class(saved_metadata, COL_CLASS_INI_META))) {
+        TRACE_ERROR_NUMBER("Invalid argument.", EINVAL);
+        return EINVAL;
+    }
+
+    md[0] = metadata;
+    md[1] = saved_metadata;
+
+    /* Get three values from each collection and compare them */
+    for (i = 0; i < 3; i++) {
+        for (j = 0; j < 2; j++) {
+            value[i][j] = get_checked_value(md[j], key[i] , &error);
+            if (error) {
+                TRACE_ERROR_NUMBER("Failed to get section.", error);
+                return error;
+            }
+        }
+        if (value[i][0] != value[i][0]) {
+            *changed = 1;
+            break;
+        }
+    }
+
+    TRACE_FLOW_STRING("config_changed", "Exit");
+    return error;
+
+}
-- 
1.5.5.6

