From 37ce3d61c955b9dfcd12a06265044ef1b21a9dbf Mon Sep 17 00:00:00 2001 From: boltgolt Date: Tue, 13 Mar 2018 21:17:49 +0100 Subject: [PATCH 01/26] Fixed installer permissions and cli location, ignoring remote sessions --- config.ini | 5 +++++ installer.py | 9 +++++++-- pam.py | 14 ++++++++++++++ 3 files changed, 26 insertions(+), 2 deletions(-) diff --git a/config.ini b/config.ini index 8e92636..3076f96 100644 --- a/config.ini +++ b/config.ini @@ -6,6 +6,11 @@ no_confirmation = false # show an error but fail silently suppress_unknown = false +# Auto dismiss lock screen on confirmation +# Will run loginctl unlock-sessions after every auth +# Expirimental, can behave incorrectly on some systems +dismiss_lockscreen = false + [video] # The certainty of the detected face belonging to the user of the account # On a scale from 1 to 10, values above 5 are not recommended diff --git a/installer.py b/installer.py index 5b4374c..5ec2fd1 100644 --- a/installer.py +++ b/installer.py @@ -141,9 +141,14 @@ for line in fileinput.input(["/lib/security/howdy/config.ini"], inplace = 1): # Secure the howdy folder handleStatus(subprocess.call(["chmod 600 -R /lib/security/howdy/"], shell=True)) +# Allow anyone to execute the python CLI +handleStatus(subprocess.call(["chmod 755 /lib/security/howdy"], shell=True)) +handleStatus(subprocess.call(["chmod 744 /lib/security/howdy/cli.py"], shell=True)) +handleStatus(subprocess.call(["chmod 744 -R /lib/security/howdy/cli"], shell=True)) + # Make the CLI executable as howdy -handleStatus(subprocess.call(["ln -s /lib/security/howdy/cli.py /usr/bin/howdy"], shell=True)) -handleStatus(subprocess.call(["chmod +x /usr/bin/howdy"], shell=True)) +handleStatus(subprocess.call(["ln -s /lib/security/howdy/cli.py /usr/local/bin/howdy"], shell=True)) +handleStatus(subprocess.call(["chmod +x /usr/local/bin/howdy"], shell=True)) # Install the command autocomplete, don't error on failure subprocess.call(["sudo cp /lib/security/howdy/autocomplete.sh /etc/bash_completion.d/howdy"], shell=True) diff --git a/pam.py b/pam.py index bd6cf06..27977ab 100644 --- a/pam.py +++ b/pam.py @@ -4,6 +4,8 @@ import subprocess import sys import os +import threading +import time # pam-python is running python 2, so we use the old module here import ConfigParser @@ -15,6 +17,11 @@ config.read(os.path.dirname(os.path.abspath(__file__)) + "/config.ini") def doAuth(pamh): """Start authentication in a seperate process""" + # Abort if the session is remote + if pamh.rhost is not None or "SSH_CONNECTION" in os.environ: + pamh.conversation(pamh.Message(pamh.PAM_TEXT_INFO, "Skipping face recognition for remote session")) + return pamh.PAM_AUTH_ERR + # Run compare as python3 subprocess to circumvent python version and import issues status = subprocess.call(["python3", os.path.dirname(os.path.abspath(__file__)) + "/compare.py", pamh.get_user()]) @@ -29,8 +36,15 @@ def doAuth(pamh): return pamh.PAM_AUTH_ERR # Status 0 is a successful exit if status == 0: + # Show the success message if it isn't suppressed if config.get("core", "no_confirmation") != "true": pamh.conversation(pamh.Message(pamh.PAM_TEXT_INFO, "Identified face as " + pamh.get_user())) + + # Try to dismiss the lock screen if enabled + if config.get("core", "dismiss_lockscreen") == "true": + # Run it as root with a timeout of 1s, and never ask for a password through the UI + subprocess.Popen(["sudo", "timeout", "1", "loginctl", "unlock-sessions", "--no-ask-password"]) + return pamh.PAM_SUCCESS # Otherwise, we can't discribe what happend but it wasn't successful From 5bd312a19de18630698ac7bc14e222ce8ad6f640 Mon Sep 17 00:00:00 2001 From: boltgolt Date: Thu, 5 Apr 2018 21:12:36 +0200 Subject: [PATCH 02/26] Structuring as a debian package --- bin/howdy | 2 + debian/changelog | 5 ++ debian/control | 11 +++ installer.py => debian/postinst | 134 ++++++++++++++------------------ uninstall.py => debian/prerm | 2 + debian/rules | 1 + pam.py | 7 -- 7 files changed, 79 insertions(+), 83 deletions(-) create mode 100755 bin/howdy create mode 100644 debian/changelog create mode 100644 debian/control rename installer.py => debian/postinst (63%) mode change 100644 => 100755 rename uninstall.py => debian/prerm (95%) mode change 100644 => 100755 create mode 100755 debian/rules diff --git a/bin/howdy b/bin/howdy new file mode 100755 index 0000000..6e9c61b --- /dev/null +++ b/bin/howdy @@ -0,0 +1,2 @@ +#!/usr/bin/env bash +python3 ../cli.py diff --git a/debian/changelog b/debian/changelog new file mode 100644 index 0000000..a77d55a --- /dev/null +++ b/debian/changelog @@ -0,0 +1,5 @@ +howdy (2.0.0) UNRELEASED; urgency=middle + + * Initial release. + + -- boltgolt Wed, 04 Apr 2018 18:13:15 +0200 diff --git a/debian/control b/debian/control new file mode 100644 index 0000000..c8facbf --- /dev/null +++ b/debian/control @@ -0,0 +1,11 @@ +Source: howdy +Section: unknown +Priority: optional +Maintainer: boltgolt +Homepage: https://github.com/Boltgolt/howdy +Package: howdy +Version: 2.0.0 +Architecture: all +Depends: git, python3, python3-pip, python3-dev, python3-setuptools, build-essential, libpam-python, fswebcam, libopencv-dev, python-opencv, cmake +Description: Windows Hello™ style authentication for Ubuntu. + Use your built-in IR emitters and camera in combination with face recognition to prove who you are. diff --git a/installer.py b/debian/postinst old mode 100644 new mode 100755 similarity index 63% rename from installer.py rename to debian/postinst index 5ec2fd1..93449cf --- a/installer.py +++ b/debian/postinst @@ -1,5 +1,6 @@ +#!/usr/bin/env python3 # Installation script to install howdy -# Runs completely independent of the others +# Executed after primary apt install # Import required modules import subprocess @@ -19,33 +20,13 @@ def handleStatus(status): """Abort if a command fails""" if (status != 0): print("\033[31mError while running last command\033[0m") - sys.exit() + sys.exit(1) # Check if we're running as root user = os.getenv("SUDO_USER") if user is None: - print("Please run this script as a sudo user") - sys.exit() - -# Print some nice intro text -print("\n\033[33m HOWDY INSTALLER FOR UBUNTU\033[0m") -print(" Version 1, 2016/02/05\n") - -# Let it sink in -time.sleep(.5) -log("Installing required apt packages") - -# Install packages though apt -handleStatus(subprocess.call(["apt", "install", "-y", "git", - "python3-pip", - "python3-dev", - "python3-setuptools", - "build-essential", - "libpam-python", - "fswebcam", - "libopencv-dev", - "python-opencv", - "cmake"])) + print("Can't continue installation without root rights.") + sys.exit(1) # Update pip handleStatus(subprocess.call(["pip3 install --upgrade pip"], shell=True)) @@ -55,55 +36,59 @@ log("Starting camera check") # Get all devices devices = os.listdir("/dev") # The picked video device id -picked = False +picked = -1 -# Loop though all devices -for dev in devices: - # Only use the video devices - if (dev[:5] == "video"): - time.sleep(.5) +# If prompting has been disabled, skip camera check +if "HOWDY_NO_PROMPT" in os.environ: + picked = "0" +else: + # Loop though all devices + for dev in devices: + # Only use the video devices + if (dev[:5] == "video"): + time.sleep(.5) - # The full path to the device is the default name - device_name = "/dev/" + dev - # Get the udevadm details to try to get a better name - udevadm = subprocess.check_output(["udevadm info -r --query=all -n " + device_name], shell=True).decode("utf-8") + # The full path to the device is the default name + device_name = "/dev/" + dev + # Get the udevadm details to try to get a better name + udevadm = subprocess.check_output(["udevadm info -r --query=all -n " + device_name], shell=True).decode("utf-8") - # Loop though udevadm to search for a better name - for line in udevadm.split("\n"): - # Match it and encase it in quotes - re_name = re.search('product.*=(.*)$', line, re.IGNORECASE) - if re_name: - device_name = '"' + re_name.group(1) + '"' + # Loop though udevadm to search for a better name + for line in udevadm.split("\n"): + # Match it and encase it in quotes + re_name = re.search('product.*=(.*)$', line, re.IGNORECASE) + if re_name: + device_name = '"' + re_name.group(1) + '"' - # Show what device we're using - print("Trying " + device_name) + # Show what device we're using + print("Trying " + device_name) - # Let fswebcam keep the camera open in the background - sub = subprocess.Popen(["fswebcam -S 9999999999 -d /dev/" + dev + " /dev/null 2>/dev/null"], shell=True, preexec_fn=os.setsid) + # Let fswebcam keep the camera open in the background + sub = subprocess.Popen(["fswebcam -S 9999999999 -d /dev/" + dev + " /dev/null 2>/dev/null"], shell=True, preexec_fn=os.setsid) - try: - # Ask the user if this is the right one - print("\033[33mOne of your cameras should now be on.\033[0m") - ans = input("Did your IR emitters turn on? [y/N]: ") - except KeyboardInterrupt: - # Kill fswebcam if the user aborts + try: + # Ask the user if this is the right one + print("\033[33mOne of your cameras should now be on.\033[0m") + ans = input("Did your IR emitters turn on? [y/N]: ") + except KeyboardInterrupt: + # Kill fswebcam if the user aborts + os.killpg(os.getpgid(sub.pid), signal.SIGTERM) + raise + + # The user has answered, kill fswebcam os.killpg(os.getpgid(sub.pid), signal.SIGTERM) - raise - # The user has answered, kill fswebcam - os.killpg(os.getpgid(sub.pid), signal.SIGTERM) - - # Set this camera as picked if the answer was yes, go to the next one if no - if (ans.lower() == "y"): - picked = dev[5:] - break - else: - print("Inerpeting as a \"NO\"\n") + # Set this camera as picked if the answer was yes, go to the next one if no + if (ans.lower() == "y"): + picked = dev[5:] + break + else: + print("Inerpeting as a \"NO\"\n") # Abort if no camera was picked -if (picked == False): +if (picked == -1): print("\033[31mNo suitable IR camera found\033[0m") - sys.exit() + sys.exit(1) log("Cloning dlib") @@ -141,14 +126,9 @@ for line in fileinput.input(["/lib/security/howdy/config.ini"], inplace = 1): # Secure the howdy folder handleStatus(subprocess.call(["chmod 600 -R /lib/security/howdy/"], shell=True)) -# Allow anyone to execute the python CLI -handleStatus(subprocess.call(["chmod 755 /lib/security/howdy"], shell=True)) -handleStatus(subprocess.call(["chmod 744 /lib/security/howdy/cli.py"], shell=True)) -handleStatus(subprocess.call(["chmod 744 -R /lib/security/howdy/cli"], shell=True)) - # Make the CLI executable as howdy -handleStatus(subprocess.call(["ln -s /lib/security/howdy/cli.py /usr/local/bin/howdy"], shell=True)) -handleStatus(subprocess.call(["chmod +x /usr/local/bin/howdy"], shell=True)) +handleStatus(subprocess.call(["ln -s /lib/security/howdy/cli.py /usr/bin/howdy"], shell=True)) +handleStatus(subprocess.call(["chmod +x /usr/bin/howdy"], shell=True)) # Install the command autocomplete, don't error on failure subprocess.call(["sudo cp /lib/security/howdy/autocomplete.sh /etc/bash_completion.d/howdy"], shell=True) @@ -207,14 +187,16 @@ for line in printlines: # Print a footer print("\033[33m" + ">>> END OF /etc/pam.d/common-auth" + "\033[0m" + "\n") -# Ask the user if this change is okay -print("Lines will be insterted in /etc/pam.d/common-auth as shown above") -ans = input("Apply this change? [y/N]: ") +# Do not prompt for a yes if we're in no promt mode +if "HOWDY_NO_PROMPT" in os.environ: + # Ask the user if this change is okay + print("Lines will be insterted in /etc/pam.d/common-auth as shown above") + ans = input("Apply this change? [y/N]: ") -# Abort the whole thing if it's not -if (ans.lower() != "y"): - print("Inerpeting as a \"NO\", aborting") - sys.exit() + # Abort the whole thing if it's not + if (ans.lower() != "y"): + print("Inerpeting as a \"NO\", aborting") + sys.exit(1) print("Adding lines to PAM\n") diff --git a/uninstall.py b/debian/prerm old mode 100644 new mode 100755 similarity index 95% rename from uninstall.py rename to debian/prerm index 8cc56a1..0cd4327 --- a/uninstall.py +++ b/debian/prerm @@ -1,3 +1,5 @@ +#!/usr/bin/env python3 +# Executed on deinstallation # Completely remove howdy from the system # Import required modules diff --git a/debian/rules b/debian/rules new file mode 100755 index 0000000..1a24852 --- /dev/null +++ b/debian/rules @@ -0,0 +1 @@ +#!/bin/sh diff --git a/pam.py b/pam.py index 27977ab..8c4eabc 100644 --- a/pam.py +++ b/pam.py @@ -4,8 +4,6 @@ import subprocess import sys import os -import threading -import time # pam-python is running python 2, so we use the old module here import ConfigParser @@ -17,11 +15,6 @@ config.read(os.path.dirname(os.path.abspath(__file__)) + "/config.ini") def doAuth(pamh): """Start authentication in a seperate process""" - # Abort if the session is remote - if pamh.rhost is not None or "SSH_CONNECTION" in os.environ: - pamh.conversation(pamh.Message(pamh.PAM_TEXT_INFO, "Skipping face recognition for remote session")) - return pamh.PAM_AUTH_ERR - # Run compare as python3 subprocess to circumvent python version and import issues status = subprocess.call(["python3", os.path.dirname(os.path.abspath(__file__)) + "/compare.py", pamh.get_user()]) From 349a90e299b88997a28844b48a22c965d2c07d55 Mon Sep 17 00:00:00 2001 From: boltgolt Date: Thu, 5 Apr 2018 21:24:12 +0200 Subject: [PATCH 03/26] Added basic travis builder --- .travis.yml | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 .travis.yml diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..64f3f22 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,13 @@ +language: python +sudo: required + +before_install: + - ln -s ./debian ./DEBIAN + - dpkg-deb --build . ./build.deb + +install: sudo apt-get install ./build.deb + +script: howdy help + +notifications: + email: false From 4b944115bd7f18ab18f31d8fca408325a105f5d4 Mon Sep 17 00:00:00 2001 From: boltgolt Date: Thu, 5 Apr 2018 21:46:11 +0200 Subject: [PATCH 04/26] Fixed travis installation --- bin/howdy | 2 +- debian/postinst | 26 ++++++++++++++------------ 2 files changed, 15 insertions(+), 13 deletions(-) diff --git a/bin/howdy b/bin/howdy index 6e9c61b..1ad96be 100755 --- a/bin/howdy +++ b/bin/howdy @@ -1,2 +1,2 @@ #!/usr/bin/env bash -python3 ../cli.py +python3 /lib/security/howdy/cli.py diff --git a/debian/postinst b/debian/postinst index 93449cf..02c6fe4 100755 --- a/debian/postinst +++ b/debian/postinst @@ -28,6 +28,8 @@ if user is None: print("Can't continue installation without root rights.") sys.exit(1) +log("Upgrading pip to the latest version") + # Update pip handleStatus(subprocess.call(["pip3 install --upgrade pip"], shell=True)) @@ -40,6 +42,7 @@ picked = -1 # If prompting has been disabled, skip camera check if "HOWDY_NO_PROMPT" in os.environ: + print("\033[33mAUTOMATED INSTALL, YOU WILL NOT BE ASKED FOR INPUT AND CHECKS WILL BE SKIPPED\033[0m") picked = "0" else: # Loop though all devices @@ -110,14 +113,21 @@ log("Installing face_recognition") # Install face_recognition though pip handleStatus(subprocess.call(["pip3", "install", "face_recognition"])) -log("Cloning howdy") +log("Copying howdy data") # Make sure /lib/security exists if not os.path.exists("/lib/security"): os.makedirs("/lib/security") -# Clone howdy into it -handleStatus(subprocess.call(["git", "clone", "https://github.com/Boltgolt/howdy.git", "/lib/security/howdy"])) +# Abort if a folder for howdy already exists +if os.path.exists("/lib/security/howdy"): + print("\033[31mPrevious installation of howdy found, aborting\033[0m") + sys.exit(1) + +subprocess.call(["ls", "-hals"]) + +# Copy howdy into it's new folder +handleStatus(subprocess.call(["cp", "-R", "..", "/lib/security/howdy/"])) # Manually change the camera id to the one picked for line in fileinput.input(["/lib/security/howdy/config.ini"], inplace = 1): @@ -126,10 +136,6 @@ for line in fileinput.input(["/lib/security/howdy/config.ini"], inplace = 1): # Secure the howdy folder handleStatus(subprocess.call(["chmod 600 -R /lib/security/howdy/"], shell=True)) -# Make the CLI executable as howdy -handleStatus(subprocess.call(["ln -s /lib/security/howdy/cli.py /usr/bin/howdy"], shell=True)) -handleStatus(subprocess.call(["chmod +x /usr/bin/howdy"], shell=True)) - # Install the command autocomplete, don't error on failure subprocess.call(["sudo cp /lib/security/howdy/autocomplete.sh /etc/bash_completion.d/howdy"], shell=True) @@ -188,7 +194,7 @@ for line in printlines: print("\033[33m" + ">>> END OF /etc/pam.d/common-auth" + "\033[0m" + "\n") # Do not prompt for a yes if we're in no promt mode -if "HOWDY_NO_PROMPT" in os.environ: +if "HOWDY_NO_PROMPT" not in os.environ: # Ask the user if this change is okay print("Lines will be insterted in /etc/pam.d/common-auth as shown above") ans = input("Apply this change? [y/N]: ") @@ -233,7 +239,3 @@ print("https://github.com/Boltgolt/howdy-reports/issues/new?title=Post-installat # Let the user know what to do with the link print("Installation complete.") print("If you want to help the development, please use the link above to post some camera-related information to github") - -# Remove the installer if it was downloaded to /tmp -if os.path.exists("/tmp/howdy_install.py"): - os.remove("/tmp/howdy_install.py") From aed1f69b5389f3420638787b220f8360c91042a9 Mon Sep 17 00:00:00 2001 From: boltgolt Date: Thu, 5 Apr 2018 23:19:52 +0200 Subject: [PATCH 05/26] A lot of tries to get debuild -us -uc to work --- .travis.yml | 7 ++++--- debian/changelog | 2 +- debian/compat | 1 + debian/control | 7 +++++-- debian/copyright | 21 +++++++++++++++++++++ debian/rules | 21 ++++++++++++++++++++- debian/source/format | 1 + debian/source/options | 6 ++++++ 8 files changed, 59 insertions(+), 7 deletions(-) create mode 100644 debian/compat create mode 100644 debian/copyright create mode 100644 debian/source/format create mode 100644 debian/source/options diff --git a/.travis.yml b/.travis.yml index 64f3f22..89fdb96 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,12 +2,13 @@ language: python sudo: required before_install: - - ln -s ./debian ./DEBIAN - - dpkg-deb --build . ./build.deb + - git clone https://github.com/Boltgolt/howdy.git /tmp/howdy_build + - cd /tmp/howdy_build + - dpkg-buildpackage -rfakeroot -d -us -uc -S install: sudo apt-get install ./build.deb script: howdy help notifications: - email: false + email: false diff --git a/debian/changelog b/debian/changelog index a77d55a..055c6a5 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,4 +1,4 @@ -howdy (2.0.0) UNRELEASED; urgency=middle +howdy (2.0.0) UNRELEASED; urgency=low * Initial release. diff --git a/debian/compat b/debian/compat new file mode 100644 index 0000000..f599e28 --- /dev/null +++ b/debian/compat @@ -0,0 +1 @@ +10 diff --git a/debian/control b/debian/control index c8facbf..7aefc59 100644 --- a/debian/control +++ b/debian/control @@ -1,10 +1,13 @@ Source: howdy Section: unknown Priority: optional +Standards-Version: 3 +Build-Depends: python, dh-python Maintainer: boltgolt -Homepage: https://github.com/Boltgolt/howdy +Vcs-Git: https://github.com/Boltgolt/howdy + Package: howdy -Version: 2.0.0 +Homepage: https://github.com/Boltgolt/howdy Architecture: all Depends: git, python3, python3-pip, python3-dev, python3-setuptools, build-essential, libpam-python, fswebcam, libopencv-dev, python-opencv, cmake Description: Windows Hello™ style authentication for Ubuntu. diff --git a/debian/copyright b/debian/copyright new file mode 100644 index 0000000..54f078c --- /dev/null +++ b/debian/copyright @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2018 boltgolt + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/debian/rules b/debian/rules index 1a24852..5e3a4f9 100755 --- a/debian/rules +++ b/debian/rules @@ -1 +1,20 @@ -#!/bin/sh +#!/usr/bin/make -f +DH_VERBOSE = 1 + +DPKG_EXPORT_BUILDFLAGS = 1 +include /usr/share/dpkg/default.mk + +%: + dh $@ + +clean: + echo "ok" + +build: + echo "ok" + +binary: + dh binary + mkdir ./debian/howdy/lib + mkdir ./debian/howdy/lib/security + cp -R bin ./debian/howdy/lib/security/howdy/ diff --git a/debian/source/format b/debian/source/format new file mode 100644 index 0000000..89ae9db --- /dev/null +++ b/debian/source/format @@ -0,0 +1 @@ +3.0 (native) diff --git a/debian/source/options b/debian/source/options new file mode 100644 index 0000000..593ee7b --- /dev/null +++ b/debian/source/options @@ -0,0 +1,6 @@ +tar-ignore = ".git" +tar-ignore = "models" +tar-ignore = ".gitignore" +tar-ignore = "README.md" +tar-ignore = "LICENSE" +tar-ignore = ".travis.yml" From 50f36f2b9076124c8d72e4e7d413c94e1f14e482 Mon Sep 17 00:00:00 2001 From: boltgolt Date: Sat, 7 Apr 2018 16:22:13 +0200 Subject: [PATCH 06/26] More debian-like folder structure --- .gitignore | 8 +++++++- .travis.yml | 6 +++--- autocomplete.sh => autocomplete/howdy | 0 debian/control | 3 ++- debian/install | 2 ++ debian/postinst | 2 +- debian/rules | 9 --------- cli.py => src/cli.py | 0 {cli => src/cli}/__init__.py | 0 {cli => src/cli}/add.py | 0 {cli => src/cli}/clear.py | 0 {cli => src/cli}/help.py | 0 {cli => src/cli}/list.py | 0 {cli => src/cli}/remove.py | 0 {cli => src/cli}/test.py | 0 compare.py => src/compare.py | 0 config.ini => src/config.ini | 0 pam.py => src/pam.py | 0 18 files changed, 15 insertions(+), 15 deletions(-) rename autocomplete.sh => autocomplete/howdy (100%) mode change 100644 => 100755 create mode 100644 debian/install rename cli.py => src/cli.py (100%) rename {cli => src/cli}/__init__.py (100%) rename {cli => src/cli}/add.py (100%) rename {cli => src/cli}/clear.py (100%) rename {cli => src/cli}/help.py (100%) rename {cli => src/cli}/list.py (100%) rename {cli => src/cli}/remove.py (100%) rename {cli => src/cli}/test.py (100%) rename compare.py => src/compare.py (100%) rename config.ini => src/config.ini (100%) rename pam.py => src/pam.py (100%) diff --git a/.gitignore b/.gitignore index 112d262..58f2dae 100644 --- a/.gitignore +++ b/.gitignore @@ -101,4 +101,10 @@ ENV/ .mypy_cache/ # generated models -/models +/src/models + +# build files +debian/howdy.substvars +debian/files +debian/debhelper-build-stamp +debian/howdy diff --git a/.travis.yml b/.travis.yml index 89fdb96..95d6041 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,11 +4,11 @@ sudo: required before_install: - git clone https://github.com/Boltgolt/howdy.git /tmp/howdy_build - cd /tmp/howdy_build - - dpkg-buildpackage -rfakeroot -d -us -uc -S + - debuild -i -us -uc -b -install: sudo apt-get install ./build.deb +install: sudo apt install ../*.deb -script: howdy help +script: sudo howdy help notifications: email: false diff --git a/autocomplete.sh b/autocomplete/howdy old mode 100644 new mode 100755 similarity index 100% rename from autocomplete.sh rename to autocomplete/howdy diff --git a/debian/control b/debian/control index 7aefc59..1f4bc91 100644 --- a/debian/control +++ b/debian/control @@ -11,4 +11,5 @@ Homepage: https://github.com/Boltgolt/howdy Architecture: all Depends: git, python3, python3-pip, python3-dev, python3-setuptools, build-essential, libpam-python, fswebcam, libopencv-dev, python-opencv, cmake Description: Windows Hello™ style authentication for Ubuntu. - Use your built-in IR emitters and camera in combination with face recognition to prove who you are. + Use your built-in IR emitters and camera in combination with face recognition + to prove who you are. diff --git a/debian/install b/debian/install new file mode 100644 index 0000000..c35719b --- /dev/null +++ b/debian/install @@ -0,0 +1,2 @@ +src/. lib/security/howdy +autocomplete/. usr/share/bash-completion/completions diff --git a/debian/postinst b/debian/postinst index 02c6fe4..9977789 100755 --- a/debian/postinst +++ b/debian/postinst @@ -137,7 +137,7 @@ for line in fileinput.input(["/lib/security/howdy/config.ini"], inplace = 1): handleStatus(subprocess.call(["chmod 600 -R /lib/security/howdy/"], shell=True)) # Install the command autocomplete, don't error on failure -subprocess.call(["sudo cp /lib/security/howdy/autocomplete.sh /etc/bash_completion.d/howdy"], shell=True) +# subprocess.call(["sudo cp /lib/security/howdy/autocomplete.sh /etc/bash_completion.d/howdy"], shell=True) log("Adding howdy as PAM module") diff --git a/debian/rules b/debian/rules index 5e3a4f9..6d3bdcb 100755 --- a/debian/rules +++ b/debian/rules @@ -7,14 +7,5 @@ include /usr/share/dpkg/default.mk %: dh $@ -clean: - echo "ok" - -build: - echo "ok" - binary: dh binary - mkdir ./debian/howdy/lib - mkdir ./debian/howdy/lib/security - cp -R bin ./debian/howdy/lib/security/howdy/ diff --git a/cli.py b/src/cli.py similarity index 100% rename from cli.py rename to src/cli.py diff --git a/cli/__init__.py b/src/cli/__init__.py similarity index 100% rename from cli/__init__.py rename to src/cli/__init__.py diff --git a/cli/add.py b/src/cli/add.py similarity index 100% rename from cli/add.py rename to src/cli/add.py diff --git a/cli/clear.py b/src/cli/clear.py similarity index 100% rename from cli/clear.py rename to src/cli/clear.py diff --git a/cli/help.py b/src/cli/help.py similarity index 100% rename from cli/help.py rename to src/cli/help.py diff --git a/cli/list.py b/src/cli/list.py similarity index 100% rename from cli/list.py rename to src/cli/list.py diff --git a/cli/remove.py b/src/cli/remove.py similarity index 100% rename from cli/remove.py rename to src/cli/remove.py diff --git a/cli/test.py b/src/cli/test.py similarity index 100% rename from cli/test.py rename to src/cli/test.py diff --git a/compare.py b/src/compare.py similarity index 100% rename from compare.py rename to src/compare.py diff --git a/config.ini b/src/config.ini similarity index 100% rename from config.ini rename to src/config.ini diff --git a/pam.py b/src/pam.py similarity index 100% rename from pam.py rename to src/pam.py From ba8159033a04af7159db46390d020710960d3869 Mon Sep 17 00:00:00 2001 From: boltgolt Date: Sat, 7 Apr 2018 16:30:31 +0200 Subject: [PATCH 07/26] Fixes for travis and prerm script --- .travis.yml | 7 +++++-- debian/prerm | 17 ----------------- 2 files changed, 5 insertions(+), 19 deletions(-) diff --git a/.travis.yml b/.travis.yml index 95d6041..c1ed9ec 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,13 +2,16 @@ language: python sudo: required before_install: + - apt install devscripts - git clone https://github.com/Boltgolt/howdy.git /tmp/howdy_build - cd /tmp/howdy_build - debuild -i -us -uc -b -install: sudo apt install ../*.deb +install: sudo apt install ../*.deb -y -script: sudo howdy help +script: + - sudo howdy help + - sudo apt purge howdy -y notifications: email: false diff --git a/debian/prerm b/debian/prerm index 0cd4327..9a6bdb6 100755 --- a/debian/prerm +++ b/debian/prerm @@ -4,27 +4,10 @@ # Import required modules import subprocess -import sys -import os - -# Check if we're running as root -user = os.getenv("SUDO_USER") -if user is None: - print("Please run the uninstaller as a sudo user") - sys.exit() - -# Double check with the user for the last time -print("This will remove Howdy and all models generated with it") -ans = input("Do you want to continue? [y/N]: ") - -# Abort if they don't say yes -if (ans.lower() != "y"): - sys.exit() # Remove files and symlinks subprocess.call(["rm -rf /lib/security/howdy/"], shell=True) subprocess.call(["rm /usr/bin/howdy"], shell=True) -subprocess.call(["rm /etc/bash_completion.d/howdy"], shell=True) # Remove face_recognition and dlib subprocess.call(["pip3 uninstall face_recognition dlib -y"], shell=True) From 04dcfaff9853e22f31db8bf20a020dd38ab6aa18 Mon Sep 17 00:00:00 2001 From: boltgolt Date: Sat, 7 Apr 2018 16:45:58 +0200 Subject: [PATCH 08/26] More small fixes --- .travis.yml | 2 +- autocomplete/howdy | 1 + debian/control | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index c1ed9ec..12c80ed 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,7 +2,7 @@ language: python sudo: required before_install: - - apt install devscripts + - sudo apt install devscripts -y - git clone https://github.com/Boltgolt/howdy.git /tmp/howdy_build - cd /tmp/howdy_build - debuild -i -us -uc -b diff --git a/autocomplete/howdy b/autocomplete/howdy index 8c16937..40ce2a2 100755 --- a/autocomplete/howdy +++ b/autocomplete/howdy @@ -1,3 +1,4 @@ +#!/bin/bash # Autocomplete file run in bash # Will sugest arguments on tab diff --git a/debian/control b/debian/control index 1f4bc91..8384eb9 100644 --- a/debian/control +++ b/debian/control @@ -1,5 +1,5 @@ Source: howdy -Section: unknown +Section: misc Priority: optional Standards-Version: 3 Build-Depends: python, dh-python From 0ed5a8655f4bf305c389f8dbd73de45e24085d53 Mon Sep 17 00:00:00 2001 From: boltgolt Date: Sat, 7 Apr 2018 16:52:58 +0200 Subject: [PATCH 09/26] 2 changes to get travis to work maybe --- .travis.yml | 2 -- debian/control | 1 + 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 12c80ed..9b95d61 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,8 +3,6 @@ sudo: required before_install: - sudo apt install devscripts -y - - git clone https://github.com/Boltgolt/howdy.git /tmp/howdy_build - - cd /tmp/howdy_build - debuild -i -us -uc -b install: sudo apt install ../*.deb -y diff --git a/debian/control b/debian/control index 8384eb9..cfb4268 100644 --- a/debian/control +++ b/debian/control @@ -9,6 +9,7 @@ Vcs-Git: https://github.com/Boltgolt/howdy Package: howdy Homepage: https://github.com/Boltgolt/howdy Architecture: all +Essential: no Depends: git, python3, python3-pip, python3-dev, python3-setuptools, build-essential, libpam-python, fswebcam, libopencv-dev, python-opencv, cmake Description: Windows Hello™ style authentication for Ubuntu. Use your built-in IR emitters and camera in combination with face recognition From f46161fa080b87b2a76750c50044fd429bf91b55 Mon Sep 17 00:00:00 2001 From: boltgolt Date: Sat, 7 Apr 2018 16:58:40 +0200 Subject: [PATCH 10/26] Final try for the day --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 9b95d61..5887fa0 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,7 +2,7 @@ language: python sudo: required before_install: - - sudo apt install devscripts -y + - sudo apt install devscripts dh-make -y - debuild -i -us -uc -b install: sudo apt install ../*.deb -y From c635b56c8657754bfa477d505343a0d751d658ad Mon Sep 17 00:00:00 2001 From: boltgolt Date: Sat, 7 Apr 2018 17:09:42 +0200 Subject: [PATCH 11/26] Just kidding here's an other one --- debian/control | 1 - debian/postinst | 16 +--------------- 2 files changed, 1 insertion(+), 16 deletions(-) diff --git a/debian/control b/debian/control index cfb4268..8384eb9 100644 --- a/debian/control +++ b/debian/control @@ -9,7 +9,6 @@ Vcs-Git: https://github.com/Boltgolt/howdy Package: howdy Homepage: https://github.com/Boltgolt/howdy Architecture: all -Essential: no Depends: git, python3, python3-pip, python3-dev, python3-setuptools, build-essential, libpam-python, fswebcam, libopencv-dev, python-opencv, cmake Description: Windows Hello™ style authentication for Ubuntu. Use your built-in IR emitters and camera in combination with face recognition diff --git a/debian/postinst b/debian/postinst index 9977789..045a1b4 100755 --- a/debian/postinst +++ b/debian/postinst @@ -115,20 +115,6 @@ handleStatus(subprocess.call(["pip3", "install", "face_recognition"])) log("Copying howdy data") -# Make sure /lib/security exists -if not os.path.exists("/lib/security"): - os.makedirs("/lib/security") - -# Abort if a folder for howdy already exists -if os.path.exists("/lib/security/howdy"): - print("\033[31mPrevious installation of howdy found, aborting\033[0m") - sys.exit(1) - -subprocess.call(["ls", "-hals"]) - -# Copy howdy into it's new folder -handleStatus(subprocess.call(["cp", "-R", "..", "/lib/security/howdy/"])) - # Manually change the camera id to the one picked for line in fileinput.input(["/lib/security/howdy/config.ini"], inplace = 1): print(line.replace("device_id = 1", "device_id = " + picked), end="") @@ -238,4 +224,4 @@ print("https://github.com/Boltgolt/howdy-reports/issues/new?title=Post-installat # Let the user know what to do with the link print("Installation complete.") -print("If you want to help the development, please use the link above to post some camera-related information to github") +print("\033[33mIf you want to help the development, please use the link above to post some camera-related information to github!\033[0m") From e5a3cf32410fae42000231bb79e68d8bccb1d839 Mon Sep 17 00:00:00 2001 From: boltgolt Date: Sat, 7 Apr 2018 17:22:30 +0200 Subject: [PATCH 12/26] Skiping optional camera data check when in no_prompt --- debian/postinst | 38 ++++++++++++++++++++------------------ 1 file changed, 20 insertions(+), 18 deletions(-) diff --git a/debian/postinst b/debian/postinst index 045a1b4..8123346 100755 --- a/debian/postinst +++ b/debian/postinst @@ -199,28 +199,30 @@ common_auth.close() # From here onwards the installation is complete # We want to gather more information about the types or IR camera's -# used though, and the following lines are data collection +# used though, and the following lines are data gathering +# No data is ever uploaded without permission -# List all video devices -diag_out = "Video devices [IR=" + picked + "]\n" -diag_out += "```\n" -diag_out += subprocess.check_output(['ls /dev/ | grep video'], shell=True).decode("utf-8") -diag_out += "```\n" +if "HOWDY_NO_PROMPT" not in os.environ: + # List all video devices + diag_out = "Video devices [IR=" + picked + "]\n" + diag_out += "```\n" + diag_out += subprocess.check_output(['ls /dev/ | grep video'], shell=True).decode("utf-8") + diag_out += "```\n" -# Get some info from the USB kernel listings -diag_out += "Lsusb output\n" -diag_out += "```\n" -diag_out += subprocess.check_output(['lsusb -vvvv | grep -i "Camera\|iFunction"'], shell=True).decode("utf-8") -diag_out += "```\n" + # Get some info from the USB kernel listings + diag_out += "Lsusb output\n" + diag_out += "```\n" + diag_out += subprocess.check_output(['lsusb -vvvv | grep -i "Camera\|iFunction"'], shell=True).decode("utf-8") + diag_out += "```\n" -# Get camera information from video4linux -diag_out += "Udevadm\n" -diag_out += "```\n" -diag_out += subprocess.check_output(['udevadm info -r --query=all -n /dev/video' + picked + ' | grep -i "ID_BUS\|ID_MODEL_ID\|ID_VENDOR_ID\|ID_V4L_PRODUCT\|ID_MODEL"'], shell=True).decode("utf-8") -diag_out += "```" + # Get camera information from video4linux + diag_out += "Udevadm\n" + diag_out += "```\n" + diag_out += subprocess.check_output(['udevadm info -r --query=all -n /dev/video' + picked + ' | grep -i "ID_BUS\|ID_MODEL_ID\|ID_VENDOR_ID\|ID_V4L_PRODUCT\|ID_MODEL"'], shell=True).decode("utf-8") + diag_out += "```" -# Print it all as a clickable link to a new github issue -print("https://github.com/Boltgolt/howdy-reports/issues/new?title=Post-installation%20camera%20information&body=" + urllib.parse.quote_plus(diag_out) + "\n") + # Print it all as a clickable link to a new github issue + print("https://github.com/Boltgolt/howdy-reports/issues/new?title=Post-installation%20camera%20information&body=" + urllib.parse.quote_plus(diag_out) + "\n") # Let the user know what to do with the link print("Installation complete.") From 8d3a4736cb5ff2ba9a8f21f38e230b453061bf0d Mon Sep 17 00:00:00 2001 From: boltgolt Date: Sat, 7 Apr 2018 17:39:14 +0200 Subject: [PATCH 13/26] Missed howdy command linking and disabled pop cache --- bin/howdy | 2 -- debian/postinst | 13 +++++++++++-- debian/prerm | 12 ++++++++++-- 3 files changed, 21 insertions(+), 6 deletions(-) delete mode 100755 bin/howdy diff --git a/bin/howdy b/bin/howdy deleted file mode 100755 index 1ad96be..0000000 --- a/bin/howdy +++ /dev/null @@ -1,2 +0,0 @@ -#!/usr/bin/env bash -python3 /lib/security/howdy/cli.py diff --git a/debian/postinst b/debian/postinst index 8123346..be4f9b3 100755 --- a/debian/postinst +++ b/debian/postinst @@ -31,7 +31,7 @@ if user is None: log("Upgrading pip to the latest version") # Update pip -handleStatus(subprocess.call(["pip3 install --upgrade pip"], shell=True)) +handleStatus(subprocess.call(["pip3 install --upgrade pip --no-cache-dir"], shell=True)) log("Starting camera check") @@ -111,7 +111,7 @@ handleStatus(subprocess.call(["rm", "-rf", "/tmp/dlib_clone"])) log("Installing face_recognition") # Install face_recognition though pip -handleStatus(subprocess.call(["pip3", "install", "face_recognition"])) +handleStatus(subprocess.call(["pip3", "install", "face_recognition", "--no-cache-dir"])) log("Copying howdy data") @@ -122,6 +122,15 @@ for line in fileinput.input(["/lib/security/howdy/config.ini"], inplace = 1): # Secure the howdy folder handleStatus(subprocess.call(["chmod 600 -R /lib/security/howdy/"], shell=True)) +# Allow anyone to execute the python CLI +handleStatus(subprocess.call(["chmod 755 /lib/security/howdy"], shell=True)) +handleStatus(subprocess.call(["chmod 744 /lib/security/howdy/cli.py"], shell=True)) +handleStatus(subprocess.call(["chmod 744 -R /lib/security/howdy/cli"], shell=True)) + +# Make the CLI executable as howdy +handleStatus(subprocess.call(["ln -s /lib/security/howdy/cli.py /usr/local/bin/howdy"], shell=True)) +handleStatus(subprocess.call(["chmod +x /usr/local/bin/howdy"], shell=True)) + # Install the command autocomplete, don't error on failure # subprocess.call(["sudo cp /lib/security/howdy/autocomplete.sh /etc/bash_completion.d/howdy"], shell=True) diff --git a/debian/prerm b/debian/prerm index 9a6bdb6..28a16e0 100755 --- a/debian/prerm +++ b/debian/prerm @@ -6,11 +6,19 @@ import subprocess # Remove files and symlinks -subprocess.call(["rm -rf /lib/security/howdy/"], shell=True) +try: + subprocess.call(["rm -rf /lib/security/howdy/"], shell=True) +except as e: + pass +try: + subprocess.call(["rm /usr/share/bash-completion/completions/howdy"], shell=True) +except as e: + pass + subprocess.call(["rm /usr/bin/howdy"], shell=True) # Remove face_recognition and dlib -subprocess.call(["pip3 uninstall face_recognition dlib -y"], shell=True) +subprocess.call(["pip3 uninstall face_recognition dlib -y --no-cache-dir"], shell=True) # Print a tearbending message print(""" From 060fc369c2b0224fe8989f360b9e04d22a9c8d5e Mon Sep 17 00:00:00 2001 From: boltgolt Date: Sat, 7 Apr 2018 17:47:30 +0200 Subject: [PATCH 14/26] Reverting pip cache change --- debian/postinst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/debian/postinst b/debian/postinst index be4f9b3..104de86 100755 --- a/debian/postinst +++ b/debian/postinst @@ -31,7 +31,7 @@ if user is None: log("Upgrading pip to the latest version") # Update pip -handleStatus(subprocess.call(["pip3 install --upgrade pip --no-cache-dir"], shell=True)) +handleStatus(subprocess.call(["pip3 install --upgrade pip"], shell=True)) log("Starting camera check") From 2b72f2b6ee39057137ae4693581189bb1b7618fe Mon Sep 17 00:00:00 2001 From: boltgolt Date: Sat, 7 Apr 2018 19:15:00 +0200 Subject: [PATCH 15/26] Mutiple small fixes --- .travis.yml | 1 + debian/control | 2 +- debian/postinst | 7 +++++-- debian/prerm | 4 ++-- 4 files changed, 9 insertions(+), 5 deletions(-) diff --git a/.travis.yml b/.travis.yml index 5887fa0..c4959e3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,6 +9,7 @@ install: sudo apt install ../*.deb -y script: - sudo howdy help + - sudo howdy list - sudo apt purge howdy -y notifications: diff --git a/debian/control b/debian/control index 8384eb9..d3c5b9c 100644 --- a/debian/control +++ b/debian/control @@ -10,6 +10,6 @@ Package: howdy Homepage: https://github.com/Boltgolt/howdy Architecture: all Depends: git, python3, python3-pip, python3-dev, python3-setuptools, build-essential, libpam-python, fswebcam, libopencv-dev, python-opencv, cmake -Description: Windows Hello™ style authentication for Ubuntu. +Description: Windows Hello style authentication for Ubuntu Use your built-in IR emitters and camera in combination with face recognition to prove who you are. diff --git a/debian/postinst b/debian/postinst index 104de86..ba50e14 100755 --- a/debian/postinst +++ b/debian/postinst @@ -107,13 +107,14 @@ log("Cleaning up dlib") # Remove the no longer needed git clone handleStatus(subprocess.call(["rm", "-rf", "/tmp/dlib_clone"])) +print("Temporary dlib files removed") log("Installing face_recognition") # Install face_recognition though pip -handleStatus(subprocess.call(["pip3", "install", "face_recognition", "--no-cache-dir"])) +handleStatus(subprocess.call(["pip3", "install", "face_recognition"])) -log("Copying howdy data") +log("Configuring howdy") # Manually change the camera id to the one picked for line in fileinput.input(["/lib/security/howdy/config.ini"], inplace = 1): @@ -126,10 +127,12 @@ handleStatus(subprocess.call(["chmod 600 -R /lib/security/howdy/"], shell=True)) handleStatus(subprocess.call(["chmod 755 /lib/security/howdy"], shell=True)) handleStatus(subprocess.call(["chmod 744 /lib/security/howdy/cli.py"], shell=True)) handleStatus(subprocess.call(["chmod 744 -R /lib/security/howdy/cli"], shell=True)) +print("Permissions set") # Make the CLI executable as howdy handleStatus(subprocess.call(["ln -s /lib/security/howdy/cli.py /usr/local/bin/howdy"], shell=True)) handleStatus(subprocess.call(["chmod +x /usr/local/bin/howdy"], shell=True)) +print("Installing howdy command") # Install the command autocomplete, don't error on failure # subprocess.call(["sudo cp /lib/security/howdy/autocomplete.sh /etc/bash_completion.d/howdy"], shell=True) diff --git a/debian/prerm b/debian/prerm index 28a16e0..e1600f3 100755 --- a/debian/prerm +++ b/debian/prerm @@ -8,11 +8,11 @@ import subprocess # Remove files and symlinks try: subprocess.call(["rm -rf /lib/security/howdy/"], shell=True) -except as e: +except e: pass try: subprocess.call(["rm /usr/share/bash-completion/completions/howdy"], shell=True) -except as e: +except e: pass subprocess.call(["rm /usr/bin/howdy"], shell=True) From 13d5f8ae1a7a7cebc4df135412d5265223f5ca96 Mon Sep 17 00:00:00 2001 From: boltgolt Date: Mon, 9 Apr 2018 19:33:42 +0200 Subject: [PATCH 16/26] Updated readme and pushed fix to launchpad --- README.md | 19 ++++++++++++++++--- debian/changelog | 10 ++++++++-- debian/control | 2 +- src/cli.py | 4 +++- 4 files changed, 28 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 3645b24..7c944d3 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Howdy for Ubuntu +# Howdy for Ubuntu ![Build Status](https://travis-ci.org/Boltgolt/howdy.svg?branch=master) Windows Hello™ style authentication for Ubuntu. Use your built-in IR emitters and camera in combination with face recognition to prove who you are. @@ -6,10 +6,12 @@ Using the central authentication system in Linux (PAM), this works everywhere yo ### Installation -Run the installer by pasting (`ctrl+shift+V`) the following command into the terminal: +Run the installer by pasting (`ctrl+shift+V`) the following commands into the terminal one at a time: ``` -wget -O /tmp/howdy_install.py https://raw.githubusercontent.com/Boltgolt/howdy/master/installer.py && sudo python3 /tmp/howdy_install.py +sudo add-apt-repository ppa:boltgolt/howdy +sudo apt update +sudo apt install howdy ``` This will guide you through the installation. When that's done run `sudo howdy USER add` and replace `USER` with your username to add a face model. @@ -22,6 +24,17 @@ If nothing went wrong we should be able to run sudo by just showing your face. O The installer adds a `howdy` command to manage face models for the current user. Use `howdy help` to list the available options. +| Command | Description | Needs user | +|-----------|-----------------------------------------------|------------| +| `add` | Add a new face model for the given user | Yes | +| `clear` | Remove all face models for the given user | Yes | +| `config` | Open the config file in nano | No | +| `disable` | Disable or enable howdy | No | +| `help` | Show a help page | No | +| `list` | List all saved face models for the given user | Yes | +| `remove` | Remove a specific model for the given user | Yes | +| `test` | Test the camera and recognition methods | No | + ### Troubleshooting Any python errors get logged directly into the console and should indicate what went wrong. If authentication still fails but no errors are printed you could take a look at the last lines in `/var/log/auth.log` to see if anything has been reported there. diff --git a/debian/changelog b/debian/changelog index 055c6a5..18017d1 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,5 +1,11 @@ -howdy (2.0.0) UNRELEASED; urgency=low +howdy (2.0.0-alpha+2) xenial; urgency=medium - * Initial release. + * Fixed build dependency issue + + -- boltgolt Sat, 07 Apr 2018 21:30:48 +0200 + +howdy (2.0.0-alpha+1) xenial; urgency=low + + * Initial packaged release. -- boltgolt Wed, 04 Apr 2018 18:13:15 +0200 diff --git a/debian/control b/debian/control index d3c5b9c..1cd88de 100644 --- a/debian/control +++ b/debian/control @@ -2,7 +2,7 @@ Source: howdy Section: misc Priority: optional Standards-Version: 3 -Build-Depends: python, dh-python +Build-Depends: python, dh-python, devscripts, dh-make Maintainer: boltgolt Vcs-Git: https://github.com/Boltgolt/howdy diff --git a/src/cli.py b/src/cli.py index 8e9e6ab..bdbf064 100755 --- a/src/cli.py +++ b/src/cli.py @@ -30,7 +30,7 @@ else: # Requre sudo for comamnds that need root rights to read the model files if os.getenv("SUDO_USER") is None: print("Please run this command with sudo") - sys.exit() + sys.exit(1) # Frome here on we require the second argument to be the username, # switching the command to the 3rd @@ -48,6 +48,8 @@ else: # If the comand is invalid, check if the user hasn't swapped the username and command if sys.argv[1] in ["list", "add", "remove", "clear"]: print("Usage: howdy ") + sys.exit(1) else: print('Unknown command "' + cmd + '"') import cli.help + sys.exit(1) From 52e6be7be8ca49c6d24db231f69980af727a6483 Mon Sep 17 00:00:00 2001 From: boltgolt Date: Mon, 9 Apr 2018 19:58:37 +0200 Subject: [PATCH 17/26] Small fixes and the start of a man page --- README.md | 8 ++------ debian/control | 2 +- debian/howdy.1 | 54 ++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 57 insertions(+), 7 deletions(-) create mode 100644 debian/howdy.1 diff --git a/README.md b/README.md index 7c944d3..3b76ee9 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Howdy for Ubuntu ![Build Status](https://travis-ci.org/Boltgolt/howdy.svg?branch=master) +# Howdy for Ubuntu [![Build Status](https://travis-ci.org/Boltgolt/howdy.svg?branch=dev)](https://travis-ci.org/Boltgolt/howdy) Windows Hello™ style authentication for Ubuntu. Use your built-in IR emitters and camera in combination with face recognition to prove who you are. @@ -14,7 +14,7 @@ sudo apt update sudo apt install howdy ``` -This will guide you through the installation. When that's done run `sudo howdy USER add` and replace `USER` with your username to add a face model. +This will guide you through the installation. When that's done run `sudo howdy add $USER` and replace `$USER` with your username to add a face model. If nothing went wrong we should be able to run sudo by just showing your face. Open a new terminal and run `sudo -i` to see it in action. @@ -41,10 +41,6 @@ Any python errors get logged directly into the console and should indicate what If you encounter an error that hasn't been reported yet, don't be afraid to open a new issue. -### Uninstalling - -There is an uninstaller available, run `sudo python3 /lib/security/howdy/uninstall.py` to remove Howdy from your system. - ### A note on security This script is in no way as secure as a password and will never be. Although it's harder to fool than normal face recognition, a person who looks similar to you or well-printed photo of you could be enough to do it. diff --git a/debian/control b/debian/control index 1cd88de..872fcf6 100644 --- a/debian/control +++ b/debian/control @@ -10,6 +10,6 @@ Package: howdy Homepage: https://github.com/Boltgolt/howdy Architecture: all Depends: git, python3, python3-pip, python3-dev, python3-setuptools, build-essential, libpam-python, fswebcam, libopencv-dev, python-opencv, cmake -Description: Windows Hello style authentication for Ubuntu +Description: Windows Hello style authentication for Ubuntu. Use your built-in IR emitters and camera in combination with face recognition to prove who you are. diff --git a/debian/howdy.1 b/debian/howdy.1 new file mode 100644 index 0000000..97c3cea --- /dev/null +++ b/debian/howdy.1 @@ -0,0 +1,54 @@ +.\" Please adjust this date whenever revising the manpage. +.TH HOWDY SECTION "April 9, 2018" +.\" Some roff macros, for reference: +.\" .nh disable hyphenation +.\" .hy enable hyphenation +.\" .ad l left justify +.\" .ad b justify to both left and right margins +.\" .nf disable filling +.\" .fi enable filling +.\" .br insert line break +.\" .sp insert n+1 empty lines +.\" for manpage-specific macros, see man(7) +.SH NAME +howdy \- Windows Hello style authentication for Ubuntu +.SH SYNOPSIS +.B howdy +.RI [ options ] " files" ... +.br +.B bar +.RI [ options ] " files" ... +.SH DESCRIPTION +This manual page documents briefly the +.B nwsmtp +and +.B bar +commands. +.PP +.\" TeX users may be more comfortable with the \fB\fP and +.\" \fI\fP escape sequences to invode bold face and italics, +.\" respectively. +\fBnwsmtp\fP is a program that... +.SH OPTIONS +These programs follow the usual GNU command line syntax, with long +options starting with two dashes (`-'). +A summary of options is included below. +For a complete description, see the Info files. +.TP +.B \-h, \-\-help +Show summary of options. +.TP +.B \-v, \-\-version +Show version of program. +.SH SEE ALSO +.BR bar (1), +.BR baz (1). +.br +The programs are documented fully by +.IR "The Rise and Fall of a Fooish Bar" , +available via the Info system. +.SH AUTHOR +nwsmtp was written by . +.PP +This manual page was written by Alexei Moisseev , +for the Debian project (and may be used by others). From fceb61a91b1b028dac8c9fdb25c7ab926d80f8c0 Mon Sep 17 00:00:00 2001 From: boltgolt Date: Thu, 12 Apr 2018 19:38:34 +0200 Subject: [PATCH 18/26] Moved camera detection to preinst so dpkg doesn't break on abort --- .travis.yml | 2 +- README.md | 27 +++++++++------ debian/postinst | 79 ++++++++----------------------------------- debian/preinst | 89 +++++++++++++++++++++++++++++++++++++++++++++++++ debian/prerm | 19 +++++------ 5 files changed, 128 insertions(+), 88 deletions(-) create mode 100644 debian/preinst diff --git a/.travis.yml b/.travis.yml index c4959e3..d27dc7f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -13,4 +13,4 @@ script: - sudo apt purge howdy -y notifications: - email: false + email: true diff --git a/README.md b/README.md index 3b76ee9..3094fa0 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Howdy for Ubuntu [![Build Status](https://travis-ci.org/Boltgolt/howdy.svg?branch=dev)](https://travis-ci.org/Boltgolt/howdy) +# Howdy for Ubuntu [![](https://travis-ci.org/Boltgolt/howdy.svg?branch=dev)](https://travis-ci.org/Boltgolt/howdy) Windows Hello™ style authentication for Ubuntu. Use your built-in IR emitters and camera in combination with face recognition to prove who you are. @@ -24,16 +24,21 @@ If nothing went wrong we should be able to run sudo by just showing your face. O The installer adds a `howdy` command to manage face models for the current user. Use `howdy help` to list the available options. -| Command | Description | Needs user | -|-----------|-----------------------------------------------|------------| -| `add` | Add a new face model for the given user | Yes | -| `clear` | Remove all face models for the given user | Yes | -| `config` | Open the config file in nano | No | -| `disable` | Disable or enable howdy | No | -| `help` | Show a help page | No | -| `list` | List all saved face models for the given user | Yes | -| `remove` | Remove a specific model for the given user | Yes | -| `test` | Test the camera and recognition methods | No | +Usage: +``` +howdy [user] [argument] +``` + +| Command | Description | User required | +|-----------|-----------------------------------------------|---------------| +| `add` | Add a new face model for the given user | Yes | +| `clear` | Remove all face models for the given user | Yes | +| `config` | Open the config file in nano | No | +| `disable` | Disable or enable howdy | No | +| `help` | Show a help page | No | +| `list` | List all saved face models for the given user | Yes | +| `remove` | Remove a specific model for the given user | Yes | +| `test` | Test the camera and recognition methods | No | ### Troubleshooting diff --git a/debian/postinst b/debian/postinst index ba50e14..2b764a1 100755 --- a/debian/postinst +++ b/debian/postinst @@ -12,6 +12,9 @@ import signal import fileinput import urllib.parse +if "configure" not in sys.argv: + sys.exit(0) + def log(text): """Print a nicely formatted line to stdout""" print("\n>>> \033[32m" + text + "\033[0m\n") @@ -22,77 +25,21 @@ def handleStatus(status): print("\033[31mError while running last command\033[0m") sys.exit(1) -# Check if we're running as root -user = os.getenv("SUDO_USER") -if user is None: - print("Can't continue installation without root rights.") - sys.exit(1) - log("Upgrading pip to the latest version") +# We're not in fresh configuration mode, so exit +if not os.path.exists("/tmp/howdy_picked_device"): + sys.exit(0) + +in_file = open("/tmp/howdy_picked_device", "r") +picked = int(in_file.read()) +in_file.close() + +subprocess.call(["rm /tmp/howdy_picked_device"], shell=True) + # Update pip handleStatus(subprocess.call(["pip3 install --upgrade pip"], shell=True)) -log("Starting camera check") - -# Get all devices -devices = os.listdir("/dev") -# The picked video device id -picked = -1 - -# If prompting has been disabled, skip camera check -if "HOWDY_NO_PROMPT" in os.environ: - print("\033[33mAUTOMATED INSTALL, YOU WILL NOT BE ASKED FOR INPUT AND CHECKS WILL BE SKIPPED\033[0m") - picked = "0" -else: - # Loop though all devices - for dev in devices: - # Only use the video devices - if (dev[:5] == "video"): - time.sleep(.5) - - # The full path to the device is the default name - device_name = "/dev/" + dev - # Get the udevadm details to try to get a better name - udevadm = subprocess.check_output(["udevadm info -r --query=all -n " + device_name], shell=True).decode("utf-8") - - # Loop though udevadm to search for a better name - for line in udevadm.split("\n"): - # Match it and encase it in quotes - re_name = re.search('product.*=(.*)$', line, re.IGNORECASE) - if re_name: - device_name = '"' + re_name.group(1) + '"' - - # Show what device we're using - print("Trying " + device_name) - - # Let fswebcam keep the camera open in the background - sub = subprocess.Popen(["fswebcam -S 9999999999 -d /dev/" + dev + " /dev/null 2>/dev/null"], shell=True, preexec_fn=os.setsid) - - try: - # Ask the user if this is the right one - print("\033[33mOne of your cameras should now be on.\033[0m") - ans = input("Did your IR emitters turn on? [y/N]: ") - except KeyboardInterrupt: - # Kill fswebcam if the user aborts - os.killpg(os.getpgid(sub.pid), signal.SIGTERM) - raise - - # The user has answered, kill fswebcam - os.killpg(os.getpgid(sub.pid), signal.SIGTERM) - - # Set this camera as picked if the answer was yes, go to the next one if no - if (ans.lower() == "y"): - picked = dev[5:] - break - else: - print("Inerpeting as a \"NO\"\n") - -# Abort if no camera was picked -if (picked == -1): - print("\033[31mNo suitable IR camera found\033[0m") - sys.exit(1) - log("Cloning dlib") # Clone the git to /tmp diff --git a/debian/preinst b/debian/preinst new file mode 100644 index 0000000..1fabc77 --- /dev/null +++ b/debian/preinst @@ -0,0 +1,89 @@ +#!/usr/bin/env python3 +# Used to check cameras before commiting to install +# Executed before primary apt install of files + +def col(id): + if id == 1: return "\033[32m" + if id == 2: return "\033[33m" + if id == 3: return "\033[31m" + return "\033[0m" + +import subprocess +import time +import sys +import os +import re +import signal + +if "install" not in sys.argv: + sys.exit(0) + +# The picked video device id +picked = -1 + +print(col(1) + "Starting IR camera check...\n" + col(0)) + +# If prompting has been disabled, skip camera check +if "HOWDY_NO_PROMPT" in os.environ: + print(col(2) + "AUTOMATED INSTALL, YOU WILL NOT BE ASKED FOR INPUT AND CHECKS WILL BE SKIPPED" + col(0)) + + with open("/tmp/howdy_picked_device", "w") as out_file: + out_file.write("0") + + sys.exit(0) + +# Get all devices +devices = os.listdir("/dev") + +# Loop though all devices +for dev in devices: + # Only use the video devices + if (dev[:5] == "video"): + time.sleep(.5) + + # The full path to the device is the default name + device_name = "/dev/" + dev + # Get the udevadm details to try to get a better name + udevadm = subprocess.check_output(["udevadm info -r --query=all -n " + device_name], shell=True).decode("utf-8") + + # Loop though udevadm to search for a better name + for line in udevadm.split("\n"): + # Match it and encase it in quotes + re_name = re.search('product.*=(.*)$', line, re.IGNORECASE) + if re_name: + device_name = '"' + re_name.group(1) + '"' + + # Show what device we're using + print("Trying " + device_name) + + # Let fswebcam keep the camera open in the background + sub = subprocess.Popen(["fswebcam -S 9999999999 -d /dev/" + dev + " /dev/null 2>/dev/null"], shell=True, preexec_fn=os.setsid) + + try: + # Ask the user if this is the right one + print(col(2) + "One of your cameras should now be on." + col(0)) + ans = input("Did your IR emitters turn on? [y/N]: ") + except KeyboardInterrupt: + # Kill fswebcam if the user aborts + os.killpg(os.getpgid(sub.pid), signal.SIGTERM) + raise + + # The user has answered, kill fswebcam + os.killpg(os.getpgid(sub.pid), signal.SIGTERM) + + # Set this camera as picked if the answer was yes, go to the next one if no + if ans.lower() == "y" or ans.lower() == "yes": + picked = dev[5:] + break + else: + print("Interpreting as a " + col(3) + "\"NO\"\n" + col(0)) + +# Abort if no camera was picked +if picked == -1: + print(col(3) + "No suitable IR camera found, aborting install." + col(0)) + sys.exit(23) + +with open("/tmp/howdy_picked_device", "w") as out_file: + out_file.write(str(picked)) + +print("") diff --git a/debian/prerm b/debian/prerm index e1600f3..910bd88 100755 --- a/debian/prerm +++ b/debian/prerm @@ -2,28 +2,27 @@ # Executed on deinstallation # Completely remove howdy from the system +def col(id): + if id == 1: return "\033[32m" + if id == 2: return "\033[33m" + if id == 3: return "\033[31m" + return "\033[0m" + # Import required modules import subprocess # Remove files and symlinks -try: - subprocess.call(["rm -rf /lib/security/howdy/"], shell=True) -except e: - pass try: subprocess.call(["rm /usr/share/bash-completion/completions/howdy"], shell=True) except e: + print("Can't remove autocompletion script") pass -subprocess.call(["rm /usr/bin/howdy"], shell=True) - # Remove face_recognition and dlib subprocess.call(["pip3 uninstall face_recognition dlib -y --no-cache-dir"], shell=True) # Print a tearbending message -print(""" -Howdy has been uninstalled :'( - +print(col(2) + """ There are still lines in /etc/pam.d/common-auth that can't be removed automatically Run "nano /etc/pam.d/common-auth" to remove them by hand\ -""") +""" + col(0)) From e15ef744398749f89805806f602759b51585f6e2 Mon Sep 17 00:00:00 2001 From: boltgolt Date: Thu, 12 Apr 2018 23:45:52 +0200 Subject: [PATCH 19/26] Fixed dlib dependency issue --- README.md | 2 +- debian/changelog | 7 +++++++ debian/postinst | 15 +++++++++++---- debian/preinst | 1 + debian/prerm | 10 +++++++++- 5 files changed, 29 insertions(+), 6 deletions(-) mode change 100644 => 100755 debian/preinst diff --git a/README.md b/README.md index 3094fa0..152b6d7 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Howdy for Ubuntu [![](https://travis-ci.org/Boltgolt/howdy.svg?branch=dev)](https://travis-ci.org/Boltgolt/howdy) +# Howdy for Ubuntu [![](https://travis-ci.org/Boltgolt/howdy.svg?branch=dev)](https://travis-ci.org/Boltgolt/howdy) ![](https://img.shields.io/github/release/Boltgolt/howdy.svg) [![](https://img.shields.io/github/issues-raw/Boltgolt/howdy/enhancement.svg?label=feature requests)](https://github.com/Boltgolt/howdy/issues?q=is%3Aissue+is%3Aopen+label%3Aenhancement) Windows Hello™ style authentication for Ubuntu. Use your built-in IR emitters and camera in combination with face recognition to prove who you are. diff --git a/debian/changelog b/debian/changelog index 18017d1..a8959b9 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,10 @@ +howdy (2.0.0-alpha+3) xenial; urgency=medium + + * Fixed issue where dlib dependency failed to install on some installations + * Added preinst script for camera detection + + -- boltgolt Thu, 12 Apr 2018 21:42:42 +0000 + howdy (2.0.0-alpha+2) xenial; urgency=medium * Fixed build dependency issue diff --git a/debian/postinst b/debian/postinst index 2b764a1..c1b45ec 100755 --- a/debian/postinst +++ b/debian/postinst @@ -25,25 +25,27 @@ def handleStatus(status): print("\033[31mError while running last command\033[0m") sys.exit(1) -log("Upgrading pip to the latest version") # We're not in fresh configuration mode, so exit if not os.path.exists("/tmp/howdy_picked_device"): sys.exit(0) in_file = open("/tmp/howdy_picked_device", "r") -picked = int(in_file.read()) +# Should be a string +picked = in_file.read() in_file.close() subprocess.call(["rm /tmp/howdy_picked_device"], shell=True) +log("Upgrading pip to the latest version") + # Update pip handleStatus(subprocess.call(["pip3 install --upgrade pip"], shell=True)) log("Cloning dlib") # Clone the git to /tmp -handleStatus(subprocess.call(["git", "clone", "https://github.com/davisking/dlib.git", "/tmp/dlib_clone"])) +handleStatus(subprocess.call(["git", "clone", "--depth", "1", "https://github.com/davisking/dlib.git", "/tmp/dlib_clone"])) log("Building dlib") @@ -56,10 +58,15 @@ log("Cleaning up dlib") handleStatus(subprocess.call(["rm", "-rf", "/tmp/dlib_clone"])) print("Temporary dlib files removed") +log("Installing python dependencies") + +# Install face_recognition though pip +handleStatus(subprocess.call(["pip3", "install", "--cache-dir", "/tmp/pip_howdy", "face_recognition_models==0.3.0", "Click>=6.0", "numpy", "Pillow"])) + log("Installing face_recognition") # Install face_recognition though pip -handleStatus(subprocess.call(["pip3", "install", "face_recognition"])) +handleStatus(subprocess.call(["pip3", "install", "--cache-dir", "/tmp/pip_howdy", "--no-deps", "face_recognition==1.2.2"])) log("Configuring howdy") diff --git a/debian/preinst b/debian/preinst old mode 100644 new mode 100755 index 1fabc77..d62616b --- a/debian/preinst +++ b/debian/preinst @@ -86,4 +86,5 @@ if picked == -1: with open("/tmp/howdy_picked_device", "w") as out_file: out_file.write(str(picked)) +# Add a line break print("") diff --git a/debian/prerm b/debian/prerm index 910bd88..cd693d2 100755 --- a/debian/prerm +++ b/debian/prerm @@ -12,14 +12,22 @@ def col(id): import subprocess # Remove files and symlinks +try: + subprocess.call(["rm /usr/local/bin/howdy"], shell=True) +except e: + print("Can't remove executable") try: subprocess.call(["rm /usr/share/bash-completion/completions/howdy"], shell=True) except e: print("Can't remove autocompletion script") +try: + subprocess.call(["rm -rf /lib/security/howdy"], shell=True) +except e: + # This error is normal pass # Remove face_recognition and dlib -subprocess.call(["pip3 uninstall face_recognition dlib -y --no-cache-dir"], shell=True) +subprocess.call(["pip3 uninstall face_recognition face_recognition_models dlib -y --no-cache-dir"], shell=True) # Print a tearbending message print(col(2) + """ From 353463eebda239db61301da7a12d21ca57e1c802 Mon Sep 17 00:00:00 2001 From: boltgolt Date: Fri, 13 Apr 2018 00:54:36 +0200 Subject: [PATCH 20/26] Added manpage and commented recent additions --- README.md | 2 +- debian/howdy.1 | 73 +++++++++++++++---------------------------- debian/howdy.manpages | 1 + debian/postinst | 29 +++++++++++------ debian/preinst | 6 +++- debian/prerm | 9 ++++++ 6 files changed, 60 insertions(+), 60 deletions(-) create mode 100644 debian/howdy.manpages diff --git a/README.md b/README.md index 152b6d7..89a0946 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Howdy for Ubuntu [![](https://travis-ci.org/Boltgolt/howdy.svg?branch=dev)](https://travis-ci.org/Boltgolt/howdy) ![](https://img.shields.io/github/release/Boltgolt/howdy.svg) [![](https://img.shields.io/github/issues-raw/Boltgolt/howdy/enhancement.svg?label=feature requests)](https://github.com/Boltgolt/howdy/issues?q=is%3Aissue+is%3Aopen+label%3Aenhancement) +# Howdy for Ubuntu [![](https://travis-ci.org/Boltgolt/howdy.svg?branch=dev)](https://travis-ci.org/Boltgolt/howdy) ![](https://img.shields.io/github/release/Boltgolt/howdy.svg) [![](https://img.shields.io/github/issues-raw/Boltgolt/howdy/enhancement.svg?label=feature+requests)](https://github.com/Boltgolt/howdy/issues?q=is%3Aissue+is%3Aopen+label%3Aenhancement) Windows Hello™ style authentication for Ubuntu. Use your built-in IR emitters and camera in combination with face recognition to prove who you are. diff --git a/debian/howdy.1 b/debian/howdy.1 index 97c3cea..8d3d47b 100644 --- a/debian/howdy.1 +++ b/debian/howdy.1 @@ -1,54 +1,31 @@ .\" Please adjust this date whenever revising the manpage. -.TH HOWDY SECTION "April 9, 2018" -.\" Some roff macros, for reference: -.\" .nh disable hyphenation -.\" .hy enable hyphenation -.\" .ad l left justify -.\" .ad b justify to both left and right margins -.\" .nf disable filling -.\" .fi enable filling -.\" .br insert line break -.\" .sp insert n+1 empty lines -.\" for manpage-specific macros, see man(7) +.TH HOWDY 1 "April 9, 2018" "Howdy help" "User Commands" .SH NAME howdy \- Windows Hello style authentication for Ubuntu -.SH SYNOPSIS -.B howdy -.RI [ options ] " files" ... -.br -.B bar -.RI [ options ] " files" ... .SH DESCRIPTION -This manual page documents briefly the -.B nwsmtp -and -.B bar -commands. +Howdy IR face recognition implements a PAM module to use your face as a authentication method. +.SS "Usage:" +.IP +howdy [user] [argument] +.SS "Commands:" +.TP +help +Show this help page +.TP +list +List all saved face models for the current user +.TP +add +Add a new face model for the current user +.TP +remove +Remove a specific model +.TP +clear +Remove all face models for the current user +.TP +test +Test the camera and recognition methods .PP -.\" TeX users may be more comfortable with the \fB\fP and -.\" \fI\fP escape sequences to invode bold face and italics, -.\" respectively. -\fBnwsmtp\fP is a program that... -.SH OPTIONS -These programs follow the usual GNU command line syntax, with long -options starting with two dashes (`-'). -A summary of options is included below. -For a complete description, see the Info files. -.TP -.B \-h, \-\-help -Show summary of options. -.TP -.B \-v, \-\-version -Show version of program. -.SH SEE ALSO -.BR bar (1), -.BR baz (1). -.br -The programs are documented fully by -.IR "The Rise and Fall of a Fooish Bar" , -available via the Info system. .SH AUTHOR -nwsmtp was written by . -.PP -This manual page was written by Alexei Moisseev , -for the Debian project (and may be used by others). +Howdy was written by boltgolt. For more information visit https://github.com/Boltgolt/howdy diff --git a/debian/howdy.manpages b/debian/howdy.manpages new file mode 100644 index 0000000..2578acd --- /dev/null +++ b/debian/howdy.manpages @@ -0,0 +1 @@ +debian/howdy.1 diff --git a/debian/postinst b/debian/postinst index c1b45ec..618d92e 100755 --- a/debian/postinst +++ b/debian/postinst @@ -2,6 +2,13 @@ # Installation script to install howdy # Executed after primary apt install +def col(id): + """Add color escape sequences""" + if id == 1: return "\033[32m" + if id == 2: return "\033[33m" + if id == 3: return "\033[31m" + return "\033[0m" + # Import required modules import subprocess import time @@ -12,29 +19,34 @@ import signal import fileinput import urllib.parse +# Don't run unless we need to configure the install +# Will also happen on upgrade but we will catch that later on if "configure" not in sys.argv: sys.exit(0) + def log(text): """Print a nicely formatted line to stdout""" - print("\n>>> \033[32m" + text + "\033[0m\n") + print("\n>>> " + col(1) + text + col(0)"\n") def handleStatus(status): """Abort if a command fails""" if (status != 0): - print("\033[31mError while running last command\033[0m") + print(col(3) + "Error while running last command" + col(0)) sys.exit(1) -# We're not in fresh configuration mode, so exit +# We're not in fresh configuration mode (probably an upgrade), so exit if not os.path.exists("/tmp/howdy_picked_device"): sys.exit(0) +# Open the temporary file containing the device ID in_file = open("/tmp/howdy_picked_device", "r") -# Should be a string +# Load it in, it should be a string picked = in_file.read() in_file.close() +# Remove the temporary file subprocess.call(["rm /tmp/howdy_picked_device"], shell=True) log("Upgrading pip to the latest version") @@ -44,7 +56,7 @@ handleStatus(subprocess.call(["pip3 install --upgrade pip"], shell=True)) log("Cloning dlib") -# Clone the git to /tmp +# Clone the dlib git to /tmp, but only the last commit handleStatus(subprocess.call(["git", "clone", "--depth", "1", "https://github.com/davisking/dlib.git", "/tmp/dlib_clone"])) log("Building dlib") @@ -86,10 +98,7 @@ print("Permissions set") # Make the CLI executable as howdy handleStatus(subprocess.call(["ln -s /lib/security/howdy/cli.py /usr/local/bin/howdy"], shell=True)) handleStatus(subprocess.call(["chmod +x /usr/local/bin/howdy"], shell=True)) -print("Installing howdy command") - -# Install the command autocomplete, don't error on failure -# subprocess.call(["sudo cp /lib/security/howdy/autocomplete.sh /etc/bash_completion.d/howdy"], shell=True) +print("Howdy command installed") log("Adding howdy as PAM module") @@ -192,4 +201,4 @@ if "HOWDY_NO_PROMPT" not in os.environ: # Let the user know what to do with the link print("Installation complete.") -print("\033[33mIf you want to help the development, please use the link above to post some camera-related information to github!\033[0m") +print(col(2) + "If you want to help the development, please use the link above to post some camera-related information to github!" + col(0)) diff --git a/debian/preinst b/debian/preinst index d62616b..02960a8 100755 --- a/debian/preinst +++ b/debian/preinst @@ -3,6 +3,7 @@ # Executed before primary apt install of files def col(id): + """Add color escape sequences""" if id == 1: return "\033[32m" if id == 2: return "\033[33m" if id == 3: return "\033[31m" @@ -15,10 +16,11 @@ import os import re import signal +# Don't run if we're not trying to install fresh if "install" not in sys.argv: sys.exit(0) -# The picked video device id +# The default picked video device id picked = -1 print(col(1) + "Starting IR camera check...\n" + col(0)) @@ -27,6 +29,7 @@ print(col(1) + "Starting IR camera check...\n" + col(0)) if "HOWDY_NO_PROMPT" in os.environ: print(col(2) + "AUTOMATED INSTALL, YOU WILL NOT BE ASKED FOR INPUT AND CHECKS WILL BE SKIPPED" + col(0)) + # Write the default device to disk and exit with open("/tmp/howdy_picked_device", "w") as out_file: out_file.write("0") @@ -83,6 +86,7 @@ if picked == -1: print(col(3) + "No suitable IR camera found, aborting install." + col(0)) sys.exit(23) +# Write the result to disk so postinst can have a look at it with open("/tmp/howdy_picked_device", "w") as out_file: out_file.write(str(picked)) diff --git a/debian/prerm b/debian/prerm index cd693d2..8c32076 100755 --- a/debian/prerm +++ b/debian/prerm @@ -3,6 +3,7 @@ # Completely remove howdy from the system def col(id): + """Add color escape sequences""" if id == 1: return "\033[32m" if id == 2: return "\033[33m" if id == 3: return "\033[31m" @@ -11,6 +12,14 @@ def col(id): # Import required modules import subprocess +# Only run when we actually want to remove +if "remove" not in sys.argv and "purge" not in sys.argv: + sys.exit(0) + +# Don't try running this if it's already gome +if not os.path.exists("/lib/security/howdy/cli"): + sys.exit(0) + # Remove files and symlinks try: subprocess.call(["rm /usr/local/bin/howdy"], shell=True) From 5e221c15506b0f58e2e8862cdc77a6da043577e5 Mon Sep 17 00:00:00 2001 From: boltgolt Date: Fri, 13 Apr 2018 01:00:36 +0200 Subject: [PATCH 21/26] Quick fix --- README.md | 2 +- debian/postinst | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 89a0946..720f89d 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Howdy for Ubuntu [![](https://travis-ci.org/Boltgolt/howdy.svg?branch=dev)](https://travis-ci.org/Boltgolt/howdy) ![](https://img.shields.io/github/release/Boltgolt/howdy.svg) [![](https://img.shields.io/github/issues-raw/Boltgolt/howdy/enhancement.svg?label=feature+requests)](https://github.com/Boltgolt/howdy/issues?q=is%3Aissue+is%3Aopen+label%3Aenhancement) +# Howdy for Ubuntu [![](https://travis-ci.org/Boltgolt/howdy.svg?branch=dev)](https://travis-ci.org/Boltgolt/howdy) ![](https://img.shields.io/github/release/Boltgolt/howdy.svg?colorB=4c1) [![](https://img.shields.io/github/issues-raw/Boltgolt/howdy/enhancement.svg?label=feature+requests&colorB=4c1)](https://github.com/Boltgolt/howdy/issues?q=is%3Aissue+is%3Aopen+label%3Aenhancement) Windows Hello™ style authentication for Ubuntu. Use your built-in IR emitters and camera in combination with face recognition to prove who you are. diff --git a/debian/postinst b/debian/postinst index 618d92e..efe76a1 100755 --- a/debian/postinst +++ b/debian/postinst @@ -27,7 +27,7 @@ if "configure" not in sys.argv: def log(text): """Print a nicely formatted line to stdout""" - print("\n>>> " + col(1) + text + col(0)"\n") + print("\n>>> " + col(1) + text + col(0) + "\n") def handleStatus(status): """Abort if a command fails""" From b534442b9d2b671d4d8cc7d9da9e14ba5c86e315 Mon Sep 17 00:00:00 2001 From: boltgolt Date: Fri, 13 Apr 2018 14:54:06 +0200 Subject: [PATCH 22/26] Reworking command line --- .travis.yml | 11 ++++++----- README.md | 5 ++--- debian/prerm | 1 + src/cli.py | 52 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 61 insertions(+), 8 deletions(-) diff --git a/.travis.yml b/.travis.yml index d27dc7f..774d48c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,16 +1,17 @@ language: python sudo: required -before_install: +install: - sudo apt install devscripts dh-make -y - - debuild -i -us -uc -b - -install: sudo apt install ../*.deb -y script: + - debuild -i -us -uc -b + - sudo apt install ../*.deb -y - sudo howdy help - sudo howdy list - sudo apt purge howdy -y notifications: - email: true + email: + on_success: never + on_failure: always diff --git a/README.md b/README.md index 720f89d..02ab96c 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Howdy for Ubuntu [![](https://travis-ci.org/Boltgolt/howdy.svg?branch=dev)](https://travis-ci.org/Boltgolt/howdy) ![](https://img.shields.io/github/release/Boltgolt/howdy.svg?colorB=4c1) [![](https://img.shields.io/github/issues-raw/Boltgolt/howdy/enhancement.svg?label=feature+requests&colorB=4c1)](https://github.com/Boltgolt/howdy/issues?q=is%3Aissue+is%3Aopen+label%3Aenhancement) +# Howdy for Ubuntu [![](https://img.shields.io/travis/Boltgolt/howdy/dev.svg)](https://travis-ci.org/Boltgolt/howdy) [![](https://img.shields.io/github/release/Boltgolt/howdy.svg?colorB=4c1)](https://github.com/Boltgolt/howdy/releases) [![](https://img.shields.io/github/issues-raw/Boltgolt/howdy/enhancement.svg?label=feature+requests&colorB=4c1)](https://github.com/Boltgolt/howdy/issues?q=is%3Aissue+is%3Aopen+label%3Aenhancement) Windows Hello™ style authentication for Ubuntu. Use your built-in IR emitters and camera in combination with face recognition to prove who you are. @@ -35,7 +35,6 @@ howdy [user] [argument] | `clear` | Remove all face models for the given user | Yes | | `config` | Open the config file in nano | No | | `disable` | Disable or enable howdy | No | -| `help` | Show a help page | No | | `list` | List all saved face models for the given user | Yes | | `remove` | Remove a specific model for the given user | Yes | | `test` | Test the camera and recognition methods | No | @@ -50,6 +49,6 @@ If you encounter an error that hasn't been reported yet, don't be afraid to open This script is in no way as secure as a password and will never be. Although it's harder to fool than normal face recognition, a person who looks similar to you or well-printed photo of you could be enough to do it. -To minimize the chance of this script being compromised, it's recommend to leave this repo in /lib/security and to keep it read only. +To minimize the chance of this program being compromised, it's recommend to leave Howdy in /lib/security and to keep it read only. DO NOT USE HOWDY AS THE SOLE AUTHENTICATION METHOD FOR YOUR SYSTEM. diff --git a/debian/prerm b/debian/prerm index 8c32076..62f4552 100755 --- a/debian/prerm +++ b/debian/prerm @@ -11,6 +11,7 @@ def col(id): # Import required modules import subprocess +import sys # Only run when we actually want to remove if "remove" not in sys.argv and "purge" not in sys.argv: diff --git a/src/cli.py b/src/cli.py index bdbf064..203a455 100755 --- a/src/cli.py +++ b/src/cli.py @@ -4,6 +4,58 @@ # Import required modules import sys import os +import subprocess +import getpass +import argparse + +user = subprocess.check_output("echo $(logname 2>/dev/null || echo $SUDO_USER)", shell=True).decode("ascii").strip() + +if user == "root" or user == "": + env_user = getpass.getuser().strip() + + if env_user == "root" or env_user == "": + print("Could not determine user, please use the --user flag") + sys.exit(1) + else: + user = env_user + +parser = argparse.ArgumentParser(description="Command line interface for Howdy face authentication.", + formatter_class=argparse.RawDescriptionHelpFormatter, + add_help=False, + prog="howdy", + epilog="For support please visit\nhttps://github.com/Boltgolt/howdy") + + +parser.add_argument("command", + help="The command option to execute, can be one of the following: add, clear, config, disable, list, remove or test.", + metavar="command", + choices=["add", "clear", "config", "disable", "list", "remove", "test"]) + +parser.add_argument("argument", + help="Either 0 or 1 for the disable command, or the model ID for the remove command.", + nargs="?") + +parser.add_argument("-U", "--user", + default=user, + help="Set the user account to use.") + +parser.add_argument("-y", + help="Skip all questions.", + action="store_true") + +parser.add_argument("-h", "--help", + action="help", + default=argparse.SUPPRESS, + help="Show this help message and exit.") + +if len(sys.argv) < 2: + parser.print_help() + sys.exit(0) + +args = parser.parse_args() + +print(args) +sys.exit(1) # Check if if a command has been given and print help otherwise if (len(sys.argv) < 2): From 0cdb4b78bd5f6435fe3f90a991d44ef90ef29fd7 Mon Sep 17 00:00:00 2001 From: boltgolt Date: Fri, 13 Apr 2018 15:27:52 +0200 Subject: [PATCH 23/26] Command line & travis improvements --- .travis.yml | 23 ++++++++++++++++------- debian/prerm | 1 + src/cli.py | 7 ++++--- 3 files changed, 21 insertions(+), 10 deletions(-) diff --git a/.travis.yml b/.travis.yml index 774d48c..211962b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,15 +1,24 @@ -language: python sudo: required +language: python +python: + - "3.4" + - "3.6" install: - - sudo apt install devscripts dh-make -y + # Install build tools and ack-grep for checks + - sudo apt install devscripts dh-make ack-grep -y script: - - debuild -i -us -uc -b - - sudo apt install ../*.deb -y - - sudo howdy help - - sudo howdy list - - sudo apt purge howdy -y + # Build the binary (.deb) + - debuild -i -us -uc -b + # Install the binary, also fireing the debian scripts + - sudo apt install ../*.deb -y + # Check if the username passthough works correctly with sudo + - 'howdy | ack-grep --passthru --color "current active user: travis"' + - 'sudo howdy | ack-grep --passthru --color "current active user: travis"' + - sudo howdy list + # Remove howdy from the installation + - sudo apt purge howdy -y notifications: email: diff --git a/debian/prerm b/debian/prerm index 62f4552..542a63c 100755 --- a/debian/prerm +++ b/debian/prerm @@ -12,6 +12,7 @@ def col(id): # Import required modules import subprocess import sys +import os # Only run when we actually want to remove if "remove" not in sys.argv and "purge" not in sys.argv: diff --git a/src/cli.py b/src/cli.py index 203a455..05d0796 100755 --- a/src/cli.py +++ b/src/cli.py @@ -49,13 +49,14 @@ parser.add_argument("-h", "--help", help="Show this help message and exit.") if len(sys.argv) < 2: - parser.print_help() - sys.exit(0) + print("current active user: " + user + "\n") + parser.print_help() + sys.exit(0) args = parser.parse_args() print(args) -sys.exit(1) +sys.exit(0) # Check if if a command has been given and print help otherwise if (len(sys.argv) < 2): From 4f9e240869a2b22fc0fca6fe1c53ef55a5c57dfe Mon Sep 17 00:00:00 2001 From: boltgolt Date: Fri, 13 Apr 2018 21:19:28 +0200 Subject: [PATCH 24/26] Completed CLI rework --- README.md | 24 +++++----- src/cli.py | 115 ++++++++++++++++++++------------------------- src/cli/add.py | 35 ++++++++------ src/cli/clear.py | 23 +++++---- src/cli/config.py | 15 ++++++ src/cli/disable.py | 41 ++++++++++++++++ src/cli/help.py | 17 ------- src/cli/list.py | 3 +- src/cli/remove.py | 43 +++++++++-------- src/config.ini | 6 +++ src/pam.py | 4 ++ 11 files changed, 189 insertions(+), 137 deletions(-) create mode 100644 src/cli/config.py create mode 100644 src/cli/disable.py delete mode 100644 src/cli/help.py diff --git a/README.md b/README.md index 02ab96c..12b54a1 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ sudo apt update sudo apt install howdy ``` -This will guide you through the installation. When that's done run `sudo howdy add $USER` and replace `$USER` with your username to add a face model. +This will guide you through the installation. When that's done run `sudo howdy add` to add a face model. If nothing went wrong we should be able to run sudo by just showing your face. Open a new terminal and run `sudo -i` to see it in action. @@ -22,22 +22,22 @@ If nothing went wrong we should be able to run sudo by just showing your face. O ### Command line -The installer adds a `howdy` command to manage face models for the current user. Use `howdy help` to list the available options. +The installer adds a `howdy` command to manage face models for the current user. Use `howdy --help` to list the available options. Usage: ``` -howdy [user] [argument] +howdy [-U user] [-y] command [argument] ``` -| Command | Description | User required | -|-----------|-----------------------------------------------|---------------| -| `add` | Add a new face model for the given user | Yes | -| `clear` | Remove all face models for the given user | Yes | -| `config` | Open the config file in nano | No | -| `disable` | Disable or enable howdy | No | -| `list` | List all saved face models for the given user | Yes | -| `remove` | Remove a specific model for the given user | Yes | -| `test` | Test the camera and recognition methods | No | +| Command | Description | +|-----------|-----------------------------------------------| +| `add` | Add a new face model for the given user | +| `clear` | Remove all face models for the given user | +| `config` | Open the config file in nano | +| `disable` | Disable or enable howdy | +| `list` | List all saved face models for the given user | +| `remove` | Remove a specific model for the given user | +| `test` | Test the camera and recognition methods | ### Troubleshooting diff --git a/src/cli.py b/src/cli.py index 05d0796..3b1640f 100755 --- a/src/cli.py +++ b/src/cli.py @@ -7,102 +7,91 @@ import os import subprocess import getpass import argparse +import builtins +# Try to get the original username (not "root") from shell user = subprocess.check_output("echo $(logname 2>/dev/null || echo $SUDO_USER)", shell=True).decode("ascii").strip() +# If that fails, try to get the direct user if user == "root" or user == "": env_user = getpass.getuser().strip() - if env_user == "root" or env_user == "": + # If even that fails, error out + if env_user == "": print("Could not determine user, please use the --user flag") sys.exit(1) else: user = env_user +# Check if we have rootish rights +if os.getenv("SUDO_USER") is None: + print("Please run this command with sudo") + sys.exit(1) + +# Basic command setup parser = argparse.ArgumentParser(description="Command line interface for Howdy face authentication.", - formatter_class=argparse.RawDescriptionHelpFormatter, - add_help=False, - prog="howdy", - epilog="For support please visit\nhttps://github.com/Boltgolt/howdy") - + formatter_class=argparse.RawDescriptionHelpFormatter, + add_help=False, + prog="howdy", + epilog="For support please visit\nhttps://github.com/Boltgolt/howdy") +# Add an argument for the command parser.add_argument("command", - help="The command option to execute, can be one of the following: add, clear, config, disable, list, remove or test.", - metavar="command", - choices=["add", "clear", "config", "disable", "list", "remove", "test"]) + help="The command option to execute, can be one of the following: add, clear, config, disable, list, remove or test.", + metavar="command", + choices=["add", "clear", "config", "disable", "list", "remove", "test"]) +# Add an argument for the extra arguments of diable and remove parser.add_argument("argument", - help="Either 0 or 1 for the disable command, or the model ID for the remove command.", - nargs="?") + help="Either 0 (enable) or 1 (disable) for the disable command, or the model ID for the remove command.", + nargs="?") +# Add the user flag parser.add_argument("-U", "--user", - default=user, + default=user, help="Set the user account to use.") +# Add the -y flag parser.add_argument("-y", help="Skip all questions.", - action="store_true") + action="store_true") +# Overwrite the default help message so we can use a uppercase S parser.add_argument("-h", "--help", - action="help", - default=argparse.SUPPRESS, + action="help", + default=argparse.SUPPRESS, help="Show this help message and exit.") +# If we only have 1 argument we print the help text if len(sys.argv) < 2: print("current active user: " + user + "\n") parser.print_help() sys.exit(0) +# Parse all arguments above args = parser.parse_args() -print(args) -sys.exit(0) +# Save the args and user as builtins which can be accessed by the imports +builtins.howdy_args = args +builtins.howdy_user = args.user -# Check if if a command has been given and print help otherwise -if (len(sys.argv) < 2): - print("Howdy IR face recognition help") - import cli.help - sys.exit() +# Beond this point the user can't change anymore, if we still have root as user we need to abort +if args.user == "root": + print("Can't run howdy commands as root, please run this command with the --user flag") + sys.exit(1) -# The command given -cmd = sys.argv[1] - -# Call the right files for commands that don't need root -if cmd == "help": - print("Howdy IR face recognition") - import cli.help -elif cmd == "test": +# Execute the right command +if args.command == "add": + import cli.add +elif args.command == "clear": + import cli.clear +elif args.command == "config": + import cli.config +elif args.command == "disable": + import cli.disable +elif args.command == "list": + import cli.list +elif args.command == "remove": + import cli.remove +elif args.command == "test": import cli.test -else: - # Check if the minimum of 3 arugemnts has been met and print help otherwise - if (len(sys.argv) < 3): - print("Howdy IR face recognition help") - import cli.help - sys.exit() - - # Requre sudo for comamnds that need root rights to read the model files - if os.getenv("SUDO_USER") is None: - print("Please run this command with sudo") - sys.exit(1) - - # Frome here on we require the second argument to be the username, - # switching the command to the 3rd - cmd = sys.argv[2] - - if cmd == "list": - import cli.list - elif cmd == "add": - import cli.add - elif cmd == "remove": - import cli.remove - elif cmd == "clear": - import cli.clear - else: - # If the comand is invalid, check if the user hasn't swapped the username and command - if sys.argv[1] in ["list", "add", "remove", "clear"]: - print("Usage: howdy ") - sys.exit(1) - else: - print('Unknown command "' + cmd + '"') - import cli.help - sys.exit(1) diff --git a/src/cli/add.py b/src/cli/add.py index 427ce4d..c9f0594 100644 --- a/src/cli/add.py +++ b/src/cli/add.py @@ -8,6 +8,7 @@ import sys import json import cv2 import configparser +import builtins # Try to import face_recognition and give a nice error if we can't # Add should be the first point where import issues show up @@ -18,7 +19,7 @@ except ImportError as err: print("\nCan't import the face_recognition module, check the output of") print("pip3 show face_recognition") - sys.exit() + sys.exit(1) # Get the absolute path to the current file path = os.path.dirname(os.path.abspath(__file__)) @@ -27,8 +28,7 @@ path = os.path.dirname(os.path.abspath(__file__)) config = configparser.ConfigParser() config.read(path + "/../config.ini") -# The current user -user = sys.argv[1] +user = builtins.howdy_user # The permanent file to store the encoded model in enc_file = path + "/../models/" + user + ".dat" # Known encodings @@ -46,11 +46,11 @@ except FileNotFoundError: encodings = [] # Print a warning if too many encodings are being added -if len(encodings) > 2: +if len(encodings) > 3: print("WARNING: Every additional model slows down the face recognition engine") - print("Press ctrl+C to cancel") + print("Press ctrl+C to cancel\n") -print("Adding face model for the user account " + user) +print("Adding face model for the user " + user) # Set the default label label = "Initial model" @@ -59,12 +59,16 @@ label = "Initial model" if len(encodings) > 0: label = "Model #" + str(len(encodings) + 1) -# Ask the user for a custom label -label_in = input("Enter a label for this new model [" + label + "]: ") +# Keep de default name if we can't ask questions +if builtins.howdy_args.y: + print("Using default label \"" + label + "\" because of -y flag") +else: + # Ask the user for a custom label + label_in = input("Enter a label for this new model [" + label + "]: ") -# Set the custom label (if any) and limit it to 24 characters -if label_in != "": - label = label_in[:24] + # Set the custom label (if any) and limit it to 24 characters + if label_in != "": + label = label_in[:24] # Prepare the metadata for insertion insert_model = { @@ -106,12 +110,12 @@ while frames < 60: # If 0 faces are detected we can't continue if len(enc) == 0: print("No face detected, aborting") - sys.exit() + sys.exit(1) # If more than 1 faces are detected we can't know wich one belongs to the user if len(enc) > 1: print("Multiple faces detected, aborting") - sys.exit() + sys.exit(1) # Totally clean array that can be exported as JSON clean_enc = [] @@ -130,5 +134,6 @@ with open(enc_file, "w") as datafile: json.dump(encodings, datafile) # Give let the user know how it went -print("Scan complete") -print("\nAdded a new model to " + user) +print("""Scan complete + +Added a new model to """ + user) diff --git a/src/cli/clear.py b/src/cli/clear.py index 4cd56c2..817de48 100644 --- a/src/cli/clear.py +++ b/src/cli/clear.py @@ -3,30 +3,33 @@ # Import required modules import os import sys +import builtins # Get the full path to this file path = os.path.dirname(os.path.abspath(__file__)) # Get the passed user -user = sys.argv[1] +user = builtins.howdy_user # Check if the models folder is there if not os.path.exists(path + "/../models"): print("No models created yet, can't clear them if they don't exist") - sys.exit() + sys.exit(1) # Check if the user has a models file to delete if not os.path.isfile(path + "/../models/" + user + ".dat"): print(user + " has no models or they have been cleared already") - sys.exit() + sys.exit(1) -# Double check with the user -print("This will clear all models for " + user) -ans = input("Do you want to continue [y/N]: ") +# Only ask the user if there's no -y flag +if not builtins.howdy_args.y: + # Double check with the user + print("This will clear all models for " + user) + ans = input("Do you want to continue [y/N]: ") -# Abort if they don't answer y or Y -if (ans.lower() != "y"): - print('\nInerpeting as a "NO"') - sys.exit() + # Abort if they don't answer y or Y + if (ans.lower() != "y"): + print('\nInerpeting as a "NO"') + sys.exit(1) # Delete otherwise os.remove(path + "/../models/" + user + ".dat") diff --git a/src/cli/config.py b/src/cli/config.py new file mode 100644 index 0000000..d0471a5 --- /dev/null +++ b/src/cli/config.py @@ -0,0 +1,15 @@ +# Open the config file in gedit + +# Import required modules +import os +import time +import subprocess + +# Let the user know what we're doing +print("Opening condig.ini in gedit") + +# Open gedit as a subprocess and fork it +subprocess.Popen(["gedit", os.path.dirname(os.path.realpath(__file__)) + "/../config.ini"], + cwd="/", + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT) diff --git a/src/cli/disable.py b/src/cli/disable.py new file mode 100644 index 0000000..2c2b03b --- /dev/null +++ b/src/cli/disable.py @@ -0,0 +1,41 @@ +# Set the disable flag + +# Import required modules +import sys +import os +import json +import builtins +import fileinput +import configparser + +# Get the absolute filepath +config_path = os.path.dirname(os.path.abspath(__file__)) + "/../config.ini" + +# Read config from disk +config = configparser.ConfigParser() +config.read(config_path) + +# Check if enough arguments have been passed +if builtins.howdy_args.argument == None: + print("Please add a 0 (enable) or a 1 (disable) as an argument") + sys.exit(1) + +# Translate the argument to the right string +if builtins.howdy_args.argument == "1": + out_value = "true" +elif builtins.howdy_args.argument == "0": + out_value = "false" +else: + # Of it's not a 0 or a 1, it's invalid + print("Please only use a 0 (enable) or a 1 (disable) as an argument") + sys.exit(1) + +# Loop though the config file and only replace the line containing the disable config +for line in fileinput.input([config_path], inplace=1): + print(line.replace("disabled = " + config.get("core", "disabled"), "disabled = " + out_value), end="") + +# Print what we just did +if builtins.howdy_args.argument == "1": + print("Howdy has been disabled") +else: + print("Howdy has been enabled") diff --git a/src/cli/help.py b/src/cli/help.py deleted file mode 100644 index 2bbd879..0000000 --- a/src/cli/help.py +++ /dev/null @@ -1,17 +0,0 @@ -# Prints a simple help page for the CLI - -print(""" -Usage: - howdy [argument] - -Commands: - help Show this help page - list List all saved face models for the current user - add Add a new face model for the current user - remove [id] Remove a specific model - clear Remove all face models for the current user - test Test the camera and recognition methods - -For support please visit -https://github.com/Boltgolt/howdy\ -""") diff --git a/src/cli/list.py b/src/cli/list.py index ff37b10..061bf49 100644 --- a/src/cli/list.py +++ b/src/cli/list.py @@ -5,10 +5,11 @@ import sys import os import json import time +import builtins # Get the absolute path and the username path = os.path.dirname(os.path.realpath(__file__)) + "/.." -user = sys.argv[1] +user = builtins.howdy_user # Check if the models file has been created yet if not os.path.exists(path + "/models"): diff --git a/src/cli/remove.py b/src/cli/remove.py index 4322839..deef825 100644 --- a/src/cli/remove.py +++ b/src/cli/remove.py @@ -4,22 +4,23 @@ import sys import os import json +import builtins # Get the absolute path and the username path = os.path.dirname(os.path.realpath(__file__)) + "/.." -user = sys.argv[1] +user = builtins.howdy_user # Check if enough arguments have been passed -if len(sys.argv) == 3: - print("Please add the ID of the model to remove as an argument") +if builtins.howdy_args.argument == None: + print("Please add the ID of the model you want to remove as an argument") print("You can find the IDs by running:") - print("\n\thowdy " + user + " list\n") + print("\n\thowdy list\n") sys.exit(1) # Check if the models file has been created yet if not os.path.exists(path + "/models"): print("Face models have not been initialized yet, please run:") - print("\n\thowdy " + user + " add\n") + print("\n\thowdy add\n") sys.exit(1) # Path to the models file @@ -30,7 +31,7 @@ try: encodings = json.load(open(enc_file)) except FileNotFoundError: print("No face model known for the user " + user + ", please run:") - print("\n\thowdy " + user + " add\n") + print("\n\thowdy add\n") sys.exit(1) # Tracks if a encoding with that id has been found @@ -38,24 +39,28 @@ found = False # Loop though all encodings and check if they match the argument for enc in encodings: - if str(enc["id"]) == sys.argv[3]: - # Double check with the user - print('This will remove the model called "' + enc["label"] + '" for ' + user) - ans = input("Do you want to continue [y/N]: ") + if str(enc["id"]) == builtins.howdy_args.argument: + # Only ask the user if there's no -y flag + if not builtins.howdy_args.y: + # Double check with the user + print('This will remove the model called "' + enc["label"] + '" for ' + user) + ans = input("Do you want to continue [y/N]: ") - # Abort if the answer isn't yes - if (ans.lower() != "y"): - print('\nInerpeting as a "NO"') - sys.exit() + # Abort if the answer isn't yes + if (ans.lower() != "y"): + print('\nInerpeting as a "NO"') + sys.exit() + + # Add a padding empty line + print() # Mark as found and print an enter found = True - print() break # Abort if no matching id was found if not found: - print("No model with ID " + sys.argv[3] + " exists for " + user) + print("No model with ID " + builtins.howdy_args.argument + " exists for " + user) sys.exit() # Remove the entire file if this encoding is the only one @@ -68,11 +73,11 @@ else: # Loop though all encodin and only add thos that don't need to be removed for enc in encodings: - if str(enc["id"]) != sys.argv[3]: + if str(enc["id"]) != builtins.howdy_args.argument: new_encodings.append(enc) - # Save this new set to disk + # Save this new set to disk with open(enc_file, "w") as datafile: json.dump(new_encodings, datafile) - print("Removed model " + sys.argv[3]) + print("Removed model " + builtins.howdy_args.argument) diff --git a/src/config.ini b/src/config.ini index 3076f96..b80f2f3 100644 --- a/src/config.ini +++ b/src/config.ini @@ -1,3 +1,5 @@ +# Howdy config file + [core] # Do not print anything when a face verification succeeds no_confirmation = false @@ -11,6 +13,10 @@ suppress_unknown = false # Expirimental, can behave incorrectly on some systems dismiss_lockscreen = false +# Disable howdy in the PAM +# The howdy command will still function +disabled = false + [video] # The certainty of the detected face belonging to the user of the account # On a scale from 1 to 10, values above 5 are not recommended diff --git a/src/pam.py b/src/pam.py index 8c4eabc..13e571b 100644 --- a/src/pam.py +++ b/src/pam.py @@ -15,6 +15,10 @@ config.read(os.path.dirname(os.path.abspath(__file__)) + "/config.ini") def doAuth(pamh): """Start authentication in a seperate process""" + # Abort is Howdy is disabled + if config.get("core", "disabled") == "true": + sys.exit(0) + # Run compare as python3 subprocess to circumvent python version and import issues status = subprocess.call(["python3", os.path.dirname(os.path.abspath(__file__)) + "/compare.py", pamh.get_user()]) From 305603b0da2d6e179dfb52bf6543603cbb56b8ea Mon Sep 17 00:00:00 2001 From: boltgolt Date: Fri, 13 Apr 2018 22:40:01 +0200 Subject: [PATCH 25/26] Fixes, getting ready for release --- .travis.yml | 1 - README.md | 16 +++++++++------- debian/changelog | 14 +++++++++++++- debian/howdy.1 | 42 +++++++++++++++++++++++++++++------------- src/cli.py | 11 ++++++----- 5 files changed, 57 insertions(+), 27 deletions(-) diff --git a/.travis.yml b/.travis.yml index 211962b..bbd8344 100644 --- a/.travis.yml +++ b/.travis.yml @@ -16,7 +16,6 @@ script: # Check if the username passthough works correctly with sudo - 'howdy | ack-grep --passthru --color "current active user: travis"' - 'sudo howdy | ack-grep --passthru --color "current active user: travis"' - - sudo howdy list # Remove howdy from the installation - sudo apt purge howdy -y diff --git a/README.md b/README.md index 12b54a1..62918c5 100644 --- a/README.md +++ b/README.md @@ -14,15 +14,17 @@ sudo apt update sudo apt install howdy ``` +**Note:** The build of dlib can hang on 100% for over a minute, give it time. + This will guide you through the installation. When that's done run `sudo howdy add` to add a face model. If nothing went wrong we should be able to run sudo by just showing your face. Open a new terminal and run `sudo -i` to see it in action. -**Note:** The build of dlib can hang on 100% for over a minute, give it time. +If you're curious you can run `sudo howdy config` to open the central config file and see the options Howdy has. ### Command line -The installer adds a `howdy` command to manage face models for the current user. Use `howdy --help` to list the available options. +The installer adds a `howdy` command to manage face models for the current user. Use `howdy --help` or `man howdy` to list the available options. Usage: ``` @@ -31,12 +33,12 @@ howdy [-U user] [-y] command [argument] | Command | Description | |-----------|-----------------------------------------------| -| `add` | Add a new face model for the given user | -| `clear` | Remove all face models for the given user | -| `config` | Open the config file in nano | +| `add` | Add a new face model for an user | +| `clear` | Remove all face models for an user | +| `config` | Open the config file in gedit | | `disable` | Disable or enable howdy | -| `list` | List all saved face models for the given user | -| `remove` | Remove a specific model for the given user | +| `list` | List all saved face models for an user | +| `remove` | Remove a specific model for an user | | `test` | Test the camera and recognition methods | ### Troubleshooting diff --git a/debian/changelog b/debian/changelog index a8959b9..b9f4dda 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,9 +1,21 @@ +howdy (2.0.1) xenial; urgency=medium + + * First complete PPA release + + -- boltgolt Fri, 13 Apr 2018 22:22:27 +0200 + +howdy (2.0.0-alpha+4) xenial; urgency=medium + + * Reworked CLI + + -- boltgolt Fri, 13 Apr 2018 22:07:27 +0200 + howdy (2.0.0-alpha+3) xenial; urgency=medium * Fixed issue where dlib dependency failed to install on some installations * Added preinst script for camera detection - -- boltgolt Thu, 12 Apr 2018 21:42:42 +0000 + -- boltgolt Thu, 12 Apr 2018 21:42:42 +0200 howdy (2.0.0-alpha+2) xenial; urgency=medium diff --git a/debian/howdy.1 b/debian/howdy.1 index 8d3d47b..7254e43 100644 --- a/debian/howdy.1 +++ b/debian/howdy.1 @@ -6,26 +6,42 @@ howdy \- Windows Hello style authentication for Ubuntu Howdy IR face recognition implements a PAM module to use your face as a authentication method. .SS "Usage:" .IP -howdy [user] [argument] +howdy [\-U USER] [\-y] [\-h] command [argument] .SS "Commands:" .TP -help -Show this help page -.TP -list -List all saved face models for the current user -.TP add -Add a new face model for the current user -.TP -remove -Remove a specific model +Add a new face model for an user. .TP clear -Remove all face models for the current user +Remove all face models for an user. +.TP +config +Open the config file in gedit. +.TP +disable +Disable or enable howdy. +.TP +list +List all saved face models for an user. +.TP +remove +Remove a specific model for an user. +.TP +clear +Remove all face models for an user. .TP test -Test the camera and recognition methods +Test the camera and recognition methods. +.SS "Optional arguments:" +.TP +\fB\-U\fR USER, \fB\-\-user\fR USER +Set the user account to use. +.TP +\fB\-y\fR +Skip all questions. +.TP +\fB\-h\fR, \fB\-\-help\fR +Show this help message and exit. .PP .SH AUTHOR Howdy was written by boltgolt. For more information visit https://github.com/Boltgolt/howdy diff --git a/src/cli.py b/src/cli.py index 3b1640f..8ec350a 100755 --- a/src/cli.py +++ b/src/cli.py @@ -23,11 +23,6 @@ if user == "root" or user == "": else: user = env_user -# Check if we have rootish rights -if os.getenv("SUDO_USER") is None: - print("Please run this command with sudo") - sys.exit(1) - # Basic command setup parser = argparse.ArgumentParser(description="Command line interface for Howdy face authentication.", formatter_class=argparse.RawDescriptionHelpFormatter, @@ -75,6 +70,12 @@ args = parser.parse_args() builtins.howdy_args = args builtins.howdy_user = args.user +# Check if we have rootish rights +# This is this far down the file so running the command for help is always possible +if os.getenv("SUDO_USER") is None: + print("Please run this command with sudo") + sys.exit(1) + # Beond this point the user can't change anymore, if we still have root as user we need to abort if args.user == "root": print("Can't run howdy commands as root, please run this command with the --user flag") From 21d6616b4217474bf48ff93f8632198381e2cbdf Mon Sep 17 00:00:00 2001 From: boltgolt Date: Fri, 13 Apr 2018 23:54:50 +0200 Subject: [PATCH 26/26] Ready to merge --- README.md | 2 +- debian/changelog | 11 +++-------- debian/control | 6 +++--- 3 files changed, 7 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 62918c5..bf29b42 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Howdy for Ubuntu [![](https://img.shields.io/travis/Boltgolt/howdy/dev.svg)](https://travis-ci.org/Boltgolt/howdy) [![](https://img.shields.io/github/release/Boltgolt/howdy.svg?colorB=4c1)](https://github.com/Boltgolt/howdy/releases) [![](https://img.shields.io/github/issues-raw/Boltgolt/howdy/enhancement.svg?label=feature+requests&colorB=4c1)](https://github.com/Boltgolt/howdy/issues?q=is%3Aissue+is%3Aopen+label%3Aenhancement) +# Howdy for Ubuntu [![](https://img.shields.io/travis/Boltgolt/howdy/master.svg)](https://travis-ci.org/Boltgolt/howdy) [![](https://img.shields.io/github/release/Boltgolt/howdy.svg?colorB=4c1)](https://github.com/Boltgolt/howdy/releases) [![](https://img.shields.io/github/issues-raw/Boltgolt/howdy/enhancement.svg?label=feature+requests&colorB=4c1)](https://github.com/Boltgolt/howdy/issues?q=is%3Aissue+is%3Aopen+label%3Aenhancement) Windows Hello™ style authentication for Ubuntu. Use your built-in IR emitters and camera in combination with face recognition to prove who you are. diff --git a/debian/changelog b/debian/changelog index b9f4dda..ca45c85 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,21 +1,16 @@ -howdy (2.0.1) xenial; urgency=medium +howdy (2.1.0) xenial; urgency=medium * First complete PPA release - - -- boltgolt Fri, 13 Apr 2018 22:22:27 +0200 - -howdy (2.0.0-alpha+4) xenial; urgency=medium - * Reworked CLI - -- boltgolt Fri, 13 Apr 2018 22:07:27 +0200 + -- boltgolt Fri, 13 Apr 2018 22:22:27 +0200 howdy (2.0.0-alpha+3) xenial; urgency=medium * Fixed issue where dlib dependency failed to install on some installations * Added preinst script for camera detection - -- boltgolt Thu, 12 Apr 2018 21:42:42 +0200 + -- boltgolt Thu, 12 Apr 2018 21:42:42 +0000 howdy (2.0.0-alpha+2) xenial; urgency=medium diff --git a/debian/control b/debian/control index 872fcf6..6668311 100644 --- a/debian/control +++ b/debian/control @@ -1,15 +1,15 @@ Source: howdy Section: misc Priority: optional -Standards-Version: 3 -Build-Depends: python, dh-python, devscripts, dh-make +Standards-Version: 3.9.7 +Build-Depends: python, dh-python, devscripts, dh-make, debhelper Maintainer: boltgolt Vcs-Git: https://github.com/Boltgolt/howdy Package: howdy Homepage: https://github.com/Boltgolt/howdy Architecture: all -Depends: git, python3, python3-pip, python3-dev, python3-setuptools, build-essential, libpam-python, fswebcam, libopencv-dev, python-opencv, cmake +Depends: ${misc:Depends}, git, python3, python3-pip, python3-dev, python3-setuptools, build-essential, libpam-python, fswebcam, libopencv-dev, python-opencv, cmake Description: Windows Hello style authentication for Ubuntu. Use your built-in IR emitters and camera in combination with face recognition to prove who you are.