ruby-saml-mod 0.1.19 → 0.1.20

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.
@@ -2,6 +2,7 @@ require 'zlib'
2
2
  require "base64"
3
3
  require "xml/libxml"
4
4
  require "xml_sec"
5
+ require "cgi"
5
6
 
6
7
  module Onelogin
7
8
  NAMESPACES = {
@@ -16,19 +16,41 @@ module Onelogin::Saml
16
16
  @id = Onelogin::Saml::AuthRequest.generate_unique_id(42)
17
17
  issue_instant = Onelogin::Saml::AuthRequest.get_timestamp
18
18
 
19
- @request_xml = "<samlp:LogoutRequest xmlns:samlp=\"urn:oasis:names:tc:SAML:2.0:protocol\" ID=\"#{@id}\" Version=\"2.0\" IssueInstant=\"#{issue_instant}\"> " +
19
+ @request_xml = "<samlp:LogoutRequest xmlns:samlp=\"urn:oasis:names:tc:SAML:2.0:protocol\" ID=\"#{@id}\" Version=\"2.0\" IssueInstant=\"#{issue_instant}\" Destination=\"#{@settings.idp_slo_target_url}\">" +
20
20
  "<saml:Issuer xmlns:saml=\"urn:oasis:names:tc:SAML:2.0:assertion\">#{@settings.issuer}</saml:Issuer>" +
21
21
  "<saml:NameID xmlns:saml=\"urn:oasis:names:tc:SAML:2.0:assertion\" NameQualifier=\"#{@session[:name_qualifier]}\" SPNameQualifier=\"#{@settings.issuer}\" Format=\"urn:oasis:names:tc:SAML:2.0:nameid-format:transient\">#{@session[:name_id]}</saml:NameID>" +
22
22
  "<samlp:SessionIndex xmlns:samlp=\"urn:oasis:names:tc:SAML:2.0:protocol\">#{@session[:session_index]}</samlp:SessionIndex>" +
23
23
  "</samlp:LogoutRequest>"
24
24
 
25
- deflated_logout_request = Zlib::Deflate.deflate(@request_xml, 9)[2..-5]
26
- base64_logout_request = Base64.encode64(deflated_logout_request)
27
- encoded_logout_request = CGI.escape(base64_logout_request)
25
+ if settings.sign?
26
+ @request_xml = XMLSecurity.sign(@request_xml, @settings.xmlsec_privatekey)
27
+ end
28
28
 
29
- @forward_url = @settings.idp_slo_target_url + (@settings.idp_slo_target_url.include?("?") ? "&" : "?") + "SAMLRequest=" + encoded_logout_request
29
+ deflated_logout_request = Zlib::Deflate.deflate(@request_xml, 9)[2..-5]
30
+ base64_logout_request = Base64.encode64(deflated_logout_request)
31
+
32
+ url, existing_query_string = @settings.idp_slo_target_url.split('?')
33
+ query_string = _query_string_append(existing_query_string, 'SAMLRequest', base64_logout_request)
34
+
35
+ if settings.sign?
36
+ query_string = _query_string_append(query_string, "SigAlg", "http://www.w3.org/2000/09/xmldsig#rsa-sha1")
37
+ signature = _generate_signature(query_string, @settings.xmlsec_privatekey)
38
+ query_string = _query_string_append(query_string, "Signature", signature)
39
+ end
40
+
41
+ @forward_url = [url, query_string].join("?")
30
42
 
31
43
  @forward_url
32
44
  end
45
+
46
+ def _generate_signature(string, private_key)
47
+ pkey = OpenSSL::PKey::RSA.new(File.read(private_key))
48
+ sign = pkey.sign(OpenSSL::Digest::SHA1.new, string)
49
+ Base64.encode64(sign).gsub(/\s/, '')
50
+ end
51
+
52
+ def _query_string_append(query_string, key, value)
53
+ [query_string, "#{CGI.escape(key)}=#{CGI.escape(value)}"].compact.join('&')
54
+ end
33
55
  end
34
56
  end
@@ -21,6 +21,20 @@ module Onelogin::Saml
21
21
  </KeyDescriptor>
22
22
  }
23
23
  end
24
+ if settings.sign?
25
+ xml += %{
26
+ <KeyDescriptor use="signing">
27
+ <KeyInfo xmlns="http://www.w3.org/2000/09/xmldsig#">
28
+ <X509Data>
29
+ <X509Certificate>
30
+ #{File.read(settings.xmlsec_certificate).gsub(/\w*-+(BEGIN|END) CERTIFICATE-+\w*/, "").strip}
31
+ </X509Certificate>
32
+ </X509Data>
33
+ </KeyInfo>
34
+ </KeyDescriptor>
35
+ }
36
+ end
37
+
24
38
  xml += %{
25
39
  <SingleLogoutService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" Location="#{settings.sp_slo_url}"/>
26
40
  }
@@ -56,5 +56,9 @@ module Onelogin::Saml
56
56
  def encryption_configured?
57
57
  !!self.xmlsec_privatekey
58
58
  end
59
+
60
+ def sign?
61
+ !!self.xmlsec_privatekey
62
+ end
59
63
  end
60
64
  end
@@ -192,17 +192,58 @@ module XMLSecurity
192
192
  attach_function :xmlSecEncCtxDecrypt, [ :pointer, :pointer ], :int
193
193
  attach_function :xmlSecEncCtxDestroy, [ :pointer ], :void
194
194
 
195
+ attach_function :xmlSecErrorsDefaultCallbackEnableOutput, [ :bool ], :void
196
+
197
+ attach_function :xmlSecTransformExclC14NGetKlass, [], :pointer
198
+ attach_function :xmlSecOpenSSLTransformRsaSha1GetKlass, [], :pointer
199
+ attach_function :xmlSecOpenSSLTransformSha1GetKlass, [], :pointer
200
+
201
+ attach_function :xmlSecTmplSignatureCreate, [ :pointer, :pointer, :pointer, :string ], :pointer
202
+ attach_function :xmlSecTmplSignatureAddReference, [ :pointer, :pointer, :pointer, :pointer, :pointer ], :pointer
203
+ attach_function :xmlSecTransformEnvelopedGetKlass, [], :pointer
204
+ attach_function :xmlSecTmplSignatureEnsureKeyInfo, [ :pointer, :pointer ], :pointer
205
+ attach_function :xmlSecTmplReferenceAddTransform, [ :pointer, :pointer ], :pointer
206
+
207
+ attach_function :xmlSecKeySetName, [ :pointer, :string ], :int
208
+
209
+ attach_function :xmlSecTmplKeyInfoAddKeyName, [ :pointer, :pointer ], :pointer
210
+
211
+ attach_function :xmlSecDSigCtxSign, [ :pointer, :pointer ], :int
212
+
213
+
214
+
215
+
195
216
  # libxml functions
196
217
  attach_function :xmlInitParser, [], :void
197
218
  attach_function :xmlDocGetRootElement, [ :pointer ], :pointer
198
219
  attach_function :xmlDocDumpFormatMemory, [ :pointer, :pointer, :pointer, :int ], :void
199
220
  attach_function :xmlFreeDoc, [ :pointer ], :void
221
+ attach_function :xmlParseMemory, [ :pointer, :int ], :pointer
222
+ attach_function :xmlAddChild, [ :pointer, :pointer ], :pointer
223
+
224
+ attach_variable :__xmlFree, :xmlFree, callback([ :pointer ], :void)
225
+ attach_variable :__xmlMalloc, :xmlMalloc, callback([ :size_t ], :pointer)
226
+
227
+ def self.xmlMalloc(size)
228
+ __xmlMalloc.call(size)
229
+ end
230
+
231
+ def self.xmlFree(ptr)
232
+ __xmlFree.call(ptr)
233
+ end
200
234
 
201
235
  self.xmlInitParser
202
236
  raise "Failed initializing XMLSec" if self.xmlSecInit < 0
203
237
  raise "Failed initializing app crypto" if self.xmlSecOpenSSLAppInit(nil) < 0
204
238
  raise "Failed initializing crypto" if self.xmlSecOpenSSLInit < 0
205
239
 
240
+
241
+ def self.mute(&block)
242
+ xmlSecErrorsDefaultCallbackEnableOutput(false)
243
+ block.call
244
+ xmlSecErrorsDefaultCallbackEnableOutput(true)
245
+ end
246
+
206
247
  module SignedDocument
207
248
  attr_reader :validation_error
208
249
 
@@ -350,4 +391,59 @@ module XMLSecurity
350
391
  result
351
392
  end
352
393
  end
394
+
395
+ class SignatureFailure < RuntimeError; end
396
+
397
+ def self.sign(xml_string, private_key)
398
+ doc = self.xmlParseMemory(xml_string, xml_string.size)
399
+ raise SignatureFailure.new("could not parse XML document") if doc.null?
400
+
401
+ canonicalization_method_id = self.xmlSecTransformExclC14NGetKlass
402
+ sign_method_id = self.xmlSecOpenSSLTransformRsaSha1GetKlass
403
+
404
+ sign_node = self.xmlSecTmplSignatureCreate(doc, canonicalization_method_id, sign_method_id, nil)
405
+
406
+ raise SignatureFailure.new("failed to create signature template") if sign_node.null?
407
+ self.xmlAddChild(self.xmlDocGetRootElement(doc), sign_node)
408
+
409
+ ref_node = self.xmlSecTmplSignatureAddReference(sign_node, self.xmlSecOpenSSLTransformSha1GetKlass, nil, nil, nil)
410
+ raise SignatureFailure.new("failed to add a reference") if ref_node.null?
411
+
412
+ envelope_result = self.xmlSecTmplReferenceAddTransform(ref_node, self.xmlSecTransformEnvelopedGetKlass)
413
+ raise SignatureFailure.new("failed to add envelope transform to reference") if envelope_result.null?
414
+
415
+ key_info_node = self.xmlSecTmplSignatureEnsureKeyInfo(sign_node, nil)
416
+ raise SignatureFailure.new("failed to add key info") if key_info_node.null?
417
+
418
+ digital_signature_context = self.xmlSecDSigCtxCreate(nil)
419
+ raise SignatureFailure.new("failed to create signature context") if digital_signature_context.null?
420
+
421
+ digital_signature_context[:signKey] = self.xmlSecOpenSSLAppKeyLoad(private_key, :xmlSecKeyDataFormatPem, nil, nil, nil)
422
+ raise SignatureFailure.new("failed to load private pem ley from #{private_key}") if digital_signature_context[:signKey].null?
423
+
424
+ if self.xmlSecKeySetName(digital_signature_context[:signKey], File.basename(private_key)) < 0
425
+ raise SignatureFailure.new("failed to set key name for key of #{private_key}")
426
+ end
427
+
428
+ if self.xmlSecTmplKeyInfoAddKeyName(key_info_node, nil).null?
429
+ raise SignatureFailure.new("failed to add key info")
430
+ end
431
+
432
+ if self.xmlSecDSigCtxSign(digital_signature_context, sign_node) < 0
433
+ raise SignatureFailure.new("signature failed!")
434
+ end
435
+
436
+ ptr = FFI::MemoryPointer.new(:pointer, 1)
437
+ sizeptr = FFI::MemoryPointer.new(:pointer, 1)
438
+ self.xmlDocDumpFormatMemory(doc, ptr, sizeptr, 1)
439
+ strptr = ptr.read_pointer
440
+ result = strptr.null? ? nil : strptr.read_string
441
+ ensure
442
+ ptr.free if defined?(ptr) && ptr
443
+ sizeptr.free if defined?(sizeptr) && sizeptr
444
+ self.xmlFreeDoc(doc) if defined?(doc) && doc && !doc.null?
445
+ self.xmlSecDSigCtxDestroy(digital_signature_context) if defined?(digital_signature_context) && digital_signature_context && !digital_signature_context.null?
446
+ self.xmlFree(strptr) if defined?(strptr) && strptr && !strptr.null?
447
+ end
448
+
353
449
  end
@@ -1,9 +1,9 @@
1
1
  Gem::Specification.new do |s|
2
2
  s.name = %q{ruby-saml-mod}
3
- s.version = "0.1.19"
3
+ s.version = "0.1.20"
4
4
 
5
- s.authors = ["OneLogin LLC", "Bracken", "Zach", "Cody", "Jeremy"]
6
- s.date = %q{2012-09-26}
5
+ s.authors = ["OneLogin LLC", "Bracken", "Zach", "Cody", "Jeremy", "Paul"]
6
+ s.date = %q{2013-02-15}
7
7
  s.extra_rdoc_files = [
8
8
  "LICENSE"
9
9
  ]
@@ -26,9 +26,6 @@ Gem::Specification.new do |s|
26
26
  s.add_dependency('libxml-ruby', '>= 2.3.0')
27
27
  s.add_dependency('ffi')
28
28
 
29
- s.add_development_dependency 'ruby-debug'
30
- s.add_development_dependency 'rspec', '1.3.2'
31
-
32
29
  s.homepage = %q{http://github.com/bracken/ruby-saml}
33
30
  s.require_paths = ["lib"]
34
31
  s.summary = %q{Ruby library for SAML service providers}
metadata CHANGED
@@ -1,95 +1,61 @@
1
- --- !ruby/object:Gem::Specification
1
+ --- !ruby/object:Gem::Specification
2
2
  name: ruby-saml-mod
3
- version: !ruby/object:Gem::Version
4
- hash: 61
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.20
5
5
  prerelease:
6
- segments:
7
- - 0
8
- - 1
9
- - 19
10
- version: 0.1.19
11
6
  platform: ruby
12
- authors:
7
+ authors:
13
8
  - OneLogin LLC
14
9
  - Bracken
15
10
  - Zach
16
11
  - Cody
17
12
  - Jeremy
13
+ - Paul
18
14
  autorequire:
19
15
  bindir: bin
20
16
  cert_chain: []
21
-
22
- date: 2012-09-26 00:00:00 Z
23
- dependencies:
24
- - !ruby/object:Gem::Dependency
17
+ date: 2013-02-15 00:00:00.000000000 Z
18
+ dependencies:
19
+ - !ruby/object:Gem::Dependency
25
20
  name: libxml-ruby
26
- prerelease: false
27
- requirement: &id001 !ruby/object:Gem::Requirement
21
+ requirement: !ruby/object:Gem::Requirement
28
22
  none: false
29
- requirements:
30
- - - ">="
31
- - !ruby/object:Gem::Version
32
- hash: 3
33
- segments:
34
- - 2
35
- - 3
36
- - 0
23
+ requirements:
24
+ - - ! '>='
25
+ - !ruby/object:Gem::Version
37
26
  version: 2.3.0
38
27
  type: :runtime
39
- version_requirements: *id001
40
- - !ruby/object:Gem::Dependency
41
- name: ffi
42
28
  prerelease: false
43
- requirement: &id002 !ruby/object:Gem::Requirement
29
+ version_requirements: !ruby/object:Gem::Requirement
44
30
  none: false
45
- requirements:
46
- - - ">="
47
- - !ruby/object:Gem::Version
48
- hash: 3
49
- segments:
50
- - 0
51
- version: "0"
52
- type: :runtime
53
- version_requirements: *id002
54
- - !ruby/object:Gem::Dependency
55
- name: ruby-debug
56
- prerelease: false
57
- requirement: &id003 !ruby/object:Gem::Requirement
31
+ requirements:
32
+ - - ! '>='
33
+ - !ruby/object:Gem::Version
34
+ version: 2.3.0
35
+ - !ruby/object:Gem::Dependency
36
+ name: ffi
37
+ requirement: !ruby/object:Gem::Requirement
58
38
  none: false
59
- requirements:
60
- - - ">="
61
- - !ruby/object:Gem::Version
62
- hash: 3
63
- segments:
64
- - 0
65
- version: "0"
66
- type: :development
67
- version_requirements: *id003
68
- - !ruby/object:Gem::Dependency
69
- name: rspec
39
+ requirements:
40
+ - - ! '>='
41
+ - !ruby/object:Gem::Version
42
+ version: '0'
43
+ type: :runtime
70
44
  prerelease: false
71
- requirement: &id004 !ruby/object:Gem::Requirement
45
+ version_requirements: !ruby/object:Gem::Requirement
72
46
  none: false
73
- requirements:
74
- - - "="
75
- - !ruby/object:Gem::Version
76
- hash: 31
77
- segments:
78
- - 1
79
- - 3
80
- - 2
81
- version: 1.3.2
82
- type: :development
83
- version_requirements: *id004
84
- description: "This is an early fork from https://github.com/onelogin/ruby-saml - I plan to \"rebase\" these changes ontop of their current version eventually. "
47
+ requirements:
48
+ - - ! '>='
49
+ - !ruby/object:Gem::Version
50
+ version: '0'
51
+ description: ! 'This is an early fork from https://github.com/onelogin/ruby-saml -
52
+ I plan to "rebase" these changes ontop of their current version eventually. '
85
53
  email:
86
54
  executables: []
87
-
88
55
  extensions: []
89
-
90
- extra_rdoc_files:
56
+ extra_rdoc_files:
91
57
  - LICENSE
92
- files:
58
+ files:
93
59
  - LICENSE
94
60
  - README
95
61
  - lib/onelogin/saml.rb
@@ -106,37 +72,27 @@ files:
106
72
  - ruby-saml-mod.gemspec
107
73
  homepage: http://github.com/bracken/ruby-saml
108
74
  licenses: []
109
-
110
75
  post_install_message:
111
76
  rdoc_options: []
112
-
113
- require_paths:
77
+ require_paths:
114
78
  - lib
115
- required_ruby_version: !ruby/object:Gem::Requirement
79
+ required_ruby_version: !ruby/object:Gem::Requirement
116
80
  none: false
117
- requirements:
118
- - - ">="
119
- - !ruby/object:Gem::Version
120
- hash: 3
121
- segments:
122
- - 0
123
- version: "0"
124
- required_rubygems_version: !ruby/object:Gem::Requirement
81
+ requirements:
82
+ - - ! '>='
83
+ - !ruby/object:Gem::Version
84
+ version: '0'
85
+ required_rubygems_version: !ruby/object:Gem::Requirement
125
86
  none: false
126
- requirements:
127
- - - ">="
128
- - !ruby/object:Gem::Version
129
- hash: 3
130
- segments:
131
- - 0
132
- version: "0"
87
+ requirements:
88
+ - - ! '>='
89
+ - !ruby/object:Gem::Version
90
+ version: '0'
133
91
  requirements: []
134
-
135
92
  rubyforge_project:
136
- rubygems_version: 1.8.15
93
+ rubygems_version: 1.8.24
137
94
  signing_key:
138
95
  specification_version: 3
139
96
  summary: Ruby library for SAML service providers
140
97
  test_files: []
141
-
142
98
  has_rdoc: