summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristian Poessinger <christian@poessinger.com>2021-05-02 19:07:13 +0200
committerChristian Poessinger <christian@poessinger.com>2021-05-02 19:07:49 +0200
commit7d2e07fd4502aed3b841484855031ca8a48aebba (patch)
tree7861b20208fd1089b0d6e67f72523d0ca6d745da
downloadlibtacplus-map-7d2e07fd4502aed3b841484855031ca8a48aebba.tar.gz
libtacplus-map-7d2e07fd4502aed3b841484855031ca8a48aebba.zip
Initial import of libtacplus-map (1.0.1-cl3u3)
-rw-r--r--AUTHORS7
-rw-r--r--COPYING255
-rw-r--r--ChangeLog5
-rw-r--r--Makefile.am39
-rw-r--r--NEWS1
-rw-r--r--README51
-rwxr-xr-xauto.sh4
-rw-r--r--config/ltoptions.m4384
-rw-r--r--config/ltsugar.m4123
-rw-r--r--config/ltversion.m423
-rw-r--r--config/lt~obsolete.m498
-rw-r--r--configure.ac69
-rw-r--r--debian/README.source5
-rw-r--r--debian/changelog37
-rw-r--r--debian/compat1
-rw-r--r--debian/control22
-rw-r--r--debian/copyright27
-rw-r--r--debian/libtacplus-map-dev.install2
-rw-r--r--debian/libtacplus-map1.install2
-rw-r--r--debian/libtacplus-map1.postinst51
-rw-r--r--debian/libtacplus-map1.symbols10
-rwxr-xr-xdebian/rules13
-rw-r--r--debian/source/format2
-rw-r--r--dumpmap.c79
-rw-r--r--map_tacplus_user.c689
-rw-r--r--map_tacplus_user.h106
-rw-r--r--tacplus.sudo18
27 files changed, 2123 insertions, 0 deletions
diff --git a/AUTHORS b/AUTHORS
new file mode 100644
index 0000000..03d59a6
--- /dev/null
+++ b/AUTHORS
@@ -0,0 +1,7 @@
+Primary Author:
+ Dave Olson <olson@cumulusnetworks.com>
+
+The TACACS code is substantially based on the pam_tacplus v1.3.9 code written by
+ Pawel Krawczyk <pawel.krawczyk@hush.com> and
+ Jeroen Nijhof <jeroen@jeroennijhof.nl>
+
diff --git a/COPYING b/COPYING
new file mode 100644
index 0000000..18f56e4
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,255 @@
+GNU GENERAL PUBLIC LICENSE
+
+Version 2, June 1991
+
+Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1355, 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 Lesser 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.
+
+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.
diff --git a/ChangeLog b/ChangeLog
new file mode 100644
index 0000000..b1997a7
--- /dev/null
+++ b/ChangeLog
@@ -0,0 +1,5 @@
+1.0.0
+* first working version with supplying mapping of users authenticated
+ via TACACS+ through the pam_tacplus.so plugin.
+
+Dave Olson, June 2016
diff --git a/Makefile.am b/Makefile.am
new file mode 100644
index 0000000..35235ff
--- /dev/null
+++ b/Makefile.am
@@ -0,0 +1,39 @@
+###########################################################################
+##
+## Copyright 2014, 2015, 2016 Cumulus Networks, Inc. All rights reserved.
+## Author: Dave Olson <olson@cumulusnetworks.com>
+##
+###########################################################################
+
+ACLOCAL_AMFLAGS = -I config
+AUTOMAKE_OPTIONS = subdir-objects
+
+lib_LTLIBRARIES = libtacplus_map.la
+
+libtacplus_map_la_SOURCES = map_tacplus_user.h \
+ map_tacplus_user.c
+libtacplus_map_la_CFLAGS = $(AM_CFLAGS) -Ilibtac/include
+libtacplus_map_la_LDFLAGS = -version-info 1:0:0 -shared -Wl,--no-undefined
+libtacplus_map_la_LIBADD = -L.libs -laudit
+
+libtacplus_map_includedir = $(oldincludedir)/tacplus
+
+libtacplus_map_include_HEADERS = \
+ map_tacplus_user.h
+
+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
+
+clean-generic:
+ rm -rf autom4te*.cache
+ rm -f *.rej *.orig *.lang dumpmap
+
+sudoersd = $(DESTDIR)$(sysconfdir)/sudoers.d
+install-data-hook:
+ $(mkinstalldirs) $(sudoersd)
+ ${INSTALL} -m 644 tacplus.sudo $(sudoersd)/tacplus
+
+# This is for debugging only, not normally built, and not installed
+dumpmap: dumpmap.c map_tacplus_user.h
+ $(CC) $(CFLAGS) $@.c -ltacplus_map -o $@
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..4d0be82
--- /dev/null
+++ b/README
@@ -0,0 +1,51 @@
+
+libtacplus_map v1.0.0
+June 22, 2016
+
+This library supports local mapping of users authenticated via TACACS with
+the pam_tacplus module.
+
+The TACACS+ users do not need entries in /etc/passwd to supply home directory, uid,
+and gid information.
+
+This is done by creating local users called tacacs0 ... tacacs15 (at least
+one, but up to all 16). The tacacs user's privilege level is used to select
+the local tacacsN user, starting with an exact match, and working down to 0.
+
+A new libtacplus_map library (map_tacplus_user.c) writes the mappings into
+a local file in /run, and cleans up on exit (for unexpected exits without
+cleanups, the file is validated and cleaned up whenever a new entry is added
+or an old entry removed).
+
+audit_[gs]etloginuid() is used to set a stable uid identifier as well as
+triggering the /proc/$$/sessionid in the process. These are both recorded
+in the mapping file, along with tty, rhost, etc.
+
+Also see the comments about immutable loginuid in Pam.d.common-example
+in the libpam-tacplus package.
+
+A separate package libnss_tacplus uses the mapping library to do lookups by
+both name and uid. uid lookups are only possible while a tacacs user is
+logged in.
+
+If multiple tacacs users at the same privilege level are logged in, the
+current behavior is that is that if a call is done from within the login
+session, the correct (login) name will be returned. If from outside the
+session (audit uid and/or session don't match in the mapping file), the name
+from first map entry is used, much like normal systems where multiple users
+have the same UID.
+
+Enabled -Werror to catch errors early (and fixed a few related items).
+
+This code is based in the pam_tacplus plugin, written by
+Pawel Krawczyk <pawel.krawczyk@hush.com> and Jeroen Nijhof
+<jeroen@jeroennijhof.nl>, as well as others. It is based
+on version pam_tacplus version 1.3.9. It uses the libtac
+as found in pam_tacplus. A few minor changes have been made,
+and libtac is built as a static archive library.
+
+
+Author:
+~~~~~~~
+
+Dave Olson <olson@cumulusnetworks.com>
diff --git a/auto.sh b/auto.sh
new file mode 100755
index 0000000..700d120
--- /dev/null
+++ b/auto.sh
@@ -0,0 +1,4 @@
+#!/bin/sh
+
+mkdir -p config
+autoreconf -f -v -i
diff --git a/config/ltoptions.m4 b/config/ltoptions.m4
new file mode 100644
index 0000000..5d9acd8
--- /dev/null
+++ b/config/ltoptions.m4
@@ -0,0 +1,384 @@
+# Helper functions for option handling. -*- Autoconf -*-
+#
+# Copyright (C) 2004, 2005, 2007, 2008, 2009 Free Software Foundation,
+# Inc.
+# Written by Gary V. Vaughan, 2004
+#
+# This file is free software; the Free Software Foundation gives
+# unlimited permission to copy and/or distribute it, with or without
+# modifications, as long as this notice is preserved.
+
+# serial 7 ltoptions.m4
+
+# This is to help aclocal find these macros, as it can't see m4_define.
+AC_DEFUN([LTOPTIONS_VERSION], [m4_if([1])])
+
+
+# _LT_MANGLE_OPTION(MACRO-NAME, OPTION-NAME)
+# ------------------------------------------
+m4_define([_LT_MANGLE_OPTION],
+[[_LT_OPTION_]m4_bpatsubst($1__$2, [[^a-zA-Z0-9_]], [_])])
+
+
+# _LT_SET_OPTION(MACRO-NAME, OPTION-NAME)
+# ---------------------------------------
+# Set option OPTION-NAME for macro MACRO-NAME, and if there is a
+# matching handler defined, dispatch to it. Other OPTION-NAMEs are
+# saved as a flag.
+m4_define([_LT_SET_OPTION],
+[m4_define(_LT_MANGLE_OPTION([$1], [$2]))dnl
+m4_ifdef(_LT_MANGLE_DEFUN([$1], [$2]),
+ _LT_MANGLE_DEFUN([$1], [$2]),
+ [m4_warning([Unknown $1 option `$2'])])[]dnl
+])
+
+
+# _LT_IF_OPTION(MACRO-NAME, OPTION-NAME, IF-SET, [IF-NOT-SET])
+# ------------------------------------------------------------
+# Execute IF-SET if OPTION is set, IF-NOT-SET otherwise.
+m4_define([_LT_IF_OPTION],
+[m4_ifdef(_LT_MANGLE_OPTION([$1], [$2]), [$3], [$4])])
+
+
+# _LT_UNLESS_OPTIONS(MACRO-NAME, OPTION-LIST, IF-NOT-SET)
+# -------------------------------------------------------
+# Execute IF-NOT-SET unless all options in OPTION-LIST for MACRO-NAME
+# are set.
+m4_define([_LT_UNLESS_OPTIONS],
+[m4_foreach([_LT_Option], m4_split(m4_normalize([$2])),
+ [m4_ifdef(_LT_MANGLE_OPTION([$1], _LT_Option),
+ [m4_define([$0_found])])])[]dnl
+m4_ifdef([$0_found], [m4_undefine([$0_found])], [$3
+])[]dnl
+])
+
+
+# _LT_SET_OPTIONS(MACRO-NAME, OPTION-LIST)
+# ----------------------------------------
+# OPTION-LIST is a space-separated list of Libtool options associated
+# with MACRO-NAME. If any OPTION has a matching handler declared with
+# LT_OPTION_DEFINE, dispatch to that macro; otherwise complain about
+# the unknown option and exit.
+m4_defun([_LT_SET_OPTIONS],
+[# Set options
+m4_foreach([_LT_Option], m4_split(m4_normalize([$2])),
+ [_LT_SET_OPTION([$1], _LT_Option)])
+
+m4_if([$1],[LT_INIT],[
+ dnl
+ dnl Simply set some default values (i.e off) if boolean options were not
+ dnl specified:
+ _LT_UNLESS_OPTIONS([LT_INIT], [dlopen], [enable_dlopen=no
+ ])
+ _LT_UNLESS_OPTIONS([LT_INIT], [win32-dll], [enable_win32_dll=no
+ ])
+ dnl
+ dnl If no reference was made to various pairs of opposing options, then
+ dnl we run the default mode handler for the pair. For example, if neither
+ dnl `shared' nor `disable-shared' was passed, we enable building of shared
+ dnl archives by default:
+ _LT_UNLESS_OPTIONS([LT_INIT], [shared disable-shared], [_LT_ENABLE_SHARED])
+ _LT_UNLESS_OPTIONS([LT_INIT], [static disable-static], [_LT_ENABLE_STATIC])
+ _LT_UNLESS_OPTIONS([LT_INIT], [pic-only no-pic], [_LT_WITH_PIC])
+ _LT_UNLESS_OPTIONS([LT_INIT], [fast-install disable-fast-install],
+ [_LT_ENABLE_FAST_INSTALL])
+ ])
+])# _LT_SET_OPTIONS
+
+
+## --------------------------------- ##
+## Macros to handle LT_INIT options. ##
+## --------------------------------- ##
+
+# _LT_MANGLE_DEFUN(MACRO-NAME, OPTION-NAME)
+# -----------------------------------------
+m4_define([_LT_MANGLE_DEFUN],
+[[_LT_OPTION_DEFUN_]m4_bpatsubst(m4_toupper([$1__$2]), [[^A-Z0-9_]], [_])])
+
+
+# LT_OPTION_DEFINE(MACRO-NAME, OPTION-NAME, CODE)
+# -----------------------------------------------
+m4_define([LT_OPTION_DEFINE],
+[m4_define(_LT_MANGLE_DEFUN([$1], [$2]), [$3])[]dnl
+])# LT_OPTION_DEFINE
+
+
+# dlopen
+# ------
+LT_OPTION_DEFINE([LT_INIT], [dlopen], [enable_dlopen=yes
+])
+
+AU_DEFUN([AC_LIBTOOL_DLOPEN],
+[_LT_SET_OPTION([LT_INIT], [dlopen])
+AC_DIAGNOSE([obsolete],
+[$0: Remove this warning and the call to _LT_SET_OPTION when you
+put the `dlopen' option into LT_INIT's first parameter.])
+])
+
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([AC_LIBTOOL_DLOPEN], [])
+
+
+# win32-dll
+# ---------
+# Declare package support for building win32 dll's.
+LT_OPTION_DEFINE([LT_INIT], [win32-dll],
+[enable_win32_dll=yes
+
+case $host in
+*-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-cegcc*)
+ AC_CHECK_TOOL(AS, as, false)
+ AC_CHECK_TOOL(DLLTOOL, dlltool, false)
+ AC_CHECK_TOOL(OBJDUMP, objdump, false)
+ ;;
+esac
+
+test -z "$AS" && AS=as
+_LT_DECL([], [AS], [1], [Assembler program])dnl
+
+test -z "$DLLTOOL" && DLLTOOL=dlltool
+_LT_DECL([], [DLLTOOL], [1], [DLL creation program])dnl
+
+test -z "$OBJDUMP" && OBJDUMP=objdump
+_LT_DECL([], [OBJDUMP], [1], [Object dumper program])dnl
+])# win32-dll
+
+AU_DEFUN([AC_LIBTOOL_WIN32_DLL],
+[AC_REQUIRE([AC_CANONICAL_HOST])dnl
+_LT_SET_OPTION([LT_INIT], [win32-dll])
+AC_DIAGNOSE([obsolete],
+[$0: Remove this warning and the call to _LT_SET_OPTION when you
+put the `win32-dll' option into LT_INIT's first parameter.])
+])
+
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([AC_LIBTOOL_WIN32_DLL], [])
+
+
+# _LT_ENABLE_SHARED([DEFAULT])
+# ----------------------------
+# implement the --enable-shared flag, and supports the `shared' and
+# `disable-shared' LT_INIT options.
+# DEFAULT is either `yes' or `no'. If omitted, it defaults to `yes'.
+m4_define([_LT_ENABLE_SHARED],
+[m4_define([_LT_ENABLE_SHARED_DEFAULT], [m4_if($1, no, no, yes)])dnl
+AC_ARG_ENABLE([shared],
+ [AS_HELP_STRING([--enable-shared@<:@=PKGS@:>@],
+ [build shared libraries @<:@default=]_LT_ENABLE_SHARED_DEFAULT[@:>@])],
+ [p=${PACKAGE-default}
+ case $enableval in
+ yes) enable_shared=yes ;;
+ no) enable_shared=no ;;
+ *)
+ enable_shared=no
+ # Look at the argument we got. We use all the common list separators.
+ lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR,"
+ for pkg in $enableval; do
+ IFS="$lt_save_ifs"
+ if test "X$pkg" = "X$p"; then
+ enable_shared=yes
+ fi
+ done
+ IFS="$lt_save_ifs"
+ ;;
+ esac],
+ [enable_shared=]_LT_ENABLE_SHARED_DEFAULT)
+
+ _LT_DECL([build_libtool_libs], [enable_shared], [0],
+ [Whether or not to build shared libraries])
+])# _LT_ENABLE_SHARED
+
+LT_OPTION_DEFINE([LT_INIT], [shared], [_LT_ENABLE_SHARED([yes])])
+LT_OPTION_DEFINE([LT_INIT], [disable-shared], [_LT_ENABLE_SHARED([no])])
+
+# Old names:
+AC_DEFUN([AC_ENABLE_SHARED],
+[_LT_SET_OPTION([LT_INIT], m4_if([$1], [no], [disable-])[shared])
+])
+
+AC_DEFUN([AC_DISABLE_SHARED],
+[_LT_SET_OPTION([LT_INIT], [disable-shared])
+])
+
+AU_DEFUN([AM_ENABLE_SHARED], [AC_ENABLE_SHARED($@)])
+AU_DEFUN([AM_DISABLE_SHARED], [AC_DISABLE_SHARED($@)])
+
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([AM_ENABLE_SHARED], [])
+dnl AC_DEFUN([AM_DISABLE_SHARED], [])
+
+
+
+# _LT_ENABLE_STATIC([DEFAULT])
+# ----------------------------
+# implement the --enable-static flag, and support the `static' and
+# `disable-static' LT_INIT options.
+# DEFAULT is either `yes' or `no'. If omitted, it defaults to `yes'.
+m4_define([_LT_ENABLE_STATIC],
+[m4_define([_LT_ENABLE_STATIC_DEFAULT], [m4_if($1, no, no, yes)])dnl
+AC_ARG_ENABLE([static],
+ [AS_HELP_STRING([--enable-static@<:@=PKGS@:>@],
+ [build static libraries @<:@default=]_LT_ENABLE_STATIC_DEFAULT[@:>@])],
+ [p=${PACKAGE-default}
+ case $enableval in
+ yes) enable_static=yes ;;
+ no) enable_static=no ;;
+ *)
+ enable_static=no
+ # Look at the argument we got. We use all the common list separators.
+ lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR,"
+ for pkg in $enableval; do
+ IFS="$lt_save_ifs"
+ if test "X$pkg" = "X$p"; then
+ enable_static=yes
+ fi
+ done
+ IFS="$lt_save_ifs"
+ ;;
+ esac],
+ [enable_static=]_LT_ENABLE_STATIC_DEFAULT)
+
+ _LT_DECL([build_old_libs], [enable_static], [0],
+ [Whether or not to build static libraries])
+])# _LT_ENABLE_STATIC
+
+LT_OPTION_DEFINE([LT_INIT], [static], [_LT_ENABLE_STATIC([yes])])
+LT_OPTION_DEFINE([LT_INIT], [disable-static], [_LT_ENABLE_STATIC([no])])
+
+# Old names:
+AC_DEFUN([AC_ENABLE_STATIC],
+[_LT_SET_OPTION([LT_INIT], m4_if([$1], [no], [disable-])[static])
+])
+
+AC_DEFUN([AC_DISABLE_STATIC],
+[_LT_SET_OPTION([LT_INIT], [disable-static])
+])
+
+AU_DEFUN([AM_ENABLE_STATIC], [AC_ENABLE_STATIC($@)])
+AU_DEFUN([AM_DISABLE_STATIC], [AC_DISABLE_STATIC($@)])
+
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([AM_ENABLE_STATIC], [])
+dnl AC_DEFUN([AM_DISABLE_STATIC], [])
+
+
+
+# _LT_ENABLE_FAST_INSTALL([DEFAULT])
+# ----------------------------------
+# implement the --enable-fast-install flag, and support the `fast-install'
+# and `disable-fast-install' LT_INIT options.
+# DEFAULT is either `yes' or `no'. If omitted, it defaults to `yes'.
+m4_define([_LT_ENABLE_FAST_INSTALL],
+[m4_define([_LT_ENABLE_FAST_INSTALL_DEFAULT], [m4_if($1, no, no, yes)])dnl
+AC_ARG_ENABLE([fast-install],
+ [AS_HELP_STRING([--enable-fast-install@<:@=PKGS@:>@],
+ [optimize for fast installation @<:@default=]_LT_ENABLE_FAST_INSTALL_DEFAULT[@:>@])],
+ [p=${PACKAGE-default}
+ case $enableval in
+ yes) enable_fast_install=yes ;;
+ no) enable_fast_install=no ;;
+ *)
+ enable_fast_install=no
+ # Look at the argument we got. We use all the common list separators.
+ lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR,"
+ for pkg in $enableval; do
+ IFS="$lt_save_ifs"
+ if test "X$pkg" = "X$p"; then
+ enable_fast_install=yes
+ fi
+ done
+ IFS="$lt_save_ifs"
+ ;;
+ esac],
+ [enable_fast_install=]_LT_ENABLE_FAST_INSTALL_DEFAULT)
+
+_LT_DECL([fast_install], [enable_fast_install], [0],
+ [Whether or not to optimize for fast installation])dnl
+])# _LT_ENABLE_FAST_INSTALL
+
+LT_OPTION_DEFINE([LT_INIT], [fast-install], [_LT_ENABLE_FAST_INSTALL([yes])])
+LT_OPTION_DEFINE([LT_INIT], [disable-fast-install], [_LT_ENABLE_FAST_INSTALL([no])])
+
+# Old names:
+AU_DEFUN([AC_ENABLE_FAST_INSTALL],
+[_LT_SET_OPTION([LT_INIT], m4_if([$1], [no], [disable-])[fast-install])
+AC_DIAGNOSE([obsolete],
+[$0: Remove this warning and the call to _LT_SET_OPTION when you put
+the `fast-install' option into LT_INIT's first parameter.])
+])
+
+AU_DEFUN([AC_DISABLE_FAST_INSTALL],
+[_LT_SET_OPTION([LT_INIT], [disable-fast-install])
+AC_DIAGNOSE([obsolete],
+[$0: Remove this warning and the call to _LT_SET_OPTION when you put
+the `disable-fast-install' option into LT_INIT's first parameter.])
+])
+
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([AC_ENABLE_FAST_INSTALL], [])
+dnl AC_DEFUN([AM_DISABLE_FAST_INSTALL], [])
+
+
+# _LT_WITH_PIC([MODE])
+# --------------------
+# implement the --with-pic flag, and support the `pic-only' and `no-pic'
+# LT_INIT options.
+# MODE is either `yes' or `no'. If omitted, it defaults to `both'.
+m4_define([_LT_WITH_PIC],
+[AC_ARG_WITH([pic],
+ [AS_HELP_STRING([--with-pic@<:@=PKGS@:>@],
+ [try to use only PIC/non-PIC objects @<:@default=use both@:>@])],
+ [lt_p=${PACKAGE-default}
+ case $withval in
+ yes|no) pic_mode=$withval ;;
+ *)
+ pic_mode=default
+ # Look at the argument we got. We use all the common list separators.
+ lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR,"
+ for lt_pkg in $withval; do
+ IFS="$lt_save_ifs"
+ if test "X$lt_pkg" = "X$lt_p"; then
+ pic_mode=yes
+ fi
+ done
+ IFS="$lt_save_ifs"
+ ;;
+ esac],
+ [pic_mode=default])
+
+test -z "$pic_mode" && pic_mode=m4_default([$1], [default])
+
+_LT_DECL([], [pic_mode], [0], [What type of objects to build])dnl
+])# _LT_WITH_PIC
+
+LT_OPTION_DEFINE([LT_INIT], [pic-only], [_LT_WITH_PIC([yes])])
+LT_OPTION_DEFINE([LT_INIT], [no-pic], [_LT_WITH_PIC([no])])
+
+# Old name:
+AU_DEFUN([AC_LIBTOOL_PICMODE],
+[_LT_SET_OPTION([LT_INIT], [pic-only])
+AC_DIAGNOSE([obsolete],
+[$0: Remove this warning and the call to _LT_SET_OPTION when you
+put the `pic-only' option into LT_INIT's first parameter.])
+])
+
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([AC_LIBTOOL_PICMODE], [])
+
+## ----------------- ##
+## LTDL_INIT Options ##
+## ----------------- ##
+
+m4_define([_LTDL_MODE], [])
+LT_OPTION_DEFINE([LTDL_INIT], [nonrecursive],
+ [m4_define([_LTDL_MODE], [nonrecursive])])
+LT_OPTION_DEFINE([LTDL_INIT], [recursive],
+ [m4_define([_LTDL_MODE], [recursive])])
+LT_OPTION_DEFINE([LTDL_INIT], [subproject],
+ [m4_define([_LTDL_MODE], [subproject])])
+
+m4_define([_LTDL_TYPE], [])
+LT_OPTION_DEFINE([LTDL_INIT], [installable],
+ [m4_define([_LTDL_TYPE], [installable])])
+LT_OPTION_DEFINE([LTDL_INIT], [convenience],
+ [m4_define([_LTDL_TYPE], [convenience])])
diff --git a/config/ltsugar.m4 b/config/ltsugar.m4
new file mode 100644
index 0000000..9000a05
--- /dev/null
+++ b/config/ltsugar.m4
@@ -0,0 +1,123 @@
+# ltsugar.m4 -- libtool m4 base layer. -*-Autoconf-*-
+#
+# Copyright (C) 2004, 2005, 2007, 2008 Free Software Foundation, Inc.
+# Written by Gary V. Vaughan, 2004
+#
+# This file is free software; the Free Software Foundation gives
+# unlimited permission to copy and/or distribute it, with or without
+# modifications, as long as this notice is preserved.
+
+# serial 6 ltsugar.m4
+
+# This is to help aclocal find these macros, as it can't see m4_define.
+AC_DEFUN([LTSUGAR_VERSION], [m4_if([0.1])])
+
+
+# lt_join(SEP, ARG1, [ARG2...])
+# -----------------------------
+# Produce ARG1SEPARG2...SEPARGn, omitting [] arguments and their
+# associated separator.
+# Needed until we can rely on m4_join from Autoconf 2.62, since all earlier
+# versions in m4sugar had bugs.
+m4_define([lt_join],
+[m4_if([$#], [1], [],
+ [$#], [2], [[$2]],
+ [m4_if([$2], [], [], [[$2]_])$0([$1], m4_shift(m4_shift($@)))])])
+m4_define([_lt_join],
+[m4_if([$#$2], [2], [],
+ [m4_if([$2], [], [], [[$1$2]])$0([$1], m4_shift(m4_shift($@)))])])
+
+
+# lt_car(LIST)
+# lt_cdr(LIST)
+# ------------
+# Manipulate m4 lists.
+# These macros are necessary as long as will still need to support
+# Autoconf-2.59 which quotes differently.
+m4_define([lt_car], [[$1]])
+m4_define([lt_cdr],
+[m4_if([$#], 0, [m4_fatal([$0: cannot be called without arguments])],
+ [$#], 1, [],
+ [m4_dquote(m4_shift($@))])])
+m4_define([lt_unquote], $1)
+
+
+# lt_append(MACRO-NAME, STRING, [SEPARATOR])
+# ------------------------------------------
+# Redefine MACRO-NAME to hold its former content plus `SEPARATOR'`STRING'.
+# Note that neither SEPARATOR nor STRING are expanded; they are appended
+# to MACRO-NAME as is (leaving the expansion for when MACRO-NAME is invoked).
+# No SEPARATOR is output if MACRO-NAME was previously undefined (different
+# than defined and empty).
+#
+# This macro is needed until we can rely on Autoconf 2.62, since earlier
+# versions of m4sugar mistakenly expanded SEPARATOR but not STRING.
+m4_define([lt_append],
+[m4_define([$1],
+ m4_ifdef([$1], [m4_defn([$1])[$3]])[$2])])
+
+
+
+# lt_combine(SEP, PREFIX-LIST, INFIX, SUFFIX1, [SUFFIX2...])
+# ----------------------------------------------------------
+# Produce a SEP delimited list of all paired combinations of elements of
+# PREFIX-LIST with SUFFIX1 through SUFFIXn. Each element of the list
+# has the form PREFIXmINFIXSUFFIXn.
+# Needed until we can rely on m4_combine added in Autoconf 2.62.
+m4_define([lt_combine],
+[m4_if(m4_eval([$# > 3]), [1],
+ [m4_pushdef([_Lt_sep], [m4_define([_Lt_sep], m4_defn([lt_car]))])]]dnl
+[[m4_foreach([_Lt_prefix], [$2],
+ [m4_foreach([_Lt_suffix],
+ ]m4_dquote(m4_dquote(m4_shift(m4_shift(m4_shift($@)))))[,
+ [_Lt_sep([$1])[]m4_defn([_Lt_prefix])[$3]m4_defn([_Lt_suffix])])])])])
+
+
+# lt_if_append_uniq(MACRO-NAME, VARNAME, [SEPARATOR], [UNIQ], [NOT-UNIQ])
+# -----------------------------------------------------------------------
+# Iff MACRO-NAME does not yet contain VARNAME, then append it (delimited
+# by SEPARATOR if supplied) and expand UNIQ, else NOT-UNIQ.
+m4_define([lt_if_append_uniq],
+[m4_ifdef([$1],
+ [m4_if(m4_index([$3]m4_defn([$1])[$3], [$3$2$3]), [-1],
+ [lt_append([$1], [$2], [$3])$4],
+ [$5])],
+ [lt_append([$1], [$2], [$3])$4])])
+
+
+# lt_dict_add(DICT, KEY, VALUE)
+# -----------------------------
+m4_define([lt_dict_add],
+[m4_define([$1($2)], [$3])])
+
+
+# lt_dict_add_subkey(DICT, KEY, SUBKEY, VALUE)
+# --------------------------------------------
+m4_define([lt_dict_add_subkey],
+[m4_define([$1($2:$3)], [$4])])
+
+
+# lt_dict_fetch(DICT, KEY, [SUBKEY])
+# ----------------------------------
+m4_define([lt_dict_fetch],
+[m4_ifval([$3],
+ m4_ifdef([$1($2:$3)], [m4_defn([$1($2:$3)])]),
+ m4_ifdef([$1($2)], [m4_defn([$1($2)])]))])
+
+
+# lt_if_dict_fetch(DICT, KEY, [SUBKEY], VALUE, IF-TRUE, [IF-FALSE])
+# -----------------------------------------------------------------
+m4_define([lt_if_dict_fetch],
+[m4_if(lt_dict_fetch([$1], [$2], [$3]), [$4],
+ [$5],
+ [$6])])
+
+
+# lt_dict_filter(DICT, [SUBKEY], VALUE, [SEPARATOR], KEY, [...])
+# --------------------------------------------------------------
+m4_define([lt_dict_filter],
+[m4_if([$5], [], [],
+ [lt_join(m4_quote(m4_default([$4], [[, ]])),
+ lt_unquote(m4_split(m4_normalize(m4_foreach(_Lt_key, lt_car([m4_shiftn(4, $@)]),
+ [lt_if_dict_fetch([$1], _Lt_key, [$2], [$3], [_Lt_key ])])))))])[]dnl
+])
diff --git a/config/ltversion.m4 b/config/ltversion.m4
new file mode 100644
index 0000000..07a8602
--- /dev/null
+++ b/config/ltversion.m4
@@ -0,0 +1,23 @@
+# ltversion.m4 -- version numbers -*- Autoconf -*-
+#
+# Copyright (C) 2004 Free Software Foundation, Inc.
+# Written by Scott James Remnant, 2004
+#
+# This file is free software; the Free Software Foundation gives
+# unlimited permission to copy and/or distribute it, with or without
+# modifications, as long as this notice is preserved.
+
+# @configure_input@
+
+# serial 3337 ltversion.m4
+# This file is part of GNU Libtool
+
+m4_define([LT_PACKAGE_VERSION], [2.4.2])
+m4_define([LT_PACKAGE_REVISION], [1.3337])
+
+AC_DEFUN([LTVERSION_VERSION],
+[macro_version='2.4.2'
+macro_revision='1.3337'
+_LT_DECL(, macro_version, 0, [Which release of libtool.m4 was used?])
+_LT_DECL(, macro_revision, 0)
+])
diff --git a/config/lt~obsolete.m4 b/config/lt~obsolete.m4
new file mode 100644
index 0000000..c573da9
--- /dev/null
+++ b/config/lt~obsolete.m4
@@ -0,0 +1,98 @@
+# lt~obsolete.m4 -- aclocal satisfying obsolete definitions. -*-Autoconf-*-
+#
+# Copyright (C) 2004, 2005, 2007, 2009 Free Software Foundation, Inc.
+# Written by Scott James Remnant, 2004.
+#
+# This file is free software; the Free Software Foundation gives
+# unlimited permission to copy and/or distribute it, with or without
+# modifications, as long as this notice is preserved.
+
+# serial 5 lt~obsolete.m4
+
+# These exist entirely to fool aclocal when bootstrapping libtool.
+#
+# In the past libtool.m4 has provided macros via AC_DEFUN (or AU_DEFUN)
+# which have later been changed to m4_define as they aren't part of the
+# exported API, or moved to Autoconf or Automake where they belong.
+#
+# The trouble is, aclocal is a bit thick. It'll see the old AC_DEFUN
+# in /usr/share/aclocal/libtool.m4 and remember it, then when it sees us
+# using a macro with the same name in our local m4/libtool.m4 it'll
+# pull the old libtool.m4 in (it doesn't see our shiny new m4_define
+# and doesn't know about Autoconf macros at all.)
+#
+# So we provide this file, which has a silly filename so it's always
+# included after everything else. This provides aclocal with the
+# AC_DEFUNs it wants, but when m4 processes it, it doesn't do anything
+# because those macros already exist, or will be overwritten later.
+# We use AC_DEFUN over AU_DEFUN for compatibility with aclocal-1.6.
+#
+# Anytime we withdraw an AC_DEFUN or AU_DEFUN, remember to add it here.
+# Yes, that means every name once taken will need to remain here until
+# we give up compatibility with versions before 1.7, at which point
+# we need to keep only those names which we still refer to.
+
+# This is to help aclocal find these macros, as it can't see m4_define.
+AC_DEFUN([LTOBSOLETE_VERSION], [m4_if([1])])
+
+m4_ifndef([AC_LIBTOOL_LINKER_OPTION], [AC_DEFUN([AC_LIBTOOL_LINKER_OPTION])])
+m4_ifndef([AC_PROG_EGREP], [AC_DEFUN([AC_PROG_EGREP])])
+m4_ifndef([_LT_AC_PROG_ECHO_BACKSLASH], [AC_DEFUN([_LT_AC_PROG_ECHO_BACKSLASH])])
+m4_ifndef([_LT_AC_SHELL_INIT], [AC_DEFUN([_LT_AC_SHELL_INIT])])
+m4_ifndef([_LT_AC_SYS_LIBPATH_AIX], [AC_DEFUN([_LT_AC_SYS_LIBPATH_AIX])])
+m4_ifndef([_LT_PROG_LTMAIN], [AC_DEFUN([_LT_PROG_LTMAIN])])
+m4_ifndef([_LT_AC_TAGVAR], [AC_DEFUN([_LT_AC_TAGVAR])])
+m4_ifndef([AC_LTDL_ENABLE_INSTALL], [AC_DEFUN([AC_LTDL_ENABLE_INSTALL])])
+m4_ifndef([AC_LTDL_PREOPEN], [AC_DEFUN([AC_LTDL_PREOPEN])])
+m4_ifndef([_LT_AC_SYS_COMPILER], [AC_DEFUN([_LT_AC_SYS_COMPILER])])
+m4_ifndef([_LT_AC_LOCK], [AC_DEFUN([_LT_AC_LOCK])])
+m4_ifndef([AC_LIBTOOL_SYS_OLD_ARCHIVE], [AC_DEFUN([AC_LIBTOOL_SYS_OLD_ARCHIVE])])
+m4_ifndef([_LT_AC_TRY_DLOPEN_SELF], [AC_DEFUN([_LT_AC_TRY_DLOPEN_SELF])])
+m4_ifndef([AC_LIBTOOL_PROG_CC_C_O], [AC_DEFUN([AC_LIBTOOL_PROG_CC_C_O])])
+m4_ifndef([AC_LIBTOOL_SYS_HARD_LINK_LOCKS], [AC_DEFUN([AC_LIBTOOL_SYS_HARD_LINK_LOCKS])])
+m4_ifndef([AC_LIBTOOL_OBJDIR], [AC_DEFUN([AC_LIBTOOL_OBJDIR])])
+m4_ifndef([AC_LTDL_OBJDIR], [AC_DEFUN([AC_LTDL_OBJDIR])])
+m4_ifndef([AC_LIBTOOL_PROG_LD_HARDCODE_LIBPATH], [AC_DEFUN([AC_LIBTOOL_PROG_LD_HARDCODE_LIBPATH])])
+m4_ifndef([AC_LIBTOOL_SYS_LIB_STRIP], [AC_DEFUN([AC_LIBTOOL_SYS_LIB_STRIP])])
+m4_ifndef([AC_PATH_MAGIC], [AC_DEFUN([AC_PATH_MAGIC])])
+m4_ifndef([AC_PROG_LD_GNU], [AC_DEFUN([AC_PROG_LD_GNU])])
+m4_ifndef([AC_PROG_LD_RELOAD_FLAG], [AC_DEFUN([AC_PROG_LD_RELOAD_FLAG])])
+m4_ifndef([AC_DEPLIBS_CHECK_METHOD], [AC_DEFUN([AC_DEPLIBS_CHECK_METHOD])])
+m4_ifndef([AC_LIBTOOL_PROG_COMPILER_NO_RTTI], [AC_DEFUN([AC_LIBTOOL_PROG_COMPILER_NO_RTTI])])
+m4_ifndef([AC_LIBTOOL_SYS_GLOBAL_SYMBOL_PIPE], [AC_DEFUN([AC_LIBTOOL_SYS_GLOBAL_SYMBOL_PIPE])])
+m4_ifndef([AC_LIBTOOL_PROG_COMPILER_PIC], [AC_DEFUN([AC_LIBTOOL_PROG_COMPILER_PIC])])
+m4_ifndef([AC_LIBTOOL_PROG_LD_SHLIBS], [AC_DEFUN([AC_LIBTOOL_PROG_LD_SHLIBS])])
+m4_ifndef([AC_LIBTOOL_POSTDEP_PREDEP], [AC_DEFUN([AC_LIBTOOL_POSTDEP_PREDEP])])
+m4_ifndef([LT_AC_PROG_EGREP], [AC_DEFUN([LT_AC_PROG_EGREP])])
+m4_ifndef([LT_AC_PROG_SED], [AC_DEFUN([LT_AC_PROG_SED])])
+m4_ifndef([_LT_CC_BASENAME], [AC_DEFUN([_LT_CC_BASENAME])])
+m4_ifndef([_LT_COMPILER_BOILERPLATE], [AC_DEFUN([_LT_COMPILER_BOILERPLATE])])
+m4_ifndef([_LT_LINKER_BOILERPLATE], [AC_DEFUN([_LT_LINKER_BOILERPLATE])])
+m4_ifndef([_AC_PROG_LIBTOOL], [AC_DEFUN([_AC_PROG_LIBTOOL])])
+m4_ifndef([AC_LIBTOOL_SETUP], [AC_DEFUN([AC_LIBTOOL_SETUP])])
+m4_ifndef([_LT_AC_CHECK_DLFCN], [AC_DEFUN([_LT_AC_CHECK_DLFCN])])
+m4_ifndef([AC_LIBTOOL_SYS_DYNAMIC_LINKER], [AC_DEFUN([AC_LIBTOOL_SYS_DYNAMIC_LINKER])])
+m4_ifndef([_LT_AC_TAGCONFIG], [AC_DEFUN([_LT_AC_TAGCONFIG])])
+m4_ifndef([AC_DISABLE_FAST_INSTALL], [AC_DEFUN([AC_DISABLE_FAST_INSTALL])])
+m4_ifndef([_LT_AC_LANG_CXX], [AC_DEFUN([_LT_AC_LANG_CXX])])
+m4_ifndef([_LT_AC_LANG_F77], [AC_DEFUN([_LT_AC_LANG_F77])])
+m4_ifndef([_LT_AC_LANG_GCJ], [AC_DEFUN([_LT_AC_LANG_GCJ])])
+m4_ifndef([AC_LIBTOOL_LANG_C_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_C_CONFIG])])
+m4_ifndef([_LT_AC_LANG_C_CONFIG], [AC_DEFUN([_LT_AC_LANG_C_CONFIG])])
+m4_ifndef([AC_LIBTOOL_LANG_CXX_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_CXX_CONFIG])])
+m4_ifndef([_LT_AC_LANG_CXX_CONFIG], [AC_DEFUN([_LT_AC_LANG_CXX_CONFIG])])
+m4_ifndef([AC_LIBTOOL_LANG_F77_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_F77_CONFIG])])
+m4_ifndef([_LT_AC_LANG_F77_CONFIG], [AC_DEFUN([_LT_AC_LANG_F77_CONFIG])])
+m4_ifndef([AC_LIBTOOL_LANG_GCJ_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_GCJ_CONFIG])])
+m4_ifndef([_LT_AC_LANG_GCJ_CONFIG], [AC_DEFUN([_LT_AC_LANG_GCJ_CONFIG])])
+m4_ifndef([AC_LIBTOOL_LANG_RC_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_RC_CONFIG])])
+m4_ifndef([_LT_AC_LANG_RC_CONFIG], [AC_DEFUN([_LT_AC_LANG_RC_CONFIG])])
+m4_ifndef([AC_LIBTOOL_CONFIG], [AC_DEFUN([AC_LIBTOOL_CONFIG])])
+m4_ifndef([_LT_AC_FILE_LTDLL_C], [AC_DEFUN([_LT_AC_FILE_LTDLL_C])])
+m4_ifndef([_LT_REQUIRED_DARWIN_CHECKS], [AC_DEFUN([_LT_REQUIRED_DARWIN_CHECKS])])
+m4_ifndef([_LT_AC_PROG_CXXCPP], [AC_DEFUN([_LT_AC_PROG_CXXCPP])])
+m4_ifndef([_LT_PREPARE_SED_QUOTE_VARS], [AC_DEFUN([_LT_PREPARE_SED_QUOTE_VARS])])
+m4_ifndef([_LT_PROG_ECHO_BACKSLASH], [AC_DEFUN([_LT_PROG_ECHO_BACKSLASH])])
+m4_ifndef([_LT_PROG_F77], [AC_DEFUN([_LT_PROG_F77])])
+m4_ifndef([_LT_PROG_FC], [AC_DEFUN([_LT_PROG_FC])])
+m4_ifndef([_LT_PROG_CXX], [AC_DEFUN([_LT_PROG_CXX])])
diff --git a/configure.ac b/configure.ac
new file mode 100644
index 0000000..346880c
--- /dev/null
+++ b/configure.ac
@@ -0,0 +1,69 @@
+dnl
+dnl File: configure.in
+dnl Revision: $Id: configure.ac,v 1.0 2014/11/03 12:04:29 olson Exp $
+dnl Created: 2014/11/02
+dnl Author: Dave Olson <olson@cumulusnetworks.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(libtacplus_map, 1.0.0, [olson@cumulusnetworks.com])
+AC_CONFIG_AUX_DIR(config)
+AM_INIT_AUTOMAKE
+AC_CONFIG_SRCDIR([map_tacplus_user.c])
+AC_CONFIG_HEADER([config.h])
+AC_CONFIG_MACRO_DIR([config])
+
+# no static lib version
+LT_INIT([disable-static])
+
+AC_ENABLE_SHARED(yes)
+AC_ENABLE_STATIC(no)
+
+dnl --------------------------------------------------------------------
+dnl Checks for programs.
+AC_PROG_CC
+AM_PROG_CC_C_O
+AC_PROG_INSTALL
+AC_PROG_LN_S
+AC_PROG_MAKE_SET
+AM_PROG_LIBTOOL
+
+# Add -Wall -Werror if we are using GCC.
+if test "x$GCC" = "xyes"; then
+ CFLAGS="$CFLAGS -Wall -Werror"
+fi
+
+dnl --------------------------------------------------------------------
+dnl Checks for libraries.
+AC_CHECK_LIB(audit, audit_getloginuid)
+
+dnl --------------------------------------------------------------------
+dnl Checks for header files.
+AC_HEADER_STDC
+AC_CHECK_HEADERS([dirent.h stdlib.h string.h sys/time.h unistd.h])
+AC_CHECK_HEADERS([libaudit.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([gettimeofday])
+
+dnl --------------------------------------------------------------------
+dnl Generate made files
+AC_CONFIG_FILES([Makefile])
+AC_OUTPUT
diff --git a/debian/README.source b/debian/README.source
new file mode 100644
index 0000000..68089c6
--- /dev/null
+++ b/debian/README.source
@@ -0,0 +1,5 @@
+This package uses quilt to manage all modifications to the upstream source.
+Changes are stored in the source package as diffs in debian/patches and
+applied during the build.
+
+See /usr/share/doc/quilt/README.source for a detailed explanation.
diff --git a/debian/changelog b/debian/changelog
new file mode 100644
index 0000000..2423348
--- /dev/null
+++ b/debian/changelog
@@ -0,0 +1,37 @@
+libtacplus-map (1.0.1-cl3u3) RELEASED; urgency=low
+
+ * Fixed problem with local fallback authentication when all TACACS
+ servers are down.
+
+ -- dev-support <dev-support@cumulusnetworks.com> Tue, 21 Aug 2018 16:23:13 -0700
+
+libtacplus-map (1.0.1-cl3u2) RELEASED; urgency=low
+
+ * tacacs users are now in group netshow (netedit for priv=15), so they
+ can run nclu commands without edits to netd.conf
+
+ -- dev-support <dev-support@cumulusnetworks.com> Wed, 14 Feb 2018 13:42:56 -0800
+
+libtacplus-map (1.0.1-cl3u1) RELEASED; urgency=low
+
+ * API and map file change to support new user_homedir config variable.
+
+ -- dev-support <dev-support@cumulusnetworks.com> Tue, 02 May 2017 12:28:44 -0700
+
+libtacplus-map (1.0.0-cl3u2) RELEASED; urgency=low
+
+ * Minor corrections to Copyright and licensing files.
+ * Provide commented-out example allowing priv 15 TACACS users to sudo
+ without password in /etc/sudoers.d/tacplus
+
+ -- dev-support <dev-support@cumulusnetworks.com> Tue, 29 Nov 2016 16:13:50 -0800
+
+libtacplus-map (1.0.0-cl3eau1) RELEASED; urgency=low
+
+ * Initial release of tacacs user mapping library
+ * libtacplus_map APIs to support local mapping, so that TACACS users do not
+ need to add TACACS+ accounts to /etc/passwd to supply home directory, uid,
+ and gid. TACACS+ users are mapped by privilege level to local tacacs0..15
+
+ -- dev-support <dev-support@cumulusnetworks.com> Wed, 22 Jun 2016 14:39:32 -0700
+
diff --git a/debian/compat b/debian/compat
new file mode 100644
index 0000000..ec63514
--- /dev/null
+++ b/debian/compat
@@ -0,0 +1 @@
+9
diff --git a/debian/control b/debian/control
new file mode 100644
index 0000000..55c3b56
--- /dev/null
+++ b/debian/control
@@ -0,0 +1,22 @@
+Source: libtacplus-map
+Section: admin
+Priority: extra
+Maintainer: dev-support <dev-support@cumulusnetworks.com>
+Build-Depends: debhelper (>= 9), dh-autoreconf, autoconf-archive, libaudit-dev, git
+Standards-Version: 3.9.6
+Homepage: http://www.cumulusnetworks.com
+
+Package: libtacplus-map1
+Architecture: any
+Depends: ${shlibs:Depends}, ${misc:Depends}, adduser, libaudit1
+Description: Library for mapping TACACS+ users without local /etc/passwd entries
+ APIs to support local mapping, so that TACACS users do not need tacacs user
+ accounts to /etc/passwd to supply home directory, uid, and gid.
+
+Package: libtacplus-map-dev
+Section: libdevel
+Architecture: any
+Depends: ${misc:Depends}, libtacplus-map1 (= ${binary:Version}), libc-dev
+Description: Development files for TACACS+ user-mapping library
+ Header files and .so shared library link for APIs to support local TACACS
+ mapping of accounts
diff --git a/debian/copyright b/debian/copyright
new file mode 100644
index 0000000..814080f
--- /dev/null
+++ b/debian/copyright
@@ -0,0 +1,27 @@
+Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
+Upstream-Name: libsimple-tacacct
+Source: http://www.cumulusnetworks.com
+
+Files: *
+Copyright: 2015, 2016 Cumulus Networks, Inc. All rights reserved.,
+ 2010 Pawel Krawczyk <pawel.krawczyk@hush.com> and Jeroen Nijhof <jeroen@jeroennijhof.nl>
+License: GPL-2+
+
+License: GPL-2+
+ 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.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ .
+ On Debian systems, the full copy of the GPL-2 license can be found in
+ /usr/share/common-licenses/GPL-2
+
diff --git a/debian/libtacplus-map-dev.install b/debian/libtacplus-map-dev.install
new file mode 100644
index 0000000..4f16771
--- /dev/null
+++ b/debian/libtacplus-map-dev.install
@@ -0,0 +1,2 @@
+usr/lib/*/libtacplus_map.so
+usr/include/tacplus/map_tacplus_user.h
diff --git a/debian/libtacplus-map1.install b/debian/libtacplus-map1.install
new file mode 100644
index 0000000..f923860
--- /dev/null
+++ b/debian/libtacplus-map1.install
@@ -0,0 +1,2 @@
+usr/lib/*/libtacplus_map.so.*
+etc/sudoers.d/*
diff --git a/debian/libtacplus-map1.postinst b/debian/libtacplus-map1.postinst
new file mode 100644
index 0000000..1a45376
--- /dev/null
+++ b/debian/libtacplus-map1.postinst
@@ -0,0 +1,51 @@
+#!/bin/sh
+# postinst script for libtacplus_map
+
+set -e
+
+case "$1" in
+ configure)
+ ;;
+
+ abort-upgrade|abort-remove|abort-deconfigure)
+ ;;
+
+ *)
+ echo "postinst called with unknown argument \`$1'" >&2
+ exit 1
+ ;;
+esac
+
+# Add the tacacs group and all 16 possible tacacs privilege-level
+# users to the password file, home directories, etc.
+# The accounts are not enabled for local login, since they are
+# only used to provide uid/gid/homedir for the mapped TACACS+
+# logins (and lookups against them).
+
+# --firstuid is used because the installed pam_tacplus configs and audit files are
+# for uid >1000. Ideally, there should be a way to specify a minimum, but not
+# override adduser.conf if it has a larger value.
+# suppress messages about already existing users, and ignore "errors" if
+# they do
+
+(set +e
+addgroup --quiet tacacs 2>&1 | grep -v 'already exists'
+level=0
+nclu_grp=netshow
+while [ $level -lt 16 ]; do
+ adduser --quiet --firstuid 1000 --disabled-login --ingroup tacacs \
+ --gecos "TACACS+ mapped user at privilege level ${level}" tacacs${level}
+ # regular tacacs users are allowed to run NCLU 'net show' commands
+ # tacacs15 (tacacs privilege level 15) user is allowed to run NCLU
+ # net configuration commands, also
+ adduser --quiet tacacs${level} $nclu_grp
+ level=$(( level+1 ))
+ [ $level -eq 15 ] && nclu_grp=netedit
+done 2>&1 | grep -v 'already exists'
+exit 0
+)
+
+
+#DEBHELPER#
+
+exit 0
diff --git a/debian/libtacplus-map1.symbols b/debian/libtacplus-map1.symbols
new file mode 100644
index 0000000..b8e23d5
--- /dev/null
+++ b/debian/libtacplus-map1.symbols
@@ -0,0 +1,10 @@
+libtacplus_map.so.1 libtacplus-map1 #MINVER#
+ __update_loguid@Base 1.0.0
+ get_user_to_auth@Base 1.0.0
+ lookup_logname@Base 1.0.0
+ lookup_mapname@Base 1.0.0
+ lookup_mapuid@Base 1.0.0
+ map_get_sessionid@Base 1.0.0
+ set_auid_immutable@Base 1.0.0
+ update_mapuser@Base 1.0.0
+
diff --git a/debian/rules b/debian/rules
new file mode 100755
index 0000000..5951990
--- /dev/null
+++ b/debian/rules
@@ -0,0 +1,13 @@
+#!/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
+SHELL := sh -e
+
+%:
+ dh $@ --with autoreconf
diff --git a/debian/source/format b/debian/source/format
new file mode 100644
index 0000000..b9b0237
--- /dev/null
+++ b/debian/source/format
@@ -0,0 +1,2 @@
+1.0
+
diff --git a/dumpmap.c b/dumpmap.c
new file mode 100644
index 0000000..8a3165c
--- /dev/null
+++ b/dumpmap.c
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2015, 2016, Cumulus Networks, Inc. All rights reserved.
+ *
+ * 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.
+ *
+ * This program is for debugging, it dumps the map file contents.
+ * Author: olson@cumulusnetworks.com>
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <syslog.h>
+#include <string.h>
+#include <unistd.h>
+#include <strings.h>
+#include <libaudit.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <dirent.h>
+#include <sys/stat.h>
+#include <sys/file.h>
+#include <stdio.h>
+
+#include "map_tacplus_user.h"
+
+
+static const char *mapfile = MAP_TACPLUS_FILE;
+
+int dump_mapfile(const char *fname)
+{
+ struct tacacs_mapping map;
+ int fd, cnt, entry=0;
+
+ fd = open(fname, O_RDONLY, 0600);
+ if(fd == -1) {
+ fprintf(stderr, "Can't open mapfile %s: %s\n", fname, strerror(errno));
+ return 1;
+ }
+
+ while((cnt=read(fd, &map, sizeof map)) == sizeof map) {
+ if (entry)
+ putchar('\n');
+ printf("Entry=%d: version=%d flags=0x%x, mapuid=%u, session=%u\n",
+ entry, map.tac_mapversion, map.tac_mapflags,
+ map.tac_mapuid, map.tac_session);
+ printf(" ts=%lu.%06lu, logname=%s, mappedname=%s\n rhost=%s\n",
+ map.tac_tv.tv_sec, map.tac_tv.tv_usec, map.tac_logname,
+ map.tac_mappedname, map.tac_rhost);
+ entry++;
+ }
+ if (cnt == -1)
+ fprintf(stderr, "Read error on mapfile %s: %s\n", fname,
+ strerror(errno));
+ else if (cnt)
+ fprintf(stderr, "Short read of %d vs %d on mapfile %s\n", cnt,
+ (int)sizeof map, fname);
+
+ close(fd);
+ return 0;
+}
+
+int main(int cnt, char **args)
+{
+ int ret;
+
+ ret = dump_mapfile(mapfile);
+ return ret;
+}
diff --git a/map_tacplus_user.c b/map_tacplus_user.c
new file mode 100644
index 0000000..47ddf78
--- /dev/null
+++ b/map_tacplus_user.c
@@ -0,0 +1,689 @@
+/*
+ * Copyright 2015, 2016, Cumulus Networks, Inc. All rights reserved.
+ *
+ * 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.
+ *
+ * Author: olson@cumulusnetworks.com>
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <syslog.h>
+#include <string.h>
+#include <unistd.h>
+#include <strings.h>
+#include <libaudit.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <dirent.h>
+#include <sys/stat.h>
+#include <sys/file.h>
+
+#include "map_tacplus_user.h"
+
+static const char *libname = "libtacplus_map";
+
+static const char *mapfile = MAP_TACPLUS_FILE;
+
+static int debug; /* for developer debug */
+
+#define MATCH_MAPPED 1 /* match mapped name in mapfile */
+#define MATCH_LOGIN 2 /* match login name in mapfile */
+/*
+ * see if a mapping file entry matches; the pid needs to be valid and
+ * the process still alive, to be consider a match.
+ * name can be NULL, if we are looking for a UID match
+ * rather than a name match.
+ * If auid and/or session are -1, they are wildcards, only match
+ * on other data.
+ * "which" controls which name we match on.
+ */
+static int is_mapmatch(struct tacacs_mapping *map, int which, const char *name,
+ uid_t auid, unsigned session)
+{
+ if(map->tac_mapversion > MAP_FILE_VERSION || !map->tac_mapversion)
+ syslog(LOG_WARNING, "%s version of tacacs client_map_file %d"
+ " != expected %d proceeding anyway", libname, map->tac_mapversion,
+ MAP_FILE_VERSION);
+ if((session == -1 || map->tac_session == session) &&
+ (auid == -1 || map->tac_mapuid == auid)) {
+ if(!name)
+ return 1; /* usually cleanup, just auid and session match */
+ switch(which) {
+ case MATCH_MAPPED:
+ if(!strcmp(name, map->tac_mappedname))
+ return 1;
+ break;
+ case MATCH_LOGIN:
+ if(!strcmp(name, map->tac_logname))
+ return 1;
+ break;
+ default:
+ syslog(LOG_WARNING, "%s invalid lookup type %d", libname, which);
+ break;
+ }
+ }
+ return 0;
+}
+
+
+/*
+ * Lookup the mapname (i.e. tacacs15) to see if there is a match, in the
+ * mapping file. and return the mapped original login name, if so. Otherwise
+ * returns the mapname passed as first argument. Passing mapname as NULL
+ * requests match on auid and session only.
+ *
+ * This only works while a mapped user is logged in, and since the auid and
+ * session are lookup keys, only for processes that are descendents
+ * of the mapped login, unless they are passed as wildcards (-1)
+ *
+ * we need to look up the auid locally only, to avoid recursing into the
+ * tacacs code. This could cause problems if somebody is using local
+ * users, ldap, and tacacs, but we just require that the mapped user always
+ * be a local user. Since the local user password isn't supposed to be
+ * used, that should be OK.
+ *
+ * We take a shared lock to prevent looking at the file while it's being
+ * updated.
+ *
+ * If returned pointer != first arg, caller should free it.
+ * There isn't a really good way to validate that an entry is still
+ * live, without searching through all the /proc/PID/sessionid files.
+ *
+ * If mapname is NULL, only match on auid & session. Used for audit records
+ * and cleanup.
+ *
+ * We don't record the PID because we can't get it right under all
+ * circumstances. If we could, it would help sanity checks.
+ *
+ * If somebody kills, e.g., the session parent login or sshd, nothing is
+ * left around to do the cleanup, and the entry could remain forever.
+ * update_loguid() does this on every add and delete.
+ */
+char *lookup_logname(const char *mapname, uid_t auid, unsigned session,
+ char **host, uint16_t *flags)
+{
+ struct tacacs_mapping map;
+ char *origuser = (char *)mapname; /* if no match, return original */
+ int fd, cnt;
+
+ if (flags)
+ *flags = 0; /* for early returns */
+ fd = open(mapfile, O_RDONLY, 0600);
+ if(fd == -1)
+ return (char *)mapname; /* not using tacacs or might be earlier error */
+
+ if(flock(fd, LOCK_SH))
+ syslog(LOG_WARNING, "%s lock of tacacs client_map_file %s failed: %m, "
+ "proceeding anyway", libname, mapfile);
+
+ while((cnt=read(fd, &map, sizeof map)) == sizeof map) {
+ if(is_mapmatch(&map, MATCH_MAPPED, mapname, auid, session)) {
+ origuser = strndup(map.tac_logname, sizeof map.tac_logname);
+ if(!origuser) {
+ syslog(LOG_WARNING,
+ "%s failed to allocate memory, user %.*s: %m",
+ libname, (int)sizeof map.tac_logname,
+ map.tac_logname);
+ origuser = (char *)mapname;
+ }
+ if(host)
+ *host = strndup(map.tac_rhost, sizeof map.tac_rhost);
+ if (flags)
+ *flags = map.tac_mapflags; /* for early returns */
+ break;
+ }
+ }
+ if(cnt > 0 && cnt != sizeof map)
+ syslog(LOG_WARNING,
+ "%s corrupted tacacs client_map_file %s: read wrong size %d",
+ libname, mapfile, cnt);
+ (void)flock(fd, LOCK_UN);
+ close(fd);
+ return origuser;
+}
+
+/*
+ * Similar to lookup_logname(), but by uid.
+ * Returns the original login username, and the mapped name
+ * in the copied to the buffered pointed to by mapped
+ * If auid and/or session are -1, they are wildcards, take
+ * the first matching uid from the mapfile
+ * Returns NULL if not found.
+ *
+ * NOTE: if this function ABI changes, sudo's plugins/sudoers/parse.c
+ * must be changed to match, since it dlopens and looks up and calls
+ * this function.
+ */
+char *lookup_mapuid(uid_t uid, uid_t auid, unsigned session,
+ char *mappedname, size_t maplen, uint16_t *flags)
+{
+ struct tacacs_mapping map;
+ int fd, cnt;
+ char *loginname = NULL;
+
+ fd = open(mapfile, O_RDONLY, 0600);
+ if(fd == -1)
+ return NULL; /* not using tacacs or might be earlier error */
+
+ if(flock(fd, LOCK_SH))
+ syslog(LOG_WARNING, "%s lock of tacacs client_map_file %s failed: %m, "
+ "proceeding anyway", libname, mapfile);
+
+ while((cnt=read(fd, &map, sizeof map)) == sizeof map) {
+ if(map.tac_mapuid == uid &&
+ is_mapmatch(&map, MATCH_LOGIN, NULL, auid, session)) {
+ loginname = strdup(map.tac_logname); /* this may leak */
+ snprintf(mappedname, maplen, "%s", map.tac_mappedname);
+ if (flags)
+ *flags = map.tac_mapflags; /* for early returns */
+ break;
+ }
+ }
+ if(cnt > 0 && cnt != sizeof map)
+ syslog(LOG_WARNING,
+ "%s corrupted tacacs client_map_file %s: read wrong size %d",
+ libname, mapfile, cnt);
+ (void)flock(fd, LOCK_UN);
+ close(fd);
+ return loginname;
+}
+
+/*
+ * Like lookup_logname(), but matches on the original login name,
+ * and returns the matching mapped name (e.g, tacacs0) if found,
+ * otherwise returns the logname argument. auid and session
+ * will most commonly be -1 wildcards for this function.
+ */
+char *lookup_mapname(const char *logname, uid_t auid, unsigned session,
+ char **host, uint16_t *flags)
+{
+ struct tacacs_mapping map;
+ char *mappeduser = (char *)logname; /* if no match, return original */
+ int fd, cnt;
+
+ if (flags)
+ *flags = 0; /* for early returns */
+ fd = open(mapfile, O_RDONLY, 0600);
+ if(fd == -1)
+ return (char *)logname; /* not using tacacs or might be earlier error */
+
+ if(flock(fd, LOCK_SH))
+ syslog(LOG_WARNING, "%s lock of tacacs client_map_file %s failed: %m, "
+ "proceeding anyway", libname, mapfile);
+
+ while((cnt=read(fd, &map, sizeof map)) == sizeof map) {
+ if(is_mapmatch(&map, MATCH_LOGIN, logname, auid, session)) {
+ mappeduser = strndup(map.tac_mappedname, sizeof map.tac_mappedname);
+ if(!mappeduser) {
+ syslog(LOG_WARNING,
+ "%s failed to allocate memory, user %.*s: %m",
+ libname, (int)sizeof map.tac_mappedname,
+ map.tac_mappedname);
+ mappeduser = (char*)logname;
+ }
+ if(host)
+ *host = strndup(map.tac_rhost, sizeof map.tac_rhost);
+ if (flags)
+ *flags = map.tac_mapflags; /* for early returns */
+ break;
+ }
+ }
+ if(cnt > 0 && cnt != sizeof map)
+ syslog(LOG_WARNING,
+ "%s corrupted tacacs client_map_file %s: read wrong size %d",
+ libname, mapfile, cnt);
+ (void)flock(fd, LOCK_UN);
+ close(fd);
+ return mappeduser;
+}
+
+/*
+ * there isn't an API to get the audit sessionid, so this will
+ * do. Returns sessionid if we can read it, else 0.
+ * 0 is not a valid sessionid; default if no auditing is -1U
+ * Don't cache the value, since it can change.
+ * We export it for users of this library.
+ *
+ * NOTE: if this function ABI changes, sudo's plugins/sudoers/parse.c
+ * must be changed to match, since it dlopens and looks up and calls
+ * this function.
+ */
+unsigned
+map_get_sessionid(void)
+{
+ int fd = -1, cnt;
+ unsigned id = 0U;
+ static char buf[12];
+
+ fd = open("/proc/self/sessionid", O_RDONLY);
+ if(fd != -1) {
+ cnt = read(fd, buf, sizeof(buf));
+ close(fd);
+ }
+ if(fd != -1 && cnt > 0) {
+ id = strtoul(buf, NULL, 0);
+ }
+ return id;
+}
+
+/*
+ * open the map file, creating if necessary, and verifying permissions
+ */
+static int
+open_map()
+{
+ int fd;
+ struct stat st;
+
+ /*
+ * create exclusive, for first time use; if that fails (regardless
+ * of errno), try a normal open.
+ */
+ fd = open(mapfile, O_CREAT|O_RDWR|O_EXCL, 0644);
+ if(fd == -1)
+ fd = open(mapfile, O_RDWR, 0600);
+ else
+ (void)fchmod(fd, 0644); /* deal with restrictive umask settings */
+ if(fd == -1) { /* directory missing? What else? */
+ syslog(LOG_ERR, "%s unable to open tacacs client_map_file %s: %m",
+ libname, mapfile);
+ }
+ else {
+ if(fstat(fd, &st) == 0 && !(st.st_mode & S_IROTH)) {
+ if(fchmod(fd, st.st_mode | S_IROTH))
+ syslog(LOG_ERR, "%s unable to chmod tacacs "
+ "client_map_file %s: %m", libname, mapfile);
+ }
+ }
+ return fd;
+}
+
+/*
+ * Lookup a sessionid for all /proc/PID/sessionid
+ * If a match is found, or there are lookup errors, return 0, else return 1.
+ */
+static int
+invalid_session(int mapsess)
+{
+ DIR *dp;
+ struct dirent *dptr;
+ int ret = 0;
+
+ dp = opendir("/proc");
+ if(!dp)
+ return 0;
+ while((dptr = readdir(dp))) {
+ char *eptr;
+ if(strtoul(dptr->d_name, &eptr, 10) && !*eptr) {
+ /* all numeric, it's a PID */
+ char nmbuf[128]; /* always short path */
+ char sess_str[16];
+ int fd, cnt, sess=0;
+ snprintf(nmbuf, sizeof nmbuf, "/proc/%s/sessionid", dptr->d_name);
+ fd = open(nmbuf, O_RDONLY);
+ if(fd == -1)
+ syslog(LOG_DEBUG, "%s: %s open fails: %m", libname, nmbuf);
+ else {
+ cnt = read(fd, sess_str, sizeof sess_str - 1);
+ close(fd);
+ if(cnt > 0) {
+ sess_str[cnt] = '\0';
+ sess = strtoul(sess_str, &eptr, 0);
+ if(sess == mapsess) {
+ goto done;
+ }
+ }
+ }
+ }
+ }
+ ret = 1;
+done:
+ closedir(dp);
+ return ret;
+}
+
+/*
+ * check for stale (invalid) entries, and clean them up if found.
+ * Called with the flock() held.
+ *
+ * Always write the version to be our current version number.
+ * If it was different, we warned in is_match().
+ */
+static void
+chk_cleanup_map(int fd)
+{
+ struct tacacs_mapping map, tmap;
+ int cnt;
+
+ if(lseek(fd, 0, SEEK_SET))
+ return;
+
+ memset(&map, 0, sizeof(map)); /* make sure it's sane */
+ map.tac_mapversion = MAP_FILE_VERSION;
+ (void)gettimeofday((struct timeval *)&map.tac_tv, NULL);
+
+ while((cnt=read(fd, &tmap, sizeof tmap)) == sizeof tmap) {
+ if(!tmap.tac_mapversion || tmap.tac_mapversion > MAP_FILE_VERSION ||
+ ((tmap.tac_mapuid || tmap.tac_mappedname) &&
+ tmap.tac_session && invalid_session(tmap.tac_session))) {
+ off_t off = (off_t)-cnt;
+ syslog(LOG_WARNING, "%s: Cleaning up stale entry in %s uid=%d, "
+ "sess=%d, mapuser=%s", libname, mapfile, tmap.tac_mapuid,
+ tmap.tac_session, tmap.tac_mappedname);
+ if(lseek(fd, off, SEEK_CUR) == -1) {
+ syslog(LOG_ERR,
+ "%s: rewrite seek failed on tacacs client_map_file %s: %m",
+ libname, mapfile);
+ break; /* we can't do anything else */
+ }
+ else if(write(fd, &map, sizeof map) != sizeof map) {
+ /* future lookups will fail... */
+ syslog(LOG_ERR, "%s unable to write tacacs client_map_file "
+ "%s: %m", libname, mapfile);
+ }
+ }
+ }
+
+ /*
+ * could lead to missing other entries if this was the add call and it
+ * fails, but there isn't much we can do about it.
+ */
+ (void)lseek(fd, 0, SEEK_SET);
+}
+
+
+/*
+ * Create an entry for the mapped user in our lookup file, with the info
+ * that will be needed by the audit and nss plugins.
+ *
+ * if olduser is NULL, then we are doing cleanup after logout, etc.
+ * If olduser is non-null we are writing the mapping entry to the map file
+ * If adding a mapping entry, walk the file to see if there is an unused
+ * entry that we can re-use. We take an exclusive flock here, shared in
+ * the lookup code, to avoid corrupting the file.
+ *
+ * Because there is a possibility of stale entries, validate and cleanup
+ * whenever we are doing the update.
+ * Stale entries can occur when somebody kills, e.g., the session parent
+ * login or sshd, nothing is left around to do the cleanup, and the entry could
+ * remain forever. update_loguid() does this on every add and delete.
+ *
+ * This would be static, but it needs to be exported to pam_tacplus.
+ * It is not a public entry point.
+*/
+
+static void
+update_loguid(char *newuser, char *olduser, char *rhost, uint16_t flags)
+{
+ struct tacacs_mapping map, tmap;
+ int fd, cnt, foundmatch = 0;
+ uid_t auid;
+ unsigned session;
+
+ fd = open_map();
+ if(fd == -1)
+ return;
+
+ if(flock(fd, LOCK_EX))
+ syslog(LOG_WARNING, "%s unable to lock tacacs client_map_file %s: %m,"
+ " proceeding anyway", libname, mapfile);
+
+ if(olduser) /* check and cleanup before adding */
+ chk_cleanup_map(fd);
+
+ memset(&map, 0, sizeof(map)); /* make sure it's sane */
+ auid = audit_getloginuid();
+ session = map_get_sessionid();
+
+ if(olduser) {
+ /* so we can map back for later accounting and for nss_tacplus; newuser
+ * *should* always be non-null. olduser will be NULL at logout */
+ snprintf(map.tac_logname, sizeof map.tac_logname, "%s",
+ newuser ? newuser : "");
+ snprintf(map.tac_mappedname, sizeof map.tac_mappedname, "%s",
+ olduser ? olduser : "");
+ snprintf(map.tac_rhost, sizeof map.tac_rhost, "%s",
+ rhost ? rhost : "");
+ map.tac_mapuid = auid;
+ map.tac_session = session;
+ map.tac_mapflags = (uint16_t)(flags & MAP_USERHOMEDIR);
+ }
+
+ (void)gettimeofday((struct timeval *)&map.tac_tv, NULL);
+ map.tac_mapversion = MAP_FILE_VERSION;
+
+ while(!foundmatch && (cnt=read(fd, &tmap, sizeof tmap)) == sizeof tmap) {
+ if(olduser && !tmap.tac_mapuid && !tmap.tac_session) {
+ foundmatch = 1; /* found an empty slot to use. */
+ }
+ if(!olduser && is_mapmatch(&tmap, MATCH_LOGIN, newuser, auid,
+ session)) {
+ foundmatch = 1;
+ }
+ }
+ if(cnt > 0 && cnt != sizeof map)
+ syslog(LOG_WARNING,
+ "%s: corrupted tacacs client_map_file %s: incorrect size %d read",
+ libname, mapfile, cnt);
+
+ if(!olduser && !foundmatch) {
+ goto done;
+ }
+
+ if(foundmatch) { /* found entry to overwrite, either to NULL or re-use */
+ off_t off = (off_t)-cnt;
+ if(lseek(fd, off, SEEK_CUR) == -1) {
+ syslog(LOG_ERR,
+ "%s: rewrite seek failed on tacacs client_map_file %s: %m",
+ libname, mapfile);
+ goto done;
+ }
+ }
+ else if(!newuser) {
+ /*
+ * if we didn't find entry to clear, something went wrong,
+ * so don't write an empty entry at the end.
+ */
+ goto done;
+ }
+
+ /* either overwrite an existing entry, or write new at end */
+ if(write(fd, &map, sizeof map) != sizeof map) {
+ /* future lookups will fail... */
+ syslog(LOG_ERR, "%s unable to write tacacs client_map_file %s: %m",
+ libname, mapfile);
+ }
+done:
+ if(!olduser) /* check and cleanup after deleting */
+ chk_cleanup_map(fd);
+ (void)flock(fd, LOCK_UN);
+ (void)fsync(fd);
+ close(fd);
+}
+
+/* entry point from pam_tacplus for cleanup on close (logout) */
+void
+__update_loguid(char *newuser)
+{
+ update_loguid(newuser, NULL, NULL, 0);
+}
+
+/*
+ * Set the audit login uid to be immutable; not supported on older libs/kernsls
+ */
+void set_auid_immutable(void)
+{
+ static int first = 1;
+ int fd;
+
+ if(!first)
+ return;
+ first = 0; /* only try once */
+ fd = audit_open();
+ if(fd == -1)
+ return; /* this should never happen */
+ if(audit_set_loginuid_immutable(fd) < 0)
+ syslog(LOG_WARNING, "%s: Unable to set loginuid to be immutable: %m", libname);
+ close(fd);
+}
+
+/*
+ * Check to see if login name found in /etc/passwd. If so, use it. If not
+ * try to map to a localuser tacacsN where N <= to the TACACS+ privilege level.
+ * The NSS lookup code needs to match this same algorithm.
+ *
+ * Returns 1 if user was mapped (!islocal), 0 if not mapped
+ */
+int
+update_mapuser(char *user, unsigned priv_level, char *rhost, unsigned flags)
+{
+ FILE *pwfile;
+ struct passwd *ent;
+ char tacuser[9]; /* "tacacs" + up to two digits plus 0 */
+ int islocal, foundtac;
+ unsigned priv = priv_level;
+ unsigned isrestrict = 0;
+ uid_t luid=0, tuid=0;
+
+ pwfile = fopen("/etc/passwd", "r");
+ if(!pwfile) {
+ syslog(LOG_WARNING, "%s: failed to open /etc/passwd: %m", libname);
+ return 0;
+ }
+
+recheck:
+ snprintf(tacuser, sizeof tacuser, "tacacs%u", priv);
+ for(islocal = foundtac = 0; (!islocal || !foundtac) &&
+ (ent = fgetpwent(pwfile)); ) {
+ if(!ent->pw_name)
+ continue; /* shouldn't happen */
+ if(!strcmp(ent->pw_name, tacuser)) {
+ foundtac++;
+ isrestrict = *ent->pw_shell == 'r';
+ tuid = ent->pw_uid;
+ }
+ }
+ if(islocal || foundtac) {
+ uint16_t homeflag;
+ fclose(pwfile);
+ pwfile = NULL;
+ /*
+ * If priv-level==N, and tacacsN isnt local, but tacacsM (0<=M<N)
+ * is present, we fallback to that lower level (with a warning logged).
+ * This sets the session ID (/proc/PID/sessionid) as a side effect, and
+ * that sessionid will remain the same for all child processes (unless
+ * something "incorrectly", calls audit_setloginuid() again.
+ *
+ * We call it here, instead of requiring pam_loginuid in pam.d/sshd,
+ * login, etc. because we need the info earlier than it is really
+ * possible via the normal pam auth/session sequencing.
+ */
+ audit_setloginuid(islocal?luid:tuid); /* set auid */
+ /*
+ * if USERHOMEDIR is set, we'll save that flag, and libnss-tacplus
+ * will return the login name in the pw_dir field replacing the
+ * local tacacsN homedir, unless the shell is a restricted shell,
+ * indicating that per-command authorization is enabled.
+ */
+ homeflag = (foundtac && !isrestrict) ? flags&MAP_USERHOMEDIR : 0;
+ update_loguid(user, islocal?user:tacuser, rhost, homeflag);
+ set_auid_immutable();
+ if(debug && !islocal && priv != priv_level)
+ syslog(LOG_DEBUG, "%s: Did not find local tacacs%u , using %s",
+ libname, priv_level, tacuser);
+ }
+ else if(priv > 0) {
+ priv--;
+ rewind(pwfile);
+ goto recheck;
+ }
+ if(pwfile)
+ fclose(pwfile);
+ return !islocal;
+}
+
+
+/*
+ * lookup a uid only in the local password file (to avoid tacacs recursion).
+ * This is supposed to be the mapped user, which should always be a local
+ * user, so we don't need to care about ldap or other remote mechanisms.
+ * Returns a pointer to strdup'ed memory, if found. Caller must free,
+ * or it will leak.
+ */
+static char *lookup_local_uid(uid_t auid)
+{
+ FILE *pwfile;
+ struct passwd *ent;
+ char *pwname = NULL; /* will be strdup'ed on success */
+
+ pwfile = fopen("/etc/passwd", "r");
+ if(!pwfile) {
+ syslog(LOG_WARNING, "%s: failed to open /etc/passwd: %m", libname);
+ return NULL;
+ }
+ while((ent = fgetpwent(pwfile)) && ent->pw_uid != auid)
+ ;
+ if(ent)
+ pwname = strdup(ent->pw_name);
+ fclose(pwfile);
+ return pwname;
+}
+
+/*
+ * If a mapped user entry already exists, we are probably being
+ * used for su or sudo, so we need to get the original user password,
+ * rather than the mapped user (the generic NSS lookup doesn't need
+ * the password).
+ * Never lookup for uid == 0 (login process, or root doing sudo), to avoid
+ * causing any issues (and because it's pointless).
+ *
+ * If auid != uid, and audit session ID already set, then do the lookup.
+ *
+ * We return strndup'ed memory on success, which will be leaked if not freed.
+ * That's OK, given that this is typically called only once per program, and
+ * that usernames are short.
+ */
+char *get_user_to_auth(char *pamuser)
+{
+ char *mapuser, *origuser;
+ unsigned session;
+ uid_t auid;
+
+ if(pamuser == NULL)
+ return NULL;
+
+ auid = audit_getloginuid();
+ if(auid == (uid_t)-1 || !auid)
+ return pamuser;
+ session = map_get_sessionid();
+ if(session == ~0U) /* sessionid not set or not enabled */
+ return pamuser;
+
+ mapuser = lookup_local_uid(auid);
+ if(!mapuser)
+ return pamuser;
+
+ if(strcmp(pamuser, mapuser)) {
+ free(mapuser);
+ return pamuser;
+ }
+ free(mapuser); /* done now */
+
+ /* returns malloced string of original user, if found, which will
+ * be a memory leak, but that shouldn't matter
+ */
+ origuser = lookup_logname(pamuser, auid, session, NULL, NULL);
+ return origuser ? origuser : pamuser;
+}
diff --git a/map_tacplus_user.h b/map_tacplus_user.h
new file mode 100644
index 0000000..9bc2dcb
--- /dev/null
+++ b/map_tacplus_user.h
@@ -0,0 +1,106 @@
+/*
+ * Copyright 2015, 2016, Cumulus Networks, Inc. All rights reserved.
+ * All Rights Reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Author: Dave Olson <olson@cumulusnetworks.com>
+ */
+
+#ifndef MAP_TACPLUS_USER_H
+#define MAP_TACPLUS_USER_H
+
+#include <stdint.h>
+#include <time.h>
+#include <pwd.h>
+#include <utmp.h>
+
+#define MAP_TACPLUS_FILE "/var/run/tacacs_client_map"
+
+#define MAP_FILE_VERSION 2 /* version two adds tac_mapflags (compatible) */
+
+#define MAP_USERHOMEDIR 0x1 /* tac_mapflags: separate homedirs per account */
+
+/*
+ * Structure to maintain mapping between login name and mapped tacacs name.
+ * Only live while session is active. Like utmp, designed to re-use slots
+ * after session is gone.
+ * Designed so that it should have the same layout in 32 and 64 bit,
+ * although currently only in use on 64 bit systems.
+ * Unlike utmp we do not maintain a login pid, because the PID we could
+ * record will not be the PID we want check.
+ */
+struct tacacs_mapping {
+ struct timeval tac_tv; /* only used for debug for now */
+# if __WORDSIZE == 32
+ uint32_t __fill__[2]; /* to keep alignment the same for 32 and 64 bit */
+#endif
+ uint16_t tac_mapversion; /* mapping version that wrote this file */
+ uint16_t tac_mapflags; /* flags such as MAP_USERHOMEDIR */
+ uint32_t tac_session; /* session ID */
+ uid_t tac_mapuid; /* for faster lookup, the login auid */
+ char tac_logname[UT_NAMESIZE+1]; /* login name. from utmp.h, + 1 for \0 */
+ char tac_mappedname[UT_NAMESIZE+1]; /* mapped name, for uid we are using */
+ char tac_rhost[UT_HOSTSIZE+1]; /* ssh, etc. originating host, for logging */
+};
+
+/* update the mapped user database */
+int update_mapuser(char *user, unsigned priv_level,
+ char *host, unsigned); /* returns true/false */
+char *get_user_to_auth(char *pamuser); /* returns NULL or strdup'ed memory */
+unsigned map_get_sessionid(void); /* return the sessionid for this session */
+
+/*
+ * Lookup the mapped name (i.e. tacacs15) to see if there is a match, in the
+ * mapping file. and return the mapped original login name, if so. Otherwise
+ * returns the name passed as first argument. Passing name as NULL
+ * requests match on auid and session only.
+ *
+ * If the returned pointer != first arg and non-NULL, caller should free it.
+ *
+ * This only works while a mapped user is logged in, and since the auid and
+ * session are lookup keys, only for processes that are descendents
+ * of the mapped login, unless they are passed as wildcards (-1)
+ *
+ * if host is non-NULL, *host is set to the originating rhost, if any
+ * It is a malloc'ed entry, and should be freed by the caller
+ */
+char *lookup_logname(const char *mapname, uid_t auid, unsigned session,
+ char **host, uint16_t *flags);
+
+/*
+ * Similar to lookup_logname(), but by uid.
+ * The same caveat applies; only works for descendent processes.
+ * Returns the original login username, and the mapped name
+ * in the copied to the buffered pointed to by mapped
+ * Returns NULL if not found. If non-NULL, the returned
+ * pointer should be freed by the caller.
+ */
+char *lookup_mapuid(uid_t uid, uid_t auid, unsigned session,
+ char *mappedname, size_t maplen, uint16_t *flags);
+
+/*
+ * Like lookup_logname(), but matches on the original login name,
+ * and returns the matching mapped name (e.g, tacacs0) if found,
+ * otherwise returns the logname argument. auid and session
+ * will most commonly be -1 wildcards for this function.
+ */
+char *lookup_mapname(const char *logname, uid_t auid, unsigned session,
+ char **host, uint16_t *flags);
+
+/* This is not a public entry point, it's a helper routine for pam_tacplus */
+void __update_loguid(char *);
+
+#endif
diff --git a/tacplus.sudo b/tacplus.sudo
new file mode 100644
index 0000000..bc90883
--- /dev/null
+++ b/tacplus.sudo
@@ -0,0 +1,18 @@
+# This file is part of the libtacplus-map package.
+# It allow tacacs privilege level 15 users (mapped to local user tacacs15)
+# to sudo without restrictions, so they can do all switch setup and
+# administration. The tacacs15 user is added by the same package, and
+# is configured to be a disabled login
+tacacs15 ALL=(ALL:ALL) ALL
+
+# If you want to allow privileged tacacs users (level 15) to execute
+# sudo without a password, comment out the tacacs 15 line above, and
+# uncomment out the line below:
+# tacacs15 ALL=(ALL:ALL) NOPASSWD:NOEXEC: ALL
+
+# Allow any tacacs group login to run this set of commands. this is just a
+# demonstration.
+# This example uses group tacacs, if you want all tacacs group users
+# to be able to run some commands thorugh sudo.
+# %tacacs ALL = (root) NOPASSWD:NOEXEC: /usr/bin/whoami
+