X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=lib%2Futils.c;h=7e850a61419f56fb1bf75508bcd83b3418f73479;hb=690c363eb90277fc53a97e3e2367ebe12055937e;hp=e1d0e4a5582f9952695996ece800f0fd9772f1c5;hpb=dfe77be7480825fd4d42ac9eb11ef67e60780831;p=platform%2Fupstream%2Fcryptsetup.git diff --git a/lib/utils.c b/lib/utils.c index e1d0e4a..7e850a6 100644 --- a/lib/utils.c +++ b/lib/utils.c @@ -1,458 +1,345 @@ +/* + * utils - miscellaneous device utilities for cryptsetup + * + * 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 + * 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 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + #include -#include -#include -#include -#include #include -#include -#include -#include -#include -#include -#include -#include #include #include +#include +#include -#include "libcryptsetup.h" #include "internal.h" -static char *error=NULL; - -void set_error_va(const char *fmt, va_list va) -{ - int r; - - if(error) { - free(error); - error = NULL; - } - - if(!fmt) return; - - r = vasprintf(&error, fmt, va); - if (r < 0) { - free(error); - error = NULL; - return; - } - - if (r && error[r - 1] == '\n') - error[r - 1] = '\0'; -} - -void set_error(const char *fmt, ...) -{ - va_list va; - - va_start(va, fmt); - set_error_va(fmt, va); - va_end(va); -} - -const char *get_error(void) +size_t crypt_getpagesize(void) { - return error; + long r = sysconf(_SC_PAGESIZE); + return r <= 0 ? DEFAULT_MEM_ALIGNMENT : (size_t)r; } -static int get_alignment(int fd) +unsigned crypt_cpusonline(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_NPROCESSORS_ONLN); + return r < 0 ? 1 : r; } -static void *aligned_malloc(void **base, int size, int alignment) +uint64_t crypt_getphysmemory_kb(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 -} -static int sector_size(int fd) -{ - int bsize; - if (ioctl(fd,BLKSSZGET, &bsize) < 0) - return -EINVAL; - else - return bsize; -} + long pagesize, phys_pages; + uint64_t phys_memory_kb; -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; -} + pagesize = sysconf(_SC_PAGESIZE); + phys_pages = sysconf(_SC_PHYS_PAGES); -ssize_t write_blockwise(int fd, const 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) - goto out; - memcpy(buf, orig_buf, count); - } else - buf = (void *)orig_buf; + if (pagesize < 0 || phys_pages < 0) + return 0; - r = write(fd, buf, solid); - if (r < 0 || r != solid) - goto out; + phys_memory_kb = pagesize / 1024; + phys_memory_kb *= phys_pages; - if (hangover) { - hangover_buf = aligned_malloc(&hangover_buf_base, bsize, alignment); - if (!hangover_buf) - goto out; + return phys_memory_kb; +} - r = read(fd, hangover_buf, bsize); - if(r < 0 || r != bsize) goto out; +/* MEMLOCK */ +#define DEFAULT_PROCESS_PRIORITY -18 - r = lseek(fd, -bsize, SEEK_CUR); - if (r < 0) - goto out; - memcpy(hangover_buf, buf + solid, hangover); +static int _priority; +static int _memlock_count = 0; - r = write(fd, hangover_buf, bsize); - if(r < 0 || r != bsize) goto out; - free(hangover_buf_base); +// return 1 if memory is locked +int crypt_memlock_inc(struct crypt_device *ctx) +{ + 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: - 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; - 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; - } 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(buf + solid, hangover_buf, hangover); - free(hangover_buf_base); - } - ret = count; - out: - 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; } -/* - * 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. +/* Keyfile processing */ + +/* + * 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. */ +static int keyfile_seek(int fd, uint64_t bytes) +{ + char tmp[BUFSIZ]; + size_t next_read; + ssize_t bytes_r; + off64_t r; -ssize_t write_lseek_blockwise(int fd, const char *buf, size_t count, off_t offset) { - int bsize = sector_size(fd); - const char *orig_buf = buf; - char frontPadBuf[bsize]; - int frontHang = offset % bsize; - int r; - int innerCount = count < bsize ? count : bsize; + r = lseek64(fd, bytes, SEEK_CUR); + if (r > 0) + return 0; + if (r < 0 && errno != ESPIPE) + return -1; - if (bsize < 0) - return bsize; + while (bytes > 0) { + /* figure out how much to read */ + next_read = bytes > sizeof(tmp) ? sizeof(tmp) : (size_t)bytes; - lseek(fd, offset - frontHang, SEEK_SET); - if(offset % bsize) { - r = read(fd,frontPadBuf,bsize); - if(r < 0) return -1; + bytes_r = read(fd, tmp, next_read); + if (bytes_r < 0) { + if (errno == EINTR) + continue; - memcpy(frontPadBuf+frontHang, buf, innerCount); + crypt_safe_memzero(tmp, sizeof(tmp)); + /* read error */ + return -1; + } - lseek(fd, offset - frontHang, SEEK_SET); - r = write(fd,frontPadBuf,bsize); - if(r < 0) return -1; + if (bytes_r == 0) + /* EOF */ + break; - buf += innerCount; - count -= innerCount; + bytes -= bytes_r; } - if(count <= 0) return buf - orig_buf; - return write_blockwise(fd, buf, count) + innerCount; + 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 = 1; - 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); - return 0; - } + if (!key || !key_size_read) + return -EINVAL; - 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")); - return 0; - } + *key = NULL; + *key_size_read = 0; - /* Try to read first sector */ - s = read_blockwise(devfd, buf, sizeof(buf)); - if (s < 0 || s != sizeof(buf)) { - log_err(cd, _("Cannot read device %s.\n"), device); - r = 0; + fd = keyfile ? open(keyfile, O_RDONLY) : STDIN_FILENO; + if (fd < 0) { + log_err(cd, _("Failed to open key file.")); + return -EINVAL; } - memset(buf, 0, sizeof(buf)); - close(devfd); + if (isatty(fd)) { + log_err(cd, _("Cannot read keyfile from a terminal.")); + r = -EINVAL; + goto out_err; + } - return r; -} + /* 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; -int get_device_infos(const char *device, struct device_infos *infos, struct crypt_device *cd) -{ - uint64_t size; - unsigned long size_small; - int readonly = 0; - int ret = -1; - int fd; - - /* Try to open read-write to check whether it is a read-only device */ - fd = open(device, O_RDWR); - if (fd < 0) { - if (errno == EROFS) { - readonly = 1; - fd = open(device, O_RDONLY); + 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; } - } else { - close(fd); - fd = open(device, O_RDONLY); - } - if (fd < 0) { - log_err(cd, _("Cannot open device: %s\n"), device); - return -1; } -#ifdef BLKROGET - /* 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 && ioctl(fd, BLKROGET, &readonly) < 0) { - log_err(cd, _("BLKROGET failed on device %s.\n"), device); - goto out; - } -#else -#error BLKROGET not available -#endif - -#ifdef BLKGETSIZE64 - if (ioctl(fd, BLKGETSIZE64, &size) >= 0) { - size >>= SECTOR_SHIFT; - ret = 0; - goto out; + pass = crypt_safe_alloc(buflen); + if (!pass) { + log_err(cd, _("Out of memory while reading passphrase.")); + goto out_err; } -#endif -#ifdef BLKGETSIZE - if (ioctl(fd, BLKGETSIZE, &size_small) >= 0) { - size = (uint64_t)size_small; - ret = 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; } -#else -# error Need at least the BLKGETSIZE ioctl! -#endif - - log_err(cd, _("BLKGETSIZE failed on device %s.\n"), device); -out: - if (ret == 0) { - infos->size = size; - infos->readonly = readonly; - } - close(fd); - return ret; -} -int wipe_device_header(const char *device, int sectors) -{ - char *buffer; - int size = sectors * SECTOR_SIZE; - int r = -1; - int devfd; + 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; + } + } - devfd = open(device, O_RDWR | O_DIRECT | O_SYNC); - if(devfd == -1) - return -EINVAL; + 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; + } - buffer = malloc(size); - if (!buffer) { - close(devfd); - return -ENOMEM; + 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; + } } - memset(buffer, 0, size); - - r = write_blockwise(devfd, buffer, size) < size ? -EIO : 0; - free(buffer); - close(devfd); + /* 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; + } - return r; -} + /* 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; + } -/* MEMLOCK */ -#define DEFAULT_PROCESS_PRIORITY -18 + if (!unlimited_read && i != key_size) { + log_err(cd, _("Cannot read requested amount of data.")); + goto out_err; + } -static int _priority; -static int _memlock_count = 0; + *key = pass; + *key_size_read = i; + r = 0; +out_err: + if (fd != STDIN_FILENO) + close(fd); -// return 1 if memory is locked -int crypt_memlock_inc(struct crypt_device *ctx) -{ - if (!_memlock_count++) { - log_dbg("Locking memory."); - if (mlockall(MCL_CURRENT | MCL_FUTURE)) { - 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 %u failed: %s"), - DEFAULT_PROCESS_PRIORITY, strerror(errno)); - } - return _memlock_count ? 1 : 0; + if (r) + crypt_safe_free(pass); + return r; } -int crypt_memlock_dec(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 && (!--_memlock_count)) { - log_dbg("Unlocking memory."); - if (munlockall()) - log_err(ctx, _("Cannot unlock memory.")); - if (setpriority(PRIO_PROCESS, 0, _priority)) - log_err(ctx, _("setpriority %u failed: %s"), _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); } -/* DEVICE TOPOLOGY */ +int kernel_version(uint64_t *kversion) +{ + struct utsname uts; + uint16_t maj, min, patch, rel; + int r = -EINVAL; -/* 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); }