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