puppet 2.7.5 → 2.7.6

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 (140) hide show
  1. data/CHANGELOG +121 -0
  2. data/conf/redhat/puppet.spec +16 -7
  3. data/lib/puppet.rb +1 -1
  4. data/lib/puppet/application/cert.rb +17 -3
  5. data/lib/puppet/application/device.rb +1 -0
  6. data/lib/puppet/application/kick.rb +0 -2
  7. data/lib/puppet/application/resource.rb +73 -66
  8. data/lib/puppet/configurer/plugin_handler.rb +6 -2
  9. data/lib/puppet/defaults.rb +60 -5
  10. data/lib/puppet/face/ca.rb +11 -2
  11. data/lib/puppet/face/certificate.rb +33 -4
  12. data/lib/puppet/file_serving/fileset.rb +1 -1
  13. data/lib/puppet/file_serving/indirection_hooks.rb +2 -2
  14. data/lib/puppet/file_serving/metadata.rb +43 -4
  15. data/lib/puppet/indirector.rb +0 -1
  16. data/lib/puppet/indirector/request.rb +3 -4
  17. data/lib/puppet/indirector/resource/active_record.rb +3 -10
  18. data/lib/puppet/indirector/resource/ral.rb +2 -2
  19. data/lib/puppet/indirector/rest.rb +1 -1
  20. data/lib/puppet/network/handler/ca.rb +16 -106
  21. data/lib/puppet/network/handler/master.rb +0 -3
  22. data/lib/puppet/network/handler/runner.rb +1 -0
  23. data/lib/puppet/parser/scope.rb +10 -0
  24. data/lib/puppet/provider/file/posix.rb +72 -34
  25. data/lib/puppet/provider/file/windows.rb +100 -0
  26. data/lib/puppet/provider/group/windows_adsi.rb +2 -2
  27. data/lib/puppet/provider/user/windows_adsi.rb +19 -4
  28. data/lib/puppet/resource.rb +16 -0
  29. data/lib/puppet/resource/catalog.rb +1 -1
  30. data/lib/puppet/ssl/certificate.rb +2 -2
  31. data/lib/puppet/ssl/certificate_authority.rb +86 -10
  32. data/lib/puppet/ssl/certificate_authority/interface.rb +64 -19
  33. data/lib/puppet/ssl/certificate_factory.rb +112 -91
  34. data/lib/puppet/ssl/certificate_request.rb +88 -1
  35. data/lib/puppet/ssl/host.rb +20 -3
  36. data/lib/puppet/type/file.rb +15 -34
  37. data/lib/puppet/type/file/group.rb +11 -91
  38. data/lib/puppet/type/file/mode.rb +11 -41
  39. data/lib/puppet/type/file/owner.rb +18 -34
  40. data/lib/puppet/type/file/source.rb +22 -7
  41. data/lib/puppet/type/group.rb +4 -3
  42. data/lib/puppet/type/user.rb +4 -1
  43. data/lib/puppet/util.rb +59 -6
  44. data/lib/puppet/util/adsi.rb +11 -0
  45. data/lib/puppet/util/log.rb +4 -0
  46. data/lib/puppet/util/log/destinations.rb +7 -1
  47. data/lib/puppet/util/monkey_patches.rb +19 -0
  48. data/lib/puppet/util/network_device/config.rb +4 -5
  49. data/lib/puppet/util/settings.rb +5 -0
  50. data/lib/puppet/util/suidmanager.rb +0 -1
  51. data/lib/puppet/util/windows.rb +4 -0
  52. data/lib/puppet/util/windows/error.rb +16 -0
  53. data/lib/puppet/util/windows/security.rb +593 -0
  54. data/spec/integration/defaults_spec.rb +27 -0
  55. data/spec/integration/network/handler_spec.rb +1 -1
  56. data/spec/integration/type/file_spec.rb +382 -145
  57. data/spec/integration/util/windows/security_spec.rb +468 -0
  58. data/spec/shared_behaviours/file_serving.rb +4 -3
  59. data/spec/unit/application/agent_spec.rb +1 -0
  60. data/spec/unit/application/device_spec.rb +5 -0
  61. data/spec/unit/application/resource_spec.rb +62 -101
  62. data/spec/unit/configurer/downloader_spec.rb +2 -2
  63. data/spec/unit/configurer/plugin_handler_spec.rb +15 -8
  64. data/spec/unit/configurer_spec.rb +2 -2
  65. data/spec/unit/face/ca_spec.rb +34 -0
  66. data/spec/unit/face/certificate_spec.rb +168 -1
  67. data/spec/unit/file_serving/fileset_spec.rb +1 -1
  68. data/spec/unit/file_serving/indirection_hooks_spec.rb +1 -1
  69. data/spec/unit/file_serving/metadata_spec.rb +151 -107
  70. data/spec/unit/indirector/certificate_request/ca_spec.rb +0 -3
  71. data/spec/unit/indirector/direct_file_server_spec.rb +10 -9
  72. data/spec/unit/indirector/file_metadata/file_spec.rb +6 -4
  73. data/spec/unit/indirector/request_spec.rb +13 -3
  74. data/spec/unit/indirector/resource/active_record_spec.rb +4 -10
  75. data/spec/unit/indirector/resource/ral_spec.rb +6 -4
  76. data/spec/unit/indirector/rest_spec.rb +5 -6
  77. data/spec/unit/network/handler/ca_spec.rb +86 -0
  78. data/spec/unit/parser/collector_spec.rb +7 -7
  79. data/spec/unit/parser/scope_spec.rb +20 -0
  80. data/spec/unit/provider/file/posix_spec.rb +226 -0
  81. data/spec/unit/provider/file/windows_spec.rb +136 -0
  82. data/spec/unit/provider/group/windows_adsi_spec.rb +7 -2
  83. data/spec/unit/provider/user/windows_adsi_spec.rb +36 -3
  84. data/spec/unit/resource/catalog_spec.rb +20 -10
  85. data/spec/unit/resource_spec.rb +55 -8
  86. data/spec/unit/ssl/certificate_authority/interface_spec.rb +97 -54
  87. data/spec/unit/ssl/certificate_authority_spec.rb +133 -23
  88. data/spec/unit/ssl/certificate_factory_spec.rb +90 -70
  89. data/spec/unit/ssl/certificate_request_spec.rb +62 -1
  90. data/spec/unit/ssl/certificate_spec.rb +20 -14
  91. data/spec/unit/ssl/host_spec.rb +52 -6
  92. data/spec/unit/type/file/content_spec.rb +4 -4
  93. data/spec/unit/type/file/group_spec.rb +34 -96
  94. data/spec/unit/type/file/mode_spec.rb +88 -0
  95. data/spec/unit/type/file/owner_spec.rb +32 -123
  96. data/spec/unit/type/file/source_spec.rb +120 -41
  97. data/spec/unit/type/file_spec.rb +1033 -753
  98. data/spec/unit/type_spec.rb +19 -1
  99. data/spec/unit/util/adsi_spec.rb +19 -0
  100. data/spec/unit/util/log/destinations_spec.rb +75 -0
  101. data/spec/unit/util/log_spec.rb +15 -0
  102. data/spec/unit/util/network_device/config_spec.rb +7 -0
  103. data/spec/unit/util/settings_spec.rb +10 -0
  104. data/spec/unit/util_spec.rb +126 -13
  105. data/test/language/functions.rb +0 -1
  106. data/test/language/snippets.rb +0 -9
  107. data/test/lib/puppettest/exetest.rb +1 -1
  108. data/test/lib/puppettest/servertest.rb +0 -1
  109. data/test/rails/rails.rb +0 -1
  110. data/test/ral/type/filesources.rb +0 -60
  111. metadata +13 -33
  112. data/lib/puppet/network/client.rb +0 -174
  113. data/lib/puppet/network/client/ca.rb +0 -56
  114. data/lib/puppet/network/client/file.rb +0 -6
  115. data/lib/puppet/network/client/proxy.rb +0 -27
  116. data/lib/puppet/network/client/report.rb +0 -26
  117. data/lib/puppet/network/client/runner.rb +0 -10
  118. data/lib/puppet/network/client/status.rb +0 -4
  119. data/lib/puppet/network/http_server.rb +0 -3
  120. data/lib/puppet/network/http_server/mongrel.rb +0 -130
  121. data/lib/puppet/network/http_server/webrick.rb +0 -155
  122. data/lib/puppet/network/xmlrpc/client.rb +0 -211
  123. data/lib/puppet/provider/file/win32.rb +0 -72
  124. data/lib/puppet/sslcertificates.rb +0 -146
  125. data/lib/puppet/sslcertificates/ca.rb +0 -375
  126. data/lib/puppet/sslcertificates/certificate.rb +0 -255
  127. data/lib/puppet/sslcertificates/inventory.rb +0 -38
  128. data/lib/puppet/sslcertificates/support.rb +0 -146
  129. data/spec/integration/network/client_spec.rb +0 -18
  130. data/spec/unit/network/xmlrpc/client_spec.rb +0 -172
  131. data/spec/unit/sslcertificates/ca_spec.rb +0 -106
  132. data/test/certmgr/certmgr.rb +0 -308
  133. data/test/certmgr/inventory.rb +0 -69
  134. data/test/certmgr/support.rb +0 -105
  135. data/test/network/client/ca.rb +0 -69
  136. data/test/network/client/dipper.rb +0 -34
  137. data/test/network/handler/ca.rb +0 -273
  138. data/test/network/server/mongrel_test.rb +0 -99
  139. data/test/network/server/webrick.rb +0 -111
  140. data/test/network/xmlrpc/client.rb +0 -45
@@ -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
@@ -418,6 +418,16 @@ class Puppet::Parser::Scope
418
418
  environment.known_resource_types.find_definition(namespaces, type.to_s.downcase)
419
419
  end
420
420
 
421
+ def method_missing(method, *args, &block)
422
+ method.to_s =~ /^function_(.*)$/
423
+ super unless $1
424
+ super unless Puppet::Parser::Functions.function($1)
425
+
426
+ # Calling .function(name) adds "function_#{name}" as a callable method on
427
+ # self if it's found, so now we can just send it
428
+ send(method, *args)
429
+ end
430
+
421
431
  def resolve_type_and_titles(type, titles)
422
432
  raise ArgumentError, "titles must be an array" unless titles.is_a?(Array)
423
433
 
@@ -8,15 +8,13 @@ Puppet::Type.type(:file).provide :posix do
8
8
 
9
9
  require 'etc'
10
10
 
11
- def id2name(id)
12
- return id.to_s if id.is_a?(Symbol)
11
+ def uid2name(id)
12
+ return id.to_s if id.is_a?(Symbol) or id.is_a?(String)
13
13
  return nil if id > Puppet[:maximum_uid].to_i
14
14
 
15
15
  begin
16
16
  user = Etc.getpwuid(id)
17
- rescue TypeError
18
- return nil
19
- rescue ArgumentError
17
+ rescue TypeError, ArgumentError
20
18
  return nil
21
19
  end
22
20
 
@@ -27,33 +25,33 @@ Puppet::Type.type(:file).provide :posix do
27
25
  end
28
26
  end
29
27
 
30
- def is_owner_insync?(current, should)
31
- should.each do |value|
32
- if value =~ /^\d+$/
33
- uid = Integer(value)
34
- elsif value.is_a?(String)
35
- fail "Could not find user #{value}" unless uid = uid(value)
36
- else
37
- uid = value
38
- end
39
-
40
- return true if uid == current
41
- end
28
+ # Determine if the user is valid, and if so, return the UID
29
+ def name2uid(value)
30
+ Integer(value) rescue uid(value) || false
31
+ end
42
32
 
43
- unless Puppet.features.root?
44
- warnonce "Cannot manage ownership unless running as root"
45
- return true
33
+ def gid2name(id)
34
+ return id.to_s if id.is_a?(Symbol) or id.is_a?(String)
35
+ return nil if id > Puppet[:maximum_uid].to_i
36
+
37
+ begin
38
+ group = Etc.getgrgid(id)
39
+ rescue TypeError, ArgumentError
40
+ return nil
46
41
  end
47
42
 
48
- false
43
+ if group.gid == ""
44
+ return nil
45
+ else
46
+ return group.name
47
+ end
49
48
  end
50
49
 
51
- # Determine if the user is valid, and if so, return the UID
52
- def validuser?(value)
53
- Integer(value) rescue uid(value) || false
50
+ def name2gid(value)
51
+ Integer(value) rescue gid(value) || false
54
52
  end
55
53
 
56
- def retrieve(resource)
54
+ def owner
57
55
  unless stat = resource.stat
58
56
  return :absent
59
57
  end
@@ -71,27 +69,67 @@ Puppet::Type.type(:file).provide :posix do
71
69
  currentvalue
72
70
  end
73
71
 
74
- def sync(path, links, should)
72
+ def owner=(should)
75
73
  # Set our method appropriately, depending on links.
76
- if links == :manage
74
+ if resource[:links] == :manage
77
75
  method = :lchown
78
76
  else
79
77
  method = :chown
80
78
  end
81
79
 
82
- uid = nil
83
- should.each do |user|
84
- break if uid = validuser?(user)
80
+ begin
81
+ File.send(method, should, nil, resource[:path])
82
+ rescue => detail
83
+ raise Puppet::Error, "Failed to set owner to '#{should}': #{detail}"
84
+ end
85
+ end
86
+
87
+ def group
88
+ return :absent unless stat = resource.stat
89
+
90
+ currentvalue = stat.gid
91
+
92
+ # On OS X, files that are owned by -2 get returned as really
93
+ # large GIDs instead of negative ones. This isn't a Ruby bug,
94
+ # it's an OS X bug, since it shows up in perl, too.
95
+ if currentvalue > Puppet[:maximum_uid].to_i
96
+ self.warning "Apparently using negative GID (#{currentvalue}) on a platform that does not consistently handle them"
97
+ currentvalue = :silly
85
98
  end
86
99
 
87
- raise Puppet::Error, "Could not find user(s) #{should.join(",")}" unless uid
100
+ currentvalue
101
+ end
102
+
103
+ def group=(should)
104
+ # Set our method appropriately, depending on links.
105
+ if resource[:links] == :manage
106
+ method = :lchown
107
+ else
108
+ method = :chown
109
+ end
88
110
 
89
111
  begin
90
- File.send(method, uid, nil, path)
112
+ File.send(method, nil, should, resource[:path])
91
113
  rescue => detail
92
- raise Puppet::Error, "Failed to set owner to '#{uid}': #{detail}"
114
+ raise Puppet::Error, "Failed to set group to '#{should}': #{detail}"
115
+ end
116
+ end
117
+
118
+ def mode
119
+ if stat = resource.stat
120
+ return (stat.mode & 007777).to_s(8)
121
+ else
122
+ return :absent
93
123
  end
124
+ end
94
125
 
95
- :file_changed
126
+ def mode=(value)
127
+ begin
128
+ File.chmod(value.to_i(8), resource[:path])
129
+ rescue => detail
130
+ error = Puppet::Error.new("failed to set mode #{mode} on #{resource[:path]}: #{detail.message}")
131
+ error.set_backtrace detail.backtrace
132
+ raise error
133
+ end
96
134
  end
97
135
  end
@@ -0,0 +1,100 @@
1
+ Puppet::Type.type(:file).provide :windows do
2
+ desc "Uses Microsoft Windows functionality to manage file's users and rights."
3
+
4
+ confine :feature => :microsoft_windows
5
+
6
+ include Puppet::Util::Warnings
7
+
8
+ if Puppet.features.microsoft_windows?
9
+ require 'puppet/util/windows'
10
+ require 'puppet/util/adsi'
11
+ include Puppet::Util::Windows::Security
12
+ end
13
+
14
+ ERROR_INVALID_SID_STRUCTURE = 1337
15
+
16
+ def id2name(id)
17
+ # If it's a valid sid, get the name. Otherwise, it's already a name, so
18
+ # just return it.
19
+ begin
20
+ if string_to_sid_ptr(id)
21
+ name = nil
22
+ Puppet::Util::ADSI.execquery(
23
+ "SELECT Name FROM Win32_Account WHERE SID = '#{id}'
24
+ AND LocalAccount = true"
25
+ ).each { |a| name ||= a.name }
26
+ return name
27
+ end
28
+ rescue Puppet::Util::Windows::Error => e
29
+ raise unless e.code == ERROR_INVALID_SID_STRUCTURE
30
+ end
31
+
32
+ id
33
+ end
34
+
35
+ # Determine if the account is valid, and if so, return the UID
36
+ def name2id(value)
37
+ # If it's a valid sid, then return it. Else, it's a name we need to convert
38
+ # to sid.
39
+ begin
40
+ return value if string_to_sid_ptr(value)
41
+ rescue Puppet::Util::Windows::Error => e
42
+ raise unless e.code == ERROR_INVALID_SID_STRUCTURE
43
+ end
44
+
45
+ Puppet::Util::ADSI.sid_for_account(value) rescue nil
46
+ end
47
+
48
+ # We use users and groups interchangeably, so use the same methods for both
49
+ # (the type expects different methods, so we have to oblige).
50
+ alias :uid2name :id2name
51
+ alias :gid2name :id2name
52
+
53
+ alias :name2gid :name2id
54
+ alias :name2uid :name2id
55
+
56
+ def owner
57
+ return :absent unless resource.exist?
58
+ get_owner(resource[:path])
59
+ end
60
+
61
+ def owner=(should)
62
+ begin
63
+ set_owner(should, resource[:path])
64
+ rescue => detail
65
+ raise Puppet::Error, "Failed to set owner to '#{should}': #{detail}"
66
+ end
67
+ end
68
+
69
+ def group
70
+ return :absent unless resource.exist?
71
+ get_group(resource[:path])
72
+ end
73
+
74
+ def group=(should)
75
+ begin
76
+ set_group(should, resource[:path])
77
+ rescue => detail
78
+ raise Puppet::Error, "Failed to set group to '#{should}': #{detail}"
79
+ end
80
+ end
81
+
82
+ def mode
83
+ if resource.exist?
84
+ get_mode(resource[:path]).to_s(8)
85
+ else
86
+ :absent
87
+ end
88
+ end
89
+
90
+ def mode=(value)
91
+ begin
92
+ set_mode(value.to_i(8), resource[:path])
93
+ rescue => detail
94
+ error = Puppet::Error.new("failed to set mode #{mode} on #{resource[:path]}: #{detail.message}")
95
+ error.set_backtrace detail.backtrace
96
+ raise error
97
+ end
98
+ :file_changed
99
+ end
100
+ end
@@ -35,11 +35,11 @@ Puppet::Type.type(:group).provide :windows_adsi do
35
35
  end
36
36
 
37
37
  def gid
38
- nil
38
+ Puppet::Util::ADSI.sid_for_account(@resource[:name])
39
39
  end
40
40
 
41
41
  def gid=(value)
42
- warning "No support for managing property gid of group #{@resource[:name]} on Windows"
42
+ fail "gid is read-only"
43
43
  end
44
44
 
45
45
  def self.instances
@@ -7,7 +7,7 @@ Puppet::Type.type(:user).provide :windows_adsi do
7
7
  confine :operatingsystem => :windows
8
8
  confine :feature => :microsoft_windows
9
9
 
10
- has_features :manages_homedir
10
+ has_features :manages_homedir, :manages_passwords
11
11
 
12
12
  def user
13
13
  @user ||= Puppet::Util::ADSI::User.new(@resource[:name])
@@ -57,11 +57,26 @@ Puppet::Type.type(:user).provide :windows_adsi do
57
57
  user['HomeDirectory'] = value
58
58
  end
59
59
 
60
- [:uid, :gid, :shell].each do |prop|
61
- define_method(prop) { nil }
60
+ def password
61
+ user.password_is?( @resource[:password] ) ? @resource[:password] : :absent
62
+ end
62
63
 
64
+ def password=(value)
65
+ user.password = value
66
+ end
67
+
68
+ def uid
69
+ Puppet::Util::ADSI.sid_for_account(@resource[:name])
70
+ end
71
+
72
+ def uid=(value)
73
+ fail "uid is read-only"
74
+ end
75
+
76
+ [:gid, :shell].each do |prop|
77
+ define_method(prop) { nil }
63
78
  define_method("#{prop}=") do |v|
64
- warning "No support for managing property #{prop} of user #{@resource[:name]} on Windows"
79
+ fail "No support for managing property #{prop} of user #{@resource[:name]} on Windows"
65
80
  end
66
81
  end
67
82
 
@@ -355,6 +355,22 @@ class Puppet::Resource
355
355
  raise ArgumentError, "Invalid parameter #{name}" unless valid_parameter?(name)
356
356
  end
357
357
 
358
+ def prune_parameters(options = {})
359
+ properties = resource_type.properties.map(&:name)
360
+
361
+ dup.collect do |attribute, value|
362
+ if value.to_s.empty? or Array(value).empty?
363
+ delete(attribute)
364
+ elsif value.to_s == "absent" and attribute.to_s != "ensure"
365
+ delete(attribute)
366
+ end
367
+
368
+ parameters_to_include = options[:parameters_to_include] || []
369
+ delete(attribute) unless properties.include?(attribute) || parameters_to_include.include?(attribute)
370
+ end
371
+ self
372
+ end
373
+
358
374
  private
359
375
 
360
376
  # Produce a canonical method name.
@@ -546,7 +546,7 @@ class Puppet::Resource::Catalog < Puppet::SimpleGraph
546
546
  def write_resource_file
547
547
  ::File.open(Puppet[:resourcefile], "w") do |f|
548
548
  to_print = resources.map do |resource|
549
- next if resource.type == :component
549
+ next unless resource.managed?
550
550
  "#{resource.type}[#{resource[resource.name_var]}]"
551
551
  end.compact
552
552
  f.puts to_print.join("\n")
@@ -27,10 +27,10 @@ class Puppet::SSL::Certificate < Puppet::SSL::Base
27
27
  [:s]
28
28
  end
29
29
 
30
- def alternate_names
30
+ def subject_alt_names
31
31
  alts = content.extensions.find{|ext| ext.oid == "subjectAltName"}
32
32
  return [] unless alts
33
- alts.value.split(/,\s+/).map{|al| al.sub(/^DNS:/,'')}
33
+ alts.value.split(/\s*,\s*/)
34
34
  end
35
35
 
36
36
  def expiration
@@ -11,6 +11,15 @@ require 'puppet/ssl/certificate_request'
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'
@@ -33,6 +42,14 @@ class Puppet::SSL::CertificateAuthority
33
42
  end
34
43
  end
35
44
 
45
+ class CertificateSigningError < RuntimeError
46
+ attr_accessor :host
47
+
48
+ def initialize(host)
49
+ @host = host
50
+ end
51
+ end
52
+
36
53
  def self.ca?
37
54
  return false unless Puppet[:ca]
38
55
  return false unless Puppet.run_mode.master?
@@ -54,7 +71,6 @@ class Puppet::SSL::CertificateAuthority
54
71
  def apply(method, options)
55
72
  raise ArgumentError, "You must specify the hosts to apply to; valid values are an array or the symbol :all" unless options[:to]
56
73
  applier = Interface.new(method, options)
57
-
58
74
  applier.apply(self)
59
75
  end
60
76
 
@@ -110,13 +126,16 @@ class Puppet::SSL::CertificateAuthority
110
126
  end
111
127
 
112
128
  # Generate a new certificate.
113
- def generate(name)
129
+ def generate(name, options = {})
114
130
  raise ArgumentError, "A Certificate already exists for #{name}" if Puppet::SSL::Certificate.indirection.find(name)
115
131
  host = Puppet::SSL::Host.new(name)
116
132
 
117
- host.generate_certificate_request
133
+ # Pass on any requested subjectAltName field.
134
+ san = options[:dns_alt_names]
118
135
 
119
- sign(name)
136
+ host = Puppet::SSL::Host.new(name)
137
+ host.generate_certificate_request(:dns_alt_names => san)
138
+ sign(name, !!san)
120
139
  end
121
140
 
122
141
  # Generate our CA certificate.
@@ -125,14 +144,16 @@ class Puppet::SSL::CertificateAuthority
125
144
 
126
145
  host.generate_key unless host.key
127
146
 
128
- # Create a new cert request. We do this
129
- # specially, because we don't want to actually
130
- # save the request anywhere.
147
+ # Create a new cert request. We do this specially, because we don't want
148
+ # to actually save the request anywhere.
131
149
  request = Puppet::SSL::CertificateRequest.new(host.name)
150
+
151
+ # We deliberately do not put any subjectAltName in here: the CA
152
+ # certificate absolutely does not need them. --daniel 2011-10-13
132
153
  request.generate(host.key)
133
154
 
134
155
  # Create a self-signed certificate.
135
- @certificate = sign(host.name, :ca, request)
156
+ @certificate = sign(host.name, false, request)
136
157
 
137
158
  # And make sure we initialize our CRL.
138
159
  crl
@@ -225,20 +246,34 @@ class Puppet::SSL::CertificateAuthority
225
246
  end
226
247
 
227
248
  # Sign a given certificate request.
228
- def sign(hostname, cert_type = :server, self_signing_csr = nil)
249
+ def sign(hostname, allow_dns_alt_names = false, self_signing_csr = nil)
229
250
  # This is a self-signed certificate
230
251
  if self_signing_csr
252
+ # # This is a self-signed certificate, which is for the CA. Since this
253
+ # # forces the certificate to be self-signed, anyone who manages to trick
254
+ # # the system into going through this path gets a certificate they could
255
+ # # generate anyway. There should be no security risk from that.
231
256
  csr = self_signing_csr
257
+ cert_type = :ca
232
258
  issuer = csr.content
233
259
  else
260
+ allow_dns_alt_names = true if hostname == Puppet[:certname].downcase
234
261
  unless csr = Puppet::SSL::CertificateRequest.indirection.find(hostname)
235
262
  raise ArgumentError, "Could not find certificate request for #{hostname}"
236
263
  end
264
+
265
+ cert_type = :server
237
266
  issuer = host.certificate.content
267
+
268
+ # Make sure that the CSR conforms to our internal signing policies.
269
+ # This will raise if the CSR doesn't conform, but just in case...
270
+ check_internal_signing_policies(hostname, csr, allow_dns_alt_names) or
271
+ raise CertificateSigningError.new(hostname), "CSR had an unknown failure checking internal signing policies, will not sign!"
238
272
  end
239
273
 
240
274
  cert = Puppet::SSL::Certificate.new(hostname)
241
- cert.content = Puppet::SSL::CertificateFactory.new(cert_type, csr.content, issuer, next_serial).result
275
+ cert.content = Puppet::SSL::CertificateFactory.
276
+ build(cert_type, csr, issuer, next_serial)
242
277
  cert.content.sign(host.key.content, OpenSSL::Digest::SHA1.new)
243
278
 
244
279
  Puppet.notice "Signed certificate request for #{hostname}"
@@ -258,6 +293,47 @@ class Puppet::SSL::CertificateAuthority
258
293
  cert
259
294
  end
260
295
 
296
+ def check_internal_signing_policies(hostname, csr, allow_dns_alt_names)
297
+ # Reject unknown request extensions.
298
+ unknown_req = csr.request_extensions.
299
+ reject {|x| RequestExtensionWhitelist.include? x["oid"] }
300
+
301
+ if unknown_req and not unknown_req.empty?
302
+ names = unknown_req.map {|x| x["oid"] }.sort.uniq.join(", ")
303
+ raise CertificateSigningError.new(hostname), "CSR has request extensions that are not permitted: #{names}"
304
+ end
305
+
306
+ # Wildcards: we don't allow 'em at any point.
307
+ #
308
+ # The stringification here makes the content visible, and saves us having
309
+ # to scrobble through the content of the CSR subject field to make sure it
310
+ # is what we expect where we expect it.
311
+ if csr.content.subject.to_s.include? '*'
312
+ raise CertificateSigningError.new(hostname), "CSR subject contains a wildcard, which is not allowed: #{csr.content.subject.to_s}"
313
+ end
314
+
315
+ unless csr.subject_alt_names.empty?
316
+ # If you alt names are allowed, they are required. Otherwise they are
317
+ # disallowed. Self-signed certs are implicitly trusted, however.
318
+ unless allow_dns_alt_names
319
+ 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."
320
+ end
321
+
322
+ # If subjectAltNames are present, validate that they are only for DNS
323
+ # labels, not any other kind.
324
+ unless csr.subject_alt_names.all? {|x| x =~ /^DNS:/ }
325
+ 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."
326
+ end
327
+
328
+ # Check for wildcards in the subjectAltName fields too.
329
+ if csr.subject_alt_names.any? {|x| x.include? '*' }
330
+ 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."
331
+ end
332
+ end
333
+
334
+ return true # good enough for us!
335
+ end
336
+
261
337
  # Verify a given host's certificate.
262
338
  def verify(name)
263
339
  unless cert = Puppet::SSL::Certificate.indirection.find(name)