blob: 527d4e640780f6566e15d6078bcf7e0d828b97e2 [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.slider.server.services.security;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang.RandomStringUtils;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.fs.RawLocalFileSystem;
import org.apache.hadoop.fs.permission.FsAction;
import org.apache.hadoop.fs.permission.FsPermission;
import org.apache.slider.common.SliderKeys;
import org.apache.slider.common.SliderXmlConfKeys;
import org.apache.slider.core.conf.MapOperations;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.io.IOException;
//import java.nio.file.Files;
//import java.nio.file.Path;
//import java.nio.file.Paths;
//import java.nio.file.attribute.PosixFilePermission;
//import java.nio.file.attribute.PosixFilePermissions;
/**
*
*/
public class SecurityUtils {
private static final Logger LOG =
LoggerFactory.getLogger(SecurityUtils.class);
private static String CA_CONFIG_CONTENTS ="[ ca ]\n"
+ "default_ca = CA_CLIENT\n"
+ "[ CA_CLIENT ]\n"
+ "dir\t\t = ${SEC_DIR}/db\n"
+ "certs = $dir/certs\n"
+ "new_certs_dir = $dir/newcerts\n"
+ "\n"
+ "database = $dir/index.txt\n"
+ "serial = $dir/serial\n"
+ "default_days = 365 \n"
+ "\n"
+ "default_crl_days = 7 \n"
+ "default_md = md5 \n"
+ "\n"
+ "policy = policy_anything \n"
+ "\n"
+ "[ policy_anything ]\n"
+ "countryName = optional\n"
+ "stateOrProvinceName = optional \n"
+ "localityName = optional\n"
+ "organizationName = optional\n"
+ "organizationalUnitName = optional\n"
+ "commonName = optional \n"
+ "emailAddress = optional \n"
+ "\n"
+ "[ jdk7_ca ]\n"
+ "subjectKeyIdentifier = hash\n"
+ "authorityKeyIdentifier = keyid:always,issuer:always\n"
+ "basicConstraints = CA:true";
private static final String PASS_TOKEN = "pass:";
private static String keystorePass;
private static String securityDir;
public static void logOpenSslExitCode(String command, int exitCode) {
if (exitCode == 0) {
LOG.info(getOpenSslCommandResult(command, exitCode));
} else {
LOG.warn(getOpenSslCommandResult(command, exitCode));
}
}
public static String hideOpenSslPassword(String command){
int start = command.indexOf(PASS_TOKEN)+PASS_TOKEN.length();
CharSequence cs = command.subSequence(start, command.indexOf(" ", start));
return command.replace(cs, "****");
}
public static String getOpenSslCommandResult(String command, int exitCode) {
return new StringBuilder().append("Command ")
.append(hideOpenSslPassword(command))
.append(" was finished with exit code: ")
.append(exitCode).append(" - ")
.append(getOpenSslExitCodeDescription(exitCode)).toString();
}
private static String getOpenSslExitCodeDescription(int exitCode) {
switch (exitCode) {
case 0: {
return "the operation was completed successfully.";
}
case 1: {
return "an error occurred parsing the command options.";
}
case 2: {
return "one of the input files could not be read.";
}
case 3: {
return "an error occurred creating the PKCS#7 file or when reading the MIME message.";
}
case 4: {
return "an error occurred decrypting or verifying the message.";
}
case 5: {
return "the message was verified correctly but an error occurred writing out the signers certificates.";
}
default:
return "unsupported code";
}
}
public static void writeCaConfigFile(String path) throws IOException {
String contents = CA_CONFIG_CONTENTS.replace("${SEC_DIR}", path);
FileUtils.writeStringToFile(new File(path, "ca.config"), contents);
}
public static String getKeystorePass() {
return keystorePass;
}
public static String getSecurityDir() {
return securityDir;
}
public static void initializeSecurityParameters(MapOperations configMap) {
initializeSecurityParameters(configMap, false);
}
public static void initializeSecurityParameters(MapOperations configMap,
boolean persistPassword) {
String keyStoreLocation = configMap.getOption(
SliderXmlConfKeys.KEY_KEYSTORE_LOCATION, getDefaultKeystoreLocation());
File secDirFile = new File(keyStoreLocation).getParentFile();
if (!secDirFile.exists()) {
// create entire required directory structure
File dbDir = new File(secDirFile, "db");
File newCertsDir = new File(dbDir, "newcerts");
newCertsDir.mkdirs();
try {
RawLocalFileSystem fileSystem = new RawLocalFileSystem();
FsPermission permissions = new FsPermission(FsAction.ALL, FsAction.NONE,
FsAction.NONE);
fileSystem.setPermission(new Path(dbDir.getAbsolutePath()),
permissions);
fileSystem.setPermission(new Path(dbDir.getAbsolutePath()), permissions);
fileSystem.setPermission(new Path(newCertsDir.getAbsolutePath()),
permissions);
File indexFile = new File(dbDir, "index.txt");
indexFile.createNewFile();
SecurityUtils.writeCaConfigFile(secDirFile.getAbsolutePath().replace('\\', '/'));
} catch (IOException e) {
LOG.error("Unable to create SSL configuration directories/files", e);
}
// need to create the password
}
keystorePass = getKeystorePassword(secDirFile, persistPassword);
securityDir = secDirFile.getAbsolutePath();
}
private static String getKeystorePassword(File secDirFile,
boolean persistPassword) {
File passFile = new File(secDirFile, SliderKeys.CRT_PASS_FILE_NAME);
String password = null;
if (!passFile.exists()) {
LOG.info("Generating keystore password");
password = RandomStringUtils.randomAlphanumeric(
Integer.valueOf(SliderKeys.PASS_LEN));
if (persistPassword) {
try {
FileUtils.writeStringToFile(passFile, password);
passFile.setWritable(true);
passFile.setReadable(true);
} catch (IOException e) {
e.printStackTrace();
throw new RuntimeException(
"Error creating certificate password file");
}
}
} else {
LOG.info("Reading password from existing file");
try {
password = FileUtils.readFileToString(passFile);
password = password.replaceAll("\\p{Cntrl}", "");
} catch (IOException e) {
e.printStackTrace();
}
}
return password;
}
private static String getDefaultKeystoreLocation() {
File workDir = null;
try {
workDir = new File(FileUtils.getTempDirectory().getAbsolutePath()
+ "/sec" + System.currentTimeMillis());
if (!workDir.mkdirs()) {
throw new IOException("Unable to create temporary security directory");
}
} catch (IOException e) {
LOG.warn("Unable to create security directory");
return null;
}
return new StringBuilder().append(workDir.getAbsolutePath())
.append(File.separator)
.append(SliderKeys.SECURITY_DIR)
.append(File.separator)
.append(SliderKeys.KEYSTORE_FILE_NAME).toString();
}
}