Hello community,

here is the log from the commit of package pam_yubico for openSUSE:Factory 
checked in at 2018-03-28 10:33:18
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/pam_yubico (Old)
 and      /work/SRC/openSUSE:Factory/.pam_yubico.new (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "pam_yubico"

Wed Mar 28 10:33:18 2018 rev:5 rq:591599 version:2.25

Changes:
--------
--- /work/SRC/openSUSE:Factory/pam_yubico/pam_yubico.changes    2017-11-29 
10:53:45.842246848 +0100
+++ /work/SRC/openSUSE:Factory/.pam_yubico.new/pam_yubico.changes       
2018-03-28 10:33:20.640810481 +0200
@@ -1,0 +2,12 @@
+Tue Mar 27 11:27:03 UTC 2018 - kbabi...@suse.com
+
+- Version 2.25 (released 2018-03-27):
+  - Security: Storage of challenges in path with restricted permissions
+  - Perform OTP validation only if token is authorized
+  - Return early if the user has no authorized tokens
+  - Compare OTP IDs against `yubi_attr` only
+  - Add nullok support to challenge-response mode
+  - Several improvements to the documentation
+  - Improved debugging output and test cases
+
+-------------------------------------------------------------------

Old:
----
  pam_yubico-2.24.tar.gz
  pam_yubico-2.24.tar.gz.sig

New:
----
  pam_yubico-2.25.tar.gz
  pam_yubico-2.25.tar.gz.sig

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ pam_yubico.spec ++++++
--- /var/tmp/diff_new_pack.bZ3wOk/_old  2018-03-28 10:33:21.724771494 +0200
+++ /var/tmp/diff_new_pack.bZ3wOk/_new  2018-03-28 10:33:21.732771207 +0200
@@ -1,7 +1,7 @@
 #
 # spec file for package pam_yubico
 #
-# Copyright (c) 2017 SUSE LINUX GmbH, Nuernberg, Germany.
+# Copyright (c) 2018 SUSE LINUX GmbH, Nuernberg, Germany.
 #
 # All modifications and additions to the file contributed by third parties
 # remain the property of their copyright owners, unless otherwise agreed
@@ -17,7 +17,7 @@
 
 
 Name:           pam_yubico
-Version:        2.24
+Version:        2.25
 Release:        0
 Summary:        Yubico Pluggable Authentication Module (PAM)
 License:        BSD-2-Clause
@@ -30,9 +30,8 @@
 BuildRequires:  libyubikey-devel
 BuildRequires:  openldap2-devel
 BuildRequires:  pam-devel
-BuildRequires:  pkg-config
+BuildRequires:  pkgconfig
 BuildRequires:  pkgconfig(ykpers-1) >= 1.11
-BuildRoot:      %{_tmppath}/%{name}-%{version}-build
 
 %description
 
@@ -46,11 +45,10 @@
 make %{?_smp_mflags}
 
 %install
-make DESTDIR=%{buildroot} install %{?_smp_mflags}
+%make_install
 find %{buildroot} -type f -name "*.la" -delete -print
 
 %files
-%defattr(-,root,root)
 %doc README COPYING AUTHORS NEWS doc/*
 %{_bindir}/ykpamcfg
 /%{_lib}/security/pam_yubico.so

++++++ pam_yubico-2.24.tar.gz -> pam_yubico-2.25.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pam_yubico-2.24/ChangeLog 
new/pam_yubico-2.25/ChangeLog
--- old/pam_yubico-2.24/ChangeLog       2016-11-25 10:15:53.000000000 +0100
+++ new/pam_yubico-2.25/ChangeLog       2018-03-27 12:41:18.000000000 +0200
@@ -1,3 +1,137 @@
+2018-03-27  Klas Lindfors <k...@yubico.com>
+
+       * NEWS: NEWS for 2.25
+
+2018-03-20  Klas Lindfors <k...@yubico.com>
+
+       * : commit 8db623de5dfe81c4b62379fcf91e7908342afa10 Author: Karol
+       Babioch <kbabi...@suse.de> Date:   Tue Mar 20 13:55:21 2018 +0100
+
+2018-03-20  Karol Babioch <kbabi...@suse.de>
+
+       * pam_yubico.c: Print out nullok parameter in debug mode This prints 
out the nullok parameter in debug mode, which was
+       introduced previously.
+
+2017-12-15  Klas Lindfors <k...@yubico.com>
+
+       * : commit 504c838b5af4ff1341eb27217ea9358eac986dd0 Author: Robert
+       Giles <r...@users.noreply.github.com> Date:   Thu Dec 14 11:51:20
+       2017 -0600
+
+2017-12-14  Robert Giles <r...@users.noreply.github.com>
+
+       * README: Typo in asciidoc syntax.
+
+2017-12-14  Robert Giles <r...@users.noreply.github.com>
+
+       * README: Clarify documentation;  this example configuration is also
+       useful for just regular pam_yubico configuration elsewhere against
+       AD, too.
+
+2017-11-20  Klas Lindfors <k...@yubico.com>
+
+       * : commit 5003f3974d2b57985cd453e0f8547ac495a6305d Author: Andy
+       Neff <andyn...@users.noreply.github.com> Date:   Wed Nov 15 15:46:46
+       2017 -0500
+
+2017-10-10  Klas Lindfors <k...@yubico.com>
+
+       * doc/YubiKey_and_OpenVPN_via_PAM.adoc: drop more text with
+       unnecessary old version deps
+
+2017-10-06  Klas Lindfors <k...@yubico.com>
+
+       * doc/YubiKey_and_OpenVPN_via_PAM.adoc: drop outdated version
+       recommendations
+
+2017-08-30  Klas Lindfors <k...@yubico.com>
+
+       * pam_yubico.c: improved debug messages when getpwnam_r() doesn't
+       return a user
+
+2017-08-07  Klas Lindfors <k...@yubico.com>
+
+       * README, pam_yubico.8.txt: doc: fixup which proxy schemes are
+       supported, add http and https fixes #127
+
+2017-06-26  Klas Lindfors <k...@yubico.com>
+
+       * : commit 42e8a06bfe1b53df9f11118222d7f36c55cf7d58 Author: Larhard
+       <larh...@gmail.com> Date:   Sat Jun 24 08:30:40 2017 +0000
+
+2017-06-23  Larhard <larh...@gmail.com>
+
+       * util.c: clear check_user_challenge_file
+
+2017-06-16  Alessio Di Mauro <ales...@yubico.com>
+
+       * README: Add message about project rewrite to README
+
+2017-04-09  Larhard <larh...@gmail.com>
+
+       * util.c: code cleanup
+
+2017-04-08  Larhard <larh...@gmail.com>
+
+       * pam_yubico.c, util.c, util.h: add nullok support to
+       challenge-response mode
+
+2017-03-06  Klas Lindfors <k...@yubico.com>
+
+       * tests/pam_test.c: add tests for use_first_pass
+
+2017-03-06  Klas Lindfors <k...@yubico.com>
+
+       * : commit d048a4a6e2fbe55982c5fc819ab2016b89ca136b Author: Mickaël
+       Thomas <micka...@gmail.com> Date:   Sun Feb 26 20:38:33 2017 +0100
+
+2017-02-26  Micka�l Thomas <micka...@gmail.com>
+
+       * pam_yubico.c: Compare OTP IDs against `yubi_attr` only Currently we 
trust the LDAP server to only return the `yubi_attr`
+       attribute, yet we loop over all possible attributes when there
+       should only be one.  Since the bundled test LDAP server ignores the 
requested attributes
+       list, we must make sure to only match against the `yubi_attr`
+       attibute as opposed to "all of them".  This also fixes an issue where 
AUTH_NOT_FOUND was returned instead
+       of AUTH_NO_TOKENS when there were no values returned for `yubi_attr`
+       but another attribute's value was considered as a candidate token.
+
+2017-02-26  Micka�l Thomas <micka...@gmail.com>
+
+       * README, pam_yubico.c, tests/util_test.c, util.c, util.h: Return
+       early if the user has no authorized tokens Currently, if a user has no 
associated tokens, we still prompt for
+       an OTP challenge and attempt to verify it.  This adds a check earlier 
to avoid the useless prompt in that case.  The `nullok` option is also added. 
It changes the return value from
+       PAM_USER_UNKNOWN to PAM_IGNORE. (fixes #97) Finally, some constants 
have been turned to symbolic form for
+       clarity and debugging output is improved.
+
+2017-02-26  Micka�l Thomas <micka...@gmail.com>
+
+       * pam_yubico.c: Perform OTP validation only if token is authorized When 
using `try_first_pass` or `use_first_pass`, the password we
+       inherit from PAM might not actually be an OTP challenge.  Currently, we 
happily leak it to the validation server without first
+       checking if it matches an authorized token ID.  This postpones sending 
the actual request until we know the token ID
+       is authorized.
+
+2017-02-23  Klas Lindfors <k...@yubico.com>
+
+       * : commit 0d61b263ca3ce5a912fbf4895830b93b64c52fb7 Author: Thomas
+       Patzke <tho...@patzke.org> Date:   Thu Feb 23 09:01:27 2017 +0100
+
+2017-01-03  Klas Lindfors <k...@yubico.com>
+
+       * ykpamcfg.1.txt: doc: fix typo
+
+2016-11-25  Klas Lindfors <k...@yubico.com>
+
+       * tests/aux/build-and-test.sh: more stuff to make a2x work properly
+       on mac for tests
+
+2016-11-25  Klas Lindfors <k...@yubico.com>
+
+       * tests/aux/build-and-test.sh: install docbook-xsl on mac for tests 
will hopefully make the tests run smoother
+
+2016-11-25  Klas Lindfors <k...@yubico.com>
+
+       * NEWS, configure.ac: bump versions
+
 2016-11-25  Klas Lindfors <k...@yubico.com>
 
        * NEWS: NEWS for 2.24
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pam_yubico-2.24/NEWS new/pam_yubico-2.25/NEWS
--- old/pam_yubico-2.24/NEWS    2016-11-25 10:15:17.000000000 +0100
+++ new/pam_yubico-2.25/NEWS    2018-03-27 12:37:14.000000000 +0200
@@ -1,5 +1,17 @@
 pam_yubico NEWS -- History of user-visible changes.             -*- outline -*-
 
+* Version 2.25 (released 2018-03-27)
+
+** Documentation updates.
+
+** Only do OTP validation if it's a token that might be valid.
+
+** Return early in case user has no valid tokens.
+
+** Ldap, compare values only with yubi_attr attributes.
+
+** Add nullok parameter.
+
 * Version 2.24 (released 2016-11-25)
 
 ** Debug mode changed, allows file output with debug_file.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pam_yubico-2.24/README new/pam_yubico-2.25/README
--- old/pam_yubico-2.24/README  2016-09-08 10:38:24.000000000 +0200
+++ new/pam_yubico-2.25/README  2017-12-15 08:08:55.000000000 +0100
@@ -5,6 +5,7 @@
 GNU/Linux, Solaris and Mac OS X for user authentication, and by other
 specialized applications such as NCSA MyProxy.
 
+IMPORTANT: This project is being rewritten and will only receive security 
updates.
 
 Status and Roadmap
 ------------------
@@ -171,6 +172,12 @@
 password is available or the password is not appropriate, the user
 will be denied access.
 
+nullok::
+If set, don't fail when there are no tokens declared for the user
+in the authorization mapping files or in LDAP.
+This can be used to make YubiKey authentication optional unless
+the user has associated tokens.
+
 urllist::
 List  of  URL  templates to be used. This is set by calling
 ykclient_set_url_bases. The list should be in the format :
@@ -193,9 +200,9 @@
 
 proxy::
 specify a proxy to connect to the validation server. Valid schemes are
-socks4://, socks4a://, socks5:// or socks5h://. Socks5h asks the proxy
-to do the dns resolving. If no scheme or port is specified HTTP proxy
-port 1080 will be used.
+http://, https://, socks4://, socks4a://, socks5:// or socks5h://.
+Socks5h asks the proxy to do the dns resolving. If no scheme or port is
+specified HTTP proxy port 1080 will be used.
 
 verbose_otp::
    This argument is used to show the OTP (One-Time Password) when it
@@ -351,13 +358,14 @@
 
 OpenVPN and ActiveDirectory
 ---------------------------
+See Michael Ludvig's sample Active Directory schema extensions for YubiKey 
public ID attribute storage / association with a particular user account:  
https://github.com/mludvig/yubikey-ldap/tree/master/microsoft-schema
 
 create file '/etc/pam.d/openvpn':
 
-   auth  required  pam_yubico.so ldap_uri=ldap://ldap-srv debug id=[Your API 
Client ID] yubi_attr=pager
-      ldapdn=dc=ad,dc=next-audience,dc=net
-      
ldap_filter=(&(sAMAccountName=%u)(memberOf=CN=mygroup,OU=DefaultUser,DC=adivser,DC=net))
-      ldap_bind_user=bind_user ldap_bind_password=bind_password try_first_pass
+   auth  required  pam_yubico.so ldap_uri=ldap://contoso.com debug id=[Your 
API ID] yubi_attr=yubiKeyId
+      ldapdn=DC=contoso,DC=com
+      
ldap_filter=(&(sAMAccountName=%u)(objectClass=user)(memberOf=CN=somegroup,DC=contoso,DC=com))
+      [ldap_bind_user=CN=binduser,OU=Service Accounts,DC=contoso,DC=com] 
ldap_bind_password=bind_password try_first_pass
    account required  pam_yubico.so
 
 create file 'openvpn.conf'
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pam_yubico-2.24/configure 
new/pam_yubico-2.25/configure
--- old/pam_yubico-2.24/configure       2016-11-07 12:11:59.000000000 +0100
+++ new/pam_yubico-2.25/configure       2017-08-30 08:31:49.000000000 +0200
@@ -1,6 +1,6 @@
 #! /bin/sh
 # Guess values for system-dependent variables and create Makefiles.
-# Generated by GNU Autoconf 2.69 for pam_yubico 2.24.
+# Generated by GNU Autoconf 2.69 for pam_yubico 2.25.
 #
 #
 # Copyright (C) 1992-1996, 1998-2012 Free Software Foundation, Inc.
@@ -587,8 +587,8 @@
 # Identity of this package.
 PACKAGE_NAME='pam_yubico'
 PACKAGE_TARNAME='pam_yubico'
-PACKAGE_VERSION='2.24'
-PACKAGE_STRING='pam_yubico 2.24'
+PACKAGE_VERSION='2.25'
+PACKAGE_STRING='pam_yubico 2.25'
 PACKAGE_BUGREPORT=''
 PACKAGE_URL=''
 
@@ -1357,7 +1357,7 @@
   # Omit some internal or obsolete options to make the list less imposing.
   # This message is too long to be a string in the A/UX 3.1 sh.
   cat <<_ACEOF
-\`configure' configures pam_yubico 2.24 to adapt to many kinds of systems.
+\`configure' configures pam_yubico 2.25 to adapt to many kinds of systems.
 
 Usage: $0 [OPTION]... [VAR=VALUE]...
 
@@ -1428,7 +1428,7 @@
 
 if test -n "$ac_init_help"; then
   case $ac_init_help in
-     short | recursive ) echo "Configuration of pam_yubico 2.24:";;
+     short | recursive ) echo "Configuration of pam_yubico 2.25:";;
    esac
   cat <<\_ACEOF
 
@@ -1553,7 +1553,7 @@
 test -n "$ac_init_help" && exit $ac_status
 if $ac_init_version; then
   cat <<\_ACEOF
-pam_yubico configure 2.24
+pam_yubico configure 2.25
 generated by GNU Autoconf 2.69
 
 Copyright (C) 2012 Free Software Foundation, Inc.
@@ -1918,7 +1918,7 @@
 This file contains any messages produced by compilers while
 running configure, to aid debugging if configure makes a mistake.
 
-It was created by pam_yubico $as_me 2.24, which was
+It was created by pam_yubico $as_me 2.25, which was
 generated by GNU Autoconf 2.69.  Invocation command line was
 
   $ $0 $@
@@ -2782,7 +2782,7 @@
 
 # Define the identity of the package.
  PACKAGE='pam_yubico'
- VERSION='2.24'
+ VERSION='2.25'
 
 
 cat >>confdefs.h <<_ACEOF
@@ -14750,7 +14750,7 @@
 # report actual input values of CONFIG_FILES etc. instead of their
 # values after options handling.
 ac_log="
-This file was extended by pam_yubico $as_me 2.24, which was
+This file was extended by pam_yubico $as_me 2.25, which was
 generated by GNU Autoconf 2.69.  Invocation command line was
 
   CONFIG_FILES    = $CONFIG_FILES
@@ -14807,7 +14807,7 @@
 cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
 ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; 
s/[\\""\`\$]/\\\\&/g'`"
 ac_cs_version="\\
-pam_yubico config.status 2.24
+pam_yubico config.status 2.25
 configured by $0, generated by GNU Autoconf 2.69,
   with options \\"\$ac_cs_config\\"
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pam_yubico-2.24/configure.ac 
new/pam_yubico-2.25/configure.ac
--- old/pam_yubico-2.24/configure.ac    2016-09-08 10:38:24.000000000 +0200
+++ new/pam_yubico-2.25/configure.ac    2017-08-30 08:24:30.000000000 +0200
@@ -26,7 +26,7 @@
 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
-AC_INIT([pam_yubico], [2.24])
+AC_INIT([pam_yubico], [2.25])
 AC_CONFIG_AUX_DIR([build-aux])
 AC_CONFIG_MACRO_DIR([m4])
 AM_INIT_AUTOMAKE([1.11 foreign -Wall -Werror])
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/pam_yubico-2.24/doc/Authentication_Using_Challenge-Response.adoc 
new/pam_yubico-2.25/doc/Authentication_Using_Challenge-Response.adoc
--- old/pam_yubico-2.24/doc/Authentication_Using_Challenge-Response.adoc        
2016-02-26 09:35:14.000000000 +0100
+++ new/pam_yubico-2.25/doc/Authentication_Using_Challenge-Response.adoc        
2017-08-30 08:24:30.000000000 +0200
@@ -68,16 +68,18 @@
 Stored initial challenge and expected response in 
'/home/alice/.yubico/challenge-123456'.
 $
 ------
-If your /home/user folder is encrypted you should move the challenge file in a 
different path (i.e. /var/yubico) and then set the right permission for the 
user to create the files. To do this do as follow:
+From security perspective, it is generally a good idea to move the challenge 
file in a system-wide path that is only read- and writable by root. To do this 
do as follow:
 
 ----
-$ mkdir /var/yubico
-$ chmod +t /var/yubico
-$ chmod 777 /var/yubico
-$ ykpamcfg -2 -v -p /var/yubico
+$ sudo mkdir /var/yubico
+$ sudo chown root.root /var/yubico
+$ sudo chmod 700 /var/yubico
+$ ykpamcfg -2 -v
 ...
-Stored initial challenge and expected response in '/var/yubico/alice-123456'.
-$
+Stored initial challenge and expected response in 
'$HOME/.yubico/challenge-123456'.
+$ sudo mv ~/.yubico/challenge-123456 /var/yubico/alice-123456
+$ sudo chown root.root /var/yubico/alice-123456
+$ sudo chmod 600 /var/yubico/alice-123456
 ----
 
 It is important that the file is named with the name of the user that is going 
to be authenticated by this YubiKey.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pam_yubico-2.24/doc/YubiKey_and_OpenVPN_via_PAM.adoc 
new/pam_yubico-2.25/doc/YubiKey_and_OpenVPN_via_PAM.adoc
--- old/pam_yubico-2.24/doc/YubiKey_and_OpenVPN_via_PAM.adoc    2015-03-13 
21:20:24.000000000 +0100
+++ new/pam_yubico-2.25/doc/YubiKey_and_OpenVPN_via_PAM.adoc    2017-10-10 
09:37:44.000000000 +0200
@@ -7,14 +7,14 @@
 
 Successful configuration of the Yubico PAM module to support two factor 
authentication for OpenVPN has the following prerequisites:
 
-Operating System:: 
-Any Unix operating system which supports 
+Operating System:: Any Unix operating system which supports
 http://www.kernel.org/pub/linux/libs/pam[PAM] (Pluggable Authentication Module)
 Complier:: http://gcc.gnu.org[GNU GCC complier]
-https://developers.yubico.com/yubico-pam[Yubico PAM Module]:: Version 1.8
-http://openvpn.net/index.php/downloads.html[OpenVPN]:: Version 2.0.9.
-http://freeradius.org/download.html[FreeRADIUS]: Version 1.1.7 or later.
-Pam_Radius:: Version 1.3.17.
+Software::
+https://developers.yubico.com/yubico-pam[Yubico PAM Module] +
+http://openvpn.net/index.php/downloads.html[OpenVPN] +
+http://freeradius.org/download.html[FreeRADIUS] +
+Pam_Radius
 
 == Configuration
 
@@ -249,11 +249,7 @@
 
 Our test environment is as follows:
 
-i) Operating System: Fedora release 8 (Werewolf)
-ii) FreeRADIUS Server : FreeRADIUS Version 1.1.7
-iii) Pam_Radius: pam_radius_auth 1.3.17
-iv) Yubico PAM: pam_yubico  Version 1.8
-v) `/etc/pam.d/openvpn` file:
+* `/etc/pam.d/openvpn` file:
 
 ------
 account         required        pam_radius_auth.so
@@ -265,19 +261,8 @@
 
 We have tested the pam_yubico configuration on following Linux sever platforms:
 
-===== Fedora 8
-Operating system:: Fedora release 8 (Werewolf)
-OpenVPN Server:: OpenVPN Version 2.0.9
-Yubico PAM:: pam_yubico  Version 1.8
-FreeRADIUS Server:: FreeRADIUS Server Version 1.1.7
-Pam_radius:: pam_radius_auth Version 1.3.17
-
-===== Fedora 6
-Operating system:: Fedora Core release 6 (Zod)
-OpenVPN Server:: OpenVPN Version 2.0.9
-Yubico PAM:: pam_yubico version 1.8
-FreeRADIUS Server:: FreeRADIUS Server Version 1.1.7
-Pam_radius:: pam_radius_auth Version 1.3.17
+* Fedora 8
+* Fedora 6
 
 To test the configuration, first create a couple of test users
 on the system where FreeRADIUS server is running and configure
@@ -292,6 +277,3 @@
 OpenVPN client will first prompt for username, enter the username.
 After that OpenVPN client will prompt for password, enter user’s
 password immediately followed by an OTP generated by a YubiKey.
-
-
-NOTE: Please use OpenVPN server Version 2.0.9 (Latest Stable Version), as 
older and newer beta versions have problems with PAM libraries. RADIUS 
authentication will fail if it is configured with older or latest beta versions 
of OpenVPN Server.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/pam_yubico-2.24/doc/Yubikey_and_SELinux_on_Fedora_18_and_up.adoc 
new/pam_yubico-2.25/doc/Yubikey_and_SELinux_on_Fedora_18_and_up.adoc
--- old/pam_yubico-2.24/doc/Yubikey_and_SELinux_on_Fedora_18_and_up.adoc        
2014-11-03 11:23:32.000000000 +0100
+++ new/pam_yubico-2.25/doc/Yubikey_and_SELinux_on_Fedora_18_and_up.adoc        
2017-11-20 10:16:40.000000000 +0100
@@ -5,3 +5,7 @@
 To make a long story short, if you want to use a Yubikey on a system running 
Fedora 18 or higher (and probably RHEL7, eventually), you'll need to toggle the 
'authlogin_yubikey' SELinux boolean, like so:
 
     setsebool -P authlogin_yubikey 1
+
+If you are using your own server via `urllist`/`url` in the pam conf file and 
using a non-standard http port, you will need to add that port to the 
`http_port_t` port list. For example, port `12345`:
+
+    semanage port -a -t http_port_t -p tcp 12345
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pam_yubico-2.24/pam_yubico.8 
new/pam_yubico-2.25/pam_yubico.8
--- old/pam_yubico-2.24/pam_yubico.8    2016-06-22 10:20:35.000000000 +0200
+++ new/pam_yubico-2.25/pam_yubico.8    2018-03-27 12:41:18.000000000 +0200
@@ -1,13 +1,13 @@
 '\" t
 .\"     Title: pam_yubico
 .\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
-.\" Generator: DocBook XSL Stylesheets v1.78.1 <http://docbook.sf.net/>
-.\"      Date: Version 2.24
+.\" Generator: DocBook XSL Stylesheets v1.79.1 <http://docbook.sf.net/>
+.\"      Date: Version 2.25
 .\"    Manual: Yubico PAM Module Manual
 .\"    Source: yubico-pam
 .\"  Language: English
 .\"
-.TH "PAM_YUBICO" "8" "Version 2\&.24" "yubico\-pam" "Yubico PAM Module Manual"
+.TH "PAM_YUBICO" "8" "Version 2\&.25" "yubico\-pam" "Yubico PAM Module Manual"
 .\" -----------------------------------------------------------------
 .\" * Define some portability stuff
 .\" -----------------------------------------------------------------
@@ -83,6 +83,11 @@
 The argument use_first_pass forces the module to use a previous stacked 
modules password and will never prompt the user \- if no password is available 
or the password is not appropriate, the user will be denied access\&.
 .RE
 .PP
+\fBnullok\fR
+.RS 4
+If set, don\(cqt fail when there are no tokens declared for the user in the 
authorization mapping files or in LDAP\&. This can be used to make YubiKey 
authentication optional unless the user has associated tokens\&.
+.RE
+.PP
 \fBurllist\fR=\fIlist\fR
 .RS 4
 List of URL templates to be used\&. This is set by calling 
ykclient_set_url_bases\&. The list should be in the format:
@@ -110,7 +115,7 @@
 .PP
 \fBproxy\fR=\fIproxy\fR
 .RS 4
-Specify a proxy to connect to the validation server\&. Valid schemes are 
socks4://, socks4a://, socks5:// or socks5h://\&. Socks5h asks the proxy to do 
the dns resolving\&. If no scheme or port is specified HTTP proxy port 1080 
will be used\&. E\&.g\&. socks5h://user:pass@10\&.10\&.0\&.1:1080
+Specify a proxy to connect to the validation server\&. Valid schemes are 
http://, https://, socks4://, socks4a://, socks5:// or socks5h://\&. Socks5h 
asks the proxy to do the dns resolving\&. If no scheme or port is specified 
HTTP proxy port 1080 will be used\&. E\&.g\&. 
socks5h://user:pass@10\&.10\&.0\&.1:1080
 .RE
 .PP
 \fBverbose_otp\fR
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pam_yubico-2.24/pam_yubico.8.txt 
new/pam_yubico-2.25/pam_yubico.8.txt
--- old/pam_yubico-2.24/pam_yubico.8.txt        2016-06-22 10:17:14.000000000 
+0200
+++ new/pam_yubico-2.25/pam_yubico.8.txt        2018-03-20 14:09:06.000000000 
+0100
@@ -41,6 +41,9 @@
 *use_first_pass*::
 The argument use_first_pass forces the module to use a previous stacked 
modules password and will never prompt the user - if no password is available 
or the password is not appropriate, the user will be denied access.
 
+*nullok*::
+If set, don’t fail when there are no tokens declared for the user in the 
authorization mapping files or in LDAP. This can be used to make YubiKey 
authentication optional unless the user has associated tokens.
+
 *urllist*=_list_::
 List of URL templates to be used. This is set by calling 
ykclient_set_url_bases.
 The list should be in the format:
@@ -54,7 +57,7 @@
 Specify the path where X509 certificates are stored. This is required if 
'https' or 'ldaps' are used in 'url' and 'ldap_uri' respectively.
 
 *proxy*=_proxy_::
-Specify a proxy to connect to the validation server. Valid schemes are 
socks4://, socks4a://, socks5:// or socks5h://. Socks5h asks the proxy to do 
the dns resolving. If no scheme or port is specified HTTP proxy port 1080 will 
be used. E.g. socks5h://user:pass@10.10.0.1:1080
+Specify a proxy to connect to the validation server. Valid schemes are 
http://, https://, socks4://, socks4a://, socks5:// or socks5h://. Socks5h asks 
the proxy to do the dns resolving. If no scheme or port is specified HTTP proxy 
port 1080 will be used. E.g. socks5h://user:pass@10.10.0.1:1080
 
 *verbose_otp*::
 This argument is used to show the OTP (One Time Password) when it is entered, 
i.e. to enable terminal echo of entered characters.  You are advised to not use 
this, if you are using two factor authentication because that will display your 
password on the screen.  This requires the service using the PAM module to 
display custom fields.  This option can not be used with OpenSSH.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pam_yubico-2.24/pam_yubico.c 
new/pam_yubico-2.25/pam_yubico.c
--- old/pam_yubico-2.24/pam_yubico.c    2016-09-08 10:38:03.000000000 +0200
+++ new/pam_yubico-2.25/pam_yubico.c    2018-03-20 14:09:06.000000000 +0100
@@ -108,6 +108,7 @@
   int verbose_otp;
   int try_first_pass;
   int use_first_pass;
+  int nullok;
   const char *auth_file;
   const char *capath;
   const char *cainfo;
@@ -136,8 +137,9 @@
 #define DBG(x...) if (cfg->debug) { D(cfg->debug_file, x); }
 
 /*
- * Authorize authenticated OTP_ID for login as USERNAME using
- * AUTHFILE.  Return -2 if the user is unknown, -1 if the OTP_ID does not 
match,  0 on internal failures, otherwise success.
+ * Authorize authenticated OTP_ID for login as USERNAME using AUTHFILE.
+ *
+ * Returns one of AUTH_FOUND, AUTH_NOT_FOUND, AUTH_NO_TOKENS, AUTH_ERROR.
  */
 static int
 authorize_user_token (struct cfg *cfg,
@@ -145,7 +147,7 @@
                      const char *otp_id,
                      pam_handle_t *pamh)
 {
-  int retval;
+  int retval = AUTH_ERROR;
 
   if (cfg->auth_file)
     {
@@ -166,8 +168,12 @@
 
       pwres = getpwnam_r (username, &pass, buf, buflen, &p);
       if (p == NULL) {
-       DBG ("getpwnam_r: %s", strerror(pwres));
-       return 0;
+        if (pwres == 0) {
+          DBG ("User '%s' not found", username);
+        } else {
+          DBG ("getpwnam_r: %s", strerror(pwres));
+        }
+        return AUTH_ERROR;
       }
 
       /* Getting file from user home directory
@@ -175,21 +181,19 @@
        */
       if (! get_user_cfgfile_path (NULL, "authorized_yubikeys", p, &userfile)) 
{
        DBG ("Failed figuring out per-user cfgfile");
-       return 0;
+       return AUTH_ERROR;
       }
 
       DBG ("Dropping privileges");
       if(pam_modutil_drop_priv(pamh, &privs, p)) {
         DBG ("could not drop privileges");
-       retval = 0;
-       goto free_out;
+        goto free_out;
       }
 
       retval = check_user_token (userfile, username, otp_id, cfg->debug, 
cfg->debug_file);
 
       if(pam_modutil_regain_priv(pamh, &privs)) {
-        DBG (("could not restore privileges"));
-        retval = 0;
+        DBG ("could not restore privileges");
         goto free_out;
       }
 
@@ -202,7 +206,7 @@
 
 /*
  * This function will look in ldap id the token correspond to the
- * requested user. It will returns 0 for failure and 1 for success.
+ * requested user.
  *
  * ldaps is only supported for ldap_uri based connections.
  * ldap_cacertfile usually needs to be set for this to work.
@@ -218,13 +222,15 @@
  * If using ldap_uri, you can specify multiple failover hosts
  * eg.
  * ldap_uri=ldaps://host1.fqdn.example.com,ldaps://host2.fqdn.example.com
+ *
+ * Returns one of AUTH_FOUND, AUTH_NOT_FOUND, AUTH_NO_TOKENS, AUTH_ERROR.
  */
 static int
 authorize_user_token_ldap (struct cfg *cfg,
                           const char *user,
                           const char *token_id)
 {
-  int retval = 0;
+  int retval = AUTH_ERROR;
 #ifdef HAVE_LIBLDAP
   /* LDAPv2 is historical -- RFC3494. */
   int protocol = LDAP_VERSION3;
@@ -232,7 +238,7 @@
   LDAP *ld = NULL;
   LDAPMessage *result = NULL, *e;
   BerElement *ber;
-  char *a;
+  char *attr_name;
   char *attrs[2] = {NULL, NULL};
 
   struct berval **vals;
@@ -261,7 +267,6 @@
       if (rc != LDAP_SUCCESS)
        {
          DBG ("ldap_initialize: %s", ldap_err2string (rc));
-         retval = 0;
          goto done;
        }
     }
@@ -270,7 +275,6 @@
       if ((ld = ldap_init (cfg->ldapserver, PORT_NUMBER)) == NULL)
        {
          DBG ("ldap_init");
-         retval = 0;
          goto done;
        }
     }
@@ -301,7 +305,6 @@
     i = (strlen(cfg->user_attr) + strlen(cfg->ldapdn) + strlen(user) + 3) * 
sizeof(char);
     if ((find = malloc(i)) == NULL) {
       DBG ("Failed allocating %zu bytes", i);
-      retval = 0;
       goto done;
     }
     sprintf (find, "%s=%s,%s", cfg->user_attr, user, cfg->ldapdn);
@@ -325,48 +328,63 @@
     {
       DBG ("ldap_search_ext_s: %s", ldap_err2string (rc));
 
-      retval = 0;
       goto done;
     }
 
+  /* Start looing for tokens */
+  retval = AUTH_NO_TOKENS;
+
   e = ldap_first_entry (ld, result);
   if (e == NULL)
     {
       DBG (("No result from LDAP search"));
-      retval = -2;
     }
   else
     {
-      retval = -1;
       /* Iterate through each returned attribute. */
-      for (a = ldap_first_attribute (ld, e, &ber);
-          a != NULL; a = ldap_next_attribute (ld, e, ber))
+      for (attr_name = ldap_first_attribute (ld, e, &ber);
+          attr_name != NULL; attr_name = ldap_next_attribute (ld, e, ber))
        {
-         if ((vals = ldap_get_values_len (ld, e, a)) != NULL)
+         if (strcmp(attr_name, cfg->yubi_attr) != 0) {
+             DBG("Ignored non-requested attribute: %s", attr_name);
+             continue;
+         }
+         if ((vals = ldap_get_values_len (ld, e, attr_name)) != NULL)
            {
              yubi_attr_prefix_len = cfg->yubi_attr_prefix ? 
strlen(cfg->yubi_attr_prefix) : 0;
 
+             DBG("LDAP : Found %i values for %s - checking if any of them 
match '%s:%s'",
+                 ldap_count_values_len(vals),
+                 attr_name,
+                 cfg->yubi_attr_prefix ? cfg->yubi_attr_prefix : "",
+                 token_id ? token_id : "(null)");
+
              /* Compare each value for the attribute against the token id. */
              for (i = 0; vals[i] != NULL; i++)
                {
-                 DBG("LDAP : Found %i values - checking if any of them match 
'%s:%s:%s'",
-                      ldap_count_values_len(vals),
-                      vals[i]->bv_val,
-                      cfg->yubi_attr_prefix ? cfg->yubi_attr_prefix : "", 
token_id);
+                 DBG("LDAP : Checking value %i: %s:%s",
+                     i + 1,
+                     cfg->yubi_attr_prefix ? cfg->yubi_attr_prefix : "",
+                     vals[i]->bv_val);
 
                  /* Only values containing this prefix are considered. */
                  if ((!cfg->yubi_attr_prefix || !strncmp 
(cfg->yubi_attr_prefix, vals[i]->bv_val, yubi_attr_prefix_len)))
                    {
-                     if(!strncmp (token_id, vals[i]->bv_val + 
yubi_attr_prefix_len, strlen (vals[i]->bv_val + yubi_attr_prefix_len)))
+                     /* We have found at least one possible token ID so change 
the default return value to AUTH_NOT_FOUND */
+                     if (retval == AUTH_NO_TOKENS)
+                       {
+                         retval = AUTH_NOT_FOUND;
+                       }
+                     if(token_id && !strncmp (token_id, vals[i]->bv_val + 
yubi_attr_prefix_len, strlen (vals[i]->bv_val + yubi_attr_prefix_len)))
                        {
                          DBG ("Token Found :: %s", vals[i]->bv_val);
-                         retval = 1;
+                         retval = AUTH_FOUND;
                        }
                    }
                }
              ldap_value_free_len (vals);
            }
-         ldap_memfree (a);
+         ldap_memfree (attr_name);
        }
       if (ber != NULL)
          ber_free (ber, 0);
@@ -459,6 +477,39 @@
 
   ret = PAM_AUTH_ERR;
 
+  pwres = getpwnam_r (username, &pass, pwbuf, pwbuflen, &p);
+  if (p == NULL) {
+      if (pwres == 0) {
+          DBG ("User '%s' not found", username);
+      } else {
+          DBG ("getpwnam_r: %s", strerror(pwres));
+      }
+      goto out;
+  }
+
+  DBG("Checking for user challenge files");
+  switch(check_user_challenge_file(cfg->chalresp_path, p, cfg->debug_file)) {
+    case AUTH_FOUND:
+      DBG("Challenge files found");
+      break;
+    case AUTH_NOT_FOUND:
+      DBG("No challenge files found");
+      if (cfg->nullok) {
+        ret = PAM_IGNORE;
+      } else {
+        ret = PAM_USER_UNKNOWN;
+      }
+      goto out;
+    case AUTH_ERROR:
+      DBG ("Internal error while looking for user challenge files");
+      ret = PAM_AUTHINFO_UNAVAIL;
+      goto out;
+    default:
+      DBG ("Unhandled value while looking for user challenge files");
+      ret = PAM_AUTHINFO_UNAVAIL;
+      goto out;
+  }
+
   if (! init_yubikey(&yk)) {
     DBG("Failed initializing YubiKey");
     goto out;
@@ -469,12 +520,6 @@
     goto out;
   }
 
-  pwres = getpwnam_r (username, &pass, pwbuf, pwbuflen, &p);
-  if (p == NULL) {
-      DBG ("getpwnam_r: %s", strerror(pwres));
-      goto out;
-  }
-
   if (! get_user_challenge_file (yk, cfg->chalresp_path, p, &userfile, 
cfg->debug_file)) {
     DBG("Failed getting user challenge file for user %s", username);
     goto out;
@@ -713,6 +758,8 @@
        cfg->try_first_pass = 1;
       if (strcmp (argv[i], "use_first_pass") == 0)
        cfg->use_first_pass = 1;
+      if (strcmp (argv[i], "nullok") == 0)
+       cfg->nullok = 1;
       if (strncmp (argv[i], "authfile=", 9) == 0)
        cfg->auth_file = argv[i] + 9;
       if (strncmp (argv[i], "capath=", 7) == 0)
@@ -795,6 +842,7 @@
   DBG ("verbose_otp=%d", cfg->verbose_otp);
   DBG ("try_first_pass=%d", cfg->try_first_pass);
   DBG ("use_first_pass=%d", cfg->use_first_pass);
+  DBG ("nullok=%d", cfg->nullok);
   DBG ("authfile=%s", cfg->auth_file ? cfg->auth_file : "(null)");
   DBG ("ldapserver=%s", cfg->ldapserver ? cfg->ldapserver : "(null)");
   DBG ("ldap_uri=%s", cfg->ldap_uri ? cfg->ldap_uri : "(null)");
@@ -962,6 +1010,37 @@
          goto done;
        }
     }
+  /* check if the user has at least one associated token id */
+  /* we set otp_id to NULL so that no matches will ever be found
+   * but AUTH_NO_TOKENS will be returned if there are no tokens for the user */
+  if (cfg->ldapserver != NULL || cfg->ldap_uri != NULL)
+    valid_token = authorize_user_token_ldap (cfg, user, NULL);
+  else
+    valid_token = authorize_user_token (cfg, user, NULL, pamh);
+
+  switch(valid_token)
+    {
+    case AUTH_ERROR:
+      DBG ("Internal error while looking for user tokens");
+      retval = PAM_AUTHINFO_UNAVAIL;
+      goto done;
+    case AUTH_NOT_FOUND:
+      /* User has associated tokens, so continue */
+      DBG ("Tokens found for user");
+      break;
+    case AUTH_NO_TOKENS:
+      DBG ("No tokens found for user");
+      if (cfg->nullok) {
+        retval = PAM_IGNORE;
+      } else {
+        retval = PAM_USER_UNKNOWN;
+      }
+      goto done;
+    default:
+      DBG ("Unhandled value while looking for user tokens");
+      retval = PAM_AUTHINFO_UNAVAIL;
+      goto done;
+    }
 
   if (password == NULL)
     {
@@ -1058,12 +1137,6 @@
   else
     password = NULL;
 
-  rc = ykclient_request (ykc, otp);
-
-  DBG ("ykclient return value (%d): %s", rc,
-       ykclient_strerror (rc));
-  DBG ("ykclient url used: %s", ykclient_get_last_url(ykc));
-
   /* authorize the user with supplied token id */
   if (cfg->ldapserver != NULL || cfg->ldap_uri != NULL)
     valid_token = authorize_user_token_ldap (cfg, user, otp_id);
@@ -1072,7 +1145,12 @@
 
   switch(valid_token)
     {
-    case 1:
+    case AUTH_FOUND:
+      DBG ("Token is associated to the user. Validating the OTP...");
+      rc = ykclient_request (ykc, otp);
+      DBG ("ykclient return value (%d): %s", rc, ykclient_strerror (rc));
+      DBG ("ykclient url used: %s", ykclient_get_last_url(ykc));
+
       switch (rc)
       {
         case YKCLIENT_OK:
@@ -1089,21 +1167,26 @@
           break;
       }
       break;
-    case 0:
-      DBG ("Internal error while validating user");
+    case AUTH_ERROR:
+      DBG ("Internal error while looking for user tokens");
       retval = PAM_AUTHINFO_UNAVAIL;
       break;
-    case -1:
+    case AUTH_NOT_FOUND:
       DBG ("Unauthorized token for this user");
       retval = PAM_AUTH_ERR;
       break;
-    case -2:
-      DBG ("Unknown user");
-      retval = PAM_USER_UNKNOWN;
+    case AUTH_NO_TOKENS:
+      DBG ("No tokens found for user");
+      if (cfg->nullok) {
+        retval = PAM_IGNORE;
+      } else {
+        retval = PAM_USER_UNKNOWN;
+      }
       break;
     default:
       DBG ("Unhandled value for token-user validation");
       retval = PAM_AUTHINFO_UNAVAIL;
+      break;
     }
 
 done:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pam_yubico-2.24/tests/aux/ldap.pl 
new/pam_yubico-2.25/tests/aux/ldap.pl
--- old/pam_yubico-2.24/tests/aux/ldap.pl       2015-04-07 09:06:39.000000000 
+0200
+++ new/pam_yubico-2.25/tests/aux/ldap.pl       2017-08-30 08:24:30.000000000 
+0200
@@ -45,6 +45,7 @@
 my %objects = (
   'base=uid=foo,ou=users,dc=example,dc=com' => {keys => ['vvincredible']},
   'base=uid=test,ou=users,dc=example,dc=com' => {keys => ['cccccccfhcbe', 
'ccccccbchvth']},
+  'base=uid=nokeys,ou=users,dc=example,dc=com' => {keys => []},
   'sub:base=:(uid=test)' => {keys => ['cccccccfhcbe', 'ccccccbchvth'], dn => 
'uid=test,out=users,dc=example,dc=com'},
 );
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pam_yubico-2.24/tests/pam_test.c 
new/pam_yubico-2.25/tests/pam_test.c
--- old/pam_yubico-2.24/tests/pam_test.c        2016-11-07 12:21:53.000000000 
+0100
+++ new/pam_yubico-2.25/tests/pam_test.c        2017-08-30 08:24:30.000000000 
+0200
@@ -66,6 +66,10 @@
   {"test", "ccccccbchvthlivuitriujjifivbvtrjkjfirllluurj"},
   {"foo", ""},
   {"bar", ""},
+  {"nokeys", ""},
+  {"foo", "testpasswordvvincredibletrerdegkkrkkneieultcjdghrejjbckh"},
+  {"foo", "testpassword"},
+  {"bar", "testpassword"},
 };
 
 
@@ -142,11 +146,14 @@
 };
 
 int pam_get_item(const pam_handle_t *pamh, int item_type, const void **item) {
-  fprintf(stderr, "in pam_get_item() %d\n", item_type);
-  if(item_type == 5) {
+  fprintf(stderr, "in pam_get_item() %d for %d\n", item_type, (int)pamh);
+  if(item_type == PAM_CONV) {
     pam_conversation.appdata_ptr = (void*)pamh;
     *item = &pam_conversation;
   }
+  if(item_type == PAM_AUTHTOK && pamh >= 8) {
+    *item = (void*)_data[(int)pamh].otp;
+  }
   return PAM_SUCCESS;
 }
 
@@ -246,6 +253,39 @@
   return pam_sm_authenticate(3, 0, sizeof(cfg) / sizeof(char*), cfg);
 }
 
+static int test_firstpass_authenticate(void) {
+  const char *cfg[] = {
+    "id=1",
+    
"urllist=http://localhost:"YKVAL_PORT2"/wsapi/2/verify;http://localhost:"YKVAL_PORT1"/wsapi/2/verify";,
+    "authfile="AUTHFILE,
+    "use_first_pass",
+    "debug"
+  };
+  return pam_sm_authenticate(8, 0, sizeof(cfg) / sizeof(char*), cfg);
+}
+
+static int test_firstpass_fail(void) {
+  const char *cfg[] = {
+    "id=1",
+    
"urllist=http://localhost:"YKVAL_PORT2"/wsapi/2/verify;http://localhost:"YKVAL_PORT1"/wsapi/2/verify";,
+    "authfile="AUTHFILE,
+    "use_first_pass",
+    "debug"
+  };
+  return pam_sm_authenticate(9, 0, sizeof(cfg) / sizeof(char*), cfg);
+}
+
+static int test_firstpass_fail2(void) {
+  const char *cfg[] = {
+    "id=1",
+    
"urllist=http://localhost:"YKVAL_PORT2"/wsapi/2/verify;http://localhost:"YKVAL_PORT1"/wsapi/2/verify";,
+    "authfile="AUTHFILE,
+    "use_first_pass",
+    "debug"
+  };
+  return pam_sm_authenticate(10, 0, sizeof(cfg) / sizeof(char*), cfg);
+}
+
 static int test_authenticate_ldap1(void) {
   return pam_sm_authenticate(0, 0, sizeof(ldap_cfg) / sizeof(char*), ldap_cfg);
 }
@@ -274,6 +314,10 @@
   return pam_sm_authenticate(6, 0, sizeof(ldap_cfg) / sizeof(char*), ldap_cfg);
 }
 
+static int test_authenticate_ldap6(void) {
+  return pam_sm_authenticate(7, 0, sizeof(ldap_cfg) / sizeof(char*), ldap_cfg);
+}
+
 static pid_t run_mock(const char *port, const char *type) {
   pid_t pid = fork();
   if(pid == 0) {
@@ -325,6 +369,18 @@
     ret = 8;
     goto out;
   }
+  if(test_firstpass_authenticate() != PAM_SUCCESS) {
+    ret = 9;
+    goto out;
+  }
+  if(test_firstpass_fail() != PAM_AUTH_ERR) {
+    ret = 10;
+    goto out;
+  }
+  if(test_firstpass_fail2() != PAM_USER_UNKNOWN) {
+    ret = 11;
+    goto out;
+  }
 #ifdef HAVE_LIBLDAP
   if(test_authenticate_ldap1() != PAM_SUCCESS) {
     ret = 1001;
@@ -354,6 +410,10 @@
     ret = 1007;
     goto out;
   }
+  if(test_authenticate_ldap6() != PAM_USER_UNKNOWN) {
+    ret = 1008;
+    goto out;
+  }
 #endif
 
 out:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pam_yubico-2.24/tests/util_test.c 
new/pam_yubico-2.25/tests/util_test.c
--- old/pam_yubico-2.24/tests/util_test.c       2016-06-16 13:16:37.000000000 
+0200
+++ new/pam_yubico-2.25/tests/util_test.c       2017-08-30 08:24:30.000000000 
+0200
@@ -35,6 +35,7 @@
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <fcntl.h>
+#include <unistd.h>
 
 #include <pwd.h>
 
@@ -75,27 +76,27 @@
   fclose(handle);
 
   ret = check_user_token(file, "foobar", "hhhvhvhdhbid", 1, stdout);
-  assert(ret == 1);
+  assert(ret == AUTH_FOUND);
   ret = check_user_token(file, "foobar", "hnhbhnhbhnhb", 1, stdout);
-  assert(ret == 1);
+  assert(ret == AUTH_FOUND);
   ret = check_user_token(file, "foobar", "hnhbhnhbhnhc", 1, stdout);
-  assert(ret == -1);
+  assert(ret == AUTH_NOT_FOUND);
   ret = check_user_token(file, "kaka", "hihbhdhrhbhj", 1, stdout);
-  assert(ret == 1);
+  assert(ret == AUTH_FOUND);
   ret = check_user_token(file, "bar", "hnhbhnhbhnhb", 1, stdout);
-  assert(ret == 1);
+  assert(ret == AUTH_FOUND);
   ret = check_user_token(file, "foo", "hdhrhbhjhvhu", 1, stdout);
-  assert(ret == -2);
+  assert(ret == AUTH_NO_TOKENS);
   ret = check_user_token(file, "foo2", "cccccccccccc", 1, stdout);
-  assert(ret == 1);
+  assert(ret == AUTH_FOUND);
   ret = check_user_token(file, "foo2", "vvvvvvvvvvvv", 1, stdout);
-  assert(ret == 1);
+  assert(ret == AUTH_FOUND);
   ret = check_user_token(file, "foo2", "vvvvvvvvvvcc", 1, stdout);
-  assert(ret == -1);
+  assert(ret == AUTH_NOT_FOUND);
   ret = check_user_token(file, "foo2", "", 1, stdout);
-  assert(ret == -1);
+  assert(ret == AUTH_NOT_FOUND);
   ret = check_user_token(file, "foo", "", 1, stdout);
-  assert(ret == -2);
+  assert(ret == AUTH_NO_TOKENS);
   remove(file);
 }
 
@@ -144,6 +145,122 @@
   fclose(file);
 }
 
+static void test_check_user_challenge_file(void) {
+  int ret;
+  char * tmpdir_path;
+  char * buf;
+  FILE * file;
+  struct passwd user;
+
+  buf = malloc(256);
+
+#define create_tmpdir_dir(path) \
+  strcpy(buf, tmpdir_path); \
+  strcat(buf, "/"); \
+  strcat(buf, path); \
+  mkdir(buf, 0755);
+
+#define remove_tmpdir_dir(path) \
+  strcpy(buf, tmpdir_path); \
+  strcat(buf, "/"); \
+  strcat(buf, path); \
+  rmdir(buf);
+
+#define create_tmpdir_file(path) \
+  strcpy(buf, tmpdir_path); \
+  strcat(buf, "/"); \
+  strcat(buf, path); \
+  file = fopen(buf, "w"); \
+  fclose(file);
+
+#define remove_tmpdir_file(path) \
+  strcpy(buf, tmpdir_path); \
+  strcat(buf, "/"); \
+  strcat(buf, path); \
+  unlink(buf);
+
+  /* create temporary directory */
+  tmpdir_path = tempnam(NULL, "pamtest");
+  assert(tmpdir_path != NULL);
+
+  ret = mkdir(tmpdir_path, 0755);
+  assert(ret == 0);
+
+  /* set user data */
+  user.pw_name = "tester";
+  user.pw_dir = tmpdir_path;
+
+  /* execute tests */
+  /* no asserts here as we have directory to remove */
+
+  int case_001_empty_chalresp_dir;
+  case_001_empty_chalresp_dir = check_user_challenge_file(tmpdir_path, &user, 
stdout);
+
+  int case_002_one_challenge_file;
+  create_tmpdir_file("tester");
+  case_002_one_challenge_file = check_user_challenge_file(tmpdir_path, &user, 
stdout);
+  remove_tmpdir_file("tester");
+
+  int case_003_multiple_challenge_files;
+  create_tmpdir_file("tester-001");
+  create_tmpdir_file("tester-002");
+  case_003_multiple_challenge_files = check_user_challenge_file(tmpdir_path, 
&user, stdout);
+  remove_tmpdir_file("tester-002");
+  remove_tmpdir_file("tester-001");
+
+  int case_004_other_users_files;
+  create_tmpdir_file("tester1");
+  create_tmpdir_file("tester1-001");
+  case_004_other_users_files = check_user_challenge_file(tmpdir_path, &user, 
stdout);
+  remove_tmpdir_file("tester1-001");
+  remove_tmpdir_file("tester1");
+
+  int case_005_no_chalresp_no_yubico;
+  case_005_no_chalresp_no_yubico = check_user_challenge_file(NULL, &user, 
stdout);
+
+  int case_006_no_chalresp_empty_yubico;
+  create_tmpdir_dir(".yubico");
+  case_006_no_chalresp_empty_yubico = check_user_challenge_file(NULL, &user, 
stdout);
+  remove_tmpdir_dir(".yubico");
+
+  int case_007_no_chalresp_one_challenge_file;
+  create_tmpdir_dir(".yubico");
+  create_tmpdir_file(".yubico/challenge");
+  case_007_no_chalresp_one_challenge_file = check_user_challenge_file(NULL, 
&user, stdout);
+  remove_tmpdir_file(".yubico/challenge");
+  remove_tmpdir_dir(".yubico");
+
+  int case_008_no_chalresp_multiple_challenge_files;
+  create_tmpdir_dir(".yubico");
+  create_tmpdir_file(".yubico/challenge-001");
+  create_tmpdir_file(".yubico/challenge-002");
+  case_008_no_chalresp_multiple_challenge_files = 
check_user_challenge_file(NULL, &user, stdout);
+  remove_tmpdir_file(".yubico/challenge-002");
+  remove_tmpdir_file(".yubico/challenge-001");
+  remove_tmpdir_dir(".yubico");
+
+  /* remove temporary directory */
+  ret = rmdir(tmpdir_path);
+  assert(ret == 0);
+  free(tmpdir_path);
+  free(buf);
+
+  /* check test results */
+  assert(case_001_empty_chalresp_dir == AUTH_NOT_FOUND);
+  assert(case_002_one_challenge_file == AUTH_FOUND);
+  assert(case_003_multiple_challenge_files == AUTH_FOUND);
+  assert(case_004_other_users_files == AUTH_NOT_FOUND);
+  assert(case_005_no_chalresp_no_yubico == AUTH_NOT_FOUND);
+  assert(case_006_no_chalresp_empty_yubico == AUTH_NOT_FOUND);
+  assert(case_007_no_chalresp_one_challenge_file == AUTH_FOUND);
+  assert(case_008_no_chalresp_multiple_challenge_files == AUTH_FOUND);
+
+#undef create_tmpdir_dir
+#undef remove_tmpdir_dir
+#undef create_tmpdir_file
+#undef remove_tmpdir_file
+}
+
 #endif /* HAVE_CR */
 
 static void test_filter_printf(void) {
@@ -171,6 +288,7 @@
   test_check_user_token();
 #if HAVE_CR
   test_load_chalresp_state();
+  test_check_user_challenge_file();
 #endif /* HAVE_CR */
   return 0;
 }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pam_yubico-2.24/util.c new/pam_yubico-2.25/util.c
--- old/pam_yubico-2.24/util.c  2016-06-16 13:16:37.000000000 +0200
+++ new/pam_yubico-2.25/util.c  2017-08-30 08:24:30.000000000 +0200
@@ -38,6 +38,7 @@
 #include <sys/stat.h>
 #include <errno.h>
 #include <fcntl.h>
+#include <glob.h>
 #include <unistd.h>
 
 #include "util.h"
@@ -85,8 +86,9 @@
 
 
 /*
- * This function will look for users name with valid user token id. It
- * will returns -2 if the user is unknown, -1 if the token do not match the 
user line, 0 for internal failure and 1 for success.
+ * This function will look for users name with valid user token id.
+ *
+ * Returns one of AUTH_FOUND, AUTH_NOT_FOUND, AUTH_NO_TOKENS, AUTH_ERROR.
  *
  * File format is as follows:
  * <user-name>:<token_id>:<token_id>
@@ -102,7 +104,7 @@
 {
   char buf[1024];
   char *s_user, *s_token;
-  int retval = 0;
+  int retval = AUTH_ERROR;
   int fd;
   struct stat st;
   FILE *opwfile;
@@ -136,7 +138,7 @@
       return retval;
   }
 
-  retval = -2;
+  retval = AUTH_NO_TOKENS;
   while (fgets (buf, 1024, opwfile))
     {
       char *saveptr = NULL;
@@ -155,17 +157,17 @@
        {
          if(verbose)
              D (debug_file, "Matched user: %s", s_user);
-      retval = -1; //We found at least one line for the user
+      retval = AUTH_NOT_FOUND; /* We found at least one line for the user */
          do
            {
              s_token = strtok_r (NULL, ":", &saveptr);
              if(verbose)
                  D (debug_file, "Authorization token: %s", s_token);
-             if (s_token && strcmp (otp_id, s_token) == 0)
+             if (s_token && otp_id && strcmp (otp_id, s_token) == 0)
                {
                  if(verbose)
                      D (debug_file, "Match user/token as %s/%s", username, 
otp_id);
-                 return 1;
+                 return AUTH_FOUND;
                }
            }
          while (s_token != NULL);
@@ -281,6 +283,71 @@
 }
 
 int
+check_user_challenge_file(const char *chalresp_path, const struct passwd 
*user, FILE *debug_file)
+{
+  /*
+   * This function will look for users challenge files.
+   *
+   * Returns one of AUTH_FOUND, AUTH_NOT_FOUND, AUTH_ERROR
+   */
+  size_t len;
+  int r;
+  int ret = AUTH_NOT_FOUND;
+  char *userfile = NULL;
+  char *userfile_pattern = NULL;
+  glob_t userfile_glob;
+  const char *filename = NULL;
+
+  if (! chalresp_path) {
+    filename = "challenge";
+  } else {
+    filename = user->pw_name;
+  }
+
+  /* check for userfile challenge files */
+  r = get_user_cfgfile_path(chalresp_path, filename, user, &userfile);
+  if (!r) {
+    D (debug_file, "Failed to get user cfgfile path");
+    ret = AUTH_ERROR;
+    goto out;
+  }
+
+  if (!access(userfile, F_OK)) {
+    ret = AUTH_FOUND;
+    goto out;
+  }
+
+  /* check for userfile-* challenge files */
+  len = strlen(userfile) + 2 + 1;
+  if ((userfile_pattern = malloc(len)) == NULL) {
+    D (debug_file, "Failed to allocate memory for userfile pattern: %s", 
strerror(errno));
+    ret = AUTH_ERROR;
+    goto out;
+  }
+  snprintf(userfile_pattern, len, "%s-*", userfile);
+
+  r = glob(userfile_pattern, 0, NULL, &userfile_glob);
+  globfree(&userfile_glob);
+  switch (r) {
+    case GLOB_NOMATCH:
+      /* No matches found, so continue */
+      break;
+    case 0:
+      ret = AUTH_FOUND;
+      goto out;
+    default:
+      D (debug_file, "Error while checking for %s challenge files: %s", 
userfile_pattern, strerror(errno));
+      ret = AUTH_ERROR;
+      goto out;
+  }
+
+out:
+  free(userfile_pattern);
+  free(userfile);
+  return ret;
+}
+
+int
 get_user_challenge_file(YK_KEY *yk, const char *chalresp_path, const struct 
passwd *user, char **fn, FILE *debug_file)
 {
   /* Getting file from user home directory, i.e. ~/.yubico/challenge, or
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pam_yubico-2.24/util.h new/pam_yubico-2.25/util.h
--- old/pam_yubico-2.24/util.h  2016-11-07 12:20:54.000000000 +0100
+++ new/pam_yubico-2.25/util.h  2017-08-30 08:24:30.000000000 +0200
@@ -44,6 +44,12 @@
   fprintf (file, "\n");                                                        
\
 } while (0)
 
+/* Return values for authorize_user_token and authorize_user_token_ldap */
+#define AUTH_NO_TOKENS -2 /* The user has no associated tokens */
+#define AUTH_ERROR      0 /* Internal error when looking up associated tokens 
*/
+#define AUTH_FOUND      1 /* The requested token is associated to the user */
+#define AUTH_NOT_FOUND -1 /* The requested token is not associated to the user 
*/
+
 int get_user_cfgfile_path(const char *common_path, const char *filename, const 
struct passwd *user, char **fn);
 int check_user_token(const char *authfile, const char *username, const char 
*otp_id, int verbose, FILE *debug_file);
 
@@ -74,6 +80,7 @@
 
 int generate_random(void *buf, int len);
 
+int check_user_challenge_file(const char *chalresp_path, const struct passwd 
*user, FILE *debug_file);
 int get_user_challenge_file(YK_KEY *yk, const char *chalresp_path, const 
struct passwd *user, char **fn, FILE *debug_file);
 
 int load_chalresp_state(FILE *f, CR_STATE *state, bool verbose, FILE 
*debug_file);
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pam_yubico-2.24/ykpamcfg.1 
new/pam_yubico-2.25/ykpamcfg.1
--- old/pam_yubico-2.24/ykpamcfg.1      2016-05-23 09:44:17.000000000 +0200
+++ new/pam_yubico-2.25/ykpamcfg.1      2017-08-30 08:31:54.000000000 +0200
@@ -1,13 +1,13 @@
 '\" t
 .\"     Title: ykpamcfg
 .\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
-.\" Generator: DocBook XSL Stylesheets v1.78.1 <http://docbook.sf.net/>
-.\"      Date: Version 2.22
+.\" Generator: DocBook XSL Stylesheets v1.79.1 <http://docbook.sf.net/>
+.\"      Date: Version 2.25
 .\"    Manual: Yubico PAM Module Manual
 .\"    Source: yubico-pam
 .\"  Language: English
 .\"
-.TH "YKPAMCFG" "1" "Version 2\&.22" "yubico\-pam" "Yubico PAM Module Manual"
+.TH "YKPAMCFG" "1" "Version 2\&.25" "yubico\-pam" "Yubico PAM Module Manual"
 .\" -----------------------------------------------------------------
 .\" * Define some portability stuff
 .\" -----------------------------------------------------------------
@@ -51,7 +51,7 @@
 .PP
 \fB\-p\fR \fIpath\fR
 .RS 4
-specify output file for, default is ~/\&.yubico/challenge
+specify output file, default is ~/\&.yubico/challenge
 .RE
 .PP
 \fB\-i\fR \fIiterations\fR
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pam_yubico-2.24/ykpamcfg.1.txt 
new/pam_yubico-2.25/ykpamcfg.1.txt
--- old/pam_yubico-2.24/ykpamcfg.1.txt  2016-02-18 13:11:10.000000000 +0100
+++ new/pam_yubico-2.25/ykpamcfg.1.txt  2017-08-30 08:24:30.000000000 +0200
@@ -21,7 +21,7 @@
 choose action to perform. See ACTIONS below.
 
 *-p* _path_::
-specify output file for, default is ~/.yubico/challenge
+specify output file, default is ~/.yubico/challenge
 
 *-i* _iterations_::
 number of iterations to use for pbkdf2 of expected response


Reply via email to