/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.ranger.plugin.util;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.KeyManager;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.hadoop.conf.Configuration;
import org.apache.ranger.authorization.hadoop.utils.RangerCredentialProvider;
import org.apache.ranger.authorization.utils.StringUtil;
import com.google.common.base.Objects;
public class RangerSslHelper {
private static final Log LOG = LogFactory.getLog(RangerSslHelper.class);
static final String RANGER_POLICYMGR_CLIENT_KEY_FILE = "xasecure.policymgr.clientssl.keystore";
static final String RANGER_POLICYMGR_CLIENT_KEY_FILE_TYPE = "xasecure.policymgr.clientssl.keystore.type";
static final String RANGER_POLICYMGR_CLIENT_KEY_FILE_CREDENTIAL = "xasecure.policymgr.clientssl.keystore.credential.file";
static final String RANGER_POLICYMGR_CLIENT_KEY_FILE_CREDENTIAL_ALIAS = "sslKeyStore";
static final String RANGER_POLICYMGR_CLIENT_KEY_FILE_TYPE_DEFAULT = "jks";
static final String RANGER_POLICYMGR_TRUSTSTORE_FILE = "xasecure.policymgr.clientssl.truststore";
static final String RANGER_POLICYMGR_TRUSTSTORE_FILE_TYPE = "xasecure.policymgr.clientssl.truststore.type";
static final String RANGER_POLICYMGR_TRUSTSTORE_FILE_CREDENTIAL = "xasecure.policymgr.clientssl.truststore.credential.file";
static final String RANGER_POLICYMGR_TRUSTSTORE_FILE_CREDENTIAL_ALIAS = "sslTrustStore";
static final String RANGER_POLICYMGR_TRUSTSTORE_FILE_TYPE_DEFAULT = "jks";
static final String RANGER_SSL_KEYMANAGER_ALGO_TYPE = KeyManagerFactory.getDefaultAlgorithm();
static final String RANGER_SSL_TRUSTMANAGER_ALGO_TYPE = TrustManagerFactory.getDefaultAlgorithm();
static final String RANGER_SSL_CONTEXT_ALGO_TYPE = "SSL";
private String mKeyStoreURL;
private String mKeyStoreAlias;
private String mKeyStoreFile;
private String mKeyStoreType;
private String mTrustStoreURL;
private String mTrustStoreAlias;
private String mTrustStoreFile;
private String mTrustStoreType;
final static HostnameVerifier _Hv = new HostnameVerifier() {
@Override
public boolean verify(String urlHostName, SSLSession session) {
return session.getPeerHost().equals(urlHostName);
}
};
final String mSslConfigFileName;
public RangerSslHelper(String sslConfigFileName) {
mSslConfigFileName = sslConfigFileName;
if(LOG.isDebugEnabled()) {
LOG.debug("==> RangerSslHelper(" + mSslConfigFileName + ")");
}
}
public SSLContext createContext() {
readConfig();
KeyManager[] kmList = getKeyManagers();
TrustManager[] tmList = getTrustManagers();
SSLContext sslContext = getSSLContext(kmList, tmList);
return sslContext;
}
public HostnameVerifier getHostnameVerifier() {
return _Hv;
}
void readConfig() {
InputStream in = null;
try {
Configuration conf = new Configuration();
in = getFileInputStream(mSslConfigFileName);
if (in != null) {
conf.addResource(in);
}
mKeyStoreURL = conf.get(RANGER_POLICYMGR_CLIENT_KEY_FILE_CREDENTIAL);
mKeyStoreAlias = RANGER_POLICYMGR_CLIENT_KEY_FILE_CREDENTIAL_ALIAS;
mKeyStoreType = conf.get(RANGER_POLICYMGR_CLIENT_KEY_FILE_TYPE, RANGER_POLICYMGR_CLIENT_KEY_FILE_TYPE_DEFAULT);
mKeyStoreFile = conf.get(RANGER_POLICYMGR_CLIENT_KEY_FILE);
mTrustStoreURL = conf.get(RANGER_POLICYMGR_TRUSTSTORE_FILE_CREDENTIAL);
mTrustStoreAlias = RANGER_POLICYMGR_TRUSTSTORE_FILE_CREDENTIAL_ALIAS;
mTrustStoreType = conf.get(RANGER_POLICYMGR_TRUSTSTORE_FILE_TYPE, RANGER_POLICYMGR_TRUSTSTORE_FILE_TYPE_DEFAULT);
mTrustStoreFile = conf.get(RANGER_POLICYMGR_TRUSTSTORE_FILE);
if (LOG.isDebugEnabled()) {
LOG.debug(Objects.toStringHelper("RangerSslHelper")
.add("keyStoreAlias", mKeyStoreAlias)
.add("keyStoreFile", mKeyStoreFile)
.add("keyStoreType", mKeyStoreType)
.add("keyStoreURL", mKeyStoreURL)
.add("trustStoreAlias", mTrustStoreAlias)
.add("trustStoreFile", mTrustStoreFile)
.add("trustStoreType", mTrustStoreType)
.add("trustStoreURL", mTrustStoreURL)
.toString());
}
}
catch(IOException ioe) {
LOG.error("Unable to load SSL Config FileName: [" + mSslConfigFileName + "]", ioe);
}
finally {
close(in, mSslConfigFileName);
}
}
private KeyManager[] getKeyManagers() {
KeyManager[] kmList = null;
String keyStoreFilepwd = getCredential(mKeyStoreURL, mKeyStoreAlias);
if (!StringUtil.isEmpty(mKeyStoreFile) && !StringUtil.isEmpty(keyStoreFilepwd)) {
InputStream in = null;
try {
in = getFileInputStream(mKeyStoreFile);
if (in != null) {
KeyStore keyStore = KeyStore.getInstance(mKeyStoreType);
keyStore.load(in, keyStoreFilepwd.toCharArray());
KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(RANGER_SSL_KEYMANAGER_ALGO_TYPE);
keyManagerFactory.init(keyStore, keyStoreFilepwd.toCharArray());
kmList = keyManagerFactory.getKeyManagers();
} else {
LOG.error("Unable to obtain keystore from file [" + mKeyStoreFile + "]");
}
} catch (KeyStoreException e) {
LOG.error("Unable to obtain from KeyStore", e);
} catch (NoSuchAlgorithmException e) {
LOG.error("SSL algorithm is available in the environment", e);
} catch (CertificateException e) {
LOG.error("Unable to obtain the requested certification ", e);
} catch (FileNotFoundException e) {
LOG.error("Unable to find the necessary SSL Keystore and TrustStore Files", e);
} catch (IOException e) {
LOG.error("Unable to read the necessary SSL Keystore and TrustStore Files", e);
} catch (UnrecoverableKeyException e) {
LOG.error("Unable to recover the key from keystore", e);
} finally {
close(in, mKeyStoreFile);
}
}
return kmList;
}
private TrustManager[] getTrustManagers() {
TrustManager[] tmList = null;
String trustStoreFilepwd = getCredential(mTrustStoreURL, mTrustStoreAlias);
if (!StringUtil.isEmpty(mTrustStoreFile) && !StringUtil.isEmpty(trustStoreFilepwd)) {
InputStream in = null;
try {
in = getFileInputStream(mTrustStoreFile);
if (in != null) {
KeyStore trustStore = KeyStore.getInstance(mTrustStoreType);
trustStore.load(in, trustStoreFilepwd.toCharArray());
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(RANGER_SSL_TRUSTMANAGER_ALGO_TYPE);
trustManagerFactory.init(trustStore);
tmList = trustManagerFactory.getTrustManagers();
} else {
LOG.error("Unable to obtain keystore from file [" + mTrustStoreFile + "]");
}
} catch (KeyStoreException e) {
LOG.error("Unable to obtain from KeyStore", e);
} catch (NoSuchAlgorithmException e) {
LOG.error("SSL algorithm is available in the environment", e);
} catch (CertificateException e) {
LOG.error("Unable to obtain the requested certification ", e);
} catch (FileNotFoundException e) {
LOG.error("Unable to find the necessary SSL Keystore and TrustStore Files", e);
} catch (IOException e) {
LOG.error("Unable to read the necessary SSL Keystore and TrustStore Files", e);
} finally {
close(in, mTrustStoreFile);
}
}
return tmList;
}
private SSLContext getSSLContext(KeyManager[] kmList, TrustManager[] tmList) {
try {
if(kmList != null && tmList != null) {
SSLContext sslContext = SSLContext.getInstance(RANGER_SSL_CONTEXT_ALGO_TYPE);
sslContext.init(kmList, tmList, new SecureRandom());
return sslContext;
}
} catch (NoSuchAlgorithmException e) {
LOG.error("SSL algorithm is available in the environment", e);
} catch (KeyManagementException e) {
LOG.error("Unable to initialize the SSLContext", e);
}
return null;
}
private String getCredential(String url, String alias) {
char[] credStr = RangerCredentialProvider.getInstance().getCredentialString(url, alias);
return credStr == null ? null : new String(credStr);
}
private InputStream getFileInputStream(String fileName) throws IOException {
InputStream in = null;
if(! StringUtil.isEmpty(fileName)) {
File f = new File(fileName);
if (f.exists()) {
in = new FileInputStream(f);
}
else {
in = ClassLoader.getSystemResourceAsStream(fileName);
}
}
return in;
}
private void close(InputStream str, String filename) {
if (str != null) {
try {
str.close();
} catch (IOException excp) {
LOG.error("Error while closing file: [" + filename + "]", excp);
}
}
}
}