blob: da10338e6f9edc9bc5d408f8466b3f270a446b10 [file] [log] [blame]
/* 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 <string.h>
#include "pppd-private.h"
#include "fsm.h"
#include "ccp.h"
#include "chap_ms.h"
#include "mppe.h"
#include "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