X-Git-Url: https://git.ozlabs.org/?a=blobdiff_plain;f=pppd%2Feap.c;h=342d91f0897d341c4ddf6132c507cac2f67b1d97;hb=d5009960b362f79a77755540780f63d3b13e9007;hp=b97d28552e076b5be5cefecc99e4e1a7514148fa;hpb=3a7593be1582b204214f1ff9bbce849e22a81876;p=ppp.git diff --git a/pppd/eap.c b/pppd/eap.c index b97d285..342d91f 100644 --- a/pppd/eap.c +++ b/pppd/eap.c @@ -48,12 +48,6 @@ * Implemented EAP-TLS authentication */ -#define RCSID "$Id: eap.c,v 1.4 2004/11/09 22:39:25 paulus Exp $" - -/* - * TODO: - */ - #include #include #include @@ -84,10 +78,12 @@ #ifdef USE_EAPTLS #include "eap-tls.h" #endif /* USE_EAPTLS */ + #ifdef CHAPMS -#include "magic.h" #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 */ @@ -123,13 +119,13 @@ static option_t eap_option_list[] = { /* * Protocol entry points. */ -static void eap_init __P((int unit)); -static void eap_input __P((int unit, u_char *inp, int inlen)); -static void eap_protrej __P((int unit)); -static void eap_lowerup __P((int unit)); -static void eap_lowerdown __P((int unit)); -static int eap_printpkt __P((u_char *inp, int inlen, - void (*)(void *arg, char *fmt, ...), void *arg)); +static void eap_init (int unit); +static void eap_input (int unit, u_char *inp, int inlen); +static void eap_protrej (int unit); +static void eap_lowerup (int unit); +static void eap_lowerdown (int unit); +static int eap_printpkt (u_char *inp, int inlen, + void (*)(void *arg, char *fmt, ...), void *arg); struct protent eap_protent = { PPP_EAP, /* protocol number */ @@ -151,6 +147,7 @@ struct protent eap_protent = { NULL /* say whether to bring up link for this pkt */ }; +#ifdef USE_SRP /* * A well-known 2048 bit modulus. */ @@ -188,16 +185,16 @@ static const u_char wkmodulus[] = { 0x9B, 0x65, 0xE3, 0x72, 0xFC, 0xD6, 0x8E, 0xF2, 0x0F, 0xA7, 0x11, 0x1F, 0x9E, 0x4A, 0xFF, 0x73 }; +#endif /* USE_SRP */ /* Local forward declarations. */ -static void eap_server_timeout __P((void *arg)); +static void eap_server_timeout (void *arg); /* * Convert EAP state code to printable string for debug. */ static const char * -eap_state_name(esc) -enum eap_state_code esc; +eap_state_name(enum eap_state_code esc) { static const char *state_names[] = { EAP_STATES }; @@ -209,8 +206,7 @@ enum eap_state_code esc; * called once by main() during start-up. */ static void -eap_init(unit) -int unit; +eap_init(int unit) { eap_state *esp = &eap_states[unit]; @@ -224,6 +220,10 @@ 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 } /* @@ -231,8 +231,7 @@ int unit; * Request messages. */ static void -eap_client_timeout(arg) -void *arg; +eap_client_timeout(void *arg) { eap_state *esp = (eap_state *) arg; @@ -251,9 +250,7 @@ void *arg; * after eap_lowerup. */ void -eap_authwithpeer(unit, localname) -int unit; -char *localname; +eap_authwithpeer(int unit, char *localname) { eap_state *esp = &eap_states[unit]; @@ -277,8 +274,7 @@ char *localname; * (Server operation) */ static void -eap_send_failure(esp) -eap_state *esp; +eap_send_failure(eap_state *esp) { u_char *outp; @@ -302,8 +298,7 @@ eap_state *esp; * (Server operation) */ static void -eap_send_success(esp) -eap_state *esp; +eap_send_success(eap_state *esp) { u_char *outp; @@ -357,11 +352,7 @@ struct b64state { }; static int -b64enc(bs, inp, inlen, outp) -struct b64state *bs; -u_char *inp; -int inlen; -u_char *outp; +b64enc(struct b64state *bs, u_char *inp, int inlen, u_char *outp) { int outlen = 0; @@ -383,9 +374,7 @@ u_char *outp; } static int -b64flush(bs, outp) -struct b64state *bs; -u_char *outp; +b64flush(struct b64state *bs, u_char *outp) { int outlen = 0; @@ -405,11 +394,7 @@ u_char *outp; } static int -b64dec(bs, inp, inlen, outp) -struct b64state *bs; -u_char *inp; -int inlen; -u_char *outp; +b64dec(struct b64state *bs, u_char *inp, int inlen, u_char *outp) { int outlen = 0; char *cp; @@ -437,9 +422,7 @@ u_char *outp; * 0 for success and non-zero for failure. */ static void -eap_figure_next_state(esp, status) -eap_state *esp; -int status; +eap_figure_next_state(eap_state *esp, int status) { #ifdef USE_SRP unsigned char secbuf[MAXWORDLEN], clear[8], *sp, *dp; @@ -735,92 +718,6 @@ int status; } #if CHAPMS -static int -eap_chapms2_verify_response(int id, char *name, - unsigned char *secret, int secret_len, - unsigned char *challenge, unsigned char *response, - char *message, int message_space) -{ - unsigned char md[MS_CHAP2_RESPONSE_LEN]; - char saresponse[MS_AUTH_RESPONSE_LENGTH+1]; - int challenge_len, response_len; - - challenge_len = *challenge++; /* skip length, is 16 */ - response_len = *response++; - if (response_len != MS_CHAP2_RESPONSE_LEN) - goto bad; /* not even the right length */ - - /* Generate the expected response and our mutual auth. */ - ChapMS2(challenge, &response[MS_CHAP2_PEER_CHALLENGE], name, - (char *)secret, secret_len, md, - (unsigned char *)saresponse, MS_CHAP2_AUTHENTICATOR); - - /* compare MDs and send the appropriate status */ - /* - * Per RFC 2759, success message must be formatted as - * "S= M=" - * where - * is the Authenticator Response (mutual auth) - * is a text message - * - * However, some versions of Windows (win98 tested) do not know - * about the M= part (required per RFC 2759) and flag - * it as an error (reported incorrectly as an encryption error - * to the user). Since the RFC requires it, and it can be - * useful information, we supply it if the peer is a conforming - * system. Luckily (?), win98 sets the Flags field to 0x04 - * (contrary to RFC requirements) so we can use that to - * distinguish between conforming and non-conforming systems. - * - * Special thanks to Alex Swiridov for - * help debugging this. - */ - if (memcmp(&md[MS_CHAP2_NTRESP], &response[MS_CHAP2_NTRESP], - MS_CHAP2_NTRESP_LEN) == 0) { - if (response[MS_CHAP2_FLAGS]) - slprintf(message, message_space, "S=%s", saresponse); - else - slprintf(message, message_space, "S=%s M=%s", - saresponse, "Access granted"); - return 1; - } - - bad: - /* - * Failure message must be formatted as - * "E=e R=r C=c V=v M=m" - * where - * e = error code (we use 691, ERROR_AUTHENTICATION_FAILURE) - * r = retry (we use 1, ok to retry) - * c = challenge to use for next response, we reuse previous - * v = Change Password version supported, we use 0 - * m = text message - * - * The M=m part is only for MS-CHAPv2. Neither win2k nor - * win98 (others untested) display the message to the user anyway. - * They also both ignore the E=e code. - * - * Note that it's safe to reuse the same challenge as we don't - * actually accept another response based on the error message - * (and no clients try to resend a response anyway). - * - * Basically, this whole bit is useless code, even the small - * implementation here is only because of overspecification. - */ - slprintf(message, message_space, "E=691 R=1 C=%0.*B V=0 M=%s", - challenge_len, challenge, "Access denied"); - return 0; -} - -static struct chap_digest_type eap_chapms2_digest = { - CHAP_MICROSOFT_V2, /* code */ - NULL, /* chapms2_generate_challenge, */ - eap_chapms2_verify_response, - NULL, /* chapms2_make_response, */ - NULL, /* chapms2_check_success, */ - NULL, /* chapms_handle_failure, */ -}; - /* * eap_chap_verify_response - check whether the peer's response matches * what we think it should be. Returns 1 if it does (authentication @@ -895,8 +792,7 @@ eap_chapms2_send_request(eap_state *esp, u_char id, * type depends on current state. (Server operation) */ static void -eap_send_request(esp) -eap_state *esp; +eap_send_request(eap_state *esp) { u_char *outp; u_char *lenloc; @@ -978,10 +874,9 @@ eap_state *esp; #ifdef CHAPMS case eapMSCHAPv2Chall: - challen = 0x10; + esp->es_server.digest->generate_challenge(esp->es_challenge); + challen = esp->es_challenge[0]; esp->es_challen = challen; - esp->es_challenge[0] = challen; - random_bytes(&esp->es_challenge[1], challen); PUTCHAR(EAPT_MSCHAPV2, outp); PUTCHAR(CHAP_CHALLENGE, outp); @@ -1177,9 +1072,7 @@ eap_state *esp; * after eap_lowerup. */ void -eap_authpeer(unit, localname) -int unit; -char *localname; +eap_authpeer(int unit, char *localname) { eap_state *esp = &eap_states[unit]; @@ -1207,8 +1100,7 @@ char *localname; * expired. */ static void -eap_server_timeout(arg) -void *arg; +eap_server_timeout(void *arg) { #ifdef USE_EAPTLS u_char *outp; @@ -1271,8 +1163,7 @@ void *arg; * will restart the timer. If it fails, then the link is dropped. */ static void -eap_rechallenge(arg) -void *arg; +eap_rechallenge(void *arg) { eap_state *esp = (eap_state *)arg; @@ -1288,8 +1179,7 @@ void *arg; } static void -srp_lwrechallenge(arg) -void *arg; +srp_lwrechallenge(void *arg) { eap_state *esp = (eap_state *)arg; @@ -1312,8 +1202,7 @@ void *arg; * thing. */ static void -eap_lowerup(unit) -int unit; +eap_lowerup(int unit) { eap_state *esp = &eap_states[unit]; @@ -1336,8 +1225,7 @@ int unit; * Cancel all timeouts and return to initial state. */ static void -eap_lowerdown(unit) -int unit; +eap_lowerdown(int unit) { eap_state *esp = &eap_states[unit]; @@ -1371,8 +1259,7 @@ int unit; * failure. */ static void -eap_protrej(unit) -int unit; +eap_protrej(int unit) { eap_state *esp = &eap_states[unit]; @@ -1391,12 +1278,8 @@ int unit; * Format and send a regular EAP Response message. */ static void -eap_send_response(esp, id, typenum, str, lenstr) -eap_state *esp; -u_char id; -u_char typenum; -u_char *str; -int lenstr; +eap_send_response(eap_state *esp, u_char id, u_char typenum, + u_char *str, int lenstr) { u_char *outp; int msglen; @@ -1422,12 +1305,8 @@ int lenstr; * Format and send an MD5-Challenge EAP Response message. */ static void -eap_chap_response(esp, id, hash, name, namelen) -eap_state *esp; -u_char id; -u_char *hash; -char *name; -int namelen; +eap_chap_response(eap_state *esp, u_char id, u_char *hash, + char *name, int namelen) { u_char *outp; int msglen; @@ -1458,12 +1337,8 @@ int namelen; * Format and send a SRP EAP Response message. */ static void -eap_srp_response(esp, id, subtypenum, str, lenstr) -eap_state *esp; -u_char id; -u_char subtypenum; -u_char *str; -int lenstr; +eap_srp_response(eap_state *esp, u_char id, u_char subtypenum, + u_char *str, int lenstr) { u_char *outp; int msglen; @@ -1490,11 +1365,7 @@ int lenstr; * Format and send a SRP EAP Client Validator Response message. */ static void -eap_srpval_response(esp, id, flags, str) -eap_state *esp; -u_char id; -u_int32_t flags; -u_char *str; +eap_srpval_response(eap_state *esp, u_char id, u_int32_t flags, u_char *str) { u_char *outp; int msglen; @@ -1523,9 +1394,7 @@ u_char *str; * Send an EAP-TLS response message with tls data */ static void -eap_tls_response(esp, id) -eap_state *esp; -u_char id; +eap_tls_response(eap_state *esp, u_char id) { u_char *outp; int outlen; @@ -1562,9 +1431,7 @@ u_char id; * Send an EAP-TLS ack */ static void -eap_tls_sendack(esp, id) -eap_state *esp; -u_char id; +eap_tls_sendack(eap_state *esp, u_char id) { u_char *outp; int outlen; @@ -1592,10 +1459,7 @@ u_char id; #endif /* USE_EAPTLS */ static void -eap_send_nak(esp, id, type) -eap_state *esp; -u_char id; -u_char type; +eap_send_nak(eap_state *esp, u_char id, u_char type) { u_char *outp; int msglen; @@ -1617,7 +1481,7 @@ u_char type; #ifdef USE_SRP static char * -name_of_pn_file() +name_of_pn_file(void) { char *user, *path, *file; struct passwd *pw; @@ -1643,8 +1507,7 @@ name_of_pn_file() } static int -open_pn_file(modebits) -mode_t modebits; +open_pn_file(mode_t modebits) { char *path; int fd, err; @@ -1659,7 +1522,7 @@ mode_t modebits; } static void -remove_pn_file() +remove_pn_file(void) { char *path; @@ -1670,10 +1533,7 @@ remove_pn_file() } static void -write_pseudonym(esp, inp, len, id) -eap_state *esp; -u_char *inp; -int len, id; +write_pseudonym(eap_state *esp, u_char *inp, int len, int id) { u_char val; u_char *datp, *digp; @@ -1728,15 +1588,44 @@ int len, 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). */ static void -eap_request(esp, inp, id, len) -eap_state *esp; -u_char *inp; -int id; -int len; +eap_request(eap_state *esp, u_char *inp, int id, int len) { u_char typenum; u_char vallen; @@ -1821,7 +1710,7 @@ int len; esp->es_usedpseudo = 2; } #endif /* USE_SRP */ - eap_send_response(esp, id, typenum, esp->es_client.ea_name, + eap_send_response(esp, id, typenum, (u_char *)esp->es_client.ea_name, esp->es_client.ea_namelen); break; @@ -1918,7 +1807,7 @@ 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; } @@ -1930,7 +1819,7 @@ 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: @@ -1974,7 +1863,7 @@ 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; } @@ -2213,6 +2102,113 @@ 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); @@ -2227,13 +2223,13 @@ 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 */ @@ -2243,11 +2239,7 @@ client_failure: * eap_response - Receive EAP Response message (server mode). */ static void -eap_response(esp, inp, id, len) -eap_state *esp; -u_char *inp; -int id; -int len; +eap_response(eap_state *esp, u_char *inp, int id, int len) { u_char typenum; u_char vallen; @@ -2420,6 +2412,12 @@ int len; #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 */ @@ -2549,6 +2547,14 @@ int len; (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 @@ -2558,7 +2564,7 @@ int len; if ((*chap_verifier)(rhostname, esp->es_server.ea_name, id, - &eap_chapms2_digest, + esp->es_server.digest, esp->es_challenge, inp - 1, response_message, @@ -2726,11 +2732,7 @@ int len; * eap_success - Receive EAP Success message (client mode). */ static void -eap_success(esp, inp, id, len) -eap_state *esp; -u_char *inp; -int id; -int len; +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 @@ -2770,11 +2772,7 @@ int len; * eap_failure - Receive EAP Failure message (client mode). */ static void -eap_failure(esp, inp, id, len) -eap_state *esp; -u_char *inp; -int id; -int len; +eap_failure(eap_state *esp, u_char *inp, int id, int len) { /* * Ignore failure messages if we're not open @@ -2807,10 +2805,7 @@ int len; * eap_input - Handle received EAP message. */ static void -eap_input(unit, inp, inlen) -int unit; -u_char *inp; -int inlen; +eap_input(int unit, u_char *inp, int inlen) { eap_state *esp = &eap_states[unit]; u_char code, id; @@ -2877,11 +2872,8 @@ static char *eap_typenames[] = { }; static int -eap_printpkt(inp, inlen, printer, arg) -u_char *inp; -int inlen; -void (*printer) __P((void *, char *, ...)); -void *arg; +eap_printpkt(u_char *inp, int inlen, + void (*printer) (void *, char *, ...), void *arg) { int code, id, len, rtype, vallen; u_char *pstart; @@ -3168,7 +3160,7 @@ void *arg; 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;