+++ LICENSE-slock @@ -0,0 +1,24 @@ +MIT/X Consortium License + +© 2015-2016 Markus Teich +© 2014 Dimitris Papastamos +© 2006-2014 Anselm R Garbe +© 2014-2016 Laslo Hunhold + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the "Software"), +to deal in the Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +++ Makefile @@ -20,9 +20,9 @@ LIBS += -lev LIBS += -lm -# OpenBSD lacks PAM, use bsd_auth(3) instead. +# On OpenBSD we use bsd_auth(3) instead. ifneq ($(UNAME),OpenBSD) - LIBS += -lpam + LIBS += -lcrypt endif FILES:=$(wildcard *.c) @@ -50,9 +50,7 @@ install: all $(INSTALL) -d $(DESTDIR)$(PREFIX)/bin - $(INSTALL) -d $(DESTDIR)$(SYSCONFDIR)/pam.d $(INSTALL) -m 755 i3lock $(DESTDIR)$(PREFIX)/bin/i3lock - $(INSTALL) -m 644 i3lock.pam $(DESTDIR)$(SYSCONFDIR)/pam.d/i3lock uninstall: rm -f $(DESTDIR)$(PREFIX)/bin/i3lock @@ -61,7 +59,7 @@ [ ! -d i3lock-${VERSION} ] || rm -rf i3lock-${VERSION} [ ! -e i3lock-${VERSION}.tar.bz2 ] || rm i3lock-${VERSION}.tar.bz2 mkdir i3lock-${VERSION} - cp *.c *.h i3lock.1 i3lock.pam Makefile LICENSE README.md CHANGELOG i3lock-${VERSION} + cp *.c *.h i3lock.1 Makefile LICENSE README.md CHANGELOG i3lock-${VERSION} sed -e 's/^I3LOCK_VERSION:=\(.*\)/I3LOCK_VERSION:=$(shell /bin/echo '${I3LOCK_VERSION}' | sed 's/\\/\\\\/g')/g;s/^VERSION:=\(.*\)/VERSION:=${VERSION}/g' Makefile > i3lock-${VERSION}/Makefile tar cfj i3lock-${VERSION}.tar.bz2 i3lock-${VERSION} rm -rf i3lock-${VERSION} +++ i3lock.1 @@ -43,8 +43,6 @@ You can specify either a background color or a PNG image which will be displayed while your screen is locked. .IP \[bu] You can specify whether i3lock should bell upon a wrong password. -.IP \[bu] -i3lock uses PAM and therefore is compatible with LDAP, etc. .SH OPTIONS @@ -66,8 +64,7 @@ .B \-u, \-\-no-unlock-indicator Disable the unlock indicator. i3lock will by default show an unlock indicator after pressing keys. This will give feedback for every keypress and it will -show you the current PAM state (whether your password is currently being -verified or whether it is wrong). +show you whether your password is currently being verified or whether it is wrong. .TP .BI \-i\ path \fR,\ \fB\-\-image= path @@ -95,7 +92,7 @@ .TP .B \-e, \-\-ignore-empty-password When an empty password is provided by the user, do not validate -it. Without this option, the empty password will be provided to PAM +it. Without this option, the empty password will be validated and, if invalid, the user will have to wait a few seconds before another try. This can be useful if the XF86ScreenSaver key is used to put a laptop to sleep and bounce on resume or if you happen to wake up +++ i3lock.c @@ -21,7 +21,9 @@ #ifdef __OpenBSD__ #include #else -#include +#include +#include +#include #endif #include #include @@ -57,7 +59,7 @@ 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). */ @@ -90,6 +92,37 @@ bool ignore_empty_password = false; bool skip_repeated_empty_password = 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 +#include + +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, "i3lock: fopen %s: %s", oomfile, strerror(errno)); + } + fprintf(f, "%d", OOM_SCORE_ADJ_MIN); + if (fclose(f)) { + if (errno == EACCES) + errx(EXIT_FAILURE, "i3lock: unable to disable OOM killer. " + "Make sure to suid or sgid i3lock."); + else + errx(EXIT_FAILURE, "i3lock: fclose %s: %s", oomfile, strerror(errno)); + } +} +#endif + /* isutf, u8_dec © 2005 Jeff Bezanson, public domain */ #define isutf(c) (((c)&0xC0) != 0x80) @@ -281,16 +314,16 @@ exit(0); } #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, "i3lock: crypt: %s", strerror(errno)); + else if (!strcmp(inputhash, hash)) { + DEBUG("successfully authenticated"); + clear_password_memory(); exit(0); } @@ -626,39 +659,6 @@ redraw_screen(); } -#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 @@ -813,10 +813,6 @@ 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; @@ -842,6 +838,48 @@ 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, however, + * and I'm not aware of any other methods to keep the calling user from + * killing the locker. + * This means that a malicious program running as your user + * could easily bypass your locker by killing it. + * However, if such a program even manages to be running, you're pretty + * screwed regardless. + */ + +#ifdef __linux__ + dontkillme(); +#endif + + hash = pw->pw_passwd; + + if (!strcmp(hash, "x")) { + struct spwd *sp; + if (!(sp = getspnam(pw->pw_name))) + errx(EXIT_FAILURE, "i3lock: getspnam: cannot retrieve shadow entry. " + "Make sure to suid or sgid i3lock."); + hash = sp->sp_pwdp; + } + + errno = 0; + if (!crypt("", hash)) + errx(EXIT_FAILURE, "i3lock: crypt: %s", strerror(errno)); + + /* drop privileges */ + if (setgroups(0, NULL) < 0) + errx(EXIT_FAILURE, "i3lock: setgroups: %s", strerror(errno)); + if (setgid(pw->pw_gid) < 0) + errx(EXIT_FAILURE, "i3lock: setgid: %s", strerror(errno)); + if (setuid(pw->pw_uid) < 0) + errx(EXIT_FAILURE, "i3lock: setuid: %s", strerror(errno)); +#endif + char *optstring = "hvnbdc:p:ui:teI:f"; while ((o = getopt_long(argc, argv, optstring, longopts, &optind)) != -1) { switch (o) { @@ -910,15 +948,6 @@ * 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). +++ i3lock.pam @@ -1,6 +0,0 @@ -# -# PAM configuration file for the i3lock screen locker. By default, it includes -# the 'login' configuration file (see /etc/pam.d/login) -# - -auth include login