X-Git-Url: https://git.ozlabs.org/?p=ppp.git;a=blobdiff_plain;f=pppd%2Feap.c;h=40f08b3c361d32649388aa7423b635ec2985d54d;hp=92d29ec8bd6c034995f3ebd6a7c737669d80a607;hb=HEAD;hpb=5ece6e68baaf9fdbd6e73daa7b27335f8852d85b diff --git a/pppd/eap.c b/pppd/eap.c index 92d29ec..7015466 100644 --- a/pppd/eap.c +++ b/pppd/eap.c @@ -48,9 +48,9 @@ * Implemented EAP-TLS authentication */ -/* - * TODO: - */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif #include #include @@ -63,35 +63,45 @@ #include #include -#include "pppd.h" +#include "pppd-private.h" +#include "options.h" #include "pathnames.h" -#include "md5.h" +#include "crypto.h" +#include "crypto_ms.h" #include "eap.h" +#ifdef PPP_WITH_PEAP +#include "peap.h" +#endif /* PPP_WITH_PEAP */ -#ifdef USE_SRP +#ifdef PPP_WITH_SRP +#ifdef HAVE_TIME_H +#include +#endif #include #include #include -#include "pppcrypt.h" -#endif /* USE_SRP */ +#endif /* PPP_WITH_SRP */ -#ifndef SHA_DIGESTSIZE -#define SHA_DIGESTSIZE 20 -#endif - -#ifdef USE_EAPTLS +#ifdef PPP_WITH_EAPTLS #include "eap-tls.h" -#endif /* USE_EAPTLS */ +#endif /* PPP_WITH_EAPTLS */ + +#ifdef PPP_WITH_CHAPMS +#include "chap.h" +#include "chap_ms.h" + +extern int chapms_strip_domain; +#endif /* PPP_WITH_CHAPMS */ eap_state eap_states[NUM_PPP]; /* EAP state; one for each unit */ -#ifdef USE_SRP +#ifdef PPP_WITH_SRP static char *pn_secret = NULL; /* Pseudonym generating secret */ #endif /* * Command-line options. */ -static option_t eap_option_list[] = { +static struct option eap_option_list[] = { { "eap-restart", o_int, &eap_states[0].es_server.ea_timeout, "Set retransmit timeout for EAP Requests (server)" }, { "eap-max-sreq", o_int, &eap_states[0].es_server.ea_maxrequests, @@ -102,7 +112,7 @@ static option_t eap_option_list[] = { "Set max number of EAP Requests allows (client)" }, { "eap-interval", o_int, &eap_states[0].es_rechallenge, "Set interval for EAP rechallenge" }, -#ifdef USE_SRP +#ifdef PPP_WITH_SRP { "srp-interval", o_int, &eap_states[0].es_lwrechallenge, "Set interval for SRP lightweight rechallenge" }, { "srp-pn-secret", o_string, &pn_secret, @@ -144,7 +154,7 @@ struct protent eap_protent = { NULL /* say whether to bring up link for this pkt */ }; -#ifdef USE_SRP +#ifdef PPP_WITH_SRP /* * A well-known 2048 bit modulus. */ @@ -182,7 +192,7 @@ static const u_char wkmodulus[] = { 0x9B, 0x65, 0xE3, 0x72, 0xFC, 0xD6, 0x8E, 0xF2, 0x0F, 0xA7, 0x11, 0x1F, 0x9E, 0x4A, 0xFF, 0x73 }; -#endif /* USE_SRP */ +#endif /* PPP_WITH_SRP */ /* Local forward declarations. */ static void eap_server_timeout (void *arg); @@ -214,9 +224,13 @@ eap_init(int unit) esp->es_server.ea_id = (u_char)(drand48() * 0x100); esp->es_client.ea_timeout = EAP_DEFREQTIME; esp->es_client.ea_maxrequests = EAP_DEFALLOWREQ; -#ifdef USE_EAPTLS +#ifdef PPP_WITH_EAPTLS esp->es_client.ea_using_eaptls = 0; -#endif /* USE_EAPTLS */ +#endif /* PPP_WITH_EAPTLS */ +#ifdef PPP_WITH_CHAPMS + esp->es_client.digest = chap_find_digest(CHAP_MICROSOFT_V2); + esp->es_server.digest = chap_find_digest(CHAP_MICROSOFT_V2); +#endif } /* @@ -310,30 +324,39 @@ eap_send_success(eap_state *esp) esp->es_server.ea_peer, esp->es_server.ea_peerlen); } -#ifdef USE_SRP +#ifdef PPP_WITH_SRP /* * Set DES key according to pseudonym-generating secret and current * date. */ static bool -pncrypt_setkey(int timeoffs) +pncrypt_getkey(int timeoffs, unsigned char *key, int keylen) { struct tm *tp; char tbuf[9]; - SHA1_CTX ctxt; - u_char dig[SHA_DIGESTSIZE]; + PPP_MD_CTX *ctxt; time_t reftime; if (pn_secret == NULL) return (0); reftime = time(NULL) + timeoffs; tp = localtime(&reftime); - SHA1Init(&ctxt); - SHA1Update(&ctxt, pn_secret, strlen(pn_secret)); - strftime(tbuf, sizeof (tbuf), "%Y%m%d", tp); - SHA1Update(&ctxt, tbuf, strlen(tbuf)); - SHA1Final(dig, &ctxt); - return (DesSetkey(dig)); + + ctxt = PPP_MD_CTX_new(); + if (ctxt) { + + strftime(tbuf, sizeof (tbuf), "%Y%m%d", tp); + + PPP_DigestInit(ctxt, PPP_sha1()); + PPP_DigestUpdate(ctxt, pn_secret, strlen(pn_secret)); + PPP_DigestUpdate(ctxt, tbuf, strlen(tbuf)); + PPP_DigestFinal(ctxt, key, &keylen); + + PPP_MD_CTX_free(ctxt); + return 1; + } + + return (0); } static char base64[] = @@ -406,7 +429,7 @@ b64dec(struct b64state *bs, u_char *inp, int inlen, u_char *outp) } return (outlen); } -#endif /* USE_SRP */ +#endif /* PPP_WITH_SRP */ /* * Assume that current waiting server state is complete and figure @@ -417,32 +440,32 @@ b64dec(struct b64state *bs, u_char *inp, int inlen, u_char *outp) static void eap_figure_next_state(eap_state *esp, int status) { -#ifdef USE_SRP - unsigned char secbuf[MAXWORDLEN], clear[8], *sp, *dp; +#ifdef PPP_WITH_SRP + unsigned char secbuf[MAXWORDLEN], clear[8], *sp, *dp, key[SHA_DIGEST_LENGTH]; struct t_pw tpw; struct t_confent *tce, mytce; char *cp, *cp2; struct t_server *ts; - int id, i, plen, toffs; + int id, i, plen, clen, toffs, keylen; u_char vals[2]; struct b64state bs; -#endif /* USE_SRP */ -#ifdef USE_EAPTLS +#endif /* PPP_WITH_SRP */ +#ifdef PPP_WITH_EAPTLS struct eaptls_session *ets; int secret_len; char secret[MAXWORDLEN]; -#endif /* USE_EAPTLS */ +#endif /* PPP_WITH_EAPTLS */ esp->es_server.ea_timeout = esp->es_savedtime; -#ifdef USE_EAPTLS +#ifdef PPP_WITH_EAPTLS esp->es_server.ea_prev_state = esp->es_server.ea_state; -#endif /* USE_EAPTLS */ +#endif /* PPP_WITH_EAPTLS */ switch (esp->es_server.ea_state) { case eapBadAuth: return; case eapIdentify: -#ifdef USE_SRP +#ifdef PPP_WITH_SRP /* Discard any previous session. */ ts = (struct t_server *)esp->es_server.ea_session; if (ts != NULL) { @@ -450,12 +473,12 @@ eap_figure_next_state(eap_state *esp, int status) esp->es_server.ea_session = NULL; esp->es_server.ea_skey = NULL; } -#endif /* USE_SRP */ +#endif /* PPP_WITH_SRP */ if (status != 0) { esp->es_server.ea_state = eapBadAuth; break; } -#ifdef USE_SRP +#ifdef PPP_WITH_SRP /* If we've got a pseudonym, try to decode to real name. */ if (esp->es_server.ea_peerlen > SRP_PSEUDO_LEN && strncmp(esp->es_server.ea_peer, SRP_PSEUDO_ID, @@ -469,11 +492,12 @@ eap_figure_next_state(eap_state *esp, int status) secbuf); toffs = 0; for (i = 0; i < 5; i++) { - pncrypt_setkey(toffs); + pncrypt_getkey(toffs, key, keylen); toffs -= 86400; - if (!DesDecrypt(secbuf, clear)) { + + if (!DesDecrypt(secbuf, key, clear)) { dbglog("no DES here; cannot decode " - "pseudonym"); + "pseudonym"); return; } id = *(unsigned char *)clear; @@ -495,7 +519,7 @@ eap_figure_next_state(eap_state *esp, int status) dp += i; sp = secbuf + 8; while (plen > 0) { - (void) DesDecrypt(sp, dp); + DesDecrypt(sp, key, dp); sp += 8; dp += 8; plen -= 8; @@ -545,7 +569,7 @@ eap_figure_next_state(eap_state *esp, int status) tpw.pebuf.name = esp->es_server.ea_peer; tpw.pebuf.password.len = t_fromb64((char *)tpw.pwbuf, cp); - tpw.pebuf.password.data = tpw.pwbuf; + tpw.pebuf.password.data = (char*) tpw.pwbuf; tpw.pebuf.salt.len = t_fromb64((char *)tpw.saltbuf, cp2); tpw.pebuf.salt.data = tpw.saltbuf; @@ -560,20 +584,20 @@ eap_figure_next_state(eap_state *esp, int status) t_servergenexp(ts); break; } -#endif /* USE_SRP */ -#ifdef USE_EAPTLS +#endif /* PPP_WITH_SRP */ +#ifdef PPP_WITH_EAPTLS if (!get_secret(esp->es_unit, esp->es_server.ea_peer, esp->es_server.ea_name, secret, &secret_len, 1)) { esp->es_server.ea_state = eapTlsStart; break; } -#endif /* USE_EAPTLS */ +#endif /* PPP_WITH_EAPTLS */ esp->es_server.ea_state = eapMD5Chall; break; -#ifdef USE_EAPTLS +#ifdef PPP_WITH_EAPTLS case eapTlsStart: /* Initialize ssl session */ if(!eaptls_init_ssl_server(esp)) { @@ -634,17 +658,17 @@ eap_figure_next_state(eap_state *esp, int status) case eapTlsSendAlert: esp->es_server.ea_state = eapTlsRecvAlertAck; break; -#endif /* USE_EAPTLS */ +#endif /* PPP_WITH_EAPTLS */ case eapSRP1: -#ifdef USE_SRP +#ifdef PPP_WITH_SRP ts = (struct t_server *)esp->es_server.ea_session; if (ts != NULL && status != 0) { t_serverclose(ts); esp->es_server.ea_session = NULL; esp->es_server.ea_skey = NULL; } -#endif /* USE_SRP */ +#endif /* PPP_WITH_SRP */ if (status == 1) { esp->es_server.ea_state = eapMD5Chall; } else if (status != 0 || esp->es_server.ea_session == NULL) { @@ -655,14 +679,14 @@ eap_figure_next_state(eap_state *esp, int status) break; case eapSRP2: -#ifdef USE_SRP +#ifdef PPP_WITH_SRP ts = (struct t_server *)esp->es_server.ea_session; if (ts != NULL && status != 0) { t_serverclose(ts); esp->es_server.ea_session = NULL; esp->es_server.ea_skey = NULL; } -#endif /* USE_SRP */ +#endif /* PPP_WITH_SRP */ if (status != 0 || esp->es_server.ea_session == NULL) { esp->es_server.ea_state = eapBadAuth; } else { @@ -672,14 +696,14 @@ eap_figure_next_state(eap_state *esp, int status) case eapSRP3: case eapSRP4: -#ifdef USE_SRP +#ifdef PPP_WITH_SRP ts = (struct t_server *)esp->es_server.ea_session; if (ts != NULL && status != 0) { t_serverclose(ts); esp->es_server.ea_session = NULL; esp->es_server.ea_skey = NULL; } -#endif /* USE_SRP */ +#endif /* PPP_WITH_SRP */ if (status != 0 || esp->es_server.ea_session == NULL) { esp->es_server.ea_state = eapBadAuth; } else { @@ -687,6 +711,9 @@ eap_figure_next_state(eap_state *esp, int status) } break; +#ifdef PPP_WITH_CHAPMS + case eapMSCHAPv2Chall: +#endif case eapMD5Chall: if (status != 0) { esp->es_server.ea_state = eapBadAuth; @@ -702,10 +729,80 @@ eap_figure_next_state(eap_state *esp, int status) if (esp->es_server.ea_state == eapBadAuth) eap_send_failure(esp); -#ifdef USE_EAPTLS +#ifdef PPP_WITH_EAPTLS dbglog("EAP id=0x%2x '%s' -> '%s'", esp->es_server.ea_id, eap_state_name(esp->es_server.ea_prev_state), eap_state_name(esp->es_server.ea_state)); -#endif /* USE_EAPTLS */ +#endif /* PPP_WITH_EAPTLS */ +} + +#if PPP_WITH_CHAPMS +/* + * eap_chap_verify_response - check whether the peer's response matches + * what we think it should be. Returns 1 if it does (authentication + * succeeded), or 0 if it doesn't. + */ +static int +eap_chap_verify_response(char *name, char *ourname, int id, + struct chap_digest_type *digest, + unsigned char *challenge, unsigned char *response, + char *message, int message_space) +{ + int ok; + unsigned char secret[MAXSECRETLEN]; + int secret_len; + + /* Get the secret that the peer is supposed to know */ + if (!get_secret(0, name, ourname, (char *)secret, &secret_len, 1)) { + error("No CHAP secret found for authenticating %q", name); + return 0; + } + + ok = digest->verify_response(id, name, secret, secret_len, challenge, + response, message, message_space); + memset(secret, 0, sizeof(secret)); + + return ok; +} + +/* + * Format and send an CHAPV2-Success/Failure EAP Request message. + */ +static void +eap_chapms2_send_request(eap_state *esp, u_char id, + u_char opcode, u_char chapid, + char *message, int message_len) +{ + u_char *outp; + int msglen; + + outp = outpacket_buf; + + MAKEHEADER(outp, PPP_EAP); + + msglen = EAP_HEADERLEN + 5 * sizeof (u_char); + msglen += message_len; + + PUTCHAR(EAP_REQUEST, outp); + PUTCHAR(id, outp); + PUTSHORT(msglen, outp); + PUTCHAR(EAPT_MSCHAPV2, outp); + PUTCHAR(opcode, outp); + PUTCHAR(chapid, outp); + /* MS len */ + PUTSHORT(msglen - 5, outp); + BCOPY(message, outp, message_len); + + output(esp->es_unit, outpacket_buf, PPP_HDRLEN + msglen); + + if (opcode == CHAP_SUCCESS) { + auth_peer_success(esp->es_unit, PPP_EAP, 0, + esp->es_server.ea_peer, esp->es_server.ea_peerlen); + } + else { + esp->es_server.ea_state = eapBadAuth; + auth_peer_fail(esp->es_unit, PPP_EAP); + } } +#endif /* PPP_WITH_CHAPMS */ /* * Format an EAP Request message and send it to the peer. Message @@ -720,13 +817,13 @@ eap_send_request(eap_state *esp) int outlen; int challen; char *str; -#ifdef USE_SRP +#ifdef PPP_WITH_SRP struct t_server *ts; - u_char clear[8], cipher[8], dig[SHA_DIGESTSIZE], *optr, *cp; - int i, j; + u_char clear[8], cipher[8], dig[SHA_DIGEST_LENGTH], *optr, *cp, key[SHA_DIGEST_LENGTH]; + int i, j, diglen, clen, keylen = sizeof(key); struct b64state b64; - SHA1_CTX ctxt; -#endif /* USE_SRP */ + PPP_MD_CTX *ctxt; +#endif /* PPP_WITH_SRP */ /* Handle both initial auth and restart */ if (esp->es_server.ea_state < eapIdentify && @@ -792,7 +889,30 @@ eap_send_request(eap_state *esp) INCPTR(esp->es_server.ea_namelen, outp); break; -#ifdef USE_EAPTLS +#ifdef PPP_WITH_CHAPMS + case eapMSCHAPv2Chall: + esp->es_server.digest->generate_challenge(esp->es_challenge); + challen = esp->es_challenge[0]; + esp->es_challen = challen; + + PUTCHAR(EAPT_MSCHAPV2, outp); + PUTCHAR(CHAP_CHALLENGE, outp); + PUTCHAR(esp->es_server.ea_id, outp); + /* MS len */ + PUTSHORT(5 + challen + + esp->es_server.ea_namelen, + outp); + /* challen + challenge */ + BCOPY(esp->es_challenge, outp, challen+1); + INCPTR(challen+1, outp); + BCOPY(esp->es_server.ea_name, + outp, + esp->es_server.ea_namelen); + INCPTR(esp->es_server.ea_namelen, outp); + break; +#endif /* PPP_WITH_CHAPMS */ + +#ifdef PPP_WITH_EAPTLS case eapTlsStart: PUTCHAR(EAPT_TLS, outp); PUTCHAR(EAP_TLS_FLAGS_START, outp); @@ -814,9 +934,9 @@ eap_send_request(eap_state *esp) eaptls_send(esp->es_server.ea_session, &outp); eap_figure_next_state(esp, 0); break; -#endif /* USE_EAPTLS */ +#endif /* PPP_WITH_EAPTLS */ -#ifdef USE_SRP +#ifdef PPP_WITH_SRP case eapSRP1: PUTCHAR(EAPT_SRP, outp); PUTCHAR(EAPSRP_CHALLENGE, outp); @@ -862,10 +982,10 @@ eap_send_request(eap_state *esp) PUTLONG(SRPVAL_EBIT, outp); ts = (struct t_server *)esp->es_server.ea_session; assert(ts != NULL); - BCOPY(t_serverresponse(ts), outp, SHA_DIGESTSIZE); - INCPTR(SHA_DIGESTSIZE, outp); + BCOPY(t_serverresponse(ts), outp, SHA_DIGEST_LENGTH); + INCPTR(SHA_DIGEST_LENGTH, outp); - if (pncrypt_setkey(0)) { + if (pncrypt_getkey(0, key, keylen)) { /* Generate pseudonym */ optr = outp; cp = (unsigned char *)esp->es_server.ea_peer; @@ -875,15 +995,17 @@ eap_send_request(eap_state *esp) BCOPY(cp, clear + 1, j); i -= j; cp += j; - if (!DesEncrypt(clear, cipher)) { + + if (!DesEncrypt(clear, key, cipher)) { dbglog("no DES here; not generating pseudonym"); break; - } + } + BZERO(&b64, sizeof (b64)); outp++; /* space for pseudonym length */ outp += b64enc(&b64, cipher, 8, outp); while (i >= 8) { - (void) DesEncrypt(cp, cipher); + DesEncrypt(cp, key, cipher); outp += b64enc(&b64, cipher, 8, outp); cp += 8; i -= 8; @@ -895,7 +1017,8 @@ eap_send_request(eap_state *esp) *cp++ = drand48() * 0x100; i++; } - (void) DesEncrypt(clear, cipher); + + DesEncrypt(clear, key, cipher); outp += b64enc(&b64, cipher, 8, outp); } outp += b64flush(&b64, outp); @@ -903,32 +1026,40 @@ eap_send_request(eap_state *esp) /* Set length and pad out to next 20 octet boundary */ i = outp - optr - 1; *optr = i; - i %= SHA_DIGESTSIZE; + i %= SHA_DIGEST_LENGTH; if (i != 0) { - while (i < SHA_DIGESTSIZE) { + while (i < SHA_DIGEST_LENGTH) { *outp++ = drand48() * 0x100; i++; } } /* Obscure the pseudonym with SHA1 hash */ - SHA1Init(&ctxt); - SHA1Update(&ctxt, &esp->es_server.ea_id, 1); - SHA1Update(&ctxt, esp->es_server.ea_skey, - SESSION_KEY_LEN); - SHA1Update(&ctxt, esp->es_server.ea_peer, - esp->es_server.ea_peerlen); - while (optr < outp) { - SHA1Final(dig, &ctxt); - cp = dig; - while (cp < dig + SHA_DIGESTSIZE) - *optr++ ^= *cp++; - SHA1Init(&ctxt); - SHA1Update(&ctxt, &esp->es_server.ea_id, 1); - SHA1Update(&ctxt, esp->es_server.ea_skey, - SESSION_KEY_LEN); - SHA1Update(&ctxt, optr - SHA_DIGESTSIZE, - SHA_DIGESTSIZE); + ctxt = PPP_MD_CTX_new(); + if (ctxt) { + + PPP_DigestInit(ctxt, PPP_sha1()); + PPP_DigestUpdate(ctxt, &esp->es_server.ea_id, 1); + PPP_DigestUpdate(ctxt, &esp->es_server.ea_skey, + SESSION_KEY_LEN); + PPP_DigestUpdate(ctxt, esp->es_server.ea_peer, + esp->es_server.ea_peerlen); + while (optr < outp) { + diglen = SHA_DIGEST_LENGTH; + PPP_DigestFinal(ctxt, dig, &diglen); + cp = dig; + while (cp < dig + SHA_DIGEST_LENGTH) + *optr++ ^= *cp++; + + PPP_DigestInit(ctxt, PPP_sha1()); + PPP_DigestUpdate(ctxt, &esp->es_server.ea_id, 1); + PPP_DigestUpdate(ctxt, esp->es_server.ea_skey, + SESSION_KEY_LEN); + PPP_DigestUpdate(ctxt, optr - SHA_DIGEST_LENGTH, + SHA_DIGEST_LENGTH); + } + + PPP_MD_CTX_free(ctxt); } } break; @@ -945,7 +1076,7 @@ eap_send_request(eap_state *esp) BCOPY(esp->es_challenge, outp, esp->es_challen); INCPTR(esp->es_challen, outp); break; -#endif /* USE_SRP */ +#endif /* PPP_WITH_SRP */ default: return; @@ -999,18 +1130,18 @@ eap_authpeer(int unit, char *localname) static void eap_server_timeout(void *arg) { -#ifdef USE_EAPTLS +#ifdef PPP_WITH_EAPTLS u_char *outp; u_char *lenloc; int outlen; -#endif /* USE_EAPTLS */ +#endif /* PPP_WITH_EAPTLS */ eap_state *esp = (eap_state *) arg; if (!eap_server_active(esp)) return; -#ifdef USE_EAPTLS +#ifdef PPP_WITH_EAPTLS switch(esp->es_server.ea_prev_state) { /* @@ -1048,7 +1179,7 @@ eap_server_timeout(void *arg) default: break; } -#endif /* USE_EAPTLS */ +#endif /* PPP_WITH_EAPTLS */ /* EAP ID number must not change on timeout. */ eap_send_request(esp); @@ -1209,19 +1340,19 @@ eap_chap_response(eap_state *esp, u_char id, u_char *hash, 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 + 2 * sizeof (u_char) + MD5_SIGNATURE_SIZE + + msglen = EAP_HEADERLEN + 2 * sizeof (u_char) + MD5_DIGEST_LENGTH + namelen; PUTSHORT(msglen, outp); PUTCHAR(EAPT_MD5CHAP, outp); - PUTCHAR(MD5_SIGNATURE_SIZE, outp); - BCOPY(hash, outp, MD5_SIGNATURE_SIZE); - INCPTR(MD5_SIGNATURE_SIZE, outp); + PUTCHAR(MD5_DIGEST_LENGTH, outp); + BCOPY(hash, outp, MD5_DIGEST_LENGTH); + INCPTR(MD5_DIGEST_LENGTH, outp); if (namelen > 0) { BCOPY(name, outp, namelen); } @@ -1229,7 +1360,7 @@ eap_chap_response(eap_state *esp, u_char id, u_char *hash, output(esp->es_unit, outpacket_buf, PPP_HDRLEN + msglen); } -#ifdef USE_SRP +#ifdef PPP_WITH_SRP /* * Format and send a SRP EAP Response message. */ @@ -1275,18 +1406,18 @@ eap_srpval_response(eap_state *esp, u_char id, u_int32_t flags, u_char *str) PUTCHAR(id, outp); esp->es_client.ea_id = id; msglen = EAP_HEADERLEN + 2 * sizeof (u_char) + sizeof (u_int32_t) + - SHA_DIGESTSIZE; + SHA_DIGEST_LENGTH; PUTSHORT(msglen, outp); PUTCHAR(EAPT_SRP, outp); PUTCHAR(EAPSRP_CVALIDATOR, outp); PUTLONG(flags, outp); - BCOPY(str, outp, SHA_DIGESTSIZE); + BCOPY(str, outp, SHA_DIGEST_LENGTH); output(esp->es_unit, outpacket_buf, PPP_HDRLEN + msglen); } -#endif /* USE_SRP */ +#endif /* PPP_WITH_SRP */ -#ifdef USE_EAPTLS +#ifdef PPP_WITH_EAPTLS /* * Send an EAP-TLS response message with tls data */ @@ -1353,7 +1484,7 @@ eap_tls_sendack(eap_state *esp, u_char id) output(esp->es_unit, outpacket_buf, PPP_HDRLEN + outlen); } -#endif /* USE_EAPTLS */ +#endif /* PPP_WITH_EAPTLS */ static void eap_send_nak(eap_state *esp, u_char id, u_char type) @@ -1376,7 +1507,7 @@ eap_send_nak(eap_state *esp, u_char id, u_char type) output(esp->es_unit, outpacket_buf, PPP_HDRLEN + msglen); } -#ifdef USE_SRP +#ifdef PPP_WITH_SRP static char * name_of_pn_file(void) { @@ -1390,7 +1521,7 @@ name_of_pn_file(void) errno = EINVAL; return (NULL); } - file = _PATH_PSEUDONYM; + file = PPP_PATH_PSEUDONYM; pl = strlen(user) + strlen(file) + 2; path = malloc(pl); if (path == NULL) @@ -1434,9 +1565,9 @@ write_pseudonym(eap_state *esp, u_char *inp, int len, int id) { u_char val; u_char *datp, *digp; - SHA1_CTX ctxt; - u_char dig[SHA_DIGESTSIZE]; - int dsize, fd, olen = len; + PPP_MD_CTX *ctxt; + u_char dig[SHA_DIGEST_LENGTH]; + int dsize, fd, olen = len, diglen = sizeof(dig); /* * Do the decoding by working backwards. This eliminates the need @@ -1444,22 +1575,30 @@ write_pseudonym(eap_state *esp, u_char *inp, int len, int id) */ val = id; while (len > 0) { - if ((dsize = len % SHA_DIGESTSIZE) == 0) - dsize = SHA_DIGESTSIZE; + if ((dsize = len % SHA_DIGEST_LENGTH) == 0) + dsize = SHA_DIGEST_LENGTH; len -= dsize; datp = inp + len; - SHA1Init(&ctxt); - SHA1Update(&ctxt, &val, 1); - SHA1Update(&ctxt, esp->es_client.ea_skey, SESSION_KEY_LEN); - if (len > 0) { - SHA1Update(&ctxt, datp, SHA_DIGESTSIZE); - } else { - SHA1Update(&ctxt, esp->es_client.ea_name, - esp->es_client.ea_namelen); + ctxt = PPP_MD_CTX_new(); + if (ctxt) { + + PPP_DigestInit(ctxt, PPP_sha1()); + PPP_DigestUpdate(ctxt, &val, 1); + PPP_DigestUpdate(ctxt, esp->es_client.ea_skey, + SESSION_KEY_LEN); + if (len > 0) { + PPP_DigestUpdate(ctxt, datp, SHA_DIGEST_LENGTH); + } else { + PPP_DigestUpdate(ctxt, esp->es_client.ea_name, + esp->es_client.ea_namelen); + } + PPP_DigestFinal(ctxt, dig, &diglen); + + for (digp = dig; digp < dig + SHA_DIGEST_LENGTH; digp++) + *datp++ ^= *digp; + + PPP_MD_CTX_free(ctxt); } - SHA1Final(dig, &ctxt); - for (digp = dig; digp < dig + SHA_DIGESTSIZE; digp++) - *datp++ ^= *digp; } /* Now check that the result is sane */ @@ -1483,7 +1622,40 @@ write_pseudonym(eap_state *esp, u_char *inp, int len, int id) remove_pn_file(); } } -#endif /* USE_SRP */ +#endif /* PPP_WITH_SRP */ + +#if PPP_WITH_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). @@ -1496,21 +1668,23 @@ eap_request(eap_state *esp, u_char *inp, int id, int len) int secret_len; char secret[MAXWORDLEN]; char rhostname[256]; - MD5_CTX mdContext; - u_char hash[MD5_SIGNATURE_SIZE]; -#ifdef USE_EAPTLS + PPP_MD_CTX *mdctx; + u_char hash[MD5_DIGEST_LENGTH]; + int hashlen = MD5_DIGEST_LENGTH; +#ifdef PPP_WITH_EAPTLS u_char flags; struct eaptls_session *ets = esp->es_client.ea_session; -#endif /* USE_EAPTLS */ +#endif /* PPP_WITH_EAPTLS */ -#ifdef USE_SRP +#ifdef PPP_WITH_SRP struct t_client *tc; struct t_num sval, gval, Nval, *Ap, Bval; u_char vals[2]; - SHA1_CTX ctxt; - u_char dig[SHA_DIGESTSIZE]; + PPP_MD_CTX *ctxt; + u_char dig[SHA_DIGEST_LENGTH]; + int diglen = sizeof(dig); int fd; -#endif /* USE_SRP */ +#endif /* PPP_WITH_SRP */ /* * Ignore requests if we're not open @@ -1547,7 +1721,7 @@ eap_request(eap_state *esp, u_char *inp, int id, int len) case EAPT_IDENTITY: if (len > 0) info("EAP: Identity prompt \"%.*q\"", len, inp); -#ifdef USE_SRP +#ifdef PPP_WITH_SRP if (esp->es_usepseudo && (esp->es_usedpseudo == 0 || (esp->es_usedpseudo == 1 && @@ -1573,7 +1747,7 @@ eap_request(eap_state *esp, u_char *inp, int id, int len) remove_pn_file(); esp->es_usedpseudo = 2; } -#endif /* USE_SRP */ +#endif /* PPP_WITH_SRP */ eap_send_response(esp, id, typenum, (u_char *)esp->es_client.ea_name, esp->es_client.ea_namelen); break; @@ -1634,18 +1808,32 @@ eap_request(eap_state *esp, u_char *inp, int id, int len) eap_send_nak(esp, id, EAPT_SRP); break; } - MD5_Init(&mdContext); - typenum = id; - MD5_Update(&mdContext, &typenum, 1); - MD5_Update(&mdContext, (u_char *)secret, secret_len); - BZERO(secret, sizeof (secret)); - MD5_Update(&mdContext, inp, vallen); - MD5_Final(hash, &mdContext); - eap_chap_response(esp, id, hash, esp->es_client.ea_name, - esp->es_client.ea_namelen); + + mdctx = PPP_MD_CTX_new(); + if (mdctx != NULL) { + if (PPP_DigestInit(mdctx, PPP_md5())) { + typenum = id; + if (PPP_DigestUpdate(mdctx, &typenum, 1)) { + if (PPP_DigestUpdate(mdctx, secret, secret_len)) { + BZERO(secret, sizeof(secret)); + if (PPP_DigestUpdate(mdctx, inp, vallen)) { + if (PPP_DigestFinal(mdctx, hash, &hashlen)) { + eap_chap_response(esp, id, hash, esp->es_client.ea_name, + esp->es_client.ea_namelen); + PPP_MD_CTX_free(mdctx); + break; + } + } + } + } + } + PPP_MD_CTX_free(mdctx); + } + dbglog("EAP: Invalid MD5 checksum"); + eap_send_nak(esp, id, EAPT_SRP); break; -#ifdef USE_EAPTLS +#ifdef PPP_WITH_EAPTLS case EAPT_TLS: switch(esp->es_client.ea_state) { @@ -1671,7 +1859,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; } @@ -1683,7 +1871,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: @@ -1713,7 +1901,7 @@ eap_request(eap_state *esp, u_char *inp, int id, int len) /* Check if TLS handshake is finished */ if(eaptls_is_init_finished(ets)) { -#ifdef MPPE +#ifdef PPP_WITH_MPPE eaptls_gen_mppe_keys(ets, 1); #endif eaptls_free_session(ets); @@ -1727,15 +1915,15 @@ eap_request(eap_state *esp, u_char *inp, int id, int len) break; default: - eap_send_nak(esp, id, EAPT_TLS); + eap_send_nak(esp, id, EAPT_MSCHAPV2); esp->es_client.ea_using_eaptls = 0; break; } break; -#endif /* USE_EAPTLS */ +#endif /* PPP_WITH_EAPTLS */ -#ifdef USE_SRP +#ifdef PPP_WITH_SRP case EAPT_SRP: if (len < 1) { error("EAP: received empty SRP Request"); @@ -1919,7 +2107,7 @@ eap_request(eap_state *esp, u_char *inp, int id, int len) esp->es_client.ea_id, id); } } else { - len -= sizeof (u_int32_t) + SHA_DIGESTSIZE; + len -= sizeof (u_int32_t) + SHA_DIGEST_LENGTH; if (len < 0 || t_clientverify(tc, inp + sizeof (u_int32_t)) != 0) { error("EAP: SRP server verification " @@ -1929,7 +2117,7 @@ eap_request(eap_state *esp, u_char *inp, int id, int len) GETLONG(esp->es_client.ea_keyflags, inp); /* Save pseudonym if user wants it. */ if (len > 0 && esp->es_usepseudo) { - INCPTR(SHA_DIGESTSIZE, inp); + INCPTR(SHA_DIGEST_LENGTH, inp); write_pseudonym(esp, inp, len, id); } } @@ -1946,17 +2134,24 @@ eap_request(eap_state *esp, u_char *inp, int id, int len) warn("EAP: malformed Lightweight rechallenge"); return; } - SHA1Init(&ctxt); - vals[0] = id; - SHA1Update(&ctxt, vals, 1); - SHA1Update(&ctxt, esp->es_client.ea_skey, - SESSION_KEY_LEN); - SHA1Update(&ctxt, inp, len); - SHA1Update(&ctxt, esp->es_client.ea_name, - esp->es_client.ea_namelen); - SHA1Final(dig, &ctxt); - eap_srp_response(esp, id, EAPSRP_LWRECHALLENGE, dig, - SHA_DIGESTSIZE); + ctxt = PPP_MD_CTX_new(); + if (ctxt) { + + vals[0] = id; + PPP_DigestInit(ctxt, PPP_sha1()); + PPP_DigestUpdate(ctxt, vals, 1); + PPP_DigestUpdate(ctxt, esp->es_client.ea_skey, + SESSION_KEY_LEN); + PPP_DigestUpdate(ctxt, inp, len); + PPP_DigestUpdate(ctxt, esp->es_client.ea_name, + esp->es_client.ea_namelen); + PPP_DigestFinal(ctxt, dig, &diglen); + + PPP_MD_CTX_free(ctxt); + + eap_srp_response(esp, id, EAPSRP_LWRECHALLENGE, dig, + SHA_DIGEST_LENGTH); + } break; default: @@ -1965,7 +2160,137 @@ eap_request(eap_state *esp, u_char *inp, int id, int len) break; } break; -#endif /* USE_SRP */ +#endif /* PPP_WITH_SRP */ + +#ifdef PPP_WITH_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; + } + esp->es_client.ea_namelen = strlen(esp->es_client.ea_name); + + /* 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 /* PPP_WITH_CHAPMS */ +#ifdef PPP_WITH_PEAP + case EAPT_PEAP: + + /* Initialize the PEAP context (if not already initialized) */ + if (!esp->ea_peap) { + rhostname[0] = '\0'; + if (explicit_remote || (remote_name[0] != '\0')) { + strlcpy(rhostname, remote_name, sizeof (rhostname)); + } + if (peap_init(&esp->ea_peap, rhostname)) { + eap_send_nak(esp, id, EAPT_TLS); + break; + } + } + + /* Process the PEAP packet */ + if (peap_process(esp, id, inp, len)) { + eap_send_nak(esp, id, EAPT_TLS); + } + + break; +#endif // PPP_WITH_PEAP default: info("EAP: unknown authentication type %d; Naking", typenum); @@ -1980,16 +2305,16 @@ 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 PPP_WITH_SRP t_clientclose(tc); auth_withpeer_fail(esp->es_unit, PPP_EAP); -#endif /* USE_SRP */ +#endif /* PPP_WITH_SRP */ } /* @@ -2003,21 +2328,26 @@ eap_response(eap_state *esp, u_char *inp, int id, int len) int secret_len; char secret[MAXSECRETLEN]; char rhostname[256]; - MD5_CTX mdContext; - u_char hash[MD5_SIGNATURE_SIZE]; -#ifdef USE_SRP + PPP_MD_CTX *mdctx; + u_char hash[MD5_DIGEST_LENGTH]; + int hashlen = MD5_DIGEST_LENGTH; +#ifdef PPP_WITH_SRP struct t_server *ts; struct t_num A; - SHA1_CTX ctxt; - u_char dig[SHA_DIGESTSIZE]; - SHA1_CTX ctxt; - u_char dig[SHA_DIGESTSIZE]; -#endif /* USE_SRP */ + PPP_MD_CTX *ctxt; + u_char dig[SHA_DIGEST_LENGTH]; + int diglen = sizeof(dig); +#endif /* PPP_WITH_SRP */ -#ifdef USE_EAPTLS +#ifdef PPP_WITH_EAPTLS struct eaptls_session *ets; u_char flags; -#endif /* USE_EAPTLS */ +#endif /* PPP_WITH_EAPTLS */ +#ifdef PPP_WITH_CHAPMS + u_char opcode; + chap_verify_hook_fn *chap_verifier; + char response_message[256]; +#endif /* PPP_WITH_CHAPMS */ /* * Ignore responses if we're not open @@ -2064,7 +2394,7 @@ eap_response(eap_state *esp, u_char *inp, int id, int len) eap_figure_next_state(esp, 0); break; -#ifdef USE_EAPTLS +#ifdef PPP_WITH_EAPTLS case EAPT_TLS: switch(esp->es_server.ea_state) { @@ -2094,7 +2424,7 @@ eap_response(eap_state *esp, u_char *inp, int id, int len) GETCHAR(flags, inp); if(len == 1 && !flags) { /* Ack = ok */ -#ifdef MPPE +#ifdef PPP_WITH_MPPE eaptls_gen_mppe_keys( esp->es_server.ea_session, 0 ); #endif eap_send_success(esp); @@ -2120,7 +2450,7 @@ eap_response(eap_state *esp, u_char *inp, int id, int len) break; } break; -#endif /* USE_EAPTLS */ +#endif /* PPP_WITH_EAPTLS */ case EAPT_NOTIFICATION: dbglog("EAP unexpected Notification; response discarded"); @@ -2153,12 +2483,25 @@ eap_response(eap_state *esp, u_char *inp, int id, int len) esp->es_server.ea_state = eapMD5Chall; break; -#ifdef USE_EAPTLS +#ifdef PPP_WITH_EAPTLS /* Send EAP-TLS start packet */ case EAPT_TLS: esp->es_server.ea_state = eapTlsStart; break; -#endif /* USE_EAPTLS */ +#endif /* PPP_WITH_EAPTLS */ + +#ifdef PPP_WITH_CHAPMS + case EAPT_MSCHAPV2: + info("EAP: peer proposes MSCHAPv2"); + /* If MSCHAPv2 digest was not found, NAK the packet */ + if (!esp->es_server.digest) { + error("EAP MSCHAPv2 not supported"); + eap_send_nak(esp, id, EAPT_SRP); + break; + } + esp->es_server.ea_state = eapMSCHAPv2Chall; + break; +#endif /* PPP_WITH_CHAPMS */ default: dbglog("EAP: peer requesting unknown Type %d", vallen); @@ -2224,24 +2567,150 @@ eap_response(eap_state *esp, u_char *inp, int id, int len) eap_send_failure(esp); break; } - MD5_Init(&mdContext); - MD5_Update(&mdContext, &esp->es_server.ea_id, 1); - MD5_Update(&mdContext, (u_char *)secret, secret_len); - BZERO(secret, sizeof (secret)); - MD5_Update(&mdContext, esp->es_challenge, esp->es_challen); - MD5_Final(hash, &mdContext); - if (BCMP(hash, inp, MD5_SIGNATURE_SIZE) != 0) { - eap_send_failure(esp); + + mdctx = PPP_MD_CTX_new(); + if (mdctx != NULL) { + + if (PPP_DigestInit(mdctx, PPP_md5())) { + + if (PPP_DigestUpdate(mdctx, &esp->es_server.ea_id, 1)) { + + if (PPP_DigestUpdate(mdctx, &secret, secret_len)) { + + BZERO(secret, sizeof(secret)); + if (PPP_DigestUpdate(mdctx, esp->es_challenge, esp->es_challen)) { + + if (PPP_DigestFinal(mdctx, hash, &hashlen)) { + + if (BCMP(hash, inp, MD5_DIGEST_LENGTH) == 0) { + esp->es_server.ea_type = EAPT_MD5CHAP; + eap_send_success(esp); + eap_figure_next_state(esp, 0); + + if (esp->es_rechallenge != 0) { + TIMEOUT(eap_rechallenge, esp, esp->es_rechallenge); + } + PPP_MD_CTX_free(mdctx); + break; + } + } + } + } + } + } + + PPP_MD_CTX_free(mdctx); + } + + eap_send_failure(esp); + break; + +#ifdef PPP_WITH_CHAPMS + case EAPT_MSCHAPV2: + if (len < 1) { + error("EAP: received MSCHAPv2 with no data"); + eap_figure_next_state(esp, 1); break; } - esp->es_server.ea_type = EAPT_MD5CHAP; - eap_send_success(esp); - eap_figure_next_state(esp, 0); - if (esp->es_rechallenge != 0) - TIMEOUT(eap_rechallenge, esp, esp->es_rechallenge); + GETCHAR(opcode, inp); + len--; + + switch (opcode) { + case CHAP_RESPONSE: + if (esp->es_server.ea_state != eapMSCHAPv2Chall) { + error("EAP: unexpected MSCHAPv2-Response"); + eap_figure_next_state(esp, 1); + break; + } + /* skip MS ID + len */ + INCPTR(3, inp); + GETCHAR(vallen, inp); + len -= 4; + + if (vallen != MS_CHAP2_RESPONSE_LEN || vallen > len) { + error("EAP: Invalid MSCHAPv2-Response " + "length %d", vallen); + eap_figure_next_state(esp, 1); + break; + } + + /* Not so likely to happen. */ + if (len - vallen >= sizeof (rhostname)) { + dbglog("EAP: trimming really long peer name down"); + BCOPY(inp + vallen, rhostname, sizeof (rhostname) - 1); + rhostname[sizeof (rhostname) - 1] = '\0'; + } else { + BCOPY(inp + vallen, rhostname, len - vallen); + rhostname[len - vallen] = '\0'; + } + + /* In case the remote doesn't give us his name. */ + if (explicit_remote || + (remote_name[0] != '\0' && vallen == len)) + strlcpy(rhostname, remote_name, sizeof (rhostname)); + + /* strip the MS domain name */ + if (chapms_strip_domain && strrchr(rhostname, '\\')) { + char tmp[MAXNAMELEN+1]; + + strcpy(tmp, strrchr(rhostname, '\\') + 1); + strlcpy(rhostname, tmp, sizeof(rhostname)); + } + + if (chap_verify_hook) + chap_verifier = chap_verify_hook; + else + chap_verifier = eap_chap_verify_response; + + esp->es_server.ea_id += 1; + if ((*chap_verifier)(rhostname, + esp->es_server.ea_name, + id, + esp->es_server.digest, + esp->es_challenge, + inp - 1, + response_message, + sizeof(response_message))) + { + info("EAP: MSCHAPv2 success for peer %q", + rhostname); + esp->es_server.ea_type = EAPT_MSCHAPV2; + eap_chapms2_send_request(esp, + esp->es_server.ea_id, + CHAP_SUCCESS, + esp->es_server.ea_id, + response_message, + strlen(response_message)); + eap_figure_next_state(esp, 0); + if (esp->es_rechallenge != 0) + TIMEOUT(eap_rechallenge, esp, esp->es_rechallenge); + } + else { + warn("EAP: MSCHAPv2 failure for peer %q", + rhostname); + eap_chapms2_send_request(esp, + esp->es_server.ea_id, + CHAP_FAILURE, + esp->es_server.ea_id, + response_message, + strlen(response_message)); + } + break; + case CHAP_SUCCESS: + info("EAP: MSCHAPv2 success confirmed"); + break; + case CHAP_FAILURE: + info("EAP: MSCHAPv2 failure confirmed"); + break; + default: + error("EAP: Unhandled MSCHAPv2 opcode %d", opcode); + eap_send_nak(esp, id, EAPT_SRP); + } + break; +#endif /* PPP_WITH_CHAPMS */ -#ifdef USE_SRP +#ifdef PPP_WITH_SRP case EAPT_SRP: if (len < 1) { error("EAP: empty SRP Response"); @@ -2277,9 +2746,9 @@ eap_response(eap_state *esp, u_char *inp, int id, int len) eap_figure_next_state(esp, 1); break; } - if (len < sizeof (u_int32_t) + SHA_DIGESTSIZE) { + if (len < sizeof (u_int32_t) + SHA_DIGEST_LENGTH) { error("EAP: M1 length %d < %d", len, - sizeof (u_int32_t) + SHA_DIGESTSIZE); + sizeof (u_int32_t) + SHA_DIGEST_LENGTH); eap_figure_next_state(esp, 1); break; } @@ -2316,33 +2785,41 @@ eap_response(eap_state *esp, u_char *inp, int id, int len) info("EAP: unexpected SRP Subtype 4 Response"); return; } - if (len != SHA_DIGESTSIZE) { + if (len != SHA_DIGEST_LENGTH) { error("EAP: bad Lightweight rechallenge " "response"); return; } - SHA1Init(&ctxt); - vallen = id; - SHA1Update(&ctxt, &vallen, 1); - SHA1Update(&ctxt, esp->es_server.ea_skey, - SESSION_KEY_LEN); - SHA1Update(&ctxt, esp->es_challenge, esp->es_challen); - SHA1Update(&ctxt, esp->es_server.ea_peer, - esp->es_server.ea_peerlen); - SHA1Final(dig, &ctxt); - if (BCMP(dig, inp, SHA_DIGESTSIZE) != 0) { - error("EAP: failed Lightweight rechallenge"); - eap_send_failure(esp); - break; + ctxt = PPP_MD_CTX_new(); + if (ctxt) { + vallen = id; + + PPP_DigestInit(ctxt, PPP_sha1()); + PPP_DigestUpdate(ctxt, &vallen, 1); + PPP_DigestUpdate(ctxt, esp->es_server.ea_skey, + SESSION_KEY_LEN); + PPP_DigestUpdate(ctxt, esp->es_challenge, esp->es_challen); + PPP_DigestUpdate(ctxt, esp->es_server.ea_peer, + esp->es_server.ea_peerlen); + PPP_DigestFinal(ctxt, dig, &diglen); + + PPP_MD_CTX_free(ctxt); + + if (BCMP(dig, inp, SHA_DIGEST_LENGTH) != 0) { + error("EAP: failed Lightweight rechallenge"); + eap_send_failure(esp); + break; + } + + esp->es_server.ea_state = eapOpen; + if (esp->es_lwrechallenge != 0) + TIMEOUT(srp_lwrechallenge, esp, + esp->es_lwrechallenge); } - esp->es_server.ea_state = eapOpen; - if (esp->es_lwrechallenge != 0) - TIMEOUT(srp_lwrechallenge, esp, - esp->es_lwrechallenge); break; } break; -#endif /* USE_SRP */ +#endif /* PPP_WITH_SRP */ default: /* This can't happen. */ @@ -2368,9 +2845,9 @@ static void eap_success(eap_state *esp, u_char *inp, int id, int len) { if (esp->es_client.ea_state != eapOpen && !eap_client_active(esp) -#ifdef USE_EAPTLS +#ifdef PPP_WITH_EAPTLS && esp->es_client.ea_state != eapTlsRecvSuccess -#endif /* USE_EAPTLS */ +#endif /* PPP_WITH_EAPTLS */ ) { dbglog("EAP unexpected success message in state %s (%d)", eap_state_name(esp->es_client.ea_state), @@ -2378,7 +2855,7 @@ eap_success(eap_state *esp, u_char *inp, int id, int len) return; } -#ifdef USE_EAPTLS +#ifdef PPP_WITH_EAPTLS if(esp->es_client.ea_using_eaptls && esp->es_client.ea_state != eapTlsRecvSuccess) { dbglog("EAP-TLS unexpected success message in state %s (%d)", @@ -2386,7 +2863,7 @@ eap_success(eap_state *esp, u_char *inp, int id, int len) esp->es_client.ea_state); return; } -#endif /* USE_EAPTLS */ +#endif /* PPP_WITH_EAPTLS */ if (esp->es_client.ea_timeout > 0) { UNTIMEOUT(eap_client_timeout, (void *)esp); @@ -2397,6 +2874,10 @@ eap_success(eap_state *esp, u_char *inp, int id, int len) PRINTMSG(inp, len); } +#ifdef PPP_WITH_PEAP + peap_finish(&esp->ea_peap); +#endif + esp->es_client.ea_state = eapOpen; auth_withpeer_success(esp->es_unit, PPP_EAP, 0); } @@ -2431,6 +2912,11 @@ eap_failure(eap_state *esp, u_char *inp, int id, int len) esp->es_client.ea_state = eapBadAuth; error("EAP: peer reports authentication failure"); + +#ifdef PPP_WITH_PEAP + peap_finish(&esp->ea_peap); +#endif + auth_withpeer_fail(esp->es_unit, PPP_EAP); } @@ -2499,7 +2985,9 @@ static char *eap_typenames[] = { "OTP", "Generic-Token", NULL, NULL, "RSA", "DSS", "KEA", "KEA-Validate", "TLS", "Defender", "Windows 2000", "Arcot", - "Cisco", "Nokia", "SRP" + "Cisco", "Nokia", "SRP", NULL, + "TTLS", "RAS", "AKA", "3COM", "PEAP", + "MSCHAPv2" }; static int @@ -2509,9 +2997,12 @@ eap_printpkt(u_char *inp, int inlen, int code, id, len, rtype, vallen; u_char *pstart; u_int32_t uval; -#ifdef USE_EAPTLS +#ifdef PPP_WITH_EAPTLS u_char flags; -#endif /* USE_EAPTLS */ +#endif /* PPP_WITH_EAPTLS */ +#ifdef PPP_WITH_CHAPMS + u_char opcode; +#endif /* PPP_WITH_CHAPMS */ if (inlen < EAP_HEADERLEN) return (0); @@ -2576,7 +3067,62 @@ eap_printpkt(u_char *inp, int inlen, } break; -#ifdef USE_EAPTLS +#ifdef PPP_WITH_CHAPMS + case EAPT_MSCHAPV2: + if (len <= 0) + break; + GETCHAR(opcode, inp); + len--; + switch (opcode) { + case CHAP_CHALLENGE: + INCPTR(3, inp); + len -= 3; + GETCHAR(vallen, inp); + len--; + if (vallen > len) + goto truncated; + len -= vallen; + printer(arg, " Challenge <"); + for (; vallen > 0; --vallen) { + u_char val; + GETCHAR(val, inp); + printer(arg, "%.2x", val); + } + printer(arg, ">"); + if (len > 0) { + printer(arg, ", "); + INCPTR(len, inp); + len = 0; + } else { + printer(arg, ", "); + } + break; + case CHAP_SUCCESS: + INCPTR(3, inp); + len -= 3; + printer(arg, " Success "); + break; + case CHAP_FAILURE: + INCPTR(3, inp); + len -= 3; + printer(arg, " Failure "); + break; + default: + INCPTR(3, inp); + len -= 3; + printer(arg, " opcode=0x%x <%.*B>", opcode, len, inp); + break; + } + break; +#endif /* PPP_WITH_CHAPMS */ + +#ifdef PPP_WITH_EAPTLS case EAPT_TLS: if (len < 1) break; @@ -2592,8 +3138,9 @@ eap_printpkt(u_char *inp, int inlen, printer(arg, flags & EAP_TLS_FLAGS_MF ? "M":"-"); printer(arg, flags & EAP_TLS_FLAGS_START ? "S":"- "); break; -#endif /* USE_EAPTLS */ +#endif /* PPP_WITH_EAPTLS */ +#ifdef PPP_WITH_SRP case EAPT_SRP: if (len < 3) goto truncated; @@ -2661,10 +3208,10 @@ eap_printpkt(u_char *inp, int inlen, if (uval != 0) { printer(arg, " f<%X>", uval); } - if ((vallen = len) > SHA_DIGESTSIZE) - vallen = SHA_DIGESTSIZE; + if ((vallen = len) > SHA_DIGEST_LENGTH) + vallen = SHA_DIGEST_LENGTH; printer(arg, " ", len, inp, - len < SHA_DIGESTSIZE ? "?" : ""); + len < SHA_DIGEST_LENGTH ? "?" : ""); INCPTR(vallen, inp); len -= vallen; if (len > 0) { @@ -2681,6 +3228,7 @@ eap_printpkt(u_char *inp, int inlen, break; } break; +#endif /* PPP_WITH_SRP */ } break; @@ -2705,7 +3253,7 @@ eap_printpkt(u_char *inp, int inlen, } break; -#ifdef USE_EAPTLS +#ifdef PPP_WITH_EAPTLS case EAPT_TLS: if (len < 1) break; @@ -2722,7 +3270,7 @@ eap_printpkt(u_char *inp, int inlen, printer(arg, flags & EAP_TLS_FLAGS_START ? "S":"- "); break; -#endif /* USE_EAPTLS */ +#endif /* PPP_WITH_EAPTLS */ case EAPT_NAK: if (len <= 0) { @@ -2733,7 +3281,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; @@ -2761,6 +3309,52 @@ eap_printpkt(u_char *inp, int inlen, } break; +#ifdef PPP_WITH_CHAPMS + case EAPT_MSCHAPV2: + if (len <= 0) + break; + GETCHAR(opcode, inp); + len--; + switch (opcode) { + case CHAP_RESPONSE: + INCPTR(3, inp); + len -= 3; + GETCHAR(vallen, inp); + len--; + if (vallen > len) + goto truncated; + len -= vallen; + printer(arg, " Response <"); + for (; vallen > 0; --vallen) { + u_char val; + GETCHAR(val, inp); + printer(arg, "%.2x", val); + } + printer(arg, ">"); + if (len > 0) { + printer(arg, ", "); + INCPTR(len, inp); + len = 0; + } else { + printer(arg, ", "); + } + break; + case CHAP_SUCCESS: + printer(arg, " Success"); + break; + case CHAP_FAILURE: + printer(arg, " Failure"); + break; + default: + printer(arg, " opcode=0x%x <%.*B>", opcode, len, inp); + break; + } + break; +#endif /* PPP_WITH_CHAPMS */ + +#ifdef PPP_WITH_SRP case EAPT_SRP: if (len < 1) goto truncated; @@ -2787,7 +3381,7 @@ eap_printpkt(u_char *inp, int inlen, printer(arg, " f<%X>", uval); } printer(arg, " ", len, inp, - len == SHA_DIGESTSIZE ? "" : "?"); + len == SHA_DIGEST_LENGTH ? "" : "?"); INCPTR(len, inp); len = 0; break; @@ -2797,14 +3391,15 @@ eap_printpkt(u_char *inp, int inlen, case EAPSRP_LWRECHALLENGE: printer(arg, " ", len, inp, - len == SHA_DIGESTSIZE ? "" : "?"); - if ((vallen = len) > SHA_DIGESTSIZE) - vallen = SHA_DIGESTSIZE; + len == SHA_DIGEST_LENGTH ? "" : "?"); + if ((vallen = len) > SHA_DIGEST_LENGTH) + vallen = SHA_DIGEST_LENGTH; INCPTR(vallen, inp); len -= vallen; break; } break; +#endif /* PPP_WITH_SRP */ } break;