4c3514690ca14cf7f58e818bd99e61bfb33b3dfd
[strongswan.git] / src / libpts / 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 <sys/utsname.h>
26 #include <errno.h>
27
28 #define PTS_BUF_SIZE 4096
29
30 typedef struct private_pts_t private_pts_t;
31
32 /**
33 * Private data of a pts_t object.
34 *
35 */
36 struct private_pts_t {
37
38 /**
39 * Public pts_t interface.
40 */
41 pts_t public;
42
43 /**
44 * PTS Protocol Capabilities
45 */
46 pts_proto_caps_flag_t proto_caps;
47
48 /**
49 * PTS Measurement Algorithm
50 */
51 pts_meas_algorithms_t algorithm;
52
53 /**
54 * DH Hash Algorithm
55 */
56 pts_meas_algorithms_t dh_hash_algorithm;
57
58 /**
59 * PTS Diffie-Hellman Secret
60 */
61 diffie_hellman_t *dh;
62
63 /**
64 * PTS Diffie-Hellman Initiator Nonce
65 */
66 chunk_t initiator_nonce;
67
68 /**
69 * PTS Diffie-Hellman Responder Nonce
70 */
71 chunk_t responder_nonce;
72
73 /**
74 * Secret assessment value to be used for TPM Quote as an external data
75 */
76 chunk_t secret;
77
78 /**
79 * Platform and OS Info
80 */
81 char *platform_info;
82
83 /**
84 * TRUE if IMC-PTS, FALSE if IMV-PTS
85 */
86 bool is_imc;
87
88 /**
89 * Do we have an activated TPM
90 */
91 bool has_tpm;
92
93 /**
94 * Contains a TPM_CAP_VERSION_INFO struct
95 */
96 chunk_t tpm_version_info;
97
98 /**
99 * Contains a Attestation Identity Key or Certificate
100 */
101 certificate_t *aik;
102
103 };
104
105 METHOD(pts_t, get_proto_caps, pts_proto_caps_flag_t,
106 private_pts_t *this)
107 {
108 return this->proto_caps;
109 }
110
111 METHOD(pts_t, set_proto_caps, void,
112 private_pts_t *this, pts_proto_caps_flag_t flags)
113 {
114 this->proto_caps = flags;
115 DBG2(DBG_PTS, "supported PTS protocol capabilities: %s%s%s%s%s",
116 flags & PTS_PROTO_CAPS_C ? "C" : ".",
117 flags & PTS_PROTO_CAPS_V ? "V" : ".",
118 flags & PTS_PROTO_CAPS_D ? "D" : ".",
119 flags & PTS_PROTO_CAPS_T ? "T" : ".",
120 flags & PTS_PROTO_CAPS_X ? "X" : ".");
121 }
122
123 METHOD(pts_t, get_meas_algorithm, pts_meas_algorithms_t,
124 private_pts_t *this)
125 {
126 return this->algorithm;
127 }
128
129 METHOD(pts_t, set_meas_algorithm, void,
130 private_pts_t *this, pts_meas_algorithms_t algorithm)
131 {
132 hash_algorithm_t hash_alg;
133
134 hash_alg = pts_meas_algo_to_hash(algorithm);
135 DBG2(DBG_PTS, "selected PTS measurement algorithm is %N",
136 hash_algorithm_names, hash_alg);
137 if (hash_alg != HASH_UNKNOWN)
138 {
139 this->algorithm = algorithm;
140 }
141 }
142
143 METHOD(pts_t, get_dh_hash_algorithm, pts_meas_algorithms_t,
144 private_pts_t *this)
145 {
146 return this->dh_hash_algorithm;
147 }
148
149 METHOD(pts_t, set_dh_hash_algorithm, void,
150 private_pts_t *this, pts_meas_algorithms_t algorithm)
151 {
152 hash_algorithm_t hash_alg;
153
154 hash_alg = pts_meas_algo_to_hash(algorithm);
155 DBG2(DBG_PTS, "selected DH hash algorithm is %N",
156 hash_algorithm_names, hash_alg);
157 if (hash_alg != HASH_UNKNOWN)
158 {
159 this->dh_hash_algorithm = algorithm;
160 }
161 }
162
163
164 METHOD(pts_t, create_dh_nonce, bool,
165 private_pts_t *this, pts_dh_group_t group, int nonce_len)
166 {
167 diffie_hellman_group_t dh_group;
168 chunk_t *nonce;
169 rng_t *rng;
170
171 dh_group = pts_dh_group_to_ike(group);
172 DBG2(DBG_PTS, "selected PTS DH group is %N",
173 diffie_hellman_group_names, dh_group);
174 DESTROY_IF(this->dh);
175 this->dh = lib->crypto->create_dh(lib->crypto, dh_group);
176
177 rng = lib->crypto->create_rng(lib->crypto, RNG_STRONG);
178 if (!rng)
179 {
180 DBG1(DBG_PTS, "no rng available");
181 return FALSE;
182 }
183 DBG2(DBG_PTS, "nonce length is %d", nonce_len);
184 nonce = this->is_imc ? &this->responder_nonce : &this->initiator_nonce;
185 chunk_free(nonce);
186 rng->allocate_bytes(rng, nonce_len, nonce);
187 rng->destroy(rng);
188
189 return TRUE;
190 }
191
192 METHOD(pts_t, get_my_public_value, void,
193 private_pts_t *this, chunk_t *value, chunk_t *nonce)
194 {
195 this->dh->get_my_public_value(this->dh, value);
196 *nonce = this->is_imc ? this->responder_nonce : this->initiator_nonce;
197 }
198
199 METHOD(pts_t, set_peer_public_value, void,
200 private_pts_t *this, chunk_t value, chunk_t nonce)
201 {
202 this->dh->set_other_public_value(this->dh, value);
203
204 nonce = chunk_clone(nonce);
205 if (this->is_imc)
206 {
207 this->initiator_nonce = nonce;
208 }
209 else
210 {
211 this->responder_nonce = nonce;
212 }
213 }
214
215 METHOD(pts_t, calculate_secret, bool,
216 private_pts_t *this)
217 {
218 hasher_t *hasher;
219 hash_algorithm_t hash_alg;
220 chunk_t shared_secret;
221
222 /* Check presence of nonces */
223 if (!this->initiator_nonce.len || !this->responder_nonce.len)
224 {
225 DBG1(DBG_PTS, "initiator and/or responder nonce is not available");
226 return FALSE;
227 }
228 DBG3(DBG_PTS, "initiator nonce: %B", &this->initiator_nonce);
229 DBG3(DBG_PTS, "responder nonce: %B", &this->responder_nonce);
230
231 /* Calculate the DH secret */
232 if (this->dh->get_shared_secret(this->dh, &shared_secret) != SUCCESS)
233 {
234 DBG1(DBG_PTS, "shared DH secret computation failed");
235 return FALSE;
236 }
237 DBG4(DBG_PTS, "shared DH secret: %B", &shared_secret);
238
239 /* Calculate the secret assessment value */
240 hash_alg = pts_meas_algo_to_hash(this->dh_hash_algorithm);
241 hasher = lib->crypto->create_hasher(lib->crypto, hash_alg);
242
243 hasher->allocate_hash(hasher, chunk_from_chars('1'), NULL);
244 hasher->allocate_hash(hasher, this->initiator_nonce, NULL);
245 hasher->allocate_hash(hasher, this->responder_nonce, NULL);
246 hasher->allocate_hash(hasher, shared_secret, &this->secret);
247 hasher->destroy(hasher);
248
249 /* The DH secret must be destroyed */
250 chunk_clear(&shared_secret);
251
252 /*
253 * Truncate the hash to 20 bytes to fit the ExternalData
254 * argument of the TPM Quote command
255 */
256 this->secret.len = min(this->secret.len, 20);
257 DBG4(DBG_PTS, "secret assessment value: %B", &this->secret);
258 return TRUE;
259 }
260
261 /**
262 * Print TPM 1.2 Version Info
263 */
264 static void print_tpm_version_info(private_pts_t *this)
265 {
266 TPM_CAP_VERSION_INFO versionInfo;
267 UINT64 offset = 0;
268 TSS_RESULT result;
269
270 result = Trspi_UnloadBlob_CAP_VERSION_INFO(&offset,
271 this->tpm_version_info.ptr, &versionInfo);
272 if (result != TSS_SUCCESS)
273 {
274 DBG1(DBG_PTS, "could not parse tpm version info: tss error 0x%x",
275 result);
276 }
277 else
278 {
279 DBG2(DBG_PTS, "TPM 1.2 Version Info: Chip Version: %hhu.%hhu.%hhu.%hhu,"
280 " Spec Level: %hu, Errata Rev: %hhu, Vendor ID: %.4s",
281 versionInfo.version.major, versionInfo.version.minor,
282 versionInfo.version.revMajor, versionInfo.version.revMinor,
283 versionInfo.specLevel, versionInfo.errataRev,
284 versionInfo.tpmVendorID);
285 }
286 }
287
288 METHOD(pts_t, get_platform_info, char*,
289 private_pts_t *this)
290 {
291 return this->platform_info;
292 }
293
294 METHOD(pts_t, set_platform_info, void,
295 private_pts_t *this, char *info)
296 {
297 free(this->platform_info);
298 this->platform_info = strdup(info);
299 }
300
301 METHOD(pts_t, get_tpm_version_info, bool,
302 private_pts_t *this, chunk_t *info)
303 {
304 if (!this->has_tpm)
305 {
306 return FALSE;
307 }
308 *info = this->tpm_version_info;
309 print_tpm_version_info(this);
310 return TRUE;
311 }
312
313 METHOD(pts_t, set_tpm_version_info, void,
314 private_pts_t *this, chunk_t info)
315 {
316 this->tpm_version_info = chunk_clone(info);
317 print_tpm_version_info(this);
318 }
319
320 /**
321 * Load an AIK certificate or public key,
322 * the certificate having precedence over the public key if both are present
323 */
324 static void load_aik(private_pts_t *this)
325 {
326 char *cert_path, *key_path;
327
328 cert_path = lib->settings->get_str(lib->settings,
329 "libimcv.plugins.imc-attestation.aik_cert", NULL);
330 key_path = lib->settings->get_str(lib->settings,
331 "libimcv.plugins.imc-attestation.aik_key", NULL);
332
333 if (cert_path)
334 {
335 this->aik = lib->creds->create(lib->creds, CRED_CERTIFICATE,
336 CERT_X509, BUILD_FROM_FILE,
337 cert_path, BUILD_END);
338 if (this->aik)
339 {
340 DBG2(DBG_PTS, "loaded AIK certificate from '%s'", cert_path);
341 return;
342 }
343 }
344 if (key_path)
345 {
346 this->aik = lib->creds->create(lib->creds, CRED_CERTIFICATE,
347 CERT_TRUSTED_PUBKEY, BUILD_FROM_FILE,
348 key_path, BUILD_END);
349 if (this->aik)
350 {
351 DBG2(DBG_PTS, "loaded AIK public key from '%s'", key_path);
352 return;
353 }
354 }
355 DBG1(DBG_PTS, "neither AIK certificate nor public key is available");
356 }
357
358 METHOD(pts_t, get_aik, certificate_t*,
359 private_pts_t *this)
360 {
361 return this->aik;
362 }
363
364 METHOD(pts_t, set_aik, void,
365 private_pts_t *this, certificate_t *aik)
366 {
367 DESTROY_IF(this->aik);
368 this->aik = aik->get_ref(aik);
369 }
370
371 METHOD(pts_t, hash_file, bool,
372 private_pts_t *this, hasher_t *hasher, char *pathname, u_char *hash)
373 {
374 u_char buffer[PTS_BUF_SIZE];
375 FILE *file;
376 int bytes_read;
377
378 file = fopen(pathname, "rb");
379 if (!file)
380 {
381 DBG1(DBG_PTS," file '%s' can not be opened, %s", pathname,
382 strerror(errno));
383 return FALSE;
384 }
385 while (TRUE)
386 {
387 bytes_read = fread(buffer, 1, sizeof(buffer), file);
388 if (bytes_read > 0)
389 {
390 hasher->get_hash(hasher, chunk_create(buffer, bytes_read), NULL);
391 }
392 else
393 {
394 hasher->get_hash(hasher, chunk_empty, hash);
395 break;
396 }
397 }
398 fclose(file);
399
400 return TRUE;
401 }
402
403 /**
404 * Get the relative filename of a fully qualified file pathname
405 */
406 static char* get_filename(char *pathname)
407 {
408 char *pos, *filename;
409
410 pos = filename = pathname;
411 while (pos && *(++pos) != '\0')
412 {
413 filename = pos;
414 pos = strchr(filename, '/');
415 }
416 return filename;
417 }
418
419 METHOD(pts_t, is_path_valid, bool,
420 private_pts_t *this, char *path, pts_error_code_t *error_code)
421 {
422 struct stat st;
423
424 *error_code = 0;
425
426 if (!stat(path, &st))
427 {
428 return TRUE;
429 }
430 else if (errno == ENOENT || errno == ENOTDIR)
431 {
432 DBG1(DBG_PTS, "file/directory does not exist %s", path);
433 *error_code = TCG_PTS_FILE_NOT_FOUND;
434 }
435 else if (errno == EFAULT)
436 {
437 DBG1(DBG_PTS, "bad address %s", path);
438 *error_code = TCG_PTS_INVALID_PATH;
439 }
440 else
441 {
442 DBG1(DBG_PTS, "error: %s occured while validating path: %s",
443 strerror(errno), path);
444 return FALSE;
445 }
446
447 return TRUE;
448 }
449
450 METHOD(pts_t, do_measurements, pts_file_meas_t*,
451 private_pts_t *this, u_int16_t request_id, char *pathname, bool is_directory)
452 {
453 hasher_t *hasher;
454 hash_algorithm_t hash_alg;
455 u_char hash[HASH_SIZE_SHA384];
456 chunk_t measurement;
457 pts_file_meas_t *measurements;
458
459 /* Create a hasher */
460 hash_alg = pts_meas_algo_to_hash(this->algorithm);
461 hasher = lib->crypto->create_hasher(lib->crypto, hash_alg);
462 if (!hasher)
463 {
464 DBG1(DBG_PTS, " hasher %N not available", hash_algorithm_names, hash_alg);
465 return NULL;
466 }
467
468 /* Create a measurement object */
469 measurements = pts_file_meas_create(request_id);
470
471 /* Link the hash to the measurement and set the measurement length */
472 measurement = chunk_create(hash, hasher->get_hash_size(hasher));
473
474 if (is_directory)
475 {
476 enumerator_t *enumerator;
477 char *rel_name, *abs_name;
478 struct stat st;
479
480 enumerator = enumerator_create_directory(pathname);
481 if (!enumerator)
482 {
483 DBG1(DBG_PTS," directory '%s' can not be opened, %s", pathname,
484 strerror(errno));
485 hasher->destroy(hasher);
486 measurements->destroy(measurements);
487 return NULL;
488 }
489 while (enumerator->enumerate(enumerator, &rel_name, &abs_name, &st))
490 {
491 /* measure regular files only */
492 if (S_ISREG(st.st_mode) && *rel_name != '.')
493 {
494 if (!hash_file(this, hasher, abs_name, hash))
495 {
496 enumerator->destroy(enumerator);
497 hasher->destroy(hasher);
498 measurements->destroy(measurements);
499 return NULL;
500 }
501 DBG2(DBG_PTS, " %#B for '%s'", &measurement, rel_name);
502 measurements->add(measurements, rel_name, measurement);
503 }
504 }
505 enumerator->destroy(enumerator);
506 }
507 else
508 {
509 char *filename;
510
511 if (!hash_file(this, hasher, pathname, hash))
512 {
513 hasher->destroy(hasher);
514 measurements->destroy(measurements);
515 return NULL;
516 }
517 filename = get_filename(pathname);
518 DBG2(DBG_PTS, " %#B for '%s'", &measurement, filename);
519 measurements->add(measurements, filename, measurement);
520 }
521 hasher->destroy(hasher);
522
523 return measurements;
524 }
525
526 /**
527 * Obtain statistical information describing a file
528 */
529 static bool file_metadata(char *pathname, pts_file_metadata_t **entry)
530 {
531 struct stat st;
532 pts_file_metadata_t *tmp;
533
534 tmp = malloc_thing(pts_file_metadata_t);
535
536 if (stat(pathname, &st))
537 {
538 DBG1(DBG_PTS, "Unable to obtain statistical information about %s", pathname);
539 return FALSE;
540 }
541
542 tmp->filename = strdup(pathname);
543 tmp->meta_length = PTS_FILE_METADATA_SIZE + strlen(tmp->filename);
544
545 if (S_ISREG(st.st_mode))
546 {
547 tmp->type = PTS_FILE_REGULAR;
548 }
549 else if (S_ISDIR(st.st_mode))
550 {
551 tmp->type = PTS_FILE_DIRECTORY;
552 }
553 else if (S_ISCHR(st.st_mode))
554 {
555 tmp->type = PTS_FILE_CHAR_SPEC;
556 }
557 else if (S_ISBLK(st.st_mode))
558 {
559 tmp->type = PTS_FILE_BLOCK_SPEC;
560 }
561 else if (S_ISFIFO(st.st_mode))
562 {
563 tmp->type = PTS_FILE_FIFO;
564 }
565 else if (S_ISLNK(st.st_mode))
566 {
567 tmp->type = PTS_FILE_SYM_LINK;
568 }
569 else if (S_ISSOCK(st.st_mode))
570 {
571 tmp->type = PTS_FILE_SOCKET;
572 }
573 else
574 {
575 tmp->type = PTS_FILE_OTHER;
576 }
577
578 tmp->filesize = (u_int64_t)st.st_size;
579 tmp->create_time = st.st_ctime;
580 tmp->last_modify_time = st.st_mtime;
581 tmp->last_access_time = st.st_atime;
582 tmp->owner_id = (u_int64_t)st.st_uid;
583 tmp->group_id = (u_int64_t)st.st_gid;
584
585 *entry = tmp;
586
587 return TRUE;
588 }
589
590 METHOD(pts_t, get_metadata, pts_file_meta_t*,
591 private_pts_t *this, char *pathname, bool is_directory)
592 {
593 pts_file_meta_t *metadata;
594 pts_file_metadata_t *entry;
595
596 /* Create a metadata object */
597 metadata = pts_file_meta_create();
598
599 if (is_directory)
600 {
601 enumerator_t *enumerator;
602 char *rel_name, *abs_name;
603 struct stat st;
604
605 enumerator = enumerator_create_directory(pathname);
606 if (!enumerator)
607 {
608 DBG1(DBG_PTS," directory '%s' can not be opened, %s", pathname,
609 strerror(errno));
610 metadata->destroy(metadata);
611 return NULL;
612 }
613 while (enumerator->enumerate(enumerator, &rel_name, &abs_name, &st))
614 {
615 /* measure regular files only */
616 if (S_ISREG(st.st_mode) && *rel_name != '.')
617 {
618 if (!file_metadata(abs_name, &entry))
619 {
620 enumerator->destroy(enumerator);
621 metadata->destroy(metadata);
622 return NULL;
623 }
624 DBG3(DBG_PTS, "File name: %s", entry->filename);
625 DBG3(DBG_PTS, " type: %d", entry->type);
626 DBG3(DBG_PTS, " size: %d", entry->filesize);
627 DBG3(DBG_PTS, " create time: %s", ctime(&entry->create_time));
628 DBG3(DBG_PTS, " last modified: %s", ctime(&entry->last_modify_time));
629 DBG3(DBG_PTS, " last accessed: %s", ctime(&entry->last_access_time));
630 DBG3(DBG_PTS, " owner id: %d", entry->owner_id);
631 DBG3(DBG_PTS, " group id: %d", entry->group_id);
632
633 metadata->add(metadata, entry);
634 }
635 }
636 enumerator->destroy(enumerator);
637 }
638 else
639 {
640 char *filename;
641
642 if (!file_metadata(pathname, &entry))
643 {
644 metadata->destroy(metadata);
645 return NULL;
646 }
647 filename = get_filename(pathname);
648 DBG3(DBG_PTS, "File name: %s", entry->filename);
649 DBG3(DBG_PTS, " type: %d", entry->type);
650 DBG3(DBG_PTS, " size: %d", entry->filesize);
651 DBG3(DBG_PTS, " create time: %s", ctime(&entry->create_time));
652 DBG3(DBG_PTS, " last modified: %s", ctime(&entry->last_modify_time));
653 DBG3(DBG_PTS, " last accessed: %s", ctime(&entry->last_access_time));
654 DBG3(DBG_PTS, " owner id: %d", entry->owner_id);
655 DBG3(DBG_PTS, " group id: %d", entry->group_id);
656 metadata->add(metadata, entry);
657 }
658
659 return metadata;
660 }
661
662 METHOD(pts_t, read_pcr, bool,
663 private_pts_t *this, u_int32_t pcr_num, chunk_t *output)
664 {
665 TSS_HCONTEXT hContext;
666 TSS_HTPM hTPM;
667 TSS_RESULT result;
668 u_int32_t pcr_length;
669 chunk_t pcr_value;
670
671 result = Tspi_Context_Create(&hContext);
672 if (result != TSS_SUCCESS)
673 {
674 DBG1(DBG_PTS, "TPM context could not be created: tss error 0x%x", result);
675 return FALSE;
676 }
677
678 result = Tspi_Context_Connect(hContext, NULL);
679 if (result != TSS_SUCCESS)
680 {
681 goto err;
682 }
683 result = Tspi_Context_GetTpmObject (hContext, &hTPM);
684 if (result != TSS_SUCCESS)
685 {
686 goto err;
687 }
688 pcr_value = chunk_alloc(PCR_LEN);
689 result = Tspi_TPM_PcrRead(hTPM, pcr_num, &pcr_length, &pcr_value.ptr);
690 if (result != TSS_SUCCESS)
691 {
692 goto err;
693 }
694
695 *output = pcr_value;
696 *output = chunk_clone(*output);
697
698 chunk_clear(&pcr_value);
699 Tspi_Context_Close(hContext);
700 DBG3(DBG_PTS, "PCR %d value:%B", pcr_num, output);
701 return TRUE;
702
703 err:
704 chunk_clear(&pcr_value);
705 DBG1(DBG_PTS, "TPM not available: tss error 0x%x", result);
706 Tspi_Context_Close(hContext);
707 return FALSE;
708 }
709
710 METHOD(pts_t, extend_pcr, bool,
711 private_pts_t *this, u_int32_t pcr_num, chunk_t input, chunk_t *output)
712 {
713 TSS_HCONTEXT hContext;
714 TSS_HTPM hTPM;
715 TSS_RESULT result;
716 u_int32_t pcr_length;
717 chunk_t pcr_value;
718
719 result = Tspi_Context_Create(&hContext);
720 if (result != TSS_SUCCESS)
721 {
722 DBG1(DBG_PTS, "TPM context could not be created: tss error 0x%x", result);
723 return FALSE;
724 }
725 result = Tspi_Context_Connect(hContext, NULL);
726 if (result != TSS_SUCCESS)
727 {
728 goto err;
729 }
730 result = Tspi_Context_GetTpmObject (hContext, &hTPM);
731 if (result != TSS_SUCCESS)
732 {
733 goto err;
734 }
735
736 pcr_value = chunk_alloc(PCR_LEN);
737 result = Tspi_TPM_PcrExtend(hTPM, pcr_num, PCR_LEN, input.ptr,
738 NULL, &pcr_length, &pcr_value.ptr);
739 if (result != TSS_SUCCESS)
740 {
741 goto err;
742 }
743
744 *output = pcr_value;
745 *output = chunk_clone(*output);
746
747 chunk_clear(&pcr_value);
748 Tspi_Context_Close(hContext);
749 DBG3(DBG_PTS, "PCR %d extended with: %B", pcr_num, &input);
750 DBG3(DBG_PTS, "PCR %d value after extend: %B", pcr_num, output);
751 return TRUE;
752
753 err:
754 chunk_clear(&pcr_value);
755 DBG1(DBG_PTS, "TPM not available: tss error 0x%x", result);
756 Tspi_Context_Close(hContext);
757 return FALSE;
758 }
759
760 METHOD(pts_t, quote_tpm, bool,
761 private_pts_t *this, linked_list_t *pcrs, chunk_t *pcr_composite,
762 chunk_t *quote_signature)
763 {
764 TSS_HCONTEXT hContext;
765 TSS_HTPM hTPM;
766 TSS_HKEY hAIK;
767 TSS_HKEY hSRK;
768 TSS_HPOLICY srkUsagePolicy;
769 TSS_UUID SRK_UUID = TSS_UUID_SRK;
770 BYTE secret[] = TSS_WELL_KNOWN_SECRET;
771 TSS_HPCRS hPcrComposite;
772 TSS_VALIDATION valData;
773 TPM_QUOTE_INFO *quoteInfo;
774 u_int32_t i, pcr;
775 TSS_RESULT result;
776 chunk_t aik_key_encoding;
777 chunk_t pcr_composite_without_nonce;
778 enumerator_t *enumerator;
779
780 result = Tspi_Context_Create(&hContext);
781 if (result != TSS_SUCCESS)
782 {
783 DBG1(DBG_PTS, "TPM context could not be created: tss error 0x%x", result);
784 return FALSE;
785 }
786 result = Tspi_Context_Connect(hContext, NULL);
787 if (result != TSS_SUCCESS)
788 {
789 goto err1;
790 }
791 result = Tspi_Context_GetTpmObject (hContext, &hTPM);
792 if (result != TSS_SUCCESS)
793 {
794 goto err1;
795 }
796
797 /* Retrieve SRK from TPM and set the authentication data as well known secret*/
798 result = Tspi_Context_LoadKeyByUUID(hContext, TSS_PS_TYPE_SYSTEM,
799 SRK_UUID, &hSRK);
800 if (result != TSS_SUCCESS)
801 {
802 goto err1;
803 }
804
805 result = Tspi_GetPolicyObject(hSRK, TSS_POLICY_USAGE, &srkUsagePolicy);
806 if (result != TSS_SUCCESS)
807 {
808 goto err1;
809 }
810 result = Tspi_Policy_SetSecret(srkUsagePolicy, TSS_SECRET_MODE_SHA1,
811 20, secret);
812 if (result != TSS_SUCCESS)
813 {
814 goto err1;
815 }
816
817 /* Create from AIK public key a HKEY object to sign Quote operation output*/
818 if (this->aik->get_type(this->aik) == CERT_TRUSTED_PUBKEY)
819 {
820 if (!this->aik->get_encoding(this->aik, CERT_ASN1_DER, &aik_key_encoding))
821 {
822 DBG1(DBG_PTS, "encoding AIK certificate for quote operation failed");
823 goto err1;
824 }
825 }
826 else if (this->aik->get_type(this->aik) == CERT_X509)
827 {
828 public_key_t *key = this->aik->get_public_key(this->aik);
829
830 if (key == NULL)
831 {
832 DBG1(DBG_PTS, "unable to retrieve public key from AIK certificate");
833 goto err1;
834 }
835 if (!key->get_encoding(key, PUBKEY_ASN1_DER, &aik_key_encoding))
836 {
837 DBG1(DBG_PTS, "encoding AIK Public Key for quote operation failed");
838 goto err1;
839 }
840 }
841 else
842 {
843 DBG1(DBG_PTS, "AIK is neither X509 certificate nor Public Key");
844 goto err1;
845 }
846
847 result = Tspi_Context_LoadKeyByBlob (hContext, hSRK, aik_key_encoding.len,
848 (BYTE*)aik_key_encoding.ptr, &hAIK);
849 if (result != TSS_SUCCESS)
850 {
851 goto err1;
852 }
853
854 /* Create PCR composite object */
855 result = Tspi_Context_CreateObject(hContext, TSS_OBJECT_TYPE_PCRS, 0, &hPcrComposite);
856 if (result != TSS_SUCCESS)
857 {
858 goto err2;
859 }
860
861 /* Select PCR's */
862 enumerator = pcrs->create_enumerator(pcrs);
863 while (enumerator->enumerate(enumerator, &pcr))
864 {
865 if (pcr < 0 || pcr >= MAX_NUM_PCR )
866 {
867 DBG1(DBG_PTS, "Invalid PCR number: %d", pcr);
868 goto err3;
869 }
870 result = Tspi_PcrComposite_SelectPcrIndex(hPcrComposite, pcr);
871 if (result != TSS_SUCCESS)
872 {
873 goto err3;
874 }
875 }
876 enumerator->destroy(enumerator);
877
878 /* Set the Validation Data */
879 valData.ulExternalDataLength = this->secret.len;
880 valData.rgbExternalData = (BYTE *)this->secret.ptr;
881
882 /* TPM Quote */
883 result = Tspi_TPM_Quote(hTPM, hAIK, hPcrComposite, &valData);
884 if (result != TSS_SUCCESS)
885 {
886 goto err4;
887 }
888
889 quoteInfo = (TPM_QUOTE_INFO *)valData.rgbData;
890
891 /* Display quote info */
892 DBG3(DBG_PTS, "version:");
893 for(i = 0 ; i < 4 ; i++)
894 {
895 DBG3(DBG_PTS, "%02x ",valData.rgbData[i]);
896 }
897 DBG3(DBG_PTS, "fixed value:");
898 for(i = 4 ; i < 8 ; i++)
899 {
900 DBG3(DBG_PTS, "%c",valData.rgbData[i]);
901 }
902 DBG3(DBG_PTS, "pcr digest:");
903 for(i = 8 ; i < 28 ; i++)
904 {
905 DBG3(DBG_PTS, "%02x ",valData.rgbData[i]);
906 }
907 DBG3(DBG_PTS, "nonce:");
908 for(i = 28 ; i < valData.ulDataLength ; i++)
909 {
910 DBG3(DBG_PTS, "%c",valData.rgbData[i]);
911 }
912
913 /* Set output chunks */
914 pcr_composite_without_nonce = chunk_alloc(
915 valData.ulDataLength - ASSESSMENT_SECRET_LEN);
916 memcpy(pcr_composite_without_nonce.ptr, valData.rgbData,
917 valData.ulDataLength - ASSESSMENT_SECRET_LEN);
918 *pcr_composite = pcr_composite_without_nonce;
919 *pcr_composite = chunk_clone(*pcr_composite);
920 free(pcr_composite_without_nonce.ptr);
921
922 *quote_signature = chunk_from_thing(valData.rgbValidationData);
923 *quote_signature = chunk_clone(*quote_signature);
924
925 Tspi_Context_FreeMemory(hContext, NULL);
926 Tspi_Context_CloseObject(hContext, hPcrComposite);
927 Tspi_Context_CloseObject(hContext, hAIK);
928 Tspi_Context_Close(hContext);
929 return TRUE;
930
931 /* Cleanup */
932 err4:
933 Tspi_Context_FreeMemory(hContext, NULL);
934
935 err3:
936 Tspi_Context_CloseObject(hContext, hPcrComposite);
937
938 err2:
939 Tspi_Context_CloseObject(hContext, hAIK);
940
941 err1:
942 Tspi_Context_Close(hContext);
943 DBG1(DBG_PTS, "TPM not available: tss error 0x%x", result);
944 return FALSE;
945 }
946
947 METHOD(pts_t, destroy, void,
948 private_pts_t *this)
949 {
950 DESTROY_IF(this->aik);
951 DESTROY_IF(this->dh);
952 free(this->initiator_nonce.ptr);
953 free(this->responder_nonce.ptr);
954 free(this->platform_info);
955 free(this->tpm_version_info.ptr);
956 free(this);
957 }
958
959 /**
960 * Determine Linux distribution and hardware platform
961 */
962 static char* extract_platform_info(void)
963 {
964 FILE *file;
965 char buf[BUF_LEN], *pos, *value = NULL;
966 int i, len;
967 struct utsname uninfo;
968
969 /* Linux/Unix distribution release info (from http://linuxmafia.com) */
970 const char* releases[] = {
971 "/etc/lsb-release", "/etc/debian_version",
972 "/etc/SuSE-release", "/etc/novell-release",
973 "/etc/sles-release", "/etc/redhat-release",
974 "/etc/fedora-release", "/etc/gentoo-release",
975 "/etc/slackware-version", "/etc/annvix-release",
976 "/etc/arch-release", "/etc/arklinux-release",
977 "/etc/aurox-release", "/etc/blackcat-release",
978 "/etc/cobalt-release", "/etc/conectiva-release",
979 "/etc/debian_release", "/etc/immunix-release",
980 "/etc/lfs-release", "/etc/linuxppc-release",
981 "/etc/mandrake-release", "/etc/mandriva-release",
982 "/etc/mandrakelinux-release", "/etc/mklinux-release",
983 "/etc/pld-release", "/etc/redhat_version",
984 "/etc/slackware-release", "/etc/e-smith-release",
985 "/etc/release", "/etc/sun-release",
986 "/etc/tinysofa-release", "/etc/turbolinux-release",
987 "/etc/ultrapenguin-release", "/etc/UnitedLinux-release",
988 "/etc/va-release", "/etc/yellowdog-release"
989 };
990
991 const char description[] = "DISTRIB_DESCRIPTION=\"";
992
993 for (i = 0; i < countof(releases); i++)
994 {
995 file = fopen(releases[i], "r");
996 if (!file)
997 {
998 continue;
999 }
1000 fseek(file, 0, SEEK_END);
1001 len = min(ftell(file), sizeof(buf)-1);
1002 rewind(file);
1003 buf[len] = '\0';
1004 if (fread(buf, 1, len, file) != len)
1005 {
1006 DBG1(DBG_PTS, "failed to read file '%s'", releases[i]);
1007 fclose(file);
1008 return NULL;
1009 }
1010 fclose(file);
1011
1012 if (i == 0) /* LSB release */
1013 {
1014 pos = strstr(buf, description);
1015 if (!pos)
1016 {
1017 DBG1(DBG_PTS, "failed to find begin of lsb-release "
1018 "DESCRIPTION field");
1019 return NULL;
1020 }
1021 value = pos + strlen(description);
1022 pos = strchr(value, '"');
1023 if (!pos)
1024 {
1025 DBG1(DBG_PTS, "failed to find end of lsb-release "
1026 "DESCRIPTION field");
1027 return NULL;
1028 }
1029 }
1030 else
1031 {
1032 value = buf;
1033 pos = strchr(value, '\n');
1034 if (!pos)
1035 {
1036 DBG1(DBG_PTS, "failed to find end of release string");
1037 return NULL;
1038 }
1039 }
1040 break;
1041 }
1042
1043 if (!value)
1044 {
1045 DBG1(DBG_PTS, "no distribution release file found");
1046 return NULL;
1047 }
1048
1049 if (uname(&uninfo) < 0)
1050 {
1051 DBG1(DBG_PTS, "could not retrieve machine architecture");
1052 return NULL;
1053 }
1054
1055 *pos++ = ' ';
1056 len = sizeof(buf)-1 + (pos - buf);
1057 strncpy(pos, uninfo.machine, len);
1058
1059 DBG1(DBG_PTS, "platform is '%s'", value);
1060 return strdup(value);
1061 }
1062
1063 /**
1064 * Check for a TPM by querying for TPM Version Info
1065 */
1066 static bool has_tpm(private_pts_t *this)
1067 {
1068 TSS_HCONTEXT hContext;
1069 TSS_HTPM hTPM;
1070 TSS_RESULT result;
1071 u_int32_t version_info_len;
1072
1073 result = Tspi_Context_Create(&hContext);
1074 if (result != TSS_SUCCESS)
1075 {
1076 DBG1(DBG_PTS, "TPM context could not be created: tss error 0x%x", result);
1077 return FALSE;
1078 }
1079 result = Tspi_Context_Connect(hContext, NULL);
1080 if (result != TSS_SUCCESS)
1081 {
1082 goto err;
1083 }
1084 result = Tspi_Context_GetTpmObject (hContext, &hTPM);
1085 if (result != TSS_SUCCESS)
1086 {
1087 goto err;
1088 }
1089 result = Tspi_TPM_GetCapability(hTPM, TSS_TPMCAP_VERSION_VAL, 0, NULL,
1090 &version_info_len,
1091 &this->tpm_version_info.ptr);
1092 this->tpm_version_info.len = version_info_len;
1093 if (result != TSS_SUCCESS)
1094 {
1095 goto err;
1096 }
1097 this->tpm_version_info = chunk_clone(this->tpm_version_info);
1098 Tspi_Context_Close(hContext);
1099 return TRUE;
1100
1101 err:
1102 DBG1(DBG_PTS, "TPM not available: tss error 0x%x", result);
1103 Tspi_Context_Close(hContext);
1104 return FALSE;
1105 }
1106
1107 /**
1108 * See header
1109 */
1110 pts_t *pts_create(bool is_imc)
1111 {
1112 private_pts_t *this;
1113
1114 INIT(this,
1115 .public = {
1116 .get_proto_caps = _get_proto_caps,
1117 .set_proto_caps = _set_proto_caps,
1118 .get_meas_algorithm = _get_meas_algorithm,
1119 .set_meas_algorithm = _set_meas_algorithm,
1120 .get_dh_hash_algorithm = _get_dh_hash_algorithm,
1121 .set_dh_hash_algorithm = _set_dh_hash_algorithm,
1122 .create_dh_nonce = _create_dh_nonce,
1123 .get_my_public_value = _get_my_public_value,
1124 .set_peer_public_value = _set_peer_public_value,
1125 .calculate_secret = _calculate_secret,
1126 .get_platform_info = _get_platform_info,
1127 .set_platform_info = _set_platform_info,
1128 .get_tpm_version_info = _get_tpm_version_info,
1129 .set_tpm_version_info = _set_tpm_version_info,
1130 .get_aik = _get_aik,
1131 .set_aik = _set_aik,
1132 .is_path_valid = _is_path_valid,
1133 .hash_file = _hash_file,
1134 .do_measurements = _do_measurements,
1135 .get_metadata = _get_metadata,
1136 .read_pcr = _read_pcr,
1137 .extend_pcr = _extend_pcr,
1138 .quote_tpm = _quote_tpm,
1139 .destroy = _destroy,
1140 },
1141 .is_imc = is_imc,
1142 .proto_caps = PTS_PROTO_CAPS_V,
1143 .algorithm = PTS_MEAS_ALGO_SHA256,
1144 .dh_hash_algorithm = PTS_MEAS_ALGO_SHA256,
1145 );
1146
1147 if (is_imc)
1148 {
1149 this->platform_info = extract_platform_info();
1150
1151 if (has_tpm(this))
1152 {
1153 this->has_tpm = TRUE;
1154 this->proto_caps |= PTS_PROTO_CAPS_T;
1155 load_aik(this);
1156 }
1157 }
1158 else
1159 {
1160 this->proto_caps |= PTS_PROTO_CAPS_T | PTS_PROTO_CAPS_C;
1161 }
1162
1163 return &this->public;
1164 }
1165