Implement recursive efficient IMA fixing
authorDmitry Kasatkin <d.kasatkin@samsung.com>
Wed, 15 Jan 2014 18:24:54 +0000 (20:24 +0200)
committerDmitry Kasatkin <d.kasatkin@samsung.com>
Fri, 17 Jan 2014 10:43:03 +0000 (12:43 +0200)
Using scripts which do many forking and execution is very slow on
embedded/mobile devices. C based implementation is about 7 times faster.

Signed-off-by: Dmitry Kasatkin <d.kasatkin@samsung.com>
src/evmctl.c

index 2c4456b..ebbbff0 100644 (file)
@@ -3,7 +3,7 @@
  *
  * Copyright (C) 2011 Nokia Corporation
  * Copyright (C) 2011,2012,2013 Intel Corporation
- * Copyright (C) 2013 Samsung Electronics
+ * Copyright (C) 2013,2014 Samsung Electronics
  *
  * Authors:
  * Dmitry Kasatkin <dmitry.kasatkin@nokia.com>
@@ -263,6 +263,7 @@ static char *uuid_str;
 static int x509;
 static int user_sig_type;
 static char *keyfile;
+static char *de_type;
 
 typedef int (*sign_hash_fn_t)(const char *algo, const unsigned char *hash, int size, const char *keyfile, unsigned char *sig);
 
@@ -1580,6 +1581,154 @@ static int cmd_hmac_evm(struct command *cmd)
        return hmac_evm(file, "/etc/keys/evm-key-plain");
 }
 
+typedef int (*find_cb_t)(const char *path);
+
+static int ima_fix(const char *path)
+{
+       int fd, size, len, ima = 0, evm = 0;
+       char buf[1024], *list = buf;
+
+       log_info("%s\n", path);
+
+       if (xattr) {
+               /* re-measuring takes a time
+                * in some cases we can skip labeling if xattrs exists
+                */
+               size = llistxattr(path, list, sizeof(buf));
+               if (size < 0) {
+                       log_errno("llistxattr() failed: %s\n", path);
+                       return -1;
+               }
+               for (; size > 0; len++, size -= len, list += len) {
+                       len = strlen(list);
+                       if (!strcmp(list, "security.ima"))
+                               ima = 1;
+                       else if (!strcmp(list, "security.evm"))
+                               evm = 1;
+               }
+               if (ima && evm)
+                       return 0;
+       }
+
+       fd = open(path, O_RDONLY);
+       if (fd < 0) {
+               log_errno("%s open failed", path);
+               return -1;
+       }
+
+       close(fd);
+
+       return 0;
+}
+
+#define REG_MASK       (1 << DT_REG)
+#define DIR_MASK       (1 << DT_DIR)
+#define LNK_MASK       (1 << DT_LNK)
+#define CHR_MASK       (1 << DT_CHR)
+#define BLK_MASK       (1 << DT_BLK)
+
+static dev_t fs_dev;
+
+static int find(const char *path, int dts, find_cb_t func)
+{
+       struct dirent *de;
+       DIR *dir;
+
+       if (fs_dev) {
+               struct stat st;
+               int err = lstat(path, &st);
+               if (err < 0) {
+                       log_err("stat() failed\n");
+                       return err;
+               }
+               if (st.st_dev != fs_dev)
+                       return 0;
+       }
+
+       dir = opendir(path);
+       if (!dir) {
+               log_err("Unable to open %s\n", path);
+               return -1;
+       }
+
+       if (fchdir(dirfd(dir))) {
+               log_err("Unable to chdir %s\n", path);
+               return -1;
+       }
+
+       while ((de = readdir(dir))) {
+               if (!strcmp(de->d_name, "..") || !strcmp(de->d_name, "."))
+                       continue;
+               log_debug("path: %s, type: %u\n", de->d_name, de->d_type);
+               if (de->d_type == DT_DIR)
+                       find(de->d_name, dts, func);
+               else if (dts & (1 << de->d_type))
+                       func(de->d_name);
+       }
+
+       if (chdir("..")) {
+               log_err("Unable to chdir %s\n", path);
+               return -1;
+       }
+
+       if (dts & DIR_MASK)
+               func(path);
+
+       closedir(dir);
+
+       return 0;
+}
+
+static int cmd_ima_fix(struct command *cmd)
+{
+       char *path = g_argv[optind++];
+       int err, dts = REG_MASK; /* only regular files by default */
+       struct stat st;
+
+       if (!path) {
+               log_err("Parameters missing\n");
+               print_usage(cmd);
+               return -1;
+       }
+
+       xattr = 0; /* do not check xattrs, fix everything */
+
+       if (de_type) {
+               int i;
+
+               dts = 0;
+               for (i = 0; de_type[i]; i++) {
+                       switch (de_type[i]) {
+                       case 'f':
+                               dts |= REG_MASK; break;
+                       case 'd':
+                               dts |= DIR_MASK; break;
+                       case 's':
+                               dts |= BLK_MASK | CHR_MASK | LNK_MASK; break;
+                       case 'x':
+                               /* check xattrs */
+                               xattr = 1; break;
+                       case 'm':
+                               /* stay within the same filesystem*/
+                               err = lstat(path, &st);
+                               if (err < 0) {
+                                       log_err("stat() failed\n");
+                                       return err;
+                               }
+                               fs_dev = st.st_dev; /* filesystem to start from */
+                               break;
+                       }
+               }
+       }
+
+       err = find(path, dts, ima_fix);
+       if (err)
+               return err;
+
+       return 0;
+}
+
+
 static char *pcrs = "/sys/class/misc/tpm0/device/pcrs";
 
 static int tpm_pcr_read(int idx, uint8_t *pcr, int len)
@@ -1894,6 +2043,9 @@ static void usage(void)
                "  -k, --key          path to signing key (default keys are /etc/keys/{privkey,pubkey}_evm.pem)\n"
                "  -p, --pass         password for encrypted signing key\n"
                "  -u, --uuid         use file system UUID in HMAC calculation (EVM v2)\n"
+               "  -t, --type         file types to fix 'fdsxm' (f - file, d - directory, s - block/char/symlink)\n"
+               "                     x - skip fixing if both ima and evm xattrs exist (caution: they may be wrong)\n"
+               "                     m - stay on the same filesystem (like 'find -xdev')\n"
                "  -n                 print result to stdout instead of setting xattr\n"
                "  -v                 increase verbosity level\n"
                "  -h, --help         display this help and exit\n"
@@ -1909,6 +2061,7 @@ struct command cmds[] = {
        {"ima_verify", cmd_verify_ima, 0, "file", "Verify IMA signature (for debugging).\n"},
        {"ima_hash", cmd_hash_ima, 0, "file", "Make file content hash.\n"},
        {"ima_measurement", cmd_ima_measurement, 0, "file", "Verify measurement list (experimental).\n"},
+       {"ima_fix", cmd_ima_fix, 0, "[-t fdsxm] path", "Recursively fix IMA/EVM xattrs in fix mode.\n"},
 #ifdef DEBUG
        {"hmac", cmd_hmac_evm, 0, "[--imahash | --imasig ] file", "Sign file metadata with HMAC using symmetric key (for testing purpose).\n"},
 #endif
@@ -1926,6 +2079,7 @@ static struct option opts[] = {
        {"uuid", 2, 0, 'u'},
        {"x509", 0, 0, 'x'},
        {"key", 1, 0, 'k'},
+       {"type", 1, 0, 't'},
        {}
 
 };
@@ -1941,7 +2095,7 @@ int main(int argc, char *argv[])
        verify_hash = verify_hash_v1;
 
        while (1) {
-               c = getopt_long(argc, argv, "hvnsda:p:fu::xk:", opts, &lind);
+               c = getopt_long(argc, argv, "hvnsda:p:fu::xk:t:", opts, &lind);
                if (c == -1)
                        break;
 
@@ -1991,6 +2145,9 @@ int main(int argc, char *argv[])
                case 'k':
                        keyfile = optarg;
                        break;
+               case 't':
+                       de_type = optarg;
+                       break;
                case '?':
                        exit(1);
                        break;