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