diff options
Diffstat (limited to 'Linux-PAM/modules/pam_cracklib/pam_cracklib.c')
-rw-r--r-- | Linux-PAM/modules/pam_cracklib/pam_cracklib.c | 850 |
1 files changed, 850 insertions, 0 deletions
diff --git a/Linux-PAM/modules/pam_cracklib/pam_cracklib.c b/Linux-PAM/modules/pam_cracklib/pam_cracklib.c new file mode 100644 index 00000000..532a72b2 --- /dev/null +++ b/Linux-PAM/modules/pam_cracklib/pam_cracklib.c @@ -0,0 +1,850 @@ +/* + * pam_cracklib module + */ + +/* + * 0.9. switch to using a distance algorithm in similar() + * 0.86. added support for setting minimum numbers of digits, uppers, + * lowers, and others + * 0.85. added six new options to use this with long passwords. + * 0.8. tidied output and improved D(()) usage for debugging. + * 0.7. added support for more obscure checks for new passwd. + * 0.6. root can reset user passwd to any values (it's only warned) + * 0.5. supports retries - 'retry=N' argument + * 0.4. added argument 'type=XXX' for 'New XXX password' prompt + * 0.3. Added argument 'debug' + * 0.2. new password is feeded to cracklib for verify after typed once + * 0.1. First release + */ + +/* + * Written by Cristian Gafton <gafton@redhat.com> 1996/09/10 + * Long password support by Philip W. Dalrymple <pwd@mdtsoft.com> 1997/07/18 + * See the end of the file for Copyright Information + * + * Modification for long password systems (>8 chars). The original + * module had problems when used in a md5 password system in that it + * allowed too short passwords but required that at least half of the + * bytes in the new password did not appear in the old one. this + * action is still the default and the changes should not break any + * current user. This modification adds 6 new options, one to set the + * number of bytes in the new password that are not in the old one, + * the other five to control the length checking, these are all + * documented (or will be before anyone else sees this code) in the PAM + * S.A.G. in the section on the cracklib module. + */ + +#include "config.h" + +#include <stdio.h> +#ifdef HAVE_CRYPT_H +# include <crypt.h> +#endif +#include <unistd.h> +#include <stdlib.h> +#include <string.h> +#include <syslog.h> +#include <stdarg.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <ctype.h> +#include <limits.h> + +#ifdef HAVE_CRACK_H +#include <crack.h> +#else +extern char *FascistCheck(char *pw, const char *dictpath); +#endif + +#ifndef CRACKLIB_DICTS +#define CRACKLIB_DICTS NULL +#endif + +/* 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 use_authtok; + char prompt_type[BUFSIZ]; + 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 +#define CO_USE_AUTHTOK 0 + +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)) + strncpy(opt->prompt_type, *argv+5, sizeof(opt->prompt_type) - 1); + 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,"use_authtok",11)) { + opt->use_authtok = 1; + } 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); + } + } + opt->prompt_type[sizeof(opt->prompt_type) - 1] = '\0'; + + return ctrl; +} + +/* Helper functions */ + +/* use this to free strings. ESPECIALLY password strings */ +static char *_pam_delete(register char *xx) +{ + _pam_overwrite(xx); + free(xx); + return NULL; +} + +/* + * 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 char * str_lower(char *string) +{ + char *cp; + + 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 *msg = NULL; + char *oldmono = NULL, *newmono, *wrapped = NULL; + + if (old && strcmp(new, old) == 0) { + msg = _("is the same as the old one"); + return msg; + } + + newmono = str_lower(x_strdup(new)); + if (old) { + oldmono = str_lower(x_strdup(old)); + wrapped = malloc(strlen(oldmono) * 2 + 1); + strcpy (wrapped, oldmono); + strcat (wrapped, oldmono); + } + + if (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"); + + memset(newmono, 0, strlen(newmono)); + free(newmono); + if (old) { + memset(oldmono, 0, strlen(oldmono)); + memset(wrapped, 0, strlen(wrapped)); + free(oldmono); + free(wrapped); + } + + return msg; +} + + +#define OLD_PASSWORDS_FILE "/etc/security/opasswd" + +static const char * check_old_password(const char *forwho, const char *newpass) +{ + static char buf[16384]; + char *s_luser, *s_uid, *s_npas, *s_pas; + const char *msg = NULL; + FILE *opwfile; + + opwfile = fopen(OLD_PASSWORDS_FILE, "r"); + if (opwfile == NULL) + return NULL; + + while (fgets(buf, 16380, opwfile)) { + if (!strncmp(buf, forwho, strlen(forwho))) { + char *sptr; + buf[strlen(buf)-1] = '\0'; + s_luser = strtok_r(buf, ":,", &sptr); + s_uid = strtok_r(NULL, ":,", &sptr); + s_npas = strtok_r(NULL, ":,", &sptr); + s_pas = strtok_r(NULL, ":,", &sptr); + while (s_pas != NULL) { + if (!strcmp(crypt(newpass, s_pas), s_pas)) { + msg = _("has been already used"); + break; + } + s_pas = strtok_r(NULL, ":,", &sptr); + } + break; + } + } + fclose(opwfile); + + 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 void *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; + } + + /* + * if one wanted to hardwire authentication token strength + * checking this would be the place + */ + msg = password_check(opt, pass_old, pass_new); + if (!msg) { + retval = pam_get_item(pamh, PAM_USER, &user); + if (retval != PAM_SUCCESS || user == NULL) { + if (ctrl & PAM_DEBUG_ARG) + pam_syslog(pamh,LOG_ERR,"Can not get username"); + return PAM_AUTHTOK_ERR; + } + msg = check_old_password(user, pass_new); + } + + 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.use_authtok = CO_USE_AUTHTOK; + memset(options.prompt_type, 0, BUFSIZ); + strcpy(options.prompt_type,"UNIX"); + 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; + char *token1, *token2, *resp; + const void *oldtoken; + + 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; + retval = PAM_SUCCESS; + } + + do { + /* + * make sure nothing inappropriate gets returned + */ + token1 = token2 = NULL; + + if (!options.retry_times) { + D(("returning %s because maxtries reached", + pam_strerror(pamh, retval))); + return retval; + } + + /* 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 + */ + + if (options.use_authtok == 1) { + const void *item = NULL; + + retval = pam_get_item(pamh, PAM_AUTHTOK, &item); + if (retval != PAM_SUCCESS) { + /* very strange. */ + pam_syslog(pamh, LOG_ALERT, + "pam_get_item returned error to pam_cracklib"); + } else if (item != NULL) { /* we have a password! */ + token1 = x_strdup(item); + item = NULL; + } else { + retval = PAM_AUTHTOK_RECOVERY_ERR; /* didn't work */ + } + + } else { + /* Prepare to ask the user for the first time */ + resp = NULL; + retval = pam_prompt (pamh, PAM_PROMPT_ECHO_OFF, &resp, + PROMPT1, options.prompt_type, + options.prompt_type[0]?" ":""); + + if (retval == PAM_SUCCESS) { /* a good conversation */ + token1 = x_strdup(resp); + if (token1 == NULL) { + pam_syslog(pamh, LOG_NOTICE, + "could not recover authentication token 1"); + retval = PAM_AUTHTOK_RECOVERY_ERR; + } + /* + * tidy up the conversation (resp_retcode) is ignored + */ + _pam_drop(resp); + } else { + retval = (retval == PAM_SUCCESS) ? + PAM_AUTHTOK_RECOVERY_ERR:retval ; + } + } + + if (retval != PAM_SUCCESS) { + if (ctrl & PAM_DEBUG_ARG) + pam_syslog(pamh,LOG_DEBUG,"unable to obtain a password"); + continue; + } + + D(("testing password, retval = %s", pam_strerror(pamh, retval))); + /* now test this passwd against cracklib */ + { + const char *crack_msg; + + D(("against cracklib")); + if ((crack_msg = FascistCheck(token1,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)) + retval = PAM_AUTHTOK_ERR; + else + retval = PAM_SUCCESS; + } else { + /* check it for strength too... */ + D(("for strength")); + retval = _pam_unix_approve_pass (pamh, ctrl, &options, + oldtoken, token1); + if (retval != PAM_SUCCESS) { + if (getuid() || (flags & PAM_CHANGE_EXPIRED_AUTHTOK)) + retval = PAM_AUTHTOK_ERR; + else + retval = PAM_SUCCESS; + } + } + } + + D(("after testing: retval = %s", pam_strerror(pamh, retval))); + /* if cracklib/strength check said it is a bad passwd... */ + if ((retval != PAM_SUCCESS) && (retval != PAM_IGNORE)) { + int temp_unused; + + temp_unused = pam_set_item(pamh, PAM_AUTHTOK, NULL); + token1 = _pam_delete(token1); + continue; + } + + /* Now we have a good passwd. Ask for it once again */ + + if (options.use_authtok == 0) { + resp = NULL; + retval = pam_prompt (pamh, PAM_PROMPT_ECHO_OFF, &resp, + PROMPT2, options.prompt_type, + options.prompt_type[0]?" ":""); + if (retval == PAM_SUCCESS) { /* a good conversation */ + token2 = x_strdup(resp); + if (token2 == NULL) { + pam_syslog(pamh,LOG_NOTICE, + "could not recover authentication token 2"); + retval = PAM_AUTHTOK_RECOVERY_ERR; + } + /* + * tidy up the conversation (resp_retcode) is ignored + */ + _pam_drop(resp); + } + + /* No else, the a retval == PAM_SUCCESS path can change retval + to a failure code. */ + if (retval != PAM_SUCCESS) { + if (ctrl & PAM_DEBUG_ARG) + pam_syslog(pamh,LOG_DEBUG,"unable to obtain retyped password"); + continue; + } + + /* Hopefully now token1 and token2 the same password ... */ + if (strcmp(token1,token2) != 0) { + /* tell the user */ + pam_error(pamh, "%s", MISTYPED_PASS); + token1 = _pam_delete(token1); + token2 = _pam_delete(token2); + pam_set_item(pamh, PAM_AUTHTOK, NULL); + if (ctrl & PAM_DEBUG_ARG) + pam_syslog(pamh,LOG_NOTICE,"Password mistyped"); + retval = PAM_AUTHTOK_RECOVERY_ERR; + continue; + } + + /* Yes, the password was typed correct twice + * we store this password as an item + */ + + { + const void *item = NULL; + + retval = pam_set_item(pamh, PAM_AUTHTOK, token1); + + /* clean up */ + token1 = _pam_delete(token1); + token2 = _pam_delete(token2); + + if ( (retval != PAM_SUCCESS) || + ((retval = pam_get_item(pamh, PAM_AUTHTOK, &item) + ) != PAM_SUCCESS) ) { + pam_syslog(pamh, LOG_CRIT, "error manipulating password"); + continue; + } + item = NULL; /* break link to password */ + return PAM_SUCCESS; + } + } + + } while (options.retry_times--); + + } 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 + * + * 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. + */ |