summaryrefslogtreecommitdiffstats
path: root/kcheckpass
diff options
context:
space:
mode:
authortoma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da>2009-11-25 17:56:58 +0000
committertoma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da>2009-11-25 17:56:58 +0000
commit4aed2c8219774f5d797760606b8489a92ddc5163 (patch)
tree3f8c130f7d269626bf6a9447407ef6c35954426a /kcheckpass
downloadtdebase-4aed2c8219774f5d797760606b8489a92ddc5163.tar.gz
tdebase-4aed2c8219774f5d797760606b8489a92ddc5163.zip
Copy the KDE 3.5 branch to branches/trinity for new KDE 3.5 features.
BUG:215923 git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/kdebase@1054174 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
Diffstat (limited to 'kcheckpass')
-rw-r--r--kcheckpass/Makefile.am26
-rw-r--r--kcheckpass/README51
-rw-r--r--kcheckpass/checkpass_aix.c95
-rw-r--r--kcheckpass/checkpass_etcpasswd.c60
-rw-r--r--kcheckpass/checkpass_osfc2passwd.c203
-rw-r--r--kcheckpass/checkpass_pam.c200
-rw-r--r--kcheckpass/checkpass_shadow.c86
-rw-r--r--kcheckpass/kcheckpass.c448
-rw-r--r--kcheckpass/kcheckpass.h142
9 files changed, 1311 insertions, 0 deletions
diff --git a/kcheckpass/Makefile.am b/kcheckpass/Makefile.am
new file mode 100644
index 000000000..bc8342301
--- /dev/null
+++ b/kcheckpass/Makefile.am
@@ -0,0 +1,26 @@
+## Makefile.am for kcheckpass
+## written by Christian Esken
+##
+
+INCLUDES= $(KDE_USE_FPIE) $(all_includes)
+
+bin_PROGRAMS = kcheckpass
+
+kcheckpass_SOURCES = kcheckpass.c \
+ checkpass_etcpasswd.c checkpass_pam.c checkpass_shadow.c \
+ checkpass_osfc2passwd.c checkpass_aix.c
+kcheckpass_LDADD = -lkdefakes $(PASSWDLIBS) $(LIBSOCKET)
+kcheckpass_LDFLAGS = $(KDE_USE_PIE) $(all_libraries)
+
+noinst_HEADERS = kcheckpass.h
+
+EXTRA_DIST = README
+
+PAM = $(KCHECKPASS_PAM_SERVICE)
+
+install-data-local:
+ -@test -n "$(DESTDIR)" || test -z "$(PAM)" || $(top_srcdir)/mkpamserv $(PAM)
+
+install-exec-hook:
+ @(chown 0 $(DESTDIR)$(bindir)/kcheckpass && chmod 4755 $(DESTDIR)$(bindir)/kcheckpass) \
+ || echo "Error: Could not install kcheckpass as setuid root (possibly you won't be able to unlock)!!"
diff --git a/kcheckpass/README b/kcheckpass/README
new file mode 100644
index 000000000..cb517b3d2
--- /dev/null
+++ b/kcheckpass/README
@@ -0,0 +1,51 @@
+The KCheckPass authentication software:
+-----------------------------------------
+
+KCheckPass is KDE's authentication program. It is meant to be
+used by any software in need of user authentication, most
+notably screensavers.
+
+It enhances security be the following means:
+
+- It's only a small program, which is hopefully simple enough to
+ allow it to be SUID root. Setting it to SUID root is necessary
+ on Shadow Password systems.
+- No other program in need of user authentication, must be
+ SUID root.
+- It provides a single implementation to check passwords. So one
+ only must take a closer look at KCheckPass to ensure password
+ security. It's much easier for programs using KCheckPass to
+ preserve security.
+
+
+Technique:
+----------
+KCheckPass is a simple password checker. Just invoke and
+send it the password on stdin.
+
+If the password was accepted, the program exits with 0;
+if it was rejected, it exits with 1. Any other exit
+code signals an error.
+
+
+
+Compilation hints:
+------------------
+Compile with -DHAVE_VSYSLOG if you have vsyslog().
+Compile with -DHAVE_PAM if you have a PAM system, and link with -lpam -ldl
+ (If libdl is present).
+Compile with -DHAVE_SHADOW if you have a shadow password system.
+
+Copyright, Author and License notice:
+-------------------------------------
+Copyright (C) 1998, Caldera, Inc.
+Released under the GNU General Public License
+
+Olaf Kirch <okir@caldera.de> General Framework and PAM support
+Christian Esken <esken@kde.org> Shadow and /etc/passwd support
+Oswald Buddenhagen <ossi@kde.org> Binary conversation interface, etc.
+
+Some parts were taken from kscreensaver's passwd.cpp
+
+Currently this software is maintained by Oswald Buddenhagen <ossi@kde.org>.
+Please send new authentication modules (checkpass_*.c) to me.
diff --git a/kcheckpass/checkpass_aix.c b/kcheckpass/checkpass_aix.c
new file mode 100644
index 000000000..8f06cfe67
--- /dev/null
+++ b/kcheckpass/checkpass_aix.c
@@ -0,0 +1,95 @@
+/*
+ * Copyright (c) 2001 Reza Arbab <arbab@austin.ibm.com>
+ * Copyright (c) 2003 Oswald Buddenhagen <ossi@kde.org>
+ *
+ * This program 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 of the License, or (at your option) any later version.
+ *
+ * This program 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.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "kcheckpass.h"
+
+#ifdef HAVE_AIX_AUTH
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+/*
+ * The AIX builtin authenticate() uses whichever method the system
+ * has been configured for. (/etc/passwd, DCE, etc.)
+ */
+int authenticate(const char *, const char *, int *, char **);
+
+AuthReturn Authenticate(const char *method,
+ const char *login, char *(*conv) (ConvRequest, const char *))
+{
+ int result;
+ int reenter; /* Tells if authenticate is done processing or not. */
+ char *passwd;
+ char *msg; /* Contains a prompt message or failure reason. */
+
+ if (!strcmp(method, "classic")) {
+
+ if (!(passwd = conv(ConvGetHidden, 0)))
+ return AuthAbort;
+
+ if ((result = authenticate(login, passwd, &reenter, &msg))) {
+ if (msg) {
+ conv(ConvPutError, msg);
+ free(msg);
+ }
+ dispose(passwd);
+ return AuthBad;
+ }
+ if (reenter) {
+ char buf[256];
+ snprintf(buf, sizeof(buf), "More authentication data requested: %s\n", msg);
+ conv(ConvPutError, buf);
+ free(msg);
+ dispose(passwd);
+ return result == ENOENT || result == ESAD ? AuthBad : AuthError;
+ }
+ dispose(passwd);
+ return AuthOk;
+
+ } else if (!strcmp(method, "generic")) {
+
+ for (passwd = 0;;) {
+ if ((result = authenticate(login, passwd, &reenter, &msg))) {
+ if (msg) {
+ conv(ConvPutError, msg);
+ free(msg);
+ }
+ if (passwd)
+ dispose(passwd);
+ return result == ENOENT || result == ESAD ? AuthBad : AuthError;
+ }
+ if (passwd)
+ dispose(passwd);
+ if (!reenter)
+ break;
+ passwd = conv(ConvGetHidden, msg);
+ free(msg);
+ if (!passwd)
+ return AuthAbort;
+ }
+ return AuthOk;
+
+ } else
+ return AuthError;
+
+}
+
+#endif
diff --git a/kcheckpass/checkpass_etcpasswd.c b/kcheckpass/checkpass_etcpasswd.c
new file mode 100644
index 000000000..1dbe06f70
--- /dev/null
+++ b/kcheckpass/checkpass_etcpasswd.c
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 1998 Christian Esken <esken@kde.org>
+ * Copyright (c) 2003 Oswald Buddenhagen <ossi@kde.org>
+ *
+ * This program 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 of the License, or (at your option) any later version.
+ *
+ * This program 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.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Copyright (C) 1998, Christian Esken <esken@kde.org>
+ */
+
+#include "kcheckpass.h"
+
+#ifdef HAVE_ETCPASSWD
+
+/*******************************************************************
+ * This is the authentication code for /etc/passwd passwords
+ *******************************************************************/
+
+#include <string.h>
+#include <stdlib.h>
+
+AuthReturn Authenticate(const char *method,
+ const char *login, char *(*conv) (ConvRequest, const char *))
+{
+ struct passwd *pw;
+ char *passwd;
+
+ if (strcmp(method, "classic"))
+ return AuthError;
+
+ /* Get the password entry for the user we want */
+ if (!(pw = getpwnam(login)))
+ return AuthBad;
+
+ if (!*pw->pw_passwd)
+ return AuthOk;
+
+ if (!(passwd = conv(ConvGetHidden, 0)))
+ return AuthAbort;
+
+ if (!strcmp(pw->pw_passwd, crypt(passwd, pw->pw_passwd))) {
+ dispose(passwd);
+ return AuthOk; /* Success */
+ }
+ dispose(passwd);
+ return AuthBad; /* Password wrong or account locked */
+}
+
+#endif
diff --git a/kcheckpass/checkpass_osfc2passwd.c b/kcheckpass/checkpass_osfc2passwd.c
new file mode 100644
index 000000000..d7663bdcc
--- /dev/null
+++ b/kcheckpass/checkpass_osfc2passwd.c
@@ -0,0 +1,203 @@
+/*
+ *
+ * Copyright (C) 1999 Mark Davies <mark@MCS.VUW.AC.NZ>
+ * Copyright (C) 2003 Oswald Buddenhagen <ossi@kde.org>
+ *
+ * This program 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 of the License, or (at your option) any later version.
+ *
+ * This program 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.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "kcheckpass.h"
+
+#ifdef HAVE_OSF_C2_PASSWD
+
+static char *osf1c2crypt(const char *pw, char *salt);
+static int osf1c2_getprpwent(char *p, char *n, int len);
+
+/*******************************************************************
+ * This is the authentication code for OSF C2 security passwords
+ *******************************************************************/
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+AuthReturn Authenticate(const char *method,
+ const char *login, char *(*conv) (ConvRequest, const char *))
+{
+ char *passwd;
+ char c2passwd[256];
+
+ if (strcmp(method, "classic"))
+ return AuthError;
+
+ if (!osf1c2_getprpwent(c2passwd, login, sizeof(c2passwd)))
+ return AuthBad;
+
+ if (!*c2passwd)
+ return AuthOk;
+
+ if (!(passwd = conv(ConvGetHidden, 0)))
+ return AuthAbort;
+
+ if (!strcmp(c2passwd, osf1c2crypt(passwd, c2passwd))) {
+ dispose(passwd);
+ return AuthOk; /* Success */
+ }
+ dispose(passwd);
+ return AuthBad; /* Password wrong or account locked */
+}
+
+
+/*
+The following code was lifted from the file osfc2.c from the ssh 1.2.26
+distribution. Parts of the code that were not needed by kcheckpass
+(notably the osf1c2_check_account_and_terminal() function and the code
+to set the external variable days_before_password_expires have been
+removed). The original copyright from the osfc2.c file is included
+below.
+*/
+
+/*
+
+osfc2.c
+
+Author: Christophe Wolfhugel
+
+Copyright (c) 1995 Christophe Wolfhugel
+
+Free use of this file is permitted for any purpose as long as
+this copyright is preserved in the header.
+
+This program implements the use of the OSF/1 C2 security extensions
+within ssh. See the file COPYING for full licensing informations.
+
+*/
+
+#include <sys/security.h>
+#include <prot.h>
+#include <sia.h>
+
+static int c2security = -1;
+static int crypt_algo;
+
+static void
+initialize_osf_security(int ac, char **av)
+{
+ FILE *f;
+ char buf[256];
+ char siad[] = "siad_ses_init=";
+
+ if (access(SIAIGOODFILE, F_OK) == -1)
+ {
+ /* Broken OSF/1 system, better don't run on it. */
+ fprintf(stderr, SIAIGOODFILE);
+ fprintf(stderr, " does not exist. Your OSF/1 system is probably broken\n");
+ exit(1);
+ }
+ if ((f = fopen(MATRIX_CONF, "r")) == NULL)
+ {
+ /* Another way OSF/1 is probably broken. */
+ fprintf(stderr, "%s unreadable. Your OSF/1 system is probably broken.\n"
+
+ MATRIX_CONF);
+ exit(1);
+ }
+
+ /* Read matrix.conf to check if we run C2 or not */
+ while (fgets(buf, sizeof(buf), f) != NULL)
+ {
+ if (strncmp(buf, siad, sizeof(siad) - 1) == 0)
+ {
+ if (strstr(buf, "OSFC2") != NULL)
+ c2security = 1;
+ else if (strstr(buf, "BSD") != NULL)
+ c2security = 0;
+ break;
+ }
+ }
+ fclose(f);
+ if (c2security == -1)
+ {
+ fprintf(stderr, "C2 security initialization failed : could not determine security level.\n");
+ exit(1);
+ }
+ if (c2security == 1)
+ set_auth_parameters(ac, av);
+}
+
+
+static int
+osf1c2_getprpwent(char *p, char *n, int len)
+{
+ time_t pschg, tnow;
+
+ if (c2security == 1)
+ {
+ struct es_passwd *es;
+ struct pr_passwd *pr = getprpwnam(n);
+ if (pr)
+ {
+ strlcpy(p, pr->ufld.fd_encrypt, len);
+ crypt_algo = pr->ufld.fd_oldcrypt;
+
+ tnow = time(NULL);
+ if (pr->uflg.fg_schange == 1)
+ pschg = pr->ufld.fd_schange;
+ else
+ pschg = 0;
+ if (pr->uflg.fg_template == 0)
+ {
+ /** default template, system values **/
+ if (pr->sflg.fg_lifetime == 1)
+ if (pr->sfld.fd_lifetime > 0 &&
+ pschg + pr->sfld.fd_lifetime < tnow)
+ return 1;
+ }
+ else /** user template, specific values **/
+ {
+ es = getespwnam(pr->ufld.fd_template);
+ if (es)
+ {
+ if (es->uflg->fg_expire == 1)
+ if (es->ufld->fd_expire > 0 &&
+ pschg + es->ufld->fd_expire < tnow)
+ return 1;
+ }
+ }
+ }
+ }
+ else
+ {
+ struct passwd *pw = getpwnam(n);
+ if (pw)
+ {
+ strlcpy(p, pw->pw_passwd, len);
+ return 1;
+ }
+ }
+ return 0;
+}
+
+static char *
+osf1c2crypt(const char *pw, char *salt)
+{
+ if (c2security == 1) {
+ return(dispcrypt(pw, salt, crypt_algo));
+ } else
+ return(crypt(pw, salt));
+}
+
+#endif
diff --git a/kcheckpass/checkpass_pam.c b/kcheckpass/checkpass_pam.c
new file mode 100644
index 000000000..6f281165f
--- /dev/null
+++ b/kcheckpass/checkpass_pam.c
@@ -0,0 +1,200 @@
+/*
+ * Copyright (C) 1998 Caldera, Inc.
+ * Copyright (C) 2003 Oswald Buddenhagen <ossi@kde.org>
+ *
+ * This program 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 of the License, or (at your option) any later version.
+ *
+ * This program 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.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "kcheckpass.h"
+
+#ifdef HAVE_PAM
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+
+#ifdef HAVE_PAM_PAM_APPL_H
+#include <pam/pam_appl.h>
+#else
+#include <security/pam_appl.h>
+#endif
+
+struct pam_data {
+ char *(*conv) (ConvRequest, const char *);
+ int abort:1;
+ int classic:1;
+};
+
+#ifdef PAM_MESSAGE_NONCONST
+typedef struct pam_message pam_message_type;
+typedef void *pam_gi_type;
+#else
+typedef const struct pam_message pam_message_type;
+typedef const void *pam_gi_type;
+#endif
+
+static int
+PAM_conv (int num_msg, pam_message_type **msg,
+ struct pam_response **resp,
+ void *appdata_ptr)
+{
+ int count;
+ struct pam_response *repl;
+ struct pam_data *pd = (struct pam_data *)appdata_ptr;
+
+ if (!(repl = calloc(num_msg, sizeof(struct pam_response))))
+ return PAM_CONV_ERR;
+
+ for (count = 0; count < num_msg; count++)
+ switch (msg[count]->msg_style) {
+ case PAM_TEXT_INFO:
+ pd->conv(ConvPutInfo, msg[count]->msg);
+ break;
+ case PAM_ERROR_MSG:
+ pd->conv(ConvPutError, msg[count]->msg);
+ break;
+ default:
+ switch (msg[count]->msg_style) {
+ case PAM_PROMPT_ECHO_ON:
+ repl[count].resp = pd->conv(ConvGetNormal, msg[count]->msg);
+ break;
+ case PAM_PROMPT_ECHO_OFF:
+ repl[count].resp =
+ pd->conv(ConvGetHidden, pd->classic ? 0 : msg[count]->msg);
+ break;
+#ifdef PAM_BINARY_PROMPT
+ case PAM_BINARY_PROMPT:
+ repl[count].resp = pd->conv(ConvGetBinary, msg[count]->msg);
+ break;
+#endif
+ default:
+ /* Must be an error of some sort... */
+ goto conv_err;
+ }
+ if (!repl[count].resp) {
+ pd->abort = 1;
+ goto conv_err;
+ }
+ repl[count].resp_retcode = PAM_SUCCESS;
+ break;
+ }
+ *resp = repl;
+ return PAM_SUCCESS;
+
+ conv_err:
+ for (; count >= 0; count--)
+ if (repl[count].resp)
+ switch (msg[count]->msg_style) {
+ case PAM_PROMPT_ECHO_OFF:
+ dispose(repl[count].resp);
+ break;
+#ifdef PAM_BINARY_PROMPT
+ case PAM_BINARY_PROMPT: /* handle differently? */
+#endif
+ case PAM_PROMPT_ECHO_ON:
+ free(repl[count].resp);
+ break;
+ }
+ free(repl);
+ return PAM_CONV_ERR;
+}
+
+static struct pam_data PAM_data;
+
+static struct pam_conv PAM_conversation = {
+ &PAM_conv,
+ &PAM_data
+};
+
+#ifdef PAM_FAIL_DELAY
+static void
+fail_delay(int retval ATTR_UNUSED, unsigned usec_delay ATTR_UNUSED,
+ void *appdata_ptr ATTR_UNUSED)
+{}
+#endif
+
+
+AuthReturn Authenticate(const char *caller, const char *method,
+ const char *user, char *(*conv) (ConvRequest, const char *))
+{
+ const char *tty;
+ pam_handle_t *pamh;
+ pam_gi_type pam_item;
+ const char *pam_service;
+ char pservb[64];
+ int pam_error;
+
+ openlog("kcheckpass", LOG_PID, LOG_AUTH);
+
+ PAM_data.conv = conv;
+ if (strcmp(method, "classic")) {
+ sprintf(pservb, "%.31s-%.31s", caller, method);
+ pam_service = pservb;
+ } else {
+ PAM_data.classic = 1;
+ pam_service = caller;
+ }
+ pam_error = pam_start(pam_service, user, &PAM_conversation, &pamh);
+ if (pam_error != PAM_SUCCESS)
+ return AuthError;
+
+ tty = ttyname(0);
+ if (!tty)
+ tty = getenv ("DISPLAY");
+
+ pam_error = pam_set_item (pamh, PAM_TTY, tty);
+ if (pam_error != PAM_SUCCESS) {
+ pam_end(pamh, pam_error);
+ return AuthError;
+ }
+
+# ifdef PAM_FAIL_DELAY
+ pam_set_item (pamh, PAM_FAIL_DELAY, (void *)fail_delay);
+# endif
+
+ pam_error = pam_authenticate(pamh, 0);
+ if (pam_error != PAM_SUCCESS) {
+ pam_end(pamh, pam_error);
+ switch (pam_error) {
+ case PAM_USER_UNKNOWN:
+ case PAM_AUTH_ERR:
+ case PAM_MAXTRIES: /* should handle this better ... */
+ case PAM_AUTHINFO_UNAVAIL: /* returned for unknown users ... bogus */
+ return AuthBad;
+ default:
+ return AuthError;
+ }
+ }
+
+ /* just in case some module is stupid enough to ignore a preset PAM_USER */
+ pam_error = pam_get_item (pamh, PAM_USER, &pam_item);
+ if (pam_error != PAM_SUCCESS) {
+ pam_end(pamh, pam_error);
+ return AuthError;
+ }
+ if (strcmp((const char *)pam_item, user)) {
+ pam_end(pamh, PAM_SUCCESS); /* maybe use PAM_AUTH_ERR? */
+ return AuthBad;
+ }
+
+ pam_error = pam_setcred(pamh, PAM_REFRESH_CRED);
+ /* ignore errors on refresh credentials. If this did not work we use the old once. */
+
+ pam_end(pamh, PAM_SUCCESS);
+ return AuthOk;
+}
+
+#endif
diff --git a/kcheckpass/checkpass_shadow.c b/kcheckpass/checkpass_shadow.c
new file mode 100644
index 000000000..ec3a4e02a
--- /dev/null
+++ b/kcheckpass/checkpass_shadow.c
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 1998 Christian Esken <esken@kde.org>
+ * Copyright (C) 2003 Oswald Buddenhagen <ossi@kde.org>
+ *
+ * This is a modified version of checkpass_shadow.cpp
+ *
+ * Modifications made by Thorsten Kukuk <kukuk@suse.de>
+ * Mathias Kettner <kettner@suse.de>
+ *
+ * ------------------------------------------------------------
+ *
+ * This program 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 of the License, or (at your option) any later version.
+ *
+ * This program 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.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "kcheckpass.h"
+
+/*******************************************************************
+ * This is the authentication code for Shadow-Passwords
+ *******************************************************************/
+
+#ifdef HAVE_SHADOW
+#include <string.h>
+#include <stdlib.h>
+#include <pwd.h>
+
+#ifndef __hpux
+#include <shadow.h>
+#endif
+
+AuthReturn Authenticate(const char *method,
+ const char *login, char *(*conv) (ConvRequest, const char *))
+{
+ char *typed_in_password;
+ char *crpt_passwd;
+ char *password;
+ struct passwd *pw;
+ struct spwd *spw;
+
+ if (strcmp(method, "classic"))
+ return AuthError;
+
+ if (!(pw = getpwnam(login)))
+ return AuthAbort;
+
+ spw = getspnam(login);
+ password = spw ? spw->sp_pwdp : pw->pw_passwd;
+
+ if (!*password)
+ return AuthOk;
+
+ if (!(typed_in_password = conv(ConvGetHidden, 0)))
+ return AuthAbort;
+
+#if defined( __linux__ ) && defined( HAVE_PW_ENCRYPT )
+ crpt_passwd = pw_encrypt(typed_in_password, password); /* (1) */
+#else
+ crpt_passwd = crypt(typed_in_password, password);
+#endif
+
+ if (!strcmp(password, crpt_passwd )) {
+ dispose(typed_in_password);
+ return AuthOk; /* Success */
+ }
+ dispose(typed_in_password);
+ return AuthBad; /* Password wrong or account locked */
+}
+
+/*
+ (1) Deprecated - long passwords have known weaknesses. Also,
+ pw_encrypt is non-standard (requires libshadow.a) while
+ everything else you need to support shadow passwords is in
+ the standard (ELF) libc.
+ */
+#endif
diff --git a/kcheckpass/kcheckpass.c b/kcheckpass/kcheckpass.c
new file mode 100644
index 000000000..6a0550969
--- /dev/null
+++ b/kcheckpass/kcheckpass.c
@@ -0,0 +1,448 @@
+/*****************************************************************
+ *
+ * kcheckpass - Simple password checker
+ *
+ * This program 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 of the License, or (at your option) any later version.
+ *
+ * This program 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.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ *
+ * kcheckpass is a simple password checker. Just invoke and
+ * send it the password on stdin.
+ *
+ * If the password was accepted, the program exits with 0;
+ * if it was rejected, it exits with 1. Any other exit
+ * code signals an error.
+ *
+ * It's hopefully simple enough to allow it to be setuid
+ * root.
+ *
+ * Compile with -DHAVE_VSYSLOG if you have vsyslog().
+ * Compile with -DHAVE_PAM if you have a PAM system,
+ * and link with -lpam -ldl.
+ * Compile with -DHAVE_SHADOW if you have a shadow
+ * password system.
+ *
+ * Copyright (C) 1998, Caldera, Inc.
+ * Released under the GNU General Public License
+ *
+ * Olaf Kirch <okir@caldera.de> General Framework and PAM support
+ * Christian Esken <esken@kde.org> Shadow and /etc/passwd support
+ * Roberto Teixeira <maragato@kde.org> other user (-U) support
+ * Oswald Buddenhagen <ossi@kde.org> Binary server mode
+ *
+ * Other parts were taken from kscreensaver's passwd.cpp.
+ *
+ *****************************************************************/
+
+#include "kcheckpass.h"
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <syslog.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <time.h>
+
+/* Compatibility: accept some options from environment variables */
+#define ACCEPT_ENV
+
+#define THROTTLE 3
+
+static int havetty, sfd = -1, nullpass;
+
+static char *
+conv_legacy (ConvRequest what, const char *prompt)
+{
+ char *p, *p2;
+ int len;
+ char buf[1024];
+
+ switch (what) {
+ case ConvGetBinary:
+ break;
+ case ConvGetNormal:
+ /* there is no prompt == 0 case */
+ if (!havetty)
+ break;
+ /* i guess we should use /dev/tty ... */
+ fputs(prompt, stdout);
+ fflush(stdout);
+ if (!fgets(buf, sizeof(buf), stdin))
+ return 0;
+ len = strlen(buf);
+ if (len && buf[len - 1] == '\n')
+ buf[--len] = 0;
+ return strdup(buf);
+ case ConvGetHidden:
+ if (havetty) {
+#ifdef HAVE_GETPASSPHRASE
+ p = getpassphrase(prompt ? prompt : "Password: ");
+#else
+ p = getpass(prompt ? prompt : "Password: ");
+#endif
+ p2 = strdup(p);
+ memset(p, 0, strlen(p));
+ return p2;
+ } else {
+ if (prompt)
+ break;
+ if ((len = read(0, buf, sizeof(buf) - 1)) < 0) {
+ message("Cannot read password\n");
+ return 0;
+ } else {
+ if (len && buf[len - 1] == '\n')
+ --len;
+ buf[len] = 0;
+ p2 = strdup(buf);
+ memset(buf, 0, len);
+ return p2;
+ }
+ }
+ case ConvPutInfo:
+ message("Information: %s\n", prompt);
+ return 0;
+ case ConvPutError:
+ message("Error: %s\n", prompt);
+ return 0;
+ }
+ message("Authentication backend requested data type which cannot be handled.\n");
+ return 0;
+}
+
+
+static int
+Reader (void *buf, int count)
+{
+ int ret, rlen;
+
+ for (rlen = 0; rlen < count; ) {
+ dord:
+ ret = read (sfd, (void *)((char *)buf + rlen), count - rlen);
+ if (ret < 0) {
+ if (errno == EINTR)
+ goto dord;
+ if (errno == EAGAIN)
+ break;
+ return -1;
+ }
+ if (!ret)
+ break;
+ rlen += ret;
+ }
+ return rlen;
+}
+
+static void
+GRead (void *buf, int count)
+{
+ if (Reader (buf, count) != count) {
+ message ("Communication breakdown on read\n");
+ exit(15);
+ }
+}
+
+static void
+GWrite (const void *buf, int count)
+{
+ if (write (sfd, buf, count) != count) {
+ message ("Communication breakdown on write\n");
+ exit(15);
+ }
+}
+
+static void
+GSendInt (int val)
+{
+ GWrite (&val, sizeof(val));
+}
+
+static void
+GSendStr (const char *buf)
+{
+ unsigned len = buf ? strlen (buf) + 1 : 0;
+ GWrite (&len, sizeof(len));
+ GWrite (buf, len);
+}
+
+static void
+GSendArr (int len, const char *buf)
+{
+ GWrite (&len, sizeof(len));
+ GWrite (buf, len);
+}
+
+static int
+GRecvInt (void)
+{
+ int val;
+
+ GRead (&val, sizeof(val));
+ return val;
+}
+
+static char *
+GRecvStr (void)
+{
+ unsigned len;
+ char *buf;
+
+ if (!(len = GRecvInt()))
+ return (char *)0;
+ if (len > 0x1000 || !(buf = malloc (len))) {
+ message ("No memory for read buffer\n");
+ exit(15);
+ }
+ GRead (buf, len);
+ buf[len - 1] = 0; /* we're setuid ... don't trust "them" */
+ return buf;
+}
+
+static char *
+GRecvArr (void)
+{
+ unsigned len;
+ char *arr;
+
+ if (!(len = (unsigned) GRecvInt()))
+ return (char *)0;
+ if (len > 0x10000 || !(arr = malloc (len))) {
+ message ("No memory for read buffer\n");
+ exit(15);
+ }
+ GRead (arr, len);
+ return arr;
+}
+
+
+static char *
+conv_server (ConvRequest what, const char *prompt)
+{
+ GSendInt (what);
+ switch (what) {
+ case ConvGetBinary:
+ {
+ unsigned const char *up = (unsigned const char *)prompt;
+ int len = up[3] | (up[2] << 8) | (up[1] << 16) | (up[0] << 24);
+ GSendArr (len, prompt);
+ return GRecvArr ();
+ }
+ case ConvGetNormal:
+ case ConvGetHidden:
+ {
+ char *msg;
+ GSendStr (prompt);
+ msg = GRecvStr ();
+ if (msg && (GRecvInt() & IsPassword) && !*msg)
+ nullpass = 1;
+ return msg;
+ }
+ case ConvPutInfo:
+ case ConvPutError:
+ default:
+ GSendStr (prompt);
+ return 0;
+ }
+}
+
+void
+message(const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ vfprintf(stderr, fmt, ap);
+ va_end(ap);
+}
+
+#ifndef O_NOFOLLOW
+# define O_NOFOLLOW 0
+#endif
+
+static void ATTR_NORETURN
+usage(int exitval)
+{
+ message(
+ "usage: kcheckpass {-h|[-c caller] [-m method] [-U username|-S handle]}\n"
+ " options:\n"
+ " -h this help message\n"
+ " -U username authenticate the specified user instead of current user\n"
+ " -S handle operate in binary server mode on file descriptor handle\n"
+ " -c caller the calling application, effectively the PAM service basename\n"
+ " -m method use the specified authentication method (default: \"classic\")\n"
+ " exit codes:\n"
+ " 0 success\n"
+ " 1 invalid password\n"
+ " 2 cannot read password database\n"
+ " Anything else tells you something's badly hosed.\n"
+ );
+ exit(exitval);
+}
+
+int
+main(int argc, char **argv)
+{
+#ifdef HAVE_PAM
+ const char *caller = KCHECKPASS_PAM_SERVICE;
+#endif
+ const char *method = "classic";
+ const char *username = 0;
+#ifdef ACCEPT_ENV
+ char *p;
+#endif
+ struct passwd *pw;
+ int c, nfd, lfd;
+ uid_t uid;
+ time_t nexttime;
+ AuthReturn ret;
+ struct flock lk;
+ char fname[64], fcont[64];
+
+#ifdef HAVE_OSF_C2_PASSWD
+ initialize_osf_security(argc, argv);
+#endif
+
+ /* Make sure stdout/stderr are open */
+ for (c = 1; c <= 2; c++) {
+ if (fcntl(c, F_GETFL) == -1) {
+ if ((nfd = open("/dev/null", O_WRONLY)) < 0) {
+ message("cannot open /dev/null: %s\n", strerror(errno));
+ exit(10);
+ }
+ if (c != nfd) {
+ dup2(nfd, c);
+ close(nfd);
+ }
+ }
+ }
+
+ havetty = isatty(0);
+
+ while ((c = getopt(argc, argv, "hc:m:U:S:")) != -1) {
+ switch (c) {
+ case 'h':
+ usage(0);
+ break;
+ case 'c':
+#ifdef HAVE_PAM
+ caller = optarg;
+#endif
+ break;
+ case 'm':
+ method = optarg;
+ break;
+ case 'U':
+ username = optarg;
+ break;
+ case 'S':
+ sfd = atoi(optarg);
+ break;
+ default:
+ message("Command line option parsing error\n");
+ usage(10);
+ }
+ }
+
+#ifdef ACCEPT_ENV
+# ifdef HAVE_PAM
+ if ((p = getenv("KDE_PAM_ACTION")))
+ caller = p;
+# endif
+ if ((p = getenv("KCHECKPASS_USER")))
+ username = p;
+#endif
+
+ uid = getuid();
+ if (!username) {
+ if (!(p = getenv("LOGNAME")) || !(pw = getpwnam(p)) || pw->pw_uid != uid)
+ if (!(p = getenv("USER")) || !(pw = getpwnam(p)) || pw->pw_uid != uid)
+ if (!(pw = getpwuid(uid))) {
+ message("Cannot determinate current user\n");
+ return AuthError;
+ }
+ if (!(username = strdup(pw->pw_name))) {
+ message("Out of memory\n");
+ return AuthError;
+ }
+ }
+
+ /*
+ * Throttle kcheckpass invocations to avoid abusing it for bruteforcing
+ * the password. This delay belongs to the *previous* invocation, where
+ * we can't enforce it reliably (without risking giving away the result
+ * before it is due). We don't differentiate between success and failure -
+ * it's not expected to have a noticable adverse effect.
+ */
+ if ( uid != geteuid() ) {
+ sprintf(fname, "/var/run/kcheckpass.%d", uid);
+ if ((lfd = open(fname, O_RDWR | O_CREAT | O_NOFOLLOW, 0600)) < 0) {
+ message("Cannot open lockfile\n");
+ return AuthError;
+ }
+
+ lk.l_type = F_WRLCK;
+ lk.l_whence = SEEK_SET;
+ lk.l_start = lk.l_len = 0;
+ if (fcntl(lfd, F_SETLKW, &lk)) {
+ message("Cannot obtain lock\n");
+ return AuthError;
+ }
+
+ if ((c = read(lfd, fcont, sizeof(fcont)-1)) > 0 &&
+ (fcont[c] = '\0', sscanf(fcont, "%ld", &nexttime) == 1))
+ {
+ time_t ct = time(0);
+ if (nexttime > ct && nexttime < ct + THROTTLE)
+ sleep(nexttime - ct);
+ }
+
+ lseek(lfd, 0, SEEK_SET);
+ write(lfd, fcont, sprintf(fcont, "%lu\n", time(0) + THROTTLE));
+
+ close(lfd);
+ }
+
+ /* Now do the fandango */
+ ret = Authenticate(
+#ifdef HAVE_PAM
+ caller,
+#endif
+ method,
+ username,
+ sfd < 0 ? conv_legacy : conv_server);
+
+ if (ret == AuthBad) {
+ message("Authentication failure\n");
+ if (!nullpass) {
+ openlog("kcheckpass", LOG_PID, LOG_AUTH);
+ syslog(LOG_NOTICE, "Authentication failure for %s (invoked by uid %d)", username, uid);
+ }
+ }
+
+ return ret;
+}
+
+void
+dispose(char *str)
+{
+ memset(str, 0, strlen(str));
+ free(str);
+}
+
+/*****************************************************************
+ The real authentication methods are in separate source files.
+ Look in checkpass_*.c
+*****************************************************************/
diff --git a/kcheckpass/kcheckpass.h b/kcheckpass/kcheckpass.h
new file mode 100644
index 000000000..5aaebf2da
--- /dev/null
+++ b/kcheckpass/kcheckpass.h
@@ -0,0 +1,142 @@
+/*****************************************************************
+ *
+ * kcheckpass
+ *
+ * Simple password checker. Just invoke and send it
+ * the password on stdin.
+ *
+ * If the password was accepted, the program exits with 0;
+ * if it was rejected, it exits with 1. Any other exit
+ * code signals an error.
+ *
+ *
+ * This program 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 of the License, or (at your option) any later version.
+ *
+ * This program 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.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Copyright (C) 1998, Caldera, Inc.
+ * Released under the GNU General Public License
+ *
+ * Olaf Kirch <okir@caldera.de> General Framework and PAM support
+ * Christian Esken <esken@kde.org> Shadow and /etc/passwd support
+ * Oswald Buddenhagen <ossi@kde.org> Binary server mode
+ *
+ * Other parts were taken from kscreensaver's passwd.cpp
+ *****************************************************************/
+
+#ifndef KCHECKPASS_H_
+#define KCHECKPASS_H_
+
+#include <config.h>
+
+#ifdef HAVE_CRYPT_H
+#include <crypt.h>
+#endif
+
+#ifdef HAVE_PATHS_H
+#include <paths.h>
+#endif
+
+#include <pwd.h>
+#include <sys/types.h>
+
+#ifndef _PATH_TMP
+#define _PATH_TMP "/tmp/"
+#endif
+
+
+#ifdef ultrix
+#include <auth.h>
+#endif
+
+#include <unistd.h>
+
+#ifdef OSF1_ENH_SEC
+#include <sys/security.h>
+#include <prot.h>
+#endif
+
+/* Make sure there is only one! */
+#if defined(HAVE_PAM)
+# undef HAVE_OSF_C2_PASSWD
+# undef HAVE_SHADOW
+#elif defined(HAVE_OSF_C2_PASSWD)
+# undef HAVE_SHADOW
+#elif defined(_AIX)
+# define HAVE_AIX_AUTH
+# undef HAVE_SHADOW
+#elif !defined(HAVE_SHADOW)
+# define HAVE_ETCPASSWD
+#endif
+
+#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ > 4)
+# define ATTR_UNUSED __attribute__((unused))
+# define ATTR_NORETURN __attribute__((noreturn))
+# define ATTR_PRINTFLIKE(fmt,var) __attribute__((format(printf,fmt,var)))
+#else
+# define ATTR_UNUSED
+# define ATTR_NORETURN
+# define ATTR_PRINTFLIKE(fmt,var)
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* these must match kcheckpass' exit codes */
+typedef enum {
+ AuthOk = 0,
+ AuthBad = 1,
+ AuthError = 2,
+ AuthAbort = 3
+} AuthReturn;
+
+typedef enum {
+ ConvGetBinary,
+ ConvGetNormal,
+ ConvGetHidden,
+ ConvPutInfo,
+ ConvPutError
+} ConvRequest;
+
+/* these must match the defs in kgreeterplugin.h */
+typedef enum {
+ IsUser = 1, /* unused in kcheckpass */
+ IsPassword = 2
+} DataTag;
+
+/*****************************************************************
+ * Authenticates user
+ *****************************************************************/
+AuthReturn Authenticate(
+#ifdef HAVE_PAM
+ const char *caller,
+#endif
+ const char *method,
+ const char *user,
+ char *(*conv) (ConvRequest, const char *));
+
+/*****************************************************************
+ * Output a message to stderr
+ *****************************************************************/
+void message(const char *, ...) ATTR_PRINTFLIKE(1, 2);
+
+/*****************************************************************
+ * Overwrite and free the passed string
+ *****************************************************************/
+void dispose(char *);
+
+#ifdef __cplusplus
+}
+#endif
+#endif