slock.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463
  1. /* See LICENSE file for license details. */
  2. #define _XOPEN_SOURCE 500
  3. #define LENGTH(X) (sizeof X / sizeof X[0])
  4. #if HAVE_SHADOW_H
  5. #include <shadow.h>
  6. #endif
  7. #include <ctype.h>
  8. #include <errno.h>
  9. #include <grp.h>
  10. #include <pwd.h>
  11. #include <stdarg.h>
  12. #include <stdlib.h>
  13. #include <stdio.h>
  14. #include <string.h>
  15. #include <unistd.h>
  16. #include <sys/types.h>
  17. #include <X11/extensions/Xrandr.h>
  18. #ifdef XINERAMA
  19. #include <X11/extensions/Xinerama.h>
  20. #endif
  21. #include <X11/keysym.h>
  22. #include <X11/Xlib.h>
  23. #include <X11/Xutil.h>
  24. #include <X11/Xft/Xft.h>
  25. #include "arg.h"
  26. #include "util.h"
  27. char *argv0;
  28. enum {
  29. BACKGROUND,
  30. INIT,
  31. INPUT,
  32. FAILED,
  33. NUMCOLS
  34. };
  35. #include "config.h"
  36. struct lock {
  37. int screen;
  38. Window root, win;
  39. Pixmap pmap;
  40. unsigned long colors[NUMCOLS];
  41. unsigned int x, y;
  42. unsigned int xoff, yoff, mw, mh;
  43. Drawable drawable;
  44. GC gc;
  45. XRectangle rectangles[LENGTH(rectangles)];
  46. };
  47. struct xrandr {
  48. int active;
  49. int evbase;
  50. int errbase;
  51. };
  52. static void
  53. die(const char *errstr, ...)
  54. {
  55. va_list ap;
  56. va_start(ap, errstr);
  57. vfprintf(stderr, errstr, ap);
  58. va_end(ap);
  59. exit(1);
  60. }
  61. #ifdef __linux__
  62. #include <fcntl.h>
  63. #include <linux/oom.h>
  64. static void
  65. dontkillme(void)
  66. {
  67. FILE *f;
  68. const char oomfile[] = "/proc/self/oom_score_adj";
  69. if (!(f = fopen(oomfile, "w"))) {
  70. if (errno == ENOENT)
  71. return;
  72. die("slock: fopen %s: %s\n", oomfile, strerror(errno));
  73. }
  74. fprintf(f, "%d", OOM_SCORE_ADJ_MIN);
  75. if (fclose(f)) {
  76. if (errno == EACCES)
  77. die("slock: unable to disable OOM killer. "
  78. "Make sure to suid or sgid slock.\n");
  79. else
  80. die("slock: fclose %s: %s\n", oomfile, strerror(errno));
  81. }
  82. }
  83. #endif
  84. static const char *
  85. gethash(void)
  86. {
  87. const char *hash;
  88. struct passwd *pw;
  89. /* Check if the current user has a password entry */
  90. errno = 0;
  91. if (!(pw = getpwuid(getuid()))) {
  92. if (errno)
  93. die("slock: getpwuid: %s\n", strerror(errno));
  94. else
  95. die("slock: cannot retrieve password entry\n");
  96. }
  97. hash = pw->pw_passwd;
  98. #if HAVE_SHADOW_H
  99. if (!strcmp(hash, "x")) {
  100. struct spwd *sp;
  101. if (!(sp = getspnam(pw->pw_name)))
  102. die("slock: getspnam: cannot retrieve shadow entry. "
  103. "Make sure to suid or sgid slock.\n");
  104. hash = sp->sp_pwdp;
  105. }
  106. #else
  107. if (!strcmp(hash, "*")) {
  108. #ifdef __OpenBSD__
  109. if (!(pw = getpwuid_shadow(getuid())))
  110. die("slock: getpwnam_shadow: cannot retrieve shadow entry. "
  111. "Make sure to suid or sgid slock.\n");
  112. hash = pw->pw_passwd;
  113. #else
  114. die("slock: getpwuid: cannot retrieve shadow entry. "
  115. "Make sure to suid or sgid slock.\n");
  116. #endif /* __OpenBSD__ */
  117. }
  118. #endif /* HAVE_SHADOW_H */
  119. return hash;
  120. }
  121. static void
  122. resizerectangles(struct lock *lock)
  123. {
  124. int i;
  125. for (i = 0; i < LENGTH(rectangles); i++){
  126. lock->rectangles[i].x = (rectangles[i].x * logosize)
  127. + lock->xoff + ((lock->mw) / 2) - (logow / 2 * logosize);
  128. lock->rectangles[i].y = (rectangles[i].y * logosize)
  129. + lock->yoff + ((lock->mh) / 2) - (logoh / 2 * logosize);
  130. lock->rectangles[i].width = rectangles[i].width * logosize;
  131. lock->rectangles[i].height = rectangles[i].height * logosize;
  132. }
  133. }
  134. static void
  135. drawlogo(Display *dpy, struct lock *lock, int color)
  136. {
  137. XSetForeground(dpy, lock->gc, lock->colors[BACKGROUND]);
  138. XFillRectangle(dpy, lock->drawable, lock->gc, 0, 0, lock->x, lock->y);
  139. XSetForeground(dpy, lock->gc, lock->colors[color]);
  140. XFillRectangles(dpy, lock->drawable, lock->gc, lock->rectangles, LENGTH(rectangles));
  141. XCopyArea(dpy, lock->drawable, lock->win, lock->gc, 0, 0, lock->x, lock->y, 0, 0);
  142. XSync(dpy, False);
  143. }
  144. static void
  145. readpw(Display *dpy, struct xrandr *rr, struct lock **locks, int nscreens,
  146. const char *hash)
  147. {
  148. XRRScreenChangeNotifyEvent *rre;
  149. char buf[32], passwd[256], *inputhash;
  150. int num, screen, running, failure, oldc;
  151. unsigned int len, color;
  152. KeySym ksym;
  153. XEvent ev;
  154. len = 0;
  155. running = 1;
  156. failure = 0;
  157. oldc = INIT;
  158. while (running && !XNextEvent(dpy, &ev)) {
  159. if (ev.type == KeyPress) {
  160. explicit_bzero(&buf, sizeof(buf));
  161. num = XLookupString(&ev.xkey, buf, sizeof(buf), &ksym, 0);
  162. if (IsKeypadKey(ksym)) {
  163. if (ksym == XK_KP_Enter)
  164. ksym = XK_Return;
  165. else if (ksym >= XK_KP_0 && ksym <= XK_KP_9)
  166. ksym = (ksym - XK_KP_0) + XK_0;
  167. }
  168. if (IsFunctionKey(ksym) ||
  169. IsKeypadKey(ksym) ||
  170. IsMiscFunctionKey(ksym) ||
  171. IsPFKey(ksym) ||
  172. IsPrivateKeypadKey(ksym))
  173. continue;
  174. switch (ksym) {
  175. case XK_Return:
  176. passwd[len] = '\0';
  177. errno = 0;
  178. if (!(inputhash = crypt(passwd, hash)))
  179. fprintf(stderr, "slock: crypt: %s\n", strerror(errno));
  180. else
  181. running = !!strcmp(inputhash, hash);
  182. if (running) {
  183. XBell(dpy, 100);
  184. failure = 1;
  185. }
  186. explicit_bzero(&passwd, sizeof(passwd));
  187. len = 0;
  188. break;
  189. case XK_Escape:
  190. explicit_bzero(&passwd, sizeof(passwd));
  191. len = 0;
  192. break;
  193. case XK_BackSpace:
  194. if (len)
  195. passwd[--len] = '\0';
  196. break;
  197. default:
  198. if (num && !iscntrl((int)buf[0]) &&
  199. (len + num < sizeof(passwd))) {
  200. memcpy(passwd + len, buf, num);
  201. len += num;
  202. }
  203. break;
  204. }
  205. color = len ? INPUT : ((failure || failonclear) ? FAILED : INIT);
  206. if (running && oldc != color) {
  207. for (screen = 0; screen < nscreens; screen++) {
  208. drawlogo(dpy, locks[screen], color);
  209. }
  210. oldc = color;
  211. }
  212. } else if (rr->active && ev.type == rr->evbase + RRScreenChangeNotify) {
  213. rre = (XRRScreenChangeNotifyEvent*)&ev;
  214. for (screen = 0; screen < nscreens; screen++) {
  215. if (locks[screen]->win == rre->window) {
  216. if (rre->rotation == RR_Rotate_90 ||
  217. rre->rotation == RR_Rotate_270)
  218. XResizeWindow(dpy, locks[screen]->win,
  219. rre->height, rre->width);
  220. else
  221. XResizeWindow(dpy, locks[screen]->win,
  222. rre->width, rre->height);
  223. XClearWindow(dpy, locks[screen]->win);
  224. break;
  225. }
  226. }
  227. } else {
  228. for (screen = 0; screen < nscreens; screen++)
  229. XRaiseWindow(dpy, locks[screen]->win);
  230. }
  231. }
  232. }
  233. static struct lock *
  234. lockscreen(Display *dpy, struct xrandr *rr, int screen)
  235. {
  236. char curs[] = {0, 0, 0, 0, 0, 0, 0, 0};
  237. int i, ptgrab, kbgrab;
  238. struct lock *lock;
  239. XColor color, dummy;
  240. XSetWindowAttributes wa;
  241. Cursor invisible;
  242. #ifdef XINERAMA
  243. XineramaScreenInfo *info;
  244. int n;
  245. #endif
  246. if (dpy == NULL || screen < 0 || !(lock = malloc(sizeof(struct lock))))
  247. return NULL;
  248. lock->screen = screen;
  249. lock->root = RootWindow(dpy, lock->screen);
  250. for (i = 0; i < NUMCOLS; i++) {
  251. XAllocNamedColor(dpy, DefaultColormap(dpy, lock->screen),
  252. colorname[i], &color, &dummy);
  253. lock->colors[i] = color.pixel;
  254. }
  255. lock->x = DisplayWidth(dpy, lock->screen);
  256. lock->y = DisplayHeight(dpy, lock->screen);
  257. #ifdef XINERAMA
  258. if ((info = XineramaQueryScreens(dpy, &n))) {
  259. lock->xoff = info[0].x_org;
  260. lock->yoff = info[0].y_org;
  261. lock->mw = info[0].width;
  262. lock->mh = info[0].height;
  263. } else
  264. #endif
  265. {
  266. lock->xoff = lock->yoff = 0;
  267. lock->mw = lock->x;
  268. lock->mh = lock->y;
  269. }
  270. lock->drawable = XCreatePixmap(dpy, lock->root,
  271. lock->x, lock->y, DefaultDepth(dpy, screen));
  272. lock->gc = XCreateGC(dpy, lock->root, 0, NULL);
  273. XSetLineAttributes(dpy, lock->gc, 1, LineSolid, CapButt, JoinMiter);
  274. /* init */
  275. wa.override_redirect = 1;
  276. wa.background_pixel = lock->colors[BACKGROUND];
  277. lock->win = XCreateWindow(dpy, lock->root, 0, 0,
  278. lock->x, lock->y,
  279. 0, DefaultDepth(dpy, lock->screen),
  280. CopyFromParent,
  281. DefaultVisual(dpy, lock->screen),
  282. CWOverrideRedirect | CWBackPixel, &wa);
  283. lock->pmap = XCreateBitmapFromData(dpy, lock->win, curs, 8, 8);
  284. invisible = XCreatePixmapCursor(dpy, lock->pmap, lock->pmap,
  285. &color, &color, 0, 0);
  286. XDefineCursor(dpy, lock->win, invisible);
  287. resizerectangles(lock);
  288. /* Try to grab mouse pointer *and* keyboard for 600ms, else fail the lock */
  289. for (i = 0, ptgrab = kbgrab = -1; i < 6; i++) {
  290. if (ptgrab != GrabSuccess) {
  291. ptgrab = XGrabPointer(dpy, lock->root, False,
  292. ButtonPressMask | ButtonReleaseMask |
  293. PointerMotionMask, GrabModeAsync,
  294. GrabModeAsync, None, invisible, CurrentTime);
  295. }
  296. if (kbgrab != GrabSuccess) {
  297. kbgrab = XGrabKeyboard(dpy, lock->root, True,
  298. GrabModeAsync, GrabModeAsync, CurrentTime);
  299. }
  300. /* input is grabbed: we can lock the screen */
  301. if (ptgrab == GrabSuccess && kbgrab == GrabSuccess) {
  302. XMapRaised(dpy, lock->win);
  303. if (rr->active)
  304. XRRSelectInput(dpy, lock->win, RRScreenChangeNotifyMask);
  305. XSelectInput(dpy, lock->root, SubstructureNotifyMask);
  306. drawlogo(dpy, lock, INIT);
  307. return lock;
  308. }
  309. /* retry on AlreadyGrabbed but fail on other errors */
  310. if ((ptgrab != AlreadyGrabbed && ptgrab != GrabSuccess) ||
  311. (kbgrab != AlreadyGrabbed && kbgrab != GrabSuccess))
  312. break;
  313. usleep(100000);
  314. }
  315. /* we couldn't grab all input: fail out */
  316. if (ptgrab != GrabSuccess)
  317. fprintf(stderr, "slock: unable to grab mouse pointer for screen %d\n",
  318. screen);
  319. if (kbgrab != GrabSuccess)
  320. fprintf(stderr, "slock: unable to grab keyboard for screen %d\n",
  321. screen);
  322. return NULL;
  323. }
  324. static void
  325. usage(void)
  326. {
  327. die("usage: slock [-v] [cmd [arg ...]]\n");
  328. }
  329. int
  330. main(int argc, char **argv) {
  331. struct xrandr rr;
  332. struct lock **locks;
  333. struct passwd *pwd;
  334. struct group *grp;
  335. uid_t duid;
  336. gid_t dgid;
  337. const char *hash;
  338. Display *dpy;
  339. int s, nlocks, nscreens;
  340. ARGBEGIN {
  341. case 'v':
  342. fprintf(stderr, "slock-"VERSION"\n");
  343. return 0;
  344. default:
  345. usage();
  346. } ARGEND
  347. /* validate drop-user and -group */
  348. errno = 0;
  349. if (!(pwd = getpwnam(user)))
  350. die("slock: getpwnam %s: %s\n", user,
  351. errno ? strerror(errno) : "user entry not found");
  352. duid = pwd->pw_uid;
  353. errno = 0;
  354. if (!(grp = getgrnam(group)))
  355. die("slock: getgrnam %s: %s\n", group,
  356. errno ? strerror(errno) : "group entry not found");
  357. dgid = grp->gr_gid;
  358. #ifdef __linux__
  359. dontkillme();
  360. #endif
  361. hash = gethash();
  362. errno = 0;
  363. if (!crypt("", hash))
  364. die("slock: crypt: %s\n", strerror(errno));
  365. if (!(dpy = XOpenDisplay(NULL)))
  366. die("slock: cannot open display\n");
  367. /* drop privileges */
  368. if (setgroups(0, NULL) < 0)
  369. die("slock: setgroups: %s\n", strerror(errno));
  370. if (setgid(dgid) < 0)
  371. die("slock: setgid: %s\n", strerror(errno));
  372. if (setuid(duid) < 0)
  373. die("slock: setuid: %s\n", strerror(errno));
  374. /* check for Xrandr support */
  375. rr.active = XRRQueryExtension(dpy, &rr.evbase, &rr.errbase);
  376. /* get number of screens in display "dpy" and blank them */
  377. nscreens = ScreenCount(dpy);
  378. if (!(locks = calloc(nscreens, sizeof(struct lock *))))
  379. die("slock: out of memory\n");
  380. for (nlocks = 0, s = 0; s < nscreens; s++) {
  381. if ((locks[s] = lockscreen(dpy, &rr, s)) != NULL)
  382. nlocks++;
  383. else
  384. break;
  385. }
  386. XSync(dpy, 0);
  387. /* did we manage to lock everything? */
  388. if (nlocks != nscreens)
  389. return 1;
  390. /* run post-lock command */
  391. if (argc > 0) {
  392. switch (fork()) {
  393. case -1:
  394. die("slock: fork failed: %s\n", strerror(errno));
  395. case 0:
  396. if (close(ConnectionNumber(dpy)) < 0)
  397. die("slock: close: %s\n", strerror(errno));
  398. execvp(argv[0], argv);
  399. fprintf(stderr, "slock: execvp %s: %s\n", argv[0], strerror(errno));
  400. _exit(1);
  401. }
  402. }
  403. /* everything is now blank. Wait for the correct password */
  404. readpw(dpy, &rr, locks, nscreens, hash);
  405. for (nlocks = 0, s = 0; s < nscreens; s++) {
  406. XFreePixmap(dpy, locks[s]->drawable);
  407. XFreeGC(dpy, locks[s]->gc);
  408. }
  409. XSync(dpy, 0);
  410. XCloseDisplay(dpy);
  411. return 0;
  412. }