0001
0002
0003
0004"""
0005PAM module for python
0006
0007Provides an authenticate function that will allow the caller to authenticate
0008a user against the Pluggable Authentication Modules (PAM) on the system.
0009
0010Implemented using ctypes, so no compilation is necessary.
0011"""
0012__all__ = ['authenticate']
0013
0014from ctypes import CDLL, POINTER, Structure, CFUNCTYPE, cast, pointer, sizeof
0015from ctypes import c_void_p, c_uint, c_char_p, c_char, c_int
0016from ctypes.util import find_library
0017
0018LIBPAM = CDLL(find_library("pam"))
0019LIBC = CDLL(find_library("c"))
0020
0021CALLOC = LIBC.calloc
0022CALLOC.restype = c_void_p
0023CALLOC.argtypes = [c_uint, c_uint]
0024
0025STRDUP = LIBC.strdup
0026STRDUP.argstypes = [c_char_p]
0027STRDUP.restype = POINTER(c_char)
0028
0029
0030PAM_PROMPT_ECHO_OFF = 1
0031PAM_PROMPT_ECHO_ON = 2
0032PAM_ERROR_MSG = 3
0033PAM_TEXT_INFO = 4
0034
0035class PamHandle(Structure):
0036 """wrapper class for pam_handle_t"""
0037 _fields_ = [
0038 ("handle", c_void_p)
0039 ]
0040
0041 def __init__(self):
0042 Structure.__init__(self)
0043 self.handle = 0
0044
0045class PamMessage(Structure):
0046 """wrapper class for pam_message structure"""
0047 _fields_ = [
0048 ("msg_style", c_int),
0049 ("msg", c_char_p),
0050 ]
0051
0052 def __repr__(self):
0053 return "<PamMessage %i '%s'>" % (self.msg_style, self.msg)
0054
0055class PamResponse(Structure):
0056 """wrapper class for pam_response structure"""
0057 _fields_ = [
0058 ("resp", c_char_p),
0059 ("resp_retcode", c_int),
0060 ]
0061
0062 def __repr__(self):
0063 return "<PamResponse %i '%s'>" % (self.resp_retcode, self.resp)
0064
0065CONV_FUNC = CFUNCTYPE(c_int,
0066 c_int, POINTER(POINTER(PamMessage)),
0067 POINTER(POINTER(PamResponse)), c_void_p)
0068
0069class PamConv(Structure):
0070 """wrapper class for pam_conv structure"""
0071 _fields_ = [
0072 ("conv", CONV_FUNC),
0073 ("appdata_ptr", c_void_p)
0074 ]
0075
0076PAM_START = LIBPAM.pam_start
0077PAM_START.restype = c_int
0078PAM_START.argtypes = [c_char_p, c_char_p, POINTER(PamConv),
0079 POINTER(PamHandle)]
0080
0081PAM_AUTHENTICATE = LIBPAM.pam_authenticate
0082PAM_AUTHENTICATE.restype = c_int
0083PAM_AUTHENTICATE.argtypes = [PamHandle, c_int]
0084
0085def authenticate(username, password, service='login'):
0086 """Returns True if the given username and password authenticate for the
0087 given service. Returns False otherwise
0088
0089 ``username``: the username to authenticate
0090
0091 ``password``: the password in plain text
0092
0093 ``service``: the PAM service to authenticate against.
0094 Defaults to 'login'"""
0095 @CONV_FUNC
0096 def my_conv(n_messages, messages, p_response, app_data):
0097 """Simple conversation function that responds to any
0098 prompt where the echo is off with the supplied password"""
0099
0100 addr = CALLOC(n_messages, sizeof(PamResponse))
0101 p_response[0] = cast(addr, POINTER(PamResponse))
0102 for i in range(n_messages):
0103 if messages[i].contents.msg_style == PAM_PROMPT_ECHO_OFF:
0104 pw_copy = STRDUP(str(password))
0105 p_response.contents[i].resp = cast(pw_copy, c_char_p)
0106 p_response.contents[i].resp_retcode = 0
0107 return 0
0108
0109 handle = PamHandle()
0110 conv = PamConv(my_conv, 0)
0111 retval = PAM_START(service, username, pointer(conv), pointer(handle))
0112
0113 if retval != 0:
0114
0115
0116 return False
0117
0118 retval = PAM_AUTHENTICATE(handle, 0)
0119 return retval == 0
0120
0121if __name__ == "__main__":
0122 import getpass
0123 print authenticate(getpass.getuser(), getpass.getpass())