Update
parent
3225234e61
commit
0e36014d15
@ -0,0 +1,66 @@
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
|
||||
project(screenlocker)
|
||||
set(CMAKE_C_STANDARD 99)
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
|
||||
set(CMAKE_INCLUDE_CURRENT_DIR ON)
|
||||
|
||||
# set(CMAKE_AUTOUIC ON)
|
||||
# set(CMAKE_AUTOMOC ON)
|
||||
# set(CMAKE_AUTORCC ON)
|
||||
|
||||
set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake)
|
||||
|
||||
include(CheckIncludeFile)
|
||||
include(CheckIncludeFiles)
|
||||
include(CheckSymbolExists)
|
||||
include(FeatureSummary)
|
||||
|
||||
find_package(Qt5 COMPONENTS Core DBus Widgets X11Extras Quick REQUIRED)
|
||||
find_package(X11)
|
||||
|
||||
# ----------- Check --------------------
|
||||
|
||||
check_include_file("sys/prctl.h" HAVE_SYS_PRCTL_H)
|
||||
check_symbol_exists(PR_SET_DUMPABLE "sys/prctl.h" HAVE_PR_SET_DUMPABLE)
|
||||
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")
|
||||
|
||||
check_include_file("sys/signalfd.h" HAVE_SIGNALFD_H)
|
||||
if (NOT HAVE_SIGNALFD_H)
|
||||
check_include_files("sys/types.h;sys/event.h" HAVE_EVENT_H)
|
||||
endif ()
|
||||
add_feature_info("sys/signalfd.h"
|
||||
HAVE_SIGNALFD_H
|
||||
"Use the signalfd() api for signalhandling")
|
||||
add_feature_info("sys/event.h"
|
||||
HAVE_EVENT_H
|
||||
"Use the kevent() and sigwaitinfo() api for signalhandling")
|
||||
|
||||
# --------------------------------------
|
||||
|
||||
option(PAM_REQUIRED "Require building with PAM" ON)
|
||||
|
||||
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)
|
||||
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=gnu90")
|
||||
|
||||
configure_file(config-screenlocker.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-screenlocker.h)
|
||||
include_directories(${CMAKE_CURRENT_BINARY_DIR})
|
||||
|
||||
add_subdirectory(screenlocker)
|
||||
add_subdirectory(checkpass)
|
||||
|
||||
feature_summary(WHAT ALL INCLUDE_QUIET_PACKAGES FATAL_ON_MISSING_REQUIRED_PACKAGES)
|
||||
@ -0,0 +1,41 @@
|
||||
set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake ${CMAKE_MODULE_PATH} )
|
||||
include(UnixAuth)
|
||||
set_package_properties(PAM PROPERTIES DESCRIPTION "PAM Libraries"
|
||||
URL "https://www.kernel.org/pub/linux/libs/pam/"
|
||||
TYPE OPTIONAL
|
||||
PURPOSE "Required for screen unlocking and optionally used by the KDM log in manager"
|
||||
)
|
||||
if(PAM_REQUIRED)
|
||||
set_package_properties(PAM PROPERTIES TYPE REQUIRED)
|
||||
endif()
|
||||
include(CheckTypeSize)
|
||||
include(FindPkgConfig)
|
||||
|
||||
if (PAM_FOUND)
|
||||
set(KDE4_COMMON_PAM_SERVICE "kde" CACHE STRING "The PAM service to use unless overridden for a particular app.")
|
||||
|
||||
macro(define_pam_service APP)
|
||||
string(TOUPPER ${APP}_PAM_SERVICE var)
|
||||
set(cvar KDE4_${var})
|
||||
set(${cvar} "${KDE4_COMMON_PAM_SERVICE}" CACHE STRING "The PAM service for ${APP}.")
|
||||
mark_as_advanced(${cvar})
|
||||
set(${var} "\"${${cvar}}\"")
|
||||
endmacro(define_pam_service)
|
||||
|
||||
define_pam_service(kscreensaver)
|
||||
endif (PAM_FOUND)
|
||||
|
||||
check_function_exists(getpassphrase HAVE_GETPASSPHRASE)
|
||||
check_function_exists(vsyslog HAVE_VSYSLOG)
|
||||
check_function_exists(statvfs HAVE_STATVFS)
|
||||
|
||||
check_include_files(limits.h HAVE_LIMITS_H)
|
||||
check_include_files(sys/time.h HAVE_SYS_TIME_H) # ksmserver, ksplashml, sftp
|
||||
check_include_files(sys/param.h HAVE_SYS_PARAM_H)
|
||||
check_include_files(unistd.h HAVE_UNISTD_H)
|
||||
check_include_files(malloc.h HAVE_MALLOC_H)
|
||||
check_function_exists(statfs HAVE_STATFS)
|
||||
|
||||
set(CMAKE_EXTRA_INCLUDE_FILES sys/socket.h)
|
||||
|
||||
check_function_exists(setpriority HAVE_SETPRIORITY) # kscreenlocker
|
||||
@ -1 +1,32 @@
|
||||
# screenlocker
|
||||
# Screen locker
|
||||
|
||||
## Third Party Code
|
||||
|
||||
kcheckpass
|
||||
|
||||
## Dependencies
|
||||
|
||||
### Debian/Ubuntu
|
||||
|
||||
```
|
||||
sudo apt install libpam0g-dev libx11-dev -y
|
||||
```
|
||||
|
||||
## Build
|
||||
|
||||
```shell
|
||||
mkdir build
|
||||
cd build
|
||||
cmake ..
|
||||
make
|
||||
```
|
||||
|
||||
## Install
|
||||
|
||||
```shell
|
||||
sudo make install
|
||||
```
|
||||
|
||||
## License
|
||||
|
||||
This project has been licensed by GPLv3.
|
||||
|
||||
@ -0,0 +1,33 @@
|
||||
include_directories( ${UNIXAUTH_INCLUDE_DIRS} )
|
||||
check_include_files(paths.h HAVE_PATHS_H)
|
||||
configure_file (config-ccheckpass.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-ccheckpass.h )
|
||||
|
||||
set(ccheckpass_SRCS
|
||||
checkpass.h
|
||||
checkpass.c
|
||||
checkpass_pam.c
|
||||
checkpass_shadow.c
|
||||
)
|
||||
|
||||
# find_package(ECM REQUIRED NO_MODULE)
|
||||
# set(CMAKE_MODULE_PATH ${ECM_MODULE_PATH} )
|
||||
# include(ECMMarkNonGuiExecutable)
|
||||
|
||||
add_executable(ccheckpass ${ccheckpass_SRCS})
|
||||
# ecm_mark_nongui_executable(ccheckpass)
|
||||
|
||||
set_property(TARGET ccheckpass APPEND_STRING PROPERTY COMPILE_FLAGS " -U_REENTRANT")
|
||||
target_link_libraries(ccheckpass ${UNIXAUTH_LIBRARIES} ${SOCKET_LIBRARIES})
|
||||
|
||||
if (PAM_FOUND)
|
||||
set(checkpass_suid "")
|
||||
else()
|
||||
set(checkpass_suid "SETUID")
|
||||
message(STATUS "PAM not found, will install a SUID-kcheckpass")
|
||||
endif()
|
||||
install(TARGETS ccheckpass
|
||||
DESTINATION /usr/bin
|
||||
PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE ${checkpass_suid}
|
||||
)
|
||||
|
||||
#EXTRA_DIST = README
|
||||
@ -0,0 +1,75 @@
|
||||
/*****************************************************************
|
||||
*
|
||||
*· 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_ENUMS_H
|
||||
#define KCHECKPASS_ENUMS_H
|
||||
|
||||
#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,
|
||||
ConvPutAuthSucceeded,
|
||||
ConvPutAuthFailed,
|
||||
ConvPutAuthError,
|
||||
ConvPutAuthAbort,
|
||||
ConvPutReadyForAuthentication,
|
||||
} ConvRequest;
|
||||
|
||||
/* these must match the defs in kgreeterplugin.h */
|
||||
typedef enum {
|
||||
IsUser = 1, /* unused in kcheckpass */
|
||||
IsPassword = 2,
|
||||
} DataTag;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* KCHECKPASS_ENUMS_H */
|
||||
@ -0,0 +1,508 @@
|
||||
/*****************************************************************
|
||||
*
|
||||
* 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 "checkpass.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <signal.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <syslog.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <config-screenlocker.h>
|
||||
|
||||
#if HAVE_SYS_PRCTL_H
|
||||
#include <sys/prctl.h>
|
||||
#endif
|
||||
#if HAVE_SYS_PROCCTL_H
|
||||
#include <sys/procctl.h>
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
#if HAVE_SIGNALFD_H
|
||||
#include <sys/signalfd.h>
|
||||
#endif
|
||||
#if HAVE_EVENT_H
|
||||
#include <sys/event.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/types.h>
|
||||
#endif
|
||||
|
||||
#define THROTTLE 3
|
||||
|
||||
static int havetty, sfd = -1, nullpass;
|
||||
|
||||
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 ConvPutAuthSucceeded:
|
||||
case ConvPutAuthFailed:
|
||||
case ConvPutAuthError:
|
||||
case ConvPutAuthAbort:
|
||||
case ConvPutReadyForAuthentication:
|
||||
return 0;
|
||||
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] -S handle}\n"
|
||||
" options:\n"
|
||||
" -h this help message\n"
|
||||
" -S handle operate in binary server mode on file descriptor handle\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)
|
||||
{
|
||||
const char *method = "classic";
|
||||
const char *username = 0;
|
||||
char *p;
|
||||
struct passwd *pw;
|
||||
int c, nfd;
|
||||
uid_t uid;
|
||||
AuthReturn ret;
|
||||
sigset_t signalMask;
|
||||
#if HAVE_SIGNALFD_H
|
||||
int signalFd;
|
||||
struct signalfd_siginfo fdsi;
|
||||
ssize_t sigReadSize;
|
||||
#endif
|
||||
#if HAVE_EVENT_H
|
||||
/* Event Queue */
|
||||
int keventQueue;
|
||||
/* Listen for two events: SIGUSR1 and SIGUSR2 */
|
||||
struct kevent keventEvent[2];
|
||||
int keventData;
|
||||
#endif
|
||||
pid_t parentPid;
|
||||
|
||||
parentPid = getppid();
|
||||
|
||||
// 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
|
||||
|
||||
// prevent becoming an orphan while waiting for SIGUSR2
|
||||
#if HAVE_PR_SET_DUMPABLE
|
||||
prctl(PR_SET_PDEATHSIG, SIGUSR2);
|
||||
#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, "hm:S:")) != -1) {
|
||||
switch (c) {
|
||||
case 'h':
|
||||
usage(0);
|
||||
break;
|
||||
case 'm':
|
||||
method = optarg;
|
||||
break;
|
||||
case 'S':
|
||||
sfd = atoi(optarg);
|
||||
break;
|
||||
default:
|
||||
message("Command line option parsing error\n");
|
||||
usage(10);
|
||||
}
|
||||
}
|
||||
|
||||
if (sfd == -1) {
|
||||
message("Only binary protocol supported\n");
|
||||
return AuthError;
|
||||
}
|
||||
|
||||
uid = getuid();
|
||||
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;
|
||||
}
|
||||
|
||||
// setup signals
|
||||
sigemptyset(&signalMask);
|
||||
sigaddset(&signalMask, SIGUSR1);
|
||||
sigaddset(&signalMask, SIGUSR2);
|
||||
// block them
|
||||
if (sigprocmask(SIG_BLOCK, &signalMask, NULL) == -1) {
|
||||
message("Block signal failed\n");
|
||||
conv_server(ConvPutAuthError, 0);
|
||||
return 1;
|
||||
}
|
||||
#if HAVE_SIGNALFD_H
|
||||
signalFd = signalfd(-1, &signalMask, SFD_CLOEXEC);
|
||||
if (signalFd == -1) {
|
||||
message("Signal fd failed\n");
|
||||
conv_server(ConvPutAuthError, 0);
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
#if HAVE_EVENT_H
|
||||
/* Setup the kequeu */
|
||||
keventQueue = kqueue();
|
||||
if (keventQueue == -1) {
|
||||
message("Failed to create kqueue for SIGUSR1\n");
|
||||
conv_server(ConvPutAuthError, 0);
|
||||
return 1;
|
||||
}
|
||||
/* Setup the events */
|
||||
EV_SET(&keventEvent[0], SIGUSR1, EVFILT_SIGNAL, EV_ADD, 0, 0, NULL);
|
||||
EV_SET(&keventEvent[1], SIGUSR2, EVFILT_SIGNAL, EV_ADD, 0, 0, NULL);
|
||||
int setupResult = kevent(keventQueue, keventEvent, 2, NULL, 0, NULL);
|
||||
if (setupResult == -1) {
|
||||
message("Failed to attach event to the kqueue\n");
|
||||
conv_server(ConvPutAuthError, 0);
|
||||
return 1;
|
||||
}
|
||||
if (keventEvent[0].flags & EV_ERROR) {
|
||||
message("Error in kevent for SIGUSR1: %s\n", strerror(keventEvent[0].data));
|
||||
conv_server(ConvPutAuthError, 0);
|
||||
return 1;
|
||||
}
|
||||
if (keventEvent[1].flags & EV_ERROR) {
|
||||
message("Error in kevent for SIGUSR2: %s\n", strerror(keventEvent[1].data));
|
||||
conv_server(ConvPutAuthError, 0);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* signal_info for sigwaitinfo() */
|
||||
siginfo_t signalInfo;
|
||||
|
||||
#endif
|
||||
// now lets block on the fd
|
||||
for (;;) {
|
||||
conv_server(ConvPutReadyForAuthentication, 0);
|
||||
#if HAVE_SIGNALFD_H
|
||||
sigReadSize = read(signalFd, &fdsi, sizeof(struct signalfd_siginfo));
|
||||
if (sigReadSize != sizeof(struct signalfd_siginfo)) {
|
||||
message("Read wrong size\n");
|
||||
return 1;
|
||||
}
|
||||
if (fdsi.ssi_signo == SIGUSR1) {
|
||||
if (fdsi.ssi_pid != parentPid) {
|
||||
message("signal from wrong process\n");
|
||||
continue;
|
||||
}
|
||||
#endif
|
||||
#if HAVE_EVENT_H
|
||||
keventData = kevent(keventQueue, NULL, 0, keventEvent, 1, NULL);
|
||||
if (keventData == -1) {
|
||||
/* Let's figure this out in the future, shall we */
|
||||
message("kevent() failed with %d\n", errno);
|
||||
return 1;
|
||||
} else if (keventData == 0) {
|
||||
/* Do we need to handle timeouts? */
|
||||
message("kevent timeout\n");
|
||||
continue;
|
||||
}
|
||||
// We know we got a SIGUSR1 or SIGUSR2, so fetch it via sigwaitinfo()
|
||||
// (otherwise, we could have used sigtimedwait() )
|
||||
int signalReturn = sigwaitinfo(&signalMask, &signalInfo);
|
||||
if (signalReturn < 0) {
|
||||
if (errno == EINTR) {
|
||||
message("sigawaitinfo() interrupted by unblocked caught signal");
|
||||
continue;
|
||||
} else if (errno == EAGAIN) {
|
||||
/* This should not happen, as kevent notified us about such a signal */
|
||||
message("no signal of type USR1 or USR2 pending.");
|
||||
continue;
|
||||
} else {
|
||||
message("Unhandled error in sigwaitinfo()");
|
||||
conv_server(ConvPutAuthError, 0);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
if (signalReturn == SIGUSR1) {
|
||||
if (signalInfo.si_pid != parentPid) {
|
||||
message("signal from wrong process\n");
|
||||
continue;
|
||||
}
|
||||
#endif
|
||||
/* Now do the fandango */
|
||||
ret = Authenticate(method, username, 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);
|
||||
}
|
||||
}
|
||||
switch (ret) {
|
||||
case AuthOk:
|
||||
conv_server(ConvPutAuthSucceeded, 0);
|
||||
break;
|
||||
case AuthBad:
|
||||
conv_server(ConvPutAuthFailed, 0);
|
||||
break;
|
||||
case AuthError:
|
||||
conv_server(ConvPutAuthError, 0);
|
||||
break;
|
||||
case AuthAbort:
|
||||
conv_server(ConvPutAuthAbort, 0);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (uid != geteuid()) {
|
||||
// we don't support multiple auth for setuid kcheckpass
|
||||
break;
|
||||
}
|
||||
#if HAVE_SIGNALFD_H
|
||||
} else if (fdsi.ssi_signo == SIGUSR2) {
|
||||
if (fdsi.ssi_pid != parentPid) {
|
||||
message("signal from wrong process\n");
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
#if HAVE_EVENT_H
|
||||
} else if (signalReturn == SIGUSR2) {
|
||||
if (signalInfo.si_pid != parentPid) {
|
||||
message("signal from wrong process\n");
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
} else {
|
||||
message("unexpected signal\n");
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void dispose(char *str)
|
||||
{
|
||||
memset(str, 0, strlen(str));
|
||||
free(str);
|
||||
}
|
||||
|
||||
/*****************************************************************
|
||||
The real authentication methods are in separate source files.
|
||||
Look in checkpass_*.c
|
||||
*****************************************************************/
|
||||
@ -0,0 +1,105 @@
|
||||
/*****************************************************************
|
||||
*
|
||||
* 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-ccheckpass.h>
|
||||
#include <config-unix.h>
|
||||
#include <config-workspace.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>
|
||||
|
||||
/* Make sure there is only one! */
|
||||
#if defined(HAVE_PAM)
|
||||
#else
|
||||
#define HAVE_SHADOW
|
||||
#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
|
||||
|
||||
#include "checkpass-enums.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*****************************************************************
|
||||
* Authenticates user
|
||||
*****************************************************************/
|
||||
AuthReturn Authenticate(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
|
||||
@ -0,0 +1,204 @@
|
||||
/*
|
||||
* 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 "checkpass.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_CONST
|
||||
typedef const struct pam_message pam_message_type;
|
||||
typedef const void *pam_gi_type;
|
||||
#else
|
||||
typedef struct pam_message pam_message_type;
|
||||
typedef 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 *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", KSCREENSAVER_PAM_SERVICE, method);
|
||||
pam_service = pservb;
|
||||
} else {
|
||||
/* PAM_data.classic = 1; */
|
||||
pam_service = KSCREENSAVER_PAM_SERVICE;
|
||||
}
|
||||
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) {
|
||||
if (PAM_data.abort) {
|
||||
PAM_data.abort = 0;
|
||||
pam_end(pamh, pam_error);
|
||||
return AuthAbort;
|
||||
}
|
||||
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 ones. */
|
||||
|
||||
pam_end(pamh, PAM_SUCCESS);
|
||||
return AuthOk;
|
||||
}
|
||||
|
||||
#endif
|
||||
@ -0,0 +1,85 @@
|
||||
/*
|
||||
* 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 "checkpass.h"
|
||||
|
||||
/*******************************************************************
|
||||
* This is the authentication code for Shadow-Passwords
|
||||
*******************************************************************/
|
||||
|
||||
#ifdef HAVE_SHADOW
|
||||
#include <pwd.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.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 (crpt_passwd && !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
|
||||
@ -0,0 +1,3 @@
|
||||
/* Define to 1 if you have the <paths.h> header file. */
|
||||
#cmakedefine HAVE_PATHS_H 1
|
||||
|
||||
@ -0,0 +1,36 @@
|
||||
#=============================================================================
|
||||
# Copyright 2017 Tobias C. Berner <tcberner@FreeBSD.org>
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions
|
||||
# are met:
|
||||
#
|
||||
# 1. Redistributions of source code must retain the copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# 2. Redistributions in binary form must reproduce the copyright
|
||||
# notice, this list of conditions and the following disclaimer in the
|
||||
# documentation and/or other materials provided with the distribution.
|
||||
# 3. The name of the author may not be used to endorse or promote products
|
||||
# derived from this software without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
#=============================================================================
|
||||
find_program(cklistsessions_EXECUTABLE NAMES ck-list-sessions)
|
||||
find_program(qdbus_EXECUTABLE NAMES qdbus)
|
||||
find_package_handle_standard_args(ConsoleKit
|
||||
FOUND_VAR
|
||||
ConsoleKit_FOUND
|
||||
REQUIRED_VARS
|
||||
cklistsessions_EXECUTABLE
|
||||
qdbus_EXECUTABLE
|
||||
)
|
||||
mark_as_advanced(ConsoleKit_FOUND cklistsessions_EXECUTABLE qdbus_EXECUTABLE)
|
||||
@ -0,0 +1,74 @@
|
||||
# - Try to find the PAM libraries
|
||||
# Once done this will define
|
||||
#
|
||||
# PAM_FOUND - system has pam
|
||||
# PAM_INCLUDE_DIR - the pam include directory
|
||||
# PAM_LIBRARIES - libpam library
|
||||
|
||||
if (PAM_INCLUDE_DIR AND PAM_LIBRARY)
|
||||
# Already in cache, be silent
|
||||
set(PAM_FIND_QUIETLY TRUE)
|
||||
endif (PAM_INCLUDE_DIR AND PAM_LIBRARY)
|
||||
|
||||
find_path(PAM_INCLUDE_DIR NAMES security/pam_appl.h pam/pam_appl.h)
|
||||
find_library(PAM_LIBRARY pam)
|
||||
find_library(DL_LIBRARY dl)
|
||||
|
||||
if (PAM_INCLUDE_DIR AND PAM_LIBRARY)
|
||||
set(PAM_FOUND TRUE)
|
||||
if (DL_LIBRARY)
|
||||
set(PAM_LIBRARIES ${PAM_LIBRARY} ${DL_LIBRARY})
|
||||
else (DL_LIBRARY)
|
||||
set(PAM_LIBRARIES ${PAM_LIBRARY})
|
||||
endif (DL_LIBRARY)
|
||||
|
||||
if (EXISTS ${PAM_INCLUDE_DIR}/pam/pam_appl.h)
|
||||
# darwin claims to be something special
|
||||
set(HAVE_PAM_PAM_APPL_H 1)
|
||||
endif (EXISTS ${PAM_INCLUDE_DIR}/pam/pam_appl.h)
|
||||
|
||||
if (NOT DEFINED PAM_MESSAGE_CONST)
|
||||
include(CheckCXXSourceCompiles)
|
||||
# XXX does this work with plain c?
|
||||
check_cxx_source_compiles("
|
||||
#if ${HAVE_PAM_PAM_APPL_H}+0
|
||||
# include <pam/pam_appl.h>
|
||||
#else
|
||||
# include <security/pam_appl.h>
|
||||
#endif
|
||||
|
||||
static int PAM_conv(
|
||||
int num_msg,
|
||||
const struct pam_message **msg, /* this is the culprit */
|
||||
struct pam_response **resp,
|
||||
void *ctx)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
struct pam_conv PAM_conversation = {
|
||||
&PAM_conv, /* this bombs out if the above does not match */
|
||||
0
|
||||
};
|
||||
|
||||
return 0;
|
||||
}
|
||||
" PAM_MESSAGE_CONST)
|
||||
endif (NOT DEFINED PAM_MESSAGE_CONST)
|
||||
set(PAM_MESSAGE_CONST ${PAM_MESSAGE_CONST} CACHE BOOL "PAM expects a conversation function with const pam_message")
|
||||
|
||||
endif (PAM_INCLUDE_DIR AND PAM_LIBRARY)
|
||||
|
||||
if (PAM_FOUND)
|
||||
if (NOT PAM_FIND_QUIETLY)
|
||||
message(STATUS "Found PAM: ${PAM_LIBRARIES}")
|
||||
endif (NOT PAM_FIND_QUIETLY)
|
||||
else (PAM_FOUND)
|
||||
if (PAM_FIND_REQUIRED)
|
||||
message(FATAL_ERROR "PAM was not found")
|
||||
endif(PAM_FIND_REQUIRED)
|
||||
endif (PAM_FOUND)
|
||||
|
||||
mark_as_advanced(PAM_INCLUDE_DIR PAM_LIBRARY DL_LIBRARY PAM_MESSAGE_CONST)
|
||||
@ -0,0 +1,34 @@
|
||||
#=============================================================================
|
||||
# Copyright 2016 Martin Gräßlin <mgraesslin@kde.org>
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions
|
||||
# are met:
|
||||
#
|
||||
# 1. Redistributions of source code must retain the copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# 2. Redistributions in binary form must reproduce the copyright
|
||||
# notice, this list of conditions and the following disclaimer in the
|
||||
# documentation and/or other materials provided with the distribution.
|
||||
# 3. The name of the author may not be used to endorse or promote products
|
||||
# derived from this software without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
#=============================================================================
|
||||
find_program(loginctl_EXECUTABLE NAMES loginctl)
|
||||
find_package_handle_standard_args(loginctl
|
||||
FOUND_VAR
|
||||
loginctl_FOUND
|
||||
REQUIRED_VARS
|
||||
loginctl_EXECUTABLE
|
||||
)
|
||||
mark_as_advanced(loginctl_FOUND loginctl_EXECUTABLE)
|
||||
@ -0,0 +1,56 @@
|
||||
find_package(PAM)
|
||||
|
||||
include(CheckFunctionExists)
|
||||
include(CheckLibraryExists)
|
||||
include(CheckIncludeFiles)
|
||||
include(CMakePushCheckState)
|
||||
|
||||
set(UNIXAUTH_LIBRARIES)
|
||||
set(UNIXAUTH_INCLUDE_DIRS)
|
||||
|
||||
set(SHADOW_LIBRARIES)
|
||||
check_function_exists(getspnam found_getspnam)
|
||||
if (found_getspnam)
|
||||
set(HAVE_GETSPNAM 1)
|
||||
else (found_getspnam)
|
||||
cmake_push_check_state()
|
||||
set(CMAKE_REQUIRED_LIBRARIES -lshadow)
|
||||
check_function_exists(getspnam found_getspnam_shadow)
|
||||
if (found_getspnam_shadow)
|
||||
set(HAVE_GETSPNAM 1)
|
||||
set(SHADOW_LIBRARIES shadow)
|
||||
check_function_exists(pw_encrypt HAVE_PW_ENCRYPT) # ancient Linux shadow
|
||||
else (found_getspnam_shadow)
|
||||
set(CMAKE_REQUIRED_LIBRARIES -lgen) # UnixWare
|
||||
check_function_exists(getspnam found_getspnam_gen)
|
||||
if (found_getspnam_gen)
|
||||
set(HAVE_GETSPNAM 1)
|
||||
set(SHADOW_LIBRARIES gen)
|
||||
endif (found_getspnam_gen)
|
||||
endif (found_getspnam_shadow)
|
||||
cmake_pop_check_state()
|
||||
endif (found_getspnam)
|
||||
|
||||
set(CRYPT_LIBRARIES)
|
||||
check_library_exists(crypt crypt "" HAVE_CRYPT)
|
||||
if (HAVE_CRYPT)
|
||||
set(CRYPT_LIBRARIES crypt)
|
||||
check_include_files(crypt.h HAVE_CRYPT_H)
|
||||
endif (HAVE_CRYPT)
|
||||
|
||||
if (PAM_FOUND)
|
||||
|
||||
set(HAVE_PAM 1)
|
||||
set(UNIXAUTH_LIBRARIES ${PAM_LIBRARIES})
|
||||
set(UNIXAUTH_INCLUDE_DIRS ${PAM_INCLUDE_DIR})
|
||||
|
||||
else (PAM_FOUND)
|
||||
|
||||
if (HAVE_GETSPNAM)
|
||||
set(UNIXAUTH_LIBRARIES ${SHADOW_LIBRARIES})
|
||||
endif (HAVE_GETSPNAM)
|
||||
if (NOT HAVE_PW_ENCRYPT)
|
||||
set(UNIXAUTH_LIBRARIES ${UNIXAUTH_LIBRARIES} ${CRYPT_LIBRARIES})
|
||||
endif (NOT HAVE_PW_ENCRYPT)
|
||||
|
||||
endif (PAM_FOUND)
|
||||
@ -0,0 +1,2 @@
|
||||
/* Define if you have the XInput extension */
|
||||
#cmakedefine X11_Xinput_FOUND 1
|
||||
@ -0,0 +1,16 @@
|
||||
#ifdef KSCREENLOCKER_TEST_APPS
|
||||
#define KCHECKPASS_BIN "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/kcheckpass"
|
||||
#elif defined(KSCREENLOCKER_UNIT_TEST)
|
||||
#define KCHECKPASS_BIN "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/fakekcheckpass"
|
||||
#else
|
||||
#define KCHECKPASS_BIN "${CMAKE_INSTALL_FULL_LIBEXECDIR}/kcheckpass"
|
||||
#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
|
||||
#cmakedefine01 HAVE_SIGNALFD_H
|
||||
#cmakedefine01 HAVE_EVENT_H
|
||||
@ -0,0 +1,32 @@
|
||||
/* Defines if you have PAM (Pluggable Authentication Modules) */
|
||||
#cmakedefine HAVE_PAM 1
|
||||
|
||||
/* Define if your PAM headers are in pam/ instead of security/ */
|
||||
#cmakedefine HAVE_PAM_PAM_APPL_H 1
|
||||
|
||||
/* Define if your PAM expects a conversation function with const pam_message (Solaris) */
|
||||
#cmakedefine PAM_MESSAGE_CONST 1
|
||||
|
||||
/* The PAM service to be used by kscreensaver */
|
||||
#cmakedefine KSCREENSAVER_PAM_SERVICE ${KSCREENSAVER_PAM_SERVICE}
|
||||
|
||||
/* Defines if your system has the getspnam function */
|
||||
#cmakedefine HAVE_GETSPNAM 1
|
||||
|
||||
/* Defines if your system has the crypt function */
|
||||
#cmakedefine HAVE_CRYPT 1
|
||||
|
||||
/* Define to 1 if you have the <crypt.h> header file. */
|
||||
#cmakedefine HAVE_CRYPT_H 1
|
||||
|
||||
/* Define to 1 if you have the `pw_encrypt' function. */
|
||||
#cmakedefine HAVE_PW_ENCRYPT 1
|
||||
|
||||
/* Define to 1 if you have the `getpassphrase' function. */
|
||||
#cmakedefine HAVE_GETPASSPHRASE 1
|
||||
|
||||
/* Define to 1 if you have the `vsyslog' function. */
|
||||
#cmakedefine HAVE_VSYSLOG 1
|
||||
|
||||
/* Define to 1 if you have the <limits.h> header file. */
|
||||
#cmakedefine HAVE_LIMITS_H 1
|
||||
@ -0,0 +1,141 @@
|
||||
/* config-workspace.h. Generated by cmake from config-workspace.h.cmake */
|
||||
|
||||
/* Define if you have DPMS support */
|
||||
#cmakedefine HAVE_DPMS 1
|
||||
|
||||
/* Define if you have the DPMSCapable prototype in <X11/extensions/dpms.h> */
|
||||
#cmakedefine HAVE_DPMSCAPABLE_PROTO 1
|
||||
|
||||
/* Define if you have the DPMSInfo prototype in <X11/extensions/dpms.h> */
|
||||
#cmakedefine HAVE_DPMSINFO_PROTO 1
|
||||
|
||||
/* Define if you have gethostname */
|
||||
#cmakedefine HAVE_GETHOSTNAME 1
|
||||
|
||||
/* Define if you have the gethostname prototype */
|
||||
#cmakedefine HAVE_GETHOSTNAME_PROTO 1
|
||||
|
||||
/* Define if you have long long as datatype */
|
||||
#cmakedefine HAVE_LONG_LONG 1
|
||||
|
||||
/* Define to 1 if you have the `nice' function. */
|
||||
#cmakedefine HAVE_NICE 1
|
||||
|
||||
/* Define to 1 if you have the <sasl.h> header file. */
|
||||
#cmakedefine HAVE_SASL_H 1
|
||||
|
||||
/* Define to 1 if you have the <sasl/sasl.h> header file. */
|
||||
#cmakedefine HAVE_SASL_SASL_H 1
|
||||
|
||||
/* Define to 1 if you have the `setpriority' function. */
|
||||
#cmakedefine HAVE_SETPRIORITY 1
|
||||
|
||||
/* Define to 1 if you have the `sigaction' function. */
|
||||
#cmakedefine HAVE_SIGACTION 1
|
||||
|
||||
/* Define to 1 if you have the `sigset' function. */
|
||||
#cmakedefine HAVE_SIGSET 1
|
||||
|
||||
/* Define to 1 if you have statvfs */
|
||||
#cmakedefine HAVE_STATVFS 1
|
||||
|
||||
/* Define to 1 if you have the <string.h> header file. */
|
||||
#cmakedefine HAVE_STRING_H 1
|
||||
|
||||
/* Define if you have the struct ucred */
|
||||
#cmakedefine HAVE_STRUCT_UCRED 1
|
||||
|
||||
/* Define to 1 if you have the <sys/loadavg.h> header file. */
|
||||
#cmakedefine HAVE_SYS_LOADAVG_H 1
|
||||
|
||||
/* Define to 1 if you have the <sys/mount.h> header file. */
|
||||
#cmakedefine HAVE_SYS_MOUNT_H 1
|
||||
|
||||
/* Define to 1 if you have the <sys/param.h> header file. */
|
||||
#cmakedefine HAVE_SYS_PARAM_H 1
|
||||
|
||||
/* Define to 1 if you have the <sys/statfs.h> header file. */
|
||||
#cmakedefine HAVE_SYS_STATFS_H 1
|
||||
|
||||
/* Define to 1 if you have the <sys/statvfs.h> header file. */
|
||||
#cmakedefine HAVE_SYS_STATVFS_H 1
|
||||
|
||||
/* Define to 1 if you have statfs(). */
|
||||
#cmakedefine HAVE_STATFS 1
|
||||
|
||||
/* Define to 1 if you have the <sys/select.h> header file. */
|
||||
#cmakedefine HAVE_SYS_SELECT_H 1
|
||||
|
||||
/* Define to 1 if you have the <sys/socket.h> header file. */
|
||||
#cmakedefine HAVE_SYS_SOCKET_H 1
|
||||
|
||||
/* Define to 1 if you have the <sys/time.h> header file. */
|
||||
#cmakedefine HAVE_SYS_TIME_H 1
|
||||
|
||||
/* Define to 1 if you have the <sys/types.h> header file. */
|
||||
#cmakedefine HAVE_SYS_TYPES_H 1
|
||||
|
||||
/* Define to 1 if you have the <sys/vfs.h> header file. */
|
||||
#cmakedefine HAVE_SYS_VFS_H 1
|
||||
|
||||
/* Define to 1 if you have the <sys/wait.h> header file. */
|
||||
#cmakedefine HAVE_SYS_WAIT_H 1
|
||||
|
||||
/* Define to 1 if you have the <unistd.h> header file. */
|
||||
#cmakedefine HAVE_UNISTD_H 1
|
||||
|
||||
/* Define to 1 if you have the <stdint.h> header file. */
|
||||
#cmakedefine HAVE_STDINT_H 1
|
||||
|
||||
/* Define to 1 if you have the <malloc.h> header file. */
|
||||
#cmakedefine HAVE_MALLOC_H 1
|
||||
|
||||
/* Define if you have unsetenv */
|
||||
#cmakedefine HAVE_UNSETENV 1
|
||||
|
||||
/* Define if you have the unsetenv prototype */
|
||||
#cmakedefine HAVE_UNSETENV_PROTO 1
|
||||
|
||||
/* Define if you have usleep */
|
||||
#cmakedefine HAVE_USLEEP 1
|
||||
|
||||
/* Define if you have the usleep prototype */
|
||||
#cmakedefine HAVE_USLEEP_PROTO 1
|
||||
|
||||
/* Define to 1 if you have the `vsnprintf' function. */
|
||||
#cmakedefine HAVE_VSNPRINTF 1
|
||||
|
||||
/* Define to 1 if you have the Wayland libraries. */
|
||||
#cmakedefine WAYLAND_FOUND 1
|
||||
|
||||
/* KDE's default home directory */
|
||||
#cmakedefine KDE_DEFAULT_HOME "${KDE_DEFAULT_HOME}"
|
||||
|
||||
/* KDE's binaries directory */
|
||||
#define KDE_BINDIR "${BIN_INSTALL_DIR}"
|
||||
|
||||
/* KDE's configuration directory */
|
||||
#define KDE_CONFDIR "${CONFIG_INSTALL_DIR}"
|
||||
|
||||
/* KDE's static data directory */
|
||||
#define KDE_DATADIR "${DATA_INSTALL_DIR}"
|
||||
|
||||
/* Define to 1 if you can safely include both <sys/time.h> and <time.h>. */
|
||||
#cmakedefine TIME_WITH_SYS_TIME 1
|
||||
|
||||
/* X binaries directory */
|
||||
#cmakedefine XBINDIR "${XBINDIR}"
|
||||
|
||||
/* X libraries directory */
|
||||
#cmakedefine XLIBDIR "${XLIBDIR}"
|
||||
|
||||
/* Number of bits in a file offset, on hosts where this is settable. */
|
||||
#define _FILE_OFFSET_BITS 64
|
||||
|
||||
/*
|
||||
* On HP-UX, the declaration of vsnprintf() is needed every time !
|
||||
*/
|
||||
|
||||
/* type to use in place of socklen_t if not defined */
|
||||
#define kde_socklen_t socklen_t
|
||||
|
||||
@ -0,0 +1,28 @@
|
||||
set(CMAKE_AUTOUIC ON)
|
||||
set(CMAKE_AUTOMOC ON)
|
||||
set(CMAKE_AUTORCC ON)
|
||||
|
||||
set(PROJECT_SOURCES
|
||||
main.cpp
|
||||
application.cpp
|
||||
authenticator.cpp
|
||||
wallpaper.cpp
|
||||
kcheckpass-enums.h
|
||||
fixx11h.h
|
||||
qml.qrc
|
||||
)
|
||||
|
||||
add_executable(screenlocker
|
||||
${PROJECT_SOURCES}
|
||||
)
|
||||
|
||||
target_link_libraries(screenlocker
|
||||
PRIVATE
|
||||
Qt5::Core
|
||||
Qt5::DBus
|
||||
Qt5::Widgets
|
||||
Qt5::X11Extras
|
||||
Qt5::Quick
|
||||
|
||||
${X11_LIBRARIES}
|
||||
)
|
||||
@ -0,0 +1,299 @@
|
||||
#include "application.h"
|
||||
#include "wallpaper.h"
|
||||
|
||||
// Qt Core
|
||||
#include <QAbstractNativeEventFilter>
|
||||
#include <QScreen>
|
||||
#include <QX11Info>
|
||||
#include <QEvent>
|
||||
|
||||
// Qt Quick
|
||||
#include <QQuickItem>
|
||||
#include <QQmlContext>
|
||||
#include <QQmlEngine>
|
||||
#include <QQmlProperty>
|
||||
|
||||
// X11
|
||||
#include <X11/Xatom.h>
|
||||
#include <X11/Xlib.h>
|
||||
#include "fixx11h.h"
|
||||
|
||||
// Xcb
|
||||
#include <xcb/xcb.h>
|
||||
|
||||
// this is usable to fake a "screensaver" installation for testing
|
||||
// *must* be "0" for every public commit!
|
||||
#define TEST_SCREENSAVER 0
|
||||
|
||||
class FocusOutEventFilter : public QAbstractNativeEventFilter
|
||||
{
|
||||
public:
|
||||
bool nativeEventFilter(const QByteArray &eventType, void *message, long int *result) override {
|
||||
Q_UNUSED(result)
|
||||
if (qstrcmp(eventType, "xcb_generic_event_t") != 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
xcb_generic_event_t *event = reinterpret_cast<xcb_generic_event_t *>(message);
|
||||
if ((event->response_type & ~0x80) == XCB_FOCUS_OUT) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
Application::Application(int &argc, char **argv)
|
||||
: QGuiApplication(argc, argv)
|
||||
, m_authenticator(new Authenticator(AuthenticationMode::Direct, this))
|
||||
{
|
||||
// It's a queued connection to give the QML part time to eventually execute code connected to Authenticator::succeeded if any
|
||||
connect(m_authenticator, &Authenticator::succeeded, this, &QCoreApplication::quit, Qt::QueuedConnection);
|
||||
|
||||
installEventFilter(this);
|
||||
|
||||
// Screens
|
||||
connect(this, &Application::screenAdded, this, &Application::onScreenAdded);
|
||||
connect(this, &Application::screenRemoved, this, &Application::desktopResized);
|
||||
|
||||
if (QX11Info::isPlatformX11()) {
|
||||
installNativeEventFilter(new FocusOutEventFilter);
|
||||
}
|
||||
}
|
||||
|
||||
Application::~Application()
|
||||
{
|
||||
// workaround QTBUG-55460
|
||||
// will be fixed when themes port to QQC2
|
||||
for (auto view : qAsConst(m_views)) {
|
||||
if (QQuickItem *focusItem = view->activeFocusItem()) {
|
||||
focusItem->setFocus(false);
|
||||
}
|
||||
}
|
||||
qDeleteAll(m_views);
|
||||
}
|
||||
|
||||
void Application::initialViewSetup()
|
||||
{
|
||||
for (QScreen *screen : screens()) {
|
||||
connect(screen, &QScreen::geometryChanged, this, [this, screen](const QRect &geo) {
|
||||
screenGeometryChanged(screen, geo);
|
||||
});
|
||||
}
|
||||
|
||||
desktopResized();
|
||||
}
|
||||
|
||||
void Application::desktopResized()
|
||||
{
|
||||
const int nScreens = screens().count();
|
||||
// remove useless views and savers
|
||||
while (m_views.count() > nScreens) {
|
||||
m_views.takeLast()->deleteLater();
|
||||
}
|
||||
|
||||
// extend views and savers to current demand
|
||||
for (int i = m_views.count(); i < nScreens; ++i) {
|
||||
// create the view
|
||||
auto *view = new QQuickView;
|
||||
view->create();
|
||||
|
||||
// engine stuff
|
||||
QQmlContext *context = view->engine()->rootContext();
|
||||
context->setContextProperty(QStringLiteral("authenticator"), m_authenticator);
|
||||
context->setContextProperty(QStringLiteral("wallpaper"), new Wallpaper);
|
||||
|
||||
view->setSource(QUrl("qrc:/qml/LockScreen.qml"));
|
||||
view->setResizeMode(QQuickView::SizeRootObjectToView);
|
||||
|
||||
view->setColor(Qt::black);
|
||||
auto screen = QGuiApplication::screens()[i];
|
||||
view->setGeometry(screen->geometry());
|
||||
|
||||
if (!m_testing) {
|
||||
if (QX11Info::isPlatformX11()) {
|
||||
view->setFlags(Qt::X11BypassWindowManagerHint);
|
||||
} else {
|
||||
view->setFlags(Qt::FramelessWindowHint);
|
||||
}
|
||||
}
|
||||
|
||||
// overwrite the factory set by kdeclarative
|
||||
// auto oldFactory = view->engine()->networkAccessManagerFactory();
|
||||
// view->engine()->setNetworkAccessManagerFactory(nullptr);
|
||||
// delete oldFactory;
|
||||
// view->engine()->setNetworkAccessManagerFactory(new NoAccessNetworkAccessManagerFactory);
|
||||
|
||||
view->setGeometry(screen->geometry());
|
||||
|
||||
connect(view, &QQuickView::frameSwapped, this, [=] { markViewsAsVisible(view); }, Qt::QueuedConnection);
|
||||
|
||||
m_views << view;
|
||||
}
|
||||
|
||||
// update geometry of all views and savers
|
||||
for (int i = 0; i < nScreens; ++i) {
|
||||
auto *view = m_views.at(i);
|
||||
auto screen = QGuiApplication::screens()[i];
|
||||
view->setScreen(screen);
|
||||
|
||||
// on Wayland we may not use fullscreen as that puts all windows on one screen
|
||||
if (m_testing || QX11Info::isPlatformX11()) {
|
||||
view->show();
|
||||
} else {
|
||||
view->showFullScreen();
|
||||
}
|
||||
|
||||
view->raise();
|
||||
}
|
||||
}
|
||||
|
||||
void Application::onScreenAdded(QScreen *screen)
|
||||
{
|
||||
// Lambda connections can not have uniqueness constraints, ensure
|
||||
// geometry change signals are only connected once
|
||||
connect(screen, &QScreen::geometryChanged, this, [this, screen](const QRect &geo) {
|
||||
screenGeometryChanged(screen, geo);
|
||||
});
|
||||
|
||||
desktopResized();
|
||||
}
|
||||
|
||||
void Application::getFocus()
|
||||
{
|
||||
QWindow *activeScreen = getActiveScreen();
|
||||
|
||||
if (!activeScreen) {
|
||||
return;
|
||||
}
|
||||
|
||||
// this loop is required to make the qml/graphicsscene properly handle the shared keyboard input
|
||||
// ie. "type something into the box of every greeter"
|
||||
for (QQuickView *view : qAsConst(m_views)) {
|
||||
if (!m_testing) {
|
||||
view->setKeyboardGrabEnabled(true); // TODO - check whether this still works in master!
|
||||
}
|
||||
}
|
||||
|
||||
// activate window and grab input to be sure it really ends up there.
|
||||
// focus setting is still required for proper internal QWidget state (and eg. visual reflection)
|
||||
if (!m_testing) {
|
||||
activeScreen->setKeyboardGrabEnabled(true); // TODO - check whether this still works in master!
|
||||
}
|
||||
|
||||
activeScreen->requestActivate();
|
||||
}
|
||||
|
||||
void Application::markViewsAsVisible(QQuickView *view)
|
||||
{
|
||||
disconnect(view, &QQuickWindow::frameSwapped, this, nullptr);
|
||||
QQmlProperty showProperty(view->rootObject(), QStringLiteral("viewVisible"));
|
||||
showProperty.write(true);
|
||||
|
||||
// random state update, actually rather required on init only
|
||||
QMetaObject::invokeMethod(this, "getFocus", Qt::QueuedConnection);
|
||||
}
|
||||
|
||||
bool Application::eventFilter(QObject *obj, QEvent *event)
|
||||
{
|
||||
if (obj != this && event->type() == QEvent::Show) {
|
||||
QQuickView *view = nullptr;
|
||||
for (QQuickView *v : qAsConst(m_views)) {
|
||||
if (v == obj) {
|
||||
view = v;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (view && view->winId() && QX11Info::isPlatformX11()) {
|
||||
// showing greeter view window, set property
|
||||
static Atom tag = XInternAtom(QX11Info::display(), "_KDE_SCREEN_LOCKER", False);
|
||||
XChangeProperty(QX11Info::display(), view->winId(), tag, tag, 32, PropModeReplace, nullptr, 0);
|
||||
}
|
||||
// no further processing
|
||||
return false;
|
||||
}
|
||||
|
||||
if (event->type() == QEvent::MouseButtonPress && QX11Info::isPlatformX11()) {
|
||||
if (getActiveScreen()) {
|
||||
getActiveScreen()->requestActivate();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
if (event->type() == QEvent::KeyPress) { // react if saver is visible
|
||||
shareEvent(event, qobject_cast<QQuickView *>(obj));
|
||||
return false; // we don't care
|
||||
} else if (event->type() == QEvent::KeyRelease) { // conditionally reshow the saver
|
||||
QKeyEvent *ke = static_cast<QKeyEvent *>(event);
|
||||
if (ke->key() != Qt::Key_Escape) {
|
||||
shareEvent(event, qobject_cast<QQuickView *>(obj));
|
||||
return false; // irrelevant
|
||||
}
|
||||
return true; // don't pass
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
QWindow *Application::getActiveScreen()
|
||||
{
|
||||
QWindow *activeScreen = nullptr;
|
||||
|
||||
if (m_views.isEmpty()) {
|
||||
return activeScreen;
|
||||
}
|
||||
|
||||
for (QQuickView *view : qAsConst(m_views)) {
|
||||
if (view->geometry().contains(QCursor::pos())) {
|
||||
activeScreen = view;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!activeScreen) {
|
||||
activeScreen = m_views.first();
|
||||
}
|
||||
|
||||
return activeScreen;
|
||||
}
|
||||
|
||||
void Application::shareEvent(QEvent *e, QQuickView *from)
|
||||
{
|
||||
// from can be NULL any time (because the parameter is passed as qobject_cast)
|
||||
// m_views.contains(from) is atm. supposed to be true but required if any further
|
||||
// QQuickView are added (which are not part of m_views)
|
||||
// this makes "from" an optimization (nullptr check aversion)
|
||||
if (from && m_views.contains(from)) {
|
||||
// NOTICE any recursion in the event sharing will prevent authentication on multiscreen setups!
|
||||
// Any change in regarded event processing shall be tested thoroughly!
|
||||
removeEventFilter(this); // prevent recursion!
|
||||
const bool accepted = e->isAccepted(); // store state
|
||||
for (QQuickView *view : qAsConst(m_views)) {
|
||||
if (view != from) {
|
||||
QCoreApplication::sendEvent(view, e);
|
||||
e->setAccepted(accepted);
|
||||
}
|
||||
}
|
||||
installEventFilter(this);
|
||||
}
|
||||
}
|
||||
|
||||
void Application::screenGeometryChanged(QScreen *screen, const QRect &geo)
|
||||
{
|
||||
// We map screens() to m_views by index and Qt is free to
|
||||
// reorder screens, so pointer to pointer connections
|
||||
// may not remain matched by index, perform index
|
||||
// mapping in the change event itself
|
||||
const int screenIndex = QGuiApplication::screens().indexOf(screen);
|
||||
if (screenIndex < 0) {
|
||||
qWarning() << "Screen not found, not updating geometry" << screen;
|
||||
return;
|
||||
}
|
||||
|
||||
if (screenIndex >= m_views.size()) {
|
||||
qWarning() << "Screen index out of range, not updating geometry" << screenIndex;
|
||||
return;
|
||||
}
|
||||
|
||||
QQuickView *view = m_views[screenIndex];
|
||||
view->setGeometry(geo);
|
||||
}
|
||||
@ -0,0 +1,44 @@
|
||||
#ifndef APPLICATION_H
|
||||
#define APPLICATION_H
|
||||
|
||||
#include <QGuiApplication>
|
||||
#include <QQuickView>
|
||||
#include "authenticator.h"
|
||||
|
||||
struct org_kde_ksld;
|
||||
|
||||
class Application : public QGuiApplication
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit Application(int &argc, char **argv);
|
||||
~Application();
|
||||
|
||||
void initialViewSetup();
|
||||
|
||||
public slots:
|
||||
void desktopResized();
|
||||
void onScreenAdded(QScreen *screen);
|
||||
|
||||
private slots:
|
||||
void getFocus();
|
||||
void markViewsAsVisible(QQuickView *view);
|
||||
|
||||
protected:
|
||||
bool eventFilter(QObject *obj, QEvent *event) override;
|
||||
|
||||
private:
|
||||
QWindow *getActiveScreen();
|
||||
void shareEvent(QEvent *e, QQuickView *from);
|
||||
void screenGeometryChanged(QScreen *screen, const QRect &geo);
|
||||
|
||||
private:
|
||||
Authenticator *m_authenticator;
|
||||
QList<QQuickView *> m_views;
|
||||
|
||||
org_kde_ksld *m_ksldInterface = nullptr;
|
||||
bool m_testing = true;
|
||||
};
|
||||
|
||||
#endif // APPLICATION_H
|
||||
@ -0,0 +1,333 @@
|
||||
/********************************************************************
|
||||
KSld - the KDE Screenlocker Daemon
|
||||
This file is part of the KDE project.
|
||||
|
||||
Copyright (C) 1999 Martin R. Jones <mjones@kde.org>
|
||||
Copyright (C) 2002 Luboš Luňák <l.lunak@kde.org>
|
||||
Copyright (C) 2003 Oswald Buddenhagen <ossi@kde.org>
|
||||
Copyright (C) 2014 Martin Gräßlin <mgraesslin@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, see <http://www.gnu.org/licenses/>.
|
||||
*********************************************************************/
|
||||
#include "authenticator.h"
|
||||
|
||||
#include "kcheckpass-enums.h"
|
||||
|
||||
// Qt
|
||||
#include <QCoreApplication>
|
||||
#include <QFile>
|
||||
#include <QSocketNotifier>
|
||||
#include <QTimer>
|
||||
|
||||
// system
|
||||
#include <errno.h>
|
||||
#include <signal.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
|
||||
Authenticator::Authenticator(AuthenticationMode mode, QObject *parent)
|
||||
: QObject(parent)
|
||||
, m_graceLockTimer(new QTimer(this))
|
||||
, m_checkPass(nullptr)
|
||||
{
|
||||
m_graceLockTimer->setSingleShot(true);
|
||||
m_graceLockTimer->setInterval(3000);
|
||||
connect(m_graceLockTimer, &QTimer::timeout, this, &Authenticator::graceLockedChanged);
|
||||
|
||||
if (mode == AuthenticationMode::Delayed) {
|
||||
m_checkPass = new KCheckPass(AuthenticationMode::Delayed, this);
|
||||
setupCheckPass();
|
||||
}
|
||||
}
|
||||
|
||||
Authenticator::~Authenticator() = default;
|
||||
|
||||
void Authenticator::tryUnlock(const QString &password)
|
||||
{
|
||||
if (isGraceLocked()) {
|
||||
Q_EMIT failed();
|
||||
return;
|
||||
}
|
||||
m_graceLockTimer->start();
|
||||
Q_EMIT graceLockedChanged();
|
||||
|
||||
if (!m_checkPass) {
|
||||
m_checkPass = new KCheckPass(AuthenticationMode::Direct, this);
|
||||
m_checkPass->setPassword(password);
|
||||
setupCheckPass();
|
||||
} else {
|
||||
if (!m_checkPass->isReady()) {
|
||||
Q_EMIT failed();
|
||||
return;
|
||||
}
|
||||
m_checkPass->setPassword(password);
|
||||
m_checkPass->startAuth();
|
||||
}
|
||||
}
|
||||
|
||||
void Authenticator::setupCheckPass()
|
||||
{
|
||||
connect(m_checkPass, &KCheckPass::succeeded, this, &Authenticator::succeeded);
|
||||
connect(m_checkPass, &KCheckPass::failed, this, &Authenticator::failed);
|
||||
connect(m_checkPass, &KCheckPass::message, this, &Authenticator::message);
|
||||
connect(m_checkPass, &KCheckPass::error, this, &Authenticator::error);
|
||||
connect(m_checkPass, &KCheckPass::destroyed, this, [this] {
|
||||
m_checkPass = nullptr;
|
||||
});
|
||||
m_checkPass->start();
|
||||
}
|
||||
|
||||
bool Authenticator::isGraceLocked() const
|
||||
{
|
||||
return m_graceLockTimer->isActive();
|
||||
}
|
||||
|
||||
KCheckPass::KCheckPass(AuthenticationMode mode, QObject *parent)
|
||||
: QObject(parent)
|
||||
, m_notifier(nullptr)
|
||||
, m_pid(0)
|
||||
, m_fd(0)
|
||||
, m_mode(mode)
|
||||
{
|
||||
if (mode == AuthenticationMode::Direct) {
|
||||
connect(this, &KCheckPass::succeeded, this, &QObject::deleteLater);
|
||||
connect(this, &KCheckPass::failed, this, &QObject::deleteLater);
|
||||
}
|
||||
}
|
||||
|
||||
KCheckPass::~KCheckPass()
|
||||
{
|
||||
reapVerify();
|
||||
}
|
||||
|
||||
void KCheckPass::start()
|
||||
{
|
||||
int sfd[2];
|
||||
char fdbuf[16];
|
||||
|
||||
if (m_notifier) {
|
||||
return;
|
||||
}
|
||||
if (::socketpair(AF_LOCAL, SOCK_STREAM, 0, sfd)) {
|
||||
cantCheck();
|
||||
return;
|
||||
}
|
||||
if ((m_pid = ::fork()) < 0) {
|
||||
::close(sfd[0]);
|
||||
::close(sfd[1]);
|
||||
cantCheck();
|
||||
return;
|
||||
}
|
||||
if (!m_pid) {
|
||||
::close(sfd[0]);
|
||||
sprintf(fdbuf, "%d", sfd[1]);
|
||||
execlp(QFile::encodeName(QStringLiteral("ccheckpass")).data(), "kcheckpass", "-m", "classic", "-S", fdbuf, (char *)nullptr);
|
||||
_exit(20);
|
||||
}
|
||||
::close(sfd[1]);
|
||||
m_fd = sfd[0];
|
||||
m_notifier = new QSocketNotifier(m_fd, QSocketNotifier::Read, this);
|
||||
connect(m_notifier, &QSocketNotifier::activated, this, &KCheckPass::handleVerify);
|
||||
}
|
||||
|
||||
////// kckeckpass interface code
|
||||
|
||||
int KCheckPass::Reader(void *buf, int count)
|
||||
{
|
||||
int ret, rlen;
|
||||
|
||||
for (rlen = 0; rlen < count;) {
|
||||
dord:
|
||||
ret = ::read(m_fd, (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;
|
||||
}
|
||||
|
||||
bool KCheckPass::GRead(void *buf, int count)
|
||||
{
|
||||
return Reader(buf, count) == count;
|
||||
}
|
||||
|
||||
bool KCheckPass::GWrite(const void *buf, int count)
|
||||
{
|
||||
return ::write(m_fd, buf, count) == count;
|
||||
}
|
||||
|
||||
bool KCheckPass::GSendInt(int val)
|
||||
{
|
||||
return GWrite(&val, sizeof(val));
|
||||
}
|
||||
|
||||
bool KCheckPass::GSendStr(const char *buf)
|
||||
{
|
||||
int len = buf ? ::strlen(buf) + 1 : 0;
|
||||
return GWrite(&len, sizeof(len)) && GWrite(buf, len);
|
||||
}
|
||||
|
||||
bool KCheckPass::GSendArr(int len, const char *buf)
|
||||
{
|
||||
return GWrite(&len, sizeof(len)) && GWrite(buf, len);
|
||||
}
|
||||
|
||||
bool KCheckPass::GRecvInt(int *val)
|
||||
{
|
||||
return GRead(val, sizeof(*val));
|
||||
}
|
||||
|
||||
bool KCheckPass::GRecvArr(char **ret)
|
||||
{
|
||||
int len;
|
||||
char *buf;
|
||||
|
||||
if (!GRecvInt(&len)) {
|
||||
return false;
|
||||
}
|
||||
if (!len) {
|
||||
*ret = nullptr;
|
||||
return true;
|
||||
}
|
||||
if (!(buf = (char *)::malloc(len))) {
|
||||
return false;
|
||||
}
|
||||
*ret = buf;
|
||||
if (GRead(buf, len)) {
|
||||
return true;
|
||||
} else {
|
||||
::free(buf);
|
||||
*ret = nullptr;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void KCheckPass::handleVerify()
|
||||
{
|
||||
m_ready = false;
|
||||
int ret;
|
||||
char *arr;
|
||||
|
||||
if (GRecvInt(&ret)) {
|
||||
switch (ret) {
|
||||
case ConvGetBinary:
|
||||
if (!GRecvArr(&arr)) {
|
||||
break;
|
||||
}
|
||||
// FIXME: not supported
|
||||
cantCheck();
|
||||
if (arr) {
|
||||
::free(arr);
|
||||
}
|
||||
return;
|
||||
case ConvGetNormal:
|
||||
case ConvGetHidden: {
|
||||
if (!GRecvArr(&arr)) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (m_password.isNull()) {
|
||||
GSendStr(nullptr);
|
||||
} else {
|
||||
QByteArray utf8pass = m_password.toUtf8();
|
||||
GSendStr(utf8pass.constData());
|
||||
GSendInt(IsPassword);
|
||||
}
|
||||
|
||||
m_password.clear();
|
||||
|
||||
if (arr) {
|
||||
::free(arr);
|
||||
}
|
||||
return;
|
||||
}
|
||||
case ConvPutInfo:
|
||||
if (!GRecvArr(&arr)) {
|
||||
break;
|
||||
}
|
||||
Q_EMIT message(QString::fromLocal8Bit(arr));
|
||||
::free(arr);
|
||||
return;
|
||||
case ConvPutError:
|
||||
if (!GRecvArr(&arr)) {
|
||||
break;
|
||||
}
|
||||
Q_EMIT error(QString::fromLocal8Bit(arr));
|
||||
::free(arr);
|
||||
return;
|
||||
case ConvPutAuthSucceeded:
|
||||
Q_EMIT succeeded();
|
||||
return;
|
||||
case ConvPutAuthFailed:
|
||||
Q_EMIT failed();
|
||||
return;
|
||||
case ConvPutAuthError:
|
||||
case ConvPutAuthAbort:
|
||||
cantCheck();
|
||||
return;
|
||||
case ConvPutReadyForAuthentication:
|
||||
m_ready = true;
|
||||
if (m_mode == AuthenticationMode::Direct) {
|
||||
::kill(m_pid, SIGUSR1);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (m_mode == AuthenticationMode::Direct) {
|
||||
reapVerify();
|
||||
} else {
|
||||
// we broke, let's restart the greeter
|
||||
// error code 1 will result in a restart through the system
|
||||
qApp->exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
void KCheckPass::reapVerify()
|
||||
{
|
||||
m_notifier->setEnabled(false);
|
||||
m_notifier->deleteLater();
|
||||
m_notifier = nullptr;
|
||||
::close(m_fd);
|
||||
int status;
|
||||
::kill(m_pid, SIGUSR2);
|
||||
while (::waitpid(m_pid, &status, 0) < 0) {
|
||||
if (errno != EINTR) { // This should not happen ...
|
||||
cantCheck();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void KCheckPass::cantCheck()
|
||||
{
|
||||
// TODO: better signal?
|
||||
Q_EMIT failed();
|
||||
}
|
||||
|
||||
void KCheckPass::startAuth()
|
||||
{
|
||||
::kill(m_pid, SIGUSR1);
|
||||
}
|
||||
@ -0,0 +1,111 @@
|
||||
/********************************************************************
|
||||
KSld - the KDE Screenlocker Daemon
|
||||
This file is part of the KDE project.
|
||||
|
||||
Copyright (C) 2014 Martin Gräßlin <mgraesslin@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, see <http://www.gnu.org/licenses/>.
|
||||
*********************************************************************/
|
||||
#ifndef AUTHENTICATOR_H
|
||||
#define AUTHENTICATOR_H
|
||||
|
||||
#include <QObject>
|
||||
|
||||
class QSocketNotifier;
|
||||
class QTimer;
|
||||
class KCheckPass;
|
||||
|
||||
enum class AuthenticationMode {
|
||||
Delayed,
|
||||
Direct,
|
||||
};
|
||||
|
||||
class Authenticator : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(bool graceLocked READ isGraceLocked NOTIFY graceLockedChanged)
|
||||
public:
|
||||
explicit Authenticator(AuthenticationMode mode = AuthenticationMode::Direct, QObject *parent = nullptr);
|
||||
~Authenticator() override;
|
||||
|
||||
bool isGraceLocked() const;
|
||||
|
||||
public Q_SLOTS:
|
||||
void tryUnlock(const QString &password);
|
||||
|
||||
Q_SIGNALS:
|
||||
void failed();
|
||||
void succeeded();
|
||||
void graceLockedChanged();
|
||||
void message(const QString &msg); // don't remove the "msg" param, used in QML!!!
|
||||
void error(const QString &err); // don't remove the "err" param, used in QML!!!
|
||||
|
||||
private:
|
||||
void setupCheckPass();
|
||||
QTimer *m_graceLockTimer;
|
||||
KCheckPass *m_checkPass;
|
||||
};
|
||||
|
||||
class KCheckPass : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit KCheckPass(AuthenticationMode mode, QObject *parent = nullptr);
|
||||
~KCheckPass() override;
|
||||
|
||||
void start();
|
||||
|
||||
bool isReady() const
|
||||
{
|
||||
return m_ready;
|
||||
}
|
||||
|
||||
void setPassword(const QString &password)
|
||||
{
|
||||
m_password = password;
|
||||
}
|
||||
|
||||
void startAuth();
|
||||
|
||||
Q_SIGNALS:
|
||||
void failed();
|
||||
void succeeded();
|
||||
void message(const QString &);
|
||||
void error(const QString &);
|
||||
|
||||
private Q_SLOTS:
|
||||
void handleVerify();
|
||||
|
||||
private:
|
||||
void cantCheck();
|
||||
void reapVerify();
|
||||
// kcheckpass interface
|
||||
int Reader(void *buf, int count);
|
||||
bool GRead(void *buf, int count);
|
||||
bool GWrite(const void *buf, int count);
|
||||
bool GSendInt(int val);
|
||||
bool GSendStr(const char *buf);
|
||||
bool GSendArr(int len, const char *buf);
|
||||
bool GRecvInt(int *val);
|
||||
bool GRecvArr(char **buf);
|
||||
|
||||
QString m_password;
|
||||
QSocketNotifier *m_notifier;
|
||||
int m_pid;
|
||||
int m_fd;
|
||||
bool m_ready = false;
|
||||
AuthenticationMode m_mode;
|
||||
};
|
||||
|
||||
#endif
|
||||
@ -0,0 +1,278 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2003 Lubos Lunak <l.lunak@kde.org>
|
||||
|
||||
SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
//#ifdef don't do this, this file is supposed to be included
|
||||
//#define multiple times
|
||||
|
||||
#include <QtGlobal>
|
||||
|
||||
/* Usage:
|
||||
|
||||
If you get compile errors caused by X11 includes (the line
|
||||
where first error appears contains word like None, Unsorted,
|
||||
Below, etc.), put #include <fixx11h.h> in the .cpp file
|
||||
(not .h file!) between the place where X11 headers are
|
||||
included and the place where the file with compile
|
||||
error is included (or the place where the compile error
|
||||
in the .cpp file occurs).
|
||||
|
||||
This file remaps X11 #defines to const variables or
|
||||
inline functions. The side effect may be that these
|
||||
symbols may now refer to different variables
|
||||
(e.g. if X11 #defined NoButton, after this file
|
||||
is included NoButton would no longer be X11's
|
||||
NoButton, but Qt::NoButton instead). At this time,
|
||||
there's no conflict known that could cause problems.
|
||||
|
||||
The original X11 symbols are still accessible
|
||||
(e.g. for None) as X::None, XNone, and also still
|
||||
None, unless name lookup finds different None
|
||||
first (in the current class, etc.)
|
||||
|
||||
Use 'Unsorted', 'Bool' and 'index' as templates.
|
||||
|
||||
*/
|
||||
|
||||
namespace X
|
||||
{
|
||||
// template --->
|
||||
// Affects: Should be without side effects.
|
||||
#ifdef Unsorted
|
||||
#ifndef FIXX11H_Unsorted
|
||||
#define FIXX11H_Unsorted
|
||||
const int XUnsorted = Unsorted;
|
||||
#undef Unsorted
|
||||
const int Unsorted = XUnsorted;
|
||||
#endif
|
||||
#undef Unsorted
|
||||
#endif
|
||||
// template <---
|
||||
|
||||
// Affects: Should be without side effects.
|
||||
#ifdef None
|
||||
#ifndef FIXX11H_None
|
||||
#define FIXX11H_None
|
||||
const XID XNone = None;
|
||||
#undef None
|
||||
const XID None = XNone;
|
||||
#endif
|
||||
#undef None
|
||||
#endif
|
||||
|
||||
// template --->
|
||||
// Affects: Should be without side effects.
|
||||
#ifdef Bool
|
||||
#ifndef FIXX11H_Bool
|
||||
#define FIXX11H_Bool
|
||||
#ifdef _XTYPEDEF_BOOL /* Xdefs.h has typedef'ed Bool already */
|
||||
#undef Bool
|
||||
#else
|
||||
typedef Bool XBool;
|
||||
#undef Bool
|
||||
typedef XBool Bool;
|
||||
#endif
|
||||
#endif
|
||||
#undef Bool
|
||||
#define _XTYPEDEF_BOOL
|
||||
#endif
|
||||
// template <---
|
||||
|
||||
// Affects: Should be without side effects.
|
||||
#ifdef KeyPress
|
||||
#ifndef FIXX11H_KeyPress
|
||||
#define FIXX11H_KeyPress
|
||||
const int XKeyPress = KeyPress;
|
||||
#undef KeyPress
|
||||
const int KeyPress = XKeyPress;
|
||||
#endif
|
||||
#undef KeyPress
|
||||
#endif
|
||||
|
||||
// Affects: Should be without side effects.
|
||||
#ifdef KeyRelease
|
||||
#ifndef FIXX11H_KeyRelease
|
||||
#define FIXX11H_KeyRelease
|
||||
const int XKeyRelease = KeyRelease;
|
||||
#undef KeyRelease
|
||||
const int KeyRelease = XKeyRelease;
|
||||
#endif
|
||||
#undef KeyRelease
|
||||
#endif
|
||||
|
||||
// Affects: Should be without side effects.
|
||||
#ifdef Above
|
||||
#ifndef FIXX11H_Above
|
||||
#define FIXX11H_Above
|
||||
const int XAbove = Above;
|
||||
#undef Above
|
||||
const int Above = XAbove;
|
||||
#endif
|
||||
#undef Above
|
||||
#endif
|
||||
|
||||
// Affects: Should be without side effects.
|
||||
#ifdef Below
|
||||
#ifndef FIXX11H_Below
|
||||
#define FIXX11H_Below
|
||||
const int XBelow = Below;
|
||||
#undef Below
|
||||
const int Below = XBelow;
|
||||
#endif
|
||||
#undef Below
|
||||
#endif
|
||||
|
||||
// Affects: Should be without side effects.
|
||||
#ifdef FocusIn
|
||||
#ifndef FIXX11H_FocusIn
|
||||
#define FIXX11H_FocusIn
|
||||
const int XFocusIn = FocusIn;
|
||||
#undef FocusIn
|
||||
const int FocusIn = XFocusIn;
|
||||
#endif
|
||||
#undef FocusIn
|
||||
#endif
|
||||
|
||||
// Affects: Should be without side effects.
|
||||
#ifdef FocusOut
|
||||
#ifndef FIXX11H_FocusOut
|
||||
#define FIXX11H_FocusOut
|
||||
const int XFocusOut = FocusOut;
|
||||
#undef FocusOut
|
||||
const int FocusOut = XFocusOut;
|
||||
#endif
|
||||
#undef FocusOut
|
||||
#endif
|
||||
|
||||
// Affects: Should be without side effects.
|
||||
#ifdef Always
|
||||
#ifndef FIXX11H_Always
|
||||
#define FIXX11H_Always
|
||||
const int XAlways = Always;
|
||||
#undef Always
|
||||
const int Always = XAlways;
|
||||
#endif
|
||||
#undef Always
|
||||
#endif
|
||||
|
||||
// Affects: Should be without side effects.
|
||||
#ifdef Expose
|
||||
#ifndef FIXX11H_Expose
|
||||
#define FIXX11H_Expose
|
||||
const int XExpose = Expose;
|
||||
#undef Expose
|
||||
const int Expose = XExpose;
|
||||
#endif
|
||||
#undef Expose
|
||||
#endif
|
||||
|
||||
// Affects: Should be without side effects.
|
||||
#ifdef Success
|
||||
#ifndef FIXX11H_Success
|
||||
#define FIXX11H_Success
|
||||
const int XSuccess = Success;
|
||||
#undef Success
|
||||
const int Success = XSuccess;
|
||||
#endif
|
||||
#undef Success
|
||||
#endif
|
||||
|
||||
// Affects: Should be without side effects.
|
||||
#ifdef GrayScale
|
||||
#ifndef FIXX11H_GrayScale
|
||||
#define FIXX11H_GrayScale
|
||||
const int XGrayScale = GrayScale;
|
||||
#undef GrayScale
|
||||
const int GrayScale = XGrayScale;
|
||||
#endif
|
||||
#undef GrayScale
|
||||
#endif
|
||||
|
||||
// Affects: Should be without side effects.
|
||||
#ifdef Status
|
||||
#ifndef FIXX11H_Status
|
||||
#define FIXX11H_Status
|
||||
typedef Status XStatus;
|
||||
#undef Status
|
||||
typedef XStatus Status;
|
||||
#endif
|
||||
#undef Status
|
||||
#endif
|
||||
|
||||
// template --->
|
||||
// Affects: Should be without side effects.
|
||||
#ifdef CursorShape
|
||||
#ifndef FIXX11H_CursorShape
|
||||
#define FIXX11H_CursorShape
|
||||
const int XCursorShape = CursorShape;
|
||||
#undef CursorShape
|
||||
const int CursorShape = XCursorShape;
|
||||
#endif
|
||||
#undef CursorShape
|
||||
#endif
|
||||
// template <---
|
||||
|
||||
// template --->
|
||||
// Affects: Should be without side effects.
|
||||
#ifdef FontChange
|
||||
#ifndef FIXX11H_FontChange
|
||||
#define FIXX11H_FontChange
|
||||
const int XFontChange = FontChange;
|
||||
#undef FontChange
|
||||
const int FontChange = XFontChange;
|
||||
#endif
|
||||
#undef FontChange
|
||||
#endif
|
||||
// template <---
|
||||
|
||||
// Affects: Should be without side effects.
|
||||
#ifdef NormalState
|
||||
#ifndef FIXX11H_NormalState
|
||||
#define FIXX11H_NormalState
|
||||
const int XNormalState = NormalState;
|
||||
#undef NormalState
|
||||
const int NormalState = XNormalState;
|
||||
#endif
|
||||
#undef NormalState
|
||||
#endif
|
||||
|
||||
// template --->
|
||||
// Affects: Should be without side effects.
|
||||
#ifdef index
|
||||
#ifndef FIXX11H_index
|
||||
#define FIXX11H_index
|
||||
inline const char *Xindex(const char *s, int c)
|
||||
{
|
||||
return index(s, c);
|
||||
}
|
||||
#undef index
|
||||
inline const char *index(const char *s, int c)
|
||||
{
|
||||
return Xindex(s, c);
|
||||
}
|
||||
#endif
|
||||
#undef index
|
||||
#endif
|
||||
// template <---
|
||||
|
||||
#ifdef rindex
|
||||
// Affects: Should be without side effects.
|
||||
#ifndef FIXX11H_rindex
|
||||
#define FIXX11H_rindex
|
||||
inline const char *Xrindex(const char *s, int c)
|
||||
{
|
||||
return rindex(s, c);
|
||||
}
|
||||
#undef rindex
|
||||
inline const char *rindex(const char *s, int c)
|
||||
{
|
||||
return Xrindex(s, c);
|
||||
}
|
||||
#endif
|
||||
#undef rindex
|
||||
#endif
|
||||
}
|
||||
|
||||
using namespace X;
|
||||
@ -0,0 +1,75 @@
|
||||
/*****************************************************************
|
||||
*
|
||||
*· 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_ENUMS_H
|
||||
#define KCHECKPASS_ENUMS_H
|
||||
|
||||
#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,
|
||||
ConvPutAuthSucceeded,
|
||||
ConvPutAuthFailed,
|
||||
ConvPutAuthError,
|
||||
ConvPutAuthAbort,
|
||||
ConvPutReadyForAuthentication,
|
||||
} ConvRequest;
|
||||
|
||||
/* these must match the defs in kgreeterplugin.h */
|
||||
typedef enum {
|
||||
IsUser = 1, /* unused in kcheckpass */
|
||||
IsPassword = 2,
|
||||
} DataTag;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* KCHECKPASS_ENUMS_H */
|
||||
@ -0,0 +1,9 @@
|
||||
#include "application.h"
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
Application app(argc, argv);
|
||||
app.setQuitOnLastWindowClosed(false);
|
||||
app.initialViewSetup();
|
||||
return app.exec();
|
||||
}
|
||||
@ -0,0 +1,6 @@
|
||||
<RCC>
|
||||
<qresource prefix="/">
|
||||
<file>qml/LockScreen.qml</file>
|
||||
<file>qml/Greeter.qml</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
@ -0,0 +1,5 @@
|
||||
import QtQuick 2.0
|
||||
|
||||
Item {
|
||||
|
||||
}
|
||||
@ -0,0 +1,117 @@
|
||||
import QtQuick 2.0
|
||||
import QtQuick.Window 2.12
|
||||
import QtQuick.Controls 2.12
|
||||
import QtQuick.Layouts 1.12
|
||||
import QtGraphicalEffects 1.0
|
||||
|
||||
import FishUI 1.0 as FishUI
|
||||
|
||||
Item {
|
||||
id: root
|
||||
|
||||
property alias notification: message.text
|
||||
|
||||
Image {
|
||||
id: wallpaperImage
|
||||
anchors.fill: parent
|
||||
source: "file://" + wallpaper.path
|
||||
sourceSize: Qt.size(width * Screen.devicePixelRatio,
|
||||
height * Screen.devicePixelRatio)
|
||||
fillMode: Image.PreserveAspectCrop
|
||||
asynchronous: false
|
||||
clip: true
|
||||
cache: false
|
||||
smooth: true
|
||||
}
|
||||
|
||||
FastBlur {
|
||||
id: wallpaperBlur
|
||||
anchors.fill: parent
|
||||
radius: 64
|
||||
source: wallpaperImage
|
||||
cached: true
|
||||
visible: true
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
anchors.fill: parent
|
||||
spacing: FishUI.Units.largeSpacing * 1.5
|
||||
|
||||
Item {
|
||||
Layout.fillHeight: true
|
||||
}
|
||||
|
||||
Label {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
text: "cutefish"
|
||||
color: "white"
|
||||
}
|
||||
|
||||
TextField {
|
||||
id: password
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
Layout.preferredHeight: 40
|
||||
Layout.preferredWidth: 240
|
||||
placeholderText: qsTr("Password")
|
||||
focus: true
|
||||
|
||||
echoMode: TextInput.Password
|
||||
|
||||
background: Rectangle {
|
||||
color: "white"
|
||||
opacity: 0.4
|
||||
radius: FishUI.Theme.bigRadius
|
||||
}
|
||||
|
||||
Keys.onEnterPressed: authenticator.tryUnlock(password.text)
|
||||
Keys.onReturnPressed: authenticator.tryUnlock(password.text)
|
||||
Keys.onEscapePressed: password.text = ""
|
||||
}
|
||||
|
||||
Button {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
Layout.preferredHeight: 40
|
||||
Layout.preferredWidth: 240
|
||||
text: qsTr("Unlock")
|
||||
onClicked: authenticator.tryUnlock(password.text)
|
||||
}
|
||||
|
||||
Label {
|
||||
id: message
|
||||
text: ""
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
font.bold: true
|
||||
color: "white"
|
||||
Behavior on opacity {
|
||||
NumberAnimation {
|
||||
duration: 250
|
||||
}
|
||||
}
|
||||
opacity: text == "" ? 0 : 1
|
||||
}
|
||||
|
||||
Item {
|
||||
Layout.fillHeight: true
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: authenticator
|
||||
function onFailed() {
|
||||
root.notification = qsTr("Unlocking failed")
|
||||
}
|
||||
function onGraceLockedChanged() {
|
||||
if (!authenticator.graceLocked) {
|
||||
root.notification = ""
|
||||
password.selectAll()
|
||||
password.focus = true
|
||||
}
|
||||
}
|
||||
function onMessage(text) {
|
||||
root.notification = text
|
||||
}
|
||||
function onError(text) {
|
||||
root.notification = text
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,24 @@
|
||||
#include "wallpaper.h"
|
||||
|
||||
Wallpaper::Wallpaper(QObject *parent)
|
||||
: QObject(parent)
|
||||
, m_interface("org.cutefish.Settings",
|
||||
"/Theme", "org.cutefish.Theme",
|
||||
QDBusConnection::sessionBus(), this)
|
||||
{
|
||||
if (m_interface.isValid()) {
|
||||
connect(&m_interface, SIGNAL(wallpaperChanged(QString)), this, SLOT(onPathChanged(QString)));
|
||||
}
|
||||
}
|
||||
|
||||
QString Wallpaper::path() const
|
||||
{
|
||||
return m_interface.property("wallpaper").toString();
|
||||
}
|
||||
|
||||
void Wallpaper::onPathChanged(QString path)
|
||||
{
|
||||
Q_UNUSED(path);
|
||||
|
||||
emit pathChanged();
|
||||
}
|
||||
@ -0,0 +1,28 @@
|
||||
#ifndef WALLPAPER_H
|
||||
#define WALLPAPER_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QDBusInterface>
|
||||
|
||||
class Wallpaper : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(QString path READ path NOTIFY pathChanged)
|
||||
|
||||
public:
|
||||
explicit Wallpaper(QObject *parent = nullptr);
|
||||
|
||||
QString path() const;
|
||||
|
||||
signals:
|
||||
void pathChanged();
|
||||
|
||||
private slots:
|
||||
void onPathChanged(QString path);
|
||||
|
||||
private:
|
||||
QDBusInterface m_interface;
|
||||
QString m_wallpaper;
|
||||
};
|
||||
|
||||
#endif // WALLPAPER_H
|
||||
Loading…
Reference in New Issue