#include <ctype.h>
#include <errno.h>
+#include <grp.h>
#include <pwd.h>
#include <stdarg.h>
#include <stdlib.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
-#if HAVE_BSD_AUTH
-#include <login_cap.h>
-#include <bsd_auth.h>
-#endif
-
#include "arg.h"
#include "util.h"
static Lock **locks;
static int nscreens;
-static Bool running = True;
-static Bool failure = False;
static Bool rr;
static int rrevbase;
static int rrerrbase;
#ifdef __linux__
#include <fcntl.h>
+#include <linux/oom.h>
static void
dontkillme(void)
{
- int fd;
+ FILE *f;
+ const char oomfile[] = "/proc/self/oom_score_adj";
- fd = open("/proc/self/oom_score_adj", O_WRONLY);
- if (fd < 0 && errno == ENOENT) {
- return;
+ if (!(f = fopen(oomfile, "w"))) {
+ if (errno == ENOENT)
+ return;
+ die("slock: fopen %s: %s\n", oomfile, strerror(errno));
}
- if (fd < 0 || write(fd, "-1000\n", (sizeof("-1000\n") - 1)) !=
- (sizeof("-1000\n") - 1) || close(fd) != 0) {
- die("can't tame the oom-killer. is suid or sgid set?\n");
+ fprintf(f, "%d", OOM_SCORE_ADJ_MIN);
+ if (fclose(f)) {
+ if (errno == EACCES)
+ die("slock: unable to disable OOM killer. "
+ "suid or sgid set?\n");
+ else
+ die("slock: fclose %s: %s\n", oomfile,
+ strerror(errno));
}
}
#endif
-#ifndef HAVE_BSD_AUTH
-/* only run as root */
static const char *
getpw(void)
{
const char *rval;
struct passwd *pw;
+ /* Check if the current user has a password entry */
errno = 0;
if (!(pw = getpwuid(getuid()))) {
if (errno)
- die("getpwuid: %s\n", strerror(errno));
+ die("slock: getpwuid: %s\n", strerror(errno));
else
- die("cannot retrieve password entry\n");
+ die("slock: cannot retrieve password entry\n");
}
rval = pw->pw_passwd;
if (rval[0] == 'x' && rval[1] == '\0') {
struct spwd *sp;
if (!(sp = getspnam(getenv("USER"))))
- die("cannot retrieve shadow entry (make sure to suid or sgid slock)\n");
+ die("slock: getspnam: cannot retrieve shadow entry (make sure to suid or sgid slock)\n");
rval = sp->sp_pwdp;
}
-#endif
+#else
+ if (rval[0] == '*' && rval[1] == '\0') {
+#ifdef __OpenBSD__
+ if (!(pw = getpwnam_shadow(getenv("USER"))))
+ die("slock: getpwnam_shadow: cannot retrieve shadow entry (make sure to suid or sgid slock)\n");
+ rval = pw->pw_passwd;
+#else
+ die("slock: getpwuid: cannot retrieve shadow entry (make sure to suid or sgid slock)\n");
+#endif /* __OpenBSD__ */
+ }
+#endif /* HAVE_SHADOW_H */
- /* drop privileges */
- if (geteuid() == 0 &&
- ((getegid() != pw->pw_gid && setgid(pw->pw_gid) < 0) || setuid(pw->pw_uid) < 0))
- die("cannot drop privileges\n");
return rval;
}
-#endif
static void
-#ifdef HAVE_BSD_AUTH
-readpw(Display *dpy)
-#else
readpw(Display *dpy, const char *pws)
-#endif
{
- char buf[32], passwd[256];
- int num, screen;
+ char buf[32], passwd[256], *encrypted;
+ int num, screen, running, failure;
unsigned int len, color;
KeySym ksym;
XEvent ev;
static int oldc = INIT;
len = 0;
- running = True;
+ running = 1;
+ failure = 0;
/* As "slock" stands for "Simple X display locker", the DPMS settings
* had been removed and you can set it with "xset" or some other
switch (ksym) {
case XK_Return:
passwd[len] = 0;
-#ifdef HAVE_BSD_AUTH
- running = !auth_userokay(getlogin(), NULL, "auth-xlock", passwd);
-#else
- running = !!strcmp(crypt(passwd, pws), pws);
-#endif
+ errno = 0;
+ if (!(encrypted = crypt(passwd, pws)))
+ fprintf(stderr, "slock: crypt: %s\n", strerror(errno));
+ else
+ running = !!strcmp(encrypted, pws);
if (running) {
XBell(dpy, 100);
failure = True;
return;
XUngrabPointer(dpy, CurrentTime);
+ XUngrabKeyboard(dpy, CurrentTime);
XFreeColors(dpy, DefaultColormap(dpy, lock->screen), lock->colors, NUMCOLS, 0);
XFreePixmap(dpy, lock->pmap);
XDestroyWindow(dpy, lock->win);
free(lock);
}
+static void
+cleanup(Display *dpy)
+{
+ int s;
+
+ for (s = 0; s < nscreens; ++s)
+ unlockscreen(dpy, locks[s]);
+
+ free(locks);
+ XCloseDisplay(dpy);
+}
+
static Lock *
lockscreen(Display *dpy, int screen)
{
char curs[] = {0, 0, 0, 0, 0, 0, 0, 0};
- int i;
+ int i, ptgrab, kbgrab;
Lock *lock;
XColor color, dummy;
XSetWindowAttributes wa;
Cursor invisible;
- if (!running || dpy == NULL || screen < 0 || !(lock = malloc(sizeof(Lock))))
+ if (dpy == NULL || screen < 0 || !(lock = malloc(sizeof(Lock))))
return NULL;
lock->screen = screen;
invisible = XCreatePixmapCursor(dpy, lock->pmap, lock->pmap, &color, &color, 0, 0);
XDefineCursor(dpy, lock->win, invisible);
- /* Try to grab mouse pointer *and* keyboard, else fail the lock */
- if (XGrabPointer(dpy, lock->root, False, ButtonPressMask |
- ButtonReleaseMask | PointerMotionMask, GrabModeAsync, GrabModeAsync,
- None, invisible, CurrentTime) != GrabSuccess) {
- fprintf(stderr, "slock: unable to grab mouse pointer for screen %d\n", screen);
- running = 0;
- unlockscreen(dpy, lock);
- return NULL;
- }
+ /* Try to grab mouse pointer *and* keyboard for 600ms, else fail the lock */
+ for (i = 0, ptgrab = kbgrab = -1; i < 6; i++) {
+ if (ptgrab != GrabSuccess) {
+ ptgrab = XGrabPointer(dpy, lock->root, False,
+ ButtonPressMask | ButtonReleaseMask |
+ PointerMotionMask, GrabModeAsync,
+ GrabModeAsync, None, invisible, CurrentTime);
+ }
+ if (kbgrab != GrabSuccess) {
+ kbgrab = XGrabKeyboard(dpy, lock->root, True,
+ GrabModeAsync, GrabModeAsync, CurrentTime);
+ }
- if (XGrabKeyboard(dpy, lock->root, True, GrabModeAsync, GrabModeAsync,
- CurrentTime) != GrabSuccess) {
- fprintf(stderr, "slock: unable to grab keyboard for screen %d\n", screen);
- running = 0;
- unlockscreen(dpy, lock);
- return NULL;
- }
+ /* input is grabbed: we can lock the screen */
+ if (ptgrab == GrabSuccess && kbgrab == GrabSuccess) {
+ XMapRaised(dpy, lock->win);
+ if (rr)
+ XRRSelectInput(dpy, lock->win, RRScreenChangeNotifyMask);
+
+ XSelectInput(dpy, lock->root, SubstructureNotifyMask);
+ return lock;
+ }
+
+ /* retry on AlreadyGrabbed but fail on other errors */
+ if ((ptgrab != AlreadyGrabbed && ptgrab != GrabSuccess) ||
+ (kbgrab != AlreadyGrabbed && kbgrab != GrabSuccess))
+ break;
- XMapRaised(dpy, lock->win);
- if (rr)
- XRRSelectInput(dpy, lock->win, RRScreenChangeNotifyMask);
+ usleep(100000);
+ }
- XSelectInput(dpy, lock->root, SubstructureNotifyMask);
- return lock;
+ /* we couldn't grab all input: fail out */
+ if (ptgrab != GrabSuccess)
+ fprintf(stderr, "slock: unable to grab mouse pointer for screen %d\n", screen);
+ if (kbgrab != GrabSuccess)
+ fprintf(stderr, "slock: unable to grab keyboard for screen %d\n", screen);
+ return NULL;
}
static void
usage(void)
{
- die("usage: slock [-v | cmd [arg ...]]\n");
+ die("usage: slock [-v] [cmd [arg ...]]\n");
}
int
main(int argc, char **argv) {
-#ifndef HAVE_BSD_AUTH
+ struct passwd *pwd;
+ struct group *grp;
+ uid_t duid;
+ gid_t dgid;
const char *pws;
-#endif
Display *dpy;
int s, nlocks;
usage();
} ARGEND
+ /* validate drop-user and -group */
+ errno = 0;
+ if (!(pwd = getpwnam(user)))
+ die("slock: getpwnam %s: %s\n", user, errno ?
+ strerror(errno) : "user entry not found");
+ duid = pwd->pw_uid;
+ errno = 0;
+ if (!(grp = getgrnam(group)))
+ die("slock: getgrnam %s: %s\n", group, errno ?
+ strerror(errno) : "group entry not found");
+ dgid = grp->gr_gid;
+
#ifdef __linux__
dontkillme();
#endif
- /* Check if the current user has a password entry */
- errno = 0;
- if (!getpwuid(getuid())) {
- if (errno == 0)
- die("slock: no password entry for current user\n");
- else
- die("slock: getpwuid: %s\n", strerror(errno));
- }
-
-#ifndef HAVE_BSD_AUTH
pws = getpw();
-#endif
+ if (strlen(pws) < 2)
+ die("slock: failed to get user password hash.\n");
if (!(dpy = XOpenDisplay(NULL)))
die("slock: cannot open display\n");
+ /* drop privileges */
+ if (setgroups(0, NULL) < 0)
+ die("slock: setgroups: %s\n", strerror(errno));
+ if (setgid(dgid) < 0)
+ die("slock: setgid: %s\n", strerror(errno));
+ if (setuid(duid) < 0)
+ die("slock: setuid: %s\n", strerror(errno));
+
/* check for Xrandr support */
rr = XRRQueryExtension(dpy, &rrevbase, &rrerrbase);
/* get number of screens in display "dpy" and blank them */
nscreens = ScreenCount(dpy);
- if (!(locks = malloc(sizeof(Lock *) * nscreens))) {
+ if (!(locks = calloc(nscreens, sizeof(Lock *)))) {
XCloseDisplay(dpy);
die("slock: out of memory\n");
}
for (nlocks = 0, s = 0; s < nscreens; s++) {
if ((locks[s] = lockscreen(dpy, s)) != NULL)
nlocks++;
+ else
+ break;
}
XSync(dpy, 0);
- /* did we actually manage to lock anything? */
- if (nlocks == 0) {
- /* nothing to protect */
- free(locks);
- XCloseDisplay(dpy);
+ /* did we manage to lock everything? */
+ if (nlocks != nscreens) {
+ cleanup(dpy);
return 1;
}
if (argc > 0) {
switch (fork()) {
case -1:
- free(locks);
- XCloseDisplay(dpy);
+ cleanup(dpy);
die("slock: fork failed: %s\n", strerror(errno));
case 0:
if (close(ConnectionNumber(dpy)) < 0)
}
/* everything is now blank. Wait for the correct password */
-#ifdef HAVE_BSD_AUTH
- readpw(dpy);
-#else
readpw(dpy, pws);
-#endif
/* password ok, unlock everything and quit */
- for (s = 0; s < nscreens; s++)
- unlockscreen(dpy, locks[s]);
-
- free(locks);
- XCloseDisplay(dpy);
+ cleanup(dpy);
return 0;
}