/*
* JBoss, Home of Professional Open Source
* Copyright 2006, JBoss Inc., and individual contributors as indicated
* by the @authors tag. See the copyright.txt in the distribution for a
* full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.jboss.internal.soa.esb.util;
import java.io.BufferedInputStream;
import java.io.DataInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import javax.net.ssl.X509TrustManager;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPSClient;
import org.apache.log4j.Logger;
import org.jboss.soa.esb.ConfigurationException;
import org.jboss.soa.esb.addressing.eprs.FTPSEpr;
import org.jboss.soa.esb.util.RemoteFileSystemException;
/**
* Simplified FTP transfers
* <p>
* Description: Implements a simple set of FTP functionality Parameters to
* establish the FTP connection are provided at construction time and cannot
* change during the lifetime of the object <br/>Hides low level details.
* </p>
*/
public class FtpsImpl extends FtpImpl
{
private static final Logger _logger = Logger.getLogger(FtpsImpl.class);
private URI m_oCertificate ;
private String m_oCertificateName ;
public FtpsImpl (FTPSEpr p_oP, boolean p_bConnect)
throws ConfigurationException, RemoteFileSystemException
{
super(p_oP) ;
try
{
m_oCertificate = p_oP.getCertificateURI() ;
}
catch (final URISyntaxException urise)
{
_logger.warn(urise);
}
m_oCertificateName = p_oP.getCertificateName() ;
configTreeFromEpr();
initialize(p_bConnect);
}
protected void configTreeFromEpr () throws RemoteFileSystemException
{
super.configTreeFromEpr() ;
try
{
if (m_oCertificate != null)
{
m_oParms.setAttribute(PARMS_CERTIFICATE, m_oCertificate.toString());
}
if (m_oCertificateName != null)
{
m_oParms.setAttribute(PARMS_CERTIFICATE_NAME, m_oCertificateName);
}
}
catch (Exception e)
{
throw new RemoteFileSystemException(e);
}
}
/**
* Checks validity and completeness of parameters, and keeps the info
* internally for subsequent FTP requests
*
* @throws ConfigurationException :
* if parameters are invalid or incomplete
* <li>Parameters: (XML attributes at the root level) </li>
* <li> ftpServer = name or IP of FTP server </li>
* <li> ftpUser = login ID for server </li>
* <li> ftpPassword </li>
* <li> localDirURI = absolute path in the local filesystem
* </li>
* <li> remoteDirURI = remote path is relative to ftp user home
* in remote computer </li>
*/
protected void checkParms() throws ConfigurationException
{
super.checkParms();
final String certificate = m_oParms.getAttribute(PARMS_CERTIFICATE) ;
if (certificate != null)
{
try
{
m_oCertificate = new URI(certificate) ;
}
catch (final URISyntaxException ex)
{
throw new ConfigurationException("Failed to create certificate URI", ex) ;
}
}
m_oCertificateName = m_oParms.getAttribute(PARMS_CERTIFICATE_NAME) ;
}
protected FTPClient instantiateClient()
throws RemoteFileSystemException
{
final FTPSClient ftpsClient ;
try
{
ftpsClient = new FTPSClient() ;
}
catch (final NoSuchAlgorithmException nsae)
{
throw new RemoteFileSystemException(nsae) ;
}
try
{
ftpsClient.setTrustManager(new ESBTrustManager(m_oCertificate)) ;
}
catch (final Exception ex)
{
throw new RemoteFileSystemException("Exception creating trust manager", ex) ;
}
return ftpsClient ;
}
protected void connect()
throws IOException
{
super.connect() ;
final FTPSClient ftpsClient = (FTPSClient)m_oConn ;
// force passive mode
ftpsClient.enterLocalPassiveMode() ;
ftpsClient.execPBSZ(0) ;
ftpsClient.execPROT("P") ;
}
/**
* Trust manager for handling the server certificate validation.
* @author kevin
*/
private static final class ESBTrustManager implements X509TrustManager
{
/**
* Certificates being imported.
*/
private final X509Certificate[] certificates ;
/**
* Start of certificate indicator.
*/
private static final String BEGIN_CERTIFICATE = "-----BEGIN CERTIFICATE-----" ;
/**
* Construct the trust manager with the specified certificate.
*
* @param certificate The certificate of the signer or null if signature not checked.
* @throws URISyntaxException
* @throws MalformedURLException
* @throws IOException
* @throws CertificateException
*/
public ESBTrustManager(final URI certificate)
throws URISyntaxException, MalformedURLException, IOException, CertificateException
{
if (certificate != null)
{
final InputStream is ;
if (certificate.isAbsolute())
{
is = certificate.toURL().openStream() ;
}
else
{
final File file = new File(certificate) ;
is = file.toURL().openStream() ;
}
final ArrayList<X509Certificate> certificates = new ArrayList<X509Certificate>() ;
try
{
final CertificateFactory cf = CertificateFactory.getInstance("X.509");
final BufferedInputStream bis = new BufferedInputStream(is) ;
final DataInputStream dis = new DataInputStream(bis) ;
if (dis.available() > 0)
{
// We have to skip any preamble to read the certificate
do
{
dis.mark(BEGIN_CERTIFICATE.length() * 2) ;
final String line = dis.readLine() ;
if (BEGIN_CERTIFICATE.equals(line))
{
dis.reset() ;
break ;
}
}
while (dis.available() > 0) ;
if (dis.available() > 0)
{
final X509Certificate cert = (X509Certificate)cf.generateCertificate(dis);
certificates.add(cert) ;
}
}
this.certificates = certificates.toArray(new X509Certificate[certificates.size()]) ;
}
finally
{
is.close() ;
}
}
else
{
this.certificates = null ;
}
}
public void checkClientTrusted(X509Certificate[] chain, String authType)
throws CertificateException
{
}
public void checkServerTrusted(X509Certificate[] chain, String authType)
throws CertificateException
{
final int numCerts = (chain == null ? 0 : chain.length) ;
for(int count = 0 ; count < numCerts ; count++)
{
final X509Certificate cert = chain[count] ;
cert.checkValidity() ;
verify(cert) ;
}
}
private void verify(final X509Certificate cert)
throws CertificateException
{
final int numCertificates = (certificates == null ? 0 : certificates.length) ;
if (numCertificates > 0)
{
for(int count = 0 ; count < numCertificates ; count++)
{
try
{
cert.verify(certificates[count].getPublicKey()) ;
return ;
}
catch (final Exception ex) {} // ignore and try next certificate
}
throw new CertificateException("Failed to verify certificate") ;
}
}
public X509Certificate[] getAcceptedIssuers()
{
return certificates ;
}
}
}