slock.c 9.5 KB

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