Index: lib/libcryptsetup.h =================================================================== --- lib/libcryptsetup.h (revision 62) +++ lib/libcryptsetup.h (working copy) @@ -65,6 +65,78 @@ int crypt_luksFormat(struct crypt_options *options); int crypt_luksDump(struct crypt_options *options); +struct crypt_luks_volume_info; + +/* Get information about DEVICE, + Return 0 on sucess, setting INFO to the volume information. + return a negative errno value otherwise, the caller can try to use + crypt_get_error() to get an error message. + INFO can be NULL, in which case the function only verifies DEVICE is a valid + LUKS device. + If INFO is not not NULL, it should be freed using crypt_luks_vi_free(). +*/ +int crypt_luks_get_volume_info(struct crypt_luks_volume_info **info, + const char *device); + +/* Get cipher name from INFO. + Return a string for free(), or NULL if out of memory. */ +char *crypt_luks_vi_get_cipher_name(struct crypt_luks_volume_info *info); + +/* Get cipher mode from INFO. + Return a string for free(), or NULL if out of memory. */ +char *crypt_luks_vi_get_cipher_mode(struct crypt_luks_volume_info *info); + +/* Get number of master key bytes from INFO. */ +unsigned crypt_luks_vi_get_key_bytes(struct crypt_luks_volume_info *info); + +/* Get UUID from INFO. + Return a string for free(), or NULL if out of memory. */ +char *crypt_luks_vi_get_uuid(struct crypt_luks_volume_info *info); + +/* Free INFO. */ +void crypt_luks_vi_free(struct crypt_luks_volume_info *info); + +/* Get the master key of DEVICE, using PASSPHRASE with PASSPHRASE_LENGTH. + Return the used slot on success, setting KEY and KEY_LENGTH to the master + key; + return a negative errno value otherwise, the caller can try to use + crypt_get_error() to get an error message. + The caller is responsible for calling free(KEY) if this function returns + 0. */ +int crypt_luks_get_master_key(unsigned char **key, size_t *key_length, + const char *device, + const unsigned char *passphrase, + size_t passphrase_length, + void (*log)(int class, char *msg)); + +/* Verify that KEY with KEY_LENGTH is valid for DEVICE. + Return 0 on success. + Return a negative errno value otherwise, the caller can try to use + crypt_get_error() to get an error message. */ +int crypt_luks_verify_master_key(const char *device, const unsigned char *key, + size_t key_length); + +/* Open DEVICE using KEY with KEY_LENGTH as NAME. + Return 0 on success. + Return a negative errno value otherwise, the caller can try to use + crypt_get_error() to get an error message. */ +int crypt_luks_open_by_master_key(const char *name, const char *device, + const unsigned char *key, size_t key_length, + int flags, void (*log)(int class, char *msg)); + +/* Add a PASSPHRASE with PASSPHRASE_LENGTH to SLOT of DEVICE, using KEY with + KEY_LENGTH. + Return the used slot on success; + Return a negative errno value otherwise, the caller can try to use + crypt_get_error() to get an error message. + SLOT may be -1 to use the first empty slot. */ +int crypt_luks_add_passphrase_by_master_key(const char *device, + const unsigned char *key, + size_t key_length, int slot, + const unsigned char *passphrase, + size_t passphrase_length, + void (*log)(int class, char *msg)); + void crypt_get_error(char *buf, size_t size); void crypt_put_options(struct crypt_options *options); const char *crypt_get_dir(void); Index: lib/setup.c =================================================================== --- lib/setup.c (revision 62) +++ lib/setup.c (working copy) @@ -282,7 +282,7 @@ } } -static int __crypt_create_device(int reload, struct setup_backend *backend, +static int __crypt_create_device(intptr_t reload, struct setup_backend *backend, struct crypt_options *options) { struct crypt_options tmp = { @@ -359,7 +359,7 @@ return r; } -static int __crypt_query_device(int details, struct setup_backend *backend, +static int __crypt_query_device(intptr_t details, struct setup_backend *backend, struct crypt_options *options) { int r = backend->status(details, options, NULL); @@ -371,7 +371,7 @@ return r; } -static int __crypt_resize_device(int details, struct setup_backend *backend, +static int __crypt_resize_device(intptr_t details, struct setup_backend *backend, struct crypt_options *options) { struct crypt_options tmp = { @@ -412,7 +412,7 @@ return r; } -static int __crypt_remove_device(int arg, struct setup_backend *backend, +static int __crypt_remove_device(intptr_t arg, struct setup_backend *backend, struct crypt_options *options) { int r; @@ -428,7 +428,7 @@ return backend->remove(0, options); } -static int __crypt_luks_format(int arg, struct setup_backend *backend, struct crypt_options *options) +static int __crypt_luks_format(intptr_t arg, struct setup_backend *backend, struct crypt_options *options) { int r; @@ -504,8 +504,94 @@ return r; } -static int __crypt_luks_open(int arg, struct setup_backend *backend, struct crypt_options *options) +static int open_from_hdr_and_mk(struct luks_phdr *hdr, + struct luks_masterkey *mk, + const struct device_infos *infos, + struct setup_backend *backend, + struct crypt_options *options) { + char *dmCipherSpec; + int excl = (options->flags & CRYPT_FLAG_NON_EXCLUSIVE_ACCESS) + ? 0 : O_EXCL; + int r; + + if (infos->readonly) + options->flags |= CRYPT_FLAG_READONLY; + options->offset = hdr->payloadOffset; + if (asprintf(&dmCipherSpec, "%s-%s", hdr->cipherName, hdr->cipherMode) + < 0) { + r = -ENOMEM; + goto out; + } + options->cipher = dmCipherSpec; + options->key_size = mk->keyLength; + options->skip = 0; + + options->size = infos->size; + if (!options->size) { + set_error("Not a block device.\n"); + r = -ENOTBLK; + goto out; + } + if (options->size <= options->offset) { + set_error("Invalid offset"); + r = -EINVAL; + goto out; + } + options->size -= options->offset; + /* FIXME: code allows multiple crypt mapping, cannot use uuid then. + * anyway, it is dangerous and can corrupt data. Remove it in next version! */ + r = backend->create(0, options, mk->key, excl ? hdr->uuid : NULL); + out: + free(dmCipherSpec); + return r; +} + +static int __crypt_luks_open_by_master_key(intptr_t arg, + struct setup_backend *backend, + struct crypt_options *options) +{ + struct luks_masterkey *mk; + struct luks_phdr hdr; + struct device_infos infos; + struct crypt_options tmp = { + .name = options->name, + }; + int r; + int excl = (options->flags & CRYPT_FLAG_NON_EXCLUSIVE_ACCESS) != 0 + ? 0 : O_EXCL ; + + mk = (struct luks_masterkey *)arg; + + r = backend->status(0, &tmp, NULL); + if (r >= 0) { + set_error("Device %s already exists.", options->name); + return -EEXIST; + } + + if (!LUKS_device_ready(options->device, O_RDONLY | excl)) + return -ENOTBLK; + + if (get_device_infos(options->device, &infos) < 0) { + set_error("Can't get device information.\n"); + return -ENOTBLK; + } + + r = LUKS_read_phdr(options->device, &hdr); + if (r < 0) + return r; + + r = LUKS_verify_master_key(&hdr, mk); + if (r == -EPERM) + set_error("Master key does not match the volume.\n"); + if (r < 0) + return r; + + return open_from_hdr_and_mk(&hdr, mk, &infos, backend, options); +} + +static int __crypt_luks_open(intptr_t arg, struct setup_backend *backend, struct crypt_options *options) +{ struct luks_masterkey *mk=NULL; struct luks_phdr hdr; char *prompt = NULL; @@ -515,7 +601,6 @@ struct crypt_options tmp = { .name = options->name, }; - char *dmCipherSpec = NULL; int r, tries = options->tries; int excl = (options->flags & CRYPT_FLAG_NON_EXCLUSIVE_ACCESS) ? 0 : O_EXCL ; @@ -533,9 +618,6 @@ return -ENOTBLK; } - if (infos.readonly) - options->flags |= CRYPT_FLAG_READONLY; - if(asprintf(&prompt, "Enter LUKS passphrase for %s: ", options->device) < 0) return -ENOMEM; @@ -559,33 +641,8 @@ logger(options, CRYPT_LOG_NORMAL,"key slot %d unlocked.\n", r); + r = open_from_hdr_and_mk(&hdr, mk, &infos, backend, options); - options->offset = hdr.payloadOffset; - if (asprintf(&dmCipherSpec, "%s-%s", hdr.cipherName, hdr.cipherMode) < 0) { - r = -ENOMEM; - goto out2; - } - options->cipher = dmCipherSpec; - options->key_size = mk->keyLength; - options->skip = 0; - - options->size = infos.size; - if (!options->size) { - set_error("Not a block device.\n"); - r = -ENOTBLK; goto out2; - } - if (options->size <= options->offset) { - set_error("Invalid offset"); - r = -EINVAL; goto out2; - } - options->size -= options->offset; - /* FIXME: code allows multiple crypt mapping, cannot use uuid then. - * anyway, it is dangerous and can corrupt data. Remove it in next version! */ - r = backend->create(0, options, mk->key, excl ? hdr.uuid : NULL); - - out2: - free(dmCipherSpec); - dmCipherSpec = NULL; out1: safe_free(password); out: @@ -598,8 +655,76 @@ return r; } -static int __crypt_luks_add_key(int arg, struct setup_backend *backend, struct crypt_options *options) +/* arg is a struct luks_masterkey **. Caller must LUKS_dealloc_masterkey(*arg) + if this function returns 0. + options->key_size is abused as passphrase length. */ +static int __crypt_luks_get_master_key(intptr_t arg, + struct setup_backend *backend, + struct crypt_options *options) { + struct luks_masterkey *mk; + struct luks_phdr hdr; + int r; + + if (!LUKS_device_ready(options->device, O_RDONLY)) + return -ENOTBLK; + + r = LUKS_open_any_key(options->device, options->passphrase, + options->key_size, &hdr, &mk, backend); + if (r == -EPERM) + set_error("No key available with this passphrase."); + if (r < 0) { + LUKS_dealloc_masterkey(mk); + return r; + } + + *(struct luks_masterkey **)arg = mk; + return r; +} + +/* options->key_size is abused as passphrase length. */ +static int __crypt_luks_add_passphrase_by_master_key + (intptr_t arg, struct setup_backend *backend, + struct crypt_options *options) +{ + struct luks_phdr hdr; + unsigned int keyIndex; + struct luks_masterkey *mk; + const char *device = options->device; + int r; + + mk = (struct luks_masterkey *)arg; + + if (!LUKS_device_ready(options->device, O_RDWR)) + return -ENOTBLK; + + r = LUKS_read_phdr(device, &hdr); + if (r < 0) + return r; + + r = LUKS_verify_master_key(&hdr, mk); + if (r < 0) { + set_error("Master key does not match the volume"); + return -EINVAL; + } + + keyIndex = keyslot_from_option(options->key_slot, &hdr, options); + if (keyIndex == -EINVAL) + return -EINVAL; + + hdr.keyblock[keyIndex].passwordIterations + = at_least_one(LUKS_benchmarkt_iterations() + * ((float)options->iteration_time / 1000)); + + r = LUKS_set_key(device, keyIndex, options->passphrase, + options->key_size, &hdr, mk, backend); + if (r < 0) + return r; + return keyIndex; +} + +static int __crypt_luks_add_key(intptr_t arg, struct setup_backend *backend, struct crypt_options *options) +{ struct luks_masterkey *mk=NULL; struct luks_phdr hdr; char *password=NULL; unsigned int passwordLen; @@ -664,7 +789,7 @@ return r; } -static int luks_remove_helper(int arg, struct setup_backend *backend, struct crypt_options *options, int supply_it) +static int luks_remove_helper(intptr_t arg, struct setup_backend *backend, struct crypt_options *options, int supply_it) { struct luks_masterkey *mk; struct luks_phdr hdr; @@ -735,18 +860,18 @@ return r; } -static int __crypt_luks_kill_slot(int arg, struct setup_backend *backend, struct crypt_options *options) { +static int __crypt_luks_kill_slot(intptr_t arg, struct setup_backend *backend, struct crypt_options *options) { return luks_remove_helper(arg, backend, options, 0); } -static int __crypt_luks_remove_key(int arg, struct setup_backend *backend, struct crypt_options *options) { +static int __crypt_luks_remove_key(intptr_t arg, struct setup_backend *backend, struct crypt_options *options) { return luks_remove_helper(arg, backend, options, 1); } -static int crypt_job(int (*job)(int arg, struct setup_backend *backend, +static int crypt_job(int (*job)(intptr_t arg, struct setup_backend *backend, struct crypt_options *options), - int arg, struct crypt_options *options) + intptr_t arg, struct crypt_options *options) { struct setup_backend *backend; int r; @@ -807,6 +932,96 @@ return crypt_job(__crypt_luks_format, 0, options); } +int crypt_luks_get_master_key(unsigned char **key, size_t *key_length, + const char *device, + const unsigned char *passphrase, + size_t passphrase_length, + void (*log)(int class, char *msg)) +{ + struct crypt_options options; + struct interface_callbacks icb; + struct luks_masterkey *mk; + int r; + + memset(&icb, 0, sizeof(icb)); + icb.log = log; + memset(&options, 0, sizeof(options)); + options.device = device; + options.icb = &icb; + options.passphrase = (const char *)passphrase; + options.key_size = passphrase_length; /* Abusing the field */ + if (options.key_size != passphrase_length) { + set_error("passphrase_length too large"); + return -EOVERFLOW; + } + r = crypt_job(__crypt_luks_get_master_key, (intptr_t)&mk, &options); + if (r < 0) + return r; + /* Note: this memory is not mlock()ed */ + *key = malloc(mk->keyLength); + if (*key == NULL) { + LUKS_dealloc_masterkey(mk); + return -ENOMEM; + } + memcpy(*key, mk->key, mk->keyLength); + *key_length = mk->keyLength; + LUKS_dealloc_masterkey(mk); + return r; +} + +int crypt_luks_verify_master_key(const char *device, const unsigned char *key, + size_t key_length) +{ + struct luks_masterkey *mk; + struct luks_phdr hdr; + int r; + + r = LUKS_read_phdr(device, &hdr); + if (r < 0) + return r; + + mk = LUKS_alloc_masterkey(key_length); + if (mk == NULL) + return -ENOMEM; + memcpy(mk->key, key, key_length); + + r = LUKS_verify_master_key(&hdr, mk); + if (r == -EPERM) + set_error("Master key does not match the volume.\n"); + + LUKS_dealloc_masterkey(mk); + + return r; +} + +int crypt_luks_open_by_master_key(const char *name, const char *device, + const unsigned char *key, size_t key_length, + int flags, void (*log)(int class, char *msg)) +{ + struct crypt_options options; + struct interface_callbacks icb; + struct luks_masterkey *mk; + int r; + + memset(&icb, 0, sizeof(icb)); + icb.log = log; + memset(&options, 0, sizeof(options)); + options.name = name; + options.device = device; + options.flags = flags; + options.offset = 0; + options.icb = &icb; + + mk = LUKS_alloc_masterkey(key_length); + if (mk == NULL) + return -ENOMEM; + memcpy(mk->key, key, key_length); + r = crypt_job(__crypt_luks_open_by_master_key, (intptr_t)mk, &options); + LUKS_dealloc_masterkey(mk); + + return r; +} + int crypt_luksOpen(struct crypt_options *options) { return crypt_job(__crypt_luks_open, 0, options); @@ -822,6 +1037,39 @@ return crypt_job(__crypt_luks_remove_key, 0, options); } +int crypt_luks_add_passphrase_by_master_key(const char *device, + const unsigned char *key, + size_t key_length, int slot, + const unsigned char *passphrase, + size_t passphrase_length, + void (*log)(int class, char *msg)) +{ + struct crypt_options options; + struct interface_callbacks icb; + struct luks_masterkey *mk; + int r; + + memset(&icb, 0, sizeof(icb)); + icb.log = log; + memset(&options, 0, sizeof(options)); + options.device = device; + options.passphrase = (const char *)passphrase; + options.key_size = passphrase_length; /* Abusing the field */ + options.key_slot = slot; + options.iteration_time = 1000; + options.icb = &icb; + + mk = LUKS_alloc_masterkey(key_length); + if (mk == NULL) + return -ENOMEM; + memcpy(mk->key, key, key_length); + r = crypt_job(__crypt_luks_add_passphrase_by_master_key, (intptr_t)mk, + &options); + LUKS_dealloc_masterkey(mk); + + return r; +} + int crypt_luksAddKey(struct crypt_options *options) { return crypt_job(__crypt_luks_add_key, 0, options); @@ -840,6 +1088,84 @@ return 0; } +struct crypt_luks_volume_info +{ + struct luks_phdr h; +}; + +int crypt_luks_get_volume_info(struct crypt_luks_volume_info **info, + const char *device) +{ + struct crypt_luks_volume_info *vi; + int r; + + vi = malloc(sizeof(*vi)); + if (vi == NULL) + return -ENOMEM; + r = LUKS_read_phdr(device, &vi->h); + if (r != 0) { + free(vi); + return r; + } + if (info != NULL) + *info = vi; + else + free(vi); + return 0; +} + +char *crypt_luks_vi_get_cipher_name(struct crypt_luks_volume_info *info) +{ + size_t field_size; + char *r; + + field_size = sizeof(info->h.cipherName); + r = malloc(field_size + 1); + if (r != NULL) { + memcpy(r, info->h.cipherName, field_size); + r[field_size] = '\0'; + } + return r; +} + +char *crypt_luks_vi_get_cipher_mode(struct crypt_luks_volume_info *info) +{ + size_t field_size; + char *r; + + field_size = sizeof(info->h.cipherMode); + r = malloc(field_size + 1); + if (r != NULL) { + memcpy(r, info->h.cipherMode, field_size); + r[field_size] = '\0'; + } + return r; +} + +unsigned crypt_luks_vi_get_key_bytes(struct crypt_luks_volume_info *info) +{ + return info->h.keyBytes; +} + +char *crypt_luks_vi_get_uuid(struct crypt_luks_volume_info *info) +{ + size_t field_size; + char *r; + + field_size = sizeof(info->h.uuid); + r = malloc(field_size + 1); + if (r != NULL) { + memcpy(r, info->h.uuid, field_size); + r[field_size] = '\0'; + } + return r; +} + +void crypt_luks_vi_free(struct crypt_luks_volume_info *info) +{ + free(info); +} + int crypt_isLuks(struct crypt_options *options) { struct luks_phdr hdr; Index: luks/keymanage.c =================================================================== --- luks/keymanage.c (revision 62) +++ luks/keymanage.c (working copy) @@ -280,6 +280,20 @@ return r; } +/* Check whether a master key is invalid. */ +int LUKS_verify_master_key(const struct luks_phdr *hdr, + const struct luks_masterkey *mk) +{ + char checkHashBuf[LUKS_DIGESTSIZE]; + + PBKDF2_HMAC_SHA1(mk->key, mk->keyLength, hdr->mkDigestSalt, + LUKS_SALTSIZE, hdr->mkDigestIterations, checkHashBuf, + LUKS_DIGESTSIZE); + + return memcmp(checkHashBuf, hdr->mkDigest, LUKS_DIGESTSIZE) == 0 + ? 0 : -EPERM; +} + /* Try to open a particular key slot, */ @@ -295,7 +309,6 @@ char derivedKey[hdr->keyBytes]; char *AfKey; size_t AFEKSize; - char checkHashBuf[LUKS_DIGESTSIZE]; int r; if(hdr->keyblock[keyIndex].active != LUKS_KEY_ENABLED) { @@ -329,13 +342,8 @@ r = AF_merge(AfKey,mk->key,mk->keyLength,hdr->keyblock[keyIndex].stripes); if(r < 0) goto out; - - PBKDF2_HMAC_SHA1(mk->key,mk->keyLength, - hdr->mkDigestSalt,LUKS_SALTSIZE, - hdr->mkDigestIterations, - checkHashBuf,LUKS_DIGESTSIZE); - r = (memcmp(checkHashBuf,hdr->mkDigest, LUKS_DIGESTSIZE) == 0)?0:-EPERM; + r = LUKS_verify_master_key(hdr, mk); out: free(AfKey); return r; Index: luks/luks.h =================================================================== --- luks/luks.h (revision 62) +++ luks/luks.h (working copy) @@ -124,6 +124,8 @@ struct luks_masterkey **mk, struct setup_backend *backend); +int LUKS_verify_master_key(const struct luks_phdr *hdr, + const struct luks_masterkey *mk); int LUKS_del_key(const char *device, unsigned int keyIndex); int LUKS_is_last_keyslot(const char *device, unsigned int keyIndex);