cloud http client with proxy

This commit is contained in:
Greg DiCristofaro 2023-07-20 16:07:59 -04:00
parent 0c3394e88f
commit 38ad881023
9 changed files with 565 additions and 45 deletions

View File

@ -0,0 +1,378 @@
/** *************************************************************************
** This data and information is proprietary to, and a valuable trade secret
** of, Basis Technology Corp. It is given in confidence by Basis Technology
** and may only be used as permitted under the license agreement under which
** it has been distributed, and in no other way.
**
** Copyright (c) 2023 Basis Technology Corp. All rights reserved.
**
** The technical data and information provided herein are provided with
** `limited rights', and the computer software provided herein is provided
** with `restricted rights' as those terms are defined in DAR and ASPR
** 7-104.9(a).
************************************************************************** */
package com.basistech.df.cybertriage.autopsy.ctapi;
import com.basistech.df.cybertriage.autopsy.ctapi.util.ObjectMapperUtil;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.IOException;
import java.net.Authenticator;
import java.net.InetAddress;
import java.net.PasswordAuthentication;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.UnknownHostException;
import java.security.NoSuchAlgorithmException;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.logging.Level;
import javax.net.ssl.SSLContext;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.HttpEntity;
import org.apache.http.HttpHost;
import org.apache.http.HttpStatus;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.NTCredentials;
import org.apache.http.client.CredentialsProvider;
import org.apache.http.client.config.AuthSchemes;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.util.EntityUtils;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.entity.StringEntity;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.client.SystemDefaultCredentialsProvider;
import org.apache.http.impl.client.WinHttpClients;
/**
* Actually makes the http requests to CT cloud.
*/
public class CTCloudHttpClient {
private static final CTCloudHttpClient instance = new CTCloudHttpClient();
private static final Logger LOGGER = Logger.getLogger(CTCloudHttpClient.class.getName());
private static final List<String> DEFAULT_SCHEME_PRIORITY
= new ArrayList<>(Arrays.asList(
AuthSchemes.SPNEGO,
AuthSchemes.KERBEROS,
AuthSchemes.NTLM,
AuthSchemes.CREDSSP,
AuthSchemes.DIGEST,
AuthSchemes.BASIC));
private static final int CONNECTION_TIMEOUT_MS = 58 * 1000; // milli sec
public static CTCloudHttpClient getInstance() {
return instance;
}
private final ObjectMapper mapper = ObjectMapperUtil.getInstance().getDefaultObjectMapper();
private final SSLContext sslContext;
private String hostName = null;
private CTCloudHttpClient() {
SSLContext tmpSslContext;
try {
tmpSslContext = SSLContext.getInstance("TLSv1.2");
} catch (NoSuchAlgorithmException ex) {
LOGGER.log(Level.WARNING, "Unable to setup ssl context instance", ex);
tmpSslContext = null;
}
this.sslContext = tmpSslContext;
}
private ProxySettingArgs getProxySettings() {
if (StringUtils.isBlank(hostName)) {
try {
hostName = InetAddress.getLocalHost().getCanonicalHostName();
} catch (UnknownHostException ex) {
LOGGER.log(Level.WARNING, "An error occurred while fetching the hostname", ex);
}
}
int proxyPort = 0;
try {
proxyPort = Integer.parseInt(ProxySettings.getHttpsPort());
} catch (NumberFormatException ex) {
LOGGER.log(Level.WARNING, "An exception occurred while converting port number to integer", ex);
}
return new ProxySettingArgs(
ProxySettings.getProxyType() != ProxySettings.DIRECT_CONNECTION,
hostName,
ProxySettings.getHttpsHost(),
proxyPort,
ProxySettings.getAuthenticationUsername(),
ProxySettings.getAuthenticationPassword(),
null
);
}
public <O> O doPost(String urlPath, Object jsonBody, Class<O> classType) throws CTCloudException {
String url = Constants.CT_CLOUD_SERVER + urlPath;
try {
LOGGER.log(Level.INFO, "initiating http connection to ctcloud server");
try (CloseableHttpClient httpclient = createConnection(getProxySettings(), sslContext)) {
URIBuilder builder = new URIBuilder(url);
URI postURI = builder.build();
HttpPost postRequest = new HttpPost(postURI);
configureRequestTimeout(postRequest);
postRequest.setHeader("Content-type", "application/json");
if (jsonBody != null) {
String requestBody = mapper.writeValueAsString(jsonBody);
if (StringUtils.isNotBlank(requestBody)) {
HttpEntity entity = new StringEntity(requestBody, "UTF-8");
postRequest.setEntity(entity);
}
}
LOGGER.log(Level.INFO, "initiating http post request to ctcloud server " + postRequest.getURI());
try (CloseableHttpResponse response = httpclient.execute(postRequest)) {
if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
LOGGER.log(Level.INFO, "Response Received. - Status OK");
// Parse Response
HttpEntity entity = response.getEntity();
String entityStr = EntityUtils.toString(entity);
O respObj = mapper.readValue(entityStr, classType);
return respObj;
} else {
LOGGER.log(Level.WARNING, "Response Received. - Status Error {}", response.getStatusLine());
handleNonOKResponse(response, "");
}
} catch (Exception ex) {
LOGGER.log(Level.WARNING, "Error when parsing response from CyberTriage Cloud", ex);
throw new CTCloudException(CTCloudException.parseUnknownException(ex), ex);
}
}
} catch (IOException ex) {
LOGGER.log(Level.WARNING, "IO Exception raised when connecting to CT Cloud using " + url, ex);
throw new CTCloudException(CTCloudException.ErrorCode.NETWORK_ERROR, ex);
} catch (URISyntaxException ex) {
LOGGER.log(Level.WARNING, "Wrong URL syntax for CT Cloud " + url, ex);
throw new CTCloudException(CTCloudException.ErrorCode.UNKNOWN, ex);
}
return null;
}
/**
* A generic way to handle the HTTP response - when the response code is NOT
* 200 OK.
*
* @param response
* @param fileName - used only for logging.
* @throws MalwareScannerException
* @throws IOException
*/
private void handleNonOKResponse(CloseableHttpResponse response, String fileName) throws CTCloudException, IOException {
LOGGER.log(Level.WARNING, MessageFormat.format(
"Response code {0}. Message Body {1}",
response.getStatusLine().getStatusCode(),
EntityUtils.toString(response.getEntity())));
switch (response.getStatusLine().getStatusCode()) {
case HttpStatus.SC_BAD_REQUEST:
//400: Bad request => Unsupported HTTP method or invalid http request (e.g., empty body).
throw new CTCloudException(CTCloudException.ErrorCode.BAD_REQUEST);
case HttpStatus.SC_UNAUTHORIZED:
//401 Invalid API key => An invalid API key, or no API key, has been provided
throw new CTCloudException(CTCloudException.ErrorCode.INVALID_KEY);
case HttpStatus.SC_PROXY_AUTHENTICATION_REQUIRED:
// 407 Proxy server authentication required.
throw new CTCloudException(CTCloudException.ErrorCode.PROXY_UNAUTHORIZED);
case HttpStatus.SC_FORBIDDEN:
throw new CTCloudException(CTCloudException.ErrorCode.UN_AUTHORIZED);
case HttpStatus.SC_INTERNAL_SERVER_ERROR:
//500 Internal error Server temporarily unavailable; please try again later. If the issue persists, please contact RL.
throw new CTCloudException(CTCloudException.ErrorCode.TEMP_UNAVAILABLE);
case HttpStatus.SC_SERVICE_UNAVAILABLE:
//503 Server is too busy. Try again later.
//503 Failed to request scan. Try again later. The server is currently unable to handle the request due to a temporary overloading or maintenance of the server. If the issue persists, please contact RL.
throw new CTCloudException(CTCloudException.ErrorCode.TEMP_UNAVAILABLE);
case HttpStatus.SC_GATEWAY_TIMEOUT:
throw new CTCloudException(CTCloudException.ErrorCode.GATEWAY_TIMEOUT);
default:
String returnData = EntityUtils.toString(response.getEntity());
LOGGER.log(Level.WARNING, MessageFormat.format("upload response content for {0}:\n {1}", fileName, returnData));
throw new CTCloudException(CTCloudException.ErrorCode.NETWORK_ERROR);
}
}
/**
* NOTE That this is not a perfect solution as timeouts set this way does
* not terminate a connection forcefully after a specified interval. so if
* there is data streaming in from the server at a small speed the
* connection will be kept open.
*
* @param request
*/
private void configureRequestTimeout(HttpRequestBase request) {
RequestConfig config = RequestConfig.custom()
.setConnectionRequestTimeout(CONNECTION_TIMEOUT_MS)
.setConnectTimeout(CONNECTION_TIMEOUT_MS)
.setSocketTimeout(CONNECTION_TIMEOUT_MS)
.build();
request.setConfig(config);
}
/**
* Creates and returns a CloseableHttpClient SYSTEM and MANUAL looks up from
* runtime proxy config settings. These are updated accordingly from the
* Proxy Config UI. This allows us to keep the CreateConnection call fairly
* simple and not have to deal with the System Proxy settings and such.
*
* @return
*/
private static CloseableHttpClient createConnection(ProxySettingArgs proxySettings, SSLContext sslContext) {
HttpClientBuilder builder = getHttpClientBuilder(proxySettings);
if (sslContext != null) {
builder.setSSLContext(sslContext);
}
return builder.build();
}
private static HttpClientBuilder getHttpClientBuilder(ProxySettingArgs proxySettings) {
if (proxySettings.isSystemOrManualProxy()) {
Authenticator.setDefault(new Authenticator() {
@Override
protected PasswordAuthentication getPasswordAuthentication() {
LOGGER.info("Requesting Password Authentication...");
return super.getPasswordAuthentication();
}
});
HttpClientBuilder builder = null;
HttpHost proxyHost = null;
CredentialsProvider proxyCredsProvider = null;
RequestConfig config = null;
if (Objects.nonNull(proxySettings.getProxyHostname()) && proxySettings.getProxyPort() > 0) {
proxyHost = new HttpHost(proxySettings.getProxyHostname(), proxySettings.getProxyPort());
proxyCredsProvider = getProxyCredentialsProvider(proxySettings);
if (StringUtils.isNotBlank(proxySettings.getAuthScheme())) {
if (!DEFAULT_SCHEME_PRIORITY.get(0).equalsIgnoreCase(proxySettings.getAuthScheme())) {
DEFAULT_SCHEME_PRIORITY.removeIf(s -> s.equalsIgnoreCase(proxySettings.getAuthScheme()));
DEFAULT_SCHEME_PRIORITY.add(0, proxySettings.getAuthScheme());
}
}
config = RequestConfig.custom().setProxyPreferredAuthSchemes(DEFAULT_SCHEME_PRIORITY).build();
}
if (Objects.isNull(proxyCredsProvider) && WinHttpClients.isWinAuthAvailable()) {
builder = WinHttpClients.custom();
builder.useSystemProperties();
LOGGER.log(Level.WARNING, "Using Win HTTP Client");
} else {
builder = HttpClients.custom();
builder.setDefaultRequestConfig(config);
if (Objects.nonNull(proxyCredsProvider)) { // make sure non null proxycreds before setting it
builder.setDefaultCredentialsProvider(proxyCredsProvider);
}
LOGGER.log(Level.WARNING, "Using default http client");
}
if (Objects.nonNull(proxyHost)) {
builder.setProxy(proxyHost);
LOGGER.log(Level.WARNING, MessageFormat.format("Using proxy {0}", proxyHost));
}
return builder;
} else {
return HttpClients.custom();
}
}
/**
* Returns a CredentialsProvider for proxy, if one is configured.
*
* @return CredentialsProvider, if a proxy is configured with credentials,
* null otherwise
*/
private static CredentialsProvider getProxyCredentialsProvider(ProxySettingArgs proxySettings) {
CredentialsProvider proxyCredsProvider = null;
if (proxySettings.isSystemOrManualProxy()) {
if (StringUtils.isNotBlank(proxySettings.getProxyUserId())) {
if (null != proxySettings.getProxyPassword() && proxySettings.getProxyPassword().length > 0) { // Password will be blank for KERBEROS / NEGOTIATE schemes.
proxyCredsProvider = new SystemDefaultCredentialsProvider();
String userId = proxySettings.getProxyUserId();
String domain = null;
if (userId.contains("\\")) {
domain = userId.split("\\\\")[0];
userId = userId.split("\\\\")[1];
}
String workStation = proxySettings.getHostName();
proxyCredsProvider.setCredentials(new AuthScope(proxySettings.getProxyHostname(), proxySettings.getProxyPort()),
new NTCredentials(userId, new String(proxySettings.getProxyPassword()), workStation, domain));
}
}
}
return proxyCredsProvider;
}
private static class ProxySettingArgs {
private final boolean systemOrManualProxy;
private final String hostName;
private final String proxyHostname;
private final int proxyPort;
private final String proxyUserId;
private final char[] proxyPassword;
private final String authScheme;
ProxySettingArgs(boolean systemOrManualProxy, String hostName, String proxyHostname, int proxyPort, String proxyUserId, char[] proxyPassword, String authScheme) {
this.systemOrManualProxy = systemOrManualProxy;
this.hostName = hostName;
this.proxyHostname = proxyHostname;
this.proxyPort = proxyPort;
this.proxyUserId = proxyUserId;
this.proxyPassword = proxyPassword;
this.authScheme = authScheme;
}
boolean isSystemOrManualProxy() {
return systemOrManualProxy;
}
String getHostName() {
return hostName;
}
String getProxyHostname() {
return proxyHostname;
}
int getProxyPort() {
return proxyPort;
}
String getProxyUserId() {
return proxyUserId;
}
char[] getProxyPassword() {
return proxyPassword;
}
public String getAuthScheme() {
return authScheme;
}
}
}

View File

@ -15,50 +15,52 @@ package com.basistech.df.cybertriage.autopsy.ctapi;
import com.basistech.df.cybertriage.autopsy.ctapi.json.AuthTokenRequest; import com.basistech.df.cybertriage.autopsy.ctapi.json.AuthTokenRequest;
import com.basistech.df.cybertriage.autopsy.ctapi.json.AuthTokenResponse; import com.basistech.df.cybertriage.autopsy.ctapi.json.AuthTokenResponse;
import com.basistech.df.cybertriage.autopsy.ctapi.json.AuthenticatedRequestData;
import com.basistech.df.cybertriage.autopsy.ctapi.json.FileReputationRequest;
import com.basistech.df.cybertriage.autopsy.ctapi.json.FileReputationResponse;
import com.basistech.df.cybertriage.autopsy.ctapi.json.FileReputationResult; import com.basistech.df.cybertriage.autopsy.ctapi.json.FileReputationResult;
import com.basistech.df.cybertriage.autopsy.ctapi.json.LicenseRequest; import com.basistech.df.cybertriage.autopsy.ctapi.json.LicenseRequest;
import com.basistech.df.cybertriage.autopsy.ctapi.json.LicenseResponse; import com.basistech.df.cybertriage.autopsy.ctapi.json.LicenseResponse;
import com.basistech.df.cybertriage.autopsy.ctapi.util.CTHostIDGenerationUtil; import com.basistech.df.cybertriage.autopsy.ctapi.util.CTHostIDGenerationUtil;
import com.basistech.df.cybertriage.autopsy.ctapi.util.ObjectMapperUtil; import java.util.Collections;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.util.List; import java.util.List;
import org.apache.commons.collections.CollectionUtils;
import org.sleuthkit.autopsy.coreutils.Version; import org.sleuthkit.autopsy.coreutils.Version;
/** /**
* *
* Data access layer for handling the CT api. * Data access layer for handling the CT api.
*/ */
public class CtApiDAO { public class CTApiDAO {
private static final String LICENSE_REQUEST_PATH = "/_ah/api/license/v1/activate"; private static final String LICENSE_REQUEST_PATH = "/_ah/api/license/v1/activate";
private static final String AUTH_TOKEN_REQUEST_PATH = "/_ah/api/auth/v2/generate_token"; private static final String AUTH_TOKEN_REQUEST_PATH = "/_ah/api/auth/v2/generate_token";
private static final String CTCLOUD_SERVER_HASH_PATH = "/_ah/api/reputation/v1/query/file/hash/md5?query_types=CORRELATION,MALWARE";
private static final CtApiDAO instance = new CtApiDAO(); private static final CTApiDAO instance = new CTApiDAO();
private final ObjectMapper mapper = ObjectMapperUtil.getInstance().getDefaultObjectMapper();
private CtApiDAO() { private CTApiDAO() {
} }
public static CtApiDAO getInstance() { public static CTApiDAO getInstance() {
return instance; return instance;
} }
private static String getAppVersion() { private static String getAppVersion() {
return Version.getName() + " " + Version.getVersion(); return Version.getName() + " " + Version.getVersion();
} }
private final CTCloudHttpClient httpClient = CTCloudHttpClient.getInstance();
private <T> T doPost(String urlPath, Object requestBody, Class<T> responseTypeRef) throws CTCloudException {
return null;
// TODO
}
public LicenseResponse getLicenseInfo(String licenseString) throws CTCloudException { public LicenseResponse getLicenseInfo(String licenseString) throws CTCloudException {
LicenseRequest licenseRequest = new LicenseRequest() LicenseRequest licenseRequest = new LicenseRequest()
.setBoostLicenseCode(licenseString) .setBoostLicenseCode(licenseString)
.setHostId(CTHostIDGenerationUtil.generateLicenseHostID()) .setHostId(CTHostIDGenerationUtil.generateLicenseHostID())
.setProduct(getAppVersion()); .setProduct(getAppVersion());
return doPost(LICENSE_REQUEST_PATH, licenseRequest, LicenseResponse.class); return httpClient.doPost(LICENSE_REQUEST_PATH, licenseRequest, LicenseResponse.class);
} }
@ -67,14 +69,25 @@ public class CtApiDAO {
.setAutopsyVersion(getAppVersion()) .setAutopsyVersion(getAppVersion())
.setRequestFileUpload(true) .setRequestFileUpload(true)
.setBoostLicenseId(boostLicenseId); .setBoostLicenseId(boostLicenseId);
return doPost(AUTH_TOKEN_REQUEST_PATH, authTokenRequest, AuthTokenResponse.class); return httpClient.doPost(AUTH_TOKEN_REQUEST_PATH, authTokenRequest, AuthTokenResponse.class);
} }
public List<FileReputationResult> getReputationResults(String authToken, List<String> md5Hashes) throws CTCloudException { public List<FileReputationResult> getReputationResults(AuthenticatedRequestData authenticatedRequestData, List<String> md5Hashes) throws CTCloudException {
// TODO if (CollectionUtils.isEmpty(md5Hashes)) {
// return cloudServiceApi.lookupFileResults(md5Hashes, HashTypes.md5); return Collections.emptyList();
return null; }
FileReputationRequest fileRepReq = new FileReputationRequest()
.setApiKey(authenticatedRequestData.getApiKey())
.setHostId(CTHostIDGenerationUtil.generateLicenseHostID())
.setToken(authenticatedRequestData.getToken())
.setHashes(md5Hashes);
FileReputationResponse resp = httpClient.doPost(CTCLOUD_SERVER_HASH_PATH, fileRepReq, FileReputationResponse.class);
return resp == null || resp.getItems() == null
? Collections.emptyList()
: resp.getItems();
} }
public enum ResultType { public enum ResultType {

View File

@ -20,9 +20,7 @@ import java.time.ZonedDateTime;
/** /**
* POJO for an auth token response. * POJO for an auth token response.
*/ */
public class AuthTokenResponse { public class AuthTokenResponse extends AuthenticatedRequestData {
private final String token;
private final String apiKey;
private final Long hashLookupCount; private final Long hashLookupCount;
private final Long hashLookupLimit; private final Long hashLookupLimit;
private final Long fileUploadLimit; private final Long fileUploadLimit;
@ -51,14 +49,6 @@ public class AuthTokenResponse {
this.expiration = expiration; this.expiration = expiration;
} }
public String getToken() {
return token;
}
public String getApiKey() {
return apiKey;
}
public Long getHashLookupCount() { public Long getHashLookupCount() {
return hashLookupCount; return hashLookupCount;
} }

View File

@ -0,0 +1,36 @@
/** *************************************************************************
** This data and information is proprietary to, and a valuable trade secret
** of, Basis Technology Corp. It is given in confidence by Basis Technology
** and may only be used as permitted under the license agreement under which
** it has been distributed, and in no other way.
**
** Copyright (c) 2023 Basis Technology Corp. All rights reserved.
**
** The technical data and information provided herein are provided with
** `limited rights', and the computer software provided herein is provided
** with `restricted rights' as those terms are defined in DAR and ASPR
** 7-104.9(a).
************************************************************************** */
package com.basistech.df.cybertriage.autopsy.ctapi.json;
import com.fasterxml.jackson.annotation.JsonProperty;
/**
* Data required for an authenticated request.
*/
public abstract class AuthenticatedRequestData {
@JsonProperty("token")
protected String token;
@JsonProperty("api_key")
protected String apiKey;
public String getToken() {
return token;
}
public String getApiKey() {
return apiKey;
}
}

View File

@ -0,0 +1,58 @@
/** *************************************************************************
** This data and information is proprietary to, and a valuable trade secret
** of, Basis Technology Corp. It is given in confidence by Basis Technology
** and may only be used as permitted under the license agreement under which
** it has been distributed, and in no other way.
**
** Copyright (c) 2023 Basis Technology Corp. All rights reserved.
**
** The technical data and information provided herein are provided with
** `limited rights', and the computer software provided herein is provided
** with `restricted rights' as those terms are defined in DAR and ASPR
** 7-104.9(a).
************************************************************************** */
package com.basistech.df.cybertriage.autopsy.ctapi.json;
import com.fasterxml.jackson.annotation.JsonProperty;
import java.util.List;
/**
* Request for file reputation results.
*/
public class FileReputationRequest extends AuthenticatedRequestData {
@JsonProperty("hashes")
private List<String> hashes;
@JsonProperty("host_id")
private String hostId;
public List<String> getHashes() {
return hashes;
}
public FileReputationRequest setHashes(List<String> hashes) {
this.hashes = hashes;
return this;
}
public FileReputationRequest setToken(String token) {
this.token = token;
return this;
}
public FileReputationRequest setApiKey(String apiKey) {
this.apiKey = apiKey;
return this;
}
public String getHostId() {
return hostId;
}
public FileReputationRequest setHostId(String hostId) {
this.hostId = hostId;
return this;
}
}

View File

@ -0,0 +1,38 @@
/** *************************************************************************
** This data and information is proprietary to, and a valuable trade secret
** of, Basis Technology Corp. It is given in confidence by Basis Technology
** and may only be used as permitted under the license agreement under which
** it has been distributed, and in no other way.
**
** Copyright (c) 2023 Basis Technology Corp. All rights reserved.
**
** The technical data and information provided herein are provided with
** `limited rights', and the computer software provided herein is provided
** with `restricted rights' as those terms are defined in DAR and ASPR
** 7-104.9(a).
************************************************************************** */
package com.basistech.df.cybertriage.autopsy.ctapi.json;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import java.util.List;
/**
* Container for file reputation result list response.
*/
public class FileReputationResponse {
private final List<FileReputationResult> items;
@JsonCreator
public FileReputationResponse(
@JsonProperty("items") List<FileReputationResult> items
) {
this.items = items;
}
public List<FileReputationResult> getItems() {
return items;
}
}

View File

@ -29,7 +29,7 @@ public class CTHostIDGenerationUtil {
private static final Logger LOGGER = Logger.getLogger(CTHostIDGenerationUtil.class.getName()); private static final Logger LOGGER = Logger.getLogger(CTHostIDGenerationUtil.class.getName());
private static final String USER_NAME = System.getProperty("user.name"); private static final String USER_NAME = System.getProperty("user.name");
private static String HOST_NAME = ""; private static String cachedId = "";
/** /**
* Host ID Algorithm: Get MAC address from License4J. Get MD5 hash of it and * Host ID Algorithm: Get MAC address from License4J. Get MD5 hash of it and
@ -41,17 +41,24 @@ public class CTHostIDGenerationUtil {
* @return * @return
*/ */
public static String generateLicenseHostID() { public static String generateLicenseHostID() {
if (StringUtils.isBlank(HOST_NAME)) { if (StringUtils.isBlank(cachedId)) {
try { try {
HOST_NAME = StringUtils.defaultString(InetAddress.getLocalHost().getCanonicalHostName()); String hostName = StringUtils.defaultString(InetAddress.getLocalHost().getCanonicalHostName());
String macAddressMd5 = StringUtils.isNotBlank(HardwareID.getHardwareIDFromEthernetAddress())
? Md5HashUtil.getMD5MessageDigest(HardwareID.getHardwareIDFromEthernetAddress()).substring(0, 16)
: Md5HashUtil.getMD5MessageDigest(hostName).substring(0, 16);
String usernameMd5 = StringUtils.isNotBlank(USER_NAME)
? Md5HashUtil.getMD5MessageDigest(USER_NAME).substring(0, 16)
: Md5HashUtil.getMD5MessageDigest(hostName).substring(0, 16);
cachedId = macAddressMd5 + "_" + usernameMd5;
} catch (UnknownHostException ex) { } catch (UnknownHostException ex) {
LOGGER.log(Level.WARNING, "UNable to determine host name.", ex); LOGGER.log(Level.WARNING, "Unable to determine host name.", ex);
} }
} }
String macAddressMd5 = StringUtils.isNotBlank(HardwareID.getHardwareIDFromEthernetAddress()) ? Md5HashUtil.getMD5MessageDigest(HardwareID.getHardwareIDFromEthernetAddress()).substring(0, 16) : Md5HashUtil.getMD5MessageDigest(HOST_NAME).substring(0, 16);
String usernameMd5 = StringUtils.isNotBlank(USER_NAME) ? Md5HashUtil.getMD5MessageDigest(USER_NAME).substring(0, 16) : Md5HashUtil.getMD5MessageDigest(HOST_NAME).substring(0, 16); return cachedId;
String md5 = macAddressMd5 + "_" + usernameMd5;
return md5;
} }
} }

View File

@ -15,7 +15,7 @@ package com.basistech.df.cybertriage.autopsy.ctoptions.ctcloud;
import com.basistech.df.cybertriage.autopsy.ctoptions.subpanel.CTOptionsSubPanel; import com.basistech.df.cybertriage.autopsy.ctoptions.subpanel.CTOptionsSubPanel;
import com.basistech.df.cybertriage.autopsy.ctapi.CTCloudException; import com.basistech.df.cybertriage.autopsy.ctapi.CTCloudException;
import com.basistech.df.cybertriage.autopsy.ctapi.CtApiDAO; import com.basistech.df.cybertriage.autopsy.ctapi.CTApiDAO;
import com.basistech.df.cybertriage.autopsy.ctapi.json.AuthTokenResponse; import com.basistech.df.cybertriage.autopsy.ctapi.json.AuthTokenResponse;
import com.basistech.df.cybertriage.autopsy.ctapi.json.LicenseInfo; import com.basistech.df.cybertriage.autopsy.ctapi.json.LicenseInfo;
import com.basistech.df.cybertriage.autopsy.ctapi.json.LicenseResponse; import com.basistech.df.cybertriage.autopsy.ctapi.json.LicenseResponse;
@ -46,7 +46,7 @@ public class CTMalwareScannerOptionsPanel extends CTOptionsSubPanel {
private static final SimpleDateFormat LICENSE_EXPIRES_FORMAT = new SimpleDateFormat("MMMM d, YYYY"); private static final SimpleDateFormat LICENSE_EXPIRES_FORMAT = new SimpleDateFormat("MMMM d, YYYY");
private static final SimpleDateFormat MALWARE_SCANS_RESET_FORMAT = new SimpleDateFormat("MMM d, YYYY' at 'h:mma"); private static final SimpleDateFormat MALWARE_SCANS_RESET_FORMAT = new SimpleDateFormat("MMM d, YYYY' at 'h:mma");
private final CtApiDAO ctApiDAO = CtApiDAO.getInstance(); private final CTApiDAO ctApiDAO = CTApiDAO.getInstance();
private final CTLicensePersistence ctPersistence = CTLicensePersistence.getInstance(); private final CTLicensePersistence ctPersistence = CTLicensePersistence.getInstance();
private volatile LicenseInfo licenseInfo = null; private volatile LicenseInfo licenseInfo = null;

View File

@ -13,7 +13,7 @@
************************************************************************** */ ************************************************************************** */
package com.basistech.df.cybertriage.autopsy.malwarescan; package com.basistech.df.cybertriage.autopsy.malwarescan;
import com.basistech.df.cybertriage.autopsy.ctapi.CtApiDAO; import com.basistech.df.cybertriage.autopsy.ctapi.CTApiDAO;
import com.basistech.df.cybertriage.autopsy.ctapi.json.AuthTokenResponse; import com.basistech.df.cybertriage.autopsy.ctapi.json.AuthTokenResponse;
import com.basistech.df.cybertriage.autopsy.ctapi.json.FileReputationResult; import com.basistech.df.cybertriage.autopsy.ctapi.json.FileReputationResult;
import com.basistech.df.cybertriage.autopsy.ctapi.json.LicenseInfo; import com.basistech.df.cybertriage.autopsy.ctapi.json.LicenseInfo;
@ -101,7 +101,7 @@ public class MalwareScanIngestModule implements FileIngestModule {
private final BatchProcessor<FileRecord> batchProcessor = new BatchProcessor<FileRecord>(BATCH_SIZE, BATCH_MILLIS_TIMEOUT, this::handleBatch); private final BatchProcessor<FileRecord> batchProcessor = new BatchProcessor<FileRecord>(BATCH_SIZE, BATCH_MILLIS_TIMEOUT, this::handleBatch);
private final CTLicensePersistence ctSettingsPersistence = CTLicensePersistence.getInstance(); private final CTLicensePersistence ctSettingsPersistence = CTLicensePersistence.getInstance();
private final CtApiDAO ctApiDAO = CtApiDAO.getInstance(); private final CTApiDAO ctApiDAO = CTApiDAO.getInstance();
private FileTypeDetector fileTypeDetector; private FileTypeDetector fileTypeDetector;
private RunState runState = null; private RunState runState = null;
@ -256,7 +256,7 @@ public class MalwareScanIngestModule implements FileIngestModule {
} }
// using auth token, get results // using auth token, get results
List<FileReputationResult> repResult = ctApiDAO.getReputationResults(authTokenResponse.getToken(), md5Hashes); List<FileReputationResult> repResult = ctApiDAO.getReputationResults(authTokenResponse, md5Hashes);
if (repResult != null && !repResult.isEmpty()) { if (repResult != null && !repResult.isEmpty()) {
SleuthkitCase.CaseDbTransaction trans = null; SleuthkitCase.CaseDbTransaction trans = null;