2c798cf0a414e32c7a63a5d05966a5e2fd50daaf
[strongswan.git] / src / libimcv / pts / pts_ima_bios_list.c
1 /*
2 * Copyright (C) 2011-2020 Andreas Steffen
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
16 #include "pts_ima_bios_list.h"
17
18 #include <utils/debug.h>
19
20 #include <sys/types.h>
21 #include <sys/stat.h>
22 #include <unistd.h>
23 #include <fcntl.h>
24 #include <errno.h>
25
26 typedef struct private_pts_ima_bios_list_t private_pts_ima_bios_list_t;
27 typedef struct bios_entry_t bios_entry_t;
28 typedef enum event_type_t event_type_t;
29
30 /* BIOS Events (TCG PC Client Specification for Conventional BIOS 1.21) */
31 enum event_type_t {
32 EV_PREBOOT_CERT = 0x00000000,
33 EV_POST_CODE = 0x00000001,
34 EV_UNUSED = 0x00000002,
35 EV_NO_ACTION = 0x00000003,
36 EV_SEPARATOR = 0x00000004,
37 EV_ACTION = 0x00000005,
38 EV_EVENT_TAG = 0x00000006,
39 EV_S_CRTM_CONTENTS = 0x00000007,
40 EV_S_CRTM_VERSION = 0x00000008,
41 EV_CPU_MICROCODE = 0x00000009,
42 EV_PLATFORM_CONFIG_FLAGS = 0x0000000A,
43 EV_TABLE_OF_DEVICES = 0x0000000B,
44 EV_COMPACT_HASH = 0x0000000C,
45 EV_IPL = 0x0000000D,
46 EV_IPL_PARTITION_DATA = 0x0000000E,
47 EV_NONHOST_CODE = 0x0000000F,
48 EV_NONHOST_CONFIG = 0x00000010,
49 EV_NONHOST_INFO = 0x00000011,
50 EV_OMIT_BOOT_DEVICE_EVENTS = 0x00000012,
51
52 /* EFI Events (TCG EFI Platform Specification 1.22) */
53 EV_EFI_VARIABLE_DRIVER_CONFIG = 0x70000001,
54 EV_EFI_VARIABLE_BOOT = 0x70000002,
55 EV_EFI_BOOT_SERVICES_APPLICATION = 0x70000003,
56 EV_EFI_BOOT_SERVICES_DRIVER = 0x70000004,
57 EV_EFI_RUNTIME_SERVICES_DRIVER = 0x70000005,
58 EV_EFI_GPT_EVENT = 0x70000006,
59 EV_EFI_ACTION = 0x70000007,
60 EV_EFI_PLATFORM_FIRMWARE_BLOB = 0x70000008,
61 EV_EFI_HANDOFF_TABLES = 0x70000009,
62
63 EV_EFI_HCRTM_EVENT = 0x70000010,
64
65 EV_EFI_VARIABLE_AUTHORITY = 0x700000E0,
66 };
67
68 /* By subtracting an offset from EFI events the enum will always be positive */
69 #define EV_EFI_EVENT_BASE 0x80000000
70 #define EV_EFI_OFFSET 0x10000000
71
72 ENUM_BEGIN(event_type_names, EV_PREBOOT_CERT, EV_OMIT_BOOT_DEVICE_EVENTS,
73 "Preboot Cert",
74 "POST Code",
75 "Unused",
76 "No Action",
77 "Separator",
78 "Action",
79 "Event Tag",
80 "S-CRTM Contents",
81 "S-CRTM Version",
82 "CPU Microcode",
83 "Platform Config Flags",
84 "Table of Devices",
85 "Compact Hash",
86 "IPL",
87 "IPL Partition Data",
88 "Nonhost Code",
89 "Nonhost Config",
90 "Nonhost Info",
91 "Omit Boot Device Events"
92 );
93 ENUM_NEXT(event_type_names, EV_EFI_VARIABLE_DRIVER_CONFIG, EV_EFI_HANDOFF_TABLES,
94 EV_OMIT_BOOT_DEVICE_EVENTS,
95 "EFI Variable Driver Config",
96 "EFI Variable Boot",
97 "EFI Boot Services Application",
98 "EFI Boot Services Driver",
99 "EFI Runtime Services Driver",
100 "EFI GPT Event",
101 "EFI Action",
102 "EFI Platform Firmware Blob",
103 "EFI Handoff Tables"
104 );
105 ENUM_NEXT(event_type_names, EV_EFI_HCRTM_EVENT, EV_EFI_HCRTM_EVENT,
106 EV_EFI_HANDOFF_TABLES,
107 "EFI HCRTM Event"
108 );
109 ENUM_NEXT(event_type_names, EV_EFI_VARIABLE_AUTHORITY, EV_EFI_VARIABLE_AUTHORITY,
110 EV_EFI_HCRTM_EVENT,
111 "EFI Variable Authority"
112 );
113 ENUM_END(event_type_names, EV_EFI_VARIABLE_AUTHORITY);
114
115 /**
116 * Private data of a pts_ima_bios_list_t object.
117 *
118 */
119 struct private_pts_ima_bios_list_t {
120
121 /**
122 * Public pts_ima_bios_list_t interface.
123 */
124 pts_ima_bios_list_t public;
125
126 /**
127 * List of BIOS measurement entries
128 */
129 linked_list_t *list;
130
131 /**
132 * Time when BIOS measurements were taken
133 */
134 time_t creation_time;
135
136 };
137
138 /**
139 * Linux IMA BIOS measurement entry
140 */
141 struct bios_entry_t {
142
143 /**
144 * PCR register
145 */
146 uint32_t pcr;
147
148 /**
149 * SHA1 measurement hash
150 */
151 chunk_t measurement;
152 };
153
154 /**
155 * Free a bios_entry_t object
156 */
157 static void free_bios_entry(bios_entry_t *this)
158 {
159 free(this->measurement.ptr);
160 free(this);
161 }
162
163 METHOD(pts_ima_bios_list_t, get_time, time_t,
164 private_pts_ima_bios_list_t *this)
165 {
166 return this->creation_time;
167 }
168
169 METHOD(pts_ima_bios_list_t, get_count, int,
170 private_pts_ima_bios_list_t *this)
171 {
172 return this->list->get_count(this->list);
173 }
174
175 METHOD(pts_ima_bios_list_t, get_next, status_t,
176 private_pts_ima_bios_list_t *this, uint32_t *pcr, chunk_t *measurement)
177 {
178 bios_entry_t *entry;
179 status_t status;
180
181 status = this->list->remove_first(this->list, (void**)&entry);
182 *pcr = entry->pcr;
183 *measurement = entry->measurement;
184 free(entry);
185
186 return status;
187 }
188
189 METHOD(pts_ima_bios_list_t, destroy, void,
190 private_pts_ima_bios_list_t *this)
191 {
192 this->list->destroy_function(this->list, (void *)free_bios_entry);
193 free(this);
194 }
195
196 /**
197 * See header
198 */
199 pts_ima_bios_list_t* pts_ima_bios_list_create(tpm_tss_t *tpm, char *file,
200 pts_meas_algorithms_t algo)
201 {
202 private_pts_ima_bios_list_t *this;
203 uint32_t pcr, ev_type, event_type, event_len, seek_len, count = 1;
204 uint32_t buf_len = 8192;
205 uint8_t event_buf[buf_len];
206 hash_algorithm_t hash_alg;
207 chunk_t event;
208 bios_entry_t *entry;
209 struct stat st;
210 ssize_t res;
211 int fd;
212
213 if (!tpm)
214 {
215 DBG1(DBG_PTS, "no TPM available");
216 return NULL;
217 }
218
219 fd = open(file, O_RDONLY);
220 if (fd == -1)
221 {
222 DBG1(DBG_PTS, "opening '%s' failed: %s", file, strerror(errno));
223 return NULL;
224 }
225
226 if (fstat(fd, &st) == -1)
227 {
228 DBG1(DBG_PTS, "getting statistics of '%s' failed: %s", file,
229 strerror(errno));
230 close(fd);
231 return FALSE;
232 }
233 hash_alg = pts_meas_algo_to_hash(algo);
234
235 INIT(this,
236 .public = {
237 .get_time = _get_time,
238 .get_count = _get_count,
239 .get_next = _get_next,
240 .destroy = _destroy,
241 },
242 .creation_time = st.st_ctime,
243 .list = linked_list_create(),
244 );
245
246 DBG2(DBG_PTS, "No. PCR Event Type (Size)");
247 while (TRUE)
248 {
249 res = read(fd, &pcr, 4);
250 if (res == 0)
251 {
252 DBG2(DBG_PTS, "loaded bios measurements '%s' (%d entries)",
253 file, this->list->get_count(this->list));
254 close(fd);
255 return &this->public;
256 }
257
258 entry = malloc_thing(bios_entry_t);
259 entry->pcr = pcr;
260 entry->measurement = chunk_empty;
261
262 if (res != 4)
263 {
264 break;
265 }
266 if (read(fd, &event_type, 4) != 4)
267 {
268 break;
269 }
270 if (!tpm->get_event_digest(tpm, fd, hash_alg, &entry->measurement))
271 {
272 break;
273 }
274 if (read(fd, &event_len, 4) != 4)
275 {
276 break;
277 }
278 ev_type = (event_type < EV_EFI_EVENT_BASE) ?
279 event_type : event_type - EV_EFI_OFFSET;
280 DBG2(DBG_PTS, "%3u %2u %N (%u bytes)", count, pcr, event_type_names,
281 ev_type, event_len);
282 seek_len = (event_len > buf_len) ? event_len - buf_len : 0;
283 event_len -= seek_len;
284
285 if (read(fd, event_buf, event_len) != event_len)
286 {
287 break;
288 }
289
290 switch (event_type)
291 {
292 case EV_PREBOOT_CERT:
293 case EV_POST_CODE:
294 case EV_NO_ACTION:
295 case EV_ACTION:
296 case EV_EFI_ACTION:
297 case EV_S_CRTM_CONTENTS:
298 case EV_IPL:
299 if (event_type == EV_NO_ACTION && event_len == 17 &&
300 streq(event_buf, "StartupLocality"))
301 {
302 DBG2(DBG_PTS, " 'StartupLocality' %x", event_buf[16]);
303 }
304 else
305 {
306 DBG2(DBG_PTS, " '%.*s'", event_len, event_buf);
307 }
308 break;
309 default:
310 break;
311 }
312 event = chunk_create(event_buf, event_len);
313 DBG3(DBG_PTS,"%B", &event);
314
315 if (seek_len > 0 && lseek(fd, seek_len, SEEK_CUR) == -1)
316 {
317 break;
318 }
319 if (event_type == EV_NO_ACTION || entry->measurement.len == 0)
320 {
321 free_bios_entry(entry);
322 DBG2(DBG_PTS, " Not extended into PCR!");
323 }
324 else
325 {
326 this->list->insert_last(this->list, entry);
327 count++;
328 }
329 }
330
331 DBG1(DBG_PTS, "loading bios measurements '%s' failed", file);
332 free_bios_entry(entry);
333 close(fd);
334 destroy(this);
335
336 return NULL;
337 }