summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--AUTHORS5
-rw-r--r--COPYING341
-rw-r--r--ChangeLog99
-rw-r--r--INSTALL18
-rw-r--r--Makefile.am60
-rw-r--r--NEWS1
-rw-r--r--README210
-rw-r--r--config/.placeholder0
-rw-r--r--configure.ac63
-rw-r--r--debian/README.Debian18
-rw-r--r--debian/changelog6
-rw-r--r--debian/control16
-rw-r--r--debian/copyright27
-rw-r--r--debian/libpam-tacplus.dirs1
-rw-r--r--debian/libpam-tacplus.docs2
-rwxr-xr-xdebian/rules93
-rw-r--r--libtac/include/cdefs.h58
-rw-r--r--libtac/include/libtac.h83
-rw-r--r--libtac/include/tacplus.h265
-rw-r--r--libtac/lib/acct_r.c97
-rw-r--r--libtac/lib/acct_s.c151
-rw-r--r--libtac/lib/attrib.c80
-rw-r--r--libtac/lib/authen_r.c104
-rw-r--r--libtac/lib/authen_s.c143
-rw-r--r--libtac/lib/author_r.c194
-rw-r--r--libtac/lib/author_s.c152
-rw-r--r--libtac/lib/connect.c144
-rw-r--r--libtac/lib/cont_s.c92
-rw-r--r--libtac/lib/crypt.c107
-rw-r--r--libtac/lib/hdr_check.c53
-rw-r--r--libtac/lib/header.c65
-rw-r--r--libtac/lib/magic.c122
-rw-r--r--libtac/lib/magic.h26
-rw-r--r--libtac/lib/md5.c276
-rw-r--r--libtac/lib/md5.h41
-rw-r--r--libtac/lib/messages.c26
-rw-r--r--libtac/lib/messages.h26
-rw-r--r--libtac/lib/version.c24
-rw-r--r--libtac/lib/xalloc.c43
-rw-r--r--libtac/lib/xalloc.h23
-rw-r--r--pam_tacplus.c645
-rw-r--r--pam_tacplus.h38
-rw-r--r--pam_tacplus.spec.in63
-rw-r--r--sample.pam4
-rw-r--r--support.c229
-rw-r--r--support.h37
46 files changed, 4371 insertions, 0 deletions
diff --git a/AUTHORS b/AUTHORS
new file mode 100644
index 0000000..0737c5d
--- /dev/null
+++ b/AUTHORS
@@ -0,0 +1,5 @@
+Primary Author:
+ Pawel Krawczyk <kravietz@ceti.com.pl>
+Other Authors and Major Contributors:
+ Jeroen Nijhof <jeroen@nijhofnet.nl>
+
diff --git a/COPYING b/COPYING
new file mode 100644
index 0000000..86cf81a
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,341 @@
+
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 675 Mass Ave, Cambridge, MA 02139, USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ Appendix: How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <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()
diff --git a/INSTALL b/INSTALL
new file mode 100644
index 0000000..e745395
--- /dev/null
+++ b/INSTALL
@@ -0,0 +1,18 @@
+
+pam_tacplus consists of the module code and `libtac' simple TACPLUS+ library.
+
+Simple `make' should compile both TACACS+ library and pam_tacplus module.
+If not, you could try to edit the following files:
+
+./Makefile
+./libtac/lib/Makefile
+
+You should get a `pam_tacplus.so' module, which should be placed in
+/lib/security. Modify `sample.pam' and install it in /etc/pam.d under
+name of the proper service.
+
+This code is known to work on Linux, Solaris and AIX for now.
+
+Jan 02 2000
+Pawel Krawczyk <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)
+
diff --git a/NEWS b/NEWS
new file mode 100644
index 0000000..73e0f18
--- /dev/null
+++ b/NEWS
@@ -0,0 +1 @@
+Check README
diff --git a/README b/README
new file mode 100644
index 0000000..724d1f0
--- /dev/null
+++ b/README
@@ -0,0 +1,210 @@
+
+pam_tacplus v1.3.2
+Jun 07 2010
+
+This PAM module support the following functions:
+
+ * authentication
+ * authorization (account management)
+ * accounting (session management)
+
+All are performed using TACACS+ protocol [1], designed by Cisco Systems.
+This is remote AAA protocol, supported by most Cisco hardware.
+A free TACACS+ server is available [2], which I'm using without any
+major problems for about a year. Advantages of TACACS+ is that all
+(unlike RADIUS) packets exchanged with the authentication server are
+encrypted. This module is an attempt to provide most useful part of
+TACACS+ functionality to applications using the PAM interface on Linux.
+
+
+Recognized options:
+~~~~~~~~~~~~~~~~~~~
+
+Option Management group Description
+--------------- ----------------------- ----------------------------------
+debug ALL output debugging information via
+ syslog(3); note, that the debugging
+ is heavy, including passwords!
+
+encrypt ALL encrypt TACACS+ packets; you should
+ use this always in normal operations
+
+secret=STRING ALL secret key used to encrypt/decrypt
+ packets sent/received from the server
+
+server=HOSTNAME auth, session can be specified more than once;
+server=IP_ADDR adds a TACACS+ server to the servers
+ list
+
+timeout=INT ALL connection timeout in seconds
+ default is 5 seconds
+
+first_hit auth if multiple servers are supplied,
+ pam_tacplus will try to authenticate
+ the user on all servers until it
+ succeds or all servers are queried
+
+login=STRING auth TACACS+ authentication service,
+ this can be "pap", "chap" or "login"
+ at the moment. Default is pap.
+
+prompt=STRING auth Custom password prompt. If you want
+ to use a space use '_' character
+ instead.
+
+acct_all session if multiple servers are supplied,
+ pam_tacplus will send accounting
+ start/stop packets to all servers
+ on the list
+
+service account, session TACACS+ service for authorization
+ and accounting
+
+protocol account, session TACACS+ protocol for authorization
+ and accounting
+
+The last two items are widely described in TACACS+ draft [1]. They are
+required by the server, but it will work if they don't match the real
+service authorized :)
+During PAM account the AV pairs returned by the TACACS+ servers are made available to the
+PAM environment, so you can use i.e. pam_exec.so to do something with these AV pairs.
+
+Example configuration:
+~~~~~~~~~~~~~~~~~~~~~~
+
+#%PAM-1.0
+auth required /lib/security/pam_tacplus.so debug server=123.123.123.123 secret=SECRET-123 encrypt
+account required /lib/security/pam_tacplus.so debug secret=SECRET-123 encrypt service=ppp protocol=lcp
+account sufficient /lib/security/pam_exec.so /usr/local/bin/showenv.sh
+password required /lib/security/pam_cracklib.so
+password required /lib/security/pam_pwdb.so shadow use_authtok
+session required /lib/security/pam_tacplus.so debug server=123.123.123.123 server=124.124.124.124 secret=SECRET-124 encrypt service=ppp protocol=lcp
+
+(note that above are five long lines)
+
+More on server lists:
+~~~~~~~~~~~~~~~~~~~~~
+
+1. Having more that one TACACS+ server defined for given management group
+has following effects on authentication:
+
+ * always, if the first server on the list is unreachable or failing
+ pam_tacplus will try to authenticate user on the another one
+
+ in case, where there are no modifiers like `first_hit', you
+ could think the of the first server on list as primary one,
+ and the others as backup/secondary servers
+
+ * if the `first_hit' option is specified, if the first server
+ on the list will return negative authentication reply, pam_tacplus
+ will try to ask another server; this will continue until it
+ will get positive reply from one of the servers, or all servers
+ are probed with negative replies; then pam_tacplus will return
+ with authentication failure
+
+ this is useful if you have e.g. dialup server authenticating
+ users who have accounts on multiple servers in your network;
+ in this case, from host B won't be authenticated by TACACS+ server
+ on host A (which is first on the list), but it will be authenticated
+ when pam_tacplus will query the server on host B next
+
+ in this case all the servers can be considered as having equal
+ authority in authenticating users
+
+ * when the authentication function gets a positive reply from
+ a server, it saves its address for future use by account
+ management function (see below)
+
+2. The account management (authorization) function asks *only one*
+TACACS+ server and it ignores the whole server list passed from command
+line. It uses server saved by authentication function after successful
+authenticating user on that server. We assume that the server is
+authoriative for queries about that user.
+
+3. The session management (accounting) functions obtain their server lists
+independently from the other functions. This allows you to account user
+sessions on different servers than those used for authentication and
+authorization.
+
+ * normally, without the `acct_all' modifier, the extra servers
+ on the list will be considered as backup servers, mostly like
+ in point 1. i.e. they will be used only if the first server
+ on the list will fail to accept our accounting packets
+
+ * with `acct_all' pam_tacplus will try to deliver the accounting
+ packets to all servers on the list; failure of one of the servers
+ will make it try another one
+
+ this is useful when your have several accounting, billing or
+ logging hosts and want to have the accounting information appear
+ on all of them at the same time
+
+
+Short introduction to PAM via TACACS+:
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+This diagram should show general idea of how the whole process looks:
+
+ +-----+
+ Authen -user/pass valid?----------> | T S |
+ / | A e |
+ PAM- Author -service allowed?----------> | C r |
+ ^ \ | A v |
+ | Acct ,-----start session----------> | C e |
+ | `----stop session-----------> | S r |
+ Application +-----+
+
+ *Client Host* *Network* *Server Host*
+
+Consider `login' application:
+
+1. Login accepts username and password from the user.
+2. Login calls PAM function pam_authenticate() to verify if the
+ supplied username/password pair is valid.
+3. PAM loads pam_tacplus module (as defined in /etc/pam.d/login)
+ and calls pam_sm_authenticate() function supplied by this module.
+4. This function sends an encrypted packet to the TACACS+ server.
+ The packet contains username and password to verify. TACACS+ server
+ replied with either positive or negative response. If the reponse
+ is negative, the whole thing is over ;)
+5. PAM calls another function from pam_tacplus - pam_sm_acct_mgmt().
+ This function is expected to verify whether the user is allowed
+ to get the service he's requesting (in this case: unix shell).
+ The function again verifies the permission on TACACS+ server. Assume
+ the server granted the user with requested service.
+6. Before user gets the shell, PAM calls one another function from
+ pam_tacplus - pam_sm_open_session(). This results in sending an
+ accounting START packet to the server. Among other things it contains
+ the terminal user loggen in on and the time session started.
+7. When user logs out, pam_sm_close_session() sends STOP packet to the
+ server. The whole session is closed.
+
+Limitations:
+~~~~~~~~~~~~
+
+Many of them for now :)
+
+ * it's still in beta
+ * only subset of TACACS+ protocol is supported; it's enough for
+ most need, though
+ * only one, common `secret' can be specified
+ * utilize PAM_SERVICE item obtained from PAM for TACACS+ services
+ * clean options and configuration code
+
+References:
+~~~~~~~~~~~
+
+TACACS+
+1. ftp://ftpeng.cisco.com/pub/tacplus/tac_plus.rfc.1.76.txt
+2. ftp://ftpeng.cisco.com/pub/tacplus/tac_plus.3.0.12.alpha.tar.Z
+
+PAM
+3. http://parc.power.net/morgan/Linux-PAM/index.html
+
+Authors:
+~~~~~~~
+
+Pawel Krawczyk <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);