From 68fc2e8b5f8bcd203598e6da2a98d5aa7359af1e Mon Sep 17 00:00:00 2001 From: Jasper Lievisse Adriaanse Date: Sat, 15 Apr 2017 14:41:32 +0200 Subject: [PATCH] Use bsd_auth(3) instead of PAM on OpenBSD Also apply two security measures for OpenBSD: - use explicit_bzero(3) - mlock(2) works for non-root users too --- Makefile | 8 ++++++-- README.md | 4 ++++ i3lock.c | 45 +++++++++++++++++++++++++++++++++++---------- unlock_indicator.h | 8 ++++---- 4 files changed, 49 insertions(+), 16 deletions(-) diff --git a/Makefile b/Makefile index b7eae33..3acb272 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,5 @@ TOPDIR=$(shell pwd) +UNAME=$(shell uname) INSTALL=install PREFIX=/usr @@ -14,13 +15,16 @@ CFLAGS += -std=c99 CFLAGS += -pipe CFLAGS += -Wall CPPFLAGS += -D_GNU_SOURCE -CPPFLAGS += -DUSE_PAM CFLAGS += $(shell $(PKG_CONFIG) --cflags cairo xcb-composite xcb-xinerama xcb-atom xcb-image xcb-xkb xkbcommon xkbcommon-x11) LIBS += $(shell $(PKG_CONFIG) --libs cairo xcb-composite xcb-xinerama xcb-atom xcb-image xcb-xkb xkbcommon xkbcommon-x11) -LIBS += -lpam LIBS += -lev LIBS += -lm +# OpenBSD lacks PAM, use bsd_auth(3) instead. +ifneq ($(UNAME),OpenBSD) + LIBS += -lpam +endif + FILES:=$(wildcard *.c) FILES:=$(FILES:.c=.o) diff --git a/README.md b/README.md index fcecbfa..fc486b2 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,7 @@ Many little improvements have been made to i3lock over time: - You can specify whether i3lock should bell upon a wrong password. - i3lock uses PAM and therefore is compatible with LDAP etc. + On OpenBSD i3lock uses the bsd_auth(3) framework. Requirements ------------ @@ -37,6 +38,9 @@ Running i3lock Simply invoke the 'i3lock' command. To get out of it, enter your password and press enter. +On OpenBSD the `i3lock` binary needs to be setgid `auth` to call the +authentication helpers, e.g. `/usr/libexec/auth/login_passwd`. + Upstream -------- Please submit pull requests to https://github.com/i3/i3lock diff --git a/i3lock.c b/i3lock.c index 26c5b9b..01f0436 100644 --- a/i3lock.c +++ b/i3lock.c @@ -18,7 +18,9 @@ #include #include #include -#ifdef USE_PAM +#ifdef __OpenBSD__ +#include +#else #include #endif #include @@ -30,6 +32,9 @@ #include #include #include +#ifdef __OpenBSD__ +#include /* explicit_bzero(3) */ +#endif #include "i3lock.h" #include "xcb.h" @@ -51,7 +56,7 @@ char color[7] = "ffffff"; uint32_t last_resolution[2]; xcb_window_t win; static xcb_cursor_t cursor; -#ifdef USE_PAM +#ifndef __OpenBSD__ static pam_handle_t *pam_handle; #endif int input_position = 0; @@ -162,6 +167,11 @@ static bool load_compose_table(const char *locale) { * */ static void clear_password_memory(void) { +#ifdef __OpenBSD__ + /* Use explicit_bzero(3) which was explicitly designed not to be + * optimized out by the compiler. */ + explicit_bzero(password, strlen(password)); +#else /* A volatile pointer to the password buffer to prevent the compiler from * optimizing this out. */ volatile char *vpassword = password; @@ -171,6 +181,7 @@ static void clear_password_memory(void) { * compiler from optimizing the calls away, since the value of 'beep' * is not known at compile-time. */ vpassword[c] = c + (int)beep; +#endif } ev_timer *start_timer(ev_timer *timer_obj, ev_tstamp timeout, ev_callback_t callback) { @@ -257,7 +268,19 @@ static void input_done(void) { unlock_state = STATE_STARTED; redraw_screen(); -#ifdef USE_PAM +#ifdef __OpenBSD__ + struct passwd *pw; + + if (!(pw = getpwuid(getuid()))) + errx(1, "unknown uid %u.", getuid()); + + if (auth_userokay(pw->pw_name, NULL, NULL, password) != 0) { + DEBUG("successfully authenticated\n"); + clear_password_memory(); + + exit(0); + } +#else if (pam_authenticate(pam_handle, 0) == PAM_SUCCESS) { DEBUG("successfully authenticated\n"); clear_password_memory(); @@ -603,7 +626,7 @@ void handle_screen_resize(void) { redraw_screen(); } -#ifdef USE_PAM +#ifndef __OpenBSD__ /* * Callback function for PAM. We only react on password request callbacks. * @@ -790,7 +813,7 @@ int main(int argc, char *argv[]) { struct passwd *pw; char *username; char *image_path = NULL; -#ifdef USE_PAM +#ifndef __OpenBSD__ int ret; struct pam_conv conv = {conv_callback, NULL}; #endif @@ -887,7 +910,7 @@ int main(int argc, char *argv[]) { * the unlock indicator upon keypresses. */ srand(time(NULL)); -#ifdef USE_PAM +#ifndef __OpenBSD__ /* Initialize PAM */ if ((ret = pam_start("i3lock", username, &conv, &pam_handle)) != PAM_SUCCESS) errx(EXIT_FAILURE, "PAM: %s", pam_strerror(pam_handle, ret)); @@ -896,10 +919,12 @@ int main(int argc, char *argv[]) { errx(EXIT_FAILURE, "PAM: %s", pam_strerror(pam_handle, ret)); #endif -/* Using mlock() as non-super-user seems only possible in Linux. Users of other - * operating systems should use encrypted swap/no swap (or remove the ifdef and - * run i3lock as super-user). */ -#if defined(__linux__) +/* Using mlock() as non-super-user seems only possible in Linux and OpenBSD. + * Users of other operating systems should use encrypted swap/no swap + * (or remove the ifdef and run i3lock as super-user). + * NB: Alas, swap is encrypted by default on OpenBSD so swapping out + * is not necessarily an issue. */ +#if defined(__linux__) || defined(__OpenBSD__) /* Lock the area where we store the password in memory, we don’t want it to * be swapped to disk. Since Linux 2.6.9, this does not require any * privileges, just enough bytes in the RLIMIT_MEMLOCK limit. */ diff --git a/unlock_indicator.h b/unlock_indicator.h index e4a8d8e..2321620 100644 --- a/unlock_indicator.h +++ b/unlock_indicator.h @@ -11,10 +11,10 @@ typedef enum { } unlock_state_t; typedef enum { - STATE_AUTH_IDLE = 0, /* no authenticator interaction at the moment */ - STATE_AUTH_VERIFY = 1, /* currently verifying the password via authenticator */ - STATE_AUTH_LOCK = 2, /* currently locking the screen */ - STATE_AUTH_WRONG = 3, /* the password was wrong */ + STATE_AUTH_IDLE = 0, /* no authenticator interaction at the moment */ + STATE_AUTH_VERIFY = 1, /* currently verifying the password via authenticator */ + STATE_AUTH_LOCK = 2, /* currently locking the screen */ + STATE_AUTH_WRONG = 3, /* the password was wrong */ STATE_I3LOCK_LOCK_FAILED = 4 /* i3lock failed to load */ } auth_state_t;