ruby-saml-mod 0.1.15 → 0.1.16
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.
- data/lib/onelogin/saml/response.rb +6 -8
- data/lib/onelogin/saml/settings.rb +3 -6
- data/lib/onelogin/saml.rb +23 -1
- data/lib/xml_sec.rb +293 -96
- data/ruby-saml-mod.gemspec +1 -1
- metadata +3 -3
@@ -22,14 +22,15 @@ module Onelogin::Saml
|
|
22
22
|
|
23
23
|
@decrypted_document = LibXML::XML::Document.document(@document)
|
24
24
|
@decrypted_document.extend(XMLSecurity::SignedDocument)
|
25
|
-
@decrypted_document.decrypt(@settings)
|
25
|
+
@decrypted_document.decrypt!(@settings)
|
26
26
|
|
27
27
|
@in_response_to = @decrypted_document.find_first("/samlp:Response", Onelogin::NAMESPACES)['InResponseTo'] rescue nil
|
28
28
|
@destination = @decrypted_document.find_first("/samlp:Response", Onelogin::NAMESPACES)['Destination'] rescue nil
|
29
29
|
@name_id = @decrypted_document.find_first("/samlp:Response/saml:Assertion/saml:Subject/saml:NameID", Onelogin::NAMESPACES).content rescue nil
|
30
30
|
@saml_attributes = {}
|
31
31
|
@decrypted_document.find("//saml:Attribute", Onelogin::NAMESPACES).each do |attr|
|
32
|
-
|
32
|
+
attrname = attr['FriendlyName'] || Onelogin::ATTRIBUTES[attr['Name']] || attr['Name']
|
33
|
+
@saml_attributes[attrname] = attr.content.strip rescue nil
|
33
34
|
end
|
34
35
|
@name_qualifier = @decrypted_document.find_first("/samlp:Response/saml:Assertion/saml:Subject/saml:NameID", Onelogin::NAMESPACES)["NameQualifier"] rescue nil
|
35
36
|
@session_index = @decrypted_document.find_first("/samlp:Response/saml:Assertion/saml:AuthnStatement", Onelogin::NAMESPACES)["SessionIndex"] rescue nil
|
@@ -62,10 +63,7 @@ module Onelogin::Saml
|
|
62
63
|
return false
|
63
64
|
end
|
64
65
|
end
|
65
|
-
|
66
|
-
# Technically we should also verify the signature inside the encrypted portion, but if
|
67
|
-
# the cryptext has already been verified, the encrypted contents couldn't have been
|
68
|
-
# tampered with. Once we switch to using libxmlsec this won't matter anymore anyway.
|
66
|
+
|
69
67
|
if !verified && @decrypted_document.find_first("//ds:Signature", Onelogin::NAMESPACES)
|
70
68
|
verified = @decrypted_document.validate(@settings.idp_cert_fingerprint, @logger)
|
71
69
|
if !verified
|
@@ -95,7 +93,7 @@ module Onelogin::Saml
|
|
95
93
|
end
|
96
94
|
|
97
95
|
def fingerprint_from_idp
|
98
|
-
if base64_cert = @
|
96
|
+
if base64_cert = @decrypted_document.find_first("//ds:X509Certificate", Onelogin::NAMESPACES)
|
99
97
|
cert_text = Base64.decode64(base64_cert.content)
|
100
98
|
cert = OpenSSL::X509::Certificate.new(cert_text)
|
101
99
|
Digest::SHA1.hexdigest(cert.to_der)
|
@@ -104,4 +102,4 @@ module Onelogin::Saml
|
|
104
102
|
end
|
105
103
|
end
|
106
104
|
end
|
107
|
-
end
|
105
|
+
end
|
@@ -46,10 +46,7 @@ module Onelogin::Saml
|
|
46
46
|
attr_accessor :tech_contact_email
|
47
47
|
|
48
48
|
## Attributes for xml encryption
|
49
|
-
|
50
|
-
# The path to the xmlsec1 binary used for xml decryption
|
51
|
-
attr_accessor :xmlsec1_path
|
52
|
-
|
49
|
+
|
53
50
|
# The PEM-encoded certificate
|
54
51
|
attr_accessor :xmlsec_certificate
|
55
52
|
|
@@ -57,7 +54,7 @@ module Onelogin::Saml
|
|
57
54
|
attr_accessor :xmlsec_privatekey
|
58
55
|
|
59
56
|
def encryption_configured?
|
60
|
-
self.
|
57
|
+
!!self.xmlsec_privatekey
|
61
58
|
end
|
62
59
|
end
|
63
|
-
end
|
60
|
+
end
|
data/lib/onelogin/saml.rb
CHANGED
@@ -10,6 +10,28 @@ module Onelogin
|
|
10
10
|
"xenc" => "http://www.w3.org/2001/04/xmlenc#",
|
11
11
|
"ds" => "http://www.w3.org/2000/09/xmldsig#"
|
12
12
|
}
|
13
|
+
|
14
|
+
# for SAML2 IDPs that omit the FriendlyName, map from the registered name
|
15
|
+
# http://middleware.internet2.edu/dir/edu-schema-oid-registry.html
|
16
|
+
ATTRIBUTES = {
|
17
|
+
"urn:oid:1.3.6.1.4.1.5923.1.1.2" => "eduPerson",
|
18
|
+
"urn:oid:1.3.6.1.4.1.5923.1.1.1.1" => "eduPersonAffiliation",
|
19
|
+
"urn:oid:1.3.6.1.4.1.5923.1.1.1.7" => "eduPersonEntitlement",
|
20
|
+
"urn:oid:1.3.6.1.4.1.5923.1.1.1.2" => "eduPersonNickname",
|
21
|
+
"urn:oid:1.3.6.1.4.1.5923.1.1.1.3" => "eduPersonOrgDN",
|
22
|
+
"urn:oid:1.3.6.1.4.1.5923.1.1.1.4" => "eduPersonOrgUnitDN",
|
23
|
+
"urn:oid:1.3.6.1.4.1.5923.1.1.1.5" => "eduPersonPrimaryAffiliation",
|
24
|
+
"urn:oid:1.3.6.1.4.1.5923.1.1.1.8" => "eduPersonPrimaryOrgUnitDN",
|
25
|
+
"urn:oid:1.3.6.1.4.1.5923.1.1.1.6" => "eduPersonPrincipalName",
|
26
|
+
"urn:oid:1.3.6.1.4.1.5923.1.1.1.9" => "eduPersonScopedAffiliation",
|
27
|
+
"urn:oid:1.3.6.1.4.1.5923.1.1.1.10" => "eduPersonTargetedID",
|
28
|
+
"urn:oid:1.3.6.1.4.1.5923.1.2.2" => "eduOrg",
|
29
|
+
"urn:oid:1.3.6.1.4.1.5923.1.2.1.2" => "eduOrgHomePageURI",
|
30
|
+
"urn:oid:1.3.6.1.4.1.5923.1.2.1.3" => "eduOrgIdentityAuthNPolicyURI",
|
31
|
+
"urn:oid:1.3.6.1.4.1.5923.1.2.1.4" => "eduOrgLegalName",
|
32
|
+
"urn:oid:1.3.6.1.4.1.5923.1.2.1.5" => "eduOrgSuperiorURI",
|
33
|
+
"urn:oid:1.3.6.1.4.1.5923.1.2.1.6" => "eduOrgWhitePagesURI",
|
34
|
+
}
|
13
35
|
end
|
14
36
|
|
15
37
|
require 'onelogin/saml/auth_request'
|
@@ -20,4 +42,4 @@ require 'onelogin/saml/name_identifiers'
|
|
20
42
|
require 'onelogin/saml/status_codes'
|
21
43
|
require 'onelogin/saml/meta_data'
|
22
44
|
require 'onelogin/saml/log_out_request'
|
23
|
-
require 'onelogin/saml/logout_response'
|
45
|
+
require 'onelogin/saml/logout_response'
|
data/lib/xml_sec.rb
CHANGED
@@ -23,136 +23,333 @@
|
|
23
23
|
# Portions Copyrighted 2007 Todd W Saxton.
|
24
24
|
|
25
25
|
require 'rubygems'
|
26
|
+
require 'ffi'
|
27
|
+
require 'base64'
|
26
28
|
require "xml/libxml"
|
27
29
|
require "openssl"
|
28
30
|
require "digest/sha1"
|
29
|
-
require "tempfile"
|
30
|
-
require "shellwords"
|
31
31
|
|
32
32
|
module XMLSecurity
|
33
|
+
extend FFI::Library
|
34
|
+
ffi_lib "xmlsec1"
|
35
|
+
|
36
|
+
enum :xmlSecKeyDataFormat, [
|
37
|
+
:xmlSecKeyDataFormatUnknown,
|
38
|
+
:xmlSecKeyDataFormatBinary,
|
39
|
+
:xmlSecKeyDataFormatPem,
|
40
|
+
:xmlSecKeyDataFormatDer,
|
41
|
+
:xmlSecKeyDataFormatPkcs8Pem,
|
42
|
+
:xmlSecKeyDataFormatPkcs8Der,
|
43
|
+
:xmlSecKeyDataFormatPkcs12,
|
44
|
+
:xmlSecKeyDataFormatCertPem,
|
45
|
+
:xmlSecKeyDataFormatCertDer
|
46
|
+
]
|
47
|
+
|
48
|
+
enum :xmlSecKeyInfoMode, [
|
49
|
+
:xmlSecKeyInfoModeRead,
|
50
|
+
:xmlSecKeyInfoModeWrite
|
51
|
+
]
|
52
|
+
|
53
|
+
enum :xmlSecAllocMode, [
|
54
|
+
:xmlSecAllocModeExact,
|
55
|
+
:xmlSecAllocModeDouble
|
56
|
+
]
|
57
|
+
|
58
|
+
enum :xmlSecTransformStatus, [
|
59
|
+
:xmlSecTransformStatusNone,
|
60
|
+
:xmlSecTransformStatusWorking,
|
61
|
+
:xmlSecTransformStatusFinished,
|
62
|
+
:xmlSecTransformStatusOk,
|
63
|
+
:xmlSecTransformStatusFail
|
64
|
+
]
|
65
|
+
|
66
|
+
enum :xmlSecTransformOperation, [
|
67
|
+
:xmlSecTransformOperationNone, 0,
|
68
|
+
:xmlSecTransformOperationEncode,
|
69
|
+
:xmlSecTransformOperationDecode,
|
70
|
+
:xmlSecTransformOperationSign,
|
71
|
+
:xmlSecTransformOperationVerify,
|
72
|
+
:xmlSecTransformOperationEncrypt,
|
73
|
+
:xmlSecTransformOperationDecrypt
|
74
|
+
]
|
75
|
+
|
76
|
+
enum :xmlSecDSigStatus, [
|
77
|
+
:xmlSecDSigStatusUnknown, 0,
|
78
|
+
:xmlSecDSigStatusSucceeded,
|
79
|
+
:xmlSecDSigStatusInvalid
|
80
|
+
]
|
81
|
+
|
82
|
+
class XmlSecPtrList < FFI::Struct
|
83
|
+
layout \
|
84
|
+
:id, :string,
|
85
|
+
:data, :pointer, # xmlSecPtr*
|
86
|
+
:use, :uint,
|
87
|
+
:max, :uint,
|
88
|
+
:allocMode, :xmlSecAllocMode
|
89
|
+
end
|
90
|
+
|
91
|
+
class XmlSecKeyReq < FFI::Struct
|
92
|
+
layout \
|
93
|
+
:keyId, :string, # xmlSecKeyDataId
|
94
|
+
:keyType, :uint, # xmlSecKeyDataType
|
95
|
+
:keyUsage, :uint, # xmlSecKeyUsage
|
96
|
+
:keyBitsSize, :uint, # xmlSecSize
|
97
|
+
:keyUseWithList, XmlSecPtrList,
|
98
|
+
:reserved1, :pointer, # void *
|
99
|
+
:reserved2, :pointer # void *
|
100
|
+
end
|
101
|
+
|
102
|
+
class XmlSecTransformCtx < FFI::Struct
|
103
|
+
layout \
|
104
|
+
:userData, :pointer, # void *
|
105
|
+
:flags, :uint,
|
106
|
+
:flags2, :uint,
|
107
|
+
:enabledUris, :uint,
|
108
|
+
:enabledTransforms, XmlSecPtrList,
|
109
|
+
:preExecCallback, :pointer, # xmlSecTransformCtxPreExecuteCallback
|
110
|
+
:result, :pointer, # xmlSecBufferPtr
|
111
|
+
:status, :xmlSecTransformStatus,
|
112
|
+
:uri, :string,
|
113
|
+
:xptrExpr, :string,
|
114
|
+
:first, :pointer, # xmlSecTransformPtr
|
115
|
+
:last, :pointer, # xmlSecTransformPtr
|
116
|
+
:reserved0, :pointer, # void *
|
117
|
+
:reserved1, :pointer # void *
|
118
|
+
end
|
119
|
+
|
120
|
+
class XmlSecKeyInfoCtx < FFI::Struct
|
121
|
+
layout \
|
122
|
+
:userDate, :pointer,
|
123
|
+
:flags, :uint,
|
124
|
+
:flags2, :uint,
|
125
|
+
:keysMngr, :pointer,
|
126
|
+
:mode, :xmlSecKeyInfoMode,
|
127
|
+
:enabledKeyData, XmlSecPtrList,
|
128
|
+
:base64LineSize, :int,
|
129
|
+
:retrievalMethodCtx, XmlSecTransformCtx,
|
130
|
+
:maxRetrievalMethodLevel, :int,
|
131
|
+
:encCtx, :pointer,
|
132
|
+
:maxEncryptedKeyLevel, :int,
|
133
|
+
:certsVerificationTime, :time_t,
|
134
|
+
:certsVerificationDepth, :int,
|
135
|
+
:pgpReserved, :pointer,
|
136
|
+
:curRetrievalMethodLevel, :int,
|
137
|
+
:curEncryptedKeyLevel, :int,
|
138
|
+
:keyReq, XmlSecKeyReq,
|
139
|
+
:reserved0, :pointer,
|
140
|
+
:reserved1, :pointer
|
141
|
+
end
|
142
|
+
|
143
|
+
class XmlSecDSigCtx < FFI::Struct
|
144
|
+
layout \
|
145
|
+
:userData, :pointer, # void *
|
146
|
+
:flags, :uint,
|
147
|
+
:flags2, :uint,
|
148
|
+
:keyInfoReadCtx, XmlSecKeyInfoCtx.by_value,
|
149
|
+
:keyInfoWriteCtx, XmlSecKeyInfoCtx.by_value,
|
150
|
+
:transformCtx, XmlSecTransformCtx.by_value,
|
151
|
+
:enabledReferenceUris, :uint, # xmlSecTransformUriType
|
152
|
+
:enabledReferenceTransforms, :pointer, # xmlSecPtrListPtr
|
153
|
+
:referencePreExecuteCallback, :pointer, # xmlSecTransformCtxPreExecuteCallback
|
154
|
+
:defSignMethodId, :string, # xmlSecTransformId
|
155
|
+
:defC14NMethodId, :string, # xmlSecTransformId
|
156
|
+
:defDigestMethodId, :string, # xmlSecTransformId
|
157
|
+
|
158
|
+
:signKey, :pointer, # xmlSecKeyPtr
|
159
|
+
:operation, :xmlSecTransformOperation,
|
160
|
+
:result, :pointer, # xmlSecBufferPtr
|
161
|
+
:status, :xmlSecDSigStatus,
|
162
|
+
:signMethod, :pointer, # xmlSecTransformPtr
|
163
|
+
:c14nMethod, :pointer, # xmlSecTransformPtr
|
164
|
+
:preSignMemBufMethod, :pointer, # xmlSecTransformPtr
|
165
|
+
:signValueNode, :pointer, # xmlNodePtr
|
166
|
+
:id, :string,
|
167
|
+
:signedInfoReferences, XmlSecPtrList,
|
168
|
+
:manifestReferences, XmlSecPtrList,
|
169
|
+
:reserved0, :pointer,
|
170
|
+
:reserved1, :pointer
|
171
|
+
end
|
172
|
+
|
173
|
+
# xmlsec functions
|
174
|
+
attach_function :xmlSecInit, [], :int
|
175
|
+
attach_function :xmlSecParseMemory, [ :pointer, :uint, :int ], :pointer
|
176
|
+
attach_function :xmlSecFindNode, [ :pointer, :string, :string ], :pointer
|
177
|
+
attach_function :xmlSecDSigCtxCreate, [ :pointer ], XmlSecDSigCtx.by_ref
|
178
|
+
attach_function :xmlSecDSigCtxVerify, [ XmlSecDSigCtx.by_ref, :pointer ], :int
|
179
|
+
attach_function :xmlSecCryptoInit, [], :int
|
180
|
+
attach_function :xmlSecCryptoDLLoadLibrary, [ :string ], :int
|
181
|
+
attach_function :xmlSecCryptoAppInit, [ :pointer ], :int
|
182
|
+
attach_function :xmlSecAddIDs, [ :pointer, :pointer, :pointer ], :void
|
183
|
+
attach_function :xmlSecDSigCtxDestroy, [ XmlSecDSigCtx.by_ref ], :void
|
184
|
+
|
185
|
+
attach_function :xmlSecKeysMngrCreate, [], :pointer
|
186
|
+
attach_function :xmlSecCryptoAppDefaultKeysMngrInit, [ :pointer ], :int
|
187
|
+
attach_function :xmlSecCryptoAppKeyLoad, [ :string, :xmlSecKeyDataFormat, :pointer, :pointer, :pointer ], :pointer
|
188
|
+
attach_function :xmlSecCryptoAppKeyLoadMemory, [ :pointer, :uint, :xmlSecKeyDataFormat, :pointer, :pointer, :pointer ], :pointer
|
189
|
+
attach_function :xmlSecCryptoAppDefaultKeysMngrAdoptKey, [ :pointer, :pointer ], :int
|
190
|
+
attach_function :xmlSecKeysMngrDestroy, [ :pointer ], :void
|
191
|
+
|
192
|
+
attach_function :xmlSecEncCtxCreate, [ :pointer ], :pointer
|
193
|
+
attach_function :xmlSecEncCtxDecrypt, [ :pointer, :pointer ], :int
|
194
|
+
attach_function :xmlSecEncCtxDestroy, [ :pointer ], :void
|
195
|
+
|
196
|
+
# libxml functions
|
197
|
+
attach_function :xmlInitParser, [], :void
|
198
|
+
attach_function :xmlDocGetRootElement, [ :pointer ], :pointer
|
199
|
+
attach_function :xmlDocDumpFormatMemory, [ :pointer, :pointer, :pointer, :int ], :void
|
200
|
+
attach_function :xmlFreeDoc, [ :pointer ], :void
|
201
|
+
|
202
|
+
self.xmlInitParser
|
203
|
+
raise "Failed initializing XMLSec" if self.xmlSecInit < 0
|
204
|
+
raise "Failed initializing xmlsec with openssl" if self.xmlSecCryptoDLLoadLibrary("openssl") < 0
|
205
|
+
raise "Failed initializing app crypto" if self.xmlSecCryptoAppInit(nil) < 0
|
206
|
+
raise "Failed initializing crypto" if self.xmlSecCryptoInit < 0
|
207
|
+
|
33
208
|
module SignedDocument
|
34
209
|
attr_reader :validation_error
|
35
210
|
|
211
|
+
def self.format_cert(cert)
|
212
|
+
# re-encode the certificate in the proper format
|
213
|
+
# this snippet is from http://bugs.ruby-lang.org/issues/4421
|
214
|
+
rsa = cert.public_key
|
215
|
+
modulus = rsa.n
|
216
|
+
exponent = rsa.e
|
217
|
+
oid = OpenSSL::ASN1::ObjectId.new("rsaEncryption")
|
218
|
+
alg_id = OpenSSL::ASN1::Sequence.new([oid, OpenSSL::ASN1::Null.new(nil)])
|
219
|
+
ary = [OpenSSL::ASN1::Integer.new(modulus), OpenSSL::ASN1::Integer.new(exponent)]
|
220
|
+
pub_key = OpenSSL::ASN1::Sequence.new(ary)
|
221
|
+
enc_pk = OpenSSL::ASN1::BitString.new(pub_key.to_der)
|
222
|
+
subject_pk_info = OpenSSL::ASN1::Sequence.new([alg_id, enc_pk])
|
223
|
+
base64 = Base64.encode64(subject_pk_info.to_der)
|
224
|
+
|
225
|
+
# This is the equivalent to the X.509 encoding used in >= 1.9.3
|
226
|
+
"-----BEGIN PUBLIC KEY-----\n#{base64}-----END PUBLIC KEY-----"
|
227
|
+
end
|
228
|
+
|
36
229
|
def validate(idp_cert_fingerprint, logger = nil)
|
37
230
|
# get cert from response
|
38
231
|
base64_cert = self.find_first("//ds:X509Certificate", Onelogin::NAMESPACES).content
|
39
232
|
cert_text = Base64.decode64(base64_cert)
|
40
233
|
cert = OpenSSL::X509::Certificate.new(cert_text)
|
41
234
|
|
42
|
-
# check cert matches registered idp cert
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
235
|
+
# check cert matches registered idp cert, unless we explicitly skip this check
|
236
|
+
unless idp_cert_fingerprint == '*'
|
237
|
+
fingerprint = Digest::SHA1.hexdigest(cert.to_der)
|
238
|
+
expected_fingerprint = idp_cert_fingerprint.gsub(":", "").downcase
|
239
|
+
if fingerprint != expected_fingerprint
|
240
|
+
@validation_error = "Invalid fingerprint (expected #{expected_fingerprint}, got #{fingerprint})"
|
241
|
+
return false
|
242
|
+
end
|
48
243
|
end
|
49
244
|
|
50
|
-
|
51
|
-
|
245
|
+
# create a copy of the document with the certificate removed
|
246
|
+
doc = LibXML::XML::Document.new
|
247
|
+
doc.root = doc.import(self.root)
|
248
|
+
sigcert = doc.find_first("//ds:Signature/ds:KeyInfo", Onelogin::NAMESPACES)
|
249
|
+
sigcert.remove!
|
52
250
|
|
53
|
-
|
54
|
-
|
55
|
-
# the hackery is imminent, so I'm not going to spend a lot of time on it.
|
56
|
-
mode = 0; comments = false
|
57
|
-
if method
|
58
|
-
mode = 1 if method =~ %r{xml-exc-c14n}
|
59
|
-
mode = 2 if method =~ %r{xml-c14n11}
|
60
|
-
comments = method =~ %r{#withcomments}i
|
61
|
-
end
|
62
|
-
doc.canonicalize(:mode => mode, :comments => comments)
|
251
|
+
# validate it!
|
252
|
+
validate_doc(doc.to_s(:indent => false), SignedDocument.format_cert(cert))
|
63
253
|
end
|
64
254
|
|
65
|
-
def
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
end
|
255
|
+
def validate_doc(xml, pem)
|
256
|
+
kmgr = nil
|
257
|
+
ctx = nil
|
258
|
+
result = false
|
70
259
|
|
71
|
-
|
72
|
-
|
73
|
-
|
260
|
+
begin
|
261
|
+
# set up the keymgr
|
262
|
+
kmgr = XMLSecurity.xmlSecKeysMngrCreate
|
263
|
+
raise "failed initializing key mgr" if XMLSecurity.xmlSecCryptoAppDefaultKeysMngrInit(kmgr) < 0
|
264
|
+
key = XMLSecurity.xmlSecCryptoAppKeyLoadMemory(pem, pem.length, :xmlSecKeyDataFormatPem, nil, nil, nil)
|
265
|
+
raise "failed loading key" if key.null?
|
266
|
+
raise "failed adding key to mgr" if XMLSecurity.xmlSecCryptoAppDefaultKeysMngrAdoptKey(kmgr, key) < 0
|
74
267
|
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
c14n_method = c14n_method_element["Algorithm"]
|
79
|
-
end
|
268
|
+
# parse the xml
|
269
|
+
doc = XMLSecurity.xmlSecParseMemory(xml, xml.length, 0)
|
270
|
+
root = XMLSecurity.xmlDocGetRootElement(doc)
|
80
271
|
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
# Create a copy document with it
|
88
|
-
ref_document = LibXML::XML::Document.new
|
89
|
-
ref_document.root = ref_document.import(ref_element)
|
90
|
-
|
91
|
-
# Remove the Signature node
|
92
|
-
ref_document_sig_element = ref_document.find_first(".//ds:Signature", Onelogin::NAMESPACES)
|
93
|
-
ref_document_sig_element.remove! if ref_document_sig_element
|
94
|
-
|
95
|
-
# Canonicalize the referenced element's document
|
96
|
-
ref_document_canonicalized = canonicalize_doc(ref_document, c14n_method)
|
97
|
-
hash = Base64::encode64(Digest::SHA1.digest(ref_document_canonicalized)).chomp
|
98
|
-
digest_value = sig_element.find_first(".//ds:DigestValue", Onelogin::NAMESPACES).content
|
99
|
-
|
100
|
-
if hash != digest_value
|
101
|
-
@validation_error = <<-EOF.gsub(/^\s+/, '')
|
102
|
-
Invalid references digest.
|
103
|
-
Got digest of
|
104
|
-
#{hash}
|
105
|
-
but expected
|
106
|
-
#{digest_value}
|
107
|
-
XML from response:
|
108
|
-
#{ref_document.to_s(:indent => false)}
|
109
|
-
Canonized XML:
|
110
|
-
#{ref_document_canonicalized}
|
111
|
-
EOF
|
112
|
-
return false
|
113
|
-
end
|
114
|
-
end
|
115
|
-
|
116
|
-
# verify signature
|
117
|
-
signed_info_element = sig_element.find_first(".//ds:SignedInfo", Onelogin::NAMESPACES)
|
118
|
-
canon_string = canonicalize_node(signed_info_element, c14n_method)
|
272
|
+
# add the ID attribute as an id. yeah, hacky
|
273
|
+
idary = FFI::MemoryPointer.new(:pointer, 2)
|
274
|
+
idary[0].put_pointer(0, FFI::MemoryPointer.from_string("ID"))
|
275
|
+
idary[1].put_pointer(0, nil)
|
276
|
+
XMLSecurity.xmlSecAddIDs(doc, root, idary)
|
119
277
|
|
120
|
-
|
121
|
-
|
278
|
+
# get the root node, and then find the signature
|
279
|
+
node = XMLSecurity.xmlSecFindNode(root, "Signature", "http://www.w3.org/2000/09/xmldsig#")
|
280
|
+
raise "Signature node not found" if node.null?
|
122
281
|
|
123
|
-
|
124
|
-
|
282
|
+
# create the sig context
|
283
|
+
ctx = XMLSecurity.xmlSecDSigCtxCreate(kmgr)
|
284
|
+
raise "failed creating digital signature context" if ctx.null?
|
125
285
|
|
126
|
-
|
127
|
-
|
128
|
-
|
286
|
+
# verify!
|
287
|
+
raise "failed verifying dsig" if XMLSecurity.xmlSecDSigCtxVerify(ctx, node) < 0
|
288
|
+
result = ctx[:status] == :xmlSecDSigStatusSucceeded
|
289
|
+
@validation_error = ctx[:status].to_s unless result
|
290
|
+
rescue Exception => e
|
291
|
+
@validation_error = e.message
|
292
|
+
ensure
|
293
|
+
XMLSecurity.xmlSecDSigCtxDestroy(ctx) if ctx
|
294
|
+
XMLSecurity.xmlFreeDoc(doc) if doc
|
295
|
+
XMLSecurity.xmlSecKeysMngrDestroy(kmgr) if kmgr
|
129
296
|
end
|
130
|
-
|
297
|
+
|
298
|
+
result
|
131
299
|
end
|
132
300
|
|
133
|
-
|
301
|
+
# replaces EncryptedData nodes with decrypted copies
|
302
|
+
def decrypt!(settings)
|
134
303
|
if settings.encryption_configured?
|
135
304
|
find("//xenc:EncryptedData", Onelogin::NAMESPACES).each do |node|
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
return false
|
144
|
-
else
|
145
|
-
decrypted_doc = LibXML::XML::Document.string(decrypted_xml)
|
146
|
-
decrypted_node = decrypted_doc.root
|
147
|
-
decrypted_node = self.import(decrypted_node)
|
148
|
-
node.parent.next = decrypted_node
|
149
|
-
node.parent.remove!
|
150
|
-
end
|
151
|
-
f.unlink
|
305
|
+
decrypted_xml = decrypt_node(settings, node.to_s)
|
306
|
+
if decrypted_xml
|
307
|
+
decrypted_doc = LibXML::XML::Document.string(decrypted_xml)
|
308
|
+
decrypted_node = decrypted_doc.root
|
309
|
+
decrypted_node = self.import(decrypted_node)
|
310
|
+
node.parent.next = decrypted_node
|
311
|
+
node.parent.remove!
|
152
312
|
end
|
153
313
|
end
|
154
314
|
end
|
155
315
|
true
|
156
316
|
end
|
317
|
+
|
318
|
+
def decrypt_node(settings, xmlstr)
|
319
|
+
kmgr = nil
|
320
|
+
ctx = nil
|
321
|
+
doc = nil
|
322
|
+
result = nil
|
323
|
+
begin
|
324
|
+
kmgr = XMLSecurity.xmlSecKeysMngrCreate
|
325
|
+
raise "Failed initializing key mgr" if XMLSecurity.xmlSecCryptoAppDefaultKeysMngrInit(kmgr) < 0
|
326
|
+
|
327
|
+
key = XMLSecurity.xmlSecCryptoAppKeyLoad(settings.xmlsec_privatekey, :xmlSecKeyDataFormatPem, nil, nil, nil)
|
328
|
+
raise "Failed loading key" if key.null?
|
329
|
+
raise "Failed adding key to mgr" if XMLSecurity.xmlSecCryptoAppDefaultKeysMngrAdoptKey(kmgr, key) < 0
|
330
|
+
|
331
|
+
doc = XMLSecurity.xmlSecParseMemory(xmlstr, xmlstr.length, 0)
|
332
|
+
raise "Failed to parse node" if doc.null?
|
333
|
+
|
334
|
+
ctx = XMLSecurity.xmlSecEncCtxCreate(kmgr)
|
335
|
+
raise "failed creating enc ctx" if ctx.null?
|
336
|
+
|
337
|
+
node = XMLSecurity.xmlDocGetRootElement(doc)
|
338
|
+
raise "failed decrypting" if XMLSecurity.xmlSecEncCtxDecrypt(ctx, node) < 0
|
339
|
+
|
340
|
+
ptr = FFI::MemoryPointer.new(:pointer, 1)
|
341
|
+
sizeptr = FFI::MemoryPointer.new(:pointer, 1)
|
342
|
+
XMLSecurity.xmlDocDumpFormatMemory(doc, ptr, sizeptr, 0)
|
343
|
+
strptr = ptr.read_pointer
|
344
|
+
result = strptr.null? ? nil : strptr.read_string
|
345
|
+
rescue Exception => e
|
346
|
+
@logger.warn "Could not decrypt: #{e.message}" if @logger
|
347
|
+
ensure
|
348
|
+
XMLSecurity.xmlSecEncCtxDestroy(ctx) if ctx
|
349
|
+
XMLSecurity.xmlFreeDoc(doc) if doc
|
350
|
+
XMLSecurity.xmlSecKeysMngrDestroy(kmgr) if kmgr
|
351
|
+
end
|
352
|
+
result
|
353
|
+
end
|
157
354
|
end
|
158
355
|
end
|
data/ruby-saml-mod.gemspec
CHANGED
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ruby-saml-mod
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 59
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 0
|
8
8
|
- 1
|
9
|
-
-
|
10
|
-
version: 0.1.
|
9
|
+
- 16
|
10
|
+
version: 0.1.16
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- OneLogin LLC
|