[#260] RIM validation report page links (#264)

* Made some minor tweaks to investigate supply chain validation report bug.  The bug doesn't save the summary report for some unknown reason (no error currently appears).  This change uses the device object to retrieve a RIM.  Still need Attestation Certificate to pull PCRs from quote.  A follow up issue will be created to move that functionality to a different object from the provisioner.
This commit is contained in:
Cyrus 2020-06-23 13:24:34 -04:00 committed by GitHub
parent 6a62002b05
commit d41cb46468
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 221 additions and 188 deletions

View File

@ -1467,8 +1467,8 @@ public abstract class AbstractAttestationCertificateAuthority
// save issued certificate
IssuedAttestationCertificate attCert = new IssuedAttestationCertificate(
derEncodedAttestationCertificate, endorsementCredential, platformCredentials);
attCert.setPcrValues(pcrValues);
attCert.setDevice(device);
attCert.setPcrValues(pcrValues);
certificateManager.save(attCert);
} catch (Exception e) {
LOG.error("Error saving generated Attestation Certificate to database.", e);

View File

@ -9,6 +9,7 @@ import java.security.cert.CertificateException;
import hirs.data.persist.TPMMeasurementRecord;
import hirs.data.persist.SwidResource;
import hirs.data.persist.PCRPolicy;
import hirs.data.persist.ArchivableEntity;
import hirs.validation.SupplyChainCredentialValidator;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
@ -215,10 +216,13 @@ public class SupplyChainValidationServiceImpl implements SupplyChainValidationSe
// if both trust store and attributes validated or failed
// combine messages
validations.remove(platformScv);
List<ArchivableEntity> aes = new ArrayList<>();
for (Certificate cert : platformScv.getCertificatesUsed()) {
aes.add(cert);
}
validations.add(new SupplyChainValidation(
platformScv.getValidationType(),
platformScv.getResult(),
platformScv.getCertificatesUsed(),
platformScv.getResult(), aes,
String.format("%s%n%s", platformScv.getMessage(),
attributeScv.getMessage())));
}
@ -235,14 +239,7 @@ public class SupplyChainValidationServiceImpl implements SupplyChainValidationSe
if (policy.isFirmwareValidationEnabled()) {
// may need to associated with device to pull the correct info
// compare tpm quote with what is pulled from RIM associated file
IssuedAttestationCertificate attCert = IssuedAttestationCertificate
.select(this.certificateManager)
.byDeviceId(device.getId()).getCertificate();
PlatformCredential pc = PlatformCredential
.select(this.certificateManager)
.byDeviceId(device.getId()).getCertificate();
validations.add(validateFirmware(pc, attCert, policy.getPcrPolicy()));
validations.add(validateFirmware(device, policy.getPcrPolicy()));
}
// Generate validation summary, save it, and return it.
@ -255,7 +252,7 @@ public class SupplyChainValidationServiceImpl implements SupplyChainValidationSe
try {
supplyChainValidatorSummaryManager.save(summary);
} catch (DBManagerException ex) {
LOGGER.error("Failed to save Supply chain summary", ex);
LOGGER.error("Failed to save Supply Chain summary", ex);
}
return summary;
}
@ -316,33 +313,35 @@ public class SupplyChainValidationServiceImpl implements SupplyChainValidationSe
return subPlatformScv;
}
private SupplyChainValidation validateFirmware(final PlatformCredential pc,
final IssuedAttestationCertificate attCert, final PCRPolicy pcrPolicy) {
private SupplyChainValidation validateFirmware(final Device device,
final PCRPolicy pcrPolicy) {
ReferenceManifest rim;
String[] baseline = new String[Integer.SIZE];
Level level = Level.ERROR;
AppraisalStatus fwStatus = null;
String manufacturer = device.getDeviceInfo()
.getHardwareInfo().getManufacturer();
if (pc != null) {
rim = ReferenceManifest.select(
this.referenceManifestManager)
.byManufacturer(pc.getManufacturer())
.getRIM();
IssuedAttestationCertificate attCert = IssuedAttestationCertificate
.select(this.certificateManager)
.byDeviceId(device.getId()).getCertificate();
ReferenceManifest rim = ReferenceManifest.select(
this.referenceManifestManager)
.byManufacturer(manufacturer)
.getRIM();
if (rim == null) {
fwStatus = new AppraisalStatus(FAIL,
String.format("Firmware validation failed: "
+ "No associated RIM file could be found for %s",
pc.getManufacturer()));
} else {
List<SwidResource> swids = rim.parseResource();
for (SwidResource swid : swids) {
baseline = swid.getPcrValues()
.toArray(new String[swid.getPcrValues().size()]);
}
pcrPolicy.setBaselinePcrs(baseline);
if (rim == null) {
fwStatus = new AppraisalStatus(FAIL,
String.format("Firmware validation failed: "
+ "No associated RIM file could be found for %s",
manufacturer));
} else {
List<SwidResource> swids = rim.parseResource();
for (SwidResource swid : swids) {
baseline = swid.getPcrValues()
.toArray(new String[swid.getPcrValues().size()]);
}
pcrPolicy.setBaselinePcrs(baseline);
}
if (attCert != null && fwStatus == null) {
@ -352,7 +351,6 @@ public class SupplyChainValidationServiceImpl implements SupplyChainValidationSe
String[] quote = new String[TPMMeasurementRecord.MAX_PCR_ID + 1];
int offset = 0;
StringBuilder sb;
fwStatus = new AppraisalStatus(PASS,
SupplyChainCredentialValidator.FIRMWARE_VALID);
@ -375,7 +373,7 @@ public class SupplyChainValidationServiceImpl implements SupplyChainValidationSe
quote[i] = pcrs256[i + offset].split(":")[1].trim();
}
}
sb = pcrPolicy.validatePcrs(quote);
StringBuilder sb = pcrPolicy.validatePcrs(quote);
if (sb.length() > 0) {
level = Level.ERROR;
fwStatus = new AppraisalStatus(FAIL, sb.toString());
@ -388,7 +386,7 @@ public class SupplyChainValidationServiceImpl implements SupplyChainValidationSe
}
return buildValidationRecord(SupplyChainValidation.ValidationType.FIRMWARE,
fwStatus.getAppStatus(), fwStatus.getMessage(), pc, level);
fwStatus.getAppStatus(), fwStatus.getMessage(), rim, level);
}
private SupplyChainValidation validateEndorsementCredential(final EndorsementCredential ec,
@ -516,22 +514,22 @@ public class SupplyChainValidationServiceImpl implements SupplyChainValidationSe
* @param validationType the type of validation
* @param result the appraisal status
* @param message the validation message to include in the summary and log
* @param certificate the certificate associated with the validation
* @param archivableEntity the archivableEntity associated with the validation
* @param logLevel the log level
* @return a SupplyChainValidation
*/
private SupplyChainValidation buildValidationRecord(
final SupplyChainValidation.ValidationType validationType,
final AppraisalStatus.Status result, final String message,
final Certificate certificate, final Level logLevel) {
final ArchivableEntity archivableEntity, final Level logLevel) {
List<Certificate> certificateList = new ArrayList<>();
if (certificate != null) {
certificateList.add(certificate);
List<ArchivableEntity> aeList = new ArrayList<>();
if (archivableEntity != null) {
aeList.add(archivableEntity);
}
LOGGER.log(logLevel, message);
return new SupplyChainValidation(validationType, result, certificateList, message);
return new SupplyChainValidation(validationType, result, aeList, message);
}
/**

View File

@ -43,83 +43,83 @@
</table>
</div>
<script>
$(document).ready(function() {
$(document).ready(function () {
var url = portal + '/validation-reports/list';
var columns = [
{
data: 'overallValidationResult',
searchable:false,
render: function (data, type, full, meta) {
var html = '';
var unknownStatus = '<img class="icon" src="${unknownIcon}" title="${unknownText}"/>';
{
data: 'overallValidationResult',
searchable: false,
render: function (data, type, full, meta) {
var html = '';
var unknownStatus = '<img class="icon" src="${unknownIcon}" title="${unknownText}"/>';
// create status icon
var result = full.overallValidationResult;
var ovallMessage = full.message;
if (result) {
switch (result) {
case "PASS":
html += '<img src="${passIcon}" title="${passText}"/>';
break;
case "FAIL":
html += '<img src="${failIcon}" title="' + ovallMessage + '"/>';
break;
case "ERROR":
html += '<img src="${errorIcon}" title="' + ovallMessage + '"/>';
break;
default:
html += unknownStatus;
break;
}
} else {
html += unknownStatus;
// create status icon
var result = full.overallValidationResult;
var ovallMessage = full.message;
if (result) {
switch (result) {
case "PASS":
html += '<img src="${passIcon}" title="${passText}"/>';
break;
case "FAIL":
html += '<img src="${failIcon}" title="' + ovallMessage + '"/>';
break;
case "ERROR":
html += '<img src="${errorIcon}" title="' + ovallMessage + '"/>';
break;
default:
html += unknownStatus;
break;
}
} else {
html += unknownStatus;
}
return html;
}
},
{
// Note: DB column is create_time, while the
// JSON property / java property is createTime. Need to sort
// on the field createTime, but the column's
// date source is create_time.
data: 'create_time',
name: 'createTime',
searchable:false,
render: function (data, type, full, meta) {
return formatDateTime(full.createTime);
}
},
{
// TODO render a link to a device details page,
// passing the device.id
data: 'device.name'
},
{
data: 'id',
searchable: false,
orderable: false,
render: function (data, type, full, meta) {
return getValidationDisplayHtml(full, "ENDORSEMENT_CREDENTIAL")
}
},
{
data: 'id',
searchable: false,
orderable: false,
render: function (data, type, full, meta) {
return getValidationDisplayHtml(full, "PLATFORM_CREDENTIAL")
}
},
{
data: 'id',
searchable: false,
orderable: false,
render: function (data, type, full, meta) {
return getValidationDisplayHtml(full, "FIRMWARE")
}
return html;
}
];
},
{
// Note: DB column is create_time, while the
// JSON property / java property is createTime. Need to sort
// on the field createTime, but the column's
// date source is create_time.
data: 'create_time',
name: 'createTime',
searchable: false,
render: function (data, type, full, meta) {
return formatDateTime(full.createTime);
}
},
{
// TODO render a link to a device details page,
// passing the device.id
data: 'device.name'
},
{
data: 'id',
searchable: false,
orderable: false,
render: function (data, type, full, meta) {
return getValidationDisplayHtml(full, "ENDORSEMENT_CREDENTIAL")
}
},
{
data: 'id',
searchable: false,
orderable: false,
render: function (data, type, full, meta) {
return getValidationDisplayHtml(full, "PLATFORM_CREDENTIAL")
}
},
{
data: 'id',
searchable: false,
orderable: false,
render: function (data, type, full, meta) {
return getValidationDisplayHtml(full, "FIRMWARE")
}
}
];
//Set data tables
var dataTable = setDataTables("#reportTable", url, columns);
@ -127,69 +127,82 @@
});
/**
* Gets HTML to display (icon tag) for the specified validation type.
* If a validation for the requested type is not found, an empty
* string is returned (and no icon will be displayed).
*/
* Gets HTML to display (icon tag) for the specified validation type.
* If a validation for the requested type is not found, an empty
* string is returned (and no icon will be displayed).
*/
function getValidationDisplayHtml(full, validation_type) {
var html = '';
// loop through all the validations, looking for the one matching
// the validation_type.
for (var i = 0; i < full.validations.length; i++) {
var curValidation = full.validations[i];
var curResult = curValidation.result;
var curMessage = curValidation.message;
var html = '';
// loop through all the validations, looking for the one matching
// the validation_type.
for (var i = 0; i < full.validations.length; i++) {
var curValidation = full.validations[i];
var curResult = curValidation.result;
var curMessage = curValidation.message;
if (curValidation.validationType === validation_type) {
var unknownStatus = '<img class="icon" src="${unknownIcon}" title="${unknownText}"/>';
if (curValidation.validationType === validation_type) {
var unknownStatus = '<img class="icon" src="${unknownIcon}" title="${unknownText}"/>';
// display appropriate icon based on result
if (curResult) {
// display appropriate icon based on result
if (curResult) {
// if this validation is associated with a certificate,
// link to the details page
if (curValidation.certificatesUsed.length > 0) {
var certType = '';
switch (validation_type) {
case "PLATFORM_CREDENTIAL":
case "PLATFORM_CREDENTIAL_ATTRIBUTES":
certType = "platform";
break;
case "ENDORSEMENT_CREDENTIAL":
certType = "endorsement";
break;
}
// if this validation is associated with a certificate,
// link to the details page
if (curValidation.certificatesUsed.length > 0) {
var certType = '';
switch (validation_type) {
case "PLATFORM_CREDENTIAL":
case "PLATFORM_CREDENTIAL_ATTRIBUTES":
certType = "platform";
break;
case "ENDORSEMENT_CREDENTIAL":
certType = "endorsement";
break;
}
html += '<a href="${portal}/certificate-details?id='
+ curValidation.certificatesUsed[0].id
+ '&type=' + certType + '">';
}
switch (validation_type) {
case "PLATFORM_CREDENTIAL":
case "PLATFORM_CREDENTIAL_ATTRIBUTES":
case "ENDORSEMENT_CREDENTIAL":
html += '<a href="${portal}/certificate-details?id='
+ curValidation.certificatesUsed[0].id
+ '&type=' + certType + '">';
break;
}
}
switch (curResult) {
case "PASS":
html += '<img src="${passIcon}" title="' + curMessage + '"/>';
break;
case "FAIL":
html += '<img src="${failIcon}" title="' + curMessage + '"/>';
break;
case "ERROR":
html += '<img src="${errorIcon}" title="' + curMessage + '"/>';
break;
default:
html += unknownStatus;
break;
}
switch (validation_type) {
case "FIRMWARE":
html += '<a href="${portal}/rim-details?id='
+ curValidation.rimId + '">';
break;
}
// add closing tag for href tag if needed.
if (curValidation.certificatesUsed.length > 0) {
html += '</a>';
}
} else {
html += unknownStatus;
}
}
}
return html;
switch (curResult) {
case "PASS":
html += '<img src="${passIcon}" title="' + curMessage + '"/>';
break;
case "FAIL":
html += '<img src="${failIcon}" title="' + curMessage + '"/>';
break;
case "ERROR":
html += '<img src="${errorIcon}" title="' + curMessage + '"/>';
break;
default:
html += unknownStatus;
break;
}
// add closing tag for href tag if needed.
if (curValidation.certificatesUsed.length > 0 || curValidation.rimId !== "") {
html += '</a>';
}
} else {
html += unknownStatus;
}
}
}
return html;
}
</script>
</jsp:body>

View File

@ -11,6 +11,7 @@ import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import java.util.Collections;
import java.util.List;
import java.util.ArrayList;
/**
* Stores results of a single element of the supply chain validation process.
@ -56,6 +57,9 @@ public class SupplyChainValidation extends ArchivableEntity {
@Column(length = MAX_MESSAGE_LENGTH)
private final String message;
@Column
private String rimId;
/**
* Default constructor necessary for Hibernate.
*/
@ -64,6 +68,7 @@ public class SupplyChainValidation extends ArchivableEntity {
this.validationResult = AppraisalStatus.Status.ERROR;
this.certificatesUsed = Collections.emptyList();
this.message = null;
this.rimId = "";
}
/**
@ -76,7 +81,7 @@ public class SupplyChainValidation extends ArchivableEntity {
*/
public SupplyChainValidation(final ValidationType validationType,
final AppraisalStatus.Status validationResult,
final List<Certificate> certificatesUsed,
final List<ArchivableEntity> certificatesUsed,
final String message) {
Preconditions.checkArgument(
validationType != null,
@ -90,7 +95,17 @@ public class SupplyChainValidation extends ArchivableEntity {
this.validationType = validationType;
this.validationResult = validationResult;
this.certificatesUsed = certificatesUsed;
this.certificatesUsed = new ArrayList<>();
this.rimId = "";
for (ArchivableEntity ae : certificatesUsed) {
if (ae instanceof ReferenceManifest) {
this.rimId = ae.getId().toString();
break;
} else {
this.certificatesUsed.add((Certificate) ae);
}
}
this.message = message;
}
@ -121,4 +136,11 @@ public class SupplyChainValidation extends ArchivableEntity {
public String getMessage() {
return message;
}
/**
* @return Getter for the Rim ID.
*/
public String getRimId() {
return rimId;
}
}

View File

@ -186,7 +186,6 @@ public class SupplyChainValidationSummary extends ArchivableEntity {
"Cannot construct a SupplyChainValidationSummary with a null validations list"
);
this.device = device;
AppraisalStatus status = calculateValidationResult(validations);
this.overallValidationResult = status.getAppStatus();
@ -243,7 +242,7 @@ public class SupplyChainValidationSummary extends ArchivableEntity {
validation.getMessage());
case FAIL:
hasAnyFailures = true;
failureMsg.append(validation.getMessage());
failureMsg.append(String.format("%s%n", validation.getValidationType()));
break;
default:
break;

View File

@ -55,8 +55,8 @@ import java.util.stream.Collectors;
import static hirs.data.persist.AppraisalStatus.Status.ERROR;
import static hirs.data.persist.AppraisalStatus.Status.FAIL;
import static hirs.data.persist.AppraisalStatus.Status.PASS;
import hirs.data.persist.ArchivableEntity;
import hirs.data.persist.SupplyChainValidation;
import hirs.data.persist.certificate.Certificate;
import hirs.data.persist.certificate.attributes.V2.ComponentIdentifierV2;
import java.util.Collections;
import java.util.Comparator;
@ -592,7 +592,7 @@ public final class SupplyChainCredentialValidator implements CredentialValidator
});
String ciSerial;
List<Certificate> certificateList = null;
List<ArchivableEntity> certificateList = null;
SupplyChainValidation scv = null;
resultMessage.append("There are errors with Delta "
+ "Component Statuses components:\n");

View File

@ -22,7 +22,7 @@ import java.util.List;
public class SupplyChainValidationSummaryTest extends SpringPersistenceTest {
private Device device;
private DeviceGroup deviceGroup;
private List<Certificate> certificates;
private List<ArchivableEntity> certificates;
/**
* Create a session factory to use for persistence testing and persist some certificates
@ -34,8 +34,8 @@ public class SupplyChainValidationSummaryTest extends SpringPersistenceTest {
public void setup() throws Exception {
certificates = CertificateTest.getAllTestCertificates();
DBCertificateManager certMan = new DBCertificateManager(sessionFactory);
for (Certificate cert : certificates) {
certMan.save(cert);
for (ArchivableEntity cert : certificates) {
certMan.save((Certificate) cert);
}
deviceGroup = new DeviceGroup("TestDeviceGroup", "TestDeviceGroupDescription");
@ -54,8 +54,8 @@ public class SupplyChainValidationSummaryTest extends SpringPersistenceTest {
@AfterClass
public void teardown() {
DBCertificateManager certManager = new DBCertificateManager(sessionFactory);
for (Certificate cert : certificates) {
certManager.deleteCertificate(cert);
for (ArchivableEntity cert : certificates) {
certManager.deleteCertificate((Certificate) cert);
}
}
@ -233,7 +233,7 @@ public class SupplyChainValidationSummaryTest extends SpringPersistenceTest {
SupplyChainValidationSummary.class, sessionFactory
);
List<Certificate> singleCert = certificates.subList(0, 1);
List<ArchivableEntity> singleCert = certificates.subList(0, 1);
SupplyChainValidationSummary smallSummary = getTestSummary(
1,
@ -304,7 +304,7 @@ public class SupplyChainValidationSummaryTest extends SpringPersistenceTest {
private SupplyChainValidationSummary getTestSummary(
final int numberOfValidations,
final int numFail,
final List<Certificate> certificates
final List<ArchivableEntity> certificates
) {
SupplyChainValidation.ValidationType[] validationTypes =
SupplyChainValidation.ValidationType.values();

View File

@ -6,7 +6,6 @@ import hirs.data.persist.certificate.CertificateTest;
import java.io.IOException;
import java.util.List;
import hirs.data.persist.certificate.Certificate;
/**
* Simple tests for the {@link SupplyChainValidation} class. Tests for the persistence of this
@ -106,7 +105,7 @@ public class SupplyChainValidationTest {
public static SupplyChainValidation getTestSupplyChainValidation(
final SupplyChainValidation.ValidationType type,
final AppraisalStatus.Status result,
final List<Certificate> certificates) {
final List<ArchivableEntity> certificates) {
return new SupplyChainValidation(
type,
result,

View File

@ -1,5 +1,6 @@
package hirs.data.persist.certificate;
import hirs.data.persist.ArchivableEntity;
import hirs.data.persist.certificate.Certificate.CertificateType;
import org.bouncycastle.cert.X509AttributeCertificateHolder;
import org.testng.Assert;
@ -524,7 +525,7 @@ public class CertificateTest {
* @return the newly-constructed Certificate
* @throws IOException if there is a problem constructing the test certificate
*/
public static <T extends Certificate> Certificate getTestCertificate(
public static <T extends ArchivableEntity> Certificate getTestCertificate(
final Class<T> certificateClass, final String filename)
throws IOException {
return getTestCertificate(certificateClass, filename, null, null);
@ -541,7 +542,7 @@ public class CertificateTest {
* @return the newly-constructed Certificate
* @throws IOException if there is a problem constructing the test certificate
*/
public static <T extends Certificate> Certificate getTestCertificate(
public static <T extends ArchivableEntity> Certificate getTestCertificate(
final Class<T> certificateClass, final String filename,
final EndorsementCredential endorsementCredential,
final Set<PlatformCredential> platformCredentials)
@ -579,7 +580,7 @@ public class CertificateTest {
* @return a list of all test certificates
* @throws IOException if there is a problem deserializing certificates
*/
public static List<Certificate> getAllTestCertificates() throws IOException {
public static List<ArchivableEntity> getAllTestCertificates() throws IOException {
return Arrays.asList(
getTestCertificate(CertificateAuthorityCredential.class, FAKE_SGI_INT_CA_FILE),
getTestCertificate(CertificateAuthorityCredential.class, FAKE_INTEL_INT_CA_FILE),

View File

@ -2,6 +2,7 @@ package hirs.validation;
import hirs.client.collector.DeviceInfoCollector;
import hirs.data.persist.AppraisalStatus;
import hirs.data.persist.ArchivableEntity;
import hirs.data.persist.info.ComponentInfo;
import hirs.data.persist.DeviceInfoReport;
import hirs.data.persist.info.FirmwareInfo;
@ -2097,7 +2098,7 @@ public class SupplyChainCredentialValidatorTest {
when(delta2.getComponentIdentifiers()).thenReturn(delta2List);
Map<PlatformCredential, SupplyChainValidation> chainCredentials = new HashMap<>(0);
List<Certificate> certsUsed = new ArrayList<>();
List<ArchivableEntity> certsUsed = new ArrayList<>();
certsUsed.add(base);
chainCredentials.put(base, new SupplyChainValidation(
SupplyChainValidation.ValidationType.PLATFORM_CREDENTIAL,
@ -2202,7 +2203,7 @@ public class SupplyChainCredentialValidatorTest {
when(delta1.getComponentIdentifiers()).thenReturn(delta1List);
Map<PlatformCredential, SupplyChainValidation> chainCredentials = new HashMap<>(0);
List<Certificate> certsUsed = new ArrayList<>();
List<ArchivableEntity> certsUsed = new ArrayList<>();
certsUsed.add(base);
chainCredentials.put(base, new SupplyChainValidation(
SupplyChainValidation.ValidationType.PLATFORM_CREDENTIAL,