freebsd-dev/usr.sbin/ppp/mppe.c
Brian Somers 662a42f752 When we miss one or more packets in stateful mode *and* need to
perform a key change, *and* our sequence numbers have wrapped,
ensure that the number of key changes is calculated correctly.

The previous code counted down from a negative number to zero,
re-encrypting the current key on each iteration - this took some
time and strangely enough got the answer wrong !!!

Fix a(nother) spelling mistake while I'm there.
2001-07-07 03:06:20 +00:00

736 lines
18 KiB
C

/*-
* Copyright (c) 2000 Semen Ustimenko <semenu@FreeBSD.org>
* All rights reserved.
*
* 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 AUTHOR 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 AUTHOR 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.
*
* $FreeBSD$
*/
#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <termios.h>
#ifdef __FreeBSD__
#include <sha.h>
#else
#include <openssl/sha.h>
#endif
#include <openssl/rc4.h>
#include "defs.h"
#include "mbuf.h"
#include "log.h"
#include "timer.h"
#include "fsm.h"
#include "lqr.h"
#include "hdlc.h"
#include "lcp.h"
#include "ccp.h"
#include "throughput.h"
#include "layer.h"
#include "link.h"
#include "chap_ms.h"
#include "proto.h"
#include "mppe.h"
/*
* Documentation:
*
* draft-ietf-pppext-mppe-04.txt
* draft-ietf-pppext-mppe-keys-02.txt
*/
#define MPPE_OPT_STATELESS 0x1000000
#define MPPE_OPT_COMPRESSED 0x01
#define MPPE_OPT_40BIT 0x20
#define MPPE_OPT_56BIT 0x80
#define MPPE_OPT_128BIT 0x40
#define MPPE_OPT_BITMASK 0xe0
#define MPPE_OPT_MASK (MPPE_OPT_STATELESS | MPPE_OPT_BITMASK)
#define MPPE_FLUSHED 0x8000
#define MPPE_ENCRYPTED 0x1000
#define MPPE_HEADER_BITMASK 0xf000
#define MPPE_HEADER_FLAG 0x00ff
#define MPPE_HEADER_FLAGMASK 0x00ff
#define MPPE_HEADER_FLAGSHIFT 8
#define MPPE_HEADER_STATEFUL_KEYCHANGES 16
struct mppe_state {
unsigned stateless : 1;
unsigned flushnext : 1;
unsigned flushrequired : 1;
int cohnum;
int keylen; /* 8 or 16 bytes */
int keybits; /* 40, 56 or 128 bits */
char sesskey[MPPE_KEY_LEN];
char mastkey[MPPE_KEY_LEN];
RC4_KEY rc4key;
};
int MPPE_MasterKeyValid = 0;
int MPPE_IsServer = 0;
char MPPE_MasterKey[MPPE_KEY_LEN];
/*
* The peer has missed a packet. Mark the next output frame to be FLUSHED
*/
static int
MPPEResetOutput(void *v)
{
struct mppe_state *mop = (struct mppe_state *)v;
if (mop->stateless)
log_Printf(LogCCP, "MPPE: Unexpected output channel reset\n");
else {
log_Printf(LogCCP, "MPPE: Output channel reset\n");
mop->flushnext = 1;
}
return 0; /* Ask FSM not to ACK */
}
static void
MPPEReduceSessionKey(struct mppe_state *mp)
{
switch(mp->keybits) {
case 40:
mp->sesskey[2] = 0x9e;
mp->sesskey[1] = 0x26;
case 56:
mp->sesskey[0] = 0xd1;
case 128:
}
}
static void
MPPEKeyChange(struct mppe_state *mp)
{
char InterimKey[MPPE_KEY_LEN];
RC4_KEY RC4Key;
GetNewKeyFromSHA(mp->mastkey, mp->sesskey, mp->keylen, InterimKey);
RC4_set_key(&RC4Key, mp->keylen, InterimKey);
RC4(&RC4Key, mp->keylen, InterimKey, mp->sesskey);
MPPEReduceSessionKey(mp);
}
static struct mbuf *
MPPEOutput(void *v, struct ccp *ccp, struct link *l, int pri, u_short *proto,
struct mbuf *mp)
{
struct mppe_state *mop = (struct mppe_state *)v;
struct mbuf *mo;
u_short nproto, prefix;
int dictinit, ilen, len;
char *rp;
ilen = m_length(mp);
dictinit = 0;
log_Printf(LogDEBUG, "MPPE: Output: Proto %02x (%d bytes)\n", *proto, ilen);
if (*proto < 0x21 && *proto > 0xFA) {
log_Printf(LogDEBUG, "MPPE: Output: Not encrypting\n");
ccp->compout += ilen;
ccp->uncompout += ilen;
return mp;
}
log_DumpBp(LogDEBUG, "MPPE: Output: Encrypt packet:", mp);
/* Get mbuf for prefixes */
mo = m_get(4, MB_CCPOUT);
mo->m_next = mp;
rp = MBUF_CTOP(mo);
prefix = MPPE_ENCRYPTED | mop->cohnum;
if (mop->stateless ||
(mop->cohnum & MPPE_HEADER_FLAGMASK) == MPPE_HEADER_FLAG) {
/* Change our key */
log_Printf(LogDEBUG, "MPPEOutput: Key changed [%d]\n", mop->cohnum);
MPPEKeyChange(mop);
dictinit = 1;
}
if (mop->stateless || mop->flushnext) {
prefix |= MPPE_FLUSHED;
dictinit = 1;
mop->flushnext = 0;
}
if (dictinit) {
/* Initialise our dictionary */
log_Printf(LogDEBUG, "MPPEOutput: Dictionary initialised [%d]\n",
mop->cohnum);
RC4_set_key(&mop->rc4key, mop->keylen, mop->sesskey);
}
/* Set MPPE packet prefix */
*(u_short *)rp = htons(prefix);
/* Save encrypted protocol number */
nproto = htons(*proto);
RC4(&mop->rc4key, 2, (char *)&nproto, rp + 2);
/* Encrypt main packet */
rp = MBUF_CTOP(mp);
RC4(&mop->rc4key, ilen, rp, rp);
mop->cohnum++;
mop->cohnum &= ~MPPE_HEADER_BITMASK;
/* Set the protocol number */
*proto = ccp_Proto(ccp);
len = m_length(mo);
ccp->uncompout += ilen;
ccp->compout += len;
log_Printf(LogDEBUG, "MPPE: Output: Encrypted: Proto %02x (%d bytes)\n",
*proto, len);
return mo;
}
static void
MPPEResetInput(void *v)
{
log_Printf(LogCCP, "MPPE: Unexpected input channel ack\n");
}
static struct mbuf *
MPPEInput(void *v, struct ccp *ccp, u_short *proto, struct mbuf *mp)
{
struct mppe_state *mip = (struct mppe_state *)v;
u_short prefix;
char *rp;
int dictinit, flushed, ilen, len, n;
ilen = m_length(mp);
dictinit = 0;
ccp->compin += ilen;
log_Printf(LogDEBUG, "MPPE: Input: Proto %02x (%d bytes)\n", *proto, ilen);
log_DumpBp(LogDEBUG, "MPPE: Input: Packet:", mp);
mp = mbuf_Read(mp, &prefix, 2);
prefix = ntohs(prefix);
flushed = prefix & MPPE_FLUSHED;
prefix &= ~flushed;
if ((prefix & MPPE_HEADER_BITMASK) != MPPE_ENCRYPTED) {
log_Printf(LogERROR, "MPPE: Input: Invalid packet (flags = 0x%x)\n",
(prefix & MPPE_HEADER_BITMASK) | flushed);
m_freem(mp);
return NULL;
}
prefix &= ~MPPE_HEADER_BITMASK;
if (!flushed && mip->stateless) {
log_Printf(LogCCP, "MPPEInput: Packet without MPPE_FLUSHED set"
" in stateless mode\n");
flushed = MPPE_FLUSHED;
/* Should we really continue ? */
}
if (mip->stateless) {
/* Change our key for each missed packet in stateless mode */
while (prefix != mip->cohnum) {
log_Printf(LogDEBUG, "MPPEInput: Key changed [%u]\n", prefix);
MPPEKeyChange(mip);
/*
* mip->cohnum contains what we received last time in stateless
* mode.
*/
mip->cohnum++;
mip->cohnum &= ~MPPE_HEADER_BITMASK;
}
dictinit = 1;
} else {
if (flushed) {
/*
* We can always process a flushed packet.
* Catch up on any outstanding key changes.
*/
n = (prefix >> MPPE_HEADER_FLAGSHIFT) -
(mip->cohnum >> MPPE_HEADER_FLAGSHIFT);
if (n < 0)
n += MPPE_HEADER_STATEFUL_KEYCHANGES;
while (n--) {
log_Printf(LogDEBUG, "MPPEInput: Key changed during catchup [%u]\n",
prefix);
MPPEKeyChange(mip);
}
mip->flushrequired = 0;
mip->cohnum = prefix;
dictinit = 1;
}
if (mip->flushrequired) {
/*
* Perhaps we should be lenient if
* (prefix & MPPE_HEADER_FLAGMASK) == MPPE_HEADER_FLAG
* The spec says that we shouldn't be though....
*/
log_Printf(LogDEBUG, "MPPE: Not flushed - discarded\n");
m_freem(mp);
return NULL;
}
if (prefix != mip->cohnum) {
/*
* We're in stateful mode and didn't receive the expected
* packet. Send a reset request, but don't tell the CCP layer
* about it as we don't expect to receive a Reset ACK !
* Guess what... M$ invented this !
*/
log_Printf(LogCCP, "MPPE: Input: Got seq %u, not %u\n",
prefix, mip->cohnum);
fsm_Output(&ccp->fsm, CODE_RESETREQ, ccp->fsm.reqid++, NULL, 0,
MB_CCPOUT);
mip->flushrequired = 1;
m_freem(mp);
return NULL;
}
if ((prefix & MPPE_HEADER_FLAGMASK) == MPPE_HEADER_FLAG) {
log_Printf(LogDEBUG, "MPPEInput: Key changed [%u]\n", prefix);
MPPEKeyChange(mip);
dictinit = 1;
} else if (flushed)
dictinit = 1;
/*
* mip->cohnum contains what we expect to receive next time in stateful
* mode.
*/
mip->cohnum++;
mip->cohnum &= ~MPPE_HEADER_BITMASK;
}
if (dictinit) {
log_Printf(LogDEBUG, "MPPEInput: Dictionary initialised [%u]\n", prefix);
RC4_set_key(&mip->rc4key, mip->keylen, mip->sesskey);
}
mp = mbuf_Read(mp, proto, 2);
RC4(&mip->rc4key, 2, (char *)proto, (char *)proto);
*proto = ntohs(*proto);
rp = MBUF_CTOP(mp);
len = m_length(mp);
RC4(&mip->rc4key, len, rp, rp);
log_Printf(LogDEBUG, "MPPEInput: Decrypted: Proto %02x (%d bytes)\n",
*proto, len);
log_DumpBp(LogDEBUG, "MPPEInput: Decrypted: Packet:", mp);
ccp->uncompin += len;
return mp;
}
static void
MPPEDictSetup(void *v, struct ccp *ccp, u_short proto, struct mbuf *mi)
{
}
static const char *
MPPEDispOpts(struct lcp_opt *o)
{
static char buf[70];
u_int32_t val = ntohl(*(u_int32_t *)o->data);
char ch;
int len;
snprintf(buf, sizeof buf, "value 0x%08x ", (unsigned)val);
len = strlen(buf);
if (!(val & MPPE_OPT_BITMASK)) {
snprintf(buf + len, sizeof buf - len, "(0");
len++;
} else {
ch = '(';
if (val & MPPE_OPT_128BIT) {
snprintf(buf + len, sizeof buf - len, "%c128", ch);
len += strlen(buf + len);
ch = '/';
}
if (val & MPPE_OPT_56BIT) {
snprintf(buf + len, sizeof buf - len, "%c56", ch);
len += strlen(buf + len);
ch = '/';
}
if (val & MPPE_OPT_40BIT) {
snprintf(buf + len, sizeof buf - len, "%c40", ch);
len += strlen(buf + len);
ch = '/';
}
}
snprintf(buf + len, sizeof buf - len, " bits, state%s",
(val & MPPE_OPT_STATELESS) ? "less" : "ful");
len += strlen(buf + len);
if (val & MPPE_OPT_COMPRESSED) {
snprintf(buf + len, sizeof buf - len, ", compressed");
len += strlen(buf + len);
}
snprintf(buf + len, sizeof buf - len, ")");
return buf;
}
static int
MPPEUsable(struct fsm *fp)
{
struct lcp *lcp;
int ok;
lcp = &fp->link->lcp;
ok = (lcp->want_auth == PROTO_CHAP && lcp->want_authtype == 0x81) ||
(lcp->his_auth == PROTO_CHAP && lcp->his_authtype == 0x81);
if (!ok)
log_Printf(LogCCP, "MPPE: Not usable without CHAP81\n");
return ok;
}
static int
MPPERequired(struct fsm *fp)
{
return fp->link->ccp.cfg.mppe.required;
}
static u_int32_t
MPPE_ConfigVal(const struct ccp_config *cfg)
{
u_int32_t val;
val = cfg->mppe.state == MPPE_STATELESS ? MPPE_OPT_STATELESS : 0;
switch(cfg->mppe.keybits) {
case 128:
val |= MPPE_OPT_128BIT;
break;
case 56:
val |= MPPE_OPT_56BIT;
break;
case 40:
val |= MPPE_OPT_40BIT;
break;
case 0:
val |= MPPE_OPT_128BIT | MPPE_OPT_56BIT | MPPE_OPT_40BIT;
break;
}
return val;
}
/*
* What options should we use for our first configure request
*/
static void
MPPEInitOptsOutput(struct lcp_opt *o, const struct ccp_config *cfg)
{
u_int32_t *p = (u_int32_t *)o->data;
o->len = 6;
if (!MPPE_MasterKeyValid) {
log_Printf(LogCCP, "MPPE: MasterKey is invalid,"
" MPPE is available only with CHAP81 authentication\n");
*p = htonl(0x0);
return;
}
*p = htonl(MPPE_ConfigVal(cfg));
}
/*
* Our CCP request was NAK'd with the given options
*/
static int
MPPESetOptsOutput(struct lcp_opt *o, const struct ccp_config *cfg)
{
u_int32_t *p = (u_int32_t *)o->data;
u_int32_t peer = ntohl(*p);
u_int32_t mval;
if (!MPPE_MasterKeyValid)
/* Treat their NAK as a REJ */
return MODE_NAK;
mval = MPPE_ConfigVal(cfg);
/*
* If we haven't been configured with a specific number of keybits, allow
* whatever the peer asks for.
*/
if (!cfg->mppe.keybits) {
mval &= ~MPPE_OPT_BITMASK;
mval |= (peer & MPPE_OPT_BITMASK);
if (!(mval & MPPE_OPT_BITMASK))
mval |= MPPE_OPT_128BIT;
}
/* Adjust our statelessness */
if (cfg->mppe.state == MPPE_ANYSTATE) {
mval &= ~MPPE_OPT_STATELESS;
mval |= (peer & MPPE_OPT_STATELESS);
}
*p = htonl(mval);
return MODE_ACK;
}
/*
* The peer has requested the given options
*/
static int
MPPESetOptsInput(struct lcp_opt *o, const struct ccp_config *cfg)
{
u_int32_t *p = (u_int32_t *)(o->data);
u_int32_t peer = ntohl(*p);
u_int32_t mval;
int res = MODE_ACK;
if (!MPPE_MasterKeyValid) {
if (*p != 0x0) {
*p = 0x0;
return MODE_NAK;
} else
return MODE_ACK;
}
mval = MPPE_ConfigVal(cfg);
if (peer & ~MPPE_OPT_MASK)
/* He's asking for bits we don't know about */
res = MODE_NAK;
if (peer & MPPE_OPT_STATELESS) {
if (cfg->mppe.state == MPPE_STATEFUL)
/* Peer can't have stateless */
res = MODE_NAK;
else
/* Peer wants stateless, that's ok */
mval |= MPPE_OPT_STATELESS;
} else {
if (cfg->mppe.state == MPPE_STATELESS)
/* Peer must have stateless */
res = MODE_NAK;
else
/* Peer doesn't want stateless, that's ok */
mval &= ~MPPE_OPT_STATELESS;
}
/* If we've got a configured number of keybits - the peer must use that */
if (cfg->mppe.keybits) {
*p = htonl(mval);
return peer == mval ? res : MODE_NAK;
}
/* If a specific number of bits hasn't been requested, we'll need to NAK */
switch (peer & MPPE_OPT_BITMASK) {
case MPPE_OPT_128BIT:
case MPPE_OPT_56BIT:
case MPPE_OPT_40BIT:
break;
default:
res = MODE_NAK;
}
/* Suggest the best number of bits */
mval &= ~MPPE_OPT_BITMASK;
if (peer & MPPE_OPT_128BIT)
mval |= MPPE_OPT_128BIT;
else if (peer & MPPE_OPT_56BIT)
mval |= MPPE_OPT_56BIT;
else if (peer & MPPE_OPT_40BIT)
mval |= MPPE_OPT_40BIT;
else
mval |= MPPE_OPT_128BIT;
*p = htonl(mval);
return res;
}
static struct mppe_state *
MPPE_InitState(struct lcp_opt *o)
{
struct mppe_state *mp;
u_int32_t val;
if ((mp = calloc(1, sizeof *mp)) != NULL) {
val = ntohl(*(u_int32_t *)o->data);
switch (val & MPPE_OPT_BITMASK) {
case MPPE_OPT_128BIT:
mp->keylen = 16;
mp->keybits = 128;
break;
case MPPE_OPT_56BIT:
mp->keylen = 8;
mp->keybits = 56;
break;
case MPPE_OPT_40BIT:
mp->keylen = 8;
mp->keybits = 40;
break;
default:
log_Printf(LogWARN, "Unexpected MPPE options 0x%08x\n", val);
free(mp);
return NULL;
}
mp->stateless = !!(val & MPPE_OPT_STATELESS);
}
return mp;
}
static void *
MPPEInitInput(struct lcp_opt *o)
{
struct mppe_state *mip;
if (!MPPE_MasterKeyValid) {
log_Printf(LogWARN, "MPPE: Cannot initialise without CHAP81\n");
return NULL;
}
if ((mip = MPPE_InitState(o)) == NULL) {
log_Printf(LogWARN, "MPPEInput: Cannot initialise - unexpected options\n");
return NULL;
}
log_Printf(LogDEBUG, "MPPE: InitInput: %d-bits\n", mip->keybits);
GetAsymetricStartKey(MPPE_MasterKey, mip->mastkey, mip->keylen, 0,
MPPE_IsServer);
GetNewKeyFromSHA(mip->mastkey, mip->mastkey, mip->keylen, mip->sesskey);
MPPEReduceSessionKey(mip);
log_Printf(LogCCP, "MPPE: Input channel initiated\n");
if (!mip->stateless) {
/*
* We need to initialise our dictionary here as the first packet we
* receive is unlikely to have the FLUSHED bit set.
*/
log_Printf(LogDEBUG, "MPPEInitInput: Dictionary initialised [%d]\n",
mip->cohnum);
RC4_set_key(&mip->rc4key, mip->keylen, mip->sesskey);
} else {
/*
* We do the first key change here as the first packet is expected
* to have a sequence number of 0 and we'll therefore not expect
* to have to change the key at that point.
*/
log_Printf(LogDEBUG, "MPPEInitInput: Key changed [%d]\n", mip->cohnum);
MPPEKeyChange(mip);
}
return mip;
}
static void *
MPPEInitOutput(struct lcp_opt *o)
{
struct mppe_state *mop;
if (!MPPE_MasterKeyValid) {
log_Printf(LogWARN, "MPPE: Cannot initialise without CHAP81\n");
return NULL;
}
if ((mop = MPPE_InitState(o)) == NULL) {
log_Printf(LogWARN, "MPPEOutput: Cannot initialise - unexpected options\n");
return NULL;
}
log_Printf(LogDEBUG, "MPPE: InitOutput: %d-bits\n", mop->keybits);
GetAsymetricStartKey(MPPE_MasterKey, mop->mastkey, mop->keylen, 1,
MPPE_IsServer);
GetNewKeyFromSHA(mop->mastkey, mop->mastkey, mop->keylen, mop->sesskey);
MPPEReduceSessionKey(mop);
log_Printf(LogCCP, "MPPE: Output channel initiated\n");
if (!mop->stateless) {
/*
* We need to initialise our dictionary now as the first packet we
* send won't have the FLUSHED bit set.
*/
log_Printf(LogDEBUG, "MPPEInitOutput: Dictionary initialised [%d]\n",
mop->cohnum);
RC4_set_key(&mop->rc4key, mop->keylen, mop->sesskey);
}
return mop;
}
static void
MPPETermInput(void *v)
{
free(v);
}
static void
MPPETermOutput(void *v)
{
free(v);
}
const struct ccp_algorithm MPPEAlgorithm = {
TY_MPPE,
CCP_NEG_MPPE,
MPPEDispOpts,
MPPEUsable,
MPPERequired,
{
MPPESetOptsInput,
MPPEInitInput,
MPPETermInput,
MPPEResetInput,
MPPEInput,
MPPEDictSetup
},
{
2,
MPPEInitOptsOutput,
MPPESetOptsOutput,
MPPEInitOutput,
MPPETermOutput,
MPPEResetOutput,
MPPEOutput
},
};