/* * Copyright (C) 2012 Intel Corporation * All rights reserved. */ package com.intel.mtwilson.agent.vmware; import com.intel.mtwilson.agent.HostAgent; import com.intel.mtwilson.datatypes.TxtHostRecord; import com.intel.mtwilson.model.Aik; import com.intel.mtwilson.model.Nonce; import com.intel.mtwilson.model.PcrIndex; import com.intel.mtwilson.model.PcrManifest; import com.intel.mtwilson.model.TpmQuote; import com.vmware.vim25.HostRuntimeInfo; import com.vmware.vim25.HostTpmAttestationReport; import com.vmware.vim25.HostTpmDigestInfo; import com.vmware.vim25.InvalidProperty; import com.vmware.vim25.RuntimeFault; import java.rmi.RemoteException; //import com.vmware.vim25.InvalidPropertyFaultMsg; import com.vmware.vim25.ManagedObjectReference; //import com.vmware.vim25.RuntimeFaultFaultMsg; import java.io.IOException; import java.security.PublicKey; import java.security.cert.X509Certificate; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; import javax.xml.bind.JAXBException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Instances of VmwareAgent should be created by the VmwareAgentFactory * * A single instance of VmwareHostAgent is tied to a specific host/connection -- * and it maintains a cache of some information received in order to not send * multiple redundant requests to the host. For example if it already obtained * the OS Name, that is not going to change between one call and the next so it * may be cached. TPM Quotes are never cached. If you want to be sure to get * fresh data, create a new instance. * @author jbuhacoff */ public class VmwareHostAgent implements HostAgent { private transient Logger log = LoggerFactory.getLogger(getClass()); private transient final VMwareClient vmware; private final String hostname; private transient ManagedObjectReference hostMOR = null; private String vCenterVersion = null; private String esxVersion = null; private Boolean isTpmAvailable = null; // private String vendorHostReport = null; private PcrManifest pcrManifest = null; public VmwareHostAgent(VMwareClient vmwareClient, String hostname) throws RemoteException { vmware = vmwareClient; this.hostname = hostname; hostMOR = vmwareClient.getHostReference(hostname); // issue #784 using more efficient method of getting a reference to the host //vmwareClient.getManagedObjectReference(hostname); vCenterVersion = vmwareClient.getVCenterVersion(); //serviceContent.getAbout().getVersion(); // required so we can choose implementations log.debug("VCenter version is {}", vCenterVersion); //esxVersion = vmwareClient.getMORProperty(hostMOR, "config.product.version").toString(); // required so we can choose implementations and report on host info esxVersion = vmwareClient.getStringMEProperty(hostMOR.type, hostname, "config.product.version"); log.debug("esxVersion: " + esxVersion); } public VMwareClient getClient() { return vmware; } /** * Currently this is getting called every time we request a PcrManifest -- * that's not necessary, we only need to check the host's TPM once when we * add it. After that we can assume that it is there, and only check it * again if we get an error while trying to read the PcrManifest. * * @return */ @Override public boolean isTpmPresent() { try { if (isTpmAvailable == null) { isTpmAvailable = (Boolean) vmware.getMEProperty(hostMOR.type, hostname, "capability.tpmSupported"); } return isTpmAvailable; } catch (InvalidProperty ex) { log.error("VCenter host does not support 'capability.tpmSupported' property: {}", ex.getLocalizedMessage()); return false; } catch (RuntimeFault ex) { log.error("Runtime fault while fetching 'capability.tpmSupported' property: {}", ex.getLocalizedMessage()); return false; } catch (RemoteException ex) { log.error("Runtime fault while fetching 'capability.tpmSupported' property: {}", ex.getLocalizedMessage()); return false; } } @Override public boolean isTpmEnabled() { return true; } @Override public boolean isEkAvailable() { return false; // vmware does not make the EK available through its API } @Override public boolean isAikAvailable() { return false; // vmware does not make the AIK available through its API } @Override public boolean isAikCaAvailable() { return false; // vmware does not make the Privacy CA Certificate available through its API, if it even uses a Privacy CA } @Override public boolean isDaaAvailable() { return false; // vmware does not support DAA } /** * Throws an exception because it is a programming error to call * getAikCertificate without first checking the result of isAikCaAvailable() * * @return */ @Override public X509Certificate getAikCertificate() { throw new UnsupportedOperationException("Vmware does not provide an AIK Certificate"); // return null; } /** * Throws an exception because it is a programming error to call * getAikCaCertificate without first checking the result of * isAikCaAvailable() * * @return */ @Override public X509Certificate getAikCaCertificate() { throw new UnsupportedOperationException("Vmware does not provide a Privacy CA Certificate"); // return null; } @Override public String getHostInformation() { throw new UnsupportedOperationException("Not supported yet."); } /** * Example ESXi report for a host that does NOT have a TPM: * <?xml version="1.0" ?><Host_Attestation_Report Host_Name="10.1.71.176" * vCenterVersion="5.1.0" HostVersion="5.1.0" TXT_Support="false"><PCRInfo * Error="Host does not support TPM."></PCRInfo></Host_Attestation_Report> * * @return * @throws IOException E */ @Override public String getVendorHostReport() throws IOException { /* try { getAllPcrAndModuleInformationFromHost(); return vendorHostReport; } catch(Exception e) { log.error("Cannot get vendor report", e); return null; }*/ try { return vmware.getHostAttestationReport(hostMOR, hostname, null); // will get default pcr list } catch (VMwareConnectionException e) { throw new IOException(String.format("Cannot get attestation report from host '%s': %s", hostname, e.toString()), e); } } @Override public TpmQuote getTpmQuote(Aik aik, Nonce nonce, Set<PcrIndex> pcr) { throw new UnsupportedOperationException("Vmware does not provide TPM Quotes"); } //private void getAllPcrAndModuleInformationFromHost() throws RuntimeFaultFaultMsg, InvalidPropertyFaultMsg, JAXBException { //} // Commenting the below function since it is not being used and klocwork is throwing a warning /*private <T> String toXml(Class<T> clazz, T object) throws JAXBException { JAXBElement<T> xmlReport = new JAXBElement<T>(new QName("urn:vim25"),clazz,object); StringWriter sw = new StringWriter(); JAXBContext jc = JAXBContext.newInstance("com.vmware.vim25"); Marshaller marshaller = jc.createMarshaller(); marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); marshaller.setProperty(Marshaller.JAXB_ENCODING, "UTF-8"); marshaller.marshal(xmlReport, sw); //log.info(sw.toString()); return sw.toString(); }*/ @Override public PcrManifest getPcrManifest() throws IOException { try { // if( isTpmPresent() ) { // issue #784 performance; no need to check if tpm is present, just try to get the report and if there's an error we can run diagnostics after // if (vCenterVersion.contains("5.1")) { if (vmware.isModuleAttestationSupportedByVcenter(vCenterVersion)) { HostTpmAttestationReport report = vmware.getAttestationReport(hostMOR); // if(hostId != null) // auditAttestionReport(hostId,report); log.debug("Retrieved HostTpmAttestationReport: {}", report); // vendorHostReport = toXml(HostTpmAttestationReport.class, report); // log.debug("Parsed HostTpmAttestationReport."); // manifestMap = postProcessing.processReport(esxVersion,report); //if(esxVersion.contains("5.1")) { if (vmware.isModuleAttestationSupportedByESX(esxVersion)) { pcrManifest = VMWare51Esxi51.createPcrManifest(report); } else { // return new VMWare50Esxi50().getPcrManiFest(report, // getRequestedPcrs(host)); } } else if (vCenterVersion.contains("5.0")) { HostRuntimeInfo runtimeInfo = (HostRuntimeInfo) vmware.getMEProperty(hostMOR.type, hostname, "runtime"); if (runtimeInfo == null) { throw new IllegalArgumentException("Cannot get host information"); } // vendorHostReport = toXml(HostRuntimeInfo.class, runtimeInfo); // Now process the digest information List<HostTpmDigestInfo> htdis = Arrays.asList(runtimeInfo.getTpmPcrValues()); log.debug("Retrieved HostTpmDigestInfo"); // ESX 5.0 did not support module measurement so we return only the PCR's pcrManifest = VMWare50Esxi50.createPcrManifest(htdis); // bug #607 new // pcrManifest = postProcessing.processDigest(esxVersion,htdis); } else { throw new UnsupportedOperationException("Not supported yet."); } // } } catch (Exception e) { log.warn("error during getManifest: " + e.toString(), e); boolean isTpmPresent = false; try { if (isTpmPresent()) { isTpmPresent = true; } } catch (Exception e2) { throw new IOException("Cannot retrieve PCR Manifest from " + hostname + ": cannot determine if TPM is present", e2); } throw new IOException("Cannot retrieve PCR Manifest from " + hostname + ": " + (isTpmPresent ? "TPM is present" : "TPM is not present") + ": " + e.toString(), e); } return pcrManifest; } /** * Content of this method is same as getHostDetails() in VMwareHelper in * Management Service and VMwareClient in trust utils library. * * @return */ @Override public TxtHostRecord getHostDetails() throws IOException { try { TxtHostRecord host = new TxtHostRecord(); host.HostName = vmware.getStringMEProperty(hostMOR.type, hostname, "name"); // hostObj.Description = serviceContent.getAbout().getVersion(); host.VMM_Name = vmware.getStringMEProperty(hostMOR.type, hostname, "config.product.name"); host.VMM_OSName = vmware.getStringMEProperty(hostMOR.type, hostname, "config.product.name"); host.VMM_OSVersion = vmware.getStringMEProperty(hostMOR.type, hostname, "config.product.version"); host.VMM_Version = vmware.getStringMEProperty(hostMOR.type, hostname, "config.product.build"); host.BIOS_Oem = vmware.getStringMEProperty(hostMOR.type, hostname, "hardware.systemInfo.vendor"); host.BIOS_Name = vmware.getStringMEProperty(hostMOR.type, hostname, "hardware.systemInfo.vendor"); host.BIOS_Version = vmware.getStringMEProperty(hostMOR.type, hostname, "hardware.biosInfo.biosVersion"); /* // Possible values for this processor Info includes. So, if there is a "-", we are assuming that it is either a Sandy Bridge or a IVY bridge system // For others starting with X56, they are Westmere systems belonging to Thurley platform // Romley: "Intel(R) Xeon(R) CPU E5-2680 0 @ 2.70GHz" // Thurley: "Intel(R) Xeon(R) CPU X5680 @ 3.33GHz" String processorInfo = vmware.getMORProperty(hostMOR, "summary.hardware.cpuModel").toString(); processorInfo = processorInfo.substring((processorInfo.indexOf("CPU") + ("CPU").length())).trim(); if (processorInfo.contains("-") || processorInfo.contains("-")) { processorInfo = processorInfo.substring(0, processorInfo.indexOf("-")); } else { processorInfo = processorInfo.substring(0, 3); }*/ // There is one more attribute in the vCenter that actually provides the processor name directly unlike the open source hosts where we // need to do the mapping // Possible values include: "intel-westmere", "intel-sandybridge" String processorInfo = vmware.getStringMEProperty(hostMOR.type, hostname, "summary.maxEVCModeKey"); if (processorInfo != null) { processorInfo = processorInfo.toLowerCase(); if (processorInfo.contains("intel")) { processorInfo = processorInfo.substring("intel".length() + 1); } host.Processor_Info = processorInfo.substring(0, 1).toUpperCase() + processorInfo.substring(1); } return host; } catch (InvalidProperty ex) { log.error("VCenter host does not support host details property: {}", ex.getLocalizedMessage()); throw new IOException(ex); } catch (RuntimeFault ex) { log.error("Runtime fault while fetching host details: {}", ex.getLocalizedMessage()); throw new IOException(ex); } } @Override public String getHostAttestationReport(String pcrList) throws IOException { try { return vmware.getHostAttestationReport(hostMOR, hostname, pcrList); } catch (VMwareConnectionException e) { throw new IOException(String.format("Cannot get attestation report from host '%s': %s", hostname, e.toString()), e); } } @Override public boolean isIntelTxtSupported() { return true; } @Override public boolean isIntelTxtEnabled() { return true; } @Override public PublicKey getAik() { throw new UnsupportedOperationException("Not supported yet."); } @Override public X509Certificate getEkCertificate() { throw new UnsupportedOperationException("Not supported yet."); } @Override public Map<String, String> getHostAttributes() throws IOException { HashMap<String, String> hm = new HashMap<>(); // Retrieve the data from the host and add it into the hashmap // Currently we are just adding the UUID of th host. Going ahead we can add additional details String hostUUID = vmware.getStringMEProperty("HostSystem", hostname, "hardware.systemInfo.uuid"); hm.put("Host_UUID", hostUUID); return hm; } @Override public void setAssetTag(com.intel.dcsg.cpg.crypto.Sha1Digest tag) throws IOException { throw new UnsupportedOperationException("Unsupported operation"); } }