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