commit pam_passwdqc for openSUSE:Factory
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 <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-poli... + * + * 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__ */
participants (1)
-
root@hilbert.suse.de