diff --git a/extensions/guacamole-auth-radius/pom.xml b/extensions/guacamole-auth-radius/pom.xml
index e9d5b5ca7..9aaa515f5 100644
--- a/extensions/guacamole-auth-radius/pom.xml
+++ b/extensions/guacamole-auth-radius/pom.xml
@@ -258,6 +258,18 @@
1.1.5
+
+ commons-lang
+ commons-lang
+ 2.6
+
+
+ commons-codec
+ commons-codec
+ 1.10
+
+
+
diff --git a/extensions/guacamole-auth-radius/src/main/java/org/apache/guacamole/auth/radius/AuthenticationProviderService.java b/extensions/guacamole-auth-radius/src/main/java/org/apache/guacamole/auth/radius/AuthenticationProviderService.java
index c6650bc8b..91bb0985b 100644
--- a/extensions/guacamole-auth-radius/src/main/java/org/apache/guacamole/auth/radius/AuthenticationProviderService.java
+++ b/extensions/guacamole-auth-radius/src/main/java/org/apache/guacamole/auth/radius/AuthenticationProviderService.java
@@ -21,9 +21,11 @@ package org.apache.guacamole.auth.radius;
import com.google.inject.Inject;
import com.google.inject.Provider;
-import java.util.Collections;
+import java.util.Arrays;
+import javax.servlet.http.HttpServletRequest;
import org.apache.guacamole.auth.radius.user.AuthenticatedUser;
import org.apache.guacamole.auth.radius.form.RadiusChallengeResponseField;
+import org.apache.guacamole.auth.radius.form.RadiusStateField;
import org.apache.guacamole.GuacamoleException;
import org.apache.guacamole.form.Field;
import org.apache.guacamole.net.auth.Credentials;
@@ -32,6 +34,7 @@ import org.apache.guacamole.net.auth.credentials.GuacamoleInvalidCredentialsExce
import org.apache.guacamole.net.auth.credentials.GuacamoleInsufficientCredentialsException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import net.jradius.dictionary.Attr_State;
import net.jradius.exception.UnknownAttributeException;
import net.jradius.packet.RadiusPacket;
import net.jradius.packet.AccessAccept;
@@ -90,69 +93,124 @@ public class AuthenticationProviderService {
public AuthenticatedUser authenticateUser(Credentials credentials)
throws GuacamoleException {
- // Initialize Radius Packet and try to authenticate
+ // Grab the HTTP Request from the credentials object
+ HttpServletRequest request = credentials.getRequest();
+
+ // Set up RadiusPacket object
RadiusPacket radPack;
- try {
- radPack = radiusService.authenticate(credentials.getUsername(),
+
+ // Ignore anonymous users
+ if (credentials.getUsername() == null || credentials.getUsername().isEmpty())
+ return null;
+
+ // Password is required
+ if (credentials.getPassword() == null || credentials.getPassword().isEmpty())
+ return null;
+
+ String challengeResponse = request.getParameter(RadiusChallengeResponseField.PARAMETER_NAME);
+ String radiusState = request.getParameter(RadiusStateField.PARAMETER_NAME);
+
+ // We do not have a challenge response, so we proceed normally
+ if (challengeResponse == null || challengeResponse.isEmpty()) {
+
+ // Initialize Radius Packet and try to authenticate
+ try {
+ radPack = radiusService.authenticate(credentials.getUsername(),
credentials.getPassword());
- }
- catch (GuacamoleException e) {
- logger.error("Cannot configure RADIUS server: {}", e.getMessage());
- logger.debug("Error configuring RADIUS server.", e);
- radPack = null;
- }
-
- // If configure fails, permission to login is denied
- if (radPack == null) {
- logger.debug("Nothing in the RADIUS packet.");
- throw new GuacamoleInvalidCredentialsException("Permission denied.", CredentialsInfo.USERNAME_PASSWORD);
- }
-
- // If we get back an AccessReject packet, login is denied.
- else if (radPack instanceof AccessReject) {
- logger.debug("Login has been rejected by RADIUS server.");
- throw new GuacamoleInvalidCredentialsException("Permission denied.", CredentialsInfo.USERNAME_PASSWORD);
- }
-
- /**
- * If we receive an AccessChallenge package, the server needs more information -
- * We create a new form/field with the challenge message.
- */
- else if (radPack instanceof AccessChallenge) {
- try {
- String replyMsg = radPack.getAttributeValue("Reply-Message").toString();
- String radState = radPack.getAttributeValue("State").toString();
- logger.debug("RADIUS sent challenge: {}", replyMsg);
- logger.debug("RADIUS sent state: {}", radState);
- Field radiusResponseField = new RadiusChallengeResponseField(credentials.getUsername(), replyMsg, radState);
- CredentialsInfo expectedCredentials = new CredentialsInfo(Collections.singletonList(radiusResponseField));
- throw new GuacamoleInsufficientCredentialsException("LOGIN.INFO_RADIUS_ADDL_REQUIRED", expectedCredentials);
}
- catch(UnknownAttributeException e) {
- logger.error("Error in talks with RADIUS server.");
- logger.debug("RADIUS challenged by didn't provide right attributes.");
- throw new GuacamoleInvalidCredentialsException("Authentication error.", CredentialsInfo.USERNAME_PASSWORD);
+ catch (GuacamoleException e) {
+ logger.error("Cannot configure RADIUS server: {}", e.getMessage());
+ logger.debug("Error configuring RADIUS server.", e);
+ radPack = null;
}
+
+ // If configure fails, permission to login is denied
+ if (radPack == null) {
+ logger.debug("Nothing in the RADIUS packet.");
+ throw new GuacamoleInvalidCredentialsException("Permission denied.", CredentialsInfo.USERNAME_PASSWORD);
+ }
+
+ // If we get back an AccessReject packet, login is denied.
+ else if (radPack instanceof AccessReject) {
+ logger.debug("Login has been rejected by RADIUS server.");
+ throw new GuacamoleInvalidCredentialsException("Permission denied.", CredentialsInfo.USERNAME_PASSWORD);
+ }
+
+ /**
+ * If we receive an AccessChallenge package, the server needs more information -
+ * We create a new form/field with the challenge message.
+ */
+ else if (radPack instanceof AccessChallenge) {
+ try {
+ RadiusAttribute stateAttr = radPack.findAttribute(Attr_State.TYPE);
+ // We should have a state attribute at this point, if not, we need to quit.
+ if (stateAttr == null) {
+ logger.error("Something went wrong, state attribute not present.");
+ logger.debug("State Attribute turned up null, which shouldn't happen in AccessChallenge.");
+ throw new GuacamoleInvalidCredentialsException("Authentication error.", CredentialsInfo.USERNAME_PASSWORD);
+ }
+ String replyMsg = radPack.getAttributeValue("Reply-Message").toString();
+ radiusState = new String(stateAttr.getValue().getBytes());
+ Field radiusResponseField = new RadiusChallengeResponseField(replyMsg);
+ Field radiusStateField = new RadiusStateField(radiusState);
+ CredentialsInfo expectedCredentials = new CredentialsInfo(Arrays.asList(radiusResponseField,radiusStateField));
+ throw new GuacamoleInsufficientCredentialsException("LOGIN.INFO_RADIUS_ADDL_REQUIRED", expectedCredentials);
+ }
+ catch (UnknownAttributeException e) {
+ logger.error("Error in talks with RADIUS server.");
+ logger.debug("RADIUS challenged by didn't provide right attributes.");
+ throw new GuacamoleInvalidCredentialsException("Authentication error.", CredentialsInfo.USERNAME_PASSWORD);
+ }
+ }
+
+ // If we receive AccessAccept, authentication has succeeded
+ else if (radPack instanceof AccessAccept) {
+ try {
+
+ // Return AuthenticatedUser if bind succeeds
+ AuthenticatedUser authenticatedUser = authenticatedUserProvider.get();
+ authenticatedUser.init(credentials);
+ return authenticatedUser;
+ }
+ finally {
+ radiusService.disconnect();
+ }
+ }
+
+ // Something unanticipated happened, so we panic
+ else
+ throw new GuacamoleInvalidCredentialsException("Unknown error trying to authenticate.", CredentialsInfo.USERNAME_PASSWORD);
}
- // If we receive AccessAccept, authentication has succeeded
- else if (radPack instanceof AccessAccept) {
+ // We did receive a challenge response, so we're going to send that back to the server
+ else {
+ // Initialize Radius Packet and try to authenticate
try {
-
- // Return AuthenticatedUser if bind succeeds
- AuthenticatedUser authenticatedUser = authenticatedUserProvider.get();
- authenticatedUser.init(credentials);
- return authenticatedUser;
+ radPack = radiusService.authenticate(credentials.getUsername(),
+ radiusState,
+ challengeResponse);
+ }
+ catch (GuacamoleException e) {
+ logger.error("Cannot configure RADIUS server: {}", e.getMessage());
+ logger.debug("Error configuring RADIUS server.", e);
+ radPack = null;
}
finally {
radiusService.disconnect();
}
+
+ if (radPack instanceof AccessAccept) {
+ AuthenticatedUser authenticatedUser = authenticatedUserProvider.get();
+ authenticatedUser.init(credentials);
+ return authenticatedUser;
+ }
+
+ else {
+ logger.warn("RADIUS Challenge/Response authentication failed.");
+ logger.debug("Did not receive a RADIUS AccessAccept packet back from server.");
+ throw new GuacamoleInvalidCredentialsException("Failed to authenticate to RADIUS.", CredentialsInfo.USERNAME_PASSWORD);
+ }
}
-
- // Something else we haven't thought of has happened, so we throw an error
- else
- throw new GuacamoleInvalidCredentialsException("Unknown error trying to authenticate.", CredentialsInfo.USERNAME_PASSWORD);
-
}
}
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 02ad9869c..51eef07a7 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
@@ -178,7 +178,6 @@ public class RadiusConnectionService {
radAttrs.add(new Attr_UserName(username));
radAttrs.add(new Attr_UserPassword(password));
AccessRequest radAcc = new AccessRequest(radiusClient, radAttrs);
- logger.debug("Sending authentication request to radius server for user {}.", username);
radAuth.setupRequest(radiusClient, radAcc);
radAuth.processRequest(radAcc);
return radiusClient.sendReceive(radAcc, confService.getRadiusRetries());
@@ -256,7 +255,6 @@ public class RadiusConnectionService {
radAttrs.add(new Attr_State(state));
radAttrs.add(new Attr_UserPassword(response));
AccessRequest radAcc = new AccessRequest(radiusClient, radAttrs);
- logger.debug("Sending authentication response to radius server for user {}.", username);
radAuth.setupRequest(radiusClient, radAcc);
radAuth.processRequest(radAcc);
return radiusClient.sendReceive(radAcc, confService.getRadiusRetries());
diff --git a/extensions/guacamole-auth-radius/src/main/java/org/apache/guacamole/auth/radius/form/RadiusChallengeResponseField.java b/extensions/guacamole-auth-radius/src/main/java/org/apache/guacamole/auth/radius/form/RadiusChallengeResponseField.java
index 111c07dee..f76c7bd7a 100644
--- a/extensions/guacamole-auth-radius/src/main/java/org/apache/guacamole/auth/radius/form/RadiusChallengeResponseField.java
+++ b/extensions/guacamole-auth-radius/src/main/java/org/apache/guacamole/auth/radius/form/RadiusChallengeResponseField.java
@@ -34,23 +34,13 @@ public class RadiusChallengeResponseField extends Field {
/**
* The field returned by the RADIUS challenge/response.
*/
- private static final String RADIUS_FIELD_NAME = "guac-radius-challenge-response";
+ public static final String PARAMETER_NAME = "guac-radius-challenge-response";
/**
* The type of field to initialize for the challenge/response.
*/
private static final String RADIUS_FIELD_TYPE = "GUAC_RADIUS_CHALLENGE_RESPONSE";
- /**
- * The username used for the RADIUS authentication attempt.
- */
- private final String username;
-
- /**
- * The state of the connection passed by the previous RADIUS attempt.
- */
- private final String radiusState;
-
/**
* The message the RADIUS server sent back in the challenge.
*/
@@ -59,24 +49,14 @@ public class RadiusChallengeResponseField extends Field {
/**
* Initialize the field with the reply message and the state.
*/
- public RadiusChallengeResponseField(String username, String replyMsg, String radiusState) {
- super(RADIUS_FIELD_NAME, RADIUS_FIELD_TYPE);
+ public RadiusChallengeResponseField(String replyMsg) {
+ super(PARAMETER_NAME, RADIUS_FIELD_TYPE);
logger.debug("Initializing the RADIUS challenge/response field: {}", replyMsg);
- this.username = username;
this.replyMsg = replyMsg;
- this.radiusState = radiusState;
}
- public String getUsername() {
- return username;
- }
-
- public String getRadiusState() {
- return radiusState;
- }
-
public String getReplyMsg() {
return replyMsg;
}
diff --git a/extensions/guacamole-auth-radius/src/main/java/org/apache/guacamole/auth/radius/form/RadiusStateField.java b/extensions/guacamole-auth-radius/src/main/java/org/apache/guacamole/auth/radius/form/RadiusStateField.java
new file mode 100644
index 000000000..97a0e447a
--- /dev/null
+++ b/extensions/guacamole-auth-radius/src/main/java/org/apache/guacamole/auth/radius/form/RadiusStateField.java
@@ -0,0 +1,64 @@
+/*
+ * 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.form;
+
+import org.apache.guacamole.form.Field;
+import org.codehaus.jackson.annotate.JsonProperty;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class RadiusStateField extends Field {
+
+ /**
+ * Logger for this class.
+ */
+ private final Logger logger = LoggerFactory.getLogger(RadiusStateField.class);
+
+ /**
+ * The parameter returned by the RADIUS state.
+ */
+ public static final String PARAMETER_NAME = "guac-radius-state";
+
+ /**
+ * The type of field to initialize for the state.
+ */
+ private static final String RADIUS_FIELD_TYPE = "GUAC_RADIUS_STATE";
+
+ /**
+ * The state of the connection passed by the previous RADIUS attempt.
+ */
+ private final String radiusState;
+
+ /**
+ * Initialize the field with the reply message and the state.
+ */
+ public RadiusStateField(String radiusState) {
+ super(PARAMETER_NAME, RADIUS_FIELD_TYPE);
+ logger.debug("Initializing the RADIUS state field: {}", radiusState);
+
+ this.radiusState = radiusState;
+
+ }
+
+ public String getRadiusState() {
+ return radiusState;
+ }
+
+}
diff --git a/extensions/guacamole-auth-radius/src/main/resources/config/radiusConfig.js b/extensions/guacamole-auth-radius/src/main/resources/config/radiusConfig.js
index bc14e63c3..cff025c57 100644
--- a/extensions/guacamole-auth-radius/src/main/resources/config/radiusConfig.js
+++ b/extensions/guacamole-auth-radius/src/main/resources/config/radiusConfig.js
@@ -27,8 +27,13 @@ angular.module('guacRadius').config(['formServiceProvider',
// Define field for the challenge from the RADIUS service
formServiceProvider.registerFieldType('GUAC_RADIUS_CHALLENGE_RESPONSE', {
module : 'guacRadius',
- controller : 'guacRadiusController',
- templateUrl : 'app/ext/radius/templates/radiusChallengeResponseField.html'
+ controller : 'radiusResponseController',
+ templateUrl : 'app/ext/radius/templates/radiusResponseField.html'
+ });
+ formServiceProvider.registerFieldType('GUAC_RADIUS_STATE', {
+ module : 'guacRadius',
+ controller : 'radiusStateController',
+ template : ''
});
}]);
diff --git a/extensions/guacamole-auth-radius/src/main/resources/controllers/guacRadiusController.js b/extensions/guacamole-auth-radius/src/main/resources/controllers/radiusResponseController.js
similarity index 70%
rename from extensions/guacamole-auth-radius/src/main/resources/controllers/guacRadiusController.js
rename to extensions/guacamole-auth-radius/src/main/resources/controllers/radiusResponseController.js
index beb544d88..16d7ce56a 100644
--- a/extensions/guacamole-auth-radius/src/main/resources/controllers/guacRadiusController.js
+++ b/extensions/guacamole-auth-radius/src/main/resources/controllers/radiusResponseController.js
@@ -22,22 +22,15 @@
* API to prompt the user for additional credentials, ultimately receiving a
* signed response from the Duo service.
*/
-angular.module('guacRadius').controller('guacRadiusController', ['$scope', '$element',
- function guacRadiusController($scope, $element) {
- console.log("In guacRadiusController() method.");
+angular.module('guacRadius').controller('radiusResponseController', ['$scope', '$element',
+ function radiusResponseController($scope, $element) {
+ console.log("In radiusResponseController() method.");
// Find the area to display the challenge message
var radiusChallenge = $element.find(document.querySelector('#radius-challenge-text'));
- // Find the hidden input to put the state in
- var radiusState = $element.find(document.querySelector('#radius-state'));
-
// Populate the reply message field
console.log("RADIUS Reply Message: " + $scope.field.replyMsg);
- radiusChellenge.html($scope.field.replyMsg);
-
- // Populate the input area for the connection state
- console.log("RADIUS State: " + scope.field.radiusState);
- radiusState.value = $scope.field.radiusState;
+ radiusChallenge.html($scope.field.replyMsg);
}]);
diff --git a/extensions/guacamole-auth-radius/src/main/resources/controllers/radiusStateController.js b/extensions/guacamole-auth-radius/src/main/resources/controllers/radiusStateController.js
new file mode 100644
index 000000000..8b51933f8
--- /dev/null
+++ b/extensions/guacamole-auth-radius/src/main/resources/controllers/radiusStateController.js
@@ -0,0 +1,33 @@
+/*
+ * 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.
+ */
+
+/**
+ * Controller for the "GUAC_RADIUS_CHALLENGE_RESPONSE" field which uses the DuoWeb
+ * API to prompt the user for additional credentials, ultimately receiving a
+ * signed response from the Duo service.
+ */
+angular.module('guacRadius').controller('radiusStateController', ['$scope', '$element',
+ function radiusStateController($scope, $element) {
+ console.log("In radiusStateController() method.");
+
+ // Populate the input area for the connection state
+ console.log("RADIUS State: " + $scope.field.radiusState);
+ $scope.model = $scope.field.radiusState;
+
+}]);
diff --git a/extensions/guacamole-auth-radius/src/main/resources/templates/radiusChallengeResponseField.html b/extensions/guacamole-auth-radius/src/main/resources/templates/radiusChallengeResponseField.html
deleted file mode 100644
index 6dedcda7b..000000000
--- a/extensions/guacamole-auth-radius/src/main/resources/templates/radiusChallengeResponseField.html
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
-
-
-
-
-
-
diff --git a/extensions/guacamole-auth-radius/src/main/resources/templates/radiusResponseField.html b/extensions/guacamole-auth-radius/src/main/resources/templates/radiusResponseField.html
new file mode 100644
index 000000000..a75f66dae
--- /dev/null
+++ b/extensions/guacamole-auth-radius/src/main/resources/templates/radiusResponseField.html
@@ -0,0 +1,5 @@
+
+