This project has retired. For details please refer to its Attic page.
SaslNettyClient 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  
19  package org.apache.giraph.comm.netty;
20  
21  import org.apache.giraph.comm.requests.SaslTokenMessageRequest;
22  import org.apache.hadoop.conf.Configuration;
23  import org.apache.hadoop.mapred.JobConf;
24  import org.apache.hadoop.mapreduce.security.TokenCache;
25  import org.apache.hadoop.mapreduce.security.token.JobTokenIdentifier;
26  import org.apache.hadoop.security.Credentials;
27  /*if_not[STATIC_SASL_SYMBOL]*/
28  import org.apache.hadoop.security.SaslPropertiesResolver;
29  /*end[STATIC_SASL_SYMBOL]*/
30  import org.apache.hadoop.security.SaslRpcServer;
31  import org.apache.hadoop.security.SaslRpcServer.AuthMethod;
32  import org.apache.hadoop.security.token.Token;
33  import org.apache.hadoop.security.token.TokenIdentifier;
34  import org.apache.hadoop.security.UserGroupInformation;
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.RealmCallback;
43  import javax.security.sasl.RealmChoiceCallback;
44  import javax.security.sasl.Sasl;
45  import javax.security.sasl.SaslClient;
46  import javax.security.sasl.SaslException;
47  import java.io.IOException;
48  
49  /**
50   * Implements SASL logic for Giraph BSP worker clients.
51   */
52  public class SaslNettyClient {
53    /** Class logger */
54    public static final Logger LOG = Logger.getLogger(SaslNettyClient.class);
55  
56    /**
57     * Used to synchronize client requests: client's work-related requests must
58     * wait until SASL authentication completes.
59     */
60    private Object authenticated = new Object();
61  
62    /**
63     * Used to respond to server's counterpart, SaslServer with SASL tokens
64     * represented as byte arrays.
65     */
66    private SaslClient saslClient;
67  
68    /**
69     * Create a SaslNettyClient for authentication with BSP servers.
70     */
71    public SaslNettyClient() {
72      try {
73        Token<? extends TokenIdentifier> token =
74            createJobToken(new Configuration());
75        if (LOG.isDebugEnabled()) {
76          LOG.debug("SaslNettyClient: Creating SASL " +
77              AuthMethod.DIGEST.getMechanismName() +
78              " client to authenticate to service at " + token.getService());
79        }
80  /*if[STATIC_SASL_SYMBOL]
81        saslClient =
82            Sasl.createSaslClient(
83                new String[] { AuthMethod.DIGEST.getMechanismName() }, null,
84                null, SaslRpcServer.SASL_DEFAULT_REALM, SaslRpcServer.SASL_PROPS,
85                new SaslClientCallbackHandler(token));
86  else[STATIC_SASL_SYMBOL]*/
87        SaslPropertiesResolver saslPropsResolver =
88            SaslPropertiesResolver.getInstance(new Configuration());
89        saslClient =
90            Sasl.createSaslClient(
91                new String[] { AuthMethod.DIGEST.getMechanismName() }, null,
92                null, SaslRpcServer.SASL_DEFAULT_REALM,
93                saslPropsResolver.getDefaultProperties(),
94                new SaslClientCallbackHandler(token));
95  /*end[STATIC_SASL_SYMBOL]*/
96      } catch (IOException e) {
97        LOG.error("SaslNettyClient: Could not obtain job token for Netty " +
98            "Client to use to authenticate with a Netty Server.");
99        saslClient = null;
100     }
101   }
102 
103   public Object getAuthenticated() {
104     return authenticated;
105   }
106 
107   /**
108    * Obtain JobToken, which we'll use as a credential for SASL authentication
109    * when connecting to other Giraph BSPWorkers.
110    *
111    * @param conf Configuration
112    * @return a JobToken containing username and password so that client can
113    * authenticate with a server.
114    */
115   private Token<JobTokenIdentifier> createJobToken(Configuration conf)
116     throws IOException {
117     String localJobTokenFile = System.getenv().get(
118         UserGroupInformation.HADOOP_TOKEN_FILE_LOCATION);
119     if (localJobTokenFile != null) {
120       JobConf jobConf = new JobConf(conf);
121       Credentials credentials =
122           TokenCache.loadTokens(localJobTokenFile, jobConf);
123       return TokenCache.getJobToken(credentials);
124     } else {
125       throw new IOException("createJobToken: Cannot obtain authentication " +
126           "credentials for job: file: '" +
127           UserGroupInformation.HADOOP_TOKEN_FILE_LOCATION + "' not found");
128     }
129   }
130 
131   /**
132    * Used by authenticateOnChannel() to initiate SASL handshake with server.
133    * @return SaslTokenMessageRequest message to be sent to server.
134    * @throws IOException
135    */
136   public SaslTokenMessageRequest firstToken()
137     throws IOException {
138     byte[] saslToken = new byte[0];
139     if (saslClient.hasInitialResponse()) {
140       saslToken = saslClient.evaluateChallenge(saslToken);
141     }
142     SaslTokenMessageRequest saslTokenMessage =
143         new SaslTokenMessageRequest();
144     saslTokenMessage.setSaslToken(saslToken);
145     return saslTokenMessage;
146   }
147 
148   public boolean isComplete() {
149     return saslClient.isComplete();
150   }
151 
152   /**
153    * Respond to server's SASL token.
154    * @param saslTokenMessage contains server's SASL token
155    * @return client's response SASL token
156    */
157   public byte[] saslResponse(SaslTokenMessageRequest saslTokenMessage) {
158     try {
159       byte[] retval =
160           saslClient.evaluateChallenge(saslTokenMessage.getSaslToken());
161       return retval;
162     } catch (SaslException e) {
163       LOG.error("saslResponse: Failed to respond to SASL server's token:", e);
164       return null;
165     }
166   }
167 
168   /**
169    * Implementation of javax.security.auth.callback.CallbackHandler
170    * that works with Hadoop JobTokens.
171    */
172   private static class SaslClientCallbackHandler implements CallbackHandler {
173     /** Generated username contained in JobToken */
174     private final String userName;
175     /** Generated password contained in JobToken */
176     private final char[] userPassword;
177 
178     /**
179      * Set private members using token.
180      * @param token Hadoop JobToken.
181      */
182     public SaslClientCallbackHandler(Token<? extends TokenIdentifier> token) {
183       this.userName = SaslNettyServer.encodeIdentifier(token.getIdentifier());
184       this.userPassword = SaslNettyServer.encodePassword(token.getPassword());
185     }
186 
187     /**
188      * Implementation used to respond to SASL tokens from server.
189      *
190      * @param callbacks objects that indicate what credential information the
191      *                  server's SaslServer requires from the client.
192      * @throws UnsupportedCallbackException
193      */
194     public void handle(Callback[] callbacks)
195       throws UnsupportedCallbackException {
196       NameCallback nc = null;
197       PasswordCallback pc = null;
198       RealmCallback rc = null;
199       for (Callback callback : callbacks) {
200         if (callback instanceof RealmChoiceCallback) {
201           continue;
202         } else if (callback instanceof NameCallback) {
203           nc = (NameCallback) callback;
204         } else if (callback instanceof PasswordCallback) {
205           pc = (PasswordCallback) callback;
206         } else if (callback instanceof RealmCallback) {
207           rc = (RealmCallback) callback;
208         } else {
209           throw new UnsupportedCallbackException(callback,
210               "handle: Unrecognized SASL client callback");
211         }
212       }
213       if (nc != null) {
214         if (LOG.isDebugEnabled()) {
215           LOG.debug("handle: SASL client callback: setting username: " +
216               userName);
217         }
218         nc.setName(userName);
219       }
220       if (pc != null) {
221         if (LOG.isDebugEnabled()) {
222           LOG.debug("handle: SASL client callback: setting userPassword");
223         }
224         pc.setPassword(userPassword);
225       }
226       if (rc != null) {
227         if (LOG.isDebugEnabled()) {
228           LOG.debug("handle: SASL client callback: setting realm: " +
229               rc.getDefaultText());
230         }
231         rc.setText(rc.getDefaultText());
232       }
233     }
234   }
235 }