Hello community,

here is the log from the commit of package pam_passwdqc for openSUSE:Factory 
checked in at 2015-05-29 10:47:21
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/pam_passwdqc (Old)
 and      /work/SRC/openSUSE:Factory/.pam_passwdqc.new (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "pam_passwdqc"

Changes:
--------
--- /work/SRC/openSUSE:Factory/pam_passwdqc/pam_passwdqc.changes        
2012-03-07 13:43:54.000000000 +0100
+++ /work/SRC/openSUSE:Factory/.pam_passwdqc.new/pam_passwdqc.changes   
2015-05-29 10:47:22.000000000 +0200
@@ -1,0 +2,26 @@
+Sun Mar  8 23:28:24 UTC 2015 - [email protected]
+
+- Update to version 1.3.0
+  * Detection of common character sequences has been improved.  This has
+    reduced the number of passing passwords for RockYou top 100k from
+    35 to 18, and for RockYou top 1M from 2333 to 2273 (all of these are
+    with passwdqc's default policy).  I also tested on lists of cracked and
+    not cracked passwords and reviewed the results manually to ensure
+    there's no significant increase in false positives.
+  * Generation of random passphrases with non-default settings has been
+    improved: case toggling has been made optional, possible use of trailing
+    single characters has been added, words are now separated with dashes
+    when different separator characters are not in use, and the range of
+    possible bit sizes of generated passphrases has been expanded (now it is
+    24 to 85 bits for the programs, and 24 to 136 bits for the API).
+    The code has been made more robust: possible NULL pointer returns from
+    crypt(3) are handled correctly, all pre-initialized arrays and structs
+    are declared as "const", greater use of cpp macros for integer constants
+    and some source code comments were added (mostly in passwdqc_random.c).
+  * Darwin (Mac OS X) support has been added to the Makefile
+  * pwqcheck.php, a PHP wrapper function around the pwqcheck program, has
+    been added.
+- Use download Url as source
+- Remove redundant %clean section
+
+-------------------------------------------------------------------

Old:
----
  passwdqc-1.2.2.tar.gz

New:
----
  passwdqc-1.3.0.tar.gz

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

Other differences:
------------------
++++++ pam_passwdqc.spec ++++++
--- /var/tmp/diff_new_pack.LlyEiv/_old  2015-05-29 10:47:22.000000000 +0200
+++ /var/tmp/diff_new_pack.LlyEiv/_new  2015-05-29 10:47:22.000000000 +0200
@@ -1,7 +1,7 @@
 #
 # spec file for package pam_passwdqc
 #
-# Copyright (c) 2012 SUSE LINUX Products GmbH, Nuernberg, Germany.
+# Copyright (c) 2015 SUSE LINUX Products GmbH, Nuernberg, Germany.
 #
 # All modifications and additions to the file contributed by third parties
 # remain the property of their copyright owners, unless otherwise agreed
@@ -22,12 +22,12 @@
 Requires:       pam
 Recommends:     passwdqc
 Provides:       pam-modules:/%_lib/security/pam_passwdqc.so
-Version:        1.2.2
+Version:        1.3.0
 Release:        0
 Summary:        Simple Password Strength Checking Module
 License:        BSD-3-Clause
 Group:          System/Libraries
-Source0:        passwdqc-%{version}.tar.gz
+Source0:        www.openwall.com/passwdqc/passwdqc-%{version}.tar.gz
 Source1:        baselibs.conf
 Source50:       dlopen.sh
 BuildRoot:      %{_tmppath}/%{name}-%{version}-build
@@ -102,9 +102,6 @@
 
 %postun -n libpasswdqc0 -p /sbin/ldconfig
 
-%clean
-rm -rf $RPM_BUILD_ROOT
-
 %files 
 %defattr(-,root,root,755)
 %attr(755,root,root) /%{_lib}/security/pam_*.so

++++++ passwdqc-1.2.2.tar.gz -> passwdqc-1.3.0.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/passwdqc-1.2.2/INSTALL new/passwdqc-1.3.0/INSTALL
--- old/passwdqc-1.2.2/INSTALL  2010-06-23 00:41:31.000000000 +0200
+++ new/passwdqc-1.3.0/INSTALL  2013-04-24 04:02:54.000000000 +0200
@@ -26,8 +26,8 @@
 Alternatively, on a Red Hat'ish Linux system and under an account
 configured to build RPM packages (perhaps with ~/.rpmmacros specifying
 the proper pathnames for %_topdir, %_tmppath, and %buildroot), you may
-build RPM packages by running "rpmbuild -tb passwdqc-1.2.2.tar.gz", then
-install the two binary subpackages with "rpm -Uvh passwdqc*-1.2.2*.rpm".
+build RPM packages by running "rpmbuild -tb passwdqc-1.3.0.tar.gz", then
+install the two binary subpackages with "rpm -Uvh passwdqc*-1.3.0*.rpm".
 This works due to the RPM spec file included in the tarball.
 
 Please refer to README and PLATFORMS for information on configuring your
@@ -37,4 +37,4 @@
 Please refer to the pwqcheck(1) and pwqgen(1) manual pages for
 information on using the command-line programs.
 
-$Owl: Owl/packages/passwdqc/passwdqc/INSTALL,v 1.5 2010/06/22 22:41:31 solar 
Exp $
+$Owl: Owl/packages/passwdqc/passwdqc/INSTALL,v 1.8 2013/04/24 02:02:54 solar 
Exp $
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/passwdqc-1.2.2/LICENSE new/passwdqc-1.3.0/LICENSE
--- old/passwdqc-1.2.2/LICENSE  2009-10-21 21:38:39.000000000 +0200
+++ new/passwdqc-1.3.0/LICENSE  2013-04-24 04:01:43.000000000 +0200
@@ -1,9 +1,9 @@
 Two manual pages (pam_passwdqc.8 and passwdqc.conf.5) are under the
 3-clause BSD-style license as specified within the files themselves.
 
-concat.c, wordset_4k.c, wordset_4k.h, and pam_macros.h are in the public
-domain, but at your option they may also be used under this package's
-license below.
+concat.c, wordset_4k.c, wordset_4k.h, pam_macros.h, and pwqcheck.php
+are in the public domain, but at your option they may also be used under
+this package's license below.
 
 The rest of the files in this package fall under the following terms
 (heavily cut-down "BSD license"):
@@ -23,4 +23,4 @@
 OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 SUCH DAMAGE.
 
-$Owl: Owl/packages/passwdqc/passwdqc/LICENSE,v 1.7 2009/10/21 19:38:39 solar 
Exp $
+$Owl: Owl/packages/passwdqc/passwdqc/LICENSE,v 1.8 2013/04/24 02:01:43 solar 
Exp $
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/passwdqc-1.2.2/Makefile new/passwdqc-1.3.0/Makefile
--- old/passwdqc-1.2.2/Makefile 2010-06-23 00:36:03.000000000 +0200
+++ new/passwdqc-1.3.0/Makefile 2012-08-19 00:24:09.000000000 +0200
@@ -7,6 +7,8 @@
 TITLE = pam_passwdqc
 SHARED_LIB = libpasswdqc.so.0
 DEVEL_LIB = libpasswdqc.so
+SHARED_LIB_DARWIN = libpasswdqc.0.dylib
+DEVEL_LIB_DARWIN = libpasswdqc.dylib
 MAP_LIB = libpasswdqc.map
 PAM_SO_SUFFIX =
 SHARED_PAM = $(TITLE).so$(PAM_SO_SUFFIX)
@@ -28,6 +30,7 @@
 DEVEL_LIBDIR = /usr/lib
 SECUREDIR = /lib/security
 SECUREDIR_SUN = /usr/lib/security
+SECUREDIR_DARWIN = /usr/lib/pam
 INCLUDEDIR = /usr/include
 MANDIR = /usr/share/man
 DESTDIR =
@@ -66,6 +69,7 @@
 LDLIBS_pam_LINUX = -lpam -lcrypt
 LDLIBS_pam_SUN = -lpam -lcrypt
 LDLIBS_pam_HP = -lpam -lsec
+LDLIBS_pam_DARWIN = -lpam -lSystem
 
 # Uncomment this to use cc instead of gcc
 #CC = cc
@@ -110,6 +114,12 @@
                        LDFLAGS_pam="$(LDFLAGS_pam_HP)" \
                        LDLIBS_pam="$(LDLIBS_pam_HP)" \
                        $@_wrapped;; \
+       Darwin) $(MAKE) \
+                       SHARED_LIB="$(SHARED_LIB_DARWIN)" \
+                       DEVEL_LIB="$(DEVEL_LIB_DARWIN)" \
+                       SECUREDIR="$(SECUREDIR_DARWIN)" \
+                       LDLIBS_pam="$(LDLIBS_pam_DARWIN)" \
+                       $@_wrapped;; \
        *)      $(MAKE) $@_wrapped;; \
        esac
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/passwdqc-1.2.2/README new/passwdqc-1.3.0/README
--- old/passwdqc-1.2.2/README   2010-03-13 22:29:15.000000000 +0100
+++ new/passwdqc-1.3.0/README   2013-04-23 16:14:07.000000000 +0200
@@ -97,7 +97,7 @@
 
        random=N[,only]                 [random=47]
 
-The size of randomly-generated passphrases in bits (26 to 81), or 0 to
+The size of randomly-generated passphrases in bits (24 to 85), or 0 to
 disable this feature.  Any passphrase that contains the offered
 randomly-generated string will be allowed regardless of other possible
 restrictions.
@@ -152,4 +152,4 @@
 -- 
 Solar Designer <solar at openwall.com>
 
-$Owl: Owl/packages/passwdqc/passwdqc/README,v 1.15 2010/03/13 21:29:15 solar 
Exp $
+$Owl: Owl/packages/passwdqc/passwdqc/README,v 1.16 2013/04/23 14:14:07 solar 
Exp $
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/passwdqc-1.2.2/pam_passwdqc.c 
new/passwdqc-1.3.0/pam_passwdqc.c
--- old/passwdqc-1.2.2/pam_passwdqc.c   2010-06-22 21:39:27.000000000 +0200
+++ new/passwdqc-1.3.0/pam_passwdqc.c   2012-08-19 00:24:09.000000000 +0200
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2000-2003,2005 by Solar Designer.  See LICENSE.
+ * Copyright (c) 2000-2003,2005,2012 by Solar Designer.  See LICENSE.
  */
 
 #ifdef __FreeBSD__
@@ -186,32 +186,39 @@
 
 static int check_pass(struct passwd *pw, const char *pass)
 {
-#ifdef HAVE_SHADOW
-       struct spwd *spw;
        const char *hash;
        int retval;
 
+#ifdef HAVE_SHADOW
 #ifdef __hpux
        if (iscomsec()) {
 #else
        if (!strcmp(pw->pw_passwd, "x")) {
 #endif
-               spw = getspnam(pw->pw_name);
+               struct spwd *spw = getspnam(pw->pw_name);
                endspent();
                if (!spw)
                        return -1;
+               hash = NULL;
+               if (strlen(spw->sp_pwdp) >= 13) {
 #ifdef __hpux
-               hash = bigcrypt(pass, spw->sp_pwdp);
+                       hash = bigcrypt(pass, spw->sp_pwdp);
 #else
-               hash = crypt(pass, spw->sp_pwdp);
+                       hash = crypt(pass, spw->sp_pwdp);
 #endif
-               retval = strcmp(hash, spw->sp_pwdp) ? -1 : 0;
+               }
+               retval = (hash && !strcmp(hash, spw->sp_pwdp)) ? 0 : -1;
                memset(spw->sp_pwdp, 0, strlen(spw->sp_pwdp));
                return retval;
        }
 #endif
 
-       return strcmp(crypt(pass, pw->pw_passwd), pw->pw_passwd) ? -1 : 0;
+       hash = NULL;
+       if (strlen(pw->pw_passwd) >= 13)
+               hash = crypt(pass, pw->pw_passwd);
+       retval = (hash && !strcmp(hash, pw->pw_passwd)) ? 0 : -1;
+       memset(pw->pw_passwd, 0, strlen(pw->pw_passwd));
+       return retval;
 }
 
 static int am_root(pam_handle_t *pamh)
@@ -299,6 +306,10 @@
                pw->pw_name = (char *)user;
                pw->pw_gecos = "";
        } else {
+/* As currently implemented, we don't avoid timing leaks for valid vs. not
+ * usernames and hashes.  Normally, the username would have already been
+ * checked and determined valid, and the check_oldauthtok option is only needed
+ * on systems that happen to have similar timing leaks all over the place. */
                pw = getpwnam(user);
                endpwent();
                if (!pw)
@@ -479,7 +490,7 @@
 #ifdef PAM_MODULE_ENTRY
 PAM_MODULE_ENTRY("pam_passwdqc");
 #elif defined(PAM_STATIC)
-struct pam_module _pam_passwdqc_modstruct = {
+const struct pam_module _pam_passwdqc_modstruct = {
        "pam_passwdqc",
        NULL,
        NULL,
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/passwdqc-1.2.2/passwdqc.conf.5 
new/passwdqc-1.3.0/passwdqc.conf.5
--- old/passwdqc-1.2.2/passwdqc.conf.5  2010-03-13 22:29:15.000000000 +0100
+++ new/passwdqc-1.3.0/passwdqc.conf.5  2013-04-23 16:14:07.000000000 +0200
@@ -35,7 +35,7 @@
 .\" SUCH DAMAGE.
 .\"
 .\" $FreeBSD: src/lib/libpam/modules/pam_passwdqc/pam_passwdqc.8,v 1.4 
2002/05/30 14:49:57 ru Exp $
-.\" $Owl: Owl/packages/passwdqc/passwdqc/passwdqc.conf.5,v 1.10 2010/03/13 
21:29:15 solar Exp $
+.\" $Owl: Owl/packages/passwdqc/passwdqc/passwdqc.conf.5,v 1.11 2013/04/23 
14:14:07 solar Exp $
 .\"
 .Dd March 13, 2010
 .Dt PASSWDQC.CONF 5
@@ -177,7 +177,7 @@
 .Sm on
 .Xc
 .Pq default: Cm random Ns = Ns 47
-The size of randomly-generated passphrases in bits (26 to 81),
+The size of randomly-generated passphrases in bits (24 to 85),
 or 0 to disable this feature.
 Any passphrase that contains the offered randomly-generated string will be
 allowed regardless of other possible restrictions.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/passwdqc-1.2.2/passwdqc.h 
new/passwdqc-1.3.0/passwdqc.h
--- old/passwdqc-1.2.2/passwdqc.h       2010-06-18 22:06:01.000000000 +0200
+++ new/passwdqc-1.3.0/passwdqc.h       2013-04-24 03:45:10.000000000 +0200
@@ -49,6 +49,6 @@
 #define F_USE_FIRST_PASS               0x00000100
 #define F_USE_AUTHTOK                  0x00000200
 
-#define PASSWDQC_VERSION               "1.2.2"
+#define PASSWDQC_VERSION               "1.3.0"
 
 #endif /* PASSWDQC_H__ */
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/passwdqc-1.2.2/passwdqc.spec 
new/passwdqc-1.3.0/passwdqc.spec
--- old/passwdqc-1.2.2/passwdqc.spec    2010-06-23 01:10:33.000000000 +0200
+++ new/passwdqc-1.3.0/passwdqc.spec    2013-04-24 04:02:54.000000000 +0200
@@ -1,8 +1,8 @@
-# $Owl: Owl/packages/passwdqc/passwdqc/passwdqc.spec,v 1.58 2010/06/22 
23:10:33 solar Exp $
+# $Owl: Owl/packages/passwdqc/passwdqc/passwdqc.spec,v 1.63 2013/04/24 
02:02:54 solar Exp $
 
 Summary: A password/passphrase strength checking and policy enforcement 
toolset.
 Name: passwdqc
-Version: 1.2.2
+Version: 1.3.0
 Release: owl1
 License: BSD-compatible
 Group: System Environment/Base
@@ -60,7 +60,7 @@
 
 %files
 %defattr(-,root,root)
-%doc LICENSE README
+%doc LICENSE README pwqcheck.php
 %config(noreplace) /etc/passwdqc.conf
 /%_lib/lib*.so*
 %_bindir/*
@@ -73,6 +73,35 @@
 %_libdir/lib*.so
 
 %changelog
+* Wed Apr 24 2013 Solar Designer <solar-at-owl.openwall.com> 1.3.0-owl1
+- When checking is_simple() after discounting a common character sequence,
+apply the (negative) bias even for the passphrase length check.  Previously,
+we were not doing this because passphrases are normally built from words, and
+the same code was being used for the check for dictionary words.
+- Expanded the list of common character sequences.  Along with the change
+above, this reduces the number of passing passwords for RockYou top 100k from
+35 to 18, and for RockYou top 1M from 2333 to 2273 (all of these are with
+passwdqc's default policy).
+- Moved the common character sequences check to be made after the dictionary
+words check, to avoid introducing more cases of misreporting.
+- Added pwqcheck.php, a PHP wrapper function around the pwqcheck program.
+
+* Tue Apr 23 2013 Solar Designer <solar-at-owl.openwall.com> 1.2.4-owl1
+- In randomly generated passphrases: toggle case of the first character of each
+word only if we wouldn't achieve sufficient entropy otherwise, use a trailing
+separator if we achieve sufficient entropy even with the final word omitted
+(in fact, we now enable the use of different separators in more cases for this
+reason), use dashes rather than spaces to separate words when different
+separator characters are not in use.
+- Expanded the allowed size of randomly-generated passphrases in bits (now it's
+24 to 85 in the tools, and 24 to 136 in the passwdqc_random() interface).
+
+* Wed Aug 15 2012 Solar Designer <solar-at-owl.openwall.com> 1.2.3-owl1
+- Handle possible NULL returns from crypt().
+- Declared all pre-initialized arrays and structs as const.
+- Added Darwin (Mac OS X) support to the Makefile, loosely based on a patch by
+Ronald Ip (thanks!)
+
 * Tue Jun 22 2010 Solar Designer <solar-at-owl.openwall.com> 1.2.2-owl1
 - Introduced the GNU'ish "uninstall" make target name (a synonym for "remove").
 - Makefile updates to make the "install" and "uninstall" targets with their
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/passwdqc-1.2.2/passwdqc_check.c 
new/passwdqc-1.3.0/passwdqc_check.c
--- old/passwdqc-1.2.2/passwdqc_check.c 2010-03-27 20:13:15.000000000 +0100
+++ new/passwdqc-1.3.0/passwdqc_check.c 2013-04-24 03:16:03.000000000 +0200
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2000-2002,2010 by Solar Designer.  See LICENSE.
+ * Copyright (c) 2000-2002,2010,2013 by Solar Designer.  See LICENSE.
  */
 
 #include <stdio.h>
@@ -66,14 +66,15 @@
  * contain enough different characters for its class, or doesn't contain
  * enough words for a passphrase.
  *
- * The bias may be positive or negative.  It is added to the length,
- * except that a negative bias is not considered in the passphrase
- * length check because a passphrase is expected to contain words.
- * The bias does not apply to the number of different characters; the
- * actual number is used in all checks.
+ * The biases are added to the length, and they may be positive or negative.
+ * The passphrase length check uses passphrase_bias instead of bias so that
+ * zero may be passed for this parameter when the (other) bias is non-zero
+ * because of a dictionary word, which is perfectly normal for a passphrase.
+ * The biases do not affect the number of different characters, character
+ * classes, and word count.
  */
 static int is_simple(const passwdqc_params_qc_t *params, const char *newpass,
-    int bias)
+    int bias, int passphrase_bias)
 {
        int length, classes, words, chars;
        int digits, lowers, uppers, others, unknowns;
@@ -155,7 +156,7 @@
                if (!params->passphrase_words ||
                    words < params->passphrase_words)
                        continue;
-               if (length + (bias > 0 ? bias : 0) >= params->min[2] &&
+               if (length + passphrase_bias >= params->min[2] &&
                    chars >= expected_different(27, params->min[2]) - 1)
                        return 0;
                continue;
@@ -291,7 +292,7 @@
                                }
                                /* add credit for match_length - 1 chars */
                                bias = params->match_length - 1;
-                               if (is_simple(params, scratch, bias)) {
+                               if (is_simple(params, scratch, bias, bias)) {
                                        clean(scratch);
                                        return 1;
                                }
@@ -319,7 +320,8 @@
                                bias += (int)params->match_length - j;
                                /* bias <= -1 */
                                if (bias < worst_bias) {
-                                       if (is_simple(params, original, bias))
+                                       if (is_simple(params, original, bias,
+                                           (mode & 0xff) == 1 ? 0 : bias))
                                                return 1;
                                        worst_bias = bias;
                                }
@@ -342,23 +344,34 @@
 
 /*
  * Common sequences of characters.
- * We don't need to list any of the characters in reverse order because the
+ * We don't need to list any of the entire strings in reverse order because the
  * code checks the new password in both "unified" and "unified and reversed"
- * form against these strings (unifying them first indeed).  We also don't
- * have to include common repeats of characters (e.g., "777", "!!!", "1000")
- * because these are often taken care of by the requirement on the number of
- * different characters.
+ * form against these strings (unifying them first indeed).  We also don't have
+ * to include common repeats of characters (e.g., "777", "!!!", "1000") because
+ * these are often taken care of by the requirement on the number of different
+ * characters.
  */
-const char *seq[] = {
+const char * const seq[] = {
        "0123456789",
        "`1234567890-=",
        "~!@#$%^&*()_+",
        "abcdefghijklmnopqrstuvwxyz",
+       "a1b2c3d4e5f6g7h8i9j0",
+       "1a2b3c4d5e6f7g8h9i0j",
+       "abc123",
        "qwertyuiop[]\\asdfghjkl;'zxcvbnm,./",
        "qwertyuiop{}|asdfghjkl:\"zxcvbnm<>?",
        "qwertyuiopasdfghjklzxcvbnm",
        "1qaz2wsx3edc4rfv5tgb6yhn7ujm8ik,9ol.0p;/-['=]\\",
-       "qazwsxedcrfvtgbyhnujmikolp"
+       "!qaz@wsx#edc$rfv%tgb^yhn&ujm*ik<(ol>)p:?_{\"+}|",
+       "qazwsxedcrfvtgbyhnujmikolp",
+       "1q2w3e4r5t6y7u8i9o0p-[=]",
+       "q1w2e3r4t5y6u7i8o9p0[-]=\\",
+       "1qaz1qaz",
+       "1qaz!qaz", /* can't unify '1' and '!' - see comment in unify() */
+       "1qazzaq1",
+       "zaq!1qaz",
+       "zaq!2wsx"
 };
 
 /*
@@ -376,7 +389,7 @@
 static const char *is_word_based(const passwdqc_params_qc_t *params,
     const char *needle, const char *original, int is_reversed)
 {
-       char word[7];
+       char word[WORDSET_4K_LENGTH_MAX + 1];
        char *unified;
        unsigned int i;
        int length;
@@ -385,22 +398,10 @@
        if (!params->match_length)      /* disabled */
                return NULL;
 
-       mode = is_reversed | 2;
-       for (i = 0; i < sizeof(seq) / sizeof(seq[0]); i++) {
-               unified = unify(NULL, seq[i]);
-               if (!unified)
-                       return REASON_ERROR;
-               if (is_based(params, unified, needle, original, mode)) {
-                       free(unified);
-                       return REASON_SEQ;
-               }
-               free(unified);
-       }
-
        mode = is_reversed | 1;
-       word[6] = '\0';
+       word[WORDSET_4K_LENGTH_MAX] = '\0';
        for (i = 0; i < 0x1000; i++) {
-               memcpy(word, _passwdqc_wordset_4k[i], 6);
+               memcpy(word, _passwdqc_wordset_4k[i], WORDSET_4K_LENGTH_MAX);
                length = strlen(word);
                if (length < params->match_length)
                        continue;
@@ -413,6 +414,17 @@
        }
 
        mode = is_reversed | 2;
+       for (i = 0; i < sizeof(seq) / sizeof(seq[0]); i++) {
+               unified = unify(NULL, seq[i]);
+               if (!unified)
+                       return REASON_ERROR;
+               if (is_based(params, unified, needle, original, mode)) {
+                       free(unified);
+                       return REASON_SEQ;
+               }
+               free(unified);
+       }
+
        if (params->match_length <= 4)
        for (i = 1900; i <= 2039; i++) {
                sprintf(word, "%u", i);
@@ -466,7 +478,7 @@
                }
        }
 
-       if (is_simple(params, newpass, 0)) {
+       if (is_simple(params, newpass, 0, 0)) {
                reason = REASON_SIMPLE;
                if (length < params->min[1] && params->min[1] <= params->max)
                        reason = REASON_SIMPLESHORT;
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/passwdqc-1.2.2/passwdqc_parse.c 
new/passwdqc-1.3.0/passwdqc_parse.c
--- old/passwdqc-1.2.2/passwdqc_parse.c 2010-03-13 22:23:08.000000000 +0100
+++ new/passwdqc-1.3.0/passwdqc_parse.c 2013-04-23 15:52:53.000000000 +0200
@@ -78,7 +78,7 @@
                        e += 5;
                        params->qc.min[4] = INT_MAX;
                }
-               if (*e || (v && v < 26) || v > 81)
+               if (*e || (v && v < 24) || v > 85)
                        goto parse_error;
                params->qc.random_bits = v;
        } else if ((p = skip_prefix(option, "enforce="))) {
@@ -152,7 +152,7 @@
        return 0;
 }
 
-static passwdqc_params_t defaults = {
+static const passwdqc_params_t defaults = {
        {
                {INT_MAX, 24, 11, 8, 7},        /* min */
                40,                             /* max */
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/passwdqc-1.2.2/passwdqc_random.c 
new/passwdqc-1.3.0/passwdqc_random.c
--- old/passwdqc-1.2.2/passwdqc_random.c        2010-03-13 22:04:51.000000000 
+0100
+++ new/passwdqc-1.3.0/passwdqc_random.c        2013-04-23 16:00:38.000000000 
+0200
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2000-2002,2005,2008,2010 by Solar Designer.  See LICENSE.
+ * Copyright (c) 2000-2002,2005,2008,2010,2013 by Solar Designer.  See LICENSE.
  */
 
 #include <stdio.h>
@@ -25,6 +25,37 @@
  */
 #define SEPARATORS                     "-_!$&*+=23456789"
 
+/*
+ * Number of bits encoded per separator character.
+ */
+#define SEPARATOR_BITS                 4
+
+/*
+ * Number of bits encoded per word.  We use 4096 words, which gives 12 bits,
+ * and we toggle the case of the first character, which gives one bit more.
+ */
+#define WORD_BITS                      13
+
+/*
+ * Number of bits encoded per separator and word.
+ */
+#define SWORD_BITS \
+       (SEPARATOR_BITS + WORD_BITS)
+
+/*
+ * Maximum number of words to use.
+ */
+#define WORDS_MAX                      8
+
+/*
+ * Minimum and maximum number of bits to encode.  With the settings above,
+ * these are 24 and 136, respectively.
+ */
+#define BITS_MIN \
+       (2 * (WORD_BITS - 1))
+#define BITS_MAX \
+       (WORDS_MAX * SWORD_BITS)
+
 static int read_loop(int fd, unsigned char *buffer, int count)
 {
        int offset, block;
@@ -52,62 +83,131 @@
 {
        char output[0x100], *retval;
        int bits;
-       int use_separators, count, i;
-       unsigned int length, extra;
-       char *start, *end;
+       int word_count, trailing_separator, use_separators, toggle_case;
+       int i;
+       unsigned int max_length, length, extra;
+       const char *start, *end;
        int fd;
        unsigned char bytes[3];
 
        bits = params->random_bits;
-       if (bits < 26 || bits > 132)
+       if (bits < BITS_MIN || bits > BITS_MAX)
                return NULL;
 
-       count = 1 + (bits + (16 - 13)) / 17;
-       use_separators = ((bits + 12) / 13 != count);
+/*
+ * Calculate the number of words to use.  The first word is always present
+ * (hence the "1 +" and the "- WORD_BITS").  Each one of the following words,
+ * if any, is prefixed by a separator character, so we use SWORD_BITS when
+ * calculating how many additional words to use.  We divide "bits - WORD_BITS"
+ * by SWORD_BITS with rounding up (hence the addition of "SWORD_BITS - 1").
+ */
+       word_count = 1 + (bits + (SWORD_BITS - 1 - WORD_BITS)) / SWORD_BITS;
+
+/*
+ * Special case: would we still encode enough bits if we omit the final word,
+ * but keep the would-be-trailing separator?
+ */
+       trailing_separator = (SWORD_BITS * (word_count - 1) >= bits);
+       word_count -= trailing_separator;
+
+/*
+ * To determine whether we need to use different separator characters or maybe
+ * not, calculate the number of words we'd need to use if we don't use
+ * different separators.  We calculate it by dividing "bits" by WORD_BITS with
+ * rounding up (hence the addition of "WORD_BITS - 1").  The resulting number
+ * is either the same as or greater than word_count.  Use different separators
+ * only if their use, in the word_count calculation above, has helped reduce
+ * word_count.
+ */
+       use_separators = ((bits + (WORD_BITS - 1)) / WORD_BITS != word_count);
+       trailing_separator &= use_separators;
+
+/*
+ * Toggle case of the first character of each word only if we wouldn't achieve
+ * sufficient entropy otherwise.
+ */
+       toggle_case = (bits >
+           ((WORD_BITS - 1) * word_count) +
+           (use_separators ?
+           (SEPARATOR_BITS * (word_count - !trailing_separator)) : 0));
 
-       length = count * 7 - 1;
-       if (length >= sizeof(output) || (int)length > params->max)
+/*
+ * Calculate and check the maximum possible length of a "passphrase" we may
+ * generate for a given word_count.  We add 1 to WORDSET_4K_LENGTH_MAX to
+ * account for separators (whether different or not).  When there's no
+ * trailing separator, we subtract 1.  The check against sizeof(output) uses
+ * ">=" to account for NUL termination.
+ */
+       max_length = word_count * (WORDSET_4K_LENGTH_MAX + 1) -
+           !trailing_separator;
+       if (max_length >= sizeof(output) || (int)max_length > params->max)
                return NULL;
 
        if ((fd = open("/dev/urandom", O_RDONLY)) < 0)
                return NULL;
 
+       retval = NULL;
        length = 0;
        do {
-               if (read_loop(fd, bytes, sizeof(bytes)) != sizeof(bytes)) {
-                       close(fd);
-                       return NULL;
-               }
+               if (read_loop(fd, bytes, sizeof(bytes)) != sizeof(bytes))
+                       goto out;
 
+/*
+ * Append a word.  Treating bytes as little-endian, we use bits 0 to 11 for the
+ * word index, and bit 13 for toggling the case of the first character.  Bits
+ * 12, 14, and 15 are left unused.  Bits 16 to 23 are left for the separator.
+ */
                i = (((int)bytes[1] & 0x0f) << 8) | (int)bytes[0];
                start = _passwdqc_wordset_4k[i];
-               end = memchr(start, '\0', 6);
+               end = memchr(start, '\0', WORDSET_4K_LENGTH_MAX);
                if (!end)
-                       end = start + 6;
+                       end = start + WORDSET_4K_LENGTH_MAX;
                extra = end - start;
-               if (length + extra >= sizeof(output) - 1) {
-                       close(fd);
-                       return NULL;
-               }
+/* The ">=" leaves room for either one more separator or NUL */
+               if (length + extra >= sizeof(output))
+                       goto out;
                memcpy(&output[length], start, extra);
-               output[length] ^= bytes[1] & 0x20; /* toggle case if bit set */
+               if (toggle_case) {
+/* Toggle case if bit set (we assume ASCII) */
+                       output[length] ^= bytes[1] & 0x20;
+                       bits--;
+               }
                length += extra;
-               bits -= 13;
+               bits -= WORD_BITS - 1;
+
+               if (bits <= 0)
+                       break;
 
-               if (use_separators && bits > 4) {
+/*
+ * Append a separator character.  We use bits 16 to 19.  Bits 20 to 23 are left
+ * unused.
+ *
+ * Special case: we may happen to leave a trailing separator if it provides
+ * enough bits on its own.  With WORD_BITS 13 and SEPARATOR_BITS 4, this
+ * happens e.g. for bits values from 31 to 34, 48 to 51, 65 to 68.
+ */
+               if (use_separators) {
                        i = bytes[2] & 0x0f;
                        output[length++] = SEPARATORS[i];
-                       bits -= 4;
-               } else if (bits > 0)
-                       output[length++] = ' ';
+                       bits -= SEPARATOR_BITS;
+               } else
+                       output[length++] = SEPARATORS[0];
        } while (bits > 0);
 
+/*
+ * Since we may have added a separator after the check in the loop above, we
+ * must check again now.
+ */
+       if (length < sizeof(output)) {
+               output[length] = '\0';
+               retval = strdup(output);
+       }
+
+out:
        memset(bytes, 0, sizeof(bytes));
+       memset(output, 0, length);
 
        close(fd);
 
-       output[length] = '\0';
-       retval = strdup(output);
-       memset(output, 0, length);
        return retval;
 }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/passwdqc-1.2.2/pwqcheck.php 
new/passwdqc-1.3.0/pwqcheck.php
--- old/passwdqc-1.2.2/pwqcheck.php     1970-01-01 01:00:00.000000000 +0100
+++ new/passwdqc-1.3.0/pwqcheck.php     2013-04-24 03:57:26.000000000 +0200
@@ -0,0 +1,84 @@
+<?php
+
+/*
+ * Copyright (c) 2010 by Solar Designer
+ * See LICENSE
+ *
+ * This file was originally written as part of demos for the "How to manage a
+ * PHP application's users and passwords" article submitted to "the Month of
+ * PHP Security" (which was May 2010):
+ *
+ * 
http://www.openwall.com/articles/PHP-Users-Passwords#enforcing-password-policy
+ *
+ * The pwqcheck() function is a wrapper around the pwqcheck(1) program from
+ * the passwdqc package:
+ *
+ * http://www.openwall.com/passwdqc/
+ *
+ * Returns 'OK' if the new password/passphrase passes the requirements.
+ * Otherwise returns a message explaining one of the reasons why the
+ * password/passphrase is rejected.
+ *
+ * $newpass and $oldpass are the new and current/old passwords/passphrases,
+ * respectively.  Only $newpass is required.
+ *
+ * $user is the username.
+ *
+ * $aux may be the user's full name, e-mail address, and/or other textual
+ * info specific to the user (multiple items may be separated with spaces).
+ *
+ * $args are additional arguments to pass to pwqcheck(1), to override the
+ * default password policy.
+ */
+function pwqcheck($newpass, $oldpass = '', $user = '', $aux = '', $args = '')
+{
+// pwqcheck(1) itself returns the same message on internal error
+       $retval = 'Bad passphrase (check failed)';
+
+       $descriptorspec = array(
+               0 => array('pipe', 'r'),
+               1 => array('pipe', 'w'));
+// Leave stderr (fd 2) pointing to where it is, likely to error_log
+
+// Replace characters that would violate the protocol
+       $newpass = strtr($newpass, "\n", '.');
+       $oldpass = strtr($oldpass, "\n", '.');
+       $user = strtr($user, "\n:", '..');
+
+// Trigger a "too short" rather than "is the same" message in this special case
+       if (!$newpass && !$oldpass)
+               $oldpass = '.';
+
+       if ($args)
+               $args = ' ' . $args;
+       if (!$user)
+               $args = ' -2' . $args; // passwdqc 1.2.0+
+
+       $command = 'exec '; // No need to keep the shell process around on Unix
+       $command .= 'pwqcheck' . $args;
+       if (!($process = @proc_open($command, $descriptorspec, $pipes)))
+               return $retval;
+
+       $err = 0;
+       fwrite($pipes[0], "$newpass\n$oldpass\n") || $err = 1;
+       if ($user)
+               fwrite($pipes[0], "$user::::$aux:/:\n") || $err = 1;
+       fclose($pipes[0]) || $err = 1;
+       ($output = stream_get_contents($pipes[1])) || $err = 1;
+       fclose($pipes[1]);
+
+       $status = proc_close($process);
+
+// There must be a linefeed character at the end.  Remove it.
+       if (substr($output, -1) === "\n")
+               $output = substr($output, 0, -1);
+       else
+               $err = 1;
+
+       if ($err === 0 && ($status === 0 || $output !== 'OK'))
+               $retval = $output;
+
+       return $retval;
+}
+
+?>
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/passwdqc-1.2.2/pwqgen.1 new/passwdqc-1.3.0/pwqgen.1
--- old/passwdqc-1.2.2/pwqgen.1 2010-03-13 22:29:15.000000000 +0100
+++ new/passwdqc-1.3.0/pwqgen.1 2013-04-23 16:14:07.000000000 +0200
@@ -16,7 +16,7 @@
 .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 .\" SUCH DAMAGE.
 .\"
-.\" $Owl: Owl/packages/passwdqc/passwdqc/pwqgen.1,v 1.10 2010/03/13 21:29:15 
solar Exp $
+.\" $Owl: Owl/packages/passwdqc/passwdqc/pwqgen.1,v 1.11 2013/04/23 14:14:07 
solar Exp $
 .\"
 .Dd March 13, 2010
 .Dt PWQGEN 1
@@ -37,7 +37,7 @@
 .Bl -tag -width indent
 .It Cm random Ns = Ns Ar N
 .Pq default: Cm random Ns = Ns 47
-The size of randomly-generated passphrase in bits (26 to 81).
+The size of randomly-generated passphrase in bits (24 to 85).
 .It Cm config Ns = Ns Ar FILE
 Load config
 .Ar FILE
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/passwdqc-1.2.2/wordset_4k.c 
new/passwdqc-1.3.0/wordset_4k.c
--- old/passwdqc-1.2.2/wordset_4k.c     2010-03-27 20:42:54.000000000 +0100
+++ new/passwdqc-1.3.0/wordset_4k.c     2013-04-23 14:22:39.000000000 +0200
@@ -57,7 +57,7 @@
 
 #include "wordset_4k.h"
 
-char _passwdqc_wordset_4k[0x1000][6] = {
+const char _passwdqc_wordset_4k[0x1000][WORDSET_4K_LENGTH_MAX] = {
        "Adam",
        "Afghan",
        "Alaska",
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/passwdqc-1.2.2/wordset_4k.h 
new/passwdqc-1.3.0/wordset_4k.h
--- old/passwdqc-1.2.2/wordset_4k.h     2009-10-10 00:46:30.000000000 +0200
+++ new/passwdqc-1.3.0/wordset_4k.h     2013-04-23 14:22:21.000000000 +0200
@@ -6,6 +6,8 @@
 #ifndef WORDSET_4K_H__
 #define WORDSET_4K_H__
 
-extern char _passwdqc_wordset_4k[0x1000][6];
+#define WORDSET_4K_LENGTH_MAX          6
+
+extern const char _passwdqc_wordset_4k[0x1000][WORDSET_4K_LENGTH_MAX];
 
 #endif /* WORDSET_4K_H__ */


Reply via email to