Skip to content
Snippets Groups Projects
Commit a89fcd1d authored by Jaime Pérez Crespo's avatar Jaime Pérez Crespo
Browse files

Merge pull request #340 from thijskh/radius

Several updates to the Radius module
parents 83a5a2b4 16d0bb79
No related branches found
No related tags found
No related merge requests found
......@@ -39,9 +39,9 @@ authentication source which uses the `radius:Radius` module to
/*
* The number of times we should retry connections to the RADIUS server.
* Please note that retries would be attempted with each server before
* trying with the next server in the queue, so if you want not to wait
* before trying the next server, retries should be set to 1.
* Please note that retries would be attempted with each server before
* trying with the next server in the queue, so if you want not to wait
* before trying the next server, retries should be set to 1.
* Optional, defaults to 3 attempts.
*/
'retries' => 3,
......@@ -52,6 +52,15 @@ authentication source which uses the `radius:Radius` module to
*/
'nas_identifier' => 'client.example.org',
/*
* An optional realm that will be suffixed to the username entered
* by the user. When set to "example.edu", and the user enters
* "bob" as their username, the radius server will be queried for
* the username "bob@example.edu".
* Optional, defaults to NULL.
*/
'realm' => 'example.edu',
/*
* The attribute name we should store the username in. Ths username
* will not be saved in any attribute if this is NULL.
......@@ -71,7 +80,7 @@ from the RADIUS server.
The code expects one vendor-attribute with a specific vendor and a specific
vendor attribute type for each user attribute. The vendor-attribute must
contain a value on the form <name>=<value>.
contain a value of the form `&lt;name&gt;=&lt;value&gt;`.
The following configuration options are available for user attributes:
......
......@@ -7,209 +7,230 @@
*
* @package SimpleSAMLphp
*/
class sspmod_radius_Auth_Source_Radius extends sspmod_core_Auth_UserPassBase {
/**
* The list of radius servers to use.
*/
private $servers;
/**
* The hostname of the radius server.
*/
private $hostname;
/**
* The port of the radius server.
*/
private $port;
/**
* The secret used when communicating with the radius server.
*/
private $secret;
/**
* The timeout for contacting the radius server.
*/
private $timeout;
/**
* The number of retries which should be attempted.
*/
private $retries;
/**
* The attribute name where the username should be stored.
*/
private $usernameAttribute;
/**
* The vendor for the RADIUS attributes we are interrested in.
*/
private $vendor;
/**
* The vendor-specific attribute for the RADIUS attributes we are interrested in.
*/
private $vendorType;
/**
* The NAS-Identifier that should be set in Access-Request packets.
*/
private $nasIdentifier;
/**
* Constructor for this authentication source.
*
* @param array $info Information about this authentication source.
* @param array $config Configuration.
*/
public function __construct($info, $config) {
assert('is_array($info)');
assert('is_array($config)');
// Call the parent constructor first, as required by the interface
parent::__construct($info, $config);
// Parse configuration.
$config = SimpleSAML_Configuration::loadFromArray($config,
'Authentication source ' . var_export($this->authId, TRUE));
$this->servers = $config->getArray('servers', array());
/* For backwards compatibility. */
if (empty($this->servers)) {
$this->hostname = $config->getString('hostname');
$this->port = $config->getIntegerRange('port', 1, 65535, 1812);
$this->secret = $config->getString('secret');
$this->servers[] = array('hostname' => $this->hostname,
'port' => $this->port,
'secret' => $this->secret);
}
$this->timeout = $config->getInteger('timeout', 5);
$this->retries = $config->getInteger('retries', 3);
$this->usernameAttribute = $config->getString('username_attribute', NULL);
$this->nasIdentifier = $config->getString('nas_identifier',
isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : 'localhost');
$this->vendor = $config->getInteger('attribute_vendor', NULL);
if ($this->vendor !== NULL) {
$this->vendorType = $config->getInteger('attribute_vendor_type');
}
}
/**
* Attempt to log in using the given username and password.
*
* @param string $username The username the user wrote.
* @param string $password The password the user wrote.
* @return array Associative array with the users attributes.
*/
protected function login($username, $password) {
assert('is_string($username)');
assert('is_string($password)');
$radius = radius_auth_open();
/* Try to add all radius servers, trigger a failure if no one works. */
$success = false;
foreach ($this->servers as $server) {
if (!isset($server['port'])) {
$server['port'] = 1812;
}
if (!radius_add_server($radius, $server['hostname'], $server['port'], $server['secret'],
$this->timeout, $this->retries)) {
SimpleSAML_Logger::info("Could not connect to server: ".radius_strerror($radius));
continue;
}
$success = true;
}
if (!$success) {
throw new Exception('Error connecting to radius server, no servers available');
}
if (!radius_create_request($radius, RADIUS_ACCESS_REQUEST)) {
throw new Exception('Error creating radius request: ' . radius_strerror($radius));
}
radius_put_attr($radius, RADIUS_USER_NAME, $username);
radius_put_attr($radius, RADIUS_USER_PASSWORD, $password);
if ($this->nasIdentifier != NULL)
radius_put_attr($radius, RADIUS_NAS_IDENTIFIER, $this->nasIdentifier);
$res = radius_send_request($radius);
if ($res != RADIUS_ACCESS_ACCEPT) {
switch ($res) {
case RADIUS_ACCESS_REJECT:
/* Invalid username or password. */
throw new SimpleSAML_Error_Error('WRONGUSERPASS');
case RADIUS_ACCESS_CHALLENGE:
throw new Exception('Radius authentication error: Challenge requested, but not supported.');
default:
throw new Exception('Error during radius authentication: ' . radius_strerror($radius));
}
}
/* If we get this far, we have a valid login. */
$attributes = array();
if ($this->usernameAttribute !== NULL) {
$attributes[$this->usernameAttribute] = array($username);
}
if ($this->vendor === NULL) {
/*
* We aren't interested in any vendor-specific attributes. We are
* therefore done now.
*/
return $attributes;
}
/* get AAI attribute sets. Contributed by Stefan Winter, (c) RESTENA */
while ($resa = radius_get_attr($radius)) {
if (!is_array($resa)) {
throw new Exception('Error getting radius attributes: ' . radius_strerror($radius));
}
/* Use the received user name */
if ($resa['attr'] == RADIUS_USER_NAME) {
$attributes[$this->usernameAttribute] = array($resa['data']);
continue;
}
if ($resa['attr'] !== RADIUS_VENDOR_SPECIFIC) {
continue;
}
$resv = radius_get_vendor_attr($resa['data']);
if (!is_array($resv)) {
throw new Exception('Error getting vendor specific attribute: ' . radius_strerror($radius));
}
$vendor = $resv['vendor'];
$attrv = $resv['attr'];
$datav = $resv['data'];
if ($vendor != $this->vendor || $attrv != $this->vendorType) {
continue;
}
$attrib_name = strtok($datav,'=');
$attrib_value = strtok('=');
/* if the attribute name is already in result set, add another value */
if (array_key_exists($attrib_name, $attributes)) {
$attributes[$attrib_name][] = $attrib_value;
} else {
$attributes[$attrib_name] = array($attrib_value);
}
}
/* end of contribution */
return $attributes;
}
class sspmod_radius_Auth_Source_Radius extends sspmod_core_Auth_UserPassBase
{
/**
* The list of radius servers to use.
*/
private $servers;
/**
* The hostname of the radius server.
*/
private $hostname;
/**
* The port of the radius server.
*/
private $port;
/**
* The secret used when communicating with the radius server.
*/
private $secret;
/**
* The timeout for contacting the radius server.
*/
private $timeout;
/**
* The number of retries which should be attempted.
*/
private $retries;
/**
* The realm to be added to the entered username.
*/
private $realm;
/**
* The attribute name where the username should be stored.
*/
private $usernameAttribute;
/**
* The vendor for the RADIUS attributes we are interrested in.
*/
private $vendor;
/**
* The vendor-specific attribute for the RADIUS attributes we are
* interrested in.
*/
private $vendorType;
/**
* The NAS-Identifier that should be set in Access-Request packets.
*/
private $nasIdentifier;
/**
* Constructor for this authentication source.
*
* @param array $info Information about this authentication source.
* @param array $config Configuration.
*/
public function __construct($info, $config)
{
assert('is_array($info)');
assert('is_array($config)');
// Call the parent constructor first, as required by the interface
parent::__construct($info, $config);
// Parse configuration.
$config = SimpleSAML_Configuration::loadFromArray($config,
'Authentication source ' . var_export($this->authId, true));
$this->servers = $config->getArray('servers', array());
/* For backwards compatibility. */
if (empty($this->servers)) {
$this->hostname = $config->getString('hostname');
$this->port = $config->getIntegerRange('port', 1, 65535, 1812);
$this->secret = $config->getString('secret');
$this->servers[] = array('hostname' => $this->hostname,
'port' => $this->port,
'secret' => $this->secret);
}
$this->timeout = $config->getInteger('timeout', 5);
$this->retries = $config->getInteger('retries', 3);
$this->realm = $config->getString('realm', null);
$this->usernameAttribute = $config->getString('username_attribute', null);
$this->nasIdentifier = $config->getString('nas_identifier',
isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : 'localhost');
$this->vendor = $config->getInteger('attribute_vendor', null);
if ($this->vendor !== null) {
$this->vendorType = $config->getInteger('attribute_vendor_type');
}
}
/**
* Attempt to log in using the given username and password.
*
* @param string $username The username the user wrote.
* @param string $password The password the user wrote.
* @return array Associative array with the user's attributes.
*/
protected function login($username, $password)
{
assert('is_string($username)');
assert('is_string($password)');
$radius = radius_auth_open();
/* Try to add all radius servers, trigger a failure if no one works. */
$success = false;
foreach ($this->servers as $server) {
if (!isset($server['port'])) {
$server['port'] = 1812;
}
if (!radius_add_server($radius,
$server['hostname'], $server['port'], $server['secret'],
$this->timeout, $this->retries)) {
SimpleSAML_Logger::info("Could not add radius server: " .
radius_strerror($radius));
continue;
}
$success = true;
}
if (!$success) {
throw new Exception('Error adding radius servers, no servers available');
}
if (!radius_create_request($radius, RADIUS_ACCESS_REQUEST)) {
throw new Exception('Error creating radius request: ' .
radius_strerror($radius));
}
if ($this->realm === null) {
radius_put_attr($radius, RADIUS_USER_NAME, $username);
} else {
radius_put_attr($radius, RADIUS_USER_NAME, $username . '@' . $this->realm);
}
radius_put_attr($radius, RADIUS_USER_PASSWORD, $password);
if ($this->nasIdentifier !== null) {
radius_put_attr($radius, RADIUS_NAS_IDENTIFIER, $this->nasIdentifier);
}
$res = radius_send_request($radius);
if ($res != RADIUS_ACCESS_ACCEPT) {
switch ($res) {
case RADIUS_ACCESS_REJECT:
/* Invalid username or password. */
throw new SimpleSAML_Error_Error('WRONGUSERPASS');
case RADIUS_ACCESS_CHALLENGE:
throw new Exception('Radius authentication error: Challenge requested, but not supported.');
default:
throw new Exception('Error during radius authentication: ' .
radius_strerror($radius));
}
}
/* If we get this far, we have a valid login. */
$attributes = array();
if ($this->usernameAttribute !== null) {
$attributes[$this->usernameAttribute] = array($username);
}
if ($this->vendor === null) {
/*
* We aren't interested in any vendor-specific attributes. We are
* therefore done now.
*/
return $attributes;
}
/* get AAI attribute sets. Contributed by Stefan Winter, (c) RESTENA */
while ($resa = radius_get_attr($radius)) {
if (!is_array($resa)) {
throw new Exception('Error getting radius attributes: ' .
radius_strerror($radius));
}
/* Use the received user name */
if ($resa['attr'] == RADIUS_USER_NAME) {
$attributes[$this->usernameAttribute] = array($resa['data']);
continue;
}
if ($resa['attr'] !== RADIUS_VENDOR_SPECIFIC) {
continue;
}
$resv = radius_get_vendor_attr($resa['data']);
if (!is_array($resv)) {
throw new Exception('Error getting vendor specific attribute: ' .
radius_strerror($radius));
}
$vendor = $resv['vendor'];
$attrv = $resv['attr'];
$datav = $resv['data'];
if ($vendor != $this->vendor || $attrv != $this->vendorType) {
continue;
}
$attrib_name = strtok($datav,'=');
$attrib_value = strtok('=');
/* if the attribute name is already in result set,
add another value */
if (array_key_exists($attrib_name, $attributes)) {
$attributes[$attrib_name][] = $attrib_value;
} else {
$attributes[$attrib_name] = array($attrib_value);
}
}
/* end of contribution */
return $attributes;
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment