blob: 02cbb579a15e34346e5a473af193a6ba2b75ba8d [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.ambari.server.serveraction.kerberos;
import org.apache.ambari.server.security.credential.PrincipalKeyCredential;
import org.apache.ambari.server.utils.ShellCommandUtil;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang.ArrayUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.directory.server.kerberos.shared.crypto.encryption.KerberosKeyFactory;
import org.apache.directory.server.kerberos.shared.keytab.Keytab;
import org.apache.directory.server.kerberos.shared.keytab.KeytabEntry;
import org.apache.directory.shared.kerberos.KerberosTime;
import org.apache.directory.shared.kerberos.codec.types.EncryptionType;
import org.apache.directory.shared.kerberos.components.EncryptionKey;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* KerberosOperationHandler is an abstract class providing basic implementations of common Kerberos
* operations (like generating secure passwords) and placeholders for KDC-specific operations
* (such as creating principals).
*/
public abstract class KerberosOperationHandler {
private final static Logger LOG = LoggerFactory.getLogger(KerberosOperationHandler.class);
/**
* Kerberos-env configuration property name: ldap_url
*/
public final static String KERBEROS_ENV_LDAP_URL = "ldap_url";
/**
* Kerberos-env configuration property name: container_dn
*/
public final static String KERBEROS_ENV_PRINCIPAL_CONTAINER_DN = "container_dn";
/**
* Kerberos-env configuration property name: group
*/
public final static String KERBEROS_ENV_USER_PRINCIPAL_GROUP = "group";
/**
* Kerberos-env configuration property name: password_chat_timeout
*/
public final static String KERBEROS_ENV_PASSWORD_CHAT_TIMEOUT = "password_chat_timeout";
/**
* Default timeout for password chat
*/
public final static int DEFAULT_PASSWORD_CHAT_TIMEOUT = 5;
/**
* Kerberos-env configuration property name: set_password_expiry
*/
public final static String KERBEROS_ENV_SET_PASSWORD_EXPIRY = "set_password_expiry";
/**
* Kerberos-env configuration property name: ad_create_attributes_template
*/
public final static String KERBEROS_ENV_AD_CREATE_ATTRIBUTES_TEMPLATE = "ad_create_attributes_template";
/**
* Kerberos-env configuration property name: kdc_create_attributes
*/
public final static String KERBEROS_ENV_KDC_CREATE_ATTRIBUTES = "kdc_create_attributes";
/**
* Kerberos-env configuration property name: encryption_types
*/
public final static String KERBEROS_ENV_ENCRYPTION_TYPES = "encryption_types";
/**
* Kerberos-env configuration property name: kdc_hosts
*/
public final static String KERBEROS_ENV_KDC_HOSTS = "kdc_hosts";
/**
* Kerberos-env configuration property name: admin_server_host
*/
public final static String KERBEROS_ENV_ADMIN_SERVER_HOST = "admin_server_host";
/**
* Kerberos-env configuration property name: executable_search_paths
*/
public final static String KERBEROS_ENV_EXECUTABLE_SEARCH_PATHS = "executable_search_paths";
/**
* An array of String values declaring the default (ordered) list of path to search for executables
*/
private static final String[] DEFAULT_EXECUTABLE_SEARCH_PATHS = {"/usr/bin", "/usr/kerberos/bin", "/usr/sbin", "/usr/lib/mit/bin", "/usr/lib/mit/sbin"};
/**
* A Map of MIT KDC Encryption types to EncryptionType values.
* <p/>
* See http://web.mit.edu/kerberos/krb5-devel/doc/admin/conf_files/kdc_conf.html#encryption-types
*/
private static final Map<String, Set<EncryptionType>> ENCRYPTION_TYPE_TRANSLATION_MAP = Collections.unmodifiableMap(
new HashMap<String, Set<EncryptionType>>() {
{
// aes: The AES family: aes256-cts-hmac-sha1-96 and aes128-cts-hmac-sha1-96
put("aes", EnumSet.of(EncryptionType.AES256_CTS_HMAC_SHA1_96, EncryptionType.AES128_CTS_HMAC_SHA1_96));
// aes256-cts-hmac-sha1-96 aes256-cts: AES-256 CTS mode with 96-bit SHA-1 HMAC
put("aes256-cts-hmac-sha1-96", EnumSet.of(EncryptionType.AES256_CTS_HMAC_SHA1_96));
put("aes256-cts", EnumSet.of(EncryptionType.AES256_CTS_HMAC_SHA1_96));
put("aes-256", EnumSet.of(EncryptionType.AES256_CTS_HMAC_SHA1_96));
// aes128-cts-hmac-sha1-96 aes128-cts AES-128: CTS mode with 96-bit SHA-1 HMAC
put("aes128-cts-hmac-sha1-96", EnumSet.of(EncryptionType.AES128_CTS_HMAC_SHA1_96));
put("aes128-cts", EnumSet.of(EncryptionType.AES128_CTS_HMAC_SHA1_96));
put("aes-128", EnumSet.of(EncryptionType.AES128_CTS_HMAC_SHA1_96));
// rc4 The RC4 family: arcfour-hmac
put("rc4", EnumSet.of(EncryptionType.RC4_HMAC));
// arcfour-hmac rc4-hmac arcfour-hmac-md5: RC4 with HMAC/MD5
put("arcfour-hmac", EnumSet.of(EncryptionType.RC4_HMAC));
put("rc4-hmac", EnumSet.of(EncryptionType.RC4_HMAC));
put("arcfour-hmac-md5", EnumSet.of(EncryptionType.UNKNOWN));
// arcfour-hmac-exp rc4-hmac-exp arcfour-hmac-md5-exp: Exportable RC4 with HMAC/MD5 (weak)
put("arcfour-hmac-exp", EnumSet.of(EncryptionType.RC4_HMAC_EXP));
put("rc4-hmac-exp", EnumSet.of(EncryptionType.RC4_HMAC_EXP));
put("arcfour-hmac-md5-exp", EnumSet.of(EncryptionType.UNKNOWN));
// camellia The Camellia family: camellia256-cts-cmac and camellia128-cts-cmac
put("camellia", EnumSet.of(EncryptionType.UNKNOWN));
// camellia256-cts-cmac camellia256-cts: Camellia-256 CTS mode with CMAC
put("camellia256-cts-cmac", EnumSet.of(EncryptionType.UNKNOWN));
put("camellia256-cts", EnumSet.of(EncryptionType.UNKNOWN));
// camellia128-cts-cmac camellia128-cts: Camellia-128 CTS mode with CMAC
put("camellia128-cts-cmac", EnumSet.of(EncryptionType.UNKNOWN));
put("camellia128-cts", EnumSet.of(EncryptionType.UNKNOWN));
//des: The DES family: des-cbc-crc, des-cbc-md5, and des-cbc-md4 (weak)
put("des", EnumSet.of(EncryptionType.DES_CBC_CRC, EncryptionType.DES_CBC_MD5, EncryptionType.DES_CBC_MD4));
// des-cbc-md4: DES cbc mode with RSA-MD4 (weak)
put("des-cbc-md4", EnumSet.of(EncryptionType.DES_CBC_MD4));
// des-cbc-md5: DES cbc mode with RSA-MD5 (weak)
put("des-cbc-md5", EnumSet.of(EncryptionType.DES_CBC_MD5));
// des-cbc-crc: DES cbc mode with CRC-32 (weak)
put("des-cbc-crc", EnumSet.of(EncryptionType.DES_CBC_CRC));
// des-cbc-raw: DES cbc mode raw (weak)
put("des-cbc-raw", EnumSet.of(EncryptionType.UNKNOWN));
// des-hmac-sha1: DES with HMAC/sha1 (weak)
put("des-hmac-sha1", EnumSet.of(EncryptionType.UNKNOWN));
// des3: The triple DES family: des3-cbc-sha1
put("des3", EnumSet.of(EncryptionType.DES3_CBC_SHA1_KD)); // Using DES3_CBC_SHA1_KD since DES3_CBC_SHA1 invalid key issues with KDC
// des3-cbc-raw: Triple DES cbc mode raw (weak)
put("des3-cbc-raw", EnumSet.of(EncryptionType.UNKNOWN));
// des3-cbc-sha1 des3-hmac-sha1 des3-cbc-sha1-kd: Triple DES cbc mode with HMAC/sha1
put("des3-cbc-sha1", EnumSet.of(EncryptionType.DES3_CBC_SHA1_KD)); // Using DES3_CBC_SHA1_KD since DES3_CBC_SHA1 invalid key issues with KDC
put("des3-hmac-sha1", EnumSet.of(EncryptionType.UNKNOWN));
put("des3-cbc-sha1-kd", EnumSet.of(EncryptionType.DES3_CBC_SHA1_KD));
}
}
);
/**
* The default set of ciphers to use for creating keytab entries
*/
private static final Set<EncryptionType> DEFAULT_CIPHERS = Collections.unmodifiableSet(
new HashSet<EncryptionType>() {{
add(EncryptionType.DES_CBC_MD5);
add(EncryptionType.DES3_CBC_SHA1_KD);
add(EncryptionType.RC4_HMAC);
add(EncryptionType.AES128_CTS_HMAC_SHA1_96);
add(EncryptionType.AES256_CTS_HMAC_SHA1_96);
}});
private PrincipalKeyCredential administratorCredential = null;
private String defaultRealm = null;
private Set<EncryptionType> keyEncryptionTypes = new HashSet<EncryptionType>(DEFAULT_CIPHERS);
private boolean open = false;
/**
* An array of String indicating an ordered list of filesystem paths to use to search for executables
* needed to perform Kerberos-related operations. For example, kadmin
*/
private String[] executableSearchPaths = null;
/**
* Prepares and creates resources to be used by this KerberosOperationHandler.
* <p/>
* It is expected that this KerberosOperationHandler will not be used before this call.
*
* @param administratorCredential a PrincipalKeyCredential containing the administrative credential
* for the relevant KDC
* @param defaultRealm a String declaring the default Kerberos realm (or domain)
* @param kerberosConfiguration a Map of key/value pairs containing data from the kerberos-env configuration set
*/
public abstract void open(PrincipalKeyCredential administratorCredential, String defaultRealm, Map<String, String> kerberosConfiguration)
throws KerberosOperationException;
/**
* Closes and cleans up any resources used by this KerberosOperationHandler
* <p/>
* It is expected that this KerberosOperationHandler will not be used after this call.
*/
public abstract void close()
throws KerberosOperationException;
/**
* Test to see if the specified principal exists in a previously configured KDC
* <p/>
* The implementation is specific to a particular type of KDC.
*
* @param principal a String containing the principal to test
* @return true if the principal exists; false otherwise
* @throws KerberosOperationException
*/
public abstract boolean principalExists(String principal)
throws KerberosOperationException;
/**
* Creates a new principal in a previously configured KDC
* <p/>
* The implementation is specific to a particular type of KDC.
*
* @param principal a String containing the principal to add
* @param password a String containing the password to use when creating the principal
* @param service a boolean value indicating whether the principal is to be created as a service principal or not
* @return an Integer declaring the generated key number
* @throws KerberosOperationException
* @throws KerberosPrincipalAlreadyExistsException if the principal already exists
*/
public abstract Integer createPrincipal(String principal, String password, boolean service)
throws KerberosOperationException;
/**
* Updates the password for an existing principal in a previously configured KDC
* <p/>
* The implementation is specific to a particular type of KDC.
*
* @param principal a String containing the principal to update
* @param password a String containing the password to set
* @return an Integer declaring the new key number
* @throws KerberosOperationException
* @throws KerberosPrincipalDoesNotExistException if the principal does not exist
*/
public abstract Integer setPrincipalPassword(String principal, String password)
throws KerberosOperationException;
/**
* Removes an existing principal in a previously configured KDC
* <p/>
* The implementation is specific to a particular type of KDC.
*
* @param principal a String containing the principal to remove
* @return true if the principal was successfully removed; otherwise false
* @throws KerberosOperationException
*/
public abstract boolean removePrincipal(String principal)
throws KerberosOperationException;
/**
* Tests to ensure the connection information and credentials allow for administrative
* connectivity to the KDC
*
* @return true of successful; otherwise false
* @throws KerberosOperationException if a failure occurs while testing the
* administrator credentials
*/
public boolean testAdministratorCredentials() throws KerberosOperationException {
if (!isOpen()) {
throw new KerberosOperationException("This operation handler has not been opened");
}
PrincipalKeyCredential credential = getAdministratorCredential();
if (credential == null) {
throw new KerberosOperationException("Missing KDC administrator credential");
} else {
return principalExists(credential.getPrincipal());
}
}
/**
* Create a keytab using the specified principal and password.
*
* @param principal a String containing the principal to test
* @param password a String containing the password to use when creating the principal
* @param keyNumber a Integer indicating the key number for the keytab entries
* @return the created Keytab
* @throws KerberosOperationException
*/
protected Keytab createKeytab(String principal, String password, Integer keyNumber)
throws KerberosOperationException {
if (StringUtils.isEmpty(principal)) {
throw new KerberosOperationException("Failed to create keytab file, missing principal");
}
if (password == null) {
throw new KerberosOperationException(String.format("Failed to create keytab file for %s, missing password", principal));
}
Set<EncryptionType> ciphers = new HashSet<EncryptionType>(keyEncryptionTypes);
List<KeytabEntry> keytabEntries = new ArrayList<KeytabEntry>();
Keytab keytab = new Keytab();
if (!ciphers.isEmpty()) {
// Create a set of keys and relevant keytab entries
Map<EncryptionType, EncryptionKey> keys = KerberosKeyFactory.getKerberosKeys(principal, password, ciphers);
if (keys != null) {
byte keyVersion = (keyNumber == null) ? 0 : keyNumber.byteValue();
KerberosTime timestamp = new KerberosTime();
for (EncryptionKey encryptionKey : keys.values()) {
keytabEntries.add(new KeytabEntry(principal, 1, timestamp, keyVersion, encryptionKey));
}
keytab.setEntries(keytabEntries);
}
}
return keytab;
}
/**
* Create or append to a keytab file using keytab data from another keytab file.
* <p/>
* If the destination keytab file contains keytab data, that data will be merged with the new data
* to create a composite set of keytab entries.
*
* @param sourceKeytabFile a File containing the absolute path to the file with the keytab data to store
* @param destinationKeytabFile a File containing the absolute path to where the keytab data is to be stored
* @return true if the keytab file was successfully created; false otherwise
* @throws KerberosOperationException
* @see #createKeytabFile(org.apache.directory.server.kerberos.shared.keytab.Keytab, java.io.File)
*/
protected boolean createKeytabFile(File sourceKeytabFile, File destinationKeytabFile)
throws KerberosOperationException {
return createKeytabFile(readKeytabFile(sourceKeytabFile), destinationKeytabFile);
}
/**
* Create or append to a keytab file using the specified principal and password.
* <p/>
* If the destination keytab file contains keytab data, that data will be merged with the new data
* to create a composite set of keytab entries.
*
* @param principal a String containing the principal to test
* @param password a String containing the password to use when creating the principal
* @param keyNumber an Integer declaring the relevant key number to use for the keytabs entries
* @param destinationKeytabFile a File containing the absolute path to where the keytab data is to be stored
* @return true if the keytab file was successfully created; false otherwise
* @throws KerberosOperationException
* @see #createKeytabFile(org.apache.directory.server.kerberos.shared.keytab.Keytab, java.io.File)
*/
protected boolean createKeytabFile(String principal, String password, Integer keyNumber, File destinationKeytabFile)
throws KerberosOperationException {
return createKeytabFile(createKeytab(principal, password, keyNumber), destinationKeytabFile);
}
/**
* Create or append to a keytab file using the specified Keytab
* <p/>
* If the destination keytab file contains keytab data, that data will be merged with the new data
* to create a composite set of keytab entries.
*
* @param keytab the Keytab containing the data to add to the keytab file
* @param destinationKeytabFile a File containing the absolute path to where the keytab data is to be stored
* @return true if the keytab file was successfully created; false otherwise
* @throws KerberosOperationException
*/
protected boolean createKeytabFile(Keytab keytab, File destinationKeytabFile)
throws KerberosOperationException {
if (destinationKeytabFile == null) {
throw new KerberosOperationException("The destination file path is null");
}
try {
mergeKeytabs(readKeytabFile(destinationKeytabFile), keytab).write(destinationKeytabFile);
return true;
} catch (IOException e) {
String message = "Failed to export keytab file";
LOG.error(message, e);
if (!destinationKeytabFile.delete()) {
destinationKeytabFile.deleteOnExit();
}
throw new KerberosOperationException(message, e);
}
}
/**
* Merge the keytab data from one keytab with the keytab data from a different keytab.
* <p/>
* If similar key entries exist for the same principal, the updated values will be used
*
* @param keytab a Keytab with the base keytab data
* @param updates a Keytab containing the updated keytab data
* @return a Keytab with the merged data
*/
protected Keytab mergeKeytabs(Keytab keytab, Keytab updates) {
List<KeytabEntry> keytabEntries = (keytab == null)
? Collections.<KeytabEntry>emptyList()
: new ArrayList<KeytabEntry>(keytab.getEntries());
List<KeytabEntry> updateEntries = (updates == null)
? Collections.<KeytabEntry>emptyList()
: new ArrayList<KeytabEntry>(updates.getEntries());
List<KeytabEntry> mergedEntries = new ArrayList<KeytabEntry>();
if (keytabEntries.isEmpty()) {
mergedEntries.addAll(updateEntries);
} else if (updateEntries.isEmpty()) {
mergedEntries.addAll(keytabEntries);
} else {
Iterator<KeytabEntry> iterator = keytabEntries.iterator();
while (iterator.hasNext()) {
KeytabEntry keytabEntry = iterator.next();
for (KeytabEntry entry : updateEntries) {
if (entry.getPrincipalName().equals(keytabEntry.getPrincipalName()) &&
entry.getKey().getKeyType().equals(keytabEntry.getKey().getKeyType())) {
iterator.remove();
break;
}
}
}
mergedEntries.addAll(keytabEntries);
mergedEntries.addAll(updateEntries);
}
Keytab mergedKeytab = new Keytab();
mergedKeytab.setEntries(mergedEntries);
return mergedKeytab;
}
/**
* Reads a file containing keytab data into a new Keytab
*
* @param file A File containing the path to the file from which to read keytab data
* @return a Keytab or null if the file was not readable
*/
protected Keytab readKeytabFile(File file) {
Keytab keytab;
if (file.exists() && file.canRead() && (file.length() > 0)) {
try {
keytab = Keytab.read(file);
} catch (IOException e) {
// There was an issue reading in the existing keytab file... quietly assume no data
keytab = null;
}
} else {
keytab = null;
}
return keytab;
}
/**
* Sets the KDC administrator credential.
*
* @return a credential
*/
public PrincipalKeyCredential getAdministratorCredential() {
return administratorCredential;
}
/**
* Sets the administrator credentials for this KerberosOperationHandler.
* <p/>
* If the supplied {@link PrincipalKeyCredential} is not <code>null</code>, validates that the administrator
* principal and values are not <code>null</code> or empty. If the credential value does not
* validate, then a {@link KerberosAdminAuthenticationException} will be thrown.
*
* @param administratorCredential the KDC administrator credential
* @throws KerberosAdminAuthenticationException if the non-null Credential fails to contain
* a non-empty principal and password values.
*/
public void setAdministratorCredential(PrincipalKeyCredential administratorCredential)
throws KerberosAdminAuthenticationException {
// Ensure the credential is not null
if (administratorCredential == null) {
throw new KerberosAdminAuthenticationException("The administrator credential must not be null");
}
// Ensure the principal is not null or empty
String principal = administratorCredential.getPrincipal();
if (StringUtils.isEmpty(principal)) {
throw new KerberosAdminAuthenticationException("Must specify a principal but it is null or empty");
}
// Ensure either the password or the keytab value is not null or empty
char[] password = administratorCredential.getKey();
if (ArrayUtils.isEmpty(password)) {
throw new KerberosAdminAuthenticationException("Must specify a password but it is null or empty");
}
this.administratorCredential = administratorCredential;
}
public String getDefaultRealm() {
return defaultRealm;
}
public void setDefaultRealm(String defaultRealm) {
this.defaultRealm = defaultRealm;
}
/**
* Gets the encryption algorithms used to encrypt keys in keytab entries
*
* @return a Set of EncryptionKey values indicating which algorithms are to be used when
* encrypting keys for keytab entries.
*/
public Set<EncryptionType> getKeyEncryptionTypes() {
return keyEncryptionTypes;
}
/**
* Sets the encryption algorithms to use to encrypt keys in keytab entries
* <p/>
* If set to <code>null</code> the default set of ciphers will be used. See {@link #DEFAULT_CIPHERS}
*
* @param keyEncryptionTypes a Set of EncryptionKey values or null to indicate the default set
*/
public void setKeyEncryptionTypes(Set<EncryptionType> keyEncryptionTypes) {
this.keyEncryptionTypes = new HashSet<EncryptionType>(
(keyEncryptionTypes == null)
? DEFAULT_CIPHERS
: keyEncryptionTypes
);
}
/**
* Gets the ordered array of search paths used to find Kerberos-related executables.
*
* @return an array of String values indicating an order list of filesystem paths to search
*/
public String[] getExecutableSearchPaths() {
return executableSearchPaths;
}
/**
* Sets the ordered array of search paths used to find Kerberos-related executables.
* <p/>
* If null, a default set of paths will be assumed when searching:
* <ul>
* <li>/usr/bin</li>
* <li>/usr/kerberos/bin</li>
* <li>/usr/sbin</li>
* <li>/usr/lib/mit/bin</li>
* <li>/usr/lib/mit/sbin</li>
* </ul>
*
* @param executableSearchPaths an array of String values indicating an ordered list of filesystem paths to search
*/
public void setExecutableSearchPaths(String[] executableSearchPaths) {
this.executableSearchPaths = executableSearchPaths;
}
/**
* Sets the ordered array of search paths used to find Kerberos-related executables.
* <p/>
* If null, a default set of paths will be assumed when searching:
* <ul>
* <li>/usr/bin</li>
* <li>/usr/kerberos/bin</li>
* <li>/usr/sbin</li>
* </ul>
*
* @param delimitedExecutableSearchPaths a String containing a comma-delimited (ordered) list of filesystem paths to search
*/
public void setExecutableSearchPaths(String delimitedExecutableSearchPaths) {
List<String> searchPaths = null;
if (delimitedExecutableSearchPaths != null) {
searchPaths = new ArrayList<String>();
for (String path : delimitedExecutableSearchPaths.split(",")) {
path = path.trim();
if (!path.isEmpty()) {
searchPaths.add(path);
}
}
}
setExecutableSearchPaths((searchPaths == null) ? null : searchPaths.toArray(new String[searchPaths.size()]));
}
/**
* Test this KerberosOperationHandler to see whether is was previously open or not
*
* @return a boolean value indicating whether this KerberosOperationHandler was open (true) or not (false)
*/
public boolean isOpen() {
return open;
}
/**
* Sets whether this KerberosOperationHandler is open or not.
*
* @param open a boolean value indicating whether this KerberosOperationHandler was open (true) or not (false)
*/
public void setOpen(boolean open) {
this.open = open;
}
/**
* Given base64-encoded keytab data, decode the String to binary data and write it to a (temporary)
* file.
* <p/>
* Upon success, a new file is created. The caller is expected to clean up this file when done
* with it.
*
* @param keytabData a String containing base64-encoded keytab data
* @return a File pointing to the decoded keytab file or null if not successful
* @throws KerberosOperationException
*/
protected File createKeytabFile(String keytabData)
throws KerberosOperationException {
boolean success = false;
File tempFile = null;
// Create a temporary file
try {
tempFile = File.createTempFile("temp", ".dat");
} catch (IOException e) {
LOG.error(String.format("Failed to create temporary keytab file: %s", e.getLocalizedMessage()), e);
}
if ((tempFile != null) && (keytabData != null)) {
OutputStream fos = null;
// Decoded the base64-encoded String and write it to the temporary file
try {
fos = new FileOutputStream(tempFile);
fos.write(Base64.decodeBase64(keytabData));
success = true;
} catch (FileNotFoundException e) {
String message = String.format("Failed to write to temporary keytab file %s: %s",
tempFile.getAbsolutePath(), e.getLocalizedMessage());
LOG.error(message, e);
throw new KerberosOperationException(message, e);
} catch (IOException e) {
String message = String.format("Failed to write to temporary keytab file %s: %s",
tempFile.getAbsolutePath(), e.getLocalizedMessage());
LOG.error(message, e);
throw new KerberosOperationException(message, e);
} finally {
if (fos != null) {
try {
fos.close();
} catch (IOException e) {
// Ignore this...
}
}
// If there was an issue, clean up the file
if (!success) {
if (!tempFile.delete()) {
tempFile.deleteOnExit();
}
tempFile = null;
}
}
}
return tempFile;
}
/**
* Executes a shell command.
* <p/>
* See {@link org.apache.ambari.server.utils.ShellCommandUtil#runCommand(String[], Map<String,String>)}
*
* @param command an array of String value representing the command and its arguments
* @param envp a map of string, string of environment variables
* @param interactiveHandler a handler to provide responses to queries from the command,
* or null if no queries are expected
* @return a ShellCommandUtil.Result declaring the result of the operation
* @throws KerberosOperationException
*/
protected ShellCommandUtil.Result executeCommand(String[] command, Map<String, String> envp, ShellCommandUtil.InteractiveHandler interactiveHandler)
throws KerberosOperationException {
if ((command == null) || (command.length == 0)) {
return null;
} else {
try {
return ShellCommandUtil.runCommand(command, envp, interactiveHandler, false);
} catch (IOException e) {
String message = String.format("Failed to execute the command: %s", e.getLocalizedMessage());
LOG.error(message, e);
throw new KerberosOperationException(message, e);
} catch (InterruptedException e) {
String message = String.format("Failed to wait for the command to complete: %s", e.getLocalizedMessage());
LOG.error(message, e);
throw new KerberosOperationException(message, e);
}
}
}
/**
* Executes a shell command.
* <p/>
* See {@link org.apache.ambari.server.utils.ShellCommandUtil#runCommand(String[])}
*
* @param command an array of String value representing the command and its arguments
* @return a ShellCommandUtil.Result declaring the result of the operation
* @throws KerberosOperationException
* @see #executeCommand(String[], Map, ShellCommandUtil.InteractiveHandler)
*/
protected ShellCommandUtil.Result executeCommand(String[] command)
throws KerberosOperationException {
return executeCommand(command, null);
}
/**
* Executes a shell command.
* <p/>
* See {@link org.apache.ambari.server.utils.ShellCommandUtil#runCommand(String[])}
*
* @param command an array of String value representing the command and its arguments
* @param interactiveHandler a handler to provide responses to queries from the command,
* or null if no queries are expected
* @return a ShellCommandUtil.Result declaring the result of the operation
* @throws KerberosOperationException
* @see #executeCommand(String[], Map, ShellCommandUtil.InteractiveHandler)
*/
protected ShellCommandUtil.Result executeCommand(String[] command, ShellCommandUtil.InteractiveHandler interactiveHandler) throws KerberosOperationException {
return executeCommand(command, null, interactiveHandler);
}
/**
* Given a principal, attempt to create a new DeconstructedPrincipal
*
* @param principal a String containing the principal to deconstruct
* @return a DeconstructedPrincipal
* @throws KerberosOperationException
*/
protected DeconstructedPrincipal createDeconstructPrincipal(String principal) throws KerberosOperationException {
try {
return DeconstructedPrincipal.valueOf(principal, getDefaultRealm());
} catch (IllegalArgumentException e) {
throw new KerberosOperationException(e.getMessage(), e);
}
}
/**
* Given a cipher (or algorithm) name, attempts to translate it into an EncryptionType value.
* <p/>
* If a translation is not able to be made, {@link org.apache.directory.shared.kerberos.codec.types.EncryptionType#UNKNOWN}
* is returned.
*
* @param name a String containing the name of the cipher to translate
* @return an EncryptionType
*/
protected Set<EncryptionType> translateEncryptionType(String name) {
Set<EncryptionType> encryptionTypes = null;
if (!StringUtils.isEmpty(name)) {
encryptionTypes = ENCRYPTION_TYPE_TRANSLATION_MAP.get(name.toLowerCase());
}
return (encryptionTypes == null) ? Collections.<EncryptionType>emptySet() : encryptionTypes;
}
/**
* Given a delimited set of encryption type names, attempts to translate into a set of EncryptionType
* values.
*
* @param names a String containing a delimited list of encryption type names
* @param delimiter a String declaring the delimiter to use to split names, if null, " " is used.
* @return a Set of EncryptionType values
*/
protected Set<EncryptionType> translateEncryptionTypes(String names, String delimiter) {
Set<EncryptionType> encryptionTypes = new HashSet<EncryptionType>();
if (!StringUtils.isEmpty(names)) {
for (String name : names.split((delimiter == null) ? "\\s+" : delimiter)) {
encryptionTypes.addAll(translateEncryptionType(name.trim()));
}
}
return encryptionTypes;
}
/**
* Iterates through the characters in a string to escape special characters
*
* @param string the String to process
* @param charactersToEscape a Set of characters declaring the special characters to escape
* @param escapeCharacter a character to use for escaping
* @return the string with escaped characters
*/
protected String escapeCharacters(String string, Set<Character> charactersToEscape, Character escapeCharacter) {
if (StringUtils.isEmpty(string) || (charactersToEscape == null) || charactersToEscape.isEmpty()) {
return string;
} else {
StringBuilder builder = new StringBuilder();
for (char character : string.toCharArray()) {
if (charactersToEscape.contains(character)) {
builder.append(escapeCharacter);
}
builder.append(character);
}
return builder.toString();
}
}
/**
* Given the name of an executable, searches the configured executable search path for an executable
* file with that name.
*
* @param executable a String declaring the name of the executable to find within the search path
* @return the absolute path of the found execute or the name of the executable if not found
* within the search path
* @see #setExecutableSearchPaths(String)
* @see #setExecutableSearchPaths(String[])
*/
protected String getExecutable(String executable) {
String[] searchPaths = getExecutableSearchPaths();
String executablePath = null;
if (searchPaths == null) {
searchPaths = DEFAULT_EXECUTABLE_SEARCH_PATHS;
}
for (String searchPath : searchPaths) {
File executableFile = new File(searchPath, executable);
if (executableFile.canExecute()) {
executablePath = executableFile.getAbsolutePath();
break;
}
}
return (executablePath == null) ? executable : executablePath;
}
}