]> git.ipfire.org Git - people/ms/strongswan.git/blame - src/libimcv/imv/imv_policy_manager.c
imv_policy_manager: Added capability to execute an allow or block shell command string
[people/ms/strongswan.git] / src / libimcv / imv / imv_policy_manager.c
CommitLineData
1ecff259 1/*
00cd79b6 2 * Copyright (C) 2013-2015 Andreas Steffen
1ecff259
AS
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"
b18a5317 18
1ecff259
AS
19#include <library.h>
20#include <utils/debug.h>
21
00cd79b6
AS
22#include <tncif_names.h>
23
1ecff259
AS
24#include <stdlib.h>
25#include <stdio.h>
632e5b0b 26#include <time.h>
1ecff259 27
59c9ec10
AS
28/* The default policy group #1 is assumed to always exist */
29#define DEFAULT_GROUP_ID 1
30
1ecff259
AS
31/**
32 * global debug output variables
33 */
b1da8368 34static int debug_level = 1;
1ecff259
AS
35static bool stderr_quiet = FALSE;
36
37/**
38 * attest dbg function
39 */
40static 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
f4dcbe3b
AS
56/**
57 * Collect all enforcements by iterating up through parent groups
58 */
ea6ab9fb
AS
59static bool iterate_enforcements(database_t *db, int device_id, int session_id,
60 int group_id)
1ecff259 61{
ea6ab9fb
AS
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;
65148217 65 char *argument;
ea6ab9fb
AS
66 time_t now;
67 enumerator_t *e, *e1, *e2;
68
69 now = time(NULL);
65148217 70
a45a2c92 71 while (group_id)
65148217 72 {
ea6ab9fb
AS
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 "
a45a2c92
AS
76 "FROM enforcements AS e JOIN policies as p ON e.policy = p.id "
77 "WHERE e.group_id = ?", DB_INT, group_id,
ea6ab9fb
AS
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)
b1da8368 81 {
a45a2c92 82 return FALSE;
b1da8368 83 }
ea6ab9fb
AS
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))
a45a2c92 87 {
ea6ab9fb
AS
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 enforcment %d\n", id);
117 continue;
118 }
119
a45a2c92
AS
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 default:
134 arg_int = 0;
135 }
b1da8368 136
a45a2c92
AS
137 /* insert a workitem */
138 if (db->execute(db, NULL,
b1da8368
AS
139 "INSERT INTO workitems (session, enforcement, type, arg_str, "
140 "arg_int, rec_fail, rec_noresult) VALUES (?, ?, ?, ?, ?, ?, ?)",
65148217 141 DB_INT, session_id, DB_INT, id, DB_INT, type, DB_TEXT, argument,
ea6ab9fb
AS
142 DB_INT, arg_int, DB_INT, e_rec_fail ? e_rec_fail : p_rec_fail,
143 DB_INT, e_rec_noresult ? e_rec_noresult : p_rec_noresult) != 1)
a45a2c92 144 {
ea6ab9fb 145 e1->destroy(e1);
a45a2c92
AS
146 fprintf(stderr, "could not insert workitem\n");
147 return FALSE;
148 }
149 }
ea6ab9fb 150 e1->destroy(e1);
a45a2c92
AS
151
152 e = db->query(db,
153 "SELECT parent FROM groups WHERE id = ?",
154 DB_INT, group_id, DB_INT);
155 if (!e)
65148217 156 {
65148217
AS
157 return FALSE;
158 }
a45a2c92
AS
159 if (e->enumerate(e, &parent))
160 {
161 group_id = parent;
162 }
163 else
164 {
165 fprintf(stderr, "group information not found\n");
166 group_id = 0;
167 }
168 e->destroy(e);
65148217 169 }
1ecff259
AS
170 return TRUE;
171}
172
f4dcbe3b
AS
173static bool policy_start(database_t *db, int session_id)
174{
175 enumerator_t *e;
176 int device_id, product_id, gid, group_id = DEFAULT_GROUP_ID;
177 u_int created;
178
179 /* get session data */
180 e = db->query(db,
181 "SELECT s.device, s.product, d.created FROM sessions AS s "
182 "LEFT JOIN devices AS d ON s.device = d.id WHERE s.id = ?",
183 DB_INT, session_id, DB_INT, DB_INT, DB_UINT);
184 if (!e || !e->enumerate(e, &device_id, &product_id, &created))
185 {
186 DESTROY_IF(e);
187 fprintf(stderr, "session %d not found\n", session_id);
188 return FALSE;
189 }
190 e->destroy(e);
191
192 /* if a device ID with a creation date exists, get all group memberships */
a21d4096 193 if (device_id && created)
f4dcbe3b
AS
194 {
195 e = db->query(db,
196 "SELECT group_id FROM groups_members WHERE device_id = ?",
197 DB_INT, device_id, DB_INT);
198 if (!e)
199 {
200 return FALSE;
201 }
202 while (e->enumerate(e, &group_id))
203 {
ea6ab9fb 204 if (!iterate_enforcements(db, device_id, session_id, group_id))
f4dcbe3b
AS
205 {
206 e->destroy(e);
207 return FALSE;
208 }
209 }
210 e->destroy(e);
211
212 return TRUE;
213 }
214
215 /* determine if a default product group exists */
216 e = db->query(db,
217 "SELECT group_id FROM groups_product_defaults "
218 "WHERE product_id = ?", DB_INT, product_id, DB_INT);
219 if (!e)
220 {
221 return FALSE;
222 }
223 if (e->enumerate(e, &gid))
224 {
225 group_id = gid;
226 }
227 e->destroy(e);
228
229 if (device_id && !created)
230 {
231 /* assign a newly created device to a default group */
232 if (db->execute(db, NULL,
233 "INSERT INTO groups_members (device_id, group_id) "
234 "VALUES (?, ?)", DB_INT, device_id, DB_INT, group_id) != 1)
235 {
236 fprintf(stderr, "could not assign device to a default group\n");
237 return FALSE;
238 }
239
240 /* set the creation date if it hasn't been set yet */
241 if (db->execute(db, NULL,
242 "UPDATE devices SET created = ? WHERE id = ?",
243 DB_UINT, time(NULL), DB_INT, device_id) != 1)
244 {
245 fprintf(stderr, "creation date of device could not be set\n");
246 return FALSE;
247 }
248 }
249
ea6ab9fb 250 return iterate_enforcements(db, device_id, session_id, group_id);
f4dcbe3b
AS
251}
252
253static bool policy_stop(database_t *db, int session_id)
1ecff259 254{
65148217 255 enumerator_t *e;
00cd79b6
AS
256 int rec, policy, final_rec, id_type;
257 chunk_t id_value;
79b5a33c
AS
258 char *result, *format, *ip_address = NULL;
259 char command[512];
00cd79b6 260 bool success = TRUE;
65148217 261
00cd79b6 262 /* store all workitem results for this session in the results table */
65148217
AS
263 e = db->query(db,
264 "SELECT w.rec_final, w.result, e.policy FROM workitems AS w "
e1db511b
AS
265 "JOIN enforcements AS e ON w.enforcement = e.id "
266 "WHERE w.session = ? AND w.result IS NOT NULL",
65148217
AS
267 DB_INT, session_id, DB_INT, DB_TEXT, DB_INT);
268 if (e)
269 {
270 while (e->enumerate(e, &rec, &result, &policy))
271 {
65148217
AS
272 db->execute(db, NULL,
273 "INSERT INTO results (session, policy, rec, result) "
274 "VALUES (?, ?, ?, ?)", DB_INT, session_id, DB_INT, policy,
275 DB_INT, rec, DB_TEXT, result);
276 }
277 e->destroy(e);
65148217 278 }
00cd79b6
AS
279 else
280 {
281 success = FALSE;
282 }
283
284 /* delete all workitems for this session from the database */
285 if (db->execute(db, NULL,
286 "DELETE FROM workitems WHERE session = ?",
287 DB_UINT, session_id) < 0)
288 {
289 success = FALSE;
290 }
291
292 final_rec = TNC_IMV_ACTION_RECOMMENDATION_NO_RECOMMENDATION;
293
294 /* retrieve the final recommendation for this session */
295 e = db->query(db,
296 "SELECT rec FROM sessions WHERE id = ?",
297 DB_INT, session_id, DB_INT);
298 if (e)
299 {
300 if (!e->enumerate(e, &final_rec))
301 {
302 success = FALSE;
303 }
304 e->destroy(e);
305 }
306 else
307 {
308 success = FALSE;
309 }
310
311 /* retrieve client IP address for this session */
312 e = db->query(db,
313 "SELECT i.type, i.value FROM identities AS i "
314 "JOIN sessions_identities AS si ON si.identity_id = i.id "
315 "WHERE si.session_id = ? AND (i.type = ? OR i.type = ?)",
316 DB_INT, session_id, DB_INT, TNC_ID_IPV4_ADDR, DB_INT,
317 TNC_ID_IPV6_ADDR, DB_INT, DB_BLOB);
318 if (e)
319 {
320 if (e->enumerate(e, &id_type, &id_value))
321 {
322 ip_address = strndup(id_value.ptr, id_value.len);
323 }
324 else
325 {
326 success = FALSE;
327 }
328 e->destroy(e);
329 }
330 else
331 {
332 success = FALSE;
333 }
334
335 fprintf(stderr, "recommendation for access requestor %s is %N\n",
336 ip_address ? ip_address : "0.0.0.0",
337 TNC_IMV_Action_Recommendation_names, final_rec);
79b5a33c
AS
338
339 if (final_rec == TNC_IMV_ACTION_RECOMMENDATION_ALLOW)
340 {
341 format = lib->settings->get_str(lib->settings,
342 "imv_policy_manager.command_allow", NULL);
343 }
344 else
345 {
346 format = lib->settings->get_str(lib->settings,
347 "imv_policy_manager.command_block", NULL);
348 }
349 if (format && ip_address)
350 {
351 /* the IP address can occur at most twice in the command string */
352 snprintf(command, sizeof(command), format, ip_address, ip_address);
353 success = system(command) == 0;
354 fprintf(stderr, "%s system command: %s\n",
355 success ? "successful" : "failed", command);
356 }
00cd79b6
AS
357 free(ip_address);
358
359 return success;
1ecff259
AS
360}
361
362int main(int argc, char *argv[])
363{
364 database_t *db;
ecc6c2e8 365 char *uri;
1ecff259
AS
366 int session_id;
367 bool start, success;
368
369 /* enable attest debugging hook */
370 dbg = stderr_dbg;
371
372 atexit(library_deinit);
373
374 /* initialize library */
34d3bfcf 375 if (!library_init(NULL, "imv_policy_manager"))
1ecff259
AS
376 {
377 exit(SS_RC_LIBSTRONGSWAN_INTEGRITY);
378 }
b18a5317 379 if (!lib->plugins->load(lib->plugins,
1ecff259
AS
380 lib->settings->get_str(lib->settings, "imv_policy_manager.load",
381 "sqlite")))
382 {
383 exit(SS_RC_INITIALIZATION_FAILED);
384 }
385
ecc6c2e8 386 if (argc < 3)
1ecff259
AS
387 {
388 usage();
389 exit(SS_RC_INITIALIZATION_FAILED);
390 }
391 if (streq(argv[1], "start"))
392 {
393 start = TRUE;
394 }
395 else if (streq(argv[1], "stop"))
396 {
397 start = FALSE;
398 }
399 else
400 {
401 usage();
402 exit(SS_RC_INITIALIZATION_FAILED);
403 }
404
ecc6c2e8 405 session_id = atoi(argv[2]);
b18a5317 406
4f6bf1a8 407 /* attach IMV database */
1ec34763
TB
408 uri = lib->settings->get_str(lib->settings,
409 "imv_policy_manager.database",
410 lib->settings->get_str(lib->settings,
411 "charon.imcv.database",
412 lib->settings->get_str(lib->settings,
413 "libimcv.database", NULL)));
b1da8368
AS
414 if (!uri)
415 {
416 fprintf(stderr, "database uri not defined.\n");
417 exit(SS_RC_INITIALIZATION_FAILED);
418 }
419
1ecff259
AS
420 db = lib->db->create(lib->db, uri);
421 if (!db)
422 {
423 fprintf(stderr, "opening database failed.\n");
424 exit(SS_RC_INITIALIZATION_FAILED);
425 }
426
427 if (start)
428 {
429 success = policy_start(db, session_id);
430 }
431 else
432 {
433 success = policy_stop(db, session_id);
434 }
435 db->destroy(db);
436
437 fprintf(stderr, "imv_policy_manager %s %s\n", start ? "start" : "stop",
438 success ? "successful" : "failed");
439
440 exit(EXIT_SUCCESS);
441}