1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 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 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 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 331 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 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811
|
// Copyright 2020 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may not
// use this file except in compliance with the License. You may obtain a copy of
// the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations under
// the License.
package attest
import (
"bytes"
"encoding/binary"
"encoding/hex"
"errors"
"fmt"
"io"
"strings"
"unicode/utf16"
"github.com/google/go-attestation/attest/internal"
)
type windowsEvent uint32
// SIPA event types
const (
sipaTypeMask windowsEvent = 0x000f0000
sipaContainer windowsEvent = 0x00010000
sipaInformation windowsEvent = 0x00020000
sipaError windowsEvent = 0x00030000
sipaPreOsParameter windowsEvent = 0x00040000
sipaOSParameter windowsEvent = 0x00050000
sipaAuthority windowsEvent = 0x00060000
sipaLoadedModule windowsEvent = 0x00070000
sipaTrustPoint windowsEvent = 0x00080000
sipaELAM windowsEvent = 0x00090000
sipaVBS windowsEvent = 0x000a0000
trustBoundary windowsEvent = 0x40010001
elamAggregation windowsEvent = 0x40010002
loadedModuleAggregation windowsEvent = 0x40010003
trustpointAggregation windowsEvent = 0xC0010004
ksrAggregation windowsEvent = 0x40010005
ksrSignedMeasurementAggregation windowsEvent = 0x40010006
information windowsEvent = 0x00020001
bootCounter windowsEvent = 0x00020002
transferControl windowsEvent = 0x00020003
applicationReturn windowsEvent = 0x00020004
bitlockerUnlock windowsEvent = 0x00020005
eventCounter windowsEvent = 0x00020006
counterID windowsEvent = 0x00020007
morBitNotCancelable windowsEvent = 0x00020008
applicationSVN windowsEvent = 0x00020009
svnChainStatus windowsEvent = 0x0002000A
morBitAPIStatus windowsEvent = 0x0002000B
bootDebugging windowsEvent = 0x00040001
bootRevocationList windowsEvent = 0x00040002
osKernelDebug windowsEvent = 0x00050001
codeIntegrity windowsEvent = 0x00050002
testSigning windowsEvent = 0x00050003
dataExecutionPrevention windowsEvent = 0x00050004
safeMode windowsEvent = 0x00050005
winPE windowsEvent = 0x00050006
physicalAddressExtension windowsEvent = 0x00050007
osDevice windowsEvent = 0x00050008
systemRoot windowsEvent = 0x00050009
hypervisorLaunchType windowsEvent = 0x0005000A
hypervisorPath windowsEvent = 0x0005000B
hypervisorIOMMUPolicy windowsEvent = 0x0005000C
hypervisorDebug windowsEvent = 0x0005000D
driverLoadPolicy windowsEvent = 0x0005000E
siPolicy windowsEvent = 0x0005000F
hypervisorMMIONXPolicy windowsEvent = 0x00050010
hypervisorMSRFilterPolicy windowsEvent = 0x00050011
vsmLaunchType windowsEvent = 0x00050012
osRevocationList windowsEvent = 0x00050013
vsmIDKInfo windowsEvent = 0x00050020
flightSigning windowsEvent = 0x00050021
pagefileEncryptionEnabled windowsEvent = 0x00050022
vsmIDKSInfo windowsEvent = 0x00050023
hibernationDisabled windowsEvent = 0x00050024
dumpsDisabled windowsEvent = 0x00050025
dumpEncryptionEnabled windowsEvent = 0x00050026
dumpEncryptionKeyDigest windowsEvent = 0x00050027
lsaISOConfig windowsEvent = 0x00050028
noAuthority windowsEvent = 0x00060001
authorityPubKey windowsEvent = 0x00060002
filePath windowsEvent = 0x00070001
imageSize windowsEvent = 0x00070002
hashAlgorithmID windowsEvent = 0x00070003
authenticodeHash windowsEvent = 0x00070004
authorityIssuer windowsEvent = 0x00070005
authoritySerial windowsEvent = 0x00070006
imageBase windowsEvent = 0x00070007
authorityPublisher windowsEvent = 0x00070008
authoritySHA1Thumbprint windowsEvent = 0x00070009
imageValidated windowsEvent = 0x0007000A
moduleSVN windowsEvent = 0x0007000B
quote windowsEvent = 0x80080001
quoteSignature windowsEvent = 0x80080002
aikID windowsEvent = 0x80080003
aikPubDigest windowsEvent = 0x80080004
elamKeyname windowsEvent = 0x00090001
elamConfiguration windowsEvent = 0x00090002
elamPolicy windowsEvent = 0x00090003
elamMeasured windowsEvent = 0x00090004
vbsVSMRequired windowsEvent = 0x000A0001
vbsSecurebootRequired windowsEvent = 0x000A0002
vbsIOMMURequired windowsEvent = 0x000A0003
vbsNXRequired windowsEvent = 0x000A0004
vbsMSRFilteringRequired windowsEvent = 0x000A0005
vbsMandatoryEnforcement windowsEvent = 0x000A0006
vbsHVCIPolicy windowsEvent = 0x000A0007
vbsMicrosoftBootChainRequired windowsEvent = 0x000A0008
ksrSignature windowsEvent = 0x000B0001
)
type WinCSPAlg uint32
// Valid CSP Algorithm IDs.
const (
WinAlgMD4 WinCSPAlg = 0x02
WinAlgMD5 WinCSPAlg = 0x03
WinAlgSHA1 WinCSPAlg = 0x04
WinAlgSHA256 WinCSPAlg = 0x0c
WinAlgSHA384 WinCSPAlg = 0x0d
WinAlgSHA512 WinCSPAlg = 0x0e
)
// BitlockerStatus describes the status of BitLocker on a Windows system.
type BitlockerStatus uint8
// Valid BitlockerStatus values.
const (
BitlockerStatusCached = 0x01
BitlockerStatusMedia = 0x02
BitlockerStatusTPM = 0x04
BitlockerStatusPin = 0x10
BitlockerStatusExternal = 0x20
BitlockerStatusRecovery = 0x40
)
// Ternary describes a boolean value that can additionally be unknown.
type Ternary uint8
// Valid Ternary values.
const (
TernaryUnknown Ternary = iota
TernaryTrue
TernaryFalse
)
// WinEvents describes information from the event log recorded during
// bootup of Microsoft Windows.
type WinEvents struct {
// ColdBoot is set to true if the system was not resuming from hibernation.
ColdBoot bool
// BootCount contains the value of the monotonic boot counter. This
// value is not set for TPM 1.2 devices and some TPMs with buggy
// implementations of monotonic counters.
BootCount uint64
// LoadedModules contains authenticode hashes for binaries which
// were loaded during boot.
LoadedModules map[string]WinModuleLoad
// ELAM describes the configuration of each Early Launch AntiMalware driver,
// for each AV Vendor key.
ELAM map[string]WinELAM
// BootDebuggingEnabled is true if boot debugging was ever reported
// as enabled.
BootDebuggingEnabled bool
// KernelDebugEnabled is true if kernel debugging was recorded as
// enabled at any point during boot.
KernelDebugEnabled bool
// DEPEnabled is true if NX (Data Execution Prevention) was consistently
// reported as enabled.
DEPEnabled Ternary
// CodeIntegrityEnabled is true if code integrity was consistently
// reported as enabled.
CodeIntegrityEnabled Ternary
// TestSigningEnabled is true if test-mode signature verification was
// ever reported as enabled.
TestSigningEnabled bool
// BitlockerUnlocks reports the bitlocker status for every instance of
// a disk unlock, where bitlocker was used to secure the disk.
BitlockerUnlocks []BitlockerStatus
}
// WinModuleLoad describes a module which was loaded while
// Windows booted.
type WinModuleLoad struct {
// FilePath represents the path from which the module was loaded. This
// information is not always present.
FilePath string
// AuthenticodeHash contains the authenticode hash of the binary
// blob which was loaded.
AuthenticodeHash []byte
// ImageBase describes all the addresses to which the the blob was loaded.
ImageBase []uint64
// ImageSize describes the size of the image in bytes. This information
// is not always present.
ImageSize uint64
// HashAlgorithm describes the hash algorithm used.
HashAlgorithm WinCSPAlg
// ImageValidated is set if the post-boot loader validated the image.
ImageValidated bool
// AuthorityIssuer identifies the issuer of the certificate which certifies
// the signature on this module.
AuthorityIssuer string
// AuthorityPublisher identifies the publisher of the certificate which
// certifies the signature on this module.
AuthorityPublisher string
// AuthoritySerial contains the serial of the certificate certifying this
// module.
AuthoritySerial []byte
// AuthoritySHA1 is the SHA1 hash of the certificate thumbprint.
AuthoritySHA1 []byte
}
// WinELAM describes the configuration of an Early Launch AntiMalware driver.
// These values represent the 3 measured registry values stored in the ELAM
// hive for the driver.
type WinELAM struct {
Measured []byte
Config []byte
Policy []byte
}
// ParseWinEvents parses a series of events to extract information about
// the bringup of Microsoft Windows. This information is not trustworthy
// unless the integrity of platform & bootloader events has already been
// established.
func ParseWinEvents(events []Event) (*WinEvents, error) {
var (
out = WinEvents{
LoadedModules: map[string]WinModuleLoad{},
ELAM: map[string]WinELAM{},
}
seenSeparator struct {
PCR12 bool
PCR13 bool
}
)
for _, e := range events {
if e.Index != 12 && e.Index != 13 {
continue
}
et, err := internal.UntrustedParseEventType(uint32(e.Type))
if err != nil {
return nil, fmt.Errorf("unrecognised event type: %v", err)
}
digestVerify := e.digestEquals(e.Data)
switch e.Index {
case 12: // 'early boot' events
switch et {
case internal.EventTag:
if seenSeparator.PCR12 {
continue
}
s, err := internal.ParseTaggedEventData(e.Data)
if err != nil {
return nil, fmt.Errorf("invalid tagged event structure at event %d: %w", e.sequence, err)
}
if digestVerify != nil {
return nil, fmt.Errorf("invalid digest for tagged event %d: %w", e.sequence, digestVerify)
}
if err := out.readWinEventBlock(s, e.Index); err != nil {
return nil, fmt.Errorf("invalid SIPA events in event %d: %w", e.sequence, err)
}
case internal.Separator:
if seenSeparator.PCR12 {
return nil, fmt.Errorf("duplicate WBCL separator at event %d", e.sequence)
}
seenSeparator.PCR12 = true
if !bytes.Equal(e.Data, []byte("WBCL")) {
return nil, fmt.Errorf("invalid WBCL separator data at event %d: %v", e.sequence, e.Data)
}
if digestVerify != nil {
return nil, fmt.Errorf("invalid separator digest at event %d: %v", e.sequence, digestVerify)
}
default:
return nil, fmt.Errorf("unexpected (PCR12) event type: %v", et)
}
case 13: // Post 'early boot' events
switch et {
case internal.EventTag:
if seenSeparator.PCR13 {
continue
}
s, err := internal.ParseTaggedEventData(e.Data)
if err != nil {
return nil, fmt.Errorf("invalid tagged event structure at event %d: %w", e.sequence, err)
}
if digestVerify != nil {
return nil, fmt.Errorf("invalid digest for tagged event %d: %w", e.sequence, digestVerify)
}
if err := out.readWinEventBlock(s, e.Index); err != nil {
return nil, fmt.Errorf("invalid SIPA events in event %d: %w", e.sequence, err)
}
case internal.Separator:
if seenSeparator.PCR13 {
return nil, fmt.Errorf("duplicate WBCL separator at event %d", e.sequence)
}
seenSeparator.PCR13 = true
if !bytes.Equal(e.Data, []byte("WBCL")) {
return nil, fmt.Errorf("invalid WBCL separator data at event %d: %v", e.sequence, e.Data)
}
if digestVerify != nil {
return nil, fmt.Errorf("invalid separator digest at event %d: %v", e.sequence, digestVerify)
}
default:
return nil, fmt.Errorf("unexpected (PCR13) event type: %v", et)
}
}
}
return &out, nil
}
type microsoftEventHeader struct {
Type windowsEvent
Size uint32
}
// unknownSIPAEvent is returned by parseSIPAEvent if the event type is
// not handled. Unlike other events in the TCG log, it is safe to skip
// unhandled SIPA events, as they are embedded within EventTag structures,
// and these structures should match the event digest.
var unknownSIPAEvent = errors.New("unknown event")
func (w *WinEvents) readBooleanInt64Event(header microsoftEventHeader, r *bytes.Reader) error {
if header.Size != 8 {
return fmt.Errorf("payload was %d bytes, want 8", header.Size)
}
var num uint64
if err := binary.Read(r, binary.LittleEndian, &num); err != nil {
return fmt.Errorf("reading u64: %w", err)
}
isSet := num != 0
switch header.Type {
// Boolean signals that latch off if the are ever false (ie: attributes
// that represent a stronger security state when set).
case dataExecutionPrevention:
if isSet && w.DEPEnabled == TernaryUnknown {
w.DEPEnabled = TernaryTrue
} else if !isSet {
w.DEPEnabled = TernaryFalse
}
}
return nil
}
func (w *WinEvents) readBooleanByteEvent(header microsoftEventHeader, r *bytes.Reader) error {
if header.Size != 1 {
return fmt.Errorf("payload was %d bytes, want 1", header.Size)
}
var b byte
if err := binary.Read(r, binary.LittleEndian, &b); err != nil {
return fmt.Errorf("reading byte: %w", err)
}
isSet := b != 0
switch header.Type {
// Boolean signals that latch on if they are ever true (ie: attributes
// that represent a weaker security state when set).
case osKernelDebug:
w.KernelDebugEnabled = w.KernelDebugEnabled || isSet
case bootDebugging:
w.BootDebuggingEnabled = w.BootDebuggingEnabled || isSet
case testSigning:
w.TestSigningEnabled = w.TestSigningEnabled || isSet
// Boolean signals that latch off if the are ever false (ie: attributes
// that represent a stronger security state when set).
case codeIntegrity:
if isSet && w.CodeIntegrityEnabled == TernaryUnknown {
w.CodeIntegrityEnabled = TernaryTrue
} else if !isSet {
w.CodeIntegrityEnabled = TernaryFalse
}
}
return nil
}
func (w *WinEvents) readUint32(header microsoftEventHeader, r io.Reader) (uint32, error) {
if header.Size != 4 {
return 0, fmt.Errorf("integer size not uint32 (%d bytes)", header.Size)
}
data := make([]uint8, header.Size)
if err := binary.Read(r, binary.LittleEndian, &data); err != nil {
return 0, fmt.Errorf("reading u32: %w", err)
}
i := binary.LittleEndian.Uint32(data)
return i, nil
}
func (w *WinEvents) readUint64(header microsoftEventHeader, r io.Reader) (uint64, error) {
if header.Size != 8 {
return 0, fmt.Errorf("integer size not uint64 (%d bytes)", header.Size)
}
data := make([]uint8, header.Size)
if err := binary.Read(r, binary.LittleEndian, &data); err != nil {
return 0, fmt.Errorf("reading u64: %w", err)
}
i := binary.LittleEndian.Uint64(data)
return i, nil
}
func (w *WinEvents) readBootCounter(header microsoftEventHeader, r *bytes.Reader) error {
i, err := w.readUint64(header, r)
if err != nil {
return fmt.Errorf("boot counter: %v", err)
}
if w.BootCount > 0 && w.BootCount != i {
return fmt.Errorf("conflicting values for boot counter: %d != %d", i, w.BootCount)
}
w.BootCount = i
return nil
}
func (w *WinEvents) readTransferControl(header microsoftEventHeader, r *bytes.Reader) error {
i, err := w.readUint32(header, r)
if err != nil {
return fmt.Errorf("transfer control: %v", err)
}
// A transferControl event with a value of 1 indicates that bootmngr
// launched WinLoad. A different (unknown) value is set if WinResume
// is launched.
w.ColdBoot = i == 0x1
return nil
}
func (w *WinEvents) readBitlockerUnlock(header microsoftEventHeader, r *bytes.Reader, pcr int) error {
if header.Size > 8 {
return fmt.Errorf("bitlocker data too large (%d bytes)", header.Size)
}
data := make([]uint8, header.Size)
if err := binary.Read(r, binary.LittleEndian, &data); err != nil {
return fmt.Errorf("reading u%d: %w", header.Size<<8, err)
}
i, n := binary.Uvarint(data)
if n <= 0 {
return fmt.Errorf("reading u%d: invalid varint", header.Size<<8)
}
if pcr == 13 {
// The bitlocker status is duplicated across both PCRs. As such,
// we prefer the earlier one, and bail here to prevent duplicate
// records.
return nil
}
w.BitlockerUnlocks = append(w.BitlockerUnlocks, BitlockerStatus(i))
return nil
}
func (w *WinEvents) parseImageValidated(header microsoftEventHeader, r io.Reader) (bool, error) {
if header.Size != 1 {
return false, fmt.Errorf("payload was %d bytes, want 1", header.Size)
}
var num byte
if err := binary.Read(r, binary.LittleEndian, &num); err != nil {
return false, fmt.Errorf("reading u8: %w", err)
}
return num == 1, nil
}
func (w *WinEvents) parseHashAlgID(header microsoftEventHeader, r io.Reader) (WinCSPAlg, error) {
i, err := w.readUint32(header, r)
if err != nil {
return 0, fmt.Errorf("hash algorithm ID: %v", err)
}
switch alg := WinCSPAlg(i & 0xff); alg {
case WinAlgMD4, WinAlgMD5, WinAlgSHA1, WinAlgSHA256, WinAlgSHA384, WinAlgSHA512:
return alg, nil
default:
return 0, fmt.Errorf("unknown algorithm ID: %x", i)
}
}
func (w *WinEvents) parseAuthoritySerial(header microsoftEventHeader, r io.Reader) ([]byte, error) {
if header.Size > 128 {
return nil, fmt.Errorf("authority serial is too long (%d bytes)", header.Size)
}
data := make([]byte, header.Size)
if err := binary.Read(r, binary.LittleEndian, &data); err != nil {
return nil, fmt.Errorf("reading bytes: %w", err)
}
return data, nil
}
func (w *WinEvents) parseAuthoritySHA1(header microsoftEventHeader, r io.Reader) ([]byte, error) {
if header.Size > 20 {
return nil, fmt.Errorf("authority thumbprint is too long (%d bytes)", header.Size)
}
data := make([]byte, header.Size)
if err := binary.Read(r, binary.LittleEndian, &data); err != nil {
return nil, fmt.Errorf("reading bytes: %w", err)
}
return data, nil
}
func (w *WinEvents) parseImageBase(header microsoftEventHeader, r io.Reader) (uint64, error) {
if header.Size != 8 {
return 0, fmt.Errorf("payload was %d bytes, want 8", header.Size)
}
var num uint64
if err := binary.Read(r, binary.LittleEndian, &num); err != nil {
return 0, fmt.Errorf("reading u64: %w", err)
}
return num, nil
}
func (w *WinEvents) parseAuthenticodeHash(header microsoftEventHeader, r io.Reader) ([]byte, error) {
if header.Size > 32 {
return nil, fmt.Errorf("authenticode hash data exceeds the size of any valid hash (%d bytes)", header.Size)
}
data := make([]byte, header.Size)
if err := binary.Read(r, binary.LittleEndian, &data); err != nil {
return nil, fmt.Errorf("reading bytes: %w", err)
}
return data, nil
}
func (w *WinEvents) readLoadedModuleAggregation(rdr *bytes.Reader, header microsoftEventHeader) error {
var (
r = &io.LimitedReader{R: rdr, N: int64(header.Size)}
codeHash []byte
imgBase, imgSize uint64
fPath string
algID WinCSPAlg
imgValidated bool
aIssuer, aPublisher string
aSerial, aSHA1 []byte
)
for r.N > 0 {
var h microsoftEventHeader
if err := binary.Read(r, binary.LittleEndian, &h); err != nil {
return fmt.Errorf("parsing LMA sub-event: %v", err)
}
if int64(h.Size) > r.N {
return fmt.Errorf("LMA sub-event is larger than available data: %d > %d", h.Size, r.N)
}
var err error
switch h.Type {
case imageBase:
if imgBase != 0 {
return errors.New("duplicate image base data in LMA event")
}
if imgBase, err = w.parseImageBase(h, r); err != nil {
return err
}
case authenticodeHash:
if codeHash != nil {
return errors.New("duplicate authenticode hash structure in LMA event")
}
if codeHash, err = w.parseAuthenticodeHash(h, r); err != nil {
return err
}
case filePath:
if fPath != "" {
return errors.New("duplicate file path in LMA event")
}
if fPath, err = w.parseUTF16(h, r); err != nil {
return err
}
case imageSize:
if imgSize != 0 {
return errors.New("duplicate image size in LMA event")
}
if imgSize, err = w.readUint64(h, r); err != nil {
return err
}
case hashAlgorithmID:
if algID != 0 {
return errors.New("duplicate hash algorithm ID in LMA event")
}
if algID, err = w.parseHashAlgID(h, r); err != nil {
return err
}
case imageValidated:
if imgValidated {
return errors.New("duplicate image validated field in LMA event")
}
if imgValidated, err = w.parseImageValidated(h, r); err != nil {
return err
}
case authorityIssuer:
if aIssuer != "" {
return errors.New("duplicate authority issuer in LMA event")
}
if aIssuer, err = w.parseUTF16(h, r); err != nil {
return err
}
case authorityPublisher:
if aPublisher != "" {
return errors.New("duplicate authority publisher in LMA event")
}
if aPublisher, err = w.parseUTF16(h, r); err != nil {
return err
}
case authoritySerial:
if aSerial != nil {
return errors.New("duplicate authority serial in LMA event")
}
if aSerial, err = w.parseAuthoritySerial(h, r); err != nil {
return err
}
case authoritySHA1Thumbprint:
if aSHA1 != nil {
return errors.New("duplicate authority SHA1 thumbprint in LMA event")
}
if aSHA1, err = w.parseAuthoritySHA1(h, r); err != nil {
return err
}
case moduleSVN:
// Ignore - consume value.
b := make([]byte, h.Size)
if err := binary.Read(r, binary.LittleEndian, &b); err != nil {
return err
}
default:
return fmt.Errorf("unknown event in LMA aggregation: %v", h.Type)
}
}
var iBase []uint64
if imgBase != 0 {
iBase = []uint64{imgBase}
}
l := WinModuleLoad{
FilePath: fPath,
AuthenticodeHash: codeHash,
ImageBase: iBase,
ImageSize: imgSize,
ImageValidated: imgValidated,
HashAlgorithm: algID,
AuthorityIssuer: aIssuer,
AuthorityPublisher: aPublisher,
AuthoritySerial: aSerial,
AuthoritySHA1: aSHA1,
}
hashHex := hex.EncodeToString(l.AuthenticodeHash)
l.ImageBase = append(l.ImageBase, w.LoadedModules[hashHex].ImageBase...)
w.LoadedModules[hashHex] = l
return nil
}
// parseUTF16 decodes data representing a UTF16 string. It is assumed the
// caller has validated that the data size is within allowable bounds.
func (w *WinEvents) parseUTF16(header microsoftEventHeader, r io.Reader) (string, error) {
data := make([]uint16, header.Size/2)
if err := binary.Read(r, binary.LittleEndian, &data); err != nil {
return "", err
}
return strings.TrimSuffix(string(utf16.Decode(data)), "\x00"), nil
}
func (w *WinEvents) readELAMAggregation(rdr io.Reader, header microsoftEventHeader) error {
var (
r = &io.LimitedReader{R: rdr, N: int64(header.Size)}
driverName string
measured []byte
policy []byte
config []byte
)
for r.N > 0 {
var h microsoftEventHeader
if err := binary.Read(r, binary.LittleEndian, &h); err != nil {
return fmt.Errorf("parsing ELAM aggregation sub-event: %v", err)
}
if int64(h.Size) > r.N {
return fmt.Errorf("ELAM aggregation sub-event is larger than available data: %d > %d", h.Size, r.N)
}
var err error
switch h.Type {
case elamAggregation:
w.readELAMAggregation(r, h)
if r.N == 0 {
return nil
}
case elamKeyname:
if driverName != "" {
return errors.New("duplicate driver name in ELAM aggregation event")
}
if driverName, err = w.parseUTF16(h, r); err != nil {
return fmt.Errorf("parsing ELAM driver name: %v", err)
}
case elamMeasured:
if measured != nil {
return errors.New("duplicate measured data in ELAM aggregation event")
}
measured = make([]byte, h.Size)
if err := binary.Read(r, binary.LittleEndian, &measured); err != nil {
return fmt.Errorf("reading ELAM measured value: %v", err)
}
case elamPolicy:
if policy != nil {
return errors.New("duplicate policy data in ELAM aggregation event")
}
policy = make([]byte, h.Size)
if err := binary.Read(r, binary.LittleEndian, &policy); err != nil {
return fmt.Errorf("reading ELAM policy value: %v", err)
}
case elamConfiguration:
if config != nil {
return errors.New("duplicate config data in ELAM aggregation event")
}
config = make([]byte, h.Size)
if err := binary.Read(r, binary.LittleEndian, &config); err != nil {
return fmt.Errorf("reading ELAM config value: %v", err)
}
default:
return fmt.Errorf("unknown event in LMA aggregation: %v", h.Type)
}
}
if driverName == "" {
return errors.New("ELAM driver name not specified")
}
w.ELAM[driverName] = WinELAM{
Measured: measured,
Config: config,
Policy: policy,
}
return nil
}
func (w *WinEvents) readSIPAEvent(r *bytes.Reader, pcr int) error {
var header microsoftEventHeader
if err := binary.Read(r, binary.LittleEndian, &header); err != nil {
return err
}
switch header.Type {
case elamAggregation:
return w.readELAMAggregation(r, header)
case loadedModuleAggregation:
return w.readLoadedModuleAggregation(r, header)
case bootCounter:
return w.readBootCounter(header, r)
case bitlockerUnlock:
return w.readBitlockerUnlock(header, r, pcr)
case transferControl:
return w.readTransferControl(header, r)
case osKernelDebug, codeIntegrity, bootDebugging, testSigning: // Parse boolean values.
return w.readBooleanByteEvent(header, r)
case dataExecutionPrevention: // Parse booleans represented as uint64's.
return w.readBooleanInt64Event(header, r)
default:
// Event type was not handled, consume the data.
if int(header.Size) > r.Len() {
return fmt.Errorf("event data len (%d bytes) larger than event length (%d bytes)", header.Size, r.Len())
}
tmp := make([]byte, header.Size)
if err := binary.Read(r, binary.LittleEndian, &tmp); err != nil {
return fmt.Errorf("reading unknown data section of length %d: %w", header.Size, err)
}
return unknownSIPAEvent
}
}
// readWinEventBlock extracts boot configuration from SIPA events contained in
// the given tagged event.
func (w *WinEvents) readWinEventBlock(evt *internal.TaggedEventData, pcr int) error {
r := bytes.NewReader(evt.Data)
// All windows information should be sub events in an enclosing SIPA
// container event.
if (windowsEvent(evt.ID) & sipaTypeMask) != sipaContainer {
return fmt.Errorf("expected container event, got %v", windowsEvent(evt.ID))
}
for r.Len() > 0 {
if err := w.readSIPAEvent(r, pcr); err != nil {
if errors.Is(err, unknownSIPAEvent) {
// Unknown SIPA events are okay as all TCG events are verifiable.
continue
}
return err
}
}
return nil
}
|