From bcfa20820fc9ff3b25bcf62308e3e737c1897dc6 Mon Sep 17 00:00:00 2001 From: "David F. Skoll" Date: Tue, 5 Mar 2002 15:14:06 +0000 Subject: [PATCH 1/1] Patch from Frank Cusack to add support for MSCHAPv2. Enhanced radiusclient to support INCLUDE lines in dictionary. --- README.MSCHAP80 | 2 +- README.MSCHAP81 | 65 ++++ pppd/Makefile.linux | 8 +- pppd/Makefile.osf | 6 +- pppd/auth.c | 25 +- pppd/chap.c | 235 ++++++++++++++- pppd/chap.h | 18 +- pppd/chap_ms.c | 285 +++++++++++++----- pppd/chap_ms.h | 30 +- pppd/lcp.c | 9 +- pppd/plugins/radius/pppd-radius.8 | 6 +- pppd/plugins/radius/radius.c | 133 +++++--- .../radius/radiusclient/etc/dictionary | 1 + .../radiusclient/include/radiusclient.h | 4 +- pppd/plugins/radius/radiusclient/lib/dict.c | 69 +++-- pppd/pppd.8 | 20 +- pppd/sha1.c | 163 ++++++++++ pppd/sha1.h | 19 ++ pppd/utils.c | 24 +- 19 files changed, 937 insertions(+), 185 deletions(-) create mode 100644 README.MSCHAP81 create mode 100644 pppd/sha1.c create mode 100644 pppd/sha1.h diff --git a/README.MSCHAP80 b/README.MSCHAP80 index fe7a019..c77d769 100644 --- a/README.MSCHAP80 +++ b/README.MSCHAP80 @@ -162,7 +162,7 @@ Assuming that everything else has been configured correctly for PPP and CHAP, the MS-CHAP-specific problems you're likely to encounter are mostly related to your Windows NT account and its settings. A Microsoft server returns error codes in its CHAP response. The following are extracted from -Microsoft's "chapexts.txt" file referenced above: +RFC 2433: 646 ERROR_RESTRICTED_LOGON_HOURS 647 ERROR_ACCT_DISABLED diff --git a/README.MSCHAP81 b/README.MSCHAP81 new file mode 100644 index 0000000..1d415a9 --- /dev/null +++ b/README.MSCHAP81 @@ -0,0 +1,65 @@ +PPP Support for Microsoft's CHAP-81 +=================================== + +Frank Cusack frank@google.com + +Some text verbatim from README.MSCHAP80, +by Eric Rosenquist, rosenqui@strataware.com + +INTRODUCTION + +First, please read README.MSCHAP80; almost everything there applies here. +MS-CHAP was basically devised by Microsoft because rather than store +plaintext passwords, they (Microsoft) store the md4 hash of passwords. +It provides no advantage over standard CHAP, since the hash is used +as plaintext-equivalent. (Well, the Change-Password packet is arguably +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. + +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 +authentication in MS-CHAPv2 is different than the case where both PPP +peers require authentication from the other; the former proves that +the server has access to the client's password, the latter proves that +the server has access to a secret which the client also has -- which +may or may not be the same as the client's password (but should not be +the same, per RFC 1994). Whether this provides any actual benefit is +outside the scope of this document. The details of MS-CHAPv2 can be +found in the document: + + + + +BUILDING THE PPPD + +In addition to the requirements for MS-CHAP, MS-CHAPv2 uses the SHA-1 +hash algorithm. A public domain implementation is provided with pppd. + + +TROUBLESHOOTING + +Assuming that everything else has been configured correctly for PPP and +CHAP, the MS-CHAPv2-specific problems you're likely to encounter are mostly +related to your Windows NT account and its settings. A Microsoft server +returns error codes in its CHAP response. The following are extracted from +RFC 2759: + + 646 ERROR_RESTRICTED_LOGON_HOURS + 647 ERROR_ACCT_DISABLED + 648 ERROR_PASSWD_EXPIRED + 649 ERROR_NO_DIALIN_PERMISSION + 691 ERROR_AUTHENTICATION_FAILURE + 709 ERROR_CHANGING_PASSWORD + +You'll see these in your pppd log as a line similar to: + + Remote message: No dialin permission + +Previously, pppd would log this as: + + Remote message: E=649 R=0 + +Now, the text message is logged (both for MS-CHAP and MS-CHAPv2). + diff --git a/pppd/Makefile.linux b/pppd/Makefile.linux index d16a71e..aea5fb9 100644 --- a/pppd/Makefile.linux +++ b/pppd/Makefile.linux @@ -1,6 +1,6 @@ # # pppd makefile for Linux -# $Id: Makefile.linux,v 1.45 2002/03/04 14:59:51 dfs Exp $ +# $Id: Makefile.linux,v 1.46 2002/03/05 15:14:04 dfs Exp $ # # Default installation locations @@ -9,12 +9,12 @@ MANDIR = /usr/man PPPDSRCS = main.c magic.c fsm.c lcp.c ipcp.c upap.c chap.c md5.c ccp.c \ ipxcp.c auth.c options.c sys-linux.c md4.c chap_ms.c cbcp.c \ - demand.c utils.c tty.c + demand.c utils.c tty.c sha1.c HEADERS = callout.h pathnames.h patchlevel.h chap.h md5.h chap_ms.h md4.h \ - ipxcp.h cbcp.h tdb.h + ipxcp.h cbcp.h tdb.h sha1.h MANPAGES = pppd.8 PPPDOBJS = main.o magic.o fsm.o lcp.o ipcp.o upap.o chap.o md5.o ccp.o \ - auth.o options.o demand.o utils.o sys-linux.o ipxcp.o tty.o + auth.o options.o demand.o utils.o sys-linux.o ipxcp.o tty.o sha1.o all: pppd diff --git a/pppd/Makefile.osf b/pppd/Makefile.osf index 825e73a..63f4eb9 100644 --- a/pppd/Makefile.osf +++ b/pppd/Makefile.osf @@ -1,16 +1,16 @@ # # pppd makefile for OSF/1 on DEC Alpha -# $Id: Makefile.osf,v 1.10 1999/04/27 22:31:18 varadhan Exp $ +# $Id: Makefile.osf,v 1.11 2002/03/05 15:14:04 dfs Exp $ # BINDIR = /usr/local/etc MANDIR = /usr/local/man PPPDSRCS = main.c magic.c fsm.c lcp.c ipcp.c upap.c chap.c md5.c ccp.c \ - auth.c options.c demand.c utils.c sys-osf.c md4.c chap_ms.c + auth.c options.c demand.c utils.c sys-osf.c md4.c chap_ms.c sha1.c PPPDOBJS = main.o magic.o fsm.o lcp.o ipcp.o upap.o chap.o md5.o ccp.o \ - auth.o options.o demand.o utils.o sys-osf.o md4.o chap_ms.o + auth.o options.o demand.o utils.o sys-osf.o md4.o chap_ms.o sha1.o CC = cc #DEBUG_FLAGS = -DDEBUGALL diff --git a/pppd/auth.c b/pppd/auth.c index 4f9c297..61b50e5 100644 --- a/pppd/auth.c +++ b/pppd/auth.c @@ -32,7 +32,7 @@ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. */ -#define RCSID "$Id: auth.c,v 1.74 2002/03/01 14:39:18 dfs Exp $" +#define RCSID "$Id: auth.c,v 1.75 2002/03/05 15:14:04 dfs Exp $" #include #include @@ -171,8 +171,10 @@ bool refuse_pap = 0; /* Don't wanna auth. ourselves with PAP */ bool refuse_chap = 0; /* Don't wanna auth. ourselves with CHAP */ #ifdef CHAPMS bool refuse_mschap = 0; /* Don't wanna auth. ourselves with MS-CHAP */ +bool refuse_mschap_v2 = 0; /* Don't wanna auth. ourselves with MS-CHAPv2 */ #else bool refuse_mschap = 1; /* Don't wanna auth. ourselves with MS-CHAP */ +bool refuse_mschap_v2 = 1; /* Don't wanna auth. ourselves with MS-CHAPv2 */ #endif bool usehostname = 0; /* Use hostname for our_name */ bool auth_required = 0; /* Always require authentication from peer */ @@ -248,6 +250,14 @@ option_t auth_options[] = { "Require MS-CHAP authentication from peer", OPT_ALIAS | OPT_PRIOSUB | OPT_A2COPY | OPT_A3OR | MDTYPE_MICROSOFT, &auth_required, 0, 0, NULL, 0, 0, &lcp_wantoptions[0].chap_mdtype }, + { "require-mschap-v2", o_bool, &lcp_wantoptions[0].neg_chap, + "Require MS-CHAPv2 authentication from peer", + OPT_PRIOSUB | OPT_A2COPY | OPT_A3OR | MDTYPE_MICROSOFT_V2, + &auth_required, 0, 0, NULL, 0, 0, &lcp_wantoptions[0].chap_mdtype }, + { "+mschap-v2", o_bool, &lcp_wantoptions[0].neg_chap, + "Require MS-CHAPv2 authentication from peer", + OPT_ALIAS | OPT_PRIOSUB | OPT_A2COPY | OPT_A3OR | MDTYPE_MICROSOFT_V2, + &auth_required, 0, 0, NULL, 0, 0, &lcp_wantoptions[0].chap_mdtype }, #endif { "refuse-pap", o_bool, &refuse_pap, @@ -263,12 +273,19 @@ option_t auth_options[] = { &lcp_allowoptions[0].chap_mdtype }, #ifdef CHAPMS { "refuse-mschap", o_bool, &refuse_mschap, - "Don't agree to auth to peer with MS-CHAP", OPT_A2CLRB | MDTYPE_MICROSOFT, - &lcp_allowoptions[0].chap_mdtype }, + "Don't agree to auth to peer with MS-CHAP", + OPT_A2CLRB | MDTYPE_MICROSOFT, &lcp_allowoptions[0].chap_mdtype }, { "-mschap", o_bool, &refuse_mschap, "Don't allow MS-CHAP authentication with peer", OPT_ALIAS | OPT_A2CLRB | MDTYPE_MICROSOFT, &lcp_allowoptions[0].chap_mdtype }, + { "refuse-mschap-v2", o_bool, &refuse_mschap_v2, + "Don't agree to auth to peer with MS-CHAPv2", + OPT_A2CLRB | MDTYPE_MICROSOFT_V2, &lcp_allowoptions[0].chap_mdtype }, + { "-mschap-v2", o_bool, &refuse_mschap_v2, + "Don't allow MS-CHAPv2 authentication with peer", + OPT_ALIAS | OPT_A2CLRB | MDTYPE_MICROSOFT_V2, + &lcp_allowoptions[0].chap_mdtype }, #endif { "name", o_string, our_name, @@ -917,7 +934,7 @@ auth_reset(unit) lcp_options *ao = &lcp_allowoptions[0]; ao->neg_upap = !refuse_pap && (passwd[0] != 0 || get_pap_passwd(NULL)); - ao->neg_chap = (!refuse_chap || !refuse_mschap) + ao->neg_chap = (!refuse_chap || !refuse_mschap || !refuse_mschap_v2) && (passwd[0] != 0 || have_chap_secret(user, (explicit_remote? remote_name: NULL), 0, NULL)); diff --git a/pppd/chap.c b/pppd/chap.c index 208a144..21b0280 100644 --- a/pppd/chap.c +++ b/pppd/chap.c @@ -33,13 +33,14 @@ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. */ -#define RCSID "$Id: chap.c,v 1.28 2002/03/04 14:59:51 dfs Exp $" +#define RCSID "$Id: chap.c,v 1.29 2002/03/05 15:14:04 dfs Exp $" /* * TODO: */ #include +#include #include #include #include @@ -483,9 +484,17 @@ ChapReceiveChallenge(cstate, inp, id, len) #ifdef CHAPMS case CHAP_MICROSOFT: - ChapMS(cstate, rchallenge, rchallenge_len, secret, secret_len); + ChapMS(cstate, rchallenge, secret, secret_len, + (MS_ChapResponse *) cstate->response); + cstate->resp_length = MS_CHAP_RESPONSE_LEN; break; -#endif + + case CHAP_MICROSOFT_V2: + ChapMS2(cstate, rchallenge, NULL, cstate->resp_name, secret, secret_len, + (MS_Chap2Response *) cstate->response, cstate->earesponse); + cstate->resp_length = MS_CHAP2_RESPONSE_LEN; + break; +#endif /* CHAPMS */ default: CHAPDEBUG(("unknown digest type %d", cstate->resp_type)); @@ -589,7 +598,7 @@ ChapReceiveResponse(cstate, inp, id, len) MD5Final(hash, &mdContext); /* compare MDs and send the appropriate status */ - if (memcmp (hash, remmd, MD5_SIGNATURE_SIZE) == 0) + if (memcmp(hash, remmd, MD5_SIGNATURE_SIZE) == 0) code = CHAP_SUCCESS; /* they are the same! */ break; @@ -598,27 +607,55 @@ ChapReceiveResponse(cstate, inp, id, len) { int response_offset, response_size; MS_ChapResponse *rmd = (MS_ChapResponse *) remmd; + MS_ChapResponse md; if (remmd_len != MS_CHAP_RESPONSE_LEN) break; /* not even the right length */ - ChapMS(cstate, cstate->challenge, cstate->chal_len, - secret, secret_len); /* Determine which part of response to verify against */ if (rmd->UseNT[0]) { response_offset = offsetof(MS_ChapResponse, NTResp); response_size = sizeof(rmd->NTResp); } else { +#ifdef MSLANMAN response_offset = offsetof(MS_ChapResponse, LANManResp); response_size = sizeof(rmd->LANManResp); +#else + /* Should really propagate this into the error packet. */ + notice("Peer request for LANMAN auth not supported"); + break; +#endif /* MSLANMAN */ } + /* Generate the expected response. */ + ChapMS(cstate, cstate->challenge, secret, secret_len, &md); + /* compare MDs and send the appropriate status */ - if (memcmp(cstate->response + response_offset, - remmd + response_offset, response_size) == 0) + if (memcmp(&md + response_offset, + remmd + response_offset, response_size) == 0) code = CHAP_SUCCESS; /* they are the same! */ break; } + + case CHAP_MICROSOFT_V2: + { + MS_Chap2Response *rmd = (MS_Chap2Response *) remmd; + MS_Chap2Response md; + + if (remmd_len != MS_CHAP2_RESPONSE_LEN) + break; /* not even the right length */ + + /* Generate the expected response and our mutual auth. */ + ChapMS2(cstate, cstate->challenge, rmd->PeerChallenge, + (explicit_remote? remote_name: rhostname), + secret, secret_len, &md, + cstate->saresponse); + + /* compare MDs and send the appropriate status */ + if (memcmp(md.NTResp, rmd->NTResp, sizeof(md.NTResp)) == 0) + code = CHAP_SUCCESS; /* yay! */ + break; + } #endif /* CHAPMS */ default: @@ -670,6 +707,37 @@ ChapReceiveSuccess(cstate, inp, id, len) UNTIMEOUT(ChapResponseTimeout, cstate); +#ifdef CHAPMS + /* + * For MS-CHAPv2, we must verify that the peer knows our secret. + */ + if (cstate->resp_type == CHAP_MICROSOFT_V2) { + if ((len >= MS_AUTH_RESPONSE_LENGTH + 2) && !strncmp(inp, "S=", 2)) { + inp += 2; len -= 2; + if (!memcmp(inp, cstate->earesponse, MS_AUTH_RESPONSE_LENGTH)) { + /* Authenticator Response matches. */ + inp += MS_AUTH_RESPONSE_LENGTH; /* Eat it */ + len -= MS_AUTH_RESPONSE_LENGTH; + if ((len >= 3) && !strncmp(inp, " M=", 3)) { + inp += 3; len -= 3; /* Eat the delimiter */ + } else if (len) { + /* Packet has extra text which does not begin " M=" */ + error("MS-CHAPv2 Success packet is badly formed."); + auth_withpeer_fail(cstate->unit, PPP_CHAP); + } + } else { + /* Authenticator Response did not match expected. */ + error("MS-CHAPv2 mutual authentication failed."); + auth_withpeer_fail(cstate->unit, PPP_CHAP); + } + } else { + /* Packet does not start with "S=" */ + error("MS-CHAPv2 Success packet is badly formed."); + auth_withpeer_fail(cstate->unit, PPP_CHAP); + } + } +#endif + /* * Print message. */ @@ -692,22 +760,103 @@ ChapReceiveFailure(cstate, inp, id, len) u_char id; int len; { + u_char *msg; + u_char *p = inp; + if (cstate->clientstate != CHAPCS_RESPONSE) { /* don't know what this is */ CHAPDEBUG(("ChapReceiveFailure: in state %d\n", cstate->clientstate)); return; } +#ifdef CHAPMS + /* We want a null-terminated string for strxxx(). */ + msg = malloc(len + 1); + if (!msg) { + p = NULL; + notice("Out of memory in ChapReceiveFailure"); + goto print_msg; + } + BCOPY(inp, msg, len); + p = msg + len; *p = '\0'; p = msg; +#endif + UNTIMEOUT(ChapResponseTimeout, cstate); +#ifdef CHAPMS + if ((cstate->resp_type == CHAP_MICROSOFT_V2) || + (cstate->resp_type == CHAP_MICROSOFT)) { + long error; + + /* + * Deal with MS-CHAP formatted failure messages; just print the + * M= part (if any). For MS-CHAP we're not really supposed + * to use M=, but it shouldn't hurt. See ChapSendStatus(). + */ + if (!strncmp(p, "E=", 2)) + error = strtol(p, NULL, 10); /* Remember the error code. */ + else + goto print_msg; /* Message is badly formatted. */ + + if (len && ((p = strstr(p, " M=")) != NULL)) { + /* M= field found. */ + p += 3; + } else { + /* No M=; use the error code. */ + switch(error - MS_CHAP_ERROR_BASE) { + case MS_CHAP_ERROR_RESTRICTED_LOGON_HOURS: + p = "Restricted logon hours"; + break; + + case MS_CHAP_ERROR_ACCT_DISABLED: + p = "Account disabled"; + break; + + case MS_CHAP_ERROR_PASSWD_EXPIRED: + p = "Password expired"; + break; + + case MS_CHAP_ERROR_NO_DIALIN_PERMISSION: + p = "No dialin permission"; + break; + + case MS_CHAP_ERROR_AUTHENTICATION_FAILURE: + p = "Authentication failure"; + break; + + case MS_CHAP_ERROR_CHANGING_PASSWORD: + /* Should never see this, we don't support Change Password. */ + p = "Error changing password"; + break; + + default: + free(msg); + p = msg = malloc(len + 33); + if (!msg) { + notice("Out of memory in ChapReceiveFailure"); + goto print_msg; + } + slprintf(p, len + 33, "Unknown authentication failure: %.*s", + len, inp); + break; + } + } + len = strlen(p); + } +#endif + /* * Print message. */ - if (len > 0) - PRINTMSG(inp, len); +print_msg: + if (len > 0 && p != NULL) + PRINTMSG(p, len); error("CHAP authentication failed"); auth_withpeer_fail(cstate->unit, PPP_CHAP); +#ifdef CHAPMS + if (msg) free(msg); +#endif } @@ -748,6 +897,7 @@ ChapSendChallenge(cstate) /* * ChapSendStatus - Send a status response (ack or nak). + * See RFC 2433 and RFC 2759 for MS-CHAP and MS-CHAPv2 message formats. */ static void ChapSendStatus(cstate, code) @@ -755,13 +905,63 @@ ChapSendStatus(cstate, code) int code; { u_char *outp; - int outlen, msglen; + int i, outlen, msglen; char msg[256]; + char *p, *q; - if (code == CHAP_SUCCESS) - slprintf(msg, sizeof(msg), "Welcome to %s.", hostname); - else - slprintf(msg, sizeof(msg), "I don't like you. Go 'way."); + p = msg; + q = p + sizeof(msg); /* points 1 byte past msg */ + + if (code == CHAP_SUCCESS) { +#ifdef CHAPMS + if (cstate->chal_type == CHAP_MICROSOFT_V2) { + /* + * Success message must be formatted as + * "S= M=" + * where + * is the Authenticator Response (mutual auth) + * is a text message + */ + slprintf(p, q - p, "S="); + p += 2; + slprintf(p, q - p, "%s", cstate->saresponse); + p += strlen(cstate->saresponse); + slprintf(p, q - p, " M="); + p += 3; + } +#endif /* CHAPMS */ + + slprintf(p, q - p, "Welcome to %s.", hostname); + } else { +#ifdef CHAPMS + if ((cstate->chal_type == CHAP_MICROSOFT_V2) || + (cstate->chal_type == CHAP_MICROSOFT)) { + /* + * Failure message must be formatted as + * "E=e R=r C=c V=v M=m" + * where + * e = error code (we use 691, ERROR_AUTHENTICATION_FAILURE) + * r = retry (we use 1, ok to retry) + * c = challenge to use for next response, we reuse previous + * v = Change Password version supported, we use 0 + * m = text message + * + * The M=m part is only for MS-CHAPv2, but MS-CHAP should ignore + * any extra text according to RFC 2433. So we'll go the easy + * (read: lazy) route and include it always. + */ + slprintf(p, q - p, "E=691 R=1 C="); + p += 12; + for (i = 0; i < cstate->chal_len; i++) + sprintf(p + i * 2, "%02X", cstate->challenge[i]); + p += cstate->chal_len * 2; + slprintf(p, q - p, " V=0 M="); + p += 7; + } +#endif /* CHAPMS */ + + slprintf(p, q - p, "I don't like you. Go 'way."); + } msglen = strlen(msg); outlen = CHAP_HEADERLEN + msglen; @@ -807,6 +1007,11 @@ ChapGenChallenge(cstate) /* MS-CHAP is fixed to an 8 octet challenge. */ chal_len = 8; break; + + case CHAP_MICROSOFT_V2: + /* MS-CHAPv2 is fixed to a 16 octet challenge. */ + chal_len = 16; + break; #endif default: fatal("ChapGenChallenge: Unsupported challenge type %d", diff --git a/pppd/chap.h b/pppd/chap.h index d15b948..13f0511 100644 --- a/pppd/chap.h +++ b/pppd/chap.h @@ -30,7 +30,7 @@ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. * - * $Id: chap.h,v 1.10 2002/03/01 14:39:18 dfs Exp $ + * $Id: chap.h,v 1.12 2002/04/02 13:54:59 dfs Exp $ */ #ifndef __CHAP_INCLUDE__ @@ -45,6 +45,7 @@ #define CHAP_DIGEST_MD5 5 /* use MD5 algorithm */ #define MD5_SIGNATURE_SIZE 16 /* 16 bytes in a MD5 message digest */ #define CHAP_MICROSOFT 0x80 /* use Microsoft-compatible alg. */ +#define CHAP_MICROSOFT_V2 0x81 /* use Microsoft-compatible alg. */ /* * Digest type and selection. @@ -52,10 +53,11 @@ /* bitmask of supported algorithms */ #define MDTYPE_MD5 0x1 -#define MDTYPE_MICROSOFT 0x2 +#define MDTYPE_MICROSOFT_V2 0x2 +#define MDTYPE_MICROSOFT 0x4 #ifdef CHAPMS -#define MDTYPE_ALL (MDTYPE_MD5 | MDTYPE_MICROSOFT) +#define MDTYPE_ALL (MDTYPE_MD5 | MDTYPE_MICROSOFT_V2 | MDTYPE_MICROSOFT) #else #define MDTYPE_ALL (MDTYPE_MD5) #endif @@ -64,6 +66,7 @@ /* Return the digest alg. ID for the most preferred digest type. */ #define CHAP_DIGEST(mdtype) \ ((mdtype) & MDTYPE_MD5)? CHAP_DIGEST_MD5: \ + ((mdtype) & MDTYPE_MICROSOFT_V2)? CHAP_MICROSOFT_V2: \ ((mdtype) & MDTYPE_MICROSOFT)? CHAP_MICROSOFT: \ 0 @@ -73,12 +76,14 @@ /* Return the bit flag for a given digest algorithm ID. */ #define CHAP_MDTYPE_D(digest) \ ((digest) == CHAP_DIGEST_MD5)? MDTYPE_MD5: \ + ((digest) == CHAP_MICROSOFT_V2)? MDTYPE_MICROSOFT_V2: \ ((digest) == CHAP_MICROSOFT)? MDTYPE_MICROSOFT: \ 0 /* Can we do the requested digest? */ #define CHAP_CANDIGEST(mdtype, digest) \ ((digest) == CHAP_DIGEST_MD5)? (mdtype) & MDTYPE_MD5: \ + ((digest) == CHAP_MICROSOFT_V2)? (mdtype) & MDTYPE_MICROSOFT_V2: \ ((digest) == CHAP_MICROSOFT)? (mdtype) & MDTYPE_MICROSOFT: \ 0 @@ -91,8 +96,10 @@ * Challenge lengths (for challenges we send) and other limits. */ #define MIN_CHALLENGE_LENGTH 16 -#define MAX_CHALLENGE_LENGTH 24 +#define MAX_CHALLENGE_LENGTH 24 /* sufficient for MS-CHAP Peer Chal. */ #define MAX_RESPONSE_LENGTH 64 /* sufficient for MD5 or MS-CHAP */ +#define MS_AUTH_RESPONSE_LENGTH 40 /* MS-CHAPv2 authenticator response, */ + /* as ASCII */ /* * Each interface is described by a chap structure. @@ -114,6 +121,9 @@ typedef struct chap_state { int chal_transmits; /* Number of transmissions of challenge */ int resp_transmits; /* Number of transmissions of response */ u_char response[MAX_RESPONSE_LENGTH]; /* Response to send */ + char saresponse[MS_AUTH_RESPONSE_LENGTH+1]; /* Auth response to send */ + char earesponse[MS_AUTH_RESPONSE_LENGTH+1]; /* Auth response expected */ + /* +1 for null terminator */ u_char resp_length; /* length of response */ u_char resp_id; /* ID for response messages */ u_char resp_type; /* hash algorithm for responses */ diff --git a/pppd/chap_ms.c b/pppd/chap_ms.c index c4b7d20..d10b99a 100644 --- a/pppd/chap_ms.c +++ b/pppd/chap_ms.c @@ -31,7 +31,14 @@ * You should also use DOMAIN\\USERNAME as described in README.MSCHAP80 */ -#define RCSID "$Id: chap_ms.c,v 1.17 2002/03/04 14:59:51 dfs Exp $" +/* + * Modifications by Frank Cusack, frank@google.com, March 2002. + * + * Implemented MS-CHAPv2 functionality. Heavily based on + * sample implementation in RFC 2759. + */ + +#define RCSID "$Id: chap_ms.c,v 1.18 2002/03/05 15:14:04 dfs Exp $" #ifdef CHAPMS @@ -50,6 +57,7 @@ #include "chap.h" #include "chap_ms.h" #include "md4.h" +#include "sha1.h" #ifndef USE_CRYPT #include @@ -58,13 +66,21 @@ static const char rcsid[] = RCSID; -static void ChallengeResponse __P((u_char *, u_char *, u_char *)); -static void DesEncrypt __P((u_char *, u_char *, u_char *)); +static void ChallengeHash __P((u_char[16], u_char *, char *, u_char[8])); +static void ascii2unicode __P((char[], int, u_char[])); +static void NTPasswordHash __P((char *, int, u_char[MD4_SIGNATURE_SIZE])); +static void ChallengeResponse __P((u_char *, u_char *, u_char[24])); +static void DesEncrypt __P((u_char *, u_char *, u_char[8])); static void MakeKey __P((u_char *, u_char *)); static u_char Get7Bits __P((u_char *, int)); -static void ChapMS_NT __P((char *, int, char *, int, MS_ChapResponse *)); +static void ChapMS_NT __P((u_char *, char *, int, u_char[24])); +static void ChapMS2_NT __P((char *, u_char[16], char *, char *, int, + u_char[24])); +static void GenerateAuthenticatorResponse __P((char*, int, u_char[24], + u_char[16], u_char *, + char *, u_char[41])); #ifdef MSLANMAN -static void ChapMS_LANMan __P((char *, int, char *, int, MS_ChapResponse *)); +static void ChapMS_LANMan __P((u_char *, char *, int, MS_ChapResponse *)); #endif #ifdef USE_CRYPT @@ -72,30 +88,31 @@ static void Expand __P((u_char *, u_char *)); static void Collapse __P((u_char *, u_char *)); #endif +extern double drand48 __P((void)); + #ifdef MSLANMAN bool ms_lanman = 0; /* Use LanMan password instead of NT */ /* Has meaning only with MS-CHAP challenges */ #endif static void -ChallengeResponse(challenge, pwHash, response) - u_char *challenge; /* IN 8 octets */ - u_char *pwHash; /* IN 16 octets */ - u_char *response; /* OUT 24 octets */ +ChallengeResponse(u_char *challenge, + u_char PasswordHash[MD4_SIGNATURE_SIZE], + u_char response[24]) { char ZPasswordHash[21]; BZERO(ZPasswordHash, sizeof(ZPasswordHash)); - BCOPY(pwHash, ZPasswordHash, MD4_SIGNATURE_SIZE); + BCOPY(PasswordHash, ZPasswordHash, MD4_SIGNATURE_SIZE); #if 0 dbglog("ChallengeResponse - ZPasswordHash %.*B", sizeof(ZPasswordHash), ZPasswordHash); #endif - DesEncrypt(challenge, ZPasswordHash + 0, response + 0); - DesEncrypt(challenge, ZPasswordHash + 7, response + 8); - DesEncrypt(challenge, ZPasswordHash + 14, response + 16); + DesEncrypt(challenge, ZPasswordHash + 0, &response[0]); + DesEncrypt(challenge, ZPasswordHash + 7, &response[8]); + DesEncrypt(challenge, ZPasswordHash + 14, &response[16]); #if 0 dbglog("ChallengeResponse - response %.24B", response); @@ -105,10 +122,7 @@ ChallengeResponse(challenge, pwHash, response) #ifdef USE_CRYPT static void -DesEncrypt(clear, key, cipher) - u_char *clear; /* IN 8 octets */ - u_char *key; /* IN 7 octets */ - u_char *cipher; /* OUT 8 octets */ +DesEncrypt(u_char *clear, u_char *key, u_char cipher[8]) { u_char des_key[8]; u_char crypt_key[66]; @@ -135,10 +149,7 @@ DesEncrypt(clear, key, cipher) #else /* USE_CRYPT */ static void -DesEncrypt(clear, key, cipher) - u_char *clear; /* IN 8 octets */ - u_char *key; /* IN 7 octets */ - u_char *cipher; /* OUT 8 octets */ +DesEncrypt(u_char *clear, u_char *key, u_char cipher[8]) { des_cblock des_key; des_key_schedule key_schedule; @@ -161,9 +172,7 @@ DesEncrypt(clear, key, cipher) #endif /* USE_CRYPT */ -static u_char Get7Bits(input, startBit) - u_char *input; - int startBit; +static u_char Get7Bits(u_char *input, int startBit) { register unsigned int word; @@ -181,9 +190,7 @@ static u_char Get7Bits(input, startBit) * out == 64-byte string where each byte is either 1 or 0 * Note that the low-order "bit" is always ignored by by setkey() */ -static void Expand(in, out) - u_char *in; - u_char *out; +static void Expand(u_char *in, u_char *out) { int j, c; int i; @@ -198,9 +205,7 @@ static void Expand(in, out) /* The inverse of Expand */ -static void Collapse(in, out) - u_char *in; - u_char *out; +static void Collapse(u_char *in, u_char *out) { int j; int i; @@ -215,9 +220,7 @@ static void Collapse(in, out) } #endif -static void MakeKey(key, des_key) - u_char *key; /* IN 56 bit DES key missing parity bits */ - u_char *des_key; /* OUT 64 bit DES key with parity bits added */ +static void MakeKey(u_char *key, u_char *des_key) { des_key[0] = Get7Bits(key, 0); des_key[1] = Get7Bits(key, 7); @@ -238,49 +241,95 @@ static void MakeKey(key, des_key) #endif } + static void -ChapMS_NT(rchallenge, rchallenge_len, secret, secret_len, response) - char *rchallenge; - int rchallenge_len; - char *secret; - int secret_len; - MS_ChapResponse *response; +ChallengeHash(u_char PeerChallenge[16], u_char *rchallenge, + char *username, u_char Challenge[8]) + +{ + SHA1_CTX sha1Context; + u_char sha1Hash[SHA1_SIGNATURE_SIZE]; + + SHA1_Init(&sha1Context); + SHA1_Update(&sha1Context, PeerChallenge, 16); + SHA1_Update(&sha1Context, rchallenge, 16); + SHA1_Update(&sha1Context, username, strlen(username)); + SHA1_Final(sha1Hash, &sha1Context); + + BCOPY(sha1Hash, Challenge, 8); +} + +/* + * Convert the ASCII version of the password to Unicode. + * This implicitly supports 8-bit ISO8859/1 characters. + * This gives us the little-endian representation, which + * is assumed by all M$ CHAP RFCs. (Unicode byte ordering + * is machine-dependent.) + */ +static void +ascii2unicode(char ascii[], int ascii_len, u_char unicode[]) +{ + int i; + + BZERO(unicode, ascii_len * 2); + for (i = 0; i < ascii_len; i++) + unicode[i * 2] = (u_char) ascii[i]; +} + +static void +NTPasswordHash(char *secret, int secret_len, u_char hash[MD4_SIGNATURE_SIZE]) { - int i; #ifdef __NetBSD__ /* NetBSD uses the libc md4 routines which take bytes instead of bits */ - int mdlen = secret_len * 2; + int mdlen = secret_len; #else - int mdlen = secret_len * 2 * 8; + int mdlen = secret_len * 8; #endif MD4_CTX md4Context; - u_char hash[MD4_SIGNATURE_SIZE]; - u_char unicodePassword[MAX_NT_PASSWORD * 2]; - - /* Initialize the Unicode version of the secret (== password). */ - /* This implicitly supports 8-bit ISO8859/1 characters. */ - BZERO(unicodePassword, sizeof(unicodePassword)); - for (i = 0; i < secret_len; i++) - unicodePassword[i * 2] = (u_char)secret[i]; MD4Init(&md4Context); - MD4Update(&md4Context, unicodePassword, mdlen); + MD4Update(&md4Context, secret, mdlen); + MD4Final(hash, &md4Context); + +} + +static void +ChapMS_NT(u_char *rchallenge, char *secret, int secret_len, + u_char NTResponse[24]) +{ + u_char unicodePassword[MAX_NT_PASSWORD * 2]; + u_char PasswordHash[MD4_SIGNATURE_SIZE]; - MD4Final(hash, &md4Context); /* Tell MD4 we're done */ + /* Hash the Unicode version of the secret (== password). */ + ascii2unicode(secret, secret_len, unicodePassword); + NTPasswordHash(unicodePassword, secret_len * 2, PasswordHash); - ChallengeResponse(rchallenge, hash, response->NTResp); + ChallengeResponse(rchallenge, PasswordHash, NTResponse); +} + +static void +ChapMS2_NT(char *rchallenge, u_char PeerChallenge[16], char *username, + char *secret, int secret_len, u_char NTResponse[24]) +{ + u_char unicodePassword[MAX_NT_PASSWORD * 2]; + u_char PasswordHash[MD4_SIGNATURE_SIZE]; + u_char Challenge[8]; + + ChallengeHash(PeerChallenge, rchallenge, username, Challenge); + + /* Hash the Unicode version of the secret (== password). */ + ascii2unicode(secret, secret_len, unicodePassword); + NTPasswordHash(unicodePassword, secret_len * 2, PasswordHash); + + ChallengeResponse(Challenge, PasswordHash, NTResponse); } #ifdef MSLANMAN static u_char *StdText = (u_char *)"KGS!@#$%"; /* key from rasapi32.dll */ static void -ChapMS_LANMan(rchallenge, rchallenge_len, secret, secret_len, response) - char *rchallenge; - int rchallenge_len; - char *secret; - int secret_len; - MS_ChapResponse *response; +ChapMS_LANMan(u_char *rchallenge, char *secret, int secret_len, + u_char LMResponse[24]) { int i; u_char UcasePassword[MAX_NT_PASSWORD]; /* max is actually 14 */ @@ -292,39 +341,125 @@ ChapMS_LANMan(rchallenge, rchallenge_len, secret, secret_len, response) UcasePassword[i] = (u_char)toupper(secret[i]); DesEncrypt( StdText, UcasePassword + 0, PasswordHash + 0 ); DesEncrypt( StdText, UcasePassword + 7, PasswordHash + 8 ); - ChallengeResponse(rchallenge, PasswordHash, response->LANManResp); + ChallengeResponse(rchallenge, PasswordHash, LMResponse); } #endif -void -ChapMS(cstate, rchallenge, rchallenge_len, secret, secret_len) - chap_state *cstate; - char *rchallenge; - int rchallenge_len; - char *secret; - int secret_len; + +static void +GenerateAuthenticatorResponse(char *secret, int secret_len, + u_char NTResponse[24], u_char PeerChallenge[16], + u_char *rchallenge, char *username, + u_char authResponse[MS_AUTH_RESPONSE_LENGTH+1]) { - MS_ChapResponse response; + /* + * "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}; + 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}; + + int i; + SHA1_CTX sha1Context; + u_char unicodePassword[MAX_NT_PASSWORD * 2]; + u_char PasswordHash[MD4_SIGNATURE_SIZE]; + u_char PasswordHashHash[MD4_SIGNATURE_SIZE]; + u_char Digest[SHA1_SIGNATURE_SIZE]; + u_char Challenge[8]; + + /* 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(Digest, &sha1Context); + + ChallengeHash(PeerChallenge, rchallenge, username, Challenge); + + SHA1_Init(&sha1Context); + SHA1_Update(&sha1Context, Digest, sizeof(Digest)); + SHA1_Update(&sha1Context, Challenge, sizeof(Challenge)); + SHA1_Update(&sha1Context, Magic2, sizeof(Magic2)); + SHA1_Final(Digest, &sha1Context); + + /* Convert to ASCII hex string. */ + for (i = 0; i < MAX((MS_AUTH_RESPONSE_LENGTH / 2), sizeof(Digest)); i++) + sprintf(&authResponse[i * 2], "%02X", Digest[i]); +} + +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, rchallenge_len, secret, secret_len, &response); + ChapMS_NT(rchallenge, secret, secret_len, response->NTResp); #ifdef MSLANMAN - ChapMS_LANMan(rchallenge, rchallenge_len, secret, secret_len, &response); + ChapMS_LANMan(rchallenge, secret, secret_len, response); /* prefered method is set by option */ - response.UseNT[0] = !ms_lanman; + response->UseNT[0] = !ms_lanman; #else - response.UseNT[0] = 1; + response->UseNT[0] = 1; #endif - BCOPY(&response, cstate->response, MS_CHAP_RESPONSE_LEN); - cstate->resp_length = MS_CHAP_RESPONSE_LEN; } + +/* + * 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. + * + * 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 *p = response->PeerChallenge; + int i; + + BZERO(response, sizeof(response)); + + /* Generate the Peer-Challenge if requested, or copy it if supplied. */ + if (!PeerChallenge) + for (i = 0; i < sizeof(response->PeerChallenge); i++) + *p++ = (u_char) (drand48() * 0xff); + else + BCOPY(PeerChallenge, response->PeerChallenge, + sizeof(response->PeerChallenge)); + + /* Generate the NT-Response */ + ChapMS2_NT(rchallenge, response->PeerChallenge, user, + secret, secret_len, response->NTResp); + + /* Generate the Authenticator Response. */ + GenerateAuthenticatorResponse(secret, secret_len, response->NTResp, + response->PeerChallenge, rchallenge, + user, authResponse); +} + + #endif /* CHAPMS */ diff --git a/pppd/chap_ms.h b/pppd/chap_ms.h index 4418c93..a8a1395 100644 --- a/pppd/chap_ms.h +++ b/pppd/chap_ms.h @@ -19,7 +19,7 @@ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. * - * $Id: chap_ms.h,v 1.4 2002/03/04 14:59:51 dfs Exp $ + * $Id: chap_ms.h,v 1.5 2002/03/05 15:14:04 dfs Exp $ */ #ifndef __CHAPMS_INCLUDE__ @@ -28,6 +28,19 @@ #define MAX_NT_PASSWORD 256 /* Max (Unicode) chars in an NT pass */ #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) /* * Use MS_CHAP_RESPONSE_LEN, rather than sizeof(MS_ChapResponse), @@ -39,7 +52,20 @@ typedef struct { u_char UseNT[1]; /* If 1, ignore the LANMan response field */ } MS_ChapResponse; -void ChapMS __P((chap_state *, char *, int, char *, int)); +/* + * Use MS_CHAP2_RESPONSE_LEN, rather than sizeof(MS_Chap2Response), + * in case this struct gets padded. + */ +typedef struct { + u_char PeerChallenge[16]; + u_char Reserved[8]; /* Must be zero */ + u_char NTResp[24]; + u_char Flags[1]; /* Must be zero */ +} MS_Chap2Response; + +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])); #define __CHAPMS_INCLUDE__ #endif /* __CHAPMS_INCLUDE__ */ diff --git a/pppd/lcp.c b/pppd/lcp.c index 8cf6029..050ad20 100644 --- a/pppd/lcp.c +++ b/pppd/lcp.c @@ -17,7 +17,7 @@ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. */ -#define RCSID "$Id: lcp.c,v 1.58 2002/03/01 14:39:18 dfs Exp $" +#define RCSID "$Id: lcp.c,v 1.60 2002/04/02 13:54:59 dfs Exp $" /* * TODO: @@ -1943,7 +1943,12 @@ lcp_printpkt(p, plen, printer, arg) break; #ifdef CHAPMS case CHAP_MICROSOFT: - printer(arg, " m$oft"); + printer(arg, " MS"); + ++p; + break; + + case CHAP_MICROSOFT_V2: + printer(arg, " MS-v2"); ++p; break; #endif diff --git a/pppd/plugins/radius/pppd-radius.8 b/pppd/plugins/radius/pppd-radius.8 index 5e9bc1a..516c96d 100644 --- a/pppd/plugins/radius/pppd-radius.8 +++ b/pppd/plugins/radius/pppd-radius.8 @@ -1,5 +1,5 @@ .\" manual page [] for RADIUS plugin for pppd 2.4 -.\" $Id: pppd-radius.8,v 1.2 2002/03/04 14:59:51 dfs Exp $ +.\" $Id: pppd-radius.8,v 1.4 2002/04/02 13:55:00 dfs Exp $ .\" SH section heading .\" SS subsection heading .\" LP paragraph @@ -17,8 +17,8 @@ radius.so \- RADIUS authentication plugin for plugin radius.so .SH DESCRIPTION .LP -The RADIUS plugin for pppd permits pppd to perform PAP, CHAP, and MS-CHAP -authentication against a RADIUS server instead of the usual +The RADIUS plugin for pppd permits pppd to perform PAP, CHAP, MS-CHAP and +MS-CHAPv2 authentication against a RADIUS server instead of the usual .I /etc/ppp/pap-secrets and .I /etc/ppp/chap-secrets diff --git a/pppd/plugins/radius/radius.c b/pppd/plugins/radius/radius.c index 358fc95..a36c343 100644 --- a/pppd/plugins/radius/radius.c +++ b/pppd/plugins/radius/radius.c @@ -2,8 +2,8 @@ * * radius.c * -* RADIUS plugin for pppd. Performs PAP, CHAP and MS-CHAP authentication -* using RADIUS. +* RADIUS plugin for pppd. Performs PAP, CHAP, MS-CHAP, MS-CHAPv2 +* authentication using RADIUS. * * Copyright (C) 2002 Roaring Penguin Software Inc. * @@ -21,7 +21,7 @@ * ***********************************************************************/ static char const RCSID[] = -"$Id: radius.c,v 1.5 2002/03/04 14:59:51 dfs Exp $"; +"$Id: radius.c,v 1.6 2002/03/05 15:14:05 dfs Exp $"; #include "pppd.h" #include "chap.h" @@ -58,7 +58,7 @@ static int radius_chap_auth(char *user, static void radius_ip_up(void *opaque, int arg); static void radius_ip_down(void *opaque, int arg); static void make_username_realm(char *user); -static int radius_setparams(VALUE_PAIR *vp, char *msg); +static int radius_setparams(chap_state *cstate, VALUE_PAIR *vp, char *msg); static void radius_choose_ip(u_int32_t *addrp); static int radius_init(char *msg); static int get_client_port(char *ifname); @@ -233,7 +233,7 @@ radius_pap_auth(char *user, } if (result == OK_RC) { - if (radius_setparams(received, radius_msg) < 0) { + if (radius_setparams(NULL, received, radius_msg) < 0) { result = ERROR_RC; } } @@ -255,7 +255,7 @@ radius_pap_auth(char *user, * %RETURNS: * CHAP_SUCCESS if we can authenticate, CHAP_FAILURE if we cannot. * %DESCRIPTION: -* Performs CHAP and MS-CHAP authentication using RADIUS +* Performs CHAP, MS-CHAP and MS-CHAPv2 authentication using RADIUS ***********************************************************************/ static int radius_chap_auth(char *user, @@ -279,6 +279,7 @@ radius_chap_auth(char *user, if ((cstate->chal_type != CHAP_DIGEST_MD5) #ifdef CHAPMS && (cstate->chal_type != CHAP_MICROSOFT) + && (cstate->chal_type != CHAP_MICROSOFT_V2) #endif ) { error("RADIUS: Challenge type %u unsupported", cstate->chal_type); @@ -325,7 +326,7 @@ radius_chap_auth(char *user, case CHAP_MICROSOFT: { /* MS-CHAP-Challenge and MS-CHAP-Response */ - MS_ChapResponse *rmd = remmd; + MS_ChapResponse *rmd = (MS_ChapResponse *) remmd; u_char *p = cpassword; *p++ = cstate->chal_id; @@ -342,6 +343,29 @@ radius_chap_auth(char *user, cpassword, MS_CHAP_RESPONSE_LEN + 1, VENDOR_MICROSOFT); break; } + + case CHAP_MICROSOFT_V2: + { + /* MS-CHAP-Challenge and MS-CHAP2-Response */ + MS_Chap2Response *rmd = (MS_Chap2Response *) remmd; + u_char *p = cpassword; + + *p++ = cstate->chal_id; + /* The idiots use a different field order in RADIUS than PPP */ + memcpy(p, rmd->Flags, sizeof(rmd->Flags)); + p += sizeof(rmd->Flags); + memcpy(p, rmd->PeerChallenge, sizeof(rmd->PeerChallenge)); + p += sizeof(rmd->PeerChallenge); + memcpy(p, rmd->Reserved, sizeof(rmd->Reserved)); + p += sizeof(rmd->Reserved); + memcpy(p, rmd->NTResp, sizeof(rmd->NTResp)); + + rc_avpair_add(&send, PW_MS_CHAP_CHALLENGE, + cstate->challenge, cstate->chal_len, VENDOR_MICROSOFT); + rc_avpair_add(&send, PW_MS_CHAP2_RESPONSE, + cpassword, MS_CHAP2_RESPONSE_LEN + 1, VENDOR_MICROSOFT); + break; + } #endif } @@ -360,7 +384,7 @@ radius_chap_auth(char *user, if (result == OK_RC) { if (!rstate.done_chap_once) { - if (radius_setparams(received, radius_msg) < 0) { + if (radius_setparams(cstate, received, radius_msg) < 0) { error("%s", radius_msg); result = ERROR_RC; } else { @@ -408,18 +432,20 @@ make_username_realm(char *user) /********************************************************************** * %FUNCTION: radius_setparams * %ARGUMENTS: +* cstate -- pppd's chap_state structure * vp -- received value-pairs * msg -- buffer in which to place error message. Holds up to BUF_LEN chars * %RETURNS: * >= 0 on success; -1 on failure * %DESCRIPTION: * Parses attributes sent by RADIUS server and sets them in pppd. Currently, -* used only to set IP address. +* used only to set IP address and MS-CHAPv2 Authenticator Response. ***********************************************************************/ static int -radius_setparams(VALUE_PAIR *vp, char *msg) +radius_setparams(chap_state *cstate, VALUE_PAIR *vp, char *msg) { u_int32_t remote; + int ms_chap2_success = 0; /* Send RADIUS attributes to anyone else who might be interested */ if (radius_attributes_hook) { @@ -432,49 +458,62 @@ radius_setparams(VALUE_PAIR *vp, char *msg) */ while (vp) { - if (vp->vendorcode == VENDOR_NONE) { - switch (vp->attribute) { - case PW_SERVICE_TYPE: - /* check for service type */ - /* if not FRAMED then exit */ - if (vp->lvalue != PW_FRAMED) { - slprintf(msg, BUF_LEN, "RADIUS: wrong service type %ld for %s", - vp->lvalue, rstate.user); - return -1; - } - break; - case PW_FRAMED_PROTOCOL: - /* check for framed protocol type */ - /* if not PPP then also exit */ - if (vp->lvalue != PW_PPP) { - slprintf(msg, BUF_LEN, "RADIUS: wrong framed protocol %ld for %s", - vp->lvalue, rstate.user); + if ((vp->attribute == PW_SERVICE_TYPE) && + (vp->vendorcode == VENDOR_NONE)) { + /* check for service type */ + /* if not FRAMED then exit */ + if (vp->lvalue != PW_FRAMED) { + slprintf(msg, BUF_LEN, "RADIUS: wrong service type %ld for %s", + vp->lvalue, rstate.user); + return -1; + } + } else if ((vp->attribute == PW_FRAMED_PROTOCOL) && + (vp->vendorcode == VENDOR_NONE)) { + /* check for framed protocol type */ + /* if not PPP then also exit */ + if (vp->lvalue != PW_PPP) { + slprintf(msg, BUF_LEN, "RADIUS: wrong framed protocol %ld for %s", + vp->lvalue, rstate.user); + return -1; + } + } else if ((vp->attribute == PW_FRAMED_IP_ADDRESS) && + (vp->vendorcode == VENDOR_NONE)) { + /* seting up remote IP addresses */ + remote = vp->lvalue; + if (remote == 0xffffffff) { + /* 0xffffffff means user should be allowed to select one */ + rstate.any_ip_addr_ok = 1; + } else if (remote != 0xfffffffe) { + /* 0xfffffffe means NAS should select an ip address */ + remote = htonl(vp->lvalue); + if (bad_ip_adrs (remote)) { + slprintf(msg, BUF_LEN, "RADIUS: bad remote IP address %I for %s", + remote, rstate.user); return -1; } - break; - - case PW_FRAMED_IP_ADDRESS: - /* seting up remote IP addresses */ - remote = vp->lvalue; - if (remote == 0xffffffff) { - /* 0xffffffff means user should be allowed to select one */ - rstate.any_ip_addr_ok = 1; - } else if (remote != 0xfffffffe) { - /* 0xfffffffe means NAS should select an ip address */ - remote = htonl(vp->lvalue); - if (bad_ip_adrs (remote)) { - slprintf(msg, BUF_LEN, "RADIUS: bad remote IP address %I for %s", - remote, rstate.user); - return -1; - } - rstate.choose_ip = 1; - rstate.ip_addr = remote; - } - break; + rstate.choose_ip = 1; + rstate.ip_addr = remote; } +#ifdef CHAPMS + } else if ((vp->attribute == PW_MS_CHAP2_SUCCESS) && + (vp->vendorcode == VENDOR_MICROSOFT)) { + if ((vp->lvalue != 43) || strncmp(vp->strvalue + 1, "S=", 2)) { + slprintf(msg, BUF_LEN, "RADIUS: bad MS-CHAP2-Success packet"); + return -1; + } + memcpy(cstate->saresponse, vp->strvalue + 3, + MS_AUTH_RESPONSE_LENGTH); + cstate->saresponse[MS_AUTH_RESPONSE_LENGTH] = '\0'; + ms_chap2_success = 1; +#endif /* CHAPMS */ } vp = vp->next; } + + /* Require a valid MS-CHAP2-SUCCESS for MS-CHAPv2 auth */ + if (cstate && (cstate->chal_type == CHAP_MICROSOFT_V2) && !ms_chap2_success) + return -1; + return 0; } diff --git a/pppd/plugins/radius/radiusclient/etc/dictionary b/pppd/plugins/radius/radiusclient/etc/dictionary index 7b3e7b0..b8d7771 100644 --- a/pppd/plugins/radius/radiusclient/etc/dictionary +++ b/pppd/plugins/radius/radiusclient/etc/dictionary @@ -228,3 +228,4 @@ VALUE Add-Port-To-IP-Address Yes 1 #VALUE Server-Config Password-Expiration 30 #VALUE Server-Config Password-Warning 5 +INCLUDE /etc/radiusclient/dictionary.microsoft diff --git a/pppd/plugins/radius/radiusclient/include/radiusclient.h b/pppd/plugins/radius/radiusclient/include/radiusclient.h index 141e690..18106a6 100644 --- a/pppd/plugins/radius/radiusclient/include/radiusclient.h +++ b/pppd/plugins/radius/radiusclient/include/radiusclient.h @@ -1,5 +1,5 @@ /* - * $Id: radiusclient.h,v 1.4 2002/03/04 14:59:52 dfs Exp $ + * $Id: radiusclient.h,v 1.5 2002/03/05 15:14:06 dfs Exp $ * * Copyright (C) 1995,1996,1997,1998 Lars Fenneberg * @@ -157,6 +157,8 @@ typedef struct pw_auth_hdr /* Vendor RADIUS attribute-value pairs */ #define PW_MS_CHAP_CHALLENGE 11 /* string */ #define PW_MS_CHAP_RESPONSE 1 /* string */ +#define PW_MS_CHAP2_RESPONSE 25 /* string */ +#define PW_MS_CHAP2_SUCCESS 26 /* string */ /* Accounting */ diff --git a/pppd/plugins/radius/radiusclient/lib/dict.c b/pppd/plugins/radius/radiusclient/lib/dict.c index 95a0838..a619630 100644 --- a/pppd/plugins/radius/radiusclient/lib/dict.c +++ b/pppd/plugins/radius/radiusclient/lib/dict.c @@ -1,5 +1,5 @@ /* - * $Id: dict.c,v 1.1 2002/01/22 16:03:02 dfs Exp $ + * $Id: dict.c,v 1.2 2002/03/05 15:14:06 dfs Exp $ * * Copyright (C) 2002 Roaring Penguin Software Inc. * @@ -51,7 +51,7 @@ int rc_read_dictionary (char *filename) int value; int type; int n; - + int retcode; if ((dictfd = fopen (filename, "r")) == (FILE *) NULL) { rc_log(LOG_ERR, "rc_read_dictionary: couldn't open dictionary %s: %s", @@ -60,6 +60,7 @@ int rc_read_dictionary (char *filename) } line_no = 0; + retcode = 0; while (fgets (buffer, sizeof (buffer), dictfd) != (char *) NULL) { line_no++; @@ -75,19 +76,22 @@ int rc_read_dictionary (char *filename) if (sscanf(buffer, "%s%s%d", dummystr, namestr, &value) != 3) { rc_log(LOG_ERR, "rc_read_dictionary: invalid vendor on line %d of dictionary %s", line_no, filename); - return (-1); + retcode = -1; + break; } /* Validate entry */ if (strlen (namestr) > NAME_LENGTH) { rc_log(LOG_ERR, "rc_read_dictionary: invalid name length on line %d of dictionary %s", line_no, filename); - return (-1); + retcode = -1; + break; } /* Create new vendor entry */ vdict = (VENDOR_DICT *) malloc (sizeof (VENDOR_DICT)); if (!vdict) { rc_log(LOG_CRIT, "rc_read_dictionary: out of memory"); - return (-1); + retcode = -1; + break; } strcpy(vdict->vendorname, namestr); vdict->vendorcode = value; @@ -107,7 +111,8 @@ int rc_read_dictionary (char *filename) { rc_log(LOG_ERR, "rc_read_dictionary: invalid attribute on line %d of dictionary %s", line_no, filename); - return (-1); + retcode = -1; + break; } /* @@ -117,14 +122,16 @@ int rc_read_dictionary (char *filename) { rc_log(LOG_ERR, "rc_read_dictionary: invalid name length on line %d of dictionary %s", line_no, filename); - return (-1); + retcode = -1; + break; } if (strlen (vendorstr) > NAME_LENGTH) { rc_log(LOG_ERR, "rc_read_dictionary: invalid name length on line %d of dictionary %s", line_no, filename); - return (-1); + retcode = -1; + break; } if (!isdigit (*valstr)) @@ -132,7 +139,8 @@ int rc_read_dictionary (char *filename) rc_log(LOG_ERR, "rc_read_dictionary: invalid value on line %d of dictionary %s", line_no, filename); - return (-1); + retcode = -1; + break; } value = atoi (valstr); @@ -157,7 +165,8 @@ int rc_read_dictionary (char *filename) rc_log(LOG_ERR, "rc_read_dictionary: invalid type on line %d of dictionary %s", line_no, filename); - return (-1); + retcode = -1; + break; } /* Search for vendor if supplied */ @@ -167,7 +176,8 @@ int rc_read_dictionary (char *filename) rc_log(LOG_ERR, "rc_read_dictionary: unknown vendor on line %d of dictionary %s", line_no, filename); - return (-1); + retcode = -1; + break; } } else { vdict = NULL; @@ -178,7 +188,8 @@ int rc_read_dictionary (char *filename) == (DICT_ATTR *) NULL) { rc_log(LOG_CRIT, "rc_read_dictionary: out of memory"); - return (-1); + retcode = -1; + break; } strcpy (attr->name, namestr); if (vdict) { @@ -207,7 +218,8 @@ int rc_read_dictionary (char *filename) rc_log(LOG_ERR, "rc_read_dictionary: invalid value entry on line %d of dictionary %s", line_no, filename); - return (-1); + retcode = -1; + break; } /* @@ -218,7 +230,8 @@ int rc_read_dictionary (char *filename) rc_log(LOG_ERR, "rc_read_dictionary: invalid attribute length on line %d of dictionary %s", line_no, filename); - return (-1); + retcode = -1; + break; } if (strlen (namestr) > NAME_LENGTH) @@ -226,7 +239,8 @@ int rc_read_dictionary (char *filename) rc_log(LOG_ERR, "rc_read_dictionary: invalid name length on line %d of dictionary %s", line_no, filename); - return (-1); + retcode = -1; + break; } if (!isdigit (*valstr)) @@ -234,7 +248,8 @@ int rc_read_dictionary (char *filename) rc_log(LOG_ERR, "rc_read_dictionary: invalid value on line %d of dictionary %s", line_no, filename); - return (-1); + retcode = -1; + break; } value = atoi (valstr); @@ -244,7 +259,8 @@ int rc_read_dictionary (char *filename) == (DICT_VALUE *) NULL) { rc_log(LOG_CRIT, "rc_read_dictionary: out of memory"); - return (-1); + retcode = -1; + break; } strcpy (dval->attrname, attrstr); strcpy (dval->name, namestr); @@ -254,9 +270,26 @@ int rc_read_dictionary (char *filename) dval->next = dictionary_values; dictionary_values = dval; } + else if (strncmp (buffer, "INCLUDE", 7) == 0) + { + /* Read the INCLUDE line */ + if (sscanf (buffer, "%s%s", dummystr, namestr) != 2) + { + rc_log(LOG_ERR, + "rc_read_dictionary: invalid include entry on line %d of dictionary %s", + line_no, filename); + retcode = -1; + break; + } + if (rc_read_dictionary(namestr) == -1) + { + retcode = -1; + break; + } + } } fclose (dictfd); - return (0); + return retcode; } /* diff --git a/pppd/pppd.8 b/pppd/pppd.8 index 229708a..f6adc5d 100644 --- a/pppd/pppd.8 +++ b/pppd/pppd.8 @@ -1,5 +1,5 @@ .\" manual page [] for pppd 2.4 -.\" $Id: pppd.8,v 1.60 2002/03/01 14:39:18 dfs Exp $ +.\" $Id: pppd.8,v 1.61 2002/03/05 15:14:04 dfs Exp $ .\" SH section heading .\" SS subsection heading .\" LP paragraph @@ -857,6 +857,10 @@ peer using CHAP. With this option, pppd will not agree to authenticate itself to the peer using MS-CHAP. .TP +.B refuse-mschap-v2 +With this option, pppd will not agree to authenticate itself to the +peer using MS-CHAPv2. +.TP .B refuse-pap With this option, pppd will not agree to authenticate itself to the peer using PAP. @@ -869,6 +873,10 @@ Handshake Authentication Protocol] authentication. Require the peer to authenticate itself using MS-CHAP [Microsft Challenge Handshake Authentication Protocol] authentication. .TP +.B require-mschap-v2 +Require the peer to authenticate itself using MS-CHAPv2 [Microsft Challenge +Handshake Authentication Protocol, Version 2] authentication. +.TP .B require-pap Require the peer to authenticate itself using PAP [Password Authentication Protocol] authentication. @@ -1020,7 +1028,8 @@ pppd will not agree to authenticate itself with a particular protocol if it has no secrets which could be used to do so. .LP Pppd stores secrets for use in authentication in secrets -files (/etc/ppp/pap-secrets for PAP, /etc/ppp/chap-secrets for CHAP/MS-CHAP). +files (/etc/ppp/pap-secrets for PAP, /etc/ppp/chap-secrets for +CHAP/MS-CHAP/MS-CHAPv2). Both secrets files have the same format. The secrets files can contain secrets for pppd to use in authenticating itself to other systems, as well as secrets for pppd to use when authenticating other @@ -1498,7 +1507,8 @@ script. .B /var/run/ppp\fIn\fB.pid \fR(BSD or Linux), \fB/etc/ppp/ppp\fIn\fB.pid \fR(others) Process-ID for pppd process on ppp interface unit \fIn\fR. .TP -.B /var/run/ppp-\fIname\fB.pid \fR(BSD or Linux), \fB/etc/ppp/ppp-\fIname\fB.pid \fR(others) +.B /var/run/ppp-\fIname\fB.pid \fR(BSD or Linux), +\fB/etc/ppp/ppp-\fIname\fB.pid \fR(others) Process-ID for pppd process for logical link \fIname\fR (see the \fIlinkname\fR option). .TP @@ -1508,8 +1518,8 @@ file should be owned by root and not readable or writable by any other user. Pppd will log a warning if this is not the case. .TP .B /etc/ppp/chap-secrets -Names, secrets and IP addresses for CHAP/MS-CHAP authentication. As for -/etc/ppp/pap-secrets, this file should be owned by root and not +Names, secrets and IP addresses for CHAP/MS-CHAP/MS-CHAPv2 authentication. +As for /etc/ppp/pap-secrets, this file should be owned by root and not readable or writable by any other user. Pppd will log a warning if this is not the case. .TP diff --git a/pppd/sha1.c b/pppd/sha1.c new file mode 100644 index 0000000..b56a7c9 --- /dev/null +++ b/pppd/sha1.c @@ -0,0 +1,163 @@ +/* + * ftp://ftp.funet.fi/pub/crypt/hash/sha/sha1.c + * + * SHA-1 in C + * By Steve Reid + * 100% Public Domain + * + * Test Vectors (from FIPS PUB 180-1) + * "abc" + * A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D + * "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq" + * 84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1 + * A million repetitions of "a" + * 34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F + */ + +/* #define SHA1HANDSOFF * Copies data before messing with it. */ + +#include +#include /* htonl() */ +#include "sha1.h" + +#define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits)))) + +/* blk0() and blk() perform the initial expand. */ +/* I got the idea of expanding during the round function from SSLeay */ +#define blk0(i) (block->l[i] = htonl(block->l[i])) +#define blk(i) (block->l[i&15] = rol(block->l[(i+13)&15]^block->l[(i+8)&15] \ + ^block->l[(i+2)&15]^block->l[i&15],1)) + +/* (R0+R1), R2, R3, R4 are the different operations used in SHA1 */ +#define R0(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk0(i)+0x5A827999+rol(v,5);w=rol(w,30); +#define R1(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=rol(w,30); +#define R2(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=rol(w,30); +#define R3(v,w,x,y,z,i) z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=rol(w,30); +#define R4(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=rol(w,30); + + +/* 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]) +{ + unsigned long a, b, c, d, e; + typedef union { + unsigned char c[64]; + unsigned long l[16]; + } CHAR64LONG16; + CHAR64LONG16 *block; + +#ifdef SHA1HANDSOFF + static unsigned char workspace[64]; + block = (CHAR64LONG16 *) workspace; + memcpy(block, buffer, 64); +#else + block = (CHAR64LONG16 *) buffer; +#endif + /* Copy context->state[] to working vars */ + a = state[0]; + b = state[1]; + c = state[2]; + d = state[3]; + e = state[4]; + /* 4 rounds of 20 operations each. Loop unrolled. */ + R0(a,b,c,d,e, 0); R0(e,a,b,c,d, 1); R0(d,e,a,b,c, 2); R0(c,d,e,a,b, 3); + R0(b,c,d,e,a, 4); R0(a,b,c,d,e, 5); R0(e,a,b,c,d, 6); R0(d,e,a,b,c, 7); + R0(c,d,e,a,b, 8); R0(b,c,d,e,a, 9); R0(a,b,c,d,e,10); R0(e,a,b,c,d,11); + R0(d,e,a,b,c,12); R0(c,d,e,a,b,13); R0(b,c,d,e,a,14); R0(a,b,c,d,e,15); + R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19); + R2(a,b,c,d,e,20); R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23); + R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26); R2(d,e,a,b,c,27); + R2(c,d,e,a,b,28); R2(b,c,d,e,a,29); R2(a,b,c,d,e,30); R2(e,a,b,c,d,31); + R2(d,e,a,b,c,32); R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35); + R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38); R2(b,c,d,e,a,39); + R3(a,b,c,d,e,40); R3(e,a,b,c,d,41); R3(d,e,a,b,c,42); R3(c,d,e,a,b,43); + R3(b,c,d,e,a,44); R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47); + R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50); R3(e,a,b,c,d,51); + R3(d,e,a,b,c,52); R3(c,d,e,a,b,53); R3(b,c,d,e,a,54); R3(a,b,c,d,e,55); + R3(e,a,b,c,d,56); R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59); + R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62); R4(c,d,e,a,b,63); + R4(b,c,d,e,a,64); R4(a,b,c,d,e,65); R4(e,a,b,c,d,66); R4(d,e,a,b,c,67); + R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71); + R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75); + R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79); + /* Add the working vars back into context.state[] */ + state[0] += a; + state[1] += b; + state[2] += c; + state[3] += d; + state[4] += e; + /* Wipe variables */ + a = b = c = d = e = 0; +} + + +/* SHA1Init - Initialize new context */ + +void SHA1_Init(SHA1_CTX *context) +{ + /* SHA1 initialization constants */ + context->state[0] = 0x67452301; + context->state[1] = 0xEFCDAB89; + context->state[2] = 0x98BADCFE; + context->state[3] = 0x10325476; + context->state[4] = 0xC3D2E1F0; + context->count[0] = context->count[1] = 0; +} + + +/* Run your data through this. */ + +void SHA1_Update(SHA1_CTX *context, const unsigned char *data, unsigned int len) +{ + unsigned int i, j; + + j = (context->count[0] >> 3) & 63; + if ((context->count[0] += len << 3) < (len << 3)) context->count[1]++; + context->count[1] += (len >> 29); + if ((j + len) > 63) { + memcpy(&context->buffer[j], data, (i = 64-j)); + SHA1_Transform(context->state, context->buffer); + for ( ; i + 63 < len; i += 64) { + SHA1_Transform(context->state, &data[i]); + } + j = 0; + } + else + i = 0; + + memcpy(&context->buffer[j], &data[i], len - i); +} + + +/* Add padding and return the message digest. */ + +void SHA1_Final(unsigned char digest[20], SHA1_CTX *context) +{ + unsigned long i, j; + unsigned char finalcount[8]; + + for (i = 0; i < 8; i++) { + finalcount[i] = (unsigned char)((context->count[(i >= 4 ? 0 : 1)] + >> ((3-(i & 3)) * 8) ) & 255); /* Endian independent */ + } + SHA1_Update(context, (unsigned char *) "\200", 1); + while ((context->count[0] & 504) != 448) { + SHA1_Update(context, (unsigned char *) "\0", 1); + } + SHA1_Update(context, finalcount, 8); /* Should cause a SHA1Transform() */ + for (i = 0; i < 20; i++) { + digest[i] = (unsigned char) + ((context->state[i>>2] >> ((3-(i & 3)) * 8) ) & 255); + } + /* Wipe variables */ + i = j = 0; + memset(context->buffer, 0, 64); + memset(context->state, 0, 20); + memset(context->count, 0, 8); + memset(&finalcount, 0, 8); +#ifdef SHA1HANDSOFF /* make SHA1Transform overwrite it's own static vars */ + SHA1Transform(context->state, context->buffer); +#endif +} + diff --git a/pppd/sha1.h b/pppd/sha1.h new file mode 100644 index 0000000..6727afb --- /dev/null +++ b/pppd/sha1.h @@ -0,0 +1,19 @@ +/* sha1.h */ + +#ifndef __SHA1_INCLUDE_ + +typedef struct { + unsigned long state[5]; + unsigned long count[2]; + unsigned char buffer[64]; +} SHA1_CTX; + +#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 *); + +#define __SHA1_INCLUDE_ +#endif /* __SHA1_INCLUDE_ */ diff --git a/pppd/utils.c b/pppd/utils.c index 935f562..38a6977 100644 --- a/pppd/utils.c +++ b/pppd/utils.c @@ -17,7 +17,7 @@ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. */ -#define RCSID "$Id: utils.c,v 1.17 2002/01/14 15:31:34 dfs Exp $" +#define RCSID "$Id: utils.c,v 1.18 2002/03/05 15:14:04 dfs Exp $" #include #include @@ -209,6 +209,28 @@ vslprintf(buf, buflen, fmt, args) neg = 0; ++fmt; switch (c) { + case 'l': + c = *fmt++; + switch (c) { + case 'd': + val = va_arg(args, long); + if (val < 0) { + neg = 1; + val = -val; + } + base = 10; + break; + case 'u': + val = va_arg(args, unsigned long); + base = 10; + break; + default: + *buf++ = '%'; --buflen; + *buf++ = 'l'; --buflen; + --fmt; /* so %lz outputs %lz etc. */ + continue; + } + break; case 'd': i = va_arg(args, int); if (i < 0) { -- 2.39.2