$NetBSD$ --- pppd/chap_ms.c.orig Fri Aug 13 08:46:12 1999 +++ pppd/chap_ms.c Wed Dec 8 14:52:48 1999 @@ -31,6 +31,14 @@ * You should also use DOMAIN\\USERNAME as described in README.MSCHAP80 */ +/* + * Mods by Tim Hockin, Cobalt Networks Inc. June 1999 + * + * Cleanup, fixes and major reorganization. + * Migrated all MPPE code to mppe.c, shared crypto code to extra_crypto.c + * + */ + #define RCSID "$Id: chap_ms.c,v 1.15 1999/08/13 06:46:12 paulus Exp $" #ifdef CHAPMS @@ -49,11 +57,13 @@ #include "pppd.h" #include "chap.h" #include "chap_ms.h" -#include "md4.h" - -#ifndef USE_CRYPT -#include +#include "sha.h" +#include "fsm.h" +#include "lcp.h" +#ifdef MPPE +#include "mppe.h" #endif +#include "extra_crypto.h" static const char rcsid[] = RCSID; @@ -65,21 +75,19 @@ /* We use MS_CHAP_RESPONSE_LEN, rather than sizeof(MS_ChapResponse), in case this struct gets padded. */ +typedef struct { + u_char PeerChallenge[16]; + u_char Reserved[8]; + u_char NTResp[24]; + u_char Flags; +} MS_ChapResponse_v2; static void ChallengeResponse __P((u_char *, u_char *, u_char *)); -static void DesEncrypt __P((u_char *, u_char *, u_char *)); -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 *)); #ifdef MSLANMAN static void ChapMS_LANMan __P((char *, int, char *, int, MS_ChapResponse *)); #endif -#ifdef USE_CRYPT -static void Expand __P((u_char *, u_char *)); -static void Collapse __P((u_char *, u_char *)); -#endif - #ifdef MSLANMAN bool ms_lanman = 0; /* Use LanMan password instead of NT */ /* Has meaning only with MS-CHAP challenges */ @@ -110,142 +118,6 @@ #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 */ -{ - u_char des_key[8]; - u_char crypt_key[66]; - u_char des_input[66]; - - MakeKey(key, des_key); - - Expand(des_key, crypt_key); - setkey(crypt_key); - -#if 0 - CHAPDEBUG((LOG_INFO, "DesEncrypt: 8 octet input : %.8B", clear)); -#endif - - Expand(clear, des_input); - encrypt(des_input, 0); - Collapse(des_input, cipher); - -#if 0 - 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 */ -{ - des_cblock des_key; - des_key_schedule key_schedule; - - MakeKey(key, des_key); - - des_set_key(&des_key, key_schedule); - -#if 0 - 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: %.8B", cipher)); -#endif -} - -#endif /* USE_CRYPT */ - - -static u_char Get7Bits(input, startBit) - u_char *input; - int startBit; -{ - register unsigned int word; - - word = (unsigned)input[startBit / 8] << 8; - word |= (unsigned)input[startBit / 8 + 1]; - - word >>= 15 - (startBit % 8 + 7); - - return word & 0xFE; -} - -#ifdef USE_CRYPT - -/* in == 8-byte string (expanded version of the 56-bit key) - * 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; -{ - int j, c; - int i; - - for(i = 0; i < 64; in++){ - c = *in; - for(j = 7; j >= 0; j--) - *out++ = (c >> j) & 01; - i += 8; - } -} - -/* The inverse of Expand - */ -static void Collapse(in, out) - u_char *in; - u_char *out; -{ - int j; - int i; - unsigned int c; - - for (i = 0; i < 64; i += 8, out++) { - c = 0; - for (j = 7; j >= 0; j--, in++) - c |= *in << j; - *out = c & 0xff; - } -} -#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 */ -{ - des_key[0] = Get7Bits(key, 0); - des_key[1] = Get7Bits(key, 7); - des_key[2] = Get7Bits(key, 14); - des_key[3] = Get7Bits(key, 21); - des_key[4] = Get7Bits(key, 28); - des_key[5] = Get7Bits(key, 35); - des_key[6] = Get7Bits(key, 42); - des_key[7] = Get7Bits(key, 49); - -#ifndef USE_CRYPT - des_set_odd_parity((des_cblock *)des_key); -#endif - -#if 0 - 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; @@ -254,34 +126,13 @@ int secret_len; MS_ChapResponse *response; { - int i; -#ifdef __NetBSD__ - /* NetBSD uses the libc md4 routines which take bytes instead of bits */ - int mdlen = secret_len * 2; -#else - int mdlen = secret_len * 2 * 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); - - MD4Final(hash, &md4Context); /* Tell MD4 we're done */ + NtPasswordHash(secret, secret_len, hash); ChallengeResponse(rchallenge, hash, response->NTResp); } #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; @@ -290,16 +141,9 @@ int secret_len; MS_ChapResponse *response; { - int i; - u_char UcasePassword[MAX_NT_PASSWORD]; /* max is actually 14 */ u_char PasswordHash[MD4_SIGNATURE_SIZE]; - /* LANMan password is case insensitive */ - BZERO(UcasePassword, sizeof(UcasePassword)); - for (i = 0; i < secret_len; i++) - UcasePassword[i] = (u_char)toupper(secret[i]); - DesEncrypt( StdText, UcasePassword + 0, PasswordHash + 0 ); - DesEncrypt( StdText, UcasePassword + 7, PasswordHash + 8 ); + LmPasswordHash(secret, secret_len, PasswordHash); ChallengeResponse(rchallenge, PasswordHash, response->LANManResp); } #endif @@ -331,8 +175,209 @@ response.UseNT = 1; #endif +#ifdef MPPE + mppe_gen_master_key(secret, secret_len, rchallenge); +#endif + BCOPY(&response, cstate->response, MS_CHAP_RESPONSE_LEN); + cstate->resp_length = MS_CHAP_RESPONSE_LEN; +} + +int +ChapMS_Resp(cstate, secret, secret_len, remmd) + chap_state *cstate; + char *secret; + int secret_len; + u_char *remmd; +{ + MS_ChapResponse local; + MS_ChapResponse *response = (MS_ChapResponse *)remmd; + int i; + + BZERO(&local, sizeof(response)); + + if(response->UseNT) + { + ChapMS_NT(cstate->challenge,cstate->chal_len, secret, secret_len, &local); + i = memcmp(local.NTResp, response->NTResp, sizeof(local.NTResp)); + +#ifdef MPPE + if(i == 0) + mppe_gen_master_key(secret, secret_len, cstate->challenge); +#endif + return(i); + } + +#ifdef MSLANMAN + ChapMS_LANMan(cstate->challenge, cstate->chal_len, secret, secret_len, + &local); + if(memcmp(local.LANManResp, response->LANManResp, + sizeof(local.LANManResp)) == 0) { +#ifdef MPPE + mppe_gen_master_key(secret, secret_len, cstate->challenge); +#endif + return(0); + } +#endif /* MSLANMAN */ + return(1); +} + +void +ChallengeHash(PeerChallenge, AuthenticatorChallenge, UserName, Challenge) + char *PeerChallenge; + char *AuthenticatorChallenge; + char *UserName; + char *Challenge; +{ + SHA_CTX Context; + u_char Digest[SHA_DIGEST_LENGTH]; + char *username; + + if((username = strrchr(UserName, '\\')) != (char *)NULL) + ++username; + else + username = UserName; + SHA1_Init(&Context); + SHA1_Update(&Context, PeerChallenge, 16); + SHA1_Update(&Context, AuthenticatorChallenge, 16); + SHA1_Update(&Context, username, strlen(username)); + SHA1_Final(Digest, &Context); + BCOPY(Digest, Challenge, 8); +} + +void +ChapMS_v2(cstate, AuthenticatorChallenge, AuthenticatorChallengeLen, Password, PasswordLen) + chap_state *cstate; + char *AuthenticatorChallenge; + int AuthenticatorChallengeLen; + char *Password; + int PasswordLen; +{ + u_char Challenge[8]; + u_char PasswordHash[MD4_SIGNATURE_SIZE]; + MS_ChapResponse_v2 response; + + BZERO(&response, sizeof(response)); + ChapGenChallenge(cstate); + BCOPY(cstate->challenge, response.PeerChallenge, + sizeof(response.PeerChallenge)); + ChallengeHash(response.PeerChallenge, AuthenticatorChallenge, + cstate->resp_name, Challenge); + NtPasswordHash(Password, PasswordLen, PasswordHash); + ChallengeResponse(Challenge, PasswordHash, response.NTResp); BCOPY(&response, cstate->response, MS_CHAP_RESPONSE_LEN); cstate->resp_length = MS_CHAP_RESPONSE_LEN; +#ifdef MPPE + mppe_gen_master_key_v2(Password, PasswordLen, response.NTResp, 0); +#endif +} + +int +ChapMS_v2_Resp(cstate, Password, PasswordLen, remmd, UserName) + chap_state *cstate; + char *Password; + int PasswordLen; + u_char *remmd; + char *UserName; +{ + u_char Challenge[8]; + u_char PasswordHash[MD4_SIGNATURE_SIZE]; + MS_ChapResponse_v2 response, response1; + int i; + + BCOPY(remmd, &response, MS_CHAP_RESPONSE_LEN); + ChallengeHash(response.PeerChallenge,cstate->challenge,UserName,Challenge); + NtPasswordHash(Password, PasswordLen, PasswordHash); + ChallengeResponse(Challenge, PasswordHash, response1.NTResp); + i = memcmp(response.NTResp, response1.NTResp, sizeof(response.NTResp)); +#ifdef MPPE + if(i == 0) + mppe_gen_master_key_v2(Password, PasswordLen, response1.NTResp, 1); +#endif + return(i); +} + +void +ChapMS_v2_Auth(cstate, Password, PasswordLen, remmd, UserName) + chap_state *cstate; + char *Password; + int PasswordLen; + u_char *remmd; + char *UserName; +{ + u_char PasswordHash[MD4_SIGNATURE_SIZE]; + u_char PasswordHashHash[MD4_SIGNATURE_SIZE]; + u_char Challenge[8]; + static char Magic1[] = "Magic server to client signing constant"; + static char Magic2[] = "Pad to make it do more than one iteration"; + + SHA_CTX Context; + u_char Digest[SHA_DIGEST_LENGTH]; + MS_ChapResponse_v2 *response = (MS_ChapResponse_v2 *)remmd; + char StrResponse[SHA_DIGEST_LENGTH * 2 + 3], *s; + int i; + static char HexDigs[] = "0123456789ABCDEF"; + + NtPasswordHash(Password, PasswordLen, PasswordHash); + md4(PasswordHash, sizeof(PasswordHash), PasswordHashHash); + + SHA1_Init(&Context); + SHA1_Update(&Context, PasswordHashHash, 16); + SHA1_Update(&Context, response->NTResp, 24); + SHA1_Update(&Context, Magic1, sizeof(Magic1) - 1); + SHA1_Final(Digest, &Context); + + ChallengeHash(response->PeerChallenge,cstate->challenge,UserName,Challenge); + + SHA1_Init(&Context); + SHA1_Update(&Context, Digest, SHA_DIGEST_LENGTH); + SHA1_Update(&Context, Challenge, 8); + SHA1_Update(&Context, Magic2, sizeof(Magic2) - 1); + SHA1_Final(Digest, &Context); + s = strcpy(StrResponse, "S=")+2; + for(i = 0; i < SHA_DIGEST_LENGTH; ++i) { + *s++ = HexDigs[Digest[i] >> 4]; + *s++ = HexDigs[Digest[i] & 0x0F]; + } + *s = '\0'; + BCOPY(StrResponse, cstate->response, sizeof(StrResponse)); + cstate->resp_length = sizeof(StrResponse) - 1; +} + +/* + * functions called from config options + */ +int +reqchapms(char **argv) +{ + lcp_wantoptions[0].neg_chap = 1; + lcp_wantoptions[0].use_chapms = 1; + auth_required = 1; + + return 1; +} + +int +nochapms(char **argv) +{ + lcp_wantoptions[0].use_chapms = 0; + return 1; +} + +int +reqchapms_v2(char **argv) +{ + lcp_wantoptions[0].neg_chap = 1; + lcp_wantoptions[0].use_chapms_v2 = 1; + auth_required = 1; + + return 1; +} + +int +nochapms_v2(char **argv) +{ + lcp_wantoptions[0].use_chapms_v2 = 0; + return 1; } #endif /* CHAPMS */