From a833eaa6513a3f7e5732721996877e442a0af100 Mon Sep 17 00:00:00 2001 From: Mikael Nordfeldth Date: Tue, 28 Jun 2016 11:51:11 +0200 Subject: [PATCH] Make all hash algorithms available (but whitelist by default) sha1 is whitelisted only because StatusNet requires it. --- lib/default.php | 3 +++ plugins/OStatus/classes/FeedSub.php | 41 +++++++++++++++++------------ 2 files changed, 27 insertions(+), 17 deletions(-) diff --git a/lib/default.php b/lib/default.php index 12e194f549..b3685a284c 100644 --- a/lib/default.php +++ b/lib/default.php @@ -64,6 +64,9 @@ $default = 'notice' => null, // site wide notice text 'build' => 1, // build number, for code-dependent cache ), + 'security' => + array('hash_algos' => ['sha1', 'sha256', 'sha512'], // set to null for anything that hash_hmac() can handle (and is in hash_algos()) + ), 'db' => array('database' => null, // must be set 'schema_location' => INSTALLDIR . '/classes', diff --git a/plugins/OStatus/classes/FeedSub.php b/plugins/OStatus/classes/FeedSub.php index 72746e9b90..13a5439421 100644 --- a/plugins/OStatus/classes/FeedSub.php +++ b/plugins/OStatus/classes/FeedSub.php @@ -442,10 +442,10 @@ class FeedSub extends Managed_DataObject */ public function receive($post, $hmac) { - common_log(LOG_INFO, __METHOD__ . ": packet for \"" . $this->getUri() . "\"! $hmac $post"); + common_log(LOG_INFO, sprintf(__METHOD__.': packet for %s with HMAC %s', _ve($this->getUri()), _ve($hmac))); if (!in_array($this->sub_state, array('active', 'nohub'))) { - common_log(LOG_ERR, __METHOD__ . ": ignoring PuSH for inactive feed " . $this->getUri() . " (in state '$this->sub_state')"); + common_log(LOG_ERR, sprintf(__METHOD__.': ignoring PuSH for inactive feed %s (in state %s)', _ve($this->getUri()), _ve($this->sub_state))); return; } @@ -480,7 +480,7 @@ class FeedSub extends Managed_DataObject * shared secret that was set up at subscription time. * * If we don't have a shared secret, there should be no signature. - * If we we do, our the calculated HMAC should match theirs. + * If we do, our calculated HMAC should match theirs. * * @param string $post raw XML source as POSTed to us * @param string $hmac X-Hub-Signature HTTP header value, or empty @@ -489,29 +489,36 @@ class FeedSub extends Managed_DataObject protected function validatePushSig($post, $hmac) { if ($this->secret) { - if (preg_match('/^sha1=([0-9a-fA-F]{40})$/', $hmac, $matches)) { - $their_hmac = strtolower($matches[1]); - $our_hmac = hash_hmac('sha1', $post, $this->secret); + // {3,16} because shortest hash algorithm name is 3 characters (md2,md4,md5) and longest + // is currently 11 characters, but we'll leave some margin in the end... + if (preg_match('/^([0-9a-zA-Z\-\,]{3,16})=([0-9a-fA-F]+)$/', $hmac, $matches)) { + $hash_algo = strtolower($matches[1]); + $their_hmac = strtolower($matches[2]); + common_debug(sprintf(__METHOD__ . ': PuSH from feed %s uses HMAC algorithm %s with value: %s', _ve($this->getUri()), _ve($hash_algo), _ve($their_hmac))); + + if (!in_array($hash_algo, hash_algos())) { + // We can't handle this at all, PHP doesn't recognize the algorithm name ('md5', 'sha1', 'sha256' etc: https://secure.php.net/manual/en/function.hash-algos.php) + common_log(LOG_ERR, sprintf(__METHOD__.': HMAC algorithm %s unsupported, not found in PHP hash_algos()', _ve($hash_algo))); + return false; + } elseif (!is_null(common_config('security', 'hash_algos')) && !in_array($hash_algo, common_config('security', 'hash_algos'))) { + // We _won't_ handle this because there is a list of accepted hash algorithms and this one is not in it. + common_log(LOG_ERR, sprintf(__METHOD__.': Whitelist for HMAC algorithms exist, but %s is not included.', _ve($hash_algo))); + return false; + } + + $our_hmac = hash_hmac($hash_algo, $post, $this->secret); if ($their_hmac === $our_hmac) { return true; } - if (common_config('feedsub', 'debug')) { - $tempfile = tempnam(sys_get_temp_dir(), 'feedsub-receive'); - if ($tempfile) { - file_put_contents($tempfile, $post); - } - common_log(LOG_ERR, __METHOD__ . ": ignoring PuSH with bad SHA-1 HMAC: got $their_hmac, expected $our_hmac for feed " . $this->getUri() . " on $this->huburi; saved to $tempfile"); - } else { - common_log(LOG_ERR, __METHOD__ . ": ignoring PuSH with bad SHA-1 HMAC: got $their_hmac, expected $our_hmac for feed " . $this->getUri() . " on $this->huburi"); - } + common_log(LOG_ERR, sprintf(__METHOD__.': ignoring PuSH with bad HMAC hash: got %s, expected %s for feed %s from hub %s', _ve($their_hmac), _ve($our_hmac), _ve($this->getUri()), _ve($this->huburi))); } else { - common_log(LOG_ERR, __METHOD__ . ": ignoring PuSH with bogus HMAC '$hmac'"); + common_log(LOG_ERR, sprintf(__METHOD__.': ignoring PuSH with bogus HMAC==', _ve($hmac))); } } else { if (empty($hmac)) { return true; } else { - common_log(LOG_ERR, __METHOD__ . ": ignoring PuSH with unexpected HMAC '$hmac'"); + common_log(LOG_ERR, sprintf(__METHOD__.': ignoring PuSH with unexpected HMAC==%s', _ve($hmac))); } } return false;