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
pull/123/head
Jasper Lievisse Adriaanse 2017-04-15 14:41:32 +02:00
parent 15973d1f52
commit 68fc2e8b5f
4 changed files with 49 additions and 16 deletions

View File

@ -1,4 +1,5 @@
TOPDIR=$(shell pwd) TOPDIR=$(shell pwd)
UNAME=$(shell uname)
INSTALL=install INSTALL=install
PREFIX=/usr PREFIX=/usr
@ -14,13 +15,16 @@ CFLAGS += -std=c99
CFLAGS += -pipe CFLAGS += -pipe
CFLAGS += -Wall CFLAGS += -Wall
CPPFLAGS += -D_GNU_SOURCE 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) 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 += $(shell $(PKG_CONFIG) --libs cairo xcb-composite xcb-xinerama xcb-atom xcb-image xcb-xkb xkbcommon xkbcommon-x11)
LIBS += -lpam
LIBS += -lev LIBS += -lev
LIBS += -lm LIBS += -lm
# OpenBSD lacks PAM, use bsd_auth(3) instead.
ifneq ($(UNAME),OpenBSD)
LIBS += -lpam
endif
FILES:=$(wildcard *.c) FILES:=$(wildcard *.c)
FILES:=$(FILES:.c=.o) FILES:=$(FILES:.c=.o)

View File

@ -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. - You can specify whether i3lock should bell upon a wrong password.
- i3lock uses PAM and therefore is compatible with LDAP etc. - i3lock uses PAM and therefore is compatible with LDAP etc.
On OpenBSD i3lock uses the bsd_auth(3) framework.
Requirements Requirements
------------ ------------
@ -37,6 +38,9 @@ Running i3lock
Simply invoke the 'i3lock' command. To get out of it, enter your password and Simply invoke the 'i3lock' command. To get out of it, enter your password and
press enter. 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 Upstream
-------- --------
Please submit pull requests to https://github.com/i3/i3lock Please submit pull requests to https://github.com/i3/i3lock

View File

@ -18,7 +18,9 @@
#include <xcb/xkb.h> #include <xcb/xkb.h>
#include <err.h> #include <err.h>
#include <assert.h> #include <assert.h>
#ifdef USE_PAM #ifdef __OpenBSD__
#include <bsd_auth.h>
#else
#include <security/pam_appl.h> #include <security/pam_appl.h>
#endif #endif
#include <getopt.h> #include <getopt.h>
@ -30,6 +32,9 @@
#include <xkbcommon/xkbcommon-x11.h> #include <xkbcommon/xkbcommon-x11.h>
#include <cairo.h> #include <cairo.h>
#include <cairo/cairo-xcb.h> #include <cairo/cairo-xcb.h>
#ifdef __OpenBSD__
#include <strings.h> /* explicit_bzero(3) */
#endif
#include "i3lock.h" #include "i3lock.h"
#include "xcb.h" #include "xcb.h"
@ -51,7 +56,7 @@ char color[7] = "ffffff";
uint32_t last_resolution[2]; uint32_t last_resolution[2];
xcb_window_t win; xcb_window_t win;
static xcb_cursor_t cursor; static xcb_cursor_t cursor;
#ifdef USE_PAM #ifndef __OpenBSD__
static pam_handle_t *pam_handle; static pam_handle_t *pam_handle;
#endif #endif
int input_position = 0; int input_position = 0;
@ -162,6 +167,11 @@ static bool load_compose_table(const char *locale) {
* *
*/ */
static void clear_password_memory(void) { 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 /* A volatile pointer to the password buffer to prevent the compiler from
* optimizing this out. */ * optimizing this out. */
volatile char *vpassword = password; 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' * compiler from optimizing the calls away, since the value of 'beep'
* is not known at compile-time. */ * is not known at compile-time. */
vpassword[c] = c + (int)beep; vpassword[c] = c + (int)beep;
#endif
} }
ev_timer *start_timer(ev_timer *timer_obj, ev_tstamp timeout, ev_callback_t callback) { 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; unlock_state = STATE_STARTED;
redraw_screen(); 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) { if (pam_authenticate(pam_handle, 0) == PAM_SUCCESS) {
DEBUG("successfully authenticated\n"); DEBUG("successfully authenticated\n");
clear_password_memory(); clear_password_memory();
@ -603,7 +626,7 @@ void handle_screen_resize(void) {
redraw_screen(); redraw_screen();
} }
#ifdef USE_PAM #ifndef __OpenBSD__
/* /*
* Callback function for PAM. We only react on password request callbacks. * Callback function for PAM. We only react on password request callbacks.
* *
@ -790,7 +813,7 @@ int main(int argc, char *argv[]) {
struct passwd *pw; struct passwd *pw;
char *username; char *username;
char *image_path = NULL; char *image_path = NULL;
#ifdef USE_PAM #ifndef __OpenBSD__
int ret; int ret;
struct pam_conv conv = {conv_callback, NULL}; struct pam_conv conv = {conv_callback, NULL};
#endif #endif
@ -887,7 +910,7 @@ int main(int argc, char *argv[]) {
* the unlock indicator upon keypresses. */ * the unlock indicator upon keypresses. */
srand(time(NULL)); srand(time(NULL));
#ifdef USE_PAM #ifndef __OpenBSD__
/* Initialize PAM */ /* Initialize PAM */
if ((ret = pam_start("i3lock", username, &conv, &pam_handle)) != PAM_SUCCESS) if ((ret = pam_start("i3lock", username, &conv, &pam_handle)) != PAM_SUCCESS)
errx(EXIT_FAILURE, "PAM: %s", pam_strerror(pam_handle, ret)); 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)); errx(EXIT_FAILURE, "PAM: %s", pam_strerror(pam_handle, ret));
#endif #endif
/* Using mlock() as non-super-user seems only possible in Linux. Users of other /* Using mlock() as non-super-user seems only possible in Linux and OpenBSD.
* operating systems should use encrypted swap/no swap (or remove the ifdef and * Users of other operating systems should use encrypted swap/no swap
* run i3lock as super-user). */ * (or remove the ifdef and run i3lock as super-user).
#if defined(__linux__) * 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 dont want it to /* Lock the area where we store the password in memory, we dont want it to
* be swapped to disk. Since Linux 2.6.9, this does not require any * be swapped to disk. Since Linux 2.6.9, this does not require any
* privileges, just enough bytes in the RLIMIT_MEMLOCK limit. */ * privileges, just enough bytes in the RLIMIT_MEMLOCK limit. */

View File

@ -11,10 +11,10 @@ typedef enum {
} unlock_state_t; } unlock_state_t;
typedef enum { typedef enum {
STATE_AUTH_IDLE = 0, /* no authenticator interaction at the moment */ STATE_AUTH_IDLE = 0, /* no authenticator interaction at the moment */
STATE_AUTH_VERIFY = 1, /* currently verifying the password via authenticator */ STATE_AUTH_VERIFY = 1, /* currently verifying the password via authenticator */
STATE_AUTH_LOCK = 2, /* currently locking the screen */ STATE_AUTH_LOCK = 2, /* currently locking the screen */
STATE_AUTH_WRONG = 3, /* the password was wrong */ STATE_AUTH_WRONG = 3, /* the password was wrong */
STATE_I3LOCK_LOCK_FAILED = 4 /* i3lock failed to load */ STATE_I3LOCK_LOCK_FAILED = 4 /* i3lock failed to load */
} auth_state_t; } auth_state_t;