We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
PAM authentication failure
### Describe the bug

<!--- A clear and concise description of what the bug is. -->

<!--- HINT: You can paste gist.github.com links for long logs or larger files -->
Enabling authentication.pam module results in TypeError exception and failure to authenticate.

### To Reproduce

1. Change authentication module to "authentication.pam" in modules.com
2. Restart cobbler service
3. Attempt to login via web UI
4. See error in cobbler.log

````
DEBUG | authn_pam: PAM service is login
INFO | Exception occured: <class 'ctypes.ArgumentError'>
INFO | Exception value: argument 1: <class 'TypeError'>: wrong type
INFO | Exception Info:
  File "/usr/lib/python3.6/site-packages/cobbler/remote.py", line 3477, in _dispatch
    return method_handle(*params)

  File "/usr/lib/python3.6/site-packages/cobbler/remote.py", line 3233, in login
    if self.__validate_user(login_user, login_password):

  File "/usr/lib/python3.6/site-packages/cobbler/remote.py", line 3113, in __validate_user
    return self.api.authenticate(input_user, input_password)

  File "/usr/lib/python3.6/site-packages/cobbler/api.py", line 1599, in authenticate
    rc = self.authn.authenticate(self, user, password)

  File "/usr/lib/python3.6/site-packages/cobbler/modules/authentication/pam.py", line 141, in authenticate
    retval = PAM_START(str(service), str(username), pointer(conv), pointer(handle))

````

### Expected behavior

<!--- A clear and concise description of what you expected to happen. -->
PAM authentication should succeed.

### Version

<!--- Paste output from `cobbler version` -->
````
Cobbler 3.1.2
  source: ?, ?
  build time: Sun May 31 02:32:34 2020
````

### Screenshots

<!--- If applicable, add screenshots to help explain your problem. -->

### Additional context

<!--- Add any other context about the problem here. -->


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `cobbler/modules/authentication/pam.py`
Content:
```
1 """
2 Authentication module that uses /etc/cobbler/auth.conf
3 Choice of authentication module is in /etc/cobbler/modules.conf
4 
5 Copyright 2007-2009, Red Hat, Inc and Others
6 Michael DeHaan <michael.dehaan AT gmail>
7 
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2 of the License, or
11 (at your option) any later version.
12 
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 GNU General Public License for more details.
17 
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21 02110-1301  USA
22 
23 PAM python code based on the pam_python code created by Chris AtLee:
24 http://atlee.ca/software/pam/
25 
26 #-----------------------------------------------
27 pam_python (c) 2007 Chris AtLee <chris@atlee.ca>
28 Licensed under the MIT license:
29 http://www.opensource.org/licenses/mit-license.php
30 
31 PAM module for python
32 
33 Provides an authenticate function that will allow the caller to authenticate
34 a user against the Pluggable Authentication Modules (PAM) on the system.
35 
36 Implemented using ctypes, so no compilation is necessary.
37 """
38 
39 from builtins import str
40 from builtins import range
41 from ctypes import CDLL, POINTER, Structure, CFUNCTYPE, cast, pointer, sizeof
42 from ctypes import c_void_p, c_uint, c_char_p, c_char, c_int
43 from ctypes.util import find_library
44 
45 LIBPAM = CDLL(find_library("pam"))
46 LIBC = CDLL(find_library("c"))
47 
48 CALLOC = LIBC.calloc
49 CALLOC.restype = c_void_p
50 CALLOC.argtypes = [c_uint, c_uint]
51 
52 STRDUP = LIBC.strdup
53 STRDUP.argstypes = [c_char_p]
54 STRDUP.restype = POINTER(c_char)        # NOT c_char_p !!!!
55 
56 # Various constants
57 PAM_PROMPT_ECHO_OFF = 1
58 PAM_PROMPT_ECHO_ON = 2
59 PAM_ERROR_MSG = 3
60 PAM_TEXT_INFO = 4
61 
62 
63 def register():
64     """
65     The mandatory Cobbler module registration hook.
66     """
67     return "authn"
68 
69 
70 class PamHandle(Structure):
71     """wrapper class for pam_handle_t"""
72     _fields_ = [("handle", c_void_p)]
73 
74     def __init__(self):
75         Structure.__init__(self)
76         self.handle = 0
77 
78 
79 class PamMessage(Structure):
80     """wrapper class for pam_message structure"""
81     _fields_ = [("msg_style", c_int), ("msg", c_char_p)]
82 
83     def __repr__(self):
84         return "<PamMessage %i '%s'>" % (self.msg_style, self.msg)
85 
86 
87 class PamResponse(Structure):
88     """wrapper class for pam_response structure"""
89     _fields_ = [("resp", c_char_p), ("resp_retcode", c_int)]
90 
91     def __repr__(self):
92         return "<PamResponse %i '%s'>" % (self.resp_retcode, self.resp)
93 
94 
95 CONV_FUNC = CFUNCTYPE(c_int, c_int, POINTER(POINTER(PamMessage)), POINTER(POINTER(PamResponse)), c_void_p)
96 
97 
98 class PamConv(Structure):
99     """wrapper class for pam_conv structure"""
100     _fields_ = [("conv", CONV_FUNC), ("appdata_ptr", c_void_p)]
101 
102 
103 PAM_START = LIBPAM.pam_start
104 PAM_START.restype = c_int
105 PAM_START.argtypes = [c_char_p, c_char_p, POINTER(PamConv), POINTER(PamHandle)]
106 
107 PAM_AUTHENTICATE = LIBPAM.pam_authenticate
108 PAM_AUTHENTICATE.restype = c_int
109 PAM_AUTHENTICATE.argtypes = [PamHandle, c_int]
110 
111 
112 def authenticate(api_handle, username, password):
113     """
114     Returns True if the given username and password authenticate for the
115     given service.  Returns False otherwise
116     """
117 
118     @CONV_FUNC
119     def my_conv(n_messages, messages, p_response, app_data):
120         """Simple conversation function that responds to any
121         prompt where the echo is off with the supplied password"""
122         # Create an array of n_messages response objects
123         addr = CALLOC(n_messages, sizeof(PamResponse))
124         p_response[0] = cast(addr, POINTER(PamResponse))
125         for i in range(n_messages):
126             if messages[i].contents.msg_style == PAM_PROMPT_ECHO_OFF:
127                 pw_copy = STRDUP(str(password))
128                 p_response.contents[i].resp = cast(pw_copy, c_char_p)
129                 p_response.contents[i].resp_retcode = 0
130         return 0
131 
132     try:
133         service = api_handle.settings().authn_pam_service
134     except:
135         service = 'login'
136 
137     api_handle.logger.debug("authn_pam: PAM service is %s" % service)
138 
139     handle = PamHandle()
140     conv = PamConv(my_conv, 0)
141     retval = PAM_START(service, username, pointer(conv), pointer(handle))
142 
143     if retval != 0:
144         # TODO: This is not an authentication error, something
145         # has gone wrong starting up PAM
146         api_handle.logger.error("authn_pam: error initializing PAM library")
147         return False
148 
149     retval = PAM_AUTHENTICATE(handle, 0)
150     return retval == 0
151
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/cobbler/modules/authentication/pam.py b/cobbler/modules/authentication/pam.py
--- a/cobbler/modules/authentication/pam.py
+++ b/cobbler/modules/authentication/pam.py
@@ -36,7 +36,6 @@
 Implemented using ctypes, so no compilation is necessary.
 """
 
-from builtins import str
 from builtins import range
 from ctypes import CDLL, POINTER, Structure, CFUNCTYPE, cast, pointer, sizeof
 from ctypes import c_void_p, c_uint, c_char_p, c_char, c_int
@@ -68,7 +67,9 @@
 
 
 class PamHandle(Structure):
-    """wrapper class for pam_handle_t"""
+    """
+    wrapper class for pam_handle_t
+    """
     _fields_ = [("handle", c_void_p)]
 
     def __init__(self):
@@ -77,7 +78,9 @@
 
 
 class PamMessage(Structure):
-    """wrapper class for pam_message structure"""
+    """
+    wrapper class for pam_message structure
+    """
     _fields_ = [("msg_style", c_int), ("msg", c_char_p)]
 
     def __repr__(self):
@@ -85,7 +88,9 @@
 
 
 class PamResponse(Structure):
-    """wrapper class for pam_response structure"""
+    """
+    wrapper class for pam_response structure
+    """
     _fields_ = [("resp", c_char_p), ("resp_retcode", c_int)]
 
     def __repr__(self):
@@ -96,7 +101,9 @@
 
 
 class PamConv(Structure):
-    """wrapper class for pam_conv structure"""
+    """
+    wrapper class for pam_conv structure
+    """
     _fields_ = [("conv", CONV_FUNC), ("appdata_ptr", c_void_p)]
 
 
@@ -111,20 +118,26 @@
 
 def authenticate(api_handle, username, password):
     """
-    Returns True if the given username and password authenticate for the
-    given service.  Returns False otherwise
+    :param api_handle: Used for resolving the the pam service name and getting the Logger.
+    :param username:The username to log in with.
+    :type username: str
+    :param password: The password to log in with.
+    :type password: str
+    :returns: True if the given username and password authenticate for the given service. Otherwise False
+    :rtype: bool
     """
 
     @CONV_FUNC
     def my_conv(n_messages, messages, p_response, app_data):
-        """Simple conversation function that responds to any
-        prompt where the echo is off with the supplied password"""
+        """
+        Simple conversation function that responds to any prompt where the echo is off with the supplied password
+        """
         # Create an array of n_messages response objects
         addr = CALLOC(n_messages, sizeof(PamResponse))
         p_response[0] = cast(addr, POINTER(PamResponse))
         for i in range(n_messages):
             if messages[i].contents.msg_style == PAM_PROMPT_ECHO_OFF:
-                pw_copy = STRDUP(str(password))
+                pw_copy = STRDUP(password.encode())
                 p_response.contents[i].resp = cast(pw_copy, c_char_p)
                 p_response.contents[i].resp_retcode = 0
         return 0
@@ -138,11 +151,10 @@
 
     handle = PamHandle()
     conv = PamConv(my_conv, 0)
-    retval = PAM_START(service, username, pointer(conv), pointer(handle))
+    retval = PAM_START(service.encode(), username.encode(), pointer(conv), pointer(handle))
 
     if retval != 0:
-        # TODO: This is not an authentication error, something
-        # has gone wrong starting up PAM
+        # TODO: This is not an authentication error, something has gone wrong starting up PAM
         api_handle.logger.error("authn_pam: error initializing PAM library")
         return False

{"golden_diff": "diff --git a/cobbler/modules/authentication/pam.py b/cobbler/modules/authentication/pam.py\n--- a/cobbler/modules/authentication/pam.py\n+++ b/cobbler/modules/authentication/pam.py\n@@ -36,7 +36,6 @@\n Implemented using ctypes, so no compilation is necessary.\n \"\"\"\n \n-from builtins import str\n from builtins import range\n from ctypes import CDLL, POINTER, Structure, CFUNCTYPE, cast, pointer, sizeof\n from ctypes import c_void_p, c_uint, c_char_p, c_char, c_int\n@@ -68,7 +67,9 @@\n \n \n class PamHandle(Structure):\n-    \"\"\"wrapper class for pam_handle_t\"\"\"\n+    \"\"\"\n+    wrapper class for pam_handle_t\n+    \"\"\"\n     _fields_ = [(\"handle\", c_void_p)]\n \n     def __init__(self):\n@@ -77,7 +78,9 @@\n \n \n class PamMessage(Structure):\n-    \"\"\"wrapper class for pam_message structure\"\"\"\n+    \"\"\"\n+    wrapper class for pam_message structure\n+    \"\"\"\n     _fields_ = [(\"msg_style\", c_int), (\"msg\", c_char_p)]\n \n     def __repr__(self):\n@@ -85,7 +88,9 @@\n \n \n class PamResponse(Structure):\n-    \"\"\"wrapper class for pam_response structure\"\"\"\n+    \"\"\"\n+    wrapper class for pam_response structure\n+    \"\"\"\n     _fields_ = [(\"resp\", c_char_p), (\"resp_retcode\", c_int)]\n \n     def __repr__(self):\n@@ -96,7 +101,9 @@\n \n \n class PamConv(Structure):\n-    \"\"\"wrapper class for pam_conv structure\"\"\"\n+    \"\"\"\n+    wrapper class for pam_conv structure\n+    \"\"\"\n     _fields_ = [(\"conv\", CONV_FUNC), (\"appdata_ptr\", c_void_p)]\n \n \n@@ -111,20 +118,26 @@\n \n def authenticate(api_handle, username, password):\n     \"\"\"\n-    Returns True if the given username and password authenticate for the\n-    given service.  Returns False otherwise\n+    :param api_handle: Used for resolving the the pam service name and getting the Logger.\n+    :param username:The username to log in with.\n+    :type username: str\n+    :param password: The password to log in with.\n+    :type password: str\n+    :returns: True if the given username and password authenticate for the given service. Otherwise False\n+    :rtype: bool\n     \"\"\"\n \n     @CONV_FUNC\n     def my_conv(n_messages, messages, p_response, app_data):\n-        \"\"\"Simple conversation function that responds to any\n-        prompt where the echo is off with the supplied password\"\"\"\n+        \"\"\"\n+        Simple conversation function that responds to any prompt where the echo is off with the supplied password\n+        \"\"\"\n         # Create an array of n_messages response objects\n         addr = CALLOC(n_messages, sizeof(PamResponse))\n         p_response[0] = cast(addr, POINTER(PamResponse))\n         for i in range(n_messages):\n             if messages[i].contents.msg_style == PAM_PROMPT_ECHO_OFF:\n-                pw_copy = STRDUP(str(password))\n+                pw_copy = STRDUP(password.encode())\n                 p_response.contents[i].resp = cast(pw_copy, c_char_p)\n                 p_response.contents[i].resp_retcode = 0\n         return 0\n@@ -138,11 +151,10 @@\n \n     handle = PamHandle()\n     conv = PamConv(my_conv, 0)\n-    retval = PAM_START(service, username, pointer(conv), pointer(handle))\n+    retval = PAM_START(service.encode(), username.encode(), pointer(conv), pointer(handle))\n \n     if retval != 0:\n-        # TODO: This is not an authentication error, something\n-        # has gone wrong starting up PAM\n+        # TODO: This is not an authentication error, something has gone wrong starting up PAM\n         api_handle.logger.error(\"authn_pam: error initializing PAM library\")\n         return False\n", "issue": "PAM authentication failure\n### Describe the bug\r\n\r\n<!--- A clear and concise description of what the bug is. -->\r\n\r\n<!--- HINT: You can paste gist.github.com links for long logs or larger files -->\r\nEnabling authentication.pam module results in TypeError exception and failure to authenticate.\r\n\r\n### To Reproduce\r\n\r\n1. Change authentication module to \"authentication.pam\" in modules.com\r\n2. Restart cobbler service\r\n3. Attempt to login via web UI\r\n4. See error in cobbler.log\r\n\r\n````\r\nDEBUG | authn_pam: PAM service is login\r\nINFO | Exception occured: <class 'ctypes.ArgumentError'>\r\nINFO | Exception value: argument 1: <class 'TypeError'>: wrong type\r\nINFO | Exception Info:\r\n  File \"/usr/lib/python3.6/site-packages/cobbler/remote.py\", line 3477, in _dispatch\r\n    return method_handle(*params)\r\n\r\n  File \"/usr/lib/python3.6/site-packages/cobbler/remote.py\", line 3233, in login\r\n    if self.__validate_user(login_user, login_password):\r\n\r\n  File \"/usr/lib/python3.6/site-packages/cobbler/remote.py\", line 3113, in __validate_user\r\n    return self.api.authenticate(input_user, input_password)\r\n\r\n  File \"/usr/lib/python3.6/site-packages/cobbler/api.py\", line 1599, in authenticate\r\n    rc = self.authn.authenticate(self, user, password)\r\n\r\n  File \"/usr/lib/python3.6/site-packages/cobbler/modules/authentication/pam.py\", line 141, in authenticate\r\n    retval = PAM_START(str(service), str(username), pointer(conv), pointer(handle))\r\n\r\n````\r\n\r\n### Expected behavior\r\n\r\n<!--- A clear and concise description of what you expected to happen. -->\r\nPAM authentication should succeed.\r\n\r\n### Version\r\n\r\n<!--- Paste output from `cobbler version` -->\r\n````\r\nCobbler 3.1.2\r\n  source: ?, ?\r\n  build time: Sun May 31 02:32:34 2020\r\n````\r\n\r\n### Screenshots\r\n\r\n<!--- If applicable, add screenshots to help explain your problem. -->\r\n\r\n### Additional context\r\n\r\n<!--- Add any other context about the problem here. -->\r\n\n", "before_files": [{"content": "\"\"\"\nAuthentication module that uses /etc/cobbler/auth.conf\nChoice of authentication module is in /etc/cobbler/modules.conf\n\nCopyright 2007-2009, Red Hat, Inc and Others\nMichael DeHaan <michael.dehaan AT gmail>\n\nThis program is free software; you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation; either version 2 of the License, or\n(at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program; if not, write to the Free Software\nFoundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA\n02110-1301  USA\n\nPAM python code based on the pam_python code created by Chris AtLee:\nhttp://atlee.ca/software/pam/\n\n#-----------------------------------------------\npam_python (c) 2007 Chris AtLee <chris@atlee.ca>\nLicensed under the MIT license:\nhttp://www.opensource.org/licenses/mit-license.php\n\nPAM module for python\n\nProvides an authenticate function that will allow the caller to authenticate\na user against the Pluggable Authentication Modules (PAM) on the system.\n\nImplemented using ctypes, so no compilation is necessary.\n\"\"\"\n\nfrom builtins import str\nfrom builtins import range\nfrom ctypes import CDLL, POINTER, Structure, CFUNCTYPE, cast, pointer, sizeof\nfrom ctypes import c_void_p, c_uint, c_char_p, c_char, c_int\nfrom ctypes.util import find_library\n\nLIBPAM = CDLL(find_library(\"pam\"))\nLIBC = CDLL(find_library(\"c\"))\n\nCALLOC = LIBC.calloc\nCALLOC.restype = c_void_p\nCALLOC.argtypes = [c_uint, c_uint]\n\nSTRDUP = LIBC.strdup\nSTRDUP.argstypes = [c_char_p]\nSTRDUP.restype = POINTER(c_char)        # NOT c_char_p !!!!\n\n# Various constants\nPAM_PROMPT_ECHO_OFF = 1\nPAM_PROMPT_ECHO_ON = 2\nPAM_ERROR_MSG = 3\nPAM_TEXT_INFO = 4\n\n\ndef register():\n    \"\"\"\n    The mandatory Cobbler module registration hook.\n    \"\"\"\n    return \"authn\"\n\n\nclass PamHandle(Structure):\n    \"\"\"wrapper class for pam_handle_t\"\"\"\n    _fields_ = [(\"handle\", c_void_p)]\n\n    def __init__(self):\n        Structure.__init__(self)\n        self.handle = 0\n\n\nclass PamMessage(Structure):\n    \"\"\"wrapper class for pam_message structure\"\"\"\n    _fields_ = [(\"msg_style\", c_int), (\"msg\", c_char_p)]\n\n    def __repr__(self):\n        return \"<PamMessage %i '%s'>\" % (self.msg_style, self.msg)\n\n\nclass PamResponse(Structure):\n    \"\"\"wrapper class for pam_response structure\"\"\"\n    _fields_ = [(\"resp\", c_char_p), (\"resp_retcode\", c_int)]\n\n    def __repr__(self):\n        return \"<PamResponse %i '%s'>\" % (self.resp_retcode, self.resp)\n\n\nCONV_FUNC = CFUNCTYPE(c_int, c_int, POINTER(POINTER(PamMessage)), POINTER(POINTER(PamResponse)), c_void_p)\n\n\nclass PamConv(Structure):\n    \"\"\"wrapper class for pam_conv structure\"\"\"\n    _fields_ = [(\"conv\", CONV_FUNC), (\"appdata_ptr\", c_void_p)]\n\n\nPAM_START = LIBPAM.pam_start\nPAM_START.restype = c_int\nPAM_START.argtypes = [c_char_p, c_char_p, POINTER(PamConv), POINTER(PamHandle)]\n\nPAM_AUTHENTICATE = LIBPAM.pam_authenticate\nPAM_AUTHENTICATE.restype = c_int\nPAM_AUTHENTICATE.argtypes = [PamHandle, c_int]\n\n\ndef authenticate(api_handle, username, password):\n    \"\"\"\n    Returns True if the given username and password authenticate for the\n    given service.  Returns False otherwise\n    \"\"\"\n\n    @CONV_FUNC\n    def my_conv(n_messages, messages, p_response, app_data):\n        \"\"\"Simple conversation function that responds to any\n        prompt where the echo is off with the supplied password\"\"\"\n        # Create an array of n_messages response objects\n        addr = CALLOC(n_messages, sizeof(PamResponse))\n        p_response[0] = cast(addr, POINTER(PamResponse))\n        for i in range(n_messages):\n            if messages[i].contents.msg_style == PAM_PROMPT_ECHO_OFF:\n                pw_copy = STRDUP(str(password))\n                p_response.contents[i].resp = cast(pw_copy, c_char_p)\n                p_response.contents[i].resp_retcode = 0\n        return 0\n\n    try:\n        service = api_handle.settings().authn_pam_service\n    except:\n        service = 'login'\n\n    api_handle.logger.debug(\"authn_pam: PAM service is %s\" % service)\n\n    handle = PamHandle()\n    conv = PamConv(my_conv, 0)\n    retval = PAM_START(service, username, pointer(conv), pointer(handle))\n\n    if retval != 0:\n        # TODO: This is not an authentication error, something\n        # has gone wrong starting up PAM\n        api_handle.logger.error(\"authn_pam: error initializing PAM library\")\n        return False\n\n    retval = PAM_AUTHENTICATE(handle, 0)\n    return retval == 0\n", "path": "cobbler/modules/authentication/pam.py"}], "after_files": [{"content": "\"\"\"\nAuthentication module that uses /etc/cobbler/auth.conf\nChoice of authentication module is in /etc/cobbler/modules.conf\n\nCopyright 2007-2009, Red Hat, Inc and Others\nMichael DeHaan <michael.dehaan AT gmail>\n\nThis program is free software; you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation; either version 2 of the License, or\n(at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program; if not, write to the Free Software\nFoundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA\n02110-1301  USA\n\nPAM python code based on the pam_python code created by Chris AtLee:\nhttp://atlee.ca/software/pam/\n\n#-----------------------------------------------\npam_python (c) 2007 Chris AtLee <chris@atlee.ca>\nLicensed under the MIT license:\nhttp://www.opensource.org/licenses/mit-license.php\n\nPAM module for python\n\nProvides an authenticate function that will allow the caller to authenticate\na user against the Pluggable Authentication Modules (PAM) on the system.\n\nImplemented using ctypes, so no compilation is necessary.\n\"\"\"\n\nfrom builtins import range\nfrom ctypes import CDLL, POINTER, Structure, CFUNCTYPE, cast, pointer, sizeof\nfrom ctypes import c_void_p, c_uint, c_char_p, c_char, c_int\nfrom ctypes.util import find_library\n\nLIBPAM = CDLL(find_library(\"pam\"))\nLIBC = CDLL(find_library(\"c\"))\n\nCALLOC = LIBC.calloc\nCALLOC.restype = c_void_p\nCALLOC.argtypes = [c_uint, c_uint]\n\nSTRDUP = LIBC.strdup\nSTRDUP.argstypes = [c_char_p]\nSTRDUP.restype = POINTER(c_char)        # NOT c_char_p !!!!\n\n# Various constants\nPAM_PROMPT_ECHO_OFF = 1\nPAM_PROMPT_ECHO_ON = 2\nPAM_ERROR_MSG = 3\nPAM_TEXT_INFO = 4\n\n\ndef register():\n    \"\"\"\n    The mandatory Cobbler module registration hook.\n    \"\"\"\n    return \"authn\"\n\n\nclass PamHandle(Structure):\n    \"\"\"\n    wrapper class for pam_handle_t\n    \"\"\"\n    _fields_ = [(\"handle\", c_void_p)]\n\n    def __init__(self):\n        Structure.__init__(self)\n        self.handle = 0\n\n\nclass PamMessage(Structure):\n    \"\"\"\n    wrapper class for pam_message structure\n    \"\"\"\n    _fields_ = [(\"msg_style\", c_int), (\"msg\", c_char_p)]\n\n    def __repr__(self):\n        return \"<PamMessage %i '%s'>\" % (self.msg_style, self.msg)\n\n\nclass PamResponse(Structure):\n    \"\"\"\n    wrapper class for pam_response structure\n    \"\"\"\n    _fields_ = [(\"resp\", c_char_p), (\"resp_retcode\", c_int)]\n\n    def __repr__(self):\n        return \"<PamResponse %i '%s'>\" % (self.resp_retcode, self.resp)\n\n\nCONV_FUNC = CFUNCTYPE(c_int, c_int, POINTER(POINTER(PamMessage)), POINTER(POINTER(PamResponse)), c_void_p)\n\n\nclass PamConv(Structure):\n    \"\"\"\n    wrapper class for pam_conv structure\n    \"\"\"\n    _fields_ = [(\"conv\", CONV_FUNC), (\"appdata_ptr\", c_void_p)]\n\n\nPAM_START = LIBPAM.pam_start\nPAM_START.restype = c_int\nPAM_START.argtypes = [c_char_p, c_char_p, POINTER(PamConv), POINTER(PamHandle)]\n\nPAM_AUTHENTICATE = LIBPAM.pam_authenticate\nPAM_AUTHENTICATE.restype = c_int\nPAM_AUTHENTICATE.argtypes = [PamHandle, c_int]\n\n\ndef authenticate(api_handle, username, password):\n    \"\"\"\n    :param api_handle: Used for resolving the the pam service name and getting the Logger.\n    :param username:The username to log in with.\n    :type username: str\n    :param password: The password to log in with.\n    :type password: str\n    :returns: True if the given username and password authenticate for the given service. Otherwise False\n    :rtype: bool\n    \"\"\"\n\n    @CONV_FUNC\n    def my_conv(n_messages, messages, p_response, app_data):\n        \"\"\"\n        Simple conversation function that responds to any prompt where the echo is off with the supplied password\n        \"\"\"\n        # Create an array of n_messages response objects\n        addr = CALLOC(n_messages, sizeof(PamResponse))\n        p_response[0] = cast(addr, POINTER(PamResponse))\n        for i in range(n_messages):\n            if messages[i].contents.msg_style == PAM_PROMPT_ECHO_OFF:\n                pw_copy = STRDUP(password.encode())\n                p_response.contents[i].resp = cast(pw_copy, c_char_p)\n                p_response.contents[i].resp_retcode = 0\n        return 0\n\n    try:\n        service = api_handle.settings().authn_pam_service\n    except:\n        service = 'login'\n\n    api_handle.logger.debug(\"authn_pam: PAM service is %s\" % service)\n\n    handle = PamHandle()\n    conv = PamConv(my_conv, 0)\n    retval = PAM_START(service.encode(), username.encode(), pointer(conv), pointer(handle))\n\n    if retval != 0:\n        # TODO: This is not an authentication error, something has gone wrong starting up PAM\n        api_handle.logger.error(\"authn_pam: error initializing PAM library\")\n        return False\n\n    retval = PAM_AUTHENTICATE(handle, 0)\n    return retval == 0\n", "path": "cobbler/modules/authentication/pam.py"}]}