]> git.ipfire.org Git - people/ms/strongswan.git/blame - src/libimcv/imv/imv_database.c
Make access requestor IP address available to TNC server
[people/ms/strongswan.git] / src / libimcv / imv / imv_database.c
CommitLineData
b8db66de 1/*
00cd79b6 2 * Copyright (C) 2013-2015 Andreas Steffen
b8db66de
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
bb9d8b18 16#define _GNU_SOURCE
b8db66de 17
bb9d8b18 18#include <stdio.h>
4f9aabbf 19#include <stdarg.h>
b8db66de
AS
20#include <string.h>
21#include <time.h>
22
bb9d8b18
AS
23#include "imv_database.h"
24
00cd79b6
AS
25#include <tncif_identity.h>
26
bb9d8b18 27#include <utils/debug.h>
a6266485 28#include <threading/mutex.h>
bb9d8b18 29
b8db66de
AS
30typedef struct private_imv_database_t private_imv_database_t;
31
b8db66de
AS
32/**
33 * Private data of a imv_database_t object.
b8db66de
AS
34 */
35struct private_imv_database_t {
36
37 /**
38 * Public imv_database_t interface.
39 */
40 imv_database_t public;
41
42 /**
43 * database instance
44 */
45 database_t *db;
46
bb9d8b18
AS
47 /**
48 * policy script
49 */
50 char *script;
51
b8db66de
AS
52};
53
4894bfa2
AS
54METHOD(imv_database_t, get_database, database_t*,
55 private_imv_database_t *this)
b8db66de 56{
4894bfa2
AS
57 return this->db;
58}
b8db66de 59
4894bfa2
AS
60/**
61 * Create a session entry in the IMV database
62 */
63static bool create_session(private_imv_database_t *this, imv_session_t *session)
64{
00cd79b6 65 enumerator_t *enumerator, *e;
4894bfa2 66 imv_os_info_t *os_info;
00cd79b6
AS
67 chunk_t device_id;
68 tncif_identity_t *tnc_id;
4894bfa2 69 TNC_ConnectionID conn_id;
4894bfa2 70 char *product, *device;
00cd79b6
AS
71 int session_id = 0, pid = 0, did = 0, trusted = 0, created;
72 bool first = TRUE, success = TRUE;
b1da8368 73
4894bfa2
AS
74 /* get product info string */
75 os_info = session->get_os_info(session);
76 product = os_info->get_info(os_info);
77 if (!product)
78 {
79 DBG1(DBG_IMV, "imv_db: product info is not available");
80 return FALSE;
81 }
b8db66de
AS
82
83 /* get primary key of product info string if it exists */
84 e = this->db->query(this->db,
85 "SELECT id FROM products WHERE name = ?", DB_TEXT, product, DB_INT);
86 if (e)
87 {
88 e->enumerate(e, &pid);
89 e->destroy(e);
90 }
91
92 /* if product info string has not been found - register it */
93 if (!pid)
94 {
95 this->db->execute(this->db, &pid,
96 "INSERT INTO products (name) VALUES (?)", DB_TEXT, product);
97 }
4894bfa2
AS
98
99 if (!pid)
100 {
101 DBG1(DBG_IMV, "imv_db: registering product info failed");
102 return FALSE;
103 }
ecc6c2e8 104
4894bfa2
AS
105 /* get device ID string */
106 if (!session->get_device_id(session, &device_id))
b8db66de 107 {
4894bfa2
AS
108 DBG1(DBG_IMV, "imv_db: device ID is not available");
109 return FALSE;
b8db66de 110 }
4894bfa2 111 device = strndup(device_id.ptr, device_id.len);
b8db66de 112
4894bfa2 113 /* get primary key of device ID if it exists */
19ce03be 114 e = this->db->query(this->db,
4894bfa2
AS
115 "SELECT id, trusted FROM devices WHERE value = ? AND product = ?",
116 DB_TEXT, device, DB_INT, pid, DB_INT, DB_INT);
19ce03be
AS
117 if (e)
118 {
4894bfa2 119 e->enumerate(e, &did, &trusted);
19ce03be
AS
120 e->destroy(e);
121 }
b8db66de 122
4894bfa2
AS
123 /* if device ID is trusted, set trust in session */
124 if (trusted)
b8db66de 125 {
4894bfa2 126 session->set_device_trust(session, TRUE);
b8db66de
AS
127 }
128
4894bfa2 129 /* if device ID has not been found - register it */
b8db66de
AS
130 if (!did)
131 {
132 this->db->execute(this->db, &did,
19ce03be 133 "INSERT INTO devices (value, product) VALUES (?, ?)",
4894bfa2 134 DB_TEXT, device, DB_INT, pid);
b8db66de 135 }
4894bfa2
AS
136 free(device);
137
138 if (!did)
139 {
140 DBG1(DBG_IMV, "imv_db: registering device ID failed");
141 return FALSE;
142 }
143
144 /* create a new session entry */
145 created = session->get_creation_time(session);
146 conn_id = session->get_connection_id(session);
147 this->db->execute(this->db, &session_id,
00cd79b6
AS
148 "INSERT INTO sessions (time, connection, product, device) "
149 "VALUES (?, ?, ?, ?)",
150 DB_INT, created, DB_INT, conn_id, DB_INT, pid, DB_INT, did);
4894bfa2
AS
151
152 if (session_id)
153 {
154 DBG2(DBG_IMV, "assigned session ID %d to Connection ID %d",
155 session_id, conn_id);
156 }
157 else
158 {
159 DBG1(DBG_IMV, "imv_db: registering session failed");
160 return FALSE;
161 }
162 session->set_session_id(session, session_id, pid, did);
163
00cd79b6
AS
164 enumerator = session->create_ar_identities_enumerator(session);
165 while (enumerator->enumerate(enumerator, &tnc_id))
166 {
167 pen_type_t ar_id_type;
168 chunk_t ar_id_value;
169 int ar_id = 0, si_id = 0;
170
171 ar_id_type = tnc_id->get_identity_type(tnc_id);
172 ar_id_value = tnc_id->get_identity_value(tnc_id);
173
174 if (ar_id_type.vendor_id != PEN_TCG || ar_id_value.len == 0)
175 {
176 continue;
177 }
178
179 /* get primary key of AR identity if it exists */
180 e = this->db->query(this->db,
181 "SELECT id FROM identities WHERE type = ? AND value = ?",
182 DB_INT, ar_id_type.type, DB_BLOB, ar_id_value, DB_INT);
183 if (e)
184 {
185 e->enumerate(e, &ar_id);
186 e->destroy(e);
187 }
188
189 /* if AR identity has not been found - register it */
190 if (!ar_id)
191 {
192 this->db->execute(this->db, &ar_id,
193 "INSERT INTO identities (type, value) VALUES (?, ?)",
194 DB_INT, ar_id_type.type, DB_BLOB, ar_id_value);
195 }
196 if (!ar_id)
197 {
198 DBG1(DBG_IMV, "imv_db: registering access requestor failed");
199 success = FALSE;
200 break;
201 }
202
203 this->db->execute(this->db, &si_id,
204 "INSERT INTO sessions_identities (session_id, identity_id) "
205 "VALUES (?, ?)",
206 DB_INT, session_id, DB_INT, ar_id);
207
208 if (!si_id)
209 {
210 DBG1(DBG_IMV, "imv_db: assigning identity to session failed");
211 success = FALSE;
212 break;
213 }
214
215 if (first)
216 {
217 this->db->execute(this->db, NULL,
218 "UPDATE sessions SET identity = ? WHERE id = ?",
219 DB_INT, ar_id, DB_INT, session_id);
220 first = FALSE;
221 }
222 }
223 enumerator->destroy(enumerator);
224
225 return success;
4894bfa2
AS
226}
227
228static bool add_workitems(private_imv_database_t *this, imv_session_t *session)
229{
230 char *arg_str;
231 int id, arg_int, rec_fail, rec_noresult;
232 imv_workitem_t *workitem;
233 imv_workitem_type_t type;
234 enumerator_t *e;
235
236 e = this->db->query(this->db,
237 "SELECT id, type, arg_str, arg_int, rec_fail, rec_noresult "
238 "FROM workitems WHERE session = ?",
239 DB_INT, session->get_session_id(session, NULL, NULL),
240 DB_INT, DB_INT, DB_TEXT, DB_INT,DB_INT, DB_INT);
241 if (!e)
b8db66de 242 {
4894bfa2
AS
243 DBG1(DBG_IMV, "imv_db: no workitem enumerator returned");
244 return FALSE;
245 }
246 while (e->enumerate(e, &id, &type, &arg_str, &arg_int, &rec_fail,
247 &rec_noresult))
248 {
249 DBG2(DBG_IMV, "%N workitem %d", imv_workitem_type_names, type, id);
250 workitem = imv_workitem_create(id, type, arg_str, arg_int, rec_fail,
251 rec_noresult);
252 session->insert_workitem(session, workitem);
b8db66de 253 }
4894bfa2 254 e->destroy(e);
b8db66de 255
4894bfa2 256 return TRUE;
b8db66de
AS
257}
258
29645621
AS
259METHOD(imv_database_t, add_recommendation, void,
260 private_imv_database_t *this, imv_session_t *session,
261 TNC_IMV_Action_Recommendation rec)
262{
4894bfa2 263 /* add final recommendation to session DB entry */
29645621
AS
264 this->db->execute(this->db, NULL,
265 "UPDATE sessions SET rec = ? WHERE id = ?",
4894bfa2 266 DB_INT, rec, DB_INT, session->get_session_id(session, NULL, NULL));
29645621
AS
267}
268
bb9d8b18 269METHOD(imv_database_t, policy_script, bool,
a6266485 270 private_imv_database_t *this, imv_session_t *session, bool start)
bb9d8b18 271{
4894bfa2 272 char command[512], resp[128], *last;
bb9d8b18
AS
273 FILE *shell;
274
4894bfa2
AS
275 if (start)
276 {
277 if (session->get_policy_started(session))
278 {
279 DBG1(DBG_IMV, "policy script as already been started");
280 return FALSE;
281 }
a6266485 282
4894bfa2
AS
283 /* add product info and device ID to session DB entry */
284 if (!create_session(this, session))
285 {
286 return FALSE;
287 }
288 }
289 else
290 {
291 if (!session->get_policy_started(session))
292 {
293 DBG1(DBG_IMV, "policy script as already been stopped");
294 return FALSE;
295 }
296 }
297
298 /* call the policy script */
ecc6c2e8
MW
299 snprintf(command, sizeof(command), "2>&1 %s %s %d",
300 this->script, start ? "start" : "stop",
301 session->get_session_id(session, NULL, NULL));
bb9d8b18
AS
302 DBG3(DBG_IMV, "running policy script: %s", command);
303
304 shell = popen(command, "r");
305 if (shell == NULL)
306 {
307 DBG1(DBG_IMV, "could not execute policy script '%s'",
308 this->script);
309 return FALSE;
310 }
311 while (TRUE)
312 {
313 if (fgets(resp, sizeof(resp), shell) == NULL)
314 {
315 if (ferror(shell))
316 {
317 DBG1(DBG_IMV, "error reading output from policy script");
318 }
319 break;
320 }
321 else
322 {
323 last = resp + strlen(resp) - 1;
324 if (last >= resp && *last == '\n')
325 {
326 /* replace trailing '\n' */
327 *last = '\0';
328 }
329 DBG1(DBG_IMV, "policy: %s", resp);
330 }
331 }
332 pclose(shell);
333
4894bfa2 334 if (start)
a6266485 335 {
4894bfa2
AS
336 /* add workitem list generated by policy manager to session object */
337 if (!add_workitems(this, session))
a6266485 338 {
a6266485
AS
339 return FALSE;
340 }
a6266485
AS
341 session->set_policy_started(session, TRUE);
342 }
4894bfa2 343 else
4f9aabbf 344 {
a6266485 345 session->set_policy_started(session, FALSE);
4f9aabbf 346 }
4f9aabbf 347
a6266485 348 return TRUE;
4f9aabbf
AS
349}
350
a6266485
AS
351METHOD(imv_database_t, finalize_workitem, bool,
352 private_imv_database_t *this, imv_workitem_t *workitem)
4f9aabbf 353{
a6266485
AS
354 char *result;
355 int rec;
4f9aabbf 356
a6266485 357 rec = workitem->get_result(workitem, &result);
4f9aabbf 358
a6266485
AS
359 return this->db->execute(this->db, NULL,
360 "UPDATE workitems SET result = ?, rec_final = ? WHERE id = ?",
361 DB_TEXT, result, DB_INT, rec,
362 DB_INT, workitem->get_id(workitem)) == 1;
4f9aabbf
AS
363}
364
b8db66de
AS
365METHOD(imv_database_t, destroy, void,
366 private_imv_database_t *this)
367{
4f6bf1a8 368 DESTROY_IF(this->db);
b8db66de
AS
369 free(this);
370}
371
372/**
373 * See header
374 */
a6266485 375imv_database_t *imv_database_create(char *uri, char *script)
b8db66de
AS
376{
377 private_imv_database_t *this;
378
379 INIT(this,
380 .public = {
4894bfa2 381 .get_database = _get_database,
bb9d8b18 382 .policy_script = _policy_script,
a6266485 383 .finalize_workitem = _finalize_workitem,
4894bfa2 384 .add_recommendation = _add_recommendation,
b8db66de
AS
385 .destroy = _destroy,
386 },
387 .db = lib->db->create(lib->db, uri),
a6266485 388 .script = script,
b8db66de
AS
389 );
390
391 if (!this->db)
392 {
393 DBG1(DBG_IMV,
394 "failed to connect to IMV database '%s'", uri);
a6266485 395 destroy(this);
b8db66de
AS
396 return NULL;
397 }
398
399 return &this->public;
400}