Merge pull request #274 from dwvisser/openssl-source-analyzer

OpenSSL source analyzer

Former-commit-id: cc2f02f3722b7480f0ec5f7979892b78dc4076d1
This commit is contained in:
Jeremy Long
2015-07-11 06:29:55 -04:00
13 changed files with 448 additions and 8 deletions

View File

@@ -250,9 +250,6 @@ public class App {
final String suppressionFile = cli.getSuppressionFile();
final boolean jarDisabled = cli.isJarDisabled();
final boolean archiveDisabled = cli.isArchiveDisabled();
final boolean pyDistDisabled = cli.isPythonDistributionDisabled();
final boolean pyPkgDisabled = cli.isPythonPackageDisabled();
final boolean autoconfDisabled = cli.isAutoconfDisabled();
final boolean assemblyDisabled = cli.isAssemblyDisabled();
final boolean nuspecDisabled = cli.isNuspecDisabled();
final boolean centralDisabled = cli.isCentralDisabled();
@@ -320,11 +317,12 @@ public class App {
//File Type Analyzer Settings
Settings.setBoolean(Settings.KEYS.ANALYZER_JAR_ENABLED, !jarDisabled);
Settings.setBoolean(Settings.KEYS.ANALYZER_ARCHIVE_ENABLED, !archiveDisabled);
Settings.setBoolean(Settings.KEYS.ANALYZER_PYTHON_DISTRIBUTION_ENABLED, !pyDistDisabled);
Settings.setBoolean(Settings.KEYS.ANALYZER_PYTHON_PACKAGE_ENABLED, !pyPkgDisabled);
Settings.setBoolean(Settings.KEYS.ANALYZER_AUTOCONF_ENABLED, !autoconfDisabled);
Settings.setBoolean(Settings.KEYS.ANALYZER_PYTHON_DISTRIBUTION_ENABLED, !cli.isPythonDistributionDisabled());
Settings.setBoolean(Settings.KEYS.ANALYZER_PYTHON_PACKAGE_ENABLED, !cli.isPythonPackageDisabled());
Settings.setBoolean(Settings.KEYS.ANALYZER_AUTOCONF_ENABLED, !cli.isAutoconfDisabled());
Settings.setBoolean(Settings.KEYS.ANALYZER_NUSPEC_ENABLED, !nuspecDisabled);
Settings.setBoolean(Settings.KEYS.ANALYZER_ASSEMBLY_ENABLED, !assemblyDisabled);
Settings.setBoolean(Settings.KEYS.ANALYZER_OPENSSL_ENABLED, !cli.isOpenSSLDisabled());
Settings.setBoolean(Settings.KEYS.ANALYZER_CENTRAL_ENABLED, !centralDisabled);
Settings.setBoolean(Settings.KEYS.ANALYZER_NEXUS_ENABLED, !nexusDisabled);

View File

@@ -367,6 +367,9 @@ public final class CliParser {
.withLongOpt(ARGUMENT.DISABLE_AUTOCONF)
.withDescription("Disable the Autoconf Analyzer.").create();
final Option disableOpenSSLAnalyzer = OptionBuilder.withLongOpt(ARGUMENT.DISABLE_OPENSSL)
.withDescription("Disable the OpenSSL Analyzer.").create();
final Option disableCentralAnalyzer = OptionBuilder.withLongOpt(ARGUMENT.DISABLE_CENTRAL)
.withDescription("Disable the Central Analyzer. If this analyzer is disabled it is likely you also want to disable "
+ "the Nexus Analyzer.").create();
@@ -396,6 +399,7 @@ public final class CliParser {
.addOption(disablePythonDistributionAnalyzer)
.addOption(disablePythonPackageAnalyzer)
.addOption(disableAutoconfAnalyzer)
.addOption(disableOpenSSLAnalyzer)
.addOption(disableNuspecAnalyzer)
.addOption(disableCentralAnalyzer)
.addOption(disableNexusAnalyzer)
@@ -521,6 +525,15 @@ public final class CliParser {
return (line != null) && line.hasOption(ARGUMENT.DISABLE_NEXUS);
}
/**
* Returns true if the disableOpenSSL command line argument was specified.
*
* @return true if the disableOpenSSL command line argument was specified; otherwise false
*/
public boolean isOpenSSLDisabled() {
return (line != null) && line.hasOption(ARGUMENT.DISABLE_OPENSSL);
}
/**
* Returns true if the disableCentral command line argument was specified.
*
@@ -1033,6 +1046,10 @@ public final class CliParser {
* Disables the Nexus Analyzer.
*/
public static final String DISABLE_NEXUS = "disableNexus";
/**
* Disables the OpenSSL Analyzer.
*/
public static final String DISABLE_OPENSSL = "disableOpenSSL";
/**
* The URL of the nexus server.
*/

View File

@@ -30,6 +30,7 @@ Short | Argument Name        | Paramete
| \-\-disablePyDist | | Sets whether the Python Distribution Analyzer will be used. | false
| \-\-disablePyPkg | | Sets whether the Python Package Analyzer will be used. | false
| \-\-disableAutoconf | | Sets whether the Autoconf Analyzer will be used. | false
| \-\-disableOpenSSL | | Sets whether the OpenSSL Analyzer will be used. | false
| \-\-disableArchive | | Sets whether the Archive Analyzer will be used. | false
| \-\-zipExtensions | \<strings\> | A comma-separated list of additional file extensions to be treated like a ZIP file, the contents will be extracted and analyzed. | &nbsp;
| \-\-disableJar | | Sets whether the Jar Analyzer will be used. | false

View File

@@ -0,0 +1,172 @@
/*
* This file is part of dependency-check-core.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* Copyright (c) 2015 Institute for Defense Analyses. All Rights Reserved.
*/
package org.owasp.dependencycheck.analyzer;
import org.apache.commons.io.FileUtils;
import org.owasp.dependencycheck.Engine;
import org.owasp.dependencycheck.analyzer.exception.AnalysisException;
import org.owasp.dependencycheck.dependency.Confidence;
import org.owasp.dependencycheck.dependency.Dependency;
import org.owasp.dependencycheck.utils.FileFilterBuilder;
import org.owasp.dependencycheck.utils.Settings;
import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* Used to analyze OpenSSL source code present in the file system.
*
* @author Dale Visser <dvisser@ida.org>
*/
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";
/**
* Filter that detects files named "__init__.py".
*/
private static final FileFilter OPENSSLV_FILTER = FileFilterBuilder.newInstance().addFilenames(OPENSSLV_H).build();
private static final Pattern VERSION_PATTERN = Pattern.compile(
"define\\s+OPENSSL_VERSION_NUMBER\\s+0x([0-9a-zA-Z]{8})L", Pattern.DOTALL
| Pattern.CASE_INSENSITIVE);
private static final int MAJOR_OFFSET = 28;
private static final long MINOR_MASK = 0x0ff00000L;
private static final int MINOR_OFFSET = 20;
private static final long FIX_MASK = 0x000ff000L;
private static final int FIX_OFFSET = 12;
private static final long PATCH_MASK = 0x00000ff0L;
private static final int PATCH_OFFSET = 4;
private static final int NUM_LETTERS = 26;
private static final int STATUS_MASK = 0x0000000f;
static String getOpenSSLVersion(long openSSLVersionConstant) {
long major = openSSLVersionConstant >>> MAJOR_OFFSET;
long minor = (openSSLVersionConstant & MINOR_MASK) >>> MINOR_OFFSET;
long fix = (openSSLVersionConstant & FIX_MASK) >>> FIX_OFFSET;
long patchLevel = (openSSLVersionConstant & PATCH_MASK) >>> PATCH_OFFSET;
String patch = 0 == patchLevel || patchLevel > NUM_LETTERS ? "" :
String.valueOf((char) (patchLevel + 'a' - 1));
int statusCode = (int) (openSSLVersionConstant & STATUS_MASK);
String status = 0xf == statusCode ? "" :
(0 == statusCode ? "-dev" : "-beta" + statusCode);
return String.format("%d.%d.%d%s%s", major, minor, fix, patch, status);
}
/**
* Returns the name of the Python Package Analyzer.
*
* @return the name of the analyzer
*/
@Override
public String getName() {
return "OpenSSL Source Analyzer";
}
/**
* Tell that we are used for information collection.
*
* @return INFORMATION_COLLECTION
*/
@Override
public AnalysisPhase getAnalysisPhase() {
return AnalysisPhase.INFORMATION_COLLECTION;
}
/**
* Returns the set of supported file extensions.
*
* @return the set of supported file extensions
*/
@Override
protected FileFilter getFileFilter() {
return OPENSSLV_FILTER;
}
/**
* No-op initializer implementation.
*
* @throws Exception never thrown
*/
@Override
protected void initializeFileTypeAnalyzer() throws Exception {
// Nothing to do here.
}
/**
* Analyzes python packages and adds evidence to the dependency.
*
* @param dependency the dependency being analyzed
* @param engine the engine being used to perform the scan
* @throws AnalysisException thrown if there is an unrecoverable error analyzing the dependency
*/
@Override
protected void analyzeFileType(Dependency dependency, Engine engine)
throws AnalysisException {
final File file = dependency.getActualFile();
final String parentName = file.getParentFile().getName();
boolean found = false;
final String contents = getFileContents(file);
if (!contents.isEmpty()) {
final Matcher matcher = VERSION_PATTERN.matcher(contents);
if (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 + File.separatorChar + OPENSSLV_H);
dependency.getVendorEvidence().addEvidence(OPENSSLV_H, "Vendor", "OpenSSL", Confidence.HIGHEST);
dependency.getProductEvidence().addEvidence(OPENSSLV_H, "Product", "OpenSSL", Confidence.HIGHEST);
} else {
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 occurred while reading dependency file.", e);
}
return contents;
}
@Override
protected String getAnalyzerEnabledSettingKey() {
return Settings.KEYS.ANALYZER_OPENSSL_ENABLED;
}
}

View File

@@ -14,4 +14,5 @@ org.owasp.dependencycheck.analyzer.NuspecAnalyzer
org.owasp.dependencycheck.analyzer.AssemblyAnalyzer
org.owasp.dependencycheck.analyzer.PythonDistributionAnalyzer
org.owasp.dependencycheck.analyzer.PythonPackageAnalyzer
org.owasp.dependencycheck.analyzer.AutoconfAnalyzer
org.owasp.dependencycheck.analyzer.AutoconfAnalyzer
org.owasp.dependencycheck.analyzer.OpenSSLAnalyzer

View File

@@ -0,0 +1,119 @@
/*
* This file is part of dependency-check-core.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* Copyright (c) 2015 Institute for Defense Analyses. All Rights Reserved.
*/
package org.owasp.dependencycheck.analyzer;
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.io.File;
import static org.hamcrest.CoreMatchers.containsString;
import static org.junit.Assert.*;
/**
* Unit tests for OpenSSLAnalyzerAnalyzer.
*
* @author Dale Visser <dvisser@ida.org>
*/
public class OpenSSLAnalyzerTest extends BaseTest {
/**
* The package analyzer to test.
*/
OpenSSLAnalyzer analyzer;
/**
* Setup the PtyhonPackageAnalyzer.
*
* @throws Exception if there is a problem
*/
@Before
public void setUp() throws Exception {
analyzer = new OpenSSLAnalyzer();
analyzer.setFilesMatched(true);
analyzer.initialize();
}
/**
* Cleanup any resources used.
*
* @throws Exception if there is a problem
*/
@After
public void tearDown() throws Exception {
analyzer.close();
analyzer = null;
}
/**
* Test of getName method, of class OpenSSLAnalyzer.
*/
@Test
public void testGetName() {
assertEquals("Analyzer name wrong.", "OpenSSL Source Analyzer",
analyzer.getName());
}
/**
* Test of supportsExtension method, of class PythonPackageAnalyzer.
*/
@Test
public void testAccept() {
assertTrue("Should support files named \"opensslv.h\".",
analyzer.accept(new File("opensslv.h")));
}
@Test
public void testVersionConstantExamples() {
final long[] constants = {0x1000203fL
, 0x00903000
, 0x00903001
, 0x00903002l
, 0x0090300f
, 0x0090301f
, 0x0090400f
, 0x102031af};
final String[] versions = {"1.0.2c",
"0.9.3-dev",
"0.9.3-beta1",
"0.9.3-beta2",
"0.9.3",
"0.9.3a",
"0.9.4",
"1.2.3z"};
assertEquals(constants.length, versions.length);
for (int i = 0; i < constants.length; i++) {
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"));
}
}

View File

@@ -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 */

View File

@@ -225,6 +225,10 @@ public final class Settings {
* The properties key for whether the Central analyzer is enabled.
*/
public static final String ANALYZER_CENTRAL_ENABLED = "analyzer.central.enabled";
/**
* The properties key for whether the OpenSSL analyzer is enabled.
*/
public static final String ANALYZER_OPENSSL_ENABLED = "analyzer.openssl.enabled";
/**
* The properties key for the Central search URL.
*/

View File

@@ -0,0 +1,11 @@
Autoconf Analyzer
=================
OWASP dependency-check includes an analyzer that will scan Autoconf project
configuration files. The analyzer will collect as much information it can
about the project. The information collected is internally referred to as
evidence and is grouped into vendor, product, and version buckets. Other
analyzers later use this evidence to identify any Common Platform Enumeration
(CPE) identifiers that apply.
File names scanned: configure, configure.in, configure.ac

View File

@@ -5,8 +5,10 @@ to extract identification information from the files analyzed.
- [Archive Analyzer](./archive-analyzer.html)
- [Assembly Analyzer](./assembly-analyzer.html)
- [Autoconf Analyzer](./autoconf-analyzer.html)
- [Central Analyzer](./central-analyzer.html)
- [Jar Analyzer](./jar-analyzer.html)
- [Nexus Analyzer](./nexus-analyzer.html)
- [Nuspec Analyzer](./nuspec-analyzer.html)
- [OpenSSL Analyzer](./openssl-analyzer.html)
- [Python Analyzer](./python-analyzer.html)

View File

@@ -0,0 +1,10 @@
OpenSSL Analyzer
================
OWASP dependency-check includes an analyzer that will scan OpenSSL source code
files for the OpenSSL version information. The information collected is
internally referred to as evidence and is grouped into vendor, product, and
version buckets. Other analyzers later use this evidence to identify any
Common Platform Enumeration (CPE) identifiers that apply.
File names scanned: opensslv.h

View File

@@ -4,7 +4,9 @@ OWASP dependency-check is an open source solution the OWASP Top 10 2013 entry:
[A9 - Using Components with Known Vulnerabilities](https://www.owasp.org/index.php/Top_10_2013-A9-Using_Components_with_Known_Vulnerabilities).
Dependency-check can currently be used to scan Java, .NET, and Python
applications (and their dependent libraries) to identify known vulnerable
components.
components. In addition, Dependency-check can be used to scan some source
code, including OpenSSL source code and source code for projects that use
Autoconf.
The problem with using known vulnerable components was covered in a paper by
Jeff Williams and Arshan Dabirsiaghi titled, "[The Unfortunate Reality of

View File

@@ -124,6 +124,12 @@ Copyright (c) 2013 Jeremy Long. All Rights Reserved.
<item name="Nuspec Analyzer" href="./analyzers/nuspec-analyzer.html">
<description>Nuspec Analyzer</description>
</item>
<item name="Autoconf Analyzer" href="./analyzers/autoconf-analyzer.html">
<description>Autoconf Analyzer</description>
</item>
<item name="OpenSSL Analyzer" href="./analyzers/openssl-analyzer.html">
<description>OpenSSL Analyzer</description>
</item>
</item>
<item collapse="true" name="Modules" href="./modules.html">
<item name="dependency-check-cli" href="./dependency-check-cli/index.html">