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