implemented Linux IMA functional component
[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 "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 IMA_SECURITY_DIR "/sys/kernel/security/tpm0/"
32 #define IMA_BIOS_MEASUREMENT_PATH IMA_SECURITY_DIR "binary_bios_measurements"
33 #define IMA_PCR_MAX 8
34 #define IMA_SEQUENCE 126
35
36 typedef struct pts_ita_comp_ima_t pts_ita_comp_ima_t;
37
38 /**
39 * Private data of a pts_ita_comp_ima_t object.
40 *
41 */
42 struct pts_ita_comp_ima_t {
43
44 /**
45 * Public pts_component_t interface.
46 */
47 pts_component_t public;
48
49 /**
50 * Component Functional Name
51 */
52 pts_comp_func_name_t *name;
53
54 /**
55 * Sub-component depth
56 */
57 u_int32_t depth;
58
59 /**
60 * IMA BIOS measurement time
61 */
62 time_t bios_measurement_time;
63
64 /**
65 * IMA BIOS measurements
66 */
67 linked_list_t *list;
68
69 /**
70 * Measurement sequence number
71 */
72 int seq_no;
73
74 /**
75 * Shadow PCR registers
76 */
77 chunk_t pcrs[IMA_PCR_MAX];
78 };
79
80 typedef struct entry_t entry_t;
81
82 /**
83 * Linux IMA measurement entry
84 */
85 struct entry_t {
86
87 /**
88 * PCR register
89 */
90 u_int32_t pcr;
91
92 /**
93 * SHA1 measurement hash
94 */
95 chunk_t measurement;
96 };
97
98 /**
99 * Free an entry_t object
100 */
101 static void free_entry(entry_t *this)
102 {
103 free(this->measurement.ptr);
104 free(this);
105 }
106
107 /**
108 * Load a PCR measurement file and determine the creation date
109 */
110 static bool load_measurements(char *file, linked_list_t *list, time_t *created)
111 {
112 u_int32_t pcr, num, len;
113 entry_t *entry;
114 struct stat st;
115 ssize_t res;
116 int fd;
117
118 fd = open(file, O_RDONLY);
119 if (fd == -1)
120 {
121 DBG1(DBG_PTS, " opening '%s' failed: %s", file, strerror(errno));
122 return FALSE;
123 }
124
125 if (fstat(fd, &st) == -1)
126 {
127 DBG1(DBG_PTS, " getting statistics of '%s' failed: %s", file,
128 strerror(errno));
129 close(fd);
130 return FALSE;
131 }
132 *created = st.st_ctime;
133
134 while (TRUE)
135 {
136 res = read(fd, &pcr, 4);
137 if (res == 0)
138 {
139 DBG2(DBG_PTS, "loaded bios measurements '%s' (%d entries)",
140 file, list->get_count(list));
141 close(fd);
142 return TRUE;
143 }
144
145 entry = malloc_thing(entry_t);
146 entry->pcr = pcr;
147 entry->measurement = chunk_alloc(HASH_SIZE_SHA1);
148
149 if (res != 4)
150 {
151 break;
152 }
153 if (read(fd, &num, 4) != 4)
154 {
155 break;
156 }
157 if (read(fd, entry->measurement.ptr, HASH_SIZE_SHA1) != HASH_SIZE_SHA1)
158 {
159 break;
160 }
161 if (read(fd, &len, 4) != 4)
162 {
163 break;
164 }
165 if (lseek(fd, len, SEEK_CUR) == -1)
166 {
167 break;
168 }
169 list->insert_last(list, entry);
170 }
171
172 DBG1(DBG_PTS, "loading bios measurements '%s' failed: %s",
173 file, strerror(errno));
174 close(fd);
175 return FALSE;
176 }
177
178 METHOD(pts_component_t, get_comp_func_name, pts_comp_func_name_t*,
179 pts_ita_comp_ima_t *this)
180 {
181 return this->name;
182 }
183
184 METHOD(pts_component_t, get_evidence_flags, u_int8_t,
185 pts_ita_comp_ima_t *this)
186 {
187 return PTS_REQ_FUNC_COMP_EVID_PCR;
188 }
189
190 METHOD(pts_component_t, get_depth, u_int32_t,
191 pts_ita_comp_ima_t *this)
192 {
193 return this->depth;
194 }
195
196 METHOD(pts_component_t, measure, status_t,
197 pts_ita_comp_ima_t *this, pts_t *pts, pts_comp_evidence_t **evidence)
198 {
199 pts_comp_evidence_t *evid;
200 chunk_t pcr_before, pcr_after;
201 pts_pcr_transform_t pcr_transform;
202 pts_meas_algorithms_t hash_algo;
203 size_t pcr_len;
204 entry_t *entry;
205 hasher_t *hasher;
206
207 hash_algo = PTS_MEAS_ALGO_SHA1;
208 pcr_len = pts->get_pcr_len(pts);
209 pcr_transform = pts_meas_algo_to_pcr_transform(hash_algo, pcr_len);
210
211 if (this->list->get_count(this->list) == 0)
212 {
213 if (!load_measurements(IMA_BIOS_MEASUREMENT_PATH, this->list,
214 &this->bios_measurement_time))
215 {
216 return FAILED;
217 }
218 }
219
220 if (this->list->remove_first(this->list, (void**)&entry) != SUCCESS)
221 {
222 DBG1(DBG_PTS, "could not retrieve measurement entry");
223 return FAILED;
224 }
225
226 pcr_before = chunk_clone(this->pcrs[entry->pcr]);
227
228 hasher = lib->crypto->create_hasher(lib->crypto, HASH_SHA1);
229 hasher->get_hash(hasher, pcr_before, NULL);
230 hasher->get_hash(hasher, entry->measurement, this->pcrs[entry->pcr].ptr);
231 hasher->destroy(hasher);
232
233 pcr_after = chunk_clone(this->pcrs[entry->pcr]);
234
235 evid = *evidence = pts_comp_evidence_create(this->name->clone(this->name),
236 this->depth, entry->pcr, hash_algo, pcr_transform,
237 this->bios_measurement_time, entry->measurement);
238 evid->set_pcr_info(evid, pcr_before, pcr_after);
239
240 free(entry);
241
242 return (this->list->get_count(this->list)) ? NEED_MORE : SUCCESS;
243 }
244
245 METHOD(pts_component_t, verify, status_t,
246 pts_ita_comp_ima_t *this, pts_t *pts, pts_database_t *pts_db,
247 pts_comp_evidence_t *evidence)
248 {
249 bool has_pcr_info;
250 char *platform_info;
251 u_int32_t extended_pcr;
252 pts_meas_algorithms_t algo;
253 pts_pcr_transform_t transform;
254 time_t measurement_time;
255 chunk_t measurement, pcr_before, pcr_after;
256
257 platform_info = pts->get_platform_info(pts);
258 if (!pts_db || !platform_info)
259 {
260 DBG1(DBG_PTS, "%s%s%s not available",
261 (pts_db) ? "" : "pts database",
262 (!pts_db && !platform_info) ? "and" : "",
263 (platform_info) ? "" : "platform info");
264 return FAILED;
265 }
266 measurement = evidence->get_measurement(evidence, &extended_pcr,
267 &algo, &transform, &measurement_time);
268
269 if (pts_db->check_comp_measurement(pts_db, measurement, this->name,
270 platform_info, ++this->seq_no, extended_pcr, algo) != SUCCESS)
271 {
272 return FAILED;
273 }
274
275 has_pcr_info = evidence->get_pcr_info(evidence, &pcr_before, &pcr_after);
276 if (has_pcr_info)
277 {
278 if (!pts->add_pcr(pts, extended_pcr, pcr_before, pcr_after))
279 {
280 return FAILED;
281 }
282 }
283
284 return (this->seq_no < IMA_SEQUENCE) ? NEED_MORE : SUCCESS;
285 }
286
287 METHOD(pts_component_t, destroy, void,
288 pts_ita_comp_ima_t *this)
289 {
290 int i;
291
292 for (i = 0; i < IMA_PCR_MAX; i++)
293 {
294 free(this->pcrs[i].ptr);
295 }
296 this->list->destroy_function(this->list, (void *)free_entry);
297 this->name->destroy(this->name);
298 free(this);
299 }
300
301 /**
302 * See header
303 */
304 pts_component_t *pts_ita_comp_ima_create(u_int8_t qualifier, u_int32_t depth)
305 {
306 pts_ita_comp_ima_t *this;
307 int i;
308
309 INIT(this,
310 .public = {
311 .get_comp_func_name = _get_comp_func_name,
312 .get_evidence_flags = _get_evidence_flags,
313 .get_depth = _get_depth,
314 .measure = _measure,
315 .verify = _verify,
316 .destroy = _destroy,
317 },
318 .name = pts_comp_func_name_create(PEN_ITA, PTS_ITA_COMP_FUNC_NAME_IMA,
319 qualifier),
320 .depth = depth,
321 .list = linked_list_create(),
322 );
323
324 for (i = 0; i < IMA_PCR_MAX; i++)
325 {
326 this->pcrs[i] = chunk_alloc(HASH_SIZE_SHA1);
327 memset(this->pcrs[i].ptr, 0x00, HASH_SIZE_SHA1);
328 }
329 return &this->public;
330 }
331