diff --git a/HIRS_Utils/src/main/java/hirs/data/persist/IMAPolicy.java b/HIRS_Utils/src/main/java/hirs/data/persist/IMAPolicy.java index 35f71d2d..c46cb290 100644 --- a/HIRS_Utils/src/main/java/hirs/data/persist/IMAPolicy.java +++ b/HIRS_Utils/src/main/java/hirs/data/persist/IMAPolicy.java @@ -457,9 +457,11 @@ public class IMAPolicy extends Policy implements HasBaselines { 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(); diff --git a/HIRS_Utils/src/main/java/hirs/data/persist/ImaAcceptableRecordBaseline.java b/HIRS_Utils/src/main/java/hirs/data/persist/ImaAcceptableRecordBaseline.java index a76411bc..920efbb2 100644 --- a/HIRS_Utils/src/main/java/hirs/data/persist/ImaAcceptableRecordBaseline.java +++ b/HIRS_Utils/src/main/java/hirs/data/persist/ImaAcceptableRecordBaseline.java @@ -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; @@ -14,6 +15,7 @@ import java.util.Set; */ @Entity public abstract class ImaAcceptableRecordBaseline extends ImaBaseline<IMABaselineRecord> { + /** * Creates a new ImaAcceptableRecordBaseline with the given name. * @@ -29,6 +31,34 @@ public abstract class ImaAcceptableRecordBaseline extends ImaBaseline<IMABaselin 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 diff --git a/HIRS_Utils/src/main/java/hirs/data/persist/ImaBaseline.java b/HIRS_Utils/src/main/java/hirs/data/persist/ImaBaseline.java index dd25cced..30b0ee8a 100644 --- a/HIRS_Utils/src/main/java/hirs/data/persist/ImaBaseline.java +++ b/HIRS_Utils/src/main/java/hirs/data/persist/ImaBaseline.java @@ -75,7 +75,7 @@ public abstract class ImaBaseline<T extends AbstractImaBaselineRecord> extends B * @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, diff --git a/HIRS_Utils/src/main/java/hirs/data/persist/ImaBlacklistBaseline.java b/HIRS_Utils/src/main/java/hirs/data/persist/ImaBlacklistBaseline.java index ec6e289b..0948404d 100644 --- a/HIRS_Utils/src/main/java/hirs/data/persist/ImaBlacklistBaseline.java +++ b/HIRS_Utils/src/main/java/hirs/data/persist/ImaBlacklistBaseline.java @@ -3,7 +3,6 @@ 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.ImaBlacklistRecordMatcher; import hirs.persist.ImaBaselineRecordManager; @@ -11,11 +10,9 @@ 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; /** @@ -53,22 +50,10 @@ public class ImaBlacklistBaseline extends ImaBaseline<ImaBlacklistRecord> { 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); } /** diff --git a/HIRS_Utils/src/main/java/hirs/data/persist/ImaIgnoreSetBaseline.java b/HIRS_Utils/src/main/java/hirs/data/persist/ImaIgnoreSetBaseline.java index f72fe9d8..b2b20e16 100644 --- a/HIRS_Utils/src/main/java/hirs/data/persist/ImaIgnoreSetBaseline.java +++ b/HIRS_Utils/src/main/java/hirs/data/persist/ImaIgnoreSetBaseline.java @@ -7,17 +7,15 @@ 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.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; @@ -157,21 +155,10 @@ public class ImaIgnoreSetBaseline extends ImaBaseline<ImaIgnoreSetRecord> { 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); } /** diff --git a/HIRS_Utils/src/main/java/hirs/data/persist/QueryableRecordImaBaseline.java b/HIRS_Utils/src/main/java/hirs/data/persist/QueryableRecordImaBaseline.java index 3cd0f613..2ad3ea45 100644 --- a/HIRS_Utils/src/main/java/hirs/data/persist/QueryableRecordImaBaseline.java +++ b/HIRS_Utils/src/main/java/hirs/data/persist/QueryableRecordImaBaseline.java @@ -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 @@ -50,13 +52,8 @@ public abstract class QueryableRecordImaBaseline extends ImaAcceptableRecordBase 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) { @@ -79,14 +76,45 @@ public abstract class QueryableRecordImaBaseline extends ImaAcceptableRecordBase } }); - 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); + } - return new BatchImaMatchStatus<>(matchStatuses); + /** + * 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 ImaAcceptableHashRecordMatcher(retrievedRecords, imaPolicy, this) + .batchMatch(records); } @Override diff --git a/HIRS_Utils/src/main/java/hirs/data/persist/SimpleImaBaseline.java b/HIRS_Utils/src/main/java/hirs/data/persist/SimpleImaBaseline.java index f3b355f4..137484bb 100644 --- a/HIRS_Utils/src/main/java/hirs/data/persist/SimpleImaBaseline.java +++ b/HIRS_Utils/src/main/java/hirs/data/persist/SimpleImaBaseline.java @@ -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; @@ -15,12 +16,10 @@ import javax.persistence.Entity; 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; @@ -175,21 +174,24 @@ public class SimpleImaBaseline extends ImaAcceptableRecordBaseline { 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 diff --git a/HIRS_Utils/src/main/java/hirs/ima/matching/BatchImaMatchStatus.java b/HIRS_Utils/src/main/java/hirs/ima/matching/BatchImaMatchStatus.java index e985822c..db75e367 100644 --- a/HIRS_Utils/src/main/java/hirs/ima/matching/BatchImaMatchStatus.java +++ b/HIRS_Utils/src/main/java/hirs/ima/matching/BatchImaMatchStatus.java @@ -307,4 +307,11 @@ public class BatchImaMatchStatus<T extends AbstractImaBaselineRecord> { public int hashCode() { return Objects.hash(matchStatuses); } + + @Override + public String toString() { + return "BatchImaMatchStatus{" + + "matchStatuses=" + matchStatuses + + '}'; + } } diff --git a/HIRS_Utils/src/main/java/hirs/ima/matching/ImaAcceptableHashRecordMatcher.java b/HIRS_Utils/src/main/java/hirs/ima/matching/ImaAcceptableHashRecordMatcher.java new file mode 100644 index 00000000..ae3c3ede --- /dev/null +++ b/HIRS_Utils/src/main/java/hirs/ima/matching/ImaAcceptableHashRecordMatcher.java @@ -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() + ); + } +} diff --git a/HIRS_Utils/src/main/java/hirs/ima/matching/ImaAcceptableRecordMatcher.java b/HIRS_Utils/src/main/java/hirs/ima/matching/ImaAcceptablePathAndHashRecordMatcher.java similarity index 89% rename from HIRS_Utils/src/main/java/hirs/ima/matching/ImaAcceptableRecordMatcher.java rename to HIRS_Utils/src/main/java/hirs/ima/matching/ImaAcceptablePathAndHashRecordMatcher.java index 768a0765..5b91adf6 100644 --- a/HIRS_Utils/src/main/java/hirs/ima/matching/ImaAcceptableRecordMatcher.java +++ b/HIRS_Utils/src/main/java/hirs/ima/matching/ImaAcceptablePathAndHashRecordMatcher.java @@ -1,5 +1,6 @@ package hirs.ima.matching; +import com.google.common.base.Preconditions; import hirs.data.persist.DigestComparisonResultType; import hirs.data.persist.IMABaselineRecord; import hirs.data.persist.IMAMeasurementRecord; @@ -16,20 +17,21 @@ import static org.apache.logging.log4j.LogManager.getLogger; /** * This class extends the base matching functionality of {@link ImaRecordMatcher} to - * compare {@link IMAMeasurementRecord}s against a collection of {@link IMABaselineRecord}s. + * compare {@link IMAMeasurementRecord}s against a collection of {@link IMABaselineRecord}s + * based on both their paths and hashes. */ -public class ImaAcceptableRecordMatcher extends ImaRecordMatcher<IMABaselineRecord> { - private static final Logger LOGGER = getLogger(ImaAcceptableRecordMatcher.class); +public class ImaAcceptablePathAndHashRecordMatcher extends ImaRecordMatcher<IMABaselineRecord> { + private static final Logger LOGGER = getLogger(ImaAcceptablePathAndHashRecordMatcher.class); /** - * Construct a new ImaAcceptableRecordMatcher. + * 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 ImaAcceptableRecordMatcher( + public ImaAcceptablePathAndHashRecordMatcher( final Collection<IMABaselineRecord> records, final IMAPolicy imaPolicy, final ImaBaseline imaBaseline) { @@ -46,9 +48,7 @@ public class ImaAcceptableRecordMatcher extends ImaRecordMatcher<IMABaselineReco */ @Override public IMAMatchStatus<IMABaselineRecord> contains(final IMAMeasurementRecord record) { - if (record == null) { - throw new IllegalArgumentException("Cannot match on null record."); - } + Preconditions.checkArgument(record != null, "Cannot match on null record."); final Set<IMABaselineRecord> matchRecords = new HashSet<>(); final Set<IMABaselineRecord> mismatchRecords = new HashSet<>(); diff --git a/HIRS_Utils/src/main/java/hirs/ima/matching/ImaRecordMatcher.java b/HIRS_Utils/src/main/java/hirs/ima/matching/ImaRecordMatcher.java index 700a6819..2e38fc4a 100644 --- a/HIRS_Utils/src/main/java/hirs/ima/matching/ImaRecordMatcher.java +++ b/HIRS_Utils/src/main/java/hirs/ima/matching/ImaRecordMatcher.java @@ -9,8 +9,10 @@ import hirs.data.persist.IMAPolicy; import hirs.data.persist.AbstractImaBaselineRecord; import hirs.data.persist.ImaBaseline; +import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; +import java.util.List; import java.util.Set; /** @@ -67,6 +69,22 @@ public abstract class ImaRecordMatcher<T extends AbstractImaBaselineRecord> { */ public abstract IMAMatchStatus<T> contains(IMAMeasurementRecord record); + /** + * Given a collection of measurement records, populate and return a BatchImaMatchStatus + * instance containing the match results according to this ImaRecordMatcher's matching + * behavior and the given IMA policy, baseline, and baseline records. + * + * @param records the measurement records to match to baseline records + * @return a BatchImaMatchStatus containing the match status of all the given records + */ + public BatchImaMatchStatus<T> batchMatch(final Collection<IMAMeasurementRecord> records) { + List<IMAMatchStatus<T>> matchStatuses = new ArrayList<>(); + for (IMAMeasurementRecord record : records) { + matchStatuses.add(contains(record)); + } + return new BatchImaMatchStatus<>(matchStatuses); + } + /** * Gets all IMA baseline records that are related to the given IMA measurement record * as determined by path similarity or equivalency. This method respects the IMA policy diff --git a/HIRS_Utils/src/test/java/hirs/data/persist/BroadRepoImaBaselineTest.java b/HIRS_Utils/src/test/java/hirs/data/persist/BroadRepoImaBaselineTest.java index e89d68b2..a7221129 100644 --- a/HIRS_Utils/src/test/java/hirs/data/persist/BroadRepoImaBaselineTest.java +++ b/HIRS_Utils/src/test/java/hirs/data/persist/BroadRepoImaBaselineTest.java @@ -5,6 +5,7 @@ import java.util.Collections; import java.util.HashSet; import java.util.Set; +import hirs.ima.matching.BatchImaMatchStatus; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import hirs.ima.matching.IMAMatchStatus; @@ -249,6 +250,122 @@ public class BroadRepoImaBaselineTest extends SpringPersistenceTest { ); } + /** + * Test that ensures a BroadRepoImaBaseline can correctly determine if + * it contains any matching baseline records solely based upon a given measurement + * record's hash. + * + * @throws UnsupportedEncodingException + * if an error is encountered while getting the test digest + */ + @Test + public final void containsHashes() throws UnsupportedEncodingException { + BroadRepoImaBaseline testBaseline = new BroadRepoImaBaseline(BASELINE_NAME); + Repository testRepo = new TestRepository("Test Repository", 0); + DBRepositoryManager repoManager = new DBRepositoryManager(sessionFactory); + testRepo = repoManager.saveRepository(testRepo); + RepoPackage testRepoPackage = + new RPMRepoPackage(NAME, VERSION1, RELEASE1, ARCHITECTURE, testRepo); + Set<IMABaselineRecord> imaRecords = new HashSet<>(); + imaRecords.add(SimpleImaBaselineTest.createTestIMARecord(FILEPATH1)); + testRepoPackage.setAllMeasurements(imaRecords, RepoPackageTest.getTestDigest()); + repoManager.saveRepoPackage(testRepoPackage); + Set<Repository<?>> originalRepositories = new HashSet<>(); + originalRepositories.add(testRepo); + testBaseline.setRepositories(originalRepositories); + testBaseline.update(repoManager); + + DBBaselineManager baselineManager = new DBBaselineManager(sessionFactory); + BroadRepoImaBaseline savedBaseline = + (BroadRepoImaBaseline) baselineManager.save(testBaseline); + + IMABaselineRecord baselineRecord = SimpleImaBaselineTest.createTestIMARecord(FILEPATH1); + IMAMeasurementRecord measurementRecord = new IMAMeasurementRecord( + baselineRecord.getPath(), + baselineRecord.getHash() + ); + Assert.assertEquals( + savedBaseline.containsHashes( + Collections.singletonList(measurementRecord), + new DbImaBaselineRecordManager(sessionFactory), + SimpleImaBaselineTest.getTestImaPolicy(false) + + ).getIMAMatchStatuses(measurementRecord), + Collections.singleton( + new IMAMatchStatus<>( + measurementRecord, ReportMatchStatus.MATCH, baselineRecord, baseline + ) + ) + ); + + measurementRecord = new IMAMeasurementRecord( + "/some/other/file", + baselineRecord.getHash() + ); + Assert.assertEquals( + savedBaseline.containsHashes( + Collections.singletonList(measurementRecord), + new DbImaBaselineRecordManager(sessionFactory), + SimpleImaBaselineTest.getTestImaPolicy(false) + + ).getIMAMatchStatuses(measurementRecord), + Collections.singleton( + new IMAMatchStatus<>( + measurementRecord, ReportMatchStatus.MATCH, baselineRecord, baseline + ) + ) + ); + } + + /** + * Test that ensures a BroadRepoImaBaseline can correctly determine that + * it does not contain any matching baseline records solely based upon a given measurement + * record's hash. + * + * @throws UnsupportedEncodingException + * if an error is encountered while getting the test digest + */ + @Test + public final void containsHashesWithNoMatches() throws UnsupportedEncodingException { + BroadRepoImaBaseline testBaseline = new BroadRepoImaBaseline(BASELINE_NAME); + Repository testRepo = new TestRepository("Test Repository", 0); + DBRepositoryManager repoManager = new DBRepositoryManager(sessionFactory); + testRepo = repoManager.saveRepository(testRepo); + RepoPackage testRepoPackage = + new RPMRepoPackage(NAME, VERSION1, RELEASE1, ARCHITECTURE, testRepo); + Set<IMABaselineRecord> imaRecords = new HashSet<>(); + imaRecords.add(SimpleImaBaselineTest.createTestIMARecord(FILEPATH1)); + testRepoPackage.setAllMeasurements(imaRecords, RepoPackageTest.getTestDigest()); + repoManager.saveRepoPackage(testRepoPackage); + Set<Repository<?>> originalRepositories = new HashSet<>(); + originalRepositories.add(testRepo); + testBaseline.setRepositories(originalRepositories); + testBaseline.update(repoManager); + + DBBaselineManager baselineManager = new DBBaselineManager(sessionFactory); + BroadRepoImaBaseline savedBaseline = + (BroadRepoImaBaseline) baselineManager.save(testBaseline); + + IMABaselineRecord baselineRecord = SimpleImaBaselineTest.createTestIMARecord(FILEPATH1); + IMAMeasurementRecord measurementRecord = new IMAMeasurementRecord( + baselineRecord.getPath(), + SimpleImaBaselineTest.getDigest("0d5f3c2f7f3003d2e4baddc46ed4763a4954f648") + ); + Assert.assertEquals( + savedBaseline.containsHashes( + Collections.singletonList(measurementRecord), + new DbImaBaselineRecordManager(sessionFactory), + SimpleImaBaselineTest.getTestImaPolicy(false) + + ), + new BatchImaMatchStatus<>(Collections.singleton(new IMAMatchStatus<>( + measurementRecord, + ReportMatchStatus.UNKNOWN, + baseline + ))) + ); + } + /** * Tests that the <code>Set</code> of <code>Repositories</code>s associated with this baseline * can be set, retrieved, and returned. diff --git a/HIRS_Utils/src/test/java/hirs/data/persist/SimpleImaBaselineTest.java b/HIRS_Utils/src/test/java/hirs/data/persist/SimpleImaBaselineTest.java index 9dadf964..263a9073 100644 --- a/HIRS_Utils/src/test/java/hirs/data/persist/SimpleImaBaselineTest.java +++ b/HIRS_Utils/src/test/java/hirs/data/persist/SimpleImaBaselineTest.java @@ -1,5 +1,6 @@ package hirs.data.persist; +import hirs.ima.matching.BatchImaMatchStatus; import hirs.ima.matching.IMAMatchStatus; import hirs.persist.BaselineManager; import hirs.persist.DBBaselineManager; @@ -223,6 +224,15 @@ public class SimpleImaBaselineTest extends SpringPersistenceTest { ).getIMAMatchStatuses(record).iterator().next(); } + private BatchImaMatchStatus<IMABaselineRecord> baselineContainsHashes( + final SimpleImaBaseline baseline, + final IMAMeasurementRecord record, + final IMAPolicy imaPolicy) { + return baseline.containsHashes( + Collections.singletonList(record), recordManager, imaPolicy + ); + } + /** * Create a test IMAPolicy object. * @@ -601,6 +611,91 @@ public class SimpleImaBaselineTest extends SpringPersistenceTest { } } + /** + * Simple test that ensures a SimpleImaBaseline can determine whether it contains + * baseline records that match measurement records based solely on their hashes. + */ + @Test + public final void containsHashes() { + final SimpleImaBaseline baseline = new SimpleImaBaseline("TestBaseline"); + final String baselineGradleFilename = "/usr/bin/gradle"; + final String measuredGradleFilename = "/usr/bin/gradle_by_another_name"; + final Digest gradleHash = getDigest("33333c2f7f3003d2e4baddc46ed4763a49543333"); + + final IMABaselineRecord baseRecSameNameMatchingHash = new IMABaselineRecord( + measuredGradleFilename, gradleHash + ); + + final IMABaselineRecord baseRecDifferentNameMatchingHash = new IMABaselineRecord( + baselineGradleFilename, gradleHash + ); + + baseline.addToBaseline(baseRecSameNameMatchingHash); + baseline.addToBaseline(baseRecDifferentNameMatchingHash); + + baseline.addToBaseline(new IMABaselineRecord( + baselineGradleFilename, + getDigest("00000c2f7f3003d2e4baddc46ed4763a49543333") + )); + baseline.addToBaseline(new IMABaselineRecord( + measuredGradleFilename, + getDigest("00000c2f7f3003d2e4baddc46ed4763a49543333") + )); + + + IMAMeasurementRecord measurementRecord = new IMAMeasurementRecord( + measuredGradleFilename, gradleHash + ); + + Set<IMABaselineRecord> matchingRecords = new HashSet<>(Arrays.asList( + baseRecSameNameMatchingHash, + baseRecDifferentNameMatchingHash + )); + + Assert.assertEquals( + baselineContainsHashes(baseline, measurementRecord, getTestImaPolicy(false)), + new BatchImaMatchStatus<>( + Collections.singleton(new IMAMatchStatus<>( + measurementRecord, + ReportMatchStatus.MATCH, + matchingRecords, + baseline + )) + ) + ); + } + + /** + * Simple test that ensures a SimpleImaBaseline can determine whether it contains + * baseline records that match measurement records based solely on their hashes. + */ + @Test + public final void containsHashesWithNoMatches() { + final SimpleImaBaseline baseline = new SimpleImaBaseline("TestBaseline"); + final String baselineGradleFilename = "/usr/bin/gradle"; + final String measuredGradleFilename = "/usr/bin/gradle_by_another_name"; + final Digest gradleHash = getDigest("33333c2f7f3003d2e4baddc46ed4763a49543333"); + + baseline.addToBaseline(new IMABaselineRecord( + baselineGradleFilename, + getDigest("00000c2f7f3003d2e4baddc46ed4763a49543333") + )); + + IMAMeasurementRecord record = new IMAMeasurementRecord( + measuredGradleFilename, gradleHash + ); + + Assert.assertEquals(baselineContainsHashes(baseline, record, getTestImaPolicy(false)), + new BatchImaMatchStatus<>( + Collections.singleton(new IMAMatchStatus<>( + record, + ReportMatchStatus.UNKNOWN, + baseline + )) + ) + ); + } + /** * Tests that getBaselineRecords() returns a list of IMA records. * diff --git a/HIRS_Utils/src/test/java/hirs/data/persist/TargetedRepoImaBaselineTest.java b/HIRS_Utils/src/test/java/hirs/data/persist/TargetedRepoImaBaselineTest.java index 430e29f0..adcc48d7 100644 --- a/HIRS_Utils/src/test/java/hirs/data/persist/TargetedRepoImaBaselineTest.java +++ b/HIRS_Utils/src/test/java/hirs/data/persist/TargetedRepoImaBaselineTest.java @@ -8,6 +8,7 @@ import java.util.HashSet; import java.util.List; import java.util.Set; +import hirs.ima.matching.BatchImaMatchStatus; import hirs.ima.matching.IMAMatchStatus; import hirs.persist.BaselineManager; import hirs.persist.DBBaselineManager; @@ -355,6 +356,123 @@ public class TargetedRepoImaBaselineTest extends SpringPersistenceTest { ); } + /** + * Test that ensures a TargetedRepoImaBaseline can correctly determine if + * it contains any matching baseline records solely based upon a given measurement + * record's hash. + * + * @throws UnsupportedEncodingException + * if an error is encountered while getting the test digest + */ + @Test + public final void containsHashes() throws UnsupportedEncodingException { + TargetedRepoImaBaseline testBaseline = new TargetedRepoImaBaseline(BASELINE_NAME); + Repository testRepo = new TestRepository("Test Repository", 0); + DBRepositoryManager repoManager = new DBRepositoryManager(sessionFactory); + testRepo = repoManager.saveRepository(testRepo); + RepoPackage testRepoPackage = + new RPMRepoPackage(NAME, VERSION1, RELEASE1, ARCHITECTURE, testRepo); + Set<IMABaselineRecord> imaRecords = new HashSet<>(); + imaRecords.add(SimpleImaBaselineTest.createTestIMARecord(FILEPATH1)); + testRepoPackage.setAllMeasurements(imaRecords, RepoPackageTest.getTestDigest()); + repoManager.saveRepoPackage(testRepoPackage); + Set<RepoPackage> originalPackages = new HashSet<>(); + originalPackages.add(testRepoPackage); + testBaseline.setRepoPackages(originalPackages); + + DBBaselineManager baselineManager = new DBBaselineManager(sessionFactory); + TargetedRepoImaBaseline savedBaseline = + (TargetedRepoImaBaseline) baselineManager.save(testBaseline); + + IMABaselineRecord baselineRecord = SimpleImaBaselineTest.createTestIMARecord(FILEPATH1); + IMAMeasurementRecord measurementRecord = new IMAMeasurementRecord( + baselineRecord.getPath(), + baselineRecord.getHash() + ); + + Assert.assertEquals( + savedBaseline.containsHashes( + Collections.singletonList(measurementRecord), + new DbImaBaselineRecordManager(sessionFactory), + SimpleImaBaselineTest.getTestImaPolicy(false) + + ).getIMAMatchStatuses(measurementRecord), + Collections.singleton( + new IMAMatchStatus<>( + measurementRecord, ReportMatchStatus.MATCH, baselineRecord, baseline + ) + ) + ); + + measurementRecord = new IMAMeasurementRecord( + "/some/other/file", + baselineRecord.getHash() + ); + Assert.assertEquals( + savedBaseline.containsHashes( + Collections.singletonList(measurementRecord), + new DbImaBaselineRecordManager(sessionFactory), + SimpleImaBaselineTest.getTestImaPolicy(false) + + ).getIMAMatchStatuses(measurementRecord), + Collections.singleton( + new IMAMatchStatus<>( + measurementRecord, ReportMatchStatus.MATCH, baselineRecord, baseline + ) + ) + ); + } + + /** + * Test that ensures a TargetedRepoImaBaseline can correctly determine that + * it does not contain any matching baseline records solely based upon a given measurement + * record's hash. + * + * @throws UnsupportedEncodingException + * if an error is encountered while getting the test digest + */ + @Test + public final void containsHashesWithNoMatches() throws UnsupportedEncodingException { + TargetedRepoImaBaseline testBaseline = new TargetedRepoImaBaseline(BASELINE_NAME); + Repository testRepo = new TestRepository("Test Repository", 0); + DBRepositoryManager repoManager = new DBRepositoryManager(sessionFactory); + testRepo = repoManager.saveRepository(testRepo); + RepoPackage testRepoPackage = + new RPMRepoPackage(NAME, VERSION1, RELEASE1, ARCHITECTURE, testRepo); + Set<IMABaselineRecord> imaRecords = new HashSet<>(); + imaRecords.add(SimpleImaBaselineTest.createTestIMARecord(FILEPATH1)); + testRepoPackage.setAllMeasurements(imaRecords, RepoPackageTest.getTestDigest()); + repoManager.saveRepoPackage(testRepoPackage); + Set<RepoPackage> originalPackages = new HashSet<>(); + originalPackages.add(testRepoPackage); + testBaseline.setRepoPackages(originalPackages); + + DBBaselineManager baselineManager = new DBBaselineManager(sessionFactory); + TargetedRepoImaBaseline savedBaseline = + (TargetedRepoImaBaseline) baselineManager.save(testBaseline); + + IMABaselineRecord baselineRecord = SimpleImaBaselineTest.createTestIMARecord(FILEPATH1); + + IMAMeasurementRecord measurementRecord = new IMAMeasurementRecord( + baselineRecord.getPath(), + SimpleImaBaselineTest.getDigest("0d5f3c2f7f3003d2e4baddc46ed4763a4954f648") + ); + + Assert.assertEquals( + savedBaseline.containsHashes( + Collections.singletonList(measurementRecord), + new DbImaBaselineRecordManager(sessionFactory), + SimpleImaBaselineTest.getTestImaPolicy(false) + + ), + new BatchImaMatchStatus<>(Collections.singleton(new IMAMatchStatus<>( + measurementRecord, + ReportMatchStatus.UNKNOWN, + baseline + ))) + ); + } + /** * Tests that a <code>TargetedRepoImaBaseline</code> can be archived. */ diff --git a/HIRS_Utils/src/test/java/hirs/ima/matching/ImaAcceptableHashRecordMatcherTest.java b/HIRS_Utils/src/test/java/hirs/ima/matching/ImaAcceptableHashRecordMatcherTest.java new file mode 100644 index 00000000..d7d3309f --- /dev/null +++ b/HIRS_Utils/src/test/java/hirs/ima/matching/ImaAcceptableHashRecordMatcherTest.java @@ -0,0 +1,153 @@ +package hirs.ima.matching; + +import hirs.data.persist.Digest; +import hirs.data.persist.IMABaselineRecord; +import hirs.data.persist.IMAMeasurementRecord; +import hirs.data.persist.ReportMatchStatus; +import hirs.data.persist.SimpleImaBaseline; +import hirs.data.persist.SimpleImaBaselineTest; +import org.testng.Assert; +import org.testng.annotations.Test; + +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; + +/** + * Tests for the ImaAcceptableHashRecordMatcher. These are basic tests of its functionality; + * more complete tests for contains() as used operationally by baselines that test various + * permutations of parameters are located in SimpleImaBaselineTest, BroadRepoImaBaselineTest, + * TargetedRepoImaBaselineTest, ImaBlacklistBaselineTest, and ImaIgnoreSetBaselineTest. + */ +public class ImaAcceptableHashRecordMatcherTest { + private static final String FILENAME_1 = "/bin/ls"; + private static final String FILENAME_2 = "/bin/ls_with_another_name"; + private static final Digest HASH_1 = + SimpleImaBaselineTest.getDigest("33333c2f7f3003d2e4baddc46ed4763a49543333"); + private static final Digest HASH_2 = + SimpleImaBaselineTest.getDigest("00000c2f7f3003d2e4baddc46ed4763a49543333"); + + /** + * Tests that the 'contains' method functions if no records are given. + */ + @Test + public void testContainsEmpty() { + SimpleImaBaseline baseline = getTestSimpleImaBaseline(); + IMAMeasurementRecord measurementRecord = new IMAMeasurementRecord(FILENAME_1, HASH_1); + Assert.assertEquals( + new ImaAcceptableHashRecordMatcher( + Collections.emptyList(), + SimpleImaBaselineTest.getTestImaPolicy(false), + baseline).contains(measurementRecord), + new IMAMatchStatus(measurementRecord, ReportMatchStatus.UNKNOWN, baseline) + ); + } + + /** + * Tests that the 'contains' method functions if a matching record is given in the case of that + * matching record being both filename and hash. + */ + @Test + public void testContainsSameFilename() { + SimpleImaBaseline baseline = getTestSimpleImaBaseline(); + IMAMeasurementRecord measurementRecord = new IMAMeasurementRecord(FILENAME_1, HASH_1); + IMABaselineRecord baselineRecord = new IMABaselineRecord(FILENAME_1, HASH_1); + Assert.assertEquals( + new ImaAcceptableHashRecordMatcher( + Collections.singletonList(baselineRecord), + SimpleImaBaselineTest.getTestImaPolicy(false), + baseline).contains(measurementRecord), + new IMAMatchStatus<>( + measurementRecord, ReportMatchStatus.MATCH, baselineRecord, baseline + ) + ); + } + + /** + * Tests that the 'contains' method matches on hash properly, even if + * a measurement record has a different filename than the matching baseline record. + */ + @Test + public void testContainsDifferentFilename() { + SimpleImaBaseline baseline = getTestSimpleImaBaseline(); + IMAMeasurementRecord measurementRecord = new IMAMeasurementRecord(FILENAME_2, HASH_1); + IMABaselineRecord baselineRecord = new IMABaselineRecord(FILENAME_1, HASH_1); + Assert.assertEquals( + new ImaAcceptableHashRecordMatcher( + Collections.singletonList(baselineRecord), + SimpleImaBaselineTest.getTestImaPolicy(false), + baseline).contains(measurementRecord), + new IMAMatchStatus<>( + measurementRecord, ReportMatchStatus.MATCH, baselineRecord, baseline + ) + ); + } + + /** + * Tests that the 'contains' method returns the expected 'UNKNOWN' match + * status if no record matches the collected measurement in a nonempty baseline. + */ + @Test + public void testContainsNonEmptyButUnknown() { + SimpleImaBaseline baseline = getTestSimpleImaBaseline(); + IMAMeasurementRecord measurementRecord = new IMAMeasurementRecord(FILENAME_1, HASH_1); + IMABaselineRecord baselineRecord = new IMABaselineRecord(FILENAME_1, HASH_2); + Assert.assertEquals( + new ImaAcceptableHashRecordMatcher( + Collections.singletonList(baselineRecord), + SimpleImaBaselineTest.getTestImaPolicy(false), + baseline).contains(measurementRecord), + new IMAMatchStatus(measurementRecord, ReportMatchStatus.UNKNOWN, baseline) + ); + } + + /** + * Tests that the 'contains' method returns all matching baseline records from a + * baseline when there are multiple matches to a given measurement record. + */ + @Test + public void testContainsMultipleMatchingBaselineRecords() { + SimpleImaBaseline baseline = getTestSimpleImaBaseline(); + IMAMeasurementRecord measurementRecord = new IMAMeasurementRecord(FILENAME_1, HASH_1); + Set<IMABaselineRecord> baselineRecords = new HashSet<>(Arrays.asList( + new IMABaselineRecord(FILENAME_1, HASH_1), + new IMABaselineRecord(FILENAME_2, HASH_1), + new IMABaselineRecord(FILENAME_1, HASH_2) + )); + Assert.assertEquals( + new ImaAcceptableHashRecordMatcher( + baselineRecords, + SimpleImaBaselineTest.getTestImaPolicy(false), + baseline + ).contains(measurementRecord), + new IMAMatchStatus<>( + measurementRecord, + ReportMatchStatus.MATCH, + new HashSet<>(Arrays.asList( + new IMABaselineRecord(FILENAME_1, HASH_1), + new IMABaselineRecord(FILENAME_2, HASH_1)) + ), + baseline + ) + ); + } + + /** + * Tests that the 'contains' method throws an IllegalArgumentException if given a null + * measurement record to check. + */ + @Test(expectedExceptions = IllegalArgumentException.class) + public void testContainsOnNullRecord() { + SimpleImaBaseline baseline = getTestSimpleImaBaseline(); + IMABaselineRecord baselineRecord = new IMABaselineRecord(FILENAME_1, HASH_1); + new ImaAcceptableHashRecordMatcher( + Collections.singletonList(baselineRecord), + SimpleImaBaselineTest.getTestImaPolicy(false), + baseline).contains(null); + } + + private static SimpleImaBaseline getTestSimpleImaBaseline() { + return new SimpleImaBaseline("Test IMA Baseline"); + } +} diff --git a/HIRS_Utils/src/test/java/hirs/ima/ImaAcceptableRecordMatcherTest.java b/HIRS_Utils/src/test/java/hirs/ima/matching/ImaAcceptablePathAndHashRecordMatcherTest.java similarity index 63% rename from HIRS_Utils/src/test/java/hirs/ima/ImaAcceptableRecordMatcherTest.java rename to HIRS_Utils/src/test/java/hirs/ima/matching/ImaAcceptablePathAndHashRecordMatcherTest.java index 8363dcd5..b0bdbddc 100644 --- a/HIRS_Utils/src/test/java/hirs/ima/ImaAcceptableRecordMatcherTest.java +++ b/HIRS_Utils/src/test/java/hirs/ima/matching/ImaAcceptablePathAndHashRecordMatcherTest.java @@ -1,26 +1,27 @@ -package hirs.ima; +package hirs.ima.matching; import hirs.data.persist.Digest; +import hirs.data.persist.DigestTest; import hirs.data.persist.IMABaselineRecord; import hirs.data.persist.IMAMeasurementRecord; +import hirs.data.persist.ImaAcceptableRecordBaseline; import hirs.data.persist.ReportMatchStatus; import hirs.data.persist.SimpleImaBaseline; -import hirs.ima.matching.IMAMatchStatus; -import hirs.ima.matching.ImaAcceptableRecordMatcher; +import hirs.data.persist.SimpleImaBaselineTest; import org.testng.Assert; import org.testng.annotations.Test; -import hirs.data.persist.SimpleImaBaselineTest; import java.util.Arrays; import java.util.Collections; +import java.util.List; /** - * Tests ImaAcceptableRecordMatcher. These are very basic tests of its functionality; + * Tests ImaAcceptablePathAndHashRecordMatcher. These are very basic tests of its functionality; * more complete tests for contains() as used operationally by baselines that test various * permutations of parameters are located in SimpleImaBaselineTest, BroadRepoImaBaselineTest, * TargetedRepoImaBaselineTest, ImaBlacklistBaselineTest, and ImaIgnoreSetBaselineTest. */ -public class ImaAcceptableRecordMatcherTest { +public class ImaAcceptablePathAndHashRecordMatcherTest { private static final String FILE_1 = "/bin/ls"; private static final String PARTIAL_FILE_1 = "ls"; private static final Digest HASH_1 = @@ -33,6 +34,7 @@ public class ImaAcceptableRecordMatcherTest { private static final Digest LIB64_LD_HASH = SimpleImaBaselineTest.getDigest("55555c2f7f3003d2e4baddc46ed4763a49543333"); + private static final Digest ONES = DigestTest.getTestSHA1Digest(); /** * Tests that contains functions if no records are given. @@ -42,7 +44,7 @@ public class ImaAcceptableRecordMatcherTest { SimpleImaBaseline baseline = getTestSimpleImaBaseline(); IMAMeasurementRecord measurementRecord = new IMAMeasurementRecord(FILE_1, HASH_1); Assert.assertEquals( - new ImaAcceptableRecordMatcher( + new ImaAcceptablePathAndHashRecordMatcher( Collections.emptyList(), SimpleImaBaselineTest.getTestImaPolicy(false), baseline).contains(measurementRecord), @@ -59,7 +61,7 @@ public class ImaAcceptableRecordMatcherTest { IMAMeasurementRecord measurementRecord = new IMAMeasurementRecord(FILE_1, HASH_1); IMABaselineRecord baselineRecord = new IMABaselineRecord(FILE_1, HASH_1); Assert.assertEquals( - new ImaAcceptableRecordMatcher( + new ImaAcceptablePathAndHashRecordMatcher( Collections.singletonList(baselineRecord), SimpleImaBaselineTest.getTestImaPolicy(false), baseline).contains(measurementRecord), @@ -78,7 +80,7 @@ public class ImaAcceptableRecordMatcherTest { IMAMeasurementRecord measurementRecord = new IMAMeasurementRecord(PARTIAL_FILE_1, HASH_1); IMABaselineRecord baselineRecord = new IMABaselineRecord(FILE_1, HASH_1); Assert.assertEquals( - new ImaAcceptableRecordMatcher( + new ImaAcceptablePathAndHashRecordMatcher( Collections.singletonList(baselineRecord), SimpleImaBaselineTest.getTestImaPolicy(true), baseline).contains(measurementRecord), @@ -99,16 +101,82 @@ public class ImaAcceptableRecordMatcherTest { IMAMeasurementRecord measurementRecord = new IMAMeasurementRecord(LIB64_LD_FILE, USR_LIB64_LD_HASH); Assert.assertEquals( - new ImaAcceptableRecordMatcher( + new ImaAcceptablePathAndHashRecordMatcher( Collections.singletonList(baselineRecord), SimpleImaBaselineTest.getTestImaPolicy(false), - baseline).contains(measurementRecord), + baseline + ).contains(measurementRecord), new IMAMatchStatus<>( measurementRecord, ReportMatchStatus.MATCH, baselineRecord, baseline ) ); } + /** + * Tests that contains correctly matches equivalent paths for real-world + * examples that have been seen. + */ + @Test + public void testContainsExperiencedEquivalentPaths() { + List<List<String>> pairs = Arrays.asList( + Arrays.asList("/usr/sbin/dhclient", "/sbin/dhclient"), + Arrays.asList("/usr/sbin/sysctl", "/sbin/sysctl"), + Arrays.asList("/usr/sbin/swapon", "/sbin/swapon"), + Arrays.asList("/sbin/audispd", "/usr/sbin/audispd"), + Arrays.asList("/usr/sbin/sysctl", "/sbin/sysctl"), + Arrays.asList("/sbin/ldconfig", "/usr/sbin/ldconfig"), + Arrays.asList("/sbin/kexec", "/usr/sbin/kexec"), + Arrays.asList("/usr/sbin/ip", "/sbin/ip"), + Arrays.asList("/usr/bin/bash", "/bin/bash") + ); + + for (List<String> pair : pairs) { + testEquivalentNames(pair.get(0), pair.get(1)); + } + } + + /** + * Tests that contains correctly matches equivalent paths. + */ + @Test + public void testContainsExhaustiveEquivalentPaths() { + List<List<String>> pairs = Arrays.asList( + Arrays.asList("/bin/foofile", "/usr/bin/foofile"), + Arrays.asList("/lib/foofile", "/usr/lib/foofile"), + Arrays.asList("/lib64/foofile", "/usr/lib64/foofile"), + Arrays.asList("/usr/bin/foofile", "/usr/sbin/foofile"), + Arrays.asList("/sbin/foofile", "/usr/sbin/foofile") + ); + + for (int i = 0; i < pairs.size(); i++) { + List<String> equivPair = pairs.get(i); + + testEquivalentNames(equivPair.get(0), equivPair.get(1)); + testEquivalentNames(equivPair.get(1), equivPair.get(0)); + } + } + + private void testEquivalentNames(final String measuredFilename, final String baselineFilename) { + final IMAMeasurementRecord measurementRecord = + new IMAMeasurementRecord(measuredFilename, ONES); + final IMABaselineRecord baselineRecord = new IMABaselineRecord(baselineFilename, ONES); + final ImaAcceptableRecordBaseline baseline = getTestSimpleImaBaseline(); + + Assert.assertEquals( + new ImaAcceptablePathAndHashRecordMatcher( + Collections.singleton(baselineRecord), + SimpleImaBaselineTest.getTestImaPolicy(false), + baseline + ).contains(measurementRecord), + new IMAMatchStatus<>( + measurementRecord, + ReportMatchStatus.MATCH, + baselineRecord, + baseline + ) + ); + } + /** * This tests a case where a baseline includes a file at both /lib64 and /usr/lib64 * with distinct hashes, and a report contains an entry for a file at /usr/lib64 whose hash @@ -128,7 +196,7 @@ public class ImaAcceptableRecordMatcherTest { IMAMeasurementRecord measurementRecord = new IMAMeasurementRecord(USR_LIB64_LD_FILE, LIB64_LD_HASH); Assert.assertEquals( - new ImaAcceptableRecordMatcher( + new ImaAcceptablePathAndHashRecordMatcher( Arrays.asList(libBaselineRecord, usrLibBaselineRecord), SimpleImaBaselineTest.getTestImaPolicy(false), baseline).contains(measurementRecord), diff --git a/HIRS_Utils/src/test/java/hirs/ima/matching/package-info.java b/HIRS_Utils/src/test/java/hirs/ima/matching/package-info.java new file mode 100644 index 00000000..ae6bbdc2 --- /dev/null +++ b/HIRS_Utils/src/test/java/hirs/ima/matching/package-info.java @@ -0,0 +1,5 @@ +/** + * Contains classes that perform record matching from IMA measurement records + * to IMA baselines records. + */ +package hirs.ima.matching;