You are viewing a plain text version of this content. The canonical link for it is here.
Posted to wss4j-dev@ws.apache.org by fa...@apache.org on 2008/04/17 18:58:02 UTC

svn commit: r649186 - in /webservices/wss4j/trunk: src/org/apache/ws/security/ src/org/apache/ws/security/handler/ src/org/apache/ws/security/processor/ test/wssec/

Author: fadushin
Date: Thu Apr 17 09:58:01 2008
New Revision: 649186

URL: http://svn.apache.org/viewvc?rev=649186&view=rev
Log:
WSS-54 UsernameToken processor improvements

 * Applied Colm's revised patch, which has been reviewed.
 * Witholds information about authentication failures from
   caller, but logs said information, instead
 * Delegates custom password types to callback handler


Modified:
    webservices/wss4j/trunk/src/org/apache/ws/security/WSSConfig.java
    webservices/wss4j/trunk/src/org/apache/ws/security/handler/WSHandler.java
    webservices/wss4j/trunk/src/org/apache/ws/security/handler/WSHandlerConstants.java
    webservices/wss4j/trunk/src/org/apache/ws/security/processor/UsernameTokenProcessor.java
    webservices/wss4j/trunk/test/wssec/TestWSSecurityNew5.java

Modified: webservices/wss4j/trunk/src/org/apache/ws/security/WSSConfig.java
URL: http://svn.apache.org/viewvc/webservices/wss4j/trunk/src/org/apache/ws/security/WSSConfig.java?rev=649186&r1=649185&r2=649186&view=diff
==============================================================================
--- webservices/wss4j/trunk/src/org/apache/ws/security/WSSConfig.java (original)
+++ webservices/wss4j/trunk/src/org/apache/ws/security/WSSConfig.java Thu Apr 17 09:58:01 2008
@@ -176,6 +176,15 @@
      * expired.
      */
     protected boolean timeStampStrict = true;
+    
+    /**
+     * This variable controls whether types other than PasswordDigest or PasswordText
+     * are allowed when processing UsernameTokens. 
+     * 
+     * By default this is set to false so that the user doesn't have to explicitly
+     * reject custom token types in the callback handler.
+     */
+    protected boolean handleCustomPasswordTypes = false;
 
     protected HashMap jceProvider = new HashMap(10);
 
@@ -320,6 +329,21 @@
     public void setEnableSignatureConfirmation(
             boolean enableSignatureConfirmation) {
         this.enableSignatureConfirmation = enableSignatureConfirmation;
+    }
+    
+    /**
+     * @param handleCustomTypes 
+     * whether to handle custom UsernameToken password types or not
+     */
+    public void setHandleCustomPasswordTypes(boolean handleCustomTypes) {
+        this.handleCustomPasswordTypes = handleCustomTypes;
+    }
+    
+    /**
+     * @return whether custom UsernameToken password types are allowed or not
+     */
+    public boolean getHandleCustomPasswordTypes() {
+        return handleCustomPasswordTypes;
     }
 
     /**

Modified: webservices/wss4j/trunk/src/org/apache/ws/security/handler/WSHandler.java
URL: http://svn.apache.org/viewvc/webservices/wss4j/trunk/src/org/apache/ws/security/handler/WSHandler.java?rev=649186&r1=649185&r2=649186&view=diff
==============================================================================
--- webservices/wss4j/trunk/src/org/apache/ws/security/handler/WSHandler.java (original)
+++ webservices/wss4j/trunk/src/org/apache/ws/security/handler/WSHandler.java Thu Apr 17 09:58:01 2008
@@ -249,6 +249,7 @@
         wssConfig
 	    .setEnableSignatureConfirmation(decodeEnableSignatureConfirmation(reqData));
         wssConfig.setTimeStampStrict(decodeTimestampStrict(reqData));
+        wssConfig.setHandleCustomPasswordTypes(decodeCustomPasswordTypes(reqData));
         reqData.setWssConfig(wssConfig);
 
         if ((doAction & WSConstants.SIGN) == WSConstants.SIGN) {
@@ -616,6 +617,22 @@
 
 	throw new WSSecurityException(
 		   "WSHandler: illegal precisionInMilliSeconds parameter");
+    }
+    
+    protected boolean decodeCustomPasswordTypes(RequestData reqData) 
+        throws WSSecurityException {
+        String value = getString(
+                WSHandlerConstants.HANDLE_CUSTOM_PASSWORD_TYPES,
+                reqData.getMsgContext()
+        );
+
+        if (value == null) {return false;}
+
+        if ("0".equals(value) || "false".equals(value)) {return false;} 
+        if ("1".equals(value) || "true".equals(value)) {return true;}
+    
+        throw new WSSecurityException(
+               "WSHandler: illegal handleCustomPasswordTypes parameter");
     }
 
     protected boolean decodeTimestampStrict(RequestData reqData) 

Modified: webservices/wss4j/trunk/src/org/apache/ws/security/handler/WSHandlerConstants.java
URL: http://svn.apache.org/viewvc/webservices/wss4j/trunk/src/org/apache/ws/security/handler/WSHandlerConstants.java?rev=649186&r1=649185&r2=649186&view=diff
==============================================================================
--- webservices/wss4j/trunk/src/org/apache/ws/security/handler/WSHandlerConstants.java (original)
+++ webservices/wss4j/trunk/src/org/apache/ws/security/handler/WSHandlerConstants.java Thu Apr 17 09:58:01 2008
@@ -454,7 +454,7 @@
 
     /**
      * Specific parameter for UsernameToken action to define the encoding
-     * of the passowrd.
+     * of the password.
      * <p/>
      * The parameter can be set to either {@link WSConstants#PW_DIGEST}
      * or to {@link WSConstants#PW_TEXT}.
@@ -469,6 +469,15 @@
      * The default setting is PW_DIGEST.
      */
     public static final String PASSWORD_TYPE = "passwordType";
+    
+    /**
+     * This variable controls whether types other than PasswordDigest or PasswordText
+     * are allowed when processing UsernameTokens. 
+     * 
+     * By default this is set to false so that the user doesn't have to explicitly
+     * reject custom token types in the callback handler.
+     */
+    public static final String HANDLE_CUSTOM_PASSWORD_TYPES = "handleCustomPasswordTypes";
 
     /**
      * Parameter to generate additional elements in <code>UsernameToken</code>.

Modified: webservices/wss4j/trunk/src/org/apache/ws/security/processor/UsernameTokenProcessor.java
URL: http://svn.apache.org/viewvc/webservices/wss4j/trunk/src/org/apache/ws/security/processor/UsernameTokenProcessor.java?rev=649186&r1=649185&r2=649186&view=diff
==============================================================================
--- webservices/wss4j/trunk/src/org/apache/ws/security/processor/UsernameTokenProcessor.java (original)
+++ webservices/wss4j/trunk/src/org/apache/ws/security/processor/UsernameTokenProcessor.java Thu Apr 17 09:58:01 2008
@@ -42,40 +42,41 @@
 
     private String utId;
     private UsernameToken ut;
+    private boolean handleCustomPasswordTypes;
     
     public void handleToken(Element elem, Crypto crypto, Crypto decCrypto, CallbackHandler cb, WSDocInfo wsDocInfo, Vector returnResults, WSSConfig wsc) throws WSSecurityException {
         if (log.isDebugEnabled()) {
             log.debug("Found UsernameToken list element");
         }
+        handleCustomPasswordTypes = wsc.getHandleCustomPasswordTypes();
+        
         Principal lastPrincipalFound = handleUsernameToken((Element) elem, cb);
         returnResults.add(0, new WSSecurityEngineResult(WSConstants.UT,
                 lastPrincipalFound, null, null, null));
         utId = elem.getAttributeNS(WSConstants.WSU_NS, "Id");
-
     }
 
     /**
      * Check the UsernameToken element. Depending on the password type
      * contained in the element the processing differs. If the password type
-     * is password digest (a hashed password) then process the password
-     * commpletely here. Use the callback class to get a stored password
-     * perform hash algorithm and compare the result with the transmitted
-     * password.
+     * is digested, then retrieve a password from the callback handler and
+     * authenticate the UsernameToken here.
      * <p/>
-     * If the password is of type password text or any other yet unknown
-     * password type the delegate the password validation to the callback
-     * class. To do so the security engine hands over all necessary data to
-     * the callback class via the WSPasswordCallback object. To distinguish
-     * from digested usernam token the usage parameter of WSPasswordCallback
-     * is set to <code>USERNAME_TOKEN_UNKNOWN</code>
+     * If the password is in plaintext or any other yet unknown password type
+     * then delegate the password validation to the callback class. Note that for unknown
+     * password types an exception is thrown if WSSConfig.getHandleCustomPasswordTypes()
+     * is set to false (as it is by default). The security engine hands over all necessary
+     * data to the callback class via the WSPasswordCallback object. The usage parameter of
+     * WSPasswordCallback is set to <code>USERNAME_TOKEN_UNKNOWN</code>.
      *
      * @param token the DOM element that contains the UsernameToken
-     * @param cb    the refernce to the callback object
+     * @param cb    the reference to the callback object
      * @return WSUsernameTokenPrincipal that contain data that an application
      *         may use to further validate the password/user combination.
      * @throws WSSecurityException
      */
-    public WSUsernameTokenPrincipal handleUsernameToken(Element token, CallbackHandler cb) throws WSSecurityException {
+    public WSUsernameTokenPrincipal handleUsernameToken(Element token, CallbackHandler cb) 
+        throws WSSecurityException {
         ut = new UsernameToken(token);
         String user = ut.getName();
         String password = ut.getPassword();
@@ -90,10 +91,14 @@
         Callback[] callbacks = new Callback[1];
         String origPassword = null;
         
+        //
+        // If the UsernameToken is hashed, then retrieve the password from the callback handler 
+        // and compare directly. If the UsernameToken is in plaintext or of some unknown type,
+        // then delegate authentication to the callback handler
+        //
         if (ut.isHashed()) {
             if (cb == null) {
-                throw new WSSecurityException(WSSecurityException.FAILURE,
-                        "noCallback");
+                throw new WSSecurityException(WSSecurityException.FAILURE, "noCallback");
             }
 
             WSPasswordCallback pwCb = new WSPasswordCallback(user, WSPasswordCallback.USERNAME_TOKEN);
@@ -101,13 +106,15 @@
             try {
                 cb.handle(callbacks);
             } catch (IOException e) {
-                throw new WSSecurityException(WSSecurityException.FAILURE,
-                        "noPassword",
-                        new Object[]{user}, e);
+                if (log.isDebugEnabled()) {
+                    log.debug(e);
+                }
+                throw new WSSecurityException(WSSecurityException.FAILED_AUTHENTICATION);
             } catch (UnsupportedCallbackException e) {
-                throw new WSSecurityException(WSSecurityException.FAILURE,
-                        "noPassword",
-                        new Object[]{user}, e);
+                if (log.isDebugEnabled()) {
+                    log.debug(e);
+                }
+                throw new WSSecurityException(WSSecurityException.FAILED_AUTHENTICATION);
             }
             origPassword = pwCb.getPassword();
             if (log.isDebugEnabled()) {
@@ -122,18 +129,30 @@
                 throw new WSSecurityException(WSSecurityException.FAILED_AUTHENTICATION);
             }
             ut.setRawPassword(origPassword);
-        } else if (cb != null) {
+        } else {
+            if (cb == null) {
+                throw new WSSecurityException(WSSecurityException.FAILURE, "noCallback");
+            } else if (!WSConstants.PASSWORD_TEXT.equals(pwType) && !handleCustomPasswordTypes) {
+                if (log.isDebugEnabled()) {
+                    log.debug("Authentication failed as handleCustomUsernameTokenTypes is false");
+                }
+                throw new WSSecurityException(WSSecurityException.FAILED_AUTHENTICATION);
+            }
             WSPasswordCallback pwCb = new WSPasswordCallback(user, password,
                     pwType, WSPasswordCallback.USERNAME_TOKEN_UNKNOWN);
             callbacks[0] = pwCb;
             try {
                 cb.handle(callbacks);
             } catch (IOException e) {
-                throw new WSSecurityException(WSSecurityException.FAILURE,
-                        "noPassword", new Object[]{user});
+                if (log.isDebugEnabled()) {
+                    log.debug(e);
+                }
+                throw new WSSecurityException(WSSecurityException.FAILED_AUTHENTICATION);
             } catch (UnsupportedCallbackException e) {
-                throw new WSSecurityException(WSSecurityException.FAILURE,
-                        "noPassword", new Object[]{user});
+                if (log.isDebugEnabled()) {
+                    log.debug(e);
+                }
+                throw new WSSecurityException(WSSecurityException.FAILED_AUTHENTICATION);
             }
             ut.setRawPassword(password);
         }

Modified: webservices/wss4j/trunk/test/wssec/TestWSSecurityNew5.java
URL: http://svn.apache.org/viewvc/webservices/wss4j/trunk/test/wssec/TestWSSecurityNew5.java?rev=649186&r1=649185&r2=649186&view=diff
==============================================================================
--- webservices/wss4j/trunk/test/wssec/TestWSSecurityNew5.java (original)
+++ webservices/wss4j/trunk/test/wssec/TestWSSecurityNew5.java Thu Apr 17 09:58:01 2008
@@ -32,6 +32,7 @@
 import org.apache.ws.security.WSPasswordCallback;
 import org.apache.ws.security.WSSecurityEngine;
 import org.apache.ws.security.WSConstants;
+import org.apache.ws.security.WSSConfig;
 import org.apache.ws.security.message.WSSecUsernameToken;
 import org.apache.ws.security.message.WSSecHeader;
 import org.w3c.dom.Document;
@@ -144,6 +145,41 @@
         log.info("After adding UsernameToken PW Digest....");
         verify(signedDoc);
     }
+    
+    /**
+     * Test that adds a UserNameToken with a bad password Digest to a WS-Security envelope
+     * <p/>
+     */
+    public void testUsernameTokenBadDigest() throws Exception {
+        WSSecUsernameToken builder = new WSSecUsernameToken();
+        builder.setUserInfo("wernerd", "verySecre");
+        log.info("Before adding UsernameToken PW Digest....");
+        Document doc = unsignedEnvelope.getAsDocument();
+        WSSecHeader secHeader = new WSSecHeader();
+        secHeader.insertSecurityHeader(doc);
+        Document signedDoc = builder.build(doc, secHeader);
+
+        /*
+         * convert the resulting document into a message first. The toAxisMessage()
+         * method performs the necessary c14n call to properly set up the signed
+         * document and convert it into a SOAP message. After that we extract it
+         * as a document again for further processing.
+         */
+
+        Message signedMsg = SOAPUtil.toAxisMessage(signedDoc);
+        if (log.isDebugEnabled()) {
+            log.debug("Message with UserNameToken PW Digest:");
+            XMLUtils.PrettyElementToWriter(signedMsg.getSOAPEnvelope().getAsDOM(), new PrintWriter(System.out));
+        }
+        signedDoc = signedMsg.getSOAPEnvelope().getAsDocument();
+        log.info("After adding UsernameToken PW Digest....");
+        try {
+            verify(signedDoc);
+        } catch (WSSecurityException ex) {
+            assertTrue(ex.getErrorCode() == WSSecurityException.FAILED_AUTHENTICATION);
+            // expected
+        }
+    }
 
     /**
      * Test that adds a UserNameToken with password text to a WS-Security envelope
@@ -169,6 +205,105 @@
     }
     
     /**
+     * Test that adds a UserNameToken with (bad) password text to a WS-Security envelope
+     * <p/>
+     */
+    public void testUsernameTokenBadText() throws Exception {
+        WSSecUsernameToken builder = new WSSecUsernameToken();
+        builder.setPasswordType(WSConstants.PASSWORD_TEXT);
+        builder.setUserInfo("wernerd", "verySecre");
+        log.info("Before adding UsernameToken PW Text....");
+        Document doc = unsignedEnvelope.getAsDocument();
+        WSSecHeader secHeader = new WSSecHeader();
+        secHeader.insertSecurityHeader(doc);
+        Document signedDoc = builder.build(doc, secHeader);
+        Message signedMsg = SOAPUtil.toAxisMessage(signedDoc);
+        if (log.isDebugEnabled()) {
+            log.debug("Message with UserNameToken PW Text:");
+            XMLUtils.PrettyElementToWriter(signedMsg.getSOAPEnvelope().getAsDOM(), new PrintWriter(System.out));
+        }
+        signedDoc = signedMsg.getSOAPEnvelope().getAsDocument();
+        log.info("After adding UsernameToken PW Text....");
+        
+        try {
+            verify(signedDoc);
+        } catch (WSSecurityException ex) {
+            assertTrue(ex.getErrorCode() == WSSecurityException.FAILED_AUTHENTICATION);
+            // expected
+        }
+    }
+    
+    /**
+     * Test with a null token type. This will fail as the default is to reject custom
+     * token types.
+     * <p/>
+     */
+    public void testUsernameTokenCustomFail() throws Exception {
+        WSSecUsernameToken builder = new WSSecUsernameToken();
+        builder.setPasswordType(null);
+        builder.setUserInfo("wernerd", null);
+        
+        Document doc = unsignedEnvelope.getAsDocument();
+        WSSecHeader secHeader = new WSSecHeader();
+        secHeader.insertSecurityHeader(doc);
+        Document signedDoc = builder.build(doc, secHeader);
+        Message signedMsg = SOAPUtil.toAxisMessage(signedDoc);
+        
+        if (log.isDebugEnabled()) {
+            log.debug("Message with UserNameToken PW Text:");
+            XMLUtils.PrettyElementToWriter(signedMsg.getSOAPEnvelope().getAsDOM(), new PrintWriter(System.out));
+        }
+        
+        signedDoc = signedMsg.getSOAPEnvelope().getAsDocument();
+        try {
+            verify(signedDoc);
+            throw new Exception("Custom token types are not permitted");
+        } catch (WSSecurityException ex) {
+            assertTrue(ex.getErrorCode() == WSSecurityException.FAILED_AUTHENTICATION);
+            // expected
+        }
+    }
+    
+    /**
+     * Test with a null token type. This will pass as the WSSConfig is configured to 
+     * handle custom token types.
+     * <p/>
+     */
+    public void testUsernameTokenCustomPass() throws Exception {
+        WSSecUsernameToken builder = new WSSecUsernameToken();
+        builder.setPasswordType(null);
+        builder.setUserInfo("wernerd", null);
+        
+        Document doc = unsignedEnvelope.getAsDocument();
+        WSSecHeader secHeader = new WSSecHeader();
+        secHeader.insertSecurityHeader(doc);
+        Document signedDoc = builder.build(doc, secHeader);
+        Message signedMsg = SOAPUtil.toAxisMessage(signedDoc);
+        
+        if (log.isDebugEnabled()) {
+            log.debug("Message with UserNameToken PW Text:");
+            XMLUtils.PrettyElementToWriter(signedMsg.getSOAPEnvelope().getAsDOM(), new PrintWriter(System.out));
+        }
+        
+        signedDoc = signedMsg.getSOAPEnvelope().getAsDocument();
+        
+        //
+        // Configure so that custom token types are accepted
+        //
+        WSSConfig cfg = WSSConfig.getNewInstance();
+        cfg.setHandleCustomPasswordTypes(true);
+        secEngine.setWssConfig(cfg);
+        verify(signedDoc);
+        
+        //
+        // Go back to default for other tests
+        //
+        cfg.setHandleCustomPasswordTypes(false);
+        secEngine.setWssConfig(cfg);
+    }
+    
+    
+    /**
      * A test for WSS-66 - the nonce string is null
      * http://issues.apache.org/jira/browse/WSS-66
      * "Possible security hole when PasswordDigest is used by client."
@@ -266,17 +401,19 @@
     }
 
     public void handle(Callback[] callbacks)
-            throws IOException, UnsupportedCallbackException {
+        throws IOException, UnsupportedCallbackException {
         for (int i = 0; i < callbacks.length; i++) {
             if (callbacks[i] instanceof WSPasswordCallback) {
                 WSPasswordCallback pc = (WSPasswordCallback) callbacks[i];
-                /*
-                 * here call a function/method to lookup the password for
-                 * the given identifier (e.g. a user name or keystore alias)
-                 * e.g.: pc.setPassword(passStore.getPassword(pc.getIdentfifier))
-                 * for Testing we supply a fixed name here.
-                 */
-                pc.setPassword("verySecret");
+                if (pc.getUsage() == WSPasswordCallback.USERNAME_TOKEN
+                    && "wernerd".equals(pc.getIdentifer())) {
+                    pc.setPassword("verySecret");
+                } else if (
+                    pc.getUsage() == WSPasswordCallback.USERNAME_TOKEN_UNKNOWN
+                    && "wernerd".equals(pc.getIdentifer())
+                    && "verySecret".equals(pc.getPassword())) {
+                    return;
+                }
             } else {
                 throw new UnsupportedCallbackException(callbacks[i], "Unrecognized Callback");
             }



---------------------------------------------------------------------
To unsubscribe, e-mail: wss4j-dev-unsubscribe@ws.apache.org
For additional commands, e-mail: wss4j-dev-help@ws.apache.org