summaryrefslogtreecommitdiffstats
path: root/lib/puppet/ssl/certificate_revocation_list.rb
blob: 293f4b8c0ec16875d651ea41bab21fcdb0abf1f0 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
require 'puppet/ssl/base'
require 'puppet/indirector'

# Manage the CRL.
class Puppet::SSL::CertificateRevocationList < Puppet::SSL::Base
  wraps OpenSSL::X509::CRL

  extend Puppet::Indirector
  indirects :certificate_revocation_list, :terminus_class => :file

  # Convert a string into an instance.
  def self.from_s(string)
    instance = wrapped_class.new(string)
    result = new('foo') # The name doesn't matter
    result.content = instance
    result
  end

  # Because of how the format handler class is included, this
  # can't be in the base class.
  def self.supported_formats
    [:s]
  end

  # Knows how to create a CRL with our system defaults.
  def generate(cert, cakey)
    Puppet.info "Creating a new certificate revocation list"
    @content = wrapped_class.new
    @content.issuer = cert.subject
    @content.version = 1

    # Init the CRL number.
    crlNum = OpenSSL::ASN1::Integer(0)
    @content.extensions = [OpenSSL::X509::Extension.new("crlNumber", crlNum)]

    # Set last/next update
    @content.last_update = Time.now
    # Keep CRL valid for 5 years
    @content.next_update = Time.now + 5 * 365*24*60*60

    @content.sign(cakey, OpenSSL::Digest::SHA1.new)

    @content
  end

  # The name doesn't actually matter; there's only one CRL.
  # We just need the name so our Indirector stuff all works more easily.
  def initialize(fakename)
    @name = "crl"
  end

  # Revoke the certificate with serial number SERIAL issued by this
  # CA, then write the CRL back to disk. The REASON must be one of the
  # OpenSSL::OCSP::REVOKED_* reasons
  def revoke(serial, cakey, reason = OpenSSL::OCSP::REVOKED_STATUS_KEYCOMPROMISE)
    Puppet.notice "Revoked certificate with serial #{serial}"
    time = Time.now

    # Add our revocation to the CRL.
    revoked = OpenSSL::X509::Revoked.new
    revoked.serial = serial
    revoked.time = time
    enum = OpenSSL::ASN1::Enumerated(reason)
    ext = OpenSSL::X509::Extension.new("CRLReason", enum)
    revoked.add_extension(ext)
    @content.add_revoked(revoked)

    # Increment the crlNumber
    e = @content.extensions.find { |e| e.oid == 'crlNumber' }
    ext = @content.extensions.reject { |e| e.oid == 'crlNumber' }
    crlNum = OpenSSL::ASN1::Integer(e ? e.value.to_i + 1 : 0)
    ext << OpenSSL::X509::Extension.new("crlNumber", crlNum)
    @content.extensions = ext

    # Set last/next update
    @content.last_update = time
    # Keep CRL valid for 5 years
    @content.next_update = time + 5 * 365*24*60*60

    @content.sign(cakey, OpenSSL::Digest::SHA1.new)

    Puppet::SSL::CertificateRevocationList.indirection.save(self)
  end
end