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