ruby-saml-mod 0.1.19 → 0.1.20

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