merged EAP framework from branch into trunk
[strongswan.git] / src / charon / sa / authenticators / eap / eap_method.c
1 /**
2 * @file eap_method.c
3 *
4 * @brief Generic constructor for eap_methods.
5 *
6 */
7
8 /*
9 * Copyright (C) 2006 Martin Willi
10 * Hochschule fuer Technik Rapperswil
11 *
12 * This program is free software; you can redistribute it and/or modify it
13 * under the terms of the GNU General Public License as published by the
14 * Free Software Foundation; either version 2 of the License, or (at your
15 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
19 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
20 * for more details.
21 */
22
23 #include <string.h>
24 #include <sys/stat.h>
25 #include <dirent.h>
26 #include <error.h>
27 #include <dlfcn.h>
28
29 #include "eap_method.h"
30
31 #include <daemon.h>
32 #include <library.h>
33 #include <utils/linked_list.h>
34 #include <utils/identification.h>
35
36
37 ENUM_BEGIN(eap_type_names, EAP_IDENTITY, EAP_TOKEN_CARD,
38 "EAP_IDENTITY",
39 "EAP_NOTIFICATION",
40 "EAP_NAK",
41 "EAP_MD5",
42 "EAP_ONE_TIME_PASSWORD",
43 "EAP_TOKEN_CARD");
44 ENUM_NEXT(eap_type_names, EAP_AKA, EAP_AKA, EAP_TOKEN_CARD,
45 "EAP_AKA");
46 ENUM_END(eap_type_names, EAP_AKA);
47
48 ENUM(eap_code_names, EAP_REQUEST, EAP_FAILURE,
49 "EAP_REQUEST",
50 "EAP_RESPONSE",
51 "EAP_SUCCESS",
52 "EAP_FAILURE",
53 );
54
55 ENUM(eap_role_names, EAP_SERVER, EAP_PEER,
56 "EAP_SERVER",
57 "EAP_PEER",
58 );
59
60
61 typedef struct module_entry_t module_entry_t;
62
63 /**
64 * Representation of a loaded module: EAP type, library handle, constructor
65 */
66 struct module_entry_t {
67 eap_type_t type;
68 void *handle;
69 eap_constructor_t constructor;
70 };
71
72 /** List of module_entry_t's */
73 static linked_list_t *modules = NULL;
74
75 /**
76 * unload modules at daemon shutdown
77 */
78 void eap_method_unload()
79 {
80 if (modules)
81 {
82 module_entry_t *entry;
83
84 while (modules->remove_last(modules, (void**)&entry) == SUCCESS)
85 {
86 DBG2(DBG_CFG, "unloaded module for %s", eap_type_names, entry->type);
87 dlclose(entry->handle);
88 free(entry);
89 }
90 modules->destroy(modules);
91 modules = NULL;
92 }
93 }
94
95 /**
96 * Load EAP modules at daemon startup
97 */
98 void eap_method_load(char *directory)
99 {
100 struct dirent* entry;
101 struct stat stb;
102 DIR* dir;
103
104 eap_method_unload();
105 modules = linked_list_create();
106
107 if (stat(directory, &stb) == -1 || !(stb.st_mode & S_IFDIR))
108 {
109 DBG1(DBG_CFG, "error opening EAP modules directory %s", directory);
110 return;
111 }
112 if (stb.st_uid != 0)
113 {
114 DBG1(DBG_CFG, "EAP modules directory %s not owned by root, skipped", directory);
115 return;
116 }
117 if (stb.st_mode & S_IWOTH || stb.st_mode & S_IWGRP)
118 {
119 DBG1(DBG_CFG, "EAP modules directory %s writable by others, skipped", directory);
120 return;
121 }
122
123 dir = opendir(directory);
124 if (dir == NULL)
125 {
126 DBG1(DBG_CFG, "error opening EAP modules directory %s", directory);
127 return;
128 }
129
130 DBG1(DBG_CFG, "loading EAP modules from '%s'", directory);
131
132 while ((entry = readdir(dir)) != NULL)
133 {
134 char file[256];
135 module_entry_t module, *loaded_module;
136 eap_method_t *method;
137 identification_t *id;
138 char *ending;
139
140 snprintf(file, sizeof(file), "%s/%s", directory, entry->d_name);
141
142 if (stat(file, &stb) == -1 || !(stb.st_mode & S_IFREG))
143 {
144 DBG2(DBG_CFG, " skipping %s, doesn't look like a file",
145 entry->d_name);
146 continue;
147 }
148 ending = entry->d_name + strlen(entry->d_name) - 3;
149 if (ending <= entry->d_name || !streq(ending, ".so"))
150 {
151 /* skip anything which does not look like a library */
152 DBG2(DBG_CFG, " skipping %s, doesn't look like a library",
153 entry->d_name);
154 continue;
155 }
156 if (stb.st_uid != 0)
157 {
158 DBG1(DBG_CFG, " skipping %s, file is not owned by root", entry->d_name);
159 return;
160 }
161 if (stb.st_mode & S_IWOTH || stb.st_mode & S_IWGRP)
162 {
163 DBG1(DBG_CFG, " skipping %s, file is writeable by others", entry->d_name);
164 continue;
165 }
166
167 /* try to load the library */
168 module.handle = dlopen(file, RTLD_LAZY);
169 if (module.handle == NULL)
170 {
171 DBG1(DBG_CFG, " opening EAP module %s failed: %s", entry->d_name,
172 dlerror());
173 continue;
174 }
175 module.constructor = dlsym(module.handle, "eap_create");
176 if (module.constructor == NULL)
177 {
178 DBG1(DBG_CFG, " EAP module %s has no eap_create() function, skipped",
179 entry->d_name);
180 dlclose(module.handle);
181 continue;
182 }
183
184 /* get the type implemented in the method, create an instance for it */
185 id = identification_create_from_string("john@doe.xyz");
186 method = module.constructor(EAP_SERVER, id, id);
187 if (method == NULL)
188 {
189 method = module.constructor(EAP_PEER, id, id);
190 }
191 id->destroy(id);
192 if (method == NULL)
193 {
194 DBG1(DBG_CFG, " unable to create instance of EAP method %s, skipped",
195 entry->d_name);
196 dlclose(module.handle);
197 continue;
198 }
199 module.type = method->get_type(method);
200 method->destroy(method);
201
202 DBG1(DBG_CFG, " loaded EAP method %N successfully from %s",
203 eap_type_names, module.type, entry->d_name);
204
205 loaded_module = malloc_thing(module_entry_t);
206 memcpy(loaded_module, &module, sizeof(module));
207 modules->insert_last(modules, loaded_module);
208 }
209 closedir(dir);
210 }
211
212 /*
213 * Described in header.
214 */
215 eap_method_t *eap_method_create(eap_type_t type, eap_role_t role,
216 identification_t *server,
217 identification_t *peer)
218 {
219 eap_method_t *method = NULL;
220 iterator_t *iterator;
221 module_entry_t *entry;
222
223 iterator = modules->create_iterator(modules, TRUE);
224 while (iterator->iterate(iterator, (void**)&entry))
225 {
226 if (entry->type == type)
227 {
228 method = entry->constructor(role, server, peer);
229 if (method)
230 {
231 break;
232 }
233 }
234 }
235 iterator->destroy(iterator);
236
237 if (method == NULL)
238 {
239 DBG1(DBG_CFG, "no EAP module found for %N %N",
240 eap_type_names, type, eap_role_names, role);
241 }
242 return method;
243 }