diff --git a/CMakeLists.txt b/CMakeLists.txt index 242f546..f7eef9f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,205 +1,212 @@ set(PROJECT_VERSION "5.6.90") set(PROJECT_VERSION_MAJOR 5) cmake_minimum_required(VERSION 2.8.12 FATAL_ERROR) set(QT_MIN_VERSION "5.4.0") set(KF5_MIN_VERSION "5.15.0") find_package(Qt5 ${QT_MIN_VERSION} CONFIG REQUIRED COMPONENTS Widgets Quick QuickWidgets Test) find_package(ECM 1.8.0 REQUIRED NO_MODULE) set(CMAKE_MODULE_PATH ${ECM_MODULE_PATH} ${ECM_KDE_MODULE_DIR}) include(KDEInstallDirs) include(KDECMakeSettings) include(KDECompilerSettings NO_POLICY_SCOPE) include(ECMSetupVersion) include(ECMGenerateHeaders) include(CheckIncludeFiles) include(ECMMarkNonGuiExecutable) include(ECMPackageConfigHelpers) include(FeatureSummary) include(GenerateExportHeader) include(CheckIncludeFile) include(CheckSymbolExists) check_include_file("sys/prctl.h" HAVE_SYS_PRCTL_H) check_symbol_exists(PR_SET_DUMPABLE "sys/prctl.h" HAVE_PR_SET_DUMPABLE) -add_feature_info("prctl-dumpable" HAVE_PR_SET_DUMPABLE "Required for disallow ptrace on greeter and kcheckpass process") +check_include_file("sys/procctl.h" HAVE_SYS_PROCCTL_H) +check_symbol_exists(PROC_TRACE_CTL "sys/procctl.h" HAVE_PROC_TRACE_CTL) +if (HAVE_PR_SET_DUMPABLE OR HAVE_PROC_TRACE_CTL) + set(CAN_DISABLE_PTRACE TRUE) +endif () +add_feature_info("prctl/procctl tracing control" + CAN_DISABLE_PTRACE + "Required for disallowing ptrace on greeter and kcheckpass process") find_package(KF5 ${KF5_MIN_VERSION} REQUIRED COMPONENTS Crash Declarative GlobalAccel I18n IdleTime KCMUtils Notifications Solid WindowSystem XmlGui ) find_package(X11) set_package_properties(X11 PROPERTIES DESCRIPTION "X11 libraries" URL "http://www.x.org" TYPE REQUIRED PURPOSE "Required for building the X11 based workspace") find_package(XCB MODULE REQUIRED COMPONENTS XCB KEYSYMS) set_package_properties(XCB PROPERTIES TYPE REQUIRED) add_feature_info("XInput" X11_Xinput_FOUND "Required for grabbing XInput2 devices in the screen locker") find_package(Qt5 ${QT_MIN_VERSION} CONFIG REQUIRED COMPONENTS X11Extras) find_package(KF5Wayland CONFIG REQUIRED) set_package_properties(KF5Wayland PROPERTIES TYPE REQUIRED PURPOSE "Required for building screenlocker") find_package(WaylandScanner) find_package(Wayland 1.3 COMPONENTS Client Server) set_package_properties(Wayland PROPERTIES TYPE REQUIRED PURPOSE "Required for building screenlocker") include(ConfigureChecks.cmake) configure_file(config-workspace.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-workspace.h) configure_file(config-unix.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-unix.h ) configure_file(config-X11.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-X11.h) # adjusting CMAKE_C_FLAGS to get wayland protocols to compile set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=gnu90") ecm_setup_version(${PROJECT_VERSION} VARIABLE_PREFIX KSCREENLOCKER VERSION_HEADER "${CMAKE_CURRENT_BINARY_DIR}/kscreenlocker_version.h" PACKAGE_VERSION_FILE "${CMAKE_CURRENT_BINARY_DIR}/KScreenLockerConfigVersion.cmake" SOVERSION 5) configure_file(config-kscreenlocker.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-kscreenlocker.h) include_directories(${CMAKE_CURRENT_BINARY_DIR}) set(KSLD_INCLUDEDIR "${CMAKE_INSTALL_INCLUDEDIR}/KScreenLocker") add_subdirectory(kcheckpass) add_subdirectory(greeter) add_subdirectory(kcm) add_definitions(-DTRANSLATION_DOMAIN=\"kscreenlocker\") set(screensaver_dbusXML dbus/org.freedesktop.ScreenSaver.xml) set(kscreensaver_dbusXML dbus/org.kde.screensaver.xml) set(powerdevilpolicyagent_xml dbus/kf5_org.kde.Solid.PowerManagement.PolicyAgent.xml) set(ksld_SRCS abstractlocker.cpp ksldapp.cpp interface.cpp globalaccel.cpp x11locker.cpp waylandlocker.cpp logind.cpp waylandserver.cpp powermanagement.cpp powermanagement_inhibition.cpp ) qt5_add_dbus_adaptor(ksld_SRCS ${screensaver_dbusXML} interface.h ScreenLocker::Interface) qt5_add_dbus_adaptor(ksld_SRCS ${kscreensaver_dbusXML} interface.h ScreenLocker::Interface kscreensaveradaptor KScreenSaverAdaptor) kconfig_add_kcfg_files(ksld_SRCS kcfg/kscreensaversettings.kcfgc) qt5_add_dbus_interface(ksld_SRCS ${powerdevilpolicyagent_xml} powerdevilpolicyagent) ecm_add_wayland_server_protocol(ksld_SRCS PROTOCOL protocols/ksld.xml BASENAME ksld ) add_library(KScreenLocker SHARED ${ksld_SRCS}) target_link_libraries(KScreenLocker PUBLIC Qt5::Core Qt5::X11Extras PRIVATE KF5::I18n KF5::IdleTime KF5::GlobalAccel KF5::Notifications KF5::CoreAddons KF5::ConfigGui KF5::WindowSystem ${X11_LIBRARIES} ${X11_Xcursor_LIB} XCB::XCB XCB::KEYSYMS KF5::WaylandServer Wayland::Server ) if (X11_Xinput_FOUND) target_link_libraries(KScreenLocker PRIVATE ${X11_Xinput_LIB}) endif() target_include_directories(KScreenLocker INTERFACE "$") # Needed to compile on Arm target. set_target_properties(KScreenLocker PROPERTIES COMPILE_FLAGS "-fPIC") add_library(PW::KScreenLocker ALIAS KScreenLocker) generate_export_header(KScreenLocker BASE_NAME KScreenLocker EXPORT_MACRO_NAME KSCREENLOCKER_EXPORT EXPORT_FILE_NAME KScreenLocker/kscreenlocker_export.h ) ecm_configure_package_config_file(ScreenSaverDBusInterfaceConfig.cmake.in ${CMAKE_CURRENT_BINARY_DIR}/ScreenSaverDBusInterfaceConfig.cmake PATH_VARS KDE_INSTALL_DBUSINTERFACEDIR INSTALL_DESTINATION "${CMAKECONFIG_INSTALL_PREFIX}/ScreenSaverDBusInterface") set_target_properties(KScreenLocker PROPERTIES VERSION ${PROJECT_VERSION} SOVERSION ${PROJECT_VERSION_MAJOR}) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/ScreenSaverDBusInterfaceConfig.cmake DESTINATION "${CMAKECONFIG_INSTALL_PREFIX}/ScreenSaverDBusInterface") ecm_configure_package_config_file("${CMAKE_CURRENT_SOURCE_DIR}/KScreenLockerConfig.cmake.in" "${CMAKE_CURRENT_BINARY_DIR}/KScreenLockerConfig.cmake" INSTALL_DESTINATION "${CMAKECONFIG_INSTALL_PREFIX}/KScreenLocker") install(TARGETS KScreenLocker EXPORT KScreenLockerTargets ${INSTALL_TARGETS_DEFAULT_ARGS} LIBRARY) install(EXPORT KScreenLockerTargets DESTINATION "${CMAKECONFIG_INSTALL_PREFIX}/KScreenLocker" FILE KScreenLockerTargets.cmake NAMESPACE PW::) ecm_generate_headers(KScreenLocker_CamelCase_HEADERS HEADER_NAMES KsldApp REQUIRED_HEADERS KScreenLocker_HEADERS) install( FILES ${CMAKE_CURRENT_BINARY_DIR}/KScreenLocker/kscreenlocker_export.h ${KScreenLocker_CamelCase_HEADERS} ${KScreenLocker_HEADERS} DESTINATION ${KSLD_INCLUDEDIR}/KScreenLocker COMPONENT Devel) install(FILES "${CMAKE_CURRENT_BINARY_DIR}/KScreenLockerConfig.cmake" "${CMAKE_CURRENT_BINARY_DIR}/KScreenLockerConfigVersion.cmake" DESTINATION "${CMAKECONFIG_INSTALL_PREFIX}/KScreenLocker" COMPONENT Devel) install(FILES kscreenlocker.notifyrc DESTINATION ${KDE_INSTALL_KNOTIFY5RCDIR} RENAME ksmserver.notifyrc) install(FILES ${screensaver_dbusXML} DESTINATION ${KDE_INSTALL_DBUSINTERFACEDIR} RENAME kf5_org.freedesktop.ScreenSaver.xml) install(FILES updaters/kscreenlocker.upd DESTINATION ${KDE_INSTALL_DATADIR}/kconf_update) install(PROGRAMS updaters/ksreenlocker_5_3_separate_autologin.pl DESTINATION ${KDE_INSTALL_DATADIR}/kconf_update) add_subdirectory(autotests) add_subdirectory(tests) feature_summary(WHAT ALL INCLUDE_QUIET_PACKAGES FATAL_ON_MISSING_REQUIRED_PACKAGES) diff --git a/config-kscreenlocker.h.cmake b/config-kscreenlocker.h.cmake index 02a8c39..436551e 100644 --- a/config-kscreenlocker.h.cmake +++ b/config-kscreenlocker.h.cmake @@ -1,10 +1,12 @@ #ifndef KSCREENLOCKER_UNIT_TEST #define KCHECKPASS_BIN "${CMAKE_INSTALL_FULL_LIBEXECDIR}/kcheckpass" #else #define KCHECKPASS_BIN "${CMAKE_CURRENT_BINARY_DIR}/greeter/autotests/fakekcheckpass" #endif #define KSCREENLOCKER_GREET_BIN "${CMAKE_INSTALL_FULL_LIBEXECDIR}/kscreenlocker_greet" #cmakedefine01 HAVE_SYS_PRCTL_H #cmakedefine01 HAVE_PR_SET_DUMPABLE +#cmakedefine01 HAVE_SYS_PROCCTL_H +#cmakedefine01 HAVE_PROC_TRACE_CTL diff --git a/greeter/main.cpp b/greeter/main.cpp index 803ae57..28e2f72 100644 --- a/greeter/main.cpp +++ b/greeter/main.cpp @@ -1,162 +1,174 @@ /******************************************************************** This file is part of the KDE project. Copyright (C) 2011 Martin Gräßlin 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, see . *********************************************************************/ #include #include #include #include #include #include #include "greeterapp.h" #include #if HAVE_SYS_PRCTL_H #include #endif +#if HAVE_SYS_PROCCTL_H +#include +#include +#endif static void signalHandler(int signum) { ScreenLocker::UnlockApp *instance = qobject_cast(QCoreApplication::instance()); if (!instance) return; switch(signum) { case SIGTERM: // exit gracefully to not leave behind screensaver processes (bug#224200) // return exit code 1 to indicate that a valid password was not entered, // to prevent circumventing the password input by sending a SIGTERM instance->exit(1); break; case SIGUSR1: instance->lockImmediately(); break; } } int main(int argc, char* argv[]) { // disable ptrace on the greeter #if HAVE_PR_SET_DUMPABLE prctl(PR_SET_DUMPABLE, 0); #endif +#if HAVE_PROC_TRACE_CTL + int mode = PROC_TRACE_CTL_DISABLE; + procctl(P_PID, getpid(), PROC_TRACE_CTL, &mode); +#endif KLocalizedString::setApplicationDomain("kscreenlocker_greet"); // explicitly disable input methods as it makes it impossible to unlock, see BUG 306932 // but explicitly set on screen keyboard such as maliit is allowed if (qgetenv("QT_IM_MODULE") != QByteArrayLiteral("maliit")) { qputenv("QT_IM_MODULE", QByteArrayLiteral("compose")); } ScreenLocker::UnlockApp app(argc, argv); app.setQuitOnLastWindowClosed(false); QCoreApplication::setApplicationName(QStringLiteral("kscreenlocker_greet")); QCoreApplication::setApplicationVersion(QStringLiteral("0.1")); QCoreApplication::setOrganizationDomain(QStringLiteral("kde.org")); // disable session management for the greeter auto disableSessionManagement = [](QSessionManager &sm) { sm.setRestartHint(QSessionManager::RestartNever); }; QObject::connect(&app, &QGuiApplication::commitDataRequest, disableSessionManagement); QObject::connect(&app, &QGuiApplication::saveStateRequest, disableSessionManagement); QCommandLineParser parser; parser.setApplicationDescription(i18n("Greeter for the KDE Plasma Workspaces Screen locker")); parser.addHelpOption(); parser.addVersionOption(); QCommandLineOption testingOption(QStringLiteral("testing"), i18n("Starts the greeter in testing mode")); QCommandLineOption themeOption(QStringLiteral("theme"), i18n("Starts the greeter with the selected theme (only in Testing mode)"), QStringLiteral("theme"), QStringLiteral("")); QCommandLineOption immediateLockOption(QStringLiteral("immediateLock"), i18n("Lock immediately, ignoring any grace time etc.")); QCommandLineOption graceTimeOption(QStringLiteral("graceTime"), i18n("Delay till the lock user interface gets shown in milliseconds."), QStringLiteral("milliseconds"), QStringLiteral("0")); QCommandLineOption nolockOption(QStringLiteral("nolock"), i18n("Don't show any lock user interface.")); QCommandLineOption waylandFdOption(QStringLiteral("ksldfd"), i18n("File descriptor for connecting to ksld."), QStringLiteral("fd")); parser.addOption(testingOption); parser.addOption(themeOption); parser.addOption(immediateLockOption); parser.addOption(graceTimeOption); parser.addOption(nolockOption); parser.addOption(waylandFdOption); parser.process(app); if (parser.isSet(testingOption)) { app.setTesting(true); app.setImmediateLock(true); //parse theme option const QString theme = parser.value(themeOption); if (!theme.isEmpty()) { app.setTheme(theme); } // allow ptrace if testing is enabled #if HAVE_PR_SET_DUMPABLE prctl(PR_SET_DUMPABLE, 1); +#endif +#if HAVE_PROC_TRACE_CTL + int mode = PROC_TRACE_CTL_ENABLE; + procctl(P_PID, getpid(), PROC_TRACE_CTL, &mode); #endif } else { app.setImmediateLock(parser.isSet(immediateLockOption)); } app.setNoLock(parser.isSet(nolockOption)); bool ok = false; int graceTime = parser.value(graceTimeOption).toInt(&ok); if (ok) { app.setGraceTime(graceTime); } if (parser.isSet(waylandFdOption)) { ok = false; const int fd = parser.value(waylandFdOption).toInt(&ok); if (ok) { app.setKsldSocket(fd); } } app.desktopResized(); // This allow ksmserver to know when the applicaion has actually finished setting itself up. // Crucial for blocking until it is ready, ensuring locking happens before sleep, e.g. std::cout << "Locked at " << QDateTime::currentDateTime().toTime_t() << std::endl; struct sigaction sa; sa.sa_handler = signalHandler; sigemptyset(&sa.sa_mask); sa.sa_flags = SA_RESTART; sigaction(SIGTERM, &sa, nullptr); sigaction(SIGUSR1, &sa, nullptr); return app.exec(); } diff --git a/kcheckpass/kcheckpass.c b/kcheckpass/kcheckpass.c index ea319e6..7e89dae 100644 --- a/kcheckpass/kcheckpass.c +++ b/kcheckpass/kcheckpass.c @@ -1,468 +1,477 @@ /***************************************************************** * * 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 General Framework and PAM support * Christian Esken Shadow and /etc/passwd support * Roberto Teixeira other user (-U) support * Oswald Buddenhagen Binary server mode * * Other parts were taken from kscreensaver's passwd.cpp. * *****************************************************************/ #include "kcheckpass.h" #include #include #include #include #include #include #include #include #include #include #if HAVE_SYS_PRCTL_H #include #endif +#if HAVE_SYS_PROCCTL_H +#include +#include +#endif + /* 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; unsigned const char *up; if (!(len = (unsigned) GRecvInt())) return (char *)0; if (len < 4) { message ("Too short binary authentication data block\n"); exit(15); } if (len > 0x10000 || !(arr = malloc (len))) { message ("No memory for read buffer\n"); exit(15); } GRead (arr, len); up = (unsigned const char *)arr; if (len != (unsigned)(up[3] | (up[2] << 8) | (up[1] << 16) | (up[0] << 24))) { message ("Mismatched binary authentication data block size\n"); exit(15); } 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 = KSCREENSAVER_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]; // disable ptrace on kcheckpass #if HAVE_PR_SET_DUMPABLE prctl(PR_SET_DUMPABLE, 0); #endif +#if HAVE_PROC_TRACE_CTL + int mode = PROC_TRACE_CTL_DISABLE; + procctl(P_PID, getpid(), PROC_TRACE_CTL, &mode); +#endif #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 noticeable 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 *****************************************************************/