RADIUS server: Add support for MAC ACL

"user" MACACL "password" style lines in the eap_user file can now be
used to configured user entries for RADIUS-based MAC ACL.

Signed-off-by: Jouni Malinen <j@w1.fi>
This commit is contained in:
Jouni Malinen 2014-03-29 19:31:56 +02:00
parent 81699e2e6e
commit 8943cc998a
8 changed files with 135 additions and 15 deletions

View file

@ -366,6 +366,10 @@ static int hostapd_config_read_eap_user(const char *fname,
EAP_TTLS_AUTH_MSCHAPV2; EAP_TTLS_AUTH_MSCHAPV2;
goto skip_eap; goto skip_eap;
} }
if (os_strcmp(start, "MACACL") == 0) {
user->macacl = 1;
goto skip_eap;
}
wpa_printf(MSG_ERROR, "Unsupported EAP type " wpa_printf(MSG_ERROR, "Unsupported EAP type "
"'%s' on line %d in '%s'", "'%s' on line %d in '%s'",
start, line, fname); start, line, fname);
@ -380,7 +384,7 @@ static int hostapd_config_read_eap_user(const char *fname,
break; break;
start = pos3; start = pos3;
} }
if (num_methods == 0 && user->ttls_auth == 0) { if (num_methods == 0 && user->ttls_auth == 0 && !user->macacl) {
wpa_printf(MSG_ERROR, "No EAP types configured on " wpa_printf(MSG_ERROR, "No EAP types configured on "
"line %d in '%s'", line, fname); "line %d in '%s'", line, fname);
goto failed; goto failed;

View file

@ -127,6 +127,7 @@ struct hostapd_eap_user {
unsigned int password_hash:1; /* whether password is hashed with unsigned int password_hash:1; /* whether password is hashed with
* nt_password_hash() */ * nt_password_hash() */
unsigned int remediation:1; unsigned int remediation:1;
unsigned int macacl:1;
int ttls_auth; /* EAP_TTLS_AUTH_* bitfield */ int ttls_auth; /* EAP_TTLS_AUTH_* bitfield */
struct hostapd_radius_attr *accept_attr; struct hostapd_radius_attr *accept_attr;
}; };

View file

@ -79,6 +79,7 @@ static int hostapd_radius_get_eap_user(void *ctx, const u8 *identity,
user->password_hash = eap_user->password_hash; user->password_hash = eap_user->password_hash;
} }
user->force_version = eap_user->force_version; user->force_version = eap_user->force_version;
user->macacl = eap_user->macacl;
user->ttls_auth = eap_user->ttls_auth; user->ttls_auth = eap_user->ttls_auth;
user->remediation = eap_user->remediation; user->remediation = eap_user->remediation;
user->accept_attr = eap_user->accept_attr; user->accept_attr = eap_user->accept_attr;

View file

@ -1787,6 +1787,7 @@ static int ieee802_1x_get_eap_user(void *ctx, const u8 *identity,
user->password_hash = eap_user->password_hash; user->password_hash = eap_user->password_hash;
} }
user->force_version = eap_user->force_version; user->force_version = eap_user->force_version;
user->macacl = eap_user->macacl;
user->ttls_auth = eap_user->ttls_auth; user->ttls_auth = eap_user->ttls_auth;
user->remediation = eap_user->remediation; user->remediation = eap_user->remediation;

View file

@ -33,6 +33,7 @@ struct eap_user {
int phase2; int phase2;
int force_version; int force_version;
unsigned int remediation:1; unsigned int remediation:1;
unsigned int macacl:1;
int ttls_auth; /* bitfield of int ttls_auth; /* bitfield of
* EAP_TTLS_AUTH_{PAP,CHAP,MSCHAP,MSCHAPV2} */ * EAP_TTLS_AUTH_{PAP,CHAP,MSCHAP,MSCHAPV2} */
struct hostapd_radius_attr *accept_attr; struct hostapd_radius_attr *accept_attr;

View file

@ -1247,30 +1247,28 @@ int radius_msg_add_wfa(struct radius_msg *msg, u8 subtype, const u8 *data,
} }
/* Add User-Password attribute to a RADIUS message and encrypt it as specified int radius_user_password_hide(struct radius_msg *msg,
* in RFC 2865, Chap. 5.2 */ const u8 *data, size_t data_len,
struct radius_attr_hdr * const u8 *secret, size_t secret_len,
radius_msg_add_attr_user_password(struct radius_msg *msg, u8 *buf, size_t buf_len)
const u8 *data, size_t data_len,
const u8 *secret, size_t secret_len)
{ {
u8 buf[128]; size_t padlen, i, pos;
size_t padlen, i, buf_len, pos;
const u8 *addr[2]; const u8 *addr[2];
size_t len[2]; size_t len[2];
u8 hash[16]; u8 hash[16];
if (data_len > 128) if (data_len + 16 > buf_len)
return NULL; return -1;
os_memcpy(buf, data, data_len); os_memcpy(buf, data, data_len);
buf_len = data_len;
padlen = data_len % 16; padlen = data_len % 16;
if (padlen && data_len < sizeof(buf)) { if (padlen && data_len < buf_len) {
padlen = 16 - padlen; padlen = 16 - padlen;
os_memset(buf + data_len, 0, padlen); os_memset(buf + data_len, 0, padlen);
buf_len += padlen; buf_len = data_len + padlen;
} else {
buf_len = data_len;
} }
addr[0] = secret; addr[0] = secret;
@ -1296,8 +1294,27 @@ radius_msg_add_attr_user_password(struct radius_msg *msg,
pos += 16; pos += 16;
} }
return buf_len;
}
/* Add User-Password attribute to a RADIUS message and encrypt it as specified
* in RFC 2865, Chap. 5.2 */
struct radius_attr_hdr *
radius_msg_add_attr_user_password(struct radius_msg *msg,
const u8 *data, size_t data_len,
const u8 *secret, size_t secret_len)
{
u8 buf[128];
int res;
res = radius_user_password_hide(msg, data, data_len,
secret, secret_len, buf, sizeof(buf));
if (res < 0)
return NULL;
return radius_msg_add_attr(msg, RADIUS_ATTR_USER_PASSWORD, return radius_msg_add_attr(msg, RADIUS_ATTR_USER_PASSWORD,
buf, buf_len); buf, res);
} }

View file

@ -251,6 +251,10 @@ int radius_msg_add_mppe_keys(struct radius_msg *msg,
const u8 *recv_key, size_t recv_key_len); const u8 *recv_key, size_t recv_key_len);
int radius_msg_add_wfa(struct radius_msg *msg, u8 subtype, const u8 *data, int radius_msg_add_wfa(struct radius_msg *msg, u8 subtype, const u8 *data,
size_t len); size_t len);
int radius_user_password_hide(struct radius_msg *msg,
const u8 *data, size_t data_len,
const u8 *secret, size_t secret_len,
u8 *buf, size_t buf_len);
struct radius_attr_hdr * struct radius_attr_hdr *
radius_msg_add_attr_user_password(struct radius_msg *msg, radius_msg_add_attr_user_password(struct radius_msg *msg,
const u8 *data, size_t data_len, const u8 *data, size_t data_len,

View file

@ -86,6 +86,7 @@ struct radius_session {
u8 last_authenticator[16]; u8 last_authenticator[16];
unsigned int remediation:1; unsigned int remediation:1;
unsigned int macacl:1;
struct hostapd_radius_attr *accept_attr; struct hostapd_radius_attr *accept_attr;
}; };
@ -636,6 +637,7 @@ radius_server_get_new_session(struct radius_server_data *data,
return NULL; return NULL;
} }
sess->accept_attr = tmp.accept_attr; sess->accept_attr = tmp.accept_attr;
sess->macacl = tmp.macacl;
sess->username = os_malloc(user_len * 2 + 1); sess->username = os_malloc(user_len * 2 + 1);
if (sess->username == NULL) { if (sess->username == NULL) {
@ -823,6 +825,87 @@ radius_server_encapsulate_eap(struct radius_server_data *data,
} }
static struct radius_msg *
radius_server_macacl(struct radius_server_data *data,
struct radius_client *client,
struct radius_session *sess,
struct radius_msg *request)
{
struct radius_msg *msg;
int code;
struct radius_hdr *hdr = radius_msg_get_hdr(request);
u8 *pw;
size_t pw_len;
code = RADIUS_CODE_ACCESS_ACCEPT;
if (radius_msg_get_attr_ptr(request, RADIUS_ATTR_USER_PASSWORD, &pw,
&pw_len, NULL) < 0) {
RADIUS_DEBUG("Could not get User-Password");
code = RADIUS_CODE_ACCESS_REJECT;
} else {
int res;
struct eap_user tmp;
os_memset(&tmp, 0, sizeof(tmp));
res = data->get_eap_user(data->conf_ctx, (u8 *) sess->username,
os_strlen(sess->username), 0, &tmp);
if (res || !tmp.macacl || tmp.password == NULL) {
RADIUS_DEBUG("No MAC ACL user entry");
os_free(tmp.password);
code = RADIUS_CODE_ACCESS_REJECT;
} else {
u8 buf[128];
res = radius_user_password_hide(
request, tmp.password, tmp.password_len,
(u8 *) client->shared_secret,
client->shared_secret_len,
buf, sizeof(buf));
os_free(tmp.password);
if (res < 0 || pw_len != (size_t) res ||
os_memcmp(pw, buf, res) != 0) {
RADIUS_DEBUG("Incorrect User-Password");
code = RADIUS_CODE_ACCESS_REJECT;
}
}
}
msg = radius_msg_new(code, hdr->identifier);
if (msg == NULL) {
RADIUS_DEBUG("Failed to allocate reply message");
return NULL;
}
if (radius_msg_copy_attr(msg, request, RADIUS_ATTR_PROXY_STATE) < 0) {
RADIUS_DEBUG("Failed to copy Proxy-State attribute(s)");
radius_msg_free(msg);
return NULL;
}
if (code == RADIUS_CODE_ACCESS_ACCEPT) {
struct hostapd_radius_attr *attr;
for (attr = sess->accept_attr; attr; attr = attr->next) {
if (!radius_msg_add_attr(msg, attr->type,
wpabuf_head(attr->val),
wpabuf_len(attr->val))) {
wpa_printf(MSG_ERROR, "Could not add RADIUS attribute");
radius_msg_free(msg);
return NULL;
}
}
}
if (radius_msg_finish_srv(msg, (u8 *) client->shared_secret,
client->shared_secret_len,
hdr->authenticator) < 0) {
RADIUS_DEBUG("Failed to add Message-Authenticator attribute");
}
return msg;
}
static int radius_server_reject(struct radius_server_data *data, static int radius_server_reject(struct radius_server_data *data,
struct radius_client *client, struct radius_client *client,
struct radius_msg *request, struct radius_msg *request,
@ -958,6 +1041,12 @@ static int radius_server_request(struct radius_server_data *data,
} }
eap = radius_msg_get_eap(msg); eap = radius_msg_get_eap(msg);
if (eap == NULL && sess->macacl) {
reply = radius_server_macacl(data, client, sess, msg);
if (reply == NULL)
return -1;
goto send_reply;
}
if (eap == NULL) { if (eap == NULL) {
RADIUS_DEBUG("No EAP-Message in RADIUS packet from %s", RADIUS_DEBUG("No EAP-Message in RADIUS packet from %s",
from_addr); from_addr);
@ -1015,6 +1104,7 @@ static int radius_server_request(struct radius_server_data *data,
reply = radius_server_encapsulate_eap(data, client, sess, msg); reply = radius_server_encapsulate_eap(data, client, sess, msg);
send_reply:
if (reply) { if (reply) {
struct wpabuf *buf; struct wpabuf *buf;
struct radius_hdr *hdr; struct radius_hdr *hdr;
@ -1904,6 +1994,7 @@ static int radius_server_get_eap_user(void *ctx, const u8 *identity,
if (ret == 0 && user) { if (ret == 0 && user) {
sess->accept_attr = user->accept_attr; sess->accept_attr = user->accept_attr;
sess->remediation = user->remediation; sess->remediation = user->remediation;
sess->macacl = user->macacl;
} }
return ret; return ret;
} }