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