pam.py 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123
  1. # (c) 2007 Chris AtLee <chris@atlee.ca>
  2. # Licensed under the MIT license:
  3. # http://www.opensource.org/licenses/mit-license.php
  4. """
  5. PAM module for python
  6. Provides an authenticate function that will allow the caller to authenticate
  7. a user against the Pluggable Authentication Modules (PAM) on the system.
  8. Implemented using ctypes, so no compilation is necessary.
  9. """
  10. __all__ = ['authenticate']
  11. from ctypes import CDLL, POINTER, Structure, CFUNCTYPE, cast, pointer, sizeof
  12. from ctypes import c_void_p, c_uint, c_char_p, c_char, c_int
  13. from ctypes.util import find_library
  14. LIBPAM = CDLL(find_library("pam"))
  15. LIBC = CDLL(find_library("c"))
  16. CALLOC = LIBC.calloc
  17. CALLOC.restype = c_void_p
  18. CALLOC.argtypes = [c_uint, c_uint]
  19. STRDUP = LIBC.strdup
  20. STRDUP.argstypes = [c_char_p]
  21. STRDUP.restype = POINTER(c_char) # NOT c_char_p !!!!
  22. # Various constants
  23. PAM_PROMPT_ECHO_OFF = 1
  24. PAM_PROMPT_ECHO_ON = 2
  25. PAM_ERROR_MSG = 3
  26. PAM_TEXT_INFO = 4
  27. class PamHandle(Structure):
  28. """wrapper class for pam_handle_t"""
  29. _fields_ = [
  30. ("handle", c_void_p)
  31. ]
  32. def __init__(self):
  33. Structure.__init__(self)
  34. self.handle = 0
  35. class PamMessage(Structure):
  36. """wrapper class for pam_message structure"""
  37. _fields_ = [
  38. ("msg_style", c_int),
  39. ("msg", c_char_p),
  40. ]
  41. def __repr__(self):
  42. return "<PamMessage %i '%s'>" % (self.msg_style, self.msg)
  43. class PamResponse(Structure):
  44. """wrapper class for pam_response structure"""
  45. _fields_ = [
  46. ("resp", c_char_p),
  47. ("resp_retcode", c_int),
  48. ]
  49. def __repr__(self):
  50. return "<PamResponse %i '%s'>" % (self.resp_retcode, self.resp)
  51. CONV_FUNC = CFUNCTYPE(c_int,
  52. c_int, POINTER(POINTER(PamMessage)),
  53. POINTER(POINTER(PamResponse)), c_void_p)
  54. class PamConv(Structure):
  55. """wrapper class for pam_conv structure"""
  56. _fields_ = [
  57. ("conv", CONV_FUNC),
  58. ("appdata_ptr", c_void_p)
  59. ]
  60. PAM_START = LIBPAM.pam_start
  61. PAM_START.restype = c_int
  62. PAM_START.argtypes = [c_char_p, c_char_p, POINTER(PamConv),
  63. POINTER(PamHandle)]
  64. PAM_AUTHENTICATE = LIBPAM.pam_authenticate
  65. PAM_AUTHENTICATE.restype = c_int
  66. PAM_AUTHENTICATE.argtypes = [PamHandle, c_int]
  67. def authenticate(username, password, service='login'):
  68. """Returns True if the given username and password authenticate for the
  69. given service. Returns False otherwise
  70. ``username``: the username to authenticate
  71. ``password``: the password in plain text
  72. ``service``: the PAM service to authenticate against.
  73. Defaults to 'login'"""
  74. @CONV_FUNC
  75. def my_conv(n_messages, messages, p_response, app_data):
  76. """Simple conversation function that responds to any
  77. prompt where the echo is off with the supplied password"""
  78. # Create an array of n_messages response objects
  79. addr = CALLOC(n_messages, sizeof(PamResponse))
  80. p_response[0] = cast(addr, POINTER(PamResponse))
  81. for i in range(n_messages):
  82. if messages[i].contents.msg_style == PAM_PROMPT_ECHO_OFF:
  83. pw_copy = STRDUP(str(password))
  84. p_response.contents[i].resp = cast(pw_copy, c_char_p)
  85. p_response.contents[i].resp_retcode = 0
  86. return 0
  87. handle = PamHandle()
  88. conv = PamConv(my_conv, 0)
  89. retval = PAM_START(service, username, pointer(conv), pointer(handle))
  90. if retval != 0:
  91. # TODO: This is not an authentication error, something
  92. # has gone wrong starting up PAM
  93. return False
  94. retval = PAM_AUTHENTICATE(handle, 0)
  95. return retval == 0
  96. if __name__ == "__main__":
  97. import getpass
  98. print authenticate(getpass.getuser(), getpass.getpass())