From e12f0f1d523c4f1eed1810cb81644739e8893000 Mon Sep 17 00:00:00 2001 From: perennate Date: Fri, 27 Sep 2013 21:57:07 -0400 Subject: [PATCH] Add optional PGP key verification on web server side. --- .../public_html/config.sample.php | 24 ++++++ .../public_html/include/common.php | 15 ++++ gpg-mailgate-web/public_html/include/gpg.php | 85 +++++++++++++++++++ gpg-mailgate-web/public_html/include/pgp.php | 9 ++ 4 files changed, 133 insertions(+) create mode 100644 gpg-mailgate-web/public_html/include/gpg.php diff --git a/gpg-mailgate-web/public_html/config.sample.php b/gpg-mailgate-web/public_html/config.sample.php index 014e9c1..6cd4684 100644 --- a/gpg-mailgate-web/public_html/config.sample.php +++ b/gpg-mailgate-web/public_html/config.sample.php @@ -42,6 +42,9 @@ $config['site_url'] = 'http://example.com/gpgmw'; //title of the website (displayed on home page) $config['site_title'] = 'PGP key management'; +//whether debug mode should be enabled +$config['debug'] = false; + // // MAIL SETTINGS // @@ -71,6 +74,27 @@ $config['db_username'] = 'gpgmw'; //database password $config['db_password'] = ''; +// +// PGP VERIFICATION SETTINGS +// + +//whether to enable immediate verification of PGP keys +// keys will always be verified with the email address in our cron job +// but this will enable verification from the web interface before email confirmation +//for this to work, Crypt_GPG from http://pear.php.net/Crypt_GPG must be installed +// (as well as any of its dependencies), and pgpverify_tmpdir must be set +$config['pgpverify_enable'] = false; + +//a temporary directory to use for PGP verification, without trailing slash +// gpgmw will create subdirectories from here to use as temporary gpg home directories +// these directories will (should) be deleted immediately after use +$config['pgpverify_tmpdir'] = '/tmp'; + +//whether to allow blank "keys" +// this is useful to allow users to delete their key from the keystore +// if they no longer want encryption +$config['pgpverify_allowblank'] = true; + // // LOCK SETTINGS // diff --git a/gpg-mailgate-web/public_html/include/common.php b/gpg-mailgate-web/public_html/include/common.php index 8af7b6e..d3bc1c1 100644 --- a/gpg-mailgate-web/public_html/include/common.php +++ b/gpg-mailgate-web/public_html/include/common.php @@ -99,6 +99,21 @@ function secure_random() { return hexdec(bin2hex(secure_random_bytes(3))); } +function recursiveDelete($dirPath) { + foreach( + new RecursiveIteratorIterator( + new RecursiveDirectoryIterator( + $dirPath, FilesystemIterator::SKIP_DOTS + ), + RecursiveIteratorIterator::CHILD_FIRST + ) + as $path) { + $path->isFile() ? unlink($path->getPathname()) : rmdir($path->getPathname()); + } + + rmdir($dirPath); +} + function gpgmw_mail($subject, $body, $to) { //returns true=ok, false=notok $config = $GLOBALS['config']; $from = filter_var($config['email_from'], FILTER_SANITIZE_EMAIL); diff --git a/gpg-mailgate-web/public_html/include/gpg.php b/gpg-mailgate-web/public_html/include/gpg.php new file mode 100644 index 0000000..03b8f7d --- /dev/null +++ b/gpg-mailgate-web/public_html/include/gpg.php @@ -0,0 +1,85 @@ +. + +*/ + +//uses gpg to verify that a key belongs to a given email address +function verifyPGPKey($content, $email) { + global $config; + + //allow blank "keys" if this is set + //this means that encryption for $email will be disabled by the cron if it + // was enabled originally + if($config['pgpverify_allowblank'] && trim($content) == '') { + return true; + } + + require_once("Crypt/GPG.php"); + + //try to create a random subdirectory of $config['pgpverify_tmpdir'] + do { + $path = $config['pgpverify_tmpdir'] . '/' . uid(16); + } while(file_exists($path)); + + $result = @mkdir($path); + + if($result === false) { + if($config['debug']) { + die("Failed to create directory [" . $path . "] for PGP verification."); + } else { + return false; + } + } + + $gpg = new Crypt_GPG(array('homedir' => $path)); + + //import the key to our GPG temp directory + try { + $gpg->importKey($content); + } catch(Crypt_GPG_NoDataException $e) { + //user supplied an invalid key! + recursiveDelete($path); + return false; + } + + //verify the email address matches + $keys = $gpg->getKeys(); + + if(count($keys) != 1) { + if($config['debug']) { + die("Error in PGP verification: key count is " . count($keys) . "!"); + } else { + recursiveDelete($path); + return false; + } + } + + $userIds = $keys[0]->getUserIds(); + + if(count($userIds) != 1 || strtolower($userIds[0]->getEmail()) != strtolower($email)) { + recursiveDelete($path); + return false; + } + + recursiveDelete($path); + return true; +} + +?> diff --git a/gpg-mailgate-web/public_html/include/pgp.php b/gpg-mailgate-web/public_html/include/pgp.php index ad88e0b..256ec11 100644 --- a/gpg-mailgate-web/public_html/include/pgp.php +++ b/gpg-mailgate-web/public_html/include/pgp.php @@ -56,6 +56,15 @@ function requestPGP($email, $key) { } } + //if PGP key verification is enabled, do it + if($config['pgpverify_enable']) { + require_once(includePath() . "/gpg.php"); + + if(!verifyPGPKey($key, $email)) { + return "your key does not appear to be valid (ensure ASCII armor is enabled and that the email address entered matches the email address of the key)"; + } + } + //well, it looks good, let's submit it lockAction('requestpgp'); $confirm = uid(32);