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.message.token;
21  
22  import java.security.Principal;
23  import java.util.Set;
24  
25  import javax.crypto.SecretKey;
26  import javax.security.auth.Subject;
27  import javax.security.auth.callback.CallbackHandler;
28  import javax.security.auth.kerberos.KerberosTicket;
29  import javax.security.auth.login.LoginContext;
30  import javax.security.auth.login.LoginException;
31  
32  import org.apache.ws.security.WSConstants;
33  import org.apache.ws.security.WSSecurityException;
34  import org.w3c.dom.Document;
35  import org.w3c.dom.Element;
36  
37  /**
38   * Kerberos Security Token.
39   */
40  public class KerberosSecurity extends BinarySecurity {
41      
42      private static org.apache.commons.logging.Log log =
43          org.apache.commons.logging.LogFactory.getLog(KerberosSecurity.class);
44      private SecretKey secretKey;
45      
46      /**
47       * This constructor creates a new Kerberos token object and initializes
48       * it from the data contained in the element.
49       *
50       * @param elem the element containing the Kerberos token data
51       * @throws WSSecurityException
52       */
53      public KerberosSecurity(Element elem) throws WSSecurityException {
54          this(elem, true);
55      }
56      
57      /**
58       * This constructor creates a new Kerberos token object and initializes
59       * it from the data contained in the element.
60       *
61       * @param elem the element containing the Kerberos token data
62       * @param bspCompliant Whether the token is processed according to the BSP spec
63       * @throws WSSecurityException
64       */
65      public KerberosSecurity(Element elem, boolean bspCompliant) throws WSSecurityException {
66          super(elem, bspCompliant);
67          String valueType = getValueType();
68          if (bspCompliant && !WSConstants.WSS_GSS_KRB_V5_AP_REQ.equals(valueType)) {
69              throw new WSSecurityException(
70                  WSSecurityException.INVALID_SECURITY_TOKEN, 
71                  "invalidValueType", 
72                  new Object[]{valueType}
73              );
74          }
75      }
76  
77      /**
78       * This constructor creates a new Kerberos element.
79       *
80       * @param doc
81       */
82      public KerberosSecurity(Document doc) {
83          super(doc);
84      }
85      
86      /**
87       * Return true if this token is a Kerberos V5 AP REQ token
88       */
89      public boolean isV5ApReq() {
90          String type = getValueType();
91          if (WSConstants.WSS_KRB_V5_AP_REQ.equals(type)
92              || WSConstants.WSS_KRB_V5_AP_REQ1510.equals(type)
93              || WSConstants.WSS_KRB_V5_AP_REQ4120.equals(type)) {
94              return true;
95          }
96          return false;
97      }
98      
99      /**
100      * Return true if this token is a Kerberos GSS V5 AP REQ token
101      */
102     public boolean isGssV5ApReq() {
103         String type = getValueType();
104         if (WSConstants.WSS_GSS_KRB_V5_AP_REQ.equals(type)
105             || WSConstants.WSS_GSS_KRB_V5_AP_REQ1510.equals(type)
106             || WSConstants.WSS_GSS_KRB_V5_AP_REQ4120.equals(type)) {
107             return true;
108         }
109         return false;
110     }
111 
112     /**
113      * Retrieve a service ticket from a KDC using the Kerberos JAAS module, and set it in this
114      * BinarySecurityToken.
115      * @param jaasLoginModuleName the JAAS Login Module name to use
116      * @param callbackHandler a CallbackHandler instance to retrieve a password (optional)
117      * @param serviceName the desired Kerberized service
118      * @throws WSSecurityException
119      */
120     public void retrieveServiceTicket(
121         String jaasLoginModuleName, 
122         CallbackHandler callbackHandler,
123         String serviceName
124     ) throws WSSecurityException {
125         // Get a TGT from the KDC using JAAS
126         LoginContext loginContext = null;
127         try {
128             if (callbackHandler == null) {
129                 loginContext = new LoginContext(jaasLoginModuleName);
130             } else {
131                 loginContext = new LoginContext(jaasLoginModuleName, callbackHandler);
132             }
133             loginContext.login();
134         } catch (LoginException ex) {
135             if (log.isDebugEnabled()) {
136                 log.debug(ex.getMessage(), ex);
137             }
138             throw new WSSecurityException(
139                 WSSecurityException.FAILURE,
140                 "kerberosLoginError", 
141                 new Object[] {ex.getMessage()},
142                 ex
143             );
144         }
145         if (log.isDebugEnabled()) {
146             log.debug("Successfully authenticated to the TGT");
147         }
148         
149         Subject clientSubject = loginContext.getSubject();
150         Set<Principal> clientPrincipals = clientSubject.getPrincipals();
151         if (clientPrincipals.isEmpty()) {
152             throw new WSSecurityException(
153                 WSSecurityException.FAILURE, 
154                 "kerberosLoginError", 
155                 new Object[] {"No Client principals found after login"}
156             );
157         }
158         // Store the TGT
159         KerberosTicket tgt = getKerberosTicket(clientSubject, null);
160         
161         // Get the service ticket
162         KerberosClientAction action = 
163             new KerberosClientAction(clientPrincipals.iterator().next(), serviceName);
164         byte[] ticket = (byte[])Subject.doAs(clientSubject, action);
165         if (ticket == null) {
166             throw new WSSecurityException(
167                 WSSecurityException.FAILURE, "kerberosServiceTicketError"
168             );
169         }
170         if (log.isDebugEnabled()) {
171             log.debug("Successfully retrieved a service ticket");
172         }
173         
174         // Get the Service Ticket (private credential)
175         KerberosTicket serviceTicket = getKerberosTicket(clientSubject, tgt);
176         if (serviceTicket != null) {
177             secretKey = serviceTicket.getSessionKey();
178         }
179         
180         setToken(ticket);
181         
182         if ("".equals(getValueType())) {
183             setValueType(WSConstants.WSS_GSS_KRB_V5_AP_REQ);
184         }
185     }
186     
187     /**
188      * Get a KerberosTicket from the clientSubject parameter, that is not equal to the supplied KerberosTicket
189      * parameter (can be null)
190      */
191     private KerberosTicket getKerberosTicket(Subject clientSubject, KerberosTicket previousTicket) {
192         Set<KerberosTicket> privateCredentials = clientSubject.getPrivateCredentials(KerberosTicket.class);
193         if (privateCredentials == null || privateCredentials.isEmpty()) {
194             if (log.isDebugEnabled()) {
195                 log.debug("Kerberos client subject private credentials are null");
196             }
197             return null;
198         }
199         
200         for (KerberosTicket privateCredential : privateCredentials) {
201             if (!privateCredential.equals(previousTicket)) {
202                 return privateCredential;
203             }
204         }
205         return null;
206     }
207     
208     /**
209      * Get the SecretKey associated with the service principal
210      * @return the SecretKey associated with the service principal
211      */
212     public SecretKey getSecretKey() {
213         return secretKey;
214     }
215     
216     /**
217      * Return true if the valueType represents a Kerberos Token
218      * @param valueType the valueType of the token
219      * @return true if the valueType represents a Kerberos Token
220      */
221     public static boolean isKerberosToken(String valueType) {
222         if (WSConstants.WSS_KRB_V5_AP_REQ.equals(valueType)
223             || WSConstants.WSS_GSS_KRB_V5_AP_REQ.equals(valueType)
224             || WSConstants.WSS_KRB_V5_AP_REQ1510.equals(valueType)
225             || WSConstants.WSS_GSS_KRB_V5_AP_REQ1510.equals(valueType)
226             || WSConstants.WSS_KRB_V5_AP_REQ4120.equals(valueType)
227             || WSConstants.WSS_GSS_KRB_V5_AP_REQ4120.equals(valueType)) {
228             return true;
229         }
230         return false;
231     }
232     
233 }