Esempio n. 1
0
/*
 * Perform the MPPE rekey algorithm, from RFC 3078, sec. 7.3.
 * Well, not what's written there, but rather what they meant.
 */
static void mppe_rekey(struct ppp_mppe_state * state, int initial_key)
{
	struct scatterlist sg_in[1], sg_out[1];
	struct blkcipher_desc desc = { .tfm = state->arc4 };

	get_new_key_from_sha(state);
	if (!initial_key) {
		crypto_blkcipher_setkey(state->arc4, state->sha1_digest,
					state->keylen);
		sg_init_table(sg_in, 1);
		sg_init_table(sg_out, 1);
		setup_sg(sg_in, state->sha1_digest, state->keylen);
		setup_sg(sg_out, state->session_key, state->keylen);
		if (crypto_blkcipher_encrypt(&desc, sg_out, sg_in,
					     state->keylen) != 0) {
    		    printk(KERN_WARNING "mppe_rekey: cipher_encrypt failed\n");
		}
	} else {
		memcpy(state->session_key, state->sha1_digest, state->keylen);
	}
	if (state->keylen == 8) {
		/* See RFC 3078 */
		state->session_key[0] = 0xd1;
		state->session_key[1] = 0x26;
		state->session_key[2] = 0x9e;
	}
	crypto_blkcipher_setkey(state->arc4, state->session_key, state->keylen);
}

/*
 * Allocate space for a (de)compressor.
 */
static void *mppe_alloc(unsigned char *options, int optlen)
{
	struct ppp_mppe_state *state;
	unsigned int digestsize;

	if (optlen != CILEN_MPPE + sizeof(state->master_key) ||
	    options[0] != CI_MPPE || options[1] != CILEN_MPPE)
		goto out;

	state = kzalloc(sizeof(*state), GFP_KERNEL);
	if (state == NULL)
		goto out;


	state->arc4 = crypto_alloc_blkcipher("ecb(arc4)", 0, CRYPTO_ALG_ASYNC);
	if (IS_ERR(state->arc4)) {
		state->arc4 = NULL;
		goto out_free;
	}

	state->sha1 = crypto_alloc_hash("sha1", 0, CRYPTO_ALG_ASYNC);
	if (IS_ERR(state->sha1)) {
		state->sha1 = NULL;
		goto out_free;
	}

	digestsize = crypto_hash_digestsize(state->sha1);
	if (digestsize < MPPE_MAX_KEY_LEN)
		goto out_free;

	state->sha1_digest = kmalloc(digestsize, GFP_KERNEL);
	if (!state->sha1_digest)
		goto out_free;

	/* Save keys. */
	memcpy(state->master_key, &options[CILEN_MPPE],
	       sizeof(state->master_key));
	memcpy(state->session_key, state->master_key,
	       sizeof(state->master_key));

	/*
	 * We defer initial key generation until mppe_init(), as mppe_alloc()
	 * is called frequently during negotiation.
	 */

	return (void *)state;

	out_free:
	    if (state->sha1_digest)
		kfree(state->sha1_digest);
	    if (state->sha1)
		crypto_free_hash(state->sha1);
	    if (state->arc4)
		crypto_free_blkcipher(state->arc4);
	    kfree(state);
	out:
	return NULL;
}

/*
 * Deallocate space for a (de)compressor.
 */
static void mppe_free(void *arg)
{
	struct ppp_mppe_state *state = (struct ppp_mppe_state *) arg;
	if (state) {
	    if (state->sha1_digest)
		kfree(state->sha1_digest);
	    if (state->sha1)
		crypto_free_hash(state->sha1);
	    if (state->arc4)
		crypto_free_blkcipher(state->arc4);
	    kfree(state);
	}
}

/*
 * Initialize (de)compressor state.
 */
static int
mppe_init(void *arg, unsigned char *options, int optlen, int unit, int debug,
	  const char *debugstr)
{
	struct ppp_mppe_state *state = (struct ppp_mppe_state *) arg;
	unsigned char mppe_opts;

	if (optlen != CILEN_MPPE ||
	    options[0] != CI_MPPE || options[1] != CILEN_MPPE)
		return 0;

	MPPE_CI_TO_OPTS(&options[2], mppe_opts);
	if (mppe_opts & MPPE_OPT_128)
		state->keylen = 16;
	else if (mppe_opts & MPPE_OPT_40)
		state->keylen = 8;
	else {
		printk(KERN_WARNING "%s[%d]: unknown key length\n", debugstr,
		       unit);
		return 0;
	}
	if (mppe_opts & MPPE_OPT_STATEFUL)
		state->stateful = 1;

	/* Generate the initial session key. */
	mppe_rekey(state, 1);

	if (debug) {
		int i;
		char mkey[sizeof(state->master_key) * 2 + 1];
		char skey[sizeof(state->session_key) * 2 + 1];

		printk(KERN_DEBUG "%s[%d]: initialized with %d-bit %s mode\n",
		       debugstr, unit, (state->keylen == 16) ? 128 : 40,
		       (state->stateful) ? "stateful" : "stateless");

		for (i = 0; i < sizeof(state->master_key); i++)
			sprintf(mkey + i * 2, "%02x", state->master_key[i]);
		for (i = 0; i < sizeof(state->session_key); i++)
			sprintf(skey + i * 2, "%02x", state->session_key[i]);
		printk(KERN_DEBUG
		       "%s[%d]: keys: master: %s initial session: %s\n",
		       debugstr, unit, mkey, skey);
	}

	/*
	 * Initialize the coherency count.  The initial value is not specified
	 * in RFC 3078, but we can make a reasonable assumption that it will
	 * start at 0.  Setting it to the max here makes the comp/decomp code
	 * do the right thing (determined through experiment).
	 */
	state->ccount = MPPE_CCOUNT_SPACE - 1;

	/*
	 * Note that even though we have initialized the key table, we don't
	 * set the FLUSHED bit.  This is contrary to RFC 3078, sec. 3.1.
	 */
	state->bits = MPPE_BIT_ENCRYPTED;

	state->unit = unit;
	state->debug = debug;

	return 1;
}

static int
mppe_comp_init(void *arg, unsigned char *options, int optlen, int unit,
	       int hdrlen, int debug)
{
	/* ARGSUSED */
	return mppe_init(arg, options, optlen, unit, debug, "mppe_comp_init");
}

/*
 * We received a CCP Reset-Request (actually, we are sending a Reset-Ack),
 * tell the compressor to rekey.  Note that we MUST NOT rekey for
 * every CCP Reset-Request; we only rekey on the next xmit packet.
 * We might get multiple CCP Reset-Requests if our CCP Reset-Ack is lost.
 * So, rekeying for every CCP Reset-Request is broken as the peer will not
 * know how many times we've rekeyed.  (If we rekey and THEN get another
 * CCP Reset-Request, we must rekey again.)
 */
static void mppe_comp_reset(void *arg)
{
	struct ppp_mppe_state *state = (struct ppp_mppe_state *) arg;

	state->bits |= MPPE_BIT_FLUSHED;
}

/*
 * Compress (encrypt) a packet.
 * It's strange to call this a compressor, since the output is always
 * MPPE_OVHD + 2 bytes larger than the input.
 */
static int
mppe_compress(void *arg, unsigned char *ibuf, unsigned char *obuf,
	      int isize, int osize)
{
	struct ppp_mppe_state *state = (struct ppp_mppe_state *) arg;
	struct blkcipher_desc desc = { .tfm = state->arc4 };
	int proto;
	struct scatterlist sg_in[1], sg_out[1];

	/*
	 * Check that the protocol is in the range we handle.
	 */
	proto = PPP_PROTOCOL(ibuf);
	if (proto < 0x0021 || proto > 0x00fa)
		return 0;

	/* Make sure we have enough room to generate an encrypted packet. */
	if (osize < isize + MPPE_OVHD + 2) {
		/* Drop the packet if we should encrypt it, but can't. */
		printk(KERN_DEBUG "mppe_compress[%d]: osize too small! "
		       "(have: %d need: %d)\n", state->unit,
		       osize, osize + MPPE_OVHD + 2);
		return -1;
	}

	osize = isize + MPPE_OVHD + 2;

	/*
	 * Copy over the PPP header and set control bits.
	 */
	obuf[0] = PPP_ADDRESS(ibuf);
	obuf[1] = PPP_CONTROL(ibuf);
	put_unaligned_be16(PPP_COMP, obuf + 2);
	obuf += PPP_HDRLEN;

	state->ccount = (state->ccount + 1) % MPPE_CCOUNT_SPACE;
	if (state->debug >= 7)
		printk(KERN_DEBUG "mppe_compress[%d]: ccount %d\n", state->unit,
		       state->ccount);
	put_unaligned_be16(state->ccount, obuf);

	if (!state->stateful ||	/* stateless mode     */
	    ((state->ccount & 0xff) == 0xff) ||	/* "flag" packet      */
	    (state->bits & MPPE_BIT_FLUSHED)) {	/* CCP Reset-Request  */
		/* We must rekey */
		if (state->debug && state->stateful)
			printk(KERN_DEBUG "mppe_compress[%d]: rekeying\n",
			       state->unit);
		mppe_rekey(state, 0);
		state->bits |= MPPE_BIT_FLUSHED;
	}
	obuf[0] |= state->bits;
	state->bits &= ~MPPE_BIT_FLUSHED;	/* reset for next xmit */

	obuf += MPPE_OVHD;
	ibuf += 2;		/* skip to proto field */
	isize -= 2;

	/* Encrypt packet */
	sg_init_table(sg_in, 1);
	sg_init_table(sg_out, 1);
	setup_sg(sg_in, ibuf, isize);
	setup_sg(sg_out, obuf, osize);
	if (crypto_blkcipher_encrypt(&desc, sg_out, sg_in, isize) != 0) {
		printk(KERN_DEBUG "crypto_cypher_encrypt failed\n");
		return -1;
	}

	state->stats.unc_bytes += isize;
	state->stats.unc_packets++;
	state->stats.comp_bytes += osize;
	state->stats.comp_packets++;

	return osize;
}

/*
 * Since every frame grows by MPPE_OVHD + 2 bytes, this is always going
 * to look bad ... and the longer the link is up the worse it will get.
 */
static void mppe_comp_stats(void *arg, struct compstat *stats)
{
	struct ppp_mppe_state *state = (struct ppp_mppe_state *) arg;

	*stats = state->stats;
}

static int
mppe_decomp_init(void *arg, unsigned char *options, int optlen, int unit,
		 int hdrlen, int mru, int debug)
{
	/* ARGSUSED */
	return mppe_init(arg, options, optlen, unit, debug, "mppe_decomp_init");
}

/*
 * We received a CCP Reset-Ack.  Just ignore it.
 */
static void mppe_decomp_reset(void *arg)
{
	/* ARGSUSED */
	return;
}

/*
 * Decompress (decrypt) an MPPE packet.
 */
static int
mppe_decompress(void *arg, unsigned char *ibuf, int isize, unsigned char *obuf,
		int osize)
{
	struct ppp_mppe_state *state = (struct ppp_mppe_state *) arg;
	struct blkcipher_desc desc = { .tfm = state->arc4 };
	unsigned ccount;
	int flushed = MPPE_BITS(ibuf) & MPPE_BIT_FLUSHED;
	int sanity = 0;
	struct scatterlist sg_in[1], sg_out[1];

	if (isize <= PPP_HDRLEN + MPPE_OVHD) {
		if (state->debug)
			printk(KERN_DEBUG
			       "mppe_decompress[%d]: short pkt (%d)\n",
			       state->unit, isize);
		return DECOMP_ERROR;
	}

	/*
	 * Make sure we have enough room to decrypt the packet.
	 * Note that for our test we only subtract 1 byte whereas in
	 * mppe_compress() we added 2 bytes (+MPPE_OVHD);
	 * this is to account for possible PFC.
	 */
	if (osize < isize - MPPE_OVHD - 1) {
		printk(KERN_DEBUG "mppe_decompress[%d]: osize too small! "
		       "(have: %d need: %d)\n", state->unit,
		       osize, isize - MPPE_OVHD - 1);
		return DECOMP_ERROR;
	}
	osize = isize - MPPE_OVHD - 2;	/* assume no PFC */

	ccount = MPPE_CCOUNT(ibuf);
	if (state->debug >= 7)
		printk(KERN_DEBUG "mppe_decompress[%d]: ccount %d\n",
		       state->unit, ccount);

	/* sanity checks -- terminate with extreme prejudice */
	if (!(MPPE_BITS(ibuf) & MPPE_BIT_ENCRYPTED)) {
		printk(KERN_DEBUG
		       "mppe_decompress[%d]: ENCRYPTED bit not set!\n",
		       state->unit);
		state->sanity_errors += 100;
		sanity = 1;
	}
	if (!state->stateful && !flushed) {
		printk(KERN_DEBUG "mppe_decompress[%d]: FLUSHED bit not set in "
		       "stateless mode!\n", state->unit);
		state->sanity_errors += 100;
		sanity = 1;
	}
	if (state->stateful && ((ccount & 0xff) == 0xff) && !flushed) {
		printk(KERN_DEBUG "mppe_decompress[%d]: FLUSHED bit not set on "
		       "flag packet!\n", state->unit);
		state->sanity_errors += 100;
		sanity = 1;
	}

	if (sanity) {
		if (state->sanity_errors < SANITY_MAX)
			return DECOMP_ERROR;
		else
			/*
			 * Take LCP down if the peer is sending too many bogons.
			 * We don't want to do this for a single or just a few
			 * instances since it could just be due to packet corruption.
			 */
			return DECOMP_FATALERROR;
	}

	/*
	 * Check the coherency count.
	 */

	if (!state->stateful) {
		/* RFC 3078, sec 8.1.  Rekey for every packet. */
		while (state->ccount != ccount) {
			mppe_rekey(state, 0);
			state->ccount = (state->ccount + 1) % MPPE_CCOUNT_SPACE;
		}
	} else {
		/* RFC 3078, sec 8.2. */
		if (!state->discard) {
			/* normal state */
			state->ccount = (state->ccount + 1) % MPPE_CCOUNT_SPACE;
			if (ccount != state->ccount) {
				/*
				 * (ccount > state->ccount)
				 * Packet loss detected, enter the discard state.
				 * Signal the peer to rekey (by sending a CCP Reset-Request).
				 */
				state->discard = 1;
				return DECOMP_ERROR;
			}
		} else {
			/* discard state */
			if (!flushed) {
				/* ccp.c will be silent (no additional CCP Reset-Requests). */
				return DECOMP_ERROR;
			} else {
				/* Rekey for every missed "flag" packet. */
				while ((ccount & ~0xff) !=
				       (state->ccount & ~0xff)) {
					mppe_rekey(state, 0);
					state->ccount =
					    (state->ccount +
					     256) % MPPE_CCOUNT_SPACE;
				}

				/* reset */
				state->discard = 0;
				state->ccount = ccount;
				/*
				 * Another problem with RFC 3078 here.  It implies that the
				 * peer need not send a Reset-Ack packet.  But RFC 1962
				 * requires it.  Hopefully, M$ does send a Reset-Ack; even
				 * though it isn't required for MPPE synchronization, it is
				 * required to reset CCP state.
				 */
			}
		}
		if (flushed)
			mppe_rekey(state, 0);
	}

	/*
	 * Fill in the first part of the PPP header.  The protocol field
	 * comes from the decrypted data.
	 */
	obuf[0] = PPP_ADDRESS(ibuf);	/* +1 */
	obuf[1] = PPP_CONTROL(ibuf);	/* +1 */
	obuf += 2;
	ibuf += PPP_HDRLEN + MPPE_OVHD;
	isize -= PPP_HDRLEN + MPPE_OVHD;	/* -6 */
	/* net osize: isize-4 */

	/*
	 * Decrypt the first byte in order to check if it is
	 * a compressed or uncompressed protocol field.
	 */
	sg_init_table(sg_in, 1);
	sg_init_table(sg_out, 1);
	setup_sg(sg_in, ibuf, 1);
	setup_sg(sg_out, obuf, 1);
	if (crypto_blkcipher_decrypt(&desc, sg_out, sg_in, 1) != 0) {
		printk(KERN_DEBUG "crypto_cypher_decrypt failed\n");
		return DECOMP_ERROR;
	}

	/*
	 * Do PFC decompression.
	 * This would be nicer if we were given the actual sk_buff
	 * instead of a char *.
	 */
	if ((obuf[0] & 0x01) != 0) {
		obuf[1] = obuf[0];
		obuf[0] = 0;
		obuf++;
		osize++;
	}

	/* And finally, decrypt the rest of the packet. */
	setup_sg(sg_in, ibuf + 1, isize - 1);
	setup_sg(sg_out, obuf + 1, osize - 1);
	if (crypto_blkcipher_decrypt(&desc, sg_out, sg_in, isize - 1)) {
		printk(KERN_DEBUG "crypto_cypher_decrypt failed\n");
		return DECOMP_ERROR;
	}

	state->stats.unc_bytes += osize;
	state->stats.unc_packets++;
	state->stats.comp_bytes += isize;
	state->stats.comp_packets++;

	/* good packet credit */
	state->sanity_errors >>= 1;

	return osize;
}

/*
 * Incompressible data has arrived (this should never happen!).
 * We should probably drop the link if the protocol is in the range
 * of what should be encrypted.  At the least, we should drop this
 * packet.  (How to do this?)
 */
static void mppe_incomp(void *arg, unsigned char *ibuf, int icnt)
{
	struct ppp_mppe_state *state = (struct ppp_mppe_state *) arg;

	if (state->debug &&
	    (PPP_PROTOCOL(ibuf) >= 0x0021 && PPP_PROTOCOL(ibuf) <= 0x00fa))
		printk(KERN_DEBUG
		       "mppe_incomp[%d]: incompressible (unencrypted) data! "
		       "(proto %04x)\n", state->unit, PPP_PROTOCOL(ibuf));

	state->stats.inc_bytes += icnt;
	state->stats.inc_packets++;
	state->stats.unc_bytes += icnt;
	state->stats.unc_packets++;
}

/*************************************************************
 * Module interface table
 *************************************************************/

/*
 * Procedures exported to if_ppp.c.
 */
static struct compressor ppp_mppe = {
	.compress_proto = CI_MPPE,
	.comp_alloc     = mppe_alloc,
	.comp_free      = mppe_free,
	.comp_init      = mppe_comp_init,
	.comp_reset     = mppe_comp_reset,
	.compress       = mppe_compress,
	.comp_stat      = mppe_comp_stats,
	.decomp_alloc   = mppe_alloc,
	.decomp_free    = mppe_free,
	.decomp_init    = mppe_decomp_init,
	.decomp_reset   = mppe_decomp_reset,
	.decompress     = mppe_decompress,
	.incomp         = mppe_incomp,
	.decomp_stat    = mppe_comp_stats,
	.owner          = THIS_MODULE,
	.comp_extra     = MPPE_PAD,
};

/*
 * ppp_mppe_init()
 *
 * Prior to allowing load, try to load the arc4 and sha1 crypto
 * libraries.  The actual use will be allocated later, but
 * this way the module will fail to insmod if they aren't available.
 */

static int __init ppp_mppe_init(void)
{
	int answer;
	if (!(crypto_has_blkcipher("ecb(arc4)", 0, CRYPTO_ALG_ASYNC) &&
	      crypto_has_hash("sha1", 0, CRYPTO_ALG_ASYNC)))
		return -ENODEV;

	sha_pad = kmalloc(sizeof(struct sha_pad), GFP_KERNEL);
	if (!sha_pad)
		return -ENOMEM;
	sha_pad_init(sha_pad);

	answer = ppp_register_compressor(&ppp_mppe);

	if (answer == 0)
		printk(KERN_INFO "PPP MPPE Compression module registered\n");
	else
		kfree(sha_pad);

	return answer;
}

static void __exit ppp_mppe_cleanup(void)
{
	ppp_unregister_compressor(&ppp_mppe);
	kfree(sha_pad);
}

module_init(ppp_mppe_init);
module_exit(ppp_mppe_cleanup);
Esempio n. 2
0
static int esp_init_state(struct xfrm_state *x)
{
	struct esp_data *esp = NULL;
	struct crypto_blkcipher *tfm;

	/* null auth and encryption can have zero length keys */
	if (x->aalg) {
		if (x->aalg->alg_key_len > 512)
			goto error;
	}
	if (x->ealg == NULL)
		goto error;

	esp = kzalloc(sizeof(*esp), GFP_KERNEL);
	if (esp == NULL)
		return -ENOMEM;

	if (x->aalg) {
		struct xfrm_algo_desc *aalg_desc;
		struct crypto_hash *hash;

		esp->auth.key = x->aalg->alg_key;
		esp->auth.key_len = (x->aalg->alg_key_len+7)/8;
		hash = crypto_alloc_hash(x->aalg->alg_name, 0,
					 CRYPTO_ALG_ASYNC);
		if (IS_ERR(hash))
			goto error;

		esp->auth.tfm = hash;
		if (crypto_hash_setkey(hash, esp->auth.key, esp->auth.key_len))
			goto error;

		aalg_desc = xfrm_aalg_get_byname(x->aalg->alg_name, 0);
		BUG_ON(!aalg_desc);

		if (aalg_desc->uinfo.auth.icv_fullbits/8 !=
		    crypto_hash_digestsize(hash)) {
			NETDEBUG(KERN_INFO "ESP: %s digestsize %u != %hu\n",
				 x->aalg->alg_name,
				 crypto_hash_digestsize(hash),
				 aalg_desc->uinfo.auth.icv_fullbits/8);
			goto error;
		}

		esp->auth.icv_full_len = aalg_desc->uinfo.auth.icv_fullbits/8;
		esp->auth.icv_trunc_len = aalg_desc->uinfo.auth.icv_truncbits/8;

		esp->auth.work_icv = kmalloc(esp->auth.icv_full_len, GFP_KERNEL);
		if (!esp->auth.work_icv)
			goto error;
	}
	esp->conf.key = x->ealg->alg_key;
	esp->conf.key_len = (x->ealg->alg_key_len+7)/8;
	tfm = crypto_alloc_blkcipher(x->ealg->alg_name, 0, CRYPTO_ALG_ASYNC);
	if (IS_ERR(tfm))
		goto error;
	esp->conf.tfm = tfm;
	esp->conf.ivlen = crypto_blkcipher_ivsize(tfm);
	esp->conf.padlen = 0;
	if (esp->conf.ivlen) {
		esp->conf.ivec = kmalloc(esp->conf.ivlen, GFP_KERNEL);
		if (unlikely(esp->conf.ivec == NULL))
			goto error;
		esp->conf.ivinitted = 0;
	}
	if (crypto_blkcipher_setkey(tfm, esp->conf.key, esp->conf.key_len))
		goto error;
	x->props.header_len = sizeof(struct ip_esp_hdr) + esp->conf.ivlen;
	if (x->props.mode == XFRM_MODE_TUNNEL)
		x->props.header_len += sizeof(struct iphdr);
	if (x->encap) {
		struct xfrm_encap_tmpl *encap = x->encap;

		switch (encap->encap_type) {
		default:
			goto error;
		case UDP_ENCAP_ESPINUDP:
			x->props.header_len += sizeof(struct udphdr);
			break;
		case UDP_ENCAP_ESPINUDP_NON_IKE:
			x->props.header_len += sizeof(struct udphdr) + 2 * sizeof(u32);
			break;
		}
	}
	x->data = esp;
	x->props.trailer_len = esp4_get_max_size(x, 0) - x->props.header_len;
	return 0;

error:
	x->data = esp;
	esp_destroy(x);
	x->data = NULL;
	return -EINVAL;
}
Esempio n. 3
0
static struct crypto_blkcipher *ceph_crypto_alloc_cipher(void)
{
	return crypto_alloc_blkcipher("cbc(aes)", 0, CRYPTO_ALG_ASYNC);
}
Esempio n. 4
0
static int tf_self_test_perform_blkcipher(
	const char *alg_name,
	const struct blkcipher_test_vector *tv,
	bool decrypt)
{
	struct blkcipher_desc desc = {0};
	struct scatterlist sg_in, sg_out;
	unsigned char *in = NULL;
	unsigned char *out = NULL;
	unsigned in_size, out_size;
	int error;

	desc.tfm = crypto_alloc_blkcipher(alg_name, 0, 0);
	if (IS_ERR_OR_NULL(desc.tfm)) {
		ERROR("crypto_alloc_blkcipher(%s) failed", alg_name);
		error = (desc.tfm == NULL ? -ENOMEM : (int)desc.tfm);
		goto abort;
	}
	INFO("%s alg_name=%s driver_name=%s key_size=%u block_size=%u",
	     decrypt ? "decrypt" : "encrypt", alg_name,
	     crypto_tfm_alg_driver_name(crypto_blkcipher_tfm(desc.tfm)),
	     tv->key_length * 8,
	     crypto_blkcipher_blocksize(desc.tfm));

	in_size = tv->length;
	in = kmalloc(in_size, GFP_KERNEL);
	if (IS_ERR_OR_NULL(in)) {
		ERROR("kmalloc(%u) failed: %d", in_size, (int)in);
		error = (in == NULL ? -ENOMEM : (int)in);
		goto abort;
	}
	memcpy(in, decrypt ? tv->ciphertext : tv->plaintext,
	       tv->length);

	out_size = tv->length + crypto_blkcipher_blocksize(desc.tfm);
	out = kmalloc(out_size, GFP_KERNEL);
	if (IS_ERR_OR_NULL(out)) {
		ERROR("kmalloc(%u) failed: %d", out_size, (int)out);
		error = (out == NULL ? -ENOMEM : (int)out);
		goto abort;
	}

	error = crypto_blkcipher_setkey(desc.tfm, tv->key, tv->key_length);
	if (error) {
		ERROR("crypto_alloc_setkey(%s) failed", alg_name);
		goto abort;
	}
	TF_TRACE_ARRAY(tv->key, tv->key_length);
	if (tv->iv != NULL) {
		unsigned iv_length = crypto_blkcipher_ivsize(desc.tfm);
		crypto_blkcipher_set_iv(desc.tfm, tv->iv, iv_length);
		TF_TRACE_ARRAY(tv->iv, iv_length);
	}

	sg_init_one(&sg_in, in, tv->length);
	sg_init_one(&sg_out, out, tv->length);
	TF_TRACE_ARRAY(in, tv->length);
	(decrypt ? crypto_blkcipher_decrypt : crypto_blkcipher_encrypt)
		(&desc, &sg_out, &sg_in, tv->length);
	if (error) {
		ERROR("crypto_blkcipher_%s(%s) failed",
		      decrypt ? "decrypt" : "encrypt", alg_name);
		goto abort;
	}
	TF_TRACE_ARRAY(out, tv->length);

	crypto_free_blkcipher(desc.tfm);

	if (memcmp((decrypt ? tv->plaintext : tv->ciphertext),
		   out, tv->length)) {
		ERROR("Wrong %s/%u %s result", alg_name, tv->key_length * 8,
		      decrypt ? "decryption" : "encryption");
		error = -EINVAL;
	} else {
		INFO("%s/%u: %s successful", alg_name, tv->key_length * 8,
		     decrypt ? "decryption" : "encryption");
		error = 0;
	}
	kfree(in);
	kfree(out);
	return error;

abort:
	if (!IS_ERR_OR_NULL(desc.tfm))
		crypto_free_blkcipher(desc.tfm);
	if (!IS_ERR_OR_NULL(out))
		kfree(out);
	if (!IS_ERR_OR_NULL(in))
		kfree(in);
	return error;
}
Esempio n. 5
0
/* 	
 *	Function definition for sys_xcrypt.
 *	This function encrypts/decrypts files using AES Block Cipher algorithm using CBC mode.
 */
asmlinkage int sys_xcrypt( const char * const infile, const char * const opfile, const char * const keybuf, const int keylen, const short int flags ) {
	const char algo[] = "cbc(aes)";
	char *ipBuf = NULL, *opBuf = NULL, *iv = NULL, *inFile = NULL, *opFile = NULL, *keyBuf = NULL;
	int errno = 0, ret = 0;
	int actReadLen = 0, actWriteLen = 0, padLen = 0, blkSiz = 0, ipFileLen = 0, opFileLen = 0, keyLen = 0;
	int delOpFile = 0, prmbLen = 0, idx = 0;
	unsigned int fileSize = 0, factor = 1;
	struct file *inFilePtr = NULL, *opFilePtr = NULL;
	struct crypto_blkcipher *tfm = NULL;
	struct blkcipher_desc desc;
	struct scatterlist sg[2];
	struct dentry *tmpDentry;
	struct inode *tmpInode = NULL;
	mm_segment_t oldfs;

	/* Check for NULL pointers or invalid values */
	if( ( NULL == infile ) || ( NULL == opfile ) || ( NULL == keybuf ) || ( ( _FLAG_ENCRYPT_ != flags ) && ( _FLAG_DECRYPT_ != flags ) ) ) {
		printk( KERN_ALERT "Invalid I/P" );
		errno = -EINVAL;
		goto OUT_OK;
	}

	/* Verify if all the pointers belong to the user's own address space */
	ret = access_ok( VERIFY_READ, infile, 0 );
	if( !ret ) {
		printk( KERN_ALERT "Invalid pointer to I/P file passed as argument" );
		errno = -EFAULT;
		goto OUT_OK;
	}

	ret = access_ok( VERIFY_READ, opfile, 0 );
	if( !ret ) {
		printk( KERN_ALERT "Invalid pointer to O/P file passed as argument" );
		errno = -EFAULT;
		goto OUT_OK;
	}

	ret = access_ok( VERIFY_READ, keybuf, 0 );
	if( !ret ) {
		printk( KERN_ALERT "Invalid pointer to Password passed as argument" );
		errno = -EFAULT;
		goto OUT_OK;
	}

	/* Find out the length of the i/p buffers */
	ipFileLen = strlen_user( infile );
	opFileLen = strlen_user( opfile );
	keyLen = strlen_user( keybuf );

	/* Allocate buffers to copy i/p arguments from user space to kernel space */
	inFile = kmalloc( ipFileLen, GFP_KERNEL );
	if( NULL == inFile ) {
		errno = -ENOMEM;
		goto OUT_OK;
	}
	else {
		ret = strncpy_from_user( inFile, infile, ipFileLen );
		if( ret < 0 ) {
			errno = ret;
			goto OUT_IP;
		}
	}
		
	opFile = kmalloc( opFileLen, GFP_KERNEL );
	if( NULL == opFile ) {
		errno = -ENOMEM;
		goto OUT_IP;
	}
	else {
		ret = strncpy_from_user( opFile, opfile, opFileLen );
		if( ret < 0 ) {
			errno = ret;
			goto OUT_IP;
		}
	}
		
	keyBuf = kmalloc( keyLen, GFP_KERNEL );
	if( NULL == keyBuf ) {
		errno = -ENOMEM;
		goto OUT_IP;
	}
	else {
		ret = strncpy_from_user( keyBuf, keybuf, keyLen );
		if( ret < 0 ) {
			errno = ret;
			goto OUT_IP;
		}
	}

	/* Open I/P file. It will report error in case of non-existing file and bad permissions but not bad owner */
	inFilePtr = filp_open( inFile, O_RDONLY, 0 );
	if ( !inFilePtr || IS_ERR( inFilePtr ) ) {
		errno = (int)PTR_ERR( inFilePtr );
		printk( KERN_ALERT "Error opening i/p file: %d\n", errno );
		inFilePtr = NULL;
		goto OUT_IP;
    	}

	/* Check if the file is a regular file or not */
	if( !S_ISREG( inFilePtr->f_path.dentry->d_inode->i_mode ) ) {
		printk( KERN_ALERT "Error as file is not a regular one" );
		errno = -EBADF;
		goto OUT_FILE;
	}

	/* Check if the I/p file and the process owner match */
	if( ( current->real_cred->uid != inFilePtr->f_path.dentry->d_inode->i_uid ) && ( current->real_cred->uid != 0 ) ) {
		printk( KERN_ALERT "Error as owner of file and process does not match" );
		errno = -EACCES;
		goto OUT_FILE;
	}

	/* Open O/P file with error handling */
	opFilePtr = filp_open( opFile, O_WRONLY | O_CREAT | O_EXCL, 0 );
	if ( !opFilePtr || IS_ERR( opFilePtr ) ) {
		errno = (int)PTR_ERR( opFilePtr );
		printk( KERN_ALERT "Error opening o/p file: %d\n", errno );
		opFilePtr = NULL;
		goto OUT_FILE;
	}

	/* 
	 * Check if the infile and opfile point to the same file
	 * If they reside on the different file partition and have same name then it should be allowed else not
	 */
	if( 	( inFilePtr->f_path.dentry->d_inode->i_sb == opFilePtr->f_path.dentry->d_inode->i_sb ) && 
		( inFilePtr->f_path.dentry->d_inode->i_ino ==  opFilePtr->f_path.dentry->d_inode->i_ino ) ) {
		printk( KERN_ALERT "I/p and O/p file cannot be same" );
		errno = -EINVAL;
		goto OUT_FILE;
	}

	/* Set the o/p file permission to i/p file */
	opFilePtr->f_path.dentry->d_inode->i_mode =  inFilePtr->f_path.dentry->d_inode->i_mode;
	
	/* Set the file position to the beginning of the file */
	inFilePtr->f_pos = 0;
	opFilePtr->f_pos = 0;

	/* Allocate buffer to read data into and to write data to. For performance reasons, set its size equal to PAGE_SIZE */
	ipBuf = kmalloc( PAGE_SIZE, GFP_KERNEL );
	if( NULL == ipBuf ) {
		errno = -ENOMEM;
		goto OUT_FILE;
	}
		
	memset( ipBuf, _NULL_CHAR_, PAGE_SIZE );

	opBuf = kmalloc( PAGE_SIZE, GFP_KERNEL );
	if( NULL == opBuf ) {
		errno = -ENOMEM;
		goto OUT_DATA_PAGE;
	}
		
	memset( opBuf, _NULL_CHAR_, PAGE_SIZE );

	/* Allocate tfm */
	tfm = crypto_alloc_blkcipher( algo, 0, CRYPTO_ALG_ASYNC );
	if ( NULL == tfm  ) {
		printk( KERN_ALERT "Failed to load transform for %s: %ld\n", algo, PTR_ERR( tfm ) );
		errno = -EINVAL;
		goto OUT_DATA_PAGE;
	}

	/* Initialize desc */
	desc.tfm = tfm;
	desc.flags = 0;

	ret = crypto_blkcipher_setkey( tfm, keybuf, keylen );
	if( ret ) {
		printk( "Setkey() failed. Flags=%x\n", crypto_blkcipher_get_flags( tfm ) );
		errno = -EINVAL;
		goto OUT_CIPHER;
	}

	/* Initialize sg structure */
	FILL_SG( &sg[0], ipBuf, PAGE_SIZE );
	FILL_SG( &sg[1], opBuf, PAGE_SIZE );

	/* Get the block size */
	blkSiz = ((tfm->base).__crt_alg)->cra_blocksize;

	/* Initialize IV */
	iv = kmalloc( blkSiz, GFP_KERNEL );
	if( NULL == iv ) {
		errno = -ENOMEM;
		goto OUT_CIPHER;
	}
		
	memset( iv, _NULL_CHAR_, blkSiz );
	crypto_blkcipher_set_iv( tfm, iv, crypto_blkcipher_ivsize( tfm ) );

	/* Store the key and file size in encrypted form in the preamble */
	switch( flags ) {
		case _FLAG_ENCRYPT_:
			memcpy( ipBuf, keybuf, keylen );
			prmbLen = keylen;
			fileSize = (unsigned int)inFilePtr->f_path.dentry->d_inode->i_size;

			while( fileSize ) {
				ipBuf[ prmbLen + idx ] = fileSize % 10;
				fileSize /= 10;
				++idx;
			}

			prmbLen += idx;

			#ifdef _DEBUG_
				printk( KERN_ALERT "idx=%d prmbLen=%d\n", idx, prmbLen );
			#endif

			memset( ipBuf + prmbLen, _ETX_, _UL_MAX_SIZE_ - idx );
			prmbLen += ( _UL_MAX_SIZE_ - idx );

			#ifdef _DEBUG_
				printk( KERN_ALERT "prmbLen=%d\n", prmbLen );
			#endif

			padLen = blkSiz - ( prmbLen % blkSiz );
			memset( ipBuf + prmbLen, _ETX_, padLen );
			prmbLen += padLen;
  
			#ifdef _DEBUG_
				printk( KERN_ALERT "padLen=%d prmbLen=%d\n", padLen, prmbLen );
			#endif

			ret = crypto_blkcipher_encrypt( &desc, &sg[1], &sg[0], prmbLen );
			if (ret) {
				printk( KERN_ALERT "Encryption failed. Flags=0x%x\n", tfm->base.crt_flags );
				delOpFile = 1;
				goto OUT_IV;
			}

			oldfs = get_fs();
			set_fs( KERNEL_DS );

			opFilePtr->f_op->write( opFilePtr, opBuf, prmbLen, &opFilePtr->f_pos );

			/* Reset the address space to user one */
			set_fs( oldfs );

			break;

		case _FLAG_DECRYPT_:
			/* Set the address space to kernel one */
			oldfs = get_fs();
			set_fs( KERNEL_DS );

			prmbLen = keylen + _UL_MAX_SIZE_;
			padLen = blkSiz - ( prmbLen % blkSiz );
			prmbLen += padLen;

			#ifdef _DEBUG_
				printk( KERN_ALERT "padLen=%d prmbLen=%d\n", padLen, prmbLen );
			#endif

			actReadLen = inFilePtr->f_op->read( inFilePtr, ipBuf, prmbLen, &inFilePtr->f_pos );
			if( actReadLen != prmbLen ) {
				printk( KERN_ALERT "Requested number of bytes for preamble are lesser" );
				delOpFile = 1;
				goto OUT_IV;
			}

			#ifdef _DEBUG_
				printk( KERN_ALERT "actReadLen=%d\n", actReadLen );
			#endif

			/* Reset the address space to user one */
			set_fs( oldfs );

			ret = crypto_blkcipher_decrypt( &desc, &sg[1], &sg[0], prmbLen );
			if (ret) {
				printk( KERN_ALERT "Decryption failed. Flags=0x%x\n", tfm->base.crt_flags );
				delOpFile = 1;
				goto OUT_IV;
			}

			ret = memcmp( keybuf, opBuf, keylen );
			if( ret ) {
				printk( "Wrong password entered." );
				errno = -EKEYREJECTED;
				goto OUT_IV;
			}

			idx = 0;
			fileSize = 0;

			while( opBuf[ keylen + idx ] != _ETX_ ) {
				fileSize += opBuf[ keylen + idx ] * factor;
				factor *= 10;
				++idx;
			}

			#ifdef _DEBUG_
				printk( KERN_ALERT "idx=%d fileSize=%u\n", idx, fileSize );
			#endif

			break;
	}

	/* Read file till the file pointer reaches to the EOF */
	while( inFilePtr->f_pos < inFilePtr->f_path.dentry->d_inode->i_size ) {
		/* Initialize it to NULL char */
		memset( ipBuf, _NULL_CHAR_, PAGE_SIZE );
		memset( opBuf, _NULL_CHAR_, PAGE_SIZE );

		/* Set the address space to kernel one */
		oldfs = get_fs();
		set_fs( KERNEL_DS );

		actReadLen = inFilePtr->f_op->read( inFilePtr, ipBuf, PAGE_SIZE, &inFilePtr->f_pos );

		/* Reset the address space to user one */
		set_fs( oldfs );

		/* As per the i/p flag, do encryption/decryption */
		switch( flags ) {
			case _FLAG_ENCRYPT_:
				/* For encryption ensure padding as per the block size */
				#ifdef _DEBUG_
					printk( KERN_ALERT "Bytes read from I/P file ::%d::\n", actReadLen );
				#endif

				if( actReadLen % blkSiz ) {
					padLen = blkSiz - ( actReadLen % blkSiz );
					memset( ipBuf + actReadLen, _ETX_, padLen );
					actReadLen += padLen;
				}

				#ifdef _DEBUG_
					printk( KERN_ALERT "Pad Length ::%d::\n", padLen );
					printk( KERN_ALERT "Data read from I/P file ::%s::\n", ipBuf );
				#endif

				/* Encrypt the data */
				ret = crypto_blkcipher_encrypt( &desc, &sg[1], &sg[0], PAGE_SIZE );
				if (ret) {
					printk( KERN_ALERT "Encryption failed. Flags=0x%x\n", tfm->base.crt_flags );
					delOpFile = 1;
					goto OUT_IV;
				}

				break;

			case _FLAG_DECRYPT_:
				/* Decrypt the data */
				ret = crypto_blkcipher_decrypt( &desc, &sg[1], &sg[0], PAGE_SIZE );
				if (ret) {
					printk( KERN_ALERT "Decryption failed. Flags=0x%x\n", tfm->base.crt_flags );
					delOpFile = 1;
					goto OUT_IV;
				}

				#ifdef _DEBUG_
					printk( KERN_ALERT "Bytes read from I/P file ::%d::\n", actReadLen );
				#endif

				while( _ETX_ == opBuf[ actReadLen - 1 ] ) {
					opBuf[ actReadLen - 1 ] = _NULL_CHAR_;
					--actReadLen;
				}

				#ifdef _DEBUG_
					printk( KERN_ALERT "Bytes read from I/P file ::%d::\n", actReadLen );
					printk( KERN_ALERT "Data read from I/P file ::%s::\n", opBuf );
				#endif


				break;
		}

		/*
		 * Start writing to the o/p file
		 * Set the address space to kernel one
		 */
		oldfs = get_fs();
		set_fs( KERNEL_DS );

		actWriteLen = opFilePtr->f_op->write( opFilePtr, opBuf, actReadLen, &opFilePtr->f_pos );

		/* Reset the address space to user one */
		set_fs( oldfs );

		#ifdef _DEBUG_
			printk( KERN_ALERT "Bytes written to O/P file ::%d::\n", actWriteLen );
		#endif
	}

	/* Free iv */
	OUT_IV:
		kfree( iv );
		iv = NULL;
		printk( KERN_ALERT "Memory for IV freed ..." );

	/* Free tfm */
	OUT_CIPHER:
		crypto_free_blkcipher( tfm );
		printk( KERN_ALERT "Encryption Transform freed ..." );

	/* Free i/p and o/p buffers */
	OUT_DATA_PAGE:
		if( ipBuf ) {
			kfree( ipBuf );
			ipBuf = NULL;
		}

		if( opBuf ) {
			kfree( opBuf );
			opBuf = NULL;
		}

		printk( KERN_ALERT "Memory for encrption/decryption freed ..." );

	/* Close any open files */
	OUT_FILE:
		if( inFilePtr ) {
			filp_close( inFilePtr, NULL );
			inFilePtr = NULL;
			printk( KERN_ALERT "I/p file closed ..." );
		}

		if( opFilePtr ) {
			filp_close( opFilePtr, NULL );
			opFilePtr = NULL;
			printk( KERN_ALERT "O/p file closed ..." );
		}

		if( delOpFile ) {
			opFilePtr = filp_open( opFile, O_WRONLY , 0 );
			if ( !opFilePtr || IS_ERR( opFilePtr ) ) {
				opFilePtr = NULL;
				goto OUT_IP;
			}

			tmpDentry = opFilePtr->f_path.dentry;
			tmpInode = tmpDentry->d_parent->d_inode;

			filp_close( opFilePtr, NULL );
			vfs_unlink( tmpInode, tmpDentry );
			printk( KERN_ALERT "O/p file deleted ..." );
		}

	OUT_IP:
		if( inFile ) {
			kfree( inFile );
			inFile = NULL;
		}

		if( opFile ) {
			kfree( opFile );
			opFile = NULL;
		}

		if( keyBuf ) {
			kfree( keyBuf );
			keyBuf = NULL;
		}

		printk( KERN_ALERT "Memory for I/P parameters freed ..." );

	/* Return final status */
	OUT_OK:
		printk( KERN_ALERT "Exiting function sys_xcrypt ..." );
		return errno;
}