blob: 4b56936b57f6db1fc1a5e2bb61e932c6472c7135 [file] [log] [blame]
/*
* 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.authentication;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.security.KeyStore;
import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Properties;
import java.util.Set;
import javax.net.ssl.KeyManager;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLServerSocket;
import javax.net.ssl.SSLServerSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import org.apache.log4j.Logger;
import org.apache.ranger.credentialapi.CredentialReader;
import org.apache.ranger.usergroupsync.UserGroupSync;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
public class UnixAuthenticationService {
private static final Logger LOG = Logger.getLogger(UnixAuthenticationService.class);
private static final String serviceName = "UnixAuthenticationService";
private static final String SSL_ALGORITHM = "TLS";
private static final String REMOTE_LOGIN_AUTH_SERVICE_PORT_PARAM = "ranger.usersync.port";
private static final String SSL_KEYSTORE_PATH_PARAM = "ranger.usersync.keystore.file";
private static final String SSL_TRUSTSTORE_PATH_PARAM = "ranger.usersync.truststore.file";
private static final String SSL_KEYSTORE_PATH_PASSWORD_ALIAS = "usersync.ssl.key.password";
private static final String SSL_TRUSTSTORE_PATH_PASSWORD_ALIAS = "usersync.ssl.truststore.password";
private static final String CRED_VALIDATOR_PROG = "ranger.usersync.passwordvalidator.path";
private static final String ADMIN_USER_LIST_PARAM = "admin.users";
private static final String ADMIN_ROLE_LIST_PARAM = "admin.roleNames";
private static final String SSL_ENABLED_PARAM = "ranger.usersync.ssl";
private static final String CREDSTORE_FILENAME_PARAM = "ranger.usersync.credstore.filename";
private String keyStorePath;
private String keyStorePathPassword;
private String trustStorePath;
private String trustStorePathPassword;
private List<String> adminUserList = new ArrayList<String>();
private String adminRoleNames;
private int portNum;
private boolean SSLEnabled = false;
static private boolean enableUnixAuth = false;
private static final String[] UGSYNC_CONFIG_XML_FILES = { "ranger-ugsync-default.xml", "ranger-ugsync-site.xml" };
private static final String PROPERTY_ELEMENT_TAGNAME = "property";
private static final String NAME_ELEMENT_TAGNAME = "name";
private static final String VALUE_ELEMENT_TAGNAME = "value";
public static void main(String[] args) {
if (args.length > 0) {
for (String s : args) {
if ("-enableUnixAuth".equalsIgnoreCase(s)) {
enableUnixAuth = true;
break;
}
}
}
UnixAuthenticationService service = new UnixAuthenticationService();
service.run();
}
public UnixAuthenticationService() {
}
public void run() {
try {
LOG.info("Starting User Sync Service!");
startUnixUserGroupSyncProcess();
if (enableUnixAuth) {
LOG.info("Enabling Unix Auth Service!");
init();
startService();
} else {
LOG.info("Unix Auth Service Disabled!");
}
}
catch(Throwable t) {
LOG.error("ERROR: Service: " + serviceName , t);
}
finally {
LOG.info("Service: " + serviceName + " - STOPPED.");
}
}
private void startUnixUserGroupSyncProcess() {
//
// Start the synchronization service ...
//
UserGroupSync syncProc = new UserGroupSync();
Thread newSyncProcThread = new Thread(syncProc);
newSyncProcThread.setName("UnixUserSyncThread");
newSyncProcThread.setDaemon(false);
newSyncProcThread.start();
}
//TODO: add more validation code
private void init() throws Throwable {
Properties prop = new Properties();
for (String fn : UGSYNC_CONFIG_XML_FILES ) {
InputStream in = getFileInputStream(fn);
if (in != null) {
try {
DocumentBuilderFactory xmlDocumentBuilderFactory = DocumentBuilderFactory.newInstance();
xmlDocumentBuilderFactory.setIgnoringComments(true);
xmlDocumentBuilderFactory.setNamespaceAware(true);
DocumentBuilder xmlDocumentBuilder = xmlDocumentBuilderFactory.newDocumentBuilder();
Document xmlDocument = xmlDocumentBuilder.parse(in);
xmlDocument.getDocumentElement().normalize();
NodeList nList = xmlDocument.getElementsByTagName(PROPERTY_ELEMENT_TAGNAME);
for (int temp = 0; temp < nList.getLength(); temp++) {
Node nNode = nList.item(temp);
if (nNode.getNodeType() == Node.ELEMENT_NODE) {
Element eElement = (Element) nNode;
String propertyName = "";
String propertyValue = "";
if (eElement.getElementsByTagName(NAME_ELEMENT_TAGNAME).item(
0) != null) {
propertyName = eElement
.getElementsByTagName(NAME_ELEMENT_TAGNAME)
.item(0).getTextContent().trim();
}
if (eElement.getElementsByTagName(VALUE_ELEMENT_TAGNAME)
.item(0) != null) {
propertyValue = eElement
.getElementsByTagName(VALUE_ELEMENT_TAGNAME)
.item(0).getTextContent().trim();
}
//LOG.info("Adding Property:[" + propertyName + "] Value:["+ propertyValue + "]");
if (prop.get(propertyName) != null ) {
prop.remove(propertyName);
}
prop.put(propertyName, propertyValue);
}
}
}
finally {
try {
in.close();
}
catch(IOException ioe) {
// Ignore IOE when closing streams
}
}
}
}
String credStoreFileName = prop.getProperty(CREDSTORE_FILENAME_PARAM);
keyStorePath = prop.getProperty(SSL_KEYSTORE_PATH_PARAM);
if (credStoreFileName == null) {
throw new RuntimeException("Credential file is not defined. param = [" + CREDSTORE_FILENAME_PARAM + "]");
}
File credFile = new File(credStoreFileName);
if (! credFile.exists()) {
throw new RuntimeException("Credential file [" + credStoreFileName + "]: does not exists." );
}
if ( ! credFile.canRead() ) {
throw new RuntimeException("Credential file [" + credStoreFileName + "]: can not be read." );
}
keyStorePathPassword = CredentialReader.getDecryptedString(credStoreFileName, SSL_KEYSTORE_PATH_PASSWORD_ALIAS);
trustStorePathPassword = CredentialReader.getDecryptedString(credStoreFileName,SSL_TRUSTSTORE_PATH_PASSWORD_ALIAS);
trustStorePath = prop.getProperty(SSL_TRUSTSTORE_PATH_PARAM);
portNum = Integer.parseInt(prop.getProperty(REMOTE_LOGIN_AUTH_SERVICE_PORT_PARAM));
String validatorProg = prop.getProperty(CRED_VALIDATOR_PROG);
if (validatorProg != null) {
PasswordValidator.setValidatorProgram(validatorProg);
}
String adminUsers = prop.getProperty(ADMIN_USER_LIST_PARAM);
if (adminUsers != null && adminUsers.trim().length() > 0) {
for(String u : adminUsers.split(",")) {
LOG.info("Adding Admin User:" + u.trim());
adminUserList.add(u.trim());
}
PasswordValidator.setAdminUserList(adminUserList);
}
adminRoleNames = prop.getProperty(ADMIN_ROLE_LIST_PARAM);
if (adminRoleNames != null) {
LOG.info("Adding Admin Group:" + adminRoleNames);
PasswordValidator.setAdminRoleNames(adminRoleNames);
}
String SSLEnabledProp = prop.getProperty(SSL_ENABLED_PARAM);
SSLEnabled = (SSLEnabledProp != null && (SSLEnabledProp.equalsIgnoreCase("true")));
// LOG.info("Key:" + keyStorePath);
// LOG.info("KeyPassword:" + keyStorePathPassword);
// LOG.info("TrustStore:" + trustStorePath);
// LOG.info("PortNum:" + portNum);
// LOG.info("ValidatorProg:" + validatorProg);
}
public void startService() throws Throwable {
SSLContext context = SSLContext.getInstance(SSL_ALGORITHM);
KeyManager[] km = null;
if (keyStorePath != null && ! keyStorePath.isEmpty()) {
KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
InputStream in = null;
in = getFileInputStream(keyStorePath);
try {
if (keyStorePathPassword == null) {
keyStorePathPassword = "";
}
ks.load(in, keyStorePathPassword.toCharArray());
}
finally {
if (in != null) {
in.close();
}
}
KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
kmf.init(ks, keyStorePathPassword.toCharArray());
km = kmf.getKeyManagers();
}
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
KeyStore trustStoreKeyStore = null;
if (trustStorePath != null && ! trustStorePath.isEmpty()) {
trustStoreKeyStore = KeyStore.getInstance(KeyStore.getDefaultType());
InputStream in = null;
in = getFileInputStream(trustStorePath);
try {
if (trustStorePathPassword == null) {
trustStorePathPassword = "";
}
trustStoreKeyStore.load(in, trustStorePathPassword.toCharArray());
}
finally {
if (in != null) {
in.close();
}
}
}
trustManagerFactory.init(trustStoreKeyStore);
TrustManager[] tm = trustManagerFactory.getTrustManagers();
SecureRandom random = new SecureRandom();
context.init(km, tm, random);
SSLServerSocketFactory sf = context.getServerSocketFactory();
ServerSocket socket = (SSLEnabled ? sf.createServerSocket(portNum) : new ServerSocket(portNum) );
if (SSLEnabled) {
SSLServerSocket secureSocket = (SSLServerSocket) socket;
String[] protocols = secureSocket.getEnabledProtocols();
Set<String> allowedProtocols = new HashSet<String>();
for(String ep : protocols) {
if (! ep.toUpperCase().startsWith("SSLV3")) {
LOG.info("Enabling Protocol: [" + ep + "]");
allowedProtocols.add(ep);
}
else {
LOG.info("Disabling Protocol: [" + ep + "]");
}
}
if (!allowedProtocols.isEmpty()) {
secureSocket.setEnabledProtocols(allowedProtocols.toArray(new String[0]));
}
}
Socket client = null;
try {
while ( (client = socket.accept()) != null ) {
Thread clientValidatorThread = new Thread(new PasswordValidator(client));
clientValidatorThread.start();
}
} catch (IOException e) {
socket.close();
throw(e);
}
}
private InputStream getFileInputStream(String path) throws FileNotFoundException {
InputStream ret = null;
File f = new File(path);
if (f.exists()) {
ret = new FileInputStream(f);
}
else {
ret = getClass().getResourceAsStream(path);
if (ret == null) {
ret = getClass().getResourceAsStream("/" + path);
}
}
return ret;
}
}