diff options
-rw-r--r-- | AUTHORS | 7 | ||||
-rw-r--r-- | COPYING | 255 | ||||
-rw-r--r-- | ChangeLog | 5 | ||||
-rw-r--r-- | Makefile.am | 39 | ||||
-rw-r--r-- | NEWS | 1 | ||||
-rw-r--r-- | README | 51 | ||||
-rwxr-xr-x | auto.sh | 4 | ||||
-rw-r--r-- | config/ltoptions.m4 | 384 | ||||
-rw-r--r-- | config/ltsugar.m4 | 123 | ||||
-rw-r--r-- | config/ltversion.m4 | 23 | ||||
-rw-r--r-- | config/lt~obsolete.m4 | 98 | ||||
-rw-r--r-- | configure.ac | 69 | ||||
-rw-r--r-- | debian/README.source | 5 | ||||
-rw-r--r-- | debian/changelog | 37 | ||||
-rw-r--r-- | debian/compat | 1 | ||||
-rw-r--r-- | debian/control | 22 | ||||
-rw-r--r-- | debian/copyright | 27 | ||||
-rw-r--r-- | debian/libtacplus-map-dev.install | 2 | ||||
-rw-r--r-- | debian/libtacplus-map1.install | 2 | ||||
-rw-r--r-- | debian/libtacplus-map1.postinst | 51 | ||||
-rw-r--r-- | debian/libtacplus-map1.symbols | 10 | ||||
-rwxr-xr-x | debian/rules | 13 | ||||
-rw-r--r-- | debian/source/format | 2 | ||||
-rw-r--r-- | dumpmap.c | 79 | ||||
-rw-r--r-- | map_tacplus_user.c | 689 | ||||
-rw-r--r-- | map_tacplus_user.h | 106 | ||||
-rw-r--r-- | tacplus.sudo | 18 |
27 files changed, 2123 insertions, 0 deletions
@@ -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> + @@ -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 $@ @@ -0,0 +1 @@ +Check 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> @@ -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 + |