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 - p.drouand@gmail.com
+
+- 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 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 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 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 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
@@ -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
@@ -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__ */