generate reason strings and remediation instructions for improper OS settings
[strongswan.git] / src / libimcv / plugins / imv_os / imv_os_state.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 <utils/debug.h>
19 #include <collections/linked_list.h>
20
21 typedef struct private_imv_os_state_t private_imv_os_state_t;
22 typedef struct package_entry_t package_entry_t;
23 typedef struct entry_t entry_t;
24 typedef struct instruction_entry_t instruction_entry_t;
25
26 /**
27 * Private data of an imv_os_state_t object.
28 */
29 struct private_imv_os_state_t {
30
31 /**
32 * Public members of imv_os_state_t
33 */
34 imv_os_state_t public;
35
36 /**
37 * TNCCS connection ID
38 */
39 TNC_ConnectionID connection_id;
40
41 /**
42 * TNCCS connection state
43 */
44 TNC_ConnectionState state;
45
46 /**
47 * Does the TNCCS connection support long message types?
48 */
49 bool has_long;
50
51 /**
52 * Does the TNCCS connection support exclusive delivery?
53 */
54 bool has_excl;
55
56 /**
57 * Maximum PA-TNC message size for this TNCCS connection
58 */
59 u_int32_t max_msg_len;
60
61 /**
62 * IMV action recommendation
63 */
64 TNC_IMV_Action_Recommendation rec;
65
66 /**
67 * IMV evaluation result
68 */
69 TNC_IMV_Evaluation_Result eval;
70
71 /**
72 * OS Product Information (concatenation of OS Name and Version)
73 */
74 char *info;
75
76 /**
77 * OS Type
78 */
79 os_type_t type;
80
81 /**
82 * OS Name
83 */
84 chunk_t name;
85
86 /**
87 * OS Version
88 */
89 chunk_t version;
90
91 /**
92 * List of vulnerable or blacklisted packages
93 */
94 linked_list_t *bad_packages;
95
96 /**
97 * Local copy of the reason string
98 */
99 char *reasons;
100
101 /**
102 * Local copy of the remediation instruction string
103 */
104 char *instructions;
105
106 /**
107 * Number of processed packages
108 */
109 int count;
110
111 /**
112 * Number of not updated packages
113 */
114 int count_update;
115
116 /**
117 * Number of blacklisted packages
118 */
119 int count_blacklist;
120
121 /**
122 * Number of whitelisted packages
123 */
124 int count_ok;
125
126 /**
127 * OS Installed Package request sent - mandatory response expected
128 */
129 bool package_request;
130
131 /**
132 * OS Settings
133 */
134 u_int os_settings;
135
136 /**
137 * Angel count
138 */
139 int angel_count;
140
141 };
142
143 /**
144 * Store a bad package entry
145 */
146 struct package_entry_t {
147 char *name;
148 os_package_state_t state;
149 };
150
151 /**
152 * Free a bad package entry
153 */
154 static void free_package_entry(package_entry_t *this)
155 {
156 free(this->name);
157 free(this);
158 }
159
160 /**
161 * Define a language string entry
162 */
163 struct entry_t {
164 char *lang;
165 char *string;
166 };
167
168 /**
169 * Table of multi-lingual improper settings reason string entries
170 */
171 static entry_t settings_reasons[] = {
172 { "en", "Improper OS settings were detected" },
173 { "de", "Unzulässige OS Einstellungen wurden festgestellt" }
174 };
175
176 /**
177 * Table of multi-lingual reason string entries
178 */
179 static entry_t reasons[] = {
180 { "en", "Vulnerable or blacklisted software packages were found" },
181 { "de", "Schwachstellenbehaftete oder gesperrte Softwarepakete wurden gefunden" }
182 };
183
184 /**
185 * Table of multi-lingual forwarding enable string entries
186 */
187 static entry_t instruction_fwd_enabled[] = {
188 { "en", "Please disable IP forwarding" },
189 { "de", "Bitte deaktivieren Sie das IP Forwarding" }
190 };
191
192 /**
193 * Table of multi-lingual default password enabled string entries
194 */
195 static entry_t instruction_default_pwd_enabled[] = {
196 { "en", "Please change the default password" },
197 { "de", "Bitte ändern Sie das default Passwort" }
198 };
199
200 /**
201 * Table of multi-lingual defaul install non market apps string entries
202 */
203 static entry_t instruction_non_market_apps[] = {
204 { "en", "Do not allow the installation of apps from unknown sources" },
205 { "de", "Erlauben Sie nicht die Installation von Apps von unbekannten Quellen" }
206 };
207
208 /**
209 * Define a remediation instruction string entry
210 */
211 struct instruction_entry_t {
212 char *lang;
213 char *update_string;
214 char *removal_string;
215 };
216
217 /**
218 * Tables of multi-lingual remediation instruction string entries
219 */
220 static instruction_entry_t instructions [] = {
221 { "en", "Please update the following software packages:\n",
222 "Please remove the following software packages:\n" },
223 { "de", "Bitte updaten Sie die folgenden Softwarepakete\n",
224 "Bitte entfernen Sie die folgenden Softwarepakete\n" },
225 { "pl", "Proszę zaktualizować następujące pakiety:\n",
226 "Proszę usunąć następujące pakiety:\n" }
227 };
228
229 METHOD(imv_state_t, get_connection_id, TNC_ConnectionID,
230 private_imv_os_state_t *this)
231 {
232 return this->connection_id;
233 }
234
235 METHOD(imv_state_t, has_long, bool,
236 private_imv_os_state_t *this)
237 {
238 return this->has_long;
239 }
240
241 METHOD(imv_state_t, has_excl, bool,
242 private_imv_os_state_t *this)
243 {
244 return this->has_excl;
245 }
246
247 METHOD(imv_state_t, set_flags, void,
248 private_imv_os_state_t *this, bool has_long, bool has_excl)
249 {
250 this->has_long = has_long;
251 this->has_excl = has_excl;
252 }
253
254 METHOD(imv_state_t, set_max_msg_len, void,
255 private_imv_os_state_t *this, u_int32_t max_msg_len)
256 {
257 this->max_msg_len = max_msg_len;
258 }
259
260 METHOD(imv_state_t, get_max_msg_len, u_int32_t,
261 private_imv_os_state_t *this)
262 {
263 return this->max_msg_len;
264 }
265
266 METHOD(imv_state_t, change_state, void,
267 private_imv_os_state_t *this, TNC_ConnectionState new_state)
268 {
269 this->state = new_state;
270 }
271
272 METHOD(imv_state_t, get_recommendation, void,
273 private_imv_os_state_t *this, TNC_IMV_Action_Recommendation *rec,
274 TNC_IMV_Evaluation_Result *eval)
275 {
276 *rec = this->rec;
277 *eval = this->eval;
278 }
279
280 METHOD(imv_state_t, set_recommendation, void,
281 private_imv_os_state_t *this, TNC_IMV_Action_Recommendation rec,
282 TNC_IMV_Evaluation_Result eval)
283 {
284 this->rec = rec;
285 this->eval = eval;
286 }
287
288 METHOD(imv_state_t, get_reason_string, bool,
289 private_imv_os_state_t *this, enumerator_t *language_enumerator,
290 char **reason_string, char **reason_language)
291 {
292 bool match = FALSE;
293 char *lang, *pos;
294 int i, i_chosen = 0, len = 0, nr_of_reasons = 0;
295
296 if (!this->count_update && !this->count_blacklist & !this->os_settings)
297 {
298 return FALSE;
299 }
300
301 while (language_enumerator->enumerate(language_enumerator, &lang))
302 {
303 for (i = 0; i < countof(reasons); i++)
304 {
305 if (streq(lang, reasons[i].lang))
306 {
307 match = TRUE;
308 i_chosen = i;
309 break;
310 }
311 }
312 if (match)
313 {
314 break;
315 }
316 }
317 *reason_language = reasons[i_chosen].lang;
318
319 if (this->count_update || this->count_blacklist)
320 {
321 len += strlen(reasons[i_chosen].string);
322 nr_of_reasons++;
323 }
324 if (this->os_settings)
325 {
326 len += strlen(settings_reasons[i_chosen].string);
327 nr_of_reasons++;
328 }
329
330 /* Allocate memory for the reason string */
331 pos = this->reasons = malloc(len + nr_of_reasons);
332
333 if (this->count_update || this->count_blacklist)
334 {
335 strcpy(pos, reasons[i_chosen].string);
336 pos += strlen(reasons[i_chosen].string);
337 if (--nr_of_reasons)
338 {
339 *pos++ = '\n';
340 }
341 }
342 if (this->os_settings)
343 {
344 strcpy(pos, settings_reasons[i_chosen].string);
345 pos += strlen(settings_reasons[i_chosen].string);
346 }
347 *pos = '\0';
348 *reason_string = this->reasons;
349
350 return TRUE;
351 }
352
353 METHOD(imv_state_t, get_remediation_instructions, bool,
354 private_imv_os_state_t *this, enumerator_t *language_enumerator,
355 char **string, char **lang_code, char **uri)
356 {
357 bool match = FALSE;
358 char *lang, *pos;
359 enumerator_t *enumerator;
360 package_entry_t *entry;
361 int i, i_chosen = 0, len = 0, nr_of_instructions = 0;
362
363 if (!this->count_update && !this->count_blacklist & !this->os_settings)
364 {
365 return FALSE;
366 }
367
368 while (language_enumerator->enumerate(language_enumerator, &lang))
369 {
370 for (i = 0; i < countof(instructions); i++)
371 {
372 if (streq(lang, instructions[i].lang))
373 {
374 match = TRUE;
375 i_chosen = i;
376 break;
377 }
378 }
379 if (match)
380 {
381 break;
382 }
383 }
384 *lang_code = instructions[i_chosen].lang;
385
386 /* Compute the size of the remediation string */
387 if (this->count_update)
388 {
389 len += strlen(instructions[i_chosen].update_string);
390 }
391 if (this->count_blacklist)
392 {
393 len += strlen(instructions[i_chosen].removal_string);
394 }
395 if (this->os_settings & OS_SETTINGS_FWD_ENABLED)
396 {
397 len += strlen(instruction_fwd_enabled[i_chosen].string);
398 nr_of_instructions++;
399 }
400 if (this->os_settings & OS_SETTINGS_DEFAULT_PWD_ENABLED)
401 {
402 len += strlen(instruction_default_pwd_enabled[i_chosen].string);
403 nr_of_instructions++;
404 }
405 if (this->os_settings & OS_SETTINGS_NON_MARKET_APPS)
406 {
407 len += strlen(instruction_non_market_apps[i_chosen].string);
408 nr_of_instructions++;
409 }
410
411 enumerator = this->bad_packages->create_enumerator(this->bad_packages);
412 while (enumerator->enumerate(enumerator, &entry))
413 {
414 len += strlen(entry->name) + 1;
415 }
416 enumerator->destroy(enumerator);
417
418 /* Allocate memory for the remediation instructions */
419 pos = this->instructions = malloc(len + nr_of_instructions + 1);
420
421 /* List of blacklisted packages, if any */
422 if (this->count_blacklist)
423 {
424 strcpy(pos, instructions[i_chosen].removal_string);
425 pos += strlen(instructions[i_chosen].removal_string);
426
427 enumerator = this->bad_packages->create_enumerator(this->bad_packages);
428 while (enumerator->enumerate(enumerator, &entry))
429 {
430 if (entry->state == OS_PACKAGE_STATE_BLACKLIST)
431 {
432 strcpy(pos, entry->name);
433 pos += strlen(entry->name);
434 *pos++ = '\n';
435 }
436 }
437 enumerator->destroy(enumerator);
438 }
439
440 /* List of packages in need of an update, if any */
441 if (this->count_update)
442 {
443 strcpy(pos, instructions[i_chosen].update_string);
444 pos += strlen(instructions[i_chosen].update_string);
445
446 enumerator = this->bad_packages->create_enumerator(this->bad_packages);
447 while (enumerator->enumerate(enumerator, &entry))
448 {
449 if (entry->state != OS_PACKAGE_STATE_BLACKLIST)
450 {
451 strcpy(pos, entry->name);
452 pos += strlen(entry->name);
453 *pos++ = '\n';
454 }
455 }
456 enumerator->destroy(enumerator);
457 }
458
459 /* Add instructions concerning improper OS settings */
460 if (this->os_settings & OS_SETTINGS_FWD_ENABLED)
461 {
462 strcpy(pos, instruction_fwd_enabled[i_chosen].string);
463 pos += strlen(instruction_fwd_enabled[i_chosen].string);
464 if (--nr_of_instructions)
465 {
466 *pos++ = '\n';
467 }
468 }
469 if (this->os_settings & OS_SETTINGS_DEFAULT_PWD_ENABLED)
470 {
471 strcpy(pos, instruction_default_pwd_enabled[i_chosen].string);
472 pos += strlen(instruction_default_pwd_enabled[i_chosen].string);
473 if (--nr_of_instructions)
474 {
475 *pos++ = '\n';
476 }
477 }
478 if (this->os_settings & OS_SETTINGS_NON_MARKET_APPS)
479 {
480 strcpy(pos, instruction_non_market_apps[i_chosen].string);
481 pos += strlen(instruction_non_market_apps[i_chosen].string);
482 }
483
484 *pos = '\0';
485 *string = this->instructions;
486 *uri = lib->settings->get_str(lib->settings,
487 "libimcv.plugins.imv-os.remediation_uri", NULL);
488
489 return TRUE;
490 }
491
492 METHOD(imv_state_t, destroy, void,
493 private_imv_os_state_t *this)
494 {
495 this->bad_packages->destroy_function(this->bad_packages,
496 (void*)free_package_entry);
497 free(this->reasons);
498 free(this->instructions);
499 free(this->info);
500 free(this->name.ptr);
501 free(this->version.ptr);
502 free(this);
503 }
504
505 METHOD(imv_os_state_t, set_info, void,
506 private_imv_os_state_t *this, os_type_t type, chunk_t name, chunk_t version)
507 {
508 int len = name.len + 1 + version.len + 1;
509
510 /* OS info is a concatenation of OS name and OS version */
511 free(this->info);
512 this->info = malloc(len);
513 snprintf(this->info, len, "%.*s %.*s", name.len, name.ptr,
514 version.len, version.ptr);
515 this->type = type;
516 this->name = chunk_clone(name);
517 this->version = chunk_clone(version);
518 }
519
520 METHOD(imv_os_state_t, get_info, char*,
521 private_imv_os_state_t *this, os_type_t *type, chunk_t *name,
522 chunk_t *version)
523 {
524 if (type)
525 {
526 *type = this->type;
527 }
528 if (name)
529 {
530 *name = this->name;
531 }
532 if (version)
533 {
534 *version = this->version;
535 }
536 return this->info;
537 }
538
539 METHOD(imv_os_state_t, set_count, void,
540 private_imv_os_state_t *this, int count, int count_update,
541 int count_blacklist, int count_ok)
542 {
543 this->count += count;
544 this->count_update += count_update;
545 this->count_blacklist += count_blacklist;
546 this->count_ok += count_ok;
547 }
548
549 METHOD(imv_os_state_t, get_count, void,
550 private_imv_os_state_t *this, int *count, int *count_update,
551 int *count_blacklist, int *count_ok)
552 {
553 if (count)
554 {
555 *count = this->count;
556 }
557 if (count_update)
558 {
559 *count_update = this->count_update;
560 }
561 if (count_blacklist)
562 {
563 *count_blacklist = this->count_blacklist;
564 }
565 if (count_ok)
566 {
567 *count_ok = this->count_ok;
568 }
569 }
570
571 METHOD(imv_os_state_t, set_package_request, void,
572 private_imv_os_state_t *this, bool set)
573 {
574 this->package_request = set;
575 }
576
577 METHOD(imv_os_state_t, get_package_request, bool,
578 private_imv_os_state_t *this)
579 {
580 return this->package_request;
581 }
582
583 METHOD(imv_os_state_t, set_os_settings, void,
584 private_imv_os_state_t *this, u_int settings)
585 {
586 this->os_settings |= settings;
587 }
588
589 METHOD(imv_os_state_t, get_os_settings, u_int,
590 private_imv_os_state_t *this)
591 {
592 return this->os_settings;
593 }
594
595 METHOD(imv_os_state_t, set_angel_count, void,
596 private_imv_os_state_t *this, bool start)
597 {
598 this->angel_count += start ? 1 : -1;
599 }
600
601 METHOD(imv_os_state_t, get_angel_count, int,
602 private_imv_os_state_t *this)
603 {
604 return this->angel_count;
605 }
606
607 METHOD(imv_os_state_t, add_bad_package, void,
608 private_imv_os_state_t *this, char *package,
609 os_package_state_t package_state)
610 {
611 package_entry_t *entry;
612
613 entry = malloc_thing(package_entry_t);
614 entry->name = strdup(package);
615 entry->state = package_state;
616 this->bad_packages->insert_last(this->bad_packages, entry);
617 }
618
619 /**
620 * Described in header.
621 */
622 imv_state_t *imv_os_state_create(TNC_ConnectionID connection_id)
623 {
624 private_imv_os_state_t *this;
625
626 INIT(this,
627 .public = {
628 .interface = {
629 .get_connection_id = _get_connection_id,
630 .has_long = _has_long,
631 .has_excl = _has_excl,
632 .set_flags = _set_flags,
633 .set_max_msg_len = _set_max_msg_len,
634 .get_max_msg_len = _get_max_msg_len,
635 .change_state = _change_state,
636 .get_recommendation = _get_recommendation,
637 .set_recommendation = _set_recommendation,
638 .get_reason_string = _get_reason_string,
639 .get_remediation_instructions = _get_remediation_instructions,
640 .destroy = _destroy,
641 },
642 .set_info = _set_info,
643 .get_info = _get_info,
644 .set_count = _set_count,
645 .get_count = _get_count,
646 .set_package_request = _set_package_request,
647 .get_package_request = _get_package_request,
648 .set_os_settings = _set_os_settings,
649 .get_os_settings = _get_os_settings,
650 .set_angel_count = _set_angel_count,
651 .get_angel_count = _get_angel_count,
652 .add_bad_package = _add_bad_package,
653 },
654 .state = TNC_CONNECTION_STATE_CREATE,
655 .rec = TNC_IMV_ACTION_RECOMMENDATION_NO_RECOMMENDATION,
656 .eval = TNC_IMV_EVALUATION_RESULT_DONT_KNOW,
657 .connection_id = connection_id,
658 .bad_packages = linked_list_create(),
659 );
660
661 return &this->public.interface;
662 }
663
664