root/usr/src/lib/pam_modules/authtok_check/rules.c
/*
 * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

/*
 * This program is copyright Alec Muffett 1993. The author disclaims all
 * responsibility or liability with respect to it's usage or its effect
 * upon hardware or computer systems, and maintains copyright as set out
 * in the "LICENCE" document which accompanies distributions of Crack v4.0
 * and upwards.
 */

#include "packer.h"


#define RULE_NOOP       ':'
#define RULE_PREPEND    '^'
#define RULE_APPEND     '$'
#define RULE_REVERSE    'r'
#define RULE_UPPERCASE  'u'
#define RULE_LOWERCASE  'l'
#define RULE_PLURALISE  'p'
#define RULE_CAPITALISE 'c'
#define RULE_DUPLICATE  'd'
#define RULE_REFLECT    'f'
#define RULE_SUBSTITUTE 's'
#define RULE_MATCH      '/'
#define RULE_NOT        '!'
#define RULE_LT         '<'
#define RULE_GT         '>'
#define RULE_EXTRACT    'x'
#define RULE_OVERSTRIKE 'o'
#define RULE_INSERT     'i'
#define RULE_EQUALS     '='
#define RULE_PURGE      '@'
#define RULE_CLASS      '?'     /* class rule? socialist ethic in cracker? */
#define RULE_DFIRST     '['
#define RULE_DLAST      ']'
#define RULE_MFIRST     '('
#define RULE_MLAST      ')'

int
Suffix(char *myword, char *suffix)
{
        register int i;
        register int j;

        i = strlen(myword);
        j = strlen(suffix);

        if (i > j) {
                return (STRCMP((myword + i - j), suffix));
        } else {
                return (-1);
        }
}

char *
Reverse(register char *str)             /* return a pointer to a reversal */
{
        register int i;
        register int j;
        static char area[PATH_MAX];

        j = i = strlen(str);
        while (*str) {
                area[--i] = *str++;
        }
        area[j] = '\0';
        return (area);
}

char *
Uppercase(register char *str)           /* return a pointer to an uppercase */
{
        register char *ptr;
        static char area[PATH_MAX];

        ptr = area;
        while (*str) {
                *(ptr++) = CRACK_TOUPPER(*str);
                str++;
        }
        *ptr = '\0';

        return (area);
}

char *
Lowercase(register char *str)           /* return a pointer to an lowercase */
{
        register char *ptr;
        static char area[PATH_MAX];

        ptr = area;
        while (*str) {
                *(ptr++) = CRACK_TOLOWER(*str);
                str++;
        }
        *ptr = '\0';

        return (area);
}

char *
Capitalise(register char *str)          /* return a pointer to an capitalised */
{
        register char *ptr;
        static char area[PATH_MAX];

        ptr = area;

        while (*str) {
                *(ptr++) = CRACK_TOLOWER(*str);
                str++;
        }

        *ptr = '\0';
        area[0] = CRACK_TOUPPER(area[0]);
        return (area);
}

char *
Pluralise(register char *string)        /* returns a pointer to a plural */
{
        register int length;
        static char area[PATH_MAX];

        length = strlen(string);
        (void) strlcpy(area, string, PATH_MAX);

        if (!Suffix(string, "ch") ||
            !Suffix(string, "ex") ||
            !Suffix(string, "ix") ||
            !Suffix(string, "sh") ||
            !Suffix(string, "ss")) {
                /* bench -> benches */
                (void) strcat(area, "es");
        } else if (length > 2 && string[length - 1] == 'y') {
                if (strchr("aeiou", string[length - 2])) {
                        /* alloy -> alloys */
                        (void) strcat(area, "s");
                } else {
                        /* gully -> gullies */
                        (void) strcpy(area + length - 1, "ies");
                }
        } else if (string[length - 1] == 's') {
                /* bias -> biases */
                (void) strcat(area, "es");
        } else {
                /* catchall */
                (void) strcat(area, "s");
        }

        return (area);
}

char *
Substitute(register char *string, register char old,
        register char new)      /* returns pointer to a swapped about copy */
{
        register char *ptr;
        static char area[PATH_MAX];

        ptr = area;
        while (*string) {
                *(ptr++) = (*string == old ? new : *string);
                string++;
        }
        *ptr = '\0';
        return (area);
}

/* returns pointer to a purged copy */
char *
Purge(register char *string, register char target)
{
        register char *ptr;
        static char area[PATH_MAX];
        ptr = area;
        while (*string) {
                if (*string != target) {
                        *(ptr++) = *string;
                }
                string++;
        }
        *ptr = '\0';
        return (area);
}
/* -------- CHARACTER CLASSES START HERE -------- */

/*
 * this function takes two inputs, a class identifier and a character, and
 * returns non-null if the given character is a member of the class, based
 * upon restrictions set out below
 */

int
MatchClass(register char class, register char input)
{
        register char c;
        register int retval;

        retval = 0;

        switch (class) {
        /* ESCAPE */

                case '?':                       /* ?? -> ? */
                        if (input == '?') {
                                retval = 1;
                        }
                        break;

        /* ILLOGICAL GROUPINGS (ie: not in ctype.h) */

                case 'V':
                case 'v':                       /* vowels */
                        c = CRACK_TOLOWER(input);
                        if (strchr("aeiou", c)) {
                                retval = 1;
                        }
                        break;

                case 'C':
                case 'c':                       /* consonants */
                        c = CRACK_TOLOWER(input);
                        if (strchr("bcdfghjklmnpqrstvwxyz", c)) {
                                retval = 1;
                        }
                        break;

                case 'W':
                case 'w':                       /* whitespace */
                        if (strchr("\t ", input)) {
                                retval = 1;
                        }
                        break;

                case 'P':
                case 'p':                       /* punctuation */
                        if (strchr(".`,:;'!?\"", input)) {
                                retval = 1;
                        }
                        break;

                case 'S':
                case 's':                       /* symbols */
                        if (strchr("$%%^&*()-_+=|\\[]{}#@/~", input)) {
                                retval = 1;
                        }
                        break;

                /* LOGICAL GROUPINGS */

                case 'L':
                case 'l':                       /* lowercase */
                        if (islower(input)) {
                                retval = 1;
                        }
                        break;

                case 'U':
                case 'u':                       /* uppercase */
                        if (isupper(input)) {
                                retval = 1;
                        }
                        break;

                case 'A':
                case 'a':                       /* alphabetic */
                        if (isalpha(input)) {
                                retval = 1;
                        }
                        break;

                case 'X':
                case 'x':                       /* alphanumeric */
                        if (isalnum(input)) {
                                retval = 1;
                        }
                        break;

                case 'D':
                case 'd':                       /* digits */
                        if (isdigit(input)) {
                                retval = 1;
                        }
                        break;
        }

        if (isupper(class)) {
                return (!retval);
        }
        return (retval);
}

char *
PolyStrchr(register char *string, register char class)
{
        while (*string) {
                if (MatchClass(class, *string)) {
                        return (string);
                }
                string++;
        }
        return ((char *)0);
}

/* returns pointer to a swapped about copy */
char *
PolySubst(register char *string, register char class, register char new)
{
        register char *ptr;
        static char area[PATH_MAX];

        ptr = area;
        while (*string) {
                *(ptr++) = (MatchClass(class, *string) ? new : *string);
                string++;
        }
        *ptr = '\0';
        return (area);
}

/* returns pointer to a purged copy */
char *
PolyPurge(register char *string, register char class)
{
        register char *ptr;
        static char area[PATH_MAX];

        ptr = area;
        while (*string) {
                if (!MatchClass(class, *string)) {
                        *(ptr++) = *string;
                }
                string++;
        }
        *ptr = '\0';
        return (area);
}
/* -------- BACK TO NORMALITY -------- */

int
Char2Int(char character)
{
        if (isdigit(character)) {
                return (character - '0');
        } else if (islower(character)) {
                return (character - 'a' + 10);
        } else if (isupper(character)) {
                return (character - 'A' + 10);
        }
        return (-1);
}

/* returns a pointer to a controlled Mangle */
char *
Mangle(char *input, char *control)
{
        int limit;
        register char *ptr;
        static char area[PATH_MAX];
        char area2[PATH_MAX];

        area[0] = '\0';
        (void) strlcpy(area, input, PATH_MAX);

        for (ptr = control; *ptr; ptr++) {
                switch (*ptr) {
                        case RULE_NOOP:
                                break;
                        case RULE_REVERSE:
                                (void) strlcpy(area, Reverse(area), PATH_MAX);
                                break;
                        case RULE_UPPERCASE:
                                (void) strlcpy(area, Uppercase(area), PATH_MAX);
                                break;
                        case RULE_LOWERCASE:
                                (void) strlcpy(area, Lowercase(area), PATH_MAX);
                                break;
                        case RULE_CAPITALISE:
                                (void) strlcpy(area, Capitalise(area),
                                    PATH_MAX);
                                break;
                        case RULE_PLURALISE:
                                (void) strlcpy(area, Pluralise(area), PATH_MAX);
                                break;
                        case RULE_REFLECT:
                                (void) strlcat(area, Reverse(area), PATH_MAX);
                                break;
                        case RULE_DUPLICATE:
                                (void) strlcpy(area2, area, PATH_MAX);
                                (void) strlcat(area, area2, PATH_MAX);
                                break;
                        case RULE_GT:
                                if (!ptr[1]) {
                                        return ((char *)0);
                                } else {
                                        limit = Char2Int(*(++ptr));
                                        if (limit < 0) {
                                                return ((char *)0);
                                        }
                                        if (strlen(area) <= limit) {
                                                return ((char *)0);
                                        }
                                }
                                break;
                        case RULE_LT:
                                if (!ptr[1]) {
                                        return ((char *)0);
                                } else {
                                        limit = Char2Int(*(++ptr));
                                        if (limit < 0) {
                                                return ((char *)0);
                                        }
                                        if (strlen(area) >= limit) {
                                                return ((char *)0);
                                        }
                                }
                                break;
                        case RULE_PREPEND:
                                if (!ptr[1]) {
                                        return ((char *)0);
                                } else {
                                        area2[0] = *(++ptr);
                                        (void) strlcpy(area2 + 1, area,
                                            PATH_MAX);
                                        (void) strlcpy(area, area2, PATH_MAX);
                                }
                                break;
                        case RULE_APPEND:
                                if (!ptr[1]) {
                                        return ((char *)0);
                                } else {
                                        register char *string;

                                        string = area;
                                        while (*(string++));
                                        string[-1] = *(++ptr);
                                        *string = '\0';
                                }
                                break;
                        case RULE_EXTRACT:
                                if (!ptr[1] || !ptr[2]) {
                                        return ((char *)0);
                                } else {
                                        register int i;
                                        int start;
                                        int length;

                                        start = Char2Int(*(++ptr));
                                        length = Char2Int(*(++ptr));
                                        if (start < 0 || length < 0) {
                                                return ((char *)0);
                                        }
                                        (void) strlcpy(area2, area, PATH_MAX);
                                        for (i = 0; length-- &&
                                            area2[start + i]; i++) {
                                                area[i] = area2[start + i];
                                        }
                                        /* cant use strncpy()-no trailing NUL */
                                        area[i] = '\0';
                                }
                                break;
                        case RULE_OVERSTRIKE:
                                if (!ptr[1] || !ptr[2]) {
                                        return ((char *)0);
                                } else {
                                        register int i;

                                        i = Char2Int(*(++ptr));
                                        if (i < 0) {
                                                return ((char *)0);
                                        } else {
                                                ++ptr;
                                                if (area[i]) {
                                                        area[i] = *ptr;
                                                }
                                        }
                                }
                                break;
                        case RULE_INSERT:
                                if (!ptr[1] || !ptr[2]) {
                                        return ((char *)0);
                                } else {
                                        register int i;
                                        register char *p1;
                                        register char *p2;

                                        i = Char2Int(*(++ptr));
                                        if (i < 0) {
                                                return ((char *)0);
                                        }
                                        p1 = area;
                                        p2 = area2;
                                        while (i && *p1) {
                                                i--;
                                                *(p2++) = *(p1++);
                                        }
                                        *(p2++) = *(++ptr);
                                        (void) strlcpy(p2, p1, PATH_MAX);
                                        (void) strlcpy(area, area2, PATH_MAX);
                                }
                                break;
            /* THE FOLLOWING RULES REQUIRE CLASS MATCHING */

                        case RULE_PURGE:        /* @x or @?c */
                                if (!ptr[1] || (ptr[1] ==
                                    RULE_CLASS && !ptr[2])) {
                                        return ((char *)0);
                                } else if (ptr[1] != RULE_CLASS) {
                                        (void) strlcpy(area, Purge(area,
                                            *(++ptr)), PATH_MAX);
                                } else {
                                        (void) strlcpy(area, PolyPurge(area,
                                            ptr[2]), PATH_MAX);
                                        ptr += 2;
                                }
                                break;
                        case RULE_SUBSTITUTE:   /* sxy || s?cy */
                                if (!ptr[1] || !ptr[2] ||
                                    (ptr[1] == RULE_CLASS && !ptr[3])) {
                                        return ((char *)0);
                                } else if (ptr[1] != RULE_CLASS) {
                                        ptr += 2;
                                } else {
                                        (void) strlcpy(area, PolySubst(area,
                                            ptr[2], ptr[3]), PATH_MAX);
                                        ptr += 3;
                                }
                                break;
                        case RULE_MATCH:        /* /x || /?c */
                                if (!ptr[1] ||
                                    (ptr[1] == RULE_CLASS && !ptr[2])) {
                                        return ((char *)0);
                                } else if (ptr[1] != RULE_CLASS) {
                                        if (!strchr(area, *(++ptr))) {
                                                return ((char *)0);
                                        }
                                } else {
                                        if (!PolyStrchr(area, ptr[2])) {
                                                return ((char *)0);
                                        }
                                        ptr += 2;
                                }
                                break;
                        case RULE_NOT:          /* !x || !?c */
                                if (!ptr[1] ||
                                    (ptr[1] == RULE_CLASS && !ptr[2])) {
                                        return ((char *)0);
                                } else if (ptr[1] != RULE_CLASS) {
                                        if (strchr(area, *(++ptr))) {
                                                return ((char *)0);
                                        }
                                } else {
                                        if (PolyStrchr(area, ptr[2])) {
                                                return ((char *)0);
                                        }
                                        ptr += 2;
                                }
                                break;
        /*
         * alternative use for a boomerang, number 1: a standard throwing
         * boomerang is an ideal thing to use to tuck the sheets under
         * the mattress when making your bed.  The streamlined shape of
         * the boomerang allows it to slip easily 'twixt mattress and
         * bedframe, and it's curve makes it very easy to hook sheets
         * into the gap.
         */

                        case RULE_EQUALS:       /* =nx || =n?c */
                                if (!ptr[1] || !ptr[2] ||
                                    (ptr[2] == RULE_CLASS && !ptr[3])) {
                                        return ((char *)0);
                                } else {
                                        register int i;

                                        if ((i = Char2Int(ptr[1])) < 0) {
                                                return ((char *)0);
                                        }
                                        if (ptr[2] != RULE_CLASS) {
                                                ptr += 2;
                                                if (area[i] != *ptr) {
                                                        return ((char *)0);
                                                }
                                        } else {
                                                ptr += 3;
                                                if (!MatchClass(*ptr,
                                                    area[i])) {
                                                        return ((char *)0);
                                                }
                                        }
                                }
                                break;

                        case RULE_DFIRST:
                                if (area[0]) {
                                        register int i;

                                        for (i = 1; area[i]; i++) {
                                                area[i - 1] = area[i];
                                        }
                                        area[i - 1] = '\0';
                                }
                                break;

                        case RULE_DLAST:
                                if (area[0]) {
                                        register int i;

                                        for (i = 1; area[i]; i++);
                                        area[i - 1] = '\0';
                                }
                                break;

                        case RULE_MFIRST:
                                if (!ptr[1] ||
                                    (ptr[1] == RULE_CLASS && !ptr[2])) {
                                        return ((char *)0);
                                } else {
                                        if (ptr[1] != RULE_CLASS) {
                                                ptr++;
                                                if (area[0] != *ptr) {
                                                        return ((char *)0);
                                                }
                                        } else {
                                                ptr += 2;
                                                if (!MatchClass(*ptr,
                                                    area[0])) {
                                                        return ((char *)0);
                                                }
                                        }
                                }
                                break;
                        case RULE_MLAST:
                                if (!ptr[1] ||
                                    (ptr[1] == RULE_CLASS && !ptr[2])) {
                                        return ((char *)0);
                                } else {
                                        register int i;

                                        for (i = 0; area[i]; i++);

                                        if (i > 0) {
                                                i--;
                                        } else {
                                                return ((char *)0);
                                        }
                                        if (ptr[1] != RULE_CLASS) {
                                                ptr++;
                                                if (area[i] != *ptr) {
                                                        return ((char *)0);
                                                }
                                        } else {
                                                ptr += 2;
                                                if (!MatchClass(*ptr,
                                                    area[i])) {
                                                        return ((char *)0);
                                                }
                                        }
                                }
                                break;
                }
        }
        if (!area[0]) {         /* have we deweted de poor widdle fing away? */
                return ((char *)0);
        }
        return (area);
}
/*
 * int
 * PMatch(register char *control, register char *string)
 * {
 *      while (*string && *control) {
 *              if (!MatchClass(*control, *string)) {
 *                      return (0);
 *              }
 *
 *              string++;
 *              control++;
 *      }
 *
 *      if (*string || *control) {
 *              return (0);
 *      }
 *
 *      return (1);
 * }
 */