ruby-saml-mod 0.1.15 → 0.1.16

Sign up to get free protection for your applications and to get access to all the features.
@@ -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