From 4e0f4aa68e082b469663e3ebc8ec83c9400dab4b Mon Sep 17 00:00:00 2001 From: Jeroen Nijhof Date: Wed, 22 Dec 2010 11:12:08 +0100 Subject: Initial commit --- AUTHORS | 5 + COPYING | 341 ++++++++++++++++++++++++ ChangeLog | 99 +++++++ INSTALL | 18 ++ Makefile.am | 60 +++++ NEWS | 1 + README | 210 +++++++++++++++ config/.placeholder | 0 configure.ac | 63 +++++ debian/README.Debian | 18 ++ debian/changelog | 6 + debian/control | 16 ++ debian/copyright | 27 ++ debian/libpam-tacplus.dirs | 1 + debian/libpam-tacplus.docs | 2 + debian/rules | 93 +++++++ libtac/include/cdefs.h | 58 ++++ libtac/include/libtac.h | 83 ++++++ libtac/include/tacplus.h | 265 +++++++++++++++++++ libtac/lib/acct_r.c | 97 +++++++ libtac/lib/acct_s.c | 151 +++++++++++ libtac/lib/attrib.c | 80 ++++++ libtac/lib/authen_r.c | 104 ++++++++ libtac/lib/authen_s.c | 143 ++++++++++ libtac/lib/author_r.c | 194 ++++++++++++++ libtac/lib/author_s.c | 152 +++++++++++ libtac/lib/connect.c | 144 ++++++++++ libtac/lib/cont_s.c | 92 +++++++ libtac/lib/crypt.c | 107 ++++++++ libtac/lib/hdr_check.c | 53 ++++ libtac/lib/header.c | 65 +++++ libtac/lib/magic.c | 122 +++++++++ libtac/lib/magic.h | 26 ++ libtac/lib/md5.c | 276 +++++++++++++++++++ libtac/lib/md5.h | 41 +++ libtac/lib/messages.c | 26 ++ libtac/lib/messages.h | 26 ++ libtac/lib/version.c | 24 ++ libtac/lib/xalloc.c | 43 +++ libtac/lib/xalloc.h | 23 ++ pam_tacplus.c | 645 +++++++++++++++++++++++++++++++++++++++++++++ pam_tacplus.h | 38 +++ pam_tacplus.spec.in | 63 +++++ sample.pam | 4 + support.c | 229 ++++++++++++++++ support.h | 37 +++ 46 files changed, 4371 insertions(+) create mode 100644 AUTHORS create mode 100644 COPYING create mode 100644 ChangeLog create mode 100644 INSTALL create mode 100644 Makefile.am create mode 100644 NEWS create mode 100644 README create mode 100644 config/.placeholder create mode 100644 configure.ac create mode 100644 debian/README.Debian create mode 100644 debian/changelog create mode 100644 debian/control create mode 100644 debian/copyright create mode 100644 debian/libpam-tacplus.dirs create mode 100644 debian/libpam-tacplus.docs create mode 100755 debian/rules create mode 100644 libtac/include/cdefs.h create mode 100644 libtac/include/libtac.h create mode 100644 libtac/include/tacplus.h create mode 100644 libtac/lib/acct_r.c create mode 100644 libtac/lib/acct_s.c create mode 100644 libtac/lib/attrib.c create mode 100644 libtac/lib/authen_r.c create mode 100644 libtac/lib/authen_s.c create mode 100644 libtac/lib/author_r.c create mode 100644 libtac/lib/author_s.c create mode 100644 libtac/lib/connect.c create mode 100644 libtac/lib/cont_s.c create mode 100644 libtac/lib/crypt.c create mode 100644 libtac/lib/hdr_check.c create mode 100644 libtac/lib/header.c create mode 100644 libtac/lib/magic.c create mode 100644 libtac/lib/magic.h create mode 100644 libtac/lib/md5.c create mode 100644 libtac/lib/md5.h create mode 100644 libtac/lib/messages.c create mode 100644 libtac/lib/messages.h create mode 100644 libtac/lib/version.c create mode 100644 libtac/lib/xalloc.c create mode 100644 libtac/lib/xalloc.h create mode 100644 pam_tacplus.c create mode 100644 pam_tacplus.h create mode 100644 pam_tacplus.spec.in create mode 100644 sample.pam create mode 100644 support.c create mode 100644 support.h diff --git a/AUTHORS b/AUTHORS new file mode 100644 index 0000000..0737c5d --- /dev/null +++ b/AUTHORS @@ -0,0 +1,5 @@ +Primary Author: + Pawel Krawczyk +Other Authors and Major Contributors: + Jeroen Nijhof + diff --git a/COPYING b/COPYING new file mode 100644 index 0000000..86cf81a --- /dev/null +++ b/COPYING @@ -0,0 +1,341 @@ + + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 675 Mass Ave, Cambridge, MA 02139, 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 + + Appendix: 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) 19yy + + 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., 675 Mass Ave, Cambridge, MA 02139, 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) 19yy 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..b4b4cb9 --- /dev/null +++ b/ChangeLog @@ -0,0 +1,99 @@ +1.3.2 +* Added autotool configuration files, thanks to Benoit Donneaux . +* Added pam_tacplus.spec file, thanks to Benoit Donneaux . +* Added license information to all files and the license itself. +* All AV pairs are now available to the PAM environment. So you can use pam_exec.so or whatever + to do something with these. Only available for PAM account. +* Rewritten attribute loop in function pam_sm_acct_mgmt() for debug and future use + of AV pairs. +* Fixed attribute buffer in author_r.c, this bug cause program stuck when you get + AV pairs from the server, reported by Oz Shitrit. + +1.3.1 +* Added custom password prompt option +* Removed password logging when in debug mode + +1.3.0 +* Released version 1.3.0 based on 1.2.13. + This release finally includes support for TACACS+ chap and login authentication. The + default is still pap for backward compatibility. + +1.2.13 +* Changed spaces into tabs for pam_tacplus.c so make it more readable +* Did some minor cleanup +* Added login option so you can choose which TACACS+ authentication you want to + use. You can use pap, chap or login (ascii) at the moment. The default login option is pap. +* Added cont_s.c needed for TACACS+ login authentication. + +1.2.12 +* Missing network byte order convertion to host byte order in function's + tac_account_read, tac_authen_pap_read and tac_author_read, reported and + patch by Sven van den Steene, thanks! +* Fixed potential memory leak, when tac_account_read and tac_authen_pap_read are + successful msg isn't freed, reported by Sven van den Steene + +1.2.11 +* Added NO_STATIC_MODULES to CFLAGS for linking with openpam on netbsd, tested by + Fredrik Pettai +* Removed libdl for compiling causing failure on netbsd, reported by + Fredrik Pettai +* hdr_check.c: forgot to include stdlib, reported by + Fredrik Pettai +* Changed defines to add support for netbsd, fixed by + Jeroen Nijhof +* magic.c: read() can have a return value, fixed by + Jeroen Nijhof +* support.c: _pam_log() va_list converted to string with vsnprintf() to support + syslog(), we have human readable error's in syslog again, fixed by + Jeroen Nijhof + +1.2.10 + The following changes where made by Jeroen Nijhof +* Changed default compile flags to be more compatible +* Fixed serveral bugs including casts and cleanup's, the code can now compile + without any warnings +* Changed some Makefile definitions to be more compatible with other versions of make +* Support added for solaris and aix, tested on aix 5.3, solaris 9 and 10. Including + standalone version of cdefs.h + +1.2.9 +* Fixed bug with passing username and password, reported by + Mark Volpe +* Fixed bug in passing the remote address, reported by + Jason Lambert and + Yury Trembach +* Fixed bug in reception of authorization packet, reported by + + +1.2.8 +* Another bugfix in tty handling - some daemons don't use any terminal, in + which case we send "unknown" terminal name to the TACACS+ server + +1.2.7 +* Fixed bug in tty determination + +1.2.6 +* Better protection against disconnection signals + +1.2.5 +* Fixed bug in task_id initialisation + +1.2.4 +* Fixed small bug in accounting + +1.2.3 +* upgraded to new libtac version, now pam_tacplus returns the attributes + received from server (currently only 'addr' attribute in PAM_RHOST) +* minor fixes + +1.2.2 +* more fixes + +1.2.1 +* pam_sm_acct_mgmt() added +* pam_sm_open_session() added +* pam_sm_close_session() added +* minor fixes + +1.0.1 +* first working version with pam_sm_authenticate() diff --git a/INSTALL b/INSTALL new file mode 100644 index 0000000..e745395 --- /dev/null +++ b/INSTALL @@ -0,0 +1,18 @@ + +pam_tacplus consists of the module code and `libtac' simple TACPLUS+ library. + +Simple `make' should compile both TACACS+ library and pam_tacplus module. +If not, you could try to edit the following files: + +./Makefile +./libtac/lib/Makefile + +You should get a `pam_tacplus.so' module, which should be placed in +/lib/security. Modify `sample.pam' and install it in /etc/pam.d under +name of the proper service. + +This code is known to work on Linux, Solaris and AIX for now. + +Jan 02 2000 +Pawel Krawczyk +http://ceti.pl/~kravietz/prog.html diff --git a/Makefile.am b/Makefile.am new file mode 100644 index 0000000..4025f53 --- /dev/null +++ b/Makefile.am @@ -0,0 +1,60 @@ +########################################################################### +## +## File: ./Makefile.am +## Versions: $Id: Makefile.am,v 1.3 2010/06/11 12:04:29 j-nijhof Exp $ +## Created: 2010/06/09 +## +########################################################################### + +ACLOCAL_AMFLAGS = -I config + +moduledir = @libdir@ +module_LTLIBRARIES = pam_tacplus.la +pam_tacplus_la_SOURCES = pam_tacplus.h \ +pam_tacplus.c \ +support.h \ +support.c \ +libtac/lib/acct_r.c \ +libtac/lib/acct_s.c \ +libtac/lib/attrib.c \ +libtac/lib/authen_r.c \ +libtac/lib/authen_s.c \ +libtac/lib/author_r.c \ +libtac/lib/author_s.c \ +libtac/lib/connect.c \ +libtac/lib/cont_s.c \ +libtac/lib/crypt.c \ +libtac/lib/hdr_check.c \ +libtac/lib/header.c \ +libtac/lib/magic.c \ +libtac/lib/magic.h \ +libtac/lib/md5.c \ +libtac/lib/md5.h \ +libtac/lib/messages.c \ +libtac/lib/messages.h \ +libtac/lib/version.c \ +libtac/lib/xalloc.c \ +libtac/lib/xalloc.h \ +libtac/include/tacplus.h \ +libtac/include/libtac.h \ +libtac/include/cdefs.h + +pam_tacplus_la_CFLAGS = $(AM_CFLAGS) -Ilibtac/include +pam_tacplus_la_LDFLAGS = -module -avoid-version + +EXTRA_DIST = pam_tacplus.spec sample.pam + +MAINTAINERCLEANFILES = Makefile.in config.h.in configure aclocal.m4 \ + config/config.guess config/config.sub config/depcomp \ + config/install-sh config/ltmain.sh config/missing + + +install-data-hook: + -rm $(DESTDIR)$(libdir)/pam_tacplus.la + -rm $(DESTDIR)$(libdir)/pam_tacplus.a + -rm $(DESTDIR)$(libdir)/pam_tacplus.so + ${INSTALL} -d $(DESTDIR)$(libdir)/security + ${INSTALL} -m 755 .libs/pam_tacplus.so $(DESTDIR)$(libdir)/security + ${INSTALL} -d $(DESTDIR)$(docdir) + ${INSTALL} -m 644 sample.pam $(DESTDIR)$(docdir) + diff --git a/NEWS b/NEWS new file mode 100644 index 0000000..73e0f18 --- /dev/null +++ b/NEWS @@ -0,0 +1 @@ +Check README diff --git a/README b/README new file mode 100644 index 0000000..724d1f0 --- /dev/null +++ b/README @@ -0,0 +1,210 @@ + +pam_tacplus v1.3.2 +Jun 07 2010 + +This PAM module support the following functions: + + * authentication + * authorization (account management) + * accounting (session management) + +All are performed using TACACS+ protocol [1], designed by Cisco Systems. +This is remote AAA protocol, supported by most Cisco hardware. +A free TACACS+ server is available [2], which I'm using without any +major problems for about a year. Advantages of TACACS+ is that all +(unlike RADIUS) packets exchanged with the authentication server are +encrypted. This module is an attempt to provide most useful part of +TACACS+ functionality to applications using the PAM interface on Linux. + + +Recognized options: +~~~~~~~~~~~~~~~~~~~ + +Option Management group Description +--------------- ----------------------- ---------------------------------- +debug ALL output debugging information via + syslog(3); note, that the debugging + is heavy, including passwords! + +encrypt ALL encrypt TACACS+ packets; you should + use this always in normal operations + +secret=STRING ALL secret key used to encrypt/decrypt + packets sent/received from the server + +server=HOSTNAME auth, session can be specified more than once; +server=IP_ADDR adds a TACACS+ server to the servers + list + +timeout=INT ALL connection timeout in seconds + default is 5 seconds + +first_hit auth if multiple servers are supplied, + pam_tacplus will try to authenticate + the user on all servers until it + succeds or all servers are queried + +login=STRING auth TACACS+ authentication service, + this can be "pap", "chap" or "login" + at the moment. Default is pap. + +prompt=STRING auth Custom password prompt. If you want + to use a space use '_' character + instead. + +acct_all session if multiple servers are supplied, + pam_tacplus will send accounting + start/stop packets to all servers + on the list + +service account, session TACACS+ service for authorization + and accounting + +protocol account, session TACACS+ protocol for authorization + and accounting + +The last two items are widely described in TACACS+ draft [1]. They are +required by the server, but it will work if they don't match the real +service authorized :) +During PAM account the AV pairs returned by the TACACS+ servers are made available to the +PAM environment, so you can use i.e. pam_exec.so to do something with these AV pairs. + +Example configuration: +~~~~~~~~~~~~~~~~~~~~~~ + +#%PAM-1.0 +auth required /lib/security/pam_tacplus.so debug server=123.123.123.123 secret=SECRET-123 encrypt +account required /lib/security/pam_tacplus.so debug secret=SECRET-123 encrypt service=ppp protocol=lcp +account sufficient /lib/security/pam_exec.so /usr/local/bin/showenv.sh +password required /lib/security/pam_cracklib.so +password required /lib/security/pam_pwdb.so shadow use_authtok +session required /lib/security/pam_tacplus.so debug server=123.123.123.123 server=124.124.124.124 secret=SECRET-124 encrypt service=ppp protocol=lcp + +(note that above are five long lines) + +More on server lists: +~~~~~~~~~~~~~~~~~~~~~ + +1. Having more that one TACACS+ server defined for given management group +has following effects on authentication: + + * always, if the first server on the list is unreachable or failing + pam_tacplus will try to authenticate user on the another one + + in case, where there are no modifiers like `first_hit', you + could think the of the first server on list as primary one, + and the others as backup/secondary servers + + * if the `first_hit' option is specified, if the first server + on the list will return negative authentication reply, pam_tacplus + will try to ask another server; this will continue until it + will get positive reply from one of the servers, or all servers + are probed with negative replies; then pam_tacplus will return + with authentication failure + + this is useful if you have e.g. dialup server authenticating + users who have accounts on multiple servers in your network; + in this case, from host B won't be authenticated by TACACS+ server + on host A (which is first on the list), but it will be authenticated + when pam_tacplus will query the server on host B next + + in this case all the servers can be considered as having equal + authority in authenticating users + + * when the authentication function gets a positive reply from + a server, it saves its address for future use by account + management function (see below) + +2. The account management (authorization) function asks *only one* +TACACS+ server and it ignores the whole server list passed from command +line. It uses server saved by authentication function after successful +authenticating user on that server. We assume that the server is +authoriative for queries about that user. + +3. The session management (accounting) functions obtain their server lists +independently from the other functions. This allows you to account user +sessions on different servers than those used for authentication and +authorization. + + * normally, without the `acct_all' modifier, the extra servers + on the list will be considered as backup servers, mostly like + in point 1. i.e. they will be used only if the first server + on the list will fail to accept our accounting packets + + * with `acct_all' pam_tacplus will try to deliver the accounting + packets to all servers on the list; failure of one of the servers + will make it try another one + + this is useful when your have several accounting, billing or + logging hosts and want to have the accounting information appear + on all of them at the same time + + +Short introduction to PAM via TACACS+: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +This diagram should show general idea of how the whole process looks: + + +-----+ + Authen -user/pass valid?----------> | T S | + / | A e | + PAM- Author -service allowed?----------> | C r | + ^ \ | A v | + | Acct ,-----start session----------> | C e | + | `----stop session-----------> | S r | + Application +-----+ + + *Client Host* *Network* *Server Host* + +Consider `login' application: + +1. Login accepts username and password from the user. +2. Login calls PAM function pam_authenticate() to verify if the + supplied username/password pair is valid. +3. PAM loads pam_tacplus module (as defined in /etc/pam.d/login) + and calls pam_sm_authenticate() function supplied by this module. +4. This function sends an encrypted packet to the TACACS+ server. + The packet contains username and password to verify. TACACS+ server + replied with either positive or negative response. If the reponse + is negative, the whole thing is over ;) +5. PAM calls another function from pam_tacplus - pam_sm_acct_mgmt(). + This function is expected to verify whether the user is allowed + to get the service he's requesting (in this case: unix shell). + The function again verifies the permission on TACACS+ server. Assume + the server granted the user with requested service. +6. Before user gets the shell, PAM calls one another function from + pam_tacplus - pam_sm_open_session(). This results in sending an + accounting START packet to the server. Among other things it contains + the terminal user loggen in on and the time session started. +7. When user logs out, pam_sm_close_session() sends STOP packet to the + server. The whole session is closed. + +Limitations: +~~~~~~~~~~~~ + +Many of them for now :) + + * it's still in beta + * only subset of TACACS+ protocol is supported; it's enough for + most need, though + * only one, common `secret' can be specified + * utilize PAM_SERVICE item obtained from PAM for TACACS+ services + * clean options and configuration code + +References: +~~~~~~~~~~~ + +TACACS+ +1. ftp://ftpeng.cisco.com/pub/tacplus/tac_plus.rfc.1.76.txt +2. ftp://ftpeng.cisco.com/pub/tacplus/tac_plus.3.0.12.alpha.tar.Z + +PAM +3. http://parc.power.net/morgan/Linux-PAM/index.html + +Authors: +~~~~~~~ + +Pawel Krawczyk +http://ceti.pl/~kravietz/prog.html + +Jeroen Nijhof diff --git a/config/.placeholder b/config/.placeholder new file mode 100644 index 0000000..e69de29 diff --git a/configure.ac b/configure.ac new file mode 100644 index 0000000..07c1cdb --- /dev/null +++ b/configure.ac @@ -0,0 +1,63 @@ +dnl +dnl File: configure.in +dnl Revision: $Id: configure.ac,v 1.4 2010/06/11 12:04:29 j-nijhof Exp $ +dnl Created: 2010/06/09 +dnl Author: Jeroen Nijhof +dnl Benoit Donneaux +dnl +dnl Process this file with autoconf to produce a configure script +dnl You need autoconf 2.59 or better! +dnl +dnl --------------------------------------------------------------------------- + +AC_PREREQ(2.59) +AC_COPYRIGHT([ +See the included file: COPYING for copyright information. +]) +AC_INIT(pam_tacplus, 1.3.2, [jeroen@nijhofnet.nl,kravietz@ceti.com.pl]) +AC_CONFIG_AUX_DIR(config) +AM_INIT_AUTOMAKE +AC_CONFIG_SRCDIR([pam_tacplus.c]) +AC_CONFIG_HEADER([config.h]) + +dnl -------------------------------------------------------------------- +dnl Checks for programs. +AC_PROG_CC +AC_PROG_INSTALL +AC_PROG_LN_S +AC_PROG_MAKE_SET +AM_PROG_LIBTOOL + +dnl -------------------------------------------------------------------- +dnl Checks for libraries. +AC_CHECK_LIB(pam, pam_start) +AC_CHECK_LIB(tac, tac_connect) + +case "$host" in + sparc-* | sparc64-*) + LIBS="$LIBS -lresolv";; +esac + +dnl -------------------------------------------------------------------- +dnl Checks for header files. +AC_HEADER_STDC +AC_CHECK_HEADERS([arpa/inet.h fcntl.h netdb.h netinet/in.h stdlib.h string.h strings.h sys/socket.h sys/time.h syslog.h unistd.h]) + +dnl -------------------------------------------------------------------- +dnl Checks for typedefs, structures, and compiler characteristics. +AC_C_CONST +AC_TYPE_SIZE_T +AC_HEADER_TIME + +dnl -------------------------------------------------------------------- +dnl Checks for library functions. +AC_FUNC_REALLOC +AC_FUNC_SELECT_ARGTYPES +AC_TYPE_SIGNAL +AC_CHECK_FUNCS([bzero gethostbyname gettimeofday inet_ntoa select socket]) + +dnl -------------------------------------------------------------------- +dnl Generate made files +AC_CONFIG_FILES([Makefile + pam_tacplus.spec]) +AC_OUTPUT diff --git a/debian/README.Debian b/debian/README.Debian new file mode 100644 index 0000000..c4f1b6c --- /dev/null +++ b/debian/README.Debian @@ -0,0 +1,18 @@ +pam_tacplus for Debian +--------------------- + +The pam_tacplus module is placed in /lib/security/ +to include pam_tacplus.so edit /etc/pam.d/common-* + +Run script -c "dpkg-buildpackage" as root +in the source directory above this one. + +You will need at least the debhelper and libpam0g-dev +packages. + +Look at the content list of the deb file with "dpkg -c" + +Change the version number by running "debchange -i" and add in the NEWS +entries for the given version. + + -- J. Nijhof , Sun, 14 Feb 2010 diff --git a/debian/changelog b/debian/changelog new file mode 100644 index 0000000..5a5ad19 --- /dev/null +++ b/debian/changelog @@ -0,0 +1,6 @@ +libpam-tacplus (1.3.1-1) unstable; urgency=low + + * First version of pam_tacplus debian package + + -- Jeroen Nijhof Sun, 14 Feb 2010 23:07:00 +0100 + diff --git a/debian/control b/debian/control new file mode 100644 index 0000000..846b7ce --- /dev/null +++ b/debian/control @@ -0,0 +1,16 @@ +Source: libpam-tacplus +Priority: optional +Maintainer: J. Nijhof +Build-Depends: debhelper (>= 4.0.0), libpam-dev, chrpath +Standards-Version: 1.3.1 +Section: libs + +Package: libpam-tacplus +Section: libs +Architecture: any +Depends: ${shlibs:Depends}, ${misc:Depends} +Suggests: libpam-doc +Description: TACACS+ protocol client library and PAM module in C. + This PAM module support authentication, authorization (account management) and + accounting (session management) performed using TACACS+ protocol designed by Cisco. + diff --git a/debian/copyright b/debian/copyright new file mode 100644 index 0000000..f5a1d76 --- /dev/null +++ b/debian/copyright @@ -0,0 +1,27 @@ + +pam-tacplus + + Copyright (C) 2010, Pawel Krawczyk + and Jeroen Nijhof . + + 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. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + MA 02110-1301, USA. + +All the other scripts and control files for building and installing +libpam-tacplus under Debian GNU/Linux are also under the GNU General Public +License (GPL) version 2 or later. + +On Debian GNU/Linux systems, the complete text of the GNU General +Public License can be found in '/usr/share/common-licenses/GPL'. + diff --git a/debian/libpam-tacplus.dirs b/debian/libpam-tacplus.dirs new file mode 100644 index 0000000..d1f6515 --- /dev/null +++ b/debian/libpam-tacplus.dirs @@ -0,0 +1 @@ +lib/security diff --git a/debian/libpam-tacplus.docs b/debian/libpam-tacplus.docs new file mode 100644 index 0000000..f1b7621 --- /dev/null +++ b/debian/libpam-tacplus.docs @@ -0,0 +1,2 @@ +README +sample.pam diff --git a/debian/rules b/debian/rules new file mode 100755 index 0000000..a7fba46 --- /dev/null +++ b/debian/rules @@ -0,0 +1,93 @@ +#!/usr/bin/make -f +# -*- makefile -*- +# Sample debian/rules that uses debhelper. +# This file was originally written by Joey Hess and Craig Small. +# As a special exception, when this file is copied by dh-make into a +# dh-make output file, you may use that output file without restriction. +# This special exception was added by Craig Small in version 0.37 of dh-make. + +# Uncomment this to turn on verbose mode. +export DH_VERBOSE=1 + +# Debhelper compatibility +export DH_COMPAT=5 + +# Do not include CVS/svn source directories +export DH_ALWAYS_EXCLUDE=CVS:.svn + +# These are used for cross-compiling and for saving the configure script +# from having to guess our platform (since we know it already) +DEB_HOST_GNU_TYPE ?= $(shell dpkg-architecture -qDEB_HOST_GNU_TYPE) +DEB_BUILD_GNU_TYPE ?= $(shell dpkg-architecture -qDEB_BUILD_GNU_TYPE) + +DEBFLAGS= +ifeq ($(DEB_HOST_ARCH),i386) + DEBFLAGS = -m32 +endif + +build: build-stamp +build-stamp: + dh_testdir + + # Add here commands to compile the package. + DEBFLAGS=$(DEBFLAGS) $(MAKE) + + touch build-stamp + +clean: + dh_testdir + dh_testroot + rm -f build-stamp + + # Add here commands to clean up after the build process. + $(MAKE) clean + + dh_clean + +install: build + dh_testdir + dh_testroot + dh_clean + dh_installdirs + + # Add here commands to install the package into debian/libpam-tacplus + cp pam_tacplus.so $(CURDIR)/debian/libpam-tacplus/lib/security/ + +# Build architecture-independent files here. +binary-indep: build install +# We have nothing to do by default. + +# Build architecture-dependent files here. +binary-arch: build install + dh_testdir + dh_testroot + dh_installchangelogs CHANGES + dh_installdocs +# dh_installexamples +# dh_install +# dh_movefiles --sourcedir=debian/tmp +# dh_installmenu +# dh_installdebconf +# dh_installlogrotate +# dh_installemacsen +# dh_installpam +# dh_installmime +# dh_installinit +# dh_installcron +# dh_installinfo +# dh_installman +# dh_link + dh_strip + dh_compress + dh_fixperms +# dh_perl +# dh_python + dh_makeshlibs + dh_installdeb + dh_shlibdeps + dh_gencontrol + dh_md5sums + dh_builddeb + +binary: binary-indep binary-arch +.PHONY: build clean binary-indep binary-arch binary install diff --git a/libtac/include/cdefs.h b/libtac/include/cdefs.h new file mode 100644 index 0000000..148f3d7 --- /dev/null +++ b/libtac/include/cdefs.h @@ -0,0 +1,58 @@ +/* cdefs.h + * + * Copyright (C) 2010, Pawel Krawczyk and + * Jeroen Nijhof + * + * 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 - see the file COPYING. + * + * See `CHANGES' file for revision history. + */ + +#undef __P +#if defined(__STDC__) || defined(__cplusplus) +#define __P(p) p +#else +#define __P(p) +#endif +#define _PTR void * +#define _AND , +#define _NOARGS void +#define _CONST const +#define _VOLATILE volatile +#define _SIGNED signed +#define _DOTS , ... +#define _VOID void +#define _EXFUN(name, proto) name proto +#define _DEFUN(name, arglist, args) name(args) +#define _DEFUN_VOID(name) name(_NOARGS) +#define _CAST_VOID (void) +#ifndef _LONG_DOUBLE +#define _LONG_DOUBLE long double +#endif +#ifndef _PARAMS +#define _PARAMS(paramlist) paramlist +#endif + +/* Support gcc's __attribute__ facility. */ + +#define _ATTRIBUTE(attrs) __attribute__ ((attrs)) + +#if defined(__cplusplus) +#define __BEGIN_DECLS extern "C" { +#define __END_DECLS } +#else +#define __BEGIN_DECLS +#define __END_DECLS +#endif + diff --git a/libtac/include/libtac.h b/libtac/include/libtac.h new file mode 100644 index 0000000..023b60d --- /dev/null +++ b/libtac/include/libtac.h @@ -0,0 +1,83 @@ +/* libtac.h + * + * Copyright (C) 2010, Pawel Krawczyk and + * Jeroen Nijhof + * + * 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 - see the file COPYING. + * + * See `CHANGES' file for revision history. + */ + +#ifndef _AUTH_TAC_H +#define _AUTH_TAC_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(DEBUGTAC) && !defined(TACDEBUG) +#define TACDEBUG(x) syslog x; +#else +#define TACDEBUG(x) +#endif + +/* u_int32_t support for sun */ +#ifdef sun +typedef unsigned int u_int32_t; +#endif + +/* version.c */ +extern int tac_ver_major; +extern int tac_ver_minor; +extern int tac_ver_patch; + +/* header.c */ +extern int session_id; +extern int tac_encryption; +extern char *tac_secret; +extern char *tac_login; + +/* connect.c */ +extern int tac_timeout; +extern int tac_connect(struct addrinfo **server, int servers); +extern int tac_connect_single(struct addrinfo *server); +extern char *tac_ntop(const struct sockaddr *sa, size_t ai_addrlen); + +extern int tac_authen_send(int fd, const char *user, char *pass, char *tty); +extern int tac_authen_read(int fd); +extern int tac_cont_send(int fd, char *pass); +extern HDR *_tac_req_header(u_char type); +extern void _tac_crypt(u_char *buf, HDR *th, int length); +extern u_char *_tac_md5_pad(int len, HDR *hdr); +extern void tac_add_attrib(struct tac_attrib **attr, char *name, char *value); +extern void tac_free_attrib(struct tac_attrib **attr); +extern int tac_account_send(int fd, int type, const char *user, char *tty, + struct tac_attrib *attr); +extern char *tac_account_read(int fd); +extern void *xcalloc(size_t nmemb, size_t size); +extern void *xrealloc(void *ptr, size_t size); +extern char *_tac_check_header(HDR *th, int type); +extern int tac_author_send(int fd, const char *user, char *tty, + struct tac_attrib *attr); +extern void tac_author_read(int fd, struct areply *arep); + +#endif + diff --git a/libtac/include/tacplus.h b/libtac/include/tacplus.h new file mode 100644 index 0000000..d43e563 --- /dev/null +++ b/libtac/include/tacplus.h @@ -0,0 +1,265 @@ +/* tacplus.h + * + * Copyright (C) 2010, Pawel Krawczyk and + * Jeroen Nijhof + * + * 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 - see the file COPYING. + * + * See `CHANGES' file for revision history. + */ + +#ifndef _TACPLUS_H +#define _TACPLUS_H + +#include +#ifdef sun + #include "cdefs.h" +#else + #include +#endif + +struct tac_attrib { + char *attr; + u_char attr_len; + struct tac_attrib *next; +}; + +struct areply { + struct tac_attrib *attr; + char *msg; + int status; +}; + +#ifndef TAC_PLUS_MAXSERVERS +#define TAC_PLUS_MAXSERVERS 4 +#endif + +#ifndef TAC_PLUS_PORT +#define TAC_PLUS_PORT 49 +#endif + +#define TAC_PLUS_READ_TIMEOUT 180 /* seconds */ +#define TAC_PLUS_WRITE_TIMEOUT 180 /* seconds */ + +/* All tacacs+ packets have the same header format */ + +struct tac_plus_pak_hdr { + u_char version; + +#define TAC_PLUS_MAJOR_VER_MASK 0xf0 +#define TAC_PLUS_MAJOR_VER 0xc0 + +#define TAC_PLUS_MINOR_VER_0 0x0 +#define TAC_PLUS_VER_0 (TAC_PLUS_MAJOR_VER | TAC_PLUS_MINOR_VER_0) + +#define TAC_PLUS_MINOR_VER_1 0x01 +#define TAC_PLUS_VER_1 (TAC_PLUS_MAJOR_VER | TAC_PLUS_MINOR_VER_1) + + u_char type; + +#define TAC_PLUS_AUTHEN 1 +#define TAC_PLUS_AUTHOR 2 +#define TAC_PLUS_ACCT 3 + + u_char seq_no; /* packet sequence number */ + u_char encryption; /* packet is encrypted or cleartext */ + +#define TAC_PLUS_ENCRYPTED 0x0 /* packet is encrypted */ +#define TAC_PLUS_CLEAR 0x1 /* packet is not encrypted */ + + int session_id; /* session identifier FIXME: Is this needed? */ + int datalength; /* length of encrypted data following this + * header */ + /* datalength bytes of encrypted data */ +}; + +#define TAC_PLUS_HDR_SIZE 12 + +typedef struct tac_plus_pak_hdr HDR; + +/* Authentication packet NAS sends to us */ + +struct authen_start { + u_char action; + +#define TAC_PLUS_AUTHEN_LOGIN 0x1 +#define TAC_PLUS_AUTHEN_CHPASS 0x2 +#define TAC_PLUS_AUTHEN_SENDPASS 0x3 /* deprecated */ +#define TAC_PLUS_AUTHEN_SENDAUTH 0x4 + + u_char priv_lvl; + +#define TAC_PLUS_PRIV_LVL_MIN 0x0 +#define TAC_PLUS_PRIV_LVL_MAX 0xf + + u_char authen_type; + +#define TAC_PLUS_AUTHEN_TYPE_ASCII 1 +#define TAC_PLUS_AUTHEN_TYPE_PAP 2 +#define TAC_PLUS_AUTHEN_TYPE_CHAP 3 +#define TAC_PLUS_AUTHEN_TYPE_ARAP 4 + + u_char service; + +#define TAC_PLUS_AUTHEN_SVC_LOGIN 1 +#define TAC_PLUS_AUTHEN_SVC_ENABLE 2 +#define TAC_PLUS_AUTHEN_SVC_PPP 3 +#define TAC_PLUS_AUTHEN_SVC_ARAP 4 +#define TAC_PLUS_AUTHEN_SVC_PT 5 +#define TAC_PLUS_AUTHEN_SVC_RCMD 6 +#define TAC_PLUS_AUTHEN_SVC_X25 7 +#define TAC_PLUS_AUTHEN_SVC_NASI 8 + + u_char user_len; + u_char port_len; + u_char rem_addr_len; + u_char data_len; + /* */ + /* */ + /* */ + /* */ +}; + +#define TAC_AUTHEN_START_FIXED_FIELDS_SIZE 8 + +/* Authentication continue packet NAS sends to us */ +struct authen_cont { + u_short user_msg_len; + u_short user_data_len; + u_char flags; + +#define TAC_PLUS_CONTINUE_FLAG_ABORT 0x1 + + /* */ + /* */ +}; + +#define TAC_AUTHEN_CONT_FIXED_FIELDS_SIZE 5 + +/* Authentication reply packet we send to NAS */ +struct authen_reply { + u_char status; + +#define TAC_PLUS_AUTHEN_STATUS_PASS 1 +#define TAC_PLUS_AUTHEN_STATUS_FAIL 2 +#define TAC_PLUS_AUTHEN_STATUS_GETDATA 3 +#define TAC_PLUS_AUTHEN_STATUS_GETUSER 4 +#define TAC_PLUS_AUTHEN_STATUS_GETPASS 5 +#define TAC_PLUS_AUTHEN_STATUS_RESTART 6 +#define TAC_PLUS_AUTHEN_STATUS_ERROR 7 +#define TAC_PLUS_AUTHEN_STATUS_FOLLOW 0x21 + + u_char flags; + +#define TAC_PLUS_AUTHEN_FLAG_NOECHO 0x1 + + u_short msg_len; + u_short data_len; + + /* */ + /* */ +}; + +#define TAC_AUTHEN_REPLY_FIXED_FIELDS_SIZE 6 + +#define AUTHEN_METH_NONE 0x01 +#define AUTHEN_METH_KRB5 0x02 +#define AUTHEN_METH_LINE 0x03 +#define AUTHEN_METH_ENABLE 0x04 +#define AUTHEN_METH_LOCAL 0x05 +#define AUTHEN_METH_TACACSPLUS 0x06 +#define AUTHEN_METH_RCMD 0x20 + +struct acct { + u_char flags; + +#define TAC_PLUS_ACCT_FLAG_MORE 0x1 +#define TAC_PLUS_ACCT_FLAG_START 0x2 +#define TAC_PLUS_ACCT_FLAG_STOP 0x4 +#define TAC_PLUS_ACCT_FLAG_WATCHDOG 0x8 + + u_char authen_method; + u_char priv_lvl; + u_char authen_type; + u_char authen_service; + u_char user_len; + u_char port_len; + u_char rem_addr_len; + u_char arg_cnt; /* the number of cmd args */ + /* one u_char containing size for each arg */ + /* */ + /* */ + /* */ + /* char data for args 1 ... n */ +}; + +#define TAC_ACCT_REQ_FIXED_FIELDS_SIZE 9 + +struct acct_reply { + u_short msg_len; + u_short data_len; + u_char status; + +#define TAC_PLUS_ACCT_STATUS_SUCCESS 0x1 +#define TAC_PLUS_ACCT_STATUS_ERROR 0x2 +#define TAC_PLUS_ACCT_STATUS_FOLLOW 0x21 + +}; + +#define TAC_ACCT_REPLY_FIXED_FIELDS_SIZE 5 + +/* An authorization request packet */ +struct author { + u_char authen_method; + u_char priv_lvl; + u_char authen_type; + u_char service; + + u_char user_len; + u_char port_len; + u_char rem_addr_len; + u_char arg_cnt; /* the number of args */ + + /* */ + /* */ + /* */ + /* */ + /* */ +}; + +#define TAC_AUTHOR_REQ_FIXED_FIELDS_SIZE 8 + +/* An authorization reply packet */ +struct author_reply { + u_char status; + u_char arg_cnt; + u_short msg_len; + u_short data_len; + +#define AUTHOR_STATUS_PASS_ADD 0x01 +#define AUTHOR_STATUS_PASS_REPL 0x02 +#define AUTHOR_STATUS_FAIL 0x10 +#define AUTHOR_STATUS_ERROR 0x11 +#define AUTHOR_STATUS_FOLLOW 0x21 + + /* */ + /* */ + /* */ + /* */ +}; + +#define TAC_AUTHOR_REPLY_FIXED_FIELDS_SIZE 6 + + +#endif diff --git a/libtac/lib/acct_r.c b/libtac/lib/acct_r.c new file mode 100644 index 0000000..d30d18f --- /dev/null +++ b/libtac/lib/acct_r.c @@ -0,0 +1,97 @@ +/* acct_r.c - Read accounting reply from server. + * + * Copyright (C) 2010, Pawel Krawczyk and + * Jeroen Nijhof + * + * 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 - see the file COPYING. + * + * See `CHANGES' file for revision history. + */ + +#include "tacplus.h" +#include "libtac.h" +#include "messages.h" + +char *tac_account_read(int fd) { + HDR th; + struct acct_reply *tb; + int len_from_header, r, len_from_body; + char *msg = NULL; + + r=read(fd, &th, TAC_PLUS_HDR_SIZE); + if(r < TAC_PLUS_HDR_SIZE) { + syslog(LOG_ERR, + "%s: short acct header, %d of %d: %m", __FUNCTION__, + r, TAC_PLUS_HDR_SIZE); + return(system_err_msg); + } + + /* check the reply fields in header */ + msg = _tac_check_header(&th, TAC_PLUS_ACCT); + if(msg != NULL) + return(msg); + + len_from_header=ntohl(th.datalength); + tb=(struct acct_reply *) xcalloc(1, len_from_header); + + /* read reply packet body */ + r=read(fd, tb, len_from_header); + if(r < len_from_header) { + syslog(LOG_ERR, + "%s: incomplete message body, %d bytes, expected %d: %m", + __FUNCTION__, + r, len_from_header); + return(system_err_msg); + } + + /* decrypt the body */ + _tac_crypt((u_char *) tb, &th, len_from_header); + + /* Convert network byte order to host byte order */ + tb->msg_len = ntohs(tb->msg_len); + tb->data_len = ntohs(tb->data_len); + + /* check the length fields */ + len_from_body=sizeof(tb->msg_len) + sizeof(tb->data_len) + + sizeof(tb->status) + tb->msg_len + tb->data_len; + + if(len_from_header != len_from_body) { + syslog(LOG_ERR, + "%s: invalid reply content, incorrect key?", + __FUNCTION__); + return(system_err_msg); + } + + /* save status and clean up */ + r=tb->status; + if(tb->msg_len) { + msg=(char *) xcalloc(1, tb->msg_len); + bcopy((u_char *) tb+TAC_ACCT_REPLY_FIXED_FIELDS_SIZE, msg, tb->msg_len); + } else + msg="Accounting failed"; + + free(tb); + + /* server logged our request successfully */ + if(r == TAC_PLUS_ACCT_STATUS_SUCCESS) { + TACDEBUG((LOG_DEBUG, "%s: accounted ok", __FUNCTION__)) + msg=NULL; + return(NULL); + } + /* return pointer to server message */ + syslog(LOG_DEBUG, "%s: accounting failed, server reply was %d (%s)", + __FUNCTION__, r, msg); + return(msg); + +} diff --git a/libtac/lib/acct_s.c b/libtac/lib/acct_s.c new file mode 100644 index 0000000..5ca07c3 --- /dev/null +++ b/libtac/lib/acct_s.c @@ -0,0 +1,151 @@ +/* acct_s.c - Send accounting event information to server. + * + * Copyright (C) 2010, Pawel Krawczyk and + * Jeroen Nijhof + * + * 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 - see the file COPYING. + * + * See `CHANGES' file for revision history. + */ + +#include "tacplus.h" +#include "libtac.h" +#include "xalloc.h" + +int tac_account_send(int fd, int type, const char *user, char *tty, + struct tac_attrib *attr) { + HDR *th; + struct acct tb; + u_char user_len, port_len; + struct tac_attrib *a; + int i = 0; /* arg count */ + int pkt_len = 0; + int pktl = 0; + int w; /* write count */ + u_char *pkt; + /* u_char *pktp; */ /* obsolute */ + int ret = 0; + + th=_tac_req_header(TAC_PLUS_ACCT); + + /* set header options */ + th->version=TAC_PLUS_VER_0; + th->encryption=tac_encryption ? TAC_PLUS_ENCRYPTED : TAC_PLUS_CLEAR; + + TACDEBUG((LOG_DEBUG, "%s: user '%s', tty '%s', encrypt: %s, type: %s", \ + __FUNCTION__, user, tty, \ + (tac_encryption) ? "yes" : "no", \ + (type == TAC_PLUS_ACCT_FLAG_START) ? "START" : "STOP")) + + user_len=(u_char) strlen(user); + port_len=(u_char) strlen(tty); + + tb.flags=(u_char) type; + tb.authen_method=AUTHEN_METH_TACACSPLUS; + tb.priv_lvl=TAC_PLUS_PRIV_LVL_MIN; + if(strcmp(tac_login,"chap") == 0) { + tb.authen_type=TAC_PLUS_AUTHEN_TYPE_CHAP; + } else if(strcmp(tac_login,"login") == 0) { + tb.authen_type=TAC_PLUS_AUTHEN_TYPE_ASCII; + } else { + tb.authen_type=TAC_PLUS_AUTHEN_TYPE_PAP; + } + tb.authen_service=TAC_PLUS_AUTHEN_SVC_PPP; + tb.user_len=user_len; + tb.port_len=port_len; + tb.rem_addr_len=0; + + /* allocate packet */ + pkt=(u_char *) xcalloc(1, TAC_ACCT_REQ_FIXED_FIELDS_SIZE); + pkt_len=sizeof(tb); + + /* fill attribute length fields */ + a = attr; + while(a) { + + pktl = pkt_len; + pkt_len += sizeof(a->attr_len); + pkt = xrealloc(pkt, pkt_len); + + /* see comments in author_s.c + pktp=pkt + pkt_len; + pkt_len += sizeof(a->attr_len); + pkt = xrealloc(pkt, pkt_len); + */ + + bcopy(&a->attr_len, pkt + pktl, sizeof(a->attr_len)); + i++; + + a = a->next; + } + + /* fill the arg count field and add the fixed fields to packet */ + tb.arg_cnt = i; + bcopy(&tb, pkt, TAC_ACCT_REQ_FIXED_FIELDS_SIZE); + + /* +#define PUTATTR(data, len) \ + pktp = pkt + pkt_len; \ + pkt_len += len; \ + pkt = xrealloc(pkt, pkt_len); \ + bcopy(data, pktp, len); +*/ +#define PUTATTR(data, len) \ + pktl = pkt_len; \ + pkt_len += len; \ + pkt = xrealloc(pkt, pkt_len); \ + bcopy(data, pkt + pktl, len); + + /* fill user and port fields */ + PUTATTR(user, user_len) + PUTATTR(tty, port_len) + + /* fill attributes */ + a = attr; + while(a) { + PUTATTR(a->attr, a->attr_len) + + a = a->next; + } + + /* finished building packet, fill len_from_header in header */ + th->datalength = htonl(pkt_len); + + /* write header */ + w=write(fd, th, TAC_PLUS_HDR_SIZE); + + if(w < TAC_PLUS_HDR_SIZE) { + syslog(LOG_ERR, "%s: acct hdr send failed: wrote %d of %d", + __FUNCTION__, w, + TAC_PLUS_HDR_SIZE); + ret = -1; + } + + /* encrypt packet body */ + _tac_crypt(pkt, th, pkt_len); + + /* write body */ + w=write(fd, pkt, pkt_len); + if(w < pkt_len) { + syslog(LOG_ERR, "%s: acct body send failed: wrote %d of %d", + __FUNCTION__, w, + pkt_len); + ret = -1; + } + + free(pkt); + free(th); + + return(ret); +} diff --git a/libtac/lib/attrib.c b/libtac/lib/attrib.c new file mode 100644 index 0000000..761cde0 --- /dev/null +++ b/libtac/lib/attrib.c @@ -0,0 +1,80 @@ +/* attrib.c - Procedures for handling internal list of attributes + * for accounting and authorization functions. + * + * Copyright (C) 2010, Pawel Krawczyk and + * Jeroen Nijhof + * + * 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 - see the file COPYING. + * + * See `CHANGES' file for revision history. + */ + +#include "tacplus.h" +#include "libtac.h" +#include "xalloc.h" + +void tac_add_attrib(struct tac_attrib **attr, char *name, char *value) { + struct tac_attrib *a; + u_char l1 = (u_char) strlen(name); + u_char l2 = (u_char) strlen(value); + int total_len = l1 + l2 + 1; /* "name" + "=" + "value" */ + + if(total_len > 255) { + syslog(LOG_WARNING, "%s: attribute `%s' total length exceeds 255 characters, skipping", __FUNCTION__, name); + return; + } + + /* initialize the list if application passed us a null pointer */ + if(*attr == NULL) { + *attr = (struct tac_attrib *) xcalloc(1, sizeof(struct tac_attrib)); + a = *attr; + } else { + /* find the last allocated block */ + a = *attr; + while(a->next != NULL) + a = a->next; /* a holds last allocated block */ + + a->next = (struct tac_attrib *) xcalloc(1, sizeof(struct tac_attrib)); + a = a->next; /* set current block pointer to the new one */ + } + + /* fill the block */ + a->attr_len=total_len; + a->attr = (char *) xcalloc(1, total_len); + bcopy(name, a->attr, l1); /* paste name */ + *(a->attr+l1)='='; /* insert "=" */ + bcopy(value, (a->attr+l1+1), l2); /* paste value */ + + a->next = NULL; /* make sure it's null */ + +} + +void tac_free_attrib(struct tac_attrib **attr) { + struct tac_attrib *a; + struct tac_attrib *b; + + if(*attr == NULL) + return; + + a = b = *attr; + + /* find last allocated block */ + do { + a = b; + b = a->next; + free(a->attr); + free(a); + } while (b != NULL); + +} diff --git a/libtac/lib/authen_r.c b/libtac/lib/authen_r.c new file mode 100644 index 0000000..55acac5 --- /dev/null +++ b/libtac/lib/authen_r.c @@ -0,0 +1,104 @@ +/* authen_r.c - Read authentication reply from server. + * + * Copyright (C) 2010, Pawel Krawczyk and + * Jeroen Nijhof + * + * 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 - see the file COPYING. + * + * See `CHANGES' file for revision history. + */ + +#include "tacplus.h" +#include "libtac.h" +#include "messages.h" + +/* reads packet from TACACS+ server; returns: + * TAC_PLUS_AUTHEN_STATUS_PASS if the authentication succeded + * an other integer if failed. Check tacplus.h for all possible values + */ +int tac_authen_read(int fd) { + HDR th; + struct authen_reply *tb; + int len_from_header, r, len_from_body; + char *msg = NULL; + + /* read the reply header */ + r=read(fd, &th, TAC_PLUS_HDR_SIZE); + if(r < TAC_PLUS_HDR_SIZE) { + syslog(LOG_ERR, + "%s: error reading authen header, read %d of %d: %m", + __FUNCTION__, + r, TAC_PLUS_HDR_SIZE); + return(TAC_PLUS_AUTHEN_STATUS_FAIL); + } + + /* check the reply fields in header */ + msg = _tac_check_header(&th, TAC_PLUS_AUTHEN); + if(msg != NULL) + return(TAC_PLUS_AUTHEN_STATUS_FAIL); + + len_from_header=ntohl(th.datalength); + tb=(struct authen_reply *) xcalloc(1, len_from_header); + + /* read reply packet body */ + r=read(fd, tb, len_from_header); + if(r < len_from_header) { + syslog(LOG_ERR, + "%s: incomplete message body, %d bytes, expected %d: %m", + __FUNCTION__, + r, len_from_header); + return(TAC_PLUS_AUTHEN_STATUS_FAIL); + } + + /* decrypt the body */ + _tac_crypt((u_char *) tb, &th, len_from_header); + + /* Convert network byte order to host byte order */ + tb->msg_len = ntohs(tb->msg_len); + tb->data_len = ntohs(tb->data_len); + + /* check the length fields */ + len_from_body=sizeof(tb->status) + sizeof(tb->flags) + + sizeof(tb->msg_len) + sizeof(tb->data_len) + + tb->msg_len + tb->data_len; + + if(len_from_header != len_from_body) { + syslog(LOG_ERR, + "%s: invalid reply content, incorrect key?", + __FUNCTION__); + return(TAC_PLUS_AUTHEN_STATUS_FAIL); + } + + /* save status and clean up */ + r=tb->status; + free(tb); + + /* server authenticated username and password successfully */ + if(r == TAC_PLUS_AUTHEN_STATUS_PASS) { + TACDEBUG((LOG_DEBUG, "%s: authentication ok", __FUNCTION__)) + return(r); + } + + /* server ask for continue packet with password */ + if(r == TAC_PLUS_AUTHEN_STATUS_GETPASS) { + TACDEBUG((LOG_DEBUG, "%s: continue packet with password needed", __FUNCTION__)) + return(r); + } + + /* return pointer to server message */ + syslog(LOG_DEBUG, "%s: authentication failed, server reply was %d", + __FUNCTION__, r); + return(r); + +} /* tac_authen_read */ diff --git a/libtac/lib/authen_s.c b/libtac/lib/authen_s.c new file mode 100644 index 0000000..4a7e55a --- /dev/null +++ b/libtac/lib/authen_s.c @@ -0,0 +1,143 @@ +/* authen_s.c - Send authentication request to the server. + * + * Copyright (C) 2010, Pawel Krawczyk and + * Jeroen Nijhof + * + * 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 - see the file COPYING. + * + * See `CHANGES' file for revision history. + */ + +#include "tacplus.h" +#include "libtac.h" +#include "md5.h" + +/* this function sends a packet do TACACS+ server, asking + * for validation of given username and password + */ +int tac_authen_send(int fd, const char *user, char *pass, char *tty) +{ + HDR *th; /* TACACS+ packet header */ + struct authen_start tb; /* message body */ + int user_len, port_len, chal_len, mdp_len, token_len, bodylength, w; + int pkt_len=0; + int ret=0; + char *chal = "1234123412341234"; + char digest[MD5_LEN]; + char *token; + u_char *pkt, *mdp; + MD5_CTX mdcontext; + + th=_tac_req_header(TAC_PLUS_AUTHEN); + + /* set some header options */ + if(strcmp(tac_login,"login") == 0) { + th->version=TAC_PLUS_VER_0; + } else { + th->version=TAC_PLUS_VER_1; + } + th->encryption=tac_encryption ? TAC_PLUS_ENCRYPTED : TAC_PLUS_CLEAR; + + TACDEBUG((LOG_DEBUG, "%s: user '%s', tty '%s', encrypt: %s", \ + __FUNCTION__, user, tty, \ + (tac_encryption) ? "yes" : "no")) + + if(strcmp(tac_login,"chap") == 0) { + chal_len = strlen(chal); + mdp_len = sizeof(u_char) + strlen(pass) + chal_len; + mdp = (u_char *) xcalloc(1, mdp_len); + mdp[0] = 5; + memcpy(&mdp[1], pass, strlen(pass)); + memcpy(mdp + strlen(pass) + 1, chal, chal_len); + MD5Init(&mdcontext); + MD5Update(&mdcontext, mdp, mdp_len); + MD5Final((u_char *) digest, &mdcontext); + free(mdp); + token = xcalloc(1, sizeof(u_char) + 1 + chal_len + MD5_LEN); + token[0] = 5; + memcpy(&token[1], chal, chal_len); + memcpy(token + chal_len + 1, digest, MD5_LEN); + } else { + token = pass; + } + + /* get size of submitted data */ + user_len=strlen(user); + port_len=strlen(tty); + token_len=strlen(token); + + /* fill the body of message */ + tb.action=TAC_PLUS_AUTHEN_LOGIN; + tb.priv_lvl=TAC_PLUS_PRIV_LVL_MIN; + if(strcmp(tac_login,"chap") == 0) { + tb.authen_type=TAC_PLUS_AUTHEN_TYPE_CHAP; + } else if(strcmp(tac_login,"login") == 0) { + tb.authen_type=TAC_PLUS_AUTHEN_TYPE_ASCII; + } else { + tb.authen_type=TAC_PLUS_AUTHEN_TYPE_PAP; + } + tb.service=TAC_PLUS_AUTHEN_SVC_PPP; + tb.user_len=user_len; + tb.port_len=port_len; + tb.rem_addr_len=0; /* may be e.g Caller-ID in future */ + tb.data_len=token_len; + + /* fill body length in header */ + bodylength=sizeof(tb) + user_len + + port_len + token_len; /* + rem_addr_len */ + + th->datalength= htonl(bodylength); + + /* we can now write the header */ + w=write(fd, th, TAC_PLUS_HDR_SIZE); + if(w < 0 || w < TAC_PLUS_HDR_SIZE) { + syslog(LOG_ERR, "%s: short write on header: wrote %d of %d: %m", + __FUNCTION__, w, TAC_PLUS_HDR_SIZE); + ret=-1; + } + + /* build the packet */ + pkt=(u_char *) xcalloc(1, bodylength+10); + + bcopy(&tb, pkt+pkt_len, sizeof(tb)); /* packet body beginning */ + pkt_len+=sizeof(tb); + bcopy(user, pkt+pkt_len, user_len); /* user */ + pkt_len+=user_len; + bcopy(tty, pkt+pkt_len, port_len); /* tty */ + pkt_len+=port_len; + bcopy(token, pkt+pkt_len, token_len); /* password */ + pkt_len+=token_len; + + /* pkt_len == bodylength ? */ + if(pkt_len != bodylength) { + syslog(LOG_ERR, "%s: bodylength %d != pkt_len %d", + __FUNCTION__, bodylength, pkt_len); + ret=-1; + } + + /* encrypt the body */ + _tac_crypt(pkt, th, bodylength); + + w=write(fd, pkt, pkt_len); + if(w < 0 || w < pkt_len) { + syslog(LOG_ERR, "%s: short write on body: wrote %d of %d: %m", + __FUNCTION__, w, pkt_len); + ret=-1; + } + + free(pkt); + free(th); + + return(ret); +} /* tac_authen_send */ diff --git a/libtac/lib/author_r.c b/libtac/lib/author_r.c new file mode 100644 index 0000000..3740e6c --- /dev/null +++ b/libtac/lib/author_r.c @@ -0,0 +1,194 @@ +/* author_r.c - Reads authorization reply from the server. + * + * Copyright (C) 2010, Pawel Krawczyk and + * Jeroen Nijhof + * + * 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 - see the file COPYING. + * + * See `CHANGES' file for revision history. + */ + +#include "tacplus.h" +#include "xalloc.h" +#include "libtac.h" +#include "messages.h" + +/* This function returns structure containing + 1. status (granted/denied) + 2. message for the user + 3. list of attributes returned by server + The attributes should be applied to service authorization + is requested for, but actually the aren't. Attributes are + discarded. +*/ +void tac_author_read(int fd, struct areply *re) { + HDR th; + struct author_reply *tb = NULL; + int len_from_header, r, len_from_body; + char *pktp; + char *msg = NULL; + + bzero(re, sizeof(struct areply)); + + r=read(fd, &th, TAC_PLUS_HDR_SIZE); + if(r < TAC_PLUS_HDR_SIZE) { + syslog(LOG_ERR, + "%s: short author header, %d of %d: %m", __FUNCTION__, + r, TAC_PLUS_HDR_SIZE); + re->msg = system_err_msg; + re->status = AUTHOR_STATUS_ERROR; + goto AuthorExit; + } + + /* check header consistency */ + msg = _tac_check_header(&th, TAC_PLUS_AUTHOR); + if(msg != NULL) { + /* no need to process body if header is broken */ + re->msg = msg; + re->status = AUTHOR_STATUS_ERROR; + goto AuthorExit; + } + + len_from_header=ntohl(th.datalength); + tb=(struct author_reply *) xcalloc(1, len_from_header); + + /* read reply packet body */ + r=read(fd, tb, len_from_header); + if(r < len_from_header) { + syslog(LOG_ERR, + "%s: short author body, %d of %d: %m", __FUNCTION__, + r, len_from_header); + re->msg = system_err_msg; + re->status = AUTHOR_STATUS_ERROR; + goto AuthorExit; + } + + /* decrypt the body */ + _tac_crypt((u_char *) tb, &th, len_from_header); + + /* Convert network byte order to host byte order */ + tb->msg_len = ntohs(tb->msg_len); + tb->data_len = ntohs(tb->data_len); + + /* check consistency of the reply body + * len_from_header = declared in header + * len_from_body = value computed from body fields + */ + len_from_body = TAC_AUTHOR_REPLY_FIXED_FIELDS_SIZE + + tb->msg_len + tb->data_len; + + pktp = (char *) tb + TAC_AUTHOR_REPLY_FIXED_FIELDS_SIZE; + + for(r = 0; r < tb->arg_cnt; r++) { + len_from_body += sizeof(u_char); /* add arg length field's size*/ + len_from_body += *pktp; /* add arg length itself */ + pktp++; + } + + if(len_from_header != len_from_body) { + syslog(LOG_ERR, + "%s: inconsistent author reply body, incorrect key?", + __FUNCTION__); + re->msg = system_err_msg; + re->status = AUTHOR_STATUS_ERROR; + goto AuthorExit; + } + + /* packet seems to be consistent, prepare return messages */ + /* server message for user */ + if(tb->msg_len) { + char *msg = (char *) xcalloc(1, tb->msg_len+1); + bcopy((u_char *) tb+TAC_AUTHOR_REPLY_FIXED_FIELDS_SIZE + + (tb->arg_cnt)*sizeof(u_char), + msg, tb->msg_len); + re->msg = msg; + } + + /* server message to syslog */ + if(tb->data_len) { + char *smsg=(char *) xcalloc(1, tb->data_len+1); + bcopy((u_char *) tb + TAC_AUTHOR_REPLY_FIXED_FIELDS_SIZE + + (tb->arg_cnt)*sizeof(u_char) + + tb->msg_len, smsg, + tb->data_len); + syslog(LOG_ERR, "%s: author failed: %s", __FUNCTION__,smsg); + free(smsg); + } + + /* prepare status */ + switch(tb->status) { + /* success conditions */ + /* XXX support optional vs mandatory arguments */ + case AUTHOR_STATUS_PASS_ADD: + case AUTHOR_STATUS_PASS_REPL: + { + char *argp; + + if(!re->msg) re->msg=author_ok_msg; + re->status=tb->status; + + /* add attributes received to attribute list returned to + the client */ + pktp = (char *) tb + TAC_AUTHOR_REPLY_FIXED_FIELDS_SIZE; + argp = pktp + (tb->arg_cnt * sizeof(u_char)) + tb->msg_len + + tb->data_len; + /* argp points to current argument string + pktp holds current argument length */ + for(r=0; r < tb->arg_cnt; r++) { + char buff[256]; + char *sep; + char *value; + + bcopy(argp, buff, *pktp); + buff[(int)*pktp] = '\0'; + sep=index(buff, '='); + if(sep == NULL) + sep=index(buff, '*'); + if(sep == NULL) + syslog(LOG_WARNING, "AUTHOR_STATUS_PASS_REPL: attribute contains no separator: %s", buff); + *sep = '\0'; + value = ++sep; + /* now buff points to attribute name, + value to the attribute value */ + tac_add_attrib(&re->attr, buff, value); + + argp += *pktp; + pktp++; + } + } + + break; + + /* authorization failure conditions */ + /* failing to follow is allowed by RFC, page 23 */ + case AUTHOR_STATUS_FOLLOW: + case AUTHOR_STATUS_FAIL: + if(!re->msg) re->msg=author_fail_msg; + re->status=AUTHOR_STATUS_FAIL; + break; + + /* error conditions */ + case AUTHOR_STATUS_ERROR: + default: + if(!re->msg) re->msg=author_err_msg; + re->status=AUTHOR_STATUS_ERROR; + } + +AuthorExit: + + free(tb); + TACDEBUG((LOG_DEBUG, "%s: server replied '%s'", __FUNCTION__, \ + re->msg)) + +} diff --git a/libtac/lib/author_s.c b/libtac/lib/author_s.c new file mode 100644 index 0000000..0bf6518 --- /dev/null +++ b/libtac/lib/author_s.c @@ -0,0 +1,152 @@ +/* author_s.c - Send authorization request to the server. + * + * Copyright (C) 2010, Pawel Krawczyk and + * Jeroen Nijhof + * + * 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 - see the file COPYING. + * + * See `CHANGES' file for revision history. + */ + +#include "tacplus.h" +#include "libtac.h" +#include "xalloc.h" + +/* Send authorization request to the server, along with attributes + specified in attribute list prepared with tac_add_attrib. +*/ +int tac_author_send(int fd, const char *user, char *tty, struct tac_attrib *attr) { + HDR *th; + struct author tb; + u_char user_len, port_len; + struct tac_attrib *a; + int i = 0; /* attributes count */ + int pkt_len = 0; /* current packet length */ + int pktl = 0; /* temporary storage for previous pkt_len values */ + int w; /* write() return value */ + u_char *pkt; /* packet building pointer */ + /* u_char *pktp; */ /* obsolete */ + int ret = 0; + + th=_tac_req_header(TAC_PLUS_AUTHOR); + + /* set header options */ + th->version=TAC_PLUS_VER_0; + th->encryption=tac_encryption ? TAC_PLUS_ENCRYPTED : TAC_PLUS_CLEAR; + + TACDEBUG((LOG_DEBUG, "%s: user '%s', tty '%s', encrypt: %s", \ + __FUNCTION__, user, \ + tty, tac_encryption ? "yes" : "no")) + + user_len=(u_char) strlen(user); + port_len=(u_char) strlen(tty); + + tb.authen_method=AUTHEN_METH_TACACSPLUS; + tb.priv_lvl=TAC_PLUS_PRIV_LVL_MIN; + if(strcmp(tac_login,"chap") == 0) { + tb.authen_type=TAC_PLUS_AUTHEN_TYPE_CHAP; + } else if(strcmp(tac_login,"login") == 0) { + tb.authen_type=TAC_PLUS_AUTHEN_TYPE_ASCII; + } else { + tb.authen_type=TAC_PLUS_AUTHEN_TYPE_PAP; + } + tb.service=TAC_PLUS_AUTHEN_SVC_PPP; + tb.user_len=user_len; + tb.port_len=port_len; + tb.rem_addr_len=0; + + /* allocate packet */ + pkt=(u_char *) xcalloc(1, TAC_AUTHOR_REQ_FIXED_FIELDS_SIZE); + pkt_len=sizeof(tb); + + /* fill attribute length fields */ + a = attr; + while(a) { + + pktl = pkt_len; + pkt_len += sizeof(a->attr_len); + pkt = xrealloc(pkt, pkt_len); + + /* bad method: realloc() is allowed to return different pointer + with each call + pktp=pkt + pkt_len; + pkt_len += sizeof(a->attr_len); + pkt = xrealloc(pkt, pkt_len); + */ + + bcopy(&a->attr_len, pkt + pktl, sizeof(a->attr_len)); + i++; + + a = a->next; + } + + /* fill the arg count field and add the fixed fields to packet */ + tb.arg_cnt = i; + bcopy(&tb, pkt, TAC_AUTHOR_REQ_FIXED_FIELDS_SIZE); +/* +#define PUTATTR(data, len) \ + pktp = pkt + pkt_len; \ + pkt_len += len; \ + pkt = xrealloc(pkt, pkt_len); \ + bcopy(data, pktp, len); +*/ + +#define PUTATTR(data, len) \ + pktl = pkt_len; \ + pkt_len += len; \ + pkt = xrealloc(pkt, pkt_len); \ + bcopy(data, pkt + pktl, len); + + /* fill user and port fields */ + PUTATTR(user, user_len) + PUTATTR(tty, port_len) + + /* fill attributes */ + a = attr; + while(a) { + PUTATTR(a->attr, a->attr_len) + + a = a->next; + } + + /* finished building packet, fill len_from_header in header */ + th->datalength = htonl(pkt_len); + + /* write header */ + w=write(fd, th, TAC_PLUS_HDR_SIZE); + + if(w < TAC_PLUS_HDR_SIZE) { + syslog(LOG_ERR, "%s: author hdr send failed: wrote %d of %d", + __FUNCTION__, w, + TAC_PLUS_HDR_SIZE); + ret = -1; + } + + /* encrypt packet body */ + _tac_crypt(pkt, th, pkt_len); + + /* write body */ + w=write(fd, pkt, pkt_len); + if(w < pkt_len) { + syslog(LOG_ERR, "%s: author body send failed: wrote %d of %d", + __FUNCTION__, w, + pkt_len); + ret = -1; + } + + free(pkt); + free(th); + + return(ret); +} diff --git a/libtac/lib/connect.c b/libtac/lib/connect.c new file mode 100644 index 0000000..7b55645 --- /dev/null +++ b/libtac/lib/connect.c @@ -0,0 +1,144 @@ +/* connect.c - Open connection to server. + * + * Copyright (C) 2010, Pawel Krawczyk and + * Jeroen Nijhof + * + * 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 - see the file COPYING. + * + * See `CHANGES' file for revision history. + */ + +#include +#include +#include +#include +#include + +#ifdef _AIX + #include +#endif + +#include "tacplus.h" +#include "libtac.h" + +/* Pointer to TACACS+ connection timeout */ +int tac_timeout = 5; + +/* Returns file descriptor of open connection + to the first available server from list passed + in server table. +*/ +int tac_connect(struct addrinfo **server, int servers) { + int tries = 0; + int fd, flags, retval; + fd_set readfds, writefds; + struct timeval tv; + + if(!servers) { + syslog(LOG_ERR, "%s: no TACACS+ servers defined", __FUNCTION__); + return(-1); + } + + while(tries < servers) { + if((fd=socket(server[tries]->ai_family, server[tries]->ai_socktype, server[tries]->ai_protocol)) == -1) { + syslog(LOG_WARNING, + "%s: socket creation error", __FUNCTION__); + tries++; + continue; + } + + /* put socket in non blocking mode for timeout support */ + flags = fcntl(fd, F_GETFL, 0); + if(fcntl(fd, F_SETFL, flags | O_NONBLOCK)) { + syslog(LOG_WARNING, "%s: cannot set socket non blocking", + __FUNCTION__); + tries++; + continue; + } + + retval = connect(fd, server[tries]->ai_addr, server[tries]->ai_addrlen); + if((retval == -1) && (errno != EINPROGRESS)) { + syslog(LOG_WARNING, + "%s: connection to %s failed: %m", __FUNCTION__, + tac_ntop(server[tries]->ai_addr, server[tries]->ai_addrlen)); + if(fcntl(fd, F_SETFL, flags)) { + syslog(LOG_WARNING, "%s: cannot restore socket flags", + __FUNCTION__); + } + tries++; + continue; + } + + /* set fds for select */ + FD_ZERO(&readfds); + FD_SET(fd, &readfds); + writefds = readfds; + + /* set timeout seconds */ + tv.tv_sec = tac_timeout; + tv.tv_usec = 0; + + /* check if socket is ready for read or write */ + if(!select(fd+1, &readfds, &writefds, NULL, &tv)) { + syslog(LOG_WARNING, + "%s: connection timeout with %s : %m", __FUNCTION__, + tac_ntop(server[tries]->ai_addr, server[tries]->ai_addrlen)); + if(fcntl(fd, F_SETFL, flags)) { + syslog(LOG_WARNING, "%s: cannot restore socket flags", + __FUNCTION__); + } + tries++; + continue; + } + + /* connected ok */ + if(fcntl(fd, F_SETFL, flags)) { + syslog(LOG_WARNING, "%s: cannot restore socket flags", + __FUNCTION__); + } + TACDEBUG((LOG_DEBUG, "%s: connected to %s", __FUNCTION__, \ + tac_ntop(server[tries]->ai_addr, server[tries]->ai_addrlen))); + + return(fd); + } + + /* all attempts failed */ + syslog(LOG_ERR, "%s: all possible TACACS+ servers failed", __FUNCTION__); + return(-1); +} /* tac_connect */ + + +int tac_connect_single(struct addrinfo *server) { + struct addrinfo *temp[1]; + temp[0] = server; + return(tac_connect(temp, 1)); +} /* tac_connect_single */ + + +char *tac_ntop(const struct sockaddr *sa, size_t ai_addrlen) { + char *str = (char *) xcalloc(1, ai_addrlen); + switch(sa->sa_family) { + case AF_INET: + inet_ntop(AF_INET, &(((struct sockaddr_in *)sa)->sin_addr), + str, ai_addrlen); + break; + case AF_INET6: + inet_ntop(AF_INET6, &(((struct sockaddr_in6 *)sa)->sin6_addr), + str, ai_addrlen); + break; + default: + strncpy(str, "Unknown AF", ai_addrlen); + } + return str; +} /* tac_ntop */ diff --git a/libtac/lib/cont_s.c b/libtac/lib/cont_s.c new file mode 100644 index 0000000..17894cf --- /dev/null +++ b/libtac/lib/cont_s.c @@ -0,0 +1,92 @@ +/* cont_s.c - Send continue request to the server. + * + * Copyright (C) 2010, Jeroen Nijhof + * + * 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 - see the file COPYING. + * + * See `CHANGES' file for revision history. + */ + +#include "tacplus.h" +#include "libtac.h" +#include "md5.h" + +/* this function sends a continue packet do TACACS+ server, asking + * for validation of given password + */ +int tac_cont_send(int fd, char *pass) +{ + HDR *th; /* TACACS+ packet header */ + struct authen_cont tb; /* continue body */ + int pass_len, bodylength, w; + int pkt_len=0; + int ret=0; + u_char *pkt; + + th=_tac_req_header(TAC_PLUS_AUTHEN); + + /* set some header options */ + th->version=TAC_PLUS_VER_0; + th->seq_no=3; /* 1 = request, 2 = reply, 3 = continue, 4 = reply */ + th->encryption=tac_encryption ? TAC_PLUS_ENCRYPTED : TAC_PLUS_CLEAR; + + /* get size of submitted data */ + pass_len=strlen(pass); + + /* fill the body of message */ + tb.user_msg_len=htons(pass_len); + tb.user_data_len=tb.flags=0; + + /* fill body length in header */ + bodylength=TAC_AUTHEN_CONT_FIXED_FIELDS_SIZE+0+pass_len; + + th->datalength=htonl(bodylength); + + /* we can now write the header */ + w=write(fd, th, TAC_PLUS_HDR_SIZE); + if(w < 0 || w < TAC_PLUS_HDR_SIZE) { + syslog(LOG_ERR, "%s: short write on header: wrote %d of %d: %m", + __FUNCTION__, w, TAC_PLUS_HDR_SIZE); + ret=-1; + } + + /* build the packet */ + pkt=(u_char *) xcalloc(1, bodylength); + + bcopy(&tb, pkt+pkt_len, TAC_AUTHEN_CONT_FIXED_FIELDS_SIZE); /* packet body beginning */ + pkt_len+=TAC_AUTHEN_CONT_FIXED_FIELDS_SIZE; + bcopy(pass, pkt+pkt_len, pass_len); /* password */ + pkt_len+=pass_len; + + /* pkt_len == bodylength ? */ + if(pkt_len != bodylength) { + syslog(LOG_ERR, "%s: bodylength %d != pkt_len %d", __FUNCTION__, bodylength, pkt_len); + ret=-1; + } + + /* encrypt the body */ + _tac_crypt(pkt, th, bodylength); + + w=write(fd, pkt, pkt_len); + if(w < 0 || w < pkt_len) { + syslog(LOG_ERR, "%s: short write on body: wrote %d of %d: %m", + __FUNCTION__, w, pkt_len); + ret=-1; + } + + free(pkt); + free(th); + + return(ret); +} /* tac_cont_send */ diff --git a/libtac/lib/crypt.c b/libtac/lib/crypt.c new file mode 100644 index 0000000..ae726fc --- /dev/null +++ b/libtac/lib/crypt.c @@ -0,0 +1,107 @@ +/* crypt.c - TACACS+ encryption related functions + * + * Copyright (C) 2010, Pawel Krawczyk and + * Jeroen Nijhof + * + * 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 - see the file COPYING. + * + * See `CHANGES' file for revision history. + */ + +#include "tacplus.h" +#include "libtac.h" +#include "xalloc.h" +#include "md5.h" + +/* Produce MD5 pseudo-random pad for TACACS+ encryption. + Use data from packet header and secret, which + should be a global variable */ +u_char *_tac_md5_pad(int len, HDR *hdr) { + int n, i, bufsize; + int bp=0; /* buffer pointer */ + int pp=0; /* pad pointer */ + u_char *pad; + u_char *buf; + MD5_CTX mdcontext; + + /* make pseudo pad */ + n=(int)(len/16)+1; /* number of MD5 runs */ + bufsize=sizeof(hdr->session_id) + strlen(tac_secret) + sizeof(hdr->version) + + sizeof(hdr->seq_no) + MD5_LEN + 10; + buf= (u_char *) xcalloc(1, bufsize); + pad= (u_char *) xcalloc(n, MD5_LEN); + + for(i=0; isession_id, buf, sizeof(session_id)); + bp+=sizeof(session_id); + bcopy(tac_secret, buf+bp, strlen(tac_secret)); + bp+=strlen(tac_secret); + bcopy(&hdr->version, buf+bp, sizeof(hdr->version)); + bp+=sizeof(hdr->version); + bcopy(&hdr->seq_no, buf+bp, sizeof(hdr->seq_no)); + bp+=sizeof(hdr->seq_no); + + /* append previous pad if this is not the first run */ + if(i) { + bcopy(pad+((i-1)*MD5_LEN), buf+bp, MD5_LEN); + bp+=MD5_LEN; + } + + MD5Init(&mdcontext); + MD5Update(&mdcontext, buf, bp); + /* this is because MD5 implementation has changed between + * pppd versions 2.2.0g and 2.3.4 + */ +#if 1 + MD5Final(pad+pp, &mdcontext); /* correct for pppd-2.3.4 */ +#else + MD5Final(&mdcontext); /* correct for pppd-2.2.0g */ + bcopy(&mdcontext.digest, pad+pp, MD5_LEN); +#endif + + pp+=MD5_LEN; + } + + free(buf); + return(pad); + +} /* _tac_md5_pad */ + +/* Perform encryption/decryption on buffer. This means simply XORing + each byte from buffer with according byte from pseudo-random + pad. */ +void _tac_crypt(u_char *buf, HDR *th, int length) { + int i; + u_char *pad; + + /* null operation if no encryption requested */ + if(th->encryption == TAC_PLUS_ENCRYPTED) { + + pad=_tac_md5_pad(length, th); + + for(i=0; i and + * Jeroen Nijhof + * + * 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 - see the file COPYING. + * + * See `CHANGES' file for revision history. + */ + +#include "tacplus.h" +#include "messages.h" +#include "libtac.h" + +/* Checks given reply header for possible inconsistencies: + * 1. reply type other than expected + * 2. sequence number other than 2 or 4 + * 3. session_id different from one sent in request + * Returns pointer to error message + * or NULL when the header seems to be correct + */ +char *_tac_check_header(HDR *th, int type) { + + if(th->type != type) { + syslog(LOG_ERR, + "%s: unrelated reply, type %d, expected %d", + __FUNCTION__, th->type, type); + return(protocol_err_msg); + } else if((th->seq_no != 2) && (th->seq_no != 4)) { + syslog(LOG_ERR, "%s: not a reply - seq_no %d != 2", + __FUNCTION__, th->seq_no); + return(protocol_err_msg); + } /* else if(ntohl(th->session_id) != session_id) { + syslog(LOG_ERR, + "%s: unrelated reply, received session_id %d != sent %d", + __FUNCTION__, ntohl(th->session_id), session_id); + return(protocol_err_msg); + } */ + + return(NULL); /* header is ok */ + +} /* check header */ diff --git a/libtac/lib/header.c b/libtac/lib/header.c new file mode 100644 index 0000000..eaccd82 --- /dev/null +++ b/libtac/lib/header.c @@ -0,0 +1,65 @@ +/* header.c - Create pre-filled header for TACACS+ request. + * + * Copyright (C) 2010, Pawel Krawczyk and + * Jeroen Nijhof + * + * 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 - see the file COPYING. + * + * See `CHANGES' file for revision history. + */ + +#include "tacplus.h" +#include "libtac.h" +#include "xalloc.h" +#include "magic.h" + +/* Miscellaneous variables that are global, because we need + * store their values between different functions and connections. + */ +/* Session identifier. */ +int session_id; + +/* Encryption flag. */ +int tac_encryption; + +/* Pointer to TACACS+ shared secret string. */ +char *tac_secret; + +/* Pointer to TACACS+ shared login string. */ +char *tac_login = "pap"; + +/* Returns pre-filled TACACS+ packet header of given type. + * 1. you MUST fill th->datalength and th->version + * 2. you MAY fill th->encryption + * 3. you are responsible for freeing allocated header + * By default packet encryption is enabled. The version + * field depends on the TACACS+ request type and thus it + * cannot be predefined. + */ +HDR *_tac_req_header(u_char type) { + HDR *th; + + th=(HDR *) xcalloc(1, TAC_PLUS_HDR_SIZE); + + /* preset some packet options in header */ + th->type=type; + th->seq_no=1; /* always 1 for request */ + th->encryption=TAC_PLUS_ENCRYPTED; + + /* make session_id from pseudo-random number */ + session_id = magic(); + th->session_id = htonl(session_id); + + return(th); +} diff --git a/libtac/lib/magic.c b/libtac/lib/magic.c new file mode 100644 index 0000000..7ac5621 --- /dev/null +++ b/libtac/lib/magic.c @@ -0,0 +1,122 @@ +/* magic.c - PPP Magic Number routines. + * + * Copyright (C) 1989 Carnegie Mellon University. + * + * 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 - see the file COPYING. + * + * See `CHANGES' file for revision history. + */ + +#include +#include +#include +#include +#include + +/* u_int32_t support for sun */ +#ifdef sun +typedef unsigned int u_int32_t; +#endif + +#include "magic.h" + +#ifndef __linux__ +extern long mrand48 __P((void)); +extern void srand48 __P((long)); +#else +#include +#include + +/* on Linux we use /dev/urandom as random numbers source + I find it really cool :) */ +int rfd = 0; /* /dev/urandom */ +#endif + +/* + * magic_init - Initialize the magic number generator. + * + * Attempts to compute a random number seed which will not repeat. + * The current method uses the current hostid, current process ID + * and current time, currently. + */ +void +magic_init() +{ + long seed; + struct timeval t; + +#ifdef __linux__ + rfd = open("/dev/urandom", O_RDONLY); + if(rfd != -1) + return; + else { + rfd = 0; +#endif + /* if /dev/urandom fails, we try traditional method */ + gettimeofday(&t, NULL); + seed = gethostid() ^ t.tv_sec ^ t.tv_usec ^ getpid(); + srand48(seed); +#ifdef __linux__ + } +#endif +} + +/* + * magic - Returns the next magic number. + */ +u_int32_t +magic() +{ +#ifdef __linux__ + u_int32_t ret = 0; + int bytes = 0; + + if(rfd) + { + bytes = read(rfd, &ret, sizeof(ret)); + return(ret); + } + else + return (u_int32_t) mrand48(); +#else + return (u_int32_t) mrand48(); +#endif +} + +#ifdef NO_DRAND48 +/* + * Substitute procedures for those systems which don't have + * drand48 et al. + */ + +double +drand48() +{ + return (double)random() / (double)0x7fffffffL; /* 2**31-1 */ +} + +long +mrand48() +{ + return random(); +} + +void +srand48(seedval) +long seedval; +{ + srandom((int)seedval); +} + +#endif diff --git a/libtac/lib/magic.h b/libtac/lib/magic.h new file mode 100644 index 0000000..ac626b3 --- /dev/null +++ b/libtac/lib/magic.h @@ -0,0 +1,26 @@ +/* magic.h - PPP Magic Number definitions. + * + * Copyright (C) 1989 Carnegie Mellon University. + * + * 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 - see the file COPYING. + * + * See `CHANGES' file for revision history. + */ + +#ifndef __linux__ + #include "cdefs.h" +#endif + +void magic_init __P((void)); /* Initialize the magic number generator */ +u_int32_t magic __P((void)); /* Returns the next magic number */ diff --git a/libtac/lib/md5.c b/libtac/lib/md5.c new file mode 100644 index 0000000..cf6799c --- /dev/null +++ b/libtac/lib/md5.c @@ -0,0 +1,276 @@ +/* md5.c - the source code for MD5 routines + * + * Copyright (C) 1990, RSA Data Security, Inc. + * + * 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 - see the file COPYING. + * + * See `CHANGES' file for revision history. + */ + +#include +#include "md5.h" + +/* forward declaration */ +static void Transform (); + +static unsigned char PADDING[64] = { + 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +/* F, G, H and I are basic MD5 functions */ +#define F(x, y, z) (((x) & (y)) | ((~x) & (z))) +#define G(x, y, z) (((x) & (z)) | ((y) & (~z))) +#define H(x, y, z) ((x) ^ (y) ^ (z)) +#define I(x, y, z) ((y) ^ ((x) | (~z))) + +/* ROTATE_LEFT rotates x left n bits */ +#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32-(n)))) + +/* FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4 */ +/* Rotation is separate from addition to prevent recomputation */ +#define FF(a, b, c, d, x, s, ac) \ + {(a) += F ((b), (c), (d)) + (x) + (UINT4)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ + } +#define GG(a, b, c, d, x, s, ac) \ + {(a) += G ((b), (c), (d)) + (x) + (UINT4)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ + } +#define HH(a, b, c, d, x, s, ac) \ + {(a) += H ((b), (c), (d)) + (x) + (UINT4)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ + } +#define II(a, b, c, d, x, s, ac) \ + {(a) += I ((b), (c), (d)) + (x) + (UINT4)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ + } + +#ifdef __STDC__ +#define UL(x) x##U +#else +#define UL(x) x +#endif + +/* The routine MD5Init initializes the message-digest context + mdContext. All fields are set to zero. + */ +void MD5Init (mdContext) +MD5_CTX *mdContext; +{ + mdContext->i[0] = mdContext->i[1] = (UINT4)0; + + /* Load magic initialization constants. + */ + mdContext->buf[0] = (UINT4)0x67452301; + mdContext->buf[1] = (UINT4)0xefcdab89; + mdContext->buf[2] = (UINT4)0x98badcfe; + mdContext->buf[3] = (UINT4)0x10325476; +} + +/* The routine MD5Update updates the message-digest context to + account for the presence of each of the characters inBuf[0..inLen-1] + in the message whose digest is being computed. + */ +void MD5Update (mdContext, inBuf, inLen) +MD5_CTX *mdContext; +unsigned char *inBuf; +unsigned int inLen; +{ + UINT4 in[16]; + int mdi; + unsigned int i, ii; + + /* compute number of bytes mod 64 */ + mdi = (int)((mdContext->i[0] >> 3) & 0x3F); + + /* update number of bits */ + if ((mdContext->i[0] + ((UINT4)inLen << 3)) < mdContext->i[0]) + mdContext->i[1]++; + mdContext->i[0] += ((UINT4)inLen << 3); + mdContext->i[1] += ((UINT4)inLen >> 29); + + while (inLen--) { + /* add new character to buffer, increment mdi */ + mdContext->in[mdi++] = *inBuf++; + + /* transform if necessary */ + if (mdi == 0x40) { + for (i = 0, ii = 0; i < 16; i++, ii += 4) + in[i] = (((UINT4)mdContext->in[ii+3]) << 24) | + (((UINT4)mdContext->in[ii+2]) << 16) | + (((UINT4)mdContext->in[ii+1]) << 8) | + ((UINT4)mdContext->in[ii]); + Transform (mdContext->buf, in); + mdi = 0; + } + } +} + +/* The routine MD5Final terminates the message-digest computation and + ends with the desired message digest in mdContext->digest[0...15]. + */ +void MD5Final (hash, mdContext) +unsigned char hash[]; +MD5_CTX *mdContext; +{ + UINT4 in[16]; + int mdi; + unsigned int i, ii; + unsigned int padLen; + + /* save number of bits */ + in[14] = mdContext->i[0]; + in[15] = mdContext->i[1]; + + /* compute number of bytes mod 64 */ + mdi = (int)((mdContext->i[0] >> 3) & 0x3F); + + /* pad out to 56 mod 64 */ + padLen = (mdi < 56) ? (56 - mdi) : (120 - mdi); + MD5Update (mdContext, PADDING, padLen); + + /* append length in bits and transform */ + for (i = 0, ii = 0; i < 14; i++, ii += 4) + in[i] = (((UINT4)mdContext->in[ii+3]) << 24) | + (((UINT4)mdContext->in[ii+2]) << 16) | + (((UINT4)mdContext->in[ii+1]) << 8) | + ((UINT4)mdContext->in[ii]); + Transform (mdContext->buf, in); + + /* store buffer in digest */ + for (i = 0, ii = 0; i < 4; i++, ii += 4) { + mdContext->digest[ii] = (unsigned char)(mdContext->buf[i] & 0xFF); + mdContext->digest[ii+1] = + (unsigned char)((mdContext->buf[i] >> 8) & 0xFF); + mdContext->digest[ii+2] = + (unsigned char)((mdContext->buf[i] >> 16) & 0xFF); + mdContext->digest[ii+3] = + (unsigned char)((mdContext->buf[i] >> 24) & 0xFF); + } + memcpy(hash, mdContext->digest, 16); +} + +/* Basic MD5 step. Transforms buf based on in. + */ +static void Transform (buf, in) +UINT4 *buf; +UINT4 *in; +{ + UINT4 a = buf[0], b = buf[1], c = buf[2], d = buf[3]; + + /* Round 1 */ +#define S11 7 +#define S12 12 +#define S13 17 +#define S14 22 + FF ( a, b, c, d, in[ 0], S11, UL(3614090360)); /* 1 */ + FF ( d, a, b, c, in[ 1], S12, UL(3905402710)); /* 2 */ + FF ( c, d, a, b, in[ 2], S13, UL( 606105819)); /* 3 */ + FF ( b, c, d, a, in[ 3], S14, UL(3250441966)); /* 4 */ + FF ( a, b, c, d, in[ 4], S11, UL(4118548399)); /* 5 */ + FF ( d, a, b, c, in[ 5], S12, UL(1200080426)); /* 6 */ + FF ( c, d, a, b, in[ 6], S13, UL(2821735955)); /* 7 */ + FF ( b, c, d, a, in[ 7], S14, UL(4249261313)); /* 8 */ + FF ( a, b, c, d, in[ 8], S11, UL(1770035416)); /* 9 */ + FF ( d, a, b, c, in[ 9], S12, UL(2336552879)); /* 10 */ + FF ( c, d, a, b, in[10], S13, UL(4294925233)); /* 11 */ + FF ( b, c, d, a, in[11], S14, UL(2304563134)); /* 12 */ + FF ( a, b, c, d, in[12], S11, UL(1804603682)); /* 13 */ + FF ( d, a, b, c, in[13], S12, UL(4254626195)); /* 14 */ + FF ( c, d, a, b, in[14], S13, UL(2792965006)); /* 15 */ + FF ( b, c, d, a, in[15], S14, UL(1236535329)); /* 16 */ + + /* Round 2 */ +#define S21 5 +#define S22 9 +#define S23 14 +#define S24 20 + GG ( a, b, c, d, in[ 1], S21, UL(4129170786)); /* 17 */ + GG ( d, a, b, c, in[ 6], S22, UL(3225465664)); /* 18 */ + GG ( c, d, a, b, in[11], S23, UL( 643717713)); /* 19 */ + GG ( b, c, d, a, in[ 0], S24, UL(3921069994)); /* 20 */ + GG ( a, b, c, d, in[ 5], S21, UL(3593408605)); /* 21 */ + GG ( d, a, b, c, in[10], S22, UL( 38016083)); /* 22 */ + GG ( c, d, a, b, in[15], S23, UL(3634488961)); /* 23 */ + GG ( b, c, d, a, in[ 4], S24, UL(3889429448)); /* 24 */ + GG ( a, b, c, d, in[ 9], S21, UL( 568446438)); /* 25 */ + GG ( d, a, b, c, in[14], S22, UL(3275163606)); /* 26 */ + GG ( c, d, a, b, in[ 3], S23, UL(4107603335)); /* 27 */ + GG ( b, c, d, a, in[ 8], S24, UL(1163531501)); /* 28 */ + GG ( a, b, c, d, in[13], S21, UL(2850285829)); /* 29 */ + GG ( d, a, b, c, in[ 2], S22, UL(4243563512)); /* 30 */ + GG ( c, d, a, b, in[ 7], S23, UL(1735328473)); /* 31 */ + GG ( b, c, d, a, in[12], S24, UL(2368359562)); /* 32 */ + + /* Round 3 */ +#define S31 4 +#define S32 11 +#define S33 16 +#define S34 23 + HH ( a, b, c, d, in[ 5], S31, UL(4294588738)); /* 33 */ + HH ( d, a, b, c, in[ 8], S32, UL(2272392833)); /* 34 */ + HH ( c, d, a, b, in[11], S33, UL(1839030562)); /* 35 */ + HH ( b, c, d, a, in[14], S34, UL(4259657740)); /* 36 */ + HH ( a, b, c, d, in[ 1], S31, UL(2763975236)); /* 37 */ + HH ( d, a, b, c, in[ 4], S32, UL(1272893353)); /* 38 */ + HH ( c, d, a, b, in[ 7], S33, UL(4139469664)); /* 39 */ + HH ( b, c, d, a, in[10], S34, UL(3200236656)); /* 40 */ + HH ( a, b, c, d, in[13], S31, UL( 681279174)); /* 41 */ + HH ( d, a, b, c, in[ 0], S32, UL(3936430074)); /* 42 */ + HH ( c, d, a, b, in[ 3], S33, UL(3572445317)); /* 43 */ + HH ( b, c, d, a, in[ 6], S34, UL( 76029189)); /* 44 */ + HH ( a, b, c, d, in[ 9], S31, UL(3654602809)); /* 45 */ + HH ( d, a, b, c, in[12], S32, UL(3873151461)); /* 46 */ + HH ( c, d, a, b, in[15], S33, UL( 530742520)); /* 47 */ + HH ( b, c, d, a, in[ 2], S34, UL(3299628645)); /* 48 */ + + /* Round 4 */ +#define S41 6 +#define S42 10 +#define S43 15 +#define S44 21 + II ( a, b, c, d, in[ 0], S41, UL(4096336452)); /* 49 */ + II ( d, a, b, c, in[ 7], S42, UL(1126891415)); /* 50 */ + II ( c, d, a, b, in[14], S43, UL(2878612391)); /* 51 */ + II ( b, c, d, a, in[ 5], S44, UL(4237533241)); /* 52 */ + II ( a, b, c, d, in[12], S41, UL(1700485571)); /* 53 */ + II ( d, a, b, c, in[ 3], S42, UL(2399980690)); /* 54 */ + II ( c, d, a, b, in[10], S43, UL(4293915773)); /* 55 */ + II ( b, c, d, a, in[ 1], S44, UL(2240044497)); /* 56 */ + II ( a, b, c, d, in[ 8], S41, UL(1873313359)); /* 57 */ + II ( d, a, b, c, in[15], S42, UL(4264355552)); /* 58 */ + II ( c, d, a, b, in[ 6], S43, UL(2734768916)); /* 59 */ + II ( b, c, d, a, in[13], S44, UL(1309151649)); /* 60 */ + II ( a, b, c, d, in[ 4], S41, UL(4149444226)); /* 61 */ + II ( d, a, b, c, in[11], S42, UL(3174756917)); /* 62 */ + II ( c, d, a, b, in[ 2], S43, UL( 718787259)); /* 63 */ + II ( b, c, d, a, in[ 9], S44, UL(3951481745)); /* 64 */ + + buf[0] += a; + buf[1] += b; + buf[2] += c; + buf[3] += d; +} + diff --git a/libtac/lib/md5.h b/libtac/lib/md5.h new file mode 100644 index 0000000..fc314ed --- /dev/null +++ b/libtac/lib/md5.h @@ -0,0 +1,41 @@ +/* md5.h - header file for implementation of MD5 + * + * Copyright (C) 1990, RSA Data Security, Inc. + * + * 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 - see the file COPYING. + * + * See `CHANGES' file for revision history. + */ + +#ifndef __MD5_INCLUDE__ + +/* typedef a 32-bit type */ +typedef unsigned int UINT4; + +/* Data structure for MD5 (Message-Digest) computation */ +typedef struct { + UINT4 i[2]; /* number of _bits_ handled mod 2^64 */ + UINT4 buf[4]; /* scratch buffer */ + unsigned char in[64]; /* input buffer */ + unsigned char digest[16]; /* actual digest after MD5Final call */ +} MD5_CTX; + +void MD5Init (); +void MD5Update (); +void MD5Final (); + +#define MD5_LEN 16 + +#define __MD5_INCLUDE__ +#endif /* __MD5_INCLUDE__ */ diff --git a/libtac/lib/messages.c b/libtac/lib/messages.c new file mode 100644 index 0000000..8a1ae3e --- /dev/null +++ b/libtac/lib/messages.c @@ -0,0 +1,26 @@ +/* messages.c - Various messages returned to user. + * + * Copyright (C) 2010, Pawel Krawczyk and + * Jeroen Nijhof + * + * 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 - see the file COPYING. + * + * See `CHANGES' file for revision history. + */ + +char *system_err_msg="Authentication error, please contact administrator."; +char *protocol_err_msg="Protocol error."; +char *author_ok_msg="Service granted."; +char *author_fail_msg="Service not allowed."; +char *author_err_msg="Protocol error."; diff --git a/libtac/lib/messages.h b/libtac/lib/messages.h new file mode 100644 index 0000000..37f2402 --- /dev/null +++ b/libtac/lib/messages.h @@ -0,0 +1,26 @@ +/* messages.h + * + * Copyright (C) 2010, Pawel Krawczyk and + * Jeroen Nijhof + * + * 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 - see the file COPYING. + * + * See `CHANGES' file for revision history. + */ + +extern char *system_err_msg; +extern char *protocol_err_msg; +extern char *author_ok_msg; +extern char *author_fail_msg; +extern char *author_err_msg; diff --git a/libtac/lib/version.c b/libtac/lib/version.c new file mode 100644 index 0000000..ccf20ee --- /dev/null +++ b/libtac/lib/version.c @@ -0,0 +1,24 @@ +/* version.c - TACACS+ library version. + * + * Copyright (C) 2010, Pawel Krawczyk and + * Jeroen Nijhof + * + * 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 - see the file COPYING. + * + * See `CHANGES' file for revision history. + */ + +int tac_ver_major = 1; +int tac_ver_minor = 6; +int tac_ver_patch = 5; /* patchlevel */ diff --git a/libtac/lib/xalloc.c b/libtac/lib/xalloc.c new file mode 100644 index 0000000..88bbb28 --- /dev/null +++ b/libtac/lib/xalloc.c @@ -0,0 +1,43 @@ +/* xalloc.c - Failsafe memory allocation functions. + * Taken from excellent glibc.info ;) + * + * Copyright (C) 2010, Pawel Krawczyk and + * Jeroen Nijhof + * + * 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 - see the file COPYING. + * + * See `CHANGES' file for revision history. + */ + +#include +#include + +void *xcalloc(size_t nmemb, size_t size) { + register void *val = calloc(nmemb, size); + if(val == 0) { + syslog(LOG_ERR, "%s: calloc(%u,%u) failed", __FUNCTION__, + (unsigned) nmemb, (unsigned) size); + exit(1); + } + return val; +} + +void *xrealloc(void *ptr, size_t size) { + register void *val = realloc(ptr, size); + if(val == 0) { + syslog(LOG_ERR, "%s: realloc(%u) failed", __FUNCTION__, (unsigned) size); + exit(1); + } + return val; +} diff --git a/libtac/lib/xalloc.h b/libtac/lib/xalloc.h new file mode 100644 index 0000000..da02dfa --- /dev/null +++ b/libtac/lib/xalloc.h @@ -0,0 +1,23 @@ +/* xalloc.h + * + * Copyright (C) 2010, Pawel Krawczyk and + * Jeroen Nijhof + * + * 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 - see the file COPYING. + * + * See `CHANGES' file for revision history. + */ + +extern void *xcalloc(size_t nmemb, size_t size); +extern void *xrealloc(void *ptr, size_t size); diff --git a/pam_tacplus.c b/pam_tacplus.c new file mode 100644 index 0000000..4b4e2a6 --- /dev/null +++ b/pam_tacplus.c @@ -0,0 +1,645 @@ +/* pam_tacplus.c - PAM interface for TACACS+ protocol. + * + * Copyright (C) 2010, Pawel Krawczyk and + * Jeroen Nijhof + * + * 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 - see the file COPYING. + * + * See `CHANGES' file for revision history. + */ + +#include /* malloc */ +#include +#include +#include /* gethostbyname */ +#include /* in_addr */ +#include +#include +#include /* va_ */ +#include +#include /* strdup */ +#include +#include +#include + +#ifndef __linux__ + #include +#endif + +#include "tacplus.h" +#include "libtac.h" +#include "pam_tacplus.h" +#include "support.h" + +#define PAM_SM_AUTH +#define PAM_SM_ACCOUNT +#define PAM_SM_SESSION +/* #define PAM_SM_PASSWORD */ + +#ifndef __linux__ + #include +#endif +#include + +#ifdef HAVE_CONFIG_H + #include "config.h" +#endif + +/* support.c */ +extern struct addrinfo *tac_srv[TAC_MAX_SERVERS]; +extern int tac_srv_no; +extern char *tac_service; +extern char *tac_protocol; +extern int _pam_parse (int argc, const char **argv); +extern unsigned long _getserveraddr (char *serv); +extern int tacacs_get_password (pam_handle_t * pamh, int flags + ,int ctrl, char **password); +extern int converse (pam_handle_t * pamh, int nargs + ,struct pam_message **message + ,struct pam_response **response); +extern void _pam_log (int err, const char *format,...); +extern void *_xcalloc (size_t size); + +/* magic.c */ +extern u_int32_t magic(); + +/* libtac */ +extern char *tac_secret; +extern int tac_encryption; + +/* address of server discovered by pam_sm_authenticate */ +static struct addrinfo *active_server; +/* accounting task identifier */ +static short int task_id = 0; + + +/* Helper functions */ +int _pam_send_account(int tac_fd, int type, const char *user, char *tty) { + char buf[40]; + struct tac_attrib *attr; + int retval, status = -1; + + + attr=(struct tac_attrib *)_xcalloc(sizeof(struct tac_attrib)); + +#ifdef _AIX + sprintf(buf, "%d", time(0)); +#else + sprintf(buf, "%lu", (long unsigned int)time(0)); +#endif + + tac_add_attrib(&attr, + (type == TAC_PLUS_ACCT_FLAG_START) ? "start_time" : "stop_time" + , buf); + sprintf(buf, "%hu", task_id); + tac_add_attrib(&attr, "task_id", buf); + tac_add_attrib(&attr, "service", tac_service); + tac_add_attrib(&attr, "protocol", tac_protocol); + + retval = tac_account_send(tac_fd, type, user, tty, attr); + + /* this is no longer needed */ + tac_free_attrib(&attr); + + if(retval < 0) { + _pam_log (LOG_WARNING, "%s: send %s accounting failed (task %hu)", + __FUNCTION__, + (type == TAC_PLUS_ACCT_FLAG_START) ? "start" : "stop", + task_id); + status = -1; + goto ErrExit; + } + + if(tac_account_read(tac_fd) != NULL) { + _pam_log (LOG_WARNING, "%s: accounting %s failed (task %hu)", + __FUNCTION__, + (type == TAC_PLUS_ACCT_FLAG_START) ? "start" : "stop", + task_id); + status = -1; + goto ErrExit; + } + + status = 0; + +ErrExit: + close(tac_fd); + return status; +} + +int _pam_account(pam_handle_t *pamh, int argc, const char **argv, int type) { + int retval; + static int ctrl; +#if (defined(__linux__) || defined(__NetBSD__)) + char *user = NULL; +#else + const char *user = NULL; +#endif + char *tty = NULL; + char *typemsg; + int status = PAM_SESSION_ERR; + + typemsg = (type == TAC_PLUS_ACCT_FLAG_START) ? "START" : "STOP"; + ctrl = _pam_parse (argc, argv); + + if (ctrl & PAM_TAC_DEBUG) + syslog (LOG_DEBUG, "%s: [%s] called (pam_tacplus v%hu.%hu.%hu)" + , __FUNCTION__, typemsg, PAM_TAC_VMAJ, PAM_TAC_VMIN, PAM_TAC_VPAT); + if (ctrl & PAM_TAC_DEBUG) + syslog(LOG_DEBUG, "%s: tac_srv_no=%d", __FUNCTION__, tac_srv_no); + +#if (defined(__linux__) || defined(__NetBSD__)) + retval = pam_get_item(pamh, PAM_USER, (const void **) (const void*) &user); +#else + retval = pam_get_item(pamh, PAM_USER, (void **) (void*) &user); +#endif + if(retval != PAM_SUCCESS || user == NULL || *user == '\0') { + _pam_log(LOG_ERR, "%s: unable to obtain username", __FUNCTION__); + return PAM_SESSION_ERR; + } + + if (ctrl & PAM_TAC_DEBUG) + syslog(LOG_DEBUG, "%s: username [%s] obtained", __FUNCTION__, user); + + tty = _pam_get_terminal(pamh); + + if(!strncmp(tty, "/dev/", 5)) + tty += 5; + + if (ctrl & PAM_TAC_DEBUG) + syslog(LOG_DEBUG, "%s: tty [%s] obtained", __FUNCTION__, tty); + + /* checks for specific data required by TACACS+, which should + be supplied in command line */ + if(tac_service == NULL || *tac_service == '\0') { + _pam_log (LOG_ERR, "TACACS+ service type not configured"); + return PAM_AUTH_ERR; + } + if(tac_protocol == NULL || *tac_protocol == '\0') { + _pam_log (LOG_ERR, "TACACS+ protocol type not configured"); + return PAM_AUTH_ERR; + } + + /* when this module is called from within pppd or other + application dealing with serial lines, it is likely + that we will get hit with signal caused by modem hangup; + this is important only for STOP packets, it's relatively + rare that modem hangs up on accounting start */ + if(type == TAC_PLUS_ACCT_FLAG_STOP) { + signal(SIGALRM, SIG_IGN); + signal(SIGCHLD, SIG_IGN); + signal(SIGHUP, SIG_IGN); + } + + if(!(ctrl & PAM_TAC_ACCT)) { + /* normal mode, send packet to the first available server */ + int tac_fd; + + status = PAM_SUCCESS; + + tac_fd = tac_connect(tac_srv, tac_srv_no); + if(tac_fd < 0) { + _pam_log(LOG_ERR, "%s: error sending %s - no servers", + __FUNCTION__, typemsg); + status = PAM_SESSION_ERR; + } + if (ctrl & PAM_TAC_DEBUG) + syslog(LOG_DEBUG, "%s: connected with fd=%d", __FUNCTION__, tac_fd); + + retval = _pam_send_account(tac_fd, type, user, tty); + if(retval < 0) { + _pam_log(LOG_ERR, "%s: error sending %s", + __FUNCTION__, typemsg); + status = PAM_SESSION_ERR; + } + + close(tac_fd); + + if (ctrl & PAM_TAC_DEBUG) { + syslog(LOG_DEBUG, "%s: [%s] for [%s] sent", + __FUNCTION__, typemsg,user); + } + } else { + /* send packet to all servers specified */ + int srv_i; + + status = PAM_SESSION_ERR; + + for(srv_i = 0; srv_i < tac_srv_no; srv_i++) { + int tac_fd; + + tac_fd = tac_connect_single(tac_srv[srv_i]); + if(tac_fd < 0) { + _pam_log(LOG_WARNING, "%s: error sending %s (fd)", + __FUNCTION__, typemsg); + continue; + } + + if (ctrl & PAM_TAC_DEBUG) + syslog(LOG_DEBUG, "%s: connected with fd=%d (srv %d)", __FUNCTION__, tac_fd, srv_i); + + retval = _pam_send_account(tac_fd, type, user, tty); + /* return code from function in this mode is + status of the last server we tried to send + packet to */ + if(retval < 0) { + _pam_log(LOG_WARNING, "%s: error sending %s (acct)", + __FUNCTION__, typemsg); + } else { + status = PAM_SUCCESS; + if (ctrl & PAM_TAC_DEBUG) + syslog(LOG_DEBUG, "%s: [%s] for [%s] sent", + __FUNCTION__, typemsg,user); + } + close(tac_fd); + } + } /* acct mode */ + + if(type == TAC_PLUS_ACCT_FLAG_STOP) { + signal(SIGALRM, SIG_DFL); + signal(SIGCHLD, SIG_DFL); + signal(SIGHUP, SIG_DFL); + } + + return status; +} + + +/* Main PAM functions */ + +/* authenticates user on remote TACACS+ server + * returns PAM_SUCCESS if the supplied username and password + * pair is valid + */ +PAM_EXTERN +int pam_sm_authenticate (pam_handle_t * pamh, int flags, + int argc, const char **argv) { + int ctrl, retval; +#if (defined(__linux__) || defined(__NetBSD__)) + const char *user; +#else + char *user; +#endif + char *pass; + char *tty; + int srv_i; + int tac_fd; + int status = PAM_AUTH_ERR; + + user = pass = tty = NULL; + + ctrl = _pam_parse (argc, argv); + + if (ctrl & PAM_TAC_DEBUG) + syslog (LOG_DEBUG, "%s: called (pam_tacplus v%hu.%hu.%hu)" + , __FUNCTION__, PAM_TAC_VMAJ, PAM_TAC_VMIN, PAM_TAC_VPAT); + + retval = pam_get_user (pamh, &user, "Username: "); + if (retval != PAM_SUCCESS || user == NULL || *user == '\0') { + _pam_log (LOG_ERR, "unable to obtain username"); + return PAM_USER_UNKNOWN; + } + + if (ctrl & PAM_TAC_DEBUG) + syslog (LOG_DEBUG, "%s: user [%s] obtained", __FUNCTION__, user); + + /* uwzgledniac PAM_DISALLOW_NULL_AUTHTOK */ + + retval = tacacs_get_password (pamh, flags, ctrl, &pass); + if (retval != PAM_SUCCESS || pass == NULL || *pass == '\0') { + _pam_log (LOG_ERR, "unable to obtain password"); + return PAM_CRED_INSUFFICIENT; + } + + retval = pam_set_item (pamh, PAM_AUTHTOK, pass); + if (retval != PAM_SUCCESS) { + _pam_log (LOG_ERR, "unable to set password"); + return PAM_CRED_INSUFFICIENT; + } + + if (ctrl & PAM_TAC_DEBUG) + syslog (LOG_DEBUG, "%s: password obtained", __FUNCTION__); + + tty = _pam_get_terminal(pamh); + + if (!strncmp (tty, "/dev/", 5)) + tty += 5; + + if (ctrl & PAM_TAC_DEBUG) + syslog (LOG_DEBUG, "%s: tty [%s] obtained", __FUNCTION__, tty); + + for (srv_i = 0; srv_i < tac_srv_no; srv_i++) { + int msg = TAC_PLUS_AUTHEN_STATUS_FAIL; + if (ctrl & PAM_TAC_DEBUG) + syslog (LOG_DEBUG, "%s: trying srv %d", __FUNCTION__, srv_i ); + + tac_fd = tac_connect_single(tac_srv[srv_i]); + if (tac_fd < 0) { + _pam_log (LOG_ERR, "connection failed srv %d: %m", srv_i); + if (srv_i == tac_srv_no-1) { + _pam_log (LOG_ERR, "no more servers to connect"); + return PAM_AUTHINFO_UNAVAIL; + } + } + if (tac_authen_send (tac_fd, user, pass, tty) < 0) { + _pam_log (LOG_ERR, "error sending auth req to TACACS+ server"); + status = PAM_AUTHINFO_UNAVAIL; + } else { + msg = tac_authen_read (tac_fd); + if (msg == TAC_PLUS_AUTHEN_STATUS_GETPASS) { + if (ctrl & PAM_TAC_DEBUG) + syslog (LOG_DEBUG, "%s: tac_cont_send called", __FUNCTION__); + if (tac_cont_send (tac_fd, pass) < 0) { + _pam_log (LOG_ERR, "error sending continue req to TACACS+ server"); + status = PAM_AUTHINFO_UNAVAIL; + } else { + msg = tac_authen_read (tac_fd); + if (msg != TAC_PLUS_AUTHEN_STATUS_PASS) { + _pam_log (LOG_ERR, "auth failed: %d", msg); + status = PAM_AUTH_ERR; + } else { + /* OK, we got authenticated; save the server that + accepted us for pam_sm_acct_mgmt and exit the loop */ + status = PAM_SUCCESS; + active_server = tac_srv[srv_i]; + close(tac_fd); + break; + } + } + } else if (msg != TAC_PLUS_AUTHEN_STATUS_PASS) { + _pam_log (LOG_ERR, "auth failed: %d", msg); + status = PAM_AUTH_ERR; + } else { + /* OK, we got authenticated; save the server that + accepted us for pam_sm_acct_mgmt and exit the loop */ + status = PAM_SUCCESS; + active_server = tac_srv[srv_i]; + close(tac_fd); + break; + } + } + close(tac_fd); + /* if we are here, this means that authentication failed + on current server; break if we are not allowed to probe + another one, continue otherwise */ + if (!(ctrl & PAM_TAC_FIRSTHIT)) + break; + } + + if (ctrl & PAM_TAC_DEBUG) + syslog (LOG_DEBUG, "%s: exit with pam status: %i", __FUNCTION__, status); + + bzero (pass, strlen (pass)); + free(pass); + pass = NULL; + + return status; +} /* pam_sm_authenticate */ + +/* no-op function to satisfy PAM authentication module */ +PAM_EXTERN +int pam_sm_setcred (pam_handle_t * pamh, int flags, + int argc, const char **argv) { + int ctrl = _pam_parse (argc, argv); + + if (ctrl & PAM_TAC_DEBUG) + syslog (LOG_DEBUG, "%s: called (pam_tacplus v%hu.%hu.%hu)" + , __FUNCTION__, PAM_TAC_VMAJ, PAM_TAC_VMIN, PAM_TAC_VPAT); + + return PAM_SUCCESS; +} /* pam_sm_setcred */ + +/* authorizes user on remote TACACS+ server, i.e. checks + * his permission to access requested service + * returns PAM_SUCCESS if the service is allowed + */ +PAM_EXTERN +int pam_sm_acct_mgmt (pam_handle_t * pamh, int flags, + int argc, const char **argv) { + int retval, ctrl, status=PAM_AUTH_ERR; +#if (defined(__linux__) || defined(__NetBSD__)) + const char *user; +#else + char *user; +#endif + char *tty; + struct areply arep; + struct tac_attrib *attr = NULL; + int tac_fd; + + user = tty = NULL; + + /* this also obtains service name for authorization + this should be normally performed by pam_get_item(PAM_SERVICE) + but since PAM service names are incompatible TACACS+ + we have to pass it via command line argument until a better + solution is found ;) */ + ctrl = _pam_parse (argc, argv); + + if (ctrl & PAM_TAC_DEBUG) { + syslog (LOG_DEBUG, "%s: called (pam_tacplus v%hu.%hu.%hu)" + , __FUNCTION__, PAM_TAC_VMAJ, PAM_TAC_VMIN, PAM_TAC_VPAT); + syslog (LOG_DEBUG, "%s: active server is [%s]", __FUNCTION__, + tac_ntop(active_server->ai_addr, active_server->ai_addrlen)); + } + +#if (defined(__linux__) || defined(__NetBSD__)) + retval = pam_get_item(pamh, PAM_USER, (const void **) (const void*) &user); +#else + retval = pam_get_item(pamh, PAM_USER, (void **) (void*) &user); +#endif + if (retval != PAM_SUCCESS || user == NULL || *user == '\0') { + _pam_log (LOG_ERR, "unable to obtain username"); + return PAM_USER_UNKNOWN; + } + + if (ctrl & PAM_TAC_DEBUG) + syslog(LOG_DEBUG, "%s: username obtained [%s]", __FUNCTION__, user); + + tty = _pam_get_terminal(pamh); + + if(!strncmp(tty, "/dev/", 5)) + tty += 5; + + if (ctrl & PAM_TAC_DEBUG) + syslog(LOG_DEBUG, "%s: tty obtained [%s]", __FUNCTION__, tty); + + /* checks if user has been successfully authenticated + by TACACS+; we cannot solely authorize user if it hasn't + been authenticated or has been authenticated by method other + than TACACS+ */ + if(!active_server) { + _pam_log (LOG_ERR, "user not authenticated by TACACS+"); + return PAM_AUTH_ERR; + } + + /* checks for specific data required by TACACS+, which should + be supplied in command line */ + if(tac_service == NULL || *tac_service == '\0') { + _pam_log (LOG_ERR, "TACACS+ service type not configured"); + return PAM_AUTH_ERR; + } + if(tac_protocol == NULL || *tac_protocol == '\0') { + _pam_log (LOG_ERR, "TACACS+ protocol type not configured"); + return PAM_AUTH_ERR; + } + + tac_add_attrib(&attr, "service", tac_service); + tac_add_attrib(&attr, "protocol", tac_protocol); + + tac_fd = tac_connect_single(active_server); + if(tac_fd < 0) { + _pam_log (LOG_ERR, "TACACS+ server unavailable"); + status = PAM_AUTH_ERR; + goto ErrExit; + } + + retval = tac_author_send(tac_fd, user, tty, attr); + + tac_free_attrib(&attr); + + if(retval < 0) { + _pam_log (LOG_ERR, "error getting authorization"); + status = PAM_AUTH_ERR; + goto ErrExit; + } + + if (ctrl & PAM_TAC_DEBUG) + syslog(LOG_DEBUG, "%s: sent authorization request", __FUNCTION__); + + tac_author_read(tac_fd, &arep); + + if(arep.status != AUTHOR_STATUS_PASS_ADD && + arep.status != AUTHOR_STATUS_PASS_REPL) { + _pam_log (LOG_ERR, "TACACS+ authorisation failed for [%s]", user); + status = PAM_PERM_DENIED; + goto ErrExit; + } + + if (ctrl & PAM_TAC_DEBUG) + syslog(LOG_DEBUG, "%s: user [%s] successfully authorized", __FUNCTION__, user); + + status = PAM_SUCCESS; + + attr = arep.attr; + while (attr != NULL) { + char attribute[attr->attr_len]; + char value[attr->attr_len]; + char *sep; + + sep = index(attr->attr, '='); + if(sep == NULL) + sep = index(attr->attr, '*'); + if(sep != NULL) { + bcopy(attr->attr, attribute, attr->attr_len-strlen(sep)); + attribute[attr->attr_len-strlen(sep)] = '\0'; + bcopy(sep, value, strlen(sep)); + value[strlen(sep)] = '\0'; + + size_t i; + for (i = 0; attribute[i] != '\0'; i++) { + attribute[i] = toupper(attribute[i]); + if (attribute[i] == '-') + attribute[i] = '_'; + } + + if (ctrl & PAM_TAC_DEBUG) + syslog(LOG_DEBUG, "%s: returned attribute `%s%s' from server", __FUNCTION__, attribute, value); + + /* set PAM_RHOST if 'addr' attribute was returned from server */ + if(!strncmp(attribute, "addr", 4) && isdigit((int)*value)) { + retval = pam_set_item(pamh, PAM_RHOST, value); + if (retval != PAM_SUCCESS) + syslog(LOG_WARNING, "%s: unable to set remote address for PAM", __FUNCTION__); + else if(ctrl & PAM_TAC_DEBUG) + syslog(LOG_DEBUG, "%s: set remote addr to `%s'", __FUNCTION__, value); + } + + /* make returned attributes available for other PAM modules via PAM environment */ + if (pam_putenv(pamh, strncat(attribute, value, strlen(value))) != PAM_SUCCESS) + syslog(LOG_WARNING, "%s: unable to set PAM environment", __FUNCTION__); + + } else { + syslog(LOG_WARNING, "%s: invalid attribute `%s', no separator", __FUNCTION__, attr->attr); + } + attr = attr->next; + } + + /* free returned attributes */ + if(arep.attr != NULL) tac_free_attrib(&arep.attr); + +ErrExit: + close(tac_fd); + return status; +} /* pam_sm_acct_mgmt */ + +/* sends START accounting request to the remote TACACS+ server + * returns PAM error only if the request was refused or there + * were problems connection to the server + */ +/* accounting packets may be directed to any TACACS+ server, + * independent from those used for authentication and authorization; + * it may be also directed to all specified servers + */ +PAM_EXTERN +int pam_sm_open_session (pam_handle_t * pamh, int flags, + int argc, const char **argv) { + task_id=(short int) magic(); + + return(_pam_account(pamh, argc, argv,TAC_PLUS_ACCT_FLAG_START)); +} /* pam_sm_open_session */ + +/* sends STOP accounting request to the remote TACACS+ server + * returns PAM error only if the request was refused or there + * were problems connection to the server + */ +PAM_EXTERN +int pam_sm_close_session (pam_handle_t * pamh, int flags, + int argc, const char **argv) { + return(_pam_account(pamh, argc, argv,TAC_PLUS_ACCT_FLAG_STOP)); +} /* pam_sm_close_session */ + + +#ifdef PAM_SM_PASSWORD +/* no-op function for future use */ +PAM_EXTERN +int pam_sm_chauthtok (pam_handle_t * pamh, int flags, + int argc, const char **argv) { + int ctrl = _pam_parse (argc, argv); + + if (ctrl & PAM_TAC_DEBUG) + syslog (LOG_DEBUG, "%s: called (pam_tacplus v%hu.%hu.%hu)" + , __FUNCTION__, PAM_TAC_VMAJ, PAM_TAC_VMIN, PAM_TAC_VPAT); + + return PAM_SUCCESS; +} /* pam_sm_chauthtok */ +#endif + + +#ifdef PAM_STATIC +struct pam_module _pam_tacplus_modstruct +{ + "pam_tacplus", + pam_sm_authenticate, + pam_sm_setcred, + pam_sm_acct_mgmt, + pam_sm_open_session, + pam_sm_close_session, +#ifdef PAM_SM_PASSWORD + pam_sm_chauthtok +#else + NULL +#endif +}; +#endif + diff --git a/pam_tacplus.h b/pam_tacplus.h new file mode 100644 index 0000000..87158a8 --- /dev/null +++ b/pam_tacplus.h @@ -0,0 +1,38 @@ +/* pam_tacplus.h + * + * Copyright (C) 2010, Pawel Krawczyk and + * Jeroen Nijhof + * + * 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 - see the file COPYING. + * + * See `CHANGES' file for revision history. + */ + +/* pam_tacplus command line options */ +#define PAM_TAC_DEBUG 01 +#define PAM_TAC_ENCRYPT 02 +#define PAM_TAC_FIRSTHIT 04 +#define PAM_TAC_ACCT 10 /* account on all specified servers */ + +/* how many TACPLUS+ servers can be defined */ +#define TAC_MAX_SERVERS 4 + +/* pam_tacplus major, minor and patchlevel version numbers */ +#define PAM_TAC_VMAJ 1 +#define PAM_TAC_VMIN 3 +#define PAM_TAC_VPAT 2 + +#ifndef PAM_EXTERN + #define PAM_EXTERN extern +#endif diff --git a/pam_tacplus.spec.in b/pam_tacplus.spec.in new file mode 100644 index 0000000..3995537 --- /dev/null +++ b/pam_tacplus.spec.in @@ -0,0 +1,63 @@ +# +# spec file for package 'name' (version 'v') +# +# The following software is released as specified below. +# This spec file is released to the public domain. +# (c) Lincom Software Team + +# Basic Information +Name: pam_tacplus +Version: @VERSION@ +Release: 1%{?dist} +Summary: PAM Tacacs+ module +Group: System +License: GPL +URL: http://tacplus.sourceforge.net/ + +# Packager Information +Packager: NRB + +# Build Information +BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n) + +# Source Information +Source0: http://downloads.sourceforge.net/project/tacplus/pam_tacplus/pam_tacplus-@VERSION@.tar.gz + +# Dependency Information +BuildRequires: gcc binutils pam-devel +Requires: pam + +%description +PAM Tacacs+ module based on code produced by Pawel Krawczyk and Jeroen Nijhof + +%prep +%setup -q -a 0 + +%build +./configure +make + +%install +[ "$RPM_BUILD_ROOT" != "/" ] && rm -rf $RPM_BUILD_ROOT +mkdir -p $RPM_BUILD_ROOT/{etc/pam.d,lib/security} + +install -m 755 pam_tacplus.so \ + $RPM_BUILD_ROOT/lib/security/ + +install -m 644 sample.pam $RPM_BUILD_ROOT/etc/pam.d/tacacs + +chmod 755 $RPM_BUILD_ROOT/lib/security/*.so* + +%clean +[ "$RPM_BUILD_ROOT" != "/" ] && rm -rf $RPM_BUILD_ROOT + +%files +%defattr(-,root,root) +%attr(0755,root,root) /lib/security/*.so* +%attr(0644,root,root) %config(noreplace) /etc/pam.d/tacacs +%doc AUTHORS COPYING README ChangeLog + +%changelog +* Mon Mar 17 2010 beNDon 1.3.1r +- Autotools aware +- spec file added for RPM building diff --git a/sample.pam b/sample.pam new file mode 100644 index 0000000..9d2114c --- /dev/null +++ b/sample.pam @@ -0,0 +1,4 @@ +#%PAM-1.0 +auth required /lib/security/pam_tacplus.so debug server=tac1.foo.net server=tac2.baz.net timeout=5 secret=SECRET-1234 encrypt login=pap prompt=Enter_password: first_hit +account required /lib/security/pam_tacplus.so debug server=tac1.foo.net timeout=5 secret=SECRET-1234 encrypt service=ppp protocol=lcp +session required /lib/security/pam_tacplus.so debug server=tac1.foo.net timeout=5 secret=SECRET-1234 encrypt service=ppp protocol=lcp diff --git a/support.c b/support.c new file mode 100644 index 0000000..ec98987 --- /dev/null +++ b/support.c @@ -0,0 +1,229 @@ +/* support.c - support functions for pam_tacplus.c + * + * Copyright (C) 2010, Pawel Krawczyk and + * Jeroen Nijhof + * + * 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 - see the file COPYING. + * + * See `CHANGES' file for revision history. + */ + +#define PAM_SM_AUTH +#define PAM_SM_ACCOUNT +#define PAM_SM_SESSION +/* #define PAM_SM_PASSWORD */ + +#ifndef __linux__ + #include +#endif +#include + +#include "pam_tacplus.h" +#include "tacplus.h" +#include "libtac.h" + +struct addrinfo *tac_srv[TAC_MAX_SERVERS]; +int tac_srv_no = 0; +char *tac_service = NULL; +char *tac_protocol = NULL; +char *tac_prompt = NULL; + +/* libtac */ +extern char *tac_secret; +extern char *tac_login; +extern int tac_encryption; +extern int tac_timeout; + +#ifndef xcalloc +void *_xcalloc (size_t size) { + register void *val = calloc (1, size); + if (val == 0) { + syslog (LOG_ERR, "xcalloc: calloc(1,%u) failed", (unsigned) size); + exit (1); + } + return val; +} +#else +#define _xcalloc xcalloc +#endif + +char *_pam_get_terminal(pam_handle_t *pamh) { + int retval; + char *tty; + + retval = pam_get_item (pamh, PAM_TTY, (void *)&tty); + if (retval != PAM_SUCCESS || tty == NULL || *tty == '\0') { + tty = ttyname(STDIN_FILENO); + if(tty == NULL || *tty == '\0') + tty = "unknown"; + } + return(tty); +} + +void _pam_log(int err, const char *format,...) { + char msg[256]; + va_list args; + + va_start(args, format); + vsnprintf(msg, sizeof(msg), format, args); + openlog("PAM-tacplus", LOG_PID, LOG_AUTH); + syslog(err, "%s", msg); + va_end(args); + closelog(); +} + + +/* stolen from pam_stress */ +int converse(pam_handle_t * pamh, int nargs + ,struct pam_message **message + ,struct pam_response **response) { + int retval; + struct pam_conv *conv; + + if ((retval = pam_get_item (pamh, PAM_CONV, (void *)&conv)) == PAM_SUCCESS) { +#if (defined(__linux__) || defined(__NetBSD__)) + retval = conv->conv (nargs, (const struct pam_message **) message +#else + retval = conv->conv (nargs, (struct pam_message **) message +#endif + ,response, conv->appdata_ptr); + if (retval != PAM_SUCCESS) { + _pam_log(LOG_ERR, "(pam_tacplus) converse returned %d", retval); + _pam_log(LOG_ERR, "that is: %s", pam_strerror (pamh, retval)); + } + } else { + _pam_log (LOG_ERR, "(pam_tacplus) converse failed to get pam_conv"); + } + + return retval; +} + +/* stolen from pam_stress */ +int tacacs_get_password (pam_handle_t * pamh, int flags + ,int ctrl, char **password) { + char *pass = NULL; + + struct pam_message msg[1], *pmsg[1]; + struct pam_response *resp; + int retval; + + if (ctrl & PAM_TAC_DEBUG) + syslog (LOG_DEBUG, "%s: called", __FUNCTION__); + + /* set up conversation call */ + pmsg[0] = &msg[0]; + msg[0].msg_style = PAM_PROMPT_ECHO_OFF; + + if (!tac_prompt) { + msg[0].msg = "Password: "; + } else { + msg[0].msg = tac_prompt; + } + resp = NULL; + + if ((retval = converse (pamh, 1, pmsg, &resp)) != PAM_SUCCESS) + return retval; + + if (resp) { + if ((resp[0].resp == NULL) && (ctrl & PAM_TAC_DEBUG)) + _pam_log (LOG_DEBUG, "pam_sm_authenticate: NULL authtok given"); + pass = resp[0].resp; /* remember this! */ + resp[0].resp = NULL; + } else if (ctrl & PAM_TAC_DEBUG) { + _pam_log (LOG_DEBUG, "pam_sm_authenticate: no error reported"); + _pam_log (LOG_DEBUG, "getting password, but NULL returned!?"); + return PAM_CONV_ERR; + } + + free(resp); + resp = NULL; + + *password = pass; /* this *MUST* be free()'d by this module */ + + if(ctrl & PAM_TAC_DEBUG) + syslog(LOG_DEBUG, "%s: obtained password", __FUNCTION__); + + return PAM_SUCCESS; +} + +int _pam_parse (int argc, const char **argv) { + int ctrl = 0; + + /* otherwise the list will grow with each call */ + tac_srv_no = 0; + + for (ctrl = 0; argc-- > 0; ++argv) { + if (!strcmp (*argv, "debug")) { /* all */ + ctrl |= PAM_TAC_DEBUG; + } else if (!strcmp (*argv, "encrypt")) { + ctrl |= PAM_TAC_ENCRYPT; + tac_encryption = 1; + } else if (!strcmp (*argv, "first_hit")) { /* authentication */ + ctrl |= PAM_TAC_FIRSTHIT; + } else if (!strncmp (*argv, "service=", 8)) { /* author & acct */ + tac_service = (char *) _xcalloc (strlen (*argv + 8) + 1); + strcpy (tac_service, *argv + 8); + } else if (!strncmp (*argv, "protocol=", 9)) { /* author & acct */ + tac_protocol = (char *) _xcalloc (strlen (*argv + 9) + 1); + strcpy (tac_protocol, *argv + 9); + } else if (!strncmp (*argv, "prompt=", 7)) { /* authentication */ + tac_prompt = (char *) _xcalloc (strlen (*argv + 7) + 1); + strcpy (tac_prompt, *argv + 7); + // Replace _ with space + int chr; + for (chr = 0; chr < strlen(tac_prompt); chr++) { + if (tac_prompt[chr] == '_') { + tac_prompt[chr] = ' '; + } + } + } else if (!strcmp (*argv, "acct_all")) { + ctrl |= PAM_TAC_ACCT; + } else if (!strncmp (*argv, "server=", 7)) { /* authen & acct */ + if(tac_srv_no < TAC_MAX_SERVERS) { + struct addrinfo hints, *servers, *server; + int rv; + + memset(&hints, 0, sizeof hints); + hints.ai_family = AF_UNSPEC; // use IPv4 or IPv6, whichever + hints.ai_socktype = SOCK_STREAM; + if ((rv = getaddrinfo(*argv + 7, "49", &hints, &servers)) == 0) { + for(server = servers; server != NULL; server = server->ai_next) { + tac_srv[tac_srv_no] = server; + tac_srv_no++; + } + } else { + _pam_log (LOG_ERR, + "skip invalid server: %s (getaddrinfo: %s)", + *argv + 7, gai_strerror(rv)); + } + } else { + _pam_log(LOG_ERR, "maximum number of servers (%d) exceeded, skipping", + TAC_MAX_SERVERS); + } + } else if (!strncmp (*argv, "secret=", 7)) { + tac_secret = (char *) _xcalloc (strlen (*argv + 7) + 1); + strcpy (tac_secret, *argv + 7); + } else if (!strncmp (*argv, "timeout=", 8)) { + tac_timeout = atoi(*argv + 8); + } else if (!strncmp (*argv, "login=", 6)) { + tac_login = (char *) _xcalloc (strlen (*argv + 6) + 1); + strcpy (tac_login, *argv + 6); + } else { + _pam_log (LOG_WARNING, "unrecognized option: %s", *argv); + } + } + + return ctrl; +} /* _pam_parse */ + diff --git a/support.h b/support.h new file mode 100644 index 0000000..3635e9d --- /dev/null +++ b/support.h @@ -0,0 +1,37 @@ +/* support.h - support functions for pam_tacplus.c + * + * Copyright (C) 2010, Pawel Krawczyk and + * Jeroen Nijhof + * + * 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 - see the file COPYING. + * + * See `CHANGES' file for revision history. + */ + +#ifndef __linux__ + #include +#endif +#include + +/* support.c */ +extern int _pam_parse (int argc, const char **argv); +extern unsigned long _resolve_name (char *serv); +extern int tacacs_get_password (pam_handle_t * pamh, int flags + ,int ctrl, char **password); +extern int converse (pam_handle_t * pamh, int nargs + ,struct pam_message **message + ,struct pam_response **response); +extern void _pam_log (int err, const char *format,...); +extern void *_xcalloc (size_t size); +extern char *_pam_get_terminal(pam_handle_t *pamh); -- cgit v1.2.3