rubygems-update 1.8.30 → 2.0.0.preview2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of rubygems-update might be problematic. Click here for more details.
- checksums.yaml +6 -6
- checksums.yaml.gz.sig +0 -0
- data.tar.gz.sig +3 -0
- data/.autotest +6 -3
- data/History.txt +137 -63
- data/LICENSE.txt +1 -5
- data/Manifest.txt +69 -32
- data/README.rdoc +11 -9
- data/Rakefile +24 -38
- data/bin/gem +0 -9
- data/bin/update_rubygems +1 -0
- data/lib/rubygems.rb +193 -405
- data/lib/rubygems/available_set.rb +95 -0
- data/lib/rubygems/command.rb +88 -45
- data/lib/rubygems/command_manager.rb +67 -40
- data/lib/rubygems/commands/build_command.rb +5 -23
- data/lib/rubygems/commands/cert_command.rb +199 -57
- data/lib/rubygems/commands/check_command.rb +14 -39
- data/lib/rubygems/commands/cleanup_command.rb +9 -1
- data/lib/rubygems/commands/contents_command.rb +30 -12
- data/lib/rubygems/commands/dependency_command.rb +3 -8
- data/lib/rubygems/commands/environment_command.rb +13 -8
- data/lib/rubygems/commands/fetch_command.rb +3 -16
- data/lib/rubygems/commands/generate_index_command.rb +7 -47
- data/lib/rubygems/commands/help_command.rb +1 -1
- data/lib/rubygems/commands/install_command.rb +69 -36
- data/lib/rubygems/commands/list_command.rb +6 -4
- data/lib/rubygems/commands/lock_command.rb +1 -1
- data/lib/rubygems/commands/mirror_command.rb +17 -0
- data/lib/rubygems/commands/outdated_command.rb +6 -3
- data/lib/rubygems/commands/owner_command.rb +13 -5
- data/lib/rubygems/commands/pristine_command.rb +19 -4
- data/lib/rubygems/commands/push_command.rb +12 -1
- data/lib/rubygems/commands/query_command.rb +43 -27
- data/lib/rubygems/commands/rdoc_command.rb +23 -28
- data/lib/rubygems/commands/search_command.rb +4 -18
- data/lib/rubygems/commands/server_command.rb +1 -1
- data/lib/rubygems/commands/setup_command.rb +124 -38
- data/lib/rubygems/commands/sources_command.rb +16 -16
- data/lib/rubygems/commands/specification_command.rb +11 -13
- data/lib/rubygems/commands/uninstall_command.rb +24 -7
- data/lib/rubygems/commands/unpack_command.rb +7 -3
- data/lib/rubygems/commands/update_command.rb +22 -36
- data/lib/rubygems/commands/yank_command.rb +98 -0
- data/lib/rubygems/compatibility.rb +51 -0
- data/lib/rubygems/config_file.rb +82 -54
- data/lib/rubygems/core_ext/kernel_gem.rb +53 -0
- data/lib/rubygems/core_ext/kernel_require.rb +119 -0
- data/lib/rubygems/defaults.rb +10 -21
- data/lib/rubygems/dependency.rb +61 -10
- data/lib/rubygems/dependency_installer.rb +157 -69
- data/lib/rubygems/dependency_list.rb +11 -19
- data/lib/rubygems/dependency_resolver.rb +562 -0
- data/lib/rubygems/deprecate.rb +40 -40
- data/lib/rubygems/errors.rb +77 -24
- data/lib/rubygems/exceptions.rb +25 -7
- data/lib/rubygems/ext/builder.rb +20 -23
- data/lib/rubygems/ext/configure_builder.rb +2 -2
- data/lib/rubygems/ext/ext_conf_builder.rb +5 -45
- data/lib/rubygems/ext/rake_builder.rb +2 -2
- data/lib/rubygems/gem_runner.rb +3 -16
- data/lib/rubygems/gemcutter_utilities.rb +22 -7
- data/lib/rubygems/indexer.rb +6 -159
- data/lib/rubygems/install_message.rb +12 -0
- data/lib/rubygems/install_update_options.rb +56 -18
- data/lib/rubygems/installer.rb +244 -134
- data/lib/rubygems/installer_test_case.rb +71 -19
- data/lib/rubygems/mock_gem_ui.rb +17 -0
- data/lib/rubygems/name_tuple.rb +110 -0
- data/lib/rubygems/package.rb +514 -43
- data/lib/rubygems/package/digest_io.rb +64 -0
- data/lib/rubygems/package/old.rb +147 -0
- data/lib/rubygems/package/tar_header.rb +18 -55
- data/lib/rubygems/package/tar_reader.rb +20 -3
- data/lib/rubygems/package/tar_writer.rb +63 -7
- data/lib/rubygems/package_task.rb +3 -4
- data/lib/rubygems/path_support.rb +14 -7
- data/lib/rubygems/platform.rb +19 -26
- data/lib/rubygems/rdoc.rb +316 -0
- data/lib/rubygems/remote_fetcher.rb +117 -54
- data/lib/rubygems/request_set.rb +182 -0
- data/lib/rubygems/requirement.rb +63 -26
- data/lib/rubygems/security.rb +295 -555
- data/lib/rubygems/security/policies.rb +115 -0
- data/lib/rubygems/security/policy.rb +227 -0
- data/lib/rubygems/security/signer.rb +136 -0
- data/lib/rubygems/security/trust_dir.rb +104 -0
- data/lib/rubygems/server.rb +45 -55
- data/lib/rubygems/source.rb +144 -0
- data/lib/rubygems/source_list.rb +87 -0
- data/lib/rubygems/source_local.rb +92 -0
- data/lib/rubygems/source_specific_file.rb +28 -0
- data/lib/rubygems/spec_fetcher.rb +116 -184
- data/lib/rubygems/specification.rb +731 -335
- data/lib/rubygems/ssl_certs/AddTrustExternalCARoot.pem +88 -30
- data/lib/rubygems/ssl_certs/Entrust_net-Secure-Server-Certification-Authority.pem +90 -0
- data/lib/rubygems/ssl_certs/VerisignClass3PublicPrimaryCertificationAuthority-G2.pem +57 -0
- data/lib/rubygems/syck_hack.rb +2 -0
- data/lib/rubygems/test_case.rb +199 -109
- data/lib/rubygems/test_utilities.rb +25 -5
- data/lib/rubygems/uninstaller.rb +62 -20
- data/lib/rubygems/user_interaction.rb +10 -0
- data/lib/rubygems/validator.rb +33 -40
- data/lib/rubygems/version.rb +19 -8
- data/setup.rb +8 -1
- data/test/rubygems/alternate_cert.pem +9 -0
- data/test/rubygems/alternate_cert_32.pem +9 -0
- data/test/rubygems/alternate_key.pem +9 -0
- data/test/rubygems/bad_rake.rb +1 -0
- data/test/rubygems/child_cert.pem +9 -0
- data/test/rubygems/child_cert_32.pem +9 -0
- data/test/rubygems/child_key.pem +9 -0
- data/test/rubygems/data/null-type.gemspec.rz +0 -0
- data/test/rubygems/expired_cert.pem +9 -0
- data/test/rubygems/future_cert.pem +9 -0
- data/test/rubygems/future_cert_32.pem +9 -0
- data/test/rubygems/good_rake.rb +1 -0
- data/test/rubygems/grandchild_cert.pem +9 -0
- data/test/rubygems/grandchild_cert_32.pem +9 -0
- data/test/rubygems/grandchild_key.pem +9 -0
- data/test/rubygems/invalid_issuer_cert.pem +9 -0
- data/test/rubygems/invalid_issuer_cert_32.pem +9 -0
- data/test/rubygems/invalid_key.pem +9 -0
- data/test/rubygems/invalid_signer_cert.pem +9 -0
- data/test/rubygems/invalid_signer_cert_32.pem +9 -0
- data/test/rubygems/invalidchild_cert.pem +9 -0
- data/test/rubygems/invalidchild_cert_32.pem +9 -0
- data/test/rubygems/invalidchild_key.pem +9 -0
- data/test/rubygems/plugin/exception/rubygems_plugin.rb +1 -1
- data/test/rubygems/plugin/standarderror/rubygems_plugin.rb +1 -1
- data/test/rubygems/private_key.pem +7 -25
- data/test/rubygems/public_cert.pem +8 -18
- data/test/rubygems/public_cert_32.pem +10 -0
- data/test/rubygems/public_key.pem +4 -0
- data/test/rubygems/rubygems/commands/crash_command.rb +1 -1
- data/test/rubygems/test_config.rb +4 -6
- data/test/rubygems/test_deprecate.rb +76 -0
- data/test/rubygems/test_gem.rb +318 -83
- data/test/rubygems/test_gem_available_set.rb +106 -0
- data/test/rubygems/test_gem_command.rb +10 -0
- data/test/rubygems/test_gem_command_manager.rb +55 -9
- data/test/rubygems/test_gem_commands_build_command.rb +11 -19
- data/test/rubygems/test_gem_commands_cert_command.rb +441 -42
- data/test/rubygems/test_gem_commands_cleanup_command.rb +29 -1
- data/test/rubygems/test_gem_commands_contents_command.rb +23 -0
- data/test/rubygems/test_gem_commands_dependency_command.rb +5 -0
- data/test/rubygems/test_gem_commands_fetch_command.rb +19 -20
- data/test/rubygems/test_gem_commands_generate_index_command.rb +2 -83
- data/test/rubygems/test_gem_commands_help_command.rb +2 -1
- data/test/rubygems/test_gem_commands_install_command.rb +647 -48
- data/test/rubygems/test_gem_commands_mirror.rb +32 -0
- data/test/rubygems/test_gem_commands_owner_command.rb +4 -8
- data/test/rubygems/test_gem_commands_pristine_command.rb +99 -4
- data/test/rubygems/test_gem_commands_push_command.rb +62 -8
- data/test/rubygems/test_gem_commands_query_command.rb +51 -0
- data/test/rubygems/test_gem_commands_search_command.rb +25 -0
- data/test/rubygems/test_gem_commands_setup_command.rb +45 -0
- data/test/rubygems/test_gem_commands_sources_command.rb +21 -6
- data/test/rubygems/test_gem_commands_specification_command.rb +33 -1
- data/test/rubygems/test_gem_commands_uninstall_command.rb +91 -31
- data/test/rubygems/test_gem_commands_unpack_command.rb +3 -3
- data/test/rubygems/test_gem_commands_update_command.rb +56 -38
- data/test/rubygems/test_gem_commands_which_command.rb +4 -4
- data/test/rubygems/test_gem_commands_yank_command.rb +97 -0
- data/test/rubygems/test_gem_config_file.rb +66 -21
- data/test/rubygems/test_gem_dependency.rb +46 -0
- data/test/rubygems/test_gem_dependency_installer.rb +228 -18
- data/test/rubygems/test_gem_dependency_list.rb +0 -9
- data/test/rubygems/test_gem_dependency_resolver.rb +327 -0
- data/test/rubygems/test_gem_ext_configure_builder.rb +4 -4
- data/test/rubygems/test_gem_ext_ext_conf_builder.rb +21 -49
- data/test/rubygems/test_gem_ext_rake_builder.rb +13 -13
- data/test/rubygems/test_gem_gem_runner.rb +27 -5
- data/test/rubygems/test_gem_gemcutter_utilities.rb +19 -0
- data/test/rubygems/test_gem_indexer.rb +14 -227
- data/test/rubygems/test_gem_install_update_options.rb +83 -3
- data/test/rubygems/test_gem_installer.rb +211 -236
- data/test/rubygems/test_gem_local_remote_options.rb +8 -2
- data/test/rubygems/test_gem_name_tuple.rb +15 -0
- data/test/rubygems/test_gem_package.rb +547 -0
- data/test/rubygems/test_gem_package_old.rb +37 -0
- data/test/rubygems/test_gem_package_tar_reader.rb +32 -0
- data/test/rubygems/test_gem_package_tar_writer.rb +84 -1
- data/test/rubygems/test_gem_path_support.rb +4 -30
- data/test/rubygems/test_gem_platform.rb +3 -6
- data/test/rubygems/test_gem_rdoc.rb +245 -0
- data/test/rubygems/test_gem_remote_fetcher.rb +51 -5
- data/test/rubygems/test_gem_request_set.rb +70 -0
- data/test/rubygems/test_gem_requirement.rb +53 -24
- data/test/rubygems/test_gem_security.rb +189 -43
- data/test/rubygems/test_gem_security_policy.rb +376 -0
- data/test/rubygems/test_gem_security_signer.rb +184 -0
- data/test/rubygems/test_gem_security_trust_dir.rb +94 -0
- data/test/rubygems/test_gem_server.rb +31 -36
- data/test/rubygems/test_gem_silent_ui.rb +2 -2
- data/test/rubygems/test_gem_source.rb +188 -0
- data/test/rubygems/test_gem_source_list.rb +87 -0
- data/test/rubygems/test_gem_source_local.rb +83 -0
- data/test/rubygems/test_gem_source_specific_file.rb +33 -0
- data/test/rubygems/test_gem_spec_fetcher.rb +91 -255
- data/test/rubygems/test_gem_specification.rb +293 -39
- data/test/rubygems/test_gem_uninstaller.rb +136 -13
- data/test/rubygems/test_gem_validator.rb +14 -41
- data/test/rubygems/test_gem_version.rb +15 -21
- data/test/rubygems/test_require.rb +193 -0
- data/test/rubygems/wrong_key_cert.pem +9 -0
- data/test/rubygems/wrong_key_cert_32.pem +9 -0
- metadata +171 -83
- metadata.gz.sig +1 -0
- data/CVE-2013-4287.txt +0 -36
- data/CVE-2013-4363.txt +0 -45
- data/ci_build.sh +0 -27
- data/cruise_config.rb +0 -32
- data/lib/rbconfig/datadir.rb +0 -13
- data/lib/rubygems/builder.rb +0 -99
- data/lib/rubygems/custom_require.rb +0 -69
- data/lib/rubygems/doc_manager.rb +0 -243
- data/lib/rubygems/format.rb +0 -82
- data/lib/rubygems/gem_openssl.rb +0 -90
- data/lib/rubygems/gem_path_searcher.rb +0 -172
- data/lib/rubygems/old_format.rb +0 -153
- data/lib/rubygems/package/f_sync_dir.rb +0 -23
- data/lib/rubygems/package/tar_input.rb +0 -234
- data/lib/rubygems/package/tar_output.rb +0 -146
- data/lib/rubygems/require_paths_builder.rb +0 -18
- data/lib/rubygems/source_index.rb +0 -406
- data/lib/rubygems/ssl_certs/AddTrustExternalCARoot-2048.pem +0 -25
- data/lib/rubygems/ssl_certs/Class3PublicPrimaryCertificationAuthority.pem +0 -14
- data/lib/rubygems/ssl_certs/DigiCertHighAssuranceEVRootCA.pem +0 -23
- data/lib/rubygems/ssl_certs/EntrustnetSecureServerCertificationAuthority.pem +0 -28
- data/lib/rubygems/ssl_certs/GeoTrustGlobalCA.pem +0 -20
- data/test/rubygems/test_bundled_ca.rb +0 -59
- data/test/rubygems/test_gem_builder.rb +0 -44
- data/test/rubygems/test_gem_doc_manager.rb +0 -32
- data/test/rubygems/test_gem_ext_builder.rb +0 -58
- data/test/rubygems/test_gem_format.rb +0 -88
- data/test/rubygems/test_gem_gem_path_searcher.rb +0 -94
- data/test/rubygems/test_gem_package_tar_input.rb +0 -129
- data/test/rubygems/test_gem_package_tar_output.rb +0 -101
- data/test/rubygems/test_gem_source_index.rb +0 -250
- data/util/update_bundled_ca_certificates.rb +0 -103
@@ -0,0 +1,182 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rubygems/dependency'
|
3
|
+
require 'rubygems/dependency_resolver'
|
4
|
+
require 'rubygems/dependency_list'
|
5
|
+
require 'rubygems/installer'
|
6
|
+
require 'tsort'
|
7
|
+
|
8
|
+
module Gem
|
9
|
+
class RequestSet
|
10
|
+
|
11
|
+
include TSort
|
12
|
+
|
13
|
+
def initialize(*deps)
|
14
|
+
@dependencies = deps
|
15
|
+
|
16
|
+
yield self if block_given?
|
17
|
+
end
|
18
|
+
|
19
|
+
attr_reader :dependencies
|
20
|
+
|
21
|
+
# Declare that a gem of name +name+ with +reqs+ requirements
|
22
|
+
# is needed.
|
23
|
+
#
|
24
|
+
def gem(name, *reqs)
|
25
|
+
@dependencies << Gem::Dependency.new(name, reqs)
|
26
|
+
end
|
27
|
+
|
28
|
+
# Add +deps+ Gem::Depedency objects to the set.
|
29
|
+
#
|
30
|
+
def import(deps)
|
31
|
+
@dependencies += deps
|
32
|
+
end
|
33
|
+
|
34
|
+
# Resolve the requested dependencies and return an Array of
|
35
|
+
# Specification objects to be activated.
|
36
|
+
#
|
37
|
+
def resolve(set=nil)
|
38
|
+
r = Gem::DependencyResolver.new(@dependencies, set)
|
39
|
+
@requests = r.resolve
|
40
|
+
end
|
41
|
+
|
42
|
+
# Resolve the requested dependencies against the gems
|
43
|
+
# available via Gem.path and return an Array of Specification
|
44
|
+
# objects to be activated.
|
45
|
+
#
|
46
|
+
def resolve_current
|
47
|
+
resolve DependencyResolver::CurrentSet.new
|
48
|
+
end
|
49
|
+
|
50
|
+
# Load a dependency management file.
|
51
|
+
#
|
52
|
+
def load_gemdeps(path)
|
53
|
+
gf = GemDepedencyAPI.new(self, path)
|
54
|
+
gf.load
|
55
|
+
end
|
56
|
+
|
57
|
+
def specs
|
58
|
+
@specs ||= @requests.map { |r| r.full_spec }
|
59
|
+
end
|
60
|
+
|
61
|
+
def tsort_each_node(&block)
|
62
|
+
@requests.each(&block)
|
63
|
+
end
|
64
|
+
|
65
|
+
def tsort_each_child(node)
|
66
|
+
node.spec.dependencies.each do |dep|
|
67
|
+
next if dep.type == :development
|
68
|
+
|
69
|
+
match = @requests.find { |r| dep.match? r.spec.name, r.spec.version }
|
70
|
+
if match
|
71
|
+
begin
|
72
|
+
yield match
|
73
|
+
rescue TSort::Cyclic
|
74
|
+
end
|
75
|
+
else
|
76
|
+
raise Gem::DependencyError, "Unresolved depedency found during sorting - #{dep}"
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def sorted_requests
|
82
|
+
@sorted ||= strongly_connected_components.flatten
|
83
|
+
end
|
84
|
+
|
85
|
+
def specs_in(dir)
|
86
|
+
Dir["#{dir}/specifications/*.gemspec"].map do |g|
|
87
|
+
Gem::Specification.load g
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
def install_into(dir, force=true, &b)
|
92
|
+
existing = force ? [] : specs_in(dir)
|
93
|
+
|
94
|
+
dir = File.expand_path dir
|
95
|
+
|
96
|
+
installed = []
|
97
|
+
|
98
|
+
sorted_requests.each do |req|
|
99
|
+
if existing.find { |s| s.full_name == req.spec.full_name }
|
100
|
+
b.call req, nil if b
|
101
|
+
next
|
102
|
+
end
|
103
|
+
|
104
|
+
path = req.download(dir)
|
105
|
+
|
106
|
+
inst = Gem::Installer.new path, :install_dir => dir,
|
107
|
+
:only_install_dir => true
|
108
|
+
|
109
|
+
b.call req, inst if b
|
110
|
+
|
111
|
+
inst.install
|
112
|
+
|
113
|
+
installed << req
|
114
|
+
end
|
115
|
+
|
116
|
+
installed
|
117
|
+
end
|
118
|
+
|
119
|
+
def install(options, &b)
|
120
|
+
if dir = options[:install_dir]
|
121
|
+
return install_into(dir, false, &b)
|
122
|
+
end
|
123
|
+
|
124
|
+
cache_dir = options[:cache_dir] || Gem.dir
|
125
|
+
|
126
|
+
specs = []
|
127
|
+
|
128
|
+
sorted_requests.each do |req|
|
129
|
+
if req.installed?
|
130
|
+
b.call req, nil if b
|
131
|
+
next
|
132
|
+
end
|
133
|
+
|
134
|
+
path = req.download cache_dir
|
135
|
+
|
136
|
+
inst = Gem::Installer.new path, options
|
137
|
+
|
138
|
+
b.call req, inst if b
|
139
|
+
|
140
|
+
specs << inst.install
|
141
|
+
end
|
142
|
+
|
143
|
+
specs
|
144
|
+
end
|
145
|
+
|
146
|
+
# A semi-compatible DSL for Bundler's Gemfile format
|
147
|
+
#
|
148
|
+
class GemDepedencyAPI
|
149
|
+
def initialize(set, path)
|
150
|
+
@set = set
|
151
|
+
@path = path
|
152
|
+
end
|
153
|
+
|
154
|
+
def load
|
155
|
+
instance_eval File.read(@path).untaint, @path, 1
|
156
|
+
end
|
157
|
+
|
158
|
+
# DSL
|
159
|
+
|
160
|
+
def source(url)
|
161
|
+
end
|
162
|
+
|
163
|
+
def gem(name, *reqs)
|
164
|
+
# Ignore the opts for now.
|
165
|
+
reqs.pop if reqs.last.kind_of?(Hash)
|
166
|
+
|
167
|
+
@set.gem name, *reqs
|
168
|
+
end
|
169
|
+
|
170
|
+
def platform(what)
|
171
|
+
if what == :ruby
|
172
|
+
yield
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
alias_method :platforms, :platform
|
177
|
+
|
178
|
+
def group(*what)
|
179
|
+
end
|
180
|
+
end
|
181
|
+
end
|
182
|
+
end
|
data/lib/rubygems/requirement.rb
CHANGED
@@ -1,5 +1,3 @@
|
|
1
|
-
require "rubygems/version"
|
2
|
-
|
3
1
|
##
|
4
2
|
# A Requirement is a set of one or more version restrictions. It supports a
|
5
3
|
# few (<tt>=, !=, >, <, >=, <=, ~></tt>) different restriction operators.
|
@@ -13,21 +11,28 @@ require "rubygems/version"
|
|
13
11
|
require "rubygems/version"
|
14
12
|
require "rubygems/deprecate"
|
15
13
|
|
16
|
-
|
17
|
-
|
14
|
+
# If we're being loaded after yaml was already required, then
|
15
|
+
# load our yaml + workarounds now.
|
16
|
+
Gem.load_yaml if defined? ::YAML
|
18
17
|
|
18
|
+
class Gem::Requirement
|
19
19
|
OPS = { #:nodoc:
|
20
20
|
"=" => lambda { |v, r| v == r },
|
21
21
|
"!=" => lambda { |v, r| v != r },
|
22
|
-
">" => lambda { |v, r| v >
|
23
|
-
"<" => lambda { |v, r| v <
|
22
|
+
">" => lambda { |v, r| v > r },
|
23
|
+
"<" => lambda { |v, r| v < r },
|
24
24
|
">=" => lambda { |v, r| v >= r },
|
25
25
|
"<=" => lambda { |v, r| v <= r },
|
26
26
|
"~>" => lambda { |v, r| v >= r && v.release < r.bump }
|
27
27
|
}
|
28
28
|
|
29
29
|
quoted = OPS.keys.map { |k| Regexp.quote k }.join "|"
|
30
|
-
|
30
|
+
PATTERN_RAW = "\\s*(#{quoted})?\\s*(#{Gem::Version::VERSION_PATTERN})\\s*"
|
31
|
+
PATTERN = /\A#{PATTERN_RAW}\z/
|
32
|
+
|
33
|
+
DefaultRequirement = [">=", Gem::Version.new(0)]
|
34
|
+
|
35
|
+
class BadRequirementError < ArgumentError; end
|
31
36
|
|
32
37
|
##
|
33
38
|
# Factory method to create a Gem::Requirement object. Input may be
|
@@ -36,6 +41,9 @@ class Gem::Requirement
|
|
36
41
|
# If the input is "weird", the default version requirement is
|
37
42
|
# returned.
|
38
43
|
|
44
|
+
# REFACTOR: There's no reason that this can't be unified with .new.
|
45
|
+
# .new is the standard Ruby factory method.
|
46
|
+
|
39
47
|
def self.create input
|
40
48
|
case input
|
41
49
|
when Gem::Requirement then
|
@@ -53,10 +61,6 @@ class Gem::Requirement
|
|
53
61
|
|
54
62
|
##
|
55
63
|
# A default "version requirement" can surely _only_ be '>= 0'.
|
56
|
-
#--
|
57
|
-
# This comment once said:
|
58
|
-
#
|
59
|
-
# "A default "version requirement" can surely _only_ be '> 0'."
|
60
64
|
|
61
65
|
def self.default
|
62
66
|
new '>= 0'
|
@@ -74,14 +78,23 @@ class Gem::Requirement
|
|
74
78
|
# parse("1.0") # => ["=", "1.0"]
|
75
79
|
# parse(Gem::Version.new("1.0")) # => ["=, "1.0"]
|
76
80
|
|
81
|
+
# REFACTOR: Little two element arrays like this have no real semantic
|
82
|
+
# value. I'd love to see something like this:
|
83
|
+
# Constraint = Struct.new(:operator, :version); (or similar)
|
84
|
+
# and have a Requirement be a list of Constraints.
|
85
|
+
|
77
86
|
def self.parse obj
|
78
87
|
return ["=", obj] if Gem::Version === obj
|
79
88
|
|
80
89
|
unless PATTERN =~ obj.to_s
|
81
|
-
raise
|
90
|
+
raise BadRequirementError, "Illformed requirement [#{obj.inspect}]"
|
82
91
|
end
|
83
92
|
|
84
|
-
|
93
|
+
if $1 == ">=" && $2 == "0"
|
94
|
+
DefaultRequirement
|
95
|
+
else
|
96
|
+
[$1 || "=", Gem::Version.new($2)]
|
97
|
+
end
|
85
98
|
end
|
86
99
|
|
87
100
|
##
|
@@ -101,13 +114,23 @@ class Gem::Requirement
|
|
101
114
|
requirements.compact!
|
102
115
|
requirements.uniq!
|
103
116
|
|
104
|
-
|
105
|
-
|
106
|
-
|
117
|
+
if requirements.empty?
|
118
|
+
@requirements = [DefaultRequirement]
|
119
|
+
else
|
120
|
+
@requirements = requirements.map! { |r| self.class.parse r }
|
121
|
+
end
|
107
122
|
end
|
108
123
|
|
124
|
+
##
|
125
|
+
# true if this gem has no requirements.
|
126
|
+
|
127
|
+
# FIX: maybe this should be using #default ?
|
109
128
|
def none?
|
110
|
-
@
|
129
|
+
if @requirements.size == 1
|
130
|
+
@requirements[0] == DefaultRequirement
|
131
|
+
else
|
132
|
+
false
|
133
|
+
end
|
111
134
|
end
|
112
135
|
|
113
136
|
def as_list # :nodoc:
|
@@ -143,6 +166,18 @@ class Gem::Requirement
|
|
143
166
|
yaml_initialize coder.tag, coder.map
|
144
167
|
end
|
145
168
|
|
169
|
+
def to_yaml_properties
|
170
|
+
["@requirements"]
|
171
|
+
end
|
172
|
+
|
173
|
+
def encode_with(coder)
|
174
|
+
coder.add 'requirements', @requirements
|
175
|
+
end
|
176
|
+
|
177
|
+
##
|
178
|
+
# A requirement is a prerelease if any of the versions inside of it
|
179
|
+
# are prereleases
|
180
|
+
|
146
181
|
def prerelease?
|
147
182
|
requirements.any? { |r| r.last.prerelease? }
|
148
183
|
end
|
@@ -157,6 +192,8 @@ class Gem::Requirement
|
|
157
192
|
# True if +version+ satisfies this Requirement.
|
158
193
|
|
159
194
|
def satisfied_by? version
|
195
|
+
raise ArgumentError, "Need a Gem::Version: #{version.inspect}" unless
|
196
|
+
Gem::Version === version
|
160
197
|
# #28965: syck has a bug with unquoted '=' YAML.loading as YAML::DefaultKey
|
161
198
|
requirements.all? { |op, rv| (OPS[op] || OPS["="]).call version, rv }
|
162
199
|
end
|
@@ -177,12 +214,14 @@ class Gem::Requirement
|
|
177
214
|
as_list.join ", "
|
178
215
|
end
|
179
216
|
|
180
|
-
|
181
|
-
|
217
|
+
# DOC: this should probably be :nodoc'd
|
218
|
+
def == other
|
219
|
+
Gem::Requirement === other and to_s == other.to_s
|
182
220
|
end
|
183
221
|
|
184
222
|
private
|
185
223
|
|
224
|
+
# DOC: this should probably be :nodoc'd
|
186
225
|
def fix_syck_default_key_in_requirements
|
187
226
|
Gem.load_yaml
|
188
227
|
|
@@ -195,11 +234,9 @@ class Gem::Requirement
|
|
195
234
|
end
|
196
235
|
end
|
197
236
|
|
198
|
-
#
|
199
|
-
#
|
200
|
-
# here for backwards compatibility. I'd like to remove this, maybe in RubyGems
|
201
|
-
# 2.0.
|
202
|
-
|
203
|
-
::Gem::Version::Requirement = ::Gem::Requirement
|
204
|
-
# :startdoc:
|
237
|
+
# This is needed for compatibility with older yaml
|
238
|
+
# gemspecs.
|
205
239
|
|
240
|
+
class Gem::Version
|
241
|
+
Requirement = Gem::Requirement
|
242
|
+
end
|
data/lib/rubygems/security.rb
CHANGED
@@ -5,80 +5,89 @@
|
|
5
5
|
#++
|
6
6
|
|
7
7
|
require 'rubygems/exceptions'
|
8
|
-
require '
|
8
|
+
require 'openssl'
|
9
9
|
require 'fileutils'
|
10
10
|
|
11
|
+
##
|
12
|
+
# = Signing gems
|
11
13
|
#
|
12
|
-
#
|
13
|
-
#
|
14
|
-
# == Table of Contents
|
15
|
-
# * Overview
|
16
|
-
# * Walkthrough
|
17
|
-
# * Command-Line Options
|
18
|
-
# * OpenSSL Reference
|
19
|
-
# * Bugs/TODO
|
20
|
-
# * About the Author
|
21
|
-
#
|
22
|
-
# == Overview
|
23
|
-
#
|
24
|
-
# Gem::Security implements cryptographic signatures in RubyGems. The section
|
14
|
+
# The Gem::Security implements cryptographic signatures for gems. The section
|
25
15
|
# below is a step-by-step guide to using signed gems and generating your own.
|
26
16
|
#
|
27
17
|
# == Walkthrough
|
28
18
|
#
|
19
|
+
# === Building your certificate
|
20
|
+
#
|
29
21
|
# In order to start signing your gems, you'll need to build a private key and
|
30
22
|
# a self-signed certificate. Here's how:
|
31
23
|
#
|
32
|
-
# # build a private key and certificate for
|
33
|
-
# $ gem cert --build
|
24
|
+
# # build a private key and certificate for yourself:
|
25
|
+
# $ gem cert --build you@example.com
|
34
26
|
#
|
35
|
-
# This could take anywhere from
|
36
|
-
# speed of your computer (public key algorithms aren't exactly the
|
37
|
-
# crypto algorithms in the world). When it's finished, you'll see
|
38
|
-
# "gem-private_key.pem" and "gem-public_cert.pem" in the current
|
27
|
+
# This could take anywhere from a few seconds to a minute or two, depending on
|
28
|
+
# the speed of your computer (public key algorithms aren't exactly the
|
29
|
+
# speediest crypto algorithms in the world). When it's finished, you'll see
|
30
|
+
# the files "gem-private_key.pem" and "gem-public_cert.pem" in the current
|
31
|
+
# directory.
|
39
32
|
#
|
40
|
-
# First things first:
|
41
|
-
#
|
42
|
-
#
|
43
|
-
# hidden; if it's compromised, someone can sign packages as you (note: PKI has
|
44
|
-
# ways of mitigating the risk of stolen keys; more on that later).
|
33
|
+
# First things first: Move both files to ~/.gem if you don't already have a
|
34
|
+
# key and certificate in that directory. Ensure the file permissions make the
|
35
|
+
# key unreadable by others (by default the file is saved securely).
|
45
36
|
#
|
46
|
-
#
|
47
|
-
# you
|
48
|
-
#
|
37
|
+
# Keep your private key hidden; if it's compromised, someone can sign packages
|
38
|
+
# as you (note: PKI has ways of mitigating the risk of stolen keys; more on
|
39
|
+
# that later).
|
49
40
|
#
|
50
|
-
#
|
51
|
-
# s.signing_key = '/mnt/floppy/gem-private_key.pem'
|
52
|
-
# s.cert_chain = ['gem-public_cert.pem']
|
41
|
+
# === Signing Gems
|
53
42
|
#
|
54
|
-
#
|
55
|
-
# key
|
43
|
+
# In RubyGems 2 and newer there is no extra work to sign a gem. RubyGems will
|
44
|
+
# automatically find your key and certificate in your home directory and use
|
45
|
+
# them to sign newly packaged gems.
|
56
46
|
#
|
57
|
-
#
|
58
|
-
#
|
59
|
-
#
|
47
|
+
# If your certificate is not self-signed (signed by a third party) RubyGems
|
48
|
+
# will attempt to load the certificate chain from the trusted certificates.
|
49
|
+
# Use <code>gem cert --add signing_cert.pem</code> to add your signers as
|
50
|
+
# trusted certificates. See below for further information on certificate
|
51
|
+
# chains.
|
60
52
|
#
|
61
|
-
#
|
62
|
-
#
|
63
|
-
#
|
53
|
+
# If you build your gem it will automatically be signed. If you peek inside
|
54
|
+
# your gem file, you'll see a couple of new files have been added:
|
55
|
+
#
|
56
|
+
# $ tar tf your-gem-1.0.gem
|
64
57
|
# metadata.gz
|
65
|
-
# metadata.gz.
|
58
|
+
# metadata.gz.sum
|
59
|
+
# metadata.gz.sig # metadata signature
|
60
|
+
# data.tar.gz
|
61
|
+
# data.tar.gz.sum
|
62
|
+
# data.tar.gz.sig # data signature
|
63
|
+
#
|
64
|
+
# === Manually signing gems
|
65
|
+
#
|
66
|
+
# If you wish to store your key in a separate secure location you'll need to
|
67
|
+
# set your gems up for signing by hand. To do this, set the
|
68
|
+
# <code>signing_key</code> and <code>cert_chain</code> in the gemspec before
|
69
|
+
# packaging your gem:
|
70
|
+
#
|
71
|
+
# s.signing_key = '/secure/path/to/gem-private_key.pem'
|
72
|
+
# s.cert_chain = %w[/secure/path/to/gem-public_cert.pem]
|
73
|
+
#
|
74
|
+
# When you package your gem with these options set RubyGems will automatically
|
75
|
+
# load your key and certificate from the secure paths.
|
76
|
+
#
|
77
|
+
# === Signed gems and security policies
|
66
78
|
#
|
67
79
|
# Now let's verify the signature. Go ahead and install the gem, but add the
|
68
|
-
# following options:
|
80
|
+
# following options: <code>-P HighSecurity</code>, like this:
|
69
81
|
#
|
70
82
|
# # install the gem with using the security policy "HighSecurity"
|
71
|
-
# $ sudo gem install
|
83
|
+
# $ sudo gem install your.gem -P HighSecurity
|
72
84
|
#
|
73
|
-
# The
|
74
|
-
# minute. Eh, what's this?
|
85
|
+
# The <code>-P</code> option sets your security policy -- we'll talk about
|
86
|
+
# that in just a minute. Eh, what's this?
|
75
87
|
#
|
76
|
-
#
|
77
|
-
# ERROR:
|
78
|
-
#
|
79
|
-
# '/CN=gemmaster/DC=example/DC=com', error = 'path
|
80
|
-
# "/root/.rubygems/trust/cert-15dbb43a6edf6a70a85d4e784e2e45312cff7030.pem"
|
81
|
-
# does not exist'
|
88
|
+
# $ gem install -P HighSecurity your-gem-1.0.gem
|
89
|
+
# ERROR: While executing gem ... (Gem::Security::Exception)
|
90
|
+
# root cert /CN=you/DC=example is not trusted
|
82
91
|
#
|
83
92
|
# The culprit here is the security policy. RubyGems has several different
|
84
93
|
# security policies. Let's take a short break and go over the security
|
@@ -111,46 +120,48 @@ require 'fileutils'
|
|
111
120
|
# RubyGems will simply refuse to install the package. Oh well, maybe
|
112
121
|
# he'll have better luck causing problems for CPAN users instead :).
|
113
122
|
#
|
114
|
-
#
|
115
|
-
#
|
116
|
-
#
|
117
|
-
#
|
118
|
-
# Here's how:
|
123
|
+
# The reason RubyGems refused to install your shiny new signed gem was because
|
124
|
+
# it was from an untrusted source. Well, your code is infallible (naturally),
|
125
|
+
# so you need to add yourself as a trusted source:
|
119
126
|
#
|
120
|
-
#
|
121
|
-
#
|
127
|
+
# # add trusted certificate
|
128
|
+
# gem cert --add ~/.gem/gem-public_cert.pem
|
122
129
|
#
|
123
|
-
#
|
124
|
-
# packages signed
|
125
|
-
# command above again:
|
130
|
+
# You've now added your public certificate as a trusted source. Now you can
|
131
|
+
# install packages signed by your private key without any hassle. Let's try
|
132
|
+
# the install command above again:
|
126
133
|
#
|
127
134
|
# # install the gem with using the HighSecurity policy (and this time
|
128
135
|
# # without any shenanigans)
|
129
|
-
# $
|
136
|
+
# $ gem install -P HighSecurity your-gem-1.0.gem
|
137
|
+
# Successfully installed your-gem-1.0
|
138
|
+
# 1 gem installed
|
130
139
|
#
|
131
|
-
# This time RubyGems
|
132
|
-
# While you're waiting for RubyGems to work it's magic, have a look at some of
|
133
|
-
# the other security commands:
|
140
|
+
# This time RubyGems will accept your signed package and begin installing.
|
134
141
|
#
|
135
|
-
#
|
142
|
+
# While you're waiting for RubyGems to work it's magic, have a look at some of
|
143
|
+
# the other security commands by running <code>gem help cert</code>:
|
136
144
|
#
|
137
145
|
# Options:
|
138
|
-
# -a, --add CERT
|
139
|
-
# -l, --list List trusted certificates
|
140
|
-
#
|
141
|
-
# -
|
142
|
-
#
|
143
|
-
# -
|
144
|
-
#
|
145
|
-
# -
|
146
|
-
#
|
147
|
-
#
|
148
|
-
#
|
149
|
-
#
|
150
|
-
#
|
151
|
-
# and
|
152
|
-
#
|
153
|
-
# what's with this
|
146
|
+
# -a, --add CERT Add a trusted certificate.
|
147
|
+
# -l, --list [FILTER] List trusted certificates where the
|
148
|
+
# subject contains FILTER
|
149
|
+
# -r, --remove FILTER Remove trusted certificates where the
|
150
|
+
# subject contains FILTER
|
151
|
+
# -b, --build EMAIL_ADDR Build private key and self-signed
|
152
|
+
# certificate for EMAIL_ADDR
|
153
|
+
# -C, --certificate CERT Signing certificate for --sign
|
154
|
+
# -K, --private-key KEY Key for --sign or --build
|
155
|
+
# -s, --sign CERT Signs CERT with the key from -K
|
156
|
+
# and the certificate from -C
|
157
|
+
#
|
158
|
+
# We've already covered the <code>--build</code> option, and the
|
159
|
+
# <code>--add</code>, <code>--list</code>, and <code>--remove</code> commands
|
160
|
+
# seem fairly straightforward; they allow you to add, list, and remove the
|
161
|
+
# certificates in your trusted certificate list. But what's with this
|
162
|
+
# <code>--sign</code> option?
|
163
|
+
#
|
164
|
+
# === Certificate chains
|
154
165
|
#
|
155
166
|
# To answer that question, let's take a look at "certificate chains", a
|
156
167
|
# concept I mentioned earlier. There are a couple of problems with
|
@@ -172,134 +183,102 @@ require 'fileutils'
|
|
172
183
|
# trust. Here's a hypothetical example of a trust hierarchy based (roughly)
|
173
184
|
# on geography:
|
174
185
|
#
|
175
|
-
#
|
176
186
|
# --------------------------
|
177
|
-
# | rubygems@
|
187
|
+
# | rubygems@rubygems.org |
|
178
188
|
# --------------------------
|
179
189
|
# |
|
180
190
|
# -----------------------------------
|
181
191
|
# | |
|
182
192
|
# ---------------------------- -----------------------------
|
183
|
-
# |
|
193
|
+
# | seattlerb@seattlerb.org | | dcrubyists@richkilmer.com |
|
184
194
|
# ---------------------------- -----------------------------
|
185
195
|
# | | | |
|
186
196
|
# --------------- ---------------- ----------- --------------
|
187
|
-
# |
|
197
|
+
# | drbrain | | zenspider | | pabs@dc | | tomcope@dc |
|
188
198
|
# --------------- ---------------- ----------- --------------
|
189
199
|
#
|
190
200
|
#
|
191
|
-
# Now, rather than having 4 trusted certificates (one for
|
192
|
-
#
|
193
|
-
# certificate
|
201
|
+
# Now, rather than having 4 trusted certificates (one for drbrain, zenspider,
|
202
|
+
# pabs@dc, and tomecope@dc), a user could actually get by with one
|
203
|
+
# certificate, the "rubygems@rubygems.org" certificate.
|
204
|
+
#
|
205
|
+
# Here's how it works:
|
206
|
+
#
|
207
|
+
# I install "rdoc-3.12.gem", a package signed by "drbrain". I've never heard
|
208
|
+
# of "drbrain", but his certificate has a valid signature from the
|
209
|
+
# "seattle.rb@seattlerb.org" certificate, which in turn has a valid signature
|
210
|
+
# from the "rubygems@rubygems.org" certificate. Voila! At this point, it's
|
211
|
+
# much more reasonable for me to trust a package signed by "drbrain", because
|
212
|
+
# I can establish a chain to "rubygems@rubygems.org", which I do trust.
|
194
213
|
#
|
195
|
-
#
|
196
|
-
# never heard of "alf@seattle", but his certificate has a valid signature from
|
197
|
-
# the "seattle.rb@zenspider.com" certificate, which in turn has a valid
|
198
|
-
# signature from the "rubygems@rubyforge.org" certificate. Voila! At this
|
199
|
-
# point, it's much more reasonable for me to trust a package signed by
|
200
|
-
# "alf@seattle", because I can establish a chain to "rubygems@rubyforge.org",
|
201
|
-
# which I do trust.
|
214
|
+
# === Signing certificates
|
202
215
|
#
|
203
|
-
#
|
204
|
-
# their build certificate with the
|
205
|
-
# certificate signed by taking it with them to their next regional
|
206
|
-
# (in our hypothetical example), and it's signed there by the
|
207
|
-
# the regional RubyGems signing certificate, which is signed at
|
208
|
-
# RubyConf by the holder of the top-level RubyGems certificate. At
|
209
|
-
# the issuer runs the same command:
|
216
|
+
# The <code>--sign</code> option allows all this to happen. A developer
|
217
|
+
# creates their build certificate with the <code>--build</code> option, then
|
218
|
+
# has their certificate signed by taking it with them to their next regional
|
219
|
+
# Ruby meetup (in our hypothetical example), and it's signed there by the
|
220
|
+
# person holding the regional RubyGems signing certificate, which is signed at
|
221
|
+
# the next RubyConf by the holder of the top-level RubyGems certificate. At
|
222
|
+
# each point the issuer runs the same command:
|
210
223
|
#
|
211
224
|
# # sign a certificate with the specified key and certificate
|
212
225
|
# # (note that this modifies client_cert.pem!)
|
213
226
|
# $ gem cert -K /mnt/floppy/issuer-priv_key.pem -C issuer-pub_cert.pem
|
214
227
|
# --sign client_cert.pem
|
215
228
|
#
|
216
|
-
# Then the holder of issued certificate (in this case,
|
217
|
-
#
|
218
|
-
#
|
219
|
-
#
|
229
|
+
# Then the holder of issued certificate (in this case, your buddy "drbrain"),
|
230
|
+
# can start using this signed certificate to sign RubyGems. By the way, in
|
231
|
+
# order to let everyone else know about his new fancy signed certificate,
|
232
|
+
# "drbrain" would save his newly signed certificate as
|
233
|
+
# <code>~/.gem/gem-public_cert.pem</code>
|
220
234
|
#
|
221
|
-
#
|
222
|
-
#
|
223
|
-
#
|
224
|
-
# # certificate chain (includes the issuer certificate now too)
|
225
|
-
# s.cert_chain = ['/home/alf/doc/seattlerb-public_cert.pem',
|
226
|
-
# '/home/alf/doc/alf_at_seattle-public_cert.pem']
|
227
|
-
#
|
228
|
-
# Obviously, this RubyGems trust infrastructure doesn't exist yet. Also, in
|
229
|
-
# the "real world" issuers actually generate the child certificate from a
|
235
|
+
# Obviously this RubyGems trust infrastructure doesn't exist yet. Also, in
|
236
|
+
# the "real world", issuers actually generate the child certificate from a
|
230
237
|
# certificate request, rather than sign an existing certificate. And our
|
231
238
|
# hypothetical infrastructure is missing a certificate revocation system.
|
232
239
|
# These are that can be fixed in the future...
|
233
240
|
#
|
234
|
-
#
|
235
|
-
#
|
236
|
-
# should know how to do all of these new and interesting things:
|
241
|
+
# At this point you should know how to do all of these new and interesting
|
242
|
+
# things:
|
237
243
|
#
|
238
244
|
# * build a gem signing key and certificate
|
239
|
-
# * modify your existing gems to support signing
|
240
245
|
# * adjust your security policy
|
241
246
|
# * modify your trusted certificate list
|
242
247
|
# * sign a certificate
|
243
248
|
#
|
244
|
-
# If you've got any questions, feel free to contact me at the email address
|
245
|
-
# below. The next couple of sections
|
246
|
-
#
|
247
|
-
#
|
248
|
-
# == Command-Line Options
|
249
|
-
#
|
250
|
-
# Here's a brief summary of the certificate-related command line options:
|
251
|
-
#
|
252
|
-
# gem install
|
253
|
-
# -P, --trust-policy POLICY Specify gem trust policy.
|
254
|
-
#
|
255
|
-
# gem cert
|
256
|
-
# -a, --add CERT Add a trusted certificate.
|
257
|
-
# -l, --list List trusted certificates.
|
258
|
-
# -r, --remove STRING Remove trusted certificates containing
|
259
|
-
# STRING.
|
260
|
-
# -b, --build EMAIL_ADDR Build private key and self-signed
|
261
|
-
# certificate for EMAIL_ADDR.
|
262
|
-
# -C, --certificate CERT Certificate for --sign command.
|
263
|
-
# -K, --private-key KEY Private key for --sign command.
|
264
|
-
# -s, --sign NEWCERT Sign a certificate with my key and
|
265
|
-
# certificate.
|
266
|
-
#
|
267
|
-
# A more detailed description of each options is available in the walkthrough
|
268
|
-
# above.
|
269
|
-
#
|
270
249
|
# == Manually verifying signatures
|
271
250
|
#
|
272
251
|
# In case you don't trust RubyGems you can verify gem signatures manually:
|
273
252
|
#
|
274
253
|
# 1. Fetch and unpack the gem
|
275
254
|
#
|
276
|
-
#
|
277
|
-
#
|
255
|
+
# gem fetch some_signed_gem
|
256
|
+
# tar -xf some_signed_gem-1.0.gem
|
278
257
|
#
|
279
258
|
# 2. Grab the public key from the gemspec
|
280
259
|
#
|
281
|
-
#
|
282
|
-
#
|
260
|
+
# gem spec some_signed_gem-1.0.gem cert_chain | \
|
261
|
+
# ruby -ryaml -e 'puts YAML.load_documents($stdin)' > public_key.crt
|
283
262
|
#
|
284
263
|
# 3. Generate a SHA1 hash of the data.tar.gz
|
285
264
|
#
|
286
|
-
#
|
265
|
+
# openssl dgst -sha1 < data.tar.gz > my.hash
|
287
266
|
#
|
288
267
|
# 4. Verify the signature
|
289
268
|
#
|
290
|
-
#
|
291
|
-
#
|
269
|
+
# openssl rsautl -verify -inkey public_key.crt -certin \
|
270
|
+
# -in data.tar.gz.sig > verified.hash
|
292
271
|
#
|
293
272
|
# 5. Compare your hash to the verified hash
|
294
273
|
#
|
295
|
-
#
|
274
|
+
# diff -s verified.hash my.hash
|
296
275
|
#
|
297
276
|
# 6. Repeat 5 and 6 with metadata.gz
|
298
277
|
#
|
299
278
|
# == OpenSSL Reference
|
300
279
|
#
|
301
|
-
# The .pem files generated by --build and --sign are
|
302
|
-
#
|
280
|
+
# The .pem files generated by --build and --sign are PEM files. Here's a
|
281
|
+
# couple of useful OpenSSL commands for manipulating them:
|
303
282
|
#
|
304
283
|
# # convert a PEM format X509 certificate into DER format:
|
305
284
|
# # (note: Windows .cer files are X509 certificates in DER format)
|
@@ -321,8 +300,8 @@ require 'fileutils'
|
|
321
300
|
# * There's no way to define a system-wide trust list.
|
322
301
|
# * custom security policies (from a YAML file, etc)
|
323
302
|
# * Simple method to generate a signed certificate request
|
324
|
-
# * Support for OCSP, SCVP, CRLs, or some other form of cert
|
325
|
-
#
|
303
|
+
# * Support for OCSP, SCVP, CRLs, or some other form of cert status check
|
304
|
+
# (list is in order of preference)
|
326
305
|
# * Support for encrypted private keys
|
327
306
|
# * Some sort of semi-formal trust hierarchy (see long-winded explanation
|
328
307
|
# above)
|
@@ -332,17 +311,13 @@ require 'fileutils'
|
|
332
311
|
# MediumSecurity and HighSecurity policies)
|
333
312
|
# * Better explanation of X509 naming (ie, we don't have to use email
|
334
313
|
# addresses)
|
335
|
-
# * Possible alternate signing mechanisms (eg, via PGP). this could be done
|
336
|
-
# pretty easily by adding a :signing_type attribute to the gemspec, then add
|
337
|
-
# the necessary support in other places
|
338
314
|
# * Honor AIA field (see note about OCSP above)
|
339
|
-
# *
|
315
|
+
# * Honor extension restrictions
|
340
316
|
# * Might be better to store the certificate chain as a PKCS#7 or PKCS#12
|
341
|
-
# file, instead of an array embedded in the metadata.
|
342
|
-
# *
|
343
|
-
# they're assumed to be the same as what's set in Gem::Security::OPT)
|
317
|
+
# file, instead of an array embedded in the metadata.
|
318
|
+
# * Flexible signature and key algorithms, not hard-coded to RSA and SHA1.
|
344
319
|
#
|
345
|
-
# ==
|
320
|
+
# == Original author
|
346
321
|
#
|
347
322
|
# Paul Duncan <pabs@pablotron.org>
|
348
323
|
# http://pablotron.org/
|
@@ -355,472 +330,237 @@ module Gem::Security
|
|
355
330
|
class Exception < Gem::Exception; end
|
356
331
|
|
357
332
|
##
|
358
|
-
#
|
359
|
-
|
360
|
-
|
361
|
-
# private key options
|
362
|
-
:key_algo => Gem::SSL::PKEY_RSA,
|
363
|
-
:key_size => 2048,
|
364
|
-
|
365
|
-
# public cert options
|
366
|
-
:cert_age => 365 * 24 * 3600, # 1 year
|
367
|
-
:dgst_algo => Gem::SSL::DIGEST_SHA1,
|
368
|
-
|
369
|
-
# x509 certificate extensions
|
370
|
-
:cert_exts => {
|
371
|
-
'basicConstraints' => 'CA:FALSE',
|
372
|
-
'subjectKeyIdentifier' => 'hash',
|
373
|
-
'keyUsage' => 'keyEncipherment,dataEncipherment,digitalSignature',
|
374
|
-
},
|
375
|
-
|
376
|
-
# save the key and cert to a file in build_self_signed_cert()?
|
377
|
-
:save_key => true,
|
378
|
-
:save_cert => true,
|
379
|
-
|
380
|
-
# if you define either of these, then they'll be used instead of
|
381
|
-
# the output_fmt macro below
|
382
|
-
:save_key_path => nil,
|
383
|
-
:save_cert_path => nil,
|
384
|
-
|
385
|
-
# output name format for self-signed certs
|
386
|
-
:output_fmt => 'gem-%s.pem',
|
387
|
-
:munge_re => Regexp.new(/[^a-z0-9_.-]+/),
|
388
|
-
|
389
|
-
# output directory for trusted certificate checksums
|
390
|
-
:trust_dir => File.join(Gem.user_home, '.gem', 'trust'),
|
391
|
-
|
392
|
-
# default permissions for trust directory and certs
|
393
|
-
:perms => {
|
394
|
-
:trust_dir => 0700,
|
395
|
-
:trusted_cert => 0600,
|
396
|
-
:signing_cert => 0600,
|
397
|
-
:signing_key => 0600,
|
398
|
-
},
|
399
|
-
}
|
333
|
+
# Digest algorithm used to sign gems
|
334
|
+
|
335
|
+
DIGEST_ALGORITHM = OpenSSL::Digest::SHA1
|
400
336
|
|
401
337
|
##
|
402
|
-
#
|
403
|
-
# signed gem files. This is the base class. You can either declare an
|
404
|
-
# instance of this or use one of the preset security policies below.
|
405
|
-
|
406
|
-
class Policy
|
407
|
-
attr_accessor :verify_data, :verify_signer, :verify_chain,
|
408
|
-
:verify_root, :only_trusted, :only_signed
|
409
|
-
|
410
|
-
#
|
411
|
-
# Create a new Gem::Security::Policy object with the given mode and
|
412
|
-
# options.
|
413
|
-
#
|
414
|
-
def initialize(policy = {}, opt = {})
|
415
|
-
# set options
|
416
|
-
@opt = Gem::Security::OPT.merge(opt)
|
417
|
-
|
418
|
-
# build policy
|
419
|
-
policy.each_pair do |key, val|
|
420
|
-
case key
|
421
|
-
when :verify_data then @verify_data = val
|
422
|
-
when :verify_signer then @verify_signer = val
|
423
|
-
when :verify_chain then @verify_chain = val
|
424
|
-
when :verify_root then @verify_root = val
|
425
|
-
when :only_trusted then @only_trusted = val
|
426
|
-
when :only_signed then @only_signed = val
|
427
|
-
end
|
428
|
-
end
|
429
|
-
end
|
338
|
+
# Used internally to select the signing digest from all computed digests
|
430
339
|
|
431
|
-
|
432
|
-
# Get the path to the file for this cert.
|
433
|
-
#
|
434
|
-
def self.trusted_cert_path(cert, opt = {})
|
435
|
-
opt = Gem::Security::OPT.merge(opt)
|
340
|
+
DIGEST_NAME = DIGEST_ALGORITHM.new.name # :nodoc:
|
436
341
|
|
437
|
-
|
438
|
-
|
439
|
-
dgst = algo.hexdigest(cert.subject.to_s)
|
342
|
+
##
|
343
|
+
# Algorithm for creating the key pair used to sign gems
|
440
344
|
|
441
|
-
|
442
|
-
name = "cert-#{dgst}.pem"
|
345
|
+
KEY_ALGORITHM = OpenSSL::PKey::RSA
|
443
346
|
|
444
|
-
|
445
|
-
|
446
|
-
end
|
347
|
+
##
|
348
|
+
# Length of keys created by KEY_ALGORITHM
|
447
349
|
|
448
|
-
|
449
|
-
# Verify that the gem data with the given signature and signing chain
|
450
|
-
# matched this security policy at the specified time.
|
451
|
-
#
|
452
|
-
def verify_gem(signature, data, chain, time = Time.now)
|
453
|
-
Gem.ensure_ssl_available
|
454
|
-
cert_class = OpenSSL::X509::Certificate
|
455
|
-
exc = Gem::Security::Exception
|
456
|
-
chain ||= []
|
457
|
-
|
458
|
-
chain = chain.map{ |str| cert_class.new(str) }
|
459
|
-
signer, ch_len = chain[-1], chain.size
|
460
|
-
|
461
|
-
# make sure signature is valid
|
462
|
-
if @verify_data
|
463
|
-
# get digest algorithm (TODO: this should be configurable)
|
464
|
-
dgst = @opt[:dgst_algo]
|
465
|
-
|
466
|
-
# verify the data signature (this is the most important part, so don't
|
467
|
-
# screw it up :D)
|
468
|
-
v = signer.public_key.verify(dgst.new, signature, data)
|
469
|
-
raise exc, "Invalid Gem Signature" unless v
|
470
|
-
|
471
|
-
# make sure the signer is valid
|
472
|
-
if @verify_signer
|
473
|
-
# make sure the signing cert is valid right now
|
474
|
-
v = signer.check_validity(nil, time)
|
475
|
-
raise exc, "Invalid Signature: #{v[:desc]}" unless v[:is_valid]
|
476
|
-
end
|
477
|
-
end
|
478
|
-
|
479
|
-
# make sure the certificate chain is valid
|
480
|
-
if @verify_chain
|
481
|
-
# iterate down over the chain and verify each certificate against it's
|
482
|
-
# issuer
|
483
|
-
(ch_len - 1).downto(1) do |i|
|
484
|
-
issuer, cert = chain[i - 1, 2]
|
485
|
-
v = cert.check_validity(issuer, time)
|
486
|
-
raise exc, "%s: cert = '%s', error = '%s'" % [
|
487
|
-
'Invalid Signing Chain', cert.subject, v[:desc]
|
488
|
-
] unless v[:is_valid]
|
489
|
-
end
|
490
|
-
|
491
|
-
# verify root of chain
|
492
|
-
if @verify_root
|
493
|
-
# make sure root is self-signed
|
494
|
-
root = chain[0]
|
495
|
-
raise exc, "%s: %s (subject = '%s', issuer = '%s')" % [
|
496
|
-
'Invalid Signing Chain Root',
|
497
|
-
'Subject does not match Issuer for Gem Signing Chain',
|
498
|
-
root.subject.to_s,
|
499
|
-
root.issuer.to_s,
|
500
|
-
] unless root.issuer.to_s == root.subject.to_s
|
501
|
-
|
502
|
-
# make sure root is valid
|
503
|
-
v = root.check_validity(root, time)
|
504
|
-
raise exc, "%s: cert = '%s', error = '%s'" % [
|
505
|
-
'Invalid Signing Chain Root', root.subject, v[:desc]
|
506
|
-
] unless v[:is_valid]
|
507
|
-
|
508
|
-
# verify that the chain root is trusted
|
509
|
-
if @only_trusted
|
510
|
-
# get digest algorithm, calculate checksum of root.subject
|
511
|
-
algo = @opt[:dgst_algo]
|
512
|
-
path = Gem::Security::Policy.trusted_cert_path(root, @opt)
|
513
|
-
|
514
|
-
# check to make sure trusted path exists
|
515
|
-
raise exc, "%s: cert = '%s', error = '%s'" % [
|
516
|
-
'Untrusted Signing Chain Root',
|
517
|
-
root.subject.to_s,
|
518
|
-
"path \"#{path}\" does not exist",
|
519
|
-
] unless File.exist?(path)
|
520
|
-
|
521
|
-
# load calculate digest from saved cert file
|
522
|
-
save_cert = OpenSSL::X509::Certificate.new(File.read(path))
|
523
|
-
save_dgst = algo.digest(save_cert.public_key.to_s)
|
524
|
-
|
525
|
-
# create digest of public key
|
526
|
-
pkey_str = root.public_key.to_s
|
527
|
-
cert_dgst = algo.digest(pkey_str)
|
528
|
-
|
529
|
-
# now compare the two digests, raise exception
|
530
|
-
# if they don't match
|
531
|
-
raise exc, "%s: %s (saved = '%s', root = '%s')" % [
|
532
|
-
'Invalid Signing Chain Root',
|
533
|
-
"Saved checksum doesn't match root checksum",
|
534
|
-
save_dgst, cert_dgst,
|
535
|
-
] unless save_dgst == cert_dgst
|
536
|
-
end
|
537
|
-
end
|
538
|
-
|
539
|
-
# return the signing chain
|
540
|
-
chain.map { |cert| cert.subject }
|
541
|
-
end
|
542
|
-
end
|
543
|
-
end
|
350
|
+
KEY_LENGTH = 2048
|
544
351
|
|
545
352
|
##
|
546
|
-
#
|
353
|
+
# One year in seconds
|
547
354
|
|
548
|
-
|
549
|
-
:verify_data => false,
|
550
|
-
:verify_signer => false,
|
551
|
-
:verify_chain => false,
|
552
|
-
:verify_root => false,
|
553
|
-
:only_trusted => false,
|
554
|
-
:only_signed => false
|
555
|
-
)
|
355
|
+
ONE_YEAR = 86400 * 365
|
556
356
|
|
557
357
|
##
|
558
|
-
#
|
559
|
-
# one that actually signed the data. Make no attempt to verify the signing
|
560
|
-
# certificate chain.
|
358
|
+
# The default set of extensions are:
|
561
359
|
#
|
562
|
-
#
|
563
|
-
#
|
564
|
-
|
565
|
-
|
566
|
-
|
567
|
-
|
568
|
-
|
569
|
-
|
570
|
-
|
571
|
-
|
572
|
-
|
360
|
+
# * The certificate is not a certificate authority
|
361
|
+
# * The key for the certificate may be used for key and data encipherment
|
362
|
+
# and digital signatures
|
363
|
+
# * The certificate contains a subject key identifier
|
364
|
+
|
365
|
+
EXTENSIONS = {
|
366
|
+
'basicConstraints' => 'CA:FALSE',
|
367
|
+
'keyUsage' =>
|
368
|
+
'keyEncipherment,dataEncipherment,digitalSignature',
|
369
|
+
'subjectKeyIdentifier' => 'hash',
|
370
|
+
}
|
573
371
|
|
574
|
-
|
575
|
-
|
576
|
-
|
577
|
-
|
578
|
-
# This policy is better than nothing, but can still be easily spoofed, and
|
579
|
-
# is not recommended.
|
580
|
-
|
581
|
-
LowSecurity = Policy.new(
|
582
|
-
:verify_data => true,
|
583
|
-
:verify_signer => true,
|
584
|
-
:verify_chain => false,
|
585
|
-
:verify_root => false,
|
586
|
-
:only_trusted => false,
|
587
|
-
:only_signed => false
|
588
|
-
)
|
372
|
+
def self.alt_name_or_x509_entry certificate, x509_entry
|
373
|
+
alt_name = certificate.extensions.find do |extension|
|
374
|
+
extension.oid == "#{x509_entry}AltName"
|
375
|
+
end
|
589
376
|
|
590
|
-
|
591
|
-
|
592
|
-
|
593
|
-
|
594
|
-
#
|
595
|
-
# This security policy is reasonable, but it allows unsigned packages, so a
|
596
|
-
# malicious person could simply delete the package signature and pass the
|
597
|
-
# gem off as unsigned.
|
598
|
-
|
599
|
-
MediumSecurity = Policy.new(
|
600
|
-
:verify_data => true,
|
601
|
-
:verify_signer => true,
|
602
|
-
:verify_chain => true,
|
603
|
-
:verify_root => true,
|
604
|
-
:only_trusted => true,
|
605
|
-
:only_signed => false
|
606
|
-
)
|
377
|
+
return alt_name.value if alt_name
|
378
|
+
|
379
|
+
certificate.send x509_entry
|
380
|
+
end
|
607
381
|
|
608
382
|
##
|
609
|
-
#
|
610
|
-
#
|
611
|
-
# the root certificate, and only trust root certificates that we have
|
612
|
-
# explicitly allowed trust for.
|
383
|
+
# Creates an unsigned certificate for +subject+ and +key+. The lifetime of
|
384
|
+
# the key is from the current time to +age+ which defaults to one year.
|
613
385
|
#
|
614
|
-
#
|
615
|
-
# a reasonable guarantee that the contents of the gem have not been altered.
|
616
|
-
|
617
|
-
HighSecurity = Policy.new(
|
618
|
-
:verify_data => true,
|
619
|
-
:verify_signer => true,
|
620
|
-
:verify_chain => true,
|
621
|
-
:verify_root => true,
|
622
|
-
:only_trusted => true,
|
623
|
-
:only_signed => true
|
624
|
-
)
|
386
|
+
# The +extensions+ restrict the key to the indicated uses.
|
625
387
|
|
626
|
-
|
627
|
-
|
628
|
-
|
629
|
-
Policies = {
|
630
|
-
'NoSecurity' => NoSecurity,
|
631
|
-
'AlmostNoSecurity' => AlmostNoSecurity,
|
632
|
-
'LowSecurity' => LowSecurity,
|
633
|
-
'MediumSecurity' => MediumSecurity,
|
634
|
-
'HighSecurity' => HighSecurity,
|
635
|
-
}
|
388
|
+
def self.create_cert subject, key, age = ONE_YEAR, extensions = EXTENSIONS,
|
389
|
+
serial = 1
|
390
|
+
cert = OpenSSL::X509::Certificate.new
|
636
391
|
|
637
|
-
|
638
|
-
|
639
|
-
|
392
|
+
cert.public_key = key.public_key
|
393
|
+
cert.version = 2
|
394
|
+
cert.serial = serial
|
640
395
|
|
641
|
-
|
642
|
-
|
396
|
+
cert.not_before = Time.now
|
397
|
+
cert.not_after = Time.now + age
|
643
398
|
|
644
|
-
cert.
|
645
|
-
cert.sign signing_key, opt[:dgst_algo].new
|
399
|
+
cert.subject = subject
|
646
400
|
|
647
|
-
cert
|
648
|
-
end
|
401
|
+
ef = OpenSSL::X509::ExtensionFactory.new nil, cert
|
649
402
|
|
650
|
-
|
651
|
-
|
652
|
-
# actually a directory. If not, then create it with the appropriate
|
653
|
-
# permissions.
|
654
|
-
|
655
|
-
def self.verify_trust_dir(path, perms)
|
656
|
-
# if the directory exists, then make sure it is in fact a directory. if
|
657
|
-
# it doesn't exist, then create it with the appropriate permissions
|
658
|
-
if File.exist?(path)
|
659
|
-
# verify that the trust directory is actually a directory
|
660
|
-
unless File.directory?(path)
|
661
|
-
err = "trust directory #{path} isn't a directory"
|
662
|
-
raise Gem::Security::Exception, err
|
663
|
-
end
|
664
|
-
else
|
665
|
-
# trust directory doesn't exist, so create it with permissions
|
666
|
-
FileUtils.mkdir_p(path)
|
667
|
-
FileUtils.chmod(perms, path)
|
403
|
+
cert.extensions = extensions.map do |ext_name, value|
|
404
|
+
ef.create_extension ext_name, value
|
668
405
|
end
|
406
|
+
|
407
|
+
cert
|
669
408
|
end
|
670
409
|
|
671
410
|
##
|
672
|
-
#
|
411
|
+
# Creates a self-signed certificate with an issuer and subject from +email+,
|
412
|
+
# a subject alternative name of +email+ and the given +extensions+ for the
|
413
|
+
# +key+.
|
673
414
|
|
674
|
-
def self.
|
675
|
-
|
676
|
-
opt = OPT.merge opt
|
415
|
+
def self.create_cert_email email, key, age = ONE_YEAR, extensions = EXTENSIONS
|
416
|
+
subject = email_to_name email
|
677
417
|
|
678
|
-
|
418
|
+
extensions = extensions.merge "subjectAltName" => "email:#{email}"
|
679
419
|
|
680
|
-
|
681
|
-
|
682
|
-
cert.public_key = key.public_key
|
683
|
-
cert.serial = 0
|
684
|
-
cert.subject = name
|
685
|
-
cert.version = 2
|
420
|
+
create_cert_self_signed subject, key, age, extensions
|
421
|
+
end
|
686
422
|
|
687
|
-
|
423
|
+
##
|
424
|
+
# Creates a self-signed certificate with an issuer and subject of +subject+
|
425
|
+
# and the given +extensions+ for the +key+.
|
688
426
|
|
689
|
-
|
690
|
-
|
691
|
-
|
427
|
+
def self.create_cert_self_signed subject, key, age = ONE_YEAR,
|
428
|
+
extensions = EXTENSIONS, serial = 1
|
429
|
+
certificate = create_cert subject, key, age, extensions
|
692
430
|
|
693
|
-
|
694
|
-
|
431
|
+
sign certificate, key, certificate, age, extensions, serial
|
432
|
+
end
|
695
433
|
|
696
|
-
|
434
|
+
##
|
435
|
+
# Creates a new key pair of the specified +length+ and +algorithm+. The
|
436
|
+
# default is a 2048 bit RSA key.
|
697
437
|
|
698
|
-
|
438
|
+
def self.create_key length = KEY_LENGTH, algorithm = KEY_ALGORITHM
|
439
|
+
algorithm.new length
|
699
440
|
end
|
700
441
|
|
701
442
|
##
|
702
|
-
#
|
443
|
+
# Turns +email_address+ into an OpenSSL::X509::Name
|
703
444
|
|
704
|
-
def self.
|
705
|
-
|
706
|
-
opt = OPT.merge(opt)
|
707
|
-
path = { :key => nil, :cert => nil }
|
445
|
+
def self.email_to_name email_address
|
446
|
+
email_address = email_address.gsub(/[^\w@.-]+/i, '_')
|
708
447
|
|
709
|
-
|
448
|
+
cn, dcs = email_address.split '@'
|
710
449
|
|
711
|
-
|
450
|
+
dcs = dcs.split '.'
|
712
451
|
|
713
|
-
|
452
|
+
name = "CN=#{cn}/#{dcs.map { |dc| "DC=#{dc}" }.join '/'}"
|
714
453
|
|
715
|
-
|
716
|
-
|
454
|
+
OpenSSL::X509::Name.parse name
|
455
|
+
end
|
717
456
|
|
718
|
-
|
719
|
-
|
720
|
-
|
721
|
-
|
457
|
+
##
|
458
|
+
# Signs +expired_certificate+ with +private_key+ if the keys match and the
|
459
|
+
# expired certificate was self-signed.
|
460
|
+
#--
|
461
|
+
# TODO increment serial
|
462
|
+
|
463
|
+
def self.re_sign expired_certificate, private_key, age = ONE_YEAR,
|
464
|
+
extensions = EXTENSIONS
|
465
|
+
raise Gem::Security::Exception,
|
466
|
+
"incorrect signing key for re-signing " \
|
467
|
+
"#{expired_certificate.subject}" unless
|
468
|
+
expired_certificate.public_key.to_pem == private_key.public_key.to_pem
|
469
|
+
|
470
|
+
unless expired_certificate.subject.to_s ==
|
471
|
+
expired_certificate.issuer.to_s then
|
472
|
+
subject = alt_name_or_x509_entry expired_certificate, :subject
|
473
|
+
issuer = alt_name_or_x509_entry expired_certificate, :issuer
|
474
|
+
|
475
|
+
raise Gem::Security::Exception,
|
476
|
+
"#{subject} is not self-signed, contact #{issuer} " \
|
477
|
+
"to obtain a valid certificate"
|
722
478
|
end
|
723
479
|
|
724
|
-
|
480
|
+
serial = expired_certificate.serial + 1
|
725
481
|
|
726
|
-
|
727
|
-
|
482
|
+
create_cert_self_signed(expired_certificate.subject, private_key, age,
|
483
|
+
extensions, serial)
|
484
|
+
end
|
728
485
|
|
729
|
-
|
730
|
-
|
731
|
-
file.write cert.to_pem
|
732
|
-
end
|
733
|
-
end
|
486
|
+
##
|
487
|
+
# Resets the trust directory for verifying gems.
|
734
488
|
|
735
|
-
|
736
|
-
|
489
|
+
def self.reset
|
490
|
+
@trust_dir = nil
|
737
491
|
end
|
738
492
|
|
739
493
|
##
|
740
|
-
#
|
494
|
+
# Sign the public key from +certificate+ with the +signing_key+ and
|
495
|
+
# +signing_cert+, using the Gem::Security::DIGEST_ALGORITHM. Uses the
|
496
|
+
# default certificate validity range and extensions.
|
497
|
+
#
|
498
|
+
# Returns the newly signed certificate.
|
741
499
|
|
742
|
-
def self.
|
743
|
-
|
500
|
+
def self.sign certificate, signing_key, signing_cert,
|
501
|
+
age = ONE_YEAR, extensions = EXTENSIONS, serial = 1
|
502
|
+
signee_subject = certificate.subject
|
503
|
+
signee_key = certificate.public_key
|
744
504
|
|
745
|
-
|
505
|
+
alt_name = certificate.extensions.find do |extension|
|
506
|
+
extension.oid == 'subjectAltName'
|
507
|
+
end
|
746
508
|
|
747
|
-
|
509
|
+
extensions = extensions.merge 'subjectAltName' => alt_name.value if
|
510
|
+
alt_name
|
748
511
|
|
749
|
-
|
750
|
-
|
512
|
+
issuer_alt_name = signing_cert.extensions.find do |extension|
|
513
|
+
extension.oid == 'subjectAltName'
|
751
514
|
end
|
752
515
|
|
753
|
-
|
516
|
+
extensions = extensions.merge 'issuerAltName' => issuer_alt_name.value if
|
517
|
+
issuer_alt_name
|
754
518
|
|
755
|
-
|
519
|
+
signed = create_cert signee_subject, signee_key, age, extensions, serial
|
520
|
+
signed.issuer = signing_cert.subject
|
521
|
+
|
522
|
+
signed.sign signing_key, Gem::Security::DIGEST_ALGORITHM.new
|
756
523
|
end
|
757
524
|
|
758
525
|
##
|
759
|
-
#
|
760
|
-
#
|
761
|
-
# Note: At the moment these are stored in OPT[:trust_dir], although that
|
762
|
-
# directory may change in the future.
|
526
|
+
# Returns a Gem::Security::TrustDir which wraps the directory where trusted
|
527
|
+
# certificates live.
|
763
528
|
|
764
|
-
def self.
|
765
|
-
|
529
|
+
def self.trust_dir
|
530
|
+
return @trust_dir if @trust_dir
|
766
531
|
|
767
|
-
|
768
|
-
path = Gem::Security::Policy.trusted_cert_path(cert, opt)
|
532
|
+
dir = File.join Gem.user_home, '.gem', 'trust'
|
769
533
|
|
770
|
-
|
771
|
-
|
534
|
+
@trust_dir ||= Gem::Security::TrustDir.new dir
|
535
|
+
end
|
772
536
|
|
773
|
-
|
774
|
-
|
775
|
-
file.chmod(opt[:perms][:trusted_cert])
|
776
|
-
file.write(cert.to_pem)
|
777
|
-
end
|
537
|
+
##
|
538
|
+
# Enumerates the trusted certificates via Gem::Security::TrustDir.
|
778
539
|
|
779
|
-
|
780
|
-
|
540
|
+
def self.trusted_certificates &block
|
541
|
+
trust_dir.each_certificate(&block)
|
781
542
|
end
|
782
543
|
|
783
544
|
##
|
784
|
-
#
|
785
|
-
|
786
|
-
class Signer
|
787
|
-
|
788
|
-
attr_accessor :cert_chain
|
789
|
-
attr_accessor :key
|
790
|
-
|
791
|
-
def initialize(key, cert_chain)
|
792
|
-
Gem.ensure_ssl_available
|
793
|
-
@algo = Gem::Security::OPT[:dgst_algo]
|
794
|
-
@key, @cert_chain = key, cert_chain
|
795
|
-
|
796
|
-
# check key, if it's a file, and if it's key, leave it alone
|
797
|
-
if @key && !@key.kind_of?(OpenSSL::PKey::PKey)
|
798
|
-
@key = OpenSSL::PKey::RSA.new(File.read(@key))
|
799
|
-
end
|
800
|
-
|
801
|
-
# check cert chain, if it's a file, load it, if it's cert data, convert
|
802
|
-
# it into a cert object, and if it's a cert object, leave it alone
|
803
|
-
if @cert_chain
|
804
|
-
@cert_chain = @cert_chain.map do |cert|
|
805
|
-
# check cert, if it's a file, load it, if it's cert data, convert it
|
806
|
-
# into a cert object, and if it's a cert object, leave it alone
|
807
|
-
if cert && !cert.kind_of?(OpenSSL::X509::Certificate)
|
808
|
-
cert = File.read(cert) if File::exist?(cert)
|
809
|
-
cert = OpenSSL::X509::Certificate.new(cert)
|
810
|
-
end
|
811
|
-
cert
|
812
|
-
end
|
813
|
-
end
|
814
|
-
end
|
545
|
+
# Writes +pemmable+, which must respond to +to_pem+ to +path+ with the given
|
546
|
+
# +permissions+.
|
815
547
|
|
816
|
-
|
817
|
-
|
548
|
+
def self.write pemmable, path, permissions = 0600
|
549
|
+
path = File.expand_path path
|
818
550
|
|
819
|
-
|
820
|
-
|
551
|
+
open path, 'wb', permissions do |io|
|
552
|
+
io.write pemmable.to_pem
|
821
553
|
end
|
822
554
|
|
555
|
+
path
|
823
556
|
end
|
824
557
|
|
558
|
+
reset
|
559
|
+
|
825
560
|
end
|
826
561
|
|
562
|
+
require 'rubygems/security/policy'
|
563
|
+
require 'rubygems/security/policies'
|
564
|
+
require 'rubygems/security/signer'
|
565
|
+
require 'rubygems/security/trust_dir'
|
566
|
+
|