This repository has been archived on 2025-02-01. You can view files and clone it, but cannot push or open issues or pull requests.
alock/aklock.c
mathias 620b251aaf initial import
--HG--
extra : convert_revision : svn%3Aeebe1cee-a9af-4fe4-bd26-ad572b19c5ab/trunk%401
2005-04-30 00:35:03 +00:00

482 lines
13 KiB
C

/*------------------------------------------------------------------*\
file: aklock.c
X Transparent Lock
copyright:
Copyright (C)1993,1994 Ian Jackson (xtrlock)
Copyright (C)2005 Mathias Gumz (forked aklock)
license:
This is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.
This is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
\*------------------------------------------------------------------*/
/*------------------------------------------------------------------*\
\*------------------------------------------------------------------*/
#include <X11/X.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/keysym.h>
#include <X11/Xos.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <pwd.h>
#include <grp.h>
#include <limits.h>
#include <string.h>
#include <unistd.h>
#include <math.h>
#include <ctype.h>
#ifdef SHADOW_PWD
# include <shadow.h>
#endif /* SHADOW_PWD */
#ifdef PAM_PWD
# include <security/pam_appl.h>
# ifdef LINUX
# include <security/pam_misc.h>
# endif
#endif /* PAM_PWD */
/*----------------------------------------------*\
\*----------------------------------------------*/
#include "lock.bitmap"
#include "mask.bitmap"
/*------------------------------------------------------------------*\
globals
\*------------------------------------------------------------------*/
#define TIMEOUTPERATTEMPT 30000
#define MAXGOODWILL (TIMEOUTPERATTEMPT*5)
#define INITIALGOODWILL MAXGOODWILL
#define GOODWILLPORTION 0.3
#ifdef DEBUG
# define DBGMSG fprintf(stderr, "%s : %d\n", __FUNCTION__, __LINE__); fflush(stderr)
#else
# define DBGMSG
#endif // DEBUG
struct passwd *pw;
/*------------------------------------------------------------------*\
\*------------------------------------------------------------------*/
void displayUsage() {
printf("\naklock [-hv] [-blank]\n\n"
" -h shows this little help\n"
" -v shows the version number\n"
" -blank hides the content of the screen\n"
"\n"
"when the screen is locked, just enter your password.\n");
}
/*------------------------------------------------------------------*\
pam-related stuff
taken from pure-ftpd's authstuff, but you can see similar stuff
in xlockmore, openssh and basicly all pam-related apps :)
\*------------------------------------------------------------------*/
#ifdef PAM_PWD
# define PAM_YN { \
if (PAM_error != 0 || pam_error != PAM_SUCCESS) { \
fprintf(stderr, "pam error:%s\n", pam_strerror(pam_handle, pam_error)); \
pam_end(pam_handle, pam_error); \
PAM_username = NULL; \
PAM_password = NULL; \
return 0;\
} \
}
# define GET_MEM \
size += sizeof(struct pam_response); \
if ((reply = realloc(reply, size)) == NULL) { \
PAM_error = 1; \
return PAM_CONV_ERR; \
}
static const char* PAM_username = NULL;
static const char* PAM_password = NULL;
static int PAM_error = 0;
static int pam_error = PAM_SUCCESS;
static int PAM_conv(int num_msg, const struct pam_message **msgs,
struct pam_response **resp, void *appdata_ptr) {
int count = 0;
unsigned int replies = 0U;
struct pam_response *reply = NULL;
size_t size = (size_t) 0U;
(void) appdata_ptr;
*resp = NULL;
for (count = 0; count < num_msg; count++) {
switch (msgs[count]->msg_style) {
case PAM_PROMPT_ECHO_ON:
GET_MEM;
memset(&reply[replies], 0, sizeof reply[replies]);
if ((reply[replies].resp = strdup(PAM_username)) == NULL) {
# ifdef PAM_BUF_ERR
reply[replies].resp_retcode = PAM_BUF_ERR;
# endif
PAM_error = 1;
return PAM_CONV_ERR;
}
reply[replies++].resp_retcode = PAM_SUCCESS;
/* PAM frees resp */
break;
case PAM_PROMPT_ECHO_OFF:
GET_MEM;
memset(&reply[replies], 0, sizeof reply[replies]);
if ((reply[replies].resp = strdup(PAM_password)) == NULL) {
# ifdef PAM_BUF_ERR
reply[replies].resp_retcode = PAM_BUF_ERR;
# endif
PAM_error = 1;
return PAM_CONV_ERR;
}
reply[replies++].resp_retcode = PAM_SUCCESS;
/* PAM frees resp */
break;
case PAM_TEXT_INFO:
/* ignore it... */
break;
case PAM_ERROR_MSG:
default:
/* Must be an error of some sort... */
free(reply);
PAM_error = 1;
return PAM_CONV_ERR;
}
}
*resp = reply;
return PAM_SUCCESS;
}
static struct pam_conv PAM_conversation = {
&PAM_conv, NULL
};
#endif /* PAM_PWD */
/*------------------------------------------------------------------*\
\*------------------------------------------------------------------*/
int passwordOk(const char *s) {
#if 0
char key[3];
char *encr;
key[0] = *(pw->pw_passwd);
key[1] = (pw->pw_passwd)[1];
key[2] = 0;
encr = crypt(s, key);
return !strcmp(encr, pw->pw_passwd);
#else
/* simpler, and should work with crypt() algorithms using longer
salt strings (like the md5-based one on freebsd). --marekm */
#ifdef PAM_PWD
pam_handle_t* pam_handle = NULL;
PAM_username = pw->pw_name;
PAM_password = s;
pam_error = pam_start("login", PAM_username, &PAM_conversation, &pam_handle);
PAM_YN;
pam_error = pam_authenticate(pam_handle, 0);
PAM_YN;
pam_error = pam_end(pam_handle, pam_error);
PAM_YN;
return 1;
#else
return !strcmp(crypt(s, pw->pw_passwd), pw->pw_passwd);
#endif /* PAM_PWD */
#endif /* 0 */
}
/*------------------------------------------------------------------*\
check if the system would be able to authentificate the user
\*------------------------------------------------------------------*/
void checkAuth() {
#ifdef SHADOW_PWD
struct spwd *sp;
#endif
errno = 0;
pw = getpwuid(getuid());
if (!pw) {
perror("password entry for uid not found");
exit(1);
}
#ifdef SHADOW_PWD
sp = getspnam(pw->pw_name);
if (sp)
pw->pw_passwd = sp->sp_pwdp;
endspent();
#endif
/* we can be installed setuid root to support shadow passwords,
and we don't need root privileges any longer. --marekm */
setuid(getuid());
#ifndef PAM_PWD
if (strlen(pw->pw_passwd) < 13) {
perror("aklock: password entry has no pwd\n");
exit(1);
}
#endif /* PAM_PWD */
}
/*------------------------------------------------------------------*\
\*------------------------------------------------------------------*/
struct XInfo {
Display* display;
Window root;
Window window;
Cursor cursor;
int width;
int height;
};
struct Opts {
char use_blank;
char* color_fg;
char* color_bg;
};
/*------------------------------------------------------------------*\
\*------------------------------------------------------------------*/
void initOpts(struct Opts* opts) {
opts->use_blank = 0;
opts->color_bg = "steelblue3";
opts->color_fg = "grey25";
}
void initXInfo(struct XInfo* xinfo, struct Opts* opts) {
Display* dpy = XOpenDisplay(NULL);
XColor color_fg;
XColor color_bg;
XColor tmp_color;
Colormap color_map;
Pixmap pixmap_cursor;
Pixmap pixmap_cursor_mask;
XWindowAttributes xgwa;
if (!dpy) {
perror("aklock: error, can't open connection to X");
exit(1);
}
xinfo->display = dpy;
xinfo->window = 0;
xinfo->root = DefaultRootWindow(dpy);
color_map = DefaultColormap(dpy, DefaultScreen(dpy));
XGetWindowAttributes(dpy, xinfo->root, &xgwa);
xinfo->width = xgwa.width;
xinfo->height = xgwa.height;
pixmap_cursor = XCreateBitmapFromData(dpy, xinfo->root, lock_bits, lock_width, lock_height);
pixmap_cursor_mask = XCreateBitmapFromData(dpy, xinfo->root, mask_bits, mask_width, mask_height);
if((XAllocNamedColor(dpy, color_map, opts->color_bg, &tmp_color, &color_bg)) == 0)
XAllocNamedColor(dpy, color_map, "black", &tmp_color, &color_bg);
if((XAllocNamedColor(dpy, color_map, opts->color_fg, &tmp_color, &color_fg)) == 0)
XAllocNamedColor(dpy, color_map, "white", &tmp_color, &color_fg);
xinfo->cursor = XCreatePixmapCursor(dpy,
pixmap_cursor, pixmap_cursor_mask,
&color_fg, &color_bg,
lock_x_hot, lock_y_hot);
}
int main(int argc, char **argv) {
XEvent ev;
KeySym ks;
char cbuf[10], rbuf[50];
int clen, rlen = 0;
long goodwill = INITIALGOODWILL, timeout = 0;
XSetWindowAttributes xswa;
long xsmask = 0;
struct XInfo xinfo;
struct Opts opts;
int arg = 0;
initOpts(&opts);
// parse options
if (argc != 1) {
for(arg = 1; arg <= argc; arg++) {
if (!strcmp(argv[arg - 1], "-blank")) {
opts.use_blank = 1;
} else if (!strcmp(argv[arg - 1], "-h")) {
displayUsage();
exit(0);
} else if (!strcmp(argv[arg - 1], "-v")) {
printf("aklock-%s by m.gumz 2005\n", VERSION);
exit(0);
}
}
}
checkAuth();
initXInfo(&xinfo, &opts);
/* create the windows */
xswa.override_redirect = True;
xsmask |= CWOverrideRedirect;
if (opts.use_blank) {
xswa.background_pixel = BlackPixel(xinfo.display, DefaultScreen(xinfo.display));
xsmask |= CWBackPixel;
xinfo.window = XCreateWindow(xinfo.display, xinfo.root,
0, 0, xinfo.width, xinfo.height,
0, /* borderwidth */
CopyFromParent, /* depth */
InputOutput, /* class */
CopyFromParent, /* visual */
xsmask, &xswa);
} else {
xinfo.window = XCreateWindow(xinfo.display, xinfo.root,
0, 0, 1, 1,
0, /* borderwidth */
CopyFromParent, /* depth */
InputOnly, /* class */
CopyFromParent, /* visual */
xsmask, &xswa);
}
XSelectInput(xinfo.display, xinfo.window, KeyPressMask|KeyReleaseMask);
XMapWindow(xinfo.display, xinfo.window);
XRaiseWindow(xinfo.display, xinfo.window);
/* try to grab 2 times, another process (windowmanager) may have grabbed
* the keyboard already */
if ((XGrabKeyboard(xinfo.display, xinfo.window, True, GrabModeAsync, GrabModeAsync,
CurrentTime)) != GrabSuccess) {
sleep(1);
if ((XGrabKeyboard(xinfo.display, xinfo.window, True, GrabModeAsync, GrabModeAsync,
CurrentTime)) != GrabSuccess) {
perror("aklock: couldnt grab the keyboard.\n");
exit(1);
}
}
if (XGrabPointer(xinfo.display, xinfo.window, False, (KeyPressMask|KeyReleaseMask) & 0,
GrabModeAsync, GrabModeAsync, None, xinfo.cursor, CurrentTime) != GrabSuccess) {
XUngrabKeyboard(xinfo.display, CurrentTime);
perror("aklock: couldnt grab the pointer.\n");
exit(1);
}
/* eventhandling */
for(;;) {
XNextEvent(xinfo.display, &ev);
switch (ev.type) {
case KeyPress:
if (ev.xkey.time < timeout) {
XBell(xinfo.display, 0);
break;
}
clen = XLookupString(&ev.xkey, cbuf, 9, &ks, 0);
switch (ks) {
case XK_Escape:
case XK_Clear:
rlen = 0;
break;
case XK_Delete:
case XK_BackSpace:
if (rlen > 0)
rlen--;
break;
case XK_Linefeed:
case XK_Return:
if (rlen == 0)
break;
rbuf[rlen] = 0;
if (passwordOk(rbuf))
goto exit;
XBell(xinfo.display, 0);
rlen = 0;
if (timeout) {
goodwill += ev.xkey.time - timeout;
if (goodwill > MAXGOODWILL) {
goodwill = MAXGOODWILL;
}
}
timeout = -goodwill * GOODWILLPORTION;
goodwill += timeout;
timeout += ev.xkey.time + TIMEOUTPERATTEMPT;
break;
default:
if (clen != 1)
break;
if (rlen < sizeof(rbuf))
rbuf[rlen] = cbuf[0];
rlen++;
break;
}
break;
default:
break;
}
}
exit:
XDestroyWindow(xinfo.display, xinfo.window);
XCloseDisplay(xinfo.display);
return 0;
}