diff options
31 files changed, 1333 insertions, 114 deletions
diff --git a/src/frontends/android/jni/libandroidbridge/kernel/android_ipsec.c b/src/frontends/android/jni/libandroidbridge/kernel/android_ipsec.c index dc272e6ff..8e8534190 100644 --- a/src/frontends/android/jni/libandroidbridge/kernel/android_ipsec.c +++ b/src/frontends/android/jni/libandroidbridge/kernel/android_ipsec.c @@ -85,7 +85,8 @@ METHOD(kernel_ipsec_t, update_sa, status_t, METHOD(kernel_ipsec_t, query_sa, status_t, private_kernel_android_ipsec_t *this, host_t *src, host_t *dst, - u_int32_t spi, u_int8_t protocol, mark_t mark, u_int64_t *bytes) + u_int32_t spi, u_int8_t protocol, mark_t mark, + u_int64_t *bytes, u_int64_t *packets) { return NOT_SUPPORTED; } diff --git a/src/libcharon/bus/bus.h b/src/libcharon/bus/bus.h index 4645bbde6..18d57bce1 100644 --- a/src/libcharon/bus/bus.h +++ b/src/libcharon/bus/bus.h @@ -103,7 +103,7 @@ enum alert_t { ALERT_PARSE_ERROR_BODY, /** sending a retransmit for a message, argument is packet_t */ ALERT_RETRANSMIT_SEND, - /** sending retransmits timed out, argument is packet_t */ + /** sending retransmits timed out, argument is packet_t, if available */ ALERT_RETRANSMIT_SEND_TIMEOUT, /** received a retransmit for a message, argument is message_t */ ALERT_RETRANSMIT_RECEIVE, @@ -130,6 +130,8 @@ enum alert_t { ALERT_VIP_FAILURE, /** an authorize() hook failed, no argument */ ALERT_AUTHORIZATION_FAILED, + /** IKE_SA hit the hard lifetime limit before it could be rekeyed */ + ALERT_IKE_SA_EXPIRED, }; /** diff --git a/src/libcharon/plugins/eap_radius/Makefile.am b/src/libcharon/plugins/eap_radius/Makefile.am index 181497ab5..628adbeb3 100644 --- a/src/libcharon/plugins/eap_radius/Makefile.am +++ b/src/libcharon/plugins/eap_radius/Makefile.am @@ -15,6 +15,7 @@ libstrongswan_eap_radius_la_SOURCES = \ eap_radius_plugin.h eap_radius_plugin.c \ eap_radius.h eap_radius.c \ eap_radius_accounting.h eap_radius_accounting.c \ + eap_radius_provider.h eap_radius_provider.c \ eap_radius_dae.h eap_radius_dae.c \ eap_radius_forward.h eap_radius_forward.c diff --git a/src/libcharon/plugins/eap_radius/eap_radius.c b/src/libcharon/plugins/eap_radius/eap_radius.c index 6009d3a1f..59340df01 100644 --- a/src/libcharon/plugins/eap_radius/eap_radius.c +++ b/src/libcharon/plugins/eap_radius/eap_radius.c @@ -16,6 +16,8 @@ #include "eap_radius.h" #include "eap_radius_plugin.h" #include "eap_radius_forward.h" +#include "eap_radius_provider.h" +#include "eap_radius_accounting.h" #include <radius_message.h> #include <radius_client.h> @@ -155,17 +157,67 @@ static bool radius2ike(private_eap_radius_t *this, return FALSE; } +/** + * Add a set of RADIUS attributes to a request message + */ +static void add_radius_request_attrs(private_eap_radius_t *this, + radius_message_t *request) +{ + ike_sa_t *ike_sa; + host_t *host; + char buf[40]; + u_int32_t value; + chunk_t chunk; + + chunk = chunk_from_str(this->id_prefix); + chunk = chunk_cata("cc", chunk, this->peer->get_encoding(this->peer)); + request->add(request, RAT_USER_NAME, chunk); + + /* virtual NAS-Port-Type */ + value = htonl(5); + request->add(request, RAT_NAS_PORT_TYPE, chunk_from_thing(value)); + /* framed ServiceType */ + value = htonl(2); + request->add(request, RAT_SERVICE_TYPE, chunk_from_thing(value)); + + ike_sa = charon->bus->get_sa(charon->bus); + if (ike_sa) + { + value = htonl(ike_sa->get_unique_id(ike_sa)); + request->add(request, RAT_NAS_PORT, chunk_from_thing(value)); + request->add(request, RAT_NAS_PORT_ID, + chunk_from_str(ike_sa->get_name(ike_sa))); + + host = ike_sa->get_my_host(ike_sa); + chunk = host->get_address(host); + switch (host->get_family(host)) + { + case AF_INET: + request->add(request, RAT_NAS_IP_ADDRESS, chunk); + break; + case AF_INET6: + request->add(request, RAT_NAS_IPV6_ADDRESS, chunk); + default: + break; + } + snprintf(buf, sizeof(buf), "%#H", host); + request->add(request, RAT_CALLED_STATION_ID, chunk_from_str(buf)); + host = ike_sa->get_other_host(ike_sa); + snprintf(buf, sizeof(buf), "%#H", host); + request->add(request, RAT_CALLING_STATION_ID, chunk_from_str(buf)); + } + + eap_radius_forward_from_ike(request); +} + METHOD(eap_method_t, initiate, status_t, private_eap_radius_t *this, eap_payload_t **out) { radius_message_t *request, *response; status_t status = FAILED; - chunk_t username; request = radius_message_create(RMC_ACCESS_REQUEST); - username = chunk_create(this->id_prefix, strlen(this->id_prefix)); - username = chunk_cata("cc", username, this->peer->get_encoding(this->peer)); - request->add(request, RAT_USER_NAME, username); + add_radius_request_attrs(this, request); if (this->eap_start) { @@ -175,7 +227,6 @@ METHOD(eap_method_t, initiate, status_t, { add_eap_identity(this, request); } - eap_radius_forward_from_ike(request); response = this->client->request(this->client, request); if (response) @@ -203,7 +254,7 @@ METHOD(eap_method_t, initiate, status_t, } else { - charon->bus->alert(charon->bus, ALERT_RADIUS_NOT_RESPONDING); + eap_radius_handle_timeout(NULL); } request->destroy(request); return status; @@ -303,7 +354,7 @@ static void process_filter_id(private_eap_radius_t *this, radius_message_t *msg) } /** - * Handle Session-Timeout attribte + * Handle Session-Timeout attribte and Interim updates */ static void process_timeout(private_eap_radius_t *this, radius_message_t *msg) { @@ -312,19 +363,78 @@ static void process_timeout(private_eap_radius_t *this, radius_message_t *msg) chunk_t data; int type; - enumerator = msg->create_enumerator(msg); - while (enumerator->enumerate(enumerator, &type, &data)) + ike_sa = charon->bus->get_sa(charon->bus); + if (ike_sa) { - if (type == RAT_SESSION_TIMEOUT && data.len == 4) + enumerator = msg->create_enumerator(msg); + while (enumerator->enumerate(enumerator, &type, &data)) { - ike_sa = charon->bus->get_sa(charon->bus); - if (ike_sa) + if (type == RAT_SESSION_TIMEOUT && data.len == 4) { ike_sa->set_auth_lifetime(ike_sa, untoh32(data.ptr)); } + else if (type == RAT_ACCT_INTERIM_INTERVAL && data.len == 4) + { + eap_radius_accounting_start_interim(ike_sa, untoh32(data.ptr)); + } } + enumerator->destroy(enumerator); + } +} + +/** + * Handle Framed-IP-Address and other IKE configuration attributes + */ +static void process_cfg_attributes(private_eap_radius_t *this, + radius_message_t *msg) +{ + eap_radius_provider_t *provider; + enumerator_t *enumerator; + ike_sa_t *ike_sa; + host_t *host; + chunk_t data; + int type, vendor; + + ike_sa = charon->bus->get_sa(charon->bus); + provider = eap_radius_provider_get(); + if (provider && ike_sa) + { + enumerator = msg->create_enumerator(msg); + while (enumerator->enumerate(enumerator, &type, &data)) + { + if (type == RAT_FRAMED_IP_ADDRESS && data.len == 4) + { + host = host_create_from_chunk(AF_INET, data, 0); + if (host) + { + provider->add_framed_ip(provider, this->peer, host); + } + } + } + enumerator->destroy(enumerator); + + enumerator = msg->create_vendor_enumerator(msg); + while (enumerator->enumerate(enumerator, &vendor, &type, &data)) + { + if (vendor == PEN_ALTIGA /* aka Cisco VPN3000 */) + { + switch (type) + { + case 15: /* CVPN3000-IPSec-Banner1 */ + case 36: /* CVPN3000-IPSec-Banner2 */ + if (ike_sa->supports_extension(ike_sa, EXT_CISCO_UNITY)) + { + provider->add_attribute(provider, this->peer, + UNITY_BANNER, data); + } + break; + default: + break; + } + } + } + enumerator->destroy(enumerator); } - enumerator->destroy(enumerator); } METHOD(eap_method_t, process, status_t, @@ -335,7 +445,8 @@ METHOD(eap_method_t, process, status_t, chunk_t data; request = radius_message_create(RMC_ACCESS_REQUEST); - request->add(request, RAT_USER_NAME, this->peer->get_encoding(this->peer)); + add_radius_request_attrs(this, request); + data = in->get_data(in); DBG3(DBG_IKE, "%N payload %B", eap_type_names, this->type, &data); @@ -348,7 +459,6 @@ METHOD(eap_method_t, process, status_t, } request->add(request, RAT_EAP_MESSAGE, data); - eap_radius_forward_from_ike(request); response = this->client->request(this->client, request); if (response) { @@ -373,6 +483,7 @@ METHOD(eap_method_t, process, status_t, process_filter_id(this, response); } process_timeout(this, response); + process_cfg_attributes(this, response); DBG1(DBG_IKE, "RADIUS authentication of '%Y' successful", this->peer); status = SUCCESS; @@ -490,4 +601,3 @@ eap_radius_t *eap_radius_create(identification_t *server, identification_t *peer this->server = server->clone(server); return &this->public; } - diff --git a/src/libcharon/plugins/eap_radius/eap_radius_accounting.c b/src/libcharon/plugins/eap_radius/eap_radius_accounting.c index 3c72c122d..3620a4052 100644 --- a/src/libcharon/plugins/eap_radius/eap_radius_accounting.c +++ b/src/libcharon/plugins/eap_radius/eap_radius_accounting.c @@ -23,6 +23,7 @@ #include <daemon.h> #include <collections/hashtable.h> #include <threading/mutex.h> +#include <processing/jobs/callback_job.h> typedef struct private_eap_radius_accounting_t private_eap_radius_accounting_t; @@ -37,7 +38,7 @@ struct private_eap_radius_accounting_t { eap_radius_accounting_t public; /** - * Hashtable with sessions, IKE_SA unique id => entry_t + * Hashtable with sessions, ike_sa_id_t => entry_t */ hashtable_t *sessions; @@ -53,20 +54,70 @@ struct private_eap_radius_accounting_t { }; /** + * Singleton instance of accounting + */ +static private_eap_radius_accounting_t *singleton = NULL; + +/** + * Acct-Terminate-Cause + */ +typedef enum { + ACCT_CAUSE_USER_REQUEST = 1, + ACCT_CAUSE_LOST_CARRIER = 2, + ACCT_CAUSE_LOST_SERVICE = 3, + ACCT_CAUSE_IDLE_TIMEOUT = 4, + ACCT_CAUSE_SESSION_TIMEOUT = 5, + ACCT_CAUSE_ADMIN_RESET = 6, + ACCT_CAUSE_ADMIN_REBOOT = 7, + ACCT_CAUSE_PORT_ERROR = 8, + ACCT_CAUSE_NAS_ERROR = 9, + ACCT_CAUSE_NAS_REQUEST = 10, + ACCT_CAUSE_NAS_REBOOT = 11, + ACCT_CAUSE_PORT_UNNEEDED = 12, + ACCT_CAUSE_PORT_PREEMPTED = 13, + ACCT_CAUSE_PORT_SUSPENDED = 14, + ACCT_CAUSE_SERVICE_UNAVAILABLE = 15, + ACCT_CAUSE_CALLBACK = 16, + ACCT_CAUSE_USER_ERROR = 17, + ACCT_CAUSE_HOST_REQUEST = 18, +} radius_acct_terminate_cause_t; + +/** * Hashtable entry with usage stats */ typedef struct { + /** IKE_SA identifier this entry is stored under */ + ike_sa_id_t *id; /** RADIUS accounting session ID */ char sid[16]; - /** number of octets sent */ - u_int64_t sent; - /** number of octets received */ - u_int64_t received; + /** number of sent/received octets/packets */ + struct { + u_int64_t sent; + u_int64_t received; + } bytes, packets; /** session creation time */ time_t created; + /** terminate cause */ + radius_acct_terminate_cause_t cause; + /* interim interval and timestamp of last update */ + struct { + u_int32_t interval; + time_t last; + } interim; + /** did we send Accounting-Start */ + bool start_sent; } entry_t; /** + * Destroy an entry_t + */ +static void destroy_entry(entry_t *this) +{ + this->id->destroy(this->id); + free(this); +} + +/** * Accounting message status types */ typedef enum { @@ -80,17 +131,17 @@ typedef enum { /** * Hashtable hash function */ -static u_int hash(uintptr_t key) +static u_int hash(ike_sa_id_t *key) { - return key; + return key->get_responder_spi(key); } /** * Hashtable equals function */ -static bool equals(uintptr_t a, uintptr_t b) +static bool equals(ike_sa_id_t *a, ike_sa_id_t *b) { - return a == b; + return a->equals(a, b); } /** @@ -99,19 +150,20 @@ static bool equals(uintptr_t a, uintptr_t b) static void update_usage(private_eap_radius_accounting_t *this, ike_sa_t *ike_sa, child_sa_t *child_sa) { - u_int64_t sent, received; + u_int64_t bytes_in, bytes_out, packets_in, packets_out; entry_t *entry; - child_sa->get_usestats(child_sa, FALSE, NULL, &sent); - child_sa->get_usestats(child_sa, TRUE, NULL, &received); + child_sa->get_usestats(child_sa, FALSE, NULL, &bytes_out, &packets_out); + child_sa->get_usestats(child_sa, TRUE, NULL, &bytes_in, &packets_in); this->mutex->lock(this->mutex); - entry = this->sessions->get(this->sessions, - (void*)(uintptr_t)ike_sa->get_unique_id(ike_sa)); + entry = this->sessions->get(this->sessions, ike_sa->get_id(ike_sa)); if (entry) { - entry->sent += sent; - entry->received += received; + entry->bytes.sent += bytes_out; + entry->bytes.received += bytes_in; + entry->packets.sent += packets_out; + entry->packets.received += packets_in; } this->mutex->unlock(this->mutex); } @@ -135,10 +187,6 @@ static bool send_message(private_eap_radius_accounting_t *this, ack = response->get_code(response) == RMC_ACCOUNTING_RESPONSE; response->destroy(response); } - else - { - charon->bus->alert(charon->bus, ALERT_RADIUS_NOT_RESPONDING); - } client->destroy(client); } return ack; @@ -150,14 +198,43 @@ static bool send_message(private_eap_radius_accounting_t *this, static void add_ike_sa_parameters(radius_message_t *message, ike_sa_t *ike_sa) { enumerator_t *enumerator; - host_t *vip; + host_t *vip, *host; char buf[64]; chunk_t data; + u_int32_t value; + + /* virtual NAS-Port-Type */ + value = htonl(5); + message->add(message, RAT_NAS_PORT_TYPE, chunk_from_thing(value)); + /* framed ServiceType */ + value = htonl(2); + message->add(message, RAT_SERVICE_TYPE, chunk_from_thing(value)); + + value = htonl(ike_sa->get_unique_id(ike_sa)); + message->add(message, RAT_NAS_PORT, chunk_from_thing(value)); + message->add(message, RAT_NAS_PORT_ID, + chunk_from_str(ike_sa->get_name(ike_sa))); + + host = ike_sa->get_my_host(ike_sa); + data = host->get_address(host); + switch (host->get_family(host)) + { + case AF_INET: + message->add(message, RAT_NAS_IP_ADDRESS, data); + break; + case AF_INET6: + message->add(message, RAT_NAS_IPV6_ADDRESS, data); + default: + break; + } + snprintf(buf, sizeof(buf), "%#H", host); + message->add(message, RAT_CALLED_STATION_ID, chunk_from_str(buf)); + host = ike_sa->get_other_host(ike_sa); + snprintf(buf, sizeof(buf), "%#H", host); + message->add(message, RAT_CALLING_STATION_ID, chunk_from_str(buf)); snprintf(buf, sizeof(buf), "%Y", ike_sa->get_other_eap_id(ike_sa)); - message->add(message, RAT_USER_NAME, chunk_create(buf, strlen(buf))); - snprintf(buf, sizeof(buf), "%#H", ike_sa->get_other_host(ike_sa)); - message->add(message, RAT_CALLING_STATION_ID, chunk_create(buf, strlen(buf))); + message->add(message, RAT_USER_NAME, chunk_from_str(buf)); enumerator = ike_sa->create_virtual_ip_enumerator(ike_sa, FALSE); while (enumerator->enumerate(enumerator, &vip)) @@ -182,34 +259,207 @@ static void add_ike_sa_parameters(radius_message_t *message, ike_sa_t *ike_sa) } /** + * Get an existing or create a new entry from the locked session table + */ +static entry_t* get_or_create_entry(private_eap_radius_accounting_t *this, + ike_sa_t *ike_sa) +{ + ike_sa_id_t *id; + entry_t *entry; + time_t now; + + entry = this->sessions->get(this->sessions, ike_sa->get_id(ike_sa)); + if (!entry) + { + now = time_monotonic(NULL); + id = ike_sa->get_id(ike_sa); + + INIT(entry, + .id = id->clone(id), + .created = now, + .interim = { + .last = now, + }, + /* default terminate cause, if none other catched */ + .cause = ACCT_CAUSE_USER_REQUEST, + ); + snprintf(entry->sid, sizeof(entry->sid), "%u-%u", + this->prefix, ike_sa->get_unique_id(ike_sa)); + this->sessions->put(this->sessions, entry->id, entry); + } + return entry; +} + +/* forward declaration */ +static void schedule_interim(private_eap_radius_accounting_t *this, + entry_t *entry); + +/** + * Data passed to send_interim() using callback job + */ +typedef struct { + /** reference to radius accounting */ + private_eap_radius_accounting_t *this; + /** IKE_SA identifier to send interim update to */ + ike_sa_id_t *id; +} interim_data_t; + +/** + * Clean up interim data + */ +void destroy_interim_data(interim_data_t *this) +{ + this->id->destroy(this->id); + free(this); +} + +/** + * Send an interim update for entry of given IKE_SA identifier + */ +static job_requeue_t send_interim(interim_data_t *data) +{ + private_eap_radius_accounting_t *this = data->this; + u_int64_t bytes_in = 0, bytes_out = 0, packets_in = 0, packets_out = 0; + u_int64_t bytes, packets; + radius_message_t *message = NULL; + enumerator_t *enumerator; + child_sa_t *child_sa; + ike_sa_t *ike_sa; + entry_t *entry; + u_int32_t value; + + ike_sa = charon->ike_sa_manager->checkout(charon->ike_sa_manager, data->id); + if (!ike_sa) + { + return JOB_REQUEUE_NONE; + } + enumerator = ike_sa->create_child_sa_enumerator(ike_sa); + while (enumerator->enumerate(enumerator, &child_sa)) + { + child_sa->get_usestats(child_sa, FALSE, NULL, &bytes, &packets); + bytes_out += bytes; + packets_out += packets; + child_sa->get_usestats(child_sa, TRUE, NULL, &bytes, &packets); + bytes_in += bytes; + packets_in += packets; + } + enumerator->destroy(enumerator); + charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa); + + /* avoid any races by returning IKE_SA before acquiring lock */ + + this->mutex->lock(this->mutex); + entry = this->sessions->get(this->sessions, data->id); + if (entry) + { + entry->interim.last = time_monotonic(NULL); + + bytes_in += entry->bytes.received; + bytes_out += entry->bytes.sent; + packets_in += entry->packets.received; + packets_out += entry->packets.sent; + + message = radius_message_create(RMC_ACCOUNTING_REQUEST); + value = htonl(ACCT_STATUS_INTERIM_UPDATE); + message->add(message, RAT_ACCT_STATUS_TYPE, chunk_from_thing(value)); + message->add(message, RAT_ACCT_SESSION_ID, + chunk_create(entry->sid, strlen(entry->sid))); + add_ike_sa_parameters(message, ike_sa); + + value = htonl(bytes_out); + message->add(message, RAT_ACCT_OUTPUT_OCTETS, chunk_from_thing(value)); + value = htonl(bytes_out >> 32); + if (value) + { + message->add(message, RAT_ACCT_OUTPUT_GIGAWORDS, + chunk_from_thing(value)); + } + value = htonl(packets_out); + message->add(message, RAT_ACCT_OUTPUT_PACKETS, chunk_from_thing(value)); + + value = htonl(bytes_in); + message->add(message, RAT_ACCT_INPUT_OCTETS, chunk_from_thing(value)); + value = htonl(bytes_in >> 32); + if (value) + { + message->add(message, RAT_ACCT_INPUT_GIGAWORDS, + chunk_from_thing(value)); + } + value = htonl(packets_in); + message->add(message, RAT_ACCT_INPUT_PACKETS, chunk_from_thing(value)); + + value = htonl(entry->interim.last - entry->created); + message->add(message, RAT_ACCT_SESSION_TIME, chunk_from_thing(value)); + + schedule_interim(this, entry); + } + this->mutex->unlock(this->mutex); + + if (message) + { + if (!send_message(this, message)) + { + eap_radius_handle_timeout(data->id); + } + message->destroy(message); + } + return JOB_REQUEUE_NONE; +} + +/** + * Schedule interim update for given entry + */ +static void schedule_interim(private_eap_radius_accounting_t *this, + entry_t *entry) +{ + if (entry->interim.interval) + { + interim_data_t *data; + timeval_t tv = { + .tv_sec = entry->interim.last + entry->interim.interval, + }; + + INIT(data, + .this = this, + .id = entry->id->clone(entry->id), + ); + lib->scheduler->schedule_job_tv(lib->scheduler, + (job_t*)callback_job_create_with_prio( + (callback_job_cb_t)send_interim, + data, (void*)destroy_interim_data, + (callback_job_cancel_t)return_false, JOB_PRIO_CRITICAL), tv); + } +} + +/** * Send an accounting start message */ static void send_start(private_eap_radius_accounting_t *this, ike_sa_t *ike_sa) { radius_message_t *message; entry_t *entry; - u_int32_t id, value; + u_int32_t value; - id = ike_sa->get_unique_id(ike_sa); - INIT(entry, - .created = time_monotonic(NULL), - ); - snprintf(entry->sid, sizeof(entry->sid), "%u-%u", this->prefix, id); + this->mutex->lock(this->mutex); + + entry = get_or_create_entry(this, ike_sa); + entry->start_sent = TRUE; message = radius_message_create(RMC_ACCOUNTING_REQUEST); value = htonl(ACCT_STATUS_START); message->add(message, RAT_ACCT_STATUS_TYPE, chunk_from_thing(value)); message->add(message, RAT_ACCT_SESSION_ID, chunk_create(entry->sid, strlen(entry->sid))); + + schedule_interim(this, entry); + this->mutex->unlock(this->mutex); + add_ike_sa_parameters(message, ike_sa); - if (send_message(this, message)) + if (!send_message(this, message)) { - this->mutex->lock(this->mutex); - entry = this->sessions->put(this->sessions, (void*)(uintptr_t)id, entry); - this->mutex->unlock(this->mutex); + eap_radius_handle_timeout(ike_sa->get_id(ike_sa)); } message->destroy(message); - free(entry); } /** @@ -223,39 +473,86 @@ static void send_stop(private_eap_radius_accounting_t *this, ike_sa_t *ike_sa) id = ike_sa->get_unique_id(ike_sa); this->mutex->lock(this->mutex); - entry = this->sessions->remove(this->sessions, (void*)(uintptr_t)id); + entry = this->sessions->remove(this->sessions, ike_sa->get_id(ike_sa)); this->mutex->unlock(this->mutex); if (entry) { + if (!entry->start_sent) + { /* we tried to authenticate this peer, but never sent a start */ + destroy_entry(entry); + return; + } message = radius_message_create(RMC_ACCOUNTING_REQUEST); value = htonl(ACCT_STATUS_STOP); message->add(message, RAT_ACCT_STATUS_TYPE, chunk_from_thing(value)); message->add(message, RAT_ACCT_SESSION_ID, chunk_create(entry->sid, strlen(entry->sid))); add_ike_sa_parameters(message, ike_sa); - value = htonl(entry->sent); + + value = htonl(entry->bytes.sent); message->add(message, RAT_ACCT_OUTPUT_OCTETS, chunk_from_thing(value)); - value = htonl(entry->sent >> 32); + value = htonl(entry->bytes.sent >> 32); if (value) { message->add(message, RAT_ACCT_OUTPUT_GIGAWORDS, chunk_from_thing(value)); } - value = htonl(entry->received); + value = htonl(entry->packets.sent); + message->add(message, RAT_ACCT_OUTPUT_PACKETS, chunk_from_thing(value)); + + value = htonl(entry->bytes.received); message->add(message, RAT_ACCT_INPUT_OCTETS, chunk_from_thing(value)); - value = htonl(entry->received >> 32); + value = htonl(entry->bytes.received >> 32); if (value) { message->add(message, RAT_ACCT_INPUT_GIGAWORDS, chunk_from_thing(value)); } + value = htonl(entry->packets.received); + message->add(message, RAT_ACCT_INPUT_PACKETS, chunk_from_thing(value)); + value = htonl(time_monotonic(NULL) - entry->created); message->add(message, RAT_ACCT_SESSION_TIME, chunk_from_thing(value)); - send_message(this, message); + + value = htonl(entry->cause); + message->add(message, RAT_ACCT_TERMINATE_CAUSE, chunk_from_thing(value)); + + if (!send_message(this, message)) + { + eap_radius_handle_timeout(NULL); + } message->destroy(message); - free(entry); + destroy_entry(entry); + } +} + +METHOD(listener_t, alert, bool, + private_eap_radius_accounting_t *this, ike_sa_t *ike_sa, alert_t alert, + va_list args) +{ + radius_acct_terminate_cause_t cause; + entry_t *entry; + + switch (alert) + { + case ALERT_IKE_SA_EXPIRED: + cause = ACCT_CAUSE_SESSION_TIMEOUT; + break; + case ALERT_RETRANSMIT_SEND_TIMEOUT: + cause = ACCT_CAUSE_LOST_SERVICE; + break; + default: + return TRUE; } + this->mutex->lock(this->mutex); + entry = this->sessions->get(this->sessions, ike_sa->get_id(ike_sa)); + if (entry) + { + entry->cause = cause; + } + this->mutex->unlock(this->mutex); + return TRUE; } METHOD(listener_t, ike_updown, bool, @@ -307,15 +604,20 @@ METHOD(listener_t, ike_rekey, bool, entry_t *entry; this->mutex->lock(this->mutex); - entry = this->sessions->remove(this->sessions, - (void*)(uintptr_t)old->get_unique_id(old)); + entry = this->sessions->remove(this->sessions, old->get_id(old)); if (entry) { - entry = this->sessions->put(this->sessions, - (void*)(uintptr_t)new->get_unique_id(new), entry); + /* update IKE_SA identifier */ + entry->id->destroy(entry->id); + entry->id = new->get_id(new); + entry->id = entry->id->clone(entry->id); + /* fire new interim update job, old gets invalid */ + schedule_interim(this, entry); + + entry = this->sessions->put(this->sessions, entry->id, entry); if (entry) { - free(entry); + destroy_entry(entry); } } this->mutex->unlock(this->mutex); @@ -346,6 +648,8 @@ METHOD(listener_t, child_updown, bool, METHOD(eap_radius_accounting_t, destroy, void, private_eap_radius_accounting_t *this) { + charon->bus->remove_listener(charon->bus, &this->public.listener); + singleton = NULL; this->mutex->destroy(this->mutex); this->sessions->destroy(this->sessions); free(this); @@ -361,6 +665,7 @@ eap_radius_accounting_t *eap_radius_accounting_create() INIT(this, .public = { .listener = { + .alert = _alert, .ike_updown = _ike_updown, .ike_rekey = _ike_rekey, .message = _message_hook, @@ -376,5 +681,28 @@ eap_radius_accounting_t *eap_radius_accounting_create() .mutex = mutex_create(MUTEX_TYPE_DEFAULT), ); + if (lib->settings->get_bool(lib->settings, + "%s.plugins.eap-radius.accounting", FALSE, charon->name)) + { + singleton = this; + charon->bus->add_listener(charon->bus, &this->public.listener); + } return &this->public; } + +/** + * See header + */ +void eap_radius_accounting_start_interim(ike_sa_t *ike_sa, u_int32_t interval) +{ + if (singleton) + { + entry_t *entry; + + DBG1(DBG_CFG, "scheduling RADIUS Interim-Updates every %us", interval); + singleton->mutex->lock(singleton->mutex); + entry = get_or_create_entry(singleton, ike_sa); + entry->interim.interval = interval; + singleton->mutex->unlock(singleton->mutex); + } +} diff --git a/src/libcharon/plugins/eap_radius/eap_radius_accounting.h b/src/libcharon/plugins/eap_radius/eap_radius_accounting.h index 811a5bb90..3e59cecb6 100644 --- a/src/libcharon/plugins/eap_radius/eap_radius_accounting.h +++ b/src/libcharon/plugins/eap_radius/eap_radius_accounting.h @@ -46,4 +46,11 @@ struct eap_radius_accounting_t { */ eap_radius_accounting_t *eap_radius_accounting_create(); +/** + * Schedule Accounting interim updates for the given IKE_SA unique identifer. + * + * @param IKE_SA unique identifier + */ +void eap_radius_accounting_start_interim(ike_sa_t *ike_sa, u_int32_t interval); + #endif /** EAP_RADIUS_ACCOUNTING_H_ @}*/ diff --git a/src/libcharon/plugins/eap_radius/eap_radius_plugin.c b/src/libcharon/plugins/eap_radius/eap_radius_plugin.c index 9d4bbe1f3..3baf46731 100644 --- a/src/libcharon/plugins/eap_radius/eap_radius_plugin.c +++ b/src/libcharon/plugins/eap_radius/eap_radius_plugin.c @@ -19,12 +19,15 @@ #include "eap_radius_accounting.h" #include "eap_radius_dae.h" #include "eap_radius_forward.h" +#include "eap_radius_provider.h" #include <radius_client.h> #include <radius_config.h> -#include <daemon.h> +#include <hydra.h> #include <threading/rwlock.h> +#include <processing/jobs/callback_job.h> +#include <processing/jobs/delete_ike_sa_job.h> /** * Default RADIUS server port for authentication @@ -64,6 +67,11 @@ struct private_eap_radius_plugin_t { eap_radius_accounting_t *accounting; /** + * IKE attribute provider + */ + eap_radius_provider_t *provider; + + /** * Dynamic authorization extensions */ eap_radius_dae_t *dae; @@ -207,6 +215,9 @@ METHOD(plugin_t, reload, bool, METHOD(plugin_t, destroy, void, private_eap_radius_plugin_t *this) { + hydra->attributes->remove_provider(hydra->attributes, + &this->provider->provider); + this->provider->destroy(this->provider); if (this->forward) { charon->bus->remove_listener(charon->bus, &this->forward->listener); @@ -216,7 +227,6 @@ METHOD(plugin_t, destroy, void, this->configs->destroy_offset(this->configs, offsetof(radius_config_t, destroy)); this->lock->destroy(this->lock); - charon->bus->remove_listener(charon->bus, &this->accounting->listener); this->accounting->destroy(this->accounting); free(this); instance = NULL; @@ -242,17 +252,13 @@ plugin_t *eap_radius_plugin_create() .lock = rwlock_create(RWLOCK_TYPE_DEFAULT), .accounting = eap_radius_accounting_create(), .forward = eap_radius_forward_create(), + .provider = eap_radius_provider_create(), ); load_configs(this); instance = this; if (lib->settings->get_bool(lib->settings, - "%s.plugins.eap-radius.accounting", FALSE, charon->name)) - { - charon->bus->add_listener(charon->bus, &this->accounting->listener); - } - if (lib->settings->get_bool(lib->settings, "%s.plugins.eap-radius.dae.enable", FALSE, charon->name)) { this->dae = eap_radius_dae_create(this->accounting); @@ -261,6 +267,8 @@ plugin_t *eap_radius_plugin_create() { charon->bus->add_listener(charon->bus, &this->forward->listener); } + hydra->attributes->add_provider(hydra->attributes, + &this->provider->provider); return &this->public.plugin; } @@ -308,3 +316,47 @@ radius_client_t *eap_radius_create_client() return NULL; } +/** + * Job to delete all active IKE_SAs + */ +static job_requeue_t delete_all_async(void *data) +{ + enumerator_t *enumerator; + ike_sa_t *ike_sa; + + enumerator = charon->ike_sa_manager->create_enumerator( + charon->ike_sa_manager, TRUE); + while (enumerator->enumerate(enumerator, &ike_sa)) + { + lib->processor->queue_job(lib->processor, + (job_t*)delete_ike_sa_job_create(ike_sa->get_id(ike_sa), TRUE)); + } + enumerator->destroy(enumerator); + + return JOB_REQUEUE_NONE; +} + +/** + * See header. + */ +void eap_radius_handle_timeout(ike_sa_id_t *id) +{ + charon->bus->alert(charon->bus, ALERT_RADIUS_NOT_RESPONDING); + + if (lib->settings->get_bool(lib->settings, + "%s.plugins.eap-radius.close_all_on_timeout", + FALSE, charon->name)) + { + DBG1(DBG_CFG, "deleting all IKE_SAs after RADIUS timeout"); + lib->processor->queue_job(lib->processor, + (job_t*)callback_job_create_with_prio( + (callback_job_cb_t)delete_all_async, NULL, NULL, + (callback_job_cancel_t)return_false, JOB_PRIO_CRITICAL)); + } + else if (id) + { + DBG1(DBG_CFG, "deleting IKE_SA after RADIUS timeout"); + lib->processor->queue_job(lib->processor, + (job_t*)delete_ike_sa_job_create(id, TRUE)); + } +} diff --git a/src/libcharon/plugins/eap_radius/eap_radius_plugin.h b/src/libcharon/plugins/eap_radius/eap_radius_plugin.h index 1570bd566..80fa209d6 100644 --- a/src/libcharon/plugins/eap_radius/eap_radius_plugin.h +++ b/src/libcharon/plugins/eap_radius/eap_radius_plugin.h @@ -27,6 +27,7 @@ #include <plugins/plugin.h> #include <radius_client.h> +#include <daemon.h> typedef struct eap_radius_plugin_t eap_radius_plugin_t; @@ -51,4 +52,14 @@ struct eap_radius_plugin_t { */ radius_client_t *eap_radius_create_client(); +/** + * Handle a RADIUS request timeout. + * + * If an IKE_SA is given, it gets deleted (unless the policy says to delete + * any established IKE_SA). + * + * @param id associated IKE_SA where timeout happened, or NULL + */ +void eap_radius_handle_timeout(ike_sa_id_t *id); + #endif /** EAP_RADIUS_PLUGIN_H_ @}*/ diff --git a/src/libcharon/plugins/eap_radius/eap_radius_provider.c b/src/libcharon/plugins/eap_radius/eap_radius_provider.c new file mode 100644 index 000000000..83fa83830 --- /dev/null +++ b/src/libcharon/plugins/eap_radius/eap_radius_provider.c @@ -0,0 +1,486 @@ +/* + * Copyright (C) 2013 Martin Willi + * Copyright (C) 2013 revosec AG + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include "eap_radius_provider.h" + +#include <daemon.h> +#include <collections/hashtable.h> +#include <threading/mutex.h> + +typedef struct private_eap_radius_provider_t private_eap_radius_provider_t; +typedef struct private_listener_t private_listener_t; + +/** + * Private data of registered listener + */ +struct private_listener_t { + + /** + * Implements listener_t interface + */ + listener_t public; + + /** + * Leases not acquired yet, identification_t => entry_t + */ + hashtable_t *unclaimed; + + /** + * Leases acquired, identification_t => entry_t + */ + hashtable_t *claimed; + + /** + * Mutex to lock leases + */ + mutex_t *mutex; +}; + +/** + * Private data of an eap_radius_provider_t object. + */ +struct private_eap_radius_provider_t { + + /** + * Public eap_radius_provider_t interface. + */ + eap_radius_provider_t public; + + /** + * Additionally implements the listener_t interface + */ + private_listener_t listener; +}; + +/** + * Singleton instance of provider + */ +static eap_radius_provider_t *singleton = NULL; + +/** + * Configuration attribute in an entry + */ +typedef struct { + /** type of attribute */ + configuration_attribute_type_t type; + /** attribute data */ + chunk_t data; +} attr_t; + +/** + * Destroy an attr_t + */ +static void destroy_attr(attr_t *this) +{ + free(this->data.ptr); + free(this); +} + +/** + * Hashtable entry with leases and attributes + */ +typedef struct { + /** identity we assigned the IP lease */ + identification_t *id; + /** list of IP leases received from AAA, as host_t */ + linked_list_t *addrs; + /** list of configuration attributes, as attr_t */ + linked_list_t *attrs; +} entry_t; + +/** + * destroy an entry_t + */ +static void destroy_entry(entry_t *this) +{ + this->id->destroy(this->id); + this->addrs->destroy_offset(this->addrs, offsetof(host_t, destroy)); + this->attrs->destroy_function(this->attrs, (void*)destroy_attr); + free(this); +} + +/** + * Get or create an entry from a locked hashtable + */ +static entry_t* get_or_create_entry(hashtable_t *hashtable, identification_t *id) +{ + entry_t *entry; + + entry = hashtable->get(hashtable, id); + if (!entry) + { + INIT(entry, + .id = id->clone(id), + .addrs = linked_list_create(), + .attrs = linked_list_create(), + ); + hashtable->put(hashtable, entry->id, entry); + } + return entry; +} + +/** + * Put an entry to hashtable, or destroy it ife empty + */ +static void put_or_destroy_entry(hashtable_t *hashtable, entry_t *entry) +{ + if (entry->addrs->get_count(entry->addrs) > 0 || + entry->attrs->get_count(entry->attrs) > 0) + { + hashtable->put(hashtable, entry->id, entry); + } + else + { + destroy_entry(entry); + } +} + +/** + * Hashtable hash function + */ +static u_int hash(identification_t *id) +{ + return chunk_hash_inc(id->get_encoding(id), id->get_type(id)); +} + +/** + * Hashtable equals function + */ +static bool equals(identification_t *a, identification_t *b) +{ + return a->equals(a, b); +} + +/** + * Insert an address entry to a locked claimed/unclaimed hashtable + */ +static void add_addr(private_eap_radius_provider_t *this, + hashtable_t *hashtable, identification_t *id, host_t *host) +{ + entry_t *entry; + + entry = get_or_create_entry(hashtable, id); + entry->addrs->insert_last(entry->addrs, host); +} + +/** + * Remove the next address from the locked hashtable stored for given id + */ +static host_t* remove_addr(private_eap_radius_provider_t *this, + hashtable_t *hashtable, identification_t *id) +{ + entry_t *entry; + host_t *addr = NULL; + + entry = hashtable->remove(hashtable, id); + if (entry) + { + entry->addrs->remove_first(entry->addrs, (void**)&addr); + put_or_destroy_entry(hashtable, entry); + } + return addr; +} + +/** + * Insert an attribute entry to a locked claimed/unclaimed hashtable + */ +static void add_attr(private_eap_radius_provider_t *this, + hashtable_t *hashtable, identification_t *id, attr_t *attr) +{ + entry_t *entry; + + entry = get_or_create_entry(hashtable, id); + entry->attrs->insert_last(entry->attrs, attr); +} + +/** + * Remove the next attribute from the locked hashtable stored for given id + */ +static attr_t* remove_attr(private_eap_radius_provider_t *this, + hashtable_t *hashtable, identification_t *id) +{ + entry_t *entry; + attr_t *attr = NULL; + + entry = hashtable->remove(hashtable, id); + if (entry) + { + entry->attrs->remove_first(entry->attrs, (void**)&attr); + put_or_destroy_entry(hashtable, entry); + } + return attr; +} + +/** + * Clean up unclaimed leases assigned for an IKE_SA + */ +static void release_unclaimed(private_listener_t *this, ike_sa_t *ike_sa) +{ + identification_t *id; + entry_t *entry; + + id = ike_sa->get_other_eap_id(ike_sa); + this->mutex->lock(this->mutex); + entry = this->unclaimed->remove(this->unclaimed, id); + this->mutex->unlock(this->mutex); + if (entry) + { + destroy_entry(entry); + } +} + +METHOD(listener_t, message_hook, bool, + private_listener_t *this, ike_sa_t *ike_sa, + message_t *message, bool incoming, bool plain) +{ + if (plain && ike_sa->get_state(ike_sa) == IKE_ESTABLISHED && + !incoming && !message->get_request(message)) + { + if ((ike_sa->get_version(ike_sa) == IKEV1 && + message->get_exchange_type(message) == TRANSACTION) || + (ike_sa->get_version(ike_sa) == IKEV2 && + message->get_exchange_type(message) == IKE_AUTH)) + { + /* if the addresses have not been claimed yet, they won't. Release + * these ressources. */ + release_unclaimed(this, ike_sa); + } + } + return TRUE; +} + +METHOD(listener_t, ike_updown, bool, + private_listener_t *this, ike_sa_t *ike_sa, bool up) +{ + if (!up) + { + /* if the message hook does not apply because of a failed exchange + * or something, make sure we release any ressources now */ + release_unclaimed(this, ike_sa); + } + return TRUE; +} + +METHOD(attribute_provider_t, acquire_address, host_t*, + private_eap_radius_provider_t *this, linked_list_t *pools, + identification_t *id, host_t *requested) +{ + enumerator_t *enumerator; + host_t *addr = NULL; + char *name; + + enumerator = pools->create_enumerator(pools); + while (enumerator->enumerate(enumerator, &name)) + { + if (streq(name, "radius")) + { + this->listener.mutex->lock(this->listener.mutex); + addr = remove_addr(this, this->listener.unclaimed, id); + if (addr) + { + add_addr(this, this->listener.claimed, id, addr->clone(addr)); + } + this->listener.mutex->unlock(this->listener.mutex); + break; + } + } + enumerator->destroy(enumerator); + + return addr; +} + +METHOD(attribute_provider_t, release_address, bool, + private_eap_radius_provider_t *this, linked_list_t *pools, host_t *address, + identification_t *id) +{ + enumerator_t *enumerator; + host_t *found = NULL; + char *name; + + enumerator = pools->create_enumerator(pools); + while (enumerator->enumerate(enumerator, &name)) + { + if (streq(name, "radius")) + { + this->listener.mutex->lock(this->listener.mutex); + found = remove_addr(this, this->listener.claimed, id); + this->listener.mutex->unlock(this->listener.mutex); + break; + } + } + enumerator->destroy(enumerator); + + if (found) + { + found->destroy(found); + return TRUE; + } + return FALSE; +} + +/** + * Enumerator implementation over attributes + */ +typedef struct { + /** implements enumerator_t */ + enumerator_t public; + /** list of attributes to enumerate */ + linked_list_t *list; + /** currently enumerating attribute */ + attr_t *current; +} attribute_enumerator_t; + + +METHOD(enumerator_t, attribute_enumerate, bool, + attribute_enumerator_t *this, configuration_attribute_type_t *type, + chunk_t *data) +{ + if (this->current) + { + destroy_attr(this->current); + this->current = NULL; + } + if (this->list->remove_first(this->list, (void**)&this->current) == SUCCESS) + { + *type = this->current->type; + *data = this->current->data; + return TRUE; + } + return FALSE; +} + +METHOD(enumerator_t, attribute_destroy, void, + attribute_enumerator_t *this) +{ + if (this->current) + { + destroy_attr(this->current); + } + this->list->destroy_function(this->list, (void*)destroy_attr); + free(this); +} + +METHOD(attribute_provider_t, create_attribute_enumerator, enumerator_t*, + private_eap_radius_provider_t *this, linked_list_t *pools, + identification_t *id, linked_list_t *vips) +{ + attribute_enumerator_t *enumerator; + attr_t *attr; + + INIT(enumerator, + .public = { + .enumerate = (void*)_attribute_enumerate, + .destroy = _attribute_destroy, + }, + .list = linked_list_create(), + ); + + /* we forward attributes regardless of pool configurations */ + this->listener.mutex->lock(this->listener.mutex); + while (TRUE) + { + attr = remove_attr(this, this->listener.unclaimed, id); + if (!attr) + { + break; + } + enumerator->list->insert_last(enumerator->list, attr); + } + this->listener.mutex->unlock(this->listener.mutex); + + return &enumerator->public; +} + +METHOD(eap_radius_provider_t, add_framed_ip, void, + private_eap_radius_provider_t *this, identification_t *id, host_t *ip) +{ + this->listener.mutex->lock(this->listener.mutex); + add_addr(this, this->listener.unclaimed, id, ip); + this->listener.mutex->unlock(this->listener.mutex); +} + +METHOD(eap_radius_provider_t, add_attribute, void, + private_eap_radius_provider_t *this, identification_t *id, + configuration_attribute_type_t type, chunk_t data) +{ + attr_t *attr; + + INIT(attr, + .type = type, + .data = chunk_clone(data), + ); + this->listener.mutex->lock(this->listener.mutex); + add_attr(this, this->listener.unclaimed, id, attr); + this->listener.mutex->unlock(this->listener.mutex); +} + +METHOD(eap_radius_provider_t, destroy, void, + private_eap_radius_provider_t *this) +{ + singleton = NULL; + charon->bus->remove_listener(charon->bus, &this->listener.public); + this->listener.mutex->destroy(this->listener.mutex); + this->listener.claimed->destroy(this->listener.claimed); + this->listener.unclaimed->destroy(this->listener.unclaimed); + free(this); +} + +/** + * See header + */ +eap_radius_provider_t *eap_radius_provider_create() +{ + if (!singleton) + { + private_eap_radius_provider_t *this; + + INIT(this, + .public = { + .provider = { + .acquire_address = _acquire_address, + .release_address = _release_address, + .create_attribute_enumerator = _create_attribute_enumerator, + }, + .add_framed_ip = _add_framed_ip, + .add_attribute = _add_attribute, + .destroy = _destroy, + }, + .listener = { + .public = { + .ike_updown = _ike_updown, + .message = _message_hook, + }, + .claimed = hashtable_create((hashtable_hash_t)hash, + (hashtable_equals_t)equals, 32), + .unclaimed = hashtable_create((hashtable_hash_t)hash, + (hashtable_equals_t)equals, 32), + .mutex = mutex_create(MUTEX_TYPE_DEFAULT), + }, + ); + + charon->bus->add_listener(charon->bus, &this->listener.public); + + singleton = &this->public; + } + return singleton; +} + +/** + * See header + */ +eap_radius_provider_t *eap_radius_provider_get() +{ + return singleton; +} diff --git a/src/libcharon/plugins/eap_radius/eap_radius_provider.h b/src/libcharon/plugins/eap_radius/eap_radius_provider.h new file mode 100644 index 000000000..a0b4a6b62 --- /dev/null +++ b/src/libcharon/plugins/eap_radius/eap_radius_provider.h @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2013 Martin Willi + * Copyright (C) 2013 revosec AG + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +/** + * @defgroup eap_radius_provider eap_radius_provider + * @{ @ingroup eap_radius + */ + +#ifndef EAP_RADIUS_PROVIDER_H_ +#define EAP_RADIUS_PROVIDER_H_ + +#include <attributes/attributes.h> +#include <attributes/attribute_provider.h> + +typedef struct eap_radius_provider_t eap_radius_provider_t; + +/** + * IKE configuration attribute fed by RADIUS attributes + */ +struct eap_radius_provider_t { + + /** + * Implements attribute_provider_t + */ + attribute_provider_t provider; + + /** + * Add a received Framed-IP-Address to the provider to serve to client. + * + * @param id client identity + * @param ip IP address received from RADIUS server, gets owned + */ + void (*add_framed_ip)(eap_radius_provider_t *this, identification_t *id, + host_t *ip); + + /** + * Add a configuration attribute received from RADIUS to forward. + * + * @param id client identity + * @param type attribute type + * @param data attribute data + */ + void (*add_attribute)(eap_radius_provider_t *this, identification_t *id, + configuration_attribute_type_t type, chunk_t data); + + /** + * Destroy a eap_radius_provider_t. + */ + void (*destroy)(eap_radius_provider_t *this); +}; + +/** + * Create a eap_radius_provider instance. + */ +eap_radius_provider_t *eap_radius_provider_create(); + +/** + * Get singleton instance previously created with eap_radius_provider_create(). + */ +eap_radius_provider_t *eap_radius_provider_get(); + +#endif /** EAP_RADIUS_PROVIDER_H_ @}*/ diff --git a/src/libcharon/plugins/load_tester/load_tester_ipsec.c b/src/libcharon/plugins/load_tester/load_tester_ipsec.c index ded6b2d20..4f84845a3 100644 --- a/src/libcharon/plugins/load_tester/load_tester_ipsec.c +++ b/src/libcharon/plugins/load_tester/load_tester_ipsec.c @@ -70,7 +70,8 @@ METHOD(kernel_ipsec_t, update_sa, status_t, METHOD(kernel_ipsec_t, query_sa, status_t, private_load_tester_ipsec_t *this, host_t *src, host_t *dst, - u_int32_t spi, u_int8_t protocol, mark_t mark, u_int64_t *bytes) + u_int32_t spi, u_int8_t protocol, mark_t mark, + u_int64_t *bytes, u_int64_t *packets) { return NOT_SUPPORTED; } @@ -145,4 +146,3 @@ load_tester_ipsec_t *load_tester_ipsec_create() return &this->public; } - diff --git a/src/libcharon/plugins/stroke/stroke_list.c b/src/libcharon/plugins/stroke/stroke_list.c index b3a20a6c7..ab33933e2 100644 --- a/src/libcharon/plugins/stroke/stroke_list.c +++ b/src/libcharon/plugins/stroke/stroke_list.c @@ -205,7 +205,7 @@ static void log_ike_sa(FILE *out, ike_sa_t *ike_sa, bool all) static void log_child_sa(FILE *out, child_sa_t *child_sa, bool all) { time_t use_in, use_out, rekey, now; - u_int64_t bytes_in, bytes_out; + u_int64_t bytes_in, bytes_out, packets_in, packets_out; proposal_t *proposal; child_cfg_t *config = child_sa->get_config(child_sa); @@ -273,18 +273,22 @@ static void log_child_sa(FILE *out, child_sa_t *child_sa, bool all) } } - child_sa->get_usestats(child_sa, TRUE, &use_in, &bytes_in); + child_sa->get_usestats(child_sa, TRUE, + &use_in, &bytes_in, &packets_in); fprintf(out, ", %" PRIu64 " bytes_i", bytes_in); if (use_in) { - fprintf(out, " (%" PRIu64 "s ago)", (u_int64_t)(now - use_in)); + fprintf(out, " (%" PRIu64 " pkts, %" PRIu64 "s ago)", + packets_in, (u_int64_t)(now - use_in)); } - child_sa->get_usestats(child_sa, FALSE, &use_out, &bytes_out); + child_sa->get_usestats(child_sa, FALSE, + &use_out, &bytes_out, &packets_out); fprintf(out, ", %" PRIu64 " bytes_o", bytes_out); if (use_out) { - fprintf(out, " (%" PRIu64 "s ago)", (u_int64_t)(now - use_out)); + fprintf(out, " (%" PRIu64 " pkts, %" PRIu64 "s ago)", + packets_out, (u_int64_t)(now - use_out)); } fprintf(out, ", rekeying "); @@ -1541,4 +1545,3 @@ stroke_list_t *stroke_list_create(stroke_attribute_t *attribute) return &this->public; } - diff --git a/src/libcharon/processing/jobs/dpd_timeout_job.c b/src/libcharon/processing/jobs/dpd_timeout_job.c index 64a9785a6..9cdce5cab 100644 --- a/src/libcharon/processing/jobs/dpd_timeout_job.c +++ b/src/libcharon/processing/jobs/dpd_timeout_job.c @@ -68,7 +68,7 @@ METHOD(job_t, execute, job_requeue_t, enumerator = ike_sa->create_child_sa_enumerator(ike_sa); while (enumerator->enumerate(enumerator, &child_sa)) { - child_sa->get_usestats(child_sa, TRUE, ¤t, NULL); + child_sa->get_usestats(child_sa, TRUE, ¤t, NULL, NULL); use_time = max(use_time, current); } enumerator->destroy(enumerator); @@ -77,6 +77,7 @@ METHOD(job_t, execute, job_requeue_t, if (use_time < this->check) { DBG1(DBG_JOB, "DPD check timed out, enforcing DPD action"); + charon->bus->alert(charon->bus, ALERT_RETRANSMIT_SEND_TIMEOUT, NULL); charon->bus->ike_updown(charon->bus, ike_sa, FALSE); ike_sa->reestablish(ike_sa); charon->ike_sa_manager->checkin_and_destroy(charon->ike_sa_manager, diff --git a/src/libcharon/processing/jobs/inactivity_job.c b/src/libcharon/processing/jobs/inactivity_job.c index 3c56b0cd7..9ab69b417 100644 --- a/src/libcharon/processing/jobs/inactivity_job.c +++ b/src/libcharon/processing/jobs/inactivity_job.c @@ -75,8 +75,8 @@ METHOD(job_t, execute, job_requeue_t, { time_t in, out, diff; - child_sa->get_usestats(child_sa, TRUE, &in, NULL); - child_sa->get_usestats(child_sa, FALSE, &out, NULL); + child_sa->get_usestats(child_sa, TRUE, &in, NULL, NULL); + child_sa->get_usestats(child_sa, FALSE, &out, NULL, NULL); diff = time_monotonic(NULL) - max(in, out); @@ -155,4 +155,3 @@ inactivity_job_t *inactivity_job_create(u_int32_t reqid, u_int32_t timeout, return &this->public; } - diff --git a/src/libcharon/sa/child_sa.c b/src/libcharon/sa/child_sa.c index f02d836cf..463ad2e22 100644 --- a/src/libcharon/sa/child_sa.c +++ b/src/libcharon/sa/child_sa.c @@ -182,6 +182,16 @@ struct private_child_sa_t { * last number of outbound bytes */ u_int64_t other_usebytes; + + /** + * last number of inbound packets + */ + u_int64_t my_usepackets; + + /** + * last number of outbound bytes + */ + u_int64_t other_usepackets; }; /** @@ -413,7 +423,7 @@ METHOD(child_sa_t, create_policy_enumerator, enumerator_t*, static status_t update_usebytes(private_child_sa_t *this, bool inbound) { status_t status = FAILED; - u_int64_t bytes; + u_int64_t bytes, packets; if (inbound) { @@ -422,12 +432,13 @@ static status_t update_usebytes(private_child_sa_t *this, bool inbound) status = hydra->kernel_interface->query_sa(hydra->kernel_interface, this->other_addr, this->my_addr, this->my_spi, proto_ike2ip(this->protocol), this->mark_in, - &bytes); + &bytes, &packets); if (status == SUCCESS) { if (bytes > this->my_usebytes) { this->my_usebytes = bytes; + this->my_usepackets = packets; return SUCCESS; } return FAILED; @@ -441,12 +452,13 @@ static status_t update_usebytes(private_child_sa_t *this, bool inbound) status = hydra->kernel_interface->query_sa(hydra->kernel_interface, this->my_addr, this->other_addr, this->other_spi, proto_ike2ip(this->protocol), this->mark_out, - &bytes); + &bytes, &packets); if (status == SUCCESS) { if (bytes > this->other_usebytes) { this->other_usebytes = bytes; + this->other_usepackets = packets; return SUCCESS; } return FAILED; @@ -512,7 +524,8 @@ static void update_usetime(private_child_sa_t *this, bool inbound) } METHOD(child_sa_t, get_usestats, void, - private_child_sa_t *this, bool inbound, time_t *time, u_int64_t *bytes) + private_child_sa_t *this, bool inbound, + time_t *time, u_int64_t *bytes, u_int64_t *packets) { if (update_usebytes(this, inbound) != FAILED) { @@ -529,6 +542,10 @@ METHOD(child_sa_t, get_usestats, void, { *bytes = inbound ? this->my_usebytes : this->other_usebytes; } + if (packets) + { + *packets = inbound ? this->my_usepackets : this->other_usepackets; + } } METHOD(child_sa_t, get_mark, mark_t, diff --git a/src/libcharon/sa/child_sa.h b/src/libcharon/sa/child_sa.h index dae3f2c18..44511edf8 100644 --- a/src/libcharon/sa/child_sa.h +++ b/src/libcharon/sa/child_sa.h @@ -270,9 +270,10 @@ struct child_sa_t { * @param inbound TRUE for inbound traffic, FALSE for outbound * @param[out] time time of last use in seconds (NULL to ignore) * @param[out] bytes number of processed bytes (NULL to ignore) + * @param[out] packets number of processed packets (NULL to ignore) */ void (*get_usestats)(child_sa_t *this, bool inbound, time_t *time, - u_int64_t *bytes); + u_int64_t *bytes, u_int64_t *packets); /** * Get the mark used with this CHILD_SA. diff --git a/src/libcharon/sa/ike_sa.c b/src/libcharon/sa/ike_sa.c index 0157599c1..8c4dabd81 100644 --- a/src/libcharon/sa/ike_sa.c +++ b/src/libcharon/sa/ike_sa.c @@ -285,7 +285,7 @@ static time_t get_use_time(private_ike_sa_t* this, bool inbound) enumerator = this->child_sas->create_enumerator(this->child_sas); while (enumerator->enumerate(enumerator, &child_sa)) { - child_sa->get_usestats(child_sa, inbound, ¤t, NULL); + child_sa->get_usestats(child_sa, inbound, ¤t, NULL, NULL); use_time = max(use_time, current); } enumerator->destroy(enumerator); @@ -1445,6 +1445,10 @@ METHOD(ike_sa_t, delete_, status_t, } /* FALL */ case IKE_ESTABLISHED: + if (time_monotonic(NULL) >= this->stats[STAT_DELETE]) + { /* IKE_SA hard lifetime hit */ + charon->bus->alert(charon->bus, ALERT_IKE_SA_EXPIRED); + } this->task_manager->queue_ike_delete(this->task_manager); return this->task_manager->initiate(this->task_manager); case IKE_CREATED: diff --git a/src/libcharon/sa/ikev1/task_manager_v1.c b/src/libcharon/sa/ikev1/task_manager_v1.c index ac3b8d0dc..20629a05e 100644 --- a/src/libcharon/sa/ikev1/task_manager_v1.c +++ b/src/libcharon/sa/ikev1/task_manager_v1.c @@ -411,7 +411,7 @@ static bool send_fragment(private_task_manager_t *this, bool request, static bool send_packet(private_task_manager_t *this, bool request, packet_t *packet) { - fragmentation_t fragmentation = FRAGMENTATION_NO; + bool use_frags = FALSE; ike_cfg_t *ike_cfg; host_t *src, *dst; chunk_t data; @@ -419,12 +419,21 @@ static bool send_packet(private_task_manager_t *this, bool request, ike_cfg = this->ike_sa->get_ike_cfg(this->ike_sa); if (ike_cfg) { - fragmentation = ike_cfg->fragmentation(ike_cfg); + switch (ike_cfg->fragmentation(ike_cfg)) + { + case FRAGMENTATION_FORCE: + use_frags = TRUE; + break; + case FRAGMENTATION_YES: + use_frags = this->ike_sa->supports_extension(this->ike_sa, + EXT_IKE_FRAGMENTATION); + break; + default: + break; + } } data = packet->get_data(packet); - if (data.len > this->frag.size && (fragmentation == FRAGMENTATION_FORCE || - (this->ike_sa->supports_extension(this->ike_sa, EXT_IKE_FRAGMENTATION) && - fragmentation == FRAGMENTATION_YES))) + if (data.len > this->frag.size && use_frags) { fragment_payload_t *fragment; u_int8_t num, count; diff --git a/src/libcharon/sa/ikev1/tasks/quick_delete.c b/src/libcharon/sa/ikev1/tasks/quick_delete.c index db48bc58e..e9f06cbe3 100644 --- a/src/libcharon/sa/ikev1/tasks/quick_delete.c +++ b/src/libcharon/sa/ikev1/tasks/quick_delete.c @@ -97,8 +97,8 @@ static bool delete_child(private_quick_delete_t *this, } else { - child_sa->get_usestats(child_sa, TRUE, NULL, &bytes_in); - child_sa->get_usestats(child_sa, FALSE, NULL, &bytes_out); + child_sa->get_usestats(child_sa, TRUE, NULL, &bytes_in, NULL); + child_sa->get_usestats(child_sa, FALSE, NULL, &bytes_out, NULL); DBG0(DBG_IKE, "closing CHILD_SA %s{%d} with SPIs " "%.8x_i (%llu bytes) %.8x_o (%llu bytes) and TS %#R=== %#R", diff --git a/src/libcharon/sa/ikev2/tasks/child_delete.c b/src/libcharon/sa/ikev2/tasks/child_delete.c index 644af782c..8652942ad 100644 --- a/src/libcharon/sa/ikev2/tasks/child_delete.c +++ b/src/libcharon/sa/ikev2/tasks/child_delete.c @@ -264,8 +264,8 @@ static void log_children(private_child_delete_t *this) } else { - child_sa->get_usestats(child_sa, TRUE, NULL, &bytes_in); - child_sa->get_usestats(child_sa, FALSE, NULL, &bytes_out); + child_sa->get_usestats(child_sa, TRUE, NULL, &bytes_in, NULL); + child_sa->get_usestats(child_sa, FALSE, NULL, &bytes_out, NULL); DBG0(DBG_IKE, "closing CHILD_SA %s{%d} with SPIs %.8x_i " "(%llu bytes) %.8x_o (%llu bytes) and TS %#R=== %#R", diff --git a/src/libhydra/kernel/kernel_interface.c b/src/libhydra/kernel/kernel_interface.c index b82fd6d3d..53b8324b7 100644 --- a/src/libhydra/kernel/kernel_interface.c +++ b/src/libhydra/kernel/kernel_interface.c @@ -207,13 +207,15 @@ METHOD(kernel_interface_t, update_sa, status_t, METHOD(kernel_interface_t, query_sa, status_t, private_kernel_interface_t *this, host_t *src, host_t *dst, - u_int32_t spi, u_int8_t protocol, mark_t mark, u_int64_t *bytes) + u_int32_t spi, u_int8_t protocol, mark_t mark, + u_int64_t *bytes, u_int64_t *packets) { if (!this->ipsec) { return NOT_SUPPORTED; } - return this->ipsec->query_sa(this->ipsec, src, dst, spi, protocol, mark, bytes); + return this->ipsec->query_sa(this->ipsec, src, dst, spi, protocol, mark, + bytes, packets); } METHOD(kernel_interface_t, del_sa, status_t, diff --git a/src/libhydra/kernel/kernel_interface.h b/src/libhydra/kernel/kernel_interface.h index 8d8d048d0..1d2253b94 100644 --- a/src/libhydra/kernel/kernel_interface.h +++ b/src/libhydra/kernel/kernel_interface.h @@ -194,11 +194,12 @@ struct kernel_interface_t { * @param protocol protocol for this SA (ESP/AH) * @param mark optional mark for this SA * @param[out] bytes the number of bytes processed by SA + * @param[out] packets number of packets processed by SA * @return SUCCESS if operation completed */ status_t (*query_sa) (kernel_interface_t *this, host_t *src, host_t *dst, u_int32_t spi, u_int8_t protocol, mark_t mark, - u_int64_t *bytes); + u_int64_t *bytes, u_int64_t *packets); /** * Delete a previously installed SA from the SAD. diff --git a/src/libhydra/kernel/kernel_ipsec.h b/src/libhydra/kernel/kernel_ipsec.h index c8afcaffd..ba67238e5 100644 --- a/src/libhydra/kernel/kernel_ipsec.h +++ b/src/libhydra/kernel/kernel_ipsec.h @@ -154,11 +154,12 @@ struct kernel_ipsec_t { * @param protocol protocol for this SA (ESP/AH) * @param mark optional mark for this SA * @param[out] bytes the number of bytes processed by SA + * @param[out] packets number of packets processed by SA * @return SUCCESS if operation completed */ status_t (*query_sa) (kernel_ipsec_t *this, host_t *src, host_t *dst, u_int32_t spi, u_int8_t protocol, mark_t mark, - u_int64_t *bytes); + u_int64_t *bytes, u_int64_t *packets); /** * Delete a previusly installed SA from the SAD. diff --git a/src/libhydra/plugins/kernel_klips/kernel_klips_ipsec.c b/src/libhydra/plugins/kernel_klips/kernel_klips_ipsec.c index 431174e72..a120b3d00 100644 --- a/src/libhydra/plugins/kernel_klips/kernel_klips_ipsec.c +++ b/src/libhydra/plugins/kernel_klips/kernel_klips_ipsec.c @@ -1910,7 +1910,8 @@ METHOD(kernel_ipsec_t, update_sa, status_t, METHOD(kernel_ipsec_t, query_sa, status_t, private_kernel_klips_ipsec_t *this, host_t *src, host_t *dst, - u_int32_t spi, u_int8_t protocol, mark_t mark, u_int64_t *bytes) + u_int32_t spi, u_int8_t protocol, mark_t mark, + u_int64_t *bytes, u_int64_t *packets) { return NOT_SUPPORTED; /* TODO */ } @@ -2648,4 +2649,3 @@ kernel_klips_ipsec_t *kernel_klips_ipsec_create() return &this->public; } - diff --git a/src/libhydra/plugins/kernel_netlink/kernel_netlink_ipsec.c b/src/libhydra/plugins/kernel_netlink/kernel_netlink_ipsec.c index 3e84d1699..9b4ade533 100644 --- a/src/libhydra/plugins/kernel_netlink/kernel_netlink_ipsec.c +++ b/src/libhydra/plugins/kernel_netlink/kernel_netlink_ipsec.c @@ -1594,7 +1594,8 @@ static void get_replay_state(private_kernel_netlink_ipsec_t *this, METHOD(kernel_ipsec_t, query_sa, status_t, private_kernel_netlink_ipsec_t *this, host_t *src, host_t *dst, - u_int32_t spi, u_int8_t protocol, mark_t mark, u_int64_t *bytes) + u_int32_t spi, u_int8_t protocol, mark_t mark, + u_int64_t *bytes, u_int64_t *packets) { netlink_buf_t request; struct nlmsghdr *out = NULL, *hdr; @@ -1671,7 +1672,14 @@ METHOD(kernel_ipsec_t, query_sa, status_t, } else { - *bytes = sa->curlft.bytes; + if (bytes) + { + *bytes = sa->curlft.bytes; + } + if (packets) + { + *packets = sa->curlft.packets; + } status = SUCCESS; } memwipe(out, len); diff --git a/src/libhydra/plugins/kernel_pfkey/kernel_pfkey_ipsec.c b/src/libhydra/plugins/kernel_pfkey/kernel_pfkey_ipsec.c index 88b028447..2521af87d 100644 --- a/src/libhydra/plugins/kernel_pfkey/kernel_pfkey_ipsec.c +++ b/src/libhydra/plugins/kernel_pfkey/kernel_pfkey_ipsec.c @@ -810,7 +810,7 @@ static kernel_algorithm_t compression_algs[] = { static int lookup_algorithm(transform_type_t type, int ikev2) { kernel_algorithm_t *list; - int alg = 0; + u_int16_t alg = 0; switch (type) { @@ -1767,7 +1767,8 @@ METHOD(kernel_ipsec_t, update_sa, status_t, METHOD(kernel_ipsec_t, query_sa, status_t, private_kernel_pfkey_ipsec_t *this, host_t *src, host_t *dst, - u_int32_t spi, u_int8_t protocol, mark_t mark, u_int64_t *bytes) + u_int32_t spi, u_int8_t protocol, mark_t mark, + u_int64_t *bytes, u_int64_t *packets) { unsigned char request[PFKEY_BUFFER_SIZE]; struct sadb_msg *msg, *out; @@ -1816,7 +1817,15 @@ METHOD(kernel_ipsec_t, query_sa, status_t, free(out); return FAILED; } - *bytes = response.lft_current->sadb_lifetime_bytes; + if (bytes) + { + *bytes = response.lft_current->sadb_lifetime_bytes; + } + if (packets) + { + /* not supported by PF_KEY */ + *packets = 0; + } free(out); return SUCCESS; diff --git a/src/libradius/radius_client.c b/src/libradius/radius_client.c index 1478c3d9e..d44c5a2e3 100644 --- a/src/libradius/radius_client.c +++ b/src/libradius/radius_client.c @@ -81,13 +81,10 @@ static void save_state(private_radius_client_t *this, radius_message_t *msg) METHOD(radius_client_t, request, radius_message_t*, private_radius_client_t *this, radius_message_t *req) { - char virtual[] = {0x00,0x00,0x00,0x05}; radius_socket_t *socket; radius_message_t *res; chunk_t data; - /* we add the "Virtual" NAS-Port-Type, as we SHOULD include one */ - req->add(req, RAT_NAS_PORT_TYPE, chunk_create(virtual, sizeof(virtual))); /* add our NAS-Identifier */ req->add(req, RAT_NAS_IDENTIFIER, this->config->get_nas_identifier(this->config)); diff --git a/src/libradius/radius_message.c b/src/libradius/radius_message.c index 059dcda4b..e7717ff7a 100644 --- a/src/libradius/radius_message.c +++ b/src/libradius/radius_message.c @@ -16,6 +16,7 @@ #include "radius_message.h" #include <utils/debug.h> +#include <bio/bio_reader.h> #include <crypto/hashers/hasher.h> typedef struct private_radius_message_t private_radius_message_t; @@ -271,6 +272,85 @@ METHOD(radius_message_t, create_enumerator, enumerator_t*, return &e->public; } +/** + * Vendor attribute enumerator implementation + */ +typedef struct { + /** implements enumerator interface */ + enumerator_t public; + /** inner attribute enumerator */ + enumerator_t *inner; + /** current vendor ID */ + u_int32_t vendor; + /** reader for current vendor ID */ + bio_reader_t *reader; +} vendor_enumerator_t; + +METHOD(enumerator_t, vendor_enumerate, bool, + vendor_enumerator_t *this, int *vendor, int *type, chunk_t *data) +{ + chunk_t inner_data; + int inner_type; + u_int8_t type8, len; + + while (TRUE) + { + if (this->reader) + { + if (this->reader->remaining(this->reader) >= 2 && + this->reader->read_uint8(this->reader, &type8) && + this->reader->read_uint8(this->reader, &len) && len >= 2 && + this->reader->read_data(this->reader, len - 2, data)) + { + *vendor = this->vendor; + *type = type8; + return TRUE; + } + this->reader->destroy(this->reader); + this->reader = NULL; + } + if (this->inner->enumerate(this->inner, &inner_type, &inner_data)) + { + if (inner_type == RAT_VENDOR_SPECIFIC) + { + this->reader = bio_reader_create(inner_data); + if (!this->reader->read_uint32(this->reader, &this->vendor)) + { + this->reader->destroy(this->reader); + this->reader = NULL; + } + } + } + else + { + return FALSE; + } + } +} +METHOD(enumerator_t, vendor_destroy, void, + vendor_enumerator_t *this) +{ + DESTROY_IF(this->reader); + this->inner->destroy(this->inner); + free(this); +} + +METHOD(radius_message_t, create_vendor_enumerator, enumerator_t*, + private_radius_message_t *this) +{ + vendor_enumerator_t *e; + + INIT(e, + .public = { + .enumerate = (void*)_vendor_enumerate, + .destroy = _vendor_destroy, + }, + .inner = create_enumerator(this), + ); + + return &e->public; +} + METHOD(radius_message_t, add, void, private_radius_message_t *this, radius_attribute_type_t type, chunk_t data) { @@ -474,6 +554,7 @@ static private_radius_message_t *radius_message_create_empty() INIT(this, .public = { .create_enumerator = _create_enumerator, + .create_vendor_enumerator = _create_vendor_enumerator, .add = _add, .get_code = _get_code, .get_identifier = _get_identifier, diff --git a/src/libradius/radius_message.h b/src/libradius/radius_message.h index 18ec581e5..c49323490 100644 --- a/src/libradius/radius_message.h +++ b/src/libradius/radius_message.h @@ -27,6 +27,7 @@ #define RADIUS_MESSAGE_H_ #include <library.h> +#include <pen/pen.h> #define MAX_RADIUS_ATTRIBUTE_SIZE 253 @@ -205,6 +206,16 @@ struct radius_message_t { enumerator_t* (*create_enumerator)(radius_message_t *this); /** + * Create an enumerator over contained RADIUS Vendor-ID attributes. + * + * This enumerator parses only vendor specific attributes in the format + * recommended in RFC2865. + * + * @return enumerator over (int vendor, int type, chunk_t data) + */ + enumerator_t* (*create_vendor_enumerator)(radius_message_t *this); + + /** * Add a RADIUS attribute to the message. * * @param type type of attribute to add diff --git a/src/libstrongswan/pen/pen.c b/src/libstrongswan/pen/pen.c index f6997d1d1..474a7a876 100644 --- a/src/libstrongswan/pen/pen.c +++ b/src/libstrongswan/pen/pen.c @@ -23,7 +23,9 @@ ENUM_NEXT(pen_names, PEN_MICROSOFT, PEN_MICROSOFT, PEN_IBM, "Microsoft"); ENUM_NEXT(pen_names, PEN_REDHAT, PEN_REDHAT, PEN_MICROSOFT, "Redhat"); -ENUM_NEXT(pen_names, PEN_OSC, PEN_OSC, PEN_REDHAT, +ENUM_NEXT(pen_names, PEN_ALTIGA, PEN_ALTIGA, PEN_REDHAT, + "Altiga"); +ENUM_NEXT(pen_names, PEN_OSC, PEN_OSC, PEN_ALTIGA, "OSC"); ENUM_NEXT(pen_names, PEN_DEBIAN, PEN_DEBIAN, PEN_OSC, "Debian Project"); diff --git a/src/libstrongswan/pen/pen.h b/src/libstrongswan/pen/pen.h index 29e312c61..1760a0578 100644 --- a/src/libstrongswan/pen/pen.h +++ b/src/libstrongswan/pen/pen.h @@ -39,6 +39,7 @@ enum pen_t { PEN_IBM = 0x000002, /* 2 */ PEN_MICROSOFT = 0x000137, /* 311 */ PEN_REDHAT = 0x000908, /* 2312 */ + PEN_ALTIGA = 0x000c04, /* 3076 */ PEN_OSC = 0x002358, /* 9048 */ PEN_DEBIAN = 0x002572, /* 9586 */ PEN_GOOGLE = 0x002B79, /* 11129 */ |