/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 |