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