No empty .Rs/.Re
[netbsd-mini2440.git] / crypto / dist / heimdal / appl / xnlock / xnlock.c
blob87e497bc097efa2decdfadf1b087652e372aeda4
1 /*
2 * xnlock -- Dan Heller, 1990
3 * "nlock" is a "new lockscreen" type program... something that prevents
4 * screen burnout by making most of it "black" while providing something
5 * of interest to be displayed in case anyone is watching.
6 * "xnlock" is the X11 version of the program.
7 * Original sunview version written by Dan Heller 1985 (not included here).
8 */
9 #ifdef HAVE_CONFIG_H
10 #include <config.h>
11 __RCSID("$Heimdal: xnlock.c 21720 2007-07-28 20:04:05Z lha $"
12 "$NetBSD$");
13 #endif
15 #include <stdio.h>
16 #include <stdlib.h>
17 #include <string.h>
18 #include <signal.h>
19 #include <X11/StringDefs.h>
20 #include <X11/Intrinsic.h>
21 #include <X11/keysym.h>
22 #include <X11/Shell.h>
23 #include <ctype.h>
24 #ifdef HAVE_SYS_TYPES_H
25 #include <sys/types.h>
26 #endif
27 #ifdef HAVE_PWD_H
28 #include <pwd.h>
29 #endif
30 #ifdef HAVE_CRYPT_H
31 #undef des_encrypt
32 #define des_encrypt wingless_pigs_mostly_fail_to_fly
33 #include <crypt.h>
34 #undef des_encrypt
35 #endif
37 #ifdef KRB5
38 #include <krb5.h>
39 #endif
40 #ifdef KRB4
41 #include <krb.h>
42 #endif
43 #if defined(KRB4) || defined(KRB5)
44 #include <kafs.h>
45 #endif
47 #include <roken.h>
48 #include <err.h>
50 static char login[16];
51 static char userprompt[128];
52 #ifdef KRB4
53 static char name[ANAME_SZ];
54 static char inst[INST_SZ];
55 static char realm[REALM_SZ];
56 #endif
57 #ifdef KRB5
58 static krb5_context context;
59 static krb5_principal client;
60 #endif
62 #define font_height(font) (font->ascent + font->descent)
64 static char *SPACE_STRING = " ";
65 static char STRING[] = "****************";
67 #define STRING_LENGTH (sizeof(STRING))
68 #define MAX_PASSWD_LENGTH 256
69 /* (sizeof(STRING)) */
71 #define PROMPT "Password: "
72 #define FAIL_MSG "Sorry, try again"
73 #define LEFT 001
74 #define RIGHT 002
75 #define DOWN 004
76 #define UP 010
77 #define FRONT 020
78 #define X_INCR 3
79 #define Y_INCR 2
80 #define XNLOCK_CTRL 1
81 #define XNLOCK_NOCTRL 0
83 static XtAppContext app;
84 static Display *dpy;
85 static unsigned short Width, Height;
86 static Widget widget;
87 static GC gc;
88 static XtIntervalId timeout_id;
89 static char *words;
90 static int x, y;
91 static Pixel Black, White;
92 static XFontStruct *font;
93 static char root_cpass[128];
94 static char user_cpass[128];
95 static int time_left, prompt_x, prompt_y, time_x, time_y;
96 static unsigned long interval;
97 static Pixmap left0, left1, right0, right1, left_front,
98 right_front, front, down;
100 #define MAXLINES 40
102 #define IS_MOVING 1
103 #define GET_PASSWD 2
104 static int state; /* indicates states: walking or getting passwd */
106 static int ALLOW_LOGOUT = (60*10); /* Allow logout after nn seconds */
107 #define LOGOUT_PASSWD "enuHDmTo5Lq4g" /* when given password "LOGOUT" */
108 static time_t locked_at;
110 struct appres_t {
111 Pixel bg;
112 Pixel fg;
113 XFontStruct *font;
114 Boolean ignore_passwd;
115 Boolean do_reverse;
116 Boolean accept_root;
117 char *text, *text_prog, *file, *logoutPasswd;
118 Boolean no_screensaver;
119 Boolean destroytickets;
120 } appres;
122 static XtResource resources[] = {
123 { XtNbackground, XtCBackground, XtRPixel, sizeof(Pixel),
124 XtOffsetOf(struct appres_t, bg), XtRString, "black" },
126 { XtNforeground, XtCForeground, XtRPixel, sizeof(Pixel),
127 XtOffsetOf(struct appres_t, fg), XtRString, "white" },
129 { XtNfont, XtCFont, XtRFontStruct, sizeof (XFontStruct *),
130 XtOffsetOf(struct appres_t, font),
131 XtRString, "-*-new century schoolbook-*-*-*-18-*" },
133 { "ignorePasswd", "IgnorePasswd", XtRBoolean, sizeof(Boolean),
134 XtOffsetOf(struct appres_t,ignore_passwd),XtRImmediate,(XtPointer)False },
136 { "acceptRootPasswd", "AcceptRootPasswd", XtRBoolean, sizeof(Boolean),
137 XtOffsetOf(struct appres_t, accept_root), XtRImmediate, (XtPointer)True },
139 { "text", "Text", XtRString, sizeof(String),
140 XtOffsetOf(struct appres_t, text), XtRString, "I'm out running around." },
142 { "program", "Program", XtRString, sizeof(String),
143 XtOffsetOf(struct appres_t, text_prog), XtRImmediate, NULL },
145 { "file", "File", XtRString, sizeof(String),
146 XtOffsetOf(struct appres_t,file), XtRImmediate, NULL },
148 { "logoutPasswd", "logoutPasswd", XtRString, sizeof(String),
149 XtOffsetOf(struct appres_t, logoutPasswd), XtRString, LOGOUT_PASSWD },
151 { "noScreenSaver", "NoScreenSaver", XtRBoolean, sizeof(Boolean),
152 XtOffsetOf(struct appres_t,no_screensaver), XtRImmediate, (XtPointer)True },
154 { "destroyTickets", "DestroyTickets", XtRBoolean, sizeof(Boolean),
155 XtOffsetOf(struct appres_t,destroytickets), XtRImmediate, (XtPointer)True },
158 static XrmOptionDescRec options[] = {
159 { "-fg", ".foreground", XrmoptionSepArg, NULL },
160 { "-foreground", ".foreground", XrmoptionSepArg, NULL },
161 { "-fn", ".font", XrmoptionSepArg, NULL },
162 { "-font", ".font", XrmoptionSepArg, NULL },
163 { "-ip", ".ignorePasswd", XrmoptionNoArg, "True" },
164 { "-noip", ".ignorePasswd", XrmoptionNoArg, "False" },
165 { "-ar", ".acceptRootPasswd", XrmoptionNoArg, "True" },
166 { "-noar", ".acceptRootPasswd", XrmoptionNoArg, "False" },
167 { "-nonoscreensaver", ".noScreenSaver", XrmoptionNoArg, "False" },
168 { "-nodestroytickets", ".destroyTickets", XrmoptionNoArg, "False" },
171 static char*
172 get_words(void)
174 FILE *pp = NULL;
175 static char buf[512];
176 long n;
178 if (appres.text_prog) {
179 pp = popen(appres.text_prog, "r");
180 if (!pp) {
181 warn("popen %s", appres.text_prog);
182 return appres.text;
184 n = fread(buf, 1, sizeof(buf) - 1, pp);
185 buf[n] = 0;
186 pclose(pp);
187 return buf;
189 if (appres.file) {
190 pp = fopen(appres.file, "r");
191 if (!pp) {
192 warn("fopen %s", appres.file);
193 return appres.text;
195 n = fread(buf, 1, sizeof(buf) - 1, pp);
196 buf[n] = 0;
197 fclose(pp);
198 return buf;
201 return appres.text;
204 static void
205 usage(int exit_code)
207 fprintf(stderr, "usage: %s [options] [message]\n", getprogname());
208 fprintf(stderr, "-fg color foreground color\n");
209 fprintf(stderr, "-bg color background color\n");
210 fprintf(stderr, "-rv reverse foreground/background colors\n");
211 fprintf(stderr, "-nrv no reverse video\n");
212 fprintf(stderr, "-ip ignore passwd\n");
213 fprintf(stderr, "-nip don't ignore passwd\n");
214 fprintf(stderr, "-ar accept root's passwd to unlock\n");
215 fprintf(stderr, "-nar don't accept root's passwd\n");
216 fprintf(stderr, "-f [file] message is read from file or ~/.msgfile\n");
217 fprintf(stderr, "-prog program text is gotten from executing `program'\n");
218 fprintf(stderr, "-nodestroytickets keep kerberos tickets\n");
219 fprintf(stderr, "--version\n");
220 fprintf(stderr, "--help\n");
221 exit(exit_code);
224 static void
225 init_words (int argc, char **argv)
227 int i = 0;
229 while(argv[i]) {
230 if(strcmp(argv[i], "-p") == 0
231 || strcmp(argv[i], "-prog") == 0) {
232 i++;
233 if(argv[i]) {
234 appres.text_prog = argv[i];
235 i++;
236 } else {
237 warnx ("-p requires an argument");
238 usage(1);
240 } else if(strcmp(argv[i], "-f") == 0) {
241 i++;
242 if(argv[i]) {
243 appres.file = argv[i];
244 i++;
245 } else {
246 int ret;
247 ret = asprintf (&appres.file,
248 "%s/.msgfile", getenv("HOME"));
249 if (ret == -1)
250 errx (1, "cannot allocate memory for message");
252 } else if(strcmp(argv[i], "--version") == 0) {
253 print_version(NULL);
254 exit(0);
255 } else if(strcmp(argv[i], "--help") == 0) {
256 usage(0);
257 } else {
258 int j;
259 int len = 1;
260 for(j = i; argv[j]; j++)
261 len += strlen(argv[j]) + 1;
262 appres.text = malloc(len);
263 if (appres.text == NULL)
264 errx (1, "cannot allocate memory for message");
265 appres.text[0] = 0;
266 for(; i < j; i++){
267 strlcat(appres.text, argv[i], len);
268 strlcat(appres.text, " ", len);
274 static void
275 ScreenSaver(int save)
277 static int timeout, interval, prefer_blank, allow_exp;
278 if(!appres.no_screensaver){
279 if (save) {
280 XGetScreenSaver(dpy, &timeout, &interval,
281 &prefer_blank, &allow_exp);
282 XSetScreenSaver(dpy, 0, interval, prefer_blank, allow_exp);
283 } else
284 /* restore state */
285 XSetScreenSaver(dpy, timeout, interval, prefer_blank, allow_exp);
289 /* Forward decls necessary */
290 static void talk(int force_erase);
291 static unsigned long look(void);
293 static int
294 zrefresh(void)
296 switch (fork()) {
297 case -1:
298 warn ("zrefresh: fork");
299 return -1;
300 case 0:
301 /* Child */
302 execlp("zrefresh", "zrefresh", NULL);
303 execl(BINDIR "/zrefresh", "zrefresh", NULL);
304 return -1;
305 default:
306 /* Parent */
307 break;
309 return 0;
312 static void
313 leave(void)
315 XUngrabPointer(dpy, CurrentTime);
316 XUngrabKeyboard(dpy, CurrentTime);
317 ScreenSaver(0);
318 XCloseDisplay(dpy);
319 zrefresh();
320 exit(0);
323 static void
324 walk(int dir)
326 int incr = 0;
327 static int lastdir;
328 static int up = 1;
329 static Pixmap frame;
331 XSetForeground(dpy, gc, White);
332 XSetBackground(dpy, gc, Black);
333 if (dir & (LEFT|RIGHT)) { /* left/right movement (mabye up/down too) */
334 up = -up; /* bouncing effect (even if hit a wall) */
335 if (dir & LEFT) {
336 incr = X_INCR;
337 frame = (up < 0) ? left0 : left1;
338 } else {
339 incr = -X_INCR;
340 frame = (up < 0) ? right0 : right1;
342 if ((lastdir == FRONT || lastdir == DOWN) && dir & UP) {
343 /* workaround silly bug that leaves screen dust when
344 * guy is facing forward or down and moves up-left/right.
346 XCopyPlane(dpy, frame, XtWindow(widget), gc, 0, 0, 64,64, x, y, 1L);
347 XFlush(dpy);
349 /* note that maybe neither UP nor DOWN is set! */
350 if (dir & UP && y > Y_INCR)
351 y -= Y_INCR;
352 else if (dir & DOWN && y < (int)Height - 64)
353 y += Y_INCR;
355 /* Explicit up/down movement only (no left/right) */
356 else if (dir == UP)
357 XCopyPlane(dpy, front, XtWindow(widget), gc,
358 0,0, 64,64, x, y -= Y_INCR, 1L);
359 else if (dir == DOWN)
360 XCopyPlane(dpy, down, XtWindow(widget), gc,
361 0,0, 64,64, x, y += Y_INCR, 1L);
362 else if (dir == FRONT && frame != front) {
363 if (up > 0)
364 up = -up;
365 if (lastdir & LEFT)
366 frame = left_front;
367 else if (lastdir & RIGHT)
368 frame = right_front;
369 else
370 frame = front;
371 XCopyPlane(dpy, frame, XtWindow(widget), gc, 0, 0, 64,64, x, y, 1L);
373 if (dir & LEFT)
374 while(--incr >= 0) {
375 XCopyPlane(dpy, frame, XtWindow(widget), gc,
376 0,0, 64,64, --x, y+up, 1L);
377 XFlush(dpy);
379 else if (dir & RIGHT)
380 while(++incr <= 0) {
381 XCopyPlane(dpy, frame, XtWindow(widget), gc,
382 0,0, 64,64, ++x, y+up, 1L);
383 XFlush(dpy);
385 lastdir = dir;
388 static long
389 my_random (void)
391 #ifdef HAVE_RANDOM
392 return random();
393 #else
394 return rand();
395 #endif
398 static int
399 think(void)
401 if (my_random() & 1)
402 walk(FRONT);
403 if (my_random() & 1) {
404 words = get_words();
405 return 1;
407 return 0;
410 static void
411 move(XtPointer _p, XtIntervalId *_id)
413 static int length, dir;
415 if (!length) {
416 int tries = 0;
417 dir = 0;
418 if ((my_random() & 1) && think()) {
419 talk(0); /* sets timeout to itself */
420 return;
422 if (!(my_random() % 3) && (interval = look())) {
423 timeout_id = XtAppAddTimeOut(app, interval, move, NULL);
424 return;
426 interval = 20 + my_random() % 100;
427 do {
428 if (!tries)
429 length = Width/100 + my_random() % 90, tries = 8;
430 else
431 tries--;
432 switch (my_random() % 8) {
433 case 0:
434 if (x - X_INCR*length >= 5)
435 dir = LEFT;
436 case 1:
437 if (x + X_INCR*length <= (int)Width - 70)
438 dir = RIGHT;
439 case 2:
440 if (y - (Y_INCR*length) >= 5)
441 dir = UP, interval = 40;
442 case 3:
443 if (y + Y_INCR*length <= (int)Height - 70)
444 dir = DOWN, interval = 20;
445 case 4:
446 if (x - X_INCR*length >= 5 && y - (Y_INCR*length) >= 5)
447 dir = (LEFT|UP);
448 case 5:
449 if (x + X_INCR * length <= (int)Width - 70 &&
450 y-Y_INCR * length >= 5)
451 dir = (RIGHT|UP);
452 case 6:
453 if (x - X_INCR * length >= 5 &&
454 y + Y_INCR * length <= (int)Height - 70)
455 dir = (LEFT|DOWN);
456 case 7:
457 if (x + X_INCR*length <= (int)Width - 70 &&
458 y + Y_INCR*length <= (int)Height - 70)
459 dir = (RIGHT|DOWN);
461 } while (!dir);
463 walk(dir);
464 --length;
465 timeout_id = XtAppAddTimeOut(app, interval, move, NULL);
468 static void
469 post_prompt_box(Window window)
471 int width = (Width / 3);
472 int height = font_height(font) * 6;
473 int box_x, box_y;
475 /* make sure the entire nose icon fits in the box */
476 if (height < 100)
477 height = 100;
479 if(width < 105 + font->max_bounds.width*STRING_LENGTH)
480 width = 105 + font->max_bounds.width*STRING_LENGTH;
481 box_x = (Width - width) / 2;
482 time_x = prompt_x = box_x + 105;
484 time_y = prompt_y = Height / 2;
485 box_y = prompt_y - 3 * font_height(font);
487 /* erase current guy -- text message may still exist */
488 XSetForeground(dpy, gc, Black);
489 XFillRectangle(dpy, window, gc, x, y, 64, 64);
490 talk(1); /* forcefully erase message if one is being displayed */
491 /* Clear area in middle of screen for prompt box */
492 XSetForeground(dpy, gc, White);
493 XFillRectangle(dpy, window, gc, box_x, box_y, width, height);
495 /* make a box that's 5 pixels thick. Then add a thin box inside it */
496 XSetForeground(dpy, gc, Black);
497 XSetLineAttributes(dpy, gc, 5, 0, 0, 0);
498 XDrawRectangle(dpy, window, gc, box_x+5, box_y+5, width-10, height-10);
499 XSetLineAttributes(dpy, gc, 0, 0, 0, 0);
500 XDrawRectangle(dpy, window, gc, box_x+12, box_y+12, width-23, height-23);
502 XDrawString(dpy, window, gc,
503 prompt_x, prompt_y-font_height(font),
504 userprompt, strlen(userprompt));
505 XDrawString(dpy, window, gc, prompt_x, prompt_y, PROMPT, strlen(PROMPT));
506 /* set background for copyplane and DrawImageString; need reverse video */
507 XSetBackground(dpy, gc, White);
508 XCopyPlane(dpy, right0, window, gc, 0,0, 64,64,
509 box_x + 20, box_y + (height - 64)/2, 1L);
510 prompt_x += XTextWidth(font, PROMPT, strlen(PROMPT));
511 time_y += 2*font_height(font);
514 static void
515 RaiseWindow(Widget w, XEvent *ev, String *s, Cardinal *n)
517 Widget x;
518 if(!XtIsRealized(w))
519 return;
520 x = XtParent(w);
521 XRaiseWindow(dpy, XtWindow(x));
525 static void
526 ClearWindow(Widget w, XEvent *_event, String *_s, Cardinal *_n)
528 XExposeEvent *event = (XExposeEvent *)_event;
529 if (!XtIsRealized(w))
530 return;
531 XClearArea(dpy, XtWindow(w), event->x, event->y,
532 event->width, event->height, False);
533 if (state == GET_PASSWD)
534 post_prompt_box(XtWindow(w));
535 if (timeout_id == 0 && event->count == 0) {
536 timeout_id = XtAppAddTimeOut(app, 1000L, move, NULL);
537 /* first grab the input focus */
538 XSetInputFocus(dpy, XtWindow(w), RevertToPointerRoot, CurrentTime);
539 /* now grab the pointer and keyboard and contrain to this window */
540 XGrabPointer(dpy, XtWindow(w), TRUE, 0, GrabModeAsync,
541 GrabModeAsync, XtWindow(w), None, CurrentTime);
545 static void
546 countdown(XtPointer _t, XtIntervalId *_d)
548 int *timeout = (int *)_t;
549 char buf[128];
550 time_t seconds;
552 if (--(*timeout) < 0) {
553 XExposeEvent event;
554 XtRemoveTimeOut(timeout_id);
555 state = IS_MOVING;
556 event.x = event.y = 0;
557 event.width = Width, event.height = Height;
558 ClearWindow(widget, (XEvent *)&event, 0, 0);
559 timeout_id = XtAppAddTimeOut(app, 200L, move, NULL);
560 return;
562 seconds = time(0) - locked_at;
563 if (seconds >= 3600)
564 snprintf(buf, sizeof(buf),
565 "Locked for %d:%02d:%02d ",
566 (int)seconds/3600, (int)seconds/60%60, (int)seconds%60);
567 else
568 snprintf(buf, sizeof(buf),
569 "Locked for %2d:%02d ",
570 (int)seconds/60, (int)seconds%60);
572 XDrawImageString(dpy, XtWindow(widget), gc,
573 time_x, time_y, buf, strlen(buf));
574 XtAppAddTimeOut(app, 1000L, countdown, timeout);
575 return;
578 #ifdef KRB5
579 static int
580 verify_krb5(const char *password)
582 krb5_error_code ret;
583 krb5_ccache id;
584 #ifdef KRB4
585 krb5_boolean get_v4_tgt;
586 #endif
588 krb5_cc_default(context, &id);
589 ret = krb5_verify_user(context,
590 client,
592 password,
594 NULL);
595 if (ret == 0){
596 #ifdef KRB4
597 krb5_appdefault_boolean(context, "xnlock",
598 krb5_principal_get_realm(context, client),
599 "krb4_get_tickets", FALSE, &get_v4_tgt);
600 if(get_v4_tgt) {
601 CREDENTIALS c;
602 krb5_creds mcred, cred;
604 krb5_cc_clear_mcred(&mcred);
606 krb5_make_principal(context, &mcred.server,
607 client->realm,
608 "krbtgt",
609 client->realm,
610 NULL);
611 mcred.client = client;
613 ret = krb5_cc_retrieve_cred(context, id, 0, &mcred, &cred);
614 if(ret == 0) {
615 ret = krb524_convert_creds_kdc_ccache(context, id, &cred, &c);
616 if(ret == 0)
617 tf_setup(&c, c.pname, c.pinst);
618 memset(&c, 0, sizeof(c));
619 krb5_free_cred_contents(context, &cred);
621 krb5_free_principal(context, mcred.server);
623 #endif
624 if (k_hasafs())
625 krb5_afslog(context, id, NULL, NULL);
626 return 0;
628 if (ret != KRB5KRB_AP_ERR_MODIFIED)
629 krb5_warn(context, ret, "verify_krb5");
631 return -1;
633 #endif
635 static int
636 verify(char *password)
639 * First try with root password, if allowed.
641 if ( appres.accept_root
642 && strcmp(crypt(password, root_cpass), root_cpass) == 0)
643 return 0;
646 * Password that log out user
648 if (getuid() != 0 &&
649 geteuid() != 0 &&
650 (time(0) - locked_at) > ALLOW_LOGOUT &&
651 strcmp(crypt(password, appres.logoutPasswd), appres.logoutPasswd) == 0)
653 signal(SIGHUP, SIG_IGN);
654 kill(-1, SIGHUP);
655 sleep(5);
656 /* If the X-server shut down then so will we, else
657 * continue */
658 signal(SIGHUP, SIG_DFL);
662 * Try copy of users password.
664 if (strcmp(crypt(password, user_cpass), user_cpass) == 0)
665 return 0;
668 * Try to verify as user in case password change.
670 if (unix_verify_user(login, password) == 0)
671 return 0;
673 #ifdef KRB5
675 * Try to verify as user with kerberos 5.
677 if(verify_krb5(password) == 0)
678 return 0;
679 #endif
681 #ifdef KRB4
683 int ret;
685 * Try to verify as user with kerberos 4.
687 ret = krb_verify_user(name, inst, realm, password,
688 KRB_VERIFY_NOT_SECURE, NULL);
689 if (ret == KSUCCESS){
690 if (k_hasafs())
691 krb_afslog(NULL, NULL);
692 return 0;
694 if (ret != INTK_BADPW)
695 warnx ("warning: %s",
696 (ret < 0) ? strerror(ret) : krb_get_err_text(ret));
698 #endif
700 return -1;
704 static void
705 GetPasswd(Widget w, XEvent *_event, String *_s, Cardinal *_n)
707 XKeyEvent *event = (XKeyEvent *)_event;
708 static char passwd[MAX_PASSWD_LENGTH];
709 static int cnt;
710 static int is_ctrl = XNLOCK_NOCTRL;
711 char c;
712 KeySym keysym;
713 int echolen;
714 int old_state = state;
716 if (event->type == ButtonPress) {
717 x = event->x, y = event->y;
718 return;
720 if (state == IS_MOVING) {
721 /* guy is running around--change to post prompt box. */
722 XtRemoveTimeOut(timeout_id);
723 state = GET_PASSWD;
724 if (appres.ignore_passwd || !strlen(user_cpass))
725 leave();
726 post_prompt_box(XtWindow(w));
727 cnt = 0;
728 time_left = 30;
729 countdown((XtPointer)&time_left, 0);
731 if (event->type == KeyRelease) {
732 keysym = XLookupKeysym(event, 0);
733 if (keysym == XK_Control_L || keysym == XK_Control_R) {
734 is_ctrl = XNLOCK_NOCTRL;
737 if (event->type != KeyPress)
738 return;
740 time_left = 30;
742 keysym = XLookupKeysym(event, 0);
743 if (keysym == XK_Control_L || keysym == XK_Control_R) {
744 is_ctrl = XNLOCK_CTRL;
745 return;
747 if (!XLookupString(event, &c, 1, &keysym, 0))
748 return;
749 if (keysym == XK_Return || keysym == XK_Linefeed) {
750 passwd[cnt] = 0;
751 if(old_state == IS_MOVING)
752 return;
753 XtRemoveTimeOut(timeout_id);
755 if(verify(passwd) == 0)
756 leave();
758 cnt = 0;
760 XDrawImageString(dpy, XtWindow(widget), gc,
761 time_x, time_y, FAIL_MSG, strlen(FAIL_MSG));
762 time_left = 0;
763 timeout_id = XtAppAddTimeOut(app, 2000L, countdown, &time_left);
764 return;
766 if (keysym == XK_BackSpace || keysym == XK_Delete || keysym == XK_Left) {
767 if (cnt)
768 passwd[cnt--] = ' ';
769 } else if (keysym == XK_u && is_ctrl == XNLOCK_CTRL) {
770 while (cnt) {
771 passwd[cnt--] = ' ';
772 echolen = min(cnt, STRING_LENGTH);
773 XDrawImageString(dpy, XtWindow(w), gc,
774 prompt_x, prompt_y, STRING, echolen);
775 XDrawImageString(dpy, XtWindow(w), gc,
776 prompt_x + XTextWidth(font, STRING, echolen),
777 prompt_y, SPACE_STRING, STRING_LENGTH - echolen + 1);
779 } else if (isprint((unsigned char)c)) {
780 if ((cnt + 1) >= MAX_PASSWD_LENGTH)
781 XBell(dpy, 50);
782 else
783 passwd[cnt++] = c;
784 } else
785 return;
786 echolen = min(cnt, STRING_LENGTH);
787 XDrawImageString(dpy, XtWindow(w), gc,
788 prompt_x, prompt_y, STRING, echolen);
789 XDrawImageString(dpy, XtWindow(w), gc,
790 prompt_x + XTextWidth(font, STRING, echolen),
791 prompt_y, SPACE_STRING, STRING_LENGTH - echolen +1);
794 #include "nose.0.left"
795 #include "nose.1.left"
796 #include "nose.0.right"
797 #include "nose.1.right"
798 #include "nose.left.front"
799 #include "nose.right.front"
800 #include "nose.front"
801 #include "nose.down"
803 static void
804 init_images(void)
806 static Pixmap *images[] = {
807 &left0, &left1, &right0, &right1,
808 &left_front, &right_front, &front, &down
810 static unsigned char *bits[] = {
811 nose_0_left_bits, nose_1_left_bits, nose_0_right_bits,
812 nose_1_right_bits, nose_left_front_bits, nose_right_front_bits,
813 nose_front_bits, nose_down_bits
815 int i;
817 for (i = 0; i < XtNumber(images); i++)
818 if (!(*images[i] =
819 XCreatePixmapFromBitmapData(dpy, DefaultRootWindow(dpy),
820 (char*)(bits[i]), 64, 64, 1, 0, 1)))
821 XtError("Can't load nose images");
824 static void
825 talk(int force_erase)
827 int width = 0, height, Z, total = 0;
828 static int X, Y, talking;
829 static struct { int x, y, width, height; } s_rect;
830 char *p, *p2;
831 char buf[BUFSIZ], args[MAXLINES][256];
833 /* clear what we've written */
834 if (talking || force_erase) {
835 if (!talking)
836 return;
837 if (talking == 2) {
838 XSetForeground(dpy, gc, Black);
839 XDrawString(dpy, XtWindow(widget), gc, X, Y, words, strlen(words));
840 } else if (talking == 1) {
841 XSetForeground(dpy, gc, Black);
842 XFillRectangle(dpy, XtWindow(widget), gc, s_rect.x-5, s_rect.y-5,
843 s_rect.width+10, s_rect.height+10);
845 talking = 0;
846 if (!force_erase)
847 timeout_id = XtAppAddTimeOut(app, 40L,
848 (XtTimerCallbackProc)move,
849 NULL);
850 return;
852 XSetForeground(dpy, gc, White);
853 talking = 1;
854 walk(FRONT);
855 strlcpy (buf, words, sizeof(buf));
856 p = buf;
858 /* possibly avoid a lot of work here
859 * if no CR or only one, then just print the line
861 if (!(p2 = strchr(p, '\n')) || !p2[1]) {
862 int w;
864 if (p2)
865 *p2 = 0;
866 w = XTextWidth(font, words, strlen(words));
867 X = x + 32 - w/2;
868 Y = y - 5 - font_height(font);
869 /* give us a nice 5 pixel margin */
870 if (X < 5)
871 X = 5;
872 else if (X + w + 15 > (int)Width + 5)
873 X = Width - w - 5;
874 if (Y < 5)
875 Y = y + 64 + 5 + font_height(font);
876 XDrawString(dpy, XtWindow(widget), gc, X, Y, words, strlen(words));
877 timeout_id = XtAppAddTimeOut(app, 5000L, (XtTimerCallbackProc)talk,
878 NULL);
879 talking++;
880 return;
883 /* p2 now points to the first '\n' */
884 for (height = 0; p; height++) {
885 int w;
886 *p2 = 0;
887 if ((w = XTextWidth(font, p, p2 - p)) > width)
888 width = w;
889 total += p2 - p; /* total chars; count to determine reading time */
890 strlcpy(args[height], p, sizeof(args[height]));
891 if (height == MAXLINES - 1) {
892 puts("Message too long!");
893 break;
895 p = p2+1;
896 if (!(p2 = strchr(p, '\n')))
897 break;
899 height++;
901 /* Figure out the height and width in pixels (height, width) extend
902 * the new box by 15 pixels on the sides (30 total) top and bottom.
904 s_rect.width = width + 30;
905 s_rect.height = height * font_height(font) + 30;
906 if (x - s_rect.width - 10 < 5)
907 s_rect.x = 5;
908 else
909 if ((s_rect.x = x+32-(s_rect.width+15)/2)
910 + s_rect.width+15 > (int)Width-5)
911 s_rect.x = Width - 15 - s_rect.width;
912 if (y - s_rect.height - 10 < 5)
913 s_rect.y = y + 64 + 5;
914 else
915 s_rect.y = y - 5 - s_rect.height;
917 XSetForeground(dpy, gc, White);
918 XFillRectangle(dpy, XtWindow(widget), gc,
919 s_rect.x-5, s_rect.y-5, s_rect.width+10, s_rect.height+10);
921 /* make a box that's 5 pixels thick. Then add a thin box inside it */
922 XSetForeground(dpy, gc, Black);
923 XSetLineAttributes(dpy, gc, 5, 0, 0, 0);
924 XDrawRectangle(dpy, XtWindow(widget), gc,
925 s_rect.x, s_rect.y, s_rect.width-1, s_rect.height-1);
926 XSetLineAttributes(dpy, gc, 0, 0, 0, 0);
927 XDrawRectangle(dpy, XtWindow(widget), gc,
928 s_rect.x + 7, s_rect.y + 7, s_rect.width - 15,
929 s_rect.height - 15);
931 X = 15;
932 Y = 15 + font_height(font);
934 /* now print each string in reverse order (start at bottom of box) */
935 for (Z = 0; Z < height; Z++) {
936 XDrawString(dpy, XtWindow(widget), gc, s_rect.x+X, s_rect.y+Y,
937 args[Z], strlen(args[Z]));
938 Y += font_height(font);
940 timeout_id = XtAppAddTimeOut(app, (total/15) * 1000,
941 (XtTimerCallbackProc)talk, NULL);
944 static unsigned long
945 look(void)
947 XSetForeground(dpy, gc, White);
948 XSetBackground(dpy, gc, Black);
949 if (my_random() % 3) {
950 XCopyPlane(dpy, (my_random() & 1)? down : front, XtWindow(widget), gc,
951 0, 0, 64,64, x, y, 1L);
952 return 1000L;
954 if (!(my_random() % 5))
955 return 0;
956 if (my_random() % 3) {
957 XCopyPlane(dpy, (my_random() & 1)? left_front : right_front,
958 XtWindow(widget), gc, 0, 0, 64,64, x, y, 1L);
959 return 1000L;
961 if (!(my_random() % 5))
962 return 0;
963 XCopyPlane(dpy, (my_random() & 1)? left0 : right0, XtWindow(widget), gc,
964 0, 0, 64,64, x, y, 1L);
965 return 1000L;
969 main (int argc, char **argv)
971 int i;
972 Widget override;
973 XGCValues gcvalues;
975 setprogname (argv[0]);
978 * Must be setuid root to read /etc/shadow, copy encrypted
979 * passwords here and then switch to sane uid.
982 struct passwd *pw;
983 uid_t uid = getuid();
984 if (!(pw = k_getpwuid(0)))
985 errx (1, "can't get root's passwd!");
986 strlcpy(root_cpass, pw->pw_passwd, sizeof(root_cpass));
988 if (!(pw = k_getpwuid(uid)))
989 errx (1, "Can't get your password entry!");
990 strlcpy(user_cpass, pw->pw_passwd, sizeof(user_cpass));
991 setuid(uid);
992 if (uid != 0 && setuid(0) != -1) {
993 fprintf(stderr, "Failed to drop privileges!\n");
994 exit(1);
996 /* Now we're no longer running setuid root. */
997 strlcpy(login, pw->pw_name, sizeof(login));
1000 #if defined(HAVE_SRANDOMDEV)
1001 srandomdev();
1002 #elif defined(HAVE_RANDOM)
1003 srandom(time(NULL));
1004 #else
1005 srand (time(NULL));
1006 #endif
1007 for (i = 0; i < STRING_LENGTH; i++)
1008 STRING[i] = ((unsigned long)my_random() % ('~' - ' ')) + ' ';
1010 locked_at = time(0);
1012 snprintf(userprompt, sizeof(userprompt), "User: %s", login);
1013 #ifdef KRB4
1014 krb_get_default_principal(name, inst, realm);
1015 snprintf(userprompt, sizeof(userprompt), "User: %s",
1016 krb_unparse_name_long(name, inst, realm));
1017 #endif
1018 #ifdef KRB5
1020 krb5_error_code ret;
1021 char *str;
1023 ret = krb5_init_context(&context);
1024 if (ret)
1025 errx (1, "krb5_init_context failed: %d", ret);
1026 krb5_get_default_principal(context, &client);
1027 krb5_unparse_name(context, client, &str);
1028 snprintf(userprompt, sizeof(userprompt), "User: %s", str);
1029 free(str);
1031 #endif
1033 override = XtVaAppInitialize(&app, "XNlock", options, XtNumber(options),
1034 &argc, argv, NULL,
1035 XtNoverrideRedirect, True,
1036 NULL);
1038 XtVaGetApplicationResources(override,(XtPointer)&appres,
1039 resources,XtNumber(resources),
1040 NULL);
1041 /* the background is black and the little guy is white */
1042 Black = appres.bg;
1043 White = appres.fg;
1045 if (appres.destroytickets) {
1046 #ifdef KRB4
1047 int fd;
1049 dest_tkt(); /* Nuke old ticket file */
1050 /* but keep a place holder */
1051 fd = open (TKT_FILE, O_WRONLY | O_CREAT | O_EXCL, 0600);
1052 if (fd >= 0)
1053 close (fd);
1054 #endif
1057 dpy = XtDisplay(override);
1059 if (dpy == 0)
1060 errx (1, "Error: Can't open display");
1062 Width = DisplayWidth(dpy, DefaultScreen(dpy)) + 2;
1063 Height = DisplayHeight(dpy, DefaultScreen(dpy)) + 2;
1065 for(i = 0; i < ScreenCount(dpy); i++){
1066 Widget shell, core;
1068 struct xxx{
1069 Pixel bg;
1070 }res;
1072 XtResource Res[] = {
1073 { XtNbackground, XtCBackground, XtRPixel, sizeof(Pixel),
1074 XtOffsetOf(struct xxx, bg), XtRString, "black" }
1077 if(i == DefaultScreen(dpy))
1078 continue;
1080 shell = XtVaAppCreateShell(NULL,NULL, applicationShellWidgetClass, dpy,
1081 XtNscreen, ScreenOfDisplay(dpy, i),
1082 XtNoverrideRedirect, True,
1083 XtNx, -1,
1084 XtNy, -1,
1085 NULL);
1087 XtVaGetApplicationResources(shell, (XtPointer)&res,
1088 Res, XtNumber(Res),
1089 NULL);
1091 core = XtVaCreateManagedWidget("_foo", widgetClass, shell,
1092 XtNwidth, DisplayWidth(dpy, i),
1093 XtNheight, DisplayHeight(dpy, i),
1094 XtNbackground, res.bg,
1095 NULL);
1096 XtRealizeWidget(shell);
1099 widget = XtVaCreateManagedWidget("_foo", widgetClass, override,
1100 XtNwidth, Width,
1101 XtNheight, Height,
1102 XtNbackground, Black,
1103 NULL);
1105 init_words(--argc, ++argv);
1106 init_images();
1108 gcvalues.foreground = Black;
1109 gcvalues.background = White;
1112 font = appres.font;
1113 gcvalues.font = font->fid;
1114 gcvalues.graphics_exposures = False;
1115 gc = XCreateGC(dpy, DefaultRootWindow(dpy),
1116 GCForeground | GCBackground | GCGraphicsExposures | GCFont,
1117 &gcvalues);
1119 x = Width / 2;
1120 y = Height / 2;
1121 srand (time(0));
1122 state = IS_MOVING;
1125 static XtActionsRec actions[] = {
1126 { "ClearWindow", ClearWindow },
1127 { "GetPasswd", GetPasswd },
1128 { "RaiseWindow", RaiseWindow },
1130 XtAppAddActions(app, actions, XtNumber(actions));
1131 XtOverrideTranslations(widget,
1132 XtParseTranslationTable(
1133 "<Expose>: ClearWindow() \n"
1134 "<BtnDown>: GetPasswd() \n"
1135 "<Visible>: RaiseWindow() \n"
1136 "<KeyRelease>: GetPasswd() \n"
1137 "<KeyPress>: GetPasswd()"));
1140 XtRealizeWidget(override);
1141 if((i = XGrabPointer(dpy, XtWindow(widget), True, 0, GrabModeAsync,
1142 GrabModeAsync, XtWindow(widget),
1143 None, CurrentTime)) != 0)
1144 errx(1, "Failed to grab pointer (%d)", i);
1146 if((i = XGrabKeyboard(dpy, XtWindow(widget), True, GrabModeAsync,
1147 GrabModeAsync, CurrentTime)) != 0)
1148 errx(1, "Failed to grab keyboard (%d)", i);
1149 ScreenSaver(1);
1150 XtAppMainLoop(app);
1151 exit(0);