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  package org.apache.wss4j.stax.impl.securityToken;
20  
21  import java.io.IOException;
22  import java.security.Key;
23  import java.security.Principal;
24  import java.security.PrivilegedActionException;
25  import java.util.Set;
26  
27  import javax.security.auth.Subject;
28  import javax.security.auth.callback.Callback;
29  import javax.security.auth.callback.CallbackHandler;
30  import javax.security.auth.callback.UnsupportedCallbackException;
31  import javax.security.auth.login.LoginContext;
32  import javax.security.auth.login.LoginException;
33  
34  import org.apache.wss4j.common.ext.WSSecurityException;
35  import org.apache.wss4j.common.ext.WSSecurityException.ErrorCode;
36  import org.apache.wss4j.common.kerberos.KerberosContextAndServiceNameCallback;
37  import org.apache.wss4j.common.kerberos.KerberosServiceContext;
38  import org.apache.wss4j.common.kerberos.KerberosServiceExceptionAction;
39  import org.apache.wss4j.common.kerberos.KerberosTokenDecoder;
40  import org.apache.wss4j.common.kerberos.KerberosTokenDecoderException;
41  import org.apache.wss4j.common.util.KeyUtils;
42  import org.apache.wss4j.stax.ext.WSInboundSecurityContext;
43  import org.apache.wss4j.stax.securityToken.KerberosServiceSecurityToken;
44  import org.apache.wss4j.stax.securityToken.WSSecurityTokenConstants;
45  import org.apache.xml.security.exceptions.XMLSecurityException;
46  import org.apache.xml.security.stax.ext.XMLSecurityConstants;
47  import org.apache.xml.security.stax.impl.securityToken.AbstractInboundSecurityToken;
48  
49  public class KerberosServiceSecurityTokenImpl extends AbstractInboundSecurityToken implements KerberosServiceSecurityToken {
50  
51      private CallbackHandler callbackHandler;
52      private byte[] binaryContent;
53      private String kerberosTokenValueType;
54  
55      private KerberosTokenDecoder kerberosTokenDecoder;
56      private Subject subject;
57      private Principal principal;
58      private byte[] sessionKey;
59  
60      public KerberosServiceSecurityTokenImpl(WSInboundSecurityContext wsInboundSecurityContext, CallbackHandler callbackHandler,
61                                              byte[] binaryContent, String kerberosTokenValueType, String id,
62                                              WSSecurityTokenConstants.KeyIdentifier keyIdentifier) {
63          super(wsInboundSecurityContext, id, keyIdentifier, true);
64          this.callbackHandler = callbackHandler;
65          this.binaryContent = binaryContent;
66          this.kerberosTokenValueType = kerberosTokenValueType;
67      }
68  
69      @Override
70      public boolean isAsymmetric() throws XMLSecurityException {
71          return false;
72      }
73  
74      @Override
75      public WSSecurityTokenConstants.TokenType getTokenType() {
76          return WSSecurityTokenConstants.KERBEROS_TOKEN;
77      }
78  
79      protected byte[] getTGTSessionKey() throws WSSecurityException {
80          if (sessionKey != null) {
81              return sessionKey;
82          }
83          try {
84              KerberosContextAndServiceNameCallback contextAndServiceNameCallback = new KerberosContextAndServiceNameCallback();
85              callbackHandler.handle(new Callback[]{contextAndServiceNameCallback});
86  
87              if (contextAndServiceNameCallback.getContextName() == null) {
88                  throw new WSSecurityException(WSSecurityException.ErrorCode.FAILURE, "kerberosCallbackContextNameNotSupplied");
89              }
90              if (contextAndServiceNameCallback.getServiceName() == null) {
91                  throw new WSSecurityException(WSSecurityException.ErrorCode.FAILURE, "kerberosCallbackServiceNameNotSupplied");
92              }
93  
94              LoginContext loginContext = new LoginContext(contextAndServiceNameCallback.getContextName(), callbackHandler);
95              loginContext.login();
96  
97              // Get the service name to use - fall back on the principal
98              this.subject = loginContext.getSubject();
99  
100             String service = contextAndServiceNameCallback.getServiceName();
101             if (service == null) {
102                 Set<Principal> principals = subject.getPrincipals();
103                 if (principals.isEmpty()) {
104                     throw new WSSecurityException(
105                         WSSecurityException.ErrorCode.FAILURE,
106                         "kerberosLoginError",
107                         new Object[] {"No Client principals found after login"}
108                     );
109                 }
110                 service = principals.iterator().next().getName();
111             }
112 
113             KerberosServiceExceptionAction action =
114                 new KerberosServiceExceptionAction(binaryContent,
115                                                    service,
116                                                    contextAndServiceNameCallback.isUsernameServiceNameForm(),
117                                                    false);
118             KerberosServiceContext krbServiceCtx = null;
119             try {
120                 krbServiceCtx = Subject.doAs(subject, action);
121             } catch (PrivilegedActionException e) {
122                 Throwable cause = e.getCause();
123                 if (cause instanceof WSSecurityException) {
124                     throw (WSSecurityException) cause;
125                 } else {
126                     throw new WSSecurityException(
127                         ErrorCode.FAILURE, new Exception(cause), "kerberosTicketValidationError"
128                     );
129                 }
130             }
131 
132             this.principal = krbServiceCtx.getPrincipal();
133 
134             Key key = krbServiceCtx.getSessionKey();
135             if (key != null) {
136                 sessionKey = key.getEncoded();
137             } else if (kerberosTokenDecoder != null) {
138                 kerberosTokenDecoder.clear();
139                 kerberosTokenDecoder.setToken(binaryContent);
140                 kerberosTokenDecoder.setSubject(subject);
141                 sessionKey = kerberosTokenDecoder.getSessionKey();
142             }
143 
144             return sessionKey;
145         } catch (LoginException | UnsupportedCallbackException | IOException e) {
146             throw new WSSecurityException(WSSecurityException.ErrorCode.FAILURE, e);
147         } catch (KerberosTokenDecoderException e) {
148             throw new WSSecurityException(WSSecurityException.ErrorCode.INVALID_SECURITY_TOKEN, e);
149         }
150     }
151 
152     @Override
153     protected Key getKey(String algorithmURI, XMLSecurityConstants.AlgorithmUsage algorithmUsage,
154                          String correlationID) throws XMLSecurityException {
155 
156         Key key = getSecretKey().get(algorithmURI);
157         if (key != null) {
158             return key;
159         }
160 
161         byte[] sk = getTGTSessionKey();
162 
163         key = KeyUtils.prepareSecretKey(algorithmURI, sk);
164         setSecretKey(algorithmURI, key);
165         return key;
166     }
167 
168     public byte[] getBinaryContent() {
169         return binaryContent;
170     }
171 
172     public String getKerberosTokenValueType() {
173         return kerberosTokenValueType;
174     }
175 
176     @Override
177     public Subject getSubject() throws WSSecurityException {
178         return subject;
179     }
180 
181     @Override
182     public Principal getPrincipal() throws WSSecurityException {
183         return principal;
184     }
185 
186     /**
187      * Get the KerberosTokenDecoder instance used to extract a session key from the received Kerberos
188      * token.
189      * @return the KerberosTokenDecoder instance used to extract a session key
190      */
191     public KerberosTokenDecoder getKerberosTokenDecoder() {
192         return kerberosTokenDecoder;
193     }
194 
195     /**
196      * Set the KerberosTokenDecoder instance used to extract a session key from the received Kerberos
197      * token.
198      * @param kerberosTokenDecoder the KerberosTokenDecoder instance used to extract a session key
199      */
200     public void setKerberosTokenDecoder(KerberosTokenDecoder kerberosTokenDecoder) {
201         this.kerberosTokenDecoder = kerberosTokenDecoder;
202     }
203 }