linux/drivers/block/cryptoloop.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-or-later
   2/*
   3   Linux loop encryption enabling module
   4
   5   Copyright (C)  2002 Herbert Valerio Riedel <hvr@gnu.org>
   6   Copyright (C)  2003 Fruhwirth Clemens <clemens@endorphin.org>
   7
   8 */
   9
  10#include <linux/module.h>
  11
  12#include <crypto/skcipher.h>
  13#include <linux/init.h>
  14#include <linux/string.h>
  15#include <linux/blkdev.h>
  16#include <linux/scatterlist.h>
  17#include <linux/uaccess.h>
  18#include "loop.h"
  19
  20MODULE_LICENSE("GPL");
  21MODULE_DESCRIPTION("loop blockdevice transferfunction adaptor / CryptoAPI");
  22MODULE_AUTHOR("Herbert Valerio Riedel <hvr@gnu.org>");
  23
  24#define LOOP_IV_SECTOR_BITS 9
  25#define LOOP_IV_SECTOR_SIZE (1 << LOOP_IV_SECTOR_BITS)
  26
  27static int
  28cryptoloop_init(struct loop_device *lo, const struct loop_info64 *info)
  29{
  30        int err = -EINVAL;
  31        int cipher_len;
  32        int mode_len;
  33        char cms[LO_NAME_SIZE];                 /* cipher-mode string */
  34        char *mode;
  35        char *cmsp = cms;                       /* c-m string pointer */
  36        struct crypto_sync_skcipher *tfm;
  37
  38        /* encryption breaks for non sector aligned offsets */
  39
  40        if (info->lo_offset % LOOP_IV_SECTOR_SIZE)
  41                goto out;
  42
  43        strncpy(cms, info->lo_crypt_name, LO_NAME_SIZE);
  44        cms[LO_NAME_SIZE - 1] = 0;
  45
  46        cipher_len = strcspn(cmsp, "-");
  47
  48        mode = cmsp + cipher_len;
  49        mode_len = 0;
  50        if (*mode) {
  51                mode++;
  52                mode_len = strcspn(mode, "-");
  53        }
  54
  55        if (!mode_len) {
  56                mode = "cbc";
  57                mode_len = 3;
  58        }
  59
  60        if (cipher_len + mode_len + 3 > LO_NAME_SIZE)
  61                return -EINVAL;
  62
  63        memmove(cms, mode, mode_len);
  64        cmsp = cms + mode_len;
  65        *cmsp++ = '(';
  66        memcpy(cmsp, info->lo_crypt_name, cipher_len);
  67        cmsp += cipher_len;
  68        *cmsp++ = ')';
  69        *cmsp = 0;
  70
  71        tfm = crypto_alloc_sync_skcipher(cms, 0, 0);
  72        if (IS_ERR(tfm))
  73                return PTR_ERR(tfm);
  74
  75        err = crypto_sync_skcipher_setkey(tfm, info->lo_encrypt_key,
  76                                          info->lo_encrypt_key_size);
  77
  78        if (err != 0)
  79                goto out_free_tfm;
  80
  81        lo->key_data = tfm;
  82        return 0;
  83
  84 out_free_tfm:
  85        crypto_free_sync_skcipher(tfm);
  86
  87 out:
  88        return err;
  89}
  90
  91
  92typedef int (*encdec_cbc_t)(struct skcipher_request *req);
  93
  94static int
  95cryptoloop_transfer(struct loop_device *lo, int cmd,
  96                    struct page *raw_page, unsigned raw_off,
  97                    struct page *loop_page, unsigned loop_off,
  98                    int size, sector_t IV)
  99{
 100        struct crypto_sync_skcipher *tfm = lo->key_data;
 101        SYNC_SKCIPHER_REQUEST_ON_STACK(req, tfm);
 102        struct scatterlist sg_out;
 103        struct scatterlist sg_in;
 104
 105        encdec_cbc_t encdecfunc;
 106        struct page *in_page, *out_page;
 107        unsigned in_offs, out_offs;
 108        int err;
 109
 110        skcipher_request_set_sync_tfm(req, tfm);
 111        skcipher_request_set_callback(req, CRYPTO_TFM_REQ_MAY_SLEEP,
 112                                      NULL, NULL);
 113
 114        sg_init_table(&sg_out, 1);
 115        sg_init_table(&sg_in, 1);
 116
 117        if (cmd == READ) {
 118                in_page = raw_page;
 119                in_offs = raw_off;
 120                out_page = loop_page;
 121                out_offs = loop_off;
 122                encdecfunc = crypto_skcipher_decrypt;
 123        } else {
 124                in_page = loop_page;
 125                in_offs = loop_off;
 126                out_page = raw_page;
 127                out_offs = raw_off;
 128                encdecfunc = crypto_skcipher_encrypt;
 129        }
 130
 131        while (size > 0) {
 132                const int sz = min(size, LOOP_IV_SECTOR_SIZE);
 133                u32 iv[4] = { 0, };
 134                iv[0] = cpu_to_le32(IV & 0xffffffff);
 135
 136                sg_set_page(&sg_in, in_page, sz, in_offs);
 137                sg_set_page(&sg_out, out_page, sz, out_offs);
 138
 139                skcipher_request_set_crypt(req, &sg_in, &sg_out, sz, iv);
 140                err = encdecfunc(req);
 141                if (err)
 142                        goto out;
 143
 144                IV++;
 145                size -= sz;
 146                in_offs += sz;
 147                out_offs += sz;
 148        }
 149
 150        err = 0;
 151
 152out:
 153        skcipher_request_zero(req);
 154        return err;
 155}
 156
 157static int
 158cryptoloop_ioctl(struct loop_device *lo, int cmd, unsigned long arg)
 159{
 160        return -EINVAL;
 161}
 162
 163static int
 164cryptoloop_release(struct loop_device *lo)
 165{
 166        struct crypto_sync_skcipher *tfm = lo->key_data;
 167        if (tfm != NULL) {
 168                crypto_free_sync_skcipher(tfm);
 169                lo->key_data = NULL;
 170                return 0;
 171        }
 172        printk(KERN_ERR "cryptoloop_release(): tfm == NULL?\n");
 173        return -EINVAL;
 174}
 175
 176static struct loop_func_table cryptoloop_funcs = {
 177        .number = LO_CRYPT_CRYPTOAPI,
 178        .init = cryptoloop_init,
 179        .ioctl = cryptoloop_ioctl,
 180        .transfer = cryptoloop_transfer,
 181        .release = cryptoloop_release,
 182        .owner = THIS_MODULE
 183};
 184
 185static int __init
 186init_cryptoloop(void)
 187{
 188        int rc = loop_register_transfer(&cryptoloop_funcs);
 189
 190        if (rc)
 191                printk(KERN_ERR "cryptoloop: loop_register_transfer failed\n");
 192        else
 193                pr_warn("the cryptoloop driver has been deprecated and will be removed in in Linux 5.16\n");
 194        return rc;
 195}
 196
 197static void __exit
 198cleanup_cryptoloop(void)
 199{
 200        if (loop_unregister_transfer(LO_CRYPT_CRYPTOAPI))
 201                printk(KERN_ERR
 202                        "cryptoloop: loop_unregister_transfer failed\n");
 203}
 204
 205module_init(init_cryptoloop);
 206module_exit(cleanup_cryptoloop);
 207