From de1f9acfa324378d9af7367738c30b945e831bd3 Mon Sep 17 00:00:00 2001 From: Andrea Luzzardi Date: Tue, 8 May 2007 01:40:22 +0000 Subject: [PATCH] Tagged 0.4.0 --- COPYING | 340 ++++++++++++++++++++++++++++++++++++++++++ ChangeLog | 181 ++++++++++++++++++++++ Makefile | 87 +++++++++++ doc/CONFIGURATION | 256 +++++++++++++++++++++++++++++++ doc/FAQ | 21 +++ doc/QUICKSTART | 227 ++++++++++++++++++++++++++++ doc/UPGRADING | 58 +++++++ doc/pamusb-agent.1.gz | Bin 0 -> 564 bytes doc/pamusb-check.1.gz | Bin 0 -> 855 bytes doc/pamusb-conf.1.gz | Bin 0 -> 452 bytes doc/pamusb.conf | 49 ++++++ src/conf.c | 187 +++++++++++++++++++++++ src/conf.h | 63 ++++++++ src/device.c | 88 +++++++++++ src/device.h | 23 +++ src/hal.c | 189 +++++++++++++++++++++++ src/hal.h | 29 ++++ src/local.c | 62 ++++++++ src/local.h | 23 +++ src/log.c | 96 ++++++++++++ src/log.h | 29 ++++ src/pad.c | 222 +++++++++++++++++++++++++++ src/pad.h | 23 +++ src/pam.c | 111 ++++++++++++++ src/pamusb-check.c | 140 +++++++++++++++++ src/version.h | 23 +++ src/volume.c | 152 +++++++++++++++++++ src/volume.h | 24 +++ src/xpath.c | 219 +++++++++++++++++++++++++++ src/xpath.h | 29 ++++ tools/pamusb-agent | 211 ++++++++++++++++++++++++++ tools/pamusb-conf | 271 +++++++++++++++++++++++++++++++++ 32 files changed, 3433 insertions(+) create mode 100644 COPYING create mode 100644 ChangeLog create mode 100644 Makefile create mode 100644 doc/CONFIGURATION create mode 100644 doc/FAQ create mode 100644 doc/QUICKSTART create mode 100644 doc/UPGRADING create mode 100644 doc/pamusb-agent.1.gz create mode 100644 doc/pamusb-check.1.gz create mode 100644 doc/pamusb-conf.1.gz create mode 100644 doc/pamusb.conf create mode 100644 src/conf.c create mode 100644 src/conf.h create mode 100644 src/device.c create mode 100644 src/device.h create mode 100644 src/hal.c create mode 100644 src/hal.h create mode 100644 src/local.c create mode 100644 src/local.h create mode 100644 src/log.c create mode 100644 src/log.h create mode 100644 src/pad.c create mode 100644 src/pad.h create mode 100644 src/pam.c create mode 100644 src/pamusb-check.c create mode 100644 src/version.h create mode 100644 src/volume.c create mode 100644 src/volume.h create mode 100644 src/xpath.c create mode 100644 src/xpath.h create mode 100755 tools/pamusb-agent create mode 100755 tools/pamusb-conf diff --git a/COPYING b/COPYING new file mode 100644 index 0000000..d60c31a --- /dev/null +++ b/COPYING @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, 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 or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +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 give any other recipients of the Program a copy of this License +along with the Program. + +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 Program or any portion +of it, thus forming a work based on the Program, 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) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +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 Program, 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 Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) 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; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, 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 executable. However, as a +special exception, the source code 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. + +If distribution of executable or 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 counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program 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. + + 5. 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 Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program 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 to +this License. + + 7. 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 Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program 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 Program. + +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. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program 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. + + 9. The Free Software Foundation may publish revised and/or new versions +of the 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 Program +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 Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, 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 + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "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 PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. 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 PROGRAM 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 PROGRAM (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 PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), 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 Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. 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 program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/ChangeLog b/ChangeLog new file mode 100644 index 0000000..9bb9243 --- /dev/null +++ b/ChangeLog @@ -0,0 +1,181 @@ +* 0.4.0 +- Both pam_usb and its tools (adm, hotplug) have been redesigned from the + ground up and rewritten from scratch. +- Hardware recognition is now done through HAL which provides a stable + interface over kernel changes. +- Certificates have been replaced by one time pads. That will prevent + copies of the USB device to be used for authentication. +- Device's manufacturer properties verification. pam_usb now verifies + device informations (vendor, product, serial number, UUID) in the + authentication process. +- Configuration is now handled in a central place, the pamusb.conf + configuration file. This XML file contains configuration entries for + users, devices and services. +- pamusb-agent (formely usbhotplug) make use of DBUS signals (sent by HAL) + instead of kernel hotplugging. Also, its configuration has been merged + into the pamusb.conf configuration file. +- A new tool named pamusb-check has been added. It can perform authentication + the way the PAM module does. It can be useful for testing and scripting + purposes. + +* 0.3.3 +- The option keypath is now splitted into local_keypath and device_keypath. +- Fixed a bug that occurred when the TTY entry was empty. +- pam_usb doesn't get anymore the tty name from PAM_TTY as it used to be + empty on some systems. +- Better defaults. The default options have been set to fit most needs, + you are no longer required to use !check_device on 2.6. +- Verbose mode. By default, pam_usb now prints some informations during + the login process (access granted, the reason why access was refused, etc). + This can be turned off using the brand new 'quiet' option. +- Other small fixes. + +* 0.3.2 +- Now pam_usb will also try to autodetect /dev/sdN devices (not just + /dev/sdNX). +- Fixed a bug that happened when the application using PAM didn't set + PAM_TTY correctly. +- Added the use_first_pass and try_first_pass options. + Now if you enter your password on another PAM module (such as pam_mount + or pam_ssh), pam_usb will use that password to decrypt the private key. + +* 0.3.1 +- Lot of misc fixes (memory management, Makefiles, sanity checks, etc). + I'd like to thank the PaX Team who did almost + the whole job. +- Added the hostname option which allows to select what hostname should + be used for authentication (useful for shared public keys over lan). + Thanks to Nicolas Chauvat who reported the issue, + the idea and the patch for this feature. + +* 0.3.0 +- Not much changes in this version beside a gcc fix, but the 0.2 branch + reached too many new features so i wanted to name this release 0.3.0 + as i should have done with 0.2.3 +- Fixed a gcc 3.3 compile issue, and all related warning. + I would like to thank the following guys for having reported this bug so fast: + Lalande Fabrice + Marco + Neil Dunbar + +* 0.2.3 +- Added the usbhotplug tool. + usbhotplug is a hotplug agent that will automagically start a lock handler + when the usb device is removed and an unlock handler when the usb device + is plugged back in and authenticated through pam_usb. + + The default handlers will start xlock when the usb device is removed, + and will kill it when the usb device is plugged back in and authenticated. + + I'd like to thank Wout Mertens as we had a couple + of discussions about hotplug which helped me implementing this tool. + +- The parser can now understand "option" and "!option" instead of + option=1 and option=-1 (e.g. debug !check_device). + Thanks to Jean-Christophe JASKULA who + suggested me that and provided an initial patch. + +- Fixed a loop bug on serial number checking. Thanks to Zs + for reporting the bug and a patch to fix it. + +- Added the direct_open option which allows to open the private key + using O_DIRECT to avoid disk caching (works only on devices that + supports it). Thanks to myles who suggested me that. + +- Added some sanity checks here and there because it seems that the PAM + API can return weird stuff from time to time. + +- Handling the mount point creation/remotion in a better way which seems + to fix a couple of mntpoint problems. + +* 0.2.2 +- Added the keep_mounted option, which allows to not umount the mount point + once logged (useful if the gpg/ssh key is stored on there) + +- Fixed the mntpoint option: do not delete the directory if it's not a + temporary one. + +- Added the support to pass multiple filesystems name with the fs= + option (comma separated list). Changed the default fs to "ext2,vfat" + +- Added the log_file option. Takes a filename as a argument. + Combined with debug=1 it can log debug messages to a file. + +- Not mounting the device as read-only anymore. Instead, the mount_opts + option has been created. It accepts a comma separated list of mount + options (accepted options are: ro,bind,sync,remount,nosuid,noexec,nodev). + +- Fixed an issue which made the allow_remote feature not working correctly + with gdm/kdm. + +- Introduced the local_hosts and local_consoles options. They contain a + comma separated lists of hosts and consoles allowed to log in while using + allow_remote=-1 + +* 0.2.1 +- Changed the naming method from x.y to x.y.z + +- pam_usb is now able to distinguish local users from remote (as in + logged via ssh), and denies the authentication of non-local users. + Setting allow_remote to 1 disable this feature. + +- Mounting is now done in read-only. + +- Added the missing mandatory PAM functions. + +* 0.2_rc2 +- Workaround to make pam_usb not use /proc so it can run on Linux 2.6 + By setting check_device to -1, pam_usb will neither check the device's + serial number, nor if it's attached. It's not a real problem if you + don't need serial number checking, but don't combine it with + check_if_mounted. + +- Added the force_device capability. Now you can specify a device that + will be mounted without going in guessing mode. If the device cannot + be mounted, it'll switch back to the default guess mode. + Useful if guess mode fails, if you don't want it to try several + devices before getting the right one (so you can login faster), or if + you want to login using a floppy disk, a cdrom or whatever you want. + +- Modified the serial number authentication method so now if no serial + numbers are avaible on a device, it will try to use the GUID. + Thanks to Damien Braillard who reported the + issue, suggested a way to fix it, and provided a first patch for it. + +* 0.2_rc1 +- Radically changed the way pam_usb authenticates the user on the + system. Now it works with a pair of DSA keys. + + Thanks to Wout Mertens who told me that i could + use a couple of SSH keys to fix the authentication issue. + That gave me the idea to use a set of private/public keys. + + Thanks to Ilkka Mattila who helped me to + find out a better way to implement the key challenge: extracting the + public key was inadequate. + + Also thanks to those who brought up weird scenarios and/or tested + pre-releases of pam_usb, in alphabetical order: + + Ilkka Mattila + Joonas Kortesalmi + Thomas Stewart + Tuure Laurinolli + +* 0.1: +- Now pam_usb doesn't require a mount point. Instead, it creates + a temporary directory under /tmp. + Thanks to Loic Jaquemet who gave me the idea. + +- Compiles with gcc 2.95 thanks to Tobias Bayer bug + report. + +* 0.1-beta2: +- procfile and device entries autodetection have been fixed thanks to + Thomas Stewart bug reports. + +- devfs support added. Thanks to Loic Jaquemet + for the bug report. + +* 0.1-beta1: +- Initial release diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..9de4f9e --- /dev/null +++ b/Makefile @@ -0,0 +1,87 @@ +# Set to 'yes' to include debugging informations, e.g. DEBUG=yes make -e +DEBUG := no + +# compiler/linker options +CC := gcc +CFLAGS := $(CFLAGS) -Wall -fPIC `pkg-config --cflags libxml-2.0` \ + `pkg-config --cflags hal-storage` +LIBS := `pkg-config --libs libxml-2.0` \ + `pkg-config --libs hal-storage` + +# common source files +SRCS := src/conf.c \ + src/log.c \ + src/xpath.c \ + src/hal.c \ + src/pad.c \ + src/volume.c \ + src/local.c \ + src/device.c +OBJS := $(SRCS:.c=.o) + +# pam_usb +PAM_USB_SRCS := src/pam.c +PAM_USB_OBJS := $(PAM_USB_SRCS:.c=.o) +PAM_USB := pam_usb.so +PAM_USB_LDFLAGS := -shared +PAM_USB_DEST := $(DESTDIR)/lib/security + +# pamusb-check +PAMUSB_CHECK_SRCS := src/pamusb-check.c +PAMUSB_CHECK_OBJS := $(PAMUSB_CHECK_SRCS:.c=.o) +PAMUSB_CHECK := pamusb-check + +# Tools +PAMUSB_CONF := pamusb-conf +PAMUSB_AGENT := pamusb-agent +TOOLS_DEST := $(DESTDIR)/usr/bin +TOOLS_SRC := tools + +# Conf +CONFS := doc/pamusb.conf +CONFS_DEST := $(DESTDIR)/etc + +# Doc +DOCS := doc/QUICKSTART doc/CONFIGURATION doc/UPGRADING doc/FAQ +DOCS_DEST := $(DESTDIR)/usr/share/doc/pamusb + +# Man +MANS := doc/pamusb-conf.1.gz doc/pamusb-agent.1.gz doc/pamusb-check.1.gz +MANS_DEST := $(DESTDIR)/usr/share/man/man1 + +# Binaries +RM := rm +INSTALL := install +MKDIR := mkdir + +ifeq (yes, ${DEBUG}) + CFLAGS := ${CFLAGS} -ggdb +endif + +all : $(PAM_USB) $(PAMUSB_CHECK) + +$(PAM_USB) : $(OBJS) $(PAM_USB_OBJS) + $(CC) -o $(PAM_USB) $(PAM_USB_LDFLAGS) $(LDFLAGS) $(OBJS) $(PAM_USB_OBJS) $(LIBS) + +$(PAMUSB_CHECK) : $(OBJS) $(PAMUSB_CHECK_OBJS) + $(CC) -o $(PAMUSB_CHECK) $(LDFLAGS) $(OBJS) $(PAMUSB_CHECK_OBJS) $(LIBS) + +%.o : %.c + ${CC} -c ${CFLAGS} $< -o $@ + +clean : + $(RM) -f $(PAM_USB) $(PAMUSB_CHECK) $(OBJS) $(PAMUSB_CHECK_OBJS) $(PAM_USB_OBJS) + +install : all + $(MKDIR) -p $(CONFS_DEST) $(DOCS_DEST) $(MANS_DEST) $(TOOLS_DEST) $(PAM_USB_DEST) + $(INSTALL) -m755 $(PAM_USB) $(PAM_USB_DEST) + $(INSTALL) -m755 $(PAMUSB_CHECK) $(TOOLS_SRC)/$(PAMUSB_CONF) $(TOOLS_SRC)/$(PAMUSB_AGENT) $(TOOLS_DEST) + $(INSTALL) -b -m644 $(CONFS) $(CONFS_DEST) + $(INSTALL) -m644 $(DOCS) $(DOCS_DEST) + $(INSTALL) -m644 $(MANS) $(MANS_DEST) + +deinstall : + $(RM) -f $(PAM_USB_DEST)/$(PAM_USB) + $(RM) -f $(TOOLS_DEST)/$(PAMUSB_CHECK) $(TOOLS_DEST)/$(PAMUSB_CONF) $(TOOLS_DEST)/$(PAMUSB_AGENT) + $(RM) -rf $(DOCS_DEST) + $(RM) -f $(MANS_DEST)/pusb_* diff --git a/doc/CONFIGURATION b/doc/CONFIGURATION new file mode 100644 index 0000000..da72e85 --- /dev/null +++ b/doc/CONFIGURATION @@ -0,0 +1,256 @@ +====== Configuration ====== + + + + + +===== Introduction ===== + +* The configuration file is formatted in XML and subdivided in 4 sections: + - Default options, shared among every device, user and service + - Devices declaration and settings + - Users declaration and settings + - Services declaration and settings + +* The syntax is the following: + + + + + + + + + + + + + + + + + + +* Location of the configuration file + +By default, pam_usb.so and its tools will look for the configuration file +located in /etc/pamusb.conf, but you can tell it to use a different file by +using the -c option: + +# /etc/pam.d/common-auth +auth sufficient pam_usb.so -c /some/other/path.conf +auth required pam_unix.so nullok_secure + +You will also have to use the -c option when calling pam_usb's tools. For +instance, when calling pamusb-agent: +pamusb-agent -c /some/other/path.conf + + + + +===== Options ===== + +^ Name ^ Type ^ Default value ^ Description ^ +| enable | Boolean | true | Enable pam_usb +| +| debug | Boolean | false | Enable debug messages +| +| quiet | Boolean | false | Quiet mode (no verbose +output) | +| color_log | Boolean | true | Enable colored output +| +| one_time_pad | Boolean | true | Enable the use of one +time pads | +| probe_timeout | Integer | 10 | Time (in seconds) to +wait for the volume to be detected| +| hostname | String | Computer's hostname | Computer name. Must be +unique accross computers using the same device | + +| system_pad_directory | String | .pamusb | Relative path to the +user's home used to store one time pads | +| device_pad_directory | String | .pamusb | Relative path to the +device used to store one time pads| + +* Example: + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +===== Devices ===== + +^ Name ^ Type ^ Description ^ +Example ^ +| id | Attribute | Arbitrary device name | +MyDevice | +| vendor | Element | device's vendor name | +SanDisk Corp. | +| model | Element | device's model name | +Cruzer Titanium | +| serial | Element | serial number of the device | +SNDKXXXXXXXXXXXXXXXX | +| volume_uuid | Element | UUID of the device's volume used to store pads | +6F6B-42FC | + + +* Example: + + +SanDisk Corp. +Cruzer Titanium +SNDKXXXXXXXXXXXXXXXX +6F6B-42FC + + + +===== Users ===== + +^ Name ^ Type ^ Description ^ +Example ^ +| id | Attribute | Login of the user | root +| +| device | Element | id of the device associated to the user | +MyDevice | +| agent | Element | Agent commands, for use with pamusb-agent | See +below | + +* Example: + + +MyDevice + + +gnome-screensaver-command --lock +beep-media-player --pause + + +gnome-screensaver-command --deactivate +beep-media-player --play + + +===== Services ===== + +^ Name ^ Type ^ Description ^ Example ^ +| id | Attribute | Name of the service | su | + + + + + + + +===== Full example ===== + +This example demonstrates how to write a pam_usb configuration file and how to +combine and override options. + + + + + + --> + + --> + + + + + + + SanDisk Corp. + Cruzer Titanium + SNDKXXXXXXXXXXXXXXXX + 6F6B-42FC + + + + + + + + + + + + MyDevice + + + + + + + + MyDevice + + + + + + gnome-screensaver-command --lock + gnome-screensaver-command --deactivate + + + + + + + + + + + + + + + + + + diff --git a/doc/FAQ b/doc/FAQ new file mode 100644 index 0000000..37c76f3 --- /dev/null +++ b/doc/FAQ @@ -0,0 +1,21 @@ +====== Frequently Asked Questions ====== + +> Q: Can I use my USB drive as usual ? +>> A: Yes. pam_usb only occupies a few kilobytes of the device's space. + +> Q: What if I lose or break my USB key ? Will I be able to log back in ? +>> A: Sure. Your usual password will be asked. + +> Q: How is the USB key identified ? +>> A: The USB device is both identified by its manufacturer attributes (vendor, +product, serial number) and by a few random bytes called one time pads that +pam_usb writes and updates on the USB device upon authentication. + +> Q: What if someone copies the content of my flash drive ? Will she/he be able +to log into my account ? +>> A: Even if that person manages to fake your device's attributes (vendor, +product, serial number, UUID), the one time pad they copied will be outdated as +soon as you authenticate. + +> Q: Is my USB drive compatible with pam_usb ? +>> A: About every USB flash drive will work with pam_usb. diff --git a/doc/QUICKSTART b/doc/QUICKSTART new file mode 100644 index 0000000..d502914 --- /dev/null +++ b/doc/QUICKSTART @@ -0,0 +1,227 @@ +====== Quickstart ====== + +Before going ahead, make sure to follow the upgrading instructions if you're +using an older version of pam_usb. + +===== Installing ==== + + +==== Installing from sources ==== +* Step 1: Download the latest release +* Step 2: Unpack the distribution tarball + +$ tar -zxvf pam_usb-.tar.gz +$ cd pam_usb- + +* Step 3: Make sure that you have installed the required dependencies + +pam_usb depends on libxml2, PAM and HAL. pam_usb's tools (pamusb-agent, +pamusb-conf) depends on python, python-celementtree and python-gobject. + +* Step 3: Compile and install + +$ make +# make install + +==== Installing from Subversion ==== + +If you want to use the development version, you can fetch the sources from +subversion +$ svn co https:pamusb.svn.sourceforge.net/svnroot/pamusb/trunk/pam_usb + +===== Setting up ===== + + + +==== Devices and Users ==== + +* Once you've connected your USB device to the computer, use pamusb-conf to add +it to the configuration file: + +# pamusb-conf --add-device MyDevice +Please select the device you wish to add. +* Using "SanDisk Corp. Cruzer Titanium (SNDKXXXXXXXXXXXXXXXX)" (only option) +Which volume would you like to use for storing data ? +* Using "/dev/sda1 (UUID: <6F6B-42FC>)" (only option) +Name : MyDevice +Vendor : SanDisk Corp. +Model : Cruzer Titanium +Serial : SNDKXXXXXXXXXXXXXXXX +Volume UUID : 6F6B-42FC (/dev/sda1) +Save to /etc/pamusb.conf ? +[Y/n] y +Done. + +Note that MyDevice can be any arbitrary name you'd like. Also, you can add as +many devices as you want. + +* Users + +Now that we have added the devices, we have to configure the users. + + # pamusb-conf --add-user root + Which device would you like to use for authentication ? + * Using "MyDevice" (only option) + User : root + Device : MyDevice + Save to /etc/pamusb.conf ? + [Y/n] y + Done. + +Repeat this step for every other username you'd like to use pam_usb with (e.g. +pamusb-conf --add-user MyUsername). + +* In order to check if everything went fine, we are going to use the +pamusb-check tool which will simulate an authentication event. + +# pamusb-check root +* Authentication request for user "root" (pamusb-check) +* Device "MyDevice" is connected (good). +* Performing one time pad verification... +* Verification match, updating one time pads... +* Access granted. + + + +==== PAM Module ==== + +The PAM module pam_usb.so is used to let applications authenticate you using +your USB device instead of asking your password. The default password-based +authentication will be used as fallback if the device authentication goes wrong. + +* Depending on the operating system you're using, you have to tell PAM to use +pam_usb.so as default authentication method. There should be a file named +either common-auth (Gentoo) under /etc/pam.d/. If you do NOT have neither of +those files, you'll have to edit each pam.d service file you want to use (e.g. +/etc/pam.d/su, /etc/pam.d/gdm and so on). + +* Locate the following line on /etc/pam.d/common-auth or /etc/pam.d/system-auth: + +auth required pam_unix.so nullok_secure + +* And change it to look something like that: + +auth sufficient pam_usb.so +auth required pam_unix.so nullok_secure + +* You should now be able to authenticate the users configured in pamusb.conf +using your USB device: + +scox $ su +* pam_usb v.SVN +* Authentication request for user "root" (su) +* Device "MyDevice" is connected (good). +* Performing one time pad verification... +* Verification match, updating one time pads... +* Access granted. + +* Try to authenticate to a different application. pam_usb.so should work with +any application using xscreensaver and many more). + + + + +==== Agent ==== + +The pam_usb agent (pamusb-agent) allows you to automatically execute commands +upon locking and unlocking events. Those events are generated when you insert or +remove your authentication device. +To configure the commands, you have to edit pam_usb's configuration file +(/etc/pamusb.conf) and add agent entries into your user section. + +For instance, you could automatically start your screensaver as soon as you +remove the device, and deactivate it when you plug the device back. + +* GNOME (gnome-screensaver): + + MyDevice + gnome-screensaver-command --lock + gnome-screensaver-command --deactivate + + +* KDE (kscreensaver): + + MyDevice + dcop kdesktop KScreensaverIface lock + dcop kdesktop KScreensaverIface quit + + +You can execute more commands by adding extra entries. + + +$ pamusb-agent +pamusb-agent[18329]: pamusb-agent up and running. +pamusb-agent[18329]: Watching device "MyDevice" for user "scox" +pamusb-agent[18329]: Device "MyDevice" has been removed, locking down user +"scox"... +pamusb-agent[18329]: Running "gnome-screensaver-command --lock" +pamusb-agent[18329]: Locked. +pamusb-agent[18329]: Device "MyDevice" has been inserted. Performing +verification... +pamusb-agent[18329]: Executing "/usr/bin/pamusb-check --quiet +--config=/etc/pamusb.conf --service=pamusb-agent scox" +pamusb-agent[18329]: Authentication succeeded. Unlocking user "scox"... +pamusb-agent[18329]: Running "gnome-screensaver-command --deactivate" +pamusb-agent[18329]: Unlocked. + +Depending on your desktop environment, you have to add pamusb-agent to the list +of autostarted applications so it will be started automatically. + +* GNOME: + - Open System -> Preferences -> Sessions + - Select Startup Programs and press Add + - Enter pamusb-agent and press OK + - Press Close + +* KDE: + - cd ~/.kde/Autostart + - ln -s /usr/bin/pamusb-agent pamusb-agent + +===== Troubleshooting ===== + + +==== Log Analysis ==== + +Both pam_usb.so and pamusb-agent use the syslog facility to log authentication +attempts. +This can be useful for GUI-driven applications (for instance GDM) where you +don't get to see console output. +Messages are logged with the AUTH facility, they are usually written to +/var/log/auth.log but may vary +depending on the operating system you're using. + +# tail -f /var/log/auth.log +pamusb-agent[25429]: Device "sandisk" has been inserted. Performing +verification... +pamusb-agent[25429]: Executing "/usr/bin/pamusb-check --quiet +--config=/etc/pamusb.conf --service=pamusb-agent scox" +pam_usb[25485]: Authentication request for user "scox" (pamusb-agent) +pam_usb[25485]: Device "sandisk" is connected (good). +pam_usb[25485]: Access granted. +pamusb-agent[25429]: Authentication succeeded. Unlocking user "scox"... +pamusb-agent[25429]: Unlocked. + + +==== Enabling debug ==== + +Enabling debug messages may help you find out what's wrong. + +To enable them, edit /etc/pamusb.conf and set the following option: + + + + +If you wish, you could enable debug messages only for a specific user, device or +service. +For instance, if you want to enable debug messages only for the sudo service, +you could do the following: + + + + + + + +===== It works - What next ? ===== + +* Have a look at the configuration documentation diff --git a/doc/UPGRADING b/doc/UPGRADING new file mode 100644 index 0000000..270b15a --- /dev/null +++ b/doc/UPGRADING @@ -0,0 +1,58 @@ +====== Upgrading ====== + +If you're already using a pam_usb version prior to 0.4.0, you will have to +remove the older version before installing. + +You do not have to do this if you're already using >=0.4.0 or Subversion. + +===== Remove pam_usb.so from pam.d ===== + +$ grep -r pam_usb.so /etc/pam.d +/etc/pam.d/su:auth sufficient pam_usb.so +/etc/pam.d/gdm:auth sufficient pam_usb.so +[...] + +Edit every matching file and remove the pam_usb.so lines. +At the end of the operation, there shouldn't be any file contanining a reference +to pam_usb.so: + +$ grep -r pam_usb /etc/pam.d +$ + + +===== Remove .auth directories ===== + +Older versions of pam_usb used to create .auth directories in both the device +and the user's home directory. Those directories aren't used anymore, so feel +free to remove them: + +# rm -rf /root/.auth +# rm -rf /home/scox/.auth +# rm -rf /media/usbdisk/.auth + + + +===== Remove configuration files ===== + +As configuration files of pam_usb 0.4.0 aren't backward compatible, the old +/etc/pam_usb is no more needed. + +# rm -rf /etc/pam_usb + + + +===== Deinstall pam_usb ===== + +If you installed the old pam_usb version using your operating system's package +manager, then remove it by the same mean. + +Otherwise, you can remove it by hand by performing the following instructions: + +# rm -f /usr/bin/usbadm /usr/share/man/usbadm.1.gz +# rm -f /usr/bin/usbhotplug /etc/hotplug.d/default/pamusb.hotplug +/etc/pam.d/usbhotplug +# rm -f /lib/security/pam_usb.so + +===== Next ===== + +Go aheand and install the new version. diff --git a/doc/pamusb-agent.1.gz b/doc/pamusb-agent.1.gz new file mode 100644 index 0000000000000000000000000000000000000000..6fad245b362fe80a8c5e9d5e289917d1081ca677 GIT binary patch literal 564 zcmV-40?Yj$iwFo?5)eiJ18`w&b#r1ZVP|D-bS^OfZBpB6+dvR~->(?lml)!;otHvs zOYJn&h9(x4{ZN_`)~k`UMA{X5@g-m1S;P%$zxw;XPR4-h&9<)WRz%s_Fm* zYP7;*2Ac!;y}zl2W_&e+M%2M=27($qjG;Gatg2u%zJ{C8=wlCh^dyt}r*%5*F~-vw zJWTHI*keAun0(BWLBx8Z65uwLN};m~Z99|xdRQdsJY`(xjOU_;+qV#e0Kcg=7?fBw z^!MlZD~--oL4_}k@a3tR39OAiZ!1wgzjk#?R`gOWf$w+e?Q)*1=8K2-7AprzgDizD z5KIoZM;W}*1xQnqRhfg(7!BkCWVlrlso$aXii%f8OJwACNoeGEblI2IgrYoI(g(r- zOO&^w;*2K=OA(>;zWKDj<_b4Y{fit}@I;sJtVNFhnnhN1d^^e?)!_vO~@y@-pam)WnG5zKxL21=*`J zexd1f0sknH$-W(lHxWr35!=o;^n$a)jnc#OmVWEsbp0b`Nri&+I;0#ol!nE|*lx62 z(4BrQR29#rQqFhoPu8p1V#y{tvnb$aczF>vQ}EeIv;XE)F&@X*zp}r!mFRW20{{Si C6B5J# literal 0 HcmV?d00001 diff --git a/doc/pamusb-check.1.gz b/doc/pamusb-check.1.gz new file mode 100644 index 0000000000000000000000000000000000000000..898bd3c269d711d002a57598769a3de78c71bbb1 GIT binary patch literal 855 zcmV-d1E~BTiwFo>5)eiJ18`w&b#r1ZV`yb#Yc4SWy;R$7+eQ$5->(=D1u6kHBc%@o zj3lUhK`kJ*nNR{+)P=AlhvcHloCo+U-A&ZfXum`IXiRW2v+#&K?ZMH z=~XV9W(yT+w9;b%*%th(f7?ne;?*s5vJEaf&uh&8fjLBr&Z-7x^D}rmoBbL=M1RTR zZnI7=BO&7S7VZ~!H{!9p9B1~9^w0Z;BweN=)}@F`*}|1rJ6YicJeJFG_+y#CUsU4! zyvC-ZufKPnxzVMn$nd_CzTR6M+Gmv`o3(6lZ!SVR&V33BJ%5XLfigJq!vJGNKdOg# zeUn~&UM8#M!@W3xDVG|YYC}UzF@ZH|CeSOR!PnM=s-|CL(l(R`s4Jy07E2Gx0d*}y z1EsN$8BdYuywVkP!FI->gNZjV5Irt3%p7drM!YCro8Y0A&zwsc-fn@dAcaNQKxa*5 zWjj5CjN)YDOyAPwR(0GjKEd|S4)9fZ>Qx{^oq;MDgg6@VMLKXH&(S#=<%|}B20{Cx z+<3}JUgaY6)>|CXAL%Ut&WAj*j^ubwet+(fg+oG)Nf8BHK)h;-AXk!{Mtr=F zE`0Y8F8ol?yKC9OrO>PK-T}M6VdNk}ILvwKe5V(r9*KxA1!|c!C@yGE1MZClW}zZm zN$dI5*dOSzF*l8|o5oZ}6^zD>S1oQjSsc0O0J6bJheE#jZ11U!^L+PYBwr$MIh1z%?&)9z z=7*Cr98Q_v{741HO-{(<^k9tLUJacJ9^#r;iKjTgKgd;KKpWp{8etgq#e<@IEgz~r zan(<3f|dlVe~8QV@2N-{l*E`rO_CHUy0y;Oft#_ywo)}Lrj2rbkfp_Xb^GvHEObHF h1|P$hFVYqY-tpf5=2SJG$HrD4#J?0}dOJ-A008zEp&vuFl2?i?`h^uU~Gn3Q|y zP@!QCIXvyb?cCMC6ruGJMmBi+ls8(nFo&!djgT;#U%=ID_925TJY{kFxUCi$A+%b; zy12a|_wC}e@V+hc*y%AS5Eaq+;3l*Jr?pIV)sOY2tX37FswK2#16;pp6S~09Amw?F zaty17!*c2vgij-L{ZVC{=iZ`;x?%&uZ+O)?K8psAI}ejz9bO`3`Fc}b->u4ewORis zgRsC-Zs2G|rvfymA!a&a6siN);{hH`49>w;hpDiMgWRQ&`>i6SL>8i2RN)Hx4h)Xkq)GBLr$VgWT u>n481wq9=TNTIkv2H*Y53o~56N89M#m+*V$bE-}Enfw8q4X^b00ssJ#Kj7j3 literal 0 HcmV?d00001 diff --git a/doc/pamusb.conf b/doc/pamusb.conf new file mode 100644 index 0000000..1a3f900 --- /dev/null +++ b/doc/pamusb.conf @@ -0,0 +1,49 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/conf.c b/src/conf.c new file mode 100644 index 0000000..e8b0aa1 --- /dev/null +++ b/src/conf.c @@ -0,0 +1,187 @@ +/* + * Copyright (c) 2003-2007 Andrea Luzzardi + * + * This file is part of the pam_usb project. pam_usb is free software; + * you can redistribute it and/or modify it under the terms of the GNU General + * Public License version 2, as published by the Free Software Foundation. + * + * pam_usb is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 59 Temple + * Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include "conf.h" +#include "xpath.h" +#include "log.h" + +static void pusb_conf_options_get_from(t_pusb_options *opts, + const char *from, + xmlDoc *doc) +{ + pusb_xpath_get_string_from(doc, from, "option[@name='hostname']", + opts->hostname, sizeof(opts->hostname)); + pusb_xpath_get_string_from(doc, from, "option[@name='system_pad_directory']", + opts->system_pad_directory, + sizeof(opts->system_pad_directory)); + pusb_xpath_get_string_from(doc, from, "option[@name='device_pad_directory']", + opts->device_pad_directory, + sizeof(opts->device_pad_directory)); + pusb_xpath_get_bool_from(doc, from, "option[@name='debug']", + &(opts->debug)); + pusb_xpath_get_bool_from(doc, from, "option[@name='quiet']", + &(opts->quiet)); + pusb_xpath_get_bool_from(doc, from, "option[@name='color_log']", + &(opts->color_log)); + pusb_xpath_get_bool_from(doc, from, "option[@name='enable']", + &(opts->enable)); + pusb_xpath_get_bool_from(doc, from, "option[@name='one_time_pad']", + &(opts->one_time_pad)); + pusb_xpath_get_int_from(doc, from, "option[@name='probe_timeout']", + &(opts->probe_timeout)); +} + +static int pusb_conf_parse_options(t_pusb_options *opts, + xmlDoc *doc, + const char *user, + const char *service) +{ + char *xpath = NULL; + size_t xpath_size; + int i; + struct s_opt_list opt_list[] = { + { CONF_DEVICE_XPATH, opts->device.name }, + { CONF_USER_XPATH, (char *)user }, + { CONF_SERVICE_XPATH, (char *)service }, + { NULL, NULL } + }; + + pusb_conf_options_get_from(opts, "//configuration/defaults/", doc); + for (i = 0; opt_list[i].name != NULL; ++i) + { + xpath_size = strlen(opt_list[i].name) + strlen(opt_list[i].value) + 1; + if (!(xpath = malloc(xpath_size))) + { + log_error("malloc error\n"); + return (0); + } + memset(xpath, 0x00, xpath_size); + snprintf(xpath, xpath_size, opt_list[i].name, opt_list[i].value, ""); + pusb_conf_options_get_from(opts, xpath, doc); + free(xpath); + } + return (1); +} + +static int pusb_conf_device_get_property(t_pusb_options *opts, + xmlDoc *doc, + const char *property, + char *store, + size_t size) +{ + char *xpath = NULL; + size_t xpath_len; + int retval; + + xpath_len = strlen(CONF_DEVICE_XPATH) + strlen(opts->device.name) + \ + strlen(property) + 1; + if (!(xpath = malloc(xpath_len))) + { + log_error("malloc error!\n"); + return (0); + } + memset(xpath, 0x00, xpath_len); + snprintf(xpath, xpath_len, CONF_DEVICE_XPATH, opts->device.name, + property); + retval = pusb_xpath_get_string(doc, xpath, store, size); + free(xpath); + return (retval); +} + +static int pusb_conf_parse_device(t_pusb_options *opts, xmlDoc *doc) +{ + if (!pusb_conf_device_get_property(opts, doc, "vendor", opts->device.vendor, + sizeof(opts->device.vendor))) + return (0); + if (!pusb_conf_device_get_property(opts, doc, "model", opts->device.model, + sizeof(opts->device.model))) + return (0); + if (!pusb_conf_device_get_property(opts, doc, "serial", opts->device.serial, + sizeof(opts->device.serial))) + return (0); + pusb_conf_device_get_property(opts, doc, "volume_uuid", + opts->device.volume_uuid, + sizeof(opts->device.volume_uuid)); + return (1); +} + +int pusb_conf_init(t_pusb_options *opts) +{ + memset(opts, 0x00, sizeof(*opts)); + if (gethostname(opts->hostname, sizeof(opts->hostname)) == -1) + { + log_error("gethostname: %s\n", strerror(errno)); + return (0); + } + strcpy(opts->system_pad_directory, ".pamusb"); + strcpy(opts->device_pad_directory, ".pamusb"); + opts->probe_timeout = 10; + opts->enable = 1; + opts->debug = 0; + opts->quiet = 0; + opts->color_log = 1; + opts->one_time_pad = 1; + return (1); +} + +int pusb_conf_parse(const char *file, t_pusb_options *opts, + const char *user, const char *service) +{ + xmlDoc *doc = NULL; + int retval; + char device_xpath[sizeof(CONF_USER_XPATH) + CONF_USER_MAXLEN + \ + sizeof("device")]; + + log_debug("Parsing settings...\n", + user, service); + if (strlen(user) > CONF_USER_MAXLEN) + { + log_error("Username \"%s\" is too long (max: %d).\n", user, + CONF_USER_MAXLEN); + return (0); + } + if (!(doc = xmlReadFile(file, NULL, 0))) + { + log_error("Unable to parse \"%s\".\n", file); + return (0); + } + snprintf(device_xpath, sizeof(device_xpath), CONF_USER_XPATH, user, + "device"); + retval = pusb_xpath_get_string(doc, + device_xpath, + opts->device.name, + sizeof(opts->device.name)); + if (!retval || !pusb_conf_parse_device(opts, doc)) + { + log_error("No device configured for user \"%s\".\n", user); + xmlFreeDoc(doc); + xmlCleanupParser(); + return (0); + } + if (!pusb_conf_parse_options(opts, doc, user, service)) + { + xmlFreeDoc(doc); + xmlCleanupParser(); + return (0); + } + xmlFreeDoc(doc); + xmlCleanupParser(); + return (1); +} diff --git a/src/conf.h b/src/conf.h new file mode 100644 index 0000000..4570048 --- /dev/null +++ b/src/conf.h @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2003-2007 Andrea Luzzardi + * + * This file is part of the pam_usb project. pam_usb is free software; + * you can redistribute it and/or modify it under the terms of the GNU General + * Public License version 2, as published by the Free Software Foundation. + * + * pam_usb is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 59 Temple + * Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef PUSB_CONF_H_ +# define PUSB_CONF_H_ +# define PUSB_CONF_FILE "/etc/pamusb.conf" +# define CONF_DEVICE_XPATH "//configuration/devices/device[@id='%s']/%s" +# define CONF_USER_XPATH "//configuration/users/user[@id='%s']/%s" +# define CONF_SERVICE_XPATH "//configuration/services/service[@id='%s']/%s" +# define CONF_USER_MAXLEN 32 +# include +# include +# ifndef PATH_MAX +# define PATH_MAX 4096 +# endif + +typedef struct pusb_device +{ + char name[32]; + char vendor[128]; + char model[128]; + char serial[128]; + char volume_uuid[128]; +} t_pusb_device; + +typedef struct pusb_options +{ + int probe_timeout; + int enable; + int debug; + int quiet; + int color_log; + int one_time_pad; + char hostname[32]; + char system_pad_directory[PATH_MAX]; + char device_pad_directory[PATH_MAX]; + t_pusb_device device; +} t_pusb_options; + +struct s_opt_list +{ + char *name; + char *value; +}; + +int pusb_conf_init(t_pusb_options *opts); +int pusb_conf_parse(const char *file, t_pusb_options *opts, const char *user, const char *service); + +#endif /* !PUSB_CONF_H_ */ diff --git a/src/device.c b/src/device.c new file mode 100644 index 0000000..8c1642c --- /dev/null +++ b/src/device.c @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2003-2007 Andrea Luzzardi + * + * This file is part of the pam_usb project. pam_usb is free software; + * you can redistribute it and/or modify it under the terms of the GNU General + * Public License version 2, as published by the Free Software Foundation. + * + * pam_usb is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 59 Temple + * Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include "conf.h" +#include "hal.h" +#include "log.h" +#include "pad.h" +#include "device.h" + +static int pusb_device_connected(t_pusb_options *opts, LibHalContext *ctx) +{ + char *udi = NULL; + + log_debug("Searching for \"%s\" in the hardware database...\n", + opts->device.name); + udi = pusb_hal_find_item(ctx, + "usb_device.serial", opts->device.serial, + "usb_device.vendor", opts->device.vendor, + "info.product", opts->device.model, + NULL); + if (!udi) + { + log_error("Device \"%s\" is not connected.\n", + opts->device.name); + return (0); + } + libhal_free_string(udi); + log_info("Device \"%s\" is connected (good).\n", opts->device.name); + return (1); +} + +int pusb_device_check(t_pusb_options *opts, + const char *user) +{ + DBusConnection *dbus = NULL; + LibHalContext *ctx = NULL; + int retval = 0; + + log_debug("Connecting to HAL...\n"); + if (!(dbus = pusb_hal_dbus_connect())) + return (0); + + if (!(ctx = pusb_hal_init(dbus))) + { + pusb_hal_dbus_disconnect(dbus); + return (0); + } + + if (!pusb_device_connected(opts, ctx)) + { + pusb_hal_dbus_disconnect(dbus); + libhal_ctx_free(ctx); + return (0); + } + + if (opts->one_time_pad) + { + log_info("Performing one time pad verification...\n"); + retval = pusb_pad_check(opts, ctx, user); + } + else + { + log_debug("One time pad is disabled, no more verifications to do.\n"); + retval = 1; + } + + pusb_hal_dbus_disconnect(dbus); + libhal_ctx_free(ctx); + return (retval); +} diff --git a/src/device.h b/src/device.h new file mode 100644 index 0000000..e9a3851 --- /dev/null +++ b/src/device.h @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2003-2007 Andrea Luzzardi + * + * This file is part of the pam_usb project. pam_usb is free software; + * you can redistribute it and/or modify it under the terms of the GNU General + * Public License version 2, as published by the Free Software Foundation. + * + * pam_usb is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 59 Temple + * Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef PUSB_DEVICE_H_ +# define PUSB_DEVICE_H_ + +int pusb_device_check(t_pusb_options *opts, const char *user); + +#endif /* !PUSB_DEVICE_H_ */ diff --git a/src/hal.c b/src/hal.c new file mode 100644 index 0000000..c19c351 --- /dev/null +++ b/src/hal.c @@ -0,0 +1,189 @@ +/* + * Copyright (c) 2003-2007 Andrea Luzzardi + * + * This file is part of the pam_usb project. pam_usb is free software; + * you can redistribute it and/or modify it under the terms of the GNU General + * Public License version 2, as published by the Free Software Foundation. + * + * pam_usb is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 59 Temple + * Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include "log.h" + +DBusConnection *pusb_hal_dbus_connect(void) +{ + DBusConnection *dbus = NULL; + DBusError error; + + dbus_error_init(&error); + if (!(dbus = dbus_bus_get(DBUS_BUS_SYSTEM, &error))) + { + log_error("Cannot connect to system bus: %s\n", + error.message); + dbus_error_free(&error); + return (NULL); + } + return (dbus); +} + +void pusb_hal_dbus_disconnect(DBusConnection *dbus) +{ + dbus_connection_unref(dbus); +} + +LibHalContext *pusb_hal_init(DBusConnection *dbus) +{ + DBusError error; + LibHalContext *ctx = NULL; + + dbus_error_init(&error); + if (!(ctx = libhal_ctx_new())) + { + log_error("Failed to create a HAL context\n"); + return (NULL); + } + if (!libhal_ctx_set_dbus_connection(ctx, dbus)) + { + log_error("Failed to attach dbus connection to hal\n"); + libhal_ctx_free(ctx); + return (NULL); + } + if (!libhal_ctx_init(ctx, &error)) + { + log_error("libhal_ctx_init: %s\n", error.name, error.message); + libhal_ctx_free(ctx); + return (NULL); + } + return (ctx); +} + +void pusb_hal_destroy(LibHalContext *ctx) +{ + libhal_ctx_free(ctx); +} + +char *pusb_hal_get_property(LibHalContext *ctx, + const char *udi, + const char *name) +{ + DBusError error; + char *data; + + dbus_error_init(&error); + data = libhal_device_get_property_string(ctx, udi, + name, &error); + if (!data) + { + log_debug("%s\n", error.message); + dbus_error_free(&error); + return (NULL); + } + return (data); +} + +int pusb_hal_check_property(LibHalContext *ctx, + const char *udi, + const char *name, + const char *value) +{ + char *data; + int retval; + + data = pusb_hal_get_property(ctx, udi, name); + if (!data) + return (0); + retval = (strcmp(data, value) == 0); + libhal_free_string(data); + return (retval); +} + +char **pusb_hal_find_all_items(LibHalContext *ctx, + const char *property, + const char *value, + int *count) + +{ + DBusError error; + char **devices; + int n_devices; + + dbus_error_init(&error); + *count = 0; + devices = libhal_manager_find_device_string_match(ctx, + property, + value, + &n_devices, + &error); + if (!devices) + { + log_error("Unable to find item \"%s\": %s\n", property, + error.message); + dbus_error_free(&error); + return (NULL); + } + if (!n_devices) + { + libhal_free_string_array(devices); + return (NULL); + } + *count = n_devices; + return (devices); +} + +char *pusb_hal_find_item(LibHalContext *ctx, + const char *property, + const char *value, + ...) +{ + char **devices; + int n_devices; + char *udi = NULL; + va_list ap; + int i; + + devices = pusb_hal_find_all_items(ctx, property, value, &n_devices); + if (!devices) + return (NULL); + if (!n_devices) + return (NULL); + + for (i = 0; i < n_devices; ++i) + { + char *key = NULL; + int match = 1; + + va_start(ap, value); + while ((key = va_arg(ap, char *))) + { + char *value = NULL; + + value = va_arg(ap, char *); + if (!pusb_hal_check_property(ctx, devices[i], + key, value)) + { + match = 0; + break; + } + match = 1; + } + if (match) + { + udi = strdup(devices[i]); + break; + } + va_end(ap); + } + libhal_free_string_array(devices); + return (udi); +} diff --git a/src/hal.h b/src/hal.h new file mode 100644 index 0000000..43f597a --- /dev/null +++ b/src/hal.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2003-2007 Andrea Luzzardi + * + * This file is part of the pam_usb project. pam_usb is free software; + * you can redistribute it and/or modify it under the terms of the GNU General + * Public License version 2, as published by the Free Software Foundation. + * + * pam_usb is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 59 Temple + * Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef PUSB_HAL_H_ +# define PUSB_HAL_H_ + +DBusConnection *pusb_hal_dbus_connect(void); +void pusb_hal_dbus_disconnect(DBusConnection *dbus); +LibHalContext *pusb_hal_init(DBusConnection *dbus); +void pusb_hal_destroy(LibHalContext *ctx); +char *pusb_hal_get_property(LibHalContext *ctx, const char *udi, const char *name); +int pusb_hal_check_property(LibHalContext *ctx, const char *udi, const char *name, const char *value); +char *pusb_hal_find_item(LibHalContext *ctx, const char *property, const char *value, ...); + +#endif /* !PUSB_HAL_H_ */ diff --git a/src/local.c b/src/local.c new file mode 100644 index 0000000..b085f49 --- /dev/null +++ b/src/local.c @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2003-2007 Andrea Luzzardi + * + * This file is part of the pam_usb project. pam_usb is free software; + * you can redistribute it and/or modify it under the terms of the GNU General + * Public License version 2, as published by the Free Software Foundation. + * + * pam_usb is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 59 Temple + * Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include "log.h" +#include "conf.h" + +int pusb_local_login(t_pusb_options *opts, const char *user) +{ + struct utmp utsearch; + struct utmp *utent; + const char *from; + int i; + + log_debug("Checking whether the caller is local or not...\n"); + from = ttyname(STDIN_FILENO); + if (!from || !(*from)) + { + log_debug("Couldn't retrieve the tty name, aborting.\n"); + return (1); + } + if (!strncmp(from, "/dev/", strlen("/dev/"))) + from += strlen("/dev/"); + log_debug("Authentication request from tty %s\n", from); + strncpy(utsearch.ut_line, from, sizeof(utsearch.ut_line)); + setutent(); + utent = getutline(&utsearch); + endutent(); + if (!utent) + { + log_debug("No utmp entry found for tty \"%s\"\n", + from); + return (1); + } + for (i = 0; i < 4; ++i) + { + if (utent->ut_addr_v6[i] != 0) + { + log_error("Remote authentication request: %s\n", utent->ut_host); + return (0); + } + } + log_debug("Caller is local (good)\n"); + return (1); +} diff --git a/src/local.h b/src/local.h new file mode 100644 index 0000000..1efb66c --- /dev/null +++ b/src/local.h @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2003-2007 Andrea Luzzardi + * + * This file is part of the pam_usb project. pam_usb is free software; + * you can redistribute it and/or modify it under the terms of the GNU General + * Public License version 2, as published by the Free Software Foundation. + * + * pam_usb is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 59 Temple + * Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef PUSB_LOCAL_H_ +# define PUSB_LOCAL_H_ + +int pusb_local_login(t_pusb_options *opts, const char *user); + +#endif /* !PUSB_LOCAL_H_ */ diff --git a/src/log.c b/src/log.c new file mode 100644 index 0000000..9918982 --- /dev/null +++ b/src/log.c @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2003-2007 Andrea Luzzardi + * + * This file is part of the pam_usb project. pam_usb is free software; + * you can redistribute it and/or modify it under the terms of the GNU General + * Public License version 2, as published by the Free Software Foundation. + * + * pam_usb is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 59 Temple + * Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include "conf.h" +#include "log.h" + +static t_pusb_options *pusb_opts = NULL; + +static void pusb_log_syslog(int level, const char *format, va_list ap) +{ + openlog("pam_usb", LOG_PID, LOG_AUTH); + vsyslog(level, format, ap); + closelog(); +} + +static void pusb_log_output(int level, const char *format, va_list ap) +{ + if ((pusb_opts && !pusb_opts->quiet) || + level == LOG_ERR) + { + if (pusb_opts && pusb_opts->color_log) + { + if (level == LOG_ERR) + fprintf(stderr, "\033[01;31m*\033[00m "); + else if (level == LOG_NOTICE) + fprintf(stderr, "\033[01;32m*\033[00m "); + } + else + fprintf(stderr, "* "); + vfprintf(stderr, format, ap); + } +} + +void __log_debug(const char *file, int line, const char *fmt, ...) +{ + va_list ap; + + if (!pusb_opts || !pusb_opts->debug) + return ; + fprintf(stderr, "[%s:%03d] ", file, line); + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + + va_start(ap, fmt); + pusb_log_syslog(LOG_DEBUG, fmt, ap); + va_end(ap); +} + +void log_error(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + pusb_log_syslog(LOG_ERR, fmt, ap); + va_end(ap); + + va_start(ap, fmt); + pusb_log_output(LOG_ERR, fmt, ap); + va_end(ap); +} + +void log_info(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + pusb_log_syslog(LOG_NOTICE, fmt, ap); + va_end(ap); + + va_start(ap, fmt); + pusb_log_output(LOG_NOTICE, fmt, ap); + va_end(ap); +} + +void pusb_log_init(t_pusb_options *opts) +{ + pusb_opts = opts; +} diff --git a/src/log.h b/src/log.h new file mode 100644 index 0000000..7c779e8 --- /dev/null +++ b/src/log.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2003-2007 Andrea Luzzardi + * + * This file is part of the pam_usb project. pam_usb is free software; + * you can redistribute it and/or modify it under the terms of the GNU General + * Public License version 2, as published by the Free Software Foundation. + * + * pam_usb is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 59 Temple + * Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef PUSB_LOG_H_ +# define PUSB_LOG_H_ +# define log_debug(s, ...) __log_debug(__FILE__, __LINE__, s, ##__VA_ARGS__) +# include "conf.h" + +void __log_debug(const char *file, int line, const char *fmt, ...); +void log_error(const char *fmt, ...); +void log_info(const char *fmt, ...); +void pusb_log_init(t_pusb_options *opts); + + +#endif /* !PUSB_LOG_H_ */ diff --git a/src/pad.c b/src/pad.c new file mode 100644 index 0000000..88d0fab --- /dev/null +++ b/src/pad.c @@ -0,0 +1,222 @@ +/* + * Copyright (c) 2003-2007 Andrea Luzzardi + * + * This file is part of the pam_usb project. pam_usb is free software; + * you can redistribute it and/or modify it under the terms of the GNU General + * Public License version 2, as published by the Free Software Foundation. + * + * pam_usb is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 59 Temple + * Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "conf.h" +#include "log.h" +#include "volume.h" +#include "pad.h" + +static FILE *pusb_pad_open_device(t_pusb_options *opts, + LibHalVolume *volume, + const char *user, + const char *mode) +{ + FILE *f; + char path[PATH_MAX]; + const char *mnt_point; + struct stat sb; + + mnt_point = (char *)libhal_volume_get_mount_point(volume); + if (!mnt_point) + return (NULL); + memset(path, 0x00, PATH_MAX); + snprintf(path, PATH_MAX, "%s/%s", mnt_point, opts->device_pad_directory); + if (stat(path, &sb) != 0) + { + log_debug("Directory %s does not exist, creating one.\n", path); + if (mkdir(path, S_IRUSR | S_IWUSR | S_IXUSR) != 0) + { + log_debug("Unable to create directory %s: %s\n", path, + strerror(errno)); + return (NULL); + } + memset(path, 0x00, PATH_MAX); + } + snprintf(path, PATH_MAX, "%s/%s/%s.%s.pad", mnt_point, + opts->device_pad_directory, user, opts->hostname); + f = fopen(path, mode); + if (!f) + { + log_debug("Cannot open device file: %s\n", strerror(errno)); + return (NULL); + } + return (f); +} + +static FILE *pusb_pad_open_system(t_pusb_options *opts, + const char *user, + const char *mode) +{ + FILE *f; + char path[PATH_MAX]; + struct passwd *user_ent = NULL; + struct stat sb; + + if (!(user_ent = getpwnam(user)) || !(user_ent->pw_dir)) + { + log_error("Unable to retrieve informations for user \"%s\": %s\n", + strerror(errno)); + return (0); + } + memset(path, 0x00, PATH_MAX); + snprintf(path, PATH_MAX, "%s/%s", user_ent->pw_dir, + opts->system_pad_directory); + if (stat(path, &sb) != 0) + { + log_debug("Directory %s does not exist, creating one.\n", path); + if (mkdir(path, S_IRUSR | S_IWUSR | S_IXUSR) != 0) + { + log_debug("Unable to create directory %s: %s\n", path, + strerror(errno)); + return (NULL); + } + chown(path, user_ent->pw_uid, user_ent->pw_gid); + chmod(path, S_IRUSR | S_IWUSR | S_IXUSR); + } + memset(path, 0x00, PATH_MAX); + snprintf(path, PATH_MAX, "%s/%s/%s.pad", user_ent->pw_dir, + opts->system_pad_directory, opts->device.name); + f = fopen(path, mode); + if (!f) + { + log_debug("Cannot open system file: %s\n", strerror(errno)); + return (NULL); + } + return (f); +} + +static int pusb_pad_protect(const char *user, int fd) +{ + struct passwd *user_ent = NULL; + + log_debug("Protecting pad file...\n"); + if (!(user_ent = getpwnam(user))) + { + log_error("Unable to retrieve informations for user \"%s\": %s\n", + strerror(errno)); + return (0); + } + if (fchown(fd, user_ent->pw_uid, user_ent->pw_gid) == -1) + { + log_error("Unable to change owner of the pad: %s\n", + strerror(errno)); + return (0); + } + if (fchmod(fd, S_IRUSR | S_IWUSR) == -1) + { + log_error("Unable to change mode of the pad: %s\n", + strerror(errno)); + return (0); + } + return (1); +} + +static void pusb_pad_update(t_pusb_options *opts, + LibHalVolume *volume, + const char *user) +{ + FILE *f_device = NULL; + FILE *f_system = NULL; + char magic[1024]; + int i; + + if (!(f_device = pusb_pad_open_device(opts, volume, user, "w+"))) + { + log_error("Unable to update pads.\n"); + return ; + } + pusb_pad_protect(user, fileno(f_device)); + if (!(f_system = pusb_pad_open_system(opts, user, "w+"))) + { + log_error("Unable to update pads.\n"); + fclose(f_device); + return ; + } + pusb_pad_protect(user, fileno(f_system)); + log_debug("Generating %d bytes unique pad...\n", sizeof(magic)); + srand(getpid() * time(NULL)); + for (i = 0; i < sizeof(magic); ++i) + magic[i] = (char)rand(); + log_debug("Writing pad to the device...\n"); + fwrite(magic, sizeof(char), sizeof(magic), f_system); + log_debug("Writing pad to the system...\n"); + fwrite(magic, sizeof(char), sizeof(magic), f_device); + log_debug("Synchronizing filesystems...\n"); + fclose(f_system); + fclose(f_device); + sync(); + log_debug("One time pads updated.\n"); +} + +static int pusb_pad_compare(t_pusb_options *opts, LibHalVolume *volume, + const char *user) +{ + FILE *f_device = NULL; + FILE *f_system = NULL; + char magic_device[1024]; + char magic_system[1024]; + int retval; + + if (!(f_system = pusb_pad_open_system(opts, user, "r"))) + return (1); + if (!(f_device = pusb_pad_open_device(opts, volume, user, "r"))) + { + fclose(f_system); + return (0); + } + log_debug("Loading device pad...\n"); + fread(magic_device, sizeof(char), sizeof(magic_device), f_device); + log_debug("Loading system pad...\n"); + fread(magic_system, sizeof(char), sizeof(magic_system), f_system); + retval = memcmp(magic_system, magic_device, sizeof(magic_system)); + fclose(f_system); + fclose(f_device); + if (!retval) + log_debug("Pad match.\n"); + return (retval == 0); +} + +int pusb_pad_check(t_pusb_options *opts, LibHalContext *ctx, + const char *user) +{ + LibHalVolume *volume = NULL; + int retval; + + volume = pusb_volume_get(opts, ctx); + if (!volume) + return (0); + retval = pusb_pad_compare(opts, volume, user); + if (retval) + { + log_info("Verification match, updating one time pads...\n"); + pusb_pad_update(opts, volume, user); + } + else + log_error("Pad checking failed !\n"); + pusb_volume_destroy(volume); + return (retval); +} diff --git a/src/pad.h b/src/pad.h new file mode 100644 index 0000000..3814ab1 --- /dev/null +++ b/src/pad.h @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2003-2007 Andrea Luzzardi + * + * This file is part of the pam_usb project. pam_usb is free software; + * you can redistribute it and/or modify it under the terms of the GNU General + * Public License version 2, as published by the Free Software Foundation. + * + * pam_usb is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 59 Temple + * Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef PUSB_OTP_H_ +# define PUSB_OTP_H_ + +int pusb_pad_check(t_pusb_options *opts, LibHalContext *ctx, const char *user); + +#endif /* !PUSB_OTP_H_ */ diff --git a/src/pam.c b/src/pam.c new file mode 100644 index 0000000..539f38c --- /dev/null +++ b/src/pam.c @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2003-2007 Andrea Luzzardi + * + * This file is part of the pam_usb project. pam_usb is free software; + * you can redistribute it and/or modify it under the terms of the GNU General + * Public License version 2, as published by the Free Software Foundation. + * + * pam_usb is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 59 Temple + * Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define PAM_SM_AUTH +#include +#include + +#include "version.h" +#include "conf.h" +#include "log.h" +#include "local.h" +#include "device.h" + +PAM_EXTERN +int pam_sm_authenticate(pam_handle_t *pamh, int flags, + int argc, const char **argv) +{ + t_pusb_options opts; + const char *service; + const char *user; + const char *tty; + char *conf_file = PUSB_CONF_FILE; + int retval; + + retval = pam_get_item(pamh, PAM_SERVICE, (const void **)&service); + if (retval != PAM_SUCCESS) + { + log_error("Unable to retrieve the PAM service name.\n"); + return (PAM_AUTH_ERR); + } + + if (pam_get_user(pamh, &user, NULL) != PAM_SUCCESS || !user || !*user) + { + log_error("Unable to retrieve the PAM user name.\n"); + return (PAM_AUTH_ERR); + } + + if (argc > 1) + if (!strcmp(argv[0], "-c")) + conf_file = (char *)argv[1]; + pusb_conf_init(&opts); + if (!pusb_conf_parse(conf_file, &opts, user, service)) + return (PAM_AUTH_ERR); + + pusb_log_init(&opts); + if (!opts.enable) + { + log_debug("Not enabled, exiting...\n"); + return (PAM_IGNORE); + } + + log_info("pam_usb v%s\n", PUSB_VERSION); + log_info("Authentication request for user \"%s\" (%s)\n", + user, service); + + if (pam_get_item(pamh, PAM_TTY, (const void **)&tty) == PAM_SUCCESS) + { + if (tty && !strcmp(tty, "ssh")) + { + log_debug("SSH Authentication, aborting.\n"); + return (0); + } + } + if (!pusb_local_login(&opts, user)) + { + log_error("Access denied.\n"); + return (PAM_AUTH_ERR); + } + if (pusb_device_check(&opts, user)) + { + log_info("Access granted.\n"); + return (PAM_SUCCESS); + } + log_error("Access denied.\n"); + return (PAM_AUTH_ERR); +} + +PAM_EXTERN +int pam_sm_setcred(pam_handle_t *pamh,int flags,int argc, + const char **argv) +{ + return (PAM_SUCCESS); +} + +#ifdef PAM_STATIC + +struct pam_module _pam_usb_modstruct = { + "pam_usb", + pam_sm_authenticate, + pam_sm_setcred, + NULL, + NULL, + NULL, + NULL +}; + +#endif diff --git a/src/pamusb-check.c b/src/pamusb-check.c new file mode 100644 index 0000000..6f8772f --- /dev/null +++ b/src/pamusb-check.c @@ -0,0 +1,140 @@ +/* + * Copyright (c) 2003-2007 Andrea Luzzardi + * + * This file is part of the pam_usb project. pam_usb is free software; + * you can redistribute it and/or modify it under the terms of the GNU General + * Public License version 2, as published by the Free Software Foundation. + * + * pam_usb is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 59 Temple + * Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include "conf.h" +#include "log.h" +#include "device.h" +#include "local.h" + +static void pusb_check_conf_dump(t_pusb_options *opts, const char *username, + const char *service) +{ + fprintf(stdout, "Configuration dump for user %s (service: %s):\n", + username, service); + fprintf(stdout, "enable\t\t\t: %s\n", opts->enable ? "true" : "false"); + fprintf(stdout, "debug\t\t\t: %s\n", opts->debug ? "true" : "false"); + fprintf(stdout, "quiet\t\t\t: %s\n", opts->quiet ? "true" : "false"); + fprintf(stdout, "color_log\t\t: %s\n", opts->color_log ? "true" : "false"); + fprintf(stdout, "one_time_pad\t\t: %s\n", + opts->one_time_pad ? "true" : "false"); + fprintf(stdout, "probe_timeout\t\t: %d\n", opts->probe_timeout); + fprintf(stdout, "hostname\t\t: %s\n", opts->hostname); + fprintf(stdout, "system_pad_directory\t: %s\n", + opts->system_pad_directory); + fprintf(stdout, "device_pad_directory\t: %s\n", + opts->device_pad_directory); +} + +static int pusb_check_perform_authentication(t_pusb_options *opts, + const char *user, + const char *service) +{ + int retval; + + if (!opts->enable) + { + log_debug("Not enabled, exiting...\n"); + return (0); + } + log_info("Authentication request for user \"%s\" (%s)\n", + user, service); + if (!pusb_local_login(opts, user)) + { + log_error("Access denied.\n"); + return (0); + } + retval = pusb_device_check(opts, user); + if (retval) + log_info("Access granted.\n"); + else + log_error("Access denied.\n"); + return (retval); +} + +static void pusb_check_usage(const char *name) +{ + fprintf(stderr, "Usage: %s [--help] [--config=path] [--service=name] [--dump] [--quiet]" \ + " \n", name); +} + +int main(int argc, char **argv) +{ + t_pusb_options opts; + char *conf_file = PUSB_CONF_FILE; + char *service = "pamusb-check"; + char *user = NULL; + int quiet = 0; + int dump = 0; + int opt; + int opt_index = 0; + extern char *optarg; + char *short_options = "hc:s:dq"; + struct option long_options[] = { + { "help", 0, 0, 0}, + { "config", 1, 0, 0}, + { "service", 1, 0, 0}, + { "dump", 0, &dump, 1 }, + { "quiet", 0, &quiet, 1}, + { 0, 0, 0, 0} + }; + + while ((opt = getopt_long(argc, argv, short_options, long_options, + &opt_index)) != EOF) + { + if (opt == 'h' || (!opt && !strcmp(long_options[opt_index].name, "help"))) + { + pusb_check_usage(argv[0]); + return (1); + } + else if (opt == 'c' || (!opt && !strcmp(long_options[opt_index].name, "config"))) + conf_file = optarg; + else if (opt == 's' || (!opt && !strcmp(long_options[opt_index].name, "service"))) + service = optarg; + else if (opt == '?') + { + pusb_check_usage(argv[0]); + return (1); + } + } + + if ((argc - 1) == optind) + user = argv[optind]; + else + { + pusb_check_usage(argv[0]); + return (1); + } + pusb_conf_init(&opts); + if (!pusb_conf_parse(conf_file, &opts, user, service)) + return (1); + if (quiet) + { + opts.quiet = 1; + opts.debug = 0; + } + pusb_log_init(&opts); + if (dump) + { + pusb_check_conf_dump(&opts, user, service); + return (1); + } + return (!pusb_check_perform_authentication(&opts, user, service)); +} diff --git a/src/version.h b/src/version.h new file mode 100644 index 0000000..d47d001 --- /dev/null +++ b/src/version.h @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2003-2007 Andrea Luzzardi + * + * This file is part of the pam_usb project. pam_usb is free software; + * you can redistribute it and/or modify it under the terms of the GNU General + * Public License version 2, as published by the Free Software Foundation. + * + * pam_usb is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 59 Temple + * Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef PUSB_VERSION_H_ +# define PUSB_VERSION_H_ + +# define PUSB_VERSION "0.4.0" + +#endif /* !PUSB_VERSION_H_ */ diff --git a/src/volume.c b/src/volume.c new file mode 100644 index 0000000..449d71d --- /dev/null +++ b/src/volume.c @@ -0,0 +1,152 @@ +/* + * Copyright (c) 2003-2007 Andrea Luzzardi + * + * This file is part of the pam_usb project. pam_usb is free software; + * you can redistribute it and/or modify it under the terms of the GNU General + * Public License version 2, as published by the Free Software Foundation. + * + * pam_usb is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 59 Temple + * Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "conf.h" +#include "log.h" +#include "hal.h" +#include "volume.h" + +static int pusb_volume_mount(t_pusb_options *opts, LibHalVolume **volume, + LibHalContext *ctx) +{ + char command[1024]; + char tempname[32]; + const char *devname; + const char *udi; + const char *fs; + + snprintf(tempname, sizeof(tempname), "pam_usb%d", getpid()); + if (!(devname = libhal_volume_get_device_file(*volume))) + { + log_error("Unable to retrieve device filename\n"); + return (0); + } + fs = libhal_volume_get_fstype(*volume); + log_debug("Attempting to mount device %s with label %s\n", + devname, tempname); + if (!fs) + snprintf(command, sizeof(command), "pmount -s %s %s", + devname, tempname); + else + snprintf(command, sizeof(command), "pmount -s -t %s %s %s", + fs, devname, tempname); + log_debug("Executing \"%s\"\n", command); + if (system(command) != 0) + { + log_error("Mount failed\n"); + return (0); + } + udi = libhal_volume_get_udi(*volume); + if (!udi) + { + log_error("Unable to retrieve volume UDI\n"); + return (0); + } + udi = strdup(udi); + libhal_volume_free(*volume); + *volume = libhal_volume_from_udi(ctx, udi); + free((char *)udi); + log_debug("Mount succeeded.\n"); + return (1); +} + +static LibHalVolume *pusb_volume_probe(t_pusb_options *opts, + LibHalContext *ctx) +{ + LibHalVolume *volume = NULL; + int maxtries = 0; + int i; + + if (!*(opts->device.volume_uuid)) + { + log_debug("No UUID configured for device\n"); + return (NULL); + } + log_debug("Searching for volume with uuid %s\n", opts->device.volume_uuid); + maxtries = ((opts->probe_timeout * 1000000) / 250000); + for (i = 0; i < maxtries; ++i) + { + char *udi = NULL; + + if (i == 1) + log_info("Probing volume (this could take a while)...\n"); + udi = pusb_hal_find_item(ctx, + "volume.uuid", opts->device.volume_uuid, + NULL); + if (!udi) + { + usleep(250000); + continue; + } + volume = libhal_volume_from_udi(ctx, udi); + libhal_free_string(udi); + if (!libhal_volume_should_ignore(volume)) + return (volume); + libhal_volume_free(volume); + usleep(250000); + } + return (NULL); +} + +LibHalVolume *pusb_volume_get(t_pusb_options *opts, LibHalContext *ctx) +{ + LibHalVolume *volume; + + if (!(volume = pusb_volume_probe(opts, ctx))) + return (NULL); + log_debug("Found volume %s\n", opts->device.volume_uuid); + if (libhal_volume_is_mounted(volume)) + { + log_debug("Volume is already mounted.\n"); + return (volume); + } + if (!pusb_volume_mount(opts, &volume, ctx)) + { + libhal_volume_free(volume); + return (NULL); + } + return (volume); +} + +void pusb_volume_destroy(LibHalVolume *volume) +{ + const char *mntpoint; + + mntpoint = libhal_volume_get_mount_point(volume); + if (mntpoint && strstr(mntpoint, "pam_usb")) + { + char command[1024]; + + log_debug("Attempting to umount %s\n", + mntpoint); + snprintf(command, sizeof(command), "pumount %s", mntpoint); + log_debug("Executing \"%s\"\n", command); + if (!system(command)) + log_debug("Umount succeeded.\n"); + else + log_error("Unable to umount %s\n", mntpoint); + } + libhal_volume_free(volume); +} diff --git a/src/volume.h b/src/volume.h new file mode 100644 index 0000000..74ab98d --- /dev/null +++ b/src/volume.h @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2003-2007 Andrea Luzzardi + * + * This file is part of the pam_usb project. pam_usb is free software; + * you can redistribute it and/or modify it under the terms of the GNU General + * Public License version 2, as published by the Free Software Foundation. + * + * pam_usb is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 59 Temple + * Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef VOLUME_H_ +# define VOLUME_H_ + +LibHalVolume *pusb_volume_get(t_pusb_options *opts, LibHalContext *ctx); +void pusb_volume_destroy(LibHalVolume *volume); + +#endif /* !VOLUME_H_ */ diff --git a/src/xpath.c b/src/xpath.c new file mode 100644 index 0000000..cd2ffeb --- /dev/null +++ b/src/xpath.c @@ -0,0 +1,219 @@ +/* + * Copyright (c) 2003-2007 Andrea Luzzardi + * + * This file is part of the pam_usb project. pam_usb is free software; + * you can redistribute it and/or modify it under the terms of the GNU General + * Public License version 2, as published by the Free Software Foundation. + * + * pam_usb is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 59 Temple + * Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include "xpath.h" +#include "log.h" + +static xmlXPathObject *pusb_xpath_match(xmlDocPtr doc, const char *path) +{ + xmlXPathContext *context = NULL; + xmlXPathObject *result = NULL; + + context = xmlXPathNewContext(doc); + if (context == NULL) + { + log_error("Unable to create XML context\n"); + return (NULL); + } + result = xmlXPathEvalExpression((xmlChar *)path, context); + xmlXPathFreeContext(context); + if (result == NULL) + { + log_error("Error in xmlXPathEvalExpression\n"); + return (NULL); + } + if (xmlXPathNodeSetIsEmpty(result->nodesetval)) + { + xmlXPathFreeObject(result); + return (NULL); + } + return (result); +} + +static int pusb_xpath_strip_string(char *dest, const char *src, + size_t size) +{ + int first_char = -1; + int last_char = -1; + int i; + + for (i = 0; src[i]; ++i) + { + if (isspace(src[i])) + continue ; + + if (first_char == -1) + first_char = i; + last_char = i; + } + + if (first_char == -1 || last_char == -1) + return (0); + + if ((last_char - first_char) > (size - 1)) + return (0); + + memset(dest, 0x0, size); + strncpy(dest, &(src[first_char]), last_char - first_char + 1); + return (1); +} + +int pusb_xpath_get_string(xmlDocPtr doc, const char *path, + char *value, size_t size) +{ + xmlXPathObject *result = NULL; + xmlNode *node = NULL; + xmlChar *result_string = NULL; + + if (!(result = pusb_xpath_match(doc, path))) + return (0); + + if (result->nodesetval->nodeNr > 1) + { + xmlXPathFreeObject(result); + log_debug("Syntax error: %s: more than one record found\n", path); + return (0); + } + + node = result->nodesetval->nodeTab[0]->xmlChildrenNode; + result_string = xmlNodeListGetString(doc, node, 1); + if (!result_string) + { + xmlXPathFreeObject(result); + log_debug("Empty value for %s\n", path); + return (0); + } + if (!pusb_xpath_strip_string(value, (const char *)result_string, size)) + { + xmlFree(result_string); + xmlXPathFreeObject(result); + log_debug("Result for %s (%s) is too long (max: %d)\n", + path, (const char *)result_string, size); + return (0); + } + xmlFree(result_string); + xmlXPathFreeObject(result); + return (1); +} + +int pusb_xpath_get_string_from(xmlDocPtr doc, + const char *base, + const char *path, + char *value, size_t size) +{ + char *xpath = NULL; + size_t xpath_size; + int retval; + + xpath_size = strlen(base) + strlen(path) + 1; + if (!(xpath = malloc(xpath_size))) + { + log_error("malloc error !\n"); + return (0); + } + memset(xpath, 0x00, xpath_size); + snprintf(xpath, xpath_size, "%s%s", base, path); + retval = pusb_xpath_get_string(doc, xpath, value, size); + if (retval) + log_debug("%s%s -> %s\n", base, path, value); + free(xpath); + return (retval); +} + +int pusb_xpath_get_bool(xmlDocPtr doc, const char *path, int *value) +{ + char ret[6]; /* strlen("false") + 1 */ + + if (!pusb_xpath_get_string(doc, path, ret, sizeof(ret))) + return (0); + + if (!strcmp(ret, "true")) + { + *value = 1; + return (1); + } + + if (!strcmp(ret, "false")) + { + *value = 0; + return (1); + } + + log_debug("Expecting a boolean, got %s\n", ret); + return (0); +} + +int pusb_xpath_get_bool_from(xmlDocPtr doc, + const char *base, + const char *path, + int *value) +{ + char *xpath = NULL; + size_t xpath_size; + int retval; + + xpath_size = strlen(base) + strlen(path) + 1; + if (!(xpath = malloc(xpath_size))) + { + log_error("malloc error!\n"); + return (0); + } + memset(xpath, 0x00, xpath_size); + snprintf(xpath, xpath_size, "%s%s", base, path); + retval = pusb_xpath_get_bool(doc, xpath, value); + free(xpath); + if (retval) + log_debug("%s%s -> %s\n", base, path, *value ? "true" : "false"); + return (retval); +} + +int pusb_xpath_get_int(xmlDocPtr doc, const char *path, int *value) +{ + char ret[64]; /* strlen("false") + 1 */ + + if (!pusb_xpath_get_string(doc, path, ret, sizeof(ret))) + return (0); + *value = atoi(ret); + return (1); +} + +int pusb_xpath_get_int_from(xmlDocPtr doc, + const char *base, + const char *path, + int *value) +{ + char *xpath = NULL; + size_t xpath_size; + int retval; + + xpath_size = strlen(base) + strlen(path) + 1; + if (!(xpath = malloc(xpath_size))) + { + log_error("malloc error!\n"); + return (0); + } + memset(xpath, 0x00, xpath_size); + snprintf(xpath, xpath_size, "%s%s", base, path); + retval = pusb_xpath_get_int(doc, xpath, value); + free(xpath); + if (retval) + log_debug("%s%s -> %d\n", base, path, *value); + return (retval); +} diff --git a/src/xpath.h b/src/xpath.h new file mode 100644 index 0000000..fd6995d --- /dev/null +++ b/src/xpath.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2003-2007 Andrea Luzzardi + * + * This file is part of the pam_usb project. pam_usb is free software; + * you can redistribute it and/or modify it under the terms of the GNU General + * Public License version 2, as published by the Free Software Foundation. + * + * pam_usb is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 59 Temple + * Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef PUSB_XPATH_H_ +# define PUSB_XPATH_H_ +# include + +int pusb_xpath_get_string(xmlDocPtr doc, const char *path, char *value, size_t size); +int pusb_xpath_get_bool(xmlDocPtr doc, const char *path, int *value); +int pusb_xpath_get_string_from(xmlDocPtr doc, const char *base, const char *path, char *value, size_t size); +int pusb_xpath_get_bool_from(xmlDocPtr doc, const char *base, const char *path, int *value); +int pusb_xpath_get_int(xmlDocPtr doc, const char *path, int *value); +int pusb_xpath_get_int_from(xmlDocPtr doc, const char *base, const char *path, int *value); + +#endif /* !PUSB_XPATH_H_ */ diff --git a/tools/pamusb-agent b/tools/pamusb-agent new file mode 100755 index 0000000..5d512e1 --- /dev/null +++ b/tools/pamusb-agent @@ -0,0 +1,211 @@ +#!/usr/bin/env python +# +# Copyright (c) 2003-2007 Andrea Luzzardi +# +# This file is part of the pam_usb project. pam_usb is free software; +# you can redistribute it and/or modify it under the terms of the GNU General +# Public License version 2, as published by the Free Software Foundation. +# +# pam_usb is distributed in the hope that it will be useful, but WITHOUT ANY +# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more +# details. +# +# You should have received a copy of the GNU General Public License along with +# this program; if not, write to the Free Software Foundation, Inc., 59 Temple +# Place, Suite 330, Boston, MA 02111-1307 USA + +import os +import sys +import pwd +import getopt +import syslog +import gobject +import dbus +if getattr(dbus, 'version', (0,0,0)) >= (0,41,0): + import dbus.glib +try: + import cElementTree as et +except ImportError: + import elementtree.ElementTree as et + +class HotPlugDevice: + def __init__(self, serial): + self.__udi = None + self.__serial = serial + self.__callbacks = [] + self.__bus = dbus.SystemBus() + self.__running = False + + def run(self): + self.__scanDevices() + self.__registerSignals() + self.__running = True + gobject.MainLoop().run() + print 'signals registered' + + def addCallback(self, callback): + self.__callbacks.append(callback) + + def __scanDevices(self): + halService = self.__bus.get_object('org.freedesktop.Hal', + '/org/freedesktop/Hal/Manager') + halManager = dbus.Interface(halService, 'org.freedesktop.Hal.Manager') + for udi in halManager.FindDeviceStringMatch('info.bus', 'usb_device'): + self.__deviceAdded(udi) + + def __registerSignals(self): + for signal, callback in (('DeviceAdded', self.__deviceAdded), + ('DeviceRemoved', self.__deviceRemoved)): + self.__bus.add_signal_receiver(callback, + signal, + 'org.freedesktop.Hal.Manager', + 'org.freedesktop.Hal', + '/org/freedesktop/Hal/Manager') + + def __deviceAdded(self, udi): + if self.__udi is not None: + return + deviceObj = self.__bus.get_object('org.freedesktop.Hal', + udi) + deviceProperties = deviceObj.GetAllProperties( + dbus_interface = 'org.freedesktop.Hal.Device') + if not deviceProperties.has_key('usb_device.serial'): + return + if deviceProperties['usb_device.serial'] != self.__serial: + return + self.__udi = udi + if self.__running: + [ cb('added') for cb in self.__callbacks ] + + def __deviceRemoved(self, udi): + if self.__udi is None: + return + if self.__udi != udi: + return + self.__udi = None + if self.__running: + [ cb('removed') for cb in self.__callbacks ] + +class Log: + def __init__(self): + syslog.openlog('pamusb-agent', syslog.LOG_PID | syslog.LOG_PERROR, + syslog.LOG_AUTH) + + def info(self, message): + self.__logMessage(syslog.LOG_NOTICE, message) + + def error(self, message): + self.__logMessage(syslog.LOG_ERR, message) + + def __logMessage(self, priority, message): + syslog.syslog(priority, message) + +def usage(): + print 'Usage: %s [--help] [--config=path] [--daemon] [--check=path]' % \ + os.path.basename(__file__) + sys.exit(1) + +import getopt + +try: + opts, args = getopt.getopt(sys.argv[1:], "hc:dc:", + ["help", "config=", "daemon", "check="]) +except getopt.GetoptError: + usage() + +options = {'configFile' : '/etc/pamusb.conf', + 'daemon' : False, + 'check' : '/usr/bin/pamusb-check'} + +if len(args) != 0: + usage() + +for o, a in opts: + if o in ('-h', '--help'): + usage() + if o in ('-c', '--config'): + options['configFile'] = a + if o in ('-d', '--daemon'): + options['daemon'] = True + if o in ('-c', '--check'): + options['check'] = a + + +if not os.path.exists(options['check']): + print '%s not found.' % options['check'] + print "You might specify manually pamusb-check's location using --check." + usage() + +username = pwd.getpwuid(os.getuid())[0] + +logger = Log() + +doc = et.parse(options['configFile']) +users = doc.findall('users/user') +for user in users: + if user.get('id') == username: + break +else: + logger.error('User %s not found in configuration file' % username) + sys.exit(1) + +events = { + 'lock' : [], + 'unlock' : [] + } + +for hotplug in user.findall('agent'): + events[hotplug.get('event')].append(hotplug.text) + +deviceName = user.find('device').text.strip() + +devices = doc.findall("devices/device") +for device in devices: + if device.get('id') == deviceName: + break +else: + logger.error('Device %s not found in configurtion file' % deviceName) + sys.exit(1) + +serial = device.find('serial').text.strip() + +def authChangeCallback(event): + if event == 'removed': + logger.info('Device "%s" has been removed, ' \ + 'locking down user "%s"...' % (deviceName, username)) + for cmd in events['lock']: + logger.info('Running "%s"' % cmd) + os.system(cmd) + logger.info('Locked.') + return + + logger.info('Device "%s" has been inserted. ' \ + 'Performing verification...' % deviceName) + cmdLine = "%s --quiet --config=%s --service=pamusb-agent %s" % ( + options['check'], options['configFile'], username) + logger.info('Executing "%s"' % cmdLine) + if not os.system(cmdLine): + logger.info('Authentication succeeded. ' \ + 'Unlocking user "%s"...' % username) + for cmd in events['unlock']: + logger.info('Running "%s"' % cmd) + os.system(cmd) + logger.info('Unlocked.') + else: + logger.info('Authentication failed for device %s. ' \ + 'Keeping user "%s" locked down.' % (deviceName, username)) + +hpDev = HotPlugDevice(serial) +hpDev.addCallback(authChangeCallback) + +if options['daemon'] and os.fork(): + sys.exit(0) + +logger.info('pamusb-agent up and running.') +logger.info('Watching device "%s" for user "%s"' % (deviceName, username)) + +try: + hpDev.run() +except KeyboardInterrupt: + logger.error('Caught keyboard interruption, exiting...') diff --git a/tools/pamusb-conf b/tools/pamusb-conf new file mode 100755 index 0000000..61149d7 --- /dev/null +++ b/tools/pamusb-conf @@ -0,0 +1,271 @@ +#!/usr/bin/env python +# +# Copyright (c) 2003-2007 Andrea Luzzardi +# +# This file is part of the pam_usb project. pam_usb is free software; +# you can redistribute it and/or modify it under the terms of the GNU General +# Public License version 2, as published by the Free Software Foundation. +# +# pam_usb is distributed in the hope that it will be useful, but WITHOUT ANY +# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more +# details. +# +# You should have received a copy of the GNU General Public License along with +# this program; if not, write to the Free Software Foundation, Inc., 59 Temple +# Place, Suite 330, Boston, MA 02111-1307 USA + + +import dbus +import sys +import os +from xml.dom import minidom + +class Device: + def __init__(self, udi): + self.__udi = udi + self.__findStorageDevice() + deviceObj = bus.get_object('org.freedesktop.Hal', + udi) + deviceProperties = deviceObj.GetAllProperties( + dbus_interface = 'org.freedesktop.Hal.Device') + self.vendor = deviceProperties['usb_device.vendor'] + self.product = deviceProperties['info.product'] + self.serialNumber = deviceProperties['usb_device.serial'] + if len(self.volumes()) < 1: + raise Exception, '%s does not contain any volume' % self.__udi + + def __isChildOfDevice(self, udi): + obj = bus.get_object('org.freedesktop.Hal', udi) + properties = obj.GetAllProperties( + dbus_interface = 'org.freedesktop.Hal.Device') + if not properties.has_key('info.parent'): + return False + if properties.has_key('info.bus') and properties['info.bus'] == 'usb_device': + return False + if properties['info.parent'] == self.__udi: + return True + return self.__isChildOfDevice(properties['info.parent']) + + def __findStorageDevice(self): + for child in halManager.FindDeviceByCapability('storage'): + if self.__isChildOfDevice(child): + self.__storageUdi = child + return + raise Exception, '%s is not a storage device.' % self.__udi + + def __repr__(self): + return "%s %s (%s)" % (self.vendor, self.product, self.serialNumber) + + def volumes(self): + vols = [] + for volume in halManager.FindDeviceByCapability('volume'): + deviceObj = bus.get_object('org.freedesktop.Hal', + volume) + deviceProperties = deviceObj.GetAllProperties( + dbus_interface = 'org.freedesktop.Hal.Device') + if deviceProperties['block.storage_device'] != self.__storageUdi: + continue + vols.append({'uuid' : deviceProperties['volume.uuid'], + 'device' : deviceProperties['block.device']}) + return vols + +def listOptions(question, options, autodetect = True): + if autodetect == True and len(options) == 1: + print question + print "* Using \"%s\" (only option)" % options[0] + print + return 0 + + while True: + try: + print question + for i in range(len(options)): + print "%d) %s" % (i, options[i]) + print + sys.stdout.write('[%s-%s]: ' % (0, len(options) - 1)) + optionId = int(sys.stdin.readline()) + print + if optionId not in range(len(options)): + raise Exception + return optionId + except KeyboardInterrupt: sys.exit() + except Exception: pass + else: break + +def writeConf(options, doc): + try: + f = open(options['configFile'], 'w') + doc.writexml(f) + f.close() + except Exception, err: + print 'Unable to save %s: %s' % (options['configFile'], err) + sys.exit(1) + else: + print 'Done.' + +def shouldSave(options, items): + print "\n".join(["%s\t\t: %s" % item for item in items]) + print + print 'Save to %s ?' % options['configFile'] + sys.stdout.write('[Y/n] ') + response = sys.stdin.readline().strip() + if len(response) > 0 and response.lower() != 'y': + sys.exit(1) + +def prettifyElement(element): + tmp = minidom.parseString(element.toprettyxml()) + return tmp.lastChild + +def addUser(options): + try: + doc = minidom.parse(options['configFile']) + except Exception, err: + print 'Unable to read %s: %s' % (options['configFile'], err) + sys.exit(1) + devSection = doc.getElementsByTagName('devices') + if len(devSection) == 0: + print 'Malformed configuration file: No section found.' + sys.exit(1) + devicesObj = devSection[0].getElementsByTagName('device') + if len(devicesObj) == 0: + print 'No devices found.' + print 'You must add a device (--add-device) before adding users' + sys.exit(1) + + devices = [] + for device in devicesObj: + devices.append(device.getAttribute('id')) + device = devices[listOptions("Which device would you like to use for authentication ?", + devices)] + + shouldSave(options, [ + ('User', options['userName']), + ('Device', device) + ]) + + users = doc.getElementsByTagName('users') + user = doc.createElement('user') + user.attributes['id'] = options['userName'] + e = doc.createElement('device') + t = doc.createTextNode(device) + e.appendChild(t) + user.appendChild(e) + users[0].appendChild(prettifyElement(user)) + writeConf(options, doc) + +def addDevice(options): + devices = [] + + for udi in halManager.FindDeviceStringMatch('info.bus', 'usb_device'): + try: + devices.append(Device(udi)) + except Exception, ex: + pass + + if len(devices) == 0: + print 'No devices detected.' + sys.exit() + device = devices[listOptions("Please select the device you wish to add.", devices)] + + volumes = device.volumes() + volume = volumes[listOptions("Which volume would you like to use for " \ + "storing data ?", + ["%s (UUID: %s)" % (volume['device'], + volume['uuid'] or "") + for volume in volumes] + )] + + if volume['uuid'] == '': + print 'WARNING: No UUID detected for device %s. One time pads will be disabled.' % volume['device'] + + shouldSave(options,[ + ('Name', options['deviceName']), + ('Vendor', device.vendor), + ('Model', device.product), + ('Serial', device.serialNumber), + ('UUID', volume['uuid'] or "") + ]) + + try: + doc = minidom.parse(options['configFile']) + except Exception, err: + print 'Unable to read %s: %s' % (options['configFile'], err) + sys.exit(1) + + devs = doc.getElementsByTagName('devices') + dev = doc.createElement('device') + dev.attributes['id'] = options['deviceName'] + + for name, value in (('vendor', device.vendor), + ('model', device.product), + ('serial', device.serialNumber), + ('volume_uuid', volume['uuid'])): + if value == '': + continue + e = doc.createElement(name) + t = doc.createTextNode(value) + e.appendChild(t) + dev.appendChild(e) + + # Disable one time pads if there's no device UUID + if volume['uuid'] == '': + e = doc.createElement('option') + e.setAttribute('name', 'one_time_pad') + e.appendChild(doc.createTextNode('false')) + dev.appendChild(e) + + devs[0].appendChild(prettifyElement(dev)) + writeConf(options, doc) + +def usage(): + print 'Usage: %s [--help] [--config=path] [--add-user=name | --add-device=name]' % os.path.basename(__file__) + sys.exit(1) + +import getopt + +try: + opts, args = getopt.getopt(sys.argv[1:], "hd:nu:c:", + ["help", "add-device=", "add-user=", "config="]) +except getopt.GetoptError: + usage() + +if len(args) != 0: + usage() + +options = { 'deviceName' : None, 'userName' : None, + 'configFile' : '/etc/pamusb.conf' } + +for o, a in opts: + if o in ("-h", "--help"): + usage() + if o in ("-d", "--add-device"): + options['deviceName'] = a + if o in ("-u", "--add-user"): + options['userName'] = a + if o in ("-c", "--config"): + options['configFile'] = a + +if options['deviceName'] is not None and options['userName'] is not None: + print 'You cannot use both --add-user and --add-device' + usage() + +if options['deviceName'] is None and options['userName'] is None: + usage() + +if options['deviceName'] is not None: + bus = dbus.SystemBus() + halService = bus.get_object('org.freedesktop.Hal', + '/org/freedesktop/Hal/Manager') + halManager = dbus.Interface(halService, 'org.freedesktop.Hal.Manager') + try: + addDevice(options) + except KeyboardInterrupt: + sys.exit(1) + +if options['userName'] is not None: + try: + addUser(options) + except KeyboardInterrupt: + sys.exit(1) +