aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlexey Yushkin <636808@mail.ru>2023-04-03 14:06:36 +0000
committerGleb Popov <arrowd@FreeBSD.org>2023-04-03 14:15:32 +0000
commit93eaa5412bfca8daf12b7c81110c81b83211f54e (patch)
treed5d2c0f9fdc631003a61b76ba6e376f829aa1054
parent858e56322963a966b76a2dd8ead9e24cb196120a (diff)
downloadports-93eaa5412bfca8daf12b7c81110c81b83211f54e.tar.gz
ports-93eaa5412bfca8daf12b7c81110c81b83211f54e.zip
security/howdy: Face recognition based authentication provider.
Co-authored-by: Alexey Donskov <voxnod@gmail.com> Co-authored-by: Gleb Popov <arrowd@FreeBSD.org> Sponsored by: Serenity Cybersecurity, LLC
-rw-r--r--security/Makefile1
-rw-r--r--security/howdy/Makefile52
-rw-r--r--security/howdy/distinfo3
-rw-r--r--security/howdy/files/patch-paths429
-rw-r--r--security/howdy/pkg-descr3
-rw-r--r--security/howdy/pkg-plist27
6 files changed, 515 insertions, 0 deletions
diff --git a/security/Makefile b/security/Makefile
index 034e957031c0..2f61c05f25c7 100644
--- a/security/Makefile
+++ b/security/Makefile
@@ -218,6 +218,7 @@
SUBDIR += honeytrap
SUBDIR += honggfuzz
SUBDIR += horcrux
+ SUBDIR += howdy
SUBDIR += hpenc
SUBDIR += hs-cryptol
SUBDIR += hydra
diff --git a/security/howdy/Makefile b/security/howdy/Makefile
new file mode 100644
index 000000000000..675fca7c5aca
--- /dev/null
+++ b/security/howdy/Makefile
@@ -0,0 +1,52 @@
+PORTNAME= howdy
+PORTVERSION= 3.0.0.b
+CATEGORIES= security
+
+MAINTAINER= arrowd@FreeBSD.org
+COMMENT= Windows Hello(TM) style authentication provider
+WWW= https://github.com/boltgolt/howdy
+
+LICENSE= MIT
+LICENSE_FILE= ${WRKSRC}/LICENSE
+
+RUN_DEPENDS= opencv>0:graphics/opencv \
+ ${PYTHON_PKGNAMEPREFIX}python-pam>0:security/py-python-pam@${PY_FLAVOR} \
+ ${PYTHON_PKGNAMEPREFIX}dlib>0:science/py-dlib@${PY_FLAVOR} \
+ ${PYTHON_PKGNAMEPREFIX}numpy>0:math/py-numpy@${PY_FLAVOR}
+
+USES= python:run shebangfix
+
+USE_GITHUB= yes
+GH_ACCOUNT= boltgolt
+GH_PROJECT= howdy
+GH_TAGNAME= 30728a6d3634479c24ffd4e094c34a30bbb43058
+
+SHEBANG_GLOB= *.py
+
+NO_BUILD= yes
+NO_ARCH= yes
+STRIP=
+
+post-patch:
+ ${ECHO_CMD} 'config_dir = "${ETCDIR}"' >> ${WRKSRC}/howdy/src/paths.py
+ ${ECHO_CMD} 'dlib_data_dir = "${LOCALBASE}/share/dlib-data/"' >> ${WRKSRC}/howdy/src/paths.py
+ ${ECHO_CMD} 'user_models_dir = "/var/db/howdy/models/"' >> ${WRKSRC}/howdy/src/paths.py
+
+do-install:
+ ${MKDIR} ${STAGEDIR}${PREFIX}/libexec/howdy
+ cd ${WRKSRC}/howdy/src/ && \
+ ${COPYTREE_SHARE} . ${STAGEDIR}${PREFIX}/libexec/howdy
+ ${MKDIR} ${STAGEDIR}${ETCDIR}
+ ${MV} ${STAGEDIR}${PREFIX}/libexec/howdy/config.ini ${STAGEDIR}${ETCDIR}/config.ini.sample
+ ${LN} -s ../libexec/howdy/cli.py ${STAGEDIR}${PREFIX}/bin/howdy
+ ${INSTALL_PROGRAM} ${WRKSRC}/howdy/src/cli.py ${STAGEDIR}${PREFIX}/libexec/howdy
+
+ ${MKDIR} ${STAGEDIR}${PREFIX}/share/bash-completion/completions
+ ${INSTALL_DATA} ${WRKSRC}/howdy/src/autocomplete/howdy ${STAGEDIR}${PREFIX}/share/bash-completion/completions/
+
+ ${RM} -r ${STAGEDIR}${PREFIX}/libexec/howdy/autocomplete
+ ${RM} -r ${STAGEDIR}${PREFIX}/libexec/howdy/dlib-data
+ ${RM} -r ${STAGEDIR}${PREFIX}/libexec/howdy/pam
+ ${RM} -r ${STAGEDIR}${PREFIX}/libexec/howdy/pam-config
+
+.include <bsd.port.mk>
diff --git a/security/howdy/distinfo b/security/howdy/distinfo
new file mode 100644
index 000000000000..8b91fb02664b
--- /dev/null
+++ b/security/howdy/distinfo
@@ -0,0 +1,3 @@
+TIMESTAMP = 1680193772
+SHA256 (boltgolt-howdy-3.0.0.b-30728a6d3634479c24ffd4e094c34a30bbb43058_GH0.tar.gz) = 76078fbfb48a186678476cf9844987c97793a71135f1e612d761350c6b27fa42
+SIZE (boltgolt-howdy-3.0.0.b-30728a6d3634479c24ffd4e094c34a30bbb43058_GH0.tar.gz) = 130651
diff --git a/security/howdy/files/patch-paths b/security/howdy/files/patch-paths
new file mode 100644
index 000000000000..57e375f2dea5
--- /dev/null
+++ b/security/howdy/files/patch-paths
@@ -0,0 +1,429 @@
+From 30034c66d72e8e15e2ad5db68d1a5940df2568b6 Mon Sep 17 00:00:00 2001
+From: Gleb Popov <6yearold@gmail.com>
+Date: Thu, 30 Mar 2023 21:55:11 +0300
+Subject: [PATCH] Put all path variables into a separate module.
+
+This makes it easier for downstream packagers to customize where howdy installs
+its files.
+---
+ howdy/src/cli/add.py | 22 ++++++++++------------
+ howdy/src/cli/clear.py | 9 ++++-----
+ howdy/src/cli/config.py | 3 ++-
+ howdy/src/cli/disable.py | 3 ++-
+ howdy/src/cli/list.py | 7 +++----
+ howdy/src/cli/remove.py | 9 ++++-----
+ howdy/src/cli/set.py | 3 ++-
+ howdy/src/cli/snap.py | 5 ++---
+ howdy/src/cli/test.py | 11 ++++++-----
+ howdy/src/compare.py | 18 ++++++++----------
+ howdy/src/paths.py | 12 ++++++++++++
+ howdy/src/snapshot.py | 15 ++++++---------
+ 12 files changed, 61 insertions(+), 56 deletions(-)
+ create mode 100644 howdy/src/paths.py
+
+diff --git a/howdy/src/cli/add.py b/howdy/src/cli/add.py
+index 7a6d9eca..5a63bdfe 100644
+--- a/howdy/src/cli/add.py
++++ ./howdy/src/cli/add.py
+@@ -8,6 +8,7 @@
+ import configparser
+ import builtins
+ import numpy as np
++import paths
+
+ from recorders.video_capture import VideoCapture
+ from i18n import _
+@@ -26,39 +27,36 @@
+ # OpenCV needs to be imported after dlib
+ import cv2
+
+-# Define the absolute path to the config directory
+-config_path = "/etc/howdy"
+-
+ # Test if at lest 1 of the data files is there and abort if it's not
+-if not os.path.isfile(config_path + "/dlib-data/shape_predictor_5_face_landmarks.dat"):
++if not os.path.isfile(paths.dlib_data_dir + "shape_predictor_5_face_landmarks.dat"):
+ print(_("Data files have not been downloaded, please run the following commands:"))
+- print("\n\tcd " + config_path + "/dlib-data")
++ print("\n\tcd " + paths.dlib_data_dir)
+ print("\tsudo ./install.sh\n")
+ sys.exit(1)
+
+ # Read config from disk
+ config = configparser.ConfigParser()
+-config.read(config_path + "/config.ini")
++config.read(paths.config_dir + "/config.ini")
+
+ use_cnn = config.getboolean("core", "use_cnn", fallback=False)
+ if use_cnn:
+- face_detector = dlib.cnn_face_detection_model_v1(config_path + "/dlib-data/mmod_human_face_detector.dat")
++ face_detector = dlib.cnn_face_detection_model_v1(paths.dlib_data_dir + "mmod_human_face_detector.dat")
+ else:
+ face_detector = dlib.get_frontal_face_detector()
+
+-pose_predictor = dlib.shape_predictor(config_path + "/dlib-data/shape_predictor_5_face_landmarks.dat")
+-face_encoder = dlib.face_recognition_model_v1(config_path + "/dlib-data/dlib_face_recognition_resnet_model_v1.dat")
++pose_predictor = dlib.shape_predictor(paths.dlib_data_dir + "shape_predictor_5_face_landmarks.dat")
++face_encoder = dlib.face_recognition_model_v1(paths.dlib_data_dir + "dlib_face_recognition_resnet_model_v1.dat")
+
+ user = builtins.howdy_user
+ # The permanent file to store the encoded model in
+-enc_file = config_path + "/models/" + user + ".dat"
++enc_file = paths.user_models_dir + user + ".dat"
+ # Known encodings
+ encodings = []
+
+ # Make the ./models folder if it doesn't already exist
+-if not os.path.exists(config_path + "/models"):
++if not os.path.exists(paths.user_models_dir):
+ print(_("No face model folder found, creating one"))
+- os.makedirs(config_path + "/models")
++ os.makedirs(paths.user_models_dir)
+
+ # To try read a premade encodings file if it exists
+ try:
+diff --git a/howdy/src/cli/clear.py b/howdy/src/cli/clear.py
+index 6fa5f3ef..aa43e152 100644
+--- a/howdy/src/cli/clear.py
++++ ./howdy/src/cli/clear.py
+@@ -4,21 +4,20 @@
+ import os
+ import sys
+ import builtins
++import paths
+
+ from i18n import _
+
+-# Get the full path to this file
+-path = "/etc/howdy/models"
+ # Get the passed user
+ user = builtins.howdy_user
+
+ # Check if the models folder is there
+-if not os.path.exists(path):
++if not os.path.exists(paths.user_models_dir):
+ print(_("No models created yet, can't clear them if they don't exist"))
+ sys.exit(1)
+
+ # Check if the user has a models file to delete
+-if not os.path.isfile(path + "/" + user + ".dat"):
++if not os.path.isfile(paths.user_models_dir + user + ".dat"):
+ print(_("{} has no models or they have been cleared already").format(user))
+ sys.exit(1)
+
+@@ -34,5 +33,5 @@
+ sys.exit(1)
+
+ # Delete otherwise
+-os.remove(path + "/" + user + ".dat")
++os.remove(paths.user_models_dir + user + ".dat")
+ print(_("\nModels cleared"))
+diff --git a/howdy/src/cli/config.py b/howdy/src/cli/config.py
+index 71064839..04c51798 100644
+--- a/howdy/src/cli/config.py
++++ ./howdy/src/cli/config.py
+@@ -3,6 +3,7 @@
+ # Import required modules
+ import os
+ import subprocess
++import paths
+
+ from i18n import _
+
+@@ -19,4 +20,4 @@
+ editor = "/etc/alternatives/editor"
+
+ # Open the editor as a subprocess and fork it
+-subprocess.call([editor, "/etc/howdy/config.ini"])
++subprocess.call([editor, paths.config_dir + "config.ini"])
+diff --git a/howdy/src/cli/disable.py b/howdy/src/cli/disable.py
+index be78c97f..1f655412 100644
+--- a/howdy/src/cli/disable.py
++++ ./howdy/src/cli/disable.py
+@@ -6,11 +6,12 @@
+ import builtins
+ import fileinput
+ import configparser
++import paths
+
+ from i18n import _
+
+ # Get the absolute filepath
+-config_path = os.path.dirname("/etc/howdy") + "/config.ini"
++config_path = os.path.dirname(paths.config_dir) + "/config.ini"
+
+ # Read config from disk
+ config = configparser.ConfigParser()
+diff --git a/howdy/src/cli/list.py b/howdy/src/cli/list.py
+index 3532e9f8..7539837d 100644
+--- a/howdy/src/cli/list.py
++++ ./howdy/src/cli/list.py
+@@ -6,21 +6,20 @@
+ import json
+ import time
+ import builtins
++import paths
+
+ from i18n import _
+
+-# Get the absolute path and the username
+-path = "/etc/howdy"
+ user = builtins.howdy_user
+
+ # Check if the models file has been created yet
+-if not os.path.exists(path + "/models"):
++if not os.path.exists(paths.user_models_dir):
+ print(_("Face models have not been initialized yet, please run:"))
+ print("\n\tsudo howdy -U " + user + " add\n")
+ sys.exit(1)
+
+ # Path to the models file
+-enc_file = path + "/models/" + user + ".dat"
++enc_file = paths.user_models_dir + user + ".dat"
+
+ # Try to load the models file and abort if the user does not have it yet
+ try:
+diff --git a/howdy/src/cli/remove.py b/howdy/src/cli/remove.py
+index 6321e0b5..37894422 100644
+--- a/howdy/src/cli/remove.py
++++ ./howdy/src/cli/remove.py
+@@ -5,11 +5,10 @@
+ import os
+ import json
+ import builtins
++import paths
+
+ from i18n import _
+
+-# Get the absolute path and the username
+-path = "/etc/howdy"
+ user = builtins.howdy_user
+
+ # Check if enough arguments have been passed
+@@ -22,13 +21,13 @@
+ sys.exit(1)
+
+ # Check if the models file has been created yet
+-if not os.path.exists(path + "/models"):
++if not os.path.exists(paths.user_models_dir):
+ print(_("Face models have not been initialized yet, please run:"))
+ print("\n\thowdy add\n")
+ sys.exit(1)
+
+ # Path to the models file
+-enc_file = path + "/models/" + user + ".dat"
++enc_file = paths.user_models_dir + user + ".dat"
+
+ # Try to load the models file and abort if the user does not have it yet
+ try:
+@@ -72,7 +71,7 @@
+
+ # Remove the entire file if this encoding is the only one
+ if len(encodings) == 1:
+- os.remove(path + "/models/" + user + ".dat")
++ os.remove(paths.user_models_dir + user + ".dat")
+ print(_("Removed last model, howdy disabled for user"))
+ else:
+ # A place holder to contain the encodings that will remain
+diff --git a/howdy/src/cli/set.py b/howdy/src/cli/set.py
+index 14d15c20..efbbee5b 100644
+--- a/howdy/src/cli/set.py
++++ ./howdy/src/cli/set.py
+@@ -5,11 +5,12 @@
+ import os
+ import builtins
+ import fileinput
++import paths
+
+ from i18n import _
+
+ # Get the absolute filepath
+-config_path = os.path.dirname("/etc/howdy") + "/config.ini"
++config_path = os.path.dirname(paths.config_dir) + "/config.ini"
+
+ # Check if enough arguments have been passed
+ if len(builtins.howdy_args.arguments) < 2:
+diff --git a/howdy/src/cli/snap.py b/howdy/src/cli/snap.py
+index cbcae501..2c625d3b 100644
+--- a/howdy/src/cli/snap.py
++++ ./howdy/src/cli/snap.py
+@@ -5,15 +5,14 @@
+ import configparser
+ import datetime
+ import snapshot
++import paths
+ from recorders.video_capture import VideoCapture
+
+ from i18n import _
+
+-path = "/etc/howdy"
+-
+ # Read the config
+ config = configparser.ConfigParser()
+-config.read(path + "/config.ini")
++config.read(paths.config_dir + "config.ini")
+
+ # Start video capture
+ video_capture = VideoCapture(config)
+diff --git a/howdy/src/cli/test.py b/howdy/src/cli/test.py
+index 3a6e4d19..563be19b 100644
+--- a/howdy/src/cli/test.py
++++ ./howdy/src/cli/test.py
+@@ -10,6 +10,7 @@
+ import dlib
+ import cv2
+ import numpy as np
++import paths
+
+ from i18n import _
+ from recorders.video_capture import VideoCapture
+@@ -19,7 +20,7 @@
+
+ # Read config from disk
+ config = configparser.ConfigParser()
+-config.read(path + "/config.ini")
++config.read(paths.config_dir + "config.ini")
+
+ if config.get("video", "recording_plugin", fallback="opencv") != "opencv":
+ print(_("Howdy has been configured to use a recorder which doesn't support the test command yet, aborting"))
+@@ -59,20 +60,20 @@ def print_text(line_number, text):
+
+ if use_cnn:
+ face_detector = dlib.cnn_face_detection_model_v1(
+- path + "/dlib-data/mmod_human_face_detector.dat"
++ paths.dlib_data_dir + "mmod_human_face_detector.dat"
+ )
+ else:
+ face_detector = dlib.get_frontal_face_detector()
+
+-pose_predictor = dlib.shape_predictor(path + "/dlib-data/shape_predictor_5_face_landmarks.dat")
+-face_encoder = dlib.face_recognition_model_v1(path + "/dlib-data/dlib_face_recognition_resnet_model_v1.dat")
++pose_predictor = dlib.shape_predictor(paths.dlib_data_dir + "shape_predictor_5_face_landmarks.dat")
++face_encoder = dlib.face_recognition_model_v1(paths.dlib_data_dir + "dlib_face_recognition_resnet_model_v1.dat")
+
+ encodings = []
+ models = None
+
+ try:
+ user = builtins.howdy_user
+- models = json.load(open(path + "/models/" + user + ".dat"))
++ models = json.load(open(paths.user_models_dir + user + ".dat"))
+
+ for model in models:
+ encodings += model["data"]
+diff --git a/howdy/src/compare.py b/howdy/src/compare.py
+index 99f5285b..f81fe386 100644
+--- a/howdy/src/compare.py
++++ ./howdy/src/compare.py
+@@ -23,6 +23,7 @@
+ import snapshot
+ import numpy as np
+ import _thread as thread
++import paths
+
+ # Allow imports from the local howdy folder
+ sys.path.append('/lib/security/howdy')
+@@ -48,22 +49,22 @@ def init_detector(lock):
+ global face_detector, pose_predictor, face_encoder
+
+ # Test if at lest 1 of the data files is there and abort if it's not
+- if not os.path.isfile(PATH + "/dlib-data/shape_predictor_5_face_landmarks.dat"):
++ if not os.path.isfile(paths.dlib_data_dir + "shape_predictor_5_face_landmarks.dat"):
+ print(_("Data files have not been downloaded, please run the following commands:"))
+- print("\n\tcd " + PATH + "/dlib-data")
++ print("\n\tcd " + paths.dlib_data_dir)
+ print("\tsudo ./install.sh\n")
+ lock.release()
+ exit(1)
+
+ # Use the CNN detector if enabled
+ if use_cnn:
+- face_detector = dlib.cnn_face_detection_model_v1(PATH + "/dlib-data/mmod_human_face_detector.dat")
++ face_detector = dlib.cnn_face_detection_model_v1(paths.dlib_data_dir + "mmod_human_face_detector.dat")
+ else:
+ face_detector = dlib.get_frontal_face_detector()
+
+ # Start the others regardless
+- pose_predictor = dlib.shape_predictor(PATH + "/dlib-data/shape_predictor_5_face_landmarks.dat")
+- face_encoder = dlib.face_recognition_model_v1(PATH + "/dlib-data/dlib_face_recognition_resnet_model_v1.dat")
++ pose_predictor = dlib.shape_predictor(paths.dlib_data_dir + "shape_predictor_5_face_landmarks.dat")
++ face_encoder = dlib.face_recognition_model_v1(paths.dlib_data_dir + "dlib_face_recognition_resnet_model_v1.dat")
+
+ # Note the time it took to initialize detectors
+ timings["ll"] = time.time() - timings["ll"]
+@@ -103,9 +104,6 @@ def send_to_ui(type, message):
+ if len(sys.argv) < 2:
+ exit(12)
+
+-# Get the absolute path to the config directory
+-PATH = "/etc/howdy"
+-
+ # The username of the user being authenticated
+ user = sys.argv[1]
+ # The model file contents
+@@ -129,7 +127,7 @@ def send_to_ui(type, message):
+
+ # Try to load the face model from the models folder
+ try:
+- models = json.load(open(PATH + "/models/" + user + ".dat"))
++ models = json.load(open(paths.user_models_dir + user + ".dat"))
+
+ for model in models:
+ encodings += model["data"]
+@@ -142,7 +140,7 @@ def send_to_ui(type, message):
+
+ # Read config from disk
+ config = configparser.ConfigParser()
+-config.read(PATH + "/config.ini")
++config.read(paths.config_dir + "config.ini")
+
+ # Get all config values needed
+ use_cnn = config.getboolean("core", "use_cnn", fallback=False)
+diff --git a/howdy/src/paths.py b/howdy/src/paths.py
+new file mode 100644
+index 00000000..22825405
+--- /dev/null
++++ ./howdy/src/paths.py
+@@ -0,0 +1,12 @@
++
++# Define the absolute path to the config directory
++config_dir = "/etc/howdy/"
++
++# Define the absolute path to the DLib models data directory
++dlib_data_dir = config_dir + "/dlib-data/"
++
++# Define the absolute path to the Howdy user models directory
++user_models_dir = config_dir + "/models/"
++
++# Define path to any howdy logs
++log_path = "/var/log/howdy"
+diff --git a/howdy/src/snapshot.py b/howdy/src/snapshot.py
+index 324b5789..9f2f563c 100644
+--- a/howdy/src/snapshot.py
++++ ./howdy/src/snapshot.py
+@@ -49,19 +49,16 @@ def generate(frames, text_lines):
+
+ line_number += 1
+
+- # Define path to any howdy logs
+- log_path = "/var/log/howdy"
+-
+ # Made sure a snapshot folder exist
+- if not os.path.exists(log_path):
+- os.makedirs(log_path)
+- if not os.path.exists(log_path + "/snapshots"):
+- os.makedirs(log_path + "/snapshots")
++ if not os.path.exists(paths.log_path):
++ os.makedirs(paths.log_path)
++ if not os.path.exists(paths.log_path + "/snapshots"):
++ os.makedirs(paths.log_path + "/snapshots")
+
+ # Generate a filename based on the current time
+ filename = datetime.datetime.utcnow().strftime("%Y%m%dT%H%M%S.jpg")
+ # Write the image to that file
+- cv2.imwrite(log_path + "/snapshots/" + filename, snap)
++ cv2.imwrite(paths.log_path + "/snapshots/" + filename, snap)
+
+ # Return the saved file location
+- return log_path + "/snapshots/" + filename
++ return paths.log_path + "/snapshots/" + filename
diff --git a/security/howdy/pkg-descr b/security/howdy/pkg-descr
new file mode 100644
index 000000000000..1fd314d7c55e
--- /dev/null
+++ b/security/howdy/pkg-descr
@@ -0,0 +1,3 @@
+Howdy is an authentication tool that allows you to unlock your desktop
+session using your webcam. It uses facial recognition to authenticate and
+unlock the session.
diff --git a/security/howdy/pkg-plist b/security/howdy/pkg-plist
new file mode 100644
index 000000000000..5353cffca512
--- /dev/null
+++ b/security/howdy/pkg-plist
@@ -0,0 +1,27 @@
+bin/howdy
+@sample %%ETCDIR%%/config.ini.sample
+libexec/howdy/cli.py
+libexec/howdy/cli/__init__.py
+libexec/howdy/cli/add.py
+libexec/howdy/cli/clear.py
+libexec/howdy/cli/config.py
+libexec/howdy/cli/disable.py
+libexec/howdy/cli/list.py
+libexec/howdy/cli/remove.py
+libexec/howdy/cli/set.py
+libexec/howdy/cli/snap.py
+libexec/howdy/cli/test.py
+libexec/howdy/compare.py
+libexec/howdy/i18n.py
+libexec/howdy/logo.png
+libexec/howdy/paths.py
+libexec/howdy/recorders/__init__.py
+libexec/howdy/recorders/ffmpeg_reader.py
+libexec/howdy/recorders/pyv4l2_reader.py
+libexec/howdy/recorders/v4l2.py
+libexec/howdy/recorders/video_capture.py
+libexec/howdy/rubberstamps/__init__.py
+libexec/howdy/rubberstamps/hotkey.py
+libexec/howdy/rubberstamps/nod.py
+libexec/howdy/snapshot.py
+share/bash-completion/completions/howdy