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