diff options
author | Sebastian Huber <sebastian.huber@embedded-brains.de> | 2018-03-27 13:25:16 +0200 |
---|---|---|
committer | Sebastian Huber <sebastian.huber@embedded-brains.de> | 2018-03-27 14:42:46 +0200 |
commit | 66003685895deb1e5bc8192be1b2915d910f5ec0 (patch) | |
tree | 14670fd6e4e79cc07000799a74753ad7947aed38 /freebsd/sys/opencrypto | |
parent | DEVFS(5): Fix warning (diff) | |
download | rtems-libbsd-66003685895deb1e5bc8192be1b2915d910f5ec0.tar.bz2 |
CRYPTO(4): Import from FreeBSD
Diffstat (limited to 'freebsd/sys/opencrypto')
-rw-r--r-- | freebsd/sys/opencrypto/cryptodev.c | 1375 |
1 files changed, 1375 insertions, 0 deletions
diff --git a/freebsd/sys/opencrypto/cryptodev.c b/freebsd/sys/opencrypto/cryptodev.c new file mode 100644 index 00000000..06e2e902 --- /dev/null +++ b/freebsd/sys/opencrypto/cryptodev.c @@ -0,0 +1,1375 @@ +#include <machine/rtems-bsd-kernel-space.h> + +/* $OpenBSD: cryptodev.c,v 1.52 2002/06/19 07:22:46 deraadt Exp $ */ + +/*- + * Copyright (c) 2001 Theo de Raadt + * Copyright (c) 2002-2006 Sam Leffler, Errno Consulting + * Copyright (c) 2014 The FreeBSD Foundation + * All rights reserved. + * + * Portions of this software were developed by John-Mark Gurney + * under sponsorship of the FreeBSD Foundation and + * Rubicon Communications, LLC (Netgate). + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Effort sponsored in part by the Defense Advanced Research Projects + * Agency (DARPA) and Air Force Research Laboratory, Air Force + * Materiel Command, USAF, under agreement number F30602-01-2-0537. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <rtems/bsd/local/opt_compat.h> + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/malloc.h> +#include <sys/mbuf.h> +#include <sys/lock.h> +#include <sys/mutex.h> +#include <sys/sysctl.h> +#include <sys/file.h> +#include <sys/filedesc.h> +#include <sys/errno.h> +#include <sys/uio.h> +#include <sys/random.h> +#include <sys/conf.h> +#include <sys/kernel.h> +#include <sys/module.h> +#include <sys/fcntl.h> +#include <sys/bus.h> +#include <sys/user.h> +#include <sys/sdt.h> + +#include <opencrypto/cryptodev.h> +#include <opencrypto/xform.h> + +SDT_PROVIDER_DECLARE(opencrypto); + +SDT_PROBE_DEFINE1(opencrypto, dev, ioctl, error, "int"/*line number*/); + +#ifdef COMPAT_FREEBSD32 +#include <sys/mount.h> +#include <compat/freebsd32/freebsd32.h> + +struct session_op32 { + u_int32_t cipher; + u_int32_t mac; + u_int32_t keylen; + u_int32_t key; + int mackeylen; + u_int32_t mackey; + u_int32_t ses; +}; + +struct session2_op32 { + u_int32_t cipher; + u_int32_t mac; + u_int32_t keylen; + u_int32_t key; + int mackeylen; + u_int32_t mackey; + u_int32_t ses; + int crid; + int pad[4]; +}; + +struct crypt_op32 { + u_int32_t ses; + u_int16_t op; + u_int16_t flags; + u_int len; + u_int32_t src, dst; + u_int32_t mac; + u_int32_t iv; +}; + +struct crparam32 { + u_int32_t crp_p; + u_int crp_nbits; +}; + +struct crypt_kop32 { + u_int crk_op; + u_int crk_status; + u_short crk_iparams; + u_short crk_oparams; + u_int crk_crid; + struct crparam32 crk_param[CRK_MAXPARAM]; +}; + +struct cryptotstat32 { + struct timespec32 acc; + struct timespec32 min; + struct timespec32 max; + u_int32_t count; +}; + +struct cryptostats32 { + u_int32_t cs_ops; + u_int32_t cs_errs; + u_int32_t cs_kops; + u_int32_t cs_kerrs; + u_int32_t cs_intrs; + u_int32_t cs_rets; + u_int32_t cs_blocks; + u_int32_t cs_kblocks; + struct cryptotstat32 cs_invoke; + struct cryptotstat32 cs_done; + struct cryptotstat32 cs_cb; + struct cryptotstat32 cs_finis; +}; + +#define CIOCGSESSION32 _IOWR('c', 101, struct session_op32) +#define CIOCCRYPT32 _IOWR('c', 103, struct crypt_op32) +#define CIOCKEY32 _IOWR('c', 104, struct crypt_kop32) +#define CIOCGSESSION232 _IOWR('c', 106, struct session2_op32) +#define CIOCKEY232 _IOWR('c', 107, struct crypt_kop32) + +static void +session_op_from_32(const struct session_op32 *from, struct session_op *to) +{ + + CP(*from, *to, cipher); + CP(*from, *to, mac); + CP(*from, *to, keylen); + PTRIN_CP(*from, *to, key); + CP(*from, *to, mackeylen); + PTRIN_CP(*from, *to, mackey); + CP(*from, *to, ses); +} + +static void +session2_op_from_32(const struct session2_op32 *from, struct session2_op *to) +{ + + session_op_from_32((const struct session_op32 *)from, + (struct session_op *)to); + CP(*from, *to, crid); +} + +static void +session_op_to_32(const struct session_op *from, struct session_op32 *to) +{ + + CP(*from, *to, cipher); + CP(*from, *to, mac); + CP(*from, *to, keylen); + PTROUT_CP(*from, *to, key); + CP(*from, *to, mackeylen); + PTROUT_CP(*from, *to, mackey); + CP(*from, *to, ses); +} + +static void +session2_op_to_32(const struct session2_op *from, struct session2_op32 *to) +{ + + session_op_to_32((const struct session_op *)from, + (struct session_op32 *)to); + CP(*from, *to, crid); +} + +static void +crypt_op_from_32(const struct crypt_op32 *from, struct crypt_op *to) +{ + + CP(*from, *to, ses); + CP(*from, *to, op); + CP(*from, *to, flags); + CP(*from, *to, len); + PTRIN_CP(*from, *to, src); + PTRIN_CP(*from, *to, dst); + PTRIN_CP(*from, *to, mac); + PTRIN_CP(*from, *to, iv); +} + +static void +crypt_op_to_32(const struct crypt_op *from, struct crypt_op32 *to) +{ + + CP(*from, *to, ses); + CP(*from, *to, op); + CP(*from, *to, flags); + CP(*from, *to, len); + PTROUT_CP(*from, *to, src); + PTROUT_CP(*from, *to, dst); + PTROUT_CP(*from, *to, mac); + PTROUT_CP(*from, *to, iv); +} + +static void +crparam_from_32(const struct crparam32 *from, struct crparam *to) +{ + + PTRIN_CP(*from, *to, crp_p); + CP(*from, *to, crp_nbits); +} + +static void +crparam_to_32(const struct crparam *from, struct crparam32 *to) +{ + + PTROUT_CP(*from, *to, crp_p); + CP(*from, *to, crp_nbits); +} + +static void +crypt_kop_from_32(const struct crypt_kop32 *from, struct crypt_kop *to) +{ + int i; + + CP(*from, *to, crk_op); + CP(*from, *to, crk_status); + CP(*from, *to, crk_iparams); + CP(*from, *to, crk_oparams); + CP(*from, *to, crk_crid); + for (i = 0; i < CRK_MAXPARAM; i++) + crparam_from_32(&from->crk_param[i], &to->crk_param[i]); +} + +static void +crypt_kop_to_32(const struct crypt_kop *from, struct crypt_kop32 *to) +{ + int i; + + CP(*from, *to, crk_op); + CP(*from, *to, crk_status); + CP(*from, *to, crk_iparams); + CP(*from, *to, crk_oparams); + CP(*from, *to, crk_crid); + for (i = 0; i < CRK_MAXPARAM; i++) + crparam_to_32(&from->crk_param[i], &to->crk_param[i]); +} +#endif + +struct csession { + TAILQ_ENTRY(csession) next; + u_int64_t sid; + u_int32_t ses; + struct mtx lock; /* for op submission */ + + u_int32_t cipher; + struct enc_xform *txform; + u_int32_t mac; + struct auth_hash *thash; + + caddr_t key; + int keylen; + u_char tmp_iv[EALG_MAX_BLOCK_LEN]; + + caddr_t mackey; + int mackeylen; + + struct iovec iovec; + struct uio uio; + int error; +}; + +struct fcrypt { + TAILQ_HEAD(csessionlist, csession) csessions; + int sesn; +}; + +static int cryptof_ioctl(struct file *, u_long, void *, + struct ucred *, struct thread *); +static int cryptof_stat(struct file *, struct stat *, + struct ucred *, struct thread *); +static int cryptof_close(struct file *, struct thread *); +static int cryptof_fill_kinfo(struct file *, struct kinfo_file *, + struct filedesc *); + +static struct fileops cryptofops = { + .fo_read = invfo_rdwr, + .fo_write = invfo_rdwr, + .fo_truncate = invfo_truncate, + .fo_ioctl = cryptof_ioctl, + .fo_poll = invfo_poll, + .fo_kqfilter = invfo_kqfilter, + .fo_stat = cryptof_stat, + .fo_close = cryptof_close, + .fo_chmod = invfo_chmod, + .fo_chown = invfo_chown, + .fo_sendfile = invfo_sendfile, + .fo_fill_kinfo = cryptof_fill_kinfo, +}; + +static struct csession *csefind(struct fcrypt *, u_int); +static int csedelete(struct fcrypt *, struct csession *); +static struct csession *cseadd(struct fcrypt *, struct csession *); +static struct csession *csecreate(struct fcrypt *, u_int64_t, caddr_t, + u_int64_t, caddr_t, u_int64_t, u_int32_t, u_int32_t, struct enc_xform *, + struct auth_hash *); +static int csefree(struct csession *); + +static int cryptodev_op(struct csession *, struct crypt_op *, + struct ucred *, struct thread *td); +static int cryptodev_aead(struct csession *, struct crypt_aead *, + struct ucred *, struct thread *); +static int cryptodev_key(struct crypt_kop *); +static int cryptodev_find(struct crypt_find_op *); + +/* + * Check a crypto identifier to see if it requested + * a software device/driver. This can be done either + * by device name/class or through search constraints. + */ +static int +checkforsoftware(int *cridp) +{ + int crid; + + crid = *cridp; + + if (!crypto_devallowsoft) { + if (crid & CRYPTOCAP_F_SOFTWARE) { + if (crid & CRYPTOCAP_F_HARDWARE) { + *cridp = CRYPTOCAP_F_HARDWARE; + return 0; + } + return EINVAL; + } + if ((crid & CRYPTOCAP_F_HARDWARE) == 0 && + (crypto_getcaps(crid) & CRYPTOCAP_F_HARDWARE) == 0) + return EINVAL; + } + return 0; +} + +/* ARGSUSED */ +static int +cryptof_ioctl( + struct file *fp, + u_long cmd, + void *data, + struct ucred *active_cred, + struct thread *td) +{ +#define SES2(p) ((struct session2_op *)p) + struct cryptoini cria, crie; + struct fcrypt *fcr = fp->f_data; + struct csession *cse; + struct session_op *sop; + struct crypt_op *cop; + struct crypt_aead *caead; + struct enc_xform *txform = NULL; + struct auth_hash *thash = NULL; + struct crypt_kop *kop; + u_int64_t sid; + u_int32_t ses; + int error = 0, crid; +#ifdef COMPAT_FREEBSD32 + struct session2_op sopc; + struct crypt_op copc; + struct crypt_kop kopc; +#endif + + switch (cmd) { + case CIOCGSESSION: + case CIOCGSESSION2: +#ifdef COMPAT_FREEBSD32 + case CIOCGSESSION32: + case CIOCGSESSION232: + if (cmd == CIOCGSESSION32) { + session_op_from_32(data, (struct session_op *)&sopc); + sop = (struct session_op *)&sopc; + } else if (cmd == CIOCGSESSION232) { + session2_op_from_32(data, &sopc); + sop = (struct session_op *)&sopc; + } else +#endif + sop = (struct session_op *)data; + switch (sop->cipher) { + case 0: + break; + case CRYPTO_DES_CBC: + txform = &enc_xform_des; + break; + case CRYPTO_3DES_CBC: + txform = &enc_xform_3des; + break; + case CRYPTO_BLF_CBC: + txform = &enc_xform_blf; + break; + case CRYPTO_CAST_CBC: + txform = &enc_xform_cast5; + break; + case CRYPTO_SKIPJACK_CBC: + txform = &enc_xform_skipjack; + break; + case CRYPTO_AES_CBC: + txform = &enc_xform_rijndael128; + break; + case CRYPTO_AES_XTS: + txform = &enc_xform_aes_xts; + break; + case CRYPTO_NULL_CBC: + txform = &enc_xform_null; + break; + case CRYPTO_ARC4: + txform = &enc_xform_arc4; + break; + case CRYPTO_CAMELLIA_CBC: + txform = &enc_xform_camellia; + break; + case CRYPTO_AES_ICM: + txform = &enc_xform_aes_icm; + break; + case CRYPTO_AES_NIST_GCM_16: + txform = &enc_xform_aes_nist_gcm; + break; + + default: + CRYPTDEB("invalid cipher"); + return (EINVAL); + } + + switch (sop->mac) { + case 0: + break; + case CRYPTO_MD5_HMAC: + thash = &auth_hash_hmac_md5; + break; + case CRYPTO_SHA1_HMAC: + thash = &auth_hash_hmac_sha1; + break; + case CRYPTO_SHA2_256_HMAC: + thash = &auth_hash_hmac_sha2_256; + break; + case CRYPTO_SHA2_384_HMAC: + thash = &auth_hash_hmac_sha2_384; + break; + case CRYPTO_SHA2_512_HMAC: + thash = &auth_hash_hmac_sha2_512; + break; + case CRYPTO_RIPEMD160_HMAC: + thash = &auth_hash_hmac_ripemd_160; + break; + case CRYPTO_AES_128_NIST_GMAC: + thash = &auth_hash_nist_gmac_aes_128; + break; + case CRYPTO_AES_192_NIST_GMAC: + thash = &auth_hash_nist_gmac_aes_192; + break; + case CRYPTO_AES_256_NIST_GMAC: + thash = &auth_hash_nist_gmac_aes_256; + break; + +#ifdef notdef + case CRYPTO_MD5: + thash = &auth_hash_md5; + break; + case CRYPTO_SHA1: + thash = &auth_hash_sha1; + break; +#endif + case CRYPTO_NULL_HMAC: + thash = &auth_hash_null; + break; + default: + CRYPTDEB("invalid mac"); + return (EINVAL); + } + + bzero(&crie, sizeof(crie)); + bzero(&cria, sizeof(cria)); + + if (txform) { + crie.cri_alg = txform->type; + crie.cri_klen = sop->keylen * 8; + if (sop->keylen > txform->maxkey || + sop->keylen < txform->minkey) { + CRYPTDEB("invalid cipher parameters"); + error = EINVAL; + goto bail; + } + + crie.cri_key = malloc(crie.cri_klen / 8, + M_XDATA, M_WAITOK); + if ((error = copyin(sop->key, crie.cri_key, + crie.cri_klen / 8))) { + CRYPTDEB("invalid key"); + goto bail; + } + if (thash) + crie.cri_next = &cria; + } + + if (thash) { + cria.cri_alg = thash->type; + cria.cri_klen = sop->mackeylen * 8; + if (sop->mackeylen != thash->keysize) { + CRYPTDEB("invalid mac key length"); + error = EINVAL; + goto bail; + } + + if (cria.cri_klen) { + cria.cri_key = malloc(cria.cri_klen / 8, + M_XDATA, M_WAITOK); + if ((error = copyin(sop->mackey, cria.cri_key, + cria.cri_klen / 8))) { + CRYPTDEB("invalid mac key"); + goto bail; + } + } + } + + /* NB: CIOCGSESSION2 has the crid */ + if (cmd == CIOCGSESSION2 +#ifdef COMPAT_FREEBSD32 + || cmd == CIOCGSESSION232 +#endif + ) { + crid = SES2(sop)->crid; + error = checkforsoftware(&crid); + if (error) { + CRYPTDEB("checkforsoftware"); + goto bail; + } + } else + crid = CRYPTOCAP_F_HARDWARE; + error = crypto_newsession(&sid, (txform ? &crie : &cria), crid); + if (error) { + CRYPTDEB("crypto_newsession"); + goto bail; + } + + cse = csecreate(fcr, sid, crie.cri_key, crie.cri_klen, + cria.cri_key, cria.cri_klen, sop->cipher, sop->mac, txform, + thash); + + if (cse == NULL) { + crypto_freesession(sid); + error = EINVAL; + CRYPTDEB("csecreate"); + goto bail; + } + sop->ses = cse->ses; + if (cmd == CIOCGSESSION2 +#ifdef COMPAT_FREEBSD32 + || cmd == CIOCGSESSION232 +#endif + ) { + /* return hardware/driver id */ + SES2(sop)->crid = CRYPTO_SESID2HID(cse->sid); + } +bail: + if (error) { + if (crie.cri_key) + free(crie.cri_key, M_XDATA); + if (cria.cri_key) + free(cria.cri_key, M_XDATA); + } +#ifdef COMPAT_FREEBSD32 + else { + if (cmd == CIOCGSESSION32) + session_op_to_32(sop, data); + else if (cmd == CIOCGSESSION232) + session2_op_to_32((struct session2_op *)sop, + data); + } +#endif + break; + case CIOCFSESSION: + ses = *(u_int32_t *)data; + cse = csefind(fcr, ses); + if (cse == NULL) + return (EINVAL); + csedelete(fcr, cse); + error = csefree(cse); + break; + case CIOCCRYPT: +#ifdef COMPAT_FREEBSD32 + case CIOCCRYPT32: + if (cmd == CIOCCRYPT32) { + cop = &copc; + crypt_op_from_32(data, cop); + } else +#endif + cop = (struct crypt_op *)data; + cse = csefind(fcr, cop->ses); + if (cse == NULL) { + SDT_PROBE1(opencrypto, dev, ioctl, error, __LINE__); + return (EINVAL); + } + error = cryptodev_op(cse, cop, active_cred, td); +#ifdef COMPAT_FREEBSD32 + if (error == 0 && cmd == CIOCCRYPT32) + crypt_op_to_32(cop, data); +#endif + break; + case CIOCKEY: + case CIOCKEY2: +#ifdef COMPAT_FREEBSD32 + case CIOCKEY32: + case CIOCKEY232: +#endif + if (!crypto_userasymcrypto) + return (EPERM); /* XXX compat? */ +#ifdef COMPAT_FREEBSD32 + if (cmd == CIOCKEY32 || cmd == CIOCKEY232) { + kop = &kopc; + crypt_kop_from_32(data, kop); + } else +#endif + kop = (struct crypt_kop *)data; + if (cmd == CIOCKEY +#ifdef COMPAT_FREEBSD32 + || cmd == CIOCKEY32 +#endif + ) { + /* NB: crypto core enforces s/w driver use */ + kop->crk_crid = + CRYPTOCAP_F_HARDWARE | CRYPTOCAP_F_SOFTWARE; + } + mtx_lock(&Giant); + error = cryptodev_key(kop); + mtx_unlock(&Giant); +#ifdef COMPAT_FREEBSD32 + if (cmd == CIOCKEY32 || cmd == CIOCKEY232) + crypt_kop_to_32(kop, data); +#endif + break; + case CIOCASYMFEAT: + if (!crypto_userasymcrypto) { + /* + * NB: if user asym crypto operations are + * not permitted return "no algorithms" + * so well-behaved applications will just + * fallback to doing them in software. + */ + *(int *)data = 0; + } else + error = crypto_getfeat((int *)data); + break; + case CIOCFINDDEV: + error = cryptodev_find((struct crypt_find_op *)data); + break; + case CIOCCRYPTAEAD: + caead = (struct crypt_aead *)data; + cse = csefind(fcr, caead->ses); + if (cse == NULL) + return (EINVAL); + error = cryptodev_aead(cse, caead, active_cred, td); + break; + default: + error = EINVAL; + break; + } + return (error); +#undef SES2 +} + +static int cryptodev_cb(void *); + + +static int +cryptodev_op( + struct csession *cse, + struct crypt_op *cop, + struct ucred *active_cred, + struct thread *td) +{ + struct cryptop *crp = NULL; + struct cryptodesc *crde = NULL, *crda = NULL; + int error; + + if (cop->len > 256*1024-4) { + SDT_PROBE1(opencrypto, dev, ioctl, error, __LINE__); + return (E2BIG); + } + + if (cse->txform) { + if (cop->len == 0 || (cop->len % cse->txform->blocksize) != 0) { + SDT_PROBE1(opencrypto, dev, ioctl, error, __LINE__); + return (EINVAL); + } + } + + cse->uio.uio_iov = &cse->iovec; + cse->uio.uio_iovcnt = 1; + cse->uio.uio_offset = 0; + cse->uio.uio_resid = cop->len; + cse->uio.uio_segflg = UIO_SYSSPACE; + cse->uio.uio_rw = UIO_WRITE; + cse->uio.uio_td = td; + cse->uio.uio_iov[0].iov_len = cop->len; + if (cse->thash) { + cse->uio.uio_iov[0].iov_len += cse->thash->hashsize; + cse->uio.uio_resid += cse->thash->hashsize; + } + cse->uio.uio_iov[0].iov_base = malloc(cse->uio.uio_iov[0].iov_len, + M_XDATA, M_WAITOK); + + crp = crypto_getreq((cse->txform != NULL) + (cse->thash != NULL)); + if (crp == NULL) { + SDT_PROBE1(opencrypto, dev, ioctl, error, __LINE__); + error = ENOMEM; + goto bail; + } + + if (cse->thash) { + crda = crp->crp_desc; + if (cse->txform) + crde = crda->crd_next; + } else { + if (cse->txform) + crde = crp->crp_desc; + else { + SDT_PROBE1(opencrypto, dev, ioctl, error, __LINE__); + error = EINVAL; + goto bail; + } + } + + if ((error = copyin(cop->src, cse->uio.uio_iov[0].iov_base, + cop->len))) { + SDT_PROBE1(opencrypto, dev, ioctl, error, __LINE__); + goto bail; + } + + if (crda) { + crda->crd_skip = 0; + crda->crd_len = cop->len; + crda->crd_inject = cop->len; + + crda->crd_alg = cse->mac; + crda->crd_key = cse->mackey; + crda->crd_klen = cse->mackeylen * 8; + } + + if (crde) { + if (cop->op == COP_ENCRYPT) + crde->crd_flags |= CRD_F_ENCRYPT; + else + crde->crd_flags &= ~CRD_F_ENCRYPT; + crde->crd_len = cop->len; + crde->crd_inject = 0; + + crde->crd_alg = cse->cipher; + crde->crd_key = cse->key; + crde->crd_klen = cse->keylen * 8; + } + + crp->crp_ilen = cop->len; + crp->crp_flags = CRYPTO_F_IOV | CRYPTO_F_CBIMM + | (cop->flags & COP_F_BATCH); + crp->crp_buf = (caddr_t)&cse->uio; + crp->crp_callback = (int (*) (struct cryptop *)) cryptodev_cb; + crp->crp_sid = cse->sid; + crp->crp_opaque = (void *)cse; + + if (cop->iv) { + if (crde == NULL) { + SDT_PROBE1(opencrypto, dev, ioctl, error, __LINE__); + error = EINVAL; + goto bail; + } + if (cse->cipher == CRYPTO_ARC4) { /* XXX use flag? */ + SDT_PROBE1(opencrypto, dev, ioctl, error, __LINE__); + error = EINVAL; + goto bail; + } + if ((error = copyin(cop->iv, cse->tmp_iv, + cse->txform->blocksize))) { + SDT_PROBE1(opencrypto, dev, ioctl, error, __LINE__); + goto bail; + } + bcopy(cse->tmp_iv, crde->crd_iv, cse->txform->blocksize); + crde->crd_flags |= CRD_F_IV_EXPLICIT | CRD_F_IV_PRESENT; + crde->crd_skip = 0; + } else if (cse->cipher == CRYPTO_ARC4) { /* XXX use flag? */ + crde->crd_skip = 0; + } else if (crde) { + crde->crd_flags |= CRD_F_IV_PRESENT; + crde->crd_skip = cse->txform->blocksize; + crde->crd_len -= cse->txform->blocksize; + } + + if (cop->mac && crda == NULL) { + SDT_PROBE1(opencrypto, dev, ioctl, error, __LINE__); + error = EINVAL; + goto bail; + } + +again: + /* + * Let the dispatch run unlocked, then, interlock against the + * callback before checking if the operation completed and going + * to sleep. This insures drivers don't inherit our lock which + * results in a lock order reversal between crypto_dispatch forced + * entry and the crypto_done callback into us. + */ + error = crypto_dispatch(crp); + mtx_lock(&cse->lock); + if (error == 0 && (crp->crp_flags & CRYPTO_F_DONE) == 0) + error = msleep(crp, &cse->lock, PWAIT, "crydev", 0); + mtx_unlock(&cse->lock); + + if (error != 0) { + SDT_PROBE1(opencrypto, dev, ioctl, error, __LINE__); + goto bail; + } + + if (crp->crp_etype == EAGAIN) { + crp->crp_etype = 0; + crp->crp_flags &= ~CRYPTO_F_DONE; + goto again; + } + + if (crp->crp_etype != 0) { + SDT_PROBE1(opencrypto, dev, ioctl, error, __LINE__); + error = crp->crp_etype; + goto bail; + } + + if (cse->error) { + SDT_PROBE1(opencrypto, dev, ioctl, error, __LINE__); + error = cse->error; + goto bail; + } + + if (cop->dst && + (error = copyout(cse->uio.uio_iov[0].iov_base, cop->dst, + cop->len))) { + SDT_PROBE1(opencrypto, dev, ioctl, error, __LINE__); + goto bail; + } + + if (cop->mac && + (error = copyout((caddr_t)cse->uio.uio_iov[0].iov_base + cop->len, + cop->mac, cse->thash->hashsize))) { + SDT_PROBE1(opencrypto, dev, ioctl, error, __LINE__); + goto bail; + } + +bail: + if (crp) + crypto_freereq(crp); + if (cse->uio.uio_iov[0].iov_base) + free(cse->uio.uio_iov[0].iov_base, M_XDATA); + + return (error); +} + +static int +cryptodev_aead( + struct csession *cse, + struct crypt_aead *caead, + struct ucred *active_cred, + struct thread *td) +{ + struct uio *uio; + struct cryptop *crp = NULL; + struct cryptodesc *crde = NULL, *crda = NULL; + int error; + + if (caead->len > 256*1024-4 || caead->aadlen > 256*1024-4) + return (E2BIG); + + if (cse->txform == NULL || cse->thash == NULL || caead->tag == NULL || + (caead->len % cse->txform->blocksize) != 0) + return (EINVAL); + + uio = &cse->uio; + uio->uio_iov = &cse->iovec; + uio->uio_iovcnt = 1; + uio->uio_offset = 0; + uio->uio_resid = caead->len + caead->aadlen + cse->thash->hashsize; + uio->uio_segflg = UIO_SYSSPACE; + uio->uio_rw = UIO_WRITE; + uio->uio_td = td; + uio->uio_iov[0].iov_len = uio->uio_resid; + + uio->uio_iov[0].iov_base = malloc(uio->uio_iov[0].iov_len, + M_XDATA, M_WAITOK); + + crp = crypto_getreq(2); + if (crp == NULL) { + error = ENOMEM; + goto bail; + } + + crda = crp->crp_desc; + crde = crda->crd_next; + + if ((error = copyin(caead->src, cse->uio.uio_iov[0].iov_base, + caead->len))) + goto bail; + + if ((error = copyin(caead->aad, (char *)cse->uio.uio_iov[0].iov_base + + caead->len, caead->aadlen))) + goto bail; + + crda->crd_skip = caead->len; + crda->crd_len = caead->aadlen; + crda->crd_inject = caead->len + caead->aadlen; + + crda->crd_alg = cse->mac; + crda->crd_key = cse->mackey; + crda->crd_klen = cse->mackeylen * 8; + + if (caead->op == COP_ENCRYPT) + crde->crd_flags |= CRD_F_ENCRYPT; + else + crde->crd_flags &= ~CRD_F_ENCRYPT; + /* crde->crd_skip set below */ + crde->crd_len = caead->len; + crde->crd_inject = 0; + + crde->crd_alg = cse->cipher; + crde->crd_key = cse->key; + crde->crd_klen = cse->keylen * 8; + + crp->crp_ilen = caead->len + caead->aadlen; + crp->crp_flags = CRYPTO_F_IOV | CRYPTO_F_CBIMM + | (caead->flags & COP_F_BATCH); + crp->crp_buf = (caddr_t)&cse->uio.uio_iov; + crp->crp_callback = (int (*) (struct cryptop *)) cryptodev_cb; + crp->crp_sid = cse->sid; + crp->crp_opaque = (void *)cse; + + if (caead->iv) { + if (caead->ivlen > sizeof cse->tmp_iv) { + error = EINVAL; + goto bail; + } + + if ((error = copyin(caead->iv, cse->tmp_iv, caead->ivlen))) + goto bail; + bcopy(cse->tmp_iv, crde->crd_iv, caead->ivlen); + crde->crd_flags |= CRD_F_IV_EXPLICIT | CRD_F_IV_PRESENT; + crde->crd_skip = 0; + } else { + crde->crd_flags |= CRD_F_IV_PRESENT; + crde->crd_skip = cse->txform->blocksize; + crde->crd_len -= cse->txform->blocksize; + } + + if ((error = copyin(caead->tag, (caddr_t)cse->uio.uio_iov[0].iov_base + + caead->len + caead->aadlen, cse->thash->hashsize))) + goto bail; +again: + /* + * Let the dispatch run unlocked, then, interlock against the + * callback before checking if the operation completed and going + * to sleep. This insures drivers don't inherit our lock which + * results in a lock order reversal between crypto_dispatch forced + * entry and the crypto_done callback into us. + */ + error = crypto_dispatch(crp); + mtx_lock(&cse->lock); + if (error == 0 && (crp->crp_flags & CRYPTO_F_DONE) == 0) + error = msleep(crp, &cse->lock, PWAIT, "crydev", 0); + mtx_unlock(&cse->lock); + + if (error != 0) + goto bail; + + if (crp->crp_etype == EAGAIN) { + crp->crp_etype = 0; + crp->crp_flags &= ~CRYPTO_F_DONE; + goto again; + } + + if (crp->crp_etype != 0) { + error = crp->crp_etype; + goto bail; + } + + if (cse->error) { + error = cse->error; + goto bail; + } + + if (caead->dst && (error = copyout(cse->uio.uio_iov[0].iov_base, + caead->dst, caead->len))) + goto bail; + + if ((error = copyout((caddr_t)cse->uio.uio_iov[0].iov_base + + caead->len + caead->aadlen, caead->tag, cse->thash->hashsize))) + goto bail; + +bail: + crypto_freereq(crp); + free(cse->uio.uio_iov[0].iov_base, M_XDATA); + + return (error); +} + +static int +cryptodev_cb(void *op) +{ + struct cryptop *crp = (struct cryptop *) op; + struct csession *cse = (struct csession *)crp->crp_opaque; + + mtx_lock(&cse->lock); + cse->error = crp->crp_etype; + wakeup_one(crp); + mtx_unlock(&cse->lock); + return (0); +} + +static int +cryptodevkey_cb(void *op) +{ + struct cryptkop *krp = (struct cryptkop *) op; + + wakeup_one(krp); + return (0); +} + +static int +cryptodev_key(struct crypt_kop *kop) +{ + struct cryptkop *krp = NULL; + int error = EINVAL; + int in, out, size, i; + + if (kop->crk_iparams + kop->crk_oparams > CRK_MAXPARAM) { + return (EFBIG); + } + + in = kop->crk_iparams; + out = kop->crk_oparams; + switch (kop->crk_op) { + case CRK_MOD_EXP: + if (in == 3 && out == 1) + break; + return (EINVAL); + case CRK_MOD_EXP_CRT: + if (in == 6 && out == 1) + break; + return (EINVAL); + case CRK_DSA_SIGN: + if (in == 5 && out == 2) + break; + return (EINVAL); + case CRK_DSA_VERIFY: + if (in == 7 && out == 0) + break; + return (EINVAL); + case CRK_DH_COMPUTE_KEY: + if (in == 3 && out == 1) + break; + return (EINVAL); + default: + return (EINVAL); + } + + krp = (struct cryptkop *)malloc(sizeof *krp, M_XDATA, M_WAITOK|M_ZERO); + if (!krp) + return (ENOMEM); + krp->krp_op = kop->crk_op; + krp->krp_status = kop->crk_status; + krp->krp_iparams = kop->crk_iparams; + krp->krp_oparams = kop->crk_oparams; + krp->krp_crid = kop->crk_crid; + krp->krp_status = 0; + krp->krp_callback = (int (*) (struct cryptkop *)) cryptodevkey_cb; + + for (i = 0; i < CRK_MAXPARAM; i++) { + if (kop->crk_param[i].crp_nbits > 65536) + /* Limit is the same as in OpenBSD */ + goto fail; + krp->krp_param[i].crp_nbits = kop->crk_param[i].crp_nbits; + } + for (i = 0; i < krp->krp_iparams + krp->krp_oparams; i++) { + size = (krp->krp_param[i].crp_nbits + 7) / 8; + if (size == 0) + continue; + krp->krp_param[i].crp_p = malloc(size, M_XDATA, M_WAITOK); + if (i >= krp->krp_iparams) + continue; + error = copyin(kop->crk_param[i].crp_p, krp->krp_param[i].crp_p, size); + if (error) + goto fail; + } + + error = crypto_kdispatch(krp); + if (error) + goto fail; + error = tsleep(krp, PSOCK, "crydev", 0); + if (error) { + /* XXX can this happen? if so, how do we recover? */ + goto fail; + } + + kop->crk_crid = krp->krp_crid; /* device that did the work */ + if (krp->krp_status != 0) { + error = krp->krp_status; + goto fail; + } + + for (i = krp->krp_iparams; i < krp->krp_iparams + krp->krp_oparams; i++) { + size = (krp->krp_param[i].crp_nbits + 7) / 8; + if (size == 0) + continue; + error = copyout(krp->krp_param[i].crp_p, kop->crk_param[i].crp_p, size); + if (error) + goto fail; + } + +fail: + if (krp) { + kop->crk_status = krp->krp_status; + for (i = 0; i < CRK_MAXPARAM; i++) { + if (krp->krp_param[i].crp_p) + free(krp->krp_param[i].crp_p, M_XDATA); + } + free(krp, M_XDATA); + } + return (error); +} + +static int +cryptodev_find(struct crypt_find_op *find) +{ + device_t dev; + size_t fnlen = sizeof find->name; + + if (find->crid != -1) { + dev = crypto_find_device_byhid(find->crid); + if (dev == NULL) + return (ENOENT); + strncpy(find->name, device_get_nameunit(dev), fnlen); + find->name[fnlen - 1] = '\x0'; + } else { + find->name[fnlen - 1] = '\x0'; + find->crid = crypto_find_driver(find->name); + if (find->crid == -1) + return (ENOENT); + } + return (0); +} + +/* ARGSUSED */ +static int +cryptof_stat( + struct file *fp, + struct stat *sb, + struct ucred *active_cred, + struct thread *td) +{ + + return (EOPNOTSUPP); +} + +/* ARGSUSED */ +static int +cryptof_close(struct file *fp, struct thread *td) +{ + struct fcrypt *fcr = fp->f_data; + struct csession *cse; + + while ((cse = TAILQ_FIRST(&fcr->csessions))) { + TAILQ_REMOVE(&fcr->csessions, cse, next); + (void)csefree(cse); + } + free(fcr, M_XDATA); + fp->f_data = NULL; + return 0; +} + +static int +cryptof_fill_kinfo(struct file *fp, struct kinfo_file *kif, struct filedesc *fdp) +{ + + kif->kf_type = KF_TYPE_CRYPTO; + return (0); +} + +static struct csession * +csefind(struct fcrypt *fcr, u_int ses) +{ + struct csession *cse; + + TAILQ_FOREACH(cse, &fcr->csessions, next) + if (cse->ses == ses) + return (cse); + return (NULL); +} + +static int +csedelete(struct fcrypt *fcr, struct csession *cse_del) +{ + struct csession *cse; + + TAILQ_FOREACH(cse, &fcr->csessions, next) { + if (cse == cse_del) { + TAILQ_REMOVE(&fcr->csessions, cse, next); + return (1); + } + } + return (0); +} + +static struct csession * +cseadd(struct fcrypt *fcr, struct csession *cse) +{ + TAILQ_INSERT_TAIL(&fcr->csessions, cse, next); + cse->ses = fcr->sesn++; + return (cse); +} + +struct csession * +csecreate(struct fcrypt *fcr, u_int64_t sid, caddr_t key, u_int64_t keylen, + caddr_t mackey, u_int64_t mackeylen, u_int32_t cipher, u_int32_t mac, + struct enc_xform *txform, struct auth_hash *thash) +{ + struct csession *cse; + + cse = malloc(sizeof(struct csession), M_XDATA, M_NOWAIT | M_ZERO); + if (cse == NULL) + return NULL; + mtx_init(&cse->lock, "cryptodev", "crypto session lock", MTX_DEF); + cse->key = key; + cse->keylen = keylen/8; + cse->mackey = mackey; + cse->mackeylen = mackeylen/8; + cse->sid = sid; + cse->cipher = cipher; + cse->mac = mac; + cse->txform = txform; + cse->thash = thash; + cseadd(fcr, cse); + return (cse); +} + +static int +csefree(struct csession *cse) +{ + int error; + + error = crypto_freesession(cse->sid); + mtx_destroy(&cse->lock); + if (cse->key) + free(cse->key, M_XDATA); + if (cse->mackey) + free(cse->mackey, M_XDATA); + free(cse, M_XDATA); + return (error); +} + +static int +cryptoopen(struct cdev *dev, int oflags, int devtype, struct thread *td) +{ + return (0); +} + +static int +cryptoread(struct cdev *dev, struct uio *uio, int ioflag) +{ + return (EIO); +} + +static int +cryptowrite(struct cdev *dev, struct uio *uio, int ioflag) +{ + return (EIO); +} + +static int +cryptoioctl(struct cdev *dev, u_long cmd, caddr_t data, int flag, struct thread *td) +{ + struct file *f; + struct fcrypt *fcr; + int fd, error; + + switch (cmd) { + case CRIOGET: + fcr = malloc(sizeof(struct fcrypt), M_XDATA, M_WAITOK); + TAILQ_INIT(&fcr->csessions); + fcr->sesn = 0; + + error = falloc(td, &f, &fd, 0); + + if (error) { + free(fcr, M_XDATA); + return (error); + } + /* falloc automatically provides an extra reference to 'f'. */ + finit(f, FREAD | FWRITE, DTYPE_CRYPTO, fcr, &cryptofops); + *(u_int32_t *)data = fd; + fdrop(f, td); + break; + case CRIOFINDDEV: + error = cryptodev_find((struct crypt_find_op *)data); + break; + case CRIOASYMFEAT: + error = crypto_getfeat((int *)data); + break; + default: + error = EINVAL; + break; + } + return (error); +} + +static struct cdevsw crypto_cdevsw = { + .d_version = D_VERSION, + .d_flags = D_NEEDGIANT, + .d_open = cryptoopen, + .d_read = cryptoread, + .d_write = cryptowrite, + .d_ioctl = cryptoioctl, + .d_name = "crypto", +}; +static struct cdev *crypto_dev; + +/* + * Initialization code, both for static and dynamic loading. + */ +static int +cryptodev_modevent(module_t mod, int type, void *unused) +{ + switch (type) { + case MOD_LOAD: + if (bootverbose) + printf("crypto: <crypto device>\n"); + crypto_dev = make_dev(&crypto_cdevsw, 0, + UID_ROOT, GID_WHEEL, 0666, + "crypto"); + return 0; + case MOD_UNLOAD: + /*XXX disallow if active sessions */ + destroy_dev(crypto_dev); + return 0; + } + return EINVAL; +} + +static moduledata_t cryptodev_mod = { + "cryptodev", + cryptodev_modevent, + 0 +}; +MODULE_VERSION(cryptodev, 1); +DECLARE_MODULE(cryptodev, cryptodev_mod, SI_SUB_PSEUDO, SI_ORDER_ANY); +MODULE_DEPEND(cryptodev, crypto, 1, 1, 1); +MODULE_DEPEND(cryptodev, zlib, 1, 1, 1); |