int LUKS_write_phdr(struct luks_phdr *hdr, struct crypt_device *ctx) { struct device *device = crypt_metadata_device(ctx); ssize_t hdr_size = sizeof(struct luks_phdr); int devfd = 0; unsigned int i; struct luks_phdr convHdr; int r; log_dbg(ctx, "Updating LUKS header of size %zu on device %s", sizeof(struct luks_phdr), device_path(device)); r = LUKS_check_device_size(ctx, hdr, 1); if (r) return r; devfd = device_open(ctx, device, O_RDWR); if (devfd < 0) { if (errno == EACCES) log_err(ctx, _("Cannot write to device %s, permission denied."), device_path(device)); else log_err(ctx, _("Cannot open device %s."), device_path(device)); return -EINVAL; } memcpy(&convHdr, hdr, hdr_size); memset(&convHdr._padding, 0, sizeof(convHdr._padding)); /* Convert every uint16/32_t item to network byte order */ convHdr.version = htons(hdr->version); convHdr.payloadOffset = htonl(hdr->payloadOffset); convHdr.keyBytes = htonl(hdr->keyBytes); convHdr.mkDigestIterations = htonl(hdr->mkDigestIterations); for(i = 0; i < LUKS_NUMKEYS; ++i) { convHdr.keyblock[i].active = htonl(hdr->keyblock[i].active); convHdr.keyblock[i].passwordIterations = htonl(hdr->keyblock[i].passwordIterations); convHdr.keyblock[i].keyMaterialOffset = htonl(hdr->keyblock[i].keyMaterialOffset); convHdr.keyblock[i].stripes = htonl(hdr->keyblock[i].stripes); } r = write_lseek_blockwise(devfd, device_block_size(ctx, device), device_alignment(device), &convHdr, hdr_size, 0) < hdr_size ? -EIO : 0; if (r) log_err(ctx, _("Error during update of LUKS header on device %s."), device_path(device)); device_sync(ctx, device); /* Re-read header from disk to be sure that in-memory and on-disk data are the same. */ if (!r) { r = LUKS_read_phdr(hdr, 1, 0, ctx); if (r) log_err(ctx, _("Error re-reading LUKS header after update on device %s."), device_path(device)); } return r; }
int LUKS_read_phdr(struct luks_phdr *hdr, int require_luks_device, int repair, struct crypt_device *ctx) { struct device *device = crypt_metadata_device(ctx); ssize_t hdr_size = sizeof(struct luks_phdr); int devfd = 0, r = 0; /* LUKS header starts at offset 0, first keyslot on LUKS_ALIGN_KEYSLOTS */ assert(sizeof(struct luks_phdr) <= LUKS_ALIGN_KEYSLOTS); /* Stripes count cannot be changed without additional code fixes yet */ assert(LUKS_STRIPES == 4000); if (repair && !require_luks_device) return -EINVAL; log_dbg("Reading LUKS header of size %zu from device %s", hdr_size, device_path(device)); devfd = device_open(device, O_RDONLY); if (devfd < 0) { log_err(ctx, _("Cannot open device %s."), device_path(device)); return -EINVAL; } if (read_blockwise(devfd, device_block_size(device), device_alignment(device), hdr, hdr_size) < hdr_size) r = -EIO; else r = _check_and_convert_hdr(device_path(device), hdr, require_luks_device, repair, ctx); if (!r) r = LUKS_check_device_size(ctx, hdr, 0); /* * Cryptsetup 1.0.0 did not align keyslots to 4k (very rare version). * Disable direct-io to avoid possible IO errors if underlying device * has bigger sector size. */ if (!r && hdr->keyblock[0].keyMaterialOffset * SECTOR_SIZE < LUKS_ALIGN_KEYSLOTS) { log_dbg("Old unaligned LUKS keyslot detected, disabling direct-io."); device_disable_direct_io(device); } close(devfd); return r; }
int LUKS2_hdr_version_unlocked(struct crypt_device *cd, const char *backup_file) { struct { char magic[LUKS2_MAGIC_L]; uint16_t version; } __attribute__ ((packed)) hdr; struct device *device = NULL; int r = 0, devfd = -1, flags; if (!backup_file) device = crypt_metadata_device(cd); else if (device_alloc(&device, backup_file) < 0) return 0; if (!device) return 0; flags = O_RDONLY; if (device_direct_io(device)) flags |= O_DIRECT; devfd = open(device_path(device), flags); if (devfd < 0) goto err; if ((read_lseek_blockwise(devfd, device_block_size(device), device_alignment(device), &hdr, sizeof(hdr), 0) == sizeof(hdr)) && !memcmp(hdr.magic, LUKS2_MAGIC_1ST, LUKS2_MAGIC_L)) r = (int)be16_to_cpu(hdr.version); err: if (devfd != -1) close(devfd); if (backup_file) device_free(device); return r; }
int LUKS_hdr_restore( const char *backup_file, struct luks_phdr *hdr, struct crypt_device *ctx) { struct device *device = crypt_metadata_device(ctx); int r = 0, devfd = -1, diff_uuid = 0; ssize_t buffer_size = 0; char *buffer = NULL, msg[200]; struct luks_phdr hdr_file; r = LUKS_read_phdr_backup(backup_file, &hdr_file, 0, ctx); if (r == -ENOENT) return r; if (!r) buffer_size = LUKS_device_sectors(&hdr_file) << SECTOR_SHIFT; if (r || buffer_size < LUKS_ALIGN_KEYSLOTS) { log_err(ctx, _("Backup file doesn't contain valid LUKS header.")); r = -EINVAL; goto out; } buffer = crypt_safe_alloc(buffer_size); if (!buffer) { r = -ENOMEM; goto out; } devfd = open(backup_file, O_RDONLY); if (devfd == -1) { log_err(ctx, _("Cannot open header backup file %s."), backup_file); r = -EINVAL; goto out; } if (read_buffer(devfd, buffer, buffer_size) < buffer_size) { log_err(ctx, _("Cannot read header backup file %s."), backup_file); r = -EIO; goto out; } close(devfd); devfd = -1; r = LUKS_read_phdr(hdr, 0, 0, ctx); if (r == 0) { log_dbg("Device %s already contains LUKS header, checking UUID and offset.", device_path(device)); if(hdr->payloadOffset != hdr_file.payloadOffset || hdr->keyBytes != hdr_file.keyBytes) { log_err(ctx, _("Data offset or key size differs on device and backup, restore failed.")); r = -EINVAL; goto out; } if (memcmp(hdr->uuid, hdr_file.uuid, UUID_STRING_L)) diff_uuid = 1; } if (snprintf(msg, sizeof(msg), _("Device %s %s%s"), device_path(device), r ? _("does not contain LUKS header. Replacing header can destroy data on that device.") : _("already contains LUKS header. Replacing header will destroy existing keyslots."), diff_uuid ? _("\nWARNING: real device header has different UUID than backup!") : "") < 0) { r = -ENOMEM; goto out; } if (!crypt_confirm(ctx, msg)) { r = -EINVAL; goto out; } log_dbg("Storing backup of header (%zu bytes) and keyslot area (%zu bytes) to device %s.", sizeof(*hdr), buffer_size - LUKS_ALIGN_KEYSLOTS, device_path(device)); devfd = device_open(device, O_RDWR); if (devfd < 0) { if (errno == EACCES) log_err(ctx, _("Cannot write to device %s, permission denied."), device_path(device)); else log_err(ctx, _("Cannot open device %s."), device_path(device)); r = -EINVAL; goto out; } if (write_blockwise(devfd, device_block_size(device), device_alignment(device), buffer, buffer_size) < buffer_size) { r = -EIO; goto out; } close(devfd); devfd = -1; /* Be sure to reload new data */ r = LUKS_read_phdr(hdr, 1, 0, ctx); out: if (devfd >= 0) close(devfd); crypt_safe_free(buffer); return r; }
int LUKS_hdr_backup(const char *backup_file, struct crypt_device *ctx) { struct device *device = crypt_metadata_device(ctx); struct luks_phdr hdr; int r = 0, devfd = -1; size_t hdr_size; size_t buffer_size; char *buffer = NULL; r = LUKS_read_phdr(&hdr, 1, 0, ctx); if (r) return r; hdr_size = LUKS_device_sectors(&hdr) << SECTOR_SHIFT; buffer_size = size_round_up(hdr_size, crypt_getpagesize()); buffer = crypt_safe_alloc(buffer_size); if (!buffer || hdr_size < LUKS_ALIGN_KEYSLOTS || hdr_size > buffer_size) { r = -ENOMEM; goto out; } log_dbg("Storing backup of header (%zu bytes) and keyslot area (%zu bytes).", sizeof(hdr), hdr_size - LUKS_ALIGN_KEYSLOTS); log_dbg("Output backup file size: %zu bytes.", buffer_size); devfd = device_open(device, O_RDONLY); if (devfd < 0) { log_err(ctx, _("Device %s is not a valid LUKS device."), device_path(device)); r = -EINVAL; goto out; } if (read_blockwise(devfd, device_block_size(device), device_alignment(device), buffer, hdr_size) < (ssize_t)hdr_size) { r = -EIO; goto out; } close(devfd); /* Wipe unused area, so backup cannot contain old signatures */ if (hdr.keyblock[0].keyMaterialOffset * SECTOR_SIZE == LUKS_ALIGN_KEYSLOTS) memset(buffer + sizeof(hdr), 0, LUKS_ALIGN_KEYSLOTS - sizeof(hdr)); devfd = open(backup_file, O_CREAT|O_EXCL|O_WRONLY, S_IRUSR); if (devfd == -1) { if (errno == EEXIST) log_err(ctx, _("Requested header backup file %s already exists."), backup_file); else log_err(ctx, _("Cannot create header backup file %s."), backup_file); r = -EINVAL; goto out; } if (write_buffer(devfd, buffer, buffer_size) < (ssize_t)buffer_size) { log_err(ctx, _("Cannot write header backup file %s."), backup_file); r = -EIO; goto out; } r = 0; out: if (devfd >= 0) close(devfd); crypt_memzero(&hdr, sizeof(hdr)); crypt_safe_free(buffer); return r; }
/* * Write LUKS2 header to disk at specific offset. */ static int hdr_write_disk(struct device *device, struct luks2_hdr *hdr, const char *json_area, int secondary) { struct luks2_hdr_disk hdr_disk; uint64_t offset = secondary ? hdr->hdr_size : 0; size_t hdr_json_len; int devfd = -1, r; log_dbg("Trying to write LUKS2 header (%zu bytes) at offset %" PRIu64 ".", hdr->hdr_size, offset); /* FIXME: read-only device silent fail? */ devfd = device_open_locked(device, O_RDWR); if (devfd < 0) return devfd == -1 ? -EINVAL : devfd; hdr_json_len = hdr->hdr_size - LUKS2_HDR_BIN_LEN; hdr_to_disk(hdr, &hdr_disk, secondary, offset); /* * Write header without checksum but with proper seqid. */ if (write_lseek_blockwise(devfd, device_block_size(device), device_alignment(device), (char *)&hdr_disk, LUKS2_HDR_BIN_LEN, offset) < (ssize_t)LUKS2_HDR_BIN_LEN) { close(devfd); return -EIO; } /* * Write json area. */ if (write_lseek_blockwise(devfd, device_block_size(device), device_alignment(device), CONST_CAST(char*)json_area, hdr_json_len, LUKS2_HDR_BIN_LEN + offset) < (ssize_t)hdr_json_len) { close(devfd); return -EIO; } /* * Calculate checksum and write header with checksum. */ r = hdr_checksum_calculate(hdr_disk.checksum_alg, &hdr_disk, json_area, hdr_json_len); if (r < 0) { close(devfd); return r; } log_dbg_checksum(hdr_disk.csum, hdr_disk.checksum_alg, "in-memory"); if (write_lseek_blockwise(devfd, device_block_size(device), device_alignment(device), (char *)&hdr_disk, LUKS2_HDR_BIN_LEN, offset) < (ssize_t)LUKS2_HDR_BIN_LEN) r = -EIO; close(devfd); return r; }
/* * Read LUKS2 header from disk at specific offset. */ static int hdr_read_disk(struct device *device, struct luks2_hdr_disk *hdr_disk, char **json_area, uint64_t offset, int secondary) { size_t hdr_json_size = 0; int devfd = -1, r; log_dbg("Trying to read %s LUKS2 header at offset %" PRIu64 ".", secondary ? "secondary" : "primary", offset); devfd = device_open_locked(device, O_RDONLY); if (devfd < 0) return devfd == -1 ? -EIO : devfd; /* * Read binary header and run sanity check before reading * JSON area and validating checksum. */ if (read_lseek_blockwise(devfd, device_block_size(device), device_alignment(device), hdr_disk, LUKS2_HDR_BIN_LEN, offset) != LUKS2_HDR_BIN_LEN) { close(devfd); return -EIO; } r = hdr_disk_sanity_check_pre(hdr_disk, &hdr_json_size, secondary, offset); if (r < 0) { close(devfd); return r; } /* * Allocate and read JSON area. Always the whole area must be read. */ *json_area = malloc(hdr_json_size); if (!*json_area) { close(devfd); return -ENOMEM; } if (read_lseek_blockwise(devfd, device_block_size(device), device_alignment(device), *json_area, hdr_json_size, offset + LUKS2_HDR_BIN_LEN) != (ssize_t)hdr_json_size) { close(devfd); free(*json_area); *json_area = NULL; return -EIO; } close(devfd); /* * Calculate and validate checksum and zero it afterwards. */ if (hdr_checksum_check(hdr_disk->checksum_alg, hdr_disk, *json_area, hdr_json_size)) { log_dbg("LUKS2 header checksum error (offset %" PRIu64 ").", offset); r = -EINVAL; } memset(hdr_disk->csum, 0, LUKS2_CHECKSUM_L); return r; }