summaryrefslogtreecommitdiff
path: root/includes/account_stuff.php
diff options
context:
space:
mode:
authorMichael Tänzer <neo@nhng.de>2011-04-11 00:43:33 +0200
committerMichael Tänzer <neo@nhng.de>2011-04-11 00:43:33 +0200
commit803eaaf70962b3beedc5cb8ce0f82aa8e7fe589f (patch)
treeae6cc3ac7e7aa53056fbb652a916232349d322ed /includes/account_stuff.php
parent384b57dc57b90c01b6c376be8c2b564470d042cb (diff)
downloadcacert-devel-803eaaf70962b3beedc5cb8ce0f82aa8e7fe589f.tar.gz
cacert-devel-803eaaf70962b3beedc5cb8ce0f82aa8e7fe589f.tar.xz
cacert-devel-803eaaf70962b3beedc5cb8ce0f82aa8e7fe589f.zip
#918: Reimplement openssl-vulnkey in PHP so we don't have to include
python in the chroot of the web server #918: "Weak keys in certificates" Signed-off-by: Michael Tänzer <neo@nhng.de>
Diffstat (limited to 'includes/account_stuff.php')
-rw-r--r--includes/account_stuff.php256
1 files changed, 156 insertions, 100 deletions
diff --git a/includes/account_stuff.php b/includes/account_stuff.php
index 0299ead..e8beb7f 100644
--- a/includes/account_stuff.php
+++ b/includes/account_stuff.php
@@ -319,57 +319,37 @@ function hideall() {
*/
function checkWeakKeyCSR($csr, $encoding = "PEM")
{
- /* If another encoding is used: convert to PEM */
- if ($encoding !== "PEM")
+ // non-PEM-encodings may be binary so don't use echo
+ $descriptorspec = array(
+ 0 => array("pipe", "r"), // STDIN for child
+ 1 => array("pipe", "w"), // STDOUT for child
+ );
+ $encoding = escapeshellarg($encoding);
+ $proc = proc_open("openssl req -inform $encoding -text -noout",
+ $descriptorspec, $pipes);
+
+ if (is_resource($proc))
{
- // other encodings may be binary so don't use echo
- $descriptorspec = array(
- 0 => array("pipe", "r"), // STDIN for child
- 1 => array("pipe", "w"), // STDOUT for child
- 2 => array("file", "/dev/null", "w") // ignore STDERR
- );
- $encoding = escapeshellarg($encoding);
- $proc = proc_open("openssl req -inform $encoding -outform PEM",
- $descriptorspec, $pipes);
+ fwrite($pipes[0], $csr);
+ fclose($pipes[0]);
- if (is_resource($proc))
+ $csrText = "";
+ while (!feof($pipes[1]))
{
- fwrite($pipes[0], $csr);
- fclose($pipes[0]);
-
- $csr = stream_get_contents($pipes[1]);
- fclose($pipes[1]);
-
- proc_close($proc);
- } else {
- trigger_error("checkWeakKeyCSR(): Failed to start OpenSSL",
- E_USER_ERROR);
- return _("Something went wrong when parsing the certificate ".
- "signing request!");
+ $csrText .= fread($pipes[1], 8192);
}
+ fclose($pipes[1]);
+
+ if (($status = proc_close($proc)) !== 0 || $csrText === "")
+ {
+ return _("I didn't receive a valid Certificate Request, hit ".
+ "the back button and try again.");
+ }
+ } else {
+ return failWithId("checkWeakKeyCSR(): Failed to start OpenSSL");
}
- /* Check for the debian OpenSSL vulnerability */
-
- $csr = escapeshellarg($csr);
- exec("echo $csr | openssl-vulnkey -q -", $dummy, $debianVuln);
- if ($debianVuln === -1)
- {
- return sprintf(_("The keys you use have very likely been ".
- "generated with a vulnerable version of OpenSSL which was ".
- "distributed by debian. Please generate new keys. More ".
- "information about this issue can be found in %sthe ".
- "wiki%s"),
- "<a href='//wiki.cacert.org/WeakKeys#DebianVulnerability'>",
- "</a>");
- } elseif ($debianVuln !== 0) {
- trigger_error("checkWeakKeyCSR(): Something went wrong in the ".
- "openssl-vulnkey call", E_USER_ERROR);
- }
-
- // $csr already escaped
- $csrText = `echo $csr | openssl req -text -noout`;
return checkWeakKeyText($csrText);
}
@@ -390,60 +370,10 @@ function hideall() {
$spkac = escapeshellarg($spkac);
$spkacname = escapeshellarg($spkacname);
-
$spkacText = `echo $spkac | openssl spkac -spkac $spkacname`;
-
- /* Which public key algorithm? */
- if (!preg_match('/^\s*Public Key Algorithm: ([^\s]+)$/m', $spkacText,
- $algorithm))
- {
- trigger_error("checkWeakKeySPKAC(): Couldn't extract the public ".
- "key algorithm used", E_USER_WARNING);
- return "";
- } else {
- $algorithm = $algorithm[1];
- }
-
- if ($algorithm === "rsaEncryption")
- {
- if (!preg_match('/^\s*RSA Public Key: \((\d+) bit\)$/m', $spkacText,
- $keysize))
- {
- trigger_error("checkWeakKeySPKAC(): Couldn't parse the RSA ".
- "key size", E_USER_WARNING);
- } else {
- $keysize = $keysize[1];
-
- // $spkac and $spkacname already escaped
- $modulus = `echo $spkac | openssl spkac -spkac $spkacname \
- -pubkey -noout | openssl rsa -pubin -modulus -noout`;
-
- if (!preg_match('/^Modulus=([0-9A-F]+)$/', $modulus, $modulus))
- {
- trigger_error("checkWeakKeySPKAC(): Couldn't parse the ".
- "RSA modulus", E_USER_WARNING);
- } else {
- $modulus = $modulus[1];
-
- $keysize = escapeshellarg($keysize);
- $modulus = escapeshellarg($modulus);
- exec("openssl-vulnkey -q -b $keysize -m $modulus", $dummy,
- $debianVuln);
- if ($debianVuln === -1)
- {
- return sprintf(_("The keys you use have very likely been ".
- "generated with a vulnerable version of OpenSSL which was ".
- "distributed by debian. Please generate new keys. More ".
- "information about this issue can be found in %sthe ".
- "wiki%s"),
- "<a href='//wiki.cacert.org/WeakKeys#DebianVulnerability'>",
- "</a>");
- } elseif ($debianVuln !== 0) {
- trigger_error("checkWeakKeySPKAC(): Something went ".
- "wrong in the openssl-vulnkey call", E_USER_ERROR);
- }
- }
- }
+ if ($spkacText === null) {
+ return _("I didn't receive a valid Certificate Request, hit the ".
+ "back button and try again.");
}
return checkWeakKeyText($spkacText);
@@ -451,9 +381,7 @@ function hideall() {
/**
* Checks whether the given text representation of a CSR or a SPKAC contains
- * a weak key.
- * ONLY TO BE USED BY THE MORE SPECIAL METHODS checkWeakKeyCSR() AND
- * checkWeakKeySPKAC()
+ * a weak key
*
* @param $text string
* The text representation of a key as output by the
@@ -501,6 +429,23 @@ function hideall() {
}
+ $debianVuln = checkDebianVulnerability($text, $keysize);
+ if ($debianVuln === true)
+ {
+ return sprintf(_("The keys you use have very likely been ".
+ "generated with a vulnerable version of OpenSSL which ".
+ "was distributed by debian. Please generate new keys. ".
+ "More information about this issue can be found in ".
+ "%sthe wiki%s"),
+ "<a href='//wiki.cacert.org/WeakKeys#DebianVulnerability'>",
+ "</a>");
+ } elseif ($debianVuln === false) {
+ // not vulnerable => do nothing
+ } else {
+ return failWithId("checkWeakKeyText(): Something went wrong in".
+ "checkDebianVulnerability()");
+ }
+
if (!preg_match('/^\s*Exponent: (\d+) \(0x[0-9a-fA-F]+\)$/m', $text,
$exponent))
{
@@ -538,4 +483,115 @@ function hideall() {
/* No weakness found */
return "";
}
+
+ /**
+ * Reimplement the functionality of the openssl-vulnkey tool
+ *
+ * @param $text string
+ * The text representation of a key as output by the
+ * "openssl <foo> -text -noout" commands
+ * @param $keysize int [optional]
+ * If the key size is already known it can be provided so it doesn't
+ * have to be parsed again. This also skips the check whether the key
+ * is an RSA key => use wisely
+ * @return TRUE if key is vulnerable, FALSE otherwise, NULL in case of error
+ */
+ function checkDebianVulnerability($text, $keysize = 0)
+ {
+ $keysize = intval($keysize);
+
+ if ($keysize === 0)
+ {
+ /* Which public key algorithm? */
+ if (!preg_match('/^\s*Public Key Algorithm: ([^\s]+)$/m', $text,
+ $algorithm))
+ {
+ trigger_error("checkDebianVulnerability(): Couldn't extract ".
+ "the public key algorithm used", E_USER_WARNING);
+ return null;
+ } else {
+ $algorithm = $algorithm[1];
+ }
+
+ if ($algorithm !== "rsaEncryption") return false;
+
+ /* Extract public key size */
+ if (!preg_match('/^\s*RSA Public Key: \((\d+) bit\)$/m', $text,
+ $keysize))
+ {
+ trigger_error("checkDebianVulnerability(): Couldn't parse the ".
+ "RSA key size", E_USER_WARNING);
+ return null;
+ } else {
+ $keysize = intval($keysize[1]);
+ }
+ }
+
+ // $keysize has been made sure to contain an int
+ $blacklist = "/usr/share/openssl-blacklist/blacklist.RSA-$keysize";
+ if (!(is_file($blacklist) && is_readable($blacklist)))
+ {
+ if (in_array($keysize, array(512, 1024, 2048, 4096)))
+ {
+ trigger_error("checkDebianVulnerability(): Blacklist for ".
+ "$keysize bit keys not accessible. Expected at ".
+ "$blacklist", E_USER_ERROR);
+ return null;
+ }
+
+ trigger_error("checkDebianVulnerability(): $blacklist is not ".
+ "readable. Unsupported key size?", E_USER_WARNING);
+ return false;
+ }
+
+
+ /* Extract RSA modulus */
+ if (!preg_match('/^\s*Modulus \(\d+ bit\):\n'.
+ '((?:\s*[0-9a-f][0-9a-f]:(?:\n)?)+[0-9a-f][0-9a-f])$/m',
+ $text, $modulus))
+ {
+ trigger_error("checkDebianVulnerability(): Couldn't extract the ".
+ "RSA modulus", E_USER_WARNING);
+ return null;
+ } else {
+ $modulus = $modulus[1];
+ // strip whitespace and colon leftovers
+ $modulus = str_replace(array(" ", "\t", "\n", ":"), "", $modulus);
+
+ // when using "openssl xxx -text" first byte was 00 in all my test
+ // cases but 00 not present in the "openssl xxx -modulus" output
+ if ($modulus[0] === "0" && $modulus[1] === "0")
+ {
+ $modulus = substr($modulus, 2);
+ } else {
+ trigger_error("checkDebianVulnerability(): First byte is not ".
+ "zero", E_USER_NOTICE);
+ }
+
+ $modulus = strtoupper($modulus);
+ }
+
+
+ /* calculate checksum and look it up in the blacklist */
+ $checksum = substr(sha1("Modulus=$modulus\n"), 20);
+
+ // $checksum and $blacklist should be safe, but just to make sure
+ $checksum = escapeshellarg($checksum);
+ $blacklist = escapeshellarg($blacklist);
+ exec("grep $checksum $blacklist", $dummy, $debianVuln);
+ if ($debianVuln === 0) // grep returned something => it is on the list
+ {
+ return true;
+ } elseif ($debianVuln === 1) { // grep returned nothing
+ return false;
+ } else {
+ trigger_error("checkDebianVulnerability(): Something went wrong ".
+ "when looking up the key with checksum $checksum in the ".
+ "blacklist $blacklist", E_USER_ERROR);
+ return null;
+ }
+
+ // Should not get here
+ return null;
+ }
?>