155 lines
4.9 KiB
PHP
155 lines
4.9 KiB
PHP
<?php
|
|
/**
|
|
* Copyright 2013-2017 Horde LLC (http://www.horde.org)
|
|
*
|
|
* See the enclosed file COPYING for license information (LGPL). If you did
|
|
* not receive this file, see http://www.horde.org/licenses/lgpl21.
|
|
*
|
|
* @author Michael J Rubinsky <mrubinsk@horde.org>
|
|
* @category Horde
|
|
* @license http://www.horde.org/licenses/lgpl21 LGPL-2.1
|
|
* @package Auth
|
|
* @since 2.1.0
|
|
*/
|
|
|
|
/**
|
|
* The Horde_Auth_X509 class provides an authentication driver for using X509
|
|
* client certificates. Since X509 certificates do not provide the password,
|
|
* if the server setup requires the use of per-user passwords, a callback
|
|
* function may be passed to obtain it from.
|
|
*
|
|
* @author Michael J Rubinsky <mrubinsk@horde.org>
|
|
* @category Horde
|
|
* @copyright 2013-2017 Horde LLC
|
|
* @license http://www.horde.org/licenses/lgpl21 LGPL-2.1
|
|
* @package Auth
|
|
* @since 2.1.0
|
|
*/
|
|
class Horde_Auth_X509 extends Horde_Auth_Base
|
|
{
|
|
/**
|
|
* An array of capabilities, so that the driver can report which
|
|
* operations it supports and which it doesn't.
|
|
*
|
|
* @var array
|
|
*/
|
|
protected $_capabilities = array(
|
|
'transparent' => true
|
|
);
|
|
|
|
/**
|
|
* Constructor.
|
|
*
|
|
* @param array $params Parameters:
|
|
* - password: (string) If available, the password to use for the session.
|
|
* DEFAULT: no password used.
|
|
* - username_field: (string) Name of the $_SERVER field that
|
|
* the username can be found in. DEFAULT: 'SSL_CLIENT_S_DN_EMAILADDRESS'.
|
|
* - certificate_field: (string) Name of the $_SERVER field that contains
|
|
* the full certificate. DEFAULT: 'SSL_CLIENT_CERT'
|
|
* - ignore_purpose: (boolean) If true, will ignore any usage restrictions
|
|
* on the presented client certificate. I.e., if openssl_x509_checkpurpose
|
|
* returns false, authentication may still proceed. DEFAULT: false - ONLY
|
|
* ENABLE THIS IF YOU KNOW WHY YOU ARE DOING SO.
|
|
* - filter: (array) An array where the keys are field names and the
|
|
* values are the values those certificate fields MUST
|
|
* match to be considered valid. Keys in the format of
|
|
* fieldone:fieldtwo will be taken as parent:child.
|
|
* DEFAULT: no additionachecks applied.
|
|
*
|
|
* @throws InvalidArgumentException
|
|
*/
|
|
public function __construct(array $params = array())
|
|
{
|
|
$params = array_merge(array(
|
|
'password' => false,
|
|
'username_field' => 'SSL_CLIENT_S_DN_CN',
|
|
'certificate_field' => 'SSL_CLIENT_CERT',
|
|
'ignore_purpose' => true,
|
|
'filter' => array()
|
|
), $params);
|
|
|
|
parent::__construct($params);
|
|
}
|
|
|
|
/**
|
|
* Not implemented.
|
|
*
|
|
* @param string $userId The userID to check.
|
|
* @param array $credentials An array of login credentials.
|
|
*
|
|
* @throws Horde_Auth_Exception
|
|
*/
|
|
protected function _authenticate($userId, $credentials)
|
|
{
|
|
throw new Horde_Auth_Exception('Unsupported.');
|
|
}
|
|
|
|
/**
|
|
* Automatic authentication: checks if the username is set in the
|
|
* configured header.
|
|
*
|
|
* @return boolean Whether or not the client is allowed.
|
|
*/
|
|
public function transparent()
|
|
{
|
|
if (!is_callable('openssl_x509_parse')) {
|
|
throw new Horde_Auth_Exception('SSL not enabled on server.');
|
|
}
|
|
|
|
if (empty($_SERVER[$this->_params['username_field']]) ||
|
|
empty($_SERVER[$this->_params['certificate_field']])) {
|
|
return false;
|
|
}
|
|
|
|
// Valid for client auth?
|
|
$cert = openssl_x509_read($_SERVER[$this->_params['certificate_field']]);
|
|
if (!$this->_params['ignore_purpose'] &&
|
|
!openssl_x509_checkpurpose($cert, X509_PURPOSE_SSL_CLIENT) &&
|
|
!openssl_x509_checkpurpose($cert, X509_PURPOSE_ANY)) {
|
|
return false;
|
|
}
|
|
|
|
$c_parsed = openssl_x509_parse($cert);
|
|
foreach ($this->_params['filter'] as $key => $value) {
|
|
$keys = explode(':', $key);
|
|
$c = $c_parsed;
|
|
foreach ($keys as $k) {
|
|
$c = $c[$k];
|
|
}
|
|
if ($c != $value) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// Handle any custom validation added by sub classes.
|
|
if (!$this->_validate($cert)) {
|
|
return false;
|
|
}
|
|
|
|
// Free resources.
|
|
openssl_x509_free($cert);
|
|
|
|
// Set credentials
|
|
$this->setCredential('userId', $_SERVER[$this->_params['username_field']]);
|
|
$cred = array('certificate_id' => $c_parsed['hash']);
|
|
if (!empty($this->_params['password'])) {
|
|
$cred['password'] = $this->_params['password'];
|
|
}
|
|
$this->setCredential('credentials', $cred);
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Perform additional validation of certificate fields.
|
|
*
|
|
* @return boolean
|
|
*/
|
|
protected function _validate($certificate)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
}
|