master
Raw Download raw file
  1/* See LICENSE file for license details. */
  2#define _XOPEN_SOURCE 500
  3#if HAVE_SHADOW_H
  4#include <shadow.h>
  5#endif
  6
  7#include <ctype.h>
  8#include <errno.h>
  9#include <pwd.h>
 10#include <stdarg.h>
 11#include <stdlib.h>
 12#include <stdio.h>
 13#include <string.h>
 14#include <unistd.h>
 15#include <sys/types.h>
 16#include <X11/extensions/Xrandr.h>
 17#include <X11/keysym.h>
 18#include <X11/Xlib.h>
 19#include <X11/Xutil.h>
 20
 21#if HAVE_BSD_AUTH
 22#include <login_cap.h>
 23#include <bsd_auth.h>
 24#endif
 25
 26enum {
 27	INIT,
 28	INPUT,
 29	FAILED,
 30	NUMCOLS
 31};
 32
 33#include "config.h"
 34
 35typedef struct {
 36	int screen;
 37	Window root, win;
 38	Pixmap pmap;
 39	unsigned long colors[NUMCOLS];
 40} Lock;
 41
 42static Lock **locks;
 43static int nscreens;
 44static Bool running = True;
 45static Bool failure = False;
 46static Bool rr;
 47static int rrevbase;
 48static int rrerrbase;
 49
 50static void
 51die(const char *errstr, ...)
 52{
 53	va_list ap;
 54
 55	fputs("slock: ", stderr);
 56	va_start(ap, errstr);
 57	vfprintf(stderr, errstr, ap);
 58	va_end(ap);
 59	exit(1);
 60}
 61
 62#ifdef __linux__
 63#include <fcntl.h>
 64
 65static void
 66dontkillme(void)
 67{
 68	int fd;
 69
 70	fd = open("/proc/self/oom_score_adj", O_WRONLY);
 71	if (fd < 0 && errno == ENOENT) {
 72		return;
 73	}
 74	if (fd < 0 || write(fd, "-1000\n", (sizeof("-1000\n") - 1)) !=
 75	    (sizeof("-1000\n") - 1) || close(fd) != 0) {
 76		die("can't tame the oom-killer. is suid or sgid set?\n");
 77	}
 78}
 79#endif
 80
 81#ifndef HAVE_BSD_AUTH
 82/* only run as root */
 83static const char *
 84getpw(void)
 85{
 86	const char *rval;
 87	struct passwd *pw;
 88
 89	errno = 0;
 90	if (!(pw = getpwuid(getuid()))) {
 91		if (errno)
 92			die("getpwuid: %s\n", strerror(errno));
 93		else
 94			die("cannot retrieve password entry\n");
 95	}
 96	rval = pw->pw_passwd;
 97
 98#if HAVE_SHADOW_H
 99	if (rval[0] == 'x' && rval[1] == '\0') {
100		struct spwd *sp;
101		if (!(sp = getspnam(getenv("USER"))))
102			die("cannot retrieve shadow entry (make sure to suid or sgid slock)\n");
103		rval = sp->sp_pwdp;
104	}
105#endif
106
107	/* drop privileges */
108	if (geteuid() == 0 &&
109	    ((getegid() != pw->pw_gid && setgid(pw->pw_gid) < 0) || setuid(pw->pw_uid) < 0))
110		die("cannot drop privileges\n");
111	return rval;
112}
113#endif
114
115static void
116#ifdef HAVE_BSD_AUTH
117readpw(Display *dpy)
118#else
119readpw(Display *dpy, const char *pws)
120#endif
121{
122	char buf[32], passwd[256];
123	int num, screen;
124	unsigned int len, color;
125	KeySym ksym;
126	XEvent ev;
127	static int oldc = INIT;
128
129	len = 0;
130	running = True;
131
132	/* As "slock" stands for "Simple X display locker", the DPMS settings
133	 * had been removed and you can set it with "xset" or some other
134	 * utility. This way the user can easily set a customized DPMS
135	 * timeout. */
136	while (running && !XNextEvent(dpy, &ev)) {
137		if (ev.type == KeyPress) {
138			buf[0] = 0;
139			num = XLookupString(&ev.xkey, buf, sizeof(buf), &ksym, 0);
140			if (IsKeypadKey(ksym)) {
141				if (ksym == XK_KP_Enter)
142					ksym = XK_Return;
143				else if (ksym >= XK_KP_0 && ksym <= XK_KP_9)
144					ksym = (ksym - XK_KP_0) + XK_0;
145			}
146			if (IsFunctionKey(ksym) ||
147			    IsKeypadKey(ksym) ||
148			    IsMiscFunctionKey(ksym) ||
149			    IsPFKey(ksym) ||
150			    IsPrivateKeypadKey(ksym))
151				continue;
152			switch (ksym) {
153			case XK_Return:
154				passwd[len] = 0;
155#ifdef HAVE_BSD_AUTH
156				running = !auth_userokay(getlogin(), NULL, "auth-xlock", passwd);
157#else
158				running = !!strcmp(crypt(passwd, pws), pws);
159#endif
160				if (running) {
161					XBell(dpy, 100);
162					failure = True;
163				}
164				len = 0;
165				break;
166			case XK_Escape:
167				len = 0;
168				break;
169			case XK_BackSpace:
170				if (len)
171					--len;
172				break;
173			default:
174				if (num && !iscntrl((int)buf[0]) && (len + num < sizeof(passwd))) {
175					memcpy(passwd + len, buf, num);
176					len += num;
177				}
178				break;
179			}
180			color = len ? INPUT : (failure || failonclear ? FAILED : INIT);
181			if (running && oldc != color) {
182				for (screen = 0; screen < nscreens; screen++) {
183					XSetWindowBackground(dpy, locks[screen]->win, locks[screen]->colors[color]);
184					XClearWindow(dpy, locks[screen]->win);
185				}
186				oldc = color;
187			}
188		} else if (rr && ev.type == rrevbase + RRScreenChangeNotify) {
189			XRRScreenChangeNotifyEvent *rre = (XRRScreenChangeNotifyEvent*)&ev;
190			for (screen = 0; screen < nscreens; screen++) {
191				if (locks[screen]->win == rre->window) {
192					XResizeWindow(dpy, locks[screen]->win, rre->width, rre->height);
193					XClearWindow(dpy, locks[screen]->win);
194				}
195			}
196		} else for (screen = 0; screen < nscreens; screen++)
197			XRaiseWindow(dpy, locks[screen]->win);
198	}
199}
200
201static void
202unlockscreen(Display *dpy, Lock *lock)
203{
204	if(dpy == NULL || lock == NULL)
205		return;
206
207	XUngrabPointer(dpy, CurrentTime);
208	XFreeColors(dpy, DefaultColormap(dpy, lock->screen), lock->colors, NUMCOLS, 0);
209	XFreePixmap(dpy, lock->pmap);
210	XDestroyWindow(dpy, lock->win);
211
212	free(lock);
213}
214
215static Lock *
216lockscreen(Display *dpy, int screen)
217{
218	char curs[] = {0, 0, 0, 0, 0, 0, 0, 0};
219	unsigned int len;
220	int i;
221	Lock *lock;
222	XColor color, dummy;
223	XSetWindowAttributes wa;
224	Cursor invisible;
225
226	if (!running || dpy == NULL || screen < 0 || !(lock = malloc(sizeof(Lock))))
227		return NULL;
228
229	lock->screen = screen;
230	lock->root = RootWindow(dpy, lock->screen);
231
232	for (i = 0; i < NUMCOLS; i++) {
233		XAllocNamedColor(dpy, DefaultColormap(dpy, lock->screen), colorname[i], &color, &dummy);
234		lock->colors[i] = color.pixel;
235	}
236
237	/* init */
238	wa.override_redirect = 1;
239	wa.background_pixel = lock->colors[INIT];
240	lock->win = XCreateWindow(dpy, lock->root, 0, 0, DisplayWidth(dpy, lock->screen), DisplayHeight(dpy, lock->screen),
241	                          0, DefaultDepth(dpy, lock->screen), CopyFromParent,
242	                          DefaultVisual(dpy, lock->screen), CWOverrideRedirect | CWBackPixel, &wa);
243	lock->pmap = XCreateBitmapFromData(dpy, lock->win, curs, 8, 8);
244	invisible = XCreatePixmapCursor(dpy, lock->pmap, lock->pmap, &color, &color, 0, 0);
245	XDefineCursor(dpy, lock->win, invisible);
246	XMapRaised(dpy, lock->win);
247	if (rr)
248		XRRSelectInput(dpy, lock->win, RRScreenChangeNotifyMask);
249
250	/* Try to grab mouse pointer *and* keyboard, else fail the lock */
251	for (len = 1000; len; len--) {
252		if (XGrabPointer(dpy, lock->root, False, ButtonPressMask | ButtonReleaseMask | PointerMotionMask,
253		    GrabModeAsync, GrabModeAsync, None, invisible, CurrentTime) == GrabSuccess)
254			break;
255		usleep(1000);
256	}
257	if (!len) {
258		fprintf(stderr, "slock: unable to grab mouse pointer for screen %d\n", screen);
259	} else {
260		for (len = 1000; len; len--) {
261			if (XGrabKeyboard(dpy, lock->root, True, GrabModeAsync, GrabModeAsync, CurrentTime) == GrabSuccess) {
262				/* everything fine, we grabbed both inputs */
263				XSelectInput(dpy, lock->root, SubstructureNotifyMask);
264				return lock;
265			}
266			usleep(1000);
267		}
268		fprintf(stderr, "slock: unable to grab keyboard for screen %d\n", screen);
269	}
270	/* grabbing one of the inputs failed */
271	running = 0;
272	unlockscreen(dpy, lock);
273	return NULL;
274}
275
276static void
277usage(void)
278{
279	fprintf(stderr, "usage: slock [-v|POST_LOCK_CMD]\n");
280	exit(1);
281}
282
283int
284main(int argc, char **argv) {
285#ifndef HAVE_BSD_AUTH
286	const char *pws;
287#endif
288	Display *dpy;
289	int screen;
290
291	if ((argc >= 2) && !strcmp("-v", argv[1]))
292		die("version %s, © 2006-2016 slock engineers\n", VERSION);
293
294	/* treat first argument starting with a '-' as option */
295	if ((argc >= 2) && argv[1][0] == '-')
296		usage();
297
298#ifdef __linux__
299	dontkillme();
300#endif
301
302	if (!getpwuid(getuid()))
303		die("no passwd entry for you\n");
304
305#ifndef HAVE_BSD_AUTH
306	pws = getpw();
307#endif
308
309	if (!(dpy = XOpenDisplay(0)))
310		die("cannot open display\n");
311	rr = XRRQueryExtension(dpy, &rrevbase, &rrerrbase);
312	/* Get the number of screens in display "dpy" and blank them all. */
313	nscreens = ScreenCount(dpy);
314	if (!(locks = malloc(sizeof(Lock*) * nscreens)))
315		die("Out of memory.\n");
316	int nlocks = 0;
317	for (screen = 0; screen < nscreens; screen++) {
318		if ((locks[screen] = lockscreen(dpy, screen)) != NULL)
319			nlocks++;
320	}
321	XSync(dpy, False);
322
323	/* Did we actually manage to lock something? */
324	if (nlocks == 0) { /* nothing to protect */
325		free(locks);
326		XCloseDisplay(dpy);
327		return 1;
328	}
329
330	if (argc >= 2 && fork() == 0) {
331		if (dpy)
332			close(ConnectionNumber(dpy));
333		execvp(argv[1], argv+1);
334		die("execvp %s failed: %s\n", argv[1], strerror(errno));
335	}
336
337	/* Everything is now blank. Now wait for the correct password. */
338#ifdef HAVE_BSD_AUTH
339	readpw(dpy);
340#else
341	readpw(dpy, pws);
342#endif
343
344	/* Password ok, unlock everything and quit. */
345	for (screen = 0; screen < nscreens; screen++)
346		unlockscreen(dpy, locks[screen]);
347
348	free(locks);
349	XCloseDisplay(dpy);
350
351	return 0;
352}