1 /*
2 * xtrlock.c
3 *
4 * X Transparent Lock
5 *
6 * Copyright (C)1993,1994 Ian Jackson
7 *
8 * This is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2, or (at your option)
11 * any later version.
12 *
13 * This is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 */
18
19 #include <X11/X.h>
20 #include <X11/Xlib.h>
21 #include <X11/Xutil.h>
22 #include <X11/keysym.h>
23 #include <X11/Xos.h>
24
25 #include <stdlib.h>
26 #include <stdio.h>
27 #include <sys/types.h>
28 #include <sys/stat.h>
29 #include <errno.h>
30 #include <pwd.h>
31 #include <grp.h>
32 #include <limits.h>
33 #include <string.h>
34 #include <unistd.h>
35 #include <unistd.h>
36 #include <math.h>
37 #include <ctype.h>
38 #include <limits.h>
39
40 #ifdef SHADOW_PWD
41 #include <shadow.h>
42 #endif
43
44 #ifdef MULTITOUCH
45 #include <X11/extensions/XInput.h>
46 #include <X11/extensions/XInput2.h>
47 #endif
48
49 #include "lock.bitmap"
50 #include "mask.bitmap"
51 #include "patchlevel.h"
52
53 Display *display;
54 Window window, root;
55
56 #define TIMEOUTPERATTEMPT 30000
57 #define MAXGOODWILL (TIMEOUTPERATTEMPT*5)
58 #define INITIALGOODWILL MAXGOODWILL
59 #define GOODWILLPORTION 0.3
60
61 struct passwd *pw;
passwordok(const char * s)62 int passwordok(const char *s) {
63 #if 0
64 char key[3];
65 char *encr;
66
67 key[0] = *(pw->pw_passwd);
68 key[1] = (pw->pw_passwd)[1];
69 key[2] = 0;
70 encr = crypt(s, key);
71 return !strcmp(encr, pw->pw_passwd);
72 #else
73 /* simpler, and should work with crypt() algorithms using longer
74 salt strings (like the md5-based one on freebsd). --marekm */
75 return !strcmp(crypt(s, pw->pw_passwd), pw->pw_passwd);
76 #endif
77 }
78
79 #if MULTITOUCH
80 XIEventMask evmask;
81
82 /* (Optimistically) attempt to grab multitouch devices which are not
83 * intercepted via XGrabPointer. */
handle_multitouch(Cursor cursor)84 void handle_multitouch(Cursor cursor) {
85 XIDeviceInfo *info;
86 int xi_ndevices;
87
88 info = XIQueryDevice(display, XIAllDevices, &xi_ndevices);
89
90 for (int i=0; i < xi_ndevices; i++) {
91 XIDeviceInfo *dev = &info[i];
92
93 for (int j=0; j < dev->num_classes; j++) {
94 if (dev->classes[j]->type == XITouchClass &&
95 dev->use == XISlavePointer) {
96 XIGrabDevice(display, dev->deviceid, window, CurrentTime, cursor,
97 GrabModeAsync, GrabModeAsync, False, &evmask);
98 }
99 }
100 }
101 XIFreeDeviceInfo(info);
102 }
103 #endif
104
main(int argc,char ** argv)105 int main(int argc, char **argv){
106 XEvent ev;
107 KeySym ks;
108 char cbuf[10], rbuf[128]; /* shadow appears to suggest 127 a good value here */
109 int clen, rlen=0;
110 long goodwill= INITIALGOODWILL, timeout= 0;
111 XSetWindowAttributes attrib;
112 Cursor cursor;
113 Pixmap csr_source,csr_mask;
114 XColor csr_fg, csr_bg, dummy, black;
115 int ret, screen, blank = 0, fork_after = 0;
116 #ifdef SHADOW_PWD
117 struct spwd *sp;
118 #endif
119 struct timeval tv;
120 int tvt, gs;
121
122 if (getenv("WAYLAND_DISPLAY"))
123 fprintf(stderr,"WARNING: Wayland X server detected: xtrlock"
124 " cannot intercept all user input. See xtrlock(1).\n");
125
126 while (argc > 1) {
127 if ((strcmp(argv[1], "-b") == 0)) {
128 blank = 1;
129 argc--;
130 argv++;
131 } else if ((strcmp(argv[1], "-f") == 0)) {
132 fork_after = 1;
133 argc--;
134 argv++;
135 } else {
136 fprintf(stderr,"xtrlock (version %s); usage: xtrlock [-b] [-f]\n",
137 program_version);
138 exit(1);
139 }
140 }
141
142 errno=0; pw= getpwuid(getuid());
143 if (!pw) { perror("password entry for uid not found"); exit(1); }
144 #ifdef SHADOW_PWD
145 sp = getspnam(pw->pw_name);
146 if (sp)
147 pw->pw_passwd = sp->sp_pwdp;
148 endspent();
149 #endif
150
151 /* logically, if we need to do the following then the same
152 applies to being installed setgid shadow.
153 we do this first, because of a bug in linux. --jdamery */
154 if (setgid(getgid())) { perror("setgid"); exit(1); }
155 /* we can be installed setuid root to support shadow passwords,
156 and we don't need root privileges any longer. --marekm */
157 if (setuid(getuid())) { perror("setuid"); exit(1); }
158
159 if (strlen(pw->pw_passwd) < 13) {
160 fputs("password entry has no pwd\n",stderr); exit(1);
161 }
162
163 display= XOpenDisplay(0);
164
165 if (display==NULL) {
166 fprintf(stderr,"xtrlock (version %s): cannot open display\n",
167 program_version);
168 exit(1);
169 }
170
171 #ifdef MULTITOUCH
172 unsigned char mask[XIMaskLen(XI_LASTEVENT)];
173 int xi_major = 2, xi_minor = 2, xi_opcode, xi_error, xi_event;
174
175 if (!XQueryExtension(display, INAME, &xi_opcode, &xi_event, &xi_error)) {
176 fprintf(stderr, "xtrlock (version %s): No X Input extension\n",
177 program_version);
178 exit(1);
179 }
180
181 if (XIQueryVersion(display, &xi_major, &xi_minor) != Success ||
182 xi_major * 10 + xi_minor < 22) {
183 fprintf(stderr,"xtrlock (version %s): Need XI 2.2\n",
184 program_version);
185 exit(1);
186 }
187
188 evmask.mask = mask;
189 evmask.mask_len = sizeof(mask);
190 memset(mask, 0, sizeof(mask));
191 evmask.deviceid = XIAllDevices;
192 XISetMask(mask, XI_HierarchyChanged);
193 XISelectEvents(display, DefaultRootWindow(display), &evmask, 1);
194 #endif
195
196 attrib.override_redirect= True;
197
198 if (blank) {
199 screen = DefaultScreen(display);
200 attrib.background_pixel = BlackPixel(display, screen);
201 window= XCreateWindow(display,DefaultRootWindow(display),
202 0,0,DisplayWidth(display, screen),DisplayHeight(display, screen),
203 0,DefaultDepth(display, screen), CopyFromParent, DefaultVisual(display, screen),
204 CWOverrideRedirect|CWBackPixel,&attrib);
205 XAllocNamedColor(display, DefaultColormap(display, screen), "black", &black, &dummy);
206 } else {
207 window= XCreateWindow(display,DefaultRootWindow(display),
208 0,0,1,1,0,CopyFromParent,InputOnly,CopyFromParent,
209 CWOverrideRedirect,&attrib);
210 }
211
212 XSelectInput(display,window,KeyPressMask|KeyReleaseMask);
213
214 csr_source= XCreateBitmapFromData(display,window,lock_bits,lock_width,lock_height);
215 csr_mask= XCreateBitmapFromData(display,window,mask_bits,mask_width,mask_height);
216
217 ret = XAllocNamedColor(display,
218 DefaultColormap(display, DefaultScreen(display)),
219 "steelblue3",
220 &dummy, &csr_bg);
221 if (ret==0)
222 XAllocNamedColor(display,
223 DefaultColormap(display, DefaultScreen(display)),
224 "black",
225 &dummy, &csr_bg);
226
227 ret = XAllocNamedColor(display,
228 DefaultColormap(display,DefaultScreen(display)),
229 "grey25",
230 &dummy, &csr_fg);
231 if (ret==0)
232 XAllocNamedColor(display,
233 DefaultColormap(display, DefaultScreen(display)),
234 "white",
235 &dummy, &csr_bg);
236
237
238
239 cursor= XCreatePixmapCursor(display,csr_source,csr_mask,&csr_fg,&csr_bg,
240 lock_x_hot,lock_y_hot);
241
242 XMapWindow(display,window);
243
244 /*Sometimes the WM doesn't ungrab the keyboard quickly enough if
245 *launching xtrlock from a keystroke shortcut, meaning xtrlock fails
246 *to start We deal with this by waiting (up to 100 times) for 10,000
247 *microsecs and trying to grab each time. If we still fail
248 *(i.e. after 1s in total), then give up, and emit an error
249 */
250
251 gs=0; /*gs==grab successful*/
252 for (tvt=0 ; tvt<100; tvt++) {
253 ret = XGrabKeyboard(display,window,False,GrabModeAsync,GrabModeAsync,
254 CurrentTime);
255 if (ret == GrabSuccess) {
256 gs=1;
257 break;
258 }
259 /*grab failed; wait .01s*/
260 tv.tv_sec=0;
261 tv.tv_usec=10000;
262 select(1,NULL,NULL,NULL,&tv);
263 }
264 if (gs==0){
265 fprintf(stderr,"xtrlock (version %s): cannot grab keyboard\n",
266 program_version);
267 exit(1);
268 }
269
270 if (XGrabPointer(display,window,False,(KeyPressMask|KeyReleaseMask)&0,
271 GrabModeAsync,GrabModeAsync,None,
272 cursor,CurrentTime)!=GrabSuccess) {
273 XUngrabKeyboard(display,CurrentTime);
274 fprintf(stderr,"xtrlock (version %s): cannot grab pointer\n",
275 program_version);
276 exit(1);
277 }
278
279 #ifdef MULTITOUCH
280 handle_multitouch(cursor);
281 #endif
282
283 if (fork_after) {
284 pid_t pid = fork();
285 if (pid < 0) {
286 fprintf(stderr,"xtrlock (version %s): cannot fork: %s\n",
287 program_version, strerror(errno));
288 exit(1);
289 } else if (pid > 0) {
290 exit(0);
291 }
292 }
293
294 for (;;) {
295 XNextEvent(display,&ev);
296 switch (ev.type) {
297 case KeyPress:
298 if (ev.xkey.time < timeout) { XBell(display,0); break; }
299 clen= XLookupString(&ev.xkey,cbuf,9,&ks,0);
300 switch (ks) {
301 case XK_Escape: case XK_Clear:
302 rlen=0; break;
303 case XK_Delete: case XK_BackSpace:
304 if (rlen>0) rlen--;
305 break;
306 case XK_Linefeed: case XK_Return: case XK_KP_Enter:
307 if (rlen==0) break;
308 rbuf[rlen]=0;
309 if (passwordok(rbuf)) goto loop_x;
310 XBell(display,0);
311 rlen= 0;
312 if (timeout) {
313 goodwill+= ev.xkey.time - timeout;
314 if (goodwill > MAXGOODWILL) {
315 goodwill= MAXGOODWILL;
316 }
317 }
318 timeout= -goodwill*GOODWILLPORTION;
319 goodwill+= timeout;
320 timeout+= ev.xkey.time + TIMEOUTPERATTEMPT;
321 break;
322 default:
323 if (clen != 1) break;
324 /* allow space for the trailing \0 */
325 if (rlen < (sizeof(rbuf) - 1)){
326 rbuf[rlen]=cbuf[0];
327 rlen++;
328 }
329 break;
330 }
331 break;
332 #if MULTITOUCH
333 case GenericEvent:
334 if (ev.xcookie.extension == xi_opcode &&
335 XGetEventData(display,&ev.xcookie) &&
336 ev.xcookie.evtype == XI_HierarchyChanged) {
337 handle_multitouch(cursor);
338 }
339 break;
340 #endif
341 default:
342 break;
343 }
344 }
345 loop_x:
346 exit(0);
347 }
348