Ffi, kerberos

Hi,

I'm trying to wrap two function calls from kerberos, krb5_init_context
and krb5_free_context. Here are the prototypes for both:

krb5_init_context(krb5_context * context) => krb5_error_code
krb5_free_context(krb5_context context) => void

krb5_context is a struct, but unfortunately I don't know what its
members are.

From the docs:

     The krb5_context structure is designed to hold all per thread
state. All
     global variables that are context specific are stored in this
structure,
     including default encryption types, credentials-cache (ticket
file), and
     default realms.

     The internals of the structure should never be accessed directly,
func-
     tions exist for extracting information.

All I know is that it's 8 bytes in size.

I tried something like this, but it didn't work:

require 'ffi'

module Krb5
  class Context
    extend FFI::Library

    class Error < StandardError; end

    ffi_lib 'krb5'

    attach_function :krb5_init_context, [:string], :uint
    attach_function :krb5_free_context, [:string], :void

    def initialize
      @ctx = FFI::MemoryPointer.new(:pointer, 8)
      ret = krb5_init_context(@ctx)

      if ret > 0
        raise Error, "krb5_init_context() failed"
      end

      if block_given?
        begin
          yield self
        ensure
          free
        end
      end
    end

    def free
      krb5_free_context(@ctx)
    end
  end
end

k = Krb5::Context.new
k.free

I can use a plain string buffer, but then it segfaults on the 'free'
call.

Any suggestions?

Thanks,

Dan

Oops, here's an updated version, but it segfaults on the 'free' call:

require 'ffi'

module Krb5
  class Context
    extend FFI::Library

    class Error < StandardError; end

    ffi_lib 'krb5'

    attach_function :krb5_init_context, [:pointer], :uint
    attach_function :krb5_free_context, [:pointer], :void

    def initialize
      @ctx = FFI::MemoryPointer.new(:char, 8)
      ret = krb5_init_context(@ctx)

      if ret > 0
        raise Error, "krb5_init_context() failed"
      end

      if block_given?
        begin
          yield self
        ensure
          free
        end
      end
    end

    def free
      krb5_free_context(@ctx)
    end
  end
end

k = Krb5::Context.new
k.free

···

On Jan 12, 3:48 pm, Daniel Berger <djber...@gmail.com> wrote:

Hi,

I'm trying to wrap two function calls from kerberos, krb5_init_context
and krb5_free_context. Here are the prototypes for both:

krb5_init_context(krb5_context * context) => krb5_error_code
krb5_free_context(krb5_context context) => void

krb5_context is a struct, but unfortunately I don't know what its
members are.

From the docs:

 The krb5\_context structure is designed to hold all per thread

state. All
global variables that are context specific are stored in this
structure,
including default encryption types, credentials-cache (ticket
file), and
default realms.

 The internals of the structure should never be accessed directly,

func-
tions exist for extracting information.

All I know is that it's 8 bytes in size.

I tried something like this, but it didn't work:

require 'ffi'

module Krb5
class Context
extend FFI::Library

class Error &lt; StandardError; end

ffi\_lib &#39;krb5&#39;

attach\_function :krb5\_init\_context, \[:string\], :uint
attach\_function :krb5\_free\_context, \[:string\], :void

def initialize
  @ctx = FFI::MemoryPointer\.new\(:pointer, 8\)
  ret = krb5\_init\_context\(@ctx\)

  if ret &gt; 0
    raise Error, &quot;krb5\_init\_context\(\) failed&quot;
  end

  if block\_given?
    begin
      yield self
    ensure
      free
    end
  end
end

def free
  krb5\_free\_context\(@ctx\)
end

end
end

k = Krb5::Context.new
k.free

I can use a plain string buffer, but then it segfaults on the 'free'
call.

Any suggestions?

Hi,

Hi,

I'm trying to wrap two function calls from kerberos, krb5_init_context
and krb5_free_context. Here are the prototypes for both:

krb5_init_context(krb5_context * context) => krb5_error_code
krb5_free_context(krb5_context context) => void

krb5_context is a struct, but unfortunately I don't know what its
members are.

From the docs:

The krb5\_context structure is designed to hold all per thread

state. All
global variables that are context specific are stored in this
structure,
including default encryption types, credentials-cache (ticket
file), and
default realms.

The internals of the structure should never be accessed directly,

func-
tions exist for extracting information.

All I know is that it's 8 bytes in size.

I tried something like this, but it didn't work:

require 'ffi'

module Krb5
class Context
extend FFI::Library

class Error < StandardError; end

ffi_lib 'krb5'

attach_function :krb5_init_context, [:string], :uint
attach_function :krb5_free_context, [:string], :void

def initialize
@ctx = FFI::MemoryPointer.new(:pointer, 8)
ret = krb5_init_context(@ctx)

 if ret &gt; 0
   raise Error, &quot;krb5\_init\_context\(\) failed&quot;
 end

 if block\_given?
   begin
     yield self
   ensure
     free
   end
 end

end

def free
krb5_free_context(@ctx)
end
end
end

k = Krb5::Context.new
k.free

I can use a plain string buffer, but then it segfaults on the 'free'
call.

Any suggestions?

In my test platform, sizeof(krb5_context) is 4.
Here is a working code:

require 'ffi'

module Krb5
class Context
   extend FFI::Library

   class Error < StandardError; end

   ffi_lib 'krb5'

   attach_function :krb5_init_context, [:pointer], :uint
   attach_function :krb5_free_context, [:uint], :void

   def initialize
     ptr = FFI::MemoryPointer.new(:char, 4)
     ret = krb5_init_context(ptr)
     if ret > 0
       raise Error, "krb5_init_context() failed"
     end
     @ctx = ptr.read_int

     if block_given?
       begin
         yield self
       ensure
         free
       end
     end
   end

   def free
     krb5_free_context(@ctx)
   end
end
end

k = Krb5::Context.new
k.free

Regards,

Park Heesob

···

2010/1/13 Daniel Berger <djberg96@gmail.com>:

You may want to post this to the ruby-ffi mailing list.

/Shawn

···

On Tue, Jan 12, 2010 at 6:00 PM, Daniel Berger <djberg96@gmail.com> wrote:

On Jan 12, 3:48 pm, Daniel Berger <djber...@gmail.com> wrote:
> Hi,
>
> I'm trying to wrap two function calls from kerberos, krb5_init_context
> and krb5_free_context. Here are the prototypes for both:
>
> krb5_init_context(krb5_context * context) => krb5_error_code
> krb5_free_context(krb5_context context) => void
>
> krb5_context is a struct, but unfortunately I don't know what its
> members are.
>
> From the docs:
>
> The krb5_context structure is designed to hold all per thread
> state. All
> global variables that are context specific are stored in this
> structure,
> including default encryption types, credentials-cache (ticket
> file), and
> default realms.
>
> The internals of the structure should never be accessed directly,
> func-
> tions exist for extracting information.
>
> All I know is that it's 8 bytes in size.
>
> I tried something like this, but it didn't work:
>
> require 'ffi'
>
> module Krb5
> class Context
> extend FFI::Library
>
> class Error < StandardError; end
>
> ffi_lib 'krb5'
>
> attach_function :krb5_init_context, [:string], :uint
> attach_function :krb5_free_context, [:string], :void
>
> def initialize
> @ctx = FFI::MemoryPointer.new(:pointer, 8)
> ret = krb5_init_context(@ctx)
>
> if ret > 0
> raise Error, "krb5_init_context() failed"
> end
>
> if block_given?
> begin
> yield self
> ensure
> free
> end
> end
> end
>
> def free
> krb5_free_context(@ctx)
> end
> end
> end
>
> k = Krb5::Context.new
> k.free
>
> I can use a plain string buffer, but then it segfaults on the 'free'
> call.
>
> Any suggestions?

Oops, here's an updated version, but it segfaults on the 'free' call:

require 'ffi'

module Krb5
class Context
   extend FFI::Library

   class Error < StandardError; end

   ffi_lib 'krb5'

    attach_function :krb5_init_context, [:pointer], :uint
   attach_function :krb5_free_context, [:pointer], :void

   def initialize
     @ctx = FFI::MemoryPointer.new(:char, 8)
      ret = krb5_init_context(@ctx)

     if ret > 0
       raise Error, "krb5_init_context() failed"
     end

     if block_given?
       begin
         yield self
       ensure
         free
       end
     end
   end

   def free
     krb5_free_context(@ctx)
   end
end
end

k = Krb5::Context.new
k.free

This almost got me there. I had to modify it somewhat to account for
the fact that I'm on Snow Leopard (x64), so I replaced :uint
with :long, 4 with 8 and ptr.read_int with ptr.read_long.

Now I need to figure out how to create an FFI::MemoryPointer without
necessarily knowing the size of the first argument.

Thanks for the help!

Regards,

Dan

···

On Jan 12, 6:52 pm, Heesob Park <pha...@gmail.com> wrote:

Hi,

2010/1/13 Daniel Berger <djber...@gmail.com>:

> Hi,

> I'm trying to wrap two function calls from kerberos, krb5_init_context
> and krb5_free_context. Here are the prototypes for both:

> krb5_init_context(krb5_context * context) => krb5_error_code
> krb5_free_context(krb5_context context) => void

> krb5_context is a struct, but unfortunately I don't know what its
> members are.

> From the docs:

> The krb5_context structure is designed to hold all per thread
> state. All
> global variables that are context specific are stored in this
> structure,
> including default encryption types, credentials-cache (ticket
> file), and
> default realms.

> The internals of the structure should never be accessed directly,
> func-
> tions exist for extracting information.

> All I know is that it's 8 bytes in size.

> I tried something like this, but it didn't work:

> require 'ffi'

> module Krb5
> class Context
> extend FFI::Library

> class Error < StandardError; end

> ffi_lib 'krb5'

> attach_function :krb5_init_context, [:string], :uint
> attach_function :krb5_free_context, [:string], :void

> def initialize
> @ctx = FFI::MemoryPointer.new(:pointer, 8)
> ret = krb5_init_context(@ctx)

> if ret > 0
> raise Error, "krb5_init_context() failed"
> end

> if block_given?
> begin
> yield self
> ensure
> free
> end
> end
> end

> def free
> krb5_free_context(@ctx)
> end
> end
> end

> k = Krb5::Context.new
> k.free

> I can use a plain string buffer, but then it segfaults on the 'free'
> call.

> Any suggestions?

In my test platform, sizeof(krb5_context) is 4.
Here is a working code:

require 'ffi'

module Krb5
class Context
extend FFI::Library

class Error < StandardError; end

ffi_lib 'krb5'

attach_function :krb5_init_context, [:pointer], :uint
attach_function :krb5_free_context, [:uint], :void

def initialize
ptr = FFI::MemoryPointer.new(:char, 4)
ret = krb5_init_context(ptr)
if ret > 0
raise Error, "krb5_init_context() failed"
end
@ctx = ptr.read_int

 if block\_given?
   begin
     yield self
   ensure
     free
   end
 end

end

def free
krb5_free_context(@ctx)
end
end
end

k = Krb5::Context.new
k.free

The one bit you are missing here is that krb5_context is not a struct. It's a *pointer* to a struct as can be seen in krb5/krb5.h:

typedef struct _krb5_context * krb5_context;

If krb5_context was a struct you wouldn't be able to do this in C:

krb5_context ctx;
krb5_init_context(&ctx);

Because event the C compiler would not know how to statically allocate without knowing the size of the struct. I assure you that it would complain :slight_smile:

So, with that in mind you can now simply completely ignore the size of the struct and only use pointers. Like this:

module FFI::Krb5
    extend FFI::Library
    ffi_lib "krb5"
    
    attach_function :krb5_init_context, [:buffer_out], :uint
    attach_function :krb5_free_context, [:pointer], :void
end

What you pass to krb5_init_context is really a pointer to a pointer. You can now use that this way:

buf = FFI::Buffer.new(:pointer)
res = FFI::Krb5.krb5_init_context(buf)
puts res

First you allocate a buffer which will contain a pointer (the krb5_context, which is, you get it now, a pointer :)), and then you pass this buffer to the krb5_init_context. But now you will probably want to *use* this context so you first have to get it out of the buffer, like this:

ctx = buf.get_pointer(0)

Which allows you to use it in any other krb5_* call, like the krb5_free_context which we have defined:

FFI::Krb5.krb5_free_context(ptr)

···

On 13 janv. 2010, at 16:53, Daniel Berger wrote:

Now I need to figure out how to create an FFI::MemoryPointer without
necessarily knowing the size of the first argument.

--
Luc Heinrich - luc@honk-honk.com

Luc Heinrich wrote:

···

On 13 janv. 2010, at 16:53, Daniel Berger wrote:

> Now I need to figure out how to create an FFI::MemoryPointer without
> necessarily knowing the size of the first argument.

The one bit you are missing here is that krb5_context is not a struct. It's a *pointer* to a struct as can be seen in krb5/krb5.h:

typedef struct _krb5_context * krb5_context;

If krb5_context was a struct you wouldn't be able to do this in C:

krb5_context ctx;
krb5_init_context(&ctx);

Because event the C compiler would not know how to statically allocate without knowing the size of the struct. I assure you that it would complain :slight_smile:

So, with that in mind you can now simply completely ignore the size of the struct and only use pointers. Like this:

module FFI::Krb5
    extend FFI::Library
    ffi_lib "krb5"

    attach_function :krb5_init_context, [:buffer_out], :uint
    attach_function :krb5_free_context, [:pointer], :void
end

What you pass to krb5_init_context is really a pointer to a pointer. You can now use that this way:

buf = FFI::Buffer.new(:pointer)
res = FFI::Krb5.krb5_init_context(buf)
puts res

First you allocate a buffer which will contain a pointer (the krb5_context, which is, you get it now, a pointer :)), and then you pass this buffer to the krb5_init_context. But now you will probably want to *use* this context so you first have to get it out of the buffer, like this:

ctx = buf.get_pointer(0)

Which allows you to use it in any other krb5_* call, like the krb5_free_context which we have defined:

FFI::Krb5.krb5_free_context(ptr)

Thank you. This was very helpful.

Regards,

Dan