]> git.ipfire.org Git - people/ms/strongswan.git/blob - 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
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 #define _GNU_SOURCE
17
18 #include <stdio.h>
19 #include <stdarg.h>
20 #include <string.h>
21 #include <time.h>
22
23 #include "imv_database.h"
24
25 #include <tncif_identity.h>
26
27 #include <utils/debug.h>
28 #include <threading/mutex.h>
29
30 typedef struct private_imv_database_t private_imv_database_t;
31
32 /**
33 * Private data of a imv_database_t object.
34 */
35 struct 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
47 /**
48 * policy script
49 */
50 char *script;
51
52 };
53
54 METHOD(imv_database_t, get_database, database_t*,
55 private_imv_database_t *this)
56 {
57 return this->db;
58 }
59
60 /**
61 * Create a session entry in the IMV database
62 */
63 static bool create_session(private_imv_database_t *this, imv_session_t *session)
64 {
65 enumerator_t *enumerator, *e;
66 imv_os_info_t *os_info;
67 chunk_t device_id;
68 tncif_identity_t *tnc_id;
69 TNC_ConnectionID conn_id;
70 char *product, *device;
71 int session_id = 0, pid = 0, did = 0, trusted = 0, created;
72 bool first = TRUE, success = TRUE;
73
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 }
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 }
98
99 if (!pid)
100 {
101 DBG1(DBG_IMV, "imv_db: registering product info failed");
102 return FALSE;
103 }
104
105 /* get device ID string */
106 if (!session->get_device_id(session, &device_id))
107 {
108 DBG1(DBG_IMV, "imv_db: device ID is not available");
109 return FALSE;
110 }
111 device = strndup(device_id.ptr, device_id.len);
112
113 /* get primary key of device ID if it exists */
114 e = this->db->query(this->db,
115 "SELECT id, trusted FROM devices WHERE value = ? AND product = ?",
116 DB_TEXT, device, DB_INT, pid, DB_INT, DB_INT);
117 if (e)
118 {
119 e->enumerate(e, &did, &trusted);
120 e->destroy(e);
121 }
122
123 /* if device ID is trusted, set trust in session */
124 if (trusted)
125 {
126 session->set_device_trust(session, TRUE);
127 }
128
129 /* if device ID has not been found - register it */
130 if (!did)
131 {
132 this->db->execute(this->db, &did,
133 "INSERT INTO devices (value, product) VALUES (?, ?)",
134 DB_TEXT, device, DB_INT, pid);
135 }
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,
148 "INSERT INTO sessions (time, connection, product, device) "
149 "VALUES (?, ?, ?, ?)",
150 DB_INT, created, DB_INT, conn_id, DB_INT, pid, DB_INT, did);
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
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;
226 }
227
228 static 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)
242 {
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);
253 }
254 e->destroy(e);
255
256 return TRUE;
257 }
258
259 METHOD(imv_database_t, add_recommendation, void,
260 private_imv_database_t *this, imv_session_t *session,
261 TNC_IMV_Action_Recommendation rec)
262 {
263 /* add final recommendation to session DB entry */
264 this->db->execute(this->db, NULL,
265 "UPDATE sessions SET rec = ? WHERE id = ?",
266 DB_INT, rec, DB_INT, session->get_session_id(session, NULL, NULL));
267 }
268
269 METHOD(imv_database_t, policy_script, bool,
270 private_imv_database_t *this, imv_session_t *session, bool start)
271 {
272 char command[512], resp[128], *last;
273 FILE *shell;
274
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 }
282
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 */
299 snprintf(command, sizeof(command), "2>&1 %s %s %d",
300 this->script, start ? "start" : "stop",
301 session->get_session_id(session, NULL, NULL));
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
334 if (start)
335 {
336 /* add workitem list generated by policy manager to session object */
337 if (!add_workitems(this, session))
338 {
339 return FALSE;
340 }
341 session->set_policy_started(session, TRUE);
342 }
343 else
344 {
345 session->set_policy_started(session, FALSE);
346 }
347
348 return TRUE;
349 }
350
351 METHOD(imv_database_t, finalize_workitem, bool,
352 private_imv_database_t *this, imv_workitem_t *workitem)
353 {
354 char *result;
355 int rec;
356
357 rec = workitem->get_result(workitem, &result);
358
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;
363 }
364
365 METHOD(imv_database_t, destroy, void,
366 private_imv_database_t *this)
367 {
368 DESTROY_IF(this->db);
369 free(this);
370 }
371
372 /**
373 * See header
374 */
375 imv_database_t *imv_database_create(char *uri, char *script)
376 {
377 private_imv_database_t *this;
378
379 INIT(this,
380 .public = {
381 .get_database = _get_database,
382 .policy_script = _policy_script,
383 .finalize_workitem = _finalize_workitem,
384 .add_recommendation = _add_recommendation,
385 .destroy = _destroy,
386 },
387 .db = lib->db->create(lib->db, uri),
388 .script = script,
389 );
390
391 if (!this->db)
392 {
393 DBG1(DBG_IMV,
394 "failed to connect to IMV database '%s'", uri);
395 destroy(this);
396 return NULL;
397 }
398
399 return &this->public;
400 }