70 Commits

Author SHA1 Message Date
7d22a2b5b9 Release 1.90.8 2020-12-11 16:00:28 +01:00
de725a91e4 verify: Print message about verification start from callback
It seems that GLib may process multiple DBus signals in one mainloop
iteration. This could cause messages to be re-ordered, which in turn
caused a race condition in the CI that could trigger random failures.
2020-12-11 16:00:28 +01:00
18392cba54 manager: Export the object manager in /net/reactivated/Fprint
Given we're going to use an object manager it can just stay at the root
of the project, while it will be just used to manage the devices
2020-12-11 15:30:26 +01:00
783d82f359 device: Expose method name when logging authorization steps 2020-12-11 14:03:37 +00:00
c00a3375d1 device: Use standard names for local errors and remove unused one 2020-12-11 14:03:37 +00:00
5aa61adabc build: make systemd dependency optional
The systemd dependency is only used to install some systemd service
files. This can easily be made optional.
2020-12-11 15:01:24 +01:00
1fc10f15ee pam: Stop authorization if we couldn't parse signals
This really should never ever happen. If it does, don't continue but
stop instead.
2020-12-11 10:34:51 +01:00
c24badfd68 pam: Move NameOwnerChanged registration after initialization
We must ignore NameOwnerChanged that happen due to automatic startup.
The easy way to do so is to just register it only when we get to the
point that a name owner change has security implications.

While add it, change it to always log at a warning level.

Fixes: #94
2020-12-11 10:34:51 +01:00
4612c1f3ed Release 1.90.7 2020-12-09 13:16:12 +01:00
ca216a32af test_pam_fprintd: Add test verifying the case in which we've no devices 2020-12-08 21:14:24 +01:00
944493e472 pam_fprintd: Protect usage of strdup for NULL values
It's not smart as g_strdup, so need to ensure we don't use it for NULL
strings.

This is a regression caused by commit bf223662
2020-12-08 21:14:01 +01:00
34f24cbe19 ci: Move build jobs to build phase 2020-12-07 18:42:24 +01:00
9314069a88 ci: Add check-source stage where we check syntax 2020-12-07 18:42:24 +01:00
66e21eac8f .git-blame-ignore-revs: Ignore formatting commit and add hint how to use it 2020-12-07 18:42:22 +01:00
f73429f062 fprintd: Reindent the source code following uncrustify rules 2020-12-07 18:41:45 +01:00
c18ebaf9da scripts: Add uncrustify scripts for reformatting fprintd source code
We follow libfprint here, using GNOME format
2020-12-07 18:41:45 +01:00
4a80bfacec fingerprint-strings: Make the finger_str_to_msg loop clearer
Just continue earlier instead of using a long if check
2020-12-07 18:41:45 +01:00
52058c1ea0 Release 1.90.6 2020-12-07 15:34:48 +01:00
22cdc0a7ea device: Always use FpFinger instead of a mixture of it and finger numbers
We already use FpFinger for storage operations and prints management,
but internally we keep still using the old finger number, that uses
different values for invalid data.

Let's be consistent, and always use FpFinger everywhere.
2020-12-07 15:27:14 +01:00
043fcaafec pam_fprintd: Guard against NULL pointer returned by finger_str_to_msg
Otherwise the PAM module will crash trying to send an info message about
the selected print.
2020-12-07 15:27:14 +01:00
bf2236620e pam_fprintd: Implement auto-pointers and use early-return more around
Implement simple auto-pointers for the types we use in pam_fprintd with
a basic implementation based on GLib one so that we can have the same
features without having neither an header-dependency on it.
2020-12-07 15:27:14 +01:00
0122d351f9 fprintd: Add tests for device properties values 2020-12-07 15:27:14 +01:00
4435706d20 tests: Add test that PAM gives up when daemon disappears 2020-12-07 15:27:14 +01:00
c5877bbc12 pam: Stop authentication with PAM_AUTHINFO_UNAVAIL on name owner change
If fprintd disappears or is replaced, then we might be getting signals
from another daemon/verifcation session.

As such we must give up at that point.

Related: #47
2020-12-07 15:27:14 +01:00
a170a3a09f tests: Check that PAM stops if the device could not be claimed 2020-12-07 15:27:14 +01:00
a76af6ce71 pam_fprintd: Clear the data value before setting it if set 2020-12-07 15:27:14 +01:00
34a24eac77 tests/pam: Ensure that we ignore verify events before VerifyStart has completed
We had a race that was causing the events to be handled even if we were
not ready to accept them, causing a potential non-authentication.

So simulate this case, by sending a 'verify-match' event before we
started the verification and ensure that we ignore it.
2020-12-07 15:27:14 +01:00
47751548b2 pam_fprintd: Ignore any verify signal if we didn't complete VerifyStart
In case fprintd is emitting a verify signal for another request that is
still going on while we're about to start a new verification, we'd just
accept such signal, so potentially allowing a log-in because another
concurrent request succeeded.

To avoid this, use async call to VerifyStart and open a verify window
(during which we accept the verification related signals) that is kept
open just once the VerifyStart call has been completed and before
stopping the verification again. As that's the only moment in which we
can be sure that we've control of the daemon events for such device.

Thanks to Benjamin to find out the race.

Fixes: #47
2020-12-07 15:27:14 +01:00
a30c45629e tests/pam: Ensure that we fail in case the user has no prints enrolled
This is both in case in we start the authentication and in the absurd
but (hey, testing!) situation in which prints gets deleted in between
the device claiming and the verification start.

To handle this second scenario we need to instruct fprintd mock to raise
an error on some special command
2020-12-07 15:27:14 +01:00
3242b99410 dbusmock/fprintd: Support more complex verify scripts
This now allows:
 * Sending signals before and after method return
 * Exiting the daemon
 * Emulating NoEnrolledPrints DBus method error

Co-authored-by: Benjamin Berg <bberg@redhat.com>
2020-12-07 15:27:14 +01:00
5ccb9ba0ec tests: Do not eat fprintd output in PAM test
It may be useful, just let it go to stdout/stderr.
2020-12-07 15:27:14 +01:00
f4eaacd0ec pam: Return a fixed string about protocol error if there is no message
This can only happen if fprintd is not adhering to the protocol.
2020-12-07 15:27:14 +01:00
34b21fa917 tests: Port test to use the OutputChecker 2020-12-07 15:27:14 +01:00
2d98d4543f verify: Add print about start for tests
This allows properly reading the output only to the point where we can
ensure that verification has started.
2020-12-07 15:27:14 +01:00
8c46fddd03 verify: Fix verify script to work correctly
The verify script would start an async routine. However, this blocks the
dbus return, which really is needed.

Also, we should only return one item of the script for each VerifyStart
run. So, fix things by pop'ing the first item and putting it on the bus
from a GLib.add_timeout handler.
2020-12-07 15:27:14 +01:00
3a00643d5b tests: Add OutputChecker class to improve test code 2020-12-07 15:27:14 +01:00
eb73e024e1 utils: Fix race in verify accepting unrelated signals
Signals like VerifyResult may be received from unrelated Verify
operations. To avoid races, we need to ignore any VerifyResult that
happenes before the DBus method returns.

The only way to do this race-free is to use the async version of the
VerifyStart method.
2020-12-07 15:27:14 +01:00
a4b06c2219 device: Emit VerifyFingerSelected after the method returned
In order to be race free, clients need to ignore all signals until after
the DBus method to start verification has returned. So the signal must
be emitted later than it currently is.
2020-12-07 15:27:14 +01:00
5ccaa094a0 build: Generate fprintd dbus sources using interactive flags
Since we can't depend on newer GLib yet, we patch the generated sources
to generate some new ones with fixed flags.
2020-12-07 15:14:07 +01:00
fc7e4d0e5c device: Do not require authentication for release/stop
If someone has started an operation, then we don't really need to
confirm they are permitted to stop it again. Not doing this has the
advantage that we cannot run into a second interactive authorization
step accidentally.
2020-12-07 15:14:07 +01:00
583cd870d8 device: Use a common error function if an action is ongoing
There is no need to dupliate the code. Just create one function that
sets an error and returns FALSE if action is not ACTION_NONE.
2020-12-07 12:02:32 +01:00
2ca2d5e62c device: Use a switch to check current action so we can be more selective
For example we were allowing to verify stop while doing other actions
different from enrolling (such as delete or open/close).
2020-12-07 12:02:32 +01:00
c5c81a2ea8 device: Add ACTION_DELETE to prevent concurrent operations
Delete needs to operate on the device, so no other actions are permitted
at the same time. And using the libfprint _sync methods does not
guard against reentrance.
2020-12-07 12:02:32 +01:00
c0ad5880a4 tests: Call VerifyStop in enroll_verify_list_delete test
The test didn't call this and the device would be left in an
inconsistent state, causing an error at shutdown time.
2020-12-07 12:02:32 +01:00
2dc3a4e2c5 device: Use more standard naming for local errors 2020-12-07 12:00:58 +01:00
3b0d93bcc2 tests: Add more authorization tests 2020-12-07 12:00:58 +01:00
eac171ab0f device: Add separate state for delete which will claim internally
Also rename the different claim states to make them a bit more
understandable.
2020-12-07 12:00:58 +01:00
7533f63a06 device: Move permissions checks per DBus invocation in a single function
This way we can avoid repeating the same checks multiple times, and
we have a single point where we check the permissions needed for method
invocation.
2020-12-07 11:59:42 +01:00
a38917ab26 fprintd: Re-order permissions table by priority and add docs
Given that we could do operations where at least one permission, is
requested, we should give more priority to the weaker ones that are
acceptable and in case raise the level at later points.
2020-12-05 01:01:36 +01:00
a92b8e5f60 device: Always return FALSE if setting an error in Check claimed
We may have a case where the sender matches with the
session's sender but have a session invocation already set.

In such case we set an error, but still return TRUE.
2020-12-05 01:01:36 +01:00
29f34cf23c tests/fprintd: Do not hang if we error on name appeared callback
But instead only wait for name to appear and do the tests in the main
function so that we can properly check the exception and depending on
its type skip the test or raise it so that it can be caught by the test
suite
2020-12-04 22:56:37 +01:00
a10f0dc22d net.reactivated.Fprint: Allow actual fprind interfaces to be used
We allowed a non-existant net.reactivated.Fprint interface, while our
interfaces are Manager and Device.

Allow them to be used.
2020-12-03 17:54:21 +01:00
267b322f6d Release 1.90.5 2020-12-01 14:22:55 +01:00
3b83240e57 tests: Fix detection of non-functional file permissions
If we rely on CI_PROJECT_NAME being set, then the test will fail in
similar environments outside of the fprintd main CI. So just add a
os.stat call afterwards to check whether the permission changes took
effect, and if not, then skip.

So, instead try to create a file and check that this fails.
2020-12-01 14:22:55 +01:00
897cbd341e tests: Skip flaky test_enroll_verify_list_delete test with old libfprint
The test can hang forever unless a fixed libfprint version is used.
2020-12-01 12:46:17 +01:00
9d0305ea05 tests: Use system utils outside of the test environment
The code to pick up the utilities from the test environment would fail
if the environment variables are not set. In that case, we can just use
the binary name and rely on PATH though.
2020-12-01 11:41:11 +01:00
3dbfdabe01 tests: Translate skips into error code 77
This makes the meson output nicer, as it will correctly display that the
test has been skipped. It only happens if all tests in the run were
skipped, but meson always does one test a time.
2020-12-01 10:04:11 +01:00
45cf63d589 tests: Skip hotplug test if "removed" property does not exist
The test requires libfprint 1.90.4 to work, otherwise it will just hang
forever.
2020-12-01 10:04:11 +01:00
29ed88a50a meson: Use warning_level feature of meson
This is nicer than adding -Wall and gives users more control.

Add -Wno-unused-parameter for now as there are lot of places where
this would need to be changed and it is reasonable in most cases.

Add -Wno-pedantic because it conflicts with
g_signal_handlers_disconnect_*
2020-12-01 09:00:55 +00:00
e301779c20 Fix integer signedness mismatches 2020-12-01 09:00:55 +00:00
be5d283a3e device: Remove unused code
The code was left over when adding the function to create the session data.
2020-12-01 09:00:55 +00:00
ebfcbdd13e pam: Use %d with errno instead of %m
Otherwise GCC warns sometimes, and it is easy enough to replace the use
of %m.
2020-12-01 09:00:55 +00:00
ec7376d7e6 meson: Fix CFLAGS use by using add_project_arguments
It seems that meson will not always apply the CFLAGS as defined through
the environment if "c_args=" is used in the default_options array for
the project() call.

Switching to add_project_arguments solves this problem.

See https://github.com/mesonbuild/meson/issues/8037
2020-12-01 09:00:55 +00:00
df568e1ce1 net.reactivated.Fprint: Explicitly allow basic D-Bus APIs 2020-11-28 20:29:57 +00:00
7ee61393ec net.reactivated.Fprint: Only allow clients to send messages with fprintd iface to us
In the way the rule is currently set it would allow clients to send
messages with the fprintd interface to any other service, while we only
allow them to be redirected to fprintd itself.

This was causing a debian linter failure [1].

[1] https://lintian.debian.org/tags/dbus-policy-without-send-destination.html
2020-11-28 20:29:57 +00:00
57ca0dc95e tests/pam: Don't run PAM tests in parallel
Given they could re-use the same pam-wrapper temporary dir, it's better
to avoid running them concurrently.
2020-11-27 19:30:08 +01:00
85ba309e9d tests: Remove pam-wrapper temporary folder on test completion
PAM wrapper creates /tmp/pam.X files during its execution (strictly as
it does not follow $TMPDIR either), however given the low number of
combinations, we may end up in re-using the same pam.* folder during
meson test, causing a failure.

As per this, remove these temporary files on tearDown so that we won't
try reusing the same folder multiple times.
2020-11-27 19:12:06 +01:00
3f2174676e device: Re-define polkit auto-pointer funcs if not defined
In case we're using an old polkit version that does not support
auto-pointers, we need to re-define such functions manually or fprintd
won't compile.

Given that polkit doesn't provide us version informations in headers we
need to get that from pkg-config
2020-11-27 18:21:29 +01:00
5e18d46971 Revert "meson: Bump polkit dependency to 0.114"
We only depend on newer polkit for auto-pointers usage, so it's not
worth to bump its dependency just for them

This reverts commit a8bd2bc25e.
2020-11-27 18:18:51 +01:00
a8bd2bc25e meson: Bump polkit dependency to 0.114
It's the first one supporting auto-pointers
2020-11-27 17:53:58 +01:00
32 changed files with 4645 additions and 3229 deletions

7
.git-blame-ignore-revs Normal file
View File

@ -0,0 +1,7 @@
# The commits that did automated reformatting. You can ignore them
# during git-blame with `--ignore-rev` or `--ignore-revs-file`.
#
# $ git config --add 'blame.ignoreRevsFile' '.git-blame-ignore-revs'
#
f73429f06226f5423c92b1c504313657c9b6f9b5

View File

@ -29,6 +29,11 @@ variables:
image: "$FEDORA_IMAGE"
stages:
- check-source
- build
- test
.fprintd_build_preconditions:
except:
variables:
@ -47,8 +52,17 @@ image: "$FEDORA_IMAGE"
# So we don't get error about this libfprint file
- echo "libfprint/demo/gtk-libfprint-test.ui" >> po/POTFILES.skip
test_indent:
stage: check-source
extends: .fprintd_build_preconditions
script:
- scripts/uncrustify.sh
- git diff
- "! git status -s | grep -q ."
build_stable:
extends: .fprintd_build_preconditions
stage: build
script:
- meson _build
- ninja -C _build -v
@ -58,6 +72,7 @@ build_dev:
extends:
- .fprintd_build_preconditions
- .install_libfprint_dev
stage: build
script:
- meson _build --werror -Dgtk_doc=true
- ninja -C _build -v

51
NEWS
View File

@ -1,6 +1,57 @@
This file lists notable changes in each release. For the full history of all
changes, see ChangeLog.
Version 1.90.8:
It seems that we are finally reaching the end of the tunnel with regard
to regressions. One more issue that cropped up was that a pam_fprintd fix
to avoid a possible authentication bypass caused issues when fprintd was
just started on demand.
Highlights:
- pam: Only listen to NameOwnerChanged after fprintd is known to run (#94)
- Place new ObjectManager DBus API at /net/reactivated/Fprint
Version 1.90.7:
While 1.90.6 fixed a number of issues, we did have a bad regression due
causing pam_fprintd to crash when there are no fingerprint devices
installed.
Highlights:
- pam: Guard strdup calls against NULL pointers
Version 1.90.6:
The 1.90.5 release was unusable due to a number of inter-related issues
with the DBus interface and authorization. We also found a number of
problems with possible security implications.
Currently fprintd will do interactive authorization even if this was not
requested using the correct DBus method call flag. All API users MUST be
updated to set the flag as it will be enabled in the future!
Highlights:
- Fix fprintd DBus configuration
- Change details of what requires authorization
- Fix various race conditions in pam_fprintd
- Permit interactive authorization from fprintd utilities
- Do not allow deletion while another operation is ongoing
Version 1.90.5:
The 1.90.4 release contained some bad errors, this release addresses those.
Highlights:
- Permit building with polkit older than 0.114
- Fix possible issues with PAM test
- Fix incorrect DBus policy
- Fix build so that CFLAGS enviroment is correctly used
- Skip hotplug test with older libfprint (which times out otherwise)
Version 1.90.4:
This fprintd release contains major core reworkings and improved testing.

View File

@ -9,3 +9,6 @@
/* Define to the version of this package. */
#mesondefine VERSION
/* Whether current polkit version supports autopointers */
#mesondefine POLKIT_HAS_AUTOPOINTERS

View File

@ -11,6 +11,7 @@ configure_file(
install_dir: dbus_service_dir,
)
if get_option('systemd')
configure_file(
configuration: configuration_data({
'libexecdir': fprintd_installdir,
@ -20,6 +21,7 @@ configure_file(
install: true,
install_dir: systemd_unit_dir,
)
endif
polkit_policy = 'net.reactivated.fprint.device.policy'
polkit_policy_target = i18n.merge_file(polkit_policy,

View File

@ -12,8 +12,18 @@
<!-- Anyone can talk to the service -->
<policy context="default">
<allow send_destination="net.reactivated.Fprint"/>
<allow send_interface="net.reactivated.Fprint"/>
<allow send_destination="net.reactivated.Fprint"
send_interface="net.reactivated.Fprint.Manager"/>
<allow send_destination="net.reactivated.Fprint"
send_interface="net.reactivated.Fprint.Device"/>
<!-- Basic D-Bus API stuff -->
<allow send_destination="net.reactivated.Fprint"
send_interface="org.freedesktop.DBus.Introspectable"/>
<allow send_destination="net.reactivated.Fprint"
send_interface="org.freedesktop.DBus.Properties"/>
<allow send_destination="net.reactivated.Fprint"
send_interface="org.freedesktop.DBus.ObjectManager"/>
</policy>
</busconfig>

View File

@ -1,13 +1,19 @@
project('fprintd', 'c',
version: '1.90.4',
version: '1.90.8',
license: 'GPLv2+',
default_options: [
'buildtype=debugoptimized',
'warning_level=1',
'warning_level=3',
'c_std=gnu99',
'c_args=' + ' '.join([
],
meson_version: '>= 0.50.0')
gnome = import('gnome')
i18n = import('i18n')
cc = meson.get_compiler('c')
common_cflags = cc.get_supported_arguments([
'-fno-strict-aliasing',
'-Wall',
'-Wcast-align',
'-Werror=address',
'-Werror=array-bounds',
@ -43,15 +49,20 @@ project('fprintd', 'c',
'-Wtype-limits',
'-Wundef',
'-Wunused',
]),
],
meson_version: '>= 0.50.0')
])
add_project_arguments(common_cflags, language: 'c')
gnome = import('gnome')
i18n = import('i18n')
common_cflags = cc.get_supported_arguments([
# The stub passes a lot of params that we do not use, maybe a good idea to
# mark it appropriately, but this works well for now.
'-Wno-unused-parameter',
# We use g_signal_handlers_disconnect_* which is not compatible with -Wpedantic
'-Wno-pedantic',
])
add_project_arguments(common_cflags, language: 'c')
cc = meson.get_compiler('c')
host_system = host_machine.system()
# NOTE: Bump gdbus-codegen min version once we can depend on 2.64!
glib_min_version = '2.56'
libfprint_min_version = '1.90.1'
@ -83,13 +94,17 @@ pod2man = find_program('pod2man', required: get_option('man'))
xsltproc = find_program('xsltproc', required: get_option('gtk_doc'))
# StateDirectory was introduced in systemd 235
systemd_dep = dependency('systemd', version: '>= 235')
systemd_dep = dependency('systemd', version: '>= 235', required: false)
systemd_unit_dir = get_option('systemd_system_unit_dir')
if systemd_unit_dir == ''
if systemd_unit_dir == '' and systemd_dep.found()
systemd_unit_dir = systemd_dep.get_pkgconfig_variable('systemdsystemunitdir')
endif
if get_option('systemd') and systemd_unit_dir == ''
error('systemd development files or systemd_system_unit_dir is needed for systemd support.')
endif
dbus_service_dir = get_option('dbus_service_dir')
dbus_data_dir = datadir
dbus_interfaces_dir = ''
@ -139,6 +154,7 @@ cdata.set_quoted('GETTEXT_PACKAGE', meson.project_name())
cdata.set_quoted('PACKAGE_VERSION', meson.project_version())
cdata.set_quoted('VERSION', meson.project_version())
cdata.set_quoted('SYSCONFDIR', sysconfdir)
cdata.set('POLKIT_HAS_AUTOPOINTERS', polkit_gobject_dep.version().version_compare('>= 0.114'))
config_h = configure_file(
input: 'config.h.in',

View File

@ -6,6 +6,10 @@ option('man',
description: 'Generate the man files',
type: 'boolean',
value: true)
option('systemd',
description: 'Install system service files',
type: 'boolean',
value: true)
option('systemd_system_unit_dir',
description: 'Directory for systemd service files',
type: 'string')

View File

@ -28,16 +28,18 @@
#define GNUC_UNUSED __attribute__((__unused__))
static bool str_equal (const char *a, const char *b)
static bool
str_equal (const char *a, const char *b)
{
if (a == NULL && b == NULL)
return true;
if (a == NULL || b == NULL)
return false;
return (strcmp (a, b) == 0);
return strcmp (a, b) == 0;
}
struct {
struct
{
const char *dbus_name;
const char *place_str_generic;
const char *place_str_specific;
@ -105,33 +107,44 @@ struct {
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wformat-nonliteral"
GNUC_UNUSED static char *finger_str_to_msg(const char *finger_name, const char *driver_name, bool is_swipe)
GNUC_UNUSED static char *
finger_str_to_msg (const char *finger_name, const char *driver_name, bool is_swipe)
{
int i;
if (finger_name == NULL)
return NULL;
for (i = 0; fingers[i].dbus_name != NULL; i++) {
if (str_equal (fingers[i].dbus_name, finger_name)) {
if (is_swipe == false) {
if (driver_name) {
for (i = 0; fingers[i].dbus_name != NULL; i++)
{
if (!str_equal (fingers[i].dbus_name, finger_name))
continue;
if (is_swipe == false)
{
if (driver_name)
{
char *s;
int ret;
ret = asprintf (&s, TR (fingers[i].place_str_specific), driver_name);
return ret >= 0 ? s : NULL;
} else {
}
else
{
return strdup (TR (fingers[i].place_str_generic));
}
} else {
if (driver_name) {
}
else
{
if (driver_name)
{
char *s;
int ret;
ret = asprintf (&s, TR (fingers[i].swipe_str_specific), driver_name);
return ret >= 0 ? s : NULL;
} else {
return strdup (TR (fingers[i].swipe_str_generic));
}
else
{
return strdup (TR (fingers[i].swipe_str_generic));
}
}
}
@ -146,12 +159,14 @@ GNUC_UNUSED static char *finger_str_to_msg(const char *finger_name, const char *
* verify-match
* verify-unknown-error
*/
GNUC_UNUSED static const char *verify_result_str_to_msg(const char *result, bool is_swipe)
GNUC_UNUSED static const char *
verify_result_str_to_msg (const char *result, bool is_swipe)
{
if (result == NULL)
return NULL;
if (strcmp (result, "verify-retry-scan") == 0) {
if (strcmp (result, "verify-retry-scan") == 0)
{
if (is_swipe == false)
return TR (N_("Place your finger on the reader again"));
else
@ -172,12 +187,14 @@ GNUC_UNUSED static const char *verify_result_str_to_msg(const char *result, bool
* enroll-failed
* enroll-unknown-error
*/
GNUC_UNUSED static const char *enroll_result_str_to_msg(const char *result, bool is_swipe)
GNUC_UNUSED static const char *
enroll_result_str_to_msg (const char *result, bool is_swipe)
{
if (result == NULL)
return NULL;
if (strcmp (result, "enroll-retry-scan") == 0 || strcmp (result, "enroll-stage-passed") == 0) {
if (strcmp (result, "enroll-retry-scan") == 0 || strcmp (result, "enroll-stage-passed") == 0)
{
if (is_swipe == false)
return TR (N_("Place your finger on the reader again"));
else
@ -192,4 +209,3 @@ GNUC_UNUSED static const char *enroll_result_str_to_msg(const char *result, bool
return NULL;
}

View File

@ -43,6 +43,7 @@
#define N_(s) (s)
#include "fingerprint-strings.h"
#include "pam_fprintd_autoptrs.h"
#define DEFAULT_MAX_TRIES 3
#define DEFAULT_TIMEOUT 30
@ -63,18 +64,21 @@ static uint64_t
now (void)
{
struct timespec ts;
clock_gettime (CLOCK_MONOTONIC, &ts);
return (uint64_t) ts.tv_sec * USEC_PER_SEC + (uint64_t) ts.tv_nsec / NSEC_PER_USEC;
}
static bool str_has_prefix (const char *s, const char *prefix)
static bool
str_has_prefix (const char *s, const char *prefix)
{
if (s == NULL || prefix == NULL)
return false;
return (strncmp (s, prefix, strlen (prefix)) == 0);
return strncmp (s, prefix, strlen (prefix)) == 0;
}
static bool send_msg(pam_handle_t *pamh, const char *msg, int style)
static bool
send_msg (pam_handle_t *pamh, const char *msg, int style)
{
const struct pam_message mymsg = {
.msg_style = style,
@ -83,24 +87,24 @@ static bool send_msg(pam_handle_t *pamh, const char *msg, int style)
const struct pam_message *msgp = &mymsg;
const struct pam_conv *pc;
struct pam_response *resp;
int r;
r = pam_get_item(pamh, PAM_CONV, (const void **) &pc);
if (r != PAM_SUCCESS)
if (pam_get_item (pamh, PAM_CONV, (const void **) &pc) != PAM_SUCCESS)
return false;
if (!pc || !pc->conv)
return false;
return (pc->conv(1, &msgp, &resp, pc->appdata_ptr) == PAM_SUCCESS);
return pc->conv (1, &msgp, &resp, pc->appdata_ptr) == PAM_SUCCESS;
}
static bool send_info_msg(pam_handle_t *pamh, const char *msg)
static bool
send_info_msg (pam_handle_t *pamh, const char *msg)
{
return send_msg (pamh, msg, PAM_TEXT_INFO);
}
static bool send_err_msg(pam_handle_t *pamh, const char *msg)
static bool
send_err_msg (pam_handle_t *pamh, const char *msg)
{
return send_msg (pamh, msg, PAM_ERROR_MSG);
}
@ -110,39 +114,37 @@ open_device (pam_handle_t *pamh,
sd_bus *bus,
bool *has_multiple_devices)
{
sd_bus_error error = SD_BUS_ERROR_NULL;
sd_bus_message *m = NULL;
pf_auto (sd_bus_error) error = SD_BUS_ERROR_NULL;
pf_autoptr (sd_bus_message) m = NULL;
size_t num_devices;
const char *path = NULL;
char *ret;
const char *s;
int r;
*has_multiple_devices = false;
r = sd_bus_call_method (bus,
if (sd_bus_call_method (bus,
"net.reactivated.Fprint",
"/net/reactivated/Fprint/Manager",
"net.reactivated.Fprint.Manager",
"GetDevices",
&error,
&m,
NULL);
if (r < 0) {
NULL) < 0)
{
pam_syslog (pamh, LOG_ERR, "GetDevices failed: %s", error.message);
sd_bus_error_free (&error);
return NULL;
}
r = sd_bus_message_enter_container (m, 'a', "o");
if (r < 0) {
if (r < 0)
{
pam_syslog (pamh, LOG_ERR, "Failed to parse answer from GetDevices(): %d", r);
goto out;
return NULL;
}
r = sd_bus_message_read_basic (m, 'o', &path);
if (r < 0)
goto out;
if (sd_bus_message_read_basic (m, 'o', &path) < 0)
return NULL;
num_devices = 1;
while ((r = sd_bus_message_read_basic (m, 'o', &s)) > 0)
@ -153,22 +155,36 @@ open_device (pam_handle_t *pamh,
sd_bus_message_exit_container (m);
out:
ret = path ? strdup (path) : NULL;
sd_bus_message_unref (m);
return ret;
return path ? strdup (path) : NULL;
}
typedef struct {
typedef struct
{
char *dev;
bool has_multiple_devices;
unsigned max_tries;
char *result;
bool timed_out;
bool is_swipe;
bool verify_started;
int verify_ret;
pam_handle_t *pamh;
char *driver;
} verify_data;
static void
verify_data_free (verify_data *data)
{
free (data->result);
free (data->driver);
free (data->dev);
free (data);
}
PF_DEFINE_AUTOPTR_CLEANUP_FUNC (verify_data, verify_data_free)
static int
verify_result (sd_bus_message *m,
void *userdata,
@ -181,27 +197,48 @@ verify_result (sd_bus_message *m,
uint64_t done = false;
int r;
if (!sd_bus_message_is_signal(m, "net.reactivated.Fprint.Device", "VerifyStatus")) {
if (!sd_bus_message_is_signal (m, "net.reactivated.Fprint.Device", "VerifyStatus"))
{
pam_syslog (data->pamh, LOG_ERR, "Not the signal we expected (iface: %s, member: %s)",
sd_bus_message_get_interface (m),
sd_bus_message_get_member (m));
return 0;
}
if ((r = sd_bus_message_read (m, "sb", &result, &done)) < 0) {
if ((r = sd_bus_message_read (m, "sb", &result, &done)) < 0)
{
pam_syslog (data->pamh, LOG_ERR, "Failed to parse VerifyResult signal: %d", r);
data->verify_ret = PAM_AUTHINFO_UNAVAIL;
return 0;
}
if (!data->verify_started)
{
pam_syslog (data->pamh, LOG_ERR, "Unexpected VerifyResult '%s', %" PRIu64 " signal", result, done);
return 0;
}
if (debug)
pam_syslog (data->pamh, LOG_DEBUG, "Verify result: %s (done: %d)", result, done ? 1 : 0);
if (done) {
if (data->result)
{
free (data->result);
data->result = NULL;
}
if (done && result)
{
data->result = strdup (result);
return 0;
}
msg = verify_result_str_to_msg (result, data->is_swipe);
if (!msg)
{
data->result = strdup ("Protocol error with fprintd!");
return 0;
}
send_err_msg (data->pamh, msg);
return 0;
@ -214,18 +251,30 @@ verify_finger_selected (sd_bus_message *m,
{
verify_data *data = userdata;
const char *finger_name = NULL;
char *msg;
pf_autofree char *msg = NULL;
if (sd_bus_message_read_basic (m, 's', &finger_name) < 0) {
pam_syslog (data->pamh, LOG_ERR, "Failed to parse VerifyFingerSelected signal: %m");
if (sd_bus_message_read_basic (m, 's', &finger_name) < 0)
{
pam_syslog (data->pamh, LOG_ERR, "Failed to parse VerifyFingerSelected signal: %d", errno);
data->verify_ret = PAM_AUTHINFO_UNAVAIL;
return 0;
}
if (!data->verify_started)
{
pam_syslog (data->pamh, LOG_ERR, "Unexpected VerifyFingerSelected %s signal", finger_name);
return 0;
}
msg = finger_str_to_msg (finger_name, data->driver, data->is_swipe);
if (!msg)
{
data->result = strdup ("Protocol error with fprintd!");
return 0;
}
if (debug)
pam_syslog (data->pamh, LOG_DEBUG, "verify_finger_selected %s", msg);
send_info_msg (data->pamh, msg);
free (msg);
return 0;
}
@ -238,9 +287,10 @@ get_property_string (sd_bus *bus,
const char *interface,
const char *member,
sd_bus_error *error,
char **ret) {
char **ret)
{
sd_bus_message *reply = NULL;
pf_autoptr (sd_bus_message) reply = NULL;
const char *s;
char *n;
int r;
@ -251,125 +301,146 @@ get_property_string (sd_bus *bus,
r = sd_bus_message_enter_container (reply, 'v', "s");
if (r < 0)
goto fail;
return sd_bus_error_set_errno (error, r);
r = sd_bus_message_read_basic (reply, 's', &s);
if (r < 0)
goto fail;
return sd_bus_error_set_errno (error, r);
n = strdup (s);
if (!n) {
r = -ENOMEM;
goto fail;
}
sd_bus_message_unref (reply);
if (!n)
return sd_bus_error_set_errno (error, -ENOMEM);
*ret = n;
return 0;
}
fail:
if (reply != NULL)
sd_bus_message_unref (reply);
return sd_bus_error_set_errno(error, r);
static int
verify_started_cb (sd_bus_message *m,
void *userdata,
sd_bus_error *ret_error)
{
const sd_bus_error *error = sd_bus_message_get_error (m);
verify_data *data = userdata;
if (error)
{
if (sd_bus_error_has_name (error, "net.reactivated.Fprint.Error.NoEnrolledPrints"))
{
pam_syslog (data->pamh, LOG_DEBUG, "No prints enrolled");
data->verify_ret = PAM_USER_UNKNOWN;
}
else
{
data->verify_ret = PAM_AUTH_ERR;
}
if (debug)
pam_syslog (data->pamh, LOG_DEBUG, "VerifyStart failed: %s", error->message);
return 1;
}
if (debug)
pam_syslog (data->pamh, LOG_DEBUG, "VerifyStart completed successfully");
data->verify_started = true;
return 1;
}
static int
do_verify (pam_handle_t *pamh,
sd_bus *bus,
const char *dev,
bool has_multiple_devices)
do_verify (sd_bus *bus,
verify_data *data)
{
verify_data *data;
sd_bus_slot *verify_status_slot, *verify_finger_selected_slot;
char *scan_type = NULL;
int ret;
pf_autoptr (sd_bus_slot) verify_status_slot = NULL;
pf_autoptr (sd_bus_slot) verify_finger_selected_slot = NULL;
pf_autofree char *scan_type = NULL;
int r;
data = calloc (1, sizeof(verify_data));
data->max_tries = max_tries;
data->pamh = pamh;
/* Get some properties for the device */
r = get_property_string (bus,
"net.reactivated.Fprint",
dev,
data->dev,
"net.reactivated.Fprint.Device",
"scan-type",
NULL,
&scan_type);
if (r < 0)
pam_syslog (data->pamh, LOG_ERR, "Failed to get scan-type for %s: %d", dev, r);
pam_syslog (data->pamh, LOG_ERR, "Failed to get scan-type for %s: %d", data->dev, r);
if (debug)
pam_syslog (data->pamh, LOG_DEBUG, "scan-type for %s: %s", dev, scan_type);
pam_syslog (data->pamh, LOG_DEBUG, "scan-type for %s: %s", data->dev, scan_type);
if (str_equal (scan_type, "swipe"))
data->is_swipe = true;
free (scan_type);
if (has_multiple_devices) {
if (data->has_multiple_devices)
{
get_property_string (bus,
"net.reactivated.Fprint",
dev,
data->dev,
"net.reactivated.Fprint.Device",
"name",
NULL,
&data->driver);
if (r < 0)
pam_syslog (data->pamh, LOG_ERR, "Failed to get driver name for %s: %d", dev, r);
pam_syslog (data->pamh, LOG_ERR, "Failed to get driver name for %s: %d", data->dev, r);
if (debug && r == 0)
pam_syslog (data->pamh, LOG_DEBUG, "driver name for %s: %s", dev, data->driver);
pam_syslog (data->pamh, LOG_DEBUG, "driver name for %s: %s", data->dev, data->driver);
}
verify_status_slot = NULL;
sd_bus_match_signal (bus,
&verify_status_slot,
"net.reactivated.Fprint",
dev,
data->dev,
"net.reactivated.Fprint.Device",
"VerifyStatus",
verify_result,
data);
verify_finger_selected_slot = NULL;
sd_bus_match_signal (bus,
&verify_finger_selected_slot,
"net.reactivated.Fprint",
dev,
data->dev,
"net.reactivated.Fprint.Device",
"VerifyFingerSelected",
verify_finger_selected,
data);
ret = PAM_AUTH_ERR;
while (ret == PAM_AUTH_ERR && data->max_tries > 0) {
while (data->max_tries > 0)
{
uint64_t verification_end = now () + (timeout * USEC_PER_SEC);
sd_bus_message *m = NULL;
sd_bus_error error = SD_BUS_ERROR_NULL;
data->timed_out = false;
data->verify_started = false;
data->verify_ret = PAM_INCOMPLETE;
r = sd_bus_call_method (bus,
free (data->result);
data->result = NULL;
if (debug)
pam_syslog (data->pamh, LOG_DEBUG, "About to call VerifyStart");
r = sd_bus_call_method_async (bus,
NULL,
"net.reactivated.Fprint",
dev,
data->dev,
"net.reactivated.Fprint.Device",
"VerifyStart",
&error,
&m,
verify_started_cb,
data,
"s",
"any");
if (r < 0) {
if (sd_bus_error_has_name (&error, "net.reactivated.Fprint.Error.NoEnrolledPrints"))
ret = PAM_USER_UNKNOWN;
if (r < 0)
{
if (debug)
pam_syslog (pamh, LOG_DEBUG, "VerifyStart failed: %s", error.message);
sd_bus_error_free (&error);
pam_syslog (data->pamh, LOG_DEBUG, "VerifyStart call failed: %d", r);
break;
}
for (;;) {
for (;;)
{
int64_t wait_time;
wait_time = verification_end - now ();
@ -379,29 +450,40 @@ do_verify (pam_handle_t *pamh,
r = sd_bus_process (bus, NULL);
if (r < 0)
break;
if (data->verify_ret != PAM_INCOMPLETE)
break;
if (!data->verify_started)
continue;
if (data->result != NULL)
break;
if (r == 0) {
if (debug) {
pam_syslog(pamh, LOG_DEBUG, "Waiting for %"PRId64" seconds (%"PRId64" usecs)",
if (r == 0)
{
if (debug)
{
pam_syslog (data->pamh, LOG_DEBUG,
"Waiting for %" PRId64 " seconds (%" PRId64 " usecs)",
wait_time / USEC_PER_SEC,
wait_time);
}
r = sd_bus_wait (bus, wait_time);
if (r < 0)
if (sd_bus_wait (bus, wait_time) < 0)
break;
}
}
if (now () >= verification_end) {
if (data->verify_ret != PAM_INCOMPLETE)
return data->verify_ret;
if (now () >= verification_end)
{
data->timed_out = true;
send_info_msg (data->pamh, _("Verification timed out"));
}
/* Ignore errors from VerifyStop */
data->verify_started = false;
sd_bus_call_method (bus,
"net.reactivated.Fprint",
dev,
data->dev,
"net.reactivated.Fprint.Device",
"VerifyStop",
NULL,
@ -409,44 +491,41 @@ do_verify (pam_handle_t *pamh,
NULL,
NULL);
if (data->timed_out) {
ret = PAM_AUTHINFO_UNAVAIL;
break;
} else {
if (str_equal (data->result, "verify-no-match")) {
send_err_msg (data->pamh, "Failed to match fingerprint");
ret = PAM_AUTH_ERR;
} else if (str_equal (data->result, "verify-match")) {
ret = PAM_SUCCESS;
break;
} else if (str_equal (data->result, "verify-unknown-error")) {
ret = PAM_AUTHINFO_UNAVAIL;
} else if (str_equal (data->result, "verify-disconnected")) {
ret = PAM_AUTHINFO_UNAVAIL;
break;
} else {
send_err_msg (data->pamh, _("An unknown error occurred"));
ret = PAM_AUTH_ERR;
break;
if (data->timed_out)
{
return PAM_AUTHINFO_UNAVAIL;
}
else
{
if (str_equal (data->result, "verify-no-match"))
{
send_err_msg (data->pamh, "Failed to match fingerprint");
}
else if (str_equal (data->result, "verify-match"))
{
return PAM_SUCCESS;
}
else if (str_equal (data->result, "verify-unknown-error"))
{
return PAM_AUTHINFO_UNAVAIL;
}
else if (str_equal (data->result, "verify-disconnected"))
{
return PAM_AUTHINFO_UNAVAIL;
}
else
{
send_err_msg (data->pamh, _("An unknown error occurred"));
return PAM_AUTH_ERR;
}
free (data->result);
data->result = NULL;
}
data->max_tries--;
}
if (data->max_tries == 0)
ret = PAM_MAXTRIES;
return PAM_MAXTRIES;
sd_bus_slot_unref (verify_status_slot);
sd_bus_slot_unref (verify_finger_selected_slot);
if (data->result)
free (data->result);
free (data->driver);
free (data);
return ret;
return PAM_AUTH_ERR;
}
static bool
@ -455,8 +534,8 @@ user_has_prints (pam_handle_t *pamh,
const char *dev,
const char *username)
{
sd_bus_error error = SD_BUS_ERROR_NULL;
sd_bus_message *m = NULL;
pf_auto (sd_bus_error) error = SD_BUS_ERROR_NULL;
pf_autoptr (sd_bus_message) m = NULL;
size_t num_fingers = 0;
const char *s;
int r;
@ -470,32 +549,29 @@ user_has_prints (pam_handle_t *pamh,
&m,
"s",
username);
if (r < 0) {
if (r < 0)
{
/* If ListEnrolledFingers fails then verification should
* also fail (both use the same underlying call), so we
* report false here and bail out early. */
if (debug) {
if (debug)
pam_syslog (pamh, LOG_DEBUG, "ListEnrolledFingers failed for %s: %s",
username, error.message);
}
sd_bus_error_free (&error);
return false;
}
r = sd_bus_message_enter_container (m, 'a', "s");
if (r < 0) {
if (r < 0)
{
pam_syslog (pamh, LOG_ERR, "Failed to parse answer from ListEnrolledFingers(): %d", r);
goto out;
return false;
}
num_fingers = 0;
while ((r = sd_bus_message_read_basic (m, 's', &s)) > 0)
num_fingers++;
sd_bus_message_exit_container (m);
out:
sd_bus_message_unref (m);
return (num_fingers > 0);
return num_fingers > 0;
}
static void
@ -503,10 +579,9 @@ release_device (pam_handle_t *pamh,
sd_bus *bus,
const char *dev)
{
sd_bus_error error = SD_BUS_ERROR_NULL;
int r;
pf_auto (sd_bus_error) error = SD_BUS_ERROR_NULL;
r = sd_bus_call_method (bus,
if (sd_bus_call_method (bus,
"net.reactivated.Fprint",
dev,
"net.reactivated.Fprint.Device",
@ -514,11 +589,8 @@ release_device (pam_handle_t *pamh,
&error,
NULL,
NULL,
NULL);
if (r < 0) {
NULL) < 0)
pam_syslog (pamh, LOG_ERR, "ReleaseDevice failed: %s", error.message);
sd_bus_error_free (&error);
}
}
static bool
@ -527,10 +599,9 @@ claim_device (pam_handle_t *pamh,
const char *dev,
const char *username)
{
sd_bus_error error = SD_BUS_ERROR_NULL;
int r;
pf_auto (sd_bus_error) error = SD_BUS_ERROR_NULL;
r = sd_bus_call_method (bus,
if (sd_bus_call_method (bus,
"net.reactivated.Fprint",
dev,
"net.reactivated.Fprint.Device",
@ -538,53 +609,96 @@ claim_device (pam_handle_t *pamh,
&error,
NULL,
"s",
username);
if (r < 0) {
username) < 0)
{
if (debug)
pam_syslog (pamh, LOG_DEBUG, "failed to claim device %s", error.message);
sd_bus_error_free (&error);
return false;
}
return true;
}
static int do_auth(pam_handle_t *pamh, const char *username)
static int
name_owner_changed (sd_bus_message *m,
void *userdata,
sd_bus_error *ret_error)
{
verify_data *data = userdata;
const char *name = NULL;
const char *old_owner = NULL;
const char *new_owner = NULL;
if (sd_bus_message_read (m, "sss", &name, &old_owner, &new_owner) < 0)
{
pam_syslog (data->pamh, LOG_ERR, "Failed to parse NameOwnerChanged signal: %d", errno);
data->verify_ret = PAM_AUTHINFO_UNAVAIL;
return 0;
}
if (strcmp (name, "net.reactivated.Fprint") != 0)
return 0;
/* Name owner for fprintd changed, give up as we might start listening
* to events from a new name owner otherwise. */
data->verify_ret = PAM_AUTHINFO_UNAVAIL;
pam_syslog (data->pamh, LOG_WARNING, "fprintd name owner changed during operation!");
return 0;
}
static int
do_auth (pam_handle_t *pamh, const char *username)
{
char *dev;
bool have_prints;
bool has_multiple_devices;
int ret = PAM_AUTHINFO_UNAVAIL;
sd_bus *bus = NULL;
if (sd_bus_open_system (&bus) < 0) {
pam_syslog (pamh, LOG_ERR, "Error with getting the bus: %m");
pf_autoptr (verify_data) data = NULL;
pf_autoptr (sd_bus) bus = NULL;
pf_autoptr (sd_bus_slot) name_owner_changed_slot = NULL;
data = calloc (1, sizeof (verify_data));
data->max_tries = max_tries;
data->pamh = pamh;
if (sd_bus_open_system (&bus) < 0)
{
pam_syslog (pamh, LOG_ERR, "Error with getting the bus: %d", errno);
return PAM_AUTHINFO_UNAVAIL;
}
dev = open_device (pamh, bus, &has_multiple_devices);
if (dev == NULL) {
sd_bus_unref (bus);
data->dev = open_device (pamh, bus, &data->has_multiple_devices);
if (data->dev == NULL)
return PAM_AUTHINFO_UNAVAIL;
}
have_prints = user_has_prints (pamh, bus, dev, username);
have_prints = user_has_prints (pamh, bus, data->dev, username);
if (debug)
pam_syslog (pamh, LOG_DEBUG, "prints registered: %s\n", have_prints ? "yes" : "no");
if (!have_prints)
goto out;
return PAM_AUTHINFO_UNAVAIL;
if (claim_device (pamh, bus, dev, username)) {
ret = do_verify (pamh, bus, dev, has_multiple_devices);
release_device (pamh, bus, dev);
/* Only connect to NameOwnerChanged when needed. In case of automatic startup
* we rely on the fact that we never see those signals.
*/
name_owner_changed_slot = NULL;
sd_bus_match_signal (bus,
&name_owner_changed_slot,
"org.freedesktop.DBus",
"/org/freedesktop/DBus",
"org.freedesktop.DBus",
"NameOwnerChanged",
name_owner_changed,
data);
if (claim_device (pamh, bus, data->dev, username))
{
int ret = do_verify (bus, data);
release_device (pamh, bus, data->dev);
return ret;
}
out:
free (dev);
sd_bus_unref (bus);
return ret;
return PAM_AUTHINFO_UNAVAIL;
}
static bool
@ -599,9 +713,8 @@ is_remote (pam_handle_t *pamh)
* We want to not run for known remote hosts */
if (rhost != NULL &&
*rhost != '\0' &&
strcmp (rhost, "localhost") != 0) {
strcmp (rhost, "localhost") != 0)
return true;
}
if (sd_session_is_remote (NULL) > 0)
return true;
@ -609,12 +722,12 @@ is_remote (pam_handle_t *pamh)
return false;
}
PAM_EXTERN int pam_sm_authenticate(pam_handle_t *pamh, int flags, int argc,
PAM_EXTERN int
pam_sm_authenticate (pam_handle_t *pamh, int flags, int argc,
const char **argv)
{
const char *username;
unsigned i;
int r;
int i;
bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
@ -622,72 +735,86 @@ PAM_EXTERN int pam_sm_authenticate(pam_handle_t *pamh, int flags, int argc,
if (is_remote (pamh))
return PAM_AUTHINFO_UNAVAIL;
r = pam_get_user(pamh, &username, NULL);
if (r != PAM_SUCCESS)
if (pam_get_user (pamh, &username, NULL) != PAM_SUCCESS)
return PAM_AUTHINFO_UNAVAIL;
for (i = 0; i < argc; i++) {
if (argv[i] != NULL) {
if (str_equal (argv[i], "debug")) {
for (i = 0; i < argc; i++)
{
if (argv[i] != NULL)
{
if (str_equal (argv[i], "debug"))
{
pam_syslog (pamh, LOG_DEBUG, "debug on");
debug = true;
} else if (str_has_prefix (argv[i], DEBUG_MATCH)) {
}
else if (str_has_prefix (argv[i], DEBUG_MATCH))
{
pam_syslog (pamh, LOG_DEBUG, "debug on");
const char *value;
value = argv[i] + strlen (DEBUG_MATCH);
if (str_equal (value, "on") ||
str_equal (value, "true") ||
str_equal (value, "1")) {
str_equal (value, "1"))
{
pam_syslog (pamh, LOG_DEBUG, "debug on");
debug = true;
} else if (str_equal (value, "off") ||
}
else if (str_equal (value, "off") ||
str_equal (value, "false") ||
str_equal (value, "0")) {
str_equal (value, "0"))
{
debug = false;
} else {
}
else
{
pam_syslog (pamh, LOG_DEBUG, "invalid debug value '%s', disabling", value);
}
} else if (str_has_prefix (argv[i], MAX_TRIES_MATCH) && strlen(argv[i]) == strlen (MAX_TRIES_MATCH) + 1) {
}
else if (str_has_prefix (argv[i], MAX_TRIES_MATCH) && strlen (argv[i]) == strlen (MAX_TRIES_MATCH) + 1)
{
max_tries = atoi (argv[i] + strlen (MAX_TRIES_MATCH));
if (max_tries < 1) {
if (debug) {
if (max_tries < 1)
{
if (debug)
pam_syslog (pamh, LOG_DEBUG, "invalid max tries '%s', using %d",
argv[i] + strlen (MAX_TRIES_MATCH), DEFAULT_MAX_TRIES);
}
max_tries = DEFAULT_MAX_TRIES;
}
if (debug)
pam_syslog (pamh, LOG_DEBUG, "max_tries specified as: %d", max_tries);
} else if (str_has_prefix (argv[i], TIMEOUT_MATCH) && strlen(argv[i]) <= strlen (TIMEOUT_MATCH) + 2) {
}
else if (str_has_prefix (argv[i], TIMEOUT_MATCH) && strlen (argv[i]) <= strlen (TIMEOUT_MATCH) + 2)
{
timeout = atoi (argv[i] + strlen (TIMEOUT_MATCH));
if (timeout < MIN_TIMEOUT) {
if (debug) {
if (timeout < MIN_TIMEOUT)
{
if (debug)
pam_syslog (pamh, LOG_DEBUG, "timeout %d secs too low, using %d",
timeout, MIN_TIMEOUT);
}
timeout = MIN_TIMEOUT;
} else if (debug) {
}
else if (debug)
{
pam_syslog (pamh, LOG_DEBUG, "timeout specified as: %d secs", timeout);
}
}
}
}
r = do_auth(pamh, username);
return r;
return do_auth (pamh, username);
}
PAM_EXTERN int pam_sm_setcred(pam_handle_t *pamh, int flags, int argc,
PAM_EXTERN int
pam_sm_setcred (pam_handle_t *pamh, int flags, int argc,
const char **argv)
{
return PAM_SUCCESS;
}
PAM_EXTERN int pam_sm_chauthtok(pam_handle_t *pamh, int flags, int argc,
PAM_EXTERN int
pam_sm_chauthtok (pam_handle_t *pamh, int flags, int argc,
const char **argv)
{
return PAM_SUCCESS;
}

View File

@ -0,0 +1,61 @@
/*
* pam_fprint: PAM module for fingerprint authentication through fprintd
* Copyright (C) 2020 Marco Trevisan <marco.trevisan@canonical.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#pragma once
#include <stdlib.h>
/* Define auto-pointers functions, based on GLib Macros */
#define _CLEANUP_FUNC(func) __attribute__((cleanup (func)))
#define _PF_AUTOPTR_FUNC_NAME(TypeName) pf_autoptr_cleanup_ ## TypeName
#define _PF_AUTOPTR_TYPENAME(TypeName) TypeName ## _pf_autoptr
#define PF_DEFINE_AUTOPTR_CLEANUP_FUNC(TypeName, cleanup) \
typedef TypeName *_PF_AUTOPTR_TYPENAME (TypeName); \
static __attribute__((__unused__)) inline void \
_PF_AUTOPTR_FUNC_NAME (TypeName) (TypeName **_ptr) \
{ if (_ptr) (cleanup) (*_ptr); }
#define PF_DEFINE_AUTO_CLEAN_FUNC(TypeName, cleanup) \
static __attribute__((__unused__)) inline void \
_PF_AUTOPTR_FUNC_NAME (TypeName) (TypeName *_ptr) \
{ cleanup (_ptr); }
static inline void
autoptr_cleanup_generic_free (void *p)
{
void **pp = (void **) p;
free (*pp);
}
#define pf_autofree _CLEANUP_FUNC (autoptr_cleanup_generic_free)
#define pf_autoptr(TypeName) \
_CLEANUP_FUNC (_PF_AUTOPTR_FUNC_NAME (TypeName)) \
_PF_AUTOPTR_TYPENAME (TypeName)
#define pf_auto(TypeName) \
_CLEANUP_FUNC (_PF_AUTOPTR_FUNC_NAME (TypeName)) TypeName
PF_DEFINE_AUTOPTR_CLEANUP_FUNC (sd_bus, sd_bus_unref)
PF_DEFINE_AUTOPTR_CLEANUP_FUNC (sd_bus_message, sd_bus_message_unref)
PF_DEFINE_AUTOPTR_CLEANUP_FUNC (sd_bus_slot, sd_bus_slot_unref)
PF_DEFINE_AUTO_CLEAN_FUNC (sd_bus_error, sd_bus_error_free)

137
scripts/uncrustify.cfg Normal file
View File

@ -0,0 +1,137 @@
newlines lf
input_tab_size 8
output_tab_size 8
string_escape_char 92
string_escape_char2 0
# indenting
indent_columns 2
indent_with_tabs 0
indent_align_string True
indent_brace 2
indent_braces false
indent_braces_no_func True
indent_func_call_param false
indent_func_def_param false
indent_func_proto_param false
indent_switch_case 0
indent_case_brace 2
indent_paren_close 1
# spacing
sp_arith Add
sp_assign Add
sp_enum_assign Add
sp_bool Add
sp_compare Add
sp_inside_paren Remove
sp_inside_fparens Remove
sp_func_def_paren Force
sp_func_proto_paren Force
sp_paren_paren Remove
sp_balance_nested_parens False
sp_paren_brace Remove
sp_before_square Remove
sp_before_squares Remove
sp_inside_square Remove
sp_before_ptr_star Add
sp_between_ptr_star Remove
sp_after_comma Add
sp_before_comma Remove
sp_after_cast Add
sp_sizeof_paren Add
sp_not Remove
sp_inv Remove
sp_addr Remove
sp_member Remove
sp_deref Remove
sp_sign Remove
sp_incdec Remove
sp_attribute_paren remove
sp_macro Force
sp_func_call_paren Force
sp_func_call_user_paren Remove
set func_call_user _ N_ C_ g_autoptr g_auto
sp_brace_typedef add
sp_cond_colon add
sp_cond_question add
sp_defined_paren remove
# alignment
align_keep_tabs False
align_with_tabs False
align_on_tabstop False
align_number_right False
align_func_params True
align_var_def_span 0
align_var_def_amp_style 1
align_var_def_colon true
align_enum_equ_span 0
align_var_struct_span 2
align_var_def_star_style 2
align_var_def_amp_style 2
align_typedef_span 2
align_typedef_func 0
align_typedef_star_style 2
align_typedef_amp_style 2
# newlines
nl_assign_leave_one_liners True
nl_enum_leave_one_liners False
nl_func_leave_one_liners False
nl_if_leave_one_liners False
nl_end_of_file Add
nl_assign_brace Remove
nl_func_var_def_blk 1
nl_fcall_brace Add
nl_enum_brace Remove
nl_struct_brace Force
nl_union_brace Force
nl_if_brace Force
nl_brace_else Force
nl_elseif_brace Force
nl_else_brace Add
nl_for_brace Force
nl_while_brace Force
nl_do_brace Force
nl_brace_while Force
nl_switch_brace Force
nl_before_case True
nl_after_case False
nl_func_type_name Force
nl_func_proto_type_name Remove
nl_func_paren Remove
nl_func_decl_start Remove
nl_func_decl_args Force
nl_func_decl_end Remove
nl_fdef_brace Force
nl_after_return False
nl_define_macro False
nl_create_if_one_liner False
nl_create_for_one_liner False
nl_create_while_one_liner False
nl_after_semicolon True
nl_multi_line_cond true
# mod
# I'd like these to be remove, but that removes brackets in if { if { foo } }, which i dislike
# Not clear what to do about that...
mod_full_brace_for Remove
mod_full_brace_if Remove
mod_full_brace_if_chain True
mod_full_brace_while Remove
mod_full_brace_do Remove
mod_full_brace_nl 3
mod_paren_on_return Remove
# line splitting
#code_width = 78
ls_for_split_full True
ls_func_split_full True
# positioning
pos_bool Trail
pos_conditional Trail

19
scripts/uncrustify.sh Executable file
View File

@ -0,0 +1,19 @@
#!/bin/bash
SRCROOT=`git rev-parse --show-toplevel`
CFG="$SRCROOT/scripts/uncrustify.cfg"
echo "srcroot: $SRCROOT"
case "$1" in
-c|--check)
OPTS="--check"
;;
*)
OPTS="--replace --no-backup"
;;
esac
pushd "$SRCROOT"
uncrustify -c "$CFG" $OPTS `git ls-tree --name-only -r HEAD | grep -E '.*\.[ch]$' | grep -v build/`
RES=$?
popd
exit $RES

View File

@ -0,0 +1,110 @@
--- a/src/fprintd-dbus.c 2020-12-04 16:38:28.527712626 +0100
+++ b/src/fprintd-dbus.c 2020-12-04 16:40:03.561692619 +0100
@@ -1149,7 +1149,7 @@
"ListEnrolledFingers",
g_variant_new ("(s)",
arg_username),
- G_DBUS_CALL_FLAGS_NONE,
+ G_DBUS_CALL_FLAGS_ALLOW_INTERACTIVE_AUTHORIZATION,
-1,
cancellable,
callback,
@@ -1213,7 +1213,7 @@
"ListEnrolledFingers",
g_variant_new ("(s)",
arg_username),
- G_DBUS_CALL_FLAGS_NONE,
+ G_DBUS_CALL_FLAGS_ALLOW_INTERACTIVE_AUTHORIZATION,
-1,
cancellable,
error);
@@ -1253,7 +1253,7 @@
"DeleteEnrolledFingers",
g_variant_new ("(s)",
arg_username),
- G_DBUS_CALL_FLAGS_NONE,
+ G_DBUS_CALL_FLAGS_ALLOW_INTERACTIVE_AUTHORIZATION,
-1,
cancellable,
callback,
@@ -1312,7 +1312,7 @@
"DeleteEnrolledFingers",
g_variant_new ("(s)",
arg_username),
- G_DBUS_CALL_FLAGS_NONE,
+ G_DBUS_CALL_FLAGS_ALLOW_INTERACTIVE_AUTHORIZATION,
-1,
cancellable,
error);
@@ -1348,7 +1348,7 @@
g_dbus_proxy_call (G_DBUS_PROXY (proxy),
"DeleteEnrolledFingers2",
g_variant_new ("()"),
- G_DBUS_CALL_FLAGS_NONE,
+ G_DBUS_CALL_FLAGS_ALLOW_INTERACTIVE_AUTHORIZATION,
-1,
cancellable,
callback,
@@ -1404,7 +1404,7 @@
_ret = g_dbus_proxy_call_sync (G_DBUS_PROXY (proxy),
"DeleteEnrolledFingers2",
g_variant_new ("()"),
- G_DBUS_CALL_FLAGS_NONE,
+ G_DBUS_CALL_FLAGS_ALLOW_INTERACTIVE_AUTHORIZATION,
-1,
cancellable,
error);
@@ -1443,7 +1443,7 @@
"Claim",
g_variant_new ("(s)",
arg_username),
- G_DBUS_CALL_FLAGS_NONE,
+ G_DBUS_CALL_FLAGS_ALLOW_INTERACTIVE_AUTHORIZATION,
-1,
cancellable,
callback,
@@ -1502,7 +1502,7 @@
"Claim",
g_variant_new ("(s)",
arg_username),
- G_DBUS_CALL_FLAGS_NONE,
+ G_DBUS_CALL_FLAGS_ALLOW_INTERACTIVE_AUTHORIZATION,
-1,
cancellable,
error);
@@ -1633,7 +1633,7 @@
"VerifyStart",
g_variant_new ("(s)",
arg_finger_name),
- G_DBUS_CALL_FLAGS_NONE,
+ G_DBUS_CALL_FLAGS_ALLOW_INTERACTIVE_AUTHORIZATION,
-1,
cancellable,
callback,
@@ -1692,7 +1692,7 @@
"VerifyStart",
g_variant_new ("(s)",
arg_finger_name),
- G_DBUS_CALL_FLAGS_NONE,
+ G_DBUS_CALL_FLAGS_ALLOW_INTERACTIVE_AUTHORIZATION,
-1,
cancellable,
error);
@@ -1823,7 +1823,7 @@
"EnrollStart",
g_variant_new ("(s)",
arg_finger_name),
- G_DBUS_CALL_FLAGS_NONE,
+ G_DBUS_CALL_FLAGS_ALLOW_INTERACTIVE_AUTHORIZATION,
-1,
cancellable,
callback,
@@ -1882,7 +1882,7 @@
"EnrollStart",
g_variant_new ("(s)",
arg_finger_name),
- G_DBUS_CALL_FLAGS_NONE,
+ G_DBUS_CALL_FLAGS_ALLOW_INTERACTIVE_AUTHORIZATION,
-1,
cancellable,
error);

File diff suppressed because it is too large Load Diff

View File

@ -44,7 +44,8 @@
static char *storage_path = NULL;
static const char *get_storage_path(void)
static const char *
get_storage_path (void)
{
const char *path = NULL;
@ -55,10 +56,12 @@ static const char *get_storage_path(void)
* taking into account the StateDirectory
* unit file setting */
path = g_getenv ("STATE_DIRECTORY");
if (path != NULL) {
if (path != NULL)
{
/* If multiple directories are set, then in the environment variable
* the paths are concatenated with colon (":"). */
if (strchr (path, ':')) {
if (strchr (path, ':'))
{
g_auto(GStrv) elems = NULL;
elems = g_strsplit (path, ":", -1);
storage_path = g_strdup (elems[0]);
@ -71,12 +74,14 @@ static const char *get_storage_path(void)
return storage_path;
}
static char *get_path_to_storedir(const char *driver, const char * device_id, char *base_store)
static char *
get_path_to_storedir (const char *driver, const char * device_id, char *base_store)
{
return g_build_filename (base_store, driver, device_id, NULL);
}
static char *__get_path_to_print(const char *driver, const char * device_id,
static char *
__get_path_to_print (const char *driver, const char * device_id,
FpFinger finger, char *base_store)
{
g_autofree char *dirpath = NULL;
@ -90,7 +95,8 @@ static char *__get_path_to_print(const char *driver, const char * device_id,
return path;
}
static char *get_path_to_print(FpDevice *dev, FpFinger finger, char *base_store)
static char *
get_path_to_print (FpDevice *dev, FpFinger finger, char *base_store)
{
return __get_path_to_print (fp_device_get_driver (dev),
fp_device_get_device_id (dev),
@ -98,7 +104,8 @@ static char *get_path_to_print(FpDevice *dev, FpFinger finger, char *base_store)
base_store);
}
static char *get_path_to_print_dscv(FpDevice *dev, FpFinger finger, char *base_store)
static char *
get_path_to_print_dscv (FpDevice *dev, FpFinger finger, char *base_store)
{
return __get_path_to_print (fp_device_get_driver (dev),
fp_device_get_device_id (dev),
@ -106,12 +113,14 @@ static char *get_path_to_print_dscv(FpDevice *dev, FpFinger finger, char *base_s
base_store);
}
static char *file_storage_get_basestore_for_username(const char *username)
static char *
file_storage_get_basestore_for_username (const char *username)
{
return g_build_filename (get_storage_path (), username, NULL);
}
int file_storage_print_data_save(FpPrint *print)
int
file_storage_print_data_save (FpPrint *print)
{
g_autoptr(GError) err = NULL;
g_autofree char *path = NULL;
@ -123,7 +132,8 @@ int file_storage_print_data_save(FpPrint *print)
base_store = file_storage_get_basestore_for_username (fp_print_get_username (print));
if (!fp_print_serialize (print, (guchar **) &buf, &len, &err)) {
if (!fp_print_serialize (print, (guchar **) &buf, &len, &err))
{
g_warning ("Error serializing data: %s", err->message);
return -ENOMEM;
}
@ -134,7 +144,8 @@ int file_storage_print_data_save(FpPrint *print)
base_store);
dirpath = g_path_get_dirname (path);
r = g_mkdir_with_parents (dirpath, DIR_PERMS);
if (r < 0) {
if (r < 0)
{
g_debug ("file_storage_print_data_save(): could not mkdir(\"%s\"): %s",
dirpath, g_strerror (r));
return r;
@ -142,7 +153,8 @@ int file_storage_print_data_save(FpPrint *print)
//fp_dbg("saving to %s", path);
g_file_set_contents (path, buf, len, &err);
if (err) {
if (err)
{
g_debug ("file_storage_print_data_save(): could not save '%s': %s",
path, err->message);
/* FIXME interpret error codes */
@ -152,7 +164,8 @@ int file_storage_print_data_save(FpPrint *print)
return 0;
}
static int load_from_file(char *path, FpPrint **print)
static int
load_from_file (char *path, FpPrint **print)
{
g_autoptr(GError) err = NULL;
gsize length;
@ -161,7 +174,8 @@ static int load_from_file(char *path, FpPrint **print)
//fp_dbg("from %s", path);
g_file_get_contents (path, &contents, &length, &err);
if (err) {
if (err)
{
int r = err->code;
/* FIXME interpret more error codes */
if (r == G_FILE_ERROR_NOENT)
@ -171,7 +185,8 @@ static int load_from_file(char *path, FpPrint **print)
}
new = fp_print_deserialize ((guchar *) contents, length, &err);
if (!new) {
if (!new)
{
g_print ("Error deserializing data: %s", err->message);
return -EIO;
}
@ -180,13 +195,15 @@ static int load_from_file(char *path, FpPrint **print)
return 0;
}
int file_storage_print_data_load(FpDevice *dev,
int
file_storage_print_data_load (FpDevice *dev,
FpFinger finger,
const char *username,
FpPrint **print)
{
g_autofree gchar *path = NULL;
g_autofree gchar *base_store = NULL;
g_autoptr(FpPrint) new = NULL;
int r;
@ -199,15 +216,15 @@ int file_storage_print_data_load(FpDevice *dev,
if (r)
return r;
if (!fp_print_compatible (new, dev)) {
if (!fp_print_compatible (new, dev))
return -EINVAL;
}
*print = g_steal_pointer (&new);
return 0;
}
int file_storage_print_data_delete(FpDevice *dev, FpFinger finger, const char *username)
int
file_storage_print_data_delete (FpDevice *dev, FpFinger finger, const char *username)
{
g_autofree gchar *base_store = NULL;
g_autofree gchar *path = NULL;
@ -225,19 +242,23 @@ int file_storage_print_data_delete(FpDevice *dev, FpFinger finger, const char *u
return g_unlink (path);
}
static GSList *scan_dev_storedir(char *devpath,
static GSList *
scan_dev_storedir (char *devpath,
GSList *list)
{
g_autoptr(GError) err = NULL;
const gchar *ent;
GDir *dir = g_dir_open (devpath, 0, &err);
if (!dir) {
if (!dir)
{
g_debug ("scan_dev_storedir(): opendir(\"%s\") failed: %s", devpath, err->message);
return list;
}
while ((ent = g_dir_read_name(dir))) {
while ((ent = g_dir_read_name (dir)))
{
/* ent is an 1 hex character fp_finger code */
guint64 val;
gchar *endptr;
@ -246,19 +267,21 @@ static GSList *scan_dev_storedir(char *devpath,
continue;
val = g_ascii_strtoull (ent, &endptr, 16);
if (endptr == ent || !FP_FINGER_IS_VALID(val)) {
if (endptr == ent || !FP_FINGER_IS_VALID (val))
{
g_debug ("scan_dev_storedir(): skipping print file '%s'", ent);
continue;
}
list = g_slist_prepend(list, GINT_TO_POINTER(val));
list = g_slist_prepend (list, GUINT_TO_POINTER (val));
}
g_dir_close (dir);
return list;
}
GSList *file_storage_discover_prints(FpDevice *dev, const char *username)
GSList *
file_storage_discover_prints (FpDevice *dev, const char *username)
{
GSList *list = NULL;
g_autofree gchar *base_store = NULL;
@ -278,18 +301,19 @@ GSList *file_storage_discover_prints(FpDevice *dev, const char *username)
return list;
}
GSList *file_storage_discover_users(void)
GSList *
file_storage_discover_users (void)
{
g_autoptr(GError) err = NULL;
GSList *list = NULL;
const gchar *ent;
GDir *dir = g_dir_open (get_storage_path (), 0, &err);
if (!dir) {
if (!dir)
return list;
}
while ((ent = g_dir_read_name(dir))) {
while ((ent = g_dir_read_name (dir)))
{
/* ent is a username */
if (*ent == 0)
continue;
@ -301,13 +325,15 @@ GSList *file_storage_discover_users(void)
return list;
}
int file_storage_init(void)
int
file_storage_init (void)
{
/* Nothing to do */
return 0;
}
int file_storage_deinit(void)
int
file_storage_deinit (void)
{
g_clear_pointer (&storage_path, g_free);
return 0;

View File

@ -35,5 +35,6 @@ int file_storage_init(void);
int file_storage_deinit (void);
GSList *file_storage_discover_prints(FpDevice *dev, const char *username);
GSList *file_storage_discover_prints (FpDevice *dev,
const char *username);
GSList *file_storage_discover_users (void);

View File

@ -53,29 +53,37 @@ typedef enum {
FPRINT_ERROR_NO_SUCH_DEVICE, /*< nick=net.reactivated.Fprint.Error.NoSuchDevice >*/
} FprintError;
/* Enum of possible permissions, orders and nick matter here:
- The order controls the priority of a required permission when various are
accepted: the lowest the value, the more priorty it has.
- Nick must match the relative polkit rule.
*/
typedef enum {
FPRINT_DEVICE_PERMISSION_NONE = 0,
FPRINT_DEVICE_PERMISSION_ENROLL = (1 << 0), /*< nick=net.reactivated.fprint.device.enroll >*/
FPRINT_DEVICE_PERMISSION_SETUSERNAME = (1 << 1), /*< nick=net.reactivated.fprint.device.setusername >*/
FPRINT_DEVICE_PERMISSION_VERIFY = (1 << 2), /*< nick=net.reactivated.fprint.device.verify >*/
FPRINT_DEVICE_PERMISSION_VERIFY = (1 << 0), /*< nick=net.reactivated.fprint.device.verify >*/
FPRINT_DEVICE_PERMISSION_ENROLL = (1 << 1), /*< nick=net.reactivated.fprint.device.enroll >*/
FPRINT_DEVICE_PERMISSION_SETUSERNAME = (1 << 2), /*< nick=net.reactivated.fprint.device.setusername >*/
} FprintDevicePermission;
/* Manager */
#define FPRINT_TYPE_MANAGER (fprint_manager_get_type ())
G_DECLARE_FINAL_TYPE (FprintManager, fprint_manager, FPRINT, MANAGER, GObject)
struct _FprintManager {
struct _FprintManager
{
GObject parent;
};
FprintManager *fprint_manager_new (GDBusConnection *connection, gboolean no_timeout);
FprintManager *fprint_manager_new (GDBusConnection *connection,
gboolean no_timeout);
/* Device */
#define FPRINT_TYPE_DEVICE (fprint_device_get_type ())
G_DECLARE_FINAL_TYPE (FprintDevice, fprint_device, FPRINT, DEVICE,
FprintDBusDeviceSkeleton)
struct _FprintDevice {
struct _FprintDevice
{
FprintDBusDeviceSkeleton parent;
};

View File

@ -69,7 +69,8 @@ load_storage_module (const char *module_name)
!g_module_symbol (module, "print_data_save", (gpointer *) &store.print_data_save) ||
!g_module_symbol (module, "print_data_load", (gpointer *) &store.print_data_load) ||
!g_module_symbol (module, "print_data_delete", (gpointer *) &store.print_data_delete) ||
!g_module_symbol (module, "discover_prints", (gpointer *) &store.discover_prints)) {
!g_module_symbol (module, "discover_prints", (gpointer *) &store.discover_prints))
{
g_module_close (module);
return FALSE;
}
@ -84,13 +85,15 @@ load_conf (void)
{
g_autofree char *filename = NULL;
g_autofree char *module_name = NULL;
g_autoptr(GKeyFile) file = NULL;
g_autoptr(GError) error = NULL;
filename = g_build_filename (SYSCONFDIR, "fprintd.conf", NULL);
file = g_key_file_new ();
g_debug ("About to load configuration file '%s'", filename);
if (!g_key_file_load_from_file (file, filename, G_KEY_FILE_NONE, &error)) {
if (!g_key_file_load_from_file (file, filename, G_KEY_FILE_NONE, &error))
{
g_warning ("Could not open \"%s\": %s\n", filename, error->message);
return FALSE;
}
@ -99,7 +102,8 @@ load_conf (void)
if (module_name == NULL)
return FALSE;
if (g_str_equal (module_name, "file")) {
if (g_str_equal (module_name, "file"))
{
set_storage_file ();
return TRUE;
}
@ -113,7 +117,8 @@ static const GOptionEntry entries[] = {
{ NULL }
};
static gboolean sigterm_callback(gpointer data)
static gboolean
sigterm_callback (gpointer data)
{
GMainLoop *loop = data;
@ -141,7 +146,8 @@ on_name_lost (GDBusConnection *connection,
g_main_loop_quit (loop);
}
int main(int argc, char **argv)
int
main (int argc, char **argv)
{
g_autoptr(GOptionContext) context = NULL;
g_autoptr(GMainLoop) loop = NULL;
@ -159,12 +165,14 @@ int main(int argc, char **argv)
context = g_option_context_new ("Fingerprint handler daemon");
g_option_context_add_main_entries (context, entries, GETTEXT_PACKAGE);
if (g_option_context_parse (context, &argc, &argv, &error) == FALSE) {
if (g_option_context_parse (context, &argc, &argv, &error) == FALSE)
{
g_warning ("couldn't parse command-line options: %s\n", error->message);
return 1;
}
if (g_fatal_warnings) {
if (g_fatal_warnings)
{
GLogLevelFlags fatal_mask;
fatal_mask = g_log_set_always_fatal (G_LOG_FATAL_MASK);
@ -174,7 +182,8 @@ int main(int argc, char **argv)
/* Obtain a connection to the system bus */
connection = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, &error);
if (!G_IS_DBUS_CONNECTION (connection)) {
if (!G_IS_DBUS_CONNECTION (connection))
{
g_warning ("Failed to open connection to bus: %s", error->message);
return 1;
}
@ -211,4 +220,3 @@ int main(int argc, char **argv)
return 0;
}

View File

@ -29,9 +29,11 @@
static void fprint_manager_constructed (GObject *object);
static gboolean fprint_manager_get_devices (FprintManager *manager,
GPtrArray **devices, GError **error);
GPtrArray **devices,
GError **error);
static gboolean fprint_manager_get_default_device (FprintManager *manager,
const char **device, GError **error);
const char **device,
GError **error);
typedef struct
{
@ -53,7 +55,8 @@ enum {
static GParamSpec *properties[N_PROPS];
static void fprint_manager_finalize(GObject *object)
static void
fprint_manager_finalize (GObject *object)
{
FprintManagerPrivate *priv = fprint_manager_get_instance_private (FPRINT_MANAGER (object));
@ -66,46 +69,54 @@ static void fprint_manager_finalize(GObject *object)
}
static FprintDevice *
fprint_dbus_object_skeleton_get_device (FprintDBusObjectSkeleton *object) {
fprint_dbus_object_skeleton_get_device (FprintDBusObjectSkeleton *object)
{
FprintDevice *rdev;
g_object_get (object, "device", &rdev, NULL);
return rdev;
}
static void fprint_manager_set_property (GObject *object, guint property_id,
static void
fprint_manager_set_property (GObject *object, guint property_id,
const GValue *value, GParamSpec *pspec)
{
FprintManager *self = FPRINT_MANAGER (object);
FprintManagerPrivate *priv = fprint_manager_get_instance_private (self);
switch (property_id) {
switch (property_id)
{
case FPRINT_MANAGER_CONNECTION:
priv->connection = g_value_dup_object (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static void fprint_manager_get_property (GObject *object, guint property_id,
static void
fprint_manager_get_property (GObject *object, guint property_id,
GValue *value, GParamSpec *pspec)
{
FprintManager *self = FPRINT_MANAGER (object);
FprintManagerPrivate *priv = fprint_manager_get_instance_private (self);
switch (property_id) {
switch (property_id)
{
case FPRINT_MANAGER_CONNECTION:
g_value_set_object (value, priv->connection);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static void fprint_manager_class_init(FprintManagerClass *klass)
static void
fprint_manager_class_init (FprintManagerClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
@ -125,7 +136,8 @@ static void fprint_manager_class_init(FprintManagerClass *klass)
g_object_class_install_properties (object_class, N_PROPS, properties);
}
static gchar *get_device_path(FprintDevice *rdev)
static gchar *
get_device_path (FprintDevice *rdev)
{
return g_strdup_printf (FPRINT_SERVICE_PATH "/Device/%d",
_fprint_device_get_id (rdev));
@ -144,11 +156,13 @@ fprint_manager_in_use_notified (FprintDevice *rdev, GParamSpec *spec, FprintMana
{
FprintManagerPrivate *priv = fprint_manager_get_instance_private (manager);
guint num_devices_used = 0;
g_autolist (GDBusObject) devices = NULL;
GList *l;
gboolean in_use;
if (priv->timeout_id > 0) {
if (priv->timeout_id > 0)
{
g_source_remove (priv->timeout_id);
priv->timeout_id = 0;
}
@ -157,7 +171,8 @@ fprint_manager_in_use_notified (FprintDevice *rdev, GParamSpec *spec, FprintMana
devices = g_dbus_object_manager_get_objects (priv->object_manager);
for (l = devices; l != NULL; l = l->next) {
for (l = devices; l != NULL; l = l->next)
{
g_autoptr(FprintDevice) dev = NULL;
FprintDBusObjectSkeleton *object = l->data;
@ -178,7 +193,8 @@ handle_get_devices (FprintManager *manager, GDBusMethodInvocation *invocation,
g_autoptr(GPtrArray) devices = NULL;
g_autoptr(GError) error = NULL;
if (!fprint_manager_get_devices (manager, &devices, &error)) {
if (!fprint_manager_get_devices (manager, &devices, &error))
{
g_dbus_method_invocation_return_gerror (invocation, error);
return TRUE;
}
@ -196,9 +212,11 @@ handle_get_default_device (FprintManager *manager,
FprintDBusManager *skeleton)
{
const gchar *device;
g_autoptr(GError) error = NULL;
if (!fprint_manager_get_default_device (manager, &device, &error)) {
if (!fprint_manager_get_default_device (manager, &device, &error))
{
g_dbus_method_invocation_return_gerror (invocation, error);
return TRUE;
}
@ -213,6 +231,7 @@ static void
device_added_cb (FprintManager *manager, FpDevice *device, FpContext *context)
{
FprintManagerPrivate *priv = fprint_manager_get_instance_private (manager);
g_autoptr(FprintDBusObjectSkeleton) object = NULL;
g_autoptr(FprintDevice) rdev = NULL;
g_autofree gchar *path = NULL;
@ -236,12 +255,14 @@ static void
device_removed_cb (FprintManager *manager, FpDevice *device, FpContext *context)
{
FprintManagerPrivate *priv = fprint_manager_get_instance_private (manager);
g_autolist (FprintDBusObjectSkeleton) objects = NULL;
GList *item;
objects = g_dbus_object_manager_get_objects (priv->object_manager);
for (item = objects; item; item = item->next) {
for (item = objects; item; item = item->next)
{
g_autoptr(FprintDevice) rdev = NULL;
g_autoptr(FpDevice) dev = NULL;
FprintDBusObjectSkeleton *object = item->data;
@ -266,14 +287,15 @@ device_removed_cb (FprintManager *manager, FpDevice *device, FpContext *context)
fprint_manager_in_use_notified (NULL, NULL, manager);
}
static void fprint_manager_constructed (GObject *object)
static void
fprint_manager_constructed (GObject *object)
{
FprintManager *manager = FPRINT_MANAGER (object);
FprintManagerPrivate *priv = fprint_manager_get_instance_private (manager);
GDBusObjectManagerServer *object_manager_server;
object_manager_server =
g_dbus_object_manager_server_new (FPRINT_SERVICE_PATH "/Device");
g_dbus_object_manager_server_new (FPRINT_SERVICE_PATH);
priv->object_manager = G_DBUS_OBJECT_MANAGER (object_manager_server);
priv->dbus_manager = fprint_dbus_manager_skeleton_new ();
@ -323,7 +345,8 @@ fprint_manager_init (FprintManager *manager)
{
}
FprintManager *fprint_manager_new (GDBusConnection *connection, gboolean no_timeout)
FprintManager *
fprint_manager_new (GDBusConnection *connection, gboolean no_timeout)
{
FprintManagerPrivate *priv;
GObject *object;
@ -338,10 +361,12 @@ FprintManager *fprint_manager_new (GDBusConnection *connection, gboolean no_time
return FPRINT_MANAGER (object);
}
static gboolean fprint_manager_get_devices(FprintManager *manager,
static gboolean
fprint_manager_get_devices (FprintManager *manager,
GPtrArray **devices, GError **error)
{
FprintManagerPrivate *priv = fprint_manager_get_instance_private (manager);
g_autolist (FprintDBusObjectSkeleton) objects = NULL;
GList *l;
int num_open;
@ -353,8 +378,10 @@ static gboolean fprint_manager_get_devices(FprintManager *manager,
num_open = g_list_length (objects);
devs = g_ptr_array_sized_new (num_open);
if (num_open > 0) {
for (l = objects; l != NULL; l = l->next) {
if (num_open > 0)
{
for (l = objects; l != NULL; l = l->next)
{
g_autoptr(FprintDevice) rdev = NULL;
FprintDBusObjectSkeleton *object = l->data;
const char *path;
@ -371,17 +398,20 @@ static gboolean fprint_manager_get_devices(FprintManager *manager,
return TRUE;
}
static gboolean fprint_manager_get_default_device(FprintManager *manager,
static gboolean
fprint_manager_get_default_device (FprintManager *manager,
const char **device, GError **error)
{
FprintManagerPrivate *priv = fprint_manager_get_instance_private (manager);
g_autolist (FprintDBusObjectSkeleton) objects = NULL;
int num_open;
objects = g_dbus_object_manager_get_objects (priv->object_manager);
num_open = g_list_length (objects);
if (num_open > 0) {
if (num_open > 0)
{
g_autoptr(FprintDevice) rdev = NULL;
FprintDBusObjectSkeleton *object = g_list_last (objects)->data;
@ -389,7 +419,9 @@ static gboolean fprint_manager_get_default_device(FprintManager *manager,
*device = g_dbus_interface_skeleton_get_object_path (
G_DBUS_INTERFACE_SKELETON (rdev));
return TRUE;
} else {
}
else
{
g_set_error (error, FPRINT_ERROR, FPRINT_ERROR_NO_SUCH_DEVICE,
"No devices available");
*device = NULL;
@ -397,10 +429,13 @@ static gboolean fprint_manager_get_default_device(FprintManager *manager,
}
}
GQuark fprint_error_quark (void)
GQuark
fprint_error_quark (void)
{
static volatile gsize quark = 0;
if (g_once_init_enter (&quark)) {
if (g_once_init_enter (&quark))
{
g_autoptr(GEnumClass) errors_enum = NULL;
GQuark domain;
unsigned i;
@ -408,7 +443,8 @@ GQuark fprint_error_quark (void)
domain = g_quark_from_static_string ("fprintd-error-quark");
errors_enum = g_type_class_ref (FPRINT_TYPE_ERROR);
for (i = 0; i < errors_enum->n_values; ++i) {
for (i = 0; i < errors_enum->n_values; ++i)
{
GEnumValue *value = &errors_enum->values[i];
g_dbus_error_register_error (domain, value->value,

View File

@ -14,7 +14,8 @@ foreach interface_name: dbus_interfaces
)
endforeach
fprintd_dbus_sources = gnome.gdbus_codegen('fprintd-dbus',
# NOTE: We should pass "--glib-min-required 2.64" but cannot
fprintd_dbus_sources_base = gnome.gdbus_codegen('fprintd-dbus',
sources: dbus_interfaces_files,
autocleanup: 'all',
interface_prefix: 'net.reactivated.Fprint.',
@ -22,6 +23,23 @@ fprintd_dbus_sources = gnome.gdbus_codegen('fprintd-dbus',
object_manager: true,
)
# FIXME: remove this and just use fprintd_dbus_sources when we're on glib 2.64
fprintd_dbus_sources = [
fprintd_dbus_sources_base[1] # header file
]
fprintd_dbus_sources += custom_target('fprintd-dbus-interactive',
input: fprintd_dbus_sources_base[0], # c file,
output: 'fprintd-dbus-interactive.c',
command: [
find_program('patch'),
'-p1',
'--merge',
'@INPUT@',
files('dbus-interactive-auth.patch'),
'-o', '@OUTPUT@',
])
fprintd_enum_files = gnome.mkenums_simple('fprintd-enums',
sources: 'fprintd.h',
)

View File

@ -28,12 +28,14 @@ typedef int (*storage_print_data_load)(FpDevice *dev,
typedef int (*storage_print_data_delete)(FpDevice *dev,
FpFinger finger,
const char *username);
typedef GSList *(*storage_discover_prints)(FpDevice *dev, const char *username);
typedef GSList *(*storage_discover_prints)(FpDevice *dev,
const char *username);
typedef GSList *(*storage_discover_users)(void);
typedef int (*storage_init)(void);
typedef int (*storage_deinit)(void);
struct storage {
struct storage
{
storage_init init;
storage_deinit deinit;
storage_print_data_save print_data_save;

View File

@ -18,6 +18,8 @@ __email__ = 'hadess@hadess.net'
__copyright__ = '(c) 2020 Red Hat Inc.'
__license__ = 'LGPL 3+'
import sys
from gi.repository import GLib
import dbus
import asyncio
@ -213,9 +215,22 @@ def can_verify_finger(device, finger_name):
return True
return False
async def send_verify_script(device, script):
for [result, done, timeout] in device.verify_script:
await asyncio.sleep(timeout)
def glib_sleep(timeout):
waiting = True
def done_waiting():
nonlocal waiting
waiting = False
GLib.timeout_add(timeout, done_waiting)
while (waiting):
GLib.main_context_default().iteration(True)
def device_run_script(device, result, done):
if result == 'MOCK: quit':
sys.exit(0)
# Emit signal
device.EmitSignal(DEVICE_IFACE, 'VerifyStatus', 'sb', [
result,
done
@ -249,13 +264,44 @@ def VerifyStart(device, finger_name):
if finger_name == 'any' and not device.has_identification:
finger_name = device.fingers[device.claimed_user][0]
device.selected_finger = finger_name
device.EmitSignal(DEVICE_IFACE, 'VerifyFingerSelected', 's', [
finger_name
# Needs to happen after method return
GLib.idle_add(device.EmitSignal,
DEVICE_IFACE, 'VerifyFingerSelected', 's', [
device.selected_finger
])
if device.verify_script is not None and len(device.verify_script) > 0:
asyncio.run(send_verify_script(device, device.verify_script))
error = None
base_delay = 0
while device.verify_script is not None and len(device.verify_script) > 0:
result, done, timeout = device.verify_script.pop(0)
# We stop when "timeout >= 0 and done"
if result == 'MOCK: no-prints':
# Special case to change return value of DBus call, ignores timeout
error = dbus.exceptions.DBusException(
'No enrolled prints for user \'%s\'' % device.claimed_user,
name='net.reactivated.Fprint.Error.NoEnrolledPrints')
elif timeout < 0:
# Negative timeouts mean emitting before the DBus call returns
device_run_script(device, result, done)
glib_sleep(-timeout)
else:
# Positive or zero means emitting afterwards the given timeout
base_delay += timeout
GLib.timeout_add(base_delay,
device_run_script,
device,
result,
done)
# Stop processing commands when the done flag is set
if done:
break
if error:
raise error
@dbus.service.method(DEVICE_MOCK_IFACE,
in_signature='sb', out_signature='')
@ -383,3 +429,11 @@ def SetVerifyScript(device, script):
'''
device.verify_script = script
@dbus.service.method(DEVICE_MOCK_IFACE,
in_signature='s', out_signature='')
def SetClaimed(device, user):
if user == '':
device.claimed_user = None
else:
device.claimed_user = user

View File

@ -138,6 +138,8 @@ class FPrintdTest(dbusmock.DBusTestCase):
fprintd = None
cls._polkitd = None
cls._has_hotplug = FPrint.Device.find_property("removed") is not None
if 'FPRINT_BUILD_DIR' in os.environ:
print('Testing local build')
build_dir = os.environ['FPRINT_BUILD_DIR']
@ -491,32 +493,55 @@ class FPrintdManagerPreStartTests(FPrintdTest):
self.manager.GetDefaultDevice()
def test_manager_get_devices_on_name_appeared(self):
self._appeared_res = []
self._appeared_name = None
def on_name_appeared(connection, name, name_owner):
self._appeared_res.append(connection.call_sync('net.reactivated.Fprint',
'/net/reactivated/Fprint/Manager',
'net.reactivated.Fprint.Manager',
'GetDefaultDevice', None, None,
Gio.DBusCallFlags.NO_AUTO_START, 500, None))
self._appeared_name = name
def on_name_vanished(connection, name):
self._appeared_name = 'NAME_VANISHED'
id = Gio.bus_watch_name_on_connection(self.dbus,
'net.reactivated.Fprint', Gio.BusNameWatcherFlags.NONE,
on_name_appeared, None)
on_name_appeared, on_name_vanished)
self.addCleanup(Gio.bus_unwatch_name, id)
self.daemon_start()
while not self._appeared_res:
while not self._appeared_name:
ctx.iteration(True)
self.assertIsNotNone(self._appeared_res[0])
dev_path = self._appeared_res[0][0]
self.assertTrue(dev_path.startswith('/net/reactivated/Fprint/Device/'))
self.assertEqual(self._appeared_name, 'net.reactivated.Fprint')
Gio.bus_unwatch_name(id)
try:
appeared_device = self.dbus.call_sync(
'net.reactivated.Fprint',
'/net/reactivated/Fprint/Manager',
'net.reactivated.Fprint.Manager',
'GetDefaultDevice', None, None,
Gio.DBusCallFlags.NO_AUTO_START, 500, None)
except GLib.GError as e:
if 'net.reactivated.Fprint.Error.NoSuchDevice' in e.message:
self.skipTest("Need virtual_image device to run the test")
raise(e)
self.assertIsNotNone(appeared_device)
[dev_path] = appeared_device
self.assertTrue(dev_path.startswith('/net/reactivated/Fprint/Device/'))
class FPrintdVirtualDeviceTest(FPrintdVirtualDeviceBaseTest):
def test_name_property(self):
self.assertEqual(self.device.get_cached_property('name').unpack(),
'Virtual image device for debugging')
def test_enroll_stages_property(self):
self.assertEqual(self.device.get_cached_property('num-enroll-stages').unpack(), 5)
def test_scan_type(self):
self.assertEqual(self.device.get_cached_property('scan-type').unpack(),
'swipe')
def test_allowed_claim_release_enroll(self):
self._polkitd_obj.SetAllowed(['net.reactivated.fprint.device.setusername',
'net.reactivated.fprint.device.enroll'])
@ -578,6 +603,34 @@ class FPrintdVirtualDeviceTest(FPrintdVirtualDeviceBaseTest):
with self.assertFprintError('PermissionDenied'):
self.device.Claim('(s)', 'testuser')
def test_unallowed_enroll_with_verify_claim(self):
self._polkitd_obj.SetAllowed(['net.reactivated.fprint.device.verify'])
self.device.Claim('(s)', '')
with self.assertFprintError('PermissionDenied'):
self.enroll_image('whorl', finger='right-thumb')
def test_unallowed_delete_with_verify_claim(self):
self._polkitd_obj.SetAllowed(['net.reactivated.fprint.device.verify'])
self.device.Claim('(s)', '')
with self.assertFprintError('PermissionDenied'):
self.device.DeleteEnrolledFingers('(s)', 'testuser')
def test_unallowed_delete2_with_verify_claim(self):
self._polkitd_obj.SetAllowed(['net.reactivated.fprint.device.verify'])
self.device.Claim('(s)', '')
with self.assertFprintError('PermissionDenied'):
self.device.DeleteEnrolledFingers2()
def test_unallowed_verify_with_enroll_claim(self):
self._polkitd_obj.SetAllowed(['net.reactivated.fprint.device.enroll'])
self.device.Claim('(s)', '')
with self.assertFprintError('PermissionDenied'):
self.device.VerifyStart('(s)', 'any')
def test_unallowed_claim_current_user(self):
self._polkitd_obj.SetAllowed([''])
@ -595,20 +648,11 @@ class FPrintdVirtualDeviceTest(FPrintdVirtualDeviceBaseTest):
self.device.Release()
def test_unallowed_release(self):
def test_always_allowed_release(self):
self.device.Claim('(s)', 'testuser')
self._polkitd_obj.SetAllowed([''])
with self.assertFprintError('PermissionDenied'):
self.device.Release()
self._polkitd_obj.SetAllowed(['net.reactivated.fprint.device.setusername'])
with self.assertFprintError('PermissionDenied'):
self.device.Release()
self._polkitd_obj.SetAllowed(['net.reactivated.fprint.device.enroll'])
self.device.Release()
def test_unclaimed_release(self):
@ -688,6 +732,9 @@ class FPrintdVirtualDeviceTest(FPrintdVirtualDeviceBaseTest):
time.sleep(1)
def test_removal_during_enroll(self):
if not self._has_hotplug:
self.skipTest("libfprint is too old for hotplug")
self._polkitd_obj.SetAllowed(['net.reactivated.fprint.device.setusername',
'net.reactivated.fprint.device.enroll'])
self.device.Claim('(s)', 'testuser')
@ -732,6 +779,11 @@ class FPrintdVirtualDeviceClaimedTest(FPrintdVirtualDeviceBaseTest):
self.device.VerifyStart('(s)', 'any')
def test_enroll_verify_list_delete(self):
# This test can trigger a race in older libfprint, only run if we have
# hotplug support, which coincides with the fixed release.
if not self._has_hotplug:
self.skipTest("libfprint is too old for hotplug")
with self.assertFprintError('NoEnrolledPrints'):
self.device.ListEnrolledFingers('(s)', 'testuser')
@ -770,6 +822,7 @@ class FPrintdVirtualDeviceClaimedTest(FPrintdVirtualDeviceBaseTest):
self.wait_for_result()
self.assertTrue(self._verify_stopped)
self.assertEqual(self._last_result, 'verify-match')
self.device.VerifyStop()
self.assertEqual(self.device.ListEnrolledFingers('(s)', 'testuser'), ['right-index-finger'])
@ -792,18 +845,29 @@ class FPrintdVirtualDeviceClaimedTest(FPrintdVirtualDeviceBaseTest):
self.assertFalse(os.path.exists(os.path.join(self.state_dir, 'testuser/virtual_image/0/7')))
def test_enroll_invalid_storage_dir(self):
if 'CI_PROJECT_NAME' in os.environ:
self.skipTest('Permissions aren\'t respected in CI environment')
# Directory wil not exist yet
os.makedirs(self.state_dir, mode=0o500)
self.addCleanup(os.chmod, self.state_dir, mode=0o700)
try:
os.open(os.path.join(self.state_dir, "testfile"), os.O_CREAT | os.O_WRONLY)
self.skipTest('Permissions aren\'t respected (CI environment?)')
except PermissionError:
pass
self.enroll_image('whorl', expected_result='enroll-failed')
def test_verify_invalid_storage_dir(self):
if 'CI_PROJECT_NAME' in os.environ:
self.skipTest('Permissions aren\'t respected in CI environment')
self.enroll_image('whorl')
os.chmod(self.state_dir, mode=0o000)
self.addCleanup(os.chmod, self.state_dir, mode=0o700)
try:
os.open(os.path.join(self.state_dir, "testfile"), os.O_CREAT | os.O_WRONLY)
self.skipTest('Permissions aren\'t respected (CI environment?)')
except PermissionError:
pass
with self.assertFprintError('NoEnrolledPrints'):
self.device.VerifyStart('(s)', 'any')
@ -938,15 +1002,11 @@ class FPrintdVirtualDeviceClaimedTest(FPrintdVirtualDeviceBaseTest):
self._polkitd_obj.SetAllowed(['net.reactivated.fprint.device.enroll'])
self.enroll_image('whorl')
def test_unallowed_enroll_stop(self):
def test_always_allowed_enroll_stop(self):
self.device.EnrollStart('(s)', 'right-index-finger')
self._polkitd_obj.SetAllowed([''])
with self.assertFprintError('PermissionDenied'):
self.device.EnrollStop()
self._polkitd_obj.SetAllowed(['net.reactivated.fprint.device.enroll'])
self.device.EnrollStop()
def test_unallowed_verify_start(self):
@ -955,15 +1015,11 @@ class FPrintdVirtualDeviceClaimedTest(FPrintdVirtualDeviceBaseTest):
with self.assertFprintError('PermissionDenied'):
self.device.VerifyStart('(s)', 'any')
def test_unallowed_verify_stop(self):
def test_always_allowed_verify_stop(self):
self.enroll_image('whorl')
self.device.VerifyStart('(s)', 'any')
self._polkitd_obj.SetAllowed([''])
with self.assertFprintError('PermissionDenied'):
self.device.VerifyStop()
self._polkitd_obj.SetAllowed(['net.reactivated.fprint.device.verify'])
self.device.VerifyStop()
def test_list_enrolled_fingers_current_user(self):
@ -1419,6 +1475,10 @@ class FPrintdUtilsTest(FPrintdVirtualDeviceBaseTest):
print('Testing JHBuild version')
jhbuild_prefix = os.environ['JHBUILD_PREFIX']
path = os.path.join(jhbuild_prefix, 'bin', util_bin)
else:
# Assume it is in path
utils[util] = util_bin
continue
assert os.path.exists(path), 'failed to find {} in {}'.format(util, path)
utils[util] = path
@ -1480,4 +1540,12 @@ if __name__ == '__main__':
print("%s %s" % (machine, human), end="\n")
sys.exit(0)
unittest.main(verbosity=2)
prog = unittest.main(verbosity=2, exit=False)
if prog.result.errors or prog.result.failures:
sys.exit(1)
# Translate to skip error
if prog.result.testsRun == len(prog.result.skipped):
sys.exit(77)
sys.exit(0)

148
tests/output_checker.py Normal file
View File

@ -0,0 +1,148 @@
#! /usr/bin/env python3
# Copyright © 2020, RedHat Inc.
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library. If not, see <http://www.gnu.org/licenses/>.
# Authors:
# Benjamin Berg <bberg@redhat.com>
import os
import sys
import fcntl
import io
import re
import time
import threading
class OutputChecker(object):
def __init__(self, out=sys.stdout):
self._output = out
self._pipe_fd_r, self._pipe_fd_w = os.pipe()
self._partial_buf = b''
self._lines_sem = threading.Semaphore()
self._lines = []
self._reader_io = io.StringIO()
# Just to be sure, shouldn't be a problem even if we didn't set it
fcntl.fcntl(self._pipe_fd_r, fcntl.F_SETFL,
fcntl.fcntl(self._pipe_fd_r, fcntl.F_GETFL) | os.O_CLOEXEC)
fcntl.fcntl(self._pipe_fd_w, fcntl.F_SETFL,
fcntl.fcntl(self._pipe_fd_w, fcntl.F_GETFL) | os.O_CLOEXEC)
# Start copier thread
self._thread = threading.Thread(target=self._copy)
self._thread.start()
def _copy(self):
while True:
r = os.read(self._pipe_fd_r, 1024)
if not r:
return
l = r.split(b'\n')
l[0] = self._partial_buf + l[0]
self._lines.extend(l[:-1])
self._partial_buf = l[-1]
self._lines_sem.release()
os.write(self._output.fileno(), r)
def check_line_re(self, needle_re, timeout=0, failmsg=None):
deadline = time.time() + timeout
if isinstance(needle_re, str):
needle_re = needle_re.encode('ascii')
r = re.compile(needle_re)
ret = []
while True:
try:
l = self._lines.pop(0)
except IndexError:
# Check if should wake up
if not self._lines_sem.acquire(timeout = deadline - time.time()):
if failmsg:
raise AssertionError(failmsg)
else:
raise AssertionError('Timed out waiting for needle %s (timeout: %0.2f)' % (str(needle_re), timeout))
continue
ret.append(l)
if r.search(l):
return ret
def check_line(self, needle, timeout=0, failmsg=None):
if isinstance(needle, str):
needle = needle.encode('ascii')
needle_re = re.escape(needle)
return self.check_line_re(needle_re, timeout=timeout, failmsg=failmsg)
def check_no_line_re(self, needle_re, wait=0, failmsg=None):
deadline = time.time() + wait
if isinstance(needle_re, str):
needle_re = needle_re.encode('ascii')
r = re.compile(needle_re)
ret = []
while True:
try:
l = self._lines.pop(0)
except IndexError:
# Check if should wake up
if not self._lines_sem.acquire(timeout = deadline - time.time()):
# Timed out, so everything is good
break
continue
ret.append(l)
if r.search(l):
if failmsg:
raise AssertionError(failmsg)
else:
raise AssertionError('Found needle %s but shouldn\'t have been there (timeout: %0.2f)' % (str(needle_re), timeout))
return ret
def check_no_line(self, needle, wait=0, failmsg=None):
if isinstance(needle, str):
needle = needle.encode('ascii')
needle_re = re.escape(needle)
return self.check_no_line_re(needle_re, wait=wait, failmsg=failmsg)
def clear(self):
ret = self._lines
self._lines = []
return ret
def assert_closed(self, timeout=1):
self._thread.join(timeout)
if self._thread.is_alive() != False:
raise AssertionError("OutputCheck: Write side has not been closed yet!")
@property
def fd(self):
return self._pipe_fd_w
def writer_attached(self):
os.close(self._pipe_fd_w)
self._pipe_fd_w = -1

View File

@ -39,6 +39,7 @@ foreach t: tests
{
'name': t,
'file': files(meson.current_source_dir() / t + '.py')[0],
'is_parallel': false,
'env': [
'TOPBUILDDIR=' + meson.build_root(),
'TOPSRCDIR=' + meson.source_root(),

View File

@ -17,8 +17,9 @@ import sys
import subprocess
import dbus
import dbusmock
import fcntl
import glob
import os
import shutil
import time
import pypamtest
@ -68,12 +69,12 @@ class TestPamFprintd(dbusmock.DBusTestCase):
def tearDownClass(klass):
klass.stop_monitor()
# Remove pam wrapper files, as they may break other tests
[shutil.rmtree(i) for i in glob.glob('/tmp/pam.[0-9A-z]')]
def setUp(self):
(self.p_mock, self.obj_fprintd_manager) = self.spawn_server_template(
self.template_name, {}, stdout=subprocess.PIPE)
# set log to nonblocking
flags = fcntl.fcntl(self.p_mock.stdout, fcntl.F_GETFL)
fcntl.fcntl(self.p_mock.stdout, fcntl.F_SETFL, flags | os.O_NONBLOCK)
self.template_name, {})
self.obj_fprintd_mock = dbus.Interface(self.obj_fprintd_manager, 'net.reactivated.Fprint.Manager.Mock')
def tearDown(self):
@ -85,6 +86,10 @@ class TestPamFprintd(dbusmock.DBusTestCase):
self.device_mock = self.dbus_con.get_object('net.reactivated.Fprint', device_path)
self.device_mock.SetEnrolledFingers('toto', ['left-little-finger', 'right-little-finger'])
def test_pam_no_device(self):
tc = pypamtest.TestCase(pypamtest.PAMTEST_AUTHENTICATE, expected_rv=PAM_AUTHINFO_UNAVAIL)
res = pypamtest.run_pamtest("toto", "fprintd-pam-test", [tc], [ 'unused' ])
def test_pam_fprintd_identify_error(self):
self.setup_device()
script = [
@ -138,6 +143,66 @@ class TestPamFprintd(dbusmock.DBusTestCase):
self.assertRegex(res.info[0], r'Swipe your left little finger across the fingerprint reader')
self.assertEqual(len(res.errors), 0)
def test_pam_fprintd_no_fingers(self):
self.setup_device()
self.device_mock.SetEnrolledFingers('toto', dbus.Array(set([]), signature='s'))
script = [
( 'verify-match', True, 1 )
]
self.device_mock.SetVerifyScript(script)
tc = pypamtest.TestCase(pypamtest.PAMTEST_AUTHENTICATE, expected_rv=PAM_AUTHINFO_UNAVAIL)
res = pypamtest.run_pamtest("toto", "fprintd-pam-test", [tc], [ 'unused' ])
def test_pam_fprintd_no_fingers_while_verifying(self):
self.setup_device()
script = [
( 'MOCK: no-prints', True, 1),
( 'verify-match', True, 1 )
]
self.device_mock.SetVerifyScript(script)
tc = pypamtest.TestCase(pypamtest.PAMTEST_AUTHENTICATE, expected_rv=PAM_USER_UNKNOWN)
res = pypamtest.run_pamtest("toto", "fprintd-pam-test", [tc], [ 'unused' ])
def test_pam_fprintd_blocks_unexpected_auth(self):
self.setup_device()
script = [
( 'verify-match', True, -500 ), # This one is sent before VerifyStart has completed
( 'verify-no-match', True, 1 ),
( 'verify-no-match', True, 1 ),
( 'verify-no-match', True, 1 ),
]
self.device_mock.SetVerifyScript(script)
tc = pypamtest.TestCase(pypamtest.PAMTEST_AUTHENTICATE, expected_rv=PAM_MAXTRIES)
res = pypamtest.run_pamtest("toto", "fprintd-pam-test", [tc], [ 'unused' ])
self.assertRegex(res.info[0], r'Swipe your left little finger across the fingerprint reader')
self.assertEqual(len(res.errors), 3)
self.assertRegex(res.errors[0], r'Failed to match fingerprint')
self.assertRegex(res.errors[0], r'Failed to match fingerprint')
self.assertRegex(res.errors[0], r'Failed to match fingerprint')
def test_pam_fprintd_blocks_unexpected_auth2(self):
self.setup_device()
script = [
( 'verify-no-match', True, 1 ),
( 'verify-match', True, -500 ), # This one is sent before VerifyStart has completed
( 'verify-no-match', True, 1 ),
( 'verify-no-match', True, 1 ),
]
self.device_mock.SetVerifyScript(script)
tc = pypamtest.TestCase(pypamtest.PAMTEST_AUTHENTICATE, expected_rv=PAM_MAXTRIES)
res = pypamtest.run_pamtest("toto", "fprintd-pam-test", [tc], [ 'unused' ])
self.assertRegex(res.info[0], r'Swipe your left little finger across the fingerprint reader')
self.assertEqual(len(res.errors), 3)
self.assertRegex(res.errors[0], r'Failed to match fingerprint')
self.assertRegex(res.errors[0], r'Failed to match fingerprint')
self.assertRegex(res.errors[0], r'Failed to match fingerprint')
def test_pam_fprintd_dual_reader_auth(self):
device_path = self.obj_fprintd_mock.AddDevice('FDO Sandpaper Reader', 3, 'press')
sandpaper_device_mock = self.dbus_con.get_object('net.reactivated.Fprint', device_path)
@ -191,6 +256,20 @@ class TestPamFprintd(dbusmock.DBusTestCase):
self.assertRegex(res.errors[1], r'Failed to match fingerprint')
self.assertRegex(res.errors[2], r'Failed to match fingerprint')
def test_pam_already_claimed(self):
self.setup_device()
script = [
( 'verify-match', True, 2 )
]
self.device_mock.SetVerifyScript(script)
self.device_mock.SetClaimed('toto')
tc = pypamtest.TestCase(pypamtest.PAMTEST_AUTHENTICATE, expected_rv=PAM_AUTHINFO_UNAVAIL)
res = pypamtest.run_pamtest("toto", "fprintd-pam-test", [tc], [ 'unused' ])
self.assertEqual(len(res.info), 0)
self.assertEqual(len(res.errors), 0)
def test_pam_timeout(self):
self.setup_device()
@ -198,6 +277,19 @@ class TestPamFprintd(dbusmock.DBusTestCase):
res = pypamtest.run_pamtest("toto", "fprintd-pam-test", [tc], [ 'unused' ])
self.assertRegex(res.info[1], r'Verification timed out')
def test_pam_notices_fprintd_disappearing(self):
self.setup_device()
script = [
( 'MOCK: quit', True, 0 ),
]
self.device_mock.SetVerifyScript(script)
tc = pypamtest.TestCase(pypamtest.PAMTEST_AUTHENTICATE, expected_rv=PAM_AUTHINFO_UNAVAIL)
res = pypamtest.run_pamtest("toto", "fprintd-pam-test", [tc], [ 'unused' ])
self.assertEqual(len(res.errors), 0)
self.assertEqual(len(res.info), 0)
if __name__ == '__main__':
if 'PAM_WRAPPER_SERVICE_DIR' not in os.environ:
print('Cannot run test without environment set correctly, run "meson test" instead')

View File

@ -18,9 +18,9 @@ import subprocess
import dbus
import dbus.mainloop.glib
import dbusmock
import fcntl
import os
import time
from output_checker import OutputChecker
VALID_FINGER_NAMES = [
@ -77,10 +77,8 @@ class TestFprintdUtilsBase(dbusmock.DBusTestCase):
def setUp(self):
super().setUp()
(self.p_mock, self.obj_fprintd_manager) = self.spawn_server_template(
self.template_name, {}, stdout=subprocess.PIPE)
self.template_name, {})
# set log to nonblocking
flags = fcntl.fcntl(self.p_mock.stdout, fcntl.F_GETFL)
fcntl.fcntl(self.p_mock.stdout, fcntl.F_SETFL, flags | os.O_NONBLOCK)
self.obj_fprintd_mock = dbus.Interface(self.obj_fprintd_manager, 'net.reactivated.Fprint.Manager.Mock')
def tearDown(self):
@ -102,22 +100,21 @@ class TestFprintdUtilsBase(dbusmock.DBusTestCase):
def start_utility_process(self, utility_name, args=[], sleep=True):
utility = [ os.path.join(self.tools_prefix, 'fprintd-{}'.format(utility_name)) ]
output = OutputChecker()
process = subprocess.Popen(self.wrapper_args + utility + args,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
universal_newlines=True)
flags = fcntl.fcntl(process.stdout, fcntl.F_GETFL)
fcntl.fcntl(process.stdout, fcntl.F_SETFL, flags | os.O_NONBLOCK)
stdout=output.fd,
stderr=subprocess.STDOUT)
output.writer_attached()
self.addCleanup(output.assert_closed)
self.addCleanup(self.try_stop_utility_process, process)
if sleep:
time.sleep(self.sleep_time)
return process
return process, output
def stop_utility_process(self, process):
print(process.stdout.read())
process.terminate()
process.wait()
@ -127,17 +124,12 @@ class TestFprintdUtilsBase(dbusmock.DBusTestCase):
except:
pass
def get_process_output(self, process):
out = process.stdout.read()
self.addCleanup(print, out)
return out
def run_utility_process(self, utility_name, args=[], sleep=True, timeout=None):
proc = self.start_utility_process(utility_name, args=args, sleep=sleep)
proc, output = self.start_utility_process(utility_name, args=args, sleep=sleep)
ret = proc.wait(timeout=timeout if timeout is not None else self.sleep_time * 4)
self.assertLessEqual(ret, 128)
return self.get_process_output(proc), ret
return b''.join(output.clear()), ret
class TestFprintdUtils(TestFprintdUtilsBase):
@ -146,65 +138,62 @@ class TestFprintdUtils(TestFprintdUtilsBase):
self.setup_device()
def test_fprintd_enroll(self):
process = self.start_utility_process('enroll', ['-f', 'right-index-finger', 'toto'])
process, out = self.start_utility_process('enroll', ['-f', 'right-index-finger', 'toto'])
out = self.get_process_output(process)
self.assertRegex(out, r'right-index-finger')
out.check_line(rb'right-index-finger', 0)
self.device_mock.EmitEnrollStatus('enroll-completed', True)
time.sleep(self.sleep_time)
out = self.get_process_output(process)
self.assertRegex(out, 'Enroll result: enroll-completed')
out.check_line(rb'Enroll result: enroll-completed', self.sleep_time)
def test_fprintd_list(self):
# Rick has no fingerprints enrolled
out, ret = self.run_utility_process('list', ['rick'])
self.assertRegex(out, r'has no fingers enrolled for')
self.assertRegex(out, rb'has no fingers enrolled for')
self.assertEqual(ret, 0)
# Toto does
out, ret = self.run_utility_process('list', ['toto'])
self.assertRegex(out, r'right-little-finger')
self.assertRegex(out, rb'right-little-finger')
self.assertEqual(ret, 0)
def test_fprintd_delete(self):
# Has fingerprints enrolled
out, ret = self.run_utility_process('list', ['toto'])
self.assertRegex(out, r'left-little-finger')
self.assertRegex(out, rb'left-little-finger')
self.assertEqual(ret, 0)
self.assertRegex(out, r'right-little-finger')
self.assertRegex(out, rb'right-little-finger')
# Delete fingerprints
out, ret = self.run_utility_process('delete', ['toto'])
self.assertRegex(out, r'Fingerprints deleted')
self.assertRegex(out, rb'Fingerprints deleted')
self.assertEqual(ret, 0)
# Doesn't have fingerprints
out, ret = self.run_utility_process('list', ['toto'])
self.assertRegex(out, r'has no fingers enrolled for')
self.assertRegex(out, rb'has no fingers enrolled for')
self.assertEqual(ret, 0)
class TestFprintdUtilsNoDeviceTests(TestFprintdUtilsBase):
def test_fprintd_enroll(self):
out, ret = self.run_utility_process('enroll', ['toto'])
self.assertIn('No devices available', out)
self.assertIn(b'No devices available', out)
self.assertEqual(ret, 1)
def test_fprintd_list(self):
out, ret = self.run_utility_process('list', ['toto'])
self.assertIn('No devices available', out)
self.assertIn(b'No devices available', out)
self.assertEqual(ret, 1)
def test_fprintd_delete(self):
out, ret = self.run_utility_process('delete', ['toto'])
self.assertIn('No devices available', out)
self.assertIn(b'No devices available', out)
self.assertEqual(ret, 1)
def test_fprintd_verify(self):
out, ret = self.run_utility_process('verify', ['toto'])
self.assertIn('No devices available', out)
self.assertIn(b'No devices available', out)
self.assertEqual(ret, 1)
@ -213,24 +202,20 @@ class TestFprintdUtilsVerify(TestFprintdUtilsBase):
super().setUp()
self.setup_device()
def start_verify_process(self, user='toto', finger=None, checkEnrolled=True):
def start_verify_process(self, user='toto', finger=None, nowait=False):
args = [user]
if finger:
args += ['-f', finger]
self.process = self.start_utility_process('verify', args)
out = self.get_process_output(self.process)
self.process, self.output = self.start_utility_process('verify', args)
if nowait:
return
self.assertNotRegex(out, r'Device already in use by [A-z]+')
self.assertNotIn('Verify result:', out)
preamble = self.output.check_line(b'Verify started!')
if checkEnrolled and finger:
self.assertNotIn('''Finger '{}' not enrolled for user {}'''.format(
finger, user), out)
out = b''.join(preamble)
if checkEnrolled:
for f in self.enrolled_fingers:
self.assertIn(f, out)
self.assertNotIn(b'Verify result:', out)
if finger:
expected_finger = finger
@ -239,9 +224,8 @@ class TestFprintdUtilsVerify(TestFprintdUtilsBase):
self.assertEqual(self.device_mock.GetSelectedFinger(), expected_finger)
def assertVerifyMatch(self, match):
self.assertIn('Verify result: {} (done)'.format(
'verify-match' if match else 'verify-no-match'),
self.get_process_output(self.process))
self.output.check_line(r'Verify result: {} (done)'.format(
'verify-match' if match else 'verify-no-match'))
def test_fprintd_verify(self):
self.start_verify_process()
@ -280,14 +264,16 @@ class TestFprintdUtilsVerify(TestFprintdUtilsBase):
def test_fprintd_verify_not_enrolled_fingers(self):
for finger in [f for f in VALID_FINGER_NAMES if f not in self.enrolled_fingers]:
self.start_verify_process(finger=finger, nowait=True)
regex = r'Finger \'{}\' not enrolled'.format(finger)
with self.assertRaisesRegex(AssertionError, regex):
self.start_verify_process(finger=finger)
self.output.check_line_re(regex, timeout=self.sleep_time)
self.device_mock.Release()
def test_fprintd_verify_no_enrolled_fingers(self):
self.set_enrolled_fingers([])
self.start_verify_process()
self.start_verify_process(nowait=True)
self.output.check_line(b'No fingers enrolled for this device.', timeout=self.sleep_time)
self.assertEqual(self.process.poll(), 1)
def test_fprintd_list_all_fingers(self):
@ -299,16 +285,17 @@ class TestFprintdUtilsVerify(TestFprintdUtilsBase):
( 'verify-match', True, 2 )
]
self.device_mock.SetVerifyScript(script)
time.sleep(2)
self.start_verify_process()
time.sleep(self.sleep_time * 4)
time.sleep(2 + self.sleep_time)
self.assertVerifyMatch(True)
def test_fprintd_multiple_verify_fails(self):
self.start_verify_process()
with self.assertRaisesRegex(AssertionError, r'Device already in use'):
self.start_verify_process()
self.start_verify_process(nowait=True)
self.output.check_line_re(rb'Device already in use by [A-z]+', timeout=self.sleep_time)
if __name__ == '__main__':
# avoid writing to stderr

View File

@ -26,12 +26,14 @@
static FprintDBusManager *manager = NULL;
static GDBusConnection *connection = NULL;
static void create_manager(void)
static void
create_manager (void)
{
g_autoptr(GError) error = NULL;
connection = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, &error);
if (connection == NULL) {
if (connection == NULL)
{
g_print ("Failed to connect to session bus: %s\n", error->message);
exit (1);
}
@ -41,55 +43,68 @@ static void create_manager(void)
"net.reactivated.Fprint",
"/net/reactivated/Fprint/Manager",
NULL, &error);
if (manager == NULL) {
if (manager == NULL)
{
g_print ("Failed to get Fprintd manager: %s\n", error->message);
exit (1);
}
}
static void delete_fingerprints (FprintDBusDevice *dev, const char *username)
static void
delete_fingerprints (FprintDBusDevice *dev, const char *username)
{
g_autoptr(GError) error = NULL;
if (!fprint_dbus_device_call_claim_sync (dev, username, NULL, &error)) {
if (!fprint_dbus_device_call_claim_sync (dev, username, NULL, &error))
{
g_print ("failed to claim device: %s\n", error->message);
exit (1);
}
if (!fprint_dbus_device_call_delete_enrolled_fingers2_sync (dev, NULL,
&error)) {
&error))
{
gboolean ignore_error = FALSE;
if (g_dbus_error_is_remote_error (error)) {
if (g_dbus_error_is_remote_error (error))
{
g_autofree char *dbus_error =
g_dbus_error_get_remote_error (error);
if (g_str_equal (dbus_error,
"net.reactivated.Fprint.Error.NoEnrolledPrints")) {
"net.reactivated.Fprint.Error.NoEnrolledPrints"))
{
g_print ("No fingerprints to delete on %s\n",
fprint_dbus_device_get_name (dev));
ignore_error = TRUE;
}
}
if (!ignore_error) {
if (!ignore_error)
{
g_print ("ListEnrolledFingers failed: %s\n",
error->message);
exit (1);
} else {
}
else
{
g_print ("No fingerprints to delete on %s\n",
fprint_dbus_device_get_name (dev));
}
} else {
}
else
{
g_print ("Fingerprints deleted on %s\n",
fprint_dbus_device_get_name (dev));
}
g_clear_error (&error);
if (!fprint_dbus_device_call_release_sync (dev, NULL, &error)) {
if (!fprint_dbus_device_call_release_sync (dev, NULL, &error))
{
g_print ("ReleaseDevice failed: %s\n", error->message);
exit (1);
}
}
static void process_devices(char **argv)
static void
process_devices (char **argv)
{
g_autoptr(GError) error = NULL;
g_auto(GStrv) devices = NULL;
@ -98,24 +113,28 @@ static void process_devices(char **argv)
guint i;
if (!fprint_dbus_manager_call_get_devices_sync (manager, &devices,
NULL, &error)) {
NULL, &error))
{
g_print ("Impossible to get devices: %s\n", error->message);
exit (1);
}
num_devices = g_strv_length (devices);
if (num_devices == 0) {
if (num_devices == 0)
{
g_print ("No devices available\n");
exit (1);
}
g_print ("found %u devices\n", num_devices);
for (i = 0; devices[i] != NULL; i++) {
for (i = 0; devices[i] != NULL; i++)
{
path = devices[i];
g_print ("Device at %s\n", path);
}
for (i = 0; devices[i] != NULL; i++) {
for (i = 0; devices[i] != NULL; i++)
{
g_autoptr(FprintDBusDevice) dev = NULL;
guint j;
@ -133,13 +152,15 @@ static void process_devices(char **argv)
}
}
int main(int argc, char **argv)
int
main (int argc, char **argv)
{
setlocale (LC_ALL, "");
create_manager ();
if (argc < 2) {
if (argc < 2)
{
g_print ("Usage: %s <username> [usernames...]\n", argv[0]);
return 1;
}
@ -148,4 +169,3 @@ int main(int argc, char **argv)
return 0;
}

View File

@ -33,12 +33,14 @@ static GDBusConnection *connection = NULL;
static char *finger_name = NULL;
static char **usernames = NULL;
static void create_manager(void)
static void
create_manager (void)
{
g_autoptr(GError) error = NULL;
connection = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, &error);
if (connection == NULL) {
if (connection == NULL)
{
g_print ("Failed to connect to session bus: %s\n", error->message);
exit (1);
}
@ -48,20 +50,23 @@ static void create_manager(void)
"net.reactivated.Fprint",
"/net/reactivated/Fprint/Manager",
NULL, &error);
if (manager == NULL) {
if (manager == NULL)
{
g_print ("Failed to get Fprintd manager: %s\n", error->message);
exit (1);
}
}
static FprintDBusDevice *open_device (const char *username)
static FprintDBusDevice *
open_device (const char *username)
{
g_autoptr(FprintDBusDevice) dev = NULL;
g_autoptr(GError) error = NULL;
g_autofree char *path = NULL;
if (!fprint_dbus_manager_call_get_default_device_sync (manager, &path,
NULL, &error)) {
NULL, &error))
{
g_print ("Impossible to enroll: %s\n", error->message);
exit (1);
}
@ -73,33 +78,39 @@ static FprintDBusDevice *open_device (const char *username)
"net.reactivated.Fprint",
path, NULL, &error);
if (error) {
if (error)
{
g_print ("failed to connect to device: %s\n", error->message);
exit (1);
}
if (!fprint_dbus_device_call_claim_sync (dev, username, NULL, &error)) {
if (!fprint_dbus_device_call_claim_sync (dev, username, NULL, &error))
{
g_print ("failed to claim device: %s\n", error->message);
exit (1);
}
return g_steal_pointer (&dev);
}
static void enroll_result(GObject *object, const char *result, gboolean done, void *user_data)
static void
enroll_result (GObject *object, const char *result, gboolean done, void *user_data)
{
gboolean *enroll_completed = user_data;
g_print ("Enroll result: %s\n", result);
if (done != FALSE)
*enroll_completed = TRUE;
}
static void proxy_signal_cb (GDBusProxy *proxy,
static void
proxy_signal_cb (GDBusProxy *proxy,
const gchar *sender_name,
const gchar *signal_name,
GVariant *parameters,
gpointer user_data)
{
if (g_str_equal (signal_name, "EnrollStatus")) {
if (g_str_equal (signal_name, "EnrollStatus"))
{
const gchar *result;
gboolean done;
@ -108,7 +119,8 @@ static void proxy_signal_cb (GDBusProxy *proxy,
}
}
static void do_enroll (FprintDBusDevice *dev)
static void
do_enroll (FprintDBusDevice *dev)
{
g_autoptr(GError) error = NULL;
gboolean enroll_completed = FALSE;
@ -119,18 +131,22 @@ static void do_enroll (FprintDBusDevice *dev)
&enroll_completed);
found = FALSE;
for (i = 0; fingers[i].dbus_name != NULL; i++) {
if (g_strcmp0 (fingers[i].dbus_name, finger_name) == 0) {
for (i = 0; fingers[i].dbus_name != NULL; i++)
{
if (g_strcmp0 (fingers[i].dbus_name, finger_name) == 0)
{
found = TRUE;
break;
}
}
if (!found) {
if (!found)
{
g_autoptr(GString) s = NULL;
s = g_string_new (NULL);
g_string_append_printf (s, "Invalid finger name '%s'. Name must be one of ", finger_name);
for (i = 0; fingers[i].dbus_name != NULL; i++) {
for (i = 0; fingers[i].dbus_name != NULL; i++)
{
g_string_append_printf (s, "%s", fingers[i].dbus_name);
if (fingers[i + 1].dbus_name != NULL)
g_string_append (s, ", ");
@ -141,7 +157,8 @@ static void do_enroll (FprintDBusDevice *dev)
g_print ("Enrolling %s finger.\n", finger_name);
if (!fprint_dbus_device_call_enroll_start_sync (dev, finger_name, NULL,
&error)) {
&error))
{
g_print ("EnrollStart failed: %s\n", error->message);
exit (1);
}
@ -151,16 +168,19 @@ static void do_enroll (FprintDBusDevice *dev)
g_signal_handlers_disconnect_by_func (dev, proxy_signal_cb, &enroll_result);
if (!fprint_dbus_device_call_enroll_stop_sync (dev, NULL, &error)) {
if (!fprint_dbus_device_call_enroll_stop_sync (dev, NULL, &error))
{
g_print ("VerifyStop failed: %s\n", error->message);
exit (1);
}
}
static void release_device (FprintDBusDevice *dev)
static void
release_device (FprintDBusDevice *dev)
{
g_autoptr(GError) error = NULL;
if (!fprint_dbus_device_call_release_sync (dev, NULL, &error)) {
if (!fprint_dbus_device_call_release_sync (dev, NULL, &error))
{
g_print ("ReleaseDevice failed: %s\n", error->message);
exit (1);
}
@ -172,10 +192,12 @@ static const GOptionEntry entries[] = {
{ NULL }
};
int main(int argc, char **argv)
int
main (int argc, char **argv)
{
g_autoptr(FprintDBusDevice) dev = NULL;
GOptionContext *context;
g_autoptr(GError) err = NULL;
setlocale (LC_ALL, "");
@ -183,7 +205,8 @@ int main(int argc, char **argv)
context = g_option_context_new ("Enroll a fingerprint");
g_option_context_add_main_entries (context, entries, NULL);
if (g_option_context_parse (context, &argc, &argv, &err) == FALSE) {
if (g_option_context_parse (context, &argc, &argv, &err) == FALSE)
{
g_print ("couldn't parse command-line options: %s\n", err->message);
return 1;
}
@ -200,4 +223,3 @@ int main(int argc, char **argv)
g_strfreev (usernames);
return 0;
}

View File

@ -26,12 +26,14 @@
static FprintDBusManager *manager = NULL;
static GDBusConnection *connection = NULL;
static void create_manager(void)
static void
create_manager (void)
{
g_autoptr(GError) error = NULL;
connection = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, &error);
if (connection == NULL) {
if (connection == NULL)
{
g_print ("Failed to connect to session bus: %s\n", error->message);
exit (1);
}
@ -41,13 +43,15 @@ static void create_manager(void)
"net.reactivated.Fprint",
"/net/reactivated/Fprint/Manager",
NULL, &error);
if (manager == NULL) {
if (manager == NULL)
{
g_print ("Failed to get Fprintd manager: %s\n", error->message);
exit (1);
}
}
static void list_fingerprints (FprintDBusDevice *dev, const char *username)
static void
list_fingerprints (FprintDBusDevice *dev, const char *username)
{
g_autoptr(GError) error = NULL;
g_auto(GStrv) fingers = NULL;
@ -55,24 +59,27 @@ static void list_fingerprints (FprintDBusDevice *dev, const char *username)
if (!fprint_dbus_device_call_list_enrolled_fingers_sync (dev, username,
&fingers, NULL,
&error)) {
&error))
{
gboolean ignore_error = FALSE;
if (g_dbus_error_is_remote_error (error)) {
if (g_dbus_error_is_remote_error (error))
{
g_autofree char *dbus_error =
g_dbus_error_get_remote_error (error);
if (g_str_equal (dbus_error,
"net.reactivated.Fprint.Error.NoEnrolledPrints")) {
"net.reactivated.Fprint.Error.NoEnrolledPrints"))
ignore_error = TRUE;
}
}
if (!ignore_error) {
if (!ignore_error)
{
g_print ("ListEnrolledFingers failed: %s\n", error->message);
exit (1);
}
}
if (fingers == NULL || g_strv_length (fingers) == 0) {
if (fingers == NULL || g_strv_length (fingers) == 0)
{
g_print ("User %s has no fingers enrolled for %s.\n", username,
fprint_dbus_device_get_name (dev));
return;
@ -83,12 +90,12 @@ static void list_fingerprints (FprintDBusDevice *dev, const char *username)
fprint_dbus_device_get_name (dev),
fprint_dbus_device_get_scan_type (dev));
for (i = 0; fingers[i] != NULL; i++) {
for (i = 0; fingers[i] != NULL; i++)
g_print (" - #%d: %s\n", i, fingers[i]);
}
}
static void process_devices(char **argv)
static void
process_devices (char **argv)
{
g_auto(GStrv) devices = NULL;
g_autoptr(GError) error = NULL;
@ -97,24 +104,28 @@ static void process_devices(char **argv)
guint i;
if (!fprint_dbus_manager_call_get_devices_sync (manager, &devices, NULL,
&error)) {
&error))
{
g_print ("Impossible to get devices: %s\n", error->message);
exit (1);
}
num_devices = g_strv_length (devices);
if (num_devices == 0) {
if (num_devices == 0)
{
g_print ("No devices available\n");
exit (1);
}
g_print ("found %u devices\n", num_devices);
for (i = 0; devices[i] != NULL; i++) {
for (i = 0; devices[i] != NULL; i++)
{
path = devices[i];
g_print ("Device at %s\n", path);
}
for (i = 0; devices[i] != NULL; i++) {
for (i = 0; devices[i] != NULL; i++)
{
g_autoptr(FprintDBusDevice) dev = NULL;
guint j;
@ -132,13 +143,15 @@ static void process_devices(char **argv)
}
}
int main(int argc, char **argv)
int
main (int argc, char **argv)
{
setlocale (LC_ALL, "");
create_manager ();
if (argc < 2) {
if (argc < 2)
{
g_print ("Usage: %s <username> [usernames...]\n", argv[0]);
return 1;
}
@ -147,4 +160,3 @@ int main(int argc, char **argv)
return 0;
}

View File

@ -31,12 +31,14 @@ static char *finger_name = NULL;
static gboolean g_fatal_warnings = FALSE;
static char **usernames = NULL;
static void create_manager(void)
static void
create_manager (void)
{
g_autoptr(GError) error = NULL;
connection = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, &error);
if (connection == NULL) {
if (connection == NULL)
{
g_print ("Failed to connect to session bus: %s\n", error->message);
exit (1);
}
@ -46,20 +48,23 @@ static void create_manager(void)
"net.reactivated.Fprint",
"/net/reactivated/Fprint/Manager",
NULL, &error);
if (manager == NULL) {
if (manager == NULL)
{
g_print ("Failed to get Fprintd manager: %s\n", error->message);
exit (1);
}
}
static FprintDBusDevice *open_device (const char *username)
static FprintDBusDevice *
open_device (const char *username)
{
g_autoptr(FprintDBusDevice) dev = NULL;
g_autoptr(GError) error = NULL;
g_autofree char *path = NULL;
if (!fprint_dbus_manager_call_get_default_device_sync (manager, &path,
NULL, &error)) {
NULL, &error))
{
g_print ("Impossible to verify: %s\n", error->message);
exit (1);
}
@ -71,12 +76,14 @@ static FprintDBusDevice *open_device (const char *username)
"net.reactivated.Fprint",
path, NULL, &error);
if (error) {
if (error)
{
g_print ("failed to connect to device: %s\n", error->message);
exit (1);
}
if (!fprint_dbus_device_call_claim_sync (dev, username, NULL, &error)) {
if (!fprint_dbus_device_call_claim_sync (dev, username, NULL, &error))
{
g_print ("failed to claim device: %s\n", error->message);
exit (1);
}
@ -84,7 +91,8 @@ static FprintDBusDevice *open_device (const char *username)
return g_steal_pointer (&dev);
}
static void find_finger (FprintDBusDevice *dev, const char *username)
static void
find_finger (FprintDBusDevice *dev, const char *username)
{
g_autoptr(GError) error = NULL;
g_auto(GStrv) fingers = NULL;
@ -92,60 +100,94 @@ static void find_finger (FprintDBusDevice *dev, const char *username)
if (!fprint_dbus_device_call_list_enrolled_fingers_sync (dev, username,
&fingers,
NULL, &error)) {
NULL, &error))
{
g_print ("ListEnrolledFingers failed: %s\n", error->message);
exit (1);
}
if (fingers == NULL || g_strv_length (fingers) == 0) {
if (fingers == NULL || g_strv_length (fingers) == 0)
{
g_print ("No fingers enrolled for this device.\n");
exit (1);
}
g_print ("Listing enrolled fingers:\n");
for (i = 0; fingers[i] != NULL; i++) {
for (i = 0; fingers[i] != NULL; i++)
g_print (" - #%d: %s\n", i, fingers[i]);
}
if (finger_name && !g_str_equal (finger_name, "any") &&
!g_strv_contains ((const char **) fingers, finger_name)) {
!g_strv_contains ((const char **) fingers, finger_name))
{
g_print ("Finger '%s' not enrolled for user %s.\n", finger_name,
username);
g_free (finger_name);
exit (1);
}
if (finger_name == NULL) {
if (finger_name == NULL)
finger_name = g_strdup (fingers[0]);
}
}
static void verify_result(GObject *object, const char *result, gboolean done, void *user_data)
struct VerifyState
{
gboolean *verify_completed = user_data;
GError *error;
gboolean started;
gboolean completed;
};
static void
verify_result (GObject *object, const char *result, gboolean done, void *user_data)
{
struct VerifyState *verify_state = user_data;
g_print ("Verify result: %s (%s)\n", result, done ? "done" : "not done");
if (done != FALSE)
*verify_completed = TRUE;
verify_state->completed = TRUE;
}
static void verify_finger_selected(GObject *object, const char *name, void *user_data)
static void
verify_finger_selected (GObject *object, const char *name, void *user_data)
{
g_print ("Verifying: %s\n", name);
}
static void proxy_signal_cb (GDBusProxy *proxy,
static void
verify_started_cb (GObject *obj,
GAsyncResult *res,
gpointer user_data)
{
struct VerifyState *verify_state = user_data;
if (fprint_dbus_device_call_verify_start_finish (FPRINT_DBUS_DEVICE (obj), res, &verify_state->error))
{
g_print ("Verify started!\n");
verify_state->started = TRUE;
}
}
static void
proxy_signal_cb (GDBusProxy *proxy,
const gchar *sender_name,
const gchar *signal_name,
GVariant *parameters,
gpointer user_data)
{
if (g_str_equal (signal_name, "VerifyStatus")) {
struct VerifyState *verify_state = user_data;
if (!verify_state->started)
return;
if (g_str_equal (signal_name, "VerifyStatus"))
{
const gchar *result;
gboolean done;
g_variant_get (parameters, "(&sb)", &result, &done);
verify_result (G_OBJECT (proxy), result, done, user_data);
} else if (g_str_equal (signal_name, "VerifyFingerSelected")) {
}
else if (g_str_equal (signal_name, "VerifyFingerSelected"))
{
const gchar *name;
g_variant_get (parameters, "(&s)", &name);
@ -153,37 +195,62 @@ static void proxy_signal_cb (GDBusProxy *proxy,
}
}
static void do_verify (FprintDBusDevice *dev)
static void
do_verify (FprintDBusDevice *dev)
{
g_autoptr(GError) error = NULL;
gboolean verify_completed = FALSE;
struct VerifyState verify_state = { 0 };
/* This one is funny. We connect to the signal immediately to avoid
* race conditions. However, we must ignore any authentication results
* that happen before our start call returns.
* This is because the verify call itself may internally try to verify
* against fprintd (possibly using a separate account).
*
* To do so, we *must* use the async version of the verify call, as the
* sync version would cause the signals to be queued and only processed
* after it returns.
*/
g_signal_connect (dev, "g-signal", G_CALLBACK (proxy_signal_cb),
&verify_completed);
&verify_state);
if (!fprint_dbus_device_call_verify_start_sync (dev, finger_name, NULL,
&error)) {
g_print("VerifyStart failed: %s\n", error->message);
fprint_dbus_device_call_verify_start (dev, finger_name, NULL,
verify_started_cb,
&verify_state);
/* Wait for verify start while discarding any VerifyStatus signals */
while (!verify_state.started && !verify_state.error)
g_main_context_iteration (NULL, TRUE);
if (verify_state.error)
{
g_print ("VerifyStart failed: %s\n", verify_state.error->message);
g_clear_error (&verify_state.error);
exit (1);
}
while (!verify_completed)
/* VerifyStatus signals are processing, wait for completion. */
while (!verify_state.completed)
g_main_context_iteration (NULL, TRUE);
g_signal_handlers_disconnect_by_func (dev, proxy_signal_cb,
&verify_completed);
&verify_state);
if (!fprint_dbus_device_call_verify_stop_sync (dev, NULL, &error)) {
if (!fprint_dbus_device_call_verify_stop_sync (dev, NULL, &error))
{
g_print ("VerifyStop failed: %s\n", error->message);
exit (1);
}
}
static void release_device (FprintDBusDevice *dev)
static void
release_device (FprintDBusDevice *dev)
{
g_autoptr(GError) error = NULL;
if (!fprint_dbus_device_call_release_sync (dev, NULL, &error)) {
if (!fprint_dbus_device_call_release_sync (dev, NULL, &error))
{
g_print ("ReleaseDevice failed: %s\n", error->message);
exit (1);
}
@ -196,7 +263,8 @@ static const GOptionEntry entries[] = {
{ NULL }
};
int main(int argc, char **argv)
int
main (int argc, char **argv)
{
g_autoptr(FprintDBusDevice) dev = NULL;
g_autoptr(GError) err = NULL;
@ -208,18 +276,19 @@ int main(int argc, char **argv)
context = g_option_context_new ("Verify a fingerprint");
g_option_context_add_main_entries (context, entries, NULL);
if (g_option_context_parse (context, &argc, &argv, &err) == FALSE) {
if (g_option_context_parse (context, &argc, &argv, &err) == FALSE)
{
g_print ("couldn't parse command-line options: %s\n", err->message);
return 1;
}
if (usernames == NULL) {
if (usernames == NULL)
username = "";
} else {
else
username = usernames[0];
}
if (g_fatal_warnings) {
if (g_fatal_warnings)
{
GLogLevelFlags fatal_mask;
fatal_mask = g_log_set_always_fatal (G_LOG_FATAL_MASK);
@ -235,4 +304,3 @@ int main(int argc, char **argv)
release_device (dev);
return 0;
}