File: principal.c

package info (click to toggle)
krb5-strength 3.3-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 3,112 kB
  • sloc: ansic: 8,236; sh: 4,977; perl: 1,528; makefile: 181
file content (193 lines) | stat: -rw-r--r-- 5,918 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
/*
 * Password strength checks based on the principal.
 *
 * Performs various checks of the password against the principal for which the
 * password is being changed, trying to detect and reject passwords based on
 * components of the principal.
 *
 * Developed by Daria Phoebe Brashear and Ken Hornstein of Sine Nomine
 * Associates, on behalf of Stanford University Extensive modifications by Russ
 * Allbery <eagle@eyrie.org> Copyright 2020 Russ Allbery <eagle@eyrie.org>
 * Copyright 2006-2007, 2009, 2012-2014
 *     The Board of Trustees of the Leland Stanford Junior University
 *
 * SPDX-License-Identifier: MIT
 */

#include <config.h>
#include <portable/system.h>

#include <ctype.h>

#include <plugin/internal.h>
#include <util/macros.h>


/*
 * Given a string taken from the principal, check if the password matches that
 * string or is that string with leading or trailing digits added.  If so,
 * sets the Kerberos error and returns a non-zero error code.  Otherwise,
 * returns 0.
 */
static krb5_error_code
check_component(krb5_context ctx, const char *component, const char *password)
{
    char *copy;
    size_t i, j, complength, passlength;
    char c;

    /* Check if the password is a simple match for the component. */
    if (strcasecmp(component, password) == 0)
        return strength_error_generic(ctx, ERROR_USERNAME);

    /*
     * If the length of the password matches the length of the component,
     * check for a reversed match.
     */
    complength = strlen(component);
    passlength = strlen(password);
    if (complength == passlength) {
        copy = strdup(component);
        if (copy == NULL)
            return strength_error_system(ctx, "cannot allocate memory");
        for (i = 0, j = complength - 1; i < j; i++, j--) {
            c = copy[i];
            copy[i] = copy[j];
            copy[j] = c;
        }
        if (strcasecmp(copy, password) == 0) {
            explicit_bzero(copy, strlen(copy));
            free(copy);
            return strength_error_generic(ctx, ERROR_USERNAME);
        }
        free(copy);
    }

    /*
     * We've checked everything we care about unless the password is longer
     * than the component.
     */
    if (passlength <= complength)
        return 0;

    /*
     * Check whether the user just added leading or trailing digits to the
     * component of the principal to form the password.
     */
    for (i = 0; i <= passlength - complength; i++) {
        if (strncasecmp(password + i, component, complength) != 0)
            continue;

        /*
         * For this to be a match, all characters from 0 to i - 1 must be
         * digits, and all characters from strlen(component) + i to
         * strlen(password) - 1 must be digits.
         */
        for (j = 0; j < i; j++)
            if (!isdigit((unsigned char) password[j]))
                return 0;
        for (j = complength + i; j < passlength; j++)
            if (!isdigit((unsigned char) password[j]))
                return 0;

        /* The password was formed by adding digits to this component. */
        return strength_error_generic(ctx, ERROR_USERNAME);
    }

    /* No similarity to component detected. */
    return 0;
}


/*
 * Returns true if a given character is a separator character for forming
 * components, and false otherwise.
 */
static bool
is_separator(unsigned char c)
{
    if (c == '-' || c == '_')
        return false;
    if (isalnum(c))
        return false;
    return true;
}


/*
 * Check whether the password is based in some way on the principal.  We do
 * this by scanning the principal (in string form) and checking both each
 * component of that password (defined as the alphanumeric, hyphen, and
 * underscore bits between other characters) and the remaining principal from
 * that point forward (to catch, for example, the entire realm).  Returns 0 if
 * it is not and some non-zero error code if it appears to be.
 */
krb5_error_code
strength_check_principal(krb5_context ctx, krb5_pwqual_moddata data UNUSED,
                         const char *principal, const char *password)
{
    krb5_error_code code;
    char *copy, *start;
    size_t i, length;

    /* Sanity check. */
    if (principal == NULL)
        return 0;

    /* Start with checking the entire principal. */
    code = check_component(ctx, principal, password);
    if (code != 0)
        return code;

    /*
     * Make a copy of the principal and scan forward past any leading
     * separators.
     */
    length = strlen(principal);
    copy = strdup(principal);
    if (copy == NULL)
        return strength_error_system(ctx, "cannot allocate memory");
    i = 0;
    while (copy[i] != '\0' && is_separator(copy[i]))
        i++;

    /*
     * Now loop for each component.  At the start of each loop, check against
     * the component formed by the rest of the principal string.
     */
    do {
        if (i != 0) {
            code = check_component(ctx, copy + i, password);
            if (code != 0) {
                explicit_bzero(copy, strlen(copy));
                free(copy);
                return code;
            }
        }

        /* Set the component start and then scan for a separator. */
        start = copy + i;
        while (i < length && !is_separator(copy[i]))
            i++;

        /* At end of string or a separator.  Truncate the component. */
        copy[i] = '\0';

        /* Check the current component. */
        code = check_component(ctx, start, password);
        if (code != 0) {
            explicit_bzero(copy, strlen(copy));
            free(copy);
            return code;
        }

        /* Scan forward past any more separators. */
        while (i < length && is_separator(copy[i]))
            i++;
    } while (i < length);

    /* Password does not appear to be based on the principal. */
    explicit_bzero(copy, strlen(copy));
    free(copy);
    return 0;
}