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,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied. See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  
20  package org.apache.ws.security.validate;
21  
22  import java.security.Principal;
23  import java.util.Set;
24  
25  import javax.security.auth.Subject;
26  import javax.security.auth.callback.CallbackHandler;
27  import javax.security.auth.login.LoginContext;
28  import javax.security.auth.login.LoginException;
29  
30  import org.apache.ws.security.WSSecurityException;
31  import org.apache.ws.security.handler.RequestData;
32  import org.apache.ws.security.message.token.BinarySecurity;
33  import org.apache.ws.security.message.token.KerberosSecurity;
34  import org.apache.ws.security.message.token.KerberosServiceAction;
35  
36  /**
37   */
38  public class KerberosTokenValidator implements Validator {
39      
40      private static org.apache.commons.logging.Log log =
41          org.apache.commons.logging.LogFactory.getLog(KerberosTokenValidator.class);
42      
43      private String serviceName;
44      private CallbackHandler callbackHandler;
45      private String contextName;
46      private KerberosTokenDecoder kerberosTokenDecoder;
47      
48      /**
49       * Get the JAAS Login context name to use.
50       * @return the JAAS Login context name to use
51       */
52      public String getContextName() {
53          return contextName;
54      }
55  
56      /**
57       * Set the JAAS Login context name to use.
58       * @param contextName the JAAS Login context name to use
59       */
60      public void setContextName(String contextName) {
61          this.contextName = contextName;
62      }
63      
64      /**
65       * @deprecated
66       * Get the JAAS Login module name to use.
67       * @return the JAAS Login module name to use
68       */
69      public String getJaasLoginModuleName() {
70          return contextName;
71      }
72  
73      /**
74       * @deprecated
75       * Set the JAAS Login module name to use.
76       * @param jaasLoginModuleName the JAAS Login module name to use
77       */
78      public void setJaasLoginModuleName(String jaasLoginModuleName) {
79          this.contextName = jaasLoginModuleName;
80      }
81  
82      /**
83       * Get the CallbackHandler to use with the LoginContext
84       * @return the CallbackHandler to use with the LoginContext
85       */
86      public CallbackHandler getCallbackHandler() {
87          return callbackHandler;
88      }
89  
90      /**
91       * Set the CallbackHandler to use with the LoginContext. It can be null.
92       * @param callbackHandler the CallbackHandler to use with the LoginContext
93       */
94      public void setCallbackHandler(CallbackHandler callbackHandler) {
95          this.callbackHandler = callbackHandler;
96      }
97  
98      /**
99       * The name of the service to use when contacting the KDC. This value can be null, in which
100      * case it defaults to the current principal name.
101      * @param serviceName the name of the service to use when contacting the KDC
102      */
103     public void setServiceName(String serviceName) {
104         this.serviceName = serviceName;
105     }
106     
107     /**
108      * Get the name of the service to use when contacting the KDC. This value can be null, in which
109      * case it defaults to the current principal name.
110      * @return the name of the service to use when contacting the KDC
111      */
112     public String getServiceName() {
113         return serviceName;
114     }
115     
116     /**
117      * Get the KerberosTokenDecoder instance used to extract a session key from the received Kerberos
118      * token.
119      * @return the KerberosTokenDecoder instance used to extract a session key
120      */
121     public KerberosTokenDecoder getKerberosTokenDecoder() {
122         return kerberosTokenDecoder;
123     }
124 
125     /**
126      * Set the KerberosTokenDecoder instance used to extract a session key from the received Kerberos
127      * token.
128      * @param kerberosTokenDecoder the KerberosTokenDecoder instance used to extract a session key
129      */
130     public void setKerberosTokenDecoder(KerberosTokenDecoder kerberosTokenDecoder) {
131         this.kerberosTokenDecoder = kerberosTokenDecoder;
132     }
133     
134     /**
135      * Validate the credential argument. It must contain a non-null BinarySecurityToken. 
136      * 
137      * @param credential the Credential to be validated
138      * @param data the RequestData associated with the request
139      * @throws WSSecurityException on a failed validation
140      */
141     public Credential validate(Credential credential, RequestData data) throws WSSecurityException {
142         if (credential == null || credential.getBinarySecurityToken() == null) {
143             throw new WSSecurityException(WSSecurityException.FAILURE, "noCredential");
144         }
145         
146         BinarySecurity binarySecurity = credential.getBinarySecurityToken();
147         if (!(binarySecurity instanceof KerberosSecurity)) {
148             return credential;
149         }
150         
151         if (log.isDebugEnabled()) {
152             try {
153                 String jaasAuth = System.getProperty("java.security.auth.login.config");
154                 String krbConf = System.getProperty("java.security.krb5.conf");
155                 log.debug("KerberosTokenValidator - Using JAAS auth login file: " + jaasAuth);
156                 log.debug("KerberosTokenValidator - Using KRB conf file: " + krbConf);
157             } catch (SecurityException ex) {
158                 log.debug(ex.getMessage(), ex);
159             }
160         }
161         
162         // Get a TGT from the KDC using JAAS
163         LoginContext loginContext = null;
164         try {
165             if (callbackHandler == null) {
166                 loginContext = new LoginContext(getContextName());
167             } else {
168                 loginContext = new LoginContext(getContextName(), callbackHandler);
169             }
170             loginContext.login();
171         } catch (LoginException ex) {
172             if (log.isDebugEnabled()) {
173                 log.debug(ex.getMessage(), ex);
174             }
175             throw new WSSecurityException(
176                 WSSecurityException.FAILURE,
177                 "kerberosLoginError", 
178                 new Object[] {ex.getMessage()},
179                 ex
180             );
181         }
182         if (log.isDebugEnabled()) {
183             log.debug("Successfully authenticated to the TGT");
184         }
185         
186         byte[] token = binarySecurity.getToken();
187         
188         // Get the service name to use - fall back on the principal
189         Subject subject = loginContext.getSubject();
190         String service = serviceName;
191         if (service == null) {
192             Set<Principal> principals = subject.getPrincipals();
193             if (principals.isEmpty()) {
194                 throw new WSSecurityException(
195                     WSSecurityException.FAILURE, 
196                     "kerberosLoginError", 
197                     new Object[] {"No Client principals found after login"}
198                 );
199             }
200             service = principals.iterator().next().getName();
201         }
202         
203         // Validate the ticket
204         KerberosServiceAction action = new KerberosServiceAction(token, service);
205         Principal principal = (Principal)Subject.doAs(subject, action);
206         if (principal == null) {
207             throw new WSSecurityException(
208                 WSSecurityException.FAILURE, "kerberosTicketValidationError"
209             );
210         }
211         credential.setPrincipal(principal);
212         credential.setSubject(subject);
213         
214         // Try to extract the session key from the token if a KerberosTokenDecoder implementation is
215         // available
216         if (kerberosTokenDecoder != null) {
217             kerberosTokenDecoder.clear();
218             kerberosTokenDecoder.setToken(token);
219             kerberosTokenDecoder.setSubject(subject);
220             byte[] sessionKey = kerberosTokenDecoder.getSessionKey();
221             credential.setSecretKey(sessionKey);
222         }
223         
224         if (log.isDebugEnabled()) {
225             log.debug("Successfully validated a ticket");
226         }
227         
228         return credential;
229     }
230     
231 }