162306a36Sopenharmony_ci/*
262306a36Sopenharmony_ci * ppp_mppe.c - interface MPPE to the PPP code.
362306a36Sopenharmony_ci * This version is for use with Linux kernel 2.6.14+
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * By Frank Cusack <fcusack@fcusack.com>.
662306a36Sopenharmony_ci * Copyright (c) 2002,2003,2004 Google, Inc.
762306a36Sopenharmony_ci * All rights reserved.
862306a36Sopenharmony_ci *
962306a36Sopenharmony_ci * License:
1062306a36Sopenharmony_ci * Permission to use, copy, modify, and distribute this software and its
1162306a36Sopenharmony_ci * documentation is hereby granted, provided that the above copyright
1262306a36Sopenharmony_ci * notice appears in all copies.  This software is provided without any
1362306a36Sopenharmony_ci * warranty, express or implied.
1462306a36Sopenharmony_ci *
1562306a36Sopenharmony_ci * ALTERNATIVELY, provided that this notice is retained in full, this product
1662306a36Sopenharmony_ci * may be distributed under the terms of the GNU General Public License (GPL),
1762306a36Sopenharmony_ci * in which case the provisions of the GPL apply INSTEAD OF those given above.
1862306a36Sopenharmony_ci *
1962306a36Sopenharmony_ci *   This program is free software; you can redistribute it and/or modify
2062306a36Sopenharmony_ci *   it under the terms of the GNU General Public License as published by
2162306a36Sopenharmony_ci *   the Free Software Foundation; either version 2 of the License, or
2262306a36Sopenharmony_ci *   (at your option) any later version.
2362306a36Sopenharmony_ci *
2462306a36Sopenharmony_ci *   This program is distributed in the hope that it will be useful,
2562306a36Sopenharmony_ci *   but WITHOUT ANY WARRANTY; without even the implied warranty of
2662306a36Sopenharmony_ci *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
2762306a36Sopenharmony_ci *   GNU General Public License for more details.
2862306a36Sopenharmony_ci *
2962306a36Sopenharmony_ci *   You should have received a copy of the GNU General Public License
3062306a36Sopenharmony_ci *   along with this program; if not, see <http://www.gnu.org/licenses/>.
3162306a36Sopenharmony_ci *
3262306a36Sopenharmony_ci *
3362306a36Sopenharmony_ci * Changelog:
3462306a36Sopenharmony_ci *      08/12/05 - Matt Domsch <Matt_Domsch@dell.com>
3562306a36Sopenharmony_ci *                 Only need extra skb padding on transmit, not receive.
3662306a36Sopenharmony_ci *      06/18/04 - Matt Domsch <Matt_Domsch@dell.com>, Oleg Makarenko <mole@quadra.ru>
3762306a36Sopenharmony_ci *                 Use Linux kernel 2.6 arc4 and sha1 routines rather than
3862306a36Sopenharmony_ci *                 providing our own.
3962306a36Sopenharmony_ci *      2/15/04 - TS: added #include <version.h> and testing for Kernel
4062306a36Sopenharmony_ci *                    version before using
4162306a36Sopenharmony_ci *                    MOD_DEC_USAGE_COUNT/MOD_INC_USAGE_COUNT which are
4262306a36Sopenharmony_ci *                    deprecated in 2.6
4362306a36Sopenharmony_ci */
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci#include <crypto/arc4.h>
4662306a36Sopenharmony_ci#include <crypto/hash.h>
4762306a36Sopenharmony_ci#include <linux/err.h>
4862306a36Sopenharmony_ci#include <linux/fips.h>
4962306a36Sopenharmony_ci#include <linux/module.h>
5062306a36Sopenharmony_ci#include <linux/kernel.h>
5162306a36Sopenharmony_ci#include <linux/init.h>
5262306a36Sopenharmony_ci#include <linux/types.h>
5362306a36Sopenharmony_ci#include <linux/slab.h>
5462306a36Sopenharmony_ci#include <linux/string.h>
5562306a36Sopenharmony_ci#include <linux/mm.h>
5662306a36Sopenharmony_ci#include <linux/ppp_defs.h>
5762306a36Sopenharmony_ci#include <linux/ppp-comp.h>
5862306a36Sopenharmony_ci#include <linux/scatterlist.h>
5962306a36Sopenharmony_ci#include <asm/unaligned.h>
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci#include "ppp_mppe.h"
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_ciMODULE_AUTHOR("Frank Cusack <fcusack@fcusack.com>");
6462306a36Sopenharmony_ciMODULE_DESCRIPTION("Point-to-Point Protocol Microsoft Point-to-Point Encryption support");
6562306a36Sopenharmony_ciMODULE_LICENSE("Dual BSD/GPL");
6662306a36Sopenharmony_ciMODULE_ALIAS("ppp-compress-" __stringify(CI_MPPE));
6762306a36Sopenharmony_ciMODULE_VERSION("1.0.2");
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ci#define SHA1_PAD_SIZE 40
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci/*
7262306a36Sopenharmony_ci * kernel crypto API needs its arguments to be in kmalloc'd memory, not in the module
7362306a36Sopenharmony_ci * static data area.  That means sha_pad needs to be kmalloc'd.
7462306a36Sopenharmony_ci */
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_cistruct sha_pad {
7762306a36Sopenharmony_ci	unsigned char sha_pad1[SHA1_PAD_SIZE];
7862306a36Sopenharmony_ci	unsigned char sha_pad2[SHA1_PAD_SIZE];
7962306a36Sopenharmony_ci};
8062306a36Sopenharmony_cistatic struct sha_pad *sha_pad;
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_cistatic inline void sha_pad_init(struct sha_pad *shapad)
8362306a36Sopenharmony_ci{
8462306a36Sopenharmony_ci	memset(shapad->sha_pad1, 0x00, sizeof(shapad->sha_pad1));
8562306a36Sopenharmony_ci	memset(shapad->sha_pad2, 0xF2, sizeof(shapad->sha_pad2));
8662306a36Sopenharmony_ci}
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ci/*
8962306a36Sopenharmony_ci * State for an MPPE (de)compressor.
9062306a36Sopenharmony_ci */
9162306a36Sopenharmony_cistruct ppp_mppe_state {
9262306a36Sopenharmony_ci	struct arc4_ctx arc4;
9362306a36Sopenharmony_ci	struct shash_desc *sha1;
9462306a36Sopenharmony_ci	unsigned char *sha1_digest;
9562306a36Sopenharmony_ci	unsigned char master_key[MPPE_MAX_KEY_LEN];
9662306a36Sopenharmony_ci	unsigned char session_key[MPPE_MAX_KEY_LEN];
9762306a36Sopenharmony_ci	unsigned keylen;	/* key length in bytes             */
9862306a36Sopenharmony_ci	/* NB: 128-bit == 16, 40-bit == 8! */
9962306a36Sopenharmony_ci	/* If we want to support 56-bit,   */
10062306a36Sopenharmony_ci	/* the unit has to change to bits  */
10162306a36Sopenharmony_ci	unsigned char bits;	/* MPPE control bits */
10262306a36Sopenharmony_ci	unsigned ccount;	/* 12-bit coherency count (seqno)  */
10362306a36Sopenharmony_ci	unsigned stateful;	/* stateful mode flag */
10462306a36Sopenharmony_ci	int discard;		/* stateful mode packet loss flag */
10562306a36Sopenharmony_ci	int sanity_errors;	/* take down LCP if too many */
10662306a36Sopenharmony_ci	int unit;
10762306a36Sopenharmony_ci	int debug;
10862306a36Sopenharmony_ci	struct compstat stats;
10962306a36Sopenharmony_ci};
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci/* struct ppp_mppe_state.bits definitions */
11262306a36Sopenharmony_ci#define MPPE_BIT_A	0x80	/* Encryption table were (re)inititalized */
11362306a36Sopenharmony_ci#define MPPE_BIT_B	0x40	/* MPPC only (not implemented) */
11462306a36Sopenharmony_ci#define MPPE_BIT_C	0x20	/* MPPC only (not implemented) */
11562306a36Sopenharmony_ci#define MPPE_BIT_D	0x10	/* This is an encrypted frame */
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci#define MPPE_BIT_FLUSHED	MPPE_BIT_A
11862306a36Sopenharmony_ci#define MPPE_BIT_ENCRYPTED	MPPE_BIT_D
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ci#define MPPE_BITS(p) ((p)[4] & 0xf0)
12162306a36Sopenharmony_ci#define MPPE_CCOUNT(p) ((((p)[4] & 0x0f) << 8) + (p)[5])
12262306a36Sopenharmony_ci#define MPPE_CCOUNT_SPACE 0x1000	/* The size of the ccount space */
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_ci#define MPPE_OVHD	2	/* MPPE overhead/packet */
12562306a36Sopenharmony_ci#define SANITY_MAX	1600	/* Max bogon factor we will tolerate */
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ci/*
12862306a36Sopenharmony_ci * Key Derivation, from RFC 3078, RFC 3079.
12962306a36Sopenharmony_ci * Equivalent to Get_Key() for MS-CHAP as described in RFC 3079.
13062306a36Sopenharmony_ci */
13162306a36Sopenharmony_cistatic void get_new_key_from_sha(struct ppp_mppe_state * state)
13262306a36Sopenharmony_ci{
13362306a36Sopenharmony_ci	crypto_shash_init(state->sha1);
13462306a36Sopenharmony_ci	crypto_shash_update(state->sha1, state->master_key,
13562306a36Sopenharmony_ci			    state->keylen);
13662306a36Sopenharmony_ci	crypto_shash_update(state->sha1, sha_pad->sha_pad1,
13762306a36Sopenharmony_ci			    sizeof(sha_pad->sha_pad1));
13862306a36Sopenharmony_ci	crypto_shash_update(state->sha1, state->session_key,
13962306a36Sopenharmony_ci			    state->keylen);
14062306a36Sopenharmony_ci	crypto_shash_update(state->sha1, sha_pad->sha_pad2,
14162306a36Sopenharmony_ci			    sizeof(sha_pad->sha_pad2));
14262306a36Sopenharmony_ci	crypto_shash_final(state->sha1, state->sha1_digest);
14362306a36Sopenharmony_ci}
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ci/*
14662306a36Sopenharmony_ci * Perform the MPPE rekey algorithm, from RFC 3078, sec. 7.3.
14762306a36Sopenharmony_ci * Well, not what's written there, but rather what they meant.
14862306a36Sopenharmony_ci */
14962306a36Sopenharmony_cistatic void mppe_rekey(struct ppp_mppe_state * state, int initial_key)
15062306a36Sopenharmony_ci{
15162306a36Sopenharmony_ci	get_new_key_from_sha(state);
15262306a36Sopenharmony_ci	if (!initial_key) {
15362306a36Sopenharmony_ci		arc4_setkey(&state->arc4, state->sha1_digest, state->keylen);
15462306a36Sopenharmony_ci		arc4_crypt(&state->arc4, state->session_key, state->sha1_digest,
15562306a36Sopenharmony_ci			   state->keylen);
15662306a36Sopenharmony_ci	} else {
15762306a36Sopenharmony_ci		memcpy(state->session_key, state->sha1_digest, state->keylen);
15862306a36Sopenharmony_ci	}
15962306a36Sopenharmony_ci	if (state->keylen == 8) {
16062306a36Sopenharmony_ci		/* See RFC 3078 */
16162306a36Sopenharmony_ci		state->session_key[0] = 0xd1;
16262306a36Sopenharmony_ci		state->session_key[1] = 0x26;
16362306a36Sopenharmony_ci		state->session_key[2] = 0x9e;
16462306a36Sopenharmony_ci	}
16562306a36Sopenharmony_ci	arc4_setkey(&state->arc4, state->session_key, state->keylen);
16662306a36Sopenharmony_ci}
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_ci/*
16962306a36Sopenharmony_ci * Allocate space for a (de)compressor.
17062306a36Sopenharmony_ci */
17162306a36Sopenharmony_cistatic void *mppe_alloc(unsigned char *options, int optlen)
17262306a36Sopenharmony_ci{
17362306a36Sopenharmony_ci	struct ppp_mppe_state *state;
17462306a36Sopenharmony_ci	struct crypto_shash *shash;
17562306a36Sopenharmony_ci	unsigned int digestsize;
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_ci	if (optlen != CILEN_MPPE + sizeof(state->master_key) ||
17862306a36Sopenharmony_ci	    options[0] != CI_MPPE || options[1] != CILEN_MPPE ||
17962306a36Sopenharmony_ci	    fips_enabled)
18062306a36Sopenharmony_ci		goto out;
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci	state = kzalloc(sizeof(*state), GFP_KERNEL);
18362306a36Sopenharmony_ci	if (state == NULL)
18462306a36Sopenharmony_ci		goto out;
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_ci	shash = crypto_alloc_shash("sha1", 0, 0);
18862306a36Sopenharmony_ci	if (IS_ERR(shash))
18962306a36Sopenharmony_ci		goto out_free;
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_ci	state->sha1 = kmalloc(sizeof(*state->sha1) +
19262306a36Sopenharmony_ci				     crypto_shash_descsize(shash),
19362306a36Sopenharmony_ci			      GFP_KERNEL);
19462306a36Sopenharmony_ci	if (!state->sha1) {
19562306a36Sopenharmony_ci		crypto_free_shash(shash);
19662306a36Sopenharmony_ci		goto out_free;
19762306a36Sopenharmony_ci	}
19862306a36Sopenharmony_ci	state->sha1->tfm = shash;
19962306a36Sopenharmony_ci
20062306a36Sopenharmony_ci	digestsize = crypto_shash_digestsize(shash);
20162306a36Sopenharmony_ci	if (digestsize < MPPE_MAX_KEY_LEN)
20262306a36Sopenharmony_ci		goto out_free;
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_ci	state->sha1_digest = kmalloc(digestsize, GFP_KERNEL);
20562306a36Sopenharmony_ci	if (!state->sha1_digest)
20662306a36Sopenharmony_ci		goto out_free;
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_ci	/* Save keys. */
20962306a36Sopenharmony_ci	memcpy(state->master_key, &options[CILEN_MPPE],
21062306a36Sopenharmony_ci	       sizeof(state->master_key));
21162306a36Sopenharmony_ci	memcpy(state->session_key, state->master_key,
21262306a36Sopenharmony_ci	       sizeof(state->master_key));
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_ci	/*
21562306a36Sopenharmony_ci	 * We defer initial key generation until mppe_init(), as mppe_alloc()
21662306a36Sopenharmony_ci	 * is called frequently during negotiation.
21762306a36Sopenharmony_ci	 */
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_ci	return (void *)state;
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_ciout_free:
22262306a36Sopenharmony_ci	kfree(state->sha1_digest);
22362306a36Sopenharmony_ci	if (state->sha1) {
22462306a36Sopenharmony_ci		crypto_free_shash(state->sha1->tfm);
22562306a36Sopenharmony_ci		kfree_sensitive(state->sha1);
22662306a36Sopenharmony_ci	}
22762306a36Sopenharmony_ci	kfree(state);
22862306a36Sopenharmony_ciout:
22962306a36Sopenharmony_ci	return NULL;
23062306a36Sopenharmony_ci}
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_ci/*
23362306a36Sopenharmony_ci * Deallocate space for a (de)compressor.
23462306a36Sopenharmony_ci */
23562306a36Sopenharmony_cistatic void mppe_free(void *arg)
23662306a36Sopenharmony_ci{
23762306a36Sopenharmony_ci	struct ppp_mppe_state *state = (struct ppp_mppe_state *) arg;
23862306a36Sopenharmony_ci	if (state) {
23962306a36Sopenharmony_ci		kfree(state->sha1_digest);
24062306a36Sopenharmony_ci		crypto_free_shash(state->sha1->tfm);
24162306a36Sopenharmony_ci		kfree_sensitive(state->sha1);
24262306a36Sopenharmony_ci		kfree_sensitive(state);
24362306a36Sopenharmony_ci	}
24462306a36Sopenharmony_ci}
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_ci/*
24762306a36Sopenharmony_ci * Initialize (de)compressor state.
24862306a36Sopenharmony_ci */
24962306a36Sopenharmony_cistatic int
25062306a36Sopenharmony_cimppe_init(void *arg, unsigned char *options, int optlen, int unit, int debug,
25162306a36Sopenharmony_ci	  const char *debugstr)
25262306a36Sopenharmony_ci{
25362306a36Sopenharmony_ci	struct ppp_mppe_state *state = (struct ppp_mppe_state *) arg;
25462306a36Sopenharmony_ci	unsigned char mppe_opts;
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_ci	if (optlen != CILEN_MPPE ||
25762306a36Sopenharmony_ci	    options[0] != CI_MPPE || options[1] != CILEN_MPPE)
25862306a36Sopenharmony_ci		return 0;
25962306a36Sopenharmony_ci
26062306a36Sopenharmony_ci	MPPE_CI_TO_OPTS(&options[2], mppe_opts);
26162306a36Sopenharmony_ci	if (mppe_opts & MPPE_OPT_128)
26262306a36Sopenharmony_ci		state->keylen = 16;
26362306a36Sopenharmony_ci	else if (mppe_opts & MPPE_OPT_40)
26462306a36Sopenharmony_ci		state->keylen = 8;
26562306a36Sopenharmony_ci	else {
26662306a36Sopenharmony_ci		printk(KERN_WARNING "%s[%d]: unknown key length\n", debugstr,
26762306a36Sopenharmony_ci		       unit);
26862306a36Sopenharmony_ci		return 0;
26962306a36Sopenharmony_ci	}
27062306a36Sopenharmony_ci	if (mppe_opts & MPPE_OPT_STATEFUL)
27162306a36Sopenharmony_ci		state->stateful = 1;
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_ci	/* Generate the initial session key. */
27462306a36Sopenharmony_ci	mppe_rekey(state, 1);
27562306a36Sopenharmony_ci
27662306a36Sopenharmony_ci	if (debug) {
27762306a36Sopenharmony_ci		printk(KERN_DEBUG "%s[%d]: initialized with %d-bit %s mode\n",
27862306a36Sopenharmony_ci		       debugstr, unit, (state->keylen == 16) ? 128 : 40,
27962306a36Sopenharmony_ci		       (state->stateful) ? "stateful" : "stateless");
28062306a36Sopenharmony_ci		printk(KERN_DEBUG
28162306a36Sopenharmony_ci		       "%s[%d]: keys: master: %*phN initial session: %*phN\n",
28262306a36Sopenharmony_ci		       debugstr, unit,
28362306a36Sopenharmony_ci		       (int)sizeof(state->master_key), state->master_key,
28462306a36Sopenharmony_ci		       (int)sizeof(state->session_key), state->session_key);
28562306a36Sopenharmony_ci	}
28662306a36Sopenharmony_ci
28762306a36Sopenharmony_ci	/*
28862306a36Sopenharmony_ci	 * Initialize the coherency count.  The initial value is not specified
28962306a36Sopenharmony_ci	 * in RFC 3078, but we can make a reasonable assumption that it will
29062306a36Sopenharmony_ci	 * start at 0.  Setting it to the max here makes the comp/decomp code
29162306a36Sopenharmony_ci	 * do the right thing (determined through experiment).
29262306a36Sopenharmony_ci	 */
29362306a36Sopenharmony_ci	state->ccount = MPPE_CCOUNT_SPACE - 1;
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_ci	/*
29662306a36Sopenharmony_ci	 * Note that even though we have initialized the key table, we don't
29762306a36Sopenharmony_ci	 * set the FLUSHED bit.  This is contrary to RFC 3078, sec. 3.1.
29862306a36Sopenharmony_ci	 */
29962306a36Sopenharmony_ci	state->bits = MPPE_BIT_ENCRYPTED;
30062306a36Sopenharmony_ci
30162306a36Sopenharmony_ci	state->unit = unit;
30262306a36Sopenharmony_ci	state->debug = debug;
30362306a36Sopenharmony_ci
30462306a36Sopenharmony_ci	return 1;
30562306a36Sopenharmony_ci}
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_cistatic int
30862306a36Sopenharmony_cimppe_comp_init(void *arg, unsigned char *options, int optlen, int unit,
30962306a36Sopenharmony_ci	       int hdrlen, int debug)
31062306a36Sopenharmony_ci{
31162306a36Sopenharmony_ci	/* ARGSUSED */
31262306a36Sopenharmony_ci	return mppe_init(arg, options, optlen, unit, debug, "mppe_comp_init");
31362306a36Sopenharmony_ci}
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_ci/*
31662306a36Sopenharmony_ci * We received a CCP Reset-Request (actually, we are sending a Reset-Ack),
31762306a36Sopenharmony_ci * tell the compressor to rekey.  Note that we MUST NOT rekey for
31862306a36Sopenharmony_ci * every CCP Reset-Request; we only rekey on the next xmit packet.
31962306a36Sopenharmony_ci * We might get multiple CCP Reset-Requests if our CCP Reset-Ack is lost.
32062306a36Sopenharmony_ci * So, rekeying for every CCP Reset-Request is broken as the peer will not
32162306a36Sopenharmony_ci * know how many times we've rekeyed.  (If we rekey and THEN get another
32262306a36Sopenharmony_ci * CCP Reset-Request, we must rekey again.)
32362306a36Sopenharmony_ci */
32462306a36Sopenharmony_cistatic void mppe_comp_reset(void *arg)
32562306a36Sopenharmony_ci{
32662306a36Sopenharmony_ci	struct ppp_mppe_state *state = (struct ppp_mppe_state *) arg;
32762306a36Sopenharmony_ci
32862306a36Sopenharmony_ci	state->bits |= MPPE_BIT_FLUSHED;
32962306a36Sopenharmony_ci}
33062306a36Sopenharmony_ci
33162306a36Sopenharmony_ci/*
33262306a36Sopenharmony_ci * Compress (encrypt) a packet.
33362306a36Sopenharmony_ci * It's strange to call this a compressor, since the output is always
33462306a36Sopenharmony_ci * MPPE_OVHD + 2 bytes larger than the input.
33562306a36Sopenharmony_ci */
33662306a36Sopenharmony_cistatic int
33762306a36Sopenharmony_cimppe_compress(void *arg, unsigned char *ibuf, unsigned char *obuf,
33862306a36Sopenharmony_ci	      int isize, int osize)
33962306a36Sopenharmony_ci{
34062306a36Sopenharmony_ci	struct ppp_mppe_state *state = (struct ppp_mppe_state *) arg;
34162306a36Sopenharmony_ci	int proto;
34262306a36Sopenharmony_ci
34362306a36Sopenharmony_ci	/*
34462306a36Sopenharmony_ci	 * Check that the protocol is in the range we handle.
34562306a36Sopenharmony_ci	 */
34662306a36Sopenharmony_ci	proto = PPP_PROTOCOL(ibuf);
34762306a36Sopenharmony_ci	if (proto < 0x0021 || proto > 0x00fa)
34862306a36Sopenharmony_ci		return 0;
34962306a36Sopenharmony_ci
35062306a36Sopenharmony_ci	/* Make sure we have enough room to generate an encrypted packet. */
35162306a36Sopenharmony_ci	if (osize < isize + MPPE_OVHD + 2) {
35262306a36Sopenharmony_ci		/* Drop the packet if we should encrypt it, but can't. */
35362306a36Sopenharmony_ci		printk(KERN_DEBUG "mppe_compress[%d]: osize too small! "
35462306a36Sopenharmony_ci		       "(have: %d need: %d)\n", state->unit,
35562306a36Sopenharmony_ci		       osize, osize + MPPE_OVHD + 2);
35662306a36Sopenharmony_ci		return -1;
35762306a36Sopenharmony_ci	}
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_ci	osize = isize + MPPE_OVHD + 2;
36062306a36Sopenharmony_ci
36162306a36Sopenharmony_ci	/*
36262306a36Sopenharmony_ci	 * Copy over the PPP header and set control bits.
36362306a36Sopenharmony_ci	 */
36462306a36Sopenharmony_ci	obuf[0] = PPP_ADDRESS(ibuf);
36562306a36Sopenharmony_ci	obuf[1] = PPP_CONTROL(ibuf);
36662306a36Sopenharmony_ci	put_unaligned_be16(PPP_COMP, obuf + 2);
36762306a36Sopenharmony_ci	obuf += PPP_HDRLEN;
36862306a36Sopenharmony_ci
36962306a36Sopenharmony_ci	state->ccount = (state->ccount + 1) % MPPE_CCOUNT_SPACE;
37062306a36Sopenharmony_ci	if (state->debug >= 7)
37162306a36Sopenharmony_ci		printk(KERN_DEBUG "mppe_compress[%d]: ccount %d\n", state->unit,
37262306a36Sopenharmony_ci		       state->ccount);
37362306a36Sopenharmony_ci	put_unaligned_be16(state->ccount, obuf);
37462306a36Sopenharmony_ci
37562306a36Sopenharmony_ci	if (!state->stateful ||	/* stateless mode     */
37662306a36Sopenharmony_ci	    ((state->ccount & 0xff) == 0xff) ||	/* "flag" packet      */
37762306a36Sopenharmony_ci	    (state->bits & MPPE_BIT_FLUSHED)) {	/* CCP Reset-Request  */
37862306a36Sopenharmony_ci		/* We must rekey */
37962306a36Sopenharmony_ci		if (state->debug && state->stateful)
38062306a36Sopenharmony_ci			printk(KERN_DEBUG "mppe_compress[%d]: rekeying\n",
38162306a36Sopenharmony_ci			       state->unit);
38262306a36Sopenharmony_ci		mppe_rekey(state, 0);
38362306a36Sopenharmony_ci		state->bits |= MPPE_BIT_FLUSHED;
38462306a36Sopenharmony_ci	}
38562306a36Sopenharmony_ci	obuf[0] |= state->bits;
38662306a36Sopenharmony_ci	state->bits &= ~MPPE_BIT_FLUSHED;	/* reset for next xmit */
38762306a36Sopenharmony_ci
38862306a36Sopenharmony_ci	obuf += MPPE_OVHD;
38962306a36Sopenharmony_ci	ibuf += 2;		/* skip to proto field */
39062306a36Sopenharmony_ci	isize -= 2;
39162306a36Sopenharmony_ci
39262306a36Sopenharmony_ci	arc4_crypt(&state->arc4, obuf, ibuf, isize);
39362306a36Sopenharmony_ci
39462306a36Sopenharmony_ci	state->stats.unc_bytes += isize;
39562306a36Sopenharmony_ci	state->stats.unc_packets++;
39662306a36Sopenharmony_ci	state->stats.comp_bytes += osize;
39762306a36Sopenharmony_ci	state->stats.comp_packets++;
39862306a36Sopenharmony_ci
39962306a36Sopenharmony_ci	return osize;
40062306a36Sopenharmony_ci}
40162306a36Sopenharmony_ci
40262306a36Sopenharmony_ci/*
40362306a36Sopenharmony_ci * Since every frame grows by MPPE_OVHD + 2 bytes, this is always going
40462306a36Sopenharmony_ci * to look bad ... and the longer the link is up the worse it will get.
40562306a36Sopenharmony_ci */
40662306a36Sopenharmony_cistatic void mppe_comp_stats(void *arg, struct compstat *stats)
40762306a36Sopenharmony_ci{
40862306a36Sopenharmony_ci	struct ppp_mppe_state *state = (struct ppp_mppe_state *) arg;
40962306a36Sopenharmony_ci
41062306a36Sopenharmony_ci	*stats = state->stats;
41162306a36Sopenharmony_ci}
41262306a36Sopenharmony_ci
41362306a36Sopenharmony_cistatic int
41462306a36Sopenharmony_cimppe_decomp_init(void *arg, unsigned char *options, int optlen, int unit,
41562306a36Sopenharmony_ci		 int hdrlen, int mru, int debug)
41662306a36Sopenharmony_ci{
41762306a36Sopenharmony_ci	/* ARGSUSED */
41862306a36Sopenharmony_ci	return mppe_init(arg, options, optlen, unit, debug, "mppe_decomp_init");
41962306a36Sopenharmony_ci}
42062306a36Sopenharmony_ci
42162306a36Sopenharmony_ci/*
42262306a36Sopenharmony_ci * We received a CCP Reset-Ack.  Just ignore it.
42362306a36Sopenharmony_ci */
42462306a36Sopenharmony_cistatic void mppe_decomp_reset(void *arg)
42562306a36Sopenharmony_ci{
42662306a36Sopenharmony_ci	/* ARGSUSED */
42762306a36Sopenharmony_ci	return;
42862306a36Sopenharmony_ci}
42962306a36Sopenharmony_ci
43062306a36Sopenharmony_ci/*
43162306a36Sopenharmony_ci * Decompress (decrypt) an MPPE packet.
43262306a36Sopenharmony_ci */
43362306a36Sopenharmony_cistatic int
43462306a36Sopenharmony_cimppe_decompress(void *arg, unsigned char *ibuf, int isize, unsigned char *obuf,
43562306a36Sopenharmony_ci		int osize)
43662306a36Sopenharmony_ci{
43762306a36Sopenharmony_ci	struct ppp_mppe_state *state = (struct ppp_mppe_state *) arg;
43862306a36Sopenharmony_ci	unsigned ccount;
43962306a36Sopenharmony_ci	int flushed = MPPE_BITS(ibuf) & MPPE_BIT_FLUSHED;
44062306a36Sopenharmony_ci
44162306a36Sopenharmony_ci	if (isize <= PPP_HDRLEN + MPPE_OVHD) {
44262306a36Sopenharmony_ci		if (state->debug)
44362306a36Sopenharmony_ci			printk(KERN_DEBUG
44462306a36Sopenharmony_ci			       "mppe_decompress[%d]: short pkt (%d)\n",
44562306a36Sopenharmony_ci			       state->unit, isize);
44662306a36Sopenharmony_ci		return DECOMP_ERROR;
44762306a36Sopenharmony_ci	}
44862306a36Sopenharmony_ci
44962306a36Sopenharmony_ci	/*
45062306a36Sopenharmony_ci	 * Make sure we have enough room to decrypt the packet.
45162306a36Sopenharmony_ci	 * Note that for our test we only subtract 1 byte whereas in
45262306a36Sopenharmony_ci	 * mppe_compress() we added 2 bytes (+MPPE_OVHD);
45362306a36Sopenharmony_ci	 * this is to account for possible PFC.
45462306a36Sopenharmony_ci	 */
45562306a36Sopenharmony_ci	if (osize < isize - MPPE_OVHD - 1) {
45662306a36Sopenharmony_ci		printk(KERN_DEBUG "mppe_decompress[%d]: osize too small! "
45762306a36Sopenharmony_ci		       "(have: %d need: %d)\n", state->unit,
45862306a36Sopenharmony_ci		       osize, isize - MPPE_OVHD - 1);
45962306a36Sopenharmony_ci		return DECOMP_ERROR;
46062306a36Sopenharmony_ci	}
46162306a36Sopenharmony_ci	osize = isize - MPPE_OVHD - 2;	/* assume no PFC */
46262306a36Sopenharmony_ci
46362306a36Sopenharmony_ci	ccount = MPPE_CCOUNT(ibuf);
46462306a36Sopenharmony_ci	if (state->debug >= 7)
46562306a36Sopenharmony_ci		printk(KERN_DEBUG "mppe_decompress[%d]: ccount %d\n",
46662306a36Sopenharmony_ci		       state->unit, ccount);
46762306a36Sopenharmony_ci
46862306a36Sopenharmony_ci	/* sanity checks -- terminate with extreme prejudice */
46962306a36Sopenharmony_ci	if (!(MPPE_BITS(ibuf) & MPPE_BIT_ENCRYPTED)) {
47062306a36Sopenharmony_ci		printk(KERN_DEBUG
47162306a36Sopenharmony_ci		       "mppe_decompress[%d]: ENCRYPTED bit not set!\n",
47262306a36Sopenharmony_ci		       state->unit);
47362306a36Sopenharmony_ci		state->sanity_errors += 100;
47462306a36Sopenharmony_ci		goto sanity_error;
47562306a36Sopenharmony_ci	}
47662306a36Sopenharmony_ci	if (!state->stateful && !flushed) {
47762306a36Sopenharmony_ci		printk(KERN_DEBUG "mppe_decompress[%d]: FLUSHED bit not set in "
47862306a36Sopenharmony_ci		       "stateless mode!\n", state->unit);
47962306a36Sopenharmony_ci		state->sanity_errors += 100;
48062306a36Sopenharmony_ci		goto sanity_error;
48162306a36Sopenharmony_ci	}
48262306a36Sopenharmony_ci	if (state->stateful && ((ccount & 0xff) == 0xff) && !flushed) {
48362306a36Sopenharmony_ci		printk(KERN_DEBUG "mppe_decompress[%d]: FLUSHED bit not set on "
48462306a36Sopenharmony_ci		       "flag packet!\n", state->unit);
48562306a36Sopenharmony_ci		state->sanity_errors += 100;
48662306a36Sopenharmony_ci		goto sanity_error;
48762306a36Sopenharmony_ci	}
48862306a36Sopenharmony_ci
48962306a36Sopenharmony_ci	/*
49062306a36Sopenharmony_ci	 * Check the coherency count.
49162306a36Sopenharmony_ci	 */
49262306a36Sopenharmony_ci
49362306a36Sopenharmony_ci	if (!state->stateful) {
49462306a36Sopenharmony_ci		/* Discard late packet */
49562306a36Sopenharmony_ci		if ((ccount - state->ccount) % MPPE_CCOUNT_SPACE
49662306a36Sopenharmony_ci						> MPPE_CCOUNT_SPACE / 2) {
49762306a36Sopenharmony_ci			state->sanity_errors++;
49862306a36Sopenharmony_ci			goto sanity_error;
49962306a36Sopenharmony_ci		}
50062306a36Sopenharmony_ci
50162306a36Sopenharmony_ci		/* RFC 3078, sec 8.1.  Rekey for every packet. */
50262306a36Sopenharmony_ci		while (state->ccount != ccount) {
50362306a36Sopenharmony_ci			mppe_rekey(state, 0);
50462306a36Sopenharmony_ci			state->ccount = (state->ccount + 1) % MPPE_CCOUNT_SPACE;
50562306a36Sopenharmony_ci		}
50662306a36Sopenharmony_ci	} else {
50762306a36Sopenharmony_ci		/* RFC 3078, sec 8.2. */
50862306a36Sopenharmony_ci		if (!state->discard) {
50962306a36Sopenharmony_ci			/* normal state */
51062306a36Sopenharmony_ci			state->ccount = (state->ccount + 1) % MPPE_CCOUNT_SPACE;
51162306a36Sopenharmony_ci			if (ccount != state->ccount) {
51262306a36Sopenharmony_ci				/*
51362306a36Sopenharmony_ci				 * (ccount > state->ccount)
51462306a36Sopenharmony_ci				 * Packet loss detected, enter the discard state.
51562306a36Sopenharmony_ci				 * Signal the peer to rekey (by sending a CCP Reset-Request).
51662306a36Sopenharmony_ci				 */
51762306a36Sopenharmony_ci				state->discard = 1;
51862306a36Sopenharmony_ci				return DECOMP_ERROR;
51962306a36Sopenharmony_ci			}
52062306a36Sopenharmony_ci		} else {
52162306a36Sopenharmony_ci			/* discard state */
52262306a36Sopenharmony_ci			if (!flushed) {
52362306a36Sopenharmony_ci				/* ccp.c will be silent (no additional CCP Reset-Requests). */
52462306a36Sopenharmony_ci				return DECOMP_ERROR;
52562306a36Sopenharmony_ci			} else {
52662306a36Sopenharmony_ci				/* Rekey for every missed "flag" packet. */
52762306a36Sopenharmony_ci				while ((ccount & ~0xff) !=
52862306a36Sopenharmony_ci				       (state->ccount & ~0xff)) {
52962306a36Sopenharmony_ci					mppe_rekey(state, 0);
53062306a36Sopenharmony_ci					state->ccount =
53162306a36Sopenharmony_ci					    (state->ccount +
53262306a36Sopenharmony_ci					     256) % MPPE_CCOUNT_SPACE;
53362306a36Sopenharmony_ci				}
53462306a36Sopenharmony_ci
53562306a36Sopenharmony_ci				/* reset */
53662306a36Sopenharmony_ci				state->discard = 0;
53762306a36Sopenharmony_ci				state->ccount = ccount;
53862306a36Sopenharmony_ci				/*
53962306a36Sopenharmony_ci				 * Another problem with RFC 3078 here.  It implies that the
54062306a36Sopenharmony_ci				 * peer need not send a Reset-Ack packet.  But RFC 1962
54162306a36Sopenharmony_ci				 * requires it.  Hopefully, M$ does send a Reset-Ack; even
54262306a36Sopenharmony_ci				 * though it isn't required for MPPE synchronization, it is
54362306a36Sopenharmony_ci				 * required to reset CCP state.
54462306a36Sopenharmony_ci				 */
54562306a36Sopenharmony_ci			}
54662306a36Sopenharmony_ci		}
54762306a36Sopenharmony_ci		if (flushed)
54862306a36Sopenharmony_ci			mppe_rekey(state, 0);
54962306a36Sopenharmony_ci	}
55062306a36Sopenharmony_ci
55162306a36Sopenharmony_ci	/*
55262306a36Sopenharmony_ci	 * Fill in the first part of the PPP header.  The protocol field
55362306a36Sopenharmony_ci	 * comes from the decrypted data.
55462306a36Sopenharmony_ci	 */
55562306a36Sopenharmony_ci	obuf[0] = PPP_ADDRESS(ibuf);	/* +1 */
55662306a36Sopenharmony_ci	obuf[1] = PPP_CONTROL(ibuf);	/* +1 */
55762306a36Sopenharmony_ci	obuf += 2;
55862306a36Sopenharmony_ci	ibuf += PPP_HDRLEN + MPPE_OVHD;
55962306a36Sopenharmony_ci	isize -= PPP_HDRLEN + MPPE_OVHD;	/* -6 */
56062306a36Sopenharmony_ci	/* net osize: isize-4 */
56162306a36Sopenharmony_ci
56262306a36Sopenharmony_ci	/*
56362306a36Sopenharmony_ci	 * Decrypt the first byte in order to check if it is
56462306a36Sopenharmony_ci	 * a compressed or uncompressed protocol field.
56562306a36Sopenharmony_ci	 */
56662306a36Sopenharmony_ci	arc4_crypt(&state->arc4, obuf, ibuf, 1);
56762306a36Sopenharmony_ci
56862306a36Sopenharmony_ci	/*
56962306a36Sopenharmony_ci	 * Do PFC decompression.
57062306a36Sopenharmony_ci	 * This would be nicer if we were given the actual sk_buff
57162306a36Sopenharmony_ci	 * instead of a char *.
57262306a36Sopenharmony_ci	 */
57362306a36Sopenharmony_ci	if ((obuf[0] & 0x01) != 0) {
57462306a36Sopenharmony_ci		obuf[1] = obuf[0];
57562306a36Sopenharmony_ci		obuf[0] = 0;
57662306a36Sopenharmony_ci		obuf++;
57762306a36Sopenharmony_ci		osize++;
57862306a36Sopenharmony_ci	}
57962306a36Sopenharmony_ci
58062306a36Sopenharmony_ci	/* And finally, decrypt the rest of the packet. */
58162306a36Sopenharmony_ci	arc4_crypt(&state->arc4, obuf + 1, ibuf + 1, isize - 1);
58262306a36Sopenharmony_ci
58362306a36Sopenharmony_ci	state->stats.unc_bytes += osize;
58462306a36Sopenharmony_ci	state->stats.unc_packets++;
58562306a36Sopenharmony_ci	state->stats.comp_bytes += isize;
58662306a36Sopenharmony_ci	state->stats.comp_packets++;
58762306a36Sopenharmony_ci
58862306a36Sopenharmony_ci	/* good packet credit */
58962306a36Sopenharmony_ci	state->sanity_errors >>= 1;
59062306a36Sopenharmony_ci
59162306a36Sopenharmony_ci	return osize;
59262306a36Sopenharmony_ci
59362306a36Sopenharmony_cisanity_error:
59462306a36Sopenharmony_ci	if (state->sanity_errors < SANITY_MAX)
59562306a36Sopenharmony_ci		return DECOMP_ERROR;
59662306a36Sopenharmony_ci	else
59762306a36Sopenharmony_ci		/* Take LCP down if the peer is sending too many bogons.
59862306a36Sopenharmony_ci		 * We don't want to do this for a single or just a few
59962306a36Sopenharmony_ci		 * instances since it could just be due to packet corruption.
60062306a36Sopenharmony_ci		 */
60162306a36Sopenharmony_ci		return DECOMP_FATALERROR;
60262306a36Sopenharmony_ci}
60362306a36Sopenharmony_ci
60462306a36Sopenharmony_ci/*
60562306a36Sopenharmony_ci * Incompressible data has arrived (this should never happen!).
60662306a36Sopenharmony_ci * We should probably drop the link if the protocol is in the range
60762306a36Sopenharmony_ci * of what should be encrypted.  At the least, we should drop this
60862306a36Sopenharmony_ci * packet.  (How to do this?)
60962306a36Sopenharmony_ci */
61062306a36Sopenharmony_cistatic void mppe_incomp(void *arg, unsigned char *ibuf, int icnt)
61162306a36Sopenharmony_ci{
61262306a36Sopenharmony_ci	struct ppp_mppe_state *state = (struct ppp_mppe_state *) arg;
61362306a36Sopenharmony_ci
61462306a36Sopenharmony_ci	if (state->debug &&
61562306a36Sopenharmony_ci	    (PPP_PROTOCOL(ibuf) >= 0x0021 && PPP_PROTOCOL(ibuf) <= 0x00fa))
61662306a36Sopenharmony_ci		printk(KERN_DEBUG
61762306a36Sopenharmony_ci		       "mppe_incomp[%d]: incompressible (unencrypted) data! "
61862306a36Sopenharmony_ci		       "(proto %04x)\n", state->unit, PPP_PROTOCOL(ibuf));
61962306a36Sopenharmony_ci
62062306a36Sopenharmony_ci	state->stats.inc_bytes += icnt;
62162306a36Sopenharmony_ci	state->stats.inc_packets++;
62262306a36Sopenharmony_ci	state->stats.unc_bytes += icnt;
62362306a36Sopenharmony_ci	state->stats.unc_packets++;
62462306a36Sopenharmony_ci}
62562306a36Sopenharmony_ci
62662306a36Sopenharmony_ci/*************************************************************
62762306a36Sopenharmony_ci * Module interface table
62862306a36Sopenharmony_ci *************************************************************/
62962306a36Sopenharmony_ci
63062306a36Sopenharmony_ci/*
63162306a36Sopenharmony_ci * Procedures exported to if_ppp.c.
63262306a36Sopenharmony_ci */
63362306a36Sopenharmony_cistatic struct compressor ppp_mppe = {
63462306a36Sopenharmony_ci	.compress_proto = CI_MPPE,
63562306a36Sopenharmony_ci	.comp_alloc     = mppe_alloc,
63662306a36Sopenharmony_ci	.comp_free      = mppe_free,
63762306a36Sopenharmony_ci	.comp_init      = mppe_comp_init,
63862306a36Sopenharmony_ci	.comp_reset     = mppe_comp_reset,
63962306a36Sopenharmony_ci	.compress       = mppe_compress,
64062306a36Sopenharmony_ci	.comp_stat      = mppe_comp_stats,
64162306a36Sopenharmony_ci	.decomp_alloc   = mppe_alloc,
64262306a36Sopenharmony_ci	.decomp_free    = mppe_free,
64362306a36Sopenharmony_ci	.decomp_init    = mppe_decomp_init,
64462306a36Sopenharmony_ci	.decomp_reset   = mppe_decomp_reset,
64562306a36Sopenharmony_ci	.decompress     = mppe_decompress,
64662306a36Sopenharmony_ci	.incomp         = mppe_incomp,
64762306a36Sopenharmony_ci	.decomp_stat    = mppe_comp_stats,
64862306a36Sopenharmony_ci	.owner          = THIS_MODULE,
64962306a36Sopenharmony_ci	.comp_extra     = MPPE_PAD,
65062306a36Sopenharmony_ci};
65162306a36Sopenharmony_ci
65262306a36Sopenharmony_ci/*
65362306a36Sopenharmony_ci * ppp_mppe_init()
65462306a36Sopenharmony_ci *
65562306a36Sopenharmony_ci * Prior to allowing load, try to load the arc4 and sha1 crypto
65662306a36Sopenharmony_ci * libraries.  The actual use will be allocated later, but
65762306a36Sopenharmony_ci * this way the module will fail to insmod if they aren't available.
65862306a36Sopenharmony_ci */
65962306a36Sopenharmony_ci
66062306a36Sopenharmony_cistatic int __init ppp_mppe_init(void)
66162306a36Sopenharmony_ci{
66262306a36Sopenharmony_ci	int answer;
66362306a36Sopenharmony_ci	if (fips_enabled || !crypto_has_ahash("sha1", 0, CRYPTO_ALG_ASYNC))
66462306a36Sopenharmony_ci		return -ENODEV;
66562306a36Sopenharmony_ci
66662306a36Sopenharmony_ci	sha_pad = kmalloc(sizeof(struct sha_pad), GFP_KERNEL);
66762306a36Sopenharmony_ci	if (!sha_pad)
66862306a36Sopenharmony_ci		return -ENOMEM;
66962306a36Sopenharmony_ci	sha_pad_init(sha_pad);
67062306a36Sopenharmony_ci
67162306a36Sopenharmony_ci	answer = ppp_register_compressor(&ppp_mppe);
67262306a36Sopenharmony_ci
67362306a36Sopenharmony_ci	if (answer == 0)
67462306a36Sopenharmony_ci		printk(KERN_INFO "PPP MPPE Compression module registered\n");
67562306a36Sopenharmony_ci	else
67662306a36Sopenharmony_ci		kfree(sha_pad);
67762306a36Sopenharmony_ci
67862306a36Sopenharmony_ci	return answer;
67962306a36Sopenharmony_ci}
68062306a36Sopenharmony_ci
68162306a36Sopenharmony_cistatic void __exit ppp_mppe_cleanup(void)
68262306a36Sopenharmony_ci{
68362306a36Sopenharmony_ci	ppp_unregister_compressor(&ppp_mppe);
68462306a36Sopenharmony_ci	kfree(sha_pad);
68562306a36Sopenharmony_ci}
68662306a36Sopenharmony_ci
68762306a36Sopenharmony_cimodule_init(ppp_mppe_init);
68862306a36Sopenharmony_cimodule_exit(ppp_mppe_cleanup);
689