| /* vi: set sw=4 ts=4: */ |
| /* |
| * Copyright 1989 - 1994, Julianne Frances Haugh <jockgrrl@austin.rr.com> |
| * Copyright 2006, Bernhard Fischer <busybox@busybox.net> |
| * 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, this list of conditions and the following disclaimer. |
| * 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. Neither the name of Julianne F. Haugh nor the names of its contributors |
| * may be used to endorse or promote products derived from this software |
| * without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY JULIE HAUGH AND CONTRIBUTORS ``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 JULIE HAUGH OR CONTRIBUTORS 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. |
| */ |
| |
| /* |
| * This version of obscure.c contains modifications to support "cracklib" |
| * by Alec Muffet (alec.muffett@uk.sun.com). You must obtain the Cracklib |
| * library source code for this function to operate. |
| */ |
| |
| #include <stdlib.h> |
| #include <stdio.h> |
| #include <string.h> |
| #include <ctype.h> |
| #include "libbb.h" |
| |
| /* |
| * can't be a palindrome - like `R A D A R' or `M A D A M' |
| */ |
| |
| static int palindrome(const char *newval) |
| { |
| int i, j; |
| |
| i = strlen(newval); |
| |
| for (j = 0; j < i; j++) |
| if (newval[i - j - 1] != newval[j]) |
| return 0; |
| |
| return 1; |
| } |
| |
| /* |
| * more than half of the characters are different ones. |
| */ |
| |
| static int similiar(const char *old, const char *newval) |
| { |
| int i, j; |
| |
| for (i = j = 0; newval[i] && old[i]; i++) |
| if (strchr(newval, old[i])) |
| j++; |
| |
| if (i >= j * 2) |
| return 0; |
| |
| return 1; |
| } |
| |
| /* |
| * a nice mix of characters. |
| */ |
| |
| static int simple(const char *newval) |
| { |
| #define digits 1 |
| #define uppers 2 |
| #define lowers 4 |
| #define others 8 |
| int c, is_simple = 0; |
| int size; |
| int i; |
| |
| for (i = 0; (c = *newval++) != 0; i++) { |
| if (isdigit(c)) |
| is_simple |= digits; |
| else if (isupper(c)) |
| is_simple |= uppers; |
| else if (islower(c)) |
| is_simple |= lowers; |
| else |
| is_simple |= others; |
| } |
| |
| /* |
| * The scam is this - a password of only one character type |
| * must be 8 letters long. Two types, 7, and so on. |
| */ |
| |
| size = 9; |
| if (is_simple & digits) |
| size--; |
| if (is_simple & uppers) |
| size--; |
| if (is_simple & lowers) |
| size--; |
| if (is_simple & others) |
| size--; |
| |
| if (size <= i) |
| return 0; |
| |
| return 1; |
| #undef digits |
| #undef uppers |
| #undef lowers |
| #undef others |
| } |
| |
| static char *str_lower(char *string) |
| { |
| char *cp; |
| |
| for (cp = string; *cp; cp++) |
| *cp = tolower(*cp); |
| return string; |
| } |
| |
| static const char * |
| password_check(const char *old, const char *newval, const struct passwd *pwdp) |
| { |
| const char *msg; |
| char *newmono, *wrapped; |
| int lenwrap; |
| |
| if (strcmp(newval, old) == 0) |
| return "no change"; |
| if (simple(newval)) |
| return "too simple"; |
| |
| msg = NULL; |
| newmono = str_lower(bb_xstrdup(newval)); |
| lenwrap = strlen(old); |
| wrapped = (char *) xmalloc(lenwrap * 2 + 1); |
| str_lower(strcpy(wrapped, old)); |
| |
| if (palindrome(newmono)) |
| msg = "a palindrome"; |
| |
| else if (strcmp(wrapped, newmono) == 0) |
| msg = "case changes only"; |
| |
| else if (similiar(wrapped, newmono)) |
| msg = "too similiar"; |
| |
| else if ( strstr(newval, pwdp->pw_name) ) |
| msg = "username in password"; |
| |
| else { |
| safe_strncpy(wrapped + lenwrap, wrapped, lenwrap + 1); |
| if (strstr(wrapped, newmono)) |
| msg = "rotated"; |
| } |
| |
| memset(newmono, 0, strlen(newmono)); |
| memset(wrapped, 0, lenwrap * 2); |
| free(newmono); |
| free(wrapped); |
| |
| return msg; |
| } |
| |
| static const char * |
| obscure_msg(const char *old, const char *newval, const struct passwd *pwdp) |
| { |
| int maxlen, oldlen, newlen; |
| char *new1, *old1; |
| const char *msg; |
| |
| oldlen = strlen(old); |
| newlen = strlen(newval); |
| |
| #if 0 /* why not check the password when set for the first time? --marekm */ |
| if (old[0] == '\0') |
| /* return (1); */ |
| return NULL; |
| #endif |
| |
| if (newlen < 5) |
| return "too short"; |
| |
| /* |
| * Remaining checks are optional. |
| */ |
| /* Not for us -- Sean |
| *if (!getdef_bool("OBSCURE_CHECKS_ENAB")) |
| * return NULL; |
| */ |
| msg = password_check(old, newval, pwdp); |
| if (msg) |
| return msg; |
| |
| /* The traditional crypt() truncates passwords to 8 chars. It is |
| possible to circumvent the above checks by choosing an easy |
| 8-char password and adding some random characters to it... |
| Example: "password$%^&*123". So check it again, this time |
| truncated to the maximum length. Idea from npasswd. --marekm */ |
| |
| maxlen = 8; |
| if (oldlen <= maxlen && newlen <= maxlen) |
| return NULL; |
| |
| new1 = (char *) bb_xstrdup(newval); |
| old1 = (char *) bb_xstrdup(old); |
| if (newlen > maxlen) |
| new1[maxlen] = '\0'; |
| if (oldlen > maxlen) |
| old1[maxlen] = '\0'; |
| |
| msg = password_check(old1, new1, pwdp); |
| |
| memset(new1, 0, newlen); |
| memset(old1, 0, oldlen); |
| free(new1); |
| free(old1); |
| |
| return msg; |
| } |
| |
| /* |
| * Obscure - see if password is obscure enough. |
| * |
| * The programmer is encouraged to add as much complexity to this |
| * routine as desired. Included are some of my favorite ways to |
| * check passwords. |
| */ |
| |
| extern int obscure(const char *old, const char *newval, const struct passwd *pwdp) |
| { |
| const char *msg = obscure_msg(old, newval, pwdp); |
| |
| /* if (msg) { */ |
| if (msg != NULL) { |
| printf("Bad password: %s.\n", msg); |
| /* return 0; */ |
| return 1; |
| } |
| /* return 1; */ |
| return 0; |
| } |