X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=lib%2Futils.c;h=7e850a61419f56fb1bf75508bcd83b3418f73479;hb=690c363eb90277fc53a97e3e2367ebe12055937e;hp=25f370f423af520b39aec8a8a1887acbbafbc8ff;hpb=ee8425b8365532217c5aa132a5d80eae1d3881df;p=platform%2Fupstream%2Fcryptsetup.git diff --git a/lib/utils.c b/lib/utils.c index 25f370f..7e850a6 100644 --- a/lib/utils.c +++ b/lib/utils.c @@ -1,13 +1,15 @@ /* * utils - miscellaneous device utilities for cryptsetup * - * Copyright (C) 2004, Christophe Saout - * Copyright (C) 2004-2007, Clemens Fruhwirth - * Copyright (C) 2009-2012, Red Hat, Inc. All rights reserved. + * Copyright (C) 2004 Jana Saout + * Copyright (C) 2004-2007 Clemens Fruhwirth + * Copyright (C) 2009-2021 Red Hat, Inc. All rights reserved. + * Copyright (C) 2009-2021 Milan Broz * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License - * version 2 as published by the Free Software Foundation. + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -20,513 +22,324 @@ */ #include -#include -#include -#include -#include #include -#include -#include -#include -#include -#include -#include -#include #include #include +#include +#include -#include "libcryptsetup.h" #include "internal.h" -static int get_alignment(int fd) +size_t crypt_getpagesize(void) { - int alignment = DEFAULT_MEM_ALIGNMENT; - -#ifdef _PC_REC_XFER_ALIGN - alignment = fpathconf(fd, _PC_REC_XFER_ALIGN); - if (alignment < 0) - alignment = DEFAULT_MEM_ALIGNMENT; -#endif - return alignment; + long r = sysconf(_SC_PAGESIZE); + return r <= 0 ? DEFAULT_MEM_ALIGNMENT : (size_t)r; } -static void *aligned_malloc(void **base, int size, int alignment) +unsigned crypt_cpusonline(void) { -#ifdef HAVE_POSIX_MEMALIGN - return posix_memalign(base, alignment, size) ? NULL : *base; -#else -/* Credits go to Michal's padlock patches for this alignment code */ - char *ptr; - - ptr = malloc(size + alignment); - if(ptr == NULL) return NULL; - - *base = ptr; - if(alignment > 1 && ((long)ptr & (alignment - 1))) { - ptr += alignment - ((long)(ptr) & (alignment - 1)); - } - return ptr; -#endif + long r = sysconf(_SC_NPROCESSORS_ONLN); + return r < 0 ? 1 : r; } -int device_read_ahead(const char *dev, uint32_t *read_ahead) +uint64_t crypt_getphysmemory_kb(void) { - int fd, r = 0; - long read_ahead_long; + long pagesize, phys_pages; + uint64_t phys_memory_kb; - if ((fd = open(dev, O_RDONLY)) < 0) - return 0; + pagesize = sysconf(_SC_PAGESIZE); + phys_pages = sysconf(_SC_PHYS_PAGES); - r = ioctl(fd, BLKRAGET, &read_ahead_long) ? 0 : 1; - close(fd); + if (pagesize < 0 || phys_pages < 0) + return 0; - if (r) - *read_ahead = (uint32_t) read_ahead_long; + phys_memory_kb = pagesize / 1024; + phys_memory_kb *= phys_pages; - return r; + return phys_memory_kb; } -static int sector_size(int fd) -{ - int bsize; - if (ioctl(fd,BLKSSZGET, &bsize) < 0) - return -EINVAL; - else - return bsize; -} +/* MEMLOCK */ +#define DEFAULT_PROCESS_PRIORITY -18 -int sector_size_for_device(const char *device) -{ - int fd = open(device, O_RDONLY); - int r; - if(fd < 0) - return -EINVAL; - r = sector_size(fd); - close(fd); - return r; -} +static int _priority; +static int _memlock_count = 0; -ssize_t write_blockwise(int fd, void *orig_buf, size_t count) +// return 1 if memory is locked +int crypt_memlock_inc(struct crypt_device *ctx) { - void *hangover_buf, *hangover_buf_base = NULL; - void *buf, *buf_base = NULL; - int r, hangover, solid, bsize, alignment; - ssize_t ret = -1; - - if ((bsize = sector_size(fd)) < 0) - return bsize; - - hangover = count % bsize; - solid = count - hangover; - alignment = get_alignment(fd); - - if ((long)orig_buf & (alignment - 1)) { - buf = aligned_malloc(&buf_base, count, alignment); - if (!buf) - goto out; - memcpy(buf, orig_buf, count); - } else - buf = orig_buf; - - r = write(fd, buf, solid); - if (r < 0 || r != solid) - goto out; - - if (hangover) { - hangover_buf = aligned_malloc(&hangover_buf_base, bsize, alignment); - if (!hangover_buf) - goto out; - - r = read(fd, hangover_buf, bsize); - if (r < 0 || r != bsize) - goto out; - - r = lseek(fd, -bsize, SEEK_CUR); - if (r < 0) - goto out; - memcpy(hangover_buf, (char*)buf + solid, hangover); - - r = write(fd, hangover_buf, bsize); - if (r < 0 || r != bsize) - goto out; + if (!_memlock_count++) { + log_dbg(ctx, "Locking memory."); + if (mlockall(MCL_CURRENT | MCL_FUTURE) == -1) { + log_dbg(ctx, "Cannot lock memory with mlockall."); + _memlock_count--; + return 0; + } + errno = 0; + if (((_priority = getpriority(PRIO_PROCESS, 0)) == -1) && errno) + log_err(ctx, _("Cannot get process priority.")); + else + if (setpriority(PRIO_PROCESS, 0, DEFAULT_PROCESS_PRIORITY)) + log_dbg(ctx, "setpriority %d failed: %s", + DEFAULT_PROCESS_PRIORITY, strerror(errno)); } - ret = count; -out: - free(hangover_buf_base); - if (buf != orig_buf) - free(buf_base); - return ret; + return _memlock_count ? 1 : 0; } -ssize_t read_blockwise(int fd, void *orig_buf, size_t count) { - void *hangover_buf, *hangover_buf_base = NULL; - void *buf, *buf_base = NULL; - int r, hangover, solid, bsize, alignment; - ssize_t ret = -1; - - if ((bsize = sector_size(fd)) < 0) - return bsize; - - hangover = count % bsize; - solid = count - hangover; - alignment = get_alignment(fd); - - if ((long)orig_buf & (alignment - 1)) { - buf = aligned_malloc(&buf_base, count, alignment); - if (!buf) - return -1; - } else - buf = orig_buf; - - r = read(fd, buf, solid); - if(r < 0 || r != solid) - goto out; - - if (hangover) { - hangover_buf = aligned_malloc(&hangover_buf_base, bsize, alignment); - if (!hangover_buf) - goto out; - r = read(fd, hangover_buf, bsize); - if (r < 0 || r != bsize) - goto out; - - memcpy((char *)buf + solid, hangover_buf, hangover); - } - ret = count; -out: - free(hangover_buf_base); - if (buf != orig_buf) { - memcpy(orig_buf, buf, count); - free(buf_base); +int crypt_memlock_dec(struct crypt_device *ctx) +{ + if (_memlock_count && (!--_memlock_count)) { + log_dbg(ctx, "Unlocking memory."); + if (munlockall() == -1) + log_err(ctx, _("Cannot unlock memory.")); + if (setpriority(PRIO_PROCESS, 0, _priority)) + log_dbg(ctx, "setpriority %d failed: %s", _priority, strerror(errno)); } - return ret; + return _memlock_count ? 1 : 0; } +/* Keyfile processing */ + /* - * Combines llseek with blockwise write. write_blockwise can already deal with short writes - * but we also need a function to deal with short writes at the start. But this information - * is implicitly included in the read/write offset, which can not be set to non-aligned - * boundaries. Hence, we combine llseek with write. + * A simple call to lseek(3) might not be possible for some inputs (e.g. + * reading from a pipe), so this function instead reads of up to BUFSIZ bytes + * at a time until the specified number of bytes. It returns -1 on read error + * or when it reaches EOF before the requested number of bytes have been + * discarded. */ -ssize_t write_lseek_blockwise(int fd, char *buf, size_t count, off_t offset) { - char *frontPadBuf; - void *frontPadBuf_base = NULL; - int r, bsize, frontHang; - size_t innerCount = 0; - ssize_t ret = -1; - - if ((bsize = sector_size(fd)) < 0) - return bsize; - - frontHang = offset % bsize; - - if (lseek(fd, offset - frontHang, SEEK_SET) < 0) - goto out; - - if (frontHang) { - frontPadBuf = aligned_malloc(&frontPadBuf_base, - bsize, get_alignment(fd)); - if (!frontPadBuf) - goto out; +static int keyfile_seek(int fd, uint64_t bytes) +{ + char tmp[BUFSIZ]; + size_t next_read; + ssize_t bytes_r; + off64_t r; - r = read(fd, frontPadBuf, bsize); - if (r < 0 || r != bsize) - goto out; + r = lseek64(fd, bytes, SEEK_CUR); + if (r > 0) + return 0; + if (r < 0 && errno != ESPIPE) + return -1; - innerCount = bsize - frontHang; - if (innerCount > count) - innerCount = count; + while (bytes > 0) { + /* figure out how much to read */ + next_read = bytes > sizeof(tmp) ? sizeof(tmp) : (size_t)bytes; - memcpy(frontPadBuf + frontHang, buf, innerCount); + bytes_r = read(fd, tmp, next_read); + if (bytes_r < 0) { + if (errno == EINTR) + continue; - if (lseek(fd, offset - frontHang, SEEK_SET) < 0) - goto out; + crypt_safe_memzero(tmp, sizeof(tmp)); + /* read error */ + return -1; + } - r = write(fd, frontPadBuf, bsize); - if (r < 0 || r != bsize) - goto out; + if (bytes_r == 0) + /* EOF */ + break; - buf += innerCount; - count -= innerCount; + bytes -= bytes_r; } - ret = count ? write_blockwise(fd, buf, count) : 0; - if (ret >= 0) - ret += innerCount; -out: - free(frontPadBuf_base); - - return ret; + crypt_safe_memzero(tmp, sizeof(tmp)); + return bytes == 0 ? 0 : -1; } -int device_ready(struct crypt_device *cd, const char *device, int mode) +int crypt_keyfile_device_read(struct crypt_device *cd, const char *keyfile, + char **key, size_t *key_size_read, + uint64_t keyfile_offset, size_t key_size, + uint32_t flags) { - int devfd, r = 0; - ssize_t s; + int fd, regular_file, char_to_read = 0, char_read = 0, unlimited_read = 0; + int r = -EINVAL, newline; + char *pass = NULL; + size_t buflen, i; + uint64_t file_read_size; struct stat st; - char buf[512]; - if(stat(device, &st) < 0) { - log_err(cd, _("Device %s doesn't exist or access denied.\n"), device); + if (!key || !key_size_read) return -EINVAL; - } - if (!S_ISBLK(st.st_mode)) - return -ENOTBLK; + *key = NULL; + *key_size_read = 0; - log_dbg("Trying to open and read device %s.", device); - devfd = open(device, mode | O_DIRECT | O_SYNC); - if(devfd < 0) { - log_err(cd, _("Cannot open device %s for %s%s access.\n"), device, - (mode & O_EXCL) ? _("exclusive ") : "", - (mode & O_RDWR) ? _("writable") : _("read-only")); + fd = keyfile ? open(keyfile, O_RDONLY) : STDIN_FILENO; + if (fd < 0) { + log_err(cd, _("Failed to open key file.")); return -EINVAL; } - /* Try to read first sector */ - s = read_blockwise(devfd, buf, sizeof(buf)); - if (s < 0 || s != sizeof(buf)) { - log_verbose(cd, _("Cannot read device %s.\n"), device); - r = -EIO; - } - - memset(buf, 0, sizeof(buf)); - close(devfd); - - return r; -} - -int device_size(const char *device, uint64_t *size) -{ - int devfd, r = 0; - - devfd = open(device, O_RDONLY); - if(devfd == -1) - return -EINVAL; - - if (ioctl(devfd, BLKGETSIZE64, size) < 0) + if (isatty(fd)) { + log_err(cd, _("Cannot read keyfile from a terminal.")); r = -EINVAL; - - close(devfd); - return r; -} - -static int get_device_infos(const char *device, enum devcheck device_check, - int *readonly, uint64_t *size) -{ - struct stat st; - unsigned long size_small; - int fd, r = -1; - int flags = 0; - - *readonly = 0; - *size = 0; - - if (stat(device, &st) < 0) - return -EINVAL; - - /* never wipe header on mounted device */ - if (device_check == DEV_EXCL && S_ISBLK(st.st_mode)) - flags |= O_EXCL; - - /* Try to open read-write to check whether it is a read-only device */ - fd = open(device, O_RDWR | flags); - if (fd == -1 && errno == EROFS) { - *readonly = 1; - fd = open(device, O_RDONLY | flags); + goto out_err; } - if (fd == -1 && device_check == DEV_EXCL && errno == EBUSY) - return -EBUSY; - - if (fd == -1) - return -EINVAL; + /* If not requested otherwise, we limit input to prevent memory exhaustion */ + if (key_size == 0) { + key_size = DEFAULT_KEYFILE_SIZE_MAXKB * 1024 + 1; + unlimited_read = 1; + /* use 4k for buffer (page divisor but avoid huge pages) */ + buflen = 4096 - sizeof(size_t); // sizeof(struct safe_allocation); + } else + buflen = key_size; - /* If the device can be opened read-write, i.e. readonly is still 0, then - * check whether BKROGET says that it is read-only. E.g. read-only loop - * devices may be openend read-write but are read-only according to BLKROGET - */ - if (*readonly == 0 && (r = ioctl(fd, BLKROGET, readonly)) < 0) - goto out; + regular_file = 0; + if (keyfile) { + if (stat(keyfile, &st) < 0) { + log_err(cd, _("Failed to stat key file.")); + goto out_err; + } + if (S_ISREG(st.st_mode)) { + regular_file = 1; + file_read_size = (uint64_t)st.st_size; + + if (keyfile_offset > file_read_size) { + log_err(cd, _("Cannot seek to requested keyfile offset.")); + goto out_err; + } + file_read_size -= keyfile_offset; + + /* known keyfile size, alloc it in one step */ + if (file_read_size >= (uint64_t)key_size) + buflen = key_size; + else if (file_read_size) + buflen = file_read_size; + } + } - if (ioctl(fd, BLKGETSIZE64, size) >= 0) { - *size >>= SECTOR_SHIFT; - r = 0; - goto out; + pass = crypt_safe_alloc(buflen); + if (!pass) { + log_err(cd, _("Out of memory while reading passphrase.")); + goto out_err; } - if (ioctl(fd, BLKGETSIZE, &size_small) >= 0) { - *size = (uint64_t)size_small; - r = 0; - goto out; + /* Discard keyfile_offset bytes on input */ + if (keyfile_offset && keyfile_seek(fd, keyfile_offset) < 0) { + log_err(cd, _("Cannot seek to requested keyfile offset.")); + goto out_err; } - r = -EINVAL; -out: - close(fd); - return r; -} + for (i = 0, newline = 0; i < key_size; i += char_read) { + if (i == buflen) { + buflen += 4096; + pass = crypt_safe_realloc(pass, buflen); + if (!pass) { + log_err(cd, _("Out of memory while reading passphrase.")); + r = -ENOMEM; + goto out_err; + } + } -int device_check_and_adjust(struct crypt_device *cd, - const char *device, - enum devcheck device_check, - uint64_t *size, - uint64_t *offset, - uint32_t *flags) -{ - int r, real_readonly; - uint64_t real_size; - - if (!device) - return -ENOTBLK; - - r = get_device_infos(device, device_check, &real_readonly, &real_size); - if (r < 0) { - if (r == -EBUSY) - log_err(cd, _("Cannot use device %s which is in use " - "(already mapped or mounted).\n"), - device); - else - log_err(cd, _("Cannot get info about device %s.\n"), - device); - return r; - } + if (flags & CRYPT_KEYFILE_STOP_EOL) { + /* If we should stop on newline, we must read the input + * one character at the time. Otherwise we might end up + * having read some bytes after the newline, which we + * promised not to do. + */ + char_to_read = 1; + } else { + /* char_to_read = min(key_size - i, buflen - i) */ + char_to_read = key_size < buflen ? + key_size - i : buflen - i; + } + char_read = read_buffer(fd, &pass[i], char_to_read); + if (char_read < 0) { + log_err(cd, _("Error reading passphrase.")); + r = -EPIPE; + goto out_err; + } - if (*offset >= real_size) { - log_err(cd, _("Requested offset is beyond real size of device %s.\n"), - device); - return -EINVAL; + if (char_read == 0) + break; + /* Stop on newline only if not requested read from keyfile */ + if ((flags & CRYPT_KEYFILE_STOP_EOL) && pass[i] == '\n') { + newline = 1; + pass[i] = '\0'; + break; + } } - if (!*size) { - *size = real_size; - if (!*size) { - log_err(cd, _("Device %s has zero size.\n"), device); - return -ENOTBLK; - } - *size -= *offset; + /* Fail if piped input dies reading nothing */ + if (!i && !regular_file && !newline) { + log_err(cd, _("Nothing to read on input.")); + r = -EPIPE; + goto out_err; } - /* in case of size is set by parameter */ - if ((real_size - *offset) < *size) { - log_dbg("Device %s: offset = %" PRIu64 " requested size = %" PRIu64 - ", backing device size = %" PRIu64, - device, *offset, *size, real_size); - log_err(cd, _("Device %s is too small.\n"), device); - return -EINVAL; + /* Fail if we exceeded internal default (no specified size) */ + if (unlimited_read && i == key_size) { + log_err(cd, _("Maximum keyfile size exceeded.")); + goto out_err; } - if (device_check == DEV_SHARED) { - log_dbg("Checking crypt segments for device %s.", device); - r = crypt_sysfs_check_crypt_segment(device, *offset, *size); - if (r < 0) { - log_err(cd, _("Cannot use device %s (crypt segments " - "overlaps or in use by another device).\n"), - device); - return r; - } + if (!unlimited_read && i != key_size) { + log_err(cd, _("Cannot read requested amount of data.")); + goto out_err; } - if (real_readonly) - *flags |= CRYPT_ACTIVATE_READONLY; + *key = pass; + *key_size_read = i; + r = 0; +out_err: + if (fd != STDIN_FILENO) + close(fd); - log_dbg("Calculated device size is %" PRIu64 " sectors (%s), offset %" PRIu64 ".", - *size, real_readonly ? "RO" : "RW", *offset); - return 0; + if (r) + crypt_safe_free(pass); + return r; } -/* MEMLOCK */ -#define DEFAULT_PROCESS_PRIORITY -18 - -static int _priority; -static int _memlock_count = 0; - -// return 1 if memory is locked -int crypt_memlock_inc(struct crypt_device *ctx) +int crypt_keyfile_read(struct crypt_device *cd, const char *keyfile, + char **key, size_t *key_size_read, + size_t keyfile_offset, size_t keyfile_size_max, + uint32_t flags) { - if (!_memlock_count++) { - log_dbg("Locking memory."); - if (mlockall(MCL_CURRENT | MCL_FUTURE) == -1) { - log_err(ctx, _("WARNING!!! Possibly insecure memory. Are you root?\n")); - _memlock_count--; - return 0; - } - errno = 0; - if (((_priority = getpriority(PRIO_PROCESS, 0)) == -1) && errno) - log_err(ctx, _("Cannot get process priority.\n")); - else - if (setpriority(PRIO_PROCESS, 0, DEFAULT_PROCESS_PRIORITY)) - log_err(ctx, _("setpriority %d failed: %s\n"), - DEFAULT_PROCESS_PRIORITY, strerror(errno)); - } - return _memlock_count ? 1 : 0; + return crypt_keyfile_device_read(cd, keyfile, key, key_size_read, + keyfile_offset, keyfile_size_max, flags); } -int crypt_memlock_dec(struct crypt_device *ctx) +int kernel_version(uint64_t *kversion) { - if (_memlock_count && (!--_memlock_count)) { - log_dbg("Unlocking memory."); - if (munlockall() == -1) - log_err(ctx, _("Cannot unlock memory.\n")); - if (setpriority(PRIO_PROCESS, 0, _priority)) - log_err(ctx, _("setpriority %d failed: %s\n"), _priority, strerror(errno)); - } - return _memlock_count ? 1 : 0; -} + struct utsname uts; + uint16_t maj, min, patch, rel; + int r = -EINVAL; -/* DEVICE TOPOLOGY */ - -/* block device topology ioctls, introduced in 2.6.32 */ -#ifndef BLKIOMIN -#define BLKIOMIN _IO(0x12,120) -#define BLKIOOPT _IO(0x12,121) -#define BLKALIGNOFF _IO(0x12,122) -#endif + if (uname(&uts) < 0) + return r; -void get_topology_alignment(const char *device, - unsigned long *required_alignment, /* bytes */ - unsigned long *alignment_offset, /* bytes */ - unsigned long default_alignment) -{ - int dev_alignment_offset = 0; - unsigned int min_io_size = 0, opt_io_size = 0; - unsigned long temp_alignment = 0; - int fd; - - *required_alignment = default_alignment; - *alignment_offset = 0; - - fd = open(device, O_RDONLY); - if (fd == -1) - return; - - /* minimum io size */ - if (ioctl(fd, BLKIOMIN, &min_io_size) == -1) { - log_dbg("Topology info for %s not supported, using default offset %lu bytes.", - device, default_alignment); - goto out; + if (sscanf(uts.release, "%" SCNu16 ".%" SCNu16 ".%" SCNu16 "-%" SCNu16, + &maj, &min, &patch, &rel) == 4) + r = 0; + else if (sscanf(uts.release, "%" SCNu16 ".%" SCNu16 ".%" SCNu16, + &maj, &min, &patch) == 3) { + rel = 0; + r = 0; } - /* optimal io size */ - if (ioctl(fd, BLKIOOPT, &opt_io_size) == -1) - opt_io_size = min_io_size; + if (!r) + *kversion = version(maj, min, patch, rel); - /* alignment offset, bogus -1 means misaligned/unknown */ - if (ioctl(fd, BLKALIGNOFF, &dev_alignment_offset) == -1 || dev_alignment_offset < 0) - dev_alignment_offset = 0; - *alignment_offset = (unsigned long)dev_alignment_offset; + return r; +} - temp_alignment = (unsigned long)min_io_size; +bool crypt_string_in(const char *str, char **list, size_t list_size) +{ + size_t i; - if (temp_alignment < (unsigned long)opt_io_size) - temp_alignment = (unsigned long)opt_io_size; + for (i = 0; *list && i < list_size; i++, list++) + if (!strcmp(str, *list)) + return true; - /* If calculated alignment is multiple of default, keep default */ - if (temp_alignment && (default_alignment % temp_alignment)) - *required_alignment = temp_alignment; + return false; +} - log_dbg("Topology: IO (%u/%u), offset = %lu; Required alignment is %lu bytes.", - min_io_size, opt_io_size, *alignment_offset, *required_alignment); -out: - (void)close(fd); +/* compare two strings (allows NULL values) */ +int crypt_strcmp(const char *a, const char *b) +{ + if (!a && !b) + return 0; + else if (!a || !b) + return 1; + return strcmp(a, b); }