diff options
46 files changed, 4371 insertions, 0 deletions
@@ -0,0 +1,5 @@ +Primary Author: + Pawel Krawczyk <kravietz@ceti.com.pl> +Other Authors and Major Contributors: + Jeroen Nijhof <jeroen@nijhofnet.nl> + @@ -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. + + <one line to give the program's name and a brief idea of what it does.> + Copyright (C) 19yy <name of author> + + 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. + + <signature of Ty Coon>, 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 <benoit.donneaux@gmail.com>. +* Added pam_tacplus.spec file, thanks to Benoit Donneaux <benoit.donneaux@gmail.com>. +* 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 <pettai@nordu.net> +* Removed libdl for compiling causing failure on netbsd, reported by + Fredrik Pettai <pettai@nordu.net> +* hdr_check.c: forgot to include stdlib, reported by + Fredrik Pettai <pettai@nordu.net> +* Changed defines to add support for netbsd, fixed by + Jeroen Nijhof <jeroen@nijhofnet.nl> +* magic.c: read() can have a return value, fixed by + Jeroen Nijhof <jeroen@nijhofnet.nl> +* 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 <jeroen@nijhofnet.nl> + +1.2.10 + The following changes where made by Jeroen Nijhof <jeroen@nijhofnet.nl> +* 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 <volpe.mark@epamail.epa.gov> +* Fixed bug in passing the remote address, reported by + Jason Lambert <jlambert@lambert-comm.net> and + Yury Trembach <yt@sns.net.ua> +* Fixed bug in reception of authorization packet, reported by + <svg@disney.surnet.ru> + +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() @@ -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 <kravietz@ceti.pl> +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) + @@ -0,0 +1 @@ +Check 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 <kravietz@ceti.com.pl> +http://ceti.pl/~kravietz/prog.html + +Jeroen Nijhof <jeroen@nijhofnet.nl> diff --git a/config/.placeholder b/config/.placeholder new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/config/.placeholder 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 <jeroen@nijhofnet.nl> +dnl Benoit Donneaux <benoit.donneaux@gmail.com> +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 <jeroen@nijhofnet.nl>, 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 <jeroen@nijhofnet.nl> 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 <jeroen@nijhofnet.nl> +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 <kravietz@ceti.com.pl> + and Jeroen Nijhof <jeroen@nijhofnet.nl>. + + 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 <kravietz@ceti.pl> and + * Jeroen Nijhof <jeroen@nijhofnet.nl> + * + * 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 <kravietz@ceti.pl> and + * Jeroen Nijhof <jeroen@nijhofnet.nl> + * + * 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 <stdarg.h> +#include <stdlib.h> +#include <stdio.h> +#include <syslog.h> +#include <string.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <netdb.h> +#include <unistd.h> + +#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 <kravietz@ceti.pl> and + * Jeroen Nijhof <jeroen@nijhofnet.nl> + * + * 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 <sys/types.h> +#ifdef sun + #include "cdefs.h" +#else + #include <sys/cdefs.h> +#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; + /* <user_len bytes of char data> */ + /* <port_len bytes of char data> */ + /* <rem_addr_len bytes of u_char data> */ + /* <data_len bytes of u_char data> */ +}; + +#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 + + /* <user_msg_len bytes of u_char data> */ + /* <user_data_len bytes of u_char data> */ +}; + +#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; + + /* <msg_len bytes of char data> */ + /* <data_len bytes of u_char data> */ +}; + +#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 */ + /* <user_len bytes of char data> */ + /* <port_len bytes of char data> */ + /* <rem_addr_len bytes of u_char data> */ + /* 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 */ + + /* <arg_cnt u_chars containing the lengths of args 1 to arg n> */ + /* <user_len bytes of char data> */ + /* <port_len bytes of char data> */ + /* <rem_addr_len bytes of u_char data> */ + /* <char data for each arg> */ +}; + +#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 + + /* <arg_cnt u_chars containing the lengths of arg 1 to arg n> */ + /* <msg_len bytes of char data> */ + /* <data_len bytes of char data> */ + /* <char data for each arg> */ +}; + +#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 <kravietz@ceti.pl> and + * Jeroen Nijhof <jeroen@nijhofnet.nl> + * + * 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 <kravietz@ceti.pl> and + * Jeroen Nijhof <jeroen@nijhofnet.nl> + * + * 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 <kravietz@ceti.pl> and + * Jeroen Nijhof <jeroen@nijhofnet.nl> + * + * 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 <kravietz@ceti.pl> and + * Jeroen Nijhof <jeroen@nijhofnet.nl> + * + * 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 <kravietz@ceti.pl> and + * Jeroen Nijhof <jeroen@nijhofnet.nl> + * + * 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 <kravietz@ceti.pl> and + * Jeroen Nijhof <jeroen@nijhofnet.nl> + * + * 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 <kravietz@ceti.pl> and + * Jeroen Nijhof <jeroen@nijhofnet.nl> + * + * 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 <kravietz@ceti.pl> and + * Jeroen Nijhof <jeroen@nijhofnet.nl> + * + * 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 <signal.h> +#include <arpa/inet.h> +#include <time.h> +#include <fcntl.h> +#include <errno.h> + +#ifdef _AIX + #include <sys/socket.h> +#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 <jeroen@nijhofnet.nl> + * + * 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 <kravietz@ceti.pl> and + * Jeroen Nijhof <jeroen@nijhofnet.nl> + * + * 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; i<n; i++) { + /* MD5_1 = MD5{session_id, secret, version, seq_no} + MD5_2 = MD5{session_id, secret, version, seq_no, MD5_1} */ + + /* place session_id, key, version and seq_no in buffer */ + bp=0; + bcopy(&hdr->session_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<length; i++) { + *(buf+i) ^= pad[i]; + } + + free(pad); + + } else { + syslog(LOG_WARNING, "%s: using no TACACS+ encryption", __FUNCTION__); + } +} /* _tac_crypt */ diff --git a/libtac/lib/hdr_check.c b/libtac/lib/hdr_check.c new file mode 100644 index 0000000..048a5e6 --- /dev/null +++ b/libtac/lib/hdr_check.c @@ -0,0 +1,53 @@ +/* hdr_check.c - Perform basic sanity checks on received packet. + * + * Copyright (C) 2010, Pawel Krawczyk <kravietz@ceti.pl> and + * Jeroen Nijhof <jeroen@nijhofnet.nl> + * + * 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 <kravietz@ceti.pl> and + * Jeroen Nijhof <jeroen@nijhofnet.nl> + * + * 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 <stdio.h> +#include <stdlib.h> +#include <sys/types.h> +#include <sys/time.h> +#include <unistd.h> + +/* 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 <sys/stat.h> +#include <fcntl.h> + +/* 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 <string.h> +#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 <kravietz@ceti.pl> and + * Jeroen Nijhof <jeroen@nijhofnet.nl> + * + * 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 <kravietz@ceti.pl> and + * Jeroen Nijhof <jeroen@nijhofnet.nl> + * + * 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 <kravietz@ceti.pl> and + * Jeroen Nijhof <jeroen@nijhofnet.nl> + * + * 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 <kravietz@ceti.pl> and + * Jeroen Nijhof <jeroen@nijhofnet.nl> + * + * 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 <syslog.h> +#include <stdlib.h> + +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 <kravietz@ceti.pl> and + * Jeroen Nijhof <jeroen@nijhofnet.nl> + * + * 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 <kravietz@ceti.pl> and + * Jeroen Nijhof <jeroen@nijhofnet.nl> + * + * 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 <stdlib.h> /* malloc */ +#include <stdio.h> +#include <syslog.h> +#include <netdb.h> /* gethostbyname */ +#include <sys/socket.h> /* in_addr */ +#include <netinet/in.h> +#include <arpa/inet.h> +#include <stdarg.h> /* va_ */ +#include <signal.h> +#include <string.h> /* strdup */ +#include <ctype.h> +#include <time.h> +#include <unistd.h> + +#ifndef __linux__ + #include <strings.h> +#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 <security/pam_appl.h> +#endif +#include <security/pam_modules.h> + +#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 <kravietz@ceti.pl> and + * Jeroen Nijhof <jeroen@nijhofnet.nl> + * + * 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 <kravietz@ceti.com.pl> and Jeroen Nijhof <jeroen@nijhofnet.nl> + +%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 <benoit.donneaux@gmail.com> 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 <kravietz@ceti.pl> and + * Jeroen Nijhof <jeroen@nijhofnet.nl> + * + * 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 <security/pam_appl.h> +#endif +#include <security/pam_modules.h> + +#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 <kravietz@ceti.pl> and + * Jeroen Nijhof <jeroen@nijhofnet.nl> + * + * 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 <security/pam_appl.h> +#endif +#include <security/pam_modules.h> + +/* 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); |