Skip to content

Commit

Permalink
[#33] IMA baselines can match measurements based solely on hashes
Browse files Browse the repository at this point in the history
ImaAcceptableRecordBaseline and its subclasses have been updated to include
a containsHashes method to be able to match IMA measurement records
based solely on their hashes.  Supporting classes have been
updated or created as necessary.

Additionally, the set of path equivalencies as specified in the IMA
policy have been updated to include additional entries.

Closes #33.
  • Loading branch information
apldev1 committed Oct 31, 2018
1 parent f192ce5 commit 0d412f5
Show file tree
Hide file tree
Showing 17 changed files with 769 additions and 95 deletions.
8 changes: 5 additions & 3 deletions HIRS_Utils/src/main/java/hirs/data/persist/IMAPolicy.java
Original file line number Diff line number Diff line change
Expand Up @@ -457,9 +457,11 @@ public final synchronized Multimap<String, String> getPathEquivalences() {
Multimap<String, String> equivalentPaths = HashMultimap.create();

// define equivalences
equivalentPaths.put("/bin/", "/usr/bin/");
equivalentPaths.put("/lib/", "/usr/lib/");
equivalentPaths.put("/lib64/", "/usr/lib64/");
equivalentPaths.put("/bin/", "/usr/bin/");
equivalentPaths.put("/lib/", "/usr/lib/");
equivalentPaths.put("/lib64/", "/usr/lib64/");
equivalentPaths.put("/usr/bin/", "/usr/sbin/");
equivalentPaths.put("/sbin/", "/usr/sbin/");

// populate inverse relationships
Multimap<String, String> bidirectionalEquivalences = HashMultimap.create();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package hirs.data.persist;

import com.fasterxml.jackson.annotation.JsonIgnore;
import hirs.ima.matching.BatchImaMatchStatus;
import hirs.persist.ImaBaselineRecordManager;

import javax.persistence.Entity;
Expand All @@ -14,6 +15,7 @@
*/
@Entity
public abstract class ImaAcceptableRecordBaseline extends ImaBaseline<IMABaselineRecord> {

/**
* Creates a new ImaAcceptableRecordBaseline with the given name.
*
Expand All @@ -29,6 +31,34 @@ public ImaAcceptableRecordBaseline(final String name) {
protected ImaAcceptableRecordBaseline() {
}

/**
* Similar to contains, but only considers the hash value and does not consider
* the path as relevant to matching at all.
*
* Each type of baseline specifies its own
* 'contains' algorithm for deciding whether the given measurements are
* considered as matches, mismatches, or unknowns to the baseline. The 'contains' method
* of ImaAcceptableRecordBaselines that is normally used to judge measurement records
* against baseline records considers both paths and hashes; this method offers an
* additional mechanism for finding matching baseline records solely based
* on matching hash values.
*
* @param records
* measurement records to find in this baseline
* @param recordManager
* an ImaBaselineRecordManager that can be used to retrieve persisted records
* @param imaPolicy
* the IMA policy to use while determining if a baseline contains the given records
*
* @return batch match status for the measurement records, according only to hashes
*/
@JsonIgnore
public abstract BatchImaMatchStatus<IMABaselineRecord> containsHashes(
Collection<IMAMeasurementRecord> records,
ImaBaselineRecordManager recordManager,
IMAPolicy imaPolicy
);

/**
* Returns an unmodifiable set of IMA baseline records found in the IMA
* baseline. The returned set only contains the baseline records from this
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ protected ImaBaseline() {
* @param imaPolicy
* the IMA policy to use while determining if a baseline contains the given records
*
* @return search status for the measurement record
* @return batch match status for the measurement records
*/
public abstract BatchImaMatchStatus<T> contains(
Collection<IMAMeasurementRecord> records,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,16 @@
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.google.common.base.Preconditions;
import hirs.ima.matching.BatchImaMatchStatus;
import hirs.ima.matching.IMAMatchStatus;
import hirs.ima.matching.ImaBlacklistRecordMatcher;
import hirs.persist.ImaBaselineRecordManager;

import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.OneToMany;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;

/**
Expand Down Expand Up @@ -53,22 +50,10 @@ public BatchImaMatchStatus<ImaBlacklistRecord> contains(
final Collection<IMAMeasurementRecord> records,
final ImaBaselineRecordManager recordManager,
final IMAPolicy imaPolicy) {
if (records == null) {
throw new IllegalArgumentException("Records cannot be null");
}

if (imaPolicy == null) {
throw new IllegalArgumentException("IMA policy cannot be null");
}

ImaBlacklistRecordMatcher recordMatcher =
new ImaBlacklistRecordMatcher(imaBlacklistRecords, imaPolicy, this);
List<IMAMatchStatus<ImaBlacklistRecord>> matchStatuses = new ArrayList<>();
for (IMAMeasurementRecord record : records) {
matchStatuses.add(recordMatcher.contains(record));
}

return new BatchImaMatchStatus<>(matchStatuses);
Preconditions.checkArgument(records != null, "Records cannot be null");
Preconditions.checkArgument(imaPolicy != null, "IMA policy cannot be null");
return new ImaBlacklistRecordMatcher(imaBlacklistRecords, imaPolicy, this)
.batchMatch(records);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,15 @@

import com.fasterxml.jackson.annotation.JsonIgnore;

import com.google.common.base.Preconditions;
import hirs.ima.matching.BatchImaMatchStatus;
import hirs.ima.matching.IMAMatchStatus;
import hirs.ima.matching.ImaIgnoreSetRecordMatcher;
import hirs.persist.ImaBaselineRecordManager;
import hirs.utils.RegexFilePathMatcher;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import javax.persistence.Access;
Expand Down Expand Up @@ -157,21 +155,10 @@ public final BatchImaMatchStatus<ImaIgnoreSetRecord> contains(
final ImaBaselineRecordManager recordManager,
final IMAPolicy imaPolicy
) {
if (records == null) {
throw new IllegalArgumentException("Records cannot be null");
}

if (imaPolicy == null) {
throw new IllegalArgumentException("IMA policy cannot be null");
}

ImaIgnoreSetRecordMatcher recordMatcher =
new ImaIgnoreSetRecordMatcher(imaIgnoreSetRecords, imaPolicy, this);
List<IMAMatchStatus<ImaIgnoreSetRecord>> matchStatuses = new ArrayList<>();
for (IMAMeasurementRecord record : records) {
matchStatuses.add(recordMatcher.contains(record));
}
return new BatchImaMatchStatus<>(matchStatuses);
Preconditions.checkArgument(records != null, "Records cannot be null");
Preconditions.checkArgument(imaPolicy != null, "IMA policy cannot be null");
return new ImaIgnoreSetRecordMatcher(imaIgnoreSetRecords, imaPolicy, this)
.batchMatch(records);
}

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -1,18 +1,20 @@
package hirs.data.persist;

import com.google.common.base.Preconditions;
import hirs.ima.matching.BatchImaMatchStatus;
import hirs.ima.matching.IMAMatchStatus;
import hirs.ima.matching.ImaAcceptableRecordMatcher;
import hirs.ima.matching.ImaAcceptableHashRecordMatcher;
import hirs.ima.matching.ImaAcceptablePathAndHashRecordMatcher;
import hirs.ima.matching.ImaRecordMatcher;
import hirs.persist.ImaBaselineRecordManager;
import hirs.utils.Callback;
import org.hibernate.Criteria;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;

/**
* This class defines the basis of operation for a baseline that supports querying
Expand Down Expand Up @@ -50,13 +52,8 @@ public final BatchImaMatchStatus<IMABaselineRecord> contains(
final Collection<IMAMeasurementRecord> records,
final ImaBaselineRecordManager recordManager,
final IMAPolicy imaPolicy) {
if (records == null) {
throw new IllegalArgumentException("Records cannot be null");
}

if (recordManager == null) {
throw new IllegalArgumentException("ImaBaselineRecordManager cannot be null");
}
Preconditions.checkArgument(records != null, "records cannot be null");
Preconditions.checkArgument(recordManager != null, "record manager cannot be null");

final Collection<String> pathsToFind = new HashSet<>();
for (IMAMeasurementRecord record : records) {
Expand All @@ -79,14 +76,45 @@ public IMABaselineRecord call(final IMABaselineRecord baselineRecord) {
}
});

ImaAcceptableRecordMatcher recordMatcher =
new ImaAcceptableRecordMatcher(retrievedRecords, imaPolicy, this);
List<IMAMatchStatus<IMABaselineRecord>> matchStatuses = new ArrayList<>();
for (IMAMeasurementRecord record : records) {
matchStatuses.add(recordMatcher.contains(record));
}
return new ImaAcceptablePathAndHashRecordMatcher(retrievedRecords, imaPolicy, this)
.batchMatch(records);
}

/**
* Check membership of the given {@link IMAMeasurementRecord}s in this baseline.
*
* @param records the records to attempt to match
* @param recordManager the {@link ImaBaselineRecordManager} to query
* @param imaPolicy the IMA policy to use while determining if a baseline contains the records
*
* @return a collection of {@link IMAMatchStatus}es reflecting the results
*/
@Override
public final BatchImaMatchStatus<IMABaselineRecord> containsHashes(
final Collection<IMAMeasurementRecord> records,
final ImaBaselineRecordManager recordManager,
final IMAPolicy imaPolicy) {
Preconditions.checkArgument(records != null, "records cannot be null");
Preconditions.checkArgument(recordManager != null, "record manager cannot be null");

final Set<Digest> hashesToFind = records.stream()
.filter(Objects::nonNull)
.map(IMAMeasurementRecord::getHash)
.collect(Collectors.toSet());

Collection<IMABaselineRecord> retrievedRecords = recordManager.iterateOverBaselineRecords(
this, new Callback<IMABaselineRecord, IMABaselineRecord>() {
@Override
public IMABaselineRecord call(final IMABaselineRecord baselineRecord) {
if (hashesToFind.contains(baselineRecord.getHash())) {
return baselineRecord;
}
return null;
}
});

return new BatchImaMatchStatus<>(matchStatuses);
return new ImaAcceptableHashRecordMatcher(retrievedRecords, imaPolicy, this)
.batchMatch(records);
}

@Override
Expand Down
36 changes: 19 additions & 17 deletions HIRS_Utils/src/main/java/hirs/data/persist/SimpleImaBaseline.java
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
package hirs.data.persist;

import com.fasterxml.jackson.annotation.JsonIgnore;
import com.google.common.base.Preconditions;
import hirs.ima.matching.BatchImaMatchStatus;
import hirs.ima.matching.IMAMatchStatus;
import hirs.ima.matching.ImaAcceptableRecordMatcher;
import hirs.ima.matching.ImaAcceptableHashRecordMatcher;
import hirs.ima.matching.ImaAcceptablePathAndHashRecordMatcher;
import hirs.persist.ImaBaselineRecordManager;
import org.apache.logging.log4j.Logger;

Expand All @@ -15,12 +16,10 @@
import javax.persistence.FetchType;
import javax.persistence.OneToMany;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;

import static org.apache.logging.log4j.LogManager.getLogger;
Expand Down Expand Up @@ -175,21 +174,24 @@ public final BatchImaMatchStatus<IMABaselineRecord> contains(
final Collection<IMAMeasurementRecord> records,
final ImaBaselineRecordManager recordManager,
final IMAPolicy imaPolicy) {
if (records == null) {
throw new IllegalArgumentException("Records cannot be null");
}
Preconditions.checkArgument(records != null, "Records cannot be null");
Preconditions.checkArgument(imaPolicy != null, "IMA policy cannot be null");

if (imaPolicy == null) {
throw new IllegalArgumentException("IMA policy cannot be null");
}
return new ImaAcceptablePathAndHashRecordMatcher(imaRecords, imaPolicy, this)
.batchMatch(records);
}

ImaAcceptableRecordMatcher recordMatcher =
new ImaAcceptableRecordMatcher(imaRecords, imaPolicy, this);
List<IMAMatchStatus<IMABaselineRecord>> matchStatuses = new ArrayList<>();
for (IMAMeasurementRecord record : records) {
matchStatuses.add(recordMatcher.contains(record));
}
return new BatchImaMatchStatus<>(matchStatuses);

@Override
public BatchImaMatchStatus<IMABaselineRecord> containsHashes(
final Collection<IMAMeasurementRecord> records,
final ImaBaselineRecordManager recordManager,
final IMAPolicy imaPolicy) {
Preconditions.checkArgument(records != null, "Records cannot be null");
Preconditions.checkArgument(imaPolicy != null, "IMA policy cannot be null");

return new ImaAcceptableHashRecordMatcher(imaRecords, imaPolicy, this)
.batchMatch(records);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -307,4 +307,11 @@ public boolean equals(final Object o) {
public int hashCode() {
return Objects.hash(matchStatuses);
}

@Override
public String toString() {
return "BatchImaMatchStatus{"
+ "matchStatuses=" + matchStatuses
+ '}';
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package hirs.ima.matching;

import com.google.common.base.Preconditions;
import hirs.data.persist.IMABaselineRecord;
import hirs.data.persist.IMAMeasurementRecord;
import hirs.data.persist.IMAPolicy;
import hirs.data.persist.ImaBaseline;
import hirs.data.persist.ReportMatchStatus;

import java.util.Collection;
import java.util.Set;

/**
* This class extends the base matching functionality of {@link ImaRecordMatcher} to
* compare {@link IMAMeasurementRecord}s against a collection of {@link IMABaselineRecord}s
* based solely on their hashes.
*/
public class ImaAcceptableHashRecordMatcher extends ImaRecordMatcher<IMABaselineRecord> {
/**
* Construct a new ImaAcceptablePathAndHashRecordMatcher.
*
* @param records the baseline records to use for matching
* @param imaPolicy the IMA policy to reference during matching; its partial path and path
* equivalence settings influence matching behavior
* @param imaBaseline the IMA baseline these records were sourced from; this is only used to
*/
public ImaAcceptableHashRecordMatcher(
final Collection<IMABaselineRecord> records,
final IMAPolicy imaPolicy,
final ImaBaseline imaBaseline) {
super(records, imaPolicy, imaBaseline);
}

/**
* Returns an IMAMatchStatus indicating whether the given {@link IMAMeasurementRecord} is
* contained within the originally provided {@link IMABaselineRecord}s.
*
* @param record the record to look up
* @return an IMAMatchStatus indicating whether the record is a match or unknown to
* the given baseline records
*/
@Override
public IMAMatchStatus<IMABaselineRecord> contains(final IMAMeasurementRecord record) {
Preconditions.checkArgument(record != null, "Cannot match on null record.");

final Set<IMABaselineRecord> matchingRecords = getRelatedBaselineRecordsByHash(record);

if (matchingRecords.isEmpty()) {
return new IMAMatchStatus<>(record, ReportMatchStatus.UNKNOWN, getImaBaseline());
}

return new IMAMatchStatus<>(
record,
ReportMatchStatus.MATCH,
matchingRecords,
getImaBaseline()
);
}
}
Loading

0 comments on commit 0d412f5

Please sign in to comment.