Implemented matching of Optional PCR Composite field value when Hashing was done...
[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 #include <bio/bio_writer.h>
21 #include <bio/bio_reader.h>
22
23 #include <trousers/tss.h>
24 #include <trousers/trousers.h>
25
26 #include <sys/stat.h>
27 #include <sys/utsname.h>
28 #include <errno.h>
29
30 #include <openssl/rsa.h>
31 #include <openssl/evp.h>
32 #include <openssl/x509.h>
33
34 #define PTS_BUF_SIZE 4096
35
36 typedef struct private_pts_t private_pts_t;
37
38 /**
39 * Private data of a pts_t object.
40 *
41 */
42 struct private_pts_t {
43
44 /**
45 * Public pts_t interface.
46 */
47 pts_t public;
48
49 /**
50 * PTS Protocol Capabilities
51 */
52 pts_proto_caps_flag_t proto_caps;
53
54 /**
55 * PTS Measurement Algorithm
56 */
57 pts_meas_algorithms_t algorithm;
58
59 /**
60 * DH Hash Algorithm
61 */
62 pts_meas_algorithms_t dh_hash_algorithm;
63
64 /**
65 * PTS Diffie-Hellman Secret
66 */
67 diffie_hellman_t *dh;
68
69 /**
70 * PTS Diffie-Hellman Initiator Nonce
71 */
72 chunk_t initiator_nonce;
73
74 /**
75 * PTS Diffie-Hellman Responder Nonce
76 */
77 chunk_t responder_nonce;
78
79 /**
80 * Secret assessment value to be used for TPM Quote as an external data
81 */
82 chunk_t secret;
83
84 /**
85 * Platform and OS Info
86 */
87 char *platform_info;
88
89 /**
90 * TRUE if IMC-PTS, FALSE if IMV-PTS
91 */
92 bool is_imc;
93
94 /**
95 * Do we have an activated TPM
96 */
97 bool has_tpm;
98
99 /**
100 * Contains a TPM_CAP_VERSION_INFO struct
101 */
102 chunk_t tpm_version_info;
103
104 /**
105 * Contains TSS Blob structure for AIK
106 */
107 chunk_t aik_blob;
108
109 /**
110 * Contains a Attestation Identity Key or Certificate
111 */
112 certificate_t *aik;
113
114 /**
115 * List of extended PCR's with corresponding values
116 */
117 linked_list_t *pcrs;
118 };
119
120 METHOD(pts_t, get_proto_caps, pts_proto_caps_flag_t,
121 private_pts_t *this)
122 {
123 return this->proto_caps;
124 }
125
126 METHOD(pts_t, set_proto_caps, void,
127 private_pts_t *this, pts_proto_caps_flag_t flags)
128 {
129 this->proto_caps = flags;
130 DBG2(DBG_PTS, "supported PTS protocol capabilities: %s%s%s%s%s",
131 flags & PTS_PROTO_CAPS_C ? "C" : ".",
132 flags & PTS_PROTO_CAPS_V ? "V" : ".",
133 flags & PTS_PROTO_CAPS_D ? "D" : ".",
134 flags & PTS_PROTO_CAPS_T ? "T" : ".",
135 flags & PTS_PROTO_CAPS_X ? "X" : ".");
136 }
137
138 METHOD(pts_t, get_meas_algorithm, pts_meas_algorithms_t,
139 private_pts_t *this)
140 {
141 return this->algorithm;
142 }
143
144 METHOD(pts_t, set_meas_algorithm, void,
145 private_pts_t *this, pts_meas_algorithms_t algorithm)
146 {
147 hash_algorithm_t hash_alg;
148
149 hash_alg = pts_meas_algo_to_hash(algorithm);
150 DBG2(DBG_PTS, "selected PTS measurement algorithm is %N",
151 hash_algorithm_names, hash_alg);
152 if (hash_alg != HASH_UNKNOWN)
153 {
154 this->algorithm = algorithm;
155 }
156 }
157
158 METHOD(pts_t, get_dh_hash_algorithm, pts_meas_algorithms_t,
159 private_pts_t *this)
160 {
161 return this->dh_hash_algorithm;
162 }
163
164 METHOD(pts_t, set_dh_hash_algorithm, void,
165 private_pts_t *this, pts_meas_algorithms_t algorithm)
166 {
167 hash_algorithm_t hash_alg;
168
169 hash_alg = pts_meas_algo_to_hash(algorithm);
170 DBG2(DBG_PTS, "selected DH hash algorithm is %N",
171 hash_algorithm_names, hash_alg);
172 if (hash_alg != HASH_UNKNOWN)
173 {
174 this->dh_hash_algorithm = algorithm;
175 }
176 }
177
178
179 METHOD(pts_t, create_dh_nonce, bool,
180 private_pts_t *this, pts_dh_group_t group, int nonce_len)
181 {
182 diffie_hellman_group_t dh_group;
183 chunk_t *nonce;
184 rng_t *rng;
185
186 dh_group = pts_dh_group_to_ike(group);
187 DBG2(DBG_PTS, "selected PTS DH group is %N",
188 diffie_hellman_group_names, dh_group);
189 DESTROY_IF(this->dh);
190 this->dh = lib->crypto->create_dh(lib->crypto, dh_group);
191
192 rng = lib->crypto->create_rng(lib->crypto, RNG_STRONG);
193 if (!rng)
194 {
195 DBG1(DBG_PTS, "no rng available");
196 return FALSE;
197 }
198 DBG2(DBG_PTS, "nonce length is %d", nonce_len);
199 nonce = this->is_imc ? &this->responder_nonce : &this->initiator_nonce;
200 chunk_free(nonce);
201 rng->allocate_bytes(rng, nonce_len, nonce);
202 rng->destroy(rng);
203
204 return TRUE;
205 }
206
207 METHOD(pts_t, get_my_public_value, void,
208 private_pts_t *this, chunk_t *value, chunk_t *nonce)
209 {
210 this->dh->get_my_public_value(this->dh, value);
211 *nonce = this->is_imc ? this->responder_nonce : this->initiator_nonce;
212 }
213
214 METHOD(pts_t, set_peer_public_value, void,
215 private_pts_t *this, chunk_t value, chunk_t nonce)
216 {
217 this->dh->set_other_public_value(this->dh, value);
218
219 nonce = chunk_clone(nonce);
220 if (this->is_imc)
221 {
222 this->initiator_nonce = nonce;
223 }
224 else
225 {
226 this->responder_nonce = nonce;
227 }
228 }
229
230 METHOD(pts_t, calculate_secret, bool,
231 private_pts_t *this)
232 {
233 hasher_t *hasher;
234 hash_algorithm_t hash_alg;
235 chunk_t shared_secret;
236
237 /* Check presence of nonces */
238 if (!this->initiator_nonce.len || !this->responder_nonce.len)
239 {
240 DBG1(DBG_PTS, "initiator and/or responder nonce is not available");
241 return FALSE;
242 }
243 DBG3(DBG_PTS, "initiator nonce: %B", &this->initiator_nonce);
244 DBG3(DBG_PTS, "responder nonce: %B", &this->responder_nonce);
245
246 /* Calculate the DH secret */
247 if (this->dh->get_shared_secret(this->dh, &shared_secret) != SUCCESS)
248 {
249 DBG1(DBG_PTS, "shared DH secret computation failed");
250 return FALSE;
251 }
252 DBG4(DBG_PTS, "shared DH secret: %B", &shared_secret);
253
254 /* Calculate the secret assessment value */
255 hash_alg = pts_meas_algo_to_hash(this->dh_hash_algorithm);
256 hasher = lib->crypto->create_hasher(lib->crypto, hash_alg);
257
258 hasher->allocate_hash(hasher, chunk_from_chars('1'), NULL);
259 hasher->allocate_hash(hasher, this->initiator_nonce, NULL);
260 hasher->allocate_hash(hasher, this->responder_nonce, NULL);
261 hasher->allocate_hash(hasher, shared_secret, &this->secret);
262 hasher->destroy(hasher);
263
264 /* The DH secret must be destroyed */
265 chunk_clear(&shared_secret);
266
267 /*
268 * Truncate the hash to 20 bytes to fit the ExternalData
269 * argument of the TPM Quote command
270 */
271 this->secret.len = min(this->secret.len, 20);
272 DBG4(DBG_PTS, "secret assessment value: %B", &this->secret);
273 return TRUE;
274 }
275
276 /**
277 * Print TPM 1.2 Version Info
278 */
279 static void print_tpm_version_info(private_pts_t *this)
280 {
281 TPM_CAP_VERSION_INFO versionInfo;
282 UINT64 offset = 0;
283 TSS_RESULT result;
284
285 result = Trspi_UnloadBlob_CAP_VERSION_INFO(&offset,
286 this->tpm_version_info.ptr, &versionInfo);
287 if (result != TSS_SUCCESS)
288 {
289 DBG1(DBG_PTS, "could not parse tpm version info: tss error 0x%x",
290 result);
291 }
292 else
293 {
294 DBG2(DBG_PTS, "TPM 1.2 Version Info: Chip Version: %hhu.%hhu.%hhu.%hhu,"
295 " Spec Level: %hu, Errata Rev: %hhu, Vendor ID: %.4s",
296 versionInfo.version.major, versionInfo.version.minor,
297 versionInfo.version.revMajor, versionInfo.version.revMinor,
298 versionInfo.specLevel, versionInfo.errataRev,
299 versionInfo.tpmVendorID);
300 }
301 }
302
303 METHOD(pts_t, get_platform_info, char*,
304 private_pts_t *this)
305 {
306 return this->platform_info;
307 }
308
309 METHOD(pts_t, set_platform_info, void,
310 private_pts_t *this, char *info)
311 {
312 free(this->platform_info);
313 this->platform_info = strdup(info);
314 }
315
316 METHOD(pts_t, get_tpm_version_info, bool,
317 private_pts_t *this, chunk_t *info)
318 {
319 if (!this->has_tpm)
320 {
321 return FALSE;
322 }
323 *info = this->tpm_version_info;
324 print_tpm_version_info(this);
325 return TRUE;
326 }
327
328 METHOD(pts_t, set_tpm_version_info, void,
329 private_pts_t *this, chunk_t info)
330 {
331 this->tpm_version_info = chunk_clone(info);
332 print_tpm_version_info(this);
333 }
334
335 /**
336 * Load an AIK Blob (TSS_TSPATTRIB_KEYBLOB_BLOB attribute)
337 */
338 static void load_aik_blob(private_pts_t *this)
339 {
340 char *blob_path;
341 FILE *fp;
342 u_int32_t aikBlobLen;
343
344 blob_path = lib->settings->get_str(lib->settings,
345 "libimcv.plugins.imc-attestation.aik_blob", NULL);
346
347 if (blob_path)
348 {
349 /* Read aik key blob from a file */
350 if ((fp = fopen(blob_path, "r")) == NULL)
351 {
352 DBG1(DBG_PTS, "unable to open AIK Blob file: %s", blob_path);
353 return;
354 }
355
356 fseek(fp, 0, SEEK_END);
357 aikBlobLen = ftell(fp);
358 fseek(fp, 0L, SEEK_SET);
359
360 this->aik_blob = chunk_alloc(aikBlobLen);
361 if (fread(this->aik_blob.ptr, 1, aikBlobLen, fp))
362 {
363 DBG2(DBG_PTS, "loaded AIK Blob from '%s'", blob_path);
364 DBG3(DBG_PTS, "AIK Blob: %B", &this->aik_blob);
365 }
366 else
367 {
368 DBG1(DBG_PTS, "unable to read AIK Blob file '%s'", blob_path);
369 }
370 fclose(fp);
371 return;
372 }
373
374 DBG1(DBG_PTS, "AIK Blob is not available");
375 }
376
377 /**
378 * Load an AIK certificate or public key
379 * the certificate having precedence over the public key if both are present
380 */
381 static void load_aik(private_pts_t *this)
382 {
383 char *cert_path, *key_path;
384
385 cert_path = lib->settings->get_str(lib->settings,
386 "libimcv.plugins.imc-attestation.aik_cert", NULL);
387 key_path = lib->settings->get_str(lib->settings,
388 "libimcv.plugins.imc-attestation.aik_key", NULL);
389
390 if (cert_path)
391 {
392 this->aik = lib->creds->create(lib->creds, CRED_CERTIFICATE,
393 CERT_X509, BUILD_FROM_FILE,
394 cert_path, BUILD_END);
395 if (this->aik)
396 {
397 DBG2(DBG_PTS, "loaded AIK certificate from '%s'", cert_path);
398 return;
399 }
400 }
401 if (key_path)
402 {
403 this->aik = lib->creds->create(lib->creds, CRED_CERTIFICATE,
404 CERT_TRUSTED_PUBKEY, BUILD_FROM_FILE,
405 key_path, BUILD_END);
406 if (this->aik)
407 {
408 DBG2(DBG_PTS, "loaded AIK public key from '%s'", key_path);
409 return;
410 }
411 }
412
413 DBG1(DBG_PTS, "neither AIK certificate nor public key is available");
414 }
415
416 METHOD(pts_t, get_aik, certificate_t*,
417 private_pts_t *this)
418 {
419 return this->aik;
420 }
421
422 METHOD(pts_t, set_aik, void,
423 private_pts_t *this, certificate_t *aik)
424 {
425 DESTROY_IF(this->aik);
426 this->aik = aik->get_ref(aik);
427 }
428
429 METHOD(pts_t, hash_file, bool,
430 private_pts_t *this, hasher_t *hasher, char *pathname, u_char *hash)
431 {
432 u_char buffer[PTS_BUF_SIZE];
433 FILE *file;
434 int bytes_read;
435
436 file = fopen(pathname, "rb");
437 if (!file)
438 {
439 DBG1(DBG_PTS," file '%s' can not be opened, %s", pathname,
440 strerror(errno));
441 return FALSE;
442 }
443 while (TRUE)
444 {
445 bytes_read = fread(buffer, 1, sizeof(buffer), file);
446 if (bytes_read > 0)
447 {
448 hasher->get_hash(hasher, chunk_create(buffer, bytes_read), NULL);
449 }
450 else
451 {
452 hasher->get_hash(hasher, chunk_empty, hash);
453 break;
454 }
455 }
456 fclose(file);
457
458 return TRUE;
459 }
460
461 /**
462 * Get the relative filename of a fully qualified file pathname
463 */
464 static char* get_filename(char *pathname)
465 {
466 char *pos, *filename;
467
468 pos = filename = pathname;
469 while (pos && *(++pos) != '\0')
470 {
471 filename = pos;
472 pos = strchr(filename, '/');
473 }
474 return filename;
475 }
476
477 METHOD(pts_t, is_path_valid, bool,
478 private_pts_t *this, char *path, pts_error_code_t *error_code)
479 {
480 struct stat st;
481
482 *error_code = 0;
483
484 if (!stat(path, &st))
485 {
486 return TRUE;
487 }
488 else if (errno == ENOENT || errno == ENOTDIR)
489 {
490 DBG1(DBG_PTS, "file/directory does not exist %s", path);
491 *error_code = TCG_PTS_FILE_NOT_FOUND;
492 }
493 else if (errno == EFAULT)
494 {
495 DBG1(DBG_PTS, "bad address %s", path);
496 *error_code = TCG_PTS_INVALID_PATH;
497 }
498 else
499 {
500 DBG1(DBG_PTS, "error: %s occured while validating path: %s",
501 strerror(errno), path);
502 return FALSE;
503 }
504
505 return TRUE;
506 }
507
508 METHOD(pts_t, do_measurements, pts_file_meas_t*,
509 private_pts_t *this, u_int16_t request_id, char *pathname, bool is_directory)
510 {
511 hasher_t *hasher;
512 hash_algorithm_t hash_alg;
513 u_char hash[HASH_SIZE_SHA384];
514 chunk_t measurement;
515 pts_file_meas_t *measurements;
516
517 /* Create a hasher */
518 hash_alg = pts_meas_algo_to_hash(this->algorithm);
519 hasher = lib->crypto->create_hasher(lib->crypto, hash_alg);
520 if (!hasher)
521 {
522 DBG1(DBG_PTS, "hasher %N not available", hash_algorithm_names, hash_alg);
523 return NULL;
524 }
525
526 /* Create a measurement object */
527 measurements = pts_file_meas_create(request_id);
528
529 /* Link the hash to the measurement and set the measurement length */
530 measurement = chunk_create(hash, hasher->get_hash_size(hasher));
531
532 if (is_directory)
533 {
534 enumerator_t *enumerator;
535 char *rel_name, *abs_name;
536 struct stat st;
537
538 enumerator = enumerator_create_directory(pathname);
539 if (!enumerator)
540 {
541 DBG1(DBG_PTS," directory '%s' can not be opened, %s", pathname,
542 strerror(errno));
543 hasher->destroy(hasher);
544 measurements->destroy(measurements);
545 return NULL;
546 }
547 while (enumerator->enumerate(enumerator, &rel_name, &abs_name, &st))
548 {
549 /* measure regular files only */
550 if (S_ISREG(st.st_mode) && *rel_name != '.')
551 {
552 if (!hash_file(this, hasher, abs_name, hash))
553 {
554 enumerator->destroy(enumerator);
555 hasher->destroy(hasher);
556 measurements->destroy(measurements);
557 return NULL;
558 }
559 DBG2(DBG_PTS, " %#B for '%s'", &measurement, rel_name);
560 measurements->add(measurements, rel_name, measurement);
561 }
562 }
563 enumerator->destroy(enumerator);
564 }
565 else
566 {
567 char *filename;
568
569 if (!hash_file(this, hasher, pathname, hash))
570 {
571 hasher->destroy(hasher);
572 measurements->destroy(measurements);
573 return NULL;
574 }
575 filename = get_filename(pathname);
576 DBG2(DBG_PTS, " %#B for '%s'", &measurement, filename);
577 measurements->add(measurements, filename, measurement);
578 }
579 hasher->destroy(hasher);
580
581 return measurements;
582 }
583
584 /**
585 * Obtain statistical information describing a file
586 */
587 static bool file_metadata(char *pathname, pts_file_metadata_t **entry)
588 {
589 struct stat st;
590 pts_file_metadata_t *this;
591
592 this = malloc_thing(pts_file_metadata_t);
593
594 if (stat(pathname, &st))
595 {
596 DBG1(DBG_PTS, "Unable to obtain statistics about '%s'", pathname);
597 return FALSE;
598 }
599
600 if (S_ISREG(st.st_mode))
601 {
602 this->type = PTS_FILE_REGULAR;
603 }
604 else if (S_ISDIR(st.st_mode))
605 {
606 this->type = PTS_FILE_DIRECTORY;
607 }
608 else if (S_ISCHR(st.st_mode))
609 {
610 this->type = PTS_FILE_CHAR_SPEC;
611 }
612 else if (S_ISBLK(st.st_mode))
613 {
614 this->type = PTS_FILE_BLOCK_SPEC;
615 }
616 else if (S_ISFIFO(st.st_mode))
617 {
618 this->type = PTS_FILE_FIFO;
619 }
620 else if (S_ISLNK(st.st_mode))
621 {
622 this->type = PTS_FILE_SYM_LINK;
623 }
624 else if (S_ISSOCK(st.st_mode))
625 {
626 this->type = PTS_FILE_SOCKET;
627 }
628 else
629 {
630 this->type = PTS_FILE_OTHER;
631 }
632
633 this->filesize = st.st_size;
634 this->created = st.st_ctime;
635 this->modified = st.st_mtime;
636 this->accessed = st.st_atime;
637 this->owner = st.st_uid;
638 this->group = st.st_gid;
639
640 *entry = this;
641 return TRUE;
642 }
643
644 METHOD(pts_t, get_metadata, pts_file_meta_t*,
645 private_pts_t *this, char *pathname, bool is_directory)
646 {
647 pts_file_meta_t *metadata;
648 pts_file_metadata_t *entry;
649
650 /* Create a metadata object */
651 metadata = pts_file_meta_create();
652
653 if (is_directory)
654 {
655 enumerator_t *enumerator;
656 char *rel_name, *abs_name;
657 struct stat st;
658
659 enumerator = enumerator_create_directory(pathname);
660 if (!enumerator)
661 {
662 DBG1(DBG_PTS," directory '%s' can not be opened, %s", pathname,
663 strerror(errno));
664 metadata->destroy(metadata);
665 return NULL;
666 }
667 while (enumerator->enumerate(enumerator, &rel_name, &abs_name, &st))
668 {
669 /* measure regular files only */
670 if (S_ISREG(st.st_mode) && *rel_name != '.')
671 {
672 if (!file_metadata(abs_name, &entry))
673 {
674 enumerator->destroy(enumerator);
675 metadata->destroy(metadata);
676 return NULL;
677 }
678 entry->filename = strdup(rel_name);
679 metadata->add(metadata, entry);
680 }
681 }
682 enumerator->destroy(enumerator);
683 }
684 else
685 {
686 if (!file_metadata(pathname, &entry))
687 {
688 metadata->destroy(metadata);
689 return NULL;
690 }
691 entry->filename = strdup(get_filename(pathname));
692 metadata->add(metadata, entry);
693 }
694
695 return metadata;
696 }
697
698 METHOD(pts_t, read_pcr, bool,
699 private_pts_t *this, u_int32_t pcr_num, chunk_t *output)
700 {
701 TSS_HCONTEXT hContext;
702 TSS_HTPM hTPM;
703 TSS_RESULT result;
704 u_int32_t pcr_length;
705 chunk_t pcr_value;
706
707 result = Tspi_Context_Create(&hContext);
708 if (result != TSS_SUCCESS)
709 {
710 DBG1(DBG_PTS, "TPM context could not be created: tss error 0x%x",
711 result);
712 return FALSE;
713 }
714
715 result = Tspi_Context_Connect(hContext, NULL);
716 if (result != TSS_SUCCESS)
717 {
718 goto err;
719 }
720 result = Tspi_Context_GetTpmObject (hContext, &hTPM);
721 if (result != TSS_SUCCESS)
722 {
723 goto err;
724 }
725 pcr_value = chunk_alloc(PCR_LEN);
726 result = Tspi_TPM_PcrRead(hTPM, pcr_num, &pcr_length, &pcr_value.ptr);
727 if (result != TSS_SUCCESS)
728 {
729 goto err;
730 }
731
732 *output = pcr_value;
733 *output = chunk_clone(*output);
734
735 chunk_clear(&pcr_value);
736 DBG3(DBG_PTS, "PCR %d value:%B", pcr_num, output);
737 Tspi_Context_Close(hContext);
738 return TRUE;
739
740 err:
741 chunk_clear(&pcr_value);
742 DBG1(DBG_PTS, "TPM not available: tss error 0x%x", result);
743 Tspi_Context_Close(hContext);
744 return FALSE;
745 }
746
747 METHOD(pts_t, extend_pcr, bool,
748 private_pts_t *this, u_int32_t pcr_num, chunk_t input, chunk_t *output)
749 {
750 TSS_HCONTEXT hContext;
751 TSS_HTPM hTPM;
752 TSS_RESULT result;
753 u_int32_t pcr_length;
754 chunk_t pcr_value;
755
756 result = Tspi_Context_Create(&hContext);
757 if (result != TSS_SUCCESS)
758 {
759 DBG1(DBG_PTS, "TPM context could not be created: tss error 0x%x",
760 result);
761 return FALSE;
762 }
763 result = Tspi_Context_Connect(hContext, NULL);
764 if (result != TSS_SUCCESS)
765 {
766 goto err;
767 }
768 result = Tspi_Context_GetTpmObject (hContext, &hTPM);
769 if (result != TSS_SUCCESS)
770 {
771 goto err;
772 }
773
774 pcr_value = chunk_alloc(PCR_LEN);
775 result = Tspi_TPM_PcrExtend(hTPM, pcr_num, PCR_LEN, input.ptr,
776 NULL, &pcr_length, &pcr_value.ptr);
777 if (result != TSS_SUCCESS)
778 {
779 goto err;
780 }
781
782 *output = pcr_value;
783 *output = chunk_clone(*output);
784
785 chunk_clear(&pcr_value);
786 Tspi_Context_Close(hContext);
787 DBG3(DBG_PTS, "PCR %d extended with: %B", pcr_num, &input);
788 DBG3(DBG_PTS, "PCR %d value after extend: %B", pcr_num, output);
789 return TRUE;
790
791 err:
792 chunk_clear(&pcr_value);
793 DBG1(DBG_PTS, "TPM not available: tss error 0x%x", result);
794 Tspi_Context_Close(hContext);
795 return FALSE;
796 }
797
798 METHOD(pts_t, quote_tpm, bool,
799 private_pts_t *this, u_int32_t *pcrs, u_int32_t num_of_pcrs,
800 chunk_t *pcr_composite, chunk_t *quote_signature)
801 {
802 TSS_HCONTEXT hContext;
803 TSS_HTPM hTPM;
804 TSS_HKEY hAIK;
805 TSS_HKEY hSRK;
806 TSS_HPOLICY srkUsagePolicy;
807 TSS_UUID SRK_UUID = TSS_UUID_SRK;
808 BYTE secret[] = TSS_WELL_KNOWN_SECRET;
809 TSS_HPCRS hPcrComposite;
810 TSS_VALIDATION valData;
811 u_int32_t i;
812 TSS_RESULT result;
813 chunk_t pcr_comp, quote_sign;
814
815 result = Tspi_Context_Create(&hContext);
816 if (result != TSS_SUCCESS)
817 {
818 DBG1(DBG_PTS, "TPM context could not be created: tss error 0x%x",
819 result);
820 return FALSE;
821 }
822 result = Tspi_Context_Connect(hContext, NULL);
823 if (result != TSS_SUCCESS)
824 {
825 goto err1;
826 }
827 result = Tspi_Context_GetTpmObject (hContext, &hTPM);
828 if (result != TSS_SUCCESS)
829 {
830 goto err1;
831 }
832
833 /* Retrieve SRK from TPM and set the authentication to well known secret*/
834 result = Tspi_Context_LoadKeyByUUID(hContext, TSS_PS_TYPE_SYSTEM,
835 SRK_UUID, &hSRK);
836 if (result != TSS_SUCCESS)
837 {
838 goto err1;
839 }
840
841 result = Tspi_GetPolicyObject(hSRK, TSS_POLICY_USAGE, &srkUsagePolicy);
842 if (result != TSS_SUCCESS)
843 {
844 goto err1;
845 }
846 result = Tspi_Policy_SetSecret(srkUsagePolicy, TSS_SECRET_MODE_SHA1,
847 20, secret);
848 if (result != TSS_SUCCESS)
849 {
850 goto err1;
851 }
852
853 result = Tspi_Context_LoadKeyByBlob (hContext, hSRK, this->aik_blob.len,
854 this->aik_blob.ptr, &hAIK);
855 if (result != TSS_SUCCESS)
856 {
857 goto err1;
858 }
859
860 /* Create PCR composite object */
861 result = Tspi_Context_CreateObject(hContext,
862 TSS_OBJECT_TYPE_PCRS, 0, &hPcrComposite);
863 if (result != TSS_SUCCESS)
864 {
865 goto err2;
866 }
867
868 /* Select PCR's */
869 for (i = 0; i < num_of_pcrs ; i++)
870 {
871 if (pcrs[i] < 0 || pcrs[i] >= MAX_NUM_PCR )
872 {
873 DBG1(DBG_PTS, "Invalid PCR number: %d", pcrs[i]);
874 goto err3;
875 }
876 result = Tspi_PcrComposite_SelectPcrIndex(hPcrComposite, pcrs[i]);
877 if (result != TSS_SUCCESS)
878 {
879 goto err3;
880 }
881 }
882
883 /* Set the Validation Data */
884 valData.ulExternalDataLength = this->secret.len;
885 valData.rgbExternalData = (BYTE *)this->secret.ptr;
886
887
888 /* TPM Quote */
889 result = Tspi_TPM_Quote(hTPM, hAIK, hPcrComposite, &valData);
890 if (result != TSS_SUCCESS)
891 {
892 goto err4;
893 }
894
895 /* Set output chunks */
896 pcr_comp = chunk_alloc(HASH_SIZE_SHA1);
897 memcpy(pcr_comp.ptr, valData.rgbData + 8, HASH_SIZE_SHA1);
898 *pcr_composite = pcr_comp;
899 *pcr_composite = chunk_clone(*pcr_composite);
900 DBG3(DBG_PTS, "Hash of PCR Composite: %B",pcr_composite);
901
902 quote_sign = chunk_alloc(valData.ulValidationDataLength);
903 memcpy(quote_sign.ptr, valData.rgbValidationData,
904 valData.ulValidationDataLength);
905 *quote_signature = quote_sign;
906 *quote_signature = chunk_clone(*quote_signature);
907 DBG3(DBG_PTS, "TOM Quote Signature: %B",quote_signature);
908
909 chunk_clear(&quote_sign);
910 Tspi_Context_FreeMemory(hContext, NULL);
911 Tspi_Context_CloseObject(hContext, hPcrComposite);
912 Tspi_Context_CloseObject(hContext, hAIK);
913 Tspi_Context_Close(hContext);
914 free(pcrs);
915 return TRUE;
916
917 /* Cleanup */
918 err4:
919 Tspi_Context_FreeMemory(hContext, NULL);
920
921 err3:
922 Tspi_Context_CloseObject(hContext, hPcrComposite);
923
924 err2:
925 Tspi_Context_CloseObject(hContext, hAIK);
926
927 err1:
928 Tspi_Context_Close(hContext);
929 free(pcrs);
930 DBG1(DBG_PTS, "TPM not available: tss error 0x%x", result);
931 return FALSE;
932 }
933
934 /**
935 * Comparison function for pcr_entry_t struct
936 */
937 static int pcr_entry_compare(const pcr_entry_t *a, const pcr_entry_t *b)
938 {
939 return (a->pcr_number - b->pcr_number);
940 }
941
942 static int pcr_entry_compare_qsort(const void *a, const void *b)
943 {
944 return pcr_entry_compare(*(const pcr_entry_t *const *)a
945 , *(const pcr_entry_t *const *)b);
946 }
947
948 METHOD(pts_t, add_pcr_entry, void,
949 private_pts_t *this, pcr_entry_t *new)
950 {
951 enumerator_t *e;
952 pcr_entry_t *entry;
953
954 if (!this->pcrs)
955 {
956 this->pcrs = linked_list_create();
957 }
958
959 e = this->pcrs->create_enumerator(this->pcrs);
960 while (e->enumerate(e, &entry))
961 {
962 if (entry->pcr_number == new->pcr_number)
963 {
964 DBG4(DBG_PTS, "updating already added PCR%d value",
965 entry->pcr_number);
966 this->pcrs->remove_at(this->pcrs, e);
967 free(entry);
968 break;
969 }
970 }
971 DESTROY_IF(e);
972
973 this->pcrs->insert_last(this->pcrs, new);
974
975 qsort(this->pcrs, this->pcrs->get_count(this->pcrs),
976 sizeof(pcr_entry_t *), pcr_entry_compare_qsort);
977 }
978
979 /**
980 * 1. build a TCPA_PCR_COMPOSITE structure which contains (pcrCompositeBuf)
981 * TCPA_PCR_SELECTION structure (bitmask length + bitmask)
982 * UINT32 (network order) gives the number of bytes following (pcr entries * 20)
983 * TCPA_PCRVALUE[] with the pcr values
984 *
985 * The first two bytes of the message represent the length
986 * of the bitmask that follows. The bitmask represents the
987 * requested PCRs to be quoted.
988 *
989 * TPM Main-Part 2 TPM Structures_v1.2 8.1
990 * The bitmask is in big endian order"
991 *
992 * BYTE 1 BYTE 2 ...
993 * Bit: 1 1 1 1 0 0 0 0 1 1 1 1 0 0 0 0 ...
994 * Pcr: 7 6 5 4 3 2 1 0 15 14 13 12 11 10 9 8 ...
995 *
996 * 2. SHA1(pcrCompositeBuf)
997 *
998 * 3. build a TCPA_QUOTE_INFO structure which contains
999 * 4 bytes of version
1000 * 4 bytes 'Q' 'U' 'O' 'T'
1001 * 20 byte SHA1 of TCPA_PCR_COMPOSITE
1002 * 20 byte nonce
1003 */
1004
1005 METHOD(pts_t, get_quote_info, bool,
1006 private_pts_t *this, pts_meas_algorithms_t composite_algo,
1007 chunk_t *out_pcr_composite, chunk_t *out_quote_info)
1008 {
1009 enumerator_t *e;
1010 pcr_entry_t *pcr_entry;
1011 chunk_t pcr_composite, hash_pcr_composite;
1012 u_int32_t pcr_composite_len;
1013 bio_writer_t *writer;
1014 u_int8_t mask_bytes[PCR_MASK_LEN] = {0,0,0}, i;
1015 hasher_t *hasher;
1016
1017 if (this->pcrs->get_count(this->pcrs) == 0)
1018 {
1019 DBG1(DBG_PTS, "PCR entries unavailable, unable to construct "
1020 "TPM Quote Info");
1021 return FALSE;
1022 }
1023
1024 pcr_composite_len = 2 + PCR_MASK_LEN + 4 +
1025 this->pcrs->get_count(this->pcrs) * PCR_LEN;
1026
1027 writer = bio_writer_create(pcr_composite_len);
1028 /* Lenght of the bist mask field */
1029 writer->write_uint16(writer, PCR_MASK_LEN);
1030 /* Bit mask indicating selected PCRs */
1031 e = this->pcrs->create_enumerator(this->pcrs);
1032 while (e->enumerate(e, &pcr_entry))
1033 {
1034 u_int32_t index = pcr_entry->pcr_number;
1035 mask_bytes[index / 8] |= (1 << (index % 8));
1036 }
1037 e->destroy(e);
1038
1039 for (i = 0; i< PCR_MASK_LEN ; i++)
1040 {
1041 writer->write_uint8(writer, mask_bytes[i]);
1042 }
1043
1044 /* Lenght of the pcr entries */
1045 writer->write_uint32(writer, this->pcrs->get_count(this->pcrs) * PCR_LEN);
1046 /* Actual PCR values */
1047 e = this->pcrs->create_enumerator(this->pcrs);
1048 while (e->enumerate(e, &pcr_entry))
1049 {
1050 writer->write_data(writer, chunk_create(pcr_entry->pcr_value, PCR_LEN));
1051 }
1052 free(pcr_entry);
1053 e->destroy(e);
1054
1055 /* PCR Composite structure */
1056 pcr_composite = chunk_clone(writer->get_buf(writer));
1057 writer->destroy(writer);
1058
1059 writer = bio_writer_create(TPM_QUOTE_INFO_LEN);
1060 /* Version number */
1061 writer->write_uint8(writer, 1);
1062 writer->write_uint8(writer, 1);
1063 writer->write_uint8(writer, 0);
1064 writer->write_uint8(writer, 0);
1065
1066 /* Magic QUOT value, depends on TPM Ordinal */
1067 writer->write_uint8(writer, 'Q');
1068 writer->write_uint8(writer, 'U');
1069 writer->write_uint8(writer, 'O');
1070 writer->write_uint8(writer, 'T');
1071
1072 /* Output the TPM_PCR_COMPOSITE expected from IMC */
1073 if (composite_algo)
1074 {
1075 hash_algorithm_t algo;
1076
1077 algo = pts_meas_algo_to_hash(composite_algo);
1078 hasher = lib->crypto->create_hasher(lib->crypto, algo);
1079
1080 /* Hash the PCR Composite Structure */
1081 hasher->allocate_hash(hasher, pcr_composite, out_pcr_composite);
1082 DBG4(DBG_PTS, "Hash of calculated PCR Composite: %B", out_pcr_composite);
1083 hasher->destroy(hasher);
1084 }
1085 else
1086 {
1087 *out_pcr_composite = chunk_clone(pcr_composite);
1088 DBG4(DBG_PTS, "calculated PCR Composite: %B", out_pcr_composite);
1089 }
1090
1091 /* SHA1 hash of PCR Composite to construct TPM_QUOTE_INFO */
1092 hasher = lib->crypto->create_hasher(lib->crypto, HASH_SHA1);
1093 hasher->allocate_hash(hasher, pcr_composite, &hash_pcr_composite);
1094 hasher->destroy(hasher);
1095
1096 writer->write_data(writer, hash_pcr_composite);
1097 chunk_clear(&pcr_composite);
1098 chunk_clear(&hash_pcr_composite);
1099
1100 if (!this->secret.ptr)
1101 {
1102 DBG1(DBG_PTS, "Secret assessment value unavailable",
1103 "unable to construct TPM Quote Info");
1104 chunk_clear(out_pcr_composite);
1105 writer->destroy(writer);
1106 return FALSE;
1107 }
1108 /* Secret assessment value 20 bytes (nonce) */
1109 writer->write_data(writer, this->secret);
1110 /* TPM Quote Info */
1111 *out_quote_info = chunk_clone(writer->get_buf(writer));
1112 DBG4(DBG_PTS, "Calculated TPM Quote Info: %B", out_quote_info);
1113 writer->destroy(writer);
1114
1115 return TRUE;
1116 }
1117
1118 METHOD(pts_t, verify_quote_signature, bool,
1119 private_pts_t *this, chunk_t data, chunk_t signature)
1120 {
1121 public_key_t *aik_pub_key;
1122 chunk_t key_encoding;
1123 EVP_PKEY *pkey = NULL;
1124 RSA *rsa = NULL;
1125 unsigned char *p;
1126
1127 aik_pub_key = this->aik->get_public_key(this->aik);
1128 if (!aik_pub_key)
1129 {
1130 DBG1(DBG_PTS, "failed to get public key from AIK certificate");
1131 return FALSE;
1132 }
1133
1134 /** Implementation using strongswan -> not working */
1135 /**if (!aik_pub_key->verify(aik_pub_key, SIGN_RSA_EMSA_PKCS1_SHA1,
1136 data, signature))
1137 {
1138 DBG1(DBG_PTS, "signature verification failed for TPM Quote Info");
1139 goto cleanup;
1140 }
1141 */
1142
1143 if (!aik_pub_key->get_encoding(aik_pub_key,
1144 PUBKEY_SPKI_ASN1_DER, &key_encoding))
1145 {
1146 DBG1(DBG_PTS, "failed to get encoding of AIK public key");
1147 goto cleanup;
1148 }
1149
1150 p = key_encoding.ptr;
1151 pkey = d2i_PUBKEY(NULL, (const unsigned char**)&p, key_encoding.len);
1152 if (!pkey)
1153 {
1154 DBG1(DBG_PTS, "failed to get EVP_PKEY object from AIK public key");
1155 goto cleanup;
1156 }
1157
1158 rsa = EVP_PKEY_get1_RSA(pkey);
1159 if (!rsa)
1160 {
1161 DBG1(DBG_PTS, "failed to get RSA object from EVP_PKEY");
1162 goto cleanup;
1163 }
1164
1165 if (RSA_verify(NID_sha1, data.ptr, data.len,
1166 signature.ptr, signature.len, rsa) != 1)
1167 {
1168 DBG1(DBG_PTS, "signature verification failed for TPM Quote Info");
1169 goto cleanup;
1170 }
1171
1172 RSA_free(rsa);
1173 EVP_PKEY_free(pkey);
1174 if (key_encoding.ptr)
1175 {
1176 chunk_clear(&key_encoding);
1177 }
1178 aik_pub_key->destroy(aik_pub_key);
1179 return TRUE;
1180
1181 cleanup:
1182 if (rsa)
1183 {
1184 RSA_free(rsa);
1185 }
1186 if (pkey)
1187 {
1188 EVP_PKEY_free(pkey);
1189 }
1190 if (key_encoding.ptr)
1191 {
1192 chunk_clear(&key_encoding);
1193 }
1194 DESTROY_IF(aik_pub_key);
1195 return FALSE;
1196 }
1197
1198 METHOD(pts_t, destroy, void,
1199 private_pts_t *this)
1200 {
1201 DESTROY_IF(this->aik);
1202 DESTROY_IF(this->dh);
1203 DESTROY_IF(this->pcrs);
1204 free(this->initiator_nonce.ptr);
1205 free(this->responder_nonce.ptr);
1206 free(this->secret.ptr);
1207 free(this->platform_info);
1208 free(this->aik_blob.ptr);
1209 free(this->tpm_version_info.ptr);
1210 free(this);
1211 }
1212
1213 /**
1214 * Determine Linux distribution and hardware platform
1215 */
1216 static char* extract_platform_info(void)
1217 {
1218 FILE *file;
1219 char buf[BUF_LEN], *pos, *value = NULL;
1220 int i, len;
1221 struct utsname uninfo;
1222
1223 /* Linux/Unix distribution release info (from http://linuxmafia.com) */
1224 const char* releases[] = {
1225 "/etc/lsb-release", "/etc/debian_version",
1226 "/etc/SuSE-release", "/etc/novell-release",
1227 "/etc/sles-release", "/etc/redhat-release",
1228 "/etc/fedora-release", "/etc/gentoo-release",
1229 "/etc/slackware-version", "/etc/annvix-release",
1230 "/etc/arch-release", "/etc/arklinux-release",
1231 "/etc/aurox-release", "/etc/blackcat-release",
1232 "/etc/cobalt-release", "/etc/conectiva-release",
1233 "/etc/debian_release", "/etc/immunix-release",
1234 "/etc/lfs-release", "/etc/linuxppc-release",
1235 "/etc/mandrake-release", "/etc/mandriva-release",
1236 "/etc/mandrakelinux-release", "/etc/mklinux-release",
1237 "/etc/pld-release", "/etc/redhat_version",
1238 "/etc/slackware-release", "/etc/e-smith-release",
1239 "/etc/release", "/etc/sun-release",
1240 "/etc/tinysofa-release", "/etc/turbolinux-release",
1241 "/etc/ultrapenguin-release", "/etc/UnitedLinux-release",
1242 "/etc/va-release", "/etc/yellowdog-release"
1243 };
1244
1245 const char description[] = "DISTRIB_DESCRIPTION=\"";
1246
1247 for (i = 0; i < countof(releases); i++)
1248 {
1249 file = fopen(releases[i], "r");
1250 if (!file)
1251 {
1252 continue;
1253 }
1254 fseek(file, 0, SEEK_END);
1255 len = min(ftell(file), sizeof(buf)-1);
1256 rewind(file);
1257 buf[len] = '\0';
1258 if (fread(buf, 1, len, file) != len)
1259 {
1260 DBG1(DBG_PTS, "failed to read file '%s'", releases[i]);
1261 fclose(file);
1262 return NULL;
1263 }
1264 fclose(file);
1265
1266 if (i == 0) /* LSB release */
1267 {
1268 pos = strstr(buf, description);
1269 if (!pos)
1270 {
1271 DBG1(DBG_PTS, "failed to find begin of lsb-release "
1272 "DESCRIPTION field");
1273 return NULL;
1274 }
1275 value = pos + strlen(description);
1276 pos = strchr(value, '"');
1277 if (!pos)
1278 {
1279 DBG1(DBG_PTS, "failed to find end of lsb-release "
1280 "DESCRIPTION field");
1281 return NULL;
1282 }
1283 }
1284 else
1285 {
1286 value = buf;
1287 pos = strchr(value, '\n');
1288 if (!pos)
1289 {
1290 DBG1(DBG_PTS, "failed to find end of release string");
1291 return NULL;
1292 }
1293 }
1294 break;
1295 }
1296
1297 if (!value)
1298 {
1299 DBG1(DBG_PTS, "no distribution release file found");
1300 return NULL;
1301 }
1302
1303 if (uname(&uninfo) < 0)
1304 {
1305 DBG1(DBG_PTS, "could not retrieve machine architecture");
1306 return NULL;
1307 }
1308
1309 *pos++ = ' ';
1310 len = sizeof(buf)-1 + (pos - buf);
1311 strncpy(pos, uninfo.machine, len);
1312
1313 DBG1(DBG_PTS, "platform is '%s'", value);
1314 return strdup(value);
1315 }
1316
1317 /**
1318 * Check for a TPM by querying for TPM Version Info
1319 */
1320 static bool has_tpm(private_pts_t *this)
1321 {
1322 TSS_HCONTEXT hContext;
1323 TSS_HTPM hTPM;
1324 TSS_RESULT result;
1325 u_int32_t version_info_len;
1326
1327 result = Tspi_Context_Create(&hContext);
1328 if (result != TSS_SUCCESS)
1329 {
1330 DBG1(DBG_PTS, "TPM context could not be created: tss error 0x%x",
1331 result);
1332 return FALSE;
1333 }
1334 result = Tspi_Context_Connect(hContext, NULL);
1335 if (result != TSS_SUCCESS)
1336 {
1337 goto err;
1338 }
1339 result = Tspi_Context_GetTpmObject (hContext, &hTPM);
1340 if (result != TSS_SUCCESS)
1341 {
1342 goto err;
1343 }
1344 result = Tspi_TPM_GetCapability(hTPM, TSS_TPMCAP_VERSION_VAL, 0, NULL,
1345 &version_info_len,
1346 &this->tpm_version_info.ptr);
1347 this->tpm_version_info.len = version_info_len;
1348 if (result != TSS_SUCCESS)
1349 {
1350 goto err;
1351 }
1352 this->tpm_version_info = chunk_clone(this->tpm_version_info);
1353
1354 Tspi_Context_FreeMemory(hContext, NULL);
1355 Tspi_Context_Close(hContext);
1356 return TRUE;
1357
1358 err:
1359 DBG1(DBG_PTS, "TPM not available: tss error 0x%x", result);
1360 Tspi_Context_FreeMemory(hContext, NULL);
1361 Tspi_Context_Close(hContext);
1362 return FALSE;
1363 }
1364
1365 /**
1366 * See header
1367 */
1368 pts_t *pts_create(bool is_imc)
1369 {
1370 private_pts_t *this;
1371
1372 INIT(this,
1373 .public = {
1374 .get_proto_caps = _get_proto_caps,
1375 .set_proto_caps = _set_proto_caps,
1376 .get_meas_algorithm = _get_meas_algorithm,
1377 .set_meas_algorithm = _set_meas_algorithm,
1378 .get_dh_hash_algorithm = _get_dh_hash_algorithm,
1379 .set_dh_hash_algorithm = _set_dh_hash_algorithm,
1380 .create_dh_nonce = _create_dh_nonce,
1381 .get_my_public_value = _get_my_public_value,
1382 .set_peer_public_value = _set_peer_public_value,
1383 .calculate_secret = _calculate_secret,
1384 .get_platform_info = _get_platform_info,
1385 .set_platform_info = _set_platform_info,
1386 .get_tpm_version_info = _get_tpm_version_info,
1387 .set_tpm_version_info = _set_tpm_version_info,
1388 .get_aik = _get_aik,
1389 .set_aik = _set_aik,
1390 .is_path_valid = _is_path_valid,
1391 .hash_file = _hash_file,
1392 .do_measurements = _do_measurements,
1393 .get_metadata = _get_metadata,
1394 .read_pcr = _read_pcr,
1395 .extend_pcr = _extend_pcr,
1396 .quote_tpm = _quote_tpm,
1397 .add_pcr_entry = _add_pcr_entry,
1398 .get_quote_info = _get_quote_info,
1399 .verify_quote_signature = _verify_quote_signature,
1400 .destroy = _destroy,
1401 },
1402 .is_imc = is_imc,
1403 .proto_caps = PTS_PROTO_CAPS_V,
1404 .algorithm = PTS_MEAS_ALGO_SHA256,
1405 .dh_hash_algorithm = PTS_MEAS_ALGO_SHA256,
1406 );
1407
1408 if (is_imc)
1409 {
1410 this->platform_info = extract_platform_info();
1411
1412 if (has_tpm(this))
1413 {
1414 this->has_tpm = TRUE;
1415 this->proto_caps |= PTS_PROTO_CAPS_T | PTS_PROTO_CAPS_D;
1416 load_aik(this);
1417 load_aik_blob(this);
1418 }
1419 }
1420 else
1421 {
1422 this->proto_caps |= PTS_PROTO_CAPS_T | PTS_PROTO_CAPS_D;
1423 }
1424
1425 return &this->public;
1426 }