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