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    package org.apache.hadoop.lib.service.security;
019    
020    import org.apache.hadoop.classification.InterfaceAudience;
021    import org.apache.hadoop.fs.http.server.HttpFSServerWebApp;
022    import org.apache.hadoop.hdfs.web.SWebHdfsFileSystem;
023    import org.apache.hadoop.hdfs.web.WebHdfsFileSystem;
024    import org.apache.hadoop.io.Text;
025    import org.apache.hadoop.lib.server.BaseService;
026    import org.apache.hadoop.lib.server.ServerException;
027    import org.apache.hadoop.lib.server.ServiceException;
028    import org.apache.hadoop.lib.service.DelegationTokenIdentifier;
029    import org.apache.hadoop.lib.service.DelegationTokenManager;
030    import org.apache.hadoop.lib.service.DelegationTokenManagerException;
031    import org.apache.hadoop.security.SecurityUtil;
032    import org.apache.hadoop.security.UserGroupInformation;
033    import org.apache.hadoop.security.token.Token;
034    import org.apache.hadoop.security.token.delegation.AbstractDelegationTokenSecretManager;
035    
036    import java.io.ByteArrayInputStream;
037    import java.io.DataInputStream;
038    import java.io.IOException;
039    
040    /**
041     * DelegationTokenManager service implementation.
042     */
043    @InterfaceAudience.Private
044    public class DelegationTokenManagerService extends BaseService
045      implements DelegationTokenManager {
046    
047      private static final String PREFIX = "delegation.token.manager";
048    
049      private static final String UPDATE_INTERVAL = "update.interval";
050    
051      private static final String MAX_LIFETIME = "max.lifetime";
052    
053      private static final String RENEW_INTERVAL = "renew.interval";
054    
055      private static final long HOUR = 60 * 60 * 1000;
056      private static final long DAY = 24 * HOUR;
057    
058      DelegationTokenSecretManager secretManager = null;
059    
060      private Text tokenKind;
061    
062      public DelegationTokenManagerService() {
063        super(PREFIX);
064      }
065    
066      /**
067       * Initializes the service.
068       *
069       * @throws ServiceException thrown if the service could not be initialized.
070       */
071      @Override
072      protected void init() throws ServiceException {
073    
074        long updateInterval = getServiceConfig().getLong(UPDATE_INTERVAL, DAY);
075        long maxLifetime = getServiceConfig().getLong(MAX_LIFETIME, 7 * DAY);
076        long renewInterval = getServiceConfig().getLong(RENEW_INTERVAL, DAY);
077        tokenKind = (HttpFSServerWebApp.get().isSslEnabled())
078                    ? SWebHdfsFileSystem.TOKEN_KIND : WebHdfsFileSystem.TOKEN_KIND;
079        secretManager = new DelegationTokenSecretManager(tokenKind, updateInterval,
080                                                         maxLifetime,
081                                                         renewInterval, HOUR);
082        try {
083          secretManager.startThreads();
084        } catch (IOException ex) {
085          throw new ServiceException(ServiceException.ERROR.S12,
086                                     DelegationTokenManager.class.getSimpleName(),
087                                     ex.toString(), ex);
088        }
089      }
090    
091      /**
092       * Destroys the service.
093       */
094      @Override
095      public void destroy() {
096        secretManager.stopThreads();
097        super.destroy();
098      }
099    
100      /**
101       * Returns the service interface.
102       *
103       * @return the service interface.
104       */
105      @Override
106      public Class getInterface() {
107        return DelegationTokenManager.class;
108      }
109    
110      /**
111       * Creates a delegation token.
112       *
113       * @param ugi UGI creating the token.
114       * @param renewer token renewer.
115       * @return new delegation token.
116       * @throws DelegationTokenManagerException thrown if the token could not be
117       * created.
118       */
119      @Override
120      public Token<DelegationTokenIdentifier> createToken(UserGroupInformation ugi,
121                                                          String renewer)
122        throws DelegationTokenManagerException {
123        renewer = (renewer == null) ? ugi.getShortUserName() : renewer;
124        String user = ugi.getUserName();
125        Text owner = new Text(user);
126        Text realUser = null;
127        if (ugi.getRealUser() != null) {
128          realUser = new Text(ugi.getRealUser().getUserName());
129        }
130        DelegationTokenIdentifier tokenIdentifier =
131          new DelegationTokenIdentifier(tokenKind, owner, new Text(renewer), realUser);
132        Token<DelegationTokenIdentifier> token =
133          new Token<DelegationTokenIdentifier>(tokenIdentifier, secretManager);
134        try {
135          SecurityUtil.setTokenService(token,
136                                       HttpFSServerWebApp.get().getAuthority());
137        } catch (ServerException ex) {
138          throw new DelegationTokenManagerException(
139            DelegationTokenManagerException.ERROR.DT04, ex.toString(), ex);
140        }
141        return token;
142      }
143    
144      /**
145       * Renews a delegation token.
146       *
147       * @param token delegation token to renew.
148       * @param renewer token renewer.
149       * @return epoc expiration time.
150       * @throws DelegationTokenManagerException thrown if the token could not be
151       * renewed.
152       */
153      @Override
154      public long renewToken(Token<DelegationTokenIdentifier> token, String renewer)
155        throws DelegationTokenManagerException {
156        try {
157          return secretManager.renewToken(token, renewer);
158        } catch (IOException ex) {
159          throw new DelegationTokenManagerException(
160            DelegationTokenManagerException.ERROR.DT02, ex.toString(), ex);
161        }
162      }
163    
164      /**
165       * Cancels a delegation token.
166       *
167       * @param token delegation token to cancel.
168       * @param canceler token canceler.
169       * @throws DelegationTokenManagerException thrown if the token could not be
170       * canceled.
171       */
172      @Override
173      public void cancelToken(Token<DelegationTokenIdentifier> token,
174                              String canceler)
175        throws DelegationTokenManagerException {
176        try {
177          secretManager.cancelToken(token, canceler);
178        } catch (IOException ex) {
179          throw new DelegationTokenManagerException(
180            DelegationTokenManagerException.ERROR.DT03, ex.toString(), ex);
181        }
182      }
183    
184      /**
185       * Verifies a delegation token.
186       *
187       * @param token delegation token to verify.
188       * @return the UGI for the token.
189       * @throws DelegationTokenManagerException thrown if the token could not be
190       * verified.
191       */
192      @Override
193      public UserGroupInformation verifyToken(Token<DelegationTokenIdentifier> token)
194        throws DelegationTokenManagerException {
195        ByteArrayInputStream buf = new ByteArrayInputStream(token.getIdentifier());
196        DataInputStream dis = new DataInputStream(buf);
197        DelegationTokenIdentifier id = new DelegationTokenIdentifier(tokenKind);
198        try {
199          id.readFields(dis);
200          dis.close();
201          secretManager.verifyToken(id, token.getPassword());
202        } catch (Exception ex) {
203          throw new DelegationTokenManagerException(
204            DelegationTokenManagerException.ERROR.DT01, ex.toString(), ex);
205        }
206        return id.getUser();
207      }
208    
209      private static class DelegationTokenSecretManager
210        extends AbstractDelegationTokenSecretManager<DelegationTokenIdentifier> {
211    
212        private Text tokenKind;
213    
214        /**
215         * Create a secret manager
216         *
217         * @param delegationKeyUpdateInterval the number of seconds for rolling new
218         * secret keys.
219         * @param delegationTokenMaxLifetime the maximum lifetime of the delegation
220         * tokens
221         * @param delegationTokenRenewInterval how often the tokens must be renewed
222         * @param delegationTokenRemoverScanInterval how often the tokens are
223         * scanned
224         * for expired tokens
225         */
226        public DelegationTokenSecretManager(Text tokenKind, long delegationKeyUpdateInterval,
227                                            long delegationTokenMaxLifetime,
228                                            long delegationTokenRenewInterval,
229                                            long delegationTokenRemoverScanInterval) {
230          super(delegationKeyUpdateInterval, delegationTokenMaxLifetime,
231                delegationTokenRenewInterval, delegationTokenRemoverScanInterval);
232          this.tokenKind = tokenKind;
233        }
234    
235        @Override
236        public DelegationTokenIdentifier createIdentifier() {
237          return new DelegationTokenIdentifier(tokenKind);
238        }
239    
240      }
241    
242    }