summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorTomas Mraz <tmraz@redhat.com>2011-09-16 19:02:43 +0200
committerTomas Mraz <tmraz@redhat.com>2011-09-16 19:02:43 +0200
commite745294c1839de3b0886c6cbe5d8601c56dd3dcf (patch)
tree2f46e7e2e66ee2a52557d577268ec738545b91c4 /src
downloadlibpwquality-e745294c1839de3b0886c6cbe5d8601c56dd3dcf.tar.gz
Initial import into the repository.
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.am44
-rw-r--r--src/check.c509
-rw-r--r--src/generate.c211
-rw-r--r--src/libpwquality.map15
-rw-r--r--src/pam_cracklib.c755
-rw-r--r--src/pam_pwquality.c309
-rw-r--r--src/pwmake.c108
-rw-r--r--src/pwqprivate.h88
-rw-r--r--src/pwquality.h139
-rw-r--r--src/pwscore.c143
-rw-r--r--src/settings.c350
11 files changed, 2671 insertions, 0 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
new file mode 100644
index 0000000..09db8ca
--- /dev/null
+++ b/src/Makefile.am
@@ -0,0 +1,44 @@
+#
+# Copyright (c) 2005, 2006, 2007, 2009 Thorsten Kukuk <kukuk@thkukuk.de>
+# Copyright (c) 2011 Red Hat, Inc.
+# Copyright (c) 2011 Tomas Mraz <tm@t8m.info>
+#
+
+CLEANFILES = *~
+
+EXTRA_DIST = libpwquality.map
+
+include_HEADERS = pwquality.h
+
+noinst_HEADERS = pwqprivate.h
+
+if HAVE_LD_VERSION_SCRIPT
+ libpwquality_version_script = -Wl,--version-script=$(srcdir)/libpwquality.map
+else
+ libpwquality_version_script =
+endif
+
+libpwquality_la_LDFLAGS = -no-undefined $(libpwquality_version_script) \
+ -version-info @PWQUALITY_LT_CURRENT@:@PWQUALITY_LT_REVISION@:@PWQUALITY_LT_AGE@
+
+libpwquality_la_LIBADD = @LIBCRACK@
+
+libpwquality_la_SOURCES = generate.c check.c settings.c
+
+pam_pwquality_la_LDFLAGS = -no-undefined -avoid-version -module
+
+pam_pwquality_la_LIBADD = libpwquality.la -lpam
+
+pam_pwquality_la_SOURCES = pam_pwquality.c
+
+pwscore_SOURCES = pwscore.c
+
+pwscore_LDADD = libpwquality.la
+
+pwmake_SOURCES = pwmake.c
+
+pwmake_LDADD = libpwquality.la
+
+lib_LTLIBRARIES = libpwquality.la pam_pwquality.la
+
+bin_PROGRAMS = pwscore pwmake
diff --git a/src/check.c b/src/check.c
new file mode 100644
index 0000000..c02d1b4
--- /dev/null
+++ b/src/check.c
@@ -0,0 +1,509 @@
+/*
+ * libpwquality main API code for quality checking
+ *
+ * See the end of the file for Copyright and License Information
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <crack.h>
+
+#include "pwquality.h"
+#include "pwqprivate.h"
+
+#ifdef MIN
+#undef MIN
+#endif
+#define MIN(_a, _b) (((_a) < (_b)) ? (_a) : (_b))
+
+/* Helper functions */
+
+/*
+ * test for a palindrome - like `R A D A R' or `M A D A M'
+ */
+static int
+palindrome(const char *new)
+{
+ int i, j;
+
+ i = strlen (new);
+
+ for (j = 0; j < i; j++)
+ if (new[i - j - 1] != new[j])
+ return 0;
+
+ return 1;
+}
+
+/*
+ * Calculate how different two strings are in terms of the number of
+ * character removals, additions, and changes needed to go from one to
+ * the other
+ */
+
+static int
+distdifferent(const char *old, const char *new,
+ size_t i, size_t j)
+{
+ char c, d;
+
+ if ((i == 0) || (strlen(old) < i)) {
+ c = 0;
+ } else {
+ c = old[i - 1];
+ }
+
+ if ((j == 0) || (strlen(new) < j)) {
+ d = 0;
+ } else {
+ d = new[j - 1];
+ }
+ return (c != d);
+}
+
+static int
+distcalculate(int **distances, const char *old, const char *new,
+ size_t i, size_t j)
+{
+ int tmp = 0;
+
+ if (distances[i][j] != -1) {
+ return distances[i][j];
+ }
+
+ tmp = distcalculate(distances, old, new, i - 1, j - 1);
+ tmp = MIN(tmp, distcalculate(distances, old, new, i, j - 1));
+ tmp = MIN(tmp, distcalculate(distances, old, new, i - 1, j));
+ tmp += distdifferent(old, new, i, j);
+
+ distances[i][j] = tmp;
+
+ return tmp;
+}
+
+static int
+distance(const char *old, const char *new)
+{
+ int **distances = NULL;
+ size_t m, n, i, j;
+ int r = -1;
+
+ m = strlen(old);
+ n = strlen(new);
+ distances = malloc(sizeof(int*) * (m + 1));
+ if (distances == NULL)
+ return -1;
+
+ for (i = 0; i <= m; i++) {
+ distances[i] = malloc(sizeof(int) * (n + 1));
+ if (distances[i] == NULL)
+ goto allocfail;
+
+ for(j = 0; j <= n; j++) {
+ distances[i][j] = -1;
+ }
+ }
+
+ for (i = 0; i <= m; i++) {
+ distances[i][0] = i;
+ }
+
+ for (j = 0; j <= n; j++) {
+ distances[0][j] = j;
+ }
+
+ r = distcalculate(distances, old, new, m, n);
+
+allocfail:
+ for (i = 0; i <= m; i++) {
+ if (distances[i]) {
+ memset(distances[i], 0, sizeof(int) * (n + 1));
+ free(distances[i]);
+ }
+ }
+ free(distances);
+
+ return r;
+}
+
+static int
+similar(pwquality_settings_t *pwq,
+ const char *old, const char *new)
+{
+ int dist;
+
+ dist = distance(old, new);
+
+ if (dist < 0)
+ return PWQ_ERROR_MEM_ALLOC;
+
+ if (dist >= pwq->diff_ok) {
+ return 0;
+ }
+
+ if (strlen(new) >= (strlen(old) * 2)) {
+ return 0;
+ }
+
+ /* passwords are too similar */
+ return PWQ_ERROR_TOO_SIMILAR;
+}
+
+/*
+ * count classes of charecters
+ */
+
+static int
+numclass(pwquality_settings_t *pwq,
+ const char *new)
+{
+ int digits = 0;
+ int uppers = 0;
+ int lowers = 0;
+ int others = 0;
+ int total_class;
+ int i;
+ int retval;
+
+ for (i = 0; new[i]; i++) {
+ if (isdigit(new[i]))
+ digits = 1;
+ else if (isupper(new[i]))
+ uppers = 1;
+ else if (islower(new[i]))
+ lowers = 1;
+ else
+ others = 1;
+ }
+
+ total_class = digits + uppers + lowers + others;
+
+ return total_class;
+}
+
+/*
+ * a nice mix of characters
+ * the credit (if positive) is a maximum value that is subtracted from
+ * the minimum allowed size of the password if letters of the class are
+ * present in the password
+ */
+static int
+simple(pwquality_settings_t *pwq, const char *new)
+{
+ int digits = 0;
+ int uppers = 0;
+ int lowers = 0;
+ int others = 0;
+ int size;
+ int i;
+
+ for (i = 0; new[i]; i++) {
+ if (isdigit(new[i]))
+ digits++;
+ else if (isupper(new[i]))
+ uppers++;
+ else if (islower(new[i]))
+ lowers++;
+ else
+ others++;
+ }
+
+ if ((pwq->dig_credit >= 0) && (digits > pwq->dig_credit))
+ digits = pwq->dig_credit;
+
+ if ((pwq->up_credit >= 0) && (uppers > pwq->up_credit))
+ uppers = pwq->up_credit;
+
+ if ((pwq->low_credit >= 0) && (lowers > pwq->low_credit))
+ lowers = pwq->low_credit;
+
+ if ((pwq->oth_credit >= 0) && (others > pwq->oth_credit))
+ others = pwq->oth_credit;
+
+ size = pwq->min_length;
+
+ if (pwq->dig_credit >= 0)
+ size -= digits;
+ else if (digits < pwq->dig_credit * -1)
+ return PWQ_ERROR_MIN_DIGITS;
+
+ if (pwq->up_credit >= 0)
+ size -= uppers;
+ else if (uppers < pwq->up_credit * -1)
+ return PWQ_ERROR_MIN_UPPERS;
+
+ if (pwq->low_credit >= 0)
+ size -= lowers;
+ else if (lowers < pwq->low_credit * -1)
+ return PWQ_ERROR_MIN_LOWERS;
+
+ if (pwq->oth_credit >= 0)
+ size -= others;
+ else if (others < pwq->oth_credit * -1)
+ return PWQ_ERROR_MIN_OTHERS;
+
+ if (size <= i)
+ return 0;
+
+ return PWQ_ERROR_MIN_LENGTH;
+}
+
+/*
+ * too many same consecutive characters
+ */
+
+static int
+consecutive(pwquality_settings_t *pwq, const char *new)
+{
+ char c;
+ int i;
+ int same;
+
+ if (pwq->max_repeat == 0)
+ return 0;
+
+ for (i = 0; new[i]; i++) {
+ if (i > 0 && new[i] == c) {
+ ++same;
+ if (same > pwq->max_repeat)
+ return 1;
+ } else {
+ c = new[i];
+ same = 1;
+ }
+ }
+ return 0;
+}
+
+static char *
+str_lower(char *string)
+{
+ char *cp;
+
+ if (!string)
+ return NULL;
+
+ for (cp = string; *cp; cp++)
+ *cp = tolower(*cp);
+ return string;
+}
+
+static char *
+x_strdup(const char *string)
+{
+ if (!string)
+ return NULL;
+ return strdup(string);
+}
+
+static int
+password_check(pwquality_settings_t *pwq,
+ const char *new, const char *old)
+{
+ int rv = 0;
+ char *oldmono = NULL, *newmono, *wrapped = NULL;
+
+ newmono = str_lower(x_strdup(new));
+ if (!newmono)
+ rv = PWQ_ERROR_MEM_ALLOC;
+
+ if (!rv && old) {
+ oldmono = str_lower(x_strdup(old));
+ if (oldmono)
+ wrapped = malloc(strlen(oldmono) * 2 + 1);
+ if (wrapped) {
+ strcpy (wrapped, oldmono);
+ strcat (wrapped, oldmono);
+ } else {
+ rv = PWQ_ERROR_MEM_ALLOC;
+ }
+ }
+
+ if (!rv && palindrome(newmono))
+ rv = PWQ_ERROR_PALINDROME;
+
+ if (!rv && oldmono && strcmp(oldmono, newmono) == 0)
+ rv = PWQ_ERROR_CASE_CHANGES_ONLY;
+
+ if (!rv && oldmono)
+ rv = similar(pwq, oldmono, newmono);
+
+ if (!rv)
+ rv = simple(pwq, new);
+
+ if (!rv && wrapped && strstr(wrapped, newmono))
+ rv = PWQ_ERROR_ROTATED;
+
+ if (!rv && numclass(pwq, new) < pwq->min_class)
+ rv = PWQ_ERROR_MIN_CLASSES;
+
+ if (!rv && consecutive(pwq, new))
+ rv = PWQ_ERROR_MAX_CONSECUTIVE;
+
+ if (newmono) {
+ memset(newmono, 0, strlen(newmono));
+ free(newmono);
+ }
+
+ if (oldmono) {
+ memset(oldmono, 0, strlen(oldmono));
+ free(oldmono);
+ }
+
+ if (wrapped) {
+ memset(wrapped, 0, strlen(wrapped));
+ free(wrapped);
+ }
+
+ return rv;
+}
+
+/* this algorithm is an arbitrary one, fine-tuned by testing */
+static int
+password_score(pwquality_settings_t *pwq, const char *password)
+{
+ int len;
+ int score;
+ int i;
+ int j;
+ unsigned char freq[256];
+ unsigned char *buf;
+
+ len = strlen(password);
+
+ if ((buf = malloc(len)) == NULL)
+ /* should get enough memory to obtain a nice score */
+ return 0;
+
+ score = (len - pwq->min_length) * 2;
+
+ memcpy(buf, password, len);
+
+ for (j = 0; j < 3; j++) {
+
+ memset(freq, 0, sizeof(freq));
+
+ for (i = 0; i < len - j; i++) {
+ ++freq[buf[i]];
+ if (i < len - j - 1)
+ buf[i] = abs(buf[i] - buf[i+1]);
+ }
+
+ for (i = 0; i < sizeof(freq); i++) {
+ if (freq[i])
+ ++score;
+ }
+ }
+
+ memset(buf, 0, len);
+ free(buf);
+
+ score += numclass(pwq, password) * 2;
+
+ score = (score * 100)/(3 * pwq->min_length +
+ + PWQ_NUM_CLASSES * 2);
+
+ score -= 50;
+
+ if (score > 100)
+ score = 100;
+ if (score < 0)
+ score = 0;
+
+ return score;
+}
+
+/* check the password according to the settings
+ * it returns either score <0-100>, negative error number,
+ * and in case of PWQ_ERROR_CRACKLIB also auxiliary
+ * error message returned from cracklib;
+ * the old password is optional */
+int
+pwquality_check(pwquality_settings_t *pwq, const char *password,
+ const char *oldpassword, const char **error)
+{
+ const char *msg;
+ int score;
+
+ if (password == NULL || *password == '\0') {
+ return PWQ_ERROR_EMPTY_PASSWORD;
+ }
+
+ if (oldpassword && strcmp(oldpassword, password) == 0) {
+ return PWQ_ERROR_SAME_PASSWORD;
+ }
+
+ msg = FascistCheck(password, pwq->dict_path);
+ if (msg) {
+ if (error)
+ *error = msg;
+ return PWQ_ERROR_CRACKLIB_CHECK;
+ }
+
+ score = password_check(pwq, password, oldpassword);
+ if (score == 0) {
+ score = password_score(pwq, password);
+ }
+
+ return score;
+}
+
+/*
+ * Copyright (c) Cristian Gafton <gafton@redhat.com>, 1996.
+ * All rights reserved
+ * Copyright (c) Red Hat, Inc, 2011
+ * Copyright (c) Tomas Mraz <tm@t8m.info>, 2011
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, and the entire permission notice in its entirety,
+ * including the disclaimer of warranties.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * ALTERNATIVELY, this product may be distributed under the terms of
+ * the GNU Public License, in which case the provisions of the GPL are
+ * required INSTEAD OF the above restrictions. (This clause is
+ * necessary due to a potential bad interaction between the GPL and
+ * the restrictions contained in a BSD-style copyright.)
+ *
+ * THIS SOFTWARE IS PROVIDED `AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * The following copyright was appended for the long password support
+ * added with the libpam 0.58 release:
+ *
+ * Modificaton Copyright (c) Philip W. Dalrymple III <pwd@mdtsoft.com>
+ * 1997. All rights reserved
+ *
+ * THE MODIFICATION THAT PROVIDES SUPPORT FOR LONG PASSWORD TYPE CHECKING TO
+ * THIS SOFTWARE IS PROVIDED `AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
diff --git a/src/generate.c b/src/generate.c
new file mode 100644
index 0000000..24c158b
--- /dev/null
+++ b/src/generate.c
@@ -0,0 +1,211 @@
+/*
+ * libpwquality main API code for password generation
+ *
+ * See the end of the file for Copyright and License Information
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <limits.h>
+#include <crack.h>
+#include <errno.h>
+
+#include "pwquality.h"
+#include "pwqprivate.h"
+
+#ifndef PATH_DEV_URANDOM
+#define PATH_DEV_URANDOM "/dev/urandom"
+#endif
+
+static char vowels[] = { 'a', '4', 'A', 'e', 'E', '3', 'i', 'I', 'o', 'O',
+ '0', 'u', 'U', 'y', 'Y', '@' }; /* 4 bits */
+static char consonants1[] = { 'b', 'c', 'd', 'f', 'g', 'h', 'j', 'k', 'l', 'm',
+ 'n', 'p', 'q', 'r', 's', 't', 'v', 'w', 'x', 'z',
+ 'B', 'D', 'G', 'H', 'J', 'K', 'L', 'M', 'N', 'P',
+ 'R', 'S' }; /* 5 bits */
+static char consonants2[] = { 'b', 'c', 'd', 'f', 'g', 'h', 'j', 'k', 'l', 'm',
+ 'n', 'p', 'q', 'r', 's', 't', 'v', 'w', 'x', 'z',
+ 'B', 'C', 'D', 'F', 'G', 'H', 'J', 'K', 'L', 'M',
+ 'N', 'P', 'Q', 'R', 'S', 'T', 'V', 'W', 'X', 'Z',
+ '1', '2', '5', '6', '7', '8', '9', '!', '#', '$',
+ '%', '^', '&', '*', '(', ')', '-', '+', '=', '[',
+ ']', ';', '.', ',' }; /* 6 bits */
+
+static int
+get_entropy_bits(char *buf, int nbits)
+{
+ int fd;
+ int rv;
+ int offset = 0;
+ int bytes = (nbits + 7) / 8; /* round up on division */
+
+ fd = open(PATH_DEV_URANDOM, O_RDONLY);
+ if (fd == -1)
+ return -1;
+
+ while (bytes > 0) {
+ rv = read(fd, buf + offset, bytes);
+
+ if (rv < 0) {
+ if (errno == EINTR) continue;
+ (void)close(fd);
+ return -1;
+ }
+ if (rv == 0) {
+ (void)close(fd);
+ return -1;
+ }
+
+ offset += rv;
+ bytes -= rv;
+ }
+
+ (void)close(fd);
+ return 0;
+}
+
+static unsigned int
+consume_entropy(char *buf, int bits, int *remaining, int *offset)
+{
+ unsigned int low = (unsigned char)buf[*offset/8];
+ unsigned int high = 0;
+
+ if (remaining)
+ *remaining -= bits;
+
+ low >>= *offset % 8;
+ low &= (1 << bits) - 1;
+
+ if (8 - *offset % 8 < bits) {
+ high = (unsigned char)buf[*offset/8 + 1];
+ high &= (1 << (bits - (8 - *offset % 8))) - 1;
+ high <<= 8 - *offset % 8;
+ low |= high;
+ }
+
+ *offset += bits;
+ return low;
+}
+
+/* generate a random password according to the settings */
+int
+pwquality_generate(pwquality_settings_t *pwq, int entropy_bits, char **password)
+{
+ char entropy[PWQ_MAX_ENTROPY_BITS/8 + 1];
+ char *tmp;
+ int maxlen;
+ int try = 0;
+
+ *password = NULL;
+
+ if (entropy_bits > PWQ_MAX_ENTROPY_BITS)
+ entropy_bits = PWQ_MAX_ENTROPY_BITS;
+
+ if (entropy_bits < PWQ_MIN_ENTROPY_BITS)
+ entropy_bits = PWQ_MIN_ENTROPY_BITS;
+
+ /* overestimate here only 9 bits per syllable of 3 characters */
+ maxlen = (entropy_bits + 8) / 9 * 3 + 1;
+
+ tmp = malloc(maxlen);
+ if (tmp == NULL) {
+ return PWQ_ERROR_FATAL_FAILURE;
+ }
+
+ do {
+ int offset = 0;
+ int remaining = entropy_bits;
+ char *ptr;
+
+ memset(tmp, '\0', maxlen);
+ /* read one more byte for rounding overflow during generation
+ and for at most every 9th bit we also drop one bit */
+ if (get_entropy_bits(entropy, entropy_bits +
+ (entropy_bits+8)/9 + 8) < 0) {
+ free(tmp);
+ return PWQ_ERROR_RNG;
+ }
+
+ ptr = tmp;
+ while (remaining > 0) {
+ unsigned int idx;
+
+ if (consume_entropy(entropy, 1, NULL, &offset)) {
+ idx = consume_entropy(entropy, 6, &remaining, &offset);
+ *ptr = consonants2[idx];
+ ++ptr;
+ if (remaining < 0)
+ break;
+ }
+
+ idx = consume_entropy(entropy, 4, &remaining, &offset);
+ *ptr = vowels[idx];
+ ++ptr;
+ if (remaining < 0)
+ break;
+
+ idx = consume_entropy(entropy, 5, &remaining, &offset);
+ *ptr = consonants1[idx];
+ ++ptr;
+ }
+ } while (pwquality_check(pwq, tmp, NULL, NULL) < 0 &&
+ ++try < PWQ_NUM_GENERATION_TRIES);
+
+ /* clean up */
+ memset(entropy, '\0', sizeof(entropy));
+
+ if (try >= PWQ_NUM_GENERATION_TRIES) {
+ free(tmp);
+ return PWQ_ERROR_GENERATION_FAILED;
+ }
+
+ *password = tmp;
+ tmp = NULL;
+ return 0;
+}
+
+
+/*
+ * Copyright (c) Red Hat, Inc, 2011
+ * Copyright (c) Tomas Mraz <tm@t8m.info>, 2011
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, and the entire permission notice in its entirety,
+ * including the disclaimer of warranties.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * ALTERNATIVELY, this product may be distributed under the terms of
+ * the GNU Public License, in which case the provisions of the GPL are
+ * required INSTEAD OF the above restrictions. (This clause is
+ * necessary due to a potential bad interaction between the GPL and
+ * the restrictions contained in a BSD-style copyright.)
+ *
+ * THIS SOFTWARE IS PROVIDED `AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
diff --git a/src/libpwquality.map b/src/libpwquality.map
new file mode 100644
index 0000000..231dd02
--- /dev/null
+++ b/src/libpwquality.map
@@ -0,0 +1,15 @@
+LIBPWQUALITY_1.0 {
+ global:
+ pwquality_default_settings;
+ pwquality_free_settings;
+ pwquality_read_config;
+ pwquality_set_option;
+ pwquality_set_int_value;
+ pwquality_set_str_value;
+ pwquality_get_int_value;
+ pwquality_get_str_value;
+ pwquality_generate;
+ pwquality_check;
+ local:
+ *;
+};
diff --git a/src/pam_cracklib.c b/src/pam_cracklib.c
new file mode 100644
index 0000000..a5e12cb
--- /dev/null
+++ b/src/pam_cracklib.c
@@ -0,0 +1,755 @@
+/*
+ * libpwquality main API code
+ *
+ * See the end of the file for Copyright and License Information
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <ctype.h>
+#include <limits.h>
+#include <crack.h>
+
+/* For Translators: "%s%s" could be replaced with "<service> " or "". */
+#define PROMPT1 _("New %s%spassword: ")
+/* For Translators: "%s%s" could be replaced with "<service> " or "". */
+#define PROMPT2 _("Retype new %s%spassword: ")
+#define MISTYPED_PASS _("Sorry, passwords do not match.")
+
+#ifdef MIN
+#undef MIN
+#endif
+#define MIN(_a, _b) (((_a) < (_b)) ? (_a) : (_b))
+
+/*
+ * here, we make a definition for the externally accessible function
+ * in this file (this definition is required for static a module
+ * but strongly encouraged generally) it is used to instruct the
+ * modules include file to define the function prototypes.
+ */
+
+#define PAM_SM_PASSWORD
+
+#include <security/pam_modules.h>
+#include <security/_pam_macros.h>
+#include <security/pam_ext.h>
+
+/* argument parsing */
+#define PAM_DEBUG_ARG 0x0001
+
+struct cracklib_options {
+ int retry_times;
+ int diff_ok;
+ int diff_ignore;
+ int min_length;
+ int dig_credit;
+ int up_credit;
+ int low_credit;
+ int oth_credit;
+ int min_class;
+ int max_repeat;
+ int reject_user;
+ const char *cracklib_dictpath;
+};
+
+#define CO_RETRY_TIMES 1
+#define CO_DIFF_OK 5
+#define CO_DIFF_IGNORE 23
+#define CO_MIN_LENGTH 9
+# define CO_MIN_LENGTH_BASE 5
+#define CO_DIG_CREDIT 1
+#define CO_UP_CREDIT 1
+#define CO_LOW_CREDIT 1
+#define CO_OTH_CREDIT 1
+
+static int
+_pam_parse (pam_handle_t *pamh, struct cracklib_options *opt,
+ int argc, const char **argv)
+{
+ int ctrl=0;
+
+ /* step through arguments */
+ for (ctrl=0; argc-- > 0; ++argv) {
+ char *ep = NULL;
+
+ /* generic options */
+
+ if (!strcmp(*argv,"debug"))
+ ctrl |= PAM_DEBUG_ARG;
+ else if (!strncmp(*argv,"type=",5))
+ pam_set_item (pamh, PAM_AUTHTOK_TYPE, *argv+5);
+ else if (!strncmp(*argv,"retry=",6)) {
+ opt->retry_times = strtol(*argv+6,&ep,10);
+ if (!ep || (opt->retry_times < 1))
+ opt->retry_times = CO_RETRY_TIMES;
+ } else if (!strncmp(*argv,"difok=",6)) {
+ opt->diff_ok = strtol(*argv+6,&ep,10);
+ if (!ep || (opt->diff_ok < 0))
+ opt->diff_ok = CO_DIFF_OK;
+ } else if (!strncmp(*argv,"difignore=",10)) {
+ opt->diff_ignore = strtol(*argv+10,&ep,10);
+ if (!ep || (opt->diff_ignore < 0))
+ opt->diff_ignore = CO_DIFF_IGNORE;
+ } else if (!strncmp(*argv,"minlen=",7)) {
+ opt->min_length = strtol(*argv+7,&ep,10);
+ if (!ep || (opt->min_length < CO_MIN_LENGTH_BASE))
+ opt->min_length = CO_MIN_LENGTH_BASE;
+ } else if (!strncmp(*argv,"dcredit=",8)) {
+ opt->dig_credit = strtol(*argv+8,&ep,10);
+ if (!ep)
+ opt->dig_credit = 0;
+ } else if (!strncmp(*argv,"ucredit=",8)) {
+ opt->up_credit = strtol(*argv+8,&ep,10);
+ if (!ep)
+ opt->up_credit = 0;
+ } else if (!strncmp(*argv,"lcredit=",8)) {
+ opt->low_credit = strtol(*argv+8,&ep,10);
+ if (!ep)
+ opt->low_credit = 0;
+ } else if (!strncmp(*argv,"ocredit=",8)) {
+ opt->oth_credit = strtol(*argv+8,&ep,10);
+ if (!ep)
+ opt->oth_credit = 0;
+ } else if (!strncmp(*argv,"minclass=",9)) {
+ opt->min_class = strtol(*argv+9,&ep,10);
+ if (!ep)
+ opt->min_class = 0;
+ if (opt->min_class > 4)
+ opt->min_class = 4;
+ } else if (!strncmp(*argv,"maxrepeat=",10)) {
+ opt->max_repeat = strtol(*argv+10,&ep,10);
+ if (!ep)
+ opt->max_repeat = 0;
+ } else if (!strncmp(*argv,"reject_username",15)) {
+ opt->reject_user = 1;
+ } else if (!strncmp(*argv,"authtok_type",12)) {
+ /* for pam_get_authtok, ignore */;
+ } else if (!strncmp(*argv,"use_authtok",11)) {
+ /* for pam_get_authtok, ignore */;
+ } else if (!strncmp(*argv,"use_first_pass",14)) {
+ /* for pam_get_authtok, ignore */;
+ } else if (!strncmp(*argv,"try_first_pass",14)) {
+ /* for pam_get_authtok, ignore */;
+ } else if (!strncmp(*argv,"dictpath=",9)) {
+ opt->cracklib_dictpath = *argv+9;
+ if (!*(opt->cracklib_dictpath)) {
+ opt->cracklib_dictpath = CRACKLIB_DICTS;
+ }
+ } else {
+ pam_syslog(pamh,LOG_ERR,"pam_parse: unknown option; %s",*argv);
+ }
+ }
+
+ return ctrl;
+}
+
+/* Helper functions */
+
+/*
+ * can't be a palindrome - like `R A D A R' or `M A D A M'
+ */
+static int palindrome(const char *new)
+{
+ int i, j;
+
+ i = strlen (new);
+
+ for (j = 0;j < i;j++)
+ if (new[i - j - 1] != new[j])
+ return 0;
+
+ return 1;
+}
+
+/*
+ * Calculate how different two strings are in terms of the number of
+ * character removals, additions, and changes needed to go from one to
+ * the other
+ */
+
+static int distdifferent(const char *old, const char *new,
+ size_t i, size_t j)
+{
+ char c, d;
+
+ if ((i == 0) || (strlen(old) < i)) {
+ c = 0;
+ } else {
+ c = old[i - 1];
+ }
+ if ((j == 0) || (strlen(new) < j)) {
+ d = 0;
+ } else {
+ d = new[j - 1];
+ }
+ return (c != d);
+}
+
+static int distcalculate(int **distances, const char *old, const char *new,
+ size_t i, size_t j)
+{
+ int tmp = 0;
+
+ if (distances[i][j] != -1) {
+ return distances[i][j];
+ }
+
+ tmp = distcalculate(distances, old, new, i - 1, j - 1);
+ tmp = MIN(tmp, distcalculate(distances, old, new, i, j - 1));
+ tmp = MIN(tmp, distcalculate(distances, old, new, i - 1, j));
+ tmp += distdifferent(old, new, i, j);
+
+ distances[i][j] = tmp;
+
+ return tmp;
+}
+
+static int distance(const char *old, const char *new)
+{
+ int **distances = NULL;
+ size_t m, n, i, j, r;
+
+ m = strlen(old);
+ n = strlen(new);
+ distances = malloc(sizeof(int*) * (m + 1));
+
+ for (i = 0; i <= m; i++) {
+ distances[i] = malloc(sizeof(int) * (n + 1));
+ for(j = 0; j <= n; j++) {
+ distances[i][j] = -1;
+ }
+ }
+ for (i = 0; i <= m; i++) {
+ distances[i][0] = i;
+ }
+ for (j = 0; j <= n; j++) {
+ distances[0][j] = j;
+ }
+ distances[0][0] = 0;
+
+ r = distcalculate(distances, old, new, m, n);
+
+ for (i = 0; i <= m; i++) {
+ memset(distances[i], 0, sizeof(int) * (n + 1));
+ free(distances[i]);
+ }
+ free(distances);
+
+ return r;
+}
+
+static int similar(struct cracklib_options *opt,
+ const char *old, const char *new)
+{
+ if (distance(old, new) >= opt->diff_ok) {
+ return 0;
+ }
+
+ if (strlen(new) >= (strlen(old) * 2)) {
+ return 0;
+ }
+
+ /* passwords are too similar */
+ return 1;
+}
+
+/*
+ * enough classes of charecters
+ */
+
+static int minclass (struct cracklib_options *opt,
+ const char *new)
+{
+ int digits = 0;
+ int uppers = 0;
+ int lowers = 0;
+ int others = 0;
+ int total_class;
+ int i;
+ int retval;
+
+ D(( "called" ));
+ for (i = 0; new[i]; i++)
+ {
+ if (isdigit (new[i]))
+ digits = 1;
+ else if (isupper (new[i]))
+ uppers = 1;
+ else if (islower (new[i]))
+ lowers = 1;
+ else
+ others = 1;
+ }
+
+ total_class = digits + uppers + lowers + others;
+
+ D (("total class: %d\tmin_class: %d", total_class, opt->min_class));
+
+ if (total_class >= opt->min_class)
+ retval = 0;
+ else
+ retval = 1;
+
+ return retval;
+}
+
+
+/*
+ * a nice mix of characters.
+ */
+static int simple(struct cracklib_options *opt, const char *new)
+{
+ int digits = 0;
+ int uppers = 0;
+ int lowers = 0;
+ int others = 0;
+ int size;
+ int i;
+
+ for (i = 0;new[i];i++) {
+ if (isdigit (new[i]))
+ digits++;
+ else if (isupper (new[i]))
+ uppers++;
+ else if (islower (new[i]))
+ lowers++;
+ else
+ others++;
+ }
+
+ /*
+ * The scam was this - a password of only one character type
+ * must be 8 letters long. Two types, 7, and so on.
+ * This is now changed, the base size and the credits or defaults
+ * see the docs on the module for info on these parameters, the
+ * defaults cause the effect to be the same as before the change
+ */
+
+ if ((opt->dig_credit >= 0) && (digits > opt->dig_credit))
+ digits = opt->dig_credit;
+
+ if ((opt->up_credit >= 0) && (uppers > opt->up_credit))
+ uppers = opt->up_credit;
+
+ if ((opt->low_credit >= 0) && (lowers > opt->low_credit))
+ lowers = opt->low_credit;
+
+ if ((opt->oth_credit >= 0) && (others > opt->oth_credit))
+ others = opt->oth_credit;
+
+ size = opt->min_length;
+
+ if (opt->dig_credit >= 0)
+ size -= digits;
+ else if (digits < opt->dig_credit * -1)
+ return 1;
+
+ if (opt->up_credit >= 0)
+ size -= uppers;
+ else if (uppers < opt->up_credit * -1)
+ return 1;
+
+ if (opt->low_credit >= 0)
+ size -= lowers;
+ else if (lowers < opt->low_credit * -1)
+ return 1;
+
+ if (opt->oth_credit >= 0)
+ size -= others;
+ else if (others < opt->oth_credit * -1)
+ return 1;
+
+ if (size <= i)
+ return 0;
+
+ return 1;
+}
+
+static int consecutive(struct cracklib_options *opt, const char *new)
+{
+ char c;
+ int i;
+ int same;
+
+ if (opt->max_repeat == 0)
+ return 0;
+
+ for (i = 0; new[i]; i++) {
+ if (i > 0 && new[i] == c) {
+ ++same;
+ if (same > opt->max_repeat)
+ return 1;
+ } else {
+ c = new[i];
+ same = 1;
+ }
+ }
+ return 0;
+}
+
+static int usercheck(struct cracklib_options *opt, const char *new,
+ char *user)
+{
+ char *f, *b;
+
+ if (!opt->reject_user)
+ return 0;
+
+ if (strstr(new, user) != NULL)
+ return 1;
+
+ /* now reverse the username, we can do that in place
+ as it is strdup-ed */
+ f = user;
+ b = user+strlen(user)-1;
+ while (f < b) {
+ char c;
+
+ c = *f;
+ *f = *b;
+ *b = c;
+ --b;
+ ++f;
+ }
+
+ if (strstr(new, user) != NULL)
+ return 1;
+ return 0;
+}
+
+static char * str_lower(char *string)
+{
+ char *cp;
+
+ if (!string)
+ return NULL;
+
+ for (cp = string; *cp; cp++)
+ *cp = tolower(*cp);
+ return string;
+}
+
+static const char *password_check(struct cracklib_options *opt,
+ const char *old, const char *new,
+ const char *user)
+{
+ const char *msg = NULL;
+ char *oldmono = NULL, *newmono, *wrapped = NULL;
+ char *usermono = NULL;
+
+ if (old && strcmp(new, old) == 0) {
+ msg = _("is the same as the old one");
+ return msg;
+ }
+
+ newmono = str_lower(x_strdup(new));
+ if (!newmono)
+ msg = _("memory allocation error");
+
+ usermono = str_lower(x_strdup(user));
+ if (!usermono)
+ msg = _("memory allocation error");
+
+ if (!msg && old) {
+ oldmono = str_lower(x_strdup(old));
+ if (oldmono)
+ wrapped = malloc(strlen(oldmono) * 2 + 1);
+ if (wrapped) {
+ strcpy (wrapped, oldmono);
+ strcat (wrapped, oldmono);
+ } else {
+ msg = _("memory allocation error");
+ }
+ }
+
+ if (!msg && palindrome(newmono))
+ msg = _("is a palindrome");
+
+ if (!msg && oldmono && strcmp(oldmono, newmono) == 0)
+ msg = _("case changes only");
+
+ if (!msg && oldmono && similar(opt, oldmono, newmono))
+ msg = _("is too similar to the old one");
+
+ if (!msg && simple(opt, new))
+ msg = _("is too simple");
+
+ if (!msg && wrapped && strstr(wrapped, newmono))
+ msg = _("is rotated");
+
+ if (!msg && minclass (opt, new))
+ msg = _("not enough character classes");
+
+ if (!msg && consecutive(opt, new))
+ msg = _("contains too many same characters consecutively");
+
+ if (!msg && usercheck(opt, newmono, usermono))
+ msg = _("contains the user name in some form");
+
+ free(usermono);
+ if (newmono) {
+ memset(newmono, 0, strlen(newmono));
+ free(newmono);
+ }
+ if (oldmono) {
+ memset(oldmono, 0, strlen(oldmono));
+ free(oldmono);
+ }
+ if (wrapped) {
+ memset(wrapped, 0, strlen(wrapped));
+ free(wrapped);
+ }
+
+ return msg;
+}
+
+
+static int _pam_unix_approve_pass(pam_handle_t *pamh,
+ unsigned int ctrl,
+ struct cracklib_options *opt,
+ const char *pass_old,
+ const char *pass_new)
+{
+ const char *msg = NULL;
+ const char *user;
+ int retval;
+
+ if (pass_new == NULL || (pass_old && !strcmp(pass_old,pass_new))) {
+ if (ctrl & PAM_DEBUG_ARG)
+ pam_syslog(pamh, LOG_DEBUG, "bad authentication token");
+ pam_error(pamh, "%s", pass_new == NULL ?
+ _("No password supplied"):_("Password unchanged"));
+ return PAM_AUTHTOK_ERR;
+ }
+
+ retval = pam_get_user(pamh, &user, NULL);
+ if (retval != PAM_SUCCESS || user == NULL) {
+ if (ctrl & PAM_DEBUG_ARG)
+ pam_syslog(pamh,LOG_ERR,"Can not get username");
+ return PAM_AUTHTOK_ERR;
+ }
+ /*
+ * if one wanted to hardwire authentication token strength
+ * checking this would be the place
+ */
+ msg = password_check(opt, pass_old, pass_new, user);
+
+ if (msg) {
+ if (ctrl & PAM_DEBUG_ARG)
+ pam_syslog(pamh, LOG_NOTICE,
+ "new passwd fails strength check: %s", msg);
+ pam_error(pamh, _("BAD PASSWORD: %s"), msg);
+ return PAM_AUTHTOK_ERR;
+ };
+ return PAM_SUCCESS;
+
+}
+
+/* The Main Thing (by Cristian Gafton, CEO at this module :-)
+ * (stolen from http://home.netscape.com)
+ */
+PAM_EXTERN int pam_sm_chauthtok(pam_handle_t *pamh, int flags,
+ int argc, const char **argv)
+{
+ unsigned int ctrl;
+ struct cracklib_options options;
+
+ D(("called."));
+
+ memset(&options, 0, sizeof(options));
+ options.retry_times = CO_RETRY_TIMES;
+ options.diff_ok = CO_DIFF_OK;
+ options.diff_ignore = CO_DIFF_IGNORE;
+ options.min_length = CO_MIN_LENGTH;
+ options.dig_credit = CO_DIG_CREDIT;
+ options.up_credit = CO_UP_CREDIT;
+ options.low_credit = CO_LOW_CREDIT;
+ options.oth_credit = CO_OTH_CREDIT;
+ options.cracklib_dictpath = CRACKLIB_DICTS;
+
+ ctrl = _pam_parse(pamh, &options, argc, argv);
+
+ if (flags & PAM_PRELIM_CHECK) {
+ /* Check for passwd dictionary */
+ /* We cannot do that, since the original path is compiled
+ into the cracklib library and we don't know it. */
+ return PAM_SUCCESS;
+ } else if (flags & PAM_UPDATE_AUTHTOK) {
+ int retval;
+ const void *oldtoken;
+ int tries;
+
+ D(("do update"));
+
+
+ retval = pam_get_item (pamh, PAM_OLDAUTHTOK, &oldtoken);
+ if (retval != PAM_SUCCESS) {
+ if (ctrl & PAM_DEBUG_ARG)
+ pam_syslog(pamh,LOG_ERR,"Can not get old passwd");
+ oldtoken = NULL;
+ }
+
+ tries = 0;
+ while (tries < options.retry_times) {
+ const char *crack_msg;
+ const char *newtoken = NULL;
+
+
+ tries++;
+
+ /* Planned modus operandi:
+ * Get a passwd.
+ * Verify it against cracklib.
+ * If okay get it a second time.
+ * Check to be the same with the first one.
+ * set PAM_AUTHTOK and return
+ */
+
+ retval = pam_get_authtok_noverify (pamh, &newtoken, NULL);
+ if (retval != PAM_SUCCESS) {
+ pam_syslog(pamh, LOG_ERR, "pam_get_authtok_noverify returned error: %s",
+ pam_strerror (pamh, retval));
+ continue;
+ } else if (newtoken == NULL) { /* user aborted password change, quit */
+ return PAM_AUTHTOK_ERR;
+ }
+
+ D(("testing password"));
+ /* now test this passwd against cracklib */
+
+ D(("against cracklib"));
+ if ((crack_msg = FascistCheck (newtoken, options.cracklib_dictpath))) {
+ if (ctrl & PAM_DEBUG_ARG)
+ pam_syslog(pamh,LOG_DEBUG,"bad password: %s",crack_msg);
+ pam_error (pamh, _("BAD PASSWORD: %s"), crack_msg);
+ if (getuid() || (flags & PAM_CHANGE_EXPIRED_AUTHTOK))
+ {
+ pam_set_item (pamh, PAM_AUTHTOK, NULL);
+ retval = PAM_AUTHTOK_ERR;
+ continue;
+ }
+ }
+
+ /* check it for strength too... */
+ D(("for strength"));
+ retval = _pam_unix_approve_pass (pamh, ctrl, &options,
+ oldtoken, newtoken);
+ if (retval != PAM_SUCCESS) {
+ if (getuid() || (flags & PAM_CHANGE_EXPIRED_AUTHTOK))
+ {
+ pam_set_item(pamh, PAM_AUTHTOK, NULL);
+ retval = PAM_AUTHTOK_ERR;
+ continue;
+ }
+ }
+
+ retval = pam_get_authtok_verify (pamh, &newtoken, NULL);
+ if (retval != PAM_SUCCESS) {
+ pam_syslog(pamh, LOG_ERR, "pam_get_authtok_verify returned error: %s",
+ pam_strerror (pamh, retval));
+ pam_set_item(pamh, PAM_AUTHTOK, NULL);
+ continue;
+ } else if (newtoken == NULL) { /* user aborted password change, quit */
+ return PAM_AUTHTOK_ERR;
+ }
+
+ return PAM_SUCCESS;
+ }
+
+ D(("returning because maxtries reached"));
+
+ pam_set_item (pamh, PAM_AUTHTOK, NULL);
+
+ /* if we have only one try, we can use the real reason,
+ else say that there were too many tries. */
+ if (options.retry_times > 1)
+ return PAM_MAXTRIES;
+ else
+ return retval;
+
+ } else {
+ if (ctrl & PAM_DEBUG_ARG)
+ pam_syslog(pamh, LOG_NOTICE, "UNKNOWN flags setting %02X",flags);
+ return PAM_SERVICE_ERR;
+ }
+
+ /* Not reached */
+ return PAM_SERVICE_ERR;
+}
+
+
+
+#ifdef PAM_STATIC
+/* static module data */
+struct pam_module _pam_cracklib_modstruct = {
+ "pam_cracklib",
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ pam_sm_chauthtok
+};
+#endif
+
+/*
+ * Copyright (c) Cristian Gafton <gafton@redhat.com>, 1996.
+ * All rights reserved
+ * Copyright (c) Red Hat, Inc, 2011
+ * Copyright (c) Tomas Mraz <tm@t8m.info>, 2011
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, and the entire permission notice in its entirety,
+ * including the disclaimer of warranties.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * ALTERNATIVELY, this product may be distributed under the terms of
+ * the GNU Public License, in which case the provisions of the GPL are
+ * required INSTEAD OF the above restrictions. (This clause is
+ * necessary due to a potential bad interaction between the GPL and
+ * the restrictions contained in a BSD-style copyright.)
+ *
+ * THIS SOFTWARE IS PROVIDED `AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * The following copyright was appended for the long password support
+ * added with the libpam 0.58 release:
+ *
+ * Modificaton Copyright (c) Philip W. Dalrymple III <pwd@mdtsoft.com>
+ * 1997. All rights reserved
+ *
+ * THE MODIFICATION THAT PROVIDES SUPPORT FOR LONG PASSWORD TYPE CHECKING TO
+ * THIS SOFTWARE IS PROVIDED `AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
diff --git a/src/pam_pwquality.c b/src/pam_pwquality.c
new file mode 100644
index 0000000..e3cd522
--- /dev/null
+++ b/src/pam_pwquality.c
@@ -0,0 +1,309 @@
+/*
+ * PAM module for password quality checking using libpwquality
+ *
+ * See the end of the file for Copyright and License Information
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <ctype.h>
+#include <limits.h>
+#include <syslog.h>
+#include <libintl.h>
+#include "pwquality.h"
+
+/* For Translators: "%s%s" could be replaced with "<service> " or "". */
+#define PROMPT1 _("New %s%spassword: ")
+/* For Translators: "%s%s" could be replaced with "<service> " or "". */
+#define PROMPT2 _("Retype new %s%spassword: ")
+#define MISTYPED_PASS _("Sorry, passwords do not match.")
+
+/*
+ * here, we make a definition for the externally accessible function
+ * in this file (this definition is required for static a module
+ * but strongly encouraged generally) it is used to instruct the
+ * modules include file to define the function prototypes.
+ */
+
+#define PAM_SM_PASSWORD
+
+#include <security/pam_modules.h>
+#include <security/_pam_macros.h>
+#include <security/pam_ext.h>
+
+/* argument parsing */
+#define PAM_DEBUG_ARG 0x0001
+
+struct module_options {
+ int retry_times;
+ pwquality_settings_t *pwq;
+};
+
+#define CO_RETRY_TIMES 1
+
+static int
+_pam_parse (pam_handle_t *pamh, struct module_options *opt,
+ int argc, const char **argv)
+{
+ int ctrl = 0;
+ pwquality_settings_t *pwq;
+
+ pwq = pwquality_default_settings();
+ if (pwq == NULL)
+ return -1;
+
+ /* just log error here */
+ if (pwquality_read_config(pwq, NULL))
+ pam_syslog(pamh, LOG_ERR,
+ "Reading pwquality configuration file failed: %m");
+
+ /* step through arguments */
+ for (ctrl = 0; argc-- > 0; ++argv) {
+ char *ep = NULL;
+
+ if (!strcmp(*argv, "debug"))
+ ctrl |= PAM_DEBUG_ARG;
+ else if (!strncmp(*argv, "type=", 5))
+ pam_set_item (pamh, PAM_AUTHTOK_TYPE, *argv+5);
+ else if (!strncmp(*argv, "retry=", 6)) {
+ opt->retry_times = strtol(*argv+6, &ep, 10);
+ if (!ep || (opt->retry_times < 1))
+ opt->retry_times = CO_RETRY_TIMES;
+ } else if (!strncmp(*argv, "reject_username", 15)) {
+ /* ignored for compatibility with pam_cracklib */
+ } else if (!strncmp(*argv, "authtok_type", 12)) {
+ /* for pam_get_authtok, ignore */;
+ } else if (!strncmp(*argv, "use_authtok", 11)) {
+ /* for pam_get_authtok, ignore */;
+ } else if (!strncmp(*argv, "use_first_pass", 14)) {
+ /* for pam_get_authtok, ignore */;
+ } else if (!strncmp(*argv, "try_first_pass", 14)) {
+ /* for pam_get_authtok, ignore */;
+ } else if (pwquality_set_option(pwq, *argv)) {
+ pam_syslog(pamh, LOG_ERR,
+ "pam_parse: unknown or broken option; %s", *argv);
+ }
+ }
+
+ opt->pwq = pwq;
+
+ return ctrl;
+}
+
+static const char *
+make_error_message(int rv, const char *crack_msg)
+{
+ switch(rv) {
+ case PWQ_ERROR_MEM_ALLOC:
+ return _("memory allocation error");
+ case PWQ_ERROR_SAME_PASSWORD:
+ return _("is the same as the old one");
+ case PWQ_ERROR_PALINDROME:
+ return _("is a palindrome");
+ case PWQ_ERROR_CASE_CHANGES_ONLY:
+ return _("case changes only");
+ case PWQ_ERROR_TOO_SIMILAR:
+ return _("is too similar to the old one");
+ case PWQ_ERROR_MIN_DIGITS:
+ case PWQ_ERROR_MIN_UPPERS:
+ case PWQ_ERROR_MIN_LOWERS:
+ case PWQ_ERROR_MIN_OTHERS:
+ case PWQ_ERROR_MIN_LENGTH:
+ return _("is too simple");
+ case PWQ_ERROR_ROTATED:
+ return _("is rotated");
+ case PWQ_ERROR_MIN_CLASSES:
+ return _("not enough character classes");
+ case PWQ_ERROR_MAX_CONSECUTIVE:
+ return _("contains too many same characters consecutively");
+ case PWQ_ERROR_EMPTY_PASSWORD:
+ return _("No password supplied");
+ case PWQ_ERROR_CRACKLIB_CHECK:
+ return crack_msg;
+ default:
+ return _("Error in service module");
+ }
+}
+
+PAM_EXTERN int
+pam_sm_chauthtok(pam_handle_t *pamh, int flags,
+ int argc, const char **argv)
+{
+ int ctrl;
+ struct module_options options;
+
+ memset(&options, 0, sizeof(options));
+ options.retry_times = CO_RETRY_TIMES;
+
+ ctrl = _pam_parse(pamh, &options, argc, argv);
+ if (ctrl < 0)
+ return PAM_BUF_ERR;
+
+ if (flags & PAM_PRELIM_CHECK) {
+ /* Check for passwd dictionary
+ * We cannot do that, since the original path is compiled
+ * into the cracklib library and we don't know it.
+ */
+ return PAM_SUCCESS;
+ } else if (flags & PAM_UPDATE_AUTHTOK) {
+ int retval;
+ const void *oldtoken;
+ int tries;
+
+ retval = pam_get_item(pamh, PAM_OLDAUTHTOK, &oldtoken);
+ if (retval != PAM_SUCCESS) {
+ if (ctrl & PAM_DEBUG_ARG)
+ pam_syslog(pamh, LOG_ERR, "Can not get old passwd");
+ oldtoken = NULL;
+ }
+
+ tries = 0;
+ while (tries < options.retry_times) {
+ const char *crack_msg;
+ const char *newtoken = NULL;
+
+ tries++;
+
+ /* Planned modus operandi:
+ * Get a passwd.
+ * Verify it against libpwquality.
+ * If okay get it a second time.
+ * Check to be the same with the first one.
+ * set PAM_AUTHTOK and return
+ */
+
+ retval = pam_get_authtok_noverify(pamh, &newtoken, NULL);
+ if (retval != PAM_SUCCESS) {
+ pam_syslog(pamh, LOG_ERR, "pam_get_authtok_noverify returned error: %s",
+ pam_strerror(pamh, retval));
+ continue;
+ } else if (newtoken == NULL) { /* user aborted password change, quit */
+ return PAM_AUTHTOK_ERR;
+ }
+
+ /* now test this passwd against libpwquality */
+ retval = pwquality_check(options.pwq, newtoken, oldtoken, &crack_msg);
+
+ if (retval < 0) {
+ const char *msg;
+ msg = make_error_message(retval, crack_msg);
+ if (ctrl & PAM_DEBUG_ARG)
+ pam_syslog(pamh, LOG_DEBUG, "bad password: %s", msg);
+ pam_error(pamh, _("BAD PASSWORD: %s"), msg);
+
+ if (getuid() || (flags & PAM_CHANGE_EXPIRED_AUTHTOK)) {
+ pam_set_item(pamh, PAM_AUTHTOK, NULL);
+ retval = PAM_AUTHTOK_ERR;
+ continue;
+ }
+ } else {
+ if (ctrl & PAM_DEBUG_ARG)
+ pam_syslog(pamh, LOG_DEBUG, "password score: %d", retval);
+ }
+
+ retval = pam_get_authtok_verify(pamh, &newtoken, NULL);
+ if (retval != PAM_SUCCESS) {
+ pam_syslog(pamh, LOG_ERR, "pam_get_authtok_verify returned error: %s",
+ pam_strerror(pamh, retval));
+ pam_set_item(pamh, PAM_AUTHTOK, NULL);
+ continue;
+ } else if (newtoken == NULL) { /* user aborted password change, quit */
+ return PAM_AUTHTOK_ERR;
+ }
+
+ return PAM_SUCCESS;
+ }
+
+ pam_set_item (pamh, PAM_AUTHTOK, NULL);
+
+ /* if we have only one try, we can use the real reason,
+ * else say that there were too many tries. */
+ if (options.retry_times > 1)
+ return PAM_MAXTRIES;
+ else
+ return retval;
+ } else {
+ if (ctrl & PAM_DEBUG_ARG)
+ pam_syslog(pamh, LOG_NOTICE, "UNKNOWN flags setting %02X",flags);
+ }
+
+ return PAM_SERVICE_ERR;
+}
+
+
+
+#ifdef PAM_STATIC
+/* static module data */
+struct pam_module _pam_pwquality_modstruct = {
+ "pam_pwquality",
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ pam_sm_chauthtok
+};
+#endif
+
+/*
+ * Copyright (c) Cristian Gafton <gafton@redhat.com>, 1996.
+ * All rights reserved
+ * Copyright (c) Red Hat, Inc, 2011
+ * Copyright (c) Tomas Mraz <tm@t8m.info>, 2011
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, and the entire permission notice in its entirety,
+ * including the disclaimer of warranties.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * ALTERNATIVELY, this product may be distributed under the terms of
+ * the GNU Public License, in which case the provisions of the GPL are
+ * required INSTEAD OF the above restrictions. (This clause is
+ * necessary due to a potential bad interaction between the GPL and
+ * the restrictions contained in a BSD-style copyright.)
+ *
+ * THIS SOFTWARE IS PROVIDED `AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * The following copyright was appended for the long password support
+ * added with the libpam 0.58 release:
+ *
+ * Modificaton Copyright (c) Philip W. Dalrymple III <pwd@mdtsoft.com>
+ * 1997. All rights reserved
+ *
+ * THE MODIFICATION THAT PROVIDES SUPPORT FOR LONG PASSWORD TYPE CHECKING TO
+ * THIS SOFTWARE IS PROVIDED `AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
diff --git a/src/pwmake.c b/src/pwmake.c
new file mode 100644
index 0000000..876ef1c
--- /dev/null
+++ b/src/pwmake.c
@@ -0,0 +1,108 @@
+/*
+ * pwmake - a simple tool for password generation
+ *
+ * See the end of the file for Copyright and License Information
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <errno.h>
+
+#include "pwquality.h"
+
+int
+usage(const char *progname) {
+ fprintf(stderr, _("Usage: %s <entropy-bits>\n"), progname);
+}
+
+static const char *
+make_error_message(int rv)
+{
+ switch(rv) {
+ case PWQ_ERROR_MEM_ALLOC:
+ return _("Memory allocation error");
+ case PWQ_ERROR_RNG:
+ return _("Cannot obtain random numbers from the RNG device");
+ case PWQ_ERROR_GENERATION_FAILED:
+ return _("Password generation failed - required entropy too low for settings");
+ default:
+ return _("Unknown error");
+ }
+}
+
+/* score a password */
+int
+main(int argc, char *argv[])
+{
+ pwquality_settings_t *pwq;
+ char *password;
+ int rv;
+ int bits;
+
+ if (argc != 2) {
+ usage(argv[0]);
+ exit(3);
+ }
+
+ bits = atoi(argv[1]);
+
+ pwq = pwquality_default_settings();
+ if (pwq == NULL) {
+ fprintf(stderr, "Error: %s\n", _("Error: Memory allocation error"));
+ exit(2);
+ }
+
+ pwquality_read_config(pwq, NULL);
+
+ rv = pwquality_generate(pwq, bits, &password);
+
+ if (rv != 0) {
+ fprintf(stderr, "Error: %s\n", make_error_message(rv));
+ exit(1);
+ }
+
+ printf("%s\n", password);
+ free(password);
+ return 0;
+}
+
+/*
+ * Copyright (c) Red Hat, Inc, 2011
+ * Copyright (c) Tomas Mraz <tm@t8m.info>, 2011
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, and the entire permission notice in its entirety,
+ * including the disclaimer of warranties.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * ALTERNATIVELY, this product may be distributed under the terms of
+ * the GNU Public License, in which case the provisions of the GPL are
+ * required INSTEAD OF the above restrictions. (This clause is
+ * necessary due to a potential bad interaction between the GPL and
+ * the restrictions contained in a BSD-style copyright.)
+ *
+ * THIS SOFTWARE IS PROVIDED `AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
diff --git a/src/pwqprivate.h b/src/pwqprivate.h
new file mode 100644
index 0000000..500c4e6
--- /dev/null
+++ b/src/pwqprivate.h
@@ -0,0 +1,88 @@
+/*
+ * libpwquality internal header
+ *
+ * Copyright (c) Red Hat, Inc, 2011
+ * Copyright (c) Tomas Mraz <tm@t8m.info>, 2011
+ *
+ * See the end of the file for the License Information
+ */
+
+#ifndef PWQPRIVATE_H
+#define PWQPRIVATE_H
+
+#include "pwquality.h"
+
+struct pwquality_settings {
+ int diff_ok;
+ int diff_ignore;
+ int min_length;
+ int dig_credit;
+ int up_credit;
+ int low_credit;
+ int oth_credit;
+ int min_class;
+ int max_repeat;
+ char *dict_path;
+
+};
+
+struct setting_mapping {
+ const char *name;
+ int id;
+ int type;
+};
+
+#define PWQ_DEFAULT_DIFF_OK 5
+#define PWQ_DEFAULT_DIFF_IGNORE 23
+#define PWQ_DEFAULT_MIN_LENGTH 9
+#define PWQ_DEFAULT_DIG_CREDIT 1
+#define PWQ_DEFAULT_UP_CREDIT 1
+#define PWQ_DEFAULT_LOW_CREDIT 1
+#define PWQ_DEFAULT_OTH_CREDIT 1
+
+#define PWQ_TYPE_INT 1
+#define PWQ_TYPE_STR 2
+#define PWQ_TYPE_SET 3
+
+#define PWQ_BASE_MIN_LENGTH 6 /* used when lower than this value of min len is set */
+#define PWQ_NUM_CLASSES 4
+#define PWQ_NUM_GENERATION_TRIES 3 /* how many times to try to generate the random password if it fails the check */
+
+#ifndef PWQUALITY_DEFAULT_CFGFILE
+#define PWQUALITY_DEFAULT_CFGFILE "/etc/security/pwquality.conf"
+#endif
+
+#endif /* PWQPRIVATE_H */
+
+/*
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, and the entire permission notice in its entirety,
+ * including the disclaimer of warranties.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * ALTERNATIVELY, this product may be distributed under the terms of
+ * the GNU Public License, in which case the provisions of the GPL are
+ * required INSTEAD OF the above restrictions. (This clause is
+ * necessary due to a potential bad interaction between the GPL and
+ * the restrictions contained in a BSD-style copyright.)
+ *
+ * THIS SOFTWARE IS PROVIDED `AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
diff --git a/src/pwquality.h b/src/pwquality.h
new file mode 100644
index 0000000..5e7105b
--- /dev/null
+++ b/src/pwquality.h
@@ -0,0 +1,139 @@
+/*
+ * libpwquality main API code header
+ *
+ * Copyright (c) Red Hat, Inc, 2011
+ * Copyright (c) Tomas Mraz <tm@t8m.info>, 2011
+ *
+ * See the end of the file for the License Information
+ */
+
+#ifndef PWQUALITY_H
+#define PWQUALITY_H
+
+#define PWQ_SETTING_DIFF_OK 1
+#define PWQ_SETTING_DIFF_IGNORE 2
+#define PWQ_SETTING_MIN_LENGTH 3
+#define PWQ_SETTING_DIG_CREDIT 4
+#define PWQ_SETTING_UP_CREDIT 5
+#define PWQ_SETTING_LOW_CREDIT 6
+#define PWQ_SETTING_OTH_CREDIT 7
+#define PWQ_SETTING_MIN_CLASS 8
+#define PWQ_SETTING_MAX_REPEAT 9
+#define PWQ_SETTING_DICT_PATH 10
+
+#define PWQ_MAX_ENTROPY_BITS 256
+#define PWQ_MIN_ENTROPY_BITS 56
+
+#define PWQ_ERROR_SUCCESS 0 /* implicit, not used in the library code */
+#define PWQ_ERROR_FATAL_FAILURE -1
+#define PWQ_ERROR_INTEGER -2
+#define PWQ_ERROR_CFGFILE_OPEN -3
+#define PWQ_ERROR_CFGFILE_MALFORMED -4
+#define PWQ_ERROR_UNKNOWN_SETTING -5
+#define PWQ_ERROR_NON_INT_SETTING -6
+#define PWQ_ERROR_NON_STR_SETTING -7
+#define PWQ_ERROR_MEM_ALLOC -8
+#define PWQ_ERROR_TOO_SIMILAR -9
+#define PWQ_ERROR_MIN_DIGITS -10
+#define PWQ_ERROR_MIN_UPPERS -11
+#define PWQ_ERROR_MIN_LOWERS -12
+#define PWQ_ERROR_MIN_OTHERS -13
+#define PWQ_ERROR_MIN_LENGTH -14
+#define PWQ_ERROR_PALINDROME -15
+#define PWQ_ERROR_CASE_CHANGES_ONLY -16
+#define PWQ_ERROR_ROTATED -17
+#define PWQ_ERROR_MIN_CLASSES -18
+#define PWQ_ERROR_MAX_CONSECUTIVE -19
+#define PWQ_ERROR_EMPTY_PASSWORD -20
+#define PWQ_ERROR_SAME_PASSWORD -21
+#define PWQ_ERROR_CRACKLIB_CHECK -22
+#define PWQ_ERROR_RNG -23
+#define PWQ_ERROR_GENERATION_FAILED -24
+
+typedef struct pwquality_settings pwquality_settings_t;
+
+/* returns default pwquality settings to be used in other library calls */
+pwquality_settings_t *
+pwquality_default_settings(void);
+
+/* frees pwquality settings data */
+void
+pwquality_free_settings(pwquality_settings_t *pwq);
+
+/* parse the configuration file (if NULL then the default one) */
+int
+pwquality_read_config(pwquality_settings_t *pwq, const char *cfgfile);
+
+/* useful for setting the options as configured on a pam module
+ * command line in form of <opt>=<val> */
+int
+pwquality_set_option(pwquality_settings_t *pwq, const char *option);
+
+/* set value of an integer setting */
+int
+pwquality_set_int_value(pwquality_settings_t *pwq, int setting, int value);
+
+/* set value of a string setting */
+int
+pwquality_set_str_value(pwquality_settings_t *pwq, int setting,
+ const char *value);
+
+/* get value of an integer setting, or -1 if setting unknown */
+int
+pwquality_get_int_value(pwquality_settings_t *pwq, int setting);
+
+/* get value of a string setting, or NULL if setting unknown */
+const char *
+pwquality_get_str_value(pwquality_settings_t *pwq, int setting);
+
+/* generate a random password of entropy_bits entropy and check it according to
+ * the settings */
+int
+pwquality_generate(pwquality_settings_t *pwq, int entropy_bits,
+ char **password);
+
+/* check the password according to the settings
+ * it returns either score <0-100>, negative error number,
+ * and in case of PWQ_ERROR_CRACKLIB also auxiliary
+ * error message returned from cracklib
+ * The old password is optional and can be NULL.
+ * The score depends on PWQ_SETTING_MIN_LENGTH. If it is set higher,
+ * the score for the same passwords will be lower. */
+int
+pwquality_check(pwquality_settings_t *pwq, const char *password,
+ const char *oldpassword, const char **error);
+
+#endif /* PWQUALITY_H */
+
+/*
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, and the entire permission notice in its entirety,
+ * including the disclaimer of warranties.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * ALTERNATIVELY, this product may be distributed under the terms of
+ * the GNU Public License, in which case the provisions of the GPL are
+ * required INSTEAD OF the above restrictions. (This clause is
+ * necessary due to a potential bad interaction between the GPL and
+ * the restrictions contained in a BSD-style copyright.)
+ *
+ * THIS SOFTWARE IS PROVIDED `AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
diff --git a/src/pwscore.c b/src/pwscore.c
new file mode 100644
index 0000000..9046bbb
--- /dev/null
+++ b/src/pwscore.c
@@ -0,0 +1,143 @@
+/*
+ * pwscore - a simple tool for password scoring
+ *
+ * See the end of the file for Copyright and License Information
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <errno.h>
+
+#include "pwquality.h"
+
+int
+usage(const char *progname) {
+ fprintf(stderr, _("Usage: %s\n"), progname);
+ fprintf(stderr, _(" The command reads the password to be scored from the standard input.\n"));
+}
+
+static const char *
+make_error_message(int rv, const char *crack_msg)
+{
+ static char buf[200];
+
+ switch(rv) {
+ case PWQ_ERROR_MEM_ALLOC:
+ return _("Memory allocation error");
+ case PWQ_ERROR_SAME_PASSWORD:
+ return _("The password is the same as the old one");
+ case PWQ_ERROR_PALINDROME:
+ return _("The password is a palindrome");
+ case PWQ_ERROR_CASE_CHANGES_ONLY:
+ return _("The password differs with case changes only");
+ case PWQ_ERROR_TOO_SIMILAR:
+ return _("The password is too similar to the old one");
+ case PWQ_ERROR_MIN_DIGITS:
+ return _("The password contains too few digits");
+ case PWQ_ERROR_MIN_UPPERS:
+ return _("The password contains too few uppercase letters");
+ case PWQ_ERROR_MIN_LOWERS:
+ return _("The password contains too few lowercase letters");
+ case PWQ_ERROR_MIN_OTHERS:
+ return _("The password contains too few non-alphanumeric characters");
+ case PWQ_ERROR_MIN_LENGTH:
+ return _("The password is too short");
+ case PWQ_ERROR_ROTATED:
+ return _("The password is just rotated old one");
+ case PWQ_ERROR_MIN_CLASSES:
+ return _("The password does not contain enough character classes");
+ case PWQ_ERROR_MAX_CONSECUTIVE:
+ return _("The password contains too many same characters consecutively");
+ case PWQ_ERROR_EMPTY_PASSWORD:
+ return _("No password supplied");
+ case PWQ_ERROR_CRACKLIB_CHECK:
+ snprintf(buf, sizeof(buf), _("The password fails the dictionary check - %s"), crack_msg);
+ return buf;
+ default:
+ return _("Unknown error");
+ }
+}
+
+
+/* score a password */
+int
+main(int argc, char *argv[])
+{
+ pwquality_settings_t *pwq;
+ int rv;
+ const char *crack_msg;
+ char buf[1024];
+ size_t len;
+
+ if (argc != 1) {
+ usage(argv[0]);
+ exit(3);
+ }
+
+ if (fgets(buf, sizeof(buf), stdin) == NULL || (len = strlen(buf)) == 0) {
+ fprintf(stderr, "Error: Could not obtain the password to be scored\n");
+ exit(4);
+ }
+ if (buf[len - 1] == '\n')
+ buf[len - 1] = '\0';
+
+ pwq = pwquality_default_settings();
+ if (pwq == NULL) {
+ fprintf(stderr, _("Error: %s\n"), make_error_message(PWQ_ERROR_MEM_ALLOC, NULL));
+ exit(2);
+ }
+
+ pwquality_read_config(pwq, NULL);
+
+ rv = pwquality_check(pwq, buf, NULL, &crack_msg);
+
+ if (rv < 0) {
+ fprintf(stderr, _("Password quality check failed: %s\n"),
+ make_error_message(rv, crack_msg));
+ exit(1);
+ }
+
+ printf("%d\n", rv);
+ return 0;
+}
+
+/*
+ * Copyright (c) Red Hat, Inc, 2011
+ * Copyright (c) Tomas Mraz <tm@t8m.info>, 2011
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, and the entire permission notice in its entirety,
+ * including the disclaimer of warranties.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * ALTERNATIVELY, this product may be distributed under the terms of
+ * the GNU Public License, in which case the provisions of the GPL are
+ * required INSTEAD OF the above restrictions. (This clause is
+ * necessary due to a potential bad interaction between the GPL and
+ * the restrictions contained in a BSD-style copyright.)
+ *
+ * THIS SOFTWARE IS PROVIDED `AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
diff --git a/src/settings.c b/src/settings.c
new file mode 100644
index 0000000..70c0c2e
--- /dev/null
+++ b/src/settings.c
@@ -0,0 +1,350 @@
+/*
+ * libpwquality main API code for reading and manipulation of settings
+ *
+ * See the end of the file for Copyright and License Information
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <limits.h>
+#include <ctype.h>
+#include <errno.h>
+
+#include "pwquality.h"
+#include "pwqprivate.h"
+
+/* returns default pwquality settings to be used in other library calls */
+pwquality_settings_t *
+pwquality_default_settings(void)
+{
+ pwquality_settings_t *pwq;
+
+ pwq = calloc(1, sizeof(*pwq));
+ if (!pwq)
+ return NULL;
+
+ pwq->diff_ok = PWQ_DEFAULT_DIFF_OK;
+ pwq->diff_ignore = PWQ_DEFAULT_DIFF_IGNORE;
+ pwq->min_length = PWQ_DEFAULT_MIN_LENGTH;
+ pwq->dig_credit = PWQ_DEFAULT_DIG_CREDIT;
+ pwq->up_credit = PWQ_DEFAULT_UP_CREDIT;
+ pwq->low_credit = PWQ_DEFAULT_LOW_CREDIT;
+ pwq->oth_credit = PWQ_DEFAULT_OTH_CREDIT;
+ /* pwq->dict_path = NULL; unneeded */
+
+ return pwq;
+}
+
+/* frees pwquality settings data */
+void
+pwquality_free_settings(pwquality_settings_t *pwq)
+{
+ if (pwq) {
+ free(pwq->dict_path);
+ free(pwq);
+ }
+}
+
+
+static const struct setting_mapping s_map[] = {
+ { "difok", PWQ_SETTING_DIFF_OK, PWQ_TYPE_INT},
+ { "difignore", PWQ_SETTING_DIFF_IGNORE, PWQ_TYPE_INT},
+ { "minlen", PWQ_SETTING_MIN_LENGTH, PWQ_TYPE_INT},
+ { "dcredit", PWQ_SETTING_DIG_CREDIT, PWQ_TYPE_INT},
+ { "ucredit", PWQ_SETTING_UP_CREDIT, PWQ_TYPE_INT},
+ { "lcredit", PWQ_SETTING_LOW_CREDIT, PWQ_TYPE_INT},
+ { "ocredit", PWQ_SETTING_OTH_CREDIT, PWQ_TYPE_INT},
+ { "minclass", PWQ_SETTING_MIN_CLASS, PWQ_TYPE_INT},
+ { "maxrepeat", PWQ_SETTING_MAX_REPEAT, PWQ_TYPE_INT},
+ { "dictpath", PWQ_SETTING_DICT_PATH, PWQ_TYPE_STR}
+};
+
+/* set setting name with value */
+static int
+set_name_value(pwquality_settings_t *pwq, const char *name, const char *value)
+{
+ int i;
+ long val;
+ char *endptr;
+
+ for (i = 0; i < sizeof(s_map)/sizeof(s_map[0]); i++) {
+ if (strcasecmp(s_map[i].name, name) == 0) {
+ switch(s_map[i].type) {
+ case PWQ_TYPE_INT:
+ errno = 0;
+ val = strtol(value, &endptr, 10);
+ if (errno != 0 || *value == '\0' ||
+ *endptr != '\0' || val >= INT_MAX || val <= INT_MIN) {
+ return PWQ_ERROR_INTEGER;
+ }
+ return pwquality_set_int_value(pwq, s_map[i].id,
+ (int)val);
+ case PWQ_TYPE_STR:
+ return pwquality_set_str_value(pwq, s_map[i].id,
+ value);
+ case PWQ_TYPE_SET:
+ return pwquality_set_int_value(pwq, s_map[i].id,
+ 1);
+ }
+ }
+ }
+ return PWQ_ERROR_UNKNOWN_SETTING;
+}
+
+#define PWQSETTINGS_MAX_LINELEN 1023
+
+/* parse the configuration file (if NULL then the default one) */
+int
+pwquality_read_config(pwquality_settings_t *pwq, const char *cfgfile)
+{
+ FILE *f;
+ char linebuf[PWQSETTINGS_MAX_LINELEN+1];
+ int rv = 0;
+
+ if (cfgfile == NULL)
+ cfgfile = PWQUALITY_DEFAULT_CFGFILE;
+
+ f = fopen(cfgfile, "r");
+ if (f == NULL)
+ return PWQ_ERROR_CFGFILE_OPEN;
+
+ while (fgets(linebuf, sizeof(linebuf), f) != NULL) {
+ size_t len;
+ char *ptr;
+ char *name;
+
+ len = strlen(linebuf);
+ if (linebuf[len - 1] != '\n' && !feof(f)) {
+ (void) fclose(f);
+ return PWQ_ERROR_CFGFILE_MALFORMED;
+ }
+
+ if ((ptr=strchr(linebuf, '#')) != NULL) {
+ *ptr = '\0';
+ } else {
+ ptr = linebuf + len;
+ }
+
+ /* drop terminating whitespace including the \n */
+ do {
+ if (!isspace(*(ptr-1))) {
+ *ptr = '\0';
+ break;
+ }
+ --ptr;
+ } while (ptr > linebuf);
+
+ /* skip initial whitespace */
+ for (ptr = linebuf; isspace(*ptr); ptr++);
+ if (*ptr == '\0')
+ continue;
+
+ name = ptr;
+ while (*ptr != '\0') {
+ if (isspace(*ptr)) {
+ *ptr = '\0';
+ ++ptr;
+ break;
+ }
+ ++ptr;
+ }
+ while (*ptr != '\0') {
+ if (!isspace(*ptr)) {
+ break;
+ }
+ ++ptr;
+ }
+
+ if ((rv=set_name_value(pwq, name, ptr)) != 0) {
+ break;
+ }
+ }
+
+ (void)fclose(f);
+ return rv;
+}
+
+/* useful for setting the options as configured on a pam module
+ * command line in form of <opt>=<val> */
+int
+pwquality_set_option(pwquality_settings_t *pwq, const char *option)
+{
+ char name[80]; /* no options with name longer than that */
+ const char *value;
+ size_t len;
+
+ value = strchr(option, '=');
+ if (value == NULL) {
+ len = strlen(option);
+ value = option + len; /* just empty value */
+ } else {
+ len = value - option;
+ ++value;
+ }
+ if (len > sizeof(name) - 1)
+ return PWQ_ERROR_UNKNOWN_SETTING;
+
+ strncpy(name, option, len);
+ name[sizeof(name) - 1] = '\0';
+
+ return set_name_value(pwq, name, value);
+}
+
+/* set value of an integer setting */
+int
+pwquality_set_int_value(pwquality_settings_t *pwq, int setting, int value)
+{
+ switch(setting) {
+ case PWQ_SETTING_DIFF_OK:
+ pwq->diff_ok = value;
+ break;
+ case PWQ_SETTING_DIFF_IGNORE:
+ pwq->diff_ignore = value;
+ break;
+ case PWQ_SETTING_MIN_LENGTH:
+ if (value < PWQ_BASE_MIN_LENGTH)
+ value = PWQ_BASE_MIN_LENGTH;
+ pwq->min_length = value;
+ break;
+ case PWQ_SETTING_DIG_CREDIT:
+ pwq->dig_credit = value;
+ break;
+ case PWQ_SETTING_UP_CREDIT:
+ pwq->up_credit = value;
+ break;
+ case PWQ_SETTING_LOW_CREDIT:
+ pwq->low_credit = value;
+ break;
+ case PWQ_SETTING_OTH_CREDIT:
+ pwq->oth_credit = value;
+ break;
+ case PWQ_SETTING_MIN_CLASS:
+ if (value > PWQ_NUM_CLASSES)
+ value = PWQ_NUM_CLASSES;
+ pwq->min_class = value;
+ break;
+ case PWQ_SETTING_MAX_REPEAT:
+ pwq->max_repeat = value;
+ break;
+ default:
+ return PWQ_ERROR_NON_INT_SETTING;
+ }
+
+ return 0;
+}
+
+/* set value of a string setting */
+int
+pwquality_set_str_value(pwquality_settings_t *pwq, int setting,
+ const char *value)
+{
+ char *dup;
+
+ if (value == NULL || *value == '\0') {
+ dup = NULL;
+ } else {
+ dup = strdup(value);
+ if (dup == NULL)
+ return PWQ_ERROR_MEM_ALLOC;
+ }
+
+ switch(setting) {
+ case PWQ_SETTING_DICT_PATH:
+ pwq->dict_path = dup;
+ break;
+ default:
+ return PWQ_ERROR_NON_STR_SETTING;
+ }
+
+ return 0;
+}
+
+/* get value of an integer setting, or -1 if setting unknown */
+int
+pwquality_get_int_value(pwquality_settings_t *pwq, int setting)
+{
+ switch(setting) {
+ case PWQ_SETTING_DIFF_OK:
+ return pwq->diff_ok;
+ break;
+ case PWQ_SETTING_DIFF_IGNORE:
+ return pwq->diff_ignore;
+ break;
+ case PWQ_SETTING_MIN_LENGTH:
+ return pwq->min_length;
+ break;
+ case PWQ_SETTING_DIG_CREDIT:
+ return pwq->dig_credit;
+ break;
+ case PWQ_SETTING_UP_CREDIT:
+ return pwq->up_credit;
+ break;
+ case PWQ_SETTING_LOW_CREDIT:
+ return pwq->low_credit;
+ break;
+ case PWQ_SETTING_OTH_CREDIT:
+ return pwq->oth_credit;
+ break;
+ case PWQ_SETTING_MIN_CLASS:
+ return pwq->min_class;
+ break;
+ case PWQ_SETTING_MAX_REPEAT:
+ return pwq->max_repeat;
+ break;
+ default:
+ return -1;
+ }
+}
+
+/* get value of a string setting, or NULL if setting unknown */
+const char *
+pwquality_get_str_value(pwquality_settings_t *pwq, int setting)
+{
+ switch(setting) {
+ case PWQ_SETTING_DICT_PATH:
+ return pwq->dict_path;
+ break;
+ default:
+ return NULL;
+ }
+
+}
+
+/*
+ * Copyright (c) Red Hat, Inc, 2011
+ * Copyright (c) Tomas Mraz <tm@t8m.info>, 2011
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, and the entire permission notice in its entirety,
+ * including the disclaimer of warranties.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * ALTERNATIVELY, this product may be distributed under the terms of
+ * the GNU Public License, in which case the provisions of the GPL are
+ * required INSTEAD OF the above restrictions. (This clause is
+ * necessary due to a potential bad interaction between the GPL and
+ * the restrictions contained in a BSD-style copyright.)
+ *
+ * THIS SOFTWARE IS PROVIDED `AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */