check boot_aggregate value
[strongswan.git] / src / libpts / pts / components / ita / ita_comp_ima.c
1 /*
2 * Copyright (C) 2011 Andreas Steffen
3 *
4 * HSR Hochschule fuer Technik Rapperswil
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 #include "ita_comp_ima.h"
18 #include "ita_comp_func_name.h"
19
20 #include "libpts.h"
21 #include "pts/components/pts_component.h"
22
23 #include <debug.h>
24 #include <pen/pen.h>
25
26 #include <sys/types.h>
27 #include <sys/stat.h>
28 #include <unistd.h>
29 #include <fcntl.h>
30 #include <errno.h>
31
32 #define SECURITY_DIR "/sys/kernel/security/"
33 #define IMA_BIOS_MEASUREMENTS SECURITY_DIR "tpm0/binary_bios_measurements"
34 #define IMA_RUNTIME_MEASUREMENTS SECURITY_DIR "ima/binary_runtime_measurements"
35 #define IMA_EVENT_NAME_LEN_MAX 255
36 #define IMA_PCR 10
37 #define IMA_PCR_MAX 16
38 #define IMA_TYPE_LEN 3
39
40 typedef struct pts_ita_comp_ima_t pts_ita_comp_ima_t;
41 typedef struct bios_entry_t bios_entry_t;
42 typedef struct ima_entry_t ima_entry_t;
43 typedef enum ima_state_t ima_state_t;
44
45 enum ima_state_t {
46 IMA_STATE_INIT,
47 IMA_STATE_BIOS,
48 IMA_STATE_BOOT_AGGREGATE,
49 IMA_STATE_RUNTIME,
50 IMA_STATE_END
51 };
52
53 /**
54 * Private data of a pts_ita_comp_ima_t object.
55 *
56 */
57 struct pts_ita_comp_ima_t {
58
59 /**
60 * Public pts_component_t interface.
61 */
62 pts_component_t public;
63
64 /**
65 * Component Functional Name
66 */
67 pts_comp_func_name_t *name;
68
69 /**
70 * AIK keyid
71 */
72 chunk_t keyid;
73
74 /**
75 * Sub-component depth
76 */
77 u_int32_t depth;
78
79 /**
80 * PTS measurement database
81 */
82 pts_database_t *pts_db;
83
84 /**
85 * Primary key for Component Functional Name database entry
86 */
87 int cid;
88
89 /**
90 * Primary key for AIK database entry
91 */
92 int kid;
93
94 /**
95 * Component is registering measurements
96 */
97 bool is_registering;
98
99 /**
100 * Measurement sequence number
101 */
102 int seq_no;
103
104 /**
105 * Expected IMA BIOS measurement count
106 */
107 int count;
108
109 /**
110 * IMA BIOS measurements
111 */
112 linked_list_t *bios_list;
113
114 /**
115 * IMA runtime file measurements
116 */
117 linked_list_t *ima_list;
118
119 /**
120 * Shadow PCR registers
121 */
122 chunk_t pcrs[IMA_PCR_MAX];
123
124 /**
125 * IMA measurement time
126 */
127 time_t measurement_time;
128
129 /**
130 * IMA state machine
131 */
132 ima_state_t state;
133
134 /**
135 * Hasher used to extend emulated PCRs
136 */
137 hasher_t *hasher;
138
139 };
140
141 /**
142 * Linux IMA BIOS measurement entry
143 */
144 struct bios_entry_t {
145
146 /**
147 * PCR register
148 */
149 u_int32_t pcr;
150
151 /**
152 * SHA1 measurement hash
153 */
154 chunk_t measurement;
155 };
156
157 /**
158 * Linux IMA runtime file measurement entry
159 */
160 struct ima_entry_t {
161
162 /**
163 * SHA1 measurement hash
164 */
165 chunk_t measurement;
166
167 /**
168 * SHA1 file measurement thash
169 */
170 chunk_t file_measurement;
171
172 /**
173 * absolute path of executable files or basename of dynamic libraries
174 */
175 chunk_t filename;
176 };
177
178 /**
179 * Free a bios_entry_t object
180 */
181 static void free_bios_entry(bios_entry_t *this)
182 {
183 free(this->measurement.ptr);
184 free(this);
185 }
186
187 /**
188 * Free an ima_entry_t object
189 */
190 static void free_ima_entry(ima_entry_t *this)
191 {
192 free(this->measurement.ptr);
193 free(this->file_measurement.ptr);
194 free(this->filename.ptr);
195 free(this);
196 }
197
198 /**
199 * Load a PCR measurement file and determine the creation date
200 */
201 static bool load_bios_measurements(char *file, linked_list_t *list,
202 time_t *created)
203 {
204 u_int32_t pcr, num, len;
205 bios_entry_t *entry;
206 struct stat st;
207 ssize_t res;
208 int fd;
209
210 fd = open(file, O_RDONLY);
211 if (fd == -1)
212 {
213 DBG1(DBG_PTS, "opening '%s' failed: %s", file, strerror(errno));
214 return FALSE;
215 }
216
217 if (fstat(fd, &st) == -1)
218 {
219 DBG1(DBG_PTS, "getting statistics of '%s' failed: %s", file,
220 strerror(errno));
221 close(fd);
222 return FALSE;
223 }
224 *created = st.st_ctime;
225
226 while (TRUE)
227 {
228 res = read(fd, &pcr, 4);
229 if (res == 0)
230 {
231 DBG2(DBG_PTS, "loaded bios measurements '%s' (%d entries)",
232 file, list->get_count(list));
233 close(fd);
234 return TRUE;
235 }
236
237 entry = malloc_thing(bios_entry_t);
238 entry->pcr = pcr;
239 entry->measurement = chunk_alloc(HASH_SIZE_SHA1);
240
241 if (res != 4)
242 {
243 break;
244 }
245 if (read(fd, &num, 4) != 4)
246 {
247 break;
248 }
249 if (read(fd, entry->measurement.ptr, HASH_SIZE_SHA1) != HASH_SIZE_SHA1)
250 {
251 break;
252 }
253 if (read(fd, &len, 4) != 4)
254 {
255 break;
256 }
257 if (lseek(fd, len, SEEK_CUR) == -1)
258 {
259 break;
260 }
261 list->insert_last(list, entry);
262 }
263
264 DBG1(DBG_PTS, "loading bios measurements '%s' failed: %s", file,
265 strerror(errno));
266 close(fd);
267 return FALSE;
268 }
269
270 /**
271 * Load an IMA runtime measurement file and determine the creation and
272 * update dates
273 */
274 static bool load_runtime_measurements(char *file, linked_list_t *list,
275 time_t *created)
276 {
277 u_int32_t pcr, len;
278 ima_entry_t *entry;
279 char type[IMA_TYPE_LEN];
280 struct stat st;
281 ssize_t res;
282 int fd;
283
284 fd = open(file, O_RDONLY);
285 if (fd == -1)
286 {
287 DBG1(DBG_PTS, "opening '%s' failed: %s", file, strerror(errno));
288 return TRUE;
289 }
290
291 if (fstat(fd, &st) == -1)
292 {
293 DBG1(DBG_PTS, "getting statistics of '%s' failed: %s", file,
294 strerror(errno));
295 close(fd);
296 return FALSE;
297 }
298 *created = st.st_ctime;
299
300 while (TRUE)
301 {
302 res = read(fd, &pcr, 4);
303 if (res == 0)
304 {
305 DBG2(DBG_PTS, "loaded ima measurements '%s' (%d entries)",
306 file, list->get_count(list));
307 close(fd);
308 return TRUE;
309 }
310
311 entry = malloc_thing(ima_entry_t);
312 entry->measurement = chunk_alloc(HASH_SIZE_SHA1);
313 entry->file_measurement = chunk_alloc(HASH_SIZE_SHA1);
314 entry->filename = chunk_empty;
315
316 if (res != 4 || pcr != IMA_PCR)
317 {
318 break;
319 }
320 if (read(fd, entry->measurement.ptr, HASH_SIZE_SHA1) != HASH_SIZE_SHA1)
321 {
322 break;
323 }
324 if (read(fd, &len, 4) != 4 || len != IMA_TYPE_LEN)
325 {
326 break;
327 }
328 if (read(fd, type, IMA_TYPE_LEN) != IMA_TYPE_LEN ||
329 memcmp(type, "ima", IMA_TYPE_LEN))
330 {
331 break;
332 }
333 if (read(fd, entry->file_measurement.ptr, HASH_SIZE_SHA1) != HASH_SIZE_SHA1)
334 {
335 break;
336 }
337 if (read(fd, &len, 4) != 4)
338 {
339 break;
340 }
341 entry->filename = chunk_alloc(len);
342 if (read(fd, entry->filename.ptr, len) != len)
343 {
344 break;
345 }
346 list->insert_last(list, entry);
347 }
348
349 DBG1(DBG_PTS, "loading ima measurements '%s' failed: %s",
350 file, strerror(errno));
351 close(fd);
352 return FALSE;
353 }
354
355 /**
356 * Extend measurement into PCR an create evidence
357 */
358 pts_comp_evidence_t* extend_pcr(pts_ita_comp_ima_t* this, u_int32_t pcr,
359 chunk_t measurement)
360 {
361 size_t pcr_len;
362 pts_pcr_transform_t pcr_transform;
363 pts_meas_algorithms_t hash_algo;
364 pts_comp_evidence_t *evidence;
365 chunk_t pcr_before, pcr_after;
366
367 hash_algo = PTS_MEAS_ALGO_SHA1;
368 pcr_len = HASH_SIZE_SHA1;
369 pcr_transform = pts_meas_algo_to_pcr_transform(hash_algo, pcr_len);
370 pcr_before = chunk_clone(this->pcrs[pcr]);
371 this->hasher->get_hash(this->hasher, pcr_before, NULL);
372 this->hasher->get_hash(this->hasher, measurement, this->pcrs[pcr].ptr);
373 pcr_after = chunk_clone(this->pcrs[pcr]);
374
375 evidence = pts_comp_evidence_create(this->name->clone(this->name),
376 this->depth, pcr, hash_algo, pcr_transform,
377 this->measurement_time, measurement);
378 evidence->set_pcr_info(evidence, pcr_before, pcr_after);
379
380 return evidence;
381 }
382
383 /**
384 * Compute and check boot aggregate value by hashing PCR0 to PCR7
385 */
386 void check_boot_aggregate(pts_ita_comp_ima_t *this, chunk_t measurement)
387 {
388 u_int32_t pcr;
389 u_char pcr_buffer[HASH_SIZE_SHA1];
390 u_char boot_aggregate_name[] = "boot_aggregate";
391 u_char filename_buffer[IMA_EVENT_NAME_LEN_MAX + 1];
392 chunk_t boot_aggregate, file_name;
393
394 /* See Linux kernel header: security/integrity/ima/ima.h */
395 boot_aggregate = chunk_create(pcr_buffer, sizeof(pcr_buffer));
396 memset(filename_buffer, 0, sizeof(filename_buffer));
397 strcpy(filename_buffer, boot_aggregate_name);
398 file_name = chunk_create(filename_buffer, sizeof(filename_buffer));
399
400 for (pcr = 0; pcr < 8; pcr++)
401 {
402 this->hasher->get_hash(this->hasher, this->pcrs[pcr], NULL);
403 }
404 this->hasher->get_hash(this->hasher, chunk_empty, pcr_buffer);
405 this->hasher->get_hash(this->hasher, boot_aggregate, NULL);
406 this->hasher->get_hash(this->hasher, file_name, pcr_buffer);
407
408 DBG1(DBG_PTS, "boot aggregate value is %scorrect",
409 chunk_equals(boot_aggregate, measurement) ? "":"in");
410 }
411
412 METHOD(pts_component_t, get_comp_func_name, pts_comp_func_name_t*,
413 pts_ita_comp_ima_t *this)
414 {
415 return this->name;
416 }
417
418 METHOD(pts_component_t, get_evidence_flags, u_int8_t,
419 pts_ita_comp_ima_t *this)
420 {
421 return PTS_REQ_FUNC_COMP_EVID_PCR;
422 }
423
424 METHOD(pts_component_t, get_depth, u_int32_t,
425 pts_ita_comp_ima_t *this)
426 {
427 return this->depth;
428 }
429
430 METHOD(pts_component_t, measure, status_t,
431 pts_ita_comp_ima_t *this, pts_t *pts, pts_comp_evidence_t **evidence)
432 {
433 bios_entry_t *bios_entry;
434 ima_entry_t *ima_entry;
435 status_t status;
436
437 switch (this->state)
438 {
439 case IMA_STATE_INIT:
440 if (!load_bios_measurements(IMA_BIOS_MEASUREMENTS, this->bios_list,
441 &this->measurement_time))
442 {
443 return FAILED;
444 }
445 this->state = IMA_STATE_BIOS;
446 /* fall through to next state */
447 case IMA_STATE_BIOS:
448 status = this->bios_list->remove_first(this->bios_list,
449 (void**)&bios_entry);
450 if (status != SUCCESS)
451 {
452 DBG1(DBG_PTS, "could not retrieve bios measurement entry");
453 return status;
454 }
455 *evidence = extend_pcr(this, bios_entry->pcr,
456 bios_entry->measurement);
457 free(bios_entry);
458
459 /* break if still some BIOS measurements are left */
460 if (this->bios_list->get_count(this->bios_list))
461 {
462 break;
463 }
464
465 /* check if IMA runtime measurements are enabled */
466 if (!load_runtime_measurements(IMA_RUNTIME_MEASUREMENTS,
467 this->ima_list, &this->measurement_time))
468 {
469 return FAILED;
470 }
471
472 this->state = this->ima_list->get_count(this->ima_list) ?
473 IMA_STATE_BOOT_AGGREGATE : IMA_STATE_END;
474 break;
475 case IMA_STATE_BOOT_AGGREGATE:
476 case IMA_STATE_RUNTIME:
477 status = this->ima_list->remove_first(this->ima_list,
478 (void**)&ima_entry);
479 if (status != SUCCESS)
480 {
481 DBG1(DBG_PTS, "could not retrieve ima measurement entry");
482 return status;
483 }
484 *evidence = extend_pcr(this, IMA_PCR, ima_entry->measurement);
485
486 if (this->state == IMA_STATE_BOOT_AGGREGATE)
487 {
488 check_boot_aggregate(this, ima_entry->measurement);
489 }
490
491 /* TODO optionally send file measurements */
492 chunk_free(&ima_entry->file_measurement);
493 chunk_free(&ima_entry->filename);
494 free(ima_entry);
495
496 this->state = this->ima_list->get_count(this->ima_list) ?
497 IMA_STATE_RUNTIME : IMA_STATE_END;
498 break;
499 case IMA_STATE_END:
500 /* shouldn't happen */
501 return FAILED;
502 }
503
504 return (this->state == IMA_STATE_END) ? SUCCESS : NEED_MORE;
505 }
506
507 METHOD(pts_component_t, verify, status_t,
508 pts_ita_comp_ima_t *this, pts_t *pts, pts_comp_evidence_t *evidence)
509 {
510 bool has_pcr_info;
511 u_int32_t extended_pcr, vid, name;
512 enum_name_t *names;
513 pts_meas_algorithms_t algo;
514 pts_pcr_transform_t transform;
515 time_t measurement_time;
516 chunk_t measurement, pcr_before, pcr_after;
517 status_t status;
518
519 measurement = evidence->get_measurement(evidence, &extended_pcr,
520 &algo, &transform, &measurement_time);
521
522 switch (this->state)
523 {
524 case IMA_STATE_INIT:
525 if (!pts->get_aik_keyid(pts, &this->keyid))
526 {
527 return FAILED;
528 }
529 this->keyid = chunk_clone(this->keyid);
530
531 if (!this->pts_db)
532 {
533 DBG1(DBG_PTS, "pts database not available");
534 return FAILED;
535 }
536 status = this->pts_db->get_comp_measurement_count(this->pts_db,
537 this->name, this->keyid, algo,
538 &this->cid, &this->kid, &this->count);
539 if (status != SUCCESS)
540 {
541 return status;
542 }
543 vid = this->name->get_vendor_id(this->name);
544 name = this->name->get_name(this->name);
545 names = pts_components->get_comp_func_names(pts_components, vid);
546
547 if (this->count)
548 {
549 DBG1(DBG_PTS, "checking %d %N '%N' functional component "
550 "evidence measurements", this->count, pen_names,
551 vid, names, name);
552 }
553 else
554 {
555 DBG1(DBG_PTS, "registering %N '%N' functional component "
556 "evidence measurements", pen_names, vid, names,
557 name);
558 this->is_registering = TRUE;
559 }
560 this->state = IMA_STATE_BIOS;
561 /* fall through to next state */
562 case IMA_STATE_BIOS:
563 if (extended_pcr != IMA_PCR)
564 {
565 if (this->is_registering)
566 {
567 status = this->pts_db->insert_comp_measurement(this->pts_db,
568 measurement, this->cid, this->kid,
569 ++this->seq_no, extended_pcr, algo);
570 if (status != SUCCESS)
571 {
572 return status;
573 }
574 this->count = this->seq_no + 1;
575 }
576 else
577 {
578 status = this->pts_db->check_comp_measurement(this->pts_db,
579 measurement, this->cid, this->kid,
580 ++this->seq_no, extended_pcr, algo);
581 if (status != SUCCESS)
582 {
583 return status;
584 }
585 }
586 break;
587 }
588 this->state = IMA_STATE_BOOT_AGGREGATE;
589 /* fall through to next state */
590 case IMA_STATE_BOOT_AGGREGATE:
591 this->state = IMA_STATE_RUNTIME;
592 break;
593 case IMA_STATE_RUNTIME:
594 break;
595 case IMA_STATE_END:
596 break;
597 }
598
599 has_pcr_info = evidence->get_pcr_info(evidence, &pcr_before, &pcr_after);
600 if (has_pcr_info)
601 {
602 if (!pts->add_pcr(pts, extended_pcr, pcr_before, pcr_after))
603 {
604 return FAILED;
605 }
606 }
607
608 return SUCCESS;
609 }
610
611 METHOD(pts_component_t, finalize, bool,
612 pts_ita_comp_ima_t *this)
613 {
614 u_int32_t vid, name;
615 enum_name_t *names;
616
617 vid = this->name->get_vendor_id(this->name);
618 name = this->name->get_name(this->name);
619 names = pts_components->get_comp_func_names(pts_components, vid);
620
621 if (this->is_registering)
622 {
623 /* close registration */
624 this->is_registering = FALSE;
625
626 DBG1(DBG_PTS, "registered %d %N '%N' functional component evidence "
627 "measurements", this->seq_no, pen_names, vid, names, name);
628 }
629 else if (this->seq_no < this->count)
630 {
631 DBG1(DBG_PTS, "%d of %d %N '%N' functional component evidence "
632 "measurements missing", this->count - this->seq_no,
633 this->count, pen_names, vid, names, name);
634 return FALSE;
635 }
636
637 return TRUE;
638 }
639
640 METHOD(pts_component_t, destroy, void,
641 pts_ita_comp_ima_t *this)
642 {
643 int i, count;
644 u_int32_t vid, name;
645 enum_name_t *names;
646
647 for (i = 0; i < IMA_PCR_MAX; i++)
648 {
649 free(this->pcrs[i].ptr);
650 }
651 if (this->is_registering)
652 {
653 count = this->pts_db->delete_comp_measurements(this->pts_db,
654 this->cid, this->kid);
655 vid = this->name->get_vendor_id(this->name);
656 name = this->name->get_name(this->name);
657 names = pts_components->get_comp_func_names(pts_components, vid);
658 DBG1(DBG_PTS, "deleted %d registered %N '%N' functional component "
659 "evidence measurements", count, pen_names, vid, names, name);
660 }
661 this->bios_list->destroy_function(this->bios_list, (void *)free_bios_entry);
662 this->ima_list->destroy_function(this->ima_list, (void *)free_ima_entry);
663 this->name->destroy(this->name);
664 this->hasher->destroy(this->hasher);
665 free(this->keyid.ptr);
666 free(this);
667 }
668
669 /**
670 * See header
671 */
672 pts_component_t *pts_ita_comp_ima_create(u_int8_t qualifier, u_int32_t depth,
673 pts_database_t *pts_db)
674 {
675 pts_ita_comp_ima_t *this;
676 int i;
677
678 INIT(this,
679 .public = {
680 .get_comp_func_name = _get_comp_func_name,
681 .get_evidence_flags = _get_evidence_flags,
682 .get_depth = _get_depth,
683 .measure = _measure,
684 .verify = _verify,
685 .finalize = _finalize,
686 .destroy = _destroy,
687 },
688 .name = pts_comp_func_name_create(PEN_ITA, PTS_ITA_COMP_FUNC_NAME_IMA,
689 qualifier),
690 .depth = depth,
691 .pts_db = pts_db,
692 .bios_list = linked_list_create(),
693 .ima_list = linked_list_create(),
694 .hasher = lib->crypto->create_hasher(lib->crypto, HASH_SHA1),
695 );
696
697 for (i = 0; i < IMA_PCR_MAX; i++)
698 {
699 this->pcrs[i] = chunk_alloc(HASH_SIZE_SHA1);
700 memset(this->pcrs[i].ptr, 0x00, HASH_SIZE_SHA1);
701 }
702 return &this->public;
703 }
704