diff options
Diffstat (limited to 'swaylock/shadow.c')
-rw-r--r-- | swaylock/shadow.c | 128 |
1 files changed, 128 insertions, 0 deletions
diff --git a/swaylock/shadow.c b/swaylock/shadow.c new file mode 100644 index 00000000..1f10514c --- /dev/null +++ b/swaylock/shadow.c @@ -0,0 +1,128 @@ +#define _XOPEN_SOURCE +#include <pwd.h> +#include <shadow.h> +#include <stdbool.h> +#include <sys/types.h> +#include <unistd.h> +#include <wlr/util/log.h> +#include "swaylock/swaylock.h" + +static int comm[2][2]; + +void run_child(void) { + /* This code runs as root */ + struct passwd *pwent = getpwuid(getuid()); + if (!pwent) { + wlr_log_errno(WLR_ERROR, "failed to getpwuid"); + exit(EXIT_FAILURE); + } + char *encpw = pwent->pw_passwd; + if (strcmp(encpw, "x") == 0) { + struct spwd *swent = getspnam(pwent->pw_name); + if (!swent) { + wlr_log_errno(WLR_ERROR, "failed to getspnam"); + exit(EXIT_FAILURE); + } + encpw = swent->sp_pwdp; + } + wlr_log(WLR_DEBUG, "prepared to authorize user %s", pwent->pw_name); + + size_t size; + char *buf; + while (1) { + ssize_t amt; + amt = read(comm[0][0], &size, sizeof(size)); + if (amt == 0) { + break; + } else if (amt < 0) { + wlr_log_errno(WLR_ERROR, "read pw request"); + } + wlr_log(WLR_DEBUG, "received pw check request"); + buf = malloc(size); + if (!buf) { + wlr_log_errno(WLR_ERROR, "failed to malloc pw buffer"); + exit(EXIT_FAILURE); + } + size_t offs = 0; + do { + amt = read(comm[0][0], &buf[offs], size - offs); + if (amt <= 0) { + wlr_log_errno(WLR_ERROR, "failed to read pw"); + exit(EXIT_FAILURE); + } + offs += (size_t)amt; + } while (offs < size); + bool result = false; + char *c = crypt(buf, encpw); + if (c == NULL) { + wlr_log_errno(WLR_ERROR, "crypt"); + } + result = strcmp(c, encpw) == 0; + if (write(comm[1][1], &result, sizeof(result)) != sizeof(result)) { + wlr_log_errno(WLR_ERROR, "failed to write pw check result"); + exit(EXIT_FAILURE); + } + free(buf); + } + exit(EXIT_SUCCESS); +} + +void initialize_pw_backend(void) { + if (geteuid() != 0) { + wlr_log(WLR_ERROR, "swaylock needs to be setuid to read /etc/shadow"); + exit(EXIT_FAILURE); + } + if (pipe(comm[0]) != 0) { + wlr_log_errno(WLR_ERROR, "failed to create pipe"); + exit(EXIT_FAILURE); + } + if (pipe(comm[1]) != 0) { + wlr_log_errno(WLR_ERROR, "failed to create pipe"); + exit(EXIT_FAILURE); + } + pid_t child = fork(); + if (child == 0) { + close(comm[0][1]); + close(comm[1][0]); + run_child(); + } else if (child < 0) { + wlr_log_errno(WLR_ERROR, "failed to fork"); + exit(EXIT_FAILURE); + } + close(comm[0][0]); + close(comm[1][1]); + if (setgid(getgid()) != 0) { + wlr_log_errno(WLR_ERROR, "Unable to drop root"); + exit(EXIT_FAILURE); + } + if (setuid(getuid()) != 0) { + wlr_log_errno(WLR_ERROR, "Unable to drop root"); + exit(EXIT_FAILURE); + } +} + +bool attempt_password(struct swaylock_password *pw) { + bool result = false; + size_t len = pw->len + 1; + size_t offs = 0; + if (write(comm[0][1], &len, sizeof(len)) < 0) { + wlr_log_errno(WLR_ERROR, "Failed to request pw check"); + goto ret; + } + do { + ssize_t amt = write(comm[0][1], &pw->buffer[offs], len - offs); + if (amt < 0) { + wlr_log_errno(WLR_ERROR, "Failed to write pw buffer"); + goto ret; + } + offs += amt; + } while (offs < len); + if (read(comm[1][0], &result, sizeof(result)) != sizeof(result)) { + wlr_log_errno(WLR_ERROR, "Failed to read pw result"); + goto ret; + } + wlr_log(WLR_DEBUG, "pw result: %d", result); +ret: + clear_password_buffer(pw); + return result; +} |