mppe.c revision 83403
1/*-
2 * Copyright (c) 2000 Semen Ustimenko <semenu@FreeBSD.org>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 *
26 * $FreeBSD: head/usr.sbin/ppp/mppe.c 83403 2001-09-13 10:03:20Z brian $
27 */
28
29#include <sys/types.h>
30
31#include <stdio.h>
32#include <stdlib.h>
33#include <string.h>
34#include <termios.h>
35#ifdef __FreeBSD__
36#include <sha.h>
37#else
38#include <openssl/sha.h>
39#endif
40#include <openssl/rc4.h>
41
42#include "defs.h"
43#include "mbuf.h"
44#include "log.h"
45#include "timer.h"
46#include "fsm.h"
47#include "lqr.h"
48#include "hdlc.h"
49#include "lcp.h"
50#include "ccp.h"
51#include "throughput.h"
52#include "layer.h"
53#include "link.h"
54#include "chap_ms.h"
55#include "proto.h"
56#include "mppe.h"
57#include "ua.h"
58
59/*
60 * Documentation:
61 *
62 * draft-ietf-pppext-mppe-04.txt
63 * draft-ietf-pppext-mppe-keys-02.txt
64 */
65
66#define	MPPE_OPT_STATELESS	0x1000000
67#define	MPPE_OPT_COMPRESSED	0x01
68#define	MPPE_OPT_40BIT		0x20
69#define	MPPE_OPT_56BIT		0x80
70#define	MPPE_OPT_128BIT		0x40
71#define	MPPE_OPT_BITMASK	0xe0
72#define	MPPE_OPT_MASK		(MPPE_OPT_STATELESS | MPPE_OPT_BITMASK)
73
74#define	MPPE_FLUSHED			0x8000
75#define	MPPE_ENCRYPTED			0x1000
76#define	MPPE_HEADER_BITMASK		0xf000
77#define	MPPE_HEADER_FLAG		0x00ff
78#define	MPPE_HEADER_FLAGMASK		0x00ff
79#define	MPPE_HEADER_FLAGSHIFT		8
80#define	MPPE_HEADER_STATEFUL_KEYCHANGES	16
81
82struct mppe_state {
83  unsigned	stateless : 1;
84  unsigned	flushnext : 1;
85  unsigned	flushrequired : 1;
86  int		cohnum;
87  int		keylen;			/* 8 or 16 bytes */
88  int 		keybits;		/* 40, 56 or 128 bits */
89  char		sesskey[MPPE_KEY_LEN];
90  char		mastkey[MPPE_KEY_LEN];
91  RC4_KEY	rc4key;
92};
93
94int MPPE_MasterKeyValid = 0;
95int MPPE_IsServer = 0;
96char MPPE_MasterKey[MPPE_KEY_LEN];
97
98/*
99 * The peer has missed a packet.  Mark the next output frame to be FLUSHED
100 */
101static int
102MPPEResetOutput(void *v)
103{
104  struct mppe_state *mop = (struct mppe_state *)v;
105
106  if (mop->stateless)
107    log_Printf(LogCCP, "MPPE: Unexpected output channel reset\n");
108  else {
109    log_Printf(LogCCP, "MPPE: Output channel reset\n");
110    mop->flushnext = 1;
111  }
112
113  return 0;		/* Ask FSM not to ACK */
114}
115
116static void
117MPPEReduceSessionKey(struct mppe_state *mp)
118{
119  switch(mp->keybits) {
120  case 40:
121    mp->sesskey[2] = 0x9e;
122    mp->sesskey[1] = 0x26;
123  case 56:
124    mp->sesskey[0] = 0xd1;
125  case 128:
126  }
127}
128
129static void
130MPPEKeyChange(struct mppe_state *mp)
131{
132  char InterimKey[MPPE_KEY_LEN];
133  RC4_KEY RC4Key;
134
135  GetNewKeyFromSHA(mp->mastkey, mp->sesskey, mp->keylen, InterimKey);
136  RC4_set_key(&RC4Key, mp->keylen, InterimKey);
137  RC4(&RC4Key, mp->keylen, InterimKey, mp->sesskey);
138
139  MPPEReduceSessionKey(mp);
140}
141
142static struct mbuf *
143MPPEOutput(void *v, struct ccp *ccp, struct link *l, int pri, u_short *proto,
144           struct mbuf *mp)
145{
146  struct mppe_state *mop = (struct mppe_state *)v;
147  struct mbuf *mo;
148  u_short nproto, prefix;
149  int dictinit, ilen, len;
150  char *rp;
151
152  ilen = m_length(mp);
153  dictinit = 0;
154
155  log_Printf(LogDEBUG, "MPPE: Output: Proto %02x (%d bytes)\n", *proto, ilen);
156  if (*proto < 0x21 && *proto > 0xFA) {
157    log_Printf(LogDEBUG, "MPPE: Output: Not encrypting\n");
158    ccp->compout += ilen;
159    ccp->uncompout += ilen;
160    return mp;
161  }
162
163  log_DumpBp(LogDEBUG, "MPPE: Output: Encrypt packet:", mp);
164
165  /* Get mbuf for prefixes */
166  mo = m_get(4, MB_CCPOUT);
167  mo->m_next = mp;
168
169  rp = MBUF_CTOP(mo);
170  prefix = MPPE_ENCRYPTED | mop->cohnum;
171
172  if (mop->stateless ||
173      (mop->cohnum & MPPE_HEADER_FLAGMASK) == MPPE_HEADER_FLAG) {
174    /* Change our key */
175    log_Printf(LogDEBUG, "MPPEOutput: Key changed [%d]\n", mop->cohnum);
176    MPPEKeyChange(mop);
177    dictinit = 1;
178  }
179
180  if (mop->stateless || mop->flushnext) {
181    prefix |= MPPE_FLUSHED;
182    dictinit = 1;
183    mop->flushnext = 0;
184  }
185
186  if (dictinit) {
187    /* Initialise our dictionary */
188    log_Printf(LogDEBUG, "MPPEOutput: Dictionary initialised [%d]\n",
189               mop->cohnum);
190    RC4_set_key(&mop->rc4key, mop->keylen, mop->sesskey);
191  }
192
193  /* Set MPPE packet prefix */
194  ua_htons(&prefix, rp);
195
196  /* Save encrypted protocol number */
197  nproto = htons(*proto);
198  RC4(&mop->rc4key, 2, (char *)&nproto, rp + 2);
199
200  /* Encrypt main packet */
201  rp = MBUF_CTOP(mp);
202  RC4(&mop->rc4key, ilen, rp, rp);
203
204  mop->cohnum++;
205  mop->cohnum &= ~MPPE_HEADER_BITMASK;
206
207  /* Set the protocol number */
208  *proto = ccp_Proto(ccp);
209  len = m_length(mo);
210  ccp->uncompout += ilen;
211  ccp->compout += len;
212
213  log_Printf(LogDEBUG, "MPPE: Output: Encrypted: Proto %02x (%d bytes)\n",
214             *proto, len);
215
216  return mo;
217}
218
219static void
220MPPEResetInput(void *v)
221{
222  log_Printf(LogCCP, "MPPE: Unexpected input channel ack\n");
223}
224
225static struct mbuf *
226MPPEInput(void *v, struct ccp *ccp, u_short *proto, struct mbuf *mp)
227{
228  struct mppe_state *mip = (struct mppe_state *)v;
229  u_short prefix;
230  char *rp;
231  int dictinit, flushed, ilen, len, n;
232
233  ilen = m_length(mp);
234  dictinit = 0;
235  ccp->compin += ilen;
236
237  log_Printf(LogDEBUG, "MPPE: Input: Proto %02x (%d bytes)\n", *proto, ilen);
238  log_DumpBp(LogDEBUG, "MPPE: Input: Packet:", mp);
239
240  mp = mbuf_Read(mp, &prefix, 2);
241  prefix = ntohs(prefix);
242  flushed = prefix & MPPE_FLUSHED;
243  prefix &= ~flushed;
244  if ((prefix & MPPE_HEADER_BITMASK) != MPPE_ENCRYPTED) {
245    log_Printf(LogERROR, "MPPE: Input: Invalid packet (flags = 0x%x)\n",
246               (prefix & MPPE_HEADER_BITMASK) | flushed);
247    m_freem(mp);
248    return NULL;
249  }
250
251  prefix &= ~MPPE_HEADER_BITMASK;
252
253  if (!flushed && mip->stateless) {
254    log_Printf(LogCCP, "MPPEInput: Packet without MPPE_FLUSHED set"
255               " in stateless mode\n");
256    flushed = MPPE_FLUSHED;
257    /* Should we really continue ? */
258  }
259
260  if (mip->stateless) {
261    /* Change our key for each missed packet in stateless mode */
262    while (prefix != mip->cohnum) {
263      log_Printf(LogDEBUG, "MPPEInput: Key changed [%u]\n", prefix);
264      MPPEKeyChange(mip);
265      /*
266       * mip->cohnum contains what we received last time in stateless
267       * mode.
268       */
269      mip->cohnum++;
270      mip->cohnum &= ~MPPE_HEADER_BITMASK;
271    }
272    dictinit = 1;
273  } else {
274    if (flushed) {
275      /*
276       * We can always process a flushed packet.
277       * Catch up on any outstanding key changes.
278       */
279      n = (prefix >> MPPE_HEADER_FLAGSHIFT) -
280          (mip->cohnum >> MPPE_HEADER_FLAGSHIFT);
281      if (n < 0)
282        n += MPPE_HEADER_STATEFUL_KEYCHANGES;
283      while (n--) {
284        log_Printf(LogDEBUG, "MPPEInput: Key changed during catchup [%u]\n",
285                   prefix);
286        MPPEKeyChange(mip);
287      }
288      mip->flushrequired = 0;
289      mip->cohnum = prefix;
290      dictinit = 1;
291    }
292
293    if (mip->flushrequired) {
294      /*
295       * Perhaps we should be lenient if
296       * (prefix & MPPE_HEADER_FLAGMASK) == MPPE_HEADER_FLAG
297       * The spec says that we shouldn't be though....
298       */
299      log_Printf(LogDEBUG, "MPPE: Not flushed - discarded\n");
300      fsm_Output(&ccp->fsm, CODE_RESETREQ, ccp->fsm.reqid++, NULL, 0,
301                 MB_CCPOUT);
302      m_freem(mp);
303      return NULL;
304    }
305
306    if (prefix != mip->cohnum) {
307      /*
308       * We're in stateful mode and didn't receive the expected
309       * packet.  Send a reset request, but don't tell the CCP layer
310       * about it as we don't expect to receive a Reset ACK !
311       * Guess what... M$ invented this !
312       */
313      log_Printf(LogCCP, "MPPE: Input: Got seq %u, not %u\n",
314                 prefix, mip->cohnum);
315      fsm_Output(&ccp->fsm, CODE_RESETREQ, ccp->fsm.reqid++, NULL, 0,
316                 MB_CCPOUT);
317      mip->flushrequired = 1;
318      m_freem(mp);
319      return NULL;
320    }
321
322    if ((prefix & MPPE_HEADER_FLAGMASK) == MPPE_HEADER_FLAG) {
323      log_Printf(LogDEBUG, "MPPEInput: Key changed [%u]\n", prefix);
324      MPPEKeyChange(mip);
325      dictinit = 1;
326    } else if (flushed)
327      dictinit = 1;
328
329    /*
330     * mip->cohnum contains what we expect to receive next time in stateful
331     * mode.
332     */
333    mip->cohnum++;
334    mip->cohnum &= ~MPPE_HEADER_BITMASK;
335  }
336
337  if (dictinit) {
338    log_Printf(LogDEBUG, "MPPEInput: Dictionary initialised [%u]\n", prefix);
339    RC4_set_key(&mip->rc4key, mip->keylen, mip->sesskey);
340  }
341
342  mp = mbuf_Read(mp, proto, 2);
343  RC4(&mip->rc4key, 2, (char *)proto, (char *)proto);
344  *proto = ntohs(*proto);
345
346  rp = MBUF_CTOP(mp);
347  len = m_length(mp);
348  RC4(&mip->rc4key, len, rp, rp);
349
350  log_Printf(LogDEBUG, "MPPEInput: Decrypted: Proto %02x (%d bytes)\n",
351             *proto, len);
352  log_DumpBp(LogDEBUG, "MPPEInput: Decrypted: Packet:", mp);
353
354  ccp->uncompin += len;
355
356  return mp;
357}
358
359static void
360MPPEDictSetup(void *v, struct ccp *ccp, u_short proto, struct mbuf *mi)
361{
362}
363
364static const char *
365MPPEDispOpts(struct lcp_opt *o)
366{
367  static char buf[70];
368  u_int32_t val;
369  char ch;
370  int len;
371
372  ua_ntohl(o->data, &val);
373  snprintf(buf, sizeof buf, "value 0x%08x ", (unsigned)val);
374  len = strlen(buf);
375  if (!(val & MPPE_OPT_BITMASK)) {
376    snprintf(buf + len, sizeof buf - len, "(0");
377    len++;
378  } else {
379    ch = '(';
380    if (val & MPPE_OPT_128BIT) {
381      snprintf(buf + len, sizeof buf - len, "%c128", ch);
382      len += strlen(buf + len);
383      ch = '/';
384    }
385    if (val & MPPE_OPT_56BIT) {
386      snprintf(buf + len, sizeof buf - len, "%c56", ch);
387      len += strlen(buf + len);
388      ch = '/';
389    }
390    if (val & MPPE_OPT_40BIT) {
391      snprintf(buf + len, sizeof buf - len, "%c40", ch);
392      len += strlen(buf + len);
393      ch = '/';
394    }
395  }
396
397  snprintf(buf + len, sizeof buf - len, " bits, state%s",
398           (val & MPPE_OPT_STATELESS) ? "less" : "ful");
399  len += strlen(buf + len);
400
401  if (val & MPPE_OPT_COMPRESSED) {
402    snprintf(buf + len, sizeof buf - len, ", compressed");
403    len += strlen(buf + len);
404  }
405
406  snprintf(buf + len, sizeof buf - len, ")");
407
408  return buf;
409}
410
411static int
412MPPEUsable(struct fsm *fp)
413{
414  struct lcp *lcp;
415  int ok;
416
417  lcp = &fp->link->lcp;
418  ok = (lcp->want_auth == PROTO_CHAP && lcp->want_authtype == 0x81) ||
419       (lcp->his_auth == PROTO_CHAP && lcp->his_authtype == 0x81);
420  if (!ok)
421    log_Printf(LogCCP, "MPPE: Not usable without CHAP81\n");
422
423  return ok;
424}
425
426static int
427MPPERequired(struct fsm *fp)
428{
429  return fp->link->ccp.cfg.mppe.required;
430}
431
432static u_int32_t
433MPPE_ConfigVal(const struct ccp_config *cfg)
434{
435  u_int32_t val;
436
437  val = cfg->mppe.state == MPPE_STATELESS ? MPPE_OPT_STATELESS : 0;
438  switch(cfg->mppe.keybits) {
439  case 128:
440    val |= MPPE_OPT_128BIT;
441    break;
442  case 56:
443    val |= MPPE_OPT_56BIT;
444    break;
445  case 40:
446    val |= MPPE_OPT_40BIT;
447    break;
448  case 0:
449    val |= MPPE_OPT_128BIT | MPPE_OPT_56BIT | MPPE_OPT_40BIT;
450    break;
451  }
452
453  return val;
454}
455
456/*
457 * What options should we use for our first configure request
458 */
459static void
460MPPEInitOptsOutput(struct lcp_opt *o, const struct ccp_config *cfg)
461{
462  u_int32_t mval;
463
464  o->len = 6;
465
466  if (!MPPE_MasterKeyValid) {
467    log_Printf(LogCCP, "MPPE: MasterKey is invalid,"
468               " MPPE is available only with CHAP81 authentication\n");
469    ua_htonl(0x0, o->data);
470    return;
471  }
472
473  mval = MPPE_ConfigVal(cfg);
474  ua_htonl(&mval, o->data);
475}
476
477/*
478 * Our CCP request was NAK'd with the given options
479 */
480static int
481MPPESetOptsOutput(struct lcp_opt *o, const struct ccp_config *cfg)
482{
483  u_int32_t mval, peer;
484
485  ua_ntohl(o->data, &peer);
486
487  if (!MPPE_MasterKeyValid)
488    /* Treat their NAK as a REJ */
489    return MODE_NAK;
490
491  mval = MPPE_ConfigVal(cfg);
492
493  /*
494   * If we haven't been configured with a specific number of keybits, allow
495   * whatever the peer asks for.
496   */
497  if (!cfg->mppe.keybits) {
498    mval &= ~MPPE_OPT_BITMASK;
499    mval |= (peer & MPPE_OPT_BITMASK);
500    if (!(mval & MPPE_OPT_BITMASK))
501      mval |= MPPE_OPT_128BIT;
502  }
503
504  /* Adjust our statelessness */
505  if (cfg->mppe.state == MPPE_ANYSTATE) {
506    mval &= ~MPPE_OPT_STATELESS;
507    mval |= (peer & MPPE_OPT_STATELESS);
508  }
509
510  ua_htonl(&mval, o->data);
511
512  return MODE_ACK;
513}
514
515/*
516 * The peer has requested the given options
517 */
518static int
519MPPESetOptsInput(struct lcp_opt *o, const struct ccp_config *cfg)
520{
521  u_int32_t mval, peer;
522  int res = MODE_ACK;
523
524  ua_ntohl(o->data, &peer);
525  if (!MPPE_MasterKeyValid) {
526    if (peer != 0) {
527      peer = 0;
528      ua_htonl(&peer, o->data);
529      return MODE_NAK;
530    } else
531      return MODE_ACK;
532  }
533
534  mval = MPPE_ConfigVal(cfg);
535
536  if (peer & ~MPPE_OPT_MASK)
537    /* He's asking for bits we don't know about */
538    res = MODE_NAK;
539
540  if (peer & MPPE_OPT_STATELESS) {
541    if (cfg->mppe.state == MPPE_STATEFUL)
542      /* Peer can't have stateless */
543      res = MODE_NAK;
544    else
545      /* Peer wants stateless, that's ok */
546      mval |= MPPE_OPT_STATELESS;
547  } else {
548    if (cfg->mppe.state == MPPE_STATELESS)
549      /* Peer must have stateless */
550      res = MODE_NAK;
551    else
552      /* Peer doesn't want stateless, that's ok */
553      mval &= ~MPPE_OPT_STATELESS;
554  }
555
556  /* If we've got a configured number of keybits - the peer must use that */
557  if (cfg->mppe.keybits) {
558    ua_htonl(&mval, o->data);
559    return peer == mval ? res : MODE_NAK;
560  }
561
562  /* If a specific number of bits hasn't been requested, we'll need to NAK */
563  switch (peer & MPPE_OPT_BITMASK) {
564  case MPPE_OPT_128BIT:
565  case MPPE_OPT_56BIT:
566  case MPPE_OPT_40BIT:
567    break;
568  default:
569    res = MODE_NAK;
570  }
571
572  /* Suggest the best number of bits */
573  mval &= ~MPPE_OPT_BITMASK;
574  if (peer & MPPE_OPT_128BIT)
575    mval |= MPPE_OPT_128BIT;
576  else if (peer & MPPE_OPT_56BIT)
577    mval |= MPPE_OPT_56BIT;
578  else if (peer & MPPE_OPT_40BIT)
579    mval |= MPPE_OPT_40BIT;
580  else
581    mval |= MPPE_OPT_128BIT;
582  ua_htonl(&mval, o->data);
583
584  return res;
585}
586
587static struct mppe_state *
588MPPE_InitState(struct lcp_opt *o)
589{
590  struct mppe_state *mp;
591  u_int32_t val;
592
593  if ((mp = calloc(1, sizeof *mp)) != NULL) {
594    ua_ntohl(o->data, &val);
595
596    switch (val & MPPE_OPT_BITMASK) {
597    case MPPE_OPT_128BIT:
598      mp->keylen = 16;
599      mp->keybits = 128;
600      break;
601    case MPPE_OPT_56BIT:
602      mp->keylen = 8;
603      mp->keybits = 56;
604      break;
605    case MPPE_OPT_40BIT:
606      mp->keylen = 8;
607      mp->keybits = 40;
608      break;
609    default:
610      log_Printf(LogWARN, "Unexpected MPPE options 0x%08x\n", val);
611      free(mp);
612      return NULL;
613    }
614
615    mp->stateless = !!(val & MPPE_OPT_STATELESS);
616  }
617
618  return mp;
619}
620
621static void *
622MPPEInitInput(struct lcp_opt *o)
623{
624  struct mppe_state *mip;
625
626  if (!MPPE_MasterKeyValid) {
627    log_Printf(LogWARN, "MPPE: Cannot initialise without CHAP81\n");
628    return NULL;
629  }
630
631  if ((mip = MPPE_InitState(o)) == NULL) {
632    log_Printf(LogWARN, "MPPEInput: Cannot initialise - unexpected options\n");
633    return NULL;
634  }
635
636  log_Printf(LogDEBUG, "MPPE: InitInput: %d-bits\n", mip->keybits);
637
638  GetAsymetricStartKey(MPPE_MasterKey, mip->mastkey, mip->keylen, 0,
639                       MPPE_IsServer);
640  GetNewKeyFromSHA(mip->mastkey, mip->mastkey, mip->keylen, mip->sesskey);
641
642  MPPEReduceSessionKey(mip);
643
644  log_Printf(LogCCP, "MPPE: Input channel initiated\n");
645
646  if (!mip->stateless) {
647    /*
648     * We need to initialise our dictionary here as the first packet we
649     * receive is unlikely to have the FLUSHED bit set.
650     */
651    log_Printf(LogDEBUG, "MPPEInitInput: Dictionary initialised [%d]\n",
652               mip->cohnum);
653    RC4_set_key(&mip->rc4key, mip->keylen, mip->sesskey);
654  } else {
655    /*
656     * We do the first key change here as the first packet is expected
657     * to have a sequence number of 0 and we'll therefore not expect
658     * to have to change the key at that point.
659     */
660    log_Printf(LogDEBUG, "MPPEInitInput: Key changed [%d]\n", mip->cohnum);
661    MPPEKeyChange(mip);
662  }
663
664  return mip;
665}
666
667static void *
668MPPEInitOutput(struct lcp_opt *o)
669{
670  struct mppe_state *mop;
671
672  if (!MPPE_MasterKeyValid) {
673    log_Printf(LogWARN, "MPPE: Cannot initialise without CHAP81\n");
674    return NULL;
675  }
676
677  if ((mop = MPPE_InitState(o)) == NULL) {
678    log_Printf(LogWARN, "MPPEOutput: Cannot initialise - unexpected options\n");
679    return NULL;
680  }
681
682  log_Printf(LogDEBUG, "MPPE: InitOutput: %d-bits\n", mop->keybits);
683
684  GetAsymetricStartKey(MPPE_MasterKey, mop->mastkey, mop->keylen, 1,
685                       MPPE_IsServer);
686  GetNewKeyFromSHA(mop->mastkey, mop->mastkey, mop->keylen, mop->sesskey);
687
688  MPPEReduceSessionKey(mop);
689
690  log_Printf(LogCCP, "MPPE: Output channel initiated\n");
691
692  if (!mop->stateless) {
693    /*
694     * We need to initialise our dictionary now as the first packet we
695     * send won't have the FLUSHED bit set.
696     */
697    log_Printf(LogDEBUG, "MPPEInitOutput: Dictionary initialised [%d]\n",
698               mop->cohnum);
699    RC4_set_key(&mop->rc4key, mop->keylen, mop->sesskey);
700  }
701
702  return mop;
703}
704
705static void
706MPPETermInput(void *v)
707{
708  free(v);
709}
710
711static void
712MPPETermOutput(void *v)
713{
714  free(v);
715}
716
717const struct ccp_algorithm MPPEAlgorithm = {
718  TY_MPPE,
719  CCP_NEG_MPPE,
720  MPPEDispOpts,
721  MPPEUsable,
722  MPPERequired,
723  {
724    MPPESetOptsInput,
725    MPPEInitInput,
726    MPPETermInput,
727    MPPEResetInput,
728    MPPEInput,
729    MPPEDictSetup
730  },
731  {
732    2,
733    MPPEInitOptsOutput,
734    MPPESetOptsOutput,
735    MPPEInitOutput,
736    MPPETermOutput,
737    MPPEResetOutput,
738    MPPEOutput
739  },
740};
741