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,115 @@
|
|
1
|
+
module Gem::Security
|
2
|
+
|
3
|
+
##
|
4
|
+
# No security policy: all package signature checks are disabled.
|
5
|
+
|
6
|
+
NoSecurity = Policy.new(
|
7
|
+
'No Security',
|
8
|
+
:verify_data => false,
|
9
|
+
:verify_signer => false,
|
10
|
+
:verify_chain => false,
|
11
|
+
:verify_root => false,
|
12
|
+
:only_trusted => false,
|
13
|
+
:only_signed => false
|
14
|
+
)
|
15
|
+
|
16
|
+
##
|
17
|
+
# AlmostNo security policy: only verify that the signing certificate is the
|
18
|
+
# one that actually signed the data. Make no attempt to verify the signing
|
19
|
+
# certificate chain.
|
20
|
+
#
|
21
|
+
# This policy is basically useless. better than nothing, but can still be
|
22
|
+
# easily spoofed, and is not recommended.
|
23
|
+
|
24
|
+
AlmostNoSecurity = Policy.new(
|
25
|
+
'Almost No Security',
|
26
|
+
:verify_data => true,
|
27
|
+
:verify_signer => false,
|
28
|
+
:verify_chain => false,
|
29
|
+
:verify_root => false,
|
30
|
+
:only_trusted => false,
|
31
|
+
:only_signed => false
|
32
|
+
)
|
33
|
+
|
34
|
+
##
|
35
|
+
# Low security policy: only verify that the signing certificate is actually
|
36
|
+
# the gem signer, and that the signing certificate is valid.
|
37
|
+
#
|
38
|
+
# This policy is better than nothing, but can still be easily spoofed, and
|
39
|
+
# is not recommended.
|
40
|
+
|
41
|
+
LowSecurity = Policy.new(
|
42
|
+
'Low Security',
|
43
|
+
:verify_data => true,
|
44
|
+
:verify_signer => true,
|
45
|
+
:verify_chain => false,
|
46
|
+
:verify_root => false,
|
47
|
+
:only_trusted => false,
|
48
|
+
:only_signed => false
|
49
|
+
)
|
50
|
+
|
51
|
+
##
|
52
|
+
# Medium security policy: verify the signing certificate, verify the signing
|
53
|
+
# certificate chain all the way to the root certificate, and only trust root
|
54
|
+
# certificates that we have explicitly allowed trust for.
|
55
|
+
#
|
56
|
+
# This security policy is reasonable, but it allows unsigned packages, so a
|
57
|
+
# malicious person could simply delete the package signature and pass the
|
58
|
+
# gem off as unsigned.
|
59
|
+
|
60
|
+
MediumSecurity = Policy.new(
|
61
|
+
'Medium Security',
|
62
|
+
:verify_data => true,
|
63
|
+
:verify_signer => true,
|
64
|
+
:verify_chain => true,
|
65
|
+
:verify_root => true,
|
66
|
+
:only_trusted => true,
|
67
|
+
:only_signed => false
|
68
|
+
)
|
69
|
+
|
70
|
+
##
|
71
|
+
# High security policy: only allow signed gems to be installed, verify the
|
72
|
+
# signing certificate, verify the signing certificate chain all the way to
|
73
|
+
# the root certificate, and only trust root certificates that we have
|
74
|
+
# explicitly allowed trust for.
|
75
|
+
#
|
76
|
+
# This security policy is significantly more difficult to bypass, and offers
|
77
|
+
# a reasonable guarantee that the contents of the gem have not been altered.
|
78
|
+
|
79
|
+
HighSecurity = Policy.new(
|
80
|
+
'High Security',
|
81
|
+
:verify_data => true,
|
82
|
+
:verify_signer => true,
|
83
|
+
:verify_chain => true,
|
84
|
+
:verify_root => true,
|
85
|
+
:only_trusted => true,
|
86
|
+
:only_signed => true
|
87
|
+
)
|
88
|
+
|
89
|
+
##
|
90
|
+
# Policy used to verify a certificate and key when signing a gem
|
91
|
+
|
92
|
+
SigningPolicy = Policy.new(
|
93
|
+
'Signing Policy',
|
94
|
+
:verify_data => false,
|
95
|
+
:verify_signer => true,
|
96
|
+
:verify_chain => true,
|
97
|
+
:verify_root => true,
|
98
|
+
:only_trusted => false,
|
99
|
+
:only_signed => false
|
100
|
+
)
|
101
|
+
|
102
|
+
##
|
103
|
+
# Hash of configured security policies
|
104
|
+
|
105
|
+
Policies = {
|
106
|
+
'NoSecurity' => NoSecurity,
|
107
|
+
'AlmostNoSecurity' => AlmostNoSecurity,
|
108
|
+
'LowSecurity' => LowSecurity,
|
109
|
+
'MediumSecurity' => MediumSecurity,
|
110
|
+
'HighSecurity' => HighSecurity,
|
111
|
+
# SigningPolicy is not intended for use by `gem -P` so do not list it
|
112
|
+
}
|
113
|
+
|
114
|
+
end
|
115
|
+
|
@@ -0,0 +1,227 @@
|
|
1
|
+
##
|
2
|
+
# A Gem::Security::Policy object encapsulates the settings for verifying
|
3
|
+
# signed gem files. This is the base class. You can either declare an
|
4
|
+
# instance of this or use one of the preset security policies in
|
5
|
+
# Gem::Security::Policies.
|
6
|
+
|
7
|
+
class Gem::Security::Policy
|
8
|
+
|
9
|
+
attr_reader :name
|
10
|
+
|
11
|
+
attr_accessor :only_signed
|
12
|
+
attr_accessor :only_trusted
|
13
|
+
attr_accessor :verify_chain
|
14
|
+
attr_accessor :verify_data
|
15
|
+
attr_accessor :verify_root
|
16
|
+
attr_accessor :verify_signer
|
17
|
+
|
18
|
+
##
|
19
|
+
# Create a new Gem::Security::Policy object with the given mode and
|
20
|
+
# options.
|
21
|
+
|
22
|
+
def initialize name, policy = {}, opt = {}
|
23
|
+
@name = name
|
24
|
+
|
25
|
+
@opt = opt
|
26
|
+
|
27
|
+
# Default to security
|
28
|
+
@only_signed = true
|
29
|
+
@only_trusted = true
|
30
|
+
@verify_chain = true
|
31
|
+
@verify_data = true
|
32
|
+
@verify_root = true
|
33
|
+
@verify_signer = true
|
34
|
+
|
35
|
+
policy.each_pair do |key, val|
|
36
|
+
case key
|
37
|
+
when :verify_data then @verify_data = val
|
38
|
+
when :verify_signer then @verify_signer = val
|
39
|
+
when :verify_chain then @verify_chain = val
|
40
|
+
when :verify_root then @verify_root = val
|
41
|
+
when :only_trusted then @only_trusted = val
|
42
|
+
when :only_signed then @only_signed = val
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
##
|
48
|
+
# Verifies each certificate in +chain+ has signed the following certificate
|
49
|
+
# and is valid for the given +time+.
|
50
|
+
|
51
|
+
def check_chain chain, time
|
52
|
+
chain.each_cons 2 do |issuer, cert|
|
53
|
+
check_cert cert, issuer, time
|
54
|
+
end
|
55
|
+
|
56
|
+
true
|
57
|
+
rescue Gem::Security::Exception => e
|
58
|
+
raise Gem::Security::Exception, "invalid signing chain: #{e.message}"
|
59
|
+
end
|
60
|
+
|
61
|
+
##
|
62
|
+
# Verifies that +data+ matches the +signature+ created by +public_key+ and
|
63
|
+
# the +digest+ algorithm.
|
64
|
+
|
65
|
+
def check_data public_key, digest, signature, data
|
66
|
+
raise Gem::Security::Exception, "invalid signature" unless
|
67
|
+
public_key.verify digest.new, signature, data.digest
|
68
|
+
|
69
|
+
true
|
70
|
+
end
|
71
|
+
|
72
|
+
##
|
73
|
+
# Ensures that +signer+ is valid for +time+ and was signed by the +issuer+.
|
74
|
+
# If the +issuer+ is +nil+ no verification is performed.
|
75
|
+
|
76
|
+
def check_cert signer, issuer, time
|
77
|
+
message = "certificate #{signer.subject}"
|
78
|
+
|
79
|
+
if not_before = signer.not_before and not_before > time then
|
80
|
+
raise Gem::Security::Exception,
|
81
|
+
"#{message} not valid before #{not_before}"
|
82
|
+
end
|
83
|
+
|
84
|
+
if not_after = signer.not_after and not_after < time then
|
85
|
+
raise Gem::Security::Exception, "#{message} not valid after #{not_after}"
|
86
|
+
end
|
87
|
+
|
88
|
+
if issuer and not signer.verify issuer.public_key then
|
89
|
+
raise Gem::Security::Exception,
|
90
|
+
"#{message} was not issued by #{issuer.subject}"
|
91
|
+
end
|
92
|
+
|
93
|
+
true
|
94
|
+
end
|
95
|
+
|
96
|
+
##
|
97
|
+
# Ensures the public key of +key+ matches the public key in +signer+
|
98
|
+
|
99
|
+
def check_key signer, key
|
100
|
+
raise Gem::Security::Exception,
|
101
|
+
"certificate #{signer.subject} does not match the signing key" unless
|
102
|
+
signer.public_key.to_pem == key.public_key.to_pem
|
103
|
+
|
104
|
+
true
|
105
|
+
end
|
106
|
+
|
107
|
+
##
|
108
|
+
# Ensures the root certificate in +chain+ is self-signed and valid for
|
109
|
+
# +time+.
|
110
|
+
|
111
|
+
def check_root chain, time
|
112
|
+
root = chain.first
|
113
|
+
|
114
|
+
raise Gem::Security::Exception,
|
115
|
+
"root certificate #{root.subject} is not self-signed " \
|
116
|
+
"(issuer #{root.issuer})" if
|
117
|
+
root.issuer.to_s != root.subject.to_s # HACK to_s is for ruby 1.8
|
118
|
+
|
119
|
+
check_cert root, root, time
|
120
|
+
end
|
121
|
+
|
122
|
+
##
|
123
|
+
# Ensures the root of +chain+ has a trusted certificate in +trust_dir+ and
|
124
|
+
# the digests of the two certificates match according to +digester+
|
125
|
+
|
126
|
+
def check_trust chain, digester, trust_dir
|
127
|
+
root = chain.first
|
128
|
+
|
129
|
+
path = Gem::Security.trust_dir.cert_path root
|
130
|
+
|
131
|
+
unless File.exist? path then
|
132
|
+
message = "root cert #{root.subject} is not trusted"
|
133
|
+
|
134
|
+
message << " (root of signing cert #{chain.last.subject})" if
|
135
|
+
chain.length > 1
|
136
|
+
|
137
|
+
raise Gem::Security::Exception, message
|
138
|
+
end
|
139
|
+
|
140
|
+
save_cert = OpenSSL::X509::Certificate.new File.read path
|
141
|
+
save_dgst = digester.digest save_cert.public_key.to_s
|
142
|
+
|
143
|
+
pkey_str = root.public_key.to_s
|
144
|
+
cert_dgst = digester.digest pkey_str
|
145
|
+
|
146
|
+
raise Gem::Security::Exception,
|
147
|
+
"trusted root certificate #{root.subject} checksum " \
|
148
|
+
"does not match signing root certificate checksum" unless
|
149
|
+
save_dgst == cert_dgst
|
150
|
+
|
151
|
+
true
|
152
|
+
end
|
153
|
+
|
154
|
+
def inspect # :nodoc:
|
155
|
+
"[Policy: %s - data: %p signer: %p chain: %p root: %p " \
|
156
|
+
"signed-only: %p trusted-only: %p]" % [
|
157
|
+
@name, @verify_chain, @verify_data, @verify_root, @verify_signer,
|
158
|
+
@only_signed, @only_trusted,
|
159
|
+
]
|
160
|
+
end
|
161
|
+
|
162
|
+
##
|
163
|
+
# Verifies the certificate +chain+ is valid, the +digests+ match the
|
164
|
+
# signatures +signatures+ created by the signer depending on the +policy+
|
165
|
+
# settings.
|
166
|
+
#
|
167
|
+
# If +key+ is given it is used to validate the signing certificate.
|
168
|
+
|
169
|
+
def verify chain, key = nil, digests = {}, signatures = {}
|
170
|
+
if @only_signed and signatures.empty? then
|
171
|
+
raise Gem::Security::Exception,
|
172
|
+
"unsigned gems are not allowed by the #{name} policy"
|
173
|
+
end
|
174
|
+
|
175
|
+
opt = @opt
|
176
|
+
digester = Gem::Security::DIGEST_ALGORITHM
|
177
|
+
trust_dir = opt[:trust_dir]
|
178
|
+
time = Time.now
|
179
|
+
|
180
|
+
signer_digests = digests.find do |algorithm, file_digests|
|
181
|
+
file_digests.values.first.name == Gem::Security::DIGEST_NAME
|
182
|
+
end
|
183
|
+
|
184
|
+
signer_digests = digests.values.first || {}
|
185
|
+
|
186
|
+
signer = chain.last
|
187
|
+
|
188
|
+
check_key signer, key if key
|
189
|
+
|
190
|
+
check_cert signer, nil, time if @verify_signer
|
191
|
+
|
192
|
+
check_chain chain, time if @verify_chain
|
193
|
+
|
194
|
+
check_root chain, time if @verify_root
|
195
|
+
|
196
|
+
check_trust chain, digester, trust_dir if @only_trusted
|
197
|
+
|
198
|
+
signer_digests.each do |file, digest|
|
199
|
+
signature = signatures[file]
|
200
|
+
|
201
|
+
raise Gem::Security::Exception, "missing signature for #{file}" unless
|
202
|
+
signature
|
203
|
+
|
204
|
+
check_data signer.public_key, digester, signature, digest if @verify_data
|
205
|
+
end
|
206
|
+
|
207
|
+
true
|
208
|
+
end
|
209
|
+
|
210
|
+
##
|
211
|
+
# Extracts the certificate chain from the +spec+ and calls #verify to ensure
|
212
|
+
# the signatures and certificate chain is valid according to the policy..
|
213
|
+
|
214
|
+
def verify_signatures spec, digests, signatures
|
215
|
+
chain = spec.cert_chain.map do |cert_pem|
|
216
|
+
OpenSSL::X509::Certificate.new cert_pem
|
217
|
+
end
|
218
|
+
|
219
|
+
verify chain, nil, digests, signatures
|
220
|
+
|
221
|
+
true
|
222
|
+
end
|
223
|
+
|
224
|
+
alias to_s name # :nodoc:
|
225
|
+
|
226
|
+
end
|
227
|
+
|
@@ -0,0 +1,136 @@
|
|
1
|
+
##
|
2
|
+
# Basic OpenSSL-based package signing class.
|
3
|
+
|
4
|
+
class Gem::Security::Signer
|
5
|
+
|
6
|
+
##
|
7
|
+
# The chain of certificates for signing including the signing certificate
|
8
|
+
|
9
|
+
attr_accessor :cert_chain
|
10
|
+
|
11
|
+
##
|
12
|
+
# The private key for the signing certificate
|
13
|
+
|
14
|
+
attr_accessor :key
|
15
|
+
|
16
|
+
##
|
17
|
+
# The digest algorithm used to create the signature
|
18
|
+
|
19
|
+
attr_reader :digest_algorithm
|
20
|
+
|
21
|
+
##
|
22
|
+
# The name of the digest algorithm, used to pull digests out of the hash by
|
23
|
+
# name.
|
24
|
+
|
25
|
+
attr_reader :digest_name # :nodoc:
|
26
|
+
|
27
|
+
##
|
28
|
+
# Creates a new signer with an RSA +key+ or path to a key, and a certificate
|
29
|
+
# +chain+ containing X509 certificates, encoding certificates or paths to
|
30
|
+
# certificates.
|
31
|
+
|
32
|
+
def initialize key, cert_chain
|
33
|
+
@cert_chain = cert_chain
|
34
|
+
@key = key
|
35
|
+
|
36
|
+
unless @key then
|
37
|
+
default_key = File.join Gem.user_home, 'gem-private_key.pem'
|
38
|
+
@key = default_key if File.exist? default_key
|
39
|
+
end
|
40
|
+
|
41
|
+
unless @cert_chain then
|
42
|
+
default_cert = File.join Gem.user_home, 'gem-public_cert.pem'
|
43
|
+
@cert_chain = [default_cert] if File.exist? default_cert
|
44
|
+
end
|
45
|
+
|
46
|
+
@digest_algorithm = Gem::Security::DIGEST_ALGORITHM
|
47
|
+
@digest_name = Gem::Security::DIGEST_NAME
|
48
|
+
|
49
|
+
@key = OpenSSL::PKey::RSA.new File.read @key if
|
50
|
+
@key and not OpenSSL::PKey::RSA === @key
|
51
|
+
|
52
|
+
if @cert_chain then
|
53
|
+
@cert_chain = @cert_chain.compact.map do |cert|
|
54
|
+
next cert if OpenSSL::X509::Certificate === cert
|
55
|
+
|
56
|
+
cert = File.read cert if File.exist? cert
|
57
|
+
|
58
|
+
OpenSSL::X509::Certificate.new cert
|
59
|
+
end
|
60
|
+
|
61
|
+
load_cert_chain
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
##
|
66
|
+
# Loads any missing issuers in the cert chain from the trusted certificates.
|
67
|
+
#
|
68
|
+
# If the issuer does not exist it is ignored as it will be checked later.
|
69
|
+
|
70
|
+
def load_cert_chain # :nodoc:
|
71
|
+
return if @cert_chain.empty?
|
72
|
+
|
73
|
+
while @cert_chain.first.issuer.to_s != @cert_chain.first.subject.to_s do
|
74
|
+
issuer = Gem::Security.trust_dir.issuer_of @cert_chain.first
|
75
|
+
|
76
|
+
break unless issuer # cert chain is verified later
|
77
|
+
|
78
|
+
@cert_chain.unshift issuer
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
##
|
83
|
+
# Sign data with given digest algorithm
|
84
|
+
|
85
|
+
def sign data
|
86
|
+
return unless @key
|
87
|
+
|
88
|
+
if @cert_chain.length == 1 and @cert_chain.last.not_after < Time.now then
|
89
|
+
re_sign_key
|
90
|
+
end
|
91
|
+
|
92
|
+
Gem::Security::SigningPolicy.verify @cert_chain, @key
|
93
|
+
|
94
|
+
@key.sign @digest_algorithm.new, data
|
95
|
+
end
|
96
|
+
|
97
|
+
##
|
98
|
+
# Attempts to re-sign the private key if the signing certificate is expired.
|
99
|
+
#
|
100
|
+
# The key will be re-signed if:
|
101
|
+
# * The expired certificate is self-signed
|
102
|
+
# * The expired certificate is saved at ~/.gem/gem-public_cert.pem
|
103
|
+
# * There is no file matching the expiry date at
|
104
|
+
# ~/.gem/gem-public_cert.pem.expired.%Y%m%d%H%M%S
|
105
|
+
#
|
106
|
+
# If the signing certificate can be re-signed the expired certificate will
|
107
|
+
# be saved as ~/.gem/gem-pubilc_cert.pem.expired.%Y%m%d%H%M%S where the
|
108
|
+
# expiry time (not after) is used for the timestamp.
|
109
|
+
|
110
|
+
def re_sign_key # :nodoc:
|
111
|
+
old_cert = @cert_chain.last
|
112
|
+
|
113
|
+
disk_cert_path = File.join Gem.user_home, 'gem-public_cert.pem'
|
114
|
+
disk_cert = File.read disk_cert_path rescue nil
|
115
|
+
disk_key =
|
116
|
+
File.read File.join(Gem.user_home, 'gem-private_key.pem') rescue nil
|
117
|
+
|
118
|
+
if disk_key == @key.to_pem and disk_cert == old_cert.to_pem then
|
119
|
+
expiry = old_cert.not_after.strftime '%Y%m%d%H%M%S'
|
120
|
+
old_cert_file = "gem-public_cert.pem.expired.#{expiry}"
|
121
|
+
old_cert_path = File.join Gem.user_home, old_cert_file
|
122
|
+
|
123
|
+
unless File.exist? old_cert_path then
|
124
|
+
Gem::Security.write old_cert, old_cert_path
|
125
|
+
|
126
|
+
cert = Gem::Security.re_sign old_cert, @key
|
127
|
+
|
128
|
+
Gem::Security.write cert, disk_cert_path
|
129
|
+
|
130
|
+
@cert_chain = [cert]
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
end
|
136
|
+
|