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