Convert many uses of strcpy/strcat (and sometimes sprintf) to accepted
[krb5.git] / src / lib / krb5 / krb / parse.c
1 /*
2  * lib/krb5/krb/parse.c
3  *
4  * Copyright 1990,1991 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 /*
64  * May the fleas of a thousand camels infest the ISO, they who think
65  * that arbitrarily large multi-component names are a Good Thing.....
66  */
67 krb5_error_code KRB5_CALLCONV
68 krb5_parse_name(krb5_context context, const char *name, 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         
83         /*
84          * Pass 1.  Find out how many components there are to the name,
85          * and get string sizes for the first FCOMPNUM components.
86          */
87         size = 0;
88         for (i=0,cp = name; (c = *cp); cp++) {
89                 if (c == QUOTECHAR) {
90                         cp++;
91                         if (!(c = *cp))
92                                 /*
93                                  * QUOTECHAR can't be at the last
94                                  * character of the name!
95                                  */
96                                 return(KRB5_PARSE_MALFORMED);
97                         size++;
98                         continue;
99                 } else if (c == COMPONENT_SEP) {
100                         if (parsed_realm)
101                                 /*
102                                  * Shouldn't see a component separator
103                                  * after we've parsed out the realm name!
104                                  */
105                                 return(KRB5_PARSE_MALFORMED);
106                         if (i < FCOMPNUM) {
107                                 fcompsize[i] = size;
108                         }
109                         size = 0;
110                         i++;
111                 } else if (c == REALM_SEP) {
112                         if (parsed_realm)
113                                 /*
114                                  * Multiple realm separaters
115                                  * not allowed; zero-length realms are.
116                                  */
117                                 return(KRB5_PARSE_MALFORMED);
118                         parsed_realm = cp+1;
119                         if (i < FCOMPNUM) {
120                                 fcompsize[i] = size;
121                         }
122                         size = 0;
123                 } else
124                         size++;
125         }
126         if (parsed_realm)
127                 realmsize = size;
128         else if (i < FCOMPNUM) 
129                 fcompsize[i] = size;
130         components = i + 1;
131         /*
132          * Now, we allocate the principal structure and all of its
133          * component pieces
134          */
135         principal = (krb5_principal)malloc(sizeof(krb5_principal_data));
136         if (!principal) {
137                 return(ENOMEM);
138         }
139         principal->data = (krb5_data *) malloc(sizeof(krb5_data) * components);
140         if (!principal->data) {
141             free((char *)principal);
142             return ENOMEM;
143         }
144         principal->length = components;
145         /*
146          * If a realm was not found, then use the defualt realm....
147          */
148         if (!parsed_realm) {
149             if (!default_realm) {
150                 retval = krb5_get_default_realm(context, &default_realm);
151                 if (retval) {
152                     krb5_xfree(principal->data);
153                     krb5_xfree((char *)principal);
154                     return(retval);
155                 }
156                 default_realm_size = strlen(default_realm);
157             }
158             realmsize = default_realm_size;
159         }
160         /*
161          * Pass 2.  Happens only if there were more than FCOMPNUM
162          * component; if this happens, someone should be shot
163          * immediately.  Nevertheless, we will attempt to handle said
164          * case..... <martyred sigh>
165          */
166         if (components >= FCOMPNUM) {
167                 size = 0;
168                 parsed_realm = NULL;
169                 for (i=0,cp = name; (c = *cp); cp++) {
170                         if (c == QUOTECHAR) {
171                                 cp++;
172                                 size++;
173                         } else if (c == COMPONENT_SEP) {
174                                 if (krb5_princ_size(context, principal) > i)
175                                         krb5_princ_component(context, principal, i)->length = size;
176                                 size = 0;
177                                 i++;
178                         } else if (c == REALM_SEP) {
179                                 if (krb5_princ_size(context, principal) > i)
180                                         krb5_princ_component(context, principal, i)->length = size;
181                                 size = 0;
182                                 parsed_realm = cp+1;
183                         } else
184                                 size++;
185                 }
186                 if (parsed_realm)
187                         krb5_princ_realm(context, principal)->length = size;
188                 else
189                         if (krb5_princ_size(context, principal) > i)
190                                 krb5_princ_component(context, principal, i)->length = size;
191                 if (i + 1 != components) {
192 #if !defined(_WIN32)
193                     fprintf(stderr,
194                             "Programming error in krb5_parse_name!");
195 #endif
196                     assert(i + 1 == components);
197                     abort();
198                 }
199         } else {
200                 /*
201                  * If there were fewer than FCOMPSIZE components (the
202                  * usual case), then just copy the sizes to the
203                  * principal structure
204                  */
205                 for (i=0; i < components; i++)
206                         krb5_princ_component(context, principal, i)->length = fcompsize[i];
207         }
208         /*      
209          * Now, we need to allocate the space for the strings themselves.....
210          */
211         tmpdata = malloc(realmsize+1);
212         if (tmpdata == 0) {
213                 krb5_xfree(principal->data);
214                 krb5_xfree(principal);
215                 krb5_xfree(default_realm);
216                 return ENOMEM;
217         }
218         krb5_princ_set_realm_length(context, principal, realmsize);
219         krb5_princ_set_realm_data(context, principal, tmpdata);
220         for (i=0; i < components; i++) {
221                 char *tmpdata2 =
222                   malloc(krb5_princ_component(context, principal, i)->length + 1);
223                 if (!tmpdata2) {
224                         for (i--; i >= 0; i--)
225                                 krb5_xfree(krb5_princ_component(context, principal, i)->data);
226                         krb5_xfree(krb5_princ_realm(context, principal)->data);
227                         krb5_xfree(principal->data);
228                         krb5_xfree(principal);
229                         krb5_xfree(default_realm);
230                         return(ENOMEM);
231                 }
232                 krb5_princ_component(context, principal, i)->data = tmpdata2;
233                 krb5_princ_component(context, principal, i)->magic = KV5M_DATA;
234         }
235         
236         /*
237          * Pass 3.  Now we go through the string a *third* time, this
238          * time filling in the krb5_principal structure which we just
239          * allocated.
240          */
241         q = krb5_princ_component(context, principal, 0)->data;
242         for (i=0,cp = name; (c = *cp); cp++) {
243                 if (c == QUOTECHAR) {
244                         cp++;
245                         switch (c = *cp) {
246                         case 'n':
247                                 *q++ = '\n';
248                                 break;
249                         case 't':
250                                 *q++ = '\t';
251                                 break;
252                         case 'b':
253                                 *q++ = '\b';
254                                 break;
255                         case '0':
256                                 *q++ = '\0';
257                                 break;
258                         default:
259                                 *q++ = c;
260                         }
261                 } else if ((c == COMPONENT_SEP) || (c == REALM_SEP)) {
262                         i++;
263                         *q++ = '\0';
264                         if (c == COMPONENT_SEP) 
265                                 q = krb5_princ_component(context, principal, i)->data;
266                         else
267                                 q = krb5_princ_realm(context, principal)->data;
268                 } else
269                         *q++ = c;
270         }
271         *q++ = '\0';
272         if (!parsed_realm)
273                 strlcpy(krb5_princ_realm(context, principal)->data, default_realm, realmsize + 1);
274         /*
275          * Alright, we're done.  Now stuff a pointer to this monstrosity
276          * into the return variable, and let's get out of here.
277          */
278         krb5_princ_type(context, principal) = KRB5_NT_PRINCIPAL;
279         principal->magic = KV5M_PRINCIPAL;
280         principal->realm.magic = KV5M_DATA;
281         *nprincipal = principal;
282
283         krb5_xfree(default_realm);
284         return(0);
285 }