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,
013: * software distributed under the License is distributed on an
014: * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015: * KIND, either express or implied. See the License for the
016: * specific language governing permissions and limitations
017: * under the License.
018: */package org.apache.cxf.ws.security.wss4j;
019:
020: import java.security.cert.X509Certificate;
021: import java.util.List;
022: import java.util.Map;
023: import java.util.Vector;
024: import java.util.logging.Level;
025: import java.util.logging.Logger;
026:
027: import javax.security.auth.callback.CallbackHandler;
028: import javax.xml.soap.SOAPBody;
029: import javax.xml.soap.SOAPException;
030: import javax.xml.soap.SOAPMessage;
031: import javax.xml.stream.XMLStreamConstants;
032: import javax.xml.stream.XMLStreamException;
033: import javax.xml.stream.XMLStreamReader;
034: import javax.xml.transform.dom.DOMSource;
035:
036: import org.apache.cxf.binding.soap.SoapFault;
037: import org.apache.cxf.binding.soap.SoapMessage;
038: import org.apache.cxf.binding.soap.SoapVersion;
039: import org.apache.cxf.binding.soap.saaj.SAAJInInterceptor;
040: import org.apache.cxf.common.i18n.Message;
041: import org.apache.cxf.interceptor.Fault;
042: import org.apache.cxf.phase.Phase;
043: import org.apache.cxf.staxutils.StaxUtils;
044: import org.apache.ws.security.WSConstants;
045: import org.apache.ws.security.WSSecurityEngineResult;
046: import org.apache.ws.security.WSSecurityException;
047: import org.apache.ws.security.handler.RequestData;
048: import org.apache.ws.security.handler.WSHandlerConstants;
049: import org.apache.ws.security.handler.WSHandlerResult;
050: import org.apache.ws.security.message.token.Timestamp;
051: import org.apache.ws.security.util.WSSecurityUtil;
052:
053: /**
054: * Performs WS-Security inbound actions.
055: *
056: * @author <a href="mailto:tsztelak@gmail.com">Tomasz Sztelak</a>
057: */
058: public class WSS4JInInterceptor extends AbstractWSS4JInterceptor {
059:
060: public static final String TIMESTAMP_RESULT = "wss4j.timestamp.result";
061: public static final String SIGNATURE_RESULT = "wss4j.signature.result";
062:
063: private static final Logger LOG = Logger
064: .getLogger(WSS4JInInterceptor.class.getName());
065: private static final Logger TIME_LOG = Logger
066: .getLogger(WSS4JInInterceptor.class.getName() + "-Time");
067:
068: public WSS4JInInterceptor() {
069: super ();
070:
071: setPhase(Phase.PRE_PROTOCOL);
072: getAfter().add(SAAJInInterceptor.class.getName());
073: }
074:
075: public WSS4JInInterceptor(Map<String, Object> properties) {
076: this ();
077: setProperties(properties);
078: }
079:
080: @SuppressWarnings("unchecked")
081: public void handleMessage(SoapMessage msg) throws Fault {
082: boolean doDebug = LOG.isLoggable(Level.FINE);
083: boolean doTimeLog = TIME_LOG.isLoggable(Level.FINE);
084:
085: SoapVersion version = msg.getVersion();
086: if (doDebug) {
087: LOG.fine("WSS4JInSecurityHandler: enter invoke()");
088: }
089:
090: long t0 = 0;
091: long t1 = 0;
092: long t2 = 0;
093: long t3 = 0;
094:
095: if (doTimeLog) {
096: t0 = System.currentTimeMillis();
097: }
098:
099: RequestData reqData = new RequestData();
100: /*
101: * The overall try, just to have a finally at the end to perform some
102: * housekeeping.
103: */
104: try {
105: reqData.setMsgContext(msg);
106:
107: Vector actions = new Vector();
108: String action = getAction(msg, version);
109:
110: int doAction = WSSecurityUtil.decodeAction(action, actions);
111:
112: String actor = (String) getOption(WSHandlerConstants.ACTOR);
113:
114: SOAPMessage doc = msg.getContent(SOAPMessage.class);
115:
116: if (doc == null) {
117: throw new SoapFault(new Message("NO_SAAJ_DOC", LOG),
118: version.getReceiver());
119: }
120:
121: CallbackHandler cbHandler = getCallback(reqData, doAction);
122:
123: /*
124: * Get and check the Signature specific parameters first because
125: * they may be used for encryption too.
126: */
127: doReceiverAction(doAction, reqData);
128:
129: Vector wsResult = null;
130: if (doTimeLog) {
131: t1 = System.currentTimeMillis();
132: }
133:
134: try {
135: wsResult = secEngine.processSecurityHeader(doc
136: .getSOAPPart(), actor, cbHandler, reqData
137: .getSigCrypto(), reqData.getDecCrypto());
138: } catch (WSSecurityException ex) {
139: LOG.log(Level.WARNING, "", ex);
140: throw new SoapFault(
141: new Message("SECURITY_FAILED", LOG), ex,
142: version.getSender());
143: }
144:
145: if (doTimeLog) {
146: t2 = System.currentTimeMillis();
147: }
148:
149: if (wsResult == null) { // no security header found
150: if (doAction == WSConstants.NO_SECURITY) {
151: return;
152: } else {
153: LOG
154: .warning("Request does not contain required Security header");
155: throw new SoapFault(
156: new Message("NO_SECURITY", LOG), version
157: .getSender());
158: }
159: }
160:
161: if (reqData.getWssConfig().isEnableSignatureConfirmation()) {
162: checkSignatureConfirmation(reqData, wsResult);
163: }
164:
165: /*
166: * Now we can check the certificate used to sign the message. In the
167: * following implementation the certificate is only trusted if
168: * either it itself or the certificate of the issuer is installed in
169: * the keystore. Note: the method verifyTrust(X509Certificate)
170: * allows custom implementations with other validation algorithms
171: * for subclasses.
172: */
173:
174: // Extract the signature action result from the action vector
175: WSSecurityEngineResult actionResult = WSSecurityUtil
176: .fetchActionResult(wsResult, WSConstants.SIGN);
177:
178: if (actionResult != null) {
179: X509Certificate returnCert = actionResult
180: .getCertificate();
181:
182: if (returnCert != null
183: && !verifyTrust(returnCert, reqData)) {
184: LOG
185: .warning("The certificate used for the signature is not trusted");
186: throw new SoapFault(new Message("UNTRUSTED_CERT",
187: LOG), version.getSender());
188: }
189: msg.put(SIGNATURE_RESULT, actionResult);
190: }
191:
192: /*
193: * Perform further checks on the timestamp that was transmitted in
194: * the header. In the following implementation the timestamp is
195: * valid if it was created after (now-ttl), where ttl is set on
196: * server side, not by the client. Note: the method
197: * verifyTimestamp(Timestamp) allows custom implementations with
198: * other validation algorithms for subclasses.
199: */
200:
201: // Extract the timestamp action result from the action vector
202: actionResult = WSSecurityUtil.fetchActionResult(wsResult,
203: WSConstants.TS);
204:
205: if (actionResult != null) {
206: Timestamp timestamp = actionResult.getTimestamp();
207:
208: if (timestamp != null
209: && !verifyTimestamp(timestamp,
210: decodeTimeToLive(reqData))) {
211: LOG.warning("The timestamp could not be validated");
212: throw new SoapFault(new Message(
213: "INVALID_TIMESTAMP", LOG), version
214: .getSender());
215: }
216: msg.put(TIMESTAMP_RESULT, actionResult);
217: }
218:
219: /*
220: * now check the security actions: do they match, in right order?
221: */
222: if (!checkReceiverResults(wsResult, actions)) {
223: LOG
224: .warning("Security processing failed (actions mismatch)");
225: throw new SoapFault(
226: new Message("ACTION_MISMATCH", LOG), version
227: .getSender());
228:
229: }
230:
231: doResults(msg, actor, doc, wsResult);
232:
233: if (doTimeLog) {
234: t3 = System.currentTimeMillis();
235: TIME_LOG.fine("Receive request: total= " + (t3 - t0)
236: + " request preparation= " + (t1 - t0)
237: + " request processing= " + (t2 - t1)
238: + " header, cert verify, timestamp= "
239: + (t3 - t2) + "\n");
240: }
241:
242: if (doDebug) {
243: LOG.fine("WSS4JInHandler: exit invoke()");
244: }
245:
246: } catch (WSSecurityException e) {
247: LOG.log(Level.WARNING, "", e);
248: throw new SoapFault(new Message("WSSECURITY_EX", LOG), e,
249: version.getSender());
250: } catch (XMLStreamException e) {
251: throw new SoapFault(new Message("STAX_EX", LOG), e, version
252: .getSender());
253: } catch (SOAPException e) {
254: throw new SoapFault(new Message("SAAJ_EX", LOG), e, version
255: .getSender());
256: } finally {
257: reqData.clear();
258: reqData = null;
259: }
260: }
261:
262: @SuppressWarnings("unchecked")
263: private void doResults(SoapMessage msg, String actor,
264: SOAPMessage doc, Vector wsResult) throws SOAPException,
265: XMLStreamException {
266: /*
267: * All ok up to this point. Now construct and setup the security result
268: * structure. The service may fetch this and check it.
269: */
270: List<Object> results = (Vector<Object>) msg
271: .get(WSHandlerConstants.RECV_RESULTS);
272: if (results == null) {
273: results = new Vector<Object>();
274: msg.put(WSHandlerConstants.RECV_RESULTS, results);
275: }
276: WSHandlerResult rResult = new WSHandlerResult(actor, wsResult);
277: results.add(0, rResult);
278:
279: SOAPBody body = doc.getSOAPBody();
280:
281: XMLStreamReader reader = StaxUtils
282: .createXMLStreamReader(new DOMSource(body));
283: // advance just past body
284: int evt = reader.next();
285: int i = 0;
286: while (reader.hasNext()
287: && i < 1
288: && (evt != XMLStreamConstants.END_ELEMENT || evt != XMLStreamConstants.START_ELEMENT)) {
289: reader.next();
290: i++;
291: }
292: msg.setContent(XMLStreamReader.class, reader);
293: }
294:
295: private String getAction(SoapMessage msg, SoapVersion version) {
296: String action = (String) getOption(WSHandlerConstants.ACTION);
297: if (action == null) {
298: action = (String) msg.get(WSHandlerConstants.ACTION);
299: }
300: if (action == null) {
301: LOG.warning("No security action was defined!");
302: throw new SoapFault("No security action was defined!",
303: version.getReceiver());
304: }
305: return action;
306: }
307:
308: private CallbackHandler getCallback(RequestData reqData,
309: int doAction) throws WSSecurityException {
310: /*
311: * To check a UsernameToken or to decrypt an encrypted message we need a
312: * password.
313: */
314: CallbackHandler cbHandler = null;
315: if ((doAction & (WSConstants.ENCR | WSConstants.UT)) != 0) {
316: cbHandler = getPasswordCB(reqData);
317: }
318: return cbHandler;
319: }
320: }
|