Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2020-03-25 00:07:54 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2020-03-25 00:07:54 +0300
commitc4db541c1b2c97ab1eda354ea3899489fe5c33e5 (patch)
tree45d5d381232179082ea11136e3b53211b37349d5 /lib/gitlab/x509
parent603c7d4cac5e28bc1c75e50c23ed2cbe56f1aafc (diff)
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'lib/gitlab/x509')
-rw-r--r--lib/gitlab/x509/commit.rb166
-rw-r--r--lib/gitlab/x509/signature.rb198
2 files changed, 205 insertions, 159 deletions
diff --git a/lib/gitlab/x509/commit.rb b/lib/gitlab/x509/commit.rb
index 4b35c0ef7d2..91951a3e505 100644
--- a/lib/gitlab/x509/commit.rb
+++ b/lib/gitlab/x509/commit.rb
@@ -31,175 +31,23 @@ module Gitlab
end
end
- def verified_signature
- strong_memoize(:verified_signature) { verified_signature? }
- end
-
- def cert
- strong_memoize(:cert) do
- signer_certificate(p7) if valid_signature?
- end
- end
-
- def cert_store
- strong_memoize(:cert_store) do
- store = OpenSSL::X509::Store.new
- store.set_default_paths
- # valid_signing_time? checks the time attributes already
- # this flag is required, otherwise expired certificates would become
- # unverified when notAfter within certificate attribute is reached
- store.flags = OpenSSL::X509::V_FLAG_NO_CHECK_TIME
- store
- end
- end
-
- def p7
- strong_memoize(:p7) do
- pkcs7_text = signature_text.sub('-----BEGIN SIGNED MESSAGE-----', '-----BEGIN PKCS7-----')
- pkcs7_text = pkcs7_text.sub('-----END SIGNED MESSAGE-----', '-----END PKCS7-----')
-
- OpenSSL::PKCS7.new(pkcs7_text)
- rescue
- nil
- end
- end
-
- def valid_signing_time?
- # rfc 5280 - 4.1.2.5 Validity
- # check if signed_time is within the time range (notBefore/notAfter)
- # non-rfc - git specific check: signed_time >= commit_time
- p7.signers[0].signed_time.between?(cert.not_before, cert.not_after) &&
- p7.signers[0].signed_time >= @commit.created_at
- end
-
- def valid_signature?
- p7.verify([], cert_store, signed_text, OpenSSL::PKCS7::NOVERIFY)
- rescue
- nil
- end
-
- def verified_signature?
- # verify has multiple options but only a boolean return value
- # so first verify without certificate chain
- if valid_signature?
- if valid_signing_time?
- # verify with system certificate chain
- p7.verify([], cert_store, signed_text)
- else
- false
- end
- else
- nil
- end
- rescue
- nil
- end
-
- def signer_certificate(p7)
- p7.certificates.each do |cert|
- next if cert.serial != p7.signers[0].serial
-
- return cert
- end
- end
-
- def certificate_crl
- extension = get_certificate_extension('crlDistributionPoints')
- crl_url = nil
-
- extension.each_line do |line|
- break if crl_url
-
- line.split('URI:').each do |item|
- item.strip
-
- if item.start_with?("http")
- crl_url = item.strip
- break
- end
- end
- end
-
- crl_url
- end
-
- def get_certificate_extension(extension)
- cert.extensions.each do |ext|
- if ext.oid == extension
- return ext.value
- end
- end
- end
-
- def issuer_subject_key_identifier
- get_certificate_extension('authorityKeyIdentifier').gsub("keyid:", "").delete!("\n")
- end
-
- def certificate_subject_key_identifier
- get_certificate_extension('subjectKeyIdentifier')
- end
-
- def certificate_issuer
- cert.issuer.to_s(OpenSSL::X509::Name::RFC2253)
- end
-
- def certificate_subject
- cert.subject.to_s(OpenSSL::X509::Name::RFC2253)
- end
-
- def certificate_email
- get_certificate_extension('subjectAltName').split('email:')[1]
- end
-
- def issuer_attributes
- return if verified_signature.nil?
-
- {
- subject_key_identifier: issuer_subject_key_identifier,
- subject: certificate_issuer,
- crl_url: certificate_crl
- }
- end
-
- def certificate_attributes
- return if verified_signature.nil?
-
- issuer = X509Issuer.safe_create!(issuer_attributes)
-
- {
- subject_key_identifier: certificate_subject_key_identifier,
- subject: certificate_subject,
- email: certificate_email,
- serial_number: cert.serial,
- x509_issuer_id: issuer.id
- }
- end
-
def attributes
- return if verified_signature.nil?
+ return if @commit.sha.nil? || @commit.project.nil?
- certificate = X509Certificate.safe_create!(certificate_attributes)
+ signature = X509::Signature.new(signature_text, signed_text, @commit.committer_email, @commit.created_at)
+
+ return if signature.verified_signature.nil? || signature.x509_certificate.nil?
{
commit_sha: @commit.sha,
project: @commit.project,
- x509_certificate_id: certificate.id,
- verification_status: verification_status(certificate)
+ x509_certificate_id: signature.x509_certificate.id,
+ verification_status: signature.verification_status
}
end
- def verification_status(certificate)
- return :unverified if certificate.revoked?
-
- if verified_signature && certificate_email == @commit.committer_email
- :verified
- else
- :unverified
- end
- end
-
def create_cached_signature!
- return if verified_signature.nil?
+ return if attributes.nil?
return X509CommitSignature.new(attributes) if Gitlab::Database.read_only?
diff --git a/lib/gitlab/x509/signature.rb b/lib/gitlab/x509/signature.rb
new file mode 100644
index 00000000000..ed248e29211
--- /dev/null
+++ b/lib/gitlab/x509/signature.rb
@@ -0,0 +1,198 @@
+# frozen_string_literal: true
+require 'openssl'
+require 'digest'
+
+module Gitlab
+ module X509
+ class Signature
+ include Gitlab::Utils::StrongMemoize
+
+ attr_reader :signature_text, :signed_text, :created_at
+
+ def initialize(signature_text, signed_text, email, created_at)
+ @signature_text = signature_text
+ @signed_text = signed_text
+ @email = email
+ @created_at = created_at
+ end
+
+ def x509_certificate
+ return if certificate_attributes.nil?
+
+ X509Certificate.safe_create!(certificate_attributes) unless verified_signature.nil?
+ end
+
+ def verified_signature
+ strong_memoize(:verified_signature) { verified_signature? }
+ end
+
+ def verification_status
+ return :unverified if x509_certificate.nil? || x509_certificate.revoked?
+
+ if verified_signature && certificate_email == @email
+ :verified
+ else
+ :unverified
+ end
+ end
+
+ private
+
+ def cert
+ strong_memoize(:cert) do
+ signer_certificate(p7) if valid_signature?
+ end
+ end
+
+ def cert_store
+ strong_memoize(:cert_store) do
+ store = OpenSSL::X509::Store.new
+ store.set_default_paths
+ # valid_signing_time? checks the time attributes already
+ # this flag is required, otherwise expired certificates would become
+ # unverified when notAfter within certificate attribute is reached
+ store.flags = OpenSSL::X509::V_FLAG_NO_CHECK_TIME
+ store
+ end
+ end
+
+ def p7
+ strong_memoize(:p7) do
+ pkcs7_text = signature_text.sub('-----BEGIN SIGNED MESSAGE-----', '-----BEGIN PKCS7-----')
+ pkcs7_text = pkcs7_text.sub('-----END SIGNED MESSAGE-----', '-----END PKCS7-----')
+
+ OpenSSL::PKCS7.new(pkcs7_text)
+ rescue
+ nil
+ end
+ end
+
+ def valid_signing_time?
+ # rfc 5280 - 4.1.2.5 Validity
+ # check if signed_time is within the time range (notBefore/notAfter)
+ # non-rfc - git specific check: signed_time >= commit_time
+ p7.signers[0].signed_time.between?(cert.not_before, cert.not_after) &&
+ p7.signers[0].signed_time >= created_at
+ end
+
+ def valid_signature?
+ p7.verify([], cert_store, signed_text, OpenSSL::PKCS7::NOVERIFY)
+ rescue
+ nil
+ end
+
+ def verified_signature?
+ # verify has multiple options but only a boolean return value
+ # so first verify without certificate chain
+ if valid_signature?
+ if valid_signing_time?
+ # verify with system certificate chain
+ p7.verify([], cert_store, signed_text)
+ else
+ false
+ end
+ else
+ nil
+ end
+ rescue
+ nil
+ end
+
+ def signer_certificate(p7)
+ p7.certificates.each do |cert|
+ next if cert.serial != p7.signers[0].serial
+
+ return cert
+ end
+ end
+
+ def certificate_crl
+ extension = get_certificate_extension('crlDistributionPoints')
+ return if extension.nil?
+
+ crl_url = nil
+
+ extension.each_line do |line|
+ break if crl_url
+
+ line.split('URI:').each do |item|
+ item.strip
+
+ if item.start_with?("http")
+ crl_url = item.strip
+ break
+ end
+ end
+ end
+
+ crl_url
+ end
+
+ def get_certificate_extension(extension)
+ ext = cert.extensions.detect { |ext| ext.oid == extension }
+ ext&.value
+ end
+
+ def issuer_subject_key_identifier
+ key_identifier = get_certificate_extension('authorityKeyIdentifier')
+ return if key_identifier.nil?
+
+ key_identifier.gsub("keyid:", "").delete!("\n")
+ end
+
+ def certificate_subject_key_identifier
+ key_identifier = get_certificate_extension('subjectKeyIdentifier')
+ return if key_identifier.nil?
+
+ key_identifier
+ end
+
+ def certificate_issuer
+ cert.issuer.to_s(OpenSSL::X509::Name::RFC2253)
+ end
+
+ def certificate_subject
+ cert.subject.to_s(OpenSSL::X509::Name::RFC2253)
+ end
+
+ def certificate_email
+ email = nil
+
+ get_certificate_extension('subjectAltName').split(',').each do |item|
+ if item.strip.start_with?("email")
+ email = item.split('email:')[1]
+ break
+ end
+ end
+
+ return if email.nil?
+
+ email
+ end
+
+ def x509_issuer
+ return if verified_signature.nil? || issuer_subject_key_identifier.nil? || certificate_crl.nil?
+
+ attributes = {
+ subject_key_identifier: issuer_subject_key_identifier,
+ subject: certificate_issuer,
+ crl_url: certificate_crl
+ }
+
+ X509Issuer.safe_create!(attributes) unless verified_signature.nil?
+ end
+
+ def certificate_attributes
+ return if verified_signature.nil? || certificate_subject_key_identifier.nil? || x509_issuer.nil?
+
+ {
+ subject_key_identifier: certificate_subject_key_identifier,
+ subject: certificate_subject,
+ email: certificate_email,
+ serial_number: cert.serial.to_i,
+ x509_issuer_id: x509_issuer.id
+ }
+ end
+ end
+ end
+end