X-Git-Url: http://git.ozlabs.org/?p=ppp.git;a=blobdiff_plain;f=pppd%2Fchap_ms.c;h=d10b99a4f1980c6ac2f99fccd51d3bd2c202c9e2;hp=f58bafc15ff5117fc36bc6f58ceac0dd901c4fd3;hb=bcfa20820fc9ff3b25bcf62308e3e737c1897dc6;hpb=bfa20ccde2a70b1252dbb614132f1a4cbee815d4 diff --git a/pppd/chap_ms.c b/pppd/chap_ms.c index f58bafc..d10b99a 100644 --- a/pppd/chap_ms.c +++ b/pppd/chap_ms.c @@ -31,9 +31,14 @@ * You should also use DOMAIN\\USERNAME as described in README.MSCHAP80 */ -#ifndef lint -static char rcsid[] = "$Id: chap_ms.c,v 1.10 1998/11/07 06:59:26 paulus Exp $"; -#endif +/* + * 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 @@ -43,7 +48,6 @@ static char rcsid[] = "$Id: chap_ms.c,v 1.10 1998/11/07 06:59:26 paulus Exp $"; #include #include #include -#include #include #ifdef HAVE_CRYPT_H #include @@ -53,27 +57,30 @@ static char rcsid[] = "$Id: chap_ms.c,v 1.10 1998/11/07 06:59:26 paulus Exp $"; #include "chap.h" #include "chap_ms.h" #include "md4.h" +#include "sha1.h" #ifndef USE_CRYPT #include #endif -typedef struct { - u_char LANManResp[24]; - u_char NTResp[24]; - u_char UseNT; /* If 1, ignore the LANMan response field */ -} MS_ChapResponse; -/* We use MS_CHAP_RESPONSE_LEN, rather than sizeof(MS_ChapResponse), - in case this struct gets padded. */ +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 @@ -81,42 +88,41 @@ 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 - log_packet(ZPasswordHash, sizeof(ZPasswordHash), "ChallengeResponse - ZPasswordHash", LOG_DEBUG); + 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 - log_packet(response, 24, "ChallengeResponse - response", LOG_DEBUG); + dbglog("ChallengeResponse - response %.24B", response); #endif } #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]; @@ -128,8 +134,7 @@ DesEncrypt(clear, key, cipher) setkey(crypt_key); #if 0 - CHAPDEBUG((LOG_INFO, "DesEncrypt: 8 octet input : %02X%02X%02X%02X%02X%02X%02X%02X", - clear[0], clear[1], clear[2], clear[3], clear[4], clear[5], clear[6], clear[7])); + CHAPDEBUG((LOG_INFO, "DesEncrypt: 8 octet input : %.8B", clear)); #endif Expand(clear, des_input); @@ -137,18 +142,14 @@ DesEncrypt(clear, key, cipher) Collapse(des_input, cipher); #if 0 - CHAPDEBUG((LOG_INFO, "DesEncrypt: 8 octet output: %02X%02X%02X%02X%02X%02X%02X%02X", - cipher[0], cipher[1], cipher[2], cipher[3], cipher[4], cipher[5], cipher[6], cipher[7])); + CHAPDEBUG((LOG_INFO, "DesEncrypt: 8 octet output: %.8B", cipher)); #endif } #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; @@ -158,24 +159,20 @@ DesEncrypt(clear, key, cipher) des_set_key(&des_key, key_schedule); #if 0 - CHAPDEBUG((LOG_INFO, "DesEncrypt: 8 octet input : %02X%02X%02X%02X%02X%02X%02X%02X", - clear[0], clear[1], clear[2], clear[3], clear[4], clear[5], clear[6], clear[7])); + CHAPDEBUG((LOG_INFO, "DesEncrypt: 8 octet input : %.8B", clear)); #endif des_ecb_encrypt((des_cblock *)clear, (des_cblock *)cipher, key_schedule, 1); #if 0 - CHAPDEBUG((LOG_INFO, "DesEncrypt: 8 octet output: %02X%02X%02X%02X%02X%02X%02X%02X", - cipher[0], cipher[1], cipher[2], cipher[3], cipher[4], cipher[5], cipher[6], cipher[7])); + CHAPDEBUG((LOG_INFO, "DesEncrypt: 8 octet output: %.8B", cipher)); #endif } #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; @@ -193,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; @@ -210,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; @@ -227,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); @@ -245,50 +236,100 @@ static void MakeKey(key, des_key) #endif #if 0 - CHAPDEBUG((LOG_INFO, "MakeKey: 56-bit input : %02X%02X%02X%02X%02X%02X%02X", - key[0], key[1], key[2], key[3], key[4], key[5], key[6])); - CHAPDEBUG((LOG_INFO, "MakeKey: 64-bit output: %02X%02X%02X%02X%02X%02X%02X%02X", - des_key[0], des_key[1], des_key[2], des_key[3], des_key[4], des_key[5], des_key[6], des_key[7])); + CHAPDEBUG((LOG_INFO, "MakeKey: 56-bit input : %.7B", key)); + CHAPDEBUG((LOG_INFO, "MakeKey: 64-bit output: %.8B", 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]) + { - int i; - MD4_CTX md4Context; - u_char hash[MD4_SIGNATURE_SIZE]; - u_char unicodePassword[MAX_NT_PASSWORD * 2]; + SHA1_CTX sha1Context; + u_char sha1Hash[SHA1_SIGNATURE_SIZE]; - /* 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]; + 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]) +{ +#ifdef __NetBSD__ + /* NetBSD uses the libc md4 routines which take bytes instead of bits */ + int mdlen = secret_len; +#else + int mdlen = secret_len * 8; +#endif + MD4_CTX md4Context; MD4Init(&md4Context); - MD4Update(&md4Context, unicodePassword, secret_len * 2 * 8); /* Unicode is 2 bytes/char, *8 for bit count */ + MD4Update(&md4Context, secret, mdlen); + MD4Final(hash, &md4Context); - MD4Final(hash, &md4Context); /* Tell MD4 we're done */ +} - ChallengeResponse(rchallenge, hash, response->NTResp); +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]; + + /* Hash the Unicode version of the secret (== password). */ + ascii2unicode(secret, secret_len, unicodePassword); + NTPasswordHash(unicodePassword, secret_len * 2, PasswordHash); + + 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 */ @@ -300,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 = !ms_lanman; + response->UseNT[0] = !ms_lanman; #else - response.UseNT = 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 */