Coverage Report

Created: 2025-03-19 07:30

/src/osquery/osquery/tables/system/linux/disk_encryption.cpp
Line
Count
Source (jump to first uncovered line)
1
/**
2
 * Copyright (c) 2014-present, The osquery authors
3
 *
4
 * This source code is licensed as defined by the LICENSE file found in the
5
 * root directory of this source tree.
6
 *
7
 * SPDX-License-Identifier: (Apache-2.0 OR GPL-2.0-only)
8
 */
9
10
#include <unistd.h>
11
12
#include <vector>
13
14
#include <osquery/core/core.h>
15
#include <osquery/core/tables.h>
16
#include <osquery/logger/logger.h>
17
#include <osquery/sql/sql.h>
18
#include <osquery/utils/conversions/join.h>
19
20
extern "C" {
21
#include <libcryptsetup.h>
22
}
23
24
// FIXME: Add enum generation for tables and remove following code
25
// Copy of values in disk_encryption.mm
26
const std::string kEncryptionStatusEncrypted = "encrypted";
27
const std::string kEncryptionStatusUndefined = "undefined";
28
const std::string kEncryptionStatusNotEncrypted = "not encrypted";
29
30
namespace osquery {
31
namespace tables {
32
void genFDEStatusForBlockDevice(const Row& block_device,
33
                                std::map<std::string, Row>& block_devices,
34
0
                                std::map<std::string, Row>& encrypted_rows) {
35
0
  const auto name = block_device.at("name");
36
0
  const auto parent_name =
37
0
      (block_device.count("parent") > 0 ? block_device.at("parent") : "");
38
39
0
  Row r;
40
0
  r["name"] = name;
41
0
  r["uuid"] = (block_device.count("uuid") > 0) ? block_device.at("uuid") : "";
42
43
0
  struct crypt_device* cd = nullptr;
44
0
  auto ci = crypt_status(cd, name.c_str());
45
46
0
  switch (ci) {
47
0
  case CRYPT_ACTIVE:
48
0
  case CRYPT_BUSY: {
49
0
    r["encrypted"] = "1";
50
0
    r["encryption_status"] = kEncryptionStatusEncrypted;
51
0
    r["type"] = "";
52
53
0
    auto crypt_init = crypt_init_by_name_and_header(&cd, name.c_str(), nullptr);
54
0
    if (crypt_init < 0) {
55
0
      VLOG(1) << "Unable to initialize crypt device for " << name;
56
0
      break;
57
0
    }
58
59
0
    struct crypt_active_device cad;
60
0
    if (crypt_get_active_device(cd, name.c_str(), &cad) < 0) {
61
0
      VLOG(1) << "Unable to get active device for " << name;
62
0
      break;
63
0
    }
64
65
    // Construct the "type" with the cipher and mode too.
66
0
    std::vector<std::string> items;
67
68
0
    auto ctype = crypt_get_type(cd);
69
0
    if (ctype != nullptr) {
70
0
      items.push_back(ctype);
71
0
    }
72
73
0
    auto ccipher = crypt_get_cipher(cd);
74
0
    if (ccipher != nullptr) {
75
0
      items.push_back(ccipher);
76
0
    }
77
78
0
    auto ccipher_mode = crypt_get_cipher_mode(cd);
79
0
    if (ccipher_mode != nullptr) {
80
0
      items.push_back(ccipher_mode);
81
0
    }
82
83
0
    r["type"] = osquery::join(items, "-");
84
0
    break;
85
0
  }
86
  // If there's no good crypt status, use the parent device's crypt status.
87
0
  default:
88
    // If there is no parent, we are likely at the root of the block device
89
    // tree. Since no good crypt status has been found, we set the empty status
90
    // and exit. All children of this block device will inherit this status if
91
    // they aren't encrypted.
92
0
    if (parent_name.empty()) {
93
0
      r["encryption_status"] = kEncryptionStatusNotEncrypted;
94
0
      r["encrypted"] = "0";
95
0
      r["type"] = "";
96
0
      break;
97
0
    }
98
99
    // If there is a parent, let's generate and use its crypt status for this
100
    // device.
101
0
    if (!encrypted_rows.count(parent_name)) {
102
0
      genFDEStatusForBlockDevice(
103
0
          block_devices[parent_name], block_devices, encrypted_rows);
104
0
    }
105
106
    // The recursive calls return back, and each child device takes the
107
    // encryption values of their parent.
108
0
    auto parent_row = encrypted_rows[parent_name];
109
0
    r["encryption_status"] = parent_row["encryption_status"];
110
0
    r["encrypted"] = parent_row["encrypted"];
111
0
    r["type"] = parent_row["type"];
112
0
  }
113
114
0
  encrypted_rows[name] = r;
115
116
0
  if (cd != nullptr) {
117
0
    crypt_free(cd);
118
0
  }
119
0
}
120
121
0
QueryData genFDEStatus(QueryContext& context) {
122
0
  QueryData results;
123
124
0
  if (getuid() || geteuid()) {
125
0
    VLOG(1) << "Not running as root, disk encryption status not available";
126
0
    return results;
127
0
  }
128
129
  // When a block device doesn't have sufficient crypt status, it needs to be
130
  // able to inherit the crypt status of its parent.
131
  // To do this, we utilize `block_devices` and `encrypted_rows` to cache block
132
  // devices at two different points. The first is when it's queried, and the
133
  // second is after setting crypt status. This helps us avoid O(N^2) issues.
134
  // We can also skip sorting nodes by using recursion.
135
0
  std::map<std::string, Row> block_devices;
136
0
  std::map<std::string, Row> encrypted_rows;
137
138
  // Ultimately we want to have proper query context here. There are underlying
139
  // issues with udev child->parent relationship on LVM volumes. See #8152.
140
0
  auto data = SQL::selectAllFrom("block_devices");
141
0
  for (const auto& row : data) {
142
0
    if (row.count("name") > 0) {
143
0
      block_devices[row.at("name")] = row;
144
0
    }
145
0
  }
146
147
  // Generate and add an encryption row result for each queried block device.
148
0
  for (const auto& pair : block_devices) {
149
0
    if (!encrypted_rows.count(pair.first)) {
150
0
      genFDEStatusForBlockDevice(pair.second, block_devices, encrypted_rows);
151
0
    }
152
153
    // Copy encrypted rows back to results.
154
0
    results.push_back(encrypted_rows[pair.first]);
155
0
  }
156
157
0
  return results;
158
0
}
159
} // namespace tables
160
} // namespace osquery