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:
@@ -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
|
||||
---
|
||||
@@ -0,0 +1,6 @@
|
||||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: "github-actions"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
@@ -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 }}
|
||||
@@ -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
@@ -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
|
||||
@@ -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.
|
||||
@@ -0,0 +1,665 @@
|
||||
# pam-krb5
|
||||
|
||||
[](https://github.com/rra/pam-krb5/actions)
|
||||
[](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.
|
||||
@@ -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.
|
||||
@@ -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
|
||||
@@ -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.
|
||||
@@ -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'
|
||||
@@ -0,0 +1 @@
|
||||
test/admin@HEIMDAL.TEST all testuser@HEIMDAL.TEST
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
@@ -0,0 +1 @@
|
||||
testuser@HEIMDAL.TEST:UID=testuser,DC=HEIMDAL,DC=TEST
|
||||
@@ -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}
|
||||
@@ -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}
|
||||
@@ -0,0 +1 @@
|
||||
test/admin@MIT.TEST mci testuser@MIT.TEST
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
@@ -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
|
||||
Executable
+105
@@ -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
|
||||
Executable
+102
@@ -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
|
||||
@@ -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
@@ -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
|
||||
@@ -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
File diff suppressed because it is too large
Load Diff
+131
@@ -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
@@ -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
@@ -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.])])])
|
||||
@@ -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])])
|
||||
@@ -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
@@ -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.])])])
|
||||
@@ -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])])
|
||||
@@ -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])])])
|
||||
@@ -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"])])])
|
||||
@@ -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/^ *//'`])
|
||||
@@ -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.])])
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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
File diff suppressed because it is too large
Load Diff
+185
@@ -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;
|
||||
}
|
||||
@@ -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
@@ -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 */
|
||||
@@ -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 */
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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:
|
||||
*;
|
||||
};
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
@@ -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);
|
||||
}
|
||||
@@ -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 */
|
||||
@@ -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 */
|
||||
@@ -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 */
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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 */
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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 */
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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 */
|
||||
@@ -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
|
||||
@@ -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
@@ -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 */
|
||||
@@ -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 */
|
||||
@@ -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
@@ -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 */
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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 */
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
@@ -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
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
Executable
+86
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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;
|
||||
@@ -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
|
||||
@@ -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)
|
||||
@@ -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=
|
||||
@@ -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)
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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=
|
||||
@@ -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
Reference in New Issue
Block a user