mirror of
https://github.com/boltgolt/howdy.git
synced 2024-09-19 09:51:19 +02:00
Finally move fully to the compiled .so
This commit is contained in:
parent
943f1e14e2
commit
1dc56b1705
8 changed files with 48 additions and 149 deletions
|
@ -2,14 +2,14 @@ Source: howdy
|
|||
Section: misc
|
||||
Priority: optional
|
||||
Standards-Version: 3.9.7
|
||||
Build-Depends: python, devscripts, dh-make, debhelper, fakeroot, python3, python3-pip, python3-setuptools, python3-wheel, ninja-build, meson, libpam0g-dev, libboost-all-dev
|
||||
Build-Depends: devscripts, git, dh-make, debhelper, fakeroot, python3, python3-pip, python3-setuptools, python3-wheel, ninja-build, meson, libpam0g-dev, libboost-all-dev, pkg-config, libevdev-dev, libinih-dev
|
||||
Maintainer: boltgolt <boltgolt@gmail.com>
|
||||
Vcs-Git: https://github.com/boltgolt/howdy
|
||||
|
||||
Package: howdy
|
||||
Homepage: https://github.com/boltgolt/howdy
|
||||
Architecture: amd64
|
||||
Depends: ${misc:Depends}, libc6, libgcc-s1, libpam0g, libstdc++6, curl | wget, python3, python3-pip, python3-dev, python3-setuptools, python3-numpy, python-opencv, libopencv-dev, cmake
|
||||
Depends: ${misc:Depends}, libc6, libgcc-s1, libpam0g, libstdc++6, curl | wget, python3, python3-pip, python3-dev, python3-setuptools, python3-numpy, python-opencv | python3-opencv, libopencv-dev, cmake, libinih-dev
|
||||
Recommends: libatlas-base-dev | libopenblas-dev | liblapack-dev, howdy-gtk
|
||||
Suggests: nvidia-cuda-dev (>= 7.5)
|
||||
Description: Howdy: Windows Hello style authentication for Linux.
|
||||
|
|
|
@ -1,16 +1,17 @@
|
|||
src/cli/. lib/security/howdy/cli
|
||||
src/dlib-data/. lib/security/howdy/dlib-data
|
||||
src/locales/. lib/security/howdy/locales
|
||||
src/recorders/. lib/security/howdy/recorders
|
||||
src/rubberstamps/. lib/security/howdy/rubberstamps
|
||||
src/cli.py lib/security/howdy
|
||||
src/compare.py lib/security/howdy
|
||||
src/config.ini lib/security/howdy
|
||||
src/i18n.py lib/security/howdy
|
||||
src/logo.png lib/security/howdy
|
||||
src/pam.py lib/security/howdy
|
||||
src/snapshot.py lib/security/howdy
|
||||
|
||||
build/pam_howdy.so lib/security/howdy
|
||||
|
||||
src/dlib-data/. etc/howdy/dlib-data
|
||||
src/config.ini etc/howdy
|
||||
|
||||
src/autocomplete/. usr/share/bash-completion/completions
|
||||
src/pam-config/. /usr/share/pam-configs
|
||||
build/libpam_howdy.so lib/security/howdy
|
||||
|
|
|
@ -11,7 +11,7 @@ build:
|
|||
# Create build dir
|
||||
meson setup -Dinih:with_INIReader=true build src/pam
|
||||
# Compile shared object
|
||||
meson compile -C build
|
||||
ninja -C build
|
||||
|
||||
clean:
|
||||
# Delete mason build directory
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
# Compare incomming video with known faces
|
||||
#!/usr/bin/env python3
|
||||
# Compare incoming video with known faces
|
||||
# Running in a local python instance to get around PATH issues
|
||||
|
||||
# Import time so we can start timing asap
|
||||
|
@ -23,11 +24,14 @@ import snapshot
|
|||
import numpy as np
|
||||
import _thread as thread
|
||||
|
||||
from i18n import _
|
||||
# Allow imports from the local howdy folder
|
||||
sys.path.append('/lib/security/howdy')
|
||||
|
||||
from recorders.video_capture import VideoCapture
|
||||
from i18n import _
|
||||
|
||||
def exit(code=None):
|
||||
"""Exit while closeing howdy-gtk properly"""
|
||||
"""Exit while closing howdy-gtk properly"""
|
||||
global gtk_proc
|
||||
|
||||
# Exit the auth ui process if there is one
|
||||
|
@ -99,8 +103,8 @@ def send_to_ui(type, message):
|
|||
if len(sys.argv) < 2:
|
||||
exit(12)
|
||||
|
||||
# Get the absolute path to the current directory
|
||||
PATH = os.path.abspath(__file__ + "/..")
|
||||
# Get the absolute path to the config directory
|
||||
PATH = "/etc/howdy"
|
||||
|
||||
# The username of the user being authenticated
|
||||
user = sys.argv[1]
|
||||
|
@ -110,7 +114,7 @@ models = []
|
|||
encodings = []
|
||||
# Amount of ignored 100% black frames
|
||||
black_tries = 0
|
||||
# Amount of ingnored dark frames
|
||||
# Amount of ignored dark frames
|
||||
dark_tries = 0
|
||||
# Total amount of frames captured
|
||||
frames = 0
|
||||
|
@ -229,7 +233,7 @@ while True:
|
|||
# Show it in the ui as subtext
|
||||
send_to_ui("S", ui_subtext)
|
||||
|
||||
# Stop if we've exceded the time limit
|
||||
# Stop if we've exceeded the time limit
|
||||
if time.time() - timings["fr"] > timeout:
|
||||
# Create a timeout snapshot if enabled
|
||||
if capture_failed:
|
||||
|
@ -268,6 +272,7 @@ while True:
|
|||
|
||||
dark_running_total += darkness
|
||||
valid_frames += 1
|
||||
|
||||
# If the image exceeds darkness threshold due to subject distance,
|
||||
# skip to the next frame
|
||||
if (darkness > dark_threshold):
|
||||
|
@ -279,6 +284,7 @@ while True:
|
|||
# Apply that factor to the frame
|
||||
frame = cv2.resize(frame, None, fx=scaling_factor, fy=scaling_factor, interpolation=cv2.INTER_AREA)
|
||||
gsframe = cv2.resize(gsframe, None, fx=scaling_factor, fy=scaling_factor, interpolation=cv2.INTER_AREA)
|
||||
|
||||
# If camera is configured to rotate = 1, check portrait in addition to landscape
|
||||
if rotate == 1:
|
||||
if frames % 3 == 1:
|
||||
|
@ -287,6 +293,7 @@ while True:
|
|||
if frames % 3 == 2:
|
||||
frame = cv2.rotate(frame, cv2.ROTATE_90_CLOCKWISE)
|
||||
gsframe = cv2.rotate(gsframe, cv2.ROTATE_90_CLOCKWISE)
|
||||
|
||||
# If camera is configured to rotate = 2, check portrait orientation
|
||||
elif rotate == 2:
|
||||
if frames % 2 == 0:
|
||||
|
@ -295,6 +302,7 @@ while True:
|
|||
else:
|
||||
frame = cv2.rotate(frame, cv2.ROTATE_90_CLOCKWISE)
|
||||
gsframe = cv2.rotate(gsframe, cv2.ROTATE_90_CLOCKWISE)
|
||||
|
||||
# Get all faces from that frame as encodings
|
||||
# Upsamples 1 time
|
||||
face_locations = face_detector(gsframe, 1)
|
||||
|
|
|
@ -3,4 +3,4 @@ Default: yes
|
|||
Priority: 512
|
||||
Auth-Type: Primary
|
||||
Auth:
|
||||
[success=end default=ignore] pam_python.so /lib/security/howdy/pam.py
|
||||
[success=end default=ignore] /lib/security/howdy/pam_howdy.so
|
||||
|
|
130
howdy/src/pam.py
130
howdy/src/pam.py
|
@ -1,130 +0,0 @@
|
|||
# PAM interface in python, launches compare.py
|
||||
|
||||
# Import required modules
|
||||
import subprocess
|
||||
import os
|
||||
import glob
|
||||
import syslog
|
||||
|
||||
# pam-python is running python 2, so we use the old module here
|
||||
import ConfigParser
|
||||
|
||||
# Read config from disk
|
||||
config = ConfigParser.ConfigParser()
|
||||
config.read(os.path.dirname(os.path.abspath(__file__)) + "/config.ini")
|
||||
|
||||
|
||||
def doAuth(pamh):
|
||||
"""Starts authentication in a separate process"""
|
||||
|
||||
# Abort if Howdy is disabled
|
||||
if config.getboolean("core", "disabled"):
|
||||
return pamh.PAM_AUTHINFO_UNAVAIL
|
||||
|
||||
# Abort if we're in a remote SSH env
|
||||
if config.getboolean("core", "ignore_ssh"):
|
||||
if "SSH_CONNECTION" in os.environ or "SSH_CLIENT" in os.environ or "SSHD_OPTS" in os.environ:
|
||||
return pamh.PAM_AUTHINFO_UNAVAIL
|
||||
|
||||
# Abort if lid is closed
|
||||
if config.getboolean("core", "ignore_closed_lid"):
|
||||
if any("closed" in open(f).read() for f in glob.glob("/proc/acpi/button/lid/*/state")):
|
||||
return pamh.PAM_AUTHINFO_UNAVAIL
|
||||
|
||||
# Abort if the video device does not exist
|
||||
if not os.path.exists(config.get("video", "device_path")):
|
||||
if config.getboolean("video", "warn_no_device"):
|
||||
print("Camera path is not configured correctly, please edit the 'device_path' config value.")
|
||||
return pamh.PAM_AUTHINFO_UNAVAIL
|
||||
|
||||
# Set up syslog
|
||||
syslog.openlog("[HOWDY]", 0, syslog.LOG_AUTH)
|
||||
|
||||
# Alert the user that we are doing face detection
|
||||
if config.getboolean("core", "detection_notice"):
|
||||
pamh.conversation(pamh.Message(pamh.PAM_TEXT_INFO, "Attempting face detection"))
|
||||
|
||||
syslog.syslog(syslog.LOG_INFO, "Attempting facial authentication for user " + pamh.get_user())
|
||||
|
||||
# Run compare as python3 subprocess to circumvent python version and import issues
|
||||
status = subprocess.call(["/usr/bin/python3", os.path.dirname(os.path.abspath(__file__)) + "/compare.py", pamh.get_user()])
|
||||
|
||||
# Status 10 means we couldn't find any face models
|
||||
if status == 10:
|
||||
if not config.getboolean("core", "suppress_unknown"):
|
||||
pamh.conversation(pamh.Message(pamh.PAM_ERROR_MSG, "No face model known"))
|
||||
|
||||
syslog.syslog(syslog.LOG_NOTICE, "Failure, no face model known")
|
||||
syslog.closelog()
|
||||
return pamh.PAM_USER_UNKNOWN
|
||||
|
||||
# Status 11 means we exceded the maximum retry count
|
||||
elif status == 11:
|
||||
if config.getboolean("core", "timeout_notice"):
|
||||
pamh.conversation(pamh.Message(pamh.PAM_ERROR_MSG, "Face detection timeout reached"))
|
||||
syslog.syslog(syslog.LOG_INFO, "Failure, timeout reached")
|
||||
syslog.closelog()
|
||||
return pamh.PAM_AUTH_ERR
|
||||
|
||||
# Status 12 means we aborted
|
||||
elif status == 12:
|
||||
syslog.syslog(syslog.LOG_INFO, "Failure, general abort")
|
||||
syslog.closelog()
|
||||
return pamh.PAM_AUTH_ERR
|
||||
|
||||
# Status 13 means the image was too dark
|
||||
elif status == 13:
|
||||
pamh.conversation(pamh.Message(pamh.PAM_ERROR_MSG, "Face detection image too dark"))
|
||||
syslog.syslog(syslog.LOG_INFO, "Failure, image too dark")
|
||||
syslog.closelog()
|
||||
return pamh.PAM_AUTH_ERR
|
||||
|
||||
# Status 14 means a rubberstamp could not be given
|
||||
elif status == 14:
|
||||
pamh.conversation(pamh.Message(pamh.PAM_ERROR_MSG, "Rubberstamp denied"))
|
||||
syslog.syslog(syslog.LOG_INFO, "Failure, rubberstamp did not succeed")
|
||||
syslog.closelog()
|
||||
return pamh.PAM_AUTH_ERR
|
||||
|
||||
# Status 1 is probably a python crash
|
||||
elif status == 1:
|
||||
pamh.conversation(pamh.Message(pamh.PAM_ERROR_MSG, "Howdy encountered error, check stderr"))
|
||||
syslog.syslog(syslog.LOG_INFO, "Failure, process crashed while authenticating")
|
||||
syslog.closelog()
|
||||
return pamh.PAM_SYSTEM_ERR
|
||||
|
||||
# Status 0 is a successful exit
|
||||
elif status == 0:
|
||||
# Show the success message if it isn't suppressed
|
||||
if not config.getboolean("core", "no_confirmation"):
|
||||
pamh.conversation(pamh.Message(pamh.PAM_TEXT_INFO, "Identified face as " + pamh.get_user()))
|
||||
|
||||
syslog.syslog(syslog.LOG_INFO, "Login approved")
|
||||
syslog.closelog()
|
||||
return pamh.PAM_SUCCESS
|
||||
|
||||
# Otherwise, we can't describe what happened but it wasn't successful
|
||||
pamh.conversation(pamh.Message(pamh.PAM_ERROR_MSG, "Unknown error: " + str(status)))
|
||||
syslog.syslog(syslog.LOG_INFO, "Failure, unknown error" + str(status))
|
||||
syslog.closelog()
|
||||
return pamh.PAM_SYSTEM_ERR
|
||||
|
||||
|
||||
def pam_sm_authenticate(pamh, flags, args):
|
||||
"""Called by PAM when the user wants to authenticate, in sudo for example"""
|
||||
return doAuth(pamh)
|
||||
|
||||
|
||||
def pam_sm_open_session(pamh, flags, args):
|
||||
"""Called when starting a session, such as su"""
|
||||
return doAuth(pamh)
|
||||
|
||||
|
||||
def pam_sm_close_session(pamh, flags, argv):
|
||||
"""We don't need to clean anyting up at the end of a session, so returns true"""
|
||||
return pamh.PAM_SUCCESS
|
||||
|
||||
|
||||
def pam_sm_setcred(pamh, flags, argv):
|
||||
"""We don't need set any credentials, so returns true"""
|
||||
return pamh.PAM_SUCCESS
|
|
@ -193,7 +193,7 @@ auto check_enabled(const INIReader &config) -> int {
|
|||
*/
|
||||
auto identify(pam_handle_t *pamh, int flags, int argc, const char **argv,
|
||||
bool auth_tok) -> int {
|
||||
INIReader config("/lib/security/howdy/config.ini");
|
||||
INIReader config("/etc/howdy/config.ini");
|
||||
openlog("pam_howdy", 0, LOG_AUTHPRIV);
|
||||
|
||||
// Error out if we could not read the config file
|
||||
|
@ -343,6 +343,27 @@ auto identify(pam_handle_t *pamh, int flags, int argc, const char **argv,
|
|||
// The compare process has finished its execution
|
||||
child_task.stop(false);
|
||||
|
||||
// Get python process status code
|
||||
int status = child_task.get();
|
||||
|
||||
// If python process ran into a timeout
|
||||
// Do not send enter presses or terminate the PAM function, as the user might still be typing their password
|
||||
if (status == CompareError::TIMEOUT_ACTIVE) {
|
||||
// Wait for the password to be typed
|
||||
pass_task.stop(false);
|
||||
|
||||
char *password = nullptr;
|
||||
std::tie(pam_res, password) = pass_task.get();
|
||||
|
||||
if (pam_res != PAM_SUCCESS) {
|
||||
return pam_res;
|
||||
}
|
||||
|
||||
// The password has been entered, we are passing it to PAM stack
|
||||
return PAM_IGNORE;
|
||||
}
|
||||
|
||||
|
||||
// We want to stop the password prompt, either by canceling the thread when
|
||||
// workaround is set to "native", or by emulating "Enter" input with
|
||||
// "input"
|
||||
|
@ -391,8 +412,6 @@ auto identify(pam_handle_t *pamh, int flags, int argc, const char **argv,
|
|||
pass_task.stop(false);
|
||||
}
|
||||
|
||||
int status = child_task.get();
|
||||
|
||||
return howdy_status(username, status, config, conv_function);
|
||||
}
|
||||
|
||||
|
|
|
@ -12,7 +12,8 @@ enum CompareError : int {
|
|||
NO_FACE_MODEL = 10,
|
||||
TIMEOUT_REACHED = 11,
|
||||
ABORT = 12,
|
||||
TOO_DARK = 13
|
||||
TOO_DARK = 13,
|
||||
TIMEOUT_ACTIVE = 2816,
|
||||
};
|
||||
|
||||
inline auto get_workaround(const std::string &workaround) -> Workaround {
|
||||
|
|
Loading…
Reference in a new issue