/* 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. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include "pppd.h" #include "fsm.h" #include "ccp.h" #include "chap_ms.h" #include "mppe.h" #include "ppp-crypto.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(unsigned char *rchallenge, unsigned char *PasswordHashHash) { PPP_MD_CTX *ctx; u_char Digest[SHA_DIGEST_LENGTH]; int DigestLen; ctx = PPP_MD_CTX_new(); if (ctx != NULL) { if (PPP_DigestInit(ctx, PPP_sha1())) { if (PPP_DigestUpdate(ctx, PasswordHashHash, MD4_DIGEST_LENGTH)) { if (PPP_DigestUpdate(ctx, PasswordHashHash, MD4_DIGEST_LENGTH)) { if (PPP_DigestUpdate(ctx, rchallenge, 8)) { DigestLen = SHA_DIGEST_LENGTH; PPP_DigestFinal(ctx, Digest, &DigestLen); } } } } PPP_MD_CTX_free(ctx); } /* 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(unsigned char *PasswordHashHash, unsigned char *NTResponse, int IsServer) { PPP_MD_CTX *ctx; u_char MasterKey[SHA_DIGEST_LENGTH]; u_char SendKey[SHA_DIGEST_LENGTH]; u_char RecvKey[SHA_DIGEST_LENGTH]; int KeyLen; 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; ctx = PPP_MD_CTX_new(); if (ctx != NULL) { if (PPP_DigestInit(ctx, PPP_sha1())) { if (PPP_DigestUpdate(ctx, PasswordHashHash, MD4_DIGEST_LENGTH)) { if (PPP_DigestUpdate(ctx, NTResponse, 24)) { if (PPP_DigestUpdate(ctx, Magic1, sizeof(Magic1))) { KeyLen = SHA_DIGEST_LENGTH; PPP_DigestFinal(ctx, MasterKey, &KeyLen); } } } } PPP_MD_CTX_free(ctx); } /* * generate send key */ if (IsServer) s = Magic3; else s = Magic2; ctx = PPP_MD_CTX_new(); if (ctx != NULL) { if (PPP_DigestInit(ctx, PPP_sha1())) { if (PPP_DigestUpdate(ctx, MasterKey, 16)) { if (PPP_DigestUpdate(ctx, SHApad1, sizeof(SHApad1))) { if (PPP_DigestUpdate(ctx, s, 84)) { if (PPP_DigestUpdate(ctx, SHApad2, sizeof(SHApad2))) { KeyLen = SHA_DIGEST_LENGTH; PPP_DigestFinal(ctx, SendKey, &KeyLen); } } } } } PPP_MD_CTX_free(ctx); } /* * generate recv key */ if (IsServer) s = Magic2; else s = Magic3; ctx = PPP_MD_CTX_new(); if (ctx != NULL) { if (PPP_DigestInit(ctx, PPP_sha1())) { if (PPP_DigestUpdate(ctx, MasterKey, 16)) { if (PPP_DigestUpdate(ctx, SHApad1, sizeof(SHApad1))) { if (PPP_DigestUpdate(ctx, s, 84)) { if (PPP_DigestUpdate(ctx, SHApad2, sizeof(SHApad2))) { KeyLen = SHA_DIGEST_LENGTH; PPP_DigestFinal(ctx, RecvKey, &KeyLen); } } } } } PPP_MD_CTX_free(ctx); } mppe_set_keys(SendKey, RecvKey, SHA_DIGEST_LENGTH); } #ifndef UNIT_TEST /* * 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; } } #endif