* Based on draft-ietf-pppext-eap-srp-03.txt.
*/
-#define RCSID "$Id: eap.c,v 1.4 2004/11/09 22:39:25 paulus Exp $"
-
/*
- * TODO:
+ * Modification by Beniamino Galvani, Mar 2005
+ * Implemented EAP-TLS authentication
*/
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "eap.h"
#ifdef USE_SRP
+#ifdef HAVE_TIME_H
+#include <time.h>
+#endif
#include <t_pwd.h>
#include <t_server.h>
#include <t_client.h>
#define SHA_DIGESTSIZE 20
#endif
+#ifdef USE_EAPTLS
+#include "eap-tls.h"
+#endif /* USE_EAPTLS */
+
+#ifdef CHAPMS
+#include "chap_ms.h"
+#include "chap-new.h"
+
+extern int chapms_strip_domain;
+#endif /* CHAPMS */
eap_state eap_states[NUM_PPP]; /* EAP state; one for each unit */
#ifdef USE_SRP
/*
* Protocol entry points.
*/
-static void eap_init __P((int unit));
-static void eap_input __P((int unit, u_char *inp, int inlen));
-static void eap_protrej __P((int unit));
-static void eap_lowerup __P((int unit));
-static void eap_lowerdown __P((int unit));
-static int eap_printpkt __P((u_char *inp, int inlen,
- void (*)(void *arg, char *fmt, ...), void *arg));
+static void eap_init (int unit);
+static void eap_input (int unit, u_char *inp, int inlen);
+static void eap_protrej (int unit);
+static void eap_lowerup (int unit);
+static void eap_lowerdown (int unit);
+static int eap_printpkt (u_char *inp, int inlen,
+ void (*)(void *arg, char *fmt, ...), void *arg);
struct protent eap_protent = {
PPP_EAP, /* protocol number */
NULL /* say whether to bring up link for this pkt */
};
+#ifdef USE_SRP
/*
* A well-known 2048 bit modulus.
*/
0x9B, 0x65, 0xE3, 0x72, 0xFC, 0xD6, 0x8E, 0xF2,
0x0F, 0xA7, 0x11, 0x1F, 0x9E, 0x4A, 0xFF, 0x73
};
+#endif /* USE_SRP */
/* Local forward declarations. */
-static void eap_server_timeout __P((void *arg));
+static void eap_server_timeout (void *arg);
/*
* Convert EAP state code to printable string for debug.
*/
static const char *
-eap_state_name(esc)
-enum eap_state_code esc;
+eap_state_name(enum eap_state_code esc)
{
static const char *state_names[] = { EAP_STATES };
* called once by main() during start-up.
*/
static void
-eap_init(unit)
-int unit;
+eap_init(int unit)
{
eap_state *esp = &eap_states[unit];
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);
+ esp->es_server.digest = chap_find_digest(CHAP_MICROSOFT_V2);
+#endif
}
/*
* Request messages.
*/
static void
-eap_client_timeout(arg)
-void *arg;
+eap_client_timeout(void *arg)
{
eap_state *esp = (eap_state *) arg;
* after eap_lowerup.
*/
void
-eap_authwithpeer(unit, localname)
-int unit;
-char *localname;
+eap_authwithpeer(int unit, char *localname)
{
eap_state *esp = &eap_states[unit];
* (Server operation)
*/
static void
-eap_send_failure(esp)
-eap_state *esp;
+eap_send_failure(eap_state *esp)
{
u_char *outp;
* (Server operation)
*/
static void
-eap_send_success(esp)
-eap_state *esp;
+eap_send_success(eap_state *esp)
{
u_char *outp;
};
static int
-b64enc(bs, inp, inlen, outp)
-struct b64state *bs;
-u_char *inp;
-int inlen;
-u_char *outp;
+b64enc(struct b64state *bs, u_char *inp, int inlen, u_char *outp)
{
int outlen = 0;
}
static int
-b64flush(bs, outp)
-struct b64state *bs;
-u_char *outp;
+b64flush(struct b64state *bs, u_char *outp)
{
int outlen = 0;
}
static int
-b64dec(bs, inp, inlen, outp)
-struct b64state *bs;
-u_char *inp;
-int inlen;
-u_char *outp;
+b64dec(struct b64state *bs, u_char *inp, int inlen, u_char *outp)
{
int outlen = 0;
char *cp;
* 0 for success and non-zero for failure.
*/
static void
-eap_figure_next_state(esp, status)
-eap_state *esp;
-int status;
+eap_figure_next_state(eap_state *esp, int status)
{
#ifdef USE_SRP
unsigned char secbuf[MAXWORDLEN], clear[8], *sp, *dp;
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;
tpw.pebuf.name = esp->es_server.ea_peer;
tpw.pebuf.password.len = t_fromb64((char *)tpw.pwbuf,
cp);
- tpw.pebuf.password.data = tpw.pwbuf;
+ tpw.pebuf.password.data = (char*) tpw.pwbuf;
tpw.pebuf.salt.len = t_fromb64((char *)tpw.saltbuf,
cp2);
tpw.pebuf.salt.data = tpw.saltbuf;
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;
}
break;
+#ifdef CHAPMS
+ case eapMSCHAPv2Chall:
+#endif
case eapMD5Chall:
if (status != 0) {
esp->es_server.ea_state = eapBadAuth;
}
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
+/*
+ * 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(esp)
-eap_state *esp;
+eap_send_request(eap_state *esp)
{
u_char *outp;
u_char *lenloc;
INCPTR(esp->es_server.ea_namelen, outp);
break;
+#ifdef CHAPMS
+ case eapMSCHAPv2Chall:
+ esp->es_server.digest->generate_challenge(esp->es_challenge);
+ challen = esp->es_challenge[0];
+ esp->es_challen = challen;
+
+ PUTCHAR(EAPT_MSCHAPV2, outp);
+ PUTCHAR(CHAP_CHALLENGE, outp);
+ PUTCHAR(esp->es_server.ea_id, outp);
+ /* MS len */
+ PUTSHORT(5 + challen +
+ esp->es_server.ea_namelen,
+ outp);
+ /* challen + challenge */
+ BCOPY(esp->es_challenge, outp, challen+1);
+ INCPTR(challen+1, outp);
+ BCOPY(esp->es_server.ea_name,
+ outp,
+ esp->es_server.ea_namelen);
+ INCPTR(esp->es_server.ea_namelen, outp);
+ break;
+#endif /* CHAPMS */
+
+#ifdef USE_EAPTLS
+ case eapTlsStart:
+ PUTCHAR(EAPT_TLS, outp);
+ 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);
* after eap_lowerup.
*/
void
-eap_authpeer(unit, localname)
-int unit;
-char *localname;
+eap_authpeer(int unit, char *localname)
{
eap_state *esp = &eap_states[unit];
* expired.
*/
static void
-eap_server_timeout(arg)
-void *arg;
+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);
}
* will restart the timer. If it fails, then the link is dropped.
*/
static void
-eap_rechallenge(arg)
-void *arg;
+eap_rechallenge(void *arg)
{
eap_state *esp = (eap_state *)arg;
}
static void
-srp_lwrechallenge(arg)
-void *arg;
+srp_lwrechallenge(void *arg)
{
eap_state *esp = (eap_state *)arg;
* thing.
*/
static void
-eap_lowerup(unit)
-int unit;
+eap_lowerup(int unit)
{
eap_state *esp = &eap_states[unit];
* Cancel all timeouts and return to initial state.
*/
static void
-eap_lowerdown(unit)
-int unit;
+eap_lowerdown(int unit)
{
eap_state *esp = &eap_states[unit];
* failure.
*/
static void
-eap_protrej(unit)
-int unit;
+eap_protrej(int unit)
{
eap_state *esp = &eap_states[unit];
* Format and send a regular EAP Response message.
*/
static void
-eap_send_response(esp, id, typenum, str, lenstr)
-eap_state *esp;
-u_char id;
-u_char typenum;
-u_char *str;
-int lenstr;
+eap_send_response(eap_state *esp, u_char id, u_char typenum,
+ u_char *str, int lenstr)
{
u_char *outp;
int msglen;
* Format and send an MD5-Challenge EAP Response message.
*/
static void
-eap_chap_response(esp, id, hash, name, namelen)
-eap_state *esp;
-u_char id;
-u_char *hash;
-char *name;
-int namelen;
+eap_chap_response(eap_state *esp, u_char id, u_char *hash,
+ char *name, int namelen)
{
u_char *outp;
int msglen;
* Format and send a SRP EAP Response message.
*/
static void
-eap_srp_response(esp, id, subtypenum, str, lenstr)
-eap_state *esp;
-u_char id;
-u_char subtypenum;
-u_char *str;
-int lenstr;
+eap_srp_response(eap_state *esp, u_char id, u_char subtypenum,
+ u_char *str, int lenstr)
{
u_char *outp;
int msglen;
* Format and send a SRP EAP Client Validator Response message.
*/
static void
-eap_srpval_response(esp, id, flags, str)
-eap_state *esp;
-u_char id;
-u_int32_t flags;
-u_char *str;
+eap_srpval_response(eap_state *esp, u_char id, u_int32_t flags, u_char *str)
{
u_char *outp;
int msglen;
}
#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(esp, id, type)
-eap_state *esp;
-u_char id;
-u_char type;
+eap_send_nak(eap_state *esp, u_char id, u_char type)
{
u_char *outp;
int msglen;
#ifdef USE_SRP
static char *
-name_of_pn_file()
+name_of_pn_file(void)
{
char *user, *path, *file;
struct passwd *pw;
}
static int
-open_pn_file(modebits)
-mode_t modebits;
+open_pn_file(mode_t modebits)
{
char *path;
int fd, err;
}
static void
-remove_pn_file()
+remove_pn_file(void)
{
char *path;
}
static void
-write_pseudonym(esp, inp, len, id)
-eap_state *esp;
-u_char *inp;
-int len, id;
+write_pseudonym(eap_state *esp, u_char *inp, int len, int id)
{
u_char val;
u_char *datp, *digp;
}
#endif /* USE_SRP */
+#if CHAPMS
+/*
+ * Format and send an CHAPV2-Challenge EAP Response message.
+ */
+static void
+eap_chapv2_response(eap_state *esp, u_char id, u_char chapid, u_char *response, char *user, int user_len)
+{
+ u_char *outp;
+ int msglen;
+
+ outp = outpacket_buf;
+
+ MAKEHEADER(outp, PPP_EAP);
+
+ PUTCHAR(EAP_RESPONSE, outp);
+ PUTCHAR(id, outp);
+ esp->es_client.ea_id = id;
+ msglen = EAP_HEADERLEN + 6 * sizeof (u_char) + MS_CHAP2_RESPONSE_LEN + user_len;
+ PUTSHORT(msglen, outp);
+ PUTCHAR(EAPT_MSCHAPV2, outp);
+ PUTCHAR(CHAP_RESPONSE, outp);
+ PUTCHAR(chapid, outp);
+ PUTCHAR(0, outp);
+ /* len */
+ PUTCHAR(5 + user_len + MS_CHAP2_RESPONSE_LEN, outp);
+ BCOPY(response, outp, MS_CHAP2_RESPONSE_LEN+1); // VLEN + VALUE
+ INCPTR(MS_CHAP2_RESPONSE_LEN+1, outp);
+ BCOPY(user, outp, user_len);
+
+ output(esp->es_unit, outpacket_buf, PPP_HDRLEN + msglen);
+}
+#endif
+
/*
* eap_request - Receive EAP Request message (client mode).
*/
static void
-eap_request(esp, inp, id, len)
-eap_state *esp;
-u_char *inp;
-int id;
-int len;
+eap_request(eap_state *esp, u_char *inp, int id, int len)
{
u_char typenum;
u_char vallen;
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;
esp->es_usedpseudo = 2;
}
#endif /* USE_SRP */
- eap_send_response(esp, id, typenum, esp->es_client.ea_name,
+ eap_send_response(esp, id, typenum, (u_char *)esp->es_client.ea_name,
esp->es_client.ea_namelen);
break;
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) {
}
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);
}
return;
-#ifdef USE_SRP
client_failure:
esp->es_client.ea_state = eapBadAuth;
if (esp->es_client.ea_timeout > 0) {
UNTIMEOUT(eap_client_timeout, (void *)esp);
}
esp->es_client.ea_session = NULL;
+#ifdef USE_SRP
t_clientclose(tc);
auth_withpeer_fail(esp->es_unit, PPP_EAP);
#endif /* USE_SRP */
* eap_response - Receive EAP Response message (server mode).
*/
static void
-eap_response(esp, inp, id, len)
-eap_state *esp;
-u_char *inp;
-int id;
-int len;
+eap_response(eap_state *esp, u_char *inp, int id, int len)
{
u_char typenum;
u_char vallen;
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
*/
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 */
+
+#ifdef CHAPMS
+ case EAPT_MSCHAPV2:
+ info("EAP: peer proposes MSCHAPv2");
+ /* If MSCHAPv2 digest was not found, NAK the packet */
+ if (!esp->es_server.digest) {
+ error("EAP MSCHAPv2 not supported");
+ eap_send_nak(esp, id, EAPT_SRP);
+ break;
+ }
+ esp->es_server.ea_state = eapMSCHAPv2Chall;
+ break;
+#endif /* CHAPMS */
+
default:
dbglog("EAP: peer requesting unknown Type %d", vallen);
switch (esp->es_server.ea_state) {
TIMEOUT(eap_rechallenge, esp, esp->es_rechallenge);
break;
+#ifdef CHAPMS
+ case EAPT_MSCHAPV2:
+ if (len < 1) {
+ error("EAP: received MSCHAPv2 with no data");
+ eap_figure_next_state(esp, 1);
+ break;
+ }
+ GETCHAR(opcode, inp);
+ len--;
+
+ switch (opcode) {
+ case CHAP_RESPONSE:
+ if (esp->es_server.ea_state != eapMSCHAPv2Chall) {
+ error("EAP: unexpected MSCHAPv2-Response");
+ eap_figure_next_state(esp, 1);
+ break;
+ }
+ /* skip MS ID + len */
+ INCPTR(3, inp);
+ GETCHAR(vallen, inp);
+ len -= 4;
+
+ if (vallen != MS_CHAP2_RESPONSE_LEN || vallen > len) {
+ error("EAP: Invalid MSCHAPv2-Response "
+ "length %d", vallen);
+ eap_figure_next_state(esp, 1);
+ break;
+ }
+
+ /* Not so likely to happen. */
+ if (len - vallen >= sizeof (rhostname)) {
+ dbglog("EAP: trimming really long peer name down");
+ BCOPY(inp + vallen, rhostname, sizeof (rhostname) - 1);
+ rhostname[sizeof (rhostname) - 1] = '\0';
+ } else {
+ BCOPY(inp + vallen, rhostname, len - vallen);
+ rhostname[len - vallen] = '\0';
+ }
+
+ /* In case the remote doesn't give us his name. */
+ if (explicit_remote ||
+ (remote_name[0] != '\0' && vallen == len))
+ strlcpy(rhostname, remote_name, sizeof (rhostname));
+
+ /* strip the MS domain name */
+ if (chapms_strip_domain && strrchr(rhostname, '\\')) {
+ char tmp[MAXNAMELEN+1];
+
+ strcpy(tmp, strrchr(rhostname, '\\') + 1);
+ strcpy(rhostname, tmp);
+ }
+
+ if (chap_verify_hook)
+ chap_verifier = chap_verify_hook;
+ else
+ chap_verifier = eap_chap_verify_response;
+
+ esp->es_server.ea_id += 1;
+ if ((*chap_verifier)(rhostname,
+ esp->es_server.ea_name,
+ id,
+ esp->es_server.digest,
+ esp->es_challenge,
+ inp - 1,
+ response_message,
+ sizeof(response_message)))
+ {
+ info("EAP: MSCHAPv2 success for peer %q",
+ rhostname);
+ esp->es_server.ea_type = EAPT_MSCHAPV2;
+ eap_chapms2_send_request(esp,
+ esp->es_server.ea_id,
+ CHAP_SUCCESS,
+ esp->es_server.ea_id,
+ response_message,
+ strlen(response_message));
+ eap_figure_next_state(esp, 0);
+ if (esp->es_rechallenge != 0)
+ TIMEOUT(eap_rechallenge, esp, esp->es_rechallenge);
+ }
+ else {
+ warn("EAP: MSCHAPv2 failure for peer %q",
+ rhostname);
+ eap_chapms2_send_request(esp,
+ esp->es_server.ea_id,
+ CHAP_FAILURE,
+ esp->es_server.ea_id,
+ response_message,
+ strlen(response_message));
+ }
+ break;
+ case CHAP_SUCCESS:
+ info("EAP: MSCHAPv2 success confirmed");
+ break;
+ case CHAP_FAILURE:
+ info("EAP: MSCHAPv2 failure confirmed");
+ break;
+ default:
+ error("EAP: Unhandled MSCHAPv2 opcode %d", opcode);
+ eap_send_nak(esp, id, EAPT_SRP);
+ }
+
+ break;
+#endif /* CHAPMS */
+
#ifdef USE_SRP
case EAPT_SRP:
if (len < 1) {
* eap_success - Receive EAP Success message (client mode).
*/
static void
-eap_success(esp, inp, id, len)
-eap_state *esp;
-u_char *inp;
-int id;
-int len;
+eap_success(eap_state *esp, u_char *inp, int id, int len)
{
- if (esp->es_client.ea_state != eapOpen && !eap_client_active(esp)) {
+ 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);
}
* eap_failure - Receive EAP Failure message (client mode).
*/
static void
-eap_failure(esp, inp, id, len)
-eap_state *esp;
-u_char *inp;
-int id;
-int len;
+eap_failure(eap_state *esp, u_char *inp, int id, int len)
{
/*
* Ignore failure messages if we're not open
* eap_input - Handle received EAP message.
*/
static void
-eap_input(unit, inp, inlen)
-int unit;
-u_char *inp;
-int inlen;
+eap_input(int unit, u_char *inp, int inlen)
{
eap_state *esp = &eap_states[unit];
u_char code, id;
"OTP", "Generic-Token", NULL, NULL,
"RSA", "DSS", "KEA", "KEA-Validate",
"TLS", "Defender", "Windows 2000", "Arcot",
- "Cisco", "Nokia", "SRP"
+ "Cisco", "Nokia", "SRP", NULL,
+ "TTLS", "RAS", "AKA", "3COM", "PEAP",
+ "MSCHAPv2"
};
static int
-eap_printpkt(inp, inlen, printer, arg)
-u_char *inp;
-int inlen;
-void (*printer) __P((void *, char *, ...));
-void *arg;
+eap_printpkt(u_char *inp, int inlen,
+ void (*printer) (void *, char *, ...), void *arg)
{
int code, id, len, rtype, vallen;
u_char *pstart;
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);
}
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 */
+
+#ifdef USE_SRP
case EAPT_SRP:
if (len < 3)
goto truncated;
break;
}
break;
+#endif /* USE_SRP */
}
break;
}
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>");
len--;
printer(arg, " <Suggested-type %02X", rtype);
if (rtype >= 1 &&
- rtype < sizeof (eap_typenames) / sizeof (char *))
+ rtype <= sizeof (eap_typenames) / sizeof (char *))
printer(arg, " (%s)", eap_typenames[rtype-1]);
printer(arg, ">");
break;
}
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 */
+
+#ifdef USE_SRP
case EAPT_SRP:
if (len < 1)
goto truncated;
break;
}
break;
+#endif /* USE_SRP */
}
break;