package org.ovirt.engine.core.vdsbroker.attestation; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.net.URL; import java.security.KeyStore; import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; import java.security.cert.CertificateException; import java.util.ArrayList; import java.util.List; import org.apache.commons.httpclient.HttpClient; import org.apache.commons.httpclient.methods.PostMethod; import org.apache.commons.httpclient.methods.StringRequestEntity; import org.apache.commons.httpclient.protocol.Protocol; import org.apache.commons.httpclient.protocol.ProtocolSocketFactory; import org.codehaus.jackson.JsonNode; import org.codehaus.jackson.JsonParseException; import org.codehaus.jackson.JsonProcessingException; import org.codehaus.jackson.map.ObjectMapper; import org.ovirt.engine.core.common.businessentities.AttestationResultEnum; import org.ovirt.engine.core.common.config.Config; import org.ovirt.engine.core.common.config.ConfigValues; import org.ovirt.engine.core.utils.ssl.AuthSSLProtocolSocketFactory; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class AttestationService { private static final String HEADER_HOSTS = "hosts"; private static final String HEADER_HOST_NAME = "host_name"; private static final String HEADER_RESULT = "trust_lvl"; private static final String HEADER_VTIME = "vtime"; private static final String CONTENT_TYPE = "application/json"; private static final AttestationService instance = new AttestationService(); private static final Logger log = LoggerFactory.getLogger(AttestationService.class); public static HttpClient getClient() { HttpClient httpClient = new HttpClient(); if (Config.getValue(ConfigValues.SecureConnectionWithOATServers)) { URL trustStoreUrl; try { int port = Config.getValue(ConfigValues.AttestationPort); trustStoreUrl = new URL("file://" + Config.resolveAttestationTrustStorePath()); String truststorePassword = Config.getValue(ConfigValues.AttestationTruststorePass); String attestationServer = Config.getValue(ConfigValues.AttestationServer); // registering the https protocol with a socket factory that // provides client authentication. ProtocolSocketFactory factory = new AuthSSLProtocolSocketFactory(getTrustStore(trustStoreUrl.getPath(), truststorePassword), Config.getValue(ConfigValues.ExternalCommunicationProtocol)); Protocol clientAuthHTTPS = new Protocol("https", factory, port); httpClient.getHostConfiguration().setHost(attestationServer, port, clientAuthHTTPS); } catch (Exception e) { log.error("Failed to init AuthSSLProtocolSocketFactory. SSL connections will not work: {}", e.getMessage()); log.debug("Exception", e); } } return httpClient; } private static KeyStore getTrustStore(String filePath, String password) throws IOException, KeyStoreException, CertificateException, NoSuchAlgorithmException { KeyStore ks; try (InputStream in = new FileInputStream(filePath)) { ks = KeyStore.getInstance("JKS"); ks.load(in, password.toCharArray()); } return ks; } public static AttestationService getInstance() { return instance; } private AttestationService() { } public List<AttestationValue> attestHosts(List<String> hosts) { String pollURI = Config.getValue(ConfigValues.PollUri); List<AttestationValue> values = new ArrayList<>(); PostMethod postMethod = new PostMethod("/" + pollURI); try { postMethod.setRequestEntity(new StringRequestEntity( writeListJson(hosts))); postMethod.addRequestHeader("Accept", CONTENT_TYPE); postMethod.addRequestHeader("Content-type", CONTENT_TYPE); HttpClient httpClient = getClient(); int statusCode = httpClient.executeMethod(postMethod); String strResponse = postMethod.getResponseBodyAsString(); log.debug("return attested result: {}", strResponse); if (statusCode == 200) { values = parsePostedResp(strResponse); } else { log.error("attestation error: {}", strResponse); } } catch (JsonParseException e) { log.error("Failed to parse result: {}", e.getMessage()); log.debug("Exception", e); } catch (IOException e) { log.error("Failed to attest hosts, make sure hosts are up and reachable: {}", e.getMessage()); log.debug("Exception", e); } finally { postMethod.releaseConnection(); } return values; } public List<AttestationValue> parsePostedResp(String str) throws JsonProcessingException, IOException { List<AttestationValue> values = new ArrayList<>(); ObjectMapper mapper = new ObjectMapper(); JsonNode tree = mapper.readTree(str); JsonNode hosts = tree.get(HEADER_HOSTS); if (hosts != null) { for (JsonNode host : hosts) { String name = host.get(HEADER_HOST_NAME).asText(); String level = host.get(HEADER_RESULT).asText(); AttestationValue value = new AttestationValue(); value.setHostName(name); value.setTrustLevel(AttestationResultEnum.valueOf(level.toUpperCase())); values.add(value); } } return values; } public String writeListJson(List<String> hosts) { StringBuilder sb = new StringBuilder("{\"").append(HEADER_HOSTS) .append("\":["); for (String host : hosts) { sb = sb.append("\"").append(host).append("\","); } String jsonString = sb.substring(0, sb.length() - 1) + "]}"; return jsonString; } }