This project has retired. For details please refer to its Attic page.
SaslNettyServer xref
View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *     http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing, software
13   * distributed under the License is distributed on an "AS IS" BASIS,
14   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   * See the License for the specific language governing permissions and
16   * limitations under the License.
17   */
18  package org.apache.giraph.comm.netty;
19  
20  import org.apache.commons.net.util.Base64;
21  import org.apache.hadoop.classification.InterfaceStability;
22  /*if_not[STATIC_SASL_SYMBOL]*/
23  import org.apache.hadoop.conf.Configuration;
24  /*end[STATIC_SASL_SYMBOL]*/
25  /*if[HADOOP_1_SECURITY]
26  else[HADOOP_1_SECURITY]*/
27  import org.apache.hadoop.ipc.StandbyException;
28  /*end[HADOOP_1_SECURITY]*/
29  import org.apache.hadoop.mapreduce.security.token.JobTokenIdentifier;
30  import org.apache.hadoop.mapreduce.security.token.JobTokenSecretManager;
31  /*if_not[STATIC_SASL_SYMBOL]*/
32  import org.apache.hadoop.security.SaslPropertiesResolver;
33  /*end[STATIC_SASL_SYMBOL]*/
34  import org.apache.hadoop.security.SaslRpcServer;
35  import org.apache.log4j.Logger;
36  
37  import javax.security.auth.callback.Callback;
38  import javax.security.auth.callback.CallbackHandler;
39  import javax.security.auth.callback.NameCallback;
40  import javax.security.auth.callback.PasswordCallback;
41  import javax.security.auth.callback.UnsupportedCallbackException;
42  import javax.security.sasl.AuthorizeCallback;
43  import javax.security.sasl.RealmCallback;
44  import javax.security.sasl.Sasl;
45  import javax.security.sasl.SaslException;
46  import javax.security.sasl.SaslServer;
47  import java.io.IOException;
48  import java.nio.charset.Charset;
49  import org.apache.hadoop.security.SaslRpcServer.AuthMethod;
50  
51  /**
52   * Encapsulates SASL server logic for Giraph BSP worker servers.
53   */
54  public class SaslNettyServer extends SaslRpcServer {
55    /** Logger */
56    public static final Logger LOG = Logger.getLogger(SaslNettyServer.class);
57  
58    /**
59     * Actual SASL work done by this object from javax.security.sasl.
60     * Initialized below in constructor.
61     */
62    private SaslServer saslServer;
63  
64    /**
65     * Constructor
66     *
67     * @param secretManager supplied by SaslServerHandler.
68     */
69    public SaslNettyServer(JobTokenSecretManager secretManager)
70      throws IOException {
71      this(secretManager, AuthMethod.SIMPLE);
72    }
73  
74    /**
75     * Constructor
76     *
77     * @param secretManager supplied by SaslServerHandler.
78     * @param authMethod Authentication method
79     */
80    public SaslNettyServer(JobTokenSecretManager secretManager,
81      AuthMethod authMethod) throws IOException {
82  /*if[HADOOP_1_SECRET_MANAGER]
83  else[HADOOP_1_SECRET_MANAGER]*/
84      super(authMethod);
85  /*end[HADOOP_1_SECRET_MANAGER]*/
86      if (LOG.isDebugEnabled()) {
87        LOG.debug("SaslNettyServer: Secret manager is: " + secretManager +
88          " with authmethod " + authMethod);
89      }
90  /*if[HADOOP_1_SECRET_MANAGER]
91  else[HADOOP_1_SECRET_MANAGER]*/
92      try {
93        secretManager.checkAvailableForRead();
94      } catch (StandbyException e) {
95        LOG.error("SaslNettyServer: Could not read secret manager: " + e);
96      }
97  /*end[HADOOP_1_SECRET_MANAGER]*/
98      try {
99        SaslDigestCallbackHandler ch =
100           new SaslNettyServer.SaslDigestCallbackHandler(secretManager);
101 /*if[STATIC_SASL_SYMBOL]
102       saslServer =
103           Sasl.createSaslServer(
104               SaslNettyServer.AuthMethod.DIGEST.getMechanismName(), null,
105               SaslRpcServer.SASL_DEFAULT_REALM, SaslRpcServer.SASL_PROPS, ch);
106 else[STATIC_SASL_SYMBOL]*/
107       SaslPropertiesResolver saslPropsResolver =
108           SaslPropertiesResolver.getInstance(new Configuration());
109       saslServer =
110           Sasl.createSaslServer(
111               SaslNettyServer.AuthMethod.DIGEST.getMechanismName(), null,
112               SaslRpcServer.SASL_DEFAULT_REALM,
113               saslPropsResolver.getDefaultProperties(), ch);
114 /*end[STATIC_SASL_SYMBOL]*/
115     } catch (SaslException e) {
116       LOG.error("SaslNettyServer: Could not create SaslServer: " + e);
117     }
118   }
119 
120   public boolean isComplete() {
121     return saslServer.isComplete();
122   }
123 
124   public String getUserName() {
125     return saslServer.getAuthorizationID();
126   }
127 
128   /**
129    * Used by SaslTokenMessage::processToken() to respond to server SASL tokens.
130    *
131    * @param token Server's SASL token
132    * @return token to send back to the server.
133    */
134   public byte[] response(byte[] token) {
135     try {
136       if (LOG.isDebugEnabled()) {
137         LOG.debug("response: Responding to input token of length: " +
138             token.length);
139       }
140       byte[] retval = saslServer.evaluateResponse(token);
141       if (LOG.isDebugEnabled()) {
142         LOG.debug("response: Response token length: " + retval.length);
143       }
144       return retval;
145     } catch (SaslException e) {
146       LOG.error("response: Failed to evaluate client token of length: " +
147           token.length + " : " + e);
148       return null;
149     }
150   }
151 
152   /**
153    * Encode a byte[] identifier as a Base64-encoded string.
154    *
155    * @param identifier identifier to encode
156    * @return Base64-encoded string
157    */
158   static String encodeIdentifier(byte[] identifier) {
159     return new String(Base64.encodeBase64(identifier),
160         Charset.defaultCharset());
161   }
162 
163   /**
164    * Encode a password as a base64-encoded char[] array.
165    * @param password as a byte array.
166    * @return password as a char array.
167    */
168   static char[] encodePassword(byte[] password) {
169     return new String(Base64.encodeBase64(password),
170         Charset.defaultCharset()).toCharArray();
171   }
172 
173   /** CallbackHandler for SASL DIGEST-MD5 mechanism */
174   @InterfaceStability.Evolving
175   public static class SaslDigestCallbackHandler implements CallbackHandler {
176     /** Used to authenticate the clients */
177     private JobTokenSecretManager secretManager;
178 
179     /**
180      * Constructor
181      *
182      * @param secretManager used to authenticate clients
183      */
184     public SaslDigestCallbackHandler(
185         JobTokenSecretManager secretManager) {
186       if (LOG.isDebugEnabled()) {
187         LOG.debug("SaslDigestCallback: Creating SaslDigestCallback handler " +
188             "with secret manager: " + secretManager);
189       }
190       this.secretManager = secretManager;
191     }
192 
193     /** {@inheritDoc} */
194     @Override
195     public void handle(Callback[] callbacks) throws IOException,
196         UnsupportedCallbackException {
197       NameCallback nc = null;
198       PasswordCallback pc = null;
199       AuthorizeCallback ac = null;
200       for (Callback callback : callbacks) {
201         if (callback instanceof AuthorizeCallback) {
202           ac = (AuthorizeCallback) callback;
203         } else if (callback instanceof NameCallback) {
204           nc = (NameCallback) callback;
205         } else if (callback instanceof PasswordCallback) {
206           pc = (PasswordCallback) callback;
207         } else if (callback instanceof RealmCallback) {
208           continue; // realm is ignored
209         } else {
210           throw new UnsupportedCallbackException(callback,
211               "handle: Unrecognized SASL DIGEST-MD5 Callback");
212         }
213       }
214       if (pc != null) {
215         JobTokenIdentifier tokenIdentifier = getIdentifier(nc.getDefaultName(),
216             secretManager);
217         char[] password =
218           encodePassword(secretManager.retrievePassword(tokenIdentifier));
219 
220         if (LOG.isDebugEnabled()) {
221           LOG.debug("handle: SASL server DIGEST-MD5 callback: setting " +
222               "password for client: " + tokenIdentifier.getUser());
223         }
224         pc.setPassword(password);
225       }
226       if (ac != null) {
227         String authid = ac.getAuthenticationID();
228         String authzid = ac.getAuthorizationID();
229         if (authid.equals(authzid)) {
230           ac.setAuthorized(true);
231         } else {
232           ac.setAuthorized(false);
233         }
234         if (ac.isAuthorized()) {
235           if (LOG.isDebugEnabled()) {
236             String username =
237               getIdentifier(authzid, secretManager).getUser().getUserName();
238             if (LOG.isDebugEnabled()) {
239               LOG.debug("handle: SASL server DIGEST-MD5 callback: setting " +
240                   "canonicalized client ID: " + username);
241             }
242           }
243           ac.setAuthorizedID(authzid);
244         }
245       }
246     }
247   }
248 }