Blame libluksmeta.c

Packit da863b
/* vim: set tabstop=8 shiftwidth=4 softtabstop=4 expandtab smarttab colorcolumn=80: */
Packit da863b
/*
Packit da863b
 * Copyright (c) 2016 Red Hat, Inc.
Packit da863b
 * Author: Nathaniel McCallum <npmccallum@redhat.com>
Packit da863b
 *
Packit da863b
 * This program is free software: you can redistribute it and/or modify it
Packit da863b
 * under the terms of the GNU Lesser General Public License as published by
Packit da863b
 * the Free Software Foundation, either version 2.1 of the License, or
Packit da863b
 * (at your option) any later version.
Packit da863b
 *
Packit da863b
 * This program is distributed in the hope that it will be useful,
Packit da863b
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit da863b
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit da863b
 * GNU Lesser General Public License for more details.
Packit da863b
 *
Packit da863b
 * You should have received a copy of the GNU Lesser General Public License
Packit da863b
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
Packit da863b
 */
Packit da863b
Packit da863b
#include "crc32c.h"
Packit da863b
#include "luksmeta.h"
Packit da863b
Packit da863b
#include <sys/types.h>
Packit da863b
#include <sys/stat.h>
Packit da863b
#include <errno.h>
Packit da863b
#include <fcntl.h>
Packit da863b
#include <stdbool.h>
Packit da863b
#include <stdlib.h>
Packit da863b
#include <string.h>
Packit da863b
#include <unistd.h>
Packit da863b
Packit da863b
#define ALIGN(s, up) (((s) + (up ? 4095 : 0)) & ~4095ULL)
Packit da863b
#define LUKS_NSLOTS 8
Packit da863b
#define LM_VERSION 1
Packit da863b
Packit da863b
static const uint8_t LM_MAGIC[] = { 'L', 'U', 'K', 'S', 'M', 'E', 'T', 'A' };
Packit da863b
Packit da863b
typedef struct __attribute__((packed)) {
Packit da863b
    luksmeta_uuid_t uuid;
Packit da863b
    uint32_t offset;   /* Bytes from the start of the hole */
Packit da863b
    uint32_t length;   /* Bytes */
Packit da863b
    uint32_t crc32c;
Packit da863b
    uint32_t _reserved; /* Reserved */
Packit da863b
} lm_slot_t;
Packit da863b
Packit da863b
typedef struct __attribute__((packed)) {
Packit da863b
    uint8_t magic[sizeof(LM_MAGIC)];
Packit da863b
    uint32_t version;
Packit da863b
    uint32_t crc32c;
Packit da863b
    lm_slot_t slots[LUKS_NSLOTS];
Packit da863b
} lm_t;
Packit da863b
Packit da863b
static bool
Packit da863b
uuid_is_zero(const luksmeta_uuid_t uuid)
Packit da863b
{
Packit da863b
    for (size_t i = 0; i < sizeof(luksmeta_uuid_t); i++) {
Packit da863b
        if (uuid[i] != 0)
Packit da863b
            return false;
Packit da863b
    }
Packit da863b
Packit da863b
    return true;
Packit da863b
}
Packit da863b
Packit da863b
static inline uint32_t
Packit da863b
checksum(lm_t lm)
Packit da863b
{
Packit da863b
    lm.crc32c = 0;
Packit da863b
    return crc32c(0, &lm, sizeof(lm_t));
Packit da863b
}
Packit da863b
Packit da863b
static inline bool
Packit da863b
overlap(const lm_t *lm, uint32_t start, size_t end)
Packit da863b
{
Packit da863b
    for (int i = 0; i < LUKS_NSLOTS; i++) {
Packit da863b
        const lm_slot_t *s = &lm->slots[i];
Packit da863b
        uint32_t e = s->offset + s->length;
Packit da863b
Packit da863b
        if (start <= s->offset && s->offset < end)
Packit da863b
            return true;
Packit da863b
Packit da863b
        if (start < e && e <= end)
Packit da863b
            return true;
Packit da863b
    }
Packit da863b
Packit da863b
    return false;
Packit da863b
}
Packit da863b
Packit da863b
static inline uint32_t
Packit da863b
find_gap(const lm_t *lm, uint32_t length, size_t size)
Packit da863b
{
Packit da863b
    size = ALIGN(size, true);
Packit da863b
Packit da863b
    for (uint32_t off = ALIGN(1, true); off < length; off += ALIGN(1, true)) {
Packit da863b
        if (!overlap(lm, off, off + size))
Packit da863b
            return off;
Packit da863b
    }
Packit da863b
Packit da863b
    return 0;
Packit da863b
}
Packit da863b
Packit da863b
static int
Packit da863b
find_unused_slot(struct crypt_device *cd, const lm_t *lm)
Packit da863b
{
Packit da863b
    for (int slot = 0; slot < LUKS_NSLOTS; slot++) {
Packit da863b
        if (crypt_keyslot_status(cd, slot) == CRYPT_SLOT_INACTIVE &&
Packit da863b
            uuid_is_zero(lm->slots[slot].uuid))
Packit da863b
            return slot;
Packit da863b
    }
Packit da863b
Packit da863b
    return -1;
Packit da863b
}
Packit da863b
Packit da863b
static inline ssize_t
Packit da863b
readall(int fd, void *data, size_t size)
Packit da863b
{
Packit da863b
    uint8_t *tmp = data;
Packit da863b
Packit da863b
    for (ssize_t r, t = 0; t < (ssize_t) size; t += r) {
Packit da863b
        r = read(fd, &tmp[t], size - t);
Packit da863b
        if (r < 0 && errno != EAGAIN)
Packit da863b
            return -errno;
Packit da863b
        if (r == 0)
Packit da863b
            return -ENOENT;
Packit da863b
    }
Packit da863b
Packit da863b
    return size;
Packit da863b
}
Packit da863b
Packit da863b
static inline ssize_t
Packit da863b
writeall(int fd, const void *buf, size_t size)
Packit da863b
{
Packit da863b
    const uint8_t *tmp = buf;
Packit da863b
Packit da863b
    for (ssize_t r, t = 0; t < (ssize_t) size; t += r) {
Packit da863b
        r = write(fd, &tmp[t], size - t);
Packit da863b
        if (r < 0) {
Packit da863b
            if (errno != EAGAIN)
Packit da863b
                return -errno;
Packit da863b
            r = 0;
Packit da863b
        }
Packit da863b
    }
Packit da863b
Packit da863b
    return size;
Packit da863b
}
Packit da863b
Packit da863b
/**
Packit da863b
 * Opens the device with the specified flags.
Packit da863b
 *
Packit da863b
 * The length parameter is set to the amount of space in the gap between the
Packit da863b
 * end of the last slot and the start of the encrypted data.
Packit da863b
 *
Packit da863b
 * The function returns either the file descriptor positioned to the start of
Packit da863b
 * the hole or a negative errno.
Packit da863b
 */
Packit da863b
static int
Packit da863b
open_hole(struct crypt_device *cd, int flags, uint32_t *length)
Packit da863b
{
Packit da863b
    const char *name = NULL;
Packit da863b
    const char *type = NULL;
Packit da863b
    uint64_t hole = 0;
Packit da863b
    uint64_t data = 0;
Packit da863b
    int fd = 0;
Packit da863b
    int r = 0;
Packit da863b
Packit da863b
    type = crypt_get_type(cd);
Packit da863b
    if (!type || strcmp(CRYPT_LUKS1, type) != 0)
Packit da863b
        return -ENOTSUP;
Packit da863b
Packit da863b
    data = crypt_get_data_offset(cd) * 512;
Packit da863b
    if (data < 4096)
Packit da863b
        return -ENOSPC;
Packit da863b
Packit da863b
    for (int slot = 0; slot < LUKS_NSLOTS; slot++) {
Packit da863b
        uint64_t off = 0;
Packit da863b
        uint64_t len = 0;
Packit da863b
Packit da863b
        r = crypt_keyslot_area(cd, slot, &off, &len;;
Packit da863b
        if (r < 0)
Packit da863b
            return r;
Packit da863b
Packit da863b
        if (hole < off + len)
Packit da863b
            hole = ALIGN(off + len, true);
Packit da863b
    }
Packit da863b
Packit da863b
    if (hole == 0)
Packit da863b
        return -ENOTSUP;
Packit da863b
Packit da863b
    if (hole >= data)
Packit da863b
        return -ENOSPC;
Packit da863b
Packit da863b
    name = crypt_get_device_name(cd);
Packit da863b
    if (!name)
Packit da863b
        return -ENOTSUP;
Packit da863b
Packit da863b
    fd = open(name, flags);
Packit da863b
    if (fd < 0)
Packit da863b
        return -errno;
Packit da863b
Packit da863b
    if (lseek(fd, hole, SEEK_SET) == -1) {
Packit da863b
        close(fd);
Packit da863b
        return -errno;
Packit da863b
    }
Packit da863b
Packit da863b
    *length = ALIGN(data - hole, false);
Packit da863b
    return fd;
Packit da863b
}
Packit da863b
Packit da863b
static int
Packit da863b
read_header(struct crypt_device *cd, int flags, uint32_t *length, lm_t *lm)
Packit da863b
{
Packit da863b
    uint32_t maxlen;
Packit da863b
    int fd = -1;
Packit da863b
    int r = 0;
Packit da863b
Packit da863b
    fd = open_hole(cd, flags, length);
Packit da863b
    if (fd < 0)
Packit da863b
        return fd;
Packit da863b
Packit da863b
    r = *length >= sizeof(lm_t) ? 0 : -ENOENT;
Packit da863b
    if (r < 0)
Packit da863b
        goto error;
Packit da863b
Packit da863b
    r = readall(fd, lm, sizeof(lm_t));
Packit da863b
    if (r < 0)
Packit da863b
        goto error;
Packit da863b
Packit da863b
    r = memcmp(LM_MAGIC, lm->magic, sizeof(LM_MAGIC)) == 0 ? 0 : -ENOENT;
Packit da863b
    if (r < 0)
Packit da863b
        goto error;
Packit da863b
Packit da863b
    r = lm->version == htobe32(LM_VERSION) ? 0 : -ENOTSUP;
Packit da863b
    if (r < 0)
Packit da863b
        goto error;
Packit da863b
Packit da863b
    lm->crc32c = be32toh(lm->crc32c);
Packit da863b
    r = checksum(*lm) == lm->crc32c ? 0 : -EINVAL;
Packit da863b
    if (r < 0)
Packit da863b
        goto error;
Packit da863b
Packit da863b
    lm->version = be32toh(lm->version);
Packit da863b
Packit da863b
    maxlen = *length - ALIGN(sizeof(lm_t), true);
Packit da863b
    for (int slot = 0; slot < LUKS_NSLOTS; slot++) {
Packit da863b
        lm_slot_t *s = &lm->slots[slot];
Packit da863b
Packit da863b
        s->offset = be32toh(s->offset);
Packit da863b
        s->length = be32toh(s->length);
Packit da863b
        s->crc32c = be32toh(s->crc32c);
Packit da863b
Packit da863b
        if (!uuid_is_zero(s->uuid)) {
Packit da863b
            r = s->offset > sizeof(lm_t) ? 0 : -EINVAL;
Packit da863b
            if (r < 0)
Packit da863b
                goto error;
Packit da863b
Packit da863b
            r = s->length <= maxlen ? 0 : -EINVAL;
Packit da863b
            if (r < 0)
Packit da863b
                goto error;
Packit da863b
        }
Packit da863b
    }
Packit da863b
Packit da863b
    return fd;
Packit da863b
Packit da863b
error:
Packit da863b
    close(fd);
Packit da863b
    return r;
Packit da863b
}
Packit da863b
Packit da863b
static int
Packit da863b
write_header(int fd, lm_t lm)
Packit da863b
{
Packit da863b
    for (int slot = 0; slot < LUKS_NSLOTS; slot++) {
Packit da863b
        lm.slots[slot].offset = htobe32(lm.slots[slot].offset);
Packit da863b
        lm.slots[slot].length = htobe32(lm.slots[slot].length);
Packit da863b
        lm.slots[slot].crc32c = htobe32(lm.slots[slot].crc32c);
Packit da863b
    }
Packit da863b
Packit da863b
    memcpy(lm.magic, LM_MAGIC, sizeof(LM_MAGIC));
Packit da863b
    lm.version = htobe32(LM_VERSION);
Packit da863b
    lm.crc32c = htobe32(checksum(lm));
Packit da863b
    return writeall(fd, &lm, sizeof(lm));
Packit da863b
}
Packit da863b
Packit da863b
int
Packit da863b
luksmeta_test(struct crypt_device *cd)
Packit da863b
{
Packit da863b
    int fd = -1;
Packit da863b
Packit da863b
    fd = read_header(cd, O_RDONLY, &(uint32_t) {0}, &(lm_t) {});
Packit da863b
    if (fd >= 0) {
Packit da863b
        close(fd);
Packit da863b
        return 0;
Packit da863b
    }
Packit da863b
Packit da863b
    return fd;
Packit da863b
}
Packit da863b
Packit da863b
int
Packit da863b
luksmeta_nuke(struct crypt_device *cd)
Packit da863b
{
Packit da863b
    uint8_t zero[ALIGN(1, true)] = {};
Packit da863b
    uint32_t length = 0;
Packit da863b
    int fd = -1;
Packit da863b
    int r = 0;
Packit da863b
Packit da863b
    fd = open_hole(cd, O_RDWR | O_SYNC, &length);
Packit da863b
    if (fd < 0)
Packit da863b
        return fd;
Packit da863b
Packit da863b
    for (size_t i = 0; r >= 0 && i < length; i += sizeof(zero))
Packit da863b
        r = writeall(fd, zero, sizeof(zero));
Packit da863b
Packit da863b
    close(fd);
Packit da863b
    return r < 0 ? r : 0;
Packit da863b
}
Packit da863b
Packit da863b
int
Packit da863b
luksmeta_init(struct crypt_device *cd)
Packit da863b
{
Packit da863b
    uint32_t length = 0;
Packit da863b
    int fd = -1;
Packit da863b
    int r = 0;
Packit da863b
Packit da863b
    r = luksmeta_test(cd);
Packit da863b
    if (r == 0)
Packit da863b
        return -EALREADY;
Packit da863b
    else if (r != -ENOENT && r != -EINVAL)
Packit da863b
        return r;
Packit da863b
Packit da863b
    fd = open_hole(cd, O_RDWR | O_SYNC, &length);
Packit da863b
    if (fd < 0)
Packit da863b
        return fd;
Packit da863b
Packit da863b
    if (length < ALIGN(sizeof(lm_t), true)) {
Packit da863b
        close(fd);
Packit da863b
        return -ENOSPC;
Packit da863b
    }
Packit da863b
Packit da863b
    r = write_header(fd, (lm_t) {});
Packit da863b
    close(fd);
Packit da863b
    return r > 0 ? 0 : r;
Packit da863b
}
Packit da863b
Packit da863b
int
Packit da863b
luksmeta_load(struct crypt_device *cd, int slot,
Packit da863b
              luksmeta_uuid_t uuid, void *buf, size_t size)
Packit da863b
{
Packit da863b
    uint32_t length = 0;
Packit da863b
    lm_slot_t *s = NULL;
Packit da863b
    lm_t lm = {};
Packit da863b
    int fd = -1;
Packit da863b
    int r = 0;
Packit da863b
Packit da863b
    if (slot < 0 || slot >= LUKS_NSLOTS)
Packit da863b
        return -EBADSLT;
Packit da863b
    s = &lm.slots[slot];
Packit da863b
Packit da863b
    fd = read_header(cd, O_RDONLY, &length, &lm);
Packit da863b
    if (fd < 0)
Packit da863b
        return fd;
Packit da863b
Packit da863b
    r = uuid_is_zero(s->uuid) ? -ENODATA : 0;
Packit da863b
    if (r < 0)
Packit da863b
        goto error;
Packit da863b
Packit da863b
    if (buf) {
Packit da863b
        r = size >= s->length ? 0 : -E2BIG;
Packit da863b
        if (r < 0)
Packit da863b
            goto error;
Packit da863b
Packit da863b
        r = lseek(fd, s->offset - sizeof(lm), SEEK_CUR) == -1 ? -errno : 0;
Packit da863b
        if (r < 0)
Packit da863b
            goto error;
Packit da863b
Packit da863b
        r = readall(fd, buf, s->length);
Packit da863b
        if (r < 0)
Packit da863b
            goto error;
Packit da863b
Packit da863b
        r = crc32c(0, buf, s->length) == s->crc32c ? 0 : -EINVAL;
Packit da863b
        if (r < 0)
Packit da863b
            goto error;
Packit da863b
    }
Packit da863b
Packit da863b
    memcpy(uuid, s->uuid, sizeof(luksmeta_uuid_t));
Packit da863b
    close(fd);
Packit da863b
    return s->length;
Packit da863b
Packit da863b
error:
Packit da863b
    close(fd);
Packit da863b
    return r;
Packit da863b
}
Packit da863b
Packit da863b
int
Packit da863b
luksmeta_save(struct crypt_device *cd, int slot,
Packit da863b
              const luksmeta_uuid_t uuid, const void *buf, size_t size)
Packit da863b
{
Packit da863b
    uint32_t length = 0;
Packit da863b
    lm_slot_t *s = NULL;
Packit da863b
    lm_t lm = {};
Packit da863b
    int fd = -1;
Packit da863b
    int r = 0;
Packit da863b
    off_t off;
Packit da863b
Packit da863b
    if (uuid_is_zero(uuid))
Packit da863b
        return -EKEYREJECTED;
Packit da863b
Packit da863b
    fd = read_header(cd, O_RDWR | O_SYNC, &length, &lm);
Packit da863b
    if (fd < 0)
Packit da863b
        return fd;
Packit da863b
Packit da863b
    if (slot == CRYPT_ANY_SLOT)
Packit da863b
        slot = find_unused_slot(cd, &lm);
Packit da863b
Packit da863b
    r = slot >= 0 && slot < LUKS_NSLOTS ? 0 : -EBADSLT;
Packit da863b
    if (r < 0)
Packit da863b
        goto error;
Packit da863b
    s = &lm.slots[slot];
Packit da863b
Packit da863b
    r = uuid_is_zero(s->uuid) ? 0 : -EALREADY;
Packit da863b
    if (r < 0)
Packit da863b
        goto error;
Packit da863b
Packit da863b
    s->offset = find_gap(&lm, length, size);
Packit da863b
    r = s->offset >= ALIGN(sizeof(lm), true) ? 0 : -ENOSPC;
Packit da863b
    if (r < 0)
Packit da863b
        goto error;
Packit da863b
Packit da863b
    memcpy(s->uuid, uuid, sizeof(luksmeta_uuid_t));
Packit da863b
    s->length = size;
Packit da863b
    s->crc32c = crc32c(0, buf, size);
Packit da863b
Packit da863b
    off = s->offset - sizeof(lm);
Packit da863b
    r = lseek(fd, off, SEEK_CUR) == -1 ? -errno : 0;
Packit da863b
    if (r < 0)
Packit da863b
        goto error;
Packit da863b
Packit da863b
    r = writeall(fd, buf, size);
Packit da863b
    if (r < 0)
Packit da863b
        goto error;
Packit da863b
Packit da863b
    off = s->offset + s->length;
Packit da863b
    r = lseek(fd, -off, SEEK_CUR) == -1 ? -errno : 0;
Packit da863b
    if (r < 0)
Packit da863b
        goto error;
Packit da863b
Packit da863b
    r = write_header(fd, lm);
Packit da863b
Packit da863b
error:
Packit da863b
    close(fd);
Packit da863b
    return r < 0 ? r : slot;
Packit da863b
}
Packit da863b
Packit da863b
int
Packit da863b
luksmeta_wipe(struct crypt_device *cd, int slot, const luksmeta_uuid_t uuid)
Packit da863b
{
Packit da863b
    uint8_t *zero = NULL;
Packit da863b
    uint32_t length = 0;
Packit da863b
    lm_slot_t *s = NULL;
Packit da863b
    lm_t lm = {};
Packit da863b
    int fd = -1;
Packit da863b
    int r = 0;
Packit da863b
    off_t off;
Packit da863b
Packit da863b
    if (slot < 0 || slot >= LUKS_NSLOTS)
Packit da863b
        return -EBADSLT;
Packit da863b
    s = &lm.slots[slot];
Packit da863b
Packit da863b
    fd = read_header(cd, O_RDWR | O_SYNC, &length, &lm);
Packit da863b
    if (fd < 0)
Packit da863b
        return fd;
Packit da863b
Packit da863b
    r = uuid_is_zero(s->uuid) ? -EALREADY : 0;
Packit da863b
    if (r < 0)
Packit da863b
        goto error;
Packit da863b
Packit da863b
    if (uuid && memcmp(uuid, s->uuid, sizeof(luksmeta_uuid_t)) != 0) {
Packit da863b
        r = -EKEYREJECTED;
Packit da863b
        goto error;
Packit da863b
    }
Packit da863b
Packit da863b
    off = s->offset - sizeof(lm_t);
Packit da863b
    r = lseek(fd, off, SEEK_CUR) == -1 ? -errno : 0;
Packit da863b
    if (r < 0)
Packit da863b
        goto error;
Packit da863b
Packit da863b
    r = (zero = calloc(1, s->length)) ? 0 : -errno;
Packit da863b
    if (r < 0)
Packit da863b
        goto error;
Packit da863b
Packit da863b
    r = writeall(fd, zero, s->length);
Packit da863b
    free(zero);
Packit da863b
    if (r < 0)
Packit da863b
        goto error;
Packit da863b
Packit da863b
    off = s->offset + s->length;
Packit da863b
    r = lseek(fd, -off, SEEK_CUR) == -1 ? -errno : 0;
Packit da863b
    if (r < 0)
Packit da863b
        goto error;
Packit da863b
Packit da863b
    memset(s, 0, sizeof(lm_slot_t));
Packit da863b
    r = write_header(fd, lm);
Packit da863b
Packit da863b
error:
Packit da863b
    close(fd);
Packit da863b
    return r < 0 ? r : 0;
Packit da863b
}