chiark / gitweb /
Add -f option, to fork after a successful locking.
[xtrlock.git] / xtrlock.c
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 <crypt.h>
35 #include <unistd.h>
36 #include <math.h>
37 #include <ctype.h>
38 #include <values.h>
39
40 #ifdef SHADOW_PWD
41 #include <shadow.h>
42 #endif
43
44 #include "lock.bitmap"
45 #include "mask.bitmap"
46 #include "patchlevel.h"
47
48 Display *display;
49 Window window, root;
50
51 #define TIMEOUTPERATTEMPT 30000
52 #define MAXGOODWILL  (TIMEOUTPERATTEMPT*5)
53 #define INITIALGOODWILL MAXGOODWILL
54 #define GOODWILLPORTION 0.3
55
56 struct passwd *pw;
57 int passwordok(const char *s) {
58 #if 0
59   char key[3];
60   char *encr;
61   
62   key[0] = *(pw->pw_passwd);
63   key[1] =  (pw->pw_passwd)[1];
64   key[2] =  0;
65   encr = crypt(s, key);
66   return !strcmp(encr, pw->pw_passwd);
67 #else
68   /* simpler, and should work with crypt() algorithms using longer
69      salt strings (like the md5-based one on freebsd).  --marekm */
70   return !strcmp(crypt(s, pw->pw_passwd), pw->pw_passwd);
71 #endif
72 }
73
74 int main(int argc, char **argv){
75   XEvent ev;
76   KeySym ks;
77   char cbuf[10], rbuf[128]; /* shadow appears to suggest 127 a good value here */
78   int clen, rlen=0;
79   long goodwill= INITIALGOODWILL, timeout= 0;
80   XSetWindowAttributes attrib;
81   Cursor cursor;
82   Pixmap csr_source,csr_mask;
83   XColor csr_fg, csr_bg, dummy, black;
84   int ret, screen, blank = 0, fork_after = 0;
85 #ifdef SHADOW_PWD
86   struct spwd *sp;
87 #endif
88   struct timeval tv;
89   int tvt, gs;
90
91   while (argc > 1) {
92     if ((strcmp(argv[1], "-b") == 0)) {
93       blank = 1;
94       argc--;
95       argv++;
96     } else if ((strcmp(argv[1], "-f") == 0)) {
97       fork_after = 1;
98       argc--;
99       argv++;
100     } else {
101       fprintf(stderr,"xtrlock (version %s); usage: xtrlock [-b] [-f]\n",
102               program_version);
103       exit(1);
104     }
105   }
106   
107   errno=0;  pw= getpwuid(getuid());
108   if (!pw) { perror("password entry for uid not found"); exit(1); }
109 #ifdef SHADOW_PWD
110   sp = getspnam(pw->pw_name);
111   if (sp)
112     pw->pw_passwd = sp->sp_pwdp;
113   endspent();
114 #endif
115
116   /* logically, if we need to do the following then the same 
117      applies to being installed setgid shadow.  
118      we do this first, because of a bug in linux. --jdamery */ 
119   if (setgid(getgid())) { perror("setgid"); exit(1); }
120   /* we can be installed setuid root to support shadow passwords,
121      and we don't need root privileges any longer.  --marekm */
122   if (setuid(getuid())) { perror("setuid"); exit(1); }
123
124   if (strlen(pw->pw_passwd) < 13) {
125     fputs("password entry has no pwd\n",stderr); exit(1);
126   }
127   
128   display= XOpenDisplay(0);
129
130   if (display==NULL) {
131     fprintf(stderr,"xtrlock (version %s): cannot open display\n",
132             program_version);
133     exit(1);
134   }
135   
136   attrib.override_redirect= True;
137
138   if (blank) {
139     screen = DefaultScreen(display);
140     attrib.background_pixel = BlackPixel(display, screen);
141     window= XCreateWindow(display,DefaultRootWindow(display),
142                           0,0,DisplayWidth(display, screen),DisplayHeight(display, screen),
143                           0,DefaultDepth(display, screen), CopyFromParent, DefaultVisual(display, screen),
144                           CWOverrideRedirect|CWBackPixel,&attrib); 
145     XAllocNamedColor(display, DefaultColormap(display, screen), "black", &black, &dummy);
146   } else {
147     window= XCreateWindow(display,DefaultRootWindow(display),
148                           0,0,1,1,0,CopyFromParent,InputOnly,CopyFromParent,
149                           CWOverrideRedirect,&attrib);
150   }
151                         
152   XSelectInput(display,window,KeyPressMask|KeyReleaseMask);
153
154   csr_source= XCreateBitmapFromData(display,window,lock_bits,lock_width,lock_height);
155   csr_mask= XCreateBitmapFromData(display,window,mask_bits,mask_width,mask_height);
156
157   ret = XAllocNamedColor(display,
158                         DefaultColormap(display, DefaultScreen(display)),
159                         "steelblue3",
160                         &dummy, &csr_bg);
161   if (ret==0)
162     XAllocNamedColor(display,
163                     DefaultColormap(display, DefaultScreen(display)),
164                     "black",
165                     &dummy, &csr_bg);
166
167   ret = XAllocNamedColor(display,
168                         DefaultColormap(display,DefaultScreen(display)),
169                         "grey25",
170                         &dummy, &csr_fg);
171   if (ret==0)
172     XAllocNamedColor(display,
173                     DefaultColormap(display, DefaultScreen(display)),
174                     "white",
175                     &dummy, &csr_bg);
176
177
178
179   cursor= XCreatePixmapCursor(display,csr_source,csr_mask,&csr_fg,&csr_bg,
180                               lock_x_hot,lock_y_hot);
181
182   XMapWindow(display,window);
183
184   /*Sometimes the WM doesn't ungrab the keyboard quickly enough if
185    *launching xtrlock from a keystroke shortcut, meaning xtrlock fails
186    *to start We deal with this by waiting (up to 100 times) for 10,000
187    *microsecs and trying to grab each time. If we still fail
188    *(i.e. after 1s in total), then give up, and emit an error
189    */
190   
191   gs=0; /*gs==grab successful*/
192   for (tvt=0 ; tvt<100; tvt++) {
193     ret = XGrabKeyboard(display,window,False,GrabModeAsync,GrabModeAsync,
194                         CurrentTime);
195     if (ret == GrabSuccess) {
196       gs=1;
197       break;
198     }
199     /*grab failed; wait .01s*/
200     tv.tv_sec=0;
201     tv.tv_usec=10000;
202     select(1,NULL,NULL,NULL,&tv);
203   }
204   if (gs==0){
205     fprintf(stderr,"xtrlock (version %s): cannot grab keyboard\n",
206             program_version);
207     exit(1);
208   }
209
210   if (XGrabPointer(display,window,False,(KeyPressMask|KeyReleaseMask)&0,
211                GrabModeAsync,GrabModeAsync,None,
212                cursor,CurrentTime)!=GrabSuccess) {
213     XUngrabKeyboard(display,CurrentTime);
214     fprintf(stderr,"xtrlock (version %s): cannot grab pointer\n",
215             program_version);
216     exit(1);
217   }
218
219   if (fork_after) {
220     pid_t pid = fork();
221     if (pid < 0) {
222       fprintf(stderr,"xtrlock (version %s): cannot fork: %s\n",
223               program_version, strerror(errno));
224       exit(1);
225     } else if (pid > 0) {
226       exit(0);
227     }
228   }
229
230   for (;;) {
231     XNextEvent(display,&ev);
232     switch (ev.type) {
233     case KeyPress:
234       if (ev.xkey.time < timeout) { XBell(display,0); break; }
235       clen= XLookupString(&ev.xkey,cbuf,9,&ks,0);
236       switch (ks) {
237       case XK_Escape: case XK_Clear:
238         rlen=0; break;
239       case XK_Delete: case XK_BackSpace:
240         if (rlen>0) rlen--;
241         break;
242       case XK_Linefeed: case XK_Return:
243         if (rlen==0) break;
244         rbuf[rlen]=0;
245         if (passwordok(rbuf)) goto loop_x;
246         XBell(display,0);
247         rlen= 0;
248         if (timeout) {
249           goodwill+= ev.xkey.time - timeout;
250           if (goodwill > MAXGOODWILL) {
251             goodwill= MAXGOODWILL;
252           }
253         }
254         timeout= -goodwill*GOODWILLPORTION;
255         goodwill+= timeout;
256         timeout+= ev.xkey.time + TIMEOUTPERATTEMPT;
257         break;
258       default:
259         if (clen != 1) break;
260         /* allow space for the trailing \0 */
261         if (rlen < (sizeof(rbuf) - 1)){
262           rbuf[rlen]=cbuf[0];
263           rlen++;
264         }
265         break;
266       }
267       break;
268     default:
269       break;
270     }
271   }
272  loop_x:
273   exit(0);
274 }