open-src/app/xlock/sun-src/xlock.c
author X gate hg captive account <xhg@xserver.us.oracle.com>
Wed, 19 Aug 2015 13:48:41 -0700
changeset 1484 bc8922b8e1d2
parent 1233 1134c9fd2fe6
permissions -rw-r--r--
Added tag s11u3_30 for changeset 221e9d9cc9cb

/*
 * Copyright (c) 1988, 2011, Oracle and/or its affiliates. All rights reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice (including the next
 * paragraph) shall be included in all copies or substantial portions of the
 * Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 */
/*
 * xlock.c - X11 client to lock a display and show a screen saver.
 *
 * Copyright (c) 1988-91 by Patrick J. Naughton.
 *
 * Permission to use, copy, modify, and distribute this software and its
 * documentation for any purpose and without fee is hereby granted,
 * provided that the above copyright notice appear in all copies and that
 * both that copyright notice and this permission notice appear in
 * supporting documentation.
 *
 * This file is provided AS IS with no warranties of any kind.  The author
 * shall have no liability with respect to the infringement of copyrights,
 * trade secrets or any patents by this file or any part thereof.  In no
 * event will the author be liable for any lost revenue or profits or
 * other special, indirect and consequential damages.
 *
 * Comments and additions should be sent to the author:
 *
 *		       [email protected]
 *
 *		       Patrick J. Naughton
 *		       MS 10-20
 *		       Sun Laboritories, Inc.
 *		       2550 Garcia Ave
 *		       Mountain View, CA  94043
 *
 * Revision History:
 * 
 * 24-Jun-91: make foreground and background color get used on mono.
 * 24-May-91: added -usefirst.
 * 16-May-91: added pyro and random modes.
 *	      ripped big comment block out of all other files.
 * 08-Jan-91: fix some problems with password entry.
 *	      removed renicing code.
 * 29-Oct-90: added cast to XFree() arg.
 *	      added volume arg to call to XBell().
 * 28-Oct-90: center prompt screen.
 *	      make sure Xlib input buffer does not use up all of swap.
 *	      make displayed text come from resource file for better I18N.
 *	      add backward compatible signal handlers for pre 4.1 machines.
 * 31-Aug-90: added blank mode.
 *	      added swarm mode.
 *	      moved usleep() and seconds() out to usleep.c.
 *	      added SVR4 defines to xlock.h
 * 29-Jul-90: added support for multiple screens to be locked by one xlock.
 *	      moved global defines to xlock.h
 *	      removed use of allowsig().
 * 07-Jul-90: reworked commandline args and resources to use Xrm.
 *	      moved resource processing out to resource.c
 * 02-Jul-90: reworked colors to not use dynamic colormap.
 * 23-May-90: added autoraise when obscured.
 * 15-Apr-90: added hostent alias searching for host authentication.
 * 18-Feb-90: added SunOS3.5 fix.
 *	      changed -mono -> -color, and -saver -> -lock.
 *	      allow non-locking screensavers to display on remote machine.
 *	      added -echokeys to disable echoing of '?'s on input.
 *	      cleaned up all of the parameters and defaults.
 * 20-Dec-89: added -xhost to allow access control list to be left alone.
 *	      added -screensaver (don't disable screen saver) for the paranoid.
 *	      Moved seconds() here from all of the display mode source files.
 *	      Fixed bug with calling XUngrabHosts() in finish().
 * 19-Dec-89: Fixed bug in GrabPointer.
 *	      Changed fontname to XLFD style.
 * 23-Sep-89: Added fix to allow local hostname:0 as a display.
 *	      Put empty case for Enter/Leave events.
 *	      Moved colormap installation later in startup.
 * 20-Sep-89: Linted and made -saver mode grab the keyboard and mouse.
 *	      Replaced SunView code for life mode with Jim Graham's version,
 *		so I could contrib it without legal problems.
 *	      Sent to expo for X11R4 contrib.
 * 19-Sep-89: Added '?'s on input.
 * 27-Mar-89: Added -qix mode.
 *	      Fixed GContext->GC.
 * 20-Mar-89: Added backup font (fixed) if XQueryLoadFont() fails.
 *	      Changed default font to lucida-sans-24.
 * 08-Mar-89: Added -nice, -mode and -display, built vector for life and hop.
 * 24-Feb-89: Replaced hopalong display with life display from SunView1.
 * 22-Feb-89: Added fix for color servers with n < 8 planes.
 * 16-Feb-89: Updated calling conventions for XCreateHsbColormap();
 *	      Added -count for number of iterations per color.
 *	      Fixed defaulting mechanism.
 *	      Ripped out VMS hacks.
 *	      Sent to expo for X11R3 contrib.
 * 15-Feb-89: Changed default font to pellucida-sans-18.
 * 20-Jan-89: Added -verbose and fixed usage message.
 * 19-Jan-89: Fixed monochrome gc bug.
 * 16-Dec-88: Added SunView style password prompting.
 * 19-Sep-88: Changed -color to -mono. (default is color on color displays).
 *	      Added -saver option. (just do display... don't lock.)
 * 31-Aug-88: Added -time option.
 *	      Removed code for fractals to separate file for modularity.
 *	      Added signal handler to restore host access.
 *	      Installs dynamic colormap with a Hue Ramp.
 *	      If grabs fail then exit.
 *	      Added VMS Hacks. (password 'iwiwuu').
 *	      Sent to expo for X11R2 contrib.
 * 08-Jun-88: Fixed root password pointer problem and changed PASSLENGTH to 20.
 * 20-May-88: Added -root to allow root to unlock.
 * 12-Apr-88: Added root password override.
 *	      Added screen saver override.
 *	      Removed XGrabServer/XUngrabServer.
 *	      Added access control handling instead.
 * 01-Apr-88: Added XGrabServer/XUngrabServer for more security.
 * 30-Mar-88: Removed startup password requirement.
 *	      Removed cursor to avoid phosphor burn.
 * 27-Mar-88: Rotate fractal by 45 degrees clockwise.
 * 24-Mar-88: Added color support. [-color]
 *	      wrote the man page.
 * 23-Mar-88: Added HOPALONG routines from Scientific American Sept. 86 p. 14.
 *	      added password requirement for invokation
 *	      removed option for command line password
 *	      added requirement for display to be "unix:0".
 * 22-Mar-88: Recieved Walter Milliken's comp.windows.x posting.
 *
 */

#include <stdio.h>
#include <signal.h>
#include <string.h>
#include <stdarg.h>
#include <crypt.h>
#ifdef SYSV
#include <shadow.h>
#endif
#include <pwd.h>

#include "xlock.h"
#include <X11/cursorfont.h>
#include <X11/Xatom.h>

#ifdef USE_PAM
# include <security/pam_appl.h>
# ifndef XLOCK_PAM_SERVICE
#  define XLOCK_PAM_SERVICE "xlock"
# endif
# define PAM_ERROR_PRINT(pamfunc)	\
 if (verbose) { \
   fprintf(stderr, "%s: %s failure: %s\n", ProgramName, pamfunc, \
     pam_strerror(pamh, pam_error)); \
 }
# ifdef sun
#  include <deflt.h>
# endif
#endif

char       *ProgramName;	/* argv[0] */
perscreen   Scr[MAXSCREENS];
Display    *dsp = NULL;		/* server display connection */
int         screen;		/* current screen */
void        (*callback) () = NULL;
void        (*init) () = NULL;

static int  screens;		/* number of screens */
static Window win[MAXSCREENS];	/* window used to cover screen */
static Window icon[MAXSCREENS];	/* window used during password typein */
static Window root[MAXSCREENS];	/* convenience pointer to the root window */
static GC   textgc[MAXSCREENS];	/* graphics context used for text rendering */
static XColor fgcol[MAXSCREENS];/* used for text rendering */
static XColor bgcol[MAXSCREENS];/* background of text screen */
XColor ssblack[MAXSCREENS];/* black color for screen saver screen */
XColor sswhite[MAXSCREENS];/* white color for screen saver screen */
static int  iconx[MAXSCREENS];	/* location of left edge of icon */
static int  icony[MAXSCREENS];	/* location of top edge of icon */
static Cursor mycursor;		/* blank cursor */
static Cursor passwdcursor;	/* cursor used in getPassword */
static Pixmap lockc;
static Pixmap lockm;		/* pixmaps for cursor and mask */
static char no_bits[] = {0};	/* dummy array for the blank cursor */
static int  passx;		/* position of the ?'s */
static int  passy;
static XFontStruct *font;
static int  sstimeout;		/* screen saver parameters */
static int  ssinterval;
static int  ssblanking;
static int  ssexposures;

static char buffer[PAM_MAX_RESP_SIZE];
static Bool reallyechokeys = False; /* Echo real keys instead of ?'s */
static Bool stoptryingfornow = False;

#define FALLBACK_FONTNAME	"fixed"
#define ICONW			64
#define ICONH			64

#ifdef DEBUG
#define WIDTH WidthOfScreen(scr) - 100
#define HEIGHT HeightOfScreen(scr) - 100
#define CWMASK CWBackPixel | CWEventMask | CWColormap
#else
#define WIDTH WidthOfScreen(scr)
#define HEIGHT HeightOfScreen(scr)
#define CWMASK CWOverrideRedirect | CWBackPixel | CWEventMask | CWColormap
#endif

#define AllPointerEventMask \
	(ButtonPressMask | ButtonReleaseMask | \
	EnterWindowMask | LeaveWindowMask | \
	PointerMotionMask | PointerMotionHintMask | \
	Button1MotionMask | Button2MotionMask | \
	Button3MotionMask | Button4MotionMask | \
	Button5MotionMask | ButtonMotionMask | \
	KeymapStateMask)

void
error(const char *format, ...)
{
    va_list args;

    fprintf(stderr, "%s: ", ProgramName);
    va_start(args, format);
    vfprintf(stderr, format, args);
    va_end(args);
    exit(1);
}

/*
 * Server access control support.
 */

static XHostAddress *XHosts;	/* the list of "friendly" client machines */
static int  HostAccessCount;	/* the number of machines in XHosts */
static Bool HostAccessState;	/* whether or not we even look at the list */

static void
XGrabHosts(dsp)
    Display    *dsp;
{
    XHosts = XListHosts(dsp, &HostAccessCount, &HostAccessState);
    if (XHosts)
	XRemoveHosts(dsp, XHosts, HostAccessCount);
    XEnableAccessControl(dsp);
}

static void
XUngrabHosts(dsp)
    Display    *dsp;
{
    if (XHosts) {
	XAddHosts(dsp, XHosts, HostAccessCount);
	XFree((char *) XHosts);
    }
    if (HostAccessState == False)
	XDisableAccessControl(dsp);
}


/*
 * Simple wrapper to get an asynchronous grab on the keyboard and mouse.
 * If either grab fails, we sleep for one second and try again since some
 * window manager might have had the mouse grabbed to drive the menu choice
 * that picked "Lock Screen..".  If either one fails the second time we print
 * an error message and exit.
 */
static void
GrabKeyboardAndMouse()
{
    Status      status;

    status = XGrabKeyboard(dsp, win[0], True,
			   GrabModeAsync, GrabModeAsync, CurrentTime);
    if (status != GrabSuccess) {
	sleep(1);
	status = XGrabKeyboard(dsp, win[0], True,
			       GrabModeAsync, GrabModeAsync, CurrentTime);

	if (status != GrabSuccess)
	    error("couldn't grab keyboard! (%d)\n", status);
    }
    status = XGrabPointer(dsp, win[0], True, AllPointerEventMask,
			  GrabModeAsync, GrabModeAsync, None, mycursor,
			  CurrentTime);
    if (status != GrabSuccess) {
	sleep(1);
	status = XGrabPointer(dsp, win[0], True, AllPointerEventMask,
			      GrabModeAsync, GrabModeAsync, None, mycursor,
			      CurrentTime);

	if (status != GrabSuccess)
	    error("couldn't grab pointer! (%d)\n", status);
    }
}


/*
 * Assuming that we already have an asynch grab on the pointer,
 * just grab it again with a new cursor shape and ignore the return code.
 */
static void
XChangeGrabbedCursor(cursor)
    Cursor      cursor;
{
#ifndef DEBUG
    (void) XGrabPointer(dsp, win[0], True, AllPointerEventMask,
		    GrabModeAsync, GrabModeAsync, None, cursor, CurrentTime);
#endif
}


/*
 * Restore all grabs, reset screensaver, restore colormap, close connection.
 */
static void
finish()
{
    XSync(dsp, False);
    if (!nolock && !allowaccess)
	XUngrabHosts(dsp);
    XUngrabPointer(dsp, CurrentTime);
    XUngrabKeyboard(dsp, CurrentTime);
    if (!enablesaver)
	XSetScreenSaver(dsp, sstimeout, ssinterval, ssblanking, ssexposures);
    XFlush(dsp);
    XCloseDisplay(dsp);
}


static int
ReadXString(s, slen)
    char       *s;
    int         slen;
{
    XEvent      event;
    char        keystr[20];
    char        c;
    int         i;
    int         bp;
    int         len;
    int         thisscreen = screen;
    char	pwbuf[PAM_MAX_RESP_SIZE];

    for (screen = 0; screen < screens; screen++)
	if (thisscreen == screen)
	    init(icon[screen]);
	else
	    init(win[screen]);
    bp = 0;
    *s = 0;
    while (True) {
	unsigned long lasteventtime = seconds();
	while (!XPending(dsp)) {
	    for (screen = 0; screen < screens; screen++)
		if (thisscreen == screen)
		    callback(icon[screen]);
		else
		    callback(win[screen]);
	    XFlush(dsp);
	    usleep(delay);
	    if (seconds() - lasteventtime > timeout) {
		screen = thisscreen;
		stoptryingfornow = True;
		return 1;
	    }
	}
	screen = thisscreen;
	XNextEvent(dsp, &event);
	switch (event.type) {
	case KeyPress:
	    len = XLookupString((XKeyEvent *) & event, keystr, 20, NULL, NULL);
	    for (i = 0; i < len; i++) {
		c = keystr[i];
		switch (c) {
		case 8:	/* ^H */
		case 127:	/* DEL */
		    if (bp > 0)
			bp--;
		    break;
		case 10:	/* ^J */
		case 13:	/* ^M */
		    s[bp] = '\0';
	            /*
	             * eat all events if there are more than enough pending... this
	             * keeps the Xlib event buffer from growing larger than all
	             * available memory and crashing xlock.
	             */
	            if (XPending(dsp) > 100) {	/* 100 is arbitrarily big enough */
		        register Status status;
		        do {
		            status = XCheckMaskEvent(dsp,
				              KeyPressMask | KeyReleaseMask, &event);
		        } while (status);
		        XBell(dsp, 100);
	            }
		    return 0;
		case 21:	/* ^U */
		    bp = 0;
		    break;
		default:
		    s[bp] = c;
		    if (bp < slen - 1)
			bp++;
		    else
			XSync(dsp, True);	/* flush input buffer */
		}
	    }
	    XSetForeground(dsp, Scr[screen].gc, bgcol[screen].pixel);
	    if (echokeys || reallyechokeys) {
		if (reallyechokeys) {
		    memcpy(pwbuf, s, slen);
		} else {
		    memset(pwbuf, '?', slen);
		} 

		XFillRectangle(dsp, win[screen], Scr[screen].gc,
			       passx, passy - font->ascent,
			       XTextWidth(font, pwbuf, slen),
			       font->ascent + font->descent);
		XDrawString(dsp, win[screen], textgc[screen],
			    passx, passy, pwbuf, bp);
	    }
	    /*
	     * eat all events if there are more than enough pending... this
	     * keeps the Xlib event buffer from growing larger than all
	     * available memory and crashing xlock.
	     */
	    if (XPending(dsp) > 100) {	/* 100 is arbitrarily big enough */
		register Status status;
		do {
		    status = XCheckMaskEvent(dsp,
				      KeyPressMask | KeyReleaseMask, &event);
		} while (status);
		XBell(dsp, 100);
	    }
	    break;

	case ButtonPress:
	    if (((XButtonEvent *) & event)->window == icon[screen]) {
		stoptryingfornow = True;
		return 1;
	    }
	    break;

	case VisibilityNotify:
	    if (event.xvisibility.state != VisibilityUnobscured) {
#ifndef DEBUG
		XRaiseWindow(dsp, win[screen]);
#endif
		s[0] = '\0';
		return 1;
	    }
	    break;

	case KeymapNotify:
	case KeyRelease:
	case ButtonRelease:
	case MotionNotify:
	case LeaveNotify:
	case EnterNotify:
	    break;

	default:
	    fprintf(stderr, "%s: unexpected event: %d\n",
		    ProgramName, event.type);
	    break;
	}
    }
}


static int
CheckPassword()
{
#ifdef SYSV
    struct spwd *rspw, *uspw;
    struct passwd *upw;
    const char  *user;
#else
    struct passwd *rpw, *upw;
#endif /* SYSV */

#ifdef SYSV
    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;
    const char *user;
#else
    const 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("caught terminate signal.\nAccess control list restored.\n");
}


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("can't even find %s!!!\n", FALLBACK_FONTNAME);
    }

    if (CheckPassword()) {
	error("can't get the user password. Exiting ...\n"
	"\tYou need to run xlock in setuid root mode on your local machine.\n"
	"\tContact your system administrator.\n");
    }
	
    screens = ScreenCount(dsp);
    if (screens > MAXSCREENS)
	error("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;
}