Inserted missing "!" mark
[strongswan.git] / src / libimcv / tcg / pts / pts.c
1 /*
2 * Copyright (C) 2011 Sansar Choinyambuu
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 "pts.h"
17
18 #include <debug.h>
19 #include <crypto/hashers/hasher.h>
20
21 #include <trousers/tss.h>
22 #include <trousers/trousers.h>
23
24 #include <sys/stat.h>
25 #include <errno.h>
26
27 #define PTS_BUF_SIZE 4096
28
29 typedef struct private_pts_t private_pts_t;
30
31 /**
32 * Private data of a pts_t object.
33 *
34 */
35 struct private_pts_t {
36
37 /**
38 * Public pts_t interface.
39 */
40 pts_t public;
41
42 /**
43 * PTS Protocol Capabilities
44 */
45 pts_proto_caps_flag_t proto_caps;
46
47 /**
48 * PTS Measurement Algorithm
49 */
50 pts_meas_algorithms_t algorithm;
51
52 /**
53 * Platform and OS Info
54 */
55 char *platform_info;
56
57 /**
58 * Do we have an activated TPM
59 */
60 bool has_tpm;
61
62 /**
63 * Contains a TPM_CAP_VERSION_INFO struct
64 */
65 chunk_t tpm_version_info;
66
67 /**
68 * Contains a Attestation Identity Key or Certificate
69 */
70 certificate_t *aik;
71
72 };
73
74 METHOD(pts_t, get_proto_caps, pts_proto_caps_flag_t,
75 private_pts_t *this)
76 {
77 return this->proto_caps;
78 }
79
80 METHOD(pts_t, set_proto_caps, void,
81 private_pts_t *this, pts_proto_caps_flag_t flags)
82 {
83 this->proto_caps = flags;
84 DBG2(DBG_IMC, "supported PTS protocol capabilities: %s%s%s%s%s",
85 flags & PTS_PROTO_CAPS_C ? "C" : ".",
86 flags & PTS_PROTO_CAPS_V ? "V" : ".",
87 flags & PTS_PROTO_CAPS_D ? "D" : ".",
88 flags & PTS_PROTO_CAPS_T ? "T" : ".",
89 flags & PTS_PROTO_CAPS_X ? "X" : ".");
90 }
91
92 METHOD(pts_t, get_meas_algorithm, pts_meas_algorithms_t,
93 private_pts_t *this)
94 {
95 return this->algorithm;
96 }
97
98 METHOD(pts_t, set_meas_algorithm, void,
99 private_pts_t *this, pts_meas_algorithms_t algorithm)
100 {
101 hash_algorithm_t hash_alg;
102
103 hash_alg = pts_meas_to_hash_algorithm(algorithm);
104 DBG2(DBG_IMC, "selected PTS measurement algorithm is %N",
105 hash_algorithm_names, hash_alg);
106 if (hash_alg != HASH_UNKNOWN)
107 {
108 this->algorithm = algorithm;
109 }
110 }
111
112 /**
113 * Print TPM 1.2 Version Info
114 */
115 static void print_tpm_version_info(private_pts_t *this)
116 {
117 TPM_CAP_VERSION_INFO versionInfo;
118 UINT64 offset = 0;
119 TSS_RESULT result;
120
121 result = Trspi_UnloadBlob_CAP_VERSION_INFO(&offset,
122 this->tpm_version_info.ptr, &versionInfo);
123 if (result != TSS_SUCCESS)
124 {
125 DBG1(DBG_IMC, "could not parse tpm version info: tss error 0x%x",
126 result);
127 }
128 else
129 {
130 DBG2(DBG_IMC, "TPM 1.2 Version Info: Chip Version: %hhu.%hhu.%hhu.%hhu,"
131 " Spec Level: %hu, Errata Rev: %hhu, Vendor ID: %.4s",
132 versionInfo.version.major, versionInfo.version.minor,
133 versionInfo.version.revMajor, versionInfo.version.revMinor,
134 versionInfo.specLevel, versionInfo.errataRev,
135 versionInfo.tpmVendorID);
136 }
137 }
138
139 METHOD(pts_t, get_platform_info, char*,
140 private_pts_t *this)
141 {
142 return this->platform_info;
143 }
144
145 METHOD(pts_t, set_platform_info, void,
146 private_pts_t *this, char *info)
147 {
148 free(this->platform_info);
149 this->platform_info = strdup(info);
150 }
151
152 METHOD(pts_t, get_tpm_version_info, bool,
153 private_pts_t *this, chunk_t *info)
154 {
155 if (!this->has_tpm)
156 {
157 return FALSE;
158 }
159 *info = this->tpm_version_info;
160 print_tpm_version_info(this);
161 return TRUE;
162 }
163
164 METHOD(pts_t, set_tpm_version_info, void,
165 private_pts_t *this, chunk_t info)
166 {
167 this->tpm_version_info = chunk_clone(info);
168 print_tpm_version_info(this);
169 }
170
171 /**
172 * Load an AIK certificate or public key,
173 * the certificate having precedence over the public key if both are present
174 */
175 static void load_aik(private_pts_t *this)
176 {
177 char *cert_path, *key_path;
178
179 cert_path = lib->settings->get_str(lib->settings,
180 "libimcv.plugins.imc-attestation.aikcert", NULL);
181 key_path = lib->settings->get_str(lib->settings,
182 "libimcv.plugins.imc-attestation.aikkey", NULL);
183
184 if (cert_path)
185 {
186 DBG2(DBG_IMC,"AIK certificate path %s", cert_path);
187 this->aik = lib->creds->create(lib->creds, CRED_CERTIFICATE,
188 CERT_X509, BUILD_FROM_FILE,
189 cert_path, BUILD_END);
190 if (this->aik)
191 {
192 return;
193 }
194 }
195 if (key_path)
196 {
197 DBG2(DBG_IMC,"AIK public key path %s", key_path);
198 this->aik = lib->creds->create(lib->creds, CRED_CERTIFICATE,
199 CERT_TRUSTED_PUBKEY, BUILD_FROM_FILE,
200 key_path, BUILD_END);
201 if (this->aik)
202 {
203 return;
204 }
205 }
206 DBG1(DBG_IMC, "neither AIK certificate nor public key is available");
207 }
208
209 METHOD(pts_t, get_aik, certificate_t*,
210 private_pts_t *this)
211 {
212 return this->aik;
213 }
214
215 METHOD(pts_t, set_aik, void,
216 private_pts_t *this, certificate_t *aik)
217 {
218 DESTROY_IF(this->aik);
219 this->aik = aik->get_ref(aik);
220 }
221
222 /**
223 * Compute a hash over a file
224 */
225 static bool hash_file(hasher_t *hasher, char *pathname, u_char *hash)
226 {
227 u_char buffer[PTS_BUF_SIZE];
228 FILE *file;
229 int bytes_read;
230
231 file = fopen(pathname, "rb");
232 if (!file)
233 {
234 DBG1(DBG_IMC," file '%s' can not be opened, %s", pathname,
235 strerror(errno));
236 return FALSE;
237 }
238 while (TRUE)
239 {
240 bytes_read = fread(buffer, 1, sizeof(buffer), file);
241 if (bytes_read > 0)
242 {
243 hasher->get_hash(hasher, chunk_create(buffer, bytes_read), NULL);
244 }
245 else
246 {
247 hasher->get_hash(hasher, chunk_empty, hash);
248 break;
249 }
250 }
251 fclose(file);
252
253 return TRUE;
254 }
255
256 /**
257 * Get the relative filename of a fully qualified file pathname
258 */
259 static char* get_filename(char *pathname)
260 {
261 char *pos, *filename;
262
263 pos = filename = pathname;
264 while (pos && *(++pos) != '\0')
265 {
266 filename = pos;
267 pos = strchr(filename, '/');
268 }
269 return filename;
270 }
271
272 METHOD(pts_t, do_measurements, pts_file_meas_t*,
273 private_pts_t *this, u_int16_t request_id, char *pathname, bool is_directory)
274 {
275 hasher_t *hasher;
276 hash_algorithm_t hash_alg;
277 u_char hash[HASH_SIZE_SHA384];
278 chunk_t measurement;
279 pts_file_meas_t *measurements;
280
281 /* Create a hasher */
282 hash_alg = pts_meas_to_hash_algorithm(this->algorithm);
283 hasher = lib->crypto->create_hasher(lib->crypto, hash_alg);
284 if (!hasher)
285 {
286 DBG1(DBG_IMC, " hasher %N not available", hash_algorithm_names, hash_alg);
287 return NULL;
288 }
289
290 /* Create a measurement object */
291 measurements = pts_file_meas_create(request_id);
292
293 /* Link the hash to the measurement and set the measurement length */
294 measurement = chunk_create(hash, hasher->get_hash_size(hasher));
295
296 if (is_directory)
297 {
298 enumerator_t *enumerator;
299 char *rel_name, *abs_name;
300 struct stat st;
301
302 enumerator = enumerator_create_directory(pathname);
303 if (!enumerator)
304 {
305 DBG1(DBG_IMC," directory '%s' can not be opened, %s", pathname,
306 strerror(errno));
307 hasher->destroy(hasher);
308 measurements->destroy(measurements);
309 return NULL;
310 }
311 while (enumerator->enumerate(enumerator, &rel_name, &abs_name, &st))
312 {
313 /* measure regular files only */
314 if (S_ISREG(st.st_mode) && *rel_name != '.')
315 {
316 if (!hash_file(hasher, abs_name, hash))
317 {
318 enumerator->destroy(enumerator);
319 hasher->destroy(hasher);
320 measurements->destroy(measurements);
321 return NULL;
322 }
323 DBG2(DBG_IMC, " %#B for '%s'", &measurement, rel_name);
324 measurements->add(measurements, rel_name, measurement);
325 }
326 }
327 enumerator->destroy(enumerator);
328 }
329 else
330 {
331 char *filename;
332
333 if (!hash_file(hasher, pathname, hash))
334 {
335 hasher->destroy(hasher);
336 measurements->destroy(measurements);
337 return NULL;
338 }
339 filename = get_filename(pathname);
340 DBG2(DBG_IMC, " %#B for '%s'", &measurement, filename);
341 measurements->add(measurements, filename, measurement);
342 }
343 hasher->destroy(hasher);
344
345 return measurements;
346 }
347
348 METHOD(pts_t, destroy, void,
349 private_pts_t *this)
350 {
351 DESTROY_IF(this->aik);
352 free(this->platform_info);
353 free(this->tpm_version_info.ptr);
354 free(this);
355 }
356
357 /**
358 * Check for a TPM by querying for TPM Version Info
359 */
360 static bool has_tpm(private_pts_t *this)
361 {
362 TSS_HCONTEXT hContext;
363 TSS_HTPM hTPM;
364 TSS_RESULT result;
365
366 result = Tspi_Context_Create(&hContext);
367 if (result != TSS_SUCCESS)
368 {
369 goto err;
370 }
371 result = Tspi_Context_Connect(hContext, NULL);
372 if (result != TSS_SUCCESS)
373 {
374 goto err;
375 }
376 result = Tspi_Context_GetTpmObject (hContext, &hTPM);
377 if (result != TSS_SUCCESS)
378 {
379 goto err;
380 }
381 result = Tspi_TPM_GetCapability(hTPM, TSS_TPMCAP_VERSION_VAL, 0, NULL,
382 &this->tpm_version_info.len,
383 &this->tpm_version_info.ptr);
384 if (result != TSS_SUCCESS)
385 {
386 goto err;
387 }
388 this->tpm_version_info = chunk_clone(this->tpm_version_info);
389 return TRUE;
390
391 err:
392 DBG1(DBG_IMC, "TPM not available: tss error 0x%x", result);
393 return FALSE;
394 }
395
396 /**
397 * See header
398 */
399 pts_t *pts_create(bool is_imc)
400 {
401 private_pts_t *this;
402
403 INIT(this,
404 .public = {
405 .get_proto_caps = _get_proto_caps,
406 .set_proto_caps = _set_proto_caps,
407 .get_meas_algorithm = _get_meas_algorithm,
408 .set_meas_algorithm = _set_meas_algorithm,
409 .get_platform_info = _get_platform_info,
410 .set_platform_info = _set_platform_info,
411 .get_tpm_version_info = _get_tpm_version_info,
412 .set_tpm_version_info = _set_tpm_version_info,
413 .get_aik = _get_aik,
414 .set_aik = _set_aik,
415 .do_measurements = _do_measurements,
416 .destroy = _destroy,
417 },
418 .proto_caps = PTS_PROTO_CAPS_V,
419 .algorithm = PTS_MEAS_ALGO_SHA256,
420 );
421
422 if (is_imc)
423 {
424 if (has_tpm(this))
425 {
426 this->has_tpm = TRUE;
427 this->proto_caps |= PTS_PROTO_CAPS_T;
428 load_aik(this);
429 }
430 }
431 else
432 {
433 this->proto_caps |= PTS_PROTO_CAPS_T | PTS_PROTO_CAPS_C;
434 }
435
436 return &this->public;
437 }
438