2 * Copyright (C) 2011 Sansar Choinyambuu
3 * HSR Hochschule fuer Technik Rapperswil
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>.
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
19 #include <crypto/hashers/hasher.h>
21 #include <trousers/tss.h>
22 #include <trousers/trousers.h>
27 #define PTS_BUF_SIZE 4096
29 typedef struct private_pts_t private_pts_t
;
32 * Private data of a pts_t object.
35 struct private_pts_t
{
38 * Public pts_t interface.
43 * PTS Protocol Capabilities
45 pts_proto_caps_flag_t proto_caps
;
48 * PTS Measurement Algorithm
50 pts_meas_algorithms_t algorithm
;
53 * Platform and OS Info
58 * Do we have an activated TPM
63 * Contains a TPM_CAP_VERSION_INFO struct
65 chunk_t tpm_version_info
;
68 * Contains a Attestation Identity Key or Certificate
74 METHOD(pts_t
, get_proto_caps
, pts_proto_caps_flag_t
,
77 return this->proto_caps
;
80 METHOD(pts_t
, set_proto_caps
, void,
81 private_pts_t
*this, pts_proto_caps_flag_t flags
)
83 this->proto_caps
= flags
;
84 DBG2(DBG_IMC
, "supported PTS protocol capabilities: %s%s%s%s%s",
85 flags
& PTS_PROTO_CAPS_C ?
"C" : ".",
86 flags
& PTS_PROTO_CAPS_V ?
"V" : ".",
87 flags
& PTS_PROTO_CAPS_D ?
"D" : ".",
88 flags
& PTS_PROTO_CAPS_T ?
"T" : ".",
89 flags
& PTS_PROTO_CAPS_X ?
"X" : ".");
92 METHOD(pts_t
, get_meas_algorithm
, pts_meas_algorithms_t
,
95 return this->algorithm
;
98 METHOD(pts_t
, set_meas_algorithm
, void,
99 private_pts_t
*this, pts_meas_algorithms_t algorithm
)
101 hash_algorithm_t hash_alg
;
103 hash_alg
= pts_meas_to_hash_algorithm(algorithm
);
104 DBG2(DBG_IMC
, "selected PTS measurement algorithm is %N",
105 hash_algorithm_names
, hash_alg
);
106 if (hash_alg
!= HASH_UNKNOWN
)
108 this->algorithm
= algorithm
;
113 * Print TPM 1.2 Version Info
115 static void print_tpm_version_info(private_pts_t
*this)
117 TPM_CAP_VERSION_INFO versionInfo
;
121 result
= Trspi_UnloadBlob_CAP_VERSION_INFO(&offset
,
122 this->tpm_version_info
.ptr
, &versionInfo
);
123 if (result
!= TSS_SUCCESS
)
125 DBG1(DBG_TNC
, "could not parse tpm version info: tss error 0x%x",
130 DBG2(DBG_TNC
, "TPM 1.2 Version Info: Chip Version: %hhu.%hhu.%hhu.%hhu,"
131 " Spec Level: %hu, Errata Rev: %hhu, Vendor ID: %.4s",
132 versionInfo
.version
.major
, versionInfo
.version
.minor
,
133 versionInfo
.version
.revMajor
, versionInfo
.version
.revMinor
,
134 versionInfo
.specLevel
, versionInfo
.errataRev
,
135 versionInfo
.tpmVendorID
);
139 METHOD(pts_t
, get_platform_info
, char*,
142 return this->platform_info
;
145 METHOD(pts_t
, set_platform_info
, void,
146 private_pts_t
*this, char *info
)
148 free(this->platform_info
);
149 this->platform_info
= strdup(info
);
152 METHOD(pts_t
, get_tpm_version_info
, bool,
153 private_pts_t
*this, chunk_t
*info
)
159 *info
= this->tpm_version_info
;
160 print_tpm_version_info(this);
164 METHOD(pts_t
, set_tpm_version_info
, void,
165 private_pts_t
*this, chunk_t info
)
167 this->tpm_version_info
= chunk_clone(info
);
168 print_tpm_version_info(this);
172 * Load an AIK certificate or public key,
173 * the certificate having precedence over the public key if both are present
175 static void load_aik(private_pts_t
*this)
177 char *cert_path
, *key_path
;
179 cert_path
= lib
->settings
->get_str(lib
->settings
,
180 "libimcv.plugins.imc-attestation.aik_cert", NULL
);
181 key_path
= lib
->settings
->get_str(lib
->settings
,
182 "libimcv.plugins.imc-attestation.aik_key", NULL
);
186 this->aik
= lib
->creds
->create(lib
->creds
, CRED_CERTIFICATE
,
187 CERT_X509
, BUILD_FROM_FILE
,
188 cert_path
, BUILD_END
);
191 DBG2(DBG_IMC
, "loaded AIK certificate from '%s'", cert_path
);
197 this->aik
= lib
->creds
->create(lib
->creds
, CRED_CERTIFICATE
,
198 CERT_TRUSTED_PUBKEY
, BUILD_FROM_FILE
,
199 key_path
, BUILD_END
);
202 DBG2(DBG_IMC
, "loaded AIK public key from '%s'", key_path
);
206 DBG1(DBG_IMC
, "neither AIK certificate nor public key is available");
209 METHOD(pts_t
, get_aik
, certificate_t
*,
215 METHOD(pts_t
, set_aik
, void,
216 private_pts_t
*this, certificate_t
*aik
)
218 DESTROY_IF(this->aik
);
219 this->aik
= aik
->get_ref(aik
);
223 * Compute a hash over a file
225 static bool hash_file(hasher_t
*hasher
, char *pathname
, u_char
*hash
)
227 u_char buffer
[PTS_BUF_SIZE
];
231 file
= fopen(pathname
, "rb");
234 DBG1(DBG_IMC
," file '%s' can not be opened, %s", pathname
,
240 bytes_read
= fread(buffer
, 1, sizeof(buffer
), file
);
243 hasher
->get_hash(hasher
, chunk_create(buffer
, bytes_read
), NULL
);
247 hasher
->get_hash(hasher
, chunk_empty
, hash
);
257 * Get the relative filename of a fully qualified file pathname
259 static char* get_filename(char *pathname
)
261 char *pos
, *filename
;
263 pos
= filename
= pathname
;
264 while (pos
&& *(++pos
) != '\0')
267 pos
= strchr(filename
, '/');
272 METHOD(pts_t
, do_measurements
, pts_file_meas_t
*,
273 private_pts_t
*this, u_int16_t request_id
, char *pathname
, bool is_directory
)
276 hash_algorithm_t hash_alg
;
277 u_char hash
[HASH_SIZE_SHA384
];
279 pts_file_meas_t
*measurements
;
281 /* Create a hasher */
282 hash_alg
= pts_meas_to_hash_algorithm(this->algorithm
);
283 hasher
= lib
->crypto
->create_hasher(lib
->crypto
, hash_alg
);
286 DBG1(DBG_IMC
, " hasher %N not available", hash_algorithm_names
, hash_alg
);
290 /* Create a measurement object */
291 measurements
= pts_file_meas_create(request_id
);
293 /* Link the hash to the measurement and set the measurement length */
294 measurement
= chunk_create(hash
, hasher
->get_hash_size(hasher
));
298 enumerator_t
*enumerator
;
299 char *rel_name
, *abs_name
;
302 enumerator
= enumerator_create_directory(pathname
);
305 DBG1(DBG_IMC
," directory '%s' can not be opened, %s", pathname
,
307 hasher
->destroy(hasher
);
308 measurements
->destroy(measurements
);
311 while (enumerator
->enumerate(enumerator
, &rel_name
, &abs_name
, &st
))
313 /* measure regular files only */
314 if (S_ISREG(st
.st_mode
) && *rel_name
!= '.')
316 if (!hash_file(hasher
, abs_name
, hash
))
318 enumerator
->destroy(enumerator
);
319 hasher
->destroy(hasher
);
320 measurements
->destroy(measurements
);
323 DBG2(DBG_IMC
, " %#B for '%s'", &measurement
, rel_name
);
324 measurements
->add(measurements
, rel_name
, measurement
);
327 enumerator
->destroy(enumerator
);
333 if (!hash_file(hasher
, pathname
, hash
))
335 hasher
->destroy(hasher
);
336 measurements
->destroy(measurements
);
339 filename
= get_filename(pathname
);
340 DBG2(DBG_IMC
, " %#B for '%s'", &measurement
, filename
);
341 measurements
->add(measurements
, filename
, measurement
);
343 hasher
->destroy(hasher
);
348 METHOD(pts_t
, destroy
, void,
351 DESTROY_IF(this->aik
);
352 free(this->platform_info
);
353 free(this->tpm_version_info
.ptr
);
358 * Determine Linux distribution and hardware platform
360 static char* extract_platform_info(void)
363 const char description
[] = "Description:";
364 char buf
[BUF_LEN
], *pos
, *value
;
367 /* open a pipe stream for reading the output of the lsb_release commmand */
368 file
= popen("/usr/bin/lsb_release -d" , "r");
371 DBG2(DBG_IMC
, "failed to run lsb_release command");
375 /* read the output the lsb_release command */
376 if (!fgets(buf
, BUF_LEN
-1, file
))
378 DBG2(DBG_IMC
, "failed to read output of lsb_release command");
384 pos
= strstr(buf
, description
);
387 DBG2(DBG_IMC
, "failed to find lsb_release description field");
390 value
= pos
+ strlen(description
);
393 while (*value
== ' ' || *value
== '\t')
398 /* remove newline at the end and move value to the front of the buffer */
399 value_len
= strlen(value
) - 1;
400 memcpy(buf
, value
, value_len
);
401 buf
[value_len
] = ' ';
403 /* open a pipe stream for reading the output of the arch commmand */
404 file
= popen("/usr/bin/arch" , "r");
407 DBG2(DBG_IMC
, "failed to run arch command");
411 /* read the output the arch command */
412 if (!fgets(buf
+ value_len
+ 1, BUF_LEN
- value_len
- 2, file
))
414 DBG2(DBG_IMC
, "failed to read output of arch command");
420 /* remove newline at the end */
421 buf
[strlen(buf
)-1] = '\0';
423 DBG1(DBG_IMV
, "platform is '%s'", buf
);
428 * Check for a TPM by querying for TPM Version Info
430 static bool has_tpm(private_pts_t
*this)
432 TSS_HCONTEXT hContext
;
436 result
= Tspi_Context_Create(&hContext
);
437 if (result
!= TSS_SUCCESS
)
441 result
= Tspi_Context_Connect(hContext
, NULL
);
442 if (result
!= TSS_SUCCESS
)
446 result
= Tspi_Context_GetTpmObject (hContext
, &hTPM
);
447 if (result
!= TSS_SUCCESS
)
451 result
= Tspi_TPM_GetCapability(hTPM
, TSS_TPMCAP_VERSION_VAL
, 0, NULL
,
452 &this->tpm_version_info
.len
,
453 &this->tpm_version_info
.ptr
);
454 if (result
!= TSS_SUCCESS
)
458 this->tpm_version_info
= chunk_clone(this->tpm_version_info
);
462 DBG1(DBG_IMC
, "TPM not available: tss error 0x%x", result
);
469 pts_t
*pts_create(bool is_imc
)
475 .get_proto_caps
= _get_proto_caps
,
476 .set_proto_caps
= _set_proto_caps
,
477 .get_meas_algorithm
= _get_meas_algorithm
,
478 .set_meas_algorithm
= _set_meas_algorithm
,
479 .get_platform_info
= _get_platform_info
,
480 .set_platform_info
= _set_platform_info
,
481 .get_tpm_version_info
= _get_tpm_version_info
,
482 .set_tpm_version_info
= _set_tpm_version_info
,
485 .do_measurements
= _do_measurements
,
488 .proto_caps
= PTS_PROTO_CAPS_V
,
489 .algorithm
= PTS_MEAS_ALGO_SHA256
,
494 this->platform_info
= extract_platform_info();
498 this->has_tpm
= TRUE
;
499 this->proto_caps
|= PTS_PROTO_CAPS_T
;
505 this->proto_caps
|= PTS_PROTO_CAPS_T
| PTS_PROTO_CAPS_C
;
508 return &this->public;