tpm.c 10.3 KB
Newer Older
1 2 3
#include <efi.h>
#include <efilib.h>
#include <string.h>
4
#include <stdint.h>
5

6
#include "shim.h"
7

8 9 10 11 12 13 14 15 16 17
typedef struct {
	CHAR16 *VariableName;
	EFI_GUID *VendorGuid;
	VOID *Data;
	UINTN Size;
} VARIABLE_RECORD;

UINTN measuredcount = 0;
VARIABLE_RECORD *measureddata = NULL;

18 19
static BOOLEAN tpm_present(efi_tpm_protocol_t *tpm)
{
20
	EFI_STATUS efi_status;
21 22 23 24 25
	TCG_EFI_BOOT_SERVICE_CAPABILITY caps;
	UINT32 flags;
	EFI_PHYSICAL_ADDRESS eventlog, lastevent;

	caps.Size = (UINT8)sizeof(caps);
26 27 28 29
	efi_status = tpm->status_check(tpm, &caps, &flags,
				       &eventlog, &lastevent);
	if (EFI_ERROR(efi_status) ||
	    caps.TPMDeactivatedFlag || !caps.TPMPresentFlag)
30 31 32 33 34
		return FALSE;

	return TRUE;
}

35 36 37
static EFI_STATUS tpm2_get_caps(efi_tpm2_protocol_t *tpm,
				EFI_TCG2_BOOT_SERVICE_CAPABILITY *caps,
				BOOLEAN *old_caps)
38
{
39
	EFI_STATUS efi_status;
40

41
	caps->Size = (UINT8)sizeof(*caps);
42

43 44 45
	efi_status = tpm->get_capability(tpm, caps);
	if (EFI_ERROR(efi_status))
		return efi_status;
46

47 48 49
	if (caps->StructureVersion.Major == 1 &&
	    caps->StructureVersion.Minor == 0)
		*old_caps = TRUE;
50 51
	else
		*old_caps = FALSE;
52 53 54 55 56 57 58 59 60 61 62 63

	return EFI_SUCCESS;
}

static BOOLEAN tpm2_present(EFI_TCG2_BOOT_SERVICE_CAPABILITY *caps,
			    BOOLEAN old_caps)
{
	TREE_BOOT_SERVICE_CAPABILITY *caps_1_0;

	if (old_caps) {
		caps_1_0 = (TREE_BOOT_SERVICE_CAPABILITY *)caps;
		if (caps_1_0->TrEEPresentFlag)
64 65 66
			return TRUE;
	}

67 68 69
	if (caps->TPMPresentFlag)
		return TRUE;

70 71 72
	return FALSE;
}

73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104
static inline EFI_TCG2_EVENT_LOG_BITMAP
tpm2_get_supported_logs(efi_tpm2_protocol_t *tpm,
			EFI_TCG2_BOOT_SERVICE_CAPABILITY *caps,
			BOOLEAN old_caps)
{
	if (old_caps)
		return ((TREE_BOOT_SERVICE_CAPABILITY *)caps)->SupportedEventLogs;

	return caps->SupportedEventLogs;
}

/*
 * According to TCG EFI Protocol Specification for TPM 2.0 family,
 * all events generated after the invocation of EFI_TCG2_GET_EVENT_LOG
 * shall be stored in an instance of an EFI_CONFIGURATION_TABLE aka
 * EFI TCG 2.0 final events table. Hence, it is necessary to trigger the
 * internal switch through calling get_event_log() in order to allow
 * to retrieve the logs from OS runtime.
 */
static EFI_STATUS trigger_tcg2_final_events_table(efi_tpm2_protocol_t *tpm2,
						  EFI_TCG2_EVENT_LOG_BITMAP supported_logs)
{
	EFI_TCG2_EVENT_LOG_FORMAT log_fmt;
	EFI_PHYSICAL_ADDRESS start;
	EFI_PHYSICAL_ADDRESS end;
	BOOLEAN truncated;

	if (supported_logs & EFI_TCG2_EVENT_LOG_FORMAT_TCG_2)
		log_fmt = EFI_TCG2_EVENT_LOG_FORMAT_TCG_2;
	else
		log_fmt = EFI_TCG2_EVENT_LOG_FORMAT_TCG_1_2;

105
	return tpm2->get_event_log(tpm2, log_fmt, &start, &end, &truncated);
106 107
}

108 109 110 111
static EFI_STATUS tpm_locate_protocol(efi_tpm_protocol_t **tpm,
				      efi_tpm2_protocol_t **tpm2,
				      BOOLEAN *old_caps_p,
				      EFI_TCG2_BOOT_SERVICE_CAPABILITY *capsp)
112
{
113
	EFI_STATUS efi_status;
114

115 116
	*tpm = NULL;
	*tpm2 = NULL;
117
	efi_status = LibLocateProtocol(&EFI_TPM2_GUID, (VOID **)tpm2);
118
	/* TPM 2.0 */
119
	if (!EFI_ERROR(efi_status)) {
120 121
		BOOLEAN old_caps;
		EFI_TCG2_BOOT_SERVICE_CAPABILITY caps;
122

123 124 125
		efi_status = tpm2_get_caps(*tpm2, &caps, &old_caps);
		if (EFI_ERROR(efi_status))
			return efi_status;
126 127 128 129 130 131

		if (tpm2_present(&caps, old_caps)) {
			if (old_caps_p)
				*old_caps_p = old_caps;
			if (capsp)
				memcpy(capsp, &caps, sizeof(caps));
132
			return EFI_SUCCESS;
133 134
		}
	} else {
135 136 137
		efi_status = LibLocateProtocol(&EFI_TPM_GUID, (VOID **)tpm);
		if (EFI_ERROR(efi_status))
			return efi_status;
138

139
		if (tpm_present(*tpm))
140
			return EFI_SUCCESS;
141 142 143 144 145 146 147 148 149
	}

	return EFI_NOT_FOUND;
}

static EFI_STATUS tpm_log_event_raw(EFI_PHYSICAL_ADDRESS buf, UINTN size,
				    UINT8 pcr, const CHAR8 *log, UINTN logsize,
				    UINT32 type, CHAR8 *hash)
{
150
	EFI_STATUS efi_status;
151 152 153 154 155
	efi_tpm_protocol_t *tpm;
	efi_tpm2_protocol_t *tpm2;
	BOOLEAN old_caps;
	EFI_TCG2_BOOT_SERVICE_CAPABILITY caps;

156 157 158 159 160 161 162 163 164 165 166
	efi_status = tpm_locate_protocol(&tpm, &tpm2, &old_caps, &caps);
	if (EFI_ERROR(efi_status)) {
#ifdef REQUIRE_TPM
		perror(L"TPM logging failed: %r\n", efi_status);
		return efi_status;
#else
		if (efi_status != EFI_NOT_FOUND) {
			perror(L"TPM logging failed: %r\n", efi_status);
			return efi_status;
		}
#endif
167 168 169
	} else if (tpm2) {
		EFI_TCG2_EVENT *event;
		EFI_TCG2_EVENT_LOG_BITMAP supported_logs;
170

171 172
		supported_logs = tpm2_get_supported_logs(tpm2, &caps, old_caps);

173 174 175 176 177 178
		efi_status = trigger_tcg2_final_events_table(tpm2,
							     supported_logs);
		if (EFI_ERROR(efi_status)) {
			perror(L"Unable to trigger tcg2 final events table: %r\n",
			       efi_status);
			return efi_status;
179 180
		}

181
		event = AllocatePool(sizeof(*event) + logsize);
182 183 184 185 186 187 188 189
		if (!event) {
			perror(L"Unable to allocate event structure\n");
			return EFI_OUT_OF_RESOURCES;
		}

		event->Header.HeaderSize = sizeof(EFI_TCG2_EVENT_HEADER);
		event->Header.HeaderVersion = 1;
		event->Header.PCRIndex = pcr;
190 191 192 193 194
		event->Header.EventType = type;
		event->Size = sizeof(*event) - sizeof(event->Event) + logsize + 1;
		CopyMem(event->Event, (VOID *)log, logsize);
		if (hash) {
			/* TPM 2 systems will generate the appropriate hash
195 196
			   themselves if we pass PE_COFF_IMAGE.  In case that
			   fails we fall back to measuring without it.
197
			*/
198 199 200 201 202 203 204
			efi_status = tpm2->hash_log_extend_event(tpm2,
				PE_COFF_IMAGE, buf, (UINT64) size, event);
		}

	        if (!hash || EFI_ERROR(efi_status)) {
			efi_status = tpm2->hash_log_extend_event(tpm2,
				0, buf, (UINT64) size, event);
205
		}
206
		FreePool(event);
207
		return efi_status;
208
	} else if (tpm) {
209
		TCG_PCR_EVENT *event;
210
		UINT32 eventnum = 0;
211 212
		EFI_PHYSICAL_ADDRESS lastevent;

213 214
		efi_status = LibLocateProtocol(&EFI_TPM_GUID, (VOID **)&tpm);
		if (EFI_ERROR(efi_status))
215 216 217 218 219
			return EFI_SUCCESS;

		if (!tpm_present(tpm))
			return EFI_SUCCESS;

220
		event = AllocatePool(sizeof(*event) + logsize);
221 222 223 224 225 226 227

		if (!event) {
			perror(L"Unable to allocate event structure\n");
			return EFI_OUT_OF_RESOURCES;
		}

		event->PCRIndex = pcr;
228 229 230 231 232 233 234 235
		event->EventType = type;
		event->EventSize = logsize;
		CopyMem(event->Event, (VOID *)log, logsize);
		if (hash) {
			/* TPM 1.2 devices require us to pass the Authenticode
			   hash rather than allowing the firmware to attempt
			   to calculate it */
			CopyMem(event->digest, hash, sizeof(event->digest));
236 237
			efi_status = tpm->log_extend_event(tpm, 0, 0,
				TPM_ALG_SHA, event, &eventnum, &lastevent);
238
		} else {
239 240 241
			efi_status = tpm->log_extend_event(tpm, buf,
				(UINT64)size, TPM_ALG_SHA, event, &eventnum,
				&lastevent);
242
		}
243
		FreePool(event);
244
		return efi_status;
245 246 247 248
	}

	return EFI_SUCCESS;
}
249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330

EFI_STATUS tpm_log_event(EFI_PHYSICAL_ADDRESS buf, UINTN size, UINT8 pcr,
			 const CHAR8 *description)
{
	return tpm_log_event_raw(buf, size, pcr, description,
				 strlen(description) + 1, 0xd, NULL);
}

EFI_STATUS tpm_log_pe(EFI_PHYSICAL_ADDRESS buf, UINTN size, UINT8 *sha1hash,
		      UINT8 pcr)
{
	EFI_IMAGE_LOAD_EVENT ImageLoad;

	// All of this is informational and forces us to do more parsing before
	// we can generate it, so let's just leave it out for now
	ImageLoad.ImageLocationInMemory = 0;
	ImageLoad.ImageLengthInMemory = 0;
	ImageLoad.ImageLinkTimeAddress = 0;
	ImageLoad.LengthOfDevicePath = 0;

	return tpm_log_event_raw(buf, size, pcr, (CHAR8 *)&ImageLoad,
				 sizeof(ImageLoad),
				 EV_EFI_BOOT_SERVICES_APPLICATION, sha1hash);
}

typedef struct {
	EFI_GUID VariableName;
	UINT64 UnicodeNameLength;
	UINT64 VariableDataLength;
	CHAR16 UnicodeName[1];
	INT8 VariableData[1];
} EFI_VARIABLE_DATA_TREE;

static BOOLEAN tpm_data_measured(CHAR16 *VarName, EFI_GUID VendorGuid, UINTN VarSize, VOID *VarData)
{
	UINTN i;

	for (i=0; i<measuredcount; i++) {
		if ((StrCmp (VarName, measureddata[i].VariableName) == 0) &&
		    (CompareGuid (&VendorGuid, measureddata[i].VendorGuid)) &&
		    (VarSize == measureddata[i].Size) &&
		    (CompareMem (VarData, measureddata[i].Data, VarSize) == 0)) {
			return TRUE;
		}
	}

	return FALSE;
}

static EFI_STATUS tpm_record_data_measurement(CHAR16 *VarName, EFI_GUID VendorGuid, UINTN VarSize, VOID *VarData)
{
	if (measureddata == NULL) {
		measureddata = AllocatePool(sizeof(*measureddata));
	} else {
		measureddata = ReallocatePool(measureddata, measuredcount * sizeof(*measureddata),
					      (measuredcount + 1) * sizeof(*measureddata));
	}

	if (measureddata == NULL)
		return EFI_OUT_OF_RESOURCES;

	measureddata[measuredcount].VariableName = AllocatePool(StrSize(VarName));
	measureddata[measuredcount].VendorGuid = AllocatePool(sizeof(EFI_GUID));
	measureddata[measuredcount].Data = AllocatePool(VarSize);

	if (measureddata[measuredcount].VariableName == NULL ||
	    measureddata[measuredcount].VendorGuid == NULL ||
	    measureddata[measuredcount].Data == NULL) {
		return EFI_OUT_OF_RESOURCES;
	}

	StrCpy(measureddata[measuredcount].VariableName, VarName);
	CopyMem(measureddata[measuredcount].VendorGuid, &VendorGuid, sizeof(EFI_GUID));
	CopyMem(measureddata[measuredcount].Data, VarData, VarSize);
	measureddata[measuredcount].Size = VarSize;
	measuredcount++;

	return EFI_SUCCESS;
}

EFI_STATUS tpm_measure_variable(CHAR16 *VarName, EFI_GUID VendorGuid, UINTN VarSize, VOID *VarData)
{
331
	EFI_STATUS efi_status;
332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360
	UINTN VarNameLength;
	EFI_VARIABLE_DATA_TREE *VarLog;
	UINT32 VarLogSize;

	/* Don't measure something that we've already measured */
	if (tpm_data_measured(VarName, VendorGuid, VarSize, VarData))
		return EFI_SUCCESS;

	VarNameLength = StrLen (VarName);
	VarLogSize = (UINT32)(sizeof (*VarLog) +
			      VarNameLength * sizeof (*VarName) +
			      VarSize -
			      sizeof (VarLog->UnicodeName) -
			      sizeof (VarLog->VariableData));

	VarLog = (EFI_VARIABLE_DATA_TREE *) AllocateZeroPool (VarLogSize);
	if (VarLog == NULL) {
		return EFI_OUT_OF_RESOURCES;
	}

	CopyMem (&VarLog->VariableName, &VendorGuid,
		 sizeof(VarLog->VariableName));
	VarLog->UnicodeNameLength  = VarNameLength;
	VarLog->VariableDataLength = VarSize;
	CopyMem (VarLog->UnicodeName, VarName,
		 VarNameLength * sizeof (*VarName));
	CopyMem ((CHAR16 *)VarLog->UnicodeName + VarNameLength, VarData,
		 VarSize);

361 362 363
	efi_status = tpm_log_event_raw((EFI_PHYSICAL_ADDRESS)(intptr_t)VarLog,
				       VarLogSize, 7, (CHAR8 *)VarLog, VarLogSize,
				       EV_EFI_VARIABLE_AUTHORITY, NULL);
364 365 366

	FreePool(VarLog);

367 368
	if (EFI_ERROR(efi_status))
		return efi_status;
369 370 371 372 373 374 375 376

	return tpm_record_data_measurement(VarName, VendorGuid, VarSize,
					   VarData);
}

EFI_STATUS
fallback_should_prefer_reset(void)
{
377
	EFI_STATUS efi_status;
378 379 380
	efi_tpm_protocol_t *tpm;
	efi_tpm2_protocol_t *tpm2;

381 382
	efi_status = tpm_locate_protocol(&tpm, &tpm2, NULL, NULL);
	if (EFI_ERROR(efi_status))
383 384 385
		return EFI_NOT_FOUND;
	return EFI_SUCCESS;
}