1189251Ssam/*
2189251Ssam * EAP peer: Method registration
3189251Ssam * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi>
4189251Ssam *
5252726Srpaulo * This software may be distributed under the terms of the BSD license.
6252726Srpaulo * See README for more details.
7189251Ssam */
8189251Ssam
9189251Ssam#include "includes.h"
10189251Ssam#ifdef CONFIG_DYNAMIC_EAP_METHODS
11189251Ssam#include <dlfcn.h>
12189251Ssam#endif /* CONFIG_DYNAMIC_EAP_METHODS */
13189251Ssam
14189251Ssam#include "common.h"
15189251Ssam#include "eap_i.h"
16189251Ssam#include "eap_methods.h"
17189251Ssam
18189251Ssam
19189251Ssamstatic struct eap_method *eap_methods = NULL;
20189251Ssam
21337817Scystatic void eap_peer_method_free(struct eap_method *method);
22189251Ssam
23337817Scy
24189251Ssam/**
25189251Ssam * eap_peer_get_eap_method - Get EAP method based on type number
26189251Ssam * @vendor: EAP Vendor-Id (0 = IETF)
27189251Ssam * @method: EAP type number
28189251Ssam * Returns: Pointer to EAP method or %NULL if not found
29189251Ssam */
30189251Ssamconst struct eap_method * eap_peer_get_eap_method(int vendor, EapType method)
31189251Ssam{
32189251Ssam	struct eap_method *m;
33189251Ssam	for (m = eap_methods; m; m = m->next) {
34189251Ssam		if (m->vendor == vendor && m->method == method)
35189251Ssam			return m;
36189251Ssam	}
37189251Ssam	return NULL;
38189251Ssam}
39189251Ssam
40189251Ssam
41189251Ssam/**
42189251Ssam * eap_peer_get_type - Get EAP type for the given EAP method name
43189251Ssam * @name: EAP method name, e.g., TLS
44189251Ssam * @vendor: Buffer for returning EAP Vendor-Id
45189251Ssam * Returns: EAP method type or %EAP_TYPE_NONE if not found
46189251Ssam *
47189251Ssam * This function maps EAP type names into EAP type numbers based on the list of
48189251Ssam * EAP methods included in the build.
49189251Ssam */
50189251SsamEapType eap_peer_get_type(const char *name, int *vendor)
51189251Ssam{
52189251Ssam	struct eap_method *m;
53189251Ssam	for (m = eap_methods; m; m = m->next) {
54189251Ssam		if (os_strcmp(m->name, name) == 0) {
55189251Ssam			*vendor = m->vendor;
56189251Ssam			return m->method;
57189251Ssam		}
58189251Ssam	}
59189251Ssam	*vendor = EAP_VENDOR_IETF;
60189251Ssam	return EAP_TYPE_NONE;
61189251Ssam}
62189251Ssam
63189251Ssam
64189251Ssam/**
65189251Ssam * eap_get_name - Get EAP method name for the given EAP type
66189251Ssam * @vendor: EAP Vendor-Id (0 = IETF)
67189251Ssam * @type: EAP method type
68189251Ssam * Returns: EAP method name, e.g., TLS, or %NULL if not found
69189251Ssam *
70189251Ssam * This function maps EAP type numbers into EAP type names based on the list of
71189251Ssam * EAP methods included in the build.
72189251Ssam */
73189251Ssamconst char * eap_get_name(int vendor, EapType type)
74189251Ssam{
75189251Ssam	struct eap_method *m;
76252726Srpaulo	if (vendor == EAP_VENDOR_IETF && type == EAP_TYPE_EXPANDED)
77252726Srpaulo		return "expanded";
78189251Ssam	for (m = eap_methods; m; m = m->next) {
79189251Ssam		if (m->vendor == vendor && m->method == type)
80189251Ssam			return m->name;
81189251Ssam	}
82189251Ssam	return NULL;
83189251Ssam}
84189251Ssam
85189251Ssam
86189251Ssam/**
87189251Ssam * eap_get_names - Get space separated list of names for supported EAP methods
88189251Ssam * @buf: Buffer for names
89189251Ssam * @buflen: Buffer length
90189251Ssam * Returns: Number of characters written into buf (not including nul
91189251Ssam * termination)
92189251Ssam */
93189251Ssamsize_t eap_get_names(char *buf, size_t buflen)
94189251Ssam{
95189251Ssam	char *pos, *end;
96189251Ssam	struct eap_method *m;
97189251Ssam	int ret;
98189251Ssam
99189251Ssam	if (buflen == 0)
100189251Ssam		return 0;
101189251Ssam
102189251Ssam	pos = buf;
103189251Ssam	end = pos + buflen;
104189251Ssam
105189251Ssam	for (m = eap_methods; m; m = m->next) {
106189251Ssam		ret = os_snprintf(pos, end - pos, "%s%s",
107189251Ssam				  m == eap_methods ? "" : " ", m->name);
108281806Srpaulo		if (os_snprintf_error(end - pos, ret))
109189251Ssam			break;
110189251Ssam		pos += ret;
111189251Ssam	}
112189251Ssam	buf[buflen - 1] = '\0';
113189251Ssam
114189251Ssam	return pos - buf;
115189251Ssam}
116189251Ssam
117189251Ssam
118189251Ssam/**
119189251Ssam * eap_get_names_as_string_array - Get supported EAP methods as string array
120189251Ssam * @num: Buffer for returning the number of items in array, not including %NULL
121189251Ssam * terminator. This parameter can be %NULL if the length is not needed.
122189251Ssam * Returns: A %NULL-terminated array of strings, or %NULL on error.
123189251Ssam *
124189251Ssam * This function returns the list of names for all supported EAP methods as an
125189251Ssam * array of strings. The caller must free the returned array items and the
126189251Ssam * array.
127189251Ssam */
128189251Ssamchar ** eap_get_names_as_string_array(size_t *num)
129189251Ssam{
130189251Ssam	struct eap_method *m;
131189251Ssam	size_t array_len = 0;
132189251Ssam	char **array;
133189251Ssam	int i = 0, j;
134189251Ssam
135189251Ssam	for (m = eap_methods; m; m = m->next)
136189251Ssam		array_len++;
137189251Ssam
138281806Srpaulo	array = os_calloc(array_len + 1, sizeof(char *));
139189251Ssam	if (array == NULL)
140189251Ssam		return NULL;
141189251Ssam
142189251Ssam	for (m = eap_methods; m; m = m->next) {
143189251Ssam		array[i++] = os_strdup(m->name);
144189251Ssam		if (array[i - 1] == NULL) {
145189251Ssam			for (j = 0; j < i; j++)
146189251Ssam				os_free(array[j]);
147189251Ssam			os_free(array);
148189251Ssam			return NULL;
149189251Ssam		}
150189251Ssam	}
151189251Ssam	array[i] = NULL;
152189251Ssam
153189251Ssam	if (num)
154189251Ssam		*num = array_len;
155189251Ssam
156189251Ssam	return array;
157189251Ssam}
158189251Ssam
159189251Ssam
160189251Ssam/**
161189251Ssam * eap_peer_get_methods - Get a list of enabled EAP peer methods
162189251Ssam * @count: Set to number of available methods
163189251Ssam * Returns: List of enabled EAP peer methods
164189251Ssam */
165189251Ssamconst struct eap_method * eap_peer_get_methods(size_t *count)
166189251Ssam{
167189251Ssam	int c = 0;
168189251Ssam	struct eap_method *m;
169189251Ssam
170189251Ssam	for (m = eap_methods; m; m = m->next)
171189251Ssam		c++;
172189251Ssam
173189251Ssam	*count = c;
174189251Ssam	return eap_methods;
175189251Ssam}
176189251Ssam
177189251Ssam
178189251Ssam#ifdef CONFIG_DYNAMIC_EAP_METHODS
179189251Ssam/**
180189251Ssam * eap_peer_method_load - Load a dynamic EAP method library (shared object)
181189251Ssam * @so: File path for the shared object file to load
182189251Ssam * Returns: 0 on success, -1 on failure
183189251Ssam */
184189251Ssamint eap_peer_method_load(const char *so)
185189251Ssam{
186189251Ssam	void *handle;
187189251Ssam	int (*dyn_init)(void);
188189251Ssam	int ret;
189189251Ssam
190189251Ssam	handle = dlopen(so, RTLD_LAZY);
191189251Ssam	if (handle == NULL) {
192189251Ssam		wpa_printf(MSG_ERROR, "EAP: Failed to open dynamic EAP method "
193189251Ssam			   "'%s': %s", so, dlerror());
194189251Ssam		return -1;
195189251Ssam	}
196189251Ssam
197189251Ssam	dyn_init = dlsym(handle, "eap_peer_method_dynamic_init");
198189251Ssam	if (dyn_init == NULL) {
199189251Ssam		dlclose(handle);
200189251Ssam		wpa_printf(MSG_ERROR, "EAP: Invalid EAP method '%s' - no "
201189251Ssam			   "eap_peer_method_dynamic_init()", so);
202189251Ssam		return -1;
203189251Ssam	}
204189251Ssam
205189251Ssam	ret = dyn_init();
206189251Ssam	if (ret) {
207189251Ssam		dlclose(handle);
208189251Ssam		wpa_printf(MSG_ERROR, "EAP: Failed to add EAP method '%s' - "
209189251Ssam			   "ret %d", so, ret);
210189251Ssam		return ret;
211189251Ssam	}
212189251Ssam
213189251Ssam	/* Store the handle for this shared object. It will be freed with
214189251Ssam	 * dlclose() when the EAP method is unregistered. */
215189251Ssam	eap_methods->dl_handle = handle;
216189251Ssam
217189251Ssam	wpa_printf(MSG_DEBUG, "EAP: Loaded dynamic EAP method: '%s'", so);
218189251Ssam
219189251Ssam	return 0;
220189251Ssam}
221189251Ssam
222189251Ssam
223189251Ssam/**
224189251Ssam * eap_peer_method_unload - Unload a dynamic EAP method library (shared object)
225189251Ssam * @method: Pointer to the dynamically loaded EAP method
226189251Ssam * Returns: 0 on success, -1 on failure
227189251Ssam *
228189251Ssam * This function can be used to unload EAP methods that have been previously
229189251Ssam * loaded with eap_peer_method_load(). Before unloading the method, all
230189251Ssam * references to the method must be removed to make sure that no dereferences
231189251Ssam * of freed memory will occur after unloading.
232189251Ssam */
233189251Ssamint eap_peer_method_unload(struct eap_method *method)
234189251Ssam{
235189251Ssam	struct eap_method *m, *prev;
236189251Ssam	void *handle;
237189251Ssam
238189251Ssam	m = eap_methods;
239189251Ssam	prev = NULL;
240189251Ssam	while (m) {
241189251Ssam		if (m == method)
242189251Ssam			break;
243189251Ssam		prev = m;
244189251Ssam		m = m->next;
245189251Ssam	}
246189251Ssam
247189251Ssam	if (m == NULL || m->dl_handle == NULL)
248189251Ssam		return -1;
249189251Ssam
250189251Ssam	if (prev)
251189251Ssam		prev->next = m->next;
252189251Ssam	else
253189251Ssam		eap_methods = m->next;
254189251Ssam
255189251Ssam	handle = m->dl_handle;
256189251Ssam
257189251Ssam	if (m->free)
258189251Ssam		m->free(m);
259189251Ssam	else
260189251Ssam		eap_peer_method_free(m);
261189251Ssam
262189251Ssam	dlclose(handle);
263189251Ssam
264189251Ssam	return 0;
265189251Ssam}
266189251Ssam#endif /* CONFIG_DYNAMIC_EAP_METHODS */
267189251Ssam
268189251Ssam
269189251Ssam/**
270189251Ssam * eap_peer_method_alloc - Allocate EAP peer method structure
271189251Ssam * @version: Version of the EAP peer method interface (set to
272189251Ssam * EAP_PEER_METHOD_INTERFACE_VERSION)
273189251Ssam * @vendor: EAP Vendor-ID (EAP_VENDOR_*) (0 = IETF)
274189251Ssam * @method: EAP type number (EAP_TYPE_*)
275189251Ssam * @name: Name of the method (e.g., "TLS")
276189251Ssam * Returns: Allocated EAP method structure or %NULL on failure
277189251Ssam *
278189251Ssam * The returned structure should be freed with eap_peer_method_free() when it
279189251Ssam * is not needed anymore.
280189251Ssam */
281189251Ssamstruct eap_method * eap_peer_method_alloc(int version, int vendor,
282189251Ssam					  EapType method, const char *name)
283189251Ssam{
284189251Ssam	struct eap_method *eap;
285189251Ssam	eap = os_zalloc(sizeof(*eap));
286189251Ssam	if (eap == NULL)
287189251Ssam		return NULL;
288189251Ssam	eap->version = version;
289189251Ssam	eap->vendor = vendor;
290189251Ssam	eap->method = method;
291189251Ssam	eap->name = name;
292189251Ssam	return eap;
293189251Ssam}
294189251Ssam
295189251Ssam
296189251Ssam/**
297189251Ssam * eap_peer_method_free - Free EAP peer method structure
298189251Ssam * @method: Method structure allocated with eap_peer_method_alloc()
299189251Ssam */
300337817Scystatic void eap_peer_method_free(struct eap_method *method)
301189251Ssam{
302189251Ssam	os_free(method);
303189251Ssam}
304189251Ssam
305189251Ssam
306189251Ssam/**
307189251Ssam * eap_peer_method_register - Register an EAP peer method
308337817Scy * @method: EAP method to register from eap_peer_method_alloc()
309189251Ssam * Returns: 0 on success, -1 on invalid method, or -2 if a matching EAP method
310189251Ssam * has already been registered
311189251Ssam *
312189251Ssam * Each EAP peer method needs to call this function to register itself as a
313337817Scy * supported EAP method. The caller must not free the allocated method data
314337817Scy * regardless of the return value.
315189251Ssam */
316189251Ssamint eap_peer_method_register(struct eap_method *method)
317189251Ssam{
318189251Ssam	struct eap_method *m, *last = NULL;
319189251Ssam
320189251Ssam	if (method == NULL || method->name == NULL ||
321337817Scy	    method->version != EAP_PEER_METHOD_INTERFACE_VERSION) {
322337817Scy		eap_peer_method_free(method);
323189251Ssam		return -1;
324337817Scy	}
325189251Ssam
326189251Ssam	for (m = eap_methods; m; m = m->next) {
327189251Ssam		if ((m->vendor == method->vendor &&
328189251Ssam		     m->method == method->method) ||
329337817Scy		    os_strcmp(m->name, method->name) == 0) {
330337817Scy			eap_peer_method_free(method);
331189251Ssam			return -2;
332337817Scy		}
333189251Ssam		last = m;
334189251Ssam	}
335189251Ssam
336189251Ssam	if (last)
337189251Ssam		last->next = method;
338189251Ssam	else
339189251Ssam		eap_methods = method;
340189251Ssam
341189251Ssam	return 0;
342189251Ssam}
343189251Ssam
344189251Ssam
345189251Ssam/**
346189251Ssam * eap_peer_unregister_methods - Unregister EAP peer methods
347189251Ssam *
348189251Ssam * This function is called at program termination to unregister all EAP peer
349189251Ssam * methods.
350189251Ssam */
351189251Ssamvoid eap_peer_unregister_methods(void)
352189251Ssam{
353189251Ssam	struct eap_method *m;
354189251Ssam#ifdef CONFIG_DYNAMIC_EAP_METHODS
355189251Ssam	void *handle;
356189251Ssam#endif /* CONFIG_DYNAMIC_EAP_METHODS */
357189251Ssam
358189251Ssam	while (eap_methods) {
359189251Ssam		m = eap_methods;
360189251Ssam		eap_methods = eap_methods->next;
361189251Ssam
362189251Ssam#ifdef CONFIG_DYNAMIC_EAP_METHODS
363189251Ssam		handle = m->dl_handle;
364189251Ssam#endif /* CONFIG_DYNAMIC_EAP_METHODS */
365189251Ssam
366189251Ssam		if (m->free)
367189251Ssam			m->free(m);
368189251Ssam		else
369189251Ssam			eap_peer_method_free(m);
370189251Ssam
371189251Ssam#ifdef CONFIG_DYNAMIC_EAP_METHODS
372189251Ssam		if (handle)
373189251Ssam			dlclose(handle);
374189251Ssam#endif /* CONFIG_DYNAMIC_EAP_METHODS */
375189251Ssam	}
376189251Ssam}
377