/*
* Copyright (C) 2013 Intel Corporation
* All rights reserved.
*/
package com.intel.mtwilson.agent.intel;
/**
* @author dsmagadX
*/
//import com.intel.mountwilson.as.common.*;
import com.intel.mtwilson.i18n.ErrorCode;
import com.intel.mountwilson.as.common.ASConfig;
import com.intel.mountwilson.as.common.ASException;
import com.intel.mountwilson.ta.data.ClientRequestType;
import com.intel.mountwilson.ta.data.daa.response.DaaResponse;
import com.intel.mountwilson.ta.data.hostinfo.HostInfo;
import com.intel.mountwilson.ta.data.quoterequest.QuoteRequest;
import com.intel.mtwilson.datatypes.*;
import com.intel.dcsg.cpg.tls.policy.TlsConnection;
import com.intel.dcsg.cpg.tls.policy.TlsPolicy;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.net.MalformedURLException;
import java.net.NoRouteToHostException;
import java.net.SocketTimeoutException;
import java.net.URL;
import java.net.UnknownHostException;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
import javax.xml.bind.JAXBException;
import javax.xml.bind.PropertyException;
import javax.xml.stream.XMLStreamException;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.intel.dcsg.cpg.xml.JAXB;;
public class TrustAgentSecureClient {
public static final int DEFAULT_TRUST_AGENT_PORT = 9999;
public static final String TA_ERROR_CODE = "error_code";
public static final String TA_ERROR_MESSAGE = "error_message";
private final Logger log = LoggerFactory.getLogger(getClass());
private String serverHostname = null;
private int serverPort = 0;
private byte[] data;
private TlsPolicy tlsPolicy;
JAXB jaxb = new JAXB();
private static int TIME_OUT = ASConfig.getTrustAgentTimeOutinMilliSecs();
// Bug #497 commenting out these two constructors because now Tls Policy is a required argument.
/*
public TrustAgentSecureClient(String serverHostname, int serverPort, byte[] data) {
this(serverHostname, serverPort);
if( data != null ) {
this.data = Arrays.copyOf(data, data.length);
}
}
public TrustAgentSecureClient(String hostName, int port) {
this.serverHostname = hostName;
this.serverPort = port;
log.info("Connecting to Trust Agent at '{}'", hostName+":"+port);
} */
public TrustAgentSecureClient(TlsConnection tlsConnection) {
tlsPolicy = tlsConnection.getTlsPolicy();
parseConnectionString(tlsConnection.getURL().toExternalForm());
log.debug("TrustAgentSecureClient hostname({}) port({})", new Object[] { serverHostname, serverPort }); // removed tlsConnection.getConnectionString(), to prevent leaking secrets
}
private void parseConnectionString(String connectionString) {
if( connectionString.startsWith("https") ) { // new format used starting with version 1.1 is URL: https://ipAddressOrHostname:port
try {
URL url = new URL(connectionString);
serverHostname = url.getHost();
serverPort = url.getPort();
if( serverPort == -1 ) {
serverPort = DEFAULT_TRUST_AGENT_PORT;
}
return;
}
catch(MalformedURLException e) {
throw new IllegalArgumentException("Invalid Trust Agent connection string: "+connectionString, e);
}
}
if( connectionString.contains(":") ) { // format used from 0.5 Alpha to 1.0-RC2
try {
String[] parts = connectionString.split(":");
serverHostname = parts[0];
serverPort = Integer.valueOf(parts[1]);
return;
}
catch(Exception e) {
throw new IllegalArgumentException("Invalid Trust Agent connection string: "+connectionString, e);
}
}
throw new IllegalArgumentException("Unrecognized Trust Agent connection string format: "+connectionString);
}
/*
public TrustAgentSecureClient(IPAddress serverIPAddress, int serverPort, byte[] data) { // datatype.IPAddress
this(serverIPAddress, serverPort);
if( data != null ) {
this.data = Arrays.copyOf(data, data.length);
}
}
public TrustAgentSecureClient(IPAddress serverIPAddress, int serverPort) { // datatype.IPAddress
this(serverIPAddress.toString(), serverPort);
}
*/
private byte[] sendRequestWithSSLSocket() throws NoSuchAlgorithmException, NoSuchAlgorithmException, KeyManagementException, UnknownHostException, IOException {
log.trace( "Opening connection to {} port {}", serverHostname, String.valueOf(serverPort));
if( data == null ) {
throw new IllegalArgumentException("Attempted to send request without data");
}
// HttpsURLConnection.setDefaultHostnameVerifier(tlsPolicy.getHostnameVerifier());
// SSLSocketFactory sslsocketfactory = getSSLContext().getSocketFactory();
// SSLSocket sock = (SSLSocket) sslsocketfactory.createSocket();
TlsConnection tlsConnection = new TlsConnection(new URL("https://"+serverHostname+":"+serverPort), tlsPolicy);
try(SSLSocket sock = tlsConnection.connect()) {
// sock.connect(new InetSocketAddress(serverHostname,serverPort), TIME_OUT);
InputStream sockInput = sock.getInputStream();
OutputStream sockOutput = sock.getOutputStream();
log.info("About to start reading/writing to/from socket.");
log.debug("Writing: {}", new String(data));
byte[] buf = new byte[5000];
sockOutput.write(data, 0, data.length);
int bytes_read = sockInput.read(buf);
log.debug( "Received " + bytes_read + " bytes to server and received them back again, msg = " +StringUtils.replace(new String(buf), "\n", "\n "));
return buf;
}
catch(SocketTimeoutException e){
throw new ASException(e,ErrorCode.AS_TRUST_AGENT_CONNNECT_TIMED_OUT,serverHostname,serverPort,(TIME_OUT/1000));
}
}
public DaaResponse sendDaaChallenge(String challenge) throws NoSuchAlgorithmException, KeyManagementException, UnknownHostException, JAXBException, IOException, XMLStreamException {
this.data = challenge.getBytes();
byte buf[] = sendRequestWithSSLSocket();
// bug #1038 use secure xml parsing settings, encapsulated in cpg-xml JAXB utility
DaaResponse response = jaxb.read(new String(buf).trim(), DaaResponse.class);
// JAXBContext jc = JAXBContext.newInstance("com.intel.mountwilson.ta.data.daa.response");
// Unmarshaller u = jc.createUnmarshaller();
// JAXBElement po = (JAXBElement) u.unmarshal(new StringReader(new String(buf).trim()));
// DaaResponse response = (DaaResponse)po.getValue();
return response;
}
/**
*
* @return an object representing the RESPONSE from the Trust Agent
* @throws UnknownHostException if the IP address of the host could not be determined from local hosts file or DNS
* @throws IOException if there was an error connecting to the host, such as it is not reachable on the network or it dropped the connection
* @throws JAXBException when the response from the host cannot be interpreted properly
* @throws NoSuchAlgorithmException
* @throws KeyManagementException
*/
public ClientRequestType sendQuoteRequest() throws UnknownHostException, IOException, JAXBException, KeyManagementException, NoSuchAlgorithmException, XMLStreamException {
byte buf[] = sendRequestWithSSLSocket();
log.info("Unmarshalling to Jaxb object.");
// bug #1038 use secure xml parsing settings, encapsulated in cpg-xml JAXB utility
ClientRequestType response = jaxb.read(new String(buf).trim(), ClientRequestType.class);
assert response != null;
checkQuoteError(response);
log.info("Done reading/writing to/from socket, closing socket.");
return response;
}
/*
private SSLContext getSSLContext() throws NoSuchAlgorithmException, KeyManagementException {
javax.net.ssl.TrustManager x509 = new javax.net.ssl.X509TrustManager() {
@Override
public void checkClientTrusted(java.security.cert.X509Certificate[] arg0, String arg1) throws java.security.cert.CertificateException {
log.info("checkClientTrusted. String argument: "+arg1);
for(java.security.cert.X509Certificate cert : arg0) {
log.info("Certificate:");
log.info(" Subject: "+cert.getSubjectX500Principal().getName());
log.info(" Issued by: "+cert.getIssuerX500Principal().getName());
cert.checkValidity();
}
return;
}
@Override
public void checkServerTrusted(java.security.cert.X509Certificate[] arg0, String arg1) throws java.security.cert.CertificateException {
log.info("checkServerTrusted. String argument: "+arg1);
for(java.security.cert.X509Certificate cert : arg0) {
log.info("Certificate:");
log.info(" Subject: "+cert.getSubjectX500Principal().getName());
log.info(" Issued by: "+cert.getIssuerX500Principal().getName());
cert.checkValidity();
}
return;
}
@Override
public java.security.cert.X509Certificate[] getAcceptedIssuers() {
log.info("getAcceptedIssuers");
return null;
}
};
SSLContext ctx = SSLContext.getInstance("SSL");
ctx.init(null, new javax.net.ssl.TrustManager[]{x509}, null);
log.debug("Connecting to trust agent with ProtocolSelector: {}", tlsPolicy.getProtocolSelector().preferred());
SSLContext ctx = SSLContext.getInstance(tlsPolicy.getProtocolSelector().preferred()); // bug #871 ssl policy should be configurable; was hardcoded to "SSL"
ctx.init(null, new javax.net.ssl.TrustManager[]{ tlsPolicy.getTrustManager() }, null);
return ctx;
}
*/
/**
* Example request:
* <identity_request></identity_request>
* Example response:
* <client_request> <timestamp>Tue Apr 09 15:45:02 PDT 2013</timestamp><clientIp>fe80:0:0:0:21e:67ff:fe10:4460%4</clientIp><error_code>0</error_code><error_message>OK</error_message><aikcert>-----BEGIN CERTIFICATE-----MIICuzCCAaOgAwIBAgIGAT3w+RgtMA0GCSqGSIb3DQEBBQUAMBkxFzAVBgNVBAMMDkhJU19Qcml2
YWN5X0NBMB4XDTEzMDQwOTIyNDQ0OVoXDTIzMDQwOTIyNDQ0OVowADCCASIwDQYJKoZIhvcNAQEB
BQADggEPADCCAQoCggEBAMtggj+vxoG7fSTrM8yAjZZm4v4SbOcou1BW+HeRcVt1VU4/nWkTJdk/
etHtNI2qady/qalIIKkHvRjG4IStubaKtS9t8cJhzkFoI9dWYLAfYNdIrDe4IB8fFWgwSB9nAbHC
E9KL5yWs+7ImRND/4HYRPHShtwm+aH2SvZdvHi0ZCDzentlhZaQQ3T71upWUPi7fnBnFy4L1xzO6
AR2aPT27D06LdSh+21ofWl/Lh28VQM3hcqul+Yyhc+DdKjMhqOUPJOOmS4y/erip8HH+YgIg7OqX
PrXdqrW9h5IWl+5T/AYayQ82Y2WAtnYa2rHa+GU55p8pM1Ohp88FDbNaSv0CAwEAAaMiMCAwHgYD
VR0RAQH/BBQwEoEQSElTIElkZW50aXR5IEtleTANBgkqhkiG9w0BAQUFAAOCAQEAQ6F2PGx26D6P
53Enht4Zt7cOnohZwIkdu6Jl6wUKLcWpMn6x1Nqdi5Q0++qFW+vqoBQA44ldSsfQ7t3rqwr59Qrb
HtTIW/+bWTUjXO0OiQlFR87adaxbNlPQlPSApQiLvH2K5PFctnMd4A/TAaH9KtbDimR8obCt4y5S
xUH54DrZMayGd5OWCUtH9bypgLBg8QvAyuRP7eszu5z75Nv61JNjQxDNUyFMe2iIqMPV9Gs1nsYV
3kWLYF/gxYjRc/3iGDUTJez9pinZrx/Ddq3q6nQs/EHGZj5G7Z0S261Pofobo+KBRHP31+kEVczi
Bhc/7BfnYNKiGYzNZh4atiMrhg==
-----END CERTIFICATE-----</aikcert></client_request>
* @return
*/
public String getAIKCertificate() {
try {
log.info("Sending Generate Identity");
byte[] identityInput = "<identity_request></identity_request>".getBytes();
this.data = identityInput;
ClientRequestType response = sendQuoteRequest();
String certificate = response.getAikcert();
return certificate;
}catch(ASException ase){
throw ase;
}catch(UnknownHostException e) {
throw new ASException(e,ErrorCode.AS_HOST_COMMUNICATION_ERROR, this.serverHostname);
}catch (Exception e) {
throw new ASException(e);
}
}
public ClientRequestType getQuote(String nonce, String pcrList) throws PropertyException, JAXBException, UnknownHostException, IOException, KeyManagementException, NoSuchAlgorithmException, XMLStreamException {
QuoteRequest quoteRequest = new QuoteRequest();
quoteRequest.setPcrList(pcrList);
quoteRequest.setNonce(nonce);
this.data = getXml(quoteRequest).getBytes();
ClientRequestType clientRequestType = sendQuoteRequest();
log.info("Got quote from server");
return clientRequestType;
}
private String getXml(QuoteRequest quoteRequest) throws PropertyException, JAXBException {
String quoteRequestXml = jaxb.write(quoteRequest);
log.debug("Quote request XML {}", quoteRequestXml);
return quoteRequestXml;
}
private void checkQuoteError(ClientRequestType response) {
int errorCode = response.getErrorCode();
log.error(String.format("Trust Agent Error %d [%s]: %s", response.getErrorCode(), response.getClientIp(), response.getErrorMessage()));
if (errorCode != 0) {
throw new ASException(ErrorCode.AS_TRUST_AGENT_ERROR, response.getErrorCode(),response.getErrorMessage());
}
}
/**
* How to test this:
*
* openssl s_client -connect 10.1.71.169:9999
* <host_info></host_info>
*
* Example response:
* <host_info><timeStamp>Tue Apr 09 15:43:47 PDT 2013</timeStamp><clientIp>fe80:0:0:0:21e:67ff:fe10:4460%4</clientIp><errorCode>0</errorCode><errorMessage>OK</errorMessage><osName>SUSE LINUX</osName><osVersion> 11</osVersion><biosOem>Intel Corp.</biosOem><biosVersion> S5500.86B.01.00.T060.070620121139</biosVersion><vmmName>Xen</vmmName><vmmVersion>4.1.0</vmmVersion></host_info>
*
*
* @return
*/
public HostInfo getHostInfo() {
this.data = "<host_info></host_info>".getBytes();
HostInfo response;
try {
byte buf[] = sendRequestWithSSLSocket();
log.debug("TrustAgent response: {}", new String(buf).trim());
// bug #1038 use secure xml parsing settings, encapsulated in cpg-xml JAXB utility
response = jaxb.read(new String(buf).trim(), HostInfo.class);
}catch(UnknownHostException e) {
throw new ASException(e,ErrorCode.AS_HOST_COMMUNICATION_ERROR,this.serverHostname);
}catch(NoRouteToHostException e) { // NoRouteToHostException is a subclass of IOException that may be thrown by the socket layer
throw new ASException(e,ErrorCode.AS_HOST_COMMUNICATION_ERROR,this.serverHostname);
}catch(IOException e) {
throw new ASException(e,ErrorCode.AS_HOST_COMMUNICATION_ERROR,this.serverHostname);
}catch(NoSuchAlgorithmException e) {
throw new ASException(e,ErrorCode.TLS_COMMMUNICATION_ERROR,this.serverHostname, e.toString());
}catch(KeyManagementException e) {
throw new ASException(e,ErrorCode.TLS_COMMMUNICATION_ERROR,this.serverHostname, e.toString());
} catch(JAXBException e) {
throw new ASException(e,ErrorCode.AS_TRUST_AGENT_INVALID_RESPONSE, e.toString());
}
catch(XMLStreamException e) {
throw new ASException(e,ErrorCode.AS_TRUST_AGENT_INVALID_RESPONSE, e.toString());
}
/*catch(Exception e) {
throw new ASException(e);
}*/
int errorCode = response.getErrorCode();
log.error(String.format("Trust Agent Error %d [%s]: %s", response.getErrorCode(), response.getClientIp(), response.getErrorMessage()));
if (errorCode != 0) {
throw new ASException(ErrorCode.AS_TRUST_AGENT_ERROR, errorCode,response.getErrorMessage());
}
return response;
}
/**
* How to test this:
*
* openssl s_client -connect 10.1.71.169:9999
* <set_asset_tag><asset_tag_hash>aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa</asset_tag_hash><asset_tag_uuid>aaaa-aaa-aaa-aaa</asset_tag_uuid></set_asset_tag>
*
* Example response:
* <response>true</response>
*
*
* @return
*/
public boolean setAssetTag(String assetTagHash, String uuid) {
String xml = "<set_asset_tag><asset_tag_hash>" + assetTagHash + "</asset_tag_hash><asset_tag_uuid>" + uuid +"</asset_tag_uuid></set_asset_tag>";
this.data = xml.getBytes();
try {
byte buf[] = sendRequestWithSSLSocket();
log.debug("TrustAgent response: {}", new String(buf));
// bug #1038 use secure xml parsing settings, encapsulated in cpg-xml JAXB utility
// response = jaxb.read(new String(buf).trim(), HostInfo.class);
}catch(UnknownHostException e) {
throw new ASException(e,ErrorCode.AS_HOST_COMMUNICATION_ERROR,this.serverHostname);
}catch(NoRouteToHostException e) { // NoRouteToHostException is a subclass of IOException that may be thrown by the socket layer
throw new ASException(e,ErrorCode.AS_HOST_COMMUNICATION_ERROR,this.serverHostname);
}catch(IOException e) {
throw new ASException(e,ErrorCode.AS_HOST_COMMUNICATION_ERROR,this.serverHostname);
}catch(NoSuchAlgorithmException e) {
throw new ASException(e,ErrorCode.TLS_COMMMUNICATION_ERROR,this.serverHostname, e.toString());
}catch(KeyManagementException e) {
throw new ASException(e,ErrorCode.TLS_COMMMUNICATION_ERROR,this.serverHostname, e.toString());
}
return true;
}
}