Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 51 additions & 0 deletions bsdauth.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
#include <pwd.h>
#include <stdlib.h>
#include <stdbool.h>
#include <sys/types.h>
#include <unistd.h>
#include <login_cap.h>
#include <bsd_auth.h>
#include <grp.h>

#include "comm.h"
#include "log.h"
#include "password-buffer.h"
#include "swaylock.h"

void initialize_pw_backend(int argc, char **argv) {
if (!spawn_comm_child()) {
exit(EXIT_FAILURE);
}
}
void run_pw_backend_child(void) {
struct passwd *pwent = getpwuid(getuid());
if (!pwent) {
swaylock_log_errno(LOG_ERROR, "failed to getpwuid");
exit(EXIT_FAILURE);
}
struct group *authg = getgrnam("auth");
if (!authg || !authg->gr_name || !*authg->gr_name) {
exit(EXIT_FAILURE);
}
/* we need setgid(auth) to use auth_userokay() */
if (setgid(authg->gr_gid)) {
exit(EXIT_FAILURE);
}
while (1) {
char *buf;
ssize_t size = read_comm_request(&buf);
if (size < 0) {
exit(EXIT_FAILURE);
} else if (size == 0) {
break;
}
bool success = auth_userokay((char *)pwent->pw_name, NULL, "swaylock", buf);
if (!write_comm_reply(success)) {
exit(EXIT_FAILURE);
}

sleep(2);
}

exit(EXIT_SUCCESS);
}
87 changes: 44 additions & 43 deletions main.c
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
#include <time.h>
#include <unistd.h>
#include <wayland-client.h>
#include <wordexp.h>
#include "background-image.h"
#include "cairo.h"
#include "comm.h"
Expand Down Expand Up @@ -322,23 +321,6 @@ static cairo_surface_t *select_image(struct swaylock_state *state,
return default_image;
}

static char *join_args(char **argv, int argc) {
assert(argc > 0);
int len = 0, i;
for (i = 0; i < argc; ++i) {
len += strlen(argv[i]) + 1;
}
char *res = malloc(len);
len = 0;
for (i = 0; i < argc; ++i) {
strcpy(res + len, argv[i]);
len += strlen(argv[i]);
res[len++] = ' ';
}
res[len - 1] = '\0';
return res;
}

static void load_image(char *arg, struct swaylock_state *state) {
// [[<output>]:]<path>
struct swaylock_image *image = calloc(1, sizeof(struct swaylock_image));
Expand Down Expand Up @@ -375,18 +357,12 @@ static void load_image(char *arg, struct swaylock_state *state) {
// The shell will not expand ~ to the value of $HOME when an output name is
// given. Also, any image paths given in the config file need to have shell
// expansions performed
wordexp_t p;
while (strstr(image->path, " ")) {
image->path = realloc(image->path, strlen(image->path) + 2);
char *ptr = strstr(image->path, " ") + 1;
memmove(ptr + 1, ptr, strlen(ptr) + 1);
*ptr = '\\';
}
if (wordexp(image->path, &p, 0) == 0) {
free(image->path);
image->path = join_args(p.we_wordv, p.we_wordc);
wordfree(&p);
}

// Load the actual image
image->cairo_surface = load_background_image(image->path);
Expand Down Expand Up @@ -956,32 +932,57 @@ static bool file_exists(const char *path) {
return path && access(path, R_OK) != -1;
}

static char *config_path(const char *prefix, const char *config_folder) {
if (!prefix || !prefix[0] || !config_folder || !config_folder[0]) {
return NULL;
}

const char *filename = "config";

size_t size = 3 + strlen(prefix) + strlen(config_folder) + strlen(filename);
char *path = calloc(size, sizeof(char));
snprintf(path, size, "%s/%s/%s", prefix, config_folder, filename);
return path;
}

static char *get_config_path(void) {
static const char *config_paths[] = {
"$HOME/.swaylock/config",
"$XDG_CONFIG_HOME/swaylock/config",
SYSCONFDIR "/swaylock/config",
char *path = NULL;
const char *home = getenv("HOME");
size_t size_fallback = 1 + strlen(home) + strlen("/.config");
char *config_home_fallback = calloc(size_fallback, sizeof(char));
snprintf(config_home_fallback, size_fallback, "%s/.config", home);

const char *config_home = getenv("XDG_CONFIG_HOME");
if (config_home == NULL || config_home[0] == '\0') {
config_home = config_home_fallback;
}

struct config_path {
const char *prefix;
const char *config_folder;
};

char *config_home = getenv("XDG_CONFIG_HOME");
if (!config_home || config_home[0] == '\0') {
config_paths[1] = "$HOME/.config/swaylock/config";
}
struct config_path config_paths[] = {
{ .prefix = home, .config_folder = ".swaylock"},
{ .prefix = config_home, .config_folder = "swaylock"},
{ .prefix = SYSCONFDIR, .config_folder = "swaylock"},
};

wordexp_t p;
char *path;
for (size_t i = 0; i < sizeof(config_paths) / sizeof(char *); ++i) {
if (wordexp(config_paths[i], &p, 0) == 0) {
path = strdup(p.we_wordv[0]);
wordfree(&p);
if (file_exists(path)) {
return path;
}
free(path);
size_t num_config_paths = sizeof(config_paths)/sizeof(config_paths[0]);
for (size_t i = 0; i < num_config_paths; i++) {
path = config_path(config_paths[i].prefix, config_paths[i].config_folder);
if (!path) {
continue;
}
if (file_exists(path)) {
break;
}
free(path);
path = NULL;
}

return NULL;
free(config_home_fallback);
return path;
}

static int load_config(char *path, struct swaylock_state *state,
Expand Down
14 changes: 9 additions & 5 deletions meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ is_freebsd = host_machine.system().startswith('freebsd')
if is_freebsd
add_project_arguments('-D_C11_SOURCE', language: 'c')
endif
is_openbsd = host_machine.system().startswith('openbsd')

wayland_client = dependency('wayland-client', version: '>=1.20.0')
wayland_protos = dependency('wayland-protocols', version: '>=1.25', fallback: 'wayland-protocols')
Expand All @@ -34,9 +35,9 @@ xkbcommon = dependency('xkbcommon')
cairo = dependency('cairo')
gdk_pixbuf = dependency('gdk-pixbuf-2.0', required: get_option('gdk-pixbuf'))
libpam = cc.find_library('pam', required: get_option('pam'))
crypt = cc.find_library('crypt', required: not libpam.found())
crypt = cc.find_library('crypt', required: not libpam.found() and not is_openbsd)
math = cc.find_library('m')
rt = cc.find_library('rt')
rt = cc.find_library('rt', required: not is_openbsd)

git = find_program('git', required: false)
scdoc = find_program('scdoc', required: get_option('man-pages'))
Expand Down Expand Up @@ -86,7 +87,6 @@ dependencies = [
cairo,
gdk_pixbuf,
math,
rt,
xkbcommon,
wayland_client,
]
Expand All @@ -108,13 +108,17 @@ sources = [

if libpam.found()
sources += ['pam.c']
dependencies += [libpam]
dependencies += [libpam, rt]
elif is_openbsd
warning('The swaylock binary must be setgid when compiled with bsd auth')
warning('You must do this manually post-install: chgrp auth /path/to/swaylock ; chmod g+s /path/to/swaylock')
sources += ['bsdauth.c']
Comment on lines +112 to +115
Copy link
Copy Markdown
Contributor

@WhyNotHugo WhyNotHugo Aug 5, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we use login_passwd(8) instead to avoid this requirement?

Running the locker as root likely has funny attack vectors.

E.g.: point WAYLAND_DISPLAY to another user's socket and lock their session.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

swaylock already has a SUID-root mode for the shadow backend. We drop privileges early in the main process.

else
warning('The swaylock binary often needs to be setuid when compiled without libpam')
warning('You must do this manually post-install: chmod a+s /path/to/swaylock')
warning('See the "Without PAM" section of the README for details.')
sources += ['shadow.c']
dependencies += [crypt]
dependencies += [crypt, rt]
endif

swaylock_inc = include_directories('include')
Expand Down
2 changes: 2 additions & 0 deletions render.c
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@
#include "swaylock.h"
#include "log.h"

#ifndef M_PI
#define M_PI 3.14159265358979323846
#endif
const float TYPE_INDICATOR_RANGE = M_PI / 3.0f;

static void set_color_for_state(cairo_t *cairo, struct swaylock_state *state,
Expand Down