GUACAMOLE-774: Merge dynamic loading of MD4 support (for RADIUS MSCHAPv1/2).

diff --git a/extensions/guacamole-auth-radius/src/main/java/org/apache/guacamole/auth/radius/RadiusAuthenticationProviderModule.java b/extensions/guacamole-auth-radius/src/main/java/org/apache/guacamole/auth/radius/RadiusAuthenticationProviderModule.java
index 37ecb79..d34b0b3 100644
--- a/extensions/guacamole-auth-radius/src/main/java/org/apache/guacamole/auth/radius/RadiusAuthenticationProviderModule.java
+++ b/extensions/guacamole-auth-radius/src/main/java/org/apache/guacamole/auth/radius/RadiusAuthenticationProviderModule.java
@@ -20,7 +20,12 @@
 package org.apache.guacamole.auth.radius;
 
 import com.google.inject.AbstractModule;
+import java.security.Provider;
+import java.security.Security;
 import org.apache.guacamole.GuacamoleException;
+import org.apache.guacamole.auth.radius.conf.ConfigurationService;
+import org.apache.guacamole.auth.radius.conf.RadiusAuthenticationProtocol;
+import org.apache.guacamole.auth.radius.conf.RadiusGuacamoleProperties;
 import org.apache.guacamole.environment.Environment;
 import org.apache.guacamole.environment.LocalEnvironment;
 import org.apache.guacamole.net.auth.AuthenticationProvider;
@@ -57,6 +62,22 @@
 
         // Get local environment
         this.environment = new LocalEnvironment();
+        
+        // Check for MD4 requirement
+        RadiusAuthenticationProtocol authProtocol = environment.getProperty(RadiusGuacamoleProperties.RADIUS_AUTH_PROTOCOL);
+        RadiusAuthenticationProtocol innerProtocol = environment.getProperty(RadiusGuacamoleProperties.RADIUS_EAP_TTLS_INNER_PROTOCOL);
+        if (authProtocol == RadiusAuthenticationProtocol.MSCHAPv1 
+                    || authProtocol == RadiusAuthenticationProtocol.MSCHAPv2
+                    || innerProtocol == RadiusAuthenticationProtocol.MSCHAPv1 
+                    || innerProtocol == RadiusAuthenticationProtocol.MSCHAPv2) {
+            
+            Security.addProvider(new Provider("MD4", 0.00, "MD4 for MSCHAPv1/2 Support") {
+                {
+                    this.put("MessageDigest.MD4", org.bouncycastle.jce.provider.JDKMessageDigest.MD4.class.getName());
+                }
+            });
+            
+        }
 
         // Store associated auth provider
         this.authProvider = authProvider;
diff --git a/extensions/guacamole-auth-radius/src/main/java/org/apache/guacamole/auth/radius/RadiusConnectionService.java b/extensions/guacamole-auth-radius/src/main/java/org/apache/guacamole/auth/radius/RadiusConnectionService.java
index ec82a63..c8a21d6 100644
--- a/extensions/guacamole-auth-radius/src/main/java/org/apache/guacamole/auth/radius/RadiusConnectionService.java
+++ b/extensions/guacamole-auth-radius/src/main/java/org/apache/guacamole/auth/radius/RadiusConnectionService.java
@@ -27,6 +27,8 @@
 import java.security.NoSuchAlgorithmException;
 import org.apache.guacamole.GuacamoleException;
 import org.apache.guacamole.GuacamoleServerException;
+import org.apache.guacamole.auth.radius.conf.ConfigurationService;
+import org.apache.guacamole.auth.radius.conf.RadiusAuthenticationProtocol;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import net.jradius.client.RadiusClient;
@@ -62,8 +64,7 @@
      */
     @Inject
     private ConfigurationService confService;
-
-
+    
     /**
      * Creates a new instance of RadiusClient, configured with parameters
      * from guacamole.properties.
@@ -115,8 +116,8 @@
      *     not configured when the client is set up for a tunneled
      *     RADIUS connection.
      */
-    private RadiusAuthenticator setupRadiusAuthenticator(RadiusClient radiusClient)
-            throws GuacamoleException {
+    private RadiusAuthenticator setupRadiusAuthenticator(
+            RadiusClient radiusClient) throws GuacamoleException {
 
         // If we don't have a radiusClient object, yet, don't go any further.
         if (radiusClient == null) {
@@ -125,7 +126,9 @@
             return null;
         }
 
-        RadiusAuthenticator radAuth = radiusClient.getAuthProtocol(confService.getRadiusAuthProtocol());
+        RadiusAuthenticator radAuth = radiusClient.getAuthProtocol(
+                confService.getRadiusAuthProtocol().toString());
+        
         if (radAuth == null)
             throw new GuacamoleException("Could not get a valid RadiusAuthenticator for specified protocol: " + confService.getRadiusAuthProtocol());
 
@@ -157,11 +160,13 @@
 
         // If we're using EAP-TTLS, we need to define tunneled protocol
         if (radAuth instanceof EAPTTLSAuthenticator) {
-            String innerProtocol = confService.getRadiusEAPTTLSInnerProtocol();
+            RadiusAuthenticationProtocol innerProtocol =
+                    confService.getRadiusEAPTTLSInnerProtocol();
+            
             if (innerProtocol == null)
-                throw new GuacamoleException("Trying to use EAP-TTLS, but no inner protocol specified.");
+                throw new GuacamoleException("Missing or invalid inner protocol for EAP-TTLS.");
 
-            ((EAPTTLSAuthenticator)radAuth).setInnerProtocol(innerProtocol);
+            ((EAPTTLSAuthenticator)radAuth).setInnerProtocol(innerProtocol.toString());
         }
 
         return radAuth;
@@ -236,14 +241,21 @@
 
             radAuth.setupRequest(radiusClient, radAcc);
             radAuth.processRequest(radAcc);
-            RadiusResponse reply = radiusClient.sendReceive(radAcc, confService.getRadiusMaxRetries());
+            RadiusResponse reply = radiusClient.sendReceive(radAcc,
+                    confService.getRadiusMaxRetries());
 
             // We receive a Challenge not asking for user input, so silently process the challenge
-            while((reply instanceof AccessChallenge) && (reply.findAttribute(Attr_ReplyMessage.TYPE) == null)) {
+            while((reply instanceof AccessChallenge) 
+                    && (reply.findAttribute(Attr_ReplyMessage.TYPE) == null)) {
+                
                 radAuth.processChallenge(radAcc, reply);
-                reply = radiusClient.sendReceive(radAcc, confService.getRadiusMaxRetries());
+                reply = radiusClient.sendReceive(radAcc,
+                        confService.getRadiusMaxRetries());
+                
             }
+            
             return reply;
+            
         }
         catch (RadiusException e) {
             logger.error("Unable to complete authentication.", e.getMessage());
@@ -282,8 +294,8 @@
      * @throws GuacamoleException
      *     If an error is encountered trying to talk to the RADIUS server.
      */
-    public RadiusPacket sendChallengeResponse(String username, String response, byte[] state)
-            throws GuacamoleException {
+    public RadiusPacket sendChallengeResponse(String username, String response,
+            byte[] state) throws GuacamoleException {
 
         if (username == null || username.isEmpty()) {
             logger.error("Challenge/response to RADIUS requires a username.");
diff --git a/extensions/guacamole-auth-radius/src/main/java/org/apache/guacamole/auth/radius/ConfigurationService.java b/extensions/guacamole-auth-radius/src/main/java/org/apache/guacamole/auth/radius/conf/ConfigurationService.java
similarity index 92%
rename from extensions/guacamole-auth-radius/src/main/java/org/apache/guacamole/auth/radius/ConfigurationService.java
rename to extensions/guacamole-auth-radius/src/main/java/org/apache/guacamole/auth/radius/conf/ConfigurationService.java
index 381ea13..2809f7c 100644
--- a/extensions/guacamole-auth-radius/src/main/java/org/apache/guacamole/auth/radius/ConfigurationService.java
+++ b/extensions/guacamole-auth-radius/src/main/java/org/apache/guacamole/auth/radius/conf/ConfigurationService.java
@@ -17,11 +17,12 @@
  * under the License.
  */
 
-package org.apache.guacamole.auth.radius;
+package org.apache.guacamole.auth.radius.conf;
 
 import com.google.inject.Inject;
 import java.io.File;
 import org.apache.guacamole.GuacamoleException;
+import org.apache.guacamole.GuacamoleServerException;
 import org.apache.guacamole.environment.Environment;
 
 /**
@@ -123,8 +124,9 @@
      * @throws GuacamoleException
      *     If guacamole.properties cannot be parsed.
      */
-    public String getRadiusAuthProtocol() throws GuacamoleException {
-        return environment.getProperty(
+    public RadiusAuthenticationProtocol getRadiusAuthProtocol()
+            throws GuacamoleException {
+        return environment.getRequiredProperty(
             RadiusGuacamoleProperties.RADIUS_AUTH_PROTOCOL
         );
     }
@@ -309,12 +311,21 @@
      *     an EAP-TTLS RADIUS connection. 
      *     
      * @throws GuacamoleException
-     *     If guacamole.properties cannot be parsed.
+     *     If guacamole.properties cannot be parsed, or if EAP-TTLS is specified
+     *     as the inner protocol.
      */
-    public String getRadiusEAPTTLSInnerProtocol() throws GuacamoleException {
-        return environment.getProperty(
+    public RadiusAuthenticationProtocol getRadiusEAPTTLSInnerProtocol()
+            throws GuacamoleException {
+        
+        RadiusAuthenticationProtocol authProtocol = environment.getProperty(
             RadiusGuacamoleProperties.RADIUS_EAP_TTLS_INNER_PROTOCOL
         );
+        
+        if (authProtocol == RadiusAuthenticationProtocol.EAP_TTLS)
+            throw new GuacamoleServerException("Invalid inner protocol specified for EAP-TTLS.");
+        
+        return authProtocol;
+        
     }
 
 }
diff --git a/extensions/guacamole-auth-radius/src/main/java/org/apache/guacamole/auth/radius/conf/RadiusAuthenticationProtocol.java b/extensions/guacamole-auth-radius/src/main/java/org/apache/guacamole/auth/radius/conf/RadiusAuthenticationProtocol.java
new file mode 100644
index 0000000..e64a695
--- /dev/null
+++ b/extensions/guacamole-auth-radius/src/main/java/org/apache/guacamole/auth/radius/conf/RadiusAuthenticationProtocol.java
@@ -0,0 +1,118 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.guacamole.auth.radius.conf;
+
+/**
+ * This enum represents supported RADIUS authentication protocols for
+ * the guacamole-auth-radius extension.
+ */
+public enum RadiusAuthenticationProtocol {
+    
+    /**
+     * Password Authentication Protocol (PAP)
+     */
+    PAP("pap"),
+    
+    /**
+     * Challenge-Handshake Authentication Protocol (CHAP)
+     */
+    CHAP("chap"),
+    
+    /**
+     * Microsoft implementation of CHAP, Version 1 (MS-CHAPv1)
+     */
+    MSCHAPv1("mschapv1"),
+    
+    /**
+     * Microsoft implementation of CHAP, Version 2 (MS-CHAPv2)
+     */
+    MSCHAPv2("mschapv2"),
+    
+    /**
+     * Extensible Authentication Protocol (EAP) with MD5 Hashing (EAP-MD5)
+     */
+    EAP_MD5("eap-md5"),
+
+    /**
+     * Extensible Authentication Protocol (EAP) with TLS encryption (EAP-TLS).
+     */
+    EAP_TLS("eap-tls"),
+
+    /**
+     * Extensible Authentication Protocol (EAP) with Tunneled TLS (EAP-TTLS).
+     */
+    EAP_TTLS("eap-ttls");
+
+    /**
+     * This variable stores the string value of the protocol, and is also
+     * used within the extension to pass to JRadius for configuring the
+     * library to talk to the RADIUS server.
+     */
+    private final String strValue;
+    
+    /**
+     * Create a new RadiusAuthenticationProtocol object having the
+     * given string value.
+     * 
+     * @param strValue
+     *     The value of the protocol to store as a string, which will be used
+     *     in specifying the protocol within the guacamole.properties file, and
+     *     will also be used by the JRadius library for its configuration.
+     */
+    RadiusAuthenticationProtocol(String strValue) {
+        this.strValue = strValue;
+    }
+    
+    /**
+    * {@inheritDoc}
+    * <p>
+    * This function returns the stored string values of the selected RADIUS
+    * protocol, which is used both in Guacamole configuration and also to pass
+    * on to the JRadius library for its configuration.
+    * 
+    * @return
+    *     The string value stored for the selected RADIUS protocol.
+    */
+    @Override
+    public String toString() {
+        return strValue;
+    }
+    
+    /**
+     * For a given String value, return the enum value that matches that string,
+     * or null if no matchi is found.
+     * 
+     * @param value
+     *     The string value to search for in the list of enums.
+     * 
+     * @return
+     *     The RadiusAuthenticationProtocol value that is identified by the
+     *     provided String value.
+     */
+    public static RadiusAuthenticationProtocol getEnum(String value) {
+    
+        for (RadiusAuthenticationProtocol v : values())
+            if(v.toString().equals(value))
+                return v;
+        
+        return null;
+    }
+    
+}
diff --git a/extensions/guacamole-auth-radius/src/main/java/org/apache/guacamole/auth/radius/conf/RadiusAuthenticationProtocolProperty.java b/extensions/guacamole-auth-radius/src/main/java/org/apache/guacamole/auth/radius/conf/RadiusAuthenticationProtocolProperty.java
new file mode 100644
index 0000000..c92c0a3
--- /dev/null
+++ b/extensions/guacamole-auth-radius/src/main/java/org/apache/guacamole/auth/radius/conf/RadiusAuthenticationProtocolProperty.java
@@ -0,0 +1,54 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.guacamole.auth.radius.conf;
+
+import org.apache.guacamole.GuacamoleException;
+import org.apache.guacamole.GuacamoleServerException;
+import org.apache.guacamole.properties.GuacamoleProperty;
+
+/**
+ * A GuacamoleProperty whose value is a RadiusAuthenticationProtocol.
+ */
+public abstract class RadiusAuthenticationProtocolProperty
+        implements GuacamoleProperty<RadiusAuthenticationProtocol> {
+    
+    @Override
+    public RadiusAuthenticationProtocol parseValue(String value)
+            throws GuacamoleException {
+        
+        // Nothing provided, nothing returned
+        if (value == null)
+            return null;
+        
+        // Attempt to parse the string value
+        RadiusAuthenticationProtocol authProtocol = 
+                RadiusAuthenticationProtocol.getEnum(value);
+        
+        // Throw an exception if nothing matched.
+        if (authProtocol == null)
+            throw new GuacamoleServerException(
+                    "Invalid or unsupported RADIUS authentication protocol.");
+        
+        // Return the answer
+        return authProtocol;
+        
+    }
+    
+}
diff --git a/extensions/guacamole-auth-radius/src/main/java/org/apache/guacamole/auth/radius/RadiusGuacamoleProperties.java b/extensions/guacamole-auth-radius/src/main/java/org/apache/guacamole/auth/radius/conf/RadiusGuacamoleProperties.java
similarity index 93%
rename from extensions/guacamole-auth-radius/src/main/java/org/apache/guacamole/auth/radius/RadiusGuacamoleProperties.java
rename to extensions/guacamole-auth-radius/src/main/java/org/apache/guacamole/auth/radius/conf/RadiusGuacamoleProperties.java
index aaa445e..af6839b 100644
--- a/extensions/guacamole-auth-radius/src/main/java/org/apache/guacamole/auth/radius/RadiusGuacamoleProperties.java
+++ b/extensions/guacamole-auth-radius/src/main/java/org/apache/guacamole/auth/radius/conf/RadiusGuacamoleProperties.java
@@ -17,7 +17,7 @@
  * under the License.
  */
 
-package org.apache.guacamole.auth.radius;
+package org.apache.guacamole.auth.radius.conf;
 
 import org.apache.guacamole.properties.BooleanGuacamoleProperty;
 import org.apache.guacamole.properties.FileGuacamoleProperty;
@@ -81,7 +81,8 @@
     /**
      * The authentication protocol of the RADIUS server to connect to when authenticating users.
      */
-    public static final StringGuacamoleProperty RADIUS_AUTH_PROTOCOL = new StringGuacamoleProperty() {
+    public static final RadiusAuthenticationProtocolProperty RADIUS_AUTH_PROTOCOL =
+            new RadiusAuthenticationProtocolProperty() {
 
         @Override
         public String getName() { return "radius-auth-protocol"; }
@@ -181,7 +182,8 @@
     /**
      * The tunneled protocol to use inside a RADIUS EAP-TTLS connection.
      */
-    public static final StringGuacamoleProperty RADIUS_EAP_TTLS_INNER_PROTOCOL = new StringGuacamoleProperty() {
+    public static final RadiusAuthenticationProtocolProperty RADIUS_EAP_TTLS_INNER_PROTOCOL =
+            new RadiusAuthenticationProtocolProperty() {
 
         @Override
         public String getName() { return "radius-eap-ttls-inner-protocol"; }