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 37ecb79fd..d34b0b344 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 @@ public class RadiusAuthenticationProviderModule extends AbstractModule { // 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 ec82a63ee..c8a21d6a1 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.net.UnknownHostException; 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 @@ public class RadiusConnectionService { */ @Inject private ConfigurationService confService; - - + /** * Creates a new instance of RadiusClient, configured with parameters * from guacamole.properties. @@ -115,8 +116,8 @@ public class RadiusConnectionService { * 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 @@ public class RadiusConnectionService { 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 @@ public class RadiusConnectionService { // 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 @@ public class RadiusConnectionService { 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 @@ public class RadiusConnectionService { * @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 381ea13a5..2809f7c76 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 @@ public class ConfigurationService { * @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 @@ public class ConfigurationService { * 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 000000000..e64a69584 --- /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} + *
+ * 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 000000000..c92c0a3ed
--- /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