ruby-saml 1.9.0 → 1.14.0
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.
- checksums.yaml +5 -5
- data/.github/workflows/test.yml +25 -0
- data/{changelog.md → CHANGELOG.md} +64 -1
- data/README.md +394 -211
- data/UPGRADING.md +149 -0
- data/lib/onelogin/ruby-saml/attributes.rb +24 -1
- data/lib/onelogin/ruby-saml/authrequest.rb +26 -10
- data/lib/onelogin/ruby-saml/idp_metadata_parser.rb +285 -184
- data/lib/onelogin/ruby-saml/logging.rb +3 -3
- data/lib/onelogin/ruby-saml/logoutrequest.rb +26 -11
- data/lib/onelogin/ruby-saml/logoutresponse.rb +27 -11
- data/lib/onelogin/ruby-saml/metadata.rb +62 -17
- data/lib/onelogin/ruby-saml/response.rb +86 -37
- data/lib/onelogin/ruby-saml/saml_message.rb +14 -5
- data/lib/onelogin/ruby-saml/setting_error.rb +6 -0
- data/lib/onelogin/ruby-saml/settings.rb +117 -41
- data/lib/onelogin/ruby-saml/slo_logoutrequest.rb +33 -31
- data/lib/onelogin/ruby-saml/slo_logoutresponse.rb +43 -20
- data/lib/onelogin/ruby-saml/utils.rb +101 -9
- data/lib/onelogin/ruby-saml/version.rb +1 -1
- data/lib/xml_security.rb +39 -13
- data/ruby-saml.gemspec +21 -8
- metadata +43 -284
- data/.travis.yml +0 -32
- data/test/certificates/certificate1 +0 -12
- data/test/certificates/certificate_without_head_foot +0 -1
- data/test/certificates/formatted_certificate +0 -14
- data/test/certificates/formatted_chained_certificate +0 -42
- data/test/certificates/formatted_private_key +0 -12
- data/test/certificates/formatted_rsa_private_key +0 -12
- data/test/certificates/invalid_certificate1 +0 -1
- data/test/certificates/invalid_certificate2 +0 -1
- data/test/certificates/invalid_certificate3 +0 -12
- data/test/certificates/invalid_chained_certificate1 +0 -1
- data/test/certificates/invalid_private_key1 +0 -1
- data/test/certificates/invalid_private_key2 +0 -1
- data/test/certificates/invalid_private_key3 +0 -10
- data/test/certificates/invalid_rsa_private_key1 +0 -1
- data/test/certificates/invalid_rsa_private_key2 +0 -1
- data/test/certificates/invalid_rsa_private_key3 +0 -10
- data/test/certificates/ruby-saml-2.crt +0 -15
- data/test/certificates/ruby-saml.crt +0 -14
- data/test/certificates/ruby-saml.key +0 -15
- data/test/idp_metadata_parser_test.rb +0 -579
- data/test/logging_test.rb +0 -62
- data/test/logout_requests/invalid_slo_request.xml +0 -6
- data/test/logout_requests/slo_request.xml +0 -4
- data/test/logout_requests/slo_request.xml.base64 +0 -1
- data/test/logout_requests/slo_request_deflated.xml.base64 +0 -1
- data/test/logout_requests/slo_request_with_name_id_format.xml +0 -4
- data/test/logout_requests/slo_request_with_session_index.xml +0 -5
- data/test/logout_responses/logoutresponse_fixtures.rb +0 -67
- data/test/logoutrequest_test.rb +0 -226
- data/test/logoutresponse_test.rb +0 -402
- data/test/metadata/idp_descriptor.xml +0 -26
- data/test/metadata/idp_descriptor_2.xml +0 -56
- data/test/metadata/idp_descriptor_3.xml +0 -14
- data/test/metadata/idp_descriptor_4.xml +0 -72
- data/test/metadata/idp_metadata_different_sign_and_encrypt_cert.xml +0 -72
- data/test/metadata/idp_metadata_multi_certs.xml +0 -75
- data/test/metadata/idp_metadata_multi_signing_certs.xml +0 -52
- data/test/metadata/idp_metadata_same_sign_and_encrypt_cert.xml +0 -71
- data/test/metadata/idp_multiple_descriptors.xml +0 -53
- data/test/metadata/no_idp_descriptor.xml +0 -21
- data/test/metadata_test.rb +0 -331
- data/test/request_test.rb +0 -323
- data/test/response_test.rb +0 -1619
- data/test/responses/adfs_response_sha1.xml +0 -46
- data/test/responses/adfs_response_sha256.xml +0 -46
- data/test/responses/adfs_response_sha384.xml +0 -46
- data/test/responses/adfs_response_sha512.xml +0 -46
- data/test/responses/adfs_response_xmlns.xml +0 -45
- data/test/responses/attackxee.xml +0 -13
- data/test/responses/invalids/duplicated_attributes.xml.base64 +0 -1
- data/test/responses/invalids/empty_destination.xml.base64 +0 -1
- data/test/responses/invalids/empty_nameid.xml.base64 +0 -1
- data/test/responses/invalids/encrypted_new_attack.xml.base64 +0 -1
- data/test/responses/invalids/invalid_audience.xml.base64 +0 -1
- data/test/responses/invalids/invalid_issuer_assertion.xml.base64 +0 -1
- data/test/responses/invalids/invalid_issuer_message.xml.base64 +0 -1
- data/test/responses/invalids/invalid_signature_position.xml.base64 +0 -1
- data/test/responses/invalids/invalid_subjectconfirmation_inresponse.xml.base64 +0 -1
- data/test/responses/invalids/invalid_subjectconfirmation_nb.xml.base64 +0 -1
- data/test/responses/invalids/invalid_subjectconfirmation_noa.xml.base64 +0 -1
- data/test/responses/invalids/invalid_subjectconfirmation_recipient.xml.base64 +0 -1
- data/test/responses/invalids/multiple_assertions.xml.base64 +0 -2
- data/test/responses/invalids/multiple_signed.xml.base64 +0 -1
- data/test/responses/invalids/no_authnstatement.xml.base64 +0 -1
- data/test/responses/invalids/no_conditions.xml.base64 +0 -1
- data/test/responses/invalids/no_id.xml.base64 +0 -1
- data/test/responses/invalids/no_issuer_assertion.xml.base64 +0 -1
- data/test/responses/invalids/no_issuer_response.xml.base64 +0 -1
- data/test/responses/invalids/no_nameid.xml.base64 +0 -1
- data/test/responses/invalids/no_saml2.xml.base64 +0 -1
- data/test/responses/invalids/no_signature.xml.base64 +0 -1
- data/test/responses/invalids/no_status.xml.base64 +0 -1
- data/test/responses/invalids/no_status_code.xml.base64 +0 -1
- data/test/responses/invalids/no_subjectconfirmation_data.xml.base64 +0 -1
- data/test/responses/invalids/no_subjectconfirmation_method.xml.base64 +0 -1
- data/test/responses/invalids/response_invalid_signed_element.xml.base64 +0 -1
- data/test/responses/invalids/response_with_concealed_signed_assertion.xml +0 -51
- data/test/responses/invalids/response_with_doubled_signed_assertion.xml +0 -49
- data/test/responses/invalids/signature_wrapping_attack.xml.base64 +0 -1
- data/test/responses/invalids/status_code_responder.xml.base64 +0 -1
- data/test/responses/invalids/status_code_responer_and_msg.xml.base64 +0 -1
- data/test/responses/invalids/wrong_spnamequalifier.xml.base64 +0 -1
- data/test/responses/no_signature_ns.xml +0 -48
- data/test/responses/open_saml_response.xml +0 -56
- data/test/responses/response_assertion_wrapped.xml.base64 +0 -93
- data/test/responses/response_audience_self_closed_tag.xml.base64 +0 -1
- data/test/responses/response_double_status_code.xml.base64 +0 -1
- data/test/responses/response_encrypted_attrs.xml.base64 +0 -1
- data/test/responses/response_encrypted_nameid.xml.base64 +0 -1
- data/test/responses/response_eval.xml +0 -7
- data/test/responses/response_no_cert_and_encrypted_attrs.xml +0 -29
- data/test/responses/response_node_text_attack.xml.base64 +0 -1
- data/test/responses/response_node_text_attack2.xml.base64 +0 -1
- data/test/responses/response_node_text_attack3.xml.base64 +0 -1
- data/test/responses/response_unsigned_xml_base64 +0 -1
- data/test/responses/response_with_ampersands.xml +0 -139
- data/test/responses/response_with_ampersands.xml.base64 +0 -93
- data/test/responses/response_with_ds_namespace_at_the_root.xml.base64 +0 -1
- data/test/responses/response_with_multiple_attribute_statements.xml +0 -72
- data/test/responses/response_with_multiple_attribute_values.xml +0 -67
- data/test/responses/response_with_retrieval_method.xml +0 -26
- data/test/responses/response_with_saml2_namespace.xml.base64 +0 -102
- data/test/responses/response_with_signed_assertion.xml.base64 +0 -66
- data/test/responses/response_with_signed_assertion_2.xml.base64 +0 -1
- data/test/responses/response_with_signed_assertion_3.xml +0 -30
- data/test/responses/response_with_signed_message_and_assertion.xml +0 -34
- data/test/responses/response_with_undefined_recipient.xml.base64 +0 -1
- data/test/responses/response_without_attributes.xml.base64 +0 -79
- data/test/responses/response_without_reference_uri.xml.base64 +0 -1
- data/test/responses/response_wrapped.xml.base64 +0 -150
- data/test/responses/signed_message_encrypted_signed_assertion.xml.base64 +0 -1
- data/test/responses/signed_message_encrypted_unsigned_assertion.xml.base64 +0 -1
- data/test/responses/signed_nameid_in_atts.xml +0 -47
- data/test/responses/signed_unqual_nameid_in_atts.xml +0 -47
- data/test/responses/simple_saml_php.xml +0 -71
- data/test/responses/starfield_response.xml.base64 +0 -1
- data/test/responses/test_sign.xml +0 -43
- data/test/responses/unsigned_encrypted_adfs.xml +0 -23
- data/test/responses/unsigned_message_aes128_encrypted_signed_assertion.xml.base64 +0 -1
- data/test/responses/unsigned_message_aes192_encrypted_signed_assertion.xml.base64 +0 -1
- data/test/responses/unsigned_message_aes256_encrypted_signed_assertion.xml.base64 +0 -1
- data/test/responses/unsigned_message_des192_encrypted_signed_assertion.xml.base64 +0 -1
- data/test/responses/unsigned_message_encrypted_assertion_without_saml_namespace.xml.base64 +0 -1
- data/test/responses/unsigned_message_encrypted_signed_assertion.xml.base64 +0 -1
- data/test/responses/unsigned_message_encrypted_unsigned_assertion.xml.base64 +0 -1
- data/test/responses/valid_response.xml.base64 +0 -1
- data/test/responses/valid_response_with_formatted_x509certificate.xml.base64 +0 -1
- data/test/responses/valid_response_without_x509certificate.xml.base64 +0 -1
- data/test/saml_message_test.rb +0 -56
- data/test/settings_test.rb +0 -329
- data/test/slo_logoutrequest_test.rb +0 -448
- data/test/slo_logoutresponse_test.rb +0 -199
- data/test/test_helper.rb +0 -327
- data/test/utils_test.rb +0 -254
- data/test/xml_security_test.rb +0 -421
|
@@ -3,6 +3,7 @@ if RUBY_VERSION < '1.9'
|
|
|
3
3
|
else
|
|
4
4
|
require 'securerandom'
|
|
5
5
|
end
|
|
6
|
+
require "openssl"
|
|
6
7
|
|
|
7
8
|
module OneLogin
|
|
8
9
|
module RubySaml
|
|
@@ -12,8 +13,69 @@ module OneLogin
|
|
|
12
13
|
class Utils
|
|
13
14
|
@@uuid_generator = UUID.new if RUBY_VERSION < '1.9'
|
|
14
15
|
|
|
15
|
-
|
|
16
|
-
|
|
16
|
+
BINDINGS = { :post => "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST".freeze,
|
|
17
|
+
:redirect => "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect".freeze }.freeze
|
|
18
|
+
DSIG = "http://www.w3.org/2000/09/xmldsig#".freeze
|
|
19
|
+
XENC = "http://www.w3.org/2001/04/xmlenc#".freeze
|
|
20
|
+
DURATION_FORMAT = %r(^
|
|
21
|
+
(-?)P # 1: Duration sign
|
|
22
|
+
(?:
|
|
23
|
+
(?:(\d+)Y)? # 2: Years
|
|
24
|
+
(?:(\d+)M)? # 3: Months
|
|
25
|
+
(?:(\d+)D)? # 4: Days
|
|
26
|
+
(?:T
|
|
27
|
+
(?:(\d+)H)? # 5: Hours
|
|
28
|
+
(?:(\d+)M)? # 6: Minutes
|
|
29
|
+
(?:(\d+(?:[.,]\d+)?)S)? # 7: Seconds
|
|
30
|
+
)?
|
|
31
|
+
|
|
|
32
|
+
(\d+)W # 8: Weeks
|
|
33
|
+
)
|
|
34
|
+
$)x.freeze
|
|
35
|
+
UUID_PREFIX = '_'
|
|
36
|
+
|
|
37
|
+
# Checks if the x509 cert provided is expired
|
|
38
|
+
#
|
|
39
|
+
# @param cert [Certificate] The x509 certificate
|
|
40
|
+
#
|
|
41
|
+
def self.is_cert_expired(cert)
|
|
42
|
+
if cert.is_a?(String)
|
|
43
|
+
cert = OpenSSL::X509::Certificate.new(cert)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
return cert.not_after < Time.now
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# Interprets a ISO8601 duration value relative to a given timestamp.
|
|
50
|
+
#
|
|
51
|
+
# @param duration [String] The duration, as a string.
|
|
52
|
+
# @param timestamp [Integer] The unix timestamp we should apply the
|
|
53
|
+
# duration to. Optional, default to the
|
|
54
|
+
# current time.
|
|
55
|
+
#
|
|
56
|
+
# @return [Integer] The new timestamp, after the duration is applied.
|
|
57
|
+
#
|
|
58
|
+
def self.parse_duration(duration, timestamp=Time.now.utc)
|
|
59
|
+
return nil if RUBY_VERSION < '1.9' # 1.8.7 not supported
|
|
60
|
+
|
|
61
|
+
matches = duration.match(DURATION_FORMAT)
|
|
62
|
+
|
|
63
|
+
if matches.nil?
|
|
64
|
+
raise Exception.new("Invalid ISO 8601 duration")
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
sign = matches[1] == '-' ? -1 : 1
|
|
68
|
+
|
|
69
|
+
durYears, durMonths, durDays, durHours, durMinutes, durSeconds, durWeeks =
|
|
70
|
+
matches[2..8].map { |match| match ? sign * match.tr(',', '.').to_f : 0.0 }
|
|
71
|
+
|
|
72
|
+
initial_datetime = Time.at(timestamp).utc.to_datetime
|
|
73
|
+
final_datetime = initial_datetime.next_year(durYears)
|
|
74
|
+
final_datetime = final_datetime.next_month(durMonths)
|
|
75
|
+
final_datetime = final_datetime.next_day((7*durWeeks) + durDays)
|
|
76
|
+
final_timestamp = final_datetime.to_time.utc.to_i + (durHours * 3600) + (durMinutes * 60) + durSeconds
|
|
77
|
+
return final_timestamp
|
|
78
|
+
end
|
|
17
79
|
|
|
18
80
|
# Return a properly formatted x509 certificate
|
|
19
81
|
#
|
|
@@ -22,7 +84,11 @@ module OneLogin
|
|
|
22
84
|
#
|
|
23
85
|
def self.format_cert(cert)
|
|
24
86
|
# don't try to format an encoded certificate or if is empty or nil
|
|
25
|
-
|
|
87
|
+
if cert.respond_to?(:ascii_only?)
|
|
88
|
+
return cert if cert.nil? || cert.empty? || !cert.ascii_only?
|
|
89
|
+
else
|
|
90
|
+
return cert if cert.nil? || cert.empty? || cert.match(/\x0d/)
|
|
91
|
+
end
|
|
26
92
|
|
|
27
93
|
if cert.scan(/BEGIN CERTIFICATE/).length > 1
|
|
28
94
|
formatted_cert = []
|
|
@@ -101,27 +167,36 @@ module OneLogin
|
|
|
101
167
|
#
|
|
102
168
|
# @param rawparams [Hash] Raw GET Parameters
|
|
103
169
|
# @param params [Hash] GET Parameters
|
|
170
|
+
# @param lowercase_url_encoding [bool] Lowercase URL Encoding (For ADFS urlencode compatiblity)
|
|
104
171
|
# @return [Hash] New raw parameters
|
|
105
172
|
#
|
|
106
|
-
def self.prepare_raw_get_params(rawparams, params)
|
|
173
|
+
def self.prepare_raw_get_params(rawparams, params, lowercase_url_encoding=false)
|
|
107
174
|
rawparams ||= {}
|
|
108
175
|
|
|
109
176
|
if rawparams['SAMLRequest'].nil? && !params['SAMLRequest'].nil?
|
|
110
|
-
rawparams['SAMLRequest'] =
|
|
177
|
+
rawparams['SAMLRequest'] = escape_request_param(params['SAMLRequest'], lowercase_url_encoding)
|
|
111
178
|
end
|
|
112
179
|
if rawparams['SAMLResponse'].nil? && !params['SAMLResponse'].nil?
|
|
113
|
-
rawparams['SAMLResponse'] =
|
|
180
|
+
rawparams['SAMLResponse'] = escape_request_param(params['SAMLResponse'], lowercase_url_encoding)
|
|
114
181
|
end
|
|
115
182
|
if rawparams['RelayState'].nil? && !params['RelayState'].nil?
|
|
116
|
-
rawparams['RelayState'] =
|
|
183
|
+
rawparams['RelayState'] = escape_request_param(params['RelayState'], lowercase_url_encoding)
|
|
117
184
|
end
|
|
118
185
|
if rawparams['SigAlg'].nil? && !params['SigAlg'].nil?
|
|
119
|
-
rawparams['SigAlg'] =
|
|
186
|
+
rawparams['SigAlg'] = escape_request_param(params['SigAlg'], lowercase_url_encoding)
|
|
120
187
|
end
|
|
121
188
|
|
|
122
189
|
rawparams
|
|
123
190
|
end
|
|
124
191
|
|
|
192
|
+
def self.escape_request_param(param, lowercase_url_encoding)
|
|
193
|
+
CGI.escape(param).tap do |escaped|
|
|
194
|
+
next unless lowercase_url_encoding
|
|
195
|
+
|
|
196
|
+
escaped.gsub!(/%[A-Fa-f0-9]{2}/) { |match| match.downcase }
|
|
197
|
+
end
|
|
198
|
+
end
|
|
199
|
+
|
|
125
200
|
# Validate the Signature parameter sent on the HTTP-Redirect binding
|
|
126
201
|
# @param params [Hash] Parameters to be used in the validation process
|
|
127
202
|
# @option params [OpenSSL::X509::Certificate] cert The Identity provider public certtificate
|
|
@@ -236,6 +311,9 @@ module OneLogin
|
|
|
236
311
|
when 'http://www.w3.org/2001/04/xmlenc#aes128-cbc' then cipher = OpenSSL::Cipher.new('AES-128-CBC').decrypt
|
|
237
312
|
when 'http://www.w3.org/2001/04/xmlenc#aes192-cbc' then cipher = OpenSSL::Cipher.new('AES-192-CBC').decrypt
|
|
238
313
|
when 'http://www.w3.org/2001/04/xmlenc#aes256-cbc' then cipher = OpenSSL::Cipher.new('AES-256-CBC').decrypt
|
|
314
|
+
when 'http://www.w3.org/2009/xmlenc11#aes128-gcm' then auth_cipher = OpenSSL::Cipher::AES.new(128, :GCM).decrypt
|
|
315
|
+
when 'http://www.w3.org/2009/xmlenc11#aes192-gcm' then auth_cipher = OpenSSL::Cipher::AES.new(192, :GCM).decrypt
|
|
316
|
+
when 'http://www.w3.org/2009/xmlenc11#aes256-gcm' then auth_cipher = OpenSSL::Cipher::AES.new(256, :GCM).decrypt
|
|
239
317
|
when 'http://www.w3.org/2001/04/xmlenc#rsa-1_5' then rsa = symmetric_key
|
|
240
318
|
when 'http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p' then oaep = symmetric_key
|
|
241
319
|
end
|
|
@@ -246,6 +324,16 @@ module OneLogin
|
|
|
246
324
|
cipher.padding, cipher.key, cipher.iv = 0, symmetric_key, cipher_text[0..iv_len-1]
|
|
247
325
|
assertion_plaintext = cipher.update(data)
|
|
248
326
|
assertion_plaintext << cipher.final
|
|
327
|
+
elsif auth_cipher
|
|
328
|
+
iv_len, text_len, tag_len = auth_cipher.iv_len, cipher_text.length, 16
|
|
329
|
+
data = cipher_text[iv_len..text_len-1-tag_len]
|
|
330
|
+
auth_cipher.padding = 0
|
|
331
|
+
auth_cipher.key = symmetric_key
|
|
332
|
+
auth_cipher.iv = cipher_text[0..iv_len-1]
|
|
333
|
+
auth_cipher.auth_data = ''
|
|
334
|
+
auth_cipher.auth_tag = cipher_text[text_len-tag_len..-1]
|
|
335
|
+
assertion_plaintext = auth_cipher.update(data)
|
|
336
|
+
assertion_plaintext << auth_cipher.final
|
|
249
337
|
elsif rsa
|
|
250
338
|
rsa.private_decrypt(cipher_text)
|
|
251
339
|
elsif oaep
|
|
@@ -255,8 +343,12 @@ module OneLogin
|
|
|
255
343
|
end
|
|
256
344
|
end
|
|
257
345
|
|
|
346
|
+
def self.set_prefix(value)
|
|
347
|
+
UUID_PREFIX.replace value
|
|
348
|
+
end
|
|
349
|
+
|
|
258
350
|
def self.uuid
|
|
259
|
-
RUBY_VERSION < '1.9' ? "
|
|
351
|
+
"#{UUID_PREFIX}" + (RUBY_VERSION < '1.9' ? "#{@@uuid_generator.generate}" : "#{SecureRandom.uuid}")
|
|
260
352
|
end
|
|
261
353
|
|
|
262
354
|
# Given two strings, attempt to match them as URIs using Rails' parse method. If they can be parsed,
|
data/lib/xml_security.rb
CHANGED
|
@@ -159,15 +159,13 @@ module XMLSecurity
|
|
|
159
159
|
x509_cert_element.text = Base64.encode64(certificate.to_der).gsub(/\n/, "")
|
|
160
160
|
|
|
161
161
|
# add the signature
|
|
162
|
-
issuer_element =
|
|
162
|
+
issuer_element = elements["//saml:Issuer"]
|
|
163
163
|
if issuer_element
|
|
164
|
-
|
|
164
|
+
root.insert_after(issuer_element, signature_element)
|
|
165
|
+
elsif first_child = root.children[0]
|
|
166
|
+
root.insert_before(first_child, signature_element)
|
|
165
167
|
else
|
|
166
|
-
|
|
167
|
-
self.root.insert_before sp_sso_descriptor, signature_element
|
|
168
|
-
else
|
|
169
|
-
self.root.add_element(signature_element)
|
|
170
|
-
end
|
|
168
|
+
root.add_element(signature_element)
|
|
171
169
|
end
|
|
172
170
|
end
|
|
173
171
|
|
|
@@ -212,7 +210,7 @@ module XMLSecurity
|
|
|
212
210
|
begin
|
|
213
211
|
cert = OpenSSL::X509::Certificate.new(cert_text)
|
|
214
212
|
rescue OpenSSL::X509::CertificateError => _e
|
|
215
|
-
return append_error("Certificate Error", soft)
|
|
213
|
+
return append_error("Document Certificate Error", soft)
|
|
216
214
|
end
|
|
217
215
|
|
|
218
216
|
if options[:fingerprint_alg]
|
|
@@ -224,7 +222,6 @@ module XMLSecurity
|
|
|
224
222
|
|
|
225
223
|
# check cert matches registered idp cert
|
|
226
224
|
if fingerprint != idp_cert_fingerprint.gsub(/[^a-zA-Z0-9]/,"").downcase
|
|
227
|
-
@errors << "Fingerprint mismatch"
|
|
228
225
|
return append_error("Fingerprint mismatch", soft)
|
|
229
226
|
end
|
|
230
227
|
else
|
|
@@ -241,7 +238,7 @@ module XMLSecurity
|
|
|
241
238
|
validate_signature(base64_cert, soft)
|
|
242
239
|
end
|
|
243
240
|
|
|
244
|
-
def validate_document_with_cert(idp_cert)
|
|
241
|
+
def validate_document_with_cert(idp_cert, soft = true)
|
|
245
242
|
# get cert from response
|
|
246
243
|
cert_element = REXML::XPath.first(
|
|
247
244
|
self,
|
|
@@ -255,12 +252,12 @@ module XMLSecurity
|
|
|
255
252
|
begin
|
|
256
253
|
cert = OpenSSL::X509::Certificate.new(cert_text)
|
|
257
254
|
rescue OpenSSL::X509::CertificateError => _e
|
|
258
|
-
return append_error("Certificate Error", soft)
|
|
255
|
+
return append_error("Document Certificate Error", soft)
|
|
259
256
|
end
|
|
260
257
|
|
|
261
258
|
# check saml response cert matches provided idp cert
|
|
262
259
|
if idp_cert.to_pem != cert.to_pem
|
|
263
|
-
return
|
|
260
|
+
return append_error("Certificate of the Signature element does not match provided certificate", soft)
|
|
264
261
|
end
|
|
265
262
|
else
|
|
266
263
|
base64_cert = Base64.encode64(idp_cert.to_pem)
|
|
@@ -326,6 +323,9 @@ module XMLSecurity
|
|
|
326
323
|
'//ds:CanonicalizationMethod',
|
|
327
324
|
{ "ds" => DSIG }
|
|
328
325
|
)
|
|
326
|
+
|
|
327
|
+
canon_algorithm = process_transforms(ref, canon_algorithm)
|
|
328
|
+
|
|
329
329
|
canon_hashed_element = hashed_element.canonicalize(canon_algorithm, inclusive_namespaces)
|
|
330
330
|
|
|
331
331
|
digest_algorithm = algorithm(REXML::XPath.first(
|
|
@@ -342,7 +342,6 @@ module XMLSecurity
|
|
|
342
342
|
digest_value = Base64.decode64(OneLogin::RubySaml::Utils.element_text(encoded_digest_value))
|
|
343
343
|
|
|
344
344
|
unless digests_match?(hash, digest_value)
|
|
345
|
-
@errors << "Digest mismatch"
|
|
346
345
|
return append_error("Digest mismatch", soft)
|
|
347
346
|
end
|
|
348
347
|
|
|
@@ -360,6 +359,33 @@ module XMLSecurity
|
|
|
360
359
|
|
|
361
360
|
private
|
|
362
361
|
|
|
362
|
+
def process_transforms(ref, canon_algorithm)
|
|
363
|
+
transforms = REXML::XPath.match(
|
|
364
|
+
ref,
|
|
365
|
+
"//ds:Transforms/ds:Transform",
|
|
366
|
+
{ "ds" => DSIG }
|
|
367
|
+
)
|
|
368
|
+
|
|
369
|
+
transforms.each do |transform_element|
|
|
370
|
+
if transform_element.attributes && transform_element.attributes["Algorithm"]
|
|
371
|
+
algorithm = transform_element.attributes["Algorithm"]
|
|
372
|
+
case algorithm
|
|
373
|
+
when "http://www.w3.org/TR/2001/REC-xml-c14n-20010315",
|
|
374
|
+
"http://www.w3.org/TR/2001/REC-xml-c14n-20010315#WithComments"
|
|
375
|
+
canon_algorithm = Nokogiri::XML::XML_C14N_1_0
|
|
376
|
+
when "http://www.w3.org/2006/12/xml-c14n11",
|
|
377
|
+
"http://www.w3.org/2006/12/xml-c14n11#WithComments"
|
|
378
|
+
canon_algorithm = Nokogiri::XML::XML_C14N_1_1
|
|
379
|
+
when "http://www.w3.org/2001/10/xml-exc-c14n#",
|
|
380
|
+
"http://www.w3.org/2001/10/xml-exc-c14n#WithComments"
|
|
381
|
+
canon_algorithm = Nokogiri::XML::XML_C14N_EXCLUSIVE_1_0
|
|
382
|
+
end
|
|
383
|
+
end
|
|
384
|
+
end
|
|
385
|
+
|
|
386
|
+
canon_algorithm
|
|
387
|
+
end
|
|
388
|
+
|
|
363
389
|
def digests_match?(hash, digest_value)
|
|
364
390
|
hash == digest_value
|
|
365
391
|
end
|
data/ruby-saml.gemspec
CHANGED
|
@@ -15,38 +15,51 @@ Gem::Specification.new do |s|
|
|
|
15
15
|
"LICENSE",
|
|
16
16
|
"README.md"
|
|
17
17
|
]
|
|
18
|
-
s.files = `git ls-files`.split("\
|
|
19
|
-
s.homepage = %q{
|
|
20
|
-
s.rubyforge_project = %q{http://www.rubygems.org/gems/ruby-saml}
|
|
18
|
+
s.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
|
19
|
+
s.homepage = %q{https://github.com/onelogin/ruby-saml}
|
|
21
20
|
s.rdoc_options = ["--charset=UTF-8"]
|
|
22
21
|
s.require_paths = ["lib"]
|
|
23
22
|
s.rubygems_version = %q{1.3.7}
|
|
24
23
|
s.required_ruby_version = '>= 1.8.7'
|
|
25
24
|
s.summary = %q{SAML Ruby Tookit}
|
|
26
|
-
s.test_files = `git ls-files test/*`.split("\n")
|
|
27
25
|
|
|
28
26
|
# Because runtime dependencies are determined at build time, we cannot make
|
|
29
27
|
# Nokogiri's version dependent on the Ruby version, even though we would
|
|
30
28
|
# have liked to constrain Ruby 1.8.7 to install only the 1.5.x versions.
|
|
31
29
|
if defined?(JRUBY_VERSION)
|
|
32
|
-
|
|
33
|
-
|
|
30
|
+
if JRUBY_VERSION < '9.2.0.0'
|
|
31
|
+
s.add_runtime_dependency('nokogiri', '>= 1.8.2', '<= 1.8.5')
|
|
32
|
+
s.add_runtime_dependency('jruby-openssl', '>= 0.9.8')
|
|
33
|
+
s.add_runtime_dependency('json', '< 2.3.0')
|
|
34
|
+
else
|
|
35
|
+
s.add_runtime_dependency('nokogiri', '>= 1.8.2')
|
|
36
|
+
end
|
|
34
37
|
elsif RUBY_VERSION < '1.9'
|
|
35
38
|
s.add_runtime_dependency('uuid')
|
|
36
39
|
s.add_runtime_dependency('nokogiri', '<= 1.5.11')
|
|
37
40
|
elsif RUBY_VERSION < '2.1'
|
|
38
41
|
s.add_runtime_dependency('nokogiri', '>= 1.5.10', '<= 1.6.8.1')
|
|
42
|
+
s.add_runtime_dependency('json', '< 2.3.0')
|
|
43
|
+
elsif RUBY_VERSION < '2.3'
|
|
44
|
+
s.add_runtime_dependency('nokogiri', '>= 1.9.1', '< 1.10.0')
|
|
39
45
|
else
|
|
40
|
-
s.add_runtime_dependency('nokogiri', '>= 1.5
|
|
46
|
+
s.add_runtime_dependency('nokogiri', '>= 1.10.5')
|
|
47
|
+
s.add_runtime_dependency('rexml')
|
|
41
48
|
end
|
|
42
49
|
|
|
50
|
+
s.add_development_dependency('coveralls')
|
|
43
51
|
s.add_development_dependency('minitest', '~> 5.5')
|
|
44
52
|
s.add_development_dependency('mocha', '~> 0.14')
|
|
45
53
|
s.add_development_dependency('rake', '~> 10')
|
|
46
54
|
s.add_development_dependency('shoulda', '~> 2.11')
|
|
47
55
|
s.add_development_dependency('simplecov')
|
|
48
56
|
s.add_development_dependency('systemu', '~> 2')
|
|
49
|
-
|
|
57
|
+
|
|
58
|
+
if RUBY_VERSION < '2.1'
|
|
59
|
+
s.add_development_dependency('timecop', '<= 0.6.0')
|
|
60
|
+
else
|
|
61
|
+
s.add_development_dependency('timecop', '~> 0.9')
|
|
62
|
+
end
|
|
50
63
|
|
|
51
64
|
if defined?(JRUBY_VERSION)
|
|
52
65
|
# All recent versions of JRuby play well with pry
|