]> git.ozlabs.org Git - ppp.git/commitdiff
pppd: Make MSCHAP-v2 cope better with packet loss
authorDeomid Ryabkov <myself@rojer.pp.ru>
Sat, 31 Mar 2012 04:14:23 +0000 (05:14 +0100)
committerPaul Mackerras <paulus@samba.org>
Sun, 20 May 2012 06:48:27 +0000 (16:48 +1000)
This implements response caching for MSCHAP-v2.  It caches our
responses and the responses we expect from the peer.  MSCHAP-v2 is
unusual in that the authenticatee's CHAP-Response contains what is
effectively a challenge to the authenticator, and the authenticator's
CHAP-Success packet contains a response to that challenge.  Having
the response cache lets us (a) answer challenges consistently and
(b) cope with a CHAP-Success packet that corresponds to one of our
CHAP-Responses that wasn't the last one we sent.

This solves a problem where MSCHAP-v2 does not handle replay/retry
properly.  Here's what a typical normal session looks like:

Mar 31 02:47:40 nbm pppd[12895]: rcvd [CHAP Challenge id=0x37 <7ac9de47e66fc440e4b142e28c1a2064>, name = "jeeves"]
Mar 31 02:47:40 nbm pppd[12895]: sent [CHAP Response id=0x37 <12986c68266e0d60e7e0de9c8326073200000000000000005da37272ed71b6743f65bc00f7ae2ca148db9210627b646500>, name = "murka"]
Mar 31 02:47:40 nbm pppd[12895]: rcvd [CHAP Success id=0x37 "S=ED8FB5829C8049C331AAE0C570F63F8B558DEA2C M=Access granted"]
Mar 31 02:47:40 nbm pppd[12895]: CHAP authentication succeeded

however, this breaks down if, for whatever reason - packet loss,
reordering or whatnot - server sends a second challenge that arrives
before the response - it changes client's expectation and the
authentication fails.  Here's how it looks in the logs:

Mar 31 02:47:47 nbm pppd[13014]: rcvd [CHAP Challenge id=0x8a <5070251e94455e2155d2cf4d698d23c9>, name = "jeeves"]
Mar 31 02:47:47 nbm pppd[13014]: sent [CHAP Response id=0x8a <14d788f835add58b60d2aff362c183160000000000000000d780f3849076e9e013272f67bcb8c8cfa0e9b51c0fe3ee2100>, name = "murka"]
Mar 31 02:47:48 nbm pppd[13014]: rcvd [CHAP Challenge id=0x8a <5070251e94455e2155d2cf4d698d23c9>, name = "jeeves"]
Mar 31 02:47:48 nbm pppd[13014]: sent [CHAP Response id=0x8a <df950da43b90e235048810469d3283dd0000000000000000ace042b145f5eb9f118349b5672d4829eb5038192050a90b00>, name = "murka"]
Mar 31 02:47:48 nbm pppd[13014]: rcvd [CHAP Success id=0x8a "S=ABAEA4DF5601FADF25F8729455D39BF6D971D501 M=Access granted"]
Mar 31 02:47:48 nbm pppd[13014]: MS-CHAPv2 mutual authentication failed.

Signed-off-by: Paul Mackerras <paulus@samba.org>
pppd/chap-new.c
pppd/chap-new.h
pppd/chap_ms.c

index 138648602c29f47a70d4167317ea2ac9e04c1401..2714bff6478589412aeed098f77262e89a91349c 100644 (file)
@@ -498,7 +498,7 @@ chap_handle_status(struct chap_client_state *cs, int code, int id,
        if (code == CHAP_SUCCESS) {
                /* used for MS-CHAP v2 mutual auth, yuck */
                if (cs->digest->check_success != NULL) {
-                       if (!(*cs->digest->check_success)(pkt, len, cs->priv))
+                       if (!(*cs->digest->check_success)(id, pkt, len))
                                code = CHAP_FAILURE;
                } else
                        msg = "CHAP authentication succeeded";
index 48235d405af786852efb4d26fcc275c4cf572498..665e78fb19a99a26ab0b44ade689a1fb20787c71 100644 (file)
@@ -105,7 +105,7 @@ struct chap_digest_type {
        void (*make_response)(unsigned char *response, int id, char *our_name,
                unsigned char *challenge, char *secret, int secret_len,
                unsigned char *priv);
-       int (*check_success)(unsigned char *pkt, int len, unsigned char *priv);
+       int (*check_success)(int id, unsigned char *pkt, int len);
        void (*handle_failure)(unsigned char *pkt, int len);
 
        struct chap_digest_type *next;
index aec12262ed46375d304b12f9b397acb88be21ba4..016b42e0c10ee9a845a3e96ad8aa55dcb2b0464c 100644 (file)
@@ -320,25 +320,90 @@ chapms_make_response(unsigned char *response, int id, char *our_name,
        ChapMS(challenge, secret, secret_len, response);
 }
 
+struct chapms2_response_cache_entry {
+       int id;
+       unsigned char challenge[16];
+       unsigned char response[MS_CHAP2_RESPONSE_LEN];
+       unsigned char auth_response[MS_AUTH_RESPONSE_LENGTH];
+};
+
+#define CHAPMS2_MAX_RESPONSE_CACHE_SIZE 10
+static struct chapms2_response_cache_entry
+    chapms2_response_cache[CHAPMS2_MAX_RESPONSE_CACHE_SIZE];
+static int chapms2_response_cache_next_index = 0;
+static int chapms2_response_cache_size = 0;
+
+static void
+chapms2_add_to_response_cache(int id, unsigned char *challenge,
+                             unsigned char *response,
+                             unsigned char *auth_response)
+{
+       int i = chapms2_response_cache_next_index;
+
+       chapms2_response_cache[i].id = id;
+       memcpy(chapms2_response_cache[i].challenge, challenge, 16);
+       memcpy(chapms2_response_cache[i].response, response,
+              MS_CHAP2_RESPONSE_LEN);
+       memcpy(chapms2_response_cache[i].auth_response,
+              auth_response, MS_AUTH_RESPONSE_LENGTH);
+       chapms2_response_cache_next_index =
+               (i + 1) % CHAPMS2_MAX_RESPONSE_CACHE_SIZE;
+       if (chapms2_response_cache_next_index > chapms2_response_cache_size)
+               chapms2_response_cache_size = chapms2_response_cache_next_index;
+       dbglog("added response cache entry %d", i);
+}
+
+static struct chapms2_response_cache_entry*
+chapms2_find_in_response_cache(int id, unsigned char *challenge,
+                     unsigned char *auth_response)
+{
+       int i;
+
+       for (i = 0; i < chapms2_response_cache_size; i++) {
+               if (id == chapms2_response_cache[i].id
+                   && (!challenge
+                       || memcmp(challenge,
+                                 chapms2_response_cache[i].challenge,
+                                 16) == 0)
+                   && (!auth_response
+                       || memcmp(auth_response,
+                                 chapms2_response_cache[i].auth_response,
+                                 MS_AUTH_RESPONSE_LENGTH) == 0)) {
+                       dbglog("response found in cache (entry %d)", i);
+                       return &chapms2_response_cache[i];
+               }
+       }
+       return NULL;  /* not found */
+}
+
 static void
 chapms2_make_response(unsigned char *response, int id, char *our_name,
                      unsigned char *challenge, char *secret, int secret_len,
                      unsigned char *private)
 {
+       const struct chapms2_response_cache_entry *cache_entry;
+       unsigned char auth_response[MS_AUTH_RESPONSE_LENGTH];
+
        challenge++;    /* skip length, should be 16 */
        *response++ = MS_CHAP2_RESPONSE_LEN;
+       cache_entry = chapms2_find_in_response_cache(id, challenge, NULL);
+       if (cache_entry) {
+               memcpy(response, cache_entry->response, MS_CHAP2_RESPONSE_LEN);
+               return;
+       }
        ChapMS2(challenge,
 #ifdef DEBUGMPPEKEY
                mschap2_peer_challenge,
 #else
                NULL,
 #endif
-               our_name, secret, secret_len, response, private,
+               our_name, secret, secret_len, response, auth_response,
                MS_CHAP2_AUTHENTICATEE);
+       chapms2_add_to_response_cache(id, challenge, response, auth_response);
 }
 
 static int
-chapms2_check_success(unsigned char *msg, int len, unsigned char *private)
+chapms2_check_success(int id, unsigned char *msg, int len)
 {
        if ((len < MS_AUTH_RESPONSE_LENGTH + 2) ||
            strncmp((char *)msg, "S=", 2) != 0) {
@@ -349,7 +414,7 @@ chapms2_check_success(unsigned char *msg, int len, unsigned char *private)
        msg += 2;
        len -= 2;
        if (len < MS_AUTH_RESPONSE_LENGTH
-           || memcmp(msg, private, MS_AUTH_RESPONSE_LENGTH)) {
+           || !chapms2_find_in_response_cache(id, NULL /* challenge */, msg)) {
                /* Authenticator Response did not match expected. */
                error("MS-CHAPv2 mutual authentication failed.");
                return 0;