lwip/src/netif/ppp/eap.c
Sylvain Rochet 5e73068e09 PPP, CORE, Removed (*datainput) from struct protent if not used
Data input is only used by CCP and ECP, which are not supported at this time,
remove this entry from struct protent to save some flash.
2015-02-18 23:14:29 +01:00

2457 lines
61 KiB
C

/*
* eap.c - Extensible Authentication Protocol for PPP (RFC 2284)
*
* Copyright (c) 2001 by Sun Microsystems, Inc.
* All rights reserved.
*
* Non-exclusive rights to redistribute, modify, translate, and use
* this software in source and binary forms, in whole or in part, is
* hereby granted, provided that the above copyright notice is
* duplicated in any source form, and that neither the name of the
* copyright holder nor the author is used to endorse or promote
* products derived from this software.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
* WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*
* Original version by James Carlson
*
* This implementation of EAP supports MD5-Challenge and SRP-SHA1
* authentication styles. Note that support of MD5-Challenge is a
* requirement of RFC 2284, and that it's essentially just a
* reimplementation of regular RFC 1994 CHAP using EAP messages.
*
* As an authenticator ("server"), there are multiple phases for each
* style. In the first phase of each style, the unauthenticated peer
* name is queried using the EAP Identity request type. If the
* "remotename" option is used, then this phase is skipped, because
* the peer's name is presumed to be known.
*
* For MD5-Challenge, there are two phases, and the second phase
* consists of sending the challenge itself and handling the
* associated response.
*
* For SRP-SHA1, there are four phases. The second sends 's', 'N',
* and 'g'. The reply contains 'A'. The third sends 'B', and the
* reply contains 'M1'. The forth sends the 'M2' value.
*
* As an authenticatee ("client"), there's just a single phase --
* responding to the queries generated by the peer. EAP is an
* authenticator-driven protocol.
*
* Based on draft-ietf-pppext-eap-srp-03.txt.
*/
#include "lwip/opt.h"
#if PPP_SUPPORT && EAP_SUPPORT /* don't build if not configured for use in lwipopts.h */
#include "netif/ppp/ppp_impl.h"
#if LWIP_INCLUDED_POLARSSL_MD5
#include "netif/ppp/polarssl/md5.h"
#else
#include "polarssl/md5.h"
#endif
#include "netif/ppp/eap.h"
#ifdef USE_SRP
#include <t_pwd.h>
#include <t_server.h>
#include <t_client.h>
#include "netif/ppp/pppcrypt.h"
#endif /* USE_SRP */
#ifndef SHA_DIGESTSIZE
#define SHA_DIGESTSIZE 20
#endif
#ifdef USE_SRP
static char *pn_secret = NULL; /* Pseudonym generating secret */
#endif
#if PPP_OPTIONS
/*
* Command-line options.
*/
static option_t 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,
"Set max number of EAP Requests sent (server)" },
{ "eap-timeout", o_int, &eap_states[0].es_client.ea_timeout,
"Set time limit for peer EAP authentication" },
{ "eap-max-rreq", o_int, &eap_states[0].es_client.ea_maxrequests,
"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
{ "srp-interval", o_int, &eap_states[0].es_lwrechallenge,
"Set interval for SRP lightweight rechallenge" },
{ "srp-pn-secret", o_string, &pn_secret,
"Long term pseudonym generation secret" },
{ "srp-use-pseudonym", o_bool, &eap_states[0].es_usepseudo,
"Use pseudonym if offered one by server", 1 },
#endif
{ NULL }
};
#endif /* PPP_OPTIONS */
/*
* Protocol entry points.
*/
static void eap_init(ppp_pcb *pcb);
static void eap_input(ppp_pcb *pcb, u_char *inp, int inlen);
static void eap_protrej(ppp_pcb *pcb);
static void eap_lowerup(ppp_pcb *pcb);
static void eap_lowerdown(ppp_pcb *pcb);
#if PRINTPKT_SUPPORT
static int eap_printpkt(u_char *inp, int inlen,
void (*)(void *arg, const char *fmt, ...), void *arg);
#endif /* PRINTPKT_SUPPORT */
const struct protent eap_protent = {
PPP_EAP, /* protocol number */
eap_init, /* initialization procedure */
eap_input, /* process a received packet */
eap_protrej, /* process a received protocol-reject */
eap_lowerup, /* lower layer has gone up */
eap_lowerdown, /* lower layer has gone down */
NULL, /* open the protocol */
NULL, /* close the protocol */
#if PRINTPKT_SUPPORT
eap_printpkt, /* print a packet in readable form */
#endif /* PRINTPKT_SUPPORT */
#if PPP_DATAINPUT
NULL, /* process a received data packet */
#endif /* PPP_DATAINPUT */
#if PRINTPKT_SUPPORT
"EAP", /* text name of protocol */
NULL, /* text name of corresponding data protocol */
#endif /* PRINTPKT_SUPPORT */
#if PPP_OPTIONS
eap_option_list, /* list of command-line options */
NULL, /* check requested options; assign defaults */
#endif /* PPP_OPTIONS */
#if DEMAND_SUPPORT
NULL, /* configure interface for demand-dial */
NULL /* say whether to bring up link for this pkt */
#endif /* DEMAND_SUPPORT */
};
#ifdef USE_SRP
/*
* A well-known 2048 bit modulus.
*/
static const u_char wkmodulus[] = {
0xAC, 0x6B, 0xDB, 0x41, 0x32, 0x4A, 0x9A, 0x9B,
0xF1, 0x66, 0xDE, 0x5E, 0x13, 0x89, 0x58, 0x2F,
0xAF, 0x72, 0xB6, 0x65, 0x19, 0x87, 0xEE, 0x07,
0xFC, 0x31, 0x92, 0x94, 0x3D, 0xB5, 0x60, 0x50,
0xA3, 0x73, 0x29, 0xCB, 0xB4, 0xA0, 0x99, 0xED,
0x81, 0x93, 0xE0, 0x75, 0x77, 0x67, 0xA1, 0x3D,
0xD5, 0x23, 0x12, 0xAB, 0x4B, 0x03, 0x31, 0x0D,
0xCD, 0x7F, 0x48, 0xA9, 0xDA, 0x04, 0xFD, 0x50,
0xE8, 0x08, 0x39, 0x69, 0xED, 0xB7, 0x67, 0xB0,
0xCF, 0x60, 0x95, 0x17, 0x9A, 0x16, 0x3A, 0xB3,
0x66, 0x1A, 0x05, 0xFB, 0xD5, 0xFA, 0xAA, 0xE8,
0x29, 0x18, 0xA9, 0x96, 0x2F, 0x0B, 0x93, 0xB8,
0x55, 0xF9, 0x79, 0x93, 0xEC, 0x97, 0x5E, 0xEA,
0xA8, 0x0D, 0x74, 0x0A, 0xDB, 0xF4, 0xFF, 0x74,
0x73, 0x59, 0xD0, 0x41, 0xD5, 0xC3, 0x3E, 0xA7,
0x1D, 0x28, 0x1E, 0x44, 0x6B, 0x14, 0x77, 0x3B,
0xCA, 0x97, 0xB4, 0x3A, 0x23, 0xFB, 0x80, 0x16,
0x76, 0xBD, 0x20, 0x7A, 0x43, 0x6C, 0x64, 0x81,
0xF1, 0xD2, 0xB9, 0x07, 0x87, 0x17, 0x46, 0x1A,
0x5B, 0x9D, 0x32, 0xE6, 0x88, 0xF8, 0x77, 0x48,
0x54, 0x45, 0x23, 0xB5, 0x24, 0xB0, 0xD5, 0x7D,
0x5E, 0xA7, 0x7A, 0x27, 0x75, 0xD2, 0xEC, 0xFA,
0x03, 0x2C, 0xFB, 0xDB, 0xF5, 0x2F, 0xB3, 0x78,
0x61, 0x60, 0x27, 0x90, 0x04, 0xE5, 0x7A, 0xE6,
0xAF, 0x87, 0x4E, 0x73, 0x03, 0xCE, 0x53, 0x29,
0x9C, 0xCC, 0x04, 0x1C, 0x7B, 0xC3, 0x08, 0xD8,
0x2A, 0x56, 0x98, 0xF3, 0xA8, 0xD0, 0xC3, 0x82,
0x71, 0xAE, 0x35, 0xF8, 0xE9, 0xDB, 0xFB, 0xB6,
0x94, 0xB5, 0xC8, 0x03, 0xD8, 0x9F, 0x7A, 0xE4,
0x35, 0xDE, 0x23, 0x6D, 0x52, 0x5F, 0x54, 0x75,
0x9B, 0x65, 0xE3, 0x72, 0xFC, 0xD6, 0x8E, 0xF2,
0x0F, 0xA7, 0x11, 0x1F, 0x9E, 0x4A, 0xFF, 0x73
};
#endif
#if PPP_SERVER
/* Local forward declarations. */
static void eap_server_timeout(void *arg);
#endif /* PPP_SERVER */
/*
* Convert EAP state code to printable string for debug.
*/
static const char * eap_state_name(enum eap_state_code esc)
{
static const char *state_names[] = { EAP_STATES };
return (state_names[(int)esc]);
}
/*
* eap_init - Initialize state for an EAP user. This is currently
* called once by main() during start-up.
*/
static void eap_init(ppp_pcb *pcb) {
BZERO(&pcb->eap, sizeof(eap_state));
#if PPP_SERVER
pcb->eap.es_server.ea_id = (u_char)(drand48() * 0x100); /* FIXME: use magic.c random function */
#endif /* PPP_SERVER */
}
/*
* eap_client_timeout - Give up waiting for the peer to send any
* Request messages.
*/
static void eap_client_timeout(void *arg) {
ppp_pcb *pcb = (ppp_pcb*)arg;
if (!eap_client_active(pcb))
return;
ppp_error("EAP: timeout waiting for Request from peer");
auth_withpeer_fail(pcb, PPP_EAP);
pcb->eap.es_client.ea_state = eapBadAuth;
}
/*
* eap_authwithpeer - Authenticate to our peer (behave as client).
*
* Start client state and wait for requests. This is called only
* after eap_lowerup.
*/
void eap_authwithpeer(ppp_pcb *pcb, const char *localname) {
if(NULL == localname)
return;
/* Save the peer name we're given */
pcb->eap.es_client.ea_name = localname;
pcb->eap.es_client.ea_namelen = strlen(localname);
pcb->eap.es_client.ea_state = eapListen;
/*
* Start a timer so that if the other end just goes
* silent, we don't sit here waiting forever.
*/
if (pcb->settings.eap_req_time > 0)
TIMEOUT(eap_client_timeout, pcb,
pcb->settings.eap_req_time);
}
#if PPP_SERVER
/*
* Format a standard EAP Failure message and send it to the peer.
* (Server operation)
*/
static void eap_send_failure(ppp_pcb *pcb) {
struct pbuf *p;
u_char *outp;
p = pbuf_alloc(PBUF_RAW, (u16_t)(PPP_HDRLEN + EAP_HEADERLEN), PPP_CTRL_PBUF_TYPE);
if(NULL == p)
return;
if(p->tot_len != p->len) {
pbuf_free(p);
return;
}
outp = (u_char*)p->payload;
MAKEHEADER(outp, PPP_EAP);
PUTCHAR(EAP_FAILURE, outp);
pcb->eap.es_server.ea_id++;
PUTCHAR(pcb->eap.es_server.ea_id, outp);
PUTSHORT(EAP_HEADERLEN, outp);
ppp_write(pcb, p);
pcb->eap.es_server.ea_state = eapBadAuth;
auth_peer_fail(pcb, PPP_EAP);
}
/*
* Format a standard EAP Success message and send it to the peer.
* (Server operation)
*/
static void eap_send_success(ppp_pcb *pcb) {
struct pbuf *p;
u_char *outp;
p = pbuf_alloc(PBUF_RAW, (u16_t)(PPP_HDRLEN + EAP_HEADERLEN), PPP_CTRL_PBUF_TYPE);
if(NULL == p)
return;
if(p->tot_len != p->len) {
pbuf_free(p);
return;
}
outp = (u_char*)p->payload;
MAKEHEADER(outp, PPP_EAP);
PUTCHAR(EAP_SUCCESS, outp);
pcb->eap.es_server.ea_id++;
PUTCHAR(pcb->eap.es_server.ea_id, outp);
PUTSHORT(EAP_HEADERLEN, outp);
ppp_write(pcb, p);
auth_peer_success(pcb, PPP_EAP, 0,
pcb->eap.es_server.ea_peer, pcb->eap.es_server.ea_peerlen);
}
#endif /* PPP_SERVER */
#ifdef USE_SRP
/*
* Set DES key according to pseudonym-generating secret and current
* date.
*/
static bool
pncrypt_setkey(int timeoffs)
{
struct tm *tp;
char tbuf[9];
SHA1_CTX ctxt;
u_char dig[SHA_DIGESTSIZE];
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);
/* FIXME: if we want to do SRP, we need to find a way to pass the PolarSSL des_context instead of using static memory */
return (DesSetkey(dig));
}
static char base64[] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
struct b64state {
u32_t bs_bits;
int bs_offs;
};
static int
b64enc(bs, inp, inlen, outp)
struct b64state *bs;
u_char *inp;
int inlen;
u_char *outp;
{
int outlen = 0;
while (inlen > 0) {
bs->bs_bits = (bs->bs_bits << 8) | *inp++;
inlen--;
bs->bs_offs += 8;
if (bs->bs_offs >= 24) {
*outp++ = base64[(bs->bs_bits >> 18) & 0x3F];
*outp++ = base64[(bs->bs_bits >> 12) & 0x3F];
*outp++ = base64[(bs->bs_bits >> 6) & 0x3F];
*outp++ = base64[bs->bs_bits & 0x3F];
outlen += 4;
bs->bs_offs = 0;
bs->bs_bits = 0;
}
}
return (outlen);
}
static int
b64flush(bs, outp)
struct b64state *bs;
u_char *outp;
{
int outlen = 0;
if (bs->bs_offs == 8) {
*outp++ = base64[(bs->bs_bits >> 2) & 0x3F];
*outp++ = base64[(bs->bs_bits << 4) & 0x3F];
outlen = 2;
} else if (bs->bs_offs == 16) {
*outp++ = base64[(bs->bs_bits >> 10) & 0x3F];
*outp++ = base64[(bs->bs_bits >> 4) & 0x3F];
*outp++ = base64[(bs->bs_bits << 2) & 0x3F];
outlen = 3;
}
bs->bs_offs = 0;
bs->bs_bits = 0;
return (outlen);
}
static int
b64dec(bs, inp, inlen, outp)
struct b64state *bs;
u_char *inp;
int inlen;
u_char *outp;
{
int outlen = 0;
char *cp;
while (inlen > 0) {
if ((cp = strchr(base64, *inp++)) == NULL)
break;
bs->bs_bits = (bs->bs_bits << 6) | (cp - base64);
inlen--;
bs->bs_offs += 6;
if (bs->bs_offs >= 8) {
*outp++ = bs->bs_bits >> (bs->bs_offs - 8);
outlen++;
bs->bs_offs -= 8;
}
}
return (outlen);
}
#endif /* USE_SRP */
#if PPP_SERVER
/*
* Assume that current waiting server state is complete and figure
* next state to use based on available authentication data. 'status'
* indicates if there was an error in handling the last query. It is
* 0 for success and non-zero for failure.
*/
static void eap_figure_next_state(ppp_pcb *pcb, int status) {
#ifdef USE_SRP
unsigned char secbuf[MAXWORDLEN], clear[8], *sp, *dp;
struct t_pw tpw;
struct t_confent *tce, mytce;
char *cp, *cp2;
struct t_server *ts;
int id, i, plen, toffs;
u_char vals[2];
struct b64state bs;
#endif /* USE_SRP */
pcb->settings.eap_timeout_time = pcb->eap.es_savedtime;
switch (pcb->eap.es_server.ea_state) {
case eapBadAuth:
return;
case eapIdentify:
#ifdef USE_SRP
/* Discard any previous session. */
ts = (struct t_server *)pcb->eap.es_server.ea_session;
if (ts != NULL) {
t_serverclose(ts);
pcb->eap.es_server.ea_session = NULL;
pcb->eap.es_server.ea_skey = NULL;
}
#endif /* USE_SRP */
if (status != 0) {
pcb->eap.es_server.ea_state = eapBadAuth;
break;
}
#ifdef USE_SRP
/* If we've got a pseudonym, try to decode to real name. */
if (pcb->eap.es_server.ea_peerlen > SRP_PSEUDO_LEN &&
strncmp(pcb->eap.es_server.ea_peer, SRP_PSEUDO_ID,
SRP_PSEUDO_LEN) == 0 &&
(pcb->eap.es_server.ea_peerlen - SRP_PSEUDO_LEN) * 3 / 4 <
sizeof (secbuf)) {
BZERO(&bs, sizeof (bs));
plen = b64dec(&bs,
pcb->eap.es_server.ea_peer + SRP_PSEUDO_LEN,
pcb->eap.es_server.ea_peerlen - SRP_PSEUDO_LEN,
secbuf);
toffs = 0;
for (i = 0; i < 5; i++) {
pncrypt_setkey(toffs);
toffs -= 86400;
/* FIXME: if we want to do SRP, we need to find a way to pass the PolarSSL des_context instead of using static memory */
if (!DesDecrypt(secbuf, clear)) {
ppp_dbglog("no DES here; cannot decode "
"pseudonym");
return;
}
id = *(unsigned char *)clear;
if (id + 1 <= plen && id + 9 > plen)
break;
}
if (plen % 8 == 0 && i < 5) {
/*
* Note that this is always shorter than the
* original stored string, so there's no need
* to realloc.
*/
if ((i = plen = *(unsigned char *)clear) > 7)
i = 7;
pcb->eap.es_server.ea_peerlen = plen;
dp = (unsigned char *)pcb->eap.es_server.ea_peer;
MEMCPY(dp, clear + 1, i);
plen -= i;
dp += i;
sp = secbuf + 8;
while (plen > 0) {
/* FIXME: if we want to do SRP, we need to find a way to pass the PolarSSL des_context instead of using static memory */
(void) DesDecrypt(sp, dp);
sp += 8;
dp += 8;
plen -= 8;
}
pcb->eap.es_server.ea_peer[
pcb->eap.es_server.ea_peerlen] = '\0';
ppp_dbglog("decoded pseudonym to \"%.*q\"",
pcb->eap.es_server.ea_peerlen,
pcb->eap.es_server.ea_peer);
} else {
ppp_dbglog("failed to decode real name");
/* Stay in eapIdentfy state; requery */
break;
}
}
/* Look up user in secrets database. */
if (get_srp_secret(pcb->eap.es_unit, pcb->eap.es_server.ea_peer,
pcb->eap.es_server.ea_name, (char *)secbuf, 1) != 0) {
/* Set up default in case SRP entry is bad */
pcb->eap.es_server.ea_state = eapMD5Chall;
/* Get t_confent based on index in srp-secrets */
id = strtol((char *)secbuf, &cp, 10);
if (*cp++ != ':' || id < 0)
break;
if (id == 0) {
mytce.index = 0;
mytce.modulus.data = (u_char *)wkmodulus;
mytce.modulus.len = sizeof (wkmodulus);
mytce.generator.data = (u_char *)"\002";
mytce.generator.len = 1;
tce = &mytce;
} else if ((tce = gettcid(id)) != NULL) {
/*
* Client will have to verify this modulus/
* generator combination, and that will take
* a while. Lengthen the timeout here.
*/
if (pcb->settings.eap_timeout_time > 0 &&
pcb->settings.eap_timeout_time < 30)
pcb->settings.eap_timeout_time = 30;
} else {
break;
}
if ((cp2 = strchr(cp, ':')) == NULL)
break;
*cp2++ = '\0';
tpw.pebuf.name = pcb->eap.es_server.ea_peer;
tpw.pebuf.password.len = t_fromb64((char *)tpw.pwbuf,
cp);
tpw.pebuf.password.data = tpw.pwbuf;
tpw.pebuf.salt.len = t_fromb64((char *)tpw.saltbuf,
cp2);
tpw.pebuf.salt.data = tpw.saltbuf;
if ((ts = t_serveropenraw(&tpw.pebuf, tce)) == NULL)
break;
pcb->eap.es_server.ea_session = (void *)ts;
pcb->eap.es_server.ea_state = eapSRP1;
vals[0] = pcb->eap.es_server.ea_id + 1;
vals[1] = EAPT_SRP;
t_serveraddexdata(ts, vals, 2);
/* Generate B; must call before t_servergetkey() */
t_servergenexp(ts);
break;
}
#endif /* USE_SRP */
pcb->eap.es_server.ea_state = eapMD5Chall;
break;
case eapSRP1:
#ifdef USE_SRP
ts = (struct t_server *)pcb->eap.es_server.ea_session;
if (ts != NULL && status != 0) {
t_serverclose(ts);
pcb->eap.es_server.ea_session = NULL;
pcb->eap.es_server.ea_skey = NULL;
}
#endif /* USE_SRP */
if (status == 1) {
pcb->eap.es_server.ea_state = eapMD5Chall;
} else if (status != 0 || pcb->eap.es_server.ea_session == NULL) {
pcb->eap.es_server.ea_state = eapBadAuth;
} else {
pcb->eap.es_server.ea_state = eapSRP2;
}
break;
case eapSRP2:
#ifdef USE_SRP
ts = (struct t_server *)pcb->eap.es_server.ea_session;
if (ts != NULL && status != 0) {
t_serverclose(ts);
pcb->eap.es_server.ea_session = NULL;
pcb->eap.es_server.ea_skey = NULL;
}
#endif /* USE_SRP */
if (status != 0 || pcb->eap.es_server.ea_session == NULL) {
pcb->eap.es_server.ea_state = eapBadAuth;
} else {
pcb->eap.es_server.ea_state = eapSRP3;
}
break;
case eapSRP3:
case eapSRP4:
#ifdef USE_SRP
ts = (struct t_server *)pcb->eap.es_server.ea_session;
if (ts != NULL && status != 0) {
t_serverclose(ts);
pcb->eap.es_server.ea_session = NULL;
pcb->eap.es_server.ea_skey = NULL;
}
#endif /* USE_SRP */
if (status != 0 || pcb->eap.es_server.ea_session == NULL) {
pcb->eap.es_server.ea_state = eapBadAuth;
} else {
pcb->eap.es_server.ea_state = eapOpen;
}
break;
case eapMD5Chall:
if (status != 0) {
pcb->eap.es_server.ea_state = eapBadAuth;
} else {
pcb->eap.es_server.ea_state = eapOpen;
}
break;
default:
pcb->eap.es_server.ea_state = eapBadAuth;
break;
}
if (pcb->eap.es_server.ea_state == eapBadAuth)
eap_send_failure(pcb);
}
/*
* Format an EAP Request message and send it to the peer. Message
* type depends on current state. (Server operation)
*/
static void eap_send_request(ppp_pcb *pcb) {
struct pbuf *p;
u_char *outp;
u_char *lenloc;
u_char *ptr;
int outlen;
int challen;
const char *str;
#ifdef USE_SRP
struct t_server *ts;
u_char clear[8], cipher[8], dig[SHA_DIGESTSIZE], *optr, *cp;
int i, j;
struct b64state b64;
SHA1_CTX ctxt;
#endif /* USE_SRP */
/* Handle both initial auth and restart */
if (pcb->eap.es_server.ea_state < eapIdentify &&
pcb->eap.es_server.ea_state != eapInitial) {
pcb->eap.es_server.ea_state = eapIdentify;
#if PPP_REMOTENAME
if (pcb->settings.explicit_remote) {
/*
* If we already know the peer's
* unauthenticated name, then there's no
* reason to ask. Go to next state instead.
*/
pcb->eap.es_server.ea_peer = pcb->remote_name;
pcb->eap.es_server.ea_peerlen = strlen(pcb->remote_name);
eap_figure_next_state(pcb, 0);
}
#endif /* PPP_REMOTENAME */
}
if (pcb->settings.eap_max_transmits > 0 &&
pcb->eap.es_server.ea_requests >= pcb->settings.eap_max_transmits) {
if (pcb->eap.es_server.ea_responses > 0)
ppp_error("EAP: too many Requests sent");
else
ppp_error("EAP: no response to Requests");
eap_send_failure(pcb);
return;
}
p = pbuf_alloc(PBUF_RAW, (u16_t)(PPP_CTRL_PBUF_MAX_SIZE), PPP_CTRL_PBUF_TYPE);
if(NULL == p)
return;
if(p->tot_len != p->len) {
pbuf_free(p);
return;
}
outp = (u_char*)p->payload;
MAKEHEADER(outp, PPP_EAP);
PUTCHAR(EAP_REQUEST, outp);
PUTCHAR(pcb->eap.es_server.ea_id, outp);
lenloc = outp;
INCPTR(2, outp);
switch (pcb->eap.es_server.ea_state) {
case eapIdentify:
PUTCHAR(EAPT_IDENTITY, outp);
str = "Name";
challen = strlen(str);
MEMCPY(outp, str, challen);
INCPTR(challen, outp);
break;
case eapMD5Chall:
PUTCHAR(EAPT_MD5CHAP, outp);
/*
* pick a random challenge length between
* EAP_MIN_CHALLENGE_LENGTH and EAP_MAX_CHALLENGE_LENGTH
*/
challen = (drand48() *
(EAP_MAX_CHALLENGE_LENGTH - EAP_MIN_CHALLENGE_LENGTH)) +
EAP_MIN_CHALLENGE_LENGTH;
PUTCHAR(challen, outp);
pcb->eap.es_challen = challen;
ptr = pcb->eap.es_challenge;
while (--challen >= 0)
*ptr++ = (u_char) (drand48() * 0x100);
MEMCPY(outp, pcb->eap.es_challenge, pcb->eap.es_challen);
INCPTR(pcb->eap.es_challen, outp);
MEMCPY(outp, pcb->eap.es_server.ea_name, pcb->eap.es_server.ea_namelen);
INCPTR(pcb->eap.es_server.ea_namelen, outp);
break;
#ifdef USE_SRP
case eapSRP1:
PUTCHAR(EAPT_SRP, outp);
PUTCHAR(EAPSRP_CHALLENGE, outp);
PUTCHAR(pcb->eap.es_server.ea_namelen, outp);
MEMCPY(outp, pcb->eap.es_server.ea_name, pcb->eap.es_server.ea_namelen);
INCPTR(pcb->eap.es_server.ea_namelen, outp);
ts = (struct t_server *)pcb->eap.es_server.ea_session;
assert(ts != NULL);
PUTCHAR(ts->s.len, outp);
MEMCPY(outp, ts->s.data, ts->s.len);
INCPTR(ts->s.len, outp);
if (ts->g.len == 1 && ts->g.data[0] == 2) {
PUTCHAR(0, outp);
} else {
PUTCHAR(ts->g.len, outp);
MEMCPY(outp, ts->g.data, ts->g.len);
INCPTR(ts->g.len, outp);
}
if (ts->n.len != sizeof (wkmodulus) ||
BCMP(ts->n.data, wkmodulus, sizeof (wkmodulus)) != 0) {
MEMCPY(outp, ts->n.data, ts->n.len);
INCPTR(ts->n.len, outp);
}
break;
case eapSRP2:
PUTCHAR(EAPT_SRP, outp);
PUTCHAR(EAPSRP_SKEY, outp);
ts = (struct t_server *)pcb->eap.es_server.ea_session;
assert(ts != NULL);
MEMCPY(outp, ts->B.data, ts->B.len);
INCPTR(ts->B.len, outp);
break;
case eapSRP3:
PUTCHAR(EAPT_SRP, outp);
PUTCHAR(EAPSRP_SVALIDATOR, outp);
PUTLONG(SRPVAL_EBIT, outp);
ts = (struct t_server *)pcb->eap.es_server.ea_session;
assert(ts != NULL);
MEMCPY(outp, t_serverresponse(ts), SHA_DIGESTSIZE);
INCPTR(SHA_DIGESTSIZE, outp);
if (pncrypt_setkey(0)) {
/* Generate pseudonym */
optr = outp;
cp = (unsigned char *)pcb->eap.es_server.ea_peer;
if ((j = i = pcb->eap.es_server.ea_peerlen) > 7)
j = 7;
clear[0] = i;
MEMCPY(clear + 1, cp, j);
i -= j;
cp += j;
/* FIXME: if we want to do SRP, we need to find a way to pass the PolarSSL des_context instead of using static memory */
if (!DesEncrypt(clear, cipher)) {
ppp_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) {
/* FIXME: if we want to do SRP, we need to find a way to pass the PolarSSL des_context instead of using static memory */
(void) DesEncrypt(cp, cipher);
outp += b64enc(&b64, cipher, 8, outp);
cp += 8;
i -= 8;
}
if (i > 0) {
MEMCPY(clear, cp, i);
cp += i;
while (i < 8) {
*cp++ = drand48() * 0x100;
i++;
}
/* FIXME: if we want to do SRP, we need to find a way to pass the PolarSSL des_context instead of using static memory */
(void) DesEncrypt(clear, cipher);
outp += b64enc(&b64, cipher, 8, outp);
}
outp += b64flush(&b64, outp);
/* Set length and pad out to next 20 octet boundary */
i = outp - optr - 1;
*optr = i;
i %= SHA_DIGESTSIZE;
if (i != 0) {
while (i < SHA_DIGESTSIZE) {
*outp++ = drand48() * 0x100;
i++;
}
}
/* Obscure the pseudonym with SHA1 hash */
SHA1Init(&ctxt);
SHA1Update(&ctxt, &pcb->eap.es_server.ea_id, 1);
SHA1Update(&ctxt, pcb->eap.es_server.ea_skey,
SESSION_KEY_LEN);
SHA1Update(&ctxt, pcb->eap.es_server.ea_peer,
pcb->eap.es_server.ea_peerlen);
while (optr < outp) {
SHA1Final(dig, &ctxt);
cp = dig;
while (cp < dig + SHA_DIGESTSIZE)
*optr++ ^= *cp++;
SHA1Init(&ctxt);
SHA1Update(&ctxt, &pcb->eap.es_server.ea_id, 1);
SHA1Update(&ctxt, pcb->eap.es_server.ea_skey,
SESSION_KEY_LEN);
SHA1Update(&ctxt, optr - SHA_DIGESTSIZE,
SHA_DIGESTSIZE);
}
}
break;
case eapSRP4:
PUTCHAR(EAPT_SRP, outp);
PUTCHAR(EAPSRP_LWRECHALLENGE, outp);
challen = EAP_MIN_CHALLENGE_LENGTH +
((EAP_MAX_CHALLENGE_LENGTH - EAP_MIN_CHALLENGE_LENGTH) * drand48());
pcb->eap.es_challen = challen;
ptr = pcb->eap.es_challenge;
while (--challen >= 0)
*ptr++ = drand48() * 0x100;
MEMCPY(outp, pcb->eap.es_challenge, pcb->eap.es_challen);
INCPTR(pcb->eap.es_challen, outp);
break;
#endif /* USE_SRP */
default:
return;
}
outlen = (outp - (unsigned char*)p->payload) - PPP_HDRLEN;
PUTSHORT(outlen, lenloc);
pbuf_realloc(p, outlen + PPP_HDRLEN);
ppp_write(pcb, p);
pcb->eap.es_server.ea_requests++;
if (pcb->settings.eap_timeout_time > 0)
TIMEOUT(eap_server_timeout, pcb, pcb->settings.eap_timeout_time);
}
/*
* eap_authpeer - Authenticate our peer (behave as server).
*
* Start server state and send first request. This is called only
* after eap_lowerup.
*/
void eap_authpeer(ppp_pcb *pcb, const char *localname) {
/* Save the name we're given. */
pcb->eap.es_server.ea_name = localname;
pcb->eap.es_server.ea_namelen = strlen(localname);
pcb->eap.es_savedtime = pcb->settings.eap_timeout_time;
/* Lower layer up yet? */
if (pcb->eap.es_server.ea_state == eapInitial ||
pcb->eap.es_server.ea_state == eapPending) {
pcb->eap.es_server.ea_state = eapPending;
return;
}
pcb->eap.es_server.ea_state = eapPending;
/* ID number not updated here intentionally; hashed into M1 */
eap_send_request(pcb);
}
/*
* eap_server_timeout - Retransmission timer for sending Requests
* expired.
*/
static void eap_server_timeout(void *arg) {
ppp_pcb *pcb = (ppp_pcb*)arg;
if (!eap_server_active(pcb))
return;
/* EAP ID number must not change on timeout. */
eap_send_request(pcb);
}
/*
* When it's time to send rechallenge the peer, this timeout is
* called. Once the rechallenge is successful, the response handler
* will restart the timer. If it fails, then the link is dropped.
*/
static void eap_rechallenge(void *arg) {
ppp_pcb *pcb = (ppp_pcb*)arg;
if (pcb->eap.es_server.ea_state != eapOpen &&
pcb->eap.es_server.ea_state != eapSRP4)
return;
pcb->eap.es_server.ea_requests = 0;
pcb->eap.es_server.ea_state = eapIdentify;
eap_figure_next_state(pcb, 0);
pcb->eap.es_server.ea_id++;
eap_send_request(pcb);
}
static void srp_lwrechallenge(void *arg) {
ppp_pcb *pcb = (ppp_pcb*)arg;
if (pcb->eap.es_server.ea_state != eapOpen ||
pcb->eap.es_server.ea_type != EAPT_SRP)
return;
pcb->eap.es_server.ea_requests = 0;
pcb->eap.es_server.ea_state = eapSRP4;
pcb->eap.es_server.ea_id++;
eap_send_request(pcb);
}
#endif /* PPP_SERVER */
/*
* eap_lowerup - The lower layer is now up.
*
* This is called before either eap_authpeer or eap_authwithpeer. See
* link_established() in auth.c. All that's necessary here is to
* return to closed state so that those two routines will do the right
* thing.
*/
static void eap_lowerup(ppp_pcb *pcb) {
/* Discard any (possibly authenticated) peer name. */
#if PPP_SERVER
if (pcb->eap.es_server.ea_peer != NULL
#if PPP_REMOTENAME
&& pcb->eap.es_server.ea_peer != pcb->remote_name
#endif /* PPP_REMOTENAME */
)
free(pcb->eap.es_server.ea_peer);
pcb->eap.es_server.ea_peer = NULL;
#endif /* PPP_SERVER */
if (pcb->eap.es_client.ea_peer != NULL)
free(pcb->eap.es_client.ea_peer);
pcb->eap.es_client.ea_peer = NULL;
pcb->eap.es_client.ea_state = eapClosed;
#if PPP_SERVER
pcb->eap.es_server.ea_state = eapClosed;
#endif /* PPP_SERVER */
}
/*
* eap_lowerdown - The lower layer is now down.
*
* Cancel all timeouts and return to initial state.
*/
static void eap_lowerdown(ppp_pcb *pcb) {
if (eap_client_active(pcb) && pcb->settings.eap_req_time > 0) {
UNTIMEOUT(eap_client_timeout, pcb);
}
#if PPP_SERVER
if (eap_server_active(pcb)) {
if (pcb->settings.eap_timeout_time > 0) {
UNTIMEOUT(eap_server_timeout, pcb);
}
} else {
if ((pcb->eap.es_server.ea_state == eapOpen ||
pcb->eap.es_server.ea_state == eapSRP4) &&
pcb->eap.es_rechallenge > 0) {
UNTIMEOUT(eap_rechallenge, (void *)pcb);
}
if (pcb->eap.es_server.ea_state == eapOpen &&
pcb->eap.es_lwrechallenge > 0) {
UNTIMEOUT(srp_lwrechallenge, (void *)pcb);
}
}
pcb->eap.es_client.ea_state = pcb->eap.es_server.ea_state = eapInitial;
pcb->eap.es_client.ea_requests = pcb->eap.es_server.ea_requests = 0;
#endif /* PPP_SERVER */
}
/*
* eap_protrej - Peer doesn't speak this protocol.
*
* This shouldn't happen. If it does, it represents authentication
* failure.
*/
static void eap_protrej(ppp_pcb *pcb) {
if (eap_client_active(pcb)) {
ppp_error("EAP authentication failed due to Protocol-Reject");
auth_withpeer_fail(pcb, PPP_EAP);
}
#if PPP_SERVER
if (eap_server_active(pcb)) {
ppp_error("EAP authentication of peer failed on Protocol-Reject");
auth_peer_fail(pcb, PPP_EAP);
}
#endif /* PPP_SERVER */
eap_lowerdown(pcb);
}
/*
* Format and send a regular EAP Response message.
*/
static void eap_send_response(ppp_pcb *pcb, u_char id, u_char typenum, u_char *str, int lenstr) {
struct pbuf *p;
u_char *outp;
int msglen;
msglen = EAP_HEADERLEN + sizeof (u_char) + lenstr;
p = pbuf_alloc(PBUF_RAW, (u16_t)(PPP_HDRLEN + msglen), PPP_CTRL_PBUF_TYPE);
if(NULL == p)
return;
if(p->tot_len != p->len) {
pbuf_free(p);
return;
}
outp = (u_char*)p->payload;
MAKEHEADER(outp, PPP_EAP);
PUTCHAR(EAP_RESPONSE, outp);
PUTCHAR(id, outp);
pcb->eap.es_client.ea_id = id;
PUTSHORT(msglen, outp);
PUTCHAR(typenum, outp);
if (lenstr > 0) {
MEMCPY(outp, str, lenstr);
}
ppp_write(pcb, p);
}
/*
* Format and send an MD5-Challenge EAP Response message.
*/
static void eap_chap_response(ppp_pcb *pcb, u_char id, u_char *hash, const char *name, int namelen) {
struct pbuf *p;
u_char *outp;
int msglen;
msglen = EAP_HEADERLEN + 2 * sizeof (u_char) + MD5_SIGNATURE_SIZE +
namelen;
p = pbuf_alloc(PBUF_RAW, (u16_t)(PPP_HDRLEN + msglen), PPP_CTRL_PBUF_TYPE);
if(NULL == p)
return;
if(p->tot_len != p->len) {
pbuf_free(p);
return;
}
outp = (u_char*)p->payload;
MAKEHEADER(outp, PPP_EAP);
PUTCHAR(EAP_RESPONSE, outp);
PUTCHAR(id, outp);
pcb->eap.es_client.ea_id = id;
PUTSHORT(msglen, outp);
PUTCHAR(EAPT_MD5CHAP, outp);
PUTCHAR(MD5_SIGNATURE_SIZE, outp);
MEMCPY(outp, hash, MD5_SIGNATURE_SIZE);
INCPTR(MD5_SIGNATURE_SIZE, outp);
if (namelen > 0) {
MEMCPY(outp, name, namelen);
}
ppp_write(pcb, p);
}
#ifdef USE_SRP
/*
* 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;
{
ppp_pcb *pcb = &ppp_pcb_list[pcb->eap.es_unit];
struct pbuf *p;
u_char *outp;
int msglen;
msglen = EAP_HEADERLEN + 2 * sizeof (u_char) + lenstr;
p = pbuf_alloc(PBUF_RAW, (u16_t)(PPP_HDRLEN + msglen), PPP_CTRL_PBUF_TYPE);
if(NULL == p)
return;
if(p->tot_len != p->len) {
pbuf_free(p);
return;
}
outp = p->payload;
MAKEHEADER(outp, PPP_EAP);
PUTCHAR(EAP_RESPONSE, outp);
PUTCHAR(id, outp);
pcb->eap.es_client.ea_id = id;
PUTSHORT(msglen, outp);
PUTCHAR(EAPT_SRP, outp);
PUTCHAR(subtypenum, outp);
if (lenstr > 0) {
MEMCPY(outp, str, lenstr);
}
ppp_write(pcb, p);
}
/*
* 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;
u32_t flags;
u_char *str;
{
ppp_pcb *pcb = &ppp_pcb_list[pcb->eap.es_unit];
struct pbuf *p;
u_char *outp;
int msglen;
msglen = EAP_HEADERLEN + 2 * sizeof (u_char) + sizeof (u32_t) +
SHA_DIGESTSIZE;
p = pbuf_alloc(PBUF_RAW, (u16_t)(PPP_HDRLEN + msglen), PPP_CTRL_PBUF_TYPE);
if(NULL == p)
return;
if(p->tot_len != p->len) {
pbuf_free(p);
return;
}
outp = p->payload;
MAKEHEADER(outp, PPP_EAP);
PUTCHAR(EAP_RESPONSE, outp);
PUTCHAR(id, outp);
pcb->eap.es_client.ea_id = id;
PUTSHORT(msglen, outp);
PUTCHAR(EAPT_SRP, outp);
PUTCHAR(EAPSRP_CVALIDATOR, outp);
PUTLONG(flags, outp);
MEMCPY(outp, str, SHA_DIGESTSIZE);
ppp_write(pcb, p);
}
#endif /* USE_SRP */
static void eap_send_nak(ppp_pcb *pcb, u_char id, u_char type) {
struct pbuf *p;
u_char *outp;
int msglen;
msglen = EAP_HEADERLEN + 2 * sizeof (u_char);
p = pbuf_alloc(PBUF_RAW, (u16_t)(PPP_HDRLEN + msglen), PPP_CTRL_PBUF_TYPE);
if(NULL == p)
return;
if(p->tot_len != p->len) {
pbuf_free(p);
return;
}
outp = (u_char*)p->payload;
MAKEHEADER(outp, PPP_EAP);
PUTCHAR(EAP_RESPONSE, outp);
PUTCHAR(id, outp);
pcb->eap.es_client.ea_id = id;
PUTSHORT(msglen, outp);
PUTCHAR(EAPT_NAK, outp);
PUTCHAR(type, outp);
ppp_write(pcb, p);
}
#ifdef USE_SRP
static char *
name_of_pn_file()
{
char *user, *path, *file;
struct passwd *pw;
size_t pl;
static bool pnlogged = 0;
pw = getpwuid(getuid());
if (pw == NULL || (user = pw->pw_dir) == NULL || user[0] == 0) {
errno = EINVAL;
return (NULL);
}
file = _PATH_PSEUDONYM;
pl = strlen(user) + strlen(file) + 2;
path = malloc(pl);
if (path == NULL)
return (NULL);
(void) slprintf(path, pl, "%s/%s", user, file);
if (!pnlogged) {
ppp_dbglog("pseudonym file: %s", path);
pnlogged = 1;
}
return (path);
}
static int
open_pn_file(modebits)
mode_t modebits;
{
char *path;
int fd, err;
if ((path = name_of_pn_file()) == NULL)
return (-1);
fd = open(path, modebits, S_IRUSR | S_IWUSR);
err = errno;
free(path);
errno = err;
return (fd);
}
static void
remove_pn_file()
{
char *path;
if ((path = name_of_pn_file()) != NULL) {
(void) unlink(path);
(void) free(path);
}
}
static void
write_pseudonym(esp, inp, len, id)
eap_state *esp;
u_char *inp;
int len, id;
{
u_char val;
u_char *datp, *digp;
SHA1_CTX ctxt;
u_char dig[SHA_DIGESTSIZE];
int dsize, fd, olen = len;
/*
* Do the decoding by working backwards. This eliminates the need
* to save the decoded output in a separate buffer.
*/
val = id;
while (len > 0) {
if ((dsize = len % SHA_DIGESTSIZE) == 0)
dsize = SHA_DIGESTSIZE;
len -= dsize;
datp = inp + len;
SHA1Init(&ctxt);
SHA1Update(&ctxt, &val, 1);
SHA1Update(&ctxt, pcb->eap.es_client.ea_skey, SESSION_KEY_LEN);
if (len > 0) {
SHA1Update(&ctxt, datp, SHA_DIGESTSIZE);
} else {
SHA1Update(&ctxt, pcb->eap.es_client.ea_name,
pcb->eap.es_client.ea_namelen);
}
SHA1Final(dig, &ctxt);
for (digp = dig; digp < dig + SHA_DIGESTSIZE; digp++)
*datp++ ^= *digp;
}
/* Now check that the result is sane */
if (olen <= 0 || *inp + 1 > olen) {
ppp_dbglog("EAP: decoded pseudonym is unusable <%.*B>", olen, inp);
return;
}
/* Save it away */
fd = open_pn_file(O_WRONLY | O_CREAT | O_TRUNC);
if (fd < 0) {
ppp_dbglog("EAP: error saving pseudonym: %m");
return;
}
len = write(fd, inp + 1, *inp);
if (close(fd) != -1 && len == *inp) {
ppp_dbglog("EAP: saved pseudonym");
pcb->eap.es_usedpseudo = 0;
} else {
ppp_dbglog("EAP: failed to save pseudonym");
remove_pn_file();
}
}
#endif /* USE_SRP */
/*
* eap_request - Receive EAP Request message (client mode).
*/
static void eap_request(ppp_pcb *pcb, u_char *inp, int id, int len) {
u_char typenum;
u_char vallen;
int secret_len;
char secret[MAXWORDLEN];
char rhostname[256];
md5_context mdContext;
u_char hash[MD5_SIGNATURE_SIZE];
#ifdef USE_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];
int fd;
#endif /* USE_SRP */
/*
* Note: we update es_client.ea_id *only if* a Response
* message is being generated. Otherwise, we leave it the
* same for duplicate detection purposes.
*/
pcb->eap.es_client.ea_requests++;
if (pcb->settings.eap_allow_req != 0 &&
pcb->eap.es_client.ea_requests > pcb->settings.eap_allow_req) {
ppp_info("EAP: received too many Request messages");
if (pcb->settings.eap_req_time > 0) {
UNTIMEOUT(eap_client_timeout, pcb);
}
auth_withpeer_fail(pcb, PPP_EAP);
return;
}
if (len <= 0) {
ppp_error("EAP: empty Request message discarded");
return;
}
GETCHAR(typenum, inp);
len--;
switch (typenum) {
case EAPT_IDENTITY:
if (len > 0)
ppp_info("EAP: Identity prompt \"%.*q\"", len, inp);
#ifdef USE_SRP
if (pcb->eap.es_usepseudo &&
(pcb->eap.es_usedpseudo == 0 ||
(pcb->eap.es_usedpseudo == 1 &&
id == pcb->eap.es_client.ea_id))) {
pcb->eap.es_usedpseudo = 1;
/* Try to get a pseudonym */
if ((fd = open_pn_file(O_RDONLY)) >= 0) {
strcpy(rhostname, SRP_PSEUDO_ID);
len = read(fd, rhostname + SRP_PSEUDO_LEN,
sizeof (rhostname) - SRP_PSEUDO_LEN);
/* XXX NAI unsupported */
if (len > 0) {
eap_send_response(pcb, id, typenum,
rhostname, len + SRP_PSEUDO_LEN);
}
(void) close(fd);
if (len > 0)
break;
}
}
/* Stop using pseudonym now. */
if (pcb->eap.es_usepseudo && pcb->eap.es_usedpseudo != 2) {
remove_pn_file();
pcb->eap.es_usedpseudo = 2;
}
#endif /* USE_SRP */
eap_send_response(pcb, id, typenum, (u_char*)pcb->eap.es_client.ea_name,
pcb->eap.es_client.ea_namelen);
break;
case EAPT_NOTIFICATION:
if (len > 0)
ppp_info("EAP: Notification \"%.*q\"", len, inp);
eap_send_response(pcb, id, typenum, NULL, 0);
break;
case EAPT_NAK:
/*
* Avoid the temptation to send Response Nak in reply
* to Request Nak here. It can only lead to trouble.
*/
ppp_warn("EAP: unexpected Nak in Request; ignored");
/* Return because we're waiting for something real. */
return;
case EAPT_MD5CHAP:
if (len < 1) {
ppp_error("EAP: received MD5-Challenge with no data");
/* Bogus request; wait for something real. */
return;
}
GETCHAR(vallen, inp);
len--;
if (vallen < 8 || vallen > len) {
ppp_error("EAP: MD5-Challenge with bad length %d (8..%d)",
vallen, len);
/* Try something better. */
eap_send_nak(pcb, id, EAPT_SRP);
break;
}
/* Not so likely to happen. */
if (vallen >= len + sizeof (rhostname)) {
ppp_dbglog("EAP: trimming really long peer name down");
MEMCPY(rhostname, inp + vallen, sizeof (rhostname) - 1);
rhostname[sizeof (rhostname) - 1] = '\0';
} else {
MEMCPY(rhostname, inp + vallen, len - vallen);
rhostname[len - vallen] = '\0';
}
#if PPP_REMOTENAME
/* In case the remote doesn't give us his name. */
if (pcb->settings.explicit_remote ||
(pcb->settings.remote_name[0] != '\0' && vallen == len))
strlcpy(rhostname, pcb->settings.remote_name, sizeof (rhostname));
#endif /* PPP_REMOTENAME */
/*
* Get the secret for authenticating ourselves with
* the specified host.
*/
if (!get_secret(pcb, pcb->eap.es_client.ea_name,
rhostname, secret, &secret_len, 0)) {
ppp_dbglog("EAP: no MD5 secret for auth to %q", rhostname);
eap_send_nak(pcb, id, EAPT_SRP);
break;
}
md5_starts(&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_finish(&mdContext, hash);
eap_chap_response(pcb, id, hash, pcb->eap.es_client.ea_name,
pcb->eap.es_client.ea_namelen);
break;
#ifdef USE_SRP
case EAPT_SRP:
if (len < 1) {
ppp_error("EAP: received empty SRP Request");
/* Bogus request; wait for something real. */
return;
}
/* Get subtype */
GETCHAR(vallen, inp);
len--;
switch (vallen) {
case EAPSRP_CHALLENGE:
tc = NULL;
if (pcb->eap.es_client.ea_session != NULL) {
tc = (struct t_client *)pcb->eap.es_client.
ea_session;
/*
* If this is a new challenge, then start
* over with a new client session context.
* Otherwise, just resend last response.
*/
if (id != pcb->eap.es_client.ea_id) {
t_clientclose(tc);
pcb->eap.es_client.ea_session = NULL;
tc = NULL;
}
}
/* No session key just yet */
pcb->eap.es_client.ea_skey = NULL;
if (tc == NULL) {
GETCHAR(vallen, inp);
len--;
if (vallen >= len) {
ppp_error("EAP: badly-formed SRP Challenge"
" (name)");
/* Ignore badly-formed messages */
return;
}
MEMCPY(rhostname, inp, vallen);
rhostname[vallen] = '\0';
INCPTR(vallen, inp);
len -= vallen;
/*
* In case the remote doesn't give us his name,
* use configured name.
*/
if (explicit_remote ||
(remote_name[0] != '\0' && vallen == 0)) {
strlcpy(rhostname, remote_name,
sizeof (rhostname));
}
if (pcb->eap.es_client.ea_peer != NULL)
free(pcb->eap.es_client.ea_peer);
pcb->eap.es_client.ea_peer = strdup(rhostname);
pcb->eap.es_client.ea_peerlen = strlen(rhostname);
GETCHAR(vallen, inp);
len--;
if (vallen >= len) {
ppp_error("EAP: badly-formed SRP Challenge"
" (s)");
/* Ignore badly-formed messages */
return;
}
sval.data = inp;
sval.len = vallen;
INCPTR(vallen, inp);
len -= vallen;
GETCHAR(vallen, inp);
len--;
if (vallen > len) {
ppp_error("EAP: badly-formed SRP Challenge"
" (g)");
/* Ignore badly-formed messages */
return;
}
/* If no generator present, then use value 2 */
if (vallen == 0) {
gval.data = (u_char *)"\002";
gval.len = 1;
} else {
gval.data = inp;
gval.len = vallen;
}
INCPTR(vallen, inp);
len -= vallen;
/*
* If no modulus present, then use well-known
* value.
*/
if (len == 0) {
Nval.data = (u_char *)wkmodulus;
Nval.len = sizeof (wkmodulus);
} else {
Nval.data = inp;
Nval.len = len;
}
tc = t_clientopen(pcb->eap.es_client.ea_name,
&Nval, &gval, &sval);
if (tc == NULL) {
eap_send_nak(pcb, id, EAPT_MD5CHAP);
break;
}
pcb->eap.es_client.ea_session = (void *)tc;
/* Add Challenge ID & type to verifier */
vals[0] = id;
vals[1] = EAPT_SRP;
t_clientaddexdata(tc, vals, 2);
}
Ap = t_clientgenexp(tc);
eap_srp_response(esp, id, EAPSRP_CKEY, Ap->data,
Ap->len);
break;
case EAPSRP_SKEY:
tc = (struct t_client *)pcb->eap.es_client.ea_session;
if (tc == NULL) {
ppp_warn("EAP: peer sent Subtype 2 without 1");
eap_send_nak(pcb, id, EAPT_MD5CHAP);
break;
}
if (pcb->eap.es_client.ea_skey != NULL) {
/*
* ID number should not change here. Warn
* if it does (but otherwise ignore).
*/
if (id != pcb->eap.es_client.ea_id) {
ppp_warn("EAP: ID changed from %d to %d "
"in SRP Subtype 2 rexmit",
pcb->eap.es_client.ea_id, id);
}
} else {
if (get_srp_secret(pcb->eap.es_unit,
pcb->eap.es_client.ea_name,
pcb->eap.es_client.ea_peer, secret, 0) == 0) {
/*
* Can't work with this peer because
* the secret is missing. Just give
* up.
*/
eap_send_nak(pcb, id, EAPT_MD5CHAP);
break;
}
Bval.data = inp;
Bval.len = len;
t_clientpasswd(tc, secret);
BZERO(secret, sizeof (secret));
pcb->eap.es_client.ea_skey =
t_clientgetkey(tc, &Bval);
if (pcb->eap.es_client.ea_skey == NULL) {
/* Server is rogue; stop now */
ppp_error("EAP: SRP server is rogue");
goto client_failure;
}
}
eap_srpval_response(esp, id, SRPVAL_EBIT,
t_clientresponse(tc));
break;
case EAPSRP_SVALIDATOR:
tc = (struct t_client *)pcb->eap.es_client.ea_session;
if (tc == NULL || pcb->eap.es_client.ea_skey == NULL) {
ppp_warn("EAP: peer sent Subtype 3 without 1/2");
eap_send_nak(pcb, id, EAPT_MD5CHAP);
break;
}
/*
* If we're already open, then this ought to be a
* duplicate. Otherwise, check that the server is
* who we think it is.
*/
if (pcb->eap.es_client.ea_state == eapOpen) {
if (id != pcb->eap.es_client.ea_id) {
ppp_warn("EAP: ID changed from %d to %d "
"in SRP Subtype 3 rexmit",
pcb->eap.es_client.ea_id, id);
}
} else {
len -= sizeof (u32_t) + SHA_DIGESTSIZE;
if (len < 0 || t_clientverify(tc, inp +
sizeof (u32_t)) != 0) {
ppp_error("EAP: SRP server verification "
"failed");
goto client_failure;
}
GETLONG(pcb->eap.es_client.ea_keyflags, inp);
/* Save pseudonym if user wants it. */
if (len > 0 && pcb->eap.es_usepseudo) {
INCPTR(SHA_DIGESTSIZE, inp);
write_pseudonym(esp, inp, len, id);
}
}
/*
* We've verified our peer. We're now mostly done,
* except for waiting on the regular EAP Success
* message.
*/
eap_srp_response(esp, id, EAPSRP_ACK, NULL, 0);
break;
case EAPSRP_LWRECHALLENGE:
if (len < 4) {
ppp_warn("EAP: malformed Lightweight rechallenge");
return;
}
SHA1Init(&ctxt);
vals[0] = id;
SHA1Update(&ctxt, vals, 1);
SHA1Update(&ctxt, pcb->eap.es_client.ea_skey,
SESSION_KEY_LEN);
SHA1Update(&ctxt, inp, len);
SHA1Update(&ctxt, pcb->eap.es_client.ea_name,
pcb->eap.es_client.ea_namelen);
SHA1Final(dig, &ctxt);
eap_srp_response(esp, id, EAPSRP_LWRECHALLENGE, dig,
SHA_DIGESTSIZE);
break;
default:
ppp_error("EAP: unknown SRP Subtype %d", vallen);
eap_send_nak(pcb, id, EAPT_MD5CHAP);
break;
}
break;
#endif /* USE_SRP */
default:
ppp_info("EAP: unknown authentication type %d; Naking", typenum);
eap_send_nak(pcb, id, EAPT_SRP);
break;
}
if (pcb->settings.eap_req_time > 0) {
UNTIMEOUT(eap_client_timeout, pcb);
TIMEOUT(eap_client_timeout, pcb,
pcb->settings.eap_req_time);
}
return;
#ifdef USE_SRP
client_failure:
pcb->eap.es_client.ea_state = eapBadAuth;
if (pcb->settings.eap_req_time > 0) {
UNTIMEOUT(eap_client_timeout, (void *)esp);
}
pcb->eap.es_client.ea_session = NULL;
t_clientclose(tc);
auth_withpeer_fail(pcb, PPP_EAP);
#endif /* USE_SRP */
}
#if PPP_SERVER
/* FIXME: remove malloc() and free() */
/*
* eap_response - Receive EAP Response message (server mode).
*/
static void eap_response(ppp_pcb *pcb, u_char *inp, int id, int len) {
u_char typenum;
u_char vallen;
int secret_len;
char secret[MAXSECRETLEN];
char rhostname[256];
md5_context mdContext;
u_char hash[MD5_SIGNATURE_SIZE];
#ifdef USE_SRP
struct t_server *ts;
struct t_num A;
SHA1_CTX ctxt;
u_char dig[SHA_DIGESTSIZE];
#endif /* USE_SRP */
if (pcb->eap.es_server.ea_id != id) {
ppp_dbglog("EAP: discarding Response %d; expected ID %d", id,
pcb->eap.es_server.ea_id);
return;
}
pcb->eap.es_server.ea_responses++;
if (len <= 0) {
ppp_error("EAP: empty Response message discarded");
return;
}
GETCHAR(typenum, inp);
len--;
switch (typenum) {
case EAPT_IDENTITY:
if (pcb->eap.es_server.ea_state != eapIdentify) {
ppp_dbglog("EAP discarding unwanted Identify \"%.q\"", len,
inp);
break;
}
ppp_info("EAP: unauthenticated peer name \"%.*q\"", len, inp);
if (pcb->eap.es_server.ea_peer != NULL
#if PPP_REMOTENAME
&& pcb->eap.es_server.ea_peer != pcb->remote_name
#endif /* PPP_REMOTENAME */
)
free(pcb->eap.es_server.ea_peer);
pcb->eap.es_server.ea_peer = (char*)malloc(len + 1);
if (pcb->eap.es_server.ea_peer == NULL) {
pcb->eap.es_server.ea_peerlen = 0;
eap_figure_next_state(pcb, 1);
break;
}
MEMCPY(pcb->eap.es_server.ea_peer, inp, len);
pcb->eap.es_server.ea_peer[len] = '\0';
pcb->eap.es_server.ea_peerlen = len;
eap_figure_next_state(pcb, 0);
break;
case EAPT_NOTIFICATION:
ppp_dbglog("EAP unexpected Notification; response discarded");
break;
case EAPT_NAK:
if (len < 1) {
ppp_info("EAP: Nak Response with no suggested protocol");
eap_figure_next_state(pcb, 1);
break;
}
GETCHAR(vallen, inp);
len--;
if (
#if PPP_REMOTENAME
!pcb->explicit_remote &&
#endif /* PPP_REMOTENAME */
pcb->eap.es_server.ea_state == eapIdentify){
/* Peer cannot Nak Identify Request */
eap_figure_next_state(pcb, 1);
break;
}
switch (vallen) {
case EAPT_SRP:
/* Run through SRP validator selection again. */
pcb->eap.es_server.ea_state = eapIdentify;
eap_figure_next_state(pcb, 0);
break;
case EAPT_MD5CHAP:
pcb->eap.es_server.ea_state = eapMD5Chall;
break;
default:
ppp_dbglog("EAP: peer requesting unknown Type %d", vallen);
switch (pcb->eap.es_server.ea_state) {
case eapSRP1:
case eapSRP2:
case eapSRP3:
pcb->eap.es_server.ea_state = eapMD5Chall;
break;
case eapMD5Chall:
case eapSRP4:
pcb->eap.es_server.ea_state = eapIdentify;
eap_figure_next_state(pcb, 0);
break;
default:
break;
}
break;
}
break;
case EAPT_MD5CHAP:
if (pcb->eap.es_server.ea_state != eapMD5Chall) {
ppp_error("EAP: unexpected MD5-Response");
eap_figure_next_state(pcb, 1);
break;
}
if (len < 1) {
ppp_error("EAP: received MD5-Response with no data");
eap_figure_next_state(pcb, 1);
break;
}
GETCHAR(vallen, inp);
len--;
if (vallen != 16 || vallen > len) {
ppp_error("EAP: MD5-Response with bad length %d", vallen);
eap_figure_next_state(pcb, 1);
break;
}
/* Not so likely to happen. */
if (vallen >= len + sizeof (rhostname)) {
ppp_dbglog("EAP: trimming really long peer name down");
MEMCPY(rhostname, inp + vallen, sizeof (rhostname) - 1);
rhostname[sizeof (rhostname) - 1] = '\0';
} else {
MEMCPY(rhostname, inp + vallen, len - vallen);
rhostname[len - vallen] = '\0';
}
#if PPP_REMOTENAME
/* 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));
#endif /* PPP_REMOTENAME */
/*
* Get the secret for authenticating the specified
* host.
*/
if (!get_secret(pcb, rhostname,
pcb->eap.es_server.ea_name, secret, &secret_len, 1)) {
ppp_dbglog("EAP: no MD5 secret for auth of %q", rhostname);
eap_send_failure(pcb);
break;
}
md5_starts(&mdContext);
md5_update(&mdContext, &pcb->eap.es_server.ea_id, 1);
md5_update(&mdContext, (u_char *)secret, secret_len);
BZERO(secret, sizeof (secret));
md5_update(&mdContext, pcb->eap.es_challenge, pcb->eap.es_challen);
md5_finish(&mdContext, hash);
if (BCMP(hash, inp, MD5_SIGNATURE_SIZE) != 0) {
eap_send_failure(pcb);
break;
}
pcb->eap.es_server.ea_type = EAPT_MD5CHAP;
eap_send_success(pcb);
eap_figure_next_state(pcb, 0);
if (pcb->eap.es_rechallenge != 0)
TIMEOUT(eap_rechallenge, pcb, pcb->eap.es_rechallenge);
break;
#ifdef USE_SRP
case EAPT_SRP:
if (len < 1) {
ppp_error("EAP: empty SRP Response");
eap_figure_next_state(pcb, 1);
break;
}
GETCHAR(typenum, inp);
len--;
switch (typenum) {
case EAPSRP_CKEY:
if (pcb->eap.es_server.ea_state != eapSRP1) {
ppp_error("EAP: unexpected SRP Subtype 1 Response");
eap_figure_next_state(pcb, 1);
break;
}
A.data = inp;
A.len = len;
ts = (struct t_server *)pcb->eap.es_server.ea_session;
assert(ts != NULL);
pcb->eap.es_server.ea_skey = t_servergetkey(ts, &A);
if (pcb->eap.es_server.ea_skey == NULL) {
/* Client's A value is bogus; terminate now */
ppp_error("EAP: bogus A value from client");
eap_send_failure(pcb);
} else {
eap_figure_next_state(pcb, 0);
}
break;
case EAPSRP_CVALIDATOR:
if (pcb->eap.es_server.ea_state != eapSRP2) {
ppp_error("EAP: unexpected SRP Subtype 2 Response");
eap_figure_next_state(pcb, 1);
break;
}
if (len < sizeof (u32_t) + SHA_DIGESTSIZE) {
ppp_error("EAP: M1 length %d < %d", len,
sizeof (u32_t) + SHA_DIGESTSIZE);
eap_figure_next_state(pcb, 1);
break;
}
GETLONG(pcb->eap.es_server.ea_keyflags, inp);
ts = (struct t_server *)pcb->eap.es_server.ea_session;
assert(ts != NULL);
if (t_serververify(ts, inp)) {
ppp_info("EAP: unable to validate client identity");
eap_send_failure(pcb);
break;
}
eap_figure_next_state(pcb, 0);
break;
case EAPSRP_ACK:
if (pcb->eap.es_server.ea_state != eapSRP3) {
ppp_error("EAP: unexpected SRP Subtype 3 Response");
eap_send_failure(esp);
break;
}
pcb->eap.es_server.ea_type = EAPT_SRP;
eap_send_success(pcb, esp);
eap_figure_next_state(pcb, 0);
if (pcb->eap.es_rechallenge != 0)
TIMEOUT(eap_rechallenge, pcb,
pcb->eap.es_rechallenge);
if (pcb->eap.es_lwrechallenge != 0)
TIMEOUT(srp_lwrechallenge, pcb,
pcb->eap.es_lwrechallenge);
break;
case EAPSRP_LWRECHALLENGE:
if (pcb->eap.es_server.ea_state != eapSRP4) {
ppp_info("EAP: unexpected SRP Subtype 4 Response");
return;
}
if (len != SHA_DIGESTSIZE) {
ppp_error("EAP: bad Lightweight rechallenge "
"response");
return;
}
SHA1Init(&ctxt);
vallen = id;
SHA1Update(&ctxt, &vallen, 1);
SHA1Update(&ctxt, pcb->eap.es_server.ea_skey,
SESSION_KEY_LEN);
SHA1Update(&ctxt, pcb->eap.es_challenge, pcb->eap.es_challen);
SHA1Update(&ctxt, pcb->eap.es_server.ea_peer,
pcb->eap.es_server.ea_peerlen);
SHA1Final(dig, &ctxt);
if (BCMP(dig, inp, SHA_DIGESTSIZE) != 0) {
ppp_error("EAP: failed Lightweight rechallenge");
eap_send_failure(pcb);
break;
}
pcb->eap.es_server.ea_state = eapOpen;
if (pcb->eap.es_lwrechallenge != 0)
TIMEOUT(srp_lwrechallenge, esp,
pcb->eap.es_lwrechallenge);
break;
}
break;
#endif /* USE_SRP */
default:
/* This can't happen. */
ppp_error("EAP: unknown Response type %d; ignored", typenum);
return;
}
if (pcb->settings.eap_timeout_time > 0) {
UNTIMEOUT(eap_server_timeout, pcb);
}
if (pcb->eap.es_server.ea_state != eapBadAuth &&
pcb->eap.es_server.ea_state != eapOpen) {
pcb->eap.es_server.ea_id++;
eap_send_request(pcb);
}
}
#endif /* PPP_SERVER */
/*
* eap_success - Receive EAP Success message (client mode).
*/
static void eap_success(ppp_pcb *pcb, u_char *inp, int id, int len) {
LWIP_UNUSED_ARG(id);
if (pcb->eap.es_client.ea_state != eapOpen && !eap_client_active(pcb)) {
ppp_dbglog("EAP unexpected success message in state %s (%d)",
eap_state_name(pcb->eap.es_client.ea_state),
pcb->eap.es_client.ea_state);
return;
}
if (pcb->settings.eap_req_time > 0) {
UNTIMEOUT(eap_client_timeout, pcb);
}
if (len > 0) {
/* This is odd. The spec doesn't allow for this. */
PRINTMSG(inp, len);
}
pcb->eap.es_client.ea_state = eapOpen;
auth_withpeer_success(pcb, PPP_EAP, 0);
}
/*
* eap_failure - Receive EAP Failure message (client mode).
*/
static void eap_failure(ppp_pcb *pcb, u_char *inp, int id, int len) {
LWIP_UNUSED_ARG(id);
if (!eap_client_active(pcb)) {
ppp_dbglog("EAP unexpected failure message in state %s (%d)",
eap_state_name(pcb->eap.es_client.ea_state),
pcb->eap.es_client.ea_state);
}
if (pcb->settings.eap_req_time > 0) {
UNTIMEOUT(eap_client_timeout, pcb);
}
if (len > 0) {
/* This is odd. The spec doesn't allow for this. */
PRINTMSG(inp, len);
}
pcb->eap.es_client.ea_state = eapBadAuth;
ppp_error("EAP: peer reports authentication failure");
auth_withpeer_fail(pcb, PPP_EAP);
}
/*
* eap_input - Handle received EAP message.
*/
static void eap_input(ppp_pcb *pcb, u_char *inp, int inlen) {
u_char code, id;
int len;
/*
* Parse header (code, id and length). If packet too short,
* drop it.
*/
if (inlen < EAP_HEADERLEN) {
ppp_error("EAP: packet too short: %d < %d", inlen, EAP_HEADERLEN);
return;
}
GETCHAR(code, inp);
GETCHAR(id, inp);
GETSHORT(len, inp);
if (len < EAP_HEADERLEN || len > inlen) {
ppp_error("EAP: packet has illegal length field %d (%d..%d)", len,
EAP_HEADERLEN, inlen);
return;
}
len -= EAP_HEADERLEN;
/* Dispatch based on message code */
switch (code) {
case EAP_REQUEST:
eap_request(pcb, inp, id, len);
break;
#if PPP_SERVER
case EAP_RESPONSE:
eap_response(pcb, inp, id, len);
break;
#endif /* PPP_SERVER */
case EAP_SUCCESS:
eap_success(pcb, inp, id, len);
break;
case EAP_FAILURE:
eap_failure(pcb, inp, id, len);
break;
default: /* XXX Need code reject */
/* Note: it's not legal to send EAP Nak here. */
ppp_warn("EAP: unknown code %d received", code);
break;
}
}
#if PRINTPKT_SUPPORT
/*
* eap_printpkt - print the contents of an EAP packet.
*/
static const char *eap_codenames[] = {
"Request", "Response", "Success", "Failure"
};
static const char *eap_typenames[] = {
"Identity", "Notification", "Nak", "MD5-Challenge",
"OTP", "Generic-Token", NULL, NULL,
"RSA", "DSS", "KEA", "KEA-Validate",
"TLS", "Defender", "Windows 2000", "Arcot",
"Cisco", "Nokia", "SRP"
};
static int eap_printpkt(u_char *inp, int inlen, void (*printer) (void *, const char *, ...), void *arg) {
int code, id, len, rtype, vallen;
u_char *pstart;
u32_t uval;
if (inlen < EAP_HEADERLEN)
return (0);
pstart = inp;
GETCHAR(code, inp);
GETCHAR(id, inp);
GETSHORT(len, inp);
if (len < EAP_HEADERLEN || len > inlen)
return (0);
if (code >= 1 && code <= (int)sizeof(eap_codenames) / (int)sizeof(char *))
printer(arg, " %s", eap_codenames[code-1]);
else
printer(arg, " code=0x%x", code);
printer(arg, " id=0x%x", id);
len -= EAP_HEADERLEN;
switch (code) {
case EAP_REQUEST:
if (len < 1) {
printer(arg, " <missing type>");
break;
}
GETCHAR(rtype, inp);
len--;
if (rtype >= 1 &&
rtype <= (int)sizeof (eap_typenames) / (int)sizeof (char *))
printer(arg, " %s", eap_typenames[rtype-1]);
else
printer(arg, " type=0x%x", rtype);
switch (rtype) {
case EAPT_IDENTITY:
case EAPT_NOTIFICATION:
if (len > 0) {
printer(arg, " <Message ");
ppp_print_string((char *)inp, len, printer, arg);
printer(arg, ">");
INCPTR(len, inp);
len = 0;
} else {
printer(arg, " <No message>");
}
break;
case EAPT_MD5CHAP:
if (len <= 0)
break;
GETCHAR(vallen, inp);
len--;
if (vallen > len)
goto truncated;
printer(arg, " <Value%.*B>", vallen, inp);
INCPTR(vallen, inp);
len -= vallen;
if (len > 0) {
printer(arg, " <Name ");
ppp_print_string((char *)inp, len, printer, arg);
printer(arg, ">");
INCPTR(len, inp);
len = 0;
} else {
printer(arg, " <No name>");
}
break;
case EAPT_SRP:
if (len < 3)
goto truncated;
GETCHAR(vallen, inp);
len--;
printer(arg, "-%d", vallen);
switch (vallen) {
case EAPSRP_CHALLENGE:
GETCHAR(vallen, inp);
len--;
if (vallen >= len)
goto truncated;
if (vallen > 0) {
printer(arg, " <Name ");
ppp_print_string((char *)inp, vallen, printer,
arg);
printer(arg, ">");
} else {
printer(arg, " <No name>");
}
INCPTR(vallen, inp);
len -= vallen;
GETCHAR(vallen, inp);
len--;
if (vallen >= len)
goto truncated;
printer(arg, " <s%.*B>", vallen, inp);
INCPTR(vallen, inp);
len -= vallen;
GETCHAR(vallen, inp);
len--;
if (vallen > len)
goto truncated;
if (vallen == 0) {
printer(arg, " <Default g=2>");
} else {
printer(arg, " <g%.*B>", vallen, inp);
}
INCPTR(vallen, inp);
len -= vallen;
if (len == 0) {
printer(arg, " <Default N>");
} else {
printer(arg, " <N%.*B>", len, inp);
INCPTR(len, inp);
len = 0;
}
break;
case EAPSRP_SKEY:
printer(arg, " <B%.*B>", len, inp);
INCPTR(len, inp);
len = 0;
break;
case EAPSRP_SVALIDATOR:
if (len < (int)sizeof (u32_t))
break;
GETLONG(uval, inp);
len -= sizeof (u32_t);
if (uval & SRPVAL_EBIT) {
printer(arg, " E");
uval &= ~SRPVAL_EBIT;
}
if (uval != 0) {
printer(arg, " f<%X>", uval);
}
if ((vallen = len) > SHA_DIGESTSIZE)
vallen = SHA_DIGESTSIZE;
printer(arg, " <M2%.*B%s>", len, inp,
len < SHA_DIGESTSIZE ? "?" : "");
INCPTR(vallen, inp);
len -= vallen;
if (len > 0) {
printer(arg, " <PN%.*B>", len, inp);
INCPTR(len, inp);
len = 0;
}
break;
case EAPSRP_LWRECHALLENGE:
printer(arg, " <Challenge%.*B>", len, inp);
INCPTR(len, inp);
len = 0;
break;
default:
break;
}
break;
default:
break;
}
break;
case EAP_RESPONSE:
if (len < 1)
break;
GETCHAR(rtype, inp);
len--;
if (rtype >= 1 &&
rtype <= (int)sizeof (eap_typenames) / (int)sizeof (char *))
printer(arg, " %s", eap_typenames[rtype-1]);
else
printer(arg, " type=0x%x", rtype);
switch (rtype) {
case EAPT_IDENTITY:
if (len > 0) {
printer(arg, " <Name ");
ppp_print_string((char *)inp, len, printer, arg);
printer(arg, ">");
INCPTR(len, inp);
len = 0;
}
break;
case EAPT_NAK:
if (len <= 0) {
printer(arg, " <missing hint>");
break;
}
GETCHAR(rtype, inp);
len--;
printer(arg, " <Suggested-type %02X", rtype);
if (rtype >= 1 &&
rtype < (int)sizeof (eap_typenames) / (int)sizeof (char *))
printer(arg, " (%s)", eap_typenames[rtype-1]);
printer(arg, ">");
break;
case EAPT_MD5CHAP:
if (len <= 0) {
printer(arg, " <missing length>");
break;
}
GETCHAR(vallen, inp);
len--;
if (vallen > len)
goto truncated;
printer(arg, " <Value%.*B>", vallen, inp);
INCPTR(vallen, inp);
len -= vallen;
if (len > 0) {
printer(arg, " <Name ");
ppp_print_string((char *)inp, len, printer, arg);
printer(arg, ">");
INCPTR(len, inp);
len = 0;
} else {
printer(arg, " <No name>");
}
break;
case EAPT_SRP:
if (len < 1)
goto truncated;
GETCHAR(vallen, inp);
len--;
printer(arg, "-%d", vallen);
switch (vallen) {
case EAPSRP_CKEY:
printer(arg, " <A%.*B>", len, inp);
INCPTR(len, inp);
len = 0;
break;
case EAPSRP_CVALIDATOR:
if (len < (int)sizeof (u32_t))
break;
GETLONG(uval, inp);
len -= sizeof (u32_t);
if (uval & SRPVAL_EBIT) {
printer(arg, " E");
uval &= ~SRPVAL_EBIT;
}
if (uval != 0) {
printer(arg, " f<%X>", uval);
}
printer(arg, " <M1%.*B%s>", len, inp,
len == SHA_DIGESTSIZE ? "" : "?");
INCPTR(len, inp);
len = 0;
break;
case EAPSRP_ACK:
break;
case EAPSRP_LWRECHALLENGE:
printer(arg, " <Response%.*B%s>", len, inp,
len == SHA_DIGESTSIZE ? "" : "?");
if ((vallen = len) > SHA_DIGESTSIZE)
vallen = SHA_DIGESTSIZE;
INCPTR(vallen, inp);
len -= vallen;
break;
default:
break;
}
break;
default:
break;
}
break;
case EAP_SUCCESS: /* No payload expected for these! */
case EAP_FAILURE:
default:
break;
truncated:
printer(arg, " <truncated>");
break;
}
if (len > 8)
printer(arg, "%8B...", inp);
else if (len > 0)
printer(arg, "%.*B", len, inp);
INCPTR(len, inp);
return (inp - pstart);
}
#endif /* PRINTPKT_SUPPORT */
#endif /* PPP_SUPPORT && EAP_SUPPORT */