From 14c1a77816e837852138e87a4adccfd66030b2c3 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Eivind=20N=C3=A6ss?= Date: Sun, 3 Jan 2021 17:34:34 -0800 Subject: [PATCH] pppd: Add support for EAP-MSCHAPv2 (client side) (#211) * Adding EAP-MSCHAPv2 support #175 Implementation based on the RFC: draft-kamath-pppext-eap-mschapv2-02. Adding support for MSCHAPv2 inside extensible authentication protocol (EAP). Signed-off-by: Thomas Omerzu * Removing empty "TODO" in comment section Signed-off-by: Eivind Naess * Add support for EAP-MSCHAPv2 #138 Rewrote the original patch to use the chap-new.c API for caching request/responses. Also incorporate feedback from @paulusmack for input validation and function signatures. Signed-off-by: Eivind Naess * Adding length checks per Paul's request Signed-off-by: Eivind Naess, eivnaes@yahoo.com Co-authored-by: Thomas Omerzu --- pppd/chap-new.c | 12 ++++ pppd/chap-new.h | 3 + pppd/eap.c | 159 +++++++++++++++++++++++++++++++++++++++++++++--- pppd/eap.h | 3 + 4 files changed, 169 insertions(+), 8 deletions(-) diff --git a/pppd/chap-new.c b/pppd/chap-new.c index fab8280..ad2f5eb 100644 --- a/pppd/chap-new.c +++ b/pppd/chap-new.c @@ -166,6 +166,18 @@ chap_register_digest(struct chap_digest_type *dp) chap_digests = dp; } +/* + * Lookup a digest type by code + */ +struct chap_digest_type * +chap_find_digest(int digest_code) { + struct chap_digest_type *dp = NULL; + for (dp = chap_digests; dp != NULL; dp = dp->next) + if (dp->code == digest_code) + break; + return dp; +} + /* * chap_lowerup - we can start doing stuff now. */ diff --git a/pppd/chap-new.h b/pppd/chap-new.h index 665e78f..e5330da 100644 --- a/pppd/chap-new.h +++ b/pppd/chap-new.h @@ -120,6 +120,9 @@ extern int (*chap_verify_hook)(char *name, char *ourname, int id, /* Called by digest code to register a digest type */ extern void chap_register_digest(struct chap_digest_type *); +/* Lookup a digest handler by type */ +extern struct chap_digest_type *chap_find_digest(int digest_code); + /* Called by authentication code to start authenticating the peer. */ extern void chap_auth_peer(int unit, char *our_name, int digest_code); diff --git a/pppd/eap.c b/pppd/eap.c index ede5ff0..0cffa8b 100644 --- a/pppd/eap.c +++ b/pppd/eap.c @@ -48,10 +48,6 @@ * Implemented EAP-TLS authentication */ -/* - * TODO: - */ - #include #include #include @@ -68,6 +64,10 @@ #include "md5.h" #include "eap.h" +#ifdef CHAPMS +#include "chap_ms.h" +#endif + #ifdef USE_SRP #include #include @@ -222,6 +222,9 @@ eap_init(int unit) #ifdef USE_EAPTLS esp->es_client.ea_using_eaptls = 0; #endif /* USE_EAPTLS */ +#ifdef CHAPMS + esp->es_client.digest = chap_find_digest(CHAP_MICROSOFT_V2); +#endif } /* @@ -1673,6 +1676,39 @@ write_pseudonym(eap_state *esp, u_char *inp, int len, int id) } #endif /* USE_SRP */ +#if CHAPMS +/* + * Format and send an CHAPV2-Challenge EAP Response message. + */ +static void +eap_chapv2_response(eap_state *esp, u_char id, u_char chapid, u_char *response, char *user, int user_len) +{ + u_char *outp; + int msglen; + + outp = outpacket_buf; + + MAKEHEADER(outp, PPP_EAP); + + PUTCHAR(EAP_RESPONSE, outp); + PUTCHAR(id, outp); + esp->es_client.ea_id = id; + msglen = EAP_HEADERLEN + 6 * sizeof (u_char) + MS_CHAP2_RESPONSE_LEN + user_len; + PUTSHORT(msglen, outp); + PUTCHAR(EAPT_MSCHAPV2, outp); + PUTCHAR(CHAP_RESPONSE, outp); + PUTCHAR(chapid, outp); + PUTCHAR(0, outp); + /* len */ + PUTCHAR(5 + user_len + MS_CHAP2_RESPONSE_LEN, outp); + BCOPY(response, outp, MS_CHAP2_RESPONSE_LEN+1); // VLEN + VALUE + INCPTR(MS_CHAP2_RESPONSE_LEN+1, outp); + BCOPY(user, outp, user_len); + + output(esp->es_unit, outpacket_buf, PPP_HDRLEN + msglen); +} +#endif + /* * eap_request - Receive EAP Request message (client mode). */ @@ -1859,7 +1895,7 @@ eap_request(eap_state *esp, u_char *inp, int id, int len) /* Init ssl session */ if(!eaptls_init_ssl_client(esp)) { dbglog("cannot init ssl"); - eap_send_nak(esp, id, EAPT_TLS); + eap_send_nak(esp, id, EAPT_MSCHAPV2); esp->es_client.ea_using_eaptls = 0; break; } @@ -1871,7 +1907,7 @@ eap_request(eap_state *esp, u_char *inp, int id, int len) } /* The server has sent a bad start packet. */ - eap_send_nak(esp, id, EAPT_TLS); + eap_send_nak(esp, id, EAPT_MSCHAPV2); break; case eapTlsRecvAck: @@ -2154,6 +2190,113 @@ eap_request(eap_state *esp, u_char *inp, int id, int len) } break; #endif /* USE_SRP */ + +#ifdef CHAPMS + case EAPT_MSCHAPV2: + if (len < 4) { + error("EAP: received invalid MSCHAPv2 packet, too short"); + return; + } + unsigned char opcode; + GETCHAR(opcode, inp); + unsigned char chapid; /* Chapv2-ID */ + GETCHAR(chapid, inp); + short mssize; + GETSHORT(mssize, inp); + + /* Validate the mssize field */ + if (len != mssize) { + error("EAP: received invalid MSCHAPv2 packet, invalid length"); + return; + } + len -= 4; + + /* If MSCHAPv2 digest was not found, NAK the packet */ + if (!esp->es_client.digest) { + error("EAP MSCHAPv2 not supported"); + eap_send_nak(esp, id, EAPT_SRP); + return; + } + + switch (opcode) { + case CHAP_CHALLENGE: { + + /* make_response() expects: VLEN + VALUE */ + u_char *challenge = inp; + + unsigned char vsize; + GETCHAR(vsize, inp); + len -= 1; + + /* Validate the VALUE field */ + if (vsize != MS_CHAP2_PEER_CHAL_LEN || len < MS_CHAP2_PEER_CHAL_LEN) { + error("EAP: received invalid MSCHAPv2 packet, invalid value-length: %d", vsize); + return; + } + + /* Increment past the VALUE field */ + INCPTR(MS_CHAP2_PEER_CHAL_LEN, inp); + len -= MS_CHAP2_PEER_CHAL_LEN; + + /* Extract the hostname */ + rhostname[0] = '\0'; + if (len > 0) { + if (len >= sizeof (rhostname)) { + dbglog("EAP: trimming really long peer name down"); + len = sizeof(rhostname) - 1; + } + BCOPY(inp, rhostname, len); + rhostname[len] = '\0'; + } + + /* In case the remote doesn't give us his name. */ + if (explicit_remote || (remote_name[0] != '\0' && len == 0)) + strlcpy(rhostname, remote_name, sizeof(rhostname)); + + /* Get the secret for authenticating ourselves with the specified host. */ + if (!get_secret(esp->es_unit, esp->es_client.ea_name, + rhostname, secret, &secret_len, 0)) { + dbglog("EAP: no CHAP secret for auth to %q", rhostname); + eap_send_nak(esp, id, EAPT_SRP); + break; + } + + /* Create the MSCHAPv2 response (and add to cache) */ + unsigned char response[MS_CHAP2_RESPONSE_LEN+1]; // VLEN + VALUE + esp->es_client.digest->make_response(response, chapid, esp->es_client.ea_name, + challenge, secret, secret_len, NULL); + + eap_chapv2_response(esp, id, chapid, response, esp->es_client.ea_name, esp->es_client.ea_namelen); + break; + } + case CHAP_SUCCESS: { + + /* Check response for mutual authentication */ + u_char status = CHAP_FAILURE; + if (esp->es_client.digest->check_success(chapid, inp, len) == 1) { + info("Chap authentication succeeded! %.*v", len, inp); + status = CHAP_SUCCESS; + } + eap_send_response(esp, id, EAPT_MSCHAPV2, &status, sizeof(status)); + break; + } + case CHAP_FAILURE: { + + /* Process the failure string, and log appropriate information */ + esp->es_client.digest->handle_failure(inp, len); + + u_char status = CHAP_FAILURE; + eap_send_response(esp, id, EAPT_MSCHAPV2, &status, sizeof(status)); + goto client_failure; /* force termination */ + } + default: + + error("EAP: received invalid MSCHAPv2 packet, invalid or unsupported opcode: %d", opcode); + eap_send_nak(esp, id, EAPT_SRP); + } + + break; +#endif /* CHAPMS */ default: info("EAP: unknown authentication type %d; Naking", typenum); @@ -2168,13 +2311,13 @@ eap_request(eap_state *esp, u_char *inp, int id, int len) } return; -#ifdef USE_SRP client_failure: esp->es_client.ea_state = eapBadAuth; if (esp->es_client.ea_timeout > 0) { UNTIMEOUT(eap_client_timeout, (void *)esp); } esp->es_client.ea_session = NULL; +#ifdef USE_SRP t_clientclose(tc); auth_withpeer_fail(esp->es_unit, PPP_EAP); #endif /* USE_SRP */ @@ -3091,7 +3234,7 @@ eap_printpkt(u_char *inp, int inlen, len--; printer(arg, " = 1 && - rtype < sizeof (eap_typenames) / sizeof (char *)) + rtype <= sizeof (eap_typenames) / sizeof (char *)) printer(arg, " (%s)", eap_typenames[rtype-1]); printer(arg, ">"); break; diff --git a/pppd/eap.h b/pppd/eap.h index f72fe61..046eb1c 100644 --- a/pppd/eap.h +++ b/pppd/eap.h @@ -146,6 +146,9 @@ struct eap_auth { enum eap_state_code ea_state; #ifdef USE_EAPTLS enum eap_state_code ea_prev_state; +#endif +#ifdef CHAPMS + struct chap_digest_type *digest; #endif u_char ea_id; /* Current id */ u_char ea_requests; /* Number of Requests sent/received */ -- 2.39.2