blob: 79146557bd32116975874f9c68cd222647a7d1f3 [file] [log] [blame]
/*
* 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.
*/
/*
* Modification by Beniamino Galvani, Mar 2005
* Implemented EAP-TLS authentication
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pwd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <assert.h>
#include <errno.h>
#include "pppd.h"
#include "pathnames.h"
#include "md5.h"
#include "eap.h"
#ifdef CHAPMS
#include "chap_ms.h"
#endif
#ifdef USE_SRP
#include <t_pwd.h>
#include <t_server.h>
#include <t_client.h>
#include "pppcrypt.h"
#endif /* USE_SRP */
#ifndef SHA_DIGESTSIZE
#define SHA_DIGESTSIZE 20
#endif
#ifdef USE_EAPTLS
#include "eap-tls.h"
#endif /* USE_EAPTLS */
#ifdef CHAPMS
#include "magic.h"
#include "chap_ms.h"
#include "chap-new.h"
#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 */
#endif
/*
* 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 }
};
/*
* Protocol entry points.
*/
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 */
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 */
eap_printpkt, /* print a packet in readable form */
NULL, /* process a received data packet */
1, /* protocol enabled */
"EAP", /* text name of protocol */
NULL, /* text name of corresponding data protocol */
eap_option_list, /* list of command-line options */
NULL, /* check requested options; assign defaults */
NULL, /* configure interface for demand-dial */
NULL /* say whether to bring up link for this pkt */
};
#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 /* USE_SRP */
/* Local forward declarations. */
static void eap_server_timeout (void *arg);
/*
* 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(int unit)
{
eap_state *esp = &eap_states[unit];
BZERO(esp, sizeof (*esp));
esp->es_unit = unit;
esp->es_server.ea_timeout = EAP_DEFTIMEOUT;
esp->es_server.ea_maxrequests = EAP_DEFTRANSMITS;
esp->es_server.ea_id = (u_char)(drand48() * 0x100);
esp->es_client.ea_timeout = EAP_DEFREQTIME;
esp->es_client.ea_maxrequests = EAP_DEFALLOWREQ;
#ifdef USE_EAPTLS
esp->es_client.ea_using_eaptls = 0;
#endif /* USE_EAPTLS */
#ifdef CHAPMS
esp->es_client.digest = chap_find_digest(CHAP_MICROSOFT_V2);
#endif
}
/*
* eap_client_timeout - Give up waiting for the peer to send any
* Request messages.
*/
static void
eap_client_timeout(void *arg)
{
eap_state *esp = (eap_state *) arg;
if (!eap_client_active(esp))
return;
error("EAP: timeout waiting for Request from peer");
auth_withpeer_fail(esp->es_unit, PPP_EAP);
esp->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(int unit, char *localname)
{
eap_state *esp = &eap_states[unit];
/* Save the peer name we're given */
esp->es_client.ea_name = localname;
esp->es_client.ea_namelen = strlen(localname);
esp->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 (esp->es_client.ea_timeout > 0)
TIMEOUT(eap_client_timeout, (void *)esp,
esp->es_client.ea_timeout);
}
/*
* Format a standard EAP Failure message and send it to the peer.
* (Server operation)
*/
static void
eap_send_failure(eap_state *esp)
{
u_char *outp;
outp = outpacket_buf;
MAKEHEADER(outp, PPP_EAP);
PUTCHAR(EAP_FAILURE, outp);
esp->es_server.ea_id++;
PUTCHAR(esp->es_server.ea_id, outp);
PUTSHORT(EAP_HEADERLEN, outp);
output(esp->es_unit, outpacket_buf, EAP_HEADERLEN + PPP_HDRLEN);
esp->es_server.ea_state = eapBadAuth;
auth_peer_fail(esp->es_unit, PPP_EAP);
}
/*
* Format a standard EAP Success message and send it to the peer.
* (Server operation)
*/
static void
eap_send_success(eap_state *esp)
{
u_char *outp;
outp = outpacket_buf;
MAKEHEADER(outp, PPP_EAP);
PUTCHAR(EAP_SUCCESS, outp);
esp->es_server.ea_id++;
PUTCHAR(esp->es_server.ea_id, outp);
PUTSHORT(EAP_HEADERLEN, outp);
output(esp->es_unit, outpacket_buf, PPP_HDRLEN + EAP_HEADERLEN);
auth_peer_success(esp->es_unit, PPP_EAP, 0,
esp->es_server.ea_peer, esp->es_server.ea_peerlen);
}
#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);
return (DesSetkey(dig));
}
static char base64[] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
struct b64state {
u_int32_t bs_bits;
int bs_offs;
};
static int
b64enc(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(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(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 */
/*
* 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(eap_state *esp, 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 */
#ifdef USE_EAPTLS
struct eaptls_session *ets;
int secret_len;
char secret[MAXWORDLEN];
#endif /* USE_EAPTLS */
esp->es_server.ea_timeout = esp->es_savedtime;
#ifdef USE_EAPTLS
esp->es_server.ea_prev_state = esp->es_server.ea_state;
#endif /* USE_EAPTLS */
switch (esp->es_server.ea_state) {
case eapBadAuth:
return;
case eapIdentify:
#ifdef USE_SRP
/* Discard any previous session. */
ts = (struct t_server *)esp->es_server.ea_session;
if (ts != NULL) {
t_serverclose(ts);
esp->es_server.ea_session = NULL;
esp->es_server.ea_skey = NULL;
}
#endif /* USE_SRP */
if (status != 0) {
esp->es_server.ea_state = eapBadAuth;
break;
}
#ifdef USE_SRP
/* If we've got a pseudonym, try to decode to real name. */
if (esp->es_server.ea_peerlen > SRP_PSEUDO_LEN &&
strncmp(esp->es_server.ea_peer, SRP_PSEUDO_ID,
SRP_PSEUDO_LEN) == 0 &&
(esp->es_server.ea_peerlen - SRP_PSEUDO_LEN) * 3 / 4 <
sizeof (secbuf)) {
BZERO(&bs, sizeof (bs));
plen = b64dec(&bs,
esp->es_server.ea_peer + SRP_PSEUDO_LEN,
esp->es_server.ea_peerlen - SRP_PSEUDO_LEN,
secbuf);
toffs = 0;
for (i = 0; i < 5; i++) {
pncrypt_setkey(toffs);
toffs -= 86400;
if (!DesDecrypt(secbuf, clear)) {
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;
esp->es_server.ea_peerlen = plen;
dp = (unsigned char *)esp->es_server.ea_peer;
BCOPY(clear + 1, dp, i);
plen -= i;
dp += i;
sp = secbuf + 8;
while (plen > 0) {
(void) DesDecrypt(sp, dp);
sp += 8;
dp += 8;
plen -= 8;
}
esp->es_server.ea_peer[
esp->es_server.ea_peerlen] = '\0';
dbglog("decoded pseudonym to \"%.*q\"",
esp->es_server.ea_peerlen,
esp->es_server.ea_peer);
} else {
dbglog("failed to decode real name");
/* Stay in eapIdentfy state; requery */
break;
}
}
/* Look up user in secrets database. */
if (get_srp_secret(esp->es_unit, esp->es_server.ea_peer,
esp->es_server.ea_name, (char *)secbuf, 1) != 0) {
/* Set up default in case SRP entry is bad */
esp->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 (esp->es_server.ea_timeout > 0 &&
esp->es_server.ea_timeout < 30)
esp->es_server.ea_timeout = 30;
} else {
break;
}
if ((cp2 = strchr(cp, ':')) == NULL)
break;
*cp2++ = '\0';
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.salt.len = t_fromb64((char *)tpw.saltbuf,
cp2);
tpw.pebuf.salt.data = tpw.saltbuf;
if ((ts = t_serveropenraw(&tpw.pebuf, tce)) == NULL)
break;
esp->es_server.ea_session = (void *)ts;
esp->es_server.ea_state = eapSRP1;
vals[0] = esp->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 */
#ifdef USE_EAPTLS
if (!get_secret(esp->es_unit, esp->es_server.ea_peer,
esp->es_server.ea_name, secret, &secret_len, 1)) {
esp->es_server.ea_state = eapTlsStart;
break;
}
#endif /* USE_EAPTLS */
esp->es_server.ea_state = eapMD5Chall;
break;
#ifdef USE_EAPTLS
case eapTlsStart:
/* Initialize ssl session */
if(!eaptls_init_ssl_server(esp)) {
esp->es_server.ea_state = eapBadAuth;
break;
}
esp->es_server.ea_state = eapTlsRecv;
break;
case eapTlsRecv:
ets = (struct eaptls_session *) esp->es_server.ea_session;
if(ets->alert_sent) {
esp->es_server.ea_state = eapTlsSendAlert;
break;
}
if (status) {
esp->es_server.ea_state = eapBadAuth;
break;
}
ets = (struct eaptls_session *) esp->es_server.ea_session;
if(ets->frag)
esp->es_server.ea_state = eapTlsSendAck;
else
esp->es_server.ea_state = eapTlsSend;
break;
case eapTlsSend:
ets = (struct eaptls_session *) esp->es_server.ea_session;
if(ets->frag)
esp->es_server.ea_state = eapTlsRecvAck;
else
if(SSL_is_init_finished(ets->ssl))
esp->es_server.ea_state = eapTlsRecvClient;
else
/* JJK Add "TLS empty record" message here ??? */
esp->es_server.ea_state = eapTlsRecv;
break;
case eapTlsSendAck:
esp->es_server.ea_state = eapTlsRecv;
break;
case eapTlsRecvAck:
if (status)
{
esp->es_server.ea_state = eapBadAuth;
break;
}
esp->es_server.ea_state = eapTlsSend;
break;
case eapTlsSendAlert:
esp->es_server.ea_state = eapTlsRecvAlertAck;
break;
#endif /* USE_EAPTLS */
case eapSRP1:
#ifdef USE_SRP
ts = (struct t_server *)esp->es_server.ea_session;
if (ts != NULL && status != 0) {
t_serverclose(ts);
esp->es_server.ea_session = NULL;
esp->es_server.ea_skey = NULL;
}
#endif /* USE_SRP */
if (status == 1) {
esp->es_server.ea_state = eapMD5Chall;
} else if (status != 0 || esp->es_server.ea_session == NULL) {
esp->es_server.ea_state = eapBadAuth;
} else {
esp->es_server.ea_state = eapSRP2;
}
break;
case eapSRP2:
#ifdef USE_SRP
ts = (struct t_server *)esp->es_server.ea_session;
if (ts != NULL && status != 0) {
t_serverclose(ts);
esp->es_server.ea_session = NULL;
esp->es_server.ea_skey = NULL;
}
#endif /* USE_SRP */
if (status != 0 || esp->es_server.ea_session == NULL) {
esp->es_server.ea_state = eapBadAuth;
} else {
esp->es_server.ea_state = eapSRP3;
}
break;
case eapSRP3:
case eapSRP4:
#ifdef USE_SRP
ts = (struct t_server *)esp->es_server.ea_session;
if (ts != NULL && status != 0) {
t_serverclose(ts);
esp->es_server.ea_session = NULL;
esp->es_server.ea_skey = NULL;
}
#endif /* USE_SRP */
if (status != 0 || esp->es_server.ea_session == NULL) {
esp->es_server.ea_state = eapBadAuth;
} else {
esp->es_server.ea_state = eapOpen;
}
break;
#ifdef CHAPMS
case eapMSCHAPv2Chall:
#endif
case eapMD5Chall:
if (status != 0) {
esp->es_server.ea_state = eapBadAuth;
} else {
esp->es_server.ea_state = eapOpen;
}
break;
default:
esp->es_server.ea_state = eapBadAuth;
break;
}
if (esp->es_server.ea_state == eapBadAuth)
eap_send_failure(esp);
#ifdef USE_EAPTLS
dbglog("EAP id=0x%2x '%s' -> '%s'", esp->es_server.ea_id, eap_state_name(esp->es_server.ea_prev_state), eap_state_name(esp->es_server.ea_state));
#endif /* USE_EAPTLS */
}
#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=<auth_string> M=<message>"
* where
* <auth_string> is the Authenticator Response (mutual auth)
* <message> is a text message
*
* However, some versions of Windows (win98 tested) do not know
* about the M=<message> 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 <say@real.kharkov.ua> 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
* 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)
*/
static void
eap_send_request(eap_state *esp)
{
u_char *outp;
u_char *lenloc;
u_char *ptr;
int outlen;
int challen;
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 (esp->es_server.ea_state < eapIdentify &&
esp->es_server.ea_state != eapInitial) {
esp->es_server.ea_state = eapIdentify;
if (explicit_remote) {
/*
* If we already know the peer's
* unauthenticated name, then there's no
* reason to ask. Go to next state instead.
*/
esp->es_server.ea_peer = remote_name;
esp->es_server.ea_peerlen = strlen(remote_name);
eap_figure_next_state(esp, 0);
}
}
if (esp->es_server.ea_maxrequests > 0 &&
esp->es_server.ea_requests >= esp->es_server.ea_maxrequests) {
if (esp->es_server.ea_responses > 0)
error("EAP: too many Requests sent");
else
error("EAP: no response to Requests");
eap_send_failure(esp);
return;
}
outp = outpacket_buf;
MAKEHEADER(outp, PPP_EAP);
PUTCHAR(EAP_REQUEST, outp);
PUTCHAR(esp->es_server.ea_id, outp);
lenloc = outp;
INCPTR(2, outp);
switch (esp->es_server.ea_state) {
case eapIdentify:
PUTCHAR(EAPT_IDENTITY, outp);
str = "Name";
challen = strlen(str);
BCOPY(str, outp, challen);
INCPTR(challen, outp);
break;
case eapMD5Chall:
PUTCHAR(EAPT_MD5CHAP, outp);
/*
* pick a random challenge length between
* MIN_CHALLENGE_LENGTH and MAX_CHALLENGE_LENGTH
*/
challen = (drand48() *
(MAX_CHALLENGE_LENGTH - MIN_CHALLENGE_LENGTH)) +
MIN_CHALLENGE_LENGTH;
PUTCHAR(challen, outp);
esp->es_challen = challen;
ptr = esp->es_challenge;
while (--challen >= 0)
*ptr++ = (u_char) (drand48() * 0x100);
BCOPY(esp->es_challenge, outp, esp->es_challen);
INCPTR(esp->es_challen, outp);
BCOPY(esp->es_server.ea_name, outp, esp->es_server.ea_namelen);
INCPTR(esp->es_server.ea_namelen, outp);
break;
#ifdef CHAPMS
case eapMSCHAPv2Chall:
challen = 0x10;
esp->es_challen = challen;
esp->es_challenge[0] = challen;
random_bytes(&esp->es_challenge[1], 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);
PUTCHAR(EAP_TLS_FLAGS_START, outp);
eap_figure_next_state(esp, 0);
break;
case eapTlsSend:
eaptls_send(esp->es_server.ea_session, &outp);
eap_figure_next_state(esp, 0);
break;
case eapTlsSendAck:
PUTCHAR(EAPT_TLS, outp);
PUTCHAR(0, outp);
eap_figure_next_state(esp, 0);
break;
case eapTlsSendAlert:
eaptls_send(esp->es_server.ea_session, &outp);
eap_figure_next_state(esp, 0);
break;
#endif /* USE_EAPTLS */
#ifdef USE_SRP
case eapSRP1:
PUTCHAR(EAPT_SRP, outp);
PUTCHAR(EAPSRP_CHALLENGE, outp);
PUTCHAR(esp->es_server.ea_namelen, outp);
BCOPY(esp->es_server.ea_name, outp, esp->es_server.ea_namelen);
INCPTR(esp->es_server.ea_namelen, outp);
ts = (struct t_server *)esp->es_server.ea_session;
assert(ts != NULL);
PUTCHAR(ts->s.len, outp);
BCOPY(ts->s.data, outp, 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);
BCOPY(ts->g.data, outp, ts->g.len);
INCPTR(ts->g.len, outp);
}
if (ts->n.len != sizeof (wkmodulus) ||
BCMP(ts->n.data, wkmodulus, sizeof (wkmodulus)) != 0) {
BCOPY(ts->n.data, outp, ts->n.len);
INCPTR(ts->n.len, outp);
}
break;
case eapSRP2:
PUTCHAR(EAPT_SRP, outp);
PUTCHAR(EAPSRP_SKEY, outp);
ts = (struct t_server *)esp->es_server.ea_session;
assert(ts != NULL);
BCOPY(ts->B.data, outp, 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 *)esp->es_server.ea_session;
assert(ts != NULL);
BCOPY(t_serverresponse(ts), outp, SHA_DIGESTSIZE);
INCPTR(SHA_DIGESTSIZE, outp);
if (pncrypt_setkey(0)) {
/* Generate pseudonym */
optr = outp;
cp = (unsigned char *)esp->es_server.ea_peer;
if ((j = i = esp->es_server.ea_peerlen) > 7)
j = 7;
clear[0] = i;
BCOPY(cp, clear + 1, j);
i -= j;
cp += j;
if (!DesEncrypt(clear, cipher)) {
dbglog("no DES here; not generating pseudonym");
break;
}
BZERO(&b64, sizeof (b64));
outp++; /* space for pseudonym length */
outp += b64enc(&b64, cipher, 8, outp);
while (i >= 8) {
(void) DesEncrypt(cp, cipher);
outp += b64enc(&b64, cipher, 8, outp);
cp += 8;
i -= 8;
}
if (i > 0) {
BCOPY(cp, clear, i);
cp += i;
while (i < 8) {
*cp++ = drand48() * 0x100;
i++;
}
(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, &esp->es_server.ea_id, 1);
SHA1Update(&ctxt, esp->es_server.ea_skey,
SESSION_KEY_LEN);
SHA1Update(&ctxt, esp->es_server.ea_peer,
esp->es_server.ea_peerlen);
while (optr < outp) {
SHA1Final(dig, &ctxt);
cp = dig;
while (cp < dig + SHA_DIGESTSIZE)
*optr++ ^= *cp++;
SHA1Init(&ctxt);
SHA1Update(&ctxt, &esp->es_server.ea_id, 1);
SHA1Update(&ctxt, esp->es_server.ea_skey,
SESSION_KEY_LEN);
SHA1Update(&ctxt, optr - SHA_DIGESTSIZE,
SHA_DIGESTSIZE);
}
}
break;
case eapSRP4:
PUTCHAR(EAPT_SRP, outp);
PUTCHAR(EAPSRP_LWRECHALLENGE, outp);
challen = MIN_CHALLENGE_LENGTH +
((MAX_CHALLENGE_LENGTH - MIN_CHALLENGE_LENGTH) * drand48());
esp->es_challen = challen;
ptr = esp->es_challenge;
while (--challen >= 0)
*ptr++ = drand48() * 0x100;
BCOPY(esp->es_challenge, outp, esp->es_challen);
INCPTR(esp->es_challen, outp);
break;
#endif /* USE_SRP */
default:
return;
}
outlen = (outp - outpacket_buf) - PPP_HDRLEN;
PUTSHORT(outlen, lenloc);
output(esp->es_unit, outpacket_buf, outlen + PPP_HDRLEN);
esp->es_server.ea_requests++;
if (esp->es_server.ea_timeout > 0)
TIMEOUT(eap_server_timeout, esp, esp->es_server.ea_timeout);
}
/*
* 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(int unit, char *localname)
{
eap_state *esp = &eap_states[unit];
/* Save the name we're given. */
esp->es_server.ea_name = localname;
esp->es_server.ea_namelen = strlen(localname);
esp->es_savedtime = esp->es_server.ea_timeout;
/* Lower layer up yet? */
if (esp->es_server.ea_state == eapInitial ||
esp->es_server.ea_state == eapPending) {
esp->es_server.ea_state = eapPending;
return;
}
esp->es_server.ea_state = eapPending;
/* ID number not updated here intentionally; hashed into M1 */
eap_send_request(esp);
}
/*
* eap_server_timeout - Retransmission timer for sending Requests
* expired.
*/
static void
eap_server_timeout(void *arg)
{
#ifdef USE_EAPTLS
u_char *outp;
u_char *lenloc;
int outlen;
#endif /* USE_EAPTLS */
eap_state *esp = (eap_state *) arg;
if (!eap_server_active(esp))
return;
#ifdef USE_EAPTLS
switch(esp->es_server.ea_prev_state) {
/*
* In eap-tls the state changes after a request, so we return to
* previous state ...
*/
case(eapTlsStart):
case(eapTlsSendAck):
esp->es_server.ea_state = esp->es_server.ea_prev_state;
break;
/*
* ... or resend the stored data
*/
case(eapTlsSend):
case(eapTlsSendAlert):
outp = outpacket_buf;
MAKEHEADER(outp, PPP_EAP);
PUTCHAR(EAP_REQUEST, outp);
PUTCHAR(esp->es_server.ea_id, outp);
lenloc = outp;
INCPTR(2, outp);
eaptls_retransmit(esp->es_server.ea_session, &outp);
outlen = (outp - outpacket_buf) - PPP_HDRLEN;
PUTSHORT(outlen, lenloc);
output(esp->es_unit, outpacket_buf, outlen + PPP_HDRLEN);
esp->es_server.ea_requests++;
if (esp->es_server.ea_timeout > 0)
TIMEOUT(eap_server_timeout, esp, esp->es_server.ea_timeout);
return;
default:
break;
}
#endif /* USE_EAPTLS */
/* EAP ID number must not change on timeout. */
eap_send_request(esp);
}
/*
* 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)
{
eap_state *esp = (eap_state *)arg;
if (esp->es_server.ea_state != eapOpen &&
esp->es_server.ea_state != eapSRP4)
return;
esp->es_server.ea_requests = 0;
esp->es_server.ea_state = eapIdentify;
eap_figure_next_state(esp, 0);
esp->es_server.ea_id++;
eap_send_request(esp);
}
static void
srp_lwrechallenge(void *arg)
{
eap_state *esp = (eap_state *)arg;
if (esp->es_server.ea_state != eapOpen ||
esp->es_server.ea_type != EAPT_SRP)
return;
esp->es_server.ea_requests = 0;
esp->es_server.ea_state = eapSRP4;
esp->es_server.ea_id++;
eap_send_request(esp);
}
/*
* 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(int unit)
{
eap_state *esp = &eap_states[unit];
/* Discard any (possibly authenticated) peer name. */
if (esp->es_server.ea_peer != NULL &&
esp->es_server.ea_peer != remote_name)
free(esp->es_server.ea_peer);
esp->es_server.ea_peer = NULL;
if (esp->es_client.ea_peer != NULL)
free(esp->es_client.ea_peer);
esp->es_client.ea_peer = NULL;
esp->es_client.ea_state = eapClosed;
esp->es_server.ea_state = eapClosed;
}
/*
* eap_lowerdown - The lower layer is now down.
*
* Cancel all timeouts and return to initial state.
*/
static void
eap_lowerdown(int unit)
{
eap_state *esp = &eap_states[unit];
if (eap_client_active(esp) && esp->es_client.ea_timeout > 0) {
UNTIMEOUT(eap_client_timeout, (void *)esp);
}
if (eap_server_active(esp)) {
if (esp->es_server.ea_timeout > 0) {
UNTIMEOUT(eap_server_timeout, (void *)esp);
}
} else {
if ((esp->es_server.ea_state == eapOpen ||
esp->es_server.ea_state == eapSRP4) &&
esp->es_rechallenge > 0) {
UNTIMEOUT(eap_rechallenge, (void *)esp);
}
if (esp->es_server.ea_state == eapOpen &&
esp->es_lwrechallenge > 0) {
UNTIMEOUT(srp_lwrechallenge, (void *)esp);
}
}
esp->es_client.ea_state = esp->es_server.ea_state = eapInitial;
esp->es_client.ea_requests = esp->es_server.ea_requests = 0;
}
/*
* eap_protrej - Peer doesn't speak this protocol.
*
* This shouldn't happen. If it does, it represents authentication
* failure.
*/
static void
eap_protrej(int unit)
{
eap_state *esp = &eap_states[unit];
if (eap_client_active(esp)) {
error("EAP authentication failed due to Protocol-Reject");
auth_withpeer_fail(unit, PPP_EAP);
}
if (eap_server_active(esp)) {
error("EAP authentication of peer failed on Protocol-Reject");
auth_peer_fail(unit, PPP_EAP);
}
eap_lowerdown(unit);
}
/*
* Format and send a regular EAP Response message.
*/
static void
eap_send_response(eap_state *esp, u_char id, u_char typenum,
u_char *str, int lenstr)
{
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 + sizeof (u_char) + lenstr;
PUTSHORT(msglen, outp);
PUTCHAR(typenum, outp);
if (lenstr > 0) {
BCOPY(str, outp, lenstr);
}
output(esp->es_unit, outpacket_buf, PPP_HDRLEN + msglen);
}
/*
* Format and send an MD5-Challenge EAP Response message.
*/
static void
eap_chap_response(eap_state *esp, u_char id, u_char *hash,
char *name, int namelen)
{
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 + 2 * sizeof (u_char) + MD5_SIGNATURE_SIZE +
namelen;
PUTSHORT(msglen, outp);
PUTCHAR(EAPT_MD5CHAP, outp);
PUTCHAR(MD5_SIGNATURE_SIZE, outp);
BCOPY(hash, outp, MD5_SIGNATURE_SIZE);
INCPTR(MD5_SIGNATURE_SIZE, outp);
if (namelen > 0) {
BCOPY(name, outp, namelen);
}
output(esp->es_unit, outpacket_buf, PPP_HDRLEN + msglen);
}
#ifdef USE_SRP
/*
* Format and send a SRP EAP Response message.
*/
static void
eap_srp_response(eap_state *esp, u_char id, u_char subtypenum,
u_char *str, int lenstr)
{
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 + 2 * sizeof (u_char) + lenstr;
PUTSHORT(msglen, outp);
PUTCHAR(EAPT_SRP, outp);
PUTCHAR(subtypenum, outp);
if (lenstr > 0) {
BCOPY(str, outp, lenstr);
}
output(esp->es_unit, outpacket_buf, PPP_HDRLEN + msglen);
}
/*
* Format and send a SRP EAP Client Validator Response message.
*/
static void
eap_srpval_response(eap_state *esp, u_char id, u_int32_t flags, u_char *str)
{
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 + 2 * sizeof (u_char) + sizeof (u_int32_t) +
SHA_DIGESTSIZE;
PUTSHORT(msglen, outp);
PUTCHAR(EAPT_SRP, outp);
PUTCHAR(EAPSRP_CVALIDATOR, outp);
PUTLONG(flags, outp);
BCOPY(str, outp, SHA_DIGESTSIZE);
output(esp->es_unit, outpacket_buf, PPP_HDRLEN + msglen);
}
#endif /* USE_SRP */
#ifdef USE_EAPTLS
/*
* Send an EAP-TLS response message with tls data
*/
static void
eap_tls_response(eap_state *esp, u_char id)
{
u_char *outp;
int outlen;
u_char *lenloc;
outp = outpacket_buf;
MAKEHEADER(outp, PPP_EAP);
PUTCHAR(EAP_RESPONSE, outp);
PUTCHAR(id, outp);
lenloc = outp;
INCPTR(2, outp);
/*
If the id in the request is unchanged, we must retransmit
the old data
*/
if(id == esp->es_client.ea_id)
eaptls_retransmit(esp->es_client.ea_session, &outp);
else
eaptls_send(esp->es_client.ea_session, &outp);
outlen = (outp - outpacket_buf) - PPP_HDRLEN;
PUTSHORT(outlen, lenloc);
output(esp->es_unit, outpacket_buf, PPP_HDRLEN + outlen);
esp->es_client.ea_id = id;
}
/*
* Send an EAP-TLS ack
*/
static void
eap_tls_sendack(eap_state *esp, u_char id)
{
u_char *outp;
int outlen;
u_char *lenloc;
outp = outpacket_buf;
MAKEHEADER(outp, PPP_EAP);
PUTCHAR(EAP_RESPONSE, outp);
PUTCHAR(id, outp);
esp->es_client.ea_id = id;
lenloc = outp;
INCPTR(2, outp);
PUTCHAR(EAPT_TLS, outp);
PUTCHAR(0, outp);
outlen = (outp - outpacket_buf) - PPP_HDRLEN;
PUTSHORT(outlen, lenloc);
output(esp->es_unit, outpacket_buf, PPP_HDRLEN + outlen);
}
#endif /* USE_EAPTLS */
static void
eap_send_nak(eap_state *esp, u_char id, u_char type)
{
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 + 2 * sizeof (u_char);
PUTSHORT(msglen, outp);
PUTCHAR(EAPT_NAK, outp);
PUTCHAR(type, outp);
output(esp->es_unit, outpacket_buf, PPP_HDRLEN + msglen);
}
#ifdef USE_SRP
static char *
name_of_pn_file(void)
{
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) {
dbglog("pseudonym file: %s", path);
pnlogged = 1;
}
return (path);
}
static int
open_pn_file(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(void)
{
char *path;
if ((path = name_of_pn_file()) != NULL) {
(void) unlink(path);
(void) free(path);
}
}
static void
write_pseudonym(eap_state *esp, u_char *inp, int len, int id)
{
u_char val;
u_char *datp, *digp;
SHA1_CTX ctxt;
u_char dig[SHA_DIGESTSIZE];
int dsize, fd, olen = len;
/*
* 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, esp->es_client.ea_skey, SESSION_KEY_LEN);
if (len > 0) {
SHA1Update(&ctxt, datp, SHA_DIGESTSIZE);
} else {
SHA1Update(&ctxt, esp->es_client.ea_name,
esp->es_client.ea_namelen);
}
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) {
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) {
dbglog("EAP: error saving pseudonym: %m");
return;
}
len = write(fd, inp + 1, *inp);
if (close(fd) != -1 && len == *inp) {
dbglog("EAP: saved pseudonym");
esp->es_usedpseudo = 0;
} else {
dbglog("EAP: failed to save pseudonym");
remove_pn_file();
}
}
#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(eap_state *esp, u_char *inp, int id, int len)
{
u_char typenum;
u_char vallen;
int secret_len;
char secret[MAXWORDLEN];
char rhostname[256];
MD5_CTX mdContext;
u_char hash[MD5_SIGNATURE_SIZE];
#ifdef USE_EAPTLS
u_char flags;
struct eaptls_session *ets = esp->es_client.ea_session;
#endif /* USE_EAPTLS */
#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 */
/*
* Ignore requests if we're not open
*/
if (esp->es_client.ea_state <= eapClosed)
return;
/*
* 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.
*/
esp->es_client.ea_requests++;
if (esp->es_client.ea_maxrequests != 0 &&
esp->es_client.ea_requests > esp->es_client.ea_maxrequests) {
info("EAP: received too many Request messages");
if (esp->es_client.ea_timeout > 0) {
UNTIMEOUT(eap_client_timeout, (void *)esp);
}
auth_withpeer_fail(esp->es_unit, PPP_EAP);
return;
}
if (len <= 0) {
error("EAP: empty Request message discarded");
return;
}
GETCHAR(typenum, inp);
len--;
switch (typenum) {
case EAPT_IDENTITY:
if (len > 0)
info("EAP: Identity prompt \"%.*q\"", len, inp);
#ifdef USE_SRP
if (esp->es_usepseudo &&
(esp->es_usedpseudo == 0 ||
(esp->es_usedpseudo == 1 &&
id == esp->es_client.ea_id))) {
esp->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(esp, id, typenum,
rhostname, len + SRP_PSEUDO_LEN);
}
(void) close(fd);
if (len > 0)
break;
}
}
/* Stop using pseudonym now. */
if (esp->es_usepseudo && esp->es_usedpseudo != 2) {
remove_pn_file();
esp->es_usedpseudo = 2;
}
#endif /* USE_SRP */
eap_send_response(esp, id, typenum, (u_char *)esp->es_client.ea_name,
esp->es_client.ea_namelen);
break;
case EAPT_NOTIFICATION:
if (len > 0)
info("EAP: Notification \"%.*q\"", len, inp);
eap_send_response(esp, 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.
*/
warn("EAP: unexpected Nak in Request; ignored");
/* Return because we're waiting for something real. */
return;
case EAPT_MD5CHAP:
if (len < 1) {
error("EAP: received MD5-Challenge with no data");
/* Bogus request; wait for something real. */
return;
}
GETCHAR(vallen, inp);
len--;
if (vallen < 8 || vallen > len) {
error("EAP: MD5-Challenge with bad length %d (8..%d)",
vallen, len);
/* Try something better. */
eap_send_nak(esp, id, EAPT_SRP);
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));
/*
* 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 MD5 secret for auth to %q", rhostname);
eap_send_nak(esp, id, EAPT_SRP);
break;
}
MD5_Init(&mdContext);
typenum = id;
MD5_Update(&mdContext, &typenum, 1);
MD5_Update(&mdContext, (u_char *)secret, secret_len);
BZERO(secret, sizeof (secret));
MD5_Update(&mdContext, inp, vallen);
MD5_Final(hash, &mdContext);
eap_chap_response(esp, id, hash, esp->es_client.ea_name,
esp->es_client.ea_namelen);
break;
#ifdef USE_EAPTLS
case EAPT_TLS:
switch(esp->es_client.ea_state) {
case eapListen:
if (len < 1) {
error("EAP: received EAP-TLS Listen packet with no data");
/* Bogus request; wait for something real. */
return;
}
GETCHAR(flags, inp);
if(flags & EAP_TLS_FLAGS_START){
esp->es_client.ea_using_eaptls = 1;
if (explicit_remote){
esp->es_client.ea_peer = strdup(remote_name);
esp->es_client.ea_peerlen = strlen(remote_name);
} else
esp->es_client.ea_peer = NULL;
/* Init ssl session */
if(!eaptls_init_ssl_client(esp)) {
dbglog("cannot init ssl");
eap_send_nak(esp, id, EAPT_MSCHAPV2);
esp->es_client.ea_using_eaptls = 0;
break;
}
ets = esp->es_client.ea_session;
eap_tls_response(esp, id);
esp->es_client.ea_state = (ets->frag ? eapTlsRecvAck : eapTlsRecv);
break;
}
/* The server has sent a bad start packet. */
eap_send_nak(esp, id, EAPT_MSCHAPV2);
break;
case eapTlsRecvAck:
eap_tls_response(esp, id);
esp->es_client.ea_state = (ets->frag ? eapTlsRecvAck : eapTlsRecv);
break;
case eapTlsRecv:
if (len < 1) {
error("EAP: discarding EAP-TLS Receive packet with no data");
/* Bogus request; wait for something real. */
return;
}
eaptls_receive(ets, inp, len);
if(ets->frag) {
eap_tls_sendack(esp, id);
esp->es_client.ea_state = eapTlsRecv;
break;
}
if(ets->alert_recv) {
eap_tls_sendack(esp, id);
esp->es_client.ea_state = eapTlsRecvFailure;
break;
}
/* Check if TLS handshake is finished */
if(eaptls_is_init_finished(ets)) {
#ifdef MPPE
eaptls_gen_mppe_keys(ets, 1);
#endif
eaptls_free_session(ets);
eap_tls_sendack(esp, id);
esp->es_client.ea_state = eapTlsRecvSuccess;
break;
}
eap_tls_response(esp,id);
esp->es_client.ea_state = (ets->frag ? eapTlsRecvAck : eapTlsRecv);
break;
default:
eap_send_nak(esp, id, EAPT_MSCHAPV2);
esp->es_client.ea_using_eaptls = 0;
break;
}
break;
#endif /* USE_EAPTLS */
#ifdef USE_SRP
case EAPT_SRP:
if (len < 1) {
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 (esp->es_client.ea_session != NULL) {
tc = (struct t_client *)esp->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 != esp->es_client.ea_id) {
t_clientclose(tc);
esp->es_client.ea_session = NULL;
tc = NULL;
}
}
/* No session key just yet */
esp->es_client.ea_skey = NULL;
if (tc == NULL) {
GETCHAR(vallen, inp);
len--;
if (vallen >= len) {
error("EAP: badly-formed SRP Challenge"
" (name)");
/* Ignore badly-formed messages */
return;
}
BCOPY(inp, rhostname, 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 (esp->es_client.ea_peer != NULL)
free(esp->es_client.ea_peer);
esp->es_client.ea_peer = strdup(rhostname);
esp->es_client.ea_peerlen = strlen(rhostname);
GETCHAR(vallen, inp);
len--;
if (vallen >= len) {
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) {
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(esp->es_client.ea_name,
&Nval, &gval, &sval);
if (tc == NULL) {
eap_send_nak(esp, id, EAPT_MD5CHAP);
break;
}
esp->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 *)esp->es_client.ea_session;
if (tc == NULL) {
warn("EAP: peer sent Subtype 2 without 1");
eap_send_nak(esp, id, EAPT_MD5CHAP);
break;
}
if (esp->es_client.ea_skey != NULL) {
/*
* ID number should not change here. Warn
* if it does (but otherwise ignore).
*/
if (id != esp->es_client.ea_id) {
warn("EAP: ID changed from %d to %d "
"in SRP Subtype 2 rexmit",
esp->es_client.ea_id, id);
}
} else {
if (get_srp_secret(esp->es_unit,
esp->es_client.ea_name,
esp->es_client.ea_peer, secret, 0) == 0) {
/*
* Can't work with this peer because
* the secret is missing. Just give
* up.
*/
eap_send_nak(esp, id, EAPT_MD5CHAP);
break;
}
Bval.data = inp;
Bval.len = len;
t_clientpasswd(tc, secret);
BZERO(secret, sizeof (secret));
esp->es_client.ea_skey =
t_clientgetkey(tc, &Bval);
if (esp->es_client.ea_skey == NULL) {
/* Server is rogue; stop now */
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 *)esp->es_client.ea_session;
if (tc == NULL || esp->es_client.ea_skey == NULL) {
warn("EAP: peer sent Subtype 3 without 1/2");
eap_send_nak(esp, 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 (esp->es_client.ea_state == eapOpen) {
if (id != esp->es_client.ea_id) {
warn("EAP: ID changed from %d to %d "
"in SRP Subtype 3 rexmit",
esp->es_client.ea_id, id);
}
} else {
len -= sizeof (u_int32_t) + SHA_DIGESTSIZE;
if (len < 0 || t_clientverify(tc, inp +
sizeof (u_int32_t)) != 0) {
error("EAP: SRP server verification "
"failed");
goto client_failure;
}
GETLONG(esp->es_client.ea_keyflags, inp);
/* Save pseudonym if user wants it. */
if (len > 0 && esp->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) {
warn("EAP: malformed Lightweight rechallenge");
return;
}
SHA1Init(&ctxt);
vals[0] = id;
SHA1Update(&ctxt, vals, 1);
SHA1Update(&ctxt, esp->es_client.ea_skey,
SESSION_KEY_LEN);
SHA1Update(&ctxt, inp, len);
SHA1Update(&ctxt, esp->es_client.ea_name,
esp->es_client.ea_namelen);
SHA1Final(dig, &ctxt);
eap_srp_response(esp, id, EAPSRP_LWRECHALLENGE, dig,
SHA_DIGESTSIZE);
break;
default:
error("EAP: unknown SRP Subtype %d", vallen);
eap_send_nak(esp, id, EAPT_MD5CHAP);
break;
}
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);
eap_send_nak(esp, id, EAPT_SRP);
break;
}
if (esp->es_client.ea_timeout > 0) {
UNTIMEOUT(eap_client_timeout, (void *)esp);
TIMEOUT(eap_client_timeout, (void *)esp,
esp->es_client.ea_timeout);
}
return;
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 */
}
/*
* eap_response - Receive EAP Response message (server mode).
*/
static void
eap_response(eap_state *esp, u_char *inp, int id, int len)
{
u_char typenum;
u_char vallen;
int secret_len;
char secret[MAXSECRETLEN];
char rhostname[256];
MD5_CTX 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];
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
*/
if (esp->es_server.ea_state <= eapClosed)
return;
if (esp->es_server.ea_id != id) {
dbglog("EAP: discarding Response %d; expected ID %d", id,
esp->es_server.ea_id);
return;
}
esp->es_server.ea_responses++;
if (len <= 0) {
error("EAP: empty Response message discarded");
return;
}
GETCHAR(typenum, inp);
len--;
switch (typenum) {
case EAPT_IDENTITY:
if (esp->es_server.ea_state != eapIdentify) {
dbglog("EAP discarding unwanted Identify \"%.q\"", len,
inp);
break;
}
info("EAP: unauthenticated peer name \"%.*q\"", len, inp);
if (esp->es_server.ea_peer != NULL &&
esp->es_server.ea_peer != remote_name)
free(esp->es_server.ea_peer);
esp->es_server.ea_peer = malloc(len + 1);
if (esp->es_server.ea_peer == NULL) {
esp->es_server.ea_peerlen = 0;
eap_figure_next_state(esp, 1);
break;
}
BCOPY(inp, esp->es_server.ea_peer, len);
esp->es_server.ea_peer[len] = '\0';
esp->es_server.ea_peerlen = len;
eap_figure_next_state(esp, 0);
break;
#ifdef USE_EAPTLS
case EAPT_TLS:
switch(esp->es_server.ea_state) {
case eapTlsRecv:
ets = (struct eaptls_session *) esp->es_server.ea_session;
eap_figure_next_state(esp,
eaptls_receive(esp->es_server.ea_session, inp, len));
if(ets->alert_recv) {
eap_send_failure(esp);
break;
}
break;
case eapTlsRecvAck:
if(len > 1) {
dbglog("EAP-TLS ACK with extra data");
}
eap_figure_next_state(esp, 0);
break;
case eapTlsRecvClient:
/* Receive authentication response from client */
if (len > 0) {
GETCHAR(flags, inp);
if(len == 1 && !flags) { /* Ack = ok */
#ifdef MPPE
eaptls_gen_mppe_keys( esp->es_server.ea_session, 0 );
#endif
eap_send_success(esp);
}
else { /* failure */
warn("Server authentication failed");
eap_send_failure(esp);
}
}
else
warn("Bogus EAP-TLS packet received from client");
eaptls_free_session(esp->es_server.ea_session);
break;
case eapTlsRecvAlertAck:
eap_send_failure(esp);
break;
default:
eap_figure_next_state(esp, 1);
break;
}
break;
#endif /* USE_EAPTLS */
case EAPT_NOTIFICATION:
dbglog("EAP unexpected Notification; response discarded");
break;
case EAPT_NAK:
if (len < 1) {
info("EAP: Nak Response with no suggested protocol");
eap_figure_next_state(esp, 1);
break;
}
GETCHAR(vallen, inp);
len--;
if (!explicit_remote && esp->es_server.ea_state == eapIdentify){
/* Peer cannot Nak Identify Request */
eap_figure_next_state(esp, 1);
break;
}
switch (vallen) {
case EAPT_SRP:
/* Run through SRP validator selection again. */
esp->es_server.ea_state = eapIdentify;
eap_figure_next_state(esp, 0);
break;
case EAPT_MD5CHAP:
esp->es_server.ea_state = eapMD5Chall;
break;
#ifdef USE_EAPTLS
/* Send EAP-TLS start packet */
case EAPT_TLS:
esp->es_server.ea_state = eapTlsStart;
break;
#endif /* USE_EAPTLS */
#ifdef CHAPMS
case EAPT_MSCHAPV2:
info("EAP: peer proposes MSCHAPv2");
esp->es_server.ea_state = eapMSCHAPv2Chall;
break;
#endif /* CHAPMS */
default:
dbglog("EAP: peer requesting unknown Type %d", vallen);
switch (esp->es_server.ea_state) {
case eapSRP1:
case eapSRP2:
case eapSRP3:
esp->es_server.ea_state = eapMD5Chall;
break;
case eapMD5Chall:
case eapSRP4:
esp->es_server.ea_state = eapIdentify;
eap_figure_next_state(esp, 0);
break;
default:
break;
}
break;
}
break;
case EAPT_MD5CHAP:
if (esp->es_server.ea_state != eapMD5Chall) {
error("EAP: unexpected MD5-Response");
eap_figure_next_state(esp, 1);
break;
}
if (len < 1) {
error("EAP: received MD5-Response with no data");
eap_figure_next_state(esp, 1);
break;
}
GETCHAR(vallen, inp);
len--;
if (vallen != 16 || vallen > len) {
error("EAP: MD5-Response with bad 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));
/*
* Get the secret for authenticating the specified
* host.
*/
if (!get_secret(esp->es_unit, rhostname,
esp->es_server.ea_name, secret, &secret_len, 1)) {
dbglog("EAP: no MD5 secret for auth of %q", rhostname);
eap_send_failure(esp);
break;
}
MD5_Init(&mdContext);
MD5_Update(&mdContext, &esp->es_server.ea_id, 1);
MD5_Update(&mdContext, (u_char *)secret, secret_len);
BZERO(secret, sizeof (secret));
MD5_Update(&mdContext, esp->es_challenge, esp->es_challen);
MD5_Final(hash, &mdContext);
if (BCMP(hash, inp, MD5_SIGNATURE_SIZE) != 0) {
eap_send_failure(esp);
break;
}
esp->es_server.ea_type = EAPT_MD5CHAP;
eap_send_success(esp);
eap_figure_next_state(esp, 0);
if (esp->es_rechallenge != 0)
TIMEOUT(eap_rechallenge, esp, esp->es_rechallenge);
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));
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,
&eap_chapms2_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) {
error("EAP: empty SRP Response");
eap_figure_next_state(esp, 1);
break;
}
GETCHAR(typenum, inp);
len--;
switch (typenum) {
case EAPSRP_CKEY:
if (esp->es_server.ea_state != eapSRP1) {
error("EAP: unexpected SRP Subtype 1 Response");
eap_figure_next_state(esp, 1);
break;
}
A.data = inp;
A.len = len;
ts = (struct t_server *)esp->es_server.ea_session;
assert(ts != NULL);
esp->es_server.ea_skey = t_servergetkey(ts, &A);
if (esp->es_server.ea_skey == NULL) {
/* Client's A value is bogus; terminate now */
error("EAP: bogus A value from client");
eap_send_failure(esp);
} else {
eap_figure_next_state(esp, 0);
}
break;
case EAPSRP_CVALIDATOR:
if (esp->es_server.ea_state != eapSRP2) {
error("EAP: unexpected SRP Subtype 2 Response");
eap_figure_next_state(esp, 1);
break;
}
if (len < sizeof (u_int32_t) + SHA_DIGESTSIZE) {
error("EAP: M1 length %d < %d", len,
sizeof (u_int32_t) + SHA_DIGESTSIZE);
eap_figure_next_state(esp, 1);
break;
}
GETLONG(esp->es_server.ea_keyflags, inp);
ts = (struct t_server *)esp->es_server.ea_session;
assert(ts != NULL);
if (t_serververify(ts, inp)) {
info("EAP: unable to validate client identity");
eap_send_failure(esp);
break;
}
eap_figure_next_state(esp, 0);
break;
case EAPSRP_ACK:
if (esp->es_server.ea_state != eapSRP3) {
error("EAP: unexpected SRP Subtype 3 Response");
eap_send_failure(esp);
break;
}
esp->es_server.ea_type = EAPT_SRP;
eap_send_success(esp);
eap_figure_next_state(esp, 0);
if (esp->es_rechallenge != 0)
TIMEOUT(eap_rechallenge, esp,
esp->es_rechallenge);
if (esp->es_lwrechallenge != 0)
TIMEOUT(srp_lwrechallenge, esp,
esp->es_lwrechallenge);
break;
case EAPSRP_LWRECHALLENGE:
if (esp->es_server.ea_state != eapSRP4) {
info("EAP: unexpected SRP Subtype 4 Response");
return;
}
if (len != SHA_DIGESTSIZE) {
error("EAP: bad Lightweight rechallenge "
"response");
return;
}
SHA1Init(&ctxt);
vallen = id;
SHA1Update(&ctxt, &vallen, 1);
SHA1Update(&ctxt, esp->es_server.ea_skey,
SESSION_KEY_LEN);
SHA1Update(&ctxt, esp->es_challenge, esp->es_challen);
SHA1Update(&ctxt, esp->es_server.ea_peer,
esp->es_server.ea_peerlen);
SHA1Final(dig, &ctxt);
if (BCMP(dig, inp, SHA_DIGESTSIZE) != 0) {
error("EAP: failed Lightweight rechallenge");
eap_send_failure(esp);
break;
}
esp->es_server.ea_state = eapOpen;
if (esp->es_lwrechallenge != 0)
TIMEOUT(srp_lwrechallenge, esp,
esp->es_lwrechallenge);
break;
}
break;
#endif /* USE_SRP */
default:
/* This can't happen. */
error("EAP: unknown Response type %d; ignored", typenum);
return;
}
if (esp->es_server.ea_timeout > 0) {
UNTIMEOUT(eap_server_timeout, (void *)esp);
}
if (esp->es_server.ea_state != eapBadAuth &&
esp->es_server.ea_state != eapOpen) {
esp->es_server.ea_id++;
eap_send_request(esp);
}
}
/*
* eap_success - Receive EAP Success message (client mode).
*/
static void
eap_success(eap_state *esp, u_char *inp, int id, int len)
{
if (esp->es_client.ea_state != eapOpen && !eap_client_active(esp)
#ifdef USE_EAPTLS
&& esp->es_client.ea_state != eapTlsRecvSuccess
#endif /* USE_EAPTLS */
) {
dbglog("EAP unexpected success message in state %s (%d)",
eap_state_name(esp->es_client.ea_state),
esp->es_client.ea_state);
return;
}
#ifdef USE_EAPTLS
if(esp->es_client.ea_using_eaptls && esp->es_client.ea_state !=
eapTlsRecvSuccess) {
dbglog("EAP-TLS unexpected success message in state %s (%d)",
eap_state_name(esp->es_client.ea_state),
esp->es_client.ea_state);
return;
}
#endif /* USE_EAPTLS */
if (esp->es_client.ea_timeout > 0) {
UNTIMEOUT(eap_client_timeout, (void *)esp);
}
if (len > 0) {
/* This is odd. The spec doesn't allow for this. */
PRINTMSG(inp, len);
}
esp->es_client.ea_state = eapOpen;
auth_withpeer_success(esp->es_unit, PPP_EAP, 0);
}
/*
* eap_failure - Receive EAP Failure message (client mode).
*/
static void
eap_failure(eap_state *esp, u_char *inp, int id, int len)
{
/*
* Ignore failure messages if we're not open
*/
if (esp->es_client.ea_state <= eapClosed)
return;
if (!eap_client_active(esp)) {
dbglog("EAP unexpected failure message in state %s (%d)",
eap_state_name(esp->es_client.ea_state),
esp->es_client.ea_state);
}
if (esp->es_client.ea_timeout > 0) {
UNTIMEOUT(eap_client_timeout, (void *)esp);
}
if (len > 0) {
/* This is odd. The spec doesn't allow for this. */
PRINTMSG(inp, len);
}
esp->es_client.ea_state = eapBadAuth;
error("EAP: peer reports authentication failure");
auth_withpeer_fail(esp->es_unit, PPP_EAP);
}
/*
* eap_input - Handle received EAP message.
*/
static void
eap_input(int unit, u_char *inp, int inlen)
{
eap_state *esp = &eap_states[unit];
u_char code, id;
int len;
/*
* Parse header (code, id and length). If packet too short,
* drop it.
*/
if (inlen < EAP_HEADERLEN) {
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) {
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(esp, inp, id, len);
break;
case EAP_RESPONSE:
eap_response(esp, inp, id, len);
break;
case EAP_SUCCESS:
eap_success(esp, inp, id, len);
break;
case EAP_FAILURE:
eap_failure(esp, inp, id, len);
break;
default: /* XXX Need code reject */
/* Note: it's not legal to send EAP Nak here. */
warn("EAP: unknown code %d received", code);
break;
}
}
/*
* eap_printpkt - print the contents of an EAP packet.
*/
static char *eap_codenames[] = {
"Request", "Response", "Success", "Failure"
};
static 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", NULL,
"TTLS", "RAS", "AKA", "3COM", "PEAP",
"MSCHAPv2"
};
static int
eap_printpkt(u_char *inp, int inlen,
void (*printer) (void *, char *, ...), void *arg)
{
int code, id, len, rtype, vallen;
u_char *pstart;
u_int32_t uval;
#ifdef USE_EAPTLS
u_char flags;
#endif /* USE_EAPTLS */
#ifdef CHAPMS
u_char opcode;
#endif /* CHAPMS */
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 <= sizeof(eap_codenames) / 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 <= sizeof (eap_typenames) / 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 ");
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 ");
print_string((char *)inp, len, printer, arg);
printer(arg, ">");
INCPTR(len, inp);
len = 0;
} else {
printer(arg, " <No name>");
}
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, ", <Name ");
print_string((char *)inp, len, printer, arg);
printer(arg, ">");
INCPTR(len, inp);
len = 0;
} else {
printer(arg, ", <No name>");
}
break;
case CHAP_SUCCESS:
INCPTR(3, inp);
len -= 3;
printer(arg, " Success <Message ");
print_string((char *)inp, len, printer, arg);
printer(arg, ">");
break;
case CHAP_FAILURE:
INCPTR(3, inp);
len -= 3;
printer(arg, " Failure <Message ");
print_string((char *)inp, len, printer, arg);
printer(arg, ">");
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)
break;
GETCHAR(flags, inp);
len--;
if(flags == 0 && len == 0){
printer(arg, " Ack");
break;
}
printer(arg, flags & EAP_TLS_FLAGS_LI ? " L":" -");
printer(arg, flags & EAP_TLS_FLAGS_MF ? "M":"-");
printer(arg, flags & EAP_TLS_FLAGS_START ? "S":"- ");
break;
#endif /* USE_EAPTLS */
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 ");
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 < sizeof (u_int32_t))
break;
GETLONG(uval, inp);
len -= sizeof (u_int32_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;
}
break;
}
break;
case EAP_RESPONSE:
if (len < 1)
break;
GETCHAR(rtype, inp);
len--;
if (rtype >= 1 &&
rtype <= sizeof (eap_typenames) / 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 ");
print_string((char *)inp, len, printer, arg);
printer(arg, ">");
INCPTR(len, inp);
len = 0;
}
break;
#ifdef USE_EAPTLS
case EAPT_TLS:
if (len < 1)
break;
GETCHAR(flags, inp);
len--;
if(flags == 0 && len == 0){
printer(arg, " Ack");
break;
}
printer(arg, flags & EAP_TLS_FLAGS_LI ? " L":" -");
printer(arg, flags & EAP_TLS_FLAGS_MF ? "M":"-");
printer(arg, flags & EAP_TLS_FLAGS_START ? "S":"- ");
break;
#endif /* USE_EAPTLS */
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 <= sizeof (eap_typenames) / 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 ");
print_string((char *)inp, len, printer, arg);
printer(arg, ">");
INCPTR(len, inp);
len = 0;
} else {
printer(arg, " <No name>");
}
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, ", <Name ");
print_string((char *)inp, len, printer, arg);
printer(arg, ">");
INCPTR(len, inp);
len = 0;
} else {
printer(arg, ", <No name>");
}
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 */
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 < sizeof (u_int32_t))
break;
GETLONG(uval, inp);
len -= sizeof (u_int32_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;
}
break;
}
break;
case EAP_SUCCESS: /* No payload expected for these! */
case EAP_FAILURE:
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);
}