]> git.ipfire.org Git - people/ms/strongswan.git/blobdiff - 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
index 1a74da277028a4860e5e129dee096d73ea6a3563..b730f8c412c70cfa7e844ae7a0c11c6e4636a6e9 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2013 Andreas Steffen
+ * Copyright (C) 2013-2015 Andreas Steffen
  * HSR Hochschule fuer Technik Rapperswil
  *
  * This program is free software; you can redistribute it and/or modify it
 
 #include "imv_policy_manager_usage.h"
 #include "imv_workitem.h"
+
 #include <library.h>
 #include <utils/debug.h>
 
+#include <tncif_names.h>
+
 #include <stdlib.h>
 #include <stdio.h>
+#include <time.h>
+
+/* The default policy group #1 is assumed to always exist */
+#define DEFAULT_GROUP_ID       1
 
 /**
  * global debug output variables
@@ -47,73 +53,70 @@ static void stderr_dbg(debug_t group, level_t level, char *fmt, ...)
        }
 }
 
-bool policy_start(database_t *db, int session_id)
+/**
+ * Collect all enforcements by iterating up through parent groups
+ */
+static bool iterate_enforcements(database_t *db, int device_id, int session_id,
+                                                                int group_id)
 {
-       enumerator_t *e;
-       int id, gid, device_id, product_id, group_id = 0, parent;
-       int type, file, dir, arg_int, rec_fail, rec_noresult;
+       int id, type, file, dir, arg_int, parent, policy, max_age;
+       int p_rec_fail, p_rec_noresult, e_rec_fail, e_rec_noresult, latest_rec;
+       bool latest_success;
        char *argument;
+       time_t now;
+       enumerator_t *e, *e1, *e2;
 
-       /* get session data */
-       e = db->query(db,
-                       "SELECT device, product FROM sessions WHERE id = ? ",
-                        DB_INT, session_id, DB_INT, DB_INT);
-       if (!e || !e->enumerate(e, &device_id, &product_id))
-       {
-               DESTROY_IF(e);
-               fprintf(stderr, "session %d not found\n", session_id);
-               return FALSE;
-       }
-       e->destroy(e);
-
-       /* if a device ID exists, check if device belongs to a group */
-       if (device_id)
-       {
-               e = db->query(db,
-                               "SELECT group_id FROM groups_members WHERE device_id = ?",
-                                DB_INT, device_id, DB_INT);
-               if (e)
-               {
-                       if (e->enumerate(e, &gid))
-                       {
-                               group_id = gid;
-                       }
-                       e->destroy(e);
-               }
-       }
-
-       /* if no group membership found, try default product group */
-       if (!group_id)
-       {
-               e = db->query(db,
-                               "SELECT group_id FROM groups_product_defaults "
-                               "WHERE product_id = ?", DB_INT, product_id, DB_INT);
-               if (e)
-               {
-                       if (e->enumerate(e, &gid))
-                       {
-                               group_id = gid;
-                       }
-                       e->destroy(e);
-               }
-       }
+       now = time(NULL);
 
-       /* get iteratively enforcements for given group */
        while (group_id)
        {
-               e = db->query(db,
-                               "SELECT e.id, "
-                               "p.type, p.argument, p.file, p.dir, p.rec_fail, p.rec_noresult "
+               e1 = db->query(db,
+                               "SELECT e.id, p.type, p.argument, p.file, p.dir, p.rec_fail, "
+                               "p.rec_noresult, e.policy, e.max_age, e.rec_fail, e.rec_noresult "
                                "FROM enforcements AS e JOIN policies as p ON e.policy = p.id "
                                "WHERE e.group_id = ?", DB_INT, group_id,
-                                DB_INT, DB_INT, DB_TEXT, DB_INT, DB_INT, DB_INT, DB_INT);
-               if (!e)
+                                DB_INT, DB_INT, DB_TEXT, DB_INT, DB_INT, DB_INT, DB_INT,
+                                DB_INT, DB_INT, DB_INT, DB_INT);
+               if (!e1)
                {
                        return FALSE;
                }
-               while (e->enumerate(e, &id, &type, &argument, &file, &dir,
-                                                          &rec_fail, &rec_noresult))
+               while (e1->enumerate(e1, &id, &type, &argument, &file, &dir,
+                                                                &p_rec_fail, &p_rec_noresult, &policy, &max_age,
+                                                                &e_rec_fail, &e_rec_noresult))
                {
+                       /* check if the latest measurement of the device was successful */
+                       latest_success = FALSE;
+
+                       if (device_id)
+                       {
+                               e2 = db->query(db,
+                                               "SELECT r.rec FROM results AS r "
+                                               "JOIN sessions AS s ON s.id = r.session "
+                                               "WHERE r.policy = ? AND s.device = ? AND s.time > ? "
+                                               "ORDER BY s.time DESC",
+                                               DB_INT, policy, DB_INT, device_id,
+                                               DB_UINT, now - max_age, DB_INT);
+                               if (!e2)
+                               {
+                                       e1->destroy(e1);
+                                       return FALSE;
+                               }
+                               if (e2->enumerate(e2, &latest_rec) &&
+                                       latest_rec == TNC_IMV_ACTION_RECOMMENDATION_ALLOW)
+                               {
+                                       latest_success = TRUE;
+                               }
+                               e2->destroy(e2);
+                       }
+
+                       if (latest_success)
+                       {
+                               /*skipping enforcement */
+                               printf("skipping enforcment %d\n", id);
+                               continue;
+                       }
+
                        /* determine arg_int */
                        switch ((imv_workitem_type_t)type)
                        {
@@ -136,14 +139,15 @@ bool policy_start(database_t *db, int session_id)
                                "INSERT INTO workitems (session, enforcement, type, arg_str, "
                                "arg_int, rec_fail, rec_noresult) VALUES (?, ?, ?, ?, ?, ?, ?)",
                                DB_INT, session_id, DB_INT, id, DB_INT, type, DB_TEXT, argument,
-                               DB_INT, arg_int, DB_INT, rec_fail, DB_INT, rec_noresult) != 1)
+                               DB_INT, arg_int, DB_INT, e_rec_fail ? e_rec_fail : p_rec_fail,
+                               DB_INT, e_rec_noresult ? e_rec_noresult : p_rec_noresult) != 1)
                        {
-                               e->destroy(e);
+                               e1->destroy(e1);
                                fprintf(stderr, "could not insert workitem\n");
                                return FALSE;
                        }
                }
-               e->destroy(e);
+               e1->destroy(e1);
 
                e = db->query(db,
                                "SELECT parent FROM groups WHERE id = ?",
@@ -163,16 +167,99 @@ bool policy_start(database_t *db, int session_id)
                }
                e->destroy(e);
        }
-
        return TRUE;
 }
 
-bool policy_stop(database_t *db, int session_id)
+static bool policy_start(database_t *db, int session_id)
 {
        enumerator_t *e;
-       int rec, policy;
-       char *result;
+       int device_id, product_id, gid, group_id = DEFAULT_GROUP_ID;
+       u_int created;
+
+       /* get session data */
+       e = db->query(db,
+                       "SELECT s.device, s.product, d.created FROM sessions AS s "
+                       "LEFT JOIN devices AS d ON s.device = d.id WHERE s.id = ?",
+                        DB_INT, session_id, DB_INT, DB_INT, DB_UINT);
+       if (!e || !e->enumerate(e, &device_id, &product_id, &created))
+       {
+               DESTROY_IF(e);
+               fprintf(stderr, "session %d not found\n", session_id);
+               return FALSE;
+       }
+       e->destroy(e);
+
+       /* if a device ID with a creation date exists, get all group memberships */
+       if (device_id && created)
+       {
+               e = db->query(db,
+                               "SELECT group_id FROM groups_members WHERE device_id = ?",
+                                DB_INT, device_id, DB_INT);
+               if (!e)
+               {
+                       return FALSE;
+               }
+               while (e->enumerate(e, &group_id))
+               {
+                       if (!iterate_enforcements(db, device_id, session_id, group_id))
+                       {
+                               e->destroy(e);
+                               return FALSE;
+                       }
+               }
+               e->destroy(e);
+
+               return TRUE;
+       }
 
+       /* determine if a default product group exists */
+       e = db->query(db,
+                       "SELECT group_id FROM groups_product_defaults "
+                       "WHERE product_id = ?", DB_INT, product_id, DB_INT);
+       if (!e)
+       {
+               return FALSE;
+       }
+       if (e->enumerate(e, &gid))
+       {
+               group_id = gid;
+       }
+       e->destroy(e);
+
+       if (device_id && !created)
+       {
+               /* assign a newly created device to a default group */
+               if (db->execute(db, NULL,
+                       "INSERT INTO groups_members (device_id, group_id) "
+                       "VALUES (?, ?)", DB_INT, device_id, DB_INT, group_id) != 1)
+               {
+                       fprintf(stderr, "could not assign device to a default group\n");
+                       return FALSE;
+               }
+
+               /* set the creation date if it hasn't been set yet */
+               if (db->execute(db, NULL,
+                               "UPDATE devices SET created = ? WHERE id = ?",
+                               DB_UINT, time(NULL), DB_INT, device_id) != 1)
+               {
+                       fprintf(stderr, "creation date of device could not be set\n");
+                       return FALSE;
+               }
+       }
+
+       return iterate_enforcements(db, device_id, session_id, group_id);
+}
+
+static bool policy_stop(database_t *db, int session_id)
+{
+       enumerator_t *e;
+       int rec, policy, final_rec, id_type;
+       chunk_t id_value;
+       char *result, *format, *ip_address = NULL;
+       char command[512];
+       bool success = TRUE;
+
+       /* store all workitem results for this session in the results table */
        e = db->query(db,
                        "SELECT w.rec_final, w.result, e.policy FROM workitems AS w "
                        "JOIN enforcements AS e ON w.enforcement = e.id "
@@ -189,15 +276,93 @@ bool policy_stop(database_t *db, int session_id)
                }
                e->destroy(e);
        }
-       return db->execute(db, NULL,
-                               "DELETE FROM workitems WHERE session = ?",
-                               DB_UINT, session_id) >= 0;
+       else
+       {
+               success = FALSE;
+       }
+
+       /* delete all workitems for this session from the database */
+       if (db->execute(db, NULL,
+                                       "DELETE FROM workitems WHERE session = ?",
+                                       DB_UINT, session_id) < 0)
+       {
+               success = FALSE;
+       }
+
+       final_rec = TNC_IMV_ACTION_RECOMMENDATION_NO_RECOMMENDATION;
+
+       /* retrieve the final recommendation for this session */
+       e = db->query(db,
+                       "SELECT rec FROM sessions WHERE id = ?",
+                        DB_INT, session_id, DB_INT);
+       if (e)
+       {
+               if (!e->enumerate(e, &final_rec))
+               {
+                       success = FALSE;
+               }
+               e->destroy(e);
+       }
+       else
+       {
+               success = FALSE;
+       }
+
+       /* retrieve client IP address for this session */
+       e = db->query(db,
+                       "SELECT i.type, i.value FROM identities AS i "
+                       "JOIN sessions_identities AS si ON si.identity_id = i.id "
+                       "WHERE si.session_id = ? AND (i.type = ? OR i.type = ?)",
+                        DB_INT, session_id, DB_INT, TNC_ID_IPV4_ADDR, DB_INT,
+                        TNC_ID_IPV6_ADDR, DB_INT, DB_BLOB);
+       if (e)
+       {
+               if (e->enumerate(e, &id_type, &id_value))
+               {
+                       ip_address = strndup(id_value.ptr, id_value.len);
+               }
+               else
+               {
+                       success = FALSE;
+               }
+               e->destroy(e);
+       }
+       else
+       {
+               success = FALSE;
+       }
+
+       fprintf(stderr, "recommendation for access requestor %s is %N\n",
+                       ip_address ? ip_address : "0.0.0.0",
+                       TNC_IMV_Action_Recommendation_names, final_rec);
+
+       if (final_rec == TNC_IMV_ACTION_RECOMMENDATION_ALLOW)
+       {
+               format = lib->settings->get_str(lib->settings,
+                                               "imv_policy_manager.command_allow", NULL);
+       }
+       else
+       {
+               format = lib->settings->get_str(lib->settings,
+                                               "imv_policy_manager.command_block", NULL);
+       }
+       if (format && ip_address)
+       {
+               /* the IP address can occur at most twice in the command string */
+               snprintf(command, sizeof(command), format, ip_address, ip_address);
+               success = system(command) == 0;
+               fprintf(stderr, "%s system command: %s\n",
+                           success ? "successful" : "failed", command);
+       }
+       free(ip_address);
+
+       return success;
 }
 
 int main(int argc, char *argv[])
 {
        database_t *db;
-       char *uri, *tnc_session_id;
+       char *uri;
        int session_id;
        bool start, success;
 
@@ -207,18 +372,18 @@ int main(int argc, char *argv[])
        atexit(library_deinit);
 
        /* initialize library */
-       if (!library_init(NULL))
+       if (!library_init(NULL, "imv_policy_manager"))
        {
                exit(SS_RC_LIBSTRONGSWAN_INTEGRITY);
        }
-       if (!lib->plugins->load(lib->plugins, NULL, 
+       if (!lib->plugins->load(lib->plugins,
                        lib->settings->get_str(lib->settings, "imv_policy_manager.load",
                                 "sqlite")))
        {
                exit(SS_RC_INITIALIZATION_FAILED);
        }
 
-       if (argc < 2)
+       if (argc < 3)
        {
                usage();
                exit(SS_RC_INITIALIZATION_FAILED);
@@ -237,17 +402,15 @@ int main(int argc, char *argv[])
                exit(SS_RC_INITIALIZATION_FAILED);
        }
 
-       /* get session ID */
-       tnc_session_id = getenv("TNC_SESSION_ID");
-       if (!tnc_session_id)
-       {
-               fprintf(stderr, "environment variable TNC_SESSION_ID is not defined\n");
-               exit(SS_RC_INITIALIZATION_FAILED);
-       }
-       session_id = atoi(tnc_session_id);
-       
+       session_id = atoi(argv[2]);
+
        /* attach IMV database */
-       uri = lib->settings->get_str(lib->settings, "libimcv.database", NULL);
+       uri = lib->settings->get_str(lib->settings,
+                       "imv_policy_manager.database",
+                       lib->settings->get_str(lib->settings,
+                               "charon.imcv.database",
+                               lib->settings->get_str(lib->settings,
+                                       "libimcv.database", NULL)));
        if (!uri)
        {
                fprintf(stderr, "database uri not defined.\n");
@@ -276,4 +439,3 @@ int main(int argc, char *argv[])
 
        exit(EXIT_SUCCESS);
 }
-