static int test_read_lseek_blockwise(void) { void *buffer = NULL; int fd = -1; ssize_t ret = -EINVAL; //printf("Entering test_read_lseek_blockwise\n"); if (posix_memalign(&buffer, test_mem_alignment, test_length)) { fprintf(stderr, "Failed to allocate aligned buffer.\n"); goto out; } fd = open(test_file, O_RDONLY | O_DIRECT); if (fd < 0) { fprintf(stderr, "Failed to open %s.\n", test_file); goto out; } ret = read_lseek_blockwise(fd, test_bsize, test_mem_alignment, buffer, test_length, test_offset); if (ret < 0) goto out; ret = (size_t) ret == test_length ? 0 : -EIO; out: if (fd >= 0) close(fd); free(buffer); return ret; }
int LUKS_read_phdr(struct luks_phdr *hdr, int require_luks_device, int repair, struct crypt_device *ctx) { int devfd, r = 0; struct device *device = crypt_metadata_device(ctx); ssize_t hdr_size = sizeof(struct luks_phdr); /* 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(ctx, "Reading LUKS header of size %zu from device %s", hdr_size, device_path(device)); devfd = device_open(ctx, device, O_RDONLY); if (devfd < 0) { log_err(ctx, _("Cannot open device %s."), device_path(device)); return -EINVAL; } if (read_lseek_blockwise(devfd, device_block_size(ctx, device), device_alignment(device), hdr, hdr_size, 0) < 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(ctx, "Old unaligned LUKS keyslot detected, disabling direct-io."); device_disable_direct_io(device); } 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_backup(const char *backup_file, struct crypt_device *ctx) { struct device *device = crypt_metadata_device(ctx); struct luks_phdr hdr; int fd, devfd, r = 0; size_t hdr_size; size_t buffer_size; ssize_t ret; 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(ctx, "Storing backup of header (%zu bytes) and keyslot area (%zu bytes).", sizeof(hdr), hdr_size - LUKS_ALIGN_KEYSLOTS); log_dbg(ctx, "Output backup file size: %zu bytes.", buffer_size); devfd = device_open(ctx, 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_lseek_blockwise(devfd, device_block_size(ctx, device), device_alignment(device), buffer, hdr_size, 0) < (ssize_t)hdr_size) { r = -EIO; goto out; } /* 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)); fd = open(backup_file, O_CREAT|O_EXCL|O_WRONLY, S_IRUSR); if (fd == -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; } ret = write_buffer(fd, buffer, buffer_size); close(fd); if (ret < (ssize_t)buffer_size) { log_err(ctx, _("Cannot write header backup file %s."), backup_file); r = -EIO; goto out; } r = 0; out: crypt_memzero(&hdr, sizeof(hdr)); crypt_safe_free(buffer); 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; }