summaryrefslogtreecommitdiff
path: root/swaylock/pam.c
diff options
context:
space:
mode:
authorDrew DeVault <[email protected]>2018-09-28 12:18:54 +0200
committerDrew DeVault <[email protected]>2018-09-28 13:53:01 +0200
commitc9773491207d36d6f5e651adcb7a64c7a015bba3 (patch)
treeed2d195ac03609bdb1b3132d1ef748ad59132e8a /swaylock/pam.c
parent58af0015170204de6d186f0f25cd0b9315d062d7 (diff)
Add support for building swaylock without PAM
This involves setuid'ing swaylock, which then forks and drops perms on the parent process. The child process remains root and listens on a pipe for requests to validate passwords against /etc/shadow.
Diffstat (limited to 'swaylock/pam.c')
-rw-r--r--swaylock/pam.c62
1 files changed, 62 insertions, 0 deletions
diff --git a/swaylock/pam.c b/swaylock/pam.c
new file mode 100644
index 00000000..cac95a85
--- /dev/null
+++ b/swaylock/pam.c
@@ -0,0 +1,62 @@
+#define _XOPEN_SOURCE 500
+#include <pwd.h>
+#include <security/pam_appl.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <wlr/util/log.h>
+#include "swaylock/swaylock.h"
+
+void initialize_pw_backend(void) {
+ // TODO: only call pam_start once. keep the same handle the whole time
+}
+
+static int function_conversation(int num_msg, const struct pam_message **msg,
+ struct pam_response **resp, void *data) {
+ struct swaylock_password *pw = data;
+ /* PAM expects an array of responses, one for each message */
+ struct pam_response *pam_reply = calloc(
+ num_msg, sizeof(struct pam_response));
+ *resp = pam_reply;
+ for (int i = 0; i < num_msg; ++i) {
+ switch (msg[i]->msg_style) {
+ case PAM_PROMPT_ECHO_OFF:
+ case PAM_PROMPT_ECHO_ON:
+ pam_reply[i].resp = strdup(pw->buffer); // PAM clears and frees this
+ break;
+ case PAM_ERROR_MSG:
+ case PAM_TEXT_INFO:
+ break;
+ }
+ }
+ return PAM_SUCCESS;
+}
+
+bool attempt_password(struct swaylock_password *pw) {
+ struct passwd *passwd = getpwuid(getuid());
+ char *username = passwd->pw_name;
+ const struct pam_conv local_conversation = {
+ function_conversation, pw
+ };
+ pam_handle_t *local_auth_handle = NULL;
+ int pam_err;
+ if ((pam_err = pam_start("swaylock", username,
+ &local_conversation, &local_auth_handle)) != PAM_SUCCESS) {
+ wlr_log(WLR_ERROR, "PAM returned error %d", pam_err);
+ }
+ if ((pam_err = pam_authenticate(local_auth_handle, 0)) != PAM_SUCCESS) {
+ wlr_log(WLR_ERROR, "pam_authenticate failed");
+ goto fail;
+ }
+ // TODO: only call pam_end once we succeed at authing. refresh tokens beforehand
+ if ((pam_err = pam_end(local_auth_handle, pam_err)) != PAM_SUCCESS) {
+ wlr_log(WLR_ERROR, "pam_end failed");
+ goto fail;
+ }
+ clear_password_buffer(pw);
+ return true;
+fail:
+ clear_password_buffer(pw);
+ return false;
+}