From: Matthias G. <mge...@su...> - 2017-08-31 21:26:49
|
Some file systems like btrfs and overlayfs don't report usable st_dev values during stat(). The result is that a command like this: evmctl sign --rsa --imasig -k ~/ima/ima_private.pem /some/file fails with Failed to read UUID. Root access might require. errno: No data available (61) Parsing /proc/self/mountinfo seems to be the usual way to get around this limitation. --- src/evmctl.c | 85 +++++++++++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 75 insertions(+), 10 deletions(-) diff --git a/src/evmctl.c b/src/evmctl.c index c54efbb..3baa965 100644 --- a/src/evmctl.c +++ b/src/evmctl.c @@ -276,25 +276,90 @@ static int pack_uuid(const char *uuid_str, char *uuid) return 0; } +#define MAJ_MIN_COL 3 +#define MOUNT_SOURCE_COL 10 + +static int get_blk_dev(dev_t dev, char *path, size_t size) +{ + /* + * Looking in /dev/block/<major>:<minor> does not suffice for some + * special file systems like btrfs or overlayfs, because their st_dev + * values do not correspond to any named block device. + * + * Instead parse /proc/self/mountinfo for the correct source device. + */ + int ret = -1; + unsigned int maj = major(dev); + unsigned int min = minor(dev); + char line[LINE_MAX]; + char maj_min_val[LINE_MAX]; + size_t print_res = 0; + FILE *mountinfo = fopen("/proc/self/mountinfo", "r"); + + if (!mountinfo) + return -1; + + // comparison string for the column parsing below + print_res = snprintf(maj_min_val, LINE_MAX, "%u:%u", maj, min); + + if (print_res >= LINE_MAX) + { + // buffer to small + ret = -2; + goto out; + } + + while (fgets(line, LINE_MAX, mountinfo) != NULL) + { + size_t column = 0; + char *token, *tmp_line = line; + + while ( (token = strtok(tmp_line, " ")) ) + { + if (tmp_line) + tmp_line = NULL; + column++; + + if (column == MAJ_MIN_COL) + { + if (strcmp(token, maj_min_val) != 0) + // not the device we're looking for + break; + } + else if (column == MOUNT_SOURCE_COL) + { + print_res = snprintf(path, size, "%s", token); + log_debug("dev: %u:%u -> %s\n", maj, min, token); + ret = print_res < size ? 0 : -2; + goto out; + } + } + } + + // not found or read/parse error + ret = -3; +out: + if (mountinfo) + fclose(mountinfo); + + return ret; +} + static int get_uuid(struct stat *st, char *uuid) { - uint32_t dev; - unsigned minor, major; - char path[PATH_MAX], _uuid[37]; + char blkdev[PATH_MAX], cmdline[PATH_MAX], _uuid[37]; FILE *fp; size_t len; if (uuid_str) return pack_uuid(uuid_str, uuid); - dev = st->st_dev; - major = (dev & 0xfff00) >> 8; - minor = (dev & 0xff) | ((dev >> 12) & 0xfff00); + if (get_blk_dev(st->st_dev, blkdev, PATH_MAX) != 0) + goto err; - log_debug("dev: %u:%u\n", major, minor); - sprintf(path, "blkid -s UUID -o value /dev/block/%u:%u", major, minor); + snprintf(cmdline, PATH_MAX, "blkid -s UUID -o value %s", blkdev); - fp = popen(path, "r"); + fp = popen(cmdline, "r"); if (!fp) goto err; @@ -305,7 +370,7 @@ static int get_uuid(struct stat *st, char *uuid) return pack_uuid(_uuid, uuid); err: - log_err("Failed to read UUID. Root access might require.\n"); + log_err("Failed to read UUID. Root access might be required.\n"); return -1; } -- 2.13.5 -- Matthias Gerstner <mat...@su...> Dipl.-Wirtsch.-Inf. (FH), Security Engineer https://www.suse.com/security Telefon: +49 911 740 53 290 SUSE Linux GmbH GF: Felix Imendörffer, Jane Smithard, Graham Norton HRB 21284 (AG Nuernberg) |