21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
|
# File 'lib/saml2/bindings/http_redirect.rb', line 21
def decode(url, public_key: nil, public_key_used: nil)
uri = begin
URI.parse(url)
rescue URI::InvalidURIError
raise CorruptMessage
end
raise MissingMessage unless uri.query
query = URI.decode_www_form(uri.query)
base64 = query.assoc('SAMLRequest')&.last
if base64
message_param = 'SAMLRequest'
else
base64 = query.assoc('SAMLResponse')&.last
message_param = 'SAMLResponse'
end
encoding = query.assoc('SAMLEncoding')&.last
relay_state = query.assoc('RelayState')&.last
signature = query.assoc('Signature')&.last
sig_alg = query.assoc('SigAlg')&.last
raise MissingMessage unless base64
raise UnsupportedEncoding if encoding && encoding != Encodings::DEFLATE
raise MessageTooLarge if base64.bytesize > SAML2.config[:max_message_size]
deflated = begin
Base64.strict_decode64(base64)
rescue ArgumentError
raise CorruptMessage
end
zstream = Zlib::Inflate.new(-Zlib::MAX_WBITS)
xml = ''
begin
(0..deflated.bytesize / 1024).each do |i|
xml.concat(zstream.inflate(deflated.byteslice(i * 1024, 1024)))
raise MessageTooLarge if xml.bytesize > SAML2.config[:max_message_size]
end
xml.concat(zstream.finish)
raise MessageTooLarge if xml.bytesize > SAML2.config[:max_message_size]
rescue Zlib::DataError, Zlib::BufError
raise CorruptMessage
end
zstream.close
message = Message.parse(xml)
public_key ||= yield(message, sig_alg) if block_given?
if public_key
raise UnsignedMessage unless signature
raise UnsupportedSignatureAlgorithm unless SigAlgs::RECOGNIZED.include?(sig_alg)
begin
signature = Base64.strict_decode64(signature)
rescue ArgumentError
raise CorruptMessage
end
base_string = find_raw_query_param(uri.query, message_param)
base_string << '&' << find_raw_query_param(uri.query, 'RelayState') if relay_state
base_string << '&' << find_raw_query_param(uri.query, 'SigAlg')
valid_signature = false
Array(public_key).each do |key|
if key.verify(OpenSSL::Digest::SHA1.new, signature, base_string)
public_key_used&.call(key)
valid_signature = true
break
end
end
raise InvalidSignature unless valid_signature
end
[message, relay_state]
end
|