You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mynewt.apache.org by ma...@apache.org on 2016/09/30 00:30:48 UTC

[05/51] [abbrv] [partial] incubator-mynewt-core git commit: net/ip/lwip_base; LwIP.

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

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/f5a0f2a0/net/ip/lwip_base/src/netif/ppp/ecp.c
----------------------------------------------------------------------
diff --git a/net/ip/lwip_base/src/netif/ppp/ecp.c b/net/ip/lwip_base/src/netif/ppp/ecp.c
new file mode 100644
index 0000000..4d84f60
--- /dev/null
+++ b/net/ip/lwip_base/src/netif/ppp/ecp.c
@@ -0,0 +1,191 @@
+/*
+ * ecp.c - PPP Encryption Control Protocol.
+ *
+ * Copyright (c) 2002 Google, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. The name(s) of the authors of this software must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission.
+ *
+ * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO
+ * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Derived from ccp.c, which is:
+ *
+ * Copyright (c) 1994-2002 Paul Mackerras. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. The name(s) of the authors of this software must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission.
+ *
+ * 3. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by Paul Mackerras
+ *     <pa...@samba.org>".
+ *
+ * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO
+ * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "netif/ppp/ppp_opts.h"
+#if PPP_SUPPORT && ECP_SUPPORT  /* don't build if not configured for use in lwipopts.h */
+
+#include <string.h>
+
+#include "netif/ppp/ppp_impl.h"
+
+#include "netif/ppp/fsm.h"
+#include "netif/ppp/ecp.h"
+
+#if PPP_OPTIONS
+static option_t ecp_option_list[] = {
+    { "noecp", o_bool, &ecp_protent.enabled_flag,
+      "Disable ECP negotiation" },
+    { "-ecp", o_bool, &ecp_protent.enabled_flag,
+      "Disable ECP negotiation", OPT_ALIAS },
+
+    { NULL }
+};
+#endif /* PPP_OPTIONS */
+
+/*
+ * Protocol entry points from main code.
+ */
+static void ecp_init (int unit);
+/*
+static void ecp_open (int unit);
+static void ecp_close (int unit, char *);
+static void ecp_lowerup (int unit);
+static void ecp_lowerdown (int);
+static void ecp_input (int unit, u_char *pkt, int len);
+static void ecp_protrej (int unit);
+*/
+#if PRINTPKT_SUPPORT
+static int  ecp_printpkt (const u_char *pkt, int len,
+			      void (*printer) (void *, char *, ...),
+			      void *arg);
+#endif /* PRINTPKT_SUPPORT */
+/*
+static void ecp_datainput (int unit, u_char *pkt, int len);
+*/
+
+const struct protent ecp_protent = {
+    PPP_ECP,
+    ecp_init,
+    NULL, /* ecp_input, */
+    NULL, /* ecp_protrej, */
+    NULL, /* ecp_lowerup, */
+    NULL, /* ecp_lowerdown, */
+    NULL, /* ecp_open, */
+    NULL, /* ecp_close, */
+#if PRINTPKT_SUPPORT
+    ecp_printpkt,
+#endif /* PRINTPKT_SUPPORT */
+#if PPP_DATAINPUT
+    NULL, /* ecp_datainput, */
+#endif /* PPP_DATAINPUT */
+#if PRINTPKT_SUPPORT
+    "ECP",
+    "Encrypted",
+#endif /* PRINTPKT_SUPPORT */
+#if PPP_OPTIONS
+    ecp_option_list,
+    NULL,
+#endif /* PPP_OPTIONS */
+#if DEMAND_SUPPORT
+    NULL,
+    NULL
+#endif /* DEMAND_SUPPORT */
+};
+
+fsm ecp_fsm[NUM_PPP];
+ecp_options ecp_wantoptions[NUM_PPP];	/* what to request the peer to use */
+ecp_options ecp_gotoptions[NUM_PPP];	/* what the peer agreed to do */
+ecp_options ecp_allowoptions[NUM_PPP];	/* what we'll agree to do */
+ecp_options ecp_hisoptions[NUM_PPP];	/* what we agreed to do */
+
+static const fsm_callbacks ecp_callbacks = {
+    NULL, /* ecp_resetci, */
+    NULL, /* ecp_cilen, */
+    NULL, /* ecp_addci, */
+    NULL, /* ecp_ackci, */
+    NULL, /* ecp_nakci, */
+    NULL, /* ecp_rejci, */
+    NULL, /* ecp_reqci, */
+    NULL, /* ecp_up, */
+    NULL, /* ecp_down, */
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL, /* ecp_extcode, */
+    "ECP"
+};
+
+/*
+ * ecp_init - initialize ECP.
+ */
+static void
+ecp_init(unit)
+    int unit;
+{
+    fsm *f = &ecp_fsm[unit];
+
+    f->unit = unit;
+    f->protocol = PPP_ECP;
+    f->callbacks = &ecp_callbacks;
+    fsm_init(f);
+
+#if 0 /* Not necessary, everything is cleared in ppp_new() */
+    memset(&ecp_wantoptions[unit],  0, sizeof(ecp_options));
+    memset(&ecp_gotoptions[unit],   0, sizeof(ecp_options));
+    memset(&ecp_allowoptions[unit], 0, sizeof(ecp_options));
+    memset(&ecp_hisoptions[unit],   0, sizeof(ecp_options));
+#endif /* 0 */
+
+}
+
+
+#if PRINTPKT_SUPPORT
+static int
+ecp_printpkt(p, plen, printer, arg)
+    const u_char *p;
+    int plen;
+    void (*printer) (void *, char *, ...);
+    void *arg;
+{
+    return 0;
+}
+#endif /* PRINTPKT_SUPPORT */
+
+#endif /* PPP_SUPPORT && ECP_SUPPORT */

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/f5a0f2a0/net/ip/lwip_base/src/netif/ppp/eui64.c
----------------------------------------------------------------------
diff --git a/net/ip/lwip_base/src/netif/ppp/eui64.c b/net/ip/lwip_base/src/netif/ppp/eui64.c
new file mode 100644
index 0000000..01493bc
--- /dev/null
+++ b/net/ip/lwip_base/src/netif/ppp/eui64.c
@@ -0,0 +1,56 @@
+/*
+ * eui64.c - EUI64 routines for IPv6CP.
+ *
+ * Copyright (c) 1999 Tommi Komulainen.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. The name(s) of the authors of this software must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission.
+ *
+ * 4. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by Tommi Komulainen
+ *     <To...@iki.fi>".
+ *
+ * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO
+ * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * $Id: eui64.c,v 1.6 2002/12/04 23:03:32 paulus Exp $
+ */
+
+#include "netif/ppp/ppp_opts.h"
+#if PPP_SUPPORT && PPP_IPV6_SUPPORT  /* don't build if not configured for use in lwipopts.h */
+
+#include "netif/ppp/ppp_impl.h"
+#include "netif/ppp/eui64.h"
+
+/*
+ * eui64_ntoa - Make an ascii representation of an interface identifier
+ */
+char *eui64_ntoa(eui64_t e) {
+    static char buf[20];
+
+    sprintf(buf, "%02x%02x:%02x%02x:%02x%02x:%02x%02x",
+	     e.e8[0], e.e8[1], e.e8[2], e.e8[3], 
+	     e.e8[4], e.e8[5], e.e8[6], e.e8[7]);
+    return buf;
+}
+
+#endif /* PPP_SUPPORT && PPP_IPV6_SUPPORT */