diff --git a/src/moonlock/auth.py b/src/moonlock/auth.py index d458c43..705a4e5 100644 --- a/src/moonlock/auth.py +++ b/src/moonlock/auth.py @@ -23,8 +23,10 @@ class PamMessage(Structure): class PamResponse(Structure): """PAM response structure (pam_response).""" + # resp is c_void_p (not c_char_p) to preserve raw malloc'd pointers — + # ctypes auto-converts c_char_p returns to Python bytes, losing the pointer. _fields_ = [ - ("resp", c_char_p), + ("resp", c_void_p), ("resp_retcode", c_int), ] @@ -56,6 +58,19 @@ def _get_libpam() -> ctypes.CDLL: return ctypes.CDLL(pam_path) +def _get_libc() -> ctypes.CDLL: + """Load and return the libc shared library.""" + libc_path = ctypes.util.find_library("c") + if not libc_path: + raise OSError("libc not found") + libc = ctypes.CDLL(libc_path) + libc.calloc.restype = c_void_p + libc.calloc.argtypes = [ctypes.c_size_t, ctypes.c_size_t] + libc.strdup.restype = c_void_p + libc.strdup.argtypes = [c_char_p] + return libc + + def _make_conv_func(password: str) -> PamConvFunc: """Create a PAM conversation callback that provides the password.""" @@ -65,13 +80,19 @@ def _make_conv_func(password: str) -> PamConvFunc: resp: POINTER(POINTER(PamResponse)), appdata_ptr: c_void_p, ) -> int: - # Allocate response array - response = (PamResponse * num_msg)() + # PAM expects malloc'd memory — it will free() the responses and resp strings + libc = _get_libc() + resp_array = libc.calloc(num_msg, ctypes.sizeof(PamResponse)) + if not resp_array: + return PAM_AUTH_ERR + + resp_ptr = ctypes.cast(resp_array, POINTER(PamResponse)) for i in range(num_msg): - response[i].resp = password.encode("utf-8") - response[i].resp_retcode = 0 - # PAM expects malloc'd memory; ctypes handles this via the array - resp[0] = ctypes.cast(response, POINTER(PamResponse)) + # strdup allocates with malloc, which PAM can safely free() + resp_ptr[i].resp = libc.strdup(password.encode("utf-8")) + resp_ptr[i].resp_retcode = 0 + + resp[0] = resp_ptr return PAM_SUCCESS return PamConvFunc(_conv) diff --git a/src/moonlock/lockscreen.py b/src/moonlock/lockscreen.py index bbcc196..60957d8 100644 --- a/src/moonlock/lockscreen.py +++ b/src/moonlock/lockscreen.py @@ -181,7 +181,10 @@ class LockscreenWindow(Gtk.ApplicationWindow): # Use GLib thread pool to avoid blocking GTK mainloop def _auth_thread() -> bool: - result = _do_auth() + try: + result = _do_auth() + except Exception: + result = False GLib.idle_add(_on_auth_done, result) return GLib.SOURCE_REMOVE