libtnccs: Optionally use RTLD_NOW to load IMC/IMVs with dlopen()
[strongswan.git] / src / libtnccs / plugins / tnc_imc / tnc_imc.c
1 /*
2 * Copyright (C) 2010-2013 Andreas Steffen,
3 * HSR 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 "tnc_imc.h"
17
18 #ifndef WIN32
19 #include <dlfcn.h>
20 #endif
21
22 #include <tncif_pa_subtypes.h>
23
24 #include <utils/debug.h>
25 #include <library.h>
26 #include <collections/linked_list.h>
27 #include <threading/mutex.h>
28
29 typedef struct private_tnc_imc_t private_tnc_imc_t;
30
31 /**
32 * Private data of an imc_t object.
33 */
34 struct private_tnc_imc_t {
35
36 /**
37 * Public members of imc_t.
38 */
39 imc_t public;
40
41 /**
42 * Name of loaded IMC
43 */
44 char *name;
45
46 /**
47 * Handle of loaded IMC
48 */
49 void *handle;
50
51 /**
52 * ID of loaded IMC
53 */
54 TNC_IMCID id;
55
56 /**
57 * list of additional IMC IDs
58 */
59 linked_list_t *additional_ids;
60
61 /**
62 * List of message types supported by IMC - Vendor ID part
63 */
64 TNC_VendorIDList supported_vids;
65
66 /**
67 * List of message types supported by IMC - Subtype part
68 */
69 TNC_MessageSubtypeList supported_subtypes;
70
71 /**
72 * Number of supported message types
73 */
74 TNC_UInt32 type_count;
75
76 /**
77 * mutex to lock the imc_t object
78 */
79 mutex_t *mutex;
80 };
81
82 METHOD(imc_t, set_id, void,
83 private_tnc_imc_t *this, TNC_IMCID id)
84 {
85 this->id = id;
86 }
87
88 METHOD(imc_t, get_id, TNC_IMCID,
89 private_tnc_imc_t *this)
90 {
91 return this->id;
92 }
93
94 METHOD(imc_t, add_id, void,
95 private_tnc_imc_t *this, TNC_IMCID id)
96 {
97 void *pointer;
98
99 /* store the scalar value in the pointer */
100 pointer = (void*)(uintptr_t)id;
101 this->additional_ids->insert_last(this->additional_ids, pointer);
102 }
103
104 METHOD(imc_t, has_id, bool,
105 private_tnc_imc_t *this, TNC_IMCID id)
106 {
107 enumerator_t *enumerator;
108 TNC_IMCID additional_id;
109 void *pointer;
110 bool found = FALSE;
111
112 /* check primary IMC ID */
113 if (id == this->id)
114 {
115 return TRUE;
116 }
117
118 /* return if there are no additional IMC IDs */
119 if (this->additional_ids->get_count(this->additional_ids) == 0)
120 {
121 return FALSE;
122 }
123
124 /* check additional IMC IDs */
125 enumerator = this->additional_ids->create_enumerator(this->additional_ids);
126 while (enumerator->enumerate(enumerator, &pointer))
127 {
128 /* interpret pointer as scalar value */
129 additional_id = (uintptr_t)pointer;
130
131 if (id == additional_id)
132 {
133 found = TRUE;
134 break;
135 }
136 }
137 enumerator->destroy(enumerator);
138
139 return found;
140 }
141
142 METHOD(imc_t, get_name, char*,
143 private_tnc_imc_t *this)
144 {
145 return this->name;
146 }
147
148 METHOD(imc_t, set_message_types, void,
149 private_tnc_imc_t *this, TNC_MessageTypeList supported_types,
150 TNC_UInt32 type_count)
151 {
152 char buf[BUF_LEN];
153 char *pos = buf;
154 int len = sizeof(buf);
155 int i, written;
156 size_t size;
157 TNC_VendorID vid;
158 TNC_MessageSubtype subtype;
159 enum_name_t *pa_subtype_names;
160
161 /* lock the imc_t instance */
162 this->mutex->lock(this->mutex);
163
164 /* Free existing VendorID and MessageSubtype lists */
165 free(this->supported_vids);
166 this->supported_vids = NULL;
167 free(this->supported_subtypes);
168 this->supported_subtypes = NULL;
169
170 /* Store the new MessageType list */
171 this->type_count = type_count;
172 if (type_count && supported_types)
173 {
174 size = type_count * sizeof(TNC_VendorID);
175 this->supported_vids = malloc(size);
176 size = type_count * sizeof(TNC_MessageSubtype);
177 this->supported_subtypes = malloc(size);
178
179 for (i = 0; i < type_count; i++)
180 {
181 vid = (supported_types[i] >> 8) & TNC_VENDORID_ANY;
182 subtype = supported_types[i] & TNC_SUBTYPE_ANY;
183
184 pa_subtype_names = get_pa_subtype_names(vid);
185 if (pa_subtype_names)
186 {
187 written = snprintf(pos, len," '%N/%N' 0x%06x/0x%02x",
188 pen_names, vid, pa_subtype_names, subtype,
189 vid, subtype);
190 }
191 else
192 {
193 written = snprintf(pos, len," '%N' 0x%06x/0x%02x",
194 pen_names, vid, vid, subtype);
195 }
196 if (written >= len)
197 {
198 break;
199 }
200 pos += written;
201 len -= written;
202
203 this->supported_vids[i] = vid;
204 this->supported_subtypes[i] = subtype;
205 }
206 }
207 *pos = '\0';
208 DBG2(DBG_TNC, "IMC %u supports %u message type%s:%s",
209 this->id, type_count, (type_count == 1) ? "":"s", buf);
210
211 /* unlock the imc_t instance */
212 this->mutex->unlock(this->mutex);
213 }
214
215 METHOD(imc_t, set_message_types_long, void,
216 private_tnc_imc_t *this, TNC_VendorIDList supported_vids,
217 TNC_MessageSubtypeList supported_subtypes, TNC_UInt32 type_count)
218 {
219 char buf[BUF_LEN];
220 char *pos = buf;
221 int len = sizeof(buf);
222 int i, written;
223 size_t size;
224 TNC_VendorID vid;
225 TNC_MessageSubtype subtype;
226 enum_name_t *pa_subtype_names;
227
228 /* lock the imc_t instance */
229 this->mutex->lock(this->mutex);
230
231 /* Free existing VendorID and MessageSubtype lists */
232 free(this->supported_vids);
233 this->supported_vids = NULL;
234 free(this->supported_subtypes);
235 this->supported_subtypes = NULL;
236
237 /* Store the new MessageType list */
238 this->type_count = type_count;
239 if (type_count && supported_vids && supported_subtypes)
240 {
241 size = type_count * sizeof(TNC_VendorID);
242 this->supported_vids = malloc(size);
243 memcpy(this->supported_vids, supported_vids, size);
244 size = type_count * sizeof(TNC_MessageSubtype);
245 this->supported_subtypes = malloc(size);
246 memcpy(this->supported_subtypes, supported_subtypes, size);
247
248 for (i = 0; i < type_count; i++)
249 {
250 vid = supported_vids[i];
251 subtype = supported_subtypes[i];
252
253 pa_subtype_names = get_pa_subtype_names(vid);
254 if (pa_subtype_names)
255 {
256 written = snprintf(pos, len," '%N/%N' 0x%06x/0x%08x",
257 pen_names, vid, pa_subtype_names, subtype,
258 vid, subtype);
259 }
260 else
261 {
262 written = snprintf(pos, len," '%N' 0x%06x/0x%08x",
263 pen_names, vid, vid, subtype);
264 }
265 if (written >= len)
266 {
267 break;
268 }
269 pos += written;
270 len -= written;
271 }
272 }
273 *pos = '\0';
274 DBG2(DBG_TNC, "IMC %u supports %u message type%s:%s",
275 this->id, type_count, (type_count == 1) ? "":"s", buf);
276
277 /* unlock the imc_t instance */
278 this->mutex->unlock(this->mutex);
279 }
280
281 METHOD(imc_t, type_supported, bool,
282 private_tnc_imc_t *this, TNC_VendorID msg_vid, TNC_MessageSubtype msg_subtype)
283 {
284 TNC_VendorID vid;
285 TNC_MessageSubtype subtype;
286 int i;
287
288 for (i = 0; i < this->type_count; i++)
289 {
290 vid = this->supported_vids[i];
291 subtype = this->supported_subtypes[i];
292
293 if ((vid == TNC_VENDORID_ANY && subtype == TNC_SUBTYPE_ANY) ||
294 (vid == msg_vid && (subtype == TNC_SUBTYPE_ANY ||
295 subtype == msg_subtype)))
296 {
297 return TRUE;
298 }
299 }
300 return FALSE;
301 }
302
303 METHOD(imc_t, destroy, void,
304 private_tnc_imc_t *this)
305 {
306 if (this->handle && lib->settings->get_bool(lib->settings,
307 "%s.plugins.tnc-imc.dlclose", TRUE, lib->ns))
308 {
309 dlclose(this->handle);
310 }
311 this->mutex->destroy(this->mutex);
312 this->additional_ids->destroy(this->additional_ids);
313 free(this->supported_vids);
314 free(this->supported_subtypes);
315 free(this->name);
316 free(this);
317 }
318
319 /**
320 * Generic constructor
321 */
322 static private_tnc_imc_t* tnc_imc_create_empty(char *name)
323 {
324 private_tnc_imc_t *this;
325
326 INIT(this,
327 .public = {
328 .set_id = _set_id,
329 .get_id = _get_id,
330 .add_id = _add_id,
331 .has_id = _has_id,
332 .get_name = _get_name,
333 .set_message_types = _set_message_types,
334 .set_message_types_long = _set_message_types_long,
335 .type_supported = _type_supported,
336 .destroy = _destroy,
337 },
338 .name = strdup(name),
339 .additional_ids = linked_list_create(),
340 .mutex = mutex_create(MUTEX_TYPE_DEFAULT),
341 );
342
343 return this;
344 }
345
346 /**
347 * See header
348 */
349 imc_t* tnc_imc_create(char *name, char *path)
350 {
351 private_tnc_imc_t *this;
352 int flag = RTLD_LAZY;
353
354 this = tnc_imc_create_empty(name);
355
356 if (lib->settings->get_bool(lib->settings, "%s.dlopen_use_rtld_now",
357 lib->ns, FALSE))
358 {
359 flag = RTLD_NOW;
360 }
361 this->handle = dlopen(path, flag);
362 if (!this->handle)
363 {
364 DBG1(DBG_TNC, "IMC \"%s\" failed to load: %s", name, dlerror());
365 destroy(this);
366 return NULL;
367 }
368
369 this->public.initialize = dlsym(this->handle, "TNC_IMC_Initialize");
370 if (!this->public.initialize)
371 {
372 DBG1(DBG_TNC, "could not resolve TNC_IMC_Initialize in %s: %s\n",
373 path, dlerror());
374 destroy(this);
375 return NULL;
376 }
377 this->public.notify_connection_change =
378 dlsym(this->handle, "TNC_IMC_NotifyConnectionChange");
379 this->public.begin_handshake = dlsym(this->handle, "TNC_IMC_BeginHandshake");
380 if (!this->public.begin_handshake)
381 {
382 DBG1(DBG_TNC, "could not resolve TNC_IMC_BeginHandshake in %s: %s\n",
383 path, dlerror());
384 destroy(this);
385 return NULL;
386 }
387 this->public.receive_message =
388 dlsym(this->handle, "TNC_IMC_ReceiveMessage");
389 this->public.receive_message_long =
390 dlsym(this->handle, "TNC_IMC_ReceiveMessageLong");
391 this->public.batch_ending =
392 dlsym(this->handle, "TNC_IMC_BatchEnding");
393 this->public.terminate =
394 dlsym(this->handle, "TNC_IMC_Terminate");
395 this->public.provide_bind_function =
396 dlsym(this->handle, "TNC_IMC_ProvideBindFunction");
397 if (!this->public.provide_bind_function)
398 {
399 DBG1(DBG_TNC, "could not resolve TNC_IMC_ProvideBindFunction in %s: %s\n",
400 path, dlerror());
401 destroy(this);
402 return NULL;
403 }
404
405 return &this->public;
406 }
407
408 /**
409 * See header
410 */
411 imc_t* tnc_imc_create_from_functions(char *name,
412 TNC_IMC_InitializePointer initialize,
413 TNC_IMC_NotifyConnectionChangePointer notify_connection_change,
414 TNC_IMC_BeginHandshakePointer begin_handshake,
415 TNC_IMC_ReceiveMessagePointer receive_message,
416 TNC_IMC_ReceiveMessageLongPointer receive_message_long,
417 TNC_IMC_BatchEndingPointer batch_ending,
418 TNC_IMC_TerminatePointer terminate,
419 TNC_IMC_ProvideBindFunctionPointer provide_bind_function)
420 {
421 private_tnc_imc_t *this;
422
423 this = tnc_imc_create_empty(name);
424
425 this->public.initialize = initialize;
426 this->public.notify_connection_change = notify_connection_change;
427 this->public.begin_handshake = begin_handshake;
428 this->public.receive_message = receive_message;
429 this->public.receive_message_long = receive_message_long;
430 this->public.batch_ending = batch_ending;
431 this->public.terminate = terminate;
432 this->public.provide_bind_function = provide_bind_function;
433
434 return &this->public;
435 }