* Based on draft-ietf-pppext-eap-srp-03.txt.
*/
-#define RCSID "$Id: eap.c,v 1.2 2002/11/14 21:50:12 fcusack Exp $"
+/*
+ * Modification by Beniamino Galvani, Mar 2005
+ * Implemented EAP-TLS authentication
+ */
+
+#define RCSID "$Id: eap.c,v 1.4 2004/11/09 22:39:25 paulus Exp $"
/*
* TODO:
#include "pppd.h"
#include "pathnames.h"
#include "md5.h"
-/* For MD5_SIGNATURE_SIZE and MIN/MAX_CHALLENGE_LENGTH; should fix. */
-#include "chap.h"
#include "eap.h"
#ifdef USE_SRP
#define SHA_DIGESTSIZE 20
#endif
-static const char rcsid[] = RCSID;
+#ifdef USE_EAPTLS
+#include "eap-tls.h"
+#endif /* USE_EAPTLS */
eap_state eap_states[NUM_PPP]; /* EAP state; one for each unit */
#ifdef USE_SRP
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 */
}
/*
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;
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 (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 */
}
/*
INCPTR(esp->es_server.ea_namelen, outp);
break;
+#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);
eap_server_timeout(arg)
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);
}
}
#endif /* USE_SRP */
+#ifdef USE_EAPTLS
+/*
+ * Send an EAP-TLS response message with tls data
+ */
+static void
+eap_tls_response(esp, id)
+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(esp, id)
+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(esp, id, type)
eap_state *esp;
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;
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
}
/* Not so likely to happen. */
- if (vallen >= len + sizeof (rhostname)) {
+ 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';
eap_send_nak(esp, id, EAPT_SRP);
break;
}
- MD5Init(&mdContext);
+ MD5_Init(&mdContext);
typenum = id;
- MD5Update(&mdContext, &typenum, 1);
- MD5Update(&mdContext, secret, secret_len);
+ MD5_Update(&mdContext, &typenum, 1);
+ MD5_Update(&mdContext, (u_char *)secret, secret_len);
BZERO(secret, sizeof (secret));
- MD5Update(&mdContext, inp, vallen);
- MD5Final(hash, &mdContext);
+ 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_TLS);
+ 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_TLS);
+ 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_TLS);
+ esp->es_client.ea_using_eaptls = 0;
+ break;
+ }
+
+ break;
+#endif /* USE_EAPTLS */
+
#ifdef USE_SRP
case EAPT_SRP:
if (len < 1) {
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 */
+
+ /*
+ * 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);
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;
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 */
+
default:
dbglog("EAP: peer requesting unknown Type %d", vallen);
switch (esp->es_server.ea_state) {
}
/* Not so likely to happen. */
- if (vallen >= len + sizeof (rhostname)) {
+ 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';
eap_send_failure(esp);
break;
}
- MD5Init(&mdContext);
- MD5Update(&mdContext, &esp->es_server.ea_id, 1);
- MD5Update(&mdContext, secret, secret_len);
+ MD5_Init(&mdContext);
+ MD5_Update(&mdContext, &esp->es_server.ea_id, 1);
+ MD5_Update(&mdContext, (u_char *)secret, secret_len);
BZERO(secret, sizeof (secret));
- MD5Update(&mdContext, esp->es_challenge, esp->es_challen);
- MD5Final(hash, &mdContext);
+ 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;
int id;
int len;
{
- if (esp->es_client.ea_state != eapOpen && !eap_client_active(esp)) {
+ 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);
}
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),
int code, id, len, rtype, vallen;
u_char *pstart;
u_int32_t uval;
+#ifdef USE_EAPTLS
+ u_char flags;
+#endif /* USE_EAPTLS */
if (inlen < EAP_HEADERLEN)
return (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_SRP:
if (len < 3)
goto truncated;
}
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>");