slrn: obsolete
[oi-userland.git] / components / x11 / xlock / src / xlock.c
blob6c09d18392247042c39736fe1621af10d5d338cb
1 /*
2 * Copyright (c) 1988, 2019, Oracle and/or its affiliates. All rights reserved.
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
11 * The above copyright notice and this permission notice (including the next
12 * paragraph) shall be included in all copies or substantial portions of the
13 * Software.
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21 * DEALINGS IN THE SOFTWARE.
24 * xlock.c - X11 client to lock a display and show a screen saver.
26 * Copyright (c) 1988-91 by Patrick J. Naughton.
28 * Permission to use, copy, modify, and distribute this software and its
29 * documentation for any purpose and without fee is hereby granted,
30 * provided that the above copyright notice appear in all copies and that
31 * both that copyright notice and this permission notice appear in
32 * supporting documentation.
34 * This file is provided AS IS with no warranties of any kind. The author
35 * shall have no liability with respect to the infringement of copyrights,
36 * trade secrets or any patents by this file or any part thereof. In no
37 * event will the author be liable for any lost revenue or profits or
38 * other special, indirect and consequential damages.
40 * Comments and additions should be sent to the author:
42 * naughton@eng.sun.com
44 * Patrick J. Naughton
45 * MS 10-20
46 * Sun Laboritories, Inc.
47 * 2550 Garcia Ave
48 * Mountain View, CA 94043
50 * Revision History:
52 * 24-Jun-91: make foreground and background color get used on mono.
53 * 24-May-91: added -usefirst.
54 * 16-May-91: added pyro and random modes.
55 * ripped big comment block out of all other files.
56 * 08-Jan-91: fix some problems with password entry.
57 * removed renicing code.
58 * 29-Oct-90: added cast to XFree() arg.
59 * added volume arg to call to XBell().
60 * 28-Oct-90: center prompt screen.
61 * make sure Xlib input buffer does not use up all of swap.
62 * make displayed text come from resource file for better I18N.
63 * add backward compatible signal handlers for pre 4.1 machines.
64 * 31-Aug-90: added blank mode.
65 * added swarm mode.
66 * moved usleep() and seconds() out to usleep.c.
67 * added SVR4 defines to xlock.h
68 * 29-Jul-90: added support for multiple screens to be locked by one xlock.
69 * moved global defines to xlock.h
70 * removed use of allowsig().
71 * 07-Jul-90: reworked commandline args and resources to use Xrm.
72 * moved resource processing out to resource.c
73 * 02-Jul-90: reworked colors to not use dynamic colormap.
74 * 23-May-90: added autoraise when obscured.
75 * 15-Apr-90: added hostent alias searching for host authentication.
76 * 18-Feb-90: added SunOS3.5 fix.
77 * changed -mono -> -color, and -saver -> -lock.
78 * allow non-locking screensavers to display on remote machine.
79 * added -echokeys to disable echoing of '?'s on input.
80 * cleaned up all of the parameters and defaults.
81 * 20-Dec-89: added -xhost to allow access control list to be left alone.
82 * added -screensaver (don't disable screen saver) for the paranoid.
83 * Moved seconds() here from all of the display mode source files.
84 * Fixed bug with calling XUngrabHosts() in finish().
85 * 19-Dec-89: Fixed bug in GrabPointer.
86 * Changed fontname to XLFD style.
87 * 23-Sep-89: Added fix to allow local hostname:0 as a display.
88 * Put empty case for Enter/Leave events.
89 * Moved colormap installation later in startup.
90 * 20-Sep-89: Linted and made -saver mode grab the keyboard and mouse.
91 * Replaced SunView code for life mode with Jim Graham's version,
92 * so I could contrib it without legal problems.
93 * Sent to expo for X11R4 contrib.
94 * 19-Sep-89: Added '?'s on input.
95 * 27-Mar-89: Added -qix mode.
96 * Fixed GContext->GC.
97 * 20-Mar-89: Added backup font (fixed) if XQueryLoadFont() fails.
98 * Changed default font to lucida-sans-24.
99 * 08-Mar-89: Added -nice, -mode and -display, built vector for life and hop.
100 * 24-Feb-89: Replaced hopalong display with life display from SunView1.
101 * 22-Feb-89: Added fix for color servers with n < 8 planes.
102 * 16-Feb-89: Updated calling conventions for XCreateHsbColormap();
103 * Added -count for number of iterations per color.
104 * Fixed defaulting mechanism.
105 * Ripped out VMS hacks.
106 * Sent to expo for X11R3 contrib.
107 * 15-Feb-89: Changed default font to pellucida-sans-18.
108 * 20-Jan-89: Added -verbose and fixed usage message.
109 * 19-Jan-89: Fixed monochrome gc bug.
110 * 16-Dec-88: Added SunView style password prompting.
111 * 19-Sep-88: Changed -color to -mono. (default is color on color displays).
112 * Added -saver option. (just do display... don't lock.)
113 * 31-Aug-88: Added -time option.
114 * Removed code for fractals to separate file for modularity.
115 * Added signal handler to restore host access.
116 * Installs dynamic colormap with a Hue Ramp.
117 * If grabs fail then exit.
118 * Added VMS Hacks. (password 'iwiwuu').
119 * Sent to expo for X11R2 contrib.
120 * 08-Jun-88: Fixed root password pointer problem and changed PASSLENGTH to 20.
121 * 20-May-88: Added -root to allow root to unlock.
122 * 12-Apr-88: Added root password override.
123 * Added screen saver override.
124 * Removed XGrabServer/XUngrabServer.
125 * Added access control handling instead.
126 * 01-Apr-88: Added XGrabServer/XUngrabServer for more security.
127 * 30-Mar-88: Removed startup password requirement.
128 * Removed cursor to avoid phosphor burn.
129 * 27-Mar-88: Rotate fractal by 45 degrees clockwise.
130 * 24-Mar-88: Added color support. [-color]
131 * wrote the man page.
132 * 23-Mar-88: Added HOPALONG routines from Scientific American Sept. 86 p. 14.
133 * added password requirement for invokation
134 * removed option for command line password
135 * added requirement for display to be "unix:0".
136 * 22-Mar-88: Recieved Walter Milliken's comp.windows.x posting.
140 #include <stdio.h>
141 #include <signal.h>
142 #include <siginfo.h>
143 #include <string.h>
144 #include <stdarg.h>
145 #include <crypt.h>
146 #include <shadow.h>
147 #include <pwd.h>
148 #include <dlfcn.h>
149 #ifdef __sun
150 # include <note.h>
151 #else
152 # define NOTE(s) /* ignored */
153 #endif
155 #include "xlock.h"
156 #include <X11/cursorfont.h>
157 #include <X11/Xatom.h>
159 #ifdef USE_PAM
160 # include <security/pam_appl.h>
161 # ifndef XLOCK_PAM_SERVICE
162 # define XLOCK_PAM_SERVICE "xlock"
163 # endif
164 # define PAM_ERROR_PRINT(pamfunc) \
165 if (verbose) { \
166 fprintf(stderr, "%s: %s failure: %s\n", ProgramName, pamfunc, \
167 pam_strerror(pamh, pam_error)); \
169 # ifdef sun
170 # include <deflt.h>
171 # endif
172 #endif
174 char *ProgramName; /* argv[0] */
175 perscreen Scr[MAXSCREENS];
176 Display *dsp = NULL; /* server display connection */
177 int screen; /* current screen */
178 void (*callback) (Window win) = NULL;
179 void (*init) (Window win) = NULL;
180 static void (*exp_bzero) (void *s, size_t n);
181 #define EXPLICIT_BZERO(s, n) (*exp_bzero)(s, n)
183 static int screens; /* number of screens */
184 static Window win[MAXSCREENS]; /* window used to cover screen */
185 static Window icon[MAXSCREENS]; /* window used during password typein */
186 static Window root[MAXSCREENS]; /* convenience pointer to the root window */
187 static GC textgc[MAXSCREENS]; /* graphics context used for text rendering */
188 static XColor fgcol[MAXSCREENS];/* used for text rendering */
189 static XColor bgcol[MAXSCREENS];/* background of text screen */
190 XColor ssblack[MAXSCREENS];/* black color for screen saver screen */
191 XColor sswhite[MAXSCREENS];/* white color for screen saver screen */
192 static int iconx[MAXSCREENS]; /* location of left edge of icon */
193 static int icony[MAXSCREENS]; /* location of top edge of icon */
194 static Cursor mycursor; /* blank cursor */
195 static Cursor passwdcursor; /* cursor used in getPassword */
196 static Pixmap lockc;
197 static Pixmap lockm; /* pixmaps for cursor and mask */
198 static char no_bits[] = {0}; /* dummy array for the blank cursor */
199 static int passx; /* position of the ?'s */
200 static int passy;
201 static XFontStruct *font;
202 static int sstimeout; /* screen saver parameters */
203 static int ssinterval;
204 static int ssblanking;
205 static int ssexposures;
207 static char buffer[PAM_MAX_RESP_SIZE];
208 static Bool reallyechokeys = False; /* Echo real keys instead of ?'s */
209 static Bool stoptryingfornow = False;
211 #define FALLBACK_FONTNAME "fixed"
212 #define ICONW 64
213 #define ICONH 64
215 #ifdef DEBUG
216 #define WIDTH WidthOfScreen(scr) - 100
217 #define HEIGHT HeightOfScreen(scr) - 100
218 #define CWMASK CWBackPixel | CWEventMask | CWColormap
219 #else
220 #define WIDTH WidthOfScreen(scr)
221 #define HEIGHT HeightOfScreen(scr)
222 #define CWMASK CWOverrideRedirect | CWBackPixel | CWEventMask | CWColormap
223 #endif
225 #define AllPointerEventMask \
226 (ButtonPressMask | ButtonReleaseMask | \
227 EnterWindowMask | LeaveWindowMask | \
228 PointerMotionMask | PointerMotionHintMask | \
229 Button1MotionMask | Button2MotionMask | \
230 Button3MotionMask | Button4MotionMask | \
231 Button5MotionMask | ButtonMotionMask | \
232 KeymapStateMask)
234 void
235 error(const char *format, ...)
237 va_list args;
239 fprintf(stderr, "%s: ", ProgramName);
240 va_start(args, format);
241 vfprintf(stderr, format, args);
242 va_end(args);
243 exit(1);
247 * Server access control support.
250 static XHostAddress *XHosts; /* the list of "friendly" client machines */
251 static int HostAccessCount; /* the number of machines in XHosts */
252 static Bool HostAccessState; /* whether or not we even look at the list */
254 static void
255 XGrabHosts(Display *dsp)
257 XHosts = XListHosts(dsp, &HostAccessCount, &HostAccessState);
258 if (XHosts)
259 XRemoveHosts(dsp, XHosts, HostAccessCount);
260 XEnableAccessControl(dsp);
263 static void
264 XUngrabHosts(Display *dsp)
266 if (XHosts) {
267 XAddHosts(dsp, XHosts, HostAccessCount);
268 XFree(XHosts);
270 if (HostAccessState == False)
271 XDisableAccessControl(dsp);
276 * Simple wrapper to get an asynchronous grab on the keyboard and mouse.
277 * If either grab fails, we sleep for one second and try again since some
278 * window manager might have had the mouse grabbed to drive the menu choice
279 * that picked "Lock Screen..". If either one fails the second time we print
280 * an error message and exit.
282 static void
283 GrabKeyboardAndMouse(void)
285 Status status;
287 status = XGrabKeyboard(dsp, win[0], True,
288 GrabModeAsync, GrabModeAsync, CurrentTime);
289 if (status != GrabSuccess) {
290 sleep(1);
291 status = XGrabKeyboard(dsp, win[0], True,
292 GrabModeAsync, GrabModeAsync, CurrentTime);
294 if (status != GrabSuccess)
295 error("couldn't grab keyboard! (%d)\n", status);
297 status = XGrabPointer(dsp, win[0], True, AllPointerEventMask,
298 GrabModeAsync, GrabModeAsync, None, mycursor,
299 CurrentTime);
300 if (status != GrabSuccess) {
301 sleep(1);
302 status = XGrabPointer(dsp, win[0], True, AllPointerEventMask,
303 GrabModeAsync, GrabModeAsync, None, mycursor,
304 CurrentTime);
306 if (status != GrabSuccess)
307 error("couldn't grab pointer! (%d)\n", status);
313 * Assuming that we already have an asynch grab on the pointer,
314 * just grab it again with a new cursor shape and ignore the return code.
316 static void
317 XChangeGrabbedCursor(Cursor cursor)
319 #ifndef DEBUG
320 (void) XGrabPointer(dsp, win[0], True, AllPointerEventMask,
321 GrabModeAsync, GrabModeAsync, None, cursor, CurrentTime);
322 #endif
327 * Restore all grabs, reset screensaver, restore colormap, close connection.
329 static void
330 finish(void)
332 XSync(dsp, False);
333 if (!nolock && !allowaccess)
334 XUngrabHosts(dsp);
335 XUngrabPointer(dsp, CurrentTime);
336 XUngrabKeyboard(dsp, CurrentTime);
337 if (!enablesaver)
338 XSetScreenSaver(dsp, sstimeout, ssinterval, ssblanking, ssexposures);
339 XFlush(dsp);
340 XCloseDisplay(dsp);
344 static volatile int sigcaught = 0;
346 static void
347 sigcatch(int sig, siginfo_t *info, void *context)
349 NOTE(ARGUNUSED(context))
350 /* note that we were caught, but don't try to re-enter Xlib from here */
351 sigcaught = sig;
352 psiginfo(info, ProgramName);
355 static void _X_NORETURN
356 sigfinish(void)
358 finish();
359 error("caught terminate signal %d.\nAccess control list restored.\n",
360 sigcaught);
364 static int
365 ReadXString(
366 char *s,
367 unsigned int slen
370 XEvent event;
371 char keystr[20];
372 char c;
373 int i;
374 int bp;
375 int len;
376 int thisscreen = screen;
377 char pwbuf[PAM_MAX_RESP_SIZE];
379 for (screen = 0; screen < screens; screen++)
380 if (thisscreen == screen)
381 init(icon[screen]);
382 else
383 init(win[screen]);
384 bp = 0;
385 *s = 0;
386 /*CONSTCOND*/
387 while (True) {
388 long lasteventtime = seconds();
389 while (!XPending(dsp)) {
390 if (sigcaught)
391 sigfinish();
392 for (screen = 0; screen < screens; screen++)
393 if (thisscreen == screen)
394 callback(icon[screen]);
395 else
396 callback(win[screen]);
397 XFlush(dsp);
398 usleep((useconds_t) delay);
399 if (seconds() - lasteventtime > (long) timeout) {
400 screen = thisscreen;
401 stoptryingfornow = True;
402 return 1;
405 screen = thisscreen;
406 XNextEvent(dsp, &event);
407 if (sigcaught)
408 sigfinish();
409 switch (event.type) {
410 case KeyPress:
411 len = XLookupString((XKeyEvent *) & event, keystr, 20, NULL, NULL);
412 for (i = 0; i < len; i++) {
413 c = keystr[i];
414 switch (c) {
415 case 8: /* ^H */
416 case 127: /* DEL */
417 if (bp > 0)
418 bp--;
419 break;
420 case 10: /* ^J */
421 case 13: /* ^M */
422 s[bp] = '\0';
424 * eat all events if there are more than enough pending... this
425 * keeps the Xlib event buffer from growing larger than all
426 * available memory and crashing xlock.
428 if (XPending(dsp) > 100) { /* 100 is arbitrarily big enough */
429 register Status status;
430 do {
431 status = XCheckMaskEvent(dsp,
432 KeyPressMask | KeyReleaseMask, &event);
433 } while (status);
434 XBell(dsp, 100);
436 return 0;
437 case 21: /* ^U */
438 bp = 0;
439 break;
440 default:
441 s[bp] = c;
442 if (bp < slen - 1)
443 bp++;
444 else
445 XSync(dsp, True); /* flush input buffer */
448 XSetForeground(dsp, Scr[screen].gc, bgcol[screen].pixel);
449 if (echokeys || reallyechokeys) {
450 if (reallyechokeys) {
451 memcpy(pwbuf, s, slen);
452 } else {
453 memset(pwbuf, '?', slen);
456 XFillRectangle(dsp, win[screen], Scr[screen].gc,
457 passx, passy - font->ascent,
458 XTextWidth(font, pwbuf, (int) slen),
459 font->ascent + font->descent);
460 XDrawString(dsp, win[screen], textgc[screen],
461 passx, passy, pwbuf, bp);
464 * eat all events if there are more than enough pending... this
465 * keeps the Xlib event buffer from growing larger than all
466 * available memory and crashing xlock.
468 if (XPending(dsp) > 100) { /* 100 is arbitrarily big enough */
469 register Status status;
470 do {
471 status = XCheckMaskEvent(dsp,
472 KeyPressMask | KeyReleaseMask, &event);
473 } while (status);
474 XBell(dsp, 100);
476 break;
478 case ButtonPress:
479 if (((XButtonEvent *) & event)->window == icon[screen]) {
480 stoptryingfornow = True;
481 return 1;
483 break;
485 case VisibilityNotify:
486 if (event.xvisibility.state != VisibilityUnobscured) {
487 #ifndef DEBUG
488 XRaiseWindow(dsp, win[screen]);
489 #endif
490 s[0] = '\0';
491 return 1;
493 break;
495 case KeymapNotify:
496 case KeyRelease:
497 case ButtonRelease:
498 case MotionNotify:
499 case LeaveNotify:
500 case EnterNotify:
501 break;
503 default:
504 fprintf(stderr, "%s: unexpected event: %d\n",
505 ProgramName, event.type);
506 break;
509 /* NOTREACHED */
513 static void passwordPrompt(const char *prompt)
515 int y, left;
516 Screen *scr = ScreenOfDisplay(dsp, screen);
518 left = iconx[screen] + ICONW + font->max_bounds.width;
519 y = icony[screen] + font->ascent + font->ascent + font->descent + 2;
521 XSetForeground(dsp, Scr[screen].gc, bgcol[screen].pixel);
523 XFillRectangle(dsp, win[screen], Scr[screen].gc,
524 left, y - font->ascent, WIDTH - left,
525 font->ascent + font->descent + 2);
527 XDrawString(dsp, win[screen], textgc[screen],
528 left, y, prompt, (int) strlen(prompt));
529 XDrawString(dsp, win[screen], textgc[screen],
530 left + 1, y, prompt, (int) strlen(prompt));
532 passx = left + 1 + XTextWidth(font, prompt, (int) strlen(prompt))
533 + XTextWidth(font, " ", 1);
534 passy = y;
537 static void displayTextInfo(const char *infoMsg)
539 int y;
540 Screen *scr = ScreenOfDisplay(dsp, screen);
542 y = icony[screen] + ICONH + font->ascent + 2;
544 XSetForeground(dsp, Scr[screen].gc, bgcol[screen].pixel);
546 XFillRectangle(dsp, win[screen], Scr[screen].gc,
547 iconx[screen], y - font->ascent,
548 WIDTH - iconx[screen],
549 font->ascent + font->descent + 2);
551 XDrawString(dsp, win[screen], textgc[screen],
552 iconx[screen], y, infoMsg, (int) strlen(infoMsg));
555 #ifdef USE_PAM
556 static int pamconv(int num_msg, struct pam_message **msg,
557 struct pam_response **response, void *appdata_ptr)
559 NOTE(ARGUNUSED(appdata_ptr))
560 int i;
561 int status = PAM_SUCCESS;
563 struct pam_message *m;
564 struct pam_response *r;
566 *response = calloc(num_msg, sizeof (struct pam_response));
567 if (*response == NULL)
568 return (PAM_BUF_ERR);
570 m = *msg;
571 r = *response;
573 for (i = 0; i < num_msg; i++ , m++ , r++) {
574 #ifdef DEBUG
575 if (verbose) {
576 fprintf(stderr, "pam_msg: %d: '%s'\n", m->msg_style, m->msg);
578 #endif
579 switch (m->msg_style) {
580 case PAM_ERROR_MSG:
581 case PAM_TEXT_INFO:
582 displayTextInfo(m->msg);
583 break;
585 case PAM_PROMPT_ECHO_ON:
586 reallyechokeys = True;
587 /* FALLTHRU */
588 case PAM_PROMPT_ECHO_OFF:
589 passwordPrompt(m->msg);
590 if (ReadXString(buffer, PAM_MAX_RESP_SIZE)) {
591 /* timeout or other error */
592 status = PAM_CONV_ERR;
593 i = num_msg;
594 } else {
595 r->resp = strdup(buffer);
596 if (r->resp == NULL) {
597 status = PAM_BUF_ERR;
598 i = num_msg;
600 #ifdef DEBUG
601 if (verbose) {
602 fprintf(stderr, "pam_resp: '%s'\n", r->resp);
604 #endif
606 reallyechokeys = False;
607 break;
609 default:
610 if (verbose) {
611 fprintf(stderr, "%s: Unknown PAM msg_style: %d\n",
612 ProgramName, m->msg_style);
616 if (status != PAM_SUCCESS) {
617 /* free responses */
618 r = *response;
619 for (i = 0; i < num_msg; i++, r++) {
620 if (r->resp)
621 free(r->resp);
623 free(*response);
624 *response = NULL;
626 return status;
628 #endif
630 #ifdef sun
631 #include <syslog.h>
632 #include <bsm/adt.h>
633 #include <bsm/adt_event.h>
637 * audit_lock - audit entry to screenlock
639 * Entry Process running with appropriate privilege to generate
640 * audit records and real uid of the user.
642 * Exit ADT_screenlock audit record written.
644 static void
645 audit_lock(void)
647 adt_session_data_t *ah; /* audit session handle */
648 adt_event_data_t *event; /* audit event handle */
650 /* Audit start of screen lock -- equivalent to logout ;-) */
652 if (adt_start_session(&ah, NULL, ADT_USE_PROC_DATA) != 0) {
654 syslog(LOG_AUTH | LOG_ALERT, "adt_start_session: %m");
655 return;
657 if ((event = adt_alloc_event(ah, ADT_screenlock)) == NULL) {
659 syslog(LOG_AUTH | LOG_ALERT,
660 "adt_alloc_event(ADT_screenlock): %m");
661 } else {
662 if (adt_put_event(event, ADT_SUCCESS, ADT_SUCCESS) != 0) {
664 syslog(LOG_AUTH | LOG_ALERT,
665 "adt_put_event(ADT_screenlock): %m");
667 adt_free_event(event);
669 (void) adt_end_session(ah);
674 * audit_unlock - audit screen unlock
676 * Entry Process running with appropriate privilege to generate
677 * audit records and real uid of the user.
678 * pam_status = PAM error code; reason for failure.
680 * Exit ADT_screenunlock audit record written.
682 static void
683 audit_unlock(int pam_status)
685 adt_session_data_t *ah; /* audit session handle */
686 adt_event_data_t *event; /* audit event handle */
688 if (adt_start_session(&ah, NULL, ADT_USE_PROC_DATA) != 0) {
690 syslog(LOG_AUTH | LOG_ALERT,
691 "adt_start_session(ADT_screenunlock): %m");
692 return;
694 if ((event = adt_alloc_event(ah, ADT_screenunlock)) == NULL) {
696 syslog(LOG_AUTH | LOG_ALERT,
697 "adt_alloc_event(ADT_screenunlock): %m");
698 } else {
699 if (adt_put_event(event,
700 pam_status == PAM_SUCCESS ? ADT_SUCCESS : ADT_FAILURE,
701 pam_status == PAM_SUCCESS ? ADT_SUCCESS : ADT_FAIL_PAM +
702 pam_status) != 0) {
704 syslog(LOG_AUTH | LOG_ALERT,
705 "adt_put_event(ADT_screenunlock(%s): %m",
706 pam_strerror(NULL, pam_status));
708 adt_free_event(event);
710 (void) adt_end_session(ah);
715 * audit_passwd - audit password change
716 * Entry Process running with appropriate privilege to generate
717 * audit records and real uid of the user.
718 * pam_status = PAM error code; reason for failure.
720 * Exit ADT_passwd audit record written.
722 static void
723 audit_passwd(int pam_status)
725 adt_session_data_t *ah; /* audit session handle */
726 adt_event_data_t *event; /* audit event handle */
728 if (adt_start_session(&ah, NULL, ADT_USE_PROC_DATA) != 0) {
730 syslog(LOG_AUTH | LOG_ALERT,
731 "adt_start_session(ADT_passwd): %m");
732 return;
734 if ((event = adt_alloc_event(ah, ADT_passwd)) == NULL) {
736 syslog(LOG_AUTH | LOG_ALERT,
737 "adt_alloc_event(ADT_passwd): %m");
738 } else {
739 if (adt_put_event(event,
740 pam_status == PAM_SUCCESS ? ADT_SUCCESS : ADT_FAILURE,
741 pam_status == PAM_SUCCESS ? ADT_SUCCESS : ADT_FAIL_PAM +
742 pam_status) != 0) {
744 syslog(LOG_AUTH | LOG_ALERT,
745 "adt_put_event(ADT_passwd(%s): %m",
746 pam_strerror(NULL, pam_status));
748 adt_free_event(event);
750 (void) adt_end_session(ah);
752 #endif /* sun */
754 static int
755 getPassword(void)
757 char *userpass = NULL;
758 char *rootpass = NULL;
759 XWindowAttributes xgwa;
760 int y, left, done;
761 struct spwd *rspw, *uspw;
762 char *suserpass = NULL;
763 char *srootpass = NULL;
764 const char *user;
765 struct passwd *rpw, *upw;
766 #ifdef USE_PAM
767 pam_handle_t *pamh = NULL;
768 struct pam_conv pc;
769 Bool use_pam = True;
770 int pam_error;
771 int pam_flags = 0;
772 #endif
773 const char *authErrMsg = text_invalid;
775 seteuid(0);
777 rpw = getpwuid(0);
778 if (rpw) {
779 user = rpw->pw_name;
780 rootpass = strdup(rpw->pw_passwd);
782 rspw = getspnam(user);
783 if (rspw && rspw->sp_pwdp)
784 srootpass = strdup(rspw->sp_pwdp);
787 upw = getpwuid(getuid());
788 if (upw) {
789 user = upw->pw_name;
791 else
792 user = "";
794 #ifdef USE_PAM
795 pc.conv = pamconv;
797 pam_error = pam_start(XLOCK_PAM_SERVICE, user, &pc, &pamh);
798 if (pam_error != PAM_SUCCESS) {
799 use_pam = False;
800 PAM_ERROR_PRINT("pam_start");
801 } else {
802 #ifdef sun
803 /* Check /etc/default/login to see if we should add
804 PAM_DISALLOW_NULL_AUTHTOK to pam_flags */
805 if (defopen("/etc/default/login") == 0) {
806 const char *ptr;
808 int flags = defcntl(DC_GETFLAGS, 0);
809 TURNOFF(flags, DC_CASE);
810 (void) defcntl(DC_SETFLAGS, flags);
812 if ((ptr = defread("PASSREQ=")) != NULL &&
813 strcasecmp("YES", ptr) == 0) {
814 pam_flags |= PAM_DISALLOW_NULL_AUTHTOK;
817 (void) defopen(NULL); /* close current file */
820 #endif
823 if (!use_pam)
824 #endif /* USE_PAM */
826 /* Get user password for non-PAM authentication */
827 userpass = strdup(upw->pw_passwd);
829 uspw = getspnam(user);
830 if (uspw && uspw->sp_pwdp)
831 suserpass = strdup(uspw->sp_pwdp);
834 seteuid(getuid());
836 XGetWindowAttributes(dsp, win[screen], &xgwa);
838 XChangeGrabbedCursor(passwdcursor);
840 XSetForeground(dsp, Scr[screen].gc, bgcol[screen].pixel);
841 XFillRectangle(dsp, win[screen], Scr[screen].gc,
842 0, 0, xgwa.width, xgwa.height);
844 XMapWindow(dsp, icon[screen]);
845 XRaiseWindow(dsp, icon[screen]);
847 left = iconx[screen] + ICONW + font->max_bounds.width;
848 y = icony[screen] + font->ascent;
850 XDrawString(dsp, win[screen], textgc[screen],
851 left, y, text_name, (int) strlen(text_name));
852 XDrawString(dsp, win[screen], textgc[screen],
853 left + 1, y, text_name, (int) strlen(text_name));
854 XDrawString(dsp, win[screen], textgc[screen],
855 left + XTextWidth(font, text_name, (int) strlen(text_name)), y,
856 user, (int) strlen(user));
858 y = icony[screen] - (font->descent + 2);
860 XDrawString(dsp, win[screen], textgc[screen],
861 iconx[screen], y, text_info, (int) strlen(text_info));
863 passwordPrompt(text_pass);
865 XFlush(dsp);
867 y = icony[screen] + ICONH + font->ascent + 2
868 + font->ascent + font->descent + 2;
870 done = False;
871 stoptryingfornow = False;
872 while (!done) {
873 if (sigcaught)
874 sigfinish();
875 #ifdef USE_PAM
876 if (use_pam) {
877 seteuid(0);
879 pam_error = pam_authenticate(pamh, pam_flags);
880 if (pam_error == PAM_SUCCESS) {
881 const char *pam_error_from = "pam_acct_mgmt";
883 pam_error = pam_acct_mgmt(pamh, pam_flags);
885 if (pam_error == PAM_NEW_AUTHTOK_REQD) {
886 do {
887 pam_error = pam_chauthtok(pamh,
888 PAM_CHANGE_EXPIRED_AUTHTOK);
889 } while (pam_error == PAM_AUTHTOK_ERR ||
890 pam_error == PAM_TRY_AGAIN);
891 pam_error_from = "pam_chauthtok";
892 #ifdef sun
893 audit_passwd(pam_error);
894 #endif /* sun */
897 if (pam_error == PAM_SUCCESS) {
898 pam_error = pam_setcred(pamh,PAM_REFRESH_CRED);
899 if (pam_error != PAM_SUCCESS) {
900 PAM_ERROR_PRINT("pam_setcred(PAM_REFRESH_CRED)");
901 } else {
902 done = True;
904 } else {
905 #ifdef sun
906 audit_unlock(pam_error);
907 #endif /* sun */
908 PAM_ERROR_PRINT(pam_error_from);
910 } else if (stoptryingfornow) {
911 seteuid(getuid());
912 break;
913 } else {
914 #ifdef sun
915 audit_unlock(pam_error);
916 #endif /* sun */
917 PAM_ERROR_PRINT("pam_authenticate");
920 if (pam_error != PAM_SUCCESS) {
921 authErrMsg = pam_strerror(pamh, pam_error);
924 seteuid(getuid());
925 } else if (ReadXString(buffer, PAM_MAX_RESP_SIZE))
926 break;
927 #endif
930 * This section gets a little messy. In SYSV, the number of
931 * cases to handle increases because of the existence of the
932 * shadow file. There are also a number of cases that need
933 * to be dealt with where either root or user passwords are
934 * nil. Hopefully the code below is easy enough to follow.
937 if (userpass) {
938 if (*userpass == '\0') {
939 done = (*buffer == '\0');
940 } else {
941 done = (!strcmp(crypt(buffer, userpass), userpass));
944 if (!done && suserpass) {
945 if (*suserpass == '\0') {
946 done = (*buffer == '\0');
947 } else {
948 done = (!strcmp(crypt(buffer, suserpass), suserpass));
951 if (!done && allowroot) {
952 if (srootpass) {
953 if (*srootpass == '\0') {
954 done = (*buffer == '\0');
955 } else {
956 done = (!strcmp(crypt(buffer, srootpass), srootpass));
959 if (!done && rootpass) {
960 if (*rootpass == '\0') {
961 done = (*buffer == '\0');
962 } else {
963 done = (!strcmp(crypt(buffer, rootpass), rootpass));
968 /* clear plaintext password so you can't grunge around /dev/kmem */
969 EXPLICIT_BZERO(buffer, sizeof(buffer));
971 displayTextInfo(text_valid);
973 if (done) {
974 /* clear encrypted passwords just in case */
975 if (rootpass) {
976 EXPLICIT_BZERO(rootpass, strlen(rootpass));
977 free(rootpass);
978 rootpass = NULL;
980 if (userpass) {
981 EXPLICIT_BZERO(userpass, strlen(userpass));
982 free(userpass);
983 userpass = NULL;
985 if (srootpass) {
986 EXPLICIT_BZERO(srootpass, strlen(srootpass));
987 free(srootpass);
988 srootpass = NULL;
990 if (suserpass) {
991 EXPLICIT_BZERO(suserpass, strlen(suserpass));
992 free(suserpass);
993 suserpass = NULL;
995 #ifdef USE_PAM
996 seteuid(0);
997 #ifdef sun
998 audit_unlock(pam_error);
999 #endif /* sun */
1000 pam_end(pamh, pam_error);
1001 seteuid(getuid());
1002 #endif
1003 return 0;
1004 } else {
1005 XSync(dsp, True); /* flush input buffer */
1006 sleep(1);
1008 displayTextInfo(authErrMsg);
1010 if (echokeys || reallyechokeys) /* erase old echo */
1011 XFillRectangle(dsp, win[screen], Scr[screen].gc,
1012 passx, passy - font->ascent,
1013 xgwa.width - passx,
1014 font->ascent + font->descent);
1017 /* clear encrypted passwords just in case */
1018 if (rootpass) {
1019 EXPLICIT_BZERO(rootpass, strlen(rootpass));
1020 free(rootpass);
1021 rootpass = NULL;
1023 if (userpass) {
1024 EXPLICIT_BZERO(userpass, strlen(userpass));
1025 free(userpass);
1026 userpass = NULL;
1028 if (srootpass) {
1029 EXPLICIT_BZERO(srootpass, strlen(srootpass));
1030 free(srootpass);
1031 srootpass = NULL;
1033 if (suserpass) {
1034 EXPLICIT_BZERO(suserpass, strlen(suserpass));
1035 free(suserpass);
1036 suserpass = NULL;
1038 #ifdef USE_PAM
1039 seteuid(0);
1040 pam_end(pamh, pam_error);
1041 seteuid(getuid());
1042 #endif
1043 XChangeGrabbedCursor(mycursor);
1044 XUnmapWindow(dsp, icon[screen]);
1045 return 1;
1048 static void
1049 justDisplay(void)
1051 XEvent event;
1053 for (screen = 0; screen < screens; screen++)
1054 init(win[screen]);
1055 do {
1056 while (!XPending(dsp)) {
1057 if (sigcaught)
1058 sigfinish();
1059 for (screen = 0; screen < screens; screen++)
1060 callback(win[screen]);
1061 XFlush(dsp);
1062 usleep((useconds_t) delay);
1064 XNextEvent(dsp, &event);
1065 if (sigcaught)
1066 sigfinish();
1067 #ifndef DEBUG
1068 if (event.type == VisibilityNotify)
1069 XRaiseWindow(dsp, event.xany.window);
1070 #endif
1071 } while (event.type != ButtonPress && event.type != KeyPress);
1072 for (screen = 0; screen < screens; screen++)
1073 if (event.xbutton.root == RootWindow(dsp, screen))
1074 break;
1075 if (usefirst)
1076 XPutBackEvent(dsp, &event);
1080 static void
1081 lockDisplay(void)
1083 if (!allowaccess) {
1084 sigset_t oldsigmask;
1085 sigset_t newsigmask;
1086 struct sigaction sigact;
1088 sigemptyset(&newsigmask);
1089 sigaddset(&newsigmask, SIGHUP);
1090 sigaddset(&newsigmask, SIGINT);
1091 sigaddset(&newsigmask, SIGQUIT);
1092 sigaddset(&newsigmask, SIGTERM);
1093 sigprocmask(SIG_BLOCK, &newsigmask, &oldsigmask);
1095 sigact.sa_sigaction = sigcatch;
1096 sigact.sa_mask = newsigmask;
1097 sigact.sa_flags = SA_SIGINFO;
1099 sigaction(SIGHUP, &sigact, NULL);
1100 sigaction(SIGINT, &sigact, NULL);
1101 sigaction(SIGQUIT, &sigact, NULL);
1102 sigaction(SIGTERM, &sigact, NULL);
1104 XGrabHosts(dsp);
1106 sigprocmask(SIG_SETMASK, &oldsigmask, &oldsigmask);
1108 #ifdef sun
1109 seteuid(0);
1110 audit_lock();
1111 seteuid(getuid());
1112 #endif /* sun */
1113 do {
1114 justDisplay();
1115 } while (getPassword());
1120 main(
1121 int argc,
1122 char *argv[]
1125 XSetWindowAttributes xswa;
1126 XGCValues xgcv;
1128 if ((geteuid() != 0) || (seteuid(getuid()) != 0)) {
1129 error("Not running with root privileges. Exiting ...\n"
1130 "\tYou need to run xlock in setuid root mode on your local machine.\n"
1131 "\tContact your system administrator.\n");
1134 ProgramName = strrchr(argv[0], '/');
1135 if (ProgramName)
1136 ProgramName++;
1137 else
1138 ProgramName = argv[0];
1140 srandom((uint_t) time((long *) 0)); /* random mode needs the seed set. */
1142 exp_bzero =
1143 (void (*)(void *, size_t)) dlsym(RTLD_DEFAULT, "explicit_bzero");
1144 if (exp_bzero == NULL) {
1145 /* If the explicit version isn't found, at least the compilers we
1146 use won't optimize out a call to a function found via dlsym(). */
1147 exp_bzero = (void (*)(void *, size_t)) dlsym(RTLD_DEFAULT, "bzero");
1148 if (exp_bzero == NULL) {
1149 const char *dle = dlerror();
1150 char errmsg[BUFSIZ];
1152 snprintf(errmsg, sizeof(errmsg), "%s\n Exiting ...\n", dle);
1153 error(errmsg);
1157 GetResources(argc, argv);
1159 CheckResources();
1161 font = XLoadQueryFont(dsp, fontname);
1162 if (font == NULL) {
1163 fprintf(stderr, "%s: can't find font: %s, using %s...\n",
1164 ProgramName, fontname, FALLBACK_FONTNAME);
1165 font = XLoadQueryFont(dsp, FALLBACK_FONTNAME);
1166 if (font == NULL)
1167 error("can't even find %s!!!\n", FALLBACK_FONTNAME);
1170 screens = ScreenCount(dsp);
1171 if (screens > MAXSCREENS)
1172 error("can only support %d screens.\n", MAXSCREENS);
1173 for (screen = 0; screen < screens; screen++) {
1174 XColor tmp;
1175 Screen *scr = ScreenOfDisplay(dsp, screen);
1176 Visual *vis = XDefaultVisual(dsp, screen);
1177 Colormap cmap;
1178 root[screen] = RootWindowOfScreen(scr);
1180 cmap = XCreateColormap(dsp, root[screen], vis, AllocNone);
1182 XAllocNamedColor(dsp, cmap, "White", &sswhite[screen], &tmp);
1183 XAllocNamedColor(dsp, cmap, "Black", &ssblack[screen], &tmp);
1185 if (mono || CellsOfScreen(scr) == 2) {
1186 if (!XAllocNamedColor(dsp, cmap, background,
1187 &bgcol[screen], &tmp)) {
1188 XAllocNamedColor(dsp, cmap, "White", &bgcol[screen], &tmp);
1190 if (!XAllocNamedColor(dsp, cmap, foreground,
1191 &fgcol[screen], &tmp)) {
1192 XAllocNamedColor(dsp, cmap, "Black", &fgcol[screen], &tmp);
1194 Scr[screen].pixels[0] = fgcol[screen].pixel;
1195 Scr[screen].pixels[1] = bgcol[screen].pixel;
1196 Scr[screen].npixels = 2;
1197 } else {
1198 int colorcount = NUMCOLORS;
1199 u_char red[NUMCOLORS];
1200 u_char green[NUMCOLORS];
1201 u_char blue[NUMCOLORS];
1202 int i;
1204 if (!XAllocNamedColor(dsp, cmap, background,
1205 &bgcol[screen], &tmp)) {
1206 fprintf(stderr, "couldn't allocate: %s\n", background);
1207 XAllocNamedColor(dsp, cmap, "White", &bgcol[screen], &tmp);
1209 if (!XAllocNamedColor(dsp, cmap, foreground,
1210 &fgcol[screen], &tmp)) {
1211 fprintf(stderr, "couldn't allocate: %s\n", foreground);
1212 XAllocNamedColor(dsp, cmap, "Black", &fgcol[screen], &tmp);
1214 hsbramp(0.0, saturation, 1.0, 1.0, saturation, 1.0, colorcount,
1215 red, green, blue);
1216 Scr[screen].npixels = 0;
1217 for (i = 0; i < colorcount; i++) {
1218 XColor xcolor = {
1219 .red = (unsigned short) (red[i] << 8),
1220 .green = (unsigned short) (green[i] << 8),
1221 .blue = (unsigned short) (blue[i] << 8),
1222 .flags = DoRed | DoGreen | DoBlue
1225 if (!XAllocColor(dsp, cmap, &xcolor))
1226 break;
1228 Scr[screen].pixels[i] = xcolor.pixel;
1229 Scr[screen].npixels++;
1231 if (verbose)
1232 fprintf(stderr, "%d pixels allocated\n", Scr[screen].npixels);
1235 xswa.override_redirect = True;
1236 xswa.background_pixel = ssblack[screen].pixel;
1237 xswa.event_mask = KeyPressMask | ButtonPressMask | VisibilityChangeMask;
1238 xswa.colormap = cmap; /* In DEBUG mode, we do not see this */
1240 win[screen] = XCreateWindow(dsp, root[screen], 0, 0, WIDTH, HEIGHT, 0,
1241 CopyFromParent, InputOutput, CopyFromParent,
1242 CWMASK, &xswa);
1244 #ifdef DEBUG
1246 XWMHints xwmh;
1248 xwmh.flags = InputHint;
1249 xwmh.input = True;
1250 XChangeProperty(dsp, win[screen],
1251 XA_WM_HINTS, XA_WM_HINTS, 32, PropModeReplace,
1252 (unsigned char *) &xwmh, sizeof(xwmh) / sizeof(int));
1254 #endif
1256 iconx[screen] = (DisplayWidth(dsp, screen) -
1257 XTextWidth(font, text_info, (int) strlen(text_info))) / 2;
1259 icony[screen] = DisplayHeight(dsp, screen) / 6;
1261 xswa.border_pixel = fgcol[screen].pixel;
1262 xswa.background_pixel = bgcol[screen].pixel;
1263 xswa.event_mask = ButtonPressMask;
1264 xswa.colormap = cmap; /* In DEBUG mode, we do not see this */
1266 #define CIMASK CWBorderPixel | CWBackPixel | CWEventMask | CWColormap
1267 icon[screen] = XCreateWindow(dsp, win[screen],
1268 iconx[screen], icony[screen],
1269 ICONW, ICONH, 1, CopyFromParent,
1270 InputOutput, CopyFromParent,
1271 CIMASK, &xswa);
1273 XMapWindow(dsp, win[screen]);
1274 XRaiseWindow(dsp, win[screen]);
1275 XInstallColormap(dsp, cmap);
1277 xgcv.foreground = sswhite[screen].pixel;
1278 xgcv.background = ssblack[screen].pixel;
1279 Scr[screen].gc = XCreateGC(dsp, win[screen],
1280 GCForeground | GCBackground, &xgcv);
1282 xgcv.foreground = fgcol[screen].pixel;
1283 xgcv.background = bgcol[screen].pixel;
1284 xgcv.font = font->fid;
1285 textgc[screen] = XCreateGC(dsp, win[screen],
1286 GCFont | GCForeground | GCBackground, &xgcv);
1288 lockc = XCreateBitmapFromData(dsp, root[0], no_bits, 1, 1);
1289 lockm = XCreateBitmapFromData(dsp, root[0], no_bits, 1, 1);
1290 mycursor = XCreatePixmapCursor(dsp, lockc, lockm,
1291 &fgcol[screen], &bgcol[screen], 0, 0);
1292 passwdcursor = XCreateFontCursor(dsp, XC_left_ptr);
1293 XFreePixmap(dsp, lockc);
1294 XFreePixmap(dsp, lockm);
1297 if (!enablesaver) {
1298 XGetScreenSaver(dsp, &sstimeout, &ssinterval,
1299 &ssblanking, &ssexposures);
1300 XSetScreenSaver(dsp, 0, 0, 0, 0); /* disable screen saver */
1302 #ifndef DEBUG
1303 GrabKeyboardAndMouse();
1304 #endif
1306 nice(nicelevel);
1308 if (nolock)
1309 justDisplay();
1310 else
1311 lockDisplay();
1313 finish();
1315 return 0;