diff options
author | Andreas Steffen <andreas.steffen@strongswan.org> | 2012-03-08 10:22:56 +0100 |
---|---|---|
committer | Andreas Steffen <andreas.steffen@strongswan.org> | 2012-03-13 16:27:17 +0100 |
commit | d4db9f44c2f940790a5af3c41ab9adcca6c0edac (patch) | |
tree | 31bbd89d4e6d570257834947669b432728e849e3 /src | |
parent | e9f32b010a83af6ee68b36b7b96b71236ed4da7d (diff) | |
download | strongswan-d4db9f44c2f940790a5af3c41ab9adcca6c0edac.tar.bz2 strongswan-d4db9f44c2f940790a5af3c41ab9adcca6c0edac.tar.xz |
simple RADIUS server example works
Diffstat (limited to 'src')
-rw-r--r-- | src/libcharon/plugins/tnc_pdp/tnc_pdp.c | 201 | ||||
-rw-r--r-- | src/libradius/radius_message.c | 74 |
2 files changed, 233 insertions, 42 deletions
diff --git a/src/libcharon/plugins/tnc_pdp/tnc_pdp.c b/src/libcharon/plugins/tnc_pdp/tnc_pdp.c index aedabeb18..0edecc845 100644 --- a/src/libcharon/plugins/tnc_pdp/tnc_pdp.c +++ b/src/libcharon/plugins/tnc_pdp/tnc_pdp.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010 Andreas Steffen + * Copyright (C) 2012 Andreas Steffen * HSR Hochschule fuer Technik Rapperswil * * This program is free software; you can redistribute it and/or modify it @@ -24,6 +24,7 @@ #include <debug.h> #include <threading/thread.h> #include <processing/jobs/callback_job.h> +#include <sa/authenticators/eap/eap_method.h> typedef struct private_tnc_pdp_t private_tnc_pdp_t; @@ -57,6 +58,25 @@ struct private_tnc_pdp_t { */ callback_job_t *job; + /** + * RADIUS shared secret + */ + chunk_t secret; + + /** + * MD5 hasher + */ + hasher_t *hasher; + + /** + * HMAC MD5 signer, with secret set + */ + signer_t *signer; + + /** + * EAP method + */ + eap_method_t *method; }; @@ -102,12 +122,12 @@ static int open_socket(private_tnc_pdp_t *this, int family, u_int16_t port) skt = socket(family, SOCK_DGRAM, IPPROTO_UDP); if (skt < 0) { - DBG1(DBG_NET, "opening RADIUS socket failed: %s", strerror(errno)); + DBG1(DBG_CFG, "opening RADIUS socket failed: %s", strerror(errno)); return 0; } if (setsockopt(skt, SOL_SOCKET, SO_REUSEADDR, (void*)&on, sizeof(on)) < 0) { - DBG1(DBG_NET, "unable to set SO_REUSEADDR on socket: %s", strerror(errno)); + DBG1(DBG_CFG, "unable to set SO_REUSEADDR on socket: %s", strerror(errno)); close(skt); return 0; } @@ -115,7 +135,7 @@ static int open_socket(private_tnc_pdp_t *this, int family, u_int16_t port) /* bind the socket */ if (bind(skt, (struct sockaddr *)&addr, addrlen) < 0) { - DBG1(DBG_NET, "unable to bind RADIUS socket: %s", strerror(errno)); + DBG1(DBG_CFG, "unable to bind RADIUS socket: %s", strerror(errno)); close(skt); return 0; } @@ -124,6 +144,127 @@ static int open_socket(private_tnc_pdp_t *this, int family, u_int16_t port) } /** + * Send a RADIUS message to client + */ +static void send_message(private_tnc_pdp_t *this, radius_message_t *message, + host_t *client) +{ + int fd; + chunk_t data; + + fd = (client->get_family(client) == AF_INET) ? this->ipv4 : this->ipv6; + data = message->get_encoding(message); + + DBG2(DBG_CFG, "sending RADIUS packet to %#H", client); + DBG3(DBG_CFG, "%B", &data); + + if (sendto(fd, data.ptr, data.len, 0, client->get_sockaddr(client), + *client->get_sockaddr_len(client)) != data.len) + { + DBG1(DBG_CFG, "sending RADIUS message failed: %s", strerror(errno)); + } +} + +/** + * Send a RADIUS response for a request + */ +static void send_response(private_tnc_pdp_t *this, + radius_message_t *request, radius_message_code_t code, + eap_payload_t *eap, host_t *client) +{ + radius_message_t *response; + chunk_t data; + + response = radius_message_create(code); + if (eap) + { + data = eap->get_data(eap); + response->add(response, RAT_EAP_MESSAGE, data); + } + response->set_identifier(response, request->get_identifier(request)); + response->sign(response, request->get_authenticator(request), + this->secret, this->hasher, this->signer, NULL); + + DBG1(DBG_CFG, "sending RADIUS %N to client '%H'", radius_message_code_names, + code, client); + send_message(this, response, client); +} + +/** + * Process EAP message + */ +static void process_eap(private_tnc_pdp_t *this, radius_message_t *request, + host_t *source) +{ + enumerator_t *enumerator; + eap_payload_t *in, *out = NULL; + eap_type_t eap_type; + chunk_t data, message = chunk_empty; + radius_message_code_t code = RMC_ACCESS_CHALLENGE; + u_int32_t eap_vendor; + int type; + + enumerator = request->create_enumerator(request); + while (enumerator->enumerate(enumerator, &type, &data)) + { + if (type == RAT_EAP_MESSAGE && data.len) + { + message = chunk_cat("mc", message, data); + } + } + enumerator->destroy(enumerator); + + if (message.len) + { + in = eap_payload_create_data(message); + + /* apply EAP method selected by RADIUS server */ + eap_type = in->get_type(in, &eap_vendor); + + DBG3(DBG_CFG, "%N payload %B", eap_type_names, eap_type, &message); + free(message.ptr); + + if (eap_type == EAP_IDENTITY) + { + identification_t *server, *peer; + + peer = identification_create_from_string("carol@strongswan.org"); + server = identification_create_from_string("server"); + this->method = charon->eap->create_instance(charon->eap, EAP_MD5, 0, + EAP_SERVER, server, peer); + if (!this->method) + { + peer->destroy(peer); + server->destroy(server); + in->destroy(in); + return; + } + this->method->initiate(this->method, &out); + } + else + { + switch (this->method->process(this->method, in, &out)) + { + case NEED_MORE: + code = RMC_ACCESS_CHALLENGE; + break; + case SUCCESS: + code = RMC_ACCESS_ACCEPT; + break; + case FAILED: + default: + code = RMC_ACCESS_REJECT; + } + } + + send_response(this, request, code, out, source); + + in->destroy(in); + DESTROY_IF(out); + } +} + +/** * Process packets received on the RADIUS socket */ static job_requeue_t receive(private_tnc_pdp_t *this) @@ -155,7 +296,7 @@ static job_requeue_t receive(private_tnc_pdp_t *this) } max_fd = max(this->ipv4, this->ipv6); - DBG2(DBG_NET, "waiting for data on RADIUS sockets"); + DBG2(DBG_CFG, "waiting for data on RADIUS sockets"); oldstate = thread_cancelability(TRUE); if (select(max_fd + 1, &rfds, NULL, NULL, NULL) <= 0) { @@ -190,26 +331,34 @@ static job_requeue_t receive(private_tnc_pdp_t *this) bytes_read = recvmsg(selected, &msg, 0); if (bytes_read < 0) { - DBG1(DBG_NET, "error reading RADIUS socket: %s", strerror(errno)); + DBG1(DBG_CFG, "error reading RADIUS socket: %s", strerror(errno)); continue; } if (msg.msg_flags & MSG_TRUNC) { - DBG1(DBG_NET, "receive buffer too small, RADIUS packet discarded"); + DBG1(DBG_CFG, "receive buffer too small, RADIUS packet discarded"); continue; } source = host_create_from_sockaddr((sockaddr_t*)&src); - DBG2(DBG_NET, "received RADIUS packet from %#H", source); - DBG3(DBG_NET, "%b", buffer, bytes_read); - request = radius_message_parse_response(chunk_create(buffer, bytes_read)); + DBG2(DBG_CFG, "received RADIUS packet from %#H", source); + DBG3(DBG_CFG, "%b", buffer, bytes_read); + request = radius_message_parse(chunk_create(buffer, bytes_read)); if (request) { - DBG1(DBG_NET, "received RADIUS %N from client '%H'", - radius_message_code_names, request->get_code(request), source); + DBG1(DBG_CFG, "received RADIUS %N from client '%H'", + radius_message_code_names, request->get_code(request), source); + + if (request->verify(request, NULL, this->secret, this->hasher, + this->signer)) + { + process_eap(this, request, source); + } + request->destroy(request); + } else { - DBG1(DBG_NET, "received invalid RADIUS message, ignored"); + DBG1(DBG_CFG, "received invalid RADIUS message, ignored"); } source->destroy(source); } @@ -219,7 +368,10 @@ static job_requeue_t receive(private_tnc_pdp_t *this) METHOD(tnc_pdp_t, destroy, void, private_tnc_pdp_t *this) { - this->job->cancel(this->job); + if (this->job) + { + this->job->cancel(this->job); + } if (this->ipv4) { close(this->ipv4); @@ -228,6 +380,9 @@ METHOD(tnc_pdp_t, destroy, void, { close(this->ipv6); } + DESTROY_IF(this->signer); + DESTROY_IF(this->hasher); + DESTROY_IF(this->method); free(this); } @@ -237,6 +392,7 @@ METHOD(tnc_pdp_t, destroy, void, tnc_pdp_t *tnc_pdp_create(u_int16_t port) { private_tnc_pdp_t *this; + char *secret; INIT(this, .public = { @@ -244,6 +400,8 @@ tnc_pdp_t *tnc_pdp_create(u_int16_t port) }, .ipv4 = open_socket(this, AF_INET, port), .ipv6 = open_socket(this, AF_INET6, port), + .hasher = lib->crypto->create_hasher(lib->crypto, HASH_MD5), + .signer = lib->crypto->create_signer(lib->crypto, AUTH_HMAC_MD5_128), ); if (!this->ipv4 && !this->ipv6) @@ -260,6 +418,21 @@ tnc_pdp_t *tnc_pdp_create(u_int16_t port) { DBG1(DBG_NET, "could not open IPv6 RADIUS socket, IPv6 disabled"); } + if (!this->hasher || !this->signer) + { + destroy(this); + return NULL; + } + secret = lib->settings->get_str(lib->settings, + "charon.plugins.tnc-pdp.secret", NULL); + if (!secret) + { + DBG1(DBG_CFG, "missing RADIUS secret, PDP disabled"); + destroy(this); + return NULL; + } + this->secret = chunk_create(secret, strlen(secret)); + this->signer->set_key(this->signer, this->secret); this->job = callback_job_create_with_prio((callback_job_cb_t)receive, this, NULL, NULL, JOB_PRIO_CRITICAL); diff --git a/src/libradius/radius_message.c b/src/libradius/radius_message.c index bd3a32f07..a63374b5c 100644 --- a/src/libradius/radius_message.c +++ b/src/libradius/radius_message.c @@ -282,10 +282,14 @@ METHOD(radius_message_t, sign, void, private_radius_message_t *this, u_int8_t *req_auth, chunk_t secret, hasher_t *hasher, signer_t *signer, rng_t *rng) { - if (rng == NULL) + if (rng) { - chunk_t msg; - + /* build Request-Authenticator */ + rng->get_bytes(rng, HASH_SIZE_MD5, this->msg->authenticator); + } + else + { + /* build Response-Authenticator */ if (req_auth) { memcpy(this->msg->authenticator, req_auth, HASH_SIZE_MD5); @@ -294,17 +298,14 @@ METHOD(radius_message_t, sign, void, { memset(this->msg->authenticator, 0, sizeof(this->msg->authenticator)); } - msg = chunk_create((u_char*)this->msg, ntohs(this->msg->length)); - hasher->get_hash(hasher, msg, NULL); - hasher->get_hash(hasher, secret, this->msg->authenticator); } - else + + if (rng || this->msg->code == RMC_ACCESS_CHALLENGE + || this->msg->code == RMC_ACCESS_ACCEPT + || this->msg->code == RMC_ACCESS_REJECT) { char buf[HASH_SIZE_MD5]; - /* build Request-Authenticator */ - rng->get_bytes(rng, HASH_SIZE_MD5, this->msg->authenticator); - /* build Message-Authenticator attribute, using 16 null bytes */ memset(buf, 0, sizeof(buf)); add(this, RAT_MESSAGE_AUTHENTICATOR, chunk_create(buf, sizeof(buf))); @@ -312,6 +313,15 @@ METHOD(radius_message_t, sign, void, chunk_create((u_char*)this->msg, ntohs(this->msg->length)), ((u_char*)this->msg) + ntohs(this->msg->length) - HASH_SIZE_MD5); } + + if (!rng) + { + chunk_t msg; + + msg = chunk_create((u_char*)this->msg, ntohs(this->msg->length)); + hasher->get_hash(hasher, msg, NULL); + hasher->get_hash(hasher, secret, this->msg->authenticator); + } } METHOD(radius_message_t, verify, bool, @@ -324,25 +334,29 @@ METHOD(radius_message_t, verify, bool, chunk_t data, msg; bool has_eap = FALSE, has_auth = FALSE; - /* replace Response by Request Authenticator for verification */ - memcpy(res_auth, this->msg->authenticator, HASH_SIZE_MD5); - if (req_auth) - { - memcpy(this->msg->authenticator, req_auth, HASH_SIZE_MD5); - } - else - { - memset(this->msg->authenticator, 0, HASH_SIZE_MD5); - } msg = chunk_create((u_char*)this->msg, ntohs(this->msg->length)); - /* verify Response-Authenticator */ - hasher->get_hash(hasher, msg, NULL); - hasher->get_hash(hasher, secret, buf); - if (!memeq(buf, res_auth, HASH_SIZE_MD5)) + if (this->msg->code != RMC_ACCESS_REQUEST) { - DBG1(DBG_CFG, "RADIUS Response-Authenticator verification failed"); - return FALSE; + /* replace Response by Request Authenticator for verification */ + memcpy(res_auth, this->msg->authenticator, HASH_SIZE_MD5); + if (req_auth) + { + memcpy(this->msg->authenticator, req_auth, HASH_SIZE_MD5); + } + else + { + memset(this->msg->authenticator, 0, HASH_SIZE_MD5); + } + + /* verify Response-Authenticator */ + hasher->get_hash(hasher, msg, NULL); + hasher->get_hash(hasher, secret, buf); + if (!memeq(buf, res_auth, HASH_SIZE_MD5)) + { + DBG1(DBG_CFG, "RADIUS Response-Authenticator verification failed"); + return FALSE; + } } /* verify Message-Authenticator attribute */ @@ -380,8 +394,12 @@ METHOD(radius_message_t, verify, bool, } } enumerator->destroy(enumerator); - /* restore Response-Authenticator */ - memcpy(this->msg->authenticator, res_auth, HASH_SIZE_MD5); + + if (this->msg->code != RMC_ACCESS_REQUEST) + { + /* restore Response-Authenticator */ + memcpy(this->msg->authenticator, res_auth, HASH_SIZE_MD5); + } if (has_eap && !has_auth) { /* Message-Authenticator is required if we have an EAP-Message */ |