diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/OpenSSLAnalyzer.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/OpenSSLAnalyzer.java index 38438aa08..b2333ddd6 100644 --- a/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/OpenSSLAnalyzer.java +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/OpenSSLAnalyzer.java @@ -17,6 +17,7 @@ */ package org.owasp.dependencycheck.analyzer; +import org.apache.commons.io.FileUtils; import org.apache.commons.io.filefilter.NameFileFilter; import org.owasp.dependencycheck.Engine; import org.owasp.dependencycheck.analyzer.exception.AnalysisException; @@ -27,10 +28,12 @@ import org.slf4j.LoggerFactory; import java.io.File; import java.io.FileFilter; +import java.io.IOException; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Set; +import java.util.regex.Matcher; import java.util.regex.Pattern; /** @@ -40,29 +43,32 @@ import java.util.regex.Pattern; */ public class OpenSSLAnalyzer extends AbstractFileTypeAnalyzer { + private static final int HEXADECIMAL = 16; + /** + * Filename to analyze. All other .h files get removed from consideration. + */ + private static final String OPENSSLV_H = "opensslv.h"; /** * Used when compiling file scanning regex patterns. */ private static final int REGEX_OPTIONS = Pattern.DOTALL | Pattern.CASE_INSENSITIVE; - /** * The logger. */ private static final Logger LOGGER = LoggerFactory .getLogger(OpenSSLAnalyzer.class); - /** * Filename extensions for files to be analyzed. */ private static final Set EXTENSIONS = Collections .unmodifiableSet(Collections.singleton("h")); - /** * Filter that detects files named "__init__.py". */ - private static final FileFilter OPENSSLV_FILTER = new NameFileFilter("opensslv.h"); - + private static final FileFilter OPENSSLV_FILTER = new NameFileFilter(OPENSSLV_H); + private static final Pattern VERSION_PATTERN = Pattern.compile( + "define\\s+OPENSSL_VERSION_NUMBER\\s+0x([0-9a-zA-Z]{8})L", REGEX_OPTIONS); private static final int MAJOR_OFFSET = 28; private static final long MINOR_MASK = 0x0ff00000L; private static final int MINOR_OFFSET = 20; @@ -140,24 +146,46 @@ public class OpenSSLAnalyzer extends AbstractFileTypeAnalyzer { final File parent = file.getParentFile(); final String parentName = parent.getName(); boolean found = false; -// if (INIT_PY_FILTER.accept(file)) { -// for (final File sourcefile : parent.listFiles(PY_FILTER)) { -// found |= analyzeFileContents(dependency, sourcefile); -// } -// } + if (OPENSSLV_FILTER.accept(file)) { + final String contents = getFileContents(file); + if (!contents.isEmpty()) { + final Matcher matcher = VERSION_PATTERN.matcher(contents); + while (matcher.find()) { + dependency.getVersionEvidence().addEvidence(OPENSSLV_H, "Version Constant", + getOpenSSLVersion(Long.parseLong(matcher.group(1), HEXADECIMAL)), Confidence.HIGH); + found = true; + } + } + } if (found) { - dependency.setDisplayFileName(parentName + "/__init__.py"); - dependency.getProductEvidence().addEvidence(file.getName(), - "PackageName", parentName, Confidence.MEDIUM); + dependency.setDisplayFileName(parentName + File.separatorChar + OPENSSLV_H); + dependency.getVendorEvidence().addEvidence(OPENSSLV_H, "Vendor", "OpenSSL", Confidence.HIGHEST); + dependency.getProductEvidence().addEvidence(OPENSSLV_H, "Product", "OpenSSL", Confidence.HIGHEST); } else { - // copy, alter and set in case some other thread is iterating over - final List deps = new ArrayList( - engine.getDependencies()); - deps.remove(dependency); - engine.setDependencies(deps); + engine.getDependencies().remove(dependency); } } + /** + * Retrieves the contents of a given file. + * + * @param actualFile the file to read + * @return the contents of the file + * @throws AnalysisException thrown if there is an IO Exception + */ + private String getFileContents(final File actualFile) + throws AnalysisException { + String contents = ""; + try { + contents = FileUtils.readFileToString(actualFile).trim(); + } catch (IOException e) { + throw new AnalysisException( + "Problem occured while reading dependency file.", e); + } + return contents; + } + + @Override protected String getAnalyzerEnabledSettingKey() { return "fixme"; diff --git a/dependency-check-core/src/test/java/org/owasp/dependencycheck/analyzer/OpenSSLAnalyzerTest.java b/dependency-check-core/src/test/java/org/owasp/dependencycheck/analyzer/OpenSSLAnalyzerTest.java index 1464e8658..d81df57a8 100644 --- a/dependency-check-core/src/test/java/org/owasp/dependencycheck/analyzer/OpenSSLAnalyzerTest.java +++ b/dependency-check-core/src/test/java/org/owasp/dependencycheck/analyzer/OpenSSLAnalyzerTest.java @@ -22,12 +22,14 @@ import org.junit.After; import org.junit.Before; import org.junit.Test; import org.owasp.dependencycheck.BaseTest; +import org.owasp.dependencycheck.analyzer.exception.AnalysisException; +import org.owasp.dependencycheck.dependency.Dependency; import java.util.Arrays; import java.util.HashSet; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; +import static org.junit.Assert.*; +import static org.hamcrest.CoreMatchers.*; /** * Unit tests for OpenSSLAnalyzerAnalyzer. @@ -117,4 +119,15 @@ public class OpenSSLAnalyzerTest extends BaseTest { assertEquals(versions[i], OpenSSLAnalyzer.getOpenSSLVersion(constants[i])); } } + + @Test + public void testOpenSSLVersionHeaderFile() throws AnalysisException { + final Dependency result = new Dependency(BaseTest.getResourceAsFile( + this, + "openssl/opensslv.h")); + analyzer.analyze(result, null); + assertThat(result.getProductEvidence().toString(), containsString("OpenSSL")); + assertThat(result.getVendorEvidence().toString(), containsString("OpenSSL")); + assertThat(result.getVersionEvidence().toString(), containsString("1.0.2c")); + } } diff --git a/dependency-check-core/src/test/resources/openssl/opensslv.h b/dependency-check-core/src/test/resources/openssl/opensslv.h new file mode 100644 index 000000000..7cc19dc51 --- /dev/null +++ b/dependency-check-core/src/test/resources/openssl/opensslv.h @@ -0,0 +1,97 @@ +#ifndef HEADER_OPENSSLV_H +# define HEADER_OPENSSLV_H + +#ifdef __cplusplus +extern "C" { +#endif + +/*- + * Numeric release version identifier: + * MNNFFPPS: major minor fix patch status + * The status nibble has one of the values 0 for development, 1 to e for betas + * 1 to 14, and f for release. The patch level is exactly that. + * For example: + * 0.9.3-dev 0x00903000 + * 0.9.3-beta1 0x00903001 + * 0.9.3-beta2-dev 0x00903002 + * 0.9.3-beta2 0x00903002 (same as ...beta2-dev) + * 0.9.3 0x0090300f + * 0.9.3a 0x0090301f + * 0.9.4 0x0090400f + * 1.2.3z 0x102031af + * + * For continuity reasons (because 0.9.5 is already out, and is coded + * 0x00905100), between 0.9.5 and 0.9.6 the coding of the patch level + * part is slightly different, by setting the highest bit. This means + * that 0.9.5a looks like this: 0x0090581f. At 0.9.6, we can start + * with 0x0090600S... + * + * (Prior to 0.9.3-dev a different scheme was used: 0.9.2b is 0x0922.) + * (Prior to 0.9.5a beta1, a different scheme was used: MMNNFFRBB for + * major minor fix final patch/beta) + */ +# define OPENSSL_VERSION_NUMBER 0x1000203fL +# ifdef OPENSSL_FIPS +# define OPENSSL_VERSION_TEXT "OpenSSL 1.0.2c-fips 12 Jun 2015" +# else +# define OPENSSL_VERSION_TEXT "OpenSSL 1.0.2c 12 Jun 2015" +# endif +# define OPENSSL_VERSION_PTEXT " part of " OPENSSL_VERSION_TEXT + +/*- + * The macros below are to be used for shared library (.so, .dll, ...) + * versioning. That kind of versioning works a bit differently between + * operating systems. The most usual scheme is to set a major and a minor + * number, and have the runtime loader check that the major number is equal + * to what it was at application link time, while the minor number has to + * be greater or equal to what it was at application link time. With this + * scheme, the version number is usually part of the file name, like this: + * + * libcrypto.so.0.9 + * + * Some unixen also make a softlink with the major verson number only: + * + * libcrypto.so.0 + * + * On Tru64 and IRIX 6.x it works a little bit differently. There, the + * shared library version is stored in the file, and is actually a series + * of versions, separated by colons. The rightmost version present in the + * library when linking an application is stored in the application to be + * matched at run time. When the application is run, a check is done to + * see if the library version stored in the application matches any of the + * versions in the version string of the library itself. + * This version string can be constructed in any way, depending on what + * kind of matching is desired. However, to implement the same scheme as + * the one used in the other unixen, all compatible versions, from lowest + * to highest, should be part of the string. Consecutive builds would + * give the following versions strings: + * + * 3.0 + * 3.0:3.1 + * 3.0:3.1:3.2 + * 4.0 + * 4.0:4.1 + * + * Notice how version 4 is completely incompatible with version, and + * therefore give the breach you can see. + * + * There may be other schemes as well that I haven't yet discovered. + * + * So, here's the way it works here: first of all, the library version + * number doesn't need at all to match the overall OpenSSL version. + * However, it's nice and more understandable if it actually does. + * The current library version is stored in the macro SHLIB_VERSION_NUMBER, + * which is just a piece of text in the format "M.m.e" (Major, minor, edit). + * For the sake of Tru64, IRIX, and any other OS that behaves in similar ways, + * we need to keep a history of version numbers, which is done in the + * macro SHLIB_VERSION_HISTORY. The numbers are separated by colons and + * should only keep the versions that are binary compatible with the current. + */ +# define SHLIB_VERSION_HISTORY "" +# define SHLIB_VERSION_NUMBER "1.0.0" + + +#ifdef __cplusplus +} +#endif +#endif /* HEADER_OPENSSLV_H */