* Implemented EAP-TLS authentication
*/
-#define RCSID "$Id: eap.c,v 1.4 2004/11/09 22:39:25 paulus Exp $"
-
-/*
- * TODO:
- */
-
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "md5.h"
#include "eap.h"
+#ifdef CHAPMS
+#include "chap_ms.h"
+#endif
+
#ifdef USE_SRP
#include <t_pwd.h>
#include <t_server.h>
/*
* 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];
#ifdef USE_EAPTLS
esp->es_client.ea_using_eaptls = 0;
#endif /* USE_EAPTLS */
+#ifdef CHAPMS
+ esp->es_client.digest = chap_find_digest(CHAP_MICROSOFT_V2);
+#endif
}
/*
* 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;
* 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;
* 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;
* 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;
* Send an EAP-TLS response message with tls data
*/
static void
-eap_tls_response(esp, id)
-eap_state *esp;
-u_char id;
+eap_tls_response(eap_state *esp, u_char id)
{
u_char *outp;
int outlen;
* Send an EAP-TLS ack
*/
static void
-eap_tls_sendack(esp, id)
-eap_state *esp;
-u_char id;
+eap_tls_sendack(eap_state *esp, u_char id)
{
u_char *outp;
int 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;
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;
/* Init ssl session */
if(!eaptls_init_ssl_client(esp)) {
dbglog("cannot init ssl");
- eap_send_nak(esp, id, EAPT_TLS);
+ eap_send_nak(esp, id, EAPT_MSCHAPV2);
esp->es_client.ea_using_eaptls = 0;
break;
}
}
/* The server has sent a bad start packet. */
- eap_send_nak(esp, id, EAPT_TLS);
+ eap_send_nak(esp, id, EAPT_MSCHAPV2);
break;
case eapTlsRecvAck:
}
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;
* 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)
#ifdef USE_EAPTLS
* 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;
};
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;
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;