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

On Jan 12, 3:48 pm, Daniel B. [email protected] wrote:

 The internals of the structure should never be accessed directly,

class Context
@ctx = FFI::MemoryPointer.new(:pointer, 8)
free
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

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

/Shawn

Hi,

2010/1/13 Daniel B. [email protected]:

  The internals of the structure should never be accessed directly,
 class Context
   @ctx = FFI::MemoryPointer.new(:pointer, 8)
     free
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 H.

On 13 janv. 2010, at 16:53, Daniel B. 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)

On Jan 12, 6:52 pm, Heesob P. [email protected] wrote:

global variables that are context specific are stored in this

ffi_lib ‘krb5’
end
def free

 if ret > 0
 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

Luc H. wrote:

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