xref: /illumos-gate/usr/src/uts/common/gssapi/mechs/krb5/krb5/krb/parse.c (revision 159d09a20817016f09b3ea28d1bdada4a336bb91)
1 /*
2  * Copyright 2008 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 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 #ifndef _KERNEL
42 #include <assert.h>
43 #include <stdarg.h>
44 #define ASSERT assert
45 #endif
46 
47 /*
48  * converts a single-string representation of the name to the
49  * multi-part principal format used in the protocols.
50  *
51  * principal will point to allocated storage which should be freed by
52  * the caller (using krb5_free_principal) after use.
53  *
54  * Conventions:  / is used to separate components.  If @ is present in the
55  * string, then the rest of the string after it represents the realm name.
56  * Otherwise the local realm name is used.
57  *
58  * error return:
59  *	KRB5_PARSE_MALFORMED	badly formatted string
60  *
61  * also returns system errors:
62  *	ENOMEM	malloc failed/out of memory
63  *
64  * get_default_realm() is called; it may return other errors.
65  */
66 
67 #define REALM_SEP	'@'
68 #define	COMPONENT_SEP	'/'
69 #define QUOTECHAR	'\\'
70 
71 #define FCOMPNUM	10
72 
73 
74 /*
75  * May the fleas of a thousand camels infest the ISO, they who think
76  * that arbitrarily large multi-component names are a Good Thing.....
77  */
78 /*ARGSUSED*/
79 krb5_error_code KRB5_CALLCONV
80 krb5_parse_name(krb5_context context, const char *name, krb5_principal *nprincipal)
81 {
82 	register const char	*cp;
83 	register char	*q;
84 	register int    i,c,size;
85 	int		components = 0;
86 	const char	*parsed_realm = NULL;
87 	int		fcompsize[FCOMPNUM];
88 	unsigned int	realmsize = 0;
89 #ifndef _KERNEL
90 	char		*default_realm = NULL;
91 	int		default_realm_size = 0;
92 	krb5_error_code retval;
93 #endif
94 	char		*tmpdata;
95 	krb5_principal	principal;
96 
97 	/*
98 	 * Pass 1.  Find out how many components there are to the name,
99 	 * and get string sizes for the first FCOMPNUM components.
100 	 */
101 	size = 0;
102 	/*LINTED*/
103 	for (i = 0, cp = name; (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) {
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) {
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 			size++;
141 	}
142 	if (parsed_realm)
143 		realmsize = size;
144 	else if (i < FCOMPNUM)
145 		fcompsize[i] = size;
146 	components = i + 1;
147 	/*
148 	 * Now, we allocate the principal structure and all of its
149 	 * component pieces
150 	 */
151 	principal = (krb5_principal)MALLOC(sizeof(krb5_principal_data));
152 	if (!principal) {
153 		return(ENOMEM);
154 	}
155 	principal->data = (krb5_data *)MALLOC(sizeof(krb5_data) * components);
156 	if (!principal->data) {
157 	    krb5_xfree_wrap((char *)principal, sizeof(krb5_principal_data));
158 	    return ENOMEM;
159 	}
160 	principal->length = components;
161 	/*
162 	 * If a realm was not found, then use the defualt realm....
163 	 */
164 	/*
165 	 * In the kernel we import the ctx and it always contains the
166 	 * default realm
167 	 */
168 
169 #ifndef _KERNEL
170 	if (!parsed_realm) {
171 	    if (!default_realm) {
172 		retval = krb5_get_default_realm(context, &default_realm);
173 		if (retval) {
174 		    krb5_xfree_wrap(principal->data,
175 			sizeof (krb5_data) * components);
176 		    krb5_xfree_wrap((char *)principal,
177 			sizeof (krb5_principal_data));
178 		    return(retval);
179 		}
180 		default_realm_size = strlen(default_realm);
181 	    }
182 	    realmsize = default_realm_size;
183 	}
184 #endif
185 	/*
186 	 * Pass 2.  Happens only if there were more than FCOMPNUM
187 	 * component; if this happens, someone should be shot
188 	 * immediately.  Nevertheless, we will attempt to handle said
189 	 * case..... <martyred sigh>
190 	 */
191 	if (components >= FCOMPNUM) {
192 		size = 0;
193 		parsed_realm = NULL;
194 		/*LINTED*/
195 		for (i=0,cp = name; (c = *cp); cp++) {
196 			if (c == QUOTECHAR) {
197 				cp++;
198 				size++;
199 			} else if (c == COMPONENT_SEP) {
200 				if (krb5_princ_size(context, principal) > i)
201 					krb5_princ_component(context, principal, i)->length = size;
202 				size = 0;
203 				i++;
204 			} else if (c == REALM_SEP) {
205 				if (krb5_princ_size(context, principal) > i)
206 					krb5_princ_component(context, principal, i)->length = size;
207 				size = 0;
208 				parsed_realm = cp+1;
209 			} else
210 				size++;
211 		}
212 		if (parsed_realm)
213 			krb5_princ_realm(context, principal)->length = size;
214 		else
215 			if (krb5_princ_size(context, principal) > i)
216 				krb5_princ_component(context, principal, i)->length = size;
217 		if (i + 1 != components) {
218 #ifndef _KERNEL
219 #if !defined(_WIN32)
220 		    fprintf(stderr,
221 			    "Programming error in krb5_parse_name!");
222 #endif
223 		    ASSERT(i + 1 == components);
224 		    abort();
225 #else
226 		    ASSERT(i + 1 == components);
227 #endif /* !_KERNEL */
228 		}
229 	} else {
230 		/*
231 		 * If there were fewer than FCOMPSIZE components (the
232 		 * usual case), then just copy the sizes to the
233 		 * principal structure
234 		 */
235 		for (i=0; i < components; i++)
236 			krb5_princ_component(context, principal, i)->length = fcompsize[i];
237 	}
238 	/*
239 	 * Now, we need to allocate the space for the strings themselves.....
240 	 */
241 	tmpdata = MALLOC(realmsize+1);
242 	if (tmpdata == 0) {
243 		krb5_xfree_wrap(principal->data,
244 		    sizeof (krb5_data) * components);
245 		krb5_xfree_wrap((char *)principal,
246 		    sizeof (krb5_principal_data));
247 #ifndef _KERNEL
248 		if (default_realm)
249 			krb5_xfree_wrap(default_realm, strlen(default_realm));
250 #endif
251 		return (ENOMEM);
252 	}
253 	krb5_princ_set_realm_length(context, principal, realmsize);
254 	krb5_princ_set_realm_data(context, principal, tmpdata);
255 	for (i=0; i < components; i++) {
256 		char *tmpdata2 =
257 		  MALLOC(krb5_princ_component(context, principal, i)->length + 1);
258 		if (!tmpdata2) {
259                         /*
260                          * Release the principle and realm strings remembering
261                          * that we allocated one additional byte beyond the
262                          * 'length' to hold the string terminating zero byte.
263                          * It's critical that the free size match the malloc
264 			 * size.
265                          */
266 			for (i--; i >= 0; i--)
267 				krb5_xfree_wrap(krb5_princ_component(context,
268 				    principal, i)->data,
269 				    krb5_princ_component(context,
270 				    principal, i)->length + 1);
271 			krb5_xfree_wrap(krb5_princ_realm(context,
272 			    principal)->data, krb5_princ_realm(context,
273 			    principal)->length + 1);
274 			krb5_xfree_wrap(principal->data, principal->length);
275 			krb5_xfree_wrap(principal, sizeof(krb5_principal_data));
276 #ifndef _KERNEL
277 			if (default_realm)
278 				krb5_xfree_wrap(default_realm,
279 						strlen(default_realm));
280 #endif
281 			return (ENOMEM);
282 		}
283 		krb5_princ_component(context, principal, i)->data = tmpdata2;
284 		krb5_princ_component(context, principal, i)->magic = KV5M_DATA;
285 	}
286 
287 	/*
288 	 * Pass 3.  Now we go through the string a *third* time, this
289 	 * time filling in the krb5_principal structure which we just
290 	 * allocated.
291 	 */
292 	q = krb5_princ_component(context, principal, 0)->data;
293 	/*LINTED*/
294 	for (i=0,cp = name; (c = *cp); cp++) {
295 		if (c == QUOTECHAR) {
296 			cp++;
297 			switch (c = *cp) {
298 			case 'n':
299 				*q++ = '\n';
300 				break;
301 			case 't':
302 				*q++ = '\t';
303 				break;
304 			case 'b':
305 				*q++ = '\b';
306 				break;
307 			case '0':
308 				*q++ = '\0';
309 				break;
310 			default:
311 				*q++ = (char) c;
312 			}
313 		} else if ((c == COMPONENT_SEP) || (c == REALM_SEP)) {
314 			i++;
315 			*q++ = '\0';
316 			if (c == COMPONENT_SEP)
317 				q = krb5_princ_component(context, principal, i)->data;
318 			else
319 				q = krb5_princ_realm(context, principal)->data;
320 		} else
321 			*q++ = (char) c;
322 	}
323 	*q++ = '\0';
324 	if (!parsed_realm)
325 #ifndef _KERNEL
326 		(void) strcpy(krb5_princ_realm(context, principal)->data,
327 			default_realm);
328 #endif
329 	/*
330 	 * Alright, we're done.  Now stuff a pointer to this monstrosity
331 	 * into the return variable, and let's get out of here.
332 	 */
333 	krb5_princ_type(context, principal) = KRB5_NT_PRINCIPAL;
334 	principal->magic = KV5M_PRINCIPAL;
335 	principal->realm.magic = KV5M_DATA;
336 	*nprincipal = principal;
337 #ifndef _KERNEL
338 	if (default_realm)
339 		krb5_xfree_wrap(default_realm, strlen(default_realm));
340 #endif
341 	return(0);
342 }
343