]>
Commit | Line | Data |
---|---|---|
510f37ab | 1 | /* |
00cd79b6 | 2 | * Copyright (C) 2011-2015 Andreas Steffen |
22e97e4f | 3 | * HSR Hochschule fuer Technik Rapperswil |
510f37ab AS |
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 | ||
d4c8fe3c | 16 | #include "imcv.h" |
510f37ab | 17 | #include "imv_agent.h" |
a6266485 AS |
18 | #include "imv_session.h" |
19 | ||
6f93927b | 20 | #include "ietf/ietf_attr_assess_result.h" |
510f37ab | 21 | |
0eb23d7b | 22 | #include <tncif_names.h> |
bd1ee5bd | 23 | #include <tncif_identity.h> |
0eb23d7b | 24 | |
f05b4272 | 25 | #include <utils/debug.h> |
bd1ee5bd AS |
26 | #include <collections/linked_list.h> |
27 | #include <bio/bio_reader.h> | |
510f37ab AS |
28 | #include <threading/rwlock.h> |
29 | ||
30 | typedef struct private_imv_agent_t private_imv_agent_t; | |
31 | ||
32 | /** | |
33 | * Private data of an imv_agent_t object. | |
34 | */ | |
35 | struct private_imv_agent_t { | |
36 | ||
37 | /** | |
38 | * Public members of imv_agent_t | |
39 | */ | |
40 | imv_agent_t public; | |
41 | ||
42 | /** | |
43 | * name of IMV | |
44 | */ | |
45 | const char *name; | |
46 | ||
47 | /** | |
5f085d7e | 48 | * message types registered by IMV |
510f37ab | 49 | */ |
5f085d7e | 50 | pen_type_t *supported_types; |
c87acaf5 AS |
51 | |
52 | /** | |
5f085d7e | 53 | * number of message types registered by IMV |
c87acaf5 | 54 | */ |
4894bfa2 | 55 | uint32_t type_count; |
ea67a75b | 56 | |
510f37ab AS |
57 | /** |
58 | * ID of IMV as assigned by TNCS | |
59 | */ | |
60 | TNC_IMVID id; | |
61 | ||
cbf2ba54 AS |
62 | /** |
63 | * List of additional IMV IDs assigned by TNCS | |
64 | */ | |
65 | linked_list_t *additional_ids; | |
66 | ||
38b5f527 AS |
67 | /** |
68 | * list of non-fatal unsupported PA-TNC attribute types | |
69 | */ | |
70 | linked_list_t *non_fatal_attr_types; | |
71 | ||
510f37ab AS |
72 | /** |
73 | * list of TNCS connection entries | |
74 | */ | |
75 | linked_list_t *connections; | |
76 | ||
77 | /** | |
78 | * rwlock to lock TNCS connection entries | |
79 | */ | |
80 | rwlock_t *connection_lock; | |
81 | ||
82 | /** | |
c87acaf5 | 83 | * Inform a TNCS about the set of message types the IMV is able to receive |
510f37ab AS |
84 | * |
85 | * @param imv_id IMV ID assigned by TNCS | |
86 | * @param supported_types list of supported message types | |
87 | * @param type_count number of list elements | |
88 | * @return TNC result code | |
89 | */ | |
90 | TNC_Result (*report_message_types)(TNC_IMVID imv_id, | |
91 | TNC_MessageTypeList supported_types, | |
92 | TNC_UInt32 type_count); | |
93 | ||
c87acaf5 AS |
94 | /** |
95 | * Inform a TNCS about the set of message types the IMV is able to receive | |
96 | * | |
97 | * @param imv_id IMV ID assigned by TNCS | |
98 | * @param supported_vids list of supported message vendor IDs | |
99 | * @param supported_subtypes list of supported message subtypes | |
100 | * @param type_count number of list elements | |
101 | * @return TNC result code | |
102 | */ | |
103 | TNC_Result (*report_message_types_long)(TNC_IMVID imv_id, | |
104 | TNC_VendorIDList supported_vids, | |
105 | TNC_MessageSubtypeList supported_subtypes, | |
106 | TNC_UInt32 type_count); | |
107 | ||
510f37ab AS |
108 | /** |
109 | * Deliver IMV Action Recommendation and IMV Evaluation Results to the TNCS | |
110 | * | |
111 | * @param imv_id IMV ID assigned by TNCS | |
112 | # @param connection_id network connection ID assigned by TNCS | |
113 | * @param rec IMV action recommendation | |
114 | * @param eval IMV evaluation result | |
115 | * @return TNC result code | |
116 | */ | |
117 | TNC_Result (*provide_recommendation)(TNC_IMVID imv_id, | |
118 | TNC_ConnectionID connection_id, | |
119 | TNC_IMV_Action_Recommendation rec, | |
120 | TNC_IMV_Evaluation_Result eval); | |
121 | ||
44bd40a1 AS |
122 | /** |
123 | * Get the value of an attribute associated with a connection | |
124 | * or with the TNCS as a whole. | |
125 | * | |
126 | * @param imv_id IMV ID assigned by TNCS | |
127 | * @param connection_id network connection ID assigned by TNCS | |
128 | * @param attribute_id attribute ID | |
129 | * @param buffer_len length of buffer in bytes | |
130 | * @param buffer buffer | |
131 | * @param out_value_len size in bytes of attribute stored in buffer | |
132 | * @return TNC result code | |
133 | */ | |
134 | TNC_Result (*get_attribute)(TNC_IMVID imv_id, | |
135 | TNC_ConnectionID connection_id, | |
136 | TNC_AttributeID attribute_id, | |
137 | TNC_UInt32 buffer_len, | |
138 | TNC_BufferReference buffer, | |
139 | TNC_UInt32 *out_value_len); | |
140 | ||
141 | /** | |
142 | * Set the value of an attribute associated with a connection | |
143 | * or with the TNCS as a whole. | |
144 | * | |
145 | * @param imv_id IMV ID assigned by TNCS | |
146 | * @param connection_id network connection ID assigned by TNCS | |
147 | * @param attribute_id attribute ID | |
148 | * @param buffer_len length of buffer in bytes | |
149 | * @param buffer buffer | |
150 | * @return TNC result code | |
151 | */ | |
152 | TNC_Result (*set_attribute)(TNC_IMVID imv_id, | |
153 | TNC_ConnectionID connection_id, | |
154 | TNC_AttributeID attribute_id, | |
155 | TNC_UInt32 buffer_len, | |
156 | TNC_BufferReference buffer); | |
ac3331e1 AS |
157 | |
158 | /** | |
159 | * Reserve an additional IMV ID | |
160 | * | |
161 | * @param imv_id primary IMV ID assigned by TNCS | |
162 | * @param out_imv_id additional IMV ID assigned by TNCS | |
163 | * @return TNC result code | |
164 | */ | |
165 | TNC_Result (*reserve_additional_id)(TNC_IMVID imv_id, | |
166 | TNC_UInt32 *out_imv_id); | |
167 | ||
510f37ab AS |
168 | }; |
169 | ||
170 | METHOD(imv_agent_t, bind_functions, TNC_Result, | |
171 | private_imv_agent_t *this, TNC_TNCS_BindFunctionPointer bind_function) | |
172 | { | |
173 | if (!bind_function) | |
174 | { | |
175 | DBG1(DBG_IMV, "TNC server failed to provide bind function"); | |
176 | return TNC_RESULT_INVALID_PARAMETER; | |
177 | } | |
178 | if (bind_function(this->id, "TNC_TNCS_ReportMessageTypes", | |
179 | (void**)&this->report_message_types) != TNC_RESULT_SUCCESS) | |
180 | { | |
181 | this->report_message_types = NULL; | |
182 | } | |
c87acaf5 AS |
183 | if (bind_function(this->id, "TNC_TNCS_ReportMessageTypesLong", |
184 | (void**)&this->report_message_types_long) != TNC_RESULT_SUCCESS) | |
185 | { | |
186 | this->report_message_types_long = NULL; | |
187 | } | |
510f37ab AS |
188 | if (bind_function(this->id, "TNC_TNCS_RequestHandshakeRetry", |
189 | (void**)&this->public.request_handshake_retry) != TNC_RESULT_SUCCESS) | |
190 | { | |
191 | this->public.request_handshake_retry = NULL; | |
192 | } | |
193 | if (bind_function(this->id, "TNC_TNCS_SendMessage", | |
6a61b795 | 194 | (void**)&this->public.send_message) != TNC_RESULT_SUCCESS) |
510f37ab | 195 | { |
6a61b795 | 196 | this->public.send_message = NULL; |
510f37ab | 197 | } |
ac3331e1 | 198 | if (bind_function(this->id, "TNC_TNCS_SendMessageLong", |
6a61b795 | 199 | (void**)&this->public.send_message_long) != TNC_RESULT_SUCCESS) |
ac3331e1 | 200 | { |
6a61b795 | 201 | this->public.send_message_long = NULL; |
ac3331e1 | 202 | } |
510f37ab AS |
203 | if (bind_function(this->id, "TNC_TNCS_ProvideRecommendation", |
204 | (void**)&this->provide_recommendation) != TNC_RESULT_SUCCESS) | |
205 | { | |
206 | this->provide_recommendation = NULL; | |
207 | } | |
208 | if (bind_function(this->id, "TNC_TNCS_GetAttribute", | |
44bd40a1 | 209 | (void**)&this->get_attribute) != TNC_RESULT_SUCCESS) |
510f37ab | 210 | { |
44bd40a1 | 211 | this->get_attribute = NULL; |
510f37ab AS |
212 | } |
213 | if (bind_function(this->id, "TNC_TNCS_SetAttribute", | |
44bd40a1 | 214 | (void**)&this->set_attribute) != TNC_RESULT_SUCCESS) |
510f37ab | 215 | { |
44bd40a1 | 216 | this->set_attribute = NULL; |
510f37ab | 217 | } |
ac3331e1 AS |
218 | if (bind_function(this->id, "TNC_TNCC_ReserveAdditionalIMVID", |
219 | (void**)&this->reserve_additional_id) != TNC_RESULT_SUCCESS) | |
220 | { | |
221 | this->reserve_additional_id = NULL; | |
222 | } | |
510f37ab | 223 | |
c87acaf5 | 224 | if (this->report_message_types_long) |
510f37ab | 225 | { |
5f085d7e AS |
226 | TNC_VendorIDList vendor_id_list; |
227 | TNC_MessageSubtypeList subtype_list; | |
228 | int i; | |
229 | ||
230 | vendor_id_list = malloc(this->type_count * sizeof(TNC_UInt32)); | |
231 | subtype_list = malloc(this->type_count * sizeof(TNC_UInt32)); | |
232 | ||
233 | for (i = 0; i < this->type_count; i++) | |
234 | { | |
235 | vendor_id_list[i] = this->supported_types[i].vendor_id; | |
236 | subtype_list[i] = this->supported_types[i].type; | |
237 | } | |
238 | this->report_message_types_long(this->id, vendor_id_list, subtype_list, | |
239 | this->type_count); | |
240 | free(vendor_id_list); | |
241 | free(subtype_list); | |
c87acaf5 | 242 | } |
5f085d7e | 243 | else if (this->report_message_types) |
c87acaf5 | 244 | { |
5f085d7e AS |
245 | TNC_MessageTypeList type_list; |
246 | int i; | |
247 | ||
248 | type_list = malloc(this->type_count * sizeof(TNC_UInt32)); | |
c87acaf5 | 249 | |
5f085d7e AS |
250 | for (i = 0; i < this->type_count; i++) |
251 | { | |
252 | type_list[i] = (this->supported_types[i].vendor_id << 8) | | |
253 | (this->supported_types[i].type & 0xff); | |
254 | } | |
255 | this->report_message_types(this->id, type_list, this->type_count); | |
256 | free(type_list); | |
510f37ab AS |
257 | } |
258 | return TNC_RESULT_SUCCESS; | |
259 | } | |
260 | ||
261 | /** | |
262 | * finds a connection state based on its Connection ID | |
263 | */ | |
264 | static imv_state_t* find_connection(private_imv_agent_t *this, | |
265 | TNC_ConnectionID id) | |
266 | { | |
267 | enumerator_t *enumerator; | |
268 | imv_state_t *state, *found = NULL; | |
269 | ||
270 | this->connection_lock->read_lock(this->connection_lock); | |
271 | enumerator = this->connections->create_enumerator(this->connections); | |
272 | while (enumerator->enumerate(enumerator, &state)) | |
273 | { | |
274 | if (id == state->get_connection_id(state)) | |
275 | { | |
276 | found = state; | |
277 | break; | |
278 | } | |
279 | } | |
280 | enumerator->destroy(enumerator); | |
281 | this->connection_lock->unlock(this->connection_lock); | |
282 | ||
283 | return found; | |
284 | } | |
285 | ||
286 | /** | |
287 | * delete a connection state with a given Connection ID | |
288 | */ | |
289 | static bool delete_connection(private_imv_agent_t *this, TNC_ConnectionID id) | |
290 | { | |
291 | enumerator_t *enumerator; | |
292 | imv_state_t *state; | |
b1da8368 | 293 | imv_session_t *session; |
510f37ab AS |
294 | bool found = FALSE; |
295 | ||
296 | this->connection_lock->write_lock(this->connection_lock); | |
297 | enumerator = this->connections->create_enumerator(this->connections); | |
298 | while (enumerator->enumerate(enumerator, &state)) | |
299 | { | |
300 | if (id == state->get_connection_id(state)) | |
301 | { | |
302 | found = TRUE; | |
b1da8368 | 303 | session = state->get_session(state); |
4894bfa2 | 304 | imcv_sessions->remove_session(imcv_sessions, session); |
510f37ab AS |
305 | state->destroy(state); |
306 | this->connections->remove_at(this->connections, enumerator); | |
307 | break; | |
308 | } | |
309 | } | |
310 | enumerator->destroy(enumerator); | |
311 | this->connection_lock->unlock(this->connection_lock); | |
312 | ||
313 | return found; | |
314 | } | |
315 | ||
1ab8dff7 AS |
316 | /** |
317 | * Read a boolean attribute | |
318 | */ | |
319 | static bool get_bool_attribute(private_imv_agent_t *this, TNC_ConnectionID id, | |
320 | TNC_AttributeID attribute_id) | |
321 | { | |
322 | TNC_UInt32 len; | |
323 | char buf[4]; | |
324 | ||
325 | return this->get_attribute && | |
326 | this->get_attribute(this->id, id, attribute_id, 4, buf, &len) == | |
327 | TNC_RESULT_SUCCESS && len == 1 && *buf == 0x01; | |
328 | } | |
329 | ||
330 | /** | |
331 | * Read a string attribute | |
332 | */ | |
333 | static char* get_str_attribute(private_imv_agent_t *this, TNC_ConnectionID id, | |
334 | TNC_AttributeID attribute_id) | |
335 | { | |
336 | TNC_UInt32 len; | |
337 | char buf[BUF_LEN]; | |
338 | ||
339 | if (this->get_attribute && | |
340 | this->get_attribute(this->id, id, attribute_id, BUF_LEN, buf, &len) == | |
341 | TNC_RESULT_SUCCESS && len <= BUF_LEN) | |
342 | { | |
343 | return strdup(buf); | |
344 | } | |
345 | return NULL; | |
346 | } | |
347 | ||
968c83cd AS |
348 | /** |
349 | * Read an UInt32 attribute | |
350 | */ | |
4894bfa2 | 351 | static uint32_t get_uint_attribute(private_imv_agent_t *this, TNC_ConnectionID id, |
968c83cd AS |
352 | TNC_AttributeID attribute_id) |
353 | { | |
354 | TNC_UInt32 len; | |
355 | char buf[4]; | |
356 | ||
357 | if (this->get_attribute && | |
358 | this->get_attribute(this->id, id, attribute_id, 4, buf, &len) == | |
359 | TNC_RESULT_SUCCESS && len == 4) | |
360 | { | |
361 | return untoh32(buf); | |
362 | } | |
363 | return 0; | |
364 | } | |
365 | ||
bd1ee5bd AS |
366 | /** |
367 | * Read a TNC identity attribute | |
368 | */ | |
369 | static linked_list_t* get_identity_attribute(private_imv_agent_t *this, | |
370 | TNC_ConnectionID id, | |
371 | TNC_AttributeID attribute_id) | |
372 | { | |
373 | TNC_UInt32 len; | |
374 | char buf[2048]; | |
4894bfa2 | 375 | uint32_t count; |
bd1ee5bd AS |
376 | tncif_identity_t *tnc_id; |
377 | bio_reader_t *reader; | |
378 | linked_list_t *list; | |
379 | ||
380 | list = linked_list_create(); | |
381 | ||
382 | if (!this->get_attribute || | |
383 | this->get_attribute(this->id, id, attribute_id, sizeof(buf), buf, &len) | |
384 | != TNC_RESULT_SUCCESS || len > sizeof(buf)) | |
385 | { | |
386 | return list; | |
387 | } | |
388 | ||
389 | reader = bio_reader_create(chunk_create(buf, len)); | |
390 | if (!reader->read_uint32(reader, &count)) | |
391 | { | |
392 | goto end; | |
393 | } | |
394 | while (count--) | |
395 | { | |
396 | tnc_id = tncif_identity_create_empty(); | |
397 | if (!tnc_id->process(tnc_id, reader)) | |
398 | { | |
399 | tnc_id->destroy(tnc_id); | |
400 | goto end; | |
401 | } | |
402 | list->insert_last(list, tnc_id); | |
403 | } | |
404 | ||
405 | end: | |
406 | reader->destroy(reader); | |
407 | return list; | |
408 | } | |
409 | ||
510f37ab AS |
410 | METHOD(imv_agent_t, create_state, TNC_Result, |
411 | private_imv_agent_t *this, imv_state_t *state) | |
412 | { | |
1ab8dff7 AS |
413 | TNC_ConnectionID conn_id; |
414 | char *tnccs_p = NULL, *tnccs_v = NULL, *t_p = NULL, *t_v = NULL; | |
00cd79b6 | 415 | bool has_long = FALSE, has_excl = FALSE, has_soh = FALSE; |
bd1ee5bd | 416 | linked_list_t *ar_identities; |
a6266485 | 417 | imv_session_t *session; |
4894bfa2 | 418 | uint32_t max_msg_len; |
510f37ab | 419 | |
1ab8dff7 AS |
420 | conn_id = state->get_connection_id(state); |
421 | if (find_connection(this, conn_id)) | |
510f37ab AS |
422 | { |
423 | DBG1(DBG_IMV, "IMV %u \"%s\" already created a state for Connection ID %u", | |
1ab8dff7 | 424 | this->id, this->name, conn_id); |
510f37ab AS |
425 | state->destroy(state); |
426 | return TNC_RESULT_OTHER; | |
427 | } | |
1ab8dff7 AS |
428 | |
429 | /* Get and display attributes from TNCS via IF-IMV */ | |
00cd79b6 AS |
430 | has_long = get_bool_attribute(this, conn_id, |
431 | TNC_ATTRIBUTEID_HAS_LONG_TYPES); | |
432 | has_excl = get_bool_attribute(this, conn_id, | |
433 | TNC_ATTRIBUTEID_HAS_EXCLUSIVE); | |
434 | has_soh = get_bool_attribute(this, conn_id, | |
435 | TNC_ATTRIBUTEID_HAS_SOH); | |
436 | tnccs_p = get_str_attribute(this, conn_id, | |
437 | TNC_ATTRIBUTEID_IFTNCCS_PROTOCOL); | |
438 | tnccs_v = get_str_attribute(this, conn_id, | |
439 | TNC_ATTRIBUTEID_IFTNCCS_VERSION); | |
440 | t_p = get_str_attribute(this, conn_id, | |
441 | TNC_ATTRIBUTEID_IFT_PROTOCOL); | |
442 | t_v = get_str_attribute(this, conn_id, | |
443 | TNC_ATTRIBUTEID_IFT_VERSION); | |
444 | max_msg_len = get_uint_attribute(this, conn_id, | |
445 | TNC_ATTRIBUTEID_MAX_MESSAGE_SIZE); | |
446 | ar_identities = get_identity_attribute(this, conn_id, | |
447 | TNC_ATTRIBUTEID_AR_IDENTITIES); | |
1ab8dff7 | 448 | |
e4e291d4 | 449 | state->set_flags(state, has_long, has_excl); |
968c83cd AS |
450 | state->set_max_msg_len(state, max_msg_len); |
451 | ||
452 | DBG2(DBG_IMV, "IMV %u \"%s\" created a state for %s %s Connection ID %u: " | |
453 | "%slong %sexcl %ssoh", this->id, this->name, | |
454 | tnccs_p ? tnccs_p:"?", tnccs_v ? tnccs_v:"?", conn_id, | |
455 | has_long ? "+":"-", has_excl ? "+":"-", has_soh ? "+":"-"); | |
8ef43d87 | 456 | DBG2(DBG_IMV, " over %s %s with maximum PA-TNC message size of %u bytes", |
968c83cd | 457 | t_p ? t_p:"?", t_v ? t_v :"?", max_msg_len); |
e4e291d4 | 458 | |
00cd79b6 | 459 | session = imcv_sessions->add_session(imcv_sessions, conn_id, ar_identities); |
4894bfa2 AS |
460 | state->set_session(state, session); |
461 | ||
1ab8dff7 AS |
462 | free(tnccs_p); |
463 | free(tnccs_v); | |
464 | free(t_p); | |
465 | free(t_v); | |
466 | ||
4894bfa2 | 467 | /* insert state in connection list */ |
510f37ab AS |
468 | this->connection_lock->write_lock(this->connection_lock); |
469 | this->connections->insert_last(this->connections, state); | |
470 | this->connection_lock->unlock(this->connection_lock); | |
4894bfa2 | 471 | |
510f37ab AS |
472 | return TNC_RESULT_SUCCESS; |
473 | } | |
474 | ||
475 | METHOD(imv_agent_t, delete_state, TNC_Result, | |
476 | private_imv_agent_t *this, TNC_ConnectionID connection_id) | |
477 | { | |
478 | if (!delete_connection(this, connection_id)) | |
479 | { | |
480 | DBG1(DBG_IMV, "IMV %u \"%s\" has no state for Connection ID %u", | |
481 | this->id, this->name, connection_id); | |
482 | return TNC_RESULT_FATAL; | |
483 | } | |
484 | DBG2(DBG_IMV, "IMV %u \"%s\" deleted the state of Connection ID %u", | |
485 | this->id, this->name, connection_id); | |
486 | return TNC_RESULT_SUCCESS; | |
487 | } | |
488 | ||
489 | METHOD(imv_agent_t, change_state, TNC_Result, | |
490 | private_imv_agent_t *this, TNC_ConnectionID connection_id, | |
74012695 AS |
491 | TNC_ConnectionState new_state, |
492 | imv_state_t **state_p) | |
510f37ab AS |
493 | { |
494 | imv_state_t *state; | |
495 | ||
496 | switch (new_state) | |
497 | { | |
498 | case TNC_CONNECTION_STATE_HANDSHAKE: | |
499 | case TNC_CONNECTION_STATE_ACCESS_ALLOWED: | |
500 | case TNC_CONNECTION_STATE_ACCESS_ISOLATED: | |
501 | case TNC_CONNECTION_STATE_ACCESS_NONE: | |
502 | state = find_connection(this, connection_id); | |
503 | if (!state) | |
504 | { | |
505 | DBG1(DBG_IMV, "IMV %u \"%s\" has no state for Connection ID %u", | |
506 | this->id, this->name, connection_id); | |
507 | return TNC_RESULT_FATAL; | |
508 | } | |
509 | state->change_state(state, new_state); | |
510 | DBG2(DBG_IMV, "IMV %u \"%s\" changed state of Connection ID %u to '%N'", | |
511 | this->id, this->name, connection_id, | |
512 | TNC_Connection_State_names, new_state); | |
74012695 AS |
513 | if (state_p) |
514 | { | |
515 | *state_p = state; | |
516 | } | |
510f37ab AS |
517 | break; |
518 | case TNC_CONNECTION_STATE_CREATE: | |
519 | DBG1(DBG_IMV, "state '%N' should be handled by create_state()", | |
520 | TNC_Connection_State_names, new_state); | |
521 | return TNC_RESULT_FATAL; | |
522 | case TNC_CONNECTION_STATE_DELETE: | |
523 | DBG1(DBG_IMV, "state '%N' should be handled by delete_state()", | |
524 | TNC_Connection_State_names, new_state); | |
525 | return TNC_RESULT_FATAL; | |
526 | default: | |
527 | DBG1(DBG_IMV, "IMV %u \"%s\" was notified of unknown state %u " | |
528 | "for Connection ID %u", | |
529 | this->id, this->name, new_state, connection_id); | |
a05f3b20 | 530 | return TNC_RESULT_INVALID_PARAMETER; |
510f37ab AS |
531 | } |
532 | return TNC_RESULT_SUCCESS; | |
533 | } | |
534 | ||
535 | METHOD(imv_agent_t, get_state, bool, | |
536 | private_imv_agent_t *this, TNC_ConnectionID connection_id, | |
537 | imv_state_t **state) | |
538 | { | |
539 | *state = find_connection(this, connection_id); | |
540 | if (!*state) | |
541 | { | |
542 | DBG1(DBG_IMV, "IMV %u \"%s\" has no state for Connection ID %u", | |
543 | this->id, this->name, connection_id); | |
544 | return FALSE; | |
545 | } | |
546 | return TRUE; | |
547 | } | |
548 | ||
6a61b795 AS |
549 | METHOD(imv_agent_t, get_name, const char*, |
550 | private_imv_agent_t *this) | |
551 | { | |
552 | return this->name; | |
553 | } | |
554 | ||
555 | METHOD(imv_agent_t, get_id, TNC_IMVID, | |
556 | private_imv_agent_t *this) | |
557 | { | |
558 | return this->id; | |
559 | } | |
560 | ||
cbf2ba54 AS |
561 | METHOD(imv_agent_t, reserve_additional_ids, TNC_Result, |
562 | private_imv_agent_t *this, int count) | |
ac3331e1 | 563 | { |
cbf2ba54 AS |
564 | TNC_Result result; |
565 | TNC_UInt32 id; | |
566 | void *pointer; | |
567 | ||
ac3331e1 AS |
568 | if (!this->reserve_additional_id) |
569 | { | |
cbf2ba54 AS |
570 | DBG1(DBG_IMV, "IMV %u \"%s\" did not detect the capability to reserve " |
571 | "additional IMV IDs from the TNCS", this->id, this->name); | |
ac3331e1 AS |
572 | return TNC_RESULT_ILLEGAL_OPERATION; |
573 | } | |
cbf2ba54 AS |
574 | while (count > 0) |
575 | { | |
576 | result = this->reserve_additional_id(this->id, &id); | |
577 | if (result != TNC_RESULT_SUCCESS) | |
578 | { | |
579 | DBG1(DBG_IMV, "IMV %u \"%s\" failed to reserve %d additional IMV IDs", | |
580 | this->id, this->name, count); | |
581 | return result; | |
582 | } | |
583 | count--; | |
584 | ||
585 | /* store the scalar value in the pointer */ | |
6b98c002 | 586 | pointer = (void*)(uintptr_t)id; |
cbf2ba54 AS |
587 | this->additional_ids->insert_last(this->additional_ids, pointer); |
588 | DBG2(DBG_IMV, "IMV %u \"%s\" reserved additional ID %u", | |
589 | this->id, this->name, id); | |
590 | } | |
591 | return TNC_RESULT_SUCCESS; | |
592 | } | |
593 | ||
594 | METHOD(imv_agent_t, count_additional_ids, int, | |
595 | private_imv_agent_t *this) | |
596 | { | |
597 | return this->additional_ids->get_count(this->additional_ids); | |
598 | } | |
599 | ||
600 | METHOD(imv_agent_t, create_id_enumerator, enumerator_t*, | |
601 | private_imv_agent_t *this) | |
602 | { | |
603 | return this->additional_ids->create_enumerator(this->additional_ids); | |
ac3331e1 AS |
604 | } |
605 | ||
db15c6da AS |
606 | typedef struct { |
607 | /** | |
608 | * implements enumerator_t | |
609 | */ | |
610 | enumerator_t public; | |
611 | ||
612 | /** | |
613 | * language length | |
614 | */ | |
615 | TNC_UInt32 lang_len; | |
616 | ||
617 | /** | |
618 | * language buffer | |
619 | */ | |
620 | char lang_buf[BUF_LEN]; | |
621 | ||
622 | /** | |
623 | * position pointer into language buffer | |
624 | */ | |
625 | char *lang_pos; | |
626 | ||
627 | } language_enumerator_t; | |
628 | ||
629 | /** | |
630 | * Implementation of language_enumerator.destroy. | |
631 | */ | |
632 | static void language_enumerator_destroy(language_enumerator_t *this) | |
633 | { | |
634 | free(this); | |
635 | } | |
636 | ||
637 | /** | |
638 | * Implementation of language_enumerator.enumerate | |
639 | */ | |
640 | static bool language_enumerator_enumerate(language_enumerator_t *this, ...) | |
641 | { | |
642 | char *pos, *cur_lang, **lang; | |
643 | TNC_UInt32 len; | |
644 | va_list args; | |
645 | ||
646 | if (!this->lang_len) | |
647 | { | |
648 | return FALSE; | |
649 | } | |
650 | cur_lang = this->lang_pos; | |
651 | pos = strchr(this->lang_pos, ','); | |
652 | if (pos) | |
653 | { | |
654 | len = pos - this->lang_pos; | |
655 | this->lang_pos += len + 1, | |
656 | this->lang_len -= len + 1; | |
657 | } | |
658 | else | |
659 | { | |
db15c6da | 660 | len = this->lang_len; |
af83700f | 661 | pos = this->lang_pos + len; |
db15c6da AS |
662 | this->lang_pos = NULL; |
663 | this->lang_len = 0; | |
664 | } | |
665 | ||
666 | /* remove preceding whitespace */ | |
667 | while (*cur_lang == ' ' && len--) | |
668 | { | |
669 | cur_lang++; | |
670 | } | |
671 | ||
672 | /* remove trailing whitespace */ | |
673 | while (len && *(--pos) == ' ') | |
674 | { | |
675 | len--; | |
676 | } | |
677 | cur_lang[len] = '\0'; | |
db15c6da AS |
678 | |
679 | va_start(args, this); | |
680 | lang = va_arg(args, char**); | |
681 | *lang = cur_lang; | |
682 | va_end(args); | |
683 | ||
684 | return TRUE; | |
685 | } | |
686 | ||
687 | METHOD(imv_agent_t, create_language_enumerator, enumerator_t*, | |
688 | private_imv_agent_t *this, imv_state_t *state) | |
689 | { | |
690 | language_enumerator_t *e; | |
691 | ||
692 | /* Create a language enumerator instance */ | |
693 | e = malloc_thing(language_enumerator_t); | |
af83700f AS |
694 | e->public.enumerate = (void*)language_enumerator_enumerate; |
695 | e->public.destroy = (void*)language_enumerator_destroy; | |
db15c6da | 696 | |
af83700f | 697 | if (!this->get_attribute || |
db15c6da AS |
698 | !this->get_attribute(this->id, state->get_connection_id(state), |
699 | TNC_ATTRIBUTEID_PREFERRED_LANGUAGE, BUF_LEN, | |
700 | e->lang_buf, &e->lang_len) == TNC_RESULT_SUCCESS || | |
701 | e->lang_len >= BUF_LEN) | |
702 | { | |
af83700f | 703 | e->lang_len = 0; |
db15c6da | 704 | } |
db15c6da AS |
705 | e->lang_buf[e->lang_len] = '\0'; |
706 | e->lang_pos = e->lang_buf; | |
707 | ||
708 | return (enumerator_t*)e; | |
709 | } | |
710 | ||
711 | METHOD(imv_agent_t, provide_recommendation, TNC_Result, | |
712 | private_imv_agent_t *this, imv_state_t *state) | |
713 | { | |
714 | TNC_IMV_Action_Recommendation rec; | |
715 | TNC_IMV_Evaluation_Result eval; | |
716 | TNC_ConnectionID connection_id; | |
ee6aeca8 AS |
717 | chunk_t reason_string; |
718 | char *reason_lang; | |
db15c6da AS |
719 | enumerator_t *e; |
720 | ||
721 | state->get_recommendation(state, &rec, &eval); | |
722 | connection_id = state->get_connection_id(state); | |
723 | ||
724 | /* send a reason string if action recommendation is not allow */ | |
725 | if (rec != TNC_IMV_ACTION_RECOMMENDATION_ALLOW) | |
726 | { | |
727 | /* find a reason string for the preferred language and set it */ | |
728 | if (this->set_attribute) | |
729 | { | |
730 | e = create_language_enumerator(this, state); | |
731 | if (state->get_reason_string(state, e, &reason_string, &reason_lang)) | |
732 | { | |
733 | this->set_attribute(this->id, connection_id, | |
734 | TNC_ATTRIBUTEID_REASON_STRING, | |
ee6aeca8 | 735 | reason_string.len, reason_string.ptr); |
db15c6da AS |
736 | this->set_attribute(this->id, connection_id, |
737 | TNC_ATTRIBUTEID_REASON_LANGUAGE, | |
738 | strlen(reason_lang), reason_lang); | |
739 | } | |
740 | e->destroy(e); | |
741 | } | |
742 | } | |
743 | return this->provide_recommendation(this->id, connection_id, rec, eval); | |
744 | } | |
745 | ||
38b5f527 AS |
746 | METHOD(imv_agent_t, add_non_fatal_attr_type, void, |
747 | private_imv_agent_t *this, pen_type_t type) | |
748 | { | |
749 | pen_type_t *type_p; | |
750 | ||
751 | type_p = malloc_thing(pen_type_t); | |
752 | *type_p = type; | |
753 | this->non_fatal_attr_types->insert_last(this->non_fatal_attr_types, type_p); | |
754 | } | |
755 | ||
756 | METHOD(imv_agent_t, get_non_fatal_attr_types, linked_list_t*, | |
757 | private_imv_agent_t *this) | |
758 | { | |
759 | return this->non_fatal_attr_types; | |
760 | } | |
761 | ||
510f37ab AS |
762 | METHOD(imv_agent_t, destroy, void, |
763 | private_imv_agent_t *this) | |
764 | { | |
54b622c3 | 765 | DBG1(DBG_IMV, "IMV %u \"%s\" terminated", this->id, this->name); |
cbf2ba54 | 766 | this->additional_ids->destroy(this->additional_ids); |
38b5f527 AS |
767 | this->non_fatal_attr_types->destroy_function(this->non_fatal_attr_types, |
768 | free); | |
510f37ab AS |
769 | this->connections->destroy_offset(this->connections, |
770 | offsetof(imv_state_t, destroy)); | |
771 | this->connection_lock->destroy(this->connection_lock); | |
772 | free(this); | |
d4c8fe3c AS |
773 | |
774 | /* decrease the reference count or terminate */ | |
775 | libimcv_deinit(); | |
510f37ab AS |
776 | } |
777 | ||
778 | /** | |
779 | * Described in header. | |
780 | */ | |
781 | imv_agent_t *imv_agent_create(const char *name, | |
4894bfa2 | 782 | pen_type_t *supported_types, uint32_t type_count, |
510f37ab AS |
783 | TNC_IMVID id, TNC_Version *actual_version) |
784 | { | |
785 | private_imv_agent_t *this; | |
786 | ||
d4c8fe3c | 787 | /* initialize or increase the reference count */ |
a6266485 | 788 | if (!libimcv_init(TRUE)) |
d4c8fe3c AS |
789 | { |
790 | return NULL; | |
791 | } | |
792 | ||
510f37ab AS |
793 | INIT(this, |
794 | .public = { | |
795 | .bind_functions = _bind_functions, | |
796 | .create_state = _create_state, | |
797 | .delete_state = _delete_state, | |
798 | .change_state = _change_state, | |
799 | .get_state = _get_state, | |
6a61b795 AS |
800 | .get_name = _get_name, |
801 | .get_id = _get_id, | |
cbf2ba54 AS |
802 | .reserve_additional_ids = _reserve_additional_ids, |
803 | .count_additional_ids = _count_additional_ids, | |
804 | .create_id_enumerator = _create_id_enumerator, | |
db15c6da AS |
805 | .create_language_enumerator = _create_language_enumerator, |
806 | .provide_recommendation = _provide_recommendation, | |
38b5f527 AS |
807 | .add_non_fatal_attr_type = _add_non_fatal_attr_type, |
808 | .get_non_fatal_attr_types = _get_non_fatal_attr_types, | |
510f37ab AS |
809 | .destroy = _destroy, |
810 | }, | |
811 | .name = name, | |
5f085d7e AS |
812 | .supported_types = supported_types, |
813 | .type_count = type_count, | |
510f37ab | 814 | .id = id, |
cbf2ba54 | 815 | .additional_ids = linked_list_create(), |
38b5f527 | 816 | .non_fatal_attr_types = linked_list_create(), |
510f37ab AS |
817 | .connections = linked_list_create(), |
818 | .connection_lock = rwlock_create(RWLOCK_TYPE_DEFAULT), | |
819 | ); | |
d4c8fe3c | 820 | |
510f37ab AS |
821 | *actual_version = TNC_IFIMV_VERSION_1; |
822 | DBG1(DBG_IMV, "IMV %u \"%s\" initialized", this->id, this->name); | |
823 | ||
824 | return &this->public; | |
825 | } |