]> git.armaanb.net Git - slock.git/blob - slock.c
applied Markus' patch to die in case /etc/passwd is unavailable
[slock.git] / slock.c
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 <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/keysym.h>
17 #include <X11/Xlib.h>
18 #include <X11/Xutil.h>
19
20 #if HAVE_BSD_AUTH
21 #include <login_cap.h>
22 #include <bsd_auth.h>
23 #endif
24
25 struct st_lock {
26         int screen;
27         Window root, w;
28         Pixmap pmap;
29 };
30
31 extern const char *__progname;
32
33 static void
34 die(const char *errstr, ...) {
35         va_list ap;
36
37         fprintf(stderr, "%s: ", __progname);
38         va_start(ap, errstr);
39         vfprintf(stderr, errstr, ap);
40         va_end(ap);
41         fprintf(stderr, "\n");
42         fflush(stderr);
43
44         exit(EXIT_FAILURE);
45 }
46
47 #ifndef HAVE_BSD_AUTH
48 static const char *
49 get_password(void) { /* only run as root */
50         const char *rval;
51         struct passwd *pw;
52
53         if(geteuid() != 0)
54                 die("cannot retrieve password entry (make sure to suid slock)");
55         pw = getpwuid(getuid());
56         endpwent();
57         rval =  pw->pw_passwd;
58
59 #if HAVE_SHADOW_H
60         {
61                 struct spwd *sp;
62                 sp = getspnam(getenv("USER"));
63                 endspent();
64                 rval = sp->sp_pwdp;
65         }
66 #endif
67
68         /* drop privileges */
69         if(setgid(pw->pw_gid) < 0 || setuid(pw->pw_uid) < 0)
70                 die("cannot drop privileges");
71         return rval;
72 }
73 #endif
74
75 static void
76 #ifdef HAVE_BSD_AUTH
77 read_password(Display *dpy)
78 #else
79 read_password(Display *dpy, const char *pws)
80 #endif
81 {
82         char buf[32], passwd[256];
83         int num;
84
85         unsigned int len;
86         Bool running = True;
87         KeySym ksym;
88         XEvent ev;
89
90         len = 0;
91         running = True;
92
93         /* As "slock" stands for "Simple X display locker", the DPMS settings
94          * had been removed and you can set it with "xset" or some other
95          * utility. This way the user can easily set a customized DPMS
96          * timeout. */
97
98         while(running && !XNextEvent(dpy, &ev)) {
99                 if(ev.type == KeyPress) {
100                         buf[0] = 0;
101                         num = XLookupString(&ev.xkey, buf, sizeof buf, &ksym, 0);
102                         if(IsKeypadKey(ksym)) {
103                                 if(ksym == XK_KP_Enter)
104                                         ksym = XK_Return;
105                                 else if(ksym >= XK_KP_0 && ksym <= XK_KP_9)
106                                         ksym = (ksym - XK_KP_0) + XK_0;
107                         }
108                         if(IsFunctionKey(ksym) || IsKeypadKey(ksym)
109                                         || IsMiscFunctionKey(ksym) || IsPFKey(ksym)
110                                         || IsPrivateKeypadKey(ksym))
111                                 continue;
112                         switch(ksym) {
113                         case XK_Return:
114                                 passwd[len] = 0;
115 #ifdef HAVE_BSD_AUTH
116                                 running = !auth_userokay(getlogin(), NULL, "auth-xlock", passwd);
117 #else
118                                 running = strcmp(crypt(passwd, pws), pws);
119 #endif
120                                 if (running != 0)
121                                         XBell(dpy, 100);
122                                 len = 0;
123                                 break;
124                         case XK_Escape:
125                                 len = 0;
126                                 break;
127                         case XK_BackSpace:
128                                 if(len)
129                                         --len;
130                                 break;
131                         default:
132                                 if(num && !iscntrl((int) buf[0]) && (len + num < sizeof passwd)) { 
133                                         memcpy(passwd + len, buf, num);
134                                         len += num;
135                                 }
136                                 break;
137                         }
138                 }
139         }
140 }
141
142 static void
143 unlockscreen(Display *dpy, struct st_lock *lock) {
144         if (dpy == NULL || lock == NULL)
145                 return;
146
147         XUngrabPointer(dpy, CurrentTime);
148         XFreePixmap(dpy, lock->pmap);
149         XDestroyWindow(dpy, lock->w);
150
151         free(lock);
152 }
153
154 static struct st_lock *
155 lockscreen(Display *dpy, int screen) {
156         char curs[] = {0, 0, 0, 0, 0, 0, 0, 0};
157         unsigned int len;
158         struct st_lock *lock;
159         Bool running = True;
160         XColor black, dummy;
161         XSetWindowAttributes wa;
162         Cursor invisible;
163
164         if (dpy == NULL || screen < 0)
165                 return NULL;
166
167         lock = malloc(sizeof(struct st_lock));
168         if (lock == NULL)
169                 return NULL;
170
171         lock->screen = screen;
172
173         lock->root = RootWindow(dpy, lock->screen);
174
175         /* init */
176         wa.override_redirect = 1;
177         wa.background_pixel = BlackPixel(dpy, lock->screen);
178         lock->w = XCreateWindow(dpy, lock->root, 0, 0, DisplayWidth(dpy, lock->screen), DisplayHeight(dpy, lock->screen),
179                         0, DefaultDepth(dpy, lock->screen), CopyFromParent,
180                         DefaultVisual(dpy, lock->screen), CWOverrideRedirect | CWBackPixel, &wa);
181         XAllocNamedColor(dpy, DefaultColormap(dpy, lock->screen), "black", &black, &dummy);
182         lock->pmap = XCreateBitmapFromData(dpy, lock->w, curs, 8, 8);
183         invisible = XCreatePixmapCursor(dpy, lock->pmap, lock->pmap, &black, &black, 0, 0);
184         XDefineCursor(dpy, lock->w, invisible);
185         XMapRaised(dpy, lock->w);
186         for(len = 1000; len; len--) {
187                 if(XGrabPointer(dpy, lock->root, False, ButtonPressMask | ButtonReleaseMask | PointerMotionMask,
188                         GrabModeAsync, GrabModeAsync, None, invisible, CurrentTime) == GrabSuccess)
189                         break;
190                 usleep(1000);
191         }
192         if((running = running && (len > 0))) {
193                 for(len = 1000; len; len--) {
194                         if(XGrabKeyboard(dpy, lock->root, True, GrabModeAsync, GrabModeAsync, CurrentTime)
195                                 == GrabSuccess)
196                                 break;
197                         usleep(1000);
198                 }
199                 running = (len > 0);
200         }
201
202         if (!running) {
203                 unlockscreen(dpy, lock);
204                 lock = NULL;
205         }
206
207         return lock;
208 }
209
210 static void
211 usage(void) {
212         fprintf(stderr, "usage: %s -v", __progname);
213         exit(EXIT_FAILURE);
214 }
215
216 int
217 main(int argc, char **argv) {
218 #ifndef HAVE_BSD_AUTH
219         const char *pws;
220 #endif
221         Display *dpy;
222         int nscreens, screen;
223
224         struct st_lock **locks;
225
226         if((argc == 2) && !strcmp("-v", argv[1]))
227                 die("slock-%s, © 2006-2008 Anselm R Garbe", VERSION);
228         else if(argc != 1)
229                 usage();
230
231         if(!getpwuid(getuid()))
232                 die("no passwd entry for you");
233
234 #ifndef HAVE_BSD_AUTH
235         pws = get_password();
236 #endif
237
238         if(!(dpy = XOpenDisplay(0)))
239                 die("cannot open display");
240
241         /* Get the number of screens in display "dpy" and blank them all. */
242         nscreens = ScreenCount(dpy);
243         locks = malloc(sizeof(struct st_lock *) * nscreens);
244         if (locks == NULL)
245                 die("malloc: %s", strerror(errno));
246
247         for (screen = 0; screen < nscreens; screen++)
248                 locks[screen] = lockscreen(dpy, screen);
249
250         XSync(dpy, False);
251
252         /* Everything is now blank. Now wait for the correct password. */
253 #ifdef HAVE_BSD_AUTH
254         read_password(dpy);
255 #else
256         read_password(dpy, pws);
257 #endif
258
259         /* Password ok, unlock everything and quit. */
260         for (screen = 0; screen < nscreens; screen++)
261                 unlockscreen(dpy, locks[screen]);
262
263         free(locks);
264
265         XCloseDisplay(dpy);
266
267         return 0;
268 }