| /*- |
| * 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: src/usr.sbin/ppp/mppe.c,v 1.28.26.1 2010/12/21 17:10:29 kensmith Exp $ |
| */ |
| |
| #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 |
| }, |
| }; |