Add support for stateful MPPE (microsoft encryption) providing

encryption compatibility with Windows 2000.  Stateful encryption
uses less CPU but is bad on lossy transports.

The ``set mppe'' command has been expanded.  If it's used with any
arguments, ppp will insist on encryption, closing LCP if the other
end refuses.

Unfortunately, Microsoft have abused the CCP reset request so that
receiving a reset request does not result in a reset ack when using
MPPE...

Sponsored by:	Monzoon Networks AG and FreeBSD Services Limited
This commit is contained in:
Brian Somers 2001-06-18 15:00:22 +00:00
parent c8b9fb53a1
commit 6cf6ee7625
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=78411
11 changed files with 683 additions and 202 deletions

View File

@ -82,7 +82,7 @@ static void CcpLayerFinish(struct fsm *);
static int CcpLayerUp(struct fsm *);
static void CcpLayerDown(struct fsm *);
static void CcpInitRestartCounter(struct fsm *, int);
static void CcpRecvResetReq(struct fsm *);
static int CcpRecvResetReq(struct fsm *);
static void CcpRecvResetAck(struct fsm *, u_char);
static struct fsm_callbacks ccp_Callbacks = {
@ -150,8 +150,10 @@ static const struct ccp_algorithm * const algorithm[] = {
int
ccp_ReportStatus(struct cmdargs const *arg)
{
struct ccp_opt **o;
struct link *l;
struct ccp *ccp;
int f;
l = command_ChooseLink(arg);
ccp = &l->ccp;
@ -166,6 +168,19 @@ ccp_ReportStatus(struct cmdargs const *arg)
ccp->compin, ccp->uncompin);
}
if (ccp->in.algorithm != -1)
prompt_Printf(arg->prompt, "\n Input Options: %s\n",
(*algorithm[ccp->in.algorithm]->Disp)(&ccp->in.opt));
if (ccp->out.algorithm != -1) {
o = &ccp->out.opt;
for (f = 0; f < ccp->out.algorithm; f++)
if (IsEnabled(ccp->cfg.neg[algorithm[f]->Neg]))
o = &(*o)->next;
prompt_Printf(arg->prompt, " Output Options: %s\n",
(*algorithm[ccp->out.algorithm]->Disp)(&(*o)->val));
}
prompt_Printf(arg->prompt, "\n Defaults: ");
prompt_Printf(arg->prompt, "FSM retry = %us, max %u Config"
" REQ%s, %u Term REQ%s\n", ccp->cfg.fsm.timeout,
@ -174,16 +189,36 @@ ccp_ReportStatus(struct cmdargs const *arg)
prompt_Printf(arg->prompt, " deflate windows: ");
prompt_Printf(arg->prompt, "incoming = %d, ", ccp->cfg.deflate.in.winsize);
prompt_Printf(arg->prompt, "outgoing = %d\n", ccp->cfg.deflate.out.winsize);
prompt_Printf(arg->prompt, " DEFLATE: %s\n",
#ifdef HAVE_DES
prompt_Printf(arg->prompt, " MPPE: ");
if (ccp->cfg.mppe.keybits)
prompt_Printf(arg->prompt, "%d bits, ", ccp->cfg.mppe.keybits);
else
prompt_Printf(arg->prompt, "any bits, ");
switch (ccp->cfg.mppe.state) {
case MPPE_STATEFUL:
prompt_Printf(arg->prompt, "statefull");
break;
case MPPE_STATELESS:
prompt_Printf(arg->prompt, "stateless");
break;
case MPPE_ANYSTATE:
prompt_Printf(arg->prompt, "any state");
break;
}
prompt_Printf(arg->prompt, "%s\n",
ccp->cfg.mppe.required ? ", required" : "");
#endif
prompt_Printf(arg->prompt, "\n DEFLATE: %s\n",
command_ShowNegval(ccp->cfg.neg[CCP_NEG_DEFLATE]));
prompt_Printf(arg->prompt, " PREDICTOR1: %s\n",
command_ShowNegval(ccp->cfg.neg[CCP_NEG_PRED1]));
prompt_Printf(arg->prompt, " DEFLATE24: %s\n",
command_ShowNegval(ccp->cfg.neg[CCP_NEG_DEFLATE24]));
#ifdef HAVE_DES
prompt_Printf(arg->prompt, " MPPE: %s",
prompt_Printf(arg->prompt, " MPPE: %s\n",
command_ShowNegval(ccp->cfg.neg[CCP_NEG_MPPE]));
prompt_Printf(arg->prompt, " (Key Size = %d-bits)\n", ccp->cfg.mppe.keybits);
#endif
return 0;
}
@ -215,7 +250,9 @@ ccp_Init(struct ccp *ccp, struct bundle *bundle, struct link *l,
ccp->cfg.neg[CCP_NEG_PRED1] = NEG_ENABLED|NEG_ACCEPTED;
ccp->cfg.neg[CCP_NEG_DEFLATE24] = 0;
#ifdef HAVE_DES
ccp->cfg.mppe.keybits = 128;
ccp->cfg.mppe.keybits = 0;
ccp->cfg.mppe.state = MPPE_ANYSTATE;
ccp->cfg.mppe.required = 0;
ccp->cfg.neg[CCP_NEG_MPPE] = NEG_ENABLED|NEG_ACCEPTED;
#endif
@ -238,6 +275,30 @@ ccp_Setup(struct ccp *ccp)
ccp->uncompin = ccp->compin = 0;
}
/*
* Is ccp *REQUIRED* ?
* We ask each of the configured ccp protocols if they're required and
* return TRUE if they are.
*
* It's not possible for the peer to reject a required ccp protocol
* without our state machine bringing the supporting lcp layer down.
*
* If ccp is required but not open, the NCP layer should not push
* any data into the link.
*/
int
ccp_Required(struct ccp *ccp)
{
int f;
for (f = 0; f < NALGORITHMS; f++)
if (IsEnabled(ccp->cfg.neg[algorithm[f]->Neg]) &&
(*algorithm[f]->Required)(&ccp->fsm))
return 1;
return 0;
}
static void
CcpInitRestartCounter(struct fsm *fp, int what)
{
@ -332,13 +393,14 @@ CcpSendTerminateAck(struct fsm *fp, u_char id)
fsm_Output(fp, CODE_TERMACK, id, NULL, 0, MB_CCPOUT);
}
static void
static int
CcpRecvResetReq(struct fsm *fp)
{
/* Got a reset REQ, reset outgoing dictionary */
struct ccp *ccp = fsm2ccp(fp);
if (ccp->out.state != NULL)
(*algorithm[ccp->out.algorithm]->o.Reset)(ccp->out.state);
if (ccp->out.state == NULL)
return 1;
return (*algorithm[ccp->out.algorithm]->o.Reset)(ccp->out.state);
}
static void
@ -397,6 +459,12 @@ CcpLayerFinish(struct fsm *fp)
free(ccp->out.opt);
ccp->out.opt = next;
}
if (ccp_Required(ccp)) {
if (fp->link->lcp.fsm.state == ST_OPENED)
log_Printf(LogLCP, "%s: Closing due to CCP completion\n", fp->link->name);
fsm_Close(&fp->link->lcp.fsm);
}
}
/* Called when CCP has reached the OPEN state */
@ -531,17 +599,28 @@ CcpDecodeConfig(struct fsm *fp, u_char *cp, int plen, int mode_type,
" option\n", fp->link->name);
else {
memcpy(&o->val, cp, length);
if ((*algorithm[f]->o.Set)(&o->val) == MODE_ACK)
if ((*algorithm[f]->o.Set)(&o->val, &ccp->cfg) == MODE_ACK)
ccp->my_proto = algorithm[f]->id;
else {
ccp->his_reject |= (1 << type);
ccp->my_proto = -1;
if (algorithm[f]->Required(fp)) {
log_Printf(LogWARN, "%s: Cannot understand peers (required)"
" %s negotiation\n", fp->link->name,
protoname(algorithm[f]->id));
fsm_Close(&fp->link->lcp.fsm);
}
}
}
break;
case MODE_REJ:
ccp->his_reject |= (1 << type);
ccp->my_proto = -1;
if (algorithm[f]->Required(fp)) {
log_Printf(LogWARN, "%s: Peer rejected (required) %s negotiation\n",
fp->link->name, protoname(algorithm[f]->id));
fsm_Close(&fp->link->lcp.fsm);
}
break;
}
}
@ -618,17 +697,26 @@ static struct mbuf *
ccp_LayerPush(struct bundle *b, struct link *l, struct mbuf *bp,
int pri, u_short *proto)
{
if (PROTO_COMPRESSIBLE(*proto) && l->ccp.fsm.state == ST_OPENED &&
l->ccp.out.state != NULL) {
bp = (*algorithm[l->ccp.out.algorithm]->o.Write)
(l->ccp.out.state, &l->ccp, l, pri, proto, bp);
switch (*proto) {
case PROTO_ICOMPD:
m_settype(bp, MB_ICOMPDOUT);
break;
case PROTO_COMPD:
m_settype(bp, MB_COMPDOUT);
break;
if (PROTO_COMPRESSIBLE(*proto)) {
if (l->ccp.fsm.state != ST_OPENED) {
if (ccp_Required(&l->ccp)) {
/* The NCP layer shouldn't have let this happen ! */
log_Printf(LogERROR, "%s: Unexpected attempt to use an unopened and"
" required CCP layer\n", l->name);
m_freem(bp);
bp = NULL;
}
} else if (l->ccp.out.state != NULL) {
bp = (*algorithm[l->ccp.out.algorithm]->o.Write)
(l->ccp.out.state, &l->ccp, l, pri, proto, bp);
switch (*proto) {
case PROTO_ICOMPD:
m_settype(bp, MB_ICOMPDOUT);
break;
case PROTO_COMPD:
m_settype(bp, MB_COMPDOUT);
break;
}
}
}
@ -706,9 +794,15 @@ ccp_SetOpenMode(struct ccp *ccp)
}
int
ccp_IsUsable(struct fsm *fp)
ccp_DefaultUsable(struct fsm *fp)
{
return 1;
}
int
ccp_DefaultRequired(struct fsm *fp)
{
return 0;
}
struct layer ccplayer = { LAYER_CCP, "ccp", ccp_LayerPush, ccp_LayerPull };

View File

@ -54,6 +54,14 @@
#define CCP_NEG_TOTAL 3
#endif
#ifdef HAVE_DES
enum mppe_negstate {
MPPE_ANYSTATE,
MPPE_STATELESS,
MPPE_STATEFUL
};
#endif
struct mbuf;
struct link;
@ -66,6 +74,8 @@ struct ccp_config {
#ifdef HAVE_DES
struct {
int keybits;
enum mppe_negstate state;
unsigned required : 1;
} mppe;
#endif
struct fsm_retry fsm; /* How often/frequently to resend requests */
@ -115,6 +125,7 @@ struct ccp_algorithm {
int Neg; /* ccp_config neg array item */
const char *(*Disp)(struct lcp_opt *); /* Use result immediately ! */
int (*Usable)(struct fsm *); /* Ok to negotiate ? */
int (*Required)(struct fsm *); /* Must negotiate ? */
struct {
int (*Set)(struct lcp_opt *, const struct ccp_config *);
void *(*Init)(struct lcp_opt *);
@ -125,10 +136,10 @@ struct ccp_algorithm {
} i;
struct {
void (*OptInit)(struct lcp_opt *, const struct ccp_config *);
int (*Set)(struct lcp_opt *);
int (*Set)(struct lcp_opt *, const struct ccp_config *);
void *(*Init)(struct lcp_opt *);
void (*Term)(void *);
void (*Reset)(void *);
int (*Reset)(void *);
struct mbuf *(*Write)(void *, struct ccp *, struct link *, int, u_short *,
struct mbuf *);
} o;
@ -137,6 +148,7 @@ struct ccp_algorithm {
extern void ccp_Init(struct ccp *, struct bundle *, struct link *,
const struct fsm_parent *);
extern void ccp_Setup(struct ccp *);
extern int ccp_Required(struct ccp *);
extern void ccp_SendResetReq(struct fsm *);
extern struct mbuf *ccp_Input(struct bundle *, struct link *, struct mbuf *);
@ -144,6 +156,7 @@ extern int ccp_ReportStatus(struct cmdargs const *);
extern u_short ccp_Proto(struct ccp *);
extern void ccp_SetupCallbacks(struct ccp *);
extern int ccp_SetOpenMode(struct ccp *);
extern int ccp_IsUsable(struct fsm *);
extern int ccp_DefaultUsable(struct fsm *);
extern int ccp_DefaultRequired(struct fsm *);
extern struct layer ccplayer;

View File

@ -135,7 +135,7 @@
#define VAR_URGENTPORTS 33
#define VAR_LOGOUT 34
#define VAR_IFQUEUE 35
#define VAR_KEYBITS 36
#define VAR_MPPE 36
/* ``accept|deny|disable|enable'' masks */
#define NEG_HISMASK (1)
@ -1610,20 +1610,43 @@ SetVariable(struct cmdargs const *arg)
break;
#ifdef HAVE_DES
case VAR_KEYBITS:
if (arg->argc > arg->argn) {
l->ccp.cfg.mppe.keybits = atoi(arg->argv[arg->argn]);
if (l->ccp.cfg.mppe.keybits != 40 &&
l->ccp.cfg.mppe.keybits != 56 &&
l->ccp.cfg.mppe.keybits != 128 ) {
log_Printf(LogWARN, "%d: Invalid bits number\n",
l->ccp.cfg.mppe.keybits);
l->ccp.cfg.mppe.keybits = 40;
}
} else {
err = "No bits number pecified\n";
log_Printf(LogWARN, err);
case VAR_MPPE:
if (arg->argc > arg->argn + 2)
return -1;
if (arg->argc == arg->argn) {
l->ccp.cfg.mppe.keybits = 0;
l->ccp.cfg.mppe.state = MPPE_ANYSTATE;
l->ccp.cfg.mppe.required = 0;
break;
}
if (!strcmp(argp, "*"))
long_val = 0;
else {
long_val = atol(argp);
if (long_val != 40 && long_val != 56 && long_val != 128) {
log_Printf(LogWARN, "%s: Invalid bits value\n", argp);
return -1;
}
}
if (arg->argc == arg->argn + 2) {
if (!strcmp(arg->argv[arg->argn + 1], "*"))
l->ccp.cfg.mppe.state = MPPE_ANYSTATE;
else if (!strcasecmp(arg->argv[arg->argn + 1], "stateless"))
l->ccp.cfg.mppe.state = MPPE_STATELESS;
else if (!strcasecmp(arg->argv[arg->argn + 1], "statefull"))
l->ccp.cfg.mppe.state = MPPE_STATEFUL;
else {
log_Printf(LogWARN, "%s: Invalid state value\n",
arg->argv[arg->argn + 1]);
return -1;
}
} else
l->ccp.cfg.mppe.state = MPPE_ANYSTATE;
l->ccp.cfg.mppe.keybits = long_val;
l->ccp.cfg.mppe.required = 1;
break;
#endif
@ -2081,8 +2104,8 @@ static struct cmdtab const SetCommands[] = {
(const void *) VAR_WINSIZE},
#ifdef HAVE_DES
{"mppe", NULL, SetVariable, LOCAL_AUTH | LOCAL_CX_OPT,
"MPPE key size", "set mppe {40|56|128}",
(const void *) VAR_KEYBITS},
"MPPE key size and state", "set mppe [40|56|128|* [statefull|stateless|*]]",
(const void *) VAR_MPPE},
#endif
{"device", "line", SetVariable, LOCAL_AUTH | LOCAL_CX,
"physical device name", "set device|line device-name[,device-name]",

View File

@ -57,7 +57,7 @@ static u_char EMPTY_BLOCK[4] = { 0x00, 0x00, 0xff, 0xff };
#define DEFLATE_CHUNK_LEN (1536 - sizeof(struct mbuf))
static void
static int
DeflateResetOutput(void *v)
{
struct deflate_state *state = (struct deflate_state *)v;
@ -66,6 +66,8 @@ DeflateResetOutput(void *v)
state->uncomp_rec = 0;
deflateReset(&state->cx);
log_Printf(LogCCP, "Deflate: Output channel reset\n");
return 1; /* Ask FSM to ACK */
}
static struct mbuf *
@ -451,7 +453,7 @@ DeflateInitOptsOutput(struct lcp_opt *o, const struct ccp_config *cfg)
}
static int
DeflateSetOptsOutput(struct lcp_opt *o)
DeflateSetOptsOutput(struct lcp_opt *o, const struct ccp_config *cfg)
{
if (o->len != 4 || (o->data[0] & 15) != 8 || o->data[1] != '\0')
return MODE_REJ;
@ -554,7 +556,8 @@ const struct ccp_algorithm PppdDeflateAlgorithm = {
TY_PPPD_DEFLATE, /* Older versions of pppd expected this ``type'' */
CCP_NEG_DEFLATE24,
DeflateDispOpts,
ccp_IsUsable,
ccp_DefaultUsable,
ccp_DefaultRequired,
{
DeflateSetOptsInput,
DeflateInitInput,
@ -577,7 +580,8 @@ const struct ccp_algorithm DeflateAlgorithm = {
TY_DEFLATE, /* rfc 1979 */
CCP_NEG_DEFLATE,
DeflateDispOpts,
ccp_IsUsable,
ccp_DefaultUsable,
ccp_DefaultRequired,
{
DeflateSetOptsInput,
DeflateInitInput,

View File

@ -978,14 +978,15 @@ FsmRecvTimeRemain(struct fsm *fp, struct fsmheader *lhp, struct mbuf *bp)
static void
FsmRecvResetReq(struct fsm *fp, struct fsmheader *lhp, struct mbuf *bp)
{
(*fp->fn->RecvResetReq)(fp);
/*
* All sendable compressed packets are queued in the first (lowest
* priority) modem output queue.... dump 'em to the priority queue
* so that they arrive at the peer before our ResetAck.
*/
link_SequenceQueue(fp->link);
fsm_Output(fp, CODE_RESETACK, lhp->id, NULL, 0, MB_CCPOUT);
if ((*fp->fn->RecvResetReq)(fp)) {
/*
* All sendable compressed packets are queued in the first (lowest
* priority) modem output queue.... dump 'em to the priority queue
* so that they arrive at the peer before our ResetAck.
*/
link_SequenceQueue(fp->link);
fsm_Output(fp, CODE_RESETACK, lhp->id, NULL, 0, MB_CCPOUT);
}
m_freem(bp);
}
@ -1050,11 +1051,12 @@ fsm_Input(struct fsm *fp, struct mbuf *bp)
(*codep->recv)(fp, &lh, bp);
}
void
int
fsm_NullRecvResetReq(struct fsm *fp)
{
log_Printf(fp->LogLevel, "%s: Oops - received unexpected reset req\n",
fp->link->name);
return 1;
}
void

View File

@ -81,7 +81,7 @@ struct fsm_callbacks {
void (*SendTerminateAck) (struct fsm *, u_char); /* Send Term ACK please */
void (*DecodeConfig) (struct fsm *, u_char *, int, int, struct fsm_decode *);
/* Deal with incoming data */
void (*RecvResetReq) (struct fsm *fp); /* Reset output */
int (*RecvResetReq) (struct fsm *fp); /* Reset output */
void (*RecvResetAck) (struct fsm *fp, u_char); /* Reset input */
};
@ -174,7 +174,7 @@ extern void fsm_Up(struct fsm *);
extern void fsm_Down(struct fsm *);
extern void fsm_Input(struct fsm *, struct mbuf *);
extern void fsm_Close(struct fsm *);
extern void fsm_NullRecvResetReq(struct fsm *);
extern int fsm_NullRecvResetReq(struct fsm *);
extern void fsm_NullRecvResetAck(struct fsm *, u_char);
extern void fsm_Reopen(struct fsm *);
extern void fsm2initial(struct fsm *);

View File

@ -906,6 +906,14 @@ ip_PushPacket(struct link *l, struct bundle *bundle)
if (ipcp->fsm.state != ST_OPENED)
return 0;
/*
* If ccp is not open but is required, do nothing.
*/
if (l->ccp.fsm.state != ST_OPENED && ccp_Required(&l->ccp)) {
log_Printf(LogPHASE, "%s: Not transmitting... waiting for CCP\n", l->name);
return 0;
}
queue = ipcp->Queue + IPCP_QUEUES(ipcp) - 1;
do {
if (queue->top) {

View File

@ -30,6 +30,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <termios.h>
#ifdef __FreeBSD__
#include <sha.h>
@ -61,23 +62,53 @@
* 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
struct mppe_state {
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;
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];
static void
/*
* The peer has missed a packet. Mark the next output frame to be FLUSHED
*/
static int
MPPEResetOutput(void *v)
{
log_Printf(LogCCP, "MPPE: Output channel reset\n");
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
@ -112,17 +143,18 @@ MPPEOutput(void *v, struct ccp *ccp, struct link *l, int pri, u_short *proto,
{
struct mppe_state *mop = (struct mppe_state *)v;
struct mbuf *mo;
u_short nproto;
int ilen;
u_short nproto, prefix;
int dictinit, ilen, len;
char *rp;
log_Printf(LogCCP, "MPPE: Output\n");
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;
}
@ -132,12 +164,32 @@ MPPEOutput(void *v, struct ccp *ccp, struct link *l, int pri, u_short *proto,
mo = m_get(4, MB_CCPOUT);
mo->m_next = mp;
/* Init RC4 keys */
RC4_set_key(&mop->rc4key, mop->keylen, mop->sesskey);
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 */
rp = MBUF_CTOP(mo);
*(u_short *)rp = htons(0x9000 | mop->cohnum);
*(u_short *)rp = htons(prefix);
/* Save encrypted protocol number */
nproto = htons(*proto);
@ -147,15 +199,17 @@ MPPEOutput(void *v, struct ccp *ccp, struct link *l, int pri, u_short *proto,
rp = MBUF_CTOP(mp);
RC4(&mop->rc4key, ilen, rp, rp);
/* Rotate keys */
MPPEKeyChange(mop);
mop->cohnum ++; mop->cohnum &= 0xFFF;
mop->cohnum++;
mop->cohnum &= ~MPPE_HEADER_BITMASK;
/* Chage protocol number */
/* 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, m_length(mo));
*proto, len);
return mo;
}
@ -163,7 +217,7 @@ MPPEOutput(void *v, struct ccp *ccp, struct link *l, int pri, u_short *proto,
static void
MPPEResetInput(void *v)
{
log_Printf(LogCCP, "MPPE: Input channel reset\n");
log_Printf(LogCCP, "MPPE: Unexpected input channel ack\n");
}
static struct mbuf *
@ -172,43 +226,126 @@ 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 ilen;
log_Printf(LogCCP, "MPPE: Input\n");
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);
if ((prefix & 0xF000) != 0x9000) {
log_Printf(LogERROR, "MPPE: Input: Invalid packet\n");
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 &= 0xFFF;
while (prefix != mip->cohnum) {
MPPEKeyChange(mip);
mip->cohnum ++; mip->cohnum &= 0xFFF;
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 ? */
}
RC4_set_key(&mip->rc4key, mip->keylen, mip->sesskey);
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);
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);
RC4(&mip->rc4key, m_length(mp), rp, rp);
len = m_length(mp);
RC4(&mip->rc4key, len, rp, rp);
log_Printf(LogDEBUG, "MPPE: Input: Decrypted: Proto %02x (%d bytes)\n",
*proto, m_length(mp));
log_Printf(LogDEBUG, "MPPEInput: Decrypted: Proto %02x (%d bytes)\n",
*proto, len);
log_DumpBp(LogDEBUG, "MPPEInput: Decrypted: Packet:", mp);
log_DumpBp(LogDEBUG, "MPPE: Input: Decrypted: Packet:", mp);
ccp->uncompin += len;
return mp;
}
@ -216,14 +353,51 @@ MPPEInput(void *v, struct ccp *ccp, u_short *proto, struct mbuf *mp)
static void
MPPEDictSetup(void *v, struct ccp *ccp, u_short proto, struct mbuf *mi)
{
log_Printf(LogCCP, "MPPE: DictSetup\n");
}
static const char *
MPPEDispOpts(struct lcp_opt *o)
{
static char buf[32];
sprintf(buf, "value 0x%08x", (int)ntohl(*(u_int32_t *)(o->data)));
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" : "full");
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;
}
@ -242,120 +416,213 @@ MPPEUsable(struct fsm *fp)
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_long val;
u_int32_t *p = (u_int32_t *)o->data;
o->len = 6;
log_Printf(LogCCP, "MPPE: InitOptsOutput\n");
if (!MPPE_MasterKeyValid) {
log_Printf(LogCCP, "MPPE: MasterKey is invalid,"
" MPPE is available only with CHAP81 authentication\n");
*(u_int32_t *)o->data = htonl(0x0);
*p = htonl(0x0);
return;
}
val = 0x1000000;
switch(cfg->mppe.keybits) {
case 128:
val |= 0x40; break;
case 56:
val |= 0x80; break;
case 40:
val |= 0x20; break;
}
*(u_int32_t *)o->data = htonl(val);
*p = htonl(MPPE_ConfigVal(cfg));
}
/*
* Our CCP request was NAK'd with the given options
*/
static int
MPPESetOptsOutput(struct lcp_opt *o)
MPPESetOptsOutput(struct lcp_opt *o, const struct ccp_config *cfg)
{
u_long *p = (u_long *)(o->data);
u_long val = ntohl(*p);
u_int32_t *p = (u_int32_t *)o->data;
u_int32_t peer = ntohl(*p);
u_int32_t mval;
log_Printf(LogCCP, "MPPE: SetOptsOutput\n");
if (!MPPE_MasterKeyValid)
/* Treat their NAK as a REJ */
return MODE_NAK;
if (!MPPE_MasterKeyValid) {
if (*p != 0x0) {
*p = 0x0;
return MODE_NAK;
} else {
return MODE_ACK;
}
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;
}
if (val == 0x01000020 ||
val == 0x01000040 ||
val == 0x01000080)
return MODE_ACK;
return MODE_NAK;
}
static int
MPPESetOptsInput(struct lcp_opt *o, const struct ccp_config *cfg)
{
u_long *p = (u_long *)(o->data);
u_long val = ntohl(*p);
u_long mval;
log_Printf(LogCCP, "MPPE: SetOptsInput\n");
if (!MPPE_MasterKeyValid) {
if (*p != 0x0) {
*p = 0x0;
return MODE_NAK;
} else {
return MODE_ACK;
}
/* Adjust our statelessness */
if (cfg->mppe.state == MPPE_ANYSTATE) {
mval &= ~MPPE_OPT_STATELESS;
mval |= (peer & MPPE_OPT_STATELESS);
}
mval = 0x01000000;
switch(cfg->mppe.keybits) {
case 128:
mval |= 0x40; break;
case 56:
mval |= 0x80; break;
case 40:
mval |= 0x20; break;
}
if (val == mval)
return MODE_ACK;
*p = htonl(mval);
return MODE_NAK;
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;
u_int32_t val = ntohl(*(unsigned long *)o->data);
log_Printf(LogCCP, "MPPE: InitInput\n");
if (!MPPE_MasterKeyValid) {
log_Printf(LogWARN, "MPPE: Cannot initialise without CHAP81\n");
return NULL;
}
mip = malloc(sizeof(*mip));
memset(mip, 0, sizeof(*mip));
if (val & 0x20) { /* 40-bits */
mip->keylen = 8;
mip->keybits = 40;
} else if (val & 0x80) { /* 56-bits */
mip->keylen = 8;
mip->keybits = 56;
} else { /* 128-bits */
mip->keylen = 16;
mip->keybits = 128;
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);
@ -366,9 +633,25 @@ MPPEInitInput(struct lcp_opt *o)
MPPEReduceSessionKey(mip);
MPPEKeyChange(mip);
log_Printf(LogCCP, "MPPE: Input channel initiated\n");
mip->cohnum = 0;
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;
}
@ -377,27 +660,15 @@ static void *
MPPEInitOutput(struct lcp_opt *o)
{
struct mppe_state *mop;
u_int32_t val = ntohl(*(unsigned long *)o->data);
log_Printf(LogCCP, "MPPE: InitOutput\n");
if (!MPPE_MasterKeyValid) {
log_Printf(LogWARN, "MPPE: Cannot initialise without CHAP81\n");
return NULL;
}
mop = malloc(sizeof(*mop));
memset(mop, 0, sizeof(*mop));
if (val & 0x20) { /* 40-bits */
mop->keylen = 8;
mop->keybits = 40;
} else if (val & 0x80) { /* 56-bits */
mop->keylen = 8;
mop->keybits = 56;
} else { /* 128-bits */
mop->keylen = 16;
mop->keybits = 128;
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);
@ -408,9 +679,17 @@ MPPEInitOutput(struct lcp_opt *o)
MPPEReduceSessionKey(mop);
MPPEKeyChange(mop);
log_Printf(LogCCP, "MPPE: Output channel initiated\n");
mop->cohnum = 0;
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;
}
@ -418,14 +697,12 @@ MPPEInitOutput(struct lcp_opt *o)
static void
MPPETermInput(void *v)
{
log_Printf(LogCCP, "MPPE: TermInput\n");
free(v);
}
static void
MPPETermOutput(void *v)
{
log_Printf(LogCCP, "MPPE: TermOutput\n");
free(v);
}
@ -434,6 +711,7 @@ const struct ccp_algorithm MPPEAlgorithm = {
CCP_NEG_MPPE,
MPPEDispOpts,
MPPEUsable,
MPPERequired,
{
MPPESetOptsInput,
MPPEInitInput,

View File

@ -4910,8 +4910,34 @@ This will allow
to do the necessary address translations to enable the process that
triggers the connection to connect once the link is up despite the
peer assigning us a new (dynamic) IP address.
.It set mppe {40|56|128}
This option selects particular key length. Default is 128.
.It set mppe Op 40|56|128|* Op stateless|statefull|*
This option selects the encryption parameters used when negotiation
MPPE. MPPE can be disabled entirely with the
.Dq disable mppe
command.
If no arguments are given,
.Nm
will attempt to negotiate a statefull link with a 128 bit key, but
will agree to whatever the peer requests (including no encryption
at all).
.Pp
If any arguments are given,
.Nm
will
.Em insist
on using MPPE and will close the link if it's rejected by the peer.
.Pp
The first argument specifies the number of bits that
.Nm
should insist on during negotiations and the second specifies whether
.Nm
should insist on statefull or stateless mode. In stateless mode, the
encryption dictionary is re-initialised with every packet according to
an encryption key that is changed with every packet. In statefull mode,
the encryption dictionary is re-initialised every 256 packets or after
the loss of any data and the key is changed every 256 packets.
Stateless mode is less efficient but is better for unreliable transport
layers.
.It set mrru Op Ar value
Setting this option enables Multi-link PPP negotiations, also known as
Multi-link Protocol or MP.

View File

@ -4910,8 +4910,34 @@ This will allow
to do the necessary address translations to enable the process that
triggers the connection to connect once the link is up despite the
peer assigning us a new (dynamic) IP address.
.It set mppe {40|56|128}
This option selects particular key length. Default is 128.
.It set mppe Op 40|56|128|* Op stateless|statefull|*
This option selects the encryption parameters used when negotiation
MPPE. MPPE can be disabled entirely with the
.Dq disable mppe
command.
If no arguments are given,
.Nm
will attempt to negotiate a statefull link with a 128 bit key, but
will agree to whatever the peer requests (including no encryption
at all).
.Pp
If any arguments are given,
.Nm
will
.Em insist
on using MPPE and will close the link if it's rejected by the peer.
.Pp
The first argument specifies the number of bits that
.Nm
should insist on during negotiations and the second specifies whether
.Nm
should insist on statefull or stateless mode. In stateless mode, the
encryption dictionary is re-initialised with every packet according to
an encryption key that is changed with every packet. In statefull mode,
the encryption dictionary is re-initialised every 256 packets or after
the loss of any data and the key is changed every 256 packets.
Stateless mode is less efficient but is better for unreliable transport
layers.
.It set mrru Op Ar value
Setting this option enables Multi-link PPP negotiations, also known as
Multi-link Protocol or MP.

View File

@ -139,13 +139,15 @@ Pred1ResetInput(void *v)
log_Printf(LogCCP, "Predictor1: Input channel reset\n");
}
static void
static int
Pred1ResetOutput(void *v)
{
struct pred1_state *state = (struct pred1_state *)v;
state->hash = 0;
memset(state->dict, '\0', sizeof state->dict);
log_Printf(LogCCP, "Predictor1: Output channel reset\n");
return 1; /* Ask FSM to ACK */
}
static void *
@ -304,7 +306,7 @@ Pred1InitOptsOutput(struct lcp_opt *o, const struct ccp_config *cfg)
}
static int
Pred1SetOptsOutput(struct lcp_opt *o)
Pred1SetOptsOutput(struct lcp_opt *o, const struct ccp_config *cfg)
{
if (o->len != 2) {
o->len = 2;
@ -316,14 +318,19 @@ Pred1SetOptsOutput(struct lcp_opt *o)
static int
Pred1SetOptsInput(struct lcp_opt *o, const struct ccp_config *cfg)
{
return Pred1SetOptsOutput(o);
if (o->len != 2) {
o->len = 2;
return MODE_NAK;
}
return MODE_ACK;
}
const struct ccp_algorithm Pred1Algorithm = {
TY_PRED1,
CCP_NEG_PRED1,
Pred1DispOpts,
ccp_IsUsable,
ccp_DefaultUsable,
ccp_DefaultRequired,
{
Pred1SetOptsInput,
Pred1InitInput,