/*
* ccp.c - PPP Compression Control Protocol.
*
- * Copyright (c) 1994 The Australian National University.
- * All rights reserved.
+ * Copyright (c) 1994-2002 Paul Mackerras. All rights reserved.
*
- * Permission to use, copy, modify, and distribute this software and its
- * documentation is hereby granted, provided that the above copyright
- * notice appears in all copies. This software is provided without any
- * warranty, express or implied. The Australian National University
- * makes no representations about the suitability of this software for
- * any purpose.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
*
- * IN NO EVENT SHALL THE AUSTRALIAN NATIONAL UNIVERSITY BE LIABLE TO ANY
- * PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
- * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
- * THE AUSTRALIAN NATIONAL UNIVERSITY HAVE BEEN ADVISED OF THE POSSIBILITY
- * OF SUCH DAMAGE.
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
*
- * THE AUSTRALIAN NATIONAL UNIVERSITY SPECIFICALLY DISCLAIMS ANY WARRANTIES,
- * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
- * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
- * ON AN "AS IS" BASIS, AND THE AUSTRALIAN NATIONAL UNIVERSITY HAS NO
- * OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS,
- * OR MODIFICATIONS.
+ * 2. The name(s) of the authors of this software must not be used to
+ * endorse or promote products derived from this software without
+ * prior written permission.
+ *
+ * 3. Redistributions of any form whatsoever must retain the following
+ * acknowledgment:
+ * "This product includes software developed by Paul Mackerras
+ * <paulus@samba.org>".
+ *
+ * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO
+ * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
-#define RCSID "$Id: ccp.c,v 1.36 2002/05/28 17:00:57 dfs Exp $"
+#define RCSID "$Id: ccp.c,v 1.48 2004/11/13 02:28:15 paulus Exp $"
#include <stdlib.h>
#include <string.h>
#include <net/ppp-comp.h>
#ifdef MPPE
-#include "chap_ms.h" /* mppe_xxxx_key */
-#include "lcp.h" /* lcp_close() */
+#include "chap_ms.h" /* mppe_xxxx_key, mppe_keys_set */
+#include "lcp.h" /* lcp_close(), lcp_fsm */
#endif
static const char rcsid[] = RCSID;
static int ccp_cilen __P((fsm *));
static void ccp_addci __P((fsm *, u_char *, int *));
static int ccp_ackci __P((fsm *, u_char *, int));
-static int ccp_nakci __P((fsm *, u_char *, int));
+static int ccp_nakci __P((fsm *, u_char *, int, int));
static int ccp_rejci __P((fsm *, u_char *, int));
static int ccp_reqci __P((fsm *, u_char *, int *, int));
static void ccp_up __P((fsm *));
fsm_lowerdown(&ccp_fsm[unit]);
#ifdef MPPE
- if (ccp_gotoptions[unit].mppe)
+ if (ccp_gotoptions[unit].mppe) {
error("MPPE required but peer negotiation failed");
lcp_close(unit, "MPPE required but peer negotiation failed");
+ }
#endif
}
return;
}
+ /* A plugin (eg radius) may not have obtained key material. */
+ if (!mppe_keys_set) {
+ error("MPPE required, but keys are not available. "
+ "Possible plugin problem?");
+ lcp_close(f->unit, "MPPE required but not available");
+ return;
+ }
+
/* LM auth not supported for MPPE */
if (auth_done[f->unit] & (CHAP_MS_WITHPEER | CHAP_MS_PEER)) {
/* This might be noise */
/*
* Add the compression types that we can receive, in decreasing
- * preference order.
+ * preference order. Get the kernel to allocate the first one
+ * in case it gets Acked.
*/
#ifdef MPPE
if (go->mppe) {
p[1] = CILEN_DEFLATE;
p[2] = DEFLATE_MAKE_OPT(go->deflate_size);
p[3] = DEFLATE_CHK_SEQUENCE;
- for (;;) {
- if (go->deflate_size < DEFLATE_MIN_WORKS) {
- go->deflate = 0;
- break;
- }
- res = ccp_test(f->unit, p, CILEN_DEFLATE, 0);
- if (res > 0) {
- p += CILEN_DEFLATE;
- break;
- } else if (res < 0) {
- go->deflate = 0;
- break;
+ if (p != p0) {
+ p += CILEN_DEFLATE;
+ } else {
+ for (;;) {
+ if (go->deflate_size < DEFLATE_MIN_WORKS) {
+ go->deflate = 0;
+ break;
+ }
+ res = ccp_test(f->unit, p, CILEN_DEFLATE, 0);
+ if (res > 0) {
+ p += CILEN_DEFLATE;
+ break;
+ } else if (res < 0) {
+ go->deflate = 0;
+ break;
+ }
+ --go->deflate_size;
+ p[2] = DEFLATE_MAKE_OPT(go->deflate_size);
}
- --go->deflate_size;
- p[2] = DEFLATE_MAKE_OPT(go->deflate_size);
}
if (p != p0 && go->deflate_correct && go->deflate_draft) {
p[0] = CI_DEFLATE_DRAFT;
p[0] = CI_BSD_COMPRESS;
p[1] = CILEN_BSD_COMPRESS;
p[2] = BSD_MAKE_OPT(BSD_CURRENT_VERSION, go->bsd_bits);
- for (;;) {
- if (go->bsd_bits < BSD_MIN_BITS) {
- go->bsd_compress = 0;
- break;
- }
- res = ccp_test(f->unit, p, CILEN_BSD_COMPRESS, 0);
- if (res > 0) {
- p += CILEN_BSD_COMPRESS;
- break;
- } else if (res < 0) {
- go->bsd_compress = 0;
- break;
+ if (p != p0) {
+ p += CILEN_BSD_COMPRESS; /* not the first option */
+ } else {
+ for (;;) {
+ if (go->bsd_bits < BSD_MIN_BITS) {
+ go->bsd_compress = 0;
+ break;
+ }
+ res = ccp_test(f->unit, p, CILEN_BSD_COMPRESS, 0);
+ if (res > 0) {
+ p += CILEN_BSD_COMPRESS;
+ break;
+ } else if (res < 0) {
+ go->bsd_compress = 0;
+ break;
+ }
+ --go->bsd_bits;
+ p[2] = BSD_MAKE_OPT(BSD_CURRENT_VERSION, go->bsd_bits);
}
- --go->bsd_bits;
- p[2] = BSD_MAKE_OPT(BSD_CURRENT_VERSION, go->bsd_bits);
}
}
/* XXX Should Predictor 2 be preferable to Predictor 1? */
* Returns 1 iff the nak was OK.
*/
static int
-ccp_nakci(f, p, len)
+ccp_nakci(f, p, len, treat_as_reject)
fsm *f;
u_char *p;
int len;
* Fail if we aren't willing to use his suggestion.
*/
MPPE_CI_TO_OPTS(&p[2], try.mppe);
- if ((try.mppe & MPPE_OPT_STATEFUL) && refuse_mppe_stateful)
+ if ((try.mppe & MPPE_OPT_STATEFUL) && refuse_mppe_stateful) {
+ error("Refusing MPPE stateful mode offered by peer");
try.mppe = 0;
- else if ((go->mppe & try.mppe) != try.mppe)
+ } else if (((go->mppe | MPPE_OPT_STATEFUL) & try.mppe) != try.mppe) {
/* Peer must have set options we didn't request (suggest) */
try.mppe = 0;
+ }
if (!try.mppe) {
error("MPPE required but peer negotiation failed");
len -= CILEN_MPPE;
}
#endif
- if (go->deflate && len >= CILEN_DEFLATE
- && p[0] == (go->deflate_correct? CI_DEFLATE: CI_DEFLATE_DRAFT)
- && p[1] == CILEN_DEFLATE) {
+ if (go->deflate_correct && len >= CILEN_DEFLATE
+ && p[0] == CI_DEFLATE && p[1] == CILEN_DEFLATE) {
if (p[2] != DEFLATE_MAKE_OPT(go->deflate_size)
|| p[3] != DEFLATE_CHK_SEQUENCE)
return 0; /* Rej is bad */
- if (go->deflate_correct)
- try.deflate_correct = 0;
- else
- try.deflate_draft = 0;
+ try.deflate_correct = 0;
+ p += CILEN_DEFLATE;
+ len -= CILEN_DEFLATE;
+ }
+ if (go->deflate_draft && len >= CILEN_DEFLATE
+ && p[0] == CI_DEFLATE_DRAFT && p[1] == CILEN_DEFLATE) {
+ if (p[2] != DEFLATE_MAKE_OPT(go->deflate_size)
+ || p[3] != DEFLATE_CHK_SEQUENCE)
+ return 0; /* Rej is bad */
+ try.deflate_draft = 0;
p += CILEN_DEFLATE;
len -= CILEN_DEFLATE;
- if (go->deflate_correct && go->deflate_draft
- && len >= CILEN_DEFLATE && p[0] == CI_DEFLATE_DRAFT
- && p[1] == CILEN_DEFLATE) {
- if (p[2] != DEFLATE_MAKE_OPT(go->deflate_size)
- || p[3] != DEFLATE_CHK_SEQUENCE)
- return 0; /* Rej is bad */
- try.deflate_draft = 0;
- p += CILEN_DEFLATE;
- len -= CILEN_DEFLATE;
- }
- if (!try.deflate_correct && !try.deflate_draft)
- try.deflate = 0;
}
+ if (!try.deflate_correct && !try.deflate_draft)
+ try.deflate = 0;
if (go->bsd_compress && len >= CILEN_BSD_COMPRESS
&& p[0] == CI_BSD_COMPRESS && p[1] == CILEN_BSD_COMPRESS) {
if (p[2] != BSD_MAKE_OPT(BSD_CURRENT_VERSION, go->bsd_bits))
ccp_options *ho = &ccp_hisoptions[f->unit];
ccp_options *ao = &ccp_allowoptions[f->unit];
#ifdef MPPE
- bool seen_ci_mppe = 0;
+ bool rej_for_ci_mppe = 1; /* Are we rejecting based on a bad/missing */
+ /* CI_MPPE, or due to other options? */
#endif
ret = CONFACK;
newret = CONFREJ;
break;
}
- seen_ci_mppe = 1;
MPPE_CI_TO_OPTS(&p[2], ho->mppe);
/* Nak if anything unsupported or unknown are set. */
/* Check state opt */
if (ho->mppe & MPPE_OPT_STATEFUL) {
+ /*
+ * We can Nak and request stateless, but it's a
+ * lot easier to just assume the peer will request
+ * it if he can do it; stateful mode is bad over
+ * the Internet -- which is where we expect MPPE.
+ */
if (refuse_mppe_stateful) {
- /*
- * We can Nak and request stateless, but it's a
- * lot easier to just assume the peer will request
- * it if he can do it; stateful mode is bad over
- * the Internet -- which is where we expect MPPE.
- */
+ error("Refusing MPPE stateful mode offered by peer");
newret = CONFREJ;
break;
- } else {
- newret = CONFNAK;
}
}
newret = CONFREJ;
}
+ /*
+ * We have accepted MPPE or are willing to negotiate
+ * MPPE parameters. A CONFREJ is due to subsequent
+ * (non-MPPE) processing.
+ */
+ rej_for_ci_mppe = 0;
break;
#endif /* MPPE */
case CI_DEFLATE:
*lenp = retp - p0;
}
#ifdef MPPE
- if (ret == CONFREJ && ao->mppe && !seen_ci_mppe) {
+ if (ret == CONFREJ && ao->mppe && rej_for_ci_mppe) {
error("MPPE required but peer negotiation failed");
lcp_close(f->unit, "MPPE required but peer negotiation failed");
}
if (go->mppe) {
BZERO(mppe_recv_key, MPPE_MAX_KEY_LEN);
BZERO(mppe_send_key, MPPE_MAX_KEY_LEN);
- start_networks(f->unit); /* Bring up IP et al */
+ continue_networks(f->unit); /* Bring up IP et al */
}
#endif
}
ccp_flags_set(f->unit, 1, 0);
#ifdef MPPE
if (ccp_gotoptions[f->unit].mppe) {
- error("MPPE disabled");
- lcp_close(f->unit, "MPPE disabled");
+ ccp_gotoptions[f->unit].mppe = 0;
+ if (lcp_fsm[f->unit].state == OPENED) {
+ /* If LCP is not already going down, make sure it does. */
+ error("MPPE disabled");
+ lcp_close(f->unit, "MPPE disabled");
+ }
}
#endif
}