Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions src/opnsense/mvc/app/library/OPNsense/Auth/Radius.php
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,11 @@ class Radius extends Base implements IAuthConnector
*/
private $lastAuthProperties = [];

/**
* @var string|null outcome of the last authenticate() call (accept|reject|timeout|error)
*/
private $lastResult = null;

/**
* @var boolean when set, synchronize groups defined in memberOf attribute to local database
*/
Expand Down Expand Up @@ -174,6 +179,7 @@ public function setProperties($config)
'radius_secret' => 'sharedSecret',
'radius_stationid' => 'calledStationId',
'radius_timeout' => 'timeout',
'radius_max_retries' => 'maxRetries',
'refid' => 'nasIdentifier',
];

Expand Down Expand Up @@ -245,6 +251,15 @@ public function getLastAuthProperties()
return $this->lastAuthProperties;
}

/**
* outcome of the last authenticate() call
* @return string|null one of accept, reject, timeout, error, or null
*/
public function getLastResult()
{
return $this->lastResult;
}

/**
* send start accounting message to radius
* @param string $username username
Expand Down Expand Up @@ -512,6 +527,7 @@ public function preauth($config)
public function authenticate($username, $password)
{
$this->lastAuthProperties = [];// reset auth properties
$this->lastResult = null;
$radius = radius_auth_open();

$error = null;
Expand Down Expand Up @@ -592,6 +608,7 @@ public function authenticate($username, $password)
break;
default:
syslog(LOG_ERR, 'Unsupported protocol ' . $this->protocol);
$this->lastResult = 'error';
return false;
}
}
Expand All @@ -601,10 +618,12 @@ public function authenticate($username, $password)

// log errors and perform actual authentication request
if ($error != null) {
$this->lastResult = 'error';
syslog(LOG_ERR, 'RadiusError: ' . $error);
} else {
$request = radius_send_request($radius);
if (!$request) {
$this->lastResult = 'timeout';
syslog(LOG_ERR, 'RadiusError: ' . radius_strerror($radius));
} else {
switch ($request) {
Expand Down Expand Up @@ -652,13 +671,16 @@ public function authenticate($username, $password)
$this->syncDefaultGroups
);
}
$this->lastResult = 'accept';
return true;
break;
case RADIUS_ACCESS_REJECT:
$this->lastResult = 'reject';
return false;
break;
default:
// unexpected result, log
$this->lastResult = 'error';
syslog(LOG_ERR, 'Radius unexpected response:' . $request);
}
}
Expand Down
12 changes: 12 additions & 0 deletions src/opnsense/scripts/openvpn/user_pass_verify.php
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,18 @@ function do_auth($common_name, $serverid, $method, $auth_file)
}
return true;
}
// only cascade on a decisive answer; an upstream that didn't
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's focus on retries and don't try to complicate the flow further than needed, if you only need a single upstream authenticator, just configure a single one.

// respond may dedup the next request and suppress new MFA prompts
if (
method_exists($authenticator, 'getLastResult')
&& $authenticator->getLastResult() === 'timeout'
) {
syslog(
LOG_WARNING,
"auth source '{$authName}' did not respond for user '{$username}', not cascading"
);
break;
}
}
}
return "user '{$username}' could not authenticate.";
Expand Down
22 changes: 22 additions & 0 deletions src/www/system_authservers.php
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@
$pconfig['radius_acct_port'] = $a_server[$id]['radius_acct_port'] ?? '';
$pconfig['radius_secret'] = $a_server[$id]['radius_secret'] ?? '';
$pconfig['radius_timeout'] = $a_server[$id]['radius_timeout'] ?? '';
$pconfig['radius_max_retries'] = $a_server[$id]['radius_max_retries'] ?? '';
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we better move these to the Radius class, same as 95483e5

$pconfig['radius_stationid'] = $a_server[$id]['radius_stationid'] ?? '';

if (!empty($pconfig['radius_auth_port']) &&
Expand Down Expand Up @@ -217,6 +218,9 @@
if (($pconfig['type'] == "radius") && isset($pconfig['radius_timeout']) && !empty($pconfig['radius_timeout']) && (!is_numeric($pconfig['radius_timeout']) || (is_numeric($pconfig['radius_timeout']) && ($pconfig['radius_timeout'] <= 0)))) {
$input_errors[] = gettext("RADIUS Timeout value must be numeric and positive.");
}
if (($pconfig['type'] == "radius") && isset($pconfig['radius_max_retries']) && !empty($pconfig['radius_max_retries']) && (!is_numeric($pconfig['radius_max_retries']) || (is_numeric($pconfig['radius_max_retries']) && ($pconfig['radius_max_retries'] <= 0)))) {
$input_errors[] = gettext("RADIUS Max Retries value must be numeric and positive.");
}
if (empty($pconfig['name'])) {
$input_errors[] = gettext('A server name must be provided.');
} elseif (strpos($pconfig['name'], ',') !== false) {
Expand Down Expand Up @@ -278,6 +282,12 @@
$server['radius_timeout'] = 5;
}

if (!empty($pconfig['radius_max_retries'])) {
$server['radius_max_retries'] = $pconfig['radius_max_retries'];
} else {
unset($server['radius_max_retries']);
}

if (!empty($pconfig['radius_stationid'])) {
$server['radius_stationid'] = $pconfig['radius_stationid'];
} else {
Expand Down Expand Up @@ -368,6 +378,7 @@
'radius_secret',
'radius_srvcs',
'radius_timeout',
'radius_max_retries',
'radius_stationid',
'sync_create_local_users',
'sync_memberof',
Expand Down Expand Up @@ -864,6 +875,17 @@
</div>
</td>
</tr>
<tr class="auth_radius auth_options hidden">
<td><a id="help_for_radius_max_retries" href="#" class="showhelp"><i class="fa fa-info-circle"></i></a> <?=gettext("Max Retries");?></td>
<td>
<input name="radius_max_retries" type="text" id="radius_max_retries" size="20" value="<?=$pconfig['radius_max_retries'];?>"/>
<div class="hidden" data-for="help_for_radius_max_retries">
<br /><?= gettext("Maximum number of request attempts to send to the RADIUS server before giving up.") ?>
<br /><?= gettext("If left blank, the default value is 3.") ?>
<br /><br /><?= gettext("NOTE: If you are using an interactive two-factor authentication system, raise this value (and/or Authentication Timeout) to extend the approval window the user has to acknowledge the prompt.") ?>
</div>
</td>
</tr>
<tr class="auth_radius auth_options hidden">
<td><a id="help_for_radius_stationid" href="#" class="showhelp"><i class="fa fa-info-circle"></i></a> <?=gettext("Called Station ID");?></td>
<td>
Expand Down