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