puppet 2.6.11 → 2.6.12

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of puppet might be problematic. Click here for more details.

Files changed (67) hide show
  1. data/CHANGELOG +33 -0
  2. data/conf/redhat/puppet.spec +7 -4
  3. data/lib/puppet.rb +1 -1
  4. data/lib/puppet/application/cert.rb +17 -3
  5. data/lib/puppet/application/kick.rb +0 -2
  6. data/lib/puppet/defaults.rb +52 -3
  7. data/lib/puppet/network/handler/ca.rb +16 -106
  8. data/lib/puppet/network/handler/master.rb +0 -3
  9. data/lib/puppet/network/handler/runner.rb +1 -0
  10. data/lib/puppet/ssl/certificate.rb +6 -0
  11. data/lib/puppet/ssl/certificate_authority.rb +86 -11
  12. data/lib/puppet/ssl/certificate_authority/interface.rb +64 -19
  13. data/lib/puppet/ssl/certificate_factory.rb +112 -91
  14. data/lib/puppet/ssl/certificate_request.rb +88 -1
  15. data/lib/puppet/ssl/host.rb +16 -3
  16. data/lib/puppet/type/file.rb +0 -1
  17. data/lib/puppet/util/command_line/puppetca +23 -2
  18. data/lib/puppet/util/monkey_patches.rb +69 -0
  19. data/lib/puppet/util/settings.rb +5 -0
  20. data/spec/integration/defaults_spec.rb +11 -0
  21. data/spec/integration/network/handler_spec.rb +1 -1
  22. data/spec/unit/configurer_spec.rb +2 -2
  23. data/spec/unit/network/handler/ca_spec.rb +86 -0
  24. data/spec/unit/ssl/certificate_authority/interface_spec.rb +92 -53
  25. data/spec/unit/ssl/certificate_authority_spec.rb +133 -23
  26. data/spec/unit/ssl/certificate_factory_spec.rb +90 -70
  27. data/spec/unit/ssl/certificate_request_spec.rb +62 -1
  28. data/spec/unit/ssl/certificate_spec.rb +31 -0
  29. data/spec/unit/ssl/host_spec.rb +44 -2
  30. data/spec/unit/util/settings_spec.rb +10 -0
  31. data/test/language/functions.rb +0 -1
  32. data/test/language/snippets.rb +0 -9
  33. data/test/lib/puppettest/exetest.rb +1 -1
  34. data/test/lib/puppettest/servertest.rb +0 -1
  35. data/test/rails/rails.rb +0 -1
  36. data/test/ral/type/filesources.rb +0 -60
  37. metadata +5 -34
  38. data/lib/puppet/network/client.rb +0 -179
  39. data/lib/puppet/network/client/ca.rb +0 -56
  40. data/lib/puppet/network/client/file.rb +0 -6
  41. data/lib/puppet/network/client/proxy.rb +0 -27
  42. data/lib/puppet/network/client/report.rb +0 -26
  43. data/lib/puppet/network/client/runner.rb +0 -10
  44. data/lib/puppet/network/client/status.rb +0 -4
  45. data/lib/puppet/network/http_server.rb +0 -3
  46. data/lib/puppet/network/http_server/mongrel.rb +0 -150
  47. data/lib/puppet/network/http_server/webrick.rb +0 -155
  48. data/lib/puppet/network/xmlrpc/client.rb +0 -211
  49. data/lib/puppet/sslcertificates.rb +0 -146
  50. data/lib/puppet/sslcertificates/ca.rb +0 -375
  51. data/lib/puppet/sslcertificates/certificate.rb +0 -255
  52. data/lib/puppet/sslcertificates/inventory.rb +0 -38
  53. data/lib/puppet/sslcertificates/monkey_patch.rb +0 -6
  54. data/lib/puppet/sslcertificates/support.rb +0 -146
  55. data/spec/integration/network/client_spec.rb +0 -19
  56. data/spec/unit/network/client_spec.rb +0 -45
  57. data/spec/unit/network/xmlrpc/client_spec.rb +0 -172
  58. data/spec/unit/sslcertificates/ca_spec.rb +0 -110
  59. data/test/certmgr/certmgr.rb +0 -308
  60. data/test/certmgr/inventory.rb +0 -69
  61. data/test/certmgr/support.rb +0 -105
  62. data/test/network/client/ca.rb +0 -69
  63. data/test/network/client/dipper.rb +0 -34
  64. data/test/network/handler/ca.rb +0 -273
  65. data/test/network/server/mongrel_test.rb +0 -99
  66. data/test/network/server/webrick.rb +0 -128
  67. data/test/network/xmlrpc/client.rb +0 -45
data/CHANGELOG CHANGED
@@ -1,3 +1,36 @@
1
+ 2.6.12 (CVE-2011-3872 see http://puppetlabs.com/security/hotfixes/cve-2011-3872/)
2
+ ===
3
+ 3ed6499 Backport Enumerable#count to Rubies < 1.8.7
4
+ 5f44c23 More 1.8.5 compatibility fixes.
5
+ ef1b960 Better 1.8.5 compatible implementation of `lines`.
6
+ 246e875 (#2848) Config options require '_', not '-'.
7
+ 3bdeb3a Ruby 1.8.5 compatibility changes in tests and code.
8
+ 6866d4b Add `lines` alias for `each_line` in Ruby 1.8.5.
9
+ 2f9ec3c s/not_to/should_not/ for older versions of RSpec 2.
10
+ 56320ea (#2848) Eliminate redundant `master_dns_alt_names`.
11
+ de19861 (#2848) Remove the legacy SSLCertificates code
12
+ cf008a6 (#2848) Rework the xmlrpc CA handler to use the modern SSL code
13
+ 32be180 (#2848) Remove unused xmlrpc code
14
+ 5f2a44d (#2848) Consistent return values from `subject_alt_names` accessors.
15
+ 5e507f2 (#2848) Consistently use `subject_alt_names` as accessor name.
16
+ 5ac2417 (#2848) Don't strip the subjectAltName label when listing.
17
+ 44cf3a2 (#2848) Don't enable `emailProtection` for server keys.
18
+ d66def9 (#2848) Only mark `subjectAltName` critical if `subject` is empty.
19
+ 8174047 (#2848) Migrate `dns-alt-names` back to settings.
20
+ f18df2b Wire up the `setbycli` slot in Puppet settings.
21
+ efa61f2 (#2848) rename subject-alt-name option to dns-alt-names
22
+ f103b20 (#2848) Rename `certdnsnames` to match new behaviour.
23
+ 363b47b (#2848) Use `certdnsnames` when bootstrapping a local master.
24
+ 49334ff (#2848) CSR subjectAltNames handling while signing.
25
+ 5f2af93 (#2848) List subject alt names in output of puppet cert --list
26
+ bb475ec (#7224) Add a helper to Puppet::SSL::Certificate to retrieve alternate names
27
+ bab9310 (#2848) Rewrite SSL Certificate Factory, fixing `subjectAltName` leak.
28
+ fca1ff0 (#2848) Reject unknown (== all) extensions on the CSR.
29
+ 443a756 (#2848) extract the subjectAltName value from the CSR.
30
+ 66101f1 (#2848) Set `certdnsnames` values into the CSR.
31
+ 77b814f (#6928) Don't blow up when the method is undefined...
32
+ 5427f1e (#6928) backport Symbol#to_proc for Ruby < 1.8.7
33
+
1
34
  2.6.11
2
35
  ===
3
36
  e158b26 (#9793) "secure" indirector file backed terminus base class.
@@ -5,13 +5,13 @@
5
5
  %global confdir conf/redhat
6
6
 
7
7
  Name: puppet
8
- Version: 2.6.11
8
+ Version: 2.6.12
9
9
  Release: 1%{?dist}
10
10
  Summary: A network tool for managing many disparate systems
11
11
  License: GPLv2
12
12
  URL: http://puppetlabs.com
13
- Source0: http://puppetlabs.com/downloads/%{name}/%{name}-%{version}rc1.tar.gz
14
- #Source1: http://puppetlabs.com/downloads/%{name}/%{name}-%{version}.tar.gz.asc
13
+ Source0: http://puppetlabs.com/downloads/%{name}/%{name}-%{version}.tar.gz
14
+ Source1: http://puppetlabs.com/downloads/%{name}/%{name}-%{version}.tar.gz.asc
15
15
 
16
16
  Group: System Environment/Base
17
17
 
@@ -65,7 +65,7 @@ Provides the central puppet server daemon which provides manifests to clients.
65
65
  The server can also function as a certificate authority and file server.
66
66
 
67
67
  %prep
68
- %setup -q -n %{name}-%{version}rc1
68
+ %setup -q -n %{name}-%{version}
69
69
  patch -s -p1 < conf/redhat/rundir-perms.patch
70
70
 
71
71
 
@@ -253,6 +253,9 @@ fi
253
253
  rm -rf %{buildroot}
254
254
 
255
255
  %changelog
256
+ * Fri Oct 21 2011 Michael Stahnke <stahnma@puppetlabs.com> - 2.6.12-1
257
+ - CVE-2011-3872 fixes
258
+
256
259
  * Fri Sep 30 2011 Michael Stahnke <stahnma@puppetlabs.com> - 2.6.11-1
257
260
  - CVE-2011-3869, 3870, 3871
258
261
 
@@ -24,7 +24,7 @@ require 'puppet/util/run_mode'
24
24
  # it's also a place to find top-level commands like 'debug'
25
25
 
26
26
  module Puppet
27
- PUPPETVERSION = '2.6.11'
27
+ PUPPETVERSION = '2.6.12'
28
28
 
29
29
  def Puppet.version
30
30
  PUPPETVERSION
@@ -10,6 +10,7 @@ class Puppet::Application::Cert < Puppet::Application
10
10
  def subcommand
11
11
  @subcommand
12
12
  end
13
+
13
14
  def subcommand=(name)
14
15
  # Handle the nasty, legacy mapping of "clean" to "destroy".
15
16
  sub = name.to_sym
@@ -38,11 +39,15 @@ class Puppet::Application::Cert < Puppet::Application
38
39
 
39
40
  require 'puppet/ssl/certificate_authority/interface'
40
41
  Puppet::SSL::CertificateAuthority::Interface::INTERFACE_METHODS.reject {|m| m == :destroy }.each do |method|
41
- option("--#{method}", "-#{method.to_s[0,1]}") do
42
+ option("--#{method.to_s.gsub('_','-')}", "-#{method.to_s[0,1]}") do
42
43
  self.subcommand = method
43
44
  end
44
45
  end
45
46
 
47
+ option("--[no-]allow-dns-alt-names") do |value|
48
+ options[:allow_dns_alt_names] = value
49
+ end
50
+
46
51
  option("--verbose", "-v") do
47
52
  Puppet::Util::Log.level = :info
48
53
  end
@@ -56,8 +61,8 @@ class Puppet::Application::Cert < Puppet::Application
56
61
  hosts = command_line.args.collect { |h| h.downcase }
57
62
  end
58
63
  begin
59
- @ca.apply(:revoke, :to => hosts) if subcommand == :destroy
60
- @ca.apply(subcommand, :to => hosts, :digest => @digest)
64
+ @ca.apply(:revoke, options.merge(:to => hosts)) if subcommand == :destroy
65
+ @ca.apply(subcommand, options.merge(:to => hosts, :digest => @digest))
61
66
  rescue => detail
62
67
  puts detail.backtrace if Puppet[:trace]
63
68
  puts detail.to_s
@@ -77,6 +82,15 @@ class Puppet::Application::Cert < Puppet::Application
77
82
  Puppet::SSL::Host.ca_location = :only
78
83
  end
79
84
 
85
+ # If we are generating, and the option came from the CLI, it gets added to
86
+ # the data. This will do the right thing for non-local certificates, in
87
+ # that the command line but *NOT* the config file option will apply.
88
+ if subcommand == :generate
89
+ if Puppet.settings.setting(:dns_alt_names).setbycli
90
+ options[:dns_alt_names] = Puppet[:dns_alt_names]
91
+ end
92
+ end
93
+
80
94
  begin
81
95
  @ca = Puppet::SSL::CertificateAuthority.new
82
96
  rescue => detail
@@ -48,8 +48,6 @@ class Puppet::Application::Kick < Puppet::Application
48
48
  end
49
49
 
50
50
  def main
51
- require 'puppet/network/client'
52
-
53
51
  Puppet.warning "Failed to load ruby LDAP library. LDAP functionality will not be available" unless Puppet.features.ldap?
54
52
  require 'puppet/util/ldap/connection'
55
53
 
@@ -205,9 +205,58 @@ module Puppet
205
205
  to the fully qualified domain name.",
206
206
  :call_on_define => true, # Call our hook with the default value, so we're always downcased
207
207
  :hook => proc { |value| raise(ArgumentError, "Certificate names must be lower case; see #1168") unless value == value.downcase }},
208
- :certdnsnames => ['', "The DNS names on the Server certificate as a colon-separated list.
209
- If it's anything other than an empty string, it will be used as an alias in the created
210
- certificate. By default, only the server gets an alias set up, and only for 'puppet'."],
208
+ :certdnsnames => {
209
+ :default => '',
210
+ :hook => proc do |value|
211
+ unless value.nil? or value == '' then
212
+ Puppet.warning <<WARN
213
+ The `certdnsnames` setting is no longer functional,
214
+ after CVE-2011-3872. We ignore the value completely.
215
+
216
+ For your own certificate request you can set `dns_alt_names` in the
217
+ configuration and it will apply locally. There is no configuration option to
218
+ set DNS alt names, or any other `subjectAltName` value, for another nodes
219
+ certificate.
220
+
221
+ Alternately you can use the `--dns_alt_names` command line option to set the
222
+ labels added while generating your own CSR.
223
+ WARN
224
+ end
225
+ end,
226
+ :desc => <<EOT
227
+ The `certdnsnames` setting is no longer functional,
228
+ after CVE-2011-3872. We ignore the value completely.
229
+
230
+ For your own certificate request you can set `dns_alt_names` in the
231
+ configuration and it will apply locally. There is no configuration option to
232
+ set DNS alt names, or any other `subjectAltName` value, for another nodes
233
+ certificate.
234
+
235
+ Alternately you can use the `--dns_alt_names` command line option to set the
236
+ labels added while generating your own CSR.
237
+ EOT
238
+ },
239
+ :dns_alt_names => {
240
+ :default => '',
241
+ :desc => <<EOT,
242
+ The comma-separated list of alternative DNS names to use for the local host.
243
+
244
+ When the node generates a CSR for itself, these are added to the request
245
+ as the desired `subjectAltName` in the certificate: additional DNS labels
246
+ that the certificate is also valid answering as.
247
+
248
+ This is generally required if you use a non-hostname `certname`, or if you
249
+ want to use `puppet kick` or `puppet resource -H` and the primary certname
250
+ does not match the DNS name you use to communicate with the host.
251
+
252
+ This is unnecessary for agents, unless you intend to use them as a server for
253
+ `puppet kick` or remote `puppet resource` management.
254
+
255
+ It is rarely necessary for servers; it is usually helpful only if you need to
256
+ have a pool of multiple load balanced masters, or for the same master to
257
+ respond on two physically separate networks under different names.
258
+ EOT
259
+ },
211
260
  :certdir => {
212
261
  :default => "$ssldir/certs",
213
262
  :owner => "service",
@@ -1,10 +1,7 @@
1
1
  require 'openssl'
2
2
  require 'puppet'
3
- require 'puppet/sslcertificates'
4
3
  require 'xmlrpc/server'
5
-
6
- # Much of this was taken from QuickCert:
7
- # http://segment7.net/projects/ruby/QuickCert/
4
+ require 'puppet/network/handler'
8
5
 
9
6
  class Puppet::Network::Handler
10
7
  class CA < Handler
@@ -18,73 +15,17 @@ class Puppet::Network::Handler
18
15
  iface.add_method("array getcert(csr)")
19
16
  }
20
17
 
21
- def autosign
22
- if defined?(@autosign)
23
- @autosign
24
- else
25
- Puppet[:autosign]
26
- end
27
- end
28
-
29
- # FIXME autosign? should probably accept both hostnames and IP addresses
30
- def autosign?(hostname)
31
- # simple values are easy
32
- if autosign == true or autosign == false
33
- return autosign
34
- end
35
-
36
- # we only otherwise know how to handle files
37
- unless autosign =~ /^\//
38
- raise Puppet::Error, "Invalid autosign value #{autosign.inspect}"
39
- end
40
-
41
- unless FileTest.exists?(autosign)
42
- unless defined?(@@warnedonautosign)
43
- @@warnedonautosign = true
44
- Puppet.info "Autosign is enabled but #{autosign} is missing"
45
- end
46
- return false
47
- end
48
- auth = Puppet::Network::AuthStore.new
49
- File.open(autosign) { |f|
50
- f.each { |line|
51
- next if line =~ /^\s*#/
52
- next if line =~ /^\s*$/
53
- auth.allow(line.chomp)
54
- }
55
- }
56
-
57
- # for now, just cheat and pass a fake IP address to allowed?
58
- auth.allowed?(hostname, "127.1.1.1")
59
- end
60
-
61
18
  def initialize(hash = {})
62
19
  Puppet.settings.use(:main, :ssl, :ca)
63
- @autosign = hash[:autosign] if hash.include? :autosign
64
20
 
65
- @ca = Puppet::SSLCertificates::CA.new(hash)
21
+ @ca = Puppet::SSL::CertificateAuthority.instance
66
22
  end
67
23
 
68
24
  # our client sends us a csr, and we either store it for later signing,
69
25
  # or we sign it right away
70
26
  def getcert(csrtext, client = nil, clientip = nil)
71
- csr = OpenSSL::X509::Request.new(csrtext)
72
-
73
- # Use the hostname from the CSR, not from the network.
74
- subject = csr.subject
75
-
76
- nameary = subject.to_a.find { |ary|
77
- ary[0] == "CN"
78
- }
79
-
80
- if nameary.nil?
81
- Puppet.err(
82
- "Invalid certificate request: could not retrieve server name"
83
- )
84
- return "invalid"
85
- end
86
-
87
- hostname = nameary[1]
27
+ csr = Puppet::SSL::CertificateRequest.from_s(csrtext)
28
+ hostname = csr.name
88
29
 
89
30
  unless @ca
90
31
  Puppet.notice "Host #{hostname} asked for signing from non-CA master"
@@ -93,57 +34,26 @@ class Puppet::Network::Handler
93
34
 
94
35
  # We used to save the public key, but it's basically unnecessary
95
36
  # and it mucks with the permissions requirements.
96
- # save_pk(hostname, csr.public_key)
97
-
98
- certfile = File.join(Puppet[:certdir], [hostname, "pem"].join("."))
99
37
 
100
38
  # first check to see if we already have a signed cert for the host
101
- cert, cacert = ca.getclientcert(hostname)
102
- if cert and cacert
39
+ cert = Puppet::SSL::Certificate.find(hostname)
40
+ cacert = Puppet::SSL::Certificate.find(@ca.host.name)
41
+
42
+ if cert
103
43
  Puppet.info "Retrieving existing certificate for #{hostname}"
104
- unless csr.public_key.to_s == cert.public_key.to_s
44
+ unless csr.content.public_key.to_s == cert.content.public_key.to_s
105
45
  raise Puppet::Error, "Certificate request does not match existing certificate; run 'puppetca --clean #{hostname}'."
106
46
  end
107
- return [cert.to_pem, cacert.to_pem]
108
- elsif @ca
109
- if self.autosign?(hostname) or client.nil?
110
- Puppet.info "Signing certificate for CA server" if client.nil?
111
- # okay, we don't have a signed cert
112
- # if we're a CA and autosign is turned on, then go ahead and sign
113
- # the csr and return the results
114
- Puppet.info "Signing certificate for #{hostname}"
115
- cert, cacert = @ca.sign(csr)
116
- #Puppet.info "Cert: #{cert.class}; Cacert: #{cacert.class}"
117
- return [cert.to_pem, cacert.to_pem]
118
- else # just write out the csr for later signing
119
- if @ca.getclientcsr(hostname)
120
- Puppet.info "Not replacing existing request from #{hostname}"
121
- else
122
- Puppet.notice "Host #{hostname} has a waiting certificate request"
123
- @ca.storeclientcsr(csr)
124
- end
125
- return ["", ""]
126
- end
47
+ [cert.to_s, cacert.to_s]
127
48
  else
128
- raise "huh?"
129
- end
130
- end
49
+ csr.save
131
50
 
132
- private
133
-
134
- # Save the public key.
135
- def save_pk(hostname, public_key)
136
- pkeyfile = File.join(Puppet[:publickeydir], [hostname, "pem"].join('.'))
137
-
138
- if FileTest.exists?(pkeyfile)
139
- currentkey = File.open(pkeyfile) { |k| k.read }
140
- unless currentkey == public_key.to_s
141
- raise Puppet::Error, "public keys for #{hostname} differ"
51
+ # We determine whether we signed the csr by checking if there's a certificate for it
52
+ if cert = Puppet::SSL::Certificate.find(hostname)
53
+ [cert.to_s, cacert.to_s]
54
+ else
55
+ nil
142
56
  end
143
- else
144
- File.open(pkeyfile, "w", 0644) { |f|
145
- f.print public_key.to_s
146
- }
147
57
  end
148
58
  end
149
59
  end
@@ -1,6 +1,5 @@
1
1
  require 'openssl'
2
2
  require 'puppet'
3
- require 'puppet/sslcertificates'
4
3
  require 'xmlrpc/server'
5
4
  require 'yaml'
6
5
 
@@ -33,8 +32,6 @@ class Puppet::Network::Handler
33
32
 
34
33
  args[:Local] = true
35
34
 
36
- @ca = (hash.include?(:CA) and hash[:CA]) ? Puppet::SSLCertificates::CA.new : nil
37
-
38
35
  # This is only used by the cfengine module, or if --loadclasses was
39
36
  # specified in +puppet+.
40
37
  args[:Classes] = hash[:Classes] if hash.include?(:Classes)
@@ -1,4 +1,5 @@
1
1
  require 'puppet/run'
2
+ require 'puppet/network/handler'
2
3
 
3
4
  class Puppet::Network::Handler
4
5
  class MissingMasterError < RuntimeError; end # Cannot find the master client
@@ -27,6 +27,12 @@ class Puppet::SSL::Certificate < Puppet::SSL::Base
27
27
  [:s]
28
28
  end
29
29
 
30
+ def subject_alt_names
31
+ alts = content.extensions.find{|ext| ext.oid == "subjectAltName"}
32
+ return [] unless alts
33
+ alts.value.split(/\s*,\s*/)
34
+ end
35
+
30
36
  def expiration
31
37
  return nil unless content
32
38
  content.not_after
@@ -11,6 +11,15 @@ require 'puppet/util/cacher'
11
11
  # it can also be seen as a general interface into all of the
12
12
  # SSL stuff.
13
13
  class Puppet::SSL::CertificateAuthority
14
+ # We will only sign extensions on this whitelist, ever. Any CSR with a
15
+ # requested extension that we don't recognize is rejected, against the risk
16
+ # that it will introduce some security issue through our ignorance of it.
17
+ #
18
+ # Adding an extension to this whitelist simply means we will consider it
19
+ # further, not that we will always accept a certificate with an extension
20
+ # requested on this list.
21
+ RequestExtensionWhitelist = %w{subjectAltName}
22
+
14
23
  require 'puppet/ssl/certificate_factory'
15
24
  require 'puppet/ssl/inventory'
16
25
  require 'puppet/ssl/certificate_revocation_list'
@@ -25,6 +34,14 @@ class Puppet::SSL::CertificateAuthority
25
34
  end
26
35
  end
27
36
 
37
+ class CertificateSigningError < RuntimeError
38
+ attr_accessor :host
39
+
40
+ def initialize(host)
41
+ @host = host
42
+ end
43
+ end
44
+
28
45
  class << self
29
46
  include Puppet::Util::Cacher
30
47
 
@@ -52,7 +69,6 @@ class Puppet::SSL::CertificateAuthority
52
69
  def apply(method, options)
53
70
  raise ArgumentError, "You must specify the hosts to apply to; valid values are an array or the symbol :all" unless options[:to]
54
71
  applier = Interface.new(method, options)
55
-
56
72
  applier.apply(self)
57
73
  end
58
74
 
@@ -108,13 +124,15 @@ class Puppet::SSL::CertificateAuthority
108
124
  end
109
125
 
110
126
  # Generate a new certificate.
111
- def generate(name)
127
+ def generate(name, options = {})
112
128
  raise ArgumentError, "A Certificate already exists for #{name}" if Puppet::SSL::Certificate.find(name)
113
- host = Puppet::SSL::Host.new(name)
114
129
 
115
- host.generate_certificate_request
130
+ # Pass on any requested subjectAltName field.
131
+ san = options[:dns_alt_names]
116
132
 
117
- sign(name)
133
+ host = Puppet::SSL::Host.new(name)
134
+ host.generate_certificate_request(:dns_alt_names => san)
135
+ sign(name, !!san)
118
136
  end
119
137
 
120
138
  # Generate our CA certificate.
@@ -123,14 +141,16 @@ class Puppet::SSL::CertificateAuthority
123
141
 
124
142
  host.generate_key unless host.key
125
143
 
126
- # Create a new cert request. We do this
127
- # specially, because we don't want to actually
128
- # save the request anywhere.
144
+ # Create a new cert request. We do this specially, because we don't want
145
+ # to actually save the request anywhere.
129
146
  request = Puppet::SSL::CertificateRequest.new(host.name)
147
+
148
+ # We deliberately do not put any subjectAltName in here: the CA
149
+ # certificate absolutely does not need them. --daniel 2011-10-13
130
150
  request.generate(host.key)
131
151
 
132
152
  # Create a self-signed certificate.
133
- @certificate = sign(host.name, :ca, request)
153
+ @certificate = sign(host.name, false, request)
134
154
 
135
155
  # And make sure we initialize our CRL.
136
156
  crl
@@ -223,20 +243,34 @@ class Puppet::SSL::CertificateAuthority
223
243
  end
224
244
 
225
245
  # Sign a given certificate request.
226
- def sign(hostname, cert_type = :server, self_signing_csr = nil)
246
+ def sign(hostname, allow_dns_alt_names = false, self_signing_csr = nil)
227
247
  # This is a self-signed certificate
228
248
  if self_signing_csr
249
+ # # This is a self-signed certificate, which is for the CA. Since this
250
+ # # forces the certificate to be self-signed, anyone who manages to trick
251
+ # # the system into going through this path gets a certificate they could
252
+ # # generate anyway. There should be no security risk from that.
229
253
  csr = self_signing_csr
254
+ cert_type = :ca
230
255
  issuer = csr.content
231
256
  else
257
+ allow_dns_alt_names = true if hostname == Puppet[:certname].downcase
232
258
  unless csr = Puppet::SSL::CertificateRequest.find(hostname)
233
259
  raise ArgumentError, "Could not find certificate request for #{hostname}"
234
260
  end
261
+
262
+ cert_type = :server
235
263
  issuer = host.certificate.content
264
+
265
+ # Make sure that the CSR conforms to our internal signing policies.
266
+ # This will raise if the CSR doesn't conform, but just in case...
267
+ check_internal_signing_policies(hostname, csr, allow_dns_alt_names) or
268
+ raise CertificateSigningError.new(hostname), "CSR had an unknown failure checking internal signing policies, will not sign!"
236
269
  end
237
270
 
238
271
  cert = Puppet::SSL::Certificate.new(hostname)
239
- cert.content = Puppet::SSL::CertificateFactory.new(cert_type, csr.content, issuer, next_serial).result
272
+ cert.content = Puppet::SSL::CertificateFactory.
273
+ build(cert_type, csr, issuer, next_serial)
240
274
  cert.content.sign(host.key.content, OpenSSL::Digest::SHA1.new)
241
275
 
242
276
  Puppet.notice "Signed certificate request for #{hostname}"
@@ -256,6 +290,47 @@ class Puppet::SSL::CertificateAuthority
256
290
  cert
257
291
  end
258
292
 
293
+ def check_internal_signing_policies(hostname, csr, allow_dns_alt_names)
294
+ # Reject unknown request extensions.
295
+ unknown_req = csr.request_extensions.
296
+ reject {|x| RequestExtensionWhitelist.include? x["oid"] }
297
+
298
+ if unknown_req and not unknown_req.empty?
299
+ names = unknown_req.map {|x| x["oid"] }.sort.uniq.join(", ")
300
+ raise CertificateSigningError.new(hostname), "CSR has request extensions that are not permitted: #{names}"
301
+ end
302
+
303
+ # Wildcards: we don't allow 'em at any point.
304
+ #
305
+ # The stringification here makes the content visible, and saves us having
306
+ # to scrobble through the content of the CSR subject field to make sure it
307
+ # is what we expect where we expect it.
308
+ if csr.content.subject.to_s.include? '*'
309
+ raise CertificateSigningError.new(hostname), "CSR subject contains a wildcard, which is not allowed: #{csr.content.subject.to_s}"
310
+ end
311
+
312
+ unless csr.subject_alt_names.empty?
313
+ # If you alt names are allowed, they are required. Otherwise they are
314
+ # disallowed. Self-signed certs are implicitly trusted, however.
315
+ unless allow_dns_alt_names
316
+ raise CertificateSigningError.new(hostname), "CSR '#{csr.name}' contains subject alternative names (#{csr.subject_alt_names.join(', ')}), which are disallowed. Use `puppet cert --allow-dns-alt-names sign #{csr.name}` to sign this request."
317
+ end
318
+
319
+ # If subjectAltNames are present, validate that they are only for DNS
320
+ # labels, not any other kind.
321
+ unless csr.subject_alt_names.all? {|x| x =~ /^DNS:/ }
322
+ raise CertificateSigningError.new(hostname), "CSR '#{csr.name}' contains a subjectAltName outside the DNS label space: #{csr.subject_alt_names.join(', ')}. To continue, this CSR needs to be cleaned."
323
+ end
324
+
325
+ # Check for wildcards in the subjectAltName fields too.
326
+ if csr.subject_alt_names.any? {|x| x.include? '*' }
327
+ raise CertificateSigningError.new(hostname), "CSR '#{csr.name}' subjectAltName contains a wildcard, which is not allowed: #{csr.subject_alt_names.join(', ')} To continue, this CSR needs to be cleaned."
328
+ end
329
+ end
330
+
331
+ return true # good enough for us!
332
+ end
333
+
259
334
  # Verify a given host's certificate.
260
335
  def verify(name)
261
336
  unless cert = Puppet::SSL::Certificate.find(name)