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>
25 #include <sys/utsname.h>
28 #define PTS_BUF_SIZE 4096
30 typedef struct private_pts_t private_pts_t
;
33 * Private data of a pts_t object.
36 struct private_pts_t
{
39 * Public pts_t interface.
44 * PTS Protocol Capabilities
46 pts_proto_caps_flag_t proto_caps
;
49 * PTS Measurement Algorithm
51 pts_meas_algorithms_t algorithm
;
54 * Platform and OS Info
59 * Do we have an activated TPM
64 * Contains a TPM_CAP_VERSION_INFO struct
66 chunk_t tpm_version_info
;
69 * Contains a Attestation Identity Key or Certificate
75 METHOD(pts_t
, get_proto_caps
, pts_proto_caps_flag_t
,
78 return this->proto_caps
;
81 METHOD(pts_t
, set_proto_caps
, void,
82 private_pts_t
*this, pts_proto_caps_flag_t flags
)
84 this->proto_caps
= flags
;
85 DBG2(DBG_PTS
, "supported PTS protocol capabilities: %s%s%s%s%s",
86 flags
& PTS_PROTO_CAPS_C ?
"C" : ".",
87 flags
& PTS_PROTO_CAPS_V ?
"V" : ".",
88 flags
& PTS_PROTO_CAPS_D ?
"D" : ".",
89 flags
& PTS_PROTO_CAPS_T ?
"T" : ".",
90 flags
& PTS_PROTO_CAPS_X ?
"X" : ".");
93 METHOD(pts_t
, get_meas_algorithm
, pts_meas_algorithms_t
,
96 return this->algorithm
;
99 METHOD(pts_t
, set_meas_algorithm
, void,
100 private_pts_t
*this, pts_meas_algorithms_t algorithm
)
102 hash_algorithm_t hash_alg
;
104 hash_alg
= pts_meas_to_hash_algorithm(algorithm
);
105 DBG2(DBG_PTS
, "selected PTS measurement algorithm is %N",
106 hash_algorithm_names
, hash_alg
);
107 if (hash_alg
!= HASH_UNKNOWN
)
109 this->algorithm
= algorithm
;
114 * Print TPM 1.2 Version Info
116 static void print_tpm_version_info(private_pts_t
*this)
118 TPM_CAP_VERSION_INFO versionInfo
;
122 result
= Trspi_UnloadBlob_CAP_VERSION_INFO(&offset
,
123 this->tpm_version_info
.ptr
, &versionInfo
);
124 if (result
!= TSS_SUCCESS
)
126 DBG1(DBG_PTS
, "could not parse tpm version info: tss error 0x%x",
131 DBG2(DBG_PTS
, "TPM 1.2 Version Info: Chip Version: %hhu.%hhu.%hhu.%hhu,"
132 " Spec Level: %hu, Errata Rev: %hhu, Vendor ID: %.4s",
133 versionInfo
.version
.major
, versionInfo
.version
.minor
,
134 versionInfo
.version
.revMajor
, versionInfo
.version
.revMinor
,
135 versionInfo
.specLevel
, versionInfo
.errataRev
,
136 versionInfo
.tpmVendorID
);
140 METHOD(pts_t
, get_platform_info
, char*,
143 return this->platform_info
;
146 METHOD(pts_t
, set_platform_info
, void,
147 private_pts_t
*this, char *info
)
149 free(this->platform_info
);
150 this->platform_info
= strdup(info
);
153 METHOD(pts_t
, get_tpm_version_info
, bool,
154 private_pts_t
*this, chunk_t
*info
)
160 *info
= this->tpm_version_info
;
161 print_tpm_version_info(this);
165 METHOD(pts_t
, set_tpm_version_info
, void,
166 private_pts_t
*this, chunk_t info
)
168 this->tpm_version_info
= chunk_clone(info
);
169 print_tpm_version_info(this);
173 * Load an AIK certificate or public key,
174 * the certificate having precedence over the public key if both are present
176 static void load_aik(private_pts_t
*this)
178 char *cert_path
, *key_path
;
180 cert_path
= lib
->settings
->get_str(lib
->settings
,
181 "libimcv.plugins.imc-attestation.aik_cert", NULL
);
182 key_path
= lib
->settings
->get_str(lib
->settings
,
183 "libimcv.plugins.imc-attestation.aik_key", NULL
);
187 this->aik
= lib
->creds
->create(lib
->creds
, CRED_CERTIFICATE
,
188 CERT_X509
, BUILD_FROM_FILE
,
189 cert_path
, BUILD_END
);
192 DBG2(DBG_PTS
, "loaded AIK certificate from '%s'", cert_path
);
198 this->aik
= lib
->creds
->create(lib
->creds
, CRED_CERTIFICATE
,
199 CERT_TRUSTED_PUBKEY
, BUILD_FROM_FILE
,
200 key_path
, BUILD_END
);
203 DBG2(DBG_PTS
, "loaded AIK public key from '%s'", key_path
);
207 DBG1(DBG_PTS
, "neither AIK certificate nor public key is available");
210 METHOD(pts_t
, get_aik
, certificate_t
*,
216 METHOD(pts_t
, set_aik
, void,
217 private_pts_t
*this, certificate_t
*aik
)
219 DESTROY_IF(this->aik
);
220 this->aik
= aik
->get_ref(aik
);
224 * Compute a hash over a file
226 static bool hash_file(hasher_t
*hasher
, char *pathname
, u_char
*hash
)
228 u_char buffer
[PTS_BUF_SIZE
];
232 file
= fopen(pathname
, "rb");
235 DBG1(DBG_PTS
," file '%s' can not be opened, %s", pathname
,
241 bytes_read
= fread(buffer
, 1, sizeof(buffer
), file
);
244 hasher
->get_hash(hasher
, chunk_create(buffer
, bytes_read
), NULL
);
248 hasher
->get_hash(hasher
, chunk_empty
, hash
);
258 * Get the relative filename of a fully qualified file pathname
260 static char* get_filename(char *pathname
)
262 char *pos
, *filename
;
264 pos
= filename
= pathname
;
265 while (pos
&& *(++pos
) != '\0')
268 pos
= strchr(filename
, '/');
273 METHOD(pts_t
, is_path_valid
, bool, private_pts_t
*this, char *path
,
274 pts_error_code_t
*error_code
)
280 error
= stat(path
, &sb
);
285 else if (error
== ENOENT
|| error
== ENOTDIR
)
287 DBG1(DBG_PTS
, "file/directory does not exist %s", path
);
288 *error_code
= TCG_PTS_FILE_NOT_FOUND
;
290 else if (error
== EFAULT
)
292 DBG1(DBG_PTS
, "bad address %s", path
);
293 *error_code
= TCG_PTS_INVALID_PATH
;
297 DBG1(DBG_PTS
, "error: %s occured while validating path: %s",
298 strerror(error
), path
);
305 METHOD(pts_t
, do_measurements
, pts_file_meas_t
*,
306 private_pts_t
*this, u_int16_t request_id
, char *pathname
, bool is_directory
)
309 hash_algorithm_t hash_alg
;
310 u_char hash
[HASH_SIZE_SHA384
];
312 pts_file_meas_t
*measurements
;
314 /* Create a hasher */
315 hash_alg
= pts_meas_to_hash_algorithm(this->algorithm
);
316 hasher
= lib
->crypto
->create_hasher(lib
->crypto
, hash_alg
);
319 DBG1(DBG_PTS
, " hasher %N not available", hash_algorithm_names
, hash_alg
);
323 /* Create a measurement object */
324 measurements
= pts_file_meas_create(request_id
);
326 /* Link the hash to the measurement and set the measurement length */
327 measurement
= chunk_create(hash
, hasher
->get_hash_size(hasher
));
331 enumerator_t
*enumerator
;
332 char *rel_name
, *abs_name
;
335 enumerator
= enumerator_create_directory(pathname
);
338 DBG1(DBG_PTS
," directory '%s' can not be opened, %s", pathname
,
340 hasher
->destroy(hasher
);
341 measurements
->destroy(measurements
);
344 while (enumerator
->enumerate(enumerator
, &rel_name
, &abs_name
, &st
))
346 /* measure regular files only */
347 if (S_ISREG(st
.st_mode
) && *rel_name
!= '.')
349 if (!hash_file(hasher
, abs_name
, hash
))
351 enumerator
->destroy(enumerator
);
352 hasher
->destroy(hasher
);
353 measurements
->destroy(measurements
);
356 DBG2(DBG_PTS
, " %#B for '%s'", &measurement
, rel_name
);
357 measurements
->add(measurements
, rel_name
, measurement
);
360 enumerator
->destroy(enumerator
);
366 if (!hash_file(hasher
, pathname
, hash
))
368 hasher
->destroy(hasher
);
369 measurements
->destroy(measurements
);
372 filename
= get_filename(pathname
);
373 DBG2(DBG_PTS
, " %#B for '%s'", &measurement
, filename
);
374 measurements
->add(measurements
, filename
, measurement
);
376 hasher
->destroy(hasher
);
381 METHOD(pts_t
, destroy
, void,
384 DESTROY_IF(this->aik
);
385 free(this->platform_info
);
386 free(this->tpm_version_info
.ptr
);
391 * Determine Linux distribution and hardware platform
393 static char* extract_platform_info(void)
396 char buf
[BUF_LEN
], *pos
, *value
= NULL
;
398 struct utsname uninfo
;
400 /* Linux/Unix distribution release info (from http://linuxmafia.com) */
401 const char* releases
[] = {
402 "/etc/lsb-release", "/etc/debian_version",
403 "/etc/SuSE-release", "/etc/novell-release",
404 "/etc/sles-release", "/etc/redhat-release",
405 "/etc/fedora-release", "/etc/gentoo-release",
406 "/etc/slackware-version", "/etc/annvix-release",
407 "/etc/arch-release", "/etc/arklinux-release",
408 "/etc/aurox-release", "/etc/blackcat-release",
409 "/etc/cobalt-release", "/etc/conectiva-release",
410 "/etc/debian_release", "/etc/immunix-release",
411 "/etc/lfs-release", "/etc/linuxppc-release",
412 "/etc/mandrake-release", "/etc/mandriva-release",
413 "/etc/mandrakelinux-release", "/etc/mklinux-release",
414 "/etc/pld-release", "/etc/redhat_version",
415 "/etc/slackware-release", "/etc/e-smith-release",
416 "/etc/release", "/etc/sun-release",
417 "/etc/tinysofa-release", "/etc/turbolinux-release",
418 "/etc/ultrapenguin-release", "/etc/UnitedLinux-release",
419 "/etc/va-release", "/etc/yellowdog-release"
422 const char description
[] = "DISTRIB_DESCRIPTION=\"";
424 for (i
= 0; i
< countof(releases
); i
++)
426 file
= fopen(releases
[i
], "r");
431 fseek(file
, 0, SEEK_END
);
432 len
= min(ftell(file
), sizeof(buf
)-1);
435 if (fread(buf
, 1, len
, file
) != len
)
437 DBG1(DBG_PTS
, "failed to read file '%s'", releases
[i
]);
443 if (i
== 0) /* LSB release */
445 pos
= strstr(buf
, description
);
448 DBG1(DBG_PTS
, "failed to find begin of lsb-release "
449 "DESCRIPTION field");
452 value
= pos
+ strlen(description
);
453 pos
= strchr(value
, '"');
456 DBG1(DBG_PTS
, "failed to find end of lsb-release "
457 "DESCRIPTION field");
464 pos
= strchr(value
, '\n');
467 DBG1(DBG_PTS
, "failed to find end of release string");
476 DBG1(DBG_PTS
, "no distribution release file found");
480 if (uname(&uninfo
) < 0)
482 DBG1(DBG_PTS
, "could not retrieve machine architecture");
487 len
= sizeof(buf
)-1 + (pos
- buf
);
488 strncpy(pos
, uninfo
.machine
, len
);
490 DBG1(DBG_PTS
, "platform is '%s'", value
);
491 return strdup(value
);
495 * Check for a TPM by querying for TPM Version Info
497 static bool has_tpm(private_pts_t
*this)
499 TSS_HCONTEXT hContext
;
502 u_int32_t version_info_len
;
504 result
= Tspi_Context_Create(&hContext
);
505 if (result
!= TSS_SUCCESS
)
507 DBG1(DBG_PTS
, "TPM context could not be created: tss error 0x%x", result
);
510 result
= Tspi_Context_Connect(hContext
, NULL
);
511 if (result
!= TSS_SUCCESS
)
515 result
= Tspi_Context_GetTpmObject (hContext
, &hTPM
);
516 if (result
!= TSS_SUCCESS
)
520 result
= Tspi_TPM_GetCapability(hTPM
, TSS_TPMCAP_VERSION_VAL
, 0, NULL
,
522 &this->tpm_version_info
.ptr
);
523 this->tpm_version_info
.len
= version_info_len
;
524 if (result
!= TSS_SUCCESS
)
528 this->tpm_version_info
= chunk_clone(this->tpm_version_info
);
532 DBG1(DBG_PTS
, "TPM not available: tss error 0x%x", result
);
533 Tspi_Context_Close(hContext
);
540 pts_t
*pts_create(bool is_imc
)
546 .get_proto_caps
= _get_proto_caps
,
547 .set_proto_caps
= _set_proto_caps
,
548 .get_meas_algorithm
= _get_meas_algorithm
,
549 .set_meas_algorithm
= _set_meas_algorithm
,
550 .get_platform_info
= _get_platform_info
,
551 .set_platform_info
= _set_platform_info
,
552 .get_tpm_version_info
= _get_tpm_version_info
,
553 .set_tpm_version_info
= _set_tpm_version_info
,
556 .is_path_valid
= _is_path_valid
,
557 .do_measurements
= _do_measurements
,
560 .proto_caps
= PTS_PROTO_CAPS_V
,
561 .algorithm
= PTS_MEAS_ALGO_SHA256
,
566 this->platform_info
= extract_platform_info();
570 this->has_tpm
= TRUE
;
571 this->proto_caps
|= PTS_PROTO_CAPS_T
;
577 this->proto_caps
|= PTS_PROTO_CAPS_T
| PTS_PROTO_CAPS_C
;
580 return &this->public;