1 /*
2 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
3 * Use is subject to license terms.
4 */
5
6 /*
7 * lib/krb5/krb/parse.c
8 *
9 * Copyright 1990,1991,2008 by the Massachusetts Institute of Technology.
10 * All Rights Reserved.
11 *
12 * Export of this software from the United States of America may
13 * require a specific license from the United States Government.
14 * It is the responsibility of any person or organization contemplating
15 * export to obtain such a license before exporting.
16 *
17 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
18 * distribute this software and its documentation for any purpose and
19 * without fee is hereby granted, provided that the above copyright
20 * notice appear in all copies and that both that copyright notice and
21 * this permission notice appear in supporting documentation, and that
22 * the name of M.I.T. not be used in advertising or publicity pertaining
23 * to distribution of the software without specific, written prior
24 * permission. Furthermore if you modify this software you must label
25 * your software as modified software and not distribute it in such a
26 * fashion that it might be confused with the original M.I.T. software.
27 * M.I.T. makes no representations about the suitability of
28 * this software for any purpose. It is provided "as is" without express
29 * or implied warranty.
30 *
31 *
32 * krb5_parse_name() routine.
33 *
34 * Rewritten by Theodore Ts'o to properly handle arbitrary quoted
35 * characters in the principal name.
36 */
37
38
39 #include "k5-int.h"
40
41 /*
42 * converts a single-string representation of the name to the
43 * multi-part principal format used in the protocols.
44 *
45 * principal will point to allocated storage which should be freed by
46 * the caller (using krb5_free_principal) after use.
47 *
48 * Conventions: / is used to separate components. If @ is present in the
49 * string, then the rest of the string after it represents the realm name.
50 * Otherwise the local realm name is used.
51 *
52 * error return:
53 * KRB5_PARSE_MALFORMED badly formatted string
54 *
55 * also returns system errors:
56 * ENOMEM malloc failed/out of memory
57 *
58 * get_default_realm() is called; it may return other errors.
59 */
60
61 #define REALM_SEP '@'
62 #define COMPONENT_SEP '/'
63 #define QUOTECHAR '\\'
64
65 #define FCOMPNUM 10
66
67 /*
68 * May the fleas of a thousand camels infest the ISO, they who think
69 * that arbitrarily large multi-component names are a Good Thing.....
70 */
71 static krb5_error_code
72 /*LINTED*/
k5_parse_name(krb5_context context,const char * name,int flags,krb5_principal * nprincipal)73 k5_parse_name(krb5_context context, const char *name,
74 int flags, krb5_principal *nprincipal)
75 {
76 register const char *cp;
77 register char *q;
78 register int i,c,size;
79 int components = 0;
80 const char *parsed_realm = NULL;
81 int fcompsize[FCOMPNUM];
82 unsigned int realmsize = 0;
83 #ifndef _KERNEL
84 char *default_realm = NULL;
85 int default_realm_size = 0;
86 krb5_error_code retval;
87 #endif
88 char *tmpdata;
89 krb5_principal principal;
90 unsigned int enterprise = (flags & KRB5_PRINCIPAL_PARSE_ENTERPRISE);
91 int first_at;
92
93 *nprincipal = NULL;
94
95 /*
96 * Pass 1. Find out how many components there are to the name,
97 * and get string sizes for the first FCOMPNUM components. For
98 * enterprise principal names (UPNs), there is only a single
99 * component.
100 */
101 size = 0;
102 /*LINTED*/
103 for (i=0,cp = name, first_at = 1; (c = *cp); cp++) {
104 if (c == QUOTECHAR) {
105 cp++;
106 /*LINTED*/
107 if (!(c = *cp))
108 /*
109 * QUOTECHAR can't be at the last
110 * character of the name!
111 */
112 return(KRB5_PARSE_MALFORMED);
113 size++;
114 continue;
115 } else if (c == COMPONENT_SEP && !enterprise) {
116 if (parsed_realm)
117 /*
118 * Shouldn't see a component separator
119 * after we've parsed out the realm name!
120 */
121 return(KRB5_PARSE_MALFORMED);
122 if (i < FCOMPNUM) {
123 fcompsize[i] = size;
124 }
125 size = 0;
126 i++;
127 } else if (c == REALM_SEP && (!enterprise || !first_at)) {
128 if (parsed_realm)
129 /*
130 * Multiple realm separaters
131 * not allowed; zero-length realms are.
132 */
133 return(KRB5_PARSE_MALFORMED);
134 parsed_realm = cp + 1;
135 if (i < FCOMPNUM) {
136 fcompsize[i] = size;
137 }
138 size = 0;
139 } else {
140 if (c == REALM_SEP && enterprise && first_at)
141 first_at = 0;
142
143 size++;
144 }
145 }
146 if (parsed_realm != NULL)
147 realmsize = size;
148 else if (i < FCOMPNUM)
149 fcompsize[i] = size;
150 components = i + 1;
151 /*
152 * Now, we allocate the principal structure and all of its
153 * component pieces
154 */
155 principal = (krb5_principal)MALLOC(sizeof(krb5_principal_data));
156 if (principal == NULL) {
157 return(ENOMEM);
158 }
159 principal->data = (krb5_data *)MALLOC(sizeof(krb5_data) * components);
160 if (principal->data == NULL) {
161 krb5_xfree_wrap(principal, sizeof(krb5_principal_data));
162 return ENOMEM;
163 }
164 principal->length = components;
165
166 /*
167 * If a realm was not found, then use the default realm, unless
168 * KRB5_PRINCIPAL_PARSE_NO_REALM was specified in which case the
169 * realm will be empty.
170 */
171 #ifndef _KERNEL
172 if (!parsed_realm) {
173 if (flags & KRB5_PRINCIPAL_PARSE_REQUIRE_REALM) {
174 krb5_set_error_message(context, KRB5_PARSE_MALFORMED,
175 "Principal %s is missing required realm", name);
176 krb5_xfree_wrap(principal->data, principal->length);
177 krb5_xfree_wrap(principal, sizeof(krb5_principal_data));
178 return KRB5_PARSE_MALFORMED;
179 }
180 if (!default_realm && (flags & KRB5_PRINCIPAL_PARSE_NO_REALM) == 0) {
181 retval = krb5_get_default_realm(context, &default_realm);
182 if (retval) {
183 krb5_xfree_wrap(principal->data, principal->length);
184 krb5_xfree_wrap(principal, sizeof(krb5_principal_data));
185 return(retval);
186 }
187 default_realm_size = strlen(default_realm);
188 }
189 realmsize = default_realm_size;
190 } else if (flags & KRB5_PRINCIPAL_PARSE_NO_REALM) {
191 krb5_set_error_message(context, KRB5_PARSE_MALFORMED,
192 "Principal %s has realm present", name);
193 krb5_xfree_wrap(principal->data, principal->length);
194 krb5_xfree_wrap(principal, sizeof(krb5_principal_data));
195 return KRB5_PARSE_MALFORMED;
196 }
197 #endif
198
199 /*
200 * Pass 2. Happens only if there were more than FCOMPNUM
201 * component; if this happens, someone should be shot
202 * immediately. Nevertheless, we will attempt to handle said
203 * case..... <martyred sigh>
204 */
205 if (components >= FCOMPNUM) {
206 size = 0;
207 parsed_realm = NULL;
208 /*LINTED*/
209 for (i=0,cp = name; (c = *cp); cp++) {
210 if (c == QUOTECHAR) {
211 cp++;
212 size++;
213 } else if (c == COMPONENT_SEP) {
214 if (krb5_princ_size(context, principal) > i)
215 krb5_princ_component(context, principal, i)->length = size;
216 size = 0;
217 i++;
218 } else if (c == REALM_SEP) {
219 if (krb5_princ_size(context, principal) > i)
220 krb5_princ_component(context, principal, i)->length = size;
221 size = 0;
222 parsed_realm = cp+1;
223 } else
224 size++;
225 }
226 if (parsed_realm)
227 krb5_princ_realm(context, principal)->length = size;
228 else
229 if (krb5_princ_size(context, principal) > i)
230 krb5_princ_component(context, principal, i)->length = size;
231 if (i + 1 != components) {
232 #ifndef _KERNEL
233 #if !defined(_WIN32)
234 fprintf(stderr,
235 "Programming error in krb5_parse_name!");
236 #endif
237 ASSERT(i + 1 == components);
238 abort();
239 #else
240 ASSERT(i + 1 == components);
241 #endif /* !_KERNEL */
242
243 }
244 } else {
245 /*
246 * If there were fewer than FCOMPSIZE components (the
247 * usual case), then just copy the sizes to the
248 * principal structure
249 */
250 for (i=0; i < components; i++)
251 krb5_princ_component(context, principal, i)->length = fcompsize[i];
252 }
253 /*
254 * Now, we need to allocate the space for the strings themselves.....
255 */
256 tmpdata = MALLOC(realmsize + 1);
257 if (tmpdata == 0) {
258 krb5_xfree_wrap(principal->data, principal->length);
259 krb5_xfree_wrap(principal, sizeof(krb5_principal_data));
260 #ifndef _KERNEL
261 krb5_xfree_wrap(default_realm, strlen(default_realm));
262 #endif
263 return ENOMEM;
264 }
265 krb5_princ_set_realm_length(context, principal, realmsize);
266 krb5_princ_set_realm_data(context, principal, tmpdata);
267 for (i=0; i < components; i++) {
268 char *tmpdata2 =
269 MALLOC(krb5_princ_component(context, principal, i)->length + 1);
270 if (tmpdata2 == NULL) {
271 for (i--; i >= 0; i--)
272 krb5_xfree_wrap(krb5_princ_component(context,
273 principal, i)->data,
274 krb5_princ_component(context,
275 principal, i)->length + 1);
276
277 krb5_xfree_wrap(krb5_princ_realm(context,
278 principal)->data, krb5_princ_realm(context,
279 principal)->length + 1);
280
281 krb5_xfree_wrap(principal->data, principal->length);
282 krb5_xfree_wrap(principal, sizeof(krb5_principal_data));
283 #ifndef _KERNEL
284 krb5_xfree_wrap(default_realm, strlen(default_realm));
285 #endif
286 return(ENOMEM);
287 }
288 krb5_princ_component(context, principal, i)->data = tmpdata2;
289 krb5_princ_component(context, principal, i)->magic = KV5M_DATA;
290 }
291
292 /*
293 * Pass 3. Now we go through the string a *third* time, this
294 * time filling in the krb5_principal structure which we just
295 * allocated.
296 */
297 q = krb5_princ_component(context, principal, 0)->data;
298 /*LINTED*/
299 for (i=0,cp = name, first_at = 1; (c = *cp); cp++) {
300 if (c == QUOTECHAR) {
301 cp++;
302 switch (c = *cp) {
303 case 'n':
304 *q++ = '\n';
305 break;
306 case 't':
307 *q++ = '\t';
308 break;
309 case 'b':
310 *q++ = '\b';
311 break;
312 case '0':
313 *q++ = '\0';
314 break;
315 default:
316 *q++ = c;
317 break;
318 }
319 } else if (c == COMPONENT_SEP && !enterprise) {
320 i++;
321 *q++ = '\0';
322 q = krb5_princ_component(context, principal, i)->data;
323 } else if (c == REALM_SEP && (!enterprise || !first_at)) {
324 i++;
325 *q++ = '\0';
326 q = krb5_princ_realm(context, principal)->data;
327 } else {
328 if (c == REALM_SEP && enterprise && first_at)
329 first_at = 0;
330
331 *q++ = c;
332 }
333 }
334 *q++ = '\0';
335 /*LINTED*/
336 if (!parsed_realm) {
337 #ifndef _KERNEL
338
339 if (flags & KRB5_PRINCIPAL_PARSE_NO_REALM)
340 (krb5_princ_realm(context, principal)->data)[0] = '\0';
341 else
342 strlcpy(krb5_princ_realm(context, principal)->data, default_realm, realmsize+1);
343 #endif
344 }
345 /*
346 * Alright, we're done. Now stuff a pointer to this monstrosity
347 * into the return variable, and let's get out of here.
348 */
349 if (enterprise)
350 krb5_princ_type(context, principal) = KRB5_NT_ENTERPRISE_PRINCIPAL;
351 else
352 krb5_princ_type(context, principal) = KRB5_NT_PRINCIPAL;
353 principal->magic = KV5M_PRINCIPAL;
354 principal->realm.magic = KV5M_DATA;
355 *nprincipal = principal;
356
357 #ifndef _KERNEL
358 if (default_realm != NULL)
359 krb5_xfree_wrap(default_realm, strlen(default_realm));
360 #endif
361
362 return(0);
363 }
364
365 krb5_error_code KRB5_CALLCONV
krb5_parse_name(krb5_context context,const char * name,krb5_principal * nprincipal)366 krb5_parse_name(krb5_context context, const char *name, krb5_principal *nprincipal)
367 {
368 return k5_parse_name(context, name, 0, nprincipal);
369 }
370
371 krb5_error_code KRB5_CALLCONV
krb5_parse_name_flags(krb5_context context,const char * name,int flags,krb5_principal * nprincipal)372 krb5_parse_name_flags(krb5_context context, const char *name,
373 int flags, krb5_principal *nprincipal)
374 {
375 return k5_parse_name(context, name, flags, nprincipal);
376 }
377