]>
Commit | Line | Data |
---|---|---|
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 |
30 | typedef 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 | */ |
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 | ||
bb9d8b18 AS |
47 | /** |
48 | * policy script | |
49 | */ | |
50 | char *script; | |
51 | ||
b8db66de AS |
52 | }; |
53 | ||
4894bfa2 AS |
54 | METHOD(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 | */ | |
63 | static 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 | ||
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) | |
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 |
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 | { | |
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 | 269 | METHOD(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 |
351 | METHOD(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 |
365 | METHOD(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 | 375 | imv_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 | } |