eap-radius: Add support for some basic IPv6-specific RADIUS attributes
[strongswan.git] / src / libcharon / plugins / eap_radius / eap_radius.c
1 /*
2 * Copyright (C) 2009 Martin Willi
3 * Hochschule fuer Technik Rapperswil
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the
7 * Free Software Foundation; either version 2 of the License, or (at your
8 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
9 *
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13 * for more details.
14 */
15
16 #include "eap_radius.h"
17 #include "eap_radius_plugin.h"
18 #include "eap_radius_forward.h"
19 #include "eap_radius_provider.h"
20 #include "eap_radius_accounting.h"
21
22 #include <radius_message.h>
23 #include <radius_client.h>
24 #include <bio/bio_writer.h>
25
26 #include <daemon.h>
27
28 typedef struct private_eap_radius_t private_eap_radius_t;
29
30 /**
31 * Private data of an eap_radius_t object.
32 */
33 struct private_eap_radius_t {
34
35 /**
36 * Public authenticator_t interface.
37 */
38 eap_radius_t public;
39
40 /**
41 * ID of the server
42 */
43 identification_t *server;
44
45 /**
46 * ID of the peer
47 */
48 identification_t *peer;
49
50 /**
51 * EAP method type we are proxying
52 */
53 eap_type_t type;
54
55 /**
56 * EAP vendor, if any
57 */
58 u_int32_t vendor;
59
60 /**
61 * EAP message identifier
62 */
63 u_int8_t identifier;
64
65 /**
66 * RADIUS client instance
67 */
68 radius_client_t *client;
69
70 /**
71 * TRUE to use EAP-Start, FALSE to send EAP-Identity Response directly
72 */
73 bool eap_start;
74
75 /**
76 * Prefix to prepend to EAP identity
77 */
78 char *id_prefix;
79 };
80
81 /**
82 * Add EAP-Identity to RADIUS message
83 */
84 static void add_eap_identity(private_eap_radius_t *this,
85 radius_message_t *request)
86 {
87 struct {
88 /** EAP code (REQUEST/RESPONSE) */
89 u_int8_t code;
90 /** unique message identifier */
91 u_int8_t identifier;
92 /** length of whole message */
93 u_int16_t length;
94 /** EAP type */
95 u_int8_t type;
96 /** identity data */
97 u_int8_t data[];
98 } __attribute__((__packed__)) *hdr;
99 chunk_t id, prefix;
100 size_t len;
101
102 id = this->peer->get_encoding(this->peer);
103 prefix = chunk_create(this->id_prefix, strlen(this->id_prefix));
104 len = sizeof(*hdr) + prefix.len + id.len;
105
106 hdr = alloca(len);
107 hdr->code = EAP_RESPONSE;
108 hdr->identifier = this->identifier;
109 hdr->length = htons(len);
110 hdr->type = EAP_IDENTITY;
111 memcpy(hdr->data, prefix.ptr, prefix.len);
112 memcpy(hdr->data + prefix.len, id.ptr, id.len);
113
114 request->add(request, RAT_EAP_MESSAGE, chunk_create((u_char*)hdr, len));
115 }
116
117 /**
118 * Copy EAP-Message attribute from RADIUS message to an new EAP payload
119 */
120 static bool radius2ike(private_eap_radius_t *this,
121 radius_message_t *msg, eap_payload_t **out)
122 {
123 enumerator_t *enumerator;
124 eap_payload_t *payload;
125 chunk_t data, message = chunk_empty;
126 int type;
127
128 enumerator = msg->create_enumerator(msg);
129 while (enumerator->enumerate(enumerator, &type, &data))
130 {
131 if (type == RAT_EAP_MESSAGE && data.len)
132 {
133 message = chunk_cat("mc", message, data);
134 }
135 }
136 enumerator->destroy(enumerator);
137 if (message.len)
138 {
139 *out = payload = eap_payload_create_data(message);
140
141 /* apply EAP method selected by RADIUS server */
142 this->type = payload->get_type(payload, &this->vendor);
143
144 DBG3(DBG_IKE, "%N payload %B", eap_type_names, this->type, &message);
145 free(message.ptr);
146 return TRUE;
147 }
148 return FALSE;
149 }
150
151 /**
152 * See header.
153 */
154 void eap_radius_build_attributes(radius_message_t *request)
155 {
156 ike_sa_t *ike_sa;
157 host_t *host;
158 char buf[40], *station_id_fmt;;
159 u_int32_t value;
160 chunk_t chunk;
161
162 /* virtual NAS-Port-Type */
163 value = htonl(5);
164 request->add(request, RAT_NAS_PORT_TYPE, chunk_from_thing(value));
165 /* framed ServiceType */
166 value = htonl(2);
167 request->add(request, RAT_SERVICE_TYPE, chunk_from_thing(value));
168
169 ike_sa = charon->bus->get_sa(charon->bus);
170 if (ike_sa)
171 {
172 value = htonl(ike_sa->get_unique_id(ike_sa));
173 request->add(request, RAT_NAS_PORT, chunk_from_thing(value));
174 request->add(request, RAT_NAS_PORT_ID,
175 chunk_from_str(ike_sa->get_name(ike_sa)));
176
177 host = ike_sa->get_my_host(ike_sa);
178 chunk = host->get_address(host);
179 switch (host->get_family(host))
180 {
181 case AF_INET:
182 request->add(request, RAT_NAS_IP_ADDRESS, chunk);
183 break;
184 case AF_INET6:
185 request->add(request, RAT_NAS_IPV6_ADDRESS, chunk);
186 default:
187 break;
188 }
189 if (lib->settings->get_bool(lib->settings,
190 "%s.plugins.eap-radius.station_id_with_port",
191 TRUE, lib->ns))
192 {
193 station_id_fmt = "%#H";
194 }
195 else
196 {
197 station_id_fmt = "%H";
198 }
199 snprintf(buf, sizeof(buf), station_id_fmt, host);
200 request->add(request, RAT_CALLED_STATION_ID, chunk_from_str(buf));
201 host = ike_sa->get_other_host(ike_sa);
202 snprintf(buf, sizeof(buf), station_id_fmt, host);
203 request->add(request, RAT_CALLING_STATION_ID, chunk_from_str(buf));
204 }
205 }
206
207 /**
208 * Add a set of RADIUS attributes to a request message
209 */
210 static void add_radius_request_attrs(private_eap_radius_t *this,
211 radius_message_t *request)
212 {
213 chunk_t chunk;
214
215 chunk = chunk_from_str(this->id_prefix);
216 chunk = chunk_cata("cc", chunk, this->peer->get_encoding(this->peer));
217 request->add(request, RAT_USER_NAME, chunk);
218
219 eap_radius_build_attributes(request);
220 eap_radius_forward_from_ike(request);
221 }
222
223 METHOD(eap_method_t, initiate, status_t,
224 private_eap_radius_t *this, eap_payload_t **out)
225 {
226 radius_message_t *request, *response;
227 status_t status = FAILED;
228
229 request = radius_message_create(RMC_ACCESS_REQUEST);
230 add_radius_request_attrs(this, request);
231
232 if (this->eap_start)
233 {
234 request->add(request, RAT_EAP_MESSAGE, chunk_empty);
235 }
236 else
237 {
238 add_eap_identity(this, request);
239 }
240
241 response = this->client->request(this->client, request);
242 if (response)
243 {
244 eap_radius_forward_to_ike(response);
245 switch (response->get_code(response))
246 {
247 case RMC_ACCESS_CHALLENGE:
248 if (radius2ike(this, response, out))
249 {
250 status = NEED_MORE;
251 }
252 break;
253 case RMC_ACCESS_ACCEPT:
254 /* Microsoft RADIUS servers can run in a mode where they respond
255 * like this on the first request (i.e. without authentication),
256 * we treat this as Access-Reject */
257 case RMC_ACCESS_REJECT:
258 default:
259 DBG1(DBG_IKE, "RADIUS authentication of '%Y' failed",
260 this->peer);
261 break;
262 }
263 response->destroy(response);
264 }
265 else
266 {
267 eap_radius_handle_timeout(NULL);
268 }
269 request->destroy(request);
270 return status;
271 }
272
273 /**
274 * Handle the Class attribute as group membership information
275 */
276 static void process_class(radius_message_t *msg)
277 {
278 enumerator_t *enumerator;
279 chunk_t data;
280 int type;
281
282 enumerator = msg->create_enumerator(msg);
283 while (enumerator->enumerate(enumerator, &type, &data))
284 {
285 if (type == RAT_CLASS)
286 {
287 identification_t *id;
288 ike_sa_t *ike_sa;
289 auth_cfg_t *auth;
290
291 if (data.len >= 44)
292 { /* quirk: ignore long class attributes, these are used for
293 * other purposes by some RADIUS servers (such as NPS). */
294 continue;
295 }
296
297 ike_sa = charon->bus->get_sa(charon->bus);
298 if (ike_sa)
299 {
300 auth = ike_sa->get_auth_cfg(ike_sa, FALSE);
301 id = identification_create_from_data(data);
302 DBG1(DBG_CFG, "received group membership '%Y' from RADIUS", id);
303 auth->add(auth, AUTH_RULE_GROUP, id);
304 }
305 }
306 }
307 enumerator->destroy(enumerator);
308 }
309
310 /**
311 * Handle the Filter-Id attribute as IPsec CHILD_SA name
312 */
313 static void process_filter_id(radius_message_t *msg)
314 {
315 enumerator_t *enumerator;
316 int type;
317 u_int8_t tunnel_tag;
318 u_int32_t tunnel_type;
319 chunk_t filter_id = chunk_empty, data;
320 bool is_esp_tunnel = FALSE;
321
322 enumerator = msg->create_enumerator(msg);
323 while (enumerator->enumerate(enumerator, &type, &data))
324 {
325 switch (type)
326 {
327 case RAT_TUNNEL_TYPE:
328 if (data.len != 4)
329 {
330 continue;
331 }
332 tunnel_tag = *data.ptr;
333 *data.ptr = 0x00;
334 tunnel_type = untoh32(data.ptr);
335 DBG1(DBG_IKE, "received RADIUS attribute Tunnel-Type: "
336 "tag = %u, value = %u", tunnel_tag, tunnel_type);
337 is_esp_tunnel = (tunnel_type == RADIUS_TUNNEL_TYPE_ESP);
338 break;
339 case RAT_FILTER_ID:
340 filter_id = data;
341 DBG1(DBG_IKE, "received RADIUS attribute Filter-Id: "
342 "'%.*s'", (int)filter_id.len, filter_id.ptr);
343 break;
344 default:
345 break;
346 }
347 }
348 enumerator->destroy(enumerator);
349
350 if (is_esp_tunnel && filter_id.len)
351 {
352 identification_t *id;
353 ike_sa_t *ike_sa;
354 auth_cfg_t *auth;
355
356 ike_sa = charon->bus->get_sa(charon->bus);
357 if (ike_sa)
358 {
359 auth = ike_sa->get_auth_cfg(ike_sa, FALSE);
360 id = identification_create_from_data(filter_id);
361 auth->add(auth, AUTH_RULE_GROUP, id);
362 }
363 }
364 }
365
366 /**
367 * Handle Session-Timeout attribte and Interim updates
368 */
369 static void process_timeout(radius_message_t *msg)
370 {
371 enumerator_t *enumerator;
372 ike_sa_t *ike_sa;
373 chunk_t data;
374 int type;
375
376 ike_sa = charon->bus->get_sa(charon->bus);
377 if (ike_sa)
378 {
379 enumerator = msg->create_enumerator(msg);
380 while (enumerator->enumerate(enumerator, &type, &data))
381 {
382 if (type == RAT_SESSION_TIMEOUT && data.len == 4)
383 {
384 ike_sa->set_auth_lifetime(ike_sa, untoh32(data.ptr));
385 }
386 else if (type == RAT_ACCT_INTERIM_INTERVAL && data.len == 4)
387 {
388 eap_radius_accounting_start_interim(ike_sa, untoh32(data.ptr));
389 }
390 }
391 enumerator->destroy(enumerator);
392 }
393 }
394
395 /**
396 * Add a Cisco Unity configuration attribute
397 */
398 static void add_unity_attribute(eap_radius_provider_t *provider, u_int32_t id,
399 int type, chunk_t data)
400 {
401 switch (type)
402 {
403 case 15: /* CVPN3000-IPSec-Banner1 */
404 case 36: /* CVPN3000-IPSec-Banner2 */
405 provider->add_attribute(provider, id, UNITY_BANNER, data);
406 break;
407 case 28: /* CVPN3000-IPSec-Default-Domain */
408 provider->add_attribute(provider, id, UNITY_DEF_DOMAIN, data);
409 break;
410 case 29: /* CVPN3000-IPSec-Split-DNS-Names */
411 provider->add_attribute(provider, id, UNITY_SPLITDNS_NAME, data);
412 break;
413 }
414 }
415
416 /**
417 * Add a DNS/NBNS configuration attribute
418 */
419 static void add_nameserver_attribute(eap_radius_provider_t *provider,
420 u_int32_t id, int type, chunk_t data)
421 {
422 /* these are from different vendors, but there is currently no conflict */
423 switch (type)
424 {
425 case 5: /* CVPN3000-Primary-DNS */
426 case 6: /* CVPN3000-Secondary-DNS */
427 case 28: /* MS-Primary-DNS-Server */
428 case 29: /* MS-Secondary-DNS-Server */
429 provider->add_attribute(provider, id, INTERNAL_IP4_DNS, data);
430 break;
431 case 7: /* CVPN3000-Primary-WINS */
432 case 8: /* CVPN3000-Secondary-WINS */
433 case 30: /* MS-Primary-NBNS-Server */
434 case 31: /* MS-Secondary-NBNS-Server */
435 provider->add_attribute(provider, id, INTERNAL_IP4_NBNS, data);
436 break;
437 case RAT_FRAMED_IPV6_DNS_SERVER:
438 provider->add_attribute(provider, id, INTERNAL_IP6_DNS, data);
439 break;
440 }
441 }
442
443 /**
444 * Add a UNITY_LOCAL_LAN or UNITY_SPLIT_INCLUDE attribute
445 */
446 static void add_unity_split_attribute(eap_radius_provider_t *provider,
447 u_int32_t id, configuration_attribute_type_t type,
448 chunk_t data)
449 {
450 enumerator_t *enumerator;
451 bio_writer_t *writer;
452 char buffer[256], *token, *slash;
453
454 if (snprintf(buffer, sizeof(buffer), "%.*s", (int)data.len,
455 data.ptr) >= sizeof(buffer))
456 {
457 return;
458 }
459 writer = bio_writer_create(16); /* two IPv4 addresses and 6 bytes padding */
460 enumerator = enumerator_create_token(buffer, ",", " ");
461 while (enumerator->enumerate(enumerator, &token))
462 {
463 host_t *net, *mask = NULL;
464 chunk_t padding;
465
466 slash = strchr(token, '/');
467 if (slash)
468 {
469 *slash++ = '\0';
470 mask = host_create_from_string(slash, 0);
471 }
472 if (!mask)
473 { /* default to /32 */
474 mask = host_create_from_string("255.255.255.255", 0);
475 }
476 net = host_create_from_string(token, 0);
477 if (!net || net->get_family(net) != AF_INET ||
478 mask->get_family(mask) != AF_INET)
479 {
480 mask->destroy(mask);
481 DESTROY_IF(net);
482 continue;
483 }
484 writer->write_data(writer, net->get_address(net));
485 writer->write_data(writer, mask->get_address(mask));
486 padding = writer->skip(writer, 6); /* 6 bytes pdding */
487 memset(padding.ptr, 0, padding.len);
488 mask->destroy(mask);
489 net->destroy(net);
490 }
491 enumerator->destroy(enumerator);
492
493 data = writer->get_buf(writer);
494 if (data.len)
495 {
496 provider->add_attribute(provider, id, type, data);
497 }
498 writer->destroy(writer);
499 }
500
501 /**
502 * Handle Framed-IP-Address and other IKE configuration attributes
503 */
504 static void process_cfg_attributes(radius_message_t *msg)
505 {
506 eap_radius_provider_t *provider;
507 enumerator_t *enumerator;
508 ike_sa_t *ike_sa;
509 host_t *host;
510 chunk_t data;
511 configuration_attribute_type_t split_type = 0;
512 int type, vendor;
513
514 ike_sa = charon->bus->get_sa(charon->bus);
515 provider = eap_radius_provider_get();
516 if (provider && ike_sa)
517 {
518 enumerator = msg->create_enumerator(msg);
519 while (enumerator->enumerate(enumerator, &type, &data))
520 {
521 if ((type == RAT_FRAMED_IP_ADDRESS && data.len == 4) ||
522 (type == RAT_FRAMED_IPV6_ADDRESS && data.len == 16))
523 {
524 host = host_create_from_chunk(AF_INET, data, 0);
525 if (host)
526 {
527 provider->add_framed_ip(provider,
528 ike_sa->get_unique_id(ike_sa), host);
529 }
530 }
531 else if (type == RAT_FRAMED_IP_NETMASK && data.len == 4)
532 {
533 provider->add_attribute(provider, ike_sa->get_unique_id(ike_sa),
534 INTERNAL_IP4_NETMASK, data);
535 }
536 else if (type == RAT_FRAMED_IPV6_DNS_SERVER && data.len == 16)
537 {
538 add_nameserver_attribute(provider,
539 ike_sa->get_unique_id(ike_sa), type, data);
540 }
541 }
542 enumerator->destroy(enumerator);
543
544 enumerator = msg->create_vendor_enumerator(msg);
545 while (enumerator->enumerate(enumerator, &vendor, &type, &data))
546 {
547 if (vendor == PEN_ALTIGA /* aka Cisco VPN3000 */)
548 {
549 switch (type)
550 {
551 case 5: /* CVPN3000-Primary-DNS */
552 case 6: /* CVPN3000-Secondary-DNS */
553 case 7: /* CVPN3000-Primary-WINS */
554 case 8: /* CVPN3000-Secondary-WINS */
555 if (data.len == 4)
556 {
557 add_nameserver_attribute(provider,
558 ike_sa->get_unique_id(ike_sa), type, data);
559 }
560 break;
561 case 15: /* CVPN3000-IPSec-Banner1 */
562 case 28: /* CVPN3000-IPSec-Default-Domain */
563 case 29: /* CVPN3000-IPSec-Split-DNS-Names */
564 case 36: /* CVPN3000-IPSec-Banner2 */
565 if (ike_sa->supports_extension(ike_sa, EXT_CISCO_UNITY))
566 {
567 add_unity_attribute(provider,
568 ike_sa->get_unique_id(ike_sa), type, data);
569 }
570 break;
571 case 55: /* CVPN3000-IPSec-Split-Tunneling-Policy */
572 if (data.len)
573 {
574 switch (data.ptr[data.len - 1])
575 {
576 case 0: /* tunnelall */
577 default:
578 break;
579 case 1: /* tunnelspecified */
580 split_type = UNITY_SPLIT_INCLUDE;
581 break;
582 case 2: /* excludespecified */
583 split_type = UNITY_LOCAL_LAN;
584 break;
585 }
586 }
587 break;
588 default:
589 break;
590 }
591 }
592 if (vendor == PEN_MICROSOFT)
593 {
594 switch (type)
595 {
596 case 28: /* MS-Primary-DNS-Server */
597 case 29: /* MS-Secondary-DNS-Server */
598 case 30: /* MS-Primary-NBNS-Server */
599 case 31: /* MS-Secondary-NBNS-Server */
600 if (data.len == 4)
601 {
602 add_nameserver_attribute(provider,
603 ike_sa->get_unique_id(ike_sa), type, data);
604 }
605 break;
606 }
607 }
608 }
609 enumerator->destroy(enumerator);
610
611 if (split_type != 0 &&
612 ike_sa->supports_extension(ike_sa, EXT_CISCO_UNITY))
613 {
614 enumerator = msg->create_vendor_enumerator(msg);
615 while (enumerator->enumerate(enumerator, &vendor, &type, &data))
616 {
617 if (vendor == PEN_ALTIGA /* aka Cisco VPN3000 */ &&
618 type == 27 /* CVPN3000-IPSec-Split-Tunnel-List */)
619 {
620 add_unity_split_attribute(provider,
621 ike_sa->get_unique_id(ike_sa), split_type, data);
622 }
623 }
624 enumerator->destroy(enumerator);
625 }
626 }
627 }
628
629 /**
630 * See header.
631 */
632 void eap_radius_process_attributes(radius_message_t *message)
633 {
634 if (lib->settings->get_bool(lib->settings,
635 "%s.plugins.eap-radius.class_group", FALSE, lib->ns))
636 {
637 process_class(message);
638 }
639 if (lib->settings->get_bool(lib->settings,
640 "%s.plugins.eap-radius.filter_id", FALSE, lib->ns))
641 {
642 process_filter_id(message);
643 }
644 process_timeout(message);
645 process_cfg_attributes(message);
646 }
647
648 METHOD(eap_method_t, process, status_t,
649 private_eap_radius_t *this, eap_payload_t *in, eap_payload_t **out)
650 {
651 radius_message_t *request, *response;
652 status_t status = FAILED;
653 chunk_t data;
654
655 request = radius_message_create(RMC_ACCESS_REQUEST);
656 add_radius_request_attrs(this, request);
657
658 data = in->get_data(in);
659 DBG3(DBG_IKE, "%N payload %B", eap_type_names, this->type, &data);
660
661 /* fragment data suitable for RADIUS */
662 while (data.len > MAX_RADIUS_ATTRIBUTE_SIZE)
663 {
664 request->add(request, RAT_EAP_MESSAGE,
665 chunk_create(data.ptr,MAX_RADIUS_ATTRIBUTE_SIZE));
666 data = chunk_skip(data, MAX_RADIUS_ATTRIBUTE_SIZE);
667 }
668 request->add(request, RAT_EAP_MESSAGE, data);
669
670 response = this->client->request(this->client, request);
671 if (response)
672 {
673 eap_radius_forward_to_ike(response);
674 switch (response->get_code(response))
675 {
676 case RMC_ACCESS_CHALLENGE:
677 if (radius2ike(this, response, out))
678 {
679 status = NEED_MORE;
680 break;
681 }
682 status = FAILED;
683 break;
684 case RMC_ACCESS_ACCEPT:
685 eap_radius_process_attributes(response);
686 DBG1(DBG_IKE, "RADIUS authentication of '%Y' successful",
687 this->peer);
688 status = SUCCESS;
689 break;
690 case RMC_ACCESS_REJECT:
691 default:
692 DBG1(DBG_IKE, "RADIUS authentication of '%Y' failed",
693 this->peer);
694 status = FAILED;
695 break;
696 }
697 response->destroy(response);
698 }
699 request->destroy(request);
700 return status;
701 }
702
703 METHOD(eap_method_t, get_type, eap_type_t,
704 private_eap_radius_t *this, u_int32_t *vendor)
705 {
706 *vendor = this->vendor;
707 return this->type;
708 }
709
710 METHOD(eap_method_t, get_msk, status_t,
711 private_eap_radius_t *this, chunk_t *out)
712 {
713 chunk_t msk;
714
715 msk = this->client->get_msk(this->client);
716 if (msk.len)
717 {
718 *out = msk;
719 return SUCCESS;
720 }
721 return FAILED;
722 }
723
724 METHOD(eap_method_t, get_identifier, u_int8_t,
725 private_eap_radius_t *this)
726 {
727 return this->identifier;
728 }
729
730 METHOD(eap_method_t, set_identifier, void,
731 private_eap_radius_t *this, u_int8_t identifier)
732 {
733 this->identifier = identifier;
734 }
735
736 METHOD(eap_method_t, is_mutual, bool,
737 private_eap_radius_t *this)
738 {
739 switch (this->type)
740 {
741 case EAP_AKA:
742 case EAP_SIM:
743 return TRUE;
744 default:
745 return FALSE;
746 }
747 }
748
749 METHOD(eap_method_t, destroy, void,
750 private_eap_radius_t *this)
751 {
752 this->peer->destroy(this->peer);
753 this->server->destroy(this->server);
754 this->client->destroy(this->client);
755 free(this);
756 }
757
758 /**
759 * Generic constructor
760 */
761 eap_radius_t *eap_radius_create(identification_t *server, identification_t *peer)
762 {
763 private_eap_radius_t *this;
764
765 INIT(this,
766 .public = {
767 .eap_method = {
768 .initiate = _initiate,
769 .process = _process,
770 .get_type = _get_type,
771 .is_mutual = _is_mutual,
772 .get_msk = _get_msk,
773 .get_identifier = _get_identifier,
774 .set_identifier = _set_identifier,
775 .destroy = _destroy,
776 },
777 },
778 /* initially EAP_RADIUS, but is set to the method selected by RADIUS */
779 .type = EAP_RADIUS,
780 .eap_start = lib->settings->get_bool(lib->settings,
781 "%s.plugins.eap-radius.eap_start", FALSE,
782 lib->ns),
783 .id_prefix = lib->settings->get_str(lib->settings,
784 "%s.plugins.eap-radius.id_prefix", "",
785 lib->ns),
786 );
787 this->client = eap_radius_create_client();
788 if (!this->client)
789 {
790 free(this);
791 return NULL;
792 }
793 this->peer = peer->clone(peer);
794 this->server = server->clone(server);
795 return &this->public;
796 }