lkml.org 
[lkml]   [2013]   [Sep]   [19]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Subject[PATCH 5/7] staging: usbip: Add encryption support to kernel
    Date
    This adds code performing the actual encryption and authentication
    operations in the usbip kernel code. The whole data stream may now be
    encrypted and authenticated with AES-GCM and symmetric 128 bit keys.

    Signed-off-by: Dominik Paulus <dominik.paulus@fau.de>
    Signed-off-by: Tobias Polzer <tobias.polzer@fau.de>
    ---
    drivers/staging/usbip/Kconfig | 2 +-
    drivers/staging/usbip/stub.h | 3 +
    drivers/staging/usbip/stub_dev.c | 8 +
    drivers/staging/usbip/usbip_common.c | 342 ++++++++++++++++++++++++++++++++++-
    drivers/staging/usbip/usbip_common.h | 22 +++
    drivers/staging/usbip/vhci_hcd.c | 4 +-
    drivers/staging/usbip/vhci_sysfs.c | 10 +
    7 files changed, 387 insertions(+), 4 deletions(-)

    diff --git a/drivers/staging/usbip/Kconfig b/drivers/staging/usbip/Kconfig
    index 8860009..87220af 100644
    --- a/drivers/staging/usbip/Kconfig
    +++ b/drivers/staging/usbip/Kconfig
    @@ -1,6 +1,6 @@
    config USBIP_CORE
    tristate "USB/IP support"
    - depends on USB && NET
    + depends on USB && NET && CRYPTO_GCM && CRYPTO_AES
    default N
    ---help---
    This enables pushing USB packets over IP to allow remote
    diff --git a/drivers/staging/usbip/stub.h b/drivers/staging/usbip/stub.h
    index cfe75d1..2aaea3a 100644
    --- a/drivers/staging/usbip/stub.h
    +++ b/drivers/staging/usbip/stub.h
    @@ -26,6 +26,9 @@
    #include <linux/types.h>
    #include <linux/usb.h>
    #include <linux/wait.h>
    +#include <linux/crypto.h>
    +#include <linux/err.h>
    +#include <linux/scatterlist.h>

    #define STUB_BUSID_OTHER 0
    #define STUB_BUSID_REMOV 1
    diff --git a/drivers/staging/usbip/stub_dev.c b/drivers/staging/usbip/stub_dev.c
    index f7bc6e1..0e61efb 100644
    --- a/drivers/staging/usbip/stub_dev.c
    +++ b/drivers/staging/usbip/stub_dev.c
    @@ -21,6 +21,7 @@
    #include <linux/file.h>
    #include <linux/kthread.h>
    #include <linux/module.h>
    +#include <linux/kfifo.h>

    #include "usbip_common.h"
    #include "stub.h"
    @@ -137,6 +138,12 @@ static ssize_t store_sockfd(struct device *dev, struct device_attribute *attr,

    spin_unlock_irq(&sdev->ud.lock);

    + if (sdev->ud.use_crypto) {
    + err = usbip_init_crypto(&sdev->ud, sendkey, recvkey);
    + if (err < 0)
    + goto err;
    + }
    +
    sdev->ud.tcp_rx = kthread_get_run(stub_rx_loop, &sdev->ud,
    "stub_rx");
    sdev->ud.tcp_tx = kthread_get_run(stub_tx_loop, &sdev->ud,
    @@ -298,6 +305,7 @@ static void stub_shutdown_connection(struct usbip_device *ud)
    }

    /* 3. free used data */
    + usbip_deinit_crypto(ud);
    stub_device_cleanup_urbs(sdev);

    /* 4. free stub_unlink */
    diff --git a/drivers/staging/usbip/usbip_common.c b/drivers/staging/usbip/usbip_common.c
    index f46c3d2..0729df6 100644
    --- a/drivers/staging/usbip/usbip_common.c
    +++ b/drivers/staging/usbip/usbip_common.c
    @@ -26,6 +26,8 @@
    #include <linux/module.h>
    #include <linux/moduleparam.h>
    #include <net/sock.h>
    +#include <linux/scatterlist.h>
    +#include <linux/crypto.h>

    #include "usbip_common.h"

    @@ -626,17 +628,353 @@ static void usbip_pack_iso(struct usbip_iso_packet_descriptor *iso,
    }
    }

    +int usbip_init_crypto(struct usbip_device *ud, unsigned char *sendkey, unsigned
    + char *recvkey)
    +{
    + int ret;
    +
    + ud->use_crypto = 1;
    +
    + ud->tfm_recv = crypto_alloc_aead("gcm(aes)", 0, 0);
    + if (IS_ERR(ud->tfm_recv))
    + return -PTR_ERR(ud->tfm_recv);
    + ud->tfm_send = crypto_alloc_aead("gcm(aes)", 0, 0);
    + if (IS_ERR(ud->tfm_send)) {
    + crypto_free_aead(ud->tfm_recv);
    + return -PTR_ERR(ud->tfm_send);
    + }
    + ret = kfifo_alloc(&ud->recv_queue, RECVQ_SIZE, GFP_KERNEL);
    + if (ret) {
    + crypto_free_aead(ud->tfm_recv);
    + crypto_free_aead(ud->tfm_send);
    + return ret;
    + }
    +
    + if (crypto_aead_setkey(ud->tfm_send, sendkey, USBIP_KEYSIZE) != 0 ||
    + crypto_aead_setkey(ud->tfm_recv, recvkey,
    + USBIP_KEYSIZE) != 0 ||
    + crypto_aead_setauthsize(ud->tfm_send,
    + USBIP_AUTHSIZE) != 0 ||
    + crypto_aead_setauthsize(ud->tfm_recv,
    + USBIP_AUTHSIZE)) {
    + crypto_free_aead(ud->tfm_recv);
    + crypto_free_aead(ud->tfm_send);
    + kfifo_free(&ud->recv_queue);
    + }
    +
    + ud->ctr_send = 0;
    + ud->ctr_recv = 0;
    +
    + return 0;
    +}
    +EXPORT_SYMBOL_GPL(usbip_init_crypto);
    +
    +void usbip_deinit_crypto(struct usbip_device *ud)
    +{
    + if (ud->use_crypto) {
    + crypto_free_aead(ud->tfm_send);
    + crypto_free_aead(ud->tfm_recv);
    + kfifo_free(&ud->recv_queue);
    + ud->use_crypto = 0;
    + }
    +}
    +EXPORT_SYMBOL_GPL(usbip_deinit_crypto);
    +
    +struct tcrypt_result {
    + struct completion completion;
    + int err;
    +};
    +
    +static void tcrypt_complete(struct crypto_async_request *req, int err)
    +{
    + struct tcrypt_result *res = req->data;
    +
    + if (err == -EINPROGRESS)
    + return;
    +
    + res->err = err;
    + complete(&res->completion);
    +}
    +
    +/*
    + * Perform encryption/decryption on one chunk of data.
    + * Uses global crypto state stored in usbip_device.
    + * Parameters:
    + * encrypt: 1 to perform encryption, 0 to perform decryption operation
    + * packetsize: Size of the encrypted packet, including the authentication tag,
    + * not including the associated data (length field).
    + * plaintext and ciphertext have to be appropiately managed by the caller
    + * (i.e. they must be at least packetsize bytes long).
    + * Returns: 0 on success
    + */
    +static int usbip_crypt(struct usbip_device *ud, int encrypt, uint32_t
    + packetsize, unsigned char *plaintext, unsigned char
    + *ciphertext)
    +{
    + struct crypto_aead *tfm;
    + struct aead_request *req;
    + struct tcrypt_result result;
    + struct scatterlist plain, cipher, assoc;
    + char iv[16];
    + u64 *iv_num;
    + u64 iv_net;
    + const int plainsize = packetsize - USBIP_AUTHSIZE;
    + int ret;
    +
    + memset(iv, 0, sizeof(iv));
    + if (encrypt) {
    + tfm = ud->tfm_send;
    + iv_num = &ud->ctr_send;
    + } else {
    + tfm = ud->tfm_recv;
    + iv_num = &ud->ctr_recv;
    + }
    + iv_net = cpu_to_be64(*iv_num);
    + memcpy(iv, &iv_net, sizeof(iv_net));
    +
    + req = aead_request_alloc(tfm, GFP_KERNEL);
    + if (IS_ERR(req))
    + return -PTR_ERR(req);
    +
    + init_completion(&result.completion);
    + aead_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG,
    + tcrypt_complete, &result);
    +
    + sg_init_one(&cipher, ciphertext, packetsize);
    + sg_init_one(&plain, plaintext, plainsize);
    + crypto_aead_clear_flags(tfm, ~0);
    +
    + if (encrypt)
    + aead_request_set_crypt(req, &plain, &cipher, plainsize, iv);
    + else
    + aead_request_set_crypt(req, &cipher, &plain, packetsize, iv);
    + packetsize = cpu_to_be32(packetsize);
    + sg_init_one(&assoc, &packetsize, sizeof(packetsize));
    + /* Associated data: Unencrypted length tag */
    + aead_request_set_assoc(req, &assoc, sizeof(packetsize));
    +
    + if (encrypt)
    + ret = crypto_aead_encrypt(req);
    + else
    + ret = crypto_aead_decrypt(req);
    +
    + switch (ret) {
    + case 0: /* Success */
    + break;
    + case -EINPROGRESS:
    + case -EBUSY:
    + wait_for_completion(&result.completion);
    + break;
    + default:
    + aead_request_free(req);
    + return ret;
    + }
    +
    + aead_request_free(req);
    +
    + (*iv_num)++; /* Increment IV */
    +
    + return 0;
    +}
    +
    +/*
    + * Wrapper to kernel_recvmsg. If necessary, also does the necessary decryption.
    + * If decryption is enabled, you _MUST_ pass 1 as parameter for num, i.e.
    + * only receive into a single continuous buffer.
    + */
    int usbip_recvmsg(struct usbip_device *ud, struct msghdr *msg, struct kvec
    *vec, size_t num, size_t size, int flags)
    {
    - return kernel_recvmsg(ud->tcp_socket, msg, vec, num, size, flags);
    + int ret;
    + size_t total = 0;
    + unsigned char *plainbuf, *cipherbuf;
    +
    + /* If crypto is disabled, we just wrap the normal kernel calls. */
    + if (!ud->use_crypto)
    + return kernel_recvmsg(ud->tcp_socket, msg, vec, num, size,
    + flags);
    +
    + if (vec[0].iov_len < size)
    + return -EINVAL;
    + if (num != 1)
    + return -EINVAL;
    +
    + plainbuf = kmalloc(USBIP_PACKETSIZE, GFP_KERNEL);
    + if (IS_ERR(plainbuf))
    + return -PTR_ERR(plainbuf);
    + cipherbuf = kmalloc(USBIP_PACKETSIZE, GFP_KERNEL);
    + if (IS_ERR(cipherbuf)) {
    + kfree(plainbuf);
    + return -PTR_ERR(cipherbuf);
    + }
    +
    + while (total < size) {
    + uint32_t packetsize;
    + struct kvec recvvec;
    +
    + /*
    + * We use a global kfifo to buffer unrequested plaintext bytes.
    + * Flush this buffer first before receiving new data.
    + */
    + if (kfifo_len(&ud->recv_queue)) {
    + size_t next = min_t(size_t, kfifo_len(&ud->recv_queue),
    + size - total);
    + /* No error checking necessary - see previous line */
    + ret = kfifo_out(&ud->recv_queue, ((char *)
    + vec[0].iov_base)+total, next);
    + total += next;
    + continue;
    + }
    +
    + /* See usbip_sendmsg() for the format of one encrypted packet */
    +
    + /*
    + * Receive size of next crypto packet
    + */
    + recvvec.iov_base = &packetsize;
    + recvvec.iov_len = sizeof(packetsize);
    +
    + ret = kernel_recvmsg(ud->tcp_socket, msg, &recvvec, 1,
    + sizeof(packetsize), flags);
    + packetsize = be32_to_cpu(packetsize);
    + if (ret <= 0) {
    + total = ret;
    + goto err;
    + } else if (ret != sizeof(packetsize)) {
    + total = -EBADMSG;
    + goto err;
    + }
    +
    + if (packetsize > USBIP_PACKETSIZE) {
    + total = -EBADMSG;
    + goto err;
    + }
    +
    + /*
    + * Receive the rest of the packet
    + */
    + recvvec.iov_base = cipherbuf;
    + recvvec.iov_len = packetsize;
    + ret = kernel_recvmsg(ud->tcp_socket, msg, &recvvec, 1,
    + packetsize, flags);
    + if (ret <= 0) {
    + total = ret;
    + goto err;
    + } else if (ret != packetsize) {
    + total = -EBADMSG;
    + goto err;
    + }
    +
    + /*
    + * Decrypt the packet. This will also authenticate the length
    + * field
    + */
    + ret = usbip_crypt(ud, 0, packetsize, plainbuf, cipherbuf);
    + if (ret != 0) {
    + total = ret;
    + goto err;
    + }
    +
    + /*
    + * Add this packet to our global buffer (It will be stored in
    + * the user buffer in the next loop iteration) No error
    + * checking necessary - we already know the packet is going to
    + * fit.
    + */
    + (void) kfifo_in(&ud->recv_queue, plainbuf, packetsize -
    + USBIP_AUTHSIZE);
    + }
    +
    +err:
    + kfree(plainbuf);
    + kfree(cipherbuf);
    +
    + return total;
    }
    EXPORT_SYMBOL_GPL(usbip_recvmsg);

    int usbip_sendmsg(struct usbip_device *ud, struct msghdr *msg, struct kvec
    *vec, size_t num, size_t size)
    {
    - return kernel_sendmsg(ud->tcp_socket, msg, vec, num, size);
    + int i = 0, ret, offset = 0;
    + size_t total = 0;
    + unsigned char *cipherbuf;
    +
    + /* If crypto is disabled, we just wrap the normal kernel calls. */
    + if (!ud->use_crypto)
    + return kernel_sendmsg(ud->tcp_socket, msg, vec, num, size);
    +
    + cipherbuf = kmalloc(USBIP_PACKETSIZE, GFP_KERNEL);
    + if (IS_ERR(cipherbuf))
    + return -PTR_ERR(cipherbuf);
    +
    + /*
    + * The receiver has to decrypt whole packets. To avoid the need
    + * to allocate large buffers at the receiving side, we split the
    + * data to be sent in USBIP_PACKETSIZE large chunks that can be
    + * decrypted separately. See below for the format of each chunk.
    + */
    +
    + /* Iterate over all kvecs, splitting them up as necessary. */
    + for (i = 0; i != num && size; ) {
    + /* Compute the remaining number of bytes to send for
    + * this kvec */
    + const size_t PLAIN_SIZE = min_t(size_t, vec[i].iov_len -
    + offset, min_t(size_t, size, USBIP_PACKETSIZE -
    + USBIP_AUTHSIZE));
    + const size_t PACKET_SIZE = PLAIN_SIZE + USBIP_AUTHSIZE;
    + uint32_t packet_size_net = cpu_to_be32(PACKET_SIZE);
    + struct kvec sendvec[2];
    +
    + if (PLAIN_SIZE == 0) {
    + ++i;
    + offset = 0;
    + continue;
    + }
    +
    + /*
    + * One encrypted packet consists of:
    + * - An unencrypted, authenticated length tag (exactly 4
    + * bytes) containing the length of the packet.
    + * - Up to USBIP_PACKETSIZE - USBIP_AUTHSIZE bytes of user
    + * payload, encrypted
    + * - Exactly USBIP_AUTHSIZE bytes authentication tag.
    + * Note: The packet length is also authenticated, but has
    + * - for obvious reasons - to be sent in plaintext. This
    + * packet format will be parsed by usbip_recvmsg (see above).
    + */
    + ret = usbip_crypt(ud, 1, PACKET_SIZE, vec[i].iov_base + offset,
    + cipherbuf);
    + if (ret != 0) {
    + kfree(cipherbuf);
    + return ret;
    + }
    +
    + /* Length field */
    + sendvec[0].iov_base = &packet_size_net;
    + sendvec[0].iov_len = sizeof(packet_size_net);
    + /* Payload and authentication tag */
    + sendvec[1].iov_base = cipherbuf;
    + sendvec[1].iov_len = PACKET_SIZE;
    + ret = kernel_sendmsg(ud->tcp_socket, msg, sendvec,
    + ARRAY_SIZE(sendvec), sendvec[0].iov_len +
    + sendvec[1].iov_len);
    + if (ret < 0) {
    + kfree(cipherbuf);
    + return ret;
    + }
    + if (ret != sendvec[0].iov_len + sendvec[1].iov_len) {
    + kfree(cipherbuf);
    + return -EPROTO;
    + }
    + offset += PLAIN_SIZE;
    + size -= PLAIN_SIZE;
    + total += PLAIN_SIZE;
    + }
    +
    + kfree(cipherbuf);
    +
    + return total;
    }
    EXPORT_SYMBOL_GPL(usbip_sendmsg);

    diff --git a/drivers/staging/usbip/usbip_common.h b/drivers/staging/usbip/usbip_common.h
    index 99f9dab..2b71c58 100644
    --- a/drivers/staging/usbip/usbip_common.h
    +++ b/drivers/staging/usbip/usbip_common.h
    @@ -29,15 +29,28 @@
    #include <linux/types.h>
    #include <linux/usb.h>
    #include <linux/wait.h>
    +#include <linux/kfifo.h>

    #define USBIP_VERSION "1.0.0"

    /*
    + * Length of the authentication tag associated with each packet, in bytes. Can
    + * be set to 4, 8, 12, 13, 14, 15 or 16. See crypto_gcm_setauthsize in
    + * crypto/gcm.c. Increasing this will increase crypto protocol overhead.
    + */
    +#define USBIP_AUTHSIZE 4
    +/*
    * Length of symmetric keys. Currently, this should be fixed at 16 bytes.
    * Will break code if changed, look at userspace and stub_dev.c/vhci_sysfs.c
    * where this constant is used before changing.
    */
    #define USBIP_KEYSIZE 16
    +/*
    + * Maximum size of encrypted packets. Decreasing this will increase overhead
    + * and decrease memory usage.
    + */
    +#define USBIP_PACKETSIZE 1024
    +#define RECVQ_SIZE (2*USBIP_PACKETSIZE)

    #undef pr_fmt

    @@ -300,6 +313,11 @@ struct usbip_device {

    /* Crypto support */
    int use_crypto;
    + struct crypto_aead *tfm_recv;
    + struct crypto_aead *tfm_send;
    + /* Counters to be used as IVs */
    + u64 ctr_send, ctr_recv;
    + DECLARE_KFIFO_PTR(recv_queue, char);
    };

    #define kthread_get_run(threadfn, data, namefmt, ...) \
    @@ -323,6 +341,10 @@ struct usbip_device {
    void usbip_dump_urb(struct urb *purb);
    void usbip_dump_header(struct usbip_header *pdu);

    +int usbip_init_crypto(struct usbip_device *ud, unsigned char *sendkey,
    + unsigned char *recvkey);
    +void usbip_deinit_crypto(struct usbip_device *ud);
    +
    int usbip_recv(struct usbip_device *ui, void *buf, int size);
    struct socket *sockfd_to_socket(unsigned int sockfd);

    diff --git a/drivers/staging/usbip/vhci_hcd.c b/drivers/staging/usbip/vhci_hcd.c
    index d7974cb..0b72bcf 100644
    --- a/drivers/staging/usbip/vhci_hcd.c
    +++ b/drivers/staging/usbip/vhci_hcd.c
    @@ -786,7 +786,9 @@ static void vhci_shutdown_connection(struct usbip_device *ud)
    kthread_stop_put(vdev->ud.tcp_tx);
    vdev->ud.tcp_tx = NULL;
    }
    - pr_info("stop threads\n");
    + pr_info("stopped threads\n");
    +
    + usbip_deinit_crypto(&vdev->ud);

    /* active connection is closed */
    if (vdev->ud.tcp_socket) {
    diff --git a/drivers/staging/usbip/vhci_sysfs.c b/drivers/staging/usbip/vhci_sysfs.c
    index 1ef3f25..ce46c16 100644
    --- a/drivers/staging/usbip/vhci_sysfs.c
    +++ b/drivers/staging/usbip/vhci_sysfs.c
    @@ -20,6 +20,8 @@
    #include <linux/kthread.h>
    #include <linux/file.h>
    #include <linux/net.h>
    +#include <linux/crypto.h>
    +#include <linux/kfifo.h>

    #include "usbip_common.h"
    #include "vhci.h"
    @@ -227,6 +229,13 @@ static ssize_t store_attach(struct device *dev, struct device_attribute *attr,
    /* begin a lock */
    spin_lock(&the_controller->lock);
    vdev = port_to_vdev(rhport);
    + if (use_crypto) {
    + int ret = usbip_init_crypto(&vdev->ud, sendkey, recvkey);
    + if (ret < 0) {
    + spin_unlock(&the_controller->lock);
    + return ret;
    + }
    + }
    spin_lock(&vdev->ud.lock);

    if (vdev->ud.status != VDEV_ST_NULL) {
    @@ -237,6 +246,7 @@ static ssize_t store_attach(struct device *dev, struct device_attribute *attr,
    fput(socket->file);

    dev_err(dev, "port %d already used\n", rhport);
    + usbip_deinit_crypto(&vdev->ud);
    return -EINVAL;
    }

    --
    1.8.4


    \
     
     \ /
      Last update: 2013-09-19 16:21    [W:5.208 / U:0.376 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site