shortened fieldnames of pts_file_metadata_t struct
[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 this->filename = strdup(pathname);
601 this->meta_length = PTS_FILE_METADATA_SIZE + strlen(this->filename);
602
603 if (S_ISREG(st.st_mode))
604 {
605 this->type = PTS_FILE_REGULAR;
606 }
607 else if (S_ISDIR(st.st_mode))
608 {
609 this->type = PTS_FILE_DIRECTORY;
610 }
611 else if (S_ISCHR(st.st_mode))
612 {
613 this->type = PTS_FILE_CHAR_SPEC;
614 }
615 else if (S_ISBLK(st.st_mode))
616 {
617 this->type = PTS_FILE_BLOCK_SPEC;
618 }
619 else if (S_ISFIFO(st.st_mode))
620 {
621 this->type = PTS_FILE_FIFO;
622 }
623 else if (S_ISLNK(st.st_mode))
624 {
625 this->type = PTS_FILE_SYM_LINK;
626 }
627 else if (S_ISSOCK(st.st_mode))
628 {
629 this->type = PTS_FILE_SOCKET;
630 }
631 else
632 {
633 this->type = PTS_FILE_OTHER;
634 }
635
636 this->filesize = st.st_size;
637 this->created = st.st_ctime;
638 this->modified = st.st_mtime;
639 this->accessed = st.st_atime;
640 this->owner = st.st_uid;
641 this->group = st.st_gid;
642
643 *entry = this;
644 return TRUE;
645 }
646
647 METHOD(pts_t, get_metadata, pts_file_meta_t*,
648 private_pts_t *this, char *pathname, bool is_directory)
649 {
650 pts_file_meta_t *metadata;
651 pts_file_metadata_t *entry;
652
653 /* Create a metadata object */
654 metadata = pts_file_meta_create();
655
656 if (is_directory)
657 {
658 enumerator_t *enumerator;
659 char *rel_name, *abs_name;
660 struct stat st;
661
662 enumerator = enumerator_create_directory(pathname);
663 if (!enumerator)
664 {
665 DBG1(DBG_PTS," directory '%s' can not be opened, %s", pathname,
666 strerror(errno));
667 metadata->destroy(metadata);
668 return NULL;
669 }
670 while (enumerator->enumerate(enumerator, &rel_name, &abs_name, &st))
671 {
672 /* measure regular files only */
673 if (S_ISREG(st.st_mode) && *rel_name != '.')
674 {
675 if (!file_metadata(abs_name, &entry))
676 {
677 enumerator->destroy(enumerator);
678 metadata->destroy(metadata);
679 return NULL;
680 }
681 metadata->add(metadata, entry);
682 }
683 }
684 enumerator->destroy(enumerator);
685 }
686 else
687 {
688 char *filename;
689
690 if (!file_metadata(pathname, &entry))
691 {
692 metadata->destroy(metadata);
693 return NULL;
694 }
695 filename = get_filename(pathname);
696 metadata->add(metadata, entry);
697 }
698
699 return metadata;
700 }
701
702 METHOD(pts_t, read_pcr, bool,
703 private_pts_t *this, u_int32_t pcr_num, chunk_t *output)
704 {
705 TSS_HCONTEXT hContext;
706 TSS_HTPM hTPM;
707 TSS_RESULT result;
708 u_int32_t pcr_length;
709 chunk_t pcr_value;
710
711 result = Tspi_Context_Create(&hContext);
712 if (result != TSS_SUCCESS)
713 {
714 DBG1(DBG_PTS, "TPM context could not be created: tss error 0x%x",
715 result);
716 return FALSE;
717 }
718
719 result = Tspi_Context_Connect(hContext, NULL);
720 if (result != TSS_SUCCESS)
721 {
722 goto err;
723 }
724 result = Tspi_Context_GetTpmObject (hContext, &hTPM);
725 if (result != TSS_SUCCESS)
726 {
727 goto err;
728 }
729 pcr_value = chunk_alloc(PCR_LEN);
730 result = Tspi_TPM_PcrRead(hTPM, pcr_num, &pcr_length, &pcr_value.ptr);
731 if (result != TSS_SUCCESS)
732 {
733 goto err;
734 }
735
736 *output = pcr_value;
737 *output = chunk_clone(*output);
738
739 chunk_clear(&pcr_value);
740 Tspi_Context_Close(hContext);
741 DBG3(DBG_PTS, "PCR %d value:%B", pcr_num, output);
742 return TRUE;
743
744 err:
745 chunk_clear(&pcr_value);
746 DBG1(DBG_PTS, "TPM not available: tss error 0x%x", result);
747 Tspi_Context_Close(hContext);
748 return FALSE;
749 }
750
751 METHOD(pts_t, extend_pcr, bool,
752 private_pts_t *this, u_int32_t pcr_num, chunk_t input, chunk_t *output)
753 {
754 TSS_HCONTEXT hContext;
755 TSS_HTPM hTPM;
756 TSS_RESULT result;
757 u_int32_t pcr_length;
758 chunk_t pcr_value;
759
760 result = Tspi_Context_Create(&hContext);
761 if (result != TSS_SUCCESS)
762 {
763 DBG1(DBG_PTS, "TPM context could not be created: tss error 0x%x",
764 result);
765 return FALSE;
766 }
767 result = Tspi_Context_Connect(hContext, NULL);
768 if (result != TSS_SUCCESS)
769 {
770 goto err;
771 }
772 result = Tspi_Context_GetTpmObject (hContext, &hTPM);
773 if (result != TSS_SUCCESS)
774 {
775 goto err;
776 }
777
778 pcr_value = chunk_alloc(PCR_LEN);
779 result = Tspi_TPM_PcrExtend(hTPM, pcr_num, PCR_LEN, input.ptr,
780 NULL, &pcr_length, &pcr_value.ptr);
781 if (result != TSS_SUCCESS)
782 {
783 goto err;
784 }
785
786 *output = pcr_value;
787 *output = chunk_clone(*output);
788
789 chunk_clear(&pcr_value);
790 Tspi_Context_Close(hContext);
791 DBG3(DBG_PTS, "PCR %d extended with: %B", pcr_num, &input);
792 DBG3(DBG_PTS, "PCR %d value after extend: %B", pcr_num, output);
793 return TRUE;
794
795 err:
796 chunk_clear(&pcr_value);
797 DBG1(DBG_PTS, "TPM not available: tss error 0x%x", result);
798 Tspi_Context_Close(hContext);
799 return FALSE;
800 }
801
802 METHOD(pts_t, quote_tpm, bool,
803 private_pts_t *this, u_int32_t *pcrs, u_int32_t num_of_pcrs,
804 chunk_t *pcr_composite, chunk_t *quote_signature)
805 {
806 TSS_HCONTEXT hContext;
807 TSS_HTPM hTPM;
808 TSS_HKEY hAIK;
809 TSS_HKEY hSRK;
810 TSS_HPOLICY srkUsagePolicy;
811 TSS_UUID SRK_UUID = TSS_UUID_SRK;
812 BYTE secret[] = TSS_WELL_KNOWN_SECRET;
813 TSS_HPCRS hPcrComposite;
814 TSS_VALIDATION valData;
815 u_int32_t i;
816 TSS_RESULT result;
817 chunk_t pcr_comp, quote_sign;
818
819 result = Tspi_Context_Create(&hContext);
820 if (result != TSS_SUCCESS)
821 {
822 DBG1(DBG_PTS, "TPM context could not be created: tss error 0x%x",
823 result);
824 return FALSE;
825 }
826 result = Tspi_Context_Connect(hContext, NULL);
827 if (result != TSS_SUCCESS)
828 {
829 goto err1;
830 }
831 result = Tspi_Context_GetTpmObject (hContext, &hTPM);
832 if (result != TSS_SUCCESS)
833 {
834 goto err1;
835 }
836
837 /* Retrieve SRK from TPM and set the authentication to well known secret*/
838 result = Tspi_Context_LoadKeyByUUID(hContext, TSS_PS_TYPE_SYSTEM,
839 SRK_UUID, &hSRK);
840 if (result != TSS_SUCCESS)
841 {
842 goto err1;
843 }
844
845 result = Tspi_GetPolicyObject(hSRK, TSS_POLICY_USAGE, &srkUsagePolicy);
846 if (result != TSS_SUCCESS)
847 {
848 goto err1;
849 }
850 result = Tspi_Policy_SetSecret(srkUsagePolicy, TSS_SECRET_MODE_SHA1,
851 20, secret);
852 if (result != TSS_SUCCESS)
853 {
854 goto err1;
855 }
856
857 result = Tspi_Context_LoadKeyByBlob (hContext, hSRK, this->aik_blob.len,
858 this->aik_blob.ptr, &hAIK);
859 if (result != TSS_SUCCESS)
860 {
861 goto err1;
862 }
863
864 /* Create PCR composite object */
865 result = Tspi_Context_CreateObject(hContext,
866 TSS_OBJECT_TYPE_PCRS, 0, &hPcrComposite);
867 if (result != TSS_SUCCESS)
868 {
869 goto err2;
870 }
871
872 /* Select PCR's */
873 for (i = 0; i < num_of_pcrs ; i++)
874 {
875 if (pcrs[i] < 0 || pcrs[i] >= MAX_NUM_PCR )
876 {
877 DBG1(DBG_PTS, "Invalid PCR number: %d", pcrs[i]);
878 goto err3;
879 }
880 result = Tspi_PcrComposite_SelectPcrIndex(hPcrComposite, pcrs[i]);
881 if (result != TSS_SUCCESS)
882 {
883 goto err3;
884 }
885 }
886
887 /* Set the Validation Data */
888 valData.ulExternalDataLength = this->secret.len;
889 valData.rgbExternalData = (BYTE *)this->secret.ptr;
890
891
892 /* TPM Quote */
893 result = Tspi_TPM_Quote(hTPM, hAIK, hPcrComposite, &valData);
894 if (result != TSS_SUCCESS)
895 {
896 goto err4;
897 }
898
899 /* Set output chunks */
900 pcr_comp = chunk_alloc(HASH_SIZE_SHA1);
901 memcpy(pcr_comp.ptr, valData.rgbData + 8, HASH_SIZE_SHA1);
902 *pcr_composite = pcr_comp;
903 *pcr_composite = chunk_clone(*pcr_composite);
904 DBG3(DBG_PTS, "Hash of PCR Composite: %B",pcr_composite);
905
906 quote_sign = chunk_alloc(valData.ulValidationDataLength);
907 memcpy(quote_sign.ptr, valData.rgbValidationData,
908 valData.ulValidationDataLength);
909 *quote_signature = quote_sign;
910 *quote_signature = chunk_clone(*quote_signature);
911 DBG3(DBG_PTS, "TOM Quote Signature: %B",quote_signature);
912
913 chunk_clear(&quote_sign);
914 Tspi_Context_FreeMemory(hContext, NULL);
915 Tspi_Context_CloseObject(hContext, hPcrComposite);
916 Tspi_Context_CloseObject(hContext, hAIK);
917 Tspi_Context_Close(hContext);
918 free(pcrs);
919 return TRUE;
920
921 /* Cleanup */
922 err4:
923 Tspi_Context_FreeMemory(hContext, NULL);
924
925 err3:
926 Tspi_Context_CloseObject(hContext, hPcrComposite);
927
928 err2:
929 Tspi_Context_CloseObject(hContext, hAIK);
930
931 err1:
932 Tspi_Context_Close(hContext);
933 free(pcrs);
934 DBG1(DBG_PTS, "TPM not available: tss error 0x%x", result);
935 return FALSE;
936 }
937
938 /**
939 * Comparison function for pcr_entry_t struct
940 */
941 static int pcr_entry_compare(const pcr_entry_t *a, const pcr_entry_t *b)
942 {
943 return (a->pcr_number - b->pcr_number);
944 }
945
946 static int pcr_entry_compare_qsort(const void *a, const void *b)
947 {
948 return pcr_entry_compare(*(const pcr_entry_t *const *)a
949 , *(const pcr_entry_t *const *)b);
950 }
951
952 METHOD(pts_t, add_pcr_entry, void,
953 private_pts_t *this, pcr_entry_t *new)
954 {
955 enumerator_t *e;
956 pcr_entry_t *entry;
957
958 if (!this->pcrs)
959 {
960 this->pcrs = linked_list_create();
961 }
962
963 e = this->pcrs->create_enumerator(this->pcrs);
964 while (e->enumerate(e, &entry))
965 {
966 if (entry->pcr_number == new->pcr_number)
967 {
968 DBG4(DBG_PTS, "updating already added PCR%d value",
969 entry->pcr_number);
970 this->pcrs->remove_at(this->pcrs, e);
971 free(entry);
972 break;
973 }
974 }
975 DESTROY_IF(e);
976
977 this->pcrs->insert_last(this->pcrs, new);
978
979 qsort(this->pcrs, this->pcrs->get_count(this->pcrs),
980 sizeof(pcr_entry_t *), pcr_entry_compare_qsort);
981 }
982
983 /**
984 * 1. build a TCPA_PCR_COMPOSITE structure which contains (pcrCompositeBuf)
985 * TCPA_PCR_SELECTION structure (bitmask length + bitmask)
986 * UINT32 (network order) gives the number of bytes following (pcr entries * 20)
987 * TCPA_PCRVALUE[] with the pcr values
988 *
989 * The first two bytes of the message represent the length
990 * of the bitmask that follows. The bitmask represents the
991 * requested PCRs to be quoted.
992 *
993 * TPM Main-Part 2 TPM Structures_v1.2 8.1
994 * The bitmask is in big endian order"
995 *
996 * BYTE 1 BYTE 2 ...
997 * Bit: 1 1 1 1 0 0 0 0 1 1 1 1 0 0 0 0 ...
998 * Pcr: 7 6 5 4 3 2 1 0 15 14 13 12 11 10 9 8 ...
999 *
1000 * 2. SHA1(pcrCompositeBuf)
1001 *
1002 * 3. build a TCPA_QUOTE_INFO structure which contains
1003 * 4 bytes of version
1004 * 4 bytes 'Q' 'U' 'O' 'T'
1005 * 20 byte SHA1 of TCPA_PCR_COMPOSITE
1006 * 20 byte nonce
1007 */
1008
1009 METHOD(pts_t, get_quote_info, bool,
1010 private_pts_t *this, chunk_t *out_pcr_composite, chunk_t *out_quote_info)
1011 {
1012 enumerator_t *e;
1013 pcr_entry_t *pcr_entry;
1014 chunk_t pcr_composite;
1015 u_int32_t pcr_composite_len;
1016 bio_writer_t *writer;
1017 u_int8_t mask_bytes[PCR_MASK_LEN] = {0,0,0}, i;
1018 hasher_t *hasher;
1019
1020 if (this->pcrs->get_count(this->pcrs) == 0)
1021 {
1022 DBG1(DBG_PTS, "PCR entries unavailable, unable to construct "
1023 "TPM Quote Info");
1024 return FALSE;
1025 }
1026
1027 pcr_composite_len = 2 + PCR_MASK_LEN + 4 +
1028 this->pcrs->get_count(this->pcrs) * PCR_LEN;
1029
1030 writer = bio_writer_create(pcr_composite_len);
1031 /* Lenght of the bist mask field */
1032 writer->write_uint16(writer, PCR_MASK_LEN);
1033 /* Bit mask indicating selected PCRs */
1034 e = this->pcrs->create_enumerator(this->pcrs);
1035 while (e->enumerate(e, &pcr_entry))
1036 {
1037 u_int32_t index = pcr_entry->pcr_number;
1038 mask_bytes[index / 8] |= (1 << (index % 8));
1039 }
1040 e->destroy(e);
1041
1042 for (i = 0; i< PCR_MASK_LEN ; i++)
1043 {
1044 writer->write_uint8(writer, mask_bytes[i]);
1045 }
1046
1047 /* Lenght of the pcr entries */
1048 writer->write_uint32(writer, this->pcrs->get_count(this->pcrs) * PCR_LEN);
1049 /* Actual PCR values */
1050 e = this->pcrs->create_enumerator(this->pcrs);
1051 while (e->enumerate(e, &pcr_entry))
1052 {
1053 writer->write_data(writer, chunk_create(pcr_entry->pcr_value, PCR_LEN));
1054 }
1055 free(pcr_entry);
1056 e->destroy(e);
1057
1058 /* PCR Composite structure */
1059 pcr_composite = chunk_clone(writer->get_buf(writer));
1060 writer->destroy(writer);
1061
1062 writer = bio_writer_create(TPM_QUOTE_INFO_LEN);
1063 /* Version number */
1064 writer->write_uint8(writer, 1);
1065 writer->write_uint8(writer, 1);
1066 writer->write_uint8(writer, 0);
1067 writer->write_uint8(writer, 0);
1068
1069 /* Magic QUOT value, depends on TPM Ordinal */
1070 writer->write_uint8(writer, 'Q');
1071 writer->write_uint8(writer, 'U');
1072 writer->write_uint8(writer, 'O');
1073 writer->write_uint8(writer, 'T');
1074
1075 /* SHA1 hash of PCR Composite Structure */
1076 hasher = lib->crypto->create_hasher(lib->crypto, HASH_SHA1);
1077 hasher->allocate_hash(hasher, pcr_composite, out_pcr_composite);
1078 DBG4(DBG_PTS, "Hash of calculated PCR Composite: %B", out_pcr_composite);
1079
1080 chunk_clear(&pcr_composite);
1081 hasher->destroy(hasher);
1082 writer->write_data(writer, *out_pcr_composite);
1083
1084 if (!this->secret.ptr)
1085 {
1086 DBG1(DBG_PTS, "Secret assessment value unavailable",
1087 "unable to construct TPM Quote Info");
1088 chunk_clear(out_pcr_composite);
1089 writer->destroy(writer);
1090 return FALSE;
1091 }
1092 /* Secret assessment value 20 bytes (nonce) */
1093 writer->write_data(writer, this->secret);
1094 /* TPM Quote Info */
1095 *out_quote_info = chunk_clone(writer->get_buf(writer));
1096 DBG4(DBG_PTS, "Calculated TPM Quote Info: %B", out_quote_info);
1097 writer->destroy(writer);
1098
1099 return TRUE;
1100 }
1101
1102 METHOD(pts_t, verify_quote_signature, bool,
1103 private_pts_t *this, chunk_t data, chunk_t signature)
1104 {
1105 public_key_t *aik_pub_key;
1106 chunk_t key_encoding;
1107 EVP_PKEY *pkey = NULL;
1108 RSA *rsa = NULL;
1109 unsigned char *p;
1110
1111 aik_pub_key = this->aik->get_public_key(this->aik);
1112 if (!aik_pub_key)
1113 {
1114 DBG1(DBG_PTS, "failed to get public key from AIK certificate");
1115 return FALSE;
1116 }
1117
1118 /** Implementation using strongswan -> not working */
1119 /**if (!aik_pub_key->verify(aik_pub_key, SIGN_RSA_EMSA_PKCS1_SHA1,
1120 data, signature))
1121 {
1122 DBG1(DBG_PTS, "signature verification failed for TPM Quote Info");
1123 goto cleanup;
1124 }
1125 */
1126
1127 if (!aik_pub_key->get_encoding(aik_pub_key,
1128 PUBKEY_SPKI_ASN1_DER, &key_encoding))
1129 {
1130 DBG1(DBG_PTS, "failed to get encoding of AIK public key");
1131 goto cleanup;
1132 }
1133
1134 p = key_encoding.ptr;
1135 pkey = d2i_PUBKEY(NULL, (const unsigned char**)&p, key_encoding.len);
1136 if (!pkey)
1137 {
1138 DBG1(DBG_PTS, "failed to get EVP_PKEY object from AIK public key");
1139 goto cleanup;
1140 }
1141
1142 rsa = EVP_PKEY_get1_RSA(pkey);
1143 if (!rsa)
1144 {
1145 DBG1(DBG_PTS, "failed to get RSA object from EVP_PKEY");
1146 goto cleanup;
1147 }
1148
1149 if (RSA_verify(NID_sha1, data.ptr, data.len,
1150 signature.ptr, signature.len, rsa) != 1)
1151 {
1152 DBG1(DBG_PTS, "signature verification failed for TPM Quote Info");
1153 goto cleanup;
1154 }
1155
1156 RSA_free(rsa);
1157 EVP_PKEY_free(pkey);
1158 if (key_encoding.ptr)
1159 {
1160 chunk_clear(&key_encoding);
1161 }
1162 aik_pub_key->destroy(aik_pub_key);
1163 return TRUE;
1164
1165 cleanup:
1166 if (rsa)
1167 {
1168 RSA_free(rsa);
1169 }
1170 if (pkey)
1171 {
1172 EVP_PKEY_free(pkey);
1173 }
1174 if (key_encoding.ptr)
1175 {
1176 chunk_clear(&key_encoding);
1177 }
1178 DESTROY_IF(aik_pub_key);
1179 return FALSE;
1180 }
1181
1182 METHOD(pts_t, destroy, void,
1183 private_pts_t *this)
1184 {
1185 DESTROY_IF(this->aik);
1186 DESTROY_IF(this->dh);
1187 DESTROY_IF(this->pcrs);
1188 free(this->initiator_nonce.ptr);
1189 free(this->responder_nonce.ptr);
1190 free(this->secret.ptr);
1191 free(this->platform_info);
1192 free(this->aik_blob.ptr);
1193 free(this->tpm_version_info.ptr);
1194 free(this);
1195 }
1196
1197 /**
1198 * Determine Linux distribution and hardware platform
1199 */
1200 static char* extract_platform_info(void)
1201 {
1202 FILE *file;
1203 char buf[BUF_LEN], *pos, *value = NULL;
1204 int i, len;
1205 struct utsname uninfo;
1206
1207 /* Linux/Unix distribution release info (from http://linuxmafia.com) */
1208 const char* releases[] = {
1209 "/etc/lsb-release", "/etc/debian_version",
1210 "/etc/SuSE-release", "/etc/novell-release",
1211 "/etc/sles-release", "/etc/redhat-release",
1212 "/etc/fedora-release", "/etc/gentoo-release",
1213 "/etc/slackware-version", "/etc/annvix-release",
1214 "/etc/arch-release", "/etc/arklinux-release",
1215 "/etc/aurox-release", "/etc/blackcat-release",
1216 "/etc/cobalt-release", "/etc/conectiva-release",
1217 "/etc/debian_release", "/etc/immunix-release",
1218 "/etc/lfs-release", "/etc/linuxppc-release",
1219 "/etc/mandrake-release", "/etc/mandriva-release",
1220 "/etc/mandrakelinux-release", "/etc/mklinux-release",
1221 "/etc/pld-release", "/etc/redhat_version",
1222 "/etc/slackware-release", "/etc/e-smith-release",
1223 "/etc/release", "/etc/sun-release",
1224 "/etc/tinysofa-release", "/etc/turbolinux-release",
1225 "/etc/ultrapenguin-release", "/etc/UnitedLinux-release",
1226 "/etc/va-release", "/etc/yellowdog-release"
1227 };
1228
1229 const char description[] = "DISTRIB_DESCRIPTION=\"";
1230
1231 for (i = 0; i < countof(releases); i++)
1232 {
1233 file = fopen(releases[i], "r");
1234 if (!file)
1235 {
1236 continue;
1237 }
1238 fseek(file, 0, SEEK_END);
1239 len = min(ftell(file), sizeof(buf)-1);
1240 rewind(file);
1241 buf[len] = '\0';
1242 if (fread(buf, 1, len, file) != len)
1243 {
1244 DBG1(DBG_PTS, "failed to read file '%s'", releases[i]);
1245 fclose(file);
1246 return NULL;
1247 }
1248 fclose(file);
1249
1250 if (i == 0) /* LSB release */
1251 {
1252 pos = strstr(buf, description);
1253 if (!pos)
1254 {
1255 DBG1(DBG_PTS, "failed to find begin of lsb-release "
1256 "DESCRIPTION field");
1257 return NULL;
1258 }
1259 value = pos + strlen(description);
1260 pos = strchr(value, '"');
1261 if (!pos)
1262 {
1263 DBG1(DBG_PTS, "failed to find end of lsb-release "
1264 "DESCRIPTION field");
1265 return NULL;
1266 }
1267 }
1268 else
1269 {
1270 value = buf;
1271 pos = strchr(value, '\n');
1272 if (!pos)
1273 {
1274 DBG1(DBG_PTS, "failed to find end of release string");
1275 return NULL;
1276 }
1277 }
1278 break;
1279 }
1280
1281 if (!value)
1282 {
1283 DBG1(DBG_PTS, "no distribution release file found");
1284 return NULL;
1285 }
1286
1287 if (uname(&uninfo) < 0)
1288 {
1289 DBG1(DBG_PTS, "could not retrieve machine architecture");
1290 return NULL;
1291 }
1292
1293 *pos++ = ' ';
1294 len = sizeof(buf)-1 + (pos - buf);
1295 strncpy(pos, uninfo.machine, len);
1296
1297 DBG1(DBG_PTS, "platform is '%s'", value);
1298 return strdup(value);
1299 }
1300
1301 /**
1302 * Check for a TPM by querying for TPM Version Info
1303 */
1304 static bool has_tpm(private_pts_t *this)
1305 {
1306 TSS_HCONTEXT hContext;
1307 TSS_HTPM hTPM;
1308 TSS_RESULT result;
1309 u_int32_t version_info_len;
1310
1311 result = Tspi_Context_Create(&hContext);
1312 if (result != TSS_SUCCESS)
1313 {
1314 DBG1(DBG_PTS, "TPM context could not be created: tss error 0x%x",
1315 result);
1316 return FALSE;
1317 }
1318 result = Tspi_Context_Connect(hContext, NULL);
1319 if (result != TSS_SUCCESS)
1320 {
1321 goto err;
1322 }
1323 result = Tspi_Context_GetTpmObject (hContext, &hTPM);
1324 if (result != TSS_SUCCESS)
1325 {
1326 goto err;
1327 }
1328 result = Tspi_TPM_GetCapability(hTPM, TSS_TPMCAP_VERSION_VAL, 0, NULL,
1329 &version_info_len,
1330 &this->tpm_version_info.ptr);
1331 this->tpm_version_info.len = version_info_len;
1332 if (result != TSS_SUCCESS)
1333 {
1334 goto err;
1335 }
1336 this->tpm_version_info = chunk_clone(this->tpm_version_info);
1337 Tspi_Context_Close(hContext);
1338 return TRUE;
1339
1340 err:
1341 DBG1(DBG_PTS, "TPM not available: tss error 0x%x", result);
1342 Tspi_Context_Close(hContext);
1343 return FALSE;
1344 }
1345
1346 /**
1347 * See header
1348 */
1349 pts_t *pts_create(bool is_imc)
1350 {
1351 private_pts_t *this;
1352
1353 INIT(this,
1354 .public = {
1355 .get_proto_caps = _get_proto_caps,
1356 .set_proto_caps = _set_proto_caps,
1357 .get_meas_algorithm = _get_meas_algorithm,
1358 .set_meas_algorithm = _set_meas_algorithm,
1359 .get_dh_hash_algorithm = _get_dh_hash_algorithm,
1360 .set_dh_hash_algorithm = _set_dh_hash_algorithm,
1361 .create_dh_nonce = _create_dh_nonce,
1362 .get_my_public_value = _get_my_public_value,
1363 .set_peer_public_value = _set_peer_public_value,
1364 .calculate_secret = _calculate_secret,
1365 .get_platform_info = _get_platform_info,
1366 .set_platform_info = _set_platform_info,
1367 .get_tpm_version_info = _get_tpm_version_info,
1368 .set_tpm_version_info = _set_tpm_version_info,
1369 .get_aik = _get_aik,
1370 .set_aik = _set_aik,
1371 .is_path_valid = _is_path_valid,
1372 .hash_file = _hash_file,
1373 .do_measurements = _do_measurements,
1374 .get_metadata = _get_metadata,
1375 .read_pcr = _read_pcr,
1376 .extend_pcr = _extend_pcr,
1377 .quote_tpm = _quote_tpm,
1378 .add_pcr_entry = _add_pcr_entry,
1379 .get_quote_info = _get_quote_info,
1380 .verify_quote_signature = _verify_quote_signature,
1381 .destroy = _destroy,
1382 },
1383 .is_imc = is_imc,
1384 .proto_caps = PTS_PROTO_CAPS_V,
1385 .algorithm = PTS_MEAS_ALGO_SHA256,
1386 .dh_hash_algorithm = PTS_MEAS_ALGO_SHA256,
1387 );
1388
1389 if (is_imc)
1390 {
1391 this->platform_info = extract_platform_info();
1392
1393 if (has_tpm(this))
1394 {
1395 this->has_tpm = TRUE;
1396 this->proto_caps |= PTS_PROTO_CAPS_T;
1397 load_aik(this);
1398 load_aik_blob(this);
1399 }
1400 }
1401 else
1402 {
1403 this->proto_caps |= PTS_PROTO_CAPS_T | PTS_PROTO_CAPS_C;
1404 }
1405
1406 return &this->public;
1407 }