Coverage Report

Created: 2025-10-12 07:18

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
}