X-Git-Url: http://git.hungrycats.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=driver%2Flock.c;h=26441f071261c8fb522fb45ee0a698e2bb3d6fb3;hb=c596d1306f5f92f7eab76224d3b049980a14adcb;hp=7a3ed6a259ffbf3ca1a6a5a6da6fdae625f8c64f;hpb=2a991a811de4c7b22f812682b267b616a809fd9a;p=xscreensaver diff --git a/driver/lock.c b/driver/lock.c index 7a3ed6a2..26441f07 100644 --- a/driver/lock.c +++ b/driver/lock.c @@ -20,9 +20,32 @@ #ifndef NO_LOCKING /* whole file */ #include +#include /* for time() */ #include "xscreensaver.h" #include "resources.h" +#ifdef HAVE_SYSLOG +# include +#endif /* HAVE_SYSLOG */ + +#ifdef HAVE_XHPDISABLERESET +# include + static void hp_lock_reset (saver_info *si, Bool lock_p); +#endif /* HAVE_XHPDISABLERESET */ + +#ifdef HAVE_VT_LOCKSWITCH +# include +# include +# include + static void linux_lock_vt_switch (saver_info *si, Bool lock_p); +#endif /* HAVE_VT_LOCKSWITCH */ + +#ifdef HAVE_XF86VMODE +# include + static void xfree_lock_mode_switch (saver_info *si, Bool lock_p); +#endif /* HAVE_XF86VMODE */ + + #ifdef _VROOT_H_ ERROR! You must not include vroot.h in this file. #endif @@ -35,7 +58,7 @@ extern char *getenv(const char *name); extern int validate_user(char *name, char *password); static Bool -vms_passwd_valid_p(char *pw) +vms_passwd_valid_p(char *pw, Bool verbose_p) { return (validate_user (getenv("USER"), typed_passwd) == 1); } @@ -48,7 +71,7 @@ vms_passwd_valid_p(char *pw) #undef MAX #define MAX(a,b) ((a)>(b)?(a):(b)) -enum passwd_state { pw_read, pw_ok, pw_fail, pw_cancel, pw_time }; +enum passwd_state { pw_read, pw_ok, pw_null, pw_fail, pw_cancel, pw_time }; struct passwd_dialog_data { @@ -58,13 +81,16 @@ struct passwd_dialog_data { int i_beam; float ratio; + Position x, y; Dimension width; Dimension height; + Dimension border_width; char *heading_label; char *body_label; char *user_label; char *passwd_label; + char *date_label; char *user_string; char *passwd_string; @@ -72,6 +98,7 @@ struct passwd_dialog_data { XFontStruct *body_font; XFontStruct *label_font; XFontStruct *passwd_font; + XFontStruct *date_font; Pixel foreground; Pixel background; @@ -93,14 +120,21 @@ struct passwd_dialog_data { Dimension thermo_field_x, thermo_field_y; Dimension thermo_field_height; + + Pixmap save_under; }; +static void draw_passwd_window (saver_info *si); +static void update_passwd_window (saver_info *si, const char *printed_passwd, + float ratio); +static void destroy_passwd_window (saver_info *si); +static void undo_vp_motion (saver_info *si); -void + +static void make_passwd_window (saver_info *si) { struct passwd *p = getpwuid (getuid ()); - int x, y, bw; XSetWindowAttributes attrs; unsigned long attrmask = 0; Screen *screen = si->default_screen->screen; @@ -118,6 +152,7 @@ make_passwd_window (saver_info *si) "Dialog.Label.Label"); pw->passwd_label = get_string_resource ("passwd.passwd.label", "Dialog.Label.Label"); + pw->date_label = get_string_resource ("dateFormat", "DateFormat"); if (!pw->heading_label) pw->heading_label = strdup("ERROR: REESOURCES NOT INSTALLED CORRECTLY"); @@ -125,6 +160,7 @@ make_passwd_window (saver_info *si) pw->body_label = strdup("ERROR: REESOURCES NOT INSTALLED CORRECTLY"); if (!pw->user_label) pw->user_label = strdup("ERROR"); if (!pw->passwd_label) pw->passwd_label = strdup("ERROR"); + if (!pw->date_label) pw->date_label = strdup("ERROR"); /* Put the version number in the label. */ { @@ -157,6 +193,11 @@ make_passwd_window (saver_info *si) if (!pw->passwd_font) pw->passwd_font = XLoadQueryFont (si->dpy, "fixed"); if (f) free (f); + f = get_string_resource("passwd.dateFont", "Dialog.Font"); + pw->date_font = XLoadQueryFont (si->dpy, (f ? f : "fixed")); + if (!pw->date_font) pw->date_font = XLoadQueryFont (si->dpy, "fixed"); + if (f) free (f); + pw->foreground = get_pixel_resource ("passwd.foreground", "Dialog.Foreground", si->dpy, cmap); @@ -291,29 +332,58 @@ make_passwd_window (saver_info *si) attrmask |= CWEventMask; attrs.event_mask = ExposureMask|KeyPressMask; { - Dimension w = WidthOfScreen(screen); - Dimension h = HeightOfScreen(screen); + int x, y, w, h; + get_screen_viewport (si->default_screen, &x, &y, &w, &h, False); if (si->prefs.debug_p) w /= 2; - x = ((w + pw->width) / 2) - pw->width; - y = ((h + pw->height) / 2) - pw->height; - if (x < 0) x = 0; - if (y < 0) y = 0; + pw->x = x + ((w + pw->width) / 2) - pw->width; + pw->y = y + ((h + pw->height) / 2) - pw->height; + if (pw->x < x) pw->x = x; + if (pw->y < y) pw->y = y; } - bw = get_integer_resource ("passwd.borderWidth", "Dialog.BorderWidth"); + pw->border_width = get_integer_resource ("passwd.borderWidth", + "Dialog.BorderWidth"); si->passwd_dialog = XCreateWindow (si->dpy, RootWindowOfScreen(screen), - x, y, pw->width, pw->height, bw, + pw->x, pw->y, pw->width, pw->height, pw->border_width, DefaultDepthOfScreen (screen), InputOutput, DefaultVisualOfScreen(screen), attrmask, &attrs); XSetWindowBackground (si->dpy, si->passwd_dialog, pw->background); + + /* Before mapping the window, save the bits that are underneath the + rectangle the window will occlude. When we lower the window, we + restore these bits. This works, because the running screenhack + has already been sent SIGSTOP, so we know nothing else is drawing + right now! */ + { + XGCValues gcv; + GC gc; + pw->save_under = XCreatePixmap (si->dpy, + si->default_screen->screensaver_window, + pw->width + (pw->border_width*2) + 1, + pw->height + (pw->border_width*2) + 1, + si->default_screen->current_depth); + gcv.function = GXcopy; + gc = XCreateGC (si->dpy, pw->save_under, GCFunction, &gcv); + XCopyArea (si->dpy, si->default_screen->screensaver_window, + pw->save_under, gc, + pw->x - pw->border_width, pw->y - pw->border_width, + pw->width + (pw->border_width*2) + 1, + pw->height + (pw->border_width*2) + 1, + 0, 0); + XFreeGC (si->dpy, gc); + } + XMapRaised (si->dpy, si->passwd_dialog); XSync (si->dpy, False); + move_mouse_grab (si, si->passwd_dialog, si->screens[0].cursor); + undo_vp_motion (si); + si->pw_data = pw; draw_passwd_window (si); @@ -321,7 +391,7 @@ make_passwd_window (saver_info *si) } -void +static void draw_passwd_window (saver_info *si) { passwd_dialog_data *pw = si->pw_data; @@ -336,7 +406,9 @@ draw_passwd_window (saver_info *si) pw->body_font->ascent + pw->body_font->descent + (2 * MAX ((pw->label_font->ascent + pw->label_font->descent), (pw->passwd_font->ascent + pw->passwd_font->descent + - (pw->shadow_width * 4))))); + (pw->shadow_width * 4)))) + + pw->date_font->ascent + pw->date_font->descent + ); spacing = ((pw->height - (2 * pw->shadow_width) - pw->internal_border - height)) / 8; if (spacing < 0) spacing = 0; @@ -444,6 +516,26 @@ draw_passwd_window (saver_info *si) pw->shadow_width, pw->shadow_bottom, pw->shadow_top); + + /* The date, below the text fields + */ + { + char buf[100]; + time_t now = time ((time_t *) 0); + struct tm *tm = localtime (&now); + memset (buf, 0, sizeof(buf)); + strftime (buf, sizeof(buf)-1, pw->date_label, tm); + + XSetFont (si->dpy, gc1, pw->date_font->fid); + y1 += pw->shadow_width; + y1 += (spacing + tb_height); + y1 += spacing/2; + sw = string_width (pw->date_font, buf); + x2 = x1 + x2 - sw; + XDrawString (si->dpy, si->passwd_dialog, gc1, x2, y1, buf, strlen(buf)); + } + + /* the logo */ XSetForeground (si->dpy, gc1, pw->logo_foreground); @@ -510,13 +602,14 @@ draw_passwd_window (saver_info *si) } -void +static void update_passwd_window (saver_info *si, const char *printed_passwd, float ratio) { passwd_dialog_data *pw = si->pw_data; XGCValues gcv; GC gc1, gc2; int x, y; + XRectangle rects[1]; pw->ratio = ratio; gcv.foreground = pw->passwd_foreground; @@ -534,22 +627,34 @@ update_passwd_window (saver_info *si, const char *printed_passwd, float ratio) /* the "password" text field */ + rects[0].x = pw->passwd_field_x; + rects[0].y = pw->passwd_field_y; + rects[0].width = pw->passwd_field_width; + rects[0].height = pw->passwd_field_height; + XFillRectangle (si->dpy, si->passwd_dialog, gc2, - pw->passwd_field_x, pw->passwd_field_y, - pw->passwd_field_width, pw->passwd_field_height); + rects[0].x, rects[0].y, rects[0].width, rects[0].height); + + XSetClipRectangles (si->dpy, gc1, 0, 0, rects, 1, Unsorted); + XDrawString (si->dpy, si->passwd_dialog, gc1, - pw->passwd_field_x + pw->shadow_width, - pw->passwd_field_y + (pw->passwd_font->ascent + - pw->passwd_font->descent), - pw->passwd_string, strlen(pw->passwd_string)); + rects[0].x + pw->shadow_width, + rects[0].y + (pw->passwd_font->ascent + + pw->passwd_font->descent), + pw->passwd_string, strlen(pw->passwd_string)); + + XSetClipMask (si->dpy, gc1, None); /* The I-beam */ if (pw->i_beam != 0) { - x = (pw->passwd_field_x + pw->shadow_width + + x = (rects[0].x + pw->shadow_width + string_width (pw->passwd_font, pw->passwd_string)); - y = pw->passwd_field_y + pw->shadow_width; + y = rects[0].y + pw->shadow_width; + + if (x > rects[0].x + rects[0].width - 1) + x = rects[0].x + rects[0].width - 1; XDrawLine (si->dpy, si->passwd_dialog, gc1, x, y, x, y + pw->passwd_font->ascent); } @@ -581,7 +686,7 @@ update_passwd_window (saver_info *si, const char *printed_passwd, float ratio) } -void +static void destroy_passwd_window (saver_info *si) { passwd_dialog_data *pw = si->pw_data; @@ -593,12 +698,33 @@ destroy_passwd_window (saver_info *si) if (pw->timer) XtRemoveTimeOut (pw->timer); + move_mouse_grab (si, RootWindowOfScreen(si->screens[0].screen), + si->screens[0].cursor); + if (si->passwd_dialog) { XDestroyWindow (si->dpy, si->passwd_dialog); si->passwd_dialog = 0; } + if (pw->save_under) + { + XGCValues gcv; + GC gc; + gcv.function = GXcopy; + gc = XCreateGC (si->dpy, si->default_screen->screensaver_window, + GCFunction, &gcv); + XCopyArea (si->dpy, pw->save_under, + si->default_screen->screensaver_window, gc, + 0, 0, + pw->width + (pw->border_width*2) + 1, + pw->height + (pw->border_width*2) + 1, + pw->x - pw->border_width, pw->y - pw->border_width); + XFreePixmap (si->dpy, pw->save_under); + pw->save_under = 0; + XFreeGC (si->dpy, gc); + } + if (pw->heading_label) free (pw->heading_label); if (pw->body_label) free (pw->body_label); if (pw->user_label) free (pw->user_label); @@ -632,6 +758,200 @@ destroy_passwd_window (saver_info *si) si->pw_data = 0; } + +#ifdef HAVE_XHPDISABLERESET +/* This function enables and disables the C-Sh-Reset hot-key, which + normally resets the X server (logging out the logged-in user.) + We don't want random people to be able to do that while the + screen is locked. + */ +static void +hp_lock_reset (saver_info *si, Bool lock_p) +{ + static Bool hp_locked_p = False; + + /* Calls to XHPDisableReset and XHPEnableReset must be balanced, + or BadAccess errors occur. (It's ok for this to be global, + since it affects the whole machine, not just the current screen.) + */ + if (hp_locked_p == lock_p) + return; + + if (lock_p) + XHPDisableReset (si->dpy); + else + XHPEnableReset (si->dpy); + hp_locked_p = lock_p; +} +#endif /* HAVE_XHPDISABLERESET */ + + + +/* This function enables and disables the C-Sh-F1 ... F12 hot-keys, + which, on Linux systems, switches to another virtual console. + We'd like the whole screen/keyboard to be locked, not just one + virtual console, so this function disables that while the X + screen is locked. + + Unfortunately, this doesn't work -- this ioctl only works when + called by root, and we have disavowed our privileges long ago. + */ +#ifdef HAVE_VT_LOCKSWITCH +static void +linux_lock_vt_switch (saver_info *si, Bool lock_p) +{ + saver_preferences *p = &si->prefs; + static Bool vt_locked_p = False; + const char *dev_console = "/dev/console"; + int fd; + + if (lock_p == vt_locked_p) + return; + + if (lock_p && !p->lock_vt_p) + return; + + fd = open (dev_console, O_RDWR); + if (fd < 0) + { + char buf [255]; + sprintf (buf, "%s: couldn't %s VTs: %s", blurb(), + (lock_p ? "lock" : "unlock"), + dev_console); +#if 1 /* #### doesn't work yet, so don't bother complaining */ + perror (buf); +#endif + return; + } + + if (ioctl (fd, (lock_p ? VT_LOCKSWITCH : VT_UNLOCKSWITCH)) == 0) + { + vt_locked_p = lock_p; + + if (p->verbose_p) + fprintf (stderr, "%s: %s VTs\n", blurb(), + (lock_p ? "locked" : "unlocked")); + } + else + { + char buf [255]; + sprintf (buf, "%s: couldn't %s VTs: ioctl", blurb(), + (lock_p ? "lock" : "unlock")); +#if 0 /* #### doesn't work yet, so don't bother complaining */ + perror (buf); +#endif + } + + close (fd); +} +#endif /* HAVE_VT_LOCKSWITCH */ + + +/* This function enables and disables the C-Alt-Plus and C-Alt-Minus + hot-keys, which normally change the resolution of the X server. + We don't want people to be able to switch the server resolution + while the screen is locked, because if they switch to a higher + resolution, it could cause part of the underlying desktop to become + exposed. + */ +#ifdef HAVE_XF86VMODE + +static int ignore_all_errors_ehandler (Display *dpy, XErrorEvent *error); +static Bool vp_got_error = False; + +static void +xfree_lock_mode_switch (saver_info *si, Bool lock_p) +{ + static Bool mode_locked_p = False; + saver_preferences *p = &si->prefs; + int screen = 0; /* always screen 0 */ + int event, error; + Bool status; + XErrorHandler old_handler; + + if (mode_locked_p == lock_p) + return; + if (!XF86VidModeQueryExtension (si->dpy, &event, &error)) + return; + + XSync (si->dpy, False); + old_handler = XSetErrorHandler (ignore_all_errors_ehandler); + status = XF86VidModeLockModeSwitch (si->dpy, screen, lock_p); + XSync (si->dpy, False); + XSetErrorHandler (old_handler); + if (vp_got_error) status = False; + + if (status) + mode_locked_p = lock_p; + + if (!status && (p->verbose_p || !lock_p)) + /* Only print this when verbose, or when we locked but can't unlock. + I tried printing this message whenever it comes up, but + mode-locking always fails if DontZoom is set in XF86Config. */ + fprintf (stderr, "%s: unable to %s mode switching!\n", + blurb(), (lock_p ? "lock" : "unlock")); + else if (p->verbose_p) + fprintf (stderr, "%s: %s mode switching.\n", + blurb(), (lock_p ? "locked" : "unlocked")); +} + +static int +ignore_all_errors_ehandler (Display *dpy, XErrorEvent *error) +{ + vp_got_error = True; + return 0; +} + +#endif /* HAVE_XF86VMODE */ + + +/* If the viewport has been scrolled since the screen was blanked, + then scroll it back to where it belongs. This function only exists + to patch over a very brief race condition. + */ +static void +undo_vp_motion (saver_info *si) +{ +#ifdef HAVE_XF86VMODE + saver_preferences *p = &si->prefs; + int screen = 0; /* always screen 0 */ + saver_screen_info *ssi = &si->screens[screen]; + int event, error, x, y; + Bool status; + + if (ssi->blank_vp_x == -1 && ssi->blank_vp_y == -1) + return; + if (!XF86VidModeQueryExtension (si->dpy, &event, &error)) + return; + if (!XF86VidModeGetViewPort (si->dpy, 0, &x, &y)) + return; + if (ssi->blank_vp_x == x && ssi->blank_vp_y == y) + return; + + /* We're going to move the viewport. The mouse has just been grabbed on + (and constrained to, thus warped to) the password window, so it is no + longer near the edge of the screen. However, wait a bit anyway, just + to make sure the server drains its last motion event, so that the + screen doesn't continue to scroll after we've reset the viewport. + */ + XSync (si->dpy, False); + usleep (250000); /* 1/4 second */ + XSync (si->dpy, False); + + status = XF86VidModeSetViewPort (si->dpy, screen, + ssi->blank_vp_x, ssi->blank_vp_y); + + if (!status) + fprintf (stderr, "%s: unable to move vp from (%d,%d) back to (%d,%d)!\n", + blurb(), x, y, ssi->blank_vp_x, ssi->blank_vp_y); + else if (p->verbose_p) + fprintf (stderr, "%s: vp moved to (%d,%d); moved it back to (%d,%d).\n", + blurb(), x, y, ssi->blank_vp_x, ssi->blank_vp_y); + +#endif /* HAVE_XF86VMODE */ +} + + /* Interactions */ @@ -668,6 +988,7 @@ passwd_animate_timer (XtPointer closure, XtIntervalId *id) static void handle_passwd_key (saver_info *si, XKeyEvent *event) { + saver_preferences *p = &si->prefs; passwd_dialog_data *pw = si->pw_data; int pw_size = sizeof (pw->typed_passwd) - 1; char *typed_passwd = pw->typed_passwd; @@ -696,10 +1017,18 @@ handle_passwd_key (saver_info *si, XKeyEvent *event) case '\012': case '\015': /* Enter */ if (pw->state != pw_read) ; /* already done? */ - else if (passwd_valid_p (typed_passwd)) - pw->state = pw_ok; + else if (typed_passwd[0] == 0) + pw->state = pw_null; else - pw->state = pw_fail; + { + update_passwd_window (si, "Checking...", pw->ratio); + XSync (si->dpy, False); + if (passwd_valid_p (typed_passwd, p->verbose_p)) + pw->state = pw_ok; + else + pw->state = pw_fail; + update_passwd_window (si, "", pw->ratio); + } break; default: @@ -726,6 +1055,7 @@ handle_passwd_key (saver_info *si, XKeyEvent *event) static void passwd_event_loop (saver_info *si) { + saver_preferences *p = &si->prefs; char *msg = 0; XEvent event; passwd_animate_timer ((XtPointer) si, 0); @@ -744,15 +1074,83 @@ passwd_event_loop (saver_info *si) switch (si->pw_data->state) { case pw_ok: msg = 0; break; + case pw_null: msg = ""; break; case pw_time: msg = "Timed out!"; break; default: msg = "Sorry!"; break; } + if (si->pw_data->state == pw_fail) + si->unlock_failures++; + + if (p->verbose_p) + switch (si->pw_data->state) + { + case pw_ok: + fprintf (stderr, "%s: password correct.\n", blurb()); break; + case pw_fail: + fprintf (stderr, "%s: password incorrect!\n", blurb()); break; + case pw_null: + case pw_cancel: + fprintf (stderr, "%s: password entry cancelled.\n", blurb()); break; + case pw_time: + fprintf (stderr, "%s: password entry timed out.\n", blurb()); break; + default: break; + } + +#ifdef HAVE_SYSLOG + if (si->pw_data->state == pw_fail) + { + /* If they typed a password (as opposed to just hitting return) and + the password was invalid, log it. + */ + struct passwd *pw = getpwuid (getuid ()); + char *d = DisplayString (si->dpy); + char *u = (pw->pw_name ? pw->pw_name : "???"); + int opt = 0; + int fac = 0; + +# ifdef LOG_PID + opt = LOG_PID; +# endif + +# if defined(LOG_AUTHPRIV) + fac = LOG_AUTHPRIV; +# elif defined(LOG_AUTH) + fac = LOG_AUTH; +# else + fac = LOG_DAEMON; +# endif + + if (!d) d = ""; + openlog (progname, opt, fac); + syslog (LOG_NOTICE, "FAILED LOGIN %d ON DISPLAY \"%s\", FOR \"%s\"", + si->unlock_failures, d, u); + closelog (); + } +#endif /* HAVE_SYSLOG */ + + if (si->pw_data->state == pw_fail) + XBell (si->dpy, False); + + if (si->pw_data->state == pw_ok && si->unlock_failures != 0) + { + if (si->unlock_failures == 1) + fprintf (real_stderr, + "%s: WARNING: 1 failed attempt to unlock the screen.\n", + blurb()); + else + fprintf (real_stderr, + "%s: WARNING: %d failed attempts to unlock the screen.\n", + blurb(), si->unlock_failures); + fflush (real_stderr); + + si->unlock_failures = 0; + } + if (msg) { si->pw_data->i_beam = 0; update_passwd_window (si, msg, 0.0); - XBell (si->dpy, False); XSync (si->dpy, False); sleep (1); @@ -765,19 +1163,51 @@ passwd_event_loop (saver_info *si) } } + +static void +handle_typeahead (saver_info *si) +{ + passwd_dialog_data *pw = si->pw_data; + int i; + if (!si->unlock_typeahead) + return; + + i = strlen (si->unlock_typeahead); + if (i >= sizeof(pw->typed_passwd) - 1) + i = sizeof(pw->typed_passwd) - 1; + + memcpy (pw->typed_passwd, si->unlock_typeahead, i); + pw->typed_passwd [i] = 0; + + memset (si->unlock_typeahead, '*', strlen(si->unlock_typeahead)); + si->unlock_typeahead[i] = 0; + update_passwd_window (si, si->unlock_typeahead, pw->ratio); + + free (si->unlock_typeahead); + si->unlock_typeahead = 0; +} + + Bool unlock_p (saver_info *si) { + saver_preferences *p = &si->prefs; Screen *screen = si->default_screen->screen; Colormap cmap = DefaultColormapOfScreen (screen); Bool status; + raise_window (si, True, True, True); + + if (p->verbose_p) + fprintf (stderr, "%s: prompting for password.\n", blurb()); + if (si->pw_data || si->passwd_dialog) destroy_passwd_window (si); make_passwd_window (si); if (cmap) XInstallColormap (si->dpy, cmap); + handle_typeahead (si); passwd_event_loop (si); status = (si->pw_data->state == pw_ok); @@ -789,4 +1219,30 @@ unlock_p (saver_info *si) return status; } -#endif /* !NO_LOCKING -- whole file */ + +void +set_locked_p (saver_info *si, Bool locked_p) +{ + si->locked_p = locked_p; + +#ifdef HAVE_XHPDISABLERESET + hp_lock_reset (si, locked_p); /* turn off/on C-Sh-Reset */ +#endif +#ifdef HAVE_VT_LOCKSWITCH + linux_lock_vt_switch (si, locked_p); /* turn off/on C-Alt-F1 */ +#endif +#ifdef HAVE_XF86VMODE + xfree_lock_mode_switch (si, locked_p); /* turn off/on C-Alt-Plus */ +#endif +} + + +#else /* NO_LOCKING -- whole file */ + +void +set_locked_p (saver_info *si, Bool locked_p) +{ + if (locked_p) abort(); +} + +#endif /* !NO_LOCKING */