157 Commits

Author SHA1 Message Date
88a627f187 Release 1.90.4 2020-11-27 14:26:12 +01:00
a8818f9bfb device: Do not disconnect g-authorize-method handler
Disconnecting the g-authorize-method handler is not really needed, as it
is a signal from the same object. This basically reverts 6eb9f263fd
(device: Disconnect authorization callback and remove clients) but keeps
the code to clear known clients in the dispose handler.

Closes: #91
2020-11-27 14:23:41 +01:00
c5a3089f89 device: Remove some debug spew
Closes: #89
2020-11-25 19:09:02 +01:00
5de1261df6 device: Re-fetch session data after synchronous operation
As the mainloop is iterated, the session-data may be modified while the
client vanished handler is running. Re-fetch the session data to avoid
seeing an old state and closing the device a second time.

See: https://gitlab.freedesktop.org/libfprint/fprintd/-/issues/87#note_706357
2020-11-25 19:09:02 +01:00
e5c82d7b96 device: Throw a critical warning if the device was not cleaned up
On finalization, the device should always be cleaned up properly (no
data associated with an action may be left). Show a critical warning if
this is not the case, as it indicates a programming error.
2020-11-25 19:09:02 +01:00
9c842e2c2f manager: Fix leak of FprintDevice reference
When adding a device a reference was leaked.
2020-11-25 19:09:02 +01:00
ec5cce083c device: Fix leaks on device finalization 2020-11-25 19:09:02 +01:00
ab8118bde2 tests: Add device removal test 2020-11-25 19:09:00 +01:00
08fb209aed manager: Fix unexport of removed devices
The correct way to unexport the object again is to unexported it on the
manager rather than on the interface skeleton. This fixes notifications
about device removal on DBus.
2020-11-25 19:08:20 +01:00
6eb9f263fd device: Disconnect authorization callback and remove clients
Add a dispose function to disconnect the authorization callback and
remove all clients (i.e. unwatch their bus names) before destroying the
hash table.
2020-11-25 19:08:20 +01:00
74d05e7996 device: Destroy auth object in finalize 2020-11-25 19:08:20 +01:00
83cd09ba2f device: Unwatch names when removing them
If a device is unplugged/destroyed while a client is using it, then we
would still end up watching the name. The vanish notification will then
access the destroyed FprintDevice object.

Fix this by unwatching the bus name when removing the client entry from
the dictionary.
2020-11-25 19:08:20 +01:00
8ed77829a7 tests: Add test for a client vanishing during claim
Note that this test only works if the virtual_image driver opens up a
window for race condition by delaying the open callback.
2020-11-25 19:05:47 +01:00
b63c76319f device: Make possible client vanished race testable using critical
The tests cannot currently parse the logs of fprintd. This means we need
to rely on fprintd aborting when a condition is hit that needs to be
tested.

This makes certain possible races when clients vanish testable.
2020-11-25 19:05:46 +01:00
fd9a86eca4 tests: Fail test if return code is less than 0
This means that the application received a signal.
2020-11-25 19:04:44 +01:00
38ba7199b7 utils: Use auto-pointers 2020-11-13 13:32:51 +01:00
827baff301 enroll: Cleanup error with autoptr 2020-11-13 13:32:51 +01:00
6a5d46c8b0 file_storage: Use autopointers 2020-11-13 13:32:51 +01:00
1ae0f4926d device: Always use auto-pointers
Remove manual memory management when possible, using new GLib facilities

In a few cases, avoid creating a temporary GError instead.
2020-11-13 13:32:31 +01:00
e1c2698807 main: Use more auto-pointers 2020-11-11 11:40:51 +01:00
b14bfd8226 device: Implement lock-free session getting scheme
Add a scheme that allows getting and referencing the current session
data while also adding a reference at the same time. This allows getting
the session and using the constant attributes from outside the main
thread without worrying about it being destroyed.

Implement the getter/setter in a safe way by marking the pointer as
invalid while we get the reference.
2020-11-11 11:35:13 +01:00
1e2f360ade device: Fix possible race condition when checking claimed state
We already check the claimed state in advance during authorization. This
makes sense, as we can avoid authorization if the API has been used
incorrectly. However, as the mainloop is running and handling other
request the claimed state might change at any point until the method
handler is actually running.

As such, check the claimed state again in each method. Doing so fixes
the possible race condition.
2020-11-10 14:45:59 +01:00
778a8540aa device.policy: Use auth-self-keep for enrollment
When user is requested for enrolling, we should ask for password as
anyone who has physical access to the machine could otherwise enroll
its own fingers, and have access to it.

Fixes #5
2020-11-10 14:45:59 +01:00
4e707f0d31 manager: Use GEnum to register DBus errors from FprintError
Given that mk_genenum already parses FprintError, add the nick metadata
to the errors so that it matches the wanted DBus error and automatically
generate the errors list.

In this way we'll have to only touch one definition to get everything
updated
2020-11-10 14:45:59 +01:00
4c78012103 device: Use meson to generate fprint device permission flags 2020-11-10 14:45:59 +01:00
e59f3cbc4f device: Use GFlags to handle permission types easily as flags
And thanks to the GEnumValues we can easily get their nick string from
them that we can pass to Polkit
2020-11-10 14:45:59 +01:00
1a860aa882 tests/fprintd: Add tests ensuring that concurrent calls to fprintd work
Simulate the case in which multiple users are trying to access a device
at the same time, verifying that the access is granted only to the one
that first completes the authorization phase and that no other client is
then allowed.
2020-11-10 14:45:59 +01:00
a183b779ec tests/fprintd: Make easier to call device methods asynchronously 2020-11-10 14:45:59 +01:00
9d6c7eb1a9 dbusmock/polkitd: Add ability to simulate call hangs or delays
Added various methods that allow to make methods to delay to return a
value, both by using timing functions and using a way to manually
stop and restart the calls.

This is mostly done using async callbacks in dbus methods
2020-11-10 14:45:59 +01:00
110c0018a2 tests/fprintd: Make possible to call gdbus client as an async process
As per this refactor the sync call we already had so that reuses the
same code.
2020-11-10 14:45:59 +01:00
5611317c72 tests: Add standalone PolkitD implementation
We need to be able to hack this to be an async daemon to perform some
multi-thread tests, so replacing default implementation with a simple
one that for now just does same of default
2020-11-10 14:45:59 +01:00
0904c8a527 tests/fprintd: Ensure we can claim and release with only the verify permission 2020-11-10 14:45:59 +01:00
a681996d1d device: Check Polkit actions asynchronously using skeleton authorize method
GDBus generated interface skeletons support natively an authorization
method that allows to filter calls before we get into the method
callback and that gets called into the call thread, before we go back
to main thread.

As per this, we can move all the polkit and other authorization checks
into this callback so that method handlers are now just assuming they're
the right to perform the requested operation.

As per the fact we'll share some data between another thread and the
callbacks, we will need to introduce some locking mechanism to ensure
safe data access.

This might be reduced by moving the claiming checks back to the method,
but would lead errors to be handled in different ordering, and so the
user to be requested for a password, and then - in case fail.
This can still happen now, but only if there are concurrent requests.
2020-11-10 14:45:59 +01:00
4e7cf47a3d device: Get sender from invocation instead that during username check
We now can get an invocation-owned sender at any moment with GDBus, so
there's no point of getting it as optional return-out value from the
username check function.
2020-11-10 14:45:59 +01:00
9d3f3fcaca device: Use some #define's for polkit supported actions
Avoid repeating the same strings everywhere that might just lead to
errors, use define's instead.
2020-11-10 14:45:59 +01:00
30474a6546 manager: Add GDBus object manager
This is not used right now in all its full possibilities, but will make
devices hotplug support easier to implement and handle at client-side
level.

As per this we can stop doing the manual tracking of the devices.
2020-11-10 14:45:59 +01:00
93bad82540 fprintd: Use GDBus codegen based implementation
Fprintd is dependent on the deprecated dbus-glib, also this doesn't provide
various features we can take advantage of, like the ones for async
authentication mechanism.

So, remove all the dbus-glib dependencies and simplify the code, but without
any further refactor, and keeping everything as it used to work, while this
will give room for further improvements in subsequent commits.

Internally, we just use dbus-codegen to generate the skeletons, and we
use the generated FprintdDBusManager with composition, while we
implement the device skeleton interface in FprintDevice, so that we
don't have to use it as a proxy, and keep being closer to what it used
to be with dbus-glib.

Fixes: #61
2020-11-10 14:45:59 +01:00
e224913b80 Revert "data: Add additional fprintd lockdown"
The current lockdown rules prevent USB devices from being accessed and
cause threading to not work.
As such, revert them until it is clear on how/if we can apply these
measures. It is primarily not clear on how to prevent fork/clone as
fprintd does not need those.

This reverts commit 2fd86624e5.

See: #82
2020-11-10 12:27:38 +00:00
b2cae5cccf tests/fprintd: Check that identification with multiple images works 2020-11-06 11:06:25 +01:00
3419901f65 build: Don't add the utils tests under the daemon suite 2020-11-04 21:06:13 +01:00
c85ca09e35 tests/fprintd-utils: Ensure that we exit with error if we have no device 2020-11-04 21:06:13 +01:00
ecc02cb588 utils: Uniform the no-devices error messages removing duplicated checks
Use error messages to be consistent, and avoid checking for a returned
value when dbus-glib function to fetch it returned false, as it's
implicit that we had a failure.

Otherwise if didn't fail we are sure that we got the requested value.
2020-11-04 21:06:13 +01:00
091f373109 tests/fprintd-utils: Check the tools return values in some tests 2020-11-04 21:06:13 +01:00
f6eb3b3ea5 verify: Pass the "any" finger parameter to the daemon
fprintd supports "any" finger parameter for the VerifyStart call, and it's
up to the daemon to pick the first known if the device doesn't support
identification.

So remove the check to verify utility and add a test to verify this is
respected.
2020-11-04 21:06:13 +01:00
d7ca9e6095 tests/fprintd-utils: Verify happens on first finger if device has no identification
Ensure that this is true when using the utility
2020-11-04 21:06:13 +01:00
6797928884 dbusmock/fprintd: Add ability to add a device with Identification support
Devices which have identification support "any" finger and do not fallback
to a single-finger check.
2020-11-04 21:06:13 +01:00
d6c70be822 dbusmock/fprintd: Add ability to remove devices 2020-11-04 21:06:13 +01:00
d33a7c7e9d tests/fprintd-utils: Check that all fingers are listed 2020-11-04 21:06:13 +01:00
94d3a18dcd tests/fprintd-utils: Check that verify returns an error if no finger is set 2020-11-04 21:06:13 +01:00
5635383c96 tests/fprintd-utils: Check that fprintd-verify requests the expected finger 2020-11-04 21:06:13 +01:00
ac98b881be dbusmock/fprintd: Make possible to retrieve the finger selected for verification
So that we can ensure that the client requested the one we expect
2020-11-04 21:06:13 +01:00
629f7fcc11 dbusmock/fprintd: Reset the current action on release
So it happens in the real daemon
2020-11-04 21:06:13 +01:00
5a703baa20 verify: Fail if we try to verify a non-enrolled finger
Since we list the fingers available fail early in case it's not found
2020-11-04 21:06:10 +01:00
6641cb6da8 tests/fprintd-utils: Verify that we can match all the enrolled fingers 2020-11-04 21:04:21 +01:00
870b48637a tests/fprintd-utils: Verify errors if the device is already claimed 2020-11-04 21:04:21 +01:00
4b0cde81fd tests/fprintd-utils: Add utility function to stop the utility process
This avoids addCleanup ordering errors and also errors when we may try to
print an invalid stdout pipe (like when we have processed it all), as python
might fail with something like:

 ======================================================================
 ERROR: test_fprintd_multiple_verify_fails (__main__.TestFprintdUtilsVerify)
 ----------------------------------------------------------------------
 Traceback (most recent call last):
   File "~/GNOME/fprintd/tests/test_fprintd_utils.py", line 102, in <lambda>
     self.addCleanup(lambda: print(process.stdout.read()))
   File "/usr/lib/python3.8/codecs.py", line 321, in decode
     data = self.buffer + input
 TypeError: can't concat NoneType to bytes
2020-11-04 21:04:21 +01:00
59b3d2af8d tests/fprintd-utils: Call addCleanup actions in reverse order
unittest addCleanup calls are called in reverse order, so we need to reverse
the order of the calls as well, otherwise we won't correctly terminate the
subprocess children
2020-11-04 21:04:21 +01:00
93bcac946e tests/fprintd-utils: Move verification tests to a single class
We can factorize various checks, so let's simplify test code
2020-11-04 21:04:19 +01:00
a5063dc0e4 tests/fprintd-utils: Setup the device on setUp()
No need to repeat the action in every unit test, but move the tests to a
different class to easily allow adding another class with tests with no
such initialization
2020-11-04 20:20:29 +01:00
5fbc38c938 tests/fprintd-utils: Run super setUp/tearDown functions 2020-11-04 20:04:21 +01:00
c42e627ddd pam: Always return translated string from helper
This means that the different functions in the header match as all
functions will return the translted string instead of only one of them.
2020-10-13 09:29:44 +00:00
2fd86624e5 data: Add additional fprintd lockdown 2020-10-13 09:28:39 +00:00
6dc699ae6f tests: Fix test not failing on error
An assertion that is raised within a callback will not be swallowed by
the C code that called the function. To ensure that errors will be
noticable, pass the result back to the surrounding scope and check it
there.
2020-10-02 17:54:20 +02:00
e075d37590 tests: Check that a verify operation can be immediately restarted
This excercises the path where we early-report a result and the
VerifyStop call must wait for the operation to complete intenernally.

Note that this test cannot fail right now due to the FpImageDevice
internal code still trying to hide the deactivation delay internally.

See libfprint!174
2020-10-01 12:19:35 +00:00
18d6a86e9d device: Use early match for reporting verify status
libfprint 1.90.1 supports early match result report which allows to inform
the clients as soon as the driver detected a match/no-match without having
to wait the whole device driver deactivation.

Use this feature in fprintd, by emitting the "VerifyStatus" signal as soon
as we've an usable result we can inform the client about.

As per this, ignore any possible error we may get afterwards, due to device
issues or late user cancellation, as the point of the verification action
(i.e. getting a match/no-match) can be considered accomplished.

Fixes https://gitlab.freedesktop.org/libfprint/fprintd/issues/35
2020-10-01 12:19:35 +00:00
14051cac76 device: Fix race condition when clients vanish
If a client vanishes while we are opening or closing then fprintd would
uncoditionally try to close the device immediately. This should not be
done, it instead needs to wait for the open/close to complete.

Add open/close to the current action enum and keep track of the fact
that they are ongoing. Also check, whether the device has been closed in
the meantime (after the nested mainloop is done).

Fixes: #65
2020-10-01 10:39:05 +00:00
4e47222962 Revert "ci: Work-around meson coverage bug"
This reverts commit c07a63da99.
2020-08-18 12:07:59 +02:00
c07a63da99 ci: Work-around meson coverage bug
See https://github.com/mesonbuild/meson/issues/7437
2020-08-17 16:41:07 +02:00
812a3552a6 build: Fix custom_target meson warning
WARNING: custom_target 'utils_marshal' has more than one output! Using the first one.
2020-08-17 16:14:25 +02:00
ff06a301f0 build: Bump libfprint req 2020-08-17 15:21:27 +02:00
19353c971c build: Add some linefeeds to generated XML files 2020-08-17 15:21:27 +02:00
290e56023f tests: Test more branches in pam verify signal handler 2020-08-17 15:11:08 +02:00
efe92a7c33 pam: Increase severity to error for unknown verify results 2020-05-27 14:15:15 +02:00
a7cf0ae3b2 pam: Fix double free after verification error
The data->result was free'ed both in the loop (before breaking) and
afterwards. As the first case did not set the pointer to NULL, this
could result in a double free.

Fix this by simply removing the free that is in the loop and relying on
the cleanup later on.
2020-05-27 13:38:37 +02:00
ba60533f71 device: Remove unused disconnected flag
We were saving the state of a disconnected device because we used a
workaround to figure it out, but now libfprint would provide us a proper
GError in such case, and so we'd handle it without the need of saving the
state, given it's never used anyways.
2020-05-11 15:11:53 +00:00
fcd2d65490 tests: Add PAM test for hardware failure
This error is supposed to help replicate the problems encountered in:
https://gitlab.freedesktop.org/libfprint/fprintd/-/issues/59
2020-04-07 10:47:35 +02:00
6dd010f05c ci: Add a job running tests built with address sanitizer 2020-04-01 13:58:08 +00:00
714f499ab6 tests: Double the timeouts when testing with address sanitizer 2020-04-01 13:58:08 +00:00
d72c802415 tests/fprintd: Ensure that the daemon doesn't crash or abort
An application terminating because of a signal like SIGSEGV, SIGABRT and
friends, will exit with a signal number that is 128 + $SIGNAL_NUMBER, so
let's ensure that the daemon has not been terminated because of a such error

This makes even more sense with address sanitizer builds, as the daemon
would exit with abort.
2020-04-01 13:58:08 +00:00
184e1bd4d0 build: Support running tests with address sanitizer
Make possible to run tests with address sanitizer to quickly check for
memory errors, although we have to disable the error exit code in case of
leaks because we have some which are due to something else down in the stack
(and LSAN suppression files doesn't allow to define the stack to ignore
as we can in valgrind).

However, we'd abort in case of memory errors anyways, so this still helps
to prevent major problems, while still logging the leaks.

In order to run pam module tests with ASAN we need to manually pass the
library to LD_PRELOAD, as we do for the wrapper.
2020-04-01 13:58:08 +00:00
f401f399a8 pam: Get preloaded libraries paths using compiler
In order to run pam module tests we need to pass the libraries via
LD_PRELOAD, this supports a list of library paths, so use the compiler in
order to find their full paths (with soname) and check their presence.

In order to support linker scripts we need to introduce a workaround.
See meson issue https://github.com/mesonbuild/meson/issues/6880
2020-04-01 13:58:08 +00:00
3dd0018f23 build: Set default CFLAGS using meson's c_args
Meson supports checking for default arguments natively without having to
do this for each one, so just use this feature.

Not doing this will become a warning as per meson 0.52.0 [1].

[1] https://github.com/mesonbuild/meson/pull/5627
2020-03-31 14:26:38 +02:00
90298134a2 tests/fprintd: Add checks for delete enrolled fingers permissions
The test doesn't need any assertion because we're calling DeleteEnrolledFingers
and in case it fails a net.reactivated.Fprint.Error.PermissionDenied error
would be thrown, and thus an exception would be raised at python level, making
the test to fail.
2020-03-27 20:45:59 +01:00
8ff4360750 tests: Add test for STATE_DIRECTORY being multiple paths 2020-03-27 17:05:13 +01:00
fd733e55be file_storage: Handle STATE_DIRECTORY containing multiple paths
As per systemd's documentation:
If multiple directories are set, then in the environment variable the
paths are concatenated with colon (":").

Handle that case by splitting the paths if there's a ":" in the
variable.

Closes: #50
2020-03-27 17:05:06 +01:00
6a1fffae82 tests/fprintd: Fix claim_from_other_client_is_released_when_vanished on CI
test_claim_from_other_client_is_released_when_vanished would fail on
the CI but work on a local system because we wouldn't want long enough
for the "vanished" code path to be taken into account. Add a small
timeout to make sure it works on the CI as well.
2020-03-27 16:57:16 +01:00
47bd3f7fbb tests/fprintd: Fix test_enroll_invalid_storage_dir test
enroll_image() was always waiting for enroll-completed rather than for
what the caller expected as the result.

======================================================================
FAIL: test_enroll_invalid_storage_dir (__main__.FPrintdVirtualDeviceClaimedTest)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/home/hadess/Projects/jhbuild/fprintd/tests/fprintd.py", line 661, in test_enroll_invalid_storage_dir
    self.enroll_image('whorl', expected_result='enroll-failed')
  File "/home/hadess/Projects/jhbuild/fprintd/tests/fprintd.py", line 384, in enroll_image
    self.wait_for_result('enroll-completed')
  File "/home/hadess/Projects/jhbuild/fprintd/tests/fprintd.py", line 373, in wait_for_result
    self.assertEqual(self._last_result, expected)
AssertionError: 'enroll-failed' != 'enroll-completed'
- enroll-failed
+ enroll-completed
2020-03-27 16:26:28 +01:00
4f3589c0dc build: List single unit python tests allowing to run them separately
We run a certain number of tests right now, without being able to easily
run them separated or to check which one failed.

So add a script to inspect all the available unittests per each python
script and use it to figure out the tests we can run in meson.

As per this, define a global 'python_tests' variable in meson that allows
to register new python tests easily without having to repeat the settings
for all the tests.

For each test we have, we check if we can fetch a list of unit tests, and
if possible we create a meson test for each one.
Otherwise we just fallback to normal behavior.

This is something that can be hopefully implemented into upstream meson [1].

[1] https://github.com/mesonbuild/meson/issues/6851
2020-03-27 02:29:57 +01:00
eccd790df7 tests/fprintd: Add missing implementation of list_tests
Use unittest_inspector that we provide for meson tests inspection as well
2020-03-27 02:01:14 +01:00
bc29114a2c tests: Add a tool to inspect the single python unit tests
The unit tests files are provided as python files, adding a tool that
allows to list them in order to be able to run them easily via

  python3 -m unittest module.Class.test_method
2020-03-27 01:57:22 +01:00
cf95187268 ci: Update CI after the fdo template changes
The CI definition needs to be updated to work with the new fdo
templates.

Adapted from bd4f118b5e
2020-03-24 15:50:57 +01:00
23c37cd9b5 Revert "ci: Fix CI syntax error"
This reverts commit 01ea517a97.
2020-03-24 15:49:30 +01:00
967e4f24ed Revert "ci: Fix unknown keys in CI"
This reverts commit 10a3e75937.
2020-03-24 15:49:22 +01:00
d7fec03f24 tests/fprintd: Increase allowed timeout
On my laptop it takes 48 seconds to run.
2020-03-23 12:22:53 -04:00
4a3ae5ccaf tests/fprintd: Allow tests to run even when virtual device is not available
The test file calls self.daemon_start() in order to start fprintd and
locate the virtual image device that's needed for the test to run.
However, since the virtual image driver is not available on all
libfprint installations, the test should be skipped if the driver is not
available.

The skipping mechanism used to work, by checking if self.device is None.
This is based on the assumption that self.device would be set to None in
cases where the driver is not available. However, even in the past
self.device is only set to None in the tearDown method and not in setUp,
so presumably in edge cases it didn't entirely work.

However, since 0fb4f3b021 which
consistently removes the self.device attribute rather than setting it to
None, the "self.device is None" check no longer works. In particular,
the following error message is shown:

    test_manager_get_default_device (__main__.FPrintdManagerTests) ...
    Did not find virtual device! Probably libfprint was build without
    the corresponding driver!
    ERROR

After this patch, the following is shown:

    test_manager_get_default_device (__main__.FPrintdManagerTests) ...
    Did not find virtual device! Probably libfprint was build without
    the corresponding driver!
    skipped 'Need virtual_image device to run the test'

We fix this bug by consistently setting self.device to None, in both the
setUp method before daemon_start gets called, and in tearDown. We also
make the same change to self.manager for consistency.

The issue was not caught on CI, as the CI configuration always installs
a libfprint version that has the virtual_image device explicitly enabled
in the preparation phase.
2020-03-23 12:22:20 -04:00
e828ea3b2d main: Add missing locale.h include
The include is present in all other binaries in utils/.
2020-03-19 23:33:58 -04:00
d27872ff86 test_fprintd_utils: Don't use hard-coded sleep time
Make sleep time on verification dependent on the environment, so that it's
different when under valgrind
2020-03-17 17:14:35 +01:00
cfbded36e1 test_fprintd_utils: Use a non-blocking pipe to read output
Avoid using a temporary file for reading the utilities output, so we can
read it as it comes, ignoring the previous one, and avoiding open/closing
the file.

To keep the output printing on cleanup working, adding an utility function
that reads the output and save it for later printing
2020-03-17 17:14:35 +01:00
072fbc2b46 tests_fprintd_utils: Print the process output when done
This is particularly useful when using valgrind or address sanitizer to read
the output log
2020-03-17 17:14:35 +01:00
7846359b65 test_fprintd_utils: De-duplicate utilities launching
Avoid repeating the same operation to launch the utilities all the times,
but provide instead a function that allows to start a process and saves its
output without having to handle this in every test.

Simplify the operation when we just want the final output, still reusing
the same code.
2020-03-17 17:14:35 +01:00
08339a0648 tests/fprintd: Skip some tests in CI depending on permissions 2020-03-17 17:14:35 +01:00
3dd10b4b37 tests/fprind: Check that device is released when Caller operation is done
Add support to run fprintd-utils to test fprint daemon, and ensure that a
device is released and its operation cancelled once a caller goes away.

Related to https://gitlab.freedesktop.org/libfprint/fprintd/issues/37
2020-03-17 17:14:35 +01:00
bee2e154b1 tests/fprintd: Ensure that other clients can't interfere with claimer 2020-03-17 17:14:35 +01:00
27f0b64d03 tests/fprintd: Add tests for verification through finger id 2020-03-17 17:14:35 +01:00
f4ee2f86a3 device: Throw NoEnrolledPrints on missing finger in gallery
Throw a NoEnrolledPrints error if the requested finger isn't present
in the gallery.
2020-03-17 17:14:35 +01:00
04829ed39f tests/fprintd: Check that we can't mix Enroll and Verify operations 2020-03-17 17:14:35 +01:00
756a80a63e tests/fprintd: Ensure we throw NoActionInProgress on *Stop() 2020-03-17 17:14:35 +01:00
b861500a9f device: Throw AlreadyInUse error if stopping an enroll during verification
When starting an enroll when verification is in progress (and vice-versa) we
emit an AlreadyInUse error, however when calling VerifyStop() during an
enrollment (and vice-versa) we just return a NoActionInProgress error, which
is not the case.

So let's be consistent and change the error type.
2020-03-17 17:14:33 +01:00
154d0c0373 device: Use proper function name in debug 2020-03-17 17:10:17 +01:00
b2cdc1ed1e tests/fprint: Check current API user permissions 2020-03-17 17:10:17 +01:00
ab47e03f05 tests/fprintd: Ensure device open is correctly handled 2020-03-17 17:10:17 +01:00
f92801a15c tests/fprintd: Ensure that verify fails on storage read failure 2020-03-17 17:10:17 +01:00
086ceb98ab tests/fprintd: Ensure enroll fails on storage save failure 2020-03-17 17:10:17 +01:00
8bdbc7e2b0 tests/fprintd: Ensure that we can't verify without enrolled fingers 2020-03-17 17:10:17 +01:00
1f8bb1abd4 tests/fprintd: Ensure that devices are available on name appeared
Given we don't support adding devices after we created the manager (yet) we
must ensure that once the name appeared we have them all
2020-03-17 17:10:17 +01:00
47d55a97c4 tests/fprintd: Add tests for Manager methods 2020-03-17 17:10:17 +01:00
657b90a066 tests/fprintd: Move assertFprintError to FPrintdTest
This may be used by any class inheriting FPrintdTest, so let's move it at
lower level.
2020-03-17 17:10:17 +01:00
3821b96ca5 tests/fprintd: Only enable 'virtual_image' driver
When we run tests in a system with real devices, we may try to initialize
the real ones, while we can just ignore them all in tests.

We do it in setUp instead of setUpClass to allow tests to change this if
they need to, but just for temporary.
2020-03-17 17:10:17 +01:00
22ad9b5ae8 tests/fprintd: Check all the error types during verify/enroll
Ensure that we properly handle all the errors that a driver may return us
2020-03-17 17:10:17 +01:00
2ddc8a86a2 tests/fprind: Add verify retry tests for all the cases
The tests are repeated for both verify and identify actions
2020-03-17 17:10:17 +01:00
930cae4647 tests/fprind: Add enroll retry tests for all the cases 2020-03-17 17:10:17 +01:00
5e9624bef5 device: Fix verify-disconnected state name
Respect protocol, and use proper name
2020-03-17 17:10:17 +01:00
efac52d94f device: Fix retry enroll error names
Use proper names for enroll retry errors, fixing a copy/paste error from the
verify code.
2020-03-17 17:10:17 +01:00
af18aa35e5 tests/fprintd: Add tests to verify permissions on prints management 2020-03-17 17:10:16 +01:00
0d7a703200 tests/fprintd: Split claimed device tests to a single class
Avoid doing fo reach test the Claim/Release procedure manually if it is just
a prerequisite for the test.
2020-03-17 17:09:19 +01:00
ad19c49e2e tests/fprintd: Move tests which need a claimed device 2020-03-17 17:09:19 +01:00
a8de1003a4 tests/fprintd: Use addCleanup to always terminate daemons
Ensure that the daemons are always terminated after each test, even on
failure, so that we don't have to do hacks such as always trying to stop
them
2020-03-17 17:09:19 +01:00
cefe939141 tests/fprintd: Use addCleanup to ensure we remove the test dir 2020-03-17 17:09:19 +01:00
24cd986476 tests/fprintd: Use GTestDBus unset to undefine envs
Unset the dbus environment that may affect the dbus tests using GTestDBus
utility, instead of doing it manually.
2020-03-17 17:09:19 +01:00
06480c7994 tests/fprintd: Use a new bus for each test unit
When creating a new unit we used to get the system bus via Gio.bus_get_sync,
however this has a singleton implementation, and so would always return the
same connection, creating issues in tests when a new test suite is added
because the newly got connection would be already closed.

So, just manually create a new bus connection, also close the bus and
cleanup the test bus in dbus.
2020-03-17 17:09:19 +01:00
b2ad590891 tests/fprintd: Call the super class on setup 2020-03-17 17:09:19 +01:00
85aad7bb01 tests/fprintd: Remove usage of never-used self.client variable 2020-03-17 17:09:19 +01:00
0fb4f3b021 tests/fprintd: Cleanup the client proxies on tearDown
Ensure we nullify them when the test is done.
2020-03-17 17:09:19 +01:00
21564885ea tests/fprintd: Do the parent tearDown after we've done our stuff 2020-03-17 17:09:19 +01:00
6064e30200 tests/fprintd: Remove the force-exit timeout
Since we rely on meson now to do this, we don't need to have manual
management of the timeout
2020-03-17 17:09:19 +01:00
fd8297306c tests/fprintd: Ensure that we get an error on early release
Ensure that we get an error when releasing a device that is in process of
enrollment, verification or identification
2020-03-17 17:09:19 +01:00
e7f804e9fc device: Cancel the ongoing operation when releasing the device
If a device is currently verifying, identifying or enrolling we may want the
user to stop the operation before we actually release the device.

Otherwise we may end-up in trying to close (failing) the internal device,
while fprintd is still considering the device active, causing a dead-lock
(the device can't be released, but neither claimed again or stop the current
action).

In fact calling Claim() -> EnrollStart() -> Release(), we would fail with
the error

  net.reactivated.Fprint.Error.Internal:
  Release failed with error: The device is still busy with another
  operation, please try again later. (36)"

However, if we try to call VerifyStop, after this error, we'd fail because
for the fprintd logic, the device is not claimed anymore, but actually
closed, and we'd need to claim it again, but... That would still cause an
internal error.

To avoid this, in case Relase() is called cancel the ongoing operation,
and wait until it's done before completing the release call.
2020-03-17 17:09:19 +01:00
0e993d92e2 device: Return 'verify-no-match' on cancelled verification
We were returning a 'verify-unknown-error' while we actually know what
happened, so better to return a soft operation failure.
2020-03-17 17:09:19 +01:00
b312a5e540 device: Return 'enroll-failed' on cancelled enrollment
We were returning an 'enroll-unknown-error' while we actually know what
happened, so better to return a soft operation failure.
2020-03-17 17:09:19 +01:00
c12778ec5b tests/fprintd: Verify that each enroll stage happens
Instead of automatically replying with the 'whorl' image for every enroll
state signal with result 'enroll-stage-passed', only perform the number
of required enroll stages and ensure that we get the expected results.

This also will allow to manually perform enroll steps in other tests.
2020-03-17 17:09:19 +01:00
dbabd4d7d3 tests/fprintd: Deduplicate enrollment code 2020-03-17 15:54:14 +01:00
db1865eb3e tests/fprintd: Deduplicate result wait code 2020-03-17 15:49:40 +01:00
10a3e75937 ci: Fix unknown keys in CI
Fix:
root config contains unknown keys: container_fedora_build
2020-03-17 15:31:08 +01:00
01ea517a97 ci: Fix CI syntax error
Fix CI syntax error:
container_fedora_build: unknown keys in `extends` (.fedora@container-build)
Caused by changes in the wayland CI templates:
4a73f030d0
2020-03-17 15:29:37 +01:00
3a98ef646b ci: Re-enable stable branch
Now that libfprint v2 has landed in rawhide.
2020-02-19 13:07:27 +01:00
750a815fdf ci: Use extends to repeat libfprint builds
This syntax is just nicer and more maintainable than the YAML anchors
2020-02-18 14:08:32 +01:00
53fcf52989 ci: Factorize the similar parameters in build jobs 2020-02-18 14:06:32 +01:00
52e12459df main: Improve comments on fprint manager creation
Explain why the manager creation is async better in both in the main file
and in the manager itself
2020-02-14 16:01:01 +01:00
554df2a8d9 utils: Fix memory leak when error is ignored in list
If we get a `NoEnrolledPrints` error while list, we don't consider it an
hard error and in such case we proceed to releasing the device, but without
clearing the previously set error first.
2020-02-14 16:00:20 +01:00
681bd1ed2a device: Fix leaked matched print on identify
When starting an identify operation we allocate a gallery of prints from the
gallery, although if we match one of them we get that back in the finish
callback but with a further reference added.

So, in order to clean it up, use an auto-pointer or we'd end up in leaking
it, and the address sanitizer was catching this in our tests already:

  Indirect leak of 12020 byte(s) in 5 object(s) allocated from:
    #0 0x7fe8bc638ce6 in calloc (/usr/lib/x86_64-linux-gnu/libasan.so.5+0x10dce6)
    #1 0x7fe8bc37ffd0 in g_malloc0 ../../glib/glib/gmem.c:132
    #2 0x55d100635c01 in load_from_file ../src/file_storage.c:159
    #3 0x55d100635c01 in file_storage_print_data_load ../src/file_storage.c:182
    #4 0x55d10063e950 in fprint_device_verify_start ../src/device.c:882
    #5 0x55d10064036b in dbus_glib_marshal_fprint_device_VOID__STRING_POINTER src/device-dbus-glue.h:96
    #6 0x7fe8bc50f6f5  (/usr/lib/x86_64-linux-gnu/libdbus-glib-1.so.2+0xd6f5)
2020-02-14 15:55:09 +01:00
8890732194 device: Don't leak the user on claim error while deleting prints
When using the delete method we check if the device was claimed, if this
fails because the device is already in use we return an error, but we don't
free the user.

While this could be fixed by just a further g_free call, let's just remove
remove the other manual free calls, and use an auto-pointer instead for this
function.
2020-02-14 15:55:09 +01:00
7dac81dcad device: Use g_clear_error instead of doing the same manually
We've now an utility function that can help us to free and unset an error
double pointer, so let's use it.
2020-02-14 15:55:09 +01:00
1ecae1d014 delete: Clear the error in case we ignore it
If we get a `NoEnrolledPrints` error during delete, we don't consider it an
hard error and in such case we proceed to releasing the device, but without
clearing the previously set error first.
2020-02-14 15:55:09 +01:00
ba7a45d3f8 device: Always free error in delete enrolled fingers2
During delete enrolled fingers2 call, if the check-claimed control fails, we
would return the error without freeing it.

While this could be fixed by just a further g_error_free call, let's just
remove the other manual free call, and use an auto-pointer instead for this
function.
2020-02-14 15:55:09 +01:00
49dced5566 device: Always free error in delete enrolled fingers
During delete enrolled fingers call, if the check-claimed control fails, and
we get an error different from FPRINT_ERROR_CLAIM_DEVICE, we would return
the error without freeing it.

While this could be fixed by just a further g_error_free call, let's just
remove all the manual free calls, and use an auto-pointer instead for this
function.
2020-02-14 15:55:09 +01:00
e25544a8f0 manager: Remove unused path variable 2020-02-14 15:55:09 +01:00
ee8589ec9d main: Ensure we always free context, loop and error
In case of early return we may not free them consistently, while this is not
a big problem in a main function, is better to have a cleaner management,
and we did get valgrind reports.
2020-02-14 15:55:09 +01:00
30 changed files with 3114 additions and 1209 deletions

View File

@ -8,9 +8,9 @@ include:
variables:
extends: .libfprint_common_variables
FEDORA_TAG: rawhide
FEDORA_VERSION: rawhide
FEDORA_IMAGE: "$CI_REGISTRY/libfprint/$CI_PROJECT_NAME/fedora/$FEDORA_VERSION:$FEDORA_TAG"
FDO_DISTRIBUTION_TAG: latest
FDO_DISTRIBUTION_VERSION: rawhide
FEDORA_IMAGE: "$CI_REGISTRY/libfprint/$CI_PROJECT_NAME/fedora/$FDO_DISTRIBUTION_VERSION:$FDO_DISTRIBUTION_TAG"
DEPENDENCIES: dbus-glib-devel
gcc
gcovr
@ -18,6 +18,7 @@ variables:
git
glibc-devel
gtk-doc
libasan
libfprint-devel
meson
pam-devel
@ -28,7 +29,12 @@ variables:
image: "$FEDORA_IMAGE"
.install_libfprint_dev: &install_libfprint_dev
.fprintd_build_preconditions:
except:
variables:
- $FPRINT_CRON_TASK == "BUILD_CI_IMAGES"
.install_libfprint_dev:
before_script:
# Make sure we don't build or link against the system libfprint
- dnf remove -y libfprint-devel
@ -42,21 +48,16 @@ image: "$FEDORA_IMAGE"
- echo "libfprint/demo/gtk-libfprint-test.ui" >> po/POTFILES.skip
build_stable:
except:
variables:
- $FPRINT_CRON_TASK == "BUILD_CI_IMAGES"
# FIXME: Stable builds will fail until libfprintv 2 reaches rawhide
allow_failure: true
extends: .fprintd_build_preconditions
script:
- meson _build
- ninja -C _build -v
- ninja -C _build -v install
build_dev:
except:
variables:
- $FPRINT_CRON_TASK == "BUILD_CI_IMAGES"
<<: *install_libfprint_dev
extends:
- .fprintd_build_preconditions
- .install_libfprint_dev
script:
- meson _build --werror -Dgtk_doc=true
- ninja -C _build -v
@ -68,11 +69,10 @@ build_dev:
- _build/meson-logs/*.txt
test_dev:
except:
variables:
- $FPRINT_CRON_TASK == "BUILD_CI_IMAGES"
extends:
- .fprintd_build_preconditions
- .install_libfprint_dev
stage: test
<<: *install_libfprint_dev
script:
- meson _build -Db_coverage=true
- meson test -C _build --verbose --no-stdsplit --timeout-multiplier 3
@ -84,15 +84,29 @@ test_dev:
paths:
- _build/meson-logs
test_dev_with_sanitizer:
extends:
- .fprintd_build_preconditions
- .install_libfprint_dev
stage: test
script:
- meson _build -Db_sanitize=address
- meson test -C _build --verbose --no-stdsplit --timeout-multiplier 5
artifacts:
name: meson-logs
when: on_failure
paths:
- _build/meson-logs
# CONTAINERS creation stage
container_fedora_build:
extends: .fedora@container-build
extends: .fdo.container-build@fedora
only:
variables:
- $FPRINT_CRON_TASK == "BUILD_CI_IMAGES"
variables:
GIT_STRATEGY: none # no need to pull the whole tree for rebuilding the image
# a list of packages to install
FEDORA_RPMS:
FDO_DISTRIBUTION_PACKAGES:
$DEPENDENCIES
$LIBFPRINT_DEPENDENCIES

12
NEWS
View File

@ -1,6 +1,18 @@
This file lists notable changes in each release. For the full history of all
changes, see ChangeLog.
Version 1.90.4:
This fprintd release contains major core reworkings and improved testing.
As such, only the most important changes are listed here, focusing on
changes relevant to distributors.
Highlights:
- Authentication is now required to enroll a new print (#5)
- Add support for the libfprint early reporting mechanism
- Proper hotplug support together with libfprint 1.90.4
- Handle STATE_DIRECTORY containing multiple paths
version 1.90.1:
- Add support for prints saved on the fingerprint device itself
- Add integration tests using the virtual image driver, and further

View File

@ -25,7 +25,7 @@
<defaults>
<allow_any>no</allow_any>
<allow_inactive>no</allow_inactive>
<allow_active>yes</allow_active>
<allow_active>auth_self_keep</allow_active>
</defaults>
</action>

View File

@ -2,8 +2,8 @@ docbook_xml_header = custom_target('docbook_xml_header',
output: 'docbook-xml-header.xml',
command: [
'echo', '-n',
'<?xml version="1.0"?>',
'<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.1.2//EN" "http://www.oasis-open.org/docbook/xml/4.1.2/docbookx.dtd">',
'<?xml version="1.0"?>\n',
'<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.1.2//EN" "http://www.oasis-open.org/docbook/xml/4.1.2/docbookx.dtd">\n',
],
capture: true,
)

View File

@ -1,10 +1,49 @@
project('fprintd', 'c',
version: '1.90.1',
version: '1.90.4',
license: 'GPLv2+',
default_options: [
'buildtype=debugoptimized',
'warning_level=1',
'c_std=gnu99',
'c_args=' + ' '.join([
'-fno-strict-aliasing',
'-Wall',
'-Wcast-align',
'-Werror=address',
'-Werror=array-bounds',
'-Werror=empty-body',
'-Werror=implicit',
'-Werror=init-self',
'-Werror=int-to-pointer-cast',
'-Werror=main',
'-Werror=missing-braces',
'-Werror=nonnull',
'-Werror=pointer-to-int-cast',
'-Werror=redundant-decls',
'-Werror=return-type',
'-Werror=sequence-point',
'-Werror=trigraphs',
'-Werror=write-strings',
'-Wformat-nonliteral',
'-Wformat-security',
'-Wformat=2',
'-Wignored-qualifiers',
'-Wimplicit-function-declaration',
'-Wlogical-op',
'-Wmissing-declarations',
'-Wmissing-format-attribute',
'-Wmissing-include-dirs',
'-Wmissing-noreturn',
'-Wmissing-prototypes',
'-Wnested-externs',
'-Wold-style-definition',
'-Wpointer-arith',
'-Wshadow',
'-Wstrict-prototypes',
'-Wtype-limits',
'-Wundef',
'-Wunused',
]),
],
meson_version: '>= 0.50.0')
@ -14,7 +53,7 @@ i18n = import('i18n')
cc = meson.get_compiler('c')
host_system = host_machine.system()
glib_min_version = '2.56'
libfprint_min_version = '1.90.0'
libfprint_min_version = '1.90.1'
fprintd_installdir = get_option('prefix') / get_option('libexecdir')
fprintd_plugindir = get_option('prefix') / get_option('libdir') / meson.project_name() / 'modules'
@ -26,55 +65,14 @@ if get_option('prefix') != '/usr'
sysconfdir = get_option('prefix') / sysconfdir
endif
common_cflags = cc.get_supported_arguments([
'-fno-strict-aliasing',
'-Wall',
'-Wcast-align',
'-Werror=address',
'-Werror=array-bounds',
'-Werror=empty-body',
'-Werror=implicit',
'-Werror=init-self',
'-Werror=int-to-pointer-cast',
'-Werror=main',
'-Werror=missing-braces',
'-Werror=nonnull',
'-Werror=pointer-to-int-cast',
'-Werror=redundant-decls',
'-Werror=return-type',
'-Werror=sequence-point',
'-Werror=trigraphs',
'-Werror=write-strings',
'-Wformat-nonliteral',
'-Wformat-security',
'-Wformat=2',
'-Wignored-qualifiers',
'-Wimplicit-function-declaration',
'-Wlogical-op',
'-Wmissing-declarations',
'-Wmissing-format-attribute',
'-Wmissing-include-dirs',
'-Wmissing-noreturn',
'-Wmissing-prototypes',
'-Wnested-externs',
'-Wold-style-definition',
'-Wpointer-arith',
'-Wshadow',
'-Wstrict-prototypes',
'-Wtype-limits',
'-Wundef',
'-Wunused',
])
add_project_arguments(common_cflags, language: 'c')
# Dependencies
glib_dep = dependency('glib-2.0', version: '>=' + glib_min_version)
gio_dep = dependency('gio-2.0', version: '>=' + glib_min_version)
gio_unix_dep = dependency('gio-unix-2.0', version: '>=' + glib_min_version)
gmodule_dep = dependency('gmodule-2.0', version: '>=' + glib_min_version)
libfprint_dep = dependency('libfprint-2', version: '>=' + libfprint_min_version)
polkit_gobject_dep = dependency('polkit-gobject-1', version: '>= 0.91')
dbus_dep = dependency('dbus-1', required: false)
dbus_glib_dep = dependency('dbus-glib-1')
libsystemd_dep = dependency('libsystemd', required: get_option('pam'))
pam_dep = cc.find_library('pam',
required: get_option('pam'),
@ -125,6 +123,7 @@ python3_test_modules = {
'dbus': true,
'dbusmock': true,
'gi': true,
'gi.repository.FPrint': true,
'pypamtest': get_option('pam'),
}
python3_available_modules = []
@ -178,5 +177,7 @@ output += ' PAM module: ' + pam_dep.found().to_string()
output += ' Manuals: ' + get_option('man').to_string()
output += ' GTK Doc: ' + get_option('gtk_doc').to_string()
output += ' XML Linter ' + xmllint.found().to_string()
output += '\nTest setup:\n'
output += ' With address sanitizer: ' + address_sanitizer.to_string()
message('\n'+'\n'.join(output)+'\n')

View File

@ -153,16 +153,16 @@ GNUC_UNUSED static const char *verify_result_str_to_msg(const char *result, bool
if (strcmp (result, "verify-retry-scan") == 0) {
if (is_swipe == false)
return N_("Place your finger on the reader again");
return TR (N_("Place your finger on the reader again"));
else
return N_("Swipe your finger again");
return TR (N_("Swipe your finger again"));
}
if (strcmp (result, "verify-swipe-too-short") == 0)
return N_("Swipe was too short, try again");
return TR (N_("Swipe was too short, try again"));
if (strcmp (result, "verify-finger-not-centered") == 0)
return N_("Your finger was not centered, try swiping your finger again");
return TR (N_("Your finger was not centered, try swiping your finger again"));
if (strcmp (result, "verify-remove-and-retry") == 0)
return N_("Remove your finger, and try swiping your finger again");
return TR (N_("Remove your finger, and try swiping your finger again"));
return NULL;
}
@ -179,16 +179,16 @@ GNUC_UNUSED static const char *enroll_result_str_to_msg(const char *result, bool
if (strcmp (result, "enroll-retry-scan") == 0 || strcmp (result, "enroll-stage-passed") == 0) {
if (is_swipe == false)
return N_("Place your finger on the reader again");
return TR (N_("Place your finger on the reader again"));
else
return N_("Swipe your finger again");
return TR (N_("Swipe your finger again"));
}
if (strcmp (result, "enroll-swipe-too-short") == 0)
return N_("Swipe was too short, try again");
return TR (N_("Swipe was too short, try again"));
if (strcmp (result, "enroll-finger-not-centered") == 0)
return N_("Your finger was not centered, try swiping your finger again");
return TR (N_("Your finger was not centered, try swiping your finger again"));
if (strcmp (result, "enroll-remove-and-retry") == 0)
return N_("Remove your finger, and try swiping your finger again");
return TR (N_("Remove your finger, and try swiping your finger again"));
return NULL;
}

View File

@ -201,7 +201,7 @@ verify_result (sd_bus_message *m,
return 0;
}
msg = _(verify_result_str_to_msg (result, data->is_swipe));
msg = verify_result_str_to_msg (result, data->is_swipe);
send_err_msg (data->pamh, msg);
return 0;
@ -423,12 +423,10 @@ do_verify (pam_handle_t *pamh,
ret = PAM_AUTHINFO_UNAVAIL;
} else if (str_equal (data->result, "verify-disconnected")) {
ret = PAM_AUTHINFO_UNAVAIL;
free (data->result);
break;
} else {
send_info_msg (data->pamh, _("An unknown error occurred"));
send_err_msg (data->pamh, _("An unknown error occurred"));
ret = PAM_AUTH_ERR;
free (data->result);
break;
}
free (data->result);

File diff suppressed because it is too large Load Diff

View File

@ -12,9 +12,6 @@
<node name="/" xmlns:doc="http://www.freedesktop.org/dbus/1.0/doc.dtd">
<interface name="net.reactivated.Fprint.Device">
<annotation name="org.freedesktop.DBus.GLib.CSymbol"
value="fprint_device" />
<doc:doc>
<doc:title id="polkit-integration">
PolicyKit integration
@ -282,8 +279,6 @@
<arg type="as" name="enrolled_fingers" direction="out">
<doc:doc><doc:summary>An array of strings representing the enrolled fingerprints. See <doc:ref type="description" to="fingerprint-names">Fingerprint names</doc:ref>.</doc:summary></doc:doc>
</arg>
<annotation name="org.freedesktop.DBus.GLib.Async" value="" />
<doc:doc>
<doc:description>
<doc:para>
@ -304,8 +299,6 @@
<arg type="s" name="username" direction="in">
<doc:doc><doc:summary>The username for whom to delete the enrolled fingerprints. See <doc:ref type="description" to="usernames">Usernames</doc:ref>.</doc:summary></doc:doc>
</arg>
<annotation name="org.freedesktop.DBus.GLib.Async" value="" />
<doc:doc>
<doc:description>
<doc:para>
@ -327,8 +320,6 @@
<!-- ************************************************************ -->
<method name="DeleteEnrolledFingers2">
<annotation name="org.freedesktop.DBus.GLib.Async" value="" />
<doc:doc>
<doc:description>
<doc:para>
@ -348,8 +339,6 @@
<arg type="s" name="username" direction="in">
<doc:doc><doc:summary>The username for whom to claim the device. See <doc:ref type="description" to="usernames">Usernames</doc:ref>.</doc:summary></doc:doc>
</arg>
<annotation name="org.freedesktop.DBus.GLib.Async" value="" />
<doc:doc>
<doc:description>
<doc:para>
@ -368,8 +357,6 @@
<!-- ************************************************************ -->
<method name="Release">
<annotation name="org.freedesktop.DBus.GLib.Async" value="" />
<doc:doc>
<doc:description>
<doc:para>
@ -390,8 +377,6 @@
<arg type="s" name="finger_name" direction="in">
<doc:doc><doc:summary>A string representing the finger to verify. See <doc:ref type="description" to="fingerprint-names">Fingerprint names</doc:ref>.</doc:summary></doc:doc>
</arg>
<annotation name="org.freedesktop.DBus.GLib.Async" value="" />
<doc:doc>
<doc:description>
<doc:para>
@ -415,8 +400,6 @@
<!-- ************************************************************ -->
<method name="VerifyStop">
<annotation name="org.freedesktop.DBus.GLib.Async" value="" />
<doc:doc>
<doc:description>
<doc:para>
@ -486,8 +469,6 @@
<doc:ref type="description" to="fingerprint-names">Fingerprint names</doc:ref>.
Note that "any" is not a valid finger name for this method.</doc:summary></doc:doc>
</arg>
<annotation name="org.freedesktop.DBus.GLib.Async" value="" />
<doc:doc>
<doc:description>
<doc:para>
@ -511,8 +492,6 @@
<!-- ************************************************************ -->
<method name="EnrollStop">
<annotation name="org.freedesktop.DBus.GLib.Async" value="" />
<doc:doc>
<doc:description>
<doc:para>

View File

@ -42,18 +42,33 @@
#define FILE_STORAGE_PATH "/var/lib/fprint"
#define DIR_PERMS 0700
static char *storage_path = NULL;
static const char *get_storage_path(void)
{
const char *path;
const char *path = NULL;
if (storage_path != NULL)
return storage_path;
/* set by systemd >= 240 to an absolute path
* taking into account the StateDirectory
* unit file setting */
path = g_getenv ("STATE_DIRECTORY");
if (path != NULL)
return path;
if (path != NULL) {
/* If multiple directories are set, then in the environment variable
* the paths are concatenated with colon (":"). */
if (strchr (path, ':')) {
g_auto(GStrv) elems = NULL;
elems = g_strsplit (path, ":", -1);
storage_path = g_strdup (elems[0]);
}
}
return FILE_STORAGE_PATH;
if (storage_path == NULL)
storage_path = g_strdup (FILE_STORAGE_PATH);
return storage_path;
}
static char *get_path_to_storedir(const char *driver, const char * device_id, char *base_store)
@ -64,7 +79,7 @@ static char *get_path_to_storedir(const char *driver, const char * device_id, ch
static char *__get_path_to_print(const char *driver, const char * device_id,
FpFinger finger, char *base_store)
{
char *dirpath;
g_autofree char *dirpath = NULL;
char *path;
char fingername[2];
@ -72,7 +87,6 @@ static char *__get_path_to_print(const char *driver, const char * device_id,
dirpath = get_path_to_storedir(driver, device_id, base_store);
path = g_build_filename(dirpath, fingername, NULL);
g_free(dirpath);
return path;
}
@ -173,7 +187,7 @@ int file_storage_print_data_load(FpDevice *dev,
{
g_autofree gchar *path = NULL;
g_autofree gchar *base_store = NULL;
FpPrint *new = NULL;
g_autoptr(FpPrint) new = NULL;
int r;
base_store = file_storage_get_basestore_for_username(username);
@ -186,11 +200,10 @@ int file_storage_print_data_load(FpDevice *dev,
return r;
if (!fp_print_compatible (new, dev)) {
g_object_unref (new);
return -EINVAL;
}
*print = new;
*print = g_steal_pointer (&new);
return 0;
}
@ -296,6 +309,6 @@ int file_storage_init(void)
int file_storage_deinit(void)
{
/* Nothing to do */
g_clear_pointer (&storage_path, g_free);
return 0;
}

View File

@ -1 +0,0 @@
VOID:STRING,BOOLEAN

View File

@ -20,31 +20,46 @@
#pragma once
#include <glib.h>
#include <dbus/dbus-glib.h>
#include <gio/gio.h>
#include <fprint.h>
#include "fprintd-enums.h"
#include "fprintd-dbus.h"
/* General */
#define TIMEOUT 30
#define FPRINT_SERVICE_NAME "net.reactivated.Fprint"
#define FPRINT_SERVICE_PATH "/net/reactivated/Fprint"
/* Errors */
GQuark fprint_error_quark(void);
GType fprint_error_get_type(void);
#define FPRINT_ERROR fprint_error_quark()
#define FPRINT_TYPE_ERROR fprint_error_get_type()
#define FPRINT_ERROR_DBUS_INTERFACE "net.reactivated.Fprint.Error"
typedef enum {
FPRINT_ERROR_CLAIM_DEVICE, /* developer didn't claim the device */
FPRINT_ERROR_ALREADY_IN_USE, /* device is already claimed by somebody else */
FPRINT_ERROR_INTERNAL, /* internal error occurred */
FPRINT_ERROR_PERMISSION_DENIED, /* PolicyKit refused the action */
FPRINT_ERROR_NO_ENROLLED_PRINTS, /* No prints are enrolled */
FPRINT_ERROR_NO_ACTION_IN_PROGRESS, /* No actions currently in progress */
FPRINT_ERROR_INVALID_FINGERNAME, /* the finger name passed was invalid */
FPRINT_ERROR_NO_SUCH_DEVICE, /* device does not exist */
/* developer didn't claim the device */
FPRINT_ERROR_CLAIM_DEVICE, /*< nick=net.reactivated.Fprint.Error.ClaimDevice >*/
/* device is already claimed by somebody else */
FPRINT_ERROR_ALREADY_IN_USE, /*< nick=net.reactivated.Fprint.Error.AlreadyInUse >*/
/* internal error occurred */
FPRINT_ERROR_INTERNAL, /*< nick=net.reactivated.Fprint.Error.Internal >*/
/* PolicyKit refused the action */
FPRINT_ERROR_PERMISSION_DENIED, /*< nick=net.reactivated.Fprint.Error.PermissionDenied >*/
/* No prints are enrolled */
FPRINT_ERROR_NO_ENROLLED_PRINTS, /*< nick=net.reactivated.Fprint.Error.NoEnrolledPrints >*/
/* No actions currently in progress */
FPRINT_ERROR_NO_ACTION_IN_PROGRESS, /*< nick=net.reactivated.Fprint.Error.NoActionInProgress >*/
/* the finger name passed was invalid */
FPRINT_ERROR_INVALID_FINGERNAME, /*< nick=net.reactivated.Fprint.Error.InvalidFingername >*/
/* device does not exist */
FPRINT_ERROR_NO_SUCH_DEVICE, /*< nick=net.reactivated.Fprint.Error.NoSuchDevice >*/
} FprintError;
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 >*/
} FprintDevicePermission;
/* Manager */
#define FPRINT_TYPE_MANAGER (fprint_manager_get_type())
G_DECLARE_FINAL_TYPE (FprintManager, fprint_manager, FPRINT, MANAGER, GObject)
@ -53,23 +68,18 @@ struct _FprintManager {
GObject parent;
};
FprintManager *fprint_manager_new(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, GObject)
G_DECLARE_FINAL_TYPE (FprintDevice, fprint_device, FPRINT, DEVICE,
FprintDBusDeviceSkeleton)
struct _FprintDevice {
GObject parent;
FprintDBusDeviceSkeleton parent;
};
FprintDevice *fprint_device_new(FpDevice *dev);
guint32 _fprint_device_get_id(FprintDevice *rdev);
/* Print */
/* TODO */
/* Binding data included in main.c through server-bindings.h which individual
* class implementations need to access.
*/
extern const DBusGObjectInfo dbus_glib_fprint_manager_object_info;
extern const DBusGObjectInfo dbus_glib_fprint_device_object_info;

View File

@ -1,6 +1,7 @@
/*
* fprint D-Bus daemon
* Copyright (C) 2008 Daniel Drake <dsd@gentoo.org>
* 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
@ -19,10 +20,11 @@
#include "config.h"
#include <locale.h>
#include <poll.h>
#include <stdlib.h>
#include <dbus/dbus-glib-bindings.h>
#include <gio/gio.h>
#include <glib.h>
#include <glib/gi18n.h>
#include <fprint.h>
@ -36,7 +38,6 @@
fp_storage store;
DBusGConnection *fprintd_dbus_conn = NULL;
static gboolean no_timeout = FALSE;
static gboolean g_fatal_warnings = FALSE;
@ -56,11 +57,10 @@ static gboolean
load_storage_module (const char *module_name)
{
GModule *module;
char *filename;
g_autofree char *filename = NULL;
filename = g_module_build_path (PLUGINDIR, module_name);
module = g_module_open (filename, 0);
g_free (filename);
if (module == NULL)
return FALSE;
@ -82,46 +82,29 @@ load_storage_module (const char *module_name)
static gboolean
load_conf (void)
{
GKeyFile *file;
char *filename;
char *module_name;
GError *error = NULL;
gboolean ret;
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)) {
g_warning ("Could not open \"%s\": %s\n", filename, error->message);
goto bail;
return FALSE;
}
g_free (filename);
filename = NULL;
module_name = g_key_file_get_string (file, "storage", "type", &error);
if (module_name == NULL)
goto bail;
g_key_file_free (file);
return FALSE;
if (g_str_equal (module_name, "file")) {
g_free (module_name);
set_storage_file ();
return TRUE;
}
ret = load_storage_module (module_name);
g_free (module_name);
return ret;
bail:
g_key_file_free (file);
g_free (filename);
g_error_free (error);
return FALSE;
return load_storage_module (module_name);
}
static const GOptionEntry entries[] = {
@ -138,13 +121,33 @@ static gboolean sigterm_callback(gpointer data)
return FALSE;
}
static void
on_name_acquired (GDBusConnection *connection,
const char *name,
gpointer user_data)
{
g_debug ("D-Bus service launched with name: %s", name);
}
static void
on_name_lost (GDBusConnection *connection,
const char *name,
gpointer user_data)
{
GMainLoop *loop = user_data;
g_warning ("Failed to get name: %s", name);
g_main_loop_quit (loop);
}
int main(int argc, char **argv)
{
GOptionContext *context;
GMainLoop *loop;
GError *error = NULL;
FprintManager *manager;
DBusGProxy *driver_proxy;
g_autoptr(GOptionContext) context = NULL;
g_autoptr(GMainLoop) loop = NULL;
g_autoptr(GError) error = NULL;
g_autoptr(FprintManager) manager = NULL;
g_autoptr(GDBusConnection) connection = NULL;
guint32 request_name_ret;
setlocale (LC_ALL, "");
@ -158,7 +161,6 @@ int main(int argc, char **argv)
if (g_option_context_parse (context, &argc, &argv, &error) == FALSE) {
g_warning ("couldn't parse command-line options: %s\n", error->message);
g_error_free (error);
return 1;
}
@ -170,16 +172,13 @@ int main(int argc, char **argv)
g_log_set_always_fatal (fatal_mask);
}
/* Obtain a connection to the session bus */
fprintd_dbus_conn = dbus_g_bus_get(DBUS_BUS_SYSTEM, &error);
if (fprintd_dbus_conn == NULL) {
/* Obtain a connection to the system bus */
connection = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, &error);
if (!G_IS_DBUS_CONNECTION (connection)) {
g_warning("Failed to open connection to bus: %s", error->message);
return 1;
}
driver_proxy = dbus_g_proxy_new_for_name(fprintd_dbus_conn,
DBUS_SERVICE_DBUS, DBUS_PATH_DBUS, DBUS_INTERFACE_DBUS);
/* Load the configuration file,
* and the default storage plugin */
if (!load_conf())
@ -192,33 +191,24 @@ int main(int argc, char **argv)
g_debug("Launching FprintObject");
/* create the one instance of the Manager object to be shared between
* all fprintd users */
manager = fprint_manager_new(no_timeout);
* all fprintd users. This blocks until all the devices are enumerated */
manager = fprint_manager_new (connection, no_timeout);
/* Obtain the well-known name after the manager has been initialized.
* Otherwise a client immediately enumerating the devices will not see
* any. */
if (!org_freedesktop_DBus_request_name(driver_proxy, FPRINT_SERVICE_NAME,
0, &request_name_ret, &error)) {
g_warning("Failed to get name: %s", error->message);
g_object_unref (manager);
return 1;
}
if (request_name_ret != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) {
g_warning ("Got result code %u from requesting name", request_name_ret);
g_object_unref (manager);
return 1;
}
g_debug("D-Bus service launched with name: %s", FPRINT_SERVICE_NAME);
request_name_ret = g_bus_own_name_on_connection (connection,
FPRINT_SERVICE_NAME,
G_BUS_NAME_OWNER_FLAGS_NONE,
on_name_acquired,
on_name_lost,
loop, NULL);
g_debug("entering main loop");
g_main_loop_run(loop);
g_bus_unown_name (request_name_ret);
g_debug("main loop completed");
g_object_unref (manager);
return 0;
}

View File

@ -1,6 +1,7 @@
/*
* /net/reactivated/Fprint/Manager object implementation
* Copyright (C) 2008 Daniel Drake <dsd@gentoo.org>
* 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
@ -19,7 +20,6 @@
#include <unistd.h>
#include <stdlib.h>
#include <dbus/dbus-glib-bindings.h>
#include <glib.h>
#include <glib/gi18n.h>
#include <fprint.h>
@ -27,47 +27,107 @@
#include "fprintd.h"
extern DBusGConnection *fprintd_dbus_conn;
static void fprint_manager_constructed (GObject *object);
static gboolean fprint_manager_get_devices(FprintManager *manager,
GPtrArray **devices, GError **error);
static gboolean fprint_manager_get_default_device(FprintManager *manager,
const char **device, GError **error);
#include "manager-dbus-glue.h"
typedef struct
{
GDBusConnection *connection;
GDBusObjectManager *object_manager;
FprintDBusManager *dbus_manager;
FpContext *context;
GSList *dev_registry;
gboolean no_timeout;
guint timeout_id;
} FprintManagerPrivate;
G_DEFINE_TYPE_WITH_CODE(FprintManager, fprint_manager, G_TYPE_OBJECT, G_ADD_PRIVATE (FprintManager))
enum {
PROP_0,
FPRINT_MANAGER_CONNECTION,
N_PROPS
};
static GParamSpec *properties[N_PROPS];
static void fprint_manager_finalize(GObject *object)
{
FprintManagerPrivate *priv = fprint_manager_get_instance_private (FPRINT_MANAGER (object));
g_clear_object (&priv->object_manager);
g_clear_object (&priv->dbus_manager);
g_clear_object (&priv->connection);
g_clear_object (&priv->context);
g_slist_free(priv->dev_registry);
G_OBJECT_CLASS(fprint_manager_parent_class)->finalize(object);
}
static FprintDevice *
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,
const GValue *value, GParamSpec *pspec)
{
FprintManager *self = FPRINT_MANAGER (object);
FprintManagerPrivate *priv = fprint_manager_get_instance_private (self);
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,
GValue *value, GParamSpec *pspec)
{
FprintManager *self = FPRINT_MANAGER (object);
FprintManagerPrivate *priv = fprint_manager_get_instance_private (self);
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)
{
dbus_g_object_type_install_info(FPRINT_TYPE_MANAGER,
&dbus_glib_fprint_manager_object_info);
dbus_g_error_domain_register (FPRINT_ERROR, FPRINT_ERROR_DBUS_INTERFACE, FPRINT_TYPE_ERROR);
GObjectClass *object_class = G_OBJECT_CLASS (klass);
G_OBJECT_CLASS(klass)->finalize = fprint_manager_finalize;
object_class->constructed = fprint_manager_constructed;
object_class->set_property = fprint_manager_set_property;
object_class->get_property = fprint_manager_get_property;
object_class->finalize = fprint_manager_finalize;
properties[FPRINT_MANAGER_CONNECTION] =
g_param_spec_object ("connection",
"Connection",
"Set GDBus connection property",
G_TYPE_DBUS_CONNECTION,
G_PARAM_CONSTRUCT_ONLY |
G_PARAM_READWRITE);
g_object_class_install_properties (object_class, N_PROPS, properties);
}
static gchar *get_device_path(FprintDevice *rdev)
{
return g_strdup_printf("/net/reactivated/Fprint/Device/%d",
return g_strdup_printf (FPRINT_SERVICE_PATH "/Device/%d",
_fprint_device_get_id(rdev));
}
@ -84,7 +144,8 @@ fprint_manager_in_use_notified (FprintDevice *rdev, GParamSpec *spec, FprintMana
{
FprintManagerPrivate *priv = fprint_manager_get_instance_private (manager);
guint num_devices_used = 0;
GSList *l;
g_autolist(GDBusObject) devices = NULL;
GList *l;
gboolean in_use;
if (priv->timeout_id > 0) {
@ -94,9 +155,13 @@ fprint_manager_in_use_notified (FprintDevice *rdev, GParamSpec *spec, FprintMana
if (priv->no_timeout)
return;
for (l = priv->dev_registry; l != NULL; l = l->next) {
FprintDevice *dev = l->data;
devices = g_dbus_object_manager_get_objects (priv->object_manager);
for (l = devices; l != NULL; l = l->next) {
g_autoptr(FprintDevice) dev = NULL;
FprintDBusObjectSkeleton *object = l->data;
dev = fprint_dbus_object_skeleton_get_device (object);
g_object_get (G_OBJECT(dev), "in-use", &in_use, NULL);
if (in_use != FALSE)
num_devices_used++;
@ -106,45 +171,91 @@ fprint_manager_in_use_notified (FprintDevice *rdev, GParamSpec *spec, FprintMana
priv->timeout_id = g_timeout_add_seconds (TIMEOUT, (GSourceFunc) fprint_manager_timeout_cb, manager);
}
static gboolean
handle_get_devices (FprintManager *manager, GDBusMethodInvocation *invocation,
FprintDBusManager *skeleton)
{
g_autoptr(GPtrArray) devices = NULL;
g_autoptr(GError) error = NULL;
if (!fprint_manager_get_devices (manager, &devices, &error)) {
g_dbus_method_invocation_return_gerror (invocation, error);
return TRUE;
}
fprint_dbus_manager_complete_get_devices (skeleton, invocation,
(const gchar *const *)
devices->pdata);
return TRUE;
}
static gboolean
handle_get_default_device (FprintManager *manager,
GDBusMethodInvocation *invocation,
FprintDBusManager *skeleton)
{
const gchar *device;
g_autoptr(GError) error = NULL;
if (!fprint_manager_get_default_device (manager, &device, &error)) {
g_dbus_method_invocation_return_gerror (invocation, error);
return TRUE;
}
fprint_dbus_manager_complete_get_default_device (skeleton, invocation,
device);
return TRUE;
}
static void
device_added_cb (FprintManager *manager, FpDevice *device, FpContext *context)
{
FprintManagerPrivate *priv = fprint_manager_get_instance_private (manager);
FprintDevice *rdev = fprint_device_new(device);
g_autoptr(FprintDBusObjectSkeleton) object = NULL;
g_autoptr(FprintDevice) rdev = NULL;
g_autofree gchar *path = NULL;
rdev = fprint_device_new(device);
g_signal_connect (G_OBJECT(rdev), "notify::in-use",
G_CALLBACK (fprint_manager_in_use_notified), manager);
priv->dev_registry = g_slist_prepend (priv->dev_registry, rdev);
path = get_device_path (rdev);
dbus_g_connection_register_g_object(fprintd_dbus_conn, path,
G_OBJECT(rdev));
object = fprint_dbus_object_skeleton_new (path);
fprint_dbus_object_skeleton_set_device (object,
FPRINT_DBUS_DEVICE (rdev));
g_dbus_object_manager_server_export (
G_DBUS_OBJECT_MANAGER_SERVER (priv->object_manager),
G_DBUS_OBJECT_SKELETON (object));
}
static void
device_removed_cb (FprintManager *manager, FpDevice *device, FpContext *context)
{
FprintManagerPrivate *priv = fprint_manager_get_instance_private (manager);
GSList *item;
g_autofree gchar *path = NULL;
g_autolist (FprintDBusObjectSkeleton) objects = NULL;
GList *item;
for (item = priv->dev_registry; item; item = item->next) {
FprintDevice *rdev;
objects = g_dbus_object_manager_get_objects (priv->object_manager);
for (item = objects; item; item = item->next) {
g_autoptr(FprintDevice) rdev = NULL;
g_autoptr(FpDevice) dev = NULL;
FprintDBusObjectSkeleton *object = item->data;
rdev = item->data;
rdev = fprint_dbus_object_skeleton_get_device (object);
g_object_get (rdev, "dev", &dev, NULL);
if (dev != device)
continue;
priv->dev_registry = g_slist_delete_link (priv->dev_registry, item);
dbus_g_connection_unregister_g_object(fprintd_dbus_conn, G_OBJECT(rdev));
g_dbus_object_manager_server_unexport (
G_DBUS_OBJECT_MANAGER_SERVER (priv->object_manager),
g_dbus_interface_skeleton_get_object_path (G_DBUS_INTERFACE_SKELETON (rdev)));
g_signal_handlers_disconnect_by_data (rdev, manager);
g_object_unref (rdev);
/* We cannot continue to iterate at this point, but we don't need to either */
break;
@ -155,13 +266,37 @@ device_removed_cb (FprintManager *manager, FpDevice *device, FpContext *context)
fprint_manager_in_use_notified (NULL, NULL, manager);
}
static void
fprint_manager_init (FprintManager *manager)
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");
priv->object_manager = G_DBUS_OBJECT_MANAGER (object_manager_server);
priv->dbus_manager = fprint_dbus_manager_skeleton_new ();
priv->context = fp_context_new ();
g_signal_connect_object (priv->dbus_manager,
"handle-get-devices",
G_CALLBACK (handle_get_devices),
manager,
G_CONNECT_SWAPPED);
g_signal_connect_object (priv->dbus_manager,
"handle-get-default-device",
G_CALLBACK (handle_get_default_device),
manager,
G_CONNECT_SWAPPED);
g_dbus_interface_skeleton_export (G_DBUS_INTERFACE_SKELETON (priv->dbus_manager),
priv->connection,
FPRINT_SERVICE_PATH "/Manager", NULL);
g_dbus_object_manager_server_set_connection (object_manager_server,
priv->connection);
/* And register the signals for initial enumeration and hotplug. */
g_signal_connect_object (priv->context,
"device-added",
@ -175,19 +310,25 @@ fprint_manager_init (FprintManager *manager)
manager,
G_CONNECT_SWAPPED);
/* Prepare everything by enumerating all devices. */
/* Prepare everything by enumerating all devices.
* This blocks the main loop until the existing devices are enumerated
*/
fp_context_enumerate (priv->context);
dbus_g_connection_register_g_object(fprintd_dbus_conn,
"/net/reactivated/Fprint/Manager", G_OBJECT(manager));
G_OBJECT_CLASS (fprint_manager_parent_class)->constructed (object);
}
FprintManager *fprint_manager_new(gboolean no_timeout)
static void
fprint_manager_init (FprintManager *manager)
{
}
FprintManager *fprint_manager_new (GDBusConnection *connection, gboolean no_timeout)
{
FprintManagerPrivate *priv;
GObject *object;
object = g_object_new(FPRINT_TYPE_MANAGER, NULL);
object = g_object_new (FPRINT_TYPE_MANAGER, "connection", connection, NULL);
priv = fprint_manager_get_instance_private (FPRINT_MANAGER (object));
priv->no_timeout = no_timeout;
@ -201,23 +342,30 @@ static gboolean fprint_manager_get_devices(FprintManager *manager,
GPtrArray **devices, GError **error)
{
FprintManagerPrivate *priv = fprint_manager_get_instance_private (manager);
GSList *elem;
GSList *l;
g_autolist (FprintDBusObjectSkeleton) objects = NULL;
GList *l;
int num_open;
GPtrArray *devs;
elem = g_slist_reverse(g_slist_copy(priv->dev_registry));
num_open = g_slist_length(elem);
objects = g_dbus_object_manager_get_objects (priv->object_manager);
objects = g_list_reverse (objects);
num_open = g_list_length (objects);
devs = g_ptr_array_sized_new(num_open);
if (num_open > 0) {
for (l = elem; l != NULL; l = l->next) {
FprintDevice *rdev = l->data;
g_ptr_array_add(devs, get_device_path(rdev));
for (l = objects; l != NULL; l = l->next) {
g_autoptr(FprintDevice) rdev = NULL;
FprintDBusObjectSkeleton *object = l->data;
const char *path;
rdev = fprint_dbus_object_skeleton_get_device (object);
path = g_dbus_interface_skeleton_get_object_path (
G_DBUS_INTERFACE_SKELETON (rdev));
g_ptr_array_add (devs, (char *) path);
}
}
g_slist_free(elem);
g_ptr_array_add (devs, NULL);
*devices = devs;
return TRUE;
@ -227,14 +375,19 @@ static gboolean fprint_manager_get_default_device(FprintManager *manager,
const char **device, GError **error)
{
FprintManagerPrivate *priv = fprint_manager_get_instance_private (manager);
GSList *elem;;
g_autolist (FprintDBusObjectSkeleton) objects = NULL;
int num_open;
elem = priv->dev_registry;
num_open = g_slist_length(elem);
objects = g_dbus_object_manager_get_objects (priv->object_manager);
num_open = g_list_length (objects);
if (num_open > 0) {
*device = get_device_path (g_slist_last (elem)->data);
g_autoptr(FprintDevice) rdev = NULL;
FprintDBusObjectSkeleton *object = g_list_last (objects)->data;
rdev = fprint_dbus_object_skeleton_get_device (object);
*device = g_dbus_interface_skeleton_get_object_path (
G_DBUS_INTERFACE_SKELETON (rdev));
return TRUE;
} else {
g_set_error (error, FPRINT_ERROR, FPRINT_ERROR_NO_SUCH_DEVICE,
@ -244,34 +397,25 @@ static gboolean fprint_manager_get_default_device(FprintManager *manager,
}
}
GQuark fprint_error_quark(void)
GQuark fprint_error_quark (void)
{
static GQuark quark = 0;
if (!quark)
quark = g_quark_from_static_string("fprintd-error-quark");
return quark;
}
static volatile gsize quark = 0;
if (g_once_init_enter (&quark)) {
g_autoptr(GEnumClass) errors_enum = NULL;
GQuark domain;
unsigned i;
#define ENUM_ENTRY(NAME, DESC) { NAME, "" #NAME "", DESC }
GType
fprint_error_get_type (void)
{
static GType etype = 0;
domain = g_quark_from_static_string ("fprintd-error-quark");
errors_enum = g_type_class_ref (FPRINT_TYPE_ERROR);
if (etype == 0) {
static const GEnumValue values[] =
{
ENUM_ENTRY (FPRINT_ERROR_CLAIM_DEVICE, "ClaimDevice"),
ENUM_ENTRY (FPRINT_ERROR_ALREADY_IN_USE, "AlreadyInUse"),
ENUM_ENTRY (FPRINT_ERROR_INTERNAL, "Internal"),
ENUM_ENTRY (FPRINT_ERROR_PERMISSION_DENIED, "PermissionDenied"),
ENUM_ENTRY (FPRINT_ERROR_NO_ENROLLED_PRINTS, "NoEnrolledPrints"),
ENUM_ENTRY (FPRINT_ERROR_NO_ACTION_IN_PROGRESS, "NoActionInProgress"),
ENUM_ENTRY (FPRINT_ERROR_INVALID_FINGERNAME, "InvalidFingername"),
ENUM_ENTRY (FPRINT_ERROR_NO_SUCH_DEVICE, "NoSuchDevice"),
{ 0, 0, 0 }
};
etype = g_enum_register_static ("FprintError", values);
for (i = 0; i < errors_enum->n_values; ++i) {
GEnumValue *value = &errors_enum->values[i];
g_dbus_error_register_error (domain, value->value,
value->value_nick);
}
g_once_init_leave (&quark, domain);
}
return etype;
return (GQuark) quark;
}

View File

@ -5,8 +5,6 @@
]>
<node name="/" xmlns:doc="http://www.freedesktop.org/dbus/1.0/doc.dtd">
<interface name="net.reactivated.Fprint.Manager">
<annotation name="org.freedesktop.DBus.GLib.CSymbol"
value="fprint_manager" />
<!-- ************************************************************ -->

View File

@ -1,30 +1,10 @@
fprintd_marshal = gnome.genmarshal('fprintd-marshal',
prefix: 'fprintd_marshal',
sources: 'fprintd-marshal.list',
valist_marshallers: true,
)
bash = find_program('bash')
dbus_binding_tool = find_program('dbus-binding-tool')
dbus_interfaces = ['Manager', 'Device']
dbus_interfaces_files = []
dbus_server_glue_sources = []
foreach interface_name: dbus_interfaces
interface = interface_name.to_lower()
interface_file = interface + '.xml'
glue_name = interface + '-dbus-glue.h'
dbus_server_glue_sources += custom_target(glue_name,
input: interface_file,
output: glue_name,
command: [
dbus_binding_tool,
'--prefix=fprint_' + interface,
'--mode=glib-server',
'--output=@OUTPUT@',
'@INPUT@',
])
dbus_interfaces_files += custom_target('dbus_interface_' + interface,
input: interface_file,
output: 'net.reactivated.Fprint.@0@.xml'.format(interface_name),
@ -34,14 +14,30 @@ foreach interface_name: dbus_interfaces
)
endforeach
fprintd_dbus_sources = gnome.gdbus_codegen('fprintd-dbus',
sources: dbus_interfaces_files,
autocleanup: 'all',
interface_prefix: 'net.reactivated.Fprint.',
namespace: 'FprintDBus',
object_manager: true,
)
fprintd_enum_files = gnome.mkenums_simple('fprintd-enums',
sources: 'fprintd.h',
)
fprintd_deps = declare_dependency(
include_directories: [
include_directories('..'),
],
sources: [
fprintd_enum_files,
fprintd_dbus_sources,
],
dependencies: [
dbus_glib_dep,
glib_dep,
gio_dep,
gio_unix_dep,
gmodule_dep,
libfprint_dep,
polkit_gobject_dep,
@ -58,8 +54,6 @@ libfprintd_private = static_library('fprintd-private',
'device.c',
'fprintd.h',
'manager.c',
dbus_server_glue_sources,
fprintd_marshal,
],
dependencies: fprintd_deps,
gnu_symbol_visibility: 'hidden',

View File

@ -0,0 +1,4 @@
leak:initialize_device
leak:usbi_alloc_device
leak:libusb-1.0.so.*
leak:PyMem_RawMalloc

View File

@ -93,8 +93,9 @@ def GetDefaultDevice(self):
return devices[0]
@dbus.service.method(MANAGER_MOCK_IFACE,
in_signature='sis', out_signature='s')
def AddDevice(self, device_name, num_enroll_stages, scan_type):
in_signature='sisb', out_signature='s')
def AddDevice(self, device_name, num_enroll_stages, scan_type,
has_identification=False):
'''Convenience method to add a fingerprint reader device
You have to specify a device name, the number of enrollment
@ -139,12 +140,27 @@ def AddDevice(self, device_name, num_enroll_stages, scan_type):
device = mockobject.objects[path]
device.fingers = {}
device.has_identification = has_identification
device.claimed_user = None
device.action = None
device.selected_finger = None
device.verify_script = []
return path
@dbus.service.method(MANAGER_MOCK_IFACE,
in_signature='o')
def RemoveDevice(self, path):
# This isn't compatible with hotplugging devices, which fprintd doesn't
# support yet, but it's meant to remove devices added to the mock for
# testing purposes.
if not path:
raise dbus.exceptions.DBusException(
'Invalid empty path.',
name='org.freedesktop.DBus.Error.InvalidArgs')
self.RemoveObject(path)
@dbus.service.method(DEVICE_IFACE,
in_signature='s', out_signature='as')
def ListEnrolledFingers(device, user):
@ -186,6 +202,8 @@ def Release(device):
'Device was not claimed before use',
name='net.reactivated.Fprint.Error.ClaimDevice')
device.claimed_user = None
device.action = None
device.selected_finger = None
def can_verify_finger(device, finger_name):
# We should already have checked that there are enrolled fingers
@ -228,8 +246,9 @@ def VerifyStart(device, finger_name):
name='net.reactivated.Fprint.Error.AlreadyInUse')
device.action = 'verify'
if finger_name == 'any':
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
])
@ -262,6 +281,7 @@ def VerifyStop(device):
'No verification to stop',
name='net.reactivated.Fprint.Error.NoActionInProgress')
device.action = None
device.selected_finger = None
@dbus.service.method(DEVICE_IFACE,
in_signature='s', out_signature='')
@ -317,11 +337,6 @@ def SetEnrolledFingers(device, user, fingers):
Returns nothing.
'''
if len(fingers) < 1:
raise dbus.exceptions.DBusException(
'Fingers array must not be empty',
name='org.freedesktop.DBus.Error.InvalidArgs')
for k in fingers:
if k not in VALID_FINGER_NAMES:
raise dbus.exceptions.DBusException(
@ -330,6 +345,30 @@ def SetEnrolledFingers(device, user, fingers):
device.fingers[user] = fingers
@dbus.service.method(DEVICE_MOCK_IFACE,
in_signature='', out_signature='s')
def GetSelectedFinger(device):
'''Convenience method to get the finger under verification
Returns the finger name that the user has selected for verifying
'''
if not device.selected_finger:
raise dbus.exceptions.DBusException(
'Device is not verifying',
name='net.reactivated.Fprint.Error.NoActionInProgress')
return device.selected_finger
@dbus.service.method(DEVICE_MOCK_IFACE,
in_signature='', out_signature='b')
def HasIdentification(device):
'''Convenience method to get if a device supports identification
Returns whether identification is supported.
'''
return device.has_identification
@dbus.service.method(DEVICE_MOCK_IFACE,
in_signature='a(sbi)', out_signature='')
def SetVerifyScript(device, script):

113
tests/dbusmock/polkitd.py Normal file
View File

@ -0,0 +1,113 @@
# -*- coding: utf-8 -*-
'''polkit mock template
This creates the basic methods and properties of the
org.freedesktop.PolicyKit1.Authority object, so that we can use it async
'''
# 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 3 of the License, or (at your option) any
# later version. See http://www.gnu.org/copyleft/lgpl.html for the full text
# of the license.
__author__ = 'Marco Trevisan'
__email__ = 'marco.trevisan@canonical.com'
__copyright__ = '(c) 2020 Canonical Ltd.'
__license__ = 'LGPL 3+'
import dbus
import time
from dbusmock import MOCK_IFACE, mockobject
BUS_NAME = 'org.freedesktop.PolicyKit1'
MAIN_OBJ = '/org/freedesktop/PolicyKit1/Authority'
MAIN_IFACE = 'org.freedesktop.PolicyKit1.Authority'
SYSTEM_BUS = True
IS_OBJECT_MANAGER = False
def load(mock, parameters):
polkitd = mockobject.objects[MAIN_OBJ]
# default state
polkitd.allow_unknown = False
polkitd.allowed = []
polkitd.delay = 0
polkitd.simulate_hang = False
polkitd.hanging_actions = []
polkitd.hanging_calls = []
mock.AddProperties(MAIN_IFACE,
dbus.Dictionary({
'BackendName': 'local',
'BackendVersion': '0.8.15',
'BackendFeatures': dbus.UInt32(1, variant_level=1),
}, signature='sv'))
@dbus.service.method(MAIN_IFACE,
in_signature='(sa{sv})sa{ss}us', out_signature='(bba{ss})',
async_callbacks=('ok_cb', 'err_cb'))
def CheckAuthorization(self, subject, action_id, details, flags, cancellation_id,
ok_cb, err_cb):
time.sleep(self.delay)
allowed = action_id in self.allowed or self.allow_unknown
ret = (allowed, False, {'test': 'test'})
if self.simulate_hang or action_id in self.hanging_actions:
self.hanging_calls.append((ok_cb, ret))
else:
ok_cb(ret)
@dbus.service.method(MOCK_IFACE, in_signature='b', out_signature='')
def AllowUnknown(self, default):
'''Control whether unknown actions are allowed
This controls the return value of CheckAuthorization for actions which were
not explicitly allowed by SetAllowed().
'''
self.allow_unknown = default
@dbus.service.method(MOCK_IFACE, in_signature='d', out_signature='')
def SetDelay(self, delay):
'''Makes the CheckAuthorization() method to delay'''
self.delay = delay
@dbus.service.method(MOCK_IFACE, in_signature='b', out_signature='')
def SimulateHang(self, hang):
'''Makes the CheckAuthorization() method to hang'''
self.simulate_hang = hang
@dbus.service.method(MOCK_IFACE, in_signature='as', out_signature='')
def SimulateHangActions(self, actions):
'''Makes the CheckAuthorization() method to hang on such actions'''
self.hanging_actions = actions
@dbus.service.method(MOCK_IFACE, in_signature='', out_signature='')
def ReleaseHangingCalls(self):
'''Calls all the hanging callbacks'''
for (cb, ret) in self.hanging_calls:
cb(ret)
self.hanging_calls = []
@dbus.service.method(MOCK_IFACE, in_signature='', out_signature='b')
def HaveHangingCalls(self):
'''Check if we've hangling calls'''
return len(self.hanging_calls)
@dbus.service.method(MOCK_IFACE, in_signature='as', out_signature='')
def SetAllowed(self, actions):
'''Set allowed actions'''
self.allowed = actions
@dbus.service.method(MAIN_IFACE,
in_signature='', out_signature='o')
def GetDefaultDevice(self):
devices = self.GetDevices()
if len(devices) < 1:
raise dbus.exceptions.DBusException(
'No devices available',
name='net.reactivated.Fprint.Error.NoSuchDevice')
return devices[0]

1119
tests/fprintd.py Executable file → Normal file

File diff suppressed because it is too large Load Diff

View File

@ -1,36 +1,118 @@
# Add a way to discover and run python unit tests separately
# https://github.com/mesonbuild/meson/issues/6851
python_tests = [
# List all the python tests, must be in the form:
# {
# 'name': 'test name',
# 'file': 'full test file path, use files('path')[0]',
# Fields below are optional:
# 'workdir': '',
# 'env': [],
# 'depends': [],
# 'suite': [],
# 'extra_args': [],
# 'timeout': 30,
# 'is_parallel': true,
# }
]
address_sanitizer = get_option('b_sanitize') == 'address'
tests = [
'fprintd',
'test_fprintd_utils',
]
foreach t: tests
test(t,
python3,
args: meson.current_source_dir() / t + '.py',
suite: ['daemon'],
depends: [
fprintd,
fprintd_utils,
],
env: [
'G_DEBUG=fatal-criticals',
'G_MESSAGES_DEBUG=all',
'FPRINT_BUILD_DIR=' + meson.build_root() / 'src',
'TOPSRCDIR=' + meson.source_root(),
],
)
python_tests += [
{
'name': t,
'file': files(meson.current_source_dir() / t + '.py')[0],
'env': [
'G_DEBUG=fatal-criticals',
'G_MESSAGES_DEBUG=all',
'FPRINT_BUILD_DIR=' + meson.build_root() / 'src',
'TOPSRCDIR=' + meson.source_root(),
],
'depends': [
fprintd,
fprintd_utils,
],
'suite': [t == 'fprintd' ? 'daemon' : ''],
}
]
endforeach
if get_option('pam')
subdir('pam')
endif
# Add a way to discover and run python unit tests separately
# https://github.com/mesonbuild/meson/issues/6851
unittest_inspector = find_program('unittest_inspector.py')
foreach pt: python_tests
r = run_command(unittest_inspector, pt.get('file'))
unit_tests = r.stdout().strip().split('\n')
base_args = [ pt.get('file') ] + pt.get('extra_args', [])
suite = pt.get('suite', [])
if r.returncode() == 0 and unit_tests.length() > 0
suite += pt.get('name')
else
unit_tests = [pt.get('name')]
endif
foreach ut: unit_tests
ut_suite = suite
ut_args = base_args
if unit_tests.length() > 1
ut_args += ut
ut_suite += ut.split('.')[0]
endif
test(ut,
python3,
args: ut_args,
suite: ut_suite,
depends: pt.get('depends', []),
workdir: pt.get('workdir', meson.build_root()),
env: pt.get('env', []),
timeout: pt.get('timeout', 30),
is_parallel: pt.get('is_parallel', true),
)
endforeach
endforeach
timeout_multiplier = 1
test_envs = [
'G_SLICE=always-malloc',
'MALLOC_CHECK_=2',
]
if address_sanitizer
timeout_multiplier = 3
test_envs += [
'ADDRESS_SANITIZER=true',
'ASAN_OPTIONS=@0@'.format(':'.join([
'abort_on_error=true',
'symbolize=true',
])),
'LSAN_OPTIONS=@0@'.format(':'.join([
'exitcode=0',
'strict_string_checks=true',
'suppressions=@0@'.format(
files(meson.current_source_dir() / 'LSAN-leaks-suppress.txt')[0]),
])),
]
endif
add_test_setup('default_setup',
is_default: true,
env: [
'G_SLICE=always-malloc',
'MALLOC_CHECK_=2',
'MALLOC_PERTURB_=55',
],
env: test_envs,
timeout_multiplier: timeout_multiplier
)
if find_program('valgrind', required: false).found()
if not address_sanitizer and find_program('valgrind', required: false).found()
glib_share = glib_dep.get_pkgconfig_variable('prefix') / 'share' / glib_dep.name()
glib_suppressions = glib_share + '/valgrind/glib.supp'
add_test_setup('valgrind',
@ -42,6 +124,3 @@ if find_program('valgrind', required: false).found()
)
endif
if get_option('pam')
subdir('pam')
endif

View File

@ -4,24 +4,55 @@ tests = [
'test_pam_fprintd',
]
foreach t: tests
test(t,
python3,
args: meson.current_source_dir() / t + '.py',
suite: ['PAM'],
depends: [
pam_fprintd,
pam_service_file,
],
env: [
'TOPBUILDDIR=' + meson.build_root(),
'TOPSRCDIR=' + meson.source_root(),
'LD_PRELOAD=libpam_wrapper.so',
'PAM_WRAPPER=1',
'PAM_WRAPPER_DEBUGLEVEL=2',
'PAM_WRAPPER_SERVICE_DIR=' + meson.current_build_dir() / 'services',
'G_DEBUG=fatal-warnings',
],
timeout: 60,
)
preloaded_libs = []
pam_tests_ld_preload = []
if address_sanitizer
# ASAN has to be the first in list
preloaded_libs += 'asan'
endif
preloaded_libs += 'pam_wrapper'
foreach libname: preloaded_libs
lib = run_command(meson.get_compiler('c'),
'-print-file-name=lib@0@.so'.format(libname)
).stdout().strip()
# Support linker script files
if run_command('grep', '-qI', '^INPUT', files(lib)).returncode() == 0
out = run_command('cat', lib).stdout()
lib = out.split('(')[1].split(')')[0].strip()
endif
if lib != '' and lib[0] == '/'
message('Found library @0@ as @1@'.format(libname, lib))
pam_tests_ld_preload += '@0@'.format(files(lib)[0])
else
tests = []
warning('No library found for ' + libname + ', skipping PAM tests')
endif
endforeach
foreach t: tests
python_tests += [
{
'name': t,
'file': files(meson.current_source_dir() / t + '.py')[0],
'env': [
'TOPBUILDDIR=' + meson.build_root(),
'TOPSRCDIR=' + meson.source_root(),
'LD_PRELOAD=' + ' '.join(pam_tests_ld_preload),
'PAM_WRAPPER=1',
'PAM_WRAPPER_DEBUGLEVEL=2',
'PAM_WRAPPER_SERVICE_DIR=' + meson.current_build_dir() / 'services',
'G_DEBUG=fatal-warnings',
],
'depends': [
pam_fprintd,
pam_service_file,
],
'suite': ['PAM'],
}
]
endforeach

View File

@ -16,15 +16,12 @@ import unittest
import sys
import subprocess
import dbus
import dbus.mainloop.glib
import dbusmock
import fcntl
import os
import time
import pypamtest
dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
PAM_SUCCESS = 0
PAM_AUTH_ERR = 7
PAM_AUTHINFO_UNAVAIL = 9
@ -88,6 +85,46 @@ 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_fprintd_identify_error(self):
self.setup_device()
script = [
( 'verify-unknown-error', True, 2 )
]
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.assertRegex(res.info[0], r'Swipe your left little finger across the fingerprint reader')
self.assertEqual(len(res.errors), 0)
def test_pam_fprintd_identify_error2(self):
self.setup_device()
script = [
( 'verify-disconnected', True, 2 )
]
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.assertRegex(res.info[0], r'Swipe your left little finger across the fingerprint reader')
self.assertEqual(len(res.errors), 0)
def test_pam_fprintd_identify_error3(self):
self.setup_device()
script = [
( 'verify-INVALID', True, 2 )
]
self.device_mock.SetVerifyScript(script)
tc = pypamtest.TestCase(pypamtest.PAMTEST_AUTHENTICATE, expected_rv=PAM_AUTH_ERR)
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), 1)
self.assertRegex(res.errors[0], r'An unknown error occurred')
def test_pam_fprintd_auth(self):
self.setup_device()
script = [

View File

@ -22,9 +22,23 @@ import fcntl
import os
import time
VALID_FINGER_NAMES = [
'left-thumb',
'left-index-finger',
'left-middle-finger',
'left-ring-finger',
'left-little-finger',
'right-thumb',
'right-index-finger',
'right-middle-finger',
'right-ring-finger',
'right-little-finger'
]
dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
class TestFprintd(dbusmock.DBusTestCase):
class TestFprintdUtilsBase(dbusmock.DBusTestCase):
'''Test fprintd utilities'''
@classmethod
@ -57,7 +71,11 @@ class TestFprintd(dbusmock.DBusTestCase):
if os.path.exists(valgrind):
klass.wrapper_args += ['--suppressions={}'.format(valgrind)]
if 'ADDRESS_SANITIZER' in os.environ:
klass.sleep_time *= 2
def setUp(self):
super().setUp()
(self.p_mock, self.obj_fprintd_manager) = self.spawn_server_template(
self.template_name, {}, stdout=subprocess.PIPE)
# set log to nonblocking
@ -68,119 +86,229 @@ class TestFprintd(dbusmock.DBusTestCase):
def tearDown(self):
self.p_mock.terminate()
self.p_mock.wait()
super().tearDown()
def setup_device(self):
device_path = self.obj_fprintd_mock.AddDevice('FDO Trigger Finger Laser Reader', 3, 'swipe')
self.device_mock = self.dbus_con.get_object('net.reactivated.Fprint', device_path)
self.device_mock.SetEnrolledFingers('toto', ['left-little-finger', 'right-little-finger'])
self.device_path = self.obj_fprintd_mock.AddDevice(
'FDO Trigger Finger Laser Reader', 3, 'swipe')
self.device_mock = self.dbus_con.get_object('net.reactivated.Fprint',
self.device_path)
self.set_enrolled_fingers(['left-little-finger', 'right-little-finger'])
def test_fprintd_enroll(self):
self.setup_device()
def set_enrolled_fingers(self, fingers, user='toto'):
self.enrolled_fingers = fingers
self.device_mock.SetEnrolledFingers('toto', self.enrolled_fingers,
signature='sas')
mock_log = tempfile.NamedTemporaryFile()
process = subprocess.Popen(self.wrapper_args + [self.tools_prefix + 'fprintd-enroll', '-f', 'right-index-finger', 'toto'],
stdout=mock_log,
def start_utility_process(self, utility_name, args=[], sleep=True):
utility = [ os.path.join(self.tools_prefix, 'fprintd-{}'.format(utility_name)) ]
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)
time.sleep(self.sleep_time)
self.addCleanup(self.try_stop_utility_process, process)
with open(mock_log.name) as f:
out = f.read()
self.assertRegex(out, r'right-index-finger')
if sleep:
time.sleep(self.sleep_time)
return process
def stop_utility_process(self, process):
print(process.stdout.read())
process.terminate()
process.wait()
def try_stop_utility_process(self, process):
try:
self.stop_utility_process(process)
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)
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
class TestFprintdUtils(TestFprintdUtilsBase):
def setUp(self):
super().setUp()
self.setup_device()
def test_fprintd_enroll(self):
process = self.start_utility_process('enroll', ['-f', 'right-index-finger', 'toto'])
out = self.get_process_output(process)
self.assertRegex(out, r'right-index-finger')
self.device_mock.EmitEnrollStatus('enroll-completed', True)
time.sleep(self.sleep_time)
with open(mock_log.name) as f:
out = f.read()
self.assertRegex(out, 'Enroll result: enroll-completed')
out = self.get_process_output(process)
self.assertRegex(out, 'Enroll result: enroll-completed')
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.assertEqual(ret, 0)
# Toto does
out, ret = self.run_utility_process('list', ['toto'])
self.assertRegex(out, r'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.assertEqual(ret, 0)
self.assertRegex(out, r'right-little-finger')
# Delete fingerprints
out, ret = self.run_utility_process('delete', ['toto'])
self.assertRegex(out, r'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.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.assertEqual(ret, 1)
def test_fprintd_list(self):
out, ret = self.run_utility_process('list', ['toto'])
self.assertIn('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.assertEqual(ret, 1)
def test_fprintd_verify(self):
out, ret = self.run_utility_process('verify', ['toto'])
self.assertIn('No devices available', out)
self.assertEqual(ret, 1)
class TestFprintdUtilsVerify(TestFprintdUtilsBase):
def setUp(self):
super().setUp()
self.setup_device()
mock_log = tempfile.NamedTemporaryFile()
process = subprocess.Popen(self.wrapper_args + [self.tools_prefix + 'fprintd-verify', 'toto'],
stdout=mock_log,
stderr=subprocess.STDOUT,
universal_newlines=True)
def start_verify_process(self, user='toto', finger=None, checkEnrolled=True):
args = [user]
if finger:
args += ['-f', finger]
time.sleep(self.sleep_time)
self.process = self.start_utility_process('verify', args)
out = self.get_process_output(self.process)
with open(mock_log.name) as f:
out = f.read()
self.assertRegex(out, r'left-little-finger')
self.assertNotRegex(out, 'Verify result: verify-match \(done\)')
self.assertNotRegex(out, r'Device already in use by [A-z]+')
self.assertNotIn('Verify result:', out)
if checkEnrolled and finger:
self.assertNotIn('''Finger '{}' not enrolled for user {}'''.format(
finger, user), out)
if checkEnrolled:
for f in self.enrolled_fingers:
self.assertIn(f, out)
if finger:
expected_finger = finger
if finger == 'any' and not self.device_mock.HasIdentification():
expected_finger = self.enrolled_fingers[0]
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))
def test_fprintd_verify(self):
self.start_verify_process()
self.device_mock.EmitVerifyStatus('verify-match', True)
time.sleep(self.sleep_time)
self.assertVerifyMatch(True)
with open(mock_log.name) as f:
out = f.read()
self.assertRegex(out, 'Verify result: verify-match \(done\)')
def test_fprintd_verify_enrolled_fingers(self):
for finger in self.enrolled_fingers:
self.start_verify_process(finger=finger)
self.device_mock.EmitVerifyStatus('verify-match', True)
time.sleep(self.sleep_time)
self.assertVerifyMatch(True)
def test_fprintd_verify_any_finger_no_identification(self):
self.start_verify_process(finger='any')
self.device_mock.EmitVerifyStatus('verify-match', True)
time.sleep(self.sleep_time)
self.assertVerifyMatch(True)
def test_fprintd_verify_any_finger_identification(self):
self.obj_fprintd_mock.RemoveDevice(self.device_path)
self.device_path = self.obj_fprintd_mock.AddDevice('Full powered device',
3, 'press', True)
self.device_mock = self.dbus_con.get_object('net.reactivated.Fprint',
self.device_path)
self.set_enrolled_fingers(VALID_FINGER_NAMES)
self.start_verify_process(finger='any')
self.device_mock.EmitVerifyStatus('verify-match', True)
time.sleep(self.sleep_time)
self.assertVerifyMatch(True)
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]:
regex = r'Finger \'{}\' not enrolled'.format(finger)
with self.assertRaisesRegex(AssertionError, regex):
self.start_verify_process(finger=finger)
self.device_mock.Release()
def test_fprintd_verify_no_enrolled_fingers(self):
self.set_enrolled_fingers([])
self.start_verify_process()
self.assertEqual(self.process.poll(), 1)
def test_fprintd_list_all_fingers(self):
self.set_enrolled_fingers(VALID_FINGER_NAMES)
self.start_verify_process()
def test_fprintd_verify_script(self):
self.setup_device()
script = [
( 'verify-match', True, 2 )
]
self.device_mock.SetVerifyScript(script)
mock_log = tempfile.NamedTemporaryFile()
process = subprocess.Popen(self.wrapper_args + [self.tools_prefix + 'fprintd-verify', 'toto'],
stdout=mock_log,
stderr=subprocess.STDOUT,
universal_newlines=True)
self.start_verify_process()
time.sleep(self.sleep_time * 4)
self.assertVerifyMatch(True)
time.sleep(self.sleep_time)
def test_fprintd_multiple_verify_fails(self):
self.start_verify_process()
with open(mock_log.name) as f:
out = f.read()
self.assertRegex(out, r'left-little-finger')
self.assertNotRegex(out, 'Verify result: verify-match \(done\)')
time.sleep(2)
with open(mock_log.name) as f:
out = f.read()
self.assertRegex(out, 'Verify result: verify-match \(done\)')
def test_fprintd_list(self):
self.setup_device()
# Rick has no fingerprints enrolled
out = subprocess.check_output(self.wrapper_args + [self.tools_prefix + 'fprintd-list', 'rick'],
stderr=subprocess.STDOUT,
universal_newlines=True)
self.assertRegex(out, r'has no fingers enrolled for')
# Toto does
out = subprocess.check_output(self.wrapper_args + [self.tools_prefix + 'fprintd-list', 'toto'],
universal_newlines=True)
self.assertRegex(out, r'left-little-finger')
self.assertRegex(out, r'right-little-finger')
def test_fprintd_delete(self):
self.setup_device()
# Has fingerprints enrolled
out = subprocess.check_output(self.wrapper_args + [self.tools_prefix + 'fprintd-list', 'toto'],
stderr=subprocess.STDOUT,
universal_newlines=True)
self.assertRegex(out, r'left-little-finger')
self.assertRegex(out, r'right-little-finger')
# Delete fingerprints
out = subprocess.check_output(self.wrapper_args + [self.tools_prefix + 'fprintd-delete', 'toto'],
stderr=subprocess.STDOUT,
universal_newlines=True)
self.assertRegex(out, r'Fingerprints deleted')
# Doesn't have fingerprints
out = subprocess.check_output(self.wrapper_args + [self.tools_prefix + 'fprintd-list', 'toto'],
stderr=subprocess.STDOUT,
universal_newlines=True)
self.assertRegex(out, r'has no fingers enrolled for')
with self.assertRaisesRegex(AssertionError, r'Device already in use'):
self.start_verify_process()
if __name__ == '__main__':
# avoid writing to stderr

46
tests/unittest_inspector.py Executable file
View File

@ -0,0 +1,46 @@
#! /usr/bin/env python3
# Copyright © 2020, Canonical Ltd
#
# 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:
# Marco Trevisan <marco.trevisan@canonical.com>
import argparse
import importlib.util
import inspect
import os
import unittest
def list_tests(module):
tests = []
for name, obj in inspect.getmembers(module):
if inspect.isclass(obj) and issubclass(obj, unittest.TestCase):
cases = unittest.defaultTestLoader.getTestCaseNames(obj)
tests += [ (obj, '{}.{}'.format(name, t)) for t in cases ]
return tests
if __name__ == '__main__':
parser = argparse.ArgumentParser()
parser.add_argument('unittest_source', type=argparse.FileType('r'))
args = parser.parse_args()
source_path = args.unittest_source.name
spec = importlib.util.spec_from_file_location(
os.path.basename(source_path), source_path)
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)
for machine, human in list_tests(module):
print(human)

View File

@ -1,6 +1,7 @@
/*
* fprintd example to delete fingerprints
* Copyright (C) 2008 Daniel Drake <dsd@gentoo.org>
* 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
@ -20,108 +21,116 @@
#include <stdio.h>
#include <stdlib.h>
#include <locale.h>
#include <dbus/dbus-glib-bindings.h>
#include "manager-dbus-glue.h"
#include "device-dbus-glue.h"
#include "fprintd-dbus.h"
static DBusGProxy *manager = NULL;
static DBusGConnection *connection = NULL;
static FprintDBusManager *manager = NULL;
static GDBusConnection *connection = NULL;
static void create_manager(void)
{
GError *error = NULL;
g_autoptr(GError) error = NULL;
connection = dbus_g_bus_get(DBUS_BUS_SYSTEM, &error);
connection = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, &error);
if (connection == NULL) {
g_print("Failed to connect to session bus: %s\n", error->message);
exit (1);
}
manager = dbus_g_proxy_new_for_name(connection,
"net.reactivated.Fprint", "/net/reactivated/Fprint/Manager",
"net.reactivated.Fprint.Manager");
}
static void delete_fingerprints(DBusGProxy *dev, const char *username)
{
GError *error = NULL;
GHashTable *props;
DBusGProxy *p;
p = dbus_g_proxy_new_from_proxy(dev, "org.freedesktop.DBus.Properties", NULL);
if (!dbus_g_proxy_call (p, "GetAll", &error, G_TYPE_STRING, "net.reactivated.Fprint.Device", G_TYPE_INVALID,
dbus_g_type_get_map ("GHashTable", G_TYPE_STRING, G_TYPE_VALUE), &props, G_TYPE_INVALID)) {
g_print("GetAll on the Properties interface failed: %s\n", error->message);
manager = fprint_dbus_manager_proxy_new_sync (connection,
G_DBUS_PROXY_FLAGS_NONE,
"net.reactivated.Fprint",
"/net/reactivated/Fprint/Manager",
NULL, &error);
if (manager == NULL) {
g_print ("Failed to get Fprintd manager: %s\n", error->message);
exit (1);
}
}
if (!net_reactivated_Fprint_Device_claim(dev, username, &error)) {
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)) {
g_print("failed to claim device: %s\n", error->message);
exit (1);
}
if (!net_reactivated_Fprint_Device_delete_enrolled_fingers2(dev, &error)) {
if (dbus_g_error_has_name (error, "net.reactivated.Fprint.Error.NoEnrolledPrints") == FALSE) {
g_print("ListEnrolledFingers failed: %s\n", error->message);
if (!fprint_dbus_device_call_delete_enrolled_fingers2_sync (dev, NULL,
&error)) {
gboolean ignore_error = FALSE;
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")) {
g_print ("No fingerprints to delete on %s\n",
fprint_dbus_device_get_name (dev));
ignore_error = TRUE;
}
}
if (!ignore_error) {
g_print("ListEnrolledFingers failed: %s\n",
error->message);
exit (1);
} else {
g_print ("No fingerprints to delete on %s\n", g_value_get_string (g_hash_table_lookup (props, "name")));
g_print ("No fingerprints to delete on %s\n",
fprint_dbus_device_get_name (dev));
}
} else {
g_print ("Fingerprints deleted on %s\n", g_value_get_string (g_hash_table_lookup (props, "name")));
g_print ("Fingerprints deleted on %s\n",
fprint_dbus_device_get_name (dev));
}
g_clear_error (&error);
if (!net_reactivated_Fprint_Device_release(dev, &error)) {
if (!fprint_dbus_device_call_release_sync (dev, NULL, &error)) {
g_print("ReleaseDevice failed: %s\n", error->message);
exit (1);
}
g_hash_table_destroy (props);
g_object_unref (p);
}
static void process_devices(char **argv)
{
GError *error = NULL;
GPtrArray *devices;
g_autoptr(GError) error = NULL;
g_auto(GStrv) devices = NULL;
char *path;
guint num_devices;
guint i;
if (!net_reactivated_Fprint_Manager_get_devices(manager, &devices, &error)) {
g_print("list_devices failed: %s\n", error->message);
if (!fprint_dbus_manager_call_get_devices_sync (manager, &devices,
NULL, &error)) {
g_print("Impossible to get devices: %s\n", error->message);
exit (1);
}
if (devices->len == 0) {
g_print("No devices found\n");
num_devices = g_strv_length (devices);
if (num_devices == 0) {
g_print("No devices available\n");
exit(1);
}
g_print("found %d devices\n", devices->len);
for (i = 0; i < devices->len; i++) {
path = g_ptr_array_index(devices, i);
g_print ("found %u devices\n", num_devices);
for (i = 0; devices[i] != NULL; i++) {
path = devices[i];
g_print("Device at %s\n", path);
}
for (i = 0; i < devices->len; i++) {
for (i = 0; devices[i] != NULL; i++) {
g_autoptr(FprintDBusDevice) dev = NULL;
guint j;
DBusGProxy *dev;
path = g_ptr_array_index(devices, i);
path = devices[i];
g_print("Using device %s\n", path);
/* FIXME use for_name_owner?? */
dev = dbus_g_proxy_new_for_name(connection, "net.reactivated.Fprint",
path, "net.reactivated.Fprint.Device");
/* NOTE: We should handle error cases! */
dev = fprint_dbus_device_proxy_new_sync (connection,
G_DBUS_PROXY_FLAGS_NONE,
"net.reactivated.Fprint",
path, NULL, NULL);
for (j = 1; argv[j] != NULL; j++)
delete_fingerprints (dev, argv[j]);
g_object_unref (dev);
}
g_ptr_array_foreach(devices, (GFunc) g_free, NULL);
g_ptr_array_free(devices, TRUE);
}
int main(int argc, char **argv)

View File

@ -1,6 +1,7 @@
/*
* fprintd example to enroll right index finger
* Copyright (C) 2008 Daniel Drake <dsd@gentoo.org>
* 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
@ -21,64 +22,67 @@
#include <stdlib.h>
#include <string.h>
#include <locale.h>
#include <dbus/dbus-glib-bindings.h>
#include "manager-dbus-glue.h"
#include "device-dbus-glue.h"
#include "marshal.h"
#include "fprintd-dbus.h"
#define N_(x) x
#define TR(x) x
#include "fingerprint-strings.h"
static DBusGProxy *manager = NULL;
static DBusGConnection *connection = NULL;
static FprintDBusManager *manager = NULL;
static GDBusConnection *connection = NULL;
static char *finger_name = NULL;
static char **usernames = NULL;
static void create_manager(void)
{
GError *error = NULL;
g_autoptr(GError) error = NULL;
connection = dbus_g_bus_get(DBUS_BUS_SYSTEM, &error);
connection = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, &error);
if (connection == NULL) {
g_print("Failed to connect to session bus: %s\n", error->message);
exit (1);
}
manager = dbus_g_proxy_new_for_name(connection,
"net.reactivated.Fprint", "/net/reactivated/Fprint/Manager",
"net.reactivated.Fprint.Manager");
}
static DBusGProxy *open_device(const char *username)
{
GError *error = NULL;
gchar *path;
DBusGProxy *dev;
if (!net_reactivated_Fprint_Manager_get_default_device(manager, &path, &error)) {
g_print("list_devices failed: %s\n", error->message);
manager = fprint_dbus_manager_proxy_new_sync (connection,
G_DBUS_PROXY_FLAGS_NONE,
"net.reactivated.Fprint",
"/net/reactivated/Fprint/Manager",
NULL, &error);
if (manager == NULL) {
g_print ("Failed to get Fprintd manager: %s\n", error->message);
exit (1);
}
if (path == NULL) {
g_print("No devices found\n");
exit(1);
}
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)) {
g_print("Impossible to enroll: %s\n", error->message);
exit (1);
}
g_print("Using device %s\n", path);
/* FIXME use for_name_owner?? */
dev = dbus_g_proxy_new_for_name(connection, "net.reactivated.Fprint",
path, "net.reactivated.Fprint.Device");
dev = fprint_dbus_device_proxy_new_sync (connection,
G_DBUS_PROXY_FLAGS_NONE,
"net.reactivated.Fprint",
path, NULL, &error);
g_free (path);
if (error) {
g_print ("failed to connect to device: %s\n", error->message);
exit (1);
}
if (!net_reactivated_Fprint_Device_claim(dev, username, &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 dev;
return g_steal_pointer (&dev);
}
static void enroll_result(GObject *object, const char *result, gboolean done, void *user_data)
@ -89,16 +93,30 @@ static void enroll_result(GObject *object, const char *result, gboolean done, vo
*enroll_completed = TRUE;
}
static void do_enroll(DBusGProxy *dev)
static void proxy_signal_cb (GDBusProxy *proxy,
const gchar *sender_name,
const gchar *signal_name,
GVariant *parameters,
gpointer user_data)
{
GError *error = NULL;
if (g_str_equal (signal_name, "EnrollStatus")) {
const gchar *result;
gboolean done;
g_variant_get (parameters, "(&sb)", &result, &done);
enroll_result (G_OBJECT (proxy), result, done, user_data);
}
}
static void do_enroll (FprintDBusDevice *dev)
{
g_autoptr(GError) error = NULL;
gboolean enroll_completed = FALSE;
gboolean found;
guint i;
dbus_g_proxy_add_signal(dev, "EnrollStatus", G_TYPE_STRING, G_TYPE_BOOLEAN, NULL);
dbus_g_proxy_connect_signal(dev, "EnrollStatus", G_CALLBACK(enroll_result),
&enroll_completed, NULL);
g_signal_connect (dev, "g-signal", G_CALLBACK (proxy_signal_cb),
&enroll_completed);
found = FALSE;
for (i = 0; fingers[i].dbus_name != NULL; i++) {
@ -108,7 +126,7 @@ static void do_enroll(DBusGProxy *dev)
}
}
if (!found) {
GString *s;
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);
@ -118,12 +136,12 @@ static void do_enroll(DBusGProxy *dev)
g_string_append (s, ", ");
}
g_warning ("%s", s->str);
g_string_free (s, TRUE);
exit (1);
}
g_print("Enrolling %s finger.\n", finger_name);
if (!net_reactivated_Fprint_Device_enroll_start(dev, finger_name, &error)) {
if (!fprint_dbus_device_call_enroll_start_sync (dev, finger_name, NULL,
&error)) {
g_print("EnrollStart failed: %s\n", error->message);
exit (1);
}
@ -131,19 +149,18 @@ static void do_enroll(DBusGProxy *dev)
while (!enroll_completed)
g_main_context_iteration(NULL, TRUE);
dbus_g_proxy_disconnect_signal(dev, "EnrollStatus",
G_CALLBACK(enroll_result), &enroll_completed);
g_signal_handlers_disconnect_by_func (dev, proxy_signal_cb, &enroll_result);
if (!net_reactivated_Fprint_Device_enroll_stop(dev, &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(DBusGProxy *dev)
static void release_device (FprintDBusDevice *dev)
{
GError *error = NULL;
if (!net_reactivated_Fprint_Device_release(dev, &error)) {
g_autoptr(GError) error = NULL;
if (!fprint_dbus_device_call_release_sync (dev, NULL, &error)) {
g_print("ReleaseDevice failed: %s\n", error->message);
exit (1);
}
@ -157,21 +174,17 @@ static const GOptionEntry entries[] = {
int main(int argc, char **argv)
{
g_autoptr(FprintDBusDevice) dev = NULL;
GOptionContext *context;
GError *err = NULL;
DBusGProxy *dev;
g_autoptr(GError) err = NULL;
setlocale (LC_ALL, "");
dbus_g_object_register_marshaller (fprintd_marshal_VOID__STRING_BOOLEAN,
G_TYPE_NONE, G_TYPE_STRING, G_TYPE_BOOLEAN, G_TYPE_INVALID);
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) {
g_print ("couldn't parse command-line options: %s\n", err->message);
g_error_free (err);
return 1;
}
@ -180,7 +193,7 @@ int main(int argc, char **argv)
create_manager();
dev = open_device(usernames ? usernames[0] : NULL);
dev = open_device (usernames ? usernames[0] : "");
do_enroll(dev);
release_device(dev);
g_free(finger_name);

View File

@ -1,6 +1,7 @@
/*
* fprintd example to list enrolled fingerprints
* Copyright (C) 2008 Daniel Drake <dsd@gentoo.org>
* 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
@ -20,113 +21,115 @@
#include <stdio.h>
#include <stdlib.h>
#include <locale.h>
#include <dbus/dbus-glib-bindings.h>
#include "manager-dbus-glue.h"
#include "device-dbus-glue.h"
#include "fprintd-dbus.h"
static DBusGProxy *manager = NULL;
static DBusGConnection *connection = NULL;
static FprintDBusManager *manager = NULL;
static GDBusConnection *connection = NULL;
static void create_manager(void)
{
GError *error = NULL;
g_autoptr(GError) error = NULL;
connection = dbus_g_bus_get(DBUS_BUS_SYSTEM, &error);
connection = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, &error);
if (connection == NULL) {
g_print("Failed to connect to session bus: %s\n", error->message);
exit (1);
}
manager = dbus_g_proxy_new_for_name(connection,
"net.reactivated.Fprint", "/net/reactivated/Fprint/Manager",
"net.reactivated.Fprint.Manager");
manager = fprint_dbus_manager_proxy_new_sync (connection,
G_DBUS_PROXY_FLAGS_NONE,
"net.reactivated.Fprint",
"/net/reactivated/Fprint/Manager",
NULL, &error);
if (manager == NULL) {
g_print ("Failed to get Fprintd manager: %s\n", error->message);
exit (1);
}
}
static void list_fingerprints(DBusGProxy *dev, const char *username)
static void list_fingerprints (FprintDBusDevice *dev, const char *username)
{
GError *error = NULL;
char **fingers;
GHashTable *props;
DBusGProxy *p;
g_autoptr(GError) error = NULL;
g_auto(GStrv) fingers = NULL;
guint i;
if (!net_reactivated_Fprint_Device_list_enrolled_fingers(dev, username, &fingers, &error)) {
if (dbus_g_error_has_name (error, "net.reactivated.Fprint.Error.NoEnrolledPrints") == FALSE) {
if (!fprint_dbus_device_call_list_enrolled_fingers_sync (dev, username,
&fingers, NULL,
&error)) {
gboolean ignore_error = FALSE;
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")) {
ignore_error = TRUE;
}
}
if (!ignore_error) {
g_print("ListEnrolledFingers failed: %s\n", error->message);
exit (1);
} else {
fingers = NULL;
}
}
p = dbus_g_proxy_new_from_proxy(dev, "org.freedesktop.DBus.Properties", NULL);
if (!dbus_g_proxy_call (p, "GetAll", &error, G_TYPE_STRING, "net.reactivated.Fprint.Device", G_TYPE_INVALID,
dbus_g_type_get_map ("GHashTable", G_TYPE_STRING, G_TYPE_VALUE), &props, G_TYPE_INVALID)) {
g_print("GetAll on the Properties interface failed: %s\n", error->message);
exit (1);
}
if (fingers == NULL || g_strv_length (fingers) == 0) {
g_print("User %s has no fingers enrolled for %s.\n", username, g_value_get_string (g_hash_table_lookup (props, "name")));
g_print ("User %s has no fingers enrolled for %s.\n", username,
fprint_dbus_device_get_name (dev));
return;
}
g_print("Fingerprints for user %s on %s (%s):\n",
username,
g_value_get_string (g_hash_table_lookup (props, "name")),
g_value_get_string (g_hash_table_lookup (props, "scan-type")));
g_hash_table_destroy (props);
g_object_unref (p);
fprint_dbus_device_get_name (dev),
fprint_dbus_device_get_scan_type (dev));
for (i = 0; fingers[i] != NULL; i++) {
g_print(" - #%d: %s\n", i, fingers[i]);
}
g_strfreev (fingers);
}
static void process_devices(char **argv)
{
GError *error = NULL;
GPtrArray *devices;
g_auto(GStrv) devices = NULL;
g_autoptr(GError) error = NULL;
char *path;
guint num_devices;
guint i;
if (!net_reactivated_Fprint_Manager_get_devices(manager, &devices, &error)) {
g_print("list_devices failed: %s\n", error->message);
if (!fprint_dbus_manager_call_get_devices_sync (manager, &devices, NULL,
&error)) {
g_print("Impossible to get devices: %s\n", error->message);
exit (1);
}
if (devices->len == 0) {
g_print("No devices found\n");
num_devices = g_strv_length (devices);
if (num_devices == 0) {
g_print("No devices available\n");
exit(1);
}
g_print("found %d devices\n", devices->len);
for (i = 0; i < devices->len; i++) {
path = g_ptr_array_index(devices, i);
g_print ("found %u devices\n", num_devices);
for (i = 0; devices[i] != NULL; i++) {
path = devices[i];
g_print("Device at %s\n", path);
}
for (i = 0; i < devices->len; i++) {
for (i = 0; devices[i] != NULL; i++) {
g_autoptr(FprintDBusDevice) dev = NULL;
guint j;
DBusGProxy *dev;
path = g_ptr_array_index(devices, i);
path = devices[i];
g_print("Using device %s\n", path);
/* FIXME use for_name_owner?? */
dev = dbus_g_proxy_new_for_name(connection, "net.reactivated.Fprint",
path, "net.reactivated.Fprint.Device");
/* NOTE: We should handle error cases! */
dev = fprint_dbus_device_proxy_new_sync (connection,
G_DBUS_PROXY_FLAGS_NONE,
"net.reactivated.Fprint",
path, NULL, NULL);
for (j = 1; argv[j] != NULL; j++)
list_fingerprints (dev, argv[j]);
g_object_unref (dev);
}
g_ptr_array_foreach(devices, (GFunc) g_free, NULL);
g_ptr_array_free(devices, TRUE);
}
int main(int argc, char **argv)

View File

@ -1,52 +1,19 @@
dbus_client_glue_sources = []
foreach interface_name: dbus_interfaces
interface = interface_name.to_lower()
interface_file = meson.source_root() / 'src' / interface + '.xml'
glue_name = interface + '-dbus-glue.h'
dbus_client_glue_sources += custom_target(glue_name,
input: interface_file,
output: glue_name,
command: [
dbus_binding_tool,
'--prefix=fprint_' + interface,
'--mode=glib-client',
'--output=@OUTPUT@',
'@INPUT@',
])
endforeach
utils_marshal = custom_target('utils_marshal',
depends: fprintd_marshal,
input: fprintd_marshal,
output: ['marshal.c', 'marshal.h'],
command: [bash, '-c',
'cp @INPUT0@ @OUTPUT0@;' +
'cp @INPUT1@ @OUTPUT1@;' +
'sed s/fprintd-//g -i ' + meson.current_build_dir() / 'marshal*.{h,c}']
)
libfprintd_utils_dep = declare_dependency(
include_directories: [
include_directories('../src'),
include_directories('../pam'),
],
dependencies: [
glib_dep,
dbus_glib_dep,
gio_dep,
gio_unix_dep,
],
sources: [
utils_marshal,
dbus_client_glue_sources,
fprintd_dbus_sources,
],
link_with: [
libfprintd_private
],
link_with: static_library('fprintd_utils',
sources: [
dbus_client_glue_sources,
utils_marshal,
],
dependencies: [
glib_dep,
]
),
)
utils = [

View File

@ -1,6 +1,7 @@
/*
* fprintd example to verify a fingerprint
* Copyright (C) 2008 Daniel Drake <dsd@gentoo.org>
* 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
@ -21,71 +22,77 @@
#include <stdlib.h>
#include <string.h>
#include <locale.h>
#include <dbus/dbus-glib-bindings.h>
#include "manager-dbus-glue.h"
#include "device-dbus-glue.h"
#include "marshal.h"
#include <gio/gio.h>
#include "fprintd-dbus.h"
static DBusGProxy *manager = NULL;
static DBusGConnection *connection = NULL;
static FprintDBusManager *manager = NULL;
static GDBusConnection *connection = NULL;
static char *finger_name = NULL;
static gboolean g_fatal_warnings = FALSE;
static char **usernames = NULL;
static void create_manager(void)
{
GError *error = NULL;
g_autoptr(GError) error = NULL;
connection = dbus_g_bus_get(DBUS_BUS_SYSTEM, &error);
connection = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, &error);
if (connection == NULL) {
g_print("Failed to connect to session bus: %s\n", error->message);
exit (1);
}
manager = dbus_g_proxy_new_for_name(connection,
"net.reactivated.Fprint", "/net/reactivated/Fprint/Manager",
"net.reactivated.Fprint.Manager");
}
static DBusGProxy *open_device(const char *username)
{
GError *error = NULL;
gchar *path;
DBusGProxy *dev;
if (!net_reactivated_Fprint_Manager_get_default_device(manager, &path, &error)) {
g_print("list_devices failed: %s\n", error->message);
manager = fprint_dbus_manager_proxy_new_sync (connection,
G_DBUS_PROXY_FLAGS_NONE,
"net.reactivated.Fprint",
"/net/reactivated/Fprint/Manager",
NULL, &error);
if (manager == NULL) {
g_print ("Failed to get Fprintd manager: %s\n", error->message);
exit (1);
}
if (path == NULL) {
g_print("No devices found\n");
exit(1);
}
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)) {
g_print("Impossible to verify: %s\n", error->message);
exit (1);
}
g_print("Using device %s\n", path);
/* FIXME use for_name_owner?? */
dev = dbus_g_proxy_new_for_name(connection, "net.reactivated.Fprint",
path, "net.reactivated.Fprint.Device");
g_free (path);
dev = fprint_dbus_device_proxy_new_sync (connection,
G_DBUS_PROXY_FLAGS_NONE,
"net.reactivated.Fprint",
path, NULL, &error);
if (!net_reactivated_Fprint_Device_claim(dev, username, &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)) {
g_print("failed to claim device: %s\n", error->message);
exit (1);
}
return dev;
return g_steal_pointer (&dev);
}
static void find_finger(DBusGProxy *dev, const char *username)
static void find_finger (FprintDBusDevice *dev, const char *username)
{
GError *error = NULL;
char **fingers;
g_autoptr(GError) error = NULL;
g_auto(GStrv) fingers = NULL;
guint i;
if (!net_reactivated_Fprint_Device_list_enrolled_fingers(dev, username, &fingers, &error)) {
if (!fprint_dbus_device_call_list_enrolled_fingers_sync (dev, username,
&fingers,
NULL, &error)) {
g_print("ListEnrolledFingers failed: %s\n", error->message);
exit (1);
}
@ -100,12 +107,17 @@ static void find_finger(DBusGProxy *dev, const char *username)
g_print(" - #%d: %s\n", i, fingers[i]);
}
if (finger_name == NULL || strcmp (finger_name, "any") == 0) {
if (finger_name && !g_str_equal (finger_name, "any") &&
!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);
finger_name = g_strdup (fingers[0]);
exit(1);
}
g_strfreev (fingers);
if (finger_name == NULL) {
finger_name = g_strdup (fingers[0]);
}
}
static void verify_result(GObject *object, const char *result, gboolean done, void *user_data)
@ -121,19 +133,36 @@ static void verify_finger_selected(GObject *object, const char *name, void *user
g_print("Verifying: %s\n", name);
}
static void do_verify(DBusGProxy *dev)
static void proxy_signal_cb (GDBusProxy *proxy,
const gchar *sender_name,
const gchar *signal_name,
GVariant *parameters,
gpointer user_data)
{
GError *error = NULL;
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")) {
const gchar *name;
g_variant_get (parameters, "(&s)", &name);
verify_finger_selected (G_OBJECT (proxy), name, user_data);
}
}
static void do_verify (FprintDBusDevice *dev)
{
g_autoptr(GError) error = NULL;
gboolean verify_completed = FALSE;
dbus_g_proxy_add_signal(dev, "VerifyStatus", G_TYPE_STRING, G_TYPE_BOOLEAN, NULL);
dbus_g_proxy_add_signal(dev, "VerifyFingerSelected", G_TYPE_INT, NULL);
dbus_g_proxy_connect_signal(dev, "VerifyStatus", G_CALLBACK(verify_result),
&verify_completed, NULL);
dbus_g_proxy_connect_signal(dev, "VerifyFingerSelected", G_CALLBACK(verify_finger_selected),
NULL, NULL);
g_signal_connect (dev, "g-signal", G_CALLBACK (proxy_signal_cb),
&verify_completed);
if (!net_reactivated_Fprint_Device_verify_start(dev, finger_name, &error)) {
if (!fprint_dbus_device_call_verify_start_sync (dev, finger_name, NULL,
&error)) {
g_print("VerifyStart failed: %s\n", error->message);
exit (1);
}
@ -141,19 +170,20 @@ static void do_verify(DBusGProxy *dev)
while (!verify_completed)
g_main_context_iteration(NULL, TRUE);
dbus_g_proxy_disconnect_signal(dev, "VerifyStatus", G_CALLBACK(verify_result), &verify_completed);
dbus_g_proxy_disconnect_signal(dev, "VerifyFingerSelected", G_CALLBACK(verify_finger_selected), NULL);
if (!net_reactivated_Fprint_Device_verify_stop(dev, &error)) {
g_signal_handlers_disconnect_by_func (dev, proxy_signal_cb,
&verify_completed);
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(DBusGProxy *dev)
static void release_device (FprintDBusDevice *dev)
{
GError *error = NULL;
if (!net_reactivated_Fprint_Device_release(dev, &error)) {
g_autoptr(GError) error = NULL;
if (!fprint_dbus_device_call_release_sync (dev, NULL, &error)) {
g_print("ReleaseDevice failed: %s\n", error->message);
exit (1);
}
@ -168,22 +198,18 @@ static const GOptionEntry entries[] = {
int main(int argc, char **argv)
{
g_autoptr(FprintDBusDevice) dev = NULL;
g_autoptr(GError) err = NULL;
GOptionContext *context;
GError *err = NULL;
DBusGProxy *dev;
const char *username = NULL;
setlocale (LC_ALL, "");
dbus_g_object_register_marshaller (fprintd_marshal_VOID__STRING_BOOLEAN,
G_TYPE_NONE, G_TYPE_STRING, G_TYPE_BOOLEAN, G_TYPE_INVALID);
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) {
g_print ("couldn't parse command-line options: %s\n", err->message);
g_error_free (err);
return 1;
}