]> git.ipfire.org Git - people/ms/strongswan.git/blob - src/libimcv/imv/imv_policy_manager.c
traffic-selector: Avoid out-of-bound array access when calculating range
[people/ms/strongswan.git] / src / libimcv / imv / imv_policy_manager.c
1 /*
2 * Copyright (C) 2013-2015 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_policy_manager_usage.h"
17 #include "imv_workitem.h"
18
19 #include <library.h>
20 #include <utils/debug.h>
21
22 #include <tncif_names.h>
23
24 #include <stdlib.h>
25 #include <stdio.h>
26 #include <time.h>
27
28 /* The default policy group #1 is assumed to always exist */
29 #define DEFAULT_GROUP_ID 1
30
31 /**
32 * global debug output variables
33 */
34 static int debug_level = 1;
35 static bool stderr_quiet = FALSE;
36
37 /**
38 * attest dbg function
39 */
40 static void stderr_dbg(debug_t group, level_t level, char *fmt, ...)
41 {
42 va_list args;
43
44 if (level <= debug_level)
45 {
46 if (!stderr_quiet)
47 {
48 va_start(args, fmt);
49 vfprintf(stderr, fmt, args);
50 fprintf(stderr, "\n");
51 va_end(args);
52 }
53 }
54 }
55
56 /**
57 * Collect all enforcements by iterating up through parent groups
58 */
59 static bool iterate_enforcements(database_t *db, int device_id, int session_id,
60 int group_id)
61 {
62 int id, type, file, dir, arg_int, parent, policy, max_age;
63 int p_rec_fail, p_rec_noresult, e_rec_fail, e_rec_noresult, latest_rec;
64 bool latest_success;
65 char *argument;
66 time_t now;
67 enumerator_t *e, *e1, *e2;
68
69 now = time(NULL);
70
71 while (group_id)
72 {
73 e1 = db->query(db,
74 "SELECT e.id, p.type, p.argument, p.file, p.dir, p.rec_fail, "
75 "p.rec_noresult, e.policy, e.max_age, e.rec_fail, e.rec_noresult "
76 "FROM enforcements AS e JOIN policies as p ON e.policy = p.id "
77 "WHERE e.group_id = ?", DB_INT, group_id,
78 DB_INT, DB_INT, DB_TEXT, DB_INT, DB_INT, DB_INT, DB_INT,
79 DB_INT, DB_INT, DB_INT, DB_INT);
80 if (!e1)
81 {
82 return FALSE;
83 }
84 while (e1->enumerate(e1, &id, &type, &argument, &file, &dir,
85 &p_rec_fail, &p_rec_noresult, &policy, &max_age,
86 &e_rec_fail, &e_rec_noresult))
87 {
88 /* check if the latest measurement of the device was successful */
89 latest_success = FALSE;
90
91 if (device_id)
92 {
93 e2 = db->query(db,
94 "SELECT r.rec FROM results AS r "
95 "JOIN sessions AS s ON s.id = r.session "
96 "WHERE r.policy = ? AND s.device = ? AND s.time > ? "
97 "ORDER BY s.time DESC",
98 DB_INT, policy, DB_INT, device_id,
99 DB_UINT, now - max_age, DB_INT);
100 if (!e2)
101 {
102 e1->destroy(e1);
103 return FALSE;
104 }
105 if (e2->enumerate(e2, &latest_rec) &&
106 latest_rec == TNC_IMV_ACTION_RECOMMENDATION_ALLOW)
107 {
108 latest_success = TRUE;
109 }
110 e2->destroy(e2);
111 }
112
113 if (latest_success)
114 {
115 /*skipping enforcement */
116 printf("skipping enforcement %d\n", id);
117 continue;
118 }
119
120 /* determine arg_int */
121 switch ((imv_workitem_type_t)type)
122 {
123 case IMV_WORKITEM_FILE_REF_MEAS:
124 case IMV_WORKITEM_FILE_MEAS:
125 case IMV_WORKITEM_FILE_META:
126 arg_int = file;
127 break;
128 case IMV_WORKITEM_DIR_REF_MEAS:
129 case IMV_WORKITEM_DIR_MEAS:
130 case IMV_WORKITEM_DIR_META:
131 arg_int = dir;
132 break;
133 case IMV_WORKITEM_SWID_TAGS:
134 /* software [identifier] inventory by default */
135 arg_int = 0;
136
137 /* software identifiers only? */
138 if (device_id && strchr(argument, 'R'))
139 {
140 /* get last EID in order to set earliest EID */
141 e2 = db->query(db,
142 "SELECT eid FROM swid_events where device == ? "
143 "ORDER BY eid DESC", DB_UINT, device_id, DB_INT);
144 if (e2)
145 {
146 if (e2->enumerate(e2, &arg_int))
147 {
148 arg_int++;
149 }
150 else
151 {
152 arg_int = 1;
153 }
154 e2->destroy(e2);
155 }
156 }
157 break;
158 default:
159 arg_int = 0;
160 }
161
162 /* insert a workitem */
163 if (db->execute(db, NULL,
164 "INSERT INTO workitems (session, enforcement, type, arg_str, "
165 "arg_int, rec_fail, rec_noresult) VALUES (?, ?, ?, ?, ?, ?, ?)",
166 DB_INT, session_id, DB_INT, id, DB_INT, type, DB_TEXT, argument,
167 DB_INT, arg_int, DB_INT, e_rec_fail ? e_rec_fail : p_rec_fail,
168 DB_INT, e_rec_noresult ? e_rec_noresult : p_rec_noresult) != 1)
169 {
170 e1->destroy(e1);
171 fprintf(stderr, "could not insert workitem\n");
172 return FALSE;
173 }
174 }
175 e1->destroy(e1);
176
177 e = db->query(db,
178 "SELECT parent FROM groups WHERE id = ?",
179 DB_INT, group_id, DB_INT);
180 if (!e)
181 {
182 return FALSE;
183 }
184 if (e->enumerate(e, &parent))
185 {
186 group_id = parent;
187 }
188 else
189 {
190 fprintf(stderr, "group information not found\n");
191 group_id = 0;
192 }
193 e->destroy(e);
194 }
195 return TRUE;
196 }
197
198 static bool policy_start(database_t *db, int session_id)
199 {
200 enumerator_t *e;
201 int device_id, product_id, gid, group_id = DEFAULT_GROUP_ID;
202 u_int created;
203
204 /* get session data */
205 e = db->query(db,
206 "SELECT s.device, s.product, d.created FROM sessions AS s "
207 "LEFT JOIN devices AS d ON s.device = d.id WHERE s.id = ?",
208 DB_INT, session_id, DB_INT, DB_INT, DB_UINT);
209 if (!e || !e->enumerate(e, &device_id, &product_id, &created))
210 {
211 DESTROY_IF(e);
212 fprintf(stderr, "session %d not found\n", session_id);
213 return FALSE;
214 }
215 e->destroy(e);
216
217 /* if a device ID with a creation date exists, get all group memberships */
218 if (device_id && created)
219 {
220 e = db->query(db,
221 "SELECT group_id FROM groups_members WHERE device_id = ?",
222 DB_INT, device_id, DB_INT);
223 if (!e)
224 {
225 return FALSE;
226 }
227 while (e->enumerate(e, &group_id))
228 {
229 if (!iterate_enforcements(db, device_id, session_id, group_id))
230 {
231 e->destroy(e);
232 return FALSE;
233 }
234 }
235 e->destroy(e);
236
237 return TRUE;
238 }
239
240 /* determine if a default product group exists */
241 e = db->query(db,
242 "SELECT group_id FROM groups_product_defaults "
243 "WHERE product_id = ?", DB_INT, product_id, DB_INT);
244 if (!e)
245 {
246 return FALSE;
247 }
248 if (e->enumerate(e, &gid))
249 {
250 group_id = gid;
251 }
252 e->destroy(e);
253
254 if (device_id && !created)
255 {
256 /* assign a newly created device to a default group */
257 if (db->execute(db, NULL,
258 "INSERT INTO groups_members (device_id, group_id) "
259 "VALUES (?, ?)", DB_INT, device_id, DB_INT, group_id) != 1)
260 {
261 fprintf(stderr, "could not assign device to a default group\n");
262 return FALSE;
263 }
264
265 /* set the creation date if it hasn't been set yet */
266 if (db->execute(db, NULL,
267 "UPDATE devices SET created = ? WHERE id = ?",
268 DB_UINT, time(NULL), DB_INT, device_id) != 1)
269 {
270 fprintf(stderr, "creation date of device could not be set\n");
271 return FALSE;
272 }
273 }
274
275 return iterate_enforcements(db, device_id, session_id, group_id);
276 }
277
278 static bool policy_stop(database_t *db, int session_id)
279 {
280 enumerator_t *e;
281 int rec, policy, final_rec, id_type;
282 chunk_t id_value;
283 char *result, *format, *ip_address = NULL;
284 char command[512];
285 bool success = TRUE;
286
287 /* store all workitem results for this session in the results table */
288 e = db->query(db,
289 "SELECT w.rec_final, w.result, e.policy FROM workitems AS w "
290 "JOIN enforcements AS e ON w.enforcement = e.id "
291 "WHERE w.session = ? AND w.result IS NOT NULL",
292 DB_INT, session_id, DB_INT, DB_TEXT, DB_INT);
293 if (e)
294 {
295 while (e->enumerate(e, &rec, &result, &policy))
296 {
297 db->execute(db, NULL,
298 "INSERT INTO results (session, policy, rec, result) "
299 "VALUES (?, ?, ?, ?)", DB_INT, session_id, DB_INT, policy,
300 DB_INT, rec, DB_TEXT, result);
301 }
302 e->destroy(e);
303 }
304 else
305 {
306 success = FALSE;
307 }
308
309 /* delete all workitems for this session from the database */
310 if (db->execute(db, NULL,
311 "DELETE FROM workitems WHERE session = ?",
312 DB_UINT, session_id) < 0)
313 {
314 success = FALSE;
315 }
316
317 final_rec = TNC_IMV_ACTION_RECOMMENDATION_NO_RECOMMENDATION;
318
319 /* retrieve the final recommendation for this session */
320 e = db->query(db,
321 "SELECT rec FROM sessions WHERE id = ?",
322 DB_INT, session_id, DB_INT);
323 if (e)
324 {
325 if (!e->enumerate(e, &final_rec))
326 {
327 success = FALSE;
328 }
329 e->destroy(e);
330 }
331 else
332 {
333 success = FALSE;
334 }
335
336 /* retrieve client IP address for this session */
337 e = db->query(db,
338 "SELECT i.type, i.value FROM identities AS i "
339 "JOIN sessions_identities AS si ON si.identity_id = i.id "
340 "WHERE si.session_id = ? AND (i.type = ? OR i.type = ?)",
341 DB_INT, session_id, DB_INT, TNC_ID_IPV4_ADDR, DB_INT,
342 TNC_ID_IPV6_ADDR, DB_INT, DB_BLOB);
343 if (e)
344 {
345 if (e->enumerate(e, &id_type, &id_value))
346 {
347 ip_address = strndup(id_value.ptr, id_value.len);
348 }
349 else
350 {
351 success = FALSE;
352 }
353 e->destroy(e);
354 }
355 else
356 {
357 success = FALSE;
358 }
359
360 fprintf(stderr, "recommendation for access requestor %s is %N\n",
361 ip_address ? ip_address : "0.0.0.0",
362 TNC_IMV_Action_Recommendation_names, final_rec);
363
364 if (final_rec == TNC_IMV_ACTION_RECOMMENDATION_ALLOW)
365 {
366 format = lib->settings->get_str(lib->settings,
367 "imv_policy_manager.command_allow", NULL);
368 }
369 else
370 {
371 format = lib->settings->get_str(lib->settings,
372 "imv_policy_manager.command_block", NULL);
373 }
374 if (format && ip_address)
375 {
376 /* the IP address can occur at most twice in the command string */
377 snprintf(command, sizeof(command), format, ip_address, ip_address);
378 success = system(command) == 0;
379 fprintf(stderr, "%s system command: %s\n",
380 success ? "successful" : "failed", command);
381 }
382 free(ip_address);
383
384 return success;
385 }
386
387 int main(int argc, char *argv[])
388 {
389 database_t *db;
390 char *uri;
391 int session_id;
392 bool start, success;
393
394 /* enable attest debugging hook */
395 dbg = stderr_dbg;
396
397 atexit(library_deinit);
398
399 /* initialize library */
400 if (!library_init(NULL, "imv_policy_manager"))
401 {
402 exit(SS_RC_LIBSTRONGSWAN_INTEGRITY);
403 }
404 if (!lib->plugins->load(lib->plugins,
405 lib->settings->get_str(lib->settings, "imv_policy_manager.load",
406 "sqlite")))
407 {
408 exit(SS_RC_INITIALIZATION_FAILED);
409 }
410
411 if (argc < 3)
412 {
413 usage();
414 exit(SS_RC_INITIALIZATION_FAILED);
415 }
416 if (streq(argv[1], "start"))
417 {
418 start = TRUE;
419 }
420 else if (streq(argv[1], "stop"))
421 {
422 start = FALSE;
423 }
424 else
425 {
426 usage();
427 exit(SS_RC_INITIALIZATION_FAILED);
428 }
429
430 session_id = atoi(argv[2]);
431
432 /* attach IMV database */
433 uri = lib->settings->get_str(lib->settings,
434 "imv_policy_manager.database",
435 lib->settings->get_str(lib->settings,
436 "charon.imcv.database",
437 lib->settings->get_str(lib->settings,
438 "libimcv.database", NULL)));
439 if (!uri)
440 {
441 fprintf(stderr, "database uri not defined.\n");
442 exit(SS_RC_INITIALIZATION_FAILED);
443 }
444
445 db = lib->db->create(lib->db, uri);
446 if (!db)
447 {
448 fprintf(stderr, "opening database failed.\n");
449 exit(SS_RC_INITIALIZATION_FAILED);
450 }
451
452 if (start)
453 {
454 success = policy_start(db, session_id);
455 }
456 else
457 {
458 success = policy_stop(db, session_id);
459 }
460 db->destroy(db);
461
462 fprintf(stderr, "imv_policy_manager %s %s\n", start ? "start" : "stop",
463 success ? "successful" : "failed");
464
465 exit(EXIT_SUCCESS);
466 }