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.
@@ -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
- @saml_attributes[attr['FriendlyName']] = attr.content.strip rescue nil
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 = @document.find_first("//ds:X509Certificate", Onelogin::NAMESPACES)
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.xmlsec1_path && self.xmlsec_certificate && self.xmlsec_privatekey
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
- fingerprint = Digest::SHA1.hexdigest(cert.to_der)
44
- expected_fingerprint = idp_cert_fingerprint.gsub(":", "").downcase
45
- if fingerprint != expected_fingerprint
46
- @validation_error = "Invalid fingerprint (expected #{expected_fingerprint}, got #{fingerprint})"
47
- return false
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
- validate_doc(base64_cert, logger)
51
- end
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
- def canonicalize_doc(doc, method)
54
- # this is not robust enough, but a switch to libxmlsec replacing all
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 canonicalize_node(node, method)
66
- tmp_document = LibXML::XML::Document.new
67
- tmp_document.root = tmp_document.import(node)
68
- canonicalize_doc(tmp_document, method)
69
- end
255
+ def validate_doc(xml, pem)
256
+ kmgr = nil
257
+ ctx = nil
258
+ result = false
70
259
 
71
- def validate_doc(base64_cert, logger)
72
- # validate references
73
- sig_element = find_first("//ds:Signature", Onelogin::NAMESPACES)
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
- c14n_method = nil
76
- c14n_method_element = sig_element.find_first(".//ds:CanonicalizationMethod", Onelogin::NAMESPACES)
77
- if c14n_method_element
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
- # check digests
82
- sig_element.find(".//ds:Reference", Onelogin::NAMESPACES).each do |ref|
83
- # Find the referenced element
84
- uri = ref["URI"]
85
- ref_element = find_first("//*[@ID='#{uri[1,uri.size]}']")
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
- base64_signature = sig_element.find_first(".//ds:SignatureValue", Onelogin::NAMESPACES).content
121
- signature = Base64.decode64(base64_signature)
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
- cert_text = Base64.decode64(base64_cert)
124
- cert = OpenSSL::X509::Certificate.new(cert_text)
282
+ # create the sig context
283
+ ctx = XMLSecurity.xmlSecDSigCtxCreate(kmgr)
284
+ raise "failed creating digital signature context" if ctx.null?
125
285
 
126
- if !cert.public_key.verify(OpenSSL::Digest::SHA1.new, signature, canon_string)
127
- @validation_error = "Invalid public key"
128
- return false
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
- return true
297
+
298
+ result
131
299
  end
132
300
 
133
- def decrypt(settings)
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
- Tempfile.open("ruby-saml-decrypt") do |f|
137
- f.puts node.to_s
138
- f.close
139
- command = [ settings.xmlsec1_path, "decrypt", "--privkey-pem", settings.xmlsec_privatekey, f.path ].shelljoin
140
- decrypted_xml = %x{#{command}}
141
- if $?.exitstatus != 0
142
- @logger.warn "Could not decrypt: #{decrypted_xml}" if @logger
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
@@ -1,6 +1,6 @@
1
1
  Gem::Specification.new do |s|
2
2
  s.name = %q{ruby-saml-mod}
3
- s.version = "0.1.15"
3
+ s.version = "0.1.16"
4
4
 
5
5
  s.authors = ["OneLogin LLC", "Bracken", "Zach", "Cody"]
6
6
  s.date = %q{2012-06-20}
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: 5
4
+ hash: 59
5
5
  prerelease:
6
6
  segments:
7
7
  - 0
8
8
  - 1
9
- - 15
10
- version: 0.1.15
9
+ - 16
10
+ version: 0.1.16
11
11
  platform: ruby
12
12
  authors:
13
13
  - OneLogin LLC