| /* |
| * 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 java.io.File; |
| 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; |
| |
| 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; |
| |
| /** |
| * 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 = "ipa_user_group"; |
| |
| /** |
| * 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<>(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 void open(PrincipalKeyCredential administratorCredential, String defaultRealm, Map<String, String> kerberosConfiguration) |
| throws KerberosOperationException { |
| |
| setAdministratorCredential(administratorCredential); |
| setDefaultRealm(defaultRealm); |
| |
| if (kerberosConfiguration != null) { |
| setKeyEncryptionTypes(translateEncryptionTypes(kerberosConfiguration.get(KERBEROS_ENV_ENCRYPTION_TYPES), "\\s+")); |
| setExecutableSearchPaths(kerberosConfiguration.get(KERBEROS_ENV_EXECUTABLE_SEARCH_PATHS)); |
| } |
| } |
| |
| /** |
| * 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 void close() throws KerberosOperationException { |
| setOpen(false); |
| } |
| |
| /** |
| * 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 |
| * @param service a boolean value indicating whether the principal is for a service or not |
| * @return true if the principal exists; false otherwise |
| * @throws KerberosOperationException |
| */ |
| public abstract boolean principalExists(String principal, boolean service) |
| 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 |
| * @param service a boolean value indicating whether the principal is for a service or not |
| * @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, boolean service) |
| 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 |
| * @param service a boolean value indicating whether the principal is for a service or not |
| * @return true if the principal was successfully removed; otherwise false |
| * @throws KerberosOperationException |
| */ |
| public abstract boolean removePrincipal(String principal, boolean service) |
| 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(), false); |
| } |
| } |
| |
| /** |
| * 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<>(keyEncryptionTypes); |
| List<KeytabEntry> keytabEntries = new ArrayList<>(); |
| 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 |
| */ |
| public 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.emptyList() |
| : new ArrayList<>(keytab.getEntries()); |
| List<KeytabEntry> updateEntries = (updates == null) |
| ? Collections.emptyList() |
| : new ArrayList<>(updates.getEntries()); |
| List<KeytabEntry> mergedEntries = new ArrayList<>(); |
| |
| 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 = Collections.unmodifiableSet(new HashSet<>( |
| (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<>(); |
| 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 (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()); |
| } |
| |
| if (encryptionTypes == null) { |
| LOG.warn("The given encryption type name ({}) is not supported.", name); |
| return Collections.emptySet(); |
| } |
| |
| return 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 |
| * @throws KerberosOperationException When all the encryption type names are not supported |
| */ |
| protected Set<EncryptionType> translateEncryptionTypes(String names, String delimiter) |
| throws KerberosOperationException { |
| Set<EncryptionType> encryptionTypes = new HashSet<>(); |
| |
| if (!StringUtils.isEmpty(names)) { |
| for (String name : names.split((delimiter == null) ? "\\s+" : delimiter)) { |
| encryptionTypes.addAll(translateEncryptionType(name.trim())); |
| } |
| } |
| |
| if (encryptionTypes.isEmpty()) { |
| throw new KerberosOperationException("All the encryption type names you set are not supported. Aborting."); |
| } |
| 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; |
| } |
| } |