From 46988a0ecd54772d66eec56f0d4d6a35a553f49b Mon Sep 17 00:00:00 2001 From: christos Date: Tue, 3 Mar 2020 00:18:44 +0000 Subject: [PATCH] Import Yubico's pam-u2f from https://github.com/Yubico/pam-u2f.git --- external/bsd/pam-u2f/dist/.clang-format | 106 ++ external/bsd/pam-u2f/dist/.travis.yml | 253 ++++ external/bsd/pam-u2f/dist/AUTHORS | 1 + external/bsd/pam-u2f/dist/COPYING | 26 + external/bsd/pam-u2f/dist/Makefile.am | 76 ++ external/bsd/pam-u2f/dist/NEWS | 48 + external/bsd/pam-u2f/dist/README | 272 ++++ external/bsd/pam-u2f/dist/autogen.sh | 12 + external/bsd/pam-u2f/dist/b64.c | 108 ++ external/bsd/pam-u2f/dist/b64.h | 11 + .../dist/build-aux/ci/build-bionic-clang7.sh | 6 + .../dist/build-aux/ci/build-bionic-clang8.sh | 6 + .../dist/build-aux/ci/build-bionic-gcc7.sh | 10 + .../dist/build-aux/ci/build-bionic-gcc8.sh | 6 + .../dist/build-aux/ci/build-bionic-gcc9.sh | 6 + .../pam-u2f/dist/build-aux/ci/build-linux.sh | 10 + .../pam-u2f/dist/build-aux/ci/build-osx.sh | 30 + .../dist/build-aux/ci/build-osx11-clang.sh | 6 + .../dist/build-aux/ci/build-osx9.4-clang.sh | 6 + .../dist/build-aux/ci/build-xenial-clang7.sh | 6 + .../dist/build-aux/ci/build-xenial-clang8.sh | 6 + .../dist/build-aux/ci/build-xenial-gcc7.sh | 6 + .../dist/build-aux/ci/build-xenial-gcc8.sh | 6 + .../dist/build-aux/ci/build-xenial-gcc9.sh | 6 + .../pam-u2f/dist/build-aux/ci/format-code.sh | 38 + external/bsd/pam-u2f/dist/configure.ac | 114 ++ external/bsd/pam-u2f/dist/drop_privs.c | 131 ++ external/bsd/pam-u2f/dist/drop_privs.h | 66 + external/bsd/pam-u2f/dist/explicit_bzero.c | 53 + .../pam-u2f/dist/m4/ax_check_compile_flag.m4 | 73 + external/bsd/pam-u2f/dist/m4/lib-ld.m4 | 110 ++ external/bsd/pam-u2f/dist/m4/lib-link.m4 | 758 +++++++++++ external/bsd/pam-u2f/dist/m4/lib-prefix.m4 | 221 +++ external/bsd/pam-u2f/dist/man/pam_u2f.8.txt | 142 ++ external/bsd/pam-u2f/dist/man/pamu2fcfg.1.txt | 60 + external/bsd/pam-u2f/dist/pam-u2f.c | 472 +++++++ .../bsd/pam-u2f/dist/pamu2fcfg/Makefile.am | 31 + .../pam-u2f/dist/pamu2fcfg/_readpassphrase.h | 42 + .../bsd/pam-u2f/dist/pamu2fcfg/cmdline.ggo | 18 + .../bsd/pam-u2f/dist/pamu2fcfg/pamu2fcfg.c | 347 +++++ .../pam-u2f/dist/pamu2fcfg/readpassphrase.c | 214 +++ external/bsd/pam-u2f/dist/tests/Makefile.am | 12 + external/bsd/pam-u2f/dist/tests/basic.c | 30 + .../bsd/pam-u2f/dist/tests/bionic/Dockerfile | 15 + external/bsd/pam-u2f/dist/tests/bionic/README | 18 + external/bsd/pam-u2f/dist/tests/bionic/run.sh | 63 + external/bsd/pam-u2f/dist/util.c | 1202 +++++++++++++++++ external/bsd/pam-u2f/dist/util.h | 80 ++ 48 files changed, 5339 insertions(+) create mode 100644 external/bsd/pam-u2f/dist/.clang-format create mode 100644 external/bsd/pam-u2f/dist/.travis.yml create mode 100644 external/bsd/pam-u2f/dist/AUTHORS create mode 100644 external/bsd/pam-u2f/dist/COPYING create mode 100644 external/bsd/pam-u2f/dist/Makefile.am create mode 100644 external/bsd/pam-u2f/dist/NEWS create mode 100644 external/bsd/pam-u2f/dist/README create mode 100755 external/bsd/pam-u2f/dist/autogen.sh create mode 100644 external/bsd/pam-u2f/dist/b64.c create mode 100644 external/bsd/pam-u2f/dist/b64.h create mode 100755 external/bsd/pam-u2f/dist/build-aux/ci/build-bionic-clang7.sh create mode 100755 external/bsd/pam-u2f/dist/build-aux/ci/build-bionic-clang8.sh create mode 100755 external/bsd/pam-u2f/dist/build-aux/ci/build-bionic-gcc7.sh create mode 100755 external/bsd/pam-u2f/dist/build-aux/ci/build-bionic-gcc8.sh create mode 100755 external/bsd/pam-u2f/dist/build-aux/ci/build-bionic-gcc9.sh create mode 100755 external/bsd/pam-u2f/dist/build-aux/ci/build-linux.sh create mode 100755 external/bsd/pam-u2f/dist/build-aux/ci/build-osx.sh create mode 100755 external/bsd/pam-u2f/dist/build-aux/ci/build-osx11-clang.sh create mode 100755 external/bsd/pam-u2f/dist/build-aux/ci/build-osx9.4-clang.sh create mode 100755 external/bsd/pam-u2f/dist/build-aux/ci/build-xenial-clang7.sh create mode 100755 external/bsd/pam-u2f/dist/build-aux/ci/build-xenial-clang8.sh create mode 100755 external/bsd/pam-u2f/dist/build-aux/ci/build-xenial-gcc7.sh create mode 100755 external/bsd/pam-u2f/dist/build-aux/ci/build-xenial-gcc8.sh create mode 100755 external/bsd/pam-u2f/dist/build-aux/ci/build-xenial-gcc9.sh create mode 100755 external/bsd/pam-u2f/dist/build-aux/ci/format-code.sh create mode 100644 external/bsd/pam-u2f/dist/configure.ac create mode 100644 external/bsd/pam-u2f/dist/drop_privs.c create mode 100644 external/bsd/pam-u2f/dist/drop_privs.h create mode 100644 external/bsd/pam-u2f/dist/explicit_bzero.c create mode 100644 external/bsd/pam-u2f/dist/m4/ax_check_compile_flag.m4 create mode 100644 external/bsd/pam-u2f/dist/m4/lib-ld.m4 create mode 100644 external/bsd/pam-u2f/dist/m4/lib-link.m4 create mode 100644 external/bsd/pam-u2f/dist/m4/lib-prefix.m4 create mode 100644 external/bsd/pam-u2f/dist/man/pam_u2f.8.txt create mode 100644 external/bsd/pam-u2f/dist/man/pamu2fcfg.1.txt create mode 100644 external/bsd/pam-u2f/dist/pam-u2f.c create mode 100644 external/bsd/pam-u2f/dist/pamu2fcfg/Makefile.am create mode 100644 external/bsd/pam-u2f/dist/pamu2fcfg/_readpassphrase.h create mode 100644 external/bsd/pam-u2f/dist/pamu2fcfg/cmdline.ggo create mode 100644 external/bsd/pam-u2f/dist/pamu2fcfg/pamu2fcfg.c create mode 100644 external/bsd/pam-u2f/dist/pamu2fcfg/readpassphrase.c create mode 100644 external/bsd/pam-u2f/dist/tests/Makefile.am create mode 100644 external/bsd/pam-u2f/dist/tests/basic.c create mode 100644 external/bsd/pam-u2f/dist/tests/bionic/Dockerfile create mode 100644 external/bsd/pam-u2f/dist/tests/bionic/README create mode 100755 external/bsd/pam-u2f/dist/tests/bionic/run.sh create mode 100644 external/bsd/pam-u2f/dist/util.c create mode 100644 external/bsd/pam-u2f/dist/util.h diff --git a/external/bsd/pam-u2f/dist/.clang-format b/external/bsd/pam-u2f/dist/.clang-format new file mode 100644 index 000000000000..21ccbda7c5de --- /dev/null +++ b/external/bsd/pam-u2f/dist/.clang-format @@ -0,0 +1,106 @@ +--- +Language: Cpp +AccessModifierOffset: -2 +AlignAfterOpenBracket: Align +AlignConsecutiveAssignments: false +AlignConsecutiveDeclarations: false +AlignEscapedNewlines: Right +AlignOperands: true +AlignTrailingComments: true +AllowAllParametersOfDeclarationOnNextLine: true +AllowShortBlocksOnASingleLine: false +AllowShortCaseLabelsOnASingleLine: false +AllowShortFunctionsOnASingleLine: All +AllowShortIfStatementsOnASingleLine: false +AllowShortLoopsOnASingleLine: false +AlwaysBreakAfterDefinitionReturnType: None +AlwaysBreakAfterReturnType: None +AlwaysBreakBeforeMultilineStrings: false +AlwaysBreakTemplateDeclarations: false +BinPackArguments: true +BinPackParameters: true +BraceWrapping: + AfterClass: false + AfterControlStatement: false + AfterEnum: false + AfterFunction: false + AfterNamespace: false + AfterObjCDeclaration: false + AfterStruct: false + AfterUnion: false + BeforeCatch: false + BeforeElse: false + IndentBraces: false + SplitEmptyFunction: true + SplitEmptyRecord: true + SplitEmptyNamespace: true +BreakBeforeBinaryOperators: None +BreakBeforeBraces: Attach +BreakBeforeInheritanceComma: false +BreakBeforeTernaryOperators: true +BreakConstructorInitializersBeforeComma: false +BreakConstructorInitializers: BeforeColon +BreakAfterJavaFieldAnnotations: false +BreakStringLiterals: true +ColumnLimit: 80 +CommentPragmas: '^ IWYU pragma:' +CompactNamespaces: false +ConstructorInitializerAllOnOneLineOrOnePerLine: false +ConstructorInitializerIndentWidth: 2 +ContinuationIndentWidth: 2 +Cpp11BracedListStyle: true +DerivePointerAlignment: false +DisableFormat: false +ExperimentalAutoDetectBinPacking: false +FixNamespaceComments: true +ForEachMacros: + - foreach + - Q_FOREACH + - BOOST_FOREACH +IncludeCategories: + - Regex: '^"(llvm|llvm-c|clang|clang-c)/' + Priority: 2 + - Regex: '^(<|"(gtest|isl|json)/)' + Priority: 3 + - Regex: '.*' + Priority: 1 +IncludeIsMainRegex: '(Test)?$' +IndentCaseLabels: true +IndentWidth: 2 +IndentWrappedFunctionNames: false +JavaScriptQuotes: Leave +JavaScriptWrapImports: true +KeepEmptyLinesAtTheStartOfBlocks: true +MacroBlockBegin: '' +MacroBlockEnd: '' +MaxEmptyLinesToKeep: 1 +NamespaceIndentation: None +ObjCBlockIndentWidth: 2 +ObjCSpaceAfterProperty: false +ObjCSpaceBeforeProtocolList: true +PenaltyBreakAssignment: 2 +PenaltyBreakBeforeFirstCallParameter: 1000 +PenaltyBreakComment: 300 +PenaltyBreakFirstLessLess: 120 +PenaltyBreakString: 1000 +PenaltyExcessCharacter: 1000000 +PenaltyReturnTypeOnItsOwnLine: 60 +PointerAlignment: Right +ReflowComments: true +SortIncludes: false +SortUsingDeclarations: true +SpaceAfterCStyleCast: true +SpaceAfterTemplateKeyword: true +SpaceBeforeAssignmentOperators: true +SpaceBeforeParens: ControlStatements +SpaceInEmptyParentheses: false +SpacesBeforeTrailingComments: 1 +SpacesInAngles: false +SpacesInContainerLiterals: true +SpacesInCStyleCastParentheses: false +SpacesInParentheses: false +SpacesInSquareBrackets: false +Standard: Cpp11 +TabWidth: 8 +UseTab: Never +... diff --git a/external/bsd/pam-u2f/dist/.travis.yml b/external/bsd/pam-u2f/dist/.travis.yml new file mode 100644 index 000000000000..f396b6b253a1 --- /dev/null +++ b/external/bsd/pam-u2f/dist/.travis.yml @@ -0,0 +1,253 @@ +language: c + +matrix: + include: + - name: Xenial gcc-7 + os: linux + compiler: gcc-7 + dist: xenial + addons: + apt: + sources: + - sourceline: 'ppa:yubico/stable' + - ubuntu-toolchain-r-test + packages: + - autoconf + - check + - gcc-7 + - gengetopt + - help2man + - libfido2-dev + - libpam-dev + - libssl-dev + - libtool + - pkg-config + script: ./build-aux/ci/build-xenial-gcc7.sh + - name: Xenial gcc-8 + os: linux + compiler: gcc-8 + dist: xenial + addons: + apt: + sources: + - sourceline: 'ppa:yubico/stable' + - ubuntu-toolchain-r-test + packages: + - autoconf + - check + - gcc-8 + - gengetopt + - help2man + - libfido2-dev + - libpam-dev + - libssl-dev + - libtool + - pkg-config + script: ./build-aux/ci/build-xenial-gcc8.sh + - name: Xenial gcc-9 + os: linux + compiler: gcc-9 + dist: xenial + addons: + apt: + sources: + - sourceline: 'ppa:yubico/stable' + - ubuntu-toolchain-r-test + packages: + - autoconf + - check + - gcc-9 + - gengetopt + - help2man + - libfido2-dev + - libpam-dev + - libssl-dev + - libtool + - pkg-config + script: ./build-aux/ci/build-xenial-gcc9.sh + - name: Xenial clang-7 + os: linux + compiler: clang-7 + dist: xenial + addons: + apt: + sources: + - sourceline: 'ppa:yubico/stable' + - ubuntu-toolchain-r-test + - llvm-toolchain-xenial-7 + packages: + - autoconf + - check + - clang-7 + - gengetopt + - help2man + - libfido2-dev + - libpam-dev + - libssl-dev + - libtool + - pkg-config + script: ./build-aux/ci/build-xenial-clang7.sh + - name: Xenial clang-8 + os: linux + compiler: clang-8 + dist: xenial + addons: + apt: + sources: + - sourceline: 'ppa:yubico/stable' + - ubuntu-toolchain-r-test + - llvm-toolchain-xenial-8 + packages: + - autoconf + - check + - clang-8 + - gengetopt + - help2man + - libfido2-dev + - libpam-dev + - libssl-dev + - libtool + - pkg-config + script: ./build-aux/ci/build-xenial-clang8.sh + - name: Bionic gcc-7 and clang-format + os: linux + compiler: gcc-7 + dist: bionic + addons: + apt: + sources: + - sourceline: 'ppa:yubico/stable' + - ubuntu-toolchain-r-test + packages: + - autoconf + - check + - clang-format-6.0 + - gcc-7 + - gengetopt + - help2man + - libfido2-dev + - libpam-dev + - libssl-dev + - libtool + - pkg-config + script: ./build-aux/ci/build-bionic-gcc7.sh + - name: Bionic gcc-8 + os: linux + compiler: gcc-8 + dist: bionic + addons: + apt: + sources: + - sourceline: 'ppa:yubico/stable' + - ubuntu-toolchain-r-test + packages: + - autoconf + - check + - gcc-8 + - gengetopt + - help2man + - libfido2-dev + - libpam-dev + - libssl-dev + - libtool + - pkg-config + script: ./build-aux/ci/build-bionic-gcc8.sh + - name: Bionic gcc-9 + os: linux + compiler: gcc-9 + dist: bionic + addons: + apt: + sources: + - sourceline: 'ppa:yubico/stable' + - sourceline: 'ppa:ubuntu-toolchain-r/test' + packages: + - autoconf + - check + - gcc-9 + - gengetopt + - help2man + - libfido2-dev + - libpam-dev + - libssl-dev + - libtool + - pkg-config + script: ./build-aux/ci/build-bionic-gcc9.sh + - name: Bionic clang-7 + os: linux + compiler: clang-7 + dist: bionic + addons: + apt: + sources: + - sourceline: 'ppa:yubico/stable' + - sourceline: 'ppa:ubuntu-toolchain-r/test' + - sourceline: 'deb http://apt.llvm.org/bionic/ llvm-toolchain-bionic-7 main' + key_url: 'https://apt.llvm.org/llvm-snapshot.gpg.key' + packages: + - autoconf + - check + - clang-7 + - gengetopt + - help2man + - libfido2-dev + - libpam-dev + - libssl-dev + - libtool + - pkg-config + script: ./build-aux/ci/build-bionic-clang7.sh + - name: Bionic clang-8 + os: linux + compiler: clang-8 + dist: bionic + addons: + apt: + sources: + - sourceline: 'ppa:yubico/stable' + - sourceline: 'ppa:ubuntu-toolchain-r/test' + - sourceline: 'deb http://apt.llvm.org/bionic/ llvm-toolchain-bionic-8 main' + key_url: 'https://apt.llvm.org/llvm-snapshot.gpg.key' + packages: + - autoconf + - check + - clang-8 + - gengetopt + - help2man + - libfido2-dev + - libpam-dev + - libssl-dev + - libtool + - pkg-config + script: ./build-aux/ci/build-bionic-clang8.sh + - name: macOS xcode9.4 + os: osx + osx_image: xcode9.4 + compiler: clang + addons: + homebrew: + packages: + - check + - cmake + - gengetopt + - help2man + - mandoc + - openssl@1.1 + - pkg-config + update: true + script: ./build-aux/ci/build-osx9.4-clang.sh + - name: macOS xcode11 + os: osx + osx_image: xcode11 + compiler: clang + addons: + homebrew: + packages: + - check + - cmake + - gengetopt + - help2man + - mandoc + - openssl@1.1 + - pkg-config + update: true + script: ./build-aux/ci/build-osx11-clang.sh diff --git a/external/bsd/pam-u2f/dist/AUTHORS b/external/bsd/pam-u2f/dist/AUTHORS new file mode 100644 index 000000000000..940f6d59f6a6 --- /dev/null +++ b/external/bsd/pam-u2f/dist/AUTHORS @@ -0,0 +1 @@ +Alessio Di Mauro diff --git a/external/bsd/pam-u2f/dist/COPYING b/external/bsd/pam-u2f/dist/COPYING new file mode 100644 index 000000000000..3233d84b72b9 --- /dev/null +++ b/external/bsd/pam-u2f/dist/COPYING @@ -0,0 +1,26 @@ +Copyright (c) 2014-2018 Yubico AB +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * 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. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"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 COPYRIGHT +OWNER OR CONTRIBUTORS 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. diff --git a/external/bsd/pam-u2f/dist/Makefile.am b/external/bsd/pam-u2f/dist/Makefile.am new file mode 100644 index 000000000000..8e3ff5978006 --- /dev/null +++ b/external/bsd/pam-u2f/dist/Makefile.am @@ -0,0 +1,76 @@ +# Copyright (C) 2014-2018 Yubico AB - See COPYING + +SUBDIRS = . pamu2fcfg tests + +ACLOCAL_AMFLAGS = -I m4 + +AM_CFLAGS = $(CWFLAGS) +AM_CPPFLAGS = $(LIBFIDO2_CFLAGS) $(LIBCRYPTO_CFLAGS) + +libdir = $(PAMDIR) + +lib_LTLIBRARIES = pam_u2f.la + +pam_u2f_la_SOURCES = pam-u2f.c +pam_u2f_la_SOURCES += util.c util.h +pam_u2f_la_SOURCES += drop_privs.h drop_privs.c +pam_u2f_la_SOURCES += b64.c b64.h +pam_u2f_la_SOURCES += explicit_bzero.c + +pam_u2f_la_LIBADD = -lpam +pam_u2f_la_LIBADD += $(LIBFIDO2_LIBS) $(LIBCRYPTO_LIBS) +pam_u2f_la_LDFLAGS = -module -avoid-version + +DEFS = -DDEBUG_PAM -DPAM_DEBUG @DEFS@ + +if ENABLE_MAN +dist_man8_MANS = $(top_builddir)/man/pam_u2f.8 +DISTCLEANFILES = $(dist_man8_MANS) + +MANSOURCES = $(top_builddir)/man/pam_u2f.8.txt +EXTRA_DIST = $(MANSOURCES) + +SUFFIXES = .8.txt .8 + +.8.txt.8: + $(A2X) --format=manpage -L -a revdate="Version $(VERSION)" $< +endif + +# Release + +install-exec-hook: + rm -f $(PAMDIR)/pam_u2f.la + chmod -f 644 $(PAMDIR)/pam_u2f.so || true + +indent: + clang-format -i *.c *.h pamu2fcfg/*.c pamu2fcfg/*.h + +ChangeLog: + cd $(srcdir) && git2cl > ChangeLog + +PROJECT = pam-u2f + +release: + @if test ! -d "$(YUBICO_WWW_REPO)"; then \ + echo "WWW repo not found!"; \ + echo "Make sure that YUBICO_WWW_REPO is set"; \ + exit 1; \ + fi + @if test -z "$(KEYID)"; then \ + echo "Try this instead:"; \ + echo " make release KEYID=[PGPKEYID]"; \ + echo "For example:"; \ + echo " make release KEYID=2117364A"; \ + exit 1; \ + fi + @head -5 $(srcdir)/NEWS | \ + grep -q "Version $(VERSION) .released `date -I`" || \ + (echo 'error: Update date/version in $(srcdir)/NEWS.'; exit 1) + rm -f $(srcdir)/ChangeLog + sudo make ChangeLog distcheck + gpg --detach-sign --default-key $(KEYID) $(PACKAGE)-$(VERSION).tar.gz + gpg --verify $(PACKAGE)-$(VERSION).tar.gz.sig + cd $(srcdir) && git push + cd $(srcdir) && git tag -u $(KEYID) -m $(VERSION) $(PACKAGE)-$(VERSION) + cd $(srcdir) && git push --tags + $(YUBICO_WWW_REPO)/publish $(PROJECT) $(VERSION) $(PACKAGE)-$(VERSION).tar.gz* diff --git a/external/bsd/pam-u2f/dist/NEWS b/external/bsd/pam-u2f/dist/NEWS new file mode 100644 index 000000000000..07b49054848c --- /dev/null +++ b/external/bsd/pam-u2f/dist/NEWS @@ -0,0 +1,48 @@ +Copyright (c) 2014-2018 Yubico AB - See COPYING + +pam-u2f NEWS -- History of user-visible changes. -*- outline -*- + +* Version 1.0.9 (unreleased) + +* Version 1.0.8 (released 2019-06-04) + ** Fix debug file descriptor leak CVE-2019-12210. + ** Fix insecure debug file handling CVE-2019-12209. + Both reported by Matthias Gerstner of the SUSE Security Team. + ** Fix a non-critical buffer oob access. + +* Version 1.0.7 (released 2018-05-15) + ** Add authpending_file to signal authentication activity + ** Add nodetect to skip to avoid unnecessary cue messages + +* Version 1.0.6 (released 2018-04-18) + ** Fix an issue when using syslog as a debug facility. + ** Do not honor cue if no sutable device is found. + +* Version 1.0.5 (released 2018-04-16) + ** General bugfixes and quality-of-life improvements. + +* Version 1.0.4 (released 2016-01-07) + ** Fixed possible permission escalation when using XDG_CONFIG_HOME. + +* Version 1.0.3 (released 2015-11-02) + ** Bugfix in pamu2fcfg. + ** Minor improvements for verbose mode in pamu2fcfg. + +* Version 1.0.2 (released 2015-10-06) + ** Changes to automake flags. + ** Improve build on OS X. + +* Version 1.0.1 (released 2015-06-18) + ** Minor changes to man pages and install hooks. + +* Version 1.0.0 (released 2015-06-17) + ** Use XDG_CONFIG_HOME as default for config files. + ** Added manual and interactive mode. + ** Added verbose mode. + +* Version 0.0.1 (released 2015-01-16) + ** Changed failure mode after authentication error. + ** Added call to setcred. + +* Version 0.0.0 (released 2014-12-16) + ** Initial release. diff --git a/external/bsd/pam-u2f/dist/README b/external/bsd/pam-u2f/dist/README new file mode 100644 index 000000000000..1d65bdc58d35 --- /dev/null +++ b/external/bsd/pam-u2f/dist/README @@ -0,0 +1,272 @@ +Pluggable Authentication Module (PAM) Universal 2nd Factor (U2F) +================================================================ + +image:https://travis-ci.org/Yubico/pam-u2f.svg?branch=master["Build Status", link="https://travis-ci.org/Yubico/pam-u2f"] +image:https://scan.coverity.com/projects/5711/badge.svg["Coverity Status", link=https://scan.coverity.com/projects/5711] + +This module implements PAM over U2F, providing an easy way to integrate the +YubiKey (or other U2F compliant authenticators) into your existing +infrastructure. + +[[building]] +Building +-------- + +This project uses 'autoconf', 'automake', 'pkg-config' and 'libtool' +to achieve portability and ease of use. + +In addition, both the Yubico U2F https://developers.yubico.com/libu2f-host['libu2f-host-dev'] and +https://developers.yubico.com/libu2f-server['libu2f-server-dev'] libaries are needed. + + Debian: apt-get install autoconf automake libtool pkg-config libu2f-host-dev libu2f-server-dev --no-install-recommends + +If you downloaded a tarball, build it as follows. + +----------- + $ ./configure + $ make +----------- + +Building from Git +----------------- + +You may check out the sources using Git with the following command: + +----------- + $ git clone git://github.com/Yubico/pam-u2f.git +----------- + +This will create a directory 'pam-u2f'. Enter the directory: + +----------- + $ cd pam-u2f +----------- + +'Autoconf', 'automake', 'libtool', and 'libpam' must be installed. 'AsciiDoc' and 'xsltproc' are used to +generate the manpages. + + Debian: apt-get install autoconf automake libtool libpam-dev libu2f-host-dev libu2f-server-dev asciidoc xsltproc libxml2-utils docbook-xml --no-install-recommends + +Generate the build system using: + +----------- + $ autoreconf --install +----------- + +Then build as usual, see above under <>. + +Installation +------------ + +Once the module is built copy the file +pam_u2f.so+ to the correct +directory for your system. Typically +/lib/security/+ or ++/lib/x86_64-linux-gnu/security/+. This is automated by +make install+ +assuming that the pam directory chosen by +configure+ is correct. +If that is not the case it can be specified with +./configure --with-pam-dir=+. + +Create a file for a new service in +/etc/pam.d/+ or edit an already +existing one by adding a line similar to this: + +---- +auth sufficient pam_u2f.so debug +---- + +Supported parameters for the module are: + +[horizontal] +debug:: +Enables debug output + +debug_file:: +Filename to write debugging messages to. **If this file is missing, nothing will be logged**. This regular file **has to be created by the user** +or **must exist and be a regular file** for anything getting logged to it. It is not created by pam-u2f on purpose (for security considerations). +This filename may be alternatively set to "stderr" (default), "stdout", or "syslog". + +origin=origin:: +Set the origin for the U2F authentication procedure. +If no value is specified, the origin "pam://$HOSTNAME" is used. + +appid=appid:: +Set the https://developers.yubico.com/U2F/App_ID.html[application ID] for the U2F authentication procedure. +If no value is specified, the same value used for origin is taken +("pam://$HOSTNAME" if also origin is not specified). + +authfile=file:: +Set the location of the file that holds the mappings of user names +to keyHandles and user keys. The format is ++username:first_keyHandle,first_public_key: +second_keyHandle,second_public_key:...+ the default location of the +file is $XDG_CONFIG_HOME/Yubico/u2f_keys. If the environment variable +is not set, $HOME/.config/Yubico/u2f_keys is used. +(more on <>). An individual (per user) file +may be configured, see <>. + +authpending_file=file:: +Set the location of the file that is used for touch request notifications. +This file will be opened when pam-u2f starts waiting for a user to touch the device, +and will be closed when it no longer waits for a touch. +Use inotify to listen on these events, or a more high-level tool like https://github.com/maximbaz/yubikey-touch-detector[yubikey-touch-detector]. +Set an empty value in order to disable this functionality, like so: `authpending_file=`. +Default value: /var/run/user/$UID/pam-u2f-authpending + +nouserok:: +Set to enable authentication attempts to succeed even if the user trying to +authenticate is not found inside authfile or if authfile is missing/malformed. + +openasuser:: +Setuid to the authenticating user when opening the authfile. Useful when the +user's home is stored on an NFS volume mounted with the root_squash option +(which maps root to nobody which will not be able to read the file). +Note that after release 1.0.8 this is done by default when no global +authfile or XDG_CONFIG_HOME environment variable has been set. + +alwaysok:: +Set to enable all authentication attempts to succeed (aka presentation mode). + +max_devices=n_devices:: +Maximum number of devices allowed per user (default is 24). Devices specified +in the authentication file that exceed this value will be ignored. + +interactive:: +Set to prompt a message and wait before testing the presence of a U2F device. +Recommended if your device doesn't have a tactile trigger. + +[prompt=your prompt here]:: +Set individual prompt message for interactive mode. Watch the square brackets +around this parameter to get spaces correctly recognized by PAM. + +manual:: +Set to drop to a manual console where challenges are printed on screen +and response read from standard input. Useful for debugging and SSH sessions +without U2F-support from the SSH client/server. +If enabled, interactive mode becomes redundant and has no effect. + +cue:: +Set to prompt a message to remind to touch the device. + +[cue_prompt=your prompt here]:: +Set individual prompt message for the cue option. Watch the square brackets +around this parameter to get spaces correctly recognized by PAM. + +nodetect:: +Set to skip detecting if a suitable U2F token is inserted before performing +the full tactile authentication. This detection was created to avoid +emitting the "cue" message if no suitable token exists, because doing so +leaks information about the authentication stack if a token is inserted but +not configured for the authenticating user. However, it was found that +versions of libu2f-user 1.1.5 or less has buggy iteration/sleep behavior +which causes a 1-second delay to occur for this initial detection. For this +reason, as well as the possibility of hypothetical tokens that do not +tolerate this double authentication, the "nodetect" option was added. + +userpresence=int:: +If 1, request user presence during authentication. If 0, do not request user +presence during authentication. Otherwise, fallback to the authenticator's +default behaviour. + +userverification=int:: +If 1, request user verification during authentication. If 0, do not request +user verification during authentication. Otherwise, fallback to the +authenticator's default behaviour. + +pinverification=int:: +If 1, request PIN verification during authentication. If 0, do not request +PIN verification during authentication. Otherwise, fallback to the +authenticator's default behaviour. + +[[files]] +Authorization Mapping Files +--------------------------- +A mapping must be made between the YubiKey token and the user name, +see <> for details on how to perform the registration +using the bundled tool. + +There are two ways to do this, either centrally in one file, or +individually, where users can create the mapping in their home directories. +If the central authorization mapping file is being used, user home directory +mappings will not be used and the opposite applies if user home directory +mappings are being used, the central authorization mappings file will not +be used. + +By default the mapping file inside a home directory will be opened as +the target user, whereas the central file will be opened as `root`. If +the `XDG_CONFIG_HOME` variable is set, privileges will not be dropped +unless the `openasuser` configuration setting is set. + +IMPORTANT: Using pam-u2f to secure the login to a computer while +storing the mapping file in an encrypted home directory, will result +in the impossibility of logging into the system. The partition is +decrypted after login and the mapping file can not be accessed. + +== Central authorization mapping +Create a file e.g. `/etc/u2f_mappings`., The file must contain +a user name, the number of registered Yubikeys and the information +obtained during the registration procedure. + +The mappings should look like this, one per line: + + :,:,:... + :,:,:... + +Now add `authfile=/etc/u2f_mappings` to your PAM configuration line, so it +looks like: + + auth sufficient pam_u2f.so authfile=/etc/u2f_mappings + +If you do not set the `openasuser` setting, the authfile will be opened +and parsed as `root` so make sure it has the correct owner and +permissions set. + +IMPORTANT: On dynamics networks (e.g. where hostnames are set by DHCP), +users should not rely on the default origin and appid ("pam://$HOSTNAME") +but set those parameters explicitly to the same value. + +[[individualAuth]] +== Individual authorization mapping by user +Each user creates a `.config/Yubico/u2f_keys` (default) file inside their +home directory and places the mapping in that file. You may want to specify +a different per user file (relative to the users' home dir), i.e. +`.ssh/u2f_keys`. Bear in mind, setting an absolute path here is possible although +very likely a fragile setup, and probably not exhibiting the intended behaviour. + +The file must have only one line: + + :,:,:... + +This is much the same concept as the SSH authorized_keys file. + +In this case, pam-u2f will drop privileges and read the mapping file +as that user. This happens regardless of the `openasuser` option being +set. + +Note that if you set the XDG_CONFIG_HOME variable, privileges will not +be dropped by default. Consider also setting `openasuser` in that case. + +[[registration]] +Obtaining key-handles and public keys +------------------------------------- + +In order to obtain the required information for the authentication procedure, +a token should be first registered. This can be done by using the command line +configuration tool provided with the module: + +---- +$ pamu2fcfg -uusername -opam://myorigin -ipam://myappid +---- + +the tool will register a connected token by using the specified origin and +appid. If neither are specified they will default to +pam://$HOSTNAME+. +During the U2F registration, the user is required to touch the token. +On success the tool prints to standard output a configuration line that +can be directly used with the module. For additional information on the +tool read the relative manpage (+man pamu2fcfg+) + +[[devices]] +Multiple Devices +---------------- + +Multiple devices are supported. If more than one device is specified, +authentication against them is attempted sequentially as they are defined +in the configuration file of the module. If during an authentication +attempt a connected device is removed or a new device is plugged in, +the authentication restarts from the top of the list. diff --git a/external/bsd/pam-u2f/dist/autogen.sh b/external/bsd/pam-u2f/dist/autogen.sh new file mode 100755 index 000000000000..58216ac28c41 --- /dev/null +++ b/external/bsd/pam-u2f/dist/autogen.sh @@ -0,0 +1,12 @@ +#!/usr/bin/env bash +set -e + +DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" + +main() { + autoreconf --install +} + +pushd "$DIR" &>/dev/null + main "$@" +popd &>/dev/null diff --git a/external/bsd/pam-u2f/dist/b64.c b/external/bsd/pam-u2f/dist/b64.c new file mode 100644 index 000000000000..3a3e90f5b164 --- /dev/null +++ b/external/bsd/pam-u2f/dist/b64.c @@ -0,0 +1,108 @@ +/* + * Copyright (C) 2018 Yubico AB - See COPYING + */ + +#include +#include +#include +#include + +#include "b64.h" + +int b64_encode(const void *ptr, size_t len, char **out) { + BIO *bio_b64 = NULL; + BIO *bio_mem = NULL; + char *b64_ptr = NULL; + long b64_len; + int n; + int ok = 0; + + if (ptr == NULL || out == NULL || len > INT_MAX) + return (0); + + *out = NULL; + + bio_b64 = BIO_new(BIO_f_base64()); + if (bio_b64 == NULL) + goto fail; + + bio_mem = BIO_new(BIO_s_mem()); + if (bio_mem == NULL) + goto fail; + + BIO_set_flags(bio_b64, BIO_FLAGS_BASE64_NO_NL); + BIO_push(bio_b64, bio_mem); + + n = BIO_write(bio_b64, ptr, (int) len); + if (n < 0 || (size_t) n != len) + goto fail; + + if (BIO_flush(bio_b64) < 0) + goto fail; + + b64_len = BIO_get_mem_data(bio_b64, &b64_ptr); + if (b64_len < 0 || (size_t) b64_len == SIZE_MAX || b64_ptr == NULL) + goto fail; + + *out = calloc(1, (size_t) b64_len + 1); + if (*out == NULL) + goto fail; + + memcpy(*out, b64_ptr, (size_t) b64_len); + ok = 1; + +fail: + BIO_free(bio_b64); + BIO_free(bio_mem); + + return (ok); +} + +int b64_decode(const char *in, void **ptr, size_t *len) { + BIO *bio_mem = NULL; + BIO *bio_b64 = NULL; + size_t alloc_len; + int n; + int ok = 0; + + if (in == NULL || ptr == NULL || len == NULL || strlen(in) > INT_MAX) + return (0); + + *ptr = NULL; + *len = 0; + + bio_b64 = BIO_new(BIO_f_base64()); + if (bio_b64 == NULL) + goto fail; + + bio_mem = BIO_new_mem_buf((void *) in, -1); + if (bio_mem == NULL) + goto fail; + + BIO_set_flags(bio_b64, BIO_FLAGS_BASE64_NO_NL); + BIO_push(bio_b64, bio_mem); + + alloc_len = strlen(in); + *ptr = calloc(1, alloc_len); + if (*ptr == NULL) + goto fail; + + n = BIO_read(bio_b64, *ptr, (int) alloc_len); + if (n < 0 || BIO_eof(bio_b64) == 0) + goto fail; + + *len = (size_t) n; + ok = 1; + +fail: + BIO_free(bio_b64); + BIO_free(bio_mem); + + if (!ok) { + free(*ptr); + *ptr = NULL; + *len = 0; + } + + return (ok); +} diff --git a/external/bsd/pam-u2f/dist/b64.h b/external/bsd/pam-u2f/dist/b64.h new file mode 100644 index 000000000000..c405a29bef16 --- /dev/null +++ b/external/bsd/pam-u2f/dist/b64.h @@ -0,0 +1,11 @@ +/* + * Copyright (C) 2018 Yubico AB - See COPYING + */ + +#ifndef B64_H +#define B64_H + +int b64_encode(const void *, size_t, char **); +int b64_decode(const char *, void **, size_t *); + +#endif /* B64_H */ diff --git a/external/bsd/pam-u2f/dist/build-aux/ci/build-bionic-clang7.sh b/external/bsd/pam-u2f/dist/build-aux/ci/build-bionic-clang7.sh new file mode 100755 index 000000000000..e855574c7a9f --- /dev/null +++ b/external/bsd/pam-u2f/dist/build-aux/ci/build-bionic-clang7.sh @@ -0,0 +1,6 @@ +#!/usr/bin/env bash +set -ex + +BUILDROOT="$(git rev-parse --show-toplevel)" + +source $BUILDROOT/build-aux/ci/build-linux.sh diff --git a/external/bsd/pam-u2f/dist/build-aux/ci/build-bionic-clang8.sh b/external/bsd/pam-u2f/dist/build-aux/ci/build-bionic-clang8.sh new file mode 100755 index 000000000000..e855574c7a9f --- /dev/null +++ b/external/bsd/pam-u2f/dist/build-aux/ci/build-bionic-clang8.sh @@ -0,0 +1,6 @@ +#!/usr/bin/env bash +set -ex + +BUILDROOT="$(git rev-parse --show-toplevel)" + +source $BUILDROOT/build-aux/ci/build-linux.sh diff --git a/external/bsd/pam-u2f/dist/build-aux/ci/build-bionic-gcc7.sh b/external/bsd/pam-u2f/dist/build-aux/ci/build-bionic-gcc7.sh new file mode 100755 index 000000000000..0a7f276520ea --- /dev/null +++ b/external/bsd/pam-u2f/dist/build-aux/ci/build-bionic-gcc7.sh @@ -0,0 +1,10 @@ +#!/usr/bin/env bash +set -ex + +BUILDROOT="$(git rev-parse --show-toplevel)" + +set +x +source $BUILDROOT/build-aux/ci/format-code.sh "$(git rev-parse HEAD~)" +set -x + +source $BUILDROOT/build-aux/ci/build-linux.sh diff --git a/external/bsd/pam-u2f/dist/build-aux/ci/build-bionic-gcc8.sh b/external/bsd/pam-u2f/dist/build-aux/ci/build-bionic-gcc8.sh new file mode 100755 index 000000000000..e855574c7a9f --- /dev/null +++ b/external/bsd/pam-u2f/dist/build-aux/ci/build-bionic-gcc8.sh @@ -0,0 +1,6 @@ +#!/usr/bin/env bash +set -ex + +BUILDROOT="$(git rev-parse --show-toplevel)" + +source $BUILDROOT/build-aux/ci/build-linux.sh diff --git a/external/bsd/pam-u2f/dist/build-aux/ci/build-bionic-gcc9.sh b/external/bsd/pam-u2f/dist/build-aux/ci/build-bionic-gcc9.sh new file mode 100755 index 000000000000..e855574c7a9f --- /dev/null +++ b/external/bsd/pam-u2f/dist/build-aux/ci/build-bionic-gcc9.sh @@ -0,0 +1,6 @@ +#!/usr/bin/env bash +set -ex + +BUILDROOT="$(git rev-parse --show-toplevel)" + +source $BUILDROOT/build-aux/ci/build-linux.sh diff --git a/external/bsd/pam-u2f/dist/build-aux/ci/build-linux.sh b/external/bsd/pam-u2f/dist/build-aux/ci/build-linux.sh new file mode 100755 index 000000000000..77ffd20cafa6 --- /dev/null +++ b/external/bsd/pam-u2f/dist/build-aux/ci/build-linux.sh @@ -0,0 +1,10 @@ +#!/usr/bin/env bash +set -ex + +BUILDROOT="$(git rev-parse --show-toplevel)" + +pushd "$BUILDROOT" &>/dev/null + ./autogen.sh + ./configure --disable-silent-rules --disable-man + make check +popd &>/dev/null diff --git a/external/bsd/pam-u2f/dist/build-aux/ci/build-osx.sh b/external/bsd/pam-u2f/dist/build-aux/ci/build-osx.sh new file mode 100755 index 000000000000..5b8e370e1442 --- /dev/null +++ b/external/bsd/pam-u2f/dist/build-aux/ci/build-osx.sh @@ -0,0 +1,30 @@ +#!/usr/bin/env bash +set -ex + +BUILDROOT="$(git rev-parse --show-toplevel)" + +pushd "/tmp" &>/dev/null + # Build and install libcbor + git clone git://github.com/pjk/libcbor + pushd "/tmp/libcbor" &>/dev/null + git checkout v0.5.0 + cmake -Bbuild -H. + cmake --build build -- --jobs=2 VERBOSE=1 + sudo make -C build install + popd &>/dev/null + + # Build and install libfido2 + export PKG_CONFIG_PATH=/usr/local/opt/openssl@1.1/lib/pkgconfig + git clone git://github.com/Yubico/libfido2 + pushd "/tmp/libfido2" &>/dev/null + cmake -Bbuild -H. + cmake --build build -- --jobs=2 VERBOSE=1 + sudo make -C build install + popd &>/dev/null +popd &>/dev/null + +pushd "$BUILDROOT" &>/dev/null + ./autogen.sh + ./configure --disable-silent-rules --disable-man + make +popd &>/dev/null diff --git a/external/bsd/pam-u2f/dist/build-aux/ci/build-osx11-clang.sh b/external/bsd/pam-u2f/dist/build-aux/ci/build-osx11-clang.sh new file mode 100755 index 000000000000..b2a227b3834d --- /dev/null +++ b/external/bsd/pam-u2f/dist/build-aux/ci/build-osx11-clang.sh @@ -0,0 +1,6 @@ +#!/usr/bin/env bash +set -ex + +BUILDROOT="$(git rev-parse --show-toplevel)" + +source $BUILDROOT/build-aux/ci/build-osx.sh diff --git a/external/bsd/pam-u2f/dist/build-aux/ci/build-osx9.4-clang.sh b/external/bsd/pam-u2f/dist/build-aux/ci/build-osx9.4-clang.sh new file mode 100755 index 000000000000..b2a227b3834d --- /dev/null +++ b/external/bsd/pam-u2f/dist/build-aux/ci/build-osx9.4-clang.sh @@ -0,0 +1,6 @@ +#!/usr/bin/env bash +set -ex + +BUILDROOT="$(git rev-parse --show-toplevel)" + +source $BUILDROOT/build-aux/ci/build-osx.sh diff --git a/external/bsd/pam-u2f/dist/build-aux/ci/build-xenial-clang7.sh b/external/bsd/pam-u2f/dist/build-aux/ci/build-xenial-clang7.sh new file mode 100755 index 000000000000..e855574c7a9f --- /dev/null +++ b/external/bsd/pam-u2f/dist/build-aux/ci/build-xenial-clang7.sh @@ -0,0 +1,6 @@ +#!/usr/bin/env bash +set -ex + +BUILDROOT="$(git rev-parse --show-toplevel)" + +source $BUILDROOT/build-aux/ci/build-linux.sh diff --git a/external/bsd/pam-u2f/dist/build-aux/ci/build-xenial-clang8.sh b/external/bsd/pam-u2f/dist/build-aux/ci/build-xenial-clang8.sh new file mode 100755 index 000000000000..e855574c7a9f --- /dev/null +++ b/external/bsd/pam-u2f/dist/build-aux/ci/build-xenial-clang8.sh @@ -0,0 +1,6 @@ +#!/usr/bin/env bash +set -ex + +BUILDROOT="$(git rev-parse --show-toplevel)" + +source $BUILDROOT/build-aux/ci/build-linux.sh diff --git a/external/bsd/pam-u2f/dist/build-aux/ci/build-xenial-gcc7.sh b/external/bsd/pam-u2f/dist/build-aux/ci/build-xenial-gcc7.sh new file mode 100755 index 000000000000..e855574c7a9f --- /dev/null +++ b/external/bsd/pam-u2f/dist/build-aux/ci/build-xenial-gcc7.sh @@ -0,0 +1,6 @@ +#!/usr/bin/env bash +set -ex + +BUILDROOT="$(git rev-parse --show-toplevel)" + +source $BUILDROOT/build-aux/ci/build-linux.sh diff --git a/external/bsd/pam-u2f/dist/build-aux/ci/build-xenial-gcc8.sh b/external/bsd/pam-u2f/dist/build-aux/ci/build-xenial-gcc8.sh new file mode 100755 index 000000000000..e855574c7a9f --- /dev/null +++ b/external/bsd/pam-u2f/dist/build-aux/ci/build-xenial-gcc8.sh @@ -0,0 +1,6 @@ +#!/usr/bin/env bash +set -ex + +BUILDROOT="$(git rev-parse --show-toplevel)" + +source $BUILDROOT/build-aux/ci/build-linux.sh diff --git a/external/bsd/pam-u2f/dist/build-aux/ci/build-xenial-gcc9.sh b/external/bsd/pam-u2f/dist/build-aux/ci/build-xenial-gcc9.sh new file mode 100755 index 000000000000..e855574c7a9f --- /dev/null +++ b/external/bsd/pam-u2f/dist/build-aux/ci/build-xenial-gcc9.sh @@ -0,0 +1,6 @@ +#!/usr/bin/env bash +set -ex + +BUILDROOT="$(git rev-parse --show-toplevel)" + +source $BUILDROOT/build-aux/ci/build-linux.sh diff --git a/external/bsd/pam-u2f/dist/build-aux/ci/format-code.sh b/external/bsd/pam-u2f/dist/build-aux/ci/format-code.sh new file mode 100755 index 000000000000..f14dcd9e098d --- /dev/null +++ b/external/bsd/pam-u2f/dist/build-aux/ci/format-code.sh @@ -0,0 +1,38 @@ +#!/bin/bash +# +# Inspired by https://github.com/antiagainst/SPIRV-Tools/commit/c4f1bf8ddf7764b7c11fed1ce18ceb1d36b2eaf6 +# +# Script to determine if source code in a diff is properly formatted. +# On Travis it uses the commit range of the PR or push, otherwise it uses a provided range +# Exits with non 0 exit code if formatting is needed. +# +# This script assumes to be invoked at the project root directory. + +COMMIT_RANGE="${TRAVIS_COMMIT_RANGE:-$1}" + +if [ -z "${COMMIT_RANGE}" ]; then + >&2 echo "Empty commit range, missing parameter" + return 0 +fi + +>&2 echo "Commit range $COMMIT_RANGE" + +FILES_TO_CHECK="$(git diff --name-only "$COMMIT_RANGE" | grep -e '\.c$' -e '\.h$' || true)" +CFV="${CLANG_FORMAT_VERSION:--6.0}" + +if [ -z "${FILES_TO_CHECK}" ]; then + >&2 echo "No source code to check for formatting" + return 0 +fi + +FORMAT_DIFF=$(git diff -U0 ${COMMIT_RANGE} -- ${FILES_TO_CHECK} | clang-format-diff$CFV -p1) + +if [ -z "${FORMAT_DIFF}" ]; then + >&2 echo "All source code in the diff is properly formatted" + return 0 +else + >&2 echo -e "Found formatting errors\n" + echo "${FORMAT_DIFF}" + >&2 echo -e "\nYou can save the diff above and apply it with 'git apply -p0 my_diff'" + exit 1 +fi diff --git a/external/bsd/pam-u2f/dist/configure.ac b/external/bsd/pam-u2f/dist/configure.ac new file mode 100644 index 000000000000..0cda5fd3ddf2 --- /dev/null +++ b/external/bsd/pam-u2f/dist/configure.ac @@ -0,0 +1,114 @@ +# Copyright (C) 2014-2019 Yubico AB +AC_PREREQ([2.65]) +AC_INIT([pam_u2f], [1.0.9], [https://github.com/Yubico/pam-u2f/issues], + [pam_u2f], [https://developers.yubico.com/pam-u2f/]) + +AC_CONFIG_AUX_DIR([build-aux]) +AC_CONFIG_MACRO_DIR([m4]) +AC_CANONICAL_HOST + +AM_INIT_AUTOMAKE([1.11 foreign subdir-objects]) + +AM_SILENT_RULES([yes]) +AM_MAINTAINER_MODE + +AC_USE_SYSTEM_EXTENSIONS +AM_PROG_CC_C_O +AM_PROG_AR +AC_LIBTOOL_WIN32_DLL +AC_DISABLE_STATIC +AC_PROG_LIBTOOL +AC_PROG_CC_STDC + +AC_ARG_ENABLE([man], + [AS_HELP_STRING([--disable-man], [Disable man page generation])], + [:], + [enable_man=yes] + ) +AS_IF([test "$enable_man" = "yes"], + [AM_MISSING_PROG([A2X], a2x, $missing_dir)] +) +AM_CONDITIONAL([ENABLE_MAN], [test "$enable_man" = "yes"]) + + +AC_CHECK_HEADERS([security/pam_appl.h], [], + [AC_MSG_ERROR([[PAM header files not found, install libpam-dev.]])]) +AC_CHECK_HEADERS([security/pam_modules.h security/_pam_macros.h security/pam_modutil.h], [], [], + [#include + #include ]) +AC_CHECK_LIB([pam], [pam_start]) + +AC_SEARCH_LIBS([pam_modutil_drop_priv], ["pam"], [AC_DEFINE([HAVE_PAM_MODUTIL_DROP_PRIV], [1])]) + +case "$host" in + *darwin*) PAMDIR="/usr/lib/pam";; + *linux*) PAMDIR="/lib/x86_64-linux-gnu/security";; + *) PAMDIR="/usr/lib" +esac +AC_ARG_WITH(pam-dir, + AC_HELP_STRING([--with-pam-dir=DIR], [Where to install the PAM module]), [ + case "${withval}" in + /*) PAMDIR="${withval}";; + ./*|../*) AC_MSG_ERROR(Bad value for --with-pam-dir);; + *);; + esac +]) +AC_SUBST(PAMDIR, "$PAMDIR") + + +PKG_CHECK_MODULES([LIBCRYPTO], [libcrypto], [], []) +PKG_CHECK_MODULES([LIBFIDO2], [libfido2 >= 1.2.0], [], []) + + +# Check for secure_getenv, readpassphrase, explicit_bzero, and memset_s +am_save_CFLAGS="$CFLAGS" +am_save_LIBS="$LIBS" +CFLAGS="$CFLAGS" +LIBS="$LIBS" +AC_CHECK_FUNCS([secure_getenv readpassphrase explicit_bzero memset_s]) +CFLAGS=$am_save_CFLAGS +LIBS=$am_save_LIBS + + +AC_ARG_VAR([CWFLAGS], [Warning flags]) +AX_CHECK_COMPILE_FLAG([-Wall], [CWFLAGS="-Wall"]) +AX_CHECK_COMPILE_FLAG([-Wextra], [CWFLAGS="$CWFLAGS -Wextra"]) +AX_CHECK_COMPILE_FLAG([-Wconversion], [CWFLAGS="$CWFLAGS -Wconversion"]) +# Because pam headers are doing sign-conversion, see PAM_MODUTIL_DEF_PRIVS in pam_modutil.h +AX_CHECK_COMPILE_FLAG([-Wconversion], [CWFLAGS="$CWFLAGS -Wno-sign-conversion"]) +AX_CHECK_COMPILE_FLAG([-Wpedantic], [CWFLAGS="$CWFLAGS -Wpedantic"]) +AX_CHECK_COMPILE_FLAG([-Wformat=2], [CWFLAGS="$CWFLAGS -Wformat=2"]) +AX_CHECK_COMPILE_FLAG([-Wstrict-prototypes], [CWFLAGS="$CWFLAGS -Wstrict-prototypes"]) +AX_CHECK_COMPILE_FLAG([-Wmissing-declarations], [CWFLAGS="$CWFLAGS -Wmissing-declarations"]) +AX_CHECK_COMPILE_FLAG([-Wmissing-prototypes], [CWFLAGS="$CWFLAGS -Wmissing-prototypes"]) +AX_CHECK_COMPILE_FLAG([-Wnull-dereference], [CWFLAGS="$CWFLAGS -Wnull-dereference"]) +AX_CHECK_COMPILE_FLAG([-Wshadow], [CWFLAGS="$CWFLAGS -Wshadow"]) +AX_CHECK_COMPILE_FLAG([-Wpointer-arith], [CWFLAGS="$CWFLAGS -Wpointer-arith"]) + +AC_ARG_VAR([CSFLAGS], [Warning suppression flags]) +AX_CHECK_COMPILE_FLAG([-Wno-unused-but-set-variable], [CSFLAGS="-Wno-unused-but-set-variable"]) + +AC_CONFIG_FILES([ + Makefile + pamu2fcfg/Makefile + tests/Makefile +]) +AC_OUTPUT + + +AC_MSG_NOTICE([Summary of build options: + + Version: ${VERSION} + Host type: ${host} + Install prefix: ${prefix} + Compiler: ${CC} + CFLAGS: ${CFLAGS} + CWFLAGS: ${CWFLAGS} + CSFLAGS: ${CSFLAGS} + Library types: Shared=${enable_shared}, Static=${enable_static} + LIBFIDO2 CFLAGS: $LIBFIDO2_CFLAGS + LIBFIDO2 LIBS: $LIBFIDO2_LIBS + LIBCRYPTO CFLAGS: $LIBCRYPTO_CFLAGS + LIBCRYPTO LIBS: $LIBCRYPTO_LIBS + PAMDIR: $PAMDIR +]) diff --git a/external/bsd/pam-u2f/dist/drop_privs.c b/external/bsd/pam-u2f/dist/drop_privs.c new file mode 100644 index 000000000000..99b6f0308eca --- /dev/null +++ b/external/bsd/pam-u2f/dist/drop_privs.c @@ -0,0 +1,131 @@ +/* Written by Ricky Zhou + * Fredrik Thulin implemented pam_modutil_drop_priv + * + * Copyright (c) 2011-2014 Yubico AB + * Copyright (c) 2011 Ricky Zhou + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 COPYRIGHT + * OWNER OR CONTRIBUTORS 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. + */ + +#ifndef HAVE_PAM_MODUTIL_DROP_PRIV + +#include +#include +#include +#include +#include +#include + +#include "drop_privs.h" +#include "util.h" + +#ifdef HAVE_SECURITY_PAM_APPL_H +#include +#endif +#ifdef HAVE_SECURITY_PAM_MODULES_H +#include +#endif + +int pam_modutil_drop_priv(pam_handle_t *pamh, struct _ykpam_privs *privs, + struct passwd *pw) { + privs->saved_euid = geteuid(); + privs->saved_egid = getegid(); + + if ((privs->saved_euid == pw->pw_uid) && (privs->saved_egid == pw->pw_gid)) { + D(privs->debug_file, "Privilges already dropped, pretend it is all right"); + return 0; + } + + privs->saved_groups_length = getgroups(0, NULL); + if (privs->saved_groups_length < 0) { + D(privs->debug_file, "getgroups: %s", strerror(errno)); + return -1; + } + + if (privs->saved_groups_length > SAVED_GROUPS_MAX_LEN) { + D(privs->debug_file, "too many groups, limiting."); + privs->saved_groups_length = SAVED_GROUPS_MAX_LEN; + } + + if (privs->saved_groups_length > 0) { + if (getgroups(privs->saved_groups_length, privs->saved_groups) < 0) { + D(privs->debug_file, "getgroups: %s", strerror(errno)); + goto free_out; + } + } + + if (initgroups(pw->pw_name, pw->pw_gid) < 0) { + D(privs->debug_file, "initgroups: %s", strerror(errno)); + goto free_out; + } + + if (setegid(pw->pw_gid) < 0) { + D(privs->debug_file, "setegid: %s", strerror(errno)); + goto free_out; + } + + if (seteuid(pw->pw_uid) < 0) { + D(privs->debug_file, "seteuid: %s", strerror(errno)); + goto free_out; + } + + return 0; +free_out: + return -1; +} + +int pam_modutil_regain_priv(pam_handle_t *pamh, struct _ykpam_privs *privs) { + if ((privs->saved_euid == geteuid()) && (privs->saved_egid == getegid())) { + D(privs->debug_file, + "Privilges already as requested, pretend it is all right"); + return 0; + } + + if (seteuid(privs->saved_euid) < 0) { + D(privs->debug_file, "seteuid: %s", strerror(errno)); + return -1; + } + + if (setegid(privs->saved_egid) < 0) { + D(privs->debug_file, "setegid: %s", strerror(errno)); + return -1; + } + + if (setgroups(privs->saved_groups_length, privs->saved_groups) < 0) { + D(privs->debug_file, "setgroups: %s", strerror(errno)); + return -1; + } + + return 0; +} + +#else + +// drop_privs.c:124: warning: ISO C forbids an empty translation unit +// [-Wpedantic] +typedef int make_iso_compilers_happy; + +#endif // HAVE_PAM_MODUTIL_DROP_PRIV diff --git a/external/bsd/pam-u2f/dist/drop_privs.h b/external/bsd/pam-u2f/dist/drop_privs.h new file mode 100644 index 000000000000..b2a886bdb04f --- /dev/null +++ b/external/bsd/pam-u2f/dist/drop_privs.h @@ -0,0 +1,66 @@ +/* Copyright (c) 2011-2014 Yubico AB + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 COPYRIGHT + * OWNER OR CONTRIBUTORS 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. + */ + +#ifndef __PAM_U2F_DROP_PRIVS_H_INCLUDED__ +#define __PAM_U2F_DROP_PRIVS_H_INCLUDED__ + +#ifdef HAVE_PAM_MODUTIL_DROP_PRIV +#include +#else + +#include +#include + +#ifdef HAVE_SECURITY_PAM_APPL_H +#include +#endif +#ifdef HAVE_SECURITY_PAM_MODULES_H +#include +#endif + +#define SAVED_GROUPS_MAX_LEN 64 /* as pam_modutil.. */ + +struct _ykpam_privs { + uid_t saved_euid; + gid_t saved_egid; + gid_t *saved_groups; + int saved_groups_length; + FILE *debug_file; +}; + +#define PAM_MODUTIL_DEF_PRIVS(n) \ + gid_t n##_saved_groups[SAVED_GROUPS_MAX_LEN]; \ + struct _ykpam_privs n = {-1, -1, n##_saved_groups, SAVED_GROUPS_MAX_LEN, \ + cfg->debug_file} + +int pam_modutil_drop_priv(pam_handle_t *, struct _ykpam_privs *, + struct passwd *); +int pam_modutil_regain_priv(pam_handle_t *, struct _ykpam_privs *); + +#endif +#endif diff --git a/external/bsd/pam-u2f/dist/explicit_bzero.c b/external/bsd/pam-u2f/dist/explicit_bzero.c new file mode 100644 index 000000000000..592e700bd1b3 --- /dev/null +++ b/external/bsd/pam-u2f/dist/explicit_bzero.c @@ -0,0 +1,53 @@ +/* OPENBSD ORIGINAL: lib/libc/string/explicit_bzero.c */ +/* $OpenBSD: explicit_bzero.c,v 1.1 2014/01/22 21:06:45 tedu Exp $ */ +/* + * Public domain. + * Written by Ted Unangst + */ + +#include + +/* + * explicit_bzero - don't let the compiler optimize away bzero + */ + +#ifndef HAVE_EXPLICIT_BZERO + +#include "util.h" + +#ifdef HAVE_MEMSET_S + +void explicit_bzero(void *p, size_t n) { + if (n == 0) + return; + (void) memset_s(p, n, 0, n); +} + +#else /* HAVE_MEMSET_S */ + +/* + * Indirect bzero through a volatile pointer to hopefully avoid + * dead-store optimisation eliminating the call. + */ +static void (*volatile ssh_bzero)(void *, size_t) = bzero; + +void explicit_bzero(void *p, size_t n) { + if (n == 0) + return; + /* + * clang -fsanitize=memory needs to intercept memset-like functions + * to correctly detect memory initialisation. Make sure one is called + * directly since our indirection trick above successfully confuses it. + */ +#if defined(__has_feature) +#if __has_feature(memory_sanitizer) + memset(p, 0, n); +#endif +#endif + + ssh_bzero(p, n); +} + +#endif /* HAVE_MEMSET_S */ + +#endif /* HAVE_EXPLICIT_BZERO */ diff --git a/external/bsd/pam-u2f/dist/m4/ax_check_compile_flag.m4 b/external/bsd/pam-u2f/dist/m4/ax_check_compile_flag.m4 new file mode 100644 index 000000000000..df5aff8cbda7 --- /dev/null +++ b/external/bsd/pam-u2f/dist/m4/ax_check_compile_flag.m4 @@ -0,0 +1,73 @@ +# =========================================================================== +# http://www.gnu.org/software/autoconf-archive/ax_check_compile_flag.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_CHECK_COMPILE_FLAG(FLAG, [ACTION-SUCCESS], [ACTION-FAILURE], [EXTRA-FLAGS]) +# +# DESCRIPTION +# +# Check whether the given FLAG works with the current language's compiler +# or gives an error. (Warnings, however, are ignored) +# +# ACTION-SUCCESS/ACTION-FAILURE are shell commands to execute on +# success/failure. +# +# If EXTRA-FLAGS is defined, it is added to the current language's default +# flags (e.g. CFLAGS) when the check is done. The check is thus made with +# the flags: "CFLAGS EXTRA-FLAGS FLAG". This can for example be used to +# force the compiler to issue an error when a bad flag is given. +# +# NOTE: Implementation based on AX_CFLAGS_GCC_OPTION. Please keep this +# macro in sync with AX_CHECK_{PREPROC,LINK}_FLAG. +# +# LICENSE +# +# Copyright (c) 2008 Guido U. Draheim +# Copyright (c) 2011 Maarten Bosmans +# +# 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. +# +# You should have received a copy of the GNU General Public License along +# with this program. If not, see . +# +# As a special exception, the respective Autoconf Macro's copyright owner +# gives unlimited permission to copy, distribute and modify the configure +# scripts that are the output of Autoconf when processing the Macro. You +# need not follow the terms of the GNU General Public License when using +# or distributing such scripts, even though portions of the text of the +# Macro appear in them. The GNU General Public License (GPL) does govern +# all other use of the material that constitutes the Autoconf Macro. +# +# This special exception to the GPL applies to versions of the Autoconf +# Macro released by the Autoconf Archive. When you make and distribute a +# modified version of the Autoconf Macro, you may extend this special +# exception to the GPL to apply to your modified version as well. + +#serial 4 + +AC_DEFUN([AX_CHECK_COMPILE_FLAG], +[AC_PREREQ(2.64)dnl for _AC_LANG_PREFIX and AS_VAR_IF +AS_VAR_PUSHDEF([CACHEVAR],[ax_cv_check_[]_AC_LANG_ABBREV[]flags_$4_$1])dnl +AC_CACHE_CHECK([whether _AC_LANG compiler accepts $1], CACHEVAR, [ + ax_check_save_flags=$[]_AC_LANG_PREFIX[]FLAGS + _AC_LANG_PREFIX[]FLAGS="$[]_AC_LANG_PREFIX[]FLAGS $4 $1" + AC_TRY_LINK([#include ], + [char x[42U], fodder = 0;if (fodder > -1000 && fgets(x,1000,stdin)) puts(x)], + [AS_VAR_SET(CACHEVAR,[yes])], + [AS_VAR_SET(CACHEVAR,[no])]) + _AC_LANG_PREFIX[]FLAGS=$ax_check_save_flags]) +AS_VAR_IF(CACHEVAR,yes, + [m4_default([$2], :)], + [m4_default([$3], :)]) +AS_VAR_POPDEF([CACHEVAR])dnl +])dnl AX_CHECK_COMPILE_FLAGS diff --git a/external/bsd/pam-u2f/dist/m4/lib-ld.m4 b/external/bsd/pam-u2f/dist/m4/lib-ld.m4 new file mode 100644 index 000000000000..e4863f2c9b76 --- /dev/null +++ b/external/bsd/pam-u2f/dist/m4/lib-ld.m4 @@ -0,0 +1,110 @@ +# lib-ld.m4 serial 4 (gettext-0.18) +dnl Copyright (C) 1996-2003, 2009 Free Software Foundation, Inc. +dnl This file is free software; the Free Software Foundation +dnl gives unlimited permission to copy and/or distribute it, +dnl with or without modifications, as long as this notice is preserved. + +dnl Subroutines of libtool.m4, +dnl with replacements s/AC_/AC_LIB/ and s/lt_cv/acl_cv/ to avoid collision +dnl with libtool.m4. + +dnl From libtool-1.4. Sets the variable with_gnu_ld to yes or no. +AC_DEFUN([AC_LIB_PROG_LD_GNU], +[AC_CACHE_CHECK([if the linker ($LD) is GNU ld], [acl_cv_prog_gnu_ld], +[# I'd rather use --version here, but apparently some GNU ld's only accept -v. +case `$LD -v 2>&1 conf$$.sh + echo "exit 0" >>conf$$.sh + chmod +x conf$$.sh + if (PATH="/nonexistent;."; conf$$.sh) >/dev/null 2>&1; then + PATH_SEPARATOR=';' + else + PATH_SEPARATOR=: + fi + rm -f conf$$.sh +fi +ac_prog=ld +if test "$GCC" = yes; then + # Check if gcc -print-prog-name=ld gives a path. + AC_MSG_CHECKING([for ld used by GCC]) + case $host in + *-*-mingw*) + # gcc leaves a trailing carriage return which upsets mingw + ac_prog=`($CC -print-prog-name=ld) 2>&5 | tr -d '\015'` ;; + *) + ac_prog=`($CC -print-prog-name=ld) 2>&5` ;; + esac + case $ac_prog in + # Accept absolute paths. + [[\\/]* | [A-Za-z]:[\\/]*)] + [re_direlt='/[^/][^/]*/\.\./'] + # Canonicalize the path of ld + ac_prog=`echo $ac_prog| sed 's%\\\\%/%g'` + while echo $ac_prog | grep "$re_direlt" > /dev/null 2>&1; do + ac_prog=`echo $ac_prog| sed "s%$re_direlt%/%"` + done + test -z "$LD" && LD="$ac_prog" + ;; + "") + # If it fails, then pretend we aren't using GCC. + ac_prog=ld + ;; + *) + # If it is relative, then search for the first ld in PATH. + with_gnu_ld=unknown + ;; + esac +elif test "$with_gnu_ld" = yes; then + AC_MSG_CHECKING([for GNU ld]) +else + AC_MSG_CHECKING([for non-GNU ld]) +fi +AC_CACHE_VAL([acl_cv_path_LD], +[if test -z "$LD"; then + IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS="${IFS}${PATH_SEPARATOR-:}" + for ac_dir in $PATH; do + test -z "$ac_dir" && ac_dir=. + if test -f "$ac_dir/$ac_prog" || test -f "$ac_dir/$ac_prog$ac_exeext"; then + acl_cv_path_LD="$ac_dir/$ac_prog" + # Check to see if the program is GNU ld. I'd rather use --version, + # but apparently some GNU ld's only accept -v. + # Break only if it was the GNU/non-GNU ld that we prefer. + case `"$acl_cv_path_LD" -v 2>&1 < /dev/null` in + *GNU* | *'with BFD'*) + test "$with_gnu_ld" != no && break ;; + *) + test "$with_gnu_ld" != yes && break ;; + esac + fi + done + IFS="$ac_save_ifs" +else + acl_cv_path_LD="$LD" # Let the user override the test with a path. +fi]) +LD="$acl_cv_path_LD" +if test -n "$LD"; then + AC_MSG_RESULT([$LD]) +else + AC_MSG_RESULT([no]) +fi +test -z "$LD" && AC_MSG_ERROR([no acceptable ld found in \$PATH]) +AC_LIB_PROG_LD_GNU +]) diff --git a/external/bsd/pam-u2f/dist/m4/lib-link.m4 b/external/bsd/pam-u2f/dist/m4/lib-link.m4 new file mode 100644 index 000000000000..fcd3391bbcef --- /dev/null +++ b/external/bsd/pam-u2f/dist/m4/lib-link.m4 @@ -0,0 +1,758 @@ +# lib-link.m4 serial 18 (gettext-0.18) +dnl Copyright (C) 2001-2009 Free Software Foundation, Inc. +dnl This file is free software; the Free Software Foundation +dnl gives unlimited permission to copy and/or distribute it, +dnl with or without modifications, as long as this notice is preserved. + +dnl From Bruno Haible. + +AC_PREREQ([2.54]) + +dnl AC_LIB_LINKFLAGS(name [, dependencies]) searches for libname and +dnl the libraries corresponding to explicit and implicit dependencies. +dnl Sets and AC_SUBSTs the LIB${NAME} and LTLIB${NAME} variables and +dnl augments the CPPFLAGS variable. +dnl Sets and AC_SUBSTs the LIB${NAME}_PREFIX variable to nonempty if libname +dnl was found in ${LIB${NAME}_PREFIX}/$acl_libdirstem. +AC_DEFUN([AC_LIB_LINKFLAGS], +[ + AC_REQUIRE([AC_LIB_PREPARE_PREFIX]) + AC_REQUIRE([AC_LIB_RPATH]) + pushdef([Name],[translit([$1],[./-], [___])]) + pushdef([NAME],[translit([$1],[abcdefghijklmnopqrstuvwxyz./-], + [ABCDEFGHIJKLMNOPQRSTUVWXYZ___])]) + AC_CACHE_CHECK([how to link with lib[]$1], [ac_cv_lib[]Name[]_libs], [ + AC_LIB_LINKFLAGS_BODY([$1], [$2]) + ac_cv_lib[]Name[]_libs="$LIB[]NAME" + ac_cv_lib[]Name[]_ltlibs="$LTLIB[]NAME" + ac_cv_lib[]Name[]_cppflags="$INC[]NAME" + ac_cv_lib[]Name[]_prefix="$LIB[]NAME[]_PREFIX" + ]) + LIB[]NAME="$ac_cv_lib[]Name[]_libs" + LTLIB[]NAME="$ac_cv_lib[]Name[]_ltlibs" + INC[]NAME="$ac_cv_lib[]Name[]_cppflags" + LIB[]NAME[]_PREFIX="$ac_cv_lib[]Name[]_prefix" + AC_LIB_APPENDTOVAR([CPPFLAGS], [$INC]NAME) + AC_SUBST([LIB]NAME) + AC_SUBST([LTLIB]NAME) + AC_SUBST([LIB]NAME[_PREFIX]) + dnl Also set HAVE_LIB[]NAME so that AC_LIB_HAVE_LINKFLAGS can reuse the + dnl results of this search when this library appears as a dependency. + HAVE_LIB[]NAME=yes + popdef([NAME]) + popdef([Name]) +]) + +dnl AC_LIB_HAVE_LINKFLAGS(name, dependencies, includes, testcode) +dnl searches for libname and the libraries corresponding to explicit and +dnl implicit dependencies, together with the specified include files and +dnl the ability to compile and link the specified testcode. If found, it +dnl sets and AC_SUBSTs HAVE_LIB${NAME}=yes and the LIB${NAME} and +dnl LTLIB${NAME} variables and augments the CPPFLAGS variable, and +dnl #defines HAVE_LIB${NAME} to 1. Otherwise, it sets and AC_SUBSTs +dnl HAVE_LIB${NAME}=no and LIB${NAME} and LTLIB${NAME} to empty. +dnl Sets and AC_SUBSTs the LIB${NAME}_PREFIX variable to nonempty if libname +dnl was found in ${LIB${NAME}_PREFIX}/$acl_libdirstem. +AC_DEFUN([AC_LIB_HAVE_LINKFLAGS], +[ + AC_REQUIRE([AC_LIB_PREPARE_PREFIX]) + AC_REQUIRE([AC_LIB_RPATH]) + pushdef([Name],[translit([$1],[./-], [___])]) + pushdef([NAME],[translit([$1],[abcdefghijklmnopqrstuvwxyz./-], + [ABCDEFGHIJKLMNOPQRSTUVWXYZ___])]) + + dnl Search for lib[]Name and define LIB[]NAME, LTLIB[]NAME and INC[]NAME + dnl accordingly. + AC_LIB_LINKFLAGS_BODY([$1], [$2]) + + dnl Add $INC[]NAME to CPPFLAGS before performing the following checks, + dnl because if the user has installed lib[]Name and not disabled its use + dnl via --without-lib[]Name-prefix, he wants to use it. + ac_save_CPPFLAGS="$CPPFLAGS" + AC_LIB_APPENDTOVAR([CPPFLAGS], [$INC]NAME) + + AC_CACHE_CHECK([for lib[]$1], [ac_cv_lib[]Name], [ + ac_save_LIBS="$LIBS" + LIBS="$LIBS $LIB[]NAME" + AC_TRY_LINK([$3], [$4], [ac_cv_lib[]Name=yes], [ac_cv_lib[]Name=no]) + LIBS="$ac_save_LIBS" + ]) + if test "$ac_cv_lib[]Name" = yes; then + HAVE_LIB[]NAME=yes + AC_DEFINE([HAVE_LIB]NAME, 1, [Define if you have the $1 library.]) + AC_MSG_CHECKING([how to link with lib[]$1]) + AC_MSG_RESULT([$LIB[]NAME]) + else + HAVE_LIB[]NAME=no + dnl If $LIB[]NAME didn't lead to a usable library, we don't need + dnl $INC[]NAME either. + CPPFLAGS="$ac_save_CPPFLAGS" + LIB[]NAME= + LTLIB[]NAME= + LIB[]NAME[]_PREFIX= + fi + AC_SUBST([HAVE_LIB]NAME) + AC_SUBST([LIB]NAME) + AC_SUBST([LTLIB]NAME) + AC_SUBST([LIB]NAME[_PREFIX]) + popdef([NAME]) + popdef([Name]) +]) + +dnl Determine the platform dependent parameters needed to use rpath: +dnl acl_libext, +dnl acl_shlibext, +dnl acl_hardcode_libdir_flag_spec, +dnl acl_hardcode_libdir_separator, +dnl acl_hardcode_direct, +dnl acl_hardcode_minus_L. +AC_DEFUN([AC_LIB_RPATH], +[ + dnl Tell automake >= 1.10 to complain if config.rpath is missing. + m4_ifdef([AC_REQUIRE_AUX_FILE], [AC_REQUIRE_AUX_FILE([config.rpath])]) + AC_REQUIRE([AC_PROG_CC]) dnl we use $CC, $GCC, $LDFLAGS + AC_REQUIRE([AC_LIB_PROG_LD]) dnl we use $LD, $with_gnu_ld + AC_REQUIRE([AC_CANONICAL_HOST]) dnl we use $host + AC_REQUIRE([AC_CONFIG_AUX_DIR_DEFAULT]) dnl we use $ac_aux_dir + AC_CACHE_CHECK([for shared library run path origin], [acl_cv_rpath], [ + CC="$CC" GCC="$GCC" LDFLAGS="$LDFLAGS" LD="$LD" with_gnu_ld="$with_gnu_ld" \ + ${CONFIG_SHELL-/bin/sh} "$ac_aux_dir/config.rpath" "$host" > conftest.sh + . ./conftest.sh + rm -f ./conftest.sh + acl_cv_rpath=done + ]) + wl="$acl_cv_wl" + acl_libext="$acl_cv_libext" + acl_shlibext="$acl_cv_shlibext" + acl_libname_spec="$acl_cv_libname_spec" + acl_library_names_spec="$acl_cv_library_names_spec" + acl_hardcode_libdir_flag_spec="$acl_cv_hardcode_libdir_flag_spec" + acl_hardcode_libdir_separator="$acl_cv_hardcode_libdir_separator" + acl_hardcode_direct="$acl_cv_hardcode_direct" + acl_hardcode_minus_L="$acl_cv_hardcode_minus_L" + dnl Determine whether the user wants rpath handling at all. + AC_ARG_ENABLE([rpath], + [ --disable-rpath do not hardcode runtime library paths], + :, enable_rpath=yes) +]) + +dnl AC_LIB_FROMPACKAGE(name, package) +dnl declares that libname comes from the given package. The configure file +dnl will then not have a --with-libname-prefix option but a +dnl --with-package-prefix option. Several libraries can come from the same +dnl package. This declaration must occur before an AC_LIB_LINKFLAGS or similar +dnl macro call that searches for libname. +AC_DEFUN([AC_LIB_FROMPACKAGE], +[ + pushdef([NAME],[translit([$1],[abcdefghijklmnopqrstuvwxyz./-], + [ABCDEFGHIJKLMNOPQRSTUVWXYZ___])]) + define([acl_frompackage_]NAME, [$2]) + popdef([NAME]) + pushdef([PACK],[$2]) + pushdef([PACKUP],[translit(PACK,[abcdefghijklmnopqrstuvwxyz./-], + [ABCDEFGHIJKLMNOPQRSTUVWXYZ___])]) + define([acl_libsinpackage_]PACKUP, + m4_ifdef([acl_libsinpackage_]PACKUP, [acl_libsinpackage_]PACKUP[[, ]],)[lib$1]) + popdef([PACKUP]) + popdef([PACK]) +]) + +dnl AC_LIB_LINKFLAGS_BODY(name [, dependencies]) searches for libname and +dnl the libraries corresponding to explicit and implicit dependencies. +dnl Sets the LIB${NAME}, LTLIB${NAME} and INC${NAME} variables. +dnl Also, sets the LIB${NAME}_PREFIX variable to nonempty if libname was found +dnl in ${LIB${NAME}_PREFIX}/$acl_libdirstem. +AC_DEFUN([AC_LIB_LINKFLAGS_BODY], +[ + AC_REQUIRE([AC_LIB_PREPARE_MULTILIB]) + pushdef([NAME],[translit([$1],[abcdefghijklmnopqrstuvwxyz./-], + [ABCDEFGHIJKLMNOPQRSTUVWXYZ___])]) + pushdef([PACK],[m4_ifdef([acl_frompackage_]NAME, [acl_frompackage_]NAME, lib[$1])]) + pushdef([PACKUP],[translit(PACK,[abcdefghijklmnopqrstuvwxyz./-], + [ABCDEFGHIJKLMNOPQRSTUVWXYZ___])]) + pushdef([PACKLIBS],[m4_ifdef([acl_frompackage_]NAME, [acl_libsinpackage_]PACKUP, lib[$1])]) + dnl Autoconf >= 2.61 supports dots in --with options. + pushdef([P_A_C_K],[m4_if(m4_version_compare(m4_defn([m4_PACKAGE_VERSION]),[2.61]),[-1],[translit(PACK,[.],[_])],PACK)]) + dnl By default, look in $includedir and $libdir. + use_additional=yes + AC_LIB_WITH_FINAL_PREFIX([ + eval additional_includedir=\"$includedir\" + eval additional_libdir=\"$libdir\" + ]) + AC_ARG_WITH(P_A_C_K[-prefix], +[[ --with-]]P_A_C_K[[-prefix[=DIR] search for ]PACKLIBS[ in DIR/include and DIR/lib + --without-]]P_A_C_K[[-prefix don't search for ]PACKLIBS[ in includedir and libdir]], +[ + if test "X$withval" = "Xno"; then + use_additional=no + else + if test "X$withval" = "X"; then + AC_LIB_WITH_FINAL_PREFIX([ + eval additional_includedir=\"$includedir\" + eval additional_libdir=\"$libdir\" + ]) + else + additional_includedir="$withval/include" + additional_libdir="$withval/$acl_libdirstem" + if test "$acl_libdirstem2" != "$acl_libdirstem" \ + && ! test -d "$withval/$acl_libdirstem"; then + additional_libdir="$withval/$acl_libdirstem2" + fi + fi + fi +]) + dnl Search the library and its dependencies in $additional_libdir and + dnl $LDFLAGS. Using breadth-first-seach. + LIB[]NAME= + LTLIB[]NAME= + INC[]NAME= + LIB[]NAME[]_PREFIX= + rpathdirs= + ltrpathdirs= + names_already_handled= + names_next_round='$1 $2' + while test -n "$names_next_round"; do + names_this_round="$names_next_round" + names_next_round= + for name in $names_this_round; do + already_handled= + for n in $names_already_handled; do + if test "$n" = "$name"; then + already_handled=yes + break + fi + done + if test -z "$already_handled"; then + names_already_handled="$names_already_handled $name" + dnl See if it was already located by an earlier AC_LIB_LINKFLAGS + dnl or AC_LIB_HAVE_LINKFLAGS call. + uppername=`echo "$name" | sed -e 'y|abcdefghijklmnopqrstuvwxyz./-|ABCDEFGHIJKLMNOPQRSTUVWXYZ___|'` + eval value=\"\$HAVE_LIB$uppername\" + if test -n "$value"; then + if test "$value" = yes; then + eval value=\"\$LIB$uppername\" + test -z "$value" || LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }$value" + eval value=\"\$LTLIB$uppername\" + test -z "$value" || LTLIB[]NAME="${LTLIB[]NAME}${LTLIB[]NAME:+ }$value" + else + dnl An earlier call to AC_LIB_HAVE_LINKFLAGS has determined + dnl that this library doesn't exist. So just drop it. + : + fi + else + dnl Search the library lib$name in $additional_libdir and $LDFLAGS + dnl and the already constructed $LIBNAME/$LTLIBNAME. + found_dir= + found_la= + found_so= + found_a= + eval libname=\"$acl_libname_spec\" # typically: libname=lib$name + if test -n "$acl_shlibext"; then + shrext=".$acl_shlibext" # typically: shrext=.so + else + shrext= + fi + if test $use_additional = yes; then + dir="$additional_libdir" + dnl The same code as in the loop below: + dnl First look for a shared library. + if test -n "$acl_shlibext"; then + if test -f "$dir/$libname$shrext"; then + found_dir="$dir" + found_so="$dir/$libname$shrext" + else + if test "$acl_library_names_spec" = '$libname$shrext$versuffix'; then + ver=`(cd "$dir" && \ + for f in "$libname$shrext".*; do echo "$f"; done \ + | sed -e "s,^$libname$shrext\\\\.,," \ + | sort -t '.' -n -r -k1,1 -k2,2 -k3,3 -k4,4 -k5,5 \ + | sed 1q ) 2>/dev/null` + if test -n "$ver" && test -f "$dir/$libname$shrext.$ver"; then + found_dir="$dir" + found_so="$dir/$libname$shrext.$ver" + fi + else + eval library_names=\"$acl_library_names_spec\" + for f in $library_names; do + if test -f "$dir/$f"; then + found_dir="$dir" + found_so="$dir/$f" + break + fi + done + fi + fi + fi + dnl Then look for a static library. + if test "X$found_dir" = "X"; then + if test -f "$dir/$libname.$acl_libext"; then + found_dir="$dir" + found_a="$dir/$libname.$acl_libext" + fi + fi + if test "X$found_dir" != "X"; then + if test -f "$dir/$libname.la"; then + found_la="$dir/$libname.la" + fi + fi + fi + if test "X$found_dir" = "X"; then + for x in $LDFLAGS $LTLIB[]NAME; do + AC_LIB_WITH_FINAL_PREFIX([eval x=\"$x\"]) + case "$x" in + -L*) + dir=`echo "X$x" | sed -e 's/^X-L//'` + dnl First look for a shared library. + if test -n "$acl_shlibext"; then + if test -f "$dir/$libname$shrext"; then + found_dir="$dir" + found_so="$dir/$libname$shrext" + else + if test "$acl_library_names_spec" = '$libname$shrext$versuffix'; then + ver=`(cd "$dir" && \ + for f in "$libname$shrext".*; do echo "$f"; done \ + | sed -e "s,^$libname$shrext\\\\.,," \ + | sort -t '.' -n -r -k1,1 -k2,2 -k3,3 -k4,4 -k5,5 \ + | sed 1q ) 2>/dev/null` + if test -n "$ver" && test -f "$dir/$libname$shrext.$ver"; then + found_dir="$dir" + found_so="$dir/$libname$shrext.$ver" + fi + else + eval library_names=\"$acl_library_names_spec\" + for f in $library_names; do + if test -f "$dir/$f"; then + found_dir="$dir" + found_so="$dir/$f" + break + fi + done + fi + fi + fi + dnl Then look for a static library. + if test "X$found_dir" = "X"; then + if test -f "$dir/$libname.$acl_libext"; then + found_dir="$dir" + found_a="$dir/$libname.$acl_libext" + fi + fi + if test "X$found_dir" != "X"; then + if test -f "$dir/$libname.la"; then + found_la="$dir/$libname.la" + fi + fi + ;; + esac + if test "X$found_dir" != "X"; then + break + fi + done + fi + if test "X$found_dir" != "X"; then + dnl Found the library. + LTLIB[]NAME="${LTLIB[]NAME}${LTLIB[]NAME:+ }-L$found_dir -l$name" + if test "X$found_so" != "X"; then + dnl Linking with a shared library. We attempt to hardcode its + dnl directory into the executable's runpath, unless it's the + dnl standard /usr/lib. + if test "$enable_rpath" = no \ + || test "X$found_dir" = "X/usr/$acl_libdirstem" \ + || test "X$found_dir" = "X/usr/$acl_libdirstem2"; then + dnl No hardcoding is needed. + LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }$found_so" + else + dnl Use an explicit option to hardcode DIR into the resulting + dnl binary. + dnl Potentially add DIR to ltrpathdirs. + dnl The ltrpathdirs will be appended to $LTLIBNAME at the end. + haveit= + for x in $ltrpathdirs; do + if test "X$x" = "X$found_dir"; then + haveit=yes + break + fi + done + if test -z "$haveit"; then + ltrpathdirs="$ltrpathdirs $found_dir" + fi + dnl The hardcoding into $LIBNAME is system dependent. + if test "$acl_hardcode_direct" = yes; then + dnl Using DIR/libNAME.so during linking hardcodes DIR into the + dnl resulting binary. + LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }$found_so" + else + if test -n "$acl_hardcode_libdir_flag_spec" && test "$acl_hardcode_minus_L" = no; then + dnl Use an explicit option to hardcode DIR into the resulting + dnl binary. + LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }$found_so" + dnl Potentially add DIR to rpathdirs. + dnl The rpathdirs will be appended to $LIBNAME at the end. + haveit= + for x in $rpathdirs; do + if test "X$x" = "X$found_dir"; then + haveit=yes + break + fi + done + if test -z "$haveit"; then + rpathdirs="$rpathdirs $found_dir" + fi + else + dnl Rely on "-L$found_dir". + dnl But don't add it if it's already contained in the LDFLAGS + dnl or the already constructed $LIBNAME + haveit= + for x in $LDFLAGS $LIB[]NAME; do + AC_LIB_WITH_FINAL_PREFIX([eval x=\"$x\"]) + if test "X$x" = "X-L$found_dir"; then + haveit=yes + break + fi + done + if test -z "$haveit"; then + LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }-L$found_dir" + fi + if test "$acl_hardcode_minus_L" != no; then + dnl FIXME: Not sure whether we should use + dnl "-L$found_dir -l$name" or "-L$found_dir $found_so" + dnl here. + LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }$found_so" + else + dnl We cannot use $acl_hardcode_runpath_var and LD_RUN_PATH + dnl here, because this doesn't fit in flags passed to the + dnl compiler. So give up. No hardcoding. This affects only + dnl very old systems. + dnl FIXME: Not sure whether we should use + dnl "-L$found_dir -l$name" or "-L$found_dir $found_so" + dnl here. + LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }-l$name" + fi + fi + fi + fi + else + if test "X$found_a" != "X"; then + dnl Linking with a static library. + LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }$found_a" + else + dnl We shouldn't come here, but anyway it's good to have a + dnl fallback. + LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }-L$found_dir -l$name" + fi + fi + dnl Assume the include files are nearby. + additional_includedir= + case "$found_dir" in + */$acl_libdirstem | */$acl_libdirstem/) + basedir=`echo "X$found_dir" | sed -e 's,^X,,' -e "s,/$acl_libdirstem/"'*$,,'` + if test "$name" = '$1'; then + LIB[]NAME[]_PREFIX="$basedir" + fi + additional_includedir="$basedir/include" + ;; + */$acl_libdirstem2 | */$acl_libdirstem2/) + basedir=`echo "X$found_dir" | sed -e 's,^X,,' -e "s,/$acl_libdirstem2/"'*$,,'` + if test "$name" = '$1'; then + LIB[]NAME[]_PREFIX="$basedir" + fi + additional_includedir="$basedir/include" + ;; + esac + if test "X$additional_includedir" != "X"; then + dnl Potentially add $additional_includedir to $INCNAME. + dnl But don't add it + dnl 1. if it's the standard /usr/include, + dnl 2. if it's /usr/local/include and we are using GCC on Linux, + dnl 3. if it's already present in $CPPFLAGS or the already + dnl constructed $INCNAME, + dnl 4. if it doesn't exist as a directory. + if test "X$additional_includedir" != "X/usr/include"; then + haveit= + if test "X$additional_includedir" = "X/usr/local/include"; then + if test -n "$GCC"; then + case $host_os in + linux* | gnu* | k*bsd*-gnu) haveit=yes;; + esac + fi + fi + if test -z "$haveit"; then + for x in $CPPFLAGS $INC[]NAME; do + AC_LIB_WITH_FINAL_PREFIX([eval x=\"$x\"]) + if test "X$x" = "X-I$additional_includedir"; then + haveit=yes + break + fi + done + if test -z "$haveit"; then + if test -d "$additional_includedir"; then + dnl Really add $additional_includedir to $INCNAME. + INC[]NAME="${INC[]NAME}${INC[]NAME:+ }-I$additional_includedir" + fi + fi + fi + fi + fi + dnl Look for dependencies. + if test -n "$found_la"; then + dnl Read the .la file. It defines the variables + dnl dlname, library_names, old_library, dependency_libs, current, + dnl age, revision, installed, dlopen, dlpreopen, libdir. + save_libdir="$libdir" + case "$found_la" in + */* | *\\*) . "$found_la" ;; + *) . "./$found_la" ;; + esac + libdir="$save_libdir" + dnl We use only dependency_libs. + for dep in $dependency_libs; do + case "$dep" in + -L*) + additional_libdir=`echo "X$dep" | sed -e 's/^X-L//'` + dnl Potentially add $additional_libdir to $LIBNAME and $LTLIBNAME. + dnl But don't add it + dnl 1. if it's the standard /usr/lib, + dnl 2. if it's /usr/local/lib and we are using GCC on Linux, + dnl 3. if it's already present in $LDFLAGS or the already + dnl constructed $LIBNAME, + dnl 4. if it doesn't exist as a directory. + if test "X$additional_libdir" != "X/usr/$acl_libdirstem" \ + && test "X$additional_libdir" != "X/usr/$acl_libdirstem2"; then + haveit= + if test "X$additional_libdir" = "X/usr/local/$acl_libdirstem" \ + || test "X$additional_libdir" = "X/usr/local/$acl_libdirstem2"; then + if test -n "$GCC"; then + case $host_os in + linux* | gnu* | k*bsd*-gnu) haveit=yes;; + esac + fi + fi + if test -z "$haveit"; then + haveit= + for x in $LDFLAGS $LIB[]NAME; do + AC_LIB_WITH_FINAL_PREFIX([eval x=\"$x\"]) + if test "X$x" = "X-L$additional_libdir"; then + haveit=yes + break + fi + done + if test -z "$haveit"; then + if test -d "$additional_libdir"; then + dnl Really add $additional_libdir to $LIBNAME. + LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }-L$additional_libdir" + fi + fi + haveit= + for x in $LDFLAGS $LTLIB[]NAME; do + AC_LIB_WITH_FINAL_PREFIX([eval x=\"$x\"]) + if test "X$x" = "X-L$additional_libdir"; then + haveit=yes + break + fi + done + if test -z "$haveit"; then + if test -d "$additional_libdir"; then + dnl Really add $additional_libdir to $LTLIBNAME. + LTLIB[]NAME="${LTLIB[]NAME}${LTLIB[]NAME:+ }-L$additional_libdir" + fi + fi + fi + fi + ;; + -R*) + dir=`echo "X$dep" | sed -e 's/^X-R//'` + if test "$enable_rpath" != no; then + dnl Potentially add DIR to rpathdirs. + dnl The rpathdirs will be appended to $LIBNAME at the end. + haveit= + for x in $rpathdirs; do + if test "X$x" = "X$dir"; then + haveit=yes + break + fi + done + if test -z "$haveit"; then + rpathdirs="$rpathdirs $dir" + fi + dnl Potentially add DIR to ltrpathdirs. + dnl The ltrpathdirs will be appended to $LTLIBNAME at the end. + haveit= + for x in $ltrpathdirs; do + if test "X$x" = "X$dir"; then + haveit=yes + break + fi + done + if test -z "$haveit"; then + ltrpathdirs="$ltrpathdirs $dir" + fi + fi + ;; + -l*) + dnl Handle this in the next round. + names_next_round="$names_next_round "`echo "X$dep" | sed -e 's/^X-l//'` + ;; + *.la) + dnl Handle this in the next round. Throw away the .la's + dnl directory; it is already contained in a preceding -L + dnl option. + names_next_round="$names_next_round "`echo "X$dep" | sed -e 's,^X.*/,,' -e 's,^lib,,' -e 's,\.la$,,'` + ;; + *) + dnl Most likely an immediate library name. + LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }$dep" + LTLIB[]NAME="${LTLIB[]NAME}${LTLIB[]NAME:+ }$dep" + ;; + esac + done + fi + else + dnl Didn't find the library; assume it is in the system directories + dnl known to the linker and runtime loader. (All the system + dnl directories known to the linker should also be known to the + dnl runtime loader, otherwise the system is severely misconfigured.) + LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }-l$name" + LTLIB[]NAME="${LTLIB[]NAME}${LTLIB[]NAME:+ }-l$name" + fi + fi + fi + done + done + if test "X$rpathdirs" != "X"; then + if test -n "$acl_hardcode_libdir_separator"; then + dnl Weird platform: only the last -rpath option counts, the user must + dnl pass all path elements in one option. We can arrange that for a + dnl single library, but not when more than one $LIBNAMEs are used. + alldirs= + for found_dir in $rpathdirs; do + alldirs="${alldirs}${alldirs:+$acl_hardcode_libdir_separator}$found_dir" + done + dnl Note: acl_hardcode_libdir_flag_spec uses $libdir and $wl. + acl_save_libdir="$libdir" + libdir="$alldirs" + eval flag=\"$acl_hardcode_libdir_flag_spec\" + libdir="$acl_save_libdir" + LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }$flag" + else + dnl The -rpath options are cumulative. + for found_dir in $rpathdirs; do + acl_save_libdir="$libdir" + libdir="$found_dir" + eval flag=\"$acl_hardcode_libdir_flag_spec\" + libdir="$acl_save_libdir" + LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }$flag" + done + fi + fi + if test "X$ltrpathdirs" != "X"; then + dnl When using libtool, the option that works for both libraries and + dnl executables is -R. The -R options are cumulative. + for found_dir in $ltrpathdirs; do + LTLIB[]NAME="${LTLIB[]NAME}${LTLIB[]NAME:+ }-R$found_dir" + done + fi + popdef([P_A_C_K]) + popdef([PACKLIBS]) + popdef([PACKUP]) + popdef([PACK]) + popdef([NAME]) +]) + +dnl AC_LIB_APPENDTOVAR(VAR, CONTENTS) appends the elements of CONTENTS to VAR, +dnl unless already present in VAR. +dnl Works only for CPPFLAGS, not for LIB* variables because that sometimes +dnl contains two or three consecutive elements that belong together. +AC_DEFUN([AC_LIB_APPENDTOVAR], +[ + for element in [$2]; do + haveit= + for x in $[$1]; do + AC_LIB_WITH_FINAL_PREFIX([eval x=\"$x\"]) + if test "X$x" = "X$element"; then + haveit=yes + break + fi + done + if test -z "$haveit"; then + [$1]="${[$1]}${[$1]:+ }$element" + fi + done +]) + +dnl For those cases where a variable contains several -L and -l options +dnl referring to unknown libraries and directories, this macro determines the +dnl necessary additional linker options for the runtime path. +dnl AC_LIB_LINKFLAGS_FROM_LIBS([LDADDVAR], [LIBSVALUE], [USE-LIBTOOL]) +dnl sets LDADDVAR to linker options needed together with LIBSVALUE. +dnl If USE-LIBTOOL evaluates to non-empty, linking with libtool is assumed, +dnl otherwise linking without libtool is assumed. +AC_DEFUN([AC_LIB_LINKFLAGS_FROM_LIBS], +[ + AC_REQUIRE([AC_LIB_RPATH]) + AC_REQUIRE([AC_LIB_PREPARE_MULTILIB]) + $1= + if test "$enable_rpath" != no; then + if test -n "$acl_hardcode_libdir_flag_spec" && test "$acl_hardcode_minus_L" = no; then + dnl Use an explicit option to hardcode directories into the resulting + dnl binary. + rpathdirs= + next= + for opt in $2; do + if test -n "$next"; then + dir="$next" + dnl No need to hardcode the standard /usr/lib. + if test "X$dir" != "X/usr/$acl_libdirstem" \ + && test "X$dir" != "X/usr/$acl_libdirstem2"; then + rpathdirs="$rpathdirs $dir" + fi + next= + else + case $opt in + -L) next=yes ;; + -L*) dir=`echo "X$opt" | sed -e 's,^X-L,,'` + dnl No need to hardcode the standard /usr/lib. + if test "X$dir" != "X/usr/$acl_libdirstem" \ + && test "X$dir" != "X/usr/$acl_libdirstem2"; then + rpathdirs="$rpathdirs $dir" + fi + next= ;; + *) next= ;; + esac + fi + done + if test "X$rpathdirs" != "X"; then + if test -n ""$3""; then + dnl libtool is used for linking. Use -R options. + for dir in $rpathdirs; do + $1="${$1}${$1:+ }-R$dir" + done + else + dnl The linker is used for linking directly. + if test -n "$acl_hardcode_libdir_separator"; then + dnl Weird platform: only the last -rpath option counts, the user + dnl must pass all path elements in one option. + alldirs= + for dir in $rpathdirs; do + alldirs="${alldirs}${alldirs:+$acl_hardcode_libdir_separator}$dir" + done + acl_save_libdir="$libdir" + libdir="$alldirs" + eval flag=\"$acl_hardcode_libdir_flag_spec\" + libdir="$acl_save_libdir" + $1="$flag" + else + dnl The -rpath options are cumulative. + for dir in $rpathdirs; do + acl_save_libdir="$libdir" + libdir="$dir" + eval flag=\"$acl_hardcode_libdir_flag_spec\" + libdir="$acl_save_libdir" + $1="${$1}${$1:+ }$flag" + done + fi + fi + fi + fi + fi + AC_SUBST([$1]) +]) diff --git a/external/bsd/pam-u2f/dist/m4/lib-prefix.m4 b/external/bsd/pam-u2f/dist/m4/lib-prefix.m4 new file mode 100644 index 000000000000..3bdc0fc574ab --- /dev/null +++ b/external/bsd/pam-u2f/dist/m4/lib-prefix.m4 @@ -0,0 +1,221 @@ +# lib-prefix.m4 serial 6 (gettext-0.18) +dnl Copyright (C) 2001-2005, 2008 Free Software Foundation, Inc. +dnl This file is free software; the Free Software Foundation +dnl gives unlimited permission to copy and/or distribute it, +dnl with or without modifications, as long as this notice is preserved. + +dnl From Bruno Haible. + +dnl AC_LIB_ARG_WITH is synonymous to AC_ARG_WITH in autoconf-2.13, and +dnl similar to AC_ARG_WITH in autoconf 2.52...2.57 except that is doesn't +dnl require excessive bracketing. +ifdef([AC_HELP_STRING], +[AC_DEFUN([AC_LIB_ARG_WITH], [AC_ARG_WITH([$1],[[$2]],[$3],[$4])])], +[AC_DEFUN([AC_][LIB_ARG_WITH], [AC_ARG_WITH([$1],[$2],[$3],[$4])])]) + +dnl AC_LIB_PREFIX adds to the CPPFLAGS and LDFLAGS the flags that are needed +dnl to access previously installed libraries. The basic assumption is that +dnl a user will want packages to use other packages he previously installed +dnl with the same --prefix option. +dnl This macro is not needed if only AC_LIB_LINKFLAGS is used to locate +dnl libraries, but is otherwise very convenient. +AC_DEFUN([AC_LIB_PREFIX], +[ + AC_BEFORE([$0], [AC_LIB_LINKFLAGS]) + AC_REQUIRE([AC_PROG_CC]) + AC_REQUIRE([AC_CANONICAL_HOST]) + AC_REQUIRE([AC_LIB_PREPARE_MULTILIB]) + AC_REQUIRE([AC_LIB_PREPARE_PREFIX]) + dnl By default, look in $includedir and $libdir. + use_additional=yes + AC_LIB_WITH_FINAL_PREFIX([ + eval additional_includedir=\"$includedir\" + eval additional_libdir=\"$libdir\" + ]) + AC_LIB_ARG_WITH([lib-prefix], +[ --with-lib-prefix[=DIR] search for libraries in DIR/include and DIR/lib + --without-lib-prefix don't search for libraries in includedir and libdir], +[ + if test "X$withval" = "Xno"; then + use_additional=no + else + if test "X$withval" = "X"; then + AC_LIB_WITH_FINAL_PREFIX([ + eval additional_includedir=\"$includedir\" + eval additional_libdir=\"$libdir\" + ]) + else + additional_includedir="$withval/include" + additional_libdir="$withval/$acl_libdirstem" + fi + fi +]) + if test $use_additional = yes; then + dnl Potentially add $additional_includedir to $CPPFLAGS. + dnl But don't add it + dnl 1. if it's the standard /usr/include, + dnl 2. if it's already present in $CPPFLAGS, + dnl 3. if it's /usr/local/include and we are using GCC on Linux, + dnl 4. if it doesn't exist as a directory. + if test "X$additional_includedir" != "X/usr/include"; then + haveit= + for x in $CPPFLAGS; do + AC_LIB_WITH_FINAL_PREFIX([eval x=\"$x\"]) + if test "X$x" = "X-I$additional_includedir"; then + haveit=yes + break + fi + done + if test -z "$haveit"; then + if test "X$additional_includedir" = "X/usr/local/include"; then + if test -n "$GCC"; then + case $host_os in + linux* | gnu* | k*bsd*-gnu) haveit=yes;; + esac + fi + fi + if test -z "$haveit"; then + if test -d "$additional_includedir"; then + dnl Really add $additional_includedir to $CPPFLAGS. + CPPFLAGS="${CPPFLAGS}${CPPFLAGS:+ }-I$additional_includedir" + fi + fi + fi + fi + dnl Potentially add $additional_libdir to $LDFLAGS. + dnl But don't add it + dnl 1. if it's the standard /usr/lib, + dnl 2. if it's already present in $LDFLAGS, + dnl 3. if it's /usr/local/lib and we are using GCC on Linux, + dnl 4. if it doesn't exist as a directory. + if test "X$additional_libdir" != "X/usr/$acl_libdirstem"; then + haveit= + for x in $LDFLAGS; do + AC_LIB_WITH_FINAL_PREFIX([eval x=\"$x\"]) + if test "X$x" = "X-L$additional_libdir"; then + haveit=yes + break + fi + done + if test -z "$haveit"; then + if test "X$additional_libdir" = "X/usr/local/$acl_libdirstem"; then + if test -n "$GCC"; then + case $host_os in + linux*) haveit=yes;; + esac + fi + fi + if test -z "$haveit"; then + if test -d "$additional_libdir"; then + dnl Really add $additional_libdir to $LDFLAGS. + LDFLAGS="${LDFLAGS}${LDFLAGS:+ }-L$additional_libdir" + fi + fi + fi + fi + fi +]) + +dnl AC_LIB_PREPARE_PREFIX creates variables acl_final_prefix, +dnl acl_final_exec_prefix, containing the values to which $prefix and +dnl $exec_prefix will expand at the end of the configure script. +AC_DEFUN([AC_LIB_PREPARE_PREFIX], +[ + dnl Unfortunately, prefix and exec_prefix get only finally determined + dnl at the end of configure. + if test "X$prefix" = "XNONE"; then + acl_final_prefix="$ac_default_prefix" + else + acl_final_prefix="$prefix" + fi + if test "X$exec_prefix" = "XNONE"; then + acl_final_exec_prefix='${prefix}' + else + acl_final_exec_prefix="$exec_prefix" + fi + acl_save_prefix="$prefix" + prefix="$acl_final_prefix" + eval acl_final_exec_prefix=\"$acl_final_exec_prefix\" + prefix="$acl_save_prefix" +]) + +dnl AC_LIB_WITH_FINAL_PREFIX([statement]) evaluates statement, with the +dnl variables prefix and exec_prefix bound to the values they will have +dnl at the end of the configure script. +AC_DEFUN([AC_LIB_WITH_FINAL_PREFIX], +[ + acl_save_prefix="$prefix" + prefix="$acl_final_prefix" + acl_save_exec_prefix="$exec_prefix" + exec_prefix="$acl_final_exec_prefix" + $1 + exec_prefix="$acl_save_exec_prefix" + prefix="$acl_save_prefix" +]) + +dnl AC_LIB_PREPARE_MULTILIB creates +dnl - a variable acl_libdirstem, containing the basename of the libdir, either +dnl "lib" or "lib64" or "lib/64", +dnl - a variable acl_libdirstem2, as a secondary possible value for +dnl acl_libdirstem, either the same as acl_libdirstem or "lib/sparcv9" or +dnl "lib/amd64". +AC_DEFUN([AC_LIB_PREPARE_MULTILIB], +[ + dnl There is no formal standard regarding lib and lib64. + dnl On glibc systems, the current practice is that on a system supporting + dnl 32-bit and 64-bit instruction sets or ABIs, 64-bit libraries go under + dnl $prefix/lib64 and 32-bit libraries go under $prefix/lib. We determine + dnl the compiler's default mode by looking at the compiler's library search + dnl path. If at least one of its elements ends in /lib64 or points to a + dnl directory whose absolute pathname ends in /lib64, we assume a 64-bit ABI. + dnl Otherwise we use the default, namely "lib". + dnl On Solaris systems, the current practice is that on a system supporting + dnl 32-bit and 64-bit instruction sets or ABIs, 64-bit libraries go under + dnl $prefix/lib/64 (which is a symlink to either $prefix/lib/sparcv9 or + dnl $prefix/lib/amd64) and 32-bit libraries go under $prefix/lib. + AC_REQUIRE([AC_CANONICAL_HOST]) + acl_libdirstem=lib + acl_libdirstem2= + case "$host_os" in + solaris*) + dnl See Solaris 10 Software Developer Collection > Solaris 64-bit Developer's Guide > The Development Environment + dnl . + dnl "Portable Makefiles should refer to any library directories using the 64 symbolic link." + dnl But we want to recognize the sparcv9 or amd64 subdirectory also if the + dnl symlink is missing, so we set acl_libdirstem2 too. + AC_CACHE_CHECK([for 64-bit host], [gl_cv_solaris_64bit], + [AC_EGREP_CPP([sixtyfour bits], [ +#ifdef _LP64 +sixtyfour bits +#endif + ], [gl_cv_solaris_64bit=yes], [gl_cv_solaris_64bit=no]) + ]) + if test $gl_cv_solaris_64bit = yes; then + acl_libdirstem=lib/64 + case "$host_cpu" in + sparc*) acl_libdirstem2=lib/sparcv9 ;; + i*86 | x86_64) acl_libdirstem2=lib/amd64 ;; + esac + fi + ;; + *) + searchpath=`(LC_ALL=C $CC -print-search-dirs) 2>/dev/null | sed -n -e 's,^libraries: ,,p' | sed -e 's,^=,,'` + if test -n "$searchpath"; then + acl_save_IFS="${IFS= }"; IFS=":" + for searchdir in $searchpath; do + if test -d "$searchdir"; then + case "$searchdir" in + */lib64/ | */lib64 ) acl_libdirstem=lib64 ;; + *) searchdir=`cd "$searchdir" && pwd` + case "$searchdir" in + */lib64 ) acl_libdirstem=lib64 ;; + esac ;; + esac + fi + done + IFS="$acl_save_IFS" + fi + ;; + esac + test -n "$acl_libdirstem2" || acl_libdirstem2="$acl_libdirstem" +]) diff --git a/external/bsd/pam-u2f/dist/man/pam_u2f.8.txt b/external/bsd/pam-u2f/dist/man/pam_u2f.8.txt new file mode 100644 index 000000000000..e2cf2c56e224 --- /dev/null +++ b/external/bsd/pam-u2f/dist/man/pam_u2f.8.txt @@ -0,0 +1,142 @@ +PAM_U2F(8) +========== +:doctype: manpage +:man source: pam-u2f +:man manual: PAM U2F Module Manual + +== NAME +pam_u2f - Module for U2F authentication + +== SYNOPSIS +*pam_u2f* [...] + +== DESCRIPTION +The module provides U2F authentication against Yubikeys and other compliant authenticators. + +== OPTIONS +*debug*:: +Enables debug output + +*debug_file*:: +Filename to write debugging messages to. *If this file is missing, nothing will be logged*. This regular file *has to be created by the user* +or *must exist and be a regular file* for anything getting logged to it. It is not created by pam-u2f on purpose (for security considerations). +This filename may be alternatively set to "stderr" (default), "stdout", or "syslog". + +*origin*=_origin_:: +Set the origin for the U2F authentication procedure. If no value is specified, the origin "pam://$HOSTNAME" is used. + +*appid*=_appid_:: +Set the application ID for the U2F authentication procedure. If no value is specified, the same value used for origin is taken ("pam://$HOSTNAME" if also origin is not specified). + +*authfile*=_file_:: +Set the location of the file that holds the mappings of user names to keyHandles and user keys. The format is username:keyHandle1,public_key1:keyHandle2,public_key2:... the default location of the file is $XDG_CONFIG_HOME/Yubico/u2f_keys. If the environment variable is not set, $HOME/.config/Yubico/u2f_keys is used. +An individual (per user) file may be configured relative to the users' home dirs, i.e. ".ssh/u2f_keys". + +*authpending_file*=_file_:: +Set the location of the file that is used for touch request notifications. This file will be opened when pam-u2f starts waiting for a user to touch the device, and will be closed when it no longer waits for a touch. Use inotify to listen on these events, or a more high-level tool like yubikey-touch-detector. Default value: /var/run/user/$UID/pam-u2f-authpending. Set an empty value in order to disable this functionality, like so: "authpending_file=". + +*nouserok*:: +Set to enable authentication attempts to succeed even if the user trying to authenticate is not found inside authfile or if authfile is missing/malformed. + +*openasuser*:: +Setuid to the authenticating user when opening the authfile. Useful when the user's home is stored on an NFS volume mounted with the root_squash option (which maps root to nobody which will not be able to read the file). +Note that after release 1.0.8 this is done by default when no global authfile or XDG_CONFIG_HOME environment variable has been set. + +*alwaysok*:: +Set to enable all authentication attempts to succeed (aka presentation mode). + +*max_devices*=_n_devices_:: +Maximum number of devices allowed per user (default is 24). Devices specified +in the authentication file that exceed this value will be ignored. + +*interactive*:: +Set to prompt a message and wait before testing the presence of a U2F device. Recommended if your device doesn't have tactile trigger. + +*[prompt=your prompt here]*:: +Set individual prompt message for interactive mode. Watch the square brackets +around this parameter to get spaces correctly recognized by PAM. + +*manual*:: +Set to drop to a manual console where challenges are printed on screen and response read from standard input. Useful for debugging and SSH sessions without U2F-support from the SSH client/server. If enabled, interactive mode becomes redundant and has no effect. + +*cue*:: +Set to prompt a message to remind to touch the device. + +*[cue_prompt=your prompt here]*:: +Set individual prompt message for the cue option. Watch the square brackets +around this parameter to get spaces correctly recognized by PAM. + +*nodetect*:: +Skip detecting if a suitable key is inserted before performing a full authentication. See *NOTES* below. + +*userpresence*=_int_:: +If 1, require user presence during authentication. If 0, do not request user +presence during authentication. Otherwise, fallback to the authenticator's +default behaviour. + +*userverification*=_int_:: +If 1, require user verification during authentication. If 0, do not request +user verification during authentication. Otherwise, fallback to the +authenticator's default behaviour. + +*pinverification*=_int_:: +If 1, require PIN verification during authentication. If 0, do not request +PIN verification during authentication. Otherwise, fallback to the +authenticator's default behaviour. + +== EXAMPLES + +auth sufficient pam_u2f.so debug origin=pam://$HOSTNAME appid=pam://$HOSTNAME + +auth required pam_u2f.so origin=http://example.com appid=http://example.com authfile=/etc/yubikey_mappings + +== CAVEATS +By default the mapping file inside a home directory will be opened as +the target user, whereas the central file will be opened as "root". If +the "XDG_CONFIG_HOME" variable is set, privileges will not be dropped +unless the "openasuser" configuration setting is set. + +Using pam-u2f to secure the login to a computer while +storing the mapping file in an encrypted home directory, will result +in the impossibility of logging into the system. The partition is +decrypted after login and the mapping file can not be accessed. + +== NOTES +The "nodetect" option should be used with caution. pam_u2f checks that a +key configured for the user is inserted before performing the full tactile +authentication. This detection is done by sending a "check-only" +authentication request to all inserted tokens to so see if at least one of +them responds affirmatively to one or more of the keyhandles configured for +the user. By doing this, pam_u2f can avoid emitting the "cue" prompt (if +configured), which can cause some confusing UI issues if the cue is emitted +followed by the underlying library immediately failing the tactile +authentication. This option is also useful to avoid an unintended 1-second +delay prior to the tactile authentication caused by versions of libu2f-host +\<= 1.1.5. + +If pam_u2f is configured to "cue" and "nodetect", an attacker can determine +that pam_u2f is part of the authentication stack by inserting any random +U2F token and performing an authentication attempt. In this scenario, the +attacker would see the cue message followed by an immediate failure, +whereas with detection enabled, the U2F authentication will fail silently. +Understand that an attacker could choose a U2F token that alerts him or +her in some way to the "check-only" authentication attempt, so this +precaution only pushes the issue back a step. + +In summary, the detection feature was added to avoid confusing UI issues +and to prevent leaking information about the authentication stack in very +specific scenario when "cue" is configured. The "nodetect" option was added +to avoid buggy sleep behavior in older versions of libu2f-host and for +hypothetical tokens that do not tolerate the double authentication. +Detection is performed, and likewise "nodetect" honored, regardless of +whether "cue" is also specified. + +== BUGS +Report pam-u2f bugs in the issue tracker: https://github.com/Yubico/pam-u2f/issues + +== SEE ALSO +*pam*(7) + +The pam-u2f home page: https://developers.yubico.com/pam-u2f/ + +YubiKeys can be obtained from Yubico: http://www.yubico.com/ diff --git a/external/bsd/pam-u2f/dist/man/pamu2fcfg.1.txt b/external/bsd/pam-u2f/dist/man/pamu2fcfg.1.txt new file mode 100644 index 000000000000..85c231a6047c --- /dev/null +++ b/external/bsd/pam-u2f/dist/man/pamu2fcfg.1.txt @@ -0,0 +1,60 @@ +PAMU2FCFG(1) +============ +:doctype: manpage +:man source: pamu2fcfg +:man manual: PAM U2F Configuration Tool + +== NAME +pamu2fcfg - Configuration tool for the U2F PAM module. + +== SYNOPSIS +*pamu2fcfg* [_OPTION_]... + +== DESCRIPTION +Perform a U2F registration procedure using a connected U2F token and output a configuration line that can be used with the U2F PAM module. + +== OPTIONS +*-d*, *--debug*:: +Print debug information (highly verbose) + +*-h*, *--help*:: +Print help and exit + +*-o*, *--origin*=_STRING_:: +Origin URL to use during registration. Defaults to pam://hostname + +*-i*, *--appid*=_STRING_:: +Application ID to use during registration. Defaults to *origin* + +*-r*, *--resident*:: +Generate a resident credential. Defaults to off. + +*-t*, *--type*=_STRING_:: +COSE type to use during registration (ES256 or RS256). Defaults to ES256. + +*-N*, *--pin-verification*:: +Require PIN verification during authentication. Defaults to off. + +*-V*, *--user-verification*:: +Require user verification during authentication. Defaults to off. + +*--version*: +*Print version and exit* + +Group: user (mutually exclusive) + +*-u*, *--username*=_STRING_:: +The name of the user registering the device. Defaults to the current user name + +*-n*, *--nouser*:: +Print only registration information (keyHandle and public key). Useful for appending + +== BUGS +Report pamu2fcfg bugs in the issue tracker: https://github.com/Yubico/pam-u2f/issues + +== SEE ALSO +*pam_u2f*(8), *pam*(7) + +The pam-u2f home page: https://developers.yubico.com/pam-u2f/ + +YubiKeys can be obtained from Yubico: https://www.yubico.com/ diff --git a/external/bsd/pam-u2f/dist/pam-u2f.c b/external/bsd/pam-u2f/dist/pam-u2f.c new file mode 100644 index 000000000000..40cf40c35ba7 --- /dev/null +++ b/external/bsd/pam-u2f/dist/pam-u2f.c @@ -0,0 +1,472 @@ +/* + * Copyright (C) 2014-2019 Yubico AB - See COPYING + */ + +/* Define which PAM interfaces we provide */ +#define PAM_SM_AUTH + +/* Include PAM headers */ +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "util.h" +#include "drop_privs.h" + +/* If secure_getenv is not defined, define it here */ +#ifndef HAVE_SECURE_GETENV +char *secure_getenv(const char *); +char *secure_getenv(const char *name) { + (void) name; + return NULL; +} +#endif + +static void parse_cfg(int flags, int argc, const char **argv, cfg_t *cfg) { + struct stat st; + FILE *file = NULL; + int fd = -1; + int i; + + memset(cfg, 0, sizeof(cfg_t)); + cfg->debug_file = stderr; + cfg->userpresence = -1; + cfg->userverification = -1; + cfg->pinverification = -1; + + for (i = 0; i < argc; i++) { + if (strncmp(argv[i], "max_devices=", 12) == 0) + sscanf(argv[i], "max_devices=%u", &cfg->max_devs); + if (strcmp(argv[i], "manual") == 0) + cfg->manual = 1; + if (strcmp(argv[i], "debug") == 0) + cfg->debug = 1; + if (strcmp(argv[i], "nouserok") == 0) + cfg->nouserok = 1; + if (strcmp(argv[i], "openasuser") == 0) + cfg->openasuser = 1; + if (strcmp(argv[i], "alwaysok") == 0) + cfg->alwaysok = 1; + if (strcmp(argv[i], "interactive") == 0) + cfg->interactive = 1; + if (strcmp(argv[i], "cue") == 0) + cfg->cue = 1; + if (strcmp(argv[i], "nodetect") == 0) + cfg->nodetect = 1; + if (strncmp(argv[i], "userpresence=", 13) == 0) + sscanf(argv[i], "userpresence=%d", &cfg->userpresence); + if (strncmp(argv[i], "userverification=", 17) == 0) + sscanf(argv[i], "userverification=%d", &cfg->userverification); + if (strncmp(argv[i], "pinverification=", 16) == 0) + sscanf(argv[i], "pinverification=%d", &cfg->pinverification); + if (strncmp(argv[i], "authfile=", 9) == 0) + cfg->auth_file = argv[i] + 9; + if (strncmp(argv[i], "authpending_file=", 17) == 0) + cfg->authpending_file = argv[i] + 17; + if (strncmp(argv[i], "origin=", 7) == 0) + cfg->origin = argv[i] + 7; + if (strncmp(argv[i], "appid=", 6) == 0) + cfg->appid = argv[i] + 6; + if (strncmp(argv[i], "prompt=", 7) == 0) + cfg->prompt = argv[i] + 7; + if (strncmp(argv[i], "cue_prompt=", 11) == 0) + cfg->cue_prompt = argv[i] + 11; + if (strncmp(argv[i], "debug_file=", 11) == 0) { + const char *filename = argv[i] + 11; + if (strncmp(filename, "stdout", 6) == 0) { + cfg->debug_file = stdout; + } else if (strncmp(filename, "stderr", 6) == 0) { + cfg->debug_file = stderr; + } else if (strncmp(filename, "syslog", 6) == 0) { + cfg->debug_file = (FILE *) -1; + } else { + fd = open(filename, + O_WRONLY | O_APPEND | O_CLOEXEC | O_NOFOLLOW | O_NOCTTY); + if (fd >= 0 && (fstat(fd, &st) == 0) && S_ISREG(st.st_mode)) { + file = fdopen(fd, "a"); + if (file != NULL) { + cfg->debug_file = file; + cfg->is_custom_debug_file = 1; + file = NULL; + fd = -1; + } + } + } + } + } + + if (cfg->debug) { + D(cfg->debug_file, "called."); + D(cfg->debug_file, "flags %d argc %d", flags, argc); + for (i = 0; i < argc; i++) { + D(cfg->debug_file, "argv[%d]=%s", i, argv[i]); + } + D(cfg->debug_file, "max_devices=%d", cfg->max_devs); + D(cfg->debug_file, "debug=%d", cfg->debug); + D(cfg->debug_file, "interactive=%d", cfg->interactive); + D(cfg->debug_file, "cue=%d", cfg->cue); + D(cfg->debug_file, "nodetect=%d", cfg->nodetect); + D(cfg->debug_file, "userpresence=%d", cfg->userpresence); + D(cfg->debug_file, "userverification=%d", cfg->userverification); + D(cfg->debug_file, "pinverification=%d", cfg->pinverification); + D(cfg->debug_file, "manual=%d", cfg->manual); + D(cfg->debug_file, "nouserok=%d", cfg->nouserok); + D(cfg->debug_file, "openasuser=%d", cfg->openasuser); + D(cfg->debug_file, "alwaysok=%d", cfg->alwaysok); + D(cfg->debug_file, "authfile=%s", + cfg->auth_file ? cfg->auth_file : "(null)"); + D(cfg->debug_file, "authpending_file=%s", + cfg->authpending_file ? cfg->authpending_file : "(null)"); + D(cfg->debug_file, "origin=%s", cfg->origin ? cfg->origin : "(null)"); + D(cfg->debug_file, "appid=%s", cfg->appid ? cfg->appid : "(null)"); + D(cfg->debug_file, "prompt=%s", cfg->prompt ? cfg->prompt : "(null)"); + } + + if (fd != -1) + close(fd); + + if (file != NULL) + fclose(file); +} + +#ifdef DBG +#undef DBG +#endif +#define DBG(...) \ + if (cfg->debug) { \ + D(cfg->debug_file, __VA_ARGS__); \ + } + +/* PAM entry point for authentication verification */ +int pam_sm_authenticate(pam_handle_t *pamh, int flags, int argc, + const char **argv) { + + struct passwd *pw = NULL, pw_s; + const char *user = NULL; + + cfg_t cfg_st; + cfg_t *cfg = &cfg_st; + char buffer[BUFSIZE]; + char *buf = NULL; + char *authfile_dir; + size_t authfile_dir_len; + int pgu_ret, gpn_ret; + int retval = PAM_IGNORE; + device_t *devices = NULL; + unsigned n_devices = 0; + int openasuser = 0; + int should_free_origin = 0; + int should_free_appid = 0; + int should_free_auth_file = 0; + int should_free_authpending_file = 0; + PAM_MODUTIL_DEF_PRIVS(privs); + + parse_cfg(flags, argc, argv, cfg); + + if (!cfg->origin) { + strcpy(buffer, DEFAULT_ORIGIN_PREFIX); + + if (gethostname(buffer + strlen(DEFAULT_ORIGIN_PREFIX), + BUFSIZE - strlen(DEFAULT_ORIGIN_PREFIX)) == -1) { + DBG("Unable to get host name"); + goto done; + } + DBG("Origin not specified, using \"%s\"", buffer); + cfg->origin = strdup(buffer); + if (!cfg->origin) { + DBG("Unable to allocate memory"); + goto done; + } else { + should_free_origin = 1; + } + } + + if (!cfg->appid) { + DBG("Appid not specified, using the same value of origin (%s)", + cfg->origin); + cfg->appid = strdup(cfg->origin); + if (!cfg->appid) { + DBG("Unable to allocate memory") + goto done; + } else { + should_free_appid = 1; + } + } + + if (cfg->max_devs == 0) { + DBG("Maximum devices number not set. Using default (%d)", MAX_DEVS); + cfg->max_devs = MAX_DEVS; + } + + devices = calloc(cfg->max_devs, sizeof(device_t)); + if (!devices) { + DBG("Unable to allocate memory"); + retval = PAM_IGNORE; + goto done; + } + + pgu_ret = pam_get_user(pamh, &user, NULL); + if (pgu_ret != PAM_SUCCESS || user == NULL) { + DBG("Unable to access user %s", user); + retval = PAM_CONV_ERR; + goto done; + } + + DBG("Requesting authentication for user %s", user); + + gpn_ret = getpwnam_r(user, &pw_s, buffer, sizeof(buffer), &pw); + if (gpn_ret != 0 || pw == NULL || pw->pw_dir == NULL || + pw->pw_dir[0] != '/') { + DBG("Unable to retrieve credentials for user %s, (%s)", user, + strerror(errno)); + retval = PAM_USER_UNKNOWN; + goto done; + } + + DBG("Found user %s", user); + DBG("Home directory for %s is %s", user, pw->pw_dir); + + if (!cfg->auth_file) { + buf = NULL; + authfile_dir = secure_getenv(DEFAULT_AUTHFILE_DIR_VAR); + if (!authfile_dir) { + DBG("Variable %s is not set. Using default value ($HOME/.config/)", + DEFAULT_AUTHFILE_DIR_VAR); + authfile_dir_len = + strlen(pw->pw_dir) + strlen("/.config") + strlen(DEFAULT_AUTHFILE) + 1; + buf = malloc(sizeof(char) * (authfile_dir_len)); + + if (!buf) { + DBG("Unable to allocate memory"); + retval = PAM_IGNORE; + goto done; + } + + /* Opening a file in a users $HOME, need to drop privs for security */ + openasuser = geteuid() == 0 ? 1 : 0; + + snprintf(buf, authfile_dir_len, "%s/.config%s", pw->pw_dir, + DEFAULT_AUTHFILE); + } else { + DBG("Variable %s set to %s", DEFAULT_AUTHFILE_DIR_VAR, authfile_dir); + authfile_dir_len = strlen(authfile_dir) + strlen(DEFAULT_AUTHFILE) + 1; + buf = malloc(sizeof(char) * (authfile_dir_len)); + + if (!buf) { + DBG("Unable to allocate memory"); + retval = PAM_IGNORE; + goto done; + } + + snprintf(buf, authfile_dir_len, "%s%s", authfile_dir, DEFAULT_AUTHFILE); + + if (!cfg->openasuser) { + DBG("WARNING: not dropping privileges when reading %s, please " + "consider setting openasuser=1 in the module configuration", + buf); + } + } + + DBG("Using authentication file %s", buf); + + cfg->auth_file = buf; /* cfg takes ownership */ + should_free_auth_file = 1; + buf = NULL; + } else { + if (cfg->auth_file[0] != '/') { + /* Individual authorization mapping by user: auth_file is not + absolute path, so prepend user home dir. */ + openasuser = geteuid() == 0 ? 1 : 0; + + authfile_dir_len = + strlen(pw->pw_dir) + strlen("/") + strlen(cfg->auth_file) + 1; + buf = malloc(sizeof(char) * (authfile_dir_len)); + + if (!buf) { + DBG("Unable to allocate memory"); + retval = PAM_IGNORE; + goto done; + } + + snprintf(buf, authfile_dir_len, "%s/%s", pw->pw_dir, cfg->auth_file); + + cfg->auth_file = buf; /* update cfg */ + should_free_auth_file = 1; + buf = NULL; + } + + DBG("Using authentication file %s", cfg->auth_file); + } + + if (!openasuser) { + openasuser = geteuid() == 0 && cfg->openasuser; + } + if (openasuser) { + DBG("Dropping privileges"); + if (pam_modutil_drop_priv(pamh, &privs, pw)) { + DBG("Unable to switch user to uid %i", pw->pw_uid); + retval = PAM_IGNORE; + goto done; + } + DBG("Switched to uid %i", pw->pw_uid); + } + retval = + get_devices_from_authfile(cfg->auth_file, user, cfg->max_devs, cfg->debug, + cfg->debug_file, devices, &n_devices); + if (openasuser) { + if (pam_modutil_regain_priv(pamh, &privs)) { + DBG("could not restore privileges"); + retval = PAM_IGNORE; + goto done; + } + DBG("Restored privileges"); + } + + if (retval != 1) { + // for nouserok; make sure errors in get_devices_from_authfile don't + // result in valid devices + n_devices = 0; + } + + if (n_devices == 0) { + if (cfg->nouserok) { + DBG("Found no devices but nouserok specified. Skipping authentication"); + retval = PAM_SUCCESS; + goto done; + } else if (retval != 1) { + DBG("Unable to get devices from file %s", cfg->auth_file); + retval = PAM_AUTHINFO_UNAVAIL; + goto done; + } else { + DBG("Found no devices. Aborting."); + retval = PAM_AUTHINFO_UNAVAIL; + goto done; + } + } + + // Determine the full path for authpending_file in order to emit touch request + // notifications + if (!cfg->authpending_file) { + int actual_size = + snprintf(buffer, BUFSIZE, DEFAULT_AUTHPENDING_FILE_PATH, getuid()); + if (actual_size >= 0 && actual_size < BUFSIZE) { + cfg->authpending_file = strdup(buffer); + } + if (!cfg->authpending_file) { + DBG("Unable to allocate memory for the authpending_file, touch request " + "notifications will not be emitted"); + } else { + should_free_authpending_file = 1; + } + } else { + if (strlen(cfg->authpending_file) == 0) { + DBG("authpending_file is set to an empty value, touch request " + "notifications will be disabled"); + cfg->authpending_file = NULL; + } + } + + int authpending_file_descriptor = -1; + if (cfg->authpending_file) { + DBG("Using file '%s' for emitting touch request notifications", + cfg->authpending_file); + + // Open (or create) the authpending_file to indicate that we start waiting + // for a touch + authpending_file_descriptor = + open(cfg->authpending_file, + O_RDONLY | O_CREAT | O_CLOEXEC | O_NOFOLLOW | O_NOCTTY, 0664); + if (authpending_file_descriptor < 0) { + DBG("Unable to emit 'authentication started' notification by opening the " + "file '%s', (%s)", + cfg->authpending_file, strerror(errno)); + } + } + + if (cfg->manual == 0) { + if (cfg->interactive) { + converse(pamh, PAM_PROMPT_ECHO_ON, + cfg->prompt != NULL ? cfg->prompt : DEFAULT_PROMPT); + } + + retval = do_authentication(cfg, devices, n_devices, pamh); + } else { + retval = do_manual_authentication(cfg, devices, n_devices, pamh); + } + + // Close the authpending_file to indicate that we stop waiting for a touch + if (authpending_file_descriptor >= 0) { + if (close(authpending_file_descriptor) < 0) { + DBG("Unable to emit 'authentication stopped' notification by closing the " + "file '%s', (%s)", + cfg->authpending_file, strerror(errno)); + } + } + + if (retval != 1) { + DBG("do_authentication returned %d", retval); + retval = PAM_AUTH_ERR; + goto done; + } + + retval = PAM_SUCCESS; + +done: + free_devices(devices, n_devices); + + if (buf) { + free(buf); + buf = NULL; + } + + if (should_free_origin) { + free((char *) cfg->origin); + cfg->origin = NULL; + } + + if (should_free_appid) { + free((char *) cfg->appid); + cfg->appid = NULL; + } + + if (should_free_auth_file) { + free((char *) cfg->auth_file); + cfg->auth_file = NULL; + } + + if (should_free_authpending_file) { + free((char *) cfg->authpending_file); + cfg->authpending_file = NULL; + } + + if (cfg->alwaysok && retval != PAM_SUCCESS) { + DBG("alwaysok needed (otherwise return with %d)", retval); + retval = PAM_SUCCESS; + } + DBG("done. [%s]", pam_strerror(pamh, retval)); + + if (cfg->is_custom_debug_file) { + fclose(cfg->debug_file); + } + + return retval; +} + +PAM_EXTERN int pam_sm_setcred(pam_handle_t *pamh, int flags, int argc, + const char **argv) { + (void) pamh; + (void) flags; + (void) argc; + (void) argv; + + return PAM_SUCCESS; +} diff --git a/external/bsd/pam-u2f/dist/pamu2fcfg/Makefile.am b/external/bsd/pam-u2f/dist/pamu2fcfg/Makefile.am new file mode 100644 index 000000000000..737f81c7f9a3 --- /dev/null +++ b/external/bsd/pam-u2f/dist/pamu2fcfg/Makefile.am @@ -0,0 +1,31 @@ +# Copyright (C) 2014-2018 Yubico AB - See COPYING + +AM_CFLAGS = $(WARN_CFLAGS) +AM_CPPFLAGS=-I$(srcdir)/.. -I$(builddir)/.. $(LIBFIDO2_CFLAGS) + +bin_PROGRAMS = pamu2fcfg + +pamu2fcfg_SOURCES = pamu2fcfg.c +pamu2fcfg_SOURCES += cmdline.ggo cmdline.c cmdline.h +pamu2fcfg_SOURCES += readpassphrase.c readpassphrase.h +pamu2fcfg_SOURCES += ../util.c ../b64.c ../explicit_bzero.c +pamu2fcfg_LDADD = $(LIBFIDO2_LIBS) $(LIBCRYPTO_LIBS) + +cmdline.c cmdline.h: cmdline.ggo Makefile.am + gengetopt --no-handle-help --input $^ + +BUILT_SOURCES = cmdline.c cmdline.h +MAINTAINERCLEANFILES = $(BUILT_SOURCES) + +if ENABLE_MAN +dist_man1_MANS = $(top_builddir)/man/pamu2fcfg.1 +DISTCLEANFILES = $(dist_man1_MANS) + +MANSOURCES = $(top_builddir)/man/pamu2fcfg.1.txt +EXTRA_DIST = $(MANSOURCES) + +SUFFIXES = .1.txt .1 + +.1.txt.1: + $(A2X) --format=manpage -L -a revdate="Version $(VERSION)" $< +endif diff --git a/external/bsd/pam-u2f/dist/pamu2fcfg/_readpassphrase.h b/external/bsd/pam-u2f/dist/pamu2fcfg/_readpassphrase.h new file mode 100644 index 000000000000..0c4a59e5ff9a --- /dev/null +++ b/external/bsd/pam-u2f/dist/pamu2fcfg/_readpassphrase.h @@ -0,0 +1,42 @@ +/* $OpenBSD: readpassphrase.h,v 1.5 2003/06/17 21:56:23 millert Exp $ */ + +/* + * Copyright (c) 2000, 2002 Todd C. Miller + * + * 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 THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR 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. + * + * Sponsored in part by the Defense Advanced Research Projects + * Agency (DARPA) and Air Force Research Laboratory, Air Force + * Materiel Command, USAF, under agreement number F39502-99-1-0512. + */ + +/* OPENBSD ORIGINAL: include/readpassphrase.h */ + +#ifndef _READPASSPHRASE_H_ +#define _READPASSPHRASE_H_ + +#ifndef HAVE_READPASSPHRASE + +#define RPP_ECHO_OFF 0x00 /* Turn off echo (default). */ +#define RPP_ECHO_ON 0x01 /* Leave echo on. */ +#define RPP_REQUIRE_TTY 0x02 /* Fail if there is no tty. */ +#define RPP_FORCELOWER 0x04 /* Force input to lower case. */ +#define RPP_FORCEUPPER 0x08 /* Force input to upper case. */ +#define RPP_SEVENBIT 0x10 /* Strip the high bit from input. */ +#define RPP_STDIN 0x20 /* Read from stdin, not /dev/tty */ + +char * readpassphrase(const char *, char *, size_t, int); + +#endif /* HAVE_READPASSPHRASE */ + +#endif /* !_READPASSPHRASE_H_ */ diff --git a/external/bsd/pam-u2f/dist/pamu2fcfg/cmdline.ggo b/external/bsd/pam-u2f/dist/pamu2fcfg/cmdline.ggo new file mode 100644 index 000000000000..23f79c3e01f0 --- /dev/null +++ b/external/bsd/pam-u2f/dist/pamu2fcfg/cmdline.ggo @@ -0,0 +1,18 @@ +# Copyright (C) 2014-2018 Yubico AB - See COPYING +# + +purpose "Perform a U2F registration operation and print a configuration line that can be used with the pam_u2f module." + +defgroup "user" + +option "origin" o "Origin URL to use during registration. Defaults to pam://hostname" string optional +option "appid" i "Application ID to use during registration. Defaults to pam://hostname" string optional +option "type" t "COSE type to use during registration (ES256 or RS256). Defaults to ES256." string optional +option "resident" r "Generate a resident credential" flag off +option "no-user-presence" P "Allow the credential to be used without ensuring the user's presence" flag off +option "pin-verification" N "Require PIN verification during authentication" flag off +option "user-verification" V "Require user verification during authentication" flag off +option "debug" d "Print debug information (highly verbose)" flag off +option "verbose" v "Print information about chosen origin and appid" flag off +groupoption "username" u "The name of the user registering the device. Defaults to the current user name" string group="user" +groupoption "nouser" n "Print only registration information (keyHandle and public key). Useful for appending" group="user" diff --git a/external/bsd/pam-u2f/dist/pamu2fcfg/pamu2fcfg.c b/external/bsd/pam-u2f/dist/pamu2fcfg/pamu2fcfg.c new file mode 100644 index 000000000000..7fe2b558ed98 --- /dev/null +++ b/external/bsd/pam-u2f/dist/pamu2fcfg/pamu2fcfg.c @@ -0,0 +1,347 @@ +/* + * Copyright (C) 2014-2018 Yubico AB - See COPYING + */ + +#define BUFSIZE 1024 +#define PAM_PREFIX "pam://" +#define TIMEOUT 15 +#define FREQUENCY 1 + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "b64.h" +#include "cmdline.h" +#include "util.h" +#ifndef HAVE_READPASSPHRASE +#include "_readpassphrase.h" +#else +#include +#endif + +int main(int argc, char *argv[]) { + int exit_code = EXIT_FAILURE; + struct gengetopt_args_info args_info; + char buf[BUFSIZE]; + char prompt[BUFSIZE]; + char pin[BUFSIZE]; + char *p; + char *response; + fido_cred_t *cred = NULL; + fido_dev_info_t *devlist = NULL; + fido_dev_t *dev = NULL; + const fido_dev_info_t *di = NULL; + size_t ndevs; + int cose_type; + int resident_key; + int user_presence; + int user_verification; + int pin_verification; + int r; + int n; + char *origin = NULL; + char *appid = NULL; + char *user = NULL; + char *b64_kh; + char *b64_pk; + struct passwd *passwd; + const unsigned char *kh = NULL; + size_t kh_len; + const unsigned char *pk = NULL; + size_t pk_len; + unsigned char userid[32]; + unsigned char challenge[32]; + unsigned i; + unsigned max_index = 0; + + if (cmdline_parser(argc, argv, &args_info) != 0) + exit(EXIT_FAILURE); + + if (args_info.help_given) { + cmdline_parser_print_help(); + printf("\nReport bugs at .\n"); + exit(EXIT_SUCCESS); + } + + fido_init(args_info.debug_flag ? FIDO_DEBUG : 0); + + cred = fido_cred_new(); + if (!cred) { + fprintf(stderr, "fido_cred_new failed\n"); + exit(EXIT_FAILURE); + } + + if (!random_bytes(challenge, sizeof(challenge))) { + fprintf(stderr, "random_bytes failed\n"); + exit(EXIT_FAILURE); + } + + if (args_info.type_given) { + if (!strcasecmp(args_info.type_arg, "es256")) + cose_type = COSE_ES256; + else if (!strcasecmp(args_info.type_arg, "rs256")) + cose_type = COSE_RS256; + else { + fprintf(stderr, "Unknown COSE type '%s'.\n", args_info.type_arg); + exit(EXIT_FAILURE); + } + } else + cose_type = COSE_ES256; + + r = fido_cred_set_type(cred, cose_type); + if (r != FIDO_OK) { + fprintf(stderr, "error: fido_cred_set_type (%d): %s\n", r, fido_strerr(r)); + exit(EXIT_FAILURE); + } + + r = fido_cred_set_clientdata_hash(cred, challenge, sizeof(challenge)); + if (r != FIDO_OK) { + fprintf(stderr, "error: fido_cred_set_clientdata_hash (%d): %s\n", r, + fido_strerr(r)); + exit(EXIT_FAILURE); + } + + if (args_info.origin_given) + origin = args_info.origin_arg; + else { + if (!strcpy(buf, PAM_PREFIX)) { + fprintf(stderr, "strcpy failed\n"); + exit(EXIT_FAILURE); + } + if (gethostname(buf + strlen(PAM_PREFIX), BUFSIZE - strlen(PAM_PREFIX)) == + -1) { + perror("gethostname"); + exit(EXIT_FAILURE); + } + origin = buf; + } + + if (args_info.verbose_given) + fprintf(stderr, "Setting origin to %s\n", origin); + + if (args_info.appid_given) + appid = args_info.appid_arg; + else { + appid = origin; + } + + if (args_info.verbose_given) + fprintf(stderr, "Setting appid to %s\n", appid); + + r = fido_cred_set_rp(cred, origin, appid); + if (r != FIDO_OK) { + fprintf(stderr, "error: fido_cred_set_rp (%d) %s\n", r, fido_strerr(r)); + exit(EXIT_FAILURE); + } + + if (args_info.username_given) + user = args_info.username_arg; + else { + passwd = getpwuid(getuid()); + if (passwd == NULL) { + perror("getpwuid"); + exit(EXIT_FAILURE); + } + user = passwd->pw_name; + } + + if (!random_bytes(userid, sizeof(userid))) { + fprintf(stderr, "random_bytes failed\n"); + exit(EXIT_FAILURE); + } + + if (args_info.verbose_given) { + fprintf(stderr, "Setting user to %s\n", user); + fprintf(stderr, "Setting user id to "); + for (size_t i = 0; i < sizeof(userid); i++) + fprintf(stderr, "%02x", userid[i]); + fprintf(stderr, "\n"); + } + + r = fido_cred_set_user(cred, userid, sizeof(userid), user, NULL, NULL); + if (r != FIDO_OK) { + fprintf(stderr, "error: fido_cred_set_user (%d) %s\n", r, fido_strerr(r)); + exit(EXIT_FAILURE); + } + + if (args_info.resident_given) + resident_key = 1; + else + resident_key = 0; + + if (args_info.no_user_presence_given) + user_presence = 0; + else + user_presence = 1; + + if (args_info.user_verification_given) + user_verification = 1; + else + user_verification = 0; + + if (args_info.pin_verification_given) + pin_verification = 1; + else + pin_verification = 0; + + r = fido_cred_set_rk(cred, resident_key); + if (r != FIDO_OK) { + fprintf(stderr, "error: fido_cred_set_rk (%d) %s\n", r, fido_strerr(r)); + exit(EXIT_FAILURE); + } + + r = fido_cred_set_uv(cred, false); + if (r != FIDO_OK) { + fprintf(stderr, "error: fido_cred_set_uv (%d) %s\n", r, fido_strerr(r)); + exit(EXIT_FAILURE); + } + + devlist = fido_dev_info_new(64); + if (!devlist) { + fprintf(stderr, "error: fido_dev_info_new failed\n"); + exit(EXIT_FAILURE); + } + + r = fido_dev_info_manifest(devlist, 64, &ndevs); + if (r != FIDO_OK) { + fprintf(stderr, "Unable to discover device(s), %s (%d)\n", fido_strerr(r), + r); + exit(EXIT_FAILURE); + } + + if (ndevs == 0) { + for (i = 0; i < TIMEOUT; i += FREQUENCY) { + fprintf(stderr, + "\rNo U2F device available, please insert one now, you " + "have %2d seconds", + TIMEOUT - i); + fflush(stderr); + sleep(FREQUENCY); + + r = fido_dev_info_manifest(devlist, 64, &ndevs); + if (r != FIDO_OK) { + fprintf(stderr, "\nUnable to discover device(s), %s (%d)", + fido_strerr(r), r); + exit(EXIT_FAILURE); + } + + if (ndevs != 0) { + fprintf(stderr, "\nDevice found!\n"); + break; + } + } + } + + if (ndevs == 0) { + fprintf(stderr, "\rNo device found. Aborting. " + " \n"); + exit(EXIT_FAILURE); + } + + /* XXX loop over every device? */ + dev = fido_dev_new(); + if (!dev) { + fprintf(stderr, "fido_dev_new failed\n"); + exit(EXIT_FAILURE); + } + + di = fido_dev_info_ptr(devlist, 0); + if (!di) { + fprintf(stderr, "error: fido_dev_info_ptr returned NULL\n"); + exit(EXIT_FAILURE); + } + + r = fido_dev_open(dev, fido_dev_info_path(di)); + if (r != FIDO_OK) { + fprintf(stderr, "error: fido_dev_open (%d) %s\n", r, fido_strerr(r)); + exit(EXIT_FAILURE); + } + + r = fido_dev_make_cred(dev, cred, NULL); + if (r == FIDO_ERR_PIN_REQUIRED) { + n = snprintf(prompt, sizeof(prompt), + "Enter PIN for %s: ", fido_dev_info_path(di)); + if (n < 0 || (size_t) n >= sizeof(prompt)) { + fprintf(stderr, "error: snprintf prompt"); + exit(EXIT_FAILURE); + } + if (!readpassphrase(prompt, pin, sizeof(pin), RPP_ECHO_OFF)) { + fprintf(stderr, "error: failed to read pin"); + exit(EXIT_FAILURE); + } + r = fido_dev_make_cred(dev, cred, pin); + } + explicit_bzero(pin, sizeof(pin)); + + if (r != FIDO_OK) { + fprintf(stderr, "error: fido_dev_make_cred (%d) %s\n", r, fido_strerr(r)); + exit(EXIT_FAILURE); + } + + r = fido_cred_verify(cred); + if (r != FIDO_OK) { + fprintf(stderr, "error: fido_cred_verify (%d) %s\n", r, fido_strerr(r)); + exit(EXIT_FAILURE); + } + + kh = fido_cred_id_ptr(cred); + if (!kh) { + fprintf(stderr, "error: fido_cred_id_ptr returned NULL\n"); + exit(EXIT_FAILURE); + } + + kh_len = fido_cred_id_len(cred); + if (kh_len == 0) { + fprintf(stderr, "error: fido_cred_id_len returned 0\n"); + exit(EXIT_FAILURE); + } + + pk = (const unsigned char *) fido_cred_pubkey_ptr(cred); + if (!pk) { + fprintf(stderr, "error: fido_cred_pubkey_ptr returned NULL\n"); + exit(EXIT_FAILURE); + } + + pk_len = fido_cred_pubkey_len(cred); + if (pk_len == 0) { + fprintf(stderr, "error: fido_cred_pubkey_len returned 0\n"); + exit(EXIT_FAILURE); + } + + if (!b64_encode(kh, kh_len, &b64_kh)) { + fprintf(stderr, "error: failed to encode key handle\n"); + exit(EXIT_FAILURE); + } + + if (!b64_encode(pk, pk_len, &b64_pk)) { + fprintf(stderr, "error: failed to encode public key\n"); + exit(EXIT_FAILURE); + } + + if (!args_info.nouser_given) + printf("%s", user); + + printf(":%s,%s,%s,%s%s%s", resident_key ? "*" : b64_kh, b64_pk, + cose_type == COSE_ES256 ? "es256" : "rs256", + user_presence ? "+presence" : "", + user_verification ? "+verification" : "", + pin_verification ? "+pin" : ""); + + exit_code = EXIT_SUCCESS; + + fido_dev_info_free(&devlist, ndevs); + fido_cred_free(&cred); + fido_dev_free(&dev); + + free(b64_kh); + free(b64_pk); + + exit(exit_code); +} diff --git a/external/bsd/pam-u2f/dist/pamu2fcfg/readpassphrase.c b/external/bsd/pam-u2f/dist/pamu2fcfg/readpassphrase.c new file mode 100644 index 000000000000..7d9b42d8113a --- /dev/null +++ b/external/bsd/pam-u2f/dist/pamu2fcfg/readpassphrase.c @@ -0,0 +1,214 @@ +/* $OpenBSD: readpassphrase.c,v 1.26 2016/10/18 12:47:18 millert Exp $ */ + +/* + * Copyright (c) 2000-2002, 2007, 2010 + * Todd C. Miller + * + * 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 THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR 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. + * + * Sponsored in part by the Defense Advanced Research Projects + * Agency (DARPA) and Air Force Research Laboratory, Air Force + * Materiel Command, USAF, under agreement number F39502-99-1-0512. + */ + +/* OPENBSD ORIGINAL: lib/libc/gen/readpassphrase.c */ + +#ifndef HAVE_READPASSPHRASE + +#include +#include +#include +#include +#include +#include +#ifdef HAVE_UNISTD_H +#include +#endif +#include + +#include "_readpassphrase.h" + +#ifndef _PATH_TTY +# define _PATH_TTY "/dev/tty" +#endif + +#ifndef TCSASOFT +/* If we don't have TCSASOFT define it so that ORing it it below is a no-op. */ +# define TCSASOFT 0 +#endif + +/* SunOS 4.x which lacks _POSIX_VDISABLE, but has VDISABLE */ +#if !defined(_POSIX_VDISABLE) && defined(VDISABLE) +# define _POSIX_VDISABLE VDISABLE +#endif + +static volatile sig_atomic_t signo[_NSIG]; + +static void handler(int); + +char * +readpassphrase(const char *prompt, char *buf, size_t bufsiz, int flags) +{ + ssize_t nr; + int input, output, save_errno, i, need_restart; + char ch, *p, *end; + struct termios term, oterm; + struct sigaction sa, savealrm, saveint, savehup, savequit, saveterm; + struct sigaction savetstp, savettin, savettou, savepipe; + + /* I suppose we could alloc on demand in this case (XXX). */ + if (bufsiz == 0) { + errno = EINVAL; + return(NULL); + } + +restart: + for (i = 0; i < _NSIG; i++) + signo[i] = 0; + need_restart = 0; + /* + * Read and write to /dev/tty if available. If not, read from + * stdin and write to stderr unless a tty is required. + */ + if ((flags & RPP_STDIN) || + (input = output = open(_PATH_TTY, O_RDWR)) == -1) { + if (flags & RPP_REQUIRE_TTY) { + errno = ENOTTY; + return(NULL); + } + input = STDIN_FILENO; + output = STDERR_FILENO; + } + + /* + * Turn off echo if possible. + * If we are using a tty but are not the foreground pgrp this will + * generate SIGTTOU, so do it *before* installing the signal handlers. + */ + if (input != STDIN_FILENO && tcgetattr(input, &oterm) == 0) { + memcpy(&term, &oterm, sizeof(term)); + if (!(flags & RPP_ECHO_ON)) + term.c_lflag &= ~(ECHO | ECHONL); +#ifdef VSTATUS + if (term.c_cc[VSTATUS] != _POSIX_VDISABLE) + term.c_cc[VSTATUS] = _POSIX_VDISABLE; +#endif + (void)tcsetattr(input, TCSAFLUSH|TCSASOFT, &term); + } else { + memset(&term, 0, sizeof(term)); + term.c_lflag |= ECHO; + memset(&oterm, 0, sizeof(oterm)); + oterm.c_lflag |= ECHO; + } + + /* + * Catch signals that would otherwise cause the user to end + * up with echo turned off in the shell. Don't worry about + * things like SIGXCPU and SIGVTALRM for now. + */ + sigemptyset(&sa.sa_mask); + sa.sa_flags = 0; /* don't restart system calls */ + sa.sa_handler = handler; + (void)sigaction(SIGALRM, &sa, &savealrm); + (void)sigaction(SIGHUP, &sa, &savehup); + (void)sigaction(SIGINT, &sa, &saveint); + (void)sigaction(SIGPIPE, &sa, &savepipe); + (void)sigaction(SIGQUIT, &sa, &savequit); + (void)sigaction(SIGTERM, &sa, &saveterm); + (void)sigaction(SIGTSTP, &sa, &savetstp); + (void)sigaction(SIGTTIN, &sa, &savettin); + (void)sigaction(SIGTTOU, &sa, &savettou); + + if (!(flags & RPP_STDIN)) + (void)write(output, prompt, strlen(prompt)); + end = buf + bufsiz - 1; + p = buf; + while ((nr = read(input, &ch, 1)) == 1 && ch != '\n' && ch != '\r') { + if (p < end) { + if ((flags & RPP_SEVENBIT)) + ch &= 0x7f; + if (isalpha((unsigned char)ch)) { + if ((flags & RPP_FORCELOWER)) + ch = (char)tolower((unsigned char)ch); + if ((flags & RPP_FORCEUPPER)) + ch = (char)toupper((unsigned char)ch); + } + *p++ = ch; + } + } + *p = '\0'; + save_errno = errno; + if (!(term.c_lflag & ECHO)) + (void)write(output, "\n", 1); + + /* Restore old terminal settings and signals. */ + if (memcmp(&term, &oterm, sizeof(term)) != 0) { + const int sigttou = signo[SIGTTOU]; + + /* Ignore SIGTTOU generated when we are not the fg pgrp. */ + while (tcsetattr(input, TCSAFLUSH|TCSASOFT, &oterm) == -1 && + errno == EINTR && !signo[SIGTTOU]) + continue; + signo[SIGTTOU] = sigttou; + } + (void)sigaction(SIGALRM, &savealrm, NULL); + (void)sigaction(SIGHUP, &savehup, NULL); + (void)sigaction(SIGINT, &saveint, NULL); + (void)sigaction(SIGQUIT, &savequit, NULL); + (void)sigaction(SIGPIPE, &savepipe, NULL); + (void)sigaction(SIGTERM, &saveterm, NULL); + (void)sigaction(SIGTSTP, &savetstp, NULL); + (void)sigaction(SIGTTIN, &savettin, NULL); + (void)sigaction(SIGTTOU, &savettou, NULL); + if (input != STDIN_FILENO) + (void)close(input); + + /* + * If we were interrupted by a signal, resend it to ourselves + * now that we have restored the signal handlers. + */ + for (i = 0; i < _NSIG; i++) { + if (signo[i]) { + kill(getpid(), i); + switch (i) { + case SIGTSTP: + case SIGTTIN: + case SIGTTOU: + need_restart = 1; + } + } + } + if (need_restart) + goto restart; + + if (save_errno) + errno = save_errno; + return(nr == -1 ? NULL : buf); +} + +#if 0 +char * +getpass(const char *prompt) +{ + static char buf[_PASSWORD_LEN + 1]; + + return(readpassphrase(prompt, buf, sizeof(buf), RPP_ECHO_OFF)); +} +#endif + +static void handler(int s) +{ + + signo[s] = 1; +} +#endif /* HAVE_READPASSPHRASE */ diff --git a/external/bsd/pam-u2f/dist/tests/Makefile.am b/external/bsd/pam-u2f/dist/tests/Makefile.am new file mode 100644 index 000000000000..14035507b534 --- /dev/null +++ b/external/bsd/pam-u2f/dist/tests/Makefile.am @@ -0,0 +1,12 @@ +# Copyright (C) 2014-2018 Yubico AB - See COPYING +# + +AM_CFLAGS = $(WARN_CFLAGS) +AM_CPPFLAGS=-I$(srcdir)/.. -I$(builddir)/.. + +AM_LDFLAGS = -no-install + +LDADD = $(top_builddir)/pam_u2f.la + +check_PROGRAMS = basic +TESTS = $(check_PROGRAMS) diff --git a/external/bsd/pam-u2f/dist/tests/basic.c b/external/bsd/pam-u2f/dist/tests/basic.c new file mode 100644 index 000000000000..85c6a4bd0259 --- /dev/null +++ b/external/bsd/pam-u2f/dist/tests/basic.c @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2014-2018 Yubico AB - See COPYING + */ + +#include +#include +#include + +/* These #defines must be present according to PAM documentation. */ +#define PAM_SM_AUTH + +#ifdef HAVE_SECURITY_PAM_APPL_H +#include +#endif +#ifdef HAVE_SECURITY_PAM_MODULES_H +#include +#endif + +int +main (int argc, const char **argv) +{ + pam_handle_t *pamh = NULL; + int rc; + + rc = pam_sm_authenticate (pamh, 0, 1, argv); + + printf ("rc %d\n", rc); + + return 0; +} diff --git a/external/bsd/pam-u2f/dist/tests/bionic/Dockerfile b/external/bsd/pam-u2f/dist/tests/bionic/Dockerfile new file mode 100644 index 000000000000..ac2177d185fe --- /dev/null +++ b/external/bsd/pam-u2f/dist/tests/bionic/Dockerfile @@ -0,0 +1,15 @@ +FROM ubuntu:bionic +COPY . /pam-u2f +WORKDIR /pam-u2f +ENV DEBIAN_FRONTEND noninteractive +RUN apt-get -qq update +RUN apt-get -qq upgrade +RUN apt-get install -qq software-properties-common +RUN add-apt-repository ppa:yubico/stable +RUN apt-get -qq update +RUN apt-get install -qq libudev-dev libssl-dev libfido2-dev +RUN apt-get install -qq build-essential autoconf automake libtool pkg-config +RUN apt-get install -qq gengetopt libpam-dev pamtester +RUN autoreconf -i . +RUN ./configure --disable-man +RUN make clean all install diff --git a/external/bsd/pam-u2f/dist/tests/bionic/README b/external/bsd/pam-u2f/dist/tests/bionic/README new file mode 100644 index 000000000000..d5179f06b1b6 --- /dev/null +++ b/external/bsd/pam-u2f/dist/tests/bionic/README @@ -0,0 +1,18 @@ +pam-u2f can be tested as follows: + +1. Build a Docker image containing everything needed to run pam-u2f. + This can be achieved through the command: + + $ docker build -t pam-u2f-test -f tests/bionic/Dockerfile . + +2. Connect a YubiKey to the host OS and take note of the path of the + device's U2F endpoint (e.g. /dev/hidraw6). +3. Execute tests/bionic/run.sh in a container, exposing the YubiKey + to the guest OS. This can be done through the command: + + $ docker run -it --rm=true --device=/dev/hidraw6 \ + pam-u2f-test /pam-u2f/tests/bionic/run.sh + + To test U2F devices, pass -e U2F_TOKEN=1 to docker-run. To test + FIDO2 devices configured with a PIN, pass -e FIDO2_PIN=1 to + docker-run. diff --git a/external/bsd/pam-u2f/dist/tests/bionic/run.sh b/external/bsd/pam-u2f/dist/tests/bionic/run.sh new file mode 100755 index 000000000000..a5fc5dfde0a2 --- /dev/null +++ b/external/bsd/pam-u2f/dist/tests/bionic/run.sh @@ -0,0 +1,63 @@ +#!/bin/sh -ex +mkdir -p ~/.config/Yubico + +create_keys() { + pamu2fcfg -t es256 -N > /tmp/es256 + [ "${U2F_TOKEN}" != "" ] && return + pamu2fcfg -t es256 -N -r -o originA > /tmp/es256.r + pamu2fcfg -t rs256 -N -r -o originB > /tmp/rs256.r +} + +run_tests() { + [ "${U2F_TOKEN}" != "" ] && return + + echo "auth sufficient pam_u2f.so" > /etc/pam.d/dummy + cp /tmp/es256 ~/.config/Yubico/u2f_keys + pamtester dummy root authenticate + + echo "auth sufficient pam_u2f.so origin=originA" > /etc/pam.d/dummy + cp /tmp/es256.r ~/.config/Yubico/u2f_keys + pamtester dummy root authenticate + + echo "auth sufficient pam_u2f.so origin=originB" > /etc/pam.d/dummy + cp /tmp/rs256.r ~/.config/Yubico/u2f_keys + pamtester dummy root authenticate +} + +run_user_presence_tests() { + echo "auth sufficient pam_u2f.so" > /etc/pam.d/dummy + cat /tmp/es256 | sed 's/-$/p/' > ~/.config/Yubico/u2f_keys + pamtester dummy root authenticate + + [ "${U2F_TOKEN}" != "" ] && return + + echo "auth sufficient pam_u2f.so origin=originA" > /etc/pam.d/dummy + cat /tmp/es256.r | sed 's/-$/p/' > ~/.config/Yubico/u2f_keys + pamtester dummy root authenticate + + echo "auth sufficient pam_u2f.so origin=originB" > /etc/pam.d/dummy + cat /tmp/rs256.r | sed 's/-$/p/' > ~/.config/Yubico/u2f_keys + pamtester dummy root authenticate +} + +run_user_verification_tests() { + [ "${U2F_TOKEN}" != "" ] && return + [ "${FIDO2_PIN}" = "" ] && return + + echo "auth sufficient pam_u2f.so" > /etc/pam.d/dummy + cat /tmp/es256 | sed 's/-$/v/' > ~/.config/Yubico/u2f_keys + pamtester dummy root authenticate + + echo "auth sufficient pam_u2f.so origin=originA" > /etc/pam.d/dummy + cat /tmp/es256.r | sed 's/-$/v/' > ~/.config/Yubico/u2f_keys + pamtester dummy root authenticate + + echo "auth sufficient pam_u2f.so origin=originB" > /etc/pam.d/dummy + cat /tmp/rs256.r | sed 's/-$/v/' > ~/.config/Yubico/u2f_keys + pamtester dummy root authenticate +} + +create_keys +run_tests +run_user_presence_tests +run_user_verification_tests diff --git a/external/bsd/pam-u2f/dist/util.c b/external/bsd/pam-u2f/dist/util.c new file mode 100644 index 000000000000..b64e7c006f27 --- /dev/null +++ b/external/bsd/pam-u2f/dist/util.c @@ -0,0 +1,1202 @@ +/* + * Copyright (C) 2014-2019 Yubico AB - See COPYING + */ + +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "b64.h" +#include "util.h" + +static int hex_decode(const char *ascii_hex, unsigned char **blob, + size_t *blob_len) { + *blob = NULL; + *blob_len = 0; + + if (ascii_hex == NULL || (strlen(ascii_hex) % 2) != 0) + return (0); + + *blob_len = strlen(ascii_hex) / 2; + *blob = calloc(1, *blob_len); + if (*blob == NULL) + return (0); + + for (size_t i = 0; i < *blob_len; i++) { + unsigned int c; + int n = -1; + int r = sscanf(ascii_hex, "%02x%n", &c, &n); + if (r != 1 || n != 2 || c > UCHAR_MAX) { + free(*blob); + *blob = NULL; + *blob_len = 0; + return (0); + } + (*blob)[i] = (unsigned char) c; + ascii_hex += n; + } + + return (1); +} + +static char *normal_b64(const char *websafe_b64) { + char *b64; + char *p; + size_t n; + + n = strlen(websafe_b64); + if (n > SIZE_MAX - 3) + return (NULL); + + b64 = calloc(1, n + 3); + if (b64 == NULL) + return (NULL); + + memcpy(b64, websafe_b64, n); + p = b64; + + while ((p = strpbrk(p, "-_")) != NULL) { + switch (*p) { + case '-': + *p++ = '+'; + break; + case '_': + *p++ = '/'; + break; + } + } + + switch (n % 4) { + case 1: + b64[n] = '='; + break; + case 2: + case 3: + b64[n] = '='; + b64[n + 1] = '='; + break; + } + + return (b64); +} + +static es256_pk_t *translate_old_format_pubkey(const unsigned char *pk, + size_t pk_len) { + es256_pk_t *es256_pk = NULL; + EC_KEY *ec = NULL; + EC_POINT *q = NULL; + const EC_GROUP *g = NULL; + int ok = 0; + + if ((ec = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1)) == NULL || + (g = EC_KEY_get0_group(ec)) == NULL) + goto fail; + + if ((q = EC_POINT_new(g)) == NULL || + !EC_POINT_oct2point(g, q, pk, pk_len, NULL) || + !EC_KEY_set_public_key(ec, q)) + goto fail; + + es256_pk = es256_pk_new(); + if (es256_pk == NULL || es256_pk_from_EC_KEY(es256_pk, ec) < 0) + goto fail; + + ok = 1; +fail: + if (ec != NULL) + EC_KEY_free(ec); + if (q != NULL) + EC_POINT_free(q); + if (!ok) + es256_pk_free(&es256_pk); + + return (es256_pk); +} + +int get_devices_from_authfile(const char *authfile, const char *username, + unsigned max_devs, int verbose, FILE *debug_file, + device_t *devices, unsigned *n_devs) { + + char *buf = NULL; + char *s_user, *s_token; + int retval = 0; + int fd = -1; + struct stat st; + struct passwd *pw = NULL, pw_s; + char buffer[BUFSIZE]; + int gpu_ret; + FILE *opwfile = NULL; + unsigned i; + + /* Ensure we never return uninitialized count. */ + *n_devs = 0; + + fd = open(authfile, O_RDONLY | O_CLOEXEC | O_NOCTTY); + if (fd < 0) { + if (verbose) + D(debug_file, "Cannot open file: %s (%s)", authfile, strerror(errno)); + goto err; + } + + if (fstat(fd, &st) < 0) { + if (verbose) + D(debug_file, "Cannot stat file: %s (%s)", authfile, strerror(errno)); + goto err; + } + + if (!S_ISREG(st.st_mode)) { + if (verbose) + D(debug_file, "%s is not a regular file", authfile); + goto err; + } + + if (st.st_size == 0) { + if (verbose) + D(debug_file, "File %s is empty", authfile); + goto err; + } + + gpu_ret = getpwuid_r(st.st_uid, &pw_s, buffer, sizeof(buffer), &pw); + if (gpu_ret != 0 || pw == NULL) { + D(debug_file, "Unable to retrieve credentials for uid %u, (%s)", st.st_uid, + strerror(errno)); + goto err; + } + + if (strcmp(pw->pw_name, username) != 0 && strcmp(pw->pw_name, "root") != 0) { + if (strcmp(username, "root") != 0) { + D(debug_file, + "The owner of the authentication file is neither %s nor root", + username); + } else { + D(debug_file, "The owner of the authentication file is not root"); + } + goto err; + } + + opwfile = fdopen(fd, "r"); + if (opwfile == NULL) { + if (verbose) + D(debug_file, "fdopen: %s", strerror(errno)); + goto err; + } else { + fd = -1; /* fd belongs to opwfile */ + } + + buf = malloc(sizeof(char) * (DEVSIZE * max_devs)); + if (!buf) { + if (verbose) + D(debug_file, "Unable to allocate memory"); + goto err; + } + + retval = -2; + while (fgets(buf, (int) (DEVSIZE * (max_devs - 1)), opwfile)) { + char *saveptr = NULL; + size_t len = strlen(buf); + if (len > 0 && buf[len - 1] == '\n') + buf[len - 1] = '\0'; + + if (verbose) + D(debug_file, "Authorization line: %s", buf); + + s_user = strtok_r(buf, ":", &saveptr); + if (s_user && strcmp(username, s_user) == 0) { + if (verbose) + D(debug_file, "Matched user: %s", s_user); + + retval = -1; // We found at least one line for the user + + // only keep last line for this user + for (i = 0; i < *n_devs; i++) { + free(devices[i].keyHandle); + free(devices[i].publicKey); + free(devices[i].coseType); + free(devices[i].attributes); + devices[i].keyHandle = NULL; + devices[i].publicKey = NULL; + devices[i].coseType = NULL; + devices[i].attributes = NULL; + devices[i].old_format = 0; + } + *n_devs = 0; + + i = 0; + while ((s_token = strtok_r(NULL, ",", &saveptr))) { + if ((*n_devs)++ > max_devs - 1) { + *n_devs = max_devs; + if (verbose) + D(debug_file, + "Found more than %d devices, ignoring the remaining ones", + max_devs); + break; + } + + devices[i].keyHandle = NULL; + devices[i].publicKey = NULL; + devices[i].coseType = NULL; + devices[i].attributes = NULL; + devices[i].old_format = 0; + + if (verbose) + D(debug_file, "KeyHandle for device number %d: %s", i + 1, s_token); + + devices[i].keyHandle = strdup(s_token); + + if (!devices[i].keyHandle) { + if (verbose) + D(debug_file, "Unable to allocate memory for keyHandle number %d", + i); + goto err; + } + + if (!strcmp(devices[i].keyHandle, "*") && verbose) + D(debug_file, "Credential is resident"); + + s_token = strtok_r(NULL, ",", &saveptr); + + if (!s_token) { + if (verbose) + D(debug_file, "Unable to retrieve publicKey number %d", i + 1); + goto err; + } + + if (verbose) + D(debug_file, "publicKey for device number %d: %s", i + 1, s_token); + + devices[i].publicKey = strdup(s_token); + + if (!devices[i].publicKey) { + if (verbose) + D(debug_file, "Unable to allocate memory for publicKey number %d", + i); + goto err; + } + + s_token = strtok_r(NULL, ",", &saveptr); + + devices[i].old_format = 0; + + if (!s_token) { + if (verbose) { + D(debug_file, "Unable to retrieve COSE type %d", i + 1); + D(debug_file, "Assuming ES256 (backwards compatibility)"); + } + devices[i].old_format = 1; + devices[i].coseType = strdup("es256"); + } else { + if (verbose) + D(debug_file, "COSE type for device number %d: %s", i + 1, s_token); + devices[i].coseType = strdup(s_token); + } + + if (!devices[i].coseType) { + if (verbose) + D(debug_file, "Unable to allocate memory for COSE type number %d", + i); + goto err; + } + + s_token = strtok_r(NULL, ":", &saveptr); + + if (!s_token) { + if (verbose) { + D(debug_file, "Unable to retrieve attributes %d", i + 1); + D(debug_file, "Assuming 'p' (backwards compatibility)"); + } + devices[i].attributes = strdup("p"); + } else { + if (verbose) + D(debug_file, "Attributes for device number %d: %s", i + 1, + s_token); + devices[i].attributes = strdup(s_token); + } + + if (!devices[i].attributes) { + if (verbose) + D(debug_file, "Unable to allocate memory for attributes number %d", + i); + goto err; + } + + if (devices[i].old_format) { + char *websafe_b64 = devices[i].keyHandle; + devices[i].keyHandle = normal_b64(websafe_b64); + free(websafe_b64); + if (!devices[i].keyHandle) { + if (verbose) + D(debug_file, "Unable to allocate memory for keyHandle number %d", + i); + goto err; + } + } + + i++; + } + } + } + + if (verbose) + D(debug_file, "Found %d device(s) for user %s", *n_devs, username); + + retval = 1; + goto out; + +err: + for (i = 0; i < *n_devs; i++) { + free(devices[i].keyHandle); + free(devices[i].publicKey); + free(devices[i].coseType); + free(devices[i].attributes); + devices[i].keyHandle = NULL; + devices[i].publicKey = NULL; + devices[i].coseType = NULL; + devices[i].attributes = NULL; + } + + *n_devs = 0; + +out: + if (buf) { + free(buf); + buf = NULL; + } + + if (opwfile) + fclose(opwfile); + + if (fd != -1) + close(fd); + + return retval; +} + +void free_devices(device_t *devices, const unsigned n_devs) { + unsigned i; + + if (!devices) + return; + + for (i = 0; i < n_devs; i++) { + free(devices[i].keyHandle); + devices[i].keyHandle = NULL; + + free(devices[i].publicKey); + devices[i].publicKey = NULL; + + free(devices[i].coseType); + devices[i].coseType = NULL; + + free(devices[i].attributes); + devices[i].attributes = NULL; + } + + free(devices); + devices = NULL; +} + +static int get_authenticators(const cfg_t *cfg, const fido_dev_info_t *devlist, + size_t devlist_len, fido_assert_t *assert, + const void *kh, fido_dev_t **authlist) { + const fido_dev_info_t *di = NULL; + fido_dev_t *dev = NULL; + int r; + size_t i; + size_t j; + + if (cfg->debug) + D(cfg->debug_file, "Working with %zu authenticator(s)", devlist_len); + + for (i = 0, j = 0; i < devlist_len; i++) { + if (cfg->debug) + D(cfg->debug_file, "Checking whether key exists in authenticator %zu", i); + + di = fido_dev_info_ptr(devlist, i); + if (!di) { + if (cfg->debug) + D(cfg->debug_file, "Unable to get device pointer"); + continue; + } + + if (cfg->debug) + D(cfg->debug_file, "Authenticator path: %s", fido_dev_info_path(di)); + + dev = fido_dev_new(); + if (!dev) { + if (cfg->debug) + D(cfg->debug_file, "Unable to allocate device type"); + continue; + } + + r = fido_dev_open(dev, fido_dev_info_path(di)); + if (r != FIDO_OK) { + if (cfg->debug) + D(cfg->debug_file, "Failed to open authenticator: %s (%d)", + fido_strerr(r), r); + fido_dev_free(&dev); + continue; + } + + if (kh == NULL || cfg->nodetect) { + /* resident credential or nodetect: try all authenticators */ + authlist[j++] = dev; + } else { + r = fido_dev_get_assert(dev, assert, NULL); + if ((!fido_dev_is_fido2(dev) && r == FIDO_ERR_USER_PRESENCE_REQUIRED) || + (fido_dev_is_fido2(dev) && r == FIDO_OK)) { + authlist[j++] = dev; + if (cfg->debug) + D(cfg->debug_file, "Found key in authenticator %zu", i); + return (1); + } + if (cfg->debug) + D(cfg->debug_file, "Key not found in authenticator %zu", i); + + fido_dev_close(dev); + fido_dev_free(&dev); + } + } + + if (kh == NULL && j != 0) + return (1); + else { + if (cfg->debug) + D(cfg->debug_file, "Key not found"); + return (0); + } +} + +int do_authentication(const cfg_t *cfg, const device_t *devices, + const unsigned n_devs, pam_handle_t *pamh) { + es256_pk_t *es256_pk = NULL; + rs256_pk_t *rs256_pk = NULL; + fido_assert_t *assert = NULL; + fido_dev_info_t *devlist = NULL; + fido_dev_t **authlist = NULL; + int cued = 0; + int r; + int retval = -2; + int cose_type; + size_t kh_len; + size_t ndevs = 0; + size_t ndevs_prev = 0; + size_t pk_len; + unsigned char challenge[32]; + unsigned char *kh = NULL; + unsigned char *pk = NULL; + unsigned i = 0; + fido_opt_t user_presence = FIDO_OPT_OMIT; + fido_opt_t user_verification = FIDO_OPT_OMIT; + fido_opt_t pin_verification = FIDO_OPT_OMIT; + char *pin = NULL; + + fido_init(cfg->debug ? FIDO_DEBUG : 0); + + devlist = fido_dev_info_new(64); + if (!devlist) { + if (cfg->debug) + D(cfg->debug_file, "Unable to allocate devlist"); + goto out; + } + + r = fido_dev_info_manifest(devlist, 64, &ndevs); + if (r != FIDO_OK) { + if (cfg->debug) + D(cfg->debug_file, "Unable to discover device(s), %s (%d)", + fido_strerr(r), r); + goto out; + } + + ndevs_prev = ndevs; + + if (cfg->debug) + D(cfg->debug_file, "Device max index is %u", ndevs); + + es256_pk = es256_pk_new(); + if (!es256_pk) { + if (cfg->debug) + D(cfg->debug_file, "Unable to allocate ES256 public key"); + goto out; + } + + rs256_pk = rs256_pk_new(); + if (!rs256_pk) { + if (cfg->debug) + D(cfg->debug_file, "Unable to allocate RS256 public key"); + goto out; + } + + authlist = calloc(64 + 1, sizeof(fido_dev_t *)); + if (!authlist) { + if (cfg->debug) + D(cfg->debug_file, "Unable to allocate authenticator list"); + goto out; + } + + if (cfg->nodetect && cfg->debug) + D(cfg->debug_file, + "nodetect option specified, suitable key detection will be skipped"); + + i = 0; + while (i < n_devs) { + retval = -2; + + if (cfg->debug) + D(cfg->debug_file, "Attempting authentication with device number %d", + i + 1); + + assert = fido_assert_new(); + if (!assert) { + if (cfg->debug) + D(cfg->debug_file, "Unable to allocate assertion"); + goto out; + } + + r = fido_assert_set_rp(assert, cfg->origin); + if (r != FIDO_OK) { + if (cfg->debug) + D(cfg->debug_file, "Unable to set origin: %s (%d)", fido_strerr(r), r); + goto out; + } + + if (!strcmp(devices[i].keyHandle, "*")) { + if (cfg->debug) + D(cfg->debug_file, "Credential is resident"); + } else { + if (cfg->debug) + D(cfg->debug_file, "Key handle: %s", devices[i].keyHandle); + if (!b64_decode(devices[i].keyHandle, (void **) &kh, &kh_len)) { + if (cfg->debug) + D(cfg->debug_file, "Failed to decode key handle"); + goto out; + } + + r = fido_assert_allow_cred(assert, kh, kh_len); + if (r != FIDO_OK) { + if (cfg->debug) + D(cfg->debug_file, "Unable to set keyHandle: %s (%d)", fido_strerr(r), + r); + goto out; + } + } + + if (devices[i].old_format) { + if (!hex_decode(devices[i].publicKey, &pk, &pk_len)) { + if (cfg->debug) + D(cfg->debug_file, "Failed to decode public key"); + goto out; + } + } else { + if (!b64_decode(devices[i].publicKey, (void **) &pk, &pk_len)) { + if (cfg->debug) + D(cfg->debug_file, "Failed to decode public key"); + goto out; + } + } + + if (!strcmp(devices[i].coseType, "es256")) { + if (devices[i].old_format) { + es256_pk = translate_old_format_pubkey(pk, pk_len); + if (es256_pk == NULL) { + if (cfg->debug) + D(cfg->debug_file, "Failed to convert ES256 public key"); + } + } else { + r = es256_pk_from_ptr(es256_pk, pk, pk_len); + if (r != FIDO_OK) { + if (cfg->debug) + D(cfg->debug_file, "Failed to convert ES256 public key"); + } + } + cose_type = COSE_ES256; + } else if (!strcmp(devices[i].coseType, "rs256")) { + r = rs256_pk_from_ptr(rs256_pk, pk, pk_len); + if (r != FIDO_OK) { + if (cfg->debug) + D(cfg->debug_file, "Failed to convert RS256 public key"); + } + cose_type = COSE_RS256; + } else { + if (cfg->debug) + D(cfg->debug_file, "Unknown COSE type '%s'", devices[i].coseType); + goto out; + } + + if (cfg->userpresence == 1 || strstr(devices[i].attributes, "presence")) + user_presence = FIDO_OPT_TRUE; + else if (cfg->userpresence == 0) + user_presence = FIDO_OPT_FALSE; + else + user_presence = FIDO_OPT_OMIT; + + if (cfg->userverification == 1 || + strstr(devices[i].attributes, "verification")) + user_verification = FIDO_OPT_TRUE; + else if (cfg->userverification == 0) + user_verification = FIDO_OPT_FALSE; + else + user_verification = FIDO_OPT_OMIT; + + if (cfg->pinverification == 1 || strstr(devices[i].attributes, "pin")) { + pin_verification = FIDO_OPT_TRUE; + user_verification = FIDO_OPT_TRUE; + } else if (cfg->pinverification == 0) + pin_verification = FIDO_OPT_FALSE; + else + pin_verification = FIDO_OPT_OMIT; + + r = fido_assert_set_up(assert, FIDO_OPT_FALSE); + if (r != FIDO_OK) { + if (cfg->debug) + D(cfg->debug_file, "Failed to set UP"); + goto out; + } + + r = fido_assert_set_uv(assert, FIDO_OPT_OMIT); + if (r != FIDO_OK) { + if (cfg->debug) + D(cfg->debug_file, "Failed to set UV"); + goto out; + } + + if (!random_bytes(challenge, sizeof(challenge))) { + if (cfg->debug) + D(cfg->debug_file, "Failed to generate challenge"); + goto out; + } + + if (cfg->debug) { + char *b64_challenge; + if (!b64_encode(challenge, sizeof(challenge), &b64_challenge)) { + D(cfg->debug_file, "Failed to encode challenge"); + } else { + D(cfg->debug_file, "Challenge: %s", b64_challenge); + free(b64_challenge); + } + } + + r = fido_assert_set_clientdata_hash(assert, challenge, sizeof(challenge)); + if (r != FIDO_OK) { + if (cfg->debug) + D(cfg->debug_file, "Unable to set challenge: %s( %d)", fido_strerr(r), + r); + goto out; + } + + if (get_authenticators(cfg, devlist, ndevs, assert, kh, authlist)) { + for (size_t j = 0; authlist[j] != NULL; j++) { + r = fido_assert_set_up(assert, user_presence); + if (r != FIDO_OK) { + if (cfg->debug) + D(cfg->debug_file, "Failed to reset UP"); + goto out; + } + + r = fido_assert_set_uv(assert, user_verification); + if (r != FIDO_OK) { + if (cfg->debug) + D(cfg->debug_file, "Failed to reset UV"); + goto out; + } + + if (!random_bytes(challenge, sizeof(challenge))) { + if (cfg->debug) + D(cfg->debug_file, "Failed to regenerate challenge"); + goto out; + } + + r = + fido_assert_set_clientdata_hash(assert, challenge, sizeof(challenge)); + if (r != FIDO_OK) { + if (cfg->debug) + D(cfg->debug_file, "Unable to reset challenge: %s( %d)", + fido_strerr(r), r); + goto out; + } + + if (pin_verification == FIDO_OPT_TRUE) + pin = converse(pamh, PAM_PROMPT_ECHO_OFF, "Please enter the PIN: "); + if (user_presence == FIDO_OPT_TRUE || + user_verification == FIDO_OPT_TRUE) { + if (cfg->manual == 0 && cfg->cue && !cued) { + cued = 1; + converse(pamh, PAM_TEXT_INFO, + cfg->cue_prompt != NULL ? cfg->cue_prompt : DEFAULT_CUE); + } + } + r = fido_dev_get_assert(authlist[j], assert, pin); + if (pin) { + explicit_bzero(pin, strlen(pin)); + free(pin); + pin = NULL; + } + if (r == FIDO_OK) { + r = fido_assert_verify(assert, 0, cose_type, + cose_type == COSE_ES256 + ? (const void *) es256_pk + : (const void *) rs256_pk); + if (r == FIDO_OK) { + retval = 1; + goto out; + } + } + } + } else { + if (cfg->debug) + D(cfg->debug_file, "Device for this keyhandle is not present."); + } + + i++; + + fido_dev_info_free(&devlist, ndevs); + + devlist = fido_dev_info_new(64); + if (!devlist) { + if (cfg->debug) + D(cfg->debug_file, "Unable to allocate devlist"); + goto out; + } + + r = fido_dev_info_manifest(devlist, 64, &ndevs); + if (r != FIDO_OK) { + if (cfg->debug) + D(cfg->debug_file, "Unable to discover device(s), %s (%d)", + fido_strerr(r), r); + goto out; + } + + if (ndevs > ndevs_prev) { + if (cfg->debug) + D(cfg->debug_file, + "Devices max_index has changed: %zu (was %zu). Starting over", ndevs, + ndevs_prev); + ndevs_prev = ndevs; + i = 0; + } + + free(kh); + free(pk); + + kh = NULL; + pk = NULL; + + for (size_t j = 0; authlist[j] != NULL; j++) { + fido_dev_close(authlist[j]); + fido_dev_free(&authlist[j]); + } + + fido_assert_free(&assert); + } + +out: + es256_pk_free(&es256_pk); + rs256_pk_free(&rs256_pk); + fido_assert_free(&assert); + fido_dev_info_free(&devlist, ndevs); + + if (authlist) { + for (size_t j = 0; authlist[j] != NULL; j++) { + fido_dev_close(authlist[j]); + fido_dev_free(&authlist[j]); + } + free(authlist); + } + + free(kh); + free(pk); + + return retval; +} + +#define MAX_PROMPT_LEN (1024) + +int do_manual_authentication(const cfg_t *cfg, const device_t *devices, + const unsigned n_devs, pam_handle_t *pamh) { + fido_assert_t *assert[n_devs]; + es256_pk_t *es256_pk[n_devs]; + rs256_pk_t *rs256_pk[n_devs]; + unsigned char challenge[32]; + unsigned char *kh = NULL; + unsigned char *pk = NULL; + unsigned char *authdata = NULL; + unsigned char *sig = NULL; + char *b64_challenge = NULL; + char *b64_cdh = NULL; + char *b64_rpid = NULL; + char *b64_authdata = NULL; + char *b64_sig = NULL; + char prompt[MAX_PROMPT_LEN]; + char buf[MAX_PROMPT_LEN]; + size_t kh_len; + size_t pk_len; + size_t authdata_len; + size_t sig_len; + int cose_type[n_devs]; + int retval = -2; + int n; + int r; + unsigned i = 0; + bool user_presence = false; + bool user_verification = false; + + memset(assert, 0, sizeof(assert)); + memset(es256_pk, 0, sizeof(es256_pk)); + memset(rs256_pk, 0, sizeof(rs256_pk)); + + fido_init(cfg->debug ? FIDO_DEBUG : 0); + + for (i = 0; i < n_devs; ++i) { + + assert[i] = fido_assert_new(); + if (!assert[i]) { + if (cfg->debug) + D(cfg->debug_file, "Unable to allocate assertion %u", i); + goto out; + } + + r = fido_assert_set_rp(assert[i], cfg->origin); + if (r != FIDO_OK) { + if (cfg->debug) + D(cfg->debug_file, "Unable to set origin: %s (%d)", fido_strerr(r), r); + goto out; + } + + if (strstr(devices[i].attributes, "presence")) + user_presence = true; + if (strstr(devices[i].attributes, "verification")) + user_verification = true; + + r = fido_assert_set_up(assert[i], user_presence); + if (r != FIDO_OK) { + if (cfg->debug) + D(cfg->debug_file, "Unable to set UP: %s (%d)", fido_strerr(r), r); + goto out; + } + + r = fido_assert_set_uv(assert[i], user_verification); + if (r != FIDO_OK) { + if (cfg->debug) + D(cfg->debug_file, "Unable to set UV: %s (%d)", fido_strerr(r), r); + goto out; + } + + if (cfg->debug) + D(cfg->debug_file, "Attempting authentication with device number %d", + i + 1); + + if (!strcmp(devices[i].keyHandle, "*")) { + if (cfg->debug) + D(cfg->debug_file, "Credential is resident"); + } else { + if (!b64_decode(devices[i].keyHandle, (void **) &kh, &kh_len)) { + if (cfg->debug) + D(cfg->debug_file, "Failed to decode key handle"); + goto out; + } + + r = fido_assert_allow_cred(assert[i], kh, kh_len); + if (r != FIDO_OK) { + if (cfg->debug) + D(cfg->debug_file, "Unable to set keyHandle: %s (%d)", fido_strerr(r), + r); + goto out; + } + + free(kh); + kh = NULL; + } + + if (devices[i].old_format) { + if (!hex_decode(devices[i].publicKey, &pk, &pk_len)) { + if (cfg->debug) + D(cfg->debug_file, "Failed to decode public key"); + goto out; + } + } else { + if (!b64_decode(devices[i].publicKey, (void **) &pk, &pk_len)) { + if (cfg->debug) + D(cfg->debug_file, "Failed to decode public key"); + goto out; + } + } + + if (!strcmp(devices[i].coseType, "es256")) { + es256_pk[i] = es256_pk_new(); + if (!es256_pk[i]) { + if (cfg->debug) + D(cfg->debug_file, "Unable to allocate key %u", i); + goto out; + } + + if (es256_pk_from_ptr(es256_pk[i], pk, pk_len) != FIDO_OK) { + if (cfg->debug) + D(cfg->debug_file, "Failed to convert public key"); + goto out; + } + + cose_type[i] = COSE_ES256; + } else { + rs256_pk[i] = rs256_pk_new(); + if (!rs256_pk[i]) { + if (cfg->debug) + D(cfg->debug_file, "Unable to allocate key %u", i); + goto out; + } + + if (rs256_pk_from_ptr(rs256_pk[i], pk, pk_len) != FIDO_OK) { + if (cfg->debug) + D(cfg->debug_file, "Failed to convert public key"); + goto out; + } + + cose_type[i] = COSE_RS256; + } + + free(pk); + pk = NULL; + + if (!random_bytes(challenge, sizeof(challenge))) { + if (cfg->debug) + D(cfg->debug_file, "Failed to generate challenge"); + goto out; + } + + r = + fido_assert_set_clientdata_hash(assert[i], challenge, sizeof(challenge)); + if (r != FIDO_OK) { + if (cfg->debug) + D(cfg->debug_file, "Failed to set challenge"); + goto out; + } + + if (!b64_encode(challenge, sizeof(challenge), &b64_challenge)) { + if (cfg->debug) + D(cfg->debug_file, "Failed to encode challenge"); + goto out; + } + + if (cfg->debug) + D(cfg->debug_file, "Challenge: %s", b64_challenge); + + n = snprintf(prompt, sizeof(prompt), "Challenge #%d:", i + 1); + if (n <= 0 || (size_t) n >= sizeof(prompt)) { + if (cfg->debug) + D(cfg->debug_file, "Failed to print challenge prompt"); + goto out; + } + + converse(pamh, PAM_TEXT_INFO, prompt); + + n = snprintf(buf, sizeof(buf), "%s\n%s\n%s", b64_challenge, cfg->origin, + devices[i].keyHandle); + if (n <= 0 || (size_t) n >= sizeof(buf)) { + if (cfg->debug) + D(cfg->debug_file, "Failed to print fido2-assert input string"); + goto out; + } + + converse(pamh, PAM_TEXT_INFO, buf); + + free(b64_challenge); + b64_challenge = NULL; + } + + converse(pamh, PAM_TEXT_INFO, + "Please pass the challenge(s) above to fido2-assert, and " + "paste the results in the prompt below."); + + retval = -1; + + for (i = 0; i < n_devs; ++i) { + n = snprintf(prompt, sizeof(prompt), "Response #%d: ", i + 1); + if (n <= 0 || (size_t) n >= sizeof(prompt)) { + if (cfg->debug) + D(cfg->debug_file, "Failed to print response prompt"); + goto out; + } + + b64_cdh = converse(pamh, PAM_PROMPT_ECHO_ON, prompt); + b64_rpid = converse(pamh, PAM_PROMPT_ECHO_ON, prompt); + b64_authdata = converse(pamh, PAM_PROMPT_ECHO_ON, prompt); + b64_sig = converse(pamh, PAM_PROMPT_ECHO_ON, prompt); + + if (!b64_decode(b64_authdata, (void **) &authdata, &authdata_len)) { + if (cfg->debug) + D(cfg->debug_file, "Failed to decode authenticator data"); + goto out; + } + + if (!b64_decode(b64_sig, (void **) &sig, &sig_len)) { + if (cfg->debug) + D(cfg->debug_file, "Failed to decode signature"); + goto out; + } + + free(b64_cdh); + free(b64_rpid); + free(b64_authdata); + free(b64_sig); + + b64_cdh = NULL; + b64_rpid = NULL; + b64_authdata = NULL; + b64_sig = NULL; + + r = fido_assert_set_count(assert[i], 1); + if (r != FIDO_OK) { + if (cfg->debug) + D(cfg->debug_file, "Failed to set signature count of assertion %u", i); + goto out; + } + + r = fido_assert_set_authdata(assert[i], 0, authdata, authdata_len); + if (r != FIDO_OK) { + if (cfg->debug) + D(cfg->debug_file, "Failed to set authdata of assertion %u", i); + goto out; + } + + r = fido_assert_set_sig(assert[i], 0, sig, sig_len); + if (r != FIDO_OK) { + if (cfg->debug) + D(cfg->debug_file, "Failed to set signature of assertion %u", i); + goto out; + } + + free(authdata); + free(sig); + + authdata = NULL; + sig = NULL; + + if (cose_type[i] == COSE_ES256) + r = fido_assert_verify(assert[i], 0, COSE_ES256, es256_pk[i]); + else + r = fido_assert_verify(assert[i], 0, COSE_RS256, rs256_pk[i]); + + if (r == FIDO_OK) { + retval = 1; + break; + } + } + +out: + for (i = 0; i < n_devs; i++) { + fido_assert_free(&assert[i]); + es256_pk_free(&es256_pk[i]); + rs256_pk_free(&rs256_pk[i]); + } + + free(kh); + free(pk); + free(b64_challenge); + free(b64_cdh); + free(b64_rpid); + free(b64_authdata); + free(b64_sig); + free(authdata); + free(sig); + + return retval; +} + +static int _converse(pam_handle_t *pamh, int nargs, + const struct pam_message **message, + struct pam_response **response) { + struct pam_conv *conv; + int retval; + + retval = pam_get_item(pamh, PAM_CONV, (void *) &conv); + + if (retval != PAM_SUCCESS) { + return retval; + } + + return conv->conv(nargs, message, response, conv->appdata_ptr); +} + +char *converse(pam_handle_t *pamh, int echocode, const char *prompt) { + const struct pam_message msg = {.msg_style = echocode, + .msg = (char *) prompt}; + const struct pam_message *msgs = &msg; + struct pam_response *resp = NULL; + int retval = _converse(pamh, 1, &msgs, &resp); + char *ret = NULL; + + if (retval != PAM_SUCCESS || resp == NULL || resp->resp == NULL || + *resp->resp == '\000') { + + if (retval == PAM_SUCCESS && resp && resp->resp) { + ret = resp->resp; + } + } else { + ret = resp->resp; + } + + // Deallocate temporary storage. + if (resp) { + if (!ret) { + free(resp->resp); + } + free(resp); + } + + return ret; +} + +#if defined(PAM_DEBUG) +void _debug(FILE *debug_file, const char *file, int line, const char *func, + const char *fmt, ...) { + va_list ap; + va_start(ap, fmt); +#ifdef __linux__ + if (debug_file == (FILE *) -1) { + syslog(LOG_AUTHPRIV | LOG_DEBUG, DEBUG_STR, file, line, func); + vsyslog(LOG_AUTHPRIV | LOG_DEBUG, fmt, ap); + } else { + fprintf(debug_file, DEBUG_STR, file, line, func); + vfprintf(debug_file, fmt, ap); + fprintf(debug_file, "\n"); + } +#else /* Windows, MAC */ + fprintf(debug_file, DEBUG_STR, file, line, func); + vfprintf(debug_file, fmt, ap); + fprintf(debug_file, "\n"); +#endif /* __linux__ */ + va_end(ap); +} +#endif /* PAM_DEBUG */ + +#ifndef RANDOM_DEV +#define RANDOM_DEV "/dev/urandom" +#endif + +int random_bytes(void *buf, size_t cnt) { + int fd; + ssize_t n; + + fd = open(RANDOM_DEV, O_RDONLY); + if (fd < 0) + return (0); + + n = read(fd, buf, cnt); + close(fd); + if (n < 0 || (size_t) n != cnt) + return (0); + + return (1); +} diff --git a/external/bsd/pam-u2f/dist/util.h b/external/bsd/pam-u2f/dist/util.h new file mode 100644 index 000000000000..2ca61546ab52 --- /dev/null +++ b/external/bsd/pam-u2f/dist/util.h @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2014-2019 Yubico AB - See COPYING + */ + +#ifndef UTIL_H +#define UTIL_H + +#include +#include + +#define BUFSIZE 1024 +#define MAX_DEVS 24 +#define PK_LEN 130 // Public key +#define KH_LEN 86 // Key handle +#define RD_LEN 40 // Rounding +#define DEVSIZE (((PK_LEN) + (KH_LEN) + (RD_LEN))) +#define DEFAULT_AUTHFILE_DIR_VAR "XDG_CONFIG_HOME" +#define DEFAULT_AUTHFILE "/Yubico/u2f_keys" +#define DEFAULT_AUTHPENDING_FILE_PATH "/var/run/user/%d/pam-u2f-authpending" +#define DEFAULT_PROMPT "Insert your U2F device, then press ENTER." +#define DEFAULT_CUE "Please touch the device." +#define DEFAULT_ORIGIN_PREFIX "pam://" +#define DEBUG_STR "debug(pam_u2f): %s:%d (%s): " + +#if defined(DEBUG_PAM) +#define D(file, ...) _debug(file, __FILE__, __LINE__, __func__, __VA_ARGS__) +#else +#define D(file, ...) +#endif /* DEBUG_PAM */ + +typedef struct { + unsigned max_devs; + const char *client_key; + int manual; + int debug; + int nouserok; + int openasuser; + int alwaysok; + int interactive; + int cue; + int nodetect; + int userpresence; + int userverification; + int pinverification; + const char *auth_file; + const char *authpending_file; + const char *origin; + const char *appid; + const char *prompt; + const char *cue_prompt; + FILE *debug_file; + int is_custom_debug_file; +} cfg_t; + +typedef struct { + char *publicKey; + char *keyHandle; + char *coseType; + char *attributes; + int old_format; +} device_t; + +int get_devices_from_authfile(const char *authfile, const char *username, + unsigned max_devs, int verbose, FILE *debug_file, + device_t *devices, unsigned *n_devs); +void free_devices(device_t *devices, const unsigned n_devs); + +int do_authentication(const cfg_t *cfg, const device_t *devices, + const unsigned n_devs, pam_handle_t *pamh); +int do_manual_authentication(const cfg_t *cfg, const device_t *devices, + const unsigned n_devs, pam_handle_t *pamh); +char *converse(pam_handle_t *pamh, int echocode, const char *prompt); +void _debug(FILE *, const char *, int, const char *, const char *, ...); +int random_bytes(void *, size_t); + +#if !defined(HAVE_EXPLICIT_BZERO) +void explicit_bzero(void *, size_t); +#endif + +#endif /* UTIL_H */