pax_global_header00006660000000000000000000000064132474050740014520gustar00rootroot0000000000000052 comment=aea66d722fb3a154a9fab875a7f276f69b9358da bolt-0.2/000077500000000000000000000000001324740507400123215ustar00rootroot00000000000000bolt-0.2/.editorconfig000066400000000000000000000003531324740507400147770ustar00rootroot00000000000000root = true [*] end_of_line = lf insert_final_newline = true trim_trailing_whitespace = true charset = utf-8 [*.{c,h}] indent_style = space indent_size = 2 indent_brace_style = gnu [meson.build] indent_style = space indent_size = 2 bolt-0.2/.gitignore000066400000000000000000000001141324740507400143050ustar00rootroot00000000000000 # files *~ *.log *.xz # directories build/ # coverity cov-int/ coverity/ bolt-0.2/.travis.yml000066400000000000000000000003741324740507400144360ustar00rootroot00000000000000language: c sudo: required dist: trusty services: - docker env: - OS=debian - OS=arch - OS=fedora install: - docker build -t bolt-$OS -f ./contrib/Dockerfile-$OS . script: - docker run -e -t -v `pwd`:/src bolt-$OS ./contrib/docker-build.sh bolt-0.2/BUGS.md000066400000000000000000000005471324740507400134110ustar00rootroot00000000000000Bugs ==== Bugs are tracked via at [github][github issues]. Debugging ========= The daemon can be run in verbose mode with the command line option `-v`. To replace the currently running daemon and run a new instance of it in the forground, launch the daemon with `--replace`: boltd -v --replace [github issues]: https://github.com/gicmo/bolt/issues bolt-0.2/COPYING000066400000000000000000000636421324740507400133670ustar00rootroot00000000000000 GNU LESSER GENERAL PUBLIC LICENSE Version 2.1, February 1999 Copyright (C) 1991, 1999 Free Software Foundation, Inc. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. [This is the first released version of the Lesser GPL. It also counts as the successor of the GNU Library Public License, version 2, hence the version number 2.1.] Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public Licenses are intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This license, the Lesser General Public License, applies to some specially designated software packages--typically libraries--of the Free Software Foundation and other authors who decide to use it. You can use it too, but we suggest you first think carefully about whether this license or the ordinary General Public License is the better strategy to use in any particular case, based on the explanations below. When we speak of free software, we are referring to freedom of use, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish); that you receive source code or can get it if you want it; that you can change the software and use pieces of it in new free programs; and that you are informed that you can do these things. To protect your rights, we need to make restrictions that forbid distributors to deny you these rights or to ask you to surrender these rights. These restrictions translate to certain responsibilities for you if you distribute copies of the library or if you modify it. For example, if you distribute copies of the library, whether gratis or for a fee, you must give the recipients all the rights that we gave you. You must make sure that they, too, receive or can get the source code. If you link other code with the library, you must provide complete object files to the recipients, so that they can relink them with the library after making changes to the library and recompiling it. And you must show them these terms so they know their rights. We protect your rights with a two-step method: (1) we copyright the library, and (2) we offer you this license, which gives you legal permission to copy, distribute and/or modify the library. To protect each distributor, we want to make it very clear that there is no warranty for the free library. Also, if the library is modified by someone else and passed on, the recipients should know that what they have is not the original version, so that the original author's reputation will not be affected by problems that might be introduced by others. Finally, software patents pose a constant threat to the existence of any free program. We wish to make sure that a company cannot effectively restrict the users of a free program by obtaining a restrictive license from a patent holder. Therefore, we insist that any patent license obtained for a version of the library must be consistent with the full freedom of use specified in this license. Most GNU software, including some libraries, is covered by the ordinary GNU General Public License. This license, the GNU Lesser General Public License, applies to certain designated libraries, and is quite different from the ordinary General Public License. We use this license for certain libraries in order to permit linking those libraries into non-free programs. When a program is linked with a library, whether statically or using a shared library, the combination of the two is legally speaking a combined work, a derivative of the original library. The ordinary General Public License therefore permits such linking only if the entire combination fits its criteria of freedom. The Lesser General Public License permits more lax criteria for linking other code with the library. We call this license the "Lesser" General Public License because it does Less to protect the user's freedom than the ordinary General Public License. It also provides other free software developers Less of an advantage over competing non-free programs. These disadvantages are the reason we use the ordinary General Public License for many libraries. However, the Lesser license provides advantages in certain special circumstances. For example, on rare occasions, there may be a special need to encourage the widest possible use of a certain library, so that it becomes a de-facto standard. To achieve this, non-free programs must be allowed to use the library. A more frequent case is that a free library does the same job as widely used non-free libraries. In this case, there is little to gain by limiting the free library to free software only, so we use the Lesser General Public License. In other cases, permission to use a particular library in non-free programs enables a greater number of people to use a large body of free software. For example, permission to use the GNU C Library in non-free programs enables many more people to use the whole GNU operating system, as well as its variant, the GNU/Linux operating system. Although the Lesser General Public License is Less protective of the users' freedom, it does ensure that the user of a program that is linked with the Library has the freedom and the wherewithal to run that program using a modified version of the Library. The precise terms and conditions for copying, distribution and modification follow. Pay close attention to the difference between a "work based on the library" and a "work that uses the library". The former contains code derived from the library, whereas the latter must be combined with the library in order to run. GNU LESSER GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License Agreement applies to any software library or other program which contains a notice placed by the copyright holder or other authorized party saying it may be distributed under the terms of this Lesser General Public License (also called "this License"). Each licensee is addressed as "you". A "library" means a collection of software functions and/or data prepared so as to be conveniently linked with application programs (which use some of those functions and data) to form executables. The "Library", below, refers to any such software library or work which has been distributed under these terms. A "work based on the Library" means either the Library or any derivative work under copyright law: that is to say, a work containing the Library or a portion of it, either verbatim or with modifications and/or translated straightforwardly into another language. (Hereinafter, translation is included without limitation in the term "modification".) "Source code" for a work means the preferred form of the work for making modifications to it. For a library, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the library. Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running a program using the Library is not restricted, and output from such a program is covered only if its contents constitute a work based on the Library (independent of the use of the Library in a tool for writing it). Whether that is true depends on what the Library does and what the program that uses the Library does. 1. You may copy and distribute verbatim copies of the Library's complete source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and distribute a copy of this License along with the Library. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Library or any portion of it, thus forming a work based on the Library, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) The modified work must itself be a software library. b) You must cause the files modified to carry prominent notices stating that you changed the files and the date of any change. c) You must cause the whole of the work to be licensed at no charge to all third parties under the terms of this License. d) If a facility in the modified Library refers to a function or a table of data to be supplied by an application program that uses the facility, other than as an argument passed when the facility is invoked, then you must make a good faith effort to ensure that, in the event an application does not supply such function or table, the facility still operates, and performs whatever part of its purpose remains meaningful. (For example, a function in a library to compute square roots has a purpose that is entirely well-defined independent of the application. Therefore, Subsection 2d requires that any application-supplied function or table used by this function must be optional: if the application does not supply it, the square root function must still compute square roots.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Library, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Library, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Library. In addition, mere aggregation of another work not based on the Library with the Library (or with a work based on the Library) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may opt to apply the terms of the ordinary GNU General Public License instead of this License to a given copy of the Library. To do this, you must alter all the notices that refer to this License, so that they refer to the ordinary GNU General Public License, version 2, instead of to this License. (If a newer version than version 2 of the ordinary GNU General Public License has appeared, then you can specify that version instead if you wish.) Do not make any other change in these notices. Once this change is made in a given copy, it is irreversible for that copy, so the ordinary GNU General Public License applies to all subsequent copies and derivative works made from that copy. This option is useful when you wish to copy part of the code of the Library into a program that is not a library. 4. You may copy and distribute the Library (or a portion or derivative of it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange. If distribution of object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place satisfies the requirement to distribute the source code, even though third parties are not compelled to copy the source along with the object code. 5. A program that contains no derivative of any portion of the Library, but is designed to work with the Library by being compiled or linked with it, is called a "work that uses the Library". Such a work, in isolation, is not a derivative work of the Library, and therefore falls outside the scope of this License. However, linking a "work that uses the Library" with the Library creates an executable that is a derivative of the Library (because it contains portions of the Library), rather than a "work that uses the library". The executable is therefore covered by this License. Section 6 states terms for distribution of such executables. When a "work that uses the Library" uses material from a header file that is part of the Library, the object code for the work may be a derivative work of the Library even though the source code is not. Whether this is true is especially significant if the work can be linked without the Library, or if the work is itself a library. The threshold for this to be true is not precisely defined by law. If such an object file uses only numerical parameters, data structure layouts and accessors, and small macros and small inline functions (ten lines or less in length), then the use of the object file is unrestricted, regardless of whether it is legally a derivative work. (Executables containing this object code plus portions of the Library will still fall under Section 6.) Otherwise, if the work is a derivative of the Library, you may distribute the object code for the work under the terms of Section 6. Any executables containing that work also fall under Section 6, whether or not they are linked directly with the Library itself. 6. As an exception to the Sections above, you may also combine or link a "work that uses the Library" with the Library to produce a work containing portions of the Library, and distribute that work under terms of your choice, provided that the terms permit modification of the work for the customer's own use and reverse engineering for debugging such modifications. You must give prominent notice with each copy of the work that the Library is used in it and that the Library and its use are covered by this License. You must supply a copy of this License. If the work during execution displays copyright notices, you must include the copyright notice for the Library among them, as well as a reference directing the user to the copy of this License. Also, you must do one of these things: a) Accompany the work with the complete corresponding machine-readable source code for the Library including whatever changes were used in the work (which must be distributed under Sections 1 and 2 above); and, if the work is an executable linked with the Library, with the complete machine-readable "work that uses the Library", as object code and/or source code, so that the user can modify the Library and then relink to produce a modified executable containing the modified Library. (It is understood that the user who changes the contents of definitions files in the Library will not necessarily be able to recompile the application to use the modified definitions.) b) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (1) uses at run time a copy of the library already present on the user's computer system, rather than copying library functions into the executable, and (2) will operate properly with a modified version of the library, if the user installs one, as long as the modified version is interface-compatible with the version that the work was made with. c) Accompany the work with a written offer, valid for at least three years, to give the same user the materials specified in Subsection 6a, above, for a charge no more than the cost of performing this distribution. d) If distribution of the work is made by offering access to copy from a designated place, offer equivalent access to copy the above specified materials from the same place. e) Verify that the user has already received a copy of these materials or that you have already sent this user a copy. For an executable, the required form of the "work that uses the Library" must include any data and utility programs needed for reproducing the executable from it. However, as a special exception, the materials to be distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. It may happen that this requirement contradicts the license restrictions of other proprietary libraries that do not normally accompany the operating system. Such a contradiction means you cannot use both them and the Library together in an executable that you distribute. 7. You may place library facilities that are a work based on the Library side-by-side in a single library together with other library facilities not covered by this License, and distribute such a combined library, provided that the separate distribution of the work based on the Library and of the other library facilities is otherwise permitted, and provided that you do these two things: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities. This must be distributed under the terms of the Sections above. b) Give prominent notice with the combined library of the fact that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 8. You may not copy, modify, sublicense, link with, or distribute the Library except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, link with, or distribute the Library is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 9. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Library or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Library (or any work based on the Library), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Library or works based on it. 10. Each time you redistribute the Library (or any work based on the Library), the recipient automatically receives a license from the original licensor to copy, distribute, link with or modify the Library subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties with this License. 11. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Library at all. For example, if a patent license would not permit royalty-free redistribution of the Library by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Library. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply, and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 12. If the distribution and/or use of the Library is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Library under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 13. The Free Software Foundation may publish revised and/or new versions of the Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Library specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Library does not specify a license version number, you may choose any version ever published by the Free Software Foundation. 14. If you wish to incorporate parts of the Library into other free programs whose distribution conditions are incompatible with these, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Libraries If you develop a new library, and you want it to be of the greatest possible use to the public, we recommend making it free software that everyone can redistribute and change. You can do so by permitting redistribution under these terms (or, alternatively, under the terms of the ordinary General Public License). To apply these terms, attach the following notices to the library. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This library 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 library 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, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Also add information on how to contact you by electronic and paper mail. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the library, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the library `Frob' (a library for tweaking knobs) written by James Random Hacker. , 1 April 1990 Ty Coon, President of Vice That's all there is to it! bolt-0.2/HACKING.md000066400000000000000000000024251324740507400137120ustar00rootroot00000000000000Patches ======= Patches should be submitted in the form of pull requests at [github][github]. Coding style ============ Run `scripts/uncrustify.sh` to format the source code before submitting pull requests. Testing ======= To run the test suite in verbose mode: meson test -C build --verbose To run `boltd` from within valgrind for the integration tests set the environment variable `VALGRIND`. A [suppression file][valgrind] can be specified via that variable as well (n.b. for meson the path must be relative to the build directory): VALGRIND=../bolt.supp meson test -C build --verbose Static analysis =============== The clang static analyzer can be run locally via: ninja -C scan-build Coverity -------- Bolt is registerd with [coverity][coverity]. To submit a local build, execute the following commands (the `cov-build` [build tool][cov-build] must be in `PATH`) from the source directory: meson coverity cov-build --dir cov-int ninja -C coverity tar caf bolt.xz cov-int Upload the `bolt.xz` file to coverity for analysis. Fix defects. Profit. [github]: https://github.com/gicmo/bolt [coverity]: https://scan.coverity.com/projects/bolt [cov-build]: https://scan.coverity.com/download [valgrind]: https://gist.github.com/gicmo/327dad149fcb386ac7f59e279b8ba322 bolt-0.2/INSTALL.md000066400000000000000000000011711324740507400137510ustar00rootroot00000000000000BUILDING ======== The [meson][meson] build system is used to configure and compile bolt. meson build # configure bolt, use build as buildir ninja -C build # compile it ninja -C build test # run the tests NB: `boltd` comes with configuration files for dbus and PolicyKit that need to be installed to the proper locations. It is probably a good idea to manually specify them with the correct values for the current distribution. This can be done by passing the corresponding options to meson: --sysconfdir=/etc --localstatedir=/var --sharedstatedir=/var/lib [meson]: http://mesonbuild.com/ bolt-0.2/README.md000066400000000000000000000044571324740507400136120ustar00rootroot00000000000000bolt ==== Userpsace system daemon to enable security levels for *Thunderbolt™ 3* on GNU/Linux®. Introduction ------------ Thunderbolt™ is the brand name of a hardware interface developed by Intel® that allows the connection of external peripherals to a computer. Devices connected via Thunderbolt can be DMA masters and thus read system memory without interference of the operating system (or even the CPU). Version 3 of the interface provides 4 different security levels, in order to mitigate the aforementioned security risk that connected devices pose to the system. The security level is set by the system firmware. The four security levels are: * `none`: Security disabled, all devices will fully functional on connect. * `dponly`: Only pass the display-port stream through to the connected device. * `user`: Connected devices need to be manually authorized by the user. * `secure`: As 'user', but also challenge the device with a secret key to verify its identity. The Linux kernel, starting with version 4.13, provides an interface via sysfs that enables userspace query the security level, the status of connected devices and, most importantly, to authorize devices, if the security level demands it. boltd - the system daemon ------------------------- The core of bolt is a system daemon (`boltd`) that interfaces with sysfs and exposes devices via D-Bus to clients. It also has a database of previously authorized devices (and their keys) and will, depending on the policy set for the individual devices, automatically authorize newly connected devices without user interaction. boltctl - command line client ----------------------------- The `boltctl` command line can be used to manage thunderbolt devices via `boltd`. It can list devices, monitor changes and initiate auhtorization of devices. Installation ============ The [meson][meson] build system is used to configure and compile bolt. meson build # configure bolt, use build as buildir ninja -C build # compile it ninja -C build test # run the tests See [INSTALL][install] for more infromation, [BUGS][bugs] for how to file issues and [HACKING][hacking] how to contribute. [meson]: http://mesonbuild.com/ [install]: INSTALL.md [bugs]: BUGS.md [hacking]: HACKING.md bolt-0.2/boltd/000077500000000000000000000000001324740507400134255ustar00rootroot00000000000000bolt-0.2/boltd/bolt-auth.c000066400000000000000000000167521324740507400155030ustar00rootroot00000000000000/* * Copyright © 2017 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 library 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 . * * Authors: * Christian J. Kellner */ #include "config.h" #include "bolt-auth.h" #include "bolt-device.h" #include "bolt-io.h" #include "bolt-store.h" #include static void async_result_iface_init (GAsyncResultIface *iface); struct _BoltAuth { GObject object; GObject *origin; BoltSecurity level; BoltKey *key; /* the device */ BoltDevice *dev; /* result */ GError *error; }; enum { PROP_0, PROP_ORIGIN, PROP_LEVEL, PROP_KEY, PROP_DEVICE, PROP_ERROR, PROP_LAST }; static GParamSpec *props[PROP_LAST] = { NULL, }; G_DEFINE_TYPE_WITH_CODE (BoltAuth, bolt_auth, G_TYPE_OBJECT, G_IMPLEMENT_INTERFACE (G_TYPE_ASYNC_RESULT, async_result_iface_init)); static void bolt_auth_finalize (GObject *object) { BoltAuth *auth = BOLT_AUTH (object); g_clear_object (&auth->origin); g_clear_object (&auth->key); g_clear_object (&auth->dev); g_clear_error (&auth->error); G_OBJECT_CLASS (bolt_auth_parent_class)->finalize (object); } static void bolt_auth_init (BoltAuth *auth) { } static void bolt_auth_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { BoltAuth *auth = BOLT_AUTH (object); switch (prop_id) { case PROP_ORIGIN: g_value_set_object (value, auth->origin); break; case PROP_LEVEL: g_value_set_enum (value, auth->level); break; case PROP_KEY: g_value_set_object (value, auth->key); break; case PROP_DEVICE: g_value_set_object (value, auth->dev); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); } } static void bolt_auth_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { BoltAuth *auth = BOLT_AUTH (object); switch (prop_id) { case PROP_ORIGIN: auth->origin = g_value_dup_object (value); break; case PROP_LEVEL: auth->level = g_value_get_enum (value); break; case PROP_KEY: auth->key = g_value_dup_object (value); break; case PROP_DEVICE: g_return_if_fail (auth->dev == NULL); auth->dev = g_value_dup_object (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); } } static void bolt_auth_class_init (BoltAuthClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); gobject_class->finalize = bolt_auth_finalize; gobject_class->get_property = bolt_auth_get_property; gobject_class->set_property = bolt_auth_set_property; props[PROP_ORIGIN] = g_param_spec_object ("origin", NULL, NULL, G_TYPE_OBJECT, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_NICK); props[PROP_LEVEL] = g_param_spec_enum ("level", NULL, NULL, BOLT_TYPE_SECURITY, BOLT_SECURITY_NONE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_NICK); props[PROP_KEY] = g_param_spec_object ("key", NULL, NULL, BOLT_TYPE_KEY, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_NICK); props[PROP_DEVICE] = g_param_spec_object ("device", NULL, NULL, BOLT_TYPE_DEVICE, G_PARAM_READWRITE | G_PARAM_STATIC_NICK); props[PROP_ERROR] = g_param_spec_boxed ("error", NULL, NULL, G_TYPE_ERROR, G_PARAM_READWRITE | G_PARAM_STATIC_NICK); g_object_class_install_properties (gobject_class, PROP_LAST, props); } /* async result methods */ static gpointer async_result_get_user_data (GAsyncResult *res) { return NULL; } static GObject * async_result_get_source_object (GAsyncResult *res) { return G_OBJECT (BOLT_AUTH (res)->dev); } static gboolean async_result_is_tagged (GAsyncResult *res, gpointer source_tag) { return FALSE; } static void async_result_iface_init (GAsyncResultIface *iface) { iface->get_source_object = async_result_get_source_object; iface->get_user_data = async_result_get_user_data; iface->is_tagged = async_result_is_tagged; } /* public methods */ BoltAuth * bolt_auth_new (gpointer origin, BoltSecurity level, BoltKey *key) { BoltAuth *auth; auth = g_object_new (BOLT_TYPE_AUTH, "origin", origin, "level", level, "key", key, NULL); return auth; } void bolt_auth_return_new_error (BoltAuth *auth, GQuark domain, gint code, const char *format, ...) { va_list args; va_start (args, format); auth->error = g_error_new_valist (domain, code, format, args); va_end (args); } void bolt_auth_return_error (BoltAuth *auth, GError **error) { g_return_if_fail (error != NULL && *error != NULL); g_return_if_fail (auth->error == NULL); auth->error = g_steal_pointer (error); } gboolean bolt_auth_check (BoltAuth *auth, GError **error) { if (auth->error) { g_autoptr(GError) err = g_error_copy (auth->error); g_propagate_error (error, g_steal_pointer (&err)); return FALSE; } return TRUE; } BoltSecurity bolt_auth_get_level (BoltAuth *auth) { return auth->level; } BoltKey * bolt_auth_get_key (BoltAuth *auth) { return auth->key; } gpointer bolt_auth_get_origin (BoltAuth *auth) { return auth->origin; } BoltStatus bolt_auth_to_status (BoltAuth *auth) { g_return_val_if_fail (BOLT_IS_AUTH (auth), BOLT_STATUS_UNKNOWN); if (auth->error != NULL) { return BOLT_STATUS_AUTH_ERROR; } else if (auth->level == BOLT_SECURITY_SECURE) { BoltKeyState ks = bolt_key_get_state (auth->key); if (ks == BOLT_KEY_NEW) return BOLT_STATUS_AUTHORIZED_NEWKEY; else return BOLT_STATUS_AUTHORIZED_SECURE; } else if (auth->level == BOLT_SECURITY_USER) { BoltKeyState ks = bolt_key_get_state (auth->key); if (ks == BOLT_KEY_NEW) return BOLT_STATUS_AUTHORIZED_NEWKEY; else return BOLT_STATUS_AUTHORIZED; } return BOLT_STATUS_UNKNOWN; } bolt-0.2/boltd/bolt-auth.h000066400000000000000000000036141324740507400155010ustar00rootroot00000000000000/* * Copyright © 2017 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 library 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 . * * Authors: * Christian J. Kellner */ #pragma once #include "bolt-enums.h" #include "bolt-key.h" #include G_BEGIN_DECLS #define BOLT_TYPE_AUTH bolt_auth_get_type () G_DECLARE_FINAL_TYPE (BoltAuth, bolt_auth, BOLT, AUTH, GObject); BoltAuth * bolt_auth_new (gpointer origin, BoltSecurity level, BoltKey *key); void bolt_auth_return_new_error (BoltAuth *auth, GQuark domain, gint code, const char *format, ...) G_GNUC_PRINTF (4, 5); void bolt_auth_return_error (BoltAuth *auth, GError **error); gboolean bolt_auth_check (BoltAuth *auth, GError **error); BoltSecurity bolt_auth_get_level (BoltAuth *auth); BoltKey * bolt_auth_get_key (BoltAuth *auth); gpointer bolt_auth_get_origin (BoltAuth *auth); BoltStatus bolt_auth_to_status (BoltAuth *auth); G_END_DECLS bolt-0.2/boltd/bolt-bouncer.c000066400000000000000000000174021324740507400161700ustar00rootroot00000000000000/* * Copyright © 2017 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 library 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 . * * Authors: * Christian J. Kellner */ #include "config.h" #include "bolt-bouncer.h" #include "bolt-log.h" #include "bolt-str.h" #include "bolt-exported.h" #include #include static void bouncer_initable_iface_init (GInitableIface *iface); static gboolean bouncer_initialize (GInitable *initable, GCancellable *cancellable, GError **error); #ifndef HAVE_POLKIT_AUTOPTR G_DEFINE_AUTOPTR_CLEANUP_FUNC (PolkitAuthorizationResult, g_object_unref) G_DEFINE_AUTOPTR_CLEANUP_FUNC (PolkitDetails, g_object_unref) G_DEFINE_AUTOPTR_CLEANUP_FUNC (PolkitSubject, g_object_unref) #endif struct _BoltBouncer { GObject object; /* */ PolkitAuthority *authority; }; G_DEFINE_TYPE_WITH_CODE (BoltBouncer, bolt_bouncer, G_TYPE_OBJECT, G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, bouncer_initable_iface_init)); static void bolt_bouncer_finalize (GObject *object) { BoltBouncer *bouncer = BOLT_BOUNCER (object); g_clear_object (&bouncer->authority); G_OBJECT_CLASS (bolt_bouncer_parent_class)->finalize (object); } static void bolt_bouncer_init (BoltBouncer *bouncer) { } static void bolt_bouncer_class_init (BoltBouncerClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); gobject_class->finalize = bolt_bouncer_finalize; } static void bouncer_initable_iface_init (GInitableIface *iface) { iface->init = bouncer_initialize; } static gboolean bouncer_initialize (GInitable *initable, GCancellable *cancellable, GError **error) { BoltBouncer *bnc = BOLT_BOUNCER (initable); bnc->authority = polkit_authority_get_sync (cancellable, error); return bnc->authority != NULL; } /* internal methods */ static gboolean bolt_bouncer_check_action (BoltBouncer *bnc, GDBusMethodInvocation *inv, const char *action, gboolean *authorized, GError **error) { g_autoptr(PolkitSubject) subject = NULL; g_autoptr(PolkitDetails) details = NULL; g_autoptr(PolkitAuthorizationResult) res = NULL; PolkitCheckAuthorizationFlags flags; const char *sender; sender = g_dbus_method_invocation_get_sender (inv); subject = polkit_system_bus_name_new (sender); details = polkit_details_new (); flags = POLKIT_CHECK_AUTHORIZATION_FLAGS_ALLOW_USER_INTERACTION; res = polkit_authority_check_authorization_sync (bnc->authority, subject, action, details, flags, NULL, error); if (res == NULL) return FALSE; *authorized = polkit_authorization_result_get_is_authorized (res); return TRUE; } static gboolean handle_authorize_method (BoltExported *exported, GDBusMethodInvocation *inv, GError **error, gpointer user_data) { g_autoptr(PolkitSubject) subject = NULL; g_autoptr(PolkitDetails) details = NULL; gboolean authorized = FALSE; BoltBouncer *bnc; const char *method_name; const char *sender; const char *action; bnc = BOLT_BOUNCER (user_data); method_name = g_dbus_method_invocation_get_method_name (inv); sender = g_dbus_method_invocation_get_sender (inv); subject = polkit_system_bus_name_new (sender); details = polkit_details_new (); action = NULL; if (bolt_streq (method_name, "EnrollDevice")) action = "org.freedesktop.bolt.enroll"; else if (bolt_streq (method_name, "Authorize")) action = "org.freedesktop.bolt.authorize"; else if (bolt_streq (method_name, "ForgetDevice")) action = "org.freedesktop.bolt.manage"; else if (bolt_streq (method_name, "ListDevices")) authorized = TRUE; else if (bolt_streq (method_name, "DeviceByUid")) authorized = TRUE; if (!authorized && action) { PolkitCheckAuthorizationFlags flags; g_autoptr(PolkitAuthorizationResult) res = NULL; flags = POLKIT_CHECK_AUTHORIZATION_FLAGS_ALLOW_USER_INTERACTION; res = polkit_authority_check_authorization_sync (bnc->authority, subject, action, details, flags, NULL, error); if (res == NULL) return FALSE; authorized = polkit_authorization_result_get_is_authorized (res); } if (authorized == FALSE) g_set_error (error, G_DBUS_ERROR, G_DBUS_ERROR_ACCESS_DENIED, "Bolt operation '%s' not allowed for user", method_name); return authorized; } static gboolean handle_authorize_property (BoltExported *exported, const char *name, gboolean setting, GDBusMethodInvocation *inv, GError **error, gpointer user_data) { const char *type_name = G_OBJECT_TYPE_NAME (exported); const char *action = NULL; gboolean authorized = FALSE; BoltBouncer *bnc; bnc = BOLT_BOUNCER (user_data); if (bolt_streq (type_name, "BoltDevice")) { if (bolt_streq (name, "label")) action = "org.freedesktop.bolt.manage"; } else if (bolt_streq (type_name, "BoltManager")) { if (bolt_streq (name, "auth-mode")) action = "org.freedesktop.bolt.manage"; } if (!authorized && action) { gboolean ok; ok = bolt_bouncer_check_action (bnc, inv, action, &authorized, error); if (!ok) return FALSE; } if (authorized == FALSE) g_set_error (error, G_DBUS_ERROR, G_DBUS_ERROR_ACCESS_DENIED, "Setting property of '%s.%s' not allowed for user", type_name, name); return authorized; } /* public methods */ BoltBouncer * bolt_bouncer_new (GCancellable *cancellable, GError **error) { return g_initable_new (BOLT_TYPE_BOUNCER, cancellable, error, NULL); } void bolt_bouncer_add_client (BoltBouncer *bnc, gpointer client) { if (BOLT_IS_EXPORTED (client)) { g_signal_connect (client, "authorize-method", G_CALLBACK (handle_authorize_method), bnc); g_signal_connect (client, "authorize-property", G_CALLBACK (handle_authorize_property), bnc); } else { bolt_critical (LOG_TOPIC ("bouncer"), "unknown client class"); } } bolt-0.2/boltd/bolt-bouncer.h000066400000000000000000000022631324740507400161740ustar00rootroot00000000000000/* * Copyright © 2017 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 library 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 . * * Authors: * Christian J. Kellner */ #pragma once #include G_BEGIN_DECLS #define BOLT_TYPE_BOUNCER bolt_bouncer_get_type () G_DECLARE_FINAL_TYPE (BoltBouncer, bolt_bouncer, BOLT, BOUNCER, GObject); BoltBouncer * bolt_bouncer_new (GCancellable *cancellable, GError **error); void bolt_bouncer_add_client (BoltBouncer *bnc, gpointer client); G_END_DECLS bolt-0.2/boltd/bolt-config.c000066400000000000000000000057761324740507400160130ustar00rootroot00000000000000/* * Copyright © 2018 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 library 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 . * * Authors: * Christian J. Kellner */ #include "config.h" #include "bolt-enums.h" #include "bolt-error.h" #include "bolt-config.h" #define DAEMON_GROUP "config" #define CFG_VERSION 1 #define DEFAULT_POLICY_KEY "DefaultPolicy" #define AUTH_MODE_KEY "AuthMode" GKeyFile * bolt_config_user_init (void) { GKeyFile *cfg; cfg = g_key_file_new (); g_key_file_set_comment (cfg, NULL, NULL, " Generated by boltd - do not edit", NULL); g_key_file_set_uint64 (cfg, DAEMON_GROUP, "version", CFG_VERSION); return cfg; } BoltTri bolt_config_load_default_policy (GKeyFile *cfg, BoltPolicy *policy, GError **error) { g_autoptr(GError) err = NULL; g_autofree char *str = NULL; BoltPolicy p; if (cfg == NULL) return TRI_NO; str = g_key_file_get_string (cfg, DAEMON_GROUP, DEFAULT_POLICY_KEY, &err); if (str == NULL) { int res = bolt_err_notfound (err) ? TRI_NO : TRI_ERROR; if (res == TRI_ERROR) g_propagate_error (error, g_steal_pointer (&err)); return res; } p = bolt_policy_from_string (str); if (!bolt_policy_validate (p)) { g_set_error (error, BOLT_ERROR, BOLT_ERROR_CFG, "invalid policy: %s", str); return TRI_ERROR; } *policy = p; return TRI_YES; } BoltTri bolt_config_load_auth_mode (GKeyFile *cfg, BoltAuthMode *authmode, GError **error) { g_autoptr(GError) err = NULL; g_autofree char *str = NULL; guint flags = 0; gboolean ok; if (cfg == NULL) return TRI_NO; str = g_key_file_get_string (cfg, DAEMON_GROUP, AUTH_MODE_KEY, &err); if (str == NULL) { int res = bolt_err_notfound (err) ? TRI_NO : TRI_ERROR; if (res == TRI_ERROR) g_propagate_error (error, g_steal_pointer (&err)); return res; } ok = bolt_flags_from_string (BOLT_TYPE_AUTH_MODE, str, &flags, error); if (!ok) return TRI_ERROR; if (authmode) *authmode = flags; return TRI_YES; } void bolt_config_set_auth_mode (GKeyFile *cfg, const char *authmode) { g_return_if_fail (cfg != NULL); g_key_file_set_string (cfg, DAEMON_GROUP, AUTH_MODE_KEY, authmode); } bolt-0.2/boltd/bolt-config.h000066400000000000000000000027151324740507400160060ustar00rootroot00000000000000/* * Copyright © 2018 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 library 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 . * * Authors: * Christian J. Kellner */ #pragma once #include #include "bolt-enums.h" G_BEGIN_DECLS typedef enum BoltTri { TRI_ERROR = -1, TRI_NO = 0, TRI_YES = 1, } BoltTri; GKeyFile * bolt_config_user_init (void); BoltTri bolt_config_load_default_policy (GKeyFile *cfg, BoltPolicy *policy, GError **error); BoltTri bolt_config_load_auth_mode (GKeyFile *cfg, BoltAuthMode *authmode, GError **error); void bolt_config_set_auth_mode (GKeyFile *cfg, const char *authmode); G_END_DECLS bolt-0.2/boltd/bolt-daemon.c000066400000000000000000000143611324740507400157770ustar00rootroot00000000000000/* * Copyright © 2017 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 library 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 . * * Authors: * Christian J. Kellner */ #include "config.h" #include "bolt-log.h" #include "bolt-manager.h" #include "bolt-names.h" #include "bolt-str.h" #include "bolt-term.h" #include "bolt-daemon-resource.h" #include #include #include #include /* globals */ static BoltManager *manager = NULL; static GMainLoop *main_loop = NULL; static guint name_owner_id = 0; typedef struct _LogCfg { gboolean debug; const char *session_id; } LogCfg; static GLogWriterOutput daemon_logger (GLogLevelFlags level, const GLogField *fields, gsize n_fields, gpointer user_data) { g_autoptr(BoltLogCtx) ctx = NULL; GLogWriterOutput res = G_LOG_WRITER_UNHANDLED; LogCfg *log = user_data; g_return_val_if_fail (fields != NULL, G_LOG_WRITER_UNHANDLED); g_return_val_if_fail (n_fields > 0, G_LOG_WRITER_UNHANDLED); ctx = bolt_log_ctx_acquire (fields, n_fields); if (ctx == NULL) return G_LOG_WRITER_UNHANDLED; /* replace the log context field with the session id */ bolt_log_ctx_set_id (ctx, log->session_id); if (level & G_LOG_LEVEL_DEBUG && log->debug == FALSE) { const char *domain = blot_log_ctx_get_domain (ctx); const char *env = g_getenv ("G_MESSAGES_DEBUG"); if (!env || !domain || !strstr (env, domain)) return G_LOG_WRITER_UNHANDLED; } if (fileno (stderr) < 0) return G_LOG_WRITER_UNHANDLED; if (g_log_writer_is_journald (fileno (stderr))) res = bolt_log_journal (ctx, level, 0); if (res == G_LOG_WRITER_UNHANDLED) res = bolt_log_stdstream (ctx, level, 0); return res; } static void on_bus_acquired (GDBusConnection *connection, const gchar *name, gpointer user_data) { g_autoptr(GError) error = NULL; bolt_debug (LOG_TOPIC ("dbus"), "got the bus [%s]", name); /* */ manager = g_initable_new (BOLT_TYPE_MANAGER, NULL, &error, NULL); if (manager == NULL) { bolt_error (LOG_ERR (error), "could not create manager"); exit (EXIT_FAILURE); } if (!bolt_manager_export (manager, connection, &error)) bolt_warn_err (error, LOG_TOPIC ("dbus"), "error exporting the manager"); } static void on_name_acquired (GDBusConnection *connection, const gchar *name, gpointer user_data) { bolt_debug (LOG_TOPIC ("dbus"), "got the name"); bolt_manager_got_the_name (manager); } static void on_name_lost (GDBusConnection *connection, const gchar *name, gpointer user_data) { bolt_debug (LOG_TOPIC ("dbus"), "name lost; shutting down..."); g_clear_object (&manager); g_bus_unown_name (name_owner_id); g_main_loop_quit (main_loop); } int main (int argc, char **argv) { g_autofree char *session_id = NULL; g_autoptr(GError) error = NULL; GOptionContext *context; gboolean replace = FALSE; gboolean show_version = FALSE; gboolean session_bus = FALSE; GBusType bus_type = G_BUS_TYPE_SYSTEM; GBusNameOwnerFlags flags; LogCfg log = { FALSE, }; const GOptionEntry options[] = { { "replace", 'r', 0, G_OPTION_ARG_NONE, &replace, "Replace old daemon.", NULL }, { "session-bus", 0, 0, G_OPTION_ARG_NONE, &session_bus, "Use the session bus.", NULL}, { "verbose", 'v', 0, G_OPTION_ARG_NONE, &log.debug, "Enable debug output.", NULL }, { "version", 0, 0, G_OPTION_ARG_NONE, &show_version, "Print daemon version.", NULL}, { NULL } }; setlocale (LC_ALL, ""); g_setenv ("GIO_USE_VFS", "local", TRUE); g_set_prgname (argv[0]); g_log_set_writer_func (daemon_logger, &log, NULL); context = g_option_context_new (""); g_option_context_set_summary (context, "Thunderbolt system daemon"); g_option_context_add_main_entries (context, options, PACKAGE_NAME); if (!g_option_context_parse (context, &argc, &argv, &error)) { g_printerr ("%s: %s", g_get_application_name (), error->message); g_printerr ("\n"); g_printerr ("Try \"%s --help\" for more information.", g_get_prgname ()); g_printerr ("\n"); g_option_context_free (context); return EXIT_FAILURE; } if (show_version) { g_print (PACKAGE_NAME " " PACKAGE_VERSION "\n"); return EXIT_SUCCESS; } /* setup logging */ if (log.debug == FALSE && g_getenv ("G_MESSAGES_DEBUG")) { const char *domains = g_getenv ("G_MESSAGES_DEBUG"); log.debug = bolt_streq (domains, "all"); } session_id = g_uuid_string_random (); log.session_id = session_id; g_resources_register (bolt_daemon_get_resource ()); bolt_msg (LOG_DIRECT (BOLT_LOG_VERSION, PACKAGE_VERSION), LOG_ID (STARTUP), PACKAGE_NAME " " PACKAGE_VERSION " starting up."); /* hop on the bus, Gus */ flags = G_BUS_NAME_OWNER_FLAGS_ALLOW_REPLACEMENT; if (replace) flags |= G_BUS_NAME_OWNER_FLAGS_REPLACE; if (session_bus) bus_type = G_BUS_TYPE_SESSION; name_owner_id = g_bus_own_name (bus_type, BOLT_DBUS_NAME, flags, on_bus_acquired, on_name_acquired, on_name_lost, NULL, NULL); main_loop = g_main_loop_new (NULL, FALSE); g_main_loop_run (main_loop); /* When all is said and done, more is said then done. */ g_main_loop_unref (main_loop); return EXIT_SUCCESS; } bolt-0.2/boltd/bolt-device.c000066400000000000000000000714621324740507400160000ustar00rootroot00000000000000/* * Copyright © 2017 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 library 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 . * * Authors: * Christian J. Kellner */ #include "config.h" #include "bolt-device.h" #include "bolt-enums.h" #include "bolt-error.h" #include "bolt-io.h" #include "bolt-log.h" #include "bolt-manager.h" #include "bolt-names.h" #include "bolt-store.h" #include "bolt-str.h" #include "bolt-sysfs.h" #include "bolt-time.h" #include #include /* dbus property setter */ static gboolean handle_set_label (BoltExported *obj, const char *name, const GValue *value, GError **error); /* dbus method calls */ static gboolean handle_authorize (BoltExported *object, GVariant *params, GDBusMethodInvocation *invocation); struct _BoltDevice { BoltExported object; /* device props */ char *dbus_path; char *uid; char *name; char *vendor; BoltDeviceType type; BoltStatus status; /* when device is attached */ char *syspath; BoltSecurity security; char *parent; guint64 conntime; guint64 authtime; /* when device is stored */ BoltStore *store; BoltPolicy policy; BoltKeyState key; guint64 storetime; char *label; }; enum { PROP_0, PROP_STORE, PROP_SECURITY, /* exported properties start here, */ PROP_UID, PROP_NAME, PROP_VENDOR, PROP_TYPE, PROP_STATUS, PROP_PARENT, PROP_SYSFS, PROP_CONNTIME, PROP_AUTHTIME, PROP_STORED, PROP_POLICY, PROP_HAVE_KEY, PROP_STORETIME, PROP_LABEL, PROP_LAST, PROP_EXPORTED = PROP_UID }; static GParamSpec *props[PROP_LAST] = {NULL, }; enum { SIGNAL_STATUS_CHANGED, SIGNAL_LAST }; static guint signals[SIGNAL_LAST] = {0}; G_DEFINE_TYPE (BoltDevice, bolt_device, BOLT_TYPE_EXPORTED) static void bolt_device_finalize (GObject *object) { BoltDevice *dev = BOLT_DEVICE (object); g_clear_object (&dev->store); g_free (dev->dbus_path); g_free (dev->uid); g_free (dev->name); g_free (dev->vendor); g_free (dev->parent); g_free (dev->syspath); g_free (dev->label); G_OBJECT_CLASS (bolt_device_parent_class)->finalize (object); } static void bolt_device_init (BoltDevice *dev) { } static void bolt_device_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { BoltDevice *dev = BOLT_DEVICE (object); switch (prop_id) { case PROP_STORE: g_value_set_object (value, dev->store); break; case PROP_SECURITY: g_value_set_enum (value, dev->security); break; case PROP_UID: g_value_set_string (value, dev->uid); break; case PROP_NAME: g_value_set_string (value, dev->name); break; case PROP_TYPE: g_value_set_enum (value, dev->type); break; case PROP_VENDOR: g_value_set_string (value, dev->vendor); break; case PROP_STATUS: g_value_set_enum (value, dev->status); break; case PROP_PARENT: g_value_set_string (value, dev->parent); break; case PROP_SYSFS: g_value_set_string (value, dev->syspath); break; case PROP_CONNTIME: g_value_set_uint64 (value, dev->conntime); break; case PROP_AUTHTIME: g_value_set_uint64 (value, dev->authtime); break; case PROP_STORED: g_value_set_boolean (value, dev->store != NULL); break; case PROP_POLICY: g_value_set_enum (value, dev->policy); break; case PROP_HAVE_KEY: g_value_set_enum (value, dev->key); break; case PROP_STORETIME: g_value_set_uint64 (value, dev->storetime); break; case PROP_LABEL: g_value_set_string (value, dev->label); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); } } static void bolt_device_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { BoltDevice *dev = BOLT_DEVICE (object); switch (prop_id) { case PROP_STORE: dev->store = g_value_dup_object (value); g_object_notify_by_pspec (object, props[PROP_STORED]); break; case PROP_SECURITY: dev->security = g_value_get_enum (value); break; case PROP_UID: g_return_if_fail (dev->uid == NULL); dev->uid = g_value_dup_string (value); break; case PROP_NAME: g_clear_pointer (&dev->name, g_free); dev->name = g_value_dup_string (value); break; case PROP_VENDOR: g_clear_pointer (&dev->vendor, g_free); dev->vendor = g_value_dup_string (value); break; case PROP_TYPE: dev->type = g_value_get_enum (value); break; case PROP_STATUS: { BoltStatus old = dev->status; BoltStatus now = g_value_get_enum (value); if (old == now) break; dev->status = now; g_signal_emit (dev, signals[SIGNAL_STATUS_CHANGED], 0, old); break; } case PROP_PARENT: g_clear_pointer (&dev->parent, g_free); dev->parent = g_value_dup_string (value); break; case PROP_SYSFS: g_clear_pointer (&dev->syspath, g_free); dev->syspath = g_value_dup_string (value); break; case PROP_CONNTIME: dev->conntime = g_value_get_uint64 (value); break; case PROP_AUTHTIME: dev->authtime = g_value_get_uint64 (value); break; case PROP_POLICY: dev->policy = g_value_get_enum (value); break; case PROP_HAVE_KEY: dev->key = g_value_get_enum (value); break; case PROP_STORETIME: dev->storetime = g_value_get_uint64 (value); break; case PROP_LABEL: g_clear_pointer (&dev->label, g_free); dev->label = g_value_dup_string (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); } } static void bolt_device_class_init (BoltDeviceClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); BoltExportedClass *exported_class = BOLT_EXPORTED_CLASS (klass); gobject_class->finalize = bolt_device_finalize; gobject_class->get_property = bolt_device_get_property; gobject_class->set_property = bolt_device_set_property; props[PROP_STORE] = g_param_spec_object ("store", NULL, NULL, BOLT_TYPE_STORE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); props[PROP_SECURITY] = g_param_spec_enum ("security", "Security", NULL, BOLT_TYPE_SECURITY, BOLT_SECURITY_NONE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); props[PROP_UID] = g_param_spec_string ("uid", "Uid", NULL, NULL, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); props[PROP_NAME] = g_param_spec_string ("name", "Name", NULL, NULL, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); props[PROP_VENDOR] = g_param_spec_string ("vendor", "Vendor", NULL, NULL, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); props[PROP_TYPE] = g_param_spec_enum ("type", "Type", NULL, BOLT_TYPE_DEVICE_TYPE, BOLT_DEVICE_PERIPHERAL, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); props[PROP_STATUS] = g_param_spec_enum ("status", "Status", NULL, BOLT_TYPE_STATUS, BOLT_STATUS_DISCONNECTED, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); props[PROP_PARENT] = g_param_spec_string ("parent", "Parent", NULL, NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); props[PROP_SYSFS] = g_param_spec_string ("sysfs-path", "SysfsPath", NULL, NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); props[PROP_CONNTIME] = g_param_spec_uint64 ("conntime", "ConnectTime", NULL, 0, G_MAXUINT64, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); props[PROP_AUTHTIME] = g_param_spec_uint64 ("authtime", "AuthorizeTime", NULL, 0, G_MAXUINT64, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); props[PROP_STORED] = g_param_spec_boolean ("stored", "Stored", NULL, FALSE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); props[PROP_POLICY] = g_param_spec_enum ("policy", "Policy", NULL, BOLT_TYPE_POLICY, BOLT_POLICY_DEFAULT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); props[PROP_HAVE_KEY] = g_param_spec_enum ("key", "Key", NULL, BOLT_TYPE_KEY_STATE, BOLT_KEY_MISSING, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); props[PROP_STORETIME] = g_param_spec_uint64 ("storetime", "StoreTime", NULL, 0, G_MAXUINT64, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); props[PROP_LABEL] = g_param_spec_string ("label", "Label", NULL, NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); g_object_class_install_properties (gobject_class, PROP_LAST, props); signals[SIGNAL_STATUS_CHANGED] = g_signal_new ("status-changed", G_TYPE_FROM_CLASS (gobject_class), G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 1, BOLT_TYPE_STATUS); bolt_exported_class_set_interface_info (exported_class, BOLT_DBUS_DEVICE_INTERFACE, "/boltd/org.freedesktop.bolt.xml"); bolt_exported_class_export_properties (exported_class, PROP_EXPORTED, PROP_LAST, props); bolt_exported_class_property_setter (exported_class, props[PROP_LABEL], handle_set_label); bolt_exported_class_export_method (exported_class, "Authorize", handle_authorize); } /* internal methods */ static const char * read_sysattr_name (struct udev_device *udev, const char *attr, GError **error) { g_autofree char *s = NULL; const char *v; s = g_strdup_printf ("%s_name", attr); v = udev_device_get_sysattr_value (udev, s); if (v != NULL) return v; v = udev_device_get_sysattr_value (udev, attr); if (v == NULL) g_set_error (error, BOLT_ERROR, BOLT_ERROR_UDEV, "failed to get sysfs attr: %s", attr); return v; } static gint read_sysfs_attr_int (struct udev_device *device, const char *attr) { const char *str; char *end; gint64 val; str = udev_device_get_sysattr_value (device, attr); if (str == NULL) return 0; val = g_ascii_strtoll (str, &end, 0); if (str == end) return 0; if (val > G_MAXINT || val < G_MININT) { bolt_warn ("value read from sysfs outside of gint's range."); val = 0; } return (gint) val; } static gboolean string_nonzero (const char *str) { return str != NULL && str[0] != '\0'; } static struct udev_device * bolt_sysfs_get_parent (struct udev_device *udev, GError **error) { struct udev_device * parent = udev_device_get_parent (udev); if (parent == NULL) g_set_error (error, BOLT_ERROR, BOLT_ERROR_UDEV, "could not get parent udev device"); return parent; } static const char * bolt_sysfs_get_parent_uid (struct udev_device *udev) { struct udev_device *parent; const char *uid = NULL; parent = udev_device_get_parent (udev); if (parent) uid = udev_device_get_sysattr_value (parent, "unique_id"); return uid; } typedef enum BoltStatTime { BOLT_ST_ATIME, BOLT_ST_CTIME, BOLT_ST_MTIME } BoltStatTime; static gint64 bolt_sysfs_device_get_time (struct udev_device *udev, BoltStatTime st) { const char *path; struct stat sb; gint64 ms; int r; path = udev_device_get_syspath (udev); if (path == NULL) return 0; r = lstat (path, &sb); if (r == -1) return 0; switch (st) { case BOLT_ST_CTIME: ms = (gint64) sb.st_ctim.tv_sec; break; case BOLT_ST_ATIME: ms = (gint64) sb.st_atim.tv_sec; break; case BOLT_ST_MTIME: ms = (gint64) sb.st_mtim.tv_sec; break; default: bolt_warn_enum_unhandled (BoltStatTime, st); return 0; } if (ms < 0) ms = 0; return ms; } static BoltStatus bolt_status_from_udev (struct udev_device *udev, BoltSecurity security) { gint authorized; const char *key; gboolean have_key; authorized = read_sysfs_attr_int (udev, "authorized"); if (authorized == 2) return BOLT_STATUS_AUTHORIZED_SECURE; key = udev_device_get_sysattr_value (udev, "key"); have_key = string_nonzero (key); if (authorized == 1) { if (security == BOLT_SECURITY_DPONLY) return BOLT_STATUS_AUTHORIZED_DPONLY; else if (have_key) return BOLT_STATUS_AUTHORIZED_NEWKEY; else return BOLT_STATUS_AUTHORIZED; } else if (authorized == 0 && have_key) { return BOLT_STATUS_AUTH_ERROR; } return BOLT_STATUS_CONNECTED; } static const char * cleanup_name (const char *name, const char *vendor, GError **error) { g_return_val_if_fail (vendor != NULL, NULL); g_return_val_if_fail (name != NULL, NULL); /* some devices have the vendor name as a prefix */ if (!g_str_has_prefix (name, vendor)) return name; name += strlen (vendor); while (g_ascii_isspace (*name)) name++; if (*name == '\0') { g_set_error_literal (error, BOLT_ERROR, BOLT_ERROR_UDEV, "device has empty name after cleanup"); return NULL; } return name; } /* device authorization */ typedef struct { BoltAuth *auth; /* the outer callback */ GAsyncReadyCallback callback; gpointer user_data; } AuthData; static void auth_data_free (gpointer data) { AuthData *auth = data; g_clear_object (&auth->auth); g_slice_free (AuthData, auth); } static gboolean authorize_device_internal (BoltDevice *dev, BoltAuth *auth, GError **error) { g_autoptr(DIR) devdir = NULL; BoltKey *key; BoltSecurity level; gboolean ok; key = bolt_auth_get_key (auth); level = bolt_auth_get_level (auth); devdir = bolt_opendir (dev->syspath, error); if (devdir == NULL) return FALSE; ok = bolt_verify_uid (dirfd (devdir), dev->uid, error); if (!ok) return FALSE; if (key) { int keyfd; bolt_debug (LOG_DEV (dev), "writing key"); keyfd = bolt_openat (dirfd (devdir), "key", O_WRONLY | O_CLOEXEC, error); if (keyfd < 0) return FALSE; ok = bolt_key_write_to (key, keyfd, &level, error); close (keyfd); if (!ok) return FALSE; } bolt_debug (LOG_DEV (dev), "writing authorization"); ok = bolt_write_char_at (dirfd (devdir), "authorized", level, error); return ok; } static void authorize_in_thread (GTask *task, gpointer source, gpointer context, GCancellable *cancellable) { g_autoptr(GError) error = NULL; BoltDevice *dev = source; AuthData *auth_data = context; BoltAuth *auth = auth_data->auth; gboolean ok; ok = authorize_device_internal (dev, auth, &error); if (!ok) g_task_return_new_error (task, BOLT_ERROR, BOLT_ERROR_FAILED, "failed to authorize device: %s", error->message); else g_task_return_boolean (task, TRUE); } static void authorize_thread_done (GObject *object, GAsyncResult *res, gpointer user_data) { g_autoptr(GError) error = NULL; BoltDevice *dev = BOLT_DEVICE (object); GTask *task = G_TASK (res); BoltStatus status; AuthData *auth_data; BoltAuth *auth; gboolean ok; guint64 now; auth_data = g_task_get_task_data (task); auth = auth_data->auth; ok = g_task_propagate_boolean (task, &error); if (!ok) bolt_auth_return_error (auth, &error); now = bolt_now_in_seconds (); status = bolt_auth_to_status (auth); g_object_set (dev, "status", status, "authtime", now, NULL); if (auth_data->callback) auth_data->callback (G_OBJECT (dev), G_ASYNC_RESULT (auth), auth_data->user_data); } void bolt_device_authorize (BoltDevice *dev, BoltAuth *auth, GAsyncReadyCallback callback, gpointer user_data) { AuthData *auth_data; GTask *task; g_object_set (auth, "device", dev, NULL); if (dev->status != BOLT_STATUS_CONNECTED && dev->status != BOLT_STATUS_AUTH_ERROR) { bolt_auth_return_new_error (auth, BOLT_ERROR, BOLT_ERROR_FAILED, "wrong device state: %d", dev->status); if (callback) callback (G_OBJECT (dev), G_ASYNC_RESULT (auth), user_data); return; } task = g_task_new (dev, NULL, authorize_thread_done, NULL); auth_data = g_slice_new (AuthData); auth_data->callback = callback; auth_data->user_data = user_data; auth_data->auth = g_object_ref (auth); g_task_set_task_data (task, auth_data, auth_data_free); g_object_set (dev, "status", BOLT_STATUS_AUTHORIZING, NULL); g_task_run_in_thread (task, authorize_in_thread); g_object_unref (task); } /* dbus property setter */ static gboolean handle_set_label (BoltExported *obj, const char *name, const GValue *value, GError **error) { g_autofree char *nick = NULL; g_autofree char *old = NULL; BoltDevice *dev = BOLT_DEVICE (obj); const char *str = g_value_get_string (value); gboolean ok; nick = bolt_strdup_validate (str); if (nick == NULL) { g_set_error_literal (error, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS, "string is invalid"); return FALSE; } else if (strlen (nick) > 255) { g_set_error_literal (error, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS, "string is too long"); return FALSE; } if (dev->store == NULL) { g_set_error_literal (error, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS, "device is not stored"); return FALSE; } old = dev->label; dev->label = g_steal_pointer (&nick); ok = bolt_store_put_device (dev->store, dev, dev->policy, NULL, error); if (!ok) { bolt_warn_err (*error, LOG_DEV (dev), "failed to store device"); nick = dev->label; dev->label = g_steal_pointer (&old); } return ok; } /* dbus methods */ static void handle_authorize_done (GObject *device, GAsyncResult *res, gpointer user_data) { GDBusMethodInvocation *inv; GError *error = NULL; BoltAuth *auth; gboolean ok; inv = user_data; auth = BOLT_AUTH (res); ok = bolt_auth_check (auth, &error); if (ok) g_dbus_method_invocation_return_value (inv, g_variant_new ("()")); else g_dbus_method_invocation_take_error (inv, error); } static gboolean handle_authorize (BoltExported *object, GVariant *params, GDBusMethodInvocation *inv) { BoltDevice *dev = BOLT_DEVICE (object); GError *error = NULL; BoltAuth *auth; BoltSecurity level; BoltKey *key; level = dev->security; key = NULL; if (level == BOLT_SECURITY_SECURE) { if (dev->key) key = bolt_store_get_key (dev->store, dev->uid, &error); else level = BOLT_SECURITY_USER; } if (level == BOLT_SECURITY_SECURE && key == NULL) { /* only happens if the key could not be read */ g_dbus_method_invocation_take_error (inv, error); return TRUE; } auth = bolt_auth_new (dev, level, key); bolt_device_authorize (dev, auth, handle_authorize_done, inv); return TRUE; } /* public methods */ BoltDevice * bolt_device_new_for_udev (struct udev_device *udev, GError **error) { struct udev_device *parent_dev; const char *uid; const char *name; const char *vendor; const char *syspath; const char *parent; BoltSecurity security; BoltStatus status; BoltDeviceType type; BoltDevice *dev; guint64 ct, at; uid = udev_device_get_sysattr_value (udev, "unique_id"); if (udev == NULL) { g_set_error (error, BOLT_ERROR, BOLT_ERROR_UDEV, "could not get unique_id for udev"); return NULL; } syspath = udev_device_get_syspath (udev); g_return_val_if_fail (syspath != NULL, NULL); name = read_sysattr_name (udev, "device", error); if (name == NULL) return NULL; vendor = read_sysattr_name (udev, "vendor", error); if (vendor == NULL) return NULL; name = cleanup_name (name, vendor, error); if (name == NULL) return NULL; parent_dev = bolt_sysfs_get_parent (udev, error); if (parent_dev == NULL) return NULL; if (bolt_sysfs_device_is_domain (parent_dev)) { parent = NULL; type = BOLT_DEVICE_HOST; } else { parent = udev_device_get_sysattr_value (parent_dev, "unique_id"); type = BOLT_DEVICE_PERIPHERAL; } ct = (guint64) bolt_sysfs_device_get_time (udev, BOLT_ST_CTIME); security = bolt_sysfs_security_for_device (udev, NULL); status = bolt_status_from_udev (udev, security); at = bolt_status_is_authorized (status) ? ct : 0; dev = g_object_new (BOLT_TYPE_DEVICE, "uid", uid, "name", name, "vendor", vendor, "type", type, "status", status, "sysfs-path", syspath, "parent", parent, "conntime", ct, "authtime", at, "security", security, NULL); return dev; } const char * bolt_device_export (BoltDevice *device, GDBusConnection *connection, GError **error) { const char *path; gboolean ok; g_return_val_if_fail (BOLT_IS_DEVICE (device), FALSE); path = bolt_device_get_object_path (device); ok = bolt_exported_export (BOLT_EXPORTED (device), connection, path, error); return ok ? path : NULL; } void bolt_device_unexport (BoltDevice *device) { bolt_exported_unexport (BOLT_EXPORTED (device)); } BoltStatus bolt_device_connected (BoltDevice *dev, struct udev_device *udev) { const char *syspath; const char *parent; BoltSecurity security; BoltStatus status; guint64 ct, at; syspath = udev_device_get_syspath (udev); security = bolt_sysfs_security_for_device (udev, NULL); status = bolt_status_from_udev (udev, security); parent = bolt_sysfs_get_parent_uid (udev); ct = (guint64) bolt_sysfs_device_get_time (udev, BOLT_ST_CTIME); at = bolt_status_is_authorized (status) ? ct : 0; g_object_set (G_OBJECT (dev), "parent", parent, "sysfs-path", syspath, "security", security, "status", status, "conntime", ct, "authtime", at, NULL); bolt_info (LOG_DEV (dev), "parent is %.13s...", dev->parent); return status; } BoltStatus bolt_device_disconnected (BoltDevice *dev) { g_object_set (G_OBJECT (dev), "parent", NULL, "sysfs-path", NULL, "security", BOLT_SECURITY_NONE, "status", BOLT_STATUS_DISCONNECTED, "conntime", 0, "authtime", 0, NULL); /* check if we have a new key for the device, and * if so, change its state to KEY_HAVE, because * now it is not new anymore. */ if (dev->key == BOLT_KEY_NEW) g_object_set (G_OBJECT (dev), "key", BOLT_KEY_HAVE, NULL); return dev->status; } gboolean bolt_device_is_connected (const BoltDevice *device) { return bolt_status_is_connected (device->status); } BoltStatus bolt_device_update_from_udev (BoltDevice *dev, struct udev_device *udev) { BoltStatus status = bolt_status_from_udev (udev, dev->security); if (status == dev->status) return status; g_object_set (G_OBJECT (dev), "status", status, NULL); if (bolt_status_is_authorized (status) && dev->status != BOLT_STATUS_AUTHORIZING) { dev->authtime = bolt_now_in_seconds (); g_object_notify_by_pspec (G_OBJECT (dev), props[PROP_AUTHTIME]); } return status; } BoltKeyState bolt_device_get_keystate (const BoltDevice *dev) { return dev->key; } const char * bolt_device_get_name (const BoltDevice *dev) { return dev->name; } const char * bolt_device_get_object_path (BoltDevice *device) { g_return_val_if_fail (BOLT_IS_DEVICE (device), FALSE); if (device->dbus_path == NULL) { char *path = NULL; path = g_strdup_printf (BOLT_DBUS_PATH "/devices/%s", device->uid); g_strdelimit (path, "-", '_'); device->dbus_path = path; } return device->dbus_path; } BoltPolicy bolt_device_get_policy (const BoltDevice *dev) { return dev->policy; } const char * bolt_device_get_uid (const BoltDevice *dev) { return dev->uid; } BoltSecurity bolt_device_get_security (const BoltDevice *dev) { return dev->security; } BoltStatus bolt_device_get_status (const BoltDevice *dev) { return dev->status; } gboolean bolt_device_get_stored (const BoltDevice *dev) { return dev->store != NULL; } const char * bolt_device_get_syspath (const BoltDevice *dev) { return dev->syspath; } const char * bolt_device_get_vendor (const BoltDevice *dev) { return dev->vendor; } BoltDeviceType bolt_device_get_device_type (const BoltDevice *dev) { return dev->type; } const char * bolt_device_get_label (const BoltDevice *dev) { return dev->label; } gint64 bolt_device_get_storetime (const BoltDevice *dev) { return dev->storetime; } gboolean bolt_device_supports_secure_mode (const BoltDevice *dev) { g_autofree char *path = NULL; if (dev->syspath == NULL) return FALSE; path = g_build_filename (dev->syspath, "key", NULL); return g_file_test (path, G_FILE_TEST_IS_REGULAR); } bolt-0.2/boltd/bolt-device.h000066400000000000000000000060631324740507400160000ustar00rootroot00000000000000/* * Copyright © 2017 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 library 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 . * * Authors: * Christian J. Kellner */ #pragma once #include "bolt-auth.h" #include "bolt-enums.h" #include "bolt-exported.h" /* forward declaration */ struct udev_device; G_BEGIN_DECLS #define BOLT_TYPE_DEVICE bolt_device_get_type () G_DECLARE_FINAL_TYPE (BoltDevice, bolt_device, BOLT, DEVICE, BoltExported); BoltDevice * bolt_device_new_for_udev (struct udev_device *udev, GError **error); const char * bolt_device_export (BoltDevice *device, GDBusConnection *connection, GError **error); void bolt_device_unexport (BoltDevice *device); BoltStatus bolt_device_connected (BoltDevice *dev, struct udev_device *udev); BoltStatus bolt_device_disconnected (BoltDevice *dev); gboolean bolt_device_is_connected (const BoltDevice *device); BoltStatus bolt_device_update_from_udev (BoltDevice *dev, struct udev_device *udev); void bolt_device_authorize (BoltDevice *dev, BoltAuth *auth, GAsyncReadyCallback callback, gpointer user_data); BoltKeyState bolt_device_get_keystate (const BoltDevice *dev); const char * bolt_device_get_name (const BoltDevice *dev); const char * bolt_device_get_object_path (BoltDevice *device); BoltPolicy bolt_device_get_policy (const BoltDevice *dev); const char * bolt_device_get_uid (const BoltDevice *dev); BoltSecurity bolt_device_get_security (const BoltDevice *dev); gboolean bolt_device_get_stored (const BoltDevice *dev); BoltStatus bolt_device_get_status (const BoltDevice *dev); const char * bolt_device_get_syspath (const BoltDevice *dev); const char * bolt_device_get_vendor (const BoltDevice *dev); BoltDeviceType bolt_device_get_device_type (const BoltDevice *dev); const char * bolt_device_get_label (const BoltDevice *dev); gint64 bolt_device_get_storetime (const BoltDevice *dev); gboolean bolt_device_supports_secure_mode (const BoltDevice *dev); G_END_DECLS bolt-0.2/boltd/bolt-exported.c000066400000000000000000001101711324740507400163620ustar00rootroot00000000000000/* * Copyright © 2017 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 library 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 . * * Authors: * Christian J. Kellner */ #include "config.h" #include "bolt-enums.h" #include "bolt-error.h" #include "bolt-log.h" #include "bolt-str.h" #include "bolt-exported.h" typedef struct _BoltExportedMethod BoltExportedMethod; typedef struct _BoltExportedProp BoltExportedProp; static GVariant * bolt_exported_get_prop (BoltExported *exported, BoltExportedProp *prop); static void bolt_exported_notify (GObject *object, GParamSpec *pspec); static gboolean handle_authorize_method_default (BoltExported *exported, GDBusMethodInvocation *inv, GError **error); static gboolean handle_authorize_property_default (BoltExported *exported, const char *name, gboolean setting, GDBusMethodInvocation *invocation, GError **error); static void bolt_exported_method_free (gpointer data); static void bolt_exported_prop_free (gpointer data); static GVariant * bolt_exported_prop_gvalue_to_gvariant (BoltExportedProp *prop, const GValue *value); static gboolean bolt_exported_prop_gvariant_to_gvalue (BoltExportedProp *prop, GVariant *variant, GValue *value, GError **error); struct _BoltExportedMethod { char *name; BoltExportedMethodHandler handler; }; struct _BoltExportedProp { GParamSpec *spec; const char *name_obj; /* shortcut for spec->name */ const char *name_bus; GVariantType *signature; /* optional */ BoltExportedSetter setter; /* auto string conversion */ GEnumClass *enum_class; /* shortcut for spec->enum_class */ GFlagsClass *flags_class; /* shortcut for spec->enum_class */ }; struct _BoltExportedClassPrivate { GDBusNodeInfo *node_info; char *iface_name; GDBusInterfaceInfo *iface_info; GHashTable *methods; GHashTable *properties; }; typedef struct _BoltExportedPrivate { GDBusConnection *dbus; char *object_path; /* if exported */ guint registration; /* property changes */ GPtrArray *props_changed; guint props_changed_id; } BoltExportedPrivate; static gpointer bolt_exported_parent_class = NULL; static gint BoltExported_private_offset = 0; static void bolt_exported_init (GTypeInstance *, gpointer g_class); static void bolt_exported_class_init (BoltExportedClass *klass); static void bolt_exported_base_init (gpointer g_class); static void bolt_exported_base_finalize (gpointer g_class); #define GET_PRIV(self) G_STRUCT_MEMBER_P (self, BoltExported_private_offset) GType bolt_exported_get_type (void) { static volatile gsize exported_type = 0; if (g_once_init_enter (&exported_type)) { GType type_id; const GTypeInfo type_info = { sizeof (BoltExportedClass), bolt_exported_base_init, (GBaseFinalizeFunc) bolt_exported_base_finalize, (GClassInitFunc) bolt_exported_class_init, NULL, /* class_finalize */ NULL, /* class_data */ sizeof (BoltExported), 0, /* n_preallocs */ bolt_exported_init, NULL, /* value_table */ }; type_id = g_type_register_static (G_TYPE_OBJECT, "BoltExported", &type_info, G_TYPE_FLAG_ABSTRACT); g_type_add_class_private (type_id, sizeof (BoltExportedClassPrivate)); BoltExported_private_offset = g_type_add_instance_private (type_id, sizeof (BoltExportedPrivate)); g_once_init_leave (&exported_type, type_id); } return exported_type; } enum { PROP_0, PROP_OBJECT_PATH, PROP_EXPORTED, PROP_LAST }; static GParamSpec *props[PROP_LAST] = {NULL, }; enum { SIGNAL_AUTHORIZE_METHOD, SIGNAL_AUTHORIZE_PROPERTY, SIGNAL_LAST }; static guint signals[SIGNAL_LAST] = {0, }; static void bolt_exported_finalize (GObject *object) { BoltExported *exported = BOLT_EXPORTED (object); BoltExportedPrivate *priv = GET_PRIV (exported); g_clear_pointer (&priv->object_path, g_free); g_ptr_array_free (priv->props_changed, TRUE); G_OBJECT_CLASS (bolt_exported_parent_class)->finalize (object); } static void bolt_exported_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { BoltExported *exported = BOLT_EXPORTED (object); BoltExportedPrivate *priv = GET_PRIV (exported); switch (prop_id) { case PROP_OBJECT_PATH: g_value_set_string (value, priv->object_path); break; case PROP_EXPORTED: g_value_set_boolean (value, priv->registration > 0); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); } } static void bolt_exported_init (GTypeInstance *instance, gpointer g_class) { BoltExported *exported = BOLT_EXPORTED (instance); BoltExportedPrivate *priv = GET_PRIV (exported); priv->props_changed = g_ptr_array_new (); } static void bolt_exported_class_init (BoltExportedClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); bolt_exported_parent_class = g_type_class_peek_parent (klass); g_type_class_adjust_private_offset (klass, &BoltExported_private_offset); gobject_class->finalize = bolt_exported_finalize; gobject_class->get_property = bolt_exported_get_property; gobject_class->notify = bolt_exported_notify; klass->authorize_method = handle_authorize_method_default; klass->authorize_property = handle_authorize_property_default; props[PROP_OBJECT_PATH] = g_param_spec_string ("object-path", NULL, NULL, NULL, G_PARAM_READABLE | G_PARAM_STATIC_NICK); props[PROP_EXPORTED] = g_param_spec_boolean ("exported", NULL, NULL, FALSE, G_PARAM_READABLE | G_PARAM_STATIC_NICK); signals[SIGNAL_AUTHORIZE_METHOD] = g_signal_new ("authorize-method", BOLT_TYPE_EXPORTED, G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (BoltExportedClass, authorize_method), g_signal_accumulator_first_wins, NULL, NULL, G_TYPE_BOOLEAN, 2, G_TYPE_DBUS_METHOD_INVOCATION, G_TYPE_POINTER); signals[SIGNAL_AUTHORIZE_PROPERTY] = g_signal_new ("authorize-property", BOLT_TYPE_EXPORTED, G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (BoltExportedClass, authorize_property), g_signal_accumulator_first_wins, NULL, NULL, G_TYPE_BOOLEAN, 4, G_TYPE_STRING, G_TYPE_BOOLEAN, G_TYPE_DBUS_METHOD_INVOCATION, G_TYPE_POINTER); } static void bolt_exported_base_init (gpointer g_class) { BoltExportedClass *klass = g_class; klass->priv = G_TYPE_CLASS_GET_PRIVATE (g_class, BOLT_TYPE_EXPORTED, BoltExportedClassPrivate); memset (klass->priv, 0, sizeof (BoltExportedClassPrivate)); klass->priv->methods = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, bolt_exported_method_free); klass->priv->properties = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, bolt_exported_prop_free); } static void bolt_exported_base_finalize (gpointer g_class) { BoltExportedClass *klass = g_class; BoltExportedClassPrivate *priv = klass->priv; if (priv->node_info) { g_dbus_node_info_unref (priv->node_info); priv->node_info = NULL; } g_hash_table_unref (priv->properties); g_hash_table_unref (priv->methods); g_clear_pointer (&priv->iface_name, g_free); } /* internal utility functions */ static const char * bolt_exported_get_iface_name (BoltExported *exported) { BoltExportedClass *klass; klass = BOLT_EXPORTED_GET_CLASS (exported); return klass->priv->iface_name; } static BoltExportedProp * bolt_exported_lookup_property (BoltExported *exported, const char *name, GError **error) { BoltExportedClass *klass; BoltExportedProp *prop; if (name == NULL) return NULL; klass = BOLT_EXPORTED_GET_CLASS (exported); prop = g_hash_table_lookup (klass->priv->properties, name); if (prop == NULL) g_set_error (error, G_DBUS_ERROR, G_DBUS_ERROR_UNKNOWN_PROPERTY, "no such property: %s", name); return prop; } static BoltExportedMethod * bolt_exported_lookup_method (BoltExported *exported, const char *name, GError **error) { BoltExportedClass *klass; BoltExportedMethod *method; if (name == NULL) return NULL; klass = BOLT_EXPORTED_GET_CLASS (exported); method = g_hash_table_lookup (klass->priv->methods, name); if (method == NULL) g_set_error (error, G_DBUS_ERROR, G_DBUS_ERROR_UNKNOWN_METHOD, "no such method: %s", name); return method; } static GVariant * bolt_exported_get_prop (BoltExported *exported, BoltExportedProp *prop) { g_auto(GValue) res = G_VALUE_INIT; const char *name; const GParamSpec *spec; GVariant *ret; name = prop->name_obj; spec = prop->spec; g_value_init (&res, spec->value_type); g_object_get_property (G_OBJECT (exported), name, &res); ret = bolt_exported_prop_gvalue_to_gvariant (prop, &res); return ret; } /* dispatch helper function */ typedef struct _DispatchData { GDBusMethodInvocation *inv; gboolean is_property; union { BoltExportedMethod *method; BoltExportedProp *prop; }; } DispatchData; static void dispatch_data_free (DispatchData *data) { g_slice_free (DispatchData, data); } G_DEFINE_AUTOPTR_CLEANUP_FUNC (DispatchData, dispatch_data_free); static GVariant * dispach_property_setter (BoltExported *exported, GDBusMethodInvocation *inv, BoltExportedProp *prop, GError **error) { g_autoptr(GError) err = NULL; g_auto(GValue) val = G_VALUE_INIT; g_autoptr(GVariant) vin = NULL; GVariant *params; gboolean ok; params = g_dbus_method_invocation_get_parameters (inv); g_variant_get_child (params, 2, "v", &vin); g_value_init (&val, prop->spec->value_type); ok = bolt_exported_prop_gvariant_to_gvalue (prop, vin, &val, &err); if (ok) ok = prop->setter (exported, prop->name_obj, &val, &err); if (!ok && err != NULL) { g_propagate_error (error, g_steal_pointer (&err)); return NULL; } else if (!ok) { bolt_critical (LOG_TOPIC ("dbus"), "property setter signaled error, but no error is set"); g_set_error (error, BOLT_ERROR, BOLT_ERROR_FAILED, "%s", "could not set property"); return NULL; } g_object_notify_by_pspec (G_OBJECT (exported), prop->spec); return g_variant_new ("()"); } static GVariant * dispatch_method_call (BoltExported *exported, GDBusMethodInvocation *inv, BoltExportedMethod *method, GError **error) { GVariant *params = g_dbus_method_invocation_get_parameters (inv); method->handler (exported, params, inv); return NULL; } static void query_authorization_done (GObject *source_object, GAsyncResult *res, gpointer user_data) { g_autoptr(GError) err = NULL; g_autoptr(DispatchData) data = user_data; GDBusMethodInvocation *inv = data->inv; BoltExported *exported = BOLT_EXPORTED (source_object); GVariant *ret; gboolean ok; ok = g_task_propagate_boolean (G_TASK (res), &err); bolt_debug (LOG_TOPIC ("dbus"), "authorization done: %s", bolt_yesno (ok)); if (!ok && err == NULL) { bolt_bug ("negative auth result, but no GError set"); g_set_error_literal (&err, G_DBUS_ERROR, G_DBUS_ERROR_ACCESS_DENIED, "access denied"); } if (!ok) { g_dbus_method_invocation_return_gerror (inv, err); return; } if (data->is_property) ret = dispach_property_setter (exported, inv, data->prop, &err); else ret = dispatch_method_call (exported, inv, data->method, &err); if (ret == NULL && err != NULL) g_dbus_method_invocation_return_gerror (inv, err); else if (ret != NULL) g_dbus_method_invocation_return_value (inv, ret); /* else: must have been handled by the method call directly */ } static void query_authorization (GTask *task, gpointer source_object, gpointer task_data, GCancellable *cancellable) { GError *error = NULL; BoltExported *exported = source_object; DispatchData *data = task_data; gboolean authorized = FALSE; if (data->is_property) { const char *method_name = g_dbus_method_invocation_get_method_name (data->inv); gboolean is_setter = bolt_streq (method_name, "Set"); g_signal_emit (exported, signals[SIGNAL_AUTHORIZE_PROPERTY], 0, data->prop->name_obj, is_setter, data->inv, &error, &authorized); } else { g_signal_emit (exported, signals[SIGNAL_AUTHORIZE_METHOD], 0, data->inv, &error, &authorized); } bolt_debug (LOG_TOPIC ("dbus"), "query_authorization returned: %s", bolt_yesno (authorized)); if (!authorized) g_task_return_error (task, error); else g_task_return_boolean (task, authorized); } static gboolean handle_authorize_method_default (BoltExported *exported, GDBusMethodInvocation *inv, GError **error) { const char *method_name; method_name = g_dbus_method_invocation_get_method_name (inv); g_set_error (error, G_DBUS_ERROR, G_DBUS_ERROR_ACCESS_DENIED, "bolt operation '%s' denied by default policy", method_name); return FALSE; } static gboolean handle_authorize_property_default (BoltExported *exported, const char *name, gboolean setting, GDBusMethodInvocation *inv, GError **error) { g_set_error (error, G_DBUS_ERROR, G_DBUS_ERROR_ACCESS_DENIED, "setting property '%s' denied by default policy", name); return FALSE; } /* DBus virtual table */ static void handle_dbus_method_call (GDBusConnection *connection, const char *sender, const char *object_path, const char *interface_name, const char *method_name, GVariant *parameters, GDBusMethodInvocation *invocation, gpointer user_data) { g_autoptr(GTask) task = NULL; g_autoptr(GError) err = NULL; BoltExported *exported; gboolean is_property; DispatchData *data; exported = BOLT_EXPORTED (user_data); bolt_debug (LOG_TOPIC ("dbus"), "method call: %s.%s at %s from %s", interface_name, method_name, object_path, sender); /* we also handle property setting here */ is_property = bolt_streq (interface_name, "org.freedesktop.DBus.Properties"); data = g_slice_new0 (DispatchData); data->inv = invocation; data->is_property = is_property; if (is_property) { const GDBusPropertyInfo *pi; BoltExportedProp *prop = NULL; gboolean is_setter; pi = g_dbus_method_invocation_get_property_info (invocation); if (pi != NULL) prop = bolt_exported_lookup_property (exported, pi->name, &err); else g_set_error (&err, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS, "property information missing"); is_setter = bolt_streq (method_name, "Set"); if (is_setter && prop != NULL && prop->setter == NULL) g_set_error (&err, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS, "property: %s has no setter", prop->name_bus); data->prop = prop; } else { BoltExportedMethod *method; method = bolt_exported_lookup_method (exported, method_name, &err); data->method = method; } if (err != NULL) { //bolt_warn_err (err, LOG_TOPIC ("dbus"), "error dispatching call"); g_dbus_method_invocation_return_gerror (invocation, err); return; } task = g_task_new (exported, NULL, query_authorization_done, data); g_task_set_source_tag (task, handle_dbus_method_call); g_task_set_task_data (task, data, NULL); g_task_run_in_thread (task, query_authorization); } static GVariant * handle_dbus_get_property (GDBusConnection *connection, const char *sender, const char *object_path, const char *interface_name, const char *property_name, GError **error, gpointer user_data) { g_autoptr(GError) err = NULL; BoltExported *exported; BoltExportedProp *prop; GVariant *ret; exported = BOLT_EXPORTED (user_data); bolt_debug (LOG_TOPIC ("dbus"), "get property: %s.%s at %s from %s", interface_name, property_name, object_path, sender); prop = bolt_exported_lookup_property (exported, property_name, &err); if (prop == NULL) { bolt_warn_err (err, LOG_TOPIC ("dbus"), "get_property"); g_propagate_error (error, g_steal_pointer (&err)); return NULL; } ret = bolt_exported_get_prop (exported, prop); return ret; } static gboolean emit_prop_changes (gpointer user_data) { g_autoptr(GVariant) changes = NULL; g_autoptr(GError) err = NULL; g_auto(GVariantBuilder) changed; g_auto(GVariantBuilder) invalidated; const char *iface_name; BoltExported *exported; BoltExportedPrivate *priv; GPtrArray *props_changed; gboolean ok; exported = BOLT_EXPORTED (user_data); priv = GET_PRIV (exported); props_changed = priv->props_changed; g_variant_builder_init (&changed, G_VARIANT_TYPE ("a{sv}")); g_variant_builder_init (&invalidated, G_VARIANT_TYPE ("as")); /* no bus, no changed signal */ if (priv->dbus == NULL || priv->object_path == NULL) { g_ptr_array_remove_range (props_changed, 0, props_changed->len); priv->props_changed_id = 0; return FALSE; } /* no changes, no changed signal */ if (props_changed->len == 0) { priv->props_changed_id = 0; return FALSE; } for (guint i = 0; i < props_changed->len; i++) { g_autoptr(GVariant) var = NULL; BoltExportedProp *prop = g_ptr_array_index (props_changed, i); var = bolt_exported_get_prop (exported, prop); g_variant_builder_add (&changed, "{sv}", prop->name_bus, var); } iface_name = bolt_exported_get_iface_name (exported); changes = g_variant_ref_sink (g_variant_new ("(sa{sv}as)", iface_name, &changed, &invalidated)); ok = g_dbus_connection_emit_signal (priv->dbus, NULL, priv->object_path, "org.freedesktop.DBus.Properties", "PropertiesChanged", changes, &err); if (!ok) bolt_warn_err (err, LOG_TOPIC ("dbus"), "error emitting property changes"); g_ptr_array_remove_range (props_changed, 0, props_changed->len); priv->props_changed_id = 0; bolt_debug (LOG_TOPIC ("dbus"), "emitted property changes"); return FALSE; } static void bolt_exported_notify (GObject *object, GParamSpec *pspec) { g_autoptr(GSource) src = NULL; BoltExported *exported; BoltExportedPrivate *priv; BoltExportedProp *prop; const char *nick; exported = BOLT_EXPORTED (object); priv = GET_PRIV (exported); if (priv->dbus == NULL) return; nick = g_param_spec_get_nick (pspec); prop = bolt_exported_lookup_property (exported, nick, NULL); if (prop == NULL) return; g_ptr_array_add (priv->props_changed, prop); if (priv->props_changed_id != 0) return; src = g_idle_source_new (); g_source_set_priority (src, G_PRIORITY_DEFAULT); g_source_set_name (src, "bolt_exported_notify"); g_source_set_callback (src, emit_prop_changes, g_object_ref (exported), (GDestroyNotify) g_object_unref); g_source_attach (src, NULL); } static GDBusInterfaceVTable dbus_vtable = { handle_dbus_method_call, handle_dbus_get_property, NULL, /* set_property (handled by method call) */ }; /* public methods: class */ void bolt_exported_class_set_interface_name (BoltExportedClass *klass, const char *name) { g_return_if_fail (BOLT_IS_EXPORTED_CLASS (klass)); g_return_if_fail (klass->priv != NULL); g_return_if_fail (klass->priv->iface_name == NULL); klass->priv->iface_name = g_strdup (name); } void bolt_exported_class_set_interface_info_from_xml (BoltExportedClass *klass, const char *xml) { g_autoptr(GError) error = NULL; BoltExportedClassPrivate *priv; GDBusInterfaceInfo **iter; GDBusInterfaceInfo *info = NULL; g_return_if_fail (BOLT_IS_EXPORTED_CLASS (klass)); g_return_if_fail (klass->priv != NULL); g_return_if_fail (klass->priv->node_info == NULL); priv = klass->priv; priv->node_info = g_dbus_node_info_new_for_xml (xml, &error); if (priv->node_info == NULL) bolt_error (LOG_TOPIC ("dbus"), LOG_ERR (error), "failed to load xml"); for (iter = klass->priv->node_info->interfaces; iter && *iter; iter++) { GDBusInterfaceInfo *ii = *iter; if (bolt_streq (ii->name, klass->priv->iface_name)) { info = ii; break; } } if (info == NULL) bolt_error (LOG_TOPIC ("dbus"), "interface information is missing"); priv->iface_info = info; } void bolt_exported_class_set_interface_info (BoltExportedClass *klass, const char *iface_name, const char *resource_name) { g_autoptr(GBytes) data = NULL; g_autoptr(GError) err = NULL; const char *xml; bolt_exported_class_set_interface_name (klass, iface_name); data = g_resources_lookup_data (resource_name, G_RESOURCE_LOOKUP_FLAGS_NONE, &err); if (data == NULL) { bolt_error (LOG_TOPIC ("dbus"), LOG_ERR (err), "could not load resource"); return; } xml = g_bytes_get_data (data, NULL); bolt_exported_class_set_interface_info_from_xml (klass, xml); } void bolt_exported_class_export_property (BoltExportedClass *klass, GParamSpec *spec) { BoltExportedClassPrivate *priv; GDBusInterfaceInfo *iface = NULL; GDBusPropertyInfo *info = NULL; GDBusPropertyInfo **iter; BoltExportedProp *prop; const char *name_bus, *name_obj; gboolean is_str_prop; const char *conv = NULL; if (!klass || !BOLT_IS_EXPORTED_CLASS (klass)) { bolt_error (LOG_TOPIC ("dbus"), "klass not a BoltExportedClass"); return; } priv = klass->priv; name_obj = g_param_spec_get_name (spec); name_bus = g_param_spec_get_nick (spec); iface = priv->iface_info; for (iter = iface->properties; iter && *iter; iter++) { GDBusPropertyInfo *pi = *iter; if (bolt_streq (pi->name, name_bus)) { info = pi; break; } } if (info == NULL) { bolt_error (LOG_TOPIC ("dbus"), "no property info for %s", name_bus); return; } prop = g_new0 (BoltExportedProp, 1); prop->spec = g_param_spec_ref (spec); prop->name_bus = name_bus; prop->name_obj = name_obj; prop->signature = g_variant_type_new (info->signature); is_str_prop = g_variant_type_equal (prop->signature, G_VARIANT_TYPE_STRING); if (is_str_prop && G_IS_PARAM_SPEC_ENUM (prop->spec)) { GParamSpecEnum *enum_spec = G_PARAM_SPEC_ENUM (prop->spec); prop->enum_class = enum_spec->enum_class; conv = " [enum-auto-convert]"; } else if (is_str_prop && G_IS_PARAM_SPEC_FLAGS (prop->spec)) { GParamSpecFlags *flags_spec = G_PARAM_SPEC_FLAGS (prop->spec); prop->flags_class = flags_spec->flags_class; conv = " [flags-auto-convert]"; } bolt_debug (LOG_TOPIC ("dbus"), "installed prop: %s -> %s%s", prop->name_bus, prop->name_obj, conv ? : ""); g_hash_table_insert (priv->properties, (gpointer) prop->name_bus, prop); } void bolt_exported_class_export_properties (BoltExportedClass *klass, guint start, guint n_pspecs, GParamSpec **specs) { g_return_if_fail (start > 0); for (guint i = start; i < n_pspecs; i++) bolt_exported_class_export_property (klass, specs[i]); } void bolt_exported_class_property_setter (BoltExportedClass *klass, GParamSpec *spec, BoltExportedSetter setter) { BoltExportedProp *prop; const char *nick; if (!klass || !BOLT_IS_EXPORTED_CLASS (klass)) { bolt_error (LOG_TOPIC ("dbus"), "klass not a BoltExportedClass"); return; } nick = g_param_spec_get_nick (spec); prop = g_hash_table_lookup (klass->priv->properties, nick); if (prop == NULL) { bolt_error (LOG_TOPIC ("dbus"), "unknown property: %s", nick); return; } prop->setter = setter; } void bolt_exported_class_export_method (BoltExportedClass *klass, const char *name, BoltExportedMethodHandler handler) { BoltExportedMethod *method; method = g_new0 (BoltExportedMethod, 1); method->name = g_strdup (name); method->handler = handler; g_hash_table_insert (klass->priv->methods, method->name, method); } /* public methods: instance */ gboolean bolt_exported_export (BoltExported *exported, GDBusConnection *connection, const char *object_path, GError **error) { BoltExportedPrivate *priv; BoltExportedClass *klass; guint id; g_return_val_if_fail (BOLT_IS_EXPORTED (exported), FALSE); g_return_val_if_fail (connection != NULL, FALSE); g_return_val_if_fail (object_path != NULL, FALSE); priv = GET_PRIV (exported); klass = BOLT_EXPORTED_GET_CLASS (exported); if (klass->priv->iface_info == NULL) { g_set_error (error, BOLT_ERROR, BOLT_ERROR_FAILED, "interface information is missing"); return FALSE; } id = g_dbus_connection_register_object (connection, object_path, klass->priv->iface_info, &dbus_vtable, g_object_ref (exported), g_object_unref, error); if (id == 0) return FALSE; bolt_debug (LOG_TOPIC ("dbus"), "registered object at %s", object_path); priv->dbus = g_object_ref (connection); priv->object_path = g_strdup (object_path); priv->registration = id; g_object_notify_by_pspec (G_OBJECT (exported), props[PROP_OBJECT_PATH]); g_object_notify_by_pspec (G_OBJECT (exported), props[PROP_EXPORTED]); return TRUE; } gboolean bolt_exported_unexport (BoltExported *exported) { g_autofree char *opath = NULL; BoltExportedPrivate *priv; gboolean ok; g_return_val_if_fail (BOLT_IS_EXPORTED (exported), FALSE); priv = GET_PRIV (exported); if (priv->dbus == NULL || priv->registration == 0) return FALSE; ok = g_dbus_connection_unregister_object (priv->dbus, priv->registration); if (ok) { g_clear_object (&priv->dbus); priv->registration = 0; opath = g_steal_pointer (&priv->object_path); g_object_notify_by_pspec (G_OBJECT (exported), props[PROP_OBJECT_PATH]); g_object_notify_by_pspec (G_OBJECT (exported), props[PROP_EXPORTED]); } bolt_debug (LOG_TOPIC ("dbus"), "unregistered object at %s: %s", opath, bolt_yesno (ok)); return ok; } GDBusConnection * bolt_exported_get_connection (BoltExported *exported) { BoltExportedPrivate *priv; g_return_val_if_fail (BOLT_IS_EXPORTED (exported), NULL); priv = GET_PRIV (exported); return priv->dbus; } const char * bolt_exported_get_object_path (BoltExported *exported) { BoltExportedPrivate *priv; g_return_val_if_fail (BOLT_IS_EXPORTED (exported), NULL); priv = GET_PRIV (exported); return priv->object_path; } gboolean bolt_exported_emit_signal (BoltExported *exported, const char *name, GVariant *parameters, GError **error) { g_autoptr(GError) err = NULL; BoltExportedPrivate *priv; const char *iface_name; gboolean ok; g_return_val_if_fail (BOLT_IS_EXPORTED (exported), FALSE); priv = GET_PRIV (exported); /* if we are not exported, we just ignore this */ if (priv->dbus == NULL || priv->object_path == NULL) return TRUE; iface_name = bolt_exported_get_iface_name (exported); ok = g_dbus_connection_emit_signal (priv->dbus, NULL, priv->object_path, iface_name, name, parameters, &err); if (!ok) { bolt_warn_err (err, LOG_TOPIC ("dbus"), "error emitting signal"); g_propagate_error (error, g_steal_pointer (&err)); } else { bolt_debug (LOG_TOPIC ("dbus"), "emitted signal: %s", name); } return ok; } void bolt_exported_flush (BoltExported *exported) { BoltExportedPrivate *priv; g_return_if_fail (BOLT_IS_EXPORTED (exported)); priv = GET_PRIV (exported); if (priv->props_changed_id == 0) return; emit_prop_changes (exported); } /* non BoltExported internal methods */ static void bolt_exported_method_free (gpointer data) { BoltExportedMethod *method = data; g_clear_pointer (&method->name, g_free); g_free (method); } static void bolt_exported_prop_free (gpointer data) { BoltExportedProp *prop = data; g_param_spec_unref (prop->spec); g_variant_type_free (prop->signature); g_free (prop); } static GVariant * enum_gvalue_to_gvariant (GEnumClass *enum_class, const GValue *value) { g_autofree char *str = NULL; const char *name; GEnumValue *ev; GVariant *res; gint iv; iv = g_value_get_enum (value); ev = g_enum_get_value (enum_class, iv); if (ev != NULL) { res = g_variant_new_string (ev->value_nick); return g_variant_ref_sink (res); } /* we got an invalid value for that enum */ str = g_strdup_printf ("%d", iv); res = g_variant_new_string (str); name = g_type_name_from_class ((GTypeClass *) enum_class); bolt_bug ("invalid enum value %d for enum '%s'", iv, name); return g_variant_ref_sink (res); } static gboolean enum_gvariant_to_gvalue (GEnumClass *enum_class, GVariant *variant, GValue *value, GError **error) { GEnumValue *ev = NULL; const char *str; str = g_variant_get_string (variant, NULL); if (str == NULL) str = "invalid"; ev = g_enum_get_value_by_nick (enum_class, str); if (ev == NULL) { const char *name; name = g_type_name_from_class ((GTypeClass *) enum_class); g_set_error (error, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS, "invalid enum value '%s' for '%s'", str, name); return FALSE; } g_value_set_enum (value, ev->value); return TRUE; } static GVariant * flags_gvalue_to_gvariant (GFlagsClass *flags_class, const GValue *value) { g_autoptr(GError) err = NULL; g_autofree char *str = NULL; GVariant *res; guint iv; iv = g_value_get_flags (value); str = bolt_flags_class_to_string (flags_class, iv, &err); if (str == NULL) { const char *name; name = g_type_name_from_class ((GTypeClass *) flags_class); str = g_strdup_printf ("%u", iv); bolt_bug ("invalid enum flags '%u' for enum '%s'", iv, name); } res = g_variant_new_string (str); return g_variant_ref_sink (res); } static gboolean flags_gvariant_to_gvalue (GFlagsClass *flags_class, GVariant *variant, GValue *value, GError **error) { gboolean ok; const char *str; guint flags; str = g_variant_get_string (variant, NULL); if (str == NULL) { const char *name; name = g_type_name_from_class ((GTypeClass *) flags_class); g_set_error (error, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS, "invalid flags value (null) for '%s'", name); } ok = bolt_flags_class_from_string (flags_class, str, &flags, error); if (ok) g_value_set_flags (value, flags); return ok; } static GVariant * bolt_exported_prop_gvalue_to_gvariant (BoltExportedProp *prop, const GValue *value) { if (prop->enum_class != NULL) return enum_gvalue_to_gvariant (prop->enum_class, value); else if (prop->flags_class != NULL) return flags_gvalue_to_gvariant (prop->flags_class, value); else return g_dbus_gvalue_to_gvariant (value, prop->signature); } static gboolean bolt_exported_prop_gvariant_to_gvalue (BoltExportedProp *prop, GVariant *variant, GValue *value, GError **error) { if (prop->enum_class != NULL) return enum_gvariant_to_gvalue (prop->enum_class, variant, value, error); else if (prop->flags_class != NULL) return flags_gvariant_to_gvalue (prop->flags_class, variant, value, error); g_dbus_gvariant_to_gvalue (variant, value); return TRUE; } bolt-0.2/boltd/bolt-exported.h000066400000000000000000000107561324740507400163770ustar00rootroot00000000000000/* * Copyright © 2018 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 library 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 . * * Authors: * Christian J. Kellner */ #pragma once #include G_BEGIN_DECLS #define BOLT_TYPE_EXPORTED bolt_exported_get_type () G_DECLARE_DERIVABLE_TYPE (BoltExported, bolt_exported, BOLT, EXPORTED, GObject); typedef struct _BoltExportedClassPrivate BoltExportedClassPrivate; struct _BoltExportedClass { GObjectClass parent_class; /*< private >*/ BoltExportedClassPrivate *priv; /*< public >*/ /* Signals */ gboolean (*authorize_method) (BoltExported *exported, GDBusMethodInvocation *invocation, GError **error); gboolean (*authorize_property) (BoltExported *exported, const char *name, gboolean setting, GDBusMethodInvocation *invocation, GError **error); /* for the future */ gpointer padding[10]; }; typedef gboolean (* BoltExportedMethodHandler) (BoltExported *obj, GVariant *params, GDBusMethodInvocation *inv); typedef gboolean (* BoltExportedSetter) (BoltExported *obj, const char *name, const GValue *value, GError **error); /* class methods */ void bolt_exported_class_set_interface_name (BoltExportedClass *klass, const char *name); void bolt_exported_class_set_interface_info_from_xml (BoltExportedClass *klass, const char *xml); void bolt_exported_class_set_interface_info (BoltExportedClass *klass, const char *iface_name, const char *resource_name); void bolt_exported_class_export_property (BoltExportedClass *klass, GParamSpec *spec); void bolt_exported_class_export_properties (BoltExportedClass *klass, guint start, guint n_pspecs, GParamSpec **specs); void bolt_exported_class_property_setter (BoltExportedClass *klass, GParamSpec *spec, BoltExportedSetter setter); void bolt_exported_class_export_method (BoltExportedClass *klass, const char *name, BoltExportedMethodHandler handler); /* instance methods */ gboolean bolt_exported_export (BoltExported *exported, GDBusConnection *connection, const char *object_path, GError **error); gboolean bolt_exported_unexport (BoltExported *exported); GDBusConnection * bolt_exported_get_connection (BoltExported *exported); const char * bolt_exported_get_object_path (BoltExported *exported); gboolean bolt_exported_emit_signal (BoltExported *exported, const char *name, GVariant *parameters, GError **error); void bolt_exported_flush (BoltExported *exported); G_END_DECLS bolt-0.2/boltd/bolt-key.c000066400000000000000000000134011324740507400153160ustar00rootroot00000000000000/* * Copyright © 2017 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 library 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 . * * Authors: * Christian J. Kellner */ #include "config.h" #include "bolt-store.h" #include "bolt-error.h" #include "bolt-fs.h" #include "bolt-io.h" #include "bolt-key.h" #include "bolt-rnd.h" #include "bolt-str.h" #include #include /* ************************************ */ /* BoltKey */ struct _BoltKey { GObject object; /* the actual key plus the null char */ char data[BOLT_KEY_CHARS + 1]; gboolean fresh; }; enum { PROP_KEY_0, PROP_KEY_FRESH, PROP_KEY_LAST }; static GParamSpec *key_props[PROP_KEY_LAST] = { NULL, }; G_DEFINE_TYPE (BoltKey, bolt_key, G_TYPE_OBJECT); static void bolt_key_finalize (GObject *object) { BoltKey *key = BOLT_KEY (object); bolt_erase_n (key->data, sizeof (key->data)); G_OBJECT_CLASS (bolt_key_parent_class)->finalize (object); } static void bolt_key_init (BoltKey *key) { memset (key->data, 0, sizeof (key->data)); } static void bolt_key_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { BoltKey *key = BOLT_KEY (object); switch (prop_id) { case PROP_KEY_FRESH: g_value_set_boolean (value, key->fresh); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); } } static void bolt_key_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { BoltKey *key = BOLT_KEY (object); switch (prop_id) { case PROP_KEY_FRESH: key->fresh = g_value_get_boolean (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); } } static void bolt_key_class_init (BoltKeyClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); gobject_class->finalize = bolt_key_finalize; gobject_class->get_property = bolt_key_get_property; gobject_class->set_property = bolt_key_set_property; key_props[PROP_KEY_FRESH] = g_param_spec_boolean ("fresh", NULL, NULL, FALSE, G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_NICK); g_object_class_install_properties (gobject_class, PROP_KEY_LAST, key_props); } /* internal methods */ /* public methods */ BoltKey * bolt_key_new (void) { BoltKey *key; char data[BOLT_KEY_BYTES]; key = g_object_new (BOLT_TYPE_KEY, NULL); bolt_get_random_data (data, BOLT_KEY_BYTES); for (guint i = 0; i < BOLT_KEY_BYTES; i++) { char *pos = key->data + 2 * i; gulong n = sizeof (key->data) - 2 * i; g_snprintf (pos, n, "%02hhx", data[i]); } bolt_erase_n (data, sizeof (data)); key->fresh = TRUE; return key; } gboolean bolt_key_write_to (BoltKey *key, int fd, BoltSecurity *level, GError **error) { g_autoptr(GError) err = NULL; gboolean ok; g_return_val_if_fail (BOLT_IS_KEY (key), FALSE); g_return_val_if_fail (fd > -1, FALSE); g_return_val_if_fail (level != NULL, FALSE); *level = BOLT_SECURITY_USER; if (key->data[0] == '\0') return TRUE; ok = bolt_write_all (fd, key->data, BOLT_KEY_CHARS, &err); if (!ok && g_error_matches (err, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT)) g_set_error_literal (error, BOLT_ERROR, BOLT_ERROR_BADKEY, "invalid key data"); else if (!ok) g_propagate_error (error, g_steal_pointer (&err)); else if (!key->fresh) /* ok == True */ *level = BOLT_SECURITY_SECURE; return ok; } gboolean bolt_key_save_file (BoltKey *key, GFile *file, GError **error) { gboolean ok; ok = g_file_replace_contents (file, key->data, BOLT_KEY_CHARS, NULL, FALSE, G_FILE_CREATE_PRIVATE, NULL, NULL, error); return ok; } BoltKey * bolt_key_load_file (GFile *file, GError **error) { g_autoptr(BoltKey) key = NULL; g_autofree char *path = NULL; gboolean ok; gsize len; int fd; key = g_object_new (BOLT_TYPE_KEY, NULL); path = g_file_get_path (file); fd = bolt_open (path, O_CLOEXEC | O_RDONLY, 0, error); if (fd < 0) return NULL; memset (key->data, 0, sizeof (key->data)); ok = bolt_read_all (fd, key->data, BOLT_KEY_CHARS, &len, error); close (fd); if (len != BOLT_KEY_CHARS) { g_set_error (error, BOLT_ERROR, BOLT_ERROR_BADKEY, "unexpected key size (corrupt key?): %zu", len); return NULL; } if (!ok) return NULL; key->fresh = FALSE; return g_steal_pointer (&key); } BoltKeyState bolt_key_get_state (BoltKey *key) { if (key == NULL) return BOLT_KEY_MISSING; return key->fresh ? BOLT_KEY_NEW : BOLT_KEY_HAVE; } bolt-0.2/boltd/bolt-key.h000066400000000000000000000032201324740507400153210ustar00rootroot00000000000000/* * Copyright © 2017 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 library 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 . * * Authors: * Christian J. Kellner */ #pragma once #include #include "bolt-enums.h" G_BEGIN_DECLS /* BoltKey - represents a key to authorize devices with */ #define BOLT_TYPE_KEY bolt_key_get_type () G_DECLARE_FINAL_TYPE (BoltKey, bolt_key, BOLT, KEY, GObject); #define BOLT_KEY_BYTES 32 #define BOLT_KEY_CHARS 64 BoltKey * bolt_key_new (void); gboolean bolt_key_write_to (BoltKey *key, int fd, BoltSecurity *level, GError **error); gboolean bolt_key_save_file (BoltKey *key, GFile *file, GError **error); BoltKey * bolt_key_load_file (GFile *file, GError **error); BoltKeyState bolt_key_get_state (BoltKey *key); G_END_DECLS bolt-0.2/boltd/bolt-log.c000066400000000000000000000336201324740507400153140ustar00rootroot00000000000000/* * Copyright © 2018 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 library 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 . * * Authors: * Christian J. Kellner */ #include "config.h" #include "bolt-device.h" #include "bolt-error.h" #include "bolt-str.h" #include "bolt-term.h" #include "bolt-log.h" #include #include #include #include #include /* stolen from glib */ static const char * log_level_to_priority (GLogLevelFlags log_level) { if (log_level & G_LOG_LEVEL_ERROR) return "3"; else if (log_level & G_LOG_LEVEL_CRITICAL) return "4"; else if (log_level & G_LOG_LEVEL_WARNING) return "4"; else if (log_level & G_LOG_LEVEL_MESSAGE) return "5"; else if (log_level & G_LOG_LEVEL_INFO) return "6"; else if (log_level & G_LOG_LEVEL_DEBUG) return "7"; /* Default to LOG_NOTICE for custom log levels. */ return "5"; } static const char * log_level_to_string (GLogLevelFlags log_level) { if (log_level & G_LOG_LEVEL_ERROR) return "error"; else if (log_level & G_LOG_LEVEL_CRITICAL) return "critical"; else if (log_level & G_LOG_LEVEL_WARNING) return "warning"; else if (log_level & G_LOG_LEVEL_MESSAGE) return "message"; else if (log_level & G_LOG_LEVEL_INFO) return "info"; else if (log_level & G_LOG_LEVEL_DEBUG) return "debug"; return "user"; } static FILE * log_level_to_file (GLogLevelFlags log_level) { return log_level & G_LOG_LEVEL_DEBUG ? stdout : stderr; } #define internal_error(fmt, ...) g_fprintf (stderr, "log-ERROR: " fmt "\n", __VA_ARGS__) static const char * bolt_color_for (FILE *out, const char *color) { int fd = fileno (out); return g_log_writer_supports_color (fd) ? color : ""; } struct _BoltLogCtx { BoltDevice *device; const GError *error; /* standard fields */ GLogField *self; GLogField *message; GLogField *priority; GLogField *domain; GLogField *topic; /* field storage */ gsize n_fields; GLogField fields[32]; /* heap or stack */ gboolean allocated; }; static gboolean bolt_log_ctx_next_field (BoltLogCtx *ctx, GLogField **next) { const gsize maxfields = G_N_ELEMENTS (ctx->fields); gboolean res = TRUE; gsize n; /* safe to use as index */ /* make sure we are ALWAYS within bounds */ n = MIN (ctx->n_fields, maxfields - 1); *next = ctx->fields + n; res = ctx->n_fields < maxfields; ctx->n_fields += 1; if (!res) internal_error ("fields overflow. '%s' dropped", ctx->fields[n].key); return res; } #define BOLT_LOG_CTX_KEY "BOLT_LOG_CONTEXT" static gsize bolt_log_ctx_finish (BoltLogCtx *ctx) { GLogField *self; gboolean ok; ok = bolt_log_ctx_next_field (ctx, &self); if (!ok) { const gsize maxfields = sizeof (ctx->fields); gsize i = ctx->n_fields - maxfields; ctx->n_fields = maxfields; internal_error ("overflow of %" G_GSIZE_FORMAT, i); } self->key = BOLT_LOG_CTX_KEY; self->value = ctx; self->length = 0; ctx->self = self; return ctx->n_fields; } static gboolean bolt_log_ctx_find_field (const BoltLogCtx *ctx, const char *name, const GLogField **out) { g_return_val_if_fail (ctx != NULL, FALSE); g_return_val_if_fail (name != NULL, FALSE); g_return_val_if_fail (out != NULL, FALSE); for (gsize i = 0; i < ctx->n_fields; i++) { const GLogField *field = ctx->fields + i; if (bolt_streq (field->key, name)) { *out = field; return TRUE; } } return FALSE; } static void handle_device_field (BoltLogCtx *ctx, const char *key, gpointer ptr) { BoltDevice *dev = BOLT_DEVICE (ptr); BoltStatus status; GLogField *field; g_return_if_fail (BOLT_IS_DEVICE (dev)); ctx->device = dev; bolt_log_ctx_next_field (ctx, &field); field->key = BOLT_LOG_DEVICE_UID; field->value = bolt_device_get_uid (dev); field->length = -1; bolt_log_ctx_next_field (ctx, &field); field->key = BOLT_LOG_DEVICE_NAME; field->value = bolt_device_get_name (dev); field->length = -1; bolt_log_ctx_next_field (ctx, &field); status = bolt_device_get_status (dev); field->key = BOLT_LOG_DEVICE_STATE; field->value = bolt_status_to_string (status); field->length = -1; } static void handle_gerror_field (BoltLogCtx *ctx, const char *key, gpointer ptr) { const GError *error = ptr; GLogField *field; ctx->error = error; bolt_log_ctx_next_field (ctx, &field); field->key = BOLT_LOG_ERROR_DOMAIN; field->value = g_quark_to_string (error->domain); field->length = -1; bolt_log_ctx_next_field (ctx, &field); field->key = BOLT_LOG_ERROR_CODE; field->value = &error->code; field->length = sizeof (gint); bolt_log_ctx_next_field (ctx, &field); field->key = BOLT_LOG_ERROR_MESSAGE; field->value = error->message; field->length = -1; } static void handle_topic_field (BoltLogCtx *ctx, const char *key, gpointer ptr) { const char *value = ptr; GLogField *field; bolt_log_ctx_next_field (ctx, &field); field->key = BOLT_LOG_TOPIC; field->value = value; field->length = -1; ctx->topic = field; } static gboolean handle_special_field (BoltLogCtx *ctx, const char *key, gpointer ptr) { gboolean handled = TRUE; key++; /* remove the special key indicator */ if (g_str_has_prefix (key, "device")) handle_device_field (ctx, key, ptr); else if (g_str_equal (key, "error")) handle_gerror_field (ctx, key, ptr); else if (g_str_equal (key, "topic")) handle_topic_field (ctx, key, ptr); else handled = FALSE; return handled; } static gboolean handle_passthrough_field (BoltLogCtx *ctx, const char *key, const char *val) { GLogField *field; key++; /* remove the pass-through key indicator */ bolt_log_ctx_next_field (ctx, &field); field->key = key; field->value = val; field->length = -1; return TRUE; } void bolt_log (const char *domain, GLogLevelFlags level, ...) { va_list args; va_start (args, level); bolt_logv (domain, level, args); va_end (args); } #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wformat-nonliteral" void bolt_logv (const char *domain, GLogLevelFlags level, va_list args) { BoltLogCtx ctx = {NULL, }; char message[1024] = {0, }; const char *key; bolt_log_ctx_next_field (&ctx, &ctx.message); bolt_log_ctx_next_field (&ctx, &ctx.priority); bolt_log_ctx_next_field (&ctx, &ctx.domain); while ((key = va_arg (args, const char *)) != NULL) { gboolean handled; if (*key == LOG_SPECIAL_CHAR) { gpointer ptr = va_arg (args, gpointer); handled = handle_special_field (&ctx, key, ptr); } else if (*key == LOG_PASSTHROUGH_CHAR) { const char *val = va_arg (args, const char *); handled = handle_passthrough_field (&ctx, key, val); } else { break; } if (!handled) internal_error ("unknown field: %s", key); } g_vsnprintf (message, sizeof (message), key ? : "", args); ctx.message->key = "MESSAGE"; ctx.message->value = message; ctx.message->length = -1; ctx.priority->key = "PRIORITY"; ctx.priority->value = log_level_to_priority (level); ctx.priority->length = -1; ctx.domain->key = "GLIB_DOMAIN"; ctx.domain->value = domain ? : "boltd"; ctx.domain->length = -1; bolt_log_ctx_finish (&ctx); /* pass it to the normal log mechanisms; * this should handle aborting on fatal * error messages for us. */ g_log_structured_array (level, ctx.fields, ctx.n_fields); } #pragma GCC diagnostic pop static char * format_device_id (BoltDevice *device, char *buffer, gsize len, int size) { static const int u = 13; const char *uid; const char *name; int n; uid = bolt_device_get_uid (device); name = bolt_device_get_name (device); n = MAX (0, size - u); g_snprintf (buffer, len, "%-.*s-%-*.*s", u, uid, n, n, name); return buffer; } #define TIME_MAXFMT 255 GLogWriterOutput bolt_log_stdstream (const BoltLogCtx *ctx, GLogLevelFlags log_level, guint flags) { FILE *out = log_level_to_file (log_level); const char *normal = bolt_color_for (out, ANSI_NORMAL); const char *gray = bolt_color_for (out, ANSI_HIGHLIGHT_BLACK); const char *blue = bolt_color_for (out, ANSI_BLUE); const char *fg = normal; const char *message; const GLogField *f; char the_time[TIME_MAXFMT]; time_t now; struct tm *tm; g_return_val_if_fail (ctx != NULL, G_LOG_WRITER_UNHANDLED); g_return_val_if_fail (ctx->message != NULL, G_LOG_WRITER_UNHANDLED); time (&now); tm = localtime (&now); if (tm && strftime (the_time, sizeof (the_time), "%T", tm) > 0) g_fprintf (out, "%s%s%s ", gray, the_time, normal); if (log_level == G_LOG_LEVEL_CRITICAL || log_level == G_LOG_LEVEL_ERROR) fg = bolt_color_for (out, ANSI_RED); else if (log_level == G_LOG_LEVEL_WARNING) fg = bolt_color_for (out, ANSI_YELLOW); else if (log_level == G_LOG_LEVEL_DEBUG) fg = gray; if (ctx->device) { char name[64]; format_device_id (ctx->device, name, sizeof (name), 30); g_fprintf (out, "[%s%s%s] ", blue, name, normal); } else if (bolt_log_ctx_find_field (ctx, BOLT_LOG_DEVICE_UID, &f)) { const char *uid = f->value; g_fprintf (out, "[%s%.13s %17s%s] ", blue, uid, " ", fg); } if (ctx->topic) g_fprintf (out, "%s%s%s: ", blue, (const char *) ctx->topic->value, fg); message = ctx->message->value; g_fprintf (out, "%s%s%s", fg, message, normal); if (ctx->error) { const char *yellow = bolt_color_for (out, ANSI_YELLOW); const char *msg = ctx->error->message; if (strlen (message) == 0) { const char *lvl = log_level_to_string (log_level); g_fprintf (out, "%s%s%s", fg, lvl, normal); } g_fprintf (out, ": %s%s%s", yellow, msg, normal); } g_fprintf (out, "\n"); fflush (out); return G_LOG_WRITER_HANDLED; } static int bolt_cat_printf (char **buffer, gsize *size, const char *fmt, ...) { va_list args; char *p = *buffer; gsize s = *size; int n; va_start (args, fmt); n = g_vsnprintf (p, s, fmt, args); va_end (args); /* TODO: check n */ *size = s - n; *buffer = p + n; return n; } GLogWriterOutput bolt_log_journal (const BoltLogCtx *ctx, GLogLevelFlags log_level, guint flags) { GLogWriterOutput res; const char *m; const char *old = NULL; char message[2048]; gsize size = sizeof (message); char *p = message; const GLogField *f; if (ctx == NULL || ctx->message == NULL) return G_LOG_WRITER_UNHANDLED; if (ctx->device) { char name[64]; format_device_id (ctx->device, name, sizeof (name), 40); bolt_cat_printf (&p, &size, "[%s] ", name); } else if (bolt_log_ctx_find_field (ctx, BOLT_LOG_DEVICE_UID, &f)) { const char *uid = f->value; bolt_cat_printf (&p, &size, "[%.13s %27s] ", uid, " "); } if (ctx->topic) bolt_cat_printf (&p, &size, "%s: ", (const char *) ctx->topic->value); m = ctx->message->value; bolt_cat_printf (&p, &size, "%s", m); if (ctx->error) { const GError *error = ctx->error; const char *msg = error->message; if (strlen (m) == 0) bolt_cat_printf (&p, &size, "%s", log_level_to_string (log_level)); bolt_cat_printf (&p, &size, ": %s", msg); } old = ctx->message->value; ctx->message->value = message; res = g_log_writer_journald (log_level, ctx->fields, ctx->n_fields, NULL); ctx->message->value = old; return res; } BoltLogCtx * bolt_log_ctx_acquire (const GLogField *fields, gsize n) { BoltLogCtx *ctx; if (bolt_streq (fields[n - 1].key, BOLT_LOG_CTX_KEY)) { ctx = (BoltLogCtx *) fields[n - 1].value; /* sanity check */ if (ctx->message == NULL) return NULL; return ctx; } ctx = g_new0 (BoltLogCtx, 1); ctx->allocated = TRUE; bolt_log_ctx_next_field (ctx, &ctx->message); bolt_log_ctx_next_field (ctx, &ctx->priority); bolt_log_ctx_next_field (ctx, &ctx->domain); for (gsize i = 0; i < n; i++) { GLogField *field = (GLogField *) &fields[i]; if (bolt_streq (field->key, "MESSAGE")) ctx->message = field; else if (bolt_streq (field->key, "GLIB_DOMAIN")) ctx->domain = field; else if (bolt_streq (field->key, "PRIORITY")) ctx->priority = field; } if (ctx->message == NULL) { g_free (ctx); return NULL; } bolt_log_ctx_finish (ctx); return ctx; } gboolean bolt_log_ctx_set_id (BoltLogCtx *ctx, const char *id) { if (ctx->self == NULL) return FALSE; else if (ctx->self->length != 0) return FALSE; ctx->self->value = id; ctx->self->length = -1; return TRUE; } void bolt_log_ctx_free (BoltLogCtx *ctx) { if (ctx == NULL || ctx->allocated == FALSE) return; g_free (ctx); } const char * blot_log_ctx_get_domain (BoltLogCtx *ctx) { return ctx->domain ? ctx->domain->value : NULL; } bolt-0.2/boltd/bolt-log.h000066400000000000000000000146021324740507400153200ustar00rootroot00000000000000/* * Copyright © 2018 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 library 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 . * * Authors: * Christian J. Kellner */ #pragma once #include "bolt-names.h" #include #include G_BEGIN_DECLS #define LOG_SPECIAL_CHAR '@' #define LOG_PASSTHROUGH_CHAR '_' #define LOG_DIRECT(k, v) "_" k, v #define LOG_DEV(device) "@device", device #define LOG_ERR(error) "@error", error #define LOG_TOPIC(topic) "@topic", topic #define LOG_DEV_UID(uid) LOG_DIRECT (BOLT_LOG_DEVICE_UID, uid) #define LOG_MSG_ID(msg_id) LOG_DIRECT ("MESSAGE_ID", msg_id) #define LOG_ID(id) LOG_MSG_ID (BOLT_LOG_MSG_ID_ ## id) #define bolt_debug(...) bolt_log (G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, \ LOG_DIRECT ("CODE_FILE", __FILE__), \ LOG_DIRECT ("CODE_LINE", G_STRINGIFY (__LINE__)), \ LOG_DIRECT ("CODE_FUNC", G_STRFUNC), \ __VA_ARGS__) #define bolt_info(...) bolt_log (G_LOG_DOMAIN, G_LOG_LEVEL_INFO, \ LOG_DIRECT ("CODE_FILE", __FILE__), \ LOG_DIRECT ("CODE_LINE", G_STRINGIFY (__LINE__)), \ LOG_DIRECT ("CODE_FUNC", G_STRFUNC), \ __VA_ARGS__) #define bolt_msg(...) bolt_log (G_LOG_DOMAIN, G_LOG_LEVEL_MESSAGE, \ LOG_DIRECT ("CODE_FILE", __FILE__), \ LOG_DIRECT ("CODE_LINE", G_STRINGIFY (__LINE__)), \ LOG_DIRECT ("CODE_FUNC", G_STRFUNC), \ __VA_ARGS__) #define bolt_warn(...) bolt_log (G_LOG_DOMAIN, G_LOG_LEVEL_WARNING, \ LOG_DIRECT ("CODE_FILE", __FILE__), \ LOG_DIRECT ("CODE_LINE", G_STRINGIFY (__LINE__)), \ LOG_DIRECT ("CODE_FUNC", G_STRFUNC), \ __VA_ARGS__) #define bolt_critical(...) bolt_log (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL, \ LOG_DIRECT ("CODE_FILE", __FILE__), \ LOG_DIRECT ("CODE_LINE", G_STRINGIFY (__LINE__)), \ LOG_DIRECT ("CODE_FUNC", G_STRFUNC), \ __VA_ARGS__) #define bolt_error(...) G_STMT_START { \ bolt_log (G_LOG_DOMAIN, G_LOG_LEVEL_ERROR, \ LOG_DIRECT ("CODE_FILE", __FILE__), \ LOG_DIRECT ("CODE_LINE", G_STRINGIFY (__LINE__)), \ LOG_DIRECT ("CODE_FUNC", G_STRFUNC), \ __VA_ARGS__); \ for (;; ) { } \ } G_STMT_END #define bolt_warn_err(e, ...) bolt_log (G_LOG_DOMAIN, G_LOG_LEVEL_WARNING, \ LOG_DIRECT ("CODE_FILE", __FILE__), \ LOG_DIRECT ("CODE_LINE", G_STRINGIFY (__LINE__)), \ LOG_DIRECT ("CODE_FUNC", G_STRFUNC), \ LOG_ERR (e), \ __VA_ARGS__) #define bolt_warn_enum_unhandled(the_enum, the_value) G_STMT_START { \ bolt_log (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL, \ LOG_DIRECT ("CODE_FILE", __FILE__), \ LOG_DIRECT ("CODE_LINE", G_STRINGIFY (__LINE__)), \ LOG_DIRECT ("CODE_FUNC", G_STRFUNC), \ LOG_TOPIC ("code"), \ "unhandled value of enum " #the_enum "%d", \ (int) the_value); \ } G_STMT_END #define bolt_bug(...) bolt_log (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL, \ LOG_DIRECT ("CODE_FILE", __FILE__), \ LOG_DIRECT ("CODE_LINE", G_STRINGIFY (__LINE__)), \ LOG_DIRECT ("CODE_FUNC", G_STRFUNC), \ LOG_TOPIC ("code"), \ __VA_ARGS__) void bolt_logv (const char *domain, GLogLevelFlags level, va_list args); void bolt_log (const char *domain, GLogLevelFlags level, ...); /* consumer functions */ typedef struct _BoltLogCtx BoltLogCtx; BoltLogCtx * bolt_log_ctx_acquire (const GLogField *fields, gsize n); gboolean bolt_log_ctx_set_id (BoltLogCtx *ctx, const char *id); void bolt_log_ctx_free (BoltLogCtx *ctx); const char * blot_log_ctx_get_domain (BoltLogCtx *ctx); GLogWriterOutput bolt_log_stdstream (const BoltLogCtx *ctx, GLogLevelFlags log_level, guint flags); GLogWriterOutput bolt_log_journal (const BoltLogCtx *ctx, GLogLevelFlags log_level, guint flags); G_DEFINE_AUTOPTR_CLEANUP_FUNC (BoltLogCtx, bolt_log_ctx_free); G_END_DECLS bolt-0.2/boltd/bolt-manager.c000066400000000000000000001356141324740507400161530ustar00rootroot00000000000000/* * Copyright © 2017 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 library 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 . * * Authors: * Christian J. Kellner */ #include "config.h" #include "bolt-bouncer.h" #include "bolt-config.h" #include "bolt-device.h" #include "bolt-error.h" #include "bolt-log.h" #include "bolt-power.h" #include "bolt-store.h" #include "bolt-str.h" #include "bolt-sysfs.h" #include "bolt-time.h" #include "bolt-manager.h" #include #include #define MSEC_PER_USEC 1000LL #define PROBING_SETTLE_TIME_MS 2000 /* in milli-seconds */ typedef struct udev_monitor udev_monitor; G_DEFINE_AUTOPTR_CLEANUP_FUNC (udev_monitor, udev_monitor_unref); typedef struct udev_device udev_device; G_DEFINE_AUTOPTR_CLEANUP_FUNC (udev_device, udev_device_unref); static void bolt_manager_initable_iface_init (GInitableIface *iface); static gboolean bolt_manager_initialize (GInitable *initable, GCancellable *cancellable, GError **error); /* */ static void manager_register_device (BoltManager *mgr, BoltDevice *device); static void manager_deregister_device (BoltManager *mgr, BoltDevice *device); static BoltDevice * manager_find_device_by_syspath (BoltManager *mgr, const char *sysfs); static BoltDevice * manager_find_device_by_uid (BoltManager *mgr, const char *uid, GError **error); static BoltDevice * bolt_manager_get_parent (BoltManager *mgr, BoltDevice *dev); static GPtrArray * bolt_manager_get_children (BoltManager *mgr, BoltDevice *target); static void handle_udev_device_added (BoltManager *mgr, struct udev_device *udev); static void handle_udev_device_changed (BoltManager *mgr, BoltDevice *dev, struct udev_device *udev); static void hanlde_udev_device_removed (BoltManager *mgr, BoltDevice *dev); static void handle_udev_device_attached (BoltManager *mgr, BoltDevice *dev, struct udev_device *udev); static void handle_udev_device_detached (BoltManager *mgr, BoltDevice *dev); static void handle_store_device_removed (BoltStore *store, const char *uid, BoltManager *mgr); /* acquiring indicator */ static void handle_device_status_changed (BoltDevice *dev, BoltStatus old, BoltManager *mgr); static void manager_probing_device_added (BoltManager *mgr, struct udev_device *dev); static void manager_probing_device_removed (BoltManager *mgr, struct udev_device *dev); static void manager_probing_activity (BoltManager *mgr, gboolean weak); /* domain related functions */ static void manager_add_domain (BoltManager *mgr, struct udev_device *domain); static int manager_count_domains (BoltManager *mgr); static gboolean manager_maybe_power_controller (BoltManager *mgr); /* config */ static void manager_load_user_config (BoltManager *mgr); /* dbus property setter */ static gboolean handle_set_authmode (BoltExported *obj, const char *name, const GValue *value, GError **error); /* dbus method calls */ static gboolean handle_list_devices (BoltExported *object, GVariant *params, GDBusMethodInvocation *invocation); static gboolean handle_device_by_uid (BoltExported *object, GVariant *params, GDBusMethodInvocation *invocation); static gboolean handle_enroll_device (BoltExported *object, GVariant *params, GDBusMethodInvocation *invocation); static gboolean handle_forget_device (BoltExported *object, GVariant *params, GDBusMethodInvocation *invocation); /* */ struct _BoltManager { BoltExported object; /* udev */ struct udev *udev; struct udev_monitor *udev_monitor; GSource *udev_source; /* state */ BoltStore *store; GPtrArray *devices; BoltPower *power; BoltSecurity security; BoltAuthMode authmode; /* policy enforcer */ BoltBouncer *bouncer; /* config */ GKeyFile *config; BoltPolicy policy; /* default enrollment policy, unless specified */ /* probing indicator */ guint authorizing; /* number of devices currently authorizing */ GPtrArray *probing_roots; /* pci device tree root */ guint probing_timeout; /* signal id & indicator */ gint64 probing_tstamp; /* time stamp of last activity */ guint probing_tsettle; /* how long to indicate after the last activity */ }; enum { PROP_0, PROP_VERSION, PROP_PROBING, PROP_POLICY, PROP_SECURITY, PROP_AUTHMODE, PROP_LAST, PROP_EXPORTED = PROP_VERSION }; static GParamSpec *props[PROP_LAST] = {NULL, }; G_DEFINE_TYPE_WITH_CODE (BoltManager, bolt_manager, BOLT_TYPE_EXPORTED, G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, bolt_manager_initable_iface_init)); static void bolt_manager_finalize (GObject *object) { BoltManager *mgr = BOLT_MANAGER (object); if (mgr->udev_monitor) { udev_monitor_unref (mgr->udev_monitor); mgr->udev_monitor = NULL; g_source_destroy (mgr->udev_source); g_source_unref (mgr->udev_source); mgr->udev_source = NULL; } if (mgr->udev) { udev_unref (mgr->udev); mgr->udev = NULL; } if (mgr->probing_timeout) { g_source_remove (mgr->probing_timeout); mgr->probing_timeout = 0; } g_clear_object (&mgr->store); g_ptr_array_free (mgr->devices, TRUE); g_clear_object (&mgr->power); G_OBJECT_CLASS (bolt_manager_parent_class)->finalize (object); } static void bolt_manager_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { BoltManager *mgr = BOLT_MANAGER (object); switch (prop_id) { case PROP_VERSION: #if VERSION_MAJOR < 1 g_value_set_uint (value, 0); #else g_value_set_uint (value, VERSION_MINOR); #endif break; case PROP_PROBING: g_value_set_boolean (value, mgr->probing_timeout > 0); break; case PROP_POLICY: g_value_set_enum (value, mgr->policy); break; case PROP_SECURITY: g_value_set_enum (value, mgr->security); break; case PROP_AUTHMODE: g_value_set_flags (value, mgr->authmode); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); } } static void bolt_manager_init (BoltManager *mgr) { mgr->devices = g_ptr_array_new_with_free_func (g_object_unref); mgr->store = bolt_store_new (g_getenv ("BOLT_DBPATH") ? : BOLT_DBDIR); mgr->probing_roots = g_ptr_array_new_with_free_func (g_free); mgr->probing_tsettle = PROBING_SETTLE_TIME_MS; /* milliseconds */ mgr->security = BOLT_SECURITY_UNKNOWN; /* default configuration */ mgr->policy = BOLT_POLICY_AUTO; mgr->authmode = BOLT_AUTH_ENABLED; g_signal_connect (mgr->store, "device-removed", G_CALLBACK (handle_store_device_removed), mgr); } static void bolt_manager_class_init (BoltManagerClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); BoltExportedClass *exported_class = BOLT_EXPORTED_CLASS (klass); gobject_class->finalize = bolt_manager_finalize; gobject_class->get_property = bolt_manager_get_property; props[PROP_VERSION] = g_param_spec_uint ("version", "Version", "Version", 0, G_MAXUINT32, 0, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); props[PROP_PROBING] = g_param_spec_boolean ("probing", "Probing", "Probing", FALSE, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); props[PROP_POLICY] = g_param_spec_enum ("default-policy", "DefaultPolicy", "DefaultPolicy", BOLT_TYPE_POLICY, BOLT_POLICY_AUTO, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); props[PROP_SECURITY] = g_param_spec_enum ("security-level", "SecurityLevel", NULL, BOLT_TYPE_SECURITY, BOLT_SECURITY_UNKNOWN, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); props[PROP_AUTHMODE] = g_param_spec_flags ("auth-mode", "AuthMode", NULL, BOLT_TYPE_AUTH_MODE, BOLT_AUTH_ENABLED, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); g_object_class_install_properties (gobject_class, PROP_LAST, props); bolt_exported_class_set_interface_info (exported_class, BOLT_DBUS_INTERFACE, "/boltd/org.freedesktop.bolt.xml"); bolt_exported_class_export_properties (exported_class, PROP_EXPORTED, PROP_LAST, props); bolt_exported_class_property_setter (exported_class, props[PROP_AUTHMODE], handle_set_authmode); bolt_exported_class_export_method (exported_class, "ListDevices", handle_list_devices); bolt_exported_class_export_method (exported_class, "DeviceByUid", handle_device_by_uid); bolt_exported_class_export_method (exported_class, "EnrollDevice", handle_enroll_device); bolt_exported_class_export_method (exported_class, "ForgetDevice", handle_forget_device); } static void bolt_manager_initable_iface_init (GInitableIface *iface) { iface->init = bolt_manager_initialize; } static gboolean monitor_add_filter (struct udev_monitor *monitor, const char *subsystem_devtype, GError **error) { g_autofree char *subsystem = NULL; char *devtype = NULL; gboolean ok; int r; subsystem = g_strdup (subsystem_devtype); devtype = strchr (subsystem, '/'); if (devtype != NULL) *devtype++ = '\0'; r = udev_monitor_filter_add_match_subsystem_devtype (monitor, subsystem, devtype); ok = r > -1; if (!ok) g_set_error (error, BOLT_ERROR, BOLT_ERROR_UDEV, "udev: could not add match for '%s' (%s) to monitor", subsystem, devtype ? : "*"); return ok; } static gboolean setup_monitor (BoltManager *mgr, const char *name, const char * const *filter, GSourceFunc callback, udev_monitor **monitor_out, GSource **watch_out, GError **error) { g_autoptr(udev_monitor) monitor = NULL; g_autoptr(GIOChannel) channel = NULL; GSource *watch; gboolean ok; int fd; int res; monitor = udev_monitor_new_from_netlink (mgr->udev, name); if (monitor == NULL) { g_set_error_literal (error, BOLT_ERROR, BOLT_ERROR_UDEV, "udev: could not create monitor"); return FALSE; } udev_monitor_set_receive_buffer_size (monitor, 128 * 1024 * 1024); for (guint i = 0; filter && filter[i] != NULL; i++) { ok = monitor_add_filter (monitor, filter[i], error); if (!ok) return FALSE; } res = udev_monitor_enable_receiving (monitor); if (res < 0) { g_set_error_literal (error, BOLT_ERROR, BOLT_ERROR_UDEV, "udev: could not enable monitoring"); return FALSE; } fd = udev_monitor_get_fd (monitor); if (fd < 0) { g_set_error_literal (error, BOLT_ERROR, BOLT_ERROR_UDEV, "udev: could not obtain fd for monitoring"); return FALSE; } channel = g_io_channel_unix_new (fd); watch = g_io_create_watch (channel, G_IO_IN); g_source_set_callback (watch, callback, mgr, NULL); g_source_attach (watch, g_main_context_get_thread_default ()); *monitor_out = udev_monitor_ref (monitor); *watch_out = watch; return TRUE; } static gboolean handle_uevent_udev (GIOChannel *source, GIOCondition condition, gpointer user_data) { g_autoptr(BoltDevice) dev = NULL; g_autoptr(udev_device) device = NULL; BoltManager *mgr; const char *action; const char *subsystem; const char *devtype; mgr = BOLT_MANAGER (user_data); device = udev_monitor_receive_device (mgr->udev_monitor); if (device == NULL) return G_SOURCE_CONTINUE; action = udev_device_get_action (device); if (action == NULL) return G_SOURCE_CONTINUE; devtype = udev_device_get_devtype (device); subsystem = udev_device_get_subsystem (device); if (g_str_equal (action, "add")) manager_probing_device_added (mgr, device); else if (g_str_equal (action, "remove")) manager_probing_device_removed (mgr, device); /* beyond this point only thunderbolt/thunderbolt_device * devices are allowed */ if (!bolt_streq (devtype, "thunderbolt_device") || !bolt_streq (subsystem, "thunderbolt")) return G_SOURCE_CONTINUE; bolt_debug (LOG_TOPIC ("udev"), "%s (%s%s%s)", action, subsystem, devtype ? "/" : "", devtype ? : ""); if (g_str_equal (action, "add") || g_str_equal (action, "change")) { const char *uid; /* filter sysfs devices (e.g. the domain) that don't have * the unique_id attribute */ uid = udev_device_get_sysattr_value (device, "unique_id"); if (uid == NULL) return G_SOURCE_CONTINUE; dev = manager_find_device_by_uid (mgr, uid, NULL); if (!dev) handle_udev_device_added (mgr, device); else if (!bolt_device_is_connected (dev)) handle_udev_device_attached (mgr, dev, device); else handle_udev_device_changed (mgr, dev, device); } else if (g_str_equal (action, "remove")) { const char *syspath; const char *name; syspath = udev_device_get_syspath (device); if (syspath == NULL) { bolt_warn (LOG_TOPIC ("udev"), "device without syspath"); return G_SOURCE_CONTINUE; } /* filter out the domain controller */ name = udev_device_get_sysname (device); if (name && g_str_has_prefix (name, "domain")) return G_SOURCE_CONTINUE; dev = manager_find_device_by_syspath (mgr, syspath); /* if we don't have any records of the device, * then we don't care */ if (!dev) return G_SOURCE_CONTINUE; if (bolt_device_get_stored (dev)) handle_udev_device_detached (mgr, dev); else hanlde_udev_device_removed (mgr, dev); } return G_SOURCE_CONTINUE; } static gboolean bolt_manager_initialize (GInitable *initable, GCancellable *cancellable, GError **error) { g_auto(GStrv) ids = NULL; BoltManager *mgr; struct udev_enumerate *enumerate; struct udev_list_entry *l, *devices; gboolean forced_power; gboolean ok; mgr = BOLT_MANAGER (initable); /* load dynamic user configuration */ manager_load_user_config (mgr); /* polkit setup */ mgr->bouncer = bolt_bouncer_new (cancellable, error); if (mgr->bouncer == NULL) return FALSE; bolt_bouncer_add_client (mgr->bouncer, mgr); /* udev setup*/ mgr->udev = udev_new (); if (mgr->udev == NULL) { g_set_error_literal (error, BOLT_ERROR, BOLT_ERROR_UDEV, "udev: could not create udev handle"); return FALSE; } ok = setup_monitor (mgr, "udev", NULL, (GSourceFunc) handle_uevent_udev, &mgr->udev_monitor, &mgr->udev_source, error); if (!ok) return FALSE; ids = bolt_store_list_uids (mgr->store, error); if (ids == NULL) { g_prefix_error (error, "failed to list devices in store"); return FALSE; } bolt_info (LOG_TOPIC ("store"), "loading devices"); for (guint i = 0; i < g_strv_length (ids); i++) { g_autoptr(GError) err = NULL; BoltDevice *dev = NULL; const char *uid = ids[i]; bolt_info (LOG_DEV_UID (uid), LOG_TOPIC ("store"), "loading device"); dev = bolt_store_get_device (mgr->store, uid, &err); if (dev == NULL) { bolt_warn_err (err, LOG_TOPIC ("store"), LOG_DIRECT (BOLT_LOG_DEVICE_UID, uid), "failed to load device (%.7s)", uid); continue; } manager_register_device (mgr, dev); } mgr->power = bolt_power_new (mgr->udev); forced_power = manager_maybe_power_controller (mgr); /* TODO: error checking */ enumerate = udev_enumerate_new (mgr->udev); udev_enumerate_add_match_subsystem (enumerate, "thunderbolt"); /* only devices (i.e. not the domain controller) */ bolt_info (LOG_TOPIC ("udev"), "enumerating devices"); udev_enumerate_scan_devices (enumerate); devices = udev_enumerate_get_list_entry (enumerate); udev_list_entry_foreach (l, devices) { g_autoptr(udev_device) udevice = NULL; g_autoptr(BoltDevice) dev = NULL; const char *uid; const char *syspath; const char *devtype; syspath = udev_list_entry_get_name (l); udevice = udev_device_new_from_syspath (mgr->udev, syspath); if (udevice == NULL) continue; devtype = udev_device_get_devtype (udevice); if (bolt_streq (devtype, "thunderbolt_domain")) manager_add_domain (mgr, udevice); if (!bolt_streq (devtype, "thunderbolt_device")) continue; uid = udev_device_get_sysattr_value (udevice, "unique_id"); if (uid == NULL) { bolt_warn ("thunderbolt device without uid"); continue; } dev = manager_find_device_by_uid (mgr, uid, NULL); if (dev) handle_udev_device_attached (mgr, dev, udevice); else handle_udev_device_added (mgr, udevice); } udev_enumerate_unref (enumerate); if (forced_power) { g_autoptr(GError) err = NULL; ok = bolt_power_force_switch (mgr->power, FALSE, &err); if (!ok) bolt_warn_err (err, LOG_TOPIC ("power"), "failed undo force power"); else bolt_info (LOG_TOPIC ("power"), "setting force_power to OFF"); } return TRUE; } static void manager_register_device (BoltManager *mgr, BoltDevice *dev) { g_ptr_array_add (mgr->devices, dev); bolt_bouncer_add_client (mgr->bouncer, dev); g_signal_connect (dev, "status-changed", G_CALLBACK (handle_device_status_changed), mgr); } static void manager_deregister_device (BoltManager *mgr, BoltDevice *dev) { g_ptr_array_remove_fast (mgr->devices, dev); // g_signal_handlers_unblock_by_func (dev, G_CALLBACK (handle_device_status_changed), mgr); } static BoltDevice * manager_find_device_by_syspath (BoltManager *mgr, const char *sysfs) { g_return_val_if_fail (sysfs != NULL, NULL); for (guint i = 0; i < mgr->devices->len; i++) { BoltDevice *dev = g_ptr_array_index (mgr->devices, i); const char *have = bolt_device_get_syspath (dev); if (bolt_streq (have, sysfs)) return g_object_ref (dev); } return NULL; } static BoltDevice * manager_find_device_by_uid (BoltManager *mgr, const char *uid, GError **error) { if (uid == NULL || uid[0] == '\0') { g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT, "empty device unique_id"); return NULL; } for (guint i = 0; i < mgr->devices->len; i++) { BoltDevice *dev = g_ptr_array_index (mgr->devices, i); if (bolt_streq (bolt_device_get_uid (dev), uid)) return g_object_ref (dev); } g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND, "device with id '%s' could not be found.", uid); return NULL; } static BoltDevice * bolt_manager_get_parent (BoltManager *mgr, BoltDevice *dev) { g_autofree char *path = NULL; const char *syspath; const char *start; char *pos; syspath = bolt_device_get_syspath (dev); if (syspath == NULL) return NULL; path = g_strdup (syspath); start = path + strlen ("/sys"); pos = strrchr (start, '/'); if (!pos || pos < start + 2) return NULL; *pos = '\0'; return manager_find_device_by_syspath (mgr, path); } static GPtrArray * bolt_manager_get_children (BoltManager *mgr, BoltDevice *target) { GPtrArray *res; res = g_ptr_array_new_with_free_func (g_object_unref); for (guint i = 0; i < mgr->devices->len; i++) { g_autoptr(BoltDevice) parent = NULL; BoltDevice *dev = g_ptr_array_index (mgr->devices, i); parent = bolt_manager_get_parent (mgr, dev); if (parent != target) continue; g_ptr_array_add (res, g_object_ref (dev)); } return res; } /* device authorization */ typedef struct { BoltAuth *auth; BoltDevice *dev; } AuthIdleData; static void authorize_device_finish (GObject *source, GAsyncResult *res, gpointer user_data) { g_autoptr(GError) err = NULL; BoltDevice *dev = BOLT_DEVICE (source); BoltAuth *auth = BOLT_AUTH (res); gboolean ok; ok = bolt_auth_check (auth, &err); if (!ok) bolt_warn_err (err, LOG_DEV (dev), "authorization failed"); else bolt_msg (LOG_DEV (dev), "authorized"); } static gboolean authorize_device_idle (gpointer user_data) { AuthIdleData *data = user_data; BoltDevice *dev = data->dev; BoltAuth *auth = data->auth; bolt_msg (LOG_DEV (dev), "authorizing"); bolt_device_authorize (dev, auth, authorize_device_finish, NULL); g_object_unref (data->auth); g_object_unref (data->dev); g_slice_free (AuthIdleData, data); return G_SOURCE_REMOVE; } static void maybe_authorize_device (BoltManager *mgr, BoltDevice *dev) { BoltStatus status = bolt_device_get_status (dev); BoltPolicy policy = bolt_device_get_policy (dev); const char *uid = bolt_device_get_uid (dev); BoltKey *key = NULL; BoltSecurity level; AuthIdleData *data; gboolean stored; bolt_info (LOG_DEV (dev), "checking possible authorization: %s (%x)", bolt_policy_to_string (policy), status); if (bolt_auth_mode_is_disabled (mgr->authmode)) { bolt_info (LOG_DEV (dev), "authorization is globally disabled."); return; } if (bolt_status_is_authorized (status) || policy != BOLT_POLICY_AUTO) return; stored = bolt_device_get_stored (dev); /* sanity check, because we already checked the policy */ g_return_if_fail (stored); level = bolt_device_get_security (dev); if (level == BOLT_SECURITY_SECURE && bolt_device_get_keystate (dev) != BOLT_KEY_MISSING) { g_autoptr(GError) err = NULL; key = bolt_store_get_key (mgr->store, uid, &err); if (key == NULL) bolt_warn_err (err, LOG_DEV (dev), "could not load key"); } if (level == BOLT_SECURITY_SECURE && key == NULL) { bolt_msg (LOG_DEV (dev), "have no key, not authorizing (secure mode)"); return; } data = g_slice_new (AuthIdleData); data->auth = bolt_auth_new (mgr, level, key); data->dev = g_object_ref (dev); g_idle_add (authorize_device_idle, data); } /* udev callbacks */ static void handle_udev_device_added (BoltManager *mgr, struct udev_device *udev) { g_autoptr(GError) err = NULL; GDBusConnection *bus; BoltDevice *dev; const char *opath; const char *syspath; dev = bolt_device_new_for_udev (udev, &err); if (dev == NULL) { bolt_warn_err (err, LOG_TOPIC ("udev"), "could not create device"); return; } manager_register_device (mgr, dev); syspath = udev_device_get_syspath (udev); bolt_msg (LOG_DEV (dev), "device added (%s)", syspath); /* if we have a valid dbus connection */ bus = bolt_exported_get_connection (BOLT_EXPORTED (mgr)); if (bus == NULL) return; opath = bolt_device_export (dev, bus, &err); if (opath == NULL) bolt_warn_err (err, LOG_DEV (dev), LOG_TOPIC ("dbus"), "error exporting"); else bolt_info (LOG_DEV (dev), LOG_TOPIC ("dbus"), "exported device at %.43s...", opath); bolt_exported_emit_signal (BOLT_EXPORTED (mgr), "DeviceAdded", g_variant_new ("(o)", opath), NULL); } static void handle_udev_device_changed (BoltManager *mgr, BoltDevice *dev, struct udev_device *udev) { g_autoptr(GPtrArray) children = NULL; BoltStatus after; BoltStatus before; before = bolt_device_get_status (dev); after = bolt_device_update_from_udev (dev, udev); if (before == after) return; bolt_info (LOG_DEV (dev), "device changed: %s", bolt_status_to_string (after)); if (!bolt_status_is_authorized (after)) return; children = bolt_manager_get_children (mgr, dev); for (guint i = 0; i < children->len; i++) { BoltDevice *child = g_ptr_array_index (children, i); maybe_authorize_device (mgr, child); } } static void hanlde_udev_device_removed (BoltManager *mgr, BoltDevice *dev) { const char *opath; const char *syspath; syspath = bolt_device_get_syspath (dev); bolt_msg (LOG_DEV (dev), "removed (%s)", syspath); manager_deregister_device (mgr, dev); opath = bolt_device_get_object_path (dev); if (opath == NULL) return; bolt_exported_emit_signal (BOLT_EXPORTED (mgr), "DeviceRemoved", g_variant_new ("(o)", opath), NULL); bolt_device_unexport (dev); bolt_info (LOG_DEV (dev), LOG_TOPIC ("dbus"), "unexported"); } static void handle_udev_device_attached (BoltManager *mgr, BoltDevice *dev, struct udev_device *udev) { g_autoptr(BoltDevice) parent = NULL; const char *syspath; BoltStatus status; status = bolt_device_connected (dev, udev); syspath = bolt_device_get_syspath (dev); bolt_msg (LOG_DEV (dev), "connected: %s (%s)", bolt_status_to_string (status), syspath); if (status != BOLT_STATUS_CONNECTED) return; parent = bolt_manager_get_parent (mgr, dev); if (parent) { const char *pid = bolt_device_get_uid (parent); status = bolt_device_get_status (parent); if (!bolt_status_is_authorized (status)) { bolt_info (LOG_DEV (dev), "parent [%s] not authorized", pid); return; } } else { bolt_warn (LOG_DEV (dev), "could not find parent"); } maybe_authorize_device (mgr, dev); } static void handle_udev_device_detached (BoltManager *mgr, BoltDevice *dev) { const char *syspath; syspath = bolt_device_get_syspath (dev); bolt_msg (LOG_DEV (dev), "disconnected (%s)", syspath); bolt_device_disconnected (dev); } static void handle_store_device_removed (BoltStore *store, const char *uid, BoltManager *mgr) { g_autoptr(BoltDevice) dev = NULL; BoltStatus status; const char *opath; dev = manager_find_device_by_uid (mgr, uid, NULL); bolt_msg (LOG_DEV (dev), "removed from store"); if (!dev) return; /* TODO: maybe move to a new bolt_device_removed (dev) */ g_object_set (dev, "store", NULL, "key", BOLT_KEY_MISSING, "policy", BOLT_POLICY_DEFAULT, NULL); status = bolt_device_get_status (dev); /* if the device is connected, keep it around */ if (status != BOLT_STATUS_DISCONNECTED) return; manager_deregister_device (mgr, dev); opath = bolt_device_get_object_path (dev); if (opath == NULL) return; bolt_exported_emit_signal (BOLT_EXPORTED (mgr), "DeviceRemoved", g_variant_new ("(o)", opath), NULL); bolt_device_unexport (dev); bolt_info (LOG_DEV (dev), "unexported"); } static void handle_device_status_changed (BoltDevice *dev, BoltStatus old, BoltManager *mgr) { BoltStatus now; now = bolt_device_get_status (dev); bolt_debug (LOG_DEV (dev), "status changed: %s -> %s", bolt_status_to_string (old), bolt_status_to_string (now)); if (now == old) return; /* sanity check */ if (now == BOLT_STATUS_AUTHORIZING) mgr->authorizing += 1; else if (old == BOLT_STATUS_AUTHORIZING) mgr->authorizing -= 1; manager_probing_activity (mgr, !mgr->authorizing); } static gboolean probing_timeout (gpointer user_data) { BoltManager *mgr; gint64 now, dt, timeout; mgr = BOLT_MANAGER (user_data); if (mgr->authorizing > 0) return G_SOURCE_CONTINUE; now = g_get_monotonic_time (); dt = now - mgr->probing_tstamp; /* dt is in microseconds, probing timeout in * milli seconds */ timeout = mgr->probing_tsettle * MSEC_PER_USEC; if (dt < timeout) return G_SOURCE_CONTINUE; /* we are done, remove us */ mgr->probing_timeout = 0; g_object_notify_by_pspec (G_OBJECT (mgr), props[PROP_PROBING]); bolt_info (LOG_TOPIC ("probing"), "timeout, done: [%ld] (%ld)", dt, timeout); bolt_exported_flush (BOLT_EXPORTED (mgr)); return G_SOURCE_REMOVE; } static void manager_probing_activity (BoltManager *mgr, gboolean weak) { guint dt; mgr->probing_tstamp = g_get_monotonic_time (); if (mgr->probing_timeout || weak) return; dt = mgr->probing_tsettle / 2; bolt_info (LOG_TOPIC ("probing"), "started [%u]", dt); mgr->probing_timeout = g_timeout_add (dt, probing_timeout, mgr); g_object_notify_by_pspec (G_OBJECT (mgr), props[PROP_PROBING]); bolt_exported_flush (BOLT_EXPORTED (mgr)); } static gboolean device_is_thunderbolt_root (struct udev_device *dev) { const char *driver; const char *subsys; driver = udev_device_get_driver (dev); subsys = udev_device_get_subsystem (dev); return bolt_streq (subsys, "pci") && bolt_streq (driver, "thunderbolt"); } static gboolean probing_add_root (BoltManager *mgr, struct udev_device *dev) { const char *syspath; GPtrArray *roots; g_return_val_if_fail (device_is_thunderbolt_root (dev), FALSE); /* we go two levels up */ for (guint i = 0; dev != NULL && i < 2; i++) dev = udev_device_get_parent (dev); if (dev == NULL) return FALSE; roots = mgr->probing_roots; syspath = udev_device_get_syspath (dev); g_ptr_array_add (roots, g_strdup (syspath)); bolt_info (LOG_TOPIC ("probing"), "adding %s to roots", syspath); return TRUE; } static void manager_probing_device_added (BoltManager *mgr, struct udev_device *dev) { const char *syspath; GPtrArray *roots; gboolean added; syspath = udev_device_get_syspath (dev); if (syspath == NULL) return; roots = mgr->probing_roots; for (guint i = 0; i < roots->len; i++) { const char *r = g_ptr_array_index (roots, i); if (g_str_has_prefix (syspath, r)) { bolt_debug (LOG_TOPIC ("probing"), "match %s", syspath); /* do something */ manager_probing_activity (mgr, FALSE); return; } } /* if we ended up here we didn't find a root, * maybe we are one */ if (!device_is_thunderbolt_root (dev)) return; added = probing_add_root (mgr, dev); if (added) manager_probing_activity (mgr, FALSE); } static void manager_probing_device_removed (BoltManager *mgr, struct udev_device *dev) { const char *syspath; GPtrArray *roots; gboolean found; guint index; syspath = udev_device_get_syspath (dev); if (syspath == NULL) return; roots = mgr->probing_roots; found = FALSE; for (index = 0; index < roots->len; index++) { const char *r = g_ptr_array_index (roots, index); found = g_str_equal (syspath, r); if (found) break; } if (!found) return; bolt_info (LOG_TOPIC ("probing"), "removing %s from roots", syspath); g_ptr_array_remove_index_fast (mgr->probing_roots, index); } static void manager_probing_domain_added (BoltManager *mgr, struct udev_device *domain) { struct udev_device *p = domain; /* walk up until we find the thunderbolt root */ while (p && !device_is_thunderbolt_root (p)) p = udev_device_get_parent (p); if (p == NULL) return; probing_add_root (mgr, p); } /* domain related function */ static void manager_add_domain (BoltManager *mgr, struct udev_device *domain) { g_autoptr(GError) err = NULL; const char *name; BoltSecurity sl; manager_probing_domain_added (mgr, domain); name = udev_device_get_sysname (domain); sl = bolt_sysfs_security_for_device (domain, &err); if (sl == BOLT_SECURITY_UNKNOWN) { bolt_warn_err (err, LOG_TOPIC ("udev"), "domain '%s'", name); return; } if (mgr->security == BOLT_SECURITY_UNKNOWN) { bolt_info ("security level set to '%s'", bolt_security_to_string (sl)); mgr->security = sl; } else if (mgr->security != sl) { bolt_warn ("multiple security levels (%s vs %s)", bolt_security_to_string (mgr->security), bolt_security_to_string (sl)); } } static int manager_count_domains (BoltManager *mgr) { struct udev_enumerate *e; struct udev_list_entry *l, *devices; int res, count = 0; e = udev_enumerate_new (mgr->udev); udev_enumerate_add_match_subsystem (e, "thunderbolt"); udev_enumerate_add_match_property (e, "DEVTYPE", "thunderbolt_domain"); res = udev_enumerate_scan_devices (e); if (res < 0) return res; devices = udev_enumerate_get_list_entry (e); udev_list_entry_foreach (l, devices) count++; udev_enumerate_unref (e); return count; } static gboolean manager_maybe_power_controller (BoltManager *mgr) { g_autoptr(GError) err = NULL; gboolean can_force_power; gboolean ok; int n; can_force_power = bolt_power_can_force (mgr->power); bolt_info (LOG_TOPIC ("power"), "force_power support: %s", bolt_yesno (can_force_power)); if (can_force_power == FALSE) return FALSE; n = manager_count_domains (mgr); if (n > 0) return FALSE; bolt_info (LOG_TOPIC ("power"), "setting force_power to ON"); ok = bolt_power_force_switch (mgr->power, TRUE, &err); if (!ok) { bolt_warn_err (err, LOG_TOPIC ("power"), "could not force power"); return ok; } /* we wait for a total of 5.0 seconds, should hopefully * be enough for at least the domain to show up. */ for (int i = 0; i < 25 && n < 1; i++) { g_usleep (200000); /* 200 000 us = 0.2s */ n = manager_count_domains (mgr); } bolt_info (LOG_TOPIC ("power"), "found %d domains", n); return ok; } /* config */ static void manager_load_user_config (BoltManager *mgr) { g_autoptr(GError) err = NULL; BoltPolicy policy; BoltAuthMode authmode; BoltTri res; bolt_info (LOG_TOPIC ("config"), "loading user config"); mgr->config = bolt_store_config_load (mgr->store, &err); if (mgr->config == NULL) { if (!bolt_err_notfound (err)) bolt_warn_err (err, LOG_TOPIC ("config"), "failed to load user config"); return; } bolt_info (LOG_TOPIC ("config"), "user config loaded successfully"); res = bolt_config_load_default_policy (mgr->config, &policy, &err); if (res == TRI_ERROR) { bolt_warn_err (err, LOG_TOPIC ("config"), "failed to load default policy"); g_clear_error (&err); } else if (res == TRI_YES) { mgr->policy = policy; bolt_info (LOG_TOPIC ("config"), "default policy set to %s", bolt_policy_to_string (policy)); g_object_notify_by_pspec (G_OBJECT (mgr), props[PROP_POLICY]); } res = bolt_config_load_auth_mode (mgr->config, &authmode, &err); if (res == TRI_ERROR) { bolt_warn_err (err, LOG_TOPIC ("config"), "failed to load auth mode"); g_clear_error (&err); } else if (res == TRI_YES) { g_autofree char *str = NULL; str = bolt_flags_to_string (BOLT_TYPE_AUTH_MODE, authmode, NULL); bolt_info (LOG_TOPIC ("config"), "auth mode set to '%s'", str); mgr->authmode = authmode; g_object_notify_by_pspec (G_OBJECT (mgr), props[PROP_POLICY]); } } /* dbus property setter */ static gboolean handle_set_authmode (BoltExported *obj, const char *name, const GValue *value, GError **error) { g_autoptr(GError) err = NULL; g_autofree char *str = NULL; BoltManager *mgr = BOLT_MANAGER (obj); BoltAuthMode authmode; gboolean ok; authmode = g_value_get_flags (value); if (authmode == mgr->authmode) return TRUE; if (mgr->config == NULL) mgr->config = bolt_config_user_init (); str = bolt_flags_to_string (BOLT_TYPE_AUTH_MODE, authmode, &err); if (str == NULL) { bolt_warn_err (err, LOG_TOPIC ("config"), "error setting authmode"); g_propagate_error (error, g_steal_pointer (&err)); return FALSE; } bolt_config_set_auth_mode (mgr->config, str); ok = bolt_store_config_save (mgr->store, mgr->config, &err); if (!ok) { bolt_warn_err (err, LOG_TOPIC ("config"), "error saving config"); g_propagate_error (error, g_steal_pointer (&err)); return FALSE; } mgr->authmode = authmode; bolt_info (LOG_TOPIC ("config"), "auth mode set to '%s'", str); return ok; } /* dbus methods */ static gboolean handle_list_devices (BoltExported *obj, GVariant *params, GDBusMethodInvocation *inv) { BoltManager *mgr = BOLT_MANAGER (obj); const char **devs; devs = g_newa (const char *, mgr->devices->len + 1); for (guint i = 0; i < mgr->devices->len; i++) { BoltDevice *d = g_ptr_array_index (mgr->devices, i); devs[i] = bolt_device_get_object_path (d); } devs[mgr->devices->len] = NULL; g_dbus_method_invocation_return_value (inv, g_variant_new ("(^ao)", devs)); return TRUE; } static gboolean handle_device_by_uid (BoltExported *obj, GVariant *params, GDBusMethodInvocation *inv) { g_autoptr(BoltDevice) dev = NULL; g_autoptr(GError) error = NULL; BoltManager *mgr; const char *uid; const char *opath; mgr = BOLT_MANAGER (obj); g_variant_get (params, "(&s)", &uid); dev = manager_find_device_by_uid (mgr, uid, &error); if (dev == NULL) { g_dbus_method_invocation_return_gerror (inv, error); return TRUE; } opath = bolt_device_get_object_path (dev); g_dbus_method_invocation_return_value (inv, g_variant_new ("(o)", opath)); return TRUE; } static void enroll_device_done (GObject *device, GAsyncResult *res, gpointer user_data) { BoltAuth *auth; BoltDevice *dev; BoltManager *mgr; GDBusMethodInvocation *inv; GError *error = NULL; const char *opath; gboolean ok; inv = user_data; dev = BOLT_DEVICE (device); auth = BOLT_AUTH (res); mgr = BOLT_MANAGER (bolt_auth_get_origin (auth)); ok = bolt_auth_check (auth, &error); if (ok) { GVariant *params; const char *str; BoltPolicy policy; guint64 now; params = g_dbus_method_invocation_get_parameters (inv); g_variant_get_child (params, 1, "&s", &str); policy = bolt_enum_from_string (BOLT_TYPE_POLICY, str, NULL); if (policy == BOLT_POLICY_DEFAULT) policy = mgr->policy; now = bolt_now_in_seconds (); g_object_set (dev, "storetime", now, NULL); ok = bolt_store_put_device (mgr->store, dev, policy, bolt_auth_get_key (auth), &error); } if (!ok) { g_dbus_method_invocation_take_error (inv, error); return; } opath = bolt_device_get_object_path (dev); g_dbus_method_invocation_return_value (inv, g_variant_new ("(o)", opath)); } static gboolean handle_enroll_device (BoltExported *obj, GVariant *params, GDBusMethodInvocation *inv) { g_autoptr(BoltDevice) dev = NULL; g_autoptr(BoltAuth) auth = NULL; g_autoptr(BoltKey) key = NULL; g_autoptr(GError) error = NULL; BoltManager *mgr; const char *uid; BoltSecurity level; const char *policy; mgr = BOLT_MANAGER (obj); g_variant_get_child (params, 0, "&s", &uid); g_variant_get_child (params, 1, "&s", &policy); dev = manager_find_device_by_uid (mgr, uid, &error); if (dev == NULL) { g_dbus_method_invocation_return_gerror (inv, error); return TRUE; } if (bolt_enum_from_string (BOLT_TYPE_POLICY, policy, &error) == -1) { g_dbus_method_invocation_return_error (inv, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS, "invalid policy: %s", policy); return TRUE; } if (bolt_device_get_stored (dev)) { g_dbus_method_invocation_return_error (inv, G_IO_ERROR, G_IO_ERROR_EXISTS, "device with id '%s' already enrolled.", uid); return TRUE; } if (bolt_auth_mode_is_disabled (mgr->authmode)) { g_dbus_method_invocation_return_error (inv, G_DBUS_ERROR, G_DBUS_ERROR_ACCESS_DENIED, "authorization of new devices is disabled"); return TRUE; } if (bolt_device_supports_secure_mode (dev)) level = bolt_device_get_security (dev); else level = BOLT_SECURITY_USER; key = NULL; if (level == BOLT_SECURITY_SECURE) key = bolt_key_new (); auth = bolt_auth_new (mgr, level, key); bolt_device_authorize (dev, auth, enroll_device_done, inv); return TRUE; } static gboolean handle_forget_device (BoltExported *obj, GVariant *params, GDBusMethodInvocation *inv) { g_autoptr(BoltDevice) dev = NULL; g_autoptr(GError) error = NULL; BoltManager *mgr; gboolean ok; const char *uid; mgr = BOLT_MANAGER (obj); g_variant_get (params, "(&s)", &uid); dev = manager_find_device_by_uid (mgr, uid, &error); if (dev == NULL) { g_dbus_method_invocation_take_error (inv, g_steal_pointer (&error)); return TRUE; } ok = bolt_store_del (mgr->store, dev, &error); if (!ok) g_dbus_method_invocation_take_error (inv, g_steal_pointer (&error)); else g_dbus_method_invocation_return_value (inv, g_variant_new ("()")); return TRUE; } /* public methods */ gboolean bolt_manager_export (BoltManager *mgr, GDBusConnection *connection, GError **error) { if (!bolt_exported_export (BOLT_EXPORTED (mgr), connection, BOLT_DBUS_PATH, error)) return FALSE; for (guint i = 0; i < mgr->devices->len; i++) { g_autoptr(GError) err = NULL; BoltDevice *dev = g_ptr_array_index (mgr->devices, i); const char *opath; opath = bolt_device_export (dev, connection, &err); if (opath == NULL) { bolt_warn_err (err, LOG_DEV (dev), LOG_TOPIC ("dbus"), "error exporting a device"); continue; } bolt_info (LOG_DEV (dev), LOG_TOPIC ("dbus"), "exported deviceat %.43s...", opath); } return TRUE; } void bolt_manager_got_the_name (BoltManager *mgr) { /* emit DeviceAdded signals now that we have the name * for all devices that are not stored and connected */ for (guint i = 0; i < mgr->devices->len; i++) { BoltDevice *dev = g_ptr_array_index (mgr->devices, i); BoltStatus status; gboolean stored; const char *opath; stored = bolt_device_get_stored (dev); if (stored) continue; status = bolt_device_get_status (dev); if (status != BOLT_STATUS_CONNECTED) continue; opath = bolt_exported_get_object_path (BOLT_EXPORTED (dev)); if (opath == NULL) continue; bolt_exported_emit_signal (BOLT_EXPORTED (mgr), "DeviceAdded", g_variant_new ("(o)", opath), NULL); } } bolt-0.2/boltd/bolt-manager.h000066400000000000000000000023241324740507400161470ustar00rootroot00000000000000/* * Copyright © 2017 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 library 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 . * * Authors: * Christian J. Kellner */ #pragma once #include "bolt-exported.h" G_BEGIN_DECLS #define BOLT_TYPE_MANAGER bolt_manager_get_type () G_DECLARE_FINAL_TYPE (BoltManager, bolt_manager, BOLT, MANAGER, BoltExported); gboolean bolt_manager_export (BoltManager *mgr, GDBusConnection *connection, GError **error); void bolt_manager_got_the_name (BoltManager *mgr); G_END_DECLS bolt-0.2/boltd/bolt-power.c000066400000000000000000000101721324740507400156640ustar00rootroot00000000000000/* * Copyright © 2017 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 library 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 . * * Authors: * Christian J. Kellner */ #include "config.h" #include "bolt-power.h" #include "bolt-log.h" #include "bolt-io.h" #include "bolt-str.h" #include #define INTEL_WMI_THUNDERBOLT_GUID "86CCFD48-205E-4A77-9C48-2021CBEDE341" typedef struct udev_device udev_device; G_DEFINE_AUTOPTR_CLEANUP_FUNC (udev_device, udev_device_unref); struct _BoltPower { GObject object; /* the actual key plus the null char */ char *path; }; enum { PROP_0, PROP_SUPPORTED, PROP_LAST }; static GParamSpec *power_props[PROP_LAST] = { NULL, }; G_DEFINE_TYPE (BoltPower, bolt_power, G_TYPE_OBJECT); static void bolt_power_finalize (GObject *object) { BoltPower *power = BOLT_POWER (object); g_clear_pointer (&power->path, g_free); G_OBJECT_CLASS (bolt_power_parent_class)->finalize (object); } static void bolt_power_init (BoltPower *power) { } static void bolt_power_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { BoltPower *power = BOLT_POWER (object); switch (prop_id) { case PROP_SUPPORTED: g_value_set_boolean (value, power->path != NULL); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); } } static void bolt_power_class_init (BoltPowerClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); gobject_class->finalize = bolt_power_finalize; gobject_class->get_property = bolt_power_get_property; power_props[PROP_SUPPORTED] = g_param_spec_boolean ("supported", NULL, NULL, FALSE, G_PARAM_READABLE | G_PARAM_STATIC_NICK); g_object_class_install_properties (gobject_class, PROP_LAST, power_props); } BoltPower * bolt_power_new (struct udev *udev) { struct udev_enumerate *e; struct udev_list_entry *l, *devices; BoltPower *power; power = g_object_new (BOLT_TYPE_POWER, NULL); e = udev_enumerate_new (udev); udev_enumerate_add_match_subsystem (e, "wmi"); udev_enumerate_add_match_property (e, "DRIVER", "intel-wmi-thunderbolt"); udev_enumerate_scan_devices (e); devices = udev_enumerate_get_list_entry (e); udev_list_entry_foreach (l, devices) { g_autofree char *path = NULL; const char *syspath; syspath = udev_list_entry_get_name (l); path = g_build_filename (syspath, "force_power", NULL); if (g_file_test (path, G_FILE_TEST_IS_REGULAR)) { power->path = g_steal_pointer (&path); break; } } udev_enumerate_unref (e); return power; } gboolean bolt_power_can_force (BoltPower *power) { return power->path != NULL; } gboolean bolt_power_force_switch (BoltPower *power, gboolean on, GError **error) { gboolean ok; int fd; g_return_val_if_fail (BOLT_IS_POWER (power), FALSE); if (power->path == NULL) { g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, "force power not supported"); return FALSE; } fd = bolt_open (power->path, O_WRONLY, 0, error); if (fd < 0) return FALSE; ok = bolt_write_all (fd, on ? "1" : "0", 1, error); bolt_close (fd, NULL); return ok; } bolt-0.2/boltd/bolt-power.h000066400000000000000000000024331324740507400156720ustar00rootroot00000000000000/* * Copyright © 2017 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 library 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 . * * Authors: * Christian J. Kellner */ #pragma once #include G_BEGIN_DECLS /* forward declaration */ struct udev; #define BOLT_TYPE_POWER bolt_power_get_type () G_DECLARE_FINAL_TYPE (BoltPower, bolt_power, BOLT, POWER, GObject); BoltPower * bolt_power_new (struct udev *udev); gboolean bolt_power_can_force (BoltPower *power); gboolean bolt_power_force_switch (BoltPower *power, gboolean on, GError **error); G_END_DECLS bolt-0.2/boltd/bolt-store.c000066400000000000000000000346211324740507400156710ustar00rootroot00000000000000/* * Copyright © 2017 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 library 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 . * * Authors: * Christian J. Kellner */ #include "config.h" #include "bolt-store.h" #include "bolt-error.h" #include "bolt-fs.h" #include "bolt-io.h" #include "bolt-log.h" #include "bolt-str.h" #include /* ************************************ */ /* BoltStore */ struct _BoltStore { GObject object; GFile *root; GFile *devices; GFile *keys; }; enum { PROP_STORE_0, PROP_ROOT, PROP_STORE_LAST }; static GParamSpec *store_props[PROP_STORE_LAST] = { NULL, }; enum { SIGNAL_DEVICE_ADDED, SIGNAL_DEVICE_REMOVED, SIGNAL_LAST }; static guint signals[SIGNAL_LAST] = {0}; G_DEFINE_TYPE (BoltStore, bolt_store, G_TYPE_OBJECT) static void bolt_store_finalize (GObject *object) { BoltStore *store = BOLT_STORE (object); if (store->root) g_clear_object (&store->root); if (store->devices) g_clear_object (&store->devices); if (store->keys) g_clear_object (&store->keys); G_OBJECT_CLASS (bolt_store_parent_class)->finalize (object); } static void bolt_store_init (BoltStore *store) { } static void bolt_store_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { BoltStore *store = BOLT_STORE (object); switch (prop_id) { case PROP_ROOT: g_value_set_object (value, store->root); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); } } static void bolt_store_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { BoltStore *store = BOLT_STORE (object); switch (prop_id) { case PROP_ROOT: store->root = g_value_dup_object (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); } } static void bolt_store_constructed (GObject *obj) { BoltStore *store = BOLT_STORE (obj); store->devices = g_file_get_child (store->root, "devices"); store->keys = g_file_get_child (store->root, "keys"); } static void bolt_store_class_init (BoltStoreClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); gobject_class->finalize = bolt_store_finalize; gobject_class->constructed = bolt_store_constructed; gobject_class->get_property = bolt_store_get_property; gobject_class->set_property = bolt_store_set_property; store_props[PROP_ROOT] = g_param_spec_object ("root", NULL, NULL, G_TYPE_FILE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_NAME); g_object_class_install_properties (gobject_class, PROP_STORE_LAST, store_props); signals[SIGNAL_DEVICE_ADDED] = g_signal_new ("device-added", G_TYPE_FROM_CLASS (gobject_class), G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 1, G_TYPE_STRING); signals[SIGNAL_DEVICE_REMOVED] = g_signal_new ("device-removed", G_TYPE_FROM_CLASS (gobject_class), G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 1, G_TYPE_STRING); } /* internal methods */ #define DEVICE_GROUP "device" #define USER_GROUP "user" #define CFG_FILE "boltd.conf" /* public methods */ BoltStore * bolt_store_new (const char *path) { g_autoptr(GFile) root = NULL; BoltStore *store; root = g_file_new_for_path (path); store = g_object_new (BOLT_TYPE_STORE, "root", root, NULL); return store; } GKeyFile * bolt_store_config_load (BoltStore *store, GError **error) { g_autoptr(GKeyFile) kf = NULL; g_autoptr(GFile) sf = NULL; g_autofree char *data = NULL; gboolean ok; gsize len; g_return_val_if_fail (store != NULL, FALSE); sf = g_file_get_child (store->root, CFG_FILE); ok = g_file_load_contents (sf, NULL, &data, &len, NULL, error); if (!ok) return NULL; kf = g_key_file_new (); ok = g_key_file_load_from_data (kf, data, len, G_KEY_FILE_NONE, error); if (!ok) return NULL; return g_steal_pointer (&kf); } gboolean bolt_store_config_save (BoltStore *store, GKeyFile *config, GError **error) { g_autoptr(GFile) sf = NULL; g_autofree char *data = NULL; gboolean ok; gsize len; sf = g_file_get_child (store->root, CFG_FILE); data = g_key_file_to_data (config, &len, error); if (!data) return FALSE; ok = g_file_replace_contents (sf, data, len, NULL, FALSE, 0, NULL, NULL, error); return ok; } GStrv bolt_store_list_uids (BoltStore *store, GError **error) { g_autoptr(GError) err = NULL; g_autoptr(GDir) dir = NULL; g_autofree char *path = NULL; g_autoptr(GPtrArray) ids = NULL; const char *name; ids = g_ptr_array_new (); path = g_file_get_path (store->devices); dir = g_dir_open (path, 0, &err); if (dir == NULL) { if (bolt_err_notfound (err)) return bolt_strv_from_ptr_array (&ids); g_propagate_error (error, g_steal_pointer (&err)); return NULL; } while ((name = g_dir_read_name (dir)) != NULL) { if (g_str_has_prefix (name, ".")) continue; g_ptr_array_add (ids, g_strdup (name)); } return bolt_strv_from_ptr_array (&ids); } gboolean bolt_store_put_device (BoltStore *store, BoltDevice *device, BoltPolicy policy, BoltKey *key, GError **error) { g_autoptr(GFile) entry = NULL; g_autoptr(GKeyFile) kf = NULL; g_autofree char *data = NULL; BoltDeviceType type; const char *uid; const char *label; gboolean ok; gint64 stime; gsize len; guint keystate = 0; g_return_val_if_fail (store != NULL, FALSE); g_return_val_if_fail (device != NULL, FALSE); uid = bolt_device_get_uid (device); g_assert (uid); entry = g_file_get_child (store->devices, uid); ok = bolt_fs_make_parent_dirs (entry, error); if (!ok) return FALSE; kf = g_key_file_new (); g_key_file_set_string (kf, DEVICE_GROUP, "name", bolt_device_get_name (device)); g_key_file_set_string (kf, DEVICE_GROUP, "vendor", bolt_device_get_vendor (device)); type = bolt_device_get_device_type (device); g_key_file_set_string (kf, DEVICE_GROUP, "type", bolt_device_type_to_string (type)); if (policy != BOLT_POLICY_DEFAULT) { const char *str = bolt_policy_to_string (policy); g_key_file_set_string (kf, USER_GROUP, "policy", str); } label = bolt_device_get_label (device); if (label != NULL) g_key_file_set_string (kf, USER_GROUP, "label", label); stime = bolt_device_get_storetime (device); if (stime > 0) g_key_file_set_uint64 (kf, USER_GROUP, "storetime", stime); data = g_key_file_to_data (kf, &len, error); if (!data) return FALSE; if (key) { g_autoptr(GError) err = NULL; g_autoptr(GFile) keypath = g_file_get_child (store->keys, uid); ok = bolt_fs_make_parent_dirs (keypath, &err); if (ok) ok = bolt_key_save_file (key, keypath, &err); if (!ok) bolt_warn_err (err, "failed to store key"); else keystate = bolt_key_get_state (key); } ok = g_file_replace_contents (entry, data, len, NULL, FALSE, 0, NULL, NULL, error); if (ok) { g_object_set (device, "store", store, "policy", policy, "key", keystate, NULL); g_signal_emit (store, signals[SIGNAL_DEVICE_ADDED], 0, uid); } return ok; } BoltDevice * bolt_store_get_device (BoltStore *store, const char *uid, GError **error) { g_autoptr(GKeyFile) kf = NULL; g_autoptr(GFile) db = NULL; g_autoptr(GError) err = NULL; g_autofree char *name = NULL; g_autofree char *vendor = NULL; g_autofree char *data = NULL; g_autofree char *typestr = NULL; g_autofree char *polstr = NULL; g_autofree char *label = NULL; BoltDeviceType type; BoltPolicy policy; BoltKeyState key; gboolean ok; guint64 stime; gsize len; g_return_val_if_fail (store != NULL, FALSE); g_return_val_if_fail (uid != NULL, FALSE); db = g_file_get_child (store->devices, uid); ok = g_file_load_contents (db, NULL, &data, &len, NULL, error); if (!ok) return NULL; kf = g_key_file_new (); ok = g_key_file_load_from_data (kf, data, len, G_KEY_FILE_NONE, error); if (!ok) return NULL; name = g_key_file_get_string (kf, DEVICE_GROUP, "name", NULL); vendor = g_key_file_get_string (kf, DEVICE_GROUP, "vendor", NULL); typestr = g_key_file_get_string (kf, DEVICE_GROUP, "type", NULL); type = bolt_device_type_from_string (typestr); polstr = g_key_file_get_string (kf, USER_GROUP, "policy", NULL); policy = bolt_policy_from_string (polstr); label = g_key_file_get_string (kf, USER_GROUP, "label", NULL); if (!bolt_device_type_validate (type)) { bolt_warn (LOG_TOPIC ("store"), LOG_DEV_UID (uid), "invalid device type: %s", typestr); type = BOLT_DEVICE_PERIPHERAL; } if (!bolt_policy_validate (policy)) { bolt_warn (LOG_TOPIC ("store"), LOG_DEV_UID (uid), "invalid policy: %s", polstr); policy = BOLT_POLICY_MANUAL; } if (label != NULL) { g_autofree char *tmp = g_steal_pointer (&label); label = bolt_strdup_validate (tmp); if (label == NULL) bolt_warn (LOG_TOPIC ("store"), LOG_DEV_UID (uid), "invalid device label: %s", label); } stime = g_key_file_get_uint64 (kf, USER_GROUP, "storetime", &err); if (err != NULL && !bolt_err_notfound (err)) bolt_warn_err (err, LOG_TOPIC ("store"), "invalid enroll-time"); if (stime == 0) { g_autoptr(GFileInfo) info = NULL; info = g_file_query_info (db, "time::changed", G_FILE_QUERY_INFO_NONE, NULL, NULL); if (info != NULL) stime = g_file_info_get_attribute_uint64 (info, "time::changed"); } key = bolt_store_have_key (store, uid); g_return_val_if_fail (name != NULL, NULL); g_return_val_if_fail (vendor != NULL, NULL); return g_object_new (BOLT_TYPE_DEVICE, "uid", uid, "name", name, "vendor", vendor, "type", type, "status", BOLT_STATUS_DISCONNECTED, "store", store, "policy", policy, "key", key, "storetime", stime, "label", label, NULL); } gboolean bolt_store_del_device (BoltStore *store, const char *uid, GError **error) { g_autoptr(GFile) devpath = NULL; gboolean ok; devpath = g_file_get_child (store->devices, uid); ok = g_file_delete (devpath, NULL, error); if (ok) g_signal_emit (store, signals[SIGNAL_DEVICE_REMOVED], 0, uid); return ok; } BoltKeyState bolt_store_have_key (BoltStore *store, const char *uid) { g_autoptr(GFileInfo) keyinfo = NULL; g_autoptr(GFile) keypath = NULL; g_autoptr(GError) err = NULL; guint key = BOLT_KEY_MISSING; keypath = g_file_get_child (store->keys, uid); keyinfo = g_file_query_info (keypath, "standard::*", 0, NULL, &err); if (keyinfo != NULL) key = BOLT_KEY_HAVE; /* todo: check size */ else if (!bolt_err_notfound (err)) bolt_warn_err (err, LOG_DEV_UID (uid), "error querying key info"); return key; } BoltKey * bolt_store_get_key (BoltStore *store, const char *uid, GError **error) { g_autoptr(GFile) keypath = NULL; keypath = g_file_get_child (store->keys, uid); return bolt_key_load_file (keypath, error); } gboolean bolt_store_del_key (BoltStore *store, const char *uid, GError **error) { g_autoptr(GFile) keypath = NULL; gboolean ok; keypath = g_file_get_child (store->keys, uid); ok = g_file_delete (keypath, NULL, error); return ok; } gboolean bolt_store_del (BoltStore *store, BoltDevice *dev, GError **error) { g_autoptr(GError) err = NULL; const char *uid; gboolean ok; g_return_val_if_fail (store != NULL, FALSE); g_return_val_if_fail (dev != NULL, FALSE); uid = bolt_device_get_uid (dev); ok = bolt_store_del_key (store, uid, &err); if (!ok && !bolt_err_notfound (err)) { g_propagate_prefixed_error (error, g_steal_pointer (&err), "could not delete key: "); return FALSE; } ok = bolt_store_del_device (store, uid, error); if (ok) { g_object_set (dev, "store", NULL, "key", BOLT_KEY_MISSING, "policy", BOLT_POLICY_DEFAULT, NULL); } return ok; } bolt-0.2/boltd/bolt-store.h000066400000000000000000000054261324740507400156770ustar00rootroot00000000000000/* * Copyright © 2017 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 library 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 . * * Authors: * Christian J. Kellner */ #pragma once #include #include "bolt-device.h" #include "bolt-key.h" #include "bolt-enums.h" G_BEGIN_DECLS /* BoltStore - database for devices, keys */ #define BOLT_TYPE_STORE bolt_store_get_type () G_DECLARE_FINAL_TYPE (BoltStore, bolt_store, BOLT, STORE, GObject); BoltStore * bolt_store_new (const char *path); GKeyFile * bolt_store_config_load (BoltStore *store, GError **error); gboolean bolt_store_config_save (BoltStore *store, GKeyFile *config, GError **error); GStrv bolt_store_list_uids (BoltStore *store, GError **error); gboolean bolt_store_del (BoltStore *store, BoltDevice *dev, GError **error); gboolean bolt_store_put_device (BoltStore *store, BoltDevice *device, BoltPolicy policy, BoltKey *key, GError **error); BoltDevice * bolt_store_get_device (BoltStore *store, const char *uid, GError **error); gboolean bolt_store_del_device (BoltStore *store, const char *uid, GError **error); BoltKeyState bolt_store_have_key (BoltStore *store, const char *uid); BoltKey * bolt_store_get_key (BoltStore *store, const char *uid, GError **error); gboolean bolt_store_del_key (BoltStore *store, const char *uid, GError **error); G_END_DECLS bolt-0.2/boltd/bolt-sysfs.c000066400000000000000000000043601324740507400157010ustar00rootroot00000000000000/* * Copyright © 2017 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 library 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 . * * Authors: * Christian J. Kellner */ #include "config.h" #include "bolt-sysfs.h" #include "bolt-error.h" #include "bolt-str.h" #include "bolt-log.h" #include gboolean bolt_sysfs_device_is_domain (struct udev_device *udev) { const char *devtype = udev_device_get_devtype (udev); return bolt_streq (devtype, "thunderbolt_domain"); } struct udev_device * bolt_sysfs_domain_for_device (struct udev_device *udev) { struct udev_device *parent; gboolean found; found = FALSE; parent = udev; do { parent = udev_device_get_parent (parent); if (!parent) break; found = bolt_sysfs_device_is_domain (parent); } while (!found); return found ? parent : NULL; } BoltSecurity bolt_sysfs_security_for_device (struct udev_device *udev, GError **error) { struct udev_device *parent = NULL; const char *v; BoltSecurity s; if (bolt_sysfs_device_is_domain (udev)) parent = udev; else parent = bolt_sysfs_domain_for_device (udev); if (parent == NULL) { g_set_error_literal (error, BOLT_ERROR, BOLT_ERROR_UDEV, "failed to determine domain device"); return BOLT_SECURITY_UNKNOWN; } v = udev_device_get_sysattr_value (parent, "security"); s = bolt_security_from_string (v); if (!bolt_security_validate (s)) { g_set_error (error, BOLT_ERROR, BOLT_ERROR_UDEV, "unknown security level '%s'", v); s = BOLT_SECURITY_UNKNOWN; } return s; } bolt-0.2/boltd/bolt-sysfs.h000066400000000000000000000023021324740507400157000ustar00rootroot00000000000000/* * Copyright © 2017 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 library 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 . * * Authors: * Christian J. Kellner */ #pragma once #include "bolt-enums.h" #include struct udev; struct udev_device; G_BEGIN_DECLS gboolean bolt_sysfs_device_is_domain (struct udev_device *udev); struct udev_device * bolt_sysfs_domain_for_device (struct udev_device *udev); BoltSecurity bolt_sysfs_security_for_device (struct udev_device *udev, GError **error); G_END_DECLS bolt-0.2/cli/000077500000000000000000000000001324740507400130705ustar00rootroot00000000000000bolt-0.2/cli/bolt-client.c000066400000000000000000000334301324740507400154530ustar00rootroot00000000000000/* * Copyright © 2017 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 library 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 . * * Authors: * Christian J. Kellner */ #include "bolt-client.h" #include "bolt-device.h" #include "bolt-error.h" #include "bolt-names.h" #include static void handle_dbus_device_added (GObject *self, GDBusProxy *bus_proxy, GVariant *params); static void handle_dbus_device_removed (GObject *self, GDBusProxy *bus_proxy, GVariant *params); struct _BoltClient { BoltProxy parent; }; enum { PROP_0, /* D-Bus Props */ PROP_VERSION, PROP_PROBING, PROP_SECURITY, PROP_LAST }; static GParamSpec *props[PROP_LAST] = {NULL, }; enum { SIGNAL_DEVICE_ADDED, SIGNAL_DEVICE_REMOVED, SIGNAL_LAST }; static guint signals[SIGNAL_LAST] = {0}; G_DEFINE_TYPE (BoltClient, bolt_client, BOLT_TYPE_PROXY); static void bolt_client_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { if (bolt_proxy_get_dbus_property (object, pspec, value)) return; } static const BoltProxySignal * bolt_client_get_dbus_signals (guint *n) { static BoltProxySignal dbus_signals[] = { {"DeviceAdded", handle_dbus_device_added}, {"DeviceRemoved", handle_dbus_device_removed}, }; *n = G_N_ELEMENTS (dbus_signals); return dbus_signals; } static void bolt_client_class_init (BoltClientClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); BoltProxyClass *proxy_class = BOLT_PROXY_CLASS (klass); gobject_class->get_property = bolt_client_get_property; proxy_class->get_dbus_signals = bolt_client_get_dbus_signals; props[PROP_VERSION] = g_param_spec_uint ("version", "Version", NULL, 0, G_MAXUINT, 0, G_PARAM_READABLE | G_PARAM_STATIC_NAME); props[PROP_PROBING] = g_param_spec_boolean ("probing", "Probing", NULL, FALSE, G_PARAM_READABLE | G_PARAM_STATIC_NAME); props[PROP_SECURITY] = g_param_spec_enum ("security-level", "SecurityLevel", NULL, BOLT_TYPE_SECURITY, BOLT_SECURITY_UNKNOWN, G_PARAM_READABLE | G_PARAM_STATIC_NAME); g_object_class_install_properties (gobject_class, PROP_LAST, props); /* signals */ signals[SIGNAL_DEVICE_ADDED] = g_signal_new ("device-added", G_TYPE_FROM_CLASS (gobject_class), G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 1, BOLT_TYPE_DEVICE); signals[SIGNAL_DEVICE_REMOVED] = g_signal_new ("device-removed", G_TYPE_FROM_CLASS (gobject_class), G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 1, G_TYPE_STRING); } static void bolt_client_init (BoltClient *cli) { } static gint device_sort (gconstpointer ap, gconstpointer bp) { BoltDevice *a = BOLT_DEVICE (*((BoltDevice **) ap)); BoltDevice *b = BOLT_DEVICE (*((BoltDevice **) bp)); g_autofree char *pa = NULL; g_autofree char *pb = NULL; g_object_get (a, "syspath", &pa, NULL); g_object_get (b, "syspath", &pb, NULL); return g_strcmp0 (pa, pb); } /* dbus signals */ static void handle_dbus_device_added (GObject *self, GDBusProxy *bus_proxy, GVariant *params) { g_autoptr(GError) error = NULL; BoltClient *cli = BOLT_CLIENT (self); GDBusConnection *bus; const char *opath = NULL; BoltDevice *dev; bus = g_dbus_proxy_get_connection (bus_proxy); g_variant_get_child (params, 0, "&o", &opath); dev = bolt_device_new_for_object_path (bus, opath, &error); if (!dev) { g_warning ("Could not construct device: %s", error->message); return; } g_signal_emit (cli, signals[SIGNAL_DEVICE_ADDED], 0, dev); g_object_unref (dev); } static void handle_dbus_device_removed (GObject *self, GDBusProxy *bus_proxy, GVariant *params) { BoltClient *cli = BOLT_CLIENT (self); const char *opath = NULL; g_variant_get_child (params, 0, "&o", &opath); g_signal_emit (cli, signals[SIGNAL_DEVICE_REMOVED], 0, opath); } /* public methods */ BoltClient * bolt_client_new (GError **error) { BoltClient *cli; GDBusConnection *bus; bus = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, error); if (bus == NULL) { g_prefix_error (error, "Error connecting to D-Bus: "); return FALSE; } cli = g_initable_new (BOLT_TYPE_CLIENT, NULL, error, "g-flags", G_DBUS_PROXY_FLAGS_NONE, "g-connection", bus, "g-name", BOLT_DBUS_NAME, "g-object-path", BOLT_DBUS_PATH, "g-interface-name", BOLT_DBUS_INTERFACE, NULL); g_object_unref (bus); return cli; } static void got_the_client (GObject *source, GAsyncResult *res, gpointer user_data) { g_autoptr(GError) error = NULL; GTask *task = user_data; GObject *obj; obj = g_async_initable_new_finish (G_ASYNC_INITABLE (source), res, &error); if (obj == NULL) { g_task_return_error (task, error); return; } g_task_return_pointer (task, obj, g_object_unref); g_object_unref (task); } static void got_the_bus (GObject *source, GAsyncResult *res, gpointer user_data) { g_autoptr(GError) error = NULL; GTask *task = user_data; GCancellable *cancellable; GDBusConnection *bus; bus = g_bus_get_finish (res, &error); if (bus == NULL) { g_prefix_error (&error, "could not connect to D-Bus: "); g_task_return_error (task, error); return; } cancellable = g_task_get_cancellable (task); g_async_initable_new_async (BOLT_TYPE_CLIENT, G_PRIORITY_DEFAULT, cancellable, got_the_client, task, "g-flags", G_DBUS_PROXY_FLAGS_NONE, "g-connection", bus, "g-name", BOLT_DBUS_NAME, "g-object-path", BOLT_DBUS_PATH, "g-interface-name", BOLT_DBUS_INTERFACE, NULL); g_object_unref (bus); } void bolt_client_new_async (GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; task = g_task_new (NULL, cancellable, callback, user_data); g_bus_get (G_BUS_TYPE_SYSTEM, cancellable, got_the_bus, task); } BoltClient * bolt_client_new_finish (GAsyncResult *res, GError **error) { g_return_val_if_fail (G_IS_TASK (res), NULL); return g_task_propagate_pointer (G_TASK (res), error); } GPtrArray * bolt_client_list_devices (BoltClient *client, GError **error) { g_autoptr(GVariant) val = NULL; g_autoptr(GPtrArray) devices = NULL; g_autoptr(GVariantIter) iter = NULL; GDBusConnection *bus = NULL; const char *d; g_return_val_if_fail (BOLT_IS_CLIENT (client), NULL); val = g_dbus_proxy_call_sync (G_DBUS_PROXY (client), "ListDevices", NULL, G_DBUS_CALL_FLAGS_NONE, -1, NULL, error); if (val == NULL) return NULL; bus = g_dbus_proxy_get_connection (G_DBUS_PROXY (client)); devices = g_ptr_array_new_with_free_func (g_object_unref); g_variant_get (val, "(ao)", &iter); while (g_variant_iter_loop (iter, "&o", &d, NULL)) { BoltDevice *dev; dev = bolt_device_new_for_object_path (bus, d, error); if (dev == NULL) return NULL; g_ptr_array_add (devices, dev); } g_ptr_array_sort (devices, device_sort); return g_steal_pointer (&devices); } BoltDevice * bolt_client_get_device (BoltClient *client, const char *uid, GError **error) { g_autoptr(GVariant) val = NULL; g_autoptr(GError) err = NULL; BoltDevice *dev = NULL; GDBusConnection *bus = NULL; const char *opath = NULL; g_return_val_if_fail (BOLT_IS_CLIENT (client), NULL); val = g_dbus_proxy_call_sync (G_DBUS_PROXY (client), "DeviceByUid", g_variant_new ("(s)", uid), G_DBUS_CALL_FLAGS_NONE, -1, NULL, &err); if (val == NULL) { if (g_dbus_error_is_remote_error (err)) g_dbus_error_strip_remote_error (err); g_propagate_error (error, g_steal_pointer (&err)); return NULL; } bus = g_dbus_proxy_get_connection (G_DBUS_PROXY (client)); g_variant_get (val, "(&o)", &opath); if (opath == NULL) return NULL; dev = bolt_device_new_for_object_path (bus, opath, error); return dev; } BoltDevice * bolt_client_enroll_device (BoltClient *client, const char *uid, BoltPolicy policy, BoltAuthFlags flags, GError **error) { g_autoptr(GVariant) val = NULL; g_autoptr(GError) err = NULL; g_autofree char *fstr = NULL; BoltDevice *dev = NULL; GDBusConnection *bus = NULL; GVariant *params = NULL; const char *opath = NULL; const char *pstr; g_return_val_if_fail (BOLT_IS_CLIENT (client), NULL); pstr = bolt_enum_to_string (BOLT_TYPE_POLICY, policy, error); if (pstr == NULL) return NULL; fstr = bolt_flags_to_string (BOLT_TYPE_AUTH_FLAGS, flags, error); if (fstr == NULL) return NULL; params = g_variant_new ("(sss)", uid, pstr, fstr); val = g_dbus_proxy_call_sync (G_DBUS_PROXY (client), "EnrollDevice", params, G_DBUS_CALL_FLAGS_NONE, -1, NULL, &err); if (val == NULL) { if (g_dbus_error_is_remote_error (err)) g_dbus_error_strip_remote_error (err); g_propagate_error (error, g_steal_pointer (&err)); return NULL; } bus = g_dbus_proxy_get_connection (G_DBUS_PROXY (client)); g_variant_get (val, "(&o)", &opath); if (opath == NULL) return NULL; dev = bolt_device_new_for_object_path (bus, opath, error); return dev; } gboolean bolt_client_forget_device (BoltClient *client, const char *uid, GError **error) { g_autoptr(GVariant) val = NULL; g_autoptr(GError) err = NULL; g_return_val_if_fail (BOLT_IS_CLIENT (client), FALSE); val = g_dbus_proxy_call_sync (G_DBUS_PROXY (client), "ForgetDevice", g_variant_new ("(s)", uid), G_DBUS_CALL_FLAGS_NONE, -1, NULL, &err); if (err != NULL) { if (g_dbus_error_is_remote_error (err)) g_dbus_error_strip_remote_error (err); g_propagate_error (error, g_steal_pointer (&err)); return FALSE; } return TRUE; } void bolt_client_forget_device_async (BoltClient *client, const char *uid, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { g_return_if_fail (BOLT_IS_CLIENT (client)); g_dbus_proxy_call (G_DBUS_PROXY (client), "ForgetDevice", g_variant_new ("(s)", uid), G_DBUS_CALL_FLAGS_NONE, -1, cancellable, callback, user_data); } gboolean bolt_client_forget_device_finish (BoltClient *client, GAsyncResult *res, GError **error) { g_autoptr(GVariant) val = NULL; g_autoptr(GError) err = NULL; g_return_val_if_fail (BOLT_IS_CLIENT (client), FALSE); val = g_dbus_proxy_call_finish (G_DBUS_PROXY (client), res, &err); if (err != NULL) { if (g_dbus_error_is_remote_error (err)) g_dbus_error_strip_remote_error (err); g_propagate_error (error, g_steal_pointer (&err)); return FALSE; } return TRUE; } bolt-0.2/cli/bolt-client.h000066400000000000000000000054041324740507400154600ustar00rootroot00000000000000/* * Copyright © 2017 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 library 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 . * * Authors: * Christian J. Kellner */ #pragma once #include "bolt-enums.h" #include "bolt-device.h" #include "bolt-proxy.h" G_BEGIN_DECLS #define BOLT_TYPE_CLIENT bolt_client_get_type () G_DECLARE_FINAL_TYPE (BoltClient, bolt_client, BOLT, CLIENT, BoltProxy); BoltClient * bolt_client_new (GError **error); void bolt_client_new_async (GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); BoltClient * bolt_client_new_finish (GAsyncResult *res, GError **error); GPtrArray * bolt_client_list_devices (BoltClient *client, GError **error); BoltDevice * bolt_client_get_device (BoltClient *client, const char *uid, GError **error); BoltDevice * bolt_client_enroll_device (BoltClient *client, const char *uid, BoltPolicy policy, BoltAuthFlags flags, GError **error); gboolean bolt_client_forget_device (BoltClient *client, const char *uid, GError **error); void bolt_client_forget_device_async (BoltClient *client, const char *uid, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); gboolean bolt_client_forget_device_finish (BoltClient *client, GAsyncResult *res, GError **error); G_END_DECLS bolt-0.2/cli/bolt-device.c000066400000000000000000000153761324740507400154450ustar00rootroot00000000000000/* * Copyright © 2017 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 library 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 . * * Authors: * Christian J. Kellner */ #include "config.h" #include "bolt-device.h" #include "bolt-enums.h" #include "bolt-error.h" #include "bolt-names.h" #include struct _BoltDevice { BoltProxy parent; }; enum { PROP_0, /* D-Bus Props */ PROP_UID, PROP_NAME, PROP_VENDOR, PROP_TYPE, PROP_STATUS, PROP_PARENT, PROP_SYSPATH, PROP_CONNTIME, PROP_AUTHTIME, PROP_STORED, PROP_POLICY, PROP_KEY, PROP_STORETIME, PROP_LABEL, PROP_LAST }; static GParamSpec *props[PROP_LAST] = {NULL, }; G_DEFINE_TYPE (BoltDevice, bolt_device, BOLT_TYPE_PROXY); static void bolt_device_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { if (bolt_proxy_get_dbus_property (object, pspec, value)) return; } static void bolt_device_class_init (BoltDeviceClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); gobject_class->get_property = bolt_device_get_property; props[PROP_UID] = g_param_spec_string ("uid", "Uid", NULL, "unknown", G_PARAM_READABLE | G_PARAM_STATIC_NICK); props[PROP_NAME] = g_param_spec_string ("name", "Name", NULL, "unknown", G_PARAM_READABLE | G_PARAM_STATIC_NICK); props[PROP_VENDOR] = g_param_spec_string ("vendor", "Vendor", NULL, "unknown", G_PARAM_READABLE | G_PARAM_STATIC_NICK); props[PROP_TYPE] = g_param_spec_enum ("type", "Type", NULL, BOLT_TYPE_DEVICE_TYPE, BOLT_DEVICE_PERIPHERAL, G_PARAM_READABLE | G_PARAM_STATIC_NICK); props[PROP_STATUS] = g_param_spec_enum ("status", "Status", NULL, BOLT_TYPE_STATUS, BOLT_STATUS_DISCONNECTED, G_PARAM_READABLE | G_PARAM_STATIC_NICK); props[PROP_PARENT] = g_param_spec_string ("parent", "Parent", NULL, "unknown", G_PARAM_READABLE | G_PARAM_STATIC_NICK); props[PROP_SYSPATH] = g_param_spec_string ("syspath", "SysfsPath", NULL, "unknown", G_PARAM_READABLE | G_PARAM_STATIC_NICK); props[PROP_CONNTIME] = g_param_spec_uint64 ("conntime", "ConnectTime", NULL, 0, G_MAXUINT64, 0, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); props[PROP_AUTHTIME] = g_param_spec_uint64 ("authtime", "AuthorizeTime", NULL, 0, G_MAXUINT64, 0, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); props[PROP_STORED] = g_param_spec_boolean ("stored", "Stored", NULL, FALSE, G_PARAM_READABLE | G_PARAM_STATIC_NICK); props[PROP_POLICY] = g_param_spec_enum ("policy", "Policy", NULL, BOLT_TYPE_POLICY, BOLT_POLICY_DEFAULT, G_PARAM_READABLE | G_PARAM_STATIC_NICK); props[PROP_KEY] = g_param_spec_enum ("key", "Key", NULL, BOLT_TYPE_KEY_STATE, BOLT_KEY_MISSING, G_PARAM_READABLE | G_PARAM_STATIC_NICK); props[PROP_STORETIME] = g_param_spec_uint64 ("storetime", "StoreTime", NULL, 0, G_MAXUINT64, 0, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); props[PROP_LABEL] = g_param_spec_string ("label", "Label", NULL, NULL, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); g_object_class_install_properties (gobject_class, PROP_LAST, props); } static void bolt_device_init (BoltDevice *mgr) { } /* public methods */ BoltDevice * bolt_device_new_for_object_path (GDBusConnection *bus, const char *path, GError **error) { BoltDevice *dev; dev = g_initable_new (BOLT_TYPE_DEVICE, NULL, error, "g-flags", G_DBUS_PROXY_FLAGS_NONE, "g-connection", bus, "g-name", BOLT_DBUS_NAME, "g-object-path", path, "g-interface-name", BOLT_DBUS_DEVICE_INTERFACE, NULL); return dev; } gboolean bolt_device_authorize (BoltDevice *dev, BoltAuthFlags flags, GError **error) { g_autoptr(GError) err = NULL; g_autofree char *fstr = NULL; g_return_val_if_fail (BOLT_IS_DEVICE (dev), FALSE); fstr = bolt_flags_to_string (BOLT_TYPE_AUTH_FLAGS, flags, error); if (fstr == NULL) return FALSE; g_dbus_proxy_call_sync (G_DBUS_PROXY (dev), "Authorize", g_variant_new ("(s)", fstr), G_DBUS_CALL_FLAGS_NONE, -1, NULL, &err); if (err != NULL) { if (g_dbus_error_is_remote_error (err)) g_dbus_error_strip_remote_error (err); g_propagate_error (error, g_steal_pointer (&err)); return FALSE; } return TRUE; } bolt-0.2/cli/bolt-device.h000066400000000000000000000025451324740507400154440ustar00rootroot00000000000000/* * Copyright © 2017 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 library 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 . * * Authors: * Christian J. Kellner */ #pragma once #include "bolt-enums.h" #include "bolt-proxy.h" G_BEGIN_DECLS #define BOLT_TYPE_DEVICE bolt_device_get_type () G_DECLARE_FINAL_TYPE (BoltDevice, bolt_device, BOLT, DEVICE, BoltProxy); BoltDevice * bolt_device_new_for_object_path (GDBusConnection *bus, const char *path, GError **error); gboolean bolt_device_authorize (BoltDevice *dev, BoltAuthFlags flags, GError **error); G_END_DECLS bolt-0.2/cli/bolt-proxy.c000066400000000000000000000202231324740507400153520ustar00rootroot00000000000000/* * Copyright © 2017 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 library 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 . * * Authors: * Christian J. Kellner */ #include "bolt-proxy.h" #include "bolt-error.h" #include "bolt-names.h" #include "bolt-str.h" static void bolt_proxy_handle_props_changed (GDBusProxy *proxy, GVariant *changed_properties, GStrv invalidated_properties, gpointer user_data); static void bolt_proxy_handle_dbus_signal (GDBusProxy *proxy, const gchar *sender_name, const gchar *signal_name, GVariant *params, gpointer user_data); enum { PROP_0, PROP_BUS, /* d-bus names */ PROP_OBJECT_PATH, PROP_IFACE_NAME, PROP_LAST }; G_DEFINE_TYPE (BoltProxy, bolt_proxy, G_TYPE_DBUS_PROXY); static void bolt_proxy_constructed (GObject *object) { G_OBJECT_CLASS (bolt_proxy_parent_class)->constructed (object); g_signal_connect (object, "g-properties-changed", G_CALLBACK (bolt_proxy_handle_props_changed), object); g_signal_connect (object, "g-signal", G_CALLBACK (bolt_proxy_handle_dbus_signal), object); } static const BoltProxySignal * bolt_proxy_get_dbus_signals (guint *n) { *n = 0; return NULL; } static void bolt_proxy_class_init (BoltProxyClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); gobject_class->constructed = bolt_proxy_constructed; klass->get_dbus_signals = bolt_proxy_get_dbus_signals; } static void bolt_proxy_init (BoltProxy *object) { } static void bolt_proxy_handle_props_changed (GDBusProxy *proxy, GVariant *changed_properties, GStrv invalidated_properties, gpointer user_data) { g_autoptr(GVariantIter) iter = NULL; gboolean handled = FALSE; GParamSpec **pp; const char *key; guint n; pp = g_object_class_list_properties (G_OBJECT_GET_CLASS (proxy), &n); g_variant_get (changed_properties, "a{sv}", &iter); while (g_variant_iter_next (iter, "{&sv}", &key, NULL)) { for (guint i = 0; !handled && i < n; i++) { GParamSpec *pspec = pp[i]; const char *nick; const char *name; nick = g_param_spec_get_nick (pspec); name = g_param_spec_get_name (pspec); handled = bolt_streq (nick, key); if (handled) g_object_notify (G_OBJECT (user_data), name); } } g_free (pp); } static void bolt_proxy_handle_dbus_signal (GDBusProxy *proxy, const gchar *sender_name, const gchar *signal_name, GVariant *params, gpointer user_data) { const BoltProxySignal *ps; guint n; if (signal_name == NULL) return; ps = BOLT_PROXY_GET_CLASS (proxy)->get_dbus_signals (&n); for (guint i = 0; i < n; i++) { const BoltProxySignal *sig = &ps[i]; if (g_str_equal (sig->theirs, signal_name)) { sig->handle (G_OBJECT (proxy), proxy, params); break; } } } /* public methods */ gboolean bolt_proxy_get_dbus_property (GObject *proxy, GParamSpec *spec, GValue *value) { g_autoptr(GVariant) val = NULL; const GVariantType *vt; gboolean handled = FALSE; const char *nick; nick = g_param_spec_get_nick (spec); val = g_dbus_proxy_get_cached_property (G_DBUS_PROXY (proxy), nick); if (val == NULL) return FALSE; vt = g_variant_get_type (val); if (g_variant_type_equal (vt, G_VARIANT_TYPE_STRING) && G_IS_PARAM_SPEC_ENUM (spec)) { GParamSpecEnum *enum_spec = G_PARAM_SPEC_ENUM (spec); GEnumValue *ev; const char *str; str = g_variant_get_string (val, NULL); ev = g_enum_get_value_by_nick (enum_spec->enum_class, str); handled = ev != NULL; if (handled) g_value_set_enum (value, ev->value); else g_value_set_enum (value, enum_spec->default_value); } else { g_dbus_gvariant_to_gvalue (val, value); } return handled; } const char * bolt_proxy_get_object_path (BoltProxy *proxy) { return g_dbus_proxy_get_object_path (G_DBUS_PROXY (proxy)); } static GParamSpec * find_property (BoltProxy *proxy, const char *name, GError **error) { GParamSpec *res = NULL; GParamSpec **pp; guint n; pp = g_object_class_list_properties (G_OBJECT_GET_CLASS (proxy), &n); for (guint i = 0; i < n; i++) { GParamSpec *pspec = pp[i]; if (bolt_streq (pspec->name, name)) { res = pspec; break; } } if (pp == NULL) g_set_error (error, G_DBUS_ERROR, G_DBUS_ERROR_UNKNOWN_PROPERTY, "could not find property '%s'", name); g_free (pp); return res; } gboolean bolt_proxy_set_property (BoltProxy *proxy, const char *name, GVariant *value, GCancellable *cancellable, GError **error) { GParamSpec *pp; const char *iface; gboolean ok = FALSE; GVariant *res; pp = find_property (proxy, name, NULL); if (pp != NULL) name = g_param_spec_get_nick (pp); iface = g_dbus_proxy_get_interface_name (G_DBUS_PROXY (proxy)); res = g_dbus_proxy_call_sync (G_DBUS_PROXY (proxy), "org.freedesktop.DBus.Properties.Set", g_variant_new ("(ssv)", iface, name, value), G_DBUS_CALL_FLAGS_NONE, -1, cancellable, error); if (res) { g_variant_unref (res); ok = TRUE; } return ok; } void bolt_proxy_set_property_async (BoltProxy *proxy, const char *name, GVariant *value, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { GParamSpec *pp; const char *iface; pp = find_property (proxy, name, NULL); if (pp != NULL) name = g_param_spec_get_nick (pp); iface = g_dbus_proxy_get_interface_name (G_DBUS_PROXY (proxy)); g_dbus_proxy_call (G_DBUS_PROXY (proxy), "org.freedesktop.DBus.Properties.Set", g_variant_new ("(ssv)", iface, name, value), G_DBUS_CALL_FLAGS_NONE, -1, cancellable, callback, user_data); } gboolean bolt_proxy_set_property_finish (GAsyncResult *res, GError **error) { BoltProxy *proxy; GVariant *val = NULL; proxy = (BoltProxy *) g_async_result_get_source_object (res); val = g_dbus_proxy_call_finish (G_DBUS_PROXY (proxy), res, error); if (val == NULL) return FALSE; g_variant_unref (val); return TRUE; } bolt-0.2/cli/bolt-proxy.h000066400000000000000000000046761324740507400153750ustar00rootroot00000000000000/* * Copyright © 2017 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 library 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 . * * Authors: * Christian J. Kellner */ #pragma once #include G_BEGIN_DECLS typedef struct BoltProxySignal { const char *theirs; void (*handle)(GObject *self, GDBusProxy *bus_proxy, GVariant *params); } BoltProxySignal; #define BOLT_TYPE_PROXY (bolt_proxy_get_type ()) G_DECLARE_DERIVABLE_TYPE (BoltProxy, bolt_proxy, BOLT, PROXY, GDBusProxy) struct _BoltProxyClass { GDBusProxyClass parent; /* virtuals */ const BoltProxySignal * (*get_dbus_signals) (guint *n); }; gboolean bolt_proxy_get_dbus_property (GObject *proxy, GParamSpec *spec, GValue *value); const char * bolt_proxy_get_object_path (BoltProxy *proxy); gboolean bolt_proxy_set_property (BoltProxy *proxy, const char *name, GVariant *value, GCancellable *cancellable, GError **error); void bolt_proxy_set_property_async (BoltProxy *proxy, const char *name, GVariant *value, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); gboolean bolt_proxy_set_property_finish (GAsyncResult *res, GError **error); G_END_DECLS bolt-0.2/cli/boltctl.c000066400000000000000000000423211324740507400147010ustar00rootroot00000000000000/* * Copyright © 2017 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 library 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 . * * Authors: * Christian J. Kellner */ #include "config.h" #include "bolt-client.h" #include "bolt-enums.h" #include "bolt-error.h" #include "bolt-str.h" #include "bolt-term.h" #include "bolt-time.h" #include #include #include static int usage_error (GError *error) { g_printerr ("%s:", g_get_application_name ()); g_printerr ("%s error: %s", bolt_color (ANSI_RED), bolt_color (ANSI_NORMAL)); g_printerr ("%s", error->message); g_printerr ("\n"); g_printerr ("Try \"%s --help\" for more information.", g_get_prgname ()); g_printerr ("\n"); return EXIT_FAILURE; } static int usage_error_need_arg (const char *arg) { g_autoptr(GError) error = NULL; g_set_error (&error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, "missing argument '%s'", arg); return usage_error (error); } static void print_device (BoltDevice *dev, gboolean verbose) { g_autofree char *path = NULL; g_autofree char *uid = NULL; g_autofree char *name = NULL; g_autofree char *vendor = NULL; g_autofree char *syspath = NULL; g_autofree char *parent = NULL; g_autofree char *label = NULL; BoltDeviceType type; BoltStatus status; BoltKeyState keystate; BoltPolicy policy; const char *status_color; const char *status_symbol; const char *type_text; const char *status_text; const char *tree_branch; const char *tree_right; const char *tree_space; gboolean stored; guint64 ct, at, st; g_object_get (dev, "g-object-path", &path, "name", &name, "vendor", &vendor, "type", &type, "status", &status, "uid", &uid, "parent", &parent, "syspath", &syspath, "conntime", &ct, "authtime", &at, "stored", &stored, "policy", &policy, "key", &keystate, "storetime", &st, "label", &label, NULL); status_symbol = bolt_glyph (BLACK_CIRCLE); tree_branch = bolt_glyph (TREE_BRANCH); tree_right = bolt_glyph (TREE_RIGHT); tree_space = bolt_glyph (TREE_SPACE); switch (status) { case BOLT_STATUS_DISCONNECTED: status_symbol = bolt_glyph (WHITE_CIRCLE); status_color = bolt_color (ANSI_NORMAL); status_text = "disconnected"; break; case BOLT_STATUS_CONNECTED: status_color = bolt_color (ANSI_YELLOW); status_text = "connected"; break; case BOLT_STATUS_AUTHORIZED: case BOLT_STATUS_AUTHORIZED_NEWKEY: case BOLT_STATUS_AUTHORIZED_SECURE: status_color = bolt_color (ANSI_GREEN); status_text = "authorized"; break; case BOLT_STATUS_AUTH_ERROR: status_color = bolt_color (ANSI_RED); status_text = "authorization error"; break; case BOLT_STATUS_AUTHORIZED_DPONLY: status_color = bolt_color (ANSI_BLUE); status_text = "connected (no thunderbolt)"; break; default: status_color = bolt_color (ANSI_NORMAL); status_text = "unknown"; break; } label = bolt_strstrip (label); g_print (" %s%s%s %s\n", status_color, status_symbol, bolt_color (ANSI_NORMAL), label ? : name); type_text = bolt_device_type_to_string (type); if (label) g_print (" %s name: %s\n", tree_branch, name); g_print (" %s type: %s\n", tree_branch, type_text); g_print (" %s vendor: %s\n", tree_branch, vendor); g_print (" %s uuid: %s\n", tree_branch, uid); if (verbose) g_print (" %s dbus path: %s\n", tree_branch, path); g_print (" %s status: %s\n", tree_branch, status_text); if (bolt_status_is_connected (status)) { g_autofree char *ctstr = NULL; ctstr = bolt_epoch_format (ct, "%c"); if (verbose) { g_print (" %s %s parent: %s\n", bolt_glyph (TREE_VERTICAL), tree_branch, parent); g_print (" %s %s syspath: %s\n", bolt_glyph (TREE_VERTICAL), tree_branch, syspath); } if (bolt_status_is_authorized (status)) { g_autofree char *atstr = NULL; atstr = bolt_epoch_format (at, "%c"); g_print (" %s %s authorized: %s\n", bolt_glyph (TREE_VERTICAL), tree_branch, atstr); } g_print (" %s %s connected: %s\n", bolt_glyph (TREE_VERTICAL), tree_right, ctstr); } g_print (" %s stored: %s\n", tree_right, bolt_yesno (stored)); if (stored) { g_autofree char *etstr = NULL; const char *pstr = bolt_policy_to_string (policy); const char *kstr; if (keystate == BOLT_KEY_MISSING) kstr = "no"; else if (keystate == BOLT_KEY_HAVE) kstr = "yes"; else if (keystate == BOLT_KEY_NEW) kstr = "yes (new)"; else kstr = "unknown"; etstr = bolt_epoch_format (st, "%c"); g_print (" %s %s when: %s\n", tree_space, tree_branch, etstr); g_print (" %s %s policy: %s\n", tree_space, tree_branch, pstr); g_print (" %s %s key: %s\n", tree_space, tree_right, kstr); } g_print ("\n"); } static int authorize (BoltClient *client, int argc, char **argv) { g_autoptr(GOptionContext) optctx = NULL; g_autoptr(BoltDevice) dev = NULL; g_autoptr(GError) error = NULL; BoltAuthFlags flags = BOLT_AUTH_NONE; const char *uid; gboolean ok; optctx = g_option_context_new ("DEVICE - Authorize a device"); if (!g_option_context_parse (optctx, &argc, &argv, &error)) return usage_error (error); if (argc < 2) return usage_error_need_arg ("DEVICE"); uid = argv[1]; dev = bolt_client_get_device (client, uid, &error); if (dev == NULL) { g_printerr ("%s\n", error->message); return EXIT_FAILURE; } ok = bolt_device_authorize (dev, flags, &error); if (!ok) g_printerr ("Authorization error: %s\n", error->message); return ok ? EXIT_SUCCESS : EXIT_FAILURE; } static int enroll (BoltClient *client, int argc, char **argv) { g_autoptr(GOptionContext) optctx = NULL; g_autoptr(BoltDevice) dev = NULL; g_autoptr(GError) error = NULL; const char *uid; BoltPolicy policy = BOLT_POLICY_DEFAULT; BoltAuthFlags flags = BOLT_AUTH_NONE; const char *policy_arg = "default"; GOptionEntry options[] = { { "policy", 0, 0, G_OPTION_ARG_STRING, &policy_arg, "Policy for the device; one of {auto, manual, *default}", "POLICY" }, { NULL } }; optctx = g_option_context_new ("DEVICE - Authorize and store a device in the database"); g_option_context_add_main_entries (optctx, options, NULL); if (!g_option_context_parse (optctx, &argc, &argv, &error)) return usage_error (error); if (argc < 2) return usage_error_need_arg ("DEVICE"); policy = bolt_policy_from_string (policy_arg); if (!bolt_policy_validate (policy)) { g_set_error (&error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, "invalid policy '%s'", policy_arg); return usage_error (error); } uid = argv[1]; dev = bolt_client_enroll_device (client, uid, policy, flags, &error); if (dev == NULL) { g_printerr ("%s\n", error->message); return EXIT_FAILURE; } print_device (dev, TRUE); return EXIT_SUCCESS; } static int forget (BoltClient *client, int argc, char **argv) { g_autoptr(GOptionContext) optctx = NULL; g_autoptr(GError) error = NULL; const char *uid; gboolean ok; optctx = g_option_context_new ("DEVICE - Remove a device form the store"); if (!g_option_context_parse (optctx, &argc, &argv, &error)) return usage_error (error); if (argc < 2) return usage_error_need_arg ("DEVICE"); uid = argv[1]; ok = bolt_client_forget_device (client, uid, &error); if (!ok) g_printerr ("Failed to forget device: %s\n", error->message); return ok ? EXIT_SUCCESS : EXIT_FAILURE; } static int info (BoltClient *client, int argc, char **argv) { g_autoptr(GOptionContext) optctx = NULL; g_autoptr(BoltDevice) dev = NULL; g_autoptr(GError) error = NULL; const char *uid; optctx = g_option_context_new ("DEVICE - Show information about a device"); if (!g_option_context_parse (optctx, &argc, &argv, &error)) return usage_error (error); if (argc < 2) return usage_error_need_arg ("DEVICE"); uid = argv[1]; dev = bolt_client_get_device (client, uid, &error); if (dev == NULL) { g_printerr ("%s\n", error->message); return EXIT_FAILURE; } print_device (dev, TRUE); return EXIT_SUCCESS; } static void handle_device_changed (GObject *gobject, GParamSpec *pspec, gpointer user_data) { g_autofree char *uid = NULL; g_autofree char *dev_name = NULL; g_autofree char *val = NULL; BoltDevice *dev = BOLT_DEVICE (gobject); const char *prop_name; GValue prop_val = G_VALUE_INIT; GValue str_val = G_VALUE_INIT; g_object_get (dev, "uid", &uid, "name", &dev_name, NULL); prop_name = g_param_spec_get_name (pspec); g_value_init (&prop_val, G_PARAM_SPEC_VALUE_TYPE (pspec)); g_value_init (&str_val, G_TYPE_STRING); g_object_get_property (G_OBJECT (dev), prop_name, &prop_val); g_print ("[%s] %30s | %10s -> ", uid, dev_name, prop_name); if (g_value_transform (&prop_val, &str_val)) val = g_value_dup_string (&str_val); else val = g_strdup_value_contents (&prop_val); g_print ("%s\n", val); g_value_unset (&str_val); g_value_unset (&prop_val); } static void handle_device_added (BoltClient *cli, BoltDevice *dev, gpointer user_data) { g_autofree char *path = NULL; GPtrArray *devices = user_data; g_object_get (dev, "g-object-path", &path, NULL); g_print (" DeviceAdded: %s\n", path); g_ptr_array_add (devices, g_object_ref (dev)); g_signal_connect (dev, "notify", G_CALLBACK (handle_device_changed), NULL); } static void handle_device_removed (BoltClient *cli, const char *opath, gpointer user_data) { GPtrArray *devices = user_data; BoltDevice *device = NULL; g_print (" DeviceRemoved: %s\n", opath); for (guint i = 0; i < devices->len; i++) { BoltDevice *dev = g_ptr_array_index (devices, i); const char *dev_opath = bolt_proxy_get_object_path (BOLT_PROXY (dev)); if (bolt_streq (opath, dev_opath)) { device = dev; break; } } if (device == NULL) { g_warning ("DeviceRemoved signal for unknown device: %s", opath); return; } g_signal_handlers_block_by_func (device, handle_device_changed, devices); g_ptr_array_remove_fast (devices, device); } static void handle_probing_changed (BoltClient *client, GParamSpec *pspec, gpointer user_data) { gboolean probing; g_object_get (client, "probing", &probing, NULL); if (probing) g_print ("Probing started\n"); else g_print ("Probing done\n"); } static int monitor (BoltClient *client, int argc, char **argv) { g_autoptr(GOptionContext) optctx = NULL; g_autoptr(GError) error = NULL; g_autoptr(GMainLoop) main_loop = NULL; g_autoptr(GPtrArray) devices = NULL; BoltSecurity security; guint version = 0; optctx = g_option_context_new ("- Watch for changes"); if (!g_option_context_parse (optctx, &argc, &argv, &error)) return usage_error (error); g_object_get (client, "version", &version, "security-level", &security, NULL); g_print ("Daemon Version: %d.%u\n", VERSION_MAJOR, version); g_print ("Security Level: %s\n", bolt_security_to_string (security)); g_print ("Ready\n"); devices = bolt_client_list_devices (client, &error); if (devices == NULL) { g_warning ("Could not list devices: %s", error->message); devices = g_ptr_array_new_with_free_func (g_object_unref); } for (guint i = 0; i < devices->len; i++) { BoltDevice *dev = g_ptr_array_index (devices, i); g_signal_connect (dev, "notify", G_CALLBACK (handle_device_changed), NULL); } g_signal_connect (client, "device-added", G_CALLBACK (handle_device_added), devices); g_signal_connect (client, "device-removed", G_CALLBACK (handle_device_removed), devices); g_signal_connect (client, "notify::probing", G_CALLBACK (handle_probing_changed), NULL); main_loop = g_main_loop_new (NULL, FALSE); g_main_loop_run (main_loop); g_signal_handlers_disconnect_by_func (client, G_CALLBACK (handle_probing_changed), NULL); return EXIT_SUCCESS; } static int list_devices (BoltClient *client, int argc, char **argv) { g_autoptr(GOptionContext) optctx = NULL; g_autoptr(GError) error = NULL; g_autoptr(GPtrArray) devices = NULL; gboolean show_all = FALSE; GOptionEntry options[] = { { "all", 'a', 0, G_OPTION_ARG_NONE, &show_all, "Show all devices", NULL }, { NULL } }; optctx = g_option_context_new ("- List thunderbolt devices"); g_option_context_add_main_entries (optctx, options, NULL); if (!g_option_context_parse (optctx, &argc, &argv, &error)) return usage_error (error); devices = bolt_client_list_devices (client, &error); if (devices == NULL) { g_printerr ("Failed to list devices: %s", error->message); return EXIT_FAILURE; } for (guint i = 0; i < devices->len; i++) { BoltDevice *dev = g_ptr_array_index (devices, i); BoltDeviceType type; if (!show_all) { g_object_get (dev, "type", &type, NULL); if (type != BOLT_DEVICE_PERIPHERAL) continue; } print_device (dev, FALSE); } return EXIT_SUCCESS; } /* **** */ typedef int (*run_t)(BoltClient *client, int argc, char **argv); typedef struct SubCommand { const char *name; run_t fn; const char *desc; } SubCommand; static SubCommand subcommands[] = { {"authorize", authorize, "Authorize a device"}, {"enroll", enroll, "Authorize and store a device in the database"}, {"forget", forget, "Remove a stored device from the database"}, {"info", info, "Show information about a device"}, {"list", list_devices, "List connected and stored devices"}, {"monitor", monitor, "Listen and print changes"} }; #define SUMMARY_SPACING 17 static void option_context_make_summary (GOptionContext *ctx) { g_autoptr(GString) s = NULL; s = g_string_new ("Commands:"); for (size_t i = 0; i < G_N_ELEMENTS (subcommands); i++) { const SubCommand *c = &subcommands[i]; int spacing = SUMMARY_SPACING - strlen (c->name); g_string_append_printf (s, "\n %s", c->name); g_string_append_printf (s, "%*s%s", spacing, "", c->desc); } g_option_context_set_summary (ctx, s->str); } int main (int argc, char **argv) { g_autoptr(GOptionContext) optctx = NULL; g_autoptr(BoltClient) client = NULL; g_autoptr(GError) error = NULL; g_autofree char *cmdline = NULL; SubCommand *cmd = NULL; const char *cmdname = NULL; setlocale (LC_ALL, ""); optctx = g_option_context_new ("[COMMAND]"); option_context_make_summary (optctx); g_option_context_set_strict_posix (optctx, TRUE); if (!g_option_context_parse (optctx, &argc, &argv, &error)) return usage_error (error); if (argc < 2) cmdname = "list"; else cmdname = argv[1]; client = bolt_client_new (&error); if (!client) { g_error ("Could not create client: %s", error->message); return EXIT_FAILURE; } for (size_t i = 0; !cmd && i < G_N_ELEMENTS (subcommands); i++) if (g_str_equal (cmdname, subcommands[i].name)) cmd = &subcommands[i]; if (cmd == NULL) { g_set_error (&error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, "Invalid command: %s", cmdname); return usage_error (error); } cmdline = g_strconcat (g_get_prgname (), " ", cmd->name, NULL); g_set_prgname (cmdline); argv[1] = cmdline; argv += 1; argc -= 1; return cmd->fn (client, argc, argv); } bolt-0.2/common/000077500000000000000000000000001324740507400136115ustar00rootroot00000000000000bolt-0.2/common/bolt-enums.c000066400000000000000000000175451324740507400160560ustar00rootroot00000000000000/* * Copyright © 2017 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 library 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 . * * Authors: * Christian J. Kellner */ #include "config.h" #include "bolt-enums.h" #include "bolt-error.h" #include G_DEFINE_AUTOPTR_CLEANUP_FUNC (GEnumClass, g_type_class_unref); G_DEFINE_AUTOPTR_CLEANUP_FUNC (GFlagsClass, g_type_class_unref); gboolean bolt_enum_class_validate (GEnumClass *enum_class, gint value, GError **error) { const char *name; gboolean oob; if (enum_class == NULL) { name = g_type_name_from_class ((GTypeClass *) enum_class); g_set_error (error, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS, "could not determine enum class for '%s'", name); return FALSE; } oob = value < enum_class->minimum || value > enum_class->maximum; if (oob) { name = g_type_name_from_class ((GTypeClass *) enum_class); g_set_error (error, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS, "enum value '%d' is out of bounds for '%s'", value, name); return FALSE; } return TRUE; } gboolean bolt_enum_validate (GType enum_type, gint value, GError **error) { g_autoptr(GEnumClass) klass = g_type_class_ref (enum_type); return bolt_enum_class_validate (klass, value, error); } const char * bolt_enum_to_string (GType enum_type, gint value, GError **error) { g_autoptr(GEnumClass) klass = NULL; GEnumValue *ev; klass = g_type_class_ref (enum_type); if (!bolt_enum_class_validate (klass, value, error)) return NULL; ev = g_enum_get_value (klass, value); return ev->value_nick; } gint bolt_enum_from_string (GType enum_type, const char *string, GError **error) { g_autoptr(GEnumClass) klass = NULL; GEnumValue *ev; klass = g_type_class_ref (enum_type); ev = g_enum_get_value_by_nick (klass, string); if (ev == NULL) { const char *name = g_type_name (enum_type); g_set_error (error, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS, "invalid str '%s' for enum '%s'", string, name); return -1; } return ev->value; } char * bolt_flags_class_to_string (GFlagsClass *flags_class, guint value, GError **error) { g_autoptr(GString) str = NULL; const char *name; GFlagsValue *fv; if (flags_class == NULL) { name = g_type_name_from_class ((GTypeClass *) flags_class); g_set_error (error, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS, "could not determine flags class for '%s'", name); return FALSE; } fv = g_flags_get_first_value (flags_class, value); if (fv == NULL) { if (value == 0) return g_strdup (""); name = g_type_name_from_class ((GTypeClass *) flags_class); g_set_error (error, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS, "invalid value '%u' for flags '%s'", value, name); return NULL; } value &= ~fv->value; str = g_string_new (fv->value_nick); while (value != 0 && (fv = g_flags_get_first_value (flags_class, value)) != NULL) { g_string_append (str, " | "); g_string_append (str, fv->value_nick); value &= ~fv->value; } if (value != 0) { name = g_type_name_from_class ((GTypeClass *) flags_class); g_set_error (error, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS, "unhandled value '%u' for flags '%s'", value, name); return NULL; } return g_string_free (g_steal_pointer (&str), FALSE); } gboolean bolt_flags_class_from_string (GFlagsClass *flags_class, const char *string, guint *flags_out, GError **error) { g_auto(GStrv) vals = NULL; const char *name; guint flags = 0; if (flags_class == NULL) { name = g_type_name_from_class ((GTypeClass *) flags_class); g_set_error (error, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS, "could not determine enum class for '%s'", name); return FALSE; } vals = g_strsplit (string, "|", -1); for (guint i = 0; vals[i]; i++) { GFlagsValue *fv; char *nick; nick = g_strstrip (vals[i]); fv = g_flags_get_value_by_nick (flags_class, nick); if (fv == NULL) { name = g_type_name_from_class ((GTypeClass *) flags_class); g_set_error (error, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS, "invalid flag '%s' for flags '%s'", string, name); return FALSE; } flags |= fv->value; } if (flags_out != NULL) *flags_out = flags; return TRUE; } char * bolt_flags_to_string (GType flags_type, guint value, GError **error) { g_autoptr(GFlagsClass) klass = NULL; klass = g_type_class_ref (flags_type); return bolt_flags_class_to_string (klass, value, error); } gboolean bolt_flags_from_string (GType flags_type, const char *string, guint *flags_out, GError **error) { g_autoptr(GFlagsClass) klass = NULL; klass = g_type_class_ref (flags_type); return bolt_flags_class_from_string (klass, string, flags_out, error); } const char * bolt_status_to_string (BoltStatus status) { return bolt_enum_to_string (BOLT_TYPE_STATUS, status, NULL); } gboolean bolt_status_is_authorized (BoltStatus status) { return status == BOLT_STATUS_AUTHORIZED || status == BOLT_STATUS_AUTHORIZED_SECURE || status == BOLT_STATUS_AUTHORIZED_NEWKEY; } gboolean bolt_status_validate (BoltStatus status) { return bolt_enum_validate (BOLT_TYPE_STATUS, status, NULL); } gboolean bolt_status_is_connected (BoltStatus status) { return status > BOLT_STATUS_DISCONNECTED; } BoltSecurity bolt_security_from_string (const char *str) { return bolt_enum_from_string (BOLT_TYPE_SECURITY, str, NULL); } const char * bolt_security_to_string (BoltSecurity security) { return bolt_enum_to_string (BOLT_TYPE_SECURITY, security, NULL); } gboolean bolt_security_validate (BoltSecurity security) { return bolt_enum_validate (BOLT_TYPE_SECURITY, security, NULL); } BoltPolicy bolt_policy_from_string (const char *str) { return bolt_enum_from_string (BOLT_TYPE_POLICY, str, NULL); } const char * bolt_policy_to_string (BoltPolicy policy) { return bolt_enum_to_string (BOLT_TYPE_POLICY, policy, NULL); } gboolean bolt_policy_validate (BoltPolicy policy) { return bolt_enum_validate (BOLT_TYPE_POLICY, policy, NULL); } BoltDeviceType bolt_device_type_from_string (const char *str) { return bolt_enum_from_string (BOLT_TYPE_DEVICE_TYPE, str, NULL); } const char * bolt_device_type_to_string (BoltDeviceType type) { return bolt_enum_to_string (BOLT_TYPE_DEVICE_TYPE, type, NULL); } gboolean bolt_device_type_validate (BoltDeviceType type) { return bolt_enum_validate (BOLT_TYPE_DEVICE_TYPE, type, NULL); } gboolean bolt_device_type_is_host (BoltDeviceType type) { return type == BOLT_DEVICE_HOST; } bolt-0.2/common/bolt-enums.h000066400000000000000000000144651324740507400160610ustar00rootroot00000000000000/* * Copyright © 2017 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 library 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 . * * Authors: * Christian J. Kellner */ #pragma once #include "bolt-names.h" #include "bolt-enum-types.h" gboolean bolt_enum_validate (GType enum_type, gint value, GError **error); gboolean bolt_enum_class_validate (GEnumClass *enum_class, gint value, GError **error); const char * bolt_enum_to_string (GType enum_type, gint value, GError **error); gint bolt_enum_from_string (GType enum_type, const char *string, GError **error); char * bolt_flags_class_to_string (GFlagsClass *flags_class, guint value, GError **error); gboolean bolt_flags_class_from_string (GFlagsClass *flags_class, const char *string, guint *flags_out, GError **error); char * bolt_flags_to_string (GType flags_type, guint value, GError **error); gboolean bolt_flags_from_string (GType flags_type, const char *string, guint *flags_out, GError **error); /** * BoltStatus * @BOLT_STATUS_UNKNOWN: Device is in an unknown state (should normally not happen). * @BOLT_STATUS_DISCONNECTED: Device is not connected. * @BOLT_STATUS_CONNECTED: Device is connected, but not authorized. * @BOLT_STATUS_AUTHORIZING: Device is currently authorizing. * @BOLT_STATUS_AUTH_ERROR: Failed to authorize a device via a key. * @BOLT_STATUS_AUTHORIZED: Device connected and authorized. * @BOLT_STATUS_AUTHORIZED_SECURE: Device connected and securely authorized via a key. * @BOLT_STATUS_AUTHORIZED_NEWKEY: Device connected and authorized via a new key. * @BOLT_STATUS_AUTHORIZED_DPONLY: Device authorized but with thunderbolt disabled. * * The current status of the device. */ typedef enum { BOLT_STATUS_UNKNOWN = -1, BOLT_STATUS_DISCONNECTED = 0, BOLT_STATUS_CONNECTED, BOLT_STATUS_AUTHORIZING, BOLT_STATUS_AUTH_ERROR, BOLT_STATUS_AUTHORIZED, BOLT_STATUS_AUTHORIZED_SECURE, BOLT_STATUS_AUTHORIZED_NEWKEY, BOLT_STATUS_AUTHORIZED_DPONLY } BoltStatus; const char * bolt_status_to_string (BoltStatus status); gboolean bolt_status_is_authorized (BoltStatus status); gboolean bolt_status_is_connected (BoltStatus status); gboolean bolt_status_validate (BoltStatus status); /** * BoltKeyState: * @BOLT_KEY_MISSING: no key * @BOLT_KEY_HAVE: key exists * @BOLT_KEY_NEW: key is new * * The state of the key. */ typedef enum { BOLT_KEY_MISSING = 0, BOLT_KEY_HAVE = 1, BOLT_KEY_NEW = 2 } BoltKeyState; /** * BoltSecurity: * @BOLT_SECURITY_UNKNOWN : Unknown security. * @BOLT_SECURITY_NONE : No security, all devices are automatically connected. * @BOLT_SECURITY_DPONLY : Display Port only devices only. * @BOLT_SECURITY_USER : User needs to authorize devices. * @BOLT_SECURITY_SECURE : User needs to authorize devices. Authorization can * be done via key exchange to verify the device identity. * * The security level of the thunderbolt domain. */ typedef enum { BOLT_SECURITY_UNKNOWN = -1, BOLT_SECURITY_NONE = 0, BOLT_SECURITY_DPONLY = 1, BOLT_SECURITY_USER = '1', BOLT_SECURITY_SECURE = '2', } BoltSecurity; BoltSecurity bolt_security_from_string (const char *str); const char * bolt_security_to_string (BoltSecurity security); gboolean bolt_security_validate (BoltSecurity security); /** * BoltPolicy: * @BOLT_POLICY_DEFAULT: Default policy. * @BOLT_POLICY_MANUAL: Manual authorization of the device. * @BOLT_POLICY_AUTO: Connect the device automatically, * with the best possible security level supported * by the domain controller. * * What do to for connected devices. */ typedef enum { BOLT_POLICY_DEFAULT = 0, BOLT_POLICY_MANUAL = 1, BOLT_POLICY_AUTO = 2, } BoltPolicy; BoltPolicy bolt_policy_from_string (const char *str); const char * bolt_policy_to_string (BoltPolicy policy); gboolean bolt_policy_validate (BoltPolicy policy); /** * BoltAuthFlags: * @BOLT_AUTH_NONE: No flags set. * * Control authorization. */ typedef enum { /*< flags >*/ BOLT_AUTH_NONE = 0 } BoltAuthFlags; /** * BoltDeviceType: * @BOLT_DEVICE_HOST: The device representing the host * @BOLT_DEVICE_PERIPHERAL: A generic thunderbolt peripheral * * The type of the device. */ typedef enum { BOLT_DEVICE_HOST, BOLT_DEVICE_PERIPHERAL } BoltDeviceType; BoltDeviceType bolt_device_type_from_string (const char *str); const char * bolt_device_type_to_string (BoltDeviceType type); gboolean bolt_device_type_validate (BoltDeviceType type); gboolean bolt_device_type_is_host (BoltDeviceType type); /** * BoltAuthMode: * @BOLT_AUTH_ENABLED: Authorization is enabled. * * Control authorization. */ typedef enum { /*< flags >*/ BOLT_AUTH_DISABLED = 0, BOLT_AUTH_ENABLED = 1 } BoltAuthMode; #define bolt_auth_mode_is_enabled(auth) ((auth & BOLT_AUTH_ENABLED) != 0) #define bolt_auth_mode_is_disabled(auth) (!bolt_auth_mode_is_enabled (auth)) bolt-0.2/common/bolt-error.c000066400000000000000000000041511324740507400160450ustar00rootroot00000000000000/* * Copyright © 2017 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 library 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 . * * Authors: * Christian J. Kellner */ #include "config.h" #include "bolt-error.h" #include "bolt-names.h" #include /** * SECTION:bolt-error * @Title: Error codes * */ static const GDBusErrorEntry bolt_error_entries[] = { {BOLT_ERROR_FAILED, BOLT_DBUS_NAME ".Error.Failed"}, {BOLT_ERROR_UDEV, BOLT_DBUS_NAME ".Error.UDev"}, }; GQuark bolt_error_quark (void) { static volatile gsize quark_volatile = 0; g_dbus_error_register_error_domain ("bolt-error-quark", &quark_volatile, bolt_error_entries, G_N_ELEMENTS (bolt_error_entries)); return (GQuark) quark_volatile; } gboolean bolt_err_notfound (const GError *error) { return g_error_matches (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND) || g_error_matches (error, G_FILE_ERROR, G_FILE_ERROR_NOENT) || g_error_matches (error, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_KEY_NOT_FOUND) || g_error_matches (error, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_GROUP_NOT_FOUND); } gboolean bolt_err_exists (const GError *error) { return g_error_matches (error, G_IO_ERROR, G_IO_ERROR_EXISTS) || g_error_matches (error, G_FILE_ERROR, G_FILE_ERROR_EXIST); } gboolean bolt_err_inval (const GError *error) { return g_error_matches (error, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_INVALID_VALUE); } bolt-0.2/common/bolt-error.h000066400000000000000000000025341324740507400160550ustar00rootroot00000000000000/* * Copyright © 2017 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 library 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 . * * Authors: * Christian J. Kellner */ #pragma once #include G_BEGIN_DECLS /** * BoltError: * @BOLT_ERROR_FAILED: Generic error code * @BOLT_ERROR_UDEV: UDev error * * Error codes used inside Bolt. */ enum { BOLT_ERROR_FAILED = 0, BOLT_ERROR_UDEV, BOLT_ERROR_NOKEY, BOLT_ERROR_BADKEY, BOLT_ERROR_CFG, } BoltError; GQuark bolt_error_quark (void); #define BOLT_ERROR (bolt_error_quark ()) /* helper function to check for certain error types */ gboolean bolt_err_notfound (const GError *error); gboolean bolt_err_exists (const GError *error); gboolean bolt_err_inval (const GError *error); G_END_DECLS bolt-0.2/common/bolt-fs.c000066400000000000000000000023531324740507400153260ustar00rootroot00000000000000/* * Copyright © 2017 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 library 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 . * * Authors: * Christian J. Kellner */ #include "config.h" #include "bolt-error.h" #include "bolt-fs.h" gboolean bolt_fs_make_parent_dirs (GFile *target, GError **error) { g_autoptr(GError) err = NULL; g_autoptr(GFile) p = NULL; gboolean ok; p = g_file_get_parent (target); ok = g_file_make_directory_with_parents (p, NULL, &err); if (!ok && !bolt_err_exists (err)) { g_propagate_error (error, g_steal_pointer (&err)); return FALSE; } return TRUE; } bolt-0.2/common/bolt-fs.h000066400000000000000000000017261324740507400153360ustar00rootroot00000000000000/* * Copyright © 2017 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 library 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 . * * Authors: * Christian J. Kellner */ #pragma once #include "bolt-error.h" #include G_BEGIN_DECLS gboolean bolt_fs_make_parent_dirs (GFile *target, GError **error); G_END_DECLS bolt-0.2/common/bolt-io.c000066400000000000000000000224571324740507400153340ustar00rootroot00000000000000/* * Copyright © 2017 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 library 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 . * * Authors: * Christian J. Kellner */ #include "config.h" #include #include #include #include #include #include #include #include #include "bolt-error.h" #include "bolt-io.h" G_DEFINE_AUTOPTR_CLEANUP_FUNC (FILE, fclose); int bolt_open (const char *path, int flags, int mode, GError **error) { int fd = g_open (path, flags, mode); if (fd < 0) { gint code = g_io_error_from_errno (errno); g_set_error (error, G_IO_ERROR, code, "could not open '%s': %s", path, g_strerror (errno)); return -1; } return fd; } static FILE * bolt_fdopen (int fd, const char *mode, GError **error) { FILE *fp = fdopen (fd, mode); if (fp == NULL) { gint code = g_io_error_from_errno (errno); g_set_error (error, G_IO_ERROR, code, "fdopen ('%d') error: %s", fd, g_strerror (errno)); return NULL; } return fp; } gboolean bolt_close (int fd, GError **error) { int r; r = close (fd); if (r == 0) return TRUE; g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errno), "could not close file: %s", g_strerror (errno)); return FALSE; } gboolean bolt_read_all (int fd, void *buf, gsize nbytes, gsize *nread, GError **error) { char *data = buf; gsize count = 0; gboolean ok = TRUE; do { ssize_t n; n = read (fd, data, nbytes); if (n < 0) { if (errno == EINTR) continue; g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errno), "read error: %s", g_strerror (errno)); ok = FALSE; break; } else if (n == 0) { break; } data += n; count += n; nbytes -= n; } while (nbytes > 0); if (nread) *nread = count; return ok; } gboolean bolt_write_all (int fd, const void *buf, gssize nbytes, GError **error) { const char *data = buf; gboolean ok = TRUE; if (nbytes < 0) nbytes = strlen (data); do { ssize_t n; n = write (fd, data, nbytes); if (n < 0) { if (errno == EINTR) continue; g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errno), "write error: %s", g_strerror (errno)); ok = FALSE; } else if (nbytes > 0 && n == 0) { g_set_error (error, G_IO_ERROR, g_io_error_from_errno (EIO), "write error (zero write)"); ok = FALSE; } if (!ok) break; data += n; nbytes -= n; } while (nbytes > 0); return ok; } DIR * bolt_opendir (const char *path, GError **error) { DIR *d = NULL; d = opendir (path); if (d == NULL) { g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errno), "could not open directory ('%s'): %s", path, g_strerror (errno)); return NULL; } return d; } int bolt_openat (int dirfd, const char *path, int oflag, GError **error) { int fd = -1; fd = openat (dirfd, path, oflag); if (fd < 0) { g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errno), "Could not open file %s: %s", path, g_strerror (errno)); } return fd; } DIR * bolt_opendir_at (int dirfd, const char *name, int oflag, GError **error) { int fd = -1; DIR *cd; fd = bolt_openat (dirfd, name, oflag, error); if (fd < 0) return NULL; cd = fdopendir (fd); if (cd == NULL) { g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errno), "failed to open directory: %s", g_strerror (errno)); (void) close (fd); } return cd; } gboolean bolt_closedir (DIR *d, GError **error) { int r; r = closedir (d); if (r < 0) { g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errno), "failed close dir: %s", g_strerror (errno)); return FALSE; } return TRUE; } gboolean bolt_rmdir (const char *name, GError **error) { int r; r = rmdir (name); if (r < 0) { g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errno), "failed to remove directory '%s': %s", name, g_strerror (errno)); return FALSE; } return TRUE; } gboolean bolt_unlink (const char *name, GError **error) { int r; r = unlink (name); if (r < 0) { g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errno), "failed to unlink '%s': %s", name, g_strerror (errno)); return FALSE; } return TRUE; } gboolean bolt_unlink_at (int dirfd, const char *name, int flag, GError **error) { int r; r = unlinkat (dirfd, name, flag); if (r < 0) { g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errno), "failed to unlink '%s': %s", name, g_strerror (errno)); return FALSE; } return TRUE; } char * bolt_read_value_at (int dirfd, const char *name, GError **error) { g_autoptr(FILE) fp = NULL; char line[LINE_MAX], *l; int fd; fd = bolt_openat (dirfd, name, O_NOFOLLOW | O_CLOEXEC | O_RDONLY, error); if (fd < 0) return NULL; fp = bolt_fdopen (fd, "re", error); if (!fp) { g_prefix_error (error, "could not open %s: ", name); return NULL; } l = fgets (line, sizeof (line) - 1, fp); if (!l) { if (ferror (fp)) { if (errno < 1) errno = -EIO; g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errno), "io error of file %s: %s", name, g_strerror (errno)); } line[0] = '\0'; } g_strstrip (line); return g_strdup (line); } gboolean bolt_write_char_at (int dirfd, const char *name, char value, GError **error) { int fd; ssize_t n; fd = bolt_openat (dirfd, name, O_WRONLY | O_CLOEXEC, error); if (fd < 0) return FALSE; retry: n = write (fd, &value, 1); if (n == -1) { int errsv = errno; if (errsv == EINTR) goto retry; if (errsv == ENOKEY) { g_set_error_literal (error, BOLT_ERROR, BOLT_ERROR_NOKEY, "device does not contain a key"); } else if (errsv == EKEYREJECTED) { g_set_error_literal (error, BOLT_ERROR, BOLT_ERROR_BADKEY, "key was rejected"); } else { g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errsv), "write error: %s", g_strerror (errno)); } } (void) close (fd); return n > 0; } gboolean bolt_verify_uid (int dirfd, const char *want, GError **error) { g_autoptr(GError) err = NULL; g_autofree char *have = NULL; gsize want_len; gsize have_len; gboolean ok; have = bolt_read_value_at (dirfd, "unique_id", &err); if (have == NULL) { g_set_error (error, BOLT_ERROR, BOLT_ERROR_FAILED, "unique id verification failed: %s", err->message); return FALSE; } have_len = strlen (have); want_len = strlen (want); ok = have_len == want_len && !memcmp (want, have, have_len); if (!ok) g_set_error (error, BOLT_ERROR, BOLT_ERROR_FAILED, "unique id verification failed [%s != %s]", have, want); return ok; } bolt-0.2/common/bolt-io.h000066400000000000000000000056541324740507400153410ustar00rootroot00000000000000/* * Copyright © 2017 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 library 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 . * * Authors: * Christian J. Kellner */ #pragma once #include #include #include #include #include G_BEGIN_DECLS G_DEFINE_AUTOPTR_CLEANUP_FUNC (DIR, closedir); int bolt_open (const char *path, int flags, int mode, GError **error); gboolean bolt_close (int fd, GError **error); gboolean bolt_read_all (int fd, void *buf, gsize nbytes, gsize *nread, GError **error); gboolean bolt_write_all (int fd, const void *buf, gssize nbytes, GError **error); DIR * bolt_opendir (const char *path, GError **error); DIR * bolt_opendir_at (int dirfd, const char *name, int oflag, GError **error); gboolean bolt_closedir (DIR *d, GError **error); gboolean bolt_rmdir (const char *name, GError **error); int bolt_openat (int dirfd, const char *path, int oflag, GError **error); gboolean bolt_unlink (const char *name, GError **error); gboolean bolt_unlink_at (int dirfd, const char *name, int flag, GError **error); char * bolt_read_value_at (int dirfd, const char *name, GError **error); gboolean bolt_write_char_at (int dirfd, const char *name, char value, GError **error); gboolean bolt_verify_uid (int dirfd, const char *uid, GError **error); G_END_DECLS bolt-0.2/common/bolt-names.h000066400000000000000000000030041324740507400160200ustar00rootroot00000000000000/* * Copyright © 2018 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 library 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 . * * Authors: * Christian J. Kellner */ #pragma once /* logging */ #define BOLT_LOG_DEVICE_UID "BOLT_DEVICE_UID" #define BOLT_LOG_DEVICE_NAME "BOLT_DEVICE_NAME" #define BOLT_LOG_DEVICE_STATE "BOLT_DEVICE_STATE" #define BOLT_LOG_ERROR_DOMAIN "ERROR_DOMAIN" #define BOLT_LOG_ERROR_CODE "ERROR_CODE" #define BOLT_LOG_ERROR_MESSAGE "ERROR_MESSAGE" #define BOLT_LOG_TOPIC "BOLT_TOPIC" #define BOLT_LOG_VERSION "BOLT_VERSION" #define BOLT_LOG_CONTEXT "BOLT_LOG_CONTEXT" /* logging - message ids */ #define BOLT_LOG_MSG_ID_STARTUP "dd11929c788e48bdbb6276fb5f26b08a" /* dbus */ #define BOLT_DBUS_NAME "org.freedesktop.bolt" #define BOLT_DBUS_PATH "/org/freedesktop/bolt" #define BOLT_DBUS_INTERFACE "org.freedesktop.bolt1.Manager" #define BOLT_DBUS_DEVICE_INTERFACE "org.freedesktop.bolt1.Device" bolt-0.2/common/bolt-rnd.c000066400000000000000000000052751324740507400155070ustar00rootroot00000000000000/* * Copyright © 2017 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 library 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 . * * Authors: * Christian J. Kellner */ #include "config.h" #include "bolt-io.h" #include "bolt-rnd.h" #include #include #include #if HAVE_FN_GETRANDOM #include # else # define GRND_NONBLOCK 0 #endif int bolt_get_random_data (void *buf, gsize n) { gboolean ok; ok = bolt_random_getrandom (buf, n, GRND_NONBLOCK, NULL); if (ok) return BOLT_RNG_GETRANDOM; ok = bolt_random_urandom (buf, n); if (ok) return BOLT_RNG_URANDOM; bolt_random_prng (buf, n); return BOLT_RNG_PRNG; } /* specific implementations */ gboolean bolt_random_getrandom (void *buf, gsize n, unsigned flags, GError **error) { int r = -1; #if HAVE_FN_GETRANDOM r = getrandom (buf, n, flags); #else errno = ENOSYS; #endif if (r < 0) { g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errno), "failed to get random data: %s", g_strerror (errno)); return FALSE; } return TRUE; } gboolean bolt_random_urandom (void *buf, gsize n) { gboolean ok; int rndfd; gsize len; rndfd = bolt_open ("/dev/urandom", O_RDONLY | O_CLOEXEC | O_NOCTTY, 0, NULL); if (rndfd < 0) return FALSE; ok = bolt_read_all (rndfd, buf, n, &len, NULL); (void) close (rndfd); /* NB: accroding to the man page random(4), "when calling read(2) for the device /dev/urandom, reads of up to 256 bytes will return as many bytes as are requested and will not be interrupted by a signal handler". */ return ok && len == n; } void bolt_random_prng (void *buf, gsize n) { char *ptr = buf; const gsize l = n % sizeof (guint32); const gsize k = n - l; for (gsize i = 0; i < k; i += sizeof (guint32)) { guint32 r = g_random_int (); memcpy (ptr + i, &r, sizeof (guint32)); } if (l > 0) { guint32 r = g_random_int (); memcpy (ptr + k, &r, l); } } bolt-0.2/common/bolt-rnd.h000066400000000000000000000026441324740507400155110ustar00rootroot00000000000000/* * Copyright © 2017 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 library 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 . * * Authors: * Christian J. Kellner */ #pragma once #include G_BEGIN_DECLS /* general function */ typedef enum { BOLT_RNG_ERROR = -1, BOLT_RNG_URANDOM = 1, BOLT_RNG_PRNG = 2, BOLT_RNG_GETRANDOM = 3, } BoltRng; BoltRng bolt_get_random_data (void *buf, gsize n); /* specific implementations */ gboolean bolt_random_getrandom (void *buf, gsize n, unsigned flags, GError **error); gboolean bolt_random_urandom (void *buf, gsize n); void bolt_random_prng (void *buf, gsize n); G_END_DECLS bolt-0.2/common/bolt-str.c000066400000000000000000000043151324740507400155260ustar00rootroot00000000000000/* * Copyright © 2017 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 library 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 . * * Authors: * Christian J. Kellner */ #include "config.h" #include "bolt-str.h" #include typedef void (* zero_fn_t) (void *s, size_t n); void bolt_erase_n (void *data, gsize n) { #if !HAVE_FN_EXPLICIT_BZERO #warning no explicit bzero, using fallback static volatile zero_fn_t explicit_bzero = bzero; #endif explicit_bzero (data, n); } void bolt_str_erase (char *str) { if (str == NULL) return; bolt_erase_n (str, strlen (str)); } void bolt_str_erase_clear (char **str) { g_return_if_fail (str != NULL); if (*str == NULL) return; bolt_str_erase (*str); g_free (*str); *str = NULL; } GStrv bolt_strv_from_ptr_array (GPtrArray **array) { GPtrArray *a; if (array == NULL || *array == NULL) return NULL; a = *array; if (a->len == 0 || a->pdata[a->len - 1] != NULL) g_ptr_array_add (a, NULL); *array = NULL; return (GStrv) g_ptr_array_free (a, FALSE); } char * bolt_strdup_validate (const char *string) { g_autofree char *str = NULL; gboolean ok; gsize l; if (string == NULL) return NULL; str = g_strdup (string); str = g_strstrip (str); l = strlen (str); if (l == 0) return NULL; ok = g_utf8_validate (str, l, NULL); if (!ok) return NULL; return g_steal_pointer (&str); } char * bolt_strstrip (char *string) { char *str; if (string == NULL) return NULL; str = g_strstrip (string); if (strlen (str) == 0) g_clear_pointer (&str, g_free); return str; } bolt-0.2/common/bolt-str.h000066400000000000000000000023231324740507400155300ustar00rootroot00000000000000/* * Copyright © 2017 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 library 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 . * * Authors: * Christian J. Kellner */ #pragma once #include #include G_BEGIN_DECLS void bolt_erase_n (void *data, gsize n); void bolt_str_erase (char *str); void bolt_str_erase_clear (char **str); #define bolt_streq(s1, s2) (g_strcmp0 (s1, s2) == 0) GStrv bolt_strv_from_ptr_array (GPtrArray **array); #define bolt_yesno(val) val ? "yes" : "no" char *bolt_strdup_validate (const char *string); char *bolt_strstrip (char *string); G_END_DECLS bolt-0.2/common/bolt-term.c000066400000000000000000000050011324740507400156560ustar00rootroot00000000000000/* * Copyright © 2017 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 library 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 . * * Authors: * Christian J. Kellner */ #include "config.h" #include "bolt-str.h" #include "bolt-term.h" #include #include static int fancy_terminal (void) { const char *val; if (!isatty (STDOUT_FILENO)) return 0; val = g_getenv ("TERM"); if (val && !g_ascii_strcasecmp (val, "dumb")) return 0; return 1; } const char * bolt_color (const char *color) { static gint on = -1; if (G_UNLIKELY (on == -1)) on = fancy_terminal (); return on ? color : ""; } /* borrowed from systemd */ static const char *const ansi_glphys[BOLT_GLYPH_LAST] = { [TREE_VERTICAL] = "| ", [TREE_BRANCH] = "|-", [TREE_RIGHT] = "`-", [TREE_SPACE] = " ", [WHITE_CIRCLE] = "o", [BLACK_CIRCLE] = "*", [ARROW] = "->", [MDASH] = "-", }; static const char *const utf8_glphys[BOLT_GLYPH_LAST] = { [TREE_VERTICAL] = "\342\224\202 ", /* │ */ [TREE_BRANCH] = "\342\224\234\342\224\200", /* ├─ */ [TREE_RIGHT] = "\342\224\224\342\224\200", /* └─ */ [TREE_SPACE] = " ", /* */ [WHITE_CIRCLE] = "\342\227\213", /* ○ */ [BLACK_CIRCLE] = "\342\227\217", /* ● */ [ARROW] = "\342\206\222", /* → */ [MDASH] = "\342\200\223", /* – */ }; const char * bolt_glyph (BoltGlyph g) { static const char *const *glyph_table = NULL; g_return_val_if_fail (g < BOLT_GLYPH_LAST, "?"); /* TODO: check for utf-8 support */ if (G_UNLIKELY (glyph_table == NULL)) { if (fancy_terminal ()) glyph_table = utf8_glphys; else glyph_table = ansi_glphys; } return glyph_table[g]; } bolt-0.2/common/bolt-term.h000066400000000000000000000025151324740507400156720ustar00rootroot00000000000000/* * Copyright © 2017 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 library 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 . * * Authors: * Christian J. Kellner */ #pragma once #include #include G_BEGIN_DECLS #define ANSI_NORMAL "\x1B[0m" #define ANSI_RED "\x1B[0;31m" #define ANSI_GREEN "\x1B[0;32m" #define ANSI_YELLOW "\x1B[0;33m" #define ANSI_BLUE "\x1B[0;34m" #define ANSI_HIGHLIGHT_BLACK "\x1B[0;1;30m" #define ANSI_HIGHLIGHT_RED "\x1B[0;1;31m" const char * bolt_color (const char *color); typedef enum { TREE_VERTICAL, TREE_BRANCH, TREE_RIGHT, TREE_SPACE, BLACK_CIRCLE, WHITE_CIRCLE, ARROW, MDASH, BOLT_GLYPH_LAST } BoltGlyph; const char * bolt_glyph (BoltGlyph g); G_END_DECLS bolt-0.2/common/bolt-time.c000066400000000000000000000022351324740507400156530ustar00rootroot00000000000000/* * Copyright © 2018 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 library 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 . * * Authors: * Christian J. Kellner */ #include "config.h" #include "bolt-time.h" char * bolt_epoch_format (guint64 seconds, const char *format) { g_autoptr(GDateTime) dt = NULL; dt = g_date_time_new_from_unix_utc ((gint64) seconds); if (dt == NULL) return NULL; return g_date_time_format (dt, format); } guint64 bolt_now_in_seconds (void) { gint64 now = g_get_real_time (); return (guint64) now / G_USEC_PER_SEC; } bolt-0.2/common/bolt-time.h000066400000000000000000000017361324740507400156650ustar00rootroot00000000000000/* * Copyright © 2018 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 library 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 . * * Authors: * Christian J. Kellner */ #pragma once #include G_BEGIN_DECLS char * bolt_epoch_format (guint64 seconds, const char *format); guint64 bolt_now_in_seconds (void); G_END_DECLS bolt-0.2/contrib/000077500000000000000000000000001324740507400137615ustar00rootroot00000000000000bolt-0.2/contrib/Dockerfile-arch000066400000000000000000000007761324740507400167000ustar00rootroot00000000000000## -*- mode: dockerfile -*- FROM archlinux/base ENV LANG en_US.UTF-8 ENV LC_ALL en_US.UTF-8 RUN rm /usr/share/libalpm/hooks/package-cleanup.hook RUN pacman -Syu --noconfirm RUN pacman -S --noconfirm base-devel RUN pacman -S --noconfirm \ asciidoc \ dbus-glib \ git \ gobject-introspection \ gtk-doc \ meson \ perl-sgmls \ polkit \ python-dbus \ python-gobject \ python-pip \ umockdev \ valgrind RUN pip3 install python-dbusmock RUN mkdir /src WORKDIR /src bolt-0.2/contrib/Dockerfile-debian000066400000000000000000000007011324740507400171710ustar00rootroot00000000000000## -*- mode: dockerfile -*- FROM debian:sid RUN apt-get update -q && apt-get install -y \ asciidoc \ git \ gobject-introspection \ libgirepository1.0-dev \ libpolkit-gobject-1-dev \ locales \ libudev-dev \ libumockdev-dev \ meson \ pkg-config \ policykit-1 \ python3-dbus \ python3-dbusmock \ udev \ umockdev \ systemd \ && rm -rf /var/lib/apt/lists/* RUN mkdir /src WORKDIR /src bolt-0.2/contrib/Dockerfile-fedora000066400000000000000000000007461324740507400172200ustar00rootroot00000000000000## -*- mode: dockerfile -*- FROM fedora:27 ENV LANG en_US.UTF-8 ENV LANGUAGE en_US:en ENV LC_ALL en_US.UTF-8 RUN dnf --enablerepo=updates-testing -y update RUN dnf --enablerepo=updates-testing -y install \ gcc \ glib2-devel \ gtk-doc \ libgudev-devel \ meson \ polkit-devel \ python3 \ python3-dbus \ python3-dbusmock \ python3-gobject \ rpm-build \ redhat-rpm-config \ systemd-devel \ umockdev-devel RUN mkdir /src WORKDIR /src bolt-0.2/contrib/PKGBUILD000066400000000000000000000014331324740507400151060ustar00rootroot00000000000000pkgname=bolt-git pkgver=r271.6d86460 pkgrel=1 pkgdesc="Thunderbolt 3 security system daemon" arch=('i686' 'x86_64') url="https://github.com/gicmo/bolt" license=('LGPL') depends=('polkit' 'systemd') makedepends=('asciidoc' 'meson' 'git') checkdepends=('umockdev') conflicts=('bolt') provides=('bolt') source=(git+https://github.com/gicmo/bolt.git) md5sums=('SKIP') pkgver() { cd bolt printf "r%s.%s" "$(git rev-list --count HEAD)" "$(git rev-parse --short HEAD)" } build() { cd "${srcdir}" install -d build arch-meson bolt build ninja -v -C build } check() { cd "${srcdir}" ninja -C build test } package() { cd "${srcdir}" DESTDIR="${pkgdir}" ninja -C build install # Fixup mode to match polkit install -d -o root -g 102 -m 750 "${pkgdir}/usr/share/polkit-1/rules.d" } bolt-0.2/contrib/bolt.spec.in000066400000000000000000000037331324740507400162100ustar00rootroot00000000000000Name: bolt Version: @version@ Release: @reltag@%{?dist} Summary: Thunderbolt device manager License: LGPLv2+ URL: https://github.com/gicmo/bolt Source0: %{url}/releases/download/%{version}/%{name}-%{version}.tar.xz BuildRequires: asciidoc BuildRequires: meson BuildRequires: libudev-devel BuildRequires: pkgconfig(gio-2.0) BuildRequires: pkgconfig(libudev) BuildRequires: pkgconfig(systemd) BuildRequires: polkit-devel BuildRequires: umockdev-devel BuildRequires: systemd %{?systemd_requires} %description bolt is a system daemon to manage thunderbolt 3 devices via a D-BUS API. Thunderbolt 3 features different security modes that require devices to be authorized before they can be used. The D-Bus API can be used to list devices, enroll them (authorize and store them in the local database) and forget them again (remove previously enrolled devices). It also emits signals if new devices are connected (or removed). During enrollment devices can be set to be automatically authorized as soon as they are connected. A command line tool, called boltctl, can be used to control the daemon and perform all the above mentioned tasks. %prep %setup -q %build %meson -Ddb-path=@dbdir@ %meson_build %check %meson_test %install %meson_install %post %systemd_post %{name}.service %preun %systemd_preun %{name}.service %postun %systemd_postun_with_restart %{name}.service %files %license COPYING %doc README.md BUGS.md INSTALL.md HACKING.md %{_bindir}/boltctl %{_libexecdir}/boltd %{_unitdir}/%{name}.service %{_udevrulesdir}/*-%{name}.rules %{_sysconfdir}/dbus-1/system.d/org.freedesktop.bolt.conf %{_datadir}/dbus-1/interfaces/org.freedesktop.bolt.xml %{_datadir}/polkit-1/actions/org.freedesktop.bolt.policy %{_datadir}/polkit-1/rules.d/org.freedesktop.bolt.rules %{_datadir}/dbus-1/system-services/org.freedesktop.bolt.service %{_mandir}/man1/boltctl.1* %{_mandir}/man8/boltd.8* %dir @dbdir@ %changelog * @longdate@ Christian Kellner - @version@-@reltag@ - build from git sources. bolt-0.2/contrib/docker-build.sh000077500000000000000000000002631324740507400166650ustar00rootroot00000000000000#!/bin/bash set -e set -x export LC_ALL=C.UTF-8 export PYTHONPATH="/usr/share/glib-2.0" rm -rf /build mkdir /build meson . /build ninja -C /build meson test -C /build --verbose bolt-0.2/contrib/js/000077500000000000000000000000001324740507400143755ustar00rootroot00000000000000bolt-0.2/contrib/js/.jshintrc000066400000000000000000000132321324740507400162230ustar00rootroot00000000000000{ "maxerr" : 50, // {int} Maximum error before stopping // Enforcing "bitwise" : false, // true: Prohibit bitwise operators (&, |, ^, etc.) "camelcase" : false, // true: Identifiers must be in camelCase "curly" : false, // true: Require {} for every new block or scope "eqeqeq" : true, // true: Require triple equals (===) for comparison "forin" : false, // true: Require filtering for..in loops with obj.hasOwnProperty() "immed" : false, // true: Require immediate invocations to be wrapped in parens e.g. `(function () { } ());` "indent" : 4, // {int} Number of spaces to use for indentation "latedef" : "nofunc", // true: Require variables/functions to be defined before being used "newcap" : true, // true: Require capitalization of all constructor functions e.g. `new F()` "noarg" : true, // true: Prohibit use of `arguments.caller` and `arguments.callee` "noempty" : true, // true: Prohibit use of empty blocks "nonew" : false, // true: Prohibit use of constructors for side-effects (without assignment) "plusplus" : false, // true: Prohibit use of `++` & `--` "quotmark" : false, // Quotation mark consistency: // false : do nothing (default) // true : ensure whatever is used is consistent // "single" : require single quotes // "double" : require double quotes "undef" : true, // true: Require all non-global variables to be declared (prevents global leaks) "unused" : false, // true: Require all defined variables be used "strict" : false, // true: Requires all functions run in ES5 Strict Mode "trailing" : true, // true: Prohibit trailing whitespaces "maxparams" : false, // {int} Max number of formal params allowed per function "maxdepth" : false, // {int} Max depth of nested blocks (within functions) "maxstatements" : false, // {int} Max number statements per function "maxcomplexity" : false, // {int} Max cyclomatic complexity per function "maxlen" : false, // {int} Max number of characters per line // Relaxing "asi" : false, // true: Tolerate Automatic Semicolon Insertion (no semicolons) "boss" : false, // true: Tolerate assignments where comparisons would be expected "debug" : false, // true: Allow debugger statements e.g. browser breakpoints. "eqnull" : false, // true: Tolerate use of `== null` "esnext" : true, // true: Allow ES.next (ES6) syntax (ex: `const`) "moz" : true, // true: Allow Mozilla specific syntax (extends and overrides esnext features) // (ex: `for each`, multiple try/catch, function expression…) "evil" : false, // true: Tolerate use of `eval` and `new Function()` "expr" : false, // true: Tolerate `ExpressionStatement` as Programs "funcscope" : false, // true: Tolerate defining variables inside control statements" "globalstrict" : false, // true: Allow global "use strict" (also enables 'strict') "iterator" : false, // true: Tolerate using the `__iterator__` property "lastsemic" : false, // true: Tolerate omitting a semicolon for the last statement of a 1-line block "laxbreak" : true, // true: Tolerate possibly unsafe line breakings "laxcomma" : false, // true: Tolerate comma-first style coding "loopfunc" : false, // true: Tolerate functions being defined in loops "multistr" : true, // true: Tolerate multi-line strings "proto" : false, // true: Tolerate using the `__proto__` property "scripturl" : false, // true: Tolerate script-targeted URLs "smarttabs" : false, // true: Tolerate mixed tabs/spaces when used for alignment "shadow" : false, // true: Allows re-define variables later in code e.g. `var x=1; x=2;` "sub" : false, // true: Tolerate using `[]` notation when it can still be expressed in dot notation "supernew" : false, // true: Tolerate `new function () { ... };` and `new Object;` "validthis" : false, // true: Tolerate using this in a non-constructor function // Environments "browser" : false, // Web Browser (window, document, etc) "couch" : false, // CouchDB "devel" : false, // Development/debugging (alert, confirm, etc) "dojo" : false, // Dojo Toolkit "jquery" : false, // jQuery "mootools" : false, // MooTools "node" : false, // Node.js "nonstandard" : false, // Widely adopted globals (escape, unescape, etc) "prototypejs" : false, // Prototype and Scriptaculous "rhino" : false, // Rhino "worker" : false, // Web Workers "wsh" : false, // Windows Scripting Host "yui" : false, // Yahoo User Interface // Legacy "nomen" : false, // true: Prohibit dangling `_` in variables "onevar" : false, // true: Allow only one `var` statement per function "passfail" : false, // true: Stop on first error "white" : false, // true: Check against strict whitespace and indentation rules // Custom Globals "predef" : [ "log", "logError", "print", "printerr", "imports", "ARGV", "pkg", "_", "C_", "N_" ] } bolt-0.2/contrib/js/client.js000066400000000000000000000213731324740507400162170ustar00rootroot00000000000000/* * Copyright © 2017 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 library 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 . * * Authors: * Christian J. Kellner */ const Gio = imports.gi.Gio; const GLib = imports.gi.GLib; const Lang = imports.lang; const Signals = imports.signals; /* Keep in sync with data/org.freedesktop.bolt.xml */ const BoltClientInterface = ' \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ '; const BoltDeviceInterface = ' \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ '; const BoltClientProxy = Gio.DBusProxy.makeProxyWrapper(BoltClientInterface); const BoltDeviceProxy = Gio.DBusProxy.makeProxyWrapper(BoltDeviceInterface); /* */ var Status = { DISCONNECTED: 0, CONNECTED: 1, AUTHORIZING: 2, AUTH_ERROR: 3, AUTHORIZED: 4, AUTHORIZED_SECURE: 5, AUTHORIZED_NEWKY: 6 }; var Policy = { DEFAULT: 0, MANUAL: 1, AUTO:2 }; var AuthFlags = { NONE: 0, }; const BOLT_DBUS_NAME = 'org.freedesktop.bolt'; const BOLT_DBUS_PATH = '/org/freedesktop/bolt'; var Client = new Lang.Class({ Name: 'BoltClient', _init: function(readyCallback) { this._readyCallback = readyCallback; this._proxy = new BoltClientProxy( Gio.DBus.system, BOLT_DBUS_NAME, BOLT_DBUS_PATH, Lang.bind(this, this._onProxyReady) ); this._signals = []; this.probing = false; }, _onProxyReady: function(proxy, error) { if (error !== null) { log(error.message); return; } this._proxy = proxy; this._proxyConnect('g-properties-changed', Lang.bind(this, this._onPropertiesChanged)); this._proxyConnect('DeviceAdded', Lang.bind(this, this._onDeviceAdded), true); this.probing = this._proxy.Probing; if (this.probing) this.emit('probing-changed', this.probing); this._readyCallback(this); }, _onPropertiesChanged: function(proxy, properties) { let unpacked = properties.deep_unpack(); if (!('Probing' in unpacked)) return; this.probing = this._proxy.Probing; this.emit('probing-changed', this.probing); }, _onDeviceAdded: function(proxy, emitter, params) { let [path] = params; let device = new BoltDeviceProxy(Gio.DBus.system, BOLT_DBUS_NAME, path); this.emit('device-added', device); }, _proxyConnect: function(name, callback, dbus) { var signal_id; if (dbus === true) signal_id = this._proxy.connectSignal(name, Lang.bind(this, callback)); else signal_id = this._proxy.connect(name, Lang.bind(this, callback)); this._signals.push([dbus, signal_id]); }, _proxyDisconnectAll: function() { while (this._signals.length) { let [dbus, sid] = this._signals.shift(); if (dbus === true) this._proxy.disconnectSignal(sid); else this._proxy.disconnect(sid); } }, /* public methods */ close: function() { this._proxyDisconnectAll(); this._proxy = null; }, listDevices: function(callback) { this._proxy.ListDevicesRemote(Lang.bind(this, function (res, error) { if (error) { callback(null, error); return; } let [paths] = res; let devices = []; for (let i = 0; i < paths.length; i++) { let path = paths[i]; let device = new BoltDeviceProxy(Gio.DBus.system, BOLT_DBUS_NAME, path); devices.push(device); } callback(devices, null); })); }, enrollDevice: function(id, policy, callback) { this._proxy.EnrollDeviceRemote(id, policy, AuthFlags.NONE, Lang.bind(this, function (res, error) { if (error) { callback(null, error); return; } let [path] = res; let device = new BoltDeviceProxy(Gio.DBus.system, BOLT_DBUS_NAME, path); callback(device, null); })); }, deviceGetParent: function(dev, callback) { let parentUid = dev.Parent; if (!parentUid) { callback (null, dev, null); return; } this._proxy.DeviceByUidRemote(dev.Parent, Lang.bind(this, function (res, error) { if (error) { callback(null, dev, error); return; } let [path] = res; let parent = new BoltDeviceProxy(Gio.DBus.system, BOLT_DBUS_NAME, path); callback(parent, dev, null); })); }, }); Signals.addSignalMethods(Client.prototype); /* helper class to automatically authorize new devices */ var AuthRobot = new Lang.Class({ Name: 'BoltAuthRobot', _init: function(client) { this._client = client; this._devicesToEnroll = []; this._enrolling = false; this._client.connect('device-added', Lang.bind(this, this._onDeviceAdded)); }, close: function() { this.disconnectAll(); this._client = null; }, /* the "device-added" signal will be emitted by boltd for every * device that is not currently stored in the database. We are * only interested in those devices, because all known devices * will be handled by the user himself */ _onDeviceAdded: function(cli, dev) { if (dev.Status !== Status.CONNECTED) { return; } /* check if we should enroll the device */ let res = [false]; this.emit('enroll-device', dev, res); if (res[0] !== true) { return; } /* ok, we should authorize the device, add it to the back * of the list */ this._devicesToEnroll.push(dev); this._enrollDevices(); }, /* The enrollment queue: * - new devices will be added to the end of the array. * - an idle callback will be scheduled that will keep * calling itself as long as there a devices to be * enrolled. */ _enrollDevices: function() { if (this._enrolling) { return; } this.enrolling = true; GLib.idle_add(GLib.PRIORITY_DEFAULT, Lang.bind(this, this._enrollDevicesIdle)); }, _onEnrollDone: function(device, error) { if (error) { this.emit('enroll-failed', error, device); } /* TODO: scan the list of devices to be authorized for children * of this device and remove them (and their children and * their children and ....) from the device queue */ this._enrolling = this._devicesToEnroll.length > 0; if (this._enrolling) { GLib.idle_add(GLib.PRIORITY_DEFAULT, Lang.bind(this, this._enrollDevicesIdle)); } }, _enrollDevicesIdle: function() { let devices = this._devicesToEnroll; let dev = devices.shift(); if (dev === undefined) { return GLib.SOURCE_REMOVE; } this._client.enrollDevice(dev.Uid, Policy.DEFAULT, Lang.bind(this, this._onEnrollDone)); return GLib.SOURCE_REMOVE; }, }); Signals.addSignalMethods(AuthRobot.prototype); bolt-0.2/contrib/js/test.js000066400000000000000000000027741324740507400157240ustar00rootroot00000000000000/* * Copyright © 2017 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 library 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 . * * Authors: * Christian J. Kellner */ const Gio = imports.gi.Gio; const GLib = imports.gi.GLib; const Lang = imports.lang; imports.searchPath.unshift('.'); const Bolt = imports.client; let gotParent = function (p, d, e) { if (e) { log(e); return; } if (!p) { print (' device: '+ d.Uid + ' -> no parent'); return; } print (' device: '+ d.Uid + ' -> parent: ' + p.Uid); }; let client = new Bolt.Client(function (client) { client.listDevices(function (devices, error) { if (error) { print ('error ' + error); return; } for (let i = 0; i < devices.length; i++) { let d = devices[i]; print(' ' + d.Uid + " " + d.Name); client.deviceGetParent(d, gotParent); } }); }); let loop = new GLib.MainLoop(null, false); loop.run(); bolt-0.2/data/000077500000000000000000000000001324740507400132325ustar00rootroot00000000000000bolt-0.2/data/90-bolt.rules000066400000000000000000000005361324740507400155000ustar00rootroot00000000000000# bolt udev rules # # SPDX-License-Identifier: GPL-2.1+ # # Copyright © 2017 Red Hat, Inc # # Authors: # Christian J. Kellner # ACTION=="remove", GOTO="bolt_end" # start bolt service if we have a thunderbolt device connected SUBSYSTEM=="thunderbolt", TAG+="systemd", ENV{SYSTEMD_WANTS}+="bolt.service" LABEL="bolt_end" bolt-0.2/data/bolt.service.in000066400000000000000000000006441324740507400161650ustar00rootroot00000000000000[Unit] Description=Thunderbolt system service [Service] Type=dbus BusName=org.freedesktop.bolt ExecStart=@libexecdir@/boltd #Environment="G_MESSAGES_DEBUG=all" Restart=on-failure MemoryDenyWriteExecute=yes PrivateTmp=yes ProtectControlGroups=yes ProtectHome=yes ProtectKernelModules=yes ProtectSystem=full RestrictAddressFamilies=AF_NETLINK AF_UNIX RestrictRealtime=yes ReadWritePaths=@dbdir@ SystemCallFilter=~@mount bolt-0.2/data/boltd.gresource.xml000066400000000000000000000002661324740507400170610ustar00rootroot00000000000000 org.freedesktop.bolt.xml bolt-0.2/data/org.freedesktop.bolt.conf000066400000000000000000000016031324740507400201410ustar00rootroot00000000000000 bolt-0.2/data/org.freedesktop.bolt.service.in000066400000000000000000000001501324740507400212550ustar00rootroot00000000000000[D-BUS Service] Name=org.freedesktop.bolt Exec=@libexecdir@/boltd User=root SystemdService=bolt.service bolt-0.2/data/org.freedesktop.bolt.xml000066400000000000000000000207151324740507400200210ustar00rootroot00000000000000 Thunderbolt device management. Version of the daemon. Indication that a new thunderbolt device has been connected and the manager is reacting to that. Probing should be true as long as the new device (and any possible attached peripherals) are initialized by the system. The policy to use during enrollment when "default" was sepcified. The security level of the system. The authorization mode of the daemon. Currently supported values are "enabled" if it is authorizing devices or "disabled" if authorization is disabled. An array of object paths for the devices. List all known devices, i.e. connected or stored in the database. The unique id of the device. Object path for the devices. Return a device given its unique identifier. The unique id of the device. Policy to use for the device. Control aspects of enrollment. Object path for the devices. Authorize a device, and on success, store the device in the database. If policy is set to "Auto", the device will be automatically authorized in the future. The unique id of the device. Remove the device and any associated inforamtion, such as the policy and its key, from the store. Object path of the new device. A new device was added. Object path of the removed device. A device was removed. Representation of a single Thunderbolt device. The unique-id of the device. The name of the device. The vendor of the device. The type of the device, i.e. 'host' or 'peripheral' The current status of the device. The unique id of the parent the device. The only device without a parent will be the device that represents the host controller. The sysfs path of the device, if it is connected. Indication if the device is stored. The authorization policy of the device. If a key is associated with the device. If set, a name that was given to the device by the user. Can only be set when device is stored. Point in time (since Epoch, in seconds) when the device was connected (0 if it is not connected). Point in time (since Epoch, in seconds) when the device was authorized (0 if it is not authorized). Point in time (since Epoch, in seconds) when the device was stored (0 if it is not stored). Control aspects of authorization. Authorize the device. bolt-0.2/docs/000077500000000000000000000000001324740507400132515ustar00rootroot00000000000000bolt-0.2/docs/boltctl.1.txt000066400000000000000000000034371324740507400156230ustar00rootroot00000000000000boltctl(1) ========== NAME ---- boltctl - control the thunderbolt device manger SYNOPSIS -------- [verse] *boltctl* 'authorize' 'DEVICE' *boltctl* 'enroll' 'DEVICE' *boltctl* 'forget' 'DEVICE' *boltctl* 'info' 'DEVICE' *boltctl* 'list' *boltctl* 'monitor' DESCRIPTION ------------ 'boltctl' is the command line interface to interact with 'boltd', the system daemon that manages Thunderbolt 3(TM) devices. It can be used to query the state of devices as well as manage them. Devices can be globally identified via their unique identifier (uuid). All commands that take a 'DEVICE' identifier expect this unique id. If no command is given, it is equivalent to 'boltctl list'. COMMANDS -------- authorize 'DEVICE' ~~~~~~~~~~~~~~~~~~ Authorize a currently unauthorized device identified via its unique id (uuid) 'DEVICE'. If a key is stored in the database it will be used, given the security level of the domain supports secure device connection. Use 'boltctl list' to find out the uuid of a device. enroll 'DEVICE' ~~~~~~~~~~~~~~~ Authorize and record the device with the unique id 'DEVICE' in the database. If the domain supports secure connection a new key will be generated and stored in the database alongside the device name and vendor name. The key, if created, will be used in the future to securely authorize the device. forget 'DEVICE' ~~~~~~~~~~~~~~~ Remove the information about the device with the unique id 'DEVICE' from the database. This includes the key, if one was previously generated. info 'DEVICE' ~~~~~~~~~~~~ Display information about the device with the unique id 'DEVICE'. list ~~~~ List and print information about all connected and stored devices. monitor ~~~~~~~ Listen for and show changes in connected devices. Author ------ Written by Christian Kellner . bolt-0.2/docs/boltd.8.txt000066400000000000000000000051451324740507400152710ustar00rootroot00000000000000boltd(8) ======== NAME ---- boltd - thunderbolt device managing system daemon SYNOPSIS -------- *boltd* ['OPTIONS'] DESCRIPTION ----------- boltd is the thunderbolt device manager daemon. Its goal is to enable the secure and convenient use of thunderbolt devices by using the security features of modern thunderbolt controllers. It provides the `org.freedesktop.bolt` name on the system bus. boltd is autostarted via systemd/udev if a thunderbolt devices is connected. The thunderbolt I/O technology works by bridging PCIe between the controllers on each end of the connection, which in turn means that devices connected via Thunderbolt are ultimately connected via PCIe. Therefore thunderbolt can achieve very high connection speeds, fast enough to even drive external graphics cards. The downside is that it also makes certain attacks possible. To mitigate these security problems, the latest version -- known as Thunderbolt 3 -- supports different *security levels*: 'none': No security. The behavior is identical to previous Thunderbolt versions. 'dponly': No PCIe tunnels are created at all, but DisplayPort tunnels are allowed and will work. 'user': Connected devices must be authorized by the user. Only then will the PCIe tunnels be activated. 'secure': Basically the same as user mode, but additionally a key will be written to the device the first time the device is connected. This key will then be used to verify the identity of the connected device. The primary task of *boltd* is to authorize thunderbolt peripherals if the security level is either `user` or `secure`. It provides a D-Bus API to list devices, enroll them (authorize and store them in the local database) and forget them again (remove previously enrolled devices). It also emits signals if new devices are connected (or removed). During enrollment devices can be set to be automatically authorized as soon as they are connected. A command line tool, called boltctl(1), can be used to control the daemon and perform all the above mentioned tasks. OPTIONS ------- *-h, --help*:: Prints a short help text and exits. *--version*:: Shows the version number and exits. *-r, --replace*:: Replace the currently running boltd instance. *-v, --verbosee*:: Print debug output. ENVIRONMENT ----------- *`BOLT_DBPATH`*:: Specifies the path where the daemon stores device information, including the keys used for authorization. Overwrites the path that was set at compile time. EXIT STATUS ----------- On success 0 is returned, a non-zero failure code otherwise. Author ------ Written by Christian Kellner . SEE ALSO -------- boltctl(1) bolt-0.2/meson.build000066400000000000000000000236171324740507400144740ustar00rootroot00000000000000project('bolt', 'c', version: '0.2', license : 'LGPL-2.1+', meson_version: '>= 0.42.0', default_options: ['warning_level=1', 'c_std=gnu99', 'buildtype=debugoptimized']) # additional compiler warnings, if supported test_args = [ '-fstack-protector-strong', '-Waggregate-return', '-Wunused', '-Warray-bounds', '-Wcast-align', '-Wclobbered', '-Wdeclaration-after-statement', '-Wempty-body', '-Wformat=2', '-Wformat-nonliteral', '-Wformat-security', '-Wformat-signedness', '-Wignored-qualifiers', '-Wimplicit-function-declaration', '-Winit-self', '-Wmissing-declarations', '-Wmissing-format-attribute', '-Wmissing-include-dirs', '-Wmissing-noreturn', '-Wmissing-parameter-type', '-Wmissing-prototypes', '-Wnested-externs', '-Wno-discarded-qualifiers', '-Wno-missing-field-initializers', '-Wno-strict-aliasing', '-Wno-suggest-attribute=format', '-Wno-unused-parameter', '-Wold-style-definition', '-Woverride-init', '-Wpointer-arith', '-Wredundant-decls', '-Wreturn-type', '-Wshadow', '-Wsign-compare', '-Wstrict-aliasing', '-Wstrict-prototypes', '-Wswitch-default', '-Wtype-limits', '-Wundef', '-Wuninitialized', '-Wunused-but-set-variable', '-Wwrite-strings' ] compiler = meson.get_compiler('c') foreach arg: test_args if compiler.has_argument(arg) add_project_arguments(arg, language : 'c') endif endforeach # build options build_man = get_option('man') req_man = build_man == 'true' # dependencies gnome = import('gnome') glib = dependency('glib-2.0', version: '>= 2.52.0') gio = dependency('gio-2.0') libudev = dependency('libudev') unix = dependency('gio-unix-2.0') udev = dependency('udev') umock = dependency('umockdev-1.0') polkit = dependency('polkit-gobject-1') systemd = dependency('systemd') git = find_program('git', required: false) a2x = find_program(['a2x', 'a2x.py'], required: req_man) # configuration & well known directories prefixdir = get_option('prefix') srcdir = meson.source_root() bindir = join_paths(prefixdir, get_option('bindir')) libexecdir = join_paths(prefixdir, get_option('libexecdir')) datadir = join_paths(prefixdir, get_option('datadir')) sysconfdir = get_option('sysconfdir') mandir = get_option('mandir') dbdir = get_option('db-path') udevdir = udev.get_pkgconfig_variable('udevdir') unitdir = systemd.get_pkgconfig_variable('systemdsystemunitdir') version_split = meson.project_version().split('.') version_major = version_split[0] version_minor = version_split[1] conf = configuration_data() conf.set_quoted('VERSION', meson.project_version()) conf.set_quoted('PACKAGE_NAME', meson.project_name()) conf.set_quoted('PACKAGE_VERSION', meson.project_version()) conf.set_quoted('DATADIR', datadir) conf.set_quoted('BOLT_DBDIR', dbdir) conf.set('VERSION_MAJOR', version_major) conf.set('VERSION_MINOR', version_minor) conf.set('_GNU_SOURCE', true) foreach fn : [ ['explicit_bzero', '''#include '''], ['getrandom', '''#include '''] ] have = compiler.has_function(fn[0], prefix : fn[1]) conf.set10('HAVE_FN_' + fn[0].to_upper(), have) endforeach if polkit.version().version_compare('>= 0.114') conf.set('HAVE_POLKIT_AUTOPTR', '1') endif config_h = configure_file(output: 'config.h', configuration: conf) cargs = ['-DG_LOG_DOMAIN="bolt"'] privileged_group = get_option('privileged-group') subs = configuration_data() subs.set('dbdir', dbdir) subs.set('libexecdir', libexecdir) subs.set('privileged_group', privileged_group) subs.set('version', meson.project_version()) longdate = run_command('date', '+%a %b %d %Y').stdout().strip() subs.set('longdate', longdate) subs.set('reltag', 'pre1') if git.found() gitres = run_command(git, ['--git-dir=@0@/.git'.format(srcdir), 'rev-parse', '--short=7', 'HEAD']) if gitres.returncode() == 0 gitrev = gitres.stdout().strip() gitdate = run_command('date', '+%Y%m%d').stdout().strip() # YYYYMMDD subs.set('reltag', '@0@git@1@'.format(gitdate, gitrev)) endif endif # common static library # contains code shared by daemon, command line tools common_deps = [glib, gio, libudev, unix] common_headers = [ 'common/bolt-enums.h', 'common/bolt-error.h' ] common_sources = [ 'common/bolt-enums.c', 'common/bolt-error.c', 'common/bolt-fs.c', 'common/bolt-io.c', 'common/bolt-rnd.c', 'common/bolt-str.c', 'common/bolt-term.c', 'common/bolt-time.c' ] common_enums = gnome.mkenums_simple('bolt-enum-types', sources: common_headers) gen_sources = [common_enums[0]] gen_headers = [common_enums[1]] common_lib = static_library('common', c_args : [cargs], sources: common_sources + gen_sources + gen_headers, dependencies: common_deps, include_directories: [ include_directories('.') ]) common = declare_dependency( sources: gen_headers, dependencies: common_deps, link_with: common_lib, include_directories: [ include_directories('common') ]) # boltd - the main daemon daemon_sources = files([ 'boltd/bolt-auth.c', 'boltd/bolt-bouncer.c', 'boltd/bolt-config.c', 'boltd/bolt-exported.c', 'boltd/bolt-manager.c', 'boltd/bolt-power.c', 'boltd/bolt-device.c', 'boltd/bolt-key.c', 'boltd/bolt-log.c', 'boltd/bolt-store.c', 'boltd/bolt-sysfs.c' ]) daemon_sources += gnome.compile_resources( 'bolt-daemon-resource', 'data/boltd.gresource.xml', source_dir: 'data', c_name: 'bolt_daemon') install_data(['data/org.freedesktop.bolt.xml'], install_dir : join_paths(datadir, 'dbus-1', 'interfaces') ) install_data(['data/org.freedesktop.bolt.conf'], install_dir : join_paths(sysconfdir, 'dbus-1', 'system.d') ) service_file = configure_file( input: 'data/org.freedesktop.bolt.service.in', output: 'org.freedesktop.bolt.service', configuration: subs ) install_data(service_file, install_dir: join_paths(datadir, 'dbus-1', 'system-services') ) policy_rules = configure_file( input: 'policy/org.freedesktop.bolt.rules.in', output: 'org.freedesktop.bolt.rules', configuration: subs ) install_data(policy_rules, install_dir: join_paths(datadir, 'polkit-1', 'rules.d') ) policy_file = configure_file( input: 'policy/org.freedesktop.bolt.policy.in', output: 'org.freedesktop.bolt.policy', configuration: subs ) install_data(policy_file, install_dir: join_paths(datadir, 'polkit-1', 'actions') ) unit_file = configure_file( input: 'data/bolt.service.in', output: 'bolt.service', configuration: subs ) install_data(unit_file, install_dir: unitdir ) install_data('data/90-bolt.rules', install_dir: join_paths(udevdir, 'rules.d') ) daemon_deps = [ glib, gio, libudev, polkit, unix, common ] daemon_library = static_library('daemon', c_args : [cargs], sources: daemon_sources, dependencies: daemon_deps, include_directories: [ include_directories('boltd') ]) libdaemon = declare_dependency( dependencies: daemon_deps, link_with: [daemon_library], include_directories: [ include_directories('boltd') ]) executable('boltd', ['boltd/bolt-daemon.c'], dependencies: [libdaemon], c_args : [ cargs, ], install: true, install_dir: libexecdir) # command line tools executable('boltctl', ['cli/bolt-client.c', 'cli/bolt-device.c', 'cli/bolt-proxy.c', 'cli/boltctl.c'], dependencies: [glib, gio, unix, common], c_args : [ cargs, ], install: true, install_dir: bindir) # testing test_resources = gnome.compile_resources( 'bolt-test-resources', 'tests/tests.gresource.xml', source_dir: 'tests', c_name: 'bolt_test') tests = [ ['test-common'], ['test-exported', [libdaemon], [test_resources]], ['test-logging', [libdaemon]], ['test-store', [libdaemon]] ] test_resources = files( 'tests/tests.gresource.xml' ) foreach t: tests test_name = t.get(0) test_deps = [common] + t.get(1, []) test_srcs = ['tests/@0@.c'.format(test_name), t.get(2, [])] e = executable(test_name, test_srcs, dependencies: test_deps) test_env = environment() test_env.prepend('LD_PRELOAD', 'libumockdev-preload.so.0') test(test_name, e, env: test_env, timeout : 120) endforeach test_it = find_program(join_paths(srcdir, 'tests', 'test-integration')) res = run_command(test_it, 'list-tests') if res.returncode() == 0 test_env = environment() test_env.prepend('LD_PRELOAD', 'libumockdev-preload.so.0') test_env.prepend('BOLT_BUILD_DIR', meson.current_build_dir()) tests = res.stdout().strip().split(' ') foreach t: tests name = 'integration @0@'.format(t.split('.')[1]) test(name, test_it, args: [t], env: test_env, timeout: 120) endforeach endif # contrib spec_file = configure_file( input: 'contrib/bolt.spec.in', output: 'bolt.spec', configuration: subs ) # documentation build_man = build_man != false and a2x.found() if build_man foreach page : [ ['boltctl', '1'], ['boltd', '8'] ] name = page[0] section = page[1] custom_target( '@0@-man'.format(page[0]), build_by_default: true, input: 'docs/@0@.@1@.txt'.format(name, section), output: '@0@.@1@'.format(name, section), command: [ a2x, '-d', 'manpage', '-f', 'manpage', '-D', '@OUTDIR@', '-a', 'manmanual=bolt Manual', '-a', 'mansource=bolt', '-a', 'revnumber=@0@'.format(meson.project_version()), '@INPUT@' ], install: true, install_dir: join_paths(mandir, 'man@0@'.format(section)), ) endforeach endif meson.add_install_script('scripts/meson-install.sh', dbdir) # all done, phew msg = ['', 'version: @0@'.format(meson.project_version()), 'database path: @0@'.format(dbdir), 'privileged group: @0@'.format(privileged_group), '', 'udevdir: @0@'.format(udevdir), 'systemd unitdir: @0@'.format(unitdir), '', 'build manpage: @0@'.format(build_man), '' ] message('\n '.join(msg)) bolt-0.2/meson_options.txt000066400000000000000000000005011324740507400157520ustar00rootroot00000000000000option('db-path', type: 'string', value: '/var/lib/boltd', description: 'Directory for the device database') option('man', type: 'combo', choices: ['auto', 'true', 'false'], value: 'auto', description: 'Build man pages') option('privileged-group', type: 'string', value: 'wheel', description: 'Name of privileged group') bolt-0.2/policy/000077500000000000000000000000001324740507400136205ustar00rootroot00000000000000bolt-0.2/policy/org.freedesktop.bolt.policy.in000066400000000000000000000034521324740507400215120ustar00rootroot00000000000000 Thunderbolt System services https://github.com/gicmo/bolt thunderbolt-symbolic Enroll new thunderbolt devices Authentication is required to enroll thunderbolt devices thunderbolt-symbolic auth_admin auth_admin auth_admin_keep Authorize thunderbolt devices Authentication is required to authorize thunderbolt devices thunderbolt-symbolic auth_admin auth_admin auth_admin_keep Manage thunderbolt devices Authentication is required to manage thunderbolt devices thunderbolt-symbolic auth_admin auth_admin auth_admin_keep bolt-0.2/policy/org.freedesktop.bolt.rules.in000066400000000000000000000005761324740507400213510ustar00rootroot00000000000000// -*- mode: js2 -*- polkit.addRule(function(action, subject) { if ((action.id === "org.freedesktop.bolt.enroll" || action.id === "org.freedesktop.bolt.authorize" || action.id === "org.freedesktop.bolt.manage") && subject.active === true && subject.local === true && subject.isInGroup("@privileged_group@")) { return polkit.Result.YES; } }); bolt-0.2/scripts/000077500000000000000000000000001324740507400140105ustar00rootroot00000000000000bolt-0.2/scripts/meson-install.sh000066400000000000000000000003131324740507400171260ustar00rootroot00000000000000#!/bin/sh if [ -z $MESON_INSTALL_PREFIX ]; then echo 'This is meant to be run by meson' exit 1 fi BOLT_DBDIR=$1 echo "Creating database dir: ${BOLT_DBDIR}" mkdir -p "${DESTDIR}/${BOLT_DBDIR}" bolt-0.2/scripts/uncrustify.cfg000066400000000000000000000106551324740507400167130ustar00rootroot00000000000000newlines lf input_tab_size 8 output_tab_size 8 string_escape_char 92 string_escape_char2 0 # indenting indent_columns 2 indent_with_tabs 0 indent_align_string True indent_brace 2 indent_braces false indent_braces_no_func True indent_func_call_param false indent_func_def_param false indent_func_proto_param false indent_switch_case 0 indent_case_brace 2 indent_paren_close 1 # spacing sp_arith Add sp_assign Add sp_enum_assign Add sp_bool Add sp_compare Add sp_inside_paren Remove sp_inside_fparens Remove sp_func_def_paren Force sp_func_proto_paren Force sp_paren_paren Remove sp_balance_nested_parens False sp_paren_brace Remove sp_before_square Remove sp_before_squares Remove sp_inside_square Remove sp_before_ptr_star Add sp_between_ptr_star Remove sp_after_comma Add sp_before_comma Remove sp_after_cast Add sp_sizeof_paren Add sp_not Remove sp_inv Remove sp_addr Remove sp_member Remove sp_deref Remove sp_sign Remove sp_incdec Remove sp_attribute_paren remove sp_macro Force sp_func_call_paren Force sp_func_call_user_paren Remove set func_call_user _ N_ C_ g_autoptr g_auto sp_brace_typedef add sp_cond_colon add sp_cond_question add sp_defined_paren remove # alignment align_keep_tabs False align_with_tabs False align_on_tabstop False align_number_left True align_func_params True align_var_def_span 0 align_var_def_amp_style 1 align_var_def_colon true align_enum_equ_span 0 align_var_struct_span 2 align_var_def_star_style 2 align_var_def_amp_style 2 align_typedef_span 2 align_typedef_func 0 align_typedef_star_style 2 align_typedef_amp_style 2 # newlines nl_assign_leave_one_liners True nl_enum_leave_one_liners False nl_func_leave_one_liners False nl_if_leave_one_liners False nl_end_of_file Add nl_assign_brace Remove nl_func_var_def_blk 1 nl_fcall_brace Add nl_enum_brace Remove nl_struct_brace Force nl_union_brace Force nl_if_brace Force nl_brace_else Force nl_elseif_brace Force nl_else_brace Add nl_for_brace Force nl_while_brace Force nl_do_brace Force nl_brace_while Force nl_switch_brace Force nl_before_case True nl_after_case False nl_func_type_name Force nl_func_proto_type_name Remove nl_func_paren Remove nl_func_decl_start Remove nl_func_decl_args Force nl_func_decl_end Remove nl_fdef_brace Force nl_after_return False nl_define_macro False nl_create_if_one_liner False nl_create_for_one_liner False nl_create_while_one_liner False nl_after_semicolon True nl_multi_line_cond true # mod # I'd like these to be remove, but that removes brackets in if { if { foo } }, which i dislike # Not clear what to do about that... mod_full_brace_for Remove mod_full_brace_if Remove mod_full_brace_if_chain True mod_full_brace_while Remove mod_full_brace_do Remove mod_full_brace_nl 3 mod_paren_on_return Remove # line splitting #code_width = 78 ls_for_split_full True ls_func_split_full True # positioning pos_bool Trail pos_conditional Trail # custom keywords set FOR udev_list_entry_foreach bolt-0.2/scripts/uncrustify.sh000077500000000000000000000003731324740507400165650ustar00rootroot00000000000000#!/bin/sh SRCROOT=`git rev-parse --show-toplevel` CFG="$SRCROOT/scripts/uncrustify.cfg" echo "srcroot: $SRCROOT" pushd "$SRCROOT" uncrustify -c "$CFG" --no-backup `git ls-tree --name-only -r HEAD | grep \\\.[ch]$ | grep -v gvdb | grep -v build/` popd bolt-0.2/tests/000077500000000000000000000000001324740507400134635ustar00rootroot00000000000000bolt-0.2/tests/example.bolt.xml000066400000000000000000000012401324740507400165740ustar00rootroot00000000000000 bolt-0.2/tests/test-common.c000066400000000000000000000321101324740507400160710ustar00rootroot00000000000000/* * Copyright © 2017 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 library 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 . * * Authors: * Christian J. Kellner */ #include "config.h" #include "bolt-enums.h" #include "bolt-error.h" #include "bolt-fs.h" #include "bolt-io.h" #include "bolt-rnd.h" #include "bolt-str.h" #include #include #include #include #include #include #include #include #include #include #include /* unlinkat */ G_DEFINE_AUTOPTR_CLEANUP_FUNC (GEnumClass, g_type_class_unref); static void cleanup_dir (DIR *d) { struct dirent *de = NULL; for (errno = 0, de = readdir (d); de != NULL; errno = 0, de = readdir (d)) { g_autoptr(GError) error = NULL; int uflag = 0; if (!g_strcmp0 (de->d_name, ".") || !g_strcmp0 (de->d_name, "..")) continue; if (de->d_type == DT_DIR) { g_autoptr(DIR) cd = NULL; cd = bolt_opendir_at (dirfd (d), de->d_name, O_RDONLY, &error); if (cd == NULL) continue; cleanup_dir (cd); uflag = AT_REMOVEDIR; } unlinkat (dirfd (d), de->d_name, uflag); } } typedef struct { int dummy; } TestRng; static void test_enums (TestRng *tt, gconstpointer user_data) { g_autoptr(GEnumClass) klass; g_autoptr(GError) err = NULL; const char *str; gint val; gboolean ok; struct EnumTest { GType enum_type; const char *name; gint value; } ett[] = { {BOLT_TYPE_SECURITY, "none", BOLT_SECURITY_NONE}, {BOLT_TYPE_SECURITY, "dponly", BOLT_SECURITY_DPONLY}, {BOLT_TYPE_SECURITY, "user", BOLT_SECURITY_USER}, {BOLT_TYPE_SECURITY, "secure", BOLT_SECURITY_SECURE}, }; for (guint i = 0; i < G_N_ELEMENTS (ett); i++) { ok = bolt_enum_validate (ett[i].enum_type, ett[i].value, &err); g_assert_no_error (err); g_assert_true (ok); /* to string */ str = bolt_enum_to_string (ett[i].enum_type, ett[i].value, &err); g_assert_no_error (err); g_assert_nonnull (str); g_assert_cmpstr (str, ==, ett[i].name); /* from string */ val = bolt_enum_from_string (ett[i].enum_type, ett[i].name, &err); g_assert_no_error (err); g_assert_nonnull (str); g_assert_cmpint (val, ==, ett[i].value); } g_assert_cmpstr (bolt_security_to_string (BOLT_SECURITY_NONE), ==, "none"); g_assert_cmpstr (bolt_security_to_string (BOLT_SECURITY_DPONLY), ==, "dponly"); g_assert_cmpstr (bolt_security_to_string (BOLT_SECURITY_USER), ==, "user"); g_assert_cmpstr (bolt_security_to_string (BOLT_SECURITY_SECURE), ==, "secure"); g_assert_cmpuint (bolt_security_from_string ("none"), ==, BOLT_SECURITY_NONE); g_assert_cmpuint (bolt_security_from_string ("dponly"), ==, BOLT_SECURITY_DPONLY); g_assert_cmpuint (bolt_security_from_string ("user"), ==, BOLT_SECURITY_USER); g_assert_cmpuint (bolt_security_from_string ("secure"), ==, BOLT_SECURITY_SECURE); klass = g_type_class_ref (BOLT_TYPE_SECURITY); ok = bolt_enum_class_validate (klass, klass->minimum, &err); g_assert_no_error (err); g_assert_true (ok); ok = bolt_enum_class_validate (klass, klass->maximum, &err); g_assert_no_error (err); g_assert_true (ok); str = bolt_enum_to_string (BOLT_TYPE_SECURITY, klass->minimum, &err); g_assert_no_error (err); g_assert_nonnull (str); str = bolt_enum_to_string (BOLT_TYPE_SECURITY, klass->maximum, &err); g_assert_no_error (err); g_assert_nonnull (str); ok = bolt_enum_class_validate (klass, klass->maximum + 1, &err); g_assert_nonnull (err); g_assert_false (ok); g_clear_error (&err); ok = bolt_enum_class_validate (klass, klass->minimum - 1, &err); g_assert_nonnull (err); g_assert_false (ok); g_clear_error (&err); ok = bolt_enum_validate (BOLT_TYPE_SECURITY, -42, &err); g_assert_nonnull (err); g_assert_false (ok); g_clear_error (&err); str = bolt_enum_to_string (BOLT_TYPE_SECURITY, -42, &err); g_assert_nonnull (err); g_assert_null (str); g_clear_error (&err); val = bolt_enum_from_string (BOLT_TYPE_SECURITY, "ILEDELI", &err); g_assert_nonnull (err); g_assert_cmpint (val, ==, -1); g_clear_error (&err); } typedef void (*rng_t) (void *buf, gsize n); #define RNG_COUNT 258 static guint test_rng_loop (guint N, rng_t fn) { char buf[RNG_COUNT] = { 0, }; guint count[RNG_COUNT] = {0, }; guint hits = 0; for (guint n = 0; n < N; n++) { memset (buf, 0, sizeof (buf)); fn (buf, sizeof (buf)); for (guint i = 0; i < RNG_COUNT; i++) if (buf[i] == 0) count[i]++; } for (guint i = 0; i < RNG_COUNT; i++) hits = MAX (hits, count[i]); return hits; } static void no_rng (void *buf, gsize n) { /* noop */ } #if HAVE_FN_GETRANDOM static void getrandom_rng (void *buf, gsize n) { g_autoptr(GError) error = NULL; gboolean ok; ok = bolt_random_getrandom (buf, n, 0, &error); g_assert_no_error (error); g_assert_true (ok); } #endif static void test_rng (TestRng *tt, gconstpointer user_data) { char buf[10] = {0, }; guint hits; gboolean ok; static guint N = 10; hits = test_rng_loop (N, no_rng); g_assert_cmpuint (hits, ==, 10); hits = test_rng_loop (N, bolt_random_prng); g_assert_cmpuint (hits, <, 10); hits = test_rng_loop (N, (rng_t) bolt_get_random_data); g_assert_cmpuint (hits, <, 10); ok = bolt_random_urandom (buf, sizeof (buf)); if (ok) { hits = test_rng_loop (N, (rng_t) bolt_random_urandom); g_assert_cmpuint (hits, <, 10); } else { g_debug ("urandom RNG seems to not be working"); } #if HAVE_FN_GETRANDOM g_debug ("testing getrandom"); hits = test_rng_loop (N, (rng_t) getrandom_rng); g_assert_cmpuint (hits, <, 10); #else g_debug ("getrandom RNG not available"); #endif } static void test_erase (TestRng *tt, gconstpointer user_data) { char buf[256] = {0, }; char *d1, *d2, *n0 = NULL; size_t n; bolt_get_random_data (buf, sizeof (buf) - 1); d1 = g_strdup (buf); d2 = g_strdup (buf); g_assert_nonnull (d2); g_assert_nonnull (d2); /* make sure we don't crash on NULL */ bolt_str_erase (NULL); bolt_str_erase (n0); bolt_str_erase_clear (&n0); bolt_str_erase_clear (&d2); g_assert_null (d2); n = strlen (d1); bolt_str_erase (d1); g_assert_cmpstr (d1, !=, buf); g_assert_cmpuint (strlen (d1), ==, 0); for (guint i = 0; i < n; i++) g_assert_cmpint (d1[i], ==, 0); bolt_erase_n (buf, sizeof (buf)); for (guint i = 0; i < n; i++) g_assert_cmpint (buf[i], ==, 0); } typedef struct { char *path; } TestIO; static void test_io_setup (TestIO *tt, gconstpointer user_data) { g_autoptr(GError) error = NULL; tt->path = g_dir_make_tmp ("bolt.io.XXXXXX", &error); if (tt->path == NULL) { g_critical ("Could not create tmp dir: %s", error->message); return; } } static void test_io_tear_down (TestIO *tt, gconstpointer user_data) { g_autoptr(GError) error = NULL; DIR *d = NULL; d = bolt_opendir (tt->path, &error); if (d) { g_debug ("Cleaning up: %s", tt->path); cleanup_dir (d); bolt_closedir (d, NULL); bolt_rmdir (tt->path, &error); } else if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND)) { g_warning ("Could not clean up dir: %s", error->message); } } static const char *valid_uid = "f96b4cc77f196068ec454cb6006514c602d1011f47dd275cf5c6b8a47744f049"; static void test_io_verify (TestIO *tt, gconstpointer user_data) { g_autoptr(GError) error = NULL; g_autoptr(DIR) d = NULL; g_autofree char *uid_path = NULL; gboolean ok; uid_path = g_build_filename (tt->path, "unique_id", NULL); ok = g_file_set_contents (uid_path, "wrong_to_small", -1, &error); g_assert_true (ok); g_assert_no_error (error); d = bolt_opendir (tt->path, &error); g_assert_nonnull (d); g_assert_no_error (error); /* must fail */ ok = bolt_verify_uid (dirfd (d), valid_uid, &error); g_assert_false (ok); g_assert_error (error, BOLT_ERROR, BOLT_ERROR_FAILED); g_clear_error (&error); ok = g_file_set_contents (uid_path, valid_uid, -1, &error); g_assert_true (ok); g_assert_no_error (error); /* must work */ ok = bolt_verify_uid (dirfd (d), valid_uid, &error); g_assert_true (ok); g_assert_no_error (error); g_clear_error (&error); unlinkat (dirfd (d), "unique_id", 0); } static void test_fs (TestIO *tt, gconstpointer user_data) { g_autoptr(GFile) base = NULL; g_autoptr(GFile) dir = NULL; g_autoptr(GFile) target = NULL; g_autoptr(GError) error = NULL; g_autofree char *path = NULL; gboolean ok; base = g_file_new_for_path (tt->path); dir = g_file_get_child (base, "in/a/galaxy/far/far"); target = g_file_get_child (dir, "luke"); ok = bolt_fs_make_parent_dirs (target, &error); g_assert_no_error (error); g_assert_true (ok); ok = g_file_query_exists (target, NULL); g_assert_false (ok); ok = g_file_query_exists (dir, NULL); g_assert_true (ok); ok = bolt_fs_make_parent_dirs (target, &error); g_assert_no_error (error); g_assert_true (ok); ok = bolt_fs_make_parent_dirs (dir, &error); g_assert_no_error (error); g_assert_true (ok); g_clear_object (&target); target = g_file_get_child (base, "darth"); path = g_file_get_path (target); ok = g_file_set_contents (path, "vader", -1, &error); g_assert_no_error (error); g_assert_true (ok); ok = g_file_query_exists (target, NULL); g_assert_true (ok); ok = bolt_fs_make_parent_dirs (target, &error); g_assert_no_error (error); g_assert_true (ok); } static void test_str (TestRng *tt, gconstpointer user_data) { GPtrArray *a = NULL; GStrv r; r = bolt_strv_from_ptr_array (NULL); g_assert_null (r); r = bolt_strv_from_ptr_array (&a); g_assert_null (r); a = g_ptr_array_new (); r = bolt_strv_from_ptr_array (&a); g_assert_nonnull (r); g_assert_null (a); g_assert_cmpuint (0U, ==, g_strv_length (r)); g_strfreev (r); a = g_ptr_array_new (); g_ptr_array_add (a, NULL); r = bolt_strv_from_ptr_array (&a); g_assert_nonnull (r); g_assert_null (a); g_assert_cmpuint (0U, ==, g_strv_length (r)); g_strfreev (r); a = g_ptr_array_new (); g_ptr_array_add (a, g_strdup ("test")); r = bolt_strv_from_ptr_array (&a); g_assert_nonnull (r); g_assert_null (a); g_assert_cmpuint (1U, ==, g_strv_length (r)); g_assert_true (g_strv_contains ((char const * const * ) r, "test")); g_strfreev (r); } static void test_str_erase (TestRng *tt, gconstpointer user_data) { char buf[256] = {0, }; char *d1, *d2, *n0 = NULL; size_t n; bolt_get_random_data (buf, sizeof (buf) - 1); d1 = g_strdup (buf); d2 = g_strdup (buf); g_assert_nonnull (d2); g_assert_nonnull (d2); /* make sure we don't crash on NULL */ bolt_str_erase (NULL); bolt_str_erase (n0); bolt_str_erase_clear (&n0); bolt_str_erase_clear (&d2); g_assert_null (d2); n = strlen (d1); bolt_str_erase (d1); g_assert_cmpstr (d1, !=, buf); g_assert_cmpuint (strlen (d1), ==, 0); for (guint i = 0; i < n; i++) g_assert_cmpint (d1[i], ==, 0); bolt_erase_n (buf, sizeof (buf)); for (guint i = 0; i < n; i++) g_assert_cmpint (buf[i], ==, 0); } int main (int argc, char **argv) { setlocale (LC_ALL, ""); g_test_init (&argc, &argv, NULL); g_test_add ("/common/enums", TestRng, NULL, NULL, test_enums, NULL); g_test_add ("/common/rng", TestRng, NULL, NULL, test_rng, NULL); g_test_add ("/common/erase", TestRng, NULL, NULL, test_erase, NULL); g_test_add ("/common/io/verify", TestIO, NULL, test_io_setup, test_io_verify, test_io_tear_down); g_test_add ("/common/fs", TestIO, NULL, test_io_setup, test_fs, test_io_tear_down); g_test_add ("/common/str", TestRng, NULL, NULL, test_str, NULL); g_test_add ("/common/str/erase", TestRng, NULL, NULL, test_str_erase, NULL); return g_test_run (); } bolt-0.2/tests/test-exported.c000066400000000000000000000661231324740507400164460ustar00rootroot00000000000000/* * Copyright © 2018 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 library 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 . * * Authors: * Christian J. Kellner */ #include "config.h" #include "bolt-exported.h" #include "bolt-enums.h" #include "bolt-error.h" #include "bolt-str.h" #include "bolt-test-resources.h" #include #include #include #include /* */ #define DBUS_IFACE "org.gnome.bolt.Example" #define BT_TYPE_EXPORTED bt_exported_get_type () G_DECLARE_FINAL_TYPE (BtExported, bt_exported, BT, EXPORTED, BoltExported); static gboolean handle_ping (BoltExported *obj, GVariant *params, GDBusMethodInvocation *inv); static gboolean handle_authorize_method (BoltExported *exported, GDBusMethodInvocation *inv, GError **error, gpointer user_data); static gboolean handle_authorize_property (BoltExported *exported, const char *name, gboolean setting, GDBusMethodInvocation *invocation, GError **error, gpointer user_data); static gboolean handle_set_str_rw (BoltExported *obj, const char *name, const GValue *value, GError **error); static gboolean handle_set_security (BoltExported *obj, const char *name, const GValue *value, GError **error); struct _BtExported { BoltExported parent; char *str; GError *setter_err; gboolean prop_bool; gboolean authorize_methods; gboolean authorize_properties; BoltSecurity security; }; G_DEFINE_TYPE (BtExported, bt_exported, BOLT_TYPE_EXPORTED); enum { PROP_0, PROP_STR, PROP_STR_RW, PROP_STR_RW_NOSETTER, PROP_BOOL, PROP_SECURITY, PROP_LAST }; static GParamSpec *props[PROP_LAST] = {NULL, }; static void bt_exported_finalize (GObject *object) { BtExported *be = BT_EXPORTED (object); g_free (be->str); G_OBJECT_CLASS (bt_exported_parent_class)->finalize (object); } static void bt_exported_init (BtExported *be) { be->str = g_strdup ("strfoo"); } static void bt_exported_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { BtExported *be = BT_EXPORTED (object); switch (prop_id) { case PROP_STR: case PROP_STR_RW: case PROP_STR_RW_NOSETTER: g_value_set_string (value, be->str); break; case PROP_BOOL: g_value_set_boolean (value, be->prop_bool); break; case PROP_SECURITY: g_value_set_enum (value, be->security); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); } } static void bt_exported_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { BtExported *be = BT_EXPORTED (object); switch (prop_id) { case PROP_STR: case PROP_STR_RW: case PROP_STR_RW_NOSETTER: g_clear_pointer (&be->str, g_free); be->str = g_value_dup_string (value); break; case PROP_BOOL: be->prop_bool = g_value_get_boolean (value); break; case PROP_SECURITY: be->security = g_value_get_enum (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); } } static void bt_exported_class_init (BtExportedClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); BoltExportedClass *exported_class = BOLT_EXPORTED_CLASS (klass); gobject_class->finalize = bt_exported_finalize; gobject_class->get_property = bt_exported_get_property; gobject_class->set_property = bt_exported_set_property; bolt_exported_class_set_interface_info (exported_class, DBUS_IFACE, "/bolt/tests/exported/example.bolt.xml"); props[PROP_STR] = g_param_spec_string ("str", "StrFoo", NULL, NULL, G_PARAM_READABLE | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB); props[PROP_STR_RW] = g_param_spec_string ("str-rw", "StrRW", NULL, NULL, G_PARAM_READWRITE | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB); props[PROP_STR_RW_NOSETTER] = g_param_spec_string ("str-rw-nosetter", "StrRWNoSetter", NULL, NULL, G_PARAM_READABLE | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB); props[PROP_BOOL] = g_param_spec_boolean ("bool", "Bool", NULL, FALSE, G_PARAM_READWRITE | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB); props[PROP_SECURITY] = g_param_spec_enum ("security", "Security", NULL, BOLT_TYPE_SECURITY, BOLT_SECURITY_UNKNOWN, G_PARAM_READWRITE | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB); g_object_class_install_properties (gobject_class, PROP_LAST, props); bolt_exported_class_export_properties (exported_class, PROP_STR, PROP_LAST, props); bolt_exported_class_export_method (exported_class, "Ping", handle_ping); bolt_exported_class_property_setter (exported_class, props[PROP_STR_RW], handle_set_str_rw); bolt_exported_class_property_setter (exported_class, props[PROP_SECURITY], handle_set_security); } static gboolean handle_authorize_method (BoltExported *exported, GDBusMethodInvocation *inv, GError **error, gpointer user_data) { BtExported *be = BT_EXPORTED (user_data); gboolean authorize = be->authorize_methods; const char *name = g_dbus_method_invocation_get_method_name (inv); g_debug ("authorizing method %s (%s)", name, authorize ? "y" : "n" ); if (!authorize) g_set_error (error, G_DBUS_ERROR, G_DBUS_ERROR_ACCESS_DENIED, "denying property write access for %s", name); return authorize; } static gboolean handle_authorize_property (BoltExported *exported, const char *name, gboolean setting, GDBusMethodInvocation *inv, GError **error, gpointer user_data) { BtExported *be = BT_EXPORTED (user_data); gboolean authorize = be->authorize_properties; g_debug ("authorizing property %s (%s)", name, authorize ? "y" : "n" ); if (!authorize) g_set_error (error, G_DBUS_ERROR, G_DBUS_ERROR_ACCESS_DENIED, "denying property write access for %s", name); return authorize; } static void bt_exported_install_method_authorizer (BtExported *be) { g_signal_connect (be, "authorize-method", G_CALLBACK (handle_authorize_method), be); } static void bt_exported_install_property_authorizer (BtExported *be) { g_signal_connect (be, "authorize-property", G_CALLBACK (handle_authorize_property), be); } static gboolean handle_ping (BoltExported *obj, GVariant *params, GDBusMethodInvocation *inv) { // BtExported *be = BT_EXPORTED (obj); g_dbus_method_invocation_return_value (inv, g_variant_new ("(s)", "PONG")); return TRUE; } static gboolean handle_set_str_rw (BoltExported *obj, const char *name, const GValue *value, GError **error) { BtExported *be = BT_EXPORTED (obj); g_printerr ("handling set str-rw\n"); if (be->setter_err) { g_printerr ("signaling error\n"); //*error = g_error_copy (be->setter_err); g_set_error_literal (error, be->setter_err->domain, be->setter_err->code, be->setter_err->message); return FALSE; } g_clear_pointer (&be->str, g_free); be->str = g_value_dup_string (value); return TRUE; } static gboolean handle_set_security (BoltExported *obj, const char *name, const GValue *value, GError **error) { BtExported *be = BT_EXPORTED (obj); be->security = g_value_get_enum (value); return TRUE; } /* *********** */ static GTestDBus *test_bus; typedef struct { GDBusConnection *bus; BtExported *obj; const char *bus_name; const char *obj_path; } TestExported; static void test_exported_setup (TestExported *tt, gconstpointer data) { g_autoptr(GError) err = NULL; const char *obj_path = "/obj"; gboolean ok; tt->bus = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &err); g_assert_no_error (err); g_assert_nonnull (tt->bus); g_assert_false (g_dbus_connection_is_closed (tt->bus)); tt->obj = g_object_new (BT_TYPE_EXPORTED, NULL); g_assert_nonnull (tt->obj); ok = bolt_exported_export (BOLT_EXPORTED (tt->obj), tt->bus, obj_path, &err); g_assert_no_error (err); g_assert_true (ok); tt->obj_path = bolt_exported_get_object_path (BOLT_EXPORTED (tt->obj)); g_assert_cmpstr (tt->obj_path, ==, obj_path); tt->bus_name = g_dbus_connection_get_unique_name (tt->bus); } static void test_exported_teardown (TestExported *tt, gconstpointer data) { gboolean ok; ok = bolt_exported_unexport (BOLT_EXPORTED (tt->obj)); g_assert_true (ok); g_clear_object (&tt->bus); g_clear_object (&tt->obj); } typedef struct CallCtx { GMainLoop *loop; GVariant *data; GError *error; } CallCtx; static CallCtx * call_ctx_new (void) { CallCtx *ctx = g_new0 (CallCtx, 1); ctx->loop = g_main_loop_new (NULL, FALSE); return ctx; } static void call_ctx_reset (CallCtx *ctx) { if (ctx->data) g_variant_unref (ctx->data); if (ctx->error) g_clear_error (&ctx->error); } static void call_ctx_free (CallCtx *ctx) { g_main_loop_unref (ctx->loop); call_ctx_reset (ctx); } G_DEFINE_AUTOPTR_CLEANUP_FUNC (CallCtx, call_ctx_free); static void dbus_call_done (GObject *source_object, GAsyncResult *res, gpointer user_data) { CallCtx *ctx = user_data; GDBusConnection *bus = G_DBUS_CONNECTION (source_object); ctx->data = g_dbus_connection_call_finish (bus, res, &ctx->error); g_main_loop_quit (ctx->loop); } static void call_ctx_run (CallCtx *ctx) { call_ctx_reset (ctx); g_main_loop_run (ctx->loop); } static void test_exported_basic (TestExported *tt, gconstpointer data) { g_autoptr(GError) error = NULL; g_autoptr(CallCtx) ctx = NULL; GDBusConnection *bus; gboolean ok; const char *str = NULL; ctx = call_ctx_new (); bus = tt->bus; ok = bolt_exported_export (BOLT_EXPORTED (tt->obj), tt->bus, tt->obj_path, &error); g_assert_error (error, G_IO_ERROR, G_IO_ERROR_EXISTS); g_assert_false (ok); g_clear_error (&error); /* unknown method */ g_dbus_connection_call (bus, tt->bus_name, tt->obj_path, DBUS_IFACE, "UnknownMethodFooBarSee", NULL, G_VARIANT_TYPE ("(s)"), G_DBUS_CALL_FLAGS_NONE, 2000, NULL, dbus_call_done, ctx); call_ctx_run (ctx); g_assert_error (ctx->error, G_DBUS_ERROR, G_DBUS_ERROR_UNKNOWN_METHOD); /* authorization missing */ g_dbus_connection_call (bus, tt->bus_name, tt->obj_path, DBUS_IFACE, "Ping", NULL, G_VARIANT_TYPE ("(s)"), G_DBUS_CALL_FLAGS_NONE, 2000, NULL, dbus_call_done, ctx); call_ctx_run (ctx); g_assert_error (ctx->error, G_DBUS_ERROR, G_DBUS_ERROR_ACCESS_DENIED); bt_exported_install_method_authorizer (tt->obj); tt->obj->authorize_methods = TRUE; g_dbus_connection_call (bus, tt->bus_name, tt->obj_path, DBUS_IFACE, "Ping", NULL, G_VARIANT_TYPE ("(s)"), G_DBUS_CALL_FLAGS_NONE, 2000, NULL, dbus_call_done, ctx); call_ctx_run (ctx); g_assert_no_error (ctx->error); g_assert_nonnull (ctx->data); g_variant_get (ctx->data, "(&s)", &str); g_assert_cmpstr (str, ==, "PONG"); } static void test_exported_props (TestExported *tt, gconstpointer data) { g_autoptr(CallCtx) ctx = NULL; g_autoptr(GVariant) v = NULL; const char *str; ctx = call_ctx_new (); /* unknown property */ g_dbus_connection_call (tt->bus, tt->bus_name, tt->obj_path, "org.freedesktop.DBus.Properties", "Get", g_variant_new ("(ss)", DBUS_IFACE, "UnknownProperty"), G_VARIANT_TYPE ("(v)"), G_DBUS_CALL_FLAGS_NONE, 2000, NULL, dbus_call_done, ctx); call_ctx_run (ctx); g_assert_error (ctx->error, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS); /* */ g_dbus_connection_call (tt->bus, tt->bus_name, tt->obj_path, "org.freedesktop.DBus.Properties", "Get", g_variant_new ("(ss)", DBUS_IFACE, "StrFoo"), G_VARIANT_TYPE ("(v)"), G_DBUS_CALL_FLAGS_NONE, 2000, NULL, dbus_call_done, ctx); call_ctx_run (ctx); g_assert_no_error (ctx->error); g_assert_nonnull (ctx->data); g_variant_get (ctx->data, "(v)", &v); str = g_variant_get_string (v, NULL); g_assert_cmpstr (str, ==, tt->obj->str); /* property setter - read only property */ g_dbus_connection_call (tt->bus, tt->bus_name, tt->obj_path, "org.freedesktop.DBus.Properties", "Set", g_variant_new ("(ssv)", DBUS_IFACE, "StrFoo", g_variant_new ("s", "se")), NULL, G_DBUS_CALL_FLAGS_NONE, 2000, NULL, dbus_call_done, ctx); call_ctx_run (ctx); g_assert_error (ctx->error, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS); /* property setter - no setter */ g_dbus_connection_call (tt->bus, tt->bus_name, tt->obj_path, "org.freedesktop.DBus.Properties", "Set", g_variant_new ("(ssv)", DBUS_IFACE, "StrRWNoSetter", g_variant_new ("s", "se")), NULL, G_DBUS_CALL_FLAGS_NONE, 2000, NULL, dbus_call_done, ctx); call_ctx_run (ctx); g_assert_error (ctx->error, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS); /* property setter - not authorized */ g_dbus_connection_call (tt->bus, tt->bus_name, tt->obj_path, "org.freedesktop.DBus.Properties", "Set", g_variant_new ("(ssv)", DBUS_IFACE, "StrRW", g_variant_new ("s", "se")), NULL, G_DBUS_CALL_FLAGS_NONE, 2000, NULL, dbus_call_done, ctx); call_ctx_run (ctx); g_assert_error (ctx->error, G_DBUS_ERROR, G_DBUS_ERROR_ACCESS_DENIED); /* install the auth handler, be reject the auth request in it */ bt_exported_install_property_authorizer (tt->obj); tt->obj->authorize_properties = FALSE; g_dbus_connection_call (tt->bus, tt->bus_name, tt->obj_path, "org.freedesktop.DBus.Properties", "Set", g_variant_new ("(ssv)", DBUS_IFACE, "StrRW", g_variant_new ("s", "se")), NULL, G_DBUS_CALL_FLAGS_NONE, 2000, NULL, dbus_call_done, ctx); call_ctx_run (ctx); g_assert_error (ctx->error, G_DBUS_ERROR, G_DBUS_ERROR_ACCESS_DENIED); /* property setter - allow it, but signal an error during property setting */ tt->obj->authorize_properties = TRUE; g_set_error (&tt->obj->setter_err, BOLT_ERROR, BOLT_ERROR_CFG, "failed"); g_dbus_connection_call (tt->bus, tt->bus_name, tt->obj_path, "org.freedesktop.DBus.Properties", "Set", g_variant_new ("(ssv)", DBUS_IFACE, "StrRW", g_variant_new ("s", "se")), NULL, G_DBUS_CALL_FLAGS_NONE, 2000, NULL, dbus_call_done, ctx); call_ctx_run (ctx); g_assert_error (ctx->error, BOLT_ERROR, BOLT_ERROR_CFG); g_clear_error (&tt->obj->setter_err); /* property setter - allow it, should work now */ tt->obj->authorize_properties = TRUE; str = "new property value"; g_dbus_connection_call (tt->bus, tt->bus_name, tt->obj_path, "org.freedesktop.DBus.Properties", "Set", g_variant_new ("(ssv)", DBUS_IFACE, "StrRW", g_variant_new ("s", str)), NULL, G_DBUS_CALL_FLAGS_NONE, 2000, NULL, dbus_call_done, ctx); call_ctx_run (ctx); g_assert_no_error (ctx->error); g_assert_cmpstr (str, ==, tt->obj->str); } static void props_changed_signal (GDBusConnection *connection, const gchar *sender_name, const gchar *object_path, const gchar *interface_name, const gchar *signal_name, GVariant *parameters, gpointer user_data) { CallCtx *ctx = user_data; const gchar *interface_name_for_signal; GVariant *changed_properties; g_auto(GStrv) invalidated_properties = NULL; g_variant_get (parameters, "(&s@a{sv}^a&s)", &interface_name_for_signal, &changed_properties, &invalidated_properties); g_debug ("got prop changes signal"); ctx->data = changed_properties; /* transfer ownership */ g_main_loop_quit (ctx->loop); } static void test_exported_props_changed (TestExported *tt, gconstpointer data) { g_autoptr(CallCtx) ctx = NULL; g_autoptr(GVariantIter) iter = NULL; gboolean have_bool = FALSE; gboolean have_str = FALSE; GVariant *value; const char *key; guint sid; ctx = call_ctx_new (); sid = g_dbus_connection_signal_subscribe (tt->bus, tt->bus_name, "org.freedesktop.DBus.Properties", "PropertiesChanged", tt->obj_path, DBUS_IFACE, G_DBUS_SIGNAL_FLAGS_NONE, props_changed_signal, ctx, NULL); g_assert_cmpuint (sid, >, 0); /* g_signal_connect (tt->obj, "notify::str-rw", */ /* G_CALLBACK (call_ctx_stop_on_notify), */ /* ctx); */ g_object_set (tt->obj, "str-rw", "huhu", "bool", TRUE, NULL); G_DEBUG_HERE (); call_ctx_run (ctx); g_assert_no_error (ctx->error); g_assert_nonnull (ctx->data); g_variant_get (ctx->data, "a{sv}", &iter); while (g_variant_iter_next (iter, "{&sv}", &key, &value)) { if (bolt_streq (key, "Bool")) { have_bool = TRUE; g_assert_true (g_variant_get_boolean (value) == tt->obj->prop_bool); } else if (bolt_streq (key, "StrRW")) { have_str = TRUE; g_assert_cmpstr (g_variant_get_string (value, NULL), ==, tt->obj->str); } g_variant_unref (value); } g_assert_true (have_bool); g_assert_true (have_str); } static void test_exported_props_enums (TestExported *tt, gconstpointer data) { g_autoptr(CallCtx) ctx = NULL; g_autoptr(GVariant) v = NULL; const char *have; const char *want; ctx = call_ctx_new (); tt->obj->security = BOLT_SECURITY_SECURE; /* valid property value, should be converted to string */ g_dbus_connection_call (tt->bus, tt->bus_name, tt->obj_path, "org.freedesktop.DBus.Properties", "Get", g_variant_new ("(ss)", DBUS_IFACE, "Security"), G_VARIANT_TYPE ("(v)"), G_DBUS_CALL_FLAGS_NONE, 2000, NULL, dbus_call_done, ctx); call_ctx_run (ctx); g_assert_no_error (ctx->error); g_assert_nonnull (ctx->data); g_variant_get (ctx->data, "(v)", &v); have = g_variant_get_string (v, NULL); want = bolt_security_to_string (tt->obj->security); g_assert_cmpstr (have, ==, want); /* setter */ bt_exported_install_property_authorizer (tt->obj); tt->obj->authorize_properties = TRUE; have = bolt_security_to_string (BOLT_SECURITY_USER); g_dbus_connection_call (tt->bus, tt->bus_name, tt->obj_path, "org.freedesktop.DBus.Properties", "Set", g_variant_new ("(ssv)", DBUS_IFACE, "Security", g_variant_new ("s", have)), NULL, G_DBUS_CALL_FLAGS_NONE, 2000, NULL, dbus_call_done, ctx); call_ctx_run (ctx); g_assert_no_error (ctx->error); g_assert_cmpint (tt->obj->security, ==, BOLT_SECURITY_USER); } int main (int argc, char **argv) { int res; setlocale (LC_ALL, ""); g_test_init (&argc, &argv, NULL); g_resources_register (bolt_test_get_resource ()); g_test_add ("/exported/basic", TestExported, NULL, test_exported_setup, test_exported_basic, test_exported_teardown); g_test_add ("/exported/props", TestExported, NULL, test_exported_setup, test_exported_props, test_exported_teardown); g_test_add ("/exported/props/changed", TestExported, NULL, test_exported_setup, test_exported_props_changed, test_exported_teardown); g_test_add ("/exported/props/enums", TestExported, NULL, test_exported_setup, test_exported_props_enums, test_exported_teardown); test_bus = g_test_dbus_new (G_TEST_DBUS_NONE); g_test_dbus_up (test_bus); g_assert_nonnull (test_bus); g_debug ("test bus at %s\n", g_getenv ("DBUS_SESSION_BUS_ADDRESS")); res = g_test_run (); g_test_dbus_down (test_bus); g_clear_object (&test_bus); return res; } bolt-0.2/tests/test-integration000077500000000000000000001212011324740507400167060ustar00rootroot00000000000000#!/usr/bin/python3 # # bolt integration test suite # # Copyright © 2017 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 . # Authors: # Christian J. Kellner import binascii import os import shutil import sys import subprocess import unittest import uuid import tempfile import time from collections import namedtuple from itertools import chain try: import gi from gi.repository import GLib from gi.repository import Gio gi.require_version('UMockdev', '1.0') from gi.repository import UMockdev import dbus import dbusmock except ImportError as e: sys.stderr.write('Skipping integration test due to missing depdendencies: %s\n' % str(e)) sys.exit(0) try: from subprocess import DEVNULL except ImportError: DEVNULL = open(os.devnull, 'wb') DBUS_NAME = 'org.freedesktop.bolt' DBUS_PATH = '/org/freedesktop/bolt' DBUS_IFACE_PREFIX = 'org.freedesktop.bolt1.' DBUS_IFACE_MANAGER = DBUS_IFACE_PREFIX + 'Manager' DBUS_IFACE_DEVICE = DBUS_IFACE_PREFIX + 'Device' SERVICE_FILE = '/usr/share/dbus-1/system-services/org.freedesktop.bolt.service' def get_timeout(topic='default'): vals = { 'valgrind': { 'default': 20, 'daemon_start': 60 }, 'default': { '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] class Signal(object): def __init__(self, name): self.name = name self.callbacks = set() self.notify = None self._bridge = None def connect(self, callback): self.callbacks.add(callback) if self.notify is not None: self.notify(self, 'connect', len(self.callbacks)) if len(self.callbacks) == 1: self._bridge_build() def disconnect(self, callback): self.callbacks.remove(callback) if self.notify is not None: self.notify(self, 'disconnect', len(self.callbacks)) if len(self.callbacks) == 0: self._bridge_destory() def disconnect_all(self): self.callbacks = set() if self.notify is not None: self.notify(self, 'disconnect', 0) self._bridge_destory() def emit(self, *args, **kwargs): res = [cb(*args, **kwargs) for cb in self.callbacks] return any(res) def bridge(self, obj, name, callback): if self._bridge is not None: raise ValueError('already bridged') self._bridge = {'object': obj, 'name': name} if callback is not None: self._bridge['filter'] = callback def birdge_destroy(self): self._bridge = None def _bridge_build(self): if self._bridge is None: return b = self._bridge signal_id = b['object'].connect(b['name'], self._bridge_signal) b['signal_id'] = signal_id def _bridge_destory(self): if self._bridge is None: return b = self._bridge b['object'].disconnect(b['signal_id']) del b['signal_id'] def _bridge_signal(self, *args, **kwargs): if 'filter' in self._bridge: res, args, kwargs = self._bridge['filter'](args, kwargs) if not res: return return self.emit(*args, **kwargs) def __call__(self, *args, **kwargs): return self.emit(*args, **kwargs) def __iadd__(self, callback): self.connect(callback) return self def __isub__(self, callback): self.disconnect(callback) return self @staticmethod def enable(klass): lst = getattr(klass, 'signals', []) methods = [m for m in dir(klass) if not m.startswith('__')] def install(l): if l is None: return if l in methods: print('WARNING: signal "%s" will overwrite method' % l, file=sys.stderr) def get_signals(self): signals = getattr(self, '__signals', None) if signals is None: signals = {} setattr(self, '__signals', signals) return signals def get_signal(self): signals = get_signals(self) if l not in signals: signals[l] = Signal(l) return signals[l] def getter(self): return get_signal(self) def setter(self, value): return get_signal(self) p = property(getter, setter) setattr(klass, l, p) return l bases = klass.__bases__ ps = {s for b in bases for s in getattr(b, 'signals', [])} klass.signals = list(ps.union({install(l) for l in lst})) return klass @Signal.enable class Recorder(object): Event = namedtuple('Event', ['what', 'name', 'details', 'time']) signals = ['event'] def __init__(self, target): self.recording = True self.events = [] self.target = target self.target.g_properties_changed += self._on_props_changed self.target.g_signal += self._on_signal def close(self): if not self.recording: return self.target.g_properties_changed -= self._on_props_changed self.target.g_signal -= self._on_signal self.recording = False return self.events def __enter__(self): return self def __exit__(self, exc_type, exc_val, exc_tb): self.close() def _on_props_changed(self, props): now = time.time() for k, v in props.items(): e = self.Event('property', k, v, now) self._add_event(e) def _on_signal(self, proxy, sender, signal, params): now = time.time() e = self.Event('signal', signal, None, now) self._add_event(e) def _add_event(self, event): self.events.append(event) self.event.emit(event) @staticmethod def event_match(e, target): if e.what != target.what or e.name != target.name: return False if target.details is None: return True return e.details == target.details def events(self, what, name, details=None): t = self.Event(what, name, details, None) return list(filter(self.event_match(t), self.events)) def have_event(self, what, name, details=None): return len(self.events(what, name, details=details)) > 0 def wait_for_events(self, lst, timeout=None): loop = GLib.MainLoop() def got_event(event): for idx, e in enumerate(lst): if self.event_match(e, event): del lst[idx] break if len(lst) == 0: loop.quit() def got_timeout(): print('WARNING: timeout reached! Waiting list: ', str(lst), file=sys.stderr) loop.quit() self.event += got_event timeout = timeout or get_timeout() GLib.timeout_add(timeout*1000, got_timeout) loop.run() self.event -= got_event return len(lst) == 0 def wait_for_event(self, what, name, details=None): t = self.Event(what, name, details, None) return self.wait_for_events([t]) @Signal.enable class ProxyWrapper(object): signals = ['g_properties_changed', 'g_signal'] def __init__(self, bus, iname, path): self._proxy = Gio.DBusProxy.new_sync(bus, Gio.DBusProxyFlags.DO_NOT_AUTO_START, None, DBUS_NAME, path, iname, None) def props_changed(args, kwargs): return True, [args[1].unpack()], {} self.g_properties_changed.bridge(self._proxy, 'g-properties-changed', props_changed) self.g_signal.bridge(self._proxy, 'g-signal', None) def __getattr__(self, name): if name.startswith('_'): raise AttributeError if '_' in name: c = name.split('_') name = "".join(x.title() for x in c) else: name = name[0].upper() + name[1:] if name in self._proxy.get_cached_property_names(): value = self._proxy.get_cached_property(name) if value is not None: return value.unpack() else: return value return getattr(self._proxy, name) def record(self): return Recorder(self) @property def object_path(self): return self._proxy.get_object_path() class BoltDevice(ProxyWrapper): UNKNOWN = -1 DISCONNECTED = 0 CONNECTED = 1 AUTHORIZING = 2 AUTH_ERROR = 3 AUTHORIZED = 4 AUTHORIZED_SECURE = 5 AUTHORIZED_NEWKEY = 6 KEY_MISSING = 0 KEY_HAVE = 1 KEY_NEW = 2 def __init__(self, bus, path): super(BoltDevice, self).__init__(bus, DBUS_IFACE_DEVICE, path) @property def is_connected(self): return self.status > self.DISCONNECTED @property def is_authorized(self): return self.status >= self.AUTHORIZED def authorize(self, flags=""): self.Authorize('(s)', flags) return True @property def status(self): res = getattr(self, 'Status') mapping = {'unknown': self.UNKNOWN, 'disconnected': self.DISCONNECTED, 'connected': self.CONNECTED, 'authorizing': self.AUTHORIZING, 'auth-error': self.AUTH_ERROR, 'authorized': self.AUTHORIZED, 'authorized-secure': self.AUTHORIZED_SECURE, 'authorized-newkey': self.AUTHORIZED_NEWKEY} return mapping.get(res, self.UNKNOWN) @property def key(self): res = getattr(self, 'Key') mapping = {'missing': self.KEY_MISSING, 'have': self.KEY_HAVE, 'new': self.KEY_NEW} return mapping.get(res, self.MISSING) @property def label(self): res = getattr(self, 'Label') if res is not None and len(res) < 1: return None return res @label.setter def label(self, value): if isinstance(value, str): value = GLib.Variant("s", value) res = self._proxy.call_sync('org.freedesktop.DBus.Properties.Set', GLib.Variant("(ssv)", (DBUS_IFACE_DEVICE, "Label", value)), 0, -1, None) return res is not None @Signal.enable class BoltClient(ProxyWrapper): signals = ['device_added', 'device_removed'] POLICY_DEFAULT = 'default' POLICY_MANUAL = 'manual' POLICY_AUTO = 'auto' def __init__(self, bus): super(BoltClient, self).__init__(bus, DBUS_IFACE_MANAGER, DBUS_PATH) self._proxy.connect('g-signal', self._on_dbus_signal) def _on_dbus_signal(self, proxy, sender, signal, params): bus = self._proxy.get_connection() if signal == 'DeviceAdded': self.device_added.emit(BoltDevice(bus, params[0])) return True elif signal == 'DeviceRemoved': self.device_removed.emit(params[0]) return True return False def list_devices(self): devices = self.ListDevices() if devices is None: return None bus = self._proxy.get_connection() return [BoltDevice(bus, d) for d in devices] def device_by_uid(self, uid): object_path = self.DeviceByUid("(s)", uid) if object_path is None: return None bus = self._proxy.get_connection() return BoltDevice(bus, object_path) def enroll(self, uid, policy=POLICY_DEFAULT, flags=""): object_path = self.EnrollDevice("(sss)", uid, policy, flags) if object_path is None: return None bus = self._proxy.get_connection() return BoltDevice(bus, object_path) def forget(self, uid): self.ForgetDevice("(s)", uid) return True # Mock Device Tree @Signal.enable class Device(object): subsystem = "unknown" udev_attrs = [] udev_props = [] signals = ['device_connected', 'device_disconnected'] def __init__(self, name, children): self._parent = None self.children = [self._adopt(c) for c in children] self.udev = None self.name = name self.syspath = None def _adopt(self, device): device.parent = self return device def _get_own(self, items): i = chain.from_iterable([a, str(getattr(self, a.lower()))] for a in items) return list(i) def collect(self, predicate): children = self.children head = [self] if predicate(self) else [] tail = chain.from_iterable(c.collect(predicate) for c in children) return head + list(filter(predicate, tail)) def first(self, predicate): if predicate(self): return self for c in self.children: found = c.first(predicate) if found: return found @property def parent(self): return self._parent @parent.setter def parent(self, value): self._parent = value @property def root(self): return self if self.parent is None else self.parent.root def connect_tree(self, bed): self.connect(bed) for c in self.children: c.connect_tree(bed) def connect(self, bed): print('connecting ' + self.name, file=sys.stderr) assert self.syspath is None attributes = self._get_own(self.udev_attrs) properties = self._get_own(self.udev_props) sysparent = self.parent and self.parent.syspath self.syspath = bed.add_device(self.subsystem, self.name, sysparent, attributes, properties) self.root.device_connected(self) self.testbed = bed def disconnect(self, bed): print('disconnecting ' + self.name, file=sys.stderr) for c in self.children: c.disconnect(bed) bed.uevent(self.syspath, "remove") bed.remove_device(self.syspath) self.authorized = 0 self.key = "" self.root.device_disconnected(self) self.syspath = None self.testbed = None class TbDevice(Device): subsystem = "thunderbolt" devtype = "thunderbolt_device" udev_attrs = ['authorized', 'device', 'device_name', 'key', 'unique_id', 'vendor', 'vendor_name'] udev_props = ['DEVTYPE'] def __init__(self, name, authorized=0, vendor=None, uid=None, children=None): super(TbDevice, self).__init__(name, children or []) self.unique_id = uid or str(uuid.uuid4()) self.device_name = 'Thunderbolt ' + name self.device = self._make_id(self.device_name) self.vendor_name = vendor or 'GNOME.org' self.vendor = self._make_id(self.vendor_name) self.authorized = authorized self.key = "" def _make_id(self, name): return '0x%X' % binascii.crc32(name.encode('utf-8')) @property def authorized_file(self): if self.syspath is None: return None return os.path.join(self.syspath, 'authorized') @property def bolt_status(self): if self.syspath is None: return BoltDevice.DISCONNECTED elif self.authorized == 0: return BoltDevice.CONNECTED elif self.authorized == 1: if self.key == "": return BoltDevice.AUTHORIZED else: return BoltDevice.AUTHORIZED_NEWKEY elif self.authorized == 2: return BoltDevice.AUTHORIZED_SECURE @property def domain(self): return self.parent.domain @staticmethod def is_unauthorized(d): return isinstance(d, TbDevice) and d.authorized == 0 def reload_auth(self): authorized = self.authorized key = self.key f = self.authorized_file with open(self.authorized_file, 'r') as f: data = f.read() self.authorized = int(data) with open(os.path.join(self.syspath, 'key'), 'r') as f: self.key = f.read().strip() if self.authorized != authorized or self.key != key: if self.syspath: self.testbed.uevent(self.syspath, 'change') class TbHost(TbDevice): def __init__(self, children): super(TbHost, self).__init__('Laptop', authorized=1, uid='3b7d4bad-4fdf-44ff-8730-ffffdeadbabe', children=children) def connect(self, bed): self.authorized = 1 super(TbHost, self).connect(bed) class TbDomain(Device): subsystem = "thunderbolt" devtype = "thunderbolt_domain" udev_attrs = ['security'] udev_props = ['DEVTYPE'] SECURITY_NONE = 'none' SECURITY_USER = 'user' SECURITY_SECURE = 'secure' def __init__(self, security=SECURITY_SECURE, index=0, host=None): assert host assert isinstance(host, TbHost) name = 'domain%d' % index super(TbDomain, self).__init__(name, children=[host]) self.security = security @property def devices(self): return self.collect(lambda c: isinstance(c, TbDevice)) @property def domain(self): return self @staticmethod def checkattr(d, k, v): return hasattr(d, k) and getattr(d, k) == v def find(self, **kwargs): def finder(d): return all([self.checkattr(d, k, v) for k, v in kwargs.items()]) return self.first(finder) class TreeChecker(object): def __init__(self, client, tree): self.client = client self.tree = tree self.remote_devices = {} devices = client.list_devices() [self._register_device(d) for d in devices] client.device_added += self._on_device_added client.device_removed += self._on_device_removed tree.device_connected += self._on_udev_connected tree.device_disconnected += self._on_udev_disconnected self.actions = {} self.loop = None # signal handler for local (udev) devices def _on_udev_connected(self, dev): if not isinstance(dev, TbDevice): return uid = dev.unique_id self.actions[uid] = 'connected' def _on_udev_disconnected(self, dev): if not isinstance(dev, TbDevice): return uid = dev.unique_id if uid in self.actions: del self.actions[uid] else: self.actions[uid] = 'disconnected' # signal handler for remote devices def _on_device_added(self, dev): self._register_device(dev) self._check_action(dev.uid, 'connected') def _on_device_removed(self, object_path): dev = self.remote_devices.get(object_path, None) if dev is None: return self._check_action(dev.uid, 'disconnected') self._unregister_device(dev) # book keeping of remote devices def _register_device(self, dev): self.remote_devices[dev.object_path] = dev return dev def _unregister_device(self, dev): del self.remote_devices[dev.object_path] def _check_action(self, uid, action): if self.actions.get(uid, None) != action: return False del self.actions[uid] self._check_sync() return True def _check_sync(self): keep_going = len(self.actions) > 0 if not keep_going: self._stop_loop() return keep_going def _stop_loop(self): if self.loop is None: return self.loop.quit() self.loop = None def _on_timeout(self): print('WARNING, timeout reached', file=sys.stderr) self._stop_loop() def sync(self, timeout=None): timeout = timeout or get_timeout() GLib.timeout_add(timeout*1000, self._on_timeout) self.loop = GLib.MainLoop() self.loop.run() def close(self): self.client.device_added.disconnect_all() self.client.device_removed.disconnect_all() self.tree.device_connected.disconnect_all() self.tree.device_disconnected.disconnect_all() # Test Suite class BoltTest(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): path = None if 'BOLT_BUILD_DIR' in os.environ: print('Testing local build') build_dir = os.environ['BOLT_BUILD_DIR'] path = os.path.join(build_dir, 'boltd') elif 'UNDER_JHBUILD' in os.environ: print('Testing JHBuild version') jhbuild_prefix = os.environ['JHBUILD_PREFIX'] path = os.path.join(jhbuild_prefix, 'libexec', 'boltd') else: print('Testing installed system binaries') path = BoltTest.path_from_service_file(SERVICE_FILE) assert path is not None, 'failed to find daemon' cls.paths = {'daemon': path} 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() dbusmock.DBusTestCase.tearDownClass() def setUp(self): self.testbed = UMockdev.Testbed.new() self.dbpath = tempfile.mkdtemp() self.client = None self.log = None self.daemon = None self.polkitd = None self.valgrind = False def tearDown(self): shutil.rmtree(self.dbpath) del self.testbed self.daemon_stop() self.polkitd_stop() # dbus helper methods def get_dbus_property(self, name, interface=DBUS_IFACE_MANAGER): proxy = Gio.DBusProxy.new_sync(self.dbus, Gio.DBusProxyFlags.DO_NOT_AUTO_START, None, DBUS_NAME, DBUS_PATH, 'org.freedesktop.DBus.Properties', None) return proxy.Get('(ss)', interface, name) # daemon helper def daemon_start(self): timeout = get_timeout('daemon_start') # seconds env = os.environ.copy() env['G_DEBUG'] = 'fatal-criticals' env['UMOCKDEV_DIR'] = self.testbed.get_root_dir() env['BOLT_DBPATH'] = self.dbpath argv = [self.paths['daemon'], '-v'] 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=self.log, stderr=subprocess.STDOUT) timeout_count = timeout * 10 timeout_sleep = 0.1 while timeout_count > 0: time.sleep(timeout_sleep) timeout_count -= 1 try: self.get_dbus_property('Version') break except GLib.GError: pass else: timeout_time = timeout_count * timeout_sleep self.fail('daemon did not start in %d seconds' % timeout_time) self.client = BoltClient(self.dbus) self.assertEqual(self.daemon.poll(), None, 'daemon crashed') def daemon_stop(self): if self.daemon: try: self.daemon.terminate() except OSError: pass self.daemon.wait() self.daemon = None self.client = None def polkitd_start(self): self._polkitd, self._polkitd_obj = self.spawn_server_template( 'polkitd', {}, stdout=DEVNULL) self.polkitd = dbus.Interface(self._polkitd_obj, dbusmock.MOCK_IFACE) def polkitd_stop(self): if self.polkitd is None: return self._polkitd.terminate() self._polkitd.wait() self.polkitd = None def user_config(self, **kwargs): import configparser cfg = configparser.ConfigParser() cfg.optionxform = lambda option: option cfg['config'] = {} for k, v in kwargs.items(): cfg['config'][k] = v path = os.path.join(self.dbpath, 'boltd.conf') with open(path, 'w') as f: cfg.write(f) with open(path, 'r') as f: print(f.read()) # mock tree stuff def default_mock_tree(self): # default mock tree mt = TbDomain(host=TbHost([ TbDevice('Cable1'), TbDevice('Cable2'), TbDevice('SSD1') ])) return mt def simple_mock_tree(self): mt = TbDomain(host=TbHost([ TbDevice('Dock') ])) return mt def assertDeviceEqual(self, local, remote): self.assertTrue(local and remote) self.assertEqual(local.unique_id, remote.uid) self.assertEqual(local.device_name, remote.Name) self.assertEqual(local.vendor_name, remote.Vendor) # if we are "connected" if local.syspath is not None: self.assertEqual(local.syspath, remote.sysfs_path) self.assertTrue(remote.is_connected) # remote.parent is also only valid if we are connected if local.parent is not None and isinstance(local.parent, TbDevice): self.assertEqual(local.parent.unique_id, remote.parent) self.assertEqual(local.bolt_status, remote.status) return True def add_device(self, parent, devid, name, vendor, domain=0, authorized=1): uid = str(uuid.uuid4()) d = self.testbed.add_device('thunderbolt', "%d-%d" % (domain, devid), parent, ['device_name', name, 'device', '0x23', 'vendor_name', vendor, 'vendor', '0x23', 'authorized', '%d' % authorized, 'key', '', 'unique_id', uid], ['DEVTYPE', 'thunderbolt_device']) return d, uid def find_device_by_uid(self, lst, uid): x = [x for x in lst if x.uid == uid] self.assertEqual(len(x), 1) return x[0] # the actual tests def test_basic(self): self.daemon_start() version = self.client.version assert version is not None d = self.client.list_devices() self.assertEqual(len(d), 0) policy = self.client.default_policy self.assertIn(policy, [self.client.POLICY_AUTO, self.client.POLICY_MANUAL]) # connect all device tree = self.default_mock_tree() chk = TreeChecker(self.client, tree) tree.connect_tree(self.testbed) chk.sync() # check for the signals devices = self.client.list_devices() self.assertEqual(len(devices), len(tree.devices)) for remote in devices: local = tree.find(unique_id=remote.uid) self.assertDeviceEqual(local, remote) # disconnect all devices again tree.disconnect(self.testbed) chk.sync() # check for the signals chk.close() devices = self.client.list_devices() self.assertEqual(len(devices), 0) self.daemon_stop() def test_signals_on_start(self): # Check that we get DeviceAdded signals for un-authorized # devices that are not in the database client = BoltClient(self.dbus) tree = self.default_mock_tree() tree.connect_tree(self.testbed) with client.record() as tape: self.daemon_start() res = tape.wait_for_event('signal', 'DeviceAdded', None) self.assertTrue(res) self.daemon_stop() def test_basic_device_name(self): domain = 0 security = 'secure' dc = self.testbed.add_device('thunderbolt', 'domain%d' % domain, None, ['security', security], ['DEVTYPE', 'thunderbolt_domain']) host = self.testbed.add_device('thunderbolt', "%d-0" % domain, dc, ['device_name', 'Host', 'device', '0x23', 'vendor_name', 'GNOME.org', 'vendor', '0x23', 'authorized', '1', 'unique_id', str(uuid.uuid4())], ['DEVTYPE', 'thunderbolt_device']) print(host) # duplicated vendor in name d1_vendor = "GNOME.org" d1_name = "Cable" d2_name = '⍾ Laptop' d2_vendor = 'Evil Corp. ☢' _, d1_uid = self.add_device(host, 1, d1_vendor + d1_name, d1_vendor) _, d2_uid = self.add_device(host, 2, d2_name, d2_vendor) self.daemon_start() devices = self.client.list_devices() self.assertEqual(len(devices), 3) device = self.find_device_by_uid(devices, d1_uid) self.assertEqual(device.Name, d1_name) self.assertEqual(device.Vendor, d1_vendor) device = self.find_device_by_uid(devices, d2_uid) self.assertEqual(device.Name, d2_name) self.assertEqual(device.Vendor, d2_vendor) label = device.label self.assertIsNone(label) self.daemon_stop() def test_basic_user_config(self): self.user_config(DefaultPolicy='manual') self.daemon_start() policy = self.client.default_policy self.assertEqual(policy, self.client.POLICY_MANUAL) self.daemon_stop() def test_device_by_uid(self): self.daemon_start() with self.assertRaises(GLib.GError): self.client.device_by_uid("") with self.assertRaises(GLib.GError): self.client.device_by_uid("nonexistant") tree = self.default_mock_tree() tree.connect_tree(self.testbed) for d in tree.devices: remote = self.client.device_by_uid(d.unique_id) self.assertIsNotNone(remote) self.assertDeviceEqual(d, remote) self.daemon_stop() def test_device_authorize(self): self.daemon_start() tree = self.default_mock_tree() tree.connect_tree(self.testbed) self.polkitd_start() to_authorize = tree.collect(TbDevice.is_unauthorized) # check that we are not allowed to authorize devices for d in to_authorize: remote = self.client.device_by_uid(d.unique_id) with self.assertRaises(GLib.GError) as cm: remote.authorize() err = cm.exception self.assertEqual(err.domain, GLib.quark_to_string(Gio.DBusError.quark())) self.assertEqual(err.code, int(Gio.DBusError.ACCESS_DENIED)) self.polkitd.SetAllowed(['org.freedesktop.bolt.authorize']) for d in to_authorize: remote = self.client.device_by_uid(d.unique_id) tape = remote.record() remote.authorize() d.reload_auth() # will emit the uevent, so the daemon can update res = tape.wait_for_event('property', 'Status', 'authorized') self.assertTrue(res) self.assertDeviceEqual(d, remote) tape.close() for d in to_authorize: remote = self.client.device_by_uid(d.unique_id) with self.assertRaises(GLib.GError) as cm: remote.authorize() self.daemon_stop() def test_device_enroll(self): self.daemon_start() tree = self.default_mock_tree() tree.connect_tree(self.testbed) self.polkitd_start() client = self.client to_enroll = tree.collect(TbDevice.is_unauthorized) # check that we are not allowed to enroll devices, i.e. the correct # policykit action is called. for d in to_enroll: with self.assertRaises(GLib.GError) as cm: client.enroll(d.unique_id) err = cm.exception self.assertEqual(err.domain, GLib.quark_to_string(Gio.DBusError.quark())) self.assertEqual(err.code, int(Gio.DBusError.ACCESS_DENIED)) self.polkitd.SetAllowed(['org.freedesktop.bolt.enroll']) # check we get a proper error for a unknown device with self.assertRaises(GLib.GError) as cm: # non-existent uuid client.forget("884c6edd-7118-4b21-b186-b02d396ecca0") policy = BoltClient.POLICY_AUTO for d in to_enroll: remote = client.enroll(d.unique_id, policy) d.reload_auth() # will emit the uevent, so the daemon can update # the security level for the domain is SECURE, which means we should # have authorized via a new key: # status should be AUTHORIZED_NEWKEY # stored should be True # key(state) should be KEY_NEW self.assertDeviceEqual(d, remote) self.assertTrue(remote.stored, True) self.assertEqual(remote.key, BoltDevice.KEY_NEW) self.assertEqual(remote.policy, policy) # we disconnect the tree, but since the devices are connected # the daemon should have them in its database now tree.disconnect(self.testbed) devices = self.client.list_devices() # the host itself is not stored in the daemon expected_number = len(tree.devices) - 1 devices = self.client.list_devices() tries = 0 while expected_number != len(devices) and tries < 3: time.sleep(.2) tries += 1 devices = self.client.list_devices() self.assertEqual(len(devices), expected_number) tree.connect(self.testbed) # we connect the domain again tree.children[0].connect(self.testbed) # and the host too for remote in devices: local = tree.find(unique_id=remote.uid) self.assertDeviceEqual(local, remote) self.assertTrue(remote.stored, True) # key status should have changed to HAVE from NEW self.assertEqual(remote.key, BoltDevice.KEY_HAVE) self.assertEqual(remote.policy, policy) # now we connect that specific device and wait for # the property changes with remote.record() as tape: local.connect(self.testbed) res = tape.wait_for_event('property', 'Status', 'authorized-secure') local.reload_auth() # will emit the uevent, so the daemon can update self.assertTrue(res) self.assertDeviceEqual(local, remote) def test_device_forget(self): self.daemon_start() tree = self.default_mock_tree() self.polkitd_start() tree.connect_tree(self.testbed) client = self.client self.polkitd.SetAllowed(['org.freedesktop.bolt.enroll']) to_enroll = tree.collect(TbDevice.is_unauthorized) policy = BoltClient.POLICY_MANUAL for d in to_enroll: remote = client.enroll(d.unique_id, policy) d.reload_auth() self.assertDeviceEqual(d, remote) self.assertTrue(remote.stored, True) self.assertEqual(remote.key, BoltDevice.KEY_NEW) self.assertEqual(remote.policy, policy) tree.disconnect(self.testbed) expected_number = len(tree.devices) - 1 # host is not stored devices = self.client.list_devices() tries = 0 while expected_number != len(devices) and tries < 3: time.sleep(.2) tries += 1 devices = self.client.list_devices() self.assertEqual(len(devices), expected_number) for remote in devices: with self.assertRaises(GLib.GError) as cm: client.forget(d.unique_id) err = cm.exception self.assertEqual(err.domain, GLib.quark_to_string(Gio.DBusError.quark())) self.assertEqual(err.code, int(Gio.DBusError.ACCESS_DENIED)) self.polkitd.SetAllowed(['org.freedesktop.bolt.manage']) # check we get a proper error for a unknown device with self.assertRaises(GLib.GError) as cm: # non-existent uuid client.forget("884c6edd-7118-4b21-b186-b02d396ecca0") for remote in devices: client.forget(remote.uid) devices = self.client.list_devices() self.assertEqual(len(devices), 0) def test_device_label(self): self.daemon_start() tree = self.simple_mock_tree() self.polkitd_start() tree.connect_tree(self.testbed) client = self.client self.polkitd.SetAllowed(['org.freedesktop.bolt.enroll']) local = tree.collect(TbDevice.is_unauthorized)[0] policy = BoltClient.POLICY_AUTO remote = client.enroll(local.unique_id, policy) local.reload_auth() label = remote.label self.assertIsNone(label) self.assertDeviceEqual(local, remote) self.assertTrue(remote.stored, True) self.assertEqual(remote.key, BoltDevice.KEY_NEW) self.assertEqual(remote.policy, policy) with self.assertRaises(GLib.GError) as cm: remote.label = 'not authorized' err = cm.exception self.assertEqual(err.domain, GLib.quark_to_string(Gio.DBusError.quark())) self.assertEqual(err.code, int(Gio.DBusError.ACCESS_DENIED)) self.polkitd.SetAllowed(['org.freedesktop.bolt.manage']) for val in ['', ' ', ' ']: with self.assertRaises(GLib.GError) as cm: remote.label = val err = cm.exception self.assertEqual(err.domain, GLib.quark_to_string(Gio.DBusError.quark())) self.assertEqual(err.code, int(Gio.DBusError.INVALID_ARGS)) label = remote.label self.assertIsNone(label) val = 'A valid label' with remote.record() as tape: remote.label = val res = tape.wait_for_event('property', 'Label', val) self.assertTrue(res) self.assertEqual(remote.label, val) self.daemon_stop() if __name__ == '__main__': if len(sys.argv) == 2 and sys.argv[1] == "list-tests": suit = unittest.defaultTestLoader.loadTestsFromTestCase(BoltTest) for t in suit: name = t.id() print(name[9:], end=" ") sys.exit(0) unittest.main(verbosity=2) bolt-0.2/tests/test-logging.c000066400000000000000000000154111324740507400162340ustar00rootroot00000000000000/* * Copyright © 2017 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 library 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 . * * Authors: * Christian J. Kellner */ #include "config.h" #include "bolt-device.h" #include "bolt-error.h" #include "bolt-str.h" #include "bolt-term.h" #include "bolt-log.h" #include "bolt-daemon-resource.h" #include #include #include #include #include #include typedef struct _LogData { GLogLevelFlags level; GHashTable *fields; } LogData; typedef struct _TestLog { LogData data; } TestLog; static void test_log_setup (TestLog *tt, gconstpointer user_data) { tt->data.fields = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); g_log_set_writer_func (g_log_writer_standard_streams, NULL, NULL); } static void test_log_tear_down (TestLog *tt, gconstpointer user_data) { g_hash_table_destroy (tt->data.fields); } static void log_expectv (TestLog *tt, GLogLevelFlags level, const char *domain, const char *message, va_list args) { const char *key; tt->data.level = level; g_hash_table_remove_all (tt->data.fields); if (domain) g_hash_table_insert (tt->data.fields, g_strdup ("GLIB_DOMAIN"), g_strdup (domain)); if (message) g_hash_table_insert (tt->data.fields, g_strdup ("MESSAGE"), g_strdup (message)); while ((key = va_arg (args, const char *)) != NULL) { const char *val = va_arg (args, const char *); g_hash_table_insert (tt->data.fields, g_strdup (key), g_strdup (val)); } } static void log_expect (TestLog *tt, GLogLevelFlags level, const char *domain, const char *message, ...) { va_list args; va_start (args, message); log_expectv (tt, level, domain, message, args); va_end (args); } static GLogWriterOutput test_writer (GLogLevelFlags log_level, const GLogField *fields, gsize n_fields, gpointer user_data) { g_autoptr(GHashTable) index = NULL; LogData *data = user_data; GHashTableIter iter; gpointer k, v; index = g_hash_table_new (g_str_hash, g_str_equal); for (gsize i = 0; i < n_fields; i++) { const char *key = fields[i].key; const char *val = (const char *) fields[i].value; if (!g_hash_table_contains (data->fields, key)) continue; g_hash_table_insert (index, (gpointer) key, (gpointer) val); } g_hash_table_iter_init (&iter, data->fields); while (g_hash_table_iter_next (&iter, &k, &v)) { const char *key = k; const char *value = v; const char *have; g_assert_true (g_hash_table_contains (index, key)); have = g_hash_table_lookup (index, key); g_assert_cmpstr (value, ==, have); //g_fprintf (stderr, "%s: %s vs %s\n", key, value, have); } return G_LOG_WRITER_HANDLED; } static void test_log_basic (TestLog *tt, gconstpointer user_data) { log_expect (tt, G_LOG_LEVEL_MESSAGE, "bolt-test", "test", NULL); g_log_set_writer_func (test_writer, &tt->data, NULL); bolt_log ("bolt-test", G_LOG_LEVEL_MESSAGE, "test"); } static void test_log_gerror (TestLog *tt, gconstpointer user_data) { g_autoptr(GError) error = NULL; const char *domain = "bolt-gerror"; GLogLevelFlags lvl = G_LOG_LEVEL_INFO; const char *msg; msg = "no udev"; g_set_error_literal (&error, BOLT_ERROR, BOLT_ERROR_UDEV, msg); log_expect (tt, lvl, domain, NULL, "ERROR_MESSAGE", msg, NULL); g_log_set_writer_func (test_writer, &tt->data, NULL); bolt_log (domain, lvl, LOG_ERR (error), NULL); } static void test_log_device (TestLog *tt, gconstpointer user_data) { g_autoptr(BoltDevice) a = NULL; const char *domain = "bolt-device"; const char *msg; GLogLevelFlags lvl; const char *uid_a = "fbc83890-e9bf-45e5-a777-b3728490989c"; a = g_object_new (BOLT_TYPE_DEVICE, "uid", uid_a, "name", "Laptop", "vendor", "GNOME.org", "status", BOLT_STATUS_DISCONNECTED, NULL); lvl = G_LOG_LEVEL_INFO; msg = "test device a"; log_expect (tt, lvl, domain, msg, BOLT_LOG_DEVICE_UID, uid_a, NULL); g_log_set_writer_func (test_writer, &tt->data, NULL); bolt_log (domain, lvl, LOG_DEV (a), msg); } static void test_log_macros (TestLog *tt, gconstpointer user_data) { g_autoptr(GError) error = NULL; GLogLevelFlags lvl = G_LOG_LEVEL_INFO; const char *msg = "da steht ich nun ich armer test"; g_log_set_writer_func (test_writer, &tt->data, NULL); log_expect (tt, G_LOG_LEVEL_MESSAGE, G_LOG_DOMAIN, msg, NULL); bolt_msg (msg); g_set_error_literal (&error, BOLT_ERROR, BOLT_ERROR_UDEV, msg); log_expect (tt, lvl, G_LOG_DOMAIN, NULL, "ERROR_MESSAGE", msg, NULL); bolt_warn_err (error, NULL); log_expect (tt, lvl, G_LOG_DOMAIN, NULL, BOLT_LOG_ERROR_MESSAGE, msg, NULL); bolt_log (G_LOG_DOMAIN, lvl, LOG_DIRECT (BOLT_LOG_ERROR_MESSAGE, msg), NULL); log_expect (tt, G_LOG_LEVEL_DEBUG, G_LOG_DOMAIN, msg, "CODE_FILE", __FILE__, "CODE_FUNC", G_STRFUNC, NULL); bolt_debug (msg); } int main (int argc, char **argv) { setlocale (LC_ALL, ""); g_test_init (&argc, &argv, NULL); g_resources_register (bolt_daemon_get_resource ()); g_test_add ("/logging/basic", TestLog, NULL, test_log_setup, test_log_basic, test_log_tear_down); g_test_add ("/logging/gerror", TestLog, NULL, test_log_setup, test_log_gerror, test_log_tear_down); g_test_add ("/logging/device", TestLog, NULL, test_log_setup, test_log_device, test_log_tear_down); g_test_add ("/logging/macros", TestLog, NULL, test_log_setup, test_log_macros, test_log_tear_down); return g_test_run (); } bolt-0.2/tests/test-store.c000066400000000000000000000217671324740507400157550ustar00rootroot00000000000000/* * Copyright © 2017 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 library 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 . * * Authors: * Christian J. Kellner */ #include "config.h" #include "bolt-error.h" #include "bolt-io.h" #include "bolt-store.h" #include "bolt-daemon-resource.h" #include #include #include #include #include #include #include #include #include #include /* unlinkat, truncate */ static void cleanup_dir (DIR *d) { struct dirent *de = NULL; for (errno = 0, de = readdir (d); de != NULL; errno = 0, de = readdir (d)) { g_autoptr(GError) error = NULL; int uflag = 0; if (!g_strcmp0 (de->d_name, ".") || !g_strcmp0 (de->d_name, "..")) continue; if (de->d_type == DT_DIR) { g_autoptr(DIR) cd = NULL; cd = bolt_opendir_at (dirfd (d), de->d_name, O_RDONLY, &error); if (cd == NULL) continue; cleanup_dir (cd); uflag = AT_REMOVEDIR; } bolt_unlink_at (dirfd (d), de->d_name, uflag, NULL); } } typedef struct { const char *path; BoltStore *store; } TestStore; static void test_store_setup (TestStore *tt, gconstpointer user_data) { g_autoptr(GError) error = NULL; tt->path = g_dir_make_tmp ("bolt.auth.XXXXXX", &error); if (tt->path == NULL) { g_critical ("Could not create tmp dir: %s", error->message); return; } tt->store = bolt_store_new (tt->path); if (tt->store == NULL) { g_critical ("Could not create store at %s", tt->path); return; } g_debug ("store at '%s'", tt->path); } static void test_store_tear_down (TestStore *tt, gconstpointer user_data) { DIR *d = NULL; g_autoptr(GError) error = NULL; if (tt->store) g_clear_object (&tt->store); d = bolt_opendir (tt->path, &error); if (d) { g_debug ("Cleaning up: %s", tt->path); cleanup_dir (d); bolt_closedir (d, NULL); bolt_rmdir (tt->path, &error); } if (error != NULL) g_warning ("Could not clean up dir: %s", error->message); } static void test_store_basic (TestStore *tt, gconstpointer user_data) { g_autoptr(BoltDevice) dev = NULL; g_autoptr(BoltDevice) stored = NULL; g_autoptr(BoltKey) key = NULL; g_autoptr(GError) error = NULL; char uid[] = "fbc83890-e9bf-45e5-a777-b3728490989c"; BoltKeyState keystate; gboolean ok; dev = g_object_new (BOLT_TYPE_DEVICE, "uid", uid, "name", "Laptop", "vendor", "GNOME.org", "status", BOLT_STATUS_DISCONNECTED, NULL); stored = bolt_store_get_device (tt->store, uid, &error); g_assert_nonnull (error); g_assert_true (bolt_err_notfound (error)); g_assert_null (stored); g_clear_error (&error); ok = bolt_store_put_device (tt->store, dev, BOLT_POLICY_AUTO, NULL, &error); g_assert_no_error (error); g_assert_true (ok); stored = bolt_store_get_device (tt->store, uid, &error); g_assert_no_error (error); g_assert_nonnull (stored); keystate = bolt_store_have_key (tt->store, uid); g_assert_cmpuint (keystate, ==, 0); g_assert_cmpstr (bolt_device_get_uid (stored), ==, bolt_device_get_uid (dev)); g_assert_cmpstr (bolt_device_get_name (stored), ==, bolt_device_get_name (dev)); g_assert_cmpstr (bolt_device_get_vendor (stored), ==, bolt_device_get_vendor (dev)); g_assert_cmpuint (bolt_device_get_policy (stored), ==, BOLT_POLICY_AUTO); g_assert_cmpuint (bolt_device_get_stored (stored), ==, TRUE); g_assert_cmpuint (bolt_device_get_keystate (stored), ==, BOLT_KEY_MISSING); g_clear_object (&stored); g_clear_object (&dev); uid[0] = 'a'; dev = g_object_new (BOLT_TYPE_DEVICE, "uid", uid, "name", "Laptop", "vendor", "GNOME.org", "status", BOLT_STATUS_DISCONNECTED, NULL); stored = bolt_store_get_device (tt->store, uid, &error); g_assert_nonnull (error); g_assert_true (bolt_err_notfound (error)); g_assert_null (stored); g_clear_error (&error); g_assert_no_error (error); key = bolt_key_new (); g_assert_no_error (error); g_assert_true (ok); ok = bolt_store_put_device (tt->store, dev, BOLT_POLICY_MANUAL, key, &error); g_assert_no_error (error); g_assert_true (ok); stored = bolt_store_get_device (tt->store, uid, &error); g_assert_no_error (error); g_assert_nonnull (stored); g_assert_cmpstr (bolt_device_get_uid (stored), ==, bolt_device_get_uid (dev)); g_assert_cmpstr (bolt_device_get_name (stored), ==, bolt_device_get_name (dev)); g_assert_cmpstr (bolt_device_get_vendor (stored), ==, bolt_device_get_vendor (dev)); g_assert_cmpuint (bolt_device_get_policy (stored), ==, BOLT_POLICY_MANUAL); g_assert_cmpuint (bolt_device_get_stored (stored), ==, TRUE); g_assert_cmpuint (bolt_device_get_keystate (stored), ==, 1); keystate = bolt_store_have_key (tt->store, uid); g_assert_cmpuint (keystate, ==, 1); g_clear_object (&key); key = bolt_store_get_key (tt->store, uid, &error); g_assert_no_error (error); g_assert_nonnull (stored); /* ** deletion */ /* non-existent */ ok = bolt_store_del_device (tt->store, "transmogrifier", &error); g_assert_nonnull (error); g_assert_true (bolt_err_notfound (error)); g_assert_false (ok); g_clear_error (&error); g_assert_no_error (error); ok = bolt_store_del_key (tt->store, "sesamoeffnedich", &error); g_assert_nonnull (error); g_assert_true (bolt_err_notfound (error)); g_assert_false (ok); g_clear_error (&error); g_assert_no_error (error); /* remove existing device & key */ ok = bolt_store_del_device (tt->store, uid, &error); g_assert_no_error (error); g_assert_true (ok); keystate = bolt_store_have_key (tt->store, uid); g_assert_cmpuint (keystate, !=, 0); ok = bolt_store_del_key (tt->store, uid, &error); g_assert_no_error (error); g_assert_true (ok); /* check that they are gone indeed */ ok = bolt_store_del_device (tt->store, uid, &error); g_assert_nonnull (error); g_assert_true (bolt_err_notfound (error)); g_assert_false (ok); g_clear_error (&error); g_assert_no_error (error); keystate = bolt_store_have_key (tt->store, uid); g_assert_cmpuint (keystate, ==, 0); ok = bolt_store_del_key (tt->store, uid, &error); g_assert_nonnull (error); g_assert_true (bolt_err_notfound (error)); g_assert_false (ok); g_clear_error (&error); g_assert_no_error (error); } static void test_key (TestStore *tt, gconstpointer user_data) { g_autoptr(BoltKey) key = NULL; g_autoptr(BoltKey) loaded = NULL; g_autoptr(GFile) base = NULL; g_autoptr(GFile) f = NULL; g_autoptr(GError) err = NULL; g_autoptr(GFileInfo) fi = NULL; g_autofree char *p = NULL; gboolean fresh = FALSE; gboolean ok; guint32 mode; int r; key = bolt_key_new (); g_assert_nonnull (key); g_object_get (key, "fresh", &fresh, NULL); g_assert_true (fresh); fresh = bolt_key_get_state (key); g_assert_true (fresh); base = g_file_new_for_path (tt->path); f = g_file_get_child (base, "key"); g_assert_nonnull (base); g_assert_nonnull (f); ok = bolt_key_save_file (key, f, &err); g_assert_no_error (err); g_assert_true (ok); fi = g_file_query_info (f, "*", 0, NULL, &err); g_assert_no_error (err); g_assert_nonnull (fi); mode = g_file_info_get_attribute_uint32 (fi, "unix::mode"); g_assert_cmpuint (mode & 0666, ==, 0600); loaded = bolt_key_load_file (f, &err); g_assert_no_error (err); g_assert_nonnull (loaded); /* corrupt the key */ p = g_file_get_path (f); r = truncate (p, 32); g_assert_cmpint (r, ==, 0); loaded = bolt_key_load_file (f, &err); g_assert_error (err, BOLT_ERROR, BOLT_ERROR_BADKEY); g_assert_null (loaded); g_clear_error (&err); } int main (int argc, char **argv) { setlocale (LC_ALL, ""); g_test_init (&argc, &argv, NULL); g_resources_register (bolt_daemon_get_resource ()); g_test_add ("/daemon/key", TestStore, NULL, test_store_setup, test_key, test_store_tear_down); g_test_add ("/daemon/store/basic", TestStore, NULL, test_store_setup, test_store_basic, test_store_tear_down); return g_test_run (); } bolt-0.2/tests/tests.gresource.xml000066400000000000000000000002741324740507400173470ustar00rootroot00000000000000 example.bolt.xml