--- mppe.c.orig 2014-07-06 19:27:29.899279626 +0400 +++ mppe.c 2015-04-02 13:22:06.787417572 +0300 @@ -92,6 +92,7 @@ #define MPPE_HEADER_FLAGMASK 0x00ff #define MPPE_HEADER_FLAGSHIFT 8 #define MPPE_HEADER_STATEFUL_KEYCHANGES 16 +#define MPPE_KEY_HISTORY_SIZE 64 /* Have to be power of 2 */ struct mppe_state { unsigned stateless : 1; @@ -103,6 +104,7 @@ struct mppe_state { char sesskey[MPPE_KEY_LEN]; char mastkey[MPPE_KEY_LEN]; RC4_KEY rc4key; + char (*sesskeyhistory)[MPPE_KEY_LEN]; }; int MPPE_MasterKeyValid = 0; @@ -152,6 +154,11 @@ MPPEKeyChange(struct mppe_state *mp) RC4(&RC4Key, mp->keylen, InterimKey, mp->sesskey); MPPEReduceSessionKey(mp); + + if (mp->sesskeyhistory) { + memcpy(mp->sesskeyhistory[(mp->cohnum + 1) % MPPE_KEY_HISTORY_SIZE], + mp->sesskey, MPPE_KEY_LEN); + } } static struct mbuf * @@ -273,6 +280,22 @@ MPPEInput(void *v, struct ccp *ccp, u_sh } if (mip->stateless) { + int cohnumchange = (prefix - mip->cohnum) & 0xfff; + if (cohnumchange > 2047) { + /* We're assuming that coherency number went backward; delayed packet. */ + log_Printf(LogDEBUG, "MPPEInput: Delayed packet recieved (coherency count is %d instead of %d)\n", + prefix, mip->cohnum + 1); + if (!mip->sesskeyhistory || (4096 - cohnumchange > MPPE_KEY_HISTORY_SIZE - 1)) { + log_Printf(LogCCP, "MPPEInput: Required key is absent in the history - packet discarded\n"); + m_freem(mp); + return NULL; + } + log_Printf(LogDEBUG, "MPPEInput: Using key from history for decryption\n"); + RC4_set_key(&mip->rc4key, mip->keylen, mip->sesskeyhistory[prefix % MPPE_KEY_HISTORY_SIZE]); + } else { + if (cohnumchange > 1) + log_Printf(LogDEBUG, "MPPEInput: %d packet(s) missed (coherency count is %d instead of %d)\n", + cohnumchange - 1, prefix, (mip->cohnum + 1) & 0xfff); /* Change our key for each missed packet in stateless mode */ while (prefix != mip->cohnum) { log_Printf(LogDEBUG, "MPPEInput: Key changed [%u]\n", prefix); @@ -285,6 +308,7 @@ MPPEInput(void *v, struct ccp *ccp, u_sh mip->cohnum &= ~MPPE_HEADER_BITMASK; } dictinit = 1; + } } else { if (flushed) { /* @@ -724,6 +748,7 @@ MPPEInitInput(struct bundle *bundle __un * to have a sequence number of 0 and we'll therefore not expect * to have to change the key at that point. */ + mip->sesskeyhistory = malloc(MPPE_KEY_HISTORY_SIZE * MPPE_KEY_LEN); log_Printf(LogDEBUG, "MPPEInitInput: Key changed [%d]\n", mip->cohnum); MPPEKeyChange(mip); } @@ -782,6 +807,7 @@ MPPEInitOutput(struct bundle *bundle __u static void MPPETermInput(void *v) { + free(((struct mppe_state *) v)->sesskeyhistory); free(v); }