slock.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460
  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. XResizeWindow(dpy, locks[screen]->win,
  190. rre->width, rre->height);
  191. XClearWindow(dpy, locks[screen]->win);
  192. }
  193. }
  194. } else for (screen = 0; screen < nscreens; screen++)
  195. XRaiseWindow(dpy, locks[screen]->win);
  196. }
  197. }
  198. static struct lock *
  199. lockscreen(Display *dpy, struct xrandr *rr, int screen)
  200. {
  201. char curs[] = {0, 0, 0, 0, 0, 0, 0, 0};
  202. int i, ptgrab, kbgrab;
  203. struct lock *lock;
  204. XColor color, dummy;
  205. XSetWindowAttributes wa;
  206. Cursor invisible;
  207. if (dpy == NULL || screen < 0 || !(lock = malloc(sizeof(struct lock))))
  208. return NULL;
  209. lock->screen = screen;
  210. lock->root = RootWindow(dpy, lock->screen);
  211. if(image)
  212. {
  213. lock->bgmap = XCreatePixmap(dpy, lock->root, DisplayWidth(dpy, lock->screen), DisplayHeight(dpy, lock->screen), DefaultDepth(dpy, lock->screen));
  214. imlib_context_set_image(image);
  215. imlib_context_set_display(dpy);
  216. imlib_context_set_visual(DefaultVisual(dpy, lock->screen));
  217. imlib_context_set_colormap(DefaultColormap(dpy, lock->screen));
  218. imlib_context_set_drawable(lock->bgmap);
  219. imlib_render_image_on_drawable(0, 0);
  220. imlib_free_image();
  221. }
  222. for (i = 0; i < NUMCOLS; i++) {
  223. XAllocNamedColor(dpy, DefaultColormap(dpy, lock->screen),
  224. colorname[i], &color, &dummy);
  225. lock->colors[i] = color.pixel;
  226. }
  227. /* init */
  228. wa.override_redirect = 1;
  229. wa.background_pixel = lock->colors[INIT];
  230. lock->win = XCreateWindow(dpy, lock->root, 0, 0,
  231. DisplayWidth(dpy, lock->screen),
  232. DisplayHeight(dpy, lock->screen),
  233. 0, DefaultDepth(dpy, lock->screen),
  234. CopyFromParent,
  235. DefaultVisual(dpy, lock->screen),
  236. CWOverrideRedirect | CWBackPixel, &wa);
  237. if(lock->bgmap)
  238. XSetWindowBackgroundPixmap(dpy, lock->win, lock->bgmap);
  239. lock->pmap = XCreateBitmapFromData(dpy, lock->win, curs, 8, 8);
  240. invisible = XCreatePixmapCursor(dpy, lock->pmap, lock->pmap,
  241. &color, &color, 0, 0);
  242. XDefineCursor(dpy, lock->win, invisible);
  243. /* Try to grab mouse pointer *and* keyboard for 600ms, else fail the lock */
  244. for (i = 0, ptgrab = kbgrab = -1; i < 6; i++) {
  245. if (ptgrab != GrabSuccess) {
  246. ptgrab = XGrabPointer(dpy, lock->root, False,
  247. ButtonPressMask | ButtonReleaseMask |
  248. PointerMotionMask, GrabModeAsync,
  249. GrabModeAsync, None, invisible, CurrentTime);
  250. }
  251. if (kbgrab != GrabSuccess) {
  252. kbgrab = XGrabKeyboard(dpy, lock->root, True,
  253. GrabModeAsync, GrabModeAsync, CurrentTime);
  254. }
  255. /* input is grabbed: we can lock the screen */
  256. if (ptgrab == GrabSuccess && kbgrab == GrabSuccess) {
  257. XMapRaised(dpy, lock->win);
  258. if (rr->active)
  259. XRRSelectInput(dpy, lock->win, RRScreenChangeNotifyMask);
  260. XSelectInput(dpy, lock->root, SubstructureNotifyMask);
  261. return lock;
  262. }
  263. /* retry on AlreadyGrabbed but fail on other errors */
  264. if ((ptgrab != AlreadyGrabbed && ptgrab != GrabSuccess) ||
  265. (kbgrab != AlreadyGrabbed && kbgrab != GrabSuccess))
  266. break;
  267. usleep(100000);
  268. }
  269. /* we couldn't grab all input: fail out */
  270. if (ptgrab != GrabSuccess)
  271. fprintf(stderr, "slock: unable to grab mouse pointer for screen %d\n",
  272. screen);
  273. if (kbgrab != GrabSuccess)
  274. fprintf(stderr, "slock: unable to grab keyboard for screen %d\n",
  275. screen);
  276. return NULL;
  277. }
  278. static void
  279. usage(void)
  280. {
  281. die("usage: slock [-v] [cmd [arg ...]]\n");
  282. }
  283. int
  284. main(int argc, char **argv) {
  285. struct xrandr rr;
  286. struct lock **locks;
  287. struct passwd *pwd;
  288. struct group *grp;
  289. uid_t duid;
  290. gid_t dgid;
  291. const char *hash;
  292. Display *dpy;
  293. int s, nlocks, nscreens;
  294. ARGBEGIN {
  295. case 'v':
  296. fprintf(stderr, "slock-"VERSION"\n");
  297. return 0;
  298. default:
  299. usage();
  300. } ARGEND
  301. /* validate drop-user and -group */
  302. errno = 0;
  303. if (!(pwd = getpwnam(getenv("USER"))))
  304. die("slock: getpwnam %s: %s\n", getenv("USER"),
  305. errno ? strerror(errno) : "user entry not found");
  306. duid = pwd->pw_uid;
  307. errno = 0;
  308. if (!(grp = getgrnam(getenv("USER"))))
  309. die("slock: getgrnam %s: %s\n", getenv("USER"),
  310. errno ? strerror(errno) : "group entry not found");
  311. dgid = grp->gr_gid;
  312. #ifdef __linux__
  313. dontkillme();
  314. #endif
  315. hash = gethash();
  316. errno = 0;
  317. if (!crypt("", hash))
  318. die("slock: crypt: %s\n", strerror(errno));
  319. if (!(dpy = XOpenDisplay(NULL)))
  320. die("slock: cannot open display\n");
  321. /* drop privileges */
  322. if (setgroups(0, NULL) < 0)
  323. die("slock: setgroups: %s\n", strerror(errno));
  324. if (setgid(dgid) < 0)
  325. die("slock: setgid: %s\n", strerror(errno));
  326. if (setuid(duid) < 0)
  327. die("slock: setuid: %s\n", strerror(errno));
  328. /*Create screenshot Image*/
  329. Screen *scr = ScreenOfDisplay(dpy, DefaultScreen(dpy));
  330. image = imlib_create_image(scr->width,scr->height);
  331. imlib_context_set_image(image);
  332. imlib_context_set_display(dpy);
  333. imlib_context_set_visual(DefaultVisual(dpy,0));
  334. imlib_context_set_drawable(RootWindow(dpy,XScreenNumberOfScreen(scr)));
  335. imlib_copy_drawable_to_image(0,0,0,scr->width,scr->height,0,0,1);
  336. #ifdef BLUR
  337. /*Blur function*/
  338. imlib_image_blur(blurRadius);
  339. #endif // BLUR
  340. #ifdef PIXELATION
  341. /*Pixelation*/
  342. int width = scr->width;
  343. int height = scr->height;
  344. for(int y = 0; y < height; y += pixelSize)
  345. {
  346. for(int x = 0; x < width; x += pixelSize)
  347. {
  348. int red = 0;
  349. int green = 0;
  350. int blue = 0;
  351. Imlib_Color pixel;
  352. Imlib_Color* pp;
  353. pp = &pixel;
  354. for(int j = 0; j < pixelSize && j < height; j++)
  355. {
  356. for(int i = 0; i < pixelSize && i < width; i++)
  357. {
  358. imlib_image_query_pixel(x+i,y+j,pp);
  359. red += pixel.red;
  360. green += pixel.green;
  361. blue += pixel.blue;
  362. }
  363. }
  364. red /= (pixelSize*pixelSize);
  365. green /= (pixelSize*pixelSize);
  366. blue /= (pixelSize*pixelSize);
  367. imlib_context_set_color(red,green,blue,pixel.alpha);
  368. imlib_image_fill_rectangle(x,y,pixelSize,pixelSize);
  369. red = 0;
  370. green = 0;
  371. blue = 0;
  372. }
  373. }
  374. #endif
  375. /* check for Xrandr support */
  376. rr.active = XRRQueryExtension(dpy, &rr.evbase, &rr.errbase);
  377. /* get number of screens in display "dpy" and blank them */
  378. nscreens = ScreenCount(dpy);
  379. if (!(locks = calloc(nscreens, sizeof(struct lock *))))
  380. die("slock: out of memory\n");
  381. for (nlocks = 0, s = 0; s < nscreens; s++) {
  382. if ((locks[s] = lockscreen(dpy, &rr, s)) != NULL)
  383. nlocks++;
  384. else
  385. break;
  386. }
  387. XSync(dpy, 0);
  388. /* did we manage to lock everything? */
  389. if (nlocks != nscreens)
  390. return 1;
  391. /* run post-lock command */
  392. if (argc > 0) {
  393. switch (fork()) {
  394. case -1:
  395. die("slock: fork failed: %s\n", strerror(errno));
  396. case 0:
  397. if (close(ConnectionNumber(dpy)) < 0)
  398. die("slock: close: %s\n", strerror(errno));
  399. execvp(argv[0], argv);
  400. fprintf(stderr, "slock: execvp %s: %s\n", argv[0], strerror(errno));
  401. _exit(1);
  402. }
  403. }
  404. /* everything is now blank. Wait for the correct password */
  405. readpw(dpy, &rr, locks, nscreens, hash);
  406. return 0;
  407. }