implemented IETF String Version attribute
[strongswan.git] / src / libimcv / plugins / imv_os / imv_os.c
1 /*
2 * Copyright (C) 2012 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 "imv_os_state.h"
17
18 #include <imv/imv_agent.h>
19 #include <pa_tnc/pa_tnc_msg.h>
20 #include <ietf/ietf_attr.h>
21 #include <ietf/ietf_attr_attr_request.h>
22 #include <ietf/ietf_attr_installed_packages.h>
23 #include <ietf/ietf_attr_pa_tnc_error.h>
24 #include <ietf/ietf_attr_product_info.h>
25 #include <ietf/ietf_attr_string_version.h>
26
27 #include <tncif_names.h>
28 #include <tncif_pa_subtypes.h>
29
30 #include <pen/pen.h>
31 #include <utils/linked_list.h>
32 #include <debug.h>
33
34 /* IMV definitions */
35
36 static const char imv_name[] = "OS";
37
38 #define IMV_VENDOR_ID PEN_IETF
39 #define IMV_SUBTYPE PA_SUBTYPE_IETF_OPERATING_SYSTEM
40
41 static imv_agent_t *imv_os;
42
43 /*
44 * see section 3.8.1 of TCG TNC IF-IMV Specification 1.3
45 */
46 TNC_Result TNC_IMV_Initialize(TNC_IMVID imv_id,
47 TNC_Version min_version,
48 TNC_Version max_version,
49 TNC_Version *actual_version)
50 {
51 if (imv_os)
52 {
53 DBG1(DBG_IMV, "IMV \"%s\" has already been initialized", imv_name);
54 return TNC_RESULT_ALREADY_INITIALIZED;
55 }
56 imv_os = imv_agent_create(imv_name, IMV_VENDOR_ID, IMV_SUBTYPE,
57 imv_id, actual_version);
58 if (!imv_os)
59 {
60 return TNC_RESULT_FATAL;
61 }
62 if (min_version > TNC_IFIMV_VERSION_1 || max_version < TNC_IFIMV_VERSION_1)
63 {
64 DBG1(DBG_IMV, "no common IF-IMV version");
65 return TNC_RESULT_NO_COMMON_VERSION;
66 }
67
68 return TNC_RESULT_SUCCESS;
69 }
70
71 /**
72 * see section 3.8.2 of TCG TNC IF-IMV Specification 1.3
73 */
74 TNC_Result TNC_IMV_NotifyConnectionChange(TNC_IMVID imv_id,
75 TNC_ConnectionID connection_id,
76 TNC_ConnectionState new_state)
77 {
78 imv_state_t *state;
79
80 if (!imv_os)
81 {
82 DBG1(DBG_IMV, "IMV \"%s\" has not been initialized", imv_name);
83 return TNC_RESULT_NOT_INITIALIZED;
84 }
85 switch (new_state)
86 {
87 case TNC_CONNECTION_STATE_CREATE:
88 state = imv_os_state_create(connection_id);
89 return imv_os->create_state(imv_os, state);
90 case TNC_CONNECTION_STATE_DELETE:
91 return imv_os->delete_state(imv_os, connection_id);
92 default:
93 return imv_os->change_state(imv_os, connection_id,
94 new_state, NULL);
95 }
96 }
97
98 static TNC_Result receive_message(TNC_IMVID imv_id,
99 TNC_ConnectionID connection_id,
100 TNC_UInt32 msg_flags,
101 chunk_t msg,
102 TNC_VendorID msg_vid,
103 TNC_MessageSubtype msg_subtype,
104 TNC_UInt32 src_imc_id,
105 TNC_UInt32 dst_imv_id)
106 {
107 pa_tnc_msg_t *pa_tnc_msg;
108 pa_tnc_attr_t *attr;
109 pen_type_t type;
110 linked_list_t *attr_list;
111 imv_state_t *state;
112 imv_os_state_t *os_state;
113 enumerator_t *enumerator;
114 TNC_Result result;
115 char *os_name = NULL;
116 chunk_t os_version = chunk_empty;
117 bool fatal_error, assessment = FALSE;
118
119 if (!imv_os)
120 {
121 DBG1(DBG_IMV, "IMV \"%s\" has not been initialized", imv_name);
122 return TNC_RESULT_NOT_INITIALIZED;
123 }
124
125 /* get current IMV state */
126 if (!imv_os->get_state(imv_os, connection_id, &state))
127 {
128 return TNC_RESULT_FATAL;
129 }
130 os_state = (imv_os_state_t*)state;
131
132 /* parse received PA-TNC message and automatically handle any errors */
133 result = imv_os->receive_message(imv_os, state, msg, msg_vid,
134 msg_subtype, src_imc_id, dst_imv_id, &pa_tnc_msg);
135
136 /* no parsed PA-TNC attributes available if an error occurred */
137 if (!pa_tnc_msg)
138 {
139 return result;
140 }
141
142 /* preprocess any IETF standard error attributes */
143 fatal_error = pa_tnc_msg->process_ietf_std_errors(pa_tnc_msg);
144
145 /* analyze PA-TNC attributes */
146 attr_list = linked_list_create();
147 enumerator = pa_tnc_msg->create_attribute_enumerator(pa_tnc_msg);
148 while (enumerator->enumerate(enumerator, &attr))
149 {
150 type = attr->get_type(attr);
151
152 if (type.vendor_id != PEN_IETF)
153 {
154 continue;
155 }
156 switch (type.type)
157 {
158 case IETF_ATTR_PRODUCT_INFORMATION:
159 {
160 ietf_attr_product_info_t *attr_cast;
161
162 attr_cast = (ietf_attr_product_info_t*)attr;
163 os_name = attr_cast->get_info(attr_cast, NULL, NULL);
164 DBG1(DBG_IMV, "operating system name is '%s'", os_name);
165 break;
166 }
167 case IETF_ATTR_STRING_VERSION:
168 {
169 ietf_attr_string_version_t *attr_cast;
170
171 attr_cast = (ietf_attr_string_version_t*)attr;
172 os_version = attr_cast->get_version(attr_cast, NULL, NULL);
173 if (os_version.len)
174 {
175 DBG1(DBG_IMV, "operating system version is '%.*s'",
176 os_version.len, os_version.ptr);
177 }
178 break;
179 }
180 case IETF_ATTR_INSTALLED_PACKAGES:
181 {
182 ietf_attr_installed_packages_t *attr_cast;
183 enumerator_t *e;
184 chunk_t name, version;
185
186 attr_cast = (ietf_attr_installed_packages_t*)attr;
187 e = attr_cast->create_enumerator(attr_cast);
188 while (e->enumerate(e, &name, &version))
189 {
190 DBG1(DBG_IMV, "package '%.*s' %.*s", name.len, name.ptr,
191 version.len, version.ptr);
192 }
193 e->destroy(e);
194
195 state->set_recommendation(state,
196 TNC_IMV_ACTION_RECOMMENDATION_ALLOW,
197 TNC_IMV_EVALUATION_RESULT_COMPLIANT);
198 assessment = TRUE;
199 break;
200 }
201 default:
202 break;
203 }
204 }
205 enumerator->destroy(enumerator);
206
207 if (os_name && os_version.len)
208 {
209 os_state->set_info(os_state, os_name, os_version);
210
211 DBG1(DBG_IMV, "requesting installed packages for '%s'",
212 os_state->get_info(os_state));
213 attr = ietf_attr_attr_request_create(PEN_IETF,
214 IETF_ATTR_INSTALLED_PACKAGES);
215 attr_list->insert_last(attr_list, attr);
216 }
217 pa_tnc_msg->destroy(pa_tnc_msg);
218
219 if (fatal_error)
220 {
221 state->set_recommendation(state,
222 TNC_IMV_ACTION_RECOMMENDATION_NO_RECOMMENDATION,
223 TNC_IMV_EVALUATION_RESULT_ERROR);
224 assessment = TRUE;
225 }
226
227 if (assessment)
228 {
229 attr_list->destroy_offset(attr_list, offsetof(pa_tnc_attr_t, destroy));
230 return imv_os->provide_recommendation(imv_os, connection_id, src_imc_id);
231 }
232
233 result = imv_os->send_message(imv_os, connection_id, TRUE, imv_id,
234 src_imc_id, attr_list);
235 attr_list->destroy(attr_list);
236
237 return result;
238 }
239
240 /**
241 * see section 3.8.4 of TCG TNC IF-IMV Specification 1.3
242 */
243 TNC_Result TNC_IMV_ReceiveMessage(TNC_IMVID imv_id,
244 TNC_ConnectionID connection_id,
245 TNC_BufferReference msg,
246 TNC_UInt32 msg_len,
247 TNC_MessageType msg_type)
248 {
249 TNC_VendorID msg_vid;
250 TNC_MessageSubtype msg_subtype;
251
252 msg_vid = msg_type >> 8;
253 msg_subtype = msg_type & TNC_SUBTYPE_ANY;
254
255 return receive_message(imv_id, connection_id, 0, chunk_create(msg, msg_len),
256 msg_vid, msg_subtype, 0, TNC_IMVID_ANY);
257 }
258
259 /**
260 * see section 3.8.6 of TCG TNC IF-IMV Specification 1.3
261 */
262 TNC_Result TNC_IMV_ReceiveMessageLong(TNC_IMVID imv_id,
263 TNC_ConnectionID connection_id,
264 TNC_UInt32 msg_flags,
265 TNC_BufferReference msg,
266 TNC_UInt32 msg_len,
267 TNC_VendorID msg_vid,
268 TNC_MessageSubtype msg_subtype,
269 TNC_UInt32 src_imc_id,
270 TNC_UInt32 dst_imv_id)
271 {
272 return receive_message(imv_id, connection_id, msg_flags,
273 chunk_create(msg, msg_len), msg_vid, msg_subtype,
274 src_imc_id, dst_imv_id);
275 }
276
277 /**
278 * see section 3.8.7 of TCG TNC IF-IMV Specification 1.3
279 */
280 TNC_Result TNC_IMV_SolicitRecommendation(TNC_IMVID imv_id,
281 TNC_ConnectionID connection_id)
282 {
283 if (!imv_os)
284 {
285 DBG1(DBG_IMV, "IMV \"%s\" has not been initialized", imv_name);
286 return TNC_RESULT_NOT_INITIALIZED;
287 }
288 return imv_os->provide_recommendation(imv_os, connection_id,
289 TNC_IMCID_ANY);
290 }
291
292 /**
293 * see section 3.8.8 of TCG TNC IF-IMV Specification 1.3
294 */
295 TNC_Result TNC_IMV_BatchEnding(TNC_IMVID imv_id,
296 TNC_ConnectionID connection_id)
297 {
298 imv_state_t *state;
299 imv_os_state_t *os_state;
300 TNC_Result result = TNC_RESULT_SUCCESS;
301
302 if (!imv_os)
303 {
304 DBG1(DBG_IMV, "IMV \"%s\" has not been initialized", imv_name);
305 return TNC_RESULT_NOT_INITIALIZED;
306 }
307
308 /* get current IMV state */
309 if (!imv_os->get_state(imv_os, connection_id, &state))
310 {
311 return TNC_RESULT_FATAL;
312 }
313 os_state = (imv_os_state_t*)state;
314
315 if (os_state->get_info(os_state) == NULL)
316 {
317 pa_tnc_attr_t *attr;
318 linked_list_t *attr_list;
319 ietf_attr_attr_request_t *attr_cast;
320
321 attr_list = linked_list_create();
322 attr = ietf_attr_attr_request_create(PEN_IETF,
323 IETF_ATTR_PRODUCT_INFORMATION);
324 attr_cast = (ietf_attr_attr_request_t*)attr;
325 attr_cast->add(attr_cast, PEN_IETF, IETF_ATTR_STRING_VERSION);
326 attr_list->insert_last(attr_list, attr);
327 result = imv_os->send_message(imv_os, connection_id, FALSE, imv_id,
328 TNC_IMCID_ANY, attr_list);
329 attr_list->destroy(attr_list);
330 }
331
332 return result;
333 }
334
335 /**
336 * see section 3.8.9 of TCG TNC IF-IMV Specification 1.3
337 */
338 TNC_Result TNC_IMV_Terminate(TNC_IMVID imv_id)
339 {
340 if (!imv_os)
341 {
342 DBG1(DBG_IMV, "IMV \"%s\" has not been initialized", imv_name);
343 return TNC_RESULT_NOT_INITIALIZED;
344 }
345 imv_os->destroy(imv_os);
346 imv_os = NULL;
347
348 return TNC_RESULT_SUCCESS;
349 }
350
351 /**
352 * see section 4.2.8.1 of TCG TNC IF-IMV Specification 1.3
353 */
354 TNC_Result TNC_IMV_ProvideBindFunction(TNC_IMVID imv_id,
355 TNC_TNCS_BindFunctionPointer bind_function)
356 {
357 if (!imv_os)
358 {
359 DBG1(DBG_IMV, "IMV \"%s\" has not been initialized", imv_name);
360 return TNC_RESULT_NOT_INITIALIZED;
361 }
362 return imv_os->bind_functions(imv_os, bind_function);
363 }