Get rid of casts of free() argument to char*, except where it's
[krb5.git] / src / lib / krb5 / krb / parse.c
1 /*
2  * lib/krb5/krb/parse.c
3  *
4  * Copyright 1990,1991,2008 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  * krb5_parse_name() routine.
28  *
29  * Rewritten by Theodore Ts'o to properly handle arbitrary quoted
30  * characters in the principal name.
31  */
32
33
34 #include "k5-int.h"
35
36 /*
37  * converts a single-string representation of the name to the
38  * multi-part principal format used in the protocols.
39  *
40  * principal will point to allocated storage which should be freed by 
41  * the caller (using krb5_free_principal) after use.
42  * 
43  * Conventions:  / is used to separate components.  If @ is present in the
44  * string, then the rest of the string after it represents the realm name.
45  * Otherwise the local realm name is used.
46  * 
47  * error return:
48  *      KRB5_PARSE_MALFORMED    badly formatted string
49  *
50  * also returns system errors:
51  *      ENOMEM  malloc failed/out of memory
52  *
53  * get_default_realm() is called; it may return other errors.
54  */
55
56 #define REALM_SEP       '@'
57 #define COMPONENT_SEP   '/'
58 #define QUOTECHAR       '\\'
59
60 #define FCOMPNUM        10
61
62 /*
63  * May the fleas of a thousand camels infest the ISO, they who think
64  * that arbitrarily large multi-component names are a Good Thing.....
65  */
66 static krb5_error_code
67 k5_parse_name(krb5_context context, const char *name,
68               int flags, krb5_principal *nprincipal)
69 {
70         register const char     *cp;
71         register char   *q;
72         register int    i,c,size;
73         int             components = 0;
74         const char      *parsed_realm = NULL;
75         int             fcompsize[FCOMPNUM];
76         unsigned int    realmsize = 0;
77         char            *default_realm = NULL;
78         int             default_realm_size = 0;
79         char            *tmpdata;
80         krb5_principal  principal;
81         krb5_error_code retval;
82         unsigned int    enterprise = (flags & KRB5_PRINCIPAL_PARSE_ENTERPRISE);
83         int             first_at;
84
85         *nprincipal = NULL;
86
87         /*
88          * Pass 1.  Find out how many components there are to the name,
89          * and get string sizes for the first FCOMPNUM components. For
90          * enterprise principal names (UPNs), there is only a single
91          * component.
92          */
93         size = 0;
94         for (i=0,cp = name, first_at = 1; (c = *cp); cp++) {
95                 if (c == QUOTECHAR) {
96                         cp++;
97                         if (!(c = *cp))
98                                 /*
99                                  * QUOTECHAR can't be at the last
100                                  * character of the name!
101                                  */
102                                 return(KRB5_PARSE_MALFORMED);
103                         size++;
104                         continue;
105                 } else if (c == COMPONENT_SEP && !enterprise) {
106                         if (parsed_realm)
107                                 /*
108                                  * Shouldn't see a component separator
109                                  * after we've parsed out the realm name!
110                                  */
111                                 return(KRB5_PARSE_MALFORMED);
112                         if (i < FCOMPNUM) {
113                                 fcompsize[i] = size;
114                         }
115                         size = 0;
116                         i++;
117                 } else if (c == REALM_SEP && (!enterprise || !first_at)) {
118                         if (parsed_realm)
119                                 /*
120                                  * Multiple realm separaters
121                                  * not allowed; zero-length realms are.
122                                  */
123                                 return(KRB5_PARSE_MALFORMED);
124                         parsed_realm = cp + 1;
125                         if (i < FCOMPNUM) {
126                                 fcompsize[i] = size;
127                         }
128                         size = 0;
129                 } else {
130                         if (c == REALM_SEP && enterprise && first_at)
131                                 first_at = 0;
132
133                         size++;
134                 }
135         }
136         if (parsed_realm != NULL)
137                 realmsize = size;
138         else if (i < FCOMPNUM) 
139                 fcompsize[i] = size;
140         components = i + 1;
141         /*
142          * Now, we allocate the principal structure and all of its
143          * component pieces
144          */
145         principal = (krb5_principal)malloc(sizeof(krb5_principal_data));
146         if (principal == NULL) {
147             return(ENOMEM);
148         }
149         principal->data = (krb5_data *) malloc(sizeof(krb5_data) * components);
150         if (principal->data == NULL) {
151             free(principal);
152             return ENOMEM;
153         }
154         principal->length = components;
155
156         /*
157          * If a realm was not found, then use the default realm, unless
158          * KRB5_PRINCIPAL_PARSE_NO_REALM was specified in which case the
159          * realm will be empty.
160          */
161         if (!parsed_realm) {
162             if (flags & KRB5_PRINCIPAL_PARSE_REQUIRE_REALM) {
163                 krb5_set_error_message(context, KRB5_PARSE_MALFORMED,
164                                        "Principal %s is missing required realm", name);
165                 free(principal->data);
166                 free(principal);
167                 return KRB5_PARSE_MALFORMED;
168             }
169             if (!default_realm && (flags & KRB5_PRINCIPAL_PARSE_NO_REALM) == 0) {
170                 retval = krb5_get_default_realm(context, &default_realm);
171                 if (retval) {
172                     free(principal->data);
173                     free(principal);
174                     return(retval);
175                 }
176                 default_realm_size = strlen(default_realm);
177             }
178             realmsize = default_realm_size;
179         } else if (flags & KRB5_PRINCIPAL_PARSE_NO_REALM) {
180             krb5_set_error_message(context, KRB5_PARSE_MALFORMED,
181                                   "Principal %s has realm present", name);
182             free(principal->data);
183             free(principal);
184             return KRB5_PARSE_MALFORMED;
185         }
186
187         /*
188          * Pass 2.  Happens only if there were more than FCOMPNUM
189          * component; if this happens, someone should be shot
190          * immediately.  Nevertheless, we will attempt to handle said
191          * case..... <martyred sigh>
192          */
193         if (components >= FCOMPNUM) {
194                 size = 0;
195                 parsed_realm = NULL;
196                 for (i=0,cp = name; (c = *cp); cp++) {
197                         if (c == QUOTECHAR) {
198                                 cp++;
199                                 size++;
200                         } else if (c == COMPONENT_SEP) {
201                                 if (krb5_princ_size(context, principal) > i)
202                                         krb5_princ_component(context, principal, i)->length = size;
203                                 size = 0;
204                                 i++;
205                         } else if (c == REALM_SEP) {
206                                 if (krb5_princ_size(context, principal) > i)
207                                         krb5_princ_component(context, principal, i)->length = size;
208                                 size = 0;
209                                 parsed_realm = cp+1;
210                         } else
211                                 size++;
212                 }
213                 if (parsed_realm)
214                         krb5_princ_realm(context, principal)->length = size;
215                 else
216                         if (krb5_princ_size(context, principal) > i)
217                                 krb5_princ_component(context, principal, i)->length = size;
218                 if (i + 1 != components) {
219 #if !defined(_WIN32)
220                     fprintf(stderr,
221                             "Programming error in krb5_parse_name!");
222 #endif
223                     assert(i + 1 == components);
224                     abort();
225                 }
226         } else {
227                 /*
228                  * If there were fewer than FCOMPSIZE components (the
229                  * usual case), then just copy the sizes to the
230                  * principal structure
231                  */
232                 for (i=0; i < components; i++)
233                         krb5_princ_component(context, principal, i)->length = fcompsize[i];
234         }
235         /*      
236          * Now, we need to allocate the space for the strings themselves.....
237          */
238         tmpdata = malloc(realmsize + 1);
239         if (tmpdata == 0) {
240                 free(principal->data);
241                 free(principal);
242                 free(default_realm);
243                 return ENOMEM;
244         }
245         krb5_princ_set_realm_length(context, principal, realmsize);
246         krb5_princ_set_realm_data(context, principal, tmpdata);
247         for (i=0; i < components; i++) {
248                 char *tmpdata2 =
249                   malloc(krb5_princ_component(context, principal, i)->length + 1);
250                 if (tmpdata2 == NULL) {
251                         for (i--; i >= 0; i--)
252                                 free(krb5_princ_component(context, principal, i)->data);
253                         free(krb5_princ_realm(context, principal)->data);
254                         free(principal->data);
255                         free(principal);
256                         free(default_realm);
257                         return(ENOMEM);
258                 }
259                 krb5_princ_component(context, principal, i)->data = tmpdata2;
260                 krb5_princ_component(context, principal, i)->magic = KV5M_DATA;
261         }
262         
263         /*
264          * Pass 3.  Now we go through the string a *third* time, this
265          * time filling in the krb5_principal structure which we just
266          * allocated.
267          */
268         q = krb5_princ_component(context, principal, 0)->data;
269         for (i=0,cp = name, first_at = 1; (c = *cp); cp++) {
270                 if (c == QUOTECHAR) {
271                         cp++;
272                         switch (c = *cp) {
273                         case 'n':
274                                 *q++ = '\n';
275                                 break;
276                         case 't':
277                                 *q++ = '\t';
278                                 break;
279                         case 'b':
280                                 *q++ = '\b';
281                                 break;
282                         case '0':
283                                 *q++ = '\0';
284                                 break;
285                         default:
286                                 *q++ = c;
287                                 break;
288                         }
289                 } else if (c == COMPONENT_SEP && !enterprise) {
290                         i++;
291                         *q++ = '\0';
292                         q = krb5_princ_component(context, principal, i)->data;
293                 } else if (c == REALM_SEP && (!enterprise || !first_at)) {
294                         i++;
295                         *q++ = '\0';
296                         q = krb5_princ_realm(context, principal)->data;
297                 } else {
298                         if (c == REALM_SEP && enterprise && first_at)
299                                 first_at = 0;
300
301                         *q++ = c;
302                 }
303         }
304         *q++ = '\0';
305         if (!parsed_realm) {
306                 if (flags & KRB5_PRINCIPAL_PARSE_NO_REALM)
307                         (krb5_princ_realm(context, principal)->data)[0] = '\0';
308                 else
309                         strlcpy(krb5_princ_realm(context, principal)->data, default_realm, realmsize+1);
310         }
311         /*
312          * Alright, we're done.  Now stuff a pointer to this monstrosity
313          * into the return variable, and let's get out of here.
314          */
315         if (enterprise)
316                 krb5_princ_type(context, principal) = KRB5_NT_ENTERPRISE_PRINCIPAL;
317         else
318                 krb5_princ_type(context, principal) = KRB5_NT_PRINCIPAL;
319         principal->magic = KV5M_PRINCIPAL;
320         principal->realm.magic = KV5M_DATA;
321         *nprincipal = principal;
322
323         if (default_realm != NULL)
324                 free(default_realm);
325
326         return(0);
327 }
328
329 krb5_error_code KRB5_CALLCONV
330 krb5_parse_name(krb5_context context, const char *name, krb5_principal *nprincipal)
331 {
332          return k5_parse_name(context, name, 0, nprincipal);
333 }
334
335 krb5_error_code KRB5_CALLCONV
336 krb5_parse_name_flags(krb5_context context, const char *name,
337                       int flags, krb5_principal *nprincipal)
338 {
339          return k5_parse_name(context, name, flags, nprincipal);
340 }