diff -r 43240086238f -r f3f84c886c69 open-src/app/xlock/sun-src/xlock.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/open-src/app/xlock/sun-src/xlock.c Sun Sep 21 18:53:48 2008 -0700 @@ -0,0 +1,1357 @@ +#pragma ident "@(#)xlock.c 35.11 08/09/18 SMI" +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * + * 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, and/or sell copies of the Software, and to permit persons + * to whom the Software is furnished to do so, provided that the above + * copyright notice(s) and this permission notice appear in all copies of + * the Software and that both the above copyright notice(s) and this + * permission notice appear in supporting documentation. + * + * 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 + * OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL + * INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING + * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, + * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION + * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Except as contained in this notice, the name of a copyright holder + * shall not be used in advertising or otherwise to promote the sale, use + * or other dealings in this Software without prior written authorization + * of the copyright holder. + */ +/* + * 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: + * + * naughton@eng.sun.com + * + * 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 +#include +#include +#ifdef SYSV +#include +#endif +#include + +#include "xlock.h" +#include +#include + +#ifdef USE_PAM +# include +# 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 +# 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) + +/* VARARGS1 */ +void +error(s1, s2) + char *s1, *s2; +{ + fprintf(stderr, s1, ProgramName, s2); + 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("%s: 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("%s: 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; + 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 +#include +#include + + +/* + * 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; +}