win32-sspi 0.0.1.rc1-x86-mingw32
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.
- checksums.yaml +7 -0
- data/License.txt +8 -0
- data/README.md +31 -0
- data/Rakefile +49 -0
- data/examples/sspi_negotiate_client.rb +42 -0
- data/examples/sspi_negotiate_server.rb +99 -0
- data/lib/win32-sspi.rb +2 -0
- data/lib/win32/sspi/api/client.rb +17 -0
- data/lib/win32/sspi/api/common.rb +133 -0
- data/lib/win32/sspi/api/server.rb +32 -0
- data/lib/win32/sspi/negotiate/client.rb +159 -0
- data/lib/win32/sspi/negotiate/server.rb +199 -0
- data/lib/win32/sspi/windows/constants.rb +36 -0
- data/lib/win32/sspi/windows/functions.rb +31 -0
- data/lib/win32/sspi/windows/misc.rb +24 -0
- data/lib/win32/sspi/windows/structs.rb +147 -0
- data/test/all_tests.rb +2 -0
- data/test/test_win32_sspi_negotiate_client.rb +417 -0
- data/test/test_win32_sspi_negotiate_server.rb +441 -0
- data/test/test_win32_sspi_structure_creates.rb +100 -0
- data/win32-sspi.gemspec +29 -0
- metadata +102 -0
    
        checksums.yaml
    ADDED
    
    | @@ -0,0 +1,7 @@ | |
| 1 | 
            +
            ---
         | 
| 2 | 
            +
            SHA1:
         | 
| 3 | 
            +
              metadata.gz: 94ed63fe1f2bf199120cb6e027ad643db0512179
         | 
| 4 | 
            +
              data.tar.gz: 64236f4dd2ae87f6fb9f56510cf9eef7a09281a7
         | 
| 5 | 
            +
            SHA512:
         | 
| 6 | 
            +
              metadata.gz: f47abf6eeec115b0e30fc2881b1493d106ed406b5795f49971531f1566c20e543317d2698c572d38ad51ef772437958ef596bda8341ceab2e3ba31139485d4c5
         | 
| 7 | 
            +
              data.tar.gz: f1c4cbbcc6bf624cec854aeecf8294f1baa3b5e82ae66969cf103bfd92404ee0cec86c5c0c26a313cc8a744b069a0cb9e3dd1ff68172e4f3909f05ad2a8fc68a
         | 
    
        data/License.txt
    ADDED
    
    | @@ -0,0 +1,8 @@ | |
| 1 | 
            +
            MIT License
         | 
| 2 | 
            +
            Copyright (c) 2015 Gary Sick
         | 
| 3 | 
            +
             | 
| 4 | 
            +
            Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
         | 
| 5 | 
            +
             | 
| 6 | 
            +
            The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
         | 
| 7 | 
            +
             | 
| 8 | 
            +
            THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
         | 
    
        data/README.md
    ADDED
    
    | @@ -0,0 +1,31 @@ | |
| 1 | 
            +
            This project is a fork of djbergers win32-sspi project which
         | 
| 2 | 
            +
            provided the beginnings of a an FFI implementation of SSPI
         | 
| 3 | 
            +
            on Windows. This project adds support for Kerberos through the
         | 
| 4 | 
            +
            SPNEGO/Negotiate protocol.
         | 
| 5 | 
            +
             | 
| 6 | 
            +
            The examples directory has working examples to illustrate
         | 
| 7 | 
            +
            usage. The sspi_negotiate_*.rb files are client/server
         | 
| 8 | 
            +
            examples of using Kerberos through SPNEGO/Negotiate protocol.
         | 
| 9 | 
            +
            In order to be used successfully across multiple systems
         | 
| 10 | 
            +
            in your domain you must define a Service Principal Name (SPN)
         | 
| 11 | 
            +
            associated with the user account under which the server is
         | 
| 12 | 
            +
            running unless the server is running as a Windows Service
         | 
| 13 | 
            +
            under the LocalSystem account. To establish a SPN use the
         | 
| 14 | 
            +
            setspn command as follows from a elevated privilege command
         | 
| 15 | 
            +
            window:
         | 
| 16 | 
            +
             | 
| 17 | 
            +
            ```
         | 
| 18 | 
            +
              setspn -S HTTP/fqdn-of-your-host USERDOMAIN\USERNAME
         | 
| 19 | 
            +
            ```
         | 
| 20 | 
            +
            The SPN you establish must be passed to the Client constructor
         | 
| 21 | 
            +
            with the spn option in order to succesfully connect and
         | 
| 22 | 
            +
            authenticate with the server.
         | 
| 23 | 
            +
             | 
| 24 | 
            +
            The negotiate client/server implementations are also capable
         | 
| 25 | 
            +
            of supporting the NTLM protocol. To do so specify the auth_type
         | 
| 26 | 
            +
            option as 'NTLM' on the construction of the client. The user name
         | 
| 27 | 
            +
            and user domain will be taken from the environment and will use
         | 
| 28 | 
            +
            the current logged in users security context to authenticate with
         | 
| 29 | 
            +
            the server. Otherwise passing the options username, domain, password
         | 
| 30 | 
            +
            to the client constructor will establish a different security
         | 
| 31 | 
            +
            context for the given username to authenticate against.
         | 
    
        data/Rakefile
    ADDED
    
    | @@ -0,0 +1,49 @@ | |
| 1 | 
            +
            require 'rake'
         | 
| 2 | 
            +
            require 'rake/clean'
         | 
| 3 | 
            +
            require 'rake/testtask'
         | 
| 4 | 
            +
            require 'rubygems'
         | 
| 5 | 
            +
            require 'rubygems/package'
         | 
| 6 | 
            +
             | 
| 7 | 
            +
            CLEAN.include('**/*.gem')
         | 
| 8 | 
            +
             | 
| 9 | 
            +
            namespace :gem do
         | 
| 10 | 
            +
              desc "Create the win32-sspi gem"
         | 
| 11 | 
            +
              task :create => [:clean] do
         | 
| 12 | 
            +
                spec = eval(IO.read('win32-sspi.gemspec'))
         | 
| 13 | 
            +
                Gem::Package.build(spec)
         | 
| 14 | 
            +
              end
         | 
| 15 | 
            +
             | 
| 16 | 
            +
              desc "Install the win32-sspi gem"
         | 
| 17 | 
            +
              task :install => [:create] do
         | 
| 18 | 
            +
                file = Dir["*.gem"].first
         | 
| 19 | 
            +
                sh "gem install #{file} -l --no-document"
         | 
| 20 | 
            +
              end
         | 
| 21 | 
            +
            end
         | 
| 22 | 
            +
             | 
| 23 | 
            +
            namespace :test do
         | 
| 24 | 
            +
              Rake::TestTask.new(:struct) do |t|
         | 
| 25 | 
            +
                t.test_files = FileList['test/test_win32_sspi_structure_creates.rb']
         | 
| 26 | 
            +
                t.warning = true
         | 
| 27 | 
            +
                t.verbose = true
         | 
| 28 | 
            +
              end
         | 
| 29 | 
            +
             | 
| 30 | 
            +
              Rake::TestTask.new(:client) do |t|
         | 
| 31 | 
            +
                t.test_files = FileList['test/test_win32_sspi_negotiate_client.rb']
         | 
| 32 | 
            +
                t.warning = true
         | 
| 33 | 
            +
                t.verbose = true
         | 
| 34 | 
            +
              end
         | 
| 35 | 
            +
             | 
| 36 | 
            +
              Rake::TestTask.new(:server) do |t|
         | 
| 37 | 
            +
                t.test_files = FileList['test/test_win32_sspi_negotiate_server.rb']
         | 
| 38 | 
            +
                t.warning = true
         | 
| 39 | 
            +
                t.verbose = true
         | 
| 40 | 
            +
              end
         | 
| 41 | 
            +
             | 
| 42 | 
            +
              Rake::TestTask.new(:all) do |t|
         | 
| 43 | 
            +
                t.test_files = FileList['test/test_win32*']
         | 
| 44 | 
            +
                t.warning = true
         | 
| 45 | 
            +
                t.verbose = true
         | 
| 46 | 
            +
              end
         | 
| 47 | 
            +
            end
         | 
| 48 | 
            +
             | 
| 49 | 
            +
            task :default => 'test:all'
         | 
| @@ -0,0 +1,42 @@ | |
| 1 | 
            +
            require 'pp'
         | 
| 2 | 
            +
            require 'net/http'
         | 
| 3 | 
            +
            unless ENV['WIN32_SSPI_TEST']
         | 
| 4 | 
            +
              require 'win32-sspi'
         | 
| 5 | 
            +
              require 'negotiate/client'
         | 
| 6 | 
            +
            else
         | 
| 7 | 
            +
              require 'win32/sspi/negotiate/client'
         | 
| 8 | 
            +
              puts "!!!! running with test environment !!!"
         | 
| 9 | 
            +
            end
         | 
| 10 | 
            +
             | 
| 11 | 
            +
            class RubySSPIClient
         | 
| 12 | 
            +
              def self.run(url,auth_type)
         | 
| 13 | 
            +
                uri = URI.parse(url)
         | 
| 14 | 
            +
                client = (Win32::SSPI::API::Common::AUTH_TYPE_NEGOTIATE == auth_type) ? 
         | 
| 15 | 
            +
                  Win32::SSPI::Negotiate::Client.new(spn:"HTTP/#{uri.host}") : 
         | 
| 16 | 
            +
                  Win32::SSPI::Negotiate::Client.new(auth_type:auth_type)
         | 
| 17 | 
            +
                
         | 
| 18 | 
            +
                Net::HTTP.start(uri.host, uri.port) do |http|
         | 
| 19 | 
            +
                  resp = nil
         | 
| 20 | 
            +
                  client.http_authenticate do |header|
         | 
| 21 | 
            +
                    req = Net::HTTP::Get.new(uri.path)
         | 
| 22 | 
            +
                    req['Authorization'] = header
         | 
| 23 | 
            +
                    resp = http.request(req)
         | 
| 24 | 
            +
                    resp['www-authenticate']
         | 
| 25 | 
            +
                  end
         | 
| 26 | 
            +
                  
         | 
| 27 | 
            +
                  puts resp.body if resp.body
         | 
| 28 | 
            +
                end
         | 
| 29 | 
            +
              end
         | 
| 30 | 
            +
            end
         | 
| 31 | 
            +
             | 
| 32 | 
            +
            if __FILE__ == $0
         | 
| 33 | 
            +
              if ARGV.length < 1
         | 
| 34 | 
            +
                puts "usage: ruby sspi_negotiate_client.rb url [auth_type (Negotiate|NTLM default=Negotiate)]"
         | 
| 35 | 
            +
                puts "where: url = http://hostname:port/path"
         | 
| 36 | 
            +
                exit(0)
         | 
| 37 | 
            +
              end
         | 
| 38 | 
            +
             | 
| 39 | 
            +
              url = ARGV[0]
         | 
| 40 | 
            +
              auth_type = (2 == ARGV.length) ? ARGV[1] : Win32::SSPI::API::Common::AUTH_TYPE_NEGOTIATE
         | 
| 41 | 
            +
              RubySSPIClient.run(url,auth_type)
         | 
| 42 | 
            +
            end
         | 
| @@ -0,0 +1,99 @@ | |
| 1 | 
            +
            # Attempting to setup an example authenticating server
         | 
| 2 | 
            +
            require 'webrick'
         | 
| 3 | 
            +
            unless ENV['WIN32_SSPI_TEST']
         | 
| 4 | 
            +
              require 'win32-sspi'
         | 
| 5 | 
            +
              require 'negotiate/server'
         | 
| 6 | 
            +
            else
         | 
| 7 | 
            +
              require 'win32/sspi/negotiate/server'
         | 
| 8 | 
            +
              puts "!!!! running with test environment !!!"
         | 
| 9 | 
            +
            end
         | 
| 10 | 
            +
             | 
| 11 | 
            +
            # A way to store state across multiple requests
         | 
| 12 | 
            +
            class StateStore
         | 
| 13 | 
            +
              def self.state
         | 
| 14 | 
            +
                @state ||= Hash.new
         | 
| 15 | 
            +
              end
         | 
| 16 | 
            +
              
         | 
| 17 | 
            +
              def self.store_state(key,value)
         | 
| 18 | 
            +
                state[key] = value
         | 
| 19 | 
            +
              end
         | 
| 20 | 
            +
              
         | 
| 21 | 
            +
              def self.retrieve_state(key)
         | 
| 22 | 
            +
                state[key]
         | 
| 23 | 
            +
              end
         | 
| 24 | 
            +
              
         | 
| 25 | 
            +
              def self.clear_state
         | 
| 26 | 
            +
                state.clear
         | 
| 27 | 
            +
              end
         | 
| 28 | 
            +
              
         | 
| 29 | 
            +
              def self.retrieve_server(auth_type=Win32::SSPI::API::Common::AUTH_TYPE_NEGOTIATE)
         | 
| 30 | 
            +
                state[:server] ||= Win32::SSPI::Negotiate::Server.new(auth_type: auth_type)
         | 
| 31 | 
            +
                state[:server]
         | 
| 32 | 
            +
              end
         | 
| 33 | 
            +
            end
         | 
| 34 | 
            +
             | 
| 35 | 
            +
             | 
| 36 | 
            +
            class RubySSPIServlet < WEBrick::HTTPServlet::AbstractServlet
         | 
| 37 | 
            +
              def initialize(server,auth_type)
         | 
| 38 | 
            +
                super server
         | 
| 39 | 
            +
                @auth_type = auth_type
         | 
| 40 | 
            +
              end
         | 
| 41 | 
            +
              
         | 
| 42 | 
            +
              def do_GET(req,resp)
         | 
| 43 | 
            +
                if req['Authorization'].nil? || req['Authorization'].empty?
         | 
| 44 | 
            +
                  resp['www-authenticate'] = @auth_type
         | 
| 45 | 
            +
                  resp.status = 401
         | 
| 46 | 
            +
                  return
         | 
| 47 | 
            +
                end
         | 
| 48 | 
            +
             | 
| 49 | 
            +
                authenticated = false
         | 
| 50 | 
            +
             | 
| 51 | 
            +
                begin
         | 
| 52 | 
            +
                  sspi_server = StateStore.retrieve_server(@auth_type)
         | 
| 53 | 
            +
                  authenticated = sspi_server.http_authenticate(req['Authorization']) do |header,complete|
         | 
| 54 | 
            +
                    resp['www-authenticate'] = header if header
         | 
| 55 | 
            +
                    if complete
         | 
| 56 | 
            +
                      resp.status = 200
         | 
| 57 | 
            +
                      resp['Remote-User'] = sspi_server.username
         | 
| 58 | 
            +
                      resp['Remote-User-Domain'] = sspi_server.domain
         | 
| 59 | 
            +
                      resp['Content-Type'] = "text/plain"
         | 
| 60 | 
            +
                      resp.body = "#{Time.now}: Hello #{sspi_server.username} at #{sspi_server.domain}"
         | 
| 61 | 
            +
                    else
         | 
| 62 | 
            +
                      resp.status = 401
         | 
| 63 | 
            +
                    end
         | 
| 64 | 
            +
                  end
         | 
| 65 | 
            +
                rescue SecurityStatusError => e
         | 
| 66 | 
            +
                  sspi_server.free_handles
         | 
| 67 | 
            +
                  StateStore.clear_state
         | 
| 68 | 
            +
                  
         | 
| 69 | 
            +
                  resp.status = 401
         | 
| 70 | 
            +
                  resp['www-authenticate'] = @auth_type
         | 
| 71 | 
            +
                  resp['Content-Type'] = "text/plain"
         | 
| 72 | 
            +
                  resp.body = e.message
         | 
| 73 | 
            +
                  puts "*** server encountered the following error ***\n #{e.message}"
         | 
| 74 | 
            +
                  return
         | 
| 75 | 
            +
                end
         | 
| 76 | 
            +
                
         | 
| 77 | 
            +
                StateStore.clear_state if authenticated
         | 
| 78 | 
            +
              end
         | 
| 79 | 
            +
             | 
| 80 | 
            +
              def self.run(url,auth_type)
         | 
| 81 | 
            +
                uri = URI.parse(url)
         | 
| 82 | 
            +
                s = WEBrick::HTTPServer.new( :Binding=>uri.host, :Port=>uri.port)
         | 
| 83 | 
            +
                s.mount(uri.path, RubySSPIServlet, auth_type)
         | 
| 84 | 
            +
                trap("INT") { s.shutdown }
         | 
| 85 | 
            +
                s.start
         | 
| 86 | 
            +
              end
         | 
| 87 | 
            +
            end
         | 
| 88 | 
            +
             | 
| 89 | 
            +
            if $0 == __FILE__
         | 
| 90 | 
            +
              if ARGV.length < 1
         | 
| 91 | 
            +
                puts "usage: ruby sspi_negotiate_server.rb url [auth_type (Negotiate|NTLM default=Negotiate)]"
         | 
| 92 | 
            +
                puts "where: url = http://hostname:port/path"
         | 
| 93 | 
            +
                exit(0)
         | 
| 94 | 
            +
              end
         | 
| 95 | 
            +
             | 
| 96 | 
            +
              url = ARGV[0]
         | 
| 97 | 
            +
              auth_type = (2 == ARGV.length) ? ARGV[1] : Win32::SSPI::API::Common::AUTH_TYPE_NEGOTIATE
         | 
| 98 | 
            +
              RubySSPIServlet.run(url,auth_type)
         | 
| 99 | 
            +
            end
         | 
    
        data/lib/win32-sspi.rb
    ADDED
    
    
| @@ -0,0 +1,17 @@ | |
| 1 | 
            +
            require_relative 'common'
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            module Win32
         | 
| 4 | 
            +
              module SSPI
         | 
| 5 | 
            +
                module API
         | 
| 6 | 
            +
                  module Client
         | 
| 7 | 
            +
                    include Common
         | 
| 8 | 
            +
             | 
| 9 | 
            +
                    def initialize_security_context(ph_credential,ph_context,psz_targetname,f_contextreq,reserved1,targetdatarep,p_input,reserved2,ph_newcontext,p_output,pf_contextattr,pts_expiry)
         | 
| 10 | 
            +
                      status = InitializeSecurityContext(ph_credential,ph_context,psz_targetname,f_contextreq,reserved1,targetdatarep,p_input,reserved2,ph_newcontext,p_output,pf_contextattr,pts_expiry)
         | 
| 11 | 
            +
                      return status
         | 
| 12 | 
            +
                    end
         | 
| 13 | 
            +
             | 
| 14 | 
            +
                  end
         | 
| 15 | 
            +
                end
         | 
| 16 | 
            +
              end
         | 
| 17 | 
            +
            end
         | 
| @@ -0,0 +1,133 @@ | |
| 1 | 
            +
            require 'base64'
         | 
| 2 | 
            +
            require_relative '../windows/constants'
         | 
| 3 | 
            +
            require_relative '../windows/structs'
         | 
| 4 | 
            +
            require_relative '../windows/functions'
         | 
| 5 | 
            +
             | 
| 6 | 
            +
            module Win32
         | 
| 7 | 
            +
              module SSPI
         | 
| 8 | 
            +
                module API
         | 
| 9 | 
            +
                  module Common
         | 
| 10 | 
            +
                    include Windows::Constants
         | 
| 11 | 
            +
                    include Windows::Structs
         | 
| 12 | 
            +
                    include Windows::Functions
         | 
| 13 | 
            +
                    
         | 
| 14 | 
            +
                    AUTH_TYPE_NEGOTIATE = 'Negotiate'
         | 
| 15 | 
            +
                    AUTH_TYPE_NTLM = 'NTLM'
         | 
| 16 | 
            +
                    
         | 
| 17 | 
            +
                    def create_sec_winnt_auth_identity(username,domain,password)
         | 
| 18 | 
            +
                      auth_struct = SEC_WINNT_AUTH_IDENTITY.new
         | 
| 19 | 
            +
                      auth_struct[:Flags] = SEC_WINNT_AUTH_IDENTITY_ANSI
         | 
| 20 | 
            +
             | 
| 21 | 
            +
                      if username
         | 
| 22 | 
            +
                        auth_struct[:User] = FFI::MemoryPointer.from_string(username.dup)
         | 
| 23 | 
            +
                        auth_struct[:UserLength] = username.size
         | 
| 24 | 
            +
                      end
         | 
| 25 | 
            +
             | 
| 26 | 
            +
                      if domain
         | 
| 27 | 
            +
                        auth_struct[:Domain] = FFI::MemoryPointer.from_string(domain.dup)
         | 
| 28 | 
            +
                        auth_struct[:DomainLength] = domain.size
         | 
| 29 | 
            +
                      end
         | 
| 30 | 
            +
             | 
| 31 | 
            +
                      if password
         | 
| 32 | 
            +
                        auth_struct[:Password] = FFI::MemoryPointer.from_string(password.dup)
         | 
| 33 | 
            +
                        auth_struct[:PasswordLength] = password.size
         | 
| 34 | 
            +
                      end
         | 
| 35 | 
            +
                      
         | 
| 36 | 
            +
                      auth_struct
         | 
| 37 | 
            +
                    end
         | 
| 38 | 
            +
             | 
| 39 | 
            +
                    def create_credhandle(lower=nil,upper=nil)
         | 
| 40 | 
            +
                      result = CredHandle.new
         | 
| 41 | 
            +
                      
         | 
| 42 | 
            +
                      if lower && upper
         | 
| 43 | 
            +
                        result.marshal_load([lower,upper])
         | 
| 44 | 
            +
                      end
         | 
| 45 | 
            +
                      
         | 
| 46 | 
            +
                      result
         | 
| 47 | 
            +
                    end
         | 
| 48 | 
            +
                    
         | 
| 49 | 
            +
                    def create_ctxhandle(lower=nil,upper=nil)
         | 
| 50 | 
            +
                      result = CtxtHandle.new
         | 
| 51 | 
            +
                      
         | 
| 52 | 
            +
                      if lower && upper
         | 
| 53 | 
            +
                        result.marshal_load([lower,upper])
         | 
| 54 | 
            +
                      end
         | 
| 55 | 
            +
                      
         | 
| 56 | 
            +
                      result
         | 
| 57 | 
            +
                    end
         | 
| 58 | 
            +
                    
         | 
| 59 | 
            +
                    def create_timestamp(low=nil,high=nil)
         | 
| 60 | 
            +
                      ts = TimeStamp.new
         | 
| 61 | 
            +
                      if low && high
         | 
| 62 | 
            +
                        ts[:dwLowDateTime] = low
         | 
| 63 | 
            +
                        ts[:dwHighDateTime] = high
         | 
| 64 | 
            +
                      end
         | 
| 65 | 
            +
                      ts
         | 
| 66 | 
            +
                    end
         | 
| 67 | 
            +
                    
         | 
| 68 | 
            +
                    def create_secbuffer(content=nil)
         | 
| 69 | 
            +
                      SecBuffer.new.init(content)
         | 
| 70 | 
            +
                    end
         | 
| 71 | 
            +
                    
         | 
| 72 | 
            +
                    def create_secbufferdesc(sec_buffer=nil)
         | 
| 73 | 
            +
                      SecBufferDesc.new.init(sec_buffer)
         | 
| 74 | 
            +
                    end
         | 
| 75 | 
            +
                    
         | 
| 76 | 
            +
                    def create_secpkg_context_names(name=nil)
         | 
| 77 | 
            +
                      result = SecPkgContext_Names.new
         | 
| 78 | 
            +
                      if name
         | 
| 79 | 
            +
                        result[:sUserName] = FFI::MemoryPointer.from_string(name.dup)
         | 
| 80 | 
            +
                      end
         | 
| 81 | 
            +
                      result
         | 
| 82 | 
            +
                    end
         | 
| 83 | 
            +
                    
         | 
| 84 | 
            +
                    def construct_http_header(auth_type, token)
         | 
| 85 | 
            +
                      b64_token = token.nil? ? nil : Base64.strict_encode64(token)
         | 
| 86 | 
            +
                      b64_token.nil? ? "#{auth_type}" : "#{auth_type} #{b64_token}"
         | 
| 87 | 
            +
                    end
         | 
| 88 | 
            +
                    
         | 
| 89 | 
            +
                    def de_construct_http_header(header)
         | 
| 90 | 
            +
                      auth_type, b64_token = header.split(' ')
         | 
| 91 | 
            +
                      token = b64_token.nil? ? nil : Base64.strict_decode64(b64_token)
         | 
| 92 | 
            +
                      [auth_type, token]
         | 
| 93 | 
            +
                    end
         | 
| 94 | 
            +
                    
         | 
| 95 | 
            +
                    def acquire_credentials_handle(psz_principal,psz_package,f_credentialuse,pv_logonid,p_authdata,p_getkeyfn,pv_getkeyarg,ph_credential,pts_expiry)
         | 
| 96 | 
            +
                      status = AcquireCredentialsHandle(psz_principal,psz_package,f_credentialuse,pv_logonid,p_authdata,p_getkeyfn,pv_getkeyarg,ph_credential,pts_expiry)
         | 
| 97 | 
            +
                      return status
         | 
| 98 | 
            +
                    end
         | 
| 99 | 
            +
                    
         | 
| 100 | 
            +
                    def query_context_attributes(ph_context,ul_attribute,p_buffer)
         | 
| 101 | 
            +
                      status = QueryContextAttributes(ph_context, ul_attribute, p_buffer)
         | 
| 102 | 
            +
                      return status
         | 
| 103 | 
            +
                    end
         | 
| 104 | 
            +
                    
         | 
| 105 | 
            +
                    def delete_security_context(ph_context)
         | 
| 106 | 
            +
                      status = DeleteSecurityContext(ph_context)
         | 
| 107 | 
            +
                      return status
         | 
| 108 | 
            +
                    end
         | 
| 109 | 
            +
                    
         | 
| 110 | 
            +
                    def free_credentials_handle(ph_credential)
         | 
| 111 | 
            +
                      status = FreeCredentialsHandle(ph_credential)
         | 
| 112 | 
            +
                      return status
         | 
| 113 | 
            +
                    end
         | 
| 114 | 
            +
                    
         | 
| 115 | 
            +
                    def free_context_and_credentials(context,credentials)
         | 
| 116 | 
            +
                      result = {name:'', status:SEC_E_OK, dsc_status:SEC_E_OK, fch_status:SEC_E_OK}
         | 
| 117 | 
            +
                      status = delete_security_context(context)
         | 
| 118 | 
            +
                      if SEC_E_OK != status
         | 
| 119 | 
            +
                        result[:name], result[:status], result[:dsc_status] = ["DeleteSecurityContext", status, status]
         | 
| 120 | 
            +
                      end
         | 
| 121 | 
            +
                      
         | 
| 122 | 
            +
                      status = free_credentials_handle(credentials)
         | 
| 123 | 
            +
                      if SEC_E_OK != status
         | 
| 124 | 
            +
                        result[:name], result[:status], result[:fch_status] = ["FreeCredentialsHandle", status, status]
         | 
| 125 | 
            +
                      end
         | 
| 126 | 
            +
             | 
| 127 | 
            +
                      return result
         | 
| 128 | 
            +
                    end
         | 
| 129 | 
            +
                    
         | 
| 130 | 
            +
                  end
         | 
| 131 | 
            +
                end
         | 
| 132 | 
            +
              end
         | 
| 133 | 
            +
            end
         | 
| @@ -0,0 +1,32 @@ | |
| 1 | 
            +
            require_relative 'common'
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            module Win32
         | 
| 4 | 
            +
              module SSPI
         | 
| 5 | 
            +
                module API
         | 
| 6 | 
            +
                  module Server
         | 
| 7 | 
            +
                    include Common
         | 
| 8 | 
            +
                    
         | 
| 9 | 
            +
                    def accept_security_context(ph_credential,ph_context,p_input,f_contextreq,targetdatarep,ph_newcontext,p_output,pf_contextattr,pts_timestamp)
         | 
| 10 | 
            +
                      status = AcceptSecurityContext(ph_credential,ph_context,p_input,f_contextreq,targetdatarep,ph_newcontext,p_output,pf_contextattr,pts_timestamp)
         | 
| 11 | 
            +
                      return status
         | 
| 12 | 
            +
                    end
         | 
| 13 | 
            +
                    
         | 
| 14 | 
            +
                    def complete_auth_token(ph_context,p_token)
         | 
| 15 | 
            +
                      status = CompleteAuthToken(ph_context,p_token)
         | 
| 16 | 
            +
                      return status
         | 
| 17 | 
            +
                    end
         | 
| 18 | 
            +
                    
         | 
| 19 | 
            +
                    def enumerate_security_packages(pc_packages,pp_packageinfo)
         | 
| 20 | 
            +
                      status = EnumerateSecurityPackages(pc_packages,pp_packageinfo)
         | 
| 21 | 
            +
                      return status
         | 
| 22 | 
            +
                    end
         | 
| 23 | 
            +
                    
         | 
| 24 | 
            +
                    def free_context_buffer(pv_contextbuffer)
         | 
| 25 | 
            +
                      status = FreeContextBuffer(pv_contextbuffer)
         | 
| 26 | 
            +
                      return status
         | 
| 27 | 
            +
                    end
         | 
| 28 | 
            +
                  end
         | 
| 29 | 
            +
                end
         | 
| 30 | 
            +
              end
         | 
| 31 | 
            +
            end
         | 
| 32 | 
            +
             | 
| @@ -0,0 +1,159 @@ | |
| 1 | 
            +
            require_relative '../windows/constants'
         | 
| 2 | 
            +
            require_relative '../windows/misc'
         | 
| 3 | 
            +
            require_relative '../api/client'
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            module Win32
         | 
| 6 | 
            +
              module SSPI
         | 
| 7 | 
            +
                module Negotiate
         | 
| 8 | 
            +
                  class Client
         | 
| 9 | 
            +
                    include Windows::Constants
         | 
| 10 | 
            +
                    include API::Client
         | 
| 11 | 
            +
                  
         | 
| 12 | 
            +
                    attr_reader :spn
         | 
| 13 | 
            +
                    attr_reader :auth_type
         | 
| 14 | 
            +
                    attr_reader :token
         | 
| 15 | 
            +
                  
         | 
| 16 | 
            +
                    def initialize(options={})
         | 
| 17 | 
            +
                      @spn = options[:spn]
         | 
| 18 | 
            +
                      @auth_type = options[:auth_type] || AUTH_TYPE_NEGOTIATE
         | 
| 19 | 
            +
                      @token = nil
         | 
| 20 | 
            +
                      @credentials_handle = nil
         | 
| 21 | 
            +
                      @context_handle = nil
         | 
| 22 | 
            +
                      @username = options[:username]
         | 
| 23 | 
            +
                      @domain = options[:domain]
         | 
| 24 | 
            +
                      @password = options[:password]
         | 
| 25 | 
            +
                    end
         | 
| 26 | 
            +
                    
         | 
| 27 | 
            +
                    def http_authenticate(&block)
         | 
| 28 | 
            +
                      perform_authenticate(block)
         | 
| 29 | 
            +
                    end
         | 
| 30 | 
            +
                    
         | 
| 31 | 
            +
                    def authenticate(&block)
         | 
| 32 | 
            +
                      perform_authenticate(block,false)
         | 
| 33 | 
            +
                    end
         | 
| 34 | 
            +
                    
         | 
| 35 | 
            +
                    def perform_authenticate(block,with_http_header=true)
         | 
| 36 | 
            +
                      status = acquire_handle
         | 
| 37 | 
            +
                      if SEC_E_OK == status
         | 
| 38 | 
            +
                        begin
         | 
| 39 | 
            +
                          status = initialize_context(self.token)
         | 
| 40 | 
            +
                          if SEC_I_CONTINUE_NEEDED == status
         | 
| 41 | 
            +
                            token_from_block_result(block.call(block_arg_from_token(with_http_header)),with_http_header)
         | 
| 42 | 
            +
                          end
         | 
| 43 | 
            +
                        end while( SEC_I_CONTINUE_NEEDED == status )
         | 
| 44 | 
            +
                        
         | 
| 45 | 
            +
                        # if using NTLM protocol we need to complete the final leg of the authentication
         | 
| 46 | 
            +
                        if AUTH_TYPE_NTLM == self.auth_type && SEC_E_OK == status
         | 
| 47 | 
            +
                          block.call(block_arg_from_token(with_http_header))
         | 
| 48 | 
            +
                        end
         | 
| 49 | 
            +
                        
         | 
| 50 | 
            +
                        if SEC_E_OK == status
         | 
| 51 | 
            +
                          free_handles
         | 
| 52 | 
            +
                        end
         | 
| 53 | 
            +
                      end
         | 
| 54 | 
            +
                    end
         | 
| 55 | 
            +
                    
         | 
| 56 | 
            +
                    def block_arg_from_token(with_http_header)
         | 
| 57 | 
            +
                      with_http_header ? construct_http_header(self.auth_type,self.token) : self.token
         | 
| 58 | 
            +
                    end
         | 
| 59 | 
            +
                    
         | 
| 60 | 
            +
                    def token_from_block_result(block_result,with_http_header)
         | 
| 61 | 
            +
                      if block_result
         | 
| 62 | 
            +
                        if with_http_header
         | 
| 63 | 
            +
                          @auth_type, @token = de_construct_http_header(block_result)
         | 
| 64 | 
            +
                        else
         | 
| 65 | 
            +
                          @token = block_result
         | 
| 66 | 
            +
                        end
         | 
| 67 | 
            +
                      end
         | 
| 68 | 
            +
                    end
         | 
| 69 | 
            +
                    
         | 
| 70 | 
            +
                    def acquire_handle
         | 
| 71 | 
            +
                      return SEC_E_OK if @credentials_handle
         | 
| 72 | 
            +
                      
         | 
| 73 | 
            +
                      auth_data = nil
         | 
| 74 | 
            +
                      if AUTH_TYPE_NTLM == @auth_type
         | 
| 75 | 
            +
                        @username ||= ENV['USERNAME']
         | 
| 76 | 
            +
                        @domain ||= ENV['USERDOMAIN']
         | 
| 77 | 
            +
                        auth_data = create_sec_winnt_auth_identity(@username,@domain,@password)
         | 
| 78 | 
            +
                      end
         | 
| 79 | 
            +
                      
         | 
| 80 | 
            +
                      @credentials_handle = create_credhandle
         | 
| 81 | 
            +
                      expiry = create_timestamp
         | 
| 82 | 
            +
                    
         | 
| 83 | 
            +
                      status = acquire_credentials_handle(
         | 
| 84 | 
            +
                        @spn,
         | 
| 85 | 
            +
                        @auth_type,
         | 
| 86 | 
            +
                        SECPKG_CRED_OUTBOUND,
         | 
| 87 | 
            +
                        nil,
         | 
| 88 | 
            +
                        auth_data,
         | 
| 89 | 
            +
                        nil,
         | 
| 90 | 
            +
                        nil,
         | 
| 91 | 
            +
                        @credentials_handle,
         | 
| 92 | 
            +
                        expiry
         | 
| 93 | 
            +
                      )
         | 
| 94 | 
            +
             | 
| 95 | 
            +
                      if SEC_E_OK != status
         | 
| 96 | 
            +
                        @credentials_handle = nil
         | 
| 97 | 
            +
                        raise SecurityStatusError.new('AcquireCredentialsHandle', status, FFI.errno)
         | 
| 98 | 
            +
                      end
         | 
| 99 | 
            +
                      
         | 
| 100 | 
            +
                      status
         | 
| 101 | 
            +
                    end
         | 
| 102 | 
            +
                  
         | 
| 103 | 
            +
                    def initialize_context(token=nil)
         | 
| 104 | 
            +
                      return SEC_E_OK if token.nil? && @context_handle
         | 
| 105 | 
            +
                      
         | 
| 106 | 
            +
                      ctx = @context_handle
         | 
| 107 | 
            +
                      @context_handle ||= create_ctxhandle
         | 
| 108 | 
            +
                      context_attributes = FFI::MemoryPointer.new(:ulong)
         | 
| 109 | 
            +
             | 
| 110 | 
            +
                      rflags = ISC_REQ_CONFIDENTIALITY | ISC_REQ_REPLAY_DETECT | ISC_REQ_CONNECTION
         | 
| 111 | 
            +
                      expiry = create_timestamp
         | 
| 112 | 
            +
             | 
| 113 | 
            +
                      if token
         | 
| 114 | 
            +
                        input_buffer   = create_secbuffer(token)
         | 
| 115 | 
            +
                        input_buffer_desc  = create_secbufferdesc(input_buffer)
         | 
| 116 | 
            +
                      end
         | 
| 117 | 
            +
             | 
| 118 | 
            +
                      output_buffer = create_secbuffer
         | 
| 119 | 
            +
                      output_buffer_desc  = create_secbufferdesc(output_buffer)
         | 
| 120 | 
            +
                    
         | 
| 121 | 
            +
                      status = initialize_security_context(
         | 
| 122 | 
            +
                        @credentials_handle,
         | 
| 123 | 
            +
                        ctx,
         | 
| 124 | 
            +
                        @spn,
         | 
| 125 | 
            +
                        rflags,
         | 
| 126 | 
            +
                        0,
         | 
| 127 | 
            +
                        SECURITY_NETWORK_DREP,
         | 
| 128 | 
            +
                        (token ? input_buffer_desc : nil),
         | 
| 129 | 
            +
                        0,
         | 
| 130 | 
            +
                        @context_handle,
         | 
| 131 | 
            +
                        output_buffer_desc,
         | 
| 132 | 
            +
                        context_attributes,
         | 
| 133 | 
            +
                        expiry
         | 
| 134 | 
            +
                      )
         | 
| 135 | 
            +
             | 
| 136 | 
            +
                      a_success = [SEC_E_OK, SEC_I_CONTINUE_NEEDED]
         | 
| 137 | 
            +
                      if a_success.include?(status)
         | 
| 138 | 
            +
                        @token = output_buffer.to_ruby_s
         | 
| 139 | 
            +
                      else
         | 
| 140 | 
            +
                        raise SecurityStatusError.new('InitializeSecurityContext', status, FFI.errno)
         | 
| 141 | 
            +
                      end
         | 
| 142 | 
            +
             | 
| 143 | 
            +
                      status
         | 
| 144 | 
            +
                    end
         | 
| 145 | 
            +
                    
         | 
| 146 | 
            +
                    def free_handles
         | 
| 147 | 
            +
                      result = free_context_and_credentials(@context_handle, @credentials_handle)
         | 
| 148 | 
            +
                      @context_handle, @credentials_handle = [nil,nil]
         | 
| 149 | 
            +
                      
         | 
| 150 | 
            +
                      if SEC_E_OK != result[:status]
         | 
| 151 | 
            +
                        raise SecurityStatusError.new(result[:name], result[:status], FFI.errno)
         | 
| 152 | 
            +
                      end
         | 
| 153 | 
            +
                      
         | 
| 154 | 
            +
                      result[:status]
         | 
| 155 | 
            +
                    end
         | 
| 156 | 
            +
                  end
         | 
| 157 | 
            +
                end
         | 
| 158 | 
            +
              end
         | 
| 159 | 
            +
            end
         |