=begin Copyright © 2010 Dan Wanek Licensed under the MIT License: http://www.opensource.org/licenses/mit-license.php =end require 'gssapi/lib_gssapi_loader' module GSSAPI module LibGSSAPI # Libc functions # void *malloc(size_t size); attach_function :malloc, [:size_t], :pointer # void *memcpy(void *dest, const void *src, size_t n); attach_function :memcpy, [:pointer, :pointer, :size_t], :pointer # This is a generic Managed Struct subclass that hides the [] methods. # Classes that implement this class should provide accessor methods to get to the attributes. class GssMStruct < FFI::ManagedStruct private def [](key) super(key) end def []=(key,val) super(key,val) end end # This is a generic Unmanaged Struct subclass that hides the [] methods. # Classes that implement this class should provide accessor methods to get to the attributes. class GssUMStruct < FFI::Struct private def [](key) super(key) end def []=(key,val) super(key,val) end end # This module provides a layout for both the managed and unmanaged GssBufferDesc structs. module GssBufferDescLayout def self.included(base) base.class_eval do layout :length, :OM_uint32, :value, :pointer # pointer of :void def length self[:length] end def value if(self[:length] == 0) nil else self[:value].read_string(self[:length]) end end end end end # This class implements the gss_buffer_desc type. Use #pointer to emulate gss_buffer_t # If you are setting the value of the buffer and it is not being set from the function # this is the type of buffer you should use. If the buffer is being allocated and set # inside the function you should use a ManagedGssBufferDesc instead so gss_release_buffer # is called for it. It states in the manpage for each gss function whether or not # gss_release_buffer needs to be called or not. # @example # buff = UnManagedGssBufferDesc.new # buff.value = "This is a test" class UnManagedGssBufferDesc < GssUMStruct include GssBufferDescLayout def initialize(ptr = nil) if(ptr.nil?) super(FFI::Pointer.new(FFI::MemoryPointer.new(self.size))) else super(ptr) end end # Set the value of the string for the "value" parameter. This method also # appropriately sets the length parameter. def value=(val) if(val.nil?) self[:length] = 0 self[:value] = val elsif(val.is_a?(String)) buff = FFI::MemoryPointer.from_string(val) self[:length] = val.length self[:value] = buff elsif(val.is_a?(Fixnum)) buff = FFI::MemoryPointer.new :OM_uint32 buff.write_int val self[:length] = FFI::type_size :OM_uint32 self[:value] = buff else raise StandardError, "Can't handle type #{val.class.name}" end end end # This class implements the gss_buffer_desc type. Use #pointer to emulate gss_buffer_t # Only functions that need to call gss_release_buffer should use this type. It states # in the manpage for each function whether or not it should be called. If it does not # you should be using UnManagedGssBufferDesc instead. class ManagedGssBufferDesc < GssMStruct include GssBufferDescLayout def initialize(ptr = nil) if(ptr.nil?) super(FFI::Pointer.new(FFI::MemoryPointer.new(self.size))) else super(ptr) end end def self.release(ptr) puts "Releasing ManagedGssBufferDesc at #{ptr.address.to_s(16)}" if $DEBUG min_stat = FFI::MemoryPointer.new :OM_uint32 maj_stat = LibGSSAPI.gss_release_buffer(min_stat, ptr) end end # @example # iov_buff = GssIOVBufferDesc.new # str = FFI::MemoryPointer.from_string("This is the string") # iov_buff[:type] = 1 # iov_buff[:buffer][:length] = str.size # iov_buff[:buffer][:value] = str class GssIOVBufferDesc < FFI::Struct layout :type, :OM_uint32, :buffer, UnManagedGssBufferDesc end class GssChannelBindingsStruct < FFI::Struct layout :initiator_addrtype, :OM_uint32, :initiator_address, UnManagedGssBufferDesc, :acceptor_addrtype, :OM_uint32, :acceptor_address, UnManagedGssBufferDesc, :application_data, UnManagedGssBufferDesc no_chn_bind = FFI::MemoryPointer.new :pointer # no_chn_bind.write_int 0 end # This s a generic AutoPointer. Gss pointers that implement this class should also implement a # class method called release_ptr that releases the structure pointed to by this pointer. class GssPointer < FFI::AutoPointer def address_of ptr_p = FFI::MemoryPointer.new :pointer ptr_p.write_pointer(self) end def self.release(ptr) if( ptr.address == 0 ) puts "NULL POINTER: Not freeing" if $DEBUG return else puts "Releasing #{self.name} at #{ptr.address.to_s(16)}" if $DEBUG self.release_ptr(ptr) end end end # A wrapper around gss_name_t so that it garbage collects class GssNameT < GssPointer def self.release_ptr(name_ptr) puts "Releasing gss_name_t at #{name_ptr.address.to_s(16)}" if $DEBUG min_stat = FFI::MemoryPointer.new :OM_uint32 maj_stat = LibGSSAPI.gss_release_name(min_stat, name_ptr) end end class GssCtxIdT < GssPointer def self.release_ptr(context_ptr) min_stat = FFI::MemoryPointer.new :OM_uint32 maj_stat = LibGSSAPI.gss_delete_sec_context(min_stat, context_ptr, LibGSSAPI::GSS_C_NO_BUFFER) end def self.gss_c_no_context self.new(GSSAPI::LibGSSAPI::GSS_C_NO_CONTEXT) end end # gss_cred_id_t class GssCredIdT < GssPointer def self.release_ptr(cred_ptr) puts "Releasing gss_cred_id_t at #{cred_ptr.address.to_s(16)}" if $DEBUG min_stat = FFI::MemoryPointer.new :OM_uint32 maj_stat = LibGSSAPI.gss_release_cred(min_stat, cred_ptr) end end # OM_uint32 gss_release_buffer(OM_uint32 * minor_status, gss_buffer_t buffer); # Function definitions # -------------------- # OM_uint32 gss_import_name(OM_uint32 * minor_status, const gss_buffer_t input_name_buffer, const gss_OID input_name_type, gss_name_t * output_name); # @example: # host_str = 'host@example.com' # buff_str = GSSAPI::LibGSSAPI::UnManagedGssBufferDesc.new # buff_str[:length] = host_str.length # buff_str[:value] = FFI::MemoryPointer.from_string(host_str) # name = FFI::MemoryPointer.new :pointer # gss_name_t # min_stat = FFI::MemoryPointer.new :OM_uint32 # maj_stat = GSSAPI::LibGSSAPI.gss_import_name(min_stat, buff_str.pointer, GSSAPI::LibGSSAPI.GSS_C_NT_HOSTBASED_SERVICE, name) # name = name.get_pointer(0) # Remember to free the allocated name (gss_name_t) space with gss_release_name attach_function :gss_import_name, [:pointer, :pointer, :pointer, :pointer], :OM_uint32 # OM_uint32 gss_export_name(OM_uint32 * minor_status, const gss_name_t input_name, gss_buffer_t exported_name); attach_function :gss_export_name, [:pointer, :pointer, :pointer], :OM_uint32 # OM_uint32 gss_canonicalize_name(OM_uint32 * minor_status, const gss_name_t input_name, const gss_OID mech_type, gss_name_t * output_name) attach_function :gss_canonicalize_name, [:pointer, :pointer, :pointer, :pointer], :OM_uint32 begin # OM_uint32 gss_oid_to_str(OM_uint32 *minor_status, const gss_OID oid, gss_buffer_t oid_str); # @example: # min_stat = FFI::MemoryPointer.new :OM_uint32 # oidstr = GSSAPI::LibGSSAPI::ManagedGssBufferDesc.new # maj_stat = GSSAPI::LibGSSAPI.gss_oid_to_str(min_stat, GSSAPI::LibGSSAPI.GSS_C_NT_HOSTBASED_SERVICE, oidstr.pointer) # oidstr[:value].read_string attach_function :gss_oid_to_str, [:pointer, :pointer, :pointer], :OM_uint32 # OM_uint32 gss_str_to_oid(OM_uint32 *minor_status, const gss_buffer_t oid_str, gss_OID *oid); # @example: Simulate GSS_C_NT_HOSTBASED_SERVICE # min_stat = FFI::MemoryPointer.new :OM_uint32 # str = "{ 1 2 840 113554 1 2 1 4 }" # oidstr = GSSAPI::LibGSSAPI::UnManagedGssBufferDesc.new # oidstr[:length] = str.length # oidstr[:value] = FFI::MemoryPointer.from_string str # oid = FFI::MemoryPointer.new :pointer # min_stat = FFI::MemoryPointer.new :OM_uint32 # maj_stat = GSSAPI::LibGSSAPI.gss_str_to_oid(min_stat, oidstr.pointer, oid) # oid = GSSAPI::LibGSSAPI::GssOID.new(oid.get_pointer(0)) attach_function :gss_str_to_oid, [:pointer, :pointer, :pointer], :OM_uint32 rescue FFI::NotFoundError => ex warn "WARNING: Could not load OID conversion methods. Check your GSSAPI C library for an update" end # OM_uint32 gss_init_sec_context(OM_uint32 * minor_status, const gss_cred_id_t initiator_cred_handle, # gss_ctx_id_t * context_handle, const gss_name_t target_name, const gss_OID mech_type, OM_uint32 req_flags, # OM_uint32 time_req, const gss_channel_bindings_t input_chan_bindings, const gss_buffer_t input_token, # gss_OID * actual_mech_type, gss_buffer_t output_token, OM_uint32 * ret_flags, OM_uint32 * time_rec); attach_function :gss_init_sec_context, [:pointer, :pointer, :pointer, :pointer, :pointer, :OM_uint32, :OM_uint32, :pointer, :pointer, :pointer, :pointer, :pointer, :pointer], :OM_uint32 # OM_uint32 gss_accept_sec_context(OM_uint32 *minor_status, gss_ctx_id_t *context_handle, const gss_cred_id_t acceptor_cred_handle, # const gss_buffer_t input_token_buffer, const gss_channel_bindings_t input_chan_bindings, gss_name_t *src_name, gss_OID *mech_type, # gss_buffer_t output_token, OM_uint32 *ret_flags, OM_uint32 *time_rec, gss_cred_id_t *delegated_cred_handle); attach_function :gss_accept_sec_context, [:pointer, :pointer, :pointer, :pointer, :pointer, :pointer, :pointer, :pointer, :pointer, :pointer, :pointer], :OM_uint32 # OM_uint32 gss_display_name(OM_uint32 * minor_status, gss_name_t input_name, gss_buffer_t output_name_buffer, gss_OID * output_name_type); attach_function :gss_display_name, [:pointer, :pointer, :pointer, :pointer], :OM_uint32 # OM_uint32 gss_acquire_cred(OM_uint32 *minor_status, const gss_name_t desired_name, OM_uint32 time_req, const gss_OID_set desired_mechs, # gss_cred_usage_t cred_usage, gss_cred_id_t *output_cred_handle, gss_OID_set *actual_mechs, OM_uint32 *time_rec); attach_function :gss_acquire_cred, [:pointer, :pointer, :OM_uint32, :pointer, :OM_uint32, :pointer, :pointer, :pointer], :OM_uint32 # OM_uint32 gss_wrap(OM_uint32 * minor_status, const gss_ctx_id_t context_handle, int conf_req_flag, # gss_qop_t qop_req, const gss_buffer_t input_message_buffer, int * conf_state, gss_buffer_t output_message_buffer); # @example: # min_stat = FFI::MemoryPointer.new :OM_uint32 # Remember to free the allocated output_message_buffer with gss_release_buffer attach_function :gss_wrap, [:pointer, :pointer, :int, :OM_uint32, :pointer, :pointer, :pointer], :OM_uint32 # Some versions of GSSAPI might not have support for IOV yet. begin # OM_uint32 GSSAPI_LIB_FUNCTION gss_wrap_iov( OM_uint32 * minor_status, gss_ctx_id_t context_handle, # int conf_req_flag, gss_qop_t qop_req, int * conf_state, gss_iov_buffer_desc * iov, int iov_count ); attach_function :gss_wrap_iov, [:pointer, :pointer, :int, :OM_uint32, :pointer, :pointer, :int], :OM_uint32 # OM_uint32 GSSAPI_LIB_FUNCTION gss_unwrap_iov ( OM_uint32 * minor_status, gss_ctx_id_t context_handle, # int * conf_state, gss_qop_t * qop_state, gss_iov_buffer_desc * iov, int iov_count ) attach_function :gss_unwrap_iov, [:pointer, :pointer, :pointer, :pointer, :pointer, :int], :OM_uint32 # OM_uint32 GSSAPI_LIB_CALL gss_wrap_iov_length ( OM_uint32 * minor_status, gss_ctx_id_t context_handle, # int conf_req_flag, gss_qop_t qop_req, int * conf_state, gss_iov_buffer_desc * iov, int iov_count) attach_function :gss_wrap_iov_length, [:pointer, :pointer, :int, :OM_uint32, :pointer, :pointer, :int], :OM_uint32 rescue FFI::NotFoundError => ex warn "WARNING: Could not load IOV methods. Check your GSSAPI C library for an update" end begin # OM_uint32 gss_wrap_aead(OM_uint32 * minor_status, gss_ctx_id_t context_handle, int conf_req_flag, # gss_qop_t qop_req, gss_buffer_t input_assoc_buffer, # gss_buffer_t input_payload_buffer, int * conf_state, gss_buffer_t output_message_buffer); attach_function :gss_wrap_aead, [:pointer, :pointer, :int, :OM_uint32, :pointer, :pointer, :pointer, :pointer], :OM_uint32 # OM_uint32 gss_unwrap_aead(OM_uint32 * minor_status, gss_ctx_id_t context_handle, gss_buffer_t input_message_buffer, # gss_buffer_t input_assoc_buffer, gss_buffer_t output_payload_buffer, int * conf_state, gss_qop_t * qop_state); attach_function :gss_unwrap_aead, [:pointer,:pointer,:pointer,:pointer,:pointer,:pointer,:pointer], :OM_uint32 rescue FFI::NotFoundError => ex warn "WARNING: Could not load AEAD methods. Check your GSSAPI C library for an update" end # OM_uint32 gss_unwrap(OM_uint32 * minor_status, const gss_ctx_id_t context_handle, # const gss_buffer_t input_message_buffer, gss_buffer_t output_message_buffer, int * conf_state, gss_qop_t * qop_state); # @example: # min_stat = FFI::MemoryPointer.new :OM_uint32 # Remember to free the allocated output_message_buffer with gss_release_buffer attach_function :gss_unwrap, [:pointer, :pointer, :pointer, :pointer, :pointer, :pointer], :OM_uint32 # OM_uint32 gss_get_mic(OM_uint32 * minor_status, const gss_ctx_id_t context_handle, gss_qop_t qop_req, const gss_buffer_t input_message_buffer, gss_buffer_t output_message_buffer) attach_function :gss_get_mic, [:pointer, :pointer, :OM_uint32, :pointer, :pointer], :OM_uint32 # OM_uint32 gss_delete_sec_context(OM_uint32 * minor_status, gss_ctx_id_t * context_handle, gss_buffer_t output_token); attach_function :gss_delete_sec_context, [:pointer, :pointer, :pointer], :OM_uint32 # OM_uint32 gss_release_name(OM_uint32 * minor_status, gss_name_t * name); attach_function :gss_release_name, [:pointer, :pointer], :OM_uint32 # OM_uint32 gss_release_buffer(OM_uint32 * minor_status, gss_buffer_t buffer); attach_function :gss_release_buffer, [:pointer, :pointer], :OM_uint32 # OM_uint32 gss_release_cred(OM_uint32 *minor_status, gss_cred_id_t *cred_handle); attach_function :gss_release_cred, [:pointer, :pointer], :OM_uint32 # Used to register alternate keytabs # OM_uint32 krb5_gss_register_acceptor_identity(const char *); attach_function :krb5_gss_register_acceptor_identity, [:string], :OM_uint32 # OM_uint32 gss_display_status(OM_uint32 *minor_status, OM_uint32 status_value, int status_type, gss_OID mech_type, OM_uint32 *message_context, gss_buffer_t status_string) attach_function :gss_display_status, [:pointer, :OM_uint32, :int, :pointer, :pointer, :pointer], :OM_uint32 # Variable definitions # -------------------- # Flag bits for context-level services. GSS_C_DELEG_FLAG = 1 GSS_C_MUTUAL_FLAG = 2 GSS_C_REPLAY_FLAG = 4 GSS_C_SEQUENCE_FLAG = 8 GSS_C_CONF_FLAG = 16 GSS_C_INTEG_FLAG = 32 GSS_C_ANON_FLAG = 64 GSS_C_PROT_READY_FLAG = 128 GSS_C_TRANS_FLAG = 256 GSS_C_DELEG_POLICY_FLAG = 32768 # Credential usage options GSS_C_BOTH = 0 GSS_C_INITIATE = 1 GSS_C_ACCEPT = 2 # Misc Constants GSS_C_INDEFINITE = 0xffffffff # Expiration time of 2^32-1 seconds means infinite lifetime for sec or cred context # Message Offsets GSS_C_CALLING_ERROR_OFFSET = 24 GSS_C_ROUTINE_ERROR_OFFSET = 16 GSS_C_SUPPLEMENTARY_OFFSET = 0 # GSS_C_CALLING_ERROR_MASK ((OM_uint32) 0377ul) # GSS_C_ROUTINE_ERROR_MASK ((OM_uint32) 0377ul) # GSS_C_SUPPLEMENTARY_MASK ((OM_uint32) 0177777ul) # QOP (Quality of Protection) GSS_C_QOP_DEFAULT = 0 # GSSAPI Status & Error Codes GSS_S_COMPLETE = 0 GSS_C_GSS_CODE = 1 GSS_C_MECH_CODE = 2 GSS_C_CALLING_ERRORS = { (1 << GSS_C_CALLING_ERROR_OFFSET) => "GSS_S_CALL_INACCESSIBLE_READ", (2 << GSS_C_CALLING_ERROR_OFFSET) => "GSS_S_CALL_INACCESSIBLE_WRITE", (3 << GSS_C_CALLING_ERROR_OFFSET) => "GSS_S_CALL_BAD_STRUCTURE" } GSS_C_SUPPLEMENTARY_CODES = { (1 << (GSS_C_SUPPLEMENTARY_OFFSET + 0)) => "GSS_S_CONTINUE_NEEDED", (1 << (GSS_C_SUPPLEMENTARY_OFFSET + 1)) => "GSS_S_DUPLICATE_TOKEN", (1 << (GSS_C_SUPPLEMENTARY_OFFSET + 2)) => "GSS_S_OLD_TOKEN", (1 << (GSS_C_SUPPLEMENTARY_OFFSET + 3)) => "GSS_S_UNSEQ_TOKEN", (1 << (GSS_C_SUPPLEMENTARY_OFFSET + 4)) => "GSS_S_GAP_TOKEN" } GSS_C_ROUTINE_ERRORS = { (1 << GSS_C_ROUTINE_ERROR_OFFSET) => "GSS_S_BAD_MECH", (2 << GSS_C_ROUTINE_ERROR_OFFSET) => "GSS_S_BAD_NAME", (3 << GSS_C_ROUTINE_ERROR_OFFSET) => "GSS_S_BAD_NAMETYPE", (4 << GSS_C_ROUTINE_ERROR_OFFSET) => "GSS_S_BAD_BINDINGS", (5 << GSS_C_ROUTINE_ERROR_OFFSET) => "GSS_S_BAD_STATUS", (6 << GSS_C_ROUTINE_ERROR_OFFSET) => "GSS_S_BAD_SIG", (7 << GSS_C_ROUTINE_ERROR_OFFSET) => "GSS_S_NO_CRED", (8 << GSS_C_ROUTINE_ERROR_OFFSET) => "GSS_S_NO_CONTEXT", (9 << GSS_C_ROUTINE_ERROR_OFFSET) => "GSS_S_DEFECTIVE_TOKEN", (10 << GSS_C_ROUTINE_ERROR_OFFSET) => "GSS_S_DEFECTIVE_CREDENTIAL", (11 << GSS_C_ROUTINE_ERROR_OFFSET) => "GSS_S_CREDENTIALS_EXPIRED", (12 << GSS_C_ROUTINE_ERROR_OFFSET) => "GSS_S_CONTEXT_EXPIRED", (13 << GSS_C_ROUTINE_ERROR_OFFSET) => "GSS_S_FAILURE", (14 << GSS_C_ROUTINE_ERROR_OFFSET) => "GSS_S_BAD_QOP", (15 << GSS_C_ROUTINE_ERROR_OFFSET) => "GSS_S_UNAUTHORIZED", (16 << GSS_C_ROUTINE_ERROR_OFFSET) => "GSS_S_UNAVAILABLE", (17 << GSS_C_ROUTINE_ERROR_OFFSET) => "GSS_S_DUPLICATE_ELEMENT", (18 << GSS_C_ROUTINE_ERROR_OFFSET) => "GSS_S_NAME_NOT_MN" } # IOV Buffer Types (gssapi_ext.h) GSS_IOV_BUFFER_TYPE_EMPTY = 0 GSS_IOV_BUFFER_TYPE_DATA = 1 # Packet data GSS_IOV_BUFFER_TYPE_HEADER = 2 # Mechanism header GSS_IOV_BUFFER_TYPE_MECH_PARAMS = 3 # Mechanism specific parameters GSS_IOV_BUFFER_TYPE_TRAILER = 7 # Mechanism trailer GSS_IOV_BUFFER_TYPE_PADDING = 9 # Padding GSS_IOV_BUFFER_TYPE_STREAM = 10 # Complete wrap token GSS_IOV_BUFFER_TYPE_SIGN_ONLY = 11 # Sign only packet data # Flags GSS_IOV_BUFFER_FLAG_MASK = 0xFFFF0000 GSS_IOV_BUFFER_FLAG_ALLOCATE = 0x00010000 # indicates GSS should allocate GSS_IOV_BUFFER_FLAG_ALLOCATED = 0x00020000 # indicates caller should free # Various Null values. (gssapi.h) GSS_C_NO_NAME = FFI::Pointer.new(:pointer, 0) # ((gss_name_t) 0) GSS_C_NO_BUFFER = FFI::Pointer.new(:pointer, 0) # ((gss_buffer_t) 0) GSS_C_NO_OID = FFI::Pointer.new(:pointer, 0) # ((gss_OID) 0) GSS_C_NO_OID_SET = FFI::Pointer.new(:pointer, 0) # ((gss_OID_set) 0) GSS_C_NO_CONTEXT = FFI::Pointer.new(:pointer, 0) # ((gss_ctx_id_t) 0) GSS_C_NO_CREDENTIAL = FFI::Pointer.new(:pointer, 0) # ((gss_cred_id_t) 0) GSS_C_NO_CHANNEL_BINDINGS = FFI::Pointer.new(:pointer, 0) # ((gss_channel_bindings_t) 0) GSS_C_EMPTY_BUFFER = ManagedGssBufferDesc.new end #end LibGSSAPI end #end GSSAPI