diff --git a/ChangeLog b/ChangeLog index 9500cfe3..6095f743 100644 --- a/ChangeLog +++ b/ChangeLog @@ -7,6 +7,8 @@ * Fix luksRemoveKey to behave as documented (do not ask for remaining keyslot passphrase). * Add more regression tests for commands. + * Disallow mapping of device which is already in use (mapped or mounted). + * Disallow luksFormat on device in use. 2010-10-27 Milan Broz * Rewrite cryptsetup luksFormat, luksOpen, luksAddKey to use new API diff --git a/lib/internal.h b/lib/internal.h index 5bd15ef9..e3005d58 100644 --- a/lib/internal.h +++ b/lib/internal.h @@ -42,11 +42,6 @@ struct hash_backend { void (*free_hashes)(struct hash_type *hashes); }; -struct device_infos { - uint64_t size; - int readonly; -}; - struct volume_key { size_t keylength; char key[]; @@ -102,7 +97,10 @@ ssize_t write_blockwise(int fd, const void *buf, size_t count); ssize_t read_blockwise(int fd, void *_buf, size_t count); ssize_t write_lseek_blockwise(int fd, const char *buf, size_t count, off_t offset); int device_ready(struct crypt_device *cd, const char *device, int mode); -int get_device_infos(const char *device, struct device_infos *infos, struct crypt_device *cd); +int get_device_infos(const char *device, + int open_exclusive, + int *readonly, + uint64_t *size); int wipe_device_header(const char *device, int sectors); void logger(struct crypt_device *cd, int class, const char *file, int line, const char *format, ...); diff --git a/lib/setup.c b/lib/setup.c index 8acaf4d8..b10c9200 100644 --- a/lib/setup.c +++ b/lib/setup.c @@ -223,19 +223,31 @@ static int find_keyslot_by_passphrase(struct crypt_device *cd, static int device_check_and_adjust(struct crypt_device *cd, const char *device, - uint64_t *size, uint64_t *offset, + int open_exclusive, + uint64_t *size, + uint64_t *offset, int *read_only) { - struct device_infos infos; + int r, real_readonly; + uint64_t real_size; - if (!device || get_device_infos(device, &infos, cd) < 0) { - log_err(cd, _("Cannot get info about device %s.\n"), - device ?: "[none]"); + if (!device) return -ENOTBLK; + + r = get_device_infos(device, open_exclusive, &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 (!*size) { - *size = infos.size; + *size = real_size; if (!*size) { log_err(cd, _("Device %s has zero size.\n"), device); return -ENOTBLK; @@ -247,7 +259,7 @@ static int device_check_and_adjust(struct crypt_device *cd, *size -= *offset; } - if (infos.readonly) + if (real_readonly) *read_only = 1; log_dbg("Calculated device size is %" PRIu64 " sectors (%s), offset %" PRIu64 ".", @@ -349,7 +361,7 @@ static int create_device_helper(struct crypt_device *cd, return -EINVAL; } - r = device_check_and_adjust(cd, cd->device, &size, &offset, &read_only); + r = device_check_and_adjust(cd, cd->device, !reload, &size, &offset, &read_only); if (r) return r; @@ -382,7 +394,7 @@ static int open_from_hdr_and_vk(struct crypt_device *cd, read_only = flags & CRYPT_ACTIVATE_READONLY; no_uuid = flags & CRYPT_ACTIVATE_NO_UUID; - r = device_check_and_adjust(cd, cd->device, &size, &offset, &read_only); + r = device_check_and_adjust(cd, cd->device, 1, &size, &offset, &read_only); if (r) return r; @@ -640,7 +652,7 @@ int crypt_resize_device(struct crypt_options *options) goto out; size = options->size; - r = device_check_and_adjust(cd, device, &size, &offset, &read_only); + r = device_check_and_adjust(cd, device, 0, &size, &offset, &read_only); if (r) goto out; @@ -1131,7 +1143,13 @@ static int _crypt_format_luks1(struct crypt_device *cd, /* Wipe first 8 sectors - fs magic numbers etc. */ r = wipe_device_header(cd->device, 8); if(r < 0) { - log_err(cd, _("Can't wipe header on device %s.\n"), cd->device); + if (r == -EBUSY) + log_err(cd, _("Cannot format device %s which is still in use.\n"), + cd->device); + else + log_err(cd, _("Cannot wipe header on device %s.\n"), + cd->device); + return r; } @@ -1237,7 +1255,7 @@ int crypt_resize(struct crypt_device *cd, const char *name, uint64_t new_size) goto out; } - r = device_check_and_adjust(cd, device, &new_size, &offset, &read_only); + r = device_check_and_adjust(cd, device, 0, &new_size, &offset, &read_only); if (r) goto out; diff --git a/lib/utils.c b/lib/utils.c index e1d0e4a5..510ab6dd 100644 --- a/lib/utils.c +++ b/lib/utils.c @@ -270,82 +270,94 @@ int device_ready(struct crypt_device *cd, const char *device, int mode) return r; } -int get_device_infos(const char *device, struct device_infos *infos, struct crypt_device *cd) +int get_device_infos(const char *device, + int open_exclusive, + int *readonly, + uint64_t *size) { - uint64_t size; + struct stat st; unsigned long size_small; - int readonly = 0; - int ret = -1; - int fd; + 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 (open_exclusive && 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); - if (fd < 0) { - if (errno == EROFS) { - readonly = 1; - fd = open(device, O_RDONLY); - } - } else { - close(fd); - fd = open(device, O_RDONLY); - } - if (fd < 0) { - log_err(cd, _("Cannot open device: %s\n"), device); - return -1; + fd = open(device, O_RDWR | flags); + if (fd == -1 && errno == EROFS) { + *readonly = 1; + fd = open(device, O_RDONLY | flags); } + if (fd == -1 && open_exclusive && errno == EBUSY) + return -EBUSY; + + if (fd == -1) + return -EINVAL; + #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); + if (*readonly == 0 && (r = ioctl(fd, BLKROGET, readonly)) < 0) goto out; - } #else #error BLKROGET not available #endif #ifdef BLKGETSIZE64 - if (ioctl(fd, BLKGETSIZE64, &size) >= 0) { - size >>= SECTOR_SHIFT; - ret = 0; + if (ioctl(fd, BLKGETSIZE64, size) >= 0) { + *size >>= SECTOR_SHIFT; + r = 0; goto out; } #endif #ifdef BLKGETSIZE if (ioctl(fd, BLKGETSIZE, &size_small) >= 0) { - size = (uint64_t)size_small; - ret = 0; + *size = (uint64_t)size_small; + r = 0; goto out; } + #else # error Need at least the BLKGETSIZE ioctl! #endif - - log_err(cd, _("BLKGETSIZE failed on device %s.\n"), device); + r = -EINVAL; out: - if (ret == 0) { - infos->size = size; - infos->readonly = readonly; - } close(fd); - return ret; + return r; } int wipe_device_header(const char *device, int sectors) { + struct stat st; char *buffer; int size = sectors * SECTOR_SIZE; int r = -1; int devfd; + int flags = O_RDWR | O_DIRECT | O_SYNC; - devfd = open(device, O_RDWR | O_DIRECT | O_SYNC); - if(devfd == -1) + if (stat(device, &st) < 0) return -EINVAL; + /* never wipe header on mounted device */ + if (S_ISBLK(st.st_mode)) + flags |= O_EXCL; + + devfd = open(device, flags); + if(devfd == -1) + return errno == EBUSY ? -EBUSY : -EINVAL; + buffer = malloc(size); if (!buffer) { close(devfd); diff --git a/luks/keymanage.c b/luks/keymanage.c index 94db61b6..472cc696 100644 --- a/luks/keymanage.c +++ b/luks/keymanage.c @@ -743,7 +743,10 @@ static int wipe(const char *device, unsigned int from, unsigned int to) return -EINVAL; buffer = (char *) malloc(bufLen); - if(!buffer) return -ENOMEM; + if(!buffer) { + close(devfd); + return -ENOMEM; + } for(i = 0; i < 39; ++i) { if (i >= 0 && i < 5) crypt_random_get(NULL, buffer, bufLen, CRYPT_RND_NORMAL); diff --git a/src/cryptsetup.c b/src/cryptsetup.c index b787d998..21b71150 100644 --- a/src/cryptsetup.c +++ b/src/cryptsetup.c @@ -535,7 +535,6 @@ out: static int action_luksRemoveKey(int arg) { struct crypt_device *cd = NULL; - crypt_keyslot_info ki; char *password = NULL; unsigned int passwordLen; int r; diff --git a/tests/compat-test b/tests/compat-test index 2afe013c..7e4f3263 100755 --- a/tests/compat-test +++ b/tests/compat-test @@ -256,5 +256,16 @@ $CRYPTSETUP create $DEV_NAME $LOOPDEV -d $KEY1 2>/dev/null && fail $CRYPTSETUP create $DEV_NAME $LOOPDEV -d blah 2>/dev/null && fail $CRYPTSETUP -q remove $DEV_NAME || fail +prepare "[20] Disallow open/create if already mapped." wipe +$CRYPTSETUP create $DEV_NAME $LOOPDEV -d $KEY1 || fail +$CRYPTSETUP create $DEV_NAME $LOOPDEV -d $KEY1 2>/dev/null && fail +$CRYPTSETUP create $DEV_NAME2 $LOOPDEV -d $KEY1 2>/dev/null && fail +echo "key0" | $CRYPTSETUP -q luksFormat $LOOPDEV 2>/dev/null && fail +$CRYPTSETUP remove $DEV_NAME || fail +echo "key0" | $CRYPTSETUP -q luksFormat $LOOPDEV || fail +echo "key0" | $CRYPTSETUP luksOpen $LOOPDEV $DEV_NAME || fail +echo "key0" | $CRYPTSETUP luksOpen $LOOPDEV $DEV_NAME2 2>/dev/null && fail +$CRYPTSETUP luksClose $DEV_NAME || fail + remove_mapping exit 0