@ -18,7 +18,11 @@
# include <xcb/xkb.h>
# include <err.h>
# include <assert.h>
# ifdef __OpenBSD__
# include <bsd_auth.h>
# else
# include <security/pam_appl.h>
# endif
# include <getopt.h>
# include <string.h>
# include <ev.h>
@ -30,6 +34,9 @@
# include <xkbcommon/xkbcommon-x11.h>
# include <cairo.h>
# include <cairo/cairo-xcb.h>
# ifdef __OpenBSD__
# include <strings.h> /* explicit_bzero(3) */
# endif
# include "i3lock.h"
# include "xcb.h"
@ -84,7 +91,9 @@ int blur_sigma = 5;
uint32_t last_resolution [ 2 ] ;
xcb_window_t win ;
static xcb_cursor_t cursor ;
# ifndef __OpenBSD__
static pam_handle_t * pam_handle ;
# endif
int input_position = 0 ;
/* Holds the password you enter (in UTF-8). */
static char password [ 512 ] ;
@ -94,11 +103,11 @@ bool unlock_indicator = true;
char * modifier_string = NULL ;
static bool dont_fork = false ;
struct ev_loop * main_loop ;
static struct ev_timer * clear_pam _wrong_timeout ;
static struct ev_timer * clear_auth _wrong_timeout ;
static struct ev_timer * clear_indicator_timeout ;
static struct ev_timer * discard_passwd_timeout ;
extern unlock_state_t unlock_state ;
extern pam_state_t pam _state;
extern auth_state_t auth _state;
int failed_attempts = 0 ;
bool show_failed_attempts = false ;
bool retry_verification = false ;
@ -197,6 +206,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 ;
@ -206,6 +220,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 ) {
@ -245,13 +260,13 @@ static void finish_input(void) {
}
/*
* Resets pam_state to STATE_PAM _IDLE 2 seconds after an unsuccessful
* Resets auth_state to STATE_AUTH _IDLE 2 seconds after an unsuccessful
* authentication event .
*
*/
static void clear_pam _wrong ( EV_P_ ev_timer * w , int revents ) {
DEBUG ( " clearing pam wrong \n " ) ;
pam_state = STATE_PAM _IDLE;
static void clear_auth _wrong ( EV_P_ ev_timer * w , int revents ) {
DEBUG ( " clearing auth wrong \n " ) ;
auth_state = STATE_AUTH _IDLE;
redraw_screen ( ) ;
/* Clear modifier string. */
@ -261,9 +276,9 @@ static void clear_pam_wrong(EV_P_ ev_timer *w, int revents) {
}
/* Now free this timeout. */
STOP_TIMER ( clear_pam _wrong_timeout ) ;
STOP_TIMER ( clear_auth _wrong_timeout ) ;
/* retry with input done during pam verification */
/* retry with input done during auth verification */
if ( retry_verification ) {
retry_verification = false ;
finish_input ( ) ;
@ -287,11 +302,24 @@ static void discard_passwd_cb(EV_P_ ev_timer *w, int revents) {
}
static void input_done ( void ) {
STOP_TIMER ( clear_pam _wrong_timeout ) ;
pam_state = STATE_PAM _VERIFY;
STOP_TIMER ( clear_auth _wrong_timeout ) ;
auth_state = STATE_AUTH _VERIFY;
unlock_state = STATE_STARTED ;
redraw_screen ( ) ;
# 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 ( ) ;
@ -305,12 +333,13 @@ static void input_done(void) {
exit ( 0 ) ;
}
# endif
if ( debug_mode )
fprintf ( stderr , " Authentication failure \n " ) ;
/* Get state of Caps and Num lock modifiers, to be displayed in
* STATE_PAM _WRONG state */
* STATE_AUTH _WRONG state */
xkb_mod_index_t idx , num_mods ;
const char * mod_name ;
@ -344,7 +373,7 @@ static void input_done(void) {
}
}
pam_state = STATE_PAM _WRONG;
auth_state = STATE_AUTH _WRONG;
failed_attempts + = 1 ;
clear_input ( ) ;
if ( unlock_indicator )
@ -353,7 +382,7 @@ static void input_done(void) {
/* Clear this state after 2 seconds (unless the user enters another
* password during that time ) . */
ev_now_update ( main_loop ) ;
START_TIMER ( clear_pam _wrong_timeout , TSTAMP_N_SECS ( 2 ) , clear_pam _wrong ) ;
START_TIMER ( clear_auth _wrong_timeout , TSTAMP_N_SECS ( 2 ) , clear_auth _wrong ) ;
/* Cancel the clear_indicator_timeout, it would hide the unlock indicator
* too early . */
@ -438,7 +467,7 @@ static void handle_key_press(xcb_key_press_event_t *event) {
if ( ( ksym = = XKB_KEY_j | | ksym = = XKB_KEY_m ) & & ! ctrl )
break ;
if ( pam_state = = STATE_PAM _WRONG) {
if ( auth_state = = STATE_AUTH _WRONG) {
retry_verification = true ;
return ;
}
@ -642,6 +671,7 @@ void handle_screen_resize(void) {
redraw_screen ( ) ;
}
# ifndef __OpenBSD__
/*
* Callback function for PAM . We only react on password request callbacks .
*
@ -672,6 +702,7 @@ static int conv_callback(int num_msg, const struct pam_message **msg,
return 0 ;
}
# endif
/*
* This callback is only a dummy , see xcb_prepare_cb and xcb_check_cb .
@ -770,7 +801,7 @@ static void xcb_check_cb(EV_P_ ev_check *w, int revents) {
/*
* This function is called from a fork ( ) ed child and will raise the i3lock
* window when the window is obscured , even when the main i3lock process is
* blocked due to PAM .
* blocked due to the authentication backend .
*
*/
static void raise_loop ( xcb_window_t window ) {
@ -827,8 +858,10 @@ int main(int argc, char *argv[]) {
struct passwd * pw ;
char * username ;
char * image_path = NULL ;
# ifndef __OpenBSD__
int ret ;
struct pam_conv conv = { conv_callback , NULL } ;
# endif
int curs_choice = CURS_NONE ;
int o ;
int optind = 0 ;
@ -1095,17 +1128,21 @@ int main(int argc, char *argv[]) {
* the unlock indicator upon keypresses . */
srand ( time ( NULL ) ) ;
# 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 ) ) ;
if ( ( ret = pam_set_item ( pam_handle , PAM_TTY , getenv ( " DISPLAY " ) ) ) ! = PAM_SUCCESS )
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 . */
@ -1227,7 +1264,7 @@ int main(int argc, char *argv[]) {
cursor = create_cursor ( conn , screen , win , curs_choice ) ;
/* Display the "locking…" message while trying to grab the pointer/keyboard. */
pam_state = STATE_PAM _LOCK;
auth_state = STATE_AUTH _LOCK;
grab_pointer_and_keyboard ( conn , screen , cursor ) ;
pid_t pid = fork ( ) ;
@ -1254,7 +1291,7 @@ int main(int argc, char *argv[]) {
errx ( EXIT_FAILURE , " Could not initialize libev. Bad LIBEV_FLAGS? \n " ) ;
/* Explicitly call the screen redraw in case "locking…" message was displayed */
pam_state = STATE_PAM _IDLE;
auth_state = STATE_AUTH _IDLE;
redraw_screen ( ) ;
struct ev_io * xcb_watcher = calloc ( sizeof ( struct ev_io ) , 1 ) ;