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