001/** 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018 019package org.apache.hadoop.security; 020 021import java.io.DataInput; 022import java.io.DataOutput; 023import java.io.IOException; 024import java.security.Security; 025import java.util.ArrayList; 026import java.util.Enumeration; 027import java.util.HashMap; 028import java.util.List; 029import java.util.Map; 030import javax.security.auth.callback.CallbackHandler; 031import javax.security.sasl.Sasl; 032import javax.security.sasl.SaslException; 033import javax.security.sasl.SaslServer; 034import javax.security.sasl.SaslServerFactory; 035 036import org.apache.commons.logging.Log; 037import org.apache.commons.logging.LogFactory; 038import org.apache.hadoop.classification.InterfaceAudience; 039import org.apache.hadoop.classification.InterfaceStability; 040import org.apache.hadoop.conf.Configuration; 041import org.apache.hadoop.ipc.Server; 042import org.apache.hadoop.ipc.Server.Connection; 043import org.apache.hadoop.security.rpcauth.DigestAuthMethod; 044import org.apache.hadoop.security.rpcauth.KerberosAuthMethod; 045import org.apache.hadoop.security.rpcauth.RpcAuthMethod; 046import org.apache.hadoop.security.rpcauth.RpcAuthRegistry; 047import org.apache.hadoop.security.token.SecretManager; 048import org.apache.hadoop.security.token.SecretManager.InvalidToken; 049import org.apache.hadoop.security.token.TokenIdentifier; 050 051/** 052 * A utility class for dealing with SASL on RPC server 053 */ 054@InterfaceAudience.LimitedPrivate({"HDFS", "MapReduce"}) 055@InterfaceStability.Evolving 056public class SaslRpcServer { 057 public static final Log LOG = LogFactory.getLog(SaslRpcServer.class); 058 public static final String SASL_DEFAULT_REALM = "default"; 059 public static final String SASL_AUTH_SECRET_MANAGER = "org.apache.hadoop.auth.secret.manager"; 060 public static final String SASL_KERBEROS_PRINCIPAL = "org.apache.hadoop.auth.kerberos.principal"; 061 public static final String SASL_AUTH_TOKEN = "org.apache.hadoop.auth.token"; 062 063 public static enum QualityOfProtection { 064 AUTHENTICATION("auth"), 065 INTEGRITY("auth-int"), 066 PRIVACY("auth-conf"); 067 068 public final String saslQop; 069 070 private QualityOfProtection(String saslQop) { 071 this.saslQop = saslQop; 072 } 073 074 public String getSaslQop() { 075 return saslQop; 076 } 077 } 078 079 public RpcAuthMethod authMethod; 080 public String mechanism; 081 public String protocol; 082 public String serverId; 083 084 @InterfaceAudience.Private 085 @InterfaceStability.Unstable 086 public SaslRpcServer(RpcAuthMethod authMethod) throws IOException { 087 this.authMethod = authMethod; 088 mechanism = authMethod.getMechanismName(); 089 if (authMethod.equals(RpcAuthRegistry.SIMPLE)) { 090 return; // no sasl for simple 091 } 092 protocol = authMethod.getProtocol(); 093 serverId = authMethod.getServerId(); 094 } 095 096 @InterfaceAudience.Private 097 @InterfaceStability.Unstable 098 public SaslServer create(final Connection connection, 099 Map<String,Object> saslProperties, 100 SecretManager<TokenIdentifier> secretManager 101 ) throws IOException, InterruptedException { 102 if (secretManager != null) { 103 saslProperties.put(SaslRpcServer.SASL_AUTH_SECRET_MANAGER, secretManager); 104 } 105 if (LOG.isDebugEnabled()) { 106 LOG.debug("creating SaslServer for authMethod: " + authMethod); 107 } 108 return authMethod.createSaslServer(connection, saslProperties); 109 } 110 111 public static void init(Configuration conf) { 112 Security.addProvider(new SaslPlainServer.SecurityProvider()); 113 } 114 115 static String encodeIdentifier(byte[] identifier) { 116 return DigestAuthMethod.encodeIdentifier(identifier); 117 } 118 119 static byte[] decodeIdentifier(String identifier) { 120 return DigestAuthMethod.decodeIdentifier(identifier); 121 } 122 123 public static <T extends TokenIdentifier> T getIdentifier(String id, 124 SecretManager<T> secretManager) throws InvalidToken { 125 return DigestAuthMethod.getIdentifier(id, secretManager); 126 } 127 128 static char[] encodePassword(byte[] password) { 129 return DigestAuthMethod.encodePassword(password); 130 } 131 132 /** Splitting fully qualified Kerberos name into parts */ 133 public static String[] splitKerberosName(String fullName) { 134 return KerberosAuthMethod.splitKerberosName(fullName); 135 } 136 137 /** Authentication method */ 138 //TODO : Deprecate this after moving all the tests to use UGI.AuthenticationMethod 139 @InterfaceStability.Evolving 140 public static enum AuthMethod { 141 SIMPLE((byte) 80, ""), 142 KERBEROS((byte) 81, "GSSAPI"), 143 @Deprecated 144 DIGEST((byte) 82, "DIGEST-MD5"), 145 TOKEN((byte) 82, "DIGEST-MD5"), 146 PLAIN((byte) 83, "PLAIN"); 147 148 /** The code for this method. */ 149 public final byte code; 150 public final String mechanismName; 151 152 private AuthMethod(byte code, String mechanismName) { 153 this.code = code; 154 this.mechanismName = mechanismName; 155 } 156 157 private static final int FIRST_CODE = values()[0].code; 158 159 /** Return the object represented by the code. */ 160 private static AuthMethod valueOf(byte code) { 161 final int i = (code & 0xff) - FIRST_CODE; 162 return i < 0 || i >= values().length ? null : values()[i]; 163 } 164 165 /** Return the SASL mechanism name */ 166 public String getMechanismName() { 167 return mechanismName; 168 } 169 170 /** Read from in */ 171 public static AuthMethod read(DataInput in) throws IOException { 172 return valueOf(in.readByte()); 173 } 174 175 /** Write to out */ 176 public void write(DataOutput out) throws IOException { 177 out.write(code); 178 } 179 }; 180 181 /** CallbackHandler for SASL DIGEST-MD5 mechanism */ 182 @InterfaceStability.Evolving 183 public static class SaslDigestCallbackHandler 184 extends org.apache.hadoop.security.rpcauth.DigestAuthMethod.SaslDigestCallbackHandler { 185 public SaslDigestCallbackHandler( 186 SecretManager<TokenIdentifier> secretManager, 187 Server.Connection connection) { 188 super(secretManager, connection); 189 } 190 } 191 192 /** CallbackHandler for SASL GSSAPI Kerberos mechanism */ 193 @InterfaceStability.Evolving 194 public static class SaslGssCallbackHandler 195 extends org.apache.hadoop.security.rpcauth.KerberosAuthMethod.SaslGssCallbackHandler { 196 } 197 198 // TODO: Consider using this inside RpcAuthMethod implementations. 199 // Sasl.createSaslServer is 100-200X slower than caching the factories! 200 private static class FastSaslServerFactory implements SaslServerFactory { 201 private final Map<String,List<SaslServerFactory>> factoryCache = 202 new HashMap<String,List<SaslServerFactory>>(); 203 204 FastSaslServerFactory(Map<String,?> props) { 205 final Enumeration<SaslServerFactory> factories = 206 Sasl.getSaslServerFactories(); 207 while (factories.hasMoreElements()) { 208 SaslServerFactory factory = factories.nextElement(); 209 for (String mech : factory.getMechanismNames(props)) { 210 if (!factoryCache.containsKey(mech)) { 211 factoryCache.put(mech, new ArrayList<SaslServerFactory>()); 212 } 213 factoryCache.get(mech).add(factory); 214 } 215 } 216 } 217 218 @Override 219 public SaslServer createSaslServer(String mechanism, String protocol, 220 String serverName, Map<String,?> props, CallbackHandler cbh) 221 throws SaslException { 222 SaslServer saslServer = null; 223 List<SaslServerFactory> factories = factoryCache.get(mechanism); 224 if (factories != null) { 225 for (SaslServerFactory factory : factories) { 226 saslServer = factory.createSaslServer( 227 mechanism, protocol, serverName, props, cbh); 228 if (saslServer != null) { 229 break; 230 } 231 } 232 } 233 return saslServer; 234 } 235 236 @Override 237 public String[] getMechanismNames(Map<String, ?> props) { 238 return factoryCache.keySet().toArray(new String[0]); 239 } 240 } 241}