/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 | } |