|
|
@ -31,7 +31,9 @@ |
|
|
|
#ifdef __OpenBSD__ |
|
|
|
#include <bsd_auth.h> |
|
|
|
#else |
|
|
|
#include <security/pam_appl.h> |
|
|
|
#include <shadow.h> |
|
|
|
#include <grp.h> |
|
|
|
#include <errno.h> |
|
|
|
#endif |
|
|
|
#include <getopt.h> |
|
|
|
#include <string.h> |
|
|
@ -186,7 +188,7 @@ uint32_t last_resolution[2]; |
|
|
|
xcb_window_t win; |
|
|
|
static xcb_cursor_t cursor; |
|
|
|
#ifndef __OpenBSD__ |
|
|
|
static pam_handle_t *pam_handle; |
|
|
|
const char *hash = NULL; |
|
|
|
#endif |
|
|
|
int input_position = 0; |
|
|
|
/* Holds the password you enter (in UTF-8). */ |
|
|
@ -256,6 +258,37 @@ char bar_expr[32] = "0\0"; |
|
|
|
bool bar_bidirectional = false; |
|
|
|
bool bar_reversed = false; |
|
|
|
|
|
|
|
/*
|
|
|
|
* Shamelessly stolen from slock. See LICENSE-slock. |
|
|
|
* This adjusts the process' out of memory score, |
|
|
|
* so it isn't killed by the kernel under any circumstances. |
|
|
|
*/ |
|
|
|
#ifdef __linux__ |
|
|
|
#include <fcntl.h> |
|
|
|
#include <linux/oom.h> |
|
|
|
|
|
|
|
static void |
|
|
|
dontkillme(void) |
|
|
|
{ |
|
|
|
FILE *f; |
|
|
|
const char oomfile[] = "/proc/self/oom_score_adj"; |
|
|
|
|
|
|
|
if (!(f = fopen(oomfile, "w"))) { |
|
|
|
if (errno == ENOENT) |
|
|
|
return; |
|
|
|
errx(EXIT_FAILURE, "fopen %s: %s", oomfile, strerror(errno)); |
|
|
|
} |
|
|
|
fprintf(f, "%d", OOM_SCORE_ADJ_MIN); |
|
|
|
if (fclose(f)) { |
|
|
|
if (errno == EACCES) |
|
|
|
errx(EXIT_FAILURE, "unable to disable OOM killer. " |
|
|
|
"Make sure to suid or sgid i3lock."); |
|
|
|
else |
|
|
|
errx(EXIT_FAILURE, "fclose %s: %s", oomfile, strerror(errno)); |
|
|
|
} |
|
|
|
} |
|
|
|
#endif |
|
|
|
|
|
|
|
/* isutf, u8_dec © 2005 Jeff Bezanson, public domain */ |
|
|
|
#define isutf(c) (((c)&0xC0) != 0x80) |
|
|
|
|
|
|
@ -511,16 +544,16 @@ static void input_done(void) { |
|
|
|
return; |
|
|
|
} |
|
|
|
#else |
|
|
|
if (pam_authenticate(pam_handle, 0) == PAM_SUCCESS) { |
|
|
|
DEBUG("successfully authenticated\n"); |
|
|
|
clear_password_memory(); |
|
|
|
/*
|
|
|
|
* Shamelessly stolen from slock. See LICENSE-slock. |
|
|
|
*/ |
|
|
|
char *inputhash; |
|
|
|
|
|
|
|
/* PAM credentials should be refreshed, this will for example update any kerberos tickets.
|
|
|
|
* Related to credentials pam_end() needs to be called to cleanup any temporary |
|
|
|
* credentials like kerberos /tmp/krb5cc_pam_* files which may of been left behind if the |
|
|
|
* refresh of the credentials failed. */ |
|
|
|
pam_setcred(pam_handle, PAM_REFRESH_CRED); |
|
|
|
pam_end(pam_handle, PAM_SUCCESS); |
|
|
|
if (!(inputhash = crypt(password, hash))) |
|
|
|
fprintf(stderr, "crypt: %s", strerror(errno)); |
|
|
|
else if (!strcmp(inputhash, hash)) { |
|
|
|
DEBUG("successfully authenticated"); |
|
|
|
clear_password_memory(); |
|
|
|
|
|
|
|
ev_break(EV_DEFAULT, EVBREAK_ALL); |
|
|
|
return; |
|
|
@ -915,39 +948,6 @@ static bool verify_png_image(const char *image_path) { |
|
|
|
return true; |
|
|
|
} |
|
|
|
|
|
|
|
#ifndef __OpenBSD__ |
|
|
|
/*
|
|
|
|
* Callback function for PAM. We only react on password request callbacks. |
|
|
|
* |
|
|
|
*/ |
|
|
|
static int conv_callback(int num_msg, const struct pam_message **msg, |
|
|
|
struct pam_response **resp, void *appdata_ptr) { |
|
|
|
if (num_msg == 0) |
|
|
|
return 1; |
|
|
|
|
|
|
|
/* PAM expects an array of responses, one for each message */ |
|
|
|
if ((*resp = calloc(num_msg, sizeof(struct pam_response))) == NULL) { |
|
|
|
perror("calloc"); |
|
|
|
return 1; |
|
|
|
} |
|
|
|
|
|
|
|
for (int c = 0; c < num_msg; c++) { |
|
|
|
if (msg[c]->msg_style != PAM_PROMPT_ECHO_OFF && |
|
|
|
msg[c]->msg_style != PAM_PROMPT_ECHO_ON) |
|
|
|
continue; |
|
|
|
|
|
|
|
/* return code is currently not used but should be set to zero */ |
|
|
|
resp[c]->resp_retcode = 0; |
|
|
|
if ((resp[c]->resp = strdup(password)) == NULL) { |
|
|
|
perror("strdup"); |
|
|
|
return 1; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
return 0; |
|
|
|
} |
|
|
|
#endif |
|
|
|
|
|
|
|
/*
|
|
|
|
* This callback is only a dummy, see xcb_prepare_cb and xcb_check_cb. |
|
|
|
* See also man libev(3): "ev_prepare" and "ev_check" - customise your event loop |
|
|
@ -1055,12 +1055,13 @@ static void xcb_check_cb(EV_P_ ev_check *w, int revents) { |
|
|
|
* |
|
|
|
*/ |
|
|
|
static void raise_loop(xcb_window_t window) { |
|
|
|
xcb_connection_t *conn; |
|
|
|
xcb_generic_event_t *event; |
|
|
|
int screens; |
|
|
|
|
|
|
|
if (xcb_connection_has_error((conn = xcb_connect(NULL, &screens))) > 0) |
|
|
|
#ifdef __OpenBSD__ |
|
|
|
xcb_connection_t *conn; |
|
|
|
if (xcb_connection_has_error((conn = xcb_connect(NULL, NULL))) > 0) |
|
|
|
errx(EXIT_FAILURE, "Cannot open display\n"); |
|
|
|
#endif |
|
|
|
|
|
|
|
/* We need to know about the window being obscured or getting destroyed. */ |
|
|
|
xcb_change_window_attributes(conn, window, XCB_CW_EVENT_MASK, |
|
|
@ -1176,8 +1177,11 @@ int main(int argc, char *argv[]) { |
|
|
|
char *username; |
|
|
|
char *image_path = NULL; |
|
|
|
#ifndef __OpenBSD__ |
|
|
|
int ret; |
|
|
|
struct pam_conv conv = {conv_callback, NULL}; |
|
|
|
struct passwd *pwd; |
|
|
|
struct group *grp; |
|
|
|
uid_t duid; |
|
|
|
gid_t dgid; |
|
|
|
xcb_connection_t *raise_conn; |
|
|
|
#endif |
|
|
|
int curs_choice = CURS_NONE; |
|
|
|
int o; |
|
|
@ -1312,6 +1316,65 @@ int main(int argc, char *argv[]) { |
|
|
|
if ((username = pw->pw_name) == NULL) |
|
|
|
errx(EXIT_FAILURE, "pw->pw_name is NULL.\n"); |
|
|
|
|
|
|
|
#ifndef __OpenBSD__ |
|
|
|
/*
|
|
|
|
* Shamelessly stolen from slock. See LICENSE-slock. |
|
|
|
* |
|
|
|
* Slock has code to make it run as nobody:nogroup, which has the added |
|
|
|
* security that the locker can only be killed by root. |
|
|
|
* It causes problems with the xcb_connect in raise_loop, and the main |
|
|
|
* xcb_connect, however. |
|
|
|
* Because of that, both xcb_connect are ran as root, before dropping the |
|
|
|
* privileges to the user, much like is being done with XOpenDisplay |
|
|
|
* in slock. |
|
|
|
* I'm unsure of any security implications that may have, as it seems to |
|
|
|
* run fine, otherwise. |
|
|
|
* Please contact me if it's something I _really_ shouldn't do. |
|
|
|
*/ |
|
|
|
|
|
|
|
/* If the nobody:nogroup don't exist, just use the password's user */ |
|
|
|
duid = pw->pw_uid; |
|
|
|
if ((pwd = getpwnam("nobody"))) |
|
|
|
duid = pwd->pw_uid; |
|
|
|
dgid = pw->pw_gid; |
|
|
|
if ((grp = getgrnam("nogroup"))) |
|
|
|
dgid = grp->gr_gid; |
|
|
|
|
|
|
|
#ifdef __linux__ |
|
|
|
dontkillme(); |
|
|
|
#endif |
|
|
|
|
|
|
|
hash = pw->pw_passwd; |
|
|
|
|
|
|
|
if (!strcmp(hash, "x")) { |
|
|
|
struct spwd *sp; |
|
|
|
if (!(sp = getspnam(pw->pw_name))) |
|
|
|
errx(EXIT_FAILURE, "getspnam: cannot retrieve shadow entry. " |
|
|
|
"Make sure to suid or sgid i3lock."); |
|
|
|
hash = sp->sp_pwdp; |
|
|
|
} |
|
|
|
|
|
|
|
errno = 0; |
|
|
|
if (!crypt("", hash)) |
|
|
|
errx(EXIT_FAILURE, "crypt: %s", strerror(errno)); |
|
|
|
|
|
|
|
/* Create the necessary connections before dropping privileges */ |
|
|
|
if ((conn = xcb_connect(NULL, NULL)) == NULL || |
|
|
|
xcb_connection_has_error(conn)) |
|
|
|
errx(EXIT_FAILURE, "Could not connect to X11, maybe you need to set DISPLAY?"); |
|
|
|
if ((raise_conn = xcb_connect(NULL, NULL)) == NULL || |
|
|
|
xcb_connection_has_error(raise_conn)) |
|
|
|
errx(EXIT_FAILURE, "Cannot open display\n"); |
|
|
|
|
|
|
|
/* drop privileges */ |
|
|
|
if (setgroups(0, NULL) < 0) |
|
|
|
errx(EXIT_FAILURE, "setgroups: %s", strerror(errno)); |
|
|
|
if (setgid(dgid) < 0) |
|
|
|
errx(EXIT_FAILURE, "setgid: %s", strerror(errno)); |
|
|
|
if (setuid(duid) < 0) |
|
|
|
errx(EXIT_FAILURE, "setuid: %s", strerror(errno)); |
|
|
|
#endif |
|
|
|
|
|
|
|
char *optstring = "hvnbdc:p:ui:teI:frsS:kB:m"; |
|
|
|
char *arg = NULL; |
|
|
|
int opt = 0; |
|
|
@ -1838,15 +1901,6 @@ 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). |
|
|
@ -1860,12 +1914,12 @@ int main(int argc, char *argv[]) { |
|
|
|
err(EXIT_FAILURE, "Could not lock page in memory, check RLIMIT_MEMLOCK"); |
|
|
|
#endif |
|
|
|
|
|
|
|
#ifdef __OpenBSD__ |
|
|
|
/* Double checking that connection is good and operatable with xcb */ |
|
|
|
int screennr; |
|
|
|
if ((conn = xcb_connect(NULL, &screennr)) == NULL || |
|
|
|
if ((conn = xcb_connect(NULL, NULL)) == NULL || |
|
|
|
xcb_connection_has_error(conn)) |
|
|
|
errx(EXIT_FAILURE, "Could not connect to X11, maybe you need to set DISPLAY?"); |
|
|
|
|
|
|
|
#endif |
|
|
|
|
|
|
|
|
|
|
|
if (xkb_x11_setup_xkb_extension(conn, |
|
|
@ -2044,10 +2098,16 @@ int main(int argc, char *argv[]) { |
|
|
|
if (pid == 0) { |
|
|
|
/* Child */ |
|
|
|
close(xcb_get_file_descriptor(conn)); |
|
|
|
#ifndef __OpenBSD__ |
|
|
|
conn = raise_conn; |
|
|
|
#endif |
|
|
|
maybe_close_sleep_lock_fd(); |
|
|
|
raise_loop(win); |
|
|
|
exit(EXIT_SUCCESS); |
|
|
|
} |
|
|
|
#ifndef __OpenBSD__ |
|
|
|
close(xcb_get_file_descriptor(raise_conn)); |
|
|
|
#endif |
|
|
|
|
|
|
|
/* Load the keymap again to sync the current modifier state. Since we first
|
|
|
|
* loaded the keymap, there might have been changes, but starting from now, |
|
|
|