xlock.c revision 546
970N/A#pragma ident "@(#)xlock.c 35.11 08/09/18 SMI"
970N/A/*
970N/A * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
970N/A * Use is subject to license terms.
1072N/A *
970N/A * Permission is hereby granted, free of charge, to any person obtaining a
970N/A * copy of this software and associated documentation files (the
970N/A * "Software"), to deal in the Software without restriction, including
970N/A * without limitation the rights to use, copy, modify, merge, publish,
970N/A * distribute, and/or sell copies of the Software, and to permit persons
970N/A * to whom the Software is furnished to do so, provided that the above
970N/A * copyright notice(s) and this permission notice appear in all copies of
970N/A * the Software and that both the above copyright notice(s) and this
970N/A * permission notice appear in supporting documentation.
970N/A *
970N/A * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
970N/A * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
970N/A * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
970N/A * OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
970N/A * HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL
970N/A * INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING
970N/A * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
970N/A * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
970N/A * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
970N/A *
970N/A * Except as contained in this notice, the name of a copyright holder
970N/A * shall not be used in advertising or otherwise to promote the sale, use
970N/A * or other dealings in this Software without prior written authorization
970N/A * of the copyright holder.
970N/A */
970N/A/*
970N/A * xlock.c - X11 client to lock a display and show a screen saver.
970N/A *
970N/A * Copyright (c) 1988-91 by Patrick J. Naughton.
970N/A *
970N/A * Permission to use, copy, modify, and distribute this software and its
970N/A * documentation for any purpose and without fee is hereby granted,
970N/A * provided that the above copyright notice appear in all copies and that
970N/A * both that copyright notice and this permission notice appear in
970N/A * supporting documentation.
970N/A *
970N/A * This file is provided AS IS with no warranties of any kind. The author
970N/A * shall have no liability with respect to the infringement of copyrights,
1105N/A * trade secrets or any patents by this file or any part thereof. In no
970N/A * event will the author be liable for any lost revenue or profits or
970N/A * other special, indirect and consequential damages.
970N/A *
970N/A * Comments and additions should be sent to the author:
970N/A *
970N/A * naughton@eng.sun.com
970N/A *
970N/A * Patrick J. Naughton
970N/A * MS 10-20
970N/A * Sun Laboritories, Inc.
970N/A * 2550 Garcia Ave
970N/A * Mountain View, CA 94043
970N/A *
970N/A * Revision History:
970N/A *
970N/A * 24-Jun-91: make foreground and background color get used on mono.
970N/A * 24-May-91: added -usefirst.
970N/A * 16-May-91: added pyro and random modes.
970N/A * ripped big comment block out of all other files.
970N/A * 08-Jan-91: fix some problems with password entry.
970N/A * removed renicing code.
970N/A * 29-Oct-90: added cast to XFree() arg.
970N/A * added volume arg to call to XBell().
970N/A * 28-Oct-90: center prompt screen.
970N/A * make sure Xlib input buffer does not use up all of swap.
1130N/A * make displayed text come from resource file for better I18N.
970N/A * add backward compatible signal handlers for pre 4.1 machines.
970N/A * 31-Aug-90: added blank mode.
970N/A * added swarm mode.
970N/A * moved usleep() and seconds() out to usleep.c.
970N/A * added SVR4 defines to xlock.h
970N/A * 29-Jul-90: added support for multiple screens to be locked by one xlock.
970N/A * moved global defines to xlock.h
970N/A * removed use of allowsig().
970N/A * 07-Jul-90: reworked commandline args and resources to use Xrm.
970N/A * moved resource processing out to resource.c
970N/A * 02-Jul-90: reworked colors to not use dynamic colormap.
970N/A * 23-May-90: added autoraise when obscured.
970N/A * 15-Apr-90: added hostent alias searching for host authentication.
970N/A * 18-Feb-90: added SunOS3.5 fix.
970N/A * changed -mono -> -color, and -saver -> -lock.
970N/A * allow non-locking screensavers to display on remote machine.
970N/A * added -echokeys to disable echoing of '?'s on input.
970N/A * cleaned up all of the parameters and defaults.
970N/A * 20-Dec-89: added -xhost to allow access control list to be left alone.
970N/A * added -screensaver (don't disable screen saver) for the paranoid.
970N/A * Moved seconds() here from all of the display mode source files.
970N/A * Fixed bug with calling XUngrabHosts() in finish().
970N/A * 19-Dec-89: Fixed bug in GrabPointer.
970N/A * Changed fontname to XLFD style.
970N/A * 23-Sep-89: Added fix to allow local hostname:0 as a display.
970N/A * Put empty case for Enter/Leave events.
970N/A * Moved colormap installation later in startup.
970N/A * 20-Sep-89: Linted and made -saver mode grab the keyboard and mouse.
1105N/A * Replaced SunView code for life mode with Jim Graham's version,
1105N/A * so I could contrib it without legal problems.
970N/A * Sent to expo for X11R4 contrib.
1105N/A * 19-Sep-89: Added '?'s on input.
970N/A * 27-Mar-89: Added -qix mode.
970N/A * Fixed GContext->GC.
970N/A * 20-Mar-89: Added backup font (fixed) if XQueryLoadFont() fails.
970N/A * Changed default font to lucida-sans-24.
970N/A * 08-Mar-89: Added -nice, -mode and -display, built vector for life and hop.
970N/A * 24-Feb-89: Replaced hopalong display with life display from SunView1.
977N/A * 22-Feb-89: Added fix for color servers with n < 8 planes.
970N/A * 16-Feb-89: Updated calling conventions for XCreateHsbColormap();
970N/A * Added -count for number of iterations per color.
970N/A * Fixed defaulting mechanism.
970N/A * Ripped out VMS hacks.
970N/A * Sent to expo for X11R3 contrib.
970N/A * 15-Feb-89: Changed default font to pellucida-sans-18.
970N/A * 20-Jan-89: Added -verbose and fixed usage message.
970N/A * 19-Jan-89: Fixed monochrome gc bug.
970N/A * 16-Dec-88: Added SunView style password prompting.
982N/A * 19-Sep-88: Changed -color to -mono. (default is color on color displays).
970N/A * Added -saver option. (just do display... don't lock.)
970N/A * 31-Aug-88: Added -time option.
970N/A * Removed code for fractals to separate file for modularity.
970N/A * Added signal handler to restore host access.
970N/A * Installs dynamic colormap with a Hue Ramp.
970N/A * If grabs fail then exit.
970N/A * Added VMS Hacks. (password 'iwiwuu').
970N/A * Sent to expo for X11R2 contrib.
970N/A * 08-Jun-88: Fixed root password pointer problem and changed PASSLENGTH to 20.
970N/A * 20-May-88: Added -root to allow root to unlock.
970N/A * 12-Apr-88: Added root password override.
970N/A * Added screen saver override.
970N/A * Removed XGrabServer/XUngrabServer.
982N/A * Added access control handling instead.
970N/A * 01-Apr-88: Added XGrabServer/XUngrabServer for more security.
970N/A * 30-Mar-88: Removed startup password requirement.
970N/A * Removed cursor to avoid phosphor burn.
970N/A * 27-Mar-88: Rotate fractal by 45 degrees clockwise.
1105N/A * 24-Mar-88: Added color support. [-color]
970N/A * wrote the man page.
970N/A * 23-Mar-88: Added HOPALONG routines from Scientific American Sept. 86 p. 14.
1105N/A * added password requirement for invokation
970N/A * removed option for command line password
970N/A * added requirement for display to be "unix:0".
1120N/A * 22-Mar-88: Recieved Walter Milliken's comp.windows.x posting.
1120N/A *
1120N/A */
970N/A
970N/A#include <stdio.h>
970N/A#include <signal.h>
970N/A#include <string.h>
970N/A#ifdef SYSV
970N/A#include <shadow.h>
970N/A#endif
970N/A#include <pwd.h>
1105N/A
1105N/A#include "xlock.h"
1105N/A#include <X11/cursorfont.h>
970N/A#include <X11/Xatom.h>
970N/A
970N/A#ifdef USE_PAM
1105N/A# include <security/pam_appl.h>
970N/A# ifndef XLOCK_PAM_SERVICE
970N/A# define XLOCK_PAM_SERVICE "xlock"
970N/A# endif
970N/A# define PAM_ERROR_PRINT(pamfunc) \
970N/A if (verbose) { \
970N/A fprintf(stderr, "%s: %s failure: %s\n", ProgramName, pamfunc, \
970N/A pam_strerror(pamh, pam_error)); \
970N/A }
970N/A# ifdef sun
970N/A# include <deflt.h>
970N/A# endif
970N/A#endif
970N/A
970N/Achar *ProgramName; /* argv[0] */
970N/Aperscreen Scr[MAXSCREENS];
970N/ADisplay *dsp = NULL; /* server display connection */
970N/Aint screen; /* current screen */
970N/Avoid (*callback) () = NULL;
1057N/Avoid (*init) () = NULL;
970N/A
970N/Astatic int screens; /* number of screens */
970N/Astatic Window win[MAXSCREENS]; /* window used to cover screen */
970N/Astatic Window icon[MAXSCREENS]; /* window used during password typein */
970N/Astatic Window root[MAXSCREENS]; /* convenience pointer to the root window */
970N/Astatic GC textgc[MAXSCREENS]; /* graphics context used for text rendering */
970N/Astatic XColor fgcol[MAXSCREENS];/* used for text rendering */
970N/Astatic XColor bgcol[MAXSCREENS];/* background of text screen */
970N/AXColor ssblack[MAXSCREENS];/* black color for screen saver screen */
970N/AXColor sswhite[MAXSCREENS];/* white color for screen saver screen */
970N/Astatic int iconx[MAXSCREENS]; /* location of left edge of icon */
970N/Astatic int icony[MAXSCREENS]; /* location of top edge of icon */
970N/Astatic Cursor mycursor; /* blank cursor */
1072N/Astatic Cursor passwdcursor; /* cursor used in getPassword */
970N/Astatic Pixmap lockc;
970N/Astatic Pixmap lockm; /* pixmaps for cursor and mask */
970N/Astatic char no_bits[] = {0}; /* dummy array for the blank cursor */
970N/Astatic int passx; /* position of the ?'s */
970N/Astatic int passy;
970N/Astatic XFontStruct *font;
970N/Astatic int sstimeout; /* screen saver parameters */
970N/Astatic int ssinterval;
970N/Astatic int ssblanking;
970N/Astatic int ssexposures;
970N/A
970N/Astatic char buffer[PAM_MAX_RESP_SIZE];
970N/Astatic Bool reallyechokeys = False; /* Echo real keys instead of ?'s */
970N/Astatic Bool stoptryingfornow = False;
970N/A
970N/A#define FALLBACK_FONTNAME "fixed"
970N/A#define ICONW 64
970N/A#define ICONH 64
1003N/A
1003N/A#ifdef DEBUG
970N/A#define WIDTH WidthOfScreen(scr) - 100
970N/A#define HEIGHT HeightOfScreen(scr) - 100
970N/A#define CWMASK CWBackPixel | CWEventMask | CWColormap
970N/A#else
970N/A#define WIDTH WidthOfScreen(scr)
970N/A#define HEIGHT HeightOfScreen(scr)
970N/A#define CWMASK CWOverrideRedirect | CWBackPixel | CWEventMask | CWColormap
1105N/A#endif
970N/A
970N/A#define AllPointerEventMask \
970N/A (ButtonPressMask | ButtonReleaseMask | \
970N/A EnterWindowMask | LeaveWindowMask | \
970N/A PointerMotionMask | PointerMotionHintMask | \
970N/A Button1MotionMask | Button2MotionMask | \
970N/A Button3MotionMask | Button4MotionMask | \
970N/A Button5MotionMask | ButtonMotionMask | \
970N/A KeymapStateMask)
970N/A
970N/A/* VARARGS1 */
970N/Avoid
970N/Aerror(s1, s2)
970N/A char *s1, *s2;
1105N/A{
1105N/A fprintf(stderr, s1, ProgramName, s2);
970N/A exit(1);
1105N/A}
1105N/A
970N/A/*
970N/A * Server access control support.
970N/A */
970N/A
1105N/Astatic XHostAddress *XHosts; /* the list of "friendly" client machines */
970N/Astatic int HostAccessCount; /* the number of machines in XHosts */
970N/Astatic Bool HostAccessState; /* whether or not we even look at the list */
970N/A
970N/Astatic void
970N/AXGrabHosts(dsp)
970N/A Display *dsp;
970N/A{
970N/A XHosts = XListHosts(dsp, &HostAccessCount, &HostAccessState);
970N/A if (XHosts)
970N/A XRemoveHosts(dsp, XHosts, HostAccessCount);
970N/A XEnableAccessControl(dsp);
970N/A}
970N/A
970N/Astatic void
970N/AXUngrabHosts(dsp)
970N/A Display *dsp;
970N/A{
970N/A if (XHosts) {
970N/A XAddHosts(dsp, XHosts, HostAccessCount);
970N/A XFree((char *) XHosts);
970N/A }
970N/A if (HostAccessState == False)
970N/A XDisableAccessControl(dsp);
970N/A}
970N/A
970N/A
970N/A/*
970N/A * Simple wrapper to get an asynchronous grab on the keyboard and mouse.
970N/A * If either grab fails, we sleep for one second and try again since some
970N/A * window manager might have had the mouse grabbed to drive the menu choice
970N/A * that picked "Lock Screen..". If either one fails the second time we print
970N/A * an error message and exit.
970N/A */
970N/Astatic void
970N/AGrabKeyboardAndMouse()
977N/A{
970N/A Status status;
970N/A
970N/A status = XGrabKeyboard(dsp, win[0], True,
1105N/A GrabModeAsync, GrabModeAsync, CurrentTime);
1105N/A if (status != GrabSuccess) {
970N/A sleep(1);
970N/A status = XGrabKeyboard(dsp, win[0], True,
970N/A GrabModeAsync, GrabModeAsync, CurrentTime);
1105N/A
970N/A if (status != GrabSuccess)
970N/A error("%s: couldn't grab keyboard! (%d)\n", status);
970N/A }
970N/A status = XGrabPointer(dsp, win[0], True, AllPointerEventMask,
970N/A GrabModeAsync, GrabModeAsync, None, mycursor,
970N/A CurrentTime);
970N/A if (status != GrabSuccess) {
970N/A sleep(1);
970N/A status = XGrabPointer(dsp, win[0], True, AllPointerEventMask,
970N/A GrabModeAsync, GrabModeAsync, None, mycursor,
970N/A CurrentTime);
970N/A
970N/A if (status != GrabSuccess)
970N/A error("%s: couldn't grab pointer! (%d)\n", status);
970N/A }
970N/A}
970N/A
970N/A
970N/A/*
970N/A * Assuming that we already have an asynch grab on the pointer,
970N/A * just grab it again with a new cursor shape and ignore the return code.
970N/A */
970N/Astatic void
970N/AXChangeGrabbedCursor(cursor)
970N/A Cursor cursor;
970N/A{
970N/A#ifndef DEBUG
970N/A (void) XGrabPointer(dsp, win[0], True, AllPointerEventMask,
970N/A GrabModeAsync, GrabModeAsync, None, cursor, CurrentTime);
970N/A#endif
970N/A}
970N/A
970N/A
970N/A/*
970N/A * Restore all grabs, reset screensaver, restore colormap, close connection.
970N/A */
970N/Astatic void
970N/Afinish()
970N/A{
970N/A XSync(dsp, False);
970N/A if (!nolock && !allowaccess)
970N/A XUngrabHosts(dsp);
970N/A XUngrabPointer(dsp, CurrentTime);
970N/A XUngrabKeyboard(dsp, CurrentTime);
970N/A if (!enablesaver)
970N/A XSetScreenSaver(dsp, sstimeout, ssinterval, ssblanking, ssexposures);
970N/A XFlush(dsp);
970N/A XCloseDisplay(dsp);
970N/A}
1046N/A
1046N/A
1046N/Astatic int
1046N/AReadXString(s, slen)
970N/A char *s;
970N/A int slen;
970N/A{
970N/A XEvent event;
970N/A char keystr[20];
970N/A char c;
970N/A int i;
970N/A int bp;
1046N/A int len;
1046N/A int thisscreen = screen;
970N/A char pwbuf[PAM_MAX_RESP_SIZE];
970N/A
970N/A for (screen = 0; screen < screens; screen++)
970N/A if (thisscreen == screen)
970N/A init(icon[screen]);
970N/A else
970N/A init(win[screen]);
970N/A bp = 0;
970N/A *s = 0;
970N/A while (True) {
970N/A unsigned long lasteventtime = seconds();
970N/A while (!XPending(dsp)) {
1130N/A for (screen = 0; screen < screens; screen++)
1130N/A if (thisscreen == screen)
1130N/A callback(icon[screen]);
1130N/A else
1130N/A callback(win[screen]);
1130N/A XFlush(dsp);
1130N/A usleep(delay);
1130N/A if (seconds() - lasteventtime > timeout) {
1130N/A screen = thisscreen;
1130N/A stoptryingfornow = True;
1130N/A return 1;
1130N/A }
1130N/A }
1130N/A screen = thisscreen;
1130N/A XNextEvent(dsp, &event);
1130N/A switch (event.type) {
1130N/A case KeyPress:
1130N/A len = XLookupString((XKeyEvent *) & event, keystr, 20, NULL, NULL);
1130N/A for (i = 0; i < len; i++) {
1130N/A c = keystr[i];
1130N/A switch (c) {
1130N/A case 8: /* ^H */
1130N/A case 127: /* DEL */
1130N/A if (bp > 0)
1130N/A bp--;
1130N/A break;
1130N/A case 10: /* ^J */
1130N/A case 13: /* ^M */
1130N/A s[bp] = '\0';
970N/A /*
970N/A * eat all events if there are more than enough pending... this
970N/A * keeps the Xlib event buffer from growing larger than all
970N/A * available memory and crashing xlock.
970N/A */
970N/A if (XPending(dsp) > 100) { /* 100 is arbitrarily big enough */
970N/A register Status status;
970N/A do {
970N/A status = XCheckMaskEvent(dsp,
970N/A KeyPressMask | KeyReleaseMask, &event);
970N/A } while (status);
970N/A XBell(dsp, 100);
970N/A }
970N/A return 0;
970N/A case 21: /* ^U */
970N/A bp = 0;
970N/A break;
1130N/A default:
970N/A s[bp] = c;
1130N/A if (bp < slen - 1)
970N/A bp++;
970N/A else
970N/A XSync(dsp, True); /* flush input buffer */
970N/A }
970N/A }
970N/A XSetForeground(dsp, Scr[screen].gc, bgcol[screen].pixel);
970N/A if (echokeys || reallyechokeys) {
970N/A if (reallyechokeys) {
970N/A memcpy(pwbuf, s, slen);
970N/A } else {
970N/A memset(pwbuf, '?', slen);
970N/A }
970N/A
970N/A XFillRectangle(dsp, win[screen], Scr[screen].gc,
970N/A passx, passy - font->ascent,
970N/A XTextWidth(font, pwbuf, slen),
970N/A font->ascent + font->descent);
970N/A XDrawString(dsp, win[screen], textgc[screen],
970N/A passx, passy, pwbuf, bp);
970N/A }
970N/A /*
970N/A * eat all events if there are more than enough pending... this
1130N/A * keeps the Xlib event buffer from growing larger than all
1130N/A * available memory and crashing xlock.
1130N/A */
1130N/A if (XPending(dsp) > 100) { /* 100 is arbitrarily big enough */
1130N/A register Status status;
1130N/A do {
1130N/A status = XCheckMaskEvent(dsp,
1130N/A KeyPressMask | KeyReleaseMask, &event);
1130N/A } while (status);
1130N/A XBell(dsp, 100);
1130N/A }
1130N/A break;
1130N/A
1130N/A case ButtonPress:
1130N/A if (((XButtonEvent *) & event)->window == icon[screen]) {
1130N/A stoptryingfornow = True;
1130N/A return 1;
1130N/A }
970N/A break;
970N/A
970N/A case VisibilityNotify:
970N/A if (event.xvisibility.state != VisibilityUnobscured) {
970N/A#ifndef DEBUG
970N/A XRaiseWindow(dsp, win[screen]);
970N/A#endif
970N/A s[0] = '\0';
970N/A return 1;
970N/A }
970N/A break;
970N/A
970N/A case KeymapNotify:
970N/A case KeyRelease:
970N/A case ButtonRelease:
970N/A case MotionNotify:
970N/A case LeaveNotify:
970N/A case EnterNotify:
970N/A break;
970N/A
970N/A default:
970N/A fprintf(stderr, "%s: unexpected event: %d\n",
970N/A ProgramName, event.type);
1120N/A break;
1120N/A }
1120N/A }
1120N/A}
1120N/A
970N/A
970N/Astatic int
970N/ACheckPassword()
970N/A{
970N/A#ifdef SYSV
970N/A struct spwd *rspw, *uspw;
1130N/A struct passwd *upw;
970N/A char *user;
970N/A#else
970N/A struct passwd *rpw, *upw;
970N/A#endif /* SYSV */
970N/A
970N/A#ifdef SYSV
970N/A rspw = getspnam("root");
upw = (struct passwd *)getpwuid(getuid());
if (upw == NULL) { /* should not pass NULL to getspnam */
user = "";
}
else {
user = upw->pw_name;
}
uspw = getspnam(user);
if (!uspw) {
if (allowroot) {
if (!rspw)
return(1);
else
return(0);
}
return(1);
}
#else /* SYSV */
rpw = (struct passwd *)getpwuid(0);
upw = (struct passwd *)getpwuid(getuid());
if (!upw) {
if (allowroot) {
if (!rpw)
return(1);
else
return(0);
}
return(1);
}
#endif /* SYSV */
return(0);
}
static void passwordPrompt(const char *prompt)
{
int y, left;
Screen *scr = ScreenOfDisplay(dsp, screen);
left = iconx[screen] + ICONW + font->max_bounds.width;
y = icony[screen] + font->ascent + font->ascent + font->descent + 2;
XSetForeground(dsp, Scr[screen].gc, bgcol[screen].pixel);
XFillRectangle(dsp, win[screen], Scr[screen].gc,
left, y - font->ascent, WIDTH - left,
font->ascent + font->descent + 2);
XDrawString(dsp, win[screen], textgc[screen],
left, y, prompt, strlen(prompt));
XDrawString(dsp, win[screen], textgc[screen],
left + 1, y, prompt, strlen(prompt));
passx = left + 1 + XTextWidth(font, prompt, strlen(prompt))
+ XTextWidth(font, " ", 1);
passy = y;
}
static void displayTextInfo(const char *infoMsg)
{
int y;
Screen *scr = ScreenOfDisplay(dsp, screen);
y = icony[screen] + ICONH + font->ascent + 2;
XSetForeground(dsp, Scr[screen].gc, bgcol[screen].pixel);
XFillRectangle(dsp, win[screen], Scr[screen].gc,
iconx[screen], y - font->ascent,
WIDTH - iconx[screen],
font->ascent + font->descent + 2);
XDrawString(dsp, win[screen], textgc[screen],
iconx[screen], y, infoMsg, strlen(infoMsg));
}
#ifdef USE_PAM
static int pamconv(int num_msg, struct pam_message **msg,
struct pam_response **response, void *appdata_ptr)
{
int i;
int status = PAM_SUCCESS;
struct pam_message *m;
struct pam_response *r;
*response = calloc(num_msg, sizeof (struct pam_response));
if (*response == NULL)
return (PAM_BUF_ERR);
m = *msg;
r = *response;
for (i = 0; i < num_msg; i++ , m++ , r++) {
#ifdef DEBUG
if (verbose) {
fprintf(stderr, "pam_msg: %d: '%s'\n", m->msg_style, m->msg);
}
#endif
switch (m->msg_style) {
case PAM_ERROR_MSG:
case PAM_TEXT_INFO:
displayTextInfo(m->msg);
break;
case PAM_PROMPT_ECHO_ON:
reallyechokeys = True;
/* FALLTHRU */
case PAM_PROMPT_ECHO_OFF:
passwordPrompt(m->msg);
if (ReadXString(buffer, PAM_MAX_RESP_SIZE)) {
/* timeout or other error */
status = PAM_CONV_ERR;
i = num_msg;
} else {
r->resp = strdup(buffer);
if (r->resp == NULL) {
status = PAM_BUF_ERR;
i = num_msg;
}
#ifdef DEBUG
if (verbose) {
fprintf(stderr, "pam_resp: '%s'\n", r->resp);
}
#endif
}
reallyechokeys = False;
break;
default:
if (verbose) {
fprintf(stderr, "%s: Unknown PAM msg_style: %d\n",
ProgramName, m->msg_style);
}
}
}
if (status != PAM_SUCCESS) {
/* free responses */
r = *response;
for (i = 0; i < num_msg; i++, r++) {
if (r->resp)
free(r->resp);
}
free(*response);
*response = NULL;
}
return status;
}
#endif
#ifdef sun
#include <syslog.h>
#include <bsm/adt.h>
#include <bsm/adt_event.h>
/*
* audit_lock - audit entry to screenlock
*
* Entry Process running with appropriate privilege to generate
* audit records and real uid of the user.
*
* Exit ADT_screenlock audit record written.
*/
static void
audit_lock(void)
{
adt_session_data_t *ah; /* audit session handle */
adt_event_data_t *event; /* audit event handle */
/* Audit start of screen lock -- equivalent to logout ;-) */
if (adt_start_session(&ah, NULL, ADT_USE_PROC_DATA) != 0) {
syslog(LOG_AUTH | LOG_ALERT, "adt_start_session: %m");
return;
}
if ((event = adt_alloc_event(ah, ADT_screenlock)) == NULL) {
syslog(LOG_AUTH | LOG_ALERT,
"adt_alloc_event(ADT_screenlock): %m");
} else {
if (adt_put_event(event, ADT_SUCCESS, ADT_SUCCESS) != 0) {
syslog(LOG_AUTH | LOG_ALERT,
"adt_put_event(ADT_screenlock): %m");
}
adt_free_event(event);
}
(void) adt_end_session(ah);
}
/*
* audit_unlock - audit screen unlock
*
* Entry Process running with appropriate privilege to generate
* audit records and real uid of the user.
* pam_status = PAM error code; reason for failure.
*
* Exit ADT_screenunlock audit record written.
*/
static void
audit_unlock(int pam_status)
{
adt_session_data_t *ah; /* audit session handle */
adt_event_data_t *event; /* audit event handle */
if (adt_start_session(&ah, NULL, ADT_USE_PROC_DATA) != 0) {
syslog(LOG_AUTH | LOG_ALERT,
"adt_start_session(ADT_screenunlock): %m");
return;
}
if ((event = adt_alloc_event(ah, ADT_screenunlock)) == NULL) {
syslog(LOG_AUTH | LOG_ALERT,
"adt_alloc_event(ADT_screenunlock): %m");
} else {
if (adt_put_event(event,
pam_status == PAM_SUCCESS ? ADT_SUCCESS : ADT_FAILURE,
pam_status == PAM_SUCCESS ? ADT_SUCCESS : ADT_FAIL_PAM +
pam_status) != 0) {
syslog(LOG_AUTH | LOG_ALERT,
"adt_put_event(ADT_screenunlock(%s): %m",
pam_strerror(NULL, pam_status));
}
adt_free_event(event);
}
(void) adt_end_session(ah);
}
/*
* audit_passwd - audit password change
* Entry Process running with appropriate privilege to generate
* audit records and real uid of the user.
* pam_status = PAM error code; reason for failure.
*
* Exit ADT_passwd audit record written.
*/
static void
audit_passwd(int pam_status)
{
adt_session_data_t *ah; /* audit session handle */
adt_event_data_t *event; /* audit event handle */
if (adt_start_session(&ah, NULL, ADT_USE_PROC_DATA) != 0) {
syslog(LOG_AUTH | LOG_ALERT,
"adt_start_session(ADT_passwd): %m");
return;
}
if ((event = adt_alloc_event(ah, ADT_passwd)) == NULL) {
syslog(LOG_AUTH | LOG_ALERT,
"adt_alloc_event(ADT_passwd): %m");
} else {
if (adt_put_event(event,
pam_status == PAM_SUCCESS ? ADT_SUCCESS : ADT_FAILURE,
pam_status == PAM_SUCCESS ? ADT_SUCCESS : ADT_FAIL_PAM +
pam_status) != 0) {
syslog(LOG_AUTH | LOG_ALERT,
"adt_put_event(ADT_passwd(%s): %m",
pam_strerror(NULL, pam_status));
}
adt_free_event(event);
}
(void) adt_end_session(ah);
}
#endif /* sun */
static int
getPassword()
{
char *userpass = NULL;
char *rootpass = NULL;
XWindowAttributes xgwa;
int y, left, done;
#ifdef SYSV
struct spwd *rspw, *uspw;
char *suserpass = NULL;
char *srootpass = NULL;
char *user;
#else
char *user = getenv("USER");
#endif /* SYSV */
struct passwd *rpw, *upw;
#ifdef USE_PAM
pam_handle_t *pamh = NULL;
struct pam_conv pc;
Bool use_pam = True;
int pam_error;
int pam_flags = 0;
#endif
const char *authErrMsg = text_invalid;
#ifdef SYSV
rpw = getpwuid(0);
if (rpw) {
user = rpw->pw_name;
rootpass = strdup(rpw->pw_passwd);
rspw = getspnam(user);
if (rspw && rspw->sp_pwdp)
srootpass = strdup(rspw->sp_pwdp);
}
upw = getpwuid(getuid());
if (upw) {
user = upw->pw_name;
userpass = strdup(upw->pw_passwd);
uspw = getspnam(user);
if (uspw && uspw->sp_pwdp)
suserpass = strdup(uspw->sp_pwdp);
}
else
user = "";
#else
rpw = (struct passwd *)getpwuid(0);
if (rpw)
rootpass = strdup(rpw->pw_passwd);
upw = (struct passwd *)getpwuid(getuid());
if (upw)
userpass = strdup(upw->pw_passwd);
#endif /* SYSV */
#ifdef USE_PAM
pc.conv = pamconv;
pam_error = pam_start(XLOCK_PAM_SERVICE, user, &pc, &pamh);
if (pam_error != PAM_SUCCESS) {
use_pam = False;
PAM_ERROR_PRINT("pam_start");
} else {
#ifdef sun
/* Check /etc/default/login to see if we should add
PAM_DISALLOW_NULL_AUTHTOK to pam_flags */
if (defopen("/etc/default/login") == 0) {
char *ptr;
int flags = defcntl(DC_GETFLAGS, 0);
TURNOFF(flags, DC_CASE);
(void) defcntl(DC_SETFLAGS, flags);
if ((ptr = defread("PASSREQ=")) != NULL &&
strcasecmp("YES", ptr) == 0) {
pam_flags |= PAM_DISALLOW_NULL_AUTHTOK;
}
(void) defopen((char *)NULL); /* close current file */
}
#endif
#ifdef SYSV
/* Disable user password non-PAM authentication */
if (userpass) {
memset(userpass, 0, strlen(userpass));
free(userpass);
userpass = NULL;
}
if (suserpass) {
memset(suserpass, 0, strlen(suserpass));
free(suserpass);
suserpass = NULL;
}
#endif
}
#endif /* USE_PAM */
XGetWindowAttributes(dsp, win[screen], &xgwa);
XChangeGrabbedCursor(passwdcursor);
XSetForeground(dsp, Scr[screen].gc, bgcol[screen].pixel);
XFillRectangle(dsp, win[screen], Scr[screen].gc,
0, 0, xgwa.width, xgwa.height);
XMapWindow(dsp, icon[screen]);
XRaiseWindow(dsp, icon[screen]);
left = iconx[screen] + ICONW + font->max_bounds.width;
y = icony[screen] + font->ascent;
XDrawString(dsp, win[screen], textgc[screen],
left, y, text_name, strlen(text_name));
XDrawString(dsp, win[screen], textgc[screen],
left + 1, y, text_name, strlen(text_name));
XDrawString(dsp, win[screen], textgc[screen],
left + XTextWidth(font, text_name, strlen(text_name)), y,
user, strlen(user));
y = icony[screen] - (font->descent + 2);
XDrawString(dsp, win[screen], textgc[screen],
iconx[screen], y, text_info, strlen(text_info));
passwordPrompt(text_pass);
XFlush(dsp);
y = icony[screen] + ICONH + font->ascent + 2
+ font->ascent + font->descent + 2;
done = False;
stoptryingfornow = False;
while (!done) {
#ifdef USE_PAM
if (use_pam) {
pam_error = pam_authenticate(pamh, pam_flags);
if (pam_error == PAM_SUCCESS) {
const char *pam_error_from = "pam_acct_mgmt";
pam_error = pam_acct_mgmt(pamh, pam_flags);
if (pam_error == PAM_NEW_AUTHTOK_REQD) {
do {
pam_error = pam_chauthtok(pamh,
PAM_CHANGE_EXPIRED_AUTHTOK);
} while (pam_error == PAM_AUTHTOK_ERR ||
pam_error == PAM_TRY_AGAIN);
pam_error_from = "pam_chauthtok";
#ifdef sun
audit_passwd(pam_error);
#endif /* sun */
}
if (pam_error == PAM_SUCCESS) {
pam_error = pam_setcred(pamh,PAM_REFRESH_CRED);
if (pam_error != PAM_SUCCESS) {
PAM_ERROR_PRINT("pam_setcred(PAM_REFRESH_CRED)");
} else {
done = True;
}
} else {
#ifdef sun
audit_unlock(pam_error);
#endif /* sun */
PAM_ERROR_PRINT(pam_error_from);
}
} else if (stoptryingfornow) {
break;
} else {
#ifdef sun
audit_unlock(pam_error);
#endif /* sun */
PAM_ERROR_PRINT("pam_authenticate");
}
if (pam_error != PAM_SUCCESS) {
authErrMsg = pam_strerror(pamh, pam_error);
}
} else
if (ReadXString(buffer, PAM_MAX_RESP_SIZE))
break;
#endif
/*
* This section gets a little messy. In SYSV, the number of
* cases to handle increases because of the existence of the
* shadow file. There are also a number of cases that need
* to be dealt with where either root or user passwords are
* nil. Hopefully the code below is easy enough to follow.
*/
#ifdef SYSV
if (userpass) {
if (*userpass == NULL) {
done = (*buffer == NULL);
} else {
done = (!strcmp(crypt(buffer, userpass), userpass));
}
}
if (!done && suserpass) {
if (*suserpass == NULL) {
done = (*buffer == NULL);
} else {
done = (!strcmp(crypt(buffer, suserpass), suserpass));
}
}
if (!done && allowroot) {
if (srootpass) {
if (*srootpass == NULL) {
done = (*buffer == NULL);
} else {
done = (!strcmp(crypt(buffer, srootpass), srootpass));
}
}
if (!done && rootpass) {
if (*rootpass == NULL) {
done = (*buffer == NULL);
} else {
done = (!strcmp(crypt(buffer, rootpass), rootpass));
}
}
}
#else
done = !((strcmp(crypt(buffer, userpass), userpass))
&& (!allowroot || strcmp(crypt(buffer, rootpass), rootpass)));
if (!done && *buffer == NULL) {
/* just hit return, and it wasn't his password */
break;
}
if (*userpass == NULL && *buffer != NULL) {
/*
* the user has no password, but something was typed anyway.
* sounds fishy: don't let him in...
*/
done = False;
}
#endif /* SYSV */
/* clear plaintext password so you can't grunge around /dev/kmem */
memset(buffer, 0, sizeof(buffer));
displayTextInfo(text_valid);
if (done) {
/* clear encrypted passwords just in case */
if (rootpass) {
memset(rootpass, 0, strlen(rootpass));
free(rootpass);
}
if (userpass) {
memset(userpass, 0, strlen(userpass));
free(userpass);
}
#ifdef SYSV
if (srootpass) {
memset(srootpass, 0, strlen(srootpass));
free(srootpass);
}
if (suserpass) {
memset(suserpass, 0, strlen(suserpass));
free(suserpass);
}
#endif
#ifdef USE_PAM
#ifdef sun
audit_unlock(pam_error);
#endif /* sun */
pam_end(pamh, pam_error);
#endif
return 0;
} else {
XSync(dsp, True); /* flush input buffer */
sleep(1);
displayTextInfo(authErrMsg);
if (echokeys || reallyechokeys) /* erase old echo */
XFillRectangle(dsp, win[screen], Scr[screen].gc,
passx, passy - font->ascent,
xgwa.width - passx,
font->ascent + font->descent);
}
}
/* clear encrypted passwords just in case */
if (rootpass) {
memset(rootpass, 0, strlen(rootpass));
free(rootpass);
}
if (userpass) {
memset(userpass, 0, strlen(userpass));
free(userpass);
}
#ifdef SYSV
if (srootpass) {
memset(srootpass, 0, strlen(srootpass));
free(srootpass);
}
if (suserpass) {
memset(suserpass, 0, strlen(suserpass));
free(suserpass);
}
#endif
#ifdef USE_PAM
pam_end(pamh, pam_error);
#endif
XChangeGrabbedCursor(mycursor);
XUnmapWindow(dsp, icon[screen]);
return 1;
}
static void
justDisplay()
{
XEvent event;
for (screen = 0; screen < screens; screen++)
init(win[screen]);
do {
while (!XPending(dsp)) {
for (screen = 0; screen < screens; screen++)
callback(win[screen]);
XFlush(dsp);
usleep(delay);
}
XNextEvent(dsp, &event);
#ifndef DEBUG
if (event.type == VisibilityNotify)
XRaiseWindow(dsp, event.xany.window);
#endif
} while (event.type != ButtonPress && event.type != KeyPress);
for (screen = 0; screen < screens; screen++)
if (event.xbutton.root == RootWindow(dsp, screen))
break;
if (usefirst)
XPutBackEvent(dsp, &event);
}
static void
sigcatch()
{
finish();
error("%s: caught terminate signal.\nAccess control list restored.\n",(char*)NULL);
}
static void
lockDisplay()
{
if (!allowaccess) {
#ifdef SYSV
sigset_t oldsigmask;
sigset_t newsigmask;
sigemptyset(&newsigmask);
sigaddset(&newsigmask, SIGHUP);
sigaddset(&newsigmask, SIGINT);
sigaddset(&newsigmask, SIGQUIT);
sigaddset(&newsigmask, SIGTERM);
sigprocmask(SIG_BLOCK, &newsigmask, &oldsigmask);
#else
int oldsigmask;
oldsigmask = sigblock(sigmask(SIGHUP) |
sigmask(SIGINT) |
sigmask(SIGQUIT) |
sigmask(SIGTERM));
#endif
signal(SIGHUP, (void (*)()) sigcatch);
signal(SIGINT, (void (*)()) sigcatch);
signal(SIGQUIT, (void (*)()) sigcatch);
signal(SIGTERM, (void (*)()) sigcatch);
XGrabHosts(dsp);
#ifdef SYSV
sigprocmask(SIG_SETMASK, &oldsigmask, &oldsigmask);
#else
sigsetmask(oldsigmask);
#endif
}
#ifdef sun
audit_lock();
#endif /* sun */
do {
justDisplay();
} while (getPassword());
}
int
main(argc, argv)
int argc;
char *argv[];
{
XSetWindowAttributes xswa;
XGCValues xgcv;
ProgramName = strrchr(argv[0], '/');
if (ProgramName)
ProgramName++;
else
ProgramName = argv[0];
srandom(time((long *) 0)); /* random mode needs the seed set. */
GetResources(argc, argv);
CheckResources();
font = XLoadQueryFont(dsp, fontname);
if (font == NULL) {
fprintf(stderr, "%s: can't find font: %s, using %s...\n",
ProgramName, fontname, FALLBACK_FONTNAME);
font = XLoadQueryFont(dsp, FALLBACK_FONTNAME);
if (font == NULL)
error("%s: can't even find %s!!!\n", FALLBACK_FONTNAME);
}
if (CheckPassword()) {
fprintf(stderr, "%s: can't get the user password. Exiting ...\n",
ProgramName);
fprintf(stderr,"\tYou need to run xlock in setuid root mode on your local machine.\n");
error("\tContact your system administrator.\n", (char *) NULL);
}
screens = ScreenCount(dsp);
if (screens > MAXSCREENS)
error("%s: can only support %d screens.\n", MAXSCREENS);
for (screen = 0; screen < screens; screen++) {
XColor tmp;
Screen *scr = ScreenOfDisplay(dsp, screen);
Visual *vis = XDefaultVisual(dsp, screen);
Colormap cmap;
root[screen] = RootWindowOfScreen(scr);
cmap = XCreateColormap(dsp, root[screen], vis, AllocNone);
XAllocNamedColor(dsp, cmap, "White", &sswhite[screen], &tmp);
XAllocNamedColor(dsp, cmap, "Black", &ssblack[screen], &tmp);
if (mono || CellsOfScreen(scr) == 2) {
if (!XAllocNamedColor(dsp, cmap, background,
&bgcol[screen], &tmp)) {
XAllocNamedColor(dsp, cmap, "White", &bgcol[screen], &tmp);
}
if (!XAllocNamedColor(dsp, cmap, foreground,
&fgcol[screen], &tmp)) {
XAllocNamedColor(dsp, cmap, "Black", &fgcol[screen], &tmp);
}
Scr[screen].pixels[0] = fgcol[screen].pixel;
Scr[screen].pixels[1] = bgcol[screen].pixel;
Scr[screen].npixels = 2;
} else {
int colorcount = NUMCOLORS;
u_char red[NUMCOLORS];
u_char green[NUMCOLORS];
u_char blue[NUMCOLORS];
int i;
if (!XAllocNamedColor(dsp, cmap, background,
&bgcol[screen], &tmp)) {
fprintf(stderr, "couldn't allocate: %s\n", background);
XAllocNamedColor(dsp, cmap, "White", &bgcol[screen], &tmp);
}
if (!XAllocNamedColor(dsp, cmap, foreground,
&fgcol[screen], &tmp)) {
fprintf(stderr, "couldn't allocate: %s\n", foreground);
XAllocNamedColor(dsp, cmap, "Black", &fgcol[screen], &tmp);
}
hsbramp(0.0, saturation, 1.0, 1.0, saturation, 1.0, colorcount,
red, green, blue);
Scr[screen].npixels = 0;
for (i = 0; i < colorcount; i++) {
XColor xcolor;
xcolor.red = red[i] << 8;
xcolor.green = green[i] << 8;
xcolor.blue = blue[i] << 8;
xcolor.flags = DoRed | DoGreen | DoBlue;
if (!XAllocColor(dsp, cmap, &xcolor))
break;
Scr[screen].pixels[i] = xcolor.pixel;
Scr[screen].npixels++;
}
if (verbose)
fprintf(stderr, "%d pixels allocated\n", Scr[screen].npixels);
}
xswa.override_redirect = True;
xswa.background_pixel = ssblack[screen].pixel;
xswa.event_mask = KeyPressMask | ButtonPressMask | VisibilityChangeMask;
xswa.colormap = cmap; /* In DEBUG mode, we do not see this */
win[screen] = XCreateWindow(dsp, root[screen], 0, 0, WIDTH, HEIGHT, 0,
CopyFromParent, InputOutput, CopyFromParent,
CWMASK, &xswa);
#ifdef DEBUG
{
XWMHints xwmh;
xwmh.flags = InputHint;
xwmh.input = True;
XChangeProperty(dsp, win[screen],
XA_WM_HINTS, XA_WM_HINTS, 32, PropModeReplace,
(unsigned char *) &xwmh, sizeof(xwmh) / sizeof(int));
}
#endif
iconx[screen] = (DisplayWidth(dsp, screen) -
XTextWidth(font, text_info, strlen(text_info))) / 2;
icony[screen] = DisplayHeight(dsp, screen) / 6;
xswa.border_pixel = fgcol[screen].pixel;
xswa.background_pixel = bgcol[screen].pixel;
xswa.event_mask = ButtonPressMask;
xswa.colormap = cmap; /* In DEBUG mode, we do not see this */
#define CIMASK CWBorderPixel | CWBackPixel | CWEventMask | CWColormap
icon[screen] = XCreateWindow(dsp, win[screen],
iconx[screen], icony[screen],
ICONW, ICONH, 1, CopyFromParent,
InputOutput, CopyFromParent,
CIMASK, &xswa);
XMapWindow(dsp, win[screen]);
XRaiseWindow(dsp, win[screen]);
XInstallColormap(dsp, cmap);
xgcv.foreground = sswhite[screen].pixel;
xgcv.background = ssblack[screen].pixel;
Scr[screen].gc = XCreateGC(dsp, win[screen],
GCForeground | GCBackground, &xgcv);
xgcv.foreground = fgcol[screen].pixel;
xgcv.background = bgcol[screen].pixel;
xgcv.font = font->fid;
textgc[screen] = XCreateGC(dsp, win[screen],
GCFont | GCForeground | GCBackground, &xgcv);
}
lockc = XCreateBitmapFromData(dsp, root[0], no_bits, 1, 1);
lockm = XCreateBitmapFromData(dsp, root[0], no_bits, 1, 1);
mycursor = XCreatePixmapCursor(dsp, lockc, lockm,
&fgcol[screen], &bgcol[screen], 0, 0);
passwdcursor = XCreateFontCursor(dsp, XC_left_ptr);
XFreePixmap(dsp, lockc);
XFreePixmap(dsp, lockm);
if (!enablesaver) {
XGetScreenSaver(dsp, &sstimeout, &ssinterval,
&ssblanking, &ssexposures);
XSetScreenSaver(dsp, 0, 0, 0, 0); /* disable screen saver */
}
#ifndef DEBUG
GrabKeyboardAndMouse();
#endif
nice(nicelevel);
if (nolock)
justDisplay();
else
lockDisplay();
finish();
return 0;
}