Fix skcipher failure handling.
[platform/upstream/cryptsetup.git] / lib / tcrypt / tcrypt.c
index 8e7f07d..69a70b4 100644 (file)
@@ -244,7 +244,7 @@ static int decrypt_blowfish_le_cbc(struct tcrypt_alg *alg,
        r = crypt_cipher_init(&cipher, "blowfish", "ecb",
                              &key[alg->key_offset], alg->key_size);
        if (r < 0)
-               goto out;
+               return r;
 
        memcpy(iv, &key[alg->iv_offset], alg->iv_size);
        for (i = 0; i < TCRYPT_HDR_LEN; i += bs) {
@@ -254,12 +254,12 @@ static int decrypt_blowfish_le_cbc(struct tcrypt_alg *alg,
                                          bs, NULL, 0);
                blowfish_le(&buf[i]);
                if (r < 0)
-                       goto out;
+                       break;
                for (j = 0; j < bs; j++)
                        buf[i + j] ^= iv[j];
                memcpy(iv, iv_old, bs);
        }
-out:
+
        crypt_cipher_destroy(cipher);
        return r;
 }
@@ -394,10 +394,16 @@ static int decrypt_hdr(struct crypt_device *cd, struct tcrypt_phdr *hdr,
                                continue;
                        r = decrypt_hdr_one(&tcrypt_cipher[i].cipher[j],
                                            tcrypt_cipher[i].mode, key, &hdr2);
-                       if (r < 0) {
-                               log_dbg("Error %s.", tcrypt_cipher[i].cipher[j].name);
+                       if (r < 0)
                                break;
-                       }
+               }
+
+               if (r < 0) {
+                       log_dbg("TCRYPT:   returned error %d, skipped.", r);
+                       if (r == -ENOTSUP)
+                               break;
+                       r = -ENOENT;
+                       continue;
                }
 
                if (!strncmp(hdr2.d.magic, TCRYPT_HDR_MAGIC, TCRYPT_HDR_MAGIC_LEN)) {
@@ -460,7 +466,7 @@ static int TCRYPT_init_hdr(struct crypt_device *cd,
        unsigned char pwd[TCRYPT_KEY_POOL_LEN] = {};
        size_t passphrase_size;
        char *key;
-       int r = -EINVAL, i, legacy_modes;
+       int r = -EINVAL, i, legacy_modes, skipped = 0;
 
        if (posix_memalign((void*)&key, crypt_getpagesize(), TCRYPT_HDR_KEY_LEN))
                return -ENOMEM;
@@ -498,19 +504,27 @@ static int TCRYPT_init_hdr(struct crypt_device *cd,
 
                /* Decrypt header */
                r = decrypt_hdr(cd, hdr, key, legacy_modes);
+               if (r == -ENOENT) {
+                       skipped++;
+                       continue;
+               }
                if (r != -EPERM)
                        break;
        }
 
+       if (skipped == i || r == -ENOTSUP)
+               log_err(cd, _("Required kernel crypto interface not available.\n"
+                             "Ensure you have algif_skcipher kernel module loaded.\n"));
        if (r < 0)
                goto out;
 
        r = hdr_from_disk(hdr, params, i, r);
        if (!r) {
                log_dbg("TCRYPT: Header version: %d, req. %d, sector %d"
-                       ", PBKDF2 hash %s", (int)hdr->d.version,
+                       ", mk_offset %" PRIu64 ", hidden_size %" PRIu64
+                       ", volume size %" PRIu64, (int)hdr->d.version,
                        (int)hdr->d.version_tc, (int)hdr->d.sector_size,
-                       params->hash_name);
+                       hdr->d.mk_offset, hdr->d.hidden_volume_size, hdr->d.volume_size);
                log_dbg("TCRYPT: Header cipher %s-%s, key size %d",
                        params->cipher, params->mode, params->key_size);
        }
@@ -528,7 +542,7 @@ int TCRYPT_read_phdr(struct crypt_device *cd,
 {
        struct device *device = crypt_metadata_device(cd);
        ssize_t hdr_size = sizeof(struct tcrypt_phdr);
-       int devfd = 0, r = -EIO, bs;
+       int devfd = 0, r, bs;
 
        assert(sizeof(struct tcrypt_phdr) == 512);
 
@@ -545,13 +559,24 @@ int TCRYPT_read_phdr(struct crypt_device *cd,
                return -EINVAL;
        }
 
+       r = -EIO;
        if (params->flags & CRYPT_TCRYPT_HIDDEN_HEADER) {
-               if (lseek(devfd, TCRYPT_HDR_HIDDEN_OFFSET, SEEK_SET) >= 0 &&
-                   read_blockwise(devfd, bs, hdr, hdr_size) == hdr_size)
-                       r = TCRYPT_init_hdr(cd, hdr, params);
-               if (r &&
-                   lseek(devfd, TCRYPT_HDR_HIDDEN_OFFSET_OLD, SEEK_END) >= 0 &&
-                   read_blockwise(devfd, bs, hdr, hdr_size) == hdr_size)
+               if (params->flags & CRYPT_TCRYPT_BACKUP_HEADER) {
+                       if (lseek(devfd, TCRYPT_HDR_HIDDEN_OFFSET_BCK, SEEK_END) >= 0 &&
+                           read_blockwise(devfd, bs, hdr, hdr_size) == hdr_size)
+                               r = TCRYPT_init_hdr(cd, hdr, params);
+               } else {
+                       if (lseek(devfd, TCRYPT_HDR_HIDDEN_OFFSET, SEEK_SET) >= 0 &&
+                           read_blockwise(devfd, bs, hdr, hdr_size) == hdr_size)
+                               r = TCRYPT_init_hdr(cd, hdr, params);
+                       if (r &&
+                           lseek(devfd, TCRYPT_HDR_HIDDEN_OFFSET_OLD, SEEK_END) >= 0 &&
+                           read_blockwise(devfd, bs, hdr, hdr_size) == hdr_size)
+                               r = TCRYPT_init_hdr(cd, hdr, params);
+               }
+       } else if (params->flags & CRYPT_TCRYPT_BACKUP_HEADER) {
+               if (lseek(devfd, TCRYPT_HDR_OFFSET_BCK, SEEK_END) >= 0 &&
+                           read_blockwise(devfd, bs, hdr, hdr_size) == hdr_size)
                        r = TCRYPT_init_hdr(cd, hdr, params);
        } else if (read_blockwise(devfd, bs, hdr, hdr_size) == hdr_size)
                r = TCRYPT_init_hdr(cd, hdr, params);
@@ -616,6 +641,11 @@ int TCRYPT_activate(struct crypt_device *cd,
        if (!algs)
                return -EINVAL;
 
+       if (params->flags & CRYPT_TCRYPT_HIDDEN_HEADER)
+               dmd.size = hdr->d.hidden_volume_size / hdr->d.sector_size;
+       else
+               dmd.size = hdr->d.volume_size / hdr->d.sector_size;
+
        r = device_block_adjust(cd, dmd.data_device, DEV_EXCL,
                                dmd.u.crypt.offset, &dmd.size, &dmd.flags);
        if (r)
@@ -791,19 +821,49 @@ int TCRYPT_init_by_name(struct crypt_device *cd, const char *name,
        return 0;
 }
 
-uint64_t TCRYPT_get_data_offset(struct tcrypt_phdr *hdr)
+uint64_t TCRYPT_get_data_offset(struct crypt_device *cd,
+                                struct tcrypt_phdr *hdr,
+                                struct crypt_params_tcrypt *params)
 {
+       uint64_t size;
+
+       if (params->mode && !strncmp(params->mode, "xts", 3)) {
+               if (hdr->d.version < 3)
+                       return 1;
+
+               if (params->flags & CRYPT_TCRYPT_HIDDEN_HEADER) {
+                       if (hdr->d.version > 3)
+                               return (hdr->d.mk_offset / hdr->d.sector_size);
+                       if (device_size(crypt_metadata_device(cd), &size) < 0)
+                               return 0;
+                       return (size - hdr->d.hidden_volume_size +
+                               (TCRYPT_HDR_HIDDEN_OFFSET_OLD)) / hdr->d.sector_size;
+               }
+               return (hdr->d.mk_offset / hdr->d.sector_size);
+       }
+
+       if (params->flags & CRYPT_TCRYPT_HIDDEN_HEADER) {
+               if (device_size(crypt_metadata_device(cd), &size) < 0)
+                       return 0;
+               return (size - hdr->d.hidden_volume_size +
+                       (TCRYPT_HDR_HIDDEN_OFFSET_OLD)) / hdr->d.sector_size;
+       }
+
        // FIXME: system vol.
-       if (!hdr->d.mk_offset)
-               return 1;
-       return (hdr->d.mk_offset / hdr->d.sector_size);
+       return hdr->d.mk_offset / hdr->d.sector_size;
 }
 
-uint64_t TCRYPT_get_iv_offset(struct tcrypt_phdr *hdr)
+uint64_t TCRYPT_get_iv_offset(struct crypt_device *cd,
+                             struct tcrypt_phdr *hdr,
+                             struct crypt_params_tcrypt *params
+)
 {
-       if (!hdr->d.mk_offset)
+       if (params->mode && !strncmp(params->mode, "xts", 3))
+               return TCRYPT_get_data_offset(cd, hdr, params);
+       else if (params->mode && !strncmp(params->mode, "lrw", 3))
                return 0;
-       return (hdr->d.mk_offset / hdr->d.sector_size);
+
+       return hdr->d.mk_offset / hdr->d.sector_size;
 }
 
 int TCRYPT_get_volume_key(struct crypt_device *cd,