patch: multiple-request, async-request support for /dev/crypto
Thor Lancelot Simon <[email protected]>
| Newsgroups | gmane.os.netbsd.devel.userlevel,gmane.os.netbsd.devel.crypto,gmane.os.netbsd.devel.kernel |
|---|---|
| Message-ID | <[email protected]> |
The attached patch adds ioctls for multiple request submission and retrieval in a single system call, and for asynchronous operation via select()/poll() to /dev/crypto. It was written for a slightly older NetBSD-current and I've hastily adapted it to Andrew's recent file descriptor allocation changes, which I hope I got right -- it's well tested before that adaptation but untested with it (caveat emptor). With this patch and a slightly clever multithreaded application that batches requests when pushing them to /dev/crypto we can do 84,000 trivial asymmetric operations/sec (32 bit modular math ops) to a rather old crypto accellerator card, with a Core 2 Duo as the host. I think it's worth the added complexity. Even simpler applications can benefit -- a lot -- from async operation and retrieving multiple requests at once when poll() fires. There is some duplicated code here in the multiple-request ioctls that could be shared with the single-request ioctls. It'd be a moderate pain to clean up and I'd prefer to do that after commit so I can get this in the tree while I have time to focus on it. Also, there is a large comment here describing the new ioctls and parameters which text I'll reproduce in or move to the manual page. I will revise the openssl engine to work as efficiently as possible with the new ioctls added here once I sort out some issues about updating OpenSSL itself in our tree and feeding changes back to the OpenSSL project in an effective way. Comments? -- Thor Lancelot Simon Coyote Point Systems, Inc. <[email protected]> Millerton, NY, USA
oc-multi.diff
(text/plain, 57.3 KB)
diff -r -u -p opencrypto.o/crypto.c opencrypto/crypto.c --- opencrypto.o/crypto.c 2008-02-05 07:26:13.000000000 -0500 +++ opencrypto/crypto.c 2008-03-27 01:32:44.000000000 -0400 @@ -2,6 +2,42 @@ /* $FreeBSD: src/sys/opencrypto/crypto.c,v 1.4.2.5 2003/02/26 00:14:05 sam Exp $ */ /* $OpenBSD: crypto.c,v 1.41 2002/07/17 23:52:38 art Exp $ */ +/*- + * Copyright (c) 2008 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Coyote Point Systems, Inc. + * + * 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. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``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 FOUNDATION OR CONTRIBUTORS + * 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. + */ + /* * The author of this code is Angelos D. Keromytis ([email protected]) * @@ -984,6 +1020,9 @@ crypto_done(struct cryptop *crp) if (crypto_timing) crypto_tstat(&cryptostats.cs_done, &crp->crp_tstamp); #endif + + crp->crp_flags |= CRYPTO_F_DONE; + /* * Normal case; queue the callback for the thread. * @@ -992,17 +1031,39 @@ crypto_done(struct cryptop *crp) * back to mark operations completed. Thus we need * to mask both while manipulating the return queue. */ - mutex_spin_enter(&crypto_mtx); - wasempty = TAILQ_EMPTY(&crp_ret_q); - DPRINTF(("crypto_done: queueing %08x\n", (uint32_t)crp)); - crp->crp_flags |= CRYPTO_F_ONRETQ|CRYPTO_F_DONE; - TAILQ_INSERT_TAIL(&crp_ret_q, crp, crp_next); - if (wasempty) { - DPRINTF(("crypto_done: waking cryptoret, %08x " \ - "hit empty queue\n.", (uint32_t)crp)); - cv_signal(&cryptoret_cv); + if (crp->crp_flags & CRYPTO_F_CBIMM) { + /* + * Do the callback directly. This is ok when the + * callback routine does very little (e.g. the + * /dev/crypto callback method just does a wakeup). + */ +#ifdef CRYPTO_TIMING + if (crypto_timing) { + /* + * NB: We must copy the timestamp before + * doing the callback as the cryptop is + * likely to be reclaimed. + */ + struct timespec t = crp->crp_tstamp; + crypto_tstat(&cryptostats.cs_cb, &t); + crp->crp_callback(crp); + crypto_tstat(&cryptostats.cs_finis, &t); + } else +#endif + crp->crp_callback(crp); + } else { + mutex_spin_enter(&crypto_mtx); + wasempty = TAILQ_EMPTY(&crp_ret_q); + DPRINTF(("crypto_done: queueing %08x\n", (uint32_t)crp)); + crp->crp_flags |= CRYPTO_F_ONRETQ; + TAILQ_INSERT_TAIL(&crp_ret_q, crp, crp_next); + if (wasempty) { + DPRINTF(("crypto_done: waking cryptoret, %08x " \ + "hit empty queue\n.", (uint32_t)crp)); + cv_signal(&cryptoret_cv); + } + mutex_spin_exit(&crypto_mtx); } - mutex_spin_exit(&crypto_mtx); } /* @@ -1015,19 +1076,26 @@ crypto_kdone(struct cryptkop *krp) if (krp->krp_status != 0) cryptostats.cs_kerrs++; + + krp->krp_flags |= CRYPTO_F_DONE; + /* * The return queue is manipulated by the swi thread * and, potentially, by crypto device drivers calling * back to mark operations completed. Thus we need * to mask both while manipulating the return queue. */ - mutex_spin_enter(&crypto_mtx); - wasempty = TAILQ_EMPTY(&crp_ret_kq); - krp->krp_flags |= CRYPTO_F_ONRETQ|CRYPTO_F_DONE; - TAILQ_INSERT_TAIL(&crp_ret_kq, krp, krp_next); - if (wasempty) - cv_signal(&cryptoret_cv); - mutex_spin_exit(&crypto_mtx); + if (krp->krp_flags & CRYPTO_F_CBIMM) { + krp->krp_callback(krp); + } else { + mutex_spin_enter(&crypto_mtx); + wasempty = TAILQ_EMPTY(&crp_ret_kq); + krp->krp_flags |= CRYPTO_F_ONRETQ; + TAILQ_INSERT_TAIL(&crp_ret_kq, krp, krp_next); + if (wasempty) + cv_signal(&cryptoret_cv); + mutex_spin_exit(&crypto_mtx); + } } int diff -r -u -p opencrypto.o/cryptodev.c opencrypto/cryptodev.c --- opencrypto.o/cryptodev.c 2008-03-21 17:55:01.000000000 -0400 +++ opencrypto/cryptodev.c 2008-03-27 01:53:34.000000000 -0400 @@ -2,6 +2,42 @@ /* $FreeBSD: src/sys/opencrypto/cryptodev.c,v 1.4.2.4 2003/06/03 00:09:02 sam Exp $ */ /* $OpenBSD: cryptodev.c,v 1.53 2002/07/10 22:21:30 mickey Exp $ */ +/*- + * Copyright (c) 2008 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Coyote Point Systems, Inc. + * + * 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. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``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 FOUNDATION OR CONTRIBUTORS + * 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. + */ + /* * Copyright (c) 2001 Theo de Raadt * @@ -39,6 +75,7 @@ __KERNEL_RCSID(0, "$NetBSD: cryptodev.c, #include <sys/param.h> #include <sys/systm.h> +#include <sys/kmem.h> #include <sys/malloc.h> #include <sys/mbuf.h> #include <sys/pool.h> @@ -51,6 +88,8 @@ __KERNEL_RCSID(0, "$NetBSD: cryptodev.c, #include <sys/conf.h> #include <sys/device.h> #include <sys/kauth.h> +#include <sys/select.h> +#include <sys/poll.h> #include "opt_ocf.h" #include <opencrypto/cryptodev.h> @@ -81,7 +120,11 @@ struct csession { struct fcrypt { TAILQ_HEAD(csessionlist, csession) csessions; + TAILQ_HEAD(crprethead, cryptop) crp_ret_mq; + TAILQ_HEAD(krprethead, cryptkop) crp_ret_mkq; int sesn; + struct selinfo sinfo; + u_int32_t requestid; }; /* For our fixed-size allocations */ @@ -97,15 +140,16 @@ static int cryptoselect(dev_t dev, int r /* Declaration of cloned-device (per-ctxt) entrypoints */ static int cryptof_read(file_t *, off_t *, struct uio *, kauth_cred_t, int); static int cryptof_write(file_t *, off_t *, struct uio *, kauth_cred_t, int); -static int cryptof_ioctl(file_t *, u_long, void *); -static int cryptof_close(file_t *); +static int cryptof_ioctl(file_t *, u_long, void*, struct lwp *l); +static int cryptof_close(file_t *, struct lwp *); +static int cryptof_poll(file_t *, int , struct lwp *); static const struct fileops cryptofops = { cryptof_read, cryptof_write, cryptof_ioctl, fnullop_fcntl, - fnullop_poll, + cryptof_poll, fbadop_stat, cryptof_close, fnullop_kqfilter @@ -120,12 +164,20 @@ static struct csession *csecreate(struct static int csefree(struct csession *); static int cryptodev_op(struct csession *, struct crypt_op *, struct lwp *); +static int cryptodev_mop(struct fcrypt *, struct crypt_n_op *, int, struct lwp *); static int cryptodev_key(struct crypt_kop *); +static int cryptodev_mkey(struct fcrypt *, struct crypt_n_kop *, int); int cryptodev_dokey(struct crypt_kop *kop, struct crparam kvp[]); +static int crypto_sessionfin(struct fcrypt *, int, u_int32_t *); static int cryptodev_cb(void *); static int cryptodevkey_cb(void *); +static int cryptodev_mcb(void *); +static int cryptodevkey_mcb(void *); + +static int crypto_getmstatus(struct fcrypt *, struct crypt_result_t *, int); +static int crypto_getstatus(struct fcrypt *, struct crypt_result_t *); /* * sysctl-able control variables for /dev/crypto now defined in crypto.c: * crypto_usercrypto, crypto_userasmcrypto, crypto_devallowsoft. @@ -155,36 +207,56 @@ cryptof_ioctl(file_t *fp, u_long cmd, vo struct fcrypt *fcr = fp->f_data; struct csession *cse; struct session_op *sop; + struct session_n_op snop, *psnop; struct crypt_op *cop; + struct crypto_mop *mop; + struct crypto_mkop *mkop; + struct crypt_n_op *cnop; + struct crypt_n_kop *knop; + struct crypt_fnop *fnop; + struct cryptret_t *cryptret; + struct crypt_result_t *crypt_result; struct enc_xform *txform = NULL; struct auth_hash *thash = NULL; u_int64_t sid; u_int32_t ses; + u_int32_t *sesid; int error = 0; + int first = 1; + size_t count; /* backwards compatibility */ file_t *criofp; struct fcrypt *criofcr; int criofd; - switch (cmd) { + switch (cmd) { case CRIOGET: /* XXX deprecated, remove after 5.0 */ - if ((error = fd_allocfile(&criofp, &criofd)) != 0) - return error; - criofcr = pool_get(&fcrpl, PR_WAITOK); + if ((error = fd_allocfile(&criofp, &criofd)) != 0) + return error; + criofcr = pool_get(&fcrpl, PR_WAITOK); + if (criofcr == NULL) { + fd_abort(curproc, criofp, &criofd); + return ENOMEM; + } mutex_spin_enter(&crypto_mtx); - TAILQ_INIT(&criofcr->csessions); + TAILQ_INIT(&criofcr->csessions); + TAILQ_INIT(&criofcr->crp_ret_mq); + TAILQ_INIT(&criofcr->crp_ret_mkq); + selinit(&criofcr->sinfo); + /* * Don't ever return session 0, to allow detection of * failed creation attempts with multi-create ioctl. */ - criofcr->sesn = 1; + criofcr->sesn = 1; + criofcr->requestid = 1; mutex_spin_exit(&crypto_mtx); - (void)fd_clone(criofp, criofd, (FREAD|FWRITE), + (void)fd_clone(criofp, criofd, (FREAD|FWRITE), &cryptofops, criofcr); - *(u_int32_t *)data = criofd; + *(u_int32_t *)data = criofd; return error; - break; + break; case CIOCGSESSION: sop = (struct session_op *)data; switch (sop->cipher) { @@ -248,6 +320,9 @@ cryptof_ioctl(file_t *fp, u_long cmd, vo } break; case CRYPTO_RIPEMD160_HMAC: + thash = &auth_hash_hmac_ripemd_160; + break; + case CRYPTO_RIPEMD160_HMAC_96: thash = &auth_hash_hmac_ripemd_160_96; break; case CRYPTO_MD5: @@ -328,12 +403,193 @@ cryptof_ioctl(file_t *fp, u_long cmd, vo mutex_spin_exit(&crypto_mtx); bail: if (error) { - if (crie.cri_key) - FREE(crie.cri_key, M_XDATA); - if (cria.cri_key) - FREE(cria.cri_key, M_XDATA); + if (crie.cri_key) { + memset(crie.cri_key, 0, crie.cri_klen / 8); + free(crie.cri_key, M_XDATA); + } + if (cria.cri_key) { + memset(cria.cri_key, 0, cria.cri_klen / 8); + free(cria.cri_key, M_XDATA); + } } break; + case CIOCNSESSION: + psnop = (struct session_n_op *)data; + bcopy(data, (void *)&snop, sizeof(struct session_n_op)); + do { + switch (snop.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_NULL_CBC: + txform = &enc_xform_null; + break; + case CRYPTO_ARC4: + txform = &enc_xform_arc4; + break; + default: + DPRINTF(("Invalid cipher %d\n", snop.cipher)); + snop.status = EINVAL; + goto mbail; + } + + switch (snop.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_MD5_HMAC_96: + thash = &auth_hash_hmac_md5_96; + break; + case CRYPTO_SHA1_HMAC_96: + thash = &auth_hash_hmac_sha1_96; + break; + case CRYPTO_SHA2_HMAC: + if (snop.mackeylen == auth_hash_hmac_sha2_256.keysize) + thash = &auth_hash_hmac_sha2_256; + else if (snop.mackeylen == auth_hash_hmac_sha2_384.keysize) + thash = &auth_hash_hmac_sha2_384; + else if (snop.mackeylen == auth_hash_hmac_sha2_512.keysize) + thash = &auth_hash_hmac_sha2_512; + else { + DPRINTF(("Invalid mackeylen %d\n", + snop.mackeylen)); + snop.status = EINVAL; + } + break; + case CRYPTO_RIPEMD160_HMAC: + thash = &auth_hash_hmac_ripemd_160; + break; + case CRYPTO_RIPEMD160_HMAC_96: + thash = &auth_hash_hmac_ripemd_160_96; + break; + case CRYPTO_MD5: + thash = &auth_hash_md5; + break; + case CRYPTO_SHA1: + thash = &auth_hash_sha1; + break; + case CRYPTO_NULL_HMAC: + thash = &auth_hash_null; + break; + default: + DPRINTF(("Invalid mac %d\n", snop.mac)); + snop.status = EINVAL; + goto mbail; + } + + bzero(&crie, sizeof(crie)); + bzero(&cria, sizeof(cria)); + + if (txform) { + crie.cri_alg = txform->type; + crie.cri_klen = snop.keylen * 8; + if (snop.keylen > txform->maxkey || + snop.keylen < txform->minkey) { + DPRINTF(("keylen %d not in [%d,%d]\n", + snop.keylen, txform->minkey, + txform->maxkey)); + snop.status = EINVAL; + goto mbail; + } + + crie.cri_key = malloc(crie.cri_klen / 8, + M_XDATA, M_WAITOK); + if((snop.status = copyin(snop.key, + crie.cri_key, crie.cri_klen / 8))) + goto mbail; + if (thash) + crie.cri_next = &cria; + } + + if (thash) { + cria.cri_alg = thash->type; + cria.cri_klen = snop.mackeylen * 8; + if (snop.mackeylen != thash->keysize) { + DPRINTF(("mackeylen %d != keysize %d\n", + snop.mackeylen, thash->keysize)); + snop.status = EINVAL; + goto mbail; + } + + if (cria.cri_klen) { + cria.cri_key = malloc(cria.cri_klen / 8, + M_XDATA, M_WAITOK); + if((snop.status = copyin(snop.mackey, + cria.cri_key, cria.cri_klen / 8))) + goto mbail; + } + } + mutex_spin_enter(&crypto_mtx); + snop.status = crypto_newsession(&sid, + (txform ? &crie : &cria), + crypto_devallowsoft); + if (!snop.status) { + cse = csecreate(fcr, sid, crie.cri_key, + crie.cri_klen, cria.cri_key, + cria.cri_klen, snop.cipher, + snop.mac, txform, thash); + if (cse != NULL) { + snop.ses = cse->ses; + } else { + DPRINTF(("csecreate failed\n")); + crypto_freesession(sid); + snop.status = EINVAL; + } + } else { + DPRINTF(("SIOCSESSION violates kernel parameters %d\n", + snop.status)); + } + mutex_spin_exit(&crypto_mtx); +mbail: + if(snop.status) { + snop.ses = 0; + if (crie.cri_key) + memset(crie.cri_key, 0, crie.cri_klen / 8); + free(crie.cri_key, M_XDATA); + if (cria.cri_key) + memset(cria.cri_key, 0, cria.cri_klen / 8); + free(cria.cri_key, M_XDATA); + } + + if(first) { + bcopy(&snop, psnop, + sizeof(struct session_n_op)); + first = 0; + } else { + error = copyout(&snop, psnop, + sizeof(struct session_n_op)); + } + + psnop = snop.next; + + if (psnop) { + copyin(psnop, &snop, + sizeof(struct session_n_op)); + } + } while (psnop && !error); + break; case CIOCFSESSION: mutex_spin_enter(&crypto_mtx); ses = *(u_int32_t *)data; @@ -344,6 +600,19 @@ bail: error = csefree(cse); mutex_spin_exit(&crypto_mtx); break; + case CIOCFNSESSION: + fnop = (struct crypt_fnop *)data; + sesid = kmem_alloc((fnop->count * sizeof(u_int32_t)), + KM_SLEEP); + if (!sesid) + return (EINVAL); + error = copyin(fnop->sesid, sesid, + (fnop->count * sizeof(u_int32_t))); + if (!error) { + error = crypto_sessionfin(fcr, fnop->count, sesid); + } + kmem_free(sesid, (fnop->count * sizeof(u_int32_t))); + break; case CIOCCRYPT: mutex_spin_enter(&crypto_mtx); cop = (struct crypt_op *)data; @@ -356,13 +625,80 @@ bail: error = cryptodev_op(cse, cop, curlwp); DPRINTF(("cryptodev_op error = %d\n", error)); break; + case CIOCNCRYPTM: + mop = (struct crypto_mop *)data; + cnop = kmem_alloc((mop->count * sizeof(struct crypt_n_op)), + KM_SLEEP); + if(!cnop) { + return(EINVAL); + } + error = copyin(mop->reqs, cnop, + (mop->count * sizeof(struct crypt_n_op))); + if(!error) { + error = cryptodev_mop(fcr, cnop, mop->count, l); + if (!error) { + error = copyout(cnop, mop->reqs, + (mop->count * + sizeof(struct crypt_n_op))); + } + } + kmem_free(cnop, (mop->count * sizeof(struct crypt_n_op))); + break; case CIOCKEY: error = cryptodev_key((struct crypt_kop *)data); DPRINTF(("cryptodev_key error = %d\n", error)); break; + case CIOCNFKEYM: + mkop = (struct crypto_mkop *)data; + knop = kmem_alloc((mkop->count * sizeof(struct crypt_n_kop)), + KM_SLEEP); + if (!knop) { + return(EINVAL); + } + error = copyin(mkop->reqs, knop, + (mkop->count * sizeof(struct crypt_n_kop))); + if (!error) { + error = cryptodev_mkey(fcr, knop, mkop->count); + if (!error) + error = copyout(knop, mkop->reqs, + (mkop->count * + sizeof(struct crypt_n_kop))); + } + kmem_free(knop, (mkop->count * sizeof(struct crypt_n_kop))); + break; case CIOCASYMFEAT: error = crypto_getfeat((int *)data); break; + case CIOCNCRYPTRETM: + cryptret = (struct cryptret_t *)data; + count = cryptret->count; + crypt_result = kmem_alloc((count * + sizeof(struct crypt_result_t)), + KM_SLEEP); + if(!crypt_result) + return(EINVAL); + error = copyin(cryptret->results, crypt_result, + (count * sizeof(struct crypt_result_t))); + if (error) + goto reterr; + cryptret->count = crypto_getmstatus(fcr, crypt_result, + cryptret->count); + /* sanity check count */ + if (cryptret->count > count) { + printf("%s.%d: error returned count %d > original count %d\n", + __FILE__, __LINE__, cryptret->count, count); + cryptret->count = count; + + } + error = copyout(crypt_result, cryptret->results, + (cryptret->count * sizeof(struct crypt_result_t))); +reterr: + kmem_free(crypt_result, + (count * sizeof(struct crypt_result_t))); + break; + case CIOCNCRYPTRET: + error = crypto_getstatus(fcr, (struct crypt_result_t *)data); + break; default: DPRINTF(("invalid ioctl cmd %ld\n", cmd)); error = EINVAL; @@ -393,7 +729,7 @@ cryptodev_op(struct csession *cse, struc UIO_SETUP_SYSSPACE(&cse->uio); memset(&cse->iovec, 0, sizeof(cse->iovec)); cse->uio.uio_iov[0].iov_len = cop->len; - cse->uio.uio_iov[0].iov_base = malloc(cop->len, M_XDATA, M_WAITOK); + cse->uio.uio_iov[0].iov_base = kmem_alloc(cop->len, KM_SLEEP); cse->uio.uio_resid = cse->uio.uio_iov[0].iov_len; crp = crypto_getreq((cse->txform != NULL) + (cse->thash != NULL)); @@ -416,7 +752,10 @@ cryptodev_op(struct csession *cse, struc } if ((error = copyin(cop->src, cse->uio.uio_iov[0].iov_base, cop->len))) + { + printf("copyin failed %s %d \n", (char *)cop->src, error); goto bail; + } if (crda) { crda->crd_skip = 0; @@ -442,8 +781,7 @@ cryptodev_op(struct csession *cse, struc } crp->crp_ilen = cop->len; - crp->crp_flags = CRYPTO_F_IOV | CRYPTO_F_CBIMM - | (cop->flags & COP_F_BATCH); + crp->crp_flags = CRYPTO_F_IOV | (cop->flags & COP_F_BATCH); crp->crp_buf = (void *)&cse->uio; crp->crp_callback = (int (*) (struct cryptop *)) cryptodev_cb; crp->crp_sid = cse->sid; @@ -458,7 +796,8 @@ cryptodev_op(struct csession *cse, struc error = EINVAL; goto bail; } - if ((error = copyin(cop->iv, cse->tmp_iv, cse->txform->blocksize))) + if ((error = copyin(cop->iv, cse->tmp_iv, + cse->txform->blocksize))) goto bail; bcopy(cse->tmp_iv, crde->crd_iv, cse->txform->blocksize); crde->crd_flags |= CRD_F_IV_EXPLICIT | CRD_F_IV_PRESENT; @@ -491,13 +830,27 @@ cryptodev_op(struct csession *cse, struc * XXX (should we arrange for crypto_dispatch to return to * XXX us with it held? it seems quite ugly to do so.) */ +#ifdef notyet +eagain: +#endif error = crypto_dispatch(crp); mutex_spin_enter(&crypto_mtx); - if (error != 0) { + + switch (error) { +#ifdef notyet /* don't loop forever -- but EAGAIN not possible here yet */ + case EAGAIN: + mutex_spin_exit(&crypto_mtx); + goto eagain; + break; +#endif + case 0: + break; + default: DPRINTF(("cryptodev_op: not waiting, error.\n")); mutex_spin_exit(&crypto_mtx); goto bail; } + while (!(crp->crp_flags & CRYPTO_F_DONE)) { DPRINTF(("cryptodev_op: sleeping on cv %08x for crp %08x\n", \ (uint32_t)&crp->crp_cv, (uint32_t)crp)); @@ -522,7 +875,8 @@ cryptodev_op(struct csession *cse, struc } if (cop->dst && - (error = copyout(cse->uio.uio_iov[0].iov_base, cop->dst, cop->len))) { + (error = copyout(cse->uio.uio_iov[0].iov_base, cop->dst, cop->len))) + { DPRINTF(("cryptodev_op: copyout error %d\n", error)); goto bail; } @@ -537,7 +891,8 @@ bail: if (crp) crypto_freereq(crp); if (cse->uio.uio_iov[0].iov_base) - free(cse->uio.uio_iov[0].iov_base, M_XDATA); + kmem_free(cse->uio.uio_iov[0].iov_base, + cse->uio.uio_iov[0].iov_len); return (error); } @@ -565,13 +920,50 @@ cryptodev_cb(void *op) } static int +cryptodev_mcb(void *op) +{ + struct cryptop *crp = (struct cryptop *) op; + struct csession *cse = (struct csession *)crp->crp_opaque; + int error=0; + + mutex_spin_enter(&crypto_mtx); + cse->error = crp->crp_etype; + if (crp->crp_etype == EAGAIN) { + mutex_spin_exit(&crypto_mtx); + error = crypto_dispatch(crp); + mutex_spin_enter(&crypto_mtx); + } + if (error != 0 || (crp->crp_flags & CRYPTO_F_DONE)) { + cv_signal(&crp->crp_cv); + } + + TAILQ_INSERT_TAIL(&crp->fcrp->crp_ret_mq, crp, crp_next); + mutex_spin_exit(&crypto_mtx); + selnotify(&crp->fcrp->sinfo, 0, 0); + return (0); +} + +static int cryptodevkey_cb(void *op) { struct cryptkop *krp = (struct cryptkop *) op; + + mutex_spin_enter(&crypto_mtx); + cv_signal(&krp->krp_cv); + mutex_spin_exit(&crypto_mtx); + return (0); +} + +static int +cryptodevkey_mcb(void *op) +{ + struct cryptkop *krp = (struct cryptkop *) op; mutex_spin_enter(&crypto_mtx); cv_signal(&krp->krp_cv); + TAILQ_INSERT_TAIL(&krp->fcrp->crp_ret_mkq, krp, krp_next); mutex_spin_exit(&crypto_mtx); + selnotify(&krp->fcrp->sinfo,0); return (0); } @@ -655,7 +1047,7 @@ cryptodev_key(struct crypt_kop *kop) 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); + krp->krp_param[i].crp_p = kmem_alloc(size, KM_SLEEP); if (i >= krp->krp_iparams) continue; error = copyin(kop->crk_param[i].crp_p, krp->krp_param[i].crp_p, size); @@ -699,8 +1091,13 @@ 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); + struct crparam *kp = &(krp->krp_param[i]); + if (krp->krp_param[i].crp_p) { + size = (kp->crp_nbits + 7) / 8; + KASSERT(size > 0); + memset(kp->crp_p, 0, size); + kmem_free(kp->crp_p, size); + } } pool_put(&cryptkop_pool, krp); } @@ -720,6 +1117,7 @@ cryptof_close(file_t *fp) TAILQ_REMOVE(&fcr->csessions, cse, next); (void)csefree(cse); } + seldestroy(&fcr->sinfo); pool_put(&fcrpl, fcr); fp->f_data = NULL; @@ -738,6 +1136,7 @@ csefind(struct fcrypt *fcr, u_int ses) TAILQ_FOREACH(cse, &fcr->csessions, next) if (cse->ses == ses) ret = cse; + return (ret); } @@ -809,9 +1208,9 @@ csefree(struct csession *cse) KASSERT(mutex_owned(&crypto_mtx)); error = crypto_freesession(cse->sid); if (cse->key) - FREE(cse->key, M_XDATA); + free(cse->key, M_XDATA); if (cse->mackey) - FREE(cse->mackey, M_XDATA); + free(cse->mackey, M_XDATA); pool_put(&csepl, cse); return (error); } @@ -831,13 +1230,21 @@ cryptoopen(dev_t dev, int flag, int mode return error; fcr = pool_get(&fcrpl, PR_WAITOK); + if (fcr == NULL) { + fdrelease(l, fd); + return ENOMEM; + } mutex_spin_enter(&crypto_mtx); TAILQ_INIT(&fcr->csessions); + TAILQ_INIT(&fcr->crp_ret_mq); + TAILQ_INIT(&fcr->crp_ret_mkq); + selinit(&fcr->sinfo); /* * Don't ever return session 0, to allow detection of * failed creation attempts with multi-create ioctl. */ fcr->sesn = 1; + fcr->requestid = 1; mutex_spin_exit(&crypto_mtx); return fd_clone(fp, fd, flag, &cryptofops, fcr); } @@ -875,6 +1282,622 @@ struct cdevsw crypto_cdevsw = { /* type */ D_OTHER, }; +static int +cryptodev_mop(struct fcrypt *fcr, + struct crypt_n_op * cnop, + int count, + struct lwp *l) +{ + struct cryptop *crp = NULL; + struct cryptodesc *crde = NULL, *crda = NULL; + int req, error=0; + struct csession *cse; + + for (req = 0; req < count; req++) { + mutex_spin_enter(&crypto_mtx); + cse = csefind(fcr, cnop[req].ses); + if (cse == NULL) { + DPRINTF(("csefind failed\n")); + cnop[req].status = EINVAL; + mutex_spin_exit(&crypto_mtx); + continue; + } + mutex_spin_exit(&crypto_mtx); + + if (cnop[req].len > 256*1024-4) { + DPRINTF(("length failed\n")); + cnop[req].status = EINVAL; + continue; + } + if (cse->txform) { + if (cnop[req].len == 0 || + (cnop[req].len % cse->txform->blocksize) != 0) { + cnop[req].status = EINVAL; + continue; + } + } + + crp = crypto_getreq((cse->txform != NULL) + (cse->thash != NULL)); + if (crp == NULL) { + cnop[req].status = ENOMEM; + goto bail; + } + + bzero(&crp->uio, sizeof(crp->uio)); + crp->uio.uio_iovcnt = 1; + crp->uio.uio_resid = 0; + crp->uio.uio_rw = UIO_WRITE; + crp->uio.uio_iov = crp->iovec; + UIO_SETUP_SYSSPACE(&crp->uio); + memset(&crp->iovec, 0, sizeof(crp->iovec)); + crp->uio.uio_iov[0].iov_len = cnop[req].len; + crp->uio.uio_iov[0].iov_base = kmem_alloc(cnop[req].len, + KM_SLEEP); + crp->uio.uio_resid = crp->uio.uio_iov[0].iov_len; + + if (cse->thash) { + crda = crp->crp_desc; + if (cse->txform) + crde = crda->crd_next; + } else { + if (cse->txform) + crde = crp->crp_desc; + else { + cnop[req].status = EINVAL; + goto bail; + } + } + + if ((copyin(cnop[req].src, + crp->uio.uio_iov[0].iov_base, cnop[req].len))) { + cnop[req].status = EINVAL; + goto bail; + } + + if (crda) { + crda->crd_skip = 0; + crda->crd_len = cnop[req].len; + crda->crd_inject = 0; /* ??? */ + + crda->crd_alg = cse->mac; + crda->crd_key = cse->mackey; + crda->crd_klen = cse->mackeylen * 8; + } + + if (crde) { + if (cnop[req].op == COP_ENCRYPT) + crde->crd_flags |= CRD_F_ENCRYPT; + else + crde->crd_flags &= ~CRD_F_ENCRYPT; + crde->crd_len = cnop[req].len; + crde->crd_inject = 0; + + crde->crd_alg = cse->cipher; + if(cnop[req].key && cnop[req].keylen) { + crde->crd_key = malloc(cnop[req].keylen, + M_XDATA, M_WAITOK); + if((error = copyin(cnop[req].key, + crde->crd_key, cnop[req].keylen))) { + cnop[req].status = EINVAL; + goto bail; + } + crde->crd_klen = cnop[req].keylen * 8; + } else { + crde->crd_key = cse->key; + crde->crd_klen = cse->keylen * 8; + } + } + + crp->crp_ilen = cnop[req].len; + crp->crp_flags = CRYPTO_F_IOV | CRYPTO_F_CBIMM + | (cnop[req].flags & COP_F_BATCH); + crp->crp_buf = (void *)&crp->uio; + crp->crp_callback = (int (*) (struct cryptop *)) cryptodev_mcb; + crp->crp_sid = cse->sid; + crp->crp_opaque = (void *)cse; + crp->fcrp = fcr; + crp->dst = cnop[req].dst; + /* we can use the crp_ilen in cryptop(crp) for this */ + crp->len = cnop[req].len; + crp->mac = cnop[req].mac; + + if (cnop[req].iv) { + if (crde == NULL) { + cnop[req].status = EINVAL; + goto bail; + } + if (cse->cipher == CRYPTO_ARC4) { /* XXX use flag? */ + cnop[req].status = EINVAL; + goto bail; + } + if ((error = copyin(cnop[req].iv, crp->tmp_iv, + cse->txform->blocksize))) { + cnop[req].status = EINVAL; + goto bail; + } + bcopy(crp->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 (crde) { + if (cse->cipher == CRYPTO_ARC4) { /* XXX use flag? */ + 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 (cnop[req].mac) { + if (crda == NULL) { + cnop[req].status = EINVAL; + goto bail; + } + crp->crp_mac=cse->tmp_mac; + } + cnop[req].reqid = atomic_inc_32_nv(&(fcr->requestid)); + crp->crp_reqid = cnop[req].reqid; + crp->crp_usropaque = cnop[req].opaque; +#ifdef notyet +eagain: +#endif + cnop[req].status = crypto_dispatch(crp); + mutex_spin_enter(&crypto_mtx); /* XXX why mutex? */ + + switch (cnop[req].status) { +#ifdef notyet /* don't loop forever -- but EAGAIN not possible here yet */ + case EAGAIN: + mutex_spin_exit(&crypto_mtx); + goto eagain; + break; +#endif + case 0: + break; + default: + DPRINTF(("cryptodev_op: not waiting, error.\n")); + mutex_spin_exit(&crypto_mtx); + goto bail; + } + + mutex_spin_exit(&crypto_mtx); +bail: + if (cnop[req].status) { + if (crp) { + crypto_freereq(crp); + if(cse->uio.uio_iov[0].iov_base) { + kmem_free(cse->uio.uio_iov[0].iov_base, + cse->uio.uio_iov[0].iov_len); + } + } + error = 0; + } + } + return (error); +} + +static int +cryptodev_mkey(struct fcrypt *fcr, struct crypt_n_kop *kop, int count) +{ + struct cryptkop *krp = NULL; + int error = EINVAL; + int in, out, size, i, req; + + for (req = 0; req < count; req++) { + if (kop[req].crk_iparams + kop[req].crk_oparams > CRK_MAXPARAM) { + return (EFBIG); + } + + in = kop[req].crk_iparams; + out = kop[req].crk_oparams; + switch (kop[req].crk_op) { + case CRK_MOD_EXP: + if (in == 3 && out == 1) + break; + kop[req].status = EINVAL; + continue; + case CRK_MOD_EXP_CRT: + if (in == 6 && out == 1) + break; + kop[req].status = EINVAL; + continue; + case CRK_DSA_SIGN: + if (in == 5 && out == 2) + break; + kop[req].status = EINVAL; + continue; + case CRK_DSA_VERIFY: + if (in == 7 && out == 0) + break; + kop[req].status = EINVAL; + continue; + case CRK_DH_COMPUTE_KEY: + if (in == 3 && out == 1) + break; + kop[req].status = EINVAL; + continue; + case CRK_MOD_ADD: + if (in == 3 && out == 1) + break; + kop[req].status = EINVAL; + continue; + case CRK_MOD_ADDINV: + if (in == 2 && out == 1) + break; + kop[req].status = EINVAL; + continue; + case CRK_MOD_SUB: + if (in == 3 && out == 1) + break; + kop[req].status = EINVAL; + continue; + case CRK_MOD_MULT: + if (in == 3 && out == 1) + break; + kop[req].status = EINVAL; + continue; + case CRK_MOD_MULTINV: + if (in == 2 && out == 1) + break; + kop[req].status = EINVAL; + continue; + case CRK_MOD: + if (in == 2 && out == 1) + break; + kop[req].status = EINVAL; + continue; + default: + kop[req].status = EINVAL; + continue; + } + + krp = pool_get(&cryptkop_pool, PR_WAITOK); + if (!krp) { + kop[req].status = ENOMEM; + goto fail; + } + bzero(krp, sizeof *krp); + cv_init(&krp->krp_cv, "crykdev"); + krp->krp_op = kop[req].crk_op; + krp->krp_status = kop[req].crk_status; + krp->krp_iparams = kop[req].crk_iparams; + krp->krp_oparams = kop[req].crk_oparams; + krp->krp_status = 0; + krp->krp_callback = + (int (*) (struct cryptkop *)) cryptodevkey_mcb; + bcopy(kop[req].crk_param, + krp->crk_param, + sizeof(kop[req].crk_param)); + + krp->krp_flags = CRYPTO_F_CBIMM; + + for (i = 0; i < CRK_MAXPARAM; i++) + krp->krp_param[i].crp_nbits = + kop[req].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 = + kmem_alloc(size, KM_SLEEP); + if (i >= krp->krp_iparams) + continue; + kop[req].status = copyin(kop[req].crk_param[i].crp_p, + krp->krp_param[i].crp_p, size); + if (kop[req].status) + goto fail; + } + krp->fcrp = fcr; + + kop[req].reqid = atomic_inc_32_nv(&(fcr->requestid)); + krp->krp_reqid = kop[req].reqid; + krp->krp_usropaque = kop[req].crk_opaque; + + kop[req].status = crypto_kdispatch(krp); + if (kop[req].status != 0) { + goto fail; + } + +fail: + if(kop[req].status) { + if (krp) { + kop[req].crk_status = krp->krp_status; + for (i = 0; i < CRK_MAXPARAM; i++) { + struct crparam *kp = + &(krp->krp_param[i]); + if (kp->crp_p) { + size = (kp->crp_nbits + 7) / 8; + KASSERT(size > 0); + memset(kp->crp_p, 0, size); + kmem_free(kp->crp_p, size); + } + } + pool_put(&cryptkop_pool, krp); + } + } + error = 0; + } + DPRINTF(("cryptodev_key: error=0x%08x\n", error)); + return (error); +} + +static int +crypto_sessionfin(struct fcrypt *fcr, int count, u_int32_t *sesid) +{ + struct csession *cse; + int req, error = 0; + + mutex_spin_enter(&crypto_mtx); + for(req = 0; req < count; req++) { + cse = csefind(fcr, sesid[req]); + if (cse == NULL) + continue; + csedelete(fcr, cse); + error = csefree(cse); + } + mutex_spin_exit(&crypto_mtx); + return 0; +} + +/* + * collect as many completed requests as are availble, or count completed requests + * whichever is less. + * return the number of requests. + */ +static int +crypto_getmstatus(struct fcrypt *fcr, struct crypt_result_t *crypt_result, int count) +{ + struct cryptop *crp = NULL; + struct cryptkop *krp = NULL; + struct csession *cse; + int i, size, req = 0; + int completed=0; + + /* On stack so nobody else can grab them -- no locking */ + SLIST_HEAD(, cryptop) crp_delfree_l = + SLIST_HEAD_INITIALIZER(crp_delfree_l); + SLIST_HEAD(, cryptkop) krp_delfree_l = + SLIST_HEAD_INITIALIZER(krp_delfree_l); + + mutex_spin_enter(&crypto_mtx); + + /* at this point we do not know which response user is requesting for + * (symmetric or asymmetric) so we copyout one from each i.e if the + * count is 2 then 1 from symmetric and 1 from asymmetric queue and + * if 3 then 2 symmetric and 1 asymmetric and so on */ + for(; req < count ;) { + crp = TAILQ_FIRST(&fcr->crp_ret_mq); + if(crp) { + cse = (struct csession *)crp->crp_opaque; + crypt_result[req].rid = crp->crp_reqid; + crypt_result[req].opaque = crp->crp_usropaque; + completed++; + cse = csefind(fcr, cse->ses); + if (cse == NULL) { + DPRINTF(("csefind failed\n")); + crypt_result[req].status = EINVAL; + goto bail; + } + + if (crp->crp_etype != 0) { + crypt_result[req].status = crp->crp_etype; + goto bail; + } + + if (cse->error) { + crypt_result[req].status = cse->error; + goto bail; + } + + if (crp->dst && + (crypt_result[req].status = copyout + (crp->uio.uio_iov[0].iov_base, + crp->dst, crp->len))) + goto bail; + + if (crp->mac && + (crypt_result[req].status = copyout + (crp->crp_mac, crp->mac, + cse->thash->authsize))) + goto bail; +bail: + TAILQ_REMOVE(&fcr->crp_ret_mq, crp, crp_next); + SLIST_INSERT_HEAD(&crp_delfree_l, crp, + crp_qun.crp_lnext); + req++; + } + + if(req < count) { + krp = TAILQ_FIRST(&fcr->crp_ret_mkq); + if (krp) { + crypt_result[req].rid = krp->krp_reqid; + crypt_result[req].opaque = krp->krp_usropaque; + completed++; + if (krp->krp_status != 0) { + DPRINTF(("cryptodev_key: " + "krp->krp_status" + "0x%08x\n", krp->krp_status)); + crypt_result[req].status = + 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; + crypt_result[req].status = copyout + (krp->krp_param[i].crp_p, + krp->crk_param[i].crp_p, size); + if (crypt_result[req].status) { + DPRINTF(("cryptodev_key: " + "copyout oparam " + "%d failed, " + "error=%d\n", + i-krp->krp_iparams, + crypt_result[req].status)); + goto fail; + } + } +fail: + TAILQ_REMOVE(&fcr->crp_ret_mkq, krp, krp_next); + /* not sure what to do for this */ + /* kop[req].crk_status = krp->krp_status; */ + SLIST_INSERT_HEAD(&krp_delfree_l, krp, + krp_qun.krp_lnext); + } + req++; + } + } + mutex_spin_exit(&crypto_mtx); + + while(!SLIST_EMPTY(&crp_delfree_l)) { + crp = SLIST_FIRST(&crp_delfree_l); + SLIST_REMOVE_HEAD(&crp_delfree_l, crp_qun.crp_lnext); + kmem_free(crp->uio.uio_iov[0].iov_base, + crp->uio.uio_iov[0].iov_len); + crypto_freereq(crp); + } + + while(!SLIST_EMPTY(&krp_delfree_l)) { + krp = SLIST_FIRST(&krp_delfree_l); + for (i = 0; i < CRK_MAXPARAM; i++) { + struct crparam *kp = &(krp->krp_param[i]); + if (kp->crp_p) { + size = (kp->crp_nbits + 7) / 8; + KASSERT(size > 0); + memset(kp->crp_p, 0, size); + kmem_free(kp->crp_p, size); + } + } + SLIST_REMOVE_HEAD(&krp_delfree_l, krp_qun.krp_lnext); + pool_put(&cryptkop_pool, krp); + } + return completed; +} + +static int +crypto_getstatus (struct fcrypt *fcr, struct crypt_result_t *crypt_result) +{ + struct cryptop *crp = NULL; + struct cryptkop *krp = NULL; + struct csession *cse; + int i, size, req = 0; + + mutex_spin_enter(&crypto_mtx); + /* Here we dont know for which request the user is requesting the + * response so checking in both the queues */ + TAILQ_FOREACH(crp, &fcr->crp_ret_mq, crp_next) { + if(crp && (crp->crp_reqid == crypt_result->rid)) { + cse = (struct csession *)crp->crp_opaque; + crypt_result->opaque = crp->crp_usropaque; + cse = csefind(fcr, cse->ses); + if (cse == NULL) { + DPRINTF(("csefind failed\n")); + crypt_result->status = EINVAL; + goto bail; + } + + if (crp->crp_etype != 0) { + crypt_result->status = crp->crp_etype; + goto bail; + } + + if (cse->error) { + crypt_result->status = cse->error; + goto bail; + } + + if (crp->dst && + (crypt_result->status = copyout + (crp->uio.uio_iov[0].iov_base, + crp->dst, crp->len))) + goto bail; + + if (crp->mac && + (crypt_result->status = copyout(crp->crp_mac, + crp->mac, cse->thash->authsize))) + goto bail; +bail: + TAILQ_REMOVE(&fcr->crp_ret_mq, crp, crp_next); + + mutex_spin_exit(&crypto_mtx); + crypto_freereq(crp); + return 0; + } + } + + TAILQ_FOREACH(krp, &fcr->crp_ret_mkq, krp_next) { + if(krp && (krp->krp_reqid == crypt_result->rid)) { + crypt_result[req].opaque = krp->krp_usropaque; + if (krp->krp_status != 0) { + DPRINTF(("cryptodev_key: " + "krp->krp_status 0x%08x\n", + krp->krp_status)); + crypt_result[req].status = 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; + crypt_result[req].status = copyout + (krp->krp_param[i].crp_p, + krp->crk_param[i].crp_p, size); + if (crypt_result[req].status) { + DPRINTF(("cryptodev_key: copyout oparam" + "%d failed, error=%d\n", + i-krp->krp_iparams, + crypt_result[req].status)); + goto fail; + } + } +fail: + TAILQ_REMOVE(&fcr->crp_ret_mkq, krp, krp_next); + mutex_spin_exit(&crypto_mtx); + /* not sure what to do for this */ + /* kop[req].crk_status = krp->krp_status; */ + for (i = 0; i < CRK_MAXPARAM; i++) { + struct crparam *kp = &(krp->krp_param[i]); + if (kp->crp_p) { + size = (kp->crp_nbits + 7) / 8; + KASSERT(size > 0); + memset(kp->crp_p, 0, size); + kmem_free(kp->crp_p, size); + } + } + pool_put(&cryptkop_pool, krp); + return 0; + } + } + mutex_spin_exit(&crypto_mtx); + return EINPROGRESS; +} + +static int +cryptof_poll(file_t *fp, int events, struct lwp *l) +{ + struct fcrypt *fcr = (struct fcrypt *)fp->f_data; + + if (!(events & (POLLIN | POLLRDNORM))) { + /* only support read and POLLIN */ + return 0; + } + + if (TAILQ_EMPTY(&fcr->crp_ret_mq) && TAILQ_EMPTY(&fcr->crp_ret_mkq)) { + /* no completed requests pending, + * save the poll for later (for selnotify()). */ + selrecord(l, &fcr->sinfo); + return 0; + } else { + /* let the app(s) know that there are completed requests */ + return events & (POLLIN | POLLRDNORM); + } +} + /* * Pseudo-device initialization routine for /dev/crypto */ diff -r -u -p opencrypto.o/cryptodev.h opencrypto/cryptodev.h --- opencrypto.o/cryptodev.h 2008-02-03 19:35:34.000000000 -0500 +++ opencrypto/cryptodev.h 2008-03-27 01:32:44.000000000 -0400 @@ -2,6 +2,42 @@ /* $FreeBSD: src/sys/opencrypto/cryptodev.h,v 1.2.2.6 2003/07/02 17:04:50 sam Exp $ */ /* $OpenBSD: cryptodev.h,v 1.33 2002/07/17 23:52:39 art Exp $ */ +/*- + * Copyright (c) 2008 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Coyote Point Systems, Inc. + * + * 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. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``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 FOUNDATION OR CONTRIBUTORS + * 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. + */ + /* * The author of this code is Angelos D. Keromytis ([email protected]) * @@ -121,6 +157,22 @@ struct session_op { u_int32_t ses; /* returns: session # */ }; +/* to support multiple session creation */ + +struct session_n_op { + u_int32_t cipher; /* ie. CRYPTO_DES_CBC */ + u_int32_t mac; /* ie. CRYPTO_MD5_HMAC */ + + u_int32_t keylen; /* cipher key */ + void * key; + int mackeylen; /* mac key */ + void * mackey; + + u_int32_t ses; /* returns: session # */ + int status; + struct session_n_op * next; /* next pointer for multiple session */ +}; + struct crypt_op { u_int32_t ses; u_int16_t op; /* i.e. COP_ENCRYPT */ @@ -134,6 +186,57 @@ struct crypt_op { void * iv; }; +/* to support multiple session creation */ +/* + * + * The reqid field is filled when the operation has + * been accepted and started, and can be used to later retrieve + * the operation results via CIOCNCRYPTRET or identify the + * request in the completion list returned by CIOCNCRYPTRETM. + * + * The opaque pointer can be set arbitrarily by the user + * and it is passed back in the crypt_result_t structure + * when the request completes. This field can be used for example + * to track context for the request and avoid lookups in the + * user application. + */ + +struct crypt_n_op { + u_int32_t ses; + u_int16_t op; /* i.e. COP_ENCRYPT */ +#define COP_ENCRYPT 1 +#define COP_DECRYPT 2 + u_int16_t flags; +#define COP_F_BATCH 0x0008 /* Dispatch as quickly as possible */ + u_int len; + + u_int32_t reqid; /* request id */ + int status; /* status of request -accepted or not */ + void *opaque; /* opaque pointer returned to user */ + u_int32_t keylen; /* cipher key - optional */ + void * key; + + void * src, *dst; /* become iov[] inside kernel */ + void * mac; /* must be big enough for chosen MAC */ + void * iv; +}; + +/* CIOCNCRYPTM ioctl argument, supporting one or more asynchronous + * crypto_n_op operations. + * Each crypo_n_op will receive a request id which can be used to check its + * status via CIOCNCRYPTRET, or to watch for its completion in the list + * obtained via CIOCNCRYPTRETM. + */ +struct crypto_mop { + size_t count; /* how many */ + struct crypt_n_op * reqs; /* where to get them */ +}; + +struct crypt_fnop { + size_t count; + u_int32_t *sesid; +}; + #define CRYPTO_MAX_MAC_LEN 20 /* bignum parameter, in packed bytes, ... */ @@ -152,6 +255,53 @@ struct crypt_kop { u_int crk_pad1; struct crparam crk_param[CRK_MAXPARAM]; }; + +/* + * Used with the CIOCNFKEYM ioctl. + * + * This structure allows the OCF to return a request id + * for each of the kop operations specified in the CIOCNFKEYM call. + * + * The crk_opaque pointer can be arbitrarily set by the user + * and it is passed back in the crypt_result_t structure + * when the request completes. This field can be used for example + * to track context for the request and avoid lookups in the + * user application. + */ +struct crypt_n_kop { + u_int crk_op; /* ie. CRK_MOD_EXP or other */ + u_int crk_status; /* return status */ + u_short crk_iparams; /* # of input parameters */ + u_short crk_oparams; /* # of output parameters */ + u_int crk_pad1; + struct crparam crk_param[CRK_MAXPARAM]; + u_int32_t reqid; /* request id */ + int status; /* status of request -accepted or not */ + void *crk_opaque; /* opaque pointer returned to user */ +}; + +struct crypto_mkop { + size_t count; /* how many */ + struct crypt_n_kop * reqs; /* where to get them */ +}; + +/* Asynchronous key or crypto result. + * Note that the status will be set in the crypt_result_t structure, + * not in the original crypt_kop structure (crk_status). + */ +struct crypt_result_t { + u_int32_t rid; /* request id */ + u_int32_t status; /* status of request: 0 if successful */ + void * opaque; /* Opaque pointer from the user, passed along */ +}; + +struct cryptret_t { + size_t count; /* space for how many */ + struct crypt_result_t * results; /* where to put them */ +}; + + +/* Assymetric key operations */ #define CRK_ALGORITM_MIN 0 #define CRK_MOD_EXP 0 #define CRK_MOD_EXP_CRT 1 @@ -178,6 +328,146 @@ struct crypt_kop { #define CRF_MOD_MULTINV (1 << CRK_MOD_MULTINV) #define CRF_MOD (1 << CRK_MOD) +/********************************************************************** + + IOCTL Request descriptions + + CRIOGET int *fd + Clone the fd argument to ioctl(4), yielding a new file descrip- + tor which can be used to create crypto sessions and request + crypto operations. + + CIOCGSESSION struct session_op *sessp + Persistently bind a file descriptor returned by a previous + CRIOGET to a session: that is, to the chosen privacy algorithm, + integrity algorithm, and keys specified in sessp. The special + value 0 for either privacy or integrity is reserved to indicate + that the indicated operation (privacy or integrity) is not + desired for this session. + + For non-zero symmetric-key privacy algorithms, the privacy + algorithm must be specified in sess->cipher, the key length in + sessp->keylen, and the key value in the octets addressed by + sessp->key. + + For keyed one-way hash algorithms, the one-way hash must be + specified in sessp->mac, the key length in sessp->mackey, and + the key value in the octets addressed by sessp->mackeylen. + + Support for a specific combination of fused privacy and + integrity-check algorithms depends on whether the underlying + hardware supports that combination. Not all combinations are + supported by all hardware, even if the hardware supports each + operation as a stand-alone non-fused operation. + + CIOCCRYPT struct crypt_op *cr_op + Request a symmetric-key (or unkeyed hash) operation. The file + descriptor argument to ioctl(4) must have been bound to a valid + session. To encrypt, set cr_op->op to COP_ENCRYPT. To + decrypt, set cr_op->op to COP_DECRYPT. The field cr_op->len + supplies the length of the input buffer; the fields cr_op->src, + cr_op->dst, cr_op->mac, cr_op->iv supply the addresses of the + input buffer, output buffer, one-way hash, and initialization + vector, respectively. + + CIOCNCRYPTM struct crypto_mop *cr_mop + This is the asynchronous version of CIOCCRYPT, which allows + multiple symmetric-key (or unkeyed hash) operations to be + started (see CIOCRYPT above for the details for each operation). + The cr_mop->count field specifies the number of operations + provided in the cr_mop->reqs array. + + Each operation is assigned a unique request id returned in the + cr_mop->reqs[n].reqid field. + + Each operation can accept an opaque value from the user to + be passed back to the user when the operation completes + (e.g. to track context for the request). The opaque field + is cr_mop->reqs[n].opaque. + + If a problem occurs with starting any of the operations then + that operation's cr_mop->reqs[n].status field is filled with + the error code. The failure of an operation does not prevent + the other operations from being started. + + + CIOCFSESSION u_int32_t *session + Destroys the specified /dev/crypto session associated with the + file-descriptor argument. + + CIOCFNSESSION struct crypt_fnop *cr_fnop + Destroys the cr_fnop->count /dev/crypto sessions specified by + the crypt_fnop array of session identifiers. + + Asymmetric-key commands + CIOCASSYMFEAT int *feature_mask + Returns a bitmask of supported asymmetric-key operations. Each + of the above-listed asymmetric operations is present if and + only if the bit position numbered by the code for that opera- + tion is set. For example, CRK_MOD_EXP is available if and only + if the bit (1 << CRK_MOD_EX) is set. + + CIOCFKEY struct crypt_kop *kop + Performs an asymmetric-key operation from the list above. The + specific operation is supplied in kop->crk_op; final status for + the operation is returned in kop->crk_status. The number of + input arguments and the number of output arguments is specified + in kop->crk_iparams and kop->crk_iparams, respectively. The + field crk_param[] must be filled in with exactly + kop->crk_iparams + kop->crk_oparams arguments, each encoded as + a struct crparam (address, bitlength) pair. + + CIOCNFKEYM struct crypt_mkop *mkop + This is the asynchronous version of CIOCKEY, which starts one + or more key operations. + mkop->count specifies the number of key operations in the + mkop->reqs array. + + Each operation is assigned a unique request id returned in the + mkop->reqs[n].reqid field. + + Each operation accepts an opaque value from the user to + be passed back to the user when the operation completes + (e.g. to track context for the request). The opaque field + is mkop->reqs[n].crk_opaque. + + If a problem occurs with starting any of the operations then + that operation's mkop->reqs[n].status field is filled with + the error code. The failure of an operation does not prevent + the other operations from being started. + + Asynchronous status commands + select() or poll() can be used to determine when /dev/crypto has completed + operations ready to be retrieved. + + CIOCNCRYPTRET struct crypt_result_t *cres + Check for the status of the request specified by cres->rid. + + The cres->status field is set as follows: + 0 - the request has completed, and its results have been + copied out to the original crypt_op or crypt_kop structure + used to start the request. The copyout takes place during + this ioctl, so the calling process must be the process that + started the request. + + EINPROGRESS - the request has not yet completed. + + EINVAL - the request was not found. + + Other values indicate a problem during the processing of + the request. + + CIOCNCRYPTRETM struct cryptret_t *cret + Retreive a number of completed requests. This ioctl accepts + a count and an array, and fills the array with up to count + completed requests. The array entries are described above + in the CIONCRYPTRET ioctl. + + The cret->results[n].rid fields filled by this ioctl call. + The results may be mixed crypt_op and crypt_kop operations. + +**********************************************************************/ + /* * done against open of /dev/crypto, to get a cloned descriptor. * Please use F_SETFD against the cloned descriptor. @@ -189,6 +479,12 @@ struct crypt_kop { #define CIOCFSESSION _IOW('c', 102, u_int32_t) #define CIOCCRYPT _IOWR('c', 103, struct crypt_op) #define CIOCKEY _IOWR('c', 104, struct crypt_kop) +#define CIOCNSESSION _IOWR('c', 106, struct session_n_op) +#define CIOCNCRYPTM _IOWR('c', 107, struct crypto_mop) +#define CIOCNFKEYM _IOWR('c', 108, struct crypto_mkop) +#define CIOCFNSESSION _IOW('c', 109, struct crypt_fnop) +#define CIOCNCRYPTRETM _IOWR('c', 110, struct cryptret_t) +#define CIOCNCRYPTRET _IOWR('c', 111, struct crypt_result_t) #define CIOCASYMFEAT _IOR('c', 105, u_int32_t) @@ -257,9 +553,16 @@ struct cryptodesc { /* Structure describing complete operation */ struct cryptop { - TAILQ_ENTRY(cryptop) crp_next; - + union { + TAILQ_ENTRY(cryptop) crp_tnext; + SLIST_ENTRY(cryptop) crp_lnext; + } crp_qun; +#define crp_next crp_qun.crp_tnext /* XXX compat */ u_int64_t crp_sid; /* Session ID */ + + u_int32_t crp_reqid; /* request id */ + void * crp_usropaque; /* Opaque pointer from user, passed along */ + int crp_ilen; /* Input data total length */ int crp_olen; /* Result total length */ @@ -293,6 +596,15 @@ struct cryptop { void * crp_mac; struct timespec crp_tstamp; /* performance time stamp */ kcondvar_t crp_cv; + struct fcrypt *fcrp; + void * dst; + void * mac; + u_int len; + u_char tmp_iv[EALG_MAX_BLOCK_LEN]; + u_char tmp_mac[CRYPTO_MAX_MAC_LEN]; + + struct iovec iovec[1]; + struct uio uio; }; #define CRYPTO_BUF_CONTIG 0x0 @@ -308,7 +620,14 @@ struct cryptop { #define CRYPTO_HINT_MORE 0x1 /* more ops coming shortly */ struct cryptkop { - TAILQ_ENTRY(cryptkop) krp_next; + union { + TAILQ_ENTRY(cryptkop) krp_tnext; + SLIST_ENTRY(cryptkop) krp_lnext; + } krp_qun; +#define krp_next krp_qun.krp_tnext /* XXX compat */ + + u_int32_t krp_reqid; /* request id */ + void * krp_usropaque; /* Opaque pointer from user, passed along */ u_int krp_op; /* ie. CRK_MOD_EXP or other */ u_int krp_status; /* return status */ @@ -319,6 +638,8 @@ struct cryptkop { int (*krp_callback)(struct cryptkop *); int krp_flags; /* same values as crp_flags */ kcondvar_t krp_cv; + struct fcrypt *fcrp; + struct crparam crk_param[CRK_MAXPARAM]; }; /* Crypto capabilities structure */