an advantage.) It does introduce a significant weakness if the LM hash
is used. Additionally, the format of the failure packet potentially
gives information to an attacker. The weakness of the LM hash is partly
-address in RFC 2433, which deprecates its use.
+addressed in RFC 2433, which deprecates its use.
MS-CHAPv2 adds 2 benefits to MS-CHAP. (1) The LM hash is no longer
used. (2) Mutual authentication is required. Note that the mutual
* OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS,
* OR MODIFICATIONS.
*
- * $Id: ppp-comp.h,v 1.8 1999/07/23 06:53:40 paulus Exp $
+ * $Id: ppp-comp.h,v 1.9 2002/04/02 13:54:59 dfs Exp $
*/
/*
- * ==FILEVERSION 980319==
+ * ==FILEVERSION 20020319==
*
* NOTE TO MAINTAINERS:
* If you modify this file at all, please set the above date.
+ DEFLATE_METHOD_VAL)
#define DEFLATE_CHK_SEQUENCE 0
+/*
+ * Definitions for MPPE.
+ */
+
+#define CI_MPPE 18 /* config option for MPPE */
+#define CILEN_MPPE 6 /* length of config option */
+
+#define MPPE_PAD 4 /* MPPE growth per frame */
+#define MPPE_MAX_KEY_LEN 16 /* largest key length (128-bit) */
+
+/* option bits for ccp_options.mppe */
+#define MPPE_OPT_40 0x01 /* 40 bit */
+#define MPPE_OPT_128 0x02 /* 128 bit */
+#define MPPE_OPT_STATEFUL 0x04 /* stateful mode */
+/* unsupported opts */
+#define MPPE_OPT_56 0x08 /* 56 bit */
+#define MPPE_OPT_MPPC 0x10 /* MPPC compression */
+#define MPPE_OPT_D 0x20 /* Unknown */
+#define MPPE_OPT_UNSUPPORTED (MPPE_OPT_56|MPPE_OPT_MPPC|MPPE_OPT_D)
+#define MPPE_OPT_UNKNOWN 0x40 /* Bits !defined in RFC 3078 were set */
+
+/*
+ * This is not nice ... the alternative is a bitfield struct though.
+ * And unfortunately, we cannot share the same bits for the option
+ * names above since C and H are the same bit. We could do a u_int32
+ * but then we have to do a htonl() all the time and/or we still need
+ * to know which octet is which.
+ */
+#define MPPE_C_BIT 0x01 /* MPPC */
+#define MPPE_D_BIT 0x10 /* Obsolete, usage unknown */
+#define MPPE_L_BIT 0x20 /* 40-bit */
+#define MPPE_S_BIT 0x40 /* 128-bit */
+#define MPPE_M_BIT 0x80 /* 56-bit, not supported */
+#define MPPE_H_BIT 0x01 /* Stateless (in a different byte) */
+
+/* Does not include H bit; used for least significant octet only. */
+#define MPPE_ALL_BITS (MPPE_D_BIT|MPPE_L_BIT|MPPE_S_BIT|MPPE_M_BIT|MPPE_H_BIT)
+
+/* Build a CI from mppe opts (see RFC 3078) */
+#define MPPE_OPTS_TO_CI(opts, ci) \
+ do { \
+ u_char *ptr = ci; /* u_char[4] */ \
+ \
+ /* H bit */ \
+ if (opts & MPPE_OPT_STATEFUL) \
+ *ptr++ = 0x0; \
+ else \
+ *ptr++ = MPPE_H_BIT; \
+ *ptr++ = 0; \
+ *ptr++ = 0; \
+ \
+ /* S,L bits */ \
+ *ptr = 0; \
+ if (opts & MPPE_OPT_128) \
+ *ptr |= MPPE_S_BIT; \
+ if (opts & MPPE_OPT_40) \
+ *ptr |= MPPE_L_BIT; \
+ /* M,D,C bits not supported */ \
+ } while (/* CONSTCOND */ 0)
+
+/* The reverse of the above */
+#define MPPE_CI_TO_OPTS(ci, opts) \
+ do { \
+ u_char *ptr = ci; /* u_char[4] */ \
+ \
+ opts = 0; \
+ \
+ /* H bit */ \
+ if (!(ptr[0] & MPPE_H_BIT)) \
+ opts |= MPPE_OPT_STATEFUL; \
+ \
+ /* S,L bits */ \
+ if (ptr[3] & MPPE_S_BIT) \
+ opts |= MPPE_OPT_128; \
+ if (ptr[3] & MPPE_L_BIT) \
+ opts |= MPPE_OPT_40; \
+ \
+ /* M,D,C bits */ \
+ if (ptr[3] & MPPE_M_BIT) \
+ opts |= MPPE_OPT_56; \
+ if (ptr[3] & MPPE_D_BIT) \
+ opts |= MPPE_OPT_D; \
+ if (ptr[3] & MPPE_C_BIT) \
+ opts |= MPPE_OPT_MPPC; \
+ \
+ /* Other bits */ \
+ if (ptr[0] & ~MPPE_H_BIT) \
+ opts |= MPPE_OPT_UNKNOWN; \
+ if (ptr[1] || ptr[2]) \
+ opts |= MPPE_OPT_UNKNOWN; \
+ if (ptr[3] & ~MPPE_ALL_BITS) \
+ opts |= MPPE_OPT_UNKNOWN; \
+ } while (/* CONSTCOND */ 0)
+
/*
* Definitions for other, as yet unsupported, compression methods.
*/
* OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS,
* OR MODIFICATIONS.
*
- * $Id: ppp-comp.h,v 1.11 1998/03/25 03:33:34 paulus Exp $
+ * $Id: ppp-comp.h,v 1.12 2002/04/02 13:54:59 dfs Exp $
*/
#ifndef _NET_PPP_COMP_H
+ DEFLATE_METHOD_VAL)
#define DEFLATE_CHK_SEQUENCE 0
+/*
+ * Definitions for MPPE.
+ */
+#define CI_MPPE 18 /* config option for MPPE */
+#define CILEN_MPPE 6 /* length of config option */
+
+#define MPPE_PAD 4 /* MPPE growth per frame */
+#define MPPE_MAX_KEY_LEN 16 /* largest key length (128-bit) */
+
+/* option bits for ccp_options.mppe */
+#define MPPE_OPT_40 0x01 /* 40 bit */
+#define MPPE_OPT_128 0x02 /* 128 bit */
+#define MPPE_OPT_STATEFUL 0x04 /* stateful mode */
+/* unsupported opts */
+#define MPPE_OPT_56 0x08 /* 56 bit */
+#define MPPE_OPT_MPPC 0x10 /* MPPC compression */
+#define MPPE_OPT_D 0x20 /* Unknown */
+#define MPPE_OPT_UNSUPPORTED (MPPE_OPT_56|MPPE_OPT_MPPC|MPPE_OPT_D)
+#define MPPE_OPT_UNKNOWN 0x40 /* Bits !defined in RFC 3078 were set */
+
+/*
+ * This is not nice ... the alternative is a bitfield struct though.
+ * And unfortunately, we cannot share the same bits for the option
+ * names above since C and H are the same bit. We could do a u_int32
+ * but then we have to do a htonl() all the time and/or we still need
+ * to know which octet is which.
+ */
+#define MPPE_C_BIT 0x01 /* MPPC */
+#define MPPE_D_BIT 0x10 /* Obsolete, usage unknown */
+#define MPPE_L_BIT 0x20 /* 40-bit */
+#define MPPE_S_BIT 0x40 /* 128-bit */
+#define MPPE_M_BIT 0x80 /* 56-bit, not supported */
+#define MPPE_H_BIT 0x01 /* Stateless (in a different byte) */
+
+/* Does not include H bit; used for least significant octet only. */
+#define MPPE_ALL_BITS (MPPE_D_BIT|MPPE_L_BIT|MPPE_S_BIT|MPPE_M_BIT|MPPE_H_BIT)
+
+/* Build a CI from mppe opts (see RFC 3078) */
+#define MPPE_OPTS_TO_CI(opts, ci) \
+ do { \
+ u_char *ptr = ci; /* u_char[4] */ \
+ \
+ /* H bit */ \
+ if (opts & MPPE_OPT_STATEFUL) \
+ *ptr++ = 0x0; \
+ else \
+ *ptr++ = MPPE_H_BIT; \
+ *ptr++ = 0; \
+ *ptr++ = 0; \
+ \
+ /* S,L bits */ \
+ *ptr = 0; \
+ if (opts & MPPE_OPT_128) \
+ *ptr |= MPPE_S_BIT; \
+ if (opts & MPPE_OPT_40) \
+ *ptr |= MPPE_L_BIT; \
+ /* M,D,C bits not supported */ \
+ } while (/* CONSTCOND */ 0)
+
+/* The reverse of the above */
+#define MPPE_CI_TO_OPTS(ci, opts) \
+ do { \
+ u_char *ptr = ci; /* u_char[4] */ \
+ \
+ opts = 0; \
+ \
+ /* H bit */ \
+ if (!(ptr[0] & MPPE_H_BIT)) \
+ opts |= MPPE_OPT_STATEFUL; \
+ \
+ /* S,L bits */ \
+ if (ptr[3] & MPPE_S_BIT) \
+ opts |= MPPE_OPT_128; \
+ if (ptr[3] & MPPE_L_BIT) \
+ opts |= MPPE_OPT_40; \
+ \
+ /* M,D,C bits */ \
+ if (ptr[3] & MPPE_M_BIT) \
+ opts |= MPPE_OPT_56; \
+ if (ptr[3] & MPPE_D_BIT) \
+ opts |= MPPE_OPT_D; \
+ if (ptr[3] & MPPE_C_BIT) \
+ opts |= MPPE_OPT_MPPC; \
+ \
+ /* Other bits */ \
+ if (ptr[0] & ~MPPE_H_BIT) \
+ opts |= MPPE_OPT_UNKNOWN; \
+ if (ptr[1] || ptr[2]) \
+ opts |= MPPE_OPT_UNKNOWN; \
+ if (ptr[3] & ~MPPE_ALL_BITS) \
+ opts |= MPPE_OPT_UNKNOWN; \
+ } while (/* CONSTCOND */ 0)
+
/*
* Definitions for other, as yet unsupported, compression methods.
*/
#
# pppd makefile for Linux
-# $Id: Makefile.linux,v 1.46 2002/03/05 15:14:04 dfs Exp $
+# $Id: Makefile.linux,v 1.47 2002/04/02 13:54:59 dfs Exp $
#
# Default installation locations
endif
# Uncomment the next 2 lines to include support for Microsoft's
-# MS-CHAP authentication protocol. Also, edit plugins/radius/Makefile.
+# MS-CHAP authentication protocol. Also, edit plugins/radius/Makefile.linux.
CHAPMS=y
USE_CRYPT=y
+# Uncomment the next line to include support for MPPE. CHAPMS (above) must
+# also be enabled. Also, edit plugins/radius/Makefile.linux.
+MPPE=y
ifneq ($(wildcard /usr/lib/libcrypt.*),)
HAVE_CRYPT_H=y
endif
ifdef MSLANMAN
CFLAGS += -DMSLANMAN=1
endif
+ifdef MPPE
+CFLAGS += -DMPPE=1
+endif
endif
ifdef HAS_SHADOW
* WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*/
-#define RCSID "$Id: auth.c,v 1.75 2002/03/05 15:14:04 dfs Exp $"
+#define RCSID "$Id: auth.c,v 1.76 2002/04/02 13:54:59 dfs Exp $"
#include <stdio.h>
#include <stddef.h>
/* Records which authentication operations haven't completed yet. */
static int auth_pending[NUM_PPP];
+/* Records which authentication operations have been completed. */
+int auth_done[NUM_PPP];
+
/* Set if we have successfully called plogin() */
static int logged_in;
static char *uafname; /* name of most recent +ua file */
-/* Bits in auth_pending[] */
-#define PAP_WITHPEER 1
-#define PAP_PEER 2
-#define CHAP_WITHPEER 4
-#define CHAP_PEER 8
-
extern char *crypt __P((const char *, const char *));
/* Prototypes for procedures local to this file. */
auth |= PAP_WITHPEER;
}
auth_pending[unit] = auth;
+ auth_done[unit] = 0;
if (!auth)
network_phase(unit);
* The peer has been successfully authenticated using `protocol'.
*/
void
-auth_peer_success(unit, protocol, name, namelen)
- int unit, protocol;
+auth_peer_success(unit, protocol, prot_flavor, name, namelen)
+ int unit, protocol, prot_flavor;
char *name;
int namelen;
{
switch (protocol) {
case PPP_CHAP:
bit = CHAP_PEER;
+ switch (prot_flavor) {
+ case CHAP_DIGEST_MD5:
+ bit |= CHAP_MD5_PEER;
+ break;
+#ifdef CHAPMS
+ case CHAP_MICROSOFT:
+ bit |= CHAP_MS_PEER;
+ break;
+ case CHAP_MICROSOFT_V2:
+ bit |= CHAP_MS2_PEER;
+ break;
+#endif
+ }
break;
case PPP_PAP:
bit = PAP_PEER;
peer_authname[namelen] = 0;
script_setenv("PEERNAME", peer_authname, 0);
+ /* Save the authentication method for later. */
+ auth_done[unit] |= bit;
+
/*
* If there is no more authentication still to be done,
* proceed to the network (or callback) phase.
* We have successfully authenticated ourselves with the peer using `protocol'.
*/
void
-auth_withpeer_success(unit, protocol)
- int unit, protocol;
+auth_withpeer_success(unit, protocol, prot_flavor)
+ int unit, protocol, prot_flavor;
{
int bit;
switch (protocol) {
case PPP_CHAP:
bit = CHAP_WITHPEER;
+ switch (prot_flavor) {
+ case CHAP_DIGEST_MD5:
+ bit |= CHAP_MD5_WITHPEER;
+ break;
+#ifdef CHAPMS
+ case CHAP_MICROSOFT:
+ bit |= CHAP_MS_WITHPEER;
+ break;
+ case CHAP_MICROSOFT_V2:
+ bit |= CHAP_MS2_WITHPEER;
+ break;
+#endif
+ }
break;
case PPP_PAP:
if (passwd_from_file)
bit = 0;
}
+ /* Save the authentication method for later. */
+ auth_done[unit] |= bit;
+
/*
* If there is no more authentication still being done,
* proceed to the network (or callback) phase.
* OR MODIFICATIONS.
*/
-#define RCSID "$Id: ccp.c,v 1.33 2002/03/06 15:00:30 dfs Exp $"
+#define RCSID "$Id: ccp.c,v 1.34 2002/04/02 13:54:59 dfs Exp $"
#include <stdlib.h>
#include <string.h>
#include "ccp.h"
#include <net/ppp-comp.h>
+#ifdef MPPE
+#include "chap_ms.h" /* mppe_xx_key */
+#include "lcp.h" /* lcp_close() */
+#endif
+
static const char rcsid[] = RCSID;
/*
static char bsd_value[8];
static char deflate_value[8];
+/*
+ * Option variables.
+ */
+#ifdef MPPE
+bool refuse_mppe_stateful = 1; /* Allow stateful mode? */
+#endif
+
static option_t ccp_option_list[] = {
{ "noccp", o_bool, &ccp_protent.enabled_flag,
"Disable CCP negotiation" },
"don't allow Predictor-1", OPT_ALIAS | OPT_PRIOSUB | OPT_A2CLR,
&ccp_allowoptions[0].predictor_1 },
+#ifdef MPPE
+ /* MPPE options are symmetrical ... we only set wantoptions here */
+ { "require-mppe", o_bool, &ccp_wantoptions[0].mppe,
+ "require MPPE encryption",
+ OPT_PRIO | MPPE_OPT_40 | MPPE_OPT_128 },
+ { "+mppe", o_bool, &ccp_wantoptions[0].mppe,
+ "require MPPE encryption",
+ OPT_ALIAS | OPT_PRIO | MPPE_OPT_40 | MPPE_OPT_128 },
+ { "nomppe", o_bool, &ccp_wantoptions[0].mppe,
+ "don't allow MPPE encryption", OPT_PRIO },
+ { "-mppe", o_bool, &ccp_wantoptions[0].mppe,
+ "don't allow MPPE encryption", OPT_ALIAS | OPT_PRIO },
+
+ /* We use ccp_allowoptions[0].mppe as a junk var ... it is reset later */
+ { "require-mppe-40", o_bool, &ccp_allowoptions[0].mppe,
+ "require MPPE 40-bit encryption", OPT_PRIO | OPT_A2OR | MPPE_OPT_40,
+ &ccp_wantoptions[0].mppe },
+ { "+mppe-40", o_bool, &ccp_allowoptions[0].mppe,
+ "require MPPE 40-bit encryption", OPT_PRIO | OPT_A2OR | MPPE_OPT_40,
+ &ccp_wantoptions[0].mppe },
+ { "nomppe-40", o_bool, &ccp_allowoptions[0].mppe,
+ "don't allow MPPE 40-bit encryption",
+ OPT_PRIOSUB | OPT_A2CLRB | MPPE_OPT_40, &ccp_wantoptions[0].mppe },
+ { "-mppe-40", o_bool, &ccp_allowoptions[0].mppe,
+ "don't allow MPPE 40-bit encryption",
+ OPT_ALIAS | OPT_PRIOSUB | OPT_A2CLRB | MPPE_OPT_40,
+ &ccp_wantoptions[0].mppe },
+
+ { "require-mppe-128", o_bool, &ccp_allowoptions[0].mppe,
+ "require MPPE 128-bit encryption", OPT_PRIO | OPT_A2OR | MPPE_OPT_128,
+ &ccp_wantoptions[0].mppe },
+ { "+mppe-128", o_bool, &ccp_allowoptions[0].mppe,
+ "require MPPE 128-bit encryption",
+ OPT_ALIAS | OPT_PRIO | OPT_A2OR | MPPE_OPT_128,
+ &ccp_wantoptions[0].mppe },
+ { "nomppe-128", o_bool, &ccp_allowoptions[0].mppe,
+ "don't allow MPPE 128-bit encryption",
+ OPT_PRIOSUB | OPT_A2CLRB | MPPE_OPT_128, &ccp_wantoptions[0].mppe },
+ { "-mppe-128", o_bool, &ccp_allowoptions[0].mppe,
+ "don't allow MPPE 128-bit encryption",
+ OPT_ALIAS | OPT_PRIOSUB | OPT_A2CLRB | MPPE_OPT_128,
+ &ccp_wantoptions[0].mppe },
+
+ /* strange one; we always request stateless, but will we allow stateful? */
+ { "mppe-stateful", o_bool, &refuse_mppe_stateful,
+ "allow MPPE stateful mode", OPT_PRIO },
+ { "nomppe-stateful", o_bool, &refuse_mppe_stateful,
+ "disallow MPPE stateful mode", OPT_PRIO | 1 },
+#endif /* MPPE */
+
{ NULL }
};
* Do we want / did we get any compression?
*/
#define ANY_COMPRESS(opt) ((opt).deflate || (opt).bsd_compress \
- || (opt).predictor_1 || (opt).predictor_2)
+ || (opt).predictor_1 || (opt).predictor_2 \
+ || (opt).mppe)
/*
* Local state (mainly for handling reset-reqs and reset-acks).
*/
oldstate = f->state;
fsm_input(f, p, len);
- if (oldstate == OPENED && p[0] == TERMREQ && f->state != OPENED)
+ if (oldstate == OPENED && p[0] == TERMREQ && f->state != OPENED) {
notice("Compression disabled by peer.");
+#ifdef MPPE
+ if (ccp_gotoptions[unit].mppe) {
+ notice("MPPE disabled, closing LCP");
+ lcp_close(unit, "MPPE disabled by peer");
+ }
+#endif
+ }
/*
* If we get a terminate-ack and we're not asking for compression,
fsm *f;
{
ccp_options *go = &ccp_gotoptions[f->unit];
- u_char opt_buf[16];
+ u_char opt_buf[CCP_MAX_OPTION_LENGTH];
*go = ccp_wantoptions[f->unit];
all_rejected[f->unit] = 0;
+#ifdef MPPE
+ if (go->mppe) {
+ ccp_options *ao = &ccp_allowoptions[f->unit];
+ int auth_mschap_bits = auth_done[f->unit];
+ int numbits;
+
+ /*
+ * Start with a basic sanity check: mschap[v2] auth must be in
+ * exactly one direction. RFC 3079 says that the keys are
+ * 'derived from the credentials of the peer that initiated the call',
+ * however the PPP protocol doesn't have such a concept, and pppd
+ * cannot get this info externally. Instead we do the best we can.
+ * NB: If MPPE is required, all other compression opts are invalid.
+ * So, we return right away if we can't do it.
+ */
+
+ /* Leave only the mschap auth bits set */
+ auth_mschap_bits &= ~(PAP_WITHPEER | PAP_PEER |
+ CHAP_WITHPEER | CHAP_PEER |
+ CHAP_MD5_WITHPEER | CHAP_MD5_PEER);
+ /* Count the mschap auths */
+ numbits = 0;
+ do {
+ numbits += auth_mschap_bits & 1;
+ auth_mschap_bits >>= 1;
+ } while (auth_mschap_bits);
+ if (numbits > 1) {
+ error("MPPE required, but auth done in both directions.");
+ lcp_close(f->unit, "MPPE required but not available");
+ return;
+ }
+ if (!numbits) {
+ error("MPPE required, but MS-CHAP[v2] auth not performed.");
+ 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 */
+ if (go->mppe & MPPE_OPT_40) {
+ notice("Disabling 40-bit MPPE; MS-CHAP LM not supported");
+ go->mppe &= ~MPPE_OPT_40;
+ }
+ }
+
+ /* Last check: can we actually negotiate something? */
+ if (!(go->mppe & (MPPE_OPT_40 | MPPE_OPT_128))) {
+ /* Could be misconfig, could be 40-bit disabled above. */
+ error("MPPE required, but both 40-bit and 128-bit disabled.");
+ lcp_close(f->unit, "MPPE required but not available");
+ return;
+ }
+
+ /* sync options */
+ ao->mppe = go->mppe;
+ /* MPPE is not compatible with other compression types */
+ ao->bsd_compress = go->bsd_compress = 0;
+ ao->predictor_1 = go->predictor_1 = 0;
+ ao->predictor_2 = go->predictor_2 = 0;
+ ao->deflate = go->deflate = 0;
+ }
+#endif /* MPPE */
+
/*
* Check whether the kernel knows about the various
* compression methods we might request.
*/
+#ifdef MPPE
+ if (go->mppe) {
+ opt_buf[0] = CI_MPPE;
+ opt_buf[1] = CILEN_MPPE;
+ MPPE_OPTS_TO_CI(go->mppe, &opt_buf[2]);
+ /* Key material unimportant here. */
+ if (ccp_test(f->unit, opt_buf, CILEN_MPPE + MPPE_MAX_KEY_LEN, 0) <= 0) {
+ error("MPPE required, but kernel has no support.");
+ lcp_close(f->unit, "MPPE required but not available");
+ }
+ }
+#endif
if (go->bsd_compress) {
opt_buf[0] = CI_BSD_COMPRESS;
opt_buf[1] = CILEN_BSD_COMPRESS;
return (go->bsd_compress? CILEN_BSD_COMPRESS: 0)
+ (go->deflate? CILEN_DEFLATE: 0)
+ (go->predictor_1? CILEN_PREDICTOR_1: 0)
- + (go->predictor_2? CILEN_PREDICTOR_2: 0);
+ + (go->predictor_2? CILEN_PREDICTOR_2: 0)
+ + (go->mppe? CILEN_MPPE: 0);
}
/*
/*
* Add the compression types that we can receive, in decreasing
- * preference order. Get the kernel to allocate the first one
- * in case it gets Acked.
+ * preference order.
*/
+#ifdef MPPE
+ if (go->mppe) {
+ u_char opt_buf[CILEN_MPPE + MPPE_MAX_KEY_LEN];
+
+ p[0] = opt_buf[0] = CI_MPPE;
+ p[1] = opt_buf[1] = CILEN_MPPE;
+ MPPE_OPTS_TO_CI(go->mppe, &p[2]);
+ MPPE_OPTS_TO_CI(go->mppe, &opt_buf[2]);
+ BCOPY(mppe_recv_key, &opt_buf[CILEN_MPPE], MPPE_MAX_KEY_LEN);
+ res = ccp_test(f->unit, opt_buf, CILEN_MPPE + MPPE_MAX_KEY_LEN, 0);
+ if (res > 0)
+ p += CILEN_MPPE;
+ else
+ /* This shouldn't happen, we've already tested it! */
+ lcp_close(f->unit, "MPPE required but not available in kernel");
+ }
+#endif
if (go->deflate) {
p[0] = go->deflate_correct? CI_DEFLATE: CI_DEFLATE_DRAFT;
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;
- }
- if (res < 0 || go->deflate_size <= DEFLATE_MIN_WORKS) {
+ } else if (res < 0) {
go->deflate = 0;
break;
}
p[0] = CI_BSD_COMPRESS;
p[1] = CILEN_BSD_COMPRESS;
p[2] = BSD_MAKE_OPT(BSD_CURRENT_VERSION, go->bsd_bits);
- if (p != p0) {
- p += CILEN_BSD_COMPRESS; /* not the first option */
- } else {
- for (;;) {
- res = ccp_test(f->unit, p, CILEN_BSD_COMPRESS, 0);
- if (res > 0) {
- p += CILEN_BSD_COMPRESS;
- break;
- }
- if (res < 0 || go->bsd_bits <= BSD_MIN_BITS) {
- go->bsd_compress = 0;
- break;
- }
- --go->bsd_bits;
- 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;
}
+ --go->bsd_bits;
+ p[2] = BSD_MAKE_OPT(BSD_CURRENT_VERSION, go->bsd_bits);
}
}
/* XXX Should Predictor 2 be preferable to Predictor 1? */
ccp_options *go = &ccp_gotoptions[f->unit];
u_char *p0 = p;
+#ifdef MPPE
+ if (go->mppe) {
+ u_char opt_buf[CILEN_MPPE];
+
+ opt_buf[0] = CI_MPPE;
+ opt_buf[1] = CILEN_MPPE;
+ MPPE_OPTS_TO_CI(go->mppe, &opt_buf[2]);
+ if (len < CILEN_MPPE || memcmp(opt_buf, p, CILEN_MPPE))
+ return 0;
+ p += CILEN_MPPE;
+ len -= CILEN_MPPE;
+ /* XXX Cope with first/fast ack */
+ if (len == 0)
+ return 1;
+ }
+#endif
if (go->deflate) {
if (len < CILEN_DEFLATE
|| p[0] != (go->deflate_correct? CI_DEFLATE: CI_DEFLATE_DRAFT)
memset(&no, 0, sizeof(no));
try = *go;
+#ifdef MPPE
+ if (go->mppe && len >= CILEN_MPPE
+ && p[0] == CI_MPPE && p[1] == CILEN_MPPE) {
+ no.mppe = 1;
+ /*
+ * Peer wants us to use a different strength or other setting.
+ * 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)
+ try.mppe = 0;
+ else if ((go->mppe & try.mppe) != try.mppe)
+ /* Peer must have set options we didn't request (suggest) */
+ try.mppe = 0;
+
+ if (!try.mppe)
+ lcp_close(f->unit, "MPPE required but peer negotiation failed");
+ }
+#endif /* MPPE */
if (go->deflate && len >= CILEN_DEFLATE
&& p[0] == (go->deflate_correct? CI_DEFLATE: CI_DEFLATE_DRAFT)
&& p[1] == CILEN_DEFLATE) {
if (len == 0 && all_rejected[f->unit])
return -1;
+#ifdef MPPE
+ if (go->mppe && len >= CILEN_MPPE
+ && p[0] == CI_MPPE && p[1] == CILEN_MPPE) {
+ lcp_close(f->unit, "MPPE required but peer refused");
+ p += CILEN_MPPE;
+ len -= CILEN_MPPE;
+ }
+#endif
if (go->deflate && len >= CILEN_DEFLATE
&& p[0] == (go->deflate_correct? CI_DEFLATE: CI_DEFLATE_DRAFT)
&& p[1] == CILEN_DEFLATE) {
clen = p[1];
switch (type) {
+#ifdef MPPE
+ case CI_MPPE:
+ if (!ao->mppe || clen != CILEN_MPPE) {
+ newret = CONFREJ;
+ break;
+ }
+ MPPE_CI_TO_OPTS(&p[2], ho->mppe);
+
+ /* Nak if anything unsupported or unknown are set. */
+ if (ho->mppe & MPPE_OPT_UNSUPPORTED) {
+ newret = CONFNAK;
+ ho->mppe &= ~MPPE_OPT_UNSUPPORTED;
+ }
+ if (ho->mppe & MPPE_OPT_UNKNOWN) {
+ newret = CONFNAK;
+ ho->mppe &= ~MPPE_OPT_UNKNOWN;
+ }
+
+ /* Check state opt */
+ if (ho->mppe & MPPE_OPT_STATEFUL) {
+ 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.
+ */
+ newret = CONFREJ;
+ break;
+ } else {
+ newret = CONFNAK;
+ }
+ }
+
+ /* Find out which of {S,L} are set. */
+ if ((ho->mppe & MPPE_OPT_128)
+ && (ho->mppe & MPPE_OPT_40)) {
+ /* Both are set, negotiate the strongest. */
+ newret = CONFNAK;
+ if (ao->mppe & MPPE_OPT_128)
+ ho->mppe &= ~MPPE_OPT_40;
+ else if (ao->mppe & MPPE_OPT_40)
+ ho->mppe &= ~MPPE_OPT_128;
+ else {
+ newret = CONFREJ;
+ break;
+ }
+ } else if (ho->mppe & MPPE_OPT_128) {
+ if (!(ao->mppe & MPPE_OPT_128)) {
+ newret = CONFREJ;
+ break;
+ }
+ } else if (ho->mppe & MPPE_OPT_40) {
+ if (!(ao->mppe & MPPE_OPT_40)) {
+ newret = CONFREJ;
+ break;
+ }
+ } else {
+ /* Neither are set. */
+ newret = CONFREJ;
+ break;
+ }
+
+ /* rebuild the opts */
+ MPPE_OPTS_TO_CI(ho->mppe, &p[2]);
+ if (newret == CONFACK) {
+ u_char opt_buf[CILEN_MPPE + MPPE_MAX_KEY_LEN];
+ int mtu;
+
+ BCOPY(p, opt_buf, CILEN_MPPE);
+ BCOPY(mppe_send_key, &opt_buf[CILEN_MPPE],
+ MPPE_MAX_KEY_LEN);
+ if (ccp_test(f->unit, opt_buf,
+ CILEN_MPPE + MPPE_MAX_KEY_LEN, 1) <= 0) {
+ /* This shouldn't happen, we've already tested it! */
+ error("MPPE required, but kernel has no support.");
+ lcp_close(f->unit, "MPPE required but not available");
+ newret = CONFREJ;
+ break;
+ }
+ /*
+ * We need to decrease the interface MTU by MPPE_PAD
+ * because MPPE frames **grow**. The kernel [must]
+ * allocate MPPE_PAD extra bytes in xmit buffers.
+ */
+ mtu = netif_get_mtu(f->unit);
+ if (mtu)
+ netif_set_mtu(f->unit, mtu - MPPE_PAD);
+ else
+ newret = CONFREJ;
+ }
+
+ break;
+#endif /* MPPE */
case CI_DEFLATE:
case CI_DEFLATE_DRAFT:
if (!ao->deflate || clen != CILEN_DEFLATE
else
*lenp = retp - p0;
}
+ if (ret == CONFREJ && ao->mppe)
+ lcp_close(f->unit, "MPPE required but peer negotiation failed");
return ret;
}
if (!ANY_COMPRESS(*opt))
return "(none)";
switch (opt->method) {
+#ifdef MPPE
+ case CI_MPPE:
+ {
+ char *p = result;
+ char *q = result + sizeof(result); /* 1 past result */
+
+ slprintf(p, q - p, "MPPE ");
+ p += 5;
+ if (opt->mppe & MPPE_OPT_128) {
+ slprintf(p, q - p, "128-bit ");
+ p += 8;
+ }
+ if (opt->mppe & MPPE_OPT_40) {
+ slprintf(p, q - p, "40-bit ");
+ p += 7;
+ }
+ if (opt->mppe & MPPE_OPT_STATEFUL)
+ slprintf(p, q - p, "stateful");
+ else
+ slprintf(p, q - p, "stateless");
+
+ break;
+ }
+#endif
case CI_DEFLATE:
case CI_DEFLATE_DRAFT:
if (opt2 != NULL && opt2->deflate_size != opt->deflate_size)
len -= optlen;
optend = p + optlen;
switch (code) {
+#ifdef MPPE
+ case CI_MPPE:
+ if (optlen >= CILEN_MPPE) {
+ u_char mppe_opts;
+
+ MPPE_CI_TO_OPTS(&p[2], mppe_opts);
+ printer(arg, "mppe %s %s %s %s %s %s%s",
+ (p[2] & MPPE_H_BIT)? "+H": "-H",
+ (p[5] & MPPE_M_BIT)? "+M": "-M",
+ (p[5] & MPPE_S_BIT)? "+S": "-S",
+ (p[5] & MPPE_L_BIT)? "+L": "-L",
+ (p[5] & MPPE_D_BIT)? "+D": "-D",
+ (p[5] & MPPE_C_BIT)? "+C": "-C",
+ (mppe_opts & MPPE_OPT_UNKNOWN)? " +U": "");
+ p += CILEN_MPPE;
+ }
+ break;
+#endif
case CI_DEFLATE:
case CI_DEFLATE_DRAFT:
if (optlen >= CILEN_DEFLATE) {
*/
error("Lost compression sync: disabling compression");
ccp_close(unit, "Lost compression sync");
+#ifdef MPPE
+ /*
+ * If we were doing MPPE, we must also take the link down.
+ */
+ if (ccp_gotoptions[unit].mppe) {
+ error("Too many MPPE errors, closing LCP");
+ lcp_close(unit, "Too many MPPE errors");
+ }
+#endif
} else {
/*
* Send a reset-request to reset the peer's compressor.
* OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS,
* OR MODIFICATIONS.
*
- * $Id: ccp.h,v 1.9 1998/11/07 06:59:26 paulus Exp $
+ * $Id: ccp.h,v 1.10 2002/04/02 13:54:59 dfs Exp $
*/
typedef struct ccp_options {
bool predictor_2; /* do Predictor-2? */
bool deflate_correct; /* use correct code for deflate? */
bool deflate_draft; /* use draft RFC code for deflate? */
+ bool mppe; /* do MPPE? */
u_short bsd_bits; /* # bits/code for BSD Compress */
u_short deflate_size; /* lg(window size) for Deflate */
short method; /* code for chosen compression method */
* WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*/
-#define RCSID "$Id: chap.c,v 1.29 2002/03/05 15:14:04 dfs Exp $"
+#define RCSID "$Id: chap.c,v 1.30 2002/04/02 13:54:59 dfs Exp $"
/*
* TODO:
static const char rcsid[] = RCSID;
+#ifdef CHAPMS
+/* For MPPE debug */
+/* Use "[]|}{?/><,`!2&&(" (sans quotes) for RFC 3079 MS-CHAPv2 test value */
+static char *mschap_challenge = NULL;
+/* Use "!@\#$%^&*()_+:3|~" (sans quotes, backslash is to escape #) for ... */
+static char *mschap2_peer_challenge = NULL;
+#endif
+
/*
* Command-line options.
*/
#ifdef MSLANMAN
{ "ms-lanman", o_bool, &ms_lanman,
"Use LanMan passwd when using MS-CHAP", 1 },
+#endif
+#ifdef DEBUGMPPEKEY
+ { "mschap-challenge", o_string, &mschap_challenge,
+ "specify CHAP challenge" },
+ { "mschap2-peer-challenge", o_string, &mschap2_peer_challenge,
+ "specify CHAP peer challenge" },
#endif
{ NULL }
};
break;
case CHAP_MICROSOFT_V2:
- ChapMS2(cstate, rchallenge, NULL, cstate->resp_name, secret, secret_len,
- (MS_Chap2Response *) cstate->response, cstate->earesponse);
+ ChapMS2(cstate, rchallenge,
+ mschap2_peer_challenge? mschap2_peer_challenge: NULL,
+ cstate->resp_name, secret, secret_len,
+ (MS_Chap2Response *) cstate->response, cstate->earesponse,
+ MS_CHAP2_AUTHENTICATEE);
cstate->resp_length = MS_CHAP2_RESPONSE_LEN;
break;
#endif /* CHAPMS */
ChapMS2(cstate, cstate->challenge, rmd->PeerChallenge,
(explicit_remote? remote_name: rhostname),
secret, secret_len, &md,
- cstate->saresponse);
+ cstate->saresponse, MS_CHAP2_AUTHENTICATOR);
/* compare MDs and send the appropriate status */
if (memcmp(md.NTResp, rmd->NTResp, sizeof(md.NTResp)) == 0)
old_state = cstate->serverstate;
cstate->serverstate = CHAPSS_OPEN;
if (old_state == CHAPSS_INITIAL_CHAL) {
- auth_peer_success(cstate->unit, PPP_CHAP, rhostname, len);
+ auth_peer_success(cstate->unit, PPP_CHAP, cstate->chal_type,
+ rhostname, len);
}
if (cstate->chal_interval != 0)
TIMEOUT(ChapRechallenge, cstate, cstate->chal_interval);
cstate->clientstate = CHAPCS_OPEN;
- auth_withpeer_success(cstate->unit, PPP_CHAP);
+ auth_withpeer_success(cstate->unit, PPP_CHAP, cstate->resp_type);
}
#ifdef CHAPMS
if ((cstate->resp_type == CHAP_MICROSOFT_V2) ||
(cstate->resp_type == CHAP_MICROSOFT)) {
- long error;
+ int error;
/*
* Deal with MS-CHAP formatted failure messages; just print the
* to use M=<message>, but it shouldn't hurt. See ChapSendStatus().
*/
if (!strncmp(p, "E=", 2))
- error = strtol(p, NULL, 10); /* Remember the error code. */
+ error = (int) strtol(p, NULL, 10); /* Remember the error code. */
else
goto print_msg; /* Message is badly formatted. */
p += 3;
} else {
/* No M=<message>; use the error code. */
- switch(error - MS_CHAP_ERROR_BASE) {
+ switch(error) {
case MS_CHAP_ERROR_RESTRICTED_LOGON_HOURS:
p = "Restricted logon hours";
break;
cstate->chal_id = ++cstate->id;
cstate->chal_transmits = 0;
- /* generate a random string */
- for (i = 0; i < chal_len; i++)
- *ptr++ = (char) (drand48() * 0xff);
+#ifdef CHAPMS
+ if (mschap_challenge)
+ for (i = 0; i < chal_len; i++)
+ *ptr++ = mschap_challenge[i];
+ else
+#endif
+ /* generate a random string */
+ for (i = 0; i < chal_len; i++)
+ *ptr++ = (char) (drand48() * 0xff);
}
/*
/*
* Modifications by Frank Cusack, frank@google.com, March 2002.
*
- * Implemented MS-CHAPv2 functionality. Heavily based on
- * sample implementation in RFC 2759.
+ * Implemented MS-CHAPv2 functionality, heavily based on sample
+ * implementation in RFC 2759. Implemented MPPE functionality,
+ * heavily based on sample implementation in RFC 3079.
+ * Copyright (c) 2002 Google, Inc.
*/
-#define RCSID "$Id: chap_ms.c,v 1.18 2002/03/05 15:14:04 dfs Exp $"
+#define RCSID "$Id: chap_ms.c,v 1.19 2002/04/02 13:54:59 dfs Exp $"
#ifdef CHAPMS
static void Collapse __P((u_char *, u_char *));
#endif
+#ifdef MPPE
+static void Set_Start_Key __P((u_char *, char *, int));
+static void SetMasterKeys __P((char *, int, u_char[24], int));
+#endif
+
extern double drand48 __P((void));
#ifdef MSLANMAN
/* Has meaning only with MS-CHAP challenges */
#endif
+#ifdef MPPE
+u_char mppe_send_key[MPPE_MAX_KEY_LEN];
+u_char mppe_recv_key[MPPE_MAX_KEY_LEN];
+#endif
+
static void
ChallengeResponse(u_char *challenge,
u_char PasswordHash[MD4_SIGNATURE_SIZE],
* "Magic" constants used in response generation, from RFC 2759.
*/
u_char Magic1[39] = /* "Magic server to client signing constant" */
- {0x4D, 0x61, 0x67, 0x69, 0x63, 0x20, 0x73, 0x65, 0x72, 0x76,
- 0x65, 0x72, 0x20, 0x74, 0x6F, 0x20, 0x63, 0x6C, 0x69, 0x65,
- 0x6E, 0x74, 0x20, 0x73, 0x69, 0x67, 0x6E, 0x69, 0x6E, 0x67,
- 0x20, 0x63, 0x6F, 0x6E, 0x73, 0x74, 0x61, 0x6E, 0x74};
+ { 0x4D, 0x61, 0x67, 0x69, 0x63, 0x20, 0x73, 0x65, 0x72, 0x76,
+ 0x65, 0x72, 0x20, 0x74, 0x6F, 0x20, 0x63, 0x6C, 0x69, 0x65,
+ 0x6E, 0x74, 0x20, 0x73, 0x69, 0x67, 0x6E, 0x69, 0x6E, 0x67,
+ 0x20, 0x63, 0x6F, 0x6E, 0x73, 0x74, 0x61, 0x6E, 0x74 };
u_char Magic2[41] = /* "Pad to make it do more than one iteration" */
- {0x50, 0x61, 0x64, 0x20, 0x74, 0x6F, 0x20, 0x6D, 0x61, 0x6B,
- 0x65, 0x20, 0x69, 0x74, 0x20, 0x64, 0x6F, 0x20, 0x6D, 0x6F,
- 0x72, 0x65, 0x20, 0x74, 0x68, 0x61, 0x6E, 0x20, 0x6F, 0x6E,
- 0x65, 0x20, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6F,
- 0x6E};
+ { 0x50, 0x61, 0x64, 0x20, 0x74, 0x6F, 0x20, 0x6D, 0x61, 0x6B,
+ 0x65, 0x20, 0x69, 0x74, 0x20, 0x64, 0x6F, 0x20, 0x6D, 0x6F,
+ 0x72, 0x65, 0x20, 0x74, 0x68, 0x61, 0x6E, 0x20, 0x6F, 0x6E,
+ 0x65, 0x20, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6F,
+ 0x6E };
int i;
SHA1_CTX sha1Context;
}
+#ifdef MPPE
+/*
+ * Set mppe_xxxx_key from the NTPasswordHashHash.
+ * RFC 2548 (RADIUS support) requires us to export this function (ugh).
+ */
+void
+mppe_set_keys(u_char *rchallenge, u_char PasswordHashHash[MD4_SIGNATURE_SIZE])
+{
+ SHA1_CTX sha1Context;
+ u_char Digest[SHA1_SIGNATURE_SIZE]; /* >= MPPE_MAX_KEY_LEN */
+
+ SHA1_Init(&sha1Context);
+ SHA1_Update(&sha1Context, PasswordHashHash, MD4_SIGNATURE_SIZE);
+ SHA1_Update(&sha1Context, PasswordHashHash, MD4_SIGNATURE_SIZE);
+ SHA1_Update(&sha1Context, rchallenge, 8);
+ SHA1_Final(Digest, &sha1Context);
+
+ /* Same key in both directions. */
+ BCOPY(Digest, mppe_send_key, sizeof(mppe_send_key));
+ BCOPY(Digest, mppe_recv_key, sizeof(mppe_recv_key));
+}
+
+/*
+ * Set mppe_xxxx_key from MS-CHAP credentials. (see RFC 3079)
+ */
+static void
+Set_Start_Key(u_char *rchallenge, char *secret, int secret_len)
+{
+ u_char unicodePassword[MAX_NT_PASSWORD * 2];
+ u_char PasswordHash[MD4_SIGNATURE_SIZE];
+ u_char PasswordHashHash[MD4_SIGNATURE_SIZE];
+
+ /* Hash (x2) the Unicode version of the secret (== password). */
+ ascii2unicode(secret, secret_len, unicodePassword);
+ NTPasswordHash(unicodePassword, secret_len * 2, PasswordHash);
+ NTPasswordHash(PasswordHash, sizeof(PasswordHash), PasswordHashHash);
+
+ mppe_set_keys(rchallenge, PasswordHashHash);
+}
+
+/*
+ * Set mppe_xxxx_key from MS-CHAPv2 credentials. (see RFC 3079)
+ */
+static void
+SetMasterKeys(char *secret, int secret_len, u_char NTResponse[24], int IsServer)
+{
+ SHA1_CTX sha1Context;
+ u_char unicodePassword[MAX_NT_PASSWORD * 2];
+ u_char PasswordHash[MD4_SIGNATURE_SIZE];
+ u_char PasswordHashHash[MD4_SIGNATURE_SIZE];
+ u_char MasterKey[SHA1_SIGNATURE_SIZE]; /* >= MPPE_MAX_KEY_LEN */
+ u_char Digest[SHA1_SIGNATURE_SIZE]; /* >= MPPE_MAX_KEY_LEN */
+
+ u_char SHApad1[40] =
+ { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+ u_char SHApad2[40] =
+ { 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2,
+ 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2,
+ 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2,
+ 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2 };
+
+ /* "This is the MPPE Master Key" */
+ u_char Magic1[27] =
+ { 0x54, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74,
+ 0x68, 0x65, 0x20, 0x4d, 0x50, 0x50, 0x45, 0x20, 0x4d,
+ 0x61, 0x73, 0x74, 0x65, 0x72, 0x20, 0x4b, 0x65, 0x79 };
+ /* "On the client side, this is the send key; "
+ "on the server side, it is the receive key." */
+ u_char Magic2[84] =
+ { 0x4f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6c, 0x69,
+ 0x65, 0x6e, 0x74, 0x20, 0x73, 0x69, 0x64, 0x65, 0x2c, 0x20,
+ 0x74, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68,
+ 0x65, 0x20, 0x73, 0x65, 0x6e, 0x64, 0x20, 0x6b, 0x65, 0x79,
+ 0x3b, 0x20, 0x6f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x73,
+ 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x73, 0x69, 0x64, 0x65,
+ 0x2c, 0x20, 0x69, 0x74, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68,
+ 0x65, 0x20, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x20,
+ 0x6b, 0x65, 0x79, 0x2e };
+ /* "On the client side, this is the receive key; "
+ "on the server side, it is the send key." */
+ u_char Magic3[84] =
+ { 0x4f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6c, 0x69,
+ 0x65, 0x6e, 0x74, 0x20, 0x73, 0x69, 0x64, 0x65, 0x2c, 0x20,
+ 0x74, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68,
+ 0x65, 0x20, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x20,
+ 0x6b, 0x65, 0x79, 0x3b, 0x20, 0x6f, 0x6e, 0x20, 0x74, 0x68,
+ 0x65, 0x20, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x73,
+ 0x69, 0x64, 0x65, 0x2c, 0x20, 0x69, 0x74, 0x20, 0x69, 0x73,
+ 0x20, 0x74, 0x68, 0x65, 0x20, 0x73, 0x65, 0x6e, 0x64, 0x20,
+ 0x6b, 0x65, 0x79, 0x2e };
+ u_char *s;
+
+ /* Hash (x2) the Unicode version of the secret (== password). */
+ ascii2unicode(secret, secret_len, unicodePassword);
+ NTPasswordHash(unicodePassword, secret_len * 2, PasswordHash);
+ NTPasswordHash(PasswordHash, sizeof(PasswordHash), PasswordHashHash);
+
+ SHA1_Init(&sha1Context);
+ SHA1_Update(&sha1Context, PasswordHashHash, sizeof(PasswordHashHash));
+ SHA1_Update(&sha1Context, NTResponse, 24);
+ SHA1_Update(&sha1Context, Magic1, sizeof(Magic1));
+ SHA1_Final(MasterKey, &sha1Context);
+
+ /*
+ * generate send key
+ */
+ if (IsServer)
+ s = Magic3;
+ else
+ s = Magic2;
+ SHA1_Init(&sha1Context);
+ SHA1_Update(&sha1Context, MasterKey, 16);
+ SHA1_Update(&sha1Context, SHApad1, sizeof(SHApad1));
+ SHA1_Update(&sha1Context, s, 84);
+ SHA1_Update(&sha1Context, SHApad2, sizeof(SHApad2));
+ SHA1_Final(Digest, &sha1Context);
+
+ BCOPY(Digest, mppe_send_key, sizeof(mppe_send_key));
+
+ /*
+ * generate recv key
+ */
+ if (IsServer)
+ s = Magic2;
+ else
+ s = Magic3;
+ SHA1_Init(&sha1Context);
+ SHA1_Update(&sha1Context, MasterKey, 16);
+ SHA1_Update(&sha1Context, SHApad1, sizeof(SHApad1));
+ SHA1_Update(&sha1Context, s, 84);
+ SHA1_Update(&sha1Context, SHApad2, sizeof(SHApad2));
+ SHA1_Final(Digest, &sha1Context);
+
+ BCOPY(Digest, mppe_recv_key, sizeof(mppe_recv_key));
+}
+
+#endif /* MPPE */
+
+
void
ChapMS(chap_state *cstate, u_char *rchallenge, char *secret, int secret_len,
MS_ChapResponse *response)
#if 0
CHAPDEBUG((LOG_INFO, "ChapMS: secret is '%.*s'", secret_len, secret));
#endif
- BZERO(response, sizeof(response));
+ BZERO(response, sizeof(*response));
/* Calculate both always */
ChapMS_NT(rchallenge, secret, secret_len, response->NTResp);
response->UseNT[0] = 1;
#endif
+#ifdef MPPE
+ Set_Start_Key(rchallenge, secret, secret_len);
+#endif
}
* If PeerChallenge is NULL, one is generated and response->PeerChallenge
* is filled in. Call this way when generating a response.
* If PeerChallenge is supplied, it is copied into response->PeerChallenge.
- * Call this way when verifying a response.
+ * Call this way when verifying a response (or debugging).
+ * Do not call with PeerChallenge = response->PeerChallenge.
*
* response->PeerChallenge is then used for calculation of the
* Authenticator Response.
void
ChapMS2(chap_state *cstate, u_char *rchallenge, u_char *PeerChallenge,
char *user, char *secret, int secret_len, MS_Chap2Response *response,
- u_char authResponse[MS_AUTH_RESPONSE_LENGTH+1])
+ u_char authResponse[MS_AUTH_RESPONSE_LENGTH+1], int authenticator)
{
+ /* ARGSUSED */
u_char *p = response->PeerChallenge;
int i;
- BZERO(response, sizeof(response));
+ BZERO(response, sizeof(*response));
/* Generate the Peer-Challenge if requested, or copy it if supplied. */
if (!PeerChallenge)
GenerateAuthenticatorResponse(secret, secret_len, response->NTResp,
response->PeerChallenge, rchallenge,
user, authResponse);
+#ifdef MPPE
+ SetMasterKeys(secret, secret_len, response->NTResp, authenticator);
+#endif
}
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
* WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*
- * $Id: chap_ms.h,v 1.5 2002/03/05 15:14:04 dfs Exp $
+ * $Id: chap_ms.h,v 1.6 2002/04/02 13:54:59 dfs Exp $
*/
#ifndef __CHAPMS_INCLUDE__
#define MS_CHAP_RESPONSE_LEN 49 /* Response length for MS-CHAP */
#define MS_CHAP2_RESPONSE_LEN 49 /* Response length for MS-CHAPv2 */
-/*
- * E=eeeeeeeeee error codes for MS-CHAP failure messages.
- * Offset by 646 (the minimum code) for switch() handling on older compilers.
- */
-#define MS_CHAP_ERROR_BASE 646
-#define MS_CHAP_ERROR_RESTRICTED_LOGON_HOURS (646 - MS_CHAP_ERROR_BASE)
-#define MS_CHAP_ERROR_ACCT_DISABLED (647 - MS_CHAP_ERROR_BASE)
-#define MS_CHAP_ERROR_PASSWD_EXPIRED (648 - MS_CHAP_ERROR_BASE)
-#define MS_CHAP_ERROR_NO_DIALIN_PERMISSION (649 - MS_CHAP_ERROR_BASE)
-#define MS_CHAP_ERROR_AUTHENTICATION_FAILURE (691 - MS_CHAP_ERROR_BASE)
-#define MS_CHAP_ERROR_CHANGING_PASSWORD (709 - MS_CHAP_ERROR_BASE)
+/* E=eeeeeeeeee error codes for MS-CHAP failure messages. */
+#define MS_CHAP_ERROR_RESTRICTED_LOGON_HOURS 646
+#define MS_CHAP_ERROR_ACCT_DISABLED 647
+#define MS_CHAP_ERROR_PASSWD_EXPIRED 648
+#define MS_CHAP_ERROR_NO_DIALIN_PERMISSION 649
+#define MS_CHAP_ERROR_AUTHENTICATION_FAILURE 691
+#define MS_CHAP_ERROR_CHANGING_PASSWORD 709
/*
* Use MS_CHAP_RESPONSE_LEN, rather than sizeof(MS_ChapResponse),
u_char Flags[1]; /* Must be zero */
} MS_Chap2Response;
+#ifdef MPPE
+#include <net/ppp-comp.h> /* MPPE_MAX_KEY_LEN */
+extern u_char mppe_send_key[MPPE_MAX_KEY_LEN];
+extern u_char mppe_recv_key[MPPE_MAX_KEY_LEN];
+#endif
+
+/* Are we the authenticator or authenticatee? For MS-CHAPv2 key derivation. */
+#define MS_CHAP2_AUTHENTICATEE 0
+#define MS_CHAP2_AUTHENTICATOR 1
+
+#include "chap.h" /* chap_state, et al */
void ChapMS __P((chap_state *, u_char *, char *, int, MS_ChapResponse *));
void ChapMS2 __P((chap_state *, u_char *, u_char *, char *, char *, int,
- MS_Chap2Response *, u_char[MS_AUTH_RESPONSE_LENGTH+1]));
+ MS_Chap2Response *, u_char[MS_AUTH_RESPONSE_LENGTH+1], int));
+#ifdef MPPE
+void mppe_set_keys __P((u_char *, u_char[MD4_SIGNATURE_SIZE]));
+#endif
#define __CHAPMS_INCLUDE__
#endif /* __CHAPMS_INCLUDE__ */
* WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*/
-#define RCSID "$Id: options.c,v 1.82 2002/03/01 14:39:18 dfs Exp $"
+#define RCSID "$Id: options.c,v 1.83 2002/04/02 13:54:59 dfs Exp $"
#include <ctype.h>
#include <stdio.h>
*(bool *)(opt->addr2) = 0;
else if (opt->addr2 && (opt->flags & OPT_A2CLRB))
*(u_char *)(opt->addr2) &= ~v;
+ else if (opt->addr2 && (opt->flags & OPT_A2OR))
+ *(u_char *)(opt->addr2) |= v;
if (opt->addr3 && (opt->flags & OPT_A3OR))
*(u_char *)(opt->addr3) |= v;
break;
}
if (opt->addr2 && (opt->flags & (OPT_A2COPY|OPT_ENABLE
- |OPT_A2PRINTER|OPT_A2STRVAL|OPT_A2LIST)) == 0)
+ |OPT_A2PRINTER|OPT_A2STRVAL|OPT_A2LIST|OPT_A2OR)) == 0)
*(bool *)(opt->addr2) = !(opt->flags & OPT_A2CLR);
mainopt->source = option_source;
*
***********************************************************************/
static char const RCSID[] =
-"$Id: radius.c,v 1.7 2002/03/12 00:28:56 dfs Exp $";
+"$Id: radius.c,v 1.8 2002/04/02 13:55:00 dfs Exp $";
#include "pppd.h"
#include "chap.h"
.\" manual page [] for pppd 2.4
-.\" $Id: pppd.8,v 1.61 2002/03/05 15:14:04 dfs Exp $
+.\" $Id: pppd.8,v 1.62 2002/04/02 13:54:59 dfs Exp $
.\" SH section heading
.\" SS subsection heading
.\" LP paragraph
Enables the use of PPP multilink; this is an alias for the `multilink'
option. This option is currently only available under Linux.
.TP
+.B mppe-stateful
+Allow MPPE to use stateful mode. Stateless mode is still attempted first.
+The default is to disallow stateful mode.
+.TP
.B mpshortseq
Enables the use of short (12-bit) sequence numbers in multilink
headers, as opposed to 24-bit sequence numbers. This option is only
Disables the use of PPP multilink. This option is currently only
available under Linux.
.TP
+.B nomppe
+Disables MPPE (Microsoft Point to Point Encryption). This is the default.
+.TP
+.B nomppe-40
+Disable 40\-bit encryption with MPPE.
+.TP
+.B nomppe-128
+Disable 128\-bit encryption with MPPE.
+.TP
+.B nomppe-stateful
+Disabled MPPE stateful mode. This is the default.
+.TP
.B nompshortseq
Disables the use of short (12-bit) sequence numbers in the PPP
multilink protocol, forcing the use of 24-bit sequence numbers. This
Require the peer to authenticate itself using CHAP [Challenge
Handshake Authentication Protocol] authentication.
.TP
+.B require-mppe
+Require the use of MPPE (Microsoft Point to Point Encryption). This
+option disables all other compression types. This option enables
+both 40\-bit and 128\-bit encryption. In order for MPPE to successfully
+come up, you must have authenticated with either MS-CHAP or MS-CHAPv2.
+.TP
+.B require-mppe-40
+Require the use of MPPE, with 40\-bit encryption.
+.TP
+.B require-mppe-128
+Require the use of MPPE, with 128\-bit encryption.
+.TP
.B require-mschap
Require the peer to authenticate itself using MS-CHAP [Microsft Challenge
Handshake Authentication Protocol] authentication.
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
* WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*
- * $Id: pppd.h,v 1.66 2002/03/01 14:39:18 dfs Exp $
+ * $Id: pppd.h,v 1.67 2002/04/02 13:54:59 dfs Exp $
*/
/*
#define OPT_NOARG 0x200 /* option doesn't take argument */
#define OPT_OR 0x400 /* OR in argument to value */
#define OPT_INC 0x800 /* increment value */
+#define OPT_A2OR 0x800 /* for o_bool, OR arg to *(u_char *)addr2 */
#define OPT_PRIV 0x1000 /* privileged option */
#define OPT_STATIC 0x2000 /* string option goes into static array */
#define OPT_LLIMIT 0x4000 /* check value against lower limit */
extern char *progname; /* Name of this program */
extern int redirect_stderr;/* Connector's stderr should go to file */
extern char peer_authname[];/* Authenticated name of peer */
+extern int auth_done[NUM_PPP]; /* Methods actually used for auth */
extern int privileged; /* We were run by real-uid root */
extern int need_holdoff; /* Need holdoff period after link terminates */
extern char **script_env; /* Environment variables for scripts */
/* Has meaning only with MS-CHAP challenges */
#endif
+/* Values for auth_pending, auth_done */
+#define PAP_WITHPEER 0x1
+#define PAP_PEER 0x2
+#define CHAP_WITHPEER 0x4
+#define CHAP_PEER 0x8
+/* Values for auth_done only */
+#define CHAP_MD5_WITHPEER 0x10
+#define CHAP_MD5_PEER 0x20
+#ifdef CHAPMS
+#define CHAP_MS_WITHPEER 0x40
+#define CHAP_MS_PEER 0x80
+#define CHAP_MS2_WITHPEER 0x100
+#define CHAP_MS2_PEER 0x200
+#endif
+
extern char *current_option; /* the name of the option being parsed */
extern int privileged_option; /* set iff the current option came from root */
extern char *option_source; /* string saying where the option came from */
void np_finished __P((int, int)); /* a network protocol no longer needs link */
void auth_peer_fail __P((int, int));
/* peer failed to authenticate itself */
-void auth_peer_success __P((int, int, char *, int));
+void auth_peer_success __P((int, int, int, char *, int));
/* peer successfully authenticated itself */
void auth_withpeer_fail __P((int, int));
/* we failed to authenticate ourselves */
-void auth_withpeer_success __P((int, int));
+void auth_withpeer_success __P((int, int, int));
/* we successfully authenticated ourselves */
void auth_check_options __P((void));
/* check authentication options supplied */
int get_ppp_stats __P((int, struct pppd_stats *));
/* Return link statistics */
void netif_set_mtu __P((int, int)); /* Set PPP interface MTU */
+int netif_get_mtu __P((int)); /* Get PPP interface MTU */
int sifvjcomp __P((int, int, int, int));
/* Configure VJ TCP header compression */
int sifup __P((int)); /* Configure i/f up for one protocol */
#include <netinet/in.h> /* htonl() */
#include "sha1.h"
+static void
+SHA1_Transform(unsigned long[5], const unsigned char[64]);
+
#define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits))))
/* blk0() and blk() perform the initial expand. */
/* Hash a single 512-bit block. This is the core of the algorithm. */
-void SHA1_Transform(unsigned long state[5], const unsigned char buffer[64])
+static void
+SHA1_Transform(unsigned long state[5], const unsigned char buffer[64])
{
unsigned long a, b, c, d, e;
typedef union {
/* SHA1Init - Initialize new context */
-void SHA1_Init(SHA1_CTX *context)
+void
+SHA1_Init(SHA1_CTX *context)
{
/* SHA1 initialization constants */
context->state[0] = 0x67452301;
/* Run your data through this. */
-void SHA1_Update(SHA1_CTX *context, const unsigned char *data, unsigned int len)
+void
+SHA1_Update(SHA1_CTX *context, const unsigned char *data, unsigned int len)
{
unsigned int i, j;
/* Add padding and return the message digest. */
-void SHA1_Final(unsigned char digest[20], SHA1_CTX *context)
+void
+SHA1_Final(unsigned char digest[20], SHA1_CTX *context)
{
unsigned long i, j;
unsigned char finalcount[8];
#define SHA1_SIGNATURE_SIZE 20
-void SHA1_Transform(unsigned long[5], const unsigned char[64]);
-void SHA1_Init(SHA1_CTX *);
-void SHA1_Update(SHA1_CTX *, const unsigned char *, unsigned int);
-void SHA1_Final(unsigned char[20], SHA1_CTX *);
+extern void SHA1_Init(SHA1_CTX *);
+extern void SHA1_Update(SHA1_CTX *, const unsigned char *, unsigned int);
+extern void SHA1_Final(unsigned char[SHA1_SIGNATURE_SIZE], SHA1_CTX *);
#define __SHA1_INCLUDE_
#endif /* __SHA1_INCLUDE_ */
error("ioctl(SIOCSIFMTU): %m (line %d)", __LINE__);
}
+/*
+ * netif_get_mtu - get the MTU on the PPP network interface.
+ */
+int
+netif_get_mtu(int unit)
+{
+ struct ifreq ifr;
+
+ memset (&ifr, '\0', sizeof (ifr));
+ strlcpy(ifr.ifr_name, ifname, sizeof (ifr.ifr_name));
+
+ if (ifunit >= 0 && ioctl(sock_fd, SIOCGIFMTU, (caddr_t) &ifr) < 0) {
+ error("ioctl(SIOCGIFMTU): %m (line %d)", __LINE__);
+ return 0;
+ }
+ return ifr.ifr_mtu;
+}
+
/********************************************************************
*
* tty_send_config - configure the transmit characteristics of
* is acceptable for use.
*/
-int ccp_test (int unit, u_char *opt_ptr, int opt_len, int for_transmit)
+int
+ccp_test(int unit, u_char *opt_ptr, int opt_len, int for_transmit)
{
struct ppp_option_data data;
* WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*/
-#define RCSID "$Id: upap.c,v 1.24 2001/03/08 05:11:16 paulus Exp $"
+#define RCSID "$Id: upap.c,v 1.25 2002/04/02 13:54:59 dfs Exp $"
/*
* TODO:
if (retcode == UPAP_AUTHACK) {
u->us_serverstate = UPAPSS_OPEN;
- auth_peer_success(u->us_unit, PPP_PAP, ruser, ruserlen);
+ auth_peer_success(u->us_unit, PPP_PAP, 0, ruser, ruserlen);
} else {
u->us_serverstate = UPAPSS_BADAUTH;
auth_peer_fail(u->us_unit, PPP_PAP);
u->us_clientstate = UPAPCS_OPEN;
- auth_withpeer_success(u->us_unit, PPP_PAP);
+ auth_withpeer_success(u->us_unit, PPP_PAP, 0);
}