Add MPPE and MSChap v2 support (denied and disabled by default)
Submitted by: Ustimenko Semen <semen@iclub.nsu.ru>
This commit is contained in:
parent
fa99586d3a
commit
a8d604ab74
|
@ -58,7 +58,7 @@ SRCS+= id.c
|
|||
.if exists(${.CURDIR}/../../secure) && !defined(NOCRYPT) && !defined(NOSECURE) && !defined(NO_OPENSSL) && !defined(RELEASE_CRUNCH)
|
||||
DISTRIBUTION=crypto
|
||||
CFLAGS+=-DHAVE_DES
|
||||
SRCS+= chap_ms.c
|
||||
SRCS+= chap_ms.c mppe.c
|
||||
LDADD+= -lcrypto
|
||||
DPADD+= ${LIBCRYPTO}
|
||||
.endif
|
||||
|
|
|
@ -61,6 +61,9 @@
|
|||
#ifndef NORADIUS
|
||||
#include "radius.h"
|
||||
#endif
|
||||
#ifdef HAVE_DES
|
||||
#include "mppe.h"
|
||||
#endif
|
||||
#include "bundle.h"
|
||||
|
||||
static void CcpSendConfigReq(struct fsm *);
|
||||
|
@ -106,7 +109,8 @@ protoname(int proto)
|
|||
NULL, NULL, NULL, NULL, NULL, NULL,
|
||||
"HWPPC", /* 16: Hewlett-Packard PPC */
|
||||
"STAC", /* 17: Stac Electronics LZS (rfc1974) */
|
||||
"MPPC", /* 18: Microsoft PPC (rfc2118) */
|
||||
"MPPE", /* 18: Microsoft PPC (rfc2118) and */
|
||||
/* Microsoft PPE (draft-ietf-pppext-mppe) */
|
||||
"GAND", /* 19: Gandalf FZA (rfc1993) */
|
||||
"V42BIS", /* 20: ARG->DATA.42bis compression */
|
||||
"BSD", /* 21: BSD LZW Compress */
|
||||
|
@ -130,6 +134,9 @@ static const struct ccp_algorithm * const algorithm[] = {
|
|||
&DeflateAlgorithm,
|
||||
&Pred1Algorithm,
|
||||
&PppdDeflateAlgorithm
|
||||
#ifdef HAVE_DES
|
||||
, &MPPEAlgorithm
|
||||
#endif
|
||||
};
|
||||
|
||||
#define NALGORITHMS (sizeof algorithm/sizeof algorithm[0])
|
||||
|
@ -167,6 +174,11 @@ ccp_ReportStatus(struct cmdargs const *arg)
|
|||
command_ShowNegval(ccp->cfg.neg[CCP_NEG_PRED1]));
|
||||
prompt_Printf(arg->prompt, " DEFLATE24: %s\n",
|
||||
command_ShowNegval(ccp->cfg.neg[CCP_NEG_DEFLATE24]));
|
||||
#ifdef HAVE_DES
|
||||
prompt_Printf(arg->prompt, " MPPE: %s\n",
|
||||
command_ShowNegval(ccp->cfg.neg[CCP_NEG_MPPE]));
|
||||
prompt_Printf(arg->prompt, "Key Size = %d-bits\n", ccp->cfg.mppe.keybits);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -196,6 +208,10 @@ ccp_Init(struct ccp *ccp, struct bundle *bundle, struct link *l,
|
|||
ccp->cfg.neg[CCP_NEG_DEFLATE] = NEG_ENABLED|NEG_ACCEPTED;
|
||||
ccp->cfg.neg[CCP_NEG_PRED1] = NEG_ENABLED|NEG_ACCEPTED;
|
||||
ccp->cfg.neg[CCP_NEG_DEFLATE24] = 0;
|
||||
#ifdef HAVE_DES
|
||||
ccp->cfg.mppe.keybits = 128;
|
||||
ccp->cfg.neg[CCP_NEG_MPPE] = 0;
|
||||
#endif
|
||||
|
||||
ccp_Setup(ccp);
|
||||
}
|
||||
|
|
|
@ -29,6 +29,7 @@
|
|||
#define TY_HWPPC 16 /* Hewlett-Packard PPC */
|
||||
#define TY_STAC 17 /* Stac Electronics LZS */
|
||||
#define TY_MSPPC 18 /* Microsoft PPC */
|
||||
#define TY_MPPE 18 /* Microsoft PPE */
|
||||
#define TY_GAND 19 /* Gandalf FZA */
|
||||
#define TY_V42BIS 20 /* V.42bis compression */
|
||||
#define TY_BSD 21 /* BSD LZW Compress */
|
||||
|
@ -38,7 +39,12 @@
|
|||
#define CCP_NEG_DEFLATE 0
|
||||
#define CCP_NEG_PRED1 1
|
||||
#define CCP_NEG_DEFLATE24 2
|
||||
#ifdef HAVE_DES
|
||||
#define CCP_NEG_MPPE 3
|
||||
#define CCP_NEG_TOTAL 4
|
||||
#else
|
||||
#define CCP_NEG_TOTAL 3
|
||||
#endif
|
||||
|
||||
struct mbuf;
|
||||
struct link;
|
||||
|
@ -49,6 +55,11 @@ struct ccp_config {
|
|||
int winsize;
|
||||
} in, out;
|
||||
} deflate;
|
||||
#ifdef HAVE_DES
|
||||
struct {
|
||||
int keybits;
|
||||
} mppe;
|
||||
#endif
|
||||
struct fsm_retry fsm; /* How often/frequently to resend requests */
|
||||
unsigned neg[CCP_NEG_TOTAL];
|
||||
};
|
||||
|
|
|
@ -75,6 +75,7 @@
|
|||
#include "datalink.h"
|
||||
#ifdef HAVE_DES
|
||||
#include "chap_ms.h"
|
||||
#include "mppe.h"
|
||||
#endif
|
||||
#include "id.h"
|
||||
|
||||
|
@ -111,7 +112,7 @@ ChapOutput(struct physical *physical, u_int code, u_int id,
|
|||
static char *
|
||||
chap_BuildAnswer(char *name, char *key, u_char id, char *challenge, u_char type
|
||||
#ifdef HAVE_DES
|
||||
, int lanman
|
||||
, char *peerchallenge, char *authresponse, int lanman
|
||||
#endif
|
||||
)
|
||||
{
|
||||
|
@ -167,6 +168,51 @@ chap_BuildAnswer(char *name, char *key, u_char id, char *challenge, u_char type
|
|||
* ---- -------- ------------- ----- ------
|
||||
* where only one of LANMan & NT digest are set.
|
||||
*/
|
||||
} else if (type == 0x81) {
|
||||
char expkey[AUTHLEN << 2];
|
||||
char pwdhash[CHAP81_HASH_LEN];
|
||||
char pwdhashhash[CHAP81_HASH_LEN];
|
||||
char *ntresponse;
|
||||
int f;
|
||||
|
||||
if ((result = malloc(1 + nlen + CHAP81_RESPONSE_LEN)) == NULL)
|
||||
return result;
|
||||
|
||||
memset(result, 0, 1 + nlen + CHAP81_RESPONSE_LEN);
|
||||
|
||||
digest = result;
|
||||
*digest++ = CHAP81_RESPONSE_LEN; /* value size */
|
||||
|
||||
/* Copy our challenge */
|
||||
memcpy(digest, peerchallenge + 1, CHAP81_CHALLENGE_LEN);
|
||||
|
||||
/* Expand password to Unicode XXX */
|
||||
for (f = 0; f < klen; f++) {
|
||||
expkey[2*f] = key[f];
|
||||
expkey[2*f+1] = '\0';
|
||||
}
|
||||
|
||||
ntresponse = digest + CHAP81_NTRESPONSE_OFF;
|
||||
|
||||
/* Get some needed hashes */
|
||||
NtPasswordHash(expkey, klen * 2, pwdhash);
|
||||
HashNtPasswordHash(pwdhash, pwdhashhash);
|
||||
|
||||
/* Generate NTRESPONSE to respond on challenge call */
|
||||
GenerateNTResponse(challenge + 1, peerchallenge + 1, name, nlen,
|
||||
expkey, klen * 2, ntresponse);
|
||||
|
||||
/* Generate MPPE MASTERKEY */
|
||||
GetMasterKey(pwdhashhash, ntresponse, MPPE_MasterKey);
|
||||
|
||||
/* Generate AUTHRESPONSE to verify on auth success */
|
||||
GenerateAuthenticatorResponse(expkey, klen * 2, ntresponse,
|
||||
peerchallenge + 1, challenge + 1, name, nlen,
|
||||
authresponse);
|
||||
|
||||
authresponse[CHAP81_AUTHRESPONSE_LEN] = 0;
|
||||
|
||||
memcpy(digest + CHAP81_RESPONSE_LEN, name, nlen);
|
||||
} else
|
||||
#endif
|
||||
if ((result = malloc(nlen + 17)) != NULL) {
|
||||
|
@ -316,7 +362,7 @@ chap_Respond(struct chap *chap, char *name, char *key, u_char type
|
|||
|
||||
ans = chap_BuildAnswer(name, key, chap->auth.id, chap->challenge.peer, type
|
||||
#ifdef HAVE_DES
|
||||
, lm
|
||||
, chap->challenge.local, chap->authresponse, lm
|
||||
#endif
|
||||
);
|
||||
|
||||
|
@ -421,7 +467,7 @@ chap_Write(struct fdescriptor *d, struct bundle *bundle, const fd_set *fdset)
|
|||
}
|
||||
|
||||
static void
|
||||
chap_Challenge(struct authinfo *authp)
|
||||
chap_ChallengeInit(struct authinfo *authp)
|
||||
{
|
||||
struct chap *chap = auth2chap(authp);
|
||||
int len, i;
|
||||
|
@ -445,6 +491,8 @@ chap_Challenge(struct authinfo *authp)
|
|||
#ifdef HAVE_DES
|
||||
if (authp->physical->link.lcp.want_authtype == 0x80)
|
||||
*cp++ = 8; /* MS does 8 byte callenges :-/ */
|
||||
else if (authp->physical->link.lcp.want_authtype == 0x81)
|
||||
*cp++ = 16; /* MS-CHAP-V2 does 16 bytes challenges */
|
||||
else
|
||||
#endif
|
||||
*cp++ = random() % (CHAPCHALLENGELEN-16) + 16;
|
||||
|
@ -453,15 +501,48 @@ chap_Challenge(struct authinfo *authp)
|
|||
}
|
||||
memcpy(cp, authp->physical->dl->bundle->cfg.auth.name, len);
|
||||
}
|
||||
ChapOutput(authp->physical, CHAP_CHALLENGE, authp->id, chap->challenge.local,
|
||||
1 + *chap->challenge.local + len, NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
chap_Challenge(struct authinfo *authp)
|
||||
{
|
||||
struct chap *chap = auth2chap(authp);
|
||||
int len;
|
||||
|
||||
log_Printf(LogDEBUG, "CHAP%02X: Challenge\n", authp->physical->link.lcp.want_authtype);
|
||||
|
||||
len = strlen(authp->physical->dl->bundle->cfg.auth.name);
|
||||
|
||||
/* Generate new local challenge value */
|
||||
if (!*chap->challenge.local)
|
||||
chap_ChallengeInit(authp);
|
||||
|
||||
#ifdef HAVE_DES
|
||||
if (authp->physical->link.lcp.want_authtype == 0x81)
|
||||
ChapOutput(authp->physical, CHAP_CHALLENGE, authp->id,
|
||||
chap->challenge.local, 1 + *chap->challenge.local, NULL);
|
||||
else
|
||||
#endif
|
||||
ChapOutput(authp->physical, CHAP_CHALLENGE, authp->id,
|
||||
chap->challenge.local, 1 + *chap->challenge.local + len, NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
chap_Success(struct authinfo *authp)
|
||||
{
|
||||
char *msg;
|
||||
datalink_GotAuthname(authp->physical->dl, authp->in.name);
|
||||
ChapOutput(authp->physical, CHAP_SUCCESS, authp->id, "Welcome!!", 10, NULL);
|
||||
#ifdef HAVE_DES
|
||||
if (authp->physical->link.lcp.want_authtype == 0x81) {
|
||||
msg = auth2chap(authp)->authresponse;
|
||||
MPPE_MasterKeyValid = 1;
|
||||
} else
|
||||
#endif
|
||||
msg = "Welcome!!";
|
||||
|
||||
ChapOutput(authp->physical, CHAP_SUCCESS, authp->id, msg, strlen(msg) + 1,
|
||||
NULL);
|
||||
|
||||
authp->physical->link.lcp.auth_ineed = 0;
|
||||
if (Enabled(authp->physical->dl->bundle, OPT_UTMP))
|
||||
physical_Login(authp->physical, authp->in.name);
|
||||
|
@ -477,7 +558,28 @@ chap_Success(struct authinfo *authp)
|
|||
static void
|
||||
chap_Failure(struct authinfo *authp)
|
||||
{
|
||||
ChapOutput(authp->physical, CHAP_FAILURE, authp->id, "Invalid!!", 9, NULL);
|
||||
#ifdef HAVE_DES
|
||||
char buf[1024];
|
||||
#endif
|
||||
char *msg;
|
||||
|
||||
#ifdef HAVE_DES
|
||||
if (authp->physical->link.lcp.want_authtype == 0x81) {
|
||||
int i;
|
||||
|
||||
msg = buf;
|
||||
msg += sprintf(buf, "E=691 R=0 C=");
|
||||
for (i=0; i<16; i++)
|
||||
msg += sprintf(msg, "%02X", *(auth2chap(authp)->challenge.local+1+i));
|
||||
|
||||
sprintf(msg, " V=3 M=Invalid!");
|
||||
msg = buf;
|
||||
} else
|
||||
#endif
|
||||
msg = "Invalid!!";
|
||||
|
||||
ChapOutput(authp->physical, CHAP_FAILURE, authp->id, msg, strlen(msg) + 1,
|
||||
NULL);
|
||||
datalink_AuthNotOk(authp->physical->dl);
|
||||
}
|
||||
|
||||
|
@ -610,6 +712,9 @@ chap_Input(struct bundle *bundle, struct link *l, struct mbuf *bp)
|
|||
lanman = p->link.lcp.his_authtype == 0x80 &&
|
||||
((chap->NTRespSent && IsAccepted(p->link.lcp.cfg.chap80lm)) ||
|
||||
!IsAccepted(p->link.lcp.cfg.chap80nt));
|
||||
|
||||
/* Generate local challenge value */
|
||||
chap_ChallengeInit(&chap->auth);
|
||||
#endif
|
||||
break;
|
||||
|
||||
|
@ -632,7 +737,8 @@ chap_Input(struct bundle *bundle, struct link *l, struct mbuf *bp)
|
|||
ans[alen+1] = '\0';
|
||||
bp = auth_ReadName(&chap->auth, bp, len);
|
||||
#ifdef HAVE_DES
|
||||
lanman = alen == 49 && ans[alen] == 0;
|
||||
lanman = p->link.lcp.want_authtype == 0x80 &&
|
||||
alen == 49 && ans[alen] == 0;
|
||||
#endif
|
||||
break;
|
||||
|
||||
|
@ -717,25 +823,39 @@ chap_Input(struct bundle *bundle, struct link *l, struct mbuf *bp)
|
|||
if (key) {
|
||||
char *myans;
|
||||
#ifdef HAVE_DES
|
||||
if (lanman && !IsEnabled(p->link.lcp.cfg.chap80lm)) {
|
||||
if (p->link.lcp.want_authtype == 0x80 &&
|
||||
lanman && !IsEnabled(p->link.lcp.cfg.chap80lm)) {
|
||||
log_Printf(LogPHASE, "Auth failure: LANMan not enabled\n");
|
||||
if (chap_HaveAnotherGo(chap))
|
||||
break;
|
||||
key = NULL;
|
||||
} else if (!lanman && !IsEnabled(p->link.lcp.cfg.chap80nt) &&
|
||||
p->link.lcp.want_authtype == 0x80) {
|
||||
} else if (p->link.lcp.want_authtype == 0x80 &&
|
||||
!lanman && !IsEnabled(p->link.lcp.cfg.chap80nt)) {
|
||||
log_Printf(LogPHASE, "Auth failure: mschap not enabled\n");
|
||||
if (chap_HaveAnotherGo(chap))
|
||||
break;
|
||||
key = NULL;
|
||||
} else if (p->link.lcp.want_authtype == 0x81 &&
|
||||
!IsEnabled(p->link.lcp.cfg.chap81)) {
|
||||
log_Printf(LogPHASE, "Auth failure: CHAP81 not enabled\n");
|
||||
key = NULL;
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
#ifdef HAVE_DES
|
||||
/* Get peer's challenge */
|
||||
if (p->link.lcp.want_authtype == 0x81) {
|
||||
chap->challenge.peer[0] = CHAP81_CHALLENGE_LEN;
|
||||
memcpy(chap->challenge.peer + 1, ans + 1, CHAP81_CHALLENGE_LEN);
|
||||
}
|
||||
#endif
|
||||
|
||||
myans = chap_BuildAnswer(name, key, chap->auth.id,
|
||||
chap->challenge.local,
|
||||
p->link.lcp.want_authtype
|
||||
#ifdef HAVE_DES
|
||||
, lanman
|
||||
, chap->challenge.peer,
|
||||
chap->authresponse, lanman
|
||||
#endif
|
||||
);
|
||||
if (myans == NULL)
|
||||
|
@ -764,13 +884,27 @@ chap_Input(struct bundle *bundle, struct link *l, struct mbuf *bp)
|
|||
case CHAP_SUCCESS:
|
||||
if (p->link.lcp.auth_iwait == PROTO_CHAP) {
|
||||
p->link.lcp.auth_iwait = 0;
|
||||
if (p->link.lcp.auth_ineed == 0)
|
||||
if (p->link.lcp.auth_ineed == 0) {
|
||||
#ifdef HAVE_DES
|
||||
if (p->link.lcp.his_authtype == 0x81) {
|
||||
if (strncmp(ans, chap->authresponse, 42)) {
|
||||
datalink_AuthNotOk(p->dl);
|
||||
log_Printf(LogDEBUG, "CHAP81: AuthenticatorResponse: (%s) != ans: (%s)\n", chap->authresponse, ans);
|
||||
|
||||
} else {
|
||||
/* Successful login */
|
||||
MPPE_MasterKeyValid = 1;
|
||||
datalink_AuthOk(p->dl);
|
||||
}
|
||||
} else
|
||||
#endif
|
||||
/*
|
||||
* We've succeeded in our ``login''
|
||||
* If we're not expecting the peer to authenticate (or he already
|
||||
* has), proceed to network phase.
|
||||
*/
|
||||
datalink_AuthOk(p->dl);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
|
|
|
@ -46,12 +46,14 @@ struct chap {
|
|||
#ifdef HAVE_DES
|
||||
unsigned NTRespSent : 1; /* Our last response */
|
||||
int peertries;
|
||||
u_char authresponse[CHAPAUTHRESPONSELEN]; /* CHAP 81 response */
|
||||
#endif
|
||||
};
|
||||
|
||||
#define descriptor2chap(d) \
|
||||
((d)->type == CHAP_DESCRIPTOR ? (struct chap *)(d) : NULL)
|
||||
#define auth2chap(a) (struct chap *)((char *)a - (int)&((struct chap *)0)->auth)
|
||||
#define auth2chap(a) \
|
||||
((struct chap *)((char *)a - (int)&((struct chap *)0)->auth))
|
||||
|
||||
extern void chap_Init(struct chap *, struct physical *);
|
||||
extern void chap_ReInit(struct chap *);
|
||||
|
|
|
@ -31,10 +31,32 @@
|
|||
#else
|
||||
#include <des.h>
|
||||
#endif
|
||||
#include <sha.h>
|
||||
#include <md4.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "chap_ms.h"
|
||||
|
||||
/*
|
||||
* Documentation & specifications:
|
||||
*
|
||||
* MS-CHAP (CHAP80) rfc2433
|
||||
* MS-CHAP-V2 (CHAP81) rfc2759
|
||||
* MPPE key management draft-ietf-pppext-mppe-keys-02.txt
|
||||
*/
|
||||
|
||||
static char SHA1_Pad1[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};
|
||||
|
||||
static char SHA1_Pad2[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};
|
||||
|
||||
/* unused, for documentation only */
|
||||
/* only NTResp is filled in for FreeBSD */
|
||||
struct MS_ChapResponse {
|
||||
|
@ -95,6 +117,217 @@ ChallengeResponse(u_char *challenge, u_char *pwHash, u_char *response)
|
|||
DesEncrypt(challenge, ZPasswordHash + 14, response + 16);
|
||||
}
|
||||
|
||||
void
|
||||
NtPasswordHash(char *key, int keylen, char *hash) {
|
||||
MD4_CTX MD4context;
|
||||
|
||||
MD4Init(&MD4context);
|
||||
MD4Update(&MD4context, key, keylen);
|
||||
MD4Final(hash, &MD4context);
|
||||
}
|
||||
|
||||
void
|
||||
HashNtPasswordHash(char *hash, char *hashhash) {
|
||||
MD4_CTX MD4context;
|
||||
|
||||
MD4Init(&MD4context);
|
||||
MD4Update(&MD4context, hash, 16);
|
||||
MD4Final(hashhash, &MD4context);
|
||||
}
|
||||
|
||||
void
|
||||
ChallengeHash(char *PeerChallenge, char *AuthenticatorChallenge, char *UserName, int UserNameLen, char *Challenge) {
|
||||
SHA_CTX Context;
|
||||
char Digest[SHA_DIGEST_LENGTH];
|
||||
char *Name;
|
||||
|
||||
Name = strrchr(UserName, '\\');
|
||||
if(NULL == Name)
|
||||
Name = UserName;
|
||||
else
|
||||
Name++;
|
||||
|
||||
SHA1_Init(&Context);
|
||||
|
||||
SHA1_Update(&Context, PeerChallenge, 16);
|
||||
SHA1_Update(&Context, AuthenticatorChallenge, 16);
|
||||
SHA1_Update(&Context, UserName, UserNameLen);
|
||||
|
||||
SHA1_Final(Digest, &Context);
|
||||
memcpy(Challenge, Digest, 8);
|
||||
}
|
||||
|
||||
void
|
||||
GenerateNTResponse(char *AuthenticatorChallenge, char *PeerChallenge, char *UserName, int UserNameLen, char *Password, int PasswordLen, char *Response) {
|
||||
char Challenge[8];
|
||||
char PasswordHash[16];
|
||||
|
||||
ChallengeHash(PeerChallenge, AuthenticatorChallenge, UserName, UserNameLen,
|
||||
Challenge);
|
||||
NtPasswordHash(Password, PasswordLen, PasswordHash);
|
||||
ChallengeResponse(Challenge, PasswordHash, Response);
|
||||
}
|
||||
|
||||
void
|
||||
GenerateAuthenticatorResponse(char *Password, int PasswordLen, char *NTResponse, char *PeerChallenge, char *AuthenticatorChallenge, char *UserName, int UserNameLen, char *AuthenticatorResponse) {
|
||||
SHA_CTX Context;
|
||||
char PasswordHash[16];
|
||||
char PasswordHashHash[16];
|
||||
char Challenge[8];
|
||||
u_char Digest[SHA_DIGEST_LENGTH];
|
||||
int i;
|
||||
|
||||
/*
|
||||
* "Magic" constants used in response generation
|
||||
*/
|
||||
char Magic1[39] =
|
||||
{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};
|
||||
|
||||
|
||||
char Magic2[41] =
|
||||
{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};
|
||||
/*
|
||||
* Hash the password with MD4
|
||||
*/
|
||||
NtPasswordHash(Password, PasswordLen, PasswordHash);
|
||||
/*
|
||||
* Now hash the hash
|
||||
*/
|
||||
HashNtPasswordHash(PasswordHash, PasswordHashHash);
|
||||
|
||||
SHA1_Init(&Context);
|
||||
SHA1_Update(&Context, PasswordHashHash, 16);
|
||||
SHA1_Update(&Context, NTResponse, 24);
|
||||
SHA1_Update(&Context, Magic1, 39);
|
||||
SHA1_Final(Digest, &Context);
|
||||
ChallengeHash(PeerChallenge, AuthenticatorChallenge, UserName, UserNameLen,
|
||||
Challenge);
|
||||
SHA1_Init(&Context);
|
||||
SHA1_Update(&Context, Digest, 20);
|
||||
SHA1_Update(&Context, Challenge, 8);
|
||||
SHA1_Update(&Context, Magic2, 41);
|
||||
|
||||
/*
|
||||
* Encode the value of 'Digest' as "S=" followed by
|
||||
* 40 ASCII hexadecimal digits and return it in
|
||||
* AuthenticatorResponse.
|
||||
* For example,
|
||||
* "S=0123456789ABCDEF0123456789ABCDEF01234567"
|
||||
*/
|
||||
AuthenticatorResponse[0] = 'S';
|
||||
AuthenticatorResponse[1] = '=';
|
||||
SHA1_End(&Context, AuthenticatorResponse + 2);
|
||||
for (i=2; i<42; i++)
|
||||
AuthenticatorResponse[i] = toupper(AuthenticatorResponse[i]);
|
||||
|
||||
}
|
||||
|
||||
void
|
||||
GetMasterKey(char *PasswordHashHash, char *NTResponse, char *MasterKey) {
|
||||
char Digest[SHA_DIGEST_LENGTH];
|
||||
SHA_CTX Context;
|
||||
static 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};
|
||||
|
||||
SHA1_Init(&Context);
|
||||
SHA1_Update(&Context, PasswordHashHash, 16);
|
||||
SHA1_Update(&Context, NTResponse, 24);
|
||||
SHA1_Update(&Context, Magic1, 27);
|
||||
SHA1_Final(Digest, &Context);
|
||||
memcpy(MasterKey, Digest, 16);
|
||||
}
|
||||
|
||||
void
|
||||
GetAsymetricStartKey(char *MasterKey, char *SessionKey, int SessionKeyLength, int IsSend, int IsServer) {
|
||||
char Digest[SHA_DIGEST_LENGTH];
|
||||
SHA_CTX Context;
|
||||
char *s;
|
||||
|
||||
static 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};
|
||||
|
||||
static 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};
|
||||
|
||||
if (IsSend) {
|
||||
if (IsServer) {
|
||||
s = Magic3;
|
||||
} else {
|
||||
s = Magic2;
|
||||
}
|
||||
} else {
|
||||
if (IsServer) {
|
||||
s = Magic2;
|
||||
} else {
|
||||
s = Magic3;
|
||||
}
|
||||
}
|
||||
|
||||
SHA1_Init(&Context);
|
||||
SHA1_Update(&Context, MasterKey, 16);
|
||||
SHA1_Update(&Context, SHA1_Pad1, 40);
|
||||
SHA1_Update(&Context, s, 84);
|
||||
SHA1_Update(&Context, SHA1_Pad2, 40);
|
||||
SHA1_Final(Digest, &Context);
|
||||
|
||||
memcpy(SessionKey, Digest, SessionKeyLength);
|
||||
}
|
||||
|
||||
void
|
||||
GetNewKeyFromSHA(char *StartKey, char *SessionKey, long SessionKeyLength, char *InterimKey) {
|
||||
SHA_CTX Context;
|
||||
char Digest[SHA_DIGEST_LENGTH];
|
||||
|
||||
SHA1_Init(&Context);
|
||||
SHA1_Update(&Context, StartKey, SessionKeyLength);
|
||||
SHA1_Update(&Context, SHA1_Pad1, 40);
|
||||
SHA1_Update(&Context, SessionKey, SessionKeyLength);
|
||||
SHA1_Update(&Context, SHA1_Pad2, 40);
|
||||
SHA1_Final(Digest, &Context);
|
||||
|
||||
memcpy(InterimKey, Digest, SessionKeyLength);
|
||||
}
|
||||
|
||||
void
|
||||
Get_Key(char *InitialSessionKey, char *CurrentSessionKey, int LengthOfDesiredKey) {
|
||||
SHA_CTX Context;
|
||||
char Digest[SHA_DIGEST_LENGTH];
|
||||
|
||||
SHA1_Init(&Context);
|
||||
SHA1_Update(&Context, InitialSessionKey, LengthOfDesiredKey);
|
||||
SHA1_Update(&Context, SHA1_Pad1, 40);
|
||||
SHA1_Update(&Context, CurrentSessionKey, LengthOfDesiredKey);
|
||||
SHA1_Update(&Context, SHA1_Pad2, 40);
|
||||
SHA1_Final(Digest, &Context);
|
||||
|
||||
memcpy(CurrentSessionKey, Digest, LengthOfDesiredKey);
|
||||
}
|
||||
|
||||
/* passwordHash 16-bytes MD4 hashed password
|
||||
challenge 8-bytes peer CHAP challenge
|
||||
since passwordHash is in a 24-byte buffer, response is written in there */
|
||||
|
|
|
@ -26,7 +26,21 @@
|
|||
#define MAX_NT_PASSWORD 256
|
||||
|
||||
/* Don't rely on sizeof(MS_ChapResponse) in case of struct padding */
|
||||
#define MS_CHAP_RESPONSE_LEN 49
|
||||
#define MS_CHAP_RESPONSE_LEN 49
|
||||
#define CHAP81_RESPONSE_LEN 49
|
||||
#define CHAP81_NTRESPONSE_LEN 24
|
||||
#define CHAP81_NTRESPONSE_OFF 24
|
||||
#define CHAP81_HASH_LEN 16
|
||||
#define CHAP81_AUTHRESPONSE_LEN 42
|
||||
#define CHAP81_CHALLENGE_LEN 16
|
||||
|
||||
extern void mschap_NT(char *, char *);
|
||||
extern void mschap_LANMan(char *, char *, char *);
|
||||
extern void GenerateNTResponse(char *, char *, char *, int, char *, int , char *);
|
||||
extern void HashNtPasswordHash(char *, char *);
|
||||
extern void NtPasswordHash(char *, int, char *);
|
||||
extern void ChallengeHash(char *, char *, char *UserName, int, char *);
|
||||
extern void GenerateAuthenticatorResponse(char *, int, char *, char *, char *, char *, int, char *);
|
||||
extern void GetAsymetricStartKey(char *, char *, int, int, int);
|
||||
extern void GetMasterKey(char *, char *, char *);
|
||||
extern void GetNewKeyFromSHA(char *, char *, long, char *);
|
||||
|
|
|
@ -127,6 +127,7 @@
|
|||
#define VAR_URGENTPORTS 33
|
||||
#define VAR_LOGOUT 34
|
||||
#define VAR_IFQUEUE 35
|
||||
#define VAR_KEYBITS 36
|
||||
|
||||
/* ``accept|deny|disable|enable'' masks */
|
||||
#define NEG_HISMASK (1)
|
||||
|
@ -147,6 +148,8 @@
|
|||
#define NEG_PROTOCOMP 51
|
||||
#define NEG_SHORTSEQ 52
|
||||
#define NEG_VJCOMP 53
|
||||
#define NEG_MPPE 54
|
||||
#define NEG_CHAP81 55
|
||||
|
||||
const char Version[] = "2.27";
|
||||
|
||||
|
@ -1574,6 +1577,24 @@ SetVariable(struct cmdargs const *arg)
|
|||
}
|
||||
break;
|
||||
|
||||
#ifdef HAVE_DES
|
||||
case VAR_KEYBITS:
|
||||
if (arg->argc > arg->argn) {
|
||||
l->ccp.cfg.mppe.keybits = atoi(arg->argv[arg->argn]);
|
||||
if (l->ccp.cfg.mppe.keybits != 40 &&
|
||||
l->ccp.cfg.mppe.keybits != 56 &&
|
||||
l->ccp.cfg.mppe.keybits != 128 ) {
|
||||
log_Printf(LogWARN, "%d: Invalid bits number\n",
|
||||
l->ccp.cfg.mppe.keybits);
|
||||
l->ccp.cfg.mppe.keybits = 40;
|
||||
}
|
||||
} else {
|
||||
err = "No bits number pecified\n";
|
||||
log_Printf(LogWARN, err);
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
|
||||
case VAR_DEVICE:
|
||||
physical_SetDeviceList(cx->physical, arg->argc - arg->argn,
|
||||
arg->argv + arg->argn);
|
||||
|
@ -1968,6 +1989,11 @@ static struct cmdtab const SetCommands[] = {
|
|||
{"deflate", NULL, SetVariable, LOCAL_AUTH | LOCAL_CX_OPT,
|
||||
"deflate window sizes", "set deflate out-winsize in-winsize",
|
||||
(const void *) VAR_WINSIZE},
|
||||
#ifdef HAVE_DES
|
||||
{"mppe", NULL, SetVariable, LOCAL_AUTH | LOCAL_CX_OPT,
|
||||
"MPPE key size", "set mppe {40|56|128}",
|
||||
(const void *) VAR_KEYBITS},
|
||||
#endif
|
||||
{"device", "line", SetVariable, LOCAL_AUTH | LOCAL_CX,
|
||||
"physical device name", "set device|line device-name[,device-name]",
|
||||
(const void *) VAR_DEVICE},
|
||||
|
@ -2414,6 +2440,14 @@ NegotiateSet(struct cmdargs const *arg)
|
|||
cx->physical->link.lcp.cfg.chap80lm &= keep;
|
||||
cx->physical->link.lcp.cfg.chap80lm |= add;
|
||||
break;
|
||||
case NEG_CHAP81:
|
||||
cx->physical->link.lcp.cfg.chap81 &= keep;
|
||||
cx->physical->link.lcp.cfg.chap81 |= add;
|
||||
break;
|
||||
case NEG_MPPE:
|
||||
l->ccp.cfg.neg[CCP_NEG_MPPE] &= keep;
|
||||
l->ccp.cfg.neg[CCP_NEG_MPPE] |= add;
|
||||
break;
|
||||
#endif
|
||||
case NEG_DEFLATE:
|
||||
l->ccp.cfg.neg[CCP_NEG_DEFLATE] &= keep;
|
||||
|
@ -2517,6 +2551,12 @@ static struct cmdtab const NegotiateCommands[] = {
|
|||
{"LANMan", "chap80lm", NegotiateSet, LOCAL_AUTH | LOCAL_CX,
|
||||
"Microsoft (NT) CHAP", "accept|deny|disable|enable",
|
||||
(const void *)NEG_CHAP80LM},
|
||||
{"mschapv2", "chap81", NegotiateSet, LOCAL_AUTH | LOCAL_CX,
|
||||
"Microsoft CHAP v2", "accept|deny|disable|enable",
|
||||
(const void *)NEG_CHAP81},
|
||||
{"mppe", NULL, NegotiateSet, LOCAL_AUTH | LOCAL_CX_OPT,
|
||||
"MPPE encryption", "accept|deny|disable|enable",
|
||||
(const void *)NEG_MPPE},
|
||||
#endif
|
||||
{"deflate", NULL, NegotiateSet, LOCAL_AUTH | LOCAL_CX_OPT,
|
||||
"Deflate compression", "accept|deny|disable|enable",
|
||||
|
|
|
@ -49,6 +49,7 @@
|
|||
#define AUTHLEN 100 /* Size of authname/authkey */
|
||||
#define CHAPDIGESTLEN 100 /* Maximum chap digest */
|
||||
#define CHAPCHALLENGELEN 48 /* Maximum chap challenge */
|
||||
#define CHAPAUTHRESPONSELEN 48 /* Maximum chap authresponse (chap81) */
|
||||
#define MAXARGS 40 /* How many args per config line */
|
||||
#define NCP_IDLE_TIMEOUT 180 /* Drop all links */
|
||||
#define CHOKED_TIMEOUT 120 /* Delete queued packets w/ blocked tun */
|
||||
|
|
|
@ -193,6 +193,8 @@ lcp_ReportStatus(struct cmdargs const *arg)
|
|||
command_ShowNegval(lcp->cfg.chap80nt));
|
||||
prompt_Printf(arg->prompt, " LANMan = %s\n",
|
||||
command_ShowNegval(lcp->cfg.chap80lm));
|
||||
prompt_Printf(arg->prompt, " CHAP81 = %s\n",
|
||||
command_ShowNegval(lcp->cfg.chap81));
|
||||
#endif
|
||||
prompt_Printf(arg->prompt, " LQR = %s\n",
|
||||
command_ShowNegval(lcp->cfg.lqr));
|
||||
|
@ -244,6 +246,7 @@ lcp_Init(struct lcp *lcp, struct bundle *bundle, struct link *l,
|
|||
#ifdef HAVE_DES
|
||||
lcp->cfg.chap80nt = NEG_ACCEPTED;
|
||||
lcp->cfg.chap80lm = NEG_ACCEPTED;
|
||||
lcp->cfg.chap81 = 0;
|
||||
#endif
|
||||
lcp->cfg.lqr = NEG_ACCEPTED;
|
||||
lcp->cfg.pap = NEG_ACCEPTED;
|
||||
|
@ -292,6 +295,9 @@ lcp_Setup(struct lcp *lcp, int openmode)
|
|||
IsEnabled(lcp->cfg.chap80lm)) {
|
||||
lcp->want_auth = PROTO_CHAP;
|
||||
lcp->want_authtype = 0x80;
|
||||
} else if (IsEnabled(lcp->cfg.chap81)) {
|
||||
lcp->want_auth = PROTO_CHAP;
|
||||
lcp->want_authtype = 0x81;
|
||||
#endif
|
||||
} else if (IsEnabled(lcp->cfg.pap)) {
|
||||
lcp->want_auth = PROTO_PAP;
|
||||
|
@ -733,6 +739,12 @@ LcpDecodeConfig(struct fsm *fp, u_char *cp, int plen, int mode_type,
|
|||
*dec->nakend++ = (unsigned char) (PROTO_CHAP >> 8);
|
||||
*dec->nakend++ = (unsigned char) PROTO_CHAP;
|
||||
*dec->nakend++ = 0x80;
|
||||
} else if (IsAccepted(lcp->cfg.chap81)) {
|
||||
*dec->nakend++ = *cp;
|
||||
*dec->nakend++ = 5;
|
||||
*dec->nakend++ = (unsigned char) (PROTO_CHAP >> 8);
|
||||
*dec->nakend++ = (unsigned char) PROTO_CHAP;
|
||||
*dec->nakend++ = 0x81;
|
||||
#endif
|
||||
} else
|
||||
goto reqreject;
|
||||
|
@ -747,6 +759,7 @@ LcpDecodeConfig(struct fsm *fp, u_char *cp, int plen, int mode_type,
|
|||
#ifdef HAVE_DES
|
||||
|| (cp[4] == 0x80 && (IsAccepted(lcp->cfg.chap80nt) ||
|
||||
(IsAccepted(lcp->cfg.chap80lm))))
|
||||
|| (cp[4] == 0x81 && IsAccepted(lcp->cfg.chap81))
|
||||
#endif
|
||||
) {
|
||||
lcp->his_auth = proto;
|
||||
|
@ -755,9 +768,11 @@ LcpDecodeConfig(struct fsm *fp, u_char *cp, int plen, int mode_type,
|
|||
dec->ackend += length;
|
||||
} else {
|
||||
#ifndef HAVE_DES
|
||||
if (cp[4] == 0x80)
|
||||
if (cp[4] == 0x80) {
|
||||
log_Printf(LogWARN, "CHAP 0x80 not available without DES\n");
|
||||
else
|
||||
} else if (cp[4] == 0x81) {
|
||||
log_Printf(LogWARN, "CHAP 0x81 not available without DES\n");
|
||||
} else
|
||||
#endif
|
||||
if (cp[4] != 0x05)
|
||||
log_Printf(LogWARN, "%s not supported\n",
|
||||
|
@ -777,6 +792,12 @@ LcpDecodeConfig(struct fsm *fp, u_char *cp, int plen, int mode_type,
|
|||
*dec->nakend++ = (unsigned char) (PROTO_CHAP >> 8);
|
||||
*dec->nakend++ = (unsigned char) PROTO_CHAP;
|
||||
*dec->nakend++ = 0x80;
|
||||
} else if (IsAccepted(lcp->cfg.chap81)) {
|
||||
*dec->nakend++ = *cp;
|
||||
*dec->nakend++ = 5;
|
||||
*dec->nakend++ = (unsigned char) (PROTO_CHAP >> 8);
|
||||
*dec->nakend++ = (unsigned char) PROTO_CHAP;
|
||||
*dec->nakend++ = 0x81;
|
||||
#endif
|
||||
} else if (IsAccepted(lcp->cfg.pap)) {
|
||||
*dec->nakend++ = *cp;
|
||||
|
@ -816,18 +837,24 @@ LcpDecodeConfig(struct fsm *fp, u_char *cp, int plen, int mode_type,
|
|||
IsEnabled(lcp->cfg.chap80lm))) {
|
||||
lcp->want_auth = PROTO_CHAP;
|
||||
lcp->want_authtype = 0x80;
|
||||
} else if (cp[4] == 0x81 && IsEnabled(lcp->cfg.chap81)) {
|
||||
lcp->want_auth = PROTO_CHAP;
|
||||
lcp->want_authtype = 0x81;
|
||||
#endif
|
||||
} else {
|
||||
#ifndef HAVE_DES
|
||||
if (cp[4] == 0x80)
|
||||
if (cp[4] == 0x80) {
|
||||
log_Printf(LogLCP, "Peer will only send MSCHAP (not available"
|
||||
" without DES)\n");
|
||||
else
|
||||
} else if (cp[4] == 0x81) {
|
||||
log_Printf(LogLCP, "Peer will only send MSCHAPV2 (not available"
|
||||
" without DES)\n");
|
||||
} else
|
||||
#endif
|
||||
log_Printf(LogLCP, "Peer will only send %s (not %s)\n",
|
||||
Auth2Nam(PROTO_CHAP, cp[4]),
|
||||
#ifdef HAVE_DES
|
||||
cp[4] == 0x80 ? "configured" :
|
||||
(cp[4] == 0x80 || cp[4] == 0x81) ? "configured" :
|
||||
#endif
|
||||
"supported");
|
||||
lcp->his_reject |= (1 << type);
|
||||
|
|
|
@ -83,6 +83,7 @@ struct lcp {
|
|||
#ifdef HAVE_DES
|
||||
unsigned chap80nt : 2; /* Microsoft (NT) CHAP */
|
||||
unsigned chap80lm : 2; /* Microsoft (LANMan) CHAP */
|
||||
unsigned chap81 : 2; /* Microsoft CHAP v2 */
|
||||
#endif
|
||||
unsigned lqr : 2; /* Link Quality Report */
|
||||
unsigned pap : 2; /* Password Authentication protocol */
|
||||
|
|
|
@ -0,0 +1,422 @@
|
|||
/*-
|
||||
* Copyright (c) 2000 Semen Ustimenko <semenu@FreeBSD.org>
|
||||
* 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.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <termios.h>
|
||||
#include <sha.h>
|
||||
#include <openssl/rc4.h>
|
||||
|
||||
#include "defs.h"
|
||||
#include "mbuf.h"
|
||||
#include "log.h"
|
||||
#include "timer.h"
|
||||
#include "fsm.h"
|
||||
#include "lqr.h"
|
||||
#include "hdlc.h"
|
||||
#include "lcp.h"
|
||||
#include "ccp.h"
|
||||
#include "chap_ms.h"
|
||||
#include "mppe.h"
|
||||
|
||||
/*
|
||||
* Documentation:
|
||||
*
|
||||
* draft-ietf-pppext-mppe-04.txt
|
||||
* draft-ietf-pppext-mppe-keys-02.txt
|
||||
*/
|
||||
|
||||
struct mppe_state {
|
||||
int cohnum;
|
||||
int keylen; /* 8 or 16 bytes */
|
||||
int keybits; /* 40, 56 or 128 bits */
|
||||
char sesskey[MPPE_KEY_LEN];
|
||||
char mastkey[MPPE_KEY_LEN];
|
||||
RC4_KEY rc4key;
|
||||
};
|
||||
|
||||
int MPPE_MasterKeyValid = 0;
|
||||
char MPPE_MasterKey[MPPE_KEY_LEN];
|
||||
|
||||
static void
|
||||
MPPEResetOutput(void *v)
|
||||
{
|
||||
log_Printf(LogCCP, "MPPE: Output channel reset\n");
|
||||
}
|
||||
|
||||
void
|
||||
MPPEReduceSessionKey(struct mppe_state *mp) {
|
||||
switch(mp->keybits) {
|
||||
case 40:
|
||||
mp->sesskey[2] = 0x9e;
|
||||
mp->sesskey[1] = 0x26;
|
||||
case 56:
|
||||
mp->sesskey[0] = 0xd1;
|
||||
case 128:
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
MPPEKeyChange(struct mppe_state *mp) {
|
||||
char InterimKey[MPPE_KEY_LEN];
|
||||
RC4_KEY RC4Key;
|
||||
|
||||
GetNewKeyFromSHA(mp->mastkey, mp->sesskey, mp->keylen, InterimKey);
|
||||
RC4_set_key(&RC4Key, mp->keylen, InterimKey);
|
||||
RC4(&RC4Key, mp->keylen, InterimKey, mp->sesskey);
|
||||
|
||||
MPPEReduceSessionKey(mp);
|
||||
}
|
||||
|
||||
static struct mbuf *
|
||||
MPPEOutput(void *v, struct ccp *ccp, struct link *l, int pri, u_short *proto,
|
||||
struct mbuf *mp)
|
||||
{
|
||||
struct mppe_state *mop = (struct mppe_state *)v;
|
||||
struct mbuf *mo;
|
||||
u_short nproto;
|
||||
int ilen;
|
||||
char *rp;
|
||||
|
||||
log_Printf(LogCCP, "MPPE: Output\n");
|
||||
|
||||
ilen = m_length(mp);
|
||||
|
||||
log_Printf(LogDEBUG, "MPPE: Output: Proto %02x (%d bytes)\n", *proto, ilen);
|
||||
if (*proto < 0x21 && *proto > 0xFA) {
|
||||
log_Printf(LogDEBUG, "MPPE: Output: Not encrypting\n");
|
||||
return mp;
|
||||
}
|
||||
|
||||
log_DumpBp(LogDEBUG, "MPPE: Output: Encrypt packet:", mp);
|
||||
|
||||
/* Get mbuf for prefixes */
|
||||
mo = m_get(4, MB_CCPOUT);
|
||||
mo->m_next = mp;
|
||||
|
||||
/* Init RC4 keys */
|
||||
RC4_set_key(&mop->rc4key, mop->keylen, mop->sesskey);
|
||||
|
||||
/* Set MPPE packet prefix */
|
||||
rp = MBUF_CTOP(mo);
|
||||
*(u_short *)rp = htons(0x9000 | mop->cohnum);
|
||||
|
||||
/* Save encrypted protocol number */
|
||||
nproto = htons(*proto);
|
||||
RC4(&mop->rc4key, 2, (char *)&nproto, rp + 2);
|
||||
|
||||
/* Encrypt main packet */
|
||||
rp = MBUF_CTOP(mp);
|
||||
RC4(&mop->rc4key, ilen, rp, rp);
|
||||
|
||||
/* Rotate keys */
|
||||
MPPEKeyChange(mop);
|
||||
mop->cohnum ++; mop->cohnum &= 0xFFF;
|
||||
|
||||
/* Chage protocol number */
|
||||
*proto = ccp_Proto(ccp);
|
||||
|
||||
log_Printf(LogDEBUG, "MPPE: Output: Encrypted: Proto %02x (%d bytes)\n", *proto, m_length(mo));
|
||||
|
||||
return mo;
|
||||
}
|
||||
|
||||
static void
|
||||
MPPEResetInput(void *v)
|
||||
{
|
||||
log_Printf(LogCCP, "MPPE: Input channel reset\n");
|
||||
}
|
||||
|
||||
static struct mbuf *
|
||||
MPPEInput(void *v, struct ccp *ccp, u_short *proto, struct mbuf *mp)
|
||||
{
|
||||
struct mppe_state *mip = (struct mppe_state *)v;
|
||||
u_short prefix;
|
||||
char *rp;
|
||||
int ilen;
|
||||
|
||||
log_Printf(LogCCP, "MPPE: Input\n");
|
||||
|
||||
ilen = m_length(mp);
|
||||
|
||||
log_Printf(LogDEBUG, "MPPE: Input: Proto %02x (%d bytes)\n", *proto, ilen);
|
||||
|
||||
log_DumpBp(LogDEBUG, "MPPE: Input: Packet:", mp);
|
||||
|
||||
mp = mbuf_Read(mp, &prefix, 2);
|
||||
prefix = ntohs(prefix);
|
||||
if ((prefix & 0xF000) != 0x9000) {
|
||||
log_Printf(LogERROR, "MPPE: Input: Invalid packet\n");
|
||||
m_freem(mp);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
prefix &= 0xFFF;
|
||||
while (prefix != mip->cohnum) {
|
||||
MPPEKeyChange(mip);
|
||||
mip->cohnum ++; mip->cohnum &= 0xFFF;
|
||||
}
|
||||
|
||||
RC4_set_key(&mip->rc4key, mip->keylen, mip->sesskey);
|
||||
|
||||
mp = mbuf_Read(mp, proto, 2);
|
||||
RC4(&mip->rc4key, 2, (char *)proto, (char *)proto);
|
||||
*proto = ntohs(*proto);
|
||||
|
||||
rp = MBUF_CTOP(mp);
|
||||
RC4(&mip->rc4key, m_length(mp), rp, rp);
|
||||
|
||||
log_Printf(LogDEBUG, "MPPE: Input: Decrypted: Proto %02x (%d bytes)\n", *proto, m_length(mp));
|
||||
|
||||
log_DumpBp(LogDEBUG, "MPPE: Input: Decrypted: Packet:", mp);
|
||||
|
||||
return mp;
|
||||
}
|
||||
|
||||
static void
|
||||
MPPEDictSetup(void *v, struct ccp *ccp, u_short proto, struct mbuf *mi)
|
||||
{
|
||||
log_Printf(LogCCP, "MPPE: DictSetup\n");
|
||||
}
|
||||
|
||||
static const char *
|
||||
MPPEDispOpts(struct lcp_opt *o)
|
||||
{
|
||||
static char buf[32];
|
||||
sprintf(buf, "value 0x%08x", (int)ntohl(*(u_int32_t *)(o->data)));
|
||||
return buf;
|
||||
}
|
||||
|
||||
static void
|
||||
MPPEInitOptsOutput(struct lcp_opt *o, const struct ccp_config *cfg)
|
||||
{
|
||||
u_long val;
|
||||
|
||||
o->len = 6;
|
||||
|
||||
log_Printf(LogCCP, "MPPE: InitOptsOutput\n");
|
||||
|
||||
if (!MPPE_MasterKeyValid) {
|
||||
log_Printf(LogWARN, "MPPE: MasterKey is invalid, MPPE is capable only with CHAP81 authentication\n");
|
||||
*(u_int32_t *)o->data = htonl(0x0);
|
||||
return;
|
||||
}
|
||||
|
||||
val = 0x1000000;
|
||||
switch(cfg->mppe.keybits) {
|
||||
case 128:
|
||||
val |= 0x40; break;
|
||||
case 56:
|
||||
val |= 0x80; break;
|
||||
case 40:
|
||||
val |= 0x20; break;
|
||||
}
|
||||
*(u_int32_t *)o->data = htonl(val);
|
||||
}
|
||||
|
||||
static int
|
||||
MPPESetOptsOutput(struct lcp_opt *o)
|
||||
{
|
||||
u_long *p = (u_long *)(o->data);
|
||||
u_long val = ntohl(*p);
|
||||
|
||||
log_Printf(LogCCP, "MPPE: SetOptsOutput\n");
|
||||
|
||||
if (!MPPE_MasterKeyValid) {
|
||||
if (*p != 0x0) {
|
||||
*p = 0x0;
|
||||
return MODE_NAK;
|
||||
} else {
|
||||
return MODE_ACK;
|
||||
}
|
||||
}
|
||||
|
||||
if (val == 0x01000020 ||
|
||||
val == 0x01000040 ||
|
||||
val == 0x01000080)
|
||||
return MODE_ACK;
|
||||
|
||||
return MODE_NAK;
|
||||
}
|
||||
|
||||
static int
|
||||
MPPESetOptsInput(struct lcp_opt *o, const struct ccp_config *cfg)
|
||||
{
|
||||
u_long *p = (u_long *)(o->data);
|
||||
u_long val = ntohl(*p);
|
||||
u_long mval;
|
||||
|
||||
log_Printf(LogCCP, "MPPE: SetOptsInput\n");
|
||||
|
||||
if (!MPPE_MasterKeyValid) {
|
||||
if (*p != 0x0) {
|
||||
*p = 0x0;
|
||||
return MODE_NAK;
|
||||
} else {
|
||||
return MODE_ACK;
|
||||
}
|
||||
}
|
||||
|
||||
mval = 0x01000000;
|
||||
switch(cfg->mppe.keybits) {
|
||||
case 128:
|
||||
mval |= 0x40; break;
|
||||
case 56:
|
||||
mval |= 0x80; break;
|
||||
case 40:
|
||||
mval |= 0x20; break;
|
||||
}
|
||||
|
||||
if (val == mval)
|
||||
return MODE_ACK;
|
||||
|
||||
*p = htonl(mval);
|
||||
|
||||
return MODE_NAK;
|
||||
}
|
||||
|
||||
static void *
|
||||
MPPEInitInput(struct lcp_opt *o)
|
||||
{
|
||||
struct mppe_state *mip;
|
||||
u_int32_t val = ntohl(*(unsigned long *)o->data);
|
||||
|
||||
log_Printf(LogCCP, "MPPE: InitInput\n");
|
||||
|
||||
if (!MPPE_MasterKeyValid) {
|
||||
log_Printf(LogERROR, "MPPE: InitInput: MasterKey is invalid!!!!\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
mip = malloc(sizeof(*mip));
|
||||
memset(mip, 0, sizeof(*mip));
|
||||
|
||||
if (val & 0x20) { /* 40-bits */
|
||||
mip->keylen = 8;
|
||||
mip->keybits = 40;
|
||||
} else if (val & 0x80) { /* 56-bits */
|
||||
mip->keylen = 8;
|
||||
mip->keybits = 56;
|
||||
} else { /* 128-bits */
|
||||
mip->keylen = 16;
|
||||
mip->keybits = 128;
|
||||
}
|
||||
|
||||
log_Printf(LogDEBUG, "MPPE: InitInput: %d-bits\n", mip->keybits);
|
||||
|
||||
GetAsymetricStartKey(MPPE_MasterKey, mip->mastkey, mip->keylen, 0, 0);
|
||||
GetNewKeyFromSHA(mip->mastkey, mip->mastkey, mip->keylen, mip->sesskey);
|
||||
|
||||
MPPEReduceSessionKey(mip);
|
||||
|
||||
MPPEKeyChange(mip);
|
||||
|
||||
mip->cohnum = 0;
|
||||
|
||||
return mip;
|
||||
}
|
||||
|
||||
static void *
|
||||
MPPEInitOutput(struct lcp_opt *o)
|
||||
{
|
||||
struct mppe_state *mop;
|
||||
u_int32_t val = ntohl(*(unsigned long *)o->data);
|
||||
|
||||
log_Printf(LogCCP, "MPPE: InitOutput\n");
|
||||
|
||||
if (!MPPE_MasterKeyValid) {
|
||||
log_Printf(LogERROR, "MPPE: InitOutput: MasterKey is invalid!!!!\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
mop = malloc(sizeof(*mop));
|
||||
memset(mop, 0, sizeof(*mop));
|
||||
|
||||
if (val & 0x20) { /* 40-bits */
|
||||
mop->keylen = 8;
|
||||
mop->keybits = 40;
|
||||
} else if (val & 0x80) { /* 56-bits */
|
||||
mop->keylen = 8;
|
||||
mop->keybits = 56;
|
||||
} else { /* 128-bits */
|
||||
mop->keylen = 16;
|
||||
mop->keybits = 128;
|
||||
}
|
||||
|
||||
log_Printf(LogDEBUG, "MPPE: InitOutput: %d-bits\n", mop->keybits);
|
||||
|
||||
GetAsymetricStartKey(MPPE_MasterKey, mop->mastkey, mop->keylen, 1, 0);
|
||||
GetNewKeyFromSHA(mop->mastkey, mop->mastkey, mop->keylen, mop->sesskey);
|
||||
|
||||
MPPEReduceSessionKey(mop);
|
||||
|
||||
MPPEKeyChange(mop);
|
||||
|
||||
mop->cohnum = 0;
|
||||
|
||||
return mop;
|
||||
}
|
||||
|
||||
static void
|
||||
MPPETermInput(void *v)
|
||||
{
|
||||
log_Printf(LogCCP, "MPPE: TermInput\n");
|
||||
free(v);
|
||||
}
|
||||
|
||||
static void
|
||||
MPPETermOutput(void *v)
|
||||
{
|
||||
log_Printf(LogCCP, "MPPE: TermOutput\n");
|
||||
free(v);
|
||||
}
|
||||
|
||||
const struct ccp_algorithm MPPEAlgorithm = {
|
||||
TY_MPPE,
|
||||
CCP_NEG_MPPE,
|
||||
MPPEDispOpts,
|
||||
{
|
||||
MPPESetOptsInput,
|
||||
MPPEInitInput,
|
||||
MPPETermInput,
|
||||
MPPEResetInput,
|
||||
MPPEInput,
|
||||
MPPEDictSetup
|
||||
},
|
||||
{
|
||||
MPPEInitOptsOutput,
|
||||
MPPESetOptsOutput,
|
||||
MPPEInitOutput,
|
||||
MPPETermOutput,
|
||||
MPPEResetOutput,
|
||||
MPPEOutput
|
||||
},
|
||||
};
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
/*-
|
||||
* Copyright (c) 2000 Semen Ustimenko <semenu@FreeBSD.org>
|
||||
* 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.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
#define MPPE_KEY_LEN 16
|
||||
extern const struct ccp_algorithm MPPEAlgorithm;
|
||||
extern int MPPE_MasterKeyValid;
|
||||
extern char MPPE_MasterKey[];
|
|
@ -243,7 +243,7 @@ In direct mode,
|
|||
acts as server which accepts incoming
|
||||
.Em PPP
|
||||
connections on stdin/stdout.
|
||||
.It Supports PAP and CHAP (rfc 1994) authentication.
|
||||
.It Supports PAP and CHAP (rfc 1994, 2433 and 2759) authentication.
|
||||
With PAP or CHAP, it is possible to skip the Unix style
|
||||
.Xr login 1
|
||||
procedure, and use the
|
||||
|
@ -353,6 +353,14 @@ It is possible to configure
|
|||
.Nm
|
||||
to open more than one physical connection to the peer, combining the
|
||||
bandwidth of all links for better throughput.
|
||||
.It Supports MPPE (draft-ietf-pppext-mppe)
|
||||
MPPE is Microsoft Point to Point Encryption scheme. It is possible to configure
|
||||
.Nm
|
||||
to participate in Microsoft's Windows VPN. For now,
|
||||
.Nm
|
||||
can only get encryption keys from CHAP 81 authentication.
|
||||
.Nm
|
||||
must be compiled with DES for MPPE to operate.
|
||||
.El
|
||||
.Sh PERMISSIONS
|
||||
.Nm
|
||||
|
@ -2654,8 +2662,20 @@ level, and any appropriate
|
|||
.Dq reconnect
|
||||
values are honoured as if the peer were responsible for dropping the
|
||||
connection.
|
||||
.It mppe
|
||||
Default: Disabled and Denied.
|
||||
This is Microsoft Point to Point Encryption scheme. MPPE key size can be
|
||||
40-, 56- and 128-bits. Refer to
|
||||
.Dq set mppe
|
||||
command.
|
||||
.It MSChapV2|chap81
|
||||
Default: Disabled and Denied.
|
||||
It is very similar to standard CHAP (type 0x05)
|
||||
except that it issues challenges of a fixed 16 bytes in length and uses a
|
||||
combination of MD4, SHA-1 and DES to encrypt the challenge rather than using the
|
||||
standard MD5 mechanism.
|
||||
.It MSChap|chap80nt
|
||||
Default: Disabled and Accepted.
|
||||
Default: Disabled and Denied.
|
||||
The use of this authentication protocol
|
||||
is discouraged as it partially violates the authentication protocol by
|
||||
implementing two different mechanisms (LANMan & NT) under the guise of
|
||||
|
@ -4738,6 +4758,8 @@ This will allow
|
|||
to do the necessary address translations to enable the process that
|
||||
triggers the connection to connect once the link is up despite the
|
||||
peer assigning us a new (dynamic) IP address.
|
||||
.It set mppe {40|56|128}
|
||||
This option selects particular key length. Default is 128.
|
||||
.It set mrru Op Ar value
|
||||
Setting this option enables Multi-link PPP negotiations, also known as
|
||||
Multi-link Protocol or MP.
|
||||
|
|
|
@ -243,7 +243,7 @@ In direct mode,
|
|||
acts as server which accepts incoming
|
||||
.Em PPP
|
||||
connections on stdin/stdout.
|
||||
.It Supports PAP and CHAP (rfc 1994) authentication.
|
||||
.It Supports PAP and CHAP (rfc 1994, 2433 and 2759) authentication.
|
||||
With PAP or CHAP, it is possible to skip the Unix style
|
||||
.Xr login 1
|
||||
procedure, and use the
|
||||
|
@ -353,6 +353,14 @@ It is possible to configure
|
|||
.Nm
|
||||
to open more than one physical connection to the peer, combining the
|
||||
bandwidth of all links for better throughput.
|
||||
.It Supports MPPE (draft-ietf-pppext-mppe)
|
||||
MPPE is Microsoft Point to Point Encryption scheme. It is possible to configure
|
||||
.Nm
|
||||
to participate in Microsoft's Windows VPN. For now,
|
||||
.Nm
|
||||
can only get encryption keys from CHAP 81 authentication.
|
||||
.Nm
|
||||
must be compiled with DES for MPPE to operate.
|
||||
.El
|
||||
.Sh PERMISSIONS
|
||||
.Nm
|
||||
|
@ -2654,8 +2662,20 @@ level, and any appropriate
|
|||
.Dq reconnect
|
||||
values are honoured as if the peer were responsible for dropping the
|
||||
connection.
|
||||
.It mppe
|
||||
Default: Disabled and Denied.
|
||||
This is Microsoft Point to Point Encryption scheme. MPPE key size can be
|
||||
40-, 56- and 128-bits. Refer to
|
||||
.Dq set mppe
|
||||
command.
|
||||
.It MSChapV2|chap81
|
||||
Default: Disabled and Denied.
|
||||
It is very similar to standard CHAP (type 0x05)
|
||||
except that it issues challenges of a fixed 16 bytes in length and uses a
|
||||
combination of MD4, SHA-1 and DES to encrypt the challenge rather than using the
|
||||
standard MD5 mechanism.
|
||||
.It MSChap|chap80nt
|
||||
Default: Disabled and Accepted.
|
||||
Default: Disabled and Denied.
|
||||
The use of this authentication protocol
|
||||
is discouraged as it partially violates the authentication protocol by
|
||||
implementing two different mechanisms (LANMan & NT) under the guise of
|
||||
|
@ -4738,6 +4758,8 @@ This will allow
|
|||
to do the necessary address translations to enable the process that
|
||||
triggers the connection to connect once the link is up despite the
|
||||
peer assigning us a new (dynamic) IP address.
|
||||
.It set mppe {40|56|128}
|
||||
This option selects particular key length. Default is 128.
|
||||
.It set mrru Op Ar value
|
||||
Setting this option enables Multi-link PPP negotiations, also known as
|
||||
Multi-link Protocol or MP.
|
||||
|
|
Loading…
Reference in New Issue