X-Git-Url: https://git.ozlabs.org/?a=blobdiff_plain;f=pppd%2Feap.c;h=6cb595f5b7c57b78cdd22ed790928535f4f29777;hb=4a54e34cf5629f9fed61f0b7d69ee3ba4d874bc6;hp=92d29ec8bd6c034995f3ebd6a7c737669d80a607;hpb=75870d7b55e36af526a0786fff94912989c73fd1;p=ppp.git diff --git a/pppd/eap.c b/pppd/eap.c index 92d29ec..6cb595f 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 @@ -67,8 +67,14 @@ #include "pathnames.h" #include "md5.h" #include "eap.h" +#ifdef USE_PEAP +#include "peap.h" +#endif /* USE_PEAP */ #ifdef USE_SRP +#ifdef HAVE_TIME_H +#include +#endif #include #include #include @@ -83,6 +89,13 @@ #include "eap-tls.h" #endif /* USE_EAPTLS */ +#ifdef CHAPMS +#include "chap_ms.h" +#include "chap-new.h" + +extern int chapms_strip_domain; +#endif /* CHAPMS */ + eap_state eap_states[NUM_PPP]; /* EAP state; one for each unit */ #ifdef USE_SRP static char *pn_secret = NULL; /* Pseudonym generating secret */ @@ -217,6 +230,10 @@ 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); + esp->es_server.digest = chap_find_digest(CHAP_MICROSOFT_V2); +#endif } /* @@ -545,7 +562,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; @@ -687,6 +704,9 @@ eap_figure_next_state(eap_state *esp, int status) } break; +#ifdef CHAPMS + case eapMSCHAPv2Chall: +#endif case eapMD5Chall: if (status != 0) { esp->es_server.ea_state = eapBadAuth; @@ -707,6 +727,76 @@ eap_figure_next_state(eap_state *esp, int status) #endif /* USE_EAPTLS */ } +#if 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 /* CHAPMS */ + /* * Format an EAP Request message and send it to the peer. Message * type depends on current state. (Server operation) @@ -792,6 +882,29 @@ eap_send_request(eap_state *esp) INCPTR(esp->es_server.ea_namelen, outp); break; +#ifdef 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 /* CHAPMS */ + #ifdef USE_EAPTLS case eapTlsStart: PUTCHAR(EAPT_TLS, outp); @@ -1485,6 +1598,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). */ @@ -1671,7 +1817,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 +1829,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: @@ -1727,7 +1873,7 @@ 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; } @@ -1966,6 +2112,136 @@ 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; + } + 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 /* CHAPMS */ +#ifdef USE_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 /* USE_PEAP */ default: info("EAP: unknown authentication type %d; Naking", typenum); @@ -1980,13 +2256,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 */ @@ -2010,14 +2286,18 @@ eap_response(eap_state *esp, u_char *inp, int id, int len) struct t_num A; SHA1_CTX ctxt; u_char dig[SHA_DIGESTSIZE]; - SHA1_CTX ctxt; - u_char dig[SHA_DIGESTSIZE]; #endif /* USE_SRP */ #ifdef USE_EAPTLS struct eaptls_session *ets; u_char flags; #endif /* USE_EAPTLS */ +#ifdef CHAPMS + u_char opcode; + int (*chap_verifier)(char *, char *, int, struct chap_digest_type *, + unsigned char *, unsigned char *, char *, int); + char response_message[256]; +#endif /* CHAPMS */ /* * Ignore responses if we're not open @@ -2160,6 +2440,19 @@ eap_response(eap_state *esp, u_char *inp, int id, int len) break; #endif /* USE_EAPTLS */ +#ifdef 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 /* CHAPMS */ + default: dbglog("EAP: peer requesting unknown Type %d", vallen); switch (esp->es_server.ea_state) { @@ -2241,6 +2534,111 @@ eap_response(eap_state *esp, u_char *inp, int id, int len) TIMEOUT(eap_rechallenge, esp, esp->es_rechallenge); break; +#ifdef CHAPMS + case EAPT_MSCHAPV2: + if (len < 1) { + error("EAP: received MSCHAPv2 with no data"); + eap_figure_next_state(esp, 1); + break; + } + 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); + strcpy(rhostname, tmp); + } + + 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 /* CHAPMS */ + #ifdef USE_SRP case EAPT_SRP: if (len < 1) { @@ -2397,6 +2795,10 @@ eap_success(eap_state *esp, u_char *inp, int id, int len) PRINTMSG(inp, len); } +#ifdef USE_PEAP + peap_finish(&esp->ea_peap); +#endif + esp->es_client.ea_state = eapOpen; auth_withpeer_success(esp->es_unit, PPP_EAP, 0); } @@ -2431,6 +2833,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 USE_PEAP + peap_finish(&esp->ea_peap); +#endif + auth_withpeer_fail(esp->es_unit, PPP_EAP); } @@ -2499,7 +2906,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 @@ -2512,6 +2921,9 @@ eap_printpkt(u_char *inp, int inlen, #ifdef USE_EAPTLS u_char flags; #endif /* USE_EAPTLS */ +#ifdef CHAPMS + u_char opcode; +#endif /* CHAPMS */ if (inlen < EAP_HEADERLEN) return (0); @@ -2576,6 +2988,61 @@ eap_printpkt(u_char *inp, int inlen, } break; +#ifdef 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 /* CHAPMS */ + #ifdef USE_EAPTLS case EAPT_TLS: if (len < 1) @@ -2594,6 +3061,7 @@ eap_printpkt(u_char *inp, int inlen, break; #endif /* USE_EAPTLS */ +#ifdef USE_SRP case EAPT_SRP: if (len < 3) goto truncated; @@ -2681,6 +3149,7 @@ eap_printpkt(u_char *inp, int inlen, break; } break; +#endif /* USE_SRP */ } break; @@ -2733,7 +3202,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 +3230,52 @@ eap_printpkt(u_char *inp, int inlen, } break; +#ifdef 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 /* CHAPMS */ + +#ifdef USE_SRP case EAPT_SRP: if (len < 1) goto truncated; @@ -2805,6 +3320,7 @@ eap_printpkt(u_char *inp, int inlen, break; } break; +#endif /* USE_SRP */ } break;