129 Commits

Author SHA1 Message Date
580cceb50e 1.90.1 2020-02-10 14:59:42 +01:00
b90b21f26b build: Make pam module installation dir configurable
And avoid treating "libdir" as an absolute path, the documentation
clearly states that it is "relative to the prefix".

Based on patch by Timothy Gu <timothygu99@gmail.com>
2020-02-10 14:49:11 +01:00
6d583cb5d8 ci: List dependencies on separate lines
This improves readability of additions and removals.
2020-02-07 17:23:40 +01:00
c776068cd2 ci: Build a docker image with dependencies and use it
Include the wayland ci-templates to be able to easily generate an image
2020-02-07 17:23:32 +01:00
b8d80fcb35 ci: Include libfprint templates to sync with its dependencies
Use libfprint templates in order to get its dependencies without having to
manually keep a list of them in sync
2020-02-06 22:07:27 +01:00
32c2ccdd8c ci: Print coverage data once available so that gitlab can parse it 2020-02-06 11:30:23 +00:00
58784f7002 doc: Fix gtk-doc generation
Closes: #44
2020-02-06 12:21:51 +01:00
f295e6c571 ci: Test gtk-doc build 2020-02-06 12:08:04 +01:00
b02825620a Revert "build: Ensure that gcov symbols are exposed when needed"
This reverts commit 526b2e8c53.

Commit 0994cc31 was enough to implement the coverage support.
2020-02-05 17:09:19 +01:00
2327307b81 build: Rename config.h template
Now that it won't clash with the autotools version.
2020-02-05 17:07:10 +01:00
7c1ae363a8 build: Remove ChangeLog
It's empty and was only there to satisfy the autotools.
2020-02-05 17:05:32 +01:00
a18af36a03 build: Remove INSTALL file
It was an autotools file, and doesn't contain useful information
anymore.
2020-02-05 17:05:08 +01:00
526b2e8c53 build: Ensure that gcov symbols are exposed when needed
When coverage is enabled, we need to expose the __gcov_* symbols in the
binaries and libraries or we won't get any coverage report for them.
2020-02-05 16:54:54 +01:00
73625233f6 build: Remove autotools support 2020-02-05 16:54:54 +01:00
08de7e33a4 ci: Rename jobs to remove meson from the names 2020-02-05 16:45:57 +01:00
c871dfc998 ci: Remove autotools targets 2020-02-05 16:45:57 +01:00
dccc5796b6 ci: Enable coverage reports in test build 2020-02-05 16:45:57 +01:00
0994cc314e main: Ensure that a gcov flush happens on SIGTERM
When coverage is enabled fprintd test won't generate any .gcda file and so
apparently no data, this happens because gcov doesn't handle properly the
process termination when SIGTERM is used, and so when in fprintd.py we
terminate the process no coverage data is reported.

To avoid this, quit the main loop cleanly on SIGTERM, so that we will exit
from the main function cleanly, making libc to perform a gcov flush when we
exit the program.
2020-02-05 16:45:57 +01:00
09b1f1f1db ci: Compile with --werror in build_dev 2020-02-05 15:25:35 +01:00
e10417a907 verify: Constify username
utils/verify.c:191:12: warning: assignment discards ‘const’ qualifier from pointer target type [-Wdiscarded-qualifiers]
191 |   username = "";
    |            ^
2020-02-05 15:25:35 +01:00
8f75563920 enroll: Fix usernames memory leak
usernames was not freed on exit.
2020-02-05 15:25:35 +01:00
2e00c7a4b1 enroll: Always use allocated memory for finger name
Given that finger_name is set by GOptionEntry, make sure it's always
using allocated memory.

../utils/enroll.c:38:28: error: initialization discards ‘const’ qualifier
  from pointer target type [-Werror=discarded-qualifiers]
38 | static char *finger_name = "right-index-finger";
   |                            ^~~~~~~~~~~~~~~~~~~~
2020-02-05 15:25:35 +01:00
72d8a0f512 build: Enable -Werror on redundant declarations
Now that we've fixed our own errors.
2020-02-05 15:25:35 +01:00
130d6cdb63 main: Move fprintd_dbus_conn declaration to main.c
There's no need to declare it as extern in the header as it is already
declared in the source files where it's used.

Fixes:
../src/device.c:51:25: error: redundant redeclaration of ‘fprintd_dbus_conn’ [-Werror=redundant-decls]
  51 | extern DBusGConnection *fprintd_dbus_conn;
     |                         ^~~~~~~~~~~~~~~~~
In file included from ../src/device.c:34:
../src/fprintd.h:29:25: note: previous declaration of ‘fprintd_dbus_conn’ was here
  29 | extern DBusGConnection *fprintd_dbus_conn;
     |                         ^~~~~~~~~~~~~~~~~
2020-02-05 15:25:35 +01:00
e2fd52190a main: Fix "function declaration isn't a prototype" warning
../src/file_storage.c:47:20: warning: function declaration isn’t a prototype [-Wstrict-prototypes]
47 | static const char *get_storage_path()
   |                    ^~~~~~~~~~~~~~~~
../src/file_storage.c: In function ‘get_storage_path’:
../src/file_storage.c:47:20: warning: old-style function definition [-Wold-style-definition]
../src/file_storage.c: In function ‘file_storage_discover_users’:
../src/file_storage.c:270:9: warning: old-style function definition [-Wold-style-definition]
270 | GSList *file_storage_discover_users()
    |         ^~~~~~~~~~~~~~~~
2020-02-05 15:25:35 +01:00
7d8450e5ab device: Mark fingers names array as const and use unique name
With the stronger warnings enabled when building with meson, we get a
warning for all the fingers definitions:

  ../src/device.c:38:24: warning: initialization discards ‘const’ qualifier
  from pointer target type [-Wdiscarded-qualifiers]
    38 |  [FP_FINGER_UNKNOWN] = "unknown",

As the `fingers` array name was shadowed in another file:

  ../src/device.c:1000:11: warning: declaration of ‘fingers’ shadows a
  global declaration [-Wshadow]
    1000 |   GSList *fingers, *finger;
2020-02-05 15:25:35 +01:00
95e95d2910 main: Fix "function declaration isn't a prototype" warning 2020-02-05 15:25:35 +01:00
75aeff4acd tests: Increase debugging level for PAM tests 2020-02-05 15:25:35 +01:00
bebd3d2a98 tests: Increase debugging for daemon tests 2020-02-05 15:25:35 +01:00
0e2bf7e804 build: Check translations using a script
Use a bash script to check the translations as we used to do during
the autotools-based distcheck.
2020-02-05 15:25:35 +01:00
f5a2c21f90 build: Add configuration recap message for meson 2020-02-05 15:25:35 +01:00
53d80c1474 build: Use gettext to translate policy file with meson
Recent versions of gettext can merge translations in xml files,
so use it and remove the intltool dependency for meson builds.
2020-02-05 15:25:35 +01:00
9bed3bed3f ci: Add jobs to build and run tests using meson 2020-02-05 15:25:35 +01:00
1a094c1a09 ci: Use a template to avoid repeating the libfprint-dev installation 2020-02-05 15:25:35 +01:00
4ed46deaf2 build: Add script to update translations 2020-02-05 15:25:35 +01:00
0724413eda README: Clean up transifex README 2020-02-05 15:25:35 +01:00
eb6dbb6953 build: Add meson build system
Reuse the generated dbus interface .xml files from fprintd to avoid
unnecessary copies.
2020-02-05 15:25:35 +01:00
5c5849dca7 build: Don't hardcode libtool specific build path
Don't hardcode the libtool specific build path in the configuration
file, but in the build declaration instead.
2020-02-05 13:43:27 +01:00
048181ae7f test_fprintd_utils: Support running under valgrind
Like for the fprintd test, run the tests under valgrind if the `VALGRIND`
environment variable is set, and use the contents of the variable as the
path to the suppression file.
2020-02-04 15:39:01 +01:00
8e3345a60f tests: Fix num-enroll-stages signature in dbusmock template
It's marked as a signed integer, not an unsigned one in the API.
2020-02-04 15:38:17 +01:00
33a21b5089 tests/fprintd: Test error conditions on Claim()/Release() 2020-02-04 15:36:06 +01:00
dd006382f5 tests/fprintd: Test ListEnrolledFingers() method 2020-02-04 15:33:09 +01:00
a34432ccf9 tests/fprintd: Add assertFprintError to check errors 2020-02-04 15:28:57 +01:00
f2804921a3 tests/fprintd: Use unittest assertions more often 2020-02-04 15:27:24 +01:00
74577a6806 tests/fprintd: Add a FPrintdVirtualDeviceTest to avoid repeating setup 2020-02-04 15:21:50 +01:00
2076025208 device: Use session data for context, sender and user
Use the device session data to store all the informations we care about
while a device is claimed, and make its cleanup easier.

Keep just one instance of the current context, given we use it only during
claim and release, and those are mutually exclusive operations.
2020-02-04 14:54:20 +01:00
77126ccf1f device: Remove unused storage_type variable 2020-02-04 14:40:17 +01:00
46b7d7c1a7 device: Clear username on open failure 2020-02-04 14:36:39 +01:00
791a208afd device: Clear session, sender and username on close failure 2020-02-04 14:31:51 +01:00
b832002c97 device: Use malloc-allocated SessionData
Allocate SessionData using g_malloc(). There are no benefits to using
GSlice for a seldom used structure. This also allows use to use
g_clear_pointer() to free the struct.
2020-02-04 14:30:10 +01:00
8a2f276003 device: Cleanup session_data on open failure 2020-02-04 14:25:00 +01:00
aab923e3cd device: Fix memory leak on object destruction
Add the missing chain-up on finalize().
2020-02-04 14:22:01 +01:00
cc4bac3410 main: Use G_DECLARE_FINAL_TYPE to define objet 2020-02-04 14:19:41 +01:00
18d4aa6d7d pam: Use systemd to check whether a session is remote
Closes: #3
2020-02-04 14:01:29 +01:00
046ec8dbf4 pam: Split off remote session code 2020-02-04 14:01:29 +01:00
874b4556ef pam: Remove unneeded assignments
Don't use return messages for D-Bus calls when we're not interested in
those return values.
2020-02-04 14:01:29 +01:00
c2aa5c0b9a pam: Fix possible memory leak
If we exited early from the loop, we need to make sure to free() this
struct member.
2020-02-04 13:48:25 +01:00
f745c49112 pam: Fix PAM authentication when it's the last try
We need to make sure that the max_tries variable isn't decremented
further when we have success in the verification loop. Add missing break
to do that.

Fixes: affffaf134
Closes: #40
2020-02-04 13:48:00 +01:00
319d859107 tests: Add PAM test that succeeds on last try
Exhaust all the tries (minus 1) before having a match on the last try.
This should not throw a warning.
2020-02-04 13:35:48 +01:00
b97903f781 data: Update built man pages
As used in the dist tarball.
2020-01-24 17:03:43 +01:00
a30697a28d data: Add pam_fprintd man page
Closes: #24
2020-01-24 17:03:43 +01:00
aad3212d22 data: Update links to other man pages 2020-01-24 17:03:43 +01:00
3624bcb114 data: Fix copy/paste error in fprintd-verify's man page 2020-01-24 17:03:43 +01:00
8fbc59a258 pam: Allow unloading libs that pam_fprintd is linked to
Remove "nodelete" linker flag now that we use sd-bus and not dbus-glib,
so that libraries that pam_fprintd links to can be unloaded.

This was added because GLib's type system expects to be initialised
once and only once per process, and re-loading this type system when it
had already been initialised caused crashes.
2020-01-24 17:03:43 +01:00
5a8da0022a tests: Make warnings fatal in PAM tests
This will catch problems with GLib being unloaded when the PAM module is
unloaded, which would have crashed when using dbus-glib.

This serves as a test for https://gitlab.freedesktop.org/libfprint/fprintd/issues/2

Closes: #2
2020-01-24 17:03:32 +01:00
6089ba6f40 pam: Better debug when timeout is invalid 2020-01-24 00:51:13 +01:00
ee6e8a6fa3 pam: Better debug when max_tries option is invalid 2020-01-24 00:51:13 +01:00
42f5280a03 pam: Better debug arguments support
Support debug=[on|off|true|false|1|0] as an option in addition to
"debug".
2020-01-24 00:51:13 +01:00
affffaf134 pam: Return PAM_MAXTRIES after too many tries 2020-01-24 00:51:13 +01:00
6ab270fb1a tests: Reduce the default timeout in tests 2020-01-24 00:51:13 +01:00
2d0bed6b13 tests: Add PAM timeout test 2020-01-24 00:51:13 +01:00
c43134e36f tests: Add PAM test with 2 readers 2020-01-24 00:51:13 +01:00
b9d23ddb87 tests: Fix dbusmock template when adding > 1 reader 2020-01-24 00:51:13 +01:00
205dedae4f tests: Add dbus-monitor'ing to PAM tests
Should prove useful debugging the PAM module if we need it.
2020-01-24 00:51:13 +01:00
d70f15b5e8 pam: Port to sd-bus
This pam plugin never used GDBus because it transparently uses threads
which do not work well with a lot of PAM applications. But even settling
on the "still better to use than plain dbus library" dbus-glib wasn't
without problems, as any use or initialisation of GIO sockets would
modify signal handler for signals such as SIGPIPE (see gio/gsocket.c).

Many years later, sd-bus is a more modern alternative to the bare dbus
library with a better API.

This includes:
- Removing use of gboolean, guint, g_new0() and many glib string helpers
- Simplifying debug logging
- Marking user-facing messages to be translated
2020-01-24 00:51:13 +01:00
6f63beb1fc pam: Add str_has_prefix() helper
To simplify the options parsing.
2020-01-23 18:45:43 +01:00
f7557c6ee7 pam: Add monotonic clock helper 2020-01-23 18:45:43 +01:00
0b598965b0 pam: Remove GLib usage from copy/paste header 2020-01-23 18:45:43 +01:00
8325d347d6 pam: Add str_equal() helper to copy/paste header 2020-01-23 18:45:43 +01:00
0bdf801043 pam: Add helper to replace G_GNUC_UNUSED to copy/paste header 2020-01-23 18:45:43 +01:00
6e3b053372 pam: Add include for bool to copy/paste header 2020-01-23 18:45:43 +01:00
c6e72c5f28 pam: Add include for asprintf to copy/paste header 2020-01-23 18:45:43 +01:00
986a42bcd1 pam: Update copyright notice 2020-01-23 18:45:43 +01:00
0c6bab8640 main: Fix redeclaration linking error
Fix linking error as the "store" global variable gets redeclared in
each C file that includes the header. Move the actual declaration to
main.c.

Fixes:
 /usr/bin/ld: ./.libs/libfprintd.a(device.o):/builds/libfprint/fprintd/src/storage.h:51: multiple definition of `store'; main.o:/builds/libfprint/fprintd/src/storage.h:51: first defined here
2020-01-23 18:25:25 +01:00
b99afd19f0 main: Use #pragma once 2020-01-23 18:25:18 +01:00
3d6dfabd8d Revert "ci: Temporarily add newer pam_wrapper build"
This reverts commit db0ab55bef.
2020-01-23 18:25:14 +01:00
67adcb59ed configure.ac: Depend on libfprint-2
libfprint changed SONAME to completely break with previous version, so
update fprintd dependency accordingly.
2020-01-23 18:24:53 +01:00
db0ab55bef ci: Temporarily add newer pam_wrapper build
So that the test suite can pass.
2020-01-22 15:34:02 +01:00
00b79d1a2f tests: Add test for the PAM module
Test the PAM module using pam_wrapper and our mock fprintd.

See https://lwn.net/Articles/671094/

Note that this requires a version of pam_wrapper with this bug fixed:
https://bugzilla.samba.org/show_bug.cgi?id=14245
2020-01-22 15:34:02 +01:00
f1517af09a tests: Add fprintd-verify test that uses scripting 2020-01-20 17:31:46 +01:00
c0bf1515fd tests: Check that verify test doesn't succeed early 2020-01-20 17:31:08 +01:00
872089883c tests: Add scripting capabilities to the verification process
Add scripting capabilities to the verification process so that the mock
daemon can send its own results without needing the client to prod it to
do that. This is incredibly useful when the client is single threaded
and blocking.

Note that there are barely any safeguards, so the scripting task is not
cancelled if an error occurs, or the VerifyStatus signals are sent out
of order.
2020-01-20 17:26:24 +01:00
431755becd tests: Add test for fprintd utils 2020-01-17 15:32:22 +01:00
0a42b90390 tests: Add dbusmock template for fprintd daemon 2020-01-17 15:18:43 +01:00
cd3ed2e450 device: Fix documentation for ERROR_INVALID_FINGERNAME 2020-01-17 14:41:22 +01:00
c929d39df1 device: Fix "enrollemnt" typo 2020-01-17 14:40:36 +01:00
1a5ef6c5a7 build: Add separate dependency for pam module 2020-01-14 15:52:21 +01:00
ce3406b20f main: Fix typos in comments 2020-01-14 14:00:56 +01:00
0d407db171 manager: Fix typo in comment 2020-01-14 14:00:56 +01:00
4eb751a218 data: Fix typos in man pages 2020-01-14 14:00:56 +01:00
0f44267ea1 README.transifex: Fix typo 2020-01-14 14:00:56 +01:00
9baea4494b device: Replace deprecated g_type_class_add_private() 2020-01-14 14:00:56 +01:00
41afbd1ced device: Simplify FprintDevicePrivate declaration 2020-01-14 14:00:56 +01:00
f2d6921b74 manager: Replace deprecated g_type_class_add_private() 2020-01-14 14:00:56 +01:00
b690daa95f all: Call setlocale() at the start of main()
This fixes some broken characters in the fprintd debug output.
2020-01-14 13:51:46 +01:00
deb3c25e51 device: Adjust to new libfprint API for early match reporting
This API was added to libfprint to allow drivers to report the match
result early before the operation has been completed. No driver makes
use of this facility yet and instead drivers try to finish the
operation early for quick result reporting. This primarily means not
waiting for finger removal.

Once drivers are updated, fprintd reactivity will regress unless the
early match callback is implemented as they would only get an operation
finished callback when the whole of the operation was finished,
including finger removal and finishing up USB communications.

See: https://gitlab.freedesktop.org/libfprint/fprintd/issues/35
2020-01-14 13:41:57 +01:00
a520896325 device: Use FP_FINGER_IS_VALID to check finger number 2019-12-19 14:29:04 +01:00
707ed01059 file_storage: Remove definition of FP_FINGER_IS_VALID
This is now provided by libfprint
2019-12-19 14:28:35 +01:00
6903c36157 file-storage: Use first/last fingers references instead of named ones
Don't depend in the hardcoded libfprint fingers order, but use instead the
aliases for first/last fingers in libfprint order
2019-12-18 17:34:04 +01:00
d0df422f9b all: Bump required glib version
Require the same version of glib as libfprint, and remove support for
very old versions.
2019-12-18 17:03:37 +01:00
882740f8a1 utils: Use new print deletion API
Use the new API that works by claiming the device and then deleting the
prints.

Fixes: #26
2019-12-05 17:54:21 +01:00
5043ef3c7d device: Print warnings for fatal errors 2019-12-04 17:03:53 +01:00
cdd79a0935 ci: Remove libfprint dependencies that are not needed
The dependency list of libfprint used to be a direct copy of the
libfprint CI list. However, many of the dependencies are not needed as
only a minimal version of libfprint is built for testing purposes.
2019-12-03 17:08:04 +01:00
776b4f4cec device: Log offending API user if DeleteEnrolledFinger is used
When the DeleteEnrolledFinger API is used, log an additional warning
with the command that made the call.
2019-12-03 17:08:04 +01:00
31bfd1b055 ci: Run make check in the test stage 2019-12-03 17:08:04 +01:00
74838f9efc tests: Add basic integration test
This test uses the virtual image driver included in libfprint for
testing.
2019-12-03 17:08:04 +01:00
54ba81191e tests: Add test prints from libfprint
These can be freely redistributed as they are in the public domain. See
the included README.
2019-12-03 17:08:03 +01:00
521ba9b124 storage: Use $STATE_DIRECTORY when available
The state directory will generally be the same as the hardcoded one.
However, being able to override it is important for testing purposes, so
add the option.
2019-12-03 17:08:03 +01:00
3db69c2c2f utils: Move test binaries into utils
These utilities are generally useful beyond only testing purproses. And,
since it is desirable to have automated tests inside the tests
subdirecty, it makes sense to move them elsewhere.
2019-12-03 17:08:03 +01:00
b2ff316e20 device: Add new API and fallback to delete prints from device
Some devices require storing the print on the device, to support this,
try deleting prints from the device before deleting them from local
storage.

To handle these devices, add a new API that requires the device to be
claimed rather than allowing deletion without claiming the device first.
Also add appropriate fallbacks so that the old API will continue to
work, but warn about its use.
2019-12-03 17:08:03 +01:00
94f54c0638 main: Register the common name after initializing the manager
When creating the FprintManager object the devices will be enumerated.
This operation calls the mainloop recursively. We do not want to receive
any client requests before the initial enumeration has happened. Because
of this, move the registration of the common name to happen after the
enumeration has finished.
2019-12-03 17:08:03 +01:00
9c8d062669 device: Handle full device storage including garbage collection
The new libfprint version has support for devices that store data on the
sensor. In that case, the on-sensor storage might fill up when the user
tries to enroll a new print.

The strategy introduced here to handle this is to try and delete prints
from the device that we do not know about (assuming, it is e.g. from an
old installation and unusable).

It can also happen that we are not able to garbage collect old prints.
If that happens, a new error code "enroll-data-full" will be returned
signalling the situation to the enrolling application.
2019-12-03 17:08:03 +01:00
ca482036c7 storage: Add function to discover users that have prints
For sensors with internal storage we may want to garbage collect prints.
Adding this API means we can list all local prints, allowing us to find
out whether there are prints on the device's storage with no
corresponding print on the host.
2019-12-03 17:08:03 +01:00
d7821aa790 Initial port to libfprint2 2019-12-03 17:08:03 +01:00
16cb1a8ec6 device: Use g_clear_pointer in places
Replace some g_free calls with subsequent NULL setting with
g_clear_pointer.
2019-12-02 16:20:01 +01:00
31ba9ebea4 ci: libfprint master is now version two
libfprint master contains version 2 of the library now. This means we
need to add dependencies and change some other things a bit.
2019-12-02 16:19:55 +01:00
8893c2f906 ci: Build against libfprint-1-0
The libfprint master branch will soon contain the v2 API. So change to
use the libfprint-1-0 which will mean that the CI will continue to work.

Note that the build_stable target will need to be removed when the new
libfprint version reaches fedora rawhide.
2019-10-07 16:39:25 +02:00
3520cb56d1 build: Move D-Bus conf file to $(datadir)/dbus-1/system.d
Since D-Bus 1.9.18 configuration files installed by third-party should
go in share/dbus-1/system.d. The old location is for sysadmin overrides.
2019-09-16 11:43:51 -04:00
96444b4156 pam: Fix rhost string length check 2019-08-13 14:16:32 +02:00
69 changed files with 3717 additions and 2377 deletions

37
.gitignore vendored
View File

@ -1,46 +1,11 @@
/*.bak
/*.lo
/*.o
/*.orig
/*.rej
/*.tab.c
/*~
/.*.sw[nop]
/.deps
/.dirstamp
/.gitignore
/.libs
/GPATH
/GRTAGS
/GSYMS
/GTAGS
/ID
/Makefile
/Makefile.in
/TAGS
/_libs
/autom4te.cache
/config.cache
/config.h
/config.log
/config.lt
/config.status
/config.status.lineno
/configure
/configure.lineno
/intltool-extract.in
/intltool-merge.in
/intltool-update.in
/libtool
/po/*.gmo
/po/*.mo
/po/.intltool-merge-cache
/po/Makefile
/po/Makefile.in
/po/Makefile.in.in
/po/POTFILES
/po/fprintd.pot
/po/stamp-it
/so_locations
/stamp-h1
/_build
/tags

View File

@ -1,36 +1,98 @@
image: fedora:rawhide
include:
- project: 'libfprint/libfprint'
ref: master
file: '/.gitlab-ci/libfprint-templates.yaml'
- project: 'wayland/ci-templates'
ref: master
file: '/templates/fedora.yml'
variables:
DEPENDENCIES: dbus-glib-devel pam-devel polkit-devel
gtk-doc meson intltool autoconf automake libtool
gcc gcc-c++ glibc-devel make
DEPENDENCIES_STABLE: $DEPENDENCIES libfprint-devel
DEPENDENCIES_DEV: $DEPENDENCIES git
# Sync'ed up with https://gitlab.freedesktop.org/libfprint/libfprint/blob/master/.gitlab-ci.yml
DEPENDENCIES_LIBFPRINT: libusb1-devel glib2-devel nss-devel pixman-devel systemd
meson gtk-doc gcc gcc-c++ glibc-devel libX11-devel
libXv-devel
extends: .libfprint_common_variables
FEDORA_TAG: rawhide
FEDORA_VERSION: rawhide
FEDORA_IMAGE: "$CI_REGISTRY/libfprint/$CI_PROJECT_NAME/fedora/$FEDORA_VERSION:$FEDORA_TAG"
DEPENDENCIES: dbus-glib-devel
gcc
gcovr
gettext
git
glibc-devel
gtk-doc
libfprint-devel
meson
pam-devel
polkit-devel
python3-dbusmock
python3-libpamtest
systemd-devel
build_stable:
before_script:
- dnf update -y --nogpgcheck && dnf install -y --nogpgcheck $DEPENDENCIES_STABLE
script:
- ./autogen.sh --disable-dependency-tracking
- make
- make install
image: "$FEDORA_IMAGE"
build_dev:
.install_libfprint_dev: &install_libfprint_dev
before_script:
- dnf update -y --nogpgcheck && dnf install -y --nogpgcheck $DEPENDENCIES_LIBFPRINT $DEPENDENCIES_DEV
# Make sure we don't build or link against the system libfprint
- dnf remove -y libfprint-devel
- git clone https://gitlab.freedesktop.org/libfprint/libfprint.git
- cd libfprint
- meson . _build --prefix=/usr
- meson . _build --prefix=/usr -Ddrivers=virtual_image -Ddoc=false
- ninja -C _build
- ninja -C _build install
- cd ..
# So we don't get error about this libfprint file
- 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
script:
- ./autogen.sh --disable-dependency-tracking
- make
- make install
- meson _build
- ninja -C _build -v
- ninja -C _build -v install
build_dev:
except:
variables:
- $FPRINT_CRON_TASK == "BUILD_CI_IMAGES"
<<: *install_libfprint_dev
script:
- meson _build --werror -Dgtk_doc=true
- ninja -C _build -v
- ninja -C _build -v install
artifacts:
name: log
when: on_failure
paths:
- _build/meson-logs/*.txt
test_dev:
except:
variables:
- $FPRINT_CRON_TASK == "BUILD_CI_IMAGES"
stage: test
<<: *install_libfprint_dev
script:
- meson _build -Db_coverage=true
- meson test -C _build --verbose --no-stdsplit --timeout-multiplier 3
- ninja -C _build coverage
- cat _build/meson-logs/coverage.txt
artifacts:
name: log-and-coverage
when: always
paths:
- _build/meson-logs
# CONTAINERS creation stage
container_fedora_build:
extends: .fedora@container-build
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:
$DEPENDENCIES
$LIBFPRINT_DEPENDENCIES

View File

237
INSTALL
View File

@ -1,237 +0,0 @@
Installation Instructions
*************************
Copyright (C) 1994, 1995, 1996, 1999, 2000, 2001, 2002, 2004, 2005,
2006, 2007 Free Software Foundation, Inc.
This file is free documentation; the Free Software Foundation gives
unlimited permission to copy, distribute and modify it.
Basic Installation
==================
Briefly, the shell commands `./configure; make; make install' should
configure, build, and install this package. The following
more-detailed instructions are generic; see the `README' file for
instructions specific to this package.
The `configure' shell script attempts to guess correct values for
various system-dependent variables used during compilation. It uses
those values to create a `Makefile' in each directory of the package.
It may also create one or more `.h' files containing system-dependent
definitions. Finally, it creates a shell script `config.status' that
you can run in the future to recreate the current configuration, and a
file `config.log' containing compiler output (useful mainly for
debugging `configure').
It can also use an optional file (typically called `config.cache'
and enabled with `--cache-file=config.cache' or simply `-C') that saves
the results of its tests to speed up reconfiguring. Caching is
disabled by default to prevent problems with accidental use of stale
cache files.
If you need to do unusual things to compile the package, please try
to figure out how `configure' could check whether to do them, and mail
diffs or instructions to the address given in the `README' so they can
be considered for the next release. If you are using the cache, and at
some point `config.cache' contains results you don't want to keep, you
may remove or edit it.
The file `configure.ac' (or `configure.in') is used to create
`configure' by a program called `autoconf'. You need `configure.ac' if
you want to change it or regenerate `configure' using a newer version
of `autoconf'.
The simplest way to compile this package is:
1. `cd' to the directory containing the package's source code and type
`./configure' to configure the package for your system.
Running `configure' might take a while. While running, it prints
some messages telling which features it is checking for.
2. Type `make' to compile the package.
3. Optionally, type `make check' to run any self-tests that come with
the package.
4. Type `make install' to install the programs and any data files and
documentation.
5. You can remove the program binaries and object files from the
source code directory by typing `make clean'. To also remove the
files that `configure' created (so you can compile the package for
a different kind of computer), type `make distclean'. There is
also a `make maintainer-clean' target, but that is intended mainly
for the package's developers. If you use it, you may have to get
all sorts of other programs in order to regenerate files that came
with the distribution.
6. Often, you can also type `make uninstall' to remove the installed
files again.
Compilers and Options
=====================
Some systems require unusual options for compilation or linking that the
`configure' script does not know about. Run `./configure --help' for
details on some of the pertinent environment variables.
You can give `configure' initial values for configuration parameters
by setting variables in the command line or in the environment. Here
is an example:
./configure CC=c99 CFLAGS=-g LIBS=-lposix
*Note Defining Variables::, for more details.
Compiling For Multiple Architectures
====================================
You can compile the package for more than one kind of computer at the
same time, by placing the object files for each architecture in their
own directory. To do this, you can use GNU `make'. `cd' to the
directory where you want the object files and executables to go and run
the `configure' script. `configure' automatically checks for the
source code in the directory that `configure' is in and in `..'.
With a non-GNU `make', it is safer to compile the package for one
architecture at a time in the source code directory. After you have
installed the package for one architecture, use `make distclean' before
reconfiguring for another architecture.
Installation Names
==================
By default, `make install' installs the package's commands under
`/usr/local/bin', include files under `/usr/local/include', etc. You
can specify an installation prefix other than `/usr/local' by giving
`configure' the option `--prefix=PREFIX'.
You can specify separate installation prefixes for
architecture-specific files and architecture-independent files. If you
pass the option `--exec-prefix=PREFIX' to `configure', the package uses
PREFIX as the prefix for installing programs and libraries.
Documentation and other data files still use the regular prefix.
In addition, if you use an unusual directory layout you can give
options like `--bindir=DIR' to specify different values for particular
kinds of files. Run `configure --help' for a list of the directories
you can set and what kinds of files go in them.
If the package supports it, you can cause programs to be installed
with an extra prefix or suffix on their names by giving `configure' the
option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'.
Optional Features
=================
Some packages pay attention to `--enable-FEATURE' options to
`configure', where FEATURE indicates an optional part of the package.
They may also pay attention to `--with-PACKAGE' options, where PACKAGE
is something like `gnu-as' or `x' (for the X Window System). The
`README' should mention any `--enable-' and `--with-' options that the
package recognizes.
For packages that use the X Window System, `configure' can usually
find the X include and library files automatically, but if it doesn't,
you can use the `configure' options `--x-includes=DIR' and
`--x-libraries=DIR' to specify their locations.
Specifying the System Type
==========================
There may be some features `configure' cannot figure out automatically,
but needs to determine by the type of machine the package will run on.
Usually, assuming the package is built to be run on the _same_
architectures, `configure' can figure that out, but if it prints a
message saying it cannot guess the machine type, give it the
`--build=TYPE' option. TYPE can either be a short name for the system
type, such as `sun4', or a canonical name which has the form:
CPU-COMPANY-SYSTEM
where SYSTEM can have one of these forms:
OS KERNEL-OS
See the file `config.sub' for the possible values of each field. If
`config.sub' isn't included in this package, then this package doesn't
need to know the machine type.
If you are _building_ compiler tools for cross-compiling, you should
use the option `--target=TYPE' to select the type of system they will
produce code for.
If you want to _use_ a cross compiler, that generates code for a
platform different from the build platform, you should specify the
"host" platform (i.e., that on which the generated programs will
eventually be run) with `--host=TYPE'.
Sharing Defaults
================
If you want to set default values for `configure' scripts to share, you
can create a site shell script called `config.site' that gives default
values for variables like `CC', `cache_file', and `prefix'.
`configure' looks for `PREFIX/share/config.site' if it exists, then
`PREFIX/etc/config.site' if it exists. Or, you can set the
`CONFIG_SITE' environment variable to the location of the site script.
A warning: not all `configure' scripts look for a site script.
Defining Variables
==================
Variables not defined in a site shell script can be set in the
environment passed to `configure'. However, some packages may run
configure again during the build, and the customized values of these
variables may be lost. In order to avoid this problem, you should set
them in the `configure' command line, using `VAR=value'. For example:
./configure CC=/usr/local2/bin/gcc
causes the specified `gcc' to be used as the C compiler (unless it is
overridden in the site shell script).
Unfortunately, this technique does not work for `CONFIG_SHELL' due to
an Autoconf bug. Until the bug is fixed you can use this workaround:
CONFIG_SHELL=/bin/bash /bin/bash ./configure CONFIG_SHELL=/bin/bash
`configure' Invocation
======================
`configure' recognizes the following options to control how it operates.
`--help'
`-h'
Print a summary of the options to `configure', and exit.
`--version'
`-V'
Print the version of Autoconf used to generate the `configure'
script, and exit.
`--cache-file=FILE'
Enable the cache: use and save the results of the tests in FILE,
traditionally `config.cache'. FILE defaults to `/dev/null' to
disable caching.
`--config-cache'
`-C'
Alias for `--cache-file=config.cache'.
`--quiet'
`--silent'
`-q'
Do not print messages saying which checks are being made. To
suppress all normal output, redirect it to `/dev/null' (any error
messages will still be shown).
`--srcdir=DIR'
Look for the package's source code in directory DIR. Usually
`configure' can determine that directory automatically.
`configure' also accepts some other, not widely useful, options. Run
`configure --help' for more details.

View File

@ -1,29 +0,0 @@
AUTOMAKE_OPTIONS = dist-bzip2
SUBDIRS = src data tests pam doc po
EXTRA_DIST = TODO intltool-extract.in intltool-merge.in intltool-update.in
DISTCHECK_CONFIGURE_FLAGS = --enable-gtk-doc --with-systemdsystemunitdir='$${libdir}/systemd/system-distcheck'
all: check create-pot
check: check-translations
create-pot:
$(MAKE) -C po fprintd.pot
check-translations:
@for i in $(top_srcdir)/po/*.po ; do \
if ! grep -q `basename $$i | sed 's,.po,,'` $(top_srcdir)/po/LINGUAS ; then \
echo '**********************************'; \
echo '***' `basename $$i | sed 's,.po,,'` missing from po/LINGUAS '***' ; \
echo '**********************************'; \
exit 1; \
fi; \
done;
update-translations: create-pot check-translations
@tx -r $(srcdir) pull --all --force --skip
@if [ ! -d .tx ] ; then mkdir .tx ; fi ; if [ x$(srcdir) != x$(builddir) ] ; then cp -f $(srcdir)/.tx/config .tx/ ; fi
@tx push --source
-include $(top_srcdir)/git.mk

15
NEWS
View File

@ -1,6 +1,21 @@
This file lists notable changes in each release. For the full history of all
changes, see ChangeLog.
version 1.90.1:
- Add support for prints saved on the fingerprint device itself
- Add integration tests using the virtual image driver, and further
tests for the utilities
- Port build system to meson
- Loads of build warnings and memory leak fixes
- PAM module:
- Port PAM module to sd-bus from dbus-glib
- Use systemd to not ask for a fingerprint scan on remote logins
- Add man page for PAM module
- Add tests
This version requires libfprint 1.90.1, a 2.0 pre-release.
version 0.9.0:
- Fix hangs when there the verification error was "retry"
- Update for fp_get_pollfds() changes

View File

@ -1,7 +1,13 @@
Transifex.net Token Verification
=================================
Updating translations
=====================
The list of tokens bellow guarantee the respective users to be able to enable
The update-transifex.sh script should be run regularly to both pull
translations from the Transifex service, and push new strings to translate.
Transifex.net Token Verification
================================
The list of tokens below guarantee the respective users to be able to enable
submission on components using the following repository url:
https://gitlab.freedesktop.org/libfprint/fprintd/

View File

@ -1,182 +0,0 @@
dnl AS_AC_EXPAND(VAR, CONFIGURE_VAR)
dnl
dnl example
dnl AS_AC_EXPAND(SYSCONFDIR, $sysconfdir)
dnl will set SYSCONFDIR to /usr/local/etc if prefix=/usr/local
AC_DEFUN([AS_AC_EXPAND],
[
EXP_VAR=[$1]
FROM_VAR=[$2]
dnl first expand prefix and exec_prefix if necessary
prefix_save=$prefix
exec_prefix_save=$exec_prefix
dnl if no prefix given, then use /usr/local, the default prefix
if test "x$prefix" = "xNONE"; then
prefix=$ac_default_prefix
fi
dnl if no exec_prefix given, then use prefix
if test "x$exec_prefix" = "xNONE"; then
exec_prefix=$prefix
fi
full_var="$FROM_VAR"
dnl loop until it doesn't change anymore
while true; do
new_full_var="`eval echo $full_var`"
if test "x$new_full_var"="x$full_var"; then break; fi
full_var=$new_full_var
done
dnl clean up
full_var=$new_full_var
AC_SUBST([$1], "$full_var")
dnl restore prefix and exec_prefix
prefix=$prefix_save
exec_prefix=$exec_prefix_save
])
dnl GNOME_COMPILE_WARNINGS
dnl Turn on many useful compiler warnings
dnl For now, only works on GCC
AC_DEFUN([GNOME_COMPILE_WARNINGS],[
dnl ******************************
dnl More compiler warnings
dnl ******************************
AC_ARG_ENABLE(compile-warnings,
AC_HELP_STRING([--enable-compile-warnings=@<:@no/minimum/yes/maximum/error@:>@],
[Turn on compiler warnings]),,
[enable_compile_warnings="m4_default([$1],[yes])"])
warnCFLAGS=
if test "x$GCC" != xyes; then
enable_compile_warnings=no
fi
warning_flags=
realsave_CFLAGS="$CFLAGS"
case "$enable_compile_warnings" in
no)
warning_flags=
;;
minimum)
warning_flags="-Wall"
;;
yes)
warning_flags="-Wall -Wmissing-prototypes"
;;
maximum|error)
warning_flags="-Wall -Wmissing-prototypes -Wnested-externs -Wpointer-arith"
CFLAGS="$warning_flags $CFLAGS"
for option in -Wno-sign-compare; do
SAVE_CFLAGS="$CFLAGS"
CFLAGS="$CFLAGS $option"
AC_MSG_CHECKING([whether gcc understands $option])
AC_TRY_COMPILE([], [],
has_option=yes,
has_option=no,)
CFLAGS="$SAVE_CFLAGS"
AC_MSG_RESULT($has_option)
if test $has_option = yes; then
warning_flags="$warning_flags $option"
fi
unset has_option
unset SAVE_CFLAGS
done
unset option
if test "$enable_compile_warnings" = "error" ; then
warning_flags="$warning_flags -Werror"
fi
;;
*)
AC_MSG_ERROR(Unknown argument '$enable_compile_warnings' to --enable-compile-warnings)
;;
esac
CFLAGS="$realsave_CFLAGS"
AC_MSG_CHECKING(what warning flags to pass to the C compiler)
AC_MSG_RESULT($warning_flags)
AC_ARG_ENABLE(iso-c,
AC_HELP_STRING([--enable-iso-c],
[Try to warn if code is not ISO C ]),,
[enable_iso_c=no])
AC_MSG_CHECKING(what language compliance flags to pass to the C compiler)
complCFLAGS=
if test "x$enable_iso_c" != "xno"; then
if test "x$GCC" = "xyes"; then
case " $CFLAGS " in
*[\ \ ]-ansi[\ \ ]*) ;;
*) complCFLAGS="$complCFLAGS -ansi" ;;
esac
case " $CFLAGS " in
*[\ \ ]-pedantic[\ \ ]*) ;;
*) complCFLAGS="$complCFLAGS -pedantic" ;;
esac
fi
fi
AC_MSG_RESULT($complCFLAGS)
WARN_CFLAGS="$warning_flags $complCFLAGS"
AC_SUBST(WARN_CFLAGS)
])
dnl For C++, do basically the same thing.
AC_DEFUN([GNOME_CXX_WARNINGS],[
AC_ARG_ENABLE(cxx-warnings,
AC_HELP_STRING([--enable-cxx-warnings=@<:@no/minimum/yes@:>@]
[Turn on compiler warnings.]),,
[enable_cxx_warnings="m4_default([$1],[minimum])"])
AC_MSG_CHECKING(what warning flags to pass to the C++ compiler)
warnCXXFLAGS=
if test "x$GXX" != xyes; then
enable_cxx_warnings=no
fi
if test "x$enable_cxx_warnings" != "xno"; then
if test "x$GXX" = "xyes"; then
case " $CXXFLAGS " in
*[\ \ ]-Wall[\ \ ]*) ;;
*) warnCXXFLAGS="-Wall -Wno-unused" ;;
esac
## -W is not all that useful. And it cannot be controlled
## with individual -Wno-xxx flags, unlike -Wall
if test "x$enable_cxx_warnings" = "xyes"; then
warnCXXFLAGS="$warnCXXFLAGS -Wshadow -Woverloaded-virtual"
fi
fi
fi
AC_MSG_RESULT($warnCXXFLAGS)
AC_ARG_ENABLE(iso-cxx,
AC_HELP_STRING([--enable-iso-cxx],
[Try to warn if code is not ISO C++ ]),,
[enable_iso_cxx=no])
AC_MSG_CHECKING(what language compliance flags to pass to the C++ compiler)
complCXXFLAGS=
if test "x$enable_iso_cxx" != "xno"; then
if test "x$GXX" = "xyes"; then
case " $CXXFLAGS " in
*[\ \ ]-ansi[\ \ ]*) ;;
*) complCXXFLAGS="$complCXXFLAGS -ansi" ;;
esac
case " $CXXFLAGS " in
*[\ \ ]-pedantic[\ \ ]*) ;;
*) complCXXFLAGS="$complCXXFLAGS -pedantic" ;;
esac
fi
fi
AC_MSG_RESULT($complCXXFLAGS)
WARN_CXXFLAGS="$CXXFLAGS $warnCXXFLAGS $complCXXFLAGS"
AC_SUBST(WARN_CXXFLAGS)
])

View File

@ -1,22 +0,0 @@
#!/bin/sh
srcdir=`dirname $0`
test -z "$srcdir" && srcdir=.
olddir=`pwd`
cd "$srcdir"
aclocal || exit 1
autoheader || exit 1
glib-gettextize -f -c || exit 1
gtkdocize --copy || exit 1
intltoolize -c -f || exit 1
libtoolize -c || exit 1
autoconf || exit 1
automake -a -c || exit 1
cd "$olddir"
if test -z "$NOCONFIGURE"; then
$srcdir/configure --enable-maintainer-mode $*
fi

11
config.h.in Normal file
View File

@ -0,0 +1,11 @@
/* Define to the Gettext package name */
#mesondefine GETTEXT_PACKAGE
/* Version number of package */
#mesondefine PACKAGE_VERSION
/* Where the configuration file will be located */
#mesondefine SYSCONFDIR
/* Define to the version of this package. */
#mesondefine VERSION

View File

@ -1,90 +0,0 @@
AC_INIT([fprintd], [0.9.0])
AM_INIT_AUTOMAKE([1.11 dist-xz no-dist-gzip check-news])
AC_CONFIG_SRCDIR([src/main.c])
AC_CONFIG_HEADERS([config.h])
# Enable silent build when available (Automake 1.11)
m4_ifdef([AM_SILENT_RULES],[AM_SILENT_RULES([yes])])
AC_PREREQ([2.50])
AC_PROG_LIBTOOL
AC_PROG_CC
AM_PROG_CC_C_O
GETTEXT_PACKAGE=fprintd
AC_DEFINE_UNQUOTED(GETTEXT_PACKAGE, ["$GETTEXT_PACKAGE"], [Define to the Gettext package name])
AC_SUBST(GETTEXT_PACKAGE)
AM_GLIB_GNU_GETTEXT
IT_PROG_INTLTOOL([0.35.0])
PKG_CHECK_MODULES(FPRINT, [libfprint > 0.1.0])
AC_SUBST(FPRINT_LIBS)
AC_SUBST(FPRINT_CFLAGS)
PKG_CHECK_MODULES(GLIB, glib-2.0 dbus-glib-1)
AC_SUBST(GLIB_CFLAGS)
AC_SUBST(GLIB_LIBS)
PKG_CHECK_MODULES(DAEMON, glib-2.0 dbus-glib-1 gmodule-2.0 polkit-gobject-1 >= 0.91 gio-2.0 >= 2.26)
AC_SUBST(DAEMON_LIBS)
AC_SUBST(DAEMON_CFLAGS)
AC_ARG_ENABLE(pam, AC_HELP_STRING([--enable-pam],[Build the fprintd PAM module]), enable_pam="$enableval", enable_pam=yes)
has_pam=no
if test x$enable_pam = xyes; then
has_pam=yes
AC_CHECK_HEADER([security/pam_modules.h], [has_pam=yes] , [has_pam=no])
if test x$has_pam = xyes; then
has_pam=no
AC_CHECK_LIB(pam, pam_start, [PAM_LIBS="-lpam"
has_pam=yes],
has_pam=no)
fi
AC_SUBST(PAM_LIBS)
fi
AM_CONDITIONAL(HAVE_PAM, test "x$has_pam" = "xyes")
AC_MSG_CHECKING(for PAM headers and library)
AC_MSG_RESULT([$has_pam])
AC_CHECK_PROG([XMLLINT], [xmllint], [xmllint])
AC_CHECK_PROG([XSLTPROC], [xsltproc], [xsltproc])
AC_CHECK_PROG([POD2MAN], [pod2man], [pod2man])
AM_CONDITIONAL(BUILD_MAN, test x"$POD2MAN" != "x")
GTK_DOC_CHECK([1.3])
AC_ARG_WITH([systemdsystemunitdir],
AS_HELP_STRING([--with-systemdsystemunitdir=DIR], [Directory for systemd service files]),
[],
[with_systemdsystemunitdir=$($PKG_CONFIG --variable=systemdsystemunitdir systemd)])
AS_IF([test "x$with_systemdsystemunitdir" != "xno"], [
# StateDirectory was introduced in systemd 235
PKG_CHECK_MODULES(SYSTEMD, systemd >= 235)
AC_SUBST([systemdsystemunitdir], [$with_systemdsystemunitdir])
])
AM_CONDITIONAL(HAVE_SYSTEMD, [test -n "$systemdsystemunitdir"])
AS_AC_EXPAND(DATADIR, $datadir)
DBUS_SERVICES_DIR="$DATADIR/dbus-1/services"
AC_SUBST(DBUS_SERVICES_DIR)
AC_DEFINE_UNQUOTED(DBUS_SERVICES_DIR, "$DBUS_SERVICES_DIR", [Where services dir for DBUS is])
AS_AC_EXPAND(SYSCONFDIR, $sysconfdir)
AC_DEFINE_UNQUOTED(SYSCONFDIR, "$SYSCONFDIR", [Where the configuration file will be located])
GNOME_COMPILE_WARNINGS
AC_OUTPUT([
Makefile
src/Makefile
data/Makefile
tests/Makefile
pam/Makefile
doc/Makefile
doc/version.xml
doc/dbus/Makefile
po/Makefile.in
])

View File

@ -1,44 +0,0 @@
dbus_servicesdir = $(datadir)/dbus-1/system-services
dbus_services_in_files = net.reactivated.Fprint.service.in
dbus_services_DATA = $(dbus_services_in_files:.service.in=.service)
$(dbus_services_DATA): $(dbus_services_in_files)
sed -e "s|\@LIBEXECDIR\@|$(libexecdir)|" $< > $@
dbus_confdir = $(sysconfdir)/dbus-1/system.d
dbus_conf_DATA = net.reactivated.Fprint.conf
systemdservice_in_files = fprintd.service.in
if HAVE_SYSTEMD
systemdservicedir = $(systemdsystemunitdir)
systemdservice_DATA = $(systemdservice_in_files:.service.in=.service)
$(systemdservice_DATA): $(systemdservice_in_files) Makefile
@sed -e "s|\@libexecdir\@|$(libexecdir)|" $< > $@
endif
polkitdir = $(datadir)/polkit-1/actions
polkit_in_files = net.reactivated.fprint.device.policy.in
@INTLTOOL_POLICY_RULE@
polkit_DATA = $(polkit_in_files:.policy.in=.policy)
confdir = $(sysconfdir)/
conf_DATA = fprintd.conf
man_MANS =
if BUILD_MAN
man_MANS += fprintd.1
fprintd.1: fprintd.pod
$(AM_V_GEN) pod2man -c "" -s 1 -q none -n fprintd -r freedesktop $< > $@
endif
EXTRA_DIST = $(dbus_services_in_files) $(dbus_conf_DATA) $(polkit_in_files) $(conf_DATA) $(systemdservice_in_files) fprintd.pod
CLEANFILES = $(polkit_DATA) $(dbus_services_DATA) $(systemdservice_DATA) fprintd.1
check:
@$(XMLLINT) --noout $(polkit_DATA)

View File

@ -1,4 +1,4 @@
.\" Automatically generated by Pod::Man 2.23 (Pod::Simple 3.14)
.\" Automatically generated by Pod::Man 4.12 (Pod::Simple 3.39)
.\"
.\" Standard preamble:
.\" ========================================================================
@ -38,27 +38,36 @@
. ds PI \(*p
. ds L" ``
. ds R" ''
. ds C`
. ds C'
'br\}
.\"
.\" Escape single quotes in literal strings from groff's Unicode transform.
.ie \n(.g .ds Aq \(aq
.el .ds Aq '
.\"
.\" If the F register is turned on, we'll generate index entries on stderr for
.\" If the F register is >0, we'll generate index entries on stderr for
.\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index
.\" entries marked with X<> in POD. Of course, you'll have to process the
.\" output yourself in some meaningful fashion.
.ie \nF \{\
.\"
.\" Avoid warning from groff about undefined register 'F'.
.de IX
..
.nr rF 0
.if \n(.g .if rF .nr rF 1
.if (\n(rF:(\n(.g==0)) \{\
. if \nF \{\
. de IX
. tm Index:\\$1\t\\n%\t"\\$2"
..
. if !\nF==2 \{\
. nr % 0
. rr F
.\}
.el \{\
. de IX
..
. nr F 2
. \}
. \}
.\}
.rr rF
.\"
.\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2).
.\" Fear. Run. Save yourself. No user-serviceable parts.
@ -124,16 +133,16 @@
.\" ========================================================================
.\"
.IX Title "fprintd 1"
.TH fprintd 1 "2010-08-16" "freedesktop" ""
.TH fprintd 1 "2020-01-24" "freedesktop" ""
.\" For nroff, turn off justification. Always turn off hyphenation; it makes
.\" way too many mistakes in technical documents.
.if n .ad l
.nh
.SH "NAME"
fprintd \- Fingerprint management daemon, and test applications
.SH "SYNOPSYS"
.IX Header "SYNOPSYS"
\&\fBfprintd-enroll\fR [username]
.SH "SYNOPSIS"
.IX Header "SYNOPSIS"
\&\fBfprintd-enroll\fR [\-f finger] [usename]
.PP
\&\fBfprintd-list\fR username [usernames...]
.PP
@ -185,7 +194,7 @@ Will list the user's enrolled fingerprints.
.SS "fprintd-verify"
.IX Subsection "fprintd-verify"
.RS 8
Will enroll the user's right index finger into the database.
Will verify the user's fingerprints against the database.
.RE
.SS "fprintd-enroll"
.IX Subsection "fprintd-enroll"
@ -200,5 +209,5 @@ Will enroll the user's right index finger into the database.
By default, fprintd stores the fingerprints in \fB/var/lib/fprint/\fR
.SH "SEE ALSO"
.IX Header "SEE ALSO"
.IP "\fBdbus-daemon\fR, \fBgnome-about-me\fR" 8
.IX Item "dbus-daemon, gnome-about-me"
.IP "\fBgnome-control-center\fR" 8
.IX Item "gnome-control-center"

View File

@ -2,7 +2,7 @@
fprintd - Fingerprint management daemon, and test applications
=head1 SYNOPSYS
=head1 SYNOPSIS
B<fprintd-enroll> [-f finger] [usename]
@ -75,7 +75,7 @@ Will list the user's enrolled fingerprints.
=over 8
Will enroll the user's right index finger into the database.
Will verify the user's fingerprints against the database.
=back
@ -99,7 +99,7 @@ By default, fprintd stores the fingerprints in B</var/lib/fprint/>
=over 8
=item B<dbus-daemon>, B<gnome-about-me>
=item B<gnome-control-center>
=back

70
data/meson.build Normal file
View File

@ -0,0 +1,70 @@
install_data('net.reactivated.Fprint.conf',
install_dir: dbus_conf_dir)
configure_file(
configuration: configuration_data({
'LIBEXECDIR': fprintd_installdir,
}),
input: 'net.reactivated.Fprint.service.in',
output: 'net.reactivated.Fprint.service',
install: true,
install_dir: dbus_service_dir,
)
configure_file(
configuration: configuration_data({
'libexecdir': fprintd_installdir,
}),
input: 'fprintd.service.in',
output: 'fprintd.service',
install: true,
install_dir: systemd_unit_dir,
)
polkit_policy = 'net.reactivated.fprint.device.policy'
polkit_policy_target = i18n.merge_file(polkit_policy,
input: '@0@.in'.format(polkit_policy),
output: polkit_policy,
po_dir: meson.source_root() / 'po',
install: true,
install_dir: polkit_policy_directory,
)
if xmllint.found()
test(polkit_policy,
xmllint,
depends: polkit_policy_target,
args: [
'--noout',
polkit_policy_target.full_path(),
])
endif
install_data('fprintd.conf',
install_dir: sysconfdir)
if get_option('man')
manfiles = {
'fprintd': 1,
'pam_fprintd': 8,
}
foreach man_name, man_section: manfiles
custom_target('man_' + man_name + '.' + man_section.to_string(),
input: man_name + '.pod',
output: man_name + '.' + man_section.to_string(),
command: [
pod2man,
'-c', '',
'-s', man_section.to_string(),
'-q', 'none',
'-n', man_name,
'-r', 'freedesktop',
'@INPUT@',
'@OUTPUT@',
],
install: true,
install_dir: datadir / 'man' / 'man' + man_section.to_string(),
)
endforeach
endif

View File

@ -10,8 +10,8 @@
<icon_name>fprint</icon_name>
<action id="net.reactivated.fprint.device.verify">
<_description>Verify a fingerprint</_description>
<_message>Privileges are required to verify fingerprints.</_message>
<description>Verify a fingerprint</description>
<message>Privileges are required to verify fingerprints.</message>
<defaults>
<allow_any>no</allow_any>
<allow_inactive>no</allow_inactive>
@ -20,8 +20,8 @@
</action>
<action id="net.reactivated.fprint.device.enroll">
<_description>Enroll new fingerprints</_description>
<_message>Privileges are required to enroll new fingerprints.</_message>
<description>Enroll new fingerprints</description>
<message>Privileges are required to enroll new fingerprints.</message>
<defaults>
<allow_any>no</allow_any>
<allow_inactive>no</allow_inactive>
@ -30,8 +30,8 @@
</action>
<action id="net.reactivated.fprint.device.setusername">
<_description>Select a user to enroll</_description>
<_message>Privileges are required to enroll new fingerprints for other users.</_message>
<description>Select a user to enroll</description>
<message>Privileges are required to enroll new fingerprints for other users.</message>
<defaults>
<allow_any>no</allow_any>
<allow_inactive>no</allow_inactive>

184
data/pam_fprintd.8 Normal file
View File

@ -0,0 +1,184 @@
.\" Automatically generated by Pod::Man 4.12 (Pod::Simple 3.39)
.\"
.\" Standard preamble:
.\" ========================================================================
.de Sp \" Vertical space (when we can't use .PP)
.if t .sp .5v
.if n .sp
..
.de Vb \" Begin verbatim text
.ft CW
.nf
.ne \\$1
..
.de Ve \" End verbatim text
.ft R
.fi
..
.\" Set up some character translations and predefined strings. \*(-- will
.\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left
.\" double quote, and \*(R" will give a right double quote. \*(C+ will
.\" give a nicer C++. Capital omega is used to do unbreakable dashes and
.\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff,
.\" nothing in troff, for use with C<>.
.tr \(*W-
.ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p'
.ie n \{\
. ds -- \(*W-
. ds PI pi
. if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch
. if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch
. ds L" ""
. ds R" ""
. ds C`
. ds C'
'br\}
.el\{\
. ds -- \|\(em\|
. ds PI \(*p
. ds L" ``
. ds R" ''
. ds C`
. ds C'
'br\}
.\"
.\" Escape single quotes in literal strings from groff's Unicode transform.
.ie \n(.g .ds Aq \(aq
.el .ds Aq '
.\"
.\" If the F register is >0, we'll generate index entries on stderr for
.\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index
.\" entries marked with X<> in POD. Of course, you'll have to process the
.\" output yourself in some meaningful fashion.
.\"
.\" Avoid warning from groff about undefined register 'F'.
.de IX
..
.nr rF 0
.if \n(.g .if rF .nr rF 1
.if (\n(rF:(\n(.g==0)) \{\
. if \nF \{\
. de IX
. tm Index:\\$1\t\\n%\t"\\$2"
..
. if !\nF==2 \{\
. nr % 0
. nr F 2
. \}
. \}
.\}
.rr rF
.\"
.\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2).
.\" Fear. Run. Save yourself. No user-serviceable parts.
. \" fudge factors for nroff and troff
.if n \{\
. ds #H 0
. ds #V .8m
. ds #F .3m
. ds #[ \f1
. ds #] \fP
.\}
.if t \{\
. ds #H ((1u-(\\\\n(.fu%2u))*.13m)
. ds #V .6m
. ds #F 0
. ds #[ \&
. ds #] \&
.\}
. \" simple accents for nroff and troff
.if n \{\
. ds ' \&
. ds ` \&
. ds ^ \&
. ds , \&
. ds ~ ~
. ds /
.\}
.if t \{\
. ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u"
. ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u'
. ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u'
. ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u'
. ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u'
. ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u'
.\}
. \" troff and (daisy-wheel) nroff accents
.ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V'
.ds 8 \h'\*(#H'\(*b\h'-\*(#H'
.ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#]
.ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H'
.ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u'
.ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#]
.ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#]
.ds ae a\h'-(\w'a'u*4/10)'e
.ds Ae A\h'-(\w'A'u*4/10)'E
. \" corrections for vroff
.if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u'
.if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u'
. \" for low resolution devices (crt and lpr)
.if \n(.H>23 .if \n(.V>19 \
\{\
. ds : e
. ds 8 ss
. ds o a
. ds d- d\h'-1'\(ga
. ds D- D\h'-1'\(hy
. ds th \o'bp'
. ds Th \o'LP'
. ds ae ae
. ds Ae AE
.\}
.rm #[ #] #H #V #F C
.\" ========================================================================
.\"
.IX Title "pam_fprintd 8"
.TH pam_fprintd 8 "2020-01-24" "freedesktop" ""
.\" For nroff, turn off justification. Always turn off hyphenation; it makes
.\" way too many mistakes in technical documents.
.if n .ad l
.nh
.SH "NAME"
pam_fprintd \- PAM module to authenticate against fprintd, the fingerprint daemon
.SH "SYNOPSIS"
.IX Header "SYNOPSIS"
\&\fBpam_fprintd.so\fR [debug|debug=[\fIon\fR|\fIoff\fR|\fItrue\fR|\fIfalse\fR|\fI1\fR|\fI0\fR]] [max\-tries=\fI\s-1MAX_TRIES\s0\fR] [timeout=\fI\s-1TIMEOUT\s0\fR]
.SH "DESCRIPTION"
.IX Header "DESCRIPTION"
The pam_fprintd module is used to verify a user's fingerprints against fingerprints
enrolled using fprintd, the fingerprint management daemon.
.SH "OPTIONS"
.IX Header "OPTIONS"
.IP "\fBdebug\fR" 8
.IX Item "debug"
.PD 0
.IP "\fBdebug=[\f(BIon\fB|\f(BIoff\fB|\f(BItrue\fB|\f(BIfalse\fB|\f(BI1\fB|\f(BI0\fB]\fR" 8
.IX Item "debug=[on|off|true|false|1|0]"
.PD
Whether debug should be turned on or off. Debug messages will be generated using
pam_syslog which means that they will be saved in the systemd journal by default.
.IP "\fBmax\-tries=\f(BI\s-1MAX_TRIES\s0\fB\fR" 8
.IX Item "max-tries=MAX_TRIES"
The number of attempts at fingerprint authentication to try before returning an
authentication failure. The minimum, and default, number of tries is 3.
.IP "\fBtimeout=\f(BI\s-1TIMEOUT\s0\fB\fR" 8
.IX Item "timeout=TIMEOUT"
The amount of time before returning an authentication failure. The default timeout
is 30 seconds, with 10 seconds being the minimum.
.SH "LIMITATIONS"
.IX Header "LIMITATIONS"
The \s-1PAM\s0 stack is by design a serialised authentication, so it is not
possible for pam_fprintd to allow authentication through passwords and
fingerprints at the same time.
.PP
It is up to the application using the \s-1PAM\s0 services to implement separate
\&\s-1PAM\s0 processes and run separate authentication stacks separately. This
is the way multiple authentication methods are made available to users
of gdm for example.
.SH "AUTHOR"
.IX Header "AUTHOR"
\&\fBfprintd\fR was written by Bastien Nocera.
.SH "SEE ALSO"
.IX Header "SEE ALSO"
.IP "\fBfprintd\fR, \fB\s-1PAM\s0\fR" 8
.IX Item "fprintd, PAM"

63
data/pam_fprintd.pod Normal file
View File

@ -0,0 +1,63 @@
=head1 NAME
pam_fprintd - PAM module to authenticate against fprintd, the fingerprint daemon
=head1 SYNOPSIS
B<pam_fprintd.so> [debug|debug=[I<on>|I<off>|I<true>|I<false>|I<1>|I<0>]] [max-tries=I<MAX_TRIES>] [timeout=I<TIMEOUT>]
=head1 DESCRIPTION
The pam_fprintd module is used to verify a user's fingerprints against fingerprints
enrolled using fprintd, the fingerprint management daemon.
=head1 OPTIONS
=over 8
=item B<debug>
=item B<debug=[I<on>|I<off>|I<true>|I<false>|I<1>|I<0>]>
Whether debug should be turned on or off. Debug messages will be generated using
pam_syslog which means that they will be saved in the systemd journal by default.
=item B<max-tries=I<MAX_TRIES>>
The number of attempts at fingerprint authentication to try before returning an
authentication failure. The minimum, and default, number of tries is 3.
=item B<timeout=I<TIMEOUT>>
The amount of time before returning an authentication failure. The default timeout
is 30 seconds, with 10 seconds being the minimum.
=back
=head1 LIMITATIONS
=over 8
=back
The PAM stack is by design a serialised authentication, so it is not
possible for pam_fprintd to allow authentication through passwords and
fingerprints at the same time.
It is up to the application using the PAM services to implement separate
PAM processes and run separate authentication stacks separately. This
is the way multiple authentication methods are made available to users
of gdm for example.
=head1 AUTHOR
B<fprintd> was written by Bastien Nocera.
=head1 SEE ALSO
=over 8
=item B<fprintd>, B<PAM>
=back

View File

@ -1,81 +0,0 @@
SUBDIRS = dbus
NULL =
AUTOMAKE_OPTIONS = 1.7
# The name of the module.
DOC_MODULE=fprintd
# The top-level SGML file.
DOC_MAIN_SGML_FILE=fprintd-docs.xml
# Extra options to supply to gtkdoc-scan
SCAN_OPTIONS=--ignore-headers=config.h
# The directory containing the source code. Relative to $(srcdir)
DOC_SOURCE_DIR=../src
# Used for dependencies
HFILE_GLOB=
#$(top_srcdir)/policy/*.h
CFILE_GLOB=
#$(top_srcdir)/policy/*.c
# Headers to ignore
IGNORE_HFILES= \
$(NULL)
# CFLAGS and LDFLAGS for compiling scan program. Only needed
# if $(DOC_MODULE).types is non-empty.
INCLUDES = \
$(GLIB_CFLAGS) \
-I$(top_srcdir)/src \
$(NULL)
GTKDOC_LIBS = \
$(GLIB_LIBS) \
$(top_builddir)/src/libfprintd.la \
$(NULL)
# Extra options to supply to gtkdoc-mkdb
MKDB_OPTIONS=--sgml-mode --output-format=xml
# Extra options to supply to gtkdoc-mktmpl
MKTMPL_OPTIONS=
# Non-autogenerated SGML files to be included in $(DOC_MAIN_SGML_FILE)
content_files = \
version.xml \
dbus/net.reactivated.Fprint.Manager.ref.xml \
dbus/net.reactivated.Fprint.Device.ref.xml \
$(NULL)
# Images to copy into HTML directory
HTML_IMAGES = \
$(NULL)
# Extra options to supply to gtkdoc-fixref
FIXXREF_OPTIONS=
DISTCLEANFILES = \
net.reactivated.Fprint.Manager.ref.xml \
net.reactivated.Fprint.Device.ref.xml
MAINTAINERCLEANFILES = \
*~ \
Makefile.in \
fprintd.types \
fprintd-*.txt \
$(NULL)
if ENABLE_GTK_DOC
include $(top_srcdir)/gtk-doc.make
else
EXTRA_DIST = fprintd-docs.xml
endif
# Version information for marking the documentation
EXTRA_DIST += version.xml.in

View File

@ -1,15 +0,0 @@
noinst_DATA = net.reactivated.Fprint.Manager.ref.xml net.reactivated.Fprint.Device.ref.xml
net.reactivated.Fprint.Manager.ref.xml : $(top_srcdir)/src/manager.xml $(top_srcdir)/doc/dbus/spec-to-docbook.xsl
echo "<?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\">" > $@
$(XSLTPROC) $(top_srcdir)/doc/dbus/spec-to-docbook.xsl $< | tail -n +2 >> $@
net.reactivated.Fprint.Device.ref.xml : $(top_srcdir)/src/device.xml $(top_srcdir)/doc/dbus/spec-to-docbook.xsl
echo "<?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\">" > $@
$(XSLTPROC) $(top_srcdir)/doc/dbus/spec-to-docbook.xsl $< | tail -n +2 >> $@
EXTRA_DIST = spec-to-docbook.xsl dbus-introspect-docs.dtd
clean-local :
rm -f *~ *.ref.xml

29
doc/dbus/meson.build Normal file
View File

@ -0,0 +1,29 @@
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">',
],
capture: true,
)
dbus_interfaces_refs = []
foreach interface_file: dbus_interfaces_files
basename = run_command('basename', interface_file.full_path(), '.xml').stdout().strip()
dbus_interfaces_refs += custom_target(basename + '_ref',
input: docbook_xml_header,
output: basename + '.ref.xml',
build_by_default: true,
depends: interface_file,
capture: true,
command: [
bash, '-c',
'cat @INPUT@;' +
xsltproc.path() + ' @0@/@1@ '.format(
meson.source_root(),
files('spec-to-docbook.xsl')[0]) +
interface_file.full_path() + '| tail -n +2;',
],
)
endforeach

27
doc/meson.build Normal file
View File

@ -0,0 +1,27 @@
subdir('dbus')
version_file = configure_file(
input: 'version.xml.in',
output: 'version.xml',
configuration: configuration_data({
'VERSION': meson.project_version(),
}),
)
gnome.gtkdoc(meson.project_name(),
main_xml: 'fprintd-docs.xml',
src_dir: meson.source_root() / 'src',
dependencies: [
declare_dependency(
sources: dbus_interfaces_refs,
link_with: libfprintd_private,
),
],
content_files: [
version_file,
dbus_interfaces_refs,
],
ignore_headers: [
'config.h',
],
install: true)

196
git.mk
View File

@ -1,196 +0,0 @@
# git.mk
#
# Copyright 2009, Red Hat, Inc.
# Written by Behdad Esfahbod
#
# Copying and distribution of this file, with or without modification,
# are permitted in any medium without royalty provided the copyright
# notice and this notice are preserved.
#
# The canonical source for this file is pango/git.mk, or whereever the
# header of pango/git.mk suggests in the future.
#
# To use in your project, import this file in your git repo's toplevel,
# then do "make -f git.mk". This modifies all Makefile.am files in
# your project to include git.mk.
#
# This enables automatic .gitignore generation. If you need to ignore
# more files, add them to the GITIGNOREFILES variable in your Makefile.am.
# But think twice before doing that. If a file has to be in .gitignore,
# chances are very high that it's a generated file and should be in one
# of MOSTLYCLEANFILES, CLEANFILES, DISTCLEANFILES, or MAINTAINERCLEANFILES.
#
# The only case that you need to manually add a file to GITIGNOREFILES is
# when remove files in one of mostlyclean-local, clean-local, distclean-local,
# or maintainer-clean-local.
#
# Note that for files like editor backup, etc, there are better places to
# ignore them. See "man gitignore".
#
# If "make maintainer-clean" removes the files but they are not recognized
# by this script (that is, if "git status" shows untracked files still), send
# me the output of "git status" as well as your Makefile.am and Makefile for
# the directories involved.
#
# For a list of toplevel files that should be in MAINTAINERCLEANFILES, see
# pango/Makefile.am.
#
# Don't EXTRA_DIST this file. It is supposed to only live in git clones,
# not tarballs. It serves no useful purpose in tarballs and clutters the
# build dir.
#
# This file knows how to handle autoconf, automake, libtool, gtk-doc,
# gnome-doc-utils, intltool, GSettings.
#
#
# KNOWN ISSUES:
#
# - Recursive configure doesn't work as $(top_srcdir)/git.mk inside the
# submodule doesn't find us. If you have configure.{in,ac} files in
# subdirs, add a proxy git.mk file in those dirs that simply does:
# "include $(top_srcdir)/../git.mk". Add more ..'s to your taste.
# And add those files to git. See vte/gnome-pty-helper/git.mk for
# example.
#
git-all: git-mk-install
git-mk-install:
@echo Installing git makefile
@any_failed=; find $(top_srcdir) -name Makefile.am | while read x; do \
if grep 'include .*/git.mk' $$x >/dev/null; then \
echo $$x already includes git.mk; \
else \
failed=; \
echo "Updating $$x"; \
{ cat $$x; \
echo ''; \
echo '-include $$(top_srcdir)/git.mk'; \
} > $$x.tmp || failed=1; \
if test x$$failed = x; then \
mv $$x.tmp $$x || failed=1; \
fi; \
if test x$$failed = x; then : else \
echo Failed updating $$x; >&2 \
any_failed=1; \
fi; \
fi; done; test -z "$$any_failed"
.PHONY: git-all git-mk-install
### .gitignore generation
$(srcdir)/.gitignore: Makefile.am $(top_srcdir)/git.mk
$(AM_V_GEN) \
{ \
if test "x$(DOC_MODULE)" = x -o "x$(DOC_MAIN_SGML_FILE)" = x; then :; else \
for x in \
$(DOC_MODULE)-decl-list.txt \
$(DOC_MODULE)-decl.txt \
tmpl/$(DOC_MODULE)-unused.sgml \
"tmpl/*.bak" \
xml html \
; do echo /$$x; done; \
fi; \
if test "x$(DOC_MODULE)" = x -o "x$(DOC_LINGUAS)" = x; then :; else \
for x in \
$(_DOC_C_DOCS) \
$(_DOC_LC_DOCS) \
$(_DOC_OMF_ALL) \
$(_DOC_DSK_ALL) \
$(_DOC_HTML_ALL) \
$(_DOC_POFILES) \
$(_DOC_MOFILES) \
$(DOC_H_FILE) \
"*/.xml2po.mo" \
"*/*.omf.out" \
; do echo /$$x; done; \
fi; \
if test "x$(gsettings_SCHEMAS)" = x; then :; else \
for x in \
$(gsettings_SCHEMAS:.xml=.valid) \
$(gsettings__enum_file) \
; do echo /$$x; done; \
fi; \
if test -f $(srcdir)/po/Makefile.in.in; then \
for x in \
po/Makefile.in.in \
po/Makefile.in \
po/Makefile \
po/POTFILES \
po/stamp-it \
po/.intltool-merge-cache \
"po/*.gmo" \
"po/*.mo" \
po/$(GETTEXT_PACKAGE).pot \
intltool-extract.in \
intltool-merge.in \
intltool-update.in \
; do echo /$$x; done; \
fi; \
if test -f $(srcdir)/configure; then \
for x in \
autom4te.cache \
configure \
config.h \
stamp-h1 \
libtool \
config.lt \
; do echo /$$x; done; \
fi; \
for x in \
.gitignore \
$(GITIGNOREFILES) \
$(CLEANFILES) \
$(PROGRAMS) \
$(check_PROGRAMS) \
$(EXTRA_PROGRAMS) \
$(LTLIBRARIES) \
so_locations \
.libs _libs \
$(MOSTLYCLEANFILES) \
"*.$(OBJEXT)" \
"*.lo" \
$(DISTCLEANFILES) \
$(am__CONFIG_DISTCLEAN_FILES) \
$(CONFIG_CLEAN_FILES) \
TAGS ID GTAGS GRTAGS GSYMS GPATH tags \
"*.tab.c" \
$(MAINTAINERCLEANFILES) \
$(BUILT_SOURCES) \
$(DEPDIR) \
Makefile \
Makefile.in \
"*.orig" \
"*.rej" \
"*.bak" \
"*~" \
".*.sw[nop]" \
".dirstamp" \
; do echo /$$x; done; \
} | \
sed "s@^/`echo "$(srcdir)" | sed 's/\(.\)/[\1]/g'`/@/@" | \
sed 's@/[.]/@/@g' | \
LC_ALL=C sort | uniq > $@.tmp && \
mv $@.tmp $@;
all: $(srcdir)/.gitignore gitignore-recurse-maybe
gitignore-recurse-maybe:
@if test "x$(SUBDIRS)" = "x$(DIST_SUBDIRS)"; then :; else \
$(MAKE) $(AM_MAKEFLAGS) gitignore-recurse; \
fi;
gitignore-recurse:
@for subdir in $(DIST_SUBDIRS); do \
case " $(SUBDIRS) " in \
*" $$subdir "*) :;; \
*) test "$$subdir" = . || (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) .gitignore gitignore-recurse || echo "Skipping $$subdir");; \
esac; \
done
gitignore: $(srcdir)/.gitignore gitignore-recurse
maintainer-clean: gitignore-clean
gitignore-clean:
-rm -f $(srcdir)/.gitignore
.PHONY: gitignore-clean gitignore gitignore-recurse gitignore-recurse-maybe

182
meson.build Normal file
View File

@ -0,0 +1,182 @@
project('fprintd', 'c',
version: '1.90.1',
license: 'GPLv2+',
default_options: [
'buildtype=debugoptimized',
'warning_level=1',
'c_std=gnu99',
],
meson_version: '>= 0.50.0')
gnome = import('gnome')
i18n = import('i18n')
cc = meson.get_compiler('c')
host_system = host_machine.system()
glib_min_version = '2.56'
libfprint_min_version = '1.90.0'
fprintd_installdir = get_option('prefix') / get_option('libexecdir')
fprintd_plugindir = get_option('prefix') / get_option('libdir') / meson.project_name() / 'modules'
storage_path = get_option('prefix') / get_option('localstatedir') / 'lib/fprint'
localedir = get_option('prefix') / get_option('localedir')
datadir = get_option('prefix') / get_option('datadir')
sysconfdir = get_option('sysconfdir')
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)
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'),
has_headers: 'security/pam_modules.h',
)
pod2man = find_program('pod2man', required: get_option('man'))
xsltproc = find_program('xsltproc', required: get_option('gtk_doc'))
# StateDirectory was introduced in systemd 235
systemd_dep = dependency('systemd', version: '>= 235')
systemd_unit_dir = get_option('systemd_system_unit_dir')
if systemd_unit_dir == ''
systemd_unit_dir = systemd_dep.get_pkgconfig_variable('systemdsystemunitdir')
endif
dbus_service_dir = get_option('dbus_service_dir')
dbus_data_dir = datadir
dbus_interfaces_dir = ''
if dbus_dep.found()
if dbus_service_dir == ''
dbus_service_dir = dbus_dep.get_pkgconfig_variable('system_bus_services_dir')
endif
dbus_interfaces_dir = dbus_dep.get_pkgconfig_variable('interfaces_dir')
dbus_data_dir = dbus_dep.get_pkgconfig_variable('datadir')
endif
dbus_conf_dir = dbus_data_dir / 'dbus-1/system.d'
if dbus_service_dir == ''
dbus_service_dir = datadir / 'dbus-1/system-services'
endif
if dbus_interfaces_dir == ''
dbus_interfaces_dir = datadir / 'dbus-1/interfaces'
endif
polkit_policy_directory = polkit_gobject_dep.get_pkgconfig_variable('policydir')
# Tests dependencies
pam_wrapper_dep = dependency('pam_wrapper', required: get_option('pam'))
xmllint = find_program('xmllint', required: false)
python3 = find_program('python3') # No meson without it!
python3_test_modules = {
'cairo': true,
'dbus': true,
'dbusmock': true,
'gi': true,
'pypamtest': get_option('pam'),
}
python3_available_modules = []
foreach module, required : python3_test_modules
if required and run_command(python3, '-c', 'import @0@'.format(module)).returncode() != 0
error('Python3 module \'' + module + '\' required by test suite not found')
endif
endforeach
cdata = configuration_data()
cdata.set_quoted('GETTEXT_PACKAGE', meson.project_name())
cdata.set_quoted('PACKAGE_VERSION', meson.project_version())
cdata.set_quoted('VERSION', meson.project_version())
cdata.set_quoted('SYSCONFDIR', sysconfdir)
config_h = configure_file(
input: 'config.h.in',
output: 'config.h',
configuration: cdata
)
subdir('src')
subdir('data')
subdir('utils')
if get_option('pam')
subdir('pam')
endif
if get_option('gtk_doc')
subdir('doc')
endif
subdir('tests')
subdir('po')
output = []
output += 'System paths:'
output += ' prefix: ' + get_option('prefix')
output += ' fprintd daemon directory: ' + fprintd_installdir
output += ' fprintd modules directory: ' + fprintd_plugindir
output += ' fprintd prints storage directory: ' + storage_path
output += ' DBus configuration directory: ' + dbus_conf_dir
output += ' DBus service directory: ' + dbus_service_dir
output += ' DBus interfaces directory: ' + dbus_interfaces_dir
output += ' Polkit policy directory: ' + polkit_policy_directory
output += ' Systemd service directory: ' + systemd_unit_dir
if get_option('pam')
output += ' PAM module directory: ' + pam_modules_dir
endif
output += '\nOptional features:\n'
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()
message('\n'+'\n'.join(output)+'\n')

21
meson_options.txt Normal file
View File

@ -0,0 +1,21 @@
option('pam',
description: 'Build the fprintd PAM module',
type: 'boolean',
value: true)
option('man',
description: 'Generate the man files',
type: 'boolean',
value: true)
option('systemd_system_unit_dir',
description: 'Directory for systemd service files',
type: 'string')
option('dbus_service_dir',
description: 'Directory for dbus service files',
type: 'string')
option('pam_modules_dir',
description: 'Directory for PAM modules',
type: 'string')
option('gtk_doc',
type: 'boolean',
value: false,
description: 'Use gtk-doc to build documentation')

View File

@ -1,22 +0,0 @@
if HAVE_PAM
pammod_LTLIBRARIES = pam_fprintd.la
pammoddir=$(libdir)/security
pam_fprintd_la_SOURCES = pam_fprintd.c $(MARSHALFILES)
pam_fprintd_la_CFLAGS = -fPIC $(WARN_CFLAGS) $(GLIB_CFLAGS) -DLOCALEDIR="\"$(localedir)\""
pam_fprintd_la_LDFLAGS = -avoid-version -module -Wl,-z,nodelete
pam_fprintd_la_LIBADD = $(PAM_LIBS) $(GLIB_LIBS)
MARSHALFILES = marshal.c marshal.h
GLIB_GENMARSHAL=`pkg-config --variable=glib_genmarshal glib-2.0`
BUILT_SOURCES = $(MARSHALFILES)
marshal.h: $(top_srcdir)/src/fprintd-marshal.list
( $(GLIB_GENMARSHAL) --prefix=fprintd_marshal $(top_srcdir)/src/fprintd-marshal.list --header > marshal.h )
marshal.c: marshal.h
( $(GLIB_GENMARSHAL) --prefix=fprintd_marshal $(top_srcdir)/src/fprintd-marshal.list --body --header > marshal.c )
endif
EXTRA_DIST = pam_fprintd.c fingerprint-strings.h

View File

@ -20,6 +20,23 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef _GNU_SOURCE
#error _GNU_SOURCE must be defined
#endif
#include <stdio.h>
#include <stdbool.h>
#define GNUC_UNUSED __attribute__((__unused__))
static bool str_equal (const char *a, const char *b)
{
if (a == NULL && b == NULL)
return true;
if (a == NULL || b == NULL)
return false;
return (strcmp (a, b) == 0);
}
struct {
const char *dbus_name;
const char *place_str_generic;
@ -88,7 +105,7 @@ struct {
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wformat-nonliteral"
G_GNUC_UNUSED static char *finger_str_to_msg(const char *finger_name, const char *driver_name, gboolean is_swipe)
GNUC_UNUSED static char *finger_str_to_msg(const char *finger_name, const char *driver_name, bool is_swipe)
{
int i;
@ -96,17 +113,25 @@ G_GNUC_UNUSED static char *finger_str_to_msg(const char *finger_name, const char
return NULL;
for (i = 0; fingers[i].dbus_name != NULL; i++) {
if (g_str_equal (fingers[i].dbus_name, finger_name)) {
if (is_swipe == FALSE) {
if (driver_name)
return g_strdup_printf (TR (fingers[i].place_str_specific), driver_name);
else
return g_strdup (TR (fingers[i].place_str_generic));
if (str_equal (fingers[i].dbus_name, finger_name)) {
if (is_swipe == false) {
if (driver_name) {
char *s;
int ret;
ret = asprintf (&s, TR (fingers[i].place_str_specific), driver_name);
return ret >= 0 ? s : NULL;
} else {
if (driver_name)
return g_strdup_printf (TR (fingers[i].swipe_str_specific), driver_name);
else
return g_strdup (TR (fingers[i].swipe_str_generic));
return strdup (TR (fingers[i].place_str_generic));
}
} else {
if (driver_name) {
char *s;
int ret;
ret = asprintf (&s, TR (fingers[i].swipe_str_specific), driver_name);
return ret >= 0 ? s : NULL;
} else {
return strdup (TR (fingers[i].swipe_str_generic));
}
}
}
}
@ -121,13 +146,13 @@ G_GNUC_UNUSED static char *finger_str_to_msg(const char *finger_name, const char
* verify-match
* verify-unknown-error
*/
G_GNUC_UNUSED static const char *verify_result_str_to_msg(const char *result, gboolean is_swipe)
GNUC_UNUSED static const char *verify_result_str_to_msg(const char *result, bool is_swipe)
{
if (result == NULL)
return NULL;
if (strcmp (result, "verify-retry-scan") == 0) {
if (is_swipe == FALSE)
if (is_swipe == false)
return N_("Place your finger on the reader again");
else
return N_("Swipe your finger again");
@ -147,13 +172,13 @@ G_GNUC_UNUSED static const char *verify_result_str_to_msg(const char *result, gb
* enroll-failed
* enroll-unknown-error
*/
G_GNUC_UNUSED static const char *enroll_result_str_to_msg(const char *result, gboolean is_swipe)
GNUC_UNUSED static const char *enroll_result_str_to_msg(const char *result, bool is_swipe)
{
if (result == NULL)
return NULL;
if (strcmp (result, "enroll-retry-scan") == 0 || strcmp (result, "enroll-stage-passed") == 0) {
if (is_swipe == FALSE)
if (is_swipe == false)
return N_("Place your finger on the reader again");
else
return N_("Swipe your finger again");

30
pam/meson.build Normal file
View File

@ -0,0 +1,30 @@
mapfile = files('pam_fprintd.ver')
pam_modules_dir = get_option('pam_modules_dir')
if pam_modules_dir == ''
pam_modules_dir = '/' / get_option('libdir') / 'security'
endif
pam_fprintd = shared_module('pam_fprintd',
name_prefix: '',
include_directories: [
include_directories('..'),
],
sources: [
'pam_fprintd.c',
'fingerprint-strings.h',
],
dependencies: [
libsystemd_dep,
pam_dep,
],
c_args: [
'-DLOCALEDIR="@0@"'.format(localedir),
],
link_args: [
'-Wl,--version-script,@0@/@1@'.format(meson.source_root(), mapfile[0]),
'-Wl,--unresolved-symbols=report-all',
],
link_depends: mapfile,
install: true,
install_dir: pam_modules_dir,
)

View File

@ -1,7 +1,7 @@
/*
* pam_fprint: PAM module for fingerprint authentication through fprintd
* Copyright (C) 2007 Daniel Drake <dsd@gentoo.org>
* Copyright (C) 2008 Bastien Nocera <hadess@hadess.net>
* Copyright (C) 2008-2014, 2017-2020 Bastien Nocera <hadess@hadess.net>
*
* 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,50 +20,64 @@
#include <config.h>
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <unistd.h>
#include <sys/types.h>
#include <string.h>
#include <syslog.h>
#include <errno.h>
#include <glib/gi18n-lib.h>
#include <dbus/dbus-glib-bindings.h>
#include <dbus/dbus-glib-lowlevel.h>
#include <libintl.h>
#include <systemd/sd-bus.h>
#include <systemd/sd-login.h>
#define PAM_SM_AUTH
#include <security/pam_modules.h>
#include <security/pam_ext.h>
#include "marshal.h"
#define _(s) ((char *) dgettext (GETTEXT_PACKAGE, s))
#define TR(s) dgettext(GETTEXT_PACKAGE, s)
#define N_(s) (s)
#include "fingerprint-strings.h"
#define DEFAULT_MAX_TRIES 3
#define DEFAULT_TIMEOUT 30
#define MIN_TIMEOUT 10
#define DEBUG_MATCH "debug="
#define MAX_TRIES_MATCH "max-tries="
#define TIMEOUT_MATCH "timeout="
#define D(pamh, ...) { \
if (debug) { \
char *s; \
s = g_strdup_printf (__VA_ARGS__); \
send_debug_msg (pamh, s); \
g_free (s); \
} \
static bool debug = false;
static unsigned max_tries = DEFAULT_MAX_TRIES;
static unsigned timeout = DEFAULT_TIMEOUT;
#define USEC_PER_SEC ((uint64_t) 1000000ULL)
#define NSEC_PER_USEC ((uint64_t) 1000ULL)
static uint64_t
now (void)
{
struct timespec ts;
clock_gettime (CLOCK_MONOTONIC, &ts);
return (uint64_t) ts.tv_sec * USEC_PER_SEC + (uint64_t) ts.tv_nsec / NSEC_PER_USEC;
}
static bool str_has_prefix (const char *s, const char *prefix)
{
if (s == NULL || prefix == NULL)
return false;
return (strncmp (s, prefix, strlen (prefix)) == 0);
}
static gboolean debug = FALSE;
static guint max_tries = DEFAULT_MAX_TRIES;
static guint timeout = DEFAULT_TIMEOUT;
static gboolean send_info_msg(pam_handle_t *pamh, const char *msg)
static bool send_msg(pam_handle_t *pamh, const char *msg, int style)
{
const struct pam_message mymsg = {
.msg_style = PAM_TEXT_INFO,
.msg_style = style,
.msg = msg,
};
const struct pam_message *msgp = &mymsg;
@ -73,407 +87,512 @@ static gboolean send_info_msg(pam_handle_t *pamh, const char *msg)
r = pam_get_item(pamh, PAM_CONV, (const void **) &pc);
if (r != PAM_SUCCESS)
return FALSE;
return false;
if (!pc || !pc->conv)
return FALSE;
return false;
return (pc->conv(1, &msgp, &resp, pc->appdata_ptr) == PAM_SUCCESS);
}
static gboolean send_err_msg(pam_handle_t *pamh, const char *msg)
static bool send_info_msg(pam_handle_t *pamh, const char *msg)
{
const struct pam_message mymsg = {
.msg_style = PAM_ERROR_MSG,
.msg = msg,
};
const struct pam_message *msgp = &mymsg;
const struct pam_conv *pc;
struct pam_response *resp;
return send_msg(pamh, msg, PAM_TEXT_INFO);
}
static bool send_err_msg(pam_handle_t *pamh, const char *msg)
{
return send_msg(pamh, msg, PAM_ERROR_MSG);
}
static char *
open_device (pam_handle_t *pamh,
sd_bus *bus,
bool *has_multiple_devices)
{
sd_bus_error error = SD_BUS_ERROR_NULL;
sd_bus_message *m = NULL;
size_t num_devices;
const char *path = NULL;
char *ret;
const char *s;
int r;
r = pam_get_item(pamh, PAM_CONV, (const void **) &pc);
if (r != PAM_SUCCESS)
return FALSE;
*has_multiple_devices = false;
if (!pc || !pc->conv)
return FALSE;
return (pc->conv(1, &msgp, &resp, pc->appdata_ptr) == PAM_SUCCESS);
}
static void send_debug_msg(pam_handle_t *pamh, const char *msg)
{
gconstpointer item;
const char *service;
if (pam_get_item(pamh, PAM_SERVICE, &item) != PAM_SUCCESS || !item)
service = "<unknown>";
else
service = item;
openlog (service, LOG_CONS | LOG_PID, LOG_AUTHPRIV);
syslog (LOG_AUTHPRIV|LOG_WARNING, "%s(%s): %s", "pam_fprintd", service, msg);
closelog ();
}
static DBusGProxy *create_manager (pam_handle_t *pamh, DBusGConnection **ret_conn, GMainLoop **ret_loop)
{
DBusGConnection *connection;
DBusConnection *conn;
DBusGProxy *manager;
DBusError error;
GMainLoop *loop;
GMainContext *ctx;
/* Otherwise dbus-glib doesn't setup it value types */
connection = dbus_g_bus_get (DBUS_BUS_SYSTEM, NULL);
if (connection != NULL)
dbus_g_connection_unref (connection);
/* And set us up a private D-Bus connection */
dbus_error_init (&error);
conn = dbus_bus_get_private (DBUS_BUS_SYSTEM, &error);
if (conn == NULL) {
D(pamh, "Error with getting the bus: %s", error.message);
dbus_error_free (&error);
return NULL;
}
/* Set up our own main loop context */
ctx = g_main_context_new ();
loop = g_main_loop_new (ctx, FALSE);
dbus_connection_setup_with_g_main (conn, ctx);
connection = dbus_connection_get_g_connection (conn);
manager = dbus_g_proxy_new_for_name(connection,
r = sd_bus_call_method (bus,
"net.reactivated.Fprint",
"/net/reactivated/Fprint/Manager",
"net.reactivated.Fprint.Manager");
*ret_conn = connection;
*ret_loop = loop;
return manager;
}
static void close_and_unref (DBusGConnection *connection)
{
DBusConnection *conn;
conn = dbus_g_connection_get_connection (connection);
dbus_connection_close (conn);
dbus_g_connection_unref (connection);
}
static void unref_loop (GMainLoop *loop)
{
GMainContext *ctx;
/* The main context was created separately, so
* we'll need to unref it ourselves */
ctx = g_main_loop_get_context (loop);
g_main_loop_unref (loop);
g_main_context_unref (ctx);
}
#define DBUS_TYPE_G_OBJECT_PATH_ARRAY (dbus_g_type_get_collection ("GPtrArray", DBUS_TYPE_G_OBJECT_PATH))
static DBusGProxy *open_device(pam_handle_t *pamh, DBusGConnection *connection, DBusGProxy *manager, gboolean *has_multiple_devices)
{
GError *error = NULL;
const char *path;
DBusGProxy *dev;
GPtrArray *paths_array;
const char **paths;
if (!dbus_g_proxy_call (manager, "GetDevices", &error,
G_TYPE_INVALID, DBUS_TYPE_G_OBJECT_PATH_ARRAY,
&paths_array, G_TYPE_INVALID)) {
D(pamh, "get_devices failed: %s", error->message);
g_error_free (error);
"net.reactivated.Fprint.Manager",
"GetDevices",
&error,
&m,
NULL);
if (r < 0) {
pam_syslog (pamh, LOG_ERR, "GetDevices failed: %s", error.message);
sd_bus_error_free (&error);
return NULL;
}
if (paths_array == NULL || paths_array->len == 0) {
if (paths_array != NULL)
g_ptr_array_free (paths_array, TRUE);
D(pamh, "No devices found\n");
return NULL;
r = sd_bus_message_enter_container (m, 'a', "o");
if (r < 0) {
pam_syslog (pamh, LOG_ERR, "Failed to parse answer from GetDevices(): %d", r);
goto out;
}
*has_multiple_devices = (paths_array->len > 1);
paths = (const char **)paths_array->pdata;
path = paths[0];
r = sd_bus_message_read_basic (m, 'o', &path);
if (r < 0)
goto out;
D(pamh, "Using device %s\n", path);
num_devices = 1;
while ((r = sd_bus_message_read_basic(m, 'o', &s)) > 0)
num_devices++;
*has_multiple_devices = (num_devices > 1);
if (debug)
pam_syslog(pamh, LOG_DEBUG, "Using device %s (out of %ld devices)", path, num_devices);
dev = dbus_g_proxy_new_for_name(connection,
"net.reactivated.Fprint",
path,
"net.reactivated.Fprint.Device");
sd_bus_message_exit_container (m);
g_ptr_array_free (paths_array, TRUE);
return dev;
out:
ret = path ? strdup (path) : NULL;
sd_bus_message_unref (m);
return ret;
}
typedef struct {
guint max_tries;
unsigned max_tries;
char *result;
gboolean timed_out;
gboolean is_swipe;
bool timed_out;
bool is_swipe;
pam_handle_t *pamh;
GMainLoop *loop;
char *driver;
} verify_data;
static void verify_result(GObject *object, const char *result, gboolean done, gpointer user_data)
static int
verify_result (sd_bus_message *m,
void *userdata,
sd_bus_error *ret_error)
{
verify_data *data = user_data;
verify_data *data = userdata;
const char *msg;
const char *result = NULL;
/* see https://github.com/systemd/systemd/issues/14643 */
uint64_t done = false;
int r;
D(data->pamh, "Verify result: %s\n", result);
if (done != FALSE) {
data->result = g_strdup (result);
g_main_loop_quit (data->loop);
return;
if (!sd_bus_message_is_signal(m, "net.reactivated.Fprint.Device", "VerifyStatus")) {
pam_syslog (data->pamh, LOG_ERR, "Not the signal we expected (iface: %s, member: %s)",
sd_bus_message_get_interface (m),
sd_bus_message_get_member (m));
return 0;
}
msg = TR(verify_result_str_to_msg (result, data->is_swipe));
if ((r = sd_bus_message_read (m, "sb", &result, &done)) < 0) {
pam_syslog (data->pamh, LOG_ERR, "Failed to parse VerifyResult signal: %d", r);
return 0;
}
if (debug)
pam_syslog (data->pamh, LOG_DEBUG, "Verify result: %s (done: %d)", result, done ? 1 : 0);
if (done) {
data->result = strdup (result);
return 0;
}
msg = _(verify_result_str_to_msg (result, data->is_swipe));
send_err_msg (data->pamh, msg);
return 0;
}
static void verify_finger_selected(GObject *object, const char *finger_name, gpointer user_data)
static int
verify_finger_selected (sd_bus_message *m,
void *userdata,
sd_bus_error *ret_error)
{
verify_data *data = user_data;
verify_data *data = userdata;
const char *finger_name = NULL;
char *msg;
msg = finger_str_to_msg(finger_name, data->driver, data->is_swipe);
D(data->pamh, "verify_finger_selected %s", msg);
send_info_msg (data->pamh, msg);
g_free (msg);
}
static gboolean verify_timeout_cb (gpointer user_data)
{
verify_data *data = user_data;
data->timed_out = TRUE;
send_info_msg (data->pamh, "Verification timed out");
g_main_loop_quit (data->loop);
return FALSE;
}
static int do_verify(GMainLoop *loop, pam_handle_t *pamh, DBusGProxy *dev, gboolean has_multiple_devices)
{
GError *error = NULL;
GHashTable *props;
DBusGProxy *p;
verify_data *data;
int ret;
data = g_new0 (verify_data, 1);
data->max_tries = max_tries;
data->pamh = pamh;
data->loop = loop;
/* Get some properties for the device */
p = dbus_g_proxy_new_from_proxy (dev, "org.freedesktop.DBus.Properties", NULL);
if (dbus_g_proxy_call (p, "GetAll", NULL, 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)) {
const char *scan_type;
if (has_multiple_devices)
data->driver = g_value_dup_string (g_hash_table_lookup (props, "name"));
scan_type = g_value_dup_string (g_hash_table_lookup (props, "scan-type"));
if (g_str_equal (scan_type, "swipe"))
data->is_swipe = TRUE;
g_hash_table_destroy (props);
if (sd_bus_message_read_basic (m, 's', &finger_name) < 0) {
pam_syslog (data->pamh, LOG_ERR, "Failed to parse VerifyFingerSelected signal: %m");
return 0;
}
g_object_unref (p);
msg = finger_str_to_msg(finger_name, data->driver, data->is_swipe);
if (debug)
pam_syslog (data->pamh, LOG_DEBUG, "verify_finger_selected %s", msg);
send_info_msg (data->pamh, msg);
free (msg);
dbus_g_proxy_add_signal(dev, "VerifyStatus", G_TYPE_STRING, G_TYPE_BOOLEAN, NULL);
dbus_g_proxy_add_signal(dev, "VerifyFingerSelected", G_TYPE_STRING, NULL);
dbus_g_proxy_connect_signal(dev, "VerifyStatus", G_CALLBACK(verify_result),
data, NULL);
dbus_g_proxy_connect_signal(dev, "VerifyFingerSelected", G_CALLBACK(verify_finger_selected),
data, NULL);
return 0;
}
/* See https://github.com/systemd/systemd/issues/14636 */
static int
get_property_string (sd_bus *bus,
const char *destination,
const char *path,
const char *interface,
const char *member,
sd_bus_error *error,
char **ret) {
sd_bus_message *reply = NULL;
const char *s;
char *n;
int r;
r = sd_bus_call_method(bus, destination, path, "org.freedesktop.DBus.Properties", "Get", error, &reply, "ss", interface, member);
if (r < 0)
return r;
r = sd_bus_message_enter_container(reply, 'v', "s");
if (r < 0)
goto fail;
r = sd_bus_message_read_basic(reply, 's', &s);
if (r < 0)
goto fail;
n = strdup(s);
if (!n) {
r = -ENOMEM;
goto fail;
}
sd_bus_message_unref (reply);
*ret = n;
return 0;
fail:
if (reply != NULL)
sd_bus_message_unref (reply);
return sd_bus_error_set_errno(error, r);
}
static int
do_verify (pam_handle_t *pamh,
sd_bus *bus,
const char *dev,
bool has_multiple_devices)
{
verify_data *data;
sd_bus_slot *verify_status_slot, *verify_finger_selected_slot;
char *scan_type = NULL;
int ret;
int r;
data = calloc (1, sizeof(verify_data));
data->max_tries = max_tries;
data->pamh = pamh;
/* Get some properties for the device */
r = get_property_string (bus,
"net.reactivated.Fprint",
dev,
"net.reactivated.Fprint.Device",
"scan-type",
NULL,
&scan_type);
if (r < 0)
pam_syslog (data->pamh, LOG_ERR, "Failed to get scan-type for %s: %d", dev, r);
if (debug)
pam_syslog (data->pamh, LOG_DEBUG, "scan-type for %s: %s", dev, scan_type);
if (str_equal (scan_type, "swipe"))
data->is_swipe = true;
free (scan_type);
if (has_multiple_devices) {
get_property_string (bus,
"net.reactivated.Fprint",
dev,
"net.reactivated.Fprint.Device",
"name",
NULL,
&data->driver);
if (r < 0)
pam_syslog (data->pamh, LOG_ERR, "Failed to get driver name for %s: %d", dev, r);
if (debug && r == 0)
pam_syslog (data->pamh, LOG_DEBUG, "driver name for %s: %s", dev, data->driver);
}
verify_status_slot = NULL;
sd_bus_match_signal (bus,
&verify_status_slot,
"net.reactivated.Fprint",
dev,
"net.reactivated.Fprint.Device",
"VerifyStatus",
verify_result,
data);
verify_finger_selected_slot = NULL;
sd_bus_match_signal (bus,
&verify_finger_selected_slot,
"net.reactivated.Fprint",
dev,
"net.reactivated.Fprint.Device",
"VerifyFingerSelected",
verify_finger_selected,
data);
ret = PAM_AUTH_ERR;
while (ret == PAM_AUTH_ERR && data->max_tries > 0) {
GSource *source;
uint64_t verification_end = now () + (timeout * USEC_PER_SEC);
sd_bus_message *m = NULL;
sd_bus_error error = SD_BUS_ERROR_NULL;
/* Set up the timeout on our non-default context */
source = g_timeout_source_new_seconds (timeout);
g_source_attach (source, g_main_loop_get_context (loop));
g_source_set_callback (source, verify_timeout_cb, data, NULL);
data->timed_out = false;
data->timed_out = FALSE;
r = sd_bus_call_method (bus,
"net.reactivated.Fprint",
dev,
"net.reactivated.Fprint.Device",
"VerifyStart",
&error,
&m,
"s",
"any");
if (!dbus_g_proxy_call (dev, "VerifyStart", &error, G_TYPE_STRING, "any", G_TYPE_INVALID, G_TYPE_INVALID)) {
if (dbus_g_error_has_name(error, "net.reactivated.Fprint.Error.NoEnrolledPrints"))
if (r < 0) {
if (sd_bus_error_has_name (&error, "net.reactivated.Fprint.Error.NoEnrolledPrints"))
ret = PAM_USER_UNKNOWN;
D(pamh, "VerifyStart failed: %s", error->message);
g_error_free (error);
g_source_destroy (source);
g_source_unref (source);
if (debug)
pam_syslog (pamh, LOG_DEBUG, "VerifyStart failed: %s", error.message);
sd_bus_error_free (&error);
break;
}
g_main_loop_run (loop);
for (;;) {
int64_t wait_time;
g_source_destroy (source);
g_source_unref (source);
wait_time = verification_end - now();
if (wait_time <= 0)
break;
r = sd_bus_process (bus, NULL);
if (r < 0)
break;
if (data->result != NULL)
break;
if (r == 0) {
if (debug) {
pam_syslog(pamh, LOG_DEBUG, "Waiting for %"PRId64" seconds (%"PRId64" usecs)",
wait_time / USEC_PER_SEC,
wait_time);
}
r = sd_bus_wait (bus, wait_time);
if (r < 0)
break;
}
}
if (now () >= verification_end) {
data->timed_out = true;
send_info_msg (data->pamh, _("Verification timed out"));
}
/* Ignore errors from VerifyStop */
dbus_g_proxy_call (dev, "VerifyStop", NULL, G_TYPE_INVALID, G_TYPE_INVALID);
sd_bus_call_method (bus,
"net.reactivated.Fprint",
dev,
"net.reactivated.Fprint.Device",
"VerifyStop",
NULL,
NULL,
NULL,
NULL);
if (data->timed_out) {
ret = PAM_AUTHINFO_UNAVAIL;
break;
} else {
if (g_str_equal (data->result, "verify-no-match")) {
if (str_equal (data->result, "verify-no-match")) {
send_err_msg (data->pamh, "Failed to match fingerprint");
ret = PAM_AUTH_ERR;
} else if (g_str_equal (data->result, "verify-match"))
} else if (str_equal (data->result, "verify-match")) {
ret = PAM_SUCCESS;
else if (g_str_equal (data->result, "verify-unknown-error"))
break;
} else if (str_equal (data->result, "verify-unknown-error")) {
ret = PAM_AUTHINFO_UNAVAIL;
else if (g_str_equal (data->result, "verify-disconnected")) {
} else if (str_equal (data->result, "verify-disconnected")) {
ret = PAM_AUTHINFO_UNAVAIL;
g_free (data->result);
free (data->result);
break;
} else {
send_info_msg (data->pamh, "An unknown error occurred");
send_info_msg (data->pamh, _("An unknown error occurred"));
ret = PAM_AUTH_ERR;
g_free (data->result);
free (data->result);
break;
}
g_free (data->result);
free (data->result);
data->result = NULL;
}
data->max_tries--;
}
dbus_g_proxy_disconnect_signal(dev, "VerifyStatus", G_CALLBACK(verify_result), data);
dbus_g_proxy_disconnect_signal(dev, "VerifyFingerSelected", G_CALLBACK(verify_finger_selected), data);
if (data->max_tries == 0)
ret = PAM_MAXTRIES;
g_free (data->driver);
g_free (data);
sd_bus_slot_unref (verify_status_slot);
sd_bus_slot_unref (verify_finger_selected_slot);
if (data->result)
free (data->result);
free (data->driver);
free (data);
return ret;
}
static gboolean user_has_prints(DBusGProxy *dev, const char *username)
static bool
user_has_prints (pam_handle_t *pamh,
sd_bus *bus,
const char *dev,
const char *username)
{
char **fingers;
gboolean have_prints;
sd_bus_error error = SD_BUS_ERROR_NULL;
sd_bus_message *m = NULL;
size_t num_fingers = 0;
const char *s;
int r;
if (!dbus_g_proxy_call (dev, "ListEnrolledFingers", NULL,
G_TYPE_STRING, username, G_TYPE_INVALID,
G_TYPE_STRV, &fingers, G_TYPE_INVALID)) {
r = sd_bus_call_method (bus,
"net.reactivated.Fprint",
dev,
"net.reactivated.Fprint.Device",
"ListEnrolledFingers",
&error,
&m,
"s",
username);
if (r < 0) {
/* If ListEnrolledFingers fails then verification should
* also fail (both use the same underlying call), so we
* report FALSE here and bail out early. */
return FALSE;
* report false here and bail out early. */
if (debug) {
pam_syslog (pamh, LOG_DEBUG, "ListEnrolledFingers failed for %s: %s",
username, error.message);
}
sd_bus_error_free (&error);
return false;
}
have_prints = fingers != NULL && g_strv_length (fingers) > 0;
g_strfreev (fingers);
r = sd_bus_message_enter_container (m, 'a', "s");
if (r < 0) {
pam_syslog (pamh, LOG_ERR, "Failed to parse answer from ListEnrolledFingers(): %d", r);
goto out;
}
return have_prints;
num_fingers = 0;
while ((r = sd_bus_message_read_basic(m, 's', &s)) > 0)
num_fingers++;
sd_bus_message_exit_container (m);
out:
sd_bus_message_unref (m);
return (num_fingers > 0);
}
static void release_device(pam_handle_t *pamh, DBusGProxy *dev)
static void
release_device (pam_handle_t *pamh,
sd_bus *bus,
const char *dev)
{
GError *error = NULL;
if (!dbus_g_proxy_call (dev, "Release", &error, G_TYPE_INVALID, G_TYPE_INVALID)) {
D(pamh, "ReleaseDevice failed: %s\n", error->message);
g_error_free (error);
sd_bus_error error = SD_BUS_ERROR_NULL;
int r;
r = sd_bus_call_method (bus,
"net.reactivated.Fprint",
dev,
"net.reactivated.Fprint.Device",
"Release",
&error,
NULL,
NULL,
NULL);
if (r < 0) {
pam_syslog (pamh, LOG_ERR, "ReleaseDevice failed: %s", error.message);
sd_bus_error_free (&error);
}
}
static gboolean claim_device(pam_handle_t *pamh, DBusGProxy *dev, const char *username)
static bool
claim_device (pam_handle_t *pamh,
sd_bus *bus,
const char *dev,
const char *username)
{
GError *error = NULL;
sd_bus_error error = SD_BUS_ERROR_NULL;
int r;
if (!dbus_g_proxy_call (dev, "Claim", &error, G_TYPE_STRING, username, G_TYPE_INVALID, G_TYPE_INVALID)) {
D(pamh, "failed to claim device %s\n", error->message);
g_error_free (error);
return FALSE;
r = sd_bus_call_method (bus,
"net.reactivated.Fprint",
dev,
"net.reactivated.Fprint.Device",
"Claim",
&error,
NULL,
"s",
username);
if (r < 0) {
if (debug)
pam_syslog (pamh, LOG_DEBUG, "failed to claim device %s", error.message);
sd_bus_error_free (&error);
return false;
}
return TRUE;
return true;
}
static int do_auth(pam_handle_t *pamh, const char *username)
{
DBusGProxy *manager;
DBusGConnection *connection;
DBusGProxy *dev;
GMainLoop *loop;
gboolean have_prints;
gboolean has_multiple_devices;
char *dev;
bool have_prints;
bool has_multiple_devices;
int ret = PAM_AUTHINFO_UNAVAIL;
sd_bus *bus = NULL;
manager = create_manager (pamh, &connection, &loop);
if (manager == NULL)
return PAM_AUTHINFO_UNAVAIL;
dev = open_device(pamh, connection, manager, &has_multiple_devices);
g_object_unref (manager);
if (!dev) {
unref_loop (loop);
close_and_unref (connection);
if (sd_bus_open_system (&bus) < 0) {
pam_syslog (pamh, LOG_ERR, "Error with getting the bus: %m");
return PAM_AUTHINFO_UNAVAIL;
}
have_prints = user_has_prints(dev, username);
D(pamh, "prints registered: %s\n", have_prints ? "yes" : "no");
if (have_prints) {
if (claim_device (pamh, dev, username)) {
ret = do_verify (loop, pamh, dev, has_multiple_devices);
release_device (pamh, dev);
}
dev = open_device (pamh, bus, &has_multiple_devices);
if (dev == NULL) {
sd_bus_unref (bus);
return PAM_AUTHINFO_UNAVAIL;
}
unref_loop (loop);
g_object_unref (dev);
close_and_unref (connection);
have_prints = user_has_prints (pamh, bus, dev, username);
if (debug)
pam_syslog (pamh, LOG_DEBUG, "prints registered: %s\n", have_prints ? "yes" : "no");
if (!have_prints)
goto out;
if (claim_device (pamh, bus, dev, username)) {
ret = do_verify (pamh, bus, dev, has_multiple_devices);
release_device (pamh, bus, dev);
}
out:
free (dev);
sd_bus_unref (bus);
return ret;
}
PAM_EXTERN int pam_sm_authenticate(pam_handle_t *pamh, int flags, int argc,
const char **argv)
static bool
is_remote (pam_handle_t *pamh)
{
const char *rhost = NULL;
const char *username;
guint i;
int r;
bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
#if !GLIB_CHECK_VERSION (2, 36, 0)
g_type_init();
#endif
dbus_g_object_register_marshaller (fprintd_marshal_VOID__STRING_BOOLEAN,
G_TYPE_NONE, G_TYPE_STRING, G_TYPE_BOOLEAN, G_TYPE_INVALID);
pam_get_item(pamh, PAM_RHOST, (const void **)(const void*) &rhost);
@ -481,32 +600,78 @@ PAM_EXTERN int pam_sm_authenticate(pam_handle_t *pamh, int flags, int argc,
* "localhost" if the host is local.
* We want to not run for known remote hosts */
if (rhost != NULL &&
rhost != '\0' &&
*rhost != '\0' &&
strcmp (rhost, "localhost") != 0) {
return PAM_AUTHINFO_UNAVAIL;
return true;
}
if (sd_session_is_remote (NULL) > 0)
return true;
return false;
}
PAM_EXTERN int pam_sm_authenticate(pam_handle_t *pamh, int flags, int argc,
const char **argv)
{
const char *username;
unsigned i;
int r;
bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
if (is_remote (pamh))
return PAM_AUTHINFO_UNAVAIL;
r = pam_get_user(pamh, &username, NULL);
if (r != PAM_SUCCESS)
return PAM_AUTHINFO_UNAVAIL;
for (i = 0; i < argc; i++) {
if (argv[i] != NULL) {
if(g_str_equal (argv[i], "debug")) {
g_message ("debug on");
debug = TRUE;
if (str_equal (argv[i], "debug")) {
pam_syslog (pamh, LOG_DEBUG, "debug on");
debug = true;
} else if (str_has_prefix (argv[i], DEBUG_MATCH)) {
pam_syslog (pamh, LOG_DEBUG, "debug on");
const char *value;
value = argv[i] + strlen (DEBUG_MATCH);
if (str_equal (value, "on") ||
str_equal (value, "true") ||
str_equal (value, "1")) {
pam_syslog (pamh, LOG_DEBUG, "debug on");
debug = true;
} else if (str_equal (value, "off") ||
str_equal (value, "false") ||
str_equal (value, "0")) {
debug = false;
} else {
pam_syslog (pamh, LOG_DEBUG, "invalid debug value '%s', disabling", value);
}
else if (strncmp(argv[i], MAX_TRIES_MATCH, strlen (MAX_TRIES_MATCH)) == 0 && strlen(argv[i]) == strlen (MAX_TRIES_MATCH) + 1) {
} else if (str_has_prefix (argv[i], MAX_TRIES_MATCH) && strlen(argv[i]) == strlen (MAX_TRIES_MATCH) + 1) {
max_tries = atoi (argv[i] + strlen (MAX_TRIES_MATCH));
if (max_tries < 1)
max_tries = DEFAULT_MAX_TRIES;
D(pamh, "max_tries specified as: %d", max_tries);
if (max_tries < 1) {
if (debug) {
pam_syslog (pamh, LOG_DEBUG, "invalid max tries '%s', using %d",
argv[i] + strlen (MAX_TRIES_MATCH), DEFAULT_MAX_TRIES);
}
else if (strncmp(argv[i], TIMEOUT_MATCH, strlen (TIMEOUT_MATCH)) == 0 && strlen(argv[i]) <= strlen (TIMEOUT_MATCH) + 2) {
max_tries = DEFAULT_MAX_TRIES;
}
if (debug)
pam_syslog (pamh, LOG_DEBUG, "max_tries specified as: %d", max_tries);
} else if (str_has_prefix (argv[i], TIMEOUT_MATCH) && strlen(argv[i]) <= strlen (TIMEOUT_MATCH) + 2) {
timeout = atoi (argv[i] + strlen (TIMEOUT_MATCH));
if (timeout < 10)
timeout = DEFAULT_TIMEOUT;
D(pamh, "timeout specified as: %d", timeout);
if (timeout < MIN_TIMEOUT) {
if (debug) {
pam_syslog (pamh, LOG_DEBUG, "timeout %d secs too low, using %d",
timeout, MIN_TIMEOUT);
}
timeout = MIN_TIMEOUT;
} else if (debug) {
pam_syslog (pamh, LOG_DEBUG, "timeout specified as: %d secs", timeout);
}
}
}
}

6
pam/pam_fprintd.ver Normal file
View File

@ -0,0 +1,6 @@
{
global:
pam_*;
local:
*;
};

View File

@ -3,3 +3,4 @@ src/main.c
src/manager.c
src/device.c
pam/fingerprint-strings.h
pam/pam_fprintd.c

14
po/check-translations.sh Executable file
View File

@ -0,0 +1,14 @@
#!/bin/bash
po_dir=$(dirname $0)
for i in $po_dir/*.po ; do
if ! grep -q `basename $i | sed 's,.po,,'` $po_dir/LINGUAS; then
echo '**********************************';
echo '***' `basename $i | sed 's,.po,,'` missing from po/LINGUAS '***' ;
echo '**********************************';
exit 1;
fi;
done;
exit 0

6
po/meson.build Normal file
View File

@ -0,0 +1,6 @@
i18n.gettext(meson.project_name(), preset: 'glib')
test('check-translations',
find_program('check-translations.sh'),
suite: ['dist'],
)

View File

@ -1,55 +0,0 @@
BUILT_SOURCES = manager-dbus-glue.h device-dbus-glue.h $(MARSHALFILES) $(interfaces_DATA)
noinst_HEADERS = $(BUILT_SOURCES)
CLEANFILES = $(BUILT_SOURCES)
EXTRA_DIST = manager.xml device.xml fprintd-marshal.list
libexec_PROGRAMS = fprintd
noinst_LTLIBRARIES = libfprintd.la
AM_CFLAGS = \
$(WARN_CFLAGS) \
$(FPRINT_CFLAGS) \
$(DAEMON_CFLAGS) \
-DG_LOG_DOMAIN=\""fprintd"\" \
-DLOCALEDIR=\""$(datadir)/locale"\" \
-DPLUGINDIR=\""$(libdir)/fprintd/modules"\"
libfprintd_la_SOURCES = \
manager.c device.c \
$(MARSHALFILES) \
fprintd.h
libfprintd_la_LIBADD = $(FPRINT_LIBS) $(DAEMON_LIBS)
libfprintd_la_LDFLAGS = -no-undefined
fprintd_SOURCES = \
main.c \
loop.c loop.h \
file_storage.c file_storage.h storage.h
fprintd_LDADD = libfprintd.la
interfaces_DATA = net.reactivated.Fprint.Manager.xml net.reactivated.Fprint.Device.xml
net.reactivated.Fprint.Manager.xml: manager.xml
cat $< > $@
net.reactivated.Fprint.Device.xml: device.xml
cat $< > $@
interfacesdir = $(datadir)/dbus-1/interfaces/
manager-dbus-glue.h: manager.xml
dbus-binding-tool --prefix=fprint_manager --mode=glib-server $< --output=$@
device-dbus-glue.h: device.xml
dbus-binding-tool --prefix=fprint_device --mode=glib-server $< --output=$@
MARSHALFILES = fprintd-marshal.c fprintd-marshal.h
GLIB_GENMARSHAL=`pkg-config --variable=glib_genmarshal glib-2.0`
fprintd-marshal.h: fprintd-marshal.list
( $(GLIB_GENMARSHAL) --prefix=fprintd_marshal $(srcdir)/fprintd-marshal.list --header > fprintd-marshal.h )
fprintd-marshal.c: fprintd-marshal.h
( $(GLIB_GENMARSHAL) --prefix=fprintd_marshal $(srcdir)/fprintd-marshal.list --body --header > fprintd-marshal.c )
install-data-hook:
if test -w $(DESTDIR)$(prefix)/; then \
mkdir -p $(DESTDIR)$(localstatedir)/lib/fprint; \
fi

File diff suppressed because it is too large Load Diff

View File

@ -245,6 +245,16 @@
The user should remove their finger from the reader and retry scanning their finger, the enrollment is still ongoing.
</doc:definition>
</doc:item>
<doc:item>
<doc:term>enroll-data-full</doc:term>
<doc:definition>
No further prints can be enrolled on this device, <doc:ref type="method" to="Device.EnrollStop">Device.EnrollStop</doc:ref> should now be called.
<doc:ref type="method" to="DeleteEnrolledFingers2">Delete other prints</doc:ref> from the device first to continue
(e.g. from other users). Note that old prints or prints from other operating systems may be deleted automatically
to resolve this error without any notification.
</doc:definition>
</doc:item>
<doc:item>
<doc:term>enroll-disconnected</doc:term>
<doc:definition>
@ -301,6 +311,29 @@
<doc:para>
Delete all the enrolled fingerprints for the chosen user.
</doc:para>
<doc:para>
This call only exists for compatibility reasons, you should instead claim the device using
<doc:ref type="method" to="Device.Claim">Device.Claim</doc:ref> and then call
<doc:ref type="method" to="DeleteEnrolledFingers2">DeleteEnrolledFingers2</doc:ref>.
</doc:para>
</doc:description>
<doc:errors>
<doc:error name="&ERROR_PERMISSION_DENIED;">if the caller lacks the appropriate PolicyKit authorization</doc:error>
</doc:errors>
</doc:doc>
</method>
<!-- ************************************************************ -->
<method name="DeleteEnrolledFingers2">
<annotation name="org.freedesktop.DBus.GLib.Async" value="" />
<doc:doc>
<doc:description>
<doc:para>
Delete all the enrolled fingerprints for the user currently claiming the device with <doc:ref type="method" to="Device.Claim">Device.Claim</doc:ref>.
</doc:para>
</doc:description>
<doc:errors>
@ -458,7 +491,7 @@
<doc:doc>
<doc:description>
<doc:para>
Start enrollemnt for the selected finger. You need to have claimed the device using
Start enrollment for the selected finger. You need to have claimed the device using
<doc:ref type="method" to="Device.Claim">Device.Claim</doc:ref> before calling
this method. Enrollment status is sent through <doc:ref type="signal" to="Device::EnrollStatus">Device::EnrollStatus</doc:ref>.
</doc:para>
@ -468,7 +501,7 @@
<doc:error name="&ERROR_PERMISSION_DENIED;">if the caller lacks the appropriate PolicyKit authorization</doc:error>
<doc:error name="&ERROR_CLAIM_DEVICE;">if the device was not claimed</doc:error>
<doc:error name="&ERROR_ALREADY_IN_USE;">if the device was already being used</doc:error>
<doc:error name="&ERROR_INVALID_FINGERNAME;">if there are no enrolled prints for the chosen user</doc:error>
<doc:error name="&ERROR_INVALID_FINGERNAME;">if the finger name passed is invalid</doc:error>
<doc:error name="&ERROR_INTERNAL;">if there was an internal error</doc:error>
</doc:errors>

View File

@ -35,29 +35,34 @@
#include <glib.h>
#include <glib/gstdio.h>
#include <libfprint/fprint.h>
#include <fprint.h>
#include "file_storage.h"
#define FILE_STORAGE_PATH "/var/lib/fprint"
#define DIR_PERMS 0700
#define FP_FINGER_IS_VALID(finger) \
((finger) >= LEFT_THUMB && (finger) <= RIGHT_LITTLE)
static char *get_path_to_storedir(uint16_t driver_id, uint32_t devtype, char *base_store)
static const char *get_storage_path(void)
{
char idstr[5];
char devtypestr[9];
const char *path;
g_snprintf(idstr, sizeof(idstr), "%04x", driver_id);
g_snprintf(devtypestr, sizeof(devtypestr), "%08x", devtype);
/* 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;
return g_build_filename(base_store, idstr, devtypestr, NULL);
return FILE_STORAGE_PATH;
}
static char *__get_path_to_print(uint16_t driver_id, uint32_t devtype,
enum fp_finger finger, char *base_store)
static char *get_path_to_storedir(const char *driver, const char * device_id, char *base_store)
{
return g_build_filename(base_store, driver, device_id, NULL);
}
static char *__get_path_to_print(const char *driver, const char * device_id,
FpFinger finger, char *base_store)
{
char *dirpath;
char *path;
@ -65,90 +70,85 @@ static char *__get_path_to_print(uint16_t driver_id, uint32_t devtype,
g_snprintf(fingername, 2, "%x", finger);
dirpath = get_path_to_storedir(driver_id, devtype, base_store);
dirpath = get_path_to_storedir(driver, device_id, base_store);
path = g_build_filename(dirpath, fingername, NULL);
g_free(dirpath);
return path;
}
static char *get_path_to_print(struct fp_dev *dev, enum fp_finger finger, char *base_store)
static char *get_path_to_print(FpDevice *dev, FpFinger finger, char *base_store)
{
return __get_path_to_print(fp_driver_get_driver_id(fp_dev_get_driver(dev)),
fp_dev_get_devtype(dev), finger, base_store);
return __get_path_to_print(fp_device_get_driver (dev),
fp_device_get_device_id(dev),
finger,
base_store);
}
static char *get_path_to_print_dscv(struct fp_dscv_dev *dev, enum fp_finger finger, char *base_store)
static char *get_path_to_print_dscv(FpDevice *dev, FpFinger finger, char *base_store)
{
return __get_path_to_print(fp_driver_get_driver_id(fp_dscv_dev_get_driver(dev)),
fp_dscv_dev_get_devtype(dev), finger, base_store);
return __get_path_to_print(fp_device_get_driver (dev),
fp_device_get_device_id(dev),
finger,
base_store);
}
static char *file_storage_get_basestore_for_username(const char *username)
{
return g_build_filename(FILE_STORAGE_PATH, username, NULL);
return g_build_filename(get_storage_path(), username, NULL);
}
/* if username == NULL function will use current username */
int file_storage_print_data_save(struct fp_print_data *data,
enum fp_finger finger, const char *username)
int file_storage_print_data_save(FpPrint *print)
{
GError *err = NULL;
char *path, *dirpath;
size_t len;
g_autoptr(GError) err = NULL;
g_autofree char *path = NULL;
g_autofree char *dirpath = NULL;
g_autofree char *base_store = NULL;
g_autofree char *buf = NULL;
gsize len;
int r;
char *base_store = NULL;
char *buf = NULL;
base_store = file_storage_get_basestore_for_username(username);
base_store = file_storage_get_basestore_for_username(fp_print_get_username (print));
len = fp_print_data_get_data(data, (guchar **) &buf);
if (!len) {
g_free(base_store);
if (!fp_print_serialize (print, (guchar **) &buf, &len, &err)) {
g_warning ("Error serializing data: %s", err->message);
return -ENOMEM;
}
path = __get_path_to_print(fp_print_data_get_driver_id(data), fp_print_data_get_devtype(data), finger, base_store);
path = __get_path_to_print(fp_print_get_driver (print),
fp_print_get_device_id (print),
fp_print_get_finger (print),
base_store);
dirpath = g_path_get_dirname(path);
r = g_mkdir_with_parents(dirpath, DIR_PERMS);
if (r < 0) {
g_debug("file_storage_print_data_save(): could not mkdir(\"%s\"): %s",
dirpath, g_strerror(r));
g_free(dirpath);
g_free(path);
goto out;
return r;
}
g_free(dirpath);
//fp_dbg("saving to %s", path);
g_file_set_contents(path, buf, len, &err);
g_free(path);
if (err) {
r = err->code;
g_debug("file_storage_print_data_save(): could not save '%s': %s",
path, err->message);
g_error_free(err);
/* FIXME interpret error codes */
goto out;
return err->code;
}
out:
g_clear_pointer(&buf, free);
g_clear_pointer(&base_store, g_free);
return r;
return 0;
}
static int load_from_file(char *path, struct fp_print_data **data)
static int load_from_file(char *path, FpPrint **print)
{
g_autoptr(GError) err = NULL;
gsize length;
char *contents;
GError *err = NULL;
struct fp_print_data *fdata;
g_autofree char *contents = NULL;
FpPrint *new;
//fp_dbg("from %s", path);
g_file_get_contents(path, &contents, &length, &err);
if (err) {
int r = err->code;
g_error_free(err);
/* FIXME interpret more error codes */
if (r == G_FILE_ERROR_NOENT)
return -ENOENT;
@ -156,71 +156,71 @@ static int load_from_file(char *path, struct fp_print_data **data)
return r;
}
fdata = fp_print_data_from_data((guchar *) contents, length);
g_free(contents);
if (!fdata)
new = fp_print_deserialize ((guchar *) contents, length, &err);
if (!new) {
g_print ("Error deserializing data: %s", err->message);
return -EIO;
*data = fdata;
}
*print = new;
return 0;
}
int file_storage_print_data_load(struct fp_dev *dev,
enum fp_finger finger, struct fp_print_data **data, const char *username)
int file_storage_print_data_load(FpDevice *dev,
FpFinger finger,
const char *username,
FpPrint **print)
{
gchar *path;
struct fp_print_data *fdata = NULL;
g_autofree gchar *path = NULL;
g_autofree gchar *base_store = NULL;
FpPrint *new = NULL;
int r;
char *base_store = NULL;
base_store = file_storage_get_basestore_for_username(username);
path = get_path_to_print(dev, finger, base_store);
r = load_from_file(path, &fdata);
r = load_from_file(path, &new);
g_debug ("file_storage_print_data_load(): loaded '%s' %s",
path, g_strerror(r));
g_free(path);
g_free(base_store);
if (r)
return r;
if (!fp_dev_supports_print_data(dev, fdata)) {
fp_print_data_free(fdata);
if (!fp_print_compatible (new, dev)) {
g_object_unref (new);
return -EINVAL;
}
*data = fdata;
*print = new;
return 0;
}
int file_storage_print_data_delete(struct fp_dscv_dev *dev,
enum fp_finger finger, const char *username)
int file_storage_print_data_delete(FpDevice *dev, FpFinger finger, const char *username)
{
g_autofree gchar *base_store = NULL;
g_autofree gchar *path = NULL;
int r;
char *base_store, *path;
base_store = file_storage_get_basestore_for_username(username);
path = get_path_to_print_dscv(dev, finger, base_store);
r = g_unlink(path);
g_debug("file_storage_print_data_delete(): unlink(\"%s\") %s",
path, g_strerror(r));
g_free(path);
g_free(base_store);
/* FIXME: cleanup empty directory */
return r;
return g_unlink(path);
}
static GSList *scan_dev_storedir(char *devpath, uint16_t driver_id,
uint32_t devtype, GSList *list)
static GSList *scan_dev_storedir(char *devpath,
GSList *list)
{
GError *err = NULL;
g_autoptr(GError) err = NULL;
const gchar *ent;
GDir *dir = g_dir_open(devpath, 0, &err);
if (!dir) {
g_debug("scan_dev_storedir(): opendir(\"%s\") failed: %s", devpath, err->message);
g_error_free(err);
return list;
}
@ -245,26 +245,46 @@ static GSList *scan_dev_storedir(char *devpath, uint16_t driver_id,
return list;
}
GSList *file_storage_discover_prints(struct fp_dscv_dev *dev, const char *username)
GSList *file_storage_discover_prints(FpDevice *dev, const char *username)
{
GSList *list = NULL;
char *base_store = NULL;
char *storedir = NULL;
g_autofree gchar *base_store = NULL;
g_autofree gchar *storedir = NULL;
base_store = file_storage_get_basestore_for_username(username);
storedir = get_path_to_storedir(fp_driver_get_driver_id(fp_dscv_dev_get_driver(dev)),
fp_dscv_dev_get_devtype(dev), base_store);
storedir = get_path_to_storedir(fp_device_get_driver (dev),
fp_device_get_device_id (dev),
base_store);
g_debug ("file_storage_discover_prints() for user '%s' in '%s'",
username, storedir);
list = scan_dev_storedir(storedir, fp_driver_get_driver_id(fp_dscv_dev_get_driver(dev)),
fp_dscv_dev_get_devtype(dev), list);
list = scan_dev_storedir(storedir, list);
g_free(base_store);
g_free(storedir);
return list;
}
GSList *file_storage_discover_users(void)
{
g_autoptr(GError) err = NULL;
GSList *list = NULL;
const gchar *ent;
GDir *dir = g_dir_open(get_storage_path(), 0, &err);
if (!dir) {
return list;
}
while ((ent = g_dir_read_name(dir))) {
/* ent is a username */
if (*ent == 0)
continue;
list = g_slist_prepend(list, g_strdup (ent));
}
g_dir_close(dir);
return list;
}

View File

@ -18,24 +18,22 @@
*
*/
#ifndef FILE_STORAGE_H
#pragma once
#define FILE_STORAGE_H
int file_storage_print_data_save(FpPrint *print);
int file_storage_print_data_save(struct fp_print_data *data,
enum fp_finger finger, const char *username);
int file_storage_print_data_load(FpDevice *dev,
FpFinger finger,
const char *username,
FpPrint **print);
int file_storage_print_data_load(struct fp_dev *dev,
enum fp_finger finger, struct fp_print_data **data, const char *username);
int file_storage_print_data_delete(struct fp_dscv_dev *dev,
enum fp_finger finger, const char *username);
int file_storage_print_data_delete(FpDevice *dev,
FpFinger finger,
const char *username);
int file_storage_init(void);
int file_storage_deinit(void);
GSList *file_storage_discover_prints(struct fp_dscv_dev *dev, const char *username);
#endif
GSList *file_storage_discover_prints(FpDevice *dev, const char *username);
GSList *file_storage_discover_users(void);

View File

@ -17,16 +17,15 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef __FPRINTD_H__
#define __FPRINTD_H__
#pragma once
#include <glib.h>
#include <dbus/dbus-glib.h>
#include <fprint.h>
/* General */
#define TIMEOUT 30
#define FPRINT_SERVICE_NAME "net.reactivated.Fprint"
extern DBusGConnection *fprintd_dbus_conn;
/* Errors */
GQuark fprint_error_quark(void);
@ -38,7 +37,7 @@ GType fprint_error_get_type(void);
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 occured */
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 */
@ -48,56 +47,29 @@ typedef enum {
/* Manager */
#define FPRINT_TYPE_MANAGER (fprint_manager_get_type())
#define FPRINT_MANAGER(object) (G_TYPE_CHECK_INSTANCE_CAST((object), FPRINT_TYPE_MANAGER, FprintManager))
#define FPRINT_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), FPRINT_TYPE_MANAGER, FprintManagerClass))
#define FPRINT_IS_MANAGER(object) (G_TYPE_CHECK_INSTANCE_TYPE((object), FPRINT_TYPE_MANAGER))
#define FPRINT_IS_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), FPRINT_TYPE_MANAGER))
#define FPRINT_MANAGER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), FPRINT_TYPE_MANAGER, FprintManagerClass))
G_DECLARE_FINAL_TYPE (FprintManager, fprint_manager, FPRINT, MANAGER, GObject)
struct FprintManager {
struct _FprintManager {
GObject parent;
};
struct FprintManagerClass {
GObjectClass parent;
};
typedef struct FprintManager FprintManager;
typedef struct FprintManagerClass FprintManagerClass;
FprintManager *fprint_manager_new(gboolean no_timeout);
GType fprint_manager_get_type(void);
/* Device */
#define FPRINT_TYPE_DEVICE (fprint_device_get_type())
#define FPRINT_DEVICE(object) (G_TYPE_CHECK_INSTANCE_CAST((object), FPRINT_DEVICE_TYPE, FprintDevice))
#define FPRINT_DEVICE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), FPRINT_DEVICE_TYPE, FprintDeviceClass))
#define FPRINT_IS_DEVICE(object) (G_TYPE_CHECK_INSTANCE_TYPE((object), FPRINT_TYPE_DEVICE))
#define FPRINT_IS_DEVICE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), FPRINT_TYPE_DEVICE))
#define FPRINT_DEVICE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), FPRINT_TYPE_DEVICE, FprintDeviceClass))
G_DECLARE_FINAL_TYPE (FprintDevice, fprint_device, FPRINT, DEVICE, GObject)
struct FprintDevice {
struct _FprintDevice {
GObject parent;
};
struct FprintDeviceClass {
GObjectClass parent;
};
typedef struct FprintDevice FprintDevice;
typedef struct FprintDeviceClass FprintDeviceClass;
FprintDevice *fprint_device_new(struct fp_dscv_dev *ddev);
GType fprint_device_get_type(void);
FprintDevice *fprint_device_new(FpDevice *dev);
guint32 _fprint_device_get_id(FprintDevice *rdev);
/* Print */
/* TODO */
/* Binding data included in main.c thorugh server-bindings.h which individual
/* 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;
#endif

View File

@ -1,196 +0,0 @@
/*
* fprint D-Bus daemon
* Copyright (C) 2008 Daniel Drake <dsd@gentoo.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "config.h"
#include <glib.h>
#include <libfprint/fprint.h>
#include <poll.h>
#include <stdlib.h>
#include "loop.h"
struct fdsource {
GSource source;
GSList *pollfds;
};
static gboolean source_prepare(GSource *source, gint *timeout)
{
int r;
struct timeval tv;
r = fp_get_next_timeout(&tv);
if (r == 0) {
*timeout = -1;
return FALSE;
}
if (!timerisset(&tv))
return TRUE;
*timeout = (tv.tv_sec * 1000) + (tv.tv_usec / 1000);
return FALSE;
}
static gboolean source_check(GSource *source)
{
struct fdsource *_fdsource = (struct fdsource *) source;
GSList *l;
struct timeval tv;
int r;
if (!_fdsource->pollfds)
return FALSE;
for (l = _fdsource->pollfds; l != NULL; l = l->next) {
GPollFD *pollfd = l->data;
if (pollfd->revents)
return TRUE;
}
r = fp_get_next_timeout(&tv);
if (r == 1 && !timerisset(&tv))
return TRUE;
return FALSE;
}
static gboolean source_dispatch(GSource *source, GSourceFunc callback,
gpointer data)
{
struct timeval zerotimeout = {
.tv_sec = 0,
.tv_usec = 0,
};
/* FIXME error handling */
fp_handle_events_timeout(&zerotimeout);
/* FIXME whats the return value used for? */
return TRUE;
}
static void source_finalize(GSource *source)
{
struct fdsource *_fdsource = (struct fdsource *) source;
GSList *l;
if (!_fdsource->pollfds)
return;
for (l = _fdsource->pollfds; l != NULL; l = l->next) {
GPollFD *pollfd = l->data;
g_source_remove_poll((GSource *) _fdsource, pollfd);
g_slice_free(GPollFD, pollfd);
_fdsource->pollfds = g_slist_delete_link(_fdsource->pollfds, l);
}
g_slist_free(_fdsource->pollfds);
}
static GSourceFuncs sourcefuncs = {
.prepare = source_prepare,
.check = source_check,
.dispatch = source_dispatch,
.finalize = source_finalize,
};
static struct fdsource *fdsource = NULL;
static void pollfd_add(int fd, short events)
{
GPollFD *pollfd;
pollfd = g_slice_new(GPollFD);
pollfd->fd = fd;
pollfd->events = 0;
pollfd->revents = 0;
if (events & POLLIN)
pollfd->events |= G_IO_IN;
if (events & POLLOUT)
pollfd->events |= G_IO_OUT;
fdsource->pollfds = g_slist_prepend(fdsource->pollfds, pollfd);
g_source_add_poll((GSource *) fdsource, pollfd);
}
static void pollfd_added_cb(int fd, short events)
{
g_debug("now monitoring fd %d", fd);
pollfd_add(fd, events);
}
static void pollfd_removed_cb(int fd)
{
GSList *l;
g_debug("no longer monitoring fd %d", fd);
if (!fdsource->pollfds) {
g_debug("cannot remove from list as list is empty?");
return;
}
for (l = fdsource->pollfds; l != NULL; l = l->next) {
GPollFD *pollfd = l->data;
if (pollfd->fd != fd)
continue;
g_source_remove_poll((GSource *) fdsource, pollfd);
g_slice_free(GPollFD, pollfd);
fdsource->pollfds = g_slist_delete_link(fdsource->pollfds, l);
return;
}
g_error("couldn't find fd %d in list\n", fd);
}
int setup_pollfds(void)
{
ssize_t numfds;
size_t i;
struct fp_pollfd *fpfds;
GSource *gsource;
gsource = g_source_new(&sourcefuncs, sizeof(struct fdsource));
fdsource = (struct fdsource *) gsource;
fdsource->pollfds = NULL;
numfds = fp_get_pollfds(&fpfds);
if (numfds < 0) {
if (fpfds)
free(fpfds);
return (int) numfds;
} else if (numfds > 0) {
for (i = 0; i < numfds; i++) {
struct fp_pollfd *fpfd = &fpfds[i];
pollfd_add(fpfd->fd, fpfd->events);
}
}
free(fpfds);
fp_set_pollfd_notifiers(pollfd_added_cb, pollfd_removed_cb);
g_source_attach(gsource, NULL);
return 0;
}

View File

@ -1,27 +0,0 @@
/*
* Copyright (C) 2008 Daniel Drake <dsd@gentoo.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
*/
#ifndef POLL_H
#define POLL_H
int setup_pollfds(void);
#endif

View File

@ -25,16 +25,18 @@
#include <dbus/dbus-glib-bindings.h>
#include <glib.h>
#include <glib/gi18n.h>
#include <libfprint/fprint.h>
#include <fprint.h>
#include <glib-object.h>
#include <glib-unix.h>
#include <gmodule.h>
#include "fprintd.h"
#include "loop.h"
#include "storage.h"
#include "file_storage.h"
extern DBusGConnection *fprintd_dbus_conn;
fp_storage store;
DBusGConnection *fprintd_dbus_conn = NULL;
static gboolean no_timeout = FALSE;
static gboolean g_fatal_warnings = FALSE;
@ -47,6 +49,7 @@ set_storage_file (void)
store.print_data_load = &file_storage_print_data_load;
store.print_data_delete = &file_storage_print_data_delete;
store.discover_prints = &file_storage_discover_prints;
store.discover_users = &file_storage_discover_users;
}
static gboolean
@ -127,6 +130,14 @@ static const GOptionEntry entries[] = {
{ NULL }
};
static gboolean sigterm_callback(gpointer data)
{
GMainLoop *loop = data;
g_main_loop_quit (loop);
return FALSE;
}
int main(int argc, char **argv)
{
GOptionContext *context;
@ -135,7 +146,8 @@ int main(int argc, char **argv)
FprintManager *manager;
DBusGProxy *driver_proxy;
guint32 request_name_ret;
int r = 0;
setlocale (LC_ALL, "");
bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
@ -144,10 +156,6 @@ int main(int argc, char **argv)
context = g_option_context_new ("Fingerprint handler daemon");
g_option_context_add_main_entries (context, entries, GETTEXT_PACKAGE);
#if !GLIB_CHECK_VERSION (2, 36, 0)
g_type_init();
#endif
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);
@ -172,37 +180,14 @@ int main(int argc, char **argv)
driver_proxy = dbus_g_proxy_new_for_name(fprintd_dbus_conn,
DBUS_SERVICE_DBUS, DBUS_PATH_DBUS, DBUS_INTERFACE_DBUS);
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);
return 1;
}
if (request_name_ret != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) {
g_warning ("Got result code %u from requesting name", request_name_ret);
return 1;
}
/* Load the configuration file,
* and the default storage plugin */
if (!load_conf())
set_storage_file ();
store.init ();
r = fp_init();
if (r < 0) {
g_warning("fprint init failed with error %d\n", r);
return r;
}
loop = g_main_loop_new(NULL, FALSE);
r = setup_pollfds();
if (r < 0) {
g_warning("pollfd setup failed\n");
goto err;
}
g_unix_signal_add (SIGTERM, sigterm_callback, loop);
g_debug("Launching FprintObject");
@ -210,6 +195,22 @@ int main(int argc, char **argv)
* all fprintd users */
manager = fprint_manager_new(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);
g_debug("entering main loop");
@ -218,8 +219,6 @@ int main(int argc, char **argv)
g_object_unref (manager);
err:
fp_exit();
return 0;
}

View File

@ -22,12 +22,12 @@
#include <dbus/dbus-glib-bindings.h>
#include <glib.h>
#include <glib/gi18n.h>
#include <libfprint/fprint.h>
#include <fprint.h>
#include <glib-object.h>
#include "fprintd.h"
DBusGConnection *fprintd_dbus_conn;
extern DBusGConnection *fprintd_dbus_conn;
static gboolean fprint_manager_get_devices(FprintManager *manager,
GPtrArray **devices, GError **error);
@ -35,27 +35,25 @@ static gboolean fprint_manager_get_default_device(FprintManager *manager,
const char **device, GError **error);
#include "manager-dbus-glue.h"
static GObjectClass *parent_class = NULL;
G_DEFINE_TYPE(FprintManager, fprint_manager, G_TYPE_OBJECT);
typedef struct
{
FpContext *context;
GSList *dev_registry;
gboolean no_timeout;
guint timeout_id;
} FprintManagerPrivate;
#define FPRINT_MANAGER_GET_PRIVATE(o) \
(G_TYPE_INSTANCE_GET_PRIVATE ((o), FPRINT_TYPE_MANAGER, FprintManagerPrivate))
G_DEFINE_TYPE_WITH_CODE(FprintManager, fprint_manager, G_TYPE_OBJECT, G_ADD_PRIVATE (FprintManager))
static void fprint_manager_finalize(GObject *object)
{
FprintManagerPrivate *priv = FPRINT_MANAGER_GET_PRIVATE (object);
FprintManagerPrivate *priv = fprint_manager_get_instance_private (FPRINT_MANAGER (object));
g_clear_object (&priv->context);
g_slist_free(priv->dev_registry);
G_OBJECT_CLASS(parent_class)->finalize(object);
G_OBJECT_CLASS(fprint_manager_parent_class)->finalize(object);
}
static void fprint_manager_class_init(FprintManagerClass *klass)
@ -64,10 +62,7 @@ static void fprint_manager_class_init(FprintManagerClass *klass)
&dbus_glib_fprint_manager_object_info);
dbus_g_error_domain_register (FPRINT_ERROR, FPRINT_ERROR_DBUS_INTERFACE, FPRINT_TYPE_ERROR);
g_type_class_add_private ((GObjectClass *) klass, sizeof (FprintManagerPrivate));
G_OBJECT_CLASS(klass)->finalize = fprint_manager_finalize;
parent_class = g_type_class_peek_parent(klass);
}
static gchar *get_device_path(FprintDevice *rdev)
@ -87,7 +82,7 @@ fprint_manager_timeout_cb (FprintManager *manager)
static void
fprint_manager_in_use_notified (FprintDevice *rdev, GParamSpec *spec, FprintManager *manager)
{
FprintManagerPrivate *priv = FPRINT_MANAGER_GET_PRIVATE (manager);
FprintManagerPrivate *priv = fprint_manager_get_instance_private (manager);
guint num_devices_used = 0;
GSList *l;
gboolean in_use;
@ -112,32 +107,79 @@ fprint_manager_in_use_notified (FprintDevice *rdev, GParamSpec *spec, FprintMana
}
static void
fprint_manager_init (FprintManager *manager)
device_added_cb (FprintManager *manager, FpDevice *device, FpContext *context)
{
FprintManagerPrivate *priv = FPRINT_MANAGER_GET_PRIVATE (manager);
struct fp_dscv_dev **discovered_devs = fp_discover_devs();
struct fp_dscv_dev *ddev;
int i = 0;
dbus_g_connection_register_g_object(fprintd_dbus_conn,
"/net/reactivated/Fprint/Manager", G_OBJECT(manager));
if (discovered_devs == NULL)
return;
while ((ddev = discovered_devs[i++]) != NULL) {
FprintDevice *rdev = fprint_device_new(ddev);
gchar *path;
FprintManagerPrivate *priv = fprint_manager_get_instance_private (manager);
FprintDevice *rdev = fprint_device_new(device);
g_autofree gchar *path = NULL;
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);
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));
g_free(path);
}
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;
for (item = priv->dev_registry; item; item = item->next) {
FprintDevice *rdev;
g_autoptr(FpDevice) dev = NULL;
rdev = item->data;
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_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;
}
/* The device that disappeared might have been in-use.
* Do we need to do anything else in this case to clean up more gracefully? */
fprint_manager_in_use_notified (NULL, NULL, manager);
}
static void
fprint_manager_init (FprintManager *manager)
{
FprintManagerPrivate *priv = fprint_manager_get_instance_private (manager);
priv->context = fp_context_new ();
/* And register the signals for initial enumeration and hotplug. */
g_signal_connect_object (priv->context,
"device-added",
(GCallback) device_added_cb,
manager,
G_CONNECT_SWAPPED);
g_signal_connect_object (priv->context,
"device-removed",
(GCallback) device_removed_cb,
manager,
G_CONNECT_SWAPPED);
/* Prepare everything by enumerating all devices. */
fp_context_enumerate (priv->context);
dbus_g_connection_register_g_object(fprintd_dbus_conn,
"/net/reactivated/Fprint/Manager", G_OBJECT(manager));
}
FprintManager *fprint_manager_new(gboolean no_timeout)
@ -146,7 +188,7 @@ FprintManager *fprint_manager_new(gboolean no_timeout)
GObject *object;
object = g_object_new(FPRINT_TYPE_MANAGER, NULL);
priv = FPRINT_MANAGER_GET_PRIVATE (object);
priv = fprint_manager_get_instance_private (FPRINT_MANAGER (object));
priv->no_timeout = no_timeout;
if (!priv->no_timeout)
@ -158,11 +200,15 @@ FprintManager *fprint_manager_new(gboolean no_timeout)
static gboolean fprint_manager_get_devices(FprintManager *manager,
GPtrArray **devices, GError **error)
{
FprintManagerPrivate *priv = FPRINT_MANAGER_GET_PRIVATE (manager);
GSList *elem = g_slist_reverse(g_slist_copy(priv->dev_registry));
FprintManagerPrivate *priv = fprint_manager_get_instance_private (manager);
GSList *elem;
GSList *l;
int num_open = g_slist_length(elem);
GPtrArray *devs = g_ptr_array_sized_new(num_open);
int num_open;
GPtrArray *devs;
elem = g_slist_reverse(g_slist_copy(priv->dev_registry));
num_open = g_slist_length(elem);
devs = g_ptr_array_sized_new(num_open);
if (num_open > 0) {
for (l = elem; l != NULL; l = l->next) {
@ -180,9 +226,12 @@ static gboolean fprint_manager_get_devices(FprintManager *manager,
static gboolean fprint_manager_get_default_device(FprintManager *manager,
const char **device, GError **error)
{
FprintManagerPrivate *priv = FPRINT_MANAGER_GET_PRIVATE (manager);
GSList *elem = priv->dev_registry;
int num_open = g_slist_length(elem);
FprintManagerPrivate *priv = fprint_manager_get_instance_private (manager);
GSList *elem;;
int num_open;
elem = priv->dev_registry;
num_open = g_slist_length(elem);
if (num_open > 0) {
*device = get_device_path (g_slist_last (elem)->data);

81
src/meson.build Normal file
View File

@ -0,0 +1,81 @@
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),
command: ['cp', '@INPUT@', '@OUTPUT@'],
install: true,
install_dir: dbus_interfaces_dir,
)
endforeach
fprintd_deps = declare_dependency(
include_directories: [
include_directories('..'),
],
dependencies: [
dbus_glib_dep,
glib_dep,
gio_dep,
gmodule_dep,
libfprint_dep,
polkit_gobject_dep,
],
compile_args: [
'-DG_LOG_DOMAIN="@0@"'.format(meson.project_name()),
'-DLOCALEDIR="@0@"'.format(localedir),
'-DPLUGINDIR="@0@"'.format(fprintd_plugindir),
],
)
libfprintd_private = static_library('fprintd-private',
sources: [
'device.c',
'fprintd.h',
'manager.c',
dbus_server_glue_sources,
fprintd_marshal,
],
dependencies: fprintd_deps,
gnu_symbol_visibility: 'hidden',
)
fprintd = executable('fprintd',
sources: [
'file_storage.c',
'file_storage.h',
'fprintd.h',
'main.c',
'storage.h',
],
dependencies: fprintd_deps,
link_with: libfprintd_private,
gnu_symbol_visibility: 'hidden',
install: true,
install_dir: fprintd_installdir,
)

View File

@ -18,17 +18,18 @@
*
*/
#ifndef STORAGE_H
#pragma once
#define STORAGE_H
typedef int (*storage_print_data_save)(struct fp_print_data *data,
enum fp_finger finger, const char *username);
typedef int (*storage_print_data_load)(struct fp_dev *dev,
enum fp_finger finger, struct fp_print_data **data, const char *username);
typedef int (*storage_print_data_delete)(struct fp_dscv_dev *dev,
enum fp_finger finger, const char *username);
typedef GSList *(*storage_discover_prints)(struct fp_dscv_dev *dev, const char *username);
typedef int (*storage_print_data_save)(FpPrint *print);
typedef int (*storage_print_data_load)(FpDevice *dev,
FpFinger finger,
const char *username,
FpPrint **print);
typedef int (*storage_print_data_delete)(FpDevice *dev,
FpFinger finger,
const char *username);
typedef GSList *(*storage_discover_prints)(FpDevice *dev, const char *username);
typedef GSList *(*storage_discover_users)(void);
typedef int (*storage_init)(void);
typedef int (*storage_deinit)(void);
@ -39,12 +40,10 @@ struct storage {
storage_print_data_load print_data_load;
storage_print_data_delete print_data_delete;
storage_discover_prints discover_prints;
storage_discover_users discover_users;
};
typedef struct storage fp_storage;
/* The currently setup store */
fp_storage store;
#endif
extern fp_storage store;

View File

@ -1,35 +0,0 @@
BUILT_SOURCES = manager-dbus-glue.h device-dbus-glue.h $(MARSHALFILES)
noinst_HEADERS = $(BUILT_SOURCES)
CLEANFILES = $(BUILT_SOURCES)
bin_PROGRAMS = fprintd-verify fprintd-enroll fprintd-list fprintd-delete
fprintd_verify_SOURCES = verify.c $(MARSHALFILES)
fprintd_verify_CFLAGS = $(WARN_CFLAGS) $(GLIB_CFLAGS)
fprintd_verify_LDADD = $(GLIB_LIBS)
fprintd_enroll_SOURCES = enroll.c $(MARSHALFILES)
fprintd_enroll_CFLAGS = $(WARN_CFLAGS) $(GLIB_CFLAGS) -I$(top_srcdir)/pam
fprintd_enroll_LDADD = $(GLIB_LIBS)
fprintd_list_SOURCES = list.c
fprintd_list_CFLAGS = $(WARN_CFLAGS) $(GLIB_CFLAGS)
fprintd_list_LDADD = $(GLIB_LIBS)
fprintd_delete_SOURCES = delete.c
fprintd_delete_CFLAGS = $(WARN_CFLAGS) $(GLIB_CFLAGS)
fprintd_delete_LDADD = $(GLIB_LIBS)
manager-dbus-glue.h: ../src/manager.xml
dbus-binding-tool --prefix=fprint_manager --mode=glib-client $< --output=$@
device-dbus-glue.h: ../src/device.xml
dbus-binding-tool --prefix=fprint_device --mode=glib-client $< --output=$@
MARSHALFILES = marshal.c marshal.h
GLIB_GENMARSHAL=`pkg-config --variable=glib_genmarshal glib-2.0`
marshal.h: $(top_srcdir)/src/fprintd-marshal.list
( $(GLIB_GENMARSHAL) --prefix=fprintd_marshal $(top_srcdir)/src/fprintd-marshal.list --header > marshal.h )
marshal.c: marshal.h
( $(GLIB_GENMARSHAL) --prefix=fprintd_marshal $(top_srcdir)/src/fprintd-marshal.list --body --header > marshal.c )

346
tests/dbusmock/fprintd.py Normal file
View File

@ -0,0 +1,346 @@
# -*- coding: utf-8 -*-
'''fprintd mock template
This creates the expected methods and properties of the
net.reactivated.Fprint.Manager object (/net/reactivated/Fprint/Manager)
but no devices.
'''
# 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__ = 'Bastien Nocera'
__email__ = 'hadess@hadess.net'
__copyright__ = '(c) 2020 Red Hat Inc.'
__license__ = 'LGPL 3+'
import dbus
import asyncio
from dbusmock import MOCK_IFACE, mockobject
BUS_NAME = 'net.reactivated.Fprint'
MAIN_OBJ = '/net/reactivated/Fprint/Manager'
SYSTEM_BUS = True
IS_OBJECT_MANAGER = False
MAIN_IFACE = 'net.reactivated.Fprint.Manager'
MANAGER_MOCK_IFACE = 'net.reactivated.Fprint.Manager.Mock'
DEVICE_IFACE = 'net.reactivated.Fprint.Device'
DEVICE_MOCK_IFACE = 'net.reactivated.Fprint.Device.Mock'
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'
]
VALID_VERIFY_STATUS = [
'verify-no-match',
'verify-match',
'verify-retry-scan',
'verify-swipe-too-short',
'verify-finger-not-centered',
'verify-remove-and-retry',
'verify-disconnected',
'verify-unknown-error'
]
VALID_ENROLL_STATUS = [
'enroll-completed',
'enroll-failed',
'enroll-stage-passed',
'enroll-retry-scan',
'enroll-swipe-too-short',
'enroll-finger-not-centered',
'enroll-remove-and-retry',
'enroll-data-full',
'enroll-disconnected',
'enroll-unknown-error'
]
def load(mock, parameters):
fprintd = mockobject.objects[MAIN_OBJ]
mock.last_device_id = 0
fprintd.fingers = {}
mock.loop = asyncio.new_event_loop()
@dbus.service.method(MAIN_IFACE,
in_signature='', out_signature='ao')
def GetDevices(self):
return [(k) for k in mockobject.objects.keys() if "/Device/" in k]
@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]
@dbus.service.method(MANAGER_MOCK_IFACE,
in_signature='sis', out_signature='s')
def AddDevice(self, device_name, num_enroll_stages, scan_type):
'''Convenience method to add a fingerprint reader device
You have to specify a device name, the number of enrollment
stages it would use (> 0) and the scan type, as a string
(either 'press' or 'swipe')
'''
if scan_type not in ['swipe', 'press']:
raise dbus.exceptions.DBusException(
'Invalid scan_type \'%s\'.' % scan_type,
name='org.freedesktop.DBus.Error.InvalidArgs')
if num_enroll_stages <= 0:
raise dbus.exceptions.DBusException(
'Invalid num_enroll_stages \'%s\'.' % num_enroll_stages,
name='org.freedesktop.DBus.Error.InvalidArgs')
self.last_device_id += 1
path = '/net/reactivated/Fprint/Device/%d' % self.last_device_id
device_properties = {
'name': dbus.String(device_name, variant_level=1),
'num-enroll-stages': dbus.Int32(num_enroll_stages, variant_level=1),
'scan-type': scan_type
}
self.AddObject(path,
DEVICE_IFACE,
# Properties
device_properties,
# Methods
[
('ListEnrolledFingers', 's', 'as', ListEnrolledFingers),
('DeleteEnrolledFingers', 's', '', DeleteEnrolledFingers),
('DeleteEnrolledFingers2', '', '', DeleteEnrolledFingers2),
('Claim', 's', '', Claim),
('Release', '', '', Release),
('VerifyStart', 's', '', VerifyStart),
('VerifyStop', '', '', VerifyStop),
('EnrollStart', 's', '', EnrollStart),
('EnrollStop', '', '', EnrollStop)
])
device = mockobject.objects[path]
device.fingers = {}
device.claimed_user = None
device.action = None
device.verify_script = []
return path
@dbus.service.method(DEVICE_IFACE,
in_signature='s', out_signature='as')
def ListEnrolledFingers(device, user):
if user in device.fingers:
return device.fingers[user]
raise dbus.exceptions.DBusException(
'No enrolled prints in device %s for user %s' % (device.path, user),
name='net.reactivated.Fprint.Error.NoEnrolledPrints')
@dbus.service.method(DEVICE_IFACE,
in_signature='s', out_signature='')
def DeleteEnrolledFingers(device, user):
device.fingers[user] = []
@dbus.service.method(DEVICE_IFACE,
in_signature='', out_signature='')
def DeleteEnrolledFingers2(device):
if not device.claimed_user:
raise dbus.exceptions.DBusException(
'Device was not claimed before use',
name='net.reactivated.Fprint.Error.ClaimDevice')
device.fingers[device.claimed_user] = []
@dbus.service.method(DEVICE_IFACE,
in_signature='s', out_signature='')
def Claim(device, user):
if device.claimed_user:
raise dbus.exceptions.DBusException(
'Device already in use by %s' % device.claimed_user,
name='net.reactivated.Fprint.Error.AlreadyInUse')
device.claimed_user = user
@dbus.service.method(DEVICE_IFACE,
in_signature='', out_signature='')
def Release(device):
if not device.claimed_user:
raise dbus.exceptions.DBusException(
'Device was not claimed before use',
name='net.reactivated.Fprint.Error.ClaimDevice')
device.claimed_user = None
def can_verify_finger(device, finger_name):
# We should already have checked that there are enrolled fingers
if finger_name == 'any':
return True
if finger_name in device.fingers[device.claimed_user]:
return True
return False
async def send_verify_script(device, script):
for [result, done, timeout] in device.verify_script:
await asyncio.sleep(timeout)
device.EmitSignal(DEVICE_IFACE, 'VerifyStatus', 'sb', [
result,
done
])
@dbus.service.method(DEVICE_IFACE,
in_signature='s', out_signature='')
def VerifyStart(device, finger_name):
if not device.claimed_user:
raise dbus.exceptions.DBusException(
'Device was not claimed before use',
name='net.reactivated.Fprint.Error.ClaimDevice')
if device.claimed_user not in device.fingers:
raise dbus.exceptions.DBusException(
'No enrolled prints for user \'%s\'' % device.claimed_user,
name='net.reactivated.Fprint.Error.NoEnrolledPrints')
if not finger_name:
raise dbus.exceptions.DBusException(
'Invalid empty finger_name.',
name='org.freedesktop.DBus.Error.InvalidArgs')
if not can_verify_finger(device, finger_name):
raise dbus.exceptions.DBusException(
'Finger \'%s\' not enrolled.' % finger_name,
name='org.freedesktop.DBus.Error.Internal')
if device.action:
raise dbus.exceptions.DBusException(
'Action \'%s\' already in progress' % device.action,
name='net.reactivated.Fprint.Error.AlreadyInUse')
device.action = 'verify'
if finger_name == 'any':
finger_name = device.fingers[device.claimed_user][0]
device.EmitSignal(DEVICE_IFACE, 'VerifyFingerSelected', 's', [
finger_name
])
if device.verify_script is not None and len(device.verify_script) > 0:
asyncio.run(send_verify_script(device, device.verify_script))
@dbus.service.method(DEVICE_MOCK_IFACE,
in_signature='sb', out_signature='')
def EmitVerifyStatus(device, result, done):
if (not device.action) or (device.action != 'verify'):
raise dbus.exceptions.DBusException(
'Cannot send verify statuses when not verifying',
name='org.freedesktop.DBus.Error.InvalidArgs')
if result not in VALID_VERIFY_STATUS:
raise dbus.exceptions.DBusException(
'Unknown verify status \'%s\'' % result,
name='org.freedesktop.DBus.Error.InvalidArgs')
device.EmitSignal(DEVICE_IFACE, 'VerifyStatus', 'sb', [
result,
done
])
@dbus.service.method(DEVICE_IFACE,
in_signature='', out_signature='')
def VerifyStop(device):
if device.action != 'verify':
raise dbus.exceptions.DBusException(
'No verification to stop',
name='net.reactivated.Fprint.Error.NoActionInProgress')
device.action = None
@dbus.service.method(DEVICE_IFACE,
in_signature='s', out_signature='')
def EnrollStart(device, finger_name):
if finger_name not in VALID_FINGER_NAMES:
raise dbus.exceptions.DBusException(
'Invalid finger name \'%s\'' % finger_name,
name='net.reactivated.Fprint.Error.InvalidFingername')
if not device.claimed_user:
raise dbus.exceptions.DBusException(
'Device was not claimed before use',
name='net.reactivated.Fprint.Error.ClaimDevice')
if device.action:
raise dbus.exceptions.DBusException(
'Action \'%s\' already in progress' % device.action,
name='net.reactivated.Fprint.Error.AlreadyInUse')
device.action = 'enroll'
@dbus.service.method(DEVICE_MOCK_IFACE,
in_signature='sb', out_signature='')
def EmitEnrollStatus(device, result, done):
if (not device.action) or (device.action != 'enroll'):
raise dbus.exceptions.DBusException(
'Cannot send enroll statuses when not enrolling',
name='org.freedesktop.DBus.Error.InvalidArgs')
if result not in VALID_ENROLL_STATUS:
raise dbus.exceptions.DBusException(
'Unknown enroll status \'%s\'' % result,
name='org.freedesktop.DBus.Error.InvalidArgs')
device.EmitSignal(DEVICE_IFACE, 'EnrollStatus', 'sb', [
result,
done
])
# FIXME save enrolled finger?
@dbus.service.method(DEVICE_IFACE,
in_signature='', out_signature='')
def EnrollStop(device):
if device.action != 'enroll':
raise dbus.exceptions.DBusException(
'No enrollment to stop',
name='net.reactivated.Fprint.Error.NoActionInProgress')
device.action = None
@dbus.service.method(DEVICE_MOCK_IFACE,
in_signature='sas', out_signature='')
def SetEnrolledFingers(device, user, fingers):
'''Convenience method to set the list of enrolled fingers.
The device_path is the return value from AddDevice(), and the
array of fingers must only contain valid finger names.
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(
'Invalid finger name \'%s\'' % k,
name='org.freedesktop.DBus.Error.InvalidArgs')
device.fingers[user] = fingers
@dbus.service.method(DEVICE_MOCK_IFACE,
in_signature='a(sbi)', out_signature='')
def SetVerifyScript(device, script):
'''Convenience method to set the verification script.
After VerifyStart is called, signal results will be sent in order after
a certain timeout declared in seconds. The array contains each
'result' followed by the 'done' argument for VerifyStatus, and the
amount of time to wait before each signal is sent.
Returns nothing.
'''
device.verify_script = script

530
tests/fprintd.py Executable file
View File

@ -0,0 +1,530 @@
#! /usr/bin/env python3
# Copyright © 2017, 2019 Red Hat, Inc
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library. If not, see <http://www.gnu.org/licenses/>.
# Authors:
# Christian J. Kellner <christian@kellner.me>
# Benjamin Berg <bberg@redhat.com>
# Marco Trevisan <marco.trevisan@canonical.com>
import unittest
import time
import subprocess
import os
import os.path
import sys
import tempfile
import glob
import shutil
import socket
import struct
import dbusmock
import gi
from gi.repository import GLib, Gio
import cairo
try:
from subprocess import DEVNULL
except ImportError:
DEVNULL = open(os.devnull, 'wb')
SERVICE_FILE = '/usr/share/dbus-1/system-services/net.reactivated.Fprint.service'
def get_timeout(topic='default'):
vals = {
'valgrind': {
'test': 300,
'default': 20,
'daemon_start': 60
},
'default': {
'test': 60,
'default': 3,
'daemon_start': 5
}
}
valgrind = os.getenv('VALGRIND')
lut = vals['valgrind' if valgrind is not None else 'default']
if topic not in lut:
raise ValueError('invalid topic')
return lut[topic]
# Copied from libfprint tests
class Connection:
def __init__(self, addr):
self.addr = addr
def __enter__(self):
self.con = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
self.con.connect(self.addr)
return self.con
def __exit__(self, exc_type, exc_val, exc_tb):
self.con.close()
del self.con
def load_image(img):
png = cairo.ImageSurface.create_from_png(img)
# Cairo wants 4 byte aligned rows, so just add a few pixel if necessary
w = png.get_width()
h = png.get_height()
w = (w + 3) // 4 * 4
h = (h + 3) // 4 * 4
img = cairo.ImageSurface(cairo.Format.A8, w, h)
cr = cairo.Context(img)
cr.set_source_rgba(1, 1, 1, 1)
cr.paint()
cr.set_source_rgba(0, 0, 0, 0)
cr.set_operator(cairo.OPERATOR_SOURCE)
cr.set_source_surface(png)
cr.paint()
return img
if hasattr(os.environ, 'TOPSRCDIR'):
root = os.environ['TOPSRCDIR']
else:
root = os.path.join(os.path.dirname(__file__), '..')
imgdir = os.path.join(root, 'tests', 'prints')
ctx = GLib.main_context_default()
class FPrintdTest(dbusmock.DBusTestCase):
@staticmethod
def path_from_service_file(sf):
with open(SERVICE_FILE) as f:
for line in f:
if not line.startswith('Exec='):
continue
return line.split('=', 1)[1].strip()
return None
@classmethod
def setUpClass(cls):
fprintd = None
if 'FPRINT_BUILD_DIR' in os.environ:
print('Testing local build')
build_dir = os.environ['FPRINT_BUILD_DIR']
fprintd = os.path.join(build_dir, 'fprintd')
elif 'UNDER_JHBUILD' in os.environ:
print('Testing JHBuild version')
jhbuild_prefix = os.environ['JHBUILD_PREFIX']
fprintd = os.path.join(jhbuild_prefix, 'libexec', 'fprintd')
else:
print('Testing installed system binaries')
fprintd = cls.path_from_service_file(SERVICE_FILE)
assert fprintd is not None, 'failed to find daemon'
cls.paths = {'daemon': fprintd }
cls.tmpdir = tempfile.mkdtemp(prefix='libfprint-')
cls.sockaddr = os.path.join(cls.tmpdir, 'virtual-image.socket')
os.environ['FP_VIRTUAL_IMAGE'] = cls.sockaddr
cls.prints = {}
for f in glob.glob(os.path.join(imgdir, '*.png')):
n = os.path.basename(f)[:-4]
cls.prints[n] = load_image(f)
cls.test_bus = Gio.TestDBus.new(Gio.TestDBusFlags.NONE)
cls.test_bus.up()
try:
del os.environ['DBUS_SESSION_BUS_ADDRESS']
except KeyError:
pass
os.environ['DBUS_SYSTEM_BUS_ADDRESS'] = cls.test_bus.get_bus_address()
cls.dbus = Gio.bus_get_sync(Gio.BusType.SYSTEM, None)
@classmethod
def tearDownClass(cls):
cls.test_bus.down()
shutil.rmtree(cls.tmpdir)
dbusmock.DBusTestCase.tearDownClass()
def daemon_start(self):
timeout = get_timeout('daemon_start') # seconds
env = os.environ.copy()
env['G_DEBUG'] = 'fatal-criticals'
env['STATE_DIRECTORY'] = self.state_dir
env['RUNTIME_DIRECTORY'] = self.run_dir
argv = [self.paths['daemon'], '-t']
valgrind = os.getenv('VALGRIND')
if valgrind is not None:
argv.insert(0, 'valgrind')
argv.insert(1, '--leak-check=full')
if os.path.exists(valgrind):
argv.insert(2, '--suppressions=%s' % valgrind)
self.valgrind = True
self.daemon = subprocess.Popen(argv,
env=env,
stdout=None,
stderr=subprocess.STDOUT)
self.device = None
timeout_count = timeout * 10
timeout_sleep = 0.1
while timeout_count > 0:
time.sleep(timeout_sleep)
timeout_count -= 1
try:
self.manager = Gio.DBusProxy.new_sync(self.dbus,
Gio.DBusProxyFlags.DO_NOT_AUTO_START,
None,
'net.reactivated.Fprint',
'/net/reactivated/Fprint/Manager',
'net.reactivated.Fprint.Manager',
None)
devices = self.manager.GetDevices()
# Find the virtual device, just in case it is a local run
# and there is another usable sensor available locally
for path in devices:
dev = Gio.DBusProxy.new_sync(self.dbus,
Gio.DBusProxyFlags.DO_NOT_AUTO_START,
None,
'net.reactivated.Fprint',
path,
'net.reactivated.Fprint.Device',
None)
if 'Virtual image device' in str(dev.get_cached_property('name')):
self.device = dev
break
else:
print('Did not find virtual device! Probably libfprint was build without the corresponding driver!')
break
except GLib.GError:
pass
else:
timeout_time = timeout * 10 * timeout_sleep
self.fail('daemon did not start in %d seconds' % timeout_time)
def daemon_stop(self):
if self.daemon:
try:
self.daemon.terminate()
except OSError:
pass
self.daemon.wait(timeout=2)
self.daemon = None
self.client = None
def polkitd_start(self):
self._polkitd, self._polkitd_obj = self.spawn_server_template(
'polkitd', {}, stdout=DEVNULL)
def polkitd_stop(self):
if self._polkitd is None:
return
self._polkitd.terminate()
self._polkitd.wait()
def setUp(self):
self.test_dir = tempfile.mkdtemp()
self.state_dir = os.path.join(self.test_dir, 'state')
self.run_dir = os.path.join(self.test_dir, 'run')
def tearDown(self):
shutil.rmtree(self.test_dir)
# From libfprint tests
def send_retry(self, retry_error=1):
# The default (1) is too-short
with Connection(self.sockaddr) as con:
con.sendall(struct.pack('ii', -1, retry_error))
# From libfprint tests
def send_image(self, image):
img = self.prints[image]
with Connection(self.sockaddr) as con:
mem = img.get_data()
mem = mem.tobytes()
self.assertEqual(len(mem), img.get_width() * img.get_height())
encoded_img = struct.pack('ii', img.get_width(), img.get_height())
encoded_img += mem
con.sendall(encoded_img)
class FPrintdVirtualDeviceTest(FPrintdTest):
def setUp(self):
super().setUp()
self.polkitd_start()
self.daemon_start()
if self.device is None:
self.daemon_stop()
self.polkitd_stop()
self.skipTest("Need virtual_image device to run the test")
def timeout_cb(*args):
# Note: With meson we could just rely on it to kill us
print("Test timed out, hard exiting")
sys.exit(1)
self.test_timeout = GLib.timeout_add(get_timeout('test') * 1000, timeout_cb)
self._polkitd_obj.SetAllowed(['net.reactivated.fprint.device.setusername',
'net.reactivated.fprint.device.enroll',
'net.reactivated.fprint.device.verify'])
def signal_cb(proxy, sender, signal, params):
print(signal, params)
if signal == 'EnrollStatus':
self._abort = params[1]
self._last_result = params[0]
if not self._abort and self._last_result == 'enroll-stage-passed':
self.send_image('whorl')
elif self._abort:
pass
else:
self._abort = True
self._last_result = 'Unexpected signal values'
print('Unexpected signal values')
elif signal == 'VerifyFingerSelected':
pass
elif signal == 'VerifyStatus':
self._abort = True
self._last_result = params[0]
self._verify_stopped = params[1]
else:
self._abort = True
self._last_result = 'Unexpected signal'
self.g_signal_id = self.device.connect('g-signal', signal_cb)
def tearDown(self):
super().tearDown()
GLib.source_remove(self.test_timeout)
self.device.disconnect(self.g_signal_id)
self.daemon_stop()
self.polkitd_stop()
def assertFprintError(self, fprint_error):
return self.assertRaisesRegex(GLib.Error,
'.*net\.reactivated\.Fprint\.Error\.{}.*'.format(fprint_error))
def test_allowed_claim(self):
self._polkitd_obj.SetAllowed(['net.reactivated.fprint.device.setusername',
'net.reactivated.fprint.device.enroll'])
self.device.Claim('(s)', 'testuser')
self.device.Release()
def test_unallowed_claim(self):
self._polkitd_obj.SetAllowed([''])
with self.assertFprintError('PermissionDenied'):
self.device.Claim('(s)', 'testuser')
self._polkitd_obj.SetAllowed(['net.reactivated.fprint.device.setusername'])
with self.assertFprintError('PermissionDenied'):
self.device.Claim('(s)', 'testuser')
self._polkitd_obj.SetAllowed(['net.reactivated.fprint.device.enroll'])
with self.assertFprintError('PermissionDenied'):
self.device.Claim('(s)', 'testuser')
def test_multiple_claims(self):
self.device.Claim('(s)', 'testuser')
with self.assertFprintError('AlreadyInUse'):
self.device.Claim('(s)', 'testuser')
self.device.Release()
def test_unallowed_release(self):
self.device.Claim('(s)', 'testuser')
self._polkitd_obj.SetAllowed([''])
with self.assertFprintError('PermissionDenied'):
self.device.Release()
self._polkitd_obj.SetAllowed(['net.reactivated.fprint.device.setusername'])
with self.assertFprintError('PermissionDenied'):
self.device.Release()
self._polkitd_obj.SetAllowed(['net.reactivated.fprint.device.enroll'])
self.device.Release()
def test_unclaimed_release(self):
with self.assertFprintError('ClaimDevice'):
self.device.Release()
def test_unclaimed_verify_start(self):
with self.assertFprintError('ClaimDevice'):
self.device.VerifyStart('(s)', 'any')
def test_unclaimed_verify_stop(self):
with self.assertFprintError('ClaimDevice'):
self.device.VerifyStop()
def test_unclaimed_enroll_start(self):
with self.assertFprintError('ClaimDevice'):
self.device.EnrollStart('(s)', 'left-index-finger')
def test_unclaimed_enroll_stop(self):
with self.assertFprintError('ClaimDevice'):
self.device.EnrollStop()
def test_wrong_finger_enroll_start(self):
self.device.Claim('(s)', 'testuser')
with self.assertFprintError('InvalidFingername'):
self.device.EnrollStart('(s)', 'any')
self.device.Release()
def test_unclaimed_delete_enrolled_fingers(self):
self.device.DeleteEnrolledFingers('(s)', 'testuser')
def test_unclaimed_delete_enrolled_fingers2(self):
with self.assertFprintError('ClaimDevice'):
self.device.DeleteEnrolledFingers2()
def test_unclaimed_list_enrolled_fingers(self):
with self.assertFprintError('NoEnrolledPrints'):
self.device.ListEnrolledFingers('(s)', 'testuser')
def test_enroll_verify_list_delete(self):
self.device.Claim('(s)', 'testuser')
with self.assertFprintError('NoEnrolledPrints'):
self.device.ListEnrolledFingers('(s)', 'testuser')
with self.assertFprintError('NoEnrolledPrints'):
self.device.ListEnrolledFingers('(s)', 'nottestuser')
self.device.EnrollStart('(s)', 'right-index-finger')
self.send_image('whorl')
self._abort = False
while not self._abort:
ctx.iteration(True)
self.assertEqual(self._last_result, 'enroll-completed')
self.device.EnrollStop()
self.assertTrue(os.path.exists(os.path.join(self.state_dir, 'testuser/virtual_image/0/7')))
with self.assertFprintError('NoEnrolledPrints'):
self.device.ListEnrolledFingers('(s)', 'nottestuser')
self.assertEqual(self.device.ListEnrolledFingers('(s)', 'testuser'), ['right-index-finger'])
# Finger is enrolled, try to verify it
self.device.VerifyStart('(s)', 'any')
# Try a wrong print; will stop verification
self.send_image('tented_arch')
self._abort = False
while not self._abort:
ctx.iteration(True)
self.assertTrue(self._verify_stopped)
self.assertEqual(self._last_result, 'verify-no-match')
self.device.VerifyStop()
self.device.VerifyStart('(s)', 'any')
# Send a retry error (swipe too short); will not stop verification
self.send_retry()
self._abort = False
while not self._abort:
ctx.iteration(True)
self.assertFalse(self._verify_stopped)
self.assertEqual(self._last_result, 'verify-swipe-too-short')
# Try the correct print; will stop verification
self.send_image('whorl')
self._abort = False
while not self._abort:
ctx.iteration(True)
self.assertTrue(self._verify_stopped)
self.assertEqual(self._last_result, 'verify-match')
self.assertEqual(self.device.ListEnrolledFingers('(s)', 'testuser'), ['right-index-finger'])
# And delete the print(s) again
self.device.DeleteEnrolledFingers('(s)', 'testuser')
self.assertFalse(os.path.exists(os.path.join(self.state_dir, 'testuser/virtual_image/0/7')))
with self.assertFprintError('NoEnrolledPrints'):
self.device.ListEnrolledFingers('(s)', 'testuser')
self.device.Release()
def test_enroll_delete2(self):
self.device.Claim('(s)', 'testuser')
self.device.EnrollStart('(s)', 'right-index-finger')
self.send_image('whorl')
self._abort = False
while not self._abort:
ctx.iteration(True)
self.assertEqual(self._last_result, 'enroll-completed')
self.device.EnrollStop()
self.assertTrue(os.path.exists(os.path.join(self.state_dir, 'testuser/virtual_image/0/7')))
# And delete the print(s) again using the new API
self.device.DeleteEnrolledFingers2()
self.assertFalse(os.path.exists(os.path.join(self.state_dir, 'testuser/virtual_image/0/7')))
self.device.Release()
if __name__ == '__main__':
if len(sys.argv) == 2 and sys.argv[1] == "list-tests":
for machine, human in list_tests():
print("%s %s" % (machine, human), end="\n")
sys.exit(0)
unittest.main(verbosity=2)

47
tests/meson.build Normal file
View File

@ -0,0 +1,47 @@
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(),
],
)
endforeach
add_test_setup('default_setup',
is_default: true,
env: [
'G_SLICE=always-malloc',
'MALLOC_CHECK_=2',
'MALLOC_PERTURB_=55',
],
)
if 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',
env: [
'G_SLICE=always-malloc',
'VALGRIND=' + glib_suppressions,
],
timeout_multiplier: 5
)
endif
if get_option('pam')
subdir('pam')
endif

27
tests/pam/meson.build Normal file
View File

@ -0,0 +1,27 @@
subdir('services')
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,
)
endforeach

View File

@ -0,0 +1 @@
auth required @FPRINTDPAMPATH@ debug timeout=10

View File

@ -0,0 +1,13 @@
# Meson doesn't allow to have configure_file's as targets we depend on... Meh!
pam_service_file = custom_target('pam_test_service_file',
output: 'null',
command: 'true',
depends: pam_fprintd,
depend_files: configure_file(
input: 'fprintd-pam-test.in',
output: 'fprintd-pam-test',
configuration: configuration_data({
'FPRINTDPAMPATH': pam_fprintd.full_path(),
}),
),
)

169
tests/pam/test_pam_fprintd.py Executable file
View File

@ -0,0 +1,169 @@
#!/usr/bin/python3
# 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__ = 'Bastien Nocera'
__email__ = 'hadess@hadess.net'
__copyright__ = '(c) 2020 Red Hat Inc.'
__license__ = 'LGPL 3+'
import tempfile
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
PAM_USER_UNKNOWN = 10
PAM_MAXTRIES = 11
class TestPamFprintd(dbusmock.DBusTestCase):
'''Test pam_fprintd'''
@classmethod
def start_monitor(klass):
'''Start dbus-monitor'''
workdir = os.environ['TOPBUILDDIR'] + '/tests/pam/'
klass.monitor_log = open(os.path.join(workdir, 'dbus-monitor.log'), 'wb', buffering=0)
klass.monitor = subprocess.Popen(['dbus-monitor', '--monitor', '--system'],
stdout=klass.monitor_log,
stderr=subprocess.STDOUT)
@classmethod
def stop_monitor(klass):
'''Stop dbus-monitor'''
assert klass.monitor
klass.monitor.terminate()
klass.monitor.wait()
klass.monitor_log.flush()
klass.monitor_log.close()
@classmethod
def setUpClass(klass):
klass.start_system_bus()
klass.start_monitor()
klass.dbus_con = klass.get_dbus(True)
template_path = './'
if 'TOPSRCDIR' in os.environ:
template_path = os.environ['TOPSRCDIR'] + '/tests/'
klass.template_name = template_path + 'dbusmock/fprintd.py'
print ('Using template from %s' % klass.template_name)
@classmethod
def tearDownClass(klass):
klass.stop_monitor()
def setUp(self):
(self.p_mock, self.obj_fprintd_manager) = self.spawn_server_template(
self.template_name, {}, stdout=subprocess.PIPE)
# set log to nonblocking
flags = fcntl.fcntl(self.p_mock.stdout, fcntl.F_GETFL)
fcntl.fcntl(self.p_mock.stdout, fcntl.F_SETFL, flags | os.O_NONBLOCK)
self.obj_fprintd_mock = dbus.Interface(self.obj_fprintd_manager, 'net.reactivated.Fprint.Manager.Mock')
def tearDown(self):
self.p_mock.terminate()
self.p_mock.wait()
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'])
def test_pam_fprintd_auth(self):
self.setup_device()
script = [
( 'verify-match', True, 2 )
]
self.device_mock.SetVerifyScript(script)
tc = pypamtest.TestCase(pypamtest.PAMTEST_AUTHENTICATE, expected_rv=PAM_SUCCESS)
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_dual_reader_auth(self):
device_path = self.obj_fprintd_mock.AddDevice('FDO Sandpaper Reader', 3, 'press')
sandpaper_device_mock = self.dbus_con.get_object('net.reactivated.Fprint', device_path)
sandpaper_device_mock.SetEnrolledFingers('toto', ['left-middle-finger', 'right-middle-finger'])
script = [
( 'verify-match', True, 2 )
]
sandpaper_device_mock.SetVerifyScript(script)
# Add a 2nd device
self.setup_device()
tc = pypamtest.TestCase(pypamtest.PAMTEST_AUTHENTICATE, expected_rv=PAM_SUCCESS)
res = pypamtest.run_pamtest("toto", "fprintd-pam-test", [tc], [ 'unused' ])
self.assertRegex(res.info[0], r'Place your left middle finger on FDO Sandpaper Reader')
self.assertEqual(len(res.errors), 0)
def test_pam_fprintd_last_try_auth(self):
self.setup_device()
script = [
( 'verify-no-match', True, 1 ),
( 'verify-no-match', True, 1 ),
( 'verify-match', True, 1 ),
]
self.device_mock.SetVerifyScript(script)
tc = pypamtest.TestCase(pypamtest.PAMTEST_AUTHENTICATE, expected_rv=PAM_SUCCESS)
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), 2)
self.assertRegex(res.errors[0], r'Failed to match fingerprint')
self.assertRegex(res.errors[1], r'Failed to match fingerprint')
def test_pam_fprintd_failed_auth(self):
self.setup_device()
script = [
( 'verify-no-match', True, 1 ),
( 'verify-no-match', True, 1 ),
( 'verify-no-match', True, 1 ),
]
self.device_mock.SetVerifyScript(script)
tc = pypamtest.TestCase(pypamtest.PAMTEST_AUTHENTICATE, expected_rv=PAM_MAXTRIES)
res = pypamtest.run_pamtest("toto", "fprintd-pam-test", [tc], [ 'unused' ])
self.assertRegex(res.info[0], r'Swipe your left little finger across the fingerprint reader')
self.assertEqual(len(res.errors), 3)
self.assertRegex(res.errors[0], r'Failed to match fingerprint')
self.assertRegex(res.errors[1], r'Failed to match fingerprint')
self.assertRegex(res.errors[2], r'Failed to match fingerprint')
def test_pam_timeout(self):
self.setup_device()
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[1], r'Verification timed out')
if __name__ == '__main__':
if 'PAM_WRAPPER_SERVICE_DIR' not in os.environ:
print('Cannot run test without environment set correctly, run "meson test" instead')
sys.exit(1)
# set stream to sys.stderr to get debug output
unittest.main(testRunner=unittest.TextTestRunner(stream=sys.stdout, verbosity=2))

3
tests/prints/README Normal file
View File

@ -0,0 +1,3 @@
These are example images from NIST and are in the public domain.
The PNG files have been generated by using the greyscale data as a mask.

BIN
tests/prints/arch.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

BIN
tests/prints/arch.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 120 KiB

BIN
tests/prints/loop-right.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

BIN
tests/prints/loop-right.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 120 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 107 KiB

BIN
tests/prints/whorl.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

BIN
tests/prints/whorl.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 102 KiB

187
tests/test_fprintd_utils.py Executable file
View File

@ -0,0 +1,187 @@
#!/usr/bin/python3
# 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__ = 'Bastien Nocera'
__email__ = 'hadess@hadess.net'
__copyright__ = '(c) 2020 Red Hat Inc.'
__license__ = 'LGPL 3+'
import tempfile
import unittest
import sys
import subprocess
import dbus
import dbus.mainloop.glib
import dbusmock
import fcntl
import os
import time
dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
class TestFprintd(dbusmock.DBusTestCase):
'''Test fprintd utilities'''
@classmethod
def setUpClass(klass):
klass.start_system_bus()
klass.dbus_con = klass.get_dbus(True)
klass.sleep_time = 0.5
template_path = './'
if 'TOPSRCDIR' in os.environ:
template_path = os.environ['TOPSRCDIR'] + '/tests/'
klass.template_name = template_path + 'dbusmock/fprintd.py'
print ('Using template from %s' % klass.template_name)
klass.tools_prefix = ''
if 'FPRINT_BUILD_DIR' in os.environ:
klass.tools_prefix = os.environ['FPRINT_BUILD_DIR'] + '/../utils/'
print ('Using tools from %s' % klass.tools_prefix)
else:
print ('Using tools from $PATH')
klass.wrapper_args = []
klass.valgrind = False
if 'VALGRIND' in os.environ:
valgrind = os.environ['VALGRIND']
if valgrind is not None:
klass.valgrind = True
klass.sleep_time *= 4
klass.wrapper_args = ['valgrind', '--leak-check=full']
if os.path.exists(valgrind):
klass.wrapper_args += ['--suppressions={}'.format(valgrind)]
def setUp(self):
(self.p_mock, self.obj_fprintd_manager) = self.spawn_server_template(
self.template_name, {}, stdout=subprocess.PIPE)
# set log to nonblocking
flags = fcntl.fcntl(self.p_mock.stdout, fcntl.F_GETFL)
fcntl.fcntl(self.p_mock.stdout, fcntl.F_SETFL, flags | os.O_NONBLOCK)
self.obj_fprintd_mock = dbus.Interface(self.obj_fprintd_manager, 'net.reactivated.Fprint.Manager.Mock')
def tearDown(self):
self.p_mock.terminate()
self.p_mock.wait()
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'])
def test_fprintd_enroll(self):
self.setup_device()
mock_log = tempfile.NamedTemporaryFile()
process = subprocess.Popen(self.wrapper_args + [self.tools_prefix + 'fprintd-enroll', '-f', 'right-index-finger', 'toto'],
stdout=mock_log,
stderr=subprocess.STDOUT,
universal_newlines=True)
time.sleep(self.sleep_time)
with open(mock_log.name) as f:
out = f.read()
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')
def test_fprintd_verify(self):
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)
time.sleep(self.sleep_time)
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.device_mock.EmitVerifyStatus('verify-match', True)
time.sleep(self.sleep_time)
with open(mock_log.name) as f:
out = f.read()
self.assertRegex(out, 'Verify result: verify-match \(done\)')
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)
time.sleep(self.sleep_time)
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')
if __name__ == '__main__':
# avoid writing to stderr
unittest.main(testRunner=unittest.TextTestRunner(stream=sys.stdout, verbosity=2))

9
update-transifex.sh Executable file
View File

@ -0,0 +1,9 @@
#!/bin/sh
test -f .tx/config || exit 1
echo Pulling translations from Transifex
tx --root `dirname $0` pull --all --force --skip
echo Pushing strings to Transifex
tx push --source

View File

@ -19,6 +19,7 @@
#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"
@ -54,7 +55,12 @@ static void delete_fingerprints(DBusGProxy *dev, const char *username)
exit (1);
}
if (!net_reactivated_Fprint_Device_delete_enrolled_fingers(dev, username, &error)) {
if (!net_reactivated_Fprint_Device_claim(dev, username, &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);
exit (1);
@ -64,6 +70,12 @@ static void delete_fingerprints(DBusGProxy *dev, const char *username)
} else {
g_print ("Fingerprints deleted on %s\n", g_value_get_string (g_hash_table_lookup (props, "name")));
}
if (!net_reactivated_Fprint_Device_release(dev, &error)) {
g_print("ReleaseDevice failed: %s\n", error->message);
exit (1);
}
g_hash_table_destroy (props);
g_object_unref (p);
}
@ -114,9 +126,7 @@ static void process_devices(char **argv)
int main(int argc, char **argv)
{
#if !GLIB_CHECK_VERSION (2, 36, 0)
g_type_init();
#endif
setlocale (LC_ALL, "");
create_manager();

View File

@ -17,8 +17,10 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#define _GNU_SOURCE
#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"
@ -30,7 +32,7 @@
static DBusGProxy *manager = NULL;
static DBusGConnection *connection = NULL;
static char *finger_name = "right-index-finger";
static char *finger_name = NULL;
static char **usernames = NULL;
static void create_manager(void)
@ -159,9 +161,7 @@ int main(int argc, char **argv)
GError *err = NULL;
DBusGProxy *dev;
#if !GLIB_CHECK_VERSION (2, 36, 0)
g_type_init();
#endif
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);
@ -175,11 +175,16 @@ int main(int argc, char **argv)
return 1;
}
if (finger_name == NULL)
finger_name = g_strdup("right-index-finger");
create_manager();
dev = open_device(usernames ? usernames[0] : NULL);
do_enroll(dev);
release_device(dev);
g_free(finger_name);
g_strfreev(usernames);
return 0;
}

View File

@ -19,6 +19,7 @@
#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"
@ -130,9 +131,7 @@ static void process_devices(char **argv)
int main(int argc, char **argv)
{
#if !GLIB_CHECK_VERSION (2, 36, 0)
g_type_init();
#endif
setlocale (LC_ALL, "");
create_manager();

67
utils/meson.build Normal file
View File

@ -0,0 +1,67 @@
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('../pam'),
],
dependencies: [
glib_dep,
dbus_glib_dep,
],
sources: [
utils_marshal,
dbus_client_glue_sources,
],
link_with: static_library('fprintd_utils',
sources: [
dbus_client_glue_sources,
utils_marshal,
],
dependencies: [
glib_dep,
]
),
)
utils = [
'delete',
'enroll',
'list',
'verify',
]
fprintd_utils = []
foreach util: utils
fprintd_utils += executable('fprintd-' + util,
sources: util + '.c',
dependencies: libfprintd_utils_dep,
install: true,
)
endforeach

View File

@ -20,6 +20,7 @@
#include <stdio.h>
#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"
@ -170,11 +171,9 @@ int main(int argc, char **argv)
GOptionContext *context;
GError *err = NULL;
DBusGProxy *dev;
char *username;
const char *username = NULL;
#if !GLIB_CHECK_VERSION (2, 36, 0)
g_type_init();
#endif
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);