From: Eivind Næss Date: Sat, 24 Apr 2021 10:00:34 +0000 (-0700) Subject: pppd: Expose the MPPE keys generated through an API (#267) X-Git-Tag: ppp-2.5.0~78 X-Git-Url: http://git.ozlabs.org/?p=ppp.git;a=commitdiff_plain;h=d706c95906d996534f13632a747af5dc617f306e pppd: Expose the MPPE keys generated through an API (#267) The lengthy part of this fix is to refactor the handling of MPPE keys by moving it into mppe.c and thus reducing the clutter in chap_ms.c. It does so by renaming the mppe_set_keys/2 to the corresponding mppe_set_chapv1/mppe_set_chapv2 versions and updates callers of these functions. Having done so, it conveniently allows the name "mppe_set_keys" to be re-used for this new purpose which will copy the key material up to its size and then clear the input parameters (avoids leaving the MPPE keys on the stack). Additional functiions added to the MPPE code allow plugins et al. to access the MPPE keys, clear the keys, and check if they are set. All plugin and CCP code has been updated to use this API. This fixes GitHub Issue #258 Signed-off-by: Eivind Naess --- diff --git a/pppd/Makefile.linux b/pppd/Makefile.linux index f92f7c0..852945e 100644 --- a/pppd/Makefile.linux +++ b/pppd/Makefile.linux @@ -109,6 +109,8 @@ CFLAGS += -DMSLANMAN=1 endif ifdef MPPE CFLAGS += -DMPPE=1 +PPPDOBJS += mppe.o +PPPDSRC += mppe.c HEADERS += mppe.h endif endif diff --git a/pppd/Makefile.sol2 b/pppd/Makefile.sol2 index 809cb4b..3a8681c 100644 --- a/pppd/Makefile.sol2 +++ b/pppd/Makefile.sol2 @@ -37,7 +37,7 @@ OBJS += ipv6cp.o eui64.o # Uncomment to enable MS-CHAP CFLAGS += -DUSE_CRYPT -DCHAPMS -DMSLANMAN -DHAVE_CRYPT_H -OBJS += chap_ms.o pppcrypt.o md4.o sha1.o +OBJS += chap_ms.o pppcrypt.o md4.o sha1.o mppe.o # Uncomment to enable MPPE (in both CHAP and EAP-TLS) CFLAGS += -DMPPE diff --git a/pppd/ccp.c b/pppd/ccp.c index 052c4c6..387b571 100644 --- a/pppd/ccp.c +++ b/pppd/ccp.c @@ -38,10 +38,9 @@ #include "ccp.h" #include -#ifdef MPPE -#include "chap_ms.h" /* mppe_xxxx_key, mppe_keys_set */ +#include "chap_ms.h" +#include "mppe.h" #include "lcp.h" /* lcp_close(), lcp_fsm */ -#endif /* @@ -574,7 +573,7 @@ ccp_resetci(fsm *f) } /* A plugin (eg radius) may not have obtained key material. */ - if (!mppe_keys_set) { + if (!mppe_keys_isset()) { error("MPPE required, but keys are not available. " "Possible plugin problem?"); lcp_close(f->unit, "MPPE required but not available"); @@ -705,7 +704,7 @@ static void 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); + mppe_get_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; @@ -1156,8 +1155,7 @@ ccp_reqci(fsm *f, u_char *p, int *lenp, int dont_nak) int mtu; BCOPY(p, opt_buf, CILEN_MPPE); - BCOPY(mppe_send_key, &opt_buf[CILEN_MPPE], - MPPE_MAX_KEY_LEN); + mppe_get_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! */ @@ -1426,8 +1424,7 @@ ccp_up(fsm *f) notice("%s transmit compression enabled", method_name(ho, NULL)); #ifdef MPPE if (go->mppe) { - BZERO(mppe_recv_key, MPPE_MAX_KEY_LEN); - BZERO(mppe_send_key, MPPE_MAX_KEY_LEN); + mppe_clear_keys(); continue_networks(f->unit); /* Bring up IP et al */ } #endif diff --git a/pppd/chap_ms.c b/pppd/chap_ms.c index df2dadd..d315ab4 100644 --- a/pppd/chap_ms.c +++ b/pppd/chap_ms.c @@ -93,8 +93,7 @@ #include "sha1.h" #include "pppcrypt.h" #include "magic.h" - - +#include "mppe.h" static void ascii2unicode (char[], int, u_char[]); static void NTPasswordHash (u_char *, int, u_char[MD4_SIGNATURE_SIZE]); @@ -109,21 +108,12 @@ static void GenerateAuthenticatorResponsePlain static void ChapMS_LANMan (u_char *, char *, int, u_char *); #endif -#ifdef MPPE -static void Set_Start_Key (u_char *, char *, int); -static void SetMasterKeys (char *, int, u_char[24], int); -#endif - #ifdef MSLANMAN bool ms_lanman = 0; /* Use LanMan password instead of NT */ /* 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]; -int mppe_keys_set = 0; /* Have the MPPE keys been set? */ - #ifdef DEBUGMPPEKEY /* For MPPE debug */ /* Use "[]|}{?/><,`!2&&(" (sans quotes) for RFC 3079 MS-CHAPv2 test value */ @@ -719,28 +709,6 @@ GenerateAuthenticatorResponsePlain #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)); - - mppe_keys_set = 1; -} /* * Set mppe_xxxx_key from MS-CHAP credentials. (see RFC 3079) @@ -757,104 +725,7 @@ Set_Start_Key(u_char *rchallenge, char *secret, int secret_len) 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) - * - * This helper function used in the Winbind module, which gets the - * NTHashHash from the server. - */ -void -mppe_set_keys2(u_char PasswordHashHash[MD4_SIGNATURE_SIZE], - u_char NTResponse[24], int IsServer) -{ - SHA1_CTX sha1Context; - 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; - - SHA1_Init(&sha1Context); - SHA1_Update(&sha1Context, PasswordHashHash, MD4_SIGNATURE_SIZE); - 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)); - - mppe_keys_set = 1; + mppe_set_chapv1(rchallenge, PasswordHashHash); } /* @@ -870,7 +741,7 @@ SetMasterKeys(char *secret, int secret_len, u_char NTResponse[24], int IsServer) ascii2unicode(secret, secret_len, unicodePassword); NTPasswordHash(unicodePassword, secret_len * 2, PasswordHash); NTPasswordHash(PasswordHash, sizeof(PasswordHash), PasswordHashHash); - mppe_set_keys2(PasswordHashHash, NTResponse, IsServer); + mppe_set_chapv2(PasswordHashHash, NTResponse, IsServer); } #endif /* MPPE */ @@ -945,38 +816,6 @@ ChapMS2(u_char *rchallenge, u_char *PeerChallenge, #endif } -#ifdef MPPE -/* - * Set MPPE options from plugins. - */ -void -set_mppe_enc_types(int policy, int types) -{ - /* Early exit for unknown policies. */ - if (policy != MPPE_ENC_POL_ENC_ALLOWED && - policy != MPPE_ENC_POL_ENC_REQUIRED) - return; - - /* Don't modify MPPE if it's optional and wasn't already configured. */ - if (policy == MPPE_ENC_POL_ENC_ALLOWED && !ccp_wantoptions[0].mppe) - return; - - /* - * Disable undesirable encryption types. Note that we don't ENABLE - * any encryption types, to avoid overriding manual configuration. - */ - switch(types) { - case MPPE_ENC_TYPES_RC4_40: - ccp_wantoptions[0].mppe &= ~MPPE_OPT_128; /* disable 128-bit */ - break; - case MPPE_ENC_TYPES_RC4_128: - ccp_wantoptions[0].mppe &= ~MPPE_OPT_40; /* disable 40-bit */ - break; - default: - break; - } -} -#endif /* MPPE */ static struct chap_digest_type chapms_digest = { CHAP_MICROSOFT, /* code */ diff --git a/pppd/chap_ms.h b/pppd/chap_ms.h index 005eb63..4e6a621 100644 --- a/pppd/chap_ms.h +++ b/pppd/chap_ms.h @@ -38,6 +38,7 @@ #define MS_CHAP_RESPONSE_LEN 49 /* Response length for MS-CHAP */ #define MS_CHAP2_RESPONSE_LEN 49 /* Response length for MS-CHAPv2 */ #define MS_AUTH_RESPONSE_LENGTH 40 /* MS-CHAPv2 authenticator response, */ +#define MS_AUTH_NTRESP_LEN 24 /* Length of NT-response field */ /* as ASCII */ /* E=eeeeeeeeee error codes for MS-CHAP failure messages. */ @@ -67,22 +68,6 @@ #define MS_CHAP2_NTRESP_LEN 24 #define MS_CHAP2_FLAGS 48 -#ifdef MPPE -#include "mppe.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]; -extern int mppe_keys_set; - -/* These values are the RADIUS attribute values--see RFC 2548. */ -#define MPPE_ENC_POL_ENC_ALLOWED 1 -#define MPPE_ENC_POL_ENC_REQUIRED 2 -#define MPPE_ENC_TYPES_RC4_40 2 -#define MPPE_ENC_TYPES_RC4_128 4 - -/* used by plugins (using above values) */ -extern void set_mppe_enc_types(int, int); -#endif - /* Are we the authenticator or authenticatee? For MS-CHAPv2 key derivation. */ #define MS_CHAP2_AUTHENTICATEE 0 #define MS_CHAP2_AUTHENTICATOR 1 @@ -90,11 +75,6 @@ extern void set_mppe_enc_types(int, int); void ChapMS (u_char *, char *, int, u_char *); void ChapMS2 (u_char *, u_char *, char *, char *, int, u_char *, u_char[MS_AUTH_RESPONSE_LENGTH+1], int); -#ifdef MPPE -void mppe_set_keys (u_char *, u_char[MD4_SIGNATURE_SIZE]); -void mppe_set_keys2(u_char PasswordHashHash[MD4_SIGNATURE_SIZE], - u_char NTResponse[24], int IsServer); -#endif void ChallengeHash (u_char[16], u_char *, char *, u_char[8]); diff --git a/pppd/eap-tls.c b/pppd/eap-tls.c index 5c202c7..bfcf199 100644 --- a/pppd/eap-tls.c +++ b/pppd/eap-tls.c @@ -48,6 +48,8 @@ #include "eap-tls.h" #include "fsm.h" #include "lcp.h" +#include "chap_ms.h" +#include "mppe.h" #include "pathnames.h" typedef struct pw_cb_data @@ -74,10 +76,6 @@ int ssl_new_session_cb(SSL *s, SSL_SESSION *sess); X509 *get_X509_from_file(char *filename); int ssl_cmp_certs(char *filename, X509 * a); -#ifdef MPPE - -#define EAPTLS_MPPE_KEY_LEN 32 - /* * OpenSSL 1.1+ introduced a generic TLS_method() * For older releases we substitute the appropriate method @@ -119,6 +117,8 @@ static inline int SSL_CTX_set_max_proto_version(SSL_CTX *ctx, long tls_ver_max) #endif /* OPENSSL_VERSION_NUMBER < 0x10100000L */ +#ifdef MPPE +#define EAPTLS_MPPE_KEY_LEN 32 /* * Generate keys according to RFC 2716 and add to reply @@ -161,24 +161,17 @@ void eaptls_gen_mppe_keys(struct eaptls_session *ets, int client) */ if (client) { - p = out; - BCOPY( p, mppe_send_key, sizeof(mppe_send_key) ); - p += EAPTLS_MPPE_KEY_LEN; - BCOPY( p, mppe_recv_key, sizeof(mppe_recv_key) ); + mppe_set_keys(out, out + EAPTLS_MPPE_KEY_LEN, EAPTLS_MPPE_KEY_LEN); } else { - p = out; - BCOPY( p, mppe_recv_key, sizeof(mppe_recv_key) ); - p += EAPTLS_MPPE_KEY_LEN; - BCOPY( p, mppe_send_key, sizeof(mppe_send_key) ); + mppe_set_keys(out + EAPTLS_MPPE_KEY_LEN, out, EAPTLS_MPPE_KEY_LEN); } - - mppe_keys_set = 1; } #endif /* MPPE */ + void log_ssl_errors( void ) { unsigned long ssl_err = ERR_get_error(); diff --git a/pppd/eap-tls.h b/pppd/eap-tls.h index c74a831..b935ec5 100644 --- a/pppd/eap-tls.h +++ b/pppd/eap-tls.h @@ -86,11 +86,6 @@ int get_eaptls_secret(int unit, char *client, char *server, char *capath, char *pkfile, int am_server); #ifdef MPPE -#include "mppe.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]; -extern int mppe_keys_set; - void eaptls_gen_mppe_keys(struct eaptls_session *ets, int client); #endif diff --git a/pppd/mppe.c b/pppd/mppe.c new file mode 100644 index 0000000..4f3d131 --- /dev/null +++ b/pppd/mppe.c @@ -0,0 +1,248 @@ +/* * mppe.c - MPPE key implementation + * + * Copyright (c) 2020 Eivind Naess. All rights reserved. + * Copyright (c) 2008 Paul Mackerras. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The name(s) of the authors of this software must not be used to + * endorse or promote products derived from this software without + * prior written permission. + * + * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO + * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY + * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN + * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#include + +#include "pppd.h" +#include "fsm.h" +#include "md4.h" +#include "sha1.h" +#include "ccp.h" +#include "chap_ms.h" +#include "mppe.h" + +u_char mppe_send_key[MPPE_MAX_KEY_SIZE]; +u_char mppe_recv_key[MPPE_MAX_KEY_SIZE]; +int mppe_keys_set = 0; + +void +mppe_set_keys(u_char *send_key, u_char *recv_key, int keylen) +{ + int length = keylen; + if (length > MPPE_MAX_KEY_SIZE) + length = MPPE_MAX_KEY_SIZE; + + if (send_key) { + BCOPY(send_key, mppe_send_key, length); + BZERO(send_key, keylen); + } + + if (recv_key) { + BCOPY(recv_key, mppe_recv_key, length); + BZERO(recv_key, keylen); + } + + mppe_keys_set = length; +} + +bool +mppe_keys_isset() +{ + return !!mppe_keys_set; +} + +int +mppe_get_recv_key(u_char *recv_key, int length) +{ + if (mppe_keys_isset()) { + if (length > mppe_keys_set) + length = mppe_keys_set; + BCOPY(mppe_recv_key, recv_key, length); + return length; + } + return 0; +} + +int +mppe_get_send_key(u_char *send_key, int length) +{ + if (mppe_keys_isset()) { + if (length > mppe_keys_set) + length = mppe_keys_set; + BCOPY(mppe_send_key, send_key, length); + return length; + } + return 0; +} + +void +mppe_clear_keys(void) +{ + mppe_keys_set = 0; + BZERO(mppe_send_key, sizeof(mppe_send_key)); + BZERO(mppe_recv_key, sizeof(mppe_recv_key)); +} + +/* + * Set mppe_xxxx_key from the NTPasswordHashHash. + * RFC 2548 (RADIUS support) requires us to export this function (ugh). + */ +void +mppe_set_chapv1(u_char *rchallenge, u_char PasswordHashHash[MD4_SIGNATURE_SIZE]) +{ + SHA1_CTX sha1Context; + u_char Digest[SHA1_SIGNATURE_SIZE]; + + 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. */ + mppe_set_keys(Digest, Digest, sizeof(Digest)); +} + +/* + * Set mppe_xxxx_key from MS-CHAPv2 credentials. (see RFC 3079) + * + * This helper function used in the Winbind module, which gets the + * NTHashHash from the server. + */ +void +mppe_set_chapv2(u_char PasswordHashHash[MD4_SIGNATURE_SIZE], + u_char NTResponse[MS_AUTH_NTRESP_LEN], int IsServer) +{ + SHA1_CTX sha1Context; + u_char MasterKey[SHA1_SIGNATURE_SIZE]; + u_char SendKey[SHA1_SIGNATURE_SIZE]; + u_char RecvKey[SHA1_SIGNATURE_SIZE]; + + 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; + + SHA1_Init(&sha1Context); + SHA1_Update(&sha1Context, PasswordHashHash, MD4_SIGNATURE_SIZE); + 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(SendKey, &sha1Context); + + /* + * 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(RecvKey, &sha1Context); + + mppe_set_keys(SendKey, RecvKey, SHA1_SIGNATURE_SIZE); +} + +/* + * Set MPPE options from plugins. + */ +void +mppe_set_enc_types(int policy, int types) +{ + /* Early exit for unknown policies. */ + if (policy != MPPE_ENC_POL_ENC_ALLOWED && + policy != MPPE_ENC_POL_ENC_REQUIRED) + return; + + /* Don't modify MPPE if it's optional and wasn't already configured. */ + if (policy == MPPE_ENC_POL_ENC_ALLOWED && !ccp_wantoptions[0].mppe) + return; + + /* + * Disable undesirable encryption types. Note that we don't ENABLE + * any encryption types, to avoid overriding manual configuration. + */ + switch(types) { + case MPPE_ENC_TYPES_RC4_40: + ccp_wantoptions[0].mppe &= ~MPPE_OPT_128; /* disable 128-bit */ + break; + case MPPE_ENC_TYPES_RC4_128: + ccp_wantoptions[0].mppe &= ~MPPE_OPT_40; /* disable 40-bit */ + break; + default: + break; + } +} + diff --git a/pppd/mppe.h b/pppd/mppe.h index 5eb3b37..98a89d3 100644 --- a/pppd/mppe.h +++ b/pppd/mppe.h @@ -32,9 +32,12 @@ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#ifndef __MPPE_H__ +#define __MPPE_H__ #define MPPE_PAD 4 /* MPPE growth per frame */ -#define MPPE_MAX_KEY_LEN 16 /* largest key length (128-bit) */ +#define MPPE_MAX_KEY_SIZE 32 /* Largest key length */ +#define MPPE_MAX_KEY_LEN 16 /* Largest key size accepted by the kernel */ /* option bits for ccp_options.mppe */ #define MPPE_OPT_40 0x01 /* 40 bit */ @@ -119,3 +122,68 @@ if (ptr[3] & ~MPPE_ALL_BITS) \ opts |= MPPE_OPT_UNKNOWN; \ } while (/* CONSTCOND */ 0) + + +#if MPPE + +/* + * NOTE: + * Access to these variables directly is discuraged. Please + * change your code to use below accessor functions. + */ + +/* The key material generated which is used for MPPE send key */ +extern u_char mppe_send_key[MPPE_MAX_KEY_SIZE]; +/* The key material generated which is used for MPPE recv key */ +extern u_char mppe_recv_key[MPPE_MAX_KEY_SIZE]; +/* Keys are set if value is non-zero */ +extern int mppe_keys_set; + +/* These values are the RADIUS attribute values--see RFC 2548. */ +#define MPPE_ENC_POL_ENC_ALLOWED 1 +#define MPPE_ENC_POL_ENC_REQUIRED 2 +#define MPPE_ENC_TYPES_RC4_40 2 +#define MPPE_ENC_TYPES_RC4_128 4 + +/* used by plugins (using above values) */ +void mppe_set_enc_types (int policy, int types); + +/* + * Set the MPPE send and recv keys. NULL values for keys are ignored + * and input values are cleared to avoid leaving them on the stack + */ +void mppe_set_keys(u_char *send_key, u_char *recv_key, int keylen); + +/* + * Get the MPPE recv key + */ +int mppe_get_recv_key(u_char *recv_key, int length); + +/* + * Get the MPPE send key + */ +int mppe_get_send_key(u_char *send_key, int length); + +/* + * Clear the MPPE keys + */ +void mppe_clear_keys(void); + +/* + * Check if the MPPE keys are set + */ +bool mppe_keys_isset(void); + +/* + * Set mppe_xxxx_key from NT Password Hash Hash (MSCHAPv1), see RFC3079 + */ +void mppe_set_chapv1(u_char *rchallenge, u_char PasswordHashHash[MD4_SIGNATURE_SIZE]); + +/* + * Set the mppe_xxxx_key from MS-CHAP-v2 credentials, see RFC3079 + */ +void mppe_set_chapv2(u_char PasswordHashHash[MD4_SIGNATURE_SIZE], + u_char NTResponse[MS_AUTH_NTRESP_LEN], int IsServer); + +#endif // #ifdef MPPE +#endif // #ifdef __MPPE_H__ diff --git a/pppd/plugins/radius/radius.c b/pppd/plugins/radius/radius.c index c579831..cf4c0f2 100644 --- a/pppd/plugins/radius/radius.c +++ b/pppd/plugins/radius/radius.c @@ -31,6 +31,7 @@ static char const RCSID[] = #ifdef CHAPMS #include "chap_ms.h" #ifdef MPPE +#include "mppe.h" #include "md5.h" #endif #endif @@ -743,11 +744,12 @@ radius_setparams(VALUE_PAIR *vp, char *msg, REQUEST_INFO *req_info, * Note that if the policy value was '0' we don't set the key! */ if (mppe_enc_policy && mppe_enc_keys) { - mppe_keys_set = 1; /* Set/modify allowed encryption types. */ if (mppe_enc_types) - set_mppe_enc_types(mppe_enc_policy, mppe_enc_types); + mppe_set_enc_types(mppe_enc_policy, mppe_enc_types); + return 0; } + mppe_clear_keys(); #endif return 0; @@ -803,7 +805,7 @@ radius_setmppekeys(VALUE_PAIR *vp, REQUEST_INFO *req_info, * the NAS (us) doesn't need; we only need the start key. So we have * to generate the start key, sigh. NB: We do not support the LM-Key. */ - mppe_set_keys(challenge, &plain[8]); + mppe_set_chapv1(challenge, &plain[8]); return 0; } @@ -855,7 +857,7 @@ radius_setmppekeys2(VALUE_PAIR *vp, REQUEST_INFO *req_info) for (i = 0; i < 16; i++) plain[i] ^= buf[i]; - if (plain[0] != sizeof(mppe_send_key) /* 16 */) { + if (plain[0] != 16) { error("RADIUS: Incorrect key length (%d) for MS-MPPE-%s-Key attribute", (int) plain[0], type); return -1; @@ -869,9 +871,9 @@ radius_setmppekeys2(VALUE_PAIR *vp, REQUEST_INFO *req_info) plain[16] ^= buf[0]; /* only need the first byte */ if (vp->attribute == PW_MS_MPPE_SEND_KEY) - memcpy(mppe_send_key, plain + 1, 16); + mppe_set_keys(plain + 1, NULL, 16); else - memcpy(mppe_recv_key, plain + 1, 16); + mppe_set_keys(NULL, plain + 1, 16); return 0; } diff --git a/pppd/plugins/winbind.c b/pppd/plugins/winbind.c index 0c395c3..67c72f6 100644 --- a/pppd/plugins/winbind.c +++ b/pppd/plugins/winbind.c @@ -37,11 +37,9 @@ #include "pppd.h" #include "chap-new.h" #include "chap_ms.h" -#ifdef MPPE -#include "md5.h" -#endif #include "fsm.h" #include "ipcp.h" +#include "mppe.h" #include #include #include @@ -583,7 +581,7 @@ winbind_chap_verify(char *user, char *ourname, int id, nt_response, nt_response_size, session_key, &error_string) == AUTHENTICATED) { - mppe_set_keys(challenge, session_key); + mppe_set_chapv1(challenge, session_key); slprintf(message, message_space, "Access granted"); return AUTHENTICATED; @@ -628,7 +626,7 @@ winbind_chap_verify(char *user, char *ourname, int id, &response[MS_CHAP2_NTRESP], &response[MS_CHAP2_PEER_CHALLENGE], challenge, user, saresponse); - mppe_set_keys2(session_key, &response[MS_CHAP2_NTRESP], + mppe_set_chapv2(session_key, &response[MS_CHAP2_NTRESP], MS_CHAP2_AUTHENTICATOR); if (response[MS_CHAP2_FLAGS]) { slprintf(message, message_space, "S=%s", saresponse);