On Jun 11, 6:01 pm, Lenard Lindstrom <[EMAIL PROTECTED]> wrote: [snip snip snip] > > if __name__ == "__main__": > > import getpass, os, sys > > @conv_func > > def my_conv(nMessages, messages, pResponse, appData): > > # Create an array of nMessages response objects > > # Does r get GC'ed after we're all done? > > r = (pam_response * nMessages)() > > The memory allocated to r is garbage collected immediately after my_conv > returns. You need to allocate it explicitly using C's calloc or such. > This assumes pam_start will free the memory for you. > > addr = calloc(sizeof(pam_response), nMessages) > > addr is the memory address as a Python integer. > > > pResponse.contents = cast(r, POINTER(pam_response)) > > pResponse.contents changes the actual value of pResponse, a value on the > stack. You want to change the value of the pointer pResponse points to: > > pResponse[0] = cast(addr, POINTER(pam_response)) > > The cast creates a POINTER(pam_reponse) instance with value addr.
Ahhh, thank you! I never understood how ctypes' pointer.contents related to C pointers. So, the following are equivalent? int v = 42; v = 42 int *p = 0; p = cast(0, POINTER(c_int)) p = &v; p.contents = v *p = 123; p[0] = 123 Using "pResponse[0] = cast(...)" got me part of the way to fixing my problem. PAM started either crashing or saying authentication had failed. The crash was in free(), and some digging around revealed that PAM tries to free the response sent by the application, which makes sense. My initial attempt to fix this involved wrapping strdup to allocate a new copy of a string to send back to PAM. Here's how I wrapped it: strdup = libc.strdup strdup.argstypes = [c_char_p] strdup.restype = c_char_p This still crashed in free(). I took a look at some of the ctypes regression tests and something made me try this: strdup = libc.strdup strdup.argstypes = [c_char_p] strdup.restype = POINTER(c_char) # NOT c_char_p !!!! This works like a charm. Not sure why though...Does ctypes do something special with c_char_p return types? It seems as if Python was freeing the result of strdup() when the string was GC'ed, and then PAM would fail when trying to free the same memory. My working code is pasted below. Thanks, Chris from ctypes import * libpam = CDLL("libpam.so") libc = CDLL("libc.so.6") calloc = libc.calloc calloc.restype = c_void_p calloc.argtypes = [c_uint, c_uint] strdup = libc.strdup strdup.argstypes = [c_char_p] strdup.restype = POINTER(c_char) # NOT c_char_p !!!! # Various constants PAM_PROMPT_ECHO_OFF = 1 PAM_PROMPT_ECHO_ON = 2 PAM_ERROR_MSG = 3 PAM_TEXT_INFO = 4 class pam_handle(Structure): _fields_ = [ ("handle", c_void_p) ] def __init__(self): self.handle = 0 class pam_message(Structure): _fields_ = [ ("msg_style", c_int), ("msg", c_char_p), ] def __repr__(self): return "<pam_message %i '%s'>" % (self.msg_style, self.msg) class pam_response(Structure): _fields_ = [ ("resp", c_char_p), ("resp_retcode", c_int), ] def __repr__(self): return "<pam_response %i '%s'>" % (self.resp_retcode, self.resp) conv_func = CFUNCTYPE(c_int, c_int, POINTER(POINTER(pam_message)), POINTER(POINTER(pam_response)), c_void_p) class pam_conv(Structure): _fields_ = [ ("conv", conv_func), ("appdata_ptr", c_void_p) ] pam_start = libpam.pam_start pam_start.restype = c_int pam_start.argtypes = [c_char_p, c_char_p, POINTER(pam_conv), POINTER(pam_handle)] pam_authenticate = libpam.pam_authenticate pam_authenticate.restype = c_int pam_authenticate.argtypes = [pam_handle, c_int] if __name__ == "__main__": import getpass, os, sys @conv_func def my_conv(nMessages, messages, pResponse, appData): # Create an array of nMessages response objects addr = calloc(nMessages, sizeof(pam_response)) pResponse[0] = cast(addr, POINTER(pam_response)) for i in range(nMessages): if messages[i].contents.msg_style == PAM_PROMPT_ECHO_OFF: p = strdup(getpass.getpass(messages[i].contents.msg)) pResponse.contents[i].resp = cast(p, c_char_p) pResponse.contents[i].resp_retcode = 0 else: print "Unknown message type" return 0 handle = pam_handle() c = pam_conv(my_conv, 0) retval = pam_start("login", getpass.getuser(), pointer(c), pointer(handle)) if retval != 0: print "Couldn't start pam session" sys.exit(-1) retval = pam_authenticate(handle, 0) if retval == 21: print "Authentication information cannot be recovered" sys.exit(-1) elif retval == 7: print "Authentication failure" elif retval == 0: print "Ok!" else: print retval -- http://mail.python.org/mailman/listinfo/python-list