Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit d33c6c0e authored by Sami Tolvanen's avatar Sami Tolvanen Committed by Gerrit Code Review
Browse files

Merge "Use structured format for verity metadata"

parents cfead6da 946a0f3e
Loading
Loading
Loading
Loading
+10 −0
Original line number Diff line number Diff line
@@ -24,6 +24,7 @@

struct fs_mgr_flag_values {
    char *key_loc;
    char *verity_loc;
    long long part_length;
    char *label;
    int partnum;
@@ -107,6 +108,14 @@ static int parse_flags(char *flags, struct flag_list *fl,
                     * location of the keys.  Get it and return it.
                     */
                    flag_vals->key_loc = strdup(strchr(p, '=') + 1);
                } else if ((fl[i].flag == MF_VERIFY) && flag_vals) {
                    /* If the verify flag is followed by an = and the
                     * location for the verity state,  get it and return it.
                     */
                    char *start = strchr(p, '=');
                    if (start) {
                        flag_vals->verity_loc = strdup(start + 1);
                    }
                } else if ((fl[i].flag == MF_FORCECRYPT) && flag_vals) {
                    /* The forceencrypt flag is followed by an = and the
                     * location of the keys.  Get it and return it.
@@ -291,6 +300,7 @@ struct fstab *fs_mgr_read_fstab(const char *fstab_path)
        fstab->recs[cnt].fs_mgr_flags = parse_flags(p, fs_mgr_flags,
                                                    &flag_vals, NULL, 0);
        fstab->recs[cnt].key_loc = flag_vals.key_loc;
        fstab->recs[cnt].verity_loc = flag_vals.verity_loc;
        fstab->recs[cnt].length = flag_vals.part_length;
        fstab->recs[cnt].label = flag_vals.label;
        fstab->recs[cnt].partnum = flag_vals.partnum;
+185 −62
Original line number Diff line number Diff line
@@ -47,16 +47,23 @@
#define VERITY_METADATA_SIZE 32768
#define VERITY_TABLE_RSA_KEY "/verity_key"

#define METADATA_MAGIC 0x01564c54
#define METADATA_TAG_MAX_LENGTH 63
#define METADATA_EOD "eod"

#define VERITY_STATE_TAG "verity_state"
#define VERITY_STATE_HEADER 0x83c0ae9d
#define VERITY_STATE_VERSION 1

#define VERITY_KMSG_RESTART "dm-verity device corrupted"
#define VERITY_KMSG_BUFSIZE 1024

#define __STRINGIFY(x) #x
#define STRINGIFY(x) __STRINGIFY(x)

struct verity_state {
    uint32_t header;
    uint32_t version;
    uint32_t size;
    int32_t mode;
};

@@ -453,83 +460,151 @@ static int was_verity_restart()
    return 0;
}

static int write_verity_state(const char *fname, off64_t offset, int32_t mode)
static int metadata_add(FILE *fp, long start, const char *tag,
        unsigned int length, off64_t *offset)
{
    int fd;
    if (TEMP_FAILURE_RETRY(fseek(fp, start, SEEK_SET)) < 0 ||
        TEMP_FAILURE_RETRY(fprintf(fp, "%s %u\n", tag, length)) < 0) {
        return -1;
    }

    *offset = ftell(fp);

    if (TEMP_FAILURE_RETRY(fseek(fp, length, SEEK_CUR)) < 0 ||
        TEMP_FAILURE_RETRY(fprintf(fp, METADATA_EOD " 0\n")) < 0) {
        return -1;
    }

    return 0;
}

static int metadata_find(const char *fname, const char *stag,
        unsigned int slength, off64_t *offset)
{
    FILE *fp = NULL;
    char tag[METADATA_TAG_MAX_LENGTH + 1];
    int rc = -1;
    int n;
    long start = 0x4000; /* skip cryptfs metadata area */
    uint32_t magic;
    unsigned int length = 0;

    struct verity_state s = {
        VERITY_STATE_HEADER,
        VERITY_STATE_VERSION,
        sizeof(struct verity_state),
        mode
    };
    if (!fname) {
        return -1;
    }

    fd = TEMP_FAILURE_RETRY(open(fname, O_WRONLY | O_SYNC | O_CLOEXEC));
    fp = TEMP_FAILURE_RETRY(fopen(fname, "r+"));

    if (fd == -1) {
    if (!fp) {
        ERROR("Failed to open %s (%s)\n", fname, strerror(errno));
        goto out;
    }

    if (TEMP_FAILURE_RETRY(lseek64(fd, offset, SEEK_SET)) < 0) {
        ERROR("Failed to seek %s to %" PRIu64 " (%s)\n", fname, offset, strerror(errno));
    /* check magic */
    if (TEMP_FAILURE_RETRY(fseek(fp, start, SEEK_SET)) < 0 ||
        TEMP_FAILURE_RETRY(fread(&magic, sizeof(magic), 1, fp)) != 1) {
        ERROR("Failed to read magic from %s (%s)\n", fname, strerror(errno));
        goto out;
    }

    if (TEMP_FAILURE_RETRY(write(fd, &s, sizeof(s))) != sizeof(s)) {
        ERROR("Failed to write %zu bytes to %s (%s)\n", sizeof(s), fname, strerror(errno));
    if (magic != METADATA_MAGIC) {
        magic = METADATA_MAGIC;

        if (TEMP_FAILURE_RETRY(fseek(fp, start, SEEK_SET)) < 0 ||
            TEMP_FAILURE_RETRY(fwrite(&magic, sizeof(magic), 1, fp)) != 1) {
            ERROR("Failed to write magic to %s (%s)\n", fname, strerror(errno));
            goto out;
        }

        rc = metadata_add(fp, start + sizeof(magic), stag, slength, offset);
        if (rc < 0) {
            ERROR("Failed to add metadata to %s: %s\n", fname, strerror(errno));
        }

        goto out;
    }

    start += sizeof(magic);

    while (1) {
        n = TEMP_FAILURE_RETRY(fscanf(fp,
                "%" STRINGIFY(METADATA_TAG_MAX_LENGTH) "s %u\n",
                tag, &length));

        if (n == 2 && strcmp(tag, METADATA_EOD)) {
            /* found a tag */
            start = ftell(fp);

            if (!strcmp(tag, stag) && length == slength) {
                *offset = start;
                rc = 0;
                goto out;
            }

            start += length;

            if (TEMP_FAILURE_RETRY(fseek(fp, length, SEEK_CUR)) < 0) {
                ERROR("Failed to seek %s (%s)\n", fname, strerror(errno));
                goto out;
            }
        } else {
            rc = metadata_add(fp, start, stag, slength, offset);
            if (rc < 0) {
                ERROR("Failed to write metadata to %s: %s\n", fname,
                    strerror(errno));
            }
            goto out;
        }
   }

out:
    if (fd != -1) {
        TEMP_FAILURE_RETRY(close(fd));
    if (fp) {
        TEMP_FAILURE_RETRY(fflush(fp));
        TEMP_FAILURE_RETRY(fclose(fp));
    }

    return rc;
}

static int get_verity_state_location(char *location, off64_t *offset)
static int write_verity_state(const char *fname, off64_t offset, int32_t mode)
{
    char state_off[PROPERTY_VALUE_MAX];
    int fd;
    int rc = -1;
    struct verity_state s = { VERITY_STATE_HEADER, VERITY_STATE_VERSION, mode };

    if (property_get("ro.verity.state.location", location, NULL) <= 0) {
        return -1;
    }
    fd = TEMP_FAILURE_RETRY(open(fname, O_WRONLY | O_SYNC | O_CLOEXEC));

    if (*location != '/' || access(location, R_OK | W_OK) == -1) {
        ERROR("Failed to access verity state %s (%s)\n", location, strerror(errno));
        return -1;
    if (fd == -1) {
        ERROR("Failed to open %s (%s)\n", fname, strerror(errno));
        goto out;
    }

    *offset = 0;
    if (TEMP_FAILURE_RETRY(pwrite64(fd, &s, sizeof(s), offset)) != sizeof(s)) {
        ERROR("Failed to write %zu bytes to %s to offset %" PRIu64 " (%s)\n",
            sizeof(s), fname, offset, strerror(errno));
        goto out;
    }

    if (property_get("ro.verity.state.offset", state_off, NULL) > 0) {
        *offset = strtoll(state_off, NULL, 0);
    rc = 0;

        if (errno == ERANGE || errno == EINVAL) {
            ERROR("Invalid value in ro.verity.state.offset (%s)\n", state_off);
            return -1;
        }
out:
    if (fd != -1) {
        TEMP_FAILURE_RETRY(close(fd));
    }

    return 0;
    return rc;
}

int fs_mgr_load_verity_state(int *mode)
static int load_verity_state(struct fstab_rec *fstab, int *mode)
{
    char fname[PROPERTY_VALUE_MAX];
    int fd = -1;
    int rc = -1;
    off64_t offset = 0;
    struct verity_state s;

    if (get_verity_state_location(fname, &offset) < 0) {
        /* location for dm-verity state is not specified, fall back to
         * default behavior: return -EIO for corrupted blocks */
    if (metadata_find(fstab->verity_loc, VERITY_STATE_TAG, sizeof(s),
            &offset) < 0) {
        /* fall back to stateless behavior */
        *mode = VERITY_MODE_EIO;
        rc = 0;
        goto out;
@@ -539,28 +614,27 @@ int fs_mgr_load_verity_state(int *mode)
        /* device was restarted after dm-verity detected a corrupted
         * block, so switch to logging mode */
        *mode = VERITY_MODE_LOGGING;
        rc = write_verity_state(fname, offset, *mode);
        rc = write_verity_state(fstab->verity_loc, offset, *mode);
        goto out;
    }

    fd = TEMP_FAILURE_RETRY(open(fname, O_RDONLY | O_CLOEXEC));
    fd = TEMP_FAILURE_RETRY(open(fstab->verity_loc, O_RDONLY | O_CLOEXEC));

    if (fd == -1) {
        ERROR("Failed to open %s (%s)\n", fname, strerror(errno));
        goto out;
    }

    if (TEMP_FAILURE_RETRY(lseek64(fd, offset, SEEK_SET)) < 0) {
        ERROR("Failed to seek %s to %" PRIu64 " (%s)\n", fname, offset, strerror(errno));
        ERROR("Failed to open %s (%s)\n", fstab->verity_loc, strerror(errno));
        goto out;
    }

    if (TEMP_FAILURE_RETRY(read(fd, &s, sizeof(s))) != sizeof(s)) {
        ERROR("Failed to read %zu bytes from %s (%s)\n", sizeof(s), fname, strerror(errno));
    if (TEMP_FAILURE_RETRY(pread64(fd, &s, sizeof(s), offset)) != sizeof(s)) {
        ERROR("Failed to read %zu bytes from %s offset %" PRIu64 " (%s)\n",
            sizeof(s), fstab->verity_loc, offset, strerror(errno));
        goto out;
    }

    if (s.header != VERITY_STATE_HEADER) {
        /* space allocated, but no state written. write default state */
        *mode = VERITY_MODE_DEFAULT;
        rc = write_verity_state(fstab->verity_loc, offset, *mode);
        goto out;
    }

@@ -569,11 +643,6 @@ int fs_mgr_load_verity_state(int *mode)
        goto out;
    }

    if (s.size != sizeof(s)) {
        ERROR("Unexpected verity state size (%u)\n", s.size);
        goto out;
    }

    if (s.mode < VERITY_MODE_EIO ||
        s.mode > VERITY_MODE_LAST) {
        ERROR("Unsupported verity mode (%u)\n", s.mode);
@@ -591,13 +660,61 @@ out:
    return rc;
}

int fs_mgr_load_verity_state(int *mode)
{
    char fstab_filename[PROPERTY_VALUE_MAX + sizeof(FSTAB_PREFIX)];
    char propbuf[PROPERTY_VALUE_MAX];
    int rc = -1;
    int i;
    struct fstab *fstab = NULL;

    *mode = VERITY_MODE_DEFAULT;

    property_get("ro.hardware", propbuf, "");
    snprintf(fstab_filename, sizeof(fstab_filename), FSTAB_PREFIX"%s", propbuf);

    fstab = fs_mgr_read_fstab(fstab_filename);

    if (!fstab) {
        ERROR("Failed to read %s\n", fstab_filename);
        goto out;
    }

    for (i = 0; i < fstab->num_entries; i++) {
        if (!fs_mgr_is_verified(&fstab->recs[i])) {
            continue;
        }

        rc = load_verity_state(&fstab->recs[i], mode);
        if (rc < 0) {
            continue;
        }

        /* if any of the verified partitions are in logging mode, return */
        if (*mode == VERITY_MODE_LOGGING) {
            rc = 0;
            goto out;
        }
    }

    /* if there were multiple partitions, all in non-logging mode, return the
     * state of the last one */
    rc = 0;

out:
    if (fstab) {
        fs_mgr_free_fstab(fstab);
    }

    return rc;
}

int fs_mgr_update_verity_state(fs_mgr_verity_state_callback callback)
{
    _Alignas(struct dm_ioctl) char buffer[DM_BUF_SIZE];
    char fstab_filename[PROPERTY_VALUE_MAX + sizeof(FSTAB_PREFIX)];
    char *mount_point;
    char propbuf[PROPERTY_VALUE_MAX];
    char state_loc[PROPERTY_VALUE_MAX];
    char *status;
    int fd = -1;
    int i;
@@ -606,10 +723,6 @@ int fs_mgr_update_verity_state(fs_mgr_verity_state_callback callback)
    struct dm_ioctl *io = (struct dm_ioctl *) buffer;
    struct fstab *fstab = NULL;

    if (get_verity_state_location(state_loc, &offset) < 0) {
        goto out;
    }

    fd = TEMP_FAILURE_RETRY(open("/dev/device-mapper", O_RDWR | O_CLOEXEC));

    if (fd == -1) {
@@ -632,6 +745,11 @@ int fs_mgr_update_verity_state(fs_mgr_verity_state_callback callback)
            continue;
        }

        if (metadata_find(fstab->recs[i].verity_loc, VERITY_STATE_TAG,
                sizeof(struct verity_state), &offset) < 0) {
            continue;
        }

        mount_point = basename(fstab->recs[i].mount_point);
        verity_ioctl_init(io, mount_point, 0);

@@ -644,7 +762,8 @@ int fs_mgr_update_verity_state(fs_mgr_verity_state_callback callback)
        status = &buffer[io->data_start + sizeof(struct dm_target_spec)];

        if (*status == 'C') {
            rc = write_verity_state(state_loc, offset, VERITY_MODE_LOGGING);
            rc = write_verity_state(fstab->recs[i].verity_loc, offset,
                    VERITY_MODE_LOGGING);

            if (rc == -1) {
                goto out;
@@ -732,8 +851,12 @@ int fs_mgr_setup_verity(struct fstab_rec *fstab) {
        goto out;
    }

    if (fs_mgr_load_verity_state(&mode) < 0) {
        mode = VERITY_MODE_RESTART; /* default dm-verity mode */
    if (load_verity_state(fstab, &mode) < 0) {
        /* if accessing or updating the state failed, switch to the default
         * safe mode. This makes sure the device won't end up in an endless
         * restart loop, and no corrupted data will be exposed to userspace
         * without a warning. */
        mode = VERITY_MODE_EIO;
    }

    INFO("Enabling dm-verity for %s (mode %d)\n",  mount_point, mode);
+2 −1
Original line number Diff line number Diff line
@@ -36,7 +36,8 @@ enum verity_mode {
    VERITY_MODE_EIO = 0,
    VERITY_MODE_LOGGING = 1,
    VERITY_MODE_RESTART = 2,
    VERITY_MODE_LAST = VERITY_MODE_RESTART
    VERITY_MODE_LAST = VERITY_MODE_RESTART,
    VERITY_MODE_DEFAULT = VERITY_MODE_RESTART
};

/*