mirror of
https://github.com/danog/tgseclib.git
synced 2024-11-27 04:34:45 +01:00
X509: auto download intermediate certs
This commit is contained in:
parent
894ac25e17
commit
f5807e1d4e
@ -320,6 +320,14 @@ class File_X509
|
||||
*/
|
||||
var $challenge;
|
||||
|
||||
/**
|
||||
* Recursion Limit
|
||||
*
|
||||
* @var int
|
||||
* @access private
|
||||
*/
|
||||
var $recur_limit = 5;
|
||||
|
||||
/**
|
||||
* Default Constructor.
|
||||
*
|
||||
@ -2144,6 +2152,113 @@ class File_X509
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches a URL
|
||||
*
|
||||
* @param string $url
|
||||
* @access private
|
||||
* @return bool|string
|
||||
*/
|
||||
function _fetchURL($url)
|
||||
{
|
||||
$parts = parse_url($url);
|
||||
$data = '';
|
||||
switch ($parts['scheme']) {
|
||||
case 'http':
|
||||
$fsock = @fsockopen($parts['host'], isset($parts['port']) ? $parts['port'] : 80);
|
||||
if (!$fsock) {
|
||||
return false;
|
||||
}
|
||||
fputs($fsock, "GET $parts[path] HTTP/1.0\r\n");
|
||||
fputs($fsock, "Host: $parts[host]\r\n\r\n");
|
||||
$line = fgets($fsock, 1024);
|
||||
if (strlen($line) < 3) {
|
||||
return false;
|
||||
}
|
||||
preg_match('#HTTP/1.\d (\d{3})#', $line, $temp);
|
||||
if ($temp[1] != '200') {
|
||||
return false;
|
||||
}
|
||||
|
||||
// skip the rest of the headers in the http response
|
||||
while (!feof($fsock) && fgets($fsock, 1024) != "\r\n") {
|
||||
}
|
||||
|
||||
while (!feof($fsock)) {
|
||||
$data.= fread($fsock, 1024);
|
||||
}
|
||||
|
||||
break;
|
||||
//case 'ftp':
|
||||
//case 'ldap':
|
||||
//default:
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates an intermediate cert as identified via authority info access extension
|
||||
*
|
||||
* See https://tools.ietf.org/html/rfc4325 for more info
|
||||
*
|
||||
* @param bool $caonly
|
||||
* @param int $count
|
||||
* @access private
|
||||
* @return bool
|
||||
*/
|
||||
function _testForIntermediate($caonly, $count)
|
||||
{
|
||||
$opts = $this->getExtension('id-pe-authorityInfoAccess');
|
||||
if (!is_array($opts)) {
|
||||
return false;
|
||||
}
|
||||
foreach ($opts as $opt) {
|
||||
if ($opt['accessMethod'] == 'id-ad-caIssuers') {
|
||||
// accessLocation is a GeneralName. GeneralName fields support stuff like email addresses, IP addresses, LDAP,
|
||||
// etc, but we're only supporting URI's. URI's and LDAP are the only thing https://tools.ietf.org/html/rfc4325
|
||||
// discusses
|
||||
if (isset($opt['accessLocation']['uniformResourceIdentifier'])) {
|
||||
$url = $opt['accessLocation']['uniformResourceIdentifier'];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!isset($url)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$cert = $this->_fetchURL($url);
|
||||
if (!is_string($cert)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$parent = new static();
|
||||
$parent->CAs = $this->CAs;
|
||||
/*
|
||||
"Conforming applications that support HTTP or FTP for accessing
|
||||
certificates MUST be able to accept .cer files and SHOULD be able
|
||||
to accept .p7c files." -- https://tools.ietf.org/html/rfc4325
|
||||
|
||||
A .p7c file is 'a "certs-only" CMS message as specified in RFC 2797"
|
||||
|
||||
These are currently unsupported
|
||||
*/
|
||||
if (!is_array($parent->loadX509($cert))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!$parent->_validateSignatureCountable($caonly, ++$count)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->CAs[] = $parent->currentCert;
|
||||
//$this->loadCA($cert);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate a signature
|
||||
*
|
||||
@ -2160,11 +2275,30 @@ class File_X509
|
||||
* @return mixed
|
||||
*/
|
||||
function validateSignature($caonly = true)
|
||||
{
|
||||
return $this->_validateSignatureCountable($caonly, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate a signature
|
||||
*
|
||||
* Performs said validation whilst keeping track of how many times validation method is called
|
||||
*
|
||||
* @param bool $caonly
|
||||
* @param int $count
|
||||
* @access private
|
||||
* @return mixed
|
||||
*/
|
||||
function _validateSignatureCountable($caonly, $count)
|
||||
{
|
||||
if (!is_array($this->currentCert) || !isset($this->signatureSubject)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if ($count == $this->recur_limit) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* TODO:
|
||||
"emailAddress attribute values are not case-sensitive (e.g., "subscriber@example.com" is the same as "SUBSCRIBER@EXAMPLE.COM")."
|
||||
-- http://tools.ietf.org/html/rfc5280#section-4.1.2.6
|
||||
@ -2210,10 +2344,10 @@ class File_X509
|
||||
}
|
||||
}
|
||||
if (count($this->CAs) == $i && $caonly) {
|
||||
return false;
|
||||
return $this->_testForIntermediate($caonly, $count) && $this->validateSignature($caonly);
|
||||
}
|
||||
} elseif (!isset($signingCert) || $caonly) {
|
||||
return false;
|
||||
return $this->_testForIntermediate($caonly, $count) && $this->validateSignature($caonly);
|
||||
}
|
||||
return $this->_validateSignature(
|
||||
$signingCert['tbsCertificate']['subjectPublicKeyInfo']['algorithm']['algorithm'],
|
||||
@ -2323,6 +2457,21 @@ class File_X509
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the recursion limit
|
||||
*
|
||||
* When validating a signature it may be necessary to download intermediate certs from URI's.
|
||||
* An intermediate cert that linked to itself would result in an infinite loop so to prevent
|
||||
* that we set a recursion limit. A negative number means that there is no recursion limit.
|
||||
*
|
||||
* @param int $count
|
||||
* @access public
|
||||
*/
|
||||
function setRecurLimit($count)
|
||||
{
|
||||
$this->recur_limit = $count;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reformat public keys
|
||||
*
|
||||
|
Loading…
Reference in New Issue
Block a user