hostap/wpa_supplicant/eapol_test.c
Jouni Malinen 3b3677b3bc eapol_test: Allow interface name to be specified
The new -i<ifname> command line argument can be used to specify the name
of the interface to use. This is mainly to allow unique control
interface names to be defined without having to use multiple
directories.

Signed-off-by: Jouni Malinen <j@w1.fi>
2015-08-20 23:13:33 +03:00

1452 lines
37 KiB
C

/*
* WPA Supplicant - test code
* Copyright (c) 2003-2013, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*
* IEEE 802.1X Supplicant test code (to be used in place of wpa_supplicant.c.
* Not used in production version.
*/
#include "includes.h"
#include <assert.h>
#include "common.h"
#include "utils/ext_password.h"
#include "config.h"
#include "eapol_supp/eapol_supp_sm.h"
#include "eap_peer/eap.h"
#include "eap_server/eap_methods.h"
#include "eloop.h"
#include "utils/base64.h"
#include "rsn_supp/wpa.h"
#include "wpa_supplicant_i.h"
#include "radius/radius.h"
#include "radius/radius_client.h"
#include "common/wpa_ctrl.h"
#include "ctrl_iface.h"
#include "pcsc_funcs.h"
#include "wpas_glue.h"
const struct wpa_driver_ops *const wpa_drivers[] = { NULL };
struct extra_radius_attr {
u8 type;
char syntax;
char *data;
struct extra_radius_attr *next;
};
struct eapol_test_data {
struct wpa_supplicant *wpa_s;
int eapol_test_num_reauths;
int no_mppe_keys;
int num_mppe_ok, num_mppe_mismatch;
int req_eap_key_name;
u8 radius_identifier;
struct radius_msg *last_recv_radius;
struct in_addr own_ip_addr;
struct radius_client_data *radius;
struct hostapd_radius_servers *radius_conf;
/* last received EAP Response from Authentication Server */
struct wpabuf *last_eap_radius;
u8 authenticator_pmk[PMK_LEN];
size_t authenticator_pmk_len;
u8 authenticator_eap_key_name[256];
size_t authenticator_eap_key_name_len;
int radius_access_accept_received;
int radius_access_reject_received;
int auth_timed_out;
u8 *eap_identity;
size_t eap_identity_len;
char *connect_info;
u8 own_addr[ETH_ALEN];
struct extra_radius_attr *extra_attrs;
FILE *server_cert_file;
const char *pcsc_reader;
const char *pcsc_pin;
};
static struct eapol_test_data eapol_test;
static void send_eap_request_identity(void *eloop_ctx, void *timeout_ctx);
static void hostapd_logger_cb(void *ctx, const u8 *addr, unsigned int module,
int level, const char *txt, size_t len)
{
if (addr)
wpa_printf(MSG_DEBUG, "STA " MACSTR ": %s\n",
MAC2STR(addr), txt);
else
wpa_printf(MSG_DEBUG, "%s", txt);
}
static int add_extra_attr(struct radius_msg *msg,
struct extra_radius_attr *attr)
{
size_t len;
char *pos;
u32 val;
char buf[RADIUS_MAX_ATTR_LEN + 1];
switch (attr->syntax) {
case 's':
os_snprintf(buf, sizeof(buf), "%s", attr->data);
len = os_strlen(buf);
break;
case 'n':
buf[0] = '\0';
len = 1;
break;
case 'x':
pos = attr->data;
if (pos[0] == '0' && pos[1] == 'x')
pos += 2;
len = os_strlen(pos);
if ((len & 1) || (len / 2) > RADIUS_MAX_ATTR_LEN) {
printf("Invalid extra attribute hexstring\n");
return -1;
}
len /= 2;
if (hexstr2bin(pos, (u8 *) buf, len) < 0) {
printf("Invalid extra attribute hexstring\n");
return -1;
}
break;
case 'd':
val = htonl(atoi(attr->data));
os_memcpy(buf, &val, 4);
len = 4;
break;
default:
printf("Incorrect extra attribute syntax specification\n");
return -1;
}
if (!radius_msg_add_attr(msg, attr->type, (u8 *) buf, len)) {
printf("Could not add attribute %d\n", attr->type);
return -1;
}
return 0;
}
static int add_extra_attrs(struct radius_msg *msg,
struct extra_radius_attr *attrs)
{
struct extra_radius_attr *p;
for (p = attrs; p; p = p->next) {
if (add_extra_attr(msg, p) < 0)
return -1;
}
return 0;
}
static struct extra_radius_attr *
find_extra_attr(struct extra_radius_attr *attrs, u8 type)
{
struct extra_radius_attr *p;
for (p = attrs; p; p = p->next) {
if (p->type == type)
return p;
}
return NULL;
}
static void ieee802_1x_encapsulate_radius(struct eapol_test_data *e,
const u8 *eap, size_t len)
{
struct radius_msg *msg;
char buf[RADIUS_MAX_ATTR_LEN + 1];
const struct eap_hdr *hdr;
const u8 *pos;
wpa_printf(MSG_DEBUG, "Encapsulating EAP message into a RADIUS "
"packet");
e->radius_identifier = radius_client_get_id(e->radius);
msg = radius_msg_new(RADIUS_CODE_ACCESS_REQUEST,
e->radius_identifier);
if (msg == NULL) {
printf("Could not create net RADIUS packet\n");
return;
}
radius_msg_make_authenticator(msg, (u8 *) e, sizeof(*e));
hdr = (const struct eap_hdr *) eap;
pos = (const u8 *) (hdr + 1);
if (len > sizeof(*hdr) && hdr->code == EAP_CODE_RESPONSE &&
pos[0] == EAP_TYPE_IDENTITY) {
pos++;
os_free(e->eap_identity);
e->eap_identity_len = len - sizeof(*hdr) - 1;
e->eap_identity = os_malloc(e->eap_identity_len);
if (e->eap_identity) {
os_memcpy(e->eap_identity, pos, e->eap_identity_len);
wpa_hexdump(MSG_DEBUG, "Learned identity from "
"EAP-Response-Identity",
e->eap_identity, e->eap_identity_len);
}
}
if (e->eap_identity &&
!radius_msg_add_attr(msg, RADIUS_ATTR_USER_NAME,
e->eap_identity, e->eap_identity_len)) {
printf("Could not add User-Name\n");
goto fail;
}
if (e->req_eap_key_name &&
!radius_msg_add_attr(msg, RADIUS_ATTR_EAP_KEY_NAME, (u8 *) "\0",
1)) {
printf("Could not add EAP-Key-Name\n");
goto fail;
}
if (!find_extra_attr(e->extra_attrs, RADIUS_ATTR_NAS_IP_ADDRESS) &&
!radius_msg_add_attr(msg, RADIUS_ATTR_NAS_IP_ADDRESS,
(u8 *) &e->own_ip_addr, 4)) {
printf("Could not add NAS-IP-Address\n");
goto fail;
}
os_snprintf(buf, sizeof(buf), RADIUS_802_1X_ADDR_FORMAT,
MAC2STR(e->wpa_s->own_addr));
if (!find_extra_attr(e->extra_attrs, RADIUS_ATTR_CALLING_STATION_ID)
&&
!radius_msg_add_attr(msg, RADIUS_ATTR_CALLING_STATION_ID,
(u8 *) buf, os_strlen(buf))) {
printf("Could not add Calling-Station-Id\n");
goto fail;
}
/* TODO: should probably check MTU from driver config; 2304 is max for
* IEEE 802.11, but use 1400 to avoid problems with too large packets
*/
if (!find_extra_attr(e->extra_attrs, RADIUS_ATTR_FRAMED_MTU) &&
!radius_msg_add_attr_int32(msg, RADIUS_ATTR_FRAMED_MTU, 1400)) {
printf("Could not add Framed-MTU\n");
goto fail;
}
if (!find_extra_attr(e->extra_attrs, RADIUS_ATTR_NAS_PORT_TYPE) &&
!radius_msg_add_attr_int32(msg, RADIUS_ATTR_NAS_PORT_TYPE,
RADIUS_NAS_PORT_TYPE_IEEE_802_11)) {
printf("Could not add NAS-Port-Type\n");
goto fail;
}
os_snprintf(buf, sizeof(buf), "%s", e->connect_info);
if (!find_extra_attr(e->extra_attrs, RADIUS_ATTR_CONNECT_INFO) &&
!radius_msg_add_attr(msg, RADIUS_ATTR_CONNECT_INFO,
(u8 *) buf, os_strlen(buf))) {
printf("Could not add Connect-Info\n");
goto fail;
}
if (add_extra_attrs(msg, e->extra_attrs) < 0)
goto fail;
if (eap && !radius_msg_add_eap(msg, eap, len)) {
printf("Could not add EAP-Message\n");
goto fail;
}
/* State attribute must be copied if and only if this packet is
* Access-Request reply to the previous Access-Challenge */
if (e->last_recv_radius &&
radius_msg_get_hdr(e->last_recv_radius)->code ==
RADIUS_CODE_ACCESS_CHALLENGE) {
int res = radius_msg_copy_attr(msg, e->last_recv_radius,
RADIUS_ATTR_STATE);
if (res < 0) {
printf("Could not copy State attribute from previous "
"Access-Challenge\n");
goto fail;
}
if (res > 0) {
wpa_printf(MSG_DEBUG, " Copied RADIUS State "
"Attribute");
}
}
if (radius_client_send(e->radius, msg, RADIUS_AUTH, e->wpa_s->own_addr)
< 0)
goto fail;
return;
fail:
radius_msg_free(msg);
}
static int eapol_test_eapol_send(void *ctx, int type, const u8 *buf,
size_t len)
{
printf("WPA: eapol_test_eapol_send(type=%d len=%lu)\n",
type, (unsigned long) len);
if (type == IEEE802_1X_TYPE_EAP_PACKET) {
wpa_hexdump(MSG_DEBUG, "TX EAP -> RADIUS", buf, len);
ieee802_1x_encapsulate_radius(&eapol_test, buf, len);
}
return 0;
}
static void eapol_test_set_config_blob(void *ctx,
struct wpa_config_blob *blob)
{
struct eapol_test_data *e = ctx;
wpa_config_set_blob(e->wpa_s->conf, blob);
}
static const struct wpa_config_blob *
eapol_test_get_config_blob(void *ctx, const char *name)
{
struct eapol_test_data *e = ctx;
return wpa_config_get_blob(e->wpa_s->conf, name);
}
static void eapol_test_eapol_done_cb(void *ctx)
{
printf("WPA: EAPOL processing complete\n");
}
static void eapol_sm_reauth(void *eloop_ctx, void *timeout_ctx)
{
struct eapol_test_data *e = eloop_ctx;
printf("\n\n\n\n\neapol_test: Triggering EAP reauthentication\n\n");
e->radius_access_accept_received = 0;
send_eap_request_identity(e->wpa_s, NULL);
}
static int eapol_test_compare_pmk(struct eapol_test_data *e)
{
u8 pmk[PMK_LEN];
int ret = 1;
const u8 *sess_id;
size_t sess_id_len;
if (eapol_sm_get_key(e->wpa_s->eapol, pmk, PMK_LEN) == 0) {
wpa_hexdump(MSG_DEBUG, "PMK from EAPOL", pmk, PMK_LEN);
if (os_memcmp(pmk, e->authenticator_pmk, PMK_LEN) != 0) {
printf("WARNING: PMK mismatch\n");
wpa_hexdump(MSG_DEBUG, "PMK from AS",
e->authenticator_pmk, PMK_LEN);
} else if (e->radius_access_accept_received)
ret = 0;
} else if (e->authenticator_pmk_len == 16 &&
eapol_sm_get_key(e->wpa_s->eapol, pmk, 16) == 0) {
wpa_hexdump(MSG_DEBUG, "LEAP PMK from EAPOL", pmk, 16);
if (os_memcmp(pmk, e->authenticator_pmk, 16) != 0) {
printf("WARNING: PMK mismatch\n");
wpa_hexdump(MSG_DEBUG, "PMK from AS",
e->authenticator_pmk, 16);
} else if (e->radius_access_accept_received)
ret = 0;
} else if (e->radius_access_accept_received && e->no_mppe_keys) {
/* No keying material expected */
ret = 0;
}
if (ret && !e->no_mppe_keys)
e->num_mppe_mismatch++;
else if (!e->no_mppe_keys)
e->num_mppe_ok++;
sess_id = eapol_sm_get_session_id(e->wpa_s->eapol, &sess_id_len);
if (!sess_id)
return ret;
if (e->authenticator_eap_key_name_len == 0) {
wpa_printf(MSG_INFO, "No EAP-Key-Name received from server");
return ret;
}
if (e->authenticator_eap_key_name_len != sess_id_len ||
os_memcmp(e->authenticator_eap_key_name, sess_id, sess_id_len) != 0)
{
wpa_printf(MSG_INFO,
"Locally derived EAP Session-Id does not match EAP-Key-Name from server");
wpa_hexdump(MSG_DEBUG, "EAP Session-Id", sess_id, sess_id_len);
wpa_hexdump(MSG_DEBUG, "EAP-Key-Name from server",
e->authenticator_eap_key_name,
e->authenticator_eap_key_name_len);
} else {
wpa_printf(MSG_INFO,
"Locally derived EAP Session-Id matches EAP-Key-Name from server");
}
return ret;
}
static void eapol_sm_cb(struct eapol_sm *eapol, enum eapol_supp_result result,
void *ctx)
{
struct eapol_test_data *e = ctx;
printf("eapol_sm_cb: result=%d\n", result);
e->eapol_test_num_reauths--;
if (e->eapol_test_num_reauths < 0)
eloop_terminate();
else {
eapol_test_compare_pmk(e);
eloop_register_timeout(0, 100000, eapol_sm_reauth, e, NULL);
}
}
static void eapol_test_write_cert(FILE *f, const char *subject,
const struct wpabuf *cert)
{
unsigned char *encoded;
encoded = base64_encode(wpabuf_head(cert), wpabuf_len(cert), NULL);
if (encoded == NULL)
return;
fprintf(f, "%s\n-----BEGIN CERTIFICATE-----\n%s"
"-----END CERTIFICATE-----\n\n", subject, encoded);
os_free(encoded);
}
#if defined(CONFIG_CTRL_IFACE) || !defined(CONFIG_NO_STDOUT_DEBUG)
static void eapol_test_eap_param_needed(void *ctx, enum wpa_ctrl_req_type field,
const char *default_txt)
{
struct eapol_test_data *e = ctx;
struct wpa_supplicant *wpa_s = e->wpa_s;
struct wpa_ssid *ssid = wpa_s->current_ssid;
const char *field_name, *txt = NULL;
char *buf;
size_t buflen;
int len;
if (ssid == NULL)
return;
field_name = wpa_supplicant_ctrl_req_to_string(field, default_txt,
&txt);
if (field_name == NULL) {
wpa_printf(MSG_WARNING, "Unhandled EAP param %d needed",
field);
return;
}
buflen = 100 + os_strlen(txt) + ssid->ssid_len;
buf = os_malloc(buflen);
if (buf == NULL)
return;
len = os_snprintf(buf, buflen,
WPA_CTRL_REQ "%s-%d:%s needed for SSID ",
field_name, ssid->id, txt);
if (os_snprintf_error(buflen, len)) {
os_free(buf);
return;
}
if (ssid->ssid && buflen > len + ssid->ssid_len) {
os_memcpy(buf + len, ssid->ssid, ssid->ssid_len);
len += ssid->ssid_len;
buf[len] = '\0';
}
buf[buflen - 1] = '\0';
wpa_msg(wpa_s, MSG_INFO, "%s", buf);
os_free(buf);
}
#else /* CONFIG_CTRL_IFACE || !CONFIG_NO_STDOUT_DEBUG */
#define eapol_test_eap_param_needed NULL
#endif /* CONFIG_CTRL_IFACE || !CONFIG_NO_STDOUT_DEBUG */
static void eapol_test_cert_cb(void *ctx, int depth, const char *subject,
const char *altsubject[], int num_altsubject,
const char *cert_hash,
const struct wpabuf *cert)
{
struct eapol_test_data *e = ctx;
wpa_msg(e->wpa_s, MSG_INFO, WPA_EVENT_EAP_PEER_CERT
"depth=%d subject='%s'%s%s",
depth, subject,
cert_hash ? " hash=" : "",
cert_hash ? cert_hash : "");
if (cert) {
char *cert_hex;
size_t len = wpabuf_len(cert) * 2 + 1;
cert_hex = os_malloc(len);
if (cert_hex) {
wpa_snprintf_hex(cert_hex, len, wpabuf_head(cert),
wpabuf_len(cert));
wpa_msg_ctrl(e->wpa_s, MSG_INFO,
WPA_EVENT_EAP_PEER_CERT
"depth=%d subject='%s' cert=%s",
depth, subject, cert_hex);
os_free(cert_hex);
}
if (e->server_cert_file)
eapol_test_write_cert(e->server_cert_file,
subject, cert);
}
if (altsubject) {
int i;
for (i = 0; i < num_altsubject; i++)
wpa_msg(e->wpa_s, MSG_INFO, WPA_EVENT_EAP_PEER_ALT
"depth=%d %s", depth, altsubject[i]);
}
}
static void eapol_test_set_anon_id(void *ctx, const u8 *id, size_t len)
{
struct eapol_test_data *e = ctx;
struct wpa_supplicant *wpa_s = e->wpa_s;
char *str;
int res;
wpa_hexdump_ascii(MSG_DEBUG, "EAP method updated anonymous_identity",
id, len);
if (wpa_s->current_ssid == NULL)
return;
if (id == NULL) {
if (wpa_config_set(wpa_s->current_ssid, "anonymous_identity",
"NULL", 0) < 0)
return;
} else {
str = os_malloc(len * 2 + 1);
if (str == NULL)
return;
wpa_snprintf_hex(str, len * 2 + 1, id, len);
res = wpa_config_set(wpa_s->current_ssid, "anonymous_identity",
str, 0);
os_free(str);
if (res < 0)
return;
}
}
static int test_eapol(struct eapol_test_data *e, struct wpa_supplicant *wpa_s,
struct wpa_ssid *ssid)
{
struct eapol_config eapol_conf;
struct eapol_ctx *ctx;
ctx = os_zalloc(sizeof(*ctx));
if (ctx == NULL) {
printf("Failed to allocate EAPOL context.\n");
return -1;
}
ctx->ctx = e;
ctx->msg_ctx = wpa_s;
ctx->scard_ctx = wpa_s->scard;
ctx->cb = eapol_sm_cb;
ctx->cb_ctx = e;
ctx->eapol_send_ctx = wpa_s;
ctx->preauth = 0;
ctx->eapol_done_cb = eapol_test_eapol_done_cb;
ctx->eapol_send = eapol_test_eapol_send;
ctx->set_config_blob = eapol_test_set_config_blob;
ctx->get_config_blob = eapol_test_get_config_blob;
ctx->opensc_engine_path = wpa_s->conf->opensc_engine_path;
ctx->pkcs11_engine_path = wpa_s->conf->pkcs11_engine_path;
ctx->pkcs11_module_path = wpa_s->conf->pkcs11_module_path;
ctx->openssl_ciphers = wpa_s->conf->openssl_ciphers;
ctx->eap_param_needed = eapol_test_eap_param_needed;
ctx->cert_cb = eapol_test_cert_cb;
ctx->cert_in_cb = 1;
ctx->set_anon_id = eapol_test_set_anon_id;
wpa_s->eapol = eapol_sm_init(ctx);
if (wpa_s->eapol == NULL) {
os_free(ctx);
printf("Failed to initialize EAPOL state machines.\n");
return -1;
}
wpa_s->current_ssid = ssid;
os_memset(&eapol_conf, 0, sizeof(eapol_conf));
eapol_conf.accept_802_1x_keys = 1;
eapol_conf.required_keys = 0;
eapol_conf.fast_reauth = wpa_s->conf->fast_reauth;
eapol_conf.workaround = ssid->eap_workaround;
eapol_conf.external_sim = wpa_s->conf->external_sim;
eapol_sm_notify_config(wpa_s->eapol, &ssid->eap, &eapol_conf);
eapol_sm_register_scard_ctx(wpa_s->eapol, wpa_s->scard);
eapol_sm_notify_portValid(wpa_s->eapol, FALSE);
/* 802.1X::portControl = Auto */
eapol_sm_notify_portEnabled(wpa_s->eapol, TRUE);
return 0;
}
static void test_eapol_clean(struct eapol_test_data *e,
struct wpa_supplicant *wpa_s)
{
struct extra_radius_attr *p, *prev;
radius_client_deinit(e->radius);
wpabuf_free(e->last_eap_radius);
radius_msg_free(e->last_recv_radius);
e->last_recv_radius = NULL;
os_free(e->eap_identity);
e->eap_identity = NULL;
eapol_sm_deinit(wpa_s->eapol);
wpa_s->eapol = NULL;
if (e->radius_conf && e->radius_conf->auth_server) {
os_free(e->radius_conf->auth_server->shared_secret);
os_free(e->radius_conf->auth_server);
}
os_free(e->radius_conf);
e->radius_conf = NULL;
scard_deinit(wpa_s->scard);
if (wpa_s->ctrl_iface) {
wpa_supplicant_ctrl_iface_deinit(wpa_s->ctrl_iface);
wpa_s->ctrl_iface = NULL;
}
ext_password_deinit(wpa_s->ext_pw);
wpa_s->ext_pw = NULL;
wpa_config_free(wpa_s->conf);
p = e->extra_attrs;
while (p) {
prev = p;
p = p->next;
os_free(prev);
}
}
static void send_eap_request_identity(void *eloop_ctx, void *timeout_ctx)
{
struct wpa_supplicant *wpa_s = eloop_ctx;
u8 buf[100], *pos;
struct ieee802_1x_hdr *hdr;
struct eap_hdr *eap;
hdr = (struct ieee802_1x_hdr *) buf;
hdr->version = EAPOL_VERSION;
hdr->type = IEEE802_1X_TYPE_EAP_PACKET;
hdr->length = htons(5);
eap = (struct eap_hdr *) (hdr + 1);
eap->code = EAP_CODE_REQUEST;
eap->identifier = 0;
eap->length = htons(5);
pos = (u8 *) (eap + 1);
*pos = EAP_TYPE_IDENTITY;
printf("Sending fake EAP-Request-Identity\n");
eapol_sm_rx_eapol(wpa_s->eapol, wpa_s->bssid, buf,
sizeof(*hdr) + 5);
}
static void eapol_test_timeout(void *eloop_ctx, void *timeout_ctx)
{
struct eapol_test_data *e = eloop_ctx;
printf("EAPOL test timed out\n");
e->auth_timed_out = 1;
eloop_terminate();
}
static char *eap_type_text(u8 type)
{
switch (type) {
case EAP_TYPE_IDENTITY: return "Identity";
case EAP_TYPE_NOTIFICATION: return "Notification";
case EAP_TYPE_NAK: return "Nak";
case EAP_TYPE_TLS: return "TLS";
case EAP_TYPE_TTLS: return "TTLS";
case EAP_TYPE_PEAP: return "PEAP";
case EAP_TYPE_SIM: return "SIM";
case EAP_TYPE_GTC: return "GTC";
case EAP_TYPE_MD5: return "MD5";
case EAP_TYPE_OTP: return "OTP";
case EAP_TYPE_FAST: return "FAST";
case EAP_TYPE_SAKE: return "SAKE";
case EAP_TYPE_PSK: return "PSK";
default: return "Unknown";
}
}
static void ieee802_1x_decapsulate_radius(struct eapol_test_data *e)
{
struct wpabuf *eap;
const struct eap_hdr *hdr;
int eap_type = -1;
char buf[64];
struct radius_msg *msg;
if (e->last_recv_radius == NULL)
return;
msg = e->last_recv_radius;
eap = radius_msg_get_eap(msg);
if (eap == NULL) {
/* draft-aboba-radius-rfc2869bis-20.txt, Chap. 2.6.3:
* RADIUS server SHOULD NOT send Access-Reject/no EAP-Message
* attribute */
wpa_printf(MSG_DEBUG, "could not extract "
"EAP-Message from RADIUS message");
wpabuf_free(e->last_eap_radius);
e->last_eap_radius = NULL;
return;
}
if (wpabuf_len(eap) < sizeof(*hdr)) {
wpa_printf(MSG_DEBUG, "too short EAP packet "
"received from authentication server");
wpabuf_free(eap);
return;
}
if (wpabuf_len(eap) > sizeof(*hdr))
eap_type = (wpabuf_head_u8(eap))[sizeof(*hdr)];
hdr = wpabuf_head(eap);
switch (hdr->code) {
case EAP_CODE_REQUEST:
os_snprintf(buf, sizeof(buf), "EAP-Request-%s (%d)",
eap_type >= 0 ? eap_type_text(eap_type) : "??",
eap_type);
break;
case EAP_CODE_RESPONSE:
os_snprintf(buf, sizeof(buf), "EAP Response-%s (%d)",
eap_type >= 0 ? eap_type_text(eap_type) : "??",
eap_type);
break;
case EAP_CODE_SUCCESS:
os_strlcpy(buf, "EAP Success", sizeof(buf));
/* LEAP uses EAP Success within an authentication, so must not
* stop here with eloop_terminate(); */
break;
case EAP_CODE_FAILURE:
os_strlcpy(buf, "EAP Failure", sizeof(buf));
eloop_terminate();
break;
default:
os_strlcpy(buf, "unknown EAP code", sizeof(buf));
wpa_hexdump_buf(MSG_DEBUG, "Decapsulated EAP packet", eap);
break;
}
wpa_printf(MSG_DEBUG, "decapsulated EAP packet (code=%d "
"id=%d len=%d) from RADIUS server: %s",
hdr->code, hdr->identifier, ntohs(hdr->length), buf);
/* sta->eapol_sm->be_auth.idFromServer = hdr->identifier; */
wpabuf_free(e->last_eap_radius);
e->last_eap_radius = eap;
{
struct ieee802_1x_hdr *dot1x;
dot1x = os_malloc(sizeof(*dot1x) + wpabuf_len(eap));
assert(dot1x != NULL);
dot1x->version = EAPOL_VERSION;
dot1x->type = IEEE802_1X_TYPE_EAP_PACKET;
dot1x->length = htons(wpabuf_len(eap));
os_memcpy((u8 *) (dot1x + 1), wpabuf_head(eap),
wpabuf_len(eap));
eapol_sm_rx_eapol(e->wpa_s->eapol, e->wpa_s->bssid,
(u8 *) dot1x,
sizeof(*dot1x) + wpabuf_len(eap));
os_free(dot1x);
}
}
static void ieee802_1x_get_keys(struct eapol_test_data *e,
struct radius_msg *msg, struct radius_msg *req,
const u8 *shared_secret,
size_t shared_secret_len)
{
struct radius_ms_mppe_keys *keys;
u8 *buf;
size_t len;
keys = radius_msg_get_ms_keys(msg, req, shared_secret,
shared_secret_len);
if (keys && keys->send == NULL && keys->recv == NULL) {
os_free(keys);
keys = radius_msg_get_cisco_keys(msg, req, shared_secret,
shared_secret_len);
}
if (keys) {
if (keys->send) {
wpa_hexdump(MSG_DEBUG, "MS-MPPE-Send-Key (sign)",
keys->send, keys->send_len);
}
if (keys->recv) {
wpa_hexdump(MSG_DEBUG, "MS-MPPE-Recv-Key (crypt)",
keys->recv, keys->recv_len);
e->authenticator_pmk_len =
keys->recv_len > PMK_LEN ? PMK_LEN :
keys->recv_len;
os_memcpy(e->authenticator_pmk, keys->recv,
e->authenticator_pmk_len);
if (e->authenticator_pmk_len == 16 && keys->send &&
keys->send_len == 16) {
/* MS-CHAP-v2 derives 16 octet keys */
wpa_printf(MSG_DEBUG, "Use MS-MPPE-Send-Key "
"to extend PMK to 32 octets");
os_memcpy(e->authenticator_pmk +
e->authenticator_pmk_len,
keys->send, keys->send_len);
e->authenticator_pmk_len += keys->send_len;
}
}
os_free(keys->send);
os_free(keys->recv);
os_free(keys);
}
if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_EAP_KEY_NAME, &buf, &len,
NULL) == 0) {
os_memcpy(e->authenticator_eap_key_name, buf, len);
e->authenticator_eap_key_name_len = len;
} else {
e->authenticator_eap_key_name_len = 0;
}
}
/* Process the RADIUS frames from Authentication Server */
static RadiusRxResult
ieee802_1x_receive_auth(struct radius_msg *msg, struct radius_msg *req,
const u8 *shared_secret, size_t shared_secret_len,
void *data)
{
struct eapol_test_data *e = data;
struct radius_hdr *hdr = radius_msg_get_hdr(msg);
/* RFC 2869, Ch. 5.13: valid Message-Authenticator attribute MUST be
* present when packet contains an EAP-Message attribute */
if (hdr->code == RADIUS_CODE_ACCESS_REJECT &&
radius_msg_get_attr(msg, RADIUS_ATTR_MESSAGE_AUTHENTICATOR, NULL,
0) < 0 &&
radius_msg_get_attr(msg, RADIUS_ATTR_EAP_MESSAGE, NULL, 0) < 0) {
wpa_printf(MSG_DEBUG, "Allowing RADIUS "
"Access-Reject without Message-Authenticator "
"since it does not include EAP-Message\n");
} else if (radius_msg_verify(msg, shared_secret, shared_secret_len,
req, 1)) {
printf("Incoming RADIUS packet did not have correct "
"Message-Authenticator - dropped\n");
return RADIUS_RX_UNKNOWN;
}
if (hdr->code != RADIUS_CODE_ACCESS_ACCEPT &&
hdr->code != RADIUS_CODE_ACCESS_REJECT &&
hdr->code != RADIUS_CODE_ACCESS_CHALLENGE) {
printf("Unknown RADIUS message code\n");
return RADIUS_RX_UNKNOWN;
}
e->radius_identifier = -1;
wpa_printf(MSG_DEBUG, "RADIUS packet matching with station");
radius_msg_free(e->last_recv_radius);
e->last_recv_radius = msg;
switch (hdr->code) {
case RADIUS_CODE_ACCESS_ACCEPT:
e->radius_access_accept_received = 1;
ieee802_1x_get_keys(e, msg, req, shared_secret,
shared_secret_len);
break;
case RADIUS_CODE_ACCESS_REJECT:
e->radius_access_reject_received = 1;
break;
}
ieee802_1x_decapsulate_radius(e);
if ((hdr->code == RADIUS_CODE_ACCESS_ACCEPT &&
e->eapol_test_num_reauths < 0) ||
hdr->code == RADIUS_CODE_ACCESS_REJECT) {
eloop_terminate();
}
return RADIUS_RX_QUEUED;
}
static void wpa_init_conf(struct eapol_test_data *e,
struct wpa_supplicant *wpa_s, const char *authsrv,
int port, const char *secret,
const char *cli_addr, const char *ifname)
{
struct hostapd_radius_server *as;
int res;
wpa_s->bssid[5] = 1;
os_memcpy(wpa_s->own_addr, e->own_addr, ETH_ALEN);
e->own_ip_addr.s_addr = htonl((127 << 24) | 1);
os_strlcpy(wpa_s->ifname, ifname, sizeof(wpa_s->ifname));
e->radius_conf = os_zalloc(sizeof(struct hostapd_radius_servers));
assert(e->radius_conf != NULL);
e->radius_conf->num_auth_servers = 1;
as = os_zalloc(sizeof(struct hostapd_radius_server));
assert(as != NULL);
#if defined(CONFIG_NATIVE_WINDOWS) || defined(CONFIG_ANSI_C_EXTRA)
{
int a[4];
u8 *pos;
sscanf(authsrv, "%d.%d.%d.%d", &a[0], &a[1], &a[2], &a[3]);
pos = (u8 *) &as->addr.u.v4;
*pos++ = a[0];
*pos++ = a[1];
*pos++ = a[2];
*pos++ = a[3];
}
#else /* CONFIG_NATIVE_WINDOWS or CONFIG_ANSI_C_EXTRA */
if (hostapd_parse_ip_addr(authsrv, &as->addr) < 0) {
wpa_printf(MSG_ERROR, "Invalid IP address '%s'",
authsrv);
assert(0);
}
#endif /* CONFIG_NATIVE_WINDOWS or CONFIG_ANSI_C_EXTRA */
as->port = port;
as->shared_secret = (u8 *) os_strdup(secret);
as->shared_secret_len = os_strlen(secret);
e->radius_conf->auth_server = as;
e->radius_conf->auth_servers = as;
e->radius_conf->msg_dumps = 1;
if (cli_addr) {
if (hostapd_parse_ip_addr(cli_addr,
&e->radius_conf->client_addr) == 0)
e->radius_conf->force_client_addr = 1;
else {
wpa_printf(MSG_ERROR, "Invalid IP address '%s'",
cli_addr);
assert(0);
}
}
e->radius = radius_client_init(wpa_s, e->radius_conf);
assert(e->radius != NULL);
res = radius_client_register(e->radius, RADIUS_AUTH,
ieee802_1x_receive_auth, e);
assert(res == 0);
}
static int scard_test(struct eapol_test_data *e)
{
struct scard_data *scard;
size_t len;
char imsi[20];
unsigned char _rand[16];
#ifdef PCSC_FUNCS
unsigned char sres[4];
unsigned char kc[8];
#endif /* PCSC_FUNCS */
#define num_triplets 5
unsigned char rand_[num_triplets][16];
unsigned char sres_[num_triplets][4];
unsigned char kc_[num_triplets][8];
int i, res;
size_t j;
#define AKA_RAND_LEN 16
#define AKA_AUTN_LEN 16
#define AKA_AUTS_LEN 14
#define RES_MAX_LEN 16
#define IK_LEN 16
#define CK_LEN 16
unsigned char aka_rand[AKA_RAND_LEN];
unsigned char aka_autn[AKA_AUTN_LEN];
unsigned char aka_auts[AKA_AUTS_LEN];
unsigned char aka_res[RES_MAX_LEN];
size_t aka_res_len;
unsigned char aka_ik[IK_LEN];
unsigned char aka_ck[CK_LEN];
scard = scard_init(e->pcsc_reader);
if (scard == NULL)
return -1;
if (scard_set_pin(scard, e->pcsc_pin)) {
wpa_printf(MSG_WARNING, "PIN validation failed");
scard_deinit(scard);
return -1;
}
len = sizeof(imsi);
if (scard_get_imsi(scard, imsi, &len))
goto failed;
wpa_hexdump_ascii(MSG_DEBUG, "SCARD: IMSI", (u8 *) imsi, len);
/* NOTE: Permanent Username: 1 | IMSI */
wpa_printf(MSG_DEBUG, "SCARD: MNC length %d",
scard_get_mnc_len(scard));
os_memset(_rand, 0, sizeof(_rand));
if (scard_gsm_auth(scard, _rand, sres, kc))
goto failed;
os_memset(_rand, 0xff, sizeof(_rand));
if (scard_gsm_auth(scard, _rand, sres, kc))
goto failed;
for (i = 0; i < num_triplets; i++) {
os_memset(rand_[i], i, sizeof(rand_[i]));
if (scard_gsm_auth(scard, rand_[i], sres_[i], kc_[i]))
goto failed;
}
for (i = 0; i < num_triplets; i++) {
printf("1");
for (j = 0; j < len; j++)
printf("%c", imsi[j]);
printf(",");
for (j = 0; j < 16; j++)
printf("%02X", rand_[i][j]);
printf(",");
for (j = 0; j < 4; j++)
printf("%02X", sres_[i][j]);
printf(",");
for (j = 0; j < 8; j++)
printf("%02X", kc_[i][j]);
printf("\n");
}
wpa_printf(MSG_DEBUG, "Trying to use UMTS authentication");
/* seq 39 (0x28) */
os_memset(aka_rand, 0xaa, 16);
os_memcpy(aka_autn, "\x86\x71\x31\xcb\xa2\xfc\x61\xdf"
"\xa3\xb3\x97\x9d\x07\x32\xa2\x12", 16);
res = scard_umts_auth(scard, aka_rand, aka_autn, aka_res, &aka_res_len,
aka_ik, aka_ck, aka_auts);
if (res == 0) {
wpa_printf(MSG_DEBUG, "UMTS auth completed successfully");
wpa_hexdump(MSG_DEBUG, "RES", aka_res, aka_res_len);
wpa_hexdump(MSG_DEBUG, "IK", aka_ik, IK_LEN);
wpa_hexdump(MSG_DEBUG, "CK", aka_ck, CK_LEN);
} else if (res == -2) {
wpa_printf(MSG_DEBUG, "UMTS auth resulted in synchronization "
"failure");
wpa_hexdump(MSG_DEBUG, "AUTS", aka_auts, AKA_AUTS_LEN);
} else {
wpa_printf(MSG_DEBUG, "UMTS auth failed");
}
failed:
scard_deinit(scard);
return 0;
#undef num_triplets
}
static int scard_get_triplets(struct eapol_test_data *e, int argc, char *argv[])
{
struct scard_data *scard;
size_t len;
char imsi[20];
unsigned char _rand[16];
unsigned char sres[4];
unsigned char kc[8];
int num_triplets;
int i;
size_t j;
if (argc < 2 || ((num_triplets = atoi(argv[1])) <= 0)) {
printf("invalid parameters for sim command\n");
return -1;
}
if (argc <= 2 || os_strcmp(argv[2], "debug") != 0) {
/* disable debug output */
wpa_debug_level = 99;
}
scard = scard_init(e->pcsc_reader);
if (scard == NULL) {
printf("Failed to open smartcard connection\n");
return -1;
}
if (scard_set_pin(scard, argv[0])) {
wpa_printf(MSG_WARNING, "PIN validation failed");
scard_deinit(scard);
return -1;
}
len = sizeof(imsi);
if (scard_get_imsi(scard, imsi, &len)) {
scard_deinit(scard);
return -1;
}
for (i = 0; i < num_triplets; i++) {
os_memset(_rand, i, sizeof(_rand));
if (scard_gsm_auth(scard, _rand, sres, kc))
break;
/* IMSI:Kc:SRES:RAND */
for (j = 0; j < len; j++)
printf("%c", imsi[j]);
printf(":");
for (j = 0; j < 8; j++)
printf("%02X", kc[j]);
printf(":");
for (j = 0; j < 4; j++)
printf("%02X", sres[j]);
printf(":");
for (j = 0; j < 16; j++)
printf("%02X", _rand[j]);
printf("\n");
}
scard_deinit(scard);
return 0;
}
static void eapol_test_terminate(int sig, void *signal_ctx)
{
struct wpa_supplicant *wpa_s = signal_ctx;
wpa_msg(wpa_s, MSG_INFO, "Signal %d received - terminating", sig);
eloop_terminate();
}
static void usage(void)
{
printf("usage:\n"
"eapol_test [-enWS] -c<conf> [-a<AS IP>] [-p<AS port>] "
"[-s<AS secret>]\\\n"
" [-r<count>] [-t<timeout>] [-C<Connect-Info>] \\\n"
" [-M<client MAC address>] [-o<server cert file] \\\n"
" [-N<attr spec>] [-R<PC/SC reader>] "
"[-P<PC/SC PIN>] \\\n"
" [-A<client IP>] [-i<ifname>]\n"
"eapol_test scard\n"
"eapol_test sim <PIN> <num triplets> [debug]\n"
"\n");
printf("options:\n"
" -c<conf> = configuration file\n"
" -a<AS IP> = IP address of the authentication server, "
"default 127.0.0.1\n"
" -p<AS port> = UDP port of the authentication server, "
"default 1812\n"
" -s<AS secret> = shared secret with the authentication "
"server, default 'radius'\n"
" -A<client IP> = IP address of the client, default: select "
"automatically\n"
" -r<count> = number of re-authentications\n"
" -e = Request EAP-Key-Name\n"
" -W = wait for a control interface monitor before starting\n"
" -S = save configuration after authentication\n"
" -n = no MPPE keys expected\n"
" -t<timeout> = sets timeout in seconds (default: 30 s)\n"
" -C<Connect-Info> = RADIUS Connect-Info (default: "
"CONNECT 11Mbps 802.11b)\n"
" -M<client MAC address> = Set own MAC address "
"(Calling-Station-Id,\n"
" default: 02:00:00:00:00:01)\n"
" -o<server cert file> = Write received server certificate\n"
" chain to the specified file\n"
" -N<attr spec> = send arbitrary attribute specified by:\n"
" attr_id:syntax:value or attr_id\n"
" attr_id - number id of the attribute\n"
" syntax - one of: s, d, x\n"
" s = string\n"
" d = integer\n"
" x = octet string\n"
" value - attribute value.\n"
" When only attr_id is specified, NULL will be used as "
"value.\n"
" Multiple attributes can be specified by using the "
"option several times.\n");
}
int main(int argc, char *argv[])
{
struct wpa_global global;
struct wpa_supplicant wpa_s;
int c, ret = 1, wait_for_monitor = 0, save_config = 0;
char *as_addr = "127.0.0.1";
int as_port = 1812;
char *as_secret = "radius";
char *cli_addr = NULL;
char *conf = NULL;
int timeout = 30;
char *pos;
struct extra_radius_attr *p = NULL, *p1;
const char *ifname = "test";
if (os_program_init())
return -1;
hostapd_logger_register_cb(hostapd_logger_cb);
os_memset(&eapol_test, 0, sizeof(eapol_test));
eapol_test.connect_info = "CONNECT 11Mbps 802.11b";
os_memcpy(eapol_test.own_addr, "\x02\x00\x00\x00\x00\x01", ETH_ALEN);
eapol_test.pcsc_pin = "1234";
wpa_debug_level = 0;
wpa_debug_show_keys = 1;
for (;;) {
c = getopt(argc, argv, "a:A:c:C:ei:M:nN:o:p:P:r:R:s:St:W");
if (c < 0)
break;
switch (c) {
case 'a':
as_addr = optarg;
break;
case 'A':
cli_addr = optarg;
break;
case 'c':
conf = optarg;
break;
case 'C':
eapol_test.connect_info = optarg;
break;
case 'e':
eapol_test.req_eap_key_name = 1;
break;
case 'i':
ifname = optarg;
break;
case 'M':
if (hwaddr_aton(optarg, eapol_test.own_addr)) {
usage();
return -1;
}
break;
case 'n':
eapol_test.no_mppe_keys++;
break;
case 'o':
if (eapol_test.server_cert_file)
fclose(eapol_test.server_cert_file);
eapol_test.server_cert_file = fopen(optarg, "w");
if (eapol_test.server_cert_file == NULL) {
printf("Could not open '%s' for writing\n",
optarg);
return -1;
}
break;
case 'p':
as_port = atoi(optarg);
break;
case 'P':
eapol_test.pcsc_pin = optarg;
break;
case 'r':
eapol_test.eapol_test_num_reauths = atoi(optarg);
break;
case 'R':
eapol_test.pcsc_reader = optarg;
break;
case 's':
as_secret = optarg;
break;
case 'S':
save_config++;
break;
case 't':
timeout = atoi(optarg);
break;
case 'W':
wait_for_monitor++;
break;
case 'N':
p1 = os_zalloc(sizeof(*p1));
if (p1 == NULL)
break;
if (!p)
eapol_test.extra_attrs = p1;
else
p->next = p1;
p = p1;
p->type = atoi(optarg);
pos = os_strchr(optarg, ':');
if (pos == NULL) {
p->syntax = 'n';
p->data = NULL;
break;
}
pos++;
if (pos[0] == '\0' || pos[1] != ':') {
printf("Incorrect format of attribute "
"specification\n");
break;
}
p->syntax = pos[0];
p->data = pos + 2;
break;
default:
usage();
return -1;
}
}
if (argc > optind && os_strcmp(argv[optind], "scard") == 0) {
return scard_test(&eapol_test);
}
if (argc > optind && os_strcmp(argv[optind], "sim") == 0) {
return scard_get_triplets(&eapol_test, argc - optind - 1,
&argv[optind + 1]);
}
if (conf == NULL) {
usage();
printf("Configuration file is required.\n");
return -1;
}
if (eap_register_methods()) {
wpa_printf(MSG_ERROR, "Failed to register EAP methods");
return -1;
}
if (eloop_init()) {
wpa_printf(MSG_ERROR, "Failed to initialize event loop");
return -1;
}
os_memset(&global, 0, sizeof(global));
os_memset(&wpa_s, 0, sizeof(wpa_s));
wpa_s.global = &global;
eapol_test.wpa_s = &wpa_s;
dl_list_init(&wpa_s.bss);
dl_list_init(&wpa_s.bss_id);
wpa_s.conf = wpa_config_read(conf, NULL);
if (wpa_s.conf == NULL) {
printf("Failed to parse configuration file '%s'.\n", conf);
return -1;
}
if (wpa_s.conf->ssid == NULL) {
printf("No networks defined.\n");
return -1;
}
if (eapol_test.pcsc_reader) {
os_free(wpa_s.conf->pcsc_reader);
wpa_s.conf->pcsc_reader = os_strdup(eapol_test.pcsc_reader);
}
wpa_init_conf(&eapol_test, &wpa_s, as_addr, as_port, as_secret,
cli_addr, ifname);
wpa_s.ctrl_iface = wpa_supplicant_ctrl_iface_init(&wpa_s);
if (wpa_s.ctrl_iface == NULL) {
printf("Failed to initialize control interface '%s'.\n"
"You may have another eapol_test process already "
"running or the file was\n"
"left by an unclean termination of eapol_test in "
"which case you will need\n"
"to manually remove this file before starting "
"eapol_test again.\n",
wpa_s.conf->ctrl_interface);
return -1;
}
if (wpa_supplicant_scard_init(&wpa_s, wpa_s.conf->ssid))
return -1;
if (test_eapol(&eapol_test, &wpa_s, wpa_s.conf->ssid))
return -1;
if (wpas_init_ext_pw(&wpa_s) < 0)
return -1;
if (wait_for_monitor)
wpa_supplicant_ctrl_iface_wait(wpa_s.ctrl_iface);
eloop_register_timeout(timeout, 0, eapol_test_timeout, &eapol_test,
NULL);
eloop_register_timeout(0, 0, send_eap_request_identity, &wpa_s, NULL);
eloop_register_signal_terminate(eapol_test_terminate, &wpa_s);
eloop_register_signal_reconfig(eapol_test_terminate, &wpa_s);
eloop_run();
eloop_cancel_timeout(eapol_test_timeout, &eapol_test, NULL);
eloop_cancel_timeout(eapol_sm_reauth, &eapol_test, NULL);
if (eapol_test_compare_pmk(&eapol_test) == 0 ||
eapol_test.no_mppe_keys)
ret = 0;
if (eapol_test.auth_timed_out)
ret = -2;
if (eapol_test.radius_access_reject_received)
ret = -3;
if (save_config)
wpa_config_write(conf, wpa_s.conf);
test_eapol_clean(&eapol_test, &wpa_s);
eap_peer_unregister_methods();
#ifdef CONFIG_AP
eap_server_unregister_methods();
#endif /* CONFIG_AP */
eloop_destroy();
if (eapol_test.server_cert_file)
fclose(eapol_test.server_cert_file);
printf("MPPE keys OK: %d mismatch: %d\n",
eapol_test.num_mppe_ok, eapol_test.num_mppe_mismatch);
if (eapol_test.num_mppe_mismatch)
ret = -4;
if (ret)
printf("FAILURE\n");
else
printf("SUCCESS\n");
os_program_deinit();
return ret;
}