Help with PAM and ctypes

Lenard Lindstrom len-l at telus.net
Tue Jun 12 00:01:36 CEST 2007


Chris AtLee wrote:
> Sorry for the repeat post...I'm not sure if my first post (on May
> 30th) went through or
> not.
> 
> I've been trying to write a PAM module using ctypes.  In the
> conversation
> function (my_conv in the script below), you're passed in a
> pam_response**
> pointer.  You're supposed to allocate an array of pam_response's and
> set
> the pointer's value to the new array.  Then you fill in the array with
> appropriate data.
> 
> I can't seem to get it working in python...The authenticate function
> always
> returns PAM_AUTHTOK_RECOVER_ERR (21), which I think means the response
> doesn't make any sense.
> 
> I've tried saving the response array outside of my_conv to make sure
> it
> doesn't get garbage collected, but that doesn't seem to help.
> 
> Any pointers would be appreciated!
> 
> Cheers,
> Chris
> 
[snip some code]
> 
> 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
>         # 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.

>         for i in range(nMessages):
>             if messages[i].contents.msg == "Password: ":
>                 p = getpass.getpass()
>                 pResponse.contents[0].resp_retcode = 0
>                 pResponse.contents[0].resp = p
>         return 0
> 
>     handle = pam_handle()
>     c = pam_conv(my_conv, 0)
>     retval = pam_start("login", os.getlogin(), 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)
> 
>     print retval
> 

If you are going to do any serious ctypes coding consider joining the 
ctypes mailing list at sourceforge.net .


--
Lenard Lindstrom
<len-l at telus.net>



More information about the Python-list mailing list