slock.c 11 KB

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