Gracefully handle unknown/invalid GPG keys

An unknown public GPG key will result in a GPGME::Error thrown from gpg,
which would cause an Error 500 on the signatures endpoint.

Closes https://gitlab.com/gitlab-org/gitlab-ce/issues/54729
This commit is contained in:
Stan Hu 2018-11-30 23:20:00 -08:00
parent cfe484795d
commit 184240e86a
2 changed files with 39 additions and 7 deletions

View file

@ -44,9 +44,8 @@ module Gitlab
def update_signature!(cached_signature) def update_signature!(cached_signature)
using_keychain do |gpg_key| using_keychain do |gpg_key|
cached_signature.update!(attributes(gpg_key)) cached_signature.update!(attributes(gpg_key))
@signature = cached_signature
end end
@signature = cached_signature
end end
private private
@ -59,11 +58,15 @@ module Gitlab
# the proper signature. # the proper signature.
# NOTE: the invoked method is #fingerprint but it's only returning # NOTE: the invoked method is #fingerprint but it's only returning
# 16 characters (the format used by keyid) instead of 40. # 16 characters (the format used by keyid) instead of 40.
gpg_key = find_gpg_key(verified_signature.fingerprint) fingerprint = verified_signature&.fingerprint
break unless fingerprint
gpg_key = find_gpg_key(fingerprint)
if gpg_key if gpg_key
Gitlab::Gpg::CurrentKeyChain.add(gpg_key.key) Gitlab::Gpg::CurrentKeyChain.add(gpg_key.key)
@verified_signature = nil clear_memoization(:verified_signature)
end end
yield gpg_key yield gpg_key
@ -71,9 +74,16 @@ module Gitlab
end end
def verified_signature def verified_signature
@verified_signature ||= GPGME::Crypto.new.verify(signature_text, signed_text: signed_text) do |verified_signature| strong_memoize(:verified_signature) { gpgme_signature }
end
def gpgme_signature
GPGME::Crypto.new.verify(signature_text, signed_text: signed_text) do |verified_signature|
# Return the first signature for now: https://gitlab.com/gitlab-org/gitlab-ce/issues/54932
break verified_signature break verified_signature
end end
rescue GPGME::Error
nil
end end
def create_cached_signature! def create_cached_signature!
@ -92,7 +102,7 @@ module Gitlab
commit_sha: @commit.sha, commit_sha: @commit.sha,
project: @commit.project, project: @commit.project,
gpg_key: gpg_key, gpg_key: gpg_key,
gpg_key_primary_keyid: gpg_key&.keyid || verified_signature.fingerprint, gpg_key_primary_keyid: gpg_key&.keyid || verified_signature&.fingerprint,
gpg_key_user_name: user_infos[:name], gpg_key_user_name: user_infos[:name],
gpg_key_user_email: user_infos[:email], gpg_key_user_email: user_infos[:email],
verification_status: verification_status verification_status: verification_status
@ -102,7 +112,7 @@ module Gitlab
def verification_status(gpg_key) def verification_status(gpg_key)
return :unknown_key unless gpg_key return :unknown_key unless gpg_key
return :unverified_key unless gpg_key.verified? return :unverified_key unless gpg_key.verified?
return :unverified unless verified_signature.valid? return :unverified unless verified_signature&.valid?
if gpg_key.verified_and_belongs_to_email?(@commit.committer_email) if gpg_key.verified_and_belongs_to_email?(@commit.committer_email)
:verified :verified

View file

@ -26,6 +26,28 @@ describe Gitlab::Gpg::Commit do
end end
end end
context 'invalid signature' do
let!(:commit) { create :commit, project: project, sha: commit_sha, committer_email: GpgHelpers::User1.emails.first }
let!(:user) { create(:user, email: GpgHelpers::User1.emails.first) }
before do
allow(Gitlab::Git::Commit).to receive(:extract_signature_lazily)
.with(Gitlab::Git::Repository, commit_sha)
.and_return(
[
# Corrupt the key
GpgHelpers::User1.signed_commit_signature.tr('=', 'a'),
GpgHelpers::User1.signed_commit_base_data
]
)
end
it 'returns nil' do
expect(described_class.new(commit).signature).to be_nil
end
end
context 'known key' do context 'known key' do
context 'user matches the key uid' do context 'user matches the key uid' do
context 'user email matches the email committer' do context 'user email matches the email committer' do