/*-
* 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.
*
* 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
*
* 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.
*
*/
u_int32_t cipher; /* note: shares name space in crd_alg */
const struct enc_xform *txform;
u_int32_t mac; /* note: shares name space in crd_alg */
const struct auth_hash *thash;
u_int32_t comp_alg; /* note: shares name space in crd_alg */
const struct comp_algo *tcomp;
void * key;
int keylen;
u_char tmp_iv[EALG_MAX_BLOCK_LEN];
void * mackey;
int mackeylen;
u_char tmp_mac[CRYPTO_MAX_MAC_LEN];
struct iovec iovec[1]; /* user requests never have more */
struct uio uio;
int error;
};
/* For our fixed-size allocations */
static struct pool fcrpl;
static struct pool csepl;
/* Declaration of master device (fd-cloning/ctxt-allocating) entrypoints */
static int cryptoopen(dev_t dev, int flag, int mode, struct lwp *l);
static int cryptoread(dev_t dev, struct uio *uio, int ioflag);
static int cryptowrite(dev_t dev, struct uio *uio, int ioflag);
static int cryptoselect(dev_t dev, int rw, struct lwp *l);
static int crypto_refcount = 0; /* Prevent detaching while in use */
/* Declaration of cloned-device (per-ctxt) entrypoints */
static int cryptof_read(struct file *, off_t *, struct uio *,
kauth_cred_t, int);
static int cryptof_write(struct file *, off_t *, struct uio *,
kauth_cred_t, int);
static int cryptof_ioctl(struct file *, u_long, void *);
static int cryptof_close(struct file *);
static int cryptof_poll(struct file *, int);
static int cryptof_stat(struct file *, struct stat *);
dst_len = crp->crp_ilen;
/* let the user know how much data was returned */
if (crp->crp_olen) {
if (crp->crp_olen > (cop->dst_len ? cop->dst_len : cop->len)) {
error = ENOSPC;
goto bail;
}
dst_len = cop->dst_len = crp->crp_olen;
}
if (cop->dst) {
DPRINTF("copyout %d bytes to %p\n", dst_len, cop->dst);
}
if (cop->dst &&
(error = copyout(cse->uio.uio_iov[0].iov_base, cop->dst, dst_len)))
{
DPRINTF("copyout error %d\n", error);
goto bail;
}
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;
case CRK_MOD_ADD:
if (in == 3 && out == 1)
break;
return EINVAL;
case CRK_MOD_ADDINV:
if (in == 2 && out == 1)
break;
return EINVAL;
case CRK_MOD_SUB:
if (in == 3 && out == 1)
break;
return EINVAL;
case CRK_MOD_MULT:
if (in == 3 && out == 1)
break;
return EINVAL;
case CRK_MOD_MULTINV:
if (in == 2 && out == 1)
break;
return EINVAL;
case CRK_MOD:
if (in == 2 && out == 1)
break;
return EINVAL;
default:
return EINVAL;
}
iov_len = cnop[req].len;
/* got a compression/decompression max size? */
if ((cse->tcomp) && cnop[req].dst_len) {
if (iov_len < cnop[req].dst_len) {
/* Need larger iov to deal with decompress */
iov_len = cnop[req].dst_len;
}
DPRINTF("iov_len -> %d for decompress\n", iov_len);
}
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].crk_status = EINVAL;
continue;
case CRK_MOD_EXP_CRT:
if (in == 6 && out == 1)
break;
kop[req].crk_status = EINVAL;
continue;
case CRK_DSA_SIGN:
if (in == 5 && out == 2)
break;
kop[req].crk_status = EINVAL;
continue;
case CRK_DSA_VERIFY:
if (in == 7 && out == 0)
break;
kop[req].crk_status = EINVAL;
continue;
case CRK_DH_COMPUTE_KEY:
if (in == 3 && out == 1)
break;
kop[req].crk_status = EINVAL;
continue;
case CRK_MOD_ADD:
if (in == 3 && out == 1)
break;
kop[req].crk_status = EINVAL;
continue;
case CRK_MOD_ADDINV:
if (in == 2 && out == 1)
break;
kop[req].crk_status = EINVAL;
continue;
case CRK_MOD_SUB:
if (in == 3 && out == 1)
break;
kop[req].crk_status = EINVAL;
continue;
case CRK_MOD_MULT:
if (in == 3 && out == 1)
break;
kop[req].crk_status = EINVAL;
continue;
case CRK_MOD_MULTINV:
if (in == 2 && out == 1)
break;
kop[req].crk_status = EINVAL;
continue;
case CRK_MOD:
if (in == 2 && out == 1)
break;
kop[req].crk_status = EINVAL;
continue;
default:
kop[req].crk_status = EINVAL;
continue;
}
/* XXX there must be a way to not embed the list of xforms here */
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_aes;
break;
case CRYPTO_CAMELLIA_CBC:
txform = &enc_xform_camellia;
break;
case CRYPTO_AES_CTR:
txform = &enc_xform_aes_ctr;
break;
case CRYPTO_AES_GCM_16:
txform = &enc_xform_aes_gcm;
break;
case CRYPTO_AES_GMAC:
txform = &enc_xform_aes_gmac;
break;
case CRYPTO_NULL_CBC:
txform = &enc_xform_null;
break;
case CRYPTO_ARC4:
txform = &enc_xform_arc4;
break;
default:
DPRINTF("Invalid cipher %d\n", sop->cipher);
return EINVAL;
}
switch (sop->comp_alg) {
case 0:
break;
case CRYPTO_DEFLATE_COMP:
tcomp = &comp_algo_deflate;
break;
case CRYPTO_GZIP_COMP:
tcomp = &comp_algo_gzip;
DPRINTF("tcomp for GZIP\n");
break;
default:
DPRINTF("Invalid compression alg %d\n", sop->comp_alg);
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_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:
/* XXX switching on key length seems questionable */
if (sop->mackeylen == auth_hash_hmac_sha2_256.keysize) {
thash = &auth_hash_hmac_sha2_256;
} else if (sop->mackeylen == auth_hash_hmac_sha2_384.keysize) {
thash = &auth_hash_hmac_sha2_384;
} else if (sop->mackeylen == auth_hash_hmac_sha2_512.keysize) {
thash = &auth_hash_hmac_sha2_512;
} else {
DPRINTF("Invalid mackeylen %d\n", sop->mackeylen);
return EINVAL;
}
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_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_AES_XCBC_MAC_96:
thash = &auth_hash_aes_xcbc_mac_96;
break;
case CRYPTO_AES_128_GMAC:
thash = &auth_hash_gmac_aes_128;
break;
case CRYPTO_AES_192_GMAC:
thash = &auth_hash_gmac_aes_192;
break;
case CRYPTO_AES_256_GMAC:
thash = &auth_hash_gmac_aes_256;
break;
case CRYPTO_NULL_HMAC:
thash = &auth_hash_null;
break;
default:
DPRINTF("Invalid mac %d\n", sop->mac);
return EINVAL;
}
/*
* collect as many completed requests as are available, or count completed
* requests, whichever is less.
* return the number of requests.
*/
static int
cryptodev_getmstatus(struct fcrypt *fcr, struct crypt_result *crypt_res,
int count)
{
struct cryptop *crp = NULL;
struct cryptkop *krp = NULL;
struct csession *cse;
int i, size, req = 0;
int completed=0;
/* On queue so nobody else can grab them
* and copyout can be delayed-- no locking */
TAILQ_HEAD(, cryptop) crp_delfree_q =
TAILQ_HEAD_INITIALIZER(crp_delfree_q);
TAILQ_HEAD(, cryptkop) krp_delfree_q =
TAILQ_HEAD_INITIALIZER(krp_delfree_q);
/* 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 */
/* pull off a list of requests while protected from changes */
mutex_enter(&cryptodev_mtx);
while (req < count) {
crp = TAILQ_FIRST(&fcr->crp_ret_mq);
if (crp) {
TAILQ_REMOVE(&fcr->crp_ret_mq, crp, crp_next);
TAILQ_INSERT_TAIL(&crp_delfree_q, crp, crp_next);
cse = (struct csession *)crp->crp_opaque;
/* see if the session is still valid */
cse = csefind(fcr, cse->ses);
if (cse != NULL) {
crypt_res[req].status = 0;
} else {
DPRINTF("csefind failed\n");
crypt_res[req].status = EINVAL;
}
req++;
}
if(req < count) {
crypt_res[req].status = 0;
krp = TAILQ_FIRST(&fcr->crp_ret_mkq);
if (krp) {
TAILQ_REMOVE(&fcr->crp_ret_mkq, krp, krp_next);
TAILQ_INSERT_TAIL(&krp_delfree_q, krp, krp_next);
req++;
}
}
}
mutex_exit(&cryptodev_mtx);
/* now do all the work outside the mutex */
for(req=0; req < count ;) {
crp = TAILQ_FIRST(&crp_delfree_q);
if (crp) {
if (crypt_res[req].status != 0) {
/* csefind failed during collection */
goto bail;
}
cse = (struct csession *)crp->crp_opaque;
crypt_res[req].reqid = crp->crp_reqid;
crypt_res[req].opaque = crp->crp_usropaque;
completed++;
mutex_enter(&cryptodev_mtx);
/* Here we dont know for which request the user is requesting the
* response so checking in both the queues */
TAILQ_FOREACH_SAFE(crp, &fcr->crp_ret_mq, crp_next, cnext) {
if(crp && (crp->crp_reqid == crypt_res->reqid)) {
cse = (struct csession *)crp->crp_opaque;
crypt_res->opaque = crp->crp_usropaque;
cse = csefind(fcr, cse->ses);
if (cse == NULL) {
DPRINTF("csefind failed\n");
crypt_res->status = EINVAL;
goto bail;
}
static int
cryptof_poll(struct file *fp, int events)
{
struct fcrypt *fcr = fp->f_fcrypt;
int revents = 0;
if (!(events & (POLLIN | POLLRDNORM))) {
/* only support read and POLLIN */
return 0;
}
mutex_enter(&cryptodev_mtx);
if (TAILQ_EMPTY(&fcr->crp_ret_mq) && TAILQ_EMPTY(&fcr->crp_ret_mkq)) {
/* no completed requests pending, save the poll for later */
selrecord(curlwp, &fcr->sinfo);
} else {
/* let the app(s) know that there are completed requests */
revents = events & (POLLIN | POLLRDNORM);
}
mutex_exit(&cryptodev_mtx);
/*
* Preallocate space for 64 users, with 5 sessions each.
* (consider that a TLS protocol session requires at least
* 3DES, MD5, and SHA1 (both hashes are used in the PRF) for
* the negotiation, plus HMAC_SHA1 for the actual SSL records,
* consuming one session here for each algorithm.
*/
pool_prime(&fcrpl, 64);
pool_prime(&csepl, 64 * 5);
}
return error;
#ifdef _MODULE
case MODULE_CMD_AUTOUNLOAD:
#if 0 /*
* XXX Completely disable auto-unload for now, since there is still
* XXX a (small) window where in-module ref-counting doesn't help
*/
if (crypto_refcount != 0)
#endif
return EBUSY;
/* FALLTHROUGH */
#endif
default:
return ENOTTY;
}
}