1
0
mirror of https://git.FreeBSD.org/src.git synced 2026-06-02 11:24:32 +00:00

pam-krb5: Import/add pam-krb5 from eyeire.org

From https://www.eyrie.org/~eagle/software/pam-krb5/:

pam-krb5 provides a Kerberos PAM module that supports authentication,
user ticket cache handling, simple authorization (via .k5login or
checking Kerberos principals against local usernames), and password
changing. It can be configured through either options in the PAM
configuration itself or through entries in the system krb5.conf file,
and it tries to work around PAM implementation flaws in commonly-used
PAM-enabled applications such as OpenSSH and xdm. It supports both
PKINIT and FAST to the extent that the underlying Kerberos libraries
support these features.

The reason for this import is to provide an MIT KRB5 compatible
pam_krb5 PAM module. The existing pam_krb5 in FreeBS only works
with Heimdal.

Sponsored by:	The FreeBSD Foundation
This commit is contained in:
Cy Schubert
2025-04-16 19:13:41 -07:00
commit 24f0b4ca2d
254 changed files with 29790 additions and 0 deletions
+30
View File
@@ -0,0 +1,30 @@
# Configuration for clang-format automated reformatting. -*- yaml -*-
#
# The canonical version of this file is maintained in the rra-c-util package,
# which can be found at <https://www.eyrie.org/~eagle/software/rra-c-util/>.
#
# Copyright 2020-2021 Russ Allbery <eagle@eyrie.org>
#
# Copying and distribution of this file, with or without modification, are
# permitted in any medium without royalty provided the copyright notice
# and this notice are preserved. This file is offered as-is, without any
# warranty.
#
# SPDX-License-Identifier: FSFAP
---
Language: Cpp
BasedOnStyle: LLVM
AlignConsecutiveMacros: true
AlignEscapedNewlines: Left
AllowShortEnumsOnASingleLine: false
AlwaysBreakAfterReturnType: AllDefinitions
BreakBeforeBinaryOperators: NonAssignment
BreakBeforeBraces: WebKit
ColumnLimit: 79
IndentPPDirectives: AfterHash
IndentWidth: 4
IndentWrappedFunctionNames: false
MaxEmptyLinesToKeep: 2
SpaceAfterCStyleCast: true
---
+6
View File
@@ -0,0 +1,6 @@
version: 2
updates:
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "weekly"
+44
View File
@@ -0,0 +1,44 @@
name: build
on:
push:
branches-ignore:
- "debian/**"
- "pristine-tar"
- "ubuntu/**"
- "upstream/**"
tags:
- "release/*"
pull_request:
branches:
- master
jobs:
build:
runs-on: ubuntu-latest
env:
AUTHOR_TESTING: 1
C_TAP_VERBOSE: 1
strategy:
fail-fast: false
matrix:
kerberos:
- "mit"
- "heimdal"
steps:
- uses: actions/checkout@v2
- name: install
run: sudo ci/install
- name: kdc-setup-mit
run: sudo ci/kdc-setup-mit
if: matrix.kerberos == 'mit'
- name: kdc-setup-heimdal
run: sudo ci/kdc-setup-heimdal
if: matrix.kerberos == 'heimdal'
- name: test
run: ci/test
env:
KERBEROS: ${{ matrix.kerberos }}
+344
View File
@@ -0,0 +1,344 @@
Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
Comment: This file documents the copyright statements and licenses for
every file in this package in a machine-readable format. For a less
detailed, higher-level overview, see README.
.
For any copyright year range specified as YYYY-ZZZZ in this file, the
range specifies every single year in that closed interval.
Files: *
Copyright: 1999-2000 Frank Cusack <fcusack@fcusack.com>
2005 Andres Salomon <dilinger@debian.org>
2005-2010, 2014-2015, 2017, 2020-2021 Russ Allbery <eagle@eyrie.org>
2008-2014 The Board of Trustees of the Leland Stanford Junior University
License: BSD-3-clause or GPL-1+
Files: .clang-format docs/pam_krb5.5 docs/pam_krb5.pod pam-util/vector.c
pam-util/vector.h portable/asprintf.c portable/dummy.c
portable/issetugid.c portable/kadmin.h portable/krb5-extra.c
portable/krb5.h portable/macros.h portable/mkstemp.c portable/pam.h
portable/pam_syslog.c portable/pam_vsyslog.c portable/reallocarray.c
portable/stdbool.h portable/strndup.c portable/system.h tests/README
tests/TESTS tests/config/README tests/data/cppcheck.supp
tests/fakepam/README tests/pam-util/vector-t.c tests/portable/asprintf-t.c
tests/portable/mkstemp-t.c tests/portable/strndup-t.c
Copyright: 2005-2012, 2014-2021 Russ Allbery <eagle@eyrie.org>
2006-2014 The Board of Trustees of the Leland Stanford Junior University
License: all-permissive
Copying and distribution of this file, with or without modification, are
permitted in any medium without royalty provided the copyright notice and
this notice are preserved. This file is offered as-is, without any
warranty.
Files: Makefile.in
Copyright: 1994-2021 Free Software Foundation, Inc.
1999-2000 Frank Cusack <fcusack@fcusack.com>
2005 Andres Salomon <dilinger@debian.org>
2005-2007, 2014, 2017, 2020-2021 Russ Allbery <eagle@eyrie.org>
2009, 2011-2012
The Board of Trustees of the Leland Stanford Junior University
License: FSF-unlimited, and BSD-3-clause or GPL-1+
Files: aclocal.m4 m4/ltoptions.m4 m4/ltsugar.m4 m4/ltversion.m4
m4/lt~obsolete.m4
Copyright: 1996-2021 Free Software Foundation, Inc.
License: FSF-unlimited
Files: build-aux/ar-lib build-aux/compile build-aux/depcomp
build-aux/missing
Copyright: 1996-2021 Free Software Foundation, Inc.
License: GPL-2+ with Autoconf exception or BSD-3-clause or GPL-1+
Files: build-aux/config.guess build-aux/config.sub
Copyright: 1992-2018 Free Software Foundation, Inc.
License: GPL-3+ with Autoconf exception or BSD-3-clause or GPL-1+
Files: build-aux/install-sh
Copyright: 1994 X Consortium
License: X11
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to permit
persons to whom the Software is furnished to do so, subject to the
following conditions:
.
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.
.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
.
Except as contained in this notice, the name of the X Consortium shall
not be used in advertising or otherwise to promote the sale, use or other
dealings in this Software without prior written authorization from the X
Consortium.
Files: build-aux/ltmain.sh
Copyright: 1996-2015 Free Software Foundation, Inc.
License: GPL-2+ with Libtool exception or BSD-3-clause or GPL-1+, and GPL-3+ with Libtool exception or BSD-3-clause or GPL-1+, and GPL-3+
Files: ci/install ci/kdc-setup-heimdal ci/kdc-setup-mit ci/test
pam-util/args.c pam-util/args.h pam-util/logging.c pam-util/logging.h
pam-util/options.c pam-util/options.h tests/data/generate-krb5-conf
tests/data/valgrind.supp tests/docs/pod-spelling-t tests/docs/pod-t
tests/docs/spdx-license-t tests/fakepam/config.c tests/fakepam/data.c
tests/fakepam/general.c tests/fakepam/internal.h tests/fakepam/kuserok.c
tests/fakepam/logging.c tests/fakepam/pam.h tests/fakepam/script.c
tests/fakepam/script.h tests/pam-util/args-t.c tests/pam-util/fakepam-t.c
tests/pam-util/logging-t.c tests/pam-util/options-t.c tests/runtests.c
tests/style/obsolete-strings-t tests/tap/basic.c tests/tap/basic.h
tests/tap/kadmin.c tests/tap/kadmin.h tests/tap/kerberos.c
tests/tap/kerberos.h tests/tap/libtap.sh tests/tap/macros.h
tests/tap/perl/Test/RRA.pm tests/tap/perl/Test/RRA/Automake.pm
tests/tap/perl/Test/RRA/Config.pm tests/tap/process.c tests/tap/process.h
tests/tap/string.c tests/tap/string.h tests/valgrind/logs-t
Copyright: 2000-2002, 2004-2021 Russ Allbery <eagle@eyrie.org>
2001-2002, 2004-2014
The Board of Trustees of the Leland Stanford Junior University
License: Expat
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to permit
persons to whom the Software is furnished to do so, subject to the
following conditions:
.
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.
.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT
OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR
THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Files: configure
Copyright: 1992-1996, 1998-2017, 2020-2021 Free Software Foundation, Inc.
License: FSF-configure, and GPL-2+ with Libtool exception or BSD-3-clause or GPL-1+
Files: m4/cc-flags.m4
Copyright: 2006, 2009, 2016 Internet Systems Consortium, Inc.
2016-2021 Russ Allbery <eagle@eyrie.org>
License: ISC
Permission to use, copy, modify, and distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
.
THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY
SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
Files: m4/clang.m4 m4/kadm5clnt.m4 m4/krb5-config.m4 m4/krb5-pkinit.m4
m4/krb5.m4 m4/ld-version.m4 m4/lib-depends.m4 m4/lib-helper.m4
m4/lib-pathname.m4 m4/pam-const.m4
Copyright: 2005-2014
The Board of Trustees of the Leland Stanford Junior University
2007, 2015, 2018, 2020-2021 Russ Allbery <eagle@eyrie.org>
2007-2008 Markus Moeller
2008-2010 Free Software Foundation, Inc.
License: unlimited
This file is free software; the authors give unlimited permission to copy
and/or distribute it, with or without modifications, as long as this
notice is preserved.
Files: m4/libtool.m4
Copyright: 1996-2001, 2003-2015 Free Software Foundation, Inc.
License: FSF-unlimited, and GPL-2+ with Libtool exception or BSD-3-clause or GPL-1+
Files: portable/krb5-profile.c
Copyright: 1985-2005 the Massachusetts Institute of Technology
License: MIT-Kerberos
Export of this software from the United States of America may require
a specific license from the United States Government. It is the
responsibility of any person or organization contemplating export to
obtain such a license before exporting.
.
WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
distribute this software and its documentation for any purpose and
without fee is hereby granted, provided that the above copyright
notice appear in all copies and that both that copyright notice and
this permission notice appear in supporting documentation, and that
the name of M.I.T. not be used in advertising or publicity pertaining
to distribution of the software without specific, written prior
permission. Furthermore if you modify this software you must label
your software as modified software and not distribute it in such a
fashion that it might be confused with the original MIT software.
M.I.T. makes no representations about the suitability of this software
for any purpose. It is provided "as is" without express or implied
warranty.
.
THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
.
Individual source code files are copyright MIT, Cygnus Support,
OpenVision, Oracle, Sun Soft, FundsXpress, and others.
.
Project Athena, Athena, Athena MUSE, Discuss, Hesiod, Kerberos, Moira,
and Zephyr are trademarks of the Massachusetts Institute of Technology
(MIT). No commercial use of these trademarks may be made without
prior written permission of MIT.
.
"Commercial use" means use of a name in a product or other for-profit
manner. It does NOT prevent a commercial firm from referring to the
MIT trademarks in order to convey information (although in doing so,
recognition of their trademark status should be given).
License: BSD-3-clause or GPL-1+
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
.
1. Redistributions of source code must retain the above copyright
notice, and the entire permission notice in its entirety, including
the disclaimer of warranties.
.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
.
3. The name of the author may not be used to endorse or promote products
derived from this software without specific prior written permission.
.
ALTERNATIVELY, this product may be distributed under the terms of the
GNU General Public License, in which case the provisions of the GPL
are required INSTEAD OF the above restrictions. (This clause is
necessary due to a potential bad interaction between the GPL and the
restrictions contained in a BSD-style copyright.)
.
THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
DAMAGE.
License: FSF-configure
This script is free software; the Free Software Foundation gives unlimited
permission to copy, distribute and modify it.
License: FSF-unlimited
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.
.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY, to the extent permitted by law; without even the
implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
License: GPL-2+ with Autoconf exception
This file 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, see <https://www.gnu.org/licenses/>.
.
As a special exception to the GNU General Public License, if you
distribute this file as part of a program that contains a configuration
script generated by Autoconf, you may include it under the same
distribution terms that you use for the rest of that program.
Comment: The option described in the license has been accepted and these
files are distributed under the same terms as the package as a whole, as
described at the top of this file.
License: GPL-2+ with Libtool exception
This file is part of GNU Libtool.
.
GNU Libtool 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.
.
As a special exception to the GNU General Public License, if you
distribute this file as part of a program or library that is built using
GNU Libtool, you may include this file under the same distribution terms
that you use for the rest of that program.
.
GNU Libtool 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.
Comment: The option described in the license has been accepted and these
files are distributed under the same terms as the package as a whole, as
described at the top of this file.
License: GPL-3+
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 3 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.
License: GPL-3+ with Autoconf exception
This file 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 3 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, see <https://www.gnu.org/licenses/>.
.
As a special exception to the GNU General Public License, if you
distribute this file as part of a program that contains a configuration
script generated by Autoconf, you may include it under the same
distribution terms that you use for the rest of that program. This
Exception is an additional permission under section 7 of the GNU General
Public License, version 3 ("GPLv3").
Comment: The option described in the license has been accepted and these
files are distributed under the same terms as the package as a whole, as
described at the top of this file.
License: GPL-3+ with Libtool exception
GNU Libtool 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 3 of the License, or
(at your option) any later version.
.
As a special exception to the GNU General Public License, if you
distribute this file as part of a program or library that is built
using GNU Libtool, you may include this file under the same
distribution terms that you use for the rest of that program.
.
GNU Libtool 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.
Comment: The option described in the license has been accepted and these
files are distributed under the same terms as the package as a whole, as
described at the top of this file.
+210
View File
@@ -0,0 +1,210 @@
# Automake makefile for pam-krb5.
#
# Written by Russ Allbery <eagle@eyrie.org>
# Copyright 2005-2007, 2014, 2017, 2020-2021 Russ Allbery <eagle@eyrie.org>
# Copyright 2009, 2011-2012
# The Board of Trustees of the Leland Stanford Junior University
# Copyright 2005 Andres Salomon <dilinger@debian.org>
# Copyright 1999-2000 Frank Cusack <fcusack@fcusack.com>
#
# SPDX-License-Identifier: BSD-3-clause or GPL-1+
ACLOCAL_AMFLAGS = -I m4
EXTRA_DIST = .clang-format .gitignore .github LICENSE README.md bootstrap \
ci/README.md ci/files/heimdal/heimdal-kdc \
ci/files/heimdal/kadmind.acl ci/files/heimdal/kdc.conf \
ci/files/heimdal/krb5.conf ci/files/heimdal/pki-mapping \
ci/files/mit/extensions.client ci/files/mit/extensions.kdc \
ci/files/mit/kadm5.acl ci/files/mit/kdc.conf ci/files/mit/krb5.conf \
ci/kdc-setup-heimdal ci/kdc-setup-mit ci/install ci/test \
docs/docknot.yaml docs/pam_krb5.pod module/pam_krb5.map \
module/pam_krb5.sym tests/README tests/TESTS tests/config/README \
tests/data/cppcheck.supp tests/data/generate-krb5-conf \
tests/data/krb5-pam.conf tests/data/krb5.conf tests/data/perl.conf \
tests/data/scripts tests/data/valgrind.supp \
tests/docs/pod-spelling-t tests/docs/pod-t \
tests/docs/spdx-license-t tests/fakepam/README tests/tap/libtap.sh \
tests/tap/perl/Test/RRA.pm tests/tap/perl/Test/RRA/Automake.pm \
tests/tap/perl/Test/RRA/Config.pm tests/style/obsolete-strings-t \
tests/valgrind/logs-t
# Everything we build needs the Kerbeors headers and library flags.
AM_CPPFLAGS = $(KRB5_CPPFLAGS)
AM_LDFLAGS = $(KRB5_LDFLAGS)
noinst_LTLIBRARIES = pam-util/libpamutil.la portable/libportable.la
portable_libportable_la_SOURCES = portable/dummy.c portable/kadmin.h \
portable/krb5.h portable/macros.h portable/pam.h portable/stdbool.h \
portable/system.h
portable_libportable_la_LIBADD = $(LTLIBOBJS)
pam_util_libpamutil_la_SOURCES = pam-util/args.c pam-util/args.h \
pam-util/logging.c pam-util/logging.h pam-util/options.c \
pam-util/options.h pam-util/vector.c pam-util/vector.h
if HAVE_LD_VERSION_SCRIPT
VERSION_LDFLAGS = -Wl,--version-script=${srcdir}/module/pam_krb5.map
else
VERSION_LDFLAGS = -export-symbols ${srcdir}/module/pam_krb5.sym
endif
pamdir = $(libdir)/security
pam_LTLIBRARIES = module/pam_krb5.la
module_pam_krb5_la_SOURCES = module/account.c module/alt-auth.c \
module/auth.c module/cache.c module/context.c module/fast.c \
module/internal.h module/options.c module/password.c \
module/prompting.c module/public.c module/setcred.c \
module/support.c
module_pam_krb5_la_LDFLAGS = -module -shared \
-avoid-version $(VERSION_LDFLAGS) $(AM_LDFLAGS)
module_pam_krb5_la_LIBADD = pam-util/libpamutil.la portable/libportable.la \
$(KRB5_LIBS)
dist_man_MANS = docs/pam_krb5.5
# The manual page is normally generated by the bootstrap script, but add a
# Makefile rule to regenerate it if it is modified.
docs/pam_krb5.5: $(srcdir)/docs/pam_krb5.pod
pod2man --release="$(VERSION)" --center=pam-krb5 -s 5 \
$(srcdir)/docs/pam_krb5.pod > $@
# Work around the GNU Coding Standards, which leave all the Autoconf and
# Automake stuff around after make maintainer-clean, thus making that command
# mostly worthless.
DISTCLEANFILES = config.h.in~ configure~
MAINTAINERCLEANFILES = Makefile.in aclocal.m4 build-aux/compile \
build-aux/config.guess build-aux/config.sub build-aux/depcomp \
build-aux/install-sh build-aux/ltmain.sh build-aux/missing \
config.h.in configure docs/pam_krb5.5 m4/libtool.m4 m4/ltoptions.m4 \
m4/ltsugar.m4 m4/ltversion.m4 m4/lt~obsolete.m4
# Separate target for a human to request building everything with as many
# compiler warnings enabled as possible.
warnings:
$(MAKE) V=0 AM_CFLAGS='$(WARNINGS_CFLAGS) $(AM_CFLAGS)' \
KRB5_CPPFLAGS='$(KRB5_CPPFLAGS_WARNINGS)'
$(MAKE) V=0 AM_CFLAGS='$(WARNINGS_CFLAGS) $(AM_CFLAGS)' \
KRB5_CPPFLAGS='$(KRB5_CPPFLAGS_WARNINGS)' $(check_PROGRAMS)
# The bits below are for the test suite, not for the main package.
check_PROGRAMS = tests/runtests tests/module/alt-auth-t \
tests/module/bad-authtok-t tests/module/basic-t \
tests/module/cache-cleanup-t tests/module/cache-t \
tests/module/expired-t tests/module/fast-anon-t tests/module/fast-t \
tests/module/long-t tests/module/no-cache-t tests/module/pam-user-t \
tests/module/password-t tests/module/pkinit-t tests/module/realm-t \
tests/module/stacked-t tests/module/trace-t tests/pam-util/args-t \
tests/pam-util/fakepam-t tests/pam-util/logging-t \
tests/pam-util/options-t tests/pam-util/vector-t \
tests/portable/asprintf-t tests/portable/mkstemp-t \
tests/portable/strndup-t
tests_runtests_CPPFLAGS = -DC_TAP_SOURCE='"$(abs_top_srcdir)/tests"' \
-DC_TAP_BUILD='"$(abs_top_builddir)/tests"'
check_LIBRARIES = tests/fakepam/libfakepam.a tests/tap/libtap.a
tests_fakepam_libfakepam_a_SOURCES = tests/fakepam/config.c \
tests/fakepam/data.c tests/fakepam/general.c \
tests/fakepam/internal.h tests/fakepam/kuserok.c \
tests/fakepam/logging.c tests/fakepam/pam.h tests/fakepam/script.c \
tests/fakepam/script.h
tests_tap_libtap_a_CPPFLAGS = $(KADM5CLNT_CPPFLAGS) $(AM_CPPFLAGS)
tests_tap_libtap_a_SOURCES = tests/tap/basic.c tests/tap/basic.h \
tests/tap/kadmin.c tests/tap/kadmin.h tests/tap/kerberos.c \
tests/tap/kerberos.h tests/tap/macros.h tests/tap/process.c \
tests/tap/process.h tests/tap/string.c tests/tap/string.h
# The list of objects and libraries used for module testing by programs that
# link with the fake PAM library or with both it and the module.
MODULE_OBJECTS = module/account.lo module/alt-auth.lo module/auth.lo \
module/cache.lo module/context.lo module/fast.lo module/options.lo \
module/password.lo module/prompting.lo module/public.lo \
module/setcred.lo module/support.lo pam-util/libpamutil.la \
tests/fakepam/libfakepam.a
# The test programs themselves.
tests_module_alt_auth_t_LDADD = $(MODULE_OBJECTS) tests/tap/libtap.a \
portable/libportable.la $(KRB5_LIBS)
tests_module_bad_authtok_t_LDADD = $(MODULE_OBJECTS) tests/tap/libtap.a \
portable/libportable.la $(KRB5_LIBS)
tests_module_basic_t_LDADD = $(MODULE_OBJECTS) tests/tap/libtap.a \
portable/libportable.la $(KRB5_LIBS)
tests_module_cache_cleanup_t_LDADD = $(MODULE_OBJECTS) tests/tap/libtap.a \
portable/libportable.la $(KRB5_LIBS)
tests_module_cache_t_LDADD = $(MODULE_OBJECTS) tests/tap/libtap.a \
portable/libportable.la $(KRB5_LIBS)
tests_module_expired_t_LDADD = $(MODULE_OBJECTS) tests/tap/libtap.a \
portable/libportable.la $(KADM5CLNT_LDFLAGS) $(KADM5CLNT_LIBS) \
$(KRB5_LIBS)
tests_module_fast_anon_t_LDADD = $(MODULE_OBJECTS) tests/tap/libtap.a \
portable/libportable.la $(KRB5_LIBS)
tests_module_fast_t_LDADD = $(MODULE_OBJECTS) tests/tap/libtap.a \
portable/libportable.la $(KRB5_LIBS)
tests_module_long_t_LDADD = $(MODULE_OBJECTS) tests/tap/libtap.a \
portable/libportable.la $(KRB5_LIBS)
tests_module_no_cache_t_LDADD = $(MODULE_OBJECTS) tests/tap/libtap.a \
portable/libportable.la $(KRB5_LIBS)
tests_module_pam_user_t_LDADD = $(MODULE_OBJECTS) tests/tap/libtap.a \
portable/libportable.la $(KRB5_LIBS)
tests_module_password_t_LDADD = $(MODULE_OBJECTS) tests/tap/libtap.a \
portable/libportable.la $(KRB5_LIBS)
tests_module_pkinit_t_LDADD = $(MODULE_OBJECTS) tests/tap/libtap.a \
portable/libportable.la $(KRB5_LIBS)
tests_module_realm_t_LDADD = $(MODULE_OBJECTS) tests/tap/libtap.a \
portable/libportable.la $(KRB5_LIBS)
tests_module_stacked_t_LDADD = $(MODULE_OBJECTS) tests/tap/libtap.a \
portable/libportable.la $(KRB5_LIBS)
tests_module_trace_t_LDADD = $(MODULE_OBJECTS) tests/tap/libtap.a \
portable/libportable.la $(KRB5_LIBS)
tests_pam_util_args_t_LDADD = pam-util/libpamutil.la \
tests/fakepam/libfakepam.a tests/tap/libtap.a \
portable/libportable.la $(KRB5_LIBS)
tests_pam_util_fakepam_t_LDADD = tests/fakepam/libfakepam.a \
tests/tap/libtap.a portable/libportable.la
tests_pam_util_logging_t_LDADD = pam-util/libpamutil.la \
tests/fakepam/libfakepam.a tests/tap/libtap.a \
portable/libportable.la $(KRB5_LIBS)
tests_pam_util_options_t_LDADD = pam-util/libpamutil.la \
tests/fakepam/libfakepam.a tests/tap/libtap.a \
portable/libportable.la $(KRB5_LIBS)
tests_pam_util_vector_t_LDADD = pam-util/libpamutil.la \
tests/fakepam/libfakepam.a tests/tap/libtap.a \
portable/libportable.la
tests_portable_asprintf_t_SOURCES = tests/portable/asprintf-t.c \
tests/portable/asprintf.c
tests_portable_asprintf_t_LDADD = tests/tap/libtap.a portable/libportable.la
tests_portable_mkstemp_t_SOURCES = tests/portable/mkstemp-t.c \
tests/portable/mkstemp.c
tests_portable_mkstemp_t_LDADD = tests/tap/libtap.a portable/libportable.la
tests_portable_strndup_t_SOURCES = tests/portable/strndup-t.c \
tests/portable/strndup.c
tests_portable_strndup_t_LDADD = tests/tap/libtap.a portable/libportable.la
check-local: $(check_PROGRAMS)
cd tests && ./runtests -l '$(abs_top_srcdir)/tests/TESTS'
# Used by maintainers to check the source code with cppcheck.
check-cppcheck:
cd $(abs_top_srcdir) && \
find . -name .git -prune -o -name '*.[ch]' -print \
| cppcheck -q --force --error-exitcode=2 --file-list=- \
--suppressions-list=tests/data/cppcheck.supp \
--enable=warning,performance,portability,style
# The full path to valgrind and its options, used when doing valgrind
# testing.
VALGRIND_COMMAND = $(PATH_VALGRIND) --leak-check=full \
--trace-children=yes \
--trace-children-skip=/bin/sh,*/generate-krb5-conf \
--suppressions=$(abs_top_srcdir)/tests/data/valgrind.supp \
--log-file=$(abs_top_builddir)/tests/tmp/valgrind/log.%p
# Used by maintainers to run the main test suite under valgrind.
check-valgrind: $(check_PROGRAMS)
rm -rf $(abs_top_builddir)/tests/tmp
mkdir $(abs_top_builddir)/tests/tmp
mkdir $(abs_top_builddir)/tests/tmp/valgrind
C_TAP_VALGRIND="$(VALGRIND_COMMAND)" tests/runtests \
-l '$(abs_top_srcdir)/tests/TESTS'
# Used by maintainers to reformat all source code using clang-format and
# excluding some files.
reformat:
find . -name '*.[ch]' \! -name krb5-profile.c -print \
| xargs clang-format -style=file -i
+1215
View File
File diff suppressed because it is too large Load Diff
+641
View File
@@ -0,0 +1,641 @@
pam-krb5 4.11
(PAM module for Kerberos authentication)
Maintained by Russ Allbery <eagle@eyrie.org>
Copyright 2005-2010, 2014-2015, 2017, 2020-2021 Russ Allbery
<eagle@eyrie.org>. Copyright 2009-2011 The Board of Trustees of the
Leland Stanford Junior University. Copyright 2005 Andres Salomon
<dilinger@debian.org>. Copyright 1999-2000 Frank Cusack
<fcusack@fcusack.com>. This software is distributed under a BSD-style
license. Please see the section LICENSE below for more information.
BLURB
pam-krb5 is a Kerberos PAM module for either MIT Kerberos or Heimdal.
It supports ticket refreshing by screen savers, configurable
authorization handling, authentication of non-local accounts for network
services, password changing, and password expiration, as well as all the
standard expected PAM features. It works correctly with OpenSSH, even
with ChallengeResponseAuthentication and PrivilegeSeparation enabled,
and supports extensive configuration either by PAM options or in
krb5.conf or both. PKINIT is supported with recent versions of both MIT
Kerberos and Heimdal and FAST is supported with recent MIT Kerberos.
DESCRIPTION
pam-krb5 provides a Kerberos PAM module that supports authentication,
user ticket cache handling, simple authorization (via .k5login or
checking Kerberos principals against local usernames), and password
changing. It can be configured through either options in the PAM
configuration itself or through entries in the system krb5.conf file,
and it tries to work around PAM implementation flaws in commonly-used
PAM-enabled applications such as OpenSSH and xdm. It supports both
PKINIT and FAST to the extent that the underlying Kerberos libraries
support these features.
This is not the Kerberos PAM module maintained on Sourceforge and used
on Red Hat systems. It is an independent implementation that, if it
ever shared any common code, diverged long ago. It supports some
features that the Sourceforge module does not (particularly around
authorization), and does not support some options (particularly ones not
directly related to Kerberos) that it does. This module will never
support Kerberos v4 or AFS. For an AFS session module that works with
this module (or any other Kerberos PAM module), see pam-afs-session [1].
[1] https://www.eyrie.org/~eagle/software/pam-afs-session/
If there are other options besides AFS and Kerberos v4 support from the
Sourceforge PAM module that you're missing in this module, please let me
know.
REQUIREMENTS
Either MIT Kerberos (or Kerberos implementations based on it) or Heimdal
are supported. MIT Keberos 1.3 or later may be required; this module
has not been tested with earlier versions.
For PKINIT support, Heimdal 0.8rc1 or later or MIT Kerberos 1.6.3 or
later are required. Earlier MIT Kerberos 1.6 releases have a bug in
their handling of PKINIT options. MIT Kerberos 1.12 or later is
required to use the use_pkinit PAM option.
For FAST (Flexible Authentication Secure Tunneling) support, MIT
Kerberos 1.7 or higher is required. For anonymous FAST support,
anonymous authentication (generally anonymous PKINIT) support is
required in both the Kerberos libraries and in the local KDC.
This module should work on Linux and build with gcc or clang. It may
still work on Solaris and build with the Sun C compiler, but I have only
tested it on Linux recently. There is beta-quality support for the AIX
NAS Kerberos implementation that has not been tested in years. Other
PAM implementations will probably require some porting, although
untested build system support is present for FreeBSD, Mac OS X, and
HP-UX. I personally can only test on Linux and rely on others to report
problems on other operating systems.
Old versions of OpenSSH are known to call pam_authenticate followed by
pam_setcred(PAM_REINITIALIZE_CRED) without first calling
pam_open_session, thereby requesting that an existing ticket cache be
renewed (similar to what a screensaver would want) rather than
requesting a new ticket cache be created. Since this behavior is
indistinguishable at the PAM level from a screensaver, pam-krb5 when
used with these old versions of OpenSSH will refresh the ticket cache of
the OpenSSH daemon rather than setting up a new ticket cache for the
user. The resulting ticket cache will have the correct permissions
(this is not a security concern), but will not be named correctly or
referenced in the user's environment and will be overwritten by the next
user login. The best solution to this problem is to upgrade OpenSSH.
I'm not sure exactly when this problem was fixed, but at the very least
OpenSSH 4.3 and later do not exhibit it.
To bootstrap from a Git checkout, or if you change the Automake files
and need to regenerate Makefile.in, you will need Automake 1.11 or
later. For bootstrap or if you change configure.ac or any of the m4
files it includes and need to regenerate configure or config.h.in, you
will need Autoconf 2.64 or later. Perl is also required to generate
manual pages from a fresh Git checkout.
BUILDING AND INSTALLATION
You can build and install pam-krb5 with the standard commands:
./configure
make
make install
If you are building from a Git clone, first run ./bootstrap in the
source directory to generate the build files. make install will
probably have to be done as root. Building outside of the source
directory is also supported, if you wish, by creating an empty directory
and then running configure with the correct relative path.
The module will be installed in /usr/local/lib/security by default, but
expect to have to override this using --libdir. The correct
installation path for PAM modules varies considerably between systems.
The module will always be installed in a subdirectory named security
under the specified value of --libdir. On Red Hat Linux, for example,
--libdir=/usr/lib64 is appropriate to install the module into the system
PAM directory. On Debian's amd64 architecture,
--libdir=/usr/lib/x86_64-linux-gnu would be correct.
Normally, configure will use krb5-config to determine the flags to use
to compile with your Kerberos libraries. To specify a particular
krb5-config script to use, either set the PATH_KRB5_CONFIG environment
variable or pass it to configure like:
./configure PATH_KRB5_CONFIG=/path/to/krb5-config
If krb5-config isn't found, configure will look for the standard
Kerberos libraries in locations already searched by your compiler. If
the the krb5-config script first in your path is not the one
corresponding to the Kerberos libraries you want to use, or if your
Kerberos libraries and includes aren't in a location searched by default
by your compiler, you need to specify a different Kerberos installation
root via --with-krb5=PATH. For example:
./configure --with-krb5=/usr/pubsw
You can also individually set the paths to the include directory and the
library directory with --with-krb5-include and --with-krb5-lib. You may
need to do this if Autoconf can't figure out whether to use lib, lib32,
or lib64 on your platform.
To not use krb5-config and force library probing even if there is a
krb5-config script on your path, set PATH_KRB5_CONFIG to a nonexistent
path:
./configure PATH_KRB5_CONFIG=/nonexistent
krb5-config is not used and library probing is always done if either
--with-krb5-include or --with-krb5-lib are given.
Pass --enable-silent-rules to configure for a quieter build (similar to
the Linux kernel). Use make warnings instead of make to build with full
compiler warnings (requires either GCC or Clang and may require a
relatively current version of the compiler).
You can pass the --enable-reduced-depends flag to configure to try to
minimize the shared library dependencies encoded in the binaries. This
omits from the link line all the libraries included solely because other
libraries depend on them and instead links the programs only against
libraries whose APIs are called directly. This will only work with
shared libraries and will only work on platforms where shared libraries
properly encode their own dependencies (this includes most modern
platforms such as all Linux). It is intended primarily for building
packages for Linux distributions to avoid encoding unnecessary shared
library dependencies that make shared library migrations more difficult.
If none of the above made any sense to you, don't bother with this flag.
TESTING
pam-krb5 comes with a comprehensive test suite, but it requires some
configuration in order to test anything other than low-level utility
functions. For the full test suite, you will need to have a running KDC
in which you can create two test accounts, one with admin access to the
other. Using a test KDC environment, if you have one, is recommended.
Follow the instructions in tests/config/README to configure the test
suite.
Now, you can run the test suite with:
make check
If a test fails, you can run a single test with verbose output via:
tests/runtests -o <name-of-test>
Do this instead of running the test program directly since it will
ensure that necessary environment variables are set up.
The default libkadm5clnt library on the system must match the
implementation of your KDC for the module/expired test to work, since
the two kadmin protocols are not compatible. If you use the MIT library
against a Heimdal server, the test will be skipped; if you use the
Heimdal library against an MIT server, the test suite may hang.
Several module/expired tests are expected to fail with Heimdal 1.5 due
to a bug in Heimdal with reauthenticating immediately after a
library-mediated password change of an expired password. This is fixed
in later releases of Heimdal.
To run the full test suite, Perl 5.10 or later is required. The
following additional Perl modules will be used if present:
* Test::Pod
* Test::Spelling
All are available on CPAN. Those tests will be skipped if the modules
are not available.
To enable tests that don't detect functionality problems but are used to
sanity-check the release, set the environment variable RELEASE_TESTING
to a true value. To enable tests that may be sensitive to the local
environment or that produce a lot of false positives without uncovering
many problems, set the environment variable AUTHOR_TESTING to a true
value.
CONFIGURING
Just installing the module does not enable it or change anything about
your system authentication configuration. To use the module for all
system authentication on Debian systems, put something like:
auth sufficient pam_krb5.so minimum_uid=1000
auth required pam_unix.so try_first_pass nullok_secure
in /etc/pam.d/common-auth, something like:
session optional pam_krb5.so minimum_uid=1000
session required pam_unix.so
in /etc/pam.d/common-session, and something like:
account required pam_krb5.so minimum_uid=1000
account required pam_unix.so
in /etc/pam.d/common-account. The minimum_uid setting tells the PAM
module to pass on any users with a UID lower than 1000, thereby
bypassing Kerberos authentication for the root account and any system
accounts. You normally want to do this since otherwise, if the network
is down, the Kerberos authentication can time out and make it difficult
to log in as root and fix matters. This also avoids problems with
Kerberos principals that happen to match system accounts accidentally
getting access to those accounts.
Be sure to include the module in the session group as well as the auth
group. Without the session entry, the user's ticket cache will not be
created properly for ssh logins (among possibly others).
If your users should normally all use Kerberos passwords exclusively,
putting something like:
password sufficient pam_krb5.so minimum_uid=1000
password required pam_unix.so try_first_pass obscure md5
in /etc/pam.d/common-password will change users' passwords in Kerberos
by default and then only fall back on Unix if that doesn't work. (You
can make this tighter by using the more complex new-style PAM
configuration.) If you instead want to synchronize local and Kerberos
passwords and change them both at the same time, you can do something
like:
password required pam_unix.so obscure sha512
password required pam_krb5.so use_authtok minimum_uid=1000
If you have multiple environments that you want to synchronize and you
don't want password changes to continue if the Kerberos password change
fails, use the clear_on_fail option. For example:
password required pam_krb5.so clear_on_fail minimum_uid=1000
password required pam_unix.so use_authtok obscure sha512
password required pam_smbpass.so use_authtok
In this case, if pam_krb5 cannot change the password (due to password
strength rules on the KDC, for example), it will clear the stored
password (because of the clear_on_fail option), and since pam_unix and
pam_smbpass are both configured with use_authtok, they will both fail.
clear_on_fail is not the default because it would interfere with the
more common pattern of falling back to local passwords if the user
doesn't exist in Kerberos.
If you use a more complex configuration with the Linux PAM [] syntax for
the session and account groups, note that pam_krb5 returns a status of
ignore, not success, if the user didn't log on with Kerberos. You may
need to handle that explicitly with ignore=ignore in your action list.
There are many, many other possibilities. See the Linux PAM
documentation for all the configuration options.
On Red Hat systems, modify /etc/pam.d/system-auth instead, which
contains all of the configuration for the different stacks.
You can also use pam-krb5 only for specific services. In that case,
modify the files in /etc/pam.d for that particular service to use
pam_krb5.so for authentication. For services that are using passwords
over TLS to authenticate users, you may want to use the ignore_k5login
and no_ccache options to the authenticate module. .k5login
authorization is only meaningful for local accounts and ticket caches
are usually (although not always) only useful for interactive sessions.
Configuring the module for Solaris is both simpler and less flexible,
since Solaris (at least Solaris 8 and 9, which are the last versions of
Solaris with which this module was extensively tested) use a single
/etc/pam.conf file that contains configuration for all programs. For
console login on Solaris, try something like:
login auth sufficient /usr/local/lib/security/pam_krb5.so minimum_uid=100
login auth required /usr/lib/security/pam_unix_auth.so.1 use_first_pass
login account required /usr/local/lib/security/pam_krb5.so minimum_uid=100
login account required /usr/lib/security/pam_unix_account.so.1
login session required /usr/local/lib/security/pam_krb5.so retain_after_close minimum_uid=100
login session required /usr/lib/security/pam_unix_session.so.1
A similar configuration could be used for other services, such as ssh.
See the pam.conf(5) man page for more information. When using this
module with Solaris login (at least on Solaris 8 and 9), you will
probably also need to add retain_after_close to the PAM configuration to
avoid having the user's credentials deleted before they are logged in.
The Solaris Kerberos library reportedly does not support prompting for a
password change of an expired account during authentication. Supporting
password change for expired accounts on Solaris with native Kerberos may
therefore require setting the defer_pwchange or force_pwchange option
for selected login applications. See the description and warnings about
that option in the pam_krb5(5) man page.
Some configuration options may be put in the krb5.conf file used by your
Kerberos libraries (usually /etc/krb5.conf or /usr/local/etc/krb5.conf)
instead or in addition to the PAM configuration. See the man page for
more details.
The Kerberos library, via pam-krb5, will prompt the user to change their
password if their password is expired, but when using OpenSSH, this will
only work when ChallengeResponseAuthentication is enabled. Unless this
option is enabled, OpenSSH doesn't pass PAM messages to the user and can
only respond to a simple password prompt.
If you are using MIT Kerberos, be aware that users whose passwords are
expired will not be prompted to change their password unless the KDC
configuration for your realm in [realms] in krb5.conf contains a
master_kdc setting or, if using DNS SRV records, you have a DNS entry
for _kerberos-master as well as _kerberos.
DEBUGGING
The first step when debugging any problems with this module is to add
debug to the PAM options for the module (either in the PAM configuration
or in krb5.conf). This will significantly increase the logging from the
module and should provide a trace of exactly what failed and any
available error information.
Many Kerberos authentication problems are due to configuration issues in
krb5.conf. If pam-krb5 doesn't work, first check that kinit works on
the same system. That will test your basic Kerberos configuration. If
the system has a keytab file installed that's readable by the process
doing authentication via PAM, make sure that the keytab is current and
contains a key for host/<system> where <system> is the fully-qualified
hostname. pam-krb5 prevents KDC spoofing by checking the user's
credentials when possible, but this means that if a keytab is present it
must be correct or authentication will fail. You can check the keytab
with klist -k and kinit -k.
Be sure that all libraries and modules, including PAM modules, loaded by
a program use the same Kerberos libraries. Sometimes programs that use
PAM, such as current versions of OpenSSH, also link against Kerberos
directly. If your sshd is linked against one set of Kerberos libraries
and pam-krb5 is linked against a different set of Kerberos libraries,
this will often cause problems (such as segmentation faults, bus errors,
assertions, or other strange behavior). Similar issues apply to the
com_err library or any other library used by both modules and shared
libraries and by the application that loads them. If your OS ships
Kerberos libraries, it's usually best if possible to build all Kerberos
software on the system against those libraries.
IMPLEMENTATION NOTES
The normal sequence of actions taken for a user login is:
pam_authenticate
pam_setcred(PAM_ESTABLISH_CRED)
pam_open_session
pam_acct_mgmt
and then at logout:
pam_close_session
followed by closing the open PAM session. The corresponding pam_sm_*
functions in this module are called when an application calls those
public interface functions. Not all applications call all of those
functions, or in particularly that order, although pam_authenticate is
always first and has to be.
When pam_authenticate is called, pam-krb5 creates a temporary ticket
cache in /tmp and sets the PAM environment variable PAM_KRB5CCNAME to
point to it. This ticket cache will be automatically destroyed when the
PAM session is closed and is there only to pass the initial credentials
to the call to pam_setcred. The module would use a memory cache, but
memory caches will only work if the application preserves the PAM
environment between the calls to pam_authenticate and pam_setcred. Most
do, but OpenSSH notoriously does not and calls pam_authenticate in a
subprocess, so this method is used to pass the tickets to the
pam_setcred call in a different process.
pam_authenticate does a complete authentication, including checking the
resulting TGT by obtaining a service ticket for the local host if
possible, but this requires read access to the system keytab. If the
keytab doesn't exist, can't be read, or doesn't include the appropriate
credentials, the default is to accept the authentication. This can be
controlled by setting verify_ap_req_nofail to true in [libdefaults] in
/etc/krb5.conf. pam_authenticate also does a basic authorization check,
by default calling krb5_kuserok (which uses ~/.k5login if available and
falls back to checking that the principal corresponds to the account
name). This can be customized with several options documented in the
pam_krb5(5) man page.
pam-krb5 treats pam_open_session and pam_setcred(PAM_ESTABLISH_CRED) as
synonymous, as some applications call one and some call the other. Both
copy the initial credentials from the temporary cache into a permanent
cache for this session and set KRB5CCNAME in the environment. It will
remember when the credential cache has been established and then avoid
doing any duplicate work afterwards, since some applications call
pam_setcred or pam_open_session multiple times (most notably X.Org 7 and
earlier xdm, which also throws away the module settings the last time it
calls them).
pam_acct_mgmt finds the ticket cache, reads it in to obtain the
authenticated principal, and then does is another authorization check
against .k5login or the local account name as described above.
After the call to pam_setcred or pam_open_session, the ticket cache will
be destroyed whenever the calling application either destroys the PAM
environment or calls pam_close_session, which it should do on user
logout.
The normal sequence of events when refreshing a ticket cache (such as
inside a screensaver) is:
pam_authenticate
pam_setcred(PAM_REINITIALIZE_CRED)
pam_acct_mgmt
(PAM_REFRESH_CRED may be used instead.) Authentication proceeds as
above. At the pam_setcred stage, rather than creating a new ticket
cache, the module instead finds the current ticket cache (from the
KRB5CCNAME environment variable or the default ticket cache location
from the Kerberos library) and then reinitializes it with the
credentials from the temporary pam_authenticate ticket cache. When
refreshing a ticket cache, the application should not open a session.
Calling pam_acct_mgmt is optional; pam-krb5 doesn't do anything
different when it's called in this case.
If pam_authenticate apparently didn't succeed, or if an account was
configured to be ignored via ignore_root or minimum_uid, pam_setcred
(and therefore pam_open_session) and pam_acct_mgmt return PAM_IGNORE,
which tells the PAM library to proceed as if that module wasn't listed
in the PAM configuration at all. pam_authenticate, however, returns
failure in the ignored user case by default, since otherwise a
configuration using ignore_root with pam-krb5 as the only PAM module
would allow anyone to log in as root without a password. There doesn't
appear to be a case where returning PAM_IGNORE instead would improve the
module's behavior, but if you know of a case, please let me know.
By default, pam_authenticate intentionally does not follow the PAM
standard for handling expired accounts and instead returns failure from
pam_authenticate unless the Kerberos libraries are able to change the
account password during authentication. Too many applications either do
not call pam_acct_mgmt or ignore its exit status. The fully correct PAM
behavior (returning success from pam_authenticate and
PAM_NEW_AUTHTOK_REQD from pam_acct_mgmt) can be enabled with the
defer_pwchange option.
The defer_pwchange option is unfortunately somewhat tricky to implement.
In this case, the calling sequence is:
pam_authenticate
pam_acct_mgmt
pam_chauthtok
pam_setcred
pam_open_session
During the first pam_authenticate, we can't obtain credentials and
therefore a ticket cache since the password is expired. But
pam_authenticate isn't called again after pam_chauthtok, so
pam_chauthtok has to create a ticket cache. We however don't want it to
do this for the normal password change (passwd) case.
What we do is set a flag in our PAM data structure saying that we're
processing an expired password, and pam_chauthtok, if it sees that flag,
redoes the authentication with password prompting disabled after it
finishes changing the password.
Unfortunately, when handling password changes this way, pam_chauthtok
will always have to prompt the user for their current password again
even though they just typed it. This is because the saved
authentication tokens are cleared after pam_authenticate returns, for
security reasons. We could hack around this by saving the password in
our PAM data structure, but this would let the application gain access
to it (exactly what the clearing is intended to prevent) and breaks a
PAM library guarantee. We could also work around this by having
pam_authenticate get the kadmin/changepw authenticator in the expired
password case and store it for pam_chauthtok, but it doesn't seem worth
the hassle.
HISTORY AND ACKNOWLEDGEMENTS
Originally written by Frank Cusack <fcusack@fcusack.com>, with the
following acknowledgement:
Thanks to Naomaru Itoi <itoi@eecs.umich.edu>, Curtis King
<curtis.king@cul.ca>, and Derrick Brashear <shadow@dementia.org>, all
of whom have written and made available Kerberos 4/5 modules.
Although no code in this module is directly from these author's
modules, (except the get_user_info() routine in support.c; derived
from whichever of these authors originally wrote the first module the
other 2 copied from), it was extremely helpful to look over their code
which aided in my design.
The module was then patched for the FreeBSD ports collection with
additional modifications by unknown maintainers and then was modified by
Joel Kociolek <joko@logidee.com> to be usable with Debian GNU/Linux.
It was packaged by Sam Hartman as the Kerberos v5 PAM module for Debian
and improved and modified by him and later by Russ Allbery to fix bugs
and add additional features. It was then adopted by Andres Salomon, who
added support for refreshing credentials.
The current distribution is maintained by Russ Allbery, who also added
support for reading configuration from krb5.conf, added many features
for compatibility with the Sourceforge module, commented and
standardized the formatting of the code, and overhauled the
documentation.
Thanks to Douglas E. Engert for the initial implementation of PKINIT
support. I have since modified and reworked it extensively, so any bugs
or compilation problems are my fault.
Thanks to Markus Moeller for lots of debugging and multiple patches and
suggestions for improved portability.
Thanks to Booker Bense for the implementation of the alt_auth_map
option.
Thanks to Sam Hartman for the FAST support implementation.
SUPPORT
The pam-krb5 web page at:
https://www.eyrie.org/~eagle/software/pam-krb5/
will always have the current version of this package, the current
documentation, and pointers to any additional resources.
For bug tracking, use the issue tracker on GitHub:
https://github.com/rra/pam-krb5/issues
However, please be aware that I tend to be extremely busy and work
projects often take priority. I'll save your report and get to it as
soon as I can, but it may take me a couple of months.
SOURCE REPOSITORY
pam-krb5 is maintained using Git. You can access the current source on
GitHub at:
https://github.com/rra/pam-krb5
or by cloning the repository at:
https://git.eyrie.org/git/kerberos/pam-krb5.git
or view the repository via the web at:
https://git.eyrie.org/?p=kerberos/pam-krb5.git
The eyrie.org repository is the canonical one, maintained by the author,
but using GitHub is probably more convenient for most purposes. Pull
requests are gratefully reviewed and normally accepted.
LICENSE
The pam-krb5 package as a whole is covered by the following copyright
statement and license:
Copyright 2005-2010, 2014-2015, 2017, 2020-2021
Russ Allbery <eagle@eyrie.org>
Copyright 2009-2011
The Board of Trustees of the Leland Stanford Junior University
Copyright 2005 Andres Salomon <dilinger@debian.org>
Copyright 1999-2000 Frank Cusack <fcusack@fcusack.com>
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
1. Redistributions of source code must retain the above copyright
notice, and the entire permission notice in its entirety, including
the disclaimer of warranties.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the
distribution.
3. The name of the author may not be used to endorse or promote
products derived from this software without specific prior written
permission.
ALTERNATIVELY, this product may be distributed under the terms of the
GNU General Public License, in which case the provisions of the GPL
are required INSTEAD OF the above restrictions. (This clause is
necessary due to a potential bad interaction between the GPL and the
restrictions contained in a BSD-style copyright.)
THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
DAMAGE.
Some files in this distribution are individually released under
different licenses, all of which are compatible with the above general
package license but which may require preservation of additional
notices. All required notices, and detailed information about the
licensing of each file, are recorded in the LICENSE file.
Files covered by a license with an assigned SPDX License Identifier
include SPDX-License-Identifier tags to enable automated processing of
license information. See https://spdx.org/licenses/ for more
information.
For any copyright range specified by files in this package as YYYY-ZZZZ,
the range specifies every single year in that closed interval.
+665
View File
@@ -0,0 +1,665 @@
# pam-krb5
[![Build
status](https://github.com/rra/pam-krb5/workflows/build/badge.svg)](https://github.com/rra/pam-krb5/actions)
[![Debian
package](https://img.shields.io/debian/v/libpam-krb5/unstable)](https://tracker.debian.org/pkg/libpam-krb5)
Copyright 2005-2010, 2014-2015, 2017, 2020-2021 Russ Allbery
<eagle@eyrie.org>. Copyright 2009-2011 The Board of Trustees of the
Leland Stanford Junior University. Copyright 2005 Andres Salomon
<dilinger@debian.org>. Copyright 1999-2000 Frank Cusack
<fcusack@fcusack.com>. This software is distributed under a BSD-style
license. Please see the section [License](#license) below for more
information.
## Blurb
pam-krb5 is a Kerberos PAM module for either MIT Kerberos or Heimdal. It
supports ticket refreshing by screen savers, configurable authorization
handling, authentication of non-local accounts for network services,
password changing, and password expiration, as well as all the standard
expected PAM features. It works correctly with OpenSSH, even with
ChallengeResponseAuthentication and PrivilegeSeparation enabled, and
supports extensive configuration either by PAM options or in krb5.conf or
both. PKINIT is supported with recent versions of both MIT Kerberos and
Heimdal and FAST is supported with recent MIT Kerberos.
## Description
pam-krb5 provides a Kerberos PAM module that supports authentication, user
ticket cache handling, simple authorization (via .k5login or checking
Kerberos principals against local usernames), and password changing. It
can be configured through either options in the PAM configuration itself
or through entries in the system krb5.conf file, and it tries to work
around PAM implementation flaws in commonly-used PAM-enabled applications
such as OpenSSH and xdm. It supports both PKINIT and FAST to the extent
that the underlying Kerberos libraries support these features.
This is not the Kerberos PAM module maintained on Sourceforge and used on
Red Hat systems. It is an independent implementation that, if it ever
shared any common code, diverged long ago. It supports some features that
the Sourceforge module does not (particularly around authorization), and
does not support some options (particularly ones not directly related to
Kerberos) that it does. This module will never support Kerberos v4 or
AFS. For an AFS session module that works with this module (or any other
Kerberos PAM module), see
[pam-afs-session](https://www.eyrie.org/~eagle/software/pam-afs-session/).
If there are other options besides AFS and Kerberos v4 support from the
Sourceforge PAM module that you're missing in this module, please let me
know.
## Requirements
Either MIT Kerberos (or Kerberos implementations based on it) or Heimdal
are supported. MIT Keberos 1.3 or later may be required; this module has
not been tested with earlier versions.
For PKINIT support, Heimdal 0.8rc1 or later or MIT Kerberos 1.6.3 or later
are required. Earlier MIT Kerberos 1.6 releases have a bug in their
handling of PKINIT options. MIT Kerberos 1.12 or later is required to use
the use_pkinit PAM option.
For FAST (Flexible Authentication Secure Tunneling) support, MIT Kerberos
1.7 or higher is required. For anonymous FAST support, anonymous
authentication (generally anonymous PKINIT) support is required in both
the Kerberos libraries and in the local KDC.
This module should work on Linux and build with gcc or clang. It may
still work on Solaris and build with the Sun C compiler, but I have only
tested it on Linux recently. There is beta-quality support for the AIX
NAS Kerberos implementation that has not been tested in years. Other PAM
implementations will probably require some porting, although untested
build system support is present for FreeBSD, Mac OS X, and HP-UX. I
personally can only test on Linux and rely on others to report problems on
other operating systems.
Old versions of OpenSSH are known to call `pam_authenticate` followed by
`pam_setcred(PAM_REINITIALIZE_CRED)` without first calling
`pam_open_session`, thereby requesting that an existing ticket cache be
renewed (similar to what a screensaver would want) rather than requesting
a new ticket cache be created. Since this behavior is indistinguishable
at the PAM level from a screensaver, pam-krb5 when used with these old
versions of OpenSSH will refresh the ticket cache of the OpenSSH daemon
rather than setting up a new ticket cache for the user. The resulting
ticket cache will have the correct permissions (this is not a security
concern), but will not be named correctly or referenced in the user's
environment and will be overwritten by the next user login. The best
solution to this problem is to upgrade OpenSSH. I'm not sure exactly when
this problem was fixed, but at the very least OpenSSH 4.3 and later do not
exhibit it.
To bootstrap from a Git checkout, or if you change the Automake files and
need to regenerate Makefile.in, you will need Automake 1.11 or later. For
bootstrap or if you change configure.ac or any of the m4 files it includes
and need to regenerate configure or config.h.in, you will need Autoconf
2.64 or later. Perl is also required to generate manual pages from a
fresh Git checkout.
## Building and Installation
You can build and install pam-krb5 with the standard commands:
```
./configure
make
make install
```
If you are building from a Git clone, first run `./bootstrap` in the
source directory to generate the build files. `make install` will
probably have to be done as root. Building outside of the source
directory is also supported, if you wish, by creating an empty directory
and then running configure with the correct relative path.
The module will be installed in `/usr/local/lib/security` by default, but
expect to have to override this using `--libdir`. The correct
installation path for PAM modules varies considerably between systems.
The module will always be installed in a subdirectory named `security`
under the specified value of `--libdir`. On Red Hat Linux, for example,
`--libdir=/usr/lib64` is appropriate to install the module into the system
PAM directory. On Debian's amd64 architecture,
`--libdir=/usr/lib/x86_64-linux-gnu` would be correct.
Normally, configure will use `krb5-config` to determine the flags to use
to compile with your Kerberos libraries. To specify a particular
`krb5-config` script to use, either set the `PATH_KRB5_CONFIG` environment
variable or pass it to configure like:
```
./configure PATH_KRB5_CONFIG=/path/to/krb5-config
```
If `krb5-config` isn't found, configure will look for the standard
Kerberos libraries in locations already searched by your compiler. If the
the `krb5-config` script first in your path is not the one corresponding
to the Kerberos libraries you want to use, or if your Kerberos libraries
and includes aren't in a location searched by default by your compiler,
you need to specify a different Kerberos installation root via
`--with-krb5=PATH`. For example:
```
./configure --with-krb5=/usr/pubsw
```
You can also individually set the paths to the include directory and the
library directory with `--with-krb5-include` and `--with-krb5-lib`. You
may need to do this if Autoconf can't figure out whether to use `lib`,
`lib32`, or `lib64` on your platform.
To not use `krb5-config` and force library probing even if there is a
`krb5-config` script on your path, set `PATH_KRB5_CONFIG` to a nonexistent
path:
```
./configure PATH_KRB5_CONFIG=/nonexistent
```
`krb5-config` is not used and library probing is always done if either
`--with-krb5-include` or `--with-krb5-lib` are given.
Pass `--enable-silent-rules` to configure for a quieter build (similar to
the Linux kernel). Use `make warnings` instead of `make` to build with
full GCC compiler warnings (requires either GCC or Clang and may require a
relatively current version of the compiler).
You can pass the `--enable-reduced-depends` flag to configure to try to
minimize the shared library dependencies encoded in the binaries. This
omits from the link line all the libraries included solely because other
libraries depend on them and instead links the programs only against
libraries whose APIs are called directly. This will only work with shared
libraries and will only work on platforms where shared libraries properly
encode their own dependencies (this includes most modern platforms such as
all Linux). It is intended primarily for building packages for Linux
distributions to avoid encoding unnecessary shared library dependencies
that make shared library migrations more difficult. If none of the above
made any sense to you, don't bother with this flag.
## Testing
pam-krb5 comes with a comprehensive test suite, but it requires some
configuration in order to test anything other than low-level utility
functions. For the full test suite, you will need to have a running KDC
in which you can create two test accounts, one with admin access to the
other. Using a test KDC environment, if you have one, is recommended.
Follow the instructions in `tests/config/README` to configure the test
suite.
Now, you can run the test suite with:
```
make check
```
If a test fails, you can run a single test with verbose output via:
```
tests/runtests -o <name-of-test>
```
Do this instead of running the test program directly since it will ensure
that necessary environment variables are set up.
The default libkadm5clnt library on the system must match the
implementation of your KDC for the module/expired test to work, since the
two kadmin protocols are not compatible. If you use the MIT library
against a Heimdal server, the test will be skipped; if you use the Heimdal
library against an MIT server, the test suite may hang.
Several `module/expired` tests are expected to fail with Heimdal 1.5 due
to a bug in Heimdal with reauthenticating immediately after a
library-mediated password change of an expired password. This is fixed in
later releases of Heimdal.
To run the full test suite, Perl 5.10 or later is required. The following
additional Perl modules will be used if present:
* Test::Pod
* Test::Spelling
All are available on CPAN. Those tests will be skipped if the modules are
not available.
To enable tests that don't detect functionality problems but are used to
sanity-check the release, set the environment variable `RELEASE_TESTING`
to a true value. To enable tests that may be sensitive to the local
environment or that produce a lot of false positives without uncovering
many problems, set the environment variable `AUTHOR_TESTING` to a true
value.
## Configuring
Just installing the module does not enable it or change anything about
your system authentication configuration. To use the module for all
system authentication on Debian systems, put something like:
```
auth sufficient pam_krb5.so minimum_uid=1000
auth required pam_unix.so try_first_pass nullok_secure
```
in `/etc/pam.d/common-auth`, something like:
```
session optional pam_krb5.so minimum_uid=1000
session required pam_unix.so
```
in `/etc/pam.d/common-session`, and something like:
```
account required pam_krb5.so minimum_uid=1000
account required pam_unix.so
```
in `/etc/pam.d/common-account`. The `minimum_uid` setting tells the PAM
module to pass on any users with a UID lower than 1000, thereby bypassing
Kerberos authentication for the root account and any system accounts. You
normally want to do this since otherwise, if the network is down, the
Kerberos authentication can time out and make it difficult to log in as
root and fix matters. This also avoids problems with Kerberos principals
that happen to match system accounts accidentally getting access to those
accounts.
Be sure to include the module in the session group as well as the auth
group. Without the session entry, the user's ticket cache will not be
created properly for ssh logins (among possibly others).
If your users should normally all use Kerberos passwords exclusively,
putting something like:
```
password sufficient pam_krb5.so minimum_uid=1000
password required pam_unix.so try_first_pass obscure md5
```
in `/etc/pam.d/common-password` will change users' passwords in Kerberos
by default and then only fall back on Unix if that doesn't work. (You can
make this tighter by using the more complex new-style PAM configuration.)
If you instead want to synchronize local and Kerberos passwords and change
them both at the same time, you can do something like:
```
password required pam_unix.so obscure sha512
password required pam_krb5.so use_authtok minimum_uid=1000
```
If you have multiple environments that you want to synchronize and you
don't want password changes to continue if the Kerberos password change
fails, use the `clear_on_fail` option. For example:
```
password required pam_krb5.so clear_on_fail minimum_uid=1000
password required pam_unix.so use_authtok obscure sha512
password required pam_smbpass.so use_authtok
```
In this case, if `pam_krb5` cannot change the password (due to password
strength rules on the KDC, for example), it will clear the stored password
(because of the `clear_on_fail` option), and since `pam_unix` and
`pam_smbpass` are both configured with `use_authtok`, they will both fail.
`clear_on_fail` is not the default because it would interfere with the
more common pattern of falling back to local passwords if the user doesn't
exist in Kerberos.
If you use a more complex configuration with the Linux PAM `[]` syntax for
the session and account groups, note that `pam_krb5` returns a status of
ignore, not success, if the user didn't log on with Kerberos. You may
need to handle that explicitly with `ignore=ignore` in your action list.
There are many, many other possibilities. See the Linux PAM documentation
for all the configuration options.
On Red Hat systems, modify `/etc/pam.d/system-auth` instead, which
contains all of the configuration for the different stacks.
You can also use pam-krb5 only for specific services. In that case,
modify the files in `/etc/pam.d` for that particular service to use
`pam_krb5.so` for authentication. For services that are using passwords
over TLS to authenticate users, you may want to use the `ignore_k5login`
and `no_ccache` options to the authenticate module. `.k5login`
authorization is only meaningful for local accounts and ticket caches are
usually (although not always) only useful for interactive sessions.
Configuring the module for Solaris is both simpler and less flexible,
since Solaris (at least Solaris 8 and 9, which are the last versions of
Solaris with which this module was extensively tested) use a single
`/etc/pam.conf` file that contains configuration for all programs. For
console login on Solaris, try something like:
```
login auth sufficient /usr/local/lib/security/pam_krb5.so minimum_uid=100
login auth required /usr/lib/security/pam_unix_auth.so.1 use_first_pass
login account required /usr/local/lib/security/pam_krb5.so minimum_uid=100
login account required /usr/lib/security/pam_unix_account.so.1
login session required /usr/local/lib/security/pam_krb5.so retain_after_close minimum_uid=100
login session required /usr/lib/security/pam_unix_session.so.1
```
A similar configuration could be used for other services, such as ssh.
See the pam.conf(5) man page for more information. When using this module
with Solaris login (at least on Solaris 8 and 9), you will probably also
need to add `retain_after_close` to the PAM configuration to avoid having
the user's credentials deleted before they are logged in.
The Solaris Kerberos library reportedly does not support prompting for a
password change of an expired account during authentication. Supporting
password change for expired accounts on Solaris with native Kerberos may
therefore require setting the `defer_pwchange` or `force_pwchange` option
for selected login applications. See the description and warnings about
that option in the pam_krb5(5) man page.
Some configuration options may be put in the `krb5.conf` file used by your
Kerberos libraries (usually `/etc/krb5.conf` or
`/usr/local/etc/krb5.conf`) instead or in addition to the PAM
configuration. See the man page for more details.
The Kerberos library, via pam-krb5, will prompt the user to change their
password if their password is expired, but when using OpenSSH, this will
only work when `ChallengeResponseAuthentication` is enabled. Unless this
option is enabled, OpenSSH doesn't pass PAM messages to the user and can
only respond to a simple password prompt.
If you are using MIT Kerberos, be aware that users whose passwords are
expired will not be prompted to change their password unless the KDC
configuration for your realm in `[realms]` in `krb5.conf` contains a
`master_kdc` setting or, if using DNS SRV records, you have a DNS entry
for `_kerberos-master` as well as `_kerberos`.
## Debugging
The first step when debugging any problems with this module is to add
`debug` to the PAM options for the module (either in the PAM configuration
or in `krb5.conf`). This will significantly increase the logging from the
module and should provide a trace of exactly what failed and any available
error information.
Many Kerberos authentication problems are due to configuration issues in
`krb5.conf`. If pam-krb5 doesn't work, first check that `kinit` works on
the same system. That will test your basic Kerberos configuration. If
the system has a keytab file installed that's readable by the process
doing authentication via PAM, make sure that the keytab is current and
contains a key for `host/<system>` where <system> is the fully-qualified
hostname. pam-krb5 prevents KDC spoofing by checking the user's
credentials when possible, but this means that if a keytab is present it
must be correct or authentication will fail. You can check the keytab
with `klist -k` and `kinit -k`.
Be sure that all libraries and modules, including PAM modules, loaded by a
program use the same Kerberos libraries. Sometimes programs that use PAM,
such as current versions of OpenSSH, also link against Kerberos directly.
If your sshd is linked against one set of Kerberos libraries and pam-krb5
is linked against a different set of Kerberos libraries, this will often
cause problems (such as segmentation faults, bus errors, assertions, or
other strange behavior). Similar issues apply to the com_err library or
any other library used by both modules and shared libraries and by the
application that loads them. If your OS ships Kerberos libraries, it's
usually best if possible to build all Kerberos software on the system
against those libraries.
## Implementation Notes
The normal sequence of actions taken for a user login is:
```
pam_authenticate
pam_setcred(PAM_ESTABLISH_CRED)
pam_open_session
pam_acct_mgmt
```
and then at logout:
```
pam_close_session
```
followed by closing the open PAM session. The corresponding `pam_sm_*`
functions in this module are called when an application calls those public
interface functions. Not all applications call all of those functions, or
in particularly that order, although `pam_authenticate` is always first
and has to be.
When `pam_authenticate` is called, pam-krb5 creates a temporary ticket
cache in `/tmp` and sets the PAM environment variable `PAM_KRB5CCNAME` to
point to it. This ticket cache will be automatically destroyed when the
PAM session is closed and is there only to pass the initial credentials to
the call to `pam_setcred`. The module would use a memory cache, but
memory caches will only work if the application preserves the PAM
environment between the calls to `pam_authenticate` and `pam_setcred`.
Most do, but OpenSSH notoriously does not and calls `pam_authenticate` in
a subprocess, so this method is used to pass the tickets to the
`pam_setcred` call in a different process.
`pam_authenticate` does a complete authentication, including checking the
resulting TGT by obtaining a service ticket for the local host if
possible, but this requires read access to the system keytab. If the
keytab doesn't exist, can't be read, or doesn't include the appropriate
credentials, the default is to accept the authentication. This can be
controlled by setting `verify_ap_req_nofail` to true in `[libdefaults]` in
`/etc/krb5.conf`. `pam_authenticate` also does a basic authorization
check, by default calling `krb5_kuserok` (which uses `~/.k5login` if
available and falls back to checking that the principal corresponds to the
account name). This can be customized with several options documented in
the pam_krb5(5) man page.
pam-krb5 treats `pam_open_session` and `pam_setcred(PAM_ESTABLISH_CRED)`
as synonymous, as some applications call one and some call the other.
Both copy the initial credentials from the temporary cache into a
permanent cache for this session and set `KRB5CCNAME` in the environment.
It will remember when the credential cache has been established and then
avoid doing any duplicate work afterwards, since some applications call
`pam_setcred` or `pam_open_session` multiple times (most notably X.Org 7
and earlier xdm, which also throws away the module settings the last time
it calls them).
`pam_acct_mgmt` finds the ticket cache, reads it in to obtain the
authenticated principal, and then does is another authorization check
against `.k5login` or the local account name as described above.
After the call to `pam_setcred` or `pam_open_session`, the ticket cache
will be destroyed whenever the calling application either destroys the PAM
environment or calls `pam_close_session`, which it should do on user
logout.
The normal sequence of events when refreshing a ticket cache (such as
inside a screensaver) is:
```
pam_authenticate
pam_setcred(PAM_REINITIALIZE_CRED)
pam_acct_mgmt
```
(`PAM_REFRESH_CRED` may be used instead.) Authentication proceeds as
above. At the `pam_setcred` stage, rather than creating a new ticket
cache, the module instead finds the current ticket cache (from the
`KRB5CCNAME` environment variable or the default ticket cache location
from the Kerberos library) and then reinitializes it with the credentials
from the temporary `pam_authenticate` ticket cache. When refreshing a
ticket cache, the application should not open a session. Calling
`pam_acct_mgmt` is optional; pam-krb5 doesn't do anything different when
it's called in this case.
If `pam_authenticate` apparently didn't succeed, or if an account was
configured to be ignored via `ignore_root` or `minimum_uid`, `pam_setcred`
(and therefore `pam_open_session`) and `pam_acct_mgmt` return
`PAM_IGNORE`, which tells the PAM library to proceed as if that module
wasn't listed in the PAM configuration at all. `pam_authenticate`,
however, returns failure in the ignored user case by default, since
otherwise a configuration using `ignore_root` with pam-krb5 as the only
PAM module would allow anyone to log in as root without a password. There
doesn't appear to be a case where returning `PAM_IGNORE` instead would
improve the module's behavior, but if you know of a case, please let me
know.
By default, `pam_authenticate` intentionally does not follow the PAM
standard for handling expired accounts and instead returns failure from
`pam_authenticate` unless the Kerberos libraries are able to change the
account password during authentication. Too many applications either do
not call `pam_acct_mgmt` or ignore its exit status. The fully correct PAM
behavior (returning success from `pam_authenticate` and
`PAM_NEW_AUTHTOK_REQD` from `pam_acct_mgmt`) can be enabled with the
`defer_pwchange` option.
The `defer_pwchange` option is unfortunately somewhat tricky to implement.
In this case, the calling sequence is:
```
pam_authenticate
pam_acct_mgmt
pam_chauthtok
pam_setcred
pam_open_session
```
During the first `pam_authenticate`, we can't obtain credentials and
therefore a ticket cache since the password is expired. But
`pam_authenticate` isn't called again after `pam_chauthtok`, so
`pam_chauthtok` has to create a ticket cache. We however don't want it to
do this for the normal password change (`passwd`) case.
What we do is set a flag in our PAM data structure saying that we're
processing an expired password, and `pam_chauthtok`, if it sees that flag,
redoes the authentication with password prompting disabled after it
finishes changing the password.
Unfortunately, when handling password changes this way, `pam_chauthtok`
will always have to prompt the user for their current password again even
though they just typed it. This is because the saved authentication
tokens are cleared after `pam_authenticate` returns, for security reasons.
We could hack around this by saving the password in our PAM data
structure, but this would let the application gain access to it (exactly
what the clearing is intended to prevent) and breaks a PAM library
guarantee. We could also work around this by having `pam_authenticate`
get the `kadmin/changepw` authenticator in the expired password case and
store it for `pam_chauthtok`, but it doesn't seem worth the hassle.
## History and Acknowledgements
Originally written by Frank Cusack <fcusack@fcusack.com>, with the
following acknowledgement:
> Thanks to Naomaru Itoi <itoi@eecs.umich.edu>, Curtis King
> <curtis.king@cul.ca>, and Derrick Brashear <shadow@dementia.org>, all of
> whom have written and made available Kerberos 4/5 modules. Although no
> code in this module is directly from these author's modules, (except the
> get_user_info() routine in support.c; derived from whichever of these
> authors originally wrote the first module the other 2 copied from), it
> was extremely helpful to look over their code which aided in my design.
The module was then patched for the FreeBSD ports collection with
additional modifications by unknown maintainers and then was modified by
Joel Kociolek <joko@logidee.com> to be usable with Debian GNU/Linux.
It was packaged by Sam Hartman as the Kerberos v5 PAM module for Debian
and improved and modified by him and later by Russ Allbery to fix bugs and
add additional features. It was then adopted by Andres Salomon, who added
support for refreshing credentials.
The current distribution is maintained by Russ Allbery, who also added
support for reading configuration from `krb5.conf`, added many features
for compatibility with the Sourceforge module, commented and standardized
the formatting of the code, and overhauled the documentation.
Thanks to Douglas E. Engert for the initial implementation of PKINIT
support. I have since modified and reworked it extensively, so any bugs
or compilation problems are my fault.
Thanks to Markus Moeller for lots of debugging and multiple patches and
suggestions for improved portability.
Thanks to Booker Bense for the implementation of the `alt_auth_map`
option.
Thanks to Sam Hartman for the FAST support implementation.
## Support
The [pam-krb5 web page](https://www.eyrie.org/~eagle/software/pam-krb5/)
will always have the current version of this package, the current
documentation, and pointers to any additional resources.
For bug tracking, use the [issue tracker on
GitHub](https://github.com/rra/pam-krb5/issues). However, please be aware
that I tend to be extremely busy and work projects often take priority.
I'll save your report and get to it as soon as I can, but it may take me a
couple of months.
## Source Repository
pam-krb5 is maintained using Git. You can access the current source on
[GitHub](https://github.com/rra/pam-krb5) or by cloning the repository at:
https://git.eyrie.org/git/kerberos/pam-krb5.git
or [view the repository on the
web](https://git.eyrie.org/?p=kerberos/pam-krb5.git).
The eyrie.org repository is the canonical one, maintained by the author,
but using GitHub is probably more convenient for most purposes. Pull
requests are gratefully reviewed and normally accepted.
## License
The pam-krb5 package as a whole is covered by the following copyright
statement and license:
> Copyright 2005-2010, 2014-2015, 2017, 2020-2021
> Russ Allbery <eagle@eyrie.org>
>
> Copyright 2009-2011
> The Board of Trustees of the Leland Stanford Junior University
>
> Copyright 2005
> Andres Salomon <dilinger@debian.org>
>
> Copyright 1999-2000
> Frank Cusack <fcusack@fcusack.com>
>
> Redistribution and use in source and binary forms, with or without
> modification, are permitted provided that the following conditions are
> met:
>
> 1. Redistributions of source code must retain the above copyright
> notice, and the entire permission notice in its entirety, including
> the disclaimer of warranties.
>
> 2. Redistributions in binary form must reproduce the above copyright
> notice, this list of conditions and the following disclaimer in the
> documentation and/or other materials provided with the distribution.
>
> 3. The name of the author may not be used to endorse or promote products
> derived from this software without specific prior written permission.
>
> ALTERNATIVELY, this product may be distributed under the terms of the GNU
> General Public License, in which case the provisions of the GPL are
> required INSTEAD OF the above restrictions. (This clause is necessary due
> to a potential bad interaction between the GPL and the restrictions
> contained in a BSD-style copyright.)
>
> THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
> INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
> AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
> THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
> EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
> PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
> PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
> LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
> NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
> SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Some files in this distribution are individually released under different
licenses, all of which are compatible with the above general package
license but which may require preservation of additional notices. All
required notices, and detailed information about the licensing of each
file, are recorded in the LICENSE file.
Files covered by a license with an assigned SPDX License Identifier
include SPDX-License-Identifier tags to enable automated processing of
license information. See https://spdx.org/licenses/ for more information.
For any copyright range specified by files in this package as YYYY-ZZZZ,
the range specifies every single year in that closed interval.
+101
View File
@@ -0,0 +1,101 @@
pam-krb5 To-Do List
PAM API:
* Support PAM_CHANGE_EXPIRED_AUTHTOK properly in pam_chauthtok. This
will require prompting for the current password (if it's not already
available in the PAM data) and trying a regular authentication first to
see if the account is expired.
* Tighter verification that all of our flags are valid might be a good
idea.
* For informational messages followed by a prompt, find a way to combine
these into one PAM conversation call for better GUI presentation
behavior.
Functionality:
* Change the authentication flow so that both Heimdal and MIT use the
same logic for attempting PKINIT first and then falling back to
password. This will fix failure to store passwords in the PAM data
with try_pkinit and MIT Kerberos on password fallback and will allow
implementation of use_pkinit for MIT. Based on discussion with MIT
Kerberos upstream, the best approach is probably to configure a custom
prompter that refuses to reply to any prompt.
* Add a daemon that can be used to verify TGTs that can be used when
pam-krb5 is run as a non-root user and hence doesn't have access to the
system keytab. Jeff Hutzelman has a daemon and protocol for doing this
developed for a different PAM authentication module, and it would be
good to stay consistent with that protocol if possible. (Debian
Bug#399001)
* The alt_auth_map parsing to find realms doesn't take into account
escaped @-signs and doesn't do proper principal parsing.
* Fix password expiration handling for the search_k5login and
alt_auth_map cases. Right now, we may return expired password errors
that would trigger password expiration handling, which probably isn't
correct.
* Support authentication from a keytab.
* Support disabling of user canonicalization so that the PAM user is
retained even if the module did an aname to lname mapping.
* Use set_out_ccache to write the resulting ticket cache, if it is
available. This ensures the correct flags are set in the ticket cache.
This poses some challenges due to the two-step ticket cache mechanism
currently used. Perhaps there's a cache copying API?
* Use krb5_chpw_message to parse password change messages from Active
Directory.
* Consider exposing the Kerberos principal in the password prompt for a
password change. (Debian Bug#667928)
Code Cleanup:
* The PKINIT code for Heimdal involves too many #ifdefs right now for my
taste. Find a way to restructure it to only wrap the main PKINIT
function for Heimdal.
* The current handling of error return codes is a mess. We need to find
a way to return a rich set of error codes from the underlying functions
and then map error codes appropriately in the interface functions.
Helpful for this would be improved documentation of what error codes
are permitted and where.
* Tracking when to free the Kerberos context and other things stored in
the PAM context is currently too complicated. It should be possible to
simplify it with a reference counting scheme.
Documentation:
* Document PKINIT configuration with MIT in krb5.conf. It looks like the
library supports configuration in [realms] with similar names to the
PAM module configuration.
Portability:
* If pam_modutil_getpwnam is not available but getpwnam_r is, roll our
own using getpwnam_r.
Logging:
* Log the information that the Kerberos library asks us to display, or at
least the info and error messages.
* Log unknown PAM flags on module entry. Currently, only the symbolic
flags we know about will be logged.
Test suite:
* Ensure that the test suite covers all possible PAM options.
* Figure out why the pin-mit script for module/pkinit prompts twice and
check if it's a bug in the module.
* Find a way of testing the PKINIT identity selection for MIT Kerberos
with use_pkinit enabled.
Executable
+13
View File
@@ -0,0 +1,13 @@
#!/bin/sh
#
# Run this shell script to bootstrap as necessary after a fresh checkout.
set -e
autoreconf -i --force
rm -rf autom4te.cache
# Generate manual pages.
version=`grep '^pam-krb5' NEWS | head -1 | cut -d' ' -f2`
pod2man --release="$version" --center=pam-krb5 -s 5 docs/pam_krb5.pod \
>docs/pam_krb5.5
+13
View File
@@ -0,0 +1,13 @@
# Continuous Integration
The files in this directory are used for continuous integration testing.
`ci/install` installs the prerequisite packages (run as root on a Debian
derivative), and `ci/test` runs the tests.
Most tests will be skipped without a Kerberos configuration. The scripts
`ci/kdc-setup-heimdal` and `ci/kdc-setup-mit` will (when run as root on a
Debian derivative) set up a Heimdal or MIT Kerberos KDC, respectively, and
generate the files required to run the complete test suite.
Tests are run automatically via GitHub Actions workflows using these
scripts and the configuration in the `.github/workflows` directory.
+9
View File
@@ -0,0 +1,9 @@
# Heimdal KDC init script setup. -*- sh -*-
# KDC configuration.
KDC_ENABLED=yes
KDC_PARAMS='--config-file=/etc/heimdal-kdc/kdc.conf'
# kpasswdd configuration.
KPASSWDD_ENABLED=yes
KPASSWDD_PARAMS='-r HEIMDAL.TEST'
+1
View File
@@ -0,0 +1 @@
test/admin@HEIMDAL.TEST all testuser@HEIMDAL.TEST
+30
View File
@@ -0,0 +1,30 @@
# Heimdal KDC configuration. -*- conf -*-
[kadmin]
default_keys = aes256-cts-hmac-sha1-96:pw-salt
[kdc]
acl_file = /etc/heimdal-kdc/kadmind.acl
check-ticket-addresses = false
logging = SYSLOG:NOTICE
ports = 88
# PKINIT configuration.
enable-pkinit = yes
pkinit_identity = FILE:/etc/heimdal-kdc/kdc.pem
pkinit_anchors = FILE:/etc/heimdal-kdc/ca/ca.pem
pkinit_mappings_file = /etc/heimdal-kdc/pki-mapping
pkinit_allow_proxy_certificate = no
pkinit_principal_in_certificate = no
[libdefaults]
default_realm = HEIMDAL.TEST
dns_lookup_kdc = false
dns_lookup_realm = false
[realms]
HEIMDAL.TEST.EYRIE.ORG = {
kdc = 127.0.0.1
master_kdc = 127.0.0.1
admin_server = 127.0.0.1
}
+19
View File
@@ -0,0 +1,19 @@
[libdefaults]
default_realm = HEIMDAL.TEST
dns_lookup_kdc = false
dns_lookup_realm = false
rdns = false
renew_lifetime = 7d
ticket_lifetime = 25h
[realms]
HEIMDAL.TEST = {
kdc = 127.0.0.1
master_kdc = 127.0.0.1
admin_server = 127.0.0.1
pkinit_anchors = FILE:/etc/heimdal-kdc/ca/ca.pem
}
[logging]
kdc = SYSLOG:NOTICE
default = SYSLOG:NOTICE
+1
View File
@@ -0,0 +1 @@
testuser@HEIMDAL.TEST:UID=testuser,DC=HEIMDAL,DC=TEST
+19
View File
@@ -0,0 +1,19 @@
[client_cert]
basicConstraints=CA:FALSE
keyUsage=digitalSignature,keyEncipherment,keyAgreement
extendedKeyUsage=1.3.6.1.5.2.3.4
subjectKeyIdentifier=hash
authorityKeyIdentifier=keyid,issuer
issuerAltName=issuer:copy
subjectAltName=otherName:1.3.6.1.5.2.2;SEQUENCE:princ_name
[princ_name]
realm=EXP:0,GeneralString:${ENV::REALM}
principal_name=EXP:1,SEQUENCE:principal_seq
[principal_seq]
name_type=EXP:0,INTEGER:1
name_string=EXP:1,SEQUENCE:principals
[principals]
princ1=GeneralString:${ENV::CLIENT}
+20
View File
@@ -0,0 +1,20 @@
[kdc_cert]
basicConstraints=CA:FALSE
keyUsage=nonRepudiation,digitalSignature,keyEncipherment,keyAgreement
extendedKeyUsage=1.3.6.1.5.2.3.5
subjectKeyIdentifier=hash
authorityKeyIdentifier=keyid,issuer
issuerAltName=issuer:copy
subjectAltName=otherName:1.3.6.1.5.2.2;SEQUENCE:kdc_princ_name
[kdc_princ_name]
realm=EXP:0,GeneralString:${ENV::REALM}
principal_name=EXP:1,SEQUENCE:kdc_principal_seq
[kdc_principal_seq]
name_type=EXP:0,INTEGER:1
name_string=EXP:1,SEQUENCE:kdc_principals
[kdc_principals]
princ1=GeneralString:krbtgt
princ2=GeneralString:${ENV::REALM}
+1
View File
@@ -0,0 +1 @@
test/admin@MIT.TEST mci testuser@MIT.TEST
+19
View File
@@ -0,0 +1,19 @@
[kdcdefaults]
kdc_ports = 88
kdc_tcp_ports = 88
restrict_anonymous_to_tgt = true
[realms]
MIT.TEST = {
database_name = /var/lib/krb5kdc/principal
admin_keytab = /var/lib/krb5kdc/kadm5.keytab
acl_file = /etc/krb5kdc/kadm5.acl
key_stash_file = /var/lib/krb5kdc/stash
max_life = 1d 1h 0m 0s
max_renewable_life = 7d 0h 0m 0s
master_key_type = aes256-cts
supported_enctypes = aes256-cts:normal
default_principal_flags = +preauth
pkinit_identity = FILE:/var/lib/krb5kdc/kdc.pem,/var/lib/krb5kdc/kdckey.pem
pkinit_anchors = FILE:/etc/krb5kdc/cacert.pem
}
+19
View File
@@ -0,0 +1,19 @@
[libdefaults]
default_realm = MIT.TEST
dns_lookup_kdc = false
dns_lookup_realm = false
rdns = false
renew_lifetime = 7d
ticket_lifetime = 25h
[realms]
MIT.TEST = {
kdc = 127.0.0.1
master_kdc = 127.0.0.1
admin_server = 127.0.0.1
pkinit_anchors = FILE:/etc/krb5kdc/cacert.pem
}
[logging]
kdc = SYSLOG:NOTICE
default = SYSLOG:NOTICE
Executable
+18
View File
@@ -0,0 +1,18 @@
#!/bin/sh
#
# Install packages for integration tests.
#
# This script is normally run via sudo in a test container or VM, such as via
# GitHub Actions.
#
# Copyright 2015-2021 Russ Allbery <eagle@eyrie.org>
#
# SPDX-License-Identifier: MIT
set -eux
# Install distribution packages.
apt-get update -qq
apt-get install aspell autoconf automake cppcheck heimdal-multidev \
krb5-config libkrb5-dev libpam0g-dev libtest-pod-perl \
libtest-spelling-perl libtool perl valgrind
+105
View File
@@ -0,0 +1,105 @@
#!/bin/sh
#
# Build a Kerberos test realm for Heimdal.
#
# This script automates the process of setting up a Kerberos test realm from
# scratch suitable for testing pam-krb5. It is primarily intended to be run
# from inside CI in a VM or container from the top of the pam-krb5 source
# tree, and must be run as root. It expects to be operating on the Debian
# Heimdal package.
#
# Copyright 2014, 2020 Russ Allbery <eagle@eyrie.org>
#
# SPDX-License-Identifier: MIT
set -eux
# Install the KDC.
apt-get install heimdal-kdc
# Install its configuration files.
cp ci/files/heimdal/heimdal-kdc /etc/default/heimdal-kdc
cp ci/files/heimdal/kadmind.acl /etc/heimdal-kdc/kadmind.acl
cp ci/files/heimdal/kdc.conf /etc/heimdal-kdc/kdc.conf
cp ci/files/heimdal/krb5.conf /etc/krb5.conf
cp ci/files/heimdal/pki-mapping /etc/heimdal-kdc/pki-mapping
# Some versions of heimdal-kdc require this.
ln -s /etc/heimdal-kdc/kadmind.acl /var/lib/heimdal-kdc/kadmind.acl
# Add domain-realm mappings for the local host, since otherwise Heimdal and
# MIT Kerberos may attempt to discover the realm of the local domain, and the
# DNS server for GitHub Actions has a habit of just not responding and causing
# the test to hang.
cat <<EOF >>/etc/krb5.conf
[domain_realm]
$(hostname -f) = HEIMDAL.TEST
EOF
cat <<EOF >>/etc/heimdal-kdc/kdc.conf
[domain_realm]
$(hostname -f) = HEIMDAL.TEST
EOF
# Create the basic KDC.
kstash --random-key
kadmin -l init --realm-max-ticket-life='1 day 1 hour' \
--realm-max-renewable-life='1 week' HEIMDAL.TEST
# Set default principal policies.
kadmin -l modify --attributes=requires-pre-auth,disallow-svr \
default@HEIMDAL.TEST
# Create and store the keytabs.
kadmin -l add -r --use-defaults --attributes=requires-pre-auth \
test/admin@HEIMDAL.TEST
kadmin -l ext_keytab -k tests/config/admin-keytab test/admin@HEIMDAL.TEST
kadmin -l add -r --use-defaults --attributes=requires-pre-auth \
test/keytab@HEIMDAL.TEST
kadmin -l ext_keytab -k tests/config/keytab test/keytab@HEIMDAL.TEST
# Create a user principal with a known password.
password="iceedKaicVevjunwiwyd"
kadmin -l add --use-defaults --password="$password" testuser@HEIMDAL.TEST
echo 'testuser@HEIMDAL.TEST' >tests/config/password
echo "$password" >>tests/config/password
# Create the root CA for PKINIT.
mkdir -p /etc/heimdal-kdc/ca
hxtool issue-certificate --self-signed --issue-ca --generate-key=rsa \
--subject=CN=CA,DC=HEIMDAL,DC=TEST --lifetime=10years \
--certificate=FILE:/etc/heimdal-kdc/ca/ca.pem
chmod 644 /etc/heimdal-kdc/ca/ca.pem
# Create the certificate for the Heimdal Kerberos KDC.
hxtool issue-certificate --ca-certificate=FILE:/etc/heimdal-kdc/ca/ca.pem \
--generate-key=rsa --type=pkinit-kdc \
--pk-init-principal=krbtgt/HEIMDAL.TEST@HEIMDAL.TEST \
--subject=uid=kdc,DC=HEIMDAL,DC=TEST \
--certificate=FILE:/etc/heimdal-kdc/kdc.pem
chmod 644 /etc/heimdal-kdc/kdc.pem
# Create the certificate for the Heimdal client.
hxtool issue-certificate --ca-certificate=FILE:/etc/heimdal-kdc/ca/ca.pem \
--generate-key=rsa --type=pkinit-client \
--pk-init-principal=testuser@HEIMDAL.TEST \
--subject=UID=testuser,DC=HEIMDAL,DC=TEST \
--certificate=FILE:tests/config/pkinit-cert
echo 'testuser@HEIMDAL.TEST' >tests/config/pkinit-principal
# Fix permissions on all the newly-created files.
chmod 644 tests/config/*
# Restart the Heimdal KDC and services.
systemctl stop heimdal-kdc
systemctl start heimdal-kdc
# Ensure that the KDC is running.
for n in $(seq 1 5); do
if echo "$password" \
| kinit --password-file=STDIN testuser@HEIMDAL.TEST; then
break
fi
sleep 1
done
klist
kdestroy
+102
View File
@@ -0,0 +1,102 @@
#!/bin/sh
#
# Build a Kerberos test realm for MIT Kerberos
#
# This script automates the process of setting up a Kerberos test realm from
# scratch suitable for testing pam-krb5. It is primarily intended to be run
# from inside CI in a VM or container from the top of the pam-krb5 source
# tree, and must be run as root. It expects to be operating on the Debian
# MIT Kerberos package.
#
# Copyright 2014, 2020 Russ Allbery <eagle@eyrie.org>
#
# SPDX-License-Identifier: MIT
set -eux
# Install the KDC and the OpenSSL command line tool.
apt-get install krb5-admin-server krb5-kdc krb5-pkinit openssl
# Install its configuration files.
cp ci/files/mit/extensions.client /etc/krb5kdc/extensions.client
cp ci/files/mit/extensions.kdc /etc/krb5kdc/extensions.kdc
cp ci/files/mit/kadm5.acl /etc/krb5kdc/kadm5.acl
cp ci/files/mit/kdc.conf /etc/krb5kdc/kdc.conf
cp ci/files/mit/krb5.conf /etc/krb5.conf
# Add domain-realm mappings for the local host, since otherwise Heimdal and
# MIT Kerberos may attempt to discover the realm of the local domain, and the
# DNS server for GitHub Actions has a habit of just not responding and causing
# the test to hang.
cat <<EOF >>/etc/krb5.conf
[domain_realm]
$(hostname -f) = MIT.TEST
EOF
# Create the basic KDC.
kdb5_util create -s -P 'this is a test master database password'
# Create and store the keytabs.
kadmin.local -q 'add_principal +requires_preauth -randkey test/admin@MIT.TEST'
kadmin.local -q 'ktadd -k tests/config/admin-keytab test/admin@MIT.TEST'
kadmin.local -q 'add_principal +requires_preauth -randkey test/keytab@MIT.TEST'
kadmin.local -q 'ktadd -k tests/config/keytab test/keytab@MIT.TEST'
# Enable anonymous PKINIT.
kadmin.local -q 'addprinc -randkey WELLKNOWN/ANONYMOUS'
# Create a user principal with a known password.
password="iceedKaicVevjunwiwyd"
kadmin.local -q \
"add_principal +requires_preauth -pw $password testuser@MIT.TEST"
echo 'testuser@MIT.TEST' >tests/config/password
echo "$password" >>tests/config/password
# Create the root CA for PKINIT.
openssl genrsa -out /etc/krb5kdc/cakey.pem 2048
openssl req -key /etc/krb5kdc/cakey.pem -new -x509 \
-out /etc/krb5kdc/cacert.pem -subj "/CN=MIT.TEST CA" -days 3650
chmod 755 /etc/krb5kdc
chmod 644 /etc/krb5kdc/cacert.pem
# Create the certificate for the MIT Kerberos KDC.
openssl genrsa -out /var/lib/krb5kdc/kdckey.pem 2048
openssl req -new -out /var/lib/krb5kdc/kdc.req \
-key /var/lib/krb5kdc/kdckey.pem -subj "/CN=MIT.TEST"
REALM=MIT.TEST openssl x509 -req -in /var/lib/krb5kdc/kdc.req \
-CAkey /etc/krb5kdc/cakey.pem -CA /etc/krb5kdc/cacert.pem \
-out /var/lib/krb5kdc/kdc.pem -days 365 \
-extfile /etc/krb5kdc/extensions.kdc -extensions kdc_cert \
-CAcreateserial
rm /var/lib/krb5kdc/kdc.req
# Create the certificate for the MIT Kerberos client.
openssl genrsa -out clientkey.pem 2048
openssl req -new -key clientkey.pem -out client.req \
-subj "/CN=testuser@MIT.TEST"
REALM="MIT.TEST" CLIENT="testuser" openssl x509 \
-CAkey /etc/krb5kdc/cakey.pem -CA /etc/krb5kdc/cacert.pem -req \
-in client.req -extensions client_cert \
-extfile /etc/krb5kdc/extensions.client -days 365 -out client.pem
cat client.pem clientkey.pem >tests/config/pkinit-cert
rm clientkey.pem client.pem client.req
echo 'testuser@MIT.TEST' >tests/config/pkinit-principal
# Fix permissions on all the newly-created files.
chmod 644 tests/config/*
# Restart the MIT Kerberos KDC and services.
systemctl stop krb5-kdc krb5-admin-server
systemctl start krb5-kdc krb5-admin-server
# Ensure that the KDC is running.
for n in $(seq 1 5); do
if echo "$password" | kinit testuser@MIT.TEST; then
break
fi
sleep 1
done
klist
kdestroy
kinit -n @MIT.TEST
kinit -X X509_user_identity=FILE:tests/config/pkinit-cert testuser@MIT.TEST
Executable
+44
View File
@@ -0,0 +1,44 @@
#!/bin/sh
#
# Run tests for continuous integration.
#
# This script is normally run in a test container or VM, such as via GitHub
# Actions.
#
# Copyright 2015-2021 Russ Allbery <eagle@eyrie.org>
#
# SPDX-License-Identifier: MIT
set -eux
# Normally, KERBEROS is set based on the CI matrix, but provide a default in
# case someone runs this test by hand.
KERBEROS="${KERBEROS:-mit}"
# Generate Autotools files.
./bootstrap
# Build everything with Clang first, with warnings enabled.
if [ "$KERBEROS" = 'heimdal' ]; then
./configure CC=clang PATH_KRB5_CONFIG=/usr/bin/krb5-config.heimdal
else
./configure CC=clang
fi
make warnings
# Then rebuild everything with GCC with warnings enabled.
make distclean
if [ "$KERBEROS" = 'heimdal' ]; then
./configure CC=gcc PATH_KRB5_CONFIG=/usr/bin/krb5-config.heimdal
else
./configure CC=gcc
fi
make warnings
# Run the tests with valgrind.
make check-valgrind
# Run additional style tests, but only in the MIT build.
if [ "$KERBEROS" = "mit" ]; then
make check-cppcheck
fi
+145
View File
@@ -0,0 +1,145 @@
dnl Autoconf configuration for pam-krb5.
dnl
dnl Written by Russ Allbery <eagle@eyrie.org>
dnl Copyright 2005-2009, 2014, 2017, 2020-2021 Russ Allbery <eagle@eyrie.org>
dnl Copyright 2009-2013
dnl The Board of Trustees of the Leland Stanford Junior University
dnl Copyright 2005 Andres Salomon <dilinger@debian.org>
dnl Copyright 1999-2000 Frank Cusack <fcusack@fcusack.com>
dnl
dnl SPDX-License-Identifier: BSD-3-clause or GPL-1+
AC_PREREQ([2.64])
AC_INIT([pam-krb5], [4.11], [eagle@eyrie.org])
AC_CONFIG_AUX_DIR([build-aux])
AC_CONFIG_LIBOBJ_DIR([portable])
AC_CONFIG_MACRO_DIR([m4])
AM_INIT_AUTOMAKE([1.11 check-news dist-xz foreign silent-rules subdir-objects
-Wall -Werror])
AM_MAINTAINER_MODE
dnl Detect unexpanded macros.
m4_pattern_forbid([^PKG_])
m4_pattern_forbid([^_?RRA_])
AC_PROG_CC
AC_USE_SYSTEM_EXTENSIONS
RRA_PROG_CC_WARNINGS_FLAGS
AC_SYS_LARGEFILE
AM_PROG_CC_C_O
m4_ifdef([AM_PROG_AR], [AM_PROG_AR])
AC_PROG_INSTALL
LT_INIT([disable-static])
AC_CANONICAL_HOST
RRA_LD_VERSION_SCRIPT
dnl Only used for the test suite.
AC_ARG_VAR([PATH_OPENSSL], [Path to openssl for the test suite])
AC_PATH_PROG([PATH_OPENSSL], [openssl])
AS_IF([test x"$PATH_OPENSSL" != x],
[AC_DEFINE_UNQUOTED([PATH_OPENSSL], ["$PATH_OPENSSL"],
[Define to the full path to openssl for some tests.])])
AC_ARG_VAR([PATH_VALGRIND], [Path to valgrind for the test suite])
AC_PATH_PROG([PATH_VALGRIND], [valgrind])
dnl Probe for the functionality of the PAM libraries and their include file
dnl naming. Mac OS X puts them in pam/* instead of security/*.
AC_SEARCH_LIBS([pam_set_data], [pam])
AC_CHECK_FUNCS([pam_getenv pam_getenvlist pam_modutil_getpwnam])
AC_REPLACE_FUNCS([pam_syslog pam_vsyslog])
AC_CHECK_HEADERS([security/pam_modutil.h], [],
[AC_CHECK_HEADERS([pam/pam_modutil.h])])
AC_CHECK_HEADERS([security/pam_appl.h], [],
[AC_CHECK_HEADERS([pam/pam_appl.h], [],
[AC_MSG_ERROR([No PAM header files found])])])
AC_CHECK_HEADERS([security/pam_ext.h], [],
[AC_CHECK_HEADERS([pam/pam_ext.h])])
RRA_HEADER_PAM_CONST
RRA_HEADER_PAM_STRERROR_CONST
AC_DEFINE([MODULE_NAME], ["pam_krb5"],
[The name of the PAM module, used by the pam_vsyslog replacement.])
dnl Probe for the location and functionality of the Kerberos libraries.
RRA_LIB_KRB5
RRA_LIB_KRB5_SWITCH
AC_CHECK_HEADERS([hx509_err.h])
AC_CHECK_MEMBER([krb5_creds.session],
[AC_DEFINE([HAVE_KRB5_HEIMDAL], [1],
[Define if your Kerberos implementation is Heimdal.])],
[AC_DEFINE([HAVE_KRB5_MIT], [1],
[Define if your Kerberos implementation is MIT.])],
[RRA_INCLUDES_KRB5])
AC_CHECK_TYPES([krb5_realm], [], [], [RRA_INCLUDES_KRB5])
AC_CHECK_FUNCS([krb5_cc_get_full_name \
krb5_data_free \
krb5_free_default_realm \
krb5_free_string \
krb5_get_init_creds_opt_alloc \
krb5_get_init_creds_opt_set_anonymous \
krb5_get_init_creds_opt_set_change_password_prompt \
krb5_get_init_creds_opt_set_default_flags \
krb5_get_init_creds_opt_set_fast_ccache_name \
krb5_get_init_creds_opt_set_out_ccache \
krb5_get_init_creds_opt_set_pa \
krb5_get_prompt_types \
krb5_init_secure_context \
krb5_principal_get_realm \
krb5_principal_set_comp_string \
krb5_set_password \
krb5_set_trace_filename \
krb5_verify_init_creds_opt_init \
krb5_xfree])
AC_CHECK_FUNCS([krb5_get_init_creds_opt_set_pkinit],
[RRA_FUNC_KRB5_GET_INIT_CREDS_OPT_SET_PKINIT_ARGS])
AC_CHECK_FUNCS([krb5_get_init_creds_opt_free],
[RRA_FUNC_KRB5_GET_INIT_CREDS_OPT_FREE_ARGS])
AC_CHECK_DECLS([krb5_kt_free_entry], [], [], [RRA_INCLUDES_KRB5])
AC_CHECK_FUNCS([krb5_appdefault_string], [],
[AC_CHECK_FUNCS([krb5_get_profile])
AC_CHECK_HEADERS([k5profile.h profile.h])
AC_LIBOBJ([krb5-profile])])
AC_LIBOBJ([krb5-extra])
RRA_LIB_KRB5_RESTORE
dnl The kadmin client libraries are only used for the test suite.
RRA_LIB_KADM5CLNT_OPTIONAL
RRA_LIB_KADM5CLNT_SWITCH
AC_CHECK_HEADERS([kadm5/kadm5_err.h])
AC_CHECK_FUNCS([kadm5_init_krb5_context kadm5_init_with_skey_ctx])
RRA_LIB_KADM5CLNT_RESTORE
dnl Regex support is only used for the test suite.
AC_CHECK_HEADER([regex.h], [AC_CHECK_FUNCS([regcomp])])
dnl Other probes of the system libraries.
AC_HEADER_STDBOOL
AC_CHECK_HEADERS([strings.h sys/bittypes.h sys/select.h sys/time.h])
AC_CHECK_DECLS([reallocarray])
AC_TYPE_LONG_LONG_INT
AC_CHECK_TYPES([ssize_t], [], [],
[#include <sys/types.h>])
AC_CHECK_FUNCS([explicit_bzero])
AC_REPLACE_FUNCS([asprintf issetugid mkstemp reallocarray strndup])
dnl Try to specify the binding so that any references within the PAM module
dnl are resolved to the functions in that module in preference to any external
dnl function.
dnl
dnl More platforms could be handled here. Contributions welcome.
AS_CASE([$host],
[*-hpux*],
[AS_IF([test x"$GCC" = x"yes"],
[AM_LDFLAGS="-Wl,-Bsymbolic $AM_LDFLAGS"],
[AM_LDFLAGS="-Wl,+vshlibunsats $AM_LDFLAGS"])],
[*-linux*],
[AM_LDFLAGS="-Wl,-z,defs -Wl,-Bsymbolic $AM_LDFLAGS"],
[*-solaris2*],
[AS_IF([test x"$GCC" = x"yes"],
[AM_LDFLAGS="-Wl,-Bsymbolic $AM_LDFLAGS"],
[AM_LDFLAGS="-Wl,-xldscope=symbolic $AM_LDFLAGS"])])
AC_CONFIG_HEADERS([config.h])
AC_CONFIG_FILES([Makefile])
AC_OUTPUT
+551
View File
@@ -0,0 +1,551 @@
# Package metadata for pam-krb5.
#
# This file contains configuration for DocKnot used to generate
# documentation files (like README.md) and web pages. Other documentation
# in this package is generated automatically from these files as part of
# the release process. For more information, see DocKnot's documentation.
#
# DocKnot is available from <https://www.eyrie.org/~eagle/software/docknot/>.
#
# Copyright 2017, 2020-2021 Russ Allbery <eagle@eyrie.org>
#
# SPDX-License-Identifier: BSD-3-clause or GPL-1+
format: v1
name: pam-krb5
maintainer: Russ Allbery <eagle@eyrie.org>
version: '4.11'
synopsis: PAM module for Kerberos authentication
license:
name: BSD-3-clause-or-GPL-1+
copyrights:
- holder: Russ Allbery <eagle@eyrie.org>
years: 2005-2010, 2014-2015, 2017, 2020-2021
- holder: The Board of Trustees of the Leland Stanford Junior University
years: 2009-2011
- holder: Andres Salomon <dilinger@debian.org>
years: '2005'
- holder: Frank Cusack <fcusack@fcusack.com>
years: 1999-2000
build:
autoconf: '2.64'
automake: '1.11'
autotools: true
kerberos: true
manpages: true
middle: |
The module will be installed in `/usr/local/lib/security` by default, but
expect to have to override this using `--libdir`. The correct
installation path for PAM modules varies considerably between systems.
The module will always be installed in a subdirectory named `security`
under the specified value of `--libdir`. On Red Hat Linux, for example,
`--libdir=/usr/lib64` is appropriate to install the module into the system
PAM directory. On Debian's amd64 architecture,
`--libdir=/usr/lib/x86_64-linux-gnu` would be correct.
reduced_depends: true
type: Autoconf
valgrind: true
distribution:
packaging:
debian:
package: libpam-krb5
summary: |
Debian packages are available from Debian in Debian 4.0 (etch) and
later releases as libpam-krb5 and libpam-heimdal. The former packages
are built against the MIT Kerberos libraries and the latter against
the Heimdal libraries.
section: kerberos
tarname: pam-krb5
version: pam-krb5
support:
email: eagle@eyrie.org
github: rra/pam-krb5
web: https://www.eyrie.org/~eagle/software/pam-krb5/
vcs:
browse: https://git.eyrie.org/?p=kerberos/pam-krb5.git
github: rra/pam-krb5
openhub: https://www.openhub.net/p/pamkrb5
status:
workflow: build
type: Git
url: https://git.eyrie.org/git/kerberos/pam-krb5.git
quote:
author: Joyce McGreevy
date: 2003-11-17
text: |
"You're always going to have some people who can't appreciate the thrill
of a tepid change for the somewhat better," explained one source.
title: '"Look, ma, no hands!"'
work: Salon
advisories:
- date: 2020-03-30
threshold: '4.9'
versions: 4.8 and earlier
- date: 2009-02-11
threshold: '3.13'
versions: 3.12 and earlier
docs:
user:
- name: pam-krb5
title: Manual page
blurb: |
pam-krb5 is a Kerberos PAM module for either MIT Kerberos or Heimdal. It
supports ticket refreshing by screen savers, configurable authorization
handling, authentication of non-local accounts for network services,
password changing, and password expiration, as well as all the standard
expected PAM features. It works correctly with OpenSSH, even with
ChallengeResponseAuthentication and PrivilegeSeparation enabled, and
supports extensive configuration either by PAM options or in krb5.conf or
both. PKINIT is supported with recent versions of both MIT Kerberos and
Heimdal and FAST is supported with recent MIT Kerberos.
description: |
pam-krb5 provides a Kerberos PAM module that supports authentication, user
ticket cache handling, simple authorization (via .k5login or checking
Kerberos principals against local usernames), and password changing. It can
be configured through either options in the PAM configuration itself or
through entries in the system krb5.conf file, and it tries to work around
PAM implementation flaws in commonly-used PAM-enabled applications such as
OpenSSH and xdm. It supports both PKINIT and FAST to the extent that the
underlying Kerberos libraries support these features.
This is not the Kerberos PAM module maintained on Sourceforge and used on
Red Hat systems. It is an independent implementation that, if it ever
shared any common code, diverged long ago. It supports some features that
the Sourceforge module does not (particularly around authorization), and
does not support some options (particularly ones not directly related to
Kerberos) that it does. This module will never support Kerberos v4 or AFS.
For an AFS session module that works with this module (or any other Kerberos
PAM module), see
[pam-afs-session](https://www.eyrie.org/~eagle/software/pam-afs-session/).
If there are other options besides AFS and Kerberos v4 support from the
Sourceforge PAM module that you're missing in this module, please let me
know.
requirements: |
Either MIT Kerberos (or Kerberos implementations based on it) or Heimdal are
supported. MIT Keberos 1.3 or later may be required; this module has not
been tested with earlier versions.
For PKINIT support, Heimdal 0.8rc1 or later or MIT Kerberos 1.6.3 or later
are required. Earlier MIT Kerberos 1.6 releases have a bug in their
handling of PKINIT options. MIT Kerberos 1.12 or later is required to use
the use_pkinit PAM option.
For FAST (Flexible Authentication Secure Tunneling) support, MIT Kerberos
1.7 or higher is required. For anonymous FAST support, anonymous
authentication (generally anonymous PKINIT) support is required in both the
Kerberos libraries and in the local KDC.
This module should work on Linux and build with gcc or clang. It may still
work on Solaris and build with the Sun C compiler, but I have only tested it
on Linux recently. There is beta-quality support for the AIX NAS Kerberos
implementation that has not been tested in years. Other PAM implementations
will probably require some porting, although untested build system support
is present for FreeBSD, Mac OS X, and HP-UX. I personally can only test on
Linux and rely on others to report problems on other operating systems.
Old versions of OpenSSH are known to call `pam_authenticate` followed by
`pam_setcred(PAM_REINITIALIZE_CRED)` without first calling
`pam_open_session`, thereby requesting that an existing ticket cache be
renewed (similar to what a screensaver would want) rather than requesting a
new ticket cache be created. Since this behavior is indistinguishable at
the PAM level from a screensaver, pam-krb5 when used with these old versions
of OpenSSH will refresh the ticket cache of the OpenSSH daemon rather than
setting up a new ticket cache for the user. The resulting ticket cache will
have the correct permissions (this is not a security concern), but will not
be named correctly or referenced in the user's environment and will be
overwritten by the next user login. The best solution to this problem is to
upgrade OpenSSH. I'm not sure exactly when this problem was fixed, but at
the very least OpenSSH 4.3 and later do not exhibit it.
test:
lancaster: true
prefix: |
pam-krb5 comes with a comprehensive test suite, but it requires some
configuration in order to test anything other than low-level utility
functions. For the full test suite, you will need to have a running KDC
in which you can create two test accounts, one with admin access to the
other. Using a test KDC environment, if you have one, is recommended.
Follow the instructions in `tests/config/README` to configure the test
suite.
Now, you can run the test suite with:
suffix: |
The default libkadm5clnt library on the system must match the
implementation of your KDC for the module/expired test to work, since the
two kadmin protocols are not compatible. If you use the MIT library
against a Heimdal server, the test will be skipped; if you use the Heimdal
library against an MIT server, the test suite may hang.
Several `module/expired` tests are expected to fail with Heimdal 1.5 due
to a bug in Heimdal with reauthenticating immediately after a
library-mediated password change of an expired password. This is fixed in
later releases of Heimdal.
To run the full test suite, Perl 5.10 or later is required. The following
additional Perl modules will be used if present:
* Test::Pod
* Test::Spelling
All are available on CPAN. Those tests will be skipped if the modules are
not available.
sections:
- title: Configuring
body: |
Just installing the module does not enable it or change anything about
your system authentication configuration. To use the module for all
system authentication on Debian systems, put something like:
```
auth sufficient pam_krb5.so minimum_uid=1000
auth required pam_unix.so try_first_pass nullok_secure
```
in `/etc/pam.d/common-auth`, something like:
```
session optional pam_krb5.so minimum_uid=1000
session required pam_unix.so
```
in `/etc/pam.d/common-session`, and something like:
```
account required pam_krb5.so minimum_uid=1000
account required pam_unix.so
```
in `/etc/pam.d/common-account`. The `minimum_uid` setting tells the PAM
module to pass on any users with a UID lower than 1000, thereby
bypassing Kerberos authentication for the root account and any system
accounts. You normally want to do this since otherwise, if the network
is down, the Kerberos authentication can time out and make it difficult
to log in as root and fix matters. This also avoids problems with
Kerberos principals that happen to match system accounts accidentally
getting access to those accounts.
Be sure to include the module in the session group as well as the auth
group. Without the session entry, the user's ticket cache will not be
created properly for ssh logins (among possibly others).
If your users should normally all use Kerberos passwords exclusively,
putting something like:
```
password sufficient pam_krb5.so minimum_uid=1000
password required pam_unix.so try_first_pass obscure md5
```
in `/etc/pam.d/common-password` will change users' passwords in Kerberos
by default and then only fall back on Unix if that doesn't work. (You
can make this tighter by using the more complex new-style PAM
configuration.) If you instead want to synchronize local and Kerberos
passwords and change them both at the same time, you can do something
like:
```
password required pam_unix.so obscure sha512
password required pam_krb5.so use_authtok minimum_uid=1000
```
If you have multiple environments that you want to synchronize and you
don't want password changes to continue if the Kerberos password change
fails, use the `clear_on_fail` option. For example:
```
password required pam_krb5.so clear_on_fail minimum_uid=1000
password required pam_unix.so use_authtok obscure sha512
password required pam_smbpass.so use_authtok
```
In this case, if `pam_krb5` cannot change the password (due to password
strength rules on the KDC, for example), it will clear the stored
password (because of the `clear_on_fail` option), and since `pam_unix`
and `pam_smbpass` are both configured with `use_authtok`, they will both
fail. `clear_on_fail` is not the default because it would interfere
with the more common pattern of falling back to local passwords if the
user doesn't exist in Kerberos.
If you use a more complex configuration with the Linux PAM `[]` syntax
for the session and account groups, note that `pam_krb5` returns a
status of ignore, not success, if the user didn't log on with Kerberos.
You may need to handle that explicitly with `ignore=ignore` in your
action list.
There are many, many other possibilities. See the Linux PAM
documentation for all the configuration options.
On Red Hat systems, modify `/etc/pam.d/system-auth` instead, which
contains all of the configuration for the different stacks.
You can also use pam-krb5 only for specific services. In that case,
modify the files in `/etc/pam.d` for that particular service to use
`pam_krb5.so` for authentication. For services that are using passwords
over TLS to authenticate users, you may want to use the `ignore_k5login`
and `no_ccache` options to the authenticate module. `.k5login`
authorization is only meaningful for local accounts and ticket caches
are usually (although not always) only useful for interactive sessions.
Configuring the module for Solaris is both simpler and less flexible,
since Solaris (at least Solaris 8 and 9, which are the last versions of
Solaris with which this module was extensively tested) use a single
`/etc/pam.conf` file that contains configuration for all programs. For
console login on Solaris, try something like:
```
login auth sufficient /usr/local/lib/security/pam_krb5.so minimum_uid=100
login auth required /usr/lib/security/pam_unix_auth.so.1 use_first_pass
login account required /usr/local/lib/security/pam_krb5.so minimum_uid=100
login account required /usr/lib/security/pam_unix_account.so.1
login session required /usr/local/lib/security/pam_krb5.so retain_after_close minimum_uid=100
login session required /usr/lib/security/pam_unix_session.so.1
```
A similar configuration could be used for other services, such as ssh.
See the pam.conf(5) man page for more information. When using this
module with Solaris login (at least on Solaris 8 and 9), you will
probably also need to add `retain_after_close` to the PAM configuration
to avoid having the user's credentials deleted before they are logged
in.
The Solaris Kerberos library reportedly does not support prompting for a
password change of an expired account during authentication. Supporting
password change for expired accounts on Solaris with native Kerberos may
therefore require setting the `defer_pwchange` or `force_pwchange`
option for selected login applications. See the description and
warnings about that option in the pam_krb5(5) man page.
Some configuration options may be put in the `krb5.conf` file used by
your Kerberos libraries (usually `/etc/krb5.conf` or
`/usr/local/etc/krb5.conf`) instead or in addition to the PAM
configuration. See the man page for more details.
The Kerberos library, via pam-krb5, will prompt the user to change their
password if their password is expired, but when using OpenSSH, this will
only work when `ChallengeResponseAuthentication` is enabled. Unless
this option is enabled, OpenSSH doesn't pass PAM messages to the user
and can only respond to a simple password prompt.
If you are using MIT Kerberos, be aware that users whose passwords are
expired will not be prompted to change their password unless the KDC
configuration for your realm in `[realms]` in `krb5.conf` contains a
`master_kdc` setting or, if using DNS SRV records, you have a DNS entry
for `_kerberos-master` as well as `_kerberos`.
- title: Debugging
body: |
The first step when debugging any problems with this module is to add
`debug` to the PAM options for the module (either in the PAM
configuration or in `krb5.conf`). This will significantly increase the
logging from the module and should provide a trace of exactly what
failed and any available error information.
Many Kerberos authentication problems are due to configuration issues in
`krb5.conf`. If pam-krb5 doesn't work, first check that `kinit` works
on the same system. That will test your basic Kerberos configuration.
If the system has a keytab file installed that's readable by the process
doing authentication via PAM, make sure that the keytab is current and
contains a key for `host/<system>` where <system> is the fully-qualified
hostname. pam-krb5 prevents KDC spoofing by checking the user's
credentials when possible, but this means that if a keytab is present it
must be correct or authentication will fail. You can check the keytab
with `klist -k` and `kinit -k`.
Be sure that all libraries and modules, including PAM modules, loaded by
a program use the same Kerberos libraries. Sometimes programs that use
PAM, such as current versions of OpenSSH, also link against Kerberos
directly. If your sshd is linked against one set of Kerberos libraries
and pam-krb5 is linked against a different set of Kerberos libraries,
this will often cause problems (such as segmentation faults, bus errors,
assertions, or other strange behavior). Similar issues apply to the
com_err library or any other library used by both modules and shared
libraries and by the application that loads them. If your OS ships
Kerberos libraries, it's usually best if possible to build all Kerberos
software on the system against those libraries.
- title: Implementation Notes
body: |
The normal sequence of actions taken for a user login is:
```
pam_authenticate
pam_setcred(PAM_ESTABLISH_CRED)
pam_open_session
pam_acct_mgmt
```
and then at logout:
```
pam_close_session
```
followed by closing the open PAM session. The corresponding `pam_sm_*`
functions in this module are called when an application calls those
public interface functions. Not all applications call all of those
functions, or in particularly that order, although `pam_authenticate` is
always first and has to be.
When `pam_authenticate` is called, pam-krb5 creates a temporary ticket
cache in `/tmp` and sets the PAM environment variable `PAM_KRB5CCNAME`
to point to it. This ticket cache will be automatically destroyed when
the PAM session is closed and is there only to pass the initial
credentials to the call to `pam_setcred`. The module would use a memory
cache, but memory caches will only work if the application preserves the
PAM environment between the calls to `pam_authenticate` and
`pam_setcred`. Most do, but OpenSSH notoriously does not and calls
`pam_authenticate` in a subprocess, so this method is used to pass the
tickets to the `pam_setcred` call in a different process.
`pam_authenticate` does a complete authentication, including checking
the resulting TGT by obtaining a service ticket for the local host if
possible, but this requires read access to the system keytab. If the
keytab doesn't exist, can't be read, or doesn't include the appropriate
credentials, the default is to accept the authentication. This can be
controlled by setting `verify_ap_req_nofail` to true in `[libdefaults]`
in `/etc/krb5.conf`. `pam_authenticate` also does a basic authorization
check, by default calling `krb5_kuserok` (which uses `~/.k5login` if
available and falls back to checking that the principal corresponds to
the account name). This can be customized with several options
documented in the pam_krb5(5) man page.
pam-krb5 treats `pam_open_session` and `pam_setcred(PAM_ESTABLISH_CRED)`
as synonymous, as some applications call one and some call the other.
Both copy the initial credentials from the temporary cache into a
permanent cache for this session and set `KRB5CCNAME` in the
environment. It will remember when the credential cache has been
established and then avoid doing any duplicate work afterwards, since
some applications call `pam_setcred` or `pam_open_session` multiple
times (most notably X.Org 7 and earlier xdm, which also throws away the
module settings the last time it calls them).
`pam_acct_mgmt` finds the ticket cache, reads it in to obtain the
authenticated principal, and then does is another authorization check
against `.k5login` or the local account name as described above.
After the call to `pam_setcred` or `pam_open_session`, the ticket cache
will be destroyed whenever the calling application either destroys the
PAM environment or calls `pam_close_session`, which it should do on user
logout.
The normal sequence of events when refreshing a ticket cache (such as
inside a screensaver) is:
```
pam_authenticate
pam_setcred(PAM_REINITIALIZE_CRED)
pam_acct_mgmt
```
(`PAM_REFRESH_CRED` may be used instead.) Authentication proceeds as
above. At the `pam_setcred` stage, rather than creating a new ticket
cache, the module instead finds the current ticket cache (from the
`KRB5CCNAME` environment variable or the default ticket cache location
from the Kerberos library) and then reinitializes it with the
credentials from the temporary `pam_authenticate` ticket cache. When
refreshing a ticket cache, the application should not open a session.
Calling `pam_acct_mgmt` is optional; pam-krb5 doesn't do anything
different when it's called in this case.
If `pam_authenticate` apparently didn't succeed, or if an account was
configured to be ignored via `ignore_root` or `minimum_uid`,
`pam_setcred` (and therefore `pam_open_session`) and `pam_acct_mgmt`
return `PAM_IGNORE`, which tells the PAM library to proceed as if that
module wasn't listed in the PAM configuration at all.
`pam_authenticate`, however, returns failure in the ignored user case by
default, since otherwise a configuration using `ignore_root` with
pam-krb5 as the only PAM module would allow anyone to log in as root
without a password. There doesn't appear to be a case where returning
`PAM_IGNORE` instead would improve the module's behavior, but if you
know of a case, please let me know.
By default, `pam_authenticate` intentionally does not follow the PAM
standard for handling expired accounts and instead returns failure from
`pam_authenticate` unless the Kerberos libraries are able to change the
account password during authentication. Too many applications either do
not call `pam_acct_mgmt` or ignore its exit status. The fully correct
PAM behavior (returning success from `pam_authenticate` and
`PAM_NEW_AUTHTOK_REQD` from `pam_acct_mgmt`) can be enabled with the
`defer_pwchange` option.
The `defer_pwchange` option is unfortunately somewhat tricky to
implement. In this case, the calling sequence is:
```
pam_authenticate
pam_acct_mgmt
pam_chauthtok
pam_setcred
pam_open_session
```
During the first `pam_authenticate`, we can't obtain credentials and
therefore a ticket cache since the password is expired. But
`pam_authenticate` isn't called again after `pam_chauthtok`, so
`pam_chauthtok` has to create a ticket cache. We however don't want it
to do this for the normal password change (`passwd`) case.
What we do is set a flag in our PAM data structure saying that we're
processing an expired password, and `pam_chauthtok`, if it sees that
flag, redoes the authentication with password prompting disabled after
it finishes changing the password.
Unfortunately, when handling password changes this way, `pam_chauthtok`
will always have to prompt the user for their current password again
even though they just typed it. This is because the saved
authentication tokens are cleared after `pam_authenticate` returns, for
security reasons. We could hack around this by saving the password in
our PAM data structure, but this would let the application gain access
to it (exactly what the clearing is intended to prevent) and breaks a
PAM library guarantee. We could also work around this by having
`pam_authenticate` get the `kadmin/changepw` authenticator in the
expired password case and store it for `pam_chauthtok`, but it doesn't
seem worth the hassle.
- title: History and Acknowledgements
body: |
Originally written by Frank Cusack <fcusack@fcusack.com>, with the
following acknowledgement:
> Thanks to Naomaru Itoi <itoi@eecs.umich.edu>, Curtis King
> <curtis.king@cul.ca>, and Derrick Brashear <shadow@dementia.org>, all
> of whom have written and made available Kerberos 4/5 modules.
> Although no code in this module is directly from these author's
> modules, (except the get_user_info() routine in support.c; derived
> from whichever of these authors originally wrote the first module the
> other 2 copied from), it was extremely helpful to look over their code
> which aided in my design.
The module was then patched for the FreeBSD ports collection with
additional modifications by unknown maintainers and then was modified by
Joel Kociolek <joko@logidee.com> to be usable with Debian GNU/Linux.
It was packaged by Sam Hartman as the Kerberos v5 PAM module for Debian
and improved and modified by him and later by Russ Allbery to fix bugs
and add additional features. It was then adopted by Andres Salomon, who
added support for refreshing credentials.
The current distribution is maintained by Russ Allbery, who also added
support for reading configuration from `krb5.conf`, added many features
for compatibility with the Sourceforge module, commented and
standardized the formatting of the code, and overhauled the
documentation.
Thanks to Douglas E. Engert for the initial implementation of PKINIT
support. I have since modified and reworked it extensively, so any bugs
or compilation problems are my fault.
Thanks to Markus Moeller for lots of debugging and multiple patches and
suggestions for improved portability.
Thanks to Booker Bense for the implementation of the `alt_auth_map`
option.
Thanks to Sam Hartman for the FAST support implementation.
+1056
View File
File diff suppressed because it is too large Load Diff
+131
View File
@@ -0,0 +1,131 @@
dnl Check whether the compiler supports particular flags.
dnl
dnl Provides RRA_PROG_CC_FLAG and RRA_PROG_LD_FLAG, which checks whether a
dnl compiler supports a given flag for either compilation or linking,
dnl respectively. If it does, the commands in the second argument are run.
dnl If not, the commands in the third argument are run.
dnl
dnl Provides RRA_PROG_CC_WARNINGS_FLAGS, which checks whether a compiler
dnl supports a large set of warning flags and sets the WARNINGS_CFLAGS
dnl substitution variable to all of the supported warning flags. (Note that
dnl this may be too aggressive for some people.)
dnl
dnl Depends on RRA_PROG_CC_CLANG.
dnl
dnl The canonical version of this file is maintained in the rra-c-util
dnl package, available at <https://www.eyrie.org/~eagle/software/rra-c-util/>.
dnl
dnl Copyright 2016-2021 Russ Allbery <eagle@eyrie.org>
dnl Copyright 2006, 2009, 2016
dnl by Internet Systems Consortium, Inc. ("ISC")
dnl
dnl Permission to use, copy, modify, and/or distribute this software for any
dnl purpose with or without fee is hereby granted, provided that the above
dnl copyright notice and this permission notice appear in all copies.
dnl
dnl THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
dnl REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
dnl MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY
dnl SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
dnl WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
dnl ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
dnl IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
dnl
dnl SPDX-License-Identifier: ISC
dnl Used to build the result cache name.
AC_DEFUN([_RRA_PROG_CC_FLAG_CACHE],
[translit([rra_cv_compiler_c_$1], [-=+,], [____])])
AC_DEFUN([_RRA_PROG_LD_FLAG_CACHE],
[translit([rra_cv_linker_c_$1], [-=+,], [____])])
dnl Check whether a given flag is supported by the compiler when compiling a C
dnl source file.
AC_DEFUN([RRA_PROG_CC_FLAG],
[AC_REQUIRE([AC_PROG_CC])
AC_MSG_CHECKING([if $CC supports $1])
AC_CACHE_VAL([_RRA_PROG_CC_FLAG_CACHE([$1])],
[save_CFLAGS=$CFLAGS
AS_CASE([$1],
[-Wno-*], [CFLAGS="$CFLAGS `AS_ECHO(["$1"]) | sed 's/-Wno-/-W/'`"],
[*], [CFLAGS="$CFLAGS $1"])
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([], [int foo = 0;])],
[_RRA_PROG_CC_FLAG_CACHE([$1])=yes],
[_RRA_PROG_CC_FLAG_CACHE([$1])=no])
CFLAGS=$save_CFLAGS])
AC_MSG_RESULT([$_RRA_PROG_CC_FLAG_CACHE([$1])])
AS_IF([test x"$_RRA_PROG_CC_FLAG_CACHE([$1])" = xyes], [$2], [$3])])
dnl Check whether a given flag is supported by the compiler when linking an
dnl executable.
AC_DEFUN([RRA_PROG_LD_FLAG],
[AC_REQUIRE([AC_PROG_CC])
AC_MSG_CHECKING([if $CC supports $1 for linking])
AC_CACHE_VAL([_RRA_PROG_LD_FLAG_CACHE([$1])],
[save_LDFLAGS=$LDFLAGS
LDFLAGS="$LDFLAGS $1"
AC_LINK_IFELSE([AC_LANG_PROGRAM([], [int foo = 0;])],
[_RRA_PROG_LD_FLAG_CACHE([$1])=yes],
[_RRA_PROG_LD_FLAG_CACHE([$1])=no])
LDFLAGS=$save_LDFLAGS])
AC_MSG_RESULT([$_RRA_PROG_LD_FLAG_CACHE([$1])])
AS_IF([test x"$_RRA_PROG_LD_FLAG_CACHE([$1])" = xyes], [$2], [$3])])
dnl Determine the full set of viable warning flags for the current compiler.
dnl
dnl This is based partly on personal preference and is a fairly aggressive set
dnl of warnings. Desirable CC warnings that can't be turned on due to other
dnl problems:
dnl
dnl -Wsign-conversion Too many fiddly changes for the benefit
dnl -Wstack-protector Too many false positives from small buffers
dnl
dnl Last checked against gcc 9.2.1 (2019-09-01). -D_FORTIFY_SOURCE=2 enables
dnl warn_unused_result attribute markings on glibc functions on Linux, which
dnl catches a few more issues. Add -O2 because gcc won't find some warnings
dnl without optimization turned on.
dnl
dnl For Clang, we try to use -Weverything, but we have to disable some of the
dnl warnings:
dnl
dnl -Wcast-qual Some structs require casting away const
dnl -Wdisabled-macro-expansion Triggers on libc (sigaction.sa_handler)
dnl -Wpadded Not an actual problem
dnl -Wreserved-id-macros Autoconf sets several of these normally
dnl -Wsign-conversion Too many fiddly changes for the benefit
dnl -Wtautological-pointer-compare False positives with for loops
dnl -Wundef Conflicts with Autoconf probe results
dnl -Wunreachable-code Happens with optional compilation
dnl -Wunreachable-code-return Other compilers get confused
dnl -Wunused-macros Often used on suppressed branches
dnl -Wused-but-marked-unused Happens a lot with conditional code
dnl
dnl Sets WARNINGS_CFLAGS as a substitution variable.
AC_DEFUN([RRA_PROG_CC_WARNINGS_FLAGS],
[AC_REQUIRE([RRA_PROG_CC_CLANG])
AS_IF([test x"$CLANG" = xyes],
[WARNINGS_CFLAGS="-Werror"
m4_foreach_w([flag],
[-Weverything -Wno-cast-qual -Wno-disabled-macro-expansion -Wno-padded
-Wno-sign-conversion -Wno-reserved-id-macro
-Wno-tautological-pointer-compare -Wno-undef -Wno-unreachable-code
-Wno-unreachable-code-return -Wno-unused-macros
-Wno-used-but-marked-unused],
[RRA_PROG_CC_FLAG(flag,
[WARNINGS_CFLAGS="${WARNINGS_CFLAGS} flag"])])],
[WARNINGS_CFLAGS="-g -O2 -D_FORTIFY_SOURCE=2 -Werror"
m4_foreach_w([flag],
[-fstrict-overflow -fstrict-aliasing -Wall -Wextra -Wformat=2
-Wformat-overflow=2 -Wformat-signedness -Wformat-truncation=2
-Wnull-dereference -Winit-self -Wswitch-enum -Wstrict-overflow=5
-Wmissing-format-attribute -Walloc-zero -Wduplicated-branches
-Wduplicated-cond -Wtrampolines -Wfloat-equal
-Wdeclaration-after-statement -Wshadow -Wpointer-arith
-Wbad-function-cast -Wcast-align -Wwrite-strings -Wconversion
-Wno-sign-conversion -Wdate-time -Wjump-misses-init -Wlogical-op
-Wstrict-prototypes -Wold-style-definition -Wmissing-prototypes
-Wmissing-declarations -Wnormalized=nfc -Wpacked -Wredundant-decls
-Wrestrict -Wnested-externs -Winline -Wvla],
[RRA_PROG_CC_FLAG(flag,
[WARNINGS_CFLAGS="${WARNINGS_CFLAGS} flag"])])])
AC_SUBST([WARNINGS_CFLAGS])])
+28
View File
@@ -0,0 +1,28 @@
dnl Determine whether the current compiler is Clang.
dnl
dnl If the current compiler is Clang, set the shell variable CLANG to yes.
dnl
dnl The canonical version of this file is maintained in the rra-c-util
dnl package, available at <https://www.eyrie.org/~eagle/software/rra-c-util/>.
dnl
dnl Copyright 2015 Russ Allbery <eagle@eyrie.org>
dnl
dnl This file is free software; the authors give unlimited permission to copy
dnl and/or distribute it, with or without modifications, as long as this
dnl notice is preserved.
dnl
dnl SPDX-License-Identifier: FSFULLR
dnl Source used by RRA_PROG_CC_CLANG.
AC_DEFUN([_RRA_PROG_CC_CLANG_SOURCE], [[
#if ! __clang__
#error
#endif
]])
AC_DEFUN([RRA_PROG_CC_CLANG],
[AC_CACHE_CHECK([if the compiler is Clang], [rra_cv_prog_cc_clang],
[AC_COMPILE_IFELSE([AC_LANG_SOURCE([_RRA_PROG_CC_CLANG_SOURCE])],
[rra_cv_prog_cc_clang=yes],
[rra_cv_prog_cc_clang=no])])
AS_IF([test x"$rra_cv_prog_cc_clang" = xyes], [CLANG=yes])])
+103
View File
@@ -0,0 +1,103 @@
dnl Find the compiler and linker flags for the kadmin client library.
dnl
dnl Finds the compiler and linker flags for linking with the kadmin client
dnl library. Provides the --with-kadm5clnt, --with-kadm5clnt-include, and
dnl --with-kadm5clnt-lib configure option to specify a non-standard path to
dnl the library. Uses krb5-config where available unless reduced dependencies
dnl is requested or --with-kadm5clnt-include or --with-kadm5clnt-lib are
dnl given.
dnl
dnl Provides the macros RRA_LIB_KADM5CLNT and RRA_LIB_KADM5CLNT_OPTIONAL and
dnl sets the substitution variables KADM5CLNT_CPPFLAGS, KADM5CLNT_LDFLAGS, and
dnl KADM5CLNT_LIBS. Also provides RRA_LIB_KADM5CLNT_SWITCH to set CPPFLAGS,
dnl LDFLAGS, and LIBS to include the kadmin client libraries, saving the
dnl ecurrent values, and RRA_LIB_KADM5CLNT_RESTORE to restore those settings
dnl to before the last RRA_LIB_KADM5CLNT_SWITCH. Defines HAVE_KADM5CLNT and
dnl sets rra_use_KADM5CLNT to true if the library is found.
dnl
dnl Depends on the RRA_LIB helper routines.
dnl
dnl The canonical version of this file is maintained in the rra-c-util
dnl package, available at <https://www.eyrie.org/~eagle/software/rra-c-util/>.
dnl
dnl Written by Russ Allbery <eagle@eyrie.org>
dnl Copyright 2005-2009, 2011, 2013
dnl The Board of Trustees of the Leland Stanford Junior University
dnl
dnl This file is free software; the authors give unlimited permission to copy
dnl and/or distribute it, with or without modifications, as long as this
dnl notice is preserved.
dnl
dnl SPDX-License-Identifier: FSFULLR
dnl Save the current CPPFLAGS, LDFLAGS, and LIBS settings and switch to
dnl versions that include the kadmin client flags. Used as a wrapper, with
dnl RRA_LIB_KADM5CLNT_RESTORE, around tests.
AC_DEFUN([RRA_LIB_KADM5CLNT_SWITCH], [RRA_LIB_HELPER_SWITCH([KADM5CLNT])])
dnl Restore CPPFLAGS, LDFLAGS, and LIBS to their previous values (before
dnl RRA_LIB_KADM5CLNT_SWITCH was called).
AC_DEFUN([RRA_LIB_KADM5CLNT_RESTORE], [RRA_LIB_HELPER_RESTORE([KADM5CLNT])])
dnl Set KADM5CLNT_CPPFLAGS and KADM5CLNT_LDFLAGS based on rra_KADM5CLNT_root,
dnl rra_KADM5CLNT_libdir, and rra_KADM5CLNT_includedir.
AC_DEFUN([_RRA_LIB_KADM5CLNT_PATHS], [RRA_LIB_HELPER_PATHS([KADM5CLNT])])
dnl Does the appropriate library checks for reduced-dependency kadmin client
dnl linkage. The single argument, if "true", says to fail if the kadmin
dnl client library could not be found.
AC_DEFUN([_RRA_LIB_KADM5CLNT_REDUCED],
[RRA_LIB_KADM5CLNT_SWITCH
AC_CHECK_LIB([kadm5clnt], [kadm5_init_with_password],
[KADM5CLNT_LIBS=-lkadm5clnt],
[AS_IF([test x"$1" = xtrue],
[AC_MSG_ERROR([cannot find usable kadmin client library])])])
RRA_LIB_KADM5CLNT_RESTORE])
dnl Sanity-check the results of krb5-config and be sure we can really link a
dnl GSS-API program. If not, fall back on the manual check.
AC_DEFUN([_RRA_LIB_KADM5CLNT_CHECK],
[RRA_LIB_HELPER_CHECK([$1], [KADM5CLNT], [kadm5_init_with_password],
[kadmin client])])
dnl Determine GSS-API compiler and linker flags from krb5-config.
AC_DEFUN([_RRA_LIB_KADM5CLNT_CONFIG],
[RRA_KRB5_CONFIG([${rra_KADM5CLNT_root}], [kadm-client], [KADM5CLNT],
[_RRA_LIB_KADM5CLNT_CHECK([$1])],
[_RRA_LIB_KADM5CLNT_PATHS
_RRA_LIB_KADM5CLNT_REDUCED([$1])])])
dnl The core of the library checking, shared between RRA_LIB_KADM5CLNT and
dnl RRA_LIB_KADM5CLNT_OPTIONAL. The single argument, if "true", says to fail
dnl if the kadmin client library could not be found.
AC_DEFUN([_RRA_LIB_KADM5CLNT_INTERNAL],
[AC_REQUIRE([RRA_ENABLE_REDUCED_DEPENDS])
AS_IF([test x"$rra_reduced_depends" = xtrue],
[_RRA_LIB_KADM5CLNT_PATHS
_RRA_LIB_KADM5CLNT_REDUCED([$1])],
[AS_IF([test x"$rra_KADM5CLNT_includedir" = x \
&& test x"$rra_KADM5CLNT_libdir" = x],
[_RRA_LIB_KADM5CLNT_CONFIG([$1])],
[_RRA_LIB_KADM5CLNT_PATHS
_RRA_LIB_KADM5CLNT_REDUCED([$1])])])])
dnl The main macro for packages with mandatory kadmin client support.
AC_DEFUN([RRA_LIB_KADM5CLNT],
[RRA_LIB_HELPER_VAR_INIT([KADM5CLNT])
RRA_LIB_HELPER_WITH([kadm-client], [kadmin client], [KADM5CLNT])
_RRA_LIB_KADM5CLNT_INTERNAL([true])
rra_use_KADM5CLNT=true
AC_DEFINE([HAVE_KADM5CLNT], 1, [Define to enable kadmin client features.])])
dnl The main macro for packages with optional kadmin client support.
AC_DEFUN([RRA_LIB_KADM5CLNT_OPTIONAL],
[RRA_LIB_HELPER_VAR_INIT([KADM5CLNT])
RRA_LIB_HELPER_WITH_OPTIONAL([kadm-client], [kadmin client], [KADM5CLNT])
AS_IF([test x"$rra_use_KADM5CLNT" != xfalse],
[AS_IF([test x"$rra_use_KADM5CLNT" = xtrue],
[_RRA_LIB_KADM5CLNT_INTERNAL([true])],
[_RRA_LIB_KADM5CLNT_INTERNAL([false])])])
AS_IF([test x"$KADM5CLNT_LIBS" != x],
[rra_use_KADM5CLNT=true
AC_DEFINE([HAVE_KADM5CLNT], 1,
[Define to enable kadmin client features.])])])
+104
View File
@@ -0,0 +1,104 @@
dnl Use krb5-config to get link paths for Kerberos libraries.
dnl
dnl Provides one macro, RRA_KRB5_CONFIG, which attempts to get compiler and
dnl linker flags for a library via krb5-config and sets the appropriate shell
dnl variables. Defines the Autoconf variable PATH_KRB5_CONFIG, which can be
dnl used to find the default path to krb5-config.
dnl
dnl Depends on RRA_ENABLE_REDUCED_DEPENDS.
dnl
dnl The canonical version of this file is maintained in the rra-c-util
dnl package, available at <https://www.eyrie.org/~eagle/software/rra-c-util/>.
dnl
dnl Written by Russ Allbery <eagle@eyrie.org>
dnl Copyright 2018, 2021 Russ Allbery <eagle@eyrie.org>
dnl Copyright 2011-2012
dnl The Board of Trustees of the Leland Stanford Junior University
dnl
dnl This file is free software; the authors give unlimited permission to copy
dnl and/or distribute it, with or without modifications, as long as this
dnl notice is preserved.
dnl
dnl SPDX-License-Identifier: FSFULLR
dnl Check for krb5-config in the user's path and set PATH_KRB5_CONFIG. This
dnl is moved into a separate macro so that it can be loaded via AC_REQUIRE,
dnl meaning it will only be run once even if we link with multiple krb5-config
dnl libraries.
AC_DEFUN([_RRA_KRB5_CONFIG_PATH],
[AC_ARG_VAR([PATH_KRB5_CONFIG], [Path to krb5-config])
AC_PATH_PROG([PATH_KRB5_CONFIG], [krb5-config], [],
[${PATH}:/usr/kerberos/bin])])
dnl Check whether the --deps flag is supported by krb5-config. Takes the path
dnl to krb5-config to use. Note that this path is not embedded in the cache
dnl variable, so this macro implicitly assumes that we will always use the
dnl same krb5-config program.
AC_DEFUN([_RRA_KRB5_CONFIG_DEPS],
[AC_REQUIRE([_RRA_KRB5_CONFIG_PATH])
AC_CACHE_CHECK([for --deps support in krb5-config],
[rra_cv_krb5_config_deps],
[AS_IF(["$1" 2>&1 | grep deps >/dev/null 2>&1],
[rra_cv_krb5_config_deps=yes],
[rra_cv_krb5_config_deps=no])])])
dnl Obtain the library flags for a particular library using krb5-config.
dnl Takes the path to the krb5-config program to use, the argument to
dnl krb5-config to use, and the variable prefix under which to store the
dnl library flags.
AC_DEFUN([_RRA_KRB5_CONFIG_LIBS],
[AC_REQUIRE([_RRA_KRB5_CONFIG_PATH])
AC_REQUIRE([RRA_ENABLE_REDUCED_DEPENDS])
_RRA_KRB5_CONFIG_DEPS([$1])
AS_IF([test x"$rra_reduced_depends" = xfalse \
&& test x"$rra_cv_krb5_config_deps" = xyes],
[$3[]_LIBS=`"$1" --deps --libs $2 2>/dev/null`],
[$3[]_LIBS=`"$1" --libs $2 2>/dev/null`])])
dnl Attempt to find the flags for a library using krb5-config. Takes the
dnl following arguments (in order):
dnl
dnl 1. The root directory for the library in question, generally from an
dnl Autoconf --with flag. Used by preference as the path to krb5-config.
dnl
dnl 2. The argument to krb5-config to retrieve flags for this particular
dnl library.
dnl
dnl 3. The variable prefix to use when setting CPPFLAGS and LIBS variables
dnl based on the result of krb5-config.
dnl
dnl 4. Further actions to take if krb5-config was found and supported that
dnl library type.
dnl
dnl 5. Further actions to take if krb5-config could not be used to get flags
dnl for that library type.
dnl
dnl Special-case a krb5-config argument of krb5 and run krb5-config without an
dnl argument if that option was requested and not supported. Old versions of
dnl krb5-config didn't take an argument to specify the library type, but
dnl always returned the flags for libkrb5.
AC_DEFUN([RRA_KRB5_CONFIG],
[rra_krb5_config_$3=
rra_krb5_config_$3[]_ok=
AS_IF([test x"$1" != x && test -x "$1/bin/krb5-config"],
[rra_krb5_config_$3="$1/bin/krb5-config"],
[_RRA_KRB5_CONFIG_PATH
rra_krb5_config_$3="$PATH_KRB5_CONFIG"])
AS_IF([test x"$rra_krb5_config_$3" != x && test -x "$rra_krb5_config_$3"],
[AC_CACHE_CHECK([for $2 support in krb5-config], [rra_cv_lib_$3[]_config],
[AS_IF(["$rra_krb5_config_$3" 2>&1 | grep $2 >/dev/null 2>&1],
[rra_cv_lib_$3[]_config=yes],
[rra_cv_lib_$3[]_config=no])])
AS_IF([test "$rra_cv_lib_$3[]_config" = yes],
[$3[]_CPPFLAGS=`"$rra_krb5_config_$3" --cflags $2 2>/dev/null`
_RRA_KRB5_CONFIG_LIBS([$rra_krb5_config_$3], [$2], [$3])
rra_krb5_config_$3[]_ok=yes],
[AS_IF([test x"$2" = xkrb5],
[$3[]_CPPFLAGS=`"$rra_krb5_config_$3" --cflags 2>/dev/null`
$3[]_LIBS=`"$rra_krb5_config_$3" --libs $2 2>/dev/null`
rra_krb5_config_$3[]_ok=yes])])])
AS_IF([test x"$rra_krb5_config_$3[]_ok" = xyes],
[$3[]_CPPFLAGS=`AS_ECHO(["$$3[]_CPPFLAGS"]) | sed 's%-I/usr/include %%'`
$3[]_CPPFLAGS=`AS_ECHO(["$$3[]_CPPFLAGS"]) | sed 's%-I/usr/include$%%'`
$4],
[$5])])
+47
View File
@@ -0,0 +1,47 @@
dnl Additional probes for Kerberos PKINIT support.
dnl
dnl Additional Kerberos library probes that check behavior of the library
dnl relevant to PKINIT support. Provides the macro:
dnl
dnl RRA_FUNC_KRB5_GET_INIT_CREDS_OPT_SET_PKINIT_ARGS
dnl
dnl and defines HAVE_KRB5_GET_INIT_CREDS_OPT_SET_PKINIT_9_ARGS if it takes
dnl only nine arguments.
dnl
dnl Written by Russ Allbery <eagle@eyrie.org>
dnl Copyright 2007, 2018, 2020-2021 Russ Allbery <eagle@eyrie.org>
dnl Copyright 2011
dnl The Board of Trustees of the Leland Stanford Junior University
dnl
dnl This file is free software; the authors give unlimited permission to copy
dnl and/or distribute it, with or without modifications, as long as this
dnl notice is preserved.
dnl
dnl SPDX-License-Identifier: FSFULLR
dnl Source used by RRA_FUNC_KRB5_GET_INIT_CREDS_OPT_SET_PKINIT_ARGS.
AC_DEFUN([_RRA_FUNC_KRB5_PKINIT_ARGS_SOURCE], [RRA_INCLUDES_KRB5] [[
int
main(void)
{
krb5_context c;
krb5_get_init_creds_opt *o;
krb5_principal p;
krb5_get_init_creds_opt_set_pkinit(c, o, p, NULL, NULL, 0, NULL, NULL,
NULL);
}
]])
dnl Check whether krb5_get_init_creds_opt_set_pkinit takes eleven arguments
dnl (0.8 release candidates and later) or only nine (0.7). Defines
dnl HAVE_KRB5_GET_INIT_CREDS_OPT_SET_PKINIT_9_ARGS if it takes nine arguments.
AC_DEFUN([RRA_FUNC_KRB5_GET_INIT_CREDS_OPT_SET_PKINIT_ARGS],
[AC_CACHE_CHECK([if krb5_get_init_creds_opt_set_pkinit takes 9 arguments],
[rra_cv_func_krb5_get_init_creds_opt_set_pkinit_args],
[AC_COMPILE_IFELSE([AC_LANG_SOURCE([_RRA_FUNC_KRB5_PKINIT_ARGS_SOURCE])],
[rra_cv_func_krb5_get_init_creds_opt_set_pkinit_args=yes],
[rra_cv_func_krb5_get_init_creds_opt_set_pkinit_args=no])])
AS_IF([test $rra_cv_func_krb5_get_init_creds_opt_set_pkinit_args = yes],
[AC_DEFINE([HAVE_KRB5_GET_INIT_CREDS_OPT_SET_PKINIT_9_ARGS], 1,
[Define if krb5_get_init_creds_opt_set_pkinit takes 9 arguments.])])])
+384
View File
@@ -0,0 +1,384 @@
dnl Find the compiler and linker flags for Kerberos.
dnl
dnl Finds the compiler and linker flags for linking with Kerberos libraries.
dnl Provides the --with-krb5, --with-krb5-include, and --with-krb5-lib
dnl configure options to specify non-standard paths to the Kerberos libraries.
dnl Uses krb5-config where available unless reduced dependencies is requested
dnl or --with-krb5-include or --with-krb5-lib are given.
dnl
dnl Provides the macro RRA_LIB_KRB5 and sets the substitution variables
dnl KRB5_CPPFLAGS, KRB5_LDFLAGS, and KRB5_LIBS. Also provides
dnl RRA_LIB_KRB5_SWITCH to set CPPFLAGS, LDFLAGS, and LIBS to include the
dnl Kerberos libraries, saving the current values first, and
dnl RRA_LIB_KRB5_RESTORE to restore those settings to before the last
dnl RRA_LIB_KRB5_SWITCH. HAVE_KRB5 will always be defined if RRA_LIB_KRB5 is
dnl used.
dnl
dnl If KRB5_CPPFLAGS, KRB5_LDFLAGS, or KRB5_LIBS are set before calling these
dnl macros, their values will be added to whatever the macros discover.
dnl
dnl KRB5_CPPFLAGS_WARNINGS will be set to the same value as KRB5_CPPFLAGS but
dnl with any occurrences of -I changed to -isystem. This may be useful to
dnl suppress warnings from the Kerberos header files when building with and
dnl aggressive warning flags. Be aware that this change will change the
dnl compiler header file search order as well.
dnl
dnl Provides the RRA_LIB_KRB5_OPTIONAL macro, which should be used if Kerberos
dnl support is optional. In this case, Kerberos libraries are mandatory if
dnl --with-krb5 is given, and will not be probed for if --without-krb5 is
dnl given. Otherwise, they'll be probed for but will not be required.
dnl Defines HAVE_KRB5 and sets rra_use_KRB5 to true if the libraries are
dnl found. The substitution variables will always be set, but they will be
dnl empty unless Kerberos libraries are found and the user did not disable
dnl Kerberos support.
dnl
dnl Sets the Automake conditional KRB5_USES_COM_ERR saying whether we use
dnl com_err, since if we're also linking with AFS libraries, we may have to
dnl change library ordering in that case.
dnl
dnl Depends on RRA_KRB5_CONFIG, RRA_ENABLE_REDUCED_DEPENDS, and
dnl RRA_SET_LDFLAGS.
dnl
dnl Also provides RRA_FUNC_KRB5_GET_INIT_CREDS_OPT_FREE_ARGS, which checks
dnl whether krb5_get_init_creds_opt_free takes one argument or two. Defines
dnl HAVE_KRB5_GET_INIT_CREDS_OPT_FREE_2_ARGS if it takes two arguments.
dnl
dnl Also provides RRA_INCLUDES_KRB5, which are the headers to include when
dnl probing the Kerberos library properties.
dnl
dnl The canonical version of this file is maintained in the rra-c-util
dnl package, available at <https://www.eyrie.org/~eagle/software/rra-c-util/>.
dnl
dnl Written by Russ Allbery <eagle@eyrie.org>
dnl Copyright 2018, 2020-2021 Russ Allbery <eagle@eyrie.org>
dnl Copyright 2005-2011, 2013-2014
dnl The Board of Trustees of the Leland Stanford Junior University
dnl
dnl This file is free software; the authors give unlimited permission to copy
dnl and/or distribute it, with or without modifications, as long as this
dnl notice is preserved.
dnl
dnl SPDX-License-Identifier: FSFULLR
dnl Headers to include when probing for Kerberos library properties.
AC_DEFUN([RRA_INCLUDES_KRB5], [[
#if HAVE_KRB5_H
# include <krb5.h>
#elif HAVE_KERBEROSV5_KRB5_H
# include <kerberosv5/krb5.h>
#else
# include <krb5/krb5.h>
#endif
]])
dnl Save the current CPPFLAGS, LDFLAGS, and LIBS settings and switch to
dnl versions that include the Kerberos flags. Used as a wrapper, with
dnl RRA_LIB_KRB5_RESTORE, around tests.
AC_DEFUN([RRA_LIB_KRB5_SWITCH],
[rra_krb5_save_CPPFLAGS="$CPPFLAGS"
rra_krb5_save_LDFLAGS="$LDFLAGS"
rra_krb5_save_LIBS="$LIBS"
CPPFLAGS="$KRB5_CPPFLAGS $CPPFLAGS"
LDFLAGS="$KRB5_LDFLAGS $LDFLAGS"
LIBS="$KRB5_LIBS $LIBS"])
dnl Restore CPPFLAGS, LDFLAGS, and LIBS to their previous values (before
dnl RRA_LIB_KRB5_SWITCH was called).
AC_DEFUN([RRA_LIB_KRB5_RESTORE],
[CPPFLAGS="$rra_krb5_save_CPPFLAGS"
LDFLAGS="$rra_krb5_save_LDFLAGS"
LIBS="$rra_krb5_save_LIBS"])
dnl Set KRB5_CPPFLAGS and KRB5_LDFLAGS based on rra_krb5_root,
dnl rra_krb5_libdir, and rra_krb5_includedir.
AC_DEFUN([_RRA_LIB_KRB5_PATHS],
[AS_IF([test x"$rra_krb5_libdir" != x],
[KRB5_LDFLAGS="-L$rra_krb5_libdir"],
[AS_IF([test x"$rra_krb5_root" != x],
[RRA_SET_LDFLAGS([KRB5_LDFLAGS], [$rra_krb5_root])])])
AS_IF([test x"$rra_krb5_includedir" != x],
[KRB5_CPPFLAGS="-I$rra_krb5_includedir"],
[AS_IF([test x"$rra_krb5_root" != x],
[AS_IF([test x"$rra_krb5_root" != x/usr],
[KRB5_CPPFLAGS="-I${rra_krb5_root}/include"])])])])
dnl Check for a header using a file existence check rather than using
dnl AC_CHECK_HEADERS. This is used if there were arguments to configure
dnl specifying the Kerberos header path, since we may have one header in the
dnl default include path and another under our explicitly-configured Kerberos
dnl location. The second argument is run if the header was found.
AC_DEFUN([_RRA_LIB_KRB5_CHECK_HEADER],
[AC_MSG_CHECKING([for $1])
AS_IF([test -f "${rra_krb5_incroot}/$1"],
[AC_DEFINE_UNQUOTED(AS_TR_CPP([HAVE_$1]), [1],
[Define to 1 if you have the <$1> header file.])
AC_MSG_RESULT([yes])
$2],
[AC_MSG_RESULT([no])])])
dnl Check for the com_err header. Internal helper macro since we need
dnl to do the same checks in multiple places.
AC_DEFUN([_RRA_LIB_KRB5_CHECK_HEADER_COM_ERR],
[AS_IF([test x"$rra_krb5_incroot" = x],
[AC_CHECK_HEADERS([et/com_err.h kerberosv5/com_err.h])],
[_RRA_LIB_KRB5_CHECK_HEADER([et/com_err.h])
_RRA_LIB_KRB5_CHECK_HEADER([kerberosv5/com_err.h])])])
dnl Check for the main Kerberos header. Internal helper macro since we need
dnl to do the same checks in multiple places. The first argument is run if
dnl some header was found, and the second if no header was found.
dnl header could not be found.
AC_DEFUN([_RRA_LIB_KRB5_CHECK_HEADER_KRB5],
[rra_krb5_found_header=
AS_IF([test x"$rra_krb5_incroot" = x],
[AC_CHECK_HEADERS([krb5.h kerberosv5/krb5.h krb5/krb5.h],
[rra_krb5_found_header=true])],
[_RRA_LIB_KRB5_CHECK_HEADER([krb5.h],
[rra_krb5_found_header=true])
_RRA_LIB_KRB5_CHECK_HEADER([kerberosv5/krb5.h],
[rra_krb5_found_header=true])
_RRA_LIB_KRB5_CHECK_HEADER([krb5/krb5.h],
[rra_krb5_found_header=true])])
AS_IF([test x"$rra_krb5_found_header" = xtrue], [$1], [$2])])
dnl Does the appropriate library checks for reduced-dependency Kerberos
dnl linkage. The single argument, if true, says to fail if Kerberos could not
dnl be found.
AC_DEFUN([_RRA_LIB_KRB5_REDUCED],
[RRA_LIB_KRB5_SWITCH
AC_CHECK_LIB([krb5], [krb5_init_context],
[KRB5_LIBS="-lkrb5"
LIBS="$KRB5_LIBS $LIBS"
AC_CHECK_FUNCS([krb5_get_error_message],
[AC_CHECK_FUNCS([krb5_free_error_message])],
[AC_CHECK_FUNCS([krb5_get_error_string], [],
[AC_CHECK_FUNCS([krb5_get_err_txt], [],
[AC_CHECK_LIB([ksvc], [krb5_svc_get_msg],
[KRB5_LIBS="$KRB5_LIBS -lksvc"
AC_DEFINE([HAVE_KRB5_SVC_GET_MSG], [1])
AC_CHECK_HEADERS([ibm_svc/krb5_svc.h], [], [],
[RRA_INCLUDES_KRB5])],
[AC_CHECK_LIB([com_err], [com_err],
[KRB5_LIBS="$KRB5_LIBS -lcom_err"],
[AS_IF([test x"$1" = xtrue],
[AC_MSG_ERROR([cannot find usable com_err library])],
[KRB5_LIBS=""])])
_RRA_LIB_KRB5_CHECK_HEADER_COM_ERR])])])])
_RRA_LIB_KRB5_CHECK_HEADER_KRB5([],
[KRB5_CPPFLAGS=
KRB5_LIBS=
AS_IF([test x"$1" = xtrue],
[AC_MSG_ERROR([cannot find usable Kerberos header])])])],
[AS_IF([test x"$1" = xtrue],
[AC_MSG_ERROR([cannot find usable Kerberos library])])])
RRA_LIB_KRB5_RESTORE])
dnl Does the appropriate library checks for Kerberos linkage when we don't
dnl have krb5-config or reduced dependencies. The single argument, if true,
dnl says to fail if Kerberos could not be found.
AC_DEFUN([_RRA_LIB_KRB5_MANUAL],
[RRA_LIB_KRB5_SWITCH
rra_krb5_extra=
LIBS=
AC_SEARCH_LIBS([res_search], [resolv], [],
[AC_SEARCH_LIBS([__res_search], [resolv])])
AC_SEARCH_LIBS([gethostbyname], [nsl])
AC_SEARCH_LIBS([socket], [socket], [],
[AC_CHECK_LIB([nsl], [socket], [LIBS="-lnsl -lsocket $LIBS"], [],
[-lsocket])])
AC_SEARCH_LIBS([crypt], [crypt])
AC_SEARCH_LIBS([roken_concat], [roken])
rra_krb5_extra="$LIBS"
LIBS="$rra_krb5_save_LIBS"
AC_CHECK_LIB([krb5], [krb5_init_context],
[KRB5_LIBS="-lkrb5 -lasn1 -lcom_err -lcrypto $rra_krb5_extra"],
[AC_CHECK_LIB([krb5support], [krb5int_getspecific],
[rra_krb5_extra="-lkrb5support $rra_krb5_extra"],
[AC_CHECK_LIB([pthreads], [pthread_setspecific],
[rra_krb5_pthread="-lpthreads"],
[AC_CHECK_LIB([pthread], [pthread_setspecific],
[rra_krb5_pthread="-lpthread"])])
AC_CHECK_LIB([krb5support], [krb5int_setspecific],
[rra_krb5_extra="-lkrb5support $rra_krb5_extra $rra_krb5_pthread"],
[], [$rra_krb5_pthread $rra_krb5_extra])],
[$rra_krb5_extra])
AC_CHECK_LIB([com_err], [error_message],
[rra_krb5_extra="-lcom_err $rra_krb5_extra"], [], [$rra_krb5_extra])
AC_CHECK_LIB([ksvc], [krb5_svc_get_msg],
[rra_krb5_extra="-lksvc $rra_krb5_extra"], [], [$rra_krb5_extra])
AC_CHECK_LIB([k5crypto], [krb5int_hash_md5],
[rra_krb5_extra="-lk5crypto $rra_krb5_extra"], [], [$rra_krb5_extra])
AC_CHECK_LIB([k5profile], [profile_get_values],
[rra_krb5_extra="-lk5profile $rra_krb5_extra"], [], [$rra_krb5_extra])
AC_CHECK_LIB([krb5], [krb5_cc_default],
[KRB5_LIBS="-lkrb5 $rra_krb5_extra"],
[AS_IF([test x"$1" = xtrue],
[AC_MSG_ERROR([cannot find usable Kerberos library])])],
[$rra_krb5_extra])],
[-lasn1 -lcom_err -lcrypto $rra_krb5_extra])
LIBS="$KRB5_LIBS $LIBS"
AC_CHECK_FUNCS([krb5_get_error_message],
[AC_CHECK_FUNCS([krb5_free_error_message])],
[AC_CHECK_FUNCS([krb5_get_error_string], [],
[AC_CHECK_FUNCS([krb5_get_err_txt], [],
[AC_CHECK_FUNCS([krb5_svc_get_msg],
[AC_CHECK_HEADERS([ibm_svc/krb5_svc.h], [], [],
[RRA_INCLUDES_KRB5])],
[_RRA_LIB_KRB5_CHECK_HEADER_COM_ERR])])])])
_RRA_LIB_KRB5_CHECK_HEADER_KRB5([],
[KRB5_CPPFLAGS=
KRB5_LIBS=
AS_IF([test x"$1" = xtrue],
[AC_MSG_ERROR([cannot find usable Kerberos header])])])
RRA_LIB_KRB5_RESTORE])
dnl Sanity-check the results of krb5-config and be sure we can really link a
dnl Kerberos program. If that fails, clear KRB5_CPPFLAGS and KRB5_LIBS so
dnl that we know we don't have usable flags and fall back on the manual
dnl check.
AC_DEFUN([_RRA_LIB_KRB5_CHECK],
[RRA_LIB_KRB5_SWITCH
AC_CHECK_FUNC([krb5_init_context],
[_RRA_LIB_KRB5_CHECK_HEADER_KRB5([RRA_LIB_KRB5_RESTORE],
[RRA_LIB_KRB5_RESTORE
KRB5_CPPFLAGS=
KRB5_LIBS=
_RRA_LIB_KRB5_PATHS
_RRA_LIB_KRB5_MANUAL([$1])])],
[RRA_LIB_KRB5_RESTORE
KRB5_CPPFLAGS=
KRB5_LIBS=
_RRA_LIB_KRB5_PATHS
_RRA_LIB_KRB5_MANUAL([$1])])])
dnl Determine Kerberos compiler and linker flags from krb5-config. Does the
dnl additional probing we need to do to uncover error handling features, and
dnl falls back on the manual checks.
AC_DEFUN([_RRA_LIB_KRB5_CONFIG],
[RRA_KRB5_CONFIG([${rra_krb5_root}], [krb5], [KRB5],
[_RRA_LIB_KRB5_CHECK([$1])
RRA_LIB_KRB5_SWITCH
AC_CHECK_FUNCS([krb5_get_error_message],
[AC_CHECK_FUNCS([krb5_free_error_message])],
[AC_CHECK_FUNCS([krb5_get_error_string], [],
[AC_CHECK_FUNCS([krb5_get_err_txt], [],
[AC_CHECK_FUNCS([krb5_svc_get_msg],
[AC_CHECK_HEADERS([ibm_svc/krb5_svc.h], [], [],
[RRA_INCLUDES_KRB5])],
[_RRA_LIB_KRB5_CHECK_HEADER_COM_ERR])])])])
RRA_LIB_KRB5_RESTORE],
[_RRA_LIB_KRB5_PATHS
_RRA_LIB_KRB5_MANUAL([$1])])])
dnl The core of the library checking, shared between RRA_LIB_KRB5 and
dnl RRA_LIB_KRB5_OPTIONAL. The single argument, if "true", says to fail if
dnl Kerberos could not be found. Set up rra_krb5_incroot for later header
dnl checking.
AC_DEFUN([_RRA_LIB_KRB5_INTERNAL],
[AC_REQUIRE([RRA_ENABLE_REDUCED_DEPENDS])
rra_krb5_incroot=
AC_SUBST([KRB5_CPPFLAGS])
AC_SUBST([KRB5_CPPFLAGS_WARNINGS])
AC_SUBST([KRB5_LDFLAGS])
AC_SUBST([KRB5_LIBS])
AS_IF([test x"$rra_krb5_includedir" != x],
[rra_krb5_incroot="$rra_krb5_includedir"],
[AS_IF([test x"$rra_krb5_root" != x],
[rra_krb5_incroot="${rra_krb5_root}/include"])])
AS_IF([test x"$rra_reduced_depends" = xtrue],
[_RRA_LIB_KRB5_PATHS
_RRA_LIB_KRB5_REDUCED([$1])],
[AS_IF([test x"$rra_krb5_includedir" = x && test x"$rra_krb5_libdir" = x],
[_RRA_LIB_KRB5_CONFIG([$1])],
[_RRA_LIB_KRB5_PATHS
_RRA_LIB_KRB5_MANUAL([$1])])])
rra_krb5_uses_com_err=false
AS_CASE([$KRB5_LIBS], [*-lcom_err*], [rra_krb5_uses_com_err=true])
AM_CONDITIONAL([KRB5_USES_COM_ERR],
[test x"$rra_krb5_uses_com_err" = xtrue])
KRB5_CPPFLAGS_WARNINGS=`AS_ECHO(["$KRB5_CPPFLAGS"]) | sed 's/-I/-isystem /g'`])
dnl The main macro for packages with mandatory Kerberos support.
AC_DEFUN([RRA_LIB_KRB5],
[rra_krb5_root=
rra_krb5_libdir=
rra_krb5_includedir=
rra_use_KRB5=true
AC_ARG_WITH([krb5],
[AS_HELP_STRING([--with-krb5=DIR],
[Location of Kerberos headers and libraries])],
[AS_IF([test x"$withval" != xyes && test x"$withval" != xno],
[rra_krb5_root="$withval"])])
AC_ARG_WITH([krb5-include],
[AS_HELP_STRING([--with-krb5-include=DIR],
[Location of Kerberos headers])],
[AS_IF([test x"$withval" != xyes && test x"$withval" != xno],
[rra_krb5_includedir="$withval"])])
AC_ARG_WITH([krb5-lib],
[AS_HELP_STRING([--with-krb5-lib=DIR],
[Location of Kerberos libraries])],
[AS_IF([test x"$withval" != xyes && test x"$withval" != xno],
[rra_krb5_libdir="$withval"])])
_RRA_LIB_KRB5_INTERNAL([true])
AC_DEFINE([HAVE_KRB5], 1, [Define to enable Kerberos features.])])
dnl The main macro for packages with optional Kerberos support.
AC_DEFUN([RRA_LIB_KRB5_OPTIONAL],
[rra_krb5_root=
rra_krb5_libdir=
rra_krb5_includedir=
rra_use_KRB5=
AC_ARG_WITH([krb5],
[AS_HELP_STRING([--with-krb5@<:@=DIR@:>@],
[Location of Kerberos headers and libraries])],
[AS_IF([test x"$withval" = xno],
[rra_use_KRB5=false],
[AS_IF([test x"$withval" != xyes], [rra_krb5_root="$withval"])
rra_use_KRB5=true])])
AC_ARG_WITH([krb5-include],
[AS_HELP_STRING([--with-krb5-include=DIR],
[Location of Kerberos headers])],
[AS_IF([test x"$withval" != xyes && test x"$withval" != xno],
[rra_krb5_includedir="$withval"])])
AC_ARG_WITH([krb5-lib],
[AS_HELP_STRING([--with-krb5-lib=DIR],
[Location of Kerberos libraries])],
[AS_IF([test x"$withval" != xyes && test x"$withval" != xno],
[rra_krb5_libdir="$withval"])])
AS_IF([test x"$rra_use_KRB5" != xfalse],
[AS_IF([test x"$rra_use_KRB5" = xtrue],
[_RRA_LIB_KRB5_INTERNAL([true])],
[_RRA_LIB_KRB5_INTERNAL([false])])],
[AM_CONDITIONAL([KRB5_USES_COM_ERR], [false])])
AS_IF([test x"$KRB5_LIBS" != x],
[rra_use_KRB5=true
AC_DEFINE([HAVE_KRB5], 1, [Define to enable Kerberos features.])])])
dnl Source used by RRA_FUNC_KRB5_GET_INIT_CREDS_OPT_FREE_ARGS.
AC_DEFUN([_RRA_FUNC_KRB5_OPT_FREE_ARGS_SOURCE], [RRA_INCLUDES_KRB5] [[
int
main(void)
{
krb5_get_init_creds_opt *opts;
krb5_context c;
krb5_get_init_creds_opt_free(c, opts);
}
]])
dnl Check whether krb5_get_init_creds_opt_free takes one argument or two.
dnl Early Heimdal used to take a single argument. Defines
dnl HAVE_KRB5_GET_INIT_CREDS_OPT_FREE_2_ARGS if it takes two arguments.
dnl
dnl Should be called with RRA_LIB_KRB5_SWITCH active.
AC_DEFUN([RRA_FUNC_KRB5_GET_INIT_CREDS_OPT_FREE_ARGS],
[AC_CACHE_CHECK([if krb5_get_init_creds_opt_free takes two arguments],
[rra_cv_func_krb5_get_init_creds_opt_free_args],
[AC_COMPILE_IFELSE([AC_LANG_SOURCE([_RRA_FUNC_KRB5_OPT_FREE_ARGS_SOURCE])],
[rra_cv_func_krb5_get_init_creds_opt_free_args=yes],
[rra_cv_func_krb5_get_init_creds_opt_free_args=no])])
AS_IF([test $rra_cv_func_krb5_get_init_creds_opt_free_args = yes],
[AC_DEFINE([HAVE_KRB5_GET_INIT_CREDS_OPT_FREE_2_ARGS], 1,
[Define if krb5_get_init_creds_opt_free takes two arguments.])])])
+40
View File
@@ -0,0 +1,40 @@
dnl Check whether the linker supports --version-script.
dnl
dnl Probes whether the linker supports --version-script with a simple version
dnl script that only defines a single version. Sets the Automake conditional
dnl HAVE_LD_VERSION_SCRIPT based on whether it is supported.
dnl
dnl The canonical version of this file is maintained in the rra-c-util
dnl package, available at <https://www.eyrie.org/~eagle/software/rra-c-util/>.
dnl
dnl Written by Russ Allbery <eagle@eyrie.org>
dnl Based on the gnulib ld-version-script macro from Simon Josefsson
dnl Copyright 2010
dnl The Board of Trustees of the Leland Stanford Junior University
dnl Copyright 2008-2010 Free Software Foundation, Inc.
dnl
dnl This file is free software; the authors give unlimited permission to copy
dnl and/or distribute it, with or without modifications, as long as this
dnl notice is preserved.
dnl
dnl SPDX-License-Identifier: FSFULLR
AC_DEFUN([RRA_LD_VERSION_SCRIPT],
[AC_CACHE_CHECK([if -Wl,--version-script works], [rra_cv_ld_version_script],
[save_LDFLAGS="$LDFLAGS"
LDFLAGS="$LDFLAGS -Wl,--version-script=conftest.map"
cat > conftest.map <<EOF
VERSION_1 {
global:
sym;
local:
*;
};
EOF
AC_LINK_IFELSE([AC_LANG_PROGRAM([], [])],
[rra_cv_ld_version_script=yes], [rra_cv_ld_version_script=no])
rm -f conftest.map
LDFLAGS="$save_LDFLAGS"])
AM_CONDITIONAL([HAVE_LD_VERSION_SCRIPT],
[test x"$rra_cv_ld_version_script" = xyes])])
+30
View File
@@ -0,0 +1,30 @@
dnl Provides option to change library probes.
dnl
dnl This file provides RRA_ENABLE_REDUCED_DEPENDS, which adds the configure
dnl option --enable-reduced-depends to request that library probes assume
dnl shared libraries are in use and dependencies of libraries should not be
dnl probed. If this option is given, the shell variable rra_reduced_depends
dnl is set to true; otherwise, it is set to false.
dnl
dnl This macro doesn't do much but is defined separately so that other macros
dnl can require it with AC_REQUIRE.
dnl
dnl The canonical version of this file is maintained in the rra-c-util
dnl package, available at <https://www.eyrie.org/~eagle/software/rra-c-util/>.
dnl
dnl Written by Russ Allbery <eagle@eyrie.org>
dnl Copyright 2005-2007
dnl The Board of Trustees of the Leland Stanford Junior University
dnl
dnl This file is free software; the authors give unlimited permission to copy
dnl and/or distribute it, with or without modifications, as long as this
dnl notice is preserved.
dnl
dnl SPDX-License-Identifier: FSFULLR
AC_DEFUN([RRA_ENABLE_REDUCED_DEPENDS],
[rra_reduced_depends=false
AC_ARG_ENABLE([reduced-depends],
[AS_HELP_STRING([--enable-reduced-depends],
[Try to minimize shared library dependencies])],
[AS_IF([test x"$enableval" = xyes], [rra_reduced_depends=true])])])
+149
View File
@@ -0,0 +1,149 @@
dnl Helper functions to manage compiler variables.
dnl
dnl These are a wide variety of helper macros to make it easier to construct
dnl standard macros to probe for a library and to set library-specific
dnl CPPFLAGS, LDFLAGS, and LIBS shell substitution variables. Most of them
dnl take as one of the arguments the prefix string to use for variables, which
dnl is usually something like "KRB5" or "GSSAPI".
dnl
dnl Depends on RRA_SET_LDFLAGS.
dnl
dnl The canonical version of this file is maintained in the rra-c-util
dnl package, available at <https://www.eyrie.org/~eagle/software/rra-c-util/>.
dnl
dnl Written by Russ Allbery <eagle@eyrie.org>
dnl Copyright 2018 Russ Allbery <eagle@eyrie.org>
dnl Copyright 2011, 2013
dnl The Board of Trustees of the Leland Stanford Junior University
dnl
dnl This file is free software; the authors give unlimited permission to copy
dnl and/or distribute it, with or without modifications, as long as this
dnl notice is preserved.
dnl
dnl SPDX-License-Identifier: FSFULLR
dnl Add the library flags to the default compiler flags and then remove them.
dnl
dnl To use these macros, pass the prefix string used for the variables as the
dnl only argument. For example, to use these for a library with KRB5 as a
dnl prefix, one would use:
dnl
dnl AC_DEFUN([RRA_LIB_KRB5_SWITCH], [RRA_LIB_HELPER_SWITCH([KRB5])])
dnl AC_DEFUN([RRA_LIB_KRB5_RESTORE], [RRA_LIB_HELPER_RESTORE([KRB5])])
dnl
dnl Then, wrap checks for library features with RRA_LIB_KRB5_SWITCH and
dnl RRA_LIB_KRB5_RESTORE.
AC_DEFUN([RRA_LIB_HELPER_SWITCH],
[rra_$1[]_save_CPPFLAGS="$CPPFLAGS"
rra_$1[]_save_LDFLAGS="$LDFLAGS"
rra_$1[]_save_LIBS="$LIBS"
CPPFLAGS="$$1[]_CPPFLAGS $CPPFLAGS"
LDFLAGS="$$1[]_LDFLAGS $LDFLAGS"
LIBS="$$1[]_LIBS $LIBS"])
AC_DEFUN([RRA_LIB_HELPER_RESTORE],
[CPPFLAGS="$rra_$1[]_save_CPPFLAGS"
LDFLAGS="$rra_$1[]_save_LDFLAGS"
LIBS="$rra_$1[]_save_LIBS"])
dnl Given _root, _libdir, and _includedir variables set for a library (set by
dnl RRA_LIB_HELPER_WITH*), set the LDFLAGS and CPPFLAGS variables for that
dnl library accordingly. Takes the variable prefix as the only argument.
AC_DEFUN([RRA_LIB_HELPER_PATHS],
[AS_IF([test x"$rra_$1[]_libdir" != x],
[$1[]_LDFLAGS="-L$rra_$1[]_libdir"],
[AS_IF([test x"$rra_$1[]_root" != x],
[RRA_SET_LDFLAGS([$1][_LDFLAGS], [${rra_$1[]_root}])])])
AS_IF([test x"$rra_$1[]_includedir" != x],
[$1[]_CPPFLAGS="-I$rra_$1[]_includedir"],
[AS_IF([test x"$rra_$1[]_root" != x],
[AS_IF([test x"$rra_$1[]_root" != x/usr],
[$1[]_CPPFLAGS="-I${rra_$1[]_root}/include"])])])])
dnl Check whether a library works. This is used as a sanity check on the
dnl results of *-config shell scripts. Takes four arguments; the first, if
dnl "true", says that a working library is mandatory and errors out if it
dnl doesn't. The second is the variable prefix. The third is a function to
dnl look for that should be in the libraries. The fourth is the
dnl human-readable name of the library for error messages.
AC_DEFUN([RRA_LIB_HELPER_CHECK],
[RRA_LIB_HELPER_SWITCH([$2])
AC_CHECK_FUNC([$3], [],
[AS_IF([test x"$1" = xtrue],
[AC_MSG_FAILURE([unable to link with $4 library])])
$2[]_CPPFLAGS=
$2[]_LDFLAGS=
$2[]_LIBS=])
RRA_LIB_HELPER_RESTORE([$2])])
dnl Initialize the variables used by a library probe and set the appropriate
dnl ones as substitution variables. Takes the library variable prefix as its
dnl only argument.
AC_DEFUN([RRA_LIB_HELPER_VAR_INIT],
[rra_$1[]_root=
rra_$1[]_libdir=
rra_$1[]_includedir=
rra_use_$1=
$1[]_CPPFLAGS=
$1[]_LDFLAGS=
$1[]_LIBS=
AC_SUBST([$1][_CPPFLAGS])
AC_SUBST([$1][_LDFLAGS])
AC_SUBST([$1][_LIBS])])
dnl Unset all of the variables used by a library probe. Used with the
dnl _OPTIONAL versions of header probes when a header or library wasn't found
dnl and therefore the library isn't usable.
AC_DEFUN([RRA_LIB_HELPER_VAR_CLEAR],
[$1[]_CPPFLAGS=
$1[]_LDFLAGS=
$1[]_LIBS=])
dnl Handles --with options for a non-optional library. First argument is the
dnl base for the switch names. Second argument is the short description.
dnl Third argument is the variable prefix. The variables set are used by
dnl RRA_LIB_HELPER_PATHS.
AC_DEFUN([RRA_LIB_HELPER_WITH],
[AC_ARG_WITH([$1],
[AS_HELP_STRING([--with-][$1][=DIR],
[Location of $2 headers and libraries])],
[AS_IF([test x"$withval" != xyes && test x"$withval" != xno],
[rra_$3[]_root="$withval"])])
AC_ARG_WITH([$1][-include],
[AS_HELP_STRING([--with-][$1][-include=DIR],
[Location of $2 headers])],
[AS_IF([test x"$withval" != xyes && test x"$withval" != xno],
[rra_$3[]_includedir="$withval"])])
AC_ARG_WITH([$1][-lib],
[AS_HELP_STRING([--with-][$1][-lib=DIR],
[Location of $2 libraries])],
[AS_IF([test x"$withval" != xyes && test x"$withval" != xno],
[rra_$3[]_libdir="$withval"])])])
dnl Handles --with options for an optional library, so --with-<library> can
dnl cause the checks to be skipped entirely or become mandatory. Sets an
dnl rra_use_PREFIX variable to true or false if the library is explicitly
dnl enabled or disabled.
dnl
dnl First argument is the base for the switch names. Second argument is the
dnl short description. Third argument is the variable prefix.
dnl
dnl The variables set are used by RRA_LIB_HELPER_PATHS.
AC_DEFUN([RRA_LIB_HELPER_WITH_OPTIONAL],
[AC_ARG_WITH([$1],
[AS_HELP_STRING([--with-][$1][@<:@=DIR@:>@],
[Location of $2 headers and libraries])],
[AS_IF([test x"$withval" = xno],
[rra_use_$3=false],
[AS_IF([test x"$withval" != xyes], [rra_$3[]_root="$withval"])
rra_use_$3=true])])
AC_ARG_WITH([$1][-include],
[AS_HELP_STRING([--with-][$1][-include=DIR],
[Location of $2 headers])],
[AS_IF([test x"$withval" != xyes && test x"$withval" != xno],
[rra_$3[]_includedir="$withval"])])
AC_ARG_WITH([$1][-lib],
[AS_HELP_STRING([--with-][$1][-lib=DIR],
[Location of $2 libraries])],
[AS_IF([test x"$withval" != xyes && test x"$withval" != xno],
[rra_$3[]_libdir="$withval"])])])
+54
View File
@@ -0,0 +1,54 @@
dnl Determine the library path name.
dnl
dnl Red Hat systems and some other Linux systems use lib64 and lib32 rather
dnl than just lib in some circumstances. This file provides an Autoconf
dnl macro, RRA_SET_LDFLAGS, which given a variable, a prefix, and an optional
dnl suffix, adds -Lprefix/lib, -Lprefix/lib32, or -Lprefix/lib64 to the
dnl variable depending on which directories exist and the size of a long in
dnl the compilation environment. If a suffix is given, a slash and that
dnl suffix will be appended, to allow for adding a subdirectory of the library
dnl directory.
dnl
dnl The canonical version of this file is maintained in the rra-c-util
dnl package, available at <https://www.eyrie.org/~eagle/software/rra-c-util/>.
dnl
dnl Written by Russ Allbery <eagle@eyrie.org>
dnl Copyright 2021 Russ Allbery <eagle@eyrie.org>
dnl Copyright 2008-2009
dnl The Board of Trustees of the Leland Stanford Junior University
dnl
dnl This file is free software; the authors give unlimited permission to copy
dnl and/or distribute it, with or without modifications, as long as this
dnl notice is preserved.
dnl
dnl SPDX-License-Identifier: FSFULLR
dnl Probe for the alternate library name that we should attempt on this
dnl architecture, given the size of an int, and set rra_lib_arch_name to that
dnl name. Separated out so that it can be AC_REQUIRE'd and not run multiple
dnl times.
dnl
dnl There is an unfortunate abstraction violation here where we assume we know
dnl the cache variable name used by Autoconf. Unfortunately, Autoconf doesn't
dnl provide any other way of getting at that information in shell that I can
dnl see.
AC_DEFUN([_RRA_LIB_ARCH_NAME],
[rra_lib_arch_name=lib
AC_CHECK_SIZEOF([long])
AS_IF([test "$ac_cv_sizeof_long" -eq 4],
[rra_lib_arch_name=lib32],
[AS_IF([test "$ac_cv_sizeof_long" -eq 8],
[rra_lib_arch_name=lib64])])])
dnl Set VARIABLE to -LPREFIX/lib{,32,64} or -LPREFIX/lib{,32,64}/SUFFIX as
dnl appropriate.
AC_DEFUN([RRA_SET_LDFLAGS],
[AC_REQUIRE([_RRA_LIB_ARCH_NAME])
AS_IF([test -d "$2/$rra_lib_arch_name"],
[AS_IF([test x"$3" = x],
[$1="[$]$1 -L$2/${rra_lib_arch_name}"],
[$1="[$]$1 -L$2/${rra_lib_arch_name}/$3"])],
[AS_IF([test x"$3" = x],
[$1="[$]$1 -L$2/lib"],
[$1="[$]$1 -L$2/lib/$3"])])
$1=`AS_ECHO(["[$]$1"]) | sed -e 's/^ *//'`])
+53
View File
@@ -0,0 +1,53 @@
dnl Determine whether PAM uses const in prototypes.
dnl
dnl Linux marks several PAM arguments const, including the argument to
dnl pam_get_item and some arguments to conversation functions, which Solaris
dnl doesn't. Mac OS X, OS X, and macOS mark the first argument to
dnl pam_strerror const, and other platforms don't. This test tries to
dnl determine which style is in use to select whether to declare variables
dnl const and how to prototype functions in order to avoid compiler warnings.
dnl
dnl Since this is just for compiler warnings, it's not horribly important if
dnl we guess wrong. This test is ugly, but it seems to work.
dnl
dnl The canonical version of this file is maintained in the rra-c-util
dnl package, available at <https://www.eyrie.org/~eagle/software/rra-c-util/>.
dnl
dnl Written by Markus Moeller
dnl Copyright 2007, 2015 Russ Allbery <eagle@eyrie.org>
dnl Copyright 2007-2008 Markus Moeller
dnl
dnl This file is free software; the authors give unlimited permission to copy
dnl and/or distribute it, with or without modifications, as long as this
dnl notice is preserved.
dnl
dnl SPDX-License-Identifier: FSFULLR
dnl Source used by RRA_HEADER_PAM_CONST.
AC_DEFUN([_RRA_HEADER_PAM_CONST_SOURCE],
[#ifdef HAVE_SECURITY_PAM_APPL_H
# include <security/pam_appl.h>
#else
# include <pam/pam_appl.h>
#endif
])
AC_DEFUN([RRA_HEADER_PAM_CONST],
[AC_CACHE_CHECK([whether PAM prefers const], [rra_cv_header_pam_const],
[AC_EGREP_CPP([const void \*\* *_?item], _RRA_HEADER_PAM_CONST_SOURCE(),
[rra_cv_header_pam_const=yes], [rra_cv_header_pam_const=no])])
AS_IF([test x"$rra_cv_header_pam_const" = xyes],
[rra_header_pam_const=const], [rra_header_pam_const=])
AC_DEFINE_UNQUOTED([PAM_CONST], [$rra_header_pam_const],
[Define to const if PAM uses const in pam_get_item, empty otherwise.])])
AC_DEFUN([RRA_HEADER_PAM_STRERROR_CONST],
[AC_CACHE_CHECK([whether pam_strerror uses const],
[rra_cv_header_pam_strerror_const],
[AC_EGREP_CPP([pam_strerror *\(const], _RRA_HEADER_PAM_CONST_SOURCE(),
[rra_cv_header_pam_strerror_const=yes],
[rra_cv_header_pam_strerror_const=no])])
AS_IF([test x"$rra_cv_header_pam_strerror_const" = xyes],
[rra_header_pam_strerror_const=const], [rra_header_pam_strerror_const=])
AC_DEFINE_UNQUOTED([PAM_STRERROR_CONST], [$rra_header_pam_strerror_const],
[Define to const if PAM uses const in pam_strerror, empty otherwise.])])
+92
View File
@@ -0,0 +1,92 @@
/*
* Implements the PAM authorization function (pam_acct_mgmt).
*
* We don't have much to do for account management, but we do recheck the
* user's authorization against .k5login (or whatever equivalent we've been
* configured for).
*
* Copyright 2005-2009, 2014, 2020-2021 Russ Allbery <eagle@eyrie.org>
* Copyright 2011
* The Board of Trustees of the Leland Stanford Junior University
* Copyright 2005 Andres Salomon <dilinger@debian.org>
* Copyright 1999-2000 Frank Cusack <fcusack@fcusack.com>
*
* SPDX-License-Identifier: BSD-3-clause or GPL-1+
*/
/* Get prototypes for the account management functions. */
#define PAM_SM_ACCOUNT
#include <config.h>
#include <portable/krb5.h>
#include <portable/pam.h>
#include <portable/system.h>
#include <errno.h>
#include <module/internal.h>
#include <pam-util/args.h>
#include <pam-util/logging.h>
/*
* Check the authorization of the user. It's not entirely clear what this
* function is supposed to do, but rechecking .k5login and friends makes the
* most sense.
*/
int
pamk5_account(struct pam_args *args)
{
struct context *ctx;
int retval;
const char *name;
/* If the account was expired, here's where we actually fail. */
ctx = args->config->ctx;
if (ctx->expired) {
pam_syslog(args->pamh, LOG_INFO, "user %s account password is expired",
ctx->name);
return PAM_NEW_AUTHTOK_REQD;
}
/*
* Re-retrieve the user rather than trusting our context; it's conceivable
* the application could have changed it. We have to cast &name due to
* C's broken type system.
*
* Use pam_get_item rather than pam_get_user here since the user should be
* set by the time we get to this point. If we would have to prompt for a
* user, something is definitely broken and we should fail.
*/
retval = pam_get_item(args->pamh, PAM_USER, (PAM_CONST void **) &name);
if (retval != PAM_SUCCESS || name == NULL) {
putil_err_pam(args, retval, "unable to retrieve user");
return PAM_AUTH_ERR;
}
if (ctx->name != name) {
free(ctx->name);
ctx->name = strdup(name);
args->user = ctx->name;
}
/*
* If we have a ticket cache, then we can apply an additional bit of
* paranoia. Rather than trusting princ in the context, extract the
* principal from the Kerberos ticket cache we actually received and then
* validate that. This should make no difference in practice, but it's a
* bit more thorough.
*/
if (ctx->cache != NULL) {
putil_debug(args, "retrieving principal from cache");
if (ctx->princ != NULL) {
krb5_free_principal(ctx->context, ctx->princ);
ctx->princ = NULL;
}
retval = krb5_cc_get_principal(ctx->context, ctx->cache, &ctx->princ);
if (retval != 0) {
putil_err_krb5(args, retval, "cannot get principal from cache");
return PAM_AUTH_ERR;
}
}
return pamk5_authorized(args);
}
+240
View File
@@ -0,0 +1,240 @@
/*
* Support for alternate authentication mapping.
*
* pam-krb5 supports a feature where the principal for authentication can be
* set via a PAM option and possibly based on the authenticating user. This
* can be used to, for example, require /root instances be used with sudo
* while still using normal instances for other system authentications.
*
* This file collects all the pieces related to that support.
*
* Original support written by Booker Bense <bbense@slac.stanford.edu>
* Further updates by Russ Allbery <eagle@eyrie.org>
* Copyright 2020 Russ Allbery <eagle@eyrie.org>
* Copyright 2008-2012
* The Board of Trustees of the Leland Stanford Junior University
*
* SPDX-License-Identifier: BSD-3-clause or GPL-1+
*/
#include <config.h>
#include <portable/krb5.h>
#include <portable/pam.h>
#include <portable/system.h>
#include <errno.h>
#include <module/internal.h>
#include <pam-util/args.h>
#include <pam-util/logging.h>
/*
* Map the user to a Kerberos principal according to alt_auth_map. Returns 0
* on success, storing the mapped principal name in newly allocated memory in
* principal. The caller is responsible for freeing. Returns an errno value
* on any error.
*/
int
pamk5_map_principal(struct pam_args *args, const char *username,
char **principal)
{
char *realm;
char *new_user = NULL;
const char *user;
const char *p;
size_t needed, offset;
int oerrno;
/* Makes no sense if alt_auth_map isn't set. */
if (args->config->alt_auth_map == NULL)
return EINVAL;
/* Need to split off the realm if it is present. */
realm = strchr(username, '@');
if (realm == NULL)
user = username;
else {
new_user = strdup(username);
if (new_user == NULL)
return errno;
realm = strchr(new_user, '@');
if (realm == NULL)
goto fail;
*realm = '\0';
realm++;
user = new_user;
}
/* Now, allocate a string and build the principal. */
needed = 0;
for (p = args->config->alt_auth_map; *p != '\0'; p++) {
if (p[0] == '%' && p[1] == 's') {
needed += strlen(user);
p++;
} else {
needed++;
}
}
if (realm != NULL && strchr(args->config->alt_auth_map, '@') == NULL)
needed += 1 + strlen(realm);
needed++;
*principal = malloc(needed);
if (*principal == NULL)
goto fail;
offset = 0;
for (p = args->config->alt_auth_map; *p != '\0'; p++) {
if (p[0] == '%' && p[1] == 's') {
memcpy(*principal + offset, user, strlen(user));
offset += strlen(user);
p++;
} else {
(*principal)[offset] = *p;
offset++;
}
}
if (realm != NULL && strchr(args->config->alt_auth_map, '@') == NULL) {
(*principal)[offset] = '@';
offset++;
memcpy(*principal + offset, realm, strlen(realm));
offset += strlen(realm);
}
(*principal)[offset] = '\0';
free(new_user);
return 0;
fail:
if (new_user != NULL) {
oerrno = errno;
free(new_user);
errno = oerrno;
}
return errno;
}
/*
* Authenticate using an alternate principal mapping.
*
* Create a principal based on the principal mapping and the user, and use the
* provided password to try to authenticate as that user. If we succeed, fill
* out creds, set princ to the successful principal in the context, and return
* 0. Otherwise, return a Kerberos error code or an errno value.
*/
krb5_error_code
pamk5_alt_auth(struct pam_args *args, const char *service,
krb5_get_init_creds_opt *opts, const char *pass,
krb5_creds *creds)
{
struct context *ctx = args->config->ctx;
char *kuser;
krb5_principal princ;
krb5_error_code retval;
retval = pamk5_map_principal(args, ctx->name, &kuser);
if (retval != 0)
return retval;
retval = krb5_parse_name(ctx->context, kuser, &princ);
if (retval != 0) {
free(kuser);
return retval;
}
free(kuser);
/* Log the principal we're attempting to authenticate as. */
if (args->debug) {
char *principal;
retval = krb5_unparse_name(ctx->context, princ, &principal);
if (retval != 0)
putil_debug_krb5(args, retval, "krb5_unparse_name failed");
else {
putil_debug(args, "mapping %s to %s", ctx->name, principal);
krb5_free_unparsed_name(ctx->context, principal);
}
}
/*
* Now, attempt to authenticate as that user. On success, save the
* principal. Return the Kerberos status code.
*/
retval = krb5_get_init_creds_password(ctx->context, creds, princ,
(char *) pass, pamk5_prompter_krb5,
args, 0, (char *) service, opts);
if (retval != 0) {
putil_debug_krb5(args, retval, "alternate authentication failed");
krb5_free_principal(ctx->context, princ);
return retval;
} else {
putil_debug(args, "alternate authentication successful");
if (ctx->princ != NULL)
krb5_free_principal(ctx->context, ctx->princ);
ctx->princ = princ;
return 0;
}
}
/*
* Verify an alternate authentication.
*
* Meant to be called from pamk5_authorized, this checks that the principal in
* the context matches the alt_auth_map-derived identity of the user we're
* authenticating. Returns PAM_SUCCESS if they match, PAM_AUTH_ERR if they
* don't match, and PAM_SERVICE_ERR on an internal error.
*/
int
pamk5_alt_auth_verify(struct pam_args *args)
{
struct context *ctx;
char *name = NULL;
char *mapped = NULL;
char *authed = NULL;
krb5_principal princ = NULL;
krb5_error_code retval;
int status = PAM_SERVICE_ERR;
if (args == NULL || args->config == NULL || args->config->ctx == NULL)
return PAM_SERVICE_ERR;
ctx = args->config->ctx;
if (ctx->context == NULL || ctx->name == NULL)
return PAM_SERVICE_ERR;
if (pamk5_map_principal(args, ctx->name, &name) != 0) {
putil_err(args, "cannot map principal name");
goto done;
}
retval = krb5_parse_name(ctx->context, name, &princ);
if (retval != 0) {
putil_err_krb5(args, retval, "cannot parse mapped principal name %s",
mapped);
goto done;
}
retval = krb5_unparse_name(ctx->context, princ, &mapped);
if (retval != 0) {
putil_err_krb5(args, retval,
"krb5_unparse_name on mapped principal failed");
goto done;
}
retval = krb5_unparse_name(ctx->context, ctx->princ, &authed);
if (retval != 0) {
putil_err_krb5(args, retval, "krb5_unparse_name failed");
goto done;
}
if (strcmp(authed, mapped) == 0)
status = PAM_SUCCESS;
else {
putil_debug(args, "mapped user %s does not match principal %s", mapped,
authed);
status = PAM_AUTH_ERR;
}
done:
free(name);
if (authed != NULL)
krb5_free_unparsed_name(ctx->context, authed);
if (mapped != NULL)
krb5_free_unparsed_name(ctx->context, mapped);
if (princ != NULL)
krb5_free_principal(ctx->context, princ);
return status;
}
+1135
View File
File diff suppressed because it is too large Load Diff
+185
View File
@@ -0,0 +1,185 @@
/*
* Ticket cache initialization.
*
* Provides functions for creating ticket caches, used by pam_authenticate,
* pam_setcred, and pam_chauthtok after changing an expired password.
*
* Copyright 2005-2009, 2014, 2020 Russ Allbery <eagle@eyrie.org>
* Copyright 2011-2012
* The Board of Trustees of the Leland Stanford Junior University
* Copyright 2005 Andres Salomon <dilinger@debian.org>
* Copyright 1999-2000 Frank Cusack <fcusack@fcusack.com>
*
* SPDX-License-Identifier: BSD-3-clause or GPL-1+
*/
#include <config.h>
#include <portable/krb5.h>
#include <portable/pam.h>
#include <portable/system.h>
#include <errno.h>
#include <module/internal.h>
#include <pam-util/args.h>
#include <pam-util/logging.h>
/*
* Get the name of a cache. Takes the name of the environment variable that
* should be set to indicate which cache to use, either the permanent cache
* (KRB5CCNAME) or the temporary cache (PAM_KRB5CCNAME).
*
* Treat an empty environment variable setting the same as if the variable
* was not set, since on FreeBSD we can't delete the environment variable,
* only set it to an empty value.
*/
const char *
pamk5_get_krb5ccname(struct pam_args *args, const char *key)
{
const char *name;
/* When refreshing a cache, we need to try the regular environment. */
name = pam_getenv(args->pamh, key);
if (name == NULL || *name == '\0')
name = getenv(key);
if (name == NULL || *name == '\0')
return NULL;
else
return name;
}
/*
* Put the ticket cache information into the environment. Takes the path and
* the environment variable to set, since this is used both for the permanent
* cache (KRB5CCNAME) and the temporary cache (PAM_KRB5CCNAME). Returns a PAM
* status code.
*/
int
pamk5_set_krb5ccname(struct pam_args *args, const char *name, const char *key)
{
char *env_name = NULL;
int pamret;
if (asprintf(&env_name, "%s=%s", key, name) < 0) {
putil_crit(args, "asprintf failed: %s", strerror(errno));
pamret = PAM_BUF_ERR;
goto done;
}
pamret = pam_putenv(args->pamh, env_name);
if (pamret != PAM_SUCCESS) {
putil_err_pam(args, pamret, "pam_putenv failed");
pamret = PAM_SERVICE_ERR;
goto done;
}
pamret = PAM_SUCCESS;
done:
free(env_name);
return pamret;
}
/*
* Given the template for a ticket cache name, initialize that file securely
* mkstemp. Returns a PAM success or error code.
*/
int
pamk5_cache_mkstemp(struct pam_args *args, char *template)
{
int ccfd, oerrno;
ccfd = mkstemp(template);
if (ccfd < 0) {
oerrno = errno;
putil_crit(args, "mkstemp(\"%s\") failed: %s", template,
strerror(errno));
errno = oerrno;
return PAM_SERVICE_ERR;
}
close(ccfd);
return PAM_SUCCESS;
}
/*
* Given a cache name and the initial credentials, initialize the cache, store
* the credentials in that cache, and return a pointer to the new cache in the
* cache argument. Returns a PAM success or error code.
*/
int
pamk5_cache_init(struct pam_args *args, const char *ccname, krb5_creds *creds,
krb5_ccache *cache)
{
struct context *ctx;
int retval;
if (args == NULL || args->config == NULL || args->config->ctx == NULL
|| args->config->ctx->context == NULL)
return PAM_SERVICE_ERR;
ctx = args->config->ctx;
retval = krb5_cc_resolve(ctx->context, ccname, cache);
if (retval != 0) {
putil_err_krb5(args, retval, "cannot resolve ticket cache %s", ccname);
retval = PAM_SERVICE_ERR;
goto done;
}
retval = krb5_cc_initialize(ctx->context, *cache, ctx->princ);
if (retval != 0) {
putil_err_krb5(args, retval, "cannot initialize ticket cache %s",
ccname);
retval = PAM_SERVICE_ERR;
goto done;
}
retval = krb5_cc_store_cred(ctx->context, *cache, creds);
if (retval != 0) {
putil_err_krb5(args, retval, "cannot store credentials in %s", ccname);
retval = PAM_SERVICE_ERR;
goto done;
}
done:
if (retval != PAM_SUCCESS && *cache != NULL) {
krb5_cc_destroy(ctx->context, *cache);
*cache = NULL;
}
return retval;
}
/*
* Initialize an internal ticket cache with a random name, store the given
* credentials in the cache, and store the cache in the context. Put the path
* in PAM_KRB5CCNAME where it can be picked up later by pam_setcred. Returns
* a PAM success or error code.
*/
int
pamk5_cache_init_random(struct pam_args *args, krb5_creds *creds)
{
char *cache_name = NULL;
const char *dir;
int pamret;
/* Store the obtained credentials in a temporary cache. */
dir = args->config->ccache_dir;
if (strncmp("FILE:", args->config->ccache_dir, strlen("FILE:")) == 0)
dir += strlen("FILE:");
if (asprintf(&cache_name, "%s/krb5cc_pam_XXXXXX", dir) < 0) {
putil_crit(args, "malloc failure: %s", strerror(errno));
return PAM_SERVICE_ERR;
}
pamret = pamk5_cache_mkstemp(args, cache_name);
if (pamret != PAM_SUCCESS)
goto done;
pamret =
pamk5_cache_init(args, cache_name, creds, &args->config->ctx->cache);
if (pamret != PAM_SUCCESS)
goto done;
putil_debug(args, "temporarily storing credentials in %s", cache_name);
pamret = pamk5_set_krb5ccname(args, cache_name, "PAM_KRB5CCNAME");
done:
free(cache_name);
return pamret;
}
+177
View File
@@ -0,0 +1,177 @@
/*
* Manage context structure.
*
* The context structure is the internal state maintained by the pam-krb5
* module between calls to the various public interfaces.
*
* Copyright 2005-2009, 2014, 2020-2021 Russ Allbery <eagle@eyrie.org>
* Copyright 2011
* The Board of Trustees of the Leland Stanford Junior University
* Copyright 2005 Andres Salomon <dilinger@debian.org>
* Copyright 1999-2000 Frank Cusack <fcusack@fcusack.com>
*
* SPDX-License-Identifier: BSD-3-clause or GPL-1+
*/
#include <config.h>
#include <portable/pam.h>
#include <portable/system.h>
#include <errno.h>
#include <module/internal.h>
#include <pam-util/args.h>
#include <pam-util/logging.h>
/*
* Create a new context and populate it with the user from PAM and the current
* Kerberos context. Set the default realm if one was configured.
*/
int
pamk5_context_new(struct pam_args *args)
{
struct context *ctx;
int retval;
PAM_CONST char *name;
ctx = calloc(1, sizeof(struct context));
if (ctx == NULL) {
retval = PAM_BUF_ERR;
goto done;
}
ctx->cache = NULL;
ctx->princ = NULL;
ctx->creds = NULL;
ctx->fast_cache = NULL;
ctx->context = args->ctx;
args->config->ctx = ctx;
/*
* This will prompt for the username if it's not already set (generally it
* will be). Otherwise, grab the saved username.
*/
retval = pam_get_user(args->pamh, &name, NULL);
if (retval != PAM_SUCCESS || name == NULL) {
if (retval == PAM_CONV_AGAIN)
retval = PAM_INCOMPLETE;
else
retval = PAM_SERVICE_ERR;
goto done;
}
ctx->name = strdup(name);
args->user = ctx->name;
/* Set a default realm if one was configured. */
if (args->realm != NULL) {
retval = krb5_set_default_realm(ctx->context, args->realm);
if (retval != 0) {
putil_err_krb5(args, retval, "cannot set default realm");
retval = PAM_SERVICE_ERR;
goto done;
}
}
done:
if (ctx != NULL && retval != PAM_SUCCESS)
pamk5_context_free(args);
return retval;
}
/*
* Retrieve a context from the PAM data structures, returning failure if no
* context was present. Note that OpenSSH loses contexts between authenticate
* and setcred, so failure shouldn't always be fatal.
*/
int
pamk5_context_fetch(struct pam_args *args)
{
int pamret;
pamret = pam_get_data(args->pamh, "pam_krb5", (void *) &args->config->ctx);
if (pamret != PAM_SUCCESS)
args->config->ctx = NULL;
if (pamret == PAM_SUCCESS && args->config->ctx == NULL)
return PAM_SERVICE_ERR;
if (args->config->ctx != NULL)
args->user = args->config->ctx->name;
return pamret;
}
/*
* Free a context and all of the data that's stored in it. Normally this also
* includes destroying the ticket cache, but don't do this (just close it) if
* a flag was set to preserve it.
*
* This function is common code between pamk5_context_free (called internally
* by our code) and pamk5_context_destroy (called by PAM as a data callback).
*/
static void
context_free(struct context *ctx, bool free_context)
{
if (ctx == NULL)
return;
free(ctx->name);
if (ctx->context != NULL) {
if (ctx->princ != NULL)
krb5_free_principal(ctx->context, ctx->princ);
if (ctx->cache != NULL) {
if (ctx->dont_destroy_cache)
krb5_cc_close(ctx->context, ctx->cache);
else
krb5_cc_destroy(ctx->context, ctx->cache);
}
if (ctx->creds != NULL) {
krb5_free_cred_contents(ctx->context, ctx->creds);
free(ctx->creds);
}
if (free_context)
krb5_free_context(ctx->context);
}
if (ctx->fast_cache != NULL)
krb5_cc_destroy(ctx->context, ctx->fast_cache);
free(ctx);
}
/*
* Free the current context, used internally by pam-krb5 code. This is a
* wrapper around context_free that makes sure we don't destroy the Kerberos
* context if it's the same as the top-level context and handles other
* bookkeeping in the top-level pam_args struct.
*/
void
pamk5_context_free(struct pam_args *args)
{
if (args->config->ctx == NULL)
return;
if (args->user == args->config->ctx->name)
args->user = NULL;
context_free(args->config->ctx, args->ctx != args->config->ctx->context);
args->config->ctx = NULL;
}
/*
* The PAM callback to destroy the context stored in the PAM data structures.
*/
void
pamk5_context_destroy(pam_handle_t *pamh UNUSED, void *data,
int pam_end_status)
{
struct context *ctx = (struct context *) data;
/*
* Do not destroy the cache if the status contains PAM_DATA_SILENT, since
* in that case we may be in a child and the parent will still rely on
* underlying resources such as the ticket cache to exist.
*/
if (PAM_DATA_SILENT != 0 && (pam_end_status & PAM_DATA_SILENT))
ctx->dont_destroy_cache = true;
/* The rest of the work is in context_free. */
if (ctx != NULL)
context_free(ctx, true);
}
+288
View File
@@ -0,0 +1,288 @@
/*
* Support for FAST (Flexible Authentication Secure Tunneling).
*
* FAST is a mechanism to protect Kerberos against password guessing attacks
* and provide other security improvements. It requires existing credentials
* to protect the initial preauthentication exchange. These can come either
* from a ticket cache for another principal or via anonymous PKINIT.
*
* Written by Russ Allbery <eagle@eyrie.org>
* Contributions from Sam Hartman and Yair Yarom
* Copyright 2017, 2020 Russ Allbery <eagle@eyrie.org>
* Copyright 2010, 2012
* The Board of Trustees of the Leland Stanford Junior University
*
* SPDX-License-Identifier: BSD-3-clause or GPL-1+
*/
#include <config.h>
#include <portable/krb5.h>
#include <portable/system.h>
#include <errno.h>
#include <module/internal.h>
#include <pam-util/args.h>
#include <pam-util/logging.h>
/*
* Initialize an internal anonymous ticket cache with a random name and store
* the resulting ticket cache in the ccache argument. Returns a Kerberos
* error code.
*/
#ifndef HAVE_KRB5_GET_INIT_CREDS_OPT_SET_ANONYMOUS
static krb5_error_code
cache_init_anonymous(struct pam_args *args, krb5_ccache *ccache UNUSED)
{
putil_debug(args, "not built with anonymous FAST support");
return KRB5KDC_ERR_BADOPTION;
}
#else /* HAVE_KRB5_GET_INIT_CREDS_OPT_SET_ANONYMOUS */
static krb5_error_code
cache_init_anonymous(struct pam_args *args, krb5_ccache *ccache)
{
krb5_context c = args->config->ctx->context;
krb5_error_code retval;
krb5_principal princ = NULL;
char *realm;
char *name = NULL;
krb5_creds creds;
bool creds_valid = false;
krb5_get_init_creds_opt *opts = NULL;
*ccache = NULL;
memset(&creds, 0, sizeof(creds));
/* Construct the anonymous principal name. */
retval = krb5_get_default_realm(c, &realm);
if (retval != 0) {
putil_debug_krb5(args, retval, "cannot find realm for anonymous FAST");
return retval;
}
retval = krb5_build_principal_ext(
c, &princ, (unsigned int) strlen(realm), realm,
strlen(KRB5_WELLKNOWN_NAME), KRB5_WELLKNOWN_NAME,
strlen(KRB5_ANON_NAME), KRB5_ANON_NAME, NULL);
if (retval != 0) {
krb5_free_default_realm(c, realm);
putil_debug_krb5(args, retval, "cannot create anonymous principal");
return retval;
}
krb5_free_default_realm(c, realm);
/*
* Set up the credential cache the anonymous credentials. We use a
* memory cache whose name is based on the pointer value of our Kerberos
* context, since that should be unique among threads.
*/
if (asprintf(&name, "MEMORY:%p", (void *) c) < 0) {
putil_crit(args, "malloc failure: %s", strerror(errno));
retval = errno;
goto done;
}
retval = krb5_cc_resolve(c, name, ccache);
if (retval != 0) {
putil_err_krb5(args, retval,
"cannot create anonymous FAST credential cache %s",
name);
goto done;
}
/* Obtain the credentials. */
retval = krb5_get_init_creds_opt_alloc(c, &opts);
if (retval != 0) {
putil_err_krb5(args, retval, "cannot create FAST credential options");
goto done;
}
krb5_get_init_creds_opt_set_anonymous(opts, 1);
krb5_get_init_creds_opt_set_tkt_life(opts, 60);
# ifdef HAVE_KRB5_GET_INIT_CREDS_OPT_SET_OUT_CCACHE
krb5_get_init_creds_opt_set_out_ccache(c, opts, *ccache);
# endif
retval = krb5_get_init_creds_password(c, &creds, princ, NULL, NULL, NULL,
0, NULL, opts);
if (retval != 0) {
putil_debug_krb5(args, retval,
"cannot obtain anonymous credentials for FAST");
goto done;
}
creds_valid = true;
/*
* If set_out_ccache was available, we're done. Otherwise, we have to
* manually set up the ticket cache. Use the principal from the acquired
* credentials when initializing the ticket cache, since the realm will
* not match the realm of our input principal.
*/
# ifndef HAVE_KRB5_GET_INIT_CREDS_OPT_SET_OUT_CCACHE
retval = krb5_cc_initialize(c, *ccache, creds.client);
if (retval != 0) {
putil_err_krb5(args, retval, "cannot initialize FAST ticket cache");
goto done;
}
retval = krb5_cc_store_cred(c, *ccache, &creds);
if (retval != 0) {
putil_err_krb5(args, retval, "cannot store FAST credentials");
goto done;
}
# endif /* !HAVE_KRB5_GET_INIT_CREDS_OPT_SET_OUT_CCACHE */
done:
if (retval != 0 && *ccache != NULL) {
krb5_cc_destroy(c, *ccache);
*ccache = NULL;
}
if (princ != NULL)
krb5_free_principal(c, princ);
free(name);
if (opts != NULL)
krb5_get_init_creds_opt_free(c, opts);
if (creds_valid)
krb5_free_cred_contents(c, &creds);
return retval;
}
#endif /* HAVE_KRB5_GET_INIT_CREDS_OPT_SET_ANONYMOUS */
/*
* Attempt to use an existing ticket cache for FAST. Checks whether
* fast_ccache is set in the options and, if so, opens that cache and does
* some sanity checks, returning the cache name to use if everything checks
* out in newly allocated memory. Caller is responsible for freeing. If not,
* returns NULL.
*/
UNUSED static char *
fast_setup_cache(struct pam_args *args)
{
krb5_context c = args->config->ctx->context;
krb5_error_code retval;
krb5_principal princ;
krb5_ccache ccache;
char *result;
const char *cache = args->config->fast_ccache;
if (cache == NULL)
return NULL;
retval = krb5_cc_resolve(c, cache, &ccache);
if (retval != 0) {
putil_debug_krb5(args, retval, "cannot open FAST ccache %s", cache);
return NULL;
}
retval = krb5_cc_get_principal(c, ccache, &princ);
if (retval != 0) {
putil_debug_krb5(args, retval,
"failed to get principal from FAST"
" ccache %s",
cache);
krb5_cc_close(c, ccache);
return NULL;
} else {
krb5_free_principal(c, princ);
krb5_cc_close(c, ccache);
result = strdup(cache);
if (result == NULL)
putil_crit(args, "strdup failure: %s", strerror(errno));
return result;
}
}
/*
* Attempt to use an anonymous ticket cache for FAST. Checks whether
* anon_fast is set in the options and, if so, opens that cache and does some
* sanity checks, returning the cache name to use if everything checks out in
* newly allocated memory. Caller is responsible for freeing. If not,
* returns NULL.
*
* If successful, store the anonymous FAST cache in the context where it will
* be freed following authentication.
*/
UNUSED static char *
fast_setup_anon(struct pam_args *args)
{
krb5_context c = args->config->ctx->context;
krb5_error_code retval;
krb5_ccache ccache;
char *cache, *result;
if (!args->config->anon_fast)
return NULL;
retval = cache_init_anonymous(args, &ccache);
if (retval != 0) {
putil_debug_krb5(args, retval, "skipping anonymous FAST");
return NULL;
}
retval = krb5_cc_get_full_name(c, ccache, &cache);
if (retval != 0) {
putil_debug_krb5(args, retval,
"cannot get name of anonymous FAST"
" credential cache");
krb5_cc_destroy(c, ccache);
return NULL;
}
result = strdup(cache);
if (result == NULL) {
putil_crit(args, "strdup failure: %s", strerror(errno));
krb5_cc_destroy(c, ccache);
}
krb5_free_string(c, cache);
putil_debug(args, "anonymous authentication for FAST succeeded");
if (args->config->ctx->fast_cache != NULL)
krb5_cc_destroy(c, args->config->ctx->fast_cache);
args->config->ctx->fast_cache = ccache;
return result;
}
/*
* Set initial credential options for FAST if support is available.
*
* If fast_ccache is set, we try to use that ticket cache first. Open it and
* read the principal from it first to ensure that the cache exists and
* contains credentials. If that fails, skip setting the FAST cache.
*
* If anon_fast is set and fast_ccache is not or is skipped for the reasons
* described above, try to obtain anonymous credentials and then use them as
* FAST armor.
*
* Note that this function cannot fail. If anything about FAST setup doesn't
* work, we continue without FAST.
*/
#ifndef HAVE_KRB5_GET_INIT_CREDS_OPT_SET_FAST_CCACHE_NAME
void
pamk5_fast_setup(struct pam_args *args UNUSED,
krb5_get_init_creds_opt *opts UNUSED)
{
}
#else /* HAVE_KRB5_GET_INIT_CREDS_OPT_SET_FAST_CCACHE_NAME */
void
pamk5_fast_setup(struct pam_args *args, krb5_get_init_creds_opt *opts)
{
krb5_context c = args->config->ctx->context;
krb5_error_code retval;
char *cache;
/* First try to use fast_ccache, and then fall back on anon_fast. */
cache = fast_setup_cache(args);
if (cache == NULL)
cache = fast_setup_anon(args);
if (cache == NULL)
return;
/* We have a valid FAST ticket cache. Set the option. */
retval = krb5_get_init_creds_opt_set_fast_ccache_name(c, opts, cache);
if (retval != 0)
putil_err_krb5(args, retval, "failed to set FAST ccache");
else
putil_debug(args, "setting FAST credential cache to %s", cache);
free(cache);
}
#endif /* HAVE_KRB5_GET_INIT_CREDS_OPT_SET_FAST_CCACHE_NAME */
+261
View File
@@ -0,0 +1,261 @@
/*
* Internal prototypes and structures for pam-krb5.
*
* Copyright 2005-2009, 2014, 2020 Russ Allbery <eagle@eyrie.org>
* Copyright 2011, 2012
* The Board of Trustees of the Leland Stanford Junior University
* Copyright 2005 Andres Salomon <dilinger@debian.org>
* Copyright 1999-2000 Frank Cusack <fcusack@fcusack.com>
*
* SPDX-License-Identifier: BSD-3-clause or GPL-1+
*/
#ifndef INTERNAL_H
#define INTERNAL_H 1
#include <config.h>
#include <portable/krb5.h>
#include <portable/macros.h>
#include <portable/pam.h>
#include <stdarg.h>
#include <syslog.h>
/* Forward declarations to avoid unnecessary includes. */
struct pam_args;
struct passwd;
struct vector;
/* Used for unused parameters to silence gcc warnings. */
#define UNUSED __attribute__((__unused__))
/*
* An authentication context, including all the data we want to preserve
* across calls to the public entry points. This context is stored in the PAM
* state and a pointer to it is stored in the pam_args struct that is passed
* as the first argument to most internal functions.
*/
struct context {
char *name; /* Username being authenticated. */
krb5_context context; /* Kerberos context. */
krb5_ccache cache; /* Active credential cache, if any. */
krb5_principal princ; /* Principal being authenticated. */
int expired; /* If set, account was expired. */
int dont_destroy_cache; /* If set, don't destroy cache on shutdown. */
int initialized; /* If set, ticket cache initialized. */
krb5_creds *creds; /* Credentials for password changing. */
krb5_ccache fast_cache; /* Temporary credential cache for FAST. */
};
/*
* The global structure holding our arguments, both from krb5.conf and from
* the PAM configuration. Filled in by pamk5_init and stored in the pam_args
* struct passed as a first argument to most internal functions. Sort by
* documentation order.
*/
struct pam_config {
/* Authorization. */
char *alt_auth_map; /* An sprintf pattern to map principals. */
bool force_alt_auth; /* Alt principal must be used if it exists. */
bool ignore_k5login; /* Don't check .k5login files. */
bool ignore_root; /* Skip authentication for root. */
long minimum_uid; /* Ignore users below this UID. */
bool only_alt_auth; /* Alt principal must be used. */
bool search_k5login; /* Try password with each line of .k5login. */
/* Kerberos behavior. */
char *fast_ccache; /* Cache containing armor ticket. */
bool anon_fast; /* sets up an anonymous fast armor cache */
bool forwardable; /* Obtain forwardable tickets. */
char *keytab; /* Keytab for credential validation. */
char *realm; /* Default realm for Kerberos. */
krb5_deltat renew_lifetime; /* Renewable lifetime of credentials. */
krb5_deltat ticket_lifetime; /* Lifetime of credentials. */
char *user_realm; /* Default realm for user principals. */
/* PAM behavior. */
bool clear_on_fail; /* Delete saved password on change failure. */
bool debug; /* Log debugging information. */
bool defer_pwchange; /* Defer expired account fail to account. */
bool fail_pwchange; /* Treat expired password as auth failure. */
bool force_pwchange; /* Change expired passwords in auth. */
bool no_update_user; /* Don't update PAM_USER with local name. */
bool silent; /* Suppress text and errors (PAM_SILENT). */
char *trace; /* File name for trace logging. */
/* PKINIT. */
char *pkinit_anchors; /* Trusted certificates, usually per realm. */
bool pkinit_prompt; /* Prompt user to insert smart card. */
char *pkinit_user; /* User ID to pass to PKINIT. */
struct vector *preauth_opt; /* Preauth options. */
bool try_pkinit; /* Attempt PKINIT, fall back to password. */
bool use_pkinit; /* Require PKINIT. */
/* Prompting. */
char *banner; /* Addition to password changing prompts. */
bool expose_account; /* Display principal in password prompts. */
bool force_first_pass; /* Require a previous password be stored. */
bool no_prompt; /* Let Kerberos handle password prompting. */
bool prompt_principal; /* Prompt for the Kerberos principal. */
bool try_first_pass; /* Try the previously entered password. */
bool use_authtok; /* Use the stored new password for changes. */
bool use_first_pass; /* Always use the previous password. */
/* Ticket caches. */
char *ccache; /* Path to write ticket cache to. */
char *ccache_dir; /* Directory for ticket cache. */
bool no_ccache; /* Don't create a ticket cache. */
bool retain_after_close; /* Don't destroy the cache on session end. */
/* The authentication context, which bundles together Kerberos data. */
struct context *ctx;
};
/* Default to a hidden visibility for all internal functions. */
#pragma GCC visibility push(hidden)
/* Parse the PAM flags, arguments, and krb5.conf and fill out pam_args. */
struct pam_args *pamk5_init(pam_handle_t *, int flags, int, const char **);
/* Free the pam_args struct when we're done. */
void pamk5_free(struct pam_args *);
/*
* The underlying functions between several of the major PAM interfaces.
*/
int pamk5_account(struct pam_args *);
int pamk5_authenticate(struct pam_args *);
/*
* The underlying function below pam_sm_chauthtok. If the second argument is
* true, we're doing the preliminary check and shouldn't actually change the
* password.
*/
int pamk5_password(struct pam_args *, bool only_auth);
/*
* Create or refresh the user's ticket cache. This is the underlying function
* beneath pam_sm_setcred and pam_sm_open_session.
*/
int pamk5_setcred(struct pam_args *, bool refresh);
/*
* Authenticate the user. Prompts for the password as needed and obtains
* tickets for in_tkt_service, krbtgt/<realm> by default. Stores the initial
* credentials in the final argument, allocating a new krb5_creds structure.
* If possible, the initial credentials are verified by checking them against
* the local system key.
*/
int pamk5_password_auth(struct pam_args *, const char *service, krb5_creds **);
/*
* Prompt the user for a new password, twice so that they can confirm. Sets
* PAM_AUTHTOK and puts the new password in newly allocated memory in pass if
* it's not NULL.
*/
int pamk5_password_prompt(struct pam_args *, char **pass);
/*
* Change the user's password. Prompts for the current password as needed and
* the new password. If the second argument is true, only obtains the
* necessary credentials without changing anything.
*/
int pamk5_password_change(struct pam_args *, bool only_auth);
/*
* Generic conversation function to display messages or get information from
* the user. Takes the message, the message type, and a place to put the
* result of a prompt.
*/
int pamk5_conv(struct pam_args *, const char *, int, char **);
/*
* Function specifically for getting a password. Takes a prefix (if non-NULL,
* args->banner will also be prepended) and a pointer into which to store the
* password. The password must be freed by the caller.
*/
int pamk5_get_password(struct pam_args *, const char *, char **);
/* Prompting function for the Kerberos libraries. */
krb5_error_code pamk5_prompter_krb5(krb5_context, void *data, const char *name,
const char *banner, int, krb5_prompt *);
/* Prompting function that doesn't allow passwords. */
krb5_error_code pamk5_prompter_krb5_no_password(krb5_context, void *data,
const char *name,
const char *banner, int,
krb5_prompt *);
/* Check the user with krb5_kuserok or the configured equivalent. */
int pamk5_authorized(struct pam_args *);
/* Returns true if we should ignore this user (root or low UID). */
int pamk5_should_ignore(struct pam_args *, PAM_CONST char *);
/*
* alt_auth_map support.
*
* pamk5_map_principal attempts to map the user to a Kerberos principal
* according to alt_auth_map. Returns 0 on success, storing the mapped
* principal name in newly allocated memory in principal. The caller is
* responsiple for freeing. Returns an errno value on any error.
*
* pamk5_alt_auth attempts an authentication to the given service with the
* given options and password and returns a Kerberos error code. On success,
* the new credentials are stored in krb5_creds.
*
* pamk5_alt_auth_verify verifies that Kerberos credentials are authorized to
* access the account given the configured alt_auth_map and is meant to be
* called from pamk5_authorized. It returns a PAM status code.
*/
int pamk5_map_principal(struct pam_args *, const char *username,
char **principal);
krb5_error_code pamk5_alt_auth(struct pam_args *, const char *service,
krb5_get_init_creds_opt *, const char *pass,
krb5_creds *);
int pamk5_alt_auth_verify(struct pam_args *);
/* FAST support. Set up FAST protection of authentication. */
void pamk5_fast_setup(struct pam_args *, krb5_get_init_creds_opt *);
/* Context management. */
int pamk5_context_new(struct pam_args *);
int pamk5_context_fetch(struct pam_args *);
void pamk5_context_free(struct pam_args *);
void pamk5_context_destroy(pam_handle_t *, void *data, int pam_end_status);
/* Get and set environment variables for the ticket cache. */
const char *pamk5_get_krb5ccname(struct pam_args *, const char *key);
int pamk5_set_krb5ccname(struct pam_args *, const char *, const char *key);
/*
* Create a ticket cache file securely given a mkstemp template. Modifies
* template in place to store the name of the created file.
*/
int pamk5_cache_mkstemp(struct pam_args *, char *template);
/*
* Create a ticket cache and initialize it with the provided credentials,
* returning the new cache in the last argument
*/
int pamk5_cache_init(struct pam_args *, const char *ccname, krb5_creds *,
krb5_ccache *);
/*
* Create a ticket cache with a random path, initialize it with the provided
* credentials, store it in the context, and put the path into PAM_KRB5CCNAME.
*/
int pamk5_cache_init_random(struct pam_args *, krb5_creds *);
/*
* Compatibility functions. Depending on whether pam_krb5 is built with MIT
* Kerberos or Heimdal, appropriate implementations for the Kerberos
* implementation will be provided.
*/
krb5_error_code pamk5_compat_set_realm(struct pam_config *, const char *);
void pamk5_compat_free_realm(struct pam_config *);
/* Undo default visibility change. */
#pragma GCC visibility pop
#endif /* !INTERNAL_H */
+259
View File
@@ -0,0 +1,259 @@
/*
* Option handling for pam-krb5.
*
* Responsible for initializing the args struct that's passed to nearly all
* internal functions. Retrieves configuration information from krb5.conf and
* parses the PAM configuration.
*
* Copyright 2005-2010, 2014, 2020 Russ Allbery <eagle@eyrie.org>
* Copyright 2011-2012
* The Board of Trustees of the Leland Stanford Junior University
* Copyright 2005 Andres Salomon <dilinger@debian.org>
* Copyright 1999-2000 Frank Cusack <fcusack@fcusack.com>
*
* SPDX-License-Identifier: BSD-3-clause or GPL-1+
*/
#include <config.h>
#include <portable/krb5.h>
#include <portable/system.h>
#include <errno.h>
#include <module/internal.h>
#include <pam-util/args.h>
#include <pam-util/logging.h>
#include <pam-util/options.h>
#include <pam-util/vector.h>
/* Our option definition. Must be sorted. */
#define K(name) (#name), offsetof(struct pam_config, name)
/* clang-format off */
static const struct option options[] = {
{ K(alt_auth_map), true, STRING (NULL) },
{ K(anon_fast), true, BOOL (false) },
{ K(banner), true, STRING ("Kerberos") },
{ K(ccache), true, STRING (NULL) },
{ K(ccache_dir), true, STRING ("FILE:/tmp") },
{ K(clear_on_fail), true, BOOL (false) },
{ K(debug), true, BOOL (false) },
{ K(defer_pwchange), true, BOOL (false) },
{ K(expose_account), true, BOOL (false) },
{ K(fail_pwchange), true, BOOL (false) },
{ K(fast_ccache), true, STRING (NULL) },
{ K(force_alt_auth), true, BOOL (false) },
{ K(force_first_pass), false, BOOL (false) },
{ K(force_pwchange), true, BOOL (false) },
{ K(forwardable), true, BOOL (false) },
{ K(ignore_k5login), true, BOOL (false) },
{ K(ignore_root), true, BOOL (false) },
{ K(keytab), true, STRING (NULL) },
{ K(minimum_uid), true, NUMBER (0) },
{ K(no_ccache), false, BOOL (false) },
{ K(no_prompt), true, BOOL (false) },
{ K(no_update_user), true, BOOL (false) },
{ K(only_alt_auth), true, BOOL (false) },
{ K(pkinit_anchors), true, STRING (NULL) },
{ K(pkinit_prompt), true, BOOL (false) },
{ K(pkinit_user), true, STRING (NULL) },
{ K(preauth_opt), true, LIST (NULL) },
{ K(prompt_principal), true, BOOL (false) },
{ K(realm), false, STRING (NULL) },
{ K(renew_lifetime), true, TIME (0) },
{ K(retain_after_close), true, BOOL (false) },
{ K(search_k5login), true, BOOL (false) },
{ K(silent), false, BOOL (false) },
{ K(ticket_lifetime), true, TIME (0) },
{ K(trace), false, STRING (NULL) },
{ K(try_first_pass), false, BOOL (false) },
{ K(try_pkinit), true, BOOL (false) },
{ K(use_authtok), false, BOOL (false) },
{ K(use_first_pass), false, BOOL (false) },
{ K(use_pkinit), true, BOOL (false) },
{ K(user_realm), true, STRING (NULL) },
};
/* clang-format on */
static const size_t optlen = sizeof(options) / sizeof(options[0]);
/*
* Allocate a new struct pam_args and initialize its data members, including
* parsing the arguments and getting settings from krb5.conf. Check the
* resulting options for consistency.
*/
struct pam_args *
pamk5_init(pam_handle_t *pamh, int flags, int argc, const char **argv)
{
int i;
struct pam_args *args;
struct pam_config *config = NULL;
args = putil_args_new(pamh, flags);
if (args == NULL) {
return NULL;
}
config = calloc(1, sizeof(struct pam_config));
if (config == NULL) {
goto nomem;
}
args->config = config;
/*
* Do an initial scan to see if the realm is already set in our options.
* If so, make sure that's set before we start loading option values,
* since it affects what comes out of krb5.conf.
*
* We will then ignore args->config->realm, set later by option parsing,
* in favor of using args->realm extracted here. However, the latter must
* exist to avoid throwing unknown option errors.
*/
for (i = 0; i < argc; i++) {
if (strncmp(argv[i], "realm=", 6) != 0)
continue;
free(args->realm);
args->realm = strdup(&argv[i][strlen("realm=")]);
if (args->realm == NULL)
goto nomem;
}
if (!putil_args_defaults(args, options, optlen)) {
free(config);
putil_args_free(args);
return NULL;
}
if (!putil_args_krb5(args, "pam", options, optlen)) {
goto fail;
}
if (!putil_args_parse(args, argc, argv, options, optlen)) {
goto fail;
}
if (config->debug) {
args->debug = true;
}
if (config->silent) {
args->silent = true;
}
/* An empty banner should be treated the same as not having one. */
if (config->banner != NULL && config->banner[0] == '\0') {
free(config->banner);
config->banner = NULL;
}
/* Sanity-check try_first_pass, use_first_pass, and force_first_pass. */
if (config->force_first_pass && config->try_first_pass) {
putil_err(args, "force_first_pass set, ignoring try_first_pass");
config->try_first_pass = 0;
}
if (config->force_first_pass && config->use_first_pass) {
putil_err(args, "force_first_pass set, ignoring use_first_pass");
config->use_first_pass = 0;
}
if (config->use_first_pass && config->try_first_pass) {
putil_err(args, "use_first_pass set, ignoring try_first_pass");
config->try_first_pass = 0;
}
/*
* Don't set expose_account if we're using search_k5login. The user will
* get a principal formed from the account into which they're logging in,
* which isn't the password they'll use (that's the whole point of
* search_k5login).
*/
if (config->search_k5login) {
config->expose_account = 0;
}
/* UIDs are unsigned on some systems. */
if (config->minimum_uid < 0) {
config->minimum_uid = 0;
}
/*
* Warn if PKINIT options were set and PKINIT isn't supported. The MIT
* method (krb5_get_init_creds_opt_set_pa) can't support use_pkinit.
*/
#ifndef HAVE_KRB5_GET_INIT_CREDS_OPT_SET_PKINIT
# ifndef HAVE_KRB5_GET_INIT_CREDS_OPT_SET_PA
if (config->try_pkinit) {
putil_err(args, "try_pkinit requested but PKINIT not available");
} else if (config->use_pkinit) {
putil_err(args, "use_pkinit requested but PKINIT not available");
}
# endif
# ifndef HAVE_KRB5_GET_PROMPT_TYPES
if (config->use_pkinit) {
putil_err(args, "use_pkinit requested but PKINIT cannot be enforced");
}
# endif
#endif
/* Warn if the FAST option was set and FAST isn't supported. */
#ifndef HAVE_KRB5_GET_INIT_CREDS_OPT_SET_FAST_CCACHE_NAME
if (config->fast_ccache || config->anon_fast) {
putil_err(args, "fast_ccache or anon_fast requested but FAST not"
" supported by Kerberos libraries");
}
#endif
/* If tracing was requested enable it if possible. */
#ifdef HAVE_KRB5_SET_TRACE_FILENAME
if (config->trace != NULL) {
krb5_error_code retval;
retval = krb5_set_trace_filename(args->ctx, config->trace);
if (retval == 0)
putil_debug(args, "enabled trace logging to %s", config->trace);
else
putil_err_krb5(args, retval, "cannot enable trace logging to %s",
config->trace);
}
#else
if (config->trace != NULL) {
putil_err(args, "trace logging requested but not supported");
}
#endif
return args;
nomem:
putil_crit(args, "cannot allocate memory: %s", strerror(errno));
free(config);
putil_args_free(args);
return NULL;
fail:
pamk5_free(args);
return NULL;
}
/*
* Free the allocated args struct and any memory it points to.
*/
void
pamk5_free(struct pam_args *args)
{
struct pam_config *config;
if (args == NULL)
return;
config = args->config;
if (config != NULL) {
free(config->alt_auth_map);
free(config->banner);
free(config->ccache);
free(config->ccache_dir);
free(config->fast_ccache);
free(config->keytab);
free(config->pkinit_anchors);
free(config->pkinit_user);
vector_free(config->preauth_opt);
free(config->realm);
free(config->trace);
free(config->user_realm);
free(args->config);
args->config = NULL;
}
putil_args_free(args);
}
+11
View File
@@ -0,0 +1,11 @@
{
global:
pam_sm_acct_mgmt;
pam_sm_authenticate;
pam_sm_chauthtok;
pam_sm_close_session;
pam_sm_open_session;
pam_sm_setcred;
local:
*;
};
+6
View File
@@ -0,0 +1,6 @@
pam_sm_acct_mgmt
pam_sm_authenticate
pam_sm_chauthtok
pam_sm_close_session
pam_sm_open_session
pam_sm_setcred
+401
View File
@@ -0,0 +1,401 @@
/*
* Kerberos password changing.
*
* Copyright 2005-2009, 2020 Russ Allbery <eagle@eyrie.org>
* Copyright 2011
* The Board of Trustees of the Leland Stanford Junior University
* Copyright 2005 Andres Salomon <dilinger@debian.org>
* Copyright 1999-2000 Frank Cusack <fcusack@fcusack.com>
*
* SPDX-License-Identifier: BSD-3-clause or GPL-1+
*/
#include <config.h>
#include <portable/krb5.h>
#include <portable/pam.h>
#include <portable/system.h>
#include <errno.h>
#include <module/internal.h>
#include <pam-util/args.h>
#include <pam-util/logging.h>
/*
* Get the new password. Store it in PAM_AUTHTOK if we obtain it and verify
* it successfully and return it in the pass parameter. If pass is set to
* NULL, only store the new password in PAM_AUTHTOK.
*
* Returns a PAM error code, usually either PAM_AUTHTOK_ERR or PAM_SUCCESS.
*/
int
pamk5_password_prompt(struct pam_args *args, char **pass)
{
int pamret = PAM_AUTHTOK_ERR;
char *pass1 = NULL;
char *pass2;
PAM_CONST void *tmp;
/* Use the password from a previous module, if so configured. */
if (pass != NULL)
*pass = NULL;
if (args->config->use_authtok) {
pamret = pam_get_item(args->pamh, PAM_AUTHTOK, &tmp);
if (tmp == NULL) {
putil_debug_pam(args, pamret, "no stored password");
pamret = PAM_AUTHTOK_ERR;
goto done;
}
if (strlen(tmp) > PAM_MAX_RESP_SIZE - 1) {
putil_debug(args, "rejecting password longer than %d",
PAM_MAX_RESP_SIZE - 1);
pamret = PAM_AUTHTOK_ERR;
goto done;
}
pass1 = strdup((const char *) tmp);
}
/* Prompt for the new password if necessary. */
if (pass1 == NULL) {
pamret = pamk5_get_password(args, "Enter new", &pass1);
if (pamret != PAM_SUCCESS) {
putil_debug_pam(args, pamret, "error getting new password");
pamret = PAM_AUTHTOK_ERR;
goto done;
}
if (strlen(pass1) > PAM_MAX_RESP_SIZE - 1) {
putil_debug(args, "rejecting password longer than %d",
PAM_MAX_RESP_SIZE - 1);
pamret = PAM_AUTHTOK_ERR;
explicit_bzero(pass1, strlen(pass1));
free(pass1);
goto done;
}
pamret = pamk5_get_password(args, "Retype new", &pass2);
if (pamret != PAM_SUCCESS) {
putil_debug_pam(args, pamret, "error getting new password");
pamret = PAM_AUTHTOK_ERR;
explicit_bzero(pass1, strlen(pass1));
free(pass1);
goto done;
}
if (strcmp(pass1, pass2) != 0) {
putil_debug(args, "new passwords don't match");
pamk5_conv(args, "Passwords don't match", PAM_ERROR_MSG, NULL);
explicit_bzero(pass1, strlen(pass1));
free(pass1);
explicit_bzero(pass2, strlen(pass2));
free(pass2);
pamret = PAM_AUTHTOK_ERR;
goto done;
}
explicit_bzero(pass2, strlen(pass2));
free(pass2);
/* Save the new password for other modules. */
pamret = pam_set_item(args->pamh, PAM_AUTHTOK, pass1);
if (pamret != PAM_SUCCESS) {
putil_err_pam(args, pamret, "error storing password");
pamret = PAM_AUTHTOK_ERR;
explicit_bzero(pass1, strlen(pass1));
free(pass1);
goto done;
}
}
if (pass != NULL)
*pass = pass1;
else {
explicit_bzero(pass1, strlen(pass1));
free(pass1);
}
done:
return pamret;
}
/*
* We've obtained credentials for the password changing interface and gotten
* the new password, so do the work of actually changing the password.
*/
static int
change_password(struct pam_args *args, const char *pass)
{
struct context *ctx;
int retval = PAM_SUCCESS;
int result_code;
krb5_data result_code_string, result_string;
const char *message;
/* Sanity check. */
if (args == NULL || args->config == NULL || args->config->ctx == NULL
|| args->config->ctx->creds == NULL)
return PAM_AUTHTOK_ERR;
ctx = args->config->ctx;
/*
* The actual change.
*
* There are two password protocols in use: the change password protocol,
* which doesn't allow specification of the principal, and the newer set
* password protocol, which does. For our purposes, either will do.
*
* Both Heimdal and MIT provide krb5_set_password. With Heimdal,
* krb5_change_password is deprecated and krb5_set_password tries both
* protocols in turn, so will work with new and old servers. With MIT,
* krb5_set_password will use the old protocol if the principal is NULL
* and the new protocol if it is not.
*
* We would like to just use krb5_set_password with a NULL principal
* argument, but Heimdal 1.5 uses the default principal for the local user
* rather than the principal from the credentials, so we need to pass in a
* principal for Heimdal. So we're stuck with an #ifdef.
*/
#ifdef HAVE_KRB5_MIT
retval =
krb5_set_password(ctx->context, ctx->creds, (char *) pass, NULL,
&result_code, &result_code_string, &result_string);
#else
retval =
krb5_set_password(ctx->context, ctx->creds, (char *) pass, ctx->princ,
&result_code, &result_code_string, &result_string);
#endif
/* Everything from here on is just handling diagnostics and output. */
if (retval != 0) {
putil_debug_krb5(args, retval, "krb5_change_password failed");
message = krb5_get_error_message(ctx->context, retval);
pamk5_conv(args, message, PAM_ERROR_MSG, NULL);
krb5_free_error_message(ctx->context, message);
retval = PAM_AUTHTOK_ERR;
goto done;
}
if (result_code != 0) {
char *output;
int status;
putil_debug(args, "krb5_change_password: %s",
(char *) result_code_string.data);
retval = PAM_AUTHTOK_ERR;
status =
asprintf(&output, "%.*s%s%.*s", (int) result_code_string.length,
(char *) result_code_string.data,
result_string.length == 0 ? "" : ": ",
(int) result_string.length, (char *) result_string.data);
if (status < 0)
putil_crit(args, "asprintf failed: %s", strerror(errno));
else {
pamk5_conv(args, output, PAM_ERROR_MSG, NULL);
free(output);
}
}
krb5_free_data_contents(ctx->context, &result_string);
krb5_free_data_contents(ctx->context, &result_code_string);
done:
/*
* On failure, when clear_on_fail is set, we set the new password to NULL
* so that subsequent password change PAM modules configured with
* use_authtok will also fail. Otherwise, since the order of the stack is
* fixed once the pre-check function runs, subsequent modules would
* continue even when we failed.
*/
if (retval != PAM_SUCCESS && args->config->clear_on_fail) {
if (pam_set_item(args->pamh, PAM_AUTHTOK, NULL))
putil_err(args, "error clearing password");
}
return retval;
}
/*
* Change a user's password. Returns a PAM status code for success or
* failure. This does the work of pam_sm_chauthtok, but also needs to be
* called from pam_sm_authenticate if we're working around a library that
* can't handle password change during authentication.
*
* If the second argument is true, only do the authentication without actually
* doing the password change (PAM_PRELIM_CHECK).
*/
int
pamk5_password_change(struct pam_args *args, bool only_auth)
{
struct context *ctx = args->config->ctx;
int pamret = PAM_SUCCESS;
char *pass = NULL;
/*
* Authenticate to the password changing service using the old password.
*/
if (ctx->creds == NULL) {
pamret = pamk5_password_auth(args, "kadmin/changepw", &ctx->creds);
if (pamret == PAM_SERVICE_ERR || pamret == PAM_AUTH_ERR)
pamret = PAM_AUTHTOK_RECOVER_ERR;
if (pamret != PAM_SUCCESS)
goto done;
}
/*
* Now, get the new password and change it unless we're just doing the
* first check.
*/
if (only_auth)
goto done;
pamret = pamk5_password_prompt(args, &pass);
if (pamret != PAM_SUCCESS)
goto done;
pamret = change_password(args, pass);
if (pamret == PAM_SUCCESS)
pam_syslog(args->pamh, LOG_INFO, "user %s changed Kerberos password",
ctx->name);
done:
if (pass != NULL) {
explicit_bzero(pass, strlen(pass));
free(pass);
}
return pamret;
}
/*
* The function underlying the main PAM interface for password changing.
* Performs preliminary checks, user notification, and any reauthentication
* that's required.
*
* If the second argument is true, only do the authentication without actually
* doing the password change (PAM_PRELIM_CHECK).
*/
int
pamk5_password(struct pam_args *args, bool only_auth)
{
struct context *ctx = NULL;
int pamret, status;
PAM_CONST char *user;
char *pass = NULL;
bool set_context = false;
/*
* Check whether we should ignore this user.
*
* If we do ignore this user, and we're not in the preliminary check
* phase, still prompt the user for the new password, but suppress our
* banner. This is a little strange, but it allows another module to be
* stacked behind pam-krb5 with use_authtok and have it still work for
* ignored users.
*
* We ignore the return status when prompting for the new password in this
* case. The worst thing that can happen is to fail to get the password,
* in which case the other module will fail (or might even not care).
*/
if (args->config->ignore_root || args->config->minimum_uid > 0) {
status = pam_get_user(args->pamh, &user, NULL);
if (status == PAM_SUCCESS && pamk5_should_ignore(args, user)) {
if (!only_auth) {
if (args->config->banner != NULL) {
free(args->config->banner);
args->config->banner = NULL;
}
pamk5_password_prompt(args, NULL);
}
pamret = PAM_IGNORE;
goto done;
}
}
/*
* If we weren't able to find an existing context to use, we're going
* into this fresh and need to create a new context.
*/
if (args->config->ctx == NULL) {
pamret = pamk5_context_new(args);
if (pamret != PAM_SUCCESS) {
putil_debug_pam(args, pamret, "creating context failed");
pamret = PAM_AUTHTOK_ERR;
goto done;
}
pamret = pam_set_data(args->pamh, "pam_krb5", args->config->ctx,
pamk5_context_destroy);
if (pamret != PAM_SUCCESS) {
putil_err_pam(args, pamret, "cannot set context data");
pamret = PAM_AUTHTOK_ERR;
goto done;
}
set_context = true;
}
ctx = args->config->ctx;
/*
* Tell the user what's going on if we're handling an expiration, but not
* if we were configured to use the same password as an earlier module in
* the stack. The correct behavior here is not clear (what if the
* Kerberos password expired but the other one didn't?), but warning
* unconditionally leads to a strange message in the middle of doing the
* password change.
*/
if (ctx->expired && ctx->creds == NULL)
if (!args->config->force_first_pass && !args->config->use_first_pass)
pamk5_conv(args, "Password expired. You must change it now.",
PAM_TEXT_INFO, NULL);
/*
* Do the password change. This may only get tickets if we're doing the
* preliminary check phase.
*/
pamret = pamk5_password_change(args, only_auth);
if (only_auth)
goto done;
/*
* If we were handling a forced password change for an expired password,
* now try to get a ticket cache with the new password. If this succeeds,
* clear the expired flag in the context.
*/
if (pamret == PAM_SUCCESS && ctx->expired) {
krb5_creds *creds = NULL;
char *principal;
krb5_error_code retval;
putil_debug(args, "obtaining credentials with new password");
args->config->force_first_pass = 1;
pamret = pamk5_password_auth(args, NULL, &creds);
if (pamret != PAM_SUCCESS)
goto done;
retval = krb5_unparse_name(ctx->context, ctx->princ, &principal);
if (retval != 0) {
putil_err_krb5(args, retval, "krb5_unparse_name failed");
pam_syslog(args->pamh, LOG_INFO,
"user %s authenticated as UNKNOWN", ctx->name);
} else {
pam_syslog(args->pamh, LOG_INFO, "user %s authenticated as %s",
ctx->name, principal);
krb5_free_unparsed_name(ctx->context, principal);
}
ctx->expired = false;
pamret = pamk5_cache_init_random(args, creds);
krb5_free_cred_contents(ctx->context, creds);
free(creds);
}
done:
if (pass != NULL) {
explicit_bzero(pass, strlen(pass));
free(pass);
}
/*
* Don't free our Kerberos context if we set a context, since the context
* will take care of that.
*/
if (set_context)
args->ctx = NULL;
if (pamret != PAM_SUCCESS) {
if (pamret == PAM_SERVICE_ERR || pamret == PAM_AUTH_ERR)
pamret = PAM_AUTHTOK_ERR;
if (pamret == PAM_AUTHINFO_UNAVAIL)
pamret = PAM_AUTHTOK_ERR;
}
return pamret;
}
+481
View File
@@ -0,0 +1,481 @@
/*
* Prompt users for information.
*
* Handles all interaction with the PAM conversation, either directly or
* indirectly through the Kerberos libraries.
*
* Copyright 2005-2007, 2009, 2014, 2017, 2020 Russ Allbery <eagle@eyrie.org>
* Copyright 2011-2012
* The Board of Trustees of the Leland Stanford Junior University
* Copyright 2005 Andres Salomon <dilinger@debian.org>
* Copyright 1999-2000 Frank Cusack <fcusack@fcusack.com>
*
* SPDX-License-Identifier: BSD-3-clause or GPL-1+
*/
#include <config.h>
#include <portable/krb5.h>
#include <portable/pam.h>
#include <portable/system.h>
#include <assert.h>
#include <errno.h>
#include <module/internal.h>
#include <pam-util/args.h>
#include <pam-util/logging.h>
/*
* Build a password prompt.
*
* The default prompt is simply "Password:". Optionally, a string describing
* the type of password is passed in as prefix. In this case, the prompts is:
*
* <prefix> <banner> password:
*
* where <prefix> is the argument passed and <banner> is the value of
* args->banner (defaulting to "Kerberos").
*
* If args->config->expose_account is set, we append the principal name (taken
* from args->config->ctx->princ) before the colon, so the prompts are:
*
* Password for <principal>:
* <prefix> <banner> password for <principal>:
*
* Normally this is not done because it exposes the realm and possibly any
* username to principal mappings, plus may confuse some ssh clients if sshd
* passes the prompt back to the client.
*
* Returns newly-allocated memory or NULL on failure. The caller is
* responsible for freeing.
*/
static char *
build_password_prompt(struct pam_args *args, const char *prefix)
{
struct context *ctx = args->config->ctx;
char *principal = NULL;
const char *banner, *bspace;
char *prompt, *tmp;
bool expose_account;
krb5_error_code k5_errno;
int retval;
/* If we're exposing the account, format the principal name. */
if (args->config->expose_account || prefix != NULL)
if (ctx != NULL && ctx->context != NULL && ctx->princ != NULL) {
k5_errno = krb5_unparse_name(ctx->context, ctx->princ, &principal);
if (k5_errno != 0)
putil_debug_krb5(args, k5_errno, "krb5_unparse_name failed");
}
/* Build the part of the prompt without the principal name. */
if (prefix == NULL)
tmp = strdup("Password");
else {
banner = (args->config->banner == NULL) ? "" : args->config->banner;
bspace = (args->config->banner == NULL) ? "" : " ";
retval = asprintf(&tmp, "%s%s%s password", prefix, bspace, banner);
if (retval < 0)
tmp = NULL;
}
if (tmp == NULL)
goto fail;
/* Add the principal, if desired, and the colon and space. */
expose_account = args->config->expose_account && principal != NULL;
if (expose_account)
retval = asprintf(&prompt, "%s for %s: ", tmp, principal);
else
retval = asprintf(&prompt, "%s: ", tmp);
free(tmp);
if (retval < 0)
goto fail;
/* Clean up and return. */
if (principal != NULL)
krb5_free_unparsed_name(ctx->context, principal);
return prompt;
fail:
if (principal != NULL)
krb5_free_unparsed_name(ctx->context, principal);
return NULL;
}
/*
* Prompt for a password.
*
* The entered password is stored in password. The memory is allocated by the
* application and returned as part of the PAM conversation. It must be freed
* by the caller.
*
* Returns a PAM success or error code.
*/
int
pamk5_get_password(struct pam_args *args, const char *prefix, char **password)
{
char *prompt;
int retval;
prompt = build_password_prompt(args, prefix);
if (prompt == NULL)
return PAM_BUF_ERR;
retval = pamk5_conv(args, prompt, PAM_PROMPT_ECHO_OFF, password);
free(prompt);
return retval;
}
/*
* Get information from the user or display a message to the user, as
* determined by type. If PAM_SILENT was given, don't pass any text or error
* messages to the application.
*
* The response variable is set to the response returned by the conversation
* function on a successful return if a response was desired. Caller is
* responsible for freeing it.
*/
int
pamk5_conv(struct pam_args *args, const char *message, int type,
char **response)
{
int pamret;
struct pam_message msg;
PAM_CONST struct pam_message *pmsg;
struct pam_response *resp = NULL;
struct pam_conv *conv;
int want_reply;
if (args->silent && (type == PAM_ERROR_MSG || type == PAM_TEXT_INFO))
return PAM_SUCCESS;
pamret = pam_get_item(args->pamh, PAM_CONV, (PAM_CONST void **) &conv);
if (pamret != PAM_SUCCESS)
return pamret;
if (conv->conv == NULL)
return PAM_CONV_ERR;
pmsg = &msg;
msg.msg_style = type;
msg.msg = (PAM_CONST char *) message;
pamret = conv->conv(1, &pmsg, &resp, conv->appdata_ptr);
if (pamret != PAM_SUCCESS)
return pamret;
/*
* Only expect a response for PAM_PROMPT_ECHO_OFF or PAM_PROMPT_ECHO_ON
* message types. This mildly annoying logic makes sure that everything
* is freed properly (except the response itself, if wanted, which is
* returned for the caller to free) and that the success status is set
* based on whether the reply matched our expectations.
*
* If we got a reply even though we didn't want one, still overwrite the
* reply before freeing in case it was a password.
*/
want_reply = (type == PAM_PROMPT_ECHO_OFF || type == PAM_PROMPT_ECHO_ON);
if (resp == NULL || resp->resp == NULL)
pamret = want_reply ? PAM_CONV_ERR : PAM_SUCCESS;
else if (want_reply && response != NULL) {
*response = resp->resp;
pamret = PAM_SUCCESS;
} else {
explicit_bzero(resp->resp, strlen(resp->resp));
free(resp->resp);
pamret = want_reply ? PAM_SUCCESS : PAM_CONV_ERR;
}
free(resp);
return pamret;
}
/*
* Allocate memory to copy all of the prompts into a pam_message.
*
* Linux PAM and Solaris PAM expect different things here. Solaris PAM
* expects to receive a pointer to a pointer to an array of pam_message
* structs. Linux PAM expects to receive a pointer to an array of pointers to
* pam_message structs. In order for the module to work with either PAM
* implementation, we need to set up a structure that is valid either way you
* look at it.
*
* We do this by making msg point to the array of struct pam_message pointers
* (what Linux PAM expects), and then make the first one of those pointers
* point to the array of pam_message structs. Solaris will then be happy,
* looking at only the first element of the outer array and finding it
* pointing to the inner array. Then, for Linux, we point the other elements
* of the outer array to the storage allocated in the inner array.
*
* All this also means we have to be careful how we free the resulting
* structure since it's double-linked in a subtle way. Thankfully, we get to
* free it ourselves.
*/
static struct pam_message **
allocate_pam_message(size_t total_prompts)
{
struct pam_message **msg;
size_t i;
msg = calloc(total_prompts, sizeof(struct pam_message *));
if (msg == NULL)
return NULL;
*msg = calloc(total_prompts, sizeof(struct pam_message));
if (*msg == NULL) {
free(msg);
return NULL;
}
for (i = 1; i < total_prompts; i++)
msg[i] = msg[0] + i;
return msg;
}
/*
* Free the structure created by allocate_pam_message.
*/
static void
free_pam_message(struct pam_message **msg, size_t total_prompts)
{
size_t i;
for (i = 0; i < total_prompts; i++)
free((char *) msg[i]->msg);
free(*msg);
free(msg);
}
/*
* Free the responses returned by the conversation function. These may
* contain passwords, so we overwrite them before we free them.
*/
static void
free_pam_responses(struct pam_response *resp, size_t total_prompts)
{
size_t i;
if (resp == NULL)
return;
for (i = 0; i < total_prompts; i++) {
if (resp[i].resp != NULL) {
explicit_bzero(resp[i].resp, strlen(resp[i].resp));
free(resp[i].resp);
}
}
free(resp);
}
/*
* Format a Kerberos prompt into a PAM prompt. Takes a krb5_prompt as input
* and writes the resulting PAM prompt into a struct pam_message.
*/
static krb5_error_code
format_prompt(krb5_prompt *prompt, struct pam_message *message)
{
size_t len = strlen(prompt->prompt);
bool has_colon;
const char *colon;
int retval, style;
/*
* Heimdal adds the trailing colon and space, while MIT does not.
* Work around the difference by looking to see if there's a trailing
* colon and space already and only adding it if there is not.
*/
has_colon = (len > 2 && memcmp(&prompt->prompt[len - 2], ": ", 2) == 0);
colon = has_colon ? "" : ": ";
retval = asprintf((char **) &message->msg, "%s%s", prompt->prompt, colon);
if (retval < 0)
return retval;
style = prompt->hidden ? PAM_PROMPT_ECHO_OFF : PAM_PROMPT_ECHO_ON;
message->msg_style = style;
return 0;
}
/*
* Given an array of struct pam_response elements, record the responses in the
* corresponding krb5_prompt structures.
*/
static krb5_error_code
record_prompt_answers(struct pam_response *resp, int num_prompts,
krb5_prompt *prompts)
{
int i;
for (i = 0; i < num_prompts; i++) {
size_t len, allowed;
if (resp[i].resp == NULL)
return KRB5_LIBOS_CANTREADPWD;
len = strlen(resp[i].resp);
allowed = prompts[i].reply->length;
if (allowed == 0 || len > allowed - 1)
return KRB5_LIBOS_CANTREADPWD;
/*
* Since the first version of this module, it has copied a nul
* character into the prompt data buffer for MIT Kerberos with the
* note that "other applications expect it to be there." I suspect
* this is incorrect and nothing cares about this nul, but have
* preserved this behavior out of an abundance of caution.
*
* Note that it shortens the maximum response length we're willing to
* accept by one (implemented above) and is the source of one prior
* security vulnerability.
*/
memcpy(prompts[i].reply->data, resp[i].resp, len + 1);
prompts[i].reply->length = (unsigned int) len;
}
return 0;
}
/*
* This is the generic prompting function called by both MIT Kerberos and
* Heimdal prompting implementations.
*
* There are a lot of structures and different layers of code at work here,
* making this code quite confusing. This function is a prompter function to
* pass into the Kerberos library, in particular krb5_get_init_creds_password.
* It is used by the Kerberos library to prompt for a password if need be, and
* also to prompt for password changes if the password was expired.
*
* The purpose of this function is to serve as glue between the Kerberos
* library and the application (by way of the PAM glue). PAM expects us to
* pass back to the conversation function an array of prompts and receive from
* the application an array of responses to those prompts. We pass the
* application an array of struct pam_message pointers, and the application
* passes us an array of struct pam_response pointers.
*
* Kerberos, meanwhile, passes us in an array of krb5_prompt structs. This
* struct contains the prompt, a flag saying whether to suppress echoing of
* what the user types for that prompt, and a buffer into which to store the
* response.
*
* Therefore, what we're doing here is copying the prompts from the
* krb5_prompt structs into pam_message structs, calling the conversation
* function, and then copying the responses back out of pam_response structs
* into the krb5_prompt structs to return to the Kerberos library.
*/
krb5_error_code
pamk5_prompter_krb5(krb5_context context UNUSED, void *data, const char *name,
const char *banner, int num_prompts, krb5_prompt *prompts)
{
struct pam_args *args = data;
int current_prompt, retval, pamret, i, offset;
int total_prompts = num_prompts;
struct pam_message **msg;
struct pam_response *resp = NULL;
struct pam_conv *conv;
/* Treat the name and banner as prompts that doesn't need input. */
if (name != NULL && !args->silent)
total_prompts++;
if (banner != NULL && !args->silent)
total_prompts++;
/* If we have zero prompts, do nothing, silently. */
if (total_prompts == 0)
return 0;
/* Obtain the conversation function from the application. */
pamret = pam_get_item(args->pamh, PAM_CONV, (PAM_CONST void **) &conv);
if (pamret != 0)
return KRB5_LIBOS_CANTREADPWD;
if (conv->conv == NULL)
return KRB5_LIBOS_CANTREADPWD;
/* Allocate memory to copy all of the prompts into a pam_message. */
msg = allocate_pam_message(total_prompts);
if (msg == NULL)
return ENOMEM;
/* current_prompt is an index into msg and a count when we're done. */
current_prompt = 0;
if (name != NULL && !args->silent) {
msg[current_prompt]->msg = strdup(name);
if (msg[current_prompt]->msg == NULL) {
retval = ENOMEM;
goto cleanup;
}
msg[current_prompt]->msg_style = PAM_TEXT_INFO;
current_prompt++;
}
if (banner != NULL && !args->silent) {
assert(current_prompt < total_prompts);
msg[current_prompt]->msg = strdup(banner);
if (msg[current_prompt]->msg == NULL) {
retval = ENOMEM;
goto cleanup;
}
msg[current_prompt]->msg_style = PAM_TEXT_INFO;
current_prompt++;
}
for (i = 0; i < num_prompts; i++) {
assert(current_prompt < total_prompts);
retval = format_prompt(&prompts[i], msg[current_prompt]);
if (retval < 0)
goto cleanup;
current_prompt++;
}
/* Call into the application conversation function. */
pamret = conv->conv(total_prompts, (PAM_CONST struct pam_message **) msg,
&resp, conv->appdata_ptr);
if (pamret != 0 || resp == NULL) {
retval = KRB5_LIBOS_CANTREADPWD;
goto cleanup;
}
/*
* Record the answers in the Kerberos data structure. If name or banner
* were provided, skip over the initial PAM responses that correspond to
* those messages.
*/
offset = 0;
if (name != NULL && !args->silent)
offset++;
if (banner != NULL && !args->silent)
offset++;
retval = record_prompt_answers(resp + offset, num_prompts, prompts);
cleanup:
free_pam_message(msg, total_prompts);
free_pam_responses(resp, total_prompts);
return retval;
}
/*
* This is a special version of krb5_prompter_krb5 that returns an error if
* the Kerberos library asks for a password. It is only used with MIT
* Kerberos as part of the implementation of try_pkinit and use_pkinit.
* (Heimdal has a different API for PKINIT authentication.)
*/
#ifdef HAVE_KRB5_GET_PROMPT_TYPES
krb5_error_code
pamk5_prompter_krb5_no_password(krb5_context context, void *data,
const char *name, const char *banner,
int num_prompts, krb5_prompt *prompts)
{
krb5_prompt_type *ptypes;
int i;
ptypes = krb5_get_prompt_types(context);
for (i = 0; i < num_prompts; i++)
if (ptypes != NULL && ptypes[i] == KRB5_PROMPT_TYPE_PASSWORD)
return KRB5_LIBOS_CANTREADPWD;
return pamk5_prompter_krb5(context, data, name, banner, num_prompts,
prompts);
}
#else /* !HAVE_KRB5_GET_PROMPT_TYPES */
krb5_error_code
pamk5_prompter_krb5_no_password(krb5_context context, void *data,
const char *name, const char *banner,
int num_prompts, krb5_prompt *prompts)
{
return pamk5_prompter_krb5(context, data, name, banner, num_prompts,
prompts);
}
#endif /* !HAVE_KRB5_GET_PROMPT_TYPES */
+260
View File
@@ -0,0 +1,260 @@
/*
* The public APIs of the pam-afs-session PAM module.
*
* Provides the public pam_sm_authenticate, pam_sm_setcred,
* pam_sm_open_session, pam_sm_close_session, and pam_sm_chauthtok functions.
* These must all be specified in the same file to work with the symbol export
* and linking mechanism used in OpenPAM, since OpenPAM will mark them all as
* static functions and export a function table instead.
*
* Written by Russ Allbery <eagle@eyrie.org>
* Copyright 2005-2009, 2017, 2020 Russ Allbery <eagle@eyrie.org>
* Copyright 2011
* The Board of Trustees of the Leland Stanford Junior University
* Copyright 2005 Andres Salomon <dilinger@debian.org>
* Copyright 1999-2000 Frank Cusack <fcusack@fcusack.com>
*
* SPDX-License-Identifier: BSD-3-clause or GPL-1+
*/
/* Get prototypes for all of the functions. */
#define PAM_SM_ACCOUNT
#define PAM_SM_AUTH
#define PAM_SM_PASSWORD
#define PAM_SM_SESSION
#include <config.h>
#include <portable/pam.h>
#include <portable/system.h>
#include <module/internal.h>
#include <pam-util/args.h>
#include <pam-util/logging.h>
/*
* The main PAM interface for authorization checking.
*/
PAM_EXTERN int
pam_sm_acct_mgmt(pam_handle_t *pamh, int flags, int argc, const char **argv)
{
struct pam_args *args;
int pamret;
args = pamk5_init(pamh, flags, argc, argv);
if (args == NULL) {
pamret = PAM_AUTH_ERR;
goto done;
}
pamret = pamk5_context_fetch(args);
ENTRY(args, flags);
/*
* Succeed if the user did not use krb5 to login. Ideally, we should
* probably fail and require that the user set up policy properly in their
* PAM configuration, but it's not common for the user to do so and that's
* not how other krb5 PAM modules work. If we don't do this, root logins
* with the system root password fail, which is a bad failure mode.
*/
if (pamret != PAM_SUCCESS || args->config->ctx == NULL) {
pamret = PAM_IGNORE;
putil_debug(args, "skipping non-Kerberos login");
goto done;
}
pamret = pamk5_account(args);
done:
EXIT(args, pamret);
pamk5_free(args);
return pamret;
}
/*
* The main PAM interface for authentication. We also do authorization checks
* here, since many applications don't call pam_acct_mgmt.
*/
PAM_EXTERN int
pam_sm_authenticate(pam_handle_t *pamh, int flags, int argc, const char **argv)
{
struct pam_args *args;
int pamret;
args = pamk5_init(pamh, flags, argc, argv);
if (args == NULL) {
pamret = PAM_SERVICE_ERR;
goto done;
}
ENTRY(args, flags);
pamret = pamk5_authenticate(args);
done:
EXIT(args, pamret);
pamk5_free(args);
return pamret;
}
/*
* The main PAM interface, in the auth stack, for establishing credentials
* obtained during authentication.
*/
PAM_EXTERN int
pam_sm_setcred(pam_handle_t *pamh, int flags, int argc, const char **argv)
{
struct pam_args *args;
bool refresh = false;
int pamret, allow;
args = pamk5_init(pamh, flags, argc, argv);
if (args == NULL) {
pamret = PAM_SERVICE_ERR;
goto done;
}
ENTRY(args, flags);
/*
* Special case. Just free the context data, which will destroy the
* ticket cache as well.
*/
if (flags & PAM_DELETE_CRED) {
pamret = pam_set_data(pamh, "pam_krb5", NULL, NULL);
if (pamret != PAM_SUCCESS)
putil_err_pam(args, pamret, "cannot clear context data");
goto done;
}
/*
* Reinitialization requested, which means that rather than creating a new
* ticket cache and setting KRB5CCNAME, we should figure out the existing
* ticket cache and just refresh its tickets.
*/
if (flags & (PAM_REINITIALIZE_CRED | PAM_REFRESH_CRED))
refresh = true;
if (refresh && (flags & PAM_ESTABLISH_CRED)) {
putil_err(args, "requested establish and refresh at the same time");
pamret = PAM_SERVICE_ERR;
goto done;
}
allow = PAM_REINITIALIZE_CRED | PAM_REFRESH_CRED | PAM_ESTABLISH_CRED;
if (!(flags & allow)) {
putil_err(args, "invalid pam_setcred flags %d", flags);
pamret = PAM_SERVICE_ERR;
goto done;
}
/* Do the work. */
pamret = pamk5_setcred(args, refresh);
/*
* Never return PAM_IGNORE from pam_setcred since this can confuse the
* Linux PAM library, at least for applications that call pam_setcred
* without pam_authenticate (possibly because authentication was done
* some other way), when used with jumps with the [] syntax. Since we
* do nothing in this case, and since the stack is already frozen from
* the auth group, success makes sense.
*
* Don't return an error here or the PAM stack will fail if pam-krb5 is
* used with [success=ok default=1], since jumps are treated as required
* during the second pass with pam_setcred.
*/
if (pamret == PAM_IGNORE)
pamret = PAM_SUCCESS;
done:
EXIT(args, pamret);
pamk5_free(args);
return pamret;
}
/*
* The main PAM interface for password changing.
*/
PAM_EXTERN int
pam_sm_chauthtok(pam_handle_t *pamh, int flags, int argc, const char **argv)
{
struct pam_args *args;
int pamret;
args = pamk5_init(pamh, flags, argc, argv);
if (args == NULL) {
pamret = PAM_AUTHTOK_ERR;
goto done;
}
pamk5_context_fetch(args);
ENTRY(args, flags);
/* We only support password changes. */
if (!(flags & PAM_UPDATE_AUTHTOK) && !(flags & PAM_PRELIM_CHECK)) {
putil_err(args, "invalid pam_chauthtok flags %d", flags);
pamret = PAM_AUTHTOK_ERR;
goto done;
}
pamret = pamk5_password(args, (flags & PAM_PRELIM_CHECK) != 0);
done:
EXIT(args, pamret);
pamk5_free(args);
return pamret;
}
/*
* The main PAM interface for opening a session.
*/
PAM_EXTERN int
pam_sm_open_session(pam_handle_t *pamh, int flags, int argc, const char **argv)
{
struct pam_args *args;
int pamret;
args = pamk5_init(pamh, flags, argc, argv);
if (args == NULL) {
pamret = PAM_SERVICE_ERR;
goto done;
}
ENTRY(args, flags);
pamret = pamk5_setcred(args, 0);
done:
EXIT(args, pamret);
pamk5_free(args);
return pamret;
}
/*
* The main PAM interface for closing a session.
*/
PAM_EXTERN int
pam_sm_close_session(pam_handle_t *pamh, int flags, int argc,
const char **argv)
{
struct pam_args *args;
int pamret;
args = pamk5_init(pamh, flags, argc, argv);
if (args == NULL) {
pamret = PAM_SERVICE_ERR;
goto done;
}
ENTRY(args, flags);
pamret = pam_set_data(pamh, "pam_krb5", NULL, NULL);
if (pamret != PAM_SUCCESS)
putil_err_pam(args, pamret, "cannot clear context data");
done:
EXIT(args, pamret);
pamk5_free(args);
return pamret;
}
/* OpenPAM uses this macro to set up a table of entry points. */
#ifdef PAM_MODULE_ENTRY
PAM_MODULE_ENTRY("pam_krb5");
#endif
+474
View File
@@ -0,0 +1,474 @@
/*
* Ticket creation routines for pam-krb5.
*
* pam_setcred and pam_open_session need to do similar but not identical work
* to create the user's ticket cache. The shared code is abstracted here into
* the pamk5_setcred function.
*
* Copyright 2005-2009, 2014, 2017, 2020 Russ Allbery <eagle@eyrie.org>
* Copyright 2011
* The Board of Trustees of the Leland Stanford Junior University
* Copyright 2005 Andres Salomon <dilinger@debian.org>
* Copyright 1999-2000 Frank Cusack <fcusack@fcusack.com>
*
* SPDX-License-Identifier: BSD-3-clause or GPL-1+
*/
#include <config.h>
#include <portable/krb5.h>
#include <portable/pam.h>
#include <portable/system.h>
#include <assert.h>
#include <errno.h>
#include <pwd.h>
#include <module/internal.h>
#include <pam-util/args.h>
#include <pam-util/logging.h>
/*
* Given a cache name and an existing cache, initialize a new cache, store the
* credentials from the existing cache in it, and return a pointer to the new
* cache in the cache argument. Returns either PAM_SUCCESS or
* PAM_SERVICE_ERR.
*/
static int
cache_init_from_cache(struct pam_args *args, const char *ccname,
krb5_ccache old, krb5_ccache *cache)
{
struct context *ctx;
krb5_creds creds;
krb5_cc_cursor cursor;
int pamret;
krb5_error_code status;
*cache = NULL;
memset(&creds, 0, sizeof(creds));
if (args == NULL || args->config == NULL || args->config->ctx == NULL
|| args->config->ctx->context == NULL)
return PAM_SERVICE_ERR;
if (old == NULL)
return PAM_SERVICE_ERR;
ctx = args->config->ctx;
status = krb5_cc_start_seq_get(ctx->context, old, &cursor);
if (status != 0) {
putil_err_krb5(args, status, "cannot open new credentials");
return PAM_SERVICE_ERR;
}
status = krb5_cc_next_cred(ctx->context, old, &cursor, &creds);
if (status != 0) {
putil_err_krb5(args, status, "cannot read new credentials");
pamret = PAM_SERVICE_ERR;
goto done;
}
pamret = pamk5_cache_init(args, ccname, &creds, cache);
if (pamret != PAM_SUCCESS) {
krb5_free_cred_contents(ctx->context, &creds);
pamret = PAM_SERVICE_ERR;
goto done;
}
krb5_free_cred_contents(ctx->context, &creds);
/*
* There probably won't be any additional credentials, but check for them
* and copy them just in case.
*/
while (krb5_cc_next_cred(ctx->context, old, &cursor, &creds) == 0) {
status = krb5_cc_store_cred(ctx->context, *cache, &creds);
krb5_free_cred_contents(ctx->context, &creds);
if (status != 0) {
putil_err_krb5(args, status,
"cannot store additional credentials"
" in %s",
ccname);
pamret = PAM_SERVICE_ERR;
goto done;
}
}
pamret = PAM_SUCCESS;
done:
krb5_cc_end_seq_get(ctx->context, ctx->cache, &cursor);
if (pamret != PAM_SUCCESS && *cache != NULL) {
krb5_cc_destroy(ctx->context, *cache);
*cache = NULL;
}
return pamret;
}
/*
* Determine the name of a new ticket cache. Handles ccache and ccache_dir
* PAM options and returns newly allocated memory.
*
* The ccache option, if set, contains a string with possible %u and %p
* escapes. The former is replaced by the UID and the latter is replaced by
* the PID (a suitable unique string).
*/
static char *
build_ccache_name(struct pam_args *args, uid_t uid)
{
char *cache_name = NULL;
int retval;
if (args->config->ccache == NULL) {
retval = asprintf(&cache_name, "%s/krb5cc_%d_XXXXXX",
args->config->ccache_dir, (int) uid);
if (retval < 0) {
putil_crit(args, "malloc failure: %s", strerror(errno));
return NULL;
}
} else {
size_t len = 0, delta;
char *p, *q;
for (p = args->config->ccache; *p != '\0'; p++) {
if (p[0] == '%' && p[1] == 'u') {
len += snprintf(NULL, 0, "%ld", (long) uid);
p++;
} else if (p[0] == '%' && p[1] == 'p') {
len += snprintf(NULL, 0, "%ld", (long) getpid());
p++;
} else {
len++;
}
}
len++;
cache_name = malloc(len);
if (cache_name == NULL) {
putil_crit(args, "malloc failure: %s", strerror(errno));
return NULL;
}
for (p = args->config->ccache, q = cache_name; *p != '\0'; p++) {
if (p[0] == '%' && p[1] == 'u') {
delta = snprintf(q, len, "%ld", (long) uid);
q += delta;
len -= delta;
p++;
} else if (p[0] == '%' && p[1] == 'p') {
delta = snprintf(q, len, "%ld", (long) getpid());
q += delta;
len -= delta;
p++;
} else {
*q = *p;
q++;
len--;
}
}
*q = '\0';
}
return cache_name;
}
/*
* Create a new context for a session if we've lost the context created during
* authentication (such as when running under OpenSSH). Return PAM_IGNORE if
* we're ignoring this user or if apparently our pam_authenticate never
* succeeded.
*/
static int
create_session_context(struct pam_args *args)
{
struct context *ctx = NULL;
PAM_CONST char *user;
const char *tmpname;
int status, pamret;
/* If we're going to ignore the user anyway, don't even bother. */
if (args->config->ignore_root || args->config->minimum_uid > 0) {
pamret = pam_get_user(args->pamh, &user, NULL);
if (pamret == PAM_SUCCESS && pamk5_should_ignore(args, user)) {
pamret = PAM_IGNORE;
goto fail;
}
}
/*
* Create the context and locate the temporary ticket cache. Load the
* ticket cache back into the context and flush out the other data that
* would have been set if we'd kept our original context.
*/
pamret = pamk5_context_new(args);
if (pamret != PAM_SUCCESS) {
putil_crit_pam(args, pamret, "creating session context failed");
goto fail;
}
ctx = args->config->ctx;
tmpname = pamk5_get_krb5ccname(args, "PAM_KRB5CCNAME");
if (tmpname == NULL) {
putil_debug(args, "unable to get PAM_KRB5CCNAME, assuming"
" non-Kerberos login");
pamret = PAM_IGNORE;
goto fail;
}
putil_debug(args, "found initial ticket cache at %s", tmpname);
status = krb5_cc_resolve(ctx->context, tmpname, &ctx->cache);
if (status != 0) {
putil_err_krb5(args, status, "cannot resolve cache %s", tmpname);
pamret = PAM_SERVICE_ERR;
goto fail;
}
status = krb5_cc_get_principal(ctx->context, ctx->cache, &ctx->princ);
if (status != 0) {
putil_err_krb5(args, status, "cannot retrieve principal");
pamret = PAM_SERVICE_ERR;
goto fail;
}
/*
* We've rebuilt the context. Push it back into the PAM state for any
* further calls to session or account management, which OpenSSH does keep
* the context for.
*/
pamret = pam_set_data(args->pamh, "pam_krb5", ctx, pamk5_context_destroy);
if (pamret != PAM_SUCCESS) {
putil_err_pam(args, pamret, "cannot set context data");
goto fail;
}
return PAM_SUCCESS;
fail:
pamk5_context_free(args);
return pamret;
}
/*
* Sets user credentials by creating the permanent ticket cache and setting
* the proper ownership. This function may be called by either pam_sm_setcred
* or pam_sm_open_session. The refresh flag should be set to true if we
* should reinitialize an existing ticket cache instead of creating a new one.
*/
int
pamk5_setcred(struct pam_args *args, bool refresh)
{
struct context *ctx = NULL;
krb5_ccache cache = NULL;
char *cache_name = NULL;
bool set_context = false;
int status = 0;
int pamret;
struct passwd *pw = NULL;
uid_t uid;
gid_t gid;
/* If configured not to create a cache, we have nothing to do. */
if (args->config->no_ccache) {
pamret = PAM_SUCCESS;
goto done;
}
/*
* If we weren't able to obtain a context, we were probably run by OpenSSH
* with its weird PAM handling, so we're going to cobble up a new context
* for ourselves.
*/
pamret = pamk5_context_fetch(args);
if (pamret != PAM_SUCCESS) {
putil_debug(args, "no context found, creating one");
pamret = create_session_context(args);
if (pamret != PAM_SUCCESS || args->config->ctx == NULL)
goto done;
set_context = true;
}
ctx = args->config->ctx;
/*
* Some programs (xdm, for instance) appear to call setcred over and over
* again, so avoid doing useless work.
*/
if (ctx->initialized) {
pamret = PAM_SUCCESS;
goto done;
}
/*
* Get the uid. The user is not required to be a local account for
* pam_authenticate, but for either pam_setcred (other than DELETE) or for
* pam_open_session, the user must be a local account.
*/
pw = pam_modutil_getpwnam(args->pamh, ctx->name);
if (pw == NULL) {
putil_err(args, "getpwnam failed for %s", ctx->name);
pamret = PAM_USER_UNKNOWN;
goto done;
}
uid = pw->pw_uid;
gid = pw->pw_gid;
/* Get the cache name. If reinitializing, this is our existing cache. */
if (refresh) {
const char *name, *k5name;
/*
* Solaris su calls pam_setcred as root with PAM_REINITIALIZE_CREDS,
* preserving the user-supplied environment. An xlock program may
* also do this if it's setuid root and doesn't drop credentials
* before calling pam_setcred.
*
* There isn't any safe way of reinitializing the exiting ticket cache
* for the user if we're setuid without calling setreuid(). Calling
* setreuid() is possible, but if the calling application is threaded,
* it will change credentials for the whole application, with possibly
* bizarre and unintended (and insecure) results. Trying to verify
* ownership of the existing ticket cache before using it fails under
* various race conditions (for example, having one of the elements of
* the path be a symlink and changing the target of that symlink
* between our check and the call to krb5_cc_resolve). Without
* calling setreuid(), we run the risk of replacing a file owned by
* another user with a credential cache.
*
* We could fail with an error in the setuid case, which would be
* maximally safe, but it would prevent use of the module for
* authentication with programs such as Solaris su. Failure to
* reinitialize the cache is normally not a serious problem, just a
* missing feature. We therefore log an error and exit with
* PAM_SUCCESS for the setuid case.
*
* We do not use issetugid here since it always returns true if setuid
* was was involved anywhere in the process of running the binary.
* This would prevent a setuid screensaver that drops permissions from
* refreshing a credential cache. The issetugid behavior is safer,
* since the environment should ideally not be trusted even if the
* binary completely changed users away from the original user, but in
* that case the binary needs to take some responsibility for either
* sanitizing the environment or being certain that the calling user
* is permitted to act as the target user.
*/
if (getuid() != geteuid() || getgid() != getegid()) {
putil_err(args, "credential reinitialization in a setuid context"
" ignored");
pamret = PAM_SUCCESS;
goto done;
}
name = pamk5_get_krb5ccname(args, "KRB5CCNAME");
if (name == NULL)
name = krb5_cc_default_name(ctx->context);
if (name == NULL) {
putil_err(args, "unable to get ticket cache name");
pamret = PAM_SERVICE_ERR;
goto done;
}
if (strncmp(name, "FILE:", strlen("FILE:")) == 0)
name += strlen("FILE:");
/*
* If the cache we have in the context and the cache we're
* reinitializing are the same cache, don't do anything; otherwise,
* we'll end up destroying the cache. This should never happen; this
* case triggering is a sign of a bug, probably in the calling
* application.
*/
if (ctx->cache != NULL) {
k5name = krb5_cc_get_name(ctx->context, ctx->cache);
if (k5name != NULL) {
if (strncmp(k5name, "FILE:", strlen("FILE:")) == 0)
k5name += strlen("FILE:");
if (strcmp(name, k5name) == 0) {
pamret = PAM_SUCCESS;
goto done;
}
}
}
cache_name = strdup(name);
if (cache_name == NULL) {
putil_crit(args, "malloc failure: %s", strerror(errno));
pamret = PAM_BUF_ERR;
goto done;
}
putil_debug(args, "refreshing ticket cache %s", cache_name);
/*
* If we're refreshing the cache, we didn't really create it and the
* user's open session created by login is probably still managing
* it. Thus, don't remove it when PAM is shut down.
*/
ctx->dont_destroy_cache = 1;
} else {
char *cache_name_tmp;
size_t len;
cache_name = build_ccache_name(args, uid);
if (cache_name == NULL) {
pamret = PAM_BUF_ERR;
goto done;
}
len = strlen(cache_name);
if (len > 6 && strncmp("XXXXXX", cache_name + len - 6, 6) == 0) {
if (strncmp(cache_name, "FILE:", strlen("FILE:")) == 0)
cache_name_tmp = cache_name + strlen("FILE:");
else
cache_name_tmp = cache_name;
pamret = pamk5_cache_mkstemp(args, cache_name_tmp);
if (pamret != PAM_SUCCESS)
goto done;
}
putil_debug(args, "initializing ticket cache %s", cache_name);
}
/*
* Initialize the new ticket cache and point the environment at it. Only
* chown the cache if the cache is of type FILE or has no type (making the
* assumption that the default cache type is FILE; otherwise, due to the
* type prefix, we'd end up with an invalid path.
*/
pamret = cache_init_from_cache(args, cache_name, ctx->cache, &cache);
if (pamret != PAM_SUCCESS)
goto done;
if (strncmp(cache_name, "FILE:", strlen("FILE:")) == 0)
status = chown(cache_name + strlen("FILE:"), uid, gid);
else if (strchr(cache_name, ':') == NULL)
status = chown(cache_name, uid, gid);
if (status == -1) {
putil_crit(args, "chown of ticket cache failed: %s", strerror(errno));
pamret = PAM_SERVICE_ERR;
goto done;
}
pamret = pamk5_set_krb5ccname(args, cache_name, "KRB5CCNAME");
if (pamret != PAM_SUCCESS) {
putil_crit(args, "setting KRB5CCNAME failed: %s", strerror(errno));
goto done;
}
/*
* If we had a temporary ticket cache, delete the environment variable so
* that we won't get confused and think we still have a temporary ticket
* cache when called again.
*
* FreeBSD PAM, at least as of 7.2, doesn't support deleting environment
* variables using the syntax supported by Solaris and Linux. Work
* around that by setting the variable to an empty value if deleting it
* fails.
*/
if (pam_getenv(args->pamh, "PAM_KRB5CCNAME") != NULL) {
pamret = pam_putenv(args->pamh, "PAM_KRB5CCNAME");
if (pamret != PAM_SUCCESS)
pamret = pam_putenv(args->pamh, "PAM_KRB5CCNAME=");
if (pamret != PAM_SUCCESS)
goto done;
}
/* Destroy the temporary cache and put the new cache in the context. */
krb5_cc_destroy(ctx->context, ctx->cache);
ctx->cache = cache;
cache = NULL;
ctx->initialized = 1;
if (args->config->retain_after_close)
ctx->dont_destroy_cache = 1;
done:
if (ctx != NULL && cache != NULL)
krb5_cc_destroy(ctx->context, cache);
free(cache_name);
/* If we stored our Kerberos context in PAM data, don't free it. */
if (set_context)
args->ctx = NULL;
return pamret;
}
+141
View File
@@ -0,0 +1,141 @@
/*
* Support functions for pam-krb5.
*
* Some general utility functions used by multiple PAM groups that aren't
* associated with any particular chunk of functionality.
*
* Copyright 2005-2007, 2009, 2020 Russ Allbery <eagle@eyrie.org>
* Copyright 2011-2012
* The Board of Trustees of the Leland Stanford Junior University
* Copyright 2005 Andres Salomon <dilinger@debian.org>
* Copyright 1999-2000 Frank Cusack <fcusack@fcusack.com>
*
* SPDX-License-Identifier: BSD-3-clause or GPL-1+
*/
#include <config.h>
#include <portable/krb5.h>
#include <portable/pam.h>
#include <portable/system.h>
#include <errno.h>
#include <pwd.h>
#include <module/internal.h>
#include <pam-util/args.h>
#include <pam-util/logging.h>
/*
* Given the PAM arguments and the user we're authenticating, see if we should
* ignore that user because they're root or have a low-numbered UID and we
* were configured to ignore such users. Returns true if we should ignore
* them, false otherwise. Ignores any fully-qualified principal names.
*/
int
pamk5_should_ignore(struct pam_args *args, PAM_CONST char *username)
{
struct passwd *pwd;
if (args->config->ignore_root && strcmp("root", username) == 0) {
putil_debug(args, "ignoring root user");
return 1;
}
if (args->config->minimum_uid > 0 && strchr(username, '@') == NULL) {
pwd = pam_modutil_getpwnam(args->pamh, username);
if (pwd != NULL && pwd->pw_uid < (uid_t) args->config->minimum_uid) {
putil_debug(args, "ignoring low-UID user (%lu < %ld)",
(unsigned long) pwd->pw_uid,
args->config->minimum_uid);
return 1;
}
}
return 0;
}
/*
* Verify the user authorization. Call krb5_kuserok if this is a local
* account, or do the krb5_aname_to_localname verification if ignore_k5login
* was requested. For non-local accounts, the principal must match the
* authentication identity.
*/
int
pamk5_authorized(struct pam_args *args)
{
struct context *ctx;
krb5_context c;
krb5_error_code retval;
int status;
struct passwd *pwd;
char kuser[65]; /* MAX_USERNAME == 65 (MIT Kerberos 1.4.1). */
if (args == NULL || args->config == NULL || args->config->ctx == NULL
|| args->config->ctx->context == NULL)
return PAM_SERVICE_ERR;
ctx = args->config->ctx;
if (ctx->name == NULL)
return PAM_SERVICE_ERR;
c = ctx->context;
/*
* If alt_auth_map was set, authorize the user if the authenticated
* principal matches the mapped principal. alt_auth_map essentially
* serves as a supplemental .k5login. PAM_SERVICE_ERR indicates fatal
* errors that should abort remaining processing; PAM_AUTH_ERR indicates
* that it just didn't match, in which case we continue to try other
* authorization methods.
*/
if (args->config->alt_auth_map != NULL) {
status = pamk5_alt_auth_verify(args);
if (status == PAM_SUCCESS || status == PAM_SERVICE_ERR)
return status;
}
/*
* If the name to which we're authenticating contains @ (is fully
* qualified), it must match the principal exactly.
*/
if (strchr(ctx->name, '@') != NULL) {
char *principal;
retval = krb5_unparse_name(c, ctx->princ, &principal);
if (retval != 0) {
putil_err_krb5(args, retval, "krb5_unparse_name failed");
return PAM_SERVICE_ERR;
}
if (strcmp(principal, ctx->name) != 0) {
putil_err(args, "user %s does not match principal %s", ctx->name,
principal);
krb5_free_unparsed_name(c, principal);
return PAM_AUTH_ERR;
}
krb5_free_unparsed_name(c, principal);
return PAM_SUCCESS;
}
/*
* Otherwise, apply either krb5_aname_to_localname or krb5_kuserok
* depending on the situation.
*/
pwd = pam_modutil_getpwnam(args->pamh, ctx->name);
if (args->config->ignore_k5login || pwd == NULL) {
retval = krb5_aname_to_localname(c, ctx->princ, sizeof(kuser), kuser);
if (retval != 0) {
putil_err_krb5(args, retval, "cannot convert principal to user");
return PAM_AUTH_ERR;
}
if (strcmp(kuser, ctx->name) != 0) {
putil_err(args, "user %s does not match local name %s", ctx->name,
kuser);
return PAM_AUTH_ERR;
}
} else {
if (!krb5_kuserok(c, ctx->princ, ctx->name)) {
putil_err(args, "krb5_kuserok for user %s failed", ctx->name);
return PAM_AUTH_ERR;
}
}
return PAM_SUCCESS;
}
+105
View File
@@ -0,0 +1,105 @@
/*
* Constructor and destructor for PAM data.
*
* The PAM utility functions often need an initial argument that encapsulates
* the PAM handle, some configuration information, and possibly a Kerberos
* context. This implements a constructor and destructor for that data
* structure.
*
* The individual PAM modules should provide a definition of the pam_config
* struct appropriate to that module. None of the PAM utility functions need
* to know what that configuration struct looks like, and it must be freed
* before calling putil_args_free().
*
* The canonical version of this file is maintained in the rra-c-util package,
* which can be found at <https://www.eyrie.org/~eagle/software/rra-c-util/>.
*
* Written by Russ Allbery <eagle@eyrie.org>
* Copyright 2010, 2012-2014
* The Board of Trustees of the Leland Stanford Junior University
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*
* SPDX-License-Identifier: MIT
*/
#include <config.h>
#ifdef HAVE_KRB5
# include <portable/krb5.h>
#endif
#include <portable/pam.h>
#include <portable/system.h>
#include <errno.h>
#include <pam-util/args.h>
#include <pam-util/logging.h>
/*
* Allocate a new pam_args struct and return it, or NULL on memory allocation
* or Kerberos initialization failure. If HAVE_KRB5 is defined, we also
* allocate a Kerberos context.
*/
struct pam_args *
putil_args_new(pam_handle_t *pamh, int flags)
{
struct pam_args *args;
#ifdef HAVE_KRB5
krb5_error_code status;
#endif
args = calloc(1, sizeof(struct pam_args));
if (args == NULL) {
putil_crit(NULL, "cannot allocate memory: %s", strerror(errno));
return NULL;
}
args->pamh = pamh;
args->silent = ((flags & PAM_SILENT) == PAM_SILENT);
#ifdef HAVE_KRB5
if (issetugid())
status = krb5_init_secure_context(&args->ctx);
else
status = krb5_init_context(&args->ctx);
if (status != 0) {
putil_err_krb5(args, status, "cannot create Kerberos context");
free(args);
return NULL;
}
#endif /* HAVE_KRB5 */
return args;
}
/*
* Free a pam_args struct. The config member must be freed separately.
*/
void
putil_args_free(struct pam_args *args)
{
if (args == NULL)
return;
#ifdef HAVE_KRB5
free(args->realm);
if (args->ctx != NULL)
krb5_free_context(args->ctx);
#endif
free(args);
}
+84
View File
@@ -0,0 +1,84 @@
/*
* Standard structure for PAM data.
*
* The PAM utility functions often need an initial argument that encapsulates
* the PAM handle, some configuration information, and possibly a Kerberos
* context. This header provides a standard structure definition.
*
* The individual PAM modules should provide a definition of the pam_config
* struct appropriate to that module. None of the PAM utility functions need
* to know what that configuration struct looks like.
*
* The canonical version of this file is maintained in the rra-c-util package,
* which can be found at <https://www.eyrie.org/~eagle/software/rra-c-util/>.
*
* Written by Russ Allbery <eagle@eyrie.org>
* Copyright 2010, 2013
* The Board of Trustees of the Leland Stanford Junior University
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*
* SPDX-License-Identifier: MIT
*/
#ifndef PAM_UTIL_ARGS_H
#define PAM_UTIL_ARGS_H 1
#include <config.h>
#ifdef HAVE_KRB5
# include <portable/krb5.h>
#endif
#include <portable/pam.h>
#include <portable/stdbool.h>
/* Opaque struct from the PAM utility perspective. */
struct pam_config;
struct pam_args {
pam_handle_t *pamh; /* Pointer back to the PAM handle. */
struct pam_config *config; /* Per-module PAM configuration. */
bool debug; /* Log debugging information. */
bool silent; /* Do not pass text to the application. */
const char *user; /* User being authenticated. */
#ifdef HAVE_KRB5
krb5_context ctx; /* Context for Kerberos operations. */
char *realm; /* Kerberos realm for configuration. */
#endif
};
BEGIN_DECLS
/* Default to a hidden visibility for all internal functions. */
#pragma GCC visibility push(hidden)
/*
* Allocate and free the pam_args struct. We assume that user is a pointer to
* a string maintained elsewhere and don't free it here. config must be freed
* separately by the caller.
*/
struct pam_args *putil_args_new(pam_handle_t *, int flags);
void putil_args_free(struct pam_args *);
/* Undo default visibility change. */
#pragma GCC visibility pop
END_DECLS
#endif /* !PAM_UTIL_ARGS_H */
+345
View File
@@ -0,0 +1,345 @@
/*
* Logging functions for PAM modules.
*
* Logs errors and debugging messages from PAM modules. The debug versions
* only log anything if debugging was enabled; the crit and err versions
* always log.
*
* The canonical version of this file is maintained in the rra-c-util package,
* which can be found at <https://www.eyrie.org/~eagle/software/rra-c-util/>.
*
* Written by Russ Allbery <eagle@eyrie.org>
* Copyright 2015, 2018, 2020 Russ Allbery <eagle@eyrie.org>
* Copyright 2005-2007, 2009-2010, 2012-2013
* The Board of Trustees of the Leland Stanford Junior University
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*
* SPDX-License-Identifier: MIT
*/
#include <config.h>
#ifdef HAVE_KRB5
# include <portable/krb5.h>
#endif
#include <portable/pam.h>
#include <portable/system.h>
#include <syslog.h>
#include <pam-util/args.h>
#include <pam-util/logging.h>
#ifndef LOG_AUTHPRIV
# define LOG_AUTHPRIV LOG_AUTH
#endif
/* Used for iterating through arrays. */
#define ARRAY_SIZE(array) (sizeof(array) / sizeof((array)[0]))
/*
* Mappings of PAM flags to symbolic names for logging when entering a PAM
* module function.
*/
static const struct {
int flag;
const char *name;
} FLAGS[] = {
/* clang-format off */
{PAM_CHANGE_EXPIRED_AUTHTOK, "expired" },
{PAM_DELETE_CRED, "delete" },
{PAM_DISALLOW_NULL_AUTHTOK, "nonull" },
{PAM_ESTABLISH_CRED, "establish"},
{PAM_PRELIM_CHECK, "prelim" },
{PAM_REFRESH_CRED, "refresh" },
{PAM_REINITIALIZE_CRED, "reinit" },
{PAM_SILENT, "silent" },
{PAM_UPDATE_AUTHTOK, "update" },
/* clang-format on */
};
/*
* Utility function to format a message into newly allocated memory, reporting
* an error via syslog if vasprintf fails.
*/
static char *__attribute__((__format__(printf, 1, 0)))
format(const char *fmt, va_list args)
{
char *msg;
if (vasprintf(&msg, fmt, args) < 0) {
syslog(LOG_CRIT | LOG_AUTHPRIV, "vasprintf failed: %m");
return NULL;
}
return msg;
}
/*
* Log wrapper function that adds the user. Log a message with the given
* priority, prefixed by (user <user>) with the account name being
* authenticated if known.
*/
static void __attribute__((__format__(printf, 3, 0)))
log_vplain(struct pam_args *pargs, int priority, const char *fmt, va_list args)
{
char *msg;
if (priority == LOG_DEBUG && (pargs == NULL || !pargs->debug))
return;
if (pargs != NULL && pargs->user != NULL) {
msg = format(fmt, args);
if (msg == NULL)
return;
pam_syslog(pargs->pamh, priority, "(user %s) %s", pargs->user, msg);
free(msg);
} else if (pargs != NULL) {
pam_vsyslog(pargs->pamh, priority, fmt, args);
} else {
msg = format(fmt, args);
if (msg == NULL)
return;
syslog(priority | LOG_AUTHPRIV, "%s", msg);
free(msg);
}
}
/*
* Wrapper around log_vplain with variadic arguments.
*/
static void __attribute__((__format__(printf, 3, 4)))
log_plain(struct pam_args *pargs, int priority, const char *fmt, ...)
{
va_list args;
va_start(args, fmt);
log_vplain(pargs, priority, fmt, args);
va_end(args);
}
/*
* Log wrapper function for reporting a PAM error. Log a message with the
* given priority, prefixed by (user <user>) with the account name being
* authenticated if known, followed by a colon and the formatted PAM error.
* However, do not include the colon and the PAM error if the PAM status is
* PAM_SUCCESS.
*/
static void __attribute__((__format__(printf, 4, 0)))
log_pam(struct pam_args *pargs, int priority, int status, const char *fmt,
va_list args)
{
char *msg;
if (priority == LOG_DEBUG && (pargs == NULL || !pargs->debug))
return;
msg = format(fmt, args);
if (msg == NULL)
return;
if (pargs == NULL)
log_plain(NULL, priority, "%s", msg);
else if (status == PAM_SUCCESS)
log_plain(pargs, priority, "%s", msg);
else
log_plain(pargs, priority, "%s: %s", msg,
pam_strerror(pargs->pamh, status));
free(msg);
}
/*
* The public interfaces. For each common log level (crit, err, and debug),
* generate a putil_<level> function and one for _pam. Do this with the
* preprocessor to save duplicate code.
*/
/* clang-format off */
#define LOG_FUNCTION(level, priority) \
void __attribute__((__format__(printf, 2, 3))) \
putil_ ## level(struct pam_args *pargs, const char *fmt, ...) \
{ \
va_list args; \
\
va_start(args, fmt); \
log_vplain(pargs, priority, fmt, args); \
va_end(args); \
} \
void __attribute__((__format__(printf, 3, 4))) \
putil_ ## level ## _pam(struct pam_args *pargs, int status, \
const char *fmt, ...) \
{ \
va_list args; \
\
va_start(args, fmt); \
log_pam(pargs, priority, status, fmt, args); \
va_end(args); \
}
LOG_FUNCTION(crit, LOG_CRIT)
LOG_FUNCTION(err, LOG_ERR)
LOG_FUNCTION(notice, LOG_NOTICE)
LOG_FUNCTION(debug, LOG_DEBUG)
/* clang-format on */
/*
* Report entry into a function. Takes the PAM arguments, the function name,
* and the flags and maps the flags to symbolic names.
*/
void
putil_log_entry(struct pam_args *pargs, const char *func, int flags)
{
size_t i, length, offset;
char *out = NULL, *nout;
if (!pargs->debug)
return;
if (flags != 0)
for (i = 0; i < ARRAY_SIZE(FLAGS); i++) {
if (!(flags & FLAGS[i].flag))
continue;
if (out == NULL) {
out = strdup(FLAGS[i].name);
if (out == NULL)
break;
} else {
length = strlen(FLAGS[i].name);
nout = realloc(out, strlen(out) + length + 2);
if (nout == NULL) {
free(out);
out = NULL;
break;
}
out = nout;
offset = strlen(out);
out[offset] = '|';
memcpy(out + offset + 1, FLAGS[i].name, length);
out[offset + 1 + length] = '\0';
}
}
if (out == NULL)
pam_syslog(pargs->pamh, LOG_DEBUG, "%s: entry", func);
else {
pam_syslog(pargs->pamh, LOG_DEBUG, "%s: entry (%s)", func, out);
free(out);
}
}
/*
* Report an authentication failure. This is a separate function since we
* want to include various PAM metadata in the log message and put it in a
* standard format. The format here is modeled after the pam_unix
* authentication failure message from Linux PAM.
*/
void __attribute__((__format__(printf, 2, 3)))
putil_log_failure(struct pam_args *pargs, const char *fmt, ...)
{
char *msg;
va_list args;
const char *ruser = NULL;
const char *rhost = NULL;
const char *tty = NULL;
const char *name = NULL;
if (pargs->user != NULL)
name = pargs->user;
va_start(args, fmt);
msg = format(fmt, args);
va_end(args);
if (msg == NULL)
return;
pam_get_item(pargs->pamh, PAM_RUSER, (PAM_CONST void **) &ruser);
pam_get_item(pargs->pamh, PAM_RHOST, (PAM_CONST void **) &rhost);
pam_get_item(pargs->pamh, PAM_TTY, (PAM_CONST void **) &tty);
/* clang-format off */
pam_syslog(pargs->pamh, LOG_NOTICE, "%s; logname=%s uid=%ld euid=%ld"
" tty=%s ruser=%s rhost=%s", msg,
(name != NULL) ? name : "",
(long) getuid(), (long) geteuid(),
(tty != NULL) ? tty : "",
(ruser != NULL) ? ruser : "",
(rhost != NULL) ? rhost : "");
/* clang-format on */
free(msg);
}
/*
* Below are the additional logging functions enabled if built with Kerberos
* support, used to report Kerberos errors.
*/
#ifdef HAVE_KRB5
/*
* Log wrapper function for reporting a Kerberos error. Log a message with
* the given priority, prefixed by (user <user>) with the account name being
* authenticated if known, followed by a colon and the formatted Kerberos
* error.
*/
__attribute__((__format__(printf, 4, 0))) static void
log_krb5(struct pam_args *pargs, int priority, int status, const char *fmt,
va_list args)
{
char *msg;
const char *k5_msg = NULL;
if (priority == LOG_DEBUG && (pargs == NULL || !pargs->debug))
return;
msg = format(fmt, args);
if (msg == NULL)
return;
if (pargs != NULL && pargs->ctx != NULL) {
k5_msg = krb5_get_error_message(pargs->ctx, status);
log_plain(pargs, priority, "%s: %s", msg, k5_msg);
} else {
log_plain(pargs, priority, "%s", msg);
}
free(msg);
if (k5_msg != NULL)
krb5_free_error_message(pargs->ctx, k5_msg);
}
/*
* The public interfaces. Do this with the preprocessor to save duplicate
* code.
*/
/* clang-format off */
#define LOG_FUNCTION_KRB5(level, priority) \
void __attribute__((__format__(printf, 3, 4))) \
putil_ ## level ## _krb5(struct pam_args *pargs, int status, \
const char *fmt, ...) \
{ \
va_list args; \
\
va_start(args, fmt); \
log_krb5(pargs, priority, status, fmt, args); \
va_end(args); \
}
LOG_FUNCTION_KRB5(crit, LOG_CRIT)
LOG_FUNCTION_KRB5(err, LOG_ERR)
LOG_FUNCTION_KRB5(notice, LOG_NOTICE)
LOG_FUNCTION_KRB5(debug, LOG_DEBUG)
/* clang-format on */
#endif /* HAVE_KRB5 */
+131
View File
@@ -0,0 +1,131 @@
/*
* Interface to standard PAM logging.
*
* The canonical version of this file is maintained in the rra-c-util package,
* which can be found at <https://www.eyrie.org/~eagle/software/rra-c-util/>.
*
* Written by Russ Allbery <eagle@eyrie.org>
* Copyright 2020 Russ Allbery <eagle@eyrie.org>
* Copyright 2006-2010, 2012-2013
* The Board of Trustees of the Leland Stanford Junior University
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*
* SPDX-License-Identifier: MIT
*/
#ifndef PAM_UTIL_LOGGING_H
#define PAM_UTIL_LOGGING_H 1
#include <config.h>
#include <portable/macros.h>
#ifdef HAVE_KRB5
# include <portable/krb5.h>
#endif
#include <portable/pam.h>
#include <stddef.h>
#include <syslog.h>
/* Forward declarations to avoid extra includes. */
struct pam_args;
BEGIN_DECLS
/* Default to a hidden visibility for all internal functions. */
#pragma GCC visibility push(hidden)
/*
* Error reporting and debugging functions. For each log level, there are two
* functions. The _log function just prints out the message it's given. The
* _log_pam function does the same but appends the pam_strerror results for
* the provided status code if it is not PAM_SUCCESS.
*/
void putil_crit(struct pam_args *, const char *, ...)
__attribute__((__format__(printf, 2, 3)));
void putil_crit_pam(struct pam_args *, int, const char *, ...)
__attribute__((__format__(printf, 3, 4)));
void putil_err(struct pam_args *, const char *, ...)
__attribute__((__format__(printf, 2, 3)));
void putil_err_pam(struct pam_args *, int, const char *, ...)
__attribute__((__format__(printf, 3, 4)));
void putil_notice(struct pam_args *, const char *, ...)
__attribute__((__format__(printf, 2, 3)));
void putil_notice_pam(struct pam_args *, int, const char *, ...)
__attribute__((__format__(printf, 3, 4)));
void putil_debug(struct pam_args *, const char *, ...)
__attribute__((__format__(printf, 2, 3)));
void putil_debug_pam(struct pam_args *, int, const char *, ...)
__attribute__((__format__(printf, 3, 4)));
/*
* The Kerberos versions of the PAM logging and debugging functions, which
* report the last Kerberos error. These are only available if built with
* Kerberos support.
*/
#ifdef HAVE_KRB5
void putil_crit_krb5(struct pam_args *, int, const char *, ...)
__attribute__((__format__(printf, 3, 4)));
void putil_err_krb5(struct pam_args *, int, const char *, ...)
__attribute__((__format__(printf, 3, 4)));
void putil_notice_krb5(struct pam_args *, int, const char *, ...)
__attribute__((__format__(printf, 3, 4)));
void putil_debug_krb5(struct pam_args *, int, const char *, ...)
__attribute__((__format__(printf, 3, 4)));
#endif
/* Log entry to a PAM function. */
void putil_log_entry(struct pam_args *, const char *, int flags)
__attribute__((__nonnull__));
/* Log an authentication failure. */
void putil_log_failure(struct pam_args *, const char *, ...)
__attribute__((__nonnull__, __format__(printf, 2, 3)));
/* Undo default visibility change. */
#pragma GCC visibility pop
END_DECLS
/* __func__ is C99, but not provided by all implementations. */
#if (__STDC_VERSION__ < 199901L) && !defined(__func__)
# if (__GNUC__ >= 2)
# define __func__ __FUNCTION__
# else
# define __func__ "<unknown>"
# endif
#endif
/* Macros to record entry and exit from the main PAM functions. */
#define ENTRY(args, flags) \
do { \
if (args->debug) \
putil_log_entry((args), __func__, (flags)); \
} while (0)
#define EXIT(args, pamret) \
do { \
if (args != NULL && args->debug) \
pam_syslog( \
(args)->pamh, LOG_DEBUG, "%s: exit (%s)", __func__, \
((pamret) == PAM_SUCCESS) \
? "success" \
: (((pamret) == PAM_IGNORE) ? "ignore" : "failure")); \
} while (0)
#endif /* !PAM_UTIL_LOGGING_H */
+720
View File
@@ -0,0 +1,720 @@
/*
* Parse PAM options into a struct.
*
* Given a struct in which to store options and a specification for what
* options go where, parse both the PAM configuration options and any options
* from a Kerberos krb5.conf file and fill out the struct.
*
* The canonical version of this file is maintained in the rra-c-util package,
* which can be found at <https://www.eyrie.org/~eagle/software/rra-c-util/>.
*
* Written by Russ Allbery <eagle@eyrie.org>
* Copyright 2020 Russ Allbery <eagle@eyrie.org>
* Copyright 2006-2008, 2010-2011, 2013-2014
* The Board of Trustees of the Leland Stanford Junior University
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*
* SPDX-License-Identifier: MIT
*/
#include <config.h>
#ifdef HAVE_KRB5
# include <portable/krb5.h>
#endif
#include <portable/system.h>
#include <errno.h>
#include <pam-util/args.h>
#include <pam-util/logging.h>
#include <pam-util/options.h>
#include <pam-util/vector.h>
/* Used for unused parameters to silence gcc warnings. */
#define UNUSED __attribute__((__unused__))
/*
* Macros used to resolve a void * pointer to the configuration struct and an
* offset into a pointer to the appropriate type. Scary violations of the C
* type system lurk here.
*/
/* clang-format off */
#define CONF_BOOL(c, o) (bool *) (void *)((char *) (c) + (o))
#define CONF_NUMBER(c, o) (long *) (void *)((char *) (c) + (o))
#define CONF_STRING(c, o) (char **) (void *)((char *) (c) + (o))
#define CONF_LIST(c, o) (struct vector **)(void *)((char *) (c) + (o))
/* clang-format on */
/*
* We can only process times properly if we have Kerberos. If not, they fall
* back to longs and we convert them as numbers.
*/
/* clang-format off */
#ifdef HAVE_KRB5
# define CONF_TIME(c, o) (krb5_deltat *)(void *)((char *) (c) + (o))
#else
# define CONF_TIME(c, o) (long *) (void *)((char *) (c) + (o))
#endif
/* clang-format on */
/*
* Set a vector argument to its default. This needs to do a deep copy of the
* vector so that we can safely free it when freeing the configuration. Takes
* the PAM argument struct, the pointer in which to store the vector, and the
* default vector. Returns true if the default was set correctly and false on
* memory allocation failure, which is also reported with putil_crit().
*/
static bool
copy_default_list(struct pam_args *args, struct vector **setting,
const struct vector *defval)
{
struct vector *result = NULL;
*setting = NULL;
if (defval != NULL && defval->strings != NULL) {
result = vector_copy(defval);
if (result == NULL) {
putil_crit(args, "cannot allocate memory: %s", strerror(errno));
return false;
}
*setting = result;
}
return true;
}
/*
* Set a vector argument to a default based on a string. Takes the PAM
* argument struct,t he pointer into which to store the vector, and the
* default string. Returns true if the default was set correctly and false on
* memory allocation failure, which is also reported with putil_crit().
*/
static bool
default_list_string(struct pam_args *args, struct vector **setting,
const char *defval)
{
struct vector *result = NULL;
*setting = NULL;
if (defval != NULL) {
result = vector_split_multi(defval, " \t,", NULL);
if (result == NULL) {
putil_crit(args, "cannot allocate memory: %s", strerror(errno));
return false;
}
*setting = result;
}
return true;
}
/*
* Set the defaults for the PAM configuration. Takes the PAM arguments, an
* option table defined as above, and the number of entries in the table. The
* config member of the args struct must already be allocated. Returns true
* on success and false on error (generally out of memory). Errors will
* already be reported using putil_crit().
*
* This function must be called before either putil_args_krb5() or
* putil_args_parse(), since neither of those functions set defaults.
*/
bool
putil_args_defaults(struct pam_args *args, const struct option options[],
size_t optlen)
{
size_t opt;
for (opt = 0; opt < optlen; opt++) {
bool *bp;
long *lp;
#ifdef HAVE_KRB5
krb5_deltat *tp;
#else
long *tp;
#endif
char **sp;
struct vector **vp;
switch (options[opt].type) {
case TYPE_BOOLEAN:
bp = CONF_BOOL(args->config, options[opt].location);
*bp = options[opt].defaults.boolean;
break;
case TYPE_NUMBER:
lp = CONF_NUMBER(args->config, options[opt].location);
*lp = options[opt].defaults.number;
break;
case TYPE_TIME:
tp = CONF_TIME(args->config, options[opt].location);
*tp = (krb5_deltat) options[opt].defaults.number;
break;
case TYPE_STRING:
sp = CONF_STRING(args->config, options[opt].location);
if (options[opt].defaults.string == NULL)
*sp = NULL;
else {
*sp = strdup(options[opt].defaults.string);
if (*sp == NULL) {
putil_crit(args, "cannot allocate memory: %s",
strerror(errno));
return false;
}
}
break;
case TYPE_LIST:
vp = CONF_LIST(args->config, options[opt].location);
if (!copy_default_list(args, vp, options[opt].defaults.list))
return false;
break;
case TYPE_STRLIST:
vp = CONF_LIST(args->config, options[opt].location);
if (!default_list_string(args, vp, options[opt].defaults.string))
return false;
break;
}
}
return true;
}
#ifdef HAVE_KRB5
/*
* Load a boolean option from Kerberos appdefaults. Takes the PAM argument
* struct, the section name, the realm, the option, and the result location.
*
* The stupidity of rewriting the realm argument into a krb5_data is required
* by MIT Kerberos.
*/
static void
default_boolean(struct pam_args *args, const char *section, const char *realm,
const char *opt, bool *result)
{
int tmp;
# ifdef HAVE_KRB5_REALM
krb5_const_realm rdata = realm;
# else
krb5_data realm_struct;
const krb5_data *rdata;
if (realm == NULL)
rdata = NULL;
else {
rdata = &realm_struct;
realm_struct.magic = KV5M_DATA;
realm_struct.data = (void *) realm;
realm_struct.length = (unsigned int) strlen(realm);
}
# endif
/*
* The MIT version of krb5_appdefault_boolean takes an int * and the
* Heimdal version takes a krb5_boolean *, so hope that Heimdal always
* defines krb5_boolean to int or this will require more portability work.
*/
krb5_appdefault_boolean(args->ctx, section, rdata, opt, *result, &tmp);
*result = tmp;
}
/*
* Load a number option from Kerberos appdefaults. Takes the PAM argument
* struct, the section name, the realm, the option, and the result location.
* The native interface doesn't support numbers, so we actually read a string
* and then convert.
*/
static void
default_number(struct pam_args *args, const char *section, const char *realm,
const char *opt, long *result)
{
char *tmp = NULL;
char *end;
long value;
# ifdef HAVE_KRB5_REALM
krb5_const_realm rdata = realm;
# else
krb5_data realm_struct;
const krb5_data *rdata;
if (realm == NULL)
rdata = NULL;
else {
rdata = &realm_struct;
realm_struct.magic = KV5M_DATA;
realm_struct.data = (void *) realm;
realm_struct.length = (unsigned int) strlen(realm);
}
# endif
krb5_appdefault_string(args->ctx, section, rdata, opt, "", &tmp);
if (tmp != NULL && tmp[0] != '\0') {
errno = 0;
value = strtol(tmp, &end, 10);
if (errno != 0 || *end != '\0')
putil_err(args, "invalid number in krb5.conf setting for %s: %s",
opt, tmp);
else
*result = value;
}
free(tmp);
}
/*
* Load a time option from Kerberos appdefaults. Takes the PAM argument
* struct, the section name, the realm, the option, and the result location.
* The native interface doesn't support numbers, so we actually read a string
* and then convert using krb5_string_to_deltat.
*/
static void
default_time(struct pam_args *args, const char *section, const char *realm,
const char *opt, krb5_deltat *result)
{
char *tmp = NULL;
krb5_deltat value;
krb5_error_code retval;
# ifdef HAVE_KRB5_REALM
krb5_const_realm rdata = realm;
# else
krb5_data realm_struct;
const krb5_data *rdata;
if (realm == NULL)
rdata = NULL;
else {
rdata = &realm_struct;
realm_struct.magic = KV5M_DATA;
realm_struct.data = (void *) realm;
realm_struct.length = (unsigned int) strlen(realm);
}
# endif
krb5_appdefault_string(args->ctx, section, rdata, opt, "", &tmp);
if (tmp != NULL && tmp[0] != '\0') {
retval = krb5_string_to_deltat(tmp, &value);
if (retval != 0)
putil_err(args, "invalid time in krb5.conf setting for %s: %s",
opt, tmp);
else
*result = value;
}
free(tmp);
}
/*
* Load a string option from Kerberos appdefaults. Takes the PAM argument
* struct, the section name, the realm, the option, and the result location.
*
* This requires an annoying workaround because one cannot specify a default
* value of NULL with MIT Kerberos, since MIT Kerberos unconditionally calls
* strdup on the default value. There's also no way to determine if memory
* allocation failed while parsing or while setting the default value, so we
* don't return an error code.
*/
static void
default_string(struct pam_args *args, const char *section, const char *realm,
const char *opt, char **result)
{
char *value = NULL;
# ifdef HAVE_KRB5_REALM
krb5_const_realm rdata = realm;
# else
krb5_data realm_struct;
const krb5_data *rdata;
if (realm == NULL)
rdata = NULL;
else {
rdata = &realm_struct;
realm_struct.magic = KV5M_DATA;
realm_struct.data = (void *) realm;
realm_struct.length = (unsigned int) strlen(realm);
}
# endif
krb5_appdefault_string(args->ctx, section, rdata, opt, "", &value);
if (value != NULL) {
if (value[0] == '\0')
free(value);
else {
if (*result != NULL)
free(*result);
*result = value;
}
}
}
/*
* Load a list option from Kerberos appdefaults. Takes the PAM arguments, the
* context, the section name, the realm, the option, and the result location.
*
* We may fail here due to memory allocation problems, in which case we return
* false to indicate that PAM setup should abort.
*/
static bool
default_list(struct pam_args *args, const char *section, const char *realm,
const char *opt, struct vector **result)
{
char *tmp = NULL;
struct vector *value;
default_string(args, section, realm, opt, &tmp);
if (tmp != NULL) {
value = vector_split_multi(tmp, " \t,", NULL);
if (value == NULL) {
free(tmp);
putil_crit(args, "cannot allocate vector: %s", strerror(errno));
return false;
}
if (*result != NULL)
vector_free(*result);
*result = value;
free(tmp);
}
return true;
}
/*
* The public interface for getting configuration information from krb5.conf.
* Takes the PAM arguments, the krb5.conf section, the options specification,
* and the number of options in the options table. The config member of the
* args struct must already be allocated. Iterate through the option list
* and, for every option where krb5_config is true, see if it's set in the
* Kerberos configuration.
*
* This looks obviously slow, but there haven't been any reports of problems
* and there's no better interface. But if you wonder where the cycles in
* your computer are getting wasted, well, here's one place.
*/
bool
putil_args_krb5(struct pam_args *args, const char *section,
const struct option options[], size_t optlen)
{
size_t i;
char *realm;
bool free_realm = false;
/* Having no local realm may be intentional, so don't report an error. */
if (args->realm != NULL)
realm = args->realm;
else {
if (krb5_get_default_realm(args->ctx, &realm) < 0)
realm = NULL;
else
free_realm = true;
}
for (i = 0; i < optlen; i++) {
const struct option *opt = &options[i];
if (!opt->krb5_config)
continue;
switch (opt->type) {
case TYPE_BOOLEAN:
default_boolean(args, section, realm, opt->name,
CONF_BOOL(args->config, opt->location));
break;
case TYPE_NUMBER:
default_number(args, section, realm, opt->name,
CONF_NUMBER(args->config, opt->location));
break;
case TYPE_TIME:
default_time(args, section, realm, opt->name,
CONF_TIME(args->config, opt->location));
break;
case TYPE_STRING:
default_string(args, section, realm, opt->name,
CONF_STRING(args->config, opt->location));
break;
case TYPE_LIST:
case TYPE_STRLIST:
if (!default_list(args, section, realm, opt->name,
CONF_LIST(args->config, opt->location)))
return false;
break;
}
}
if (free_realm)
krb5_free_default_realm(args->ctx, realm);
return true;
}
#else /* !HAVE_KRB5 */
/*
* Stub function for getting configuration information from krb5.conf used
* when the PAM module is not built with Kerberos support so that the function
* can be called unconditionally.
*/
bool
putil_args_krb5(struct pam_args *args UNUSED, const char *section UNUSED,
const struct option options[] UNUSED, size_t optlen UNUSED)
{
return true;
}
#endif /* !HAVE_KRB5 */
/*
* bsearch comparison function for finding PAM arguments in an array of struct
* options. We only compare up to the first '=' in the key so that we don't
* have to munge the string before searching.
*/
static int
option_compare(const void *key, const void *member)
{
const char *string = key;
const struct option *option = member;
const char *p;
size_t length;
int result;
p = strchr(string, '=');
if (p == NULL)
return strcmp(string, option->name);
else {
length = (size_t)(p - string);
if (length == 0)
return -1;
result = strncmp(string, option->name, length);
if (result == 0 && strlen(option->name) > length)
return -1;
return result;
}
}
/*
* Given a PAM argument, convert the value portion of the argument to a
* boolean and store it in the provided location. If the value is missing,
* that's equivalent to a true value. If the value is invalid, report an
* error and leave the location unchanged.
*/
static void
convert_boolean(struct pam_args *args, const char *arg, bool *setting)
{
const char *value;
value = strchr(arg, '=');
if (value == NULL)
*setting = true;
else {
value++;
/* clang-format off */
if ( strcasecmp(value, "true") == 0
|| strcasecmp(value, "yes") == 0
|| strcasecmp(value, "on") == 0
|| strcmp (value, "1") == 0)
*setting = true;
else if ( strcasecmp(value, "false") == 0
|| strcasecmp(value, "no") == 0
|| strcasecmp(value, "off") == 0
|| strcmp (value, "0") == 0)
*setting = false;
else
putil_err(args, "invalid boolean in setting: %s", arg);
/* clang-format on */
}
}
/*
* Given a PAM argument, convert the value portion of the argument to a number
* and store it in the provided location. If the value is missing or isn't a
* number, report an error and leave the location unchanged.
*/
static void
convert_number(struct pam_args *args, const char *arg, long *setting)
{
const char *value;
char *end;
long result;
value = strchr(arg, '=');
if (value == NULL || value[1] == '\0') {
putil_err(args, "value missing for option %s", arg);
return;
}
errno = 0;
result = strtol(value + 1, &end, 10);
if (errno != 0 || *end != '\0') {
putil_err(args, "invalid number in setting: %s", arg);
return;
}
*setting = result;
}
/*
* Given a PAM argument, convert the value portion of the argument from a
* Kerberos time string to a krb5_deltat and store it in the provided
* location. If the value is missing or isn't a number, report an error and
* leave the location unchanged.
*/
#ifdef HAVE_KRB5
static void
convert_time(struct pam_args *args, const char *arg, krb5_deltat *setting)
{
const char *value;
krb5_deltat result;
krb5_error_code retval;
value = strchr(arg, '=');
if (value == NULL || value[1] == '\0') {
putil_err(args, "value missing for option %s", arg);
return;
}
retval = krb5_string_to_deltat((char *) value + 1, &result);
if (retval != 0)
putil_err(args, "bad time value in setting: %s", arg);
else
*setting = result;
}
#else /* HAVE_KRB5 */
static void
convert_time(struct pam_args *args, const char *arg, long *setting)
{
convert_number(args, arg, setting);
}
#endif /* !HAVE_KRB5 */
/*
* Given a PAM argument, convert the value portion of the argument to a string
* and store it in the provided location. If the value is missing, report an
* error and leave the location unchanged, returning true since that's a
* non-fatal error. If memory allocation fails, return false, since PAM setup
* should abort.
*/
static bool
convert_string(struct pam_args *args, const char *arg, char **setting)
{
const char *value;
char *result;
value = strchr(arg, '=');
if (value == NULL) {
putil_err(args, "value missing for option %s", arg);
return true;
}
result = strdup(value + 1);
if (result == NULL) {
putil_crit(args, "cannot allocate memory: %s", strerror(errno));
return false;
}
free(*setting);
*setting = result;
return true;
}
/*
* Given a PAM argument, convert the value portion of the argument to a vector
* and store it in the provided location. If the value is missing, report an
* error and leave the location unchanged, returning true since that's a
* non-fatal error. If memory allocation fails, return false, since PAM setup
* should abort.
*/
static bool
convert_list(struct pam_args *args, const char *arg, struct vector **setting)
{
const char *value;
struct vector *result;
value = strchr(arg, '=');
if (value == NULL) {
putil_err(args, "value missing for option %s", arg);
return true;
}
result = vector_split_multi(value + 1, " \t,", NULL);
if (result == NULL) {
putil_crit(args, "cannot allocate vector: %s", strerror(errno));
return false;
}
vector_free(*setting);
*setting = result;
return true;
}
/*
* Parse the PAM arguments. Takes the PAM argument struct, the argument count
* and vector, the option table, and the number of elements in the option
* table. The config member of the args struct must already be allocated.
* Returns true on success and false on error. An error return should be
* considered fatal. Report errors using putil_crit(). Unknown options will
* also be diagnosed (to syslog at LOG_ERR using putil_err()), but are not
* considered fatal errors and will still return true.
*
* If options should be retrieved from krb5.conf, call putil_args_krb5()
* first, before calling this function.
*/
bool
putil_args_parse(struct pam_args *args, int argc, const char *argv[],
const struct option options[], size_t optlen)
{
int i;
const struct option *option;
/*
* Second pass: find each option we were given and set the corresponding
* configuration parameter.
*/
for (i = 0; i < argc; i++) {
option = bsearch(argv[i], options, optlen, sizeof(struct option),
option_compare);
if (option == NULL) {
putil_err(args, "unknown option %s", argv[i]);
continue;
}
switch (option->type) {
case TYPE_BOOLEAN:
convert_boolean(args, argv[i],
CONF_BOOL(args->config, option->location));
break;
case TYPE_NUMBER:
convert_number(args, argv[i],
CONF_NUMBER(args->config, option->location));
break;
case TYPE_TIME:
convert_time(args, argv[i],
CONF_TIME(args->config, option->location));
break;
case TYPE_STRING:
if (!convert_string(args, argv[i],
CONF_STRING(args->config, option->location)))
return false;
break;
case TYPE_LIST:
case TYPE_STRLIST:
if (!convert_list(args, argv[i],
CONF_LIST(args->config, option->location)))
return false;
break;
}
}
return true;
}
+205
View File
@@ -0,0 +1,205 @@
/*
* Interface to PAM option parsing.
*
* This interface defines a lot of macros and types with very short names, and
* hence without a lot of namespace protection. It should be included only in
* the file that's doing the option parsing and not elsewhere to remove the
* risk of clashes.
*
* The canonical version of this file is maintained in the rra-c-util package,
* which can be found at <https://www.eyrie.org/~eagle/software/rra-c-util/>.
*
* Written by Russ Allbery <eagle@eyrie.org>
* Copyright 2020 Russ Allbery <eagle@eyrie.org>
* Copyright 2010-2011, 2013
* The Board of Trustees of the Leland Stanford Junior University
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*
* SPDX-License-Identifier: MIT
*/
#ifndef PAM_UTIL_OPTIONS_H
#define PAM_UTIL_OPTIONS_H 1
#include <config.h>
#ifdef HAVE_KRB5
# include <portable/krb5.h>
#endif
#include <portable/macros.h>
#include <portable/stdbool.h>
#include <stddef.h>
/* Forward declarations to avoid additional includes. */
struct vector;
/*
* The types of configuration values possible. STRLIST is a list data type
* that takes its default from a string value instead of a vector. For
* STRLIST, the default string value will be turned into a vector by splitting
* on comma, space, and tab. (This is the same as would be done with the
* value of a PAM setting when the target variable type is a list.)
*/
enum type
{
TYPE_BOOLEAN,
TYPE_NUMBER,
TYPE_TIME,
TYPE_STRING,
TYPE_LIST,
TYPE_STRLIST
};
/*
* Each configuration option is defined by a struct option. This specifies
* the name of the option, its offset into the configuration struct, whether
* it can be specified in a krb5.conf file, its type, and its default value if
* not set. Note that PAM configuration options are specified as strings, so
* there's no native way of representing a list argument. List values are
* always initialized by splitting a string on whitespace or commas.
*
* The default value should really be a union, but you can't initialize unions
* properly in C in a static initializer without C99 named initializer
* support, which we can't (yet) assume. So use a struct instead, and
* initialize all the members, even though we'll only care about one of them.
*
* Note that numbers set in the configuration struct created by this interface
* must be longs, not ints. There is currently no provision for unsigned
* numbers.
*
* Times take their default from defaults.number. The difference between time
* and number is in the parsing of a user-supplied value and the type of the
* stored attribute.
*/
struct option {
const char *name;
size_t location;
bool krb5_config;
enum type type;
struct {
bool boolean;
long number;
const char *string;
const struct vector *list;
} defaults;
};
/*
* The following macros are helpers to make it easier to define the table that
* specifies how to convert the configuration into a struct. They provide an
* initializer for the type and default fields.
*/
/* clang-format off */
#define BOOL(def) TYPE_BOOLEAN, { (def), 0, NULL, NULL }
#define NUMBER(def) TYPE_NUMBER, { 0, (def), NULL, NULL }
#define TIME(def) TYPE_TIME, { 0, (def), NULL, NULL }
#define STRING(def) TYPE_STRING, { 0, 0, (def), NULL }
#define LIST(def) TYPE_LIST, { 0, 0, NULL, (def) }
#define STRLIST(def) TYPE_STRLIST, { 0, 0, (def), NULL }
/* clang-format on */
/*
* The user of this file should also define a macro of the following form:
*
* #define K(name) (#name), offsetof(struct pam_config, name)
*
* Then, the definition of the necessary table for building the configuration
* will look something like this:
*
* const struct option options[] = {
* { K(aklog_homedir), true, BOOL (false) },
* { K(cells), true, LIST (NULL) },
* { K(debug), false, BOOL (false) },
* { K(minimum_uid), true, NUMBER (0) },
* { K(program), true, STRING (NULL) },
* };
*
* which provides a nice, succinct syntax for creating the table. The options
* MUST be in sorted order, since the options parsing code does a binary
* search.
*/
BEGIN_DECLS
/* Default to a hidden visibility for all internal functions. */
#pragma GCC visibility push(hidden)
/*
* Set the defaults for the PAM configuration. Takes the PAM arguments, an
* option table defined as above, and the number of entries in the table. The
* config member of the args struct must already be allocated. Returns true
* on success and false on error (generally out of memory). Errors will
* already be reported using putil_crit().
*
* This function must be called before either putil_args_krb5() or
* putil_args_parse(), since neither of those functions set defaults.
*/
bool putil_args_defaults(struct pam_args *, const struct option options[],
size_t optlen) __attribute__((__nonnull__));
/*
* Fill out options from krb5.conf. Takes the PAM args structure, the name of
* the section for the software being configured, an option table defined as
* above, and the number of entries in the table. The config member of the
* args struct must already be allocated. Only those options whose
* krb5_config attribute is true will be considered.
*
* This code automatically checks for configuration settings scoped to the
* local realm, so the default realm should be set before calling this
* function. If that's done based on a configuration option, one may need to
* pre-parse the configuration options.
*
* Returns true on success and false on an error. An error return should be
* considered fatal. Errors will already be reported using putil_crit*() or
* putil_err*() as appropriate. If Kerberos is not available, returns without
* doing anything.
*
* putil_args_defaults() should be called before this function.
*/
bool putil_args_krb5(struct pam_args *, const char *section,
const struct option options[], size_t optlen)
__attribute__((__nonnull__));
/*
* Parse the PAM arguments and fill out the provided struct. Takes the PAM
* arguments, the argument count and vector, an option table defined as above,
* and the number of entries in the table. The config member of the args
* struct must already be allocated. Returns true on success and false on
* error. An error return should be considered fatal. Errors will already be
* reported using putil_crit(). Unknown options will also be diagnosed (to
* syslog at LOG_ERR using putil_err()), but are not considered fatal errors
* and will still return true.
*
* The krb5_config option of the option configuration is ignored by this
* function. If options should be retrieved from krb5.conf, call
* putil_args_krb5() first, before calling this function.
*
* putil_args_defaults() should be called before this function.
*/
bool putil_args_parse(struct pam_args *, int argc, const char *argv[],
const struct option options[], size_t optlen)
__attribute__((__nonnull__));
/* Undo default visibility change. */
#pragma GCC visibility pop
END_DECLS
#endif /* !PAM_UTIL_OPTIONS_H */
+289
View File
@@ -0,0 +1,289 @@
/*
* Vector handling (counted lists of char *'s).
*
* A vector is a table for handling a list of strings with less overhead than
* linked list. The intention is for vectors, once allocated, to be reused;
* this saves on memory allocations once the array of char *'s reaches a
* stable size.
*
* This is based on the util/vector.c library, but that library uses xmalloc
* routines to exit the program if memory allocation fails. This is a
* modified version of the vector library that instead returns false on
* failure to allocate memory, allowing the caller to do appropriate recovery.
*
* Vectors require list of strings, not arbitrary binary data, and cannot
* handle data elements containing nul characters.
*
* Only the portions of the vector library used by PAM modules are
* implemented.
*
* The canonical version of this file is maintained in the rra-c-util package,
* which can be found at <https://www.eyrie.org/~eagle/software/rra-c-util/>.
*
* Written by Russ Allbery <eagle@eyrie.org>
* Copyright 2017-2018 Russ Allbery <eagle@eyrie.org>
* Copyright 2010-2011, 2014
* The Board of Trustees of the Leland Stanford Junior University
*
* Copying and distribution of this file, with or without modification, are
* permitted in any medium without royalty provided the copyright notice and
* this notice are preserved. This file is offered as-is, without any
* warranty.
*
* SPDX-License-Identifier: FSFAP
*/
#include <config.h>
#include <portable/system.h>
#include <pam-util/vector.h>
/*
* Allocate a new, empty vector. Returns NULL if memory allocation fails.
*/
struct vector *
vector_new(void)
{
struct vector *vector;
vector = calloc(1, sizeof(struct vector));
vector->allocated = 1;
vector->strings = calloc(1, sizeof(char *));
return vector;
}
/*
* Allocate a new vector that's a copy of an existing vector. Returns NULL if
* memory allocation fails.
*/
struct vector *
vector_copy(const struct vector *old)
{
struct vector *vector;
size_t i;
vector = vector_new();
if (!vector_resize(vector, old->count)) {
vector_free(vector);
return NULL;
}
vector->count = old->count;
for (i = 0; i < old->count; i++) {
vector->strings[i] = strdup(old->strings[i]);
if (vector->strings[i] == NULL) {
vector_free(vector);
return NULL;
}
}
return vector;
}
/*
* Resize a vector (using reallocarray to resize the table). Return false if
* memory allocation fails.
*/
bool
vector_resize(struct vector *vector, size_t size)
{
size_t i;
char **strings;
if (vector->count > size) {
for (i = size; i < vector->count; i++)
free(vector->strings[i]);
vector->count = size;
}
if (size == 0)
size = 1;
strings = reallocarray(vector->strings, size, sizeof(char *));
if (strings == NULL)
return false;
vector->strings = strings;
vector->allocated = size;
return true;
}
/*
* Add a new string to the vector, resizing the vector as necessary. The
* vector is resized an element at a time; if a lot of resizes are expected,
* vector_resize should be called explicitly with a more suitable size.
* Return false if memory allocation fails.
*/
bool
vector_add(struct vector *vector, const char *string)
{
size_t next = vector->count;
if (vector->count == vector->allocated)
if (!vector_resize(vector, vector->allocated + 1))
return false;
vector->strings[next] = strdup(string);
if (vector->strings[next] == NULL)
return false;
vector->count++;
return true;
}
/*
* Empty a vector but keep the allocated memory for the pointer table.
*/
void
vector_clear(struct vector *vector)
{
size_t i;
for (i = 0; i < vector->count; i++)
if (vector->strings[i] != NULL)
free(vector->strings[i]);
vector->count = 0;
}
/*
* Free a vector completely.
*/
void
vector_free(struct vector *vector)
{
if (vector == NULL)
return;
vector_clear(vector);
free(vector->strings);
free(vector);
}
/*
* Given a vector that we may be reusing, clear it out. If the first argument
* is NULL, allocate a new vector. Used by vector_split*. Returns NULL if
* memory allocation fails.
*/
static struct vector *
vector_reuse(struct vector *vector)
{
if (vector == NULL)
return vector_new();
else {
vector_clear(vector);
return vector;
}
}
/*
* Given a string and a set of separators expressed as a string, count the
* number of strings that it will split into when splitting on those
* separators.
*/
static size_t
split_multi_count(const char *string, const char *seps)
{
const char *p;
size_t count;
if (*string == '\0')
return 0;
for (count = 1, p = string + 1; *p != '\0'; p++)
if (strchr(seps, *p) != NULL && strchr(seps, p[-1]) == NULL)
count++;
/*
* If the string ends in separators, we've overestimated the number of
* strings by one.
*/
if (strchr(seps, p[-1]) != NULL)
count--;
return count;
}
/*
* Given a string, split it at any of the provided separators to form a
* vector, copying each string segment. If the third argument isn't NULL,
* reuse that vector; otherwise, allocate a new one. Any number of
* consecutive separators are considered a single separator. Returns NULL on
* memory allocation failure, after which the provided vector may only have
* partial results.
*/
struct vector *
vector_split_multi(const char *string, const char *seps, struct vector *vector)
{
const char *p, *start;
size_t i, count;
bool created = false;
if (vector == NULL)
created = true;
vector = vector_reuse(vector);
if (vector == NULL)
return NULL;
count = split_multi_count(string, seps);
if (vector->allocated < count && !vector_resize(vector, count))
goto fail;
vector->count = 0;
for (start = string, p = string, i = 0; *p != '\0'; p++)
if (strchr(seps, *p) != NULL) {
if (start != p) {
vector->strings[i] = strndup(start, (size_t)(p - start));
if (vector->strings[i] == NULL)
goto fail;
i++;
vector->count++;
}
start = p + 1;
}
if (start != p) {
vector->strings[i] = strndup(start, (size_t)(p - start));
if (vector->strings[i] == NULL)
goto fail;
vector->count++;
}
return vector;
fail:
if (created)
vector_free(vector);
return NULL;
}
/*
* Given a vector and a path to a program, exec that program with the vector
* as its arguments. This requires adding a NULL terminator to the vector and
* casting it appropriately. Returns 0 on success and -1 on error, like exec
* does.
*/
int
vector_exec(const char *path, struct vector *vector)
{
if (vector->allocated == vector->count)
if (!vector_resize(vector, vector->count + 1))
return -1;
vector->strings[vector->count] = NULL;
return execv(path, (char *const *) vector->strings);
}
/*
* Given a vector, a path to a program, and the environment, exec that program
* with the vector as its arguments and the given environment. This requires
* adding a NULL terminator to the vector and casting it appropriately.
* Returns 0 on success and -1 on error, like exec does.
*/
int
vector_exec_env(const char *path, struct vector *vector,
const char *const env[])
{
if (vector->allocated == vector->count)
if (!vector_resize(vector, vector->count + 1))
return -1;
vector->strings[vector->count] = NULL;
return execve(path, (char *const *) vector->strings, (char *const *) env);
}
+120
View File
@@ -0,0 +1,120 @@
/*
* Prototypes for vector handling.
*
* A vector is a list of strings, with dynamic resizing of the list as new
* strings are added and support for various operations on strings (such as
* splitting them on delimiters).
*
* Vectors require list of strings, not arbitrary binary data, and cannot
* handle data elements containing nul characters.
*
* This is based on the util/vector.c library, but that library uses xmalloc
* routines to exit the program if memory allocation fails. This is a
* modified version of the vector library that instead returns false on
* failure to allocate memory, allowing the caller to do appropriate recovery.
*
* Only the portions of the vector library used by PAM modules are
* implemented.
*
* The canonical version of this file is maintained in the rra-c-util package,
* which can be found at <https://www.eyrie.org/~eagle/software/rra-c-util/>.
*
* Written by Russ Allbery <eagle@eyrie.org>
* Copyright 2010-2011, 2014
* The Board of Trustees of the Leland Stanford Junior University
*
* Copying and distribution of this file, with or without modification, are
* permitted in any medium without royalty provided the copyright notice and
* this notice are preserved. This file is offered as-is, without any
* warranty.
*
* SPDX-License-Identifier: FSFAP
*/
#ifndef PAM_UTIL_VECTOR_H
#define PAM_UTIL_VECTOR_H 1
#include <config.h>
#include <portable/macros.h>
#include <portable/stdbool.h>
#include <stddef.h>
struct vector {
size_t count;
size_t allocated;
char **strings;
};
BEGIN_DECLS
/* Default to a hidden visibility for all util functions. */
#pragma GCC visibility push(hidden)
/* Create a new, empty vector. Returns NULL on memory allocation failure. */
struct vector *vector_new(void) __attribute__((__malloc__));
/*
* Create a new vector that's a copy of an existing vector. Returns NULL on
* memory allocation failure.
*/
struct vector *vector_copy(const struct vector *)
__attribute__((__malloc__, __nonnull__));
/*
* Add a string to a vector. Resizes the vector if necessary. Returns false
* on failure to allocate memory.
*/
bool vector_add(struct vector *, const char *string)
__attribute__((__nonnull__));
/*
* Resize the array of strings to hold size entries. Saves reallocation work
* in vector_add if it's known in advance how many entries there will be.
* Returns false on failure to allocate memory.
*/
bool vector_resize(struct vector *, size_t size) __attribute__((__nonnull__));
/*
* Reset the number of elements to zero, freeing all of the strings for a
* regular vector, but not freeing the strings array (to cut down on memory
* allocations if the vector will be reused).
*/
void vector_clear(struct vector *) __attribute__((__nonnull__));
/* Free the vector and all resources allocated for it. */
void vector_free(struct vector *);
/*
* Split functions build a vector from a string. vector_split_multi splits on
* a set of characters. If the vector argument is NULL, a new vector is
* allocated; otherwise, the provided one is reused. Returns NULL on memory
* allocation failure, after which the provided vector may have been modified
* to only have partial results.
*
* Empty strings will yield zero-length vectors. Adjacent delimiters are
* treated as a single delimiter by vector_split_multi. Any leading or
* trailing delimiters are ignored, so this function will never create
* zero-length strings (similar to the behavior of strtok).
*/
struct vector *vector_split_multi(const char *string, const char *seps,
struct vector *)
__attribute__((__nonnull__(1, 2)));
/*
* Exec the given program with the vector as its arguments. Return behavior
* is the same as execv. Note the argument order is different than the other
* vector functions (but the same as execv). The vector_exec_env variant
* calls execve and passes in the environment for the program.
*/
int vector_exec(const char *path, struct vector *)
__attribute__((__nonnull__));
int vector_exec_env(const char *path, struct vector *, const char *const env[])
__attribute__((__nonnull__));
/* Undo default visibility change. */
#pragma GCC visibility pop
END_DECLS
#endif /* UTIL_VECTOR_H */
+84
View File
@@ -0,0 +1,84 @@
/*
* Replacement for a missing asprintf and vasprintf.
*
* Provides the same functionality as the standard GNU library routines
* asprintf and vasprintf for those platforms that don't have them.
*
* The canonical version of this file is maintained in the rra-c-util package,
* which can be found at <https://www.eyrie.org/~eagle/software/rra-c-util/>.
*
* Written by Russ Allbery <eagle@eyrie.org>
* Copyright 2006, 2015 Russ Allbery <eagle@eyrie.org>
* Copyright 2008-2009, 2011, 2013
* The Board of Trustees of the Leland Stanford Junior University
*
* Copying and distribution of this file, with or without modification, are
* permitted in any medium without royalty provided the copyright notice and
* this notice are preserved. This file is offered as-is, without any
* warranty.
*
* SPDX-License-Identifier: FSFAP
*/
#include <config.h>
#include <portable/macros.h>
#include <portable/system.h>
#include <errno.h>
/*
* If we're running the test suite, rename the functions to avoid conflicts
* with the system versions.
*/
#if TESTING
# undef asprintf
# undef vasprintf
# define asprintf test_asprintf
# define vasprintf test_vasprintf
int test_asprintf(char **, const char *, ...)
__attribute__((__format__(printf, 2, 3)));
int test_vasprintf(char **, const char *, va_list)
__attribute__((__format__(printf, 2, 0)));
#endif
int
asprintf(char **strp, const char *fmt, ...)
{
va_list args;
int status;
va_start(args, fmt);
status = vasprintf(strp, fmt, args);
va_end(args);
return status;
}
int
vasprintf(char **strp, const char *fmt, va_list args)
{
va_list args_copy;
int status, needed, oerrno;
va_copy(args_copy, args);
needed = vsnprintf(NULL, 0, fmt, args_copy);
va_end(args_copy);
if (needed < 0) {
*strp = NULL;
return needed;
}
*strp = malloc(needed + 1);
if (*strp == NULL)
return -1;
status = vsnprintf(*strp, needed + 1, fmt, args);
if (status >= 0)
return status;
else {
oerrno = errno;
free(*strp);
*strp = NULL;
errno = oerrno;
return status;
}
}
+33
View File
@@ -0,0 +1,33 @@
/*
* Dummy symbol to prevent an empty library.
*
* On platforms that already have all of the functions that libportable would
* supply, Automake builds an empty library and then calls ar with nonsensical
* arguments. Ensure that libportable always contains at least one symbol.
*
* The canonical version of this file is maintained in the rra-c-util package,
* which can be found at <https://www.eyrie.org/~eagle/software/rra-c-util/>.
*
* Written by Russ Allbery <eagle@eyrie.org>
* Copyright 2017 Russ Allbery <eagle@eyrie.org>
* Copyright 2008, 2011, 2013
* The Board of Trustees of the Leland Stanford Junior University
*
* Copying and distribution of this file, with or without modification, are
* permitted in any medium without royalty provided the copyright notice and
* this notice are preserved. This file is offered as-is, without any
* warranty.
*
* SPDX-License-Identifier: FSFAP
*/
#include <portable/macros.h>
/* Prototype to avoid gcc warnings and set visibility. */
int portable_dummy(void) __attribute__((__const__, __visibility__("hidden")));
int
portable_dummy(void)
{
return 42;
}
+35
View File
@@ -0,0 +1,35 @@
/*
* Replacement for a missing issetugid.
*
* Simulates the functionality as the Solaris function issetugid, which
* returns true if the running program was setuid or setgid. The replacement
* test is not quite as comprehensive as what the Solaris function does, but
* it should be good enough.
*
* The canonical version of this file is maintained in the rra-c-util package,
* which can be found at <https://www.eyrie.org/~eagle/software/rra-c-util/>.
*
* Written by Russ Allbery <eagle@eyrie.org>
* Copyright 2010-2011
* The Board of Trustees of the Leland Stanford Junior University
*
* Copying and distribution of this file, with or without modification, are
* permitted in any medium without royalty provided the copyright notice and
* this notice are preserved. This file is offered as-is, without any
* warranty.
*
* SPDX-License-Identifier: FSFAP
*/
#include <config.h>
#include <portable/system.h>
int
issetugid(void)
{
if (getuid() != geteuid())
return 1;
if (getgid() != getegid())
return 1;
return 0;
}
+82
View File
@@ -0,0 +1,82 @@
/*
* Portability wrapper around kadm5/admin.h.
*
* This header adjusts for differences between the MIT and Heimdal kadmin
* client libraries so that the code can be written to a consistent API
* (favoring the Heimdal API as the exposed one).
*
* The canonical version of this file is maintained in the rra-c-util package,
* which can be found at <https://www.eyrie.org/~eagle/software/rra-c-util/>.
*
* Written by Russ Allbery <eagle@eyrie.org>
* Copyright 2015 Russ Allbery <eagle@eyrie.org>
* Copyright 2011, 2013
* The Board of Trustees of the Leland Stanford Junior University
*
* Copying and distribution of this file, with or without modification, are
* permitted in any medium without royalty provided the copyright notice and
* this notice are preserved. This file is offered as-is, without any
* warranty.
*
* SPDX-License-Identifier: FSFAP
*/
#ifndef PORTABLE_KADMIN_H
#define PORTABLE_KADMIN_H 1
#include <config.h>
#include <kadm5/admin.h>
#ifdef HAVE_KADM5_KADM5_ERR_H
# include <kadm5/kadm5_err.h>
#else
# include <kadm5/kadm_err.h>
#endif
/*
* MIT as of 1.10 supports version 3. Heimdal as of 1.5 has a maximum version
* of 2. Define a KADM5_API_VERSION symbol that holds the maximum version.
* (Heimdal does this for us, so we only have to do that with MIT, but be
* general just in case.)
*/
#ifndef KADM5_API_VERSION
# ifdef KADM5_API_VERSION_3
# define KADM5_API_VERSION KADM5_API_VERSION_3
# else
# define KADM5_API_VERSION KADM5_API_VERSION_2
# endif
#endif
/* Heimdal doesn't define KADM5_PASS_Q_GENERIC. */
#ifndef KADM5_PASS_Q_GENERIC
# define KADM5_PASS_Q_GENERIC KADM5_PASS_Q_DICT
#endif
/* Heimdal doesn't define KADM5_MISSING_KRB5_CONF_PARAMS. */
#ifndef KADM5_MISSING_KRB5_CONF_PARAMS
# define KADM5_MISSING_KRB5_CONF_PARAMS KADM5_MISSING_CONF_PARAMS
#endif
/*
* MIT Kerberos provides this function for pure kadmin clients to get a
* Kerberos context. With Heimdal, just use krb5_init_context.
*/
#ifndef HAVE_KADM5_INIT_KRB5_CONTEXT
# define kadm5_init_krb5_context(c) krb5_init_context(c)
#endif
/*
* Heimdal provides _ctx functions that take an existing context. MIT always
* requires the context be passed in. Code should use the _ctx variant, and
* the below will fix it up if built against MIT.
*
* MIT also doesn't have a const prototype for the server argument, so cast it
* so that we can use the KADM5_ADMIN_SERVICE define.
*/
#ifndef HAVE_KADM5_INIT_WITH_SKEY_CTX
# define kadm5_init_with_skey_ctx(c, u, k, s, p, sv, av, h) \
kadm5_init_with_skey((c), (u), (k), (char *) (s), (p), (sv), (av), \
NULL, (h))
#endif
#endif /* !PORTABLE_KADMIN_H */
+186
View File
@@ -0,0 +1,186 @@
/*
* Portability glue functions for Kerberos.
*
* This file provides definitions of the interfaces that portable/krb5.h
* ensures exist if the function wasn't available in the Kerberos libraries.
* Everything in this file will be protected by #ifndef. If the native
* Kerberos libraries are fully capable, this file will be skipped.
*
* The canonical version of this file is maintained in the rra-c-util package,
* which can be found at <https://www.eyrie.org/~eagle/software/rra-c-util/>.
*
* Written by Russ Allbery <eagle@eyrie.org>
* Copyright 2015-2016, 2018 Russ Allbery <eagle@eyrie.org>
* Copyright 2010-2012, 2014
* The Board of Trustees of the Leland Stanford Junior University
*
* Copying and distribution of this file, with or without modification, are
* permitted in any medium without royalty provided the copyright notice and
* this notice are preserved. This file is offered as-is, without any
* warranty.
*
* SPDX-License-Identifier: FSFAP
*/
#include <config.h>
#include <portable/krb5.h>
#include <portable/macros.h>
#include <portable/system.h>
#include <errno.h>
/* Figure out what header files to include for error reporting. */
#if !defined(HAVE_KRB5_GET_ERROR_MESSAGE) && !defined(HAVE_KRB5_GET_ERR_TEXT)
# if !defined(HAVE_KRB5_GET_ERROR_STRING)
# if defined(HAVE_IBM_SVC_KRB5_SVC_H)
# include <ibm_svc/krb5_svc.h>
# elif defined(HAVE_ET_COM_ERR_H)
# include <et/com_err.h>
# elif defined(HAVE_KERBEROSV5_COM_ERR_H)
# include <kerberosv5/com_err.h>
# else
# include <com_err.h>
# endif
# endif
#endif
/* Used for unused parameters to silence gcc warnings. */
#define UNUSED __attribute__((__unused__))
/*
* This string is returned for unknown error messages. We use a static
* variable so that we can be sure not to free it.
*/
#if !defined(HAVE_KRB5_GET_ERROR_MESSAGE) \
|| !defined(HAVE_KRB5_FREE_ERROR_MESSAGE)
static const char error_unknown[] = "unknown error";
#endif
#ifndef HAVE_KRB5_CC_GET_FULL_NAME
/*
* Given a Kerberos ticket cache, return the full name (TYPE:name) in
* newly-allocated memory. Returns an error code. Avoid asprintf and
* snprintf here in case someone wants to use this code without the rest of
* the portability layer.
*/
krb5_error_code
krb5_cc_get_full_name(krb5_context ctx, krb5_ccache ccache, char **out)
{
const char *type, *name;
size_t length;
type = krb5_cc_get_type(ctx, ccache);
if (type == NULL)
type = "FILE";
name = krb5_cc_get_name(ctx, ccache);
if (name == NULL)
return EINVAL;
length = strlen(type) + 1 + strlen(name) + 1;
*out = malloc(length);
if (*out == NULL)
return errno;
sprintf(*out, "%s:%s", type, name);
return 0;
}
#endif /* !HAVE_KRB5_CC_GET_FULL_NAME */
#ifndef HAVE_KRB5_GET_ERROR_MESSAGE
/*
* Given a Kerberos error code, return the corresponding error. Prefer the
* Kerberos interface if available since it will provide context-specific
* error information, whereas the error_message() call will only provide a
* fixed message.
*/
const char *
krb5_get_error_message(krb5_context ctx UNUSED, krb5_error_code code UNUSED)
{
const char *msg;
# if defined(HAVE_KRB5_GET_ERROR_STRING)
msg = krb5_get_error_string(ctx);
# elif defined(HAVE_KRB5_GET_ERR_TEXT)
msg = krb5_get_err_text(ctx, code);
# elif defined(HAVE_KRB5_SVC_GET_MSG)
krb5_svc_get_msg(code, (char **) &msg);
# else
msg = error_message(code);
# endif
if (msg == NULL)
return error_unknown;
else
return msg;
}
#endif /* !HAVE_KRB5_GET_ERROR_MESSAGE */
#ifndef HAVE_KRB5_FREE_ERROR_MESSAGE
/*
* Free an error string if necessary. If we returned a static string, make
* sure we don't free it.
*
* This code assumes that the set of implementations that have
* krb5_free_error_message is a subset of those with krb5_get_error_message.
* If this assumption ever breaks, we may call the wrong free function.
*/
void
krb5_free_error_message(krb5_context ctx UNUSED, const char *msg)
{
if (msg == error_unknown)
return;
# if defined(HAVE_KRB5_GET_ERROR_STRING)
krb5_free_error_string(ctx, (char *) msg);
# elif defined(HAVE_KRB5_SVC_GET_MSG)
krb5_free_string(ctx, (char *) msg);
# endif
}
#endif /* !HAVE_KRB5_FREE_ERROR_MESSAGE */
#ifndef HAVE_KRB5_GET_INIT_CREDS_OPT_ALLOC
/*
* Allocate and initialize a krb5_get_init_creds_opt struct. This code
* assumes that an all-zero bit pattern will create a NULL pointer.
*/
krb5_error_code
krb5_get_init_creds_opt_alloc(krb5_context ctx UNUSED,
krb5_get_init_creds_opt **opts)
{
*opts = calloc(1, sizeof(krb5_get_init_creds_opt));
if (*opts == NULL)
return errno;
krb5_get_init_creds_opt_init(*opts);
return 0;
}
#endif /* !HAVE_KRB5_GET_INIT_CREDS_OPT_ALLOC */
#ifndef HAVE_KRB5_PRINCIPAL_GET_REALM
/*
* Return the realm of a principal as a const char *.
*/
const char *
krb5_principal_get_realm(krb5_context ctx UNUSED, krb5_const_principal princ)
{
const krb5_data *data;
data = krb5_princ_realm(ctx, princ);
if (data == NULL || data->data == NULL)
return NULL;
return data->data;
}
#endif /* !HAVE_KRB5_PRINCIPAL_GET_REALM */
#ifndef HAVE_KRB5_VERIFY_INIT_CREDS_OPT_INIT
/*
* Initialize the option struct for krb5_verify_init_creds.
*/
void
krb5_verify_init_creds_opt_init(krb5_verify_init_creds_opt *opt)
{
opt->flags = 0;
opt->ap_req_nofail = 0;
}
#endif
+237
View File
@@ -0,0 +1,237 @@
/*
* Kerberos compatibility functions for AIX's NAS libraries.
*
* AIX for some reason doesn't provide the krb5_appdefault_* functions, but
* does provide the underlying profile library functions (as a separate
* libk5profile with a separate k5profile.h header file).
*
* This file is therefore (apart from the includes, opening and closing
* comments, and the spots marked with an rra-c-util comment) a verbatim copy
* of src/lib/krb5/krb/appdefault.c from MIT Kerberos 1.4.4.
*
* The canonical version of this file is maintained in the rra-c-util package,
* which can be found at <https://www.eyrie.org/~eagle/software/rra-c-util/>.
*
* Copyright 1985-2005 by the Massachusetts Institute of Technology.
* For license information, see the end of this file.
*/
#include <config.h>
#include <krb5.h>
#ifdef HAVE_K5PROFILE_H
# include <k5profile.h>
#endif
#ifdef HAVE_PROFILE_H
# include <profile.h>
#endif
#include <stdio.h>
#include <string.h>
/*xxx Duplicating this is annoying; try to work on a better way.*/
static const char *const conf_yes[] = {
"y", "yes", "true", "t", "1", "on",
0,
};
static const char *const conf_no[] = {
"n", "no", "false", "nil", "0", "off",
0,
};
static int conf_boolean(char *s)
{
const char * const *p;
for(p=conf_yes; *p; p++) {
if (!strcasecmp(*p,s))
return 1;
}
for(p=conf_no; *p; p++) {
if (!strcasecmp(*p,s))
return 0;
}
/* Default to "no" */
return 0;
}
static krb5_error_code appdefault_get(krb5_context context, const char *appname, const krb5_data *realm, const char *option, char **ret_value)
{
profile_t profile;
const char *names[5];
char **nameval = NULL;
krb5_error_code retval;
const char * realmstr = realm?realm->data:NULL;
/*
* rra-c-util: The magic values are internal, so a magic check for the
* context struct was removed here. Call krb5_get_profile if it's
* available since the krb5_context struct may be opaque.
*/
if (!context)
return KV5M_CONTEXT;
#ifdef HAVE_KRB5_GET_PROFILE
krb5_get_profile(context, &profile);
#else
profile = context->profile;
#endif
/*
* Try number one:
*
* [appdefaults]
* app = {
* SOME.REALM = {
* option = <boolean>
* }
* }
*/
names[0] = "appdefaults";
names[1] = appname;
if (realmstr) {
names[2] = realmstr;
names[3] = option;
names[4] = 0;
retval = profile_get_values(profile, names, &nameval);
if (retval == 0 && nameval && nameval[0]) {
*ret_value = strdup(nameval[0]);
goto goodbye;
}
}
/*
* Try number two:
*
* [appdefaults]
* app = {
* option = <boolean>
* }
*/
names[2] = option;
names[3] = 0;
retval = profile_get_values(profile, names, &nameval);
if (retval == 0 && nameval && nameval[0]) {
*ret_value = strdup(nameval[0]);
goto goodbye;
}
/*
* Try number three:
*
* [appdefaults]
* realm = {
* option = <boolean>
*/
if (realmstr) {
names[1] = realmstr;
names[2] = option;
names[3] = 0;
retval = profile_get_values(profile, names, &nameval);
if (retval == 0 && nameval && nameval[0]) {
*ret_value = strdup(nameval[0]);
goto goodbye;
}
}
/*
* Try number four:
*
* [appdefaults]
* option = <boolean>
*/
names[1] = option;
names[2] = 0;
retval = profile_get_values(profile, names, &nameval);
if (retval == 0 && nameval && nameval[0]) {
*ret_value = strdup(nameval[0]);
} else {
return retval;
}
goodbye:
if (nameval) {
char **cpp;
for (cpp = nameval; *cpp; cpp++)
free(*cpp);
free(nameval);
}
return 0;
}
void KRB5_CALLCONV
krb5_appdefault_boolean(krb5_context context, const char *appname, const krb5_data *realm, const char *option, int default_value, int *ret_value)
{
char *string = NULL;
krb5_error_code retval;
retval = appdefault_get(context, appname, realm, option, &string);
if (! retval && string) {
*ret_value = conf_boolean(string);
free(string);
} else
*ret_value = default_value;
}
void KRB5_CALLCONV
krb5_appdefault_string(krb5_context context, const char *appname, const krb5_data *realm, const char *option, const char *default_value, char **ret_value)
{
krb5_error_code retval;
char *string;
retval = appdefault_get(context, appname, realm, option, &string);
if (! retval && string) {
*ret_value = string;
} else {
*ret_value = strdup(default_value);
}
}
/*
* Copyright (C) 1985-2005 by the Massachusetts Institute of Technology.
* All rights reserved.
*
* Export of this software from the United States of America may require
* a specific license from the United States Government. It is the
* responsibility of any person or organization contemplating export to
* obtain such a license before exporting.
*
* WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
* distribute this software and its documentation for any purpose and
* without fee is hereby granted, provided that the above copyright
* notice appear in all copies and that both that copyright notice and
* this permission notice appear in supporting documentation, and that
* the name of M.I.T. not be used in advertising or publicity pertaining
* to distribution of the software without specific, written prior
* permission. Furthermore if you modify this software you must label
* your software as modified software and not distribute it in such a
* fashion that it might be confused with the original MIT software.
* M.I.T. makes no representations about the suitability of this software
* for any purpose. It is provided "as is" without express or implied
* warranty.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*
* Individual source code files are copyright MIT, Cygnus Support,
* OpenVision, Oracle, Sun Soft, FundsXpress, and others.
*
* Project Athena, Athena, Athena MUSE, Discuss, Hesiod, Kerberos, Moira,
* and Zephyr are trademarks of the Massachusetts Institute of Technology
* (MIT). No commercial use of these trademarks may be made without
* prior written permission of MIT.
*
* "Commercial use" means use of a name in a product or other for-profit
* manner. It does NOT prevent a commercial firm from referring to the
* MIT trademarks in order to convey information (although in doing so,
* recognition of their trademark status should be given).
*
* There is no SPDX-License-Identifier registered for this license.
*/
+248
View File
@@ -0,0 +1,248 @@
/*
* Portability wrapper around krb5.h.
*
* This header includes krb5.h and then adjusts for various portability
* issues, primarily between MIT Kerberos and Heimdal, so that code can be
* written to a consistent API.
*
* Unfortunately, due to the nature of the differences between MIT Kerberos
* and Heimdal, it's not possible to write code to either one of the APIs and
* adjust for the other one. In general, this header tries to make available
* the Heimdal API and fix it for MIT Kerberos, but there are places where MIT
* Kerberos requires a more specific call. For those cases, it provides the
* most specific interface.
*
* For example, MIT Kerberos has krb5_free_unparsed_name() whereas Heimdal
* prefers the generic krb5_xfree(). In this case, this header provides
* krb5_free_unparsed_name() for both APIs since it's the most specific call.
*
* The canonical version of this file is maintained in the rra-c-util package,
* which can be found at <https://www.eyrie.org/~eagle/software/rra-c-util/>.
*
* Written by Russ Allbery <eagle@eyrie.org>
* Copyright 2015, 2017, 2020 Russ Allbery <eagle@eyrie.org>
* Copyright 2010-2014
* The Board of Trustees of the Leland Stanford Junior University
*
* Copying and distribution of this file, with or without modification, are
* permitted in any medium without royalty provided the copyright notice and
* this notice are preserved. This file is offered as-is, without any
* warranty.
*
* SPDX-License-Identifier: FSFAP
*/
#ifndef PORTABLE_KRB5_H
#define PORTABLE_KRB5_H 1
/*
* Allow inclusion of config.h to be skipped, since sometimes we have to use a
* stripped-down version of config.h with a different name.
*/
#ifndef CONFIG_H_INCLUDED
# include <config.h>
#endif
#include <portable/macros.h>
#if defined(HAVE_KRB5_H)
# include <krb5.h>
#elif defined(HAVE_KERBEROSV5_KRB5_H)
# include <kerberosv5/krb5.h>
#else
# include <krb5/krb5.h>
#endif
#include <stdlib.h>
/* Heimdal: KRB5_WELLKNOWN_NAME, MIT: KRB5_WELLKNOWN_NAMESTR. */
#ifndef KRB5_WELLKNOWN_NAME
# ifdef KRB5_WELLKNOWN_NAMESTR
# define KRB5_WELLKNOWN_NAME KRB5_WELLKNOWN_NAMESTR
# else
# define KRB5_WELLKNOWN_NAME "WELLKNOWN"
# endif
#endif
/* Heimdal: KRB5_ANON_NAME, MIT: KRB5_ANONYMOUS_PRINCSTR. */
#ifndef KRB5_ANON_NAME
# ifdef KRB5_ANONYMOUS_PRINCSTR
# define KRB5_ANON_NAME KRB5_ANONYMOUS_PRINCSTR
# else
# define KRB5_ANON_NAME "ANONYMOUS"
# endif
#endif
/* Heimdal: KRB5_ANON_REALM, MIT: KRB5_ANONYMOUS_REALMSTR. */
#ifndef KRB5_ANON_REALM
# ifdef KRB5_ANONYMOUS_REALMSTR
# define KRB5_ANON_REALM KRB5_ANONYMOUS_REALMSTR
# else
# define KRB5_ANON_REALM "WELLKNOWN:ANONYMOUS"
# endif
#endif
BEGIN_DECLS
/* Default to a hidden visibility for all portability functions. */
#pragma GCC visibility push(hidden)
/*
* AIX included Kerberos includes the profile library but not the
* krb5_appdefault functions, so we provide replacements that we have to
* prototype.
*/
#ifndef HAVE_KRB5_APPDEFAULT_STRING
void krb5_appdefault_boolean(krb5_context, const char *, const krb5_data *,
const char *, int, int *);
void krb5_appdefault_string(krb5_context, const char *, const krb5_data *,
const char *, const char *, char **);
#endif
/*
* Now present in both Heimdal and MIT, but very new in MIT and not present in
* older Heimdal.
*/
#ifndef HAVE_KRB5_CC_GET_FULL_NAME
krb5_error_code krb5_cc_get_full_name(krb5_context, krb5_ccache, char **);
#endif
/* Heimdal: krb5_data_free, MIT: krb5_free_data_contents. */
#ifdef HAVE_KRB5_DATA_FREE
# define krb5_free_data_contents(c, d) krb5_data_free(d)
#endif
/*
* MIT-specific. The Heimdal documentation says to use free(), but that
* doesn't actually make sense since the memory is allocated inside the
* Kerberos library. Use krb5_xfree instead.
*/
#ifndef HAVE_KRB5_FREE_DEFAULT_REALM
# define krb5_free_default_realm(c, r) krb5_xfree(r)
#endif
/*
* Heimdal: krb5_xfree, MIT: krb5_free_string, older MIT uses free(). Note
* that we can incorrectly allocate in the library and call free() if
* krb5_free_string is not available but something we use that API for is
* available, such as krb5_appdefaults_*, but there isn't anything we can
* really do about it.
*/
#ifndef HAVE_KRB5_FREE_STRING
# ifdef HAVE_KRB5_XFREE
# define krb5_free_string(c, s) krb5_xfree(s)
# else
# define krb5_free_string(c, s) free(s)
# endif
#endif
/* Heimdal: krb5_xfree, MIT: krb5_free_unparsed_name. */
#ifdef HAVE_KRB5_XFREE
# define krb5_free_unparsed_name(c, p) krb5_xfree(p)
#endif
/*
* krb5_{get,free}_error_message are the preferred APIs for both current MIT
* and current Heimdal, but there are tons of older APIs we may have to fall
* back on for earlier versions.
*
* This function should be called immediately after the corresponding error
* without any intervening Kerberos calls. Otherwise, the correct error
* message and supporting information may not be returned.
*/
#ifndef HAVE_KRB5_GET_ERROR_MESSAGE
const char *krb5_get_error_message(krb5_context, krb5_error_code);
#endif
#ifndef HAVE_KRB5_FREE_ERROR_MESSAGE
void krb5_free_error_message(krb5_context, const char *);
#endif
/*
* Both current MIT and current Heimdal prefer _opt_alloc and _opt_free, but
* older versions of both require allocating your own struct and calling
* _opt_init.
*/
#ifndef HAVE_KRB5_GET_INIT_CREDS_OPT_ALLOC
krb5_error_code krb5_get_init_creds_opt_alloc(krb5_context,
krb5_get_init_creds_opt **);
#endif
#ifdef HAVE_KRB5_GET_INIT_CREDS_OPT_FREE
# ifndef HAVE_KRB5_GET_INIT_CREDS_OPT_FREE_2_ARGS
# define krb5_get_init_creds_opt_free(c, o) \
krb5_get_init_creds_opt_free(o)
# endif
#else
# define krb5_get_init_creds_opt_free(c, o) free(o)
#endif
/* Not available in versions of Heimdal prior to 7.0.1. */
#ifndef HAVE_KRB5_GET_INIT_CREDS_OPT_SET_CHANGE_PASSWORD_PROMPT
# define krb5_get_init_creds_opt_set_change_password_prompt(o, f) /* */
#endif
/* Heimdal-specific. */
#ifndef HAVE_KRB5_GET_INIT_CREDS_OPT_SET_DEFAULT_FLAGS
# define krb5_get_init_creds_opt_set_default_flags(c, p, r, o) /* empty */
#endif
/*
* Old versions of Heimdal (0.7 and earlier) take only nine arguments to the
* krb5_get_init_creds_opt_set_pkinit instead of the 11 arguments that current
* versions take. Adjust if needed. This function is Heimdal-specific.
*/
#ifdef HAVE_KRB5_GET_INIT_CREDS_OPT_SET_PKINIT
# ifdef HAVE_KRB5_GET_INIT_CREDS_OPT_SET_PKINIT_9_ARGS
# define krb5_get_init_creds_opt_set_pkinit(c, o, p, u, a, l, r, f, m, \
d, s) \
krb5_get_init_creds_opt_set_pkinit((c), (o), (p), (u), (a), (f), \
(m), (d), (s));
# endif
#endif
/*
* MIT-specific. Heimdal automatically ignores environment variables if
* called in a setuid context.
*/
#ifndef HAVE_KRB5_INIT_SECURE_CONTEXT
# define krb5_init_secure_context(c) krb5_init_context(c)
#endif
/*
* Heimdal: krb5_kt_free_entry, MIT: krb5_free_keytab_entry_contents. We
* check for the declaration rather than the function since the function is
* present in older MIT Kerberos libraries but not prototyped.
*/
#if !HAVE_DECL_KRB5_KT_FREE_ENTRY
# define krb5_kt_free_entry(c, e) krb5_free_keytab_entry_contents((c), (e))
#endif
/*
* Heimdal provides a nice function that just returns a const char *. On MIT,
* there's an accessor macro that returns the krb5_data pointer, which
* requires more work to get at the underlying char *.
*/
#ifndef HAVE_KRB5_PRINCIPAL_GET_REALM
const char *krb5_principal_get_realm(krb5_context, krb5_const_principal);
#endif
/*
* krb5_change_password is deprecated in favor of krb5_set_password in current
* Heimdal. Current MIT provides both.
*/
#ifndef HAVE_KRB5_SET_PASSWORD
# define krb5_set_password(c, cr, pw, p, rc, rcs, rs) \
krb5_change_password((c), (cr), (pw), (rc), (rcs), (rs))
#endif
/*
* AIX's NAS Kerberos implementation mysteriously provides the struct and the
* krb5_verify_init_creds function but not this function.
*/
#ifndef HAVE_KRB5_VERIFY_INIT_CREDS_OPT_INIT
void krb5_verify_init_creds_opt_init(krb5_verify_init_creds_opt *opt);
#endif
/* Undo default visibility change. */
#pragma GCC visibility pop
END_DECLS
#endif /* !PORTABLE_KRB5_H */
+72
View File
@@ -0,0 +1,72 @@
/*
* Portability macros used in include files.
*
* The canonical version of this file is maintained in the rra-c-util package,
* which can be found at <https://www.eyrie.org/~eagle/software/rra-c-util/>.
*
* Written by Russ Allbery <eagle@eyrie.org>
* Copyright 2015 Russ Allbery <eagle@eyrie.org>
* Copyright 2008, 2011-2012
* The Board of Trustees of the Leland Stanford Junior University
*
* Copying and distribution of this file, with or without modification, are
* permitted in any medium without royalty provided the copyright notice and
* this notice are preserved. This file is offered as-is, without any
* warranty.
*
* SPDX-License-Identifier: FSFAP
*/
#ifndef PORTABLE_MACROS_H
#define PORTABLE_MACROS_H 1
/*
* __attribute__ is available in gcc 2.5 and later, but only with gcc 2.7
* could you use the __format__ form of the attributes, which is what we use
* (to avoid confusion with other macros).
*/
#ifndef __attribute__
# if __GNUC__ < 2 || (__GNUC__ == 2 && __GNUC_MINOR__ < 7)
# define __attribute__(spec) /* empty */
# endif
#endif
/*
* We use __alloc_size__, but it was only available in fairly recent versions
* of GCC. Suppress warnings about the unknown attribute if GCC is too old.
* We know that we're GCC at this point, so we can use the GCC variadic macro
* extension, which will still work with versions of GCC too old to have C99
* variadic macro support.
*/
#if !defined(__attribute__) && !defined(__alloc_size__)
# if (__GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 3)) \
&& !defined(__clang__)
# define __alloc_size__(spec, args...) /* empty */
# endif
#endif
/*
* LLVM and Clang pretend to be GCC but don't support all of the __attribute__
* settings that GCC does. For them, suppress warnings about unknown
* attributes on declarations. This unfortunately will affect the entire
* compilation context, but there's no push and pop available.
*/
#if !defined(__attribute__) && (defined(__llvm__) || defined(__clang__))
# pragma GCC diagnostic ignored "-Wattributes"
#endif
/*
* BEGIN_DECLS is used at the beginning of declarations so that C++
* compilers don't mangle their names. END_DECLS is used at the end.
*/
#undef BEGIN_DECLS
#undef END_DECLS
#ifdef __cplusplus
# define BEGIN_DECLS extern "C" {
# define END_DECLS }
#else
# define BEGIN_DECLS /* empty */
# define END_DECLS /* empty */
#endif
#endif /* !PORTABLE_MACROS_H */
+101
View File
@@ -0,0 +1,101 @@
/*
* Replacement for a missing mkstemp.
*
* Provides the same functionality as the library function mkstemp for those
* systems that don't have it.
*
* The canonical version of this file is maintained in the rra-c-util package,
* which can be found at <https://www.eyrie.org/~eagle/software/rra-c-util/>.
*
* Written by Russ Allbery <eagle@eyrie.org>
* Copyright 2009, 2011, 2014
* The Board of Trustees of the Leland Stanford Junior University
*
* Copying and distribution of this file, with or without modification, are
* permitted in any medium without royalty provided the copyright notice and
* this notice are preserved. This file is offered as-is, without any
* warranty.
*
* SPDX-License-Identifier: FSFAP
*/
#include <config.h>
#include <portable/system.h>
#include <errno.h>
#include <fcntl.h>
#ifdef HAVE_SYS_TIME_H
# include <sys/time.h>
#endif
#include <time.h>
/*
* If we're running the test suite, rename mkstemp to avoid conflicts with the
* system version. #undef it first because some systems may define it to
* another name.
*/
#if TESTING
# undef mkstemp
# define mkstemp test_mkstemp
int test_mkstemp(char *);
#endif
/* Pick the longest available integer type. */
#if HAVE_LONG_LONG_INT
typedef unsigned long long long_int_type;
#else
typedef unsigned long long_int_type;
#endif
int
mkstemp(char *template)
{
static const char letters[] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
size_t length;
char *XXXXXX;
struct timeval tv;
long_int_type randnum, working;
int i, tries, fd;
/*
* Make sure we have a valid template and initialize p to point at the
* beginning of the template portion of the string.
*/
length = strlen(template);
if (length < 6) {
errno = EINVAL;
return -1;
}
XXXXXX = template + length - 6;
if (strcmp(XXXXXX, "XXXXXX") != 0) {
errno = EINVAL;
return -1;
}
/* Get some more-or-less random information. */
gettimeofday(&tv, NULL);
randnum = ((long_int_type) tv.tv_usec << 16) ^ tv.tv_sec ^ getpid();
/*
* Now, try to find a working file name. We try no more than TMP_MAX file
* names.
*/
for (tries = 0; tries < TMP_MAX; tries++) {
for (working = randnum, i = 0; i < 6; i++) {
XXXXXX[i] = letters[working % 62];
working /= 62;
}
fd = open(template, O_RDWR | O_CREAT | O_EXCL, 0600);
if (fd >= 0 || (errno != EEXIST && errno != EISDIR))
return fd;
/*
* This is a relatively random increment. Cut off the tail end of
* tv_usec since it's often predictable.
*/
randnum += (tv.tv_usec >> 10) & 0xfff;
}
errno = EEXIST;
return -1;
}
+129
View File
@@ -0,0 +1,129 @@
/*
* Portability wrapper around PAM header files.
*
* This header file includes the various PAM headers, wherever they may be
* found on the system, and defines replacements for PAM functions that may
* not be available on the local system.
*
* The canonical version of this file is maintained in the rra-c-util package,
* which can be found at <https://www.eyrie.org/~eagle/software/rra-c-util/>.
*
* Written by Russ Allbery <eagle@eyrie.org>
* Copyright 2015, 2020 Russ Allbery <eagle@eyrie.org>
* Copyright 2010-2011, 2014
* The Board of Trustees of the Leland Stanford Junior University
*
* Copying and distribution of this file, with or without modification, are
* permitted in any medium without royalty provided the copyright notice and
* this notice are preserved. This file is offered as-is, without any
* warranty.
*
* SPDX-License-Identifier: FSFAP
*/
#ifndef PORTABLE_PAM_H
#define PORTABLE_PAM_H 1
#include <config.h>
#include <portable/macros.h>
/* Linux PAM 1.1.0 requires sys/types.h before security/pam_modutil.h. */
#include <sys/types.h>
#ifndef HAVE_PAM_MODUTIL_GETPWNAM
# include <pwd.h>
#endif
#if defined(HAVE_SECURITY_PAM_APPL_H)
# include <security/pam_appl.h>
# include <security/pam_modules.h>
#elif defined(HAVE_PAM_PAM_APPL_H)
# include <pam/pam_appl.h>
# include <pam/pam_modules.h>
#endif
#if defined(HAVE_SECURITY_PAM_EXT_H)
# include <security/pam_ext.h>
#elif defined(HAVE_PAM_PAM_EXT_H)
# include <pam/pam_ext.h>
#endif
#if defined(HAVE_SECURITY_PAM_MODUTIL_H)
# include <security/pam_modutil.h>
#elif defined(HAVE_PAM_PAM_MODUTIL_H)
# include <pam/pam_modutil.h>
#endif
#include <stdarg.h>
/* Solaris doesn't have these. */
#ifndef PAM_CONV_AGAIN
# define PAM_CONV_AGAIN 0
# define PAM_INCOMPLETE PAM_SERVICE_ERR
#endif
/* Solaris 8 has deficient PAM. */
#ifndef PAM_AUTHTOK_RECOVER_ERR
# define PAM_AUTHTOK_RECOVER_ERR PAM_AUTHTOK_ERR
#endif
/*
* Mac OS X 10 doesn't define these. They're meant to be logically or'd with
* an exit status in pam_set_data, so define them to 0 if not defined to
* deactivate them.
*/
#ifndef PAM_DATA_REPLACE
# define PAM_DATA_REPLACE 0
#endif
#ifndef PAM_DATA_SILENT
# define PAM_DATA_SILENT 0
#endif
/*
* Mac OS X 10 apparently doesn't use PAM_BAD_ITEM and returns PAM_SYMBOL_ERR
* instead.
*/
#ifndef PAM_BAD_ITEM
# define PAM_BAD_ITEM PAM_SYMBOL_ERR
#endif
/* We use this as a limit on password length, so make sure it's defined. */
#ifndef PAM_MAX_RESP_SIZE
# define PAM_MAX_RESP_SIZE 512
#endif
/*
* Some PAM implementations support building the module static and exporting
* the call points via a struct instead. (This is the default in OpenPAM, for
* example.) To support this, the pam_sm_* functions are declared PAM_EXTERN.
* Ensure that's defined for implementations that don't have this.
*/
#ifndef PAM_EXTERN
# define PAM_EXTERN
#endif
BEGIN_DECLS
/* Default to a hidden visibility for all portability functions. */
#pragma GCC visibility push(hidden)
/*
* If pam_modutil_getpwnam is missing, ideally we should roll our own using
* getpwnam_r. However, this is a fair bit of work, since we have to stash
* the allocated memory in the PAM data so that it will be freed properly.
* Bail for right now.
*/
#if !HAVE_PAM_MODUTIL_GETPWNAM
# define pam_modutil_getpwnam(h, u) getpwnam(u)
#endif
/* Prototype missing optional PAM functions. */
#if !HAVE_PAM_SYSLOG
void pam_syslog(const pam_handle_t *, int, const char *, ...);
#endif
#if !HAVE_PAM_VSYSLOG
void pam_vsyslog(const pam_handle_t *, int, const char *, va_list);
#endif
/* Undo default visibility change. */
#pragma GCC visibility pop
END_DECLS
#endif /* !PORTABLE_PAM_H */
+36
View File
@@ -0,0 +1,36 @@
/*
* Replacement for a missing pam_syslog.
*
* Implements pam_syslog in terms of pam_vsyslog (which itself may be a
* replacement) if the PAM implementation does not provide it. This is a
* Linux PAM extension.
*
* The canonical version of this file is maintained in the rra-c-util package,
* which can be found at <https://www.eyrie.org/~eagle/software/rra-c-util/>.
*
* Written by Russ Allbery <eagle@eyrie.org>
* Copyright 2010-2011
* The Board of Trustees of the Leland Stanford Junior University
*
* Copying and distribution of this file, with or without modification, are
* permitted in any medium without royalty provided the copyright notice and
* this notice are preserved. This file is offered as-is, without any
* warranty.
*
* SPDX-License-Identifier: FSFAP
*/
#include <config.h>
#include <portable/pam.h>
#include <stdarg.h>
void
pam_syslog(const pam_handle_t *pamh, int priority, const char *fmt, ...)
{
va_list args;
va_start(args, fmt);
pam_vsyslog(pamh, priority, fmt, args);
va_end(args);
}
+63
View File
@@ -0,0 +1,63 @@
/*
* Replacement for a missing pam_vsyslog.
*
* Provides close to the same functionality as the Linux PAM function
* pam_vsyslog for other PAM implementations. The logging prefix will not be
* quite as good, since we don't have access to the PAM group name.
*
* To use this replacement, the Autoconf script for the package must define
* MODULE_NAME to the name of the PAM module. (PACKAGE isn't used since it
* may use dashes where the module uses underscores.)
*
* The canonical version of this file is maintained in the rra-c-util package,
* which can be found at <https://www.eyrie.org/~eagle/software/rra-c-util/>.
*
* Written by Russ Allbery <eagle@eyrie.org>
* Copyright 2020 Russ Allbery <eagle@eyrie.org>
* Copyright 2010-2011
* The Board of Trustees of the Leland Stanford Junior University
*
* Copying and distribution of this file, with or without modification, are
* permitted in any medium without royalty provided the copyright notice and
* this notice are preserved. This file is offered as-is, without any
* warranty.
*
* SPDX-License-Identifier: FSFAP
*/
#include <config.h>
#include <portable/pam.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <syslog.h>
#ifndef LOG_AUTHPRIV
# define LOG_AUTHPRIV LOG_AUTH
#endif
void
pam_vsyslog(const pam_handle_t *pamh, int priority, const char *fmt,
va_list args)
{
char *msg = NULL;
const char *service = NULL;
int retval;
retval = pam_get_item(pamh, PAM_SERVICE, (PAM_CONST void **) &service);
if (retval != PAM_SUCCESS)
service = NULL;
if (vasprintf(&msg, fmt, args) < 0) {
syslog(LOG_CRIT | LOG_AUTHPRIV,
"cannot allocate memory in vasprintf: %m");
return;
}
/* clang-format off */
syslog(priority | LOG_AUTHPRIV, MODULE_NAME "%s%s%s: %s",
(service == NULL) ? "" : "(",
(service == NULL) ? "" : service,
(service == NULL) ? "" : ")", msg);
/* clang-format on */
free(msg);
}
+64
View File
@@ -0,0 +1,64 @@
/*
* Replacement for a missing reallocarray.
*
* Provides the same functionality as the OpenBSD library function
* reallocarray for those systems that don't have it. This function is the
* same as realloc, but takes the size arguments in the same form as calloc
* and checks for overflow so that the caller doesn't need to.
*
* The canonical version of this file is maintained in the rra-c-util package,
* which can be found at <https://www.eyrie.org/~eagle/software/rra-c-util/>.
*
* Written by Russ Allbery <eagle@eyrie.org>
* Copyright 2017 Russ Allbery <eagle@eyrie.org>
* Copyright 2014
* The Board of Trustees of the Leland Stanford Junior University
*
* Copying and distribution of this file, with or without modification, are
* permitted in any medium without royalty provided the copyright notice and
* this notice are preserved. This file is offered as-is, without any
* warranty.
*
* SPDX-License-Identifier: FSFAP
*/
#include <config.h>
#include <portable/system.h>
#include <errno.h>
/*
* If we're running the test suite, rename reallocarray to avoid conflicts
* with the system version. #undef it first because some systems may define
* it to another name.
*/
#if TESTING
# undef reallocarray
# define reallocarray test_reallocarray
void *test_reallocarray(void *, size_t, size_t);
#endif
/*
* nmemb * size cannot overflow if both are smaller than sqrt(SIZE_MAX). We
* can calculate that value statically by using 2^(sizeof(size_t) * 8) as the
* value of SIZE_MAX and then taking the square root, which gives
* 2^(sizeof(size_t) * 4). Compute the exponentiation with shift.
*/
#define CHECK_THRESHOLD (1UL << (sizeof(size_t) * 4))
void *
reallocarray(void *ptr, size_t nmemb, size_t size)
{
if (nmemb >= CHECK_THRESHOLD || size >= CHECK_THRESHOLD)
if (nmemb > 0 && SIZE_MAX / nmemb <= size) {
errno = ENOMEM;
return NULL;
}
/* Avoid a zero-size allocation. */
if (nmemb == 0 || size == 0) {
nmemb = 1;
size = 1;
}
return realloc(ptr, nmemb * size);
}
+63
View File
@@ -0,0 +1,63 @@
/*
* Portability wrapper around <stdbool.h>.
*
* Provides the bool and _Bool types and the true and false constants,
* following the C99 specification, on hosts that don't have stdbool.h. This
* logic is based heavily on the example in the Autoconf manual.
*
* The canonical version of this file is maintained in the rra-c-util package,
* which can be found at <https://www.eyrie.org/~eagle/software/rra-c-util/>.
*
* Written by Russ Allbery <eagle@eyrie.org>
* Copyright 2008, 2011
* The Board of Trustees of the Leland Stanford Junior University
*
* Copying and distribution of this file, with or without modification, are
* permitted in any medium without royalty provided the copyright notice and
* this notice are preserved. This file is offered as-is, without any
* warranty.
*
* SPDX-License-Identifier: FSFAP
*/
#ifndef PORTABLE_STDBOOL_H
#define PORTABLE_STDBOOL_H 1
/*
* Allow inclusion of config.h to be skipped, since sometimes we have to use a
* stripped-down version of config.h with a different name.
*/
#ifndef CONFIG_H_INCLUDED
# include <config.h>
#endif
#if HAVE_STDBOOL_H
# include <stdbool.h>
#else
# if HAVE__BOOL
# define bool _Bool
# else
# ifdef __cplusplus
typedef bool _Bool;
# elif _WIN32
# include <windef.h>
# define bool BOOL
# else
typedef unsigned char _Bool;
# define bool _Bool
# endif
# endif
# define false 0
# define true 1
# define __bool_true_false_are_defined 1
#endif
/*
* If we define bool and don't tell Perl, it will try to define its own and
* fail. Only of interest for programs that also include Perl headers.
*/
#ifndef HAS_BOOL
# define HAS_BOOL 1
#endif
#endif /* !PORTABLE_STDBOOL_H */
+56
View File
@@ -0,0 +1,56 @@
/*
* Replacement for a missing strndup.
*
* The canonical version of this file is maintained in the rra-c-util package,
* which can be found at <https://www.eyrie.org/~eagle/software/rra-c-util/>.
*
* Written by Russ Allbery <eagle@eyrie.org>
* Copyright 2011-2012
* The Board of Trustees of the Leland Stanford Junior University
*
* Copying and distribution of this file, with or without modification, are
* permitted in any medium without royalty provided the copyright notice and
* this notice are preserved. This file is offered as-is, without any
* warranty.
*
* SPDX-License-Identifier: FSFAP
*/
#include <config.h>
#include <portable/system.h>
#include <errno.h>
/*
* If we're running the test suite, rename the functions to avoid conflicts
* with the system versions.
*/
#if TESTING
# undef strndup
# define strndup test_strndup
char *test_strndup(const char *, size_t);
#endif
char *
strndup(const char *s, size_t n)
{
const char *p;
size_t length;
char *copy;
if (s == NULL) {
errno = EINVAL;
return NULL;
}
/* Don't assume that the source string is nul-terminated. */
for (p = s; (size_t)(p - s) < n && *p != '\0'; p++)
;
length = p - s;
copy = malloc(length + 1);
if (copy == NULL)
return NULL;
memcpy(copy, s, length);
copy[length] = '\0';
return copy;
}
+154
View File
@@ -0,0 +1,154 @@
/*
* Standard system includes and portability adjustments.
*
* Declarations of routines and variables in the C library. Including this
* file is the equivalent of including all of the following headers,
* portably:
*
* #include <inttypes.h>
* #include <limits.h>
* #include <stdarg.h>
* #include <stdbool.h>
* #include <stddef.h>
* #include <stdio.h>
* #include <stdlib.h>
* #include <stdint.h>
* #include <string.h>
* #include <strings.h>
* #include <sys/types.h>
* #include <unistd.h>
*
* Missing functions are provided via #define or prototyped if available from
* the portable helper library. Also provides some standard #defines.
*
* The canonical version of this file is maintained in the rra-c-util package,
* which can be found at <https://www.eyrie.org/~eagle/software/rra-c-util/>.
*
* Written by Russ Allbery <eagle@eyrie.org>
* Copyright 2014, 2016, 2018, 2020 Russ Allbery <eagle@eyrie.org>
* Copyright 2006-2011, 2013-2014
* The Board of Trustees of the Leland Stanford Junior University
*
* Copying and distribution of this file, with or without modification, are
* permitted in any medium without royalty provided the copyright notice and
* this notice are preserved. This file is offered as-is, without any
* warranty.
*
* SPDX-License-Identifier: FSFAP
*/
#ifndef PORTABLE_SYSTEM_H
#define PORTABLE_SYSTEM_H 1
/* Make sure we have our configuration information. */
#include <config.h>
/* BEGIN_DECL and __attribute__. */
#include <portable/macros.h>
/* A set of standard ANSI C headers. We don't care about pre-ANSI systems. */
#if HAVE_INTTYPES_H
# include <inttypes.h>
#endif
#include <limits.h>
#include <stdarg.h>
#include <stddef.h>
#if HAVE_STDINT_H
# include <stdint.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#if HAVE_STRINGS_H
# include <strings.h>
#endif
#include <sys/types.h>
#if HAVE_UNISTD_H
# include <unistd.h>
#endif
/* SCO OpenServer gets int32_t from here. */
#if HAVE_SYS_BITYPES_H
# include <sys/bitypes.h>
#endif
/* Get the bool type. */
#include <portable/stdbool.h>
/* Windows provides snprintf under a different name. */
#ifdef _WIN32
# define snprintf _snprintf
#endif
/* Windows does not define ssize_t. */
#ifndef HAVE_SSIZE_T
typedef ptrdiff_t ssize_t;
#endif
/*
* POSIX requires that these be defined in <unistd.h>. If one of them has
* been defined, all the rest almost certainly have.
*/
#ifndef STDIN_FILENO
# define STDIN_FILENO 0
# define STDOUT_FILENO 1
# define STDERR_FILENO 2
#endif
/*
* C99 requires va_copy. Older versions of GCC provide __va_copy. Per the
* Autoconf manual, memcpy is a generally portable fallback.
*/
#ifndef va_copy
# ifdef __va_copy
# define va_copy(d, s) __va_copy((d), (s))
# else
# define va_copy(d, s) memcpy(&(d), &(s), sizeof(va_list))
# endif
#endif
/*
* If explicit_bzero is not available, fall back on memset. This does NOT
* provide any of the security guarantees of explicit_bzero and will probably
* be optimized away by the compiler. It just ensures that code will compile
* and function on systems without explicit_bzero.
*/
#if !HAVE_EXPLICIT_BZERO
# define explicit_bzero(s, n) memset((s), 0, (n))
#endif
BEGIN_DECLS
/* Default to a hidden visibility for all portability functions. */
#pragma GCC visibility push(hidden)
/*
* Provide prototypes for functions not declared in system headers. Use the
* HAVE_DECL macros for those functions that may be prototyped but implemented
* incorrectly or implemented without a prototype.
*/
#if !HAVE_ASPRINTF
extern int asprintf(char **, const char *, ...)
__attribute__((__format__(printf, 2, 3)));
extern int vasprintf(char **, const char *, va_list)
__attribute__((__format__(printf, 2, 0)));
#endif
#if !HAVE_ISSETUGID
extern int issetugid(void);
#endif
#if !HAVE_MKSTEMP
extern int mkstemp(char *);
#endif
#if !HAVE_DECL_REALLOCARRAY
extern void *reallocarray(void *, size_t, size_t);
#endif
#if !HAVE_STRNDUP
extern char *strndup(const char *, size_t);
#endif
/* Undo default visibility change. */
#pragma GCC visibility pop
END_DECLS
#endif /* !PORTABLE_SYSTEM_H */
+252
View File
@@ -0,0 +1,252 @@
Writing TAP Tests
Introduction
This is a guide for users of the C TAP Harness package or similar
TAP-based test harnesses explaining how to write tests. If your
package uses C TAP Harness as the test suite driver, you may want to
copy this document to an appropriate file name in your test suite as
documentation for contributors.
About TAP
TAP is the Test Anything Protocol, a protocol for communication
between test cases and a test harness. This is the protocol used by
Perl for its internal test suite and for nearly all Perl modules,
since it's the format used by the build tools for Perl modules to run
tests and report their results.
A TAP-based test suite works with a somewhat different set of
assumptions than an xUnit test suite. In TAP, each test case is a
separate program. That program, when run, must produce output in the
following format:
1..4
ok 1 - the first test
ok 2
# a diagnostic, ignored by the harness
not ok 3 - a failing test
ok 4 # skip a skipped test
The output should all go to standard output. The first line specifies
the number of tests to be run, and then each test produces output that
looks like either "ok <n>" or "not ok <n>" depending on whether the
test succeeded or failed. Additional information about the test can
be provided after the "ok <n>" or "not ok <n>", but is optional.
Additional diagnostics and information can be provided in lines
beginning with a "#".
Processing directives are supported after the "ok <n>" or "not ok <n>"
and start with a "#". The main one of interest is "# skip" which says
that the test was skipped rather than successful and optionally gives
the reason. Also supported is "# todo", which normally annotates a
failing test and indicates that test is expected to fail, optionally
providing a reason for why.
There are three more special cases. First, the initial line stating
the number of tests to run, called the plan, may appear at the end of
the output instead of the beginning. This can be useful if the number
of tests to run is not known in advance. Second, a plan in the form:
1..0 # skip entire test case skipped
can be given instead, which indicates that this entire test case has
been skipped (generally because it depends on facilities or optional
configuration which is not present). Finally, if the test case
encounters a fatal error, it should print the text:
Bail out!
on standard output, optionally followed by an error message, and then
exit. This tells the harness that the test aborted unexpectedly.
The exit status of a successful test case should always be 0. The
harness will report the test as "dubious" if all the tests appeared to
succeed but it exited with a non-zero status.
Writing TAP Tests
Environment
One of the special features of C TAP Harness is the environment that
it sets up for your test cases. If your test program is called under
the runtests driver, the environment variables C_TAP_SOURCE and
C_TAP_BUILD will be set to the top of the test directory in the source
tree and the top of the build tree, respectively. You can use those
environment variables to locate additional test data, programs and
libraries built as part of your software build, and other supporting
information needed by tests.
The C and shell TAP libraries support a test_file_path() function,
which looks for a file under the build tree and then under the source
tree, using the C_TAP_BUILD and C_TAP_SOURCE environment variables,
and return the full path to the file. This can be used to locate
supporting data files. They also support a test_tmpdir() function
that returns a directory that can be used for temporary files during
tests.
Perl
Since TAP is the native test framework for Perl, writing TAP tests in
Perl is very easy and extremely well-supported. If you've never
written tests in Perl before, start by reading the documentation for
Test::Tutorial and Test::Simple, which walks you through the basics,
including the TAP output syntax. Then, the best Perl module to use
for serious testing is Test::More, which provides a lot of additional
functions over Test::Simple including support for skipping tests,
bailing out, and not planning tests in advance. See the documentation
of Test::More for all the details and lots of examples.
C TAP Harness can run Perl test scripts directly and interpret the
results correctly, and similarly the Perl Test::Harness module and
prove command can run TAP tests written in other languages using, for
example, the TAP library that comes with C TAP Harness. You can, if
you wish, use the library that comes with C TAP Harness but use prove
instead of runtests for running the test suite.
C
C TAP Harness provides a basic TAP library that takes away most of the
pain of writing TAP test cases in C. A C test case should start with
a call to plan(), passing in the number of tests to run. Then, each
test should use is_int(), is_string(), is_double(), or is_hex() as
appropriate to compare expected and seen values, or ok() to do a
simpler boolean test. The is_*() functions take expected and seen
values and then a printf-style format string explaining the test
(which may be NULL). ok() takes a boolean and then the printf-style
string.
Here's a complete example test program that uses the C TAP library:
#include <stddef.h>
#include <tap/basic.h>
int
main(void)
{
plan(4);
ok(1, "the first test");
is_int(42, 42, NULL);
diag("a diagnostic, ignored by the harness");
ok(0, "a failing test");
skip("a skipped test");
return 0;
}
This test program produces the output shown above in the section on
TAP and demonstrates most of the functions. The other functions of
interest are sysdiag() (like diag() but adds strerror() results),
bail() and sysbail() for fatal errors, skip_block() to skip a whole
block of tests, and skip_all() which is called instead of plan() to
skip an entire test case.
The C TAP library also provides plan_lazy(), which can be called
instead of plan(). If plan_lazy() is called, the library will keep
track of how many test results are reported and will print out the
plan at the end of execution of the program. This should normally be
avoided since the test may appear to be successful even if it exits
prematurely, but it can make writing tests easier in some
circumstances.
Complete API documentation for the basic C TAP library that comes with
C TAP Harness is available at:
<https://www.eyrie.org/~eagle/software/c-tap-harness/>
It's common to need additional test functions and utility functions
for your C tests, particularly if you have to set up and tear down a
test environment for your test programs, and it's useful to have them
all in the libtap library so that you only have to link your test
programs with one library. Rather than editing tap/basic.c and
tap/basic.h to add those additional functions, add additional *.c and
*.h files into the tap directory with the function implementations and
prototypes, and then add those additional objects to the library.
That way, you can update tap/basic.c and tap/basic.h from subsequent
releases of C TAP Harness without having to merge changes with your
own code.
Libraries of additional useful TAP test functions are available in
rra-c-util at:
<https://www.eyrie.org/~eagle/software/rra-c-util/>
Some of the code there is particularly useful when testing programs
that require Kerberos keys.
If you implement new test functions that compare an expected and seen
value, it's best to name them is_<something> and take the expected
value, the seen value, and then a printf-style format string and
possible arguments to match the calling convention of the functions
provided by C TAP Harness.
Shell
C TAP Harness provides a library of shell functions to make it easier
to write TAP tests in shell. That library includes much of the same
functionality as the C TAP library, but takes its parameters in a
somewhat different order to make better use of shell features.
The libtap.sh file should be installed in a directory named tap in
your test suite area. It can then be loaded by tests written in shell
using the environment set up by runtests with:
. "$C_TAP_SOURCE"/tap/libtap.sh
Here is a complete test case written in shell which produces the same
output as the TAP sample above:
#!/bin/sh
. "$C_TAP_SOURCE"/tap/libtap.sh
cd "$C_TAP_BUILD"
plan 4
ok 'the first test' true
ok '' [ 42 -eq 42 ]
diag a diagnostic, ignored by the harness
ok '' false
skip 'a skipped test'
The shell framework doesn't provide the is_* functions, so you'll use
the ok function more. It takes a string describing the text and then
treats all of its remaining arguments as a condition, evaluated the
same way as the arguments to the "if" statement. If that condition
evaluates to true, the test passes; otherwise, the test fails.
The plan, plan_lazy, diag, and bail functions work the same as with
the C library. skip takes a string and skips the next test with that
explanation. skip_block takes a count and a string and skips that
many tests with that explanation. skip_all takes an optional reason
and skips the entire test case.
Since it's common for shell programs to want to test the output of
commands, there's an additional function ok_program provided by the
shell test library. It takes the test description string, the
expected exit status, the expected program output, and then treats the
rest of its arguments as the program to run. That program is run with
standard error and standard output combined, and then its exit status
and output are tested against the provided values.
A utility function, strip_colon_error, is provided that runs the
command given as its arguments and strips text following a colon and a
space from the output (unless there is no whitespace on the line
before the colon and the space, normally indicating a prefix of the
program name). This function can be used to wrap commands that are
expected to fail with output that has a system- or locale-specific
error message appended, such as the output of strerror().
License
This file is part of the documentation of C TAP Harness, which can be
found at <https://www.eyrie.org/~eagle/software/c-tap-harness/>.
Copyright 2010, 2016 Russ Allbery <eagle@eyrie.org>
Copying and distribution of this file, with or without modification,
are permitted in any medium without royalty provided the copyright
notice and this notice are preserved. This file is offered as-is,
without any warranty.
SPDX-License-Identifier: FSFAP
+46
View File
@@ -0,0 +1,46 @@
# Test list for pam-krb5. -*- conf -*-
#
# Written by Russ Allbery <eagle@eyrie.org>
# Copyright 2014, 2017, 2020 Russ Allbery <eagle@eyrie.org>
# Copyright 2011-2012
# The Board of Trustees of the Leland Stanford Junior University
#
# Copying and distribution of this file, with or without modification, are
# permitted in any medium without royalty provided the copyright notice and
# this notice are preserved. This file is offered as-is, without any
# warranty.
#
# SPDX-License-Identifier: FSFAP
# Exclude the tests that use the pkinit.so MIT Kerberos module from valgrind
# testing (module/fast-anon and module/pkinit) because they cause valgrind to
# go into an infinite loop.
docs/pod
docs/pod-spelling
docs/spdx-license
module/alt-auth valgrind
module/bad-authtok valgrind
module/basic valgrind
module/cache valgrind
module/cache-cleanup valgrind
module/expired valgrind
module/fast valgrind
module/fast-anon
module/long valgrind
module/no-cache valgrind
module/pam-user valgrind
module/password valgrind
module/pkinit
module/realm valgrind
module/stacked valgrind
pam-util/args valgrind
pam-util/fakepam valgrind
pam-util/logging valgrind
pam-util/options valgrind
pam-util/vector valgrind
portable/asprintf valgrind
portable/mkstemp valgrind
portable/strndup valgrind
style/obsolete-strings
valgrind/logs
+70
View File
@@ -0,0 +1,70 @@
This directory contains configuration required to run the complete
pam-krb5 test suite. If there is no configuration in this directory, many
of the tests will be skipped. To enable the full test suite, create the
following files:
admin-keytab
A keytab for a principal (in the same realm as the test principal
configured in password) that has admin access to inspect and modify
that test principal. For an MIT Kerberos KDC, it needs "mci"
permissions in kadm5.acl for that principal. For a Heimdal KDC, it
needs "cpw,list,modify" permissions (obviously, "all" will do). This
file is optional; if not present, the tests requiring admin
modification of a principal will be skipped.
krb5.conf
This is optional and not required if the Kerberos realm used for
testing is configured in DNS or your system krb5.conf file and that
file is in either /etc/krb5.conf or /usr/local/etc/krb5.conf.
Otherwise, create a krb5.conf file that contains the realm information
(KDC, kpasswd server, and admin server) for the realm you're using for
testing. You don't need to worry about setting the default realm;
this will be done automatically in the generated file used by the test
suite.
keytab
An optional keytab for a principal, which generally should be in the
same realm as the user configured in the password file. This is used
to test FAST support with a ticket cache.
password
This file should contain two lines. The first line is the
fully-qualified principal (including the realm) of a Kerberos
principal to use for testing authentication. The second line is the
password for that principal.
If the realm of the principal is not configured in either DNS or in
your system krb5.conf file (/usr/local/etc/krb5.conf or
/etc/krb5.conf) with the KDC, kpasswd server, and admin server, you
will need to also provide a krb5.conf file in this directory. See
below.
pkinit-cert
Certificate and private key (concatenated together) for PKINIT
authentication for the user listed in the pkinit-principal file.
Optional; PKINIT checks will be skipped if this file isn't present.
pkinit-principal
Principal to use to test PKINIT authentication. Must be the Kerberos
identity corresponding to the certificate and private key given in
pkinit-cert. Optional; PKINIT checks will be skipped if this file
isn't present.
-----
Copyright 2017, 2020 Russ Allbery <eagle@eyrie.org>
Copyright 2011-2012
The Board of Trustees of the Leland Stanford Junior University
Copying and distribution of this file, with or without modification, are
permitted in any medium without royalty provided the copyright notice and
this notice are preserved. This file is offered as-is, without any
warranty.
SPDX-License-Identifier: FSFAP
+72
View File
@@ -0,0 +1,72 @@
// Suppressions file for cppcheck. -*- conf -*-
//
// This includes suppressions for all of my projects, including files that
// aren't in rra-c-util, for ease of sharing between projects. The ones that
// don't apply to a particular project should hopefully be harmless.
//
// To determine the correct suppression to add for a new error, run cppcheck
// with the --xml flag and then add a suppression for the error id, file
// location, and line.
//
// Copyright 2018-2021 Russ Allbery <eagle@eyrie.org>
//
// Copying and distribution of this file, with or without modification, are
// permitted in any medium without royalty provided the copyright notice and
// this notice are preserved. This file is offered as-is, without any
// warranty.
//
// SPDX-License-Identifier: FSFAP
// I like declaring variables at the top of a function rather than cluttering
// every if and loop body with declarations.
variableScope
// strlen of a constant string is more maintainable code than hard-coding the
// string length.
constArgument:tests/runtests.c:804
// False positive due to recursive function.
knownConditionTrueFalse:portable/getopt.c:146
// Bug in cppcheck 2.3. cppcheck can't see the assignment because of the
// void * cast.
knownConditionTrueFalse:portable/k_haspag.c:61
// False positive since the string comes from a command-line define.
knownConditionTrueFalse:tests/tap/process.c:415
knownConditionTrueFalse:tests/tap/remctl.c:79
// Stored in the returned ai struct, but cppcheck can't see the assignment
// because of the struct sockaddr * cast.
memleak:portable/getaddrinfo.c:236
// Bug in cppcheck 1.89 (fixed in 2.3). The address of this variable is
// passed to a Windows function (albeit through a cast).
nullPointer:portable/winsock.c:61
// Bug in cppcheck 2.3.
nullPointerRedundantCheck:portable/krb5-profile.c:61
// Bug in cppcheck 2.3.
nullPointerRedundantCheck:portable/krb5-renew.c:82
nullPointerRedundantCheck:portable/krb5-renew.c:83
// Setting the variable to NULL explicitly after deallocation.
redundantAssignment:tests/pam-util/options-t.c
// (remctl) Bug in cppcheck 1.89 (fixed in 2.3). The address of these
// variables are passed to a PHP function.
uninitvar:php/php_remctl.c:119
uninitvar:php/php_remctl.c:123
uninitvar:php/php_remctl.c:315
uninitvar:php/php5_remctl.c:125
uninitvar:php/php5_remctl.c:129
uninitvar:php/php5_remctl.c:321
// (remctl) Bug in cppcheck 1.82. A pointer to this array is stored in a
// struct that's passed to another function.
redundantAssignment:tests/server/acl-t.c
// (pam-krb5) cppcheck doesn't recognize the unused attribute on labels.
unusedLabel:module/auth.c:895
unusedLabelConfiguration:module/auth.c:895
+86
View File
@@ -0,0 +1,86 @@
#!/bin/sh
# Generate a krb5.conf file in the current directory for testing purposes.
# Takes one command-line argument: the default realm to use. Strips out the
# entire [appdefaults] section to avoid picking up any local configuration and
# sets the default realm as indicated.
#
# The canonical version of this file is maintained in the rra-c-util package,
# which can be found at <https://www.eyrie.org/~eagle/software/rra-c-util/>.
#
# Written by Russ Allbery <eagle@eyrie.org>
# Copyright 2016, 2020 Russ Allbery <eagle@eyrie.org>
# Copyright 2006-2008, 2010-2011
# The Board of Trustees of the Leland Stanford Junior University
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to
# deal in the Software without restriction, including without limitation the
# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
# sell copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
# IN THE SOFTWARE.
#
# SPDX-License-Identifier: MIT
set -e
# Load the test library.
. "$C_TAP_SOURCE/tap/libtap.sh"
cd "$C_TAP_BUILD"
# If there is no default realm specified on the command line, we leave the
# realm information alone.
realm="$1"
# Locate the krb5.conf file to use as a base. Prefer the one in the test
# configuration area, if it exists.
krb5conf=`test_file_path config/krb5.conf`
if [ -z "$krb5conf" ] ; then
for p in /etc/krb5.conf /usr/local/etc/krb5.conf ; do
if [ -r "$p" ] ; then
krb5conf="$p"
break
fi
done
fi
if [ -z "$krb5conf" ] ; then
echo 'no krb5.conf found, see test instructions' >&2
exit 1
fi
# We found a krb5.conf file. Generate our munged one.
mkdir -p tmp
awk '
BEGIN { skip = 0 }
/^ *\[appdefaults\]/ { skip = 1 }
!/^ *\[appdefaults\]/ && / *\[/ { skip = 0 }
{ if (skip == 0) print }
' "$krb5conf" > tmp/krb5.conf.tmp
if [ -n "$realm" ] ; then
pattern='^[ ]*default_realm.*='
if grep "$pattern" tmp/krb5.conf.tmp >/dev/null 2>/dev/null; then
sed -e "s/\\(default_realm.*=\\) .*/\\1 $realm/" \
tmp/krb5.conf.tmp >tmp/krb5.conf
else
(
cat tmp/krb5.conf.tmp
echo "[libdefaults]"
echo " default_realm = $realm"
) >tmp/krb5.conf
fi
rm tmp/krb5.conf.tmp
else
mv tmp/krb5.conf.tmp tmp/krb5.conf
fi
+30
View File
@@ -0,0 +1,30 @@
# Test krb5.conf file for PAM option parsing.
[appdefaults]
FOO.COM = {
program = /bin/false
}
BAR.COM = {
program = echo /bin/true
}
testing = {
minimum_uid = 1000
ignore_root = false
expires = 30m
FOO.COM = {
cells = foo.com,bar.com
}
BAR.COM = {
cells = bar.com foo.com
}
}
other-test = {
minimum_uid = -1000
}
bad-number = {
minimum_uid = 1000foo
}
bad-time = {
expires = ft87
}
debug = true
+30
View File
@@ -0,0 +1,30 @@
# Test krb5.conf file for PAM option parsing.
[appdefaults]
FOO.COM = {
program = /bin/false
}
BAR.COM = {
program = echo /bin/true
}
testing = {
minimum_uid = 1000
ignore_root = false
expires = 30m
FOO.COM = {
cells = foo.com,bar.com
}
BAR.COM = {
cells = bar.com foo.com
}
}
other-test = {
minimum_uid = -1000
}
bad-number = {
minimum_uid = 1000foo
}
bad-time = {
expires = ft87
}
debug = true
+19
View File
@@ -0,0 +1,19 @@
# Configuration for Perl tests. -*- perl -*-
# Ignore these top-level directories for perlcritic testing.
@CRITIC_IGNORE = qw();
# Add this directory (or a .libs subdirectory) relative to the top of the
# source tree to LD_LIBRARY_PATH when checking the syntax of Perl modules.
# This may be required to pick up libraries that are used by in-tree Perl
# modules.
#$LIBRARY_PATH = 'lib';
# Default minimum version requirement for included Perl scripts.
$MINIMUM_VERSION = '5.006';
# Minimum version exceptions for specific top-level directories.
%MINIMUM_VERSION = ();
# File must end with this line.
1;
+19
View File
@@ -0,0 +1,19 @@
# Test simplest case of alternative authentication principal. -*- conf -*-
#
# Written by Russ Allbery <eagle@eyrie.org>
# Copyright 2014, 2020 Russ Allbery <eagle@eyrie.org>
# Copyright 2010-2011
# The Board of Trustees of the Leland Stanford Junior University
#
# SPDX-License-Identifier: BSD-3-clause or GPL-1+
[options]
auth = alt_auth_map=%1 force_first_pass no_ccache
account = alt_auth_map=%1 no_ccache
[run]
authenticate = PAM_SUCCESS
acct_mgmt = PAM_SUCCESS
[output]
INFO user %u authenticated as %1
+25
View File
@@ -0,0 +1,25 @@
# Test simplest case of alternative authentication principal. -*- conf -*-
#
# Written by Russ Allbery <eagle@eyrie.org>
# Copyright 2014, 2020 Russ Allbery <eagle@eyrie.org>
# Copyright 2010-2011
# The Board of Trustees of the Leland Stanford Junior University
#
# SPDX-License-Identifier: BSD-3-clause or GPL-1+
[options]
auth = alt_auth_map=%1 force_first_pass no_ccache debug
account = alt_auth_map=%1 no_ccache debug
[run]
authenticate = PAM_SUCCESS
acct_mgmt = PAM_SUCCESS
[output]
DEBUG pam_sm_authenticate: entry
DEBUG (user %u) mapping bogus-nonexistent-account to %1
DEBUG (user %u) alternate authentication successful
INFO user %u authenticated as %1
DEBUG pam_sm_authenticate: exit (success)
DEBUG pam_sm_acct_mgmt: entry
DEBUG pam_sm_acct_mgmt: exit (success)
+19
View File
@@ -0,0 +1,19 @@
# Test failure of alternative authentication principal. -*- conf -*-
#
# Written by Russ Allbery <eagle@eyrie.org>
# Copyright 2014, 2020 Russ Allbery <eagle@eyrie.org>
# Copyright 2010-2011
# The Board of Trustees of the Leland Stanford Junior University
#
# SPDX-License-Identifier: BSD-3-clause or GPL-1+
[options]
auth = alt_auth_map=bogus force_first_pass no_ccache
account = alt_auth_map=bogus no_ccache
[run]
authenticate = PAM_AUTHINFO_UNAVAIL
acct_mgmt = PAM_IGNORE
[output]
NOTICE authentication failure; logname=%u uid=%i euid=%i tty= ruser= rhost=
+28
View File
@@ -0,0 +1,28 @@
# Test failure of alternative authentication principal. -*- conf -*-
#
# Written by Russ Allbery <eagle@eyrie.org>
# Copyright 2014, 2020 Russ Allbery <eagle@eyrie.org>
# Copyright 2010-2011
# The Board of Trustees of the Leland Stanford Junior University
#
# SPDX-License-Identifier: BSD-3-clause or GPL-1+
[options]
auth = alt_auth_map=bogus force_first_pass no_ccache debug
account = alt_auth_map=bogus no_ccache debug
[run]
authenticate = PAM_AUTHINFO_UNAVAIL
acct_mgmt = PAM_IGNORE
[output]
DEBUG pam_sm_authenticate: entry
DEBUG (user %u) mapping bogus-nonexistent-account to bogus@%2
DEBUG /^\(user %u\) alternate authentication failed: /
DEBUG (user %u) attempting authentication as %u@%2
DEBUG /^\(user %u\) krb5_get_init_creds_password: /
NOTICE authentication failure; logname=%u uid=%i euid=%i tty= ruser= rhost=
DEBUG pam_sm_authenticate: exit (failure)
DEBUG pam_sm_acct_mgmt: entry
DEBUG skipping non-Kerberos login
DEBUG pam_sm_acct_mgmt: exit (ignore)
+25
View File
@@ -0,0 +1,25 @@
# Test alternative authentication principal. -*- conf -*-
#
# Written by Russ Allbery <eagle@eyrie.org>
# Copyright 2014, 2020 Russ Allbery <eagle@eyrie.org>
# Copyright 2010-2011
# The Board of Trustees of the Leland Stanford Junior University
#
# SPDX-License-Identifier: BSD-3-clause or GPL-1+
[options]
auth = alt_auth_map=%%s/unknown-user no_ccache
account = alt_auth_map=%%s/unknown-user no_ccache
session = no_ccache
[run]
authenticate = PAM_SUCCESS
acct_mgmt = PAM_SUCCESS
open_session = PAM_SUCCESS
close_session = PAM_SUCCESS
[prompts]
echo_off = Password: |%p
[output]
INFO user %u authenticated as %u
@@ -0,0 +1,38 @@
# Test alternative authentication principal with debug logging. -*- conf -*-
#
# Written by Russ Allbery <eagle@eyrie.org>
# Copyright 2014, 2020 Russ Allbery <eagle@eyrie.org>
# Copyright 2010-2011
# The Board of Trustees of the Leland Stanford Junior University
#
# SPDX-License-Identifier: BSD-3-clause or GPL-1+
[options]
auth = alt_auth_map=%%s/unknown-user no_ccache debug
account = alt_auth_map=%%s/unknown-user no_ccache debug
session = no_ccache debug
[run]
authenticate = PAM_SUCCESS
acct_mgmt = PAM_SUCCESS
open_session = PAM_SUCCESS
close_session = PAM_SUCCESS
[prompts]
echo_off = Password: |%p
[output]
DEBUG pam_sm_authenticate: entry
DEBUG (user %u) mapping %u to %0/unknown-user@%2
DEBUG /^\(user %u\) alternate authentication failed: /
DEBUG (user %u) attempting authentication as %u
DEBUG (user %u) mapped user %0/unknown-user@%2 does not match principal %u
INFO user %u authenticated as %u
DEBUG pam_sm_authenticate: exit (success)
DEBUG pam_sm_acct_mgmt: entry
DEBUG (user %u) mapped user %0/unknown-user@%2 does not match principal %u
DEBUG pam_sm_acct_mgmt: exit (success)
DEBUG pam_sm_open_session: entry
DEBUG pam_sm_open_session: exit (success)
DEBUG pam_sm_close_session: entry
DEBUG pam_sm_close_session: exit (success)
@@ -0,0 +1,25 @@
# Test alternative authentication principal. -*- conf -*-
#
# Written by Russ Allbery <eagle@eyrie.org>
# Copyright 2014, 2020 Russ Allbery <eagle@eyrie.org>
# Copyright 2010-2011
# The Board of Trustees of the Leland Stanford Junior University
#
# SPDX-License-Identifier: BSD-3-clause or GPL-1+
[options]
auth = alt_auth_map=%%s@BOGUS.EXAMPLE.COM no_ccache
account = alt_auth_map=%%s@BOGUS.EXAMPLE.COM no_ccache
session = no_ccache
[run]
authenticate = PAM_SUCCESS
acct_mgmt = PAM_SUCCESS
open_session = PAM_SUCCESS
close_session = PAM_SUCCESS
[prompts]
echo_off = Password: |%p
[output]
INFO user %u authenticated as %u
+19
View File
@@ -0,0 +1,19 @@
# Test forced alternative authentication principal. -*- conf -*-
#
# Written by Russ Allbery <eagle@eyrie.org>
# Copyright 2014, 2020 Russ Allbery <eagle@eyrie.org>
# Copyright 2010-2011
# The Board of Trustees of the Leland Stanford Junior University
#
# SPDX-License-Identifier: BSD-3-clause or GPL-1+
[options]
auth = alt_auth_map=%1 force_alt_auth force_first_pass no_ccache
account = alt_auth_map=%1 no_ccache
[run]
authenticate = PAM_SUCCESS
acct_mgmt = PAM_SUCCESS
[output]
INFO user %u authenticated as %1
@@ -0,0 +1,26 @@
# Test failure of forced authentication principal (no fallback). -*- conf -*-
#
# Written by Russ Allbery <eagle@eyrie.org>
# Copyright 2014, 2020 Russ Allbery <eagle@eyrie.org>
# Copyright 2010-2011
# The Board of Trustees of the Leland Stanford Junior University
#
# SPDX-License-Identifier: BSD-3-clause or GPL-1+
[options]
auth = alt_auth_map=%1 force_alt_auth force_first_pass no_ccache debug
account = alt_auth_map=%1 no_ccache debug
[run]
authenticate = PAM_AUTH_ERR
acct_mgmt = PAM_IGNORE
[output]
DEBUG pam_sm_authenticate: entry
DEBUG (user %u) mapping bogus-nonexistent-account to %1
DEBUG /^\(user %u\) alternate authentication failed: /
NOTICE authentication failure; logname=%u uid=%i euid=%i tty= ruser= rhost=
DEBUG pam_sm_authenticate: exit (failure)
DEBUG pam_sm_acct_mgmt: entry
DEBUG skipping non-Kerberos login
DEBUG pam_sm_acct_mgmt: exit (ignore)
@@ -0,0 +1,25 @@
# Test forced alternative authentication with fallback. -*- conf -*-
#
# Written by Russ Allbery <eagle@eyrie.org>
# Copyright 2014, 2020 Russ Allbery <eagle@eyrie.org>
# Copyright 2010-2011
# The Board of Trustees of the Leland Stanford Junior University
#
# SPDX-License-Identifier: BSD-3-clause or GPL-1+
[options]
auth = alt_auth_map=%%s/unknown-user force_alt_auth no_ccache
account = alt_auth_map=%%s/unknown-user no_ccache
session = no_ccache
[run]
authenticate = PAM_SUCCESS
acct_mgmt = PAM_SUCCESS
open_session = PAM_SUCCESS
close_session = PAM_SUCCESS
[prompts]
echo_off = Password: |%p
[output]
INFO user %u authenticated as %u
+19
View File
@@ -0,0 +1,19 @@
# Test required alternative authentication principal. -*- conf -*-
#
# Written by Russ Allbery <eagle@eyrie.org>
# Copyright 2014, 2020 Russ Allbery <eagle@eyrie.org>
# Copyright 2010-2011
# The Board of Trustees of the Leland Stanford Junior University
#
# SPDX-License-Identifier: BSD-3-clause or GPL-1+
[options]
auth = alt_auth_map=%1 only_alt_auth force_first_pass no_ccache
account = alt_auth_map=%1 no_ccache
[run]
authenticate = PAM_SUCCESS
acct_mgmt = PAM_SUCCESS
[output]
INFO user %u authenticated as %1
+22
View File
@@ -0,0 +1,22 @@
# Test failure of required alternative authentication. -*- conf -*-
#
# Written by Russ Allbery <eagle@eyrie.org>
# Copyright 2014, 2020 Russ Allbery <eagle@eyrie.org>
# Copyright 2010-2011
# The Board of Trustees of the Leland Stanford Junior University
#
# SPDX-License-Identifier: BSD-3-clause or GPL-1+
[options]
auth = alt_auth_map=bogus only_alt_auth no_ccache
account = alt_auth_map=bogus no_ccache
[run]
authenticate = PAM_USER_UNKNOWN
acct_mgmt = PAM_IGNORE
[prompts]
echo_off = Password: |%p
[output]
NOTICE authentication failure; logname=%u uid=%i euid=%i tty= ruser= rhost=
+19
View File
@@ -0,0 +1,19 @@
# Test username mapping of alternative authentication principal. -*- conf -*-
#
# Written by Russ Allbery <eagle@eyrie.org>
# Copyright 2014, 2020 Russ Allbery <eagle@eyrie.org>
# Copyright 2010-2011
# The Board of Trustees of the Leland Stanford Junior University
#
# SPDX-License-Identifier: BSD-3-clause or GPL-1+
[options]
auth = alt_auth_map=%%s@%2 force_first_pass no_ccache
account = alt_auth_map=%%s@%2 no_ccache
[run]
authenticate = PAM_SUCCESS
acct_mgmt = PAM_SUCCESS
[output]
INFO user %u authenticated as %1
@@ -0,0 +1,19 @@
# Test username mapping of alternative authentication principal. -*- conf -*-
#
# Written by Russ Allbery <eagle@eyrie.org>
# Copyright 2014, 2020 Russ Allbery <eagle@eyrie.org>
# Copyright 2010-2011
# The Board of Trustees of the Leland Stanford Junior University
#
# SPDX-License-Identifier: BSD-3-clause or GPL-1+
[options]
auth = alt_auth_map=%3%%s@%2 force_first_pass no_ccache
account = alt_auth_map=%3%%s@%2 no_ccache
[run]
authenticate = PAM_SUCCESS
acct_mgmt = PAM_SUCCESS
[output]
INFO user %u authenticated as %1

Some files were not shown because too many files have changed in this diff Show More