1de7b4b805
Mainly focus on files that use BSD 2-Clause license, however the tool I was using misidentified many licenses so this was mostly a manual - error prone - task. The Software Package Data Exchange (SPDX) group provides a specification to make it easier for automated tools to detect and summarize well known opensource licenses. We are gradually adopting the specification, noting that the tags are considered only advisory and do not, in any way, superceed or replace the license texts. No functional change intended.
820 lines
20 KiB
C
820 lines
20 KiB
C
/*-
|
|
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD
|
|
*
|
|
* 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/param.h>
|
|
|
|
#include <sys/socket.h>
|
|
#include <netinet/in_systm.h>
|
|
#include <netinet/in.h>
|
|
#include <netinet/ip.h>
|
|
#include <sys/un.h>
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <termios.h>
|
|
#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"
|
|
#include "ua.h"
|
|
#include "descriptor.h"
|
|
#ifndef NORADIUS
|
|
#include "radius.h"
|
|
#endif
|
|
#include "ncpaddr.h"
|
|
#include "iplist.h"
|
|
#include "slcompress.h"
|
|
#include "ipcp.h"
|
|
#include "ipv6cp.h"
|
|
#include "filter.h"
|
|
#include "mp.h"
|
|
#include "ncp.h"
|
|
#include "bundle.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;
|
|
unsigned 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:
|
|
break;
|
|
}
|
|
}
|
|
|
|
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 __unused, int pri __unused,
|
|
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 */
|
|
ua_htons(&prefix, rp);
|
|
|
|
/* 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 __unused)
|
|
{
|
|
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");
|
|
fsm_Output(&ccp->fsm, CODE_RESETREQ, ccp->fsm.reqid++, NULL, 0,
|
|
MB_CCPOUT);
|
|
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 __unused, struct ccp *ccp __unused,
|
|
u_short proto __unused, struct mbuf *mp __unused)
|
|
{
|
|
/* Nothing to see here */
|
|
}
|
|
|
|
static const char *
|
|
MPPEDispOpts(struct fsm_opt *o)
|
|
{
|
|
static char buf[70];
|
|
u_int32_t val;
|
|
char ch;
|
|
int len, n;
|
|
|
|
ua_ntohl(o->data, &val);
|
|
len = 0;
|
|
if ((n = snprintf(buf, sizeof buf, "value 0x%08x ", (unsigned)val)) > 0)
|
|
len += n;
|
|
if (!(val & MPPE_OPT_BITMASK)) {
|
|
if ((n = snprintf(buf + len, sizeof buf - len, "(0")) > 0)
|
|
len += n;
|
|
} else {
|
|
ch = '(';
|
|
if (val & MPPE_OPT_128BIT) {
|
|
if ((n = snprintf(buf + len, sizeof buf - len, "%c128", ch)) > 0)
|
|
len += n;
|
|
ch = '/';
|
|
}
|
|
if (val & MPPE_OPT_56BIT) {
|
|
if ((n = snprintf(buf + len, sizeof buf - len, "%c56", ch)) > 0)
|
|
len += n;
|
|
ch = '/';
|
|
}
|
|
if (val & MPPE_OPT_40BIT) {
|
|
if ((n = snprintf(buf + len, sizeof buf - len, "%c40", ch)) > 0)
|
|
len += n;
|
|
ch = '/';
|
|
}
|
|
}
|
|
|
|
if ((n = snprintf(buf + len, sizeof buf - len, " bits, state%s",
|
|
(val & MPPE_OPT_STATELESS) ? "less" : "ful")) > 0)
|
|
len += n;
|
|
|
|
if (val & MPPE_OPT_COMPRESSED) {
|
|
if ((n = snprintf(buf + len, sizeof buf - len, ", compressed")) > 0)
|
|
len += n;
|
|
}
|
|
|
|
snprintf(buf + len, sizeof buf - len, ")");
|
|
|
|
return buf;
|
|
}
|
|
|
|
static int
|
|
MPPEUsable(struct fsm *fp)
|
|
{
|
|
int ok;
|
|
#ifndef NORADIUS
|
|
struct radius *r = &fp->bundle->radius;
|
|
|
|
/*
|
|
* If the radius server gave us RAD_MICROSOFT_MS_MPPE_ENCRYPTION_TYPES,
|
|
* use that instead of our configuration value.
|
|
*/
|
|
if (*r->cfg.file) {
|
|
ok = r->mppe.sendkeylen && r->mppe.recvkeylen;
|
|
if (!ok)
|
|
log_Printf(LogCCP, "MPPE: Not permitted by RADIUS server\n");
|
|
} else
|
|
#endif
|
|
{
|
|
struct lcp *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)
|
|
{
|
|
#ifndef NORADIUS
|
|
/*
|
|
* If the radius server gave us RAD_MICROSOFT_MS_MPPE_ENCRYPTION_POLICY,
|
|
* use that instead of our configuration value.
|
|
*/
|
|
if (*fp->bundle->radius.cfg.file && fp->bundle->radius.mppe.policy)
|
|
return fp->bundle->radius.mppe.policy == MPPE_POLICY_REQUIRED ? 1 : 0;
|
|
#endif
|
|
|
|
return fp->link->ccp.cfg.mppe.required;
|
|
}
|
|
|
|
static u_int32_t
|
|
MPPE_ConfigVal(struct bundle *bundle __unused, const struct ccp_config *cfg)
|
|
{
|
|
u_int32_t val;
|
|
|
|
val = cfg->mppe.state == MPPE_STATELESS ? MPPE_OPT_STATELESS : 0;
|
|
#ifndef NORADIUS
|
|
/*
|
|
* If the radius server gave us RAD_MICROSOFT_MS_MPPE_ENCRYPTION_TYPES,
|
|
* use that instead of our configuration value.
|
|
*/
|
|
if (*bundle->radius.cfg.file && bundle->radius.mppe.types) {
|
|
if (bundle->radius.mppe.types & MPPE_TYPE_40BIT)
|
|
val |= MPPE_OPT_40BIT;
|
|
if (bundle->radius.mppe.types & MPPE_TYPE_128BIT)
|
|
val |= MPPE_OPT_128BIT;
|
|
} else
|
|
#endif
|
|
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 bundle *bundle, struct fsm_opt *o,
|
|
const struct ccp_config *cfg)
|
|
{
|
|
u_int32_t mval;
|
|
|
|
o->hdr.len = 6;
|
|
|
|
if (!MPPE_MasterKeyValid) {
|
|
log_Printf(LogCCP, "MPPE: MasterKey is invalid,"
|
|
" MPPE is available only with CHAP81 authentication\n");
|
|
mval = 0;
|
|
ua_htonl(&mval, o->data);
|
|
return;
|
|
}
|
|
|
|
|
|
mval = MPPE_ConfigVal(bundle, cfg);
|
|
ua_htonl(&mval, o->data);
|
|
}
|
|
|
|
/*
|
|
* Our CCP request was NAK'd with the given options
|
|
*/
|
|
static int
|
|
MPPESetOptsOutput(struct bundle *bundle, struct fsm_opt *o,
|
|
const struct ccp_config *cfg)
|
|
{
|
|
u_int32_t mval, peer;
|
|
|
|
ua_ntohl(o->data, &peer);
|
|
|
|
if (!MPPE_MasterKeyValid)
|
|
/* Treat their NAK as a REJ */
|
|
return MODE_NAK;
|
|
|
|
mval = MPPE_ConfigVal(bundle, 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);
|
|
}
|
|
|
|
ua_htonl(&mval, o->data);
|
|
|
|
return MODE_ACK;
|
|
}
|
|
|
|
/*
|
|
* The peer has requested the given options
|
|
*/
|
|
static int
|
|
MPPESetOptsInput(struct bundle *bundle, struct fsm_opt *o,
|
|
const struct ccp_config *cfg)
|
|
{
|
|
u_int32_t mval, peer;
|
|
int res = MODE_ACK;
|
|
|
|
ua_ntohl(o->data, &peer);
|
|
if (!MPPE_MasterKeyValid) {
|
|
if (peer != 0) {
|
|
peer = 0;
|
|
ua_htonl(&peer, o->data);
|
|
return MODE_NAK;
|
|
} else
|
|
return MODE_ACK;
|
|
}
|
|
|
|
mval = MPPE_ConfigVal(bundle, 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) {
|
|
ua_htonl(&mval, o->data);
|
|
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;
|
|
ua_htonl(&mval, o->data);
|
|
|
|
return res;
|
|
}
|
|
|
|
static struct mppe_state *
|
|
MPPE_InitState(struct fsm_opt *o)
|
|
{
|
|
struct mppe_state *mp;
|
|
u_int32_t val;
|
|
|
|
if ((mp = calloc(1, sizeof *mp)) != NULL) {
|
|
ua_ntohl(o->data, &val);
|
|
|
|
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 bundle *bundle __unused, struct fsm_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);
|
|
|
|
#ifndef NORADIUS
|
|
if (*bundle->radius.cfg.file && bundle->radius.mppe.recvkey) {
|
|
if (mip->keylen > bundle->radius.mppe.recvkeylen)
|
|
mip->keylen = bundle->radius.mppe.recvkeylen;
|
|
if (mip->keylen > sizeof mip->mastkey)
|
|
mip->keylen = sizeof mip->mastkey;
|
|
memcpy(mip->mastkey, bundle->radius.mppe.recvkey, mip->keylen);
|
|
} else
|
|
#endif
|
|
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 bundle *bundle __unused, struct fsm_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);
|
|
|
|
#ifndef NORADIUS
|
|
if (*bundle->radius.cfg.file && bundle->radius.mppe.sendkey) {
|
|
if (mop->keylen > bundle->radius.mppe.sendkeylen)
|
|
mop->keylen = bundle->radius.mppe.sendkeylen;
|
|
if (mop->keylen > sizeof mop->mastkey)
|
|
mop->keylen = sizeof mop->mastkey;
|
|
memcpy(mop->mastkey, bundle->radius.mppe.sendkey, mop->keylen);
|
|
} else
|
|
#endif
|
|
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
|
|
},
|
|
};
|