Coverage Report

Created: 2024-02-11 06:29

/src/krb5/src/lib/krb5/krb/parse.c
Line
Count
Source (jump to first uncovered line)
1
/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2
/* lib/krb5/krb/parse.c - Parse strings into krb5_principals */
3
/*
4
 * Copyright 1990,1991,2008,2012 by the Massachusetts Institute of Technology.
5
 * All Rights Reserved.
6
 *
7
 * Export of this software from the United States of America may
8
 *   require a specific license from the United States Government.
9
 *   It is the responsibility of any person or organization contemplating
10
 *   export to obtain such a license before exporting.
11
 *
12
 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
13
 * distribute this software and its documentation for any purpose and
14
 * without fee is hereby granted, provided that the above copyright
15
 * notice appear in all copies and that both that copyright notice and
16
 * this permission notice appear in supporting documentation, and that
17
 * the name of M.I.T. not be used in advertising or publicity pertaining
18
 * to distribution of the software without specific, written prior
19
 * permission.  Furthermore if you modify this software you must label
20
 * your software as modified software and not distribute it in such a
21
 * fashion that it might be confused with the original M.I.T. software.
22
 * M.I.T. makes no representations about the suitability of
23
 * this software for any purpose.  It is provided "as is" without express
24
 * or implied warranty.
25
 */
26
27
#include "k5-int.h"
28
#include "int-proto.h"
29
30
/*
31
 * Scan name and allocate a shell principal with enough space in each field.
32
 * If enterprise is true, use enterprise principal parsing rules.  Return
33
 * KRB5_PARSE_MALFORMED if name is malformed.  Set *has_realm_out according to
34
 * whether name contains a realm separator.
35
 */
36
static krb5_error_code
37
allocate_princ(krb5_context context, const char *name, krb5_boolean enterprise,
38
               krb5_principal *princ_out, krb5_boolean *has_realm_out)
39
0
{
40
0
    krb5_error_code ret;
41
0
    const char *p;
42
0
    krb5_boolean first_at = TRUE;
43
0
    krb5_principal princ = NULL;
44
0
    krb5_data *cur_data, *new_comps;
45
0
    krb5_int32 i;
46
47
0
    *princ_out = NULL;
48
0
    *has_realm_out = FALSE;
49
50
    /* Allocate a starting principal with one component. */
51
0
    princ = k5alloc(sizeof(*princ), &ret);
52
0
    if (princ == NULL)
53
0
        goto cleanup;
54
0
    princ->data = k5alloc(sizeof(*princ->data), &ret);
55
0
    if (princ->data == NULL)
56
0
        goto cleanup;
57
0
    princ->realm = empty_data();
58
0
    princ->data[0] = empty_data();
59
0
    princ->length = 1;
60
61
0
    cur_data = &princ->data[0];
62
0
    for (p = name; *p != '\0'; p++) {
63
0
        if (*p == '/' && !enterprise) {
64
            /* Component separator (for non-enterprise principals).  We
65
             * shouldn't see this in the realm name. */
66
0
            if (cur_data == &princ->realm) {
67
0
                ret = KRB5_PARSE_MALFORMED;
68
0
                goto cleanup;
69
0
            }
70
0
            new_comps = realloc(princ->data,
71
0
                                (princ->length + 1) * sizeof(*princ->data));
72
0
            if (new_comps == NULL) {
73
0
                ret = ENOMEM;
74
0
                goto cleanup;
75
0
            }
76
0
            princ->data = new_comps;
77
0
            princ->length++;
78
0
            cur_data = &princ->data[princ->length - 1];
79
0
            *cur_data = empty_data();
80
0
        } else if (*p == '@' && (!enterprise || !first_at)) {
81
            /* Realm separator.  In enterprise principals, the first one of
82
             * these we see is part of the component. */
83
0
            if (cur_data == &princ->realm) {
84
0
                ret = KRB5_PARSE_MALFORMED;
85
0
                goto cleanup;
86
0
            }
87
0
            cur_data = &princ->realm;
88
0
        } else {
89
            /* Component or realm character, possibly quoted.  Make note if
90
             * we're seeing the first '@' in an enterprise principal. */
91
0
            cur_data->length++;
92
0
            if (*p == '@' && enterprise)
93
0
                first_at = FALSE;
94
0
            if (*p == '\\') {
95
                /* Quote character can't be the last character of the name. */
96
0
                if (*++p == '\0') {
97
0
                    ret = KRB5_PARSE_MALFORMED;
98
0
                    goto cleanup;
99
0
                }
100
0
            }
101
0
        }
102
0
    }
103
104
    /* Allocate space for each component and the realm, with space for null
105
     * terminators on each field. */
106
0
    for (i = 0; i < princ->length; i++) {
107
0
        princ->data[i].data = k5alloc(princ->data[i].length + 1, &ret);
108
0
        if (princ->data[i].data == NULL)
109
0
            goto cleanup;
110
0
    }
111
0
    princ->realm.data = k5alloc(princ->realm.length + 1, &ret);
112
0
    if (princ->realm.data == NULL)
113
0
        goto cleanup;
114
115
0
    *princ_out = princ;
116
0
    *has_realm_out = (cur_data == &princ->realm);
117
0
    princ = NULL;
118
0
cleanup:
119
0
    krb5_free_principal(context, princ);
120
0
    return ret;
121
0
}
122
123
/*
124
 * Parse name into princ, assuming that name is correctly formed and that all
125
 * principal fields are allocated to the correct length with zero-filled memory
126
 * (so we get null-terminated fields without any extra work).  If enterprise is
127
 * true, use enterprise principal parsing rules.
128
 */
129
static void
130
parse_name_into_princ(const char *name, krb5_boolean enterprise,
131
                      krb5_principal princ)
132
0
{
133
0
    const char *p;
134
0
    char c;
135
0
    krb5_boolean first_at = TRUE;
136
0
    krb5_data *cur_data = princ->data;
137
0
    unsigned int pos = 0;
138
139
0
    for (p = name; *p != '\0'; p++) {
140
0
        if (*p == '/' && !enterprise) {
141
            /* Advance to the next component. */
142
0
            assert(pos == cur_data->length);
143
0
            assert(cur_data != &princ->realm);
144
0
            assert(cur_data - princ->data + 1 < princ->length);
145
0
            cur_data++;
146
0
            pos = 0;
147
0
        } else if (*p == '@' && (!enterprise || !first_at)) {
148
            /* Advance to the realm. */
149
0
            assert(pos == cur_data->length);
150
0
            cur_data = &princ->realm;
151
0
            pos = 0;
152
0
        } else {
153
            /* Add to the current component or to the realm. */
154
0
            if (*p == '@' && enterprise)
155
0
                first_at = FALSE;
156
0
            c = *p;
157
0
            if (c == '\\') {
158
0
                c = *++p;
159
0
                if (c == 'n')
160
0
                    c = '\n';
161
0
                else if (c == 't')
162
0
                    c = '\t';
163
0
                else if (c == 'b')
164
0
                    c = '\b';
165
0
                else if (c == '0')
166
0
                    c = '\0';
167
0
            }
168
0
            assert(pos < cur_data->length);
169
0
            cur_data->data[pos++] = c;
170
0
        }
171
0
    }
172
0
    assert(pos == cur_data->length);
173
0
}
174
175
krb5_error_code KRB5_CALLCONV
176
krb5_parse_name_flags(krb5_context context, const char *name,
177
                      int flags, krb5_principal *principal_out)
178
0
{
179
0
    krb5_error_code ret;
180
0
    krb5_principal princ = NULL;
181
0
    char *default_realm;
182
0
    krb5_boolean has_realm;
183
0
    krb5_boolean enterprise = (flags & KRB5_PRINCIPAL_PARSE_ENTERPRISE);
184
0
    krb5_boolean require_realm = (flags & KRB5_PRINCIPAL_PARSE_REQUIRE_REALM);
185
0
    krb5_boolean no_realm = (flags & KRB5_PRINCIPAL_PARSE_NO_REALM);
186
0
    krb5_boolean ignore_realm = (flags & KRB5_PRINCIPAL_PARSE_IGNORE_REALM);
187
0
    krb5_boolean no_def_realm = (flags & KRB5_PRINCIPAL_PARSE_NO_DEF_REALM);
188
189
0
    *principal_out = NULL;
190
191
0
    ret = allocate_princ(context, name, enterprise, &princ, &has_realm);
192
0
    if (ret)
193
0
        goto cleanup;
194
0
    parse_name_into_princ(name, enterprise, princ);
195
196
    /* If a realm was not included, use the default realm, unless flags
197
     * indicate otherwise. */
198
0
    if (!has_realm) {
199
0
        if (require_realm) {
200
0
            ret = KRB5_PARSE_MALFORMED;
201
0
            k5_setmsg(context, ret,
202
0
                      _("Principal %s is missing required realm"), name);
203
0
            goto cleanup;
204
0
        }
205
0
        if (!no_realm && !ignore_realm && !no_def_realm) {
206
0
            ret = krb5_get_default_realm(context, &default_realm);
207
0
            if (ret)
208
0
                goto cleanup;
209
0
            krb5_free_data_contents(context, &princ->realm);
210
0
            princ->realm = string2data(default_realm);
211
0
        }
212
0
    } else if (no_realm) {
213
0
        ret = KRB5_PARSE_MALFORMED;
214
0
        k5_setmsg(context, ret, _("Principal %s has realm present"), name);
215
0
        goto cleanup;
216
0
    } else if (ignore_realm) {
217
0
        krb5_free_data_contents(context, &princ->realm);
218
0
        princ->realm = empty_data();
219
0
    }
220
221
0
    princ->type = (enterprise) ? KRB5_NT_ENTERPRISE_PRINCIPAL :
222
0
        k5_infer_principal_type(princ);
223
224
0
    princ->magic = KV5M_PRINCIPAL;
225
0
    *principal_out = princ;
226
0
    princ = NULL;
227
228
0
cleanup:
229
0
    krb5_free_principal(context, princ);
230
0
    return ret;
231
0
}
232
233
krb5_error_code KRB5_CALLCONV
234
krb5_parse_name(krb5_context context, const char *name,
235
                krb5_principal *principal_out)
236
0
{
237
0
    return krb5_parse_name_flags(context, name, 0, principal_out);
238
0
}