xref: /freebsd/crypto/krb5/src/kdc/kdc_transit.c (revision 7f2fe78b9dd5f51c821d771b63d2e096f6fd49e9)
1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /* kdc/kdc_transit.c */
3 /*
4  * Copyright (C) 2012 by the Massachusetts Institute of Technology.
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  * * Redistributions of source code must retain the above copyright
12  *   notice, this list of conditions and the following disclaimer.
13  *
14  * * Redistributions in binary form must reproduce the above copyright
15  *   notice, this list of conditions and the following disclaimer in
16  *   the documentation and/or other materials provided with the
17  *   distribution.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
22  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
23  * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
24  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
25  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
26  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
28  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
30  * OF THE POSSIBILITY OF SUCH DAMAGE.
31  */
32 
33 #include "k5-int.h"
34 #include "kdc_util.h"
35 
36 #define MAX_REALM_LN 500
37 
38 /*
39  * subrealm - determine if r2 is a subrealm of r1
40  *
41  *            SUBREALM takes two realms, r1 and r2, and
42  *            determines if r2 is a subrealm of r1.
43  *            r2 is a subrealm of r1 if (r1 is a prefix
44  *            of r2 AND r1 and r2 begin with a /) or if
45  *            (r1 is a suffix of r2 and neither r1 nor r2
46  *            begin with a /).
47  *
48  * RETURNS:   If r2 is a subrealm, and r1 is a prefix, the number
49  *            of characters in the suffix of r2 is returned as a
50  *            negative number.
51  *
52  *            If r2 is a subrealm, and r1 is a suffix, the number
53  *            of characters in the prefix of r2 is returned as a
54  *            positive number.
55  *
56  *            If r2 is not a subrealm, SUBREALM returns 0.
57  */
58 static  int
subrealm(char * r1,char * r2)59 subrealm(char *r1, char *r2)
60 {
61     size_t l1,l2;
62     l1 = strlen(r1);
63     l2 = strlen(r2);
64     if(l2 <= l1) return(0);
65     if((*r1 == '/') && (*r2 == '/') && (strncmp(r1,r2,l1) == 0)) return(l1-l2);
66     if((*r1 != '/') && (*r2 != '/') && (strncmp(r1,r2+l2-l1,l1) == 0))
67         return(l2-l1);
68     return(0);
69 }
70 
71 /*
72  * add_to_transited  Adds the name of the realm which issued the
73  *                   ticket granting ticket on which the new ticket to
74  *                   be issued is based (note that this is the same as
75  *                   the realm of the server listed in the ticket
76  *                   granting ticket.
77  *
78  * ASSUMPTIONS:  This procedure assumes that the transited field from
79  *               the existing ticket granting ticket already appears
80  *               in compressed form.  It will add the new realm while
81  *               maintaining that form.   As long as each successive
82  *               realm is added using this (or a similar) routine, the
83  *               transited field will be in compressed form.  The
84  *               basis step is an empty transited field which is, by
85  *               its nature, in its most compressed form.
86  *
87  * ARGUMENTS: krb5_data *tgt_trans  Transited field from TGT
88  *            krb5_data *new_trans  The transited field for the new ticket
89  *            krb5_principal tgs    Name of ticket granting server
90  *                                  This includes the realm of the KDC
91  *                                  that issued the ticket granting
92  *                                  ticket.  This is the realm that is
93  *                                  to be added to the transited field.
94  *            krb5_principal client Name of the client
95  *            krb5_principal server The name of the requested server.
96  *                                  This may be the an intermediate
97  *                                  ticket granting server.
98  *
99  *            The last two argument are needed since they are
100  *            implicitly part of the transited field of the new ticket
101  *            even though they are not explicitly listed.
102  *
103  * RETURNS:   krb5_error_code - Success, or out of memory
104  *
105  * MODIFIES:  new_trans:  ->length will contain the length of the new
106  *                        transited field.
107  *
108  *                        If ->data was not null when this procedure
109  *                        is called, the memory referenced by ->data
110  *                        will be deallocated.
111  *
112  *                        Memory will be allocated for the new transited field
113  *                        ->data will be updated to point to the newly
114  *                        allocated memory.
115  *
116  * BUGS:  The space allocated for the new transited field is the
117  *        maximum that might be needed given the old transited field,
118  *        and the realm to be added.  This length is calculated
119  *        assuming that no compression of the new realm is possible.
120  *        This has no adverse consequences other than the allocation
121  *        of more space than required.
122  *
123  *        This procedure will not yet use the null subfield notation,
124  *        and it will get confused if it sees it.
125  *
126  *        This procedure does not check for quoted commas in realm
127  *        names.
128  */
129 
130 char *
data2string(krb5_data * d)131 data2string (krb5_data *d)
132 {
133     char *s;
134     s = malloc(d->length + 1);
135     if (s) {
136         if (d->length > 0)
137             memcpy(s, d->data, d->length);
138         s[d->length] = 0;
139     }
140     return s;
141 }
142 
143 krb5_error_code
add_to_transited(krb5_data * tgt_trans,krb5_data * new_trans,krb5_principal tgs,krb5_principal client,krb5_principal server)144 add_to_transited(krb5_data *tgt_trans, krb5_data *new_trans,
145                  krb5_principal tgs, krb5_principal client,
146                  krb5_principal server)
147 {
148     krb5_error_code retval;
149     char        *realm;
150     char        *trans;
151     char        *otrans, *otrans_ptr;
152     size_t       bufsize;
153 
154     /* The following are for stepping through the transited field     */
155 
156     char        prev[MAX_REALM_LN];
157     char        next[MAX_REALM_LN];
158     char        current[MAX_REALM_LN];
159     char        exp[MAX_REALM_LN];      /* Expanded current realm name     */
160 
161     int         i;
162     int         clst, nlst;    /* count of last character in current and next */
163     int         pl, pl1;       /* prefix length                               */
164     int         added;         /* TRUE = new realm has been added             */
165 
166     realm = data2string(krb5_princ_realm(kdc_context, tgs));
167     if (realm == NULL)
168         return(ENOMEM);
169 
170     otrans = data2string(tgt_trans);
171     if (otrans == NULL) {
172         free(realm);
173         return(ENOMEM);
174     }
175     /* Keep track of start so we can free */
176     otrans_ptr = otrans;
177 
178     /* +1 for null,
179        +1 for extra comma which may be added between
180        +1 for potential space when leading slash in realm */
181     bufsize = strlen(realm) + strlen(otrans) + 3;
182     if (bufsize > MAX_REALM_LN)
183         bufsize = MAX_REALM_LN;
184     if (!(trans = (char *) malloc(bufsize))) {
185         retval = ENOMEM;
186         goto fail;
187     }
188 
189     if (new_trans->data)  free(new_trans->data);
190     new_trans->data = trans;
191     new_trans->length = 0;
192 
193     trans[0] = '\0';
194 
195     /* For the purpose of appending, the realm preceding the first */
196     /* realm in the transited field is considered the null realm   */
197 
198     prev[0] = '\0';
199 
200     /* read field into current */
201     for (i = 0; *otrans != '\0';) {
202         if (*otrans == '\\') {
203             if (*(++otrans) == '\0')
204                 break;
205             else
206                 continue;
207         }
208         if (*otrans == ',') {
209             otrans++;
210             break;
211         }
212         current[i++] = *otrans++;
213         if (i >= MAX_REALM_LN) {
214             retval = KRB5KRB_AP_ERR_ILL_CR_TKT;
215             goto fail;
216         }
217     }
218     current[i] = '\0';
219 
220     added = (krb5_princ_realm(kdc_context, client)->length == strlen(realm) &&
221              !strncmp(krb5_princ_realm(kdc_context, client)->data, realm, strlen(realm))) ||
222         (krb5_princ_realm(kdc_context, server)->length == strlen(realm) &&
223          !strncmp(krb5_princ_realm(kdc_context, server)->data, realm, strlen(realm)));
224 
225     while (current[0]) {
226 
227         /* figure out expanded form of current name */
228 
229         clst = strlen(current) - 1;
230         if (current[0] == ' ') {
231             strncpy(exp, current+1, sizeof(exp) - 1);
232             exp[sizeof(exp) - 1] = '\0';
233         }
234         else if ((current[0] == '/') && (prev[0] == '/')) {
235             strncpy(exp, prev, sizeof(exp) - 1);
236             exp[sizeof(exp) - 1] = '\0';
237             if (strlen(exp) + strlen(current) + 1 >= MAX_REALM_LN) {
238                 retval = KRB5KRB_AP_ERR_ILL_CR_TKT;
239                 goto fail;
240             }
241             strncat(exp, current, sizeof(exp) - 1 - strlen(exp));
242         }
243         else if (current[clst] == '.') {
244             strncpy(exp, current, sizeof(exp) - 1);
245             exp[sizeof(exp) - 1] = '\0';
246             if (strlen(exp) + strlen(prev) + 1 >= MAX_REALM_LN) {
247                 retval = KRB5KRB_AP_ERR_ILL_CR_TKT;
248                 goto fail;
249             }
250             strncat(exp, prev, sizeof(exp) - 1 - strlen(exp));
251         }
252         else {
253             strncpy(exp, current, sizeof(exp) - 1);
254             exp[sizeof(exp) - 1] = '\0';
255         }
256 
257         /* read field into next */
258         for (i = 0; *otrans != '\0';) {
259             if (*otrans == '\\') {
260                 if (*(++otrans) == '\0')
261                     break;
262                 else
263                     continue;
264             }
265             if (*otrans == ',') {
266                 otrans++;
267                 break;
268             }
269             next[i++] = *otrans++;
270             if (i >= MAX_REALM_LN) {
271                 retval = KRB5KRB_AP_ERR_ILL_CR_TKT;
272                 goto fail;
273             }
274         }
275         next[i] = '\0';
276         nlst = i - 1;
277 
278         if (!strcmp(exp, realm))  added = TRUE;
279 
280         /* If we still have to insert the new realm */
281 
282         if (!added) {
283 
284             /* Is the next field compressed?  If not, and if the new */
285             /* realm is a subrealm of the current realm, compress    */
286             /* the new realm, and insert immediately following the   */
287             /* current one.  Note that we can not do this if the next*/
288             /* field is already compressed since it would mess up    */
289             /* what has already been done.  In most cases, this is   */
290             /* not a problem because the realm to be added will be a */
291             /* subrealm of the next field too, and we will catch     */
292             /* it in a future iteration.                             */
293 
294             /* Note that the second test here is an unsigned comparison,
295                so the first half (or a cast) is also required.  */
296             assert(nlst < 0 || nlst < (int)sizeof(next));
297             if ((nlst < 0 || next[nlst] != '.') &&
298                 (next[0] != '/') &&
299                 (pl = subrealm(exp, realm))) {
300                 added = TRUE;
301                 current[sizeof(current) - 1] = '\0';
302                 if (strlen(current) + (pl>0?pl:-pl) + 2 >= MAX_REALM_LN) {
303                     retval = KRB5KRB_AP_ERR_ILL_CR_TKT;
304                     goto fail;
305                 }
306                 strncat(current, ",", sizeof(current) - 1 - strlen(current));
307                 if (pl > 0) {
308                     strncat(current, realm, (unsigned) pl);
309                 }
310                 else {
311                     strncat(current, realm+strlen(realm)+pl, (unsigned) (-pl));
312                 }
313             }
314 
315             /* Whether or not the next field is compressed, if the    */
316             /* realm to be added is a superrealm of the current realm,*/
317             /* then the current realm can be compressed.  First the   */
318             /* realm to be added must be compressed relative to the   */
319             /* previous realm (if possible), and then the current     */
320             /* realm compressed relative to the new realm.  Note that */
321             /* if the realm to be added is also a superrealm of the   */
322             /* previous realm, it would have been added earlier, and  */
323             /* we would not reach this step this time around.         */
324 
325             else if ((pl = subrealm(realm, exp))) {
326                 added      = TRUE;
327                 current[0] = '\0';
328                 if ((pl1 = subrealm(prev,realm))) {
329                     if (strlen(current) + (pl1>0?pl1:-pl1) + 1 >= MAX_REALM_LN) {
330                         retval = KRB5KRB_AP_ERR_ILL_CR_TKT;
331                         goto fail;
332                     }
333                     if (pl1 > 0) {
334                         strncat(current, realm, (unsigned) pl1);
335                     }
336                     else {
337                         strncat(current, realm+strlen(realm)+pl1, (unsigned) (-pl1));
338                     }
339                 }
340                 else { /* If not a subrealm */
341                     if ((realm[0] == '/') && prev[0]) {
342                         if (strlen(current) + 2 >= MAX_REALM_LN) {
343                             retval = KRB5KRB_AP_ERR_ILL_CR_TKT;
344                             goto fail;
345                         }
346                         strncat(current, " ", sizeof(current) - 1 - strlen(current));
347                         current[sizeof(current) - 1] = '\0';
348                     }
349                     if (strlen(current) + strlen(realm) + 1 >= MAX_REALM_LN) {
350                         retval = KRB5KRB_AP_ERR_ILL_CR_TKT;
351                         goto fail;
352                     }
353                     strncat(current, realm, sizeof(current) - 1 - strlen(current));
354                     current[sizeof(current) - 1] = '\0';
355                 }
356                 if (strlen(current) + (pl>0?pl:-pl) + 2 >= MAX_REALM_LN) {
357                     retval = KRB5KRB_AP_ERR_ILL_CR_TKT;
358                     goto fail;
359                 }
360                 strncat(current,",", sizeof(current) - 1 - strlen(current));
361                 current[sizeof(current) - 1] = '\0';
362                 if (pl > 0) {
363                     strncat(current, exp, (unsigned) pl);
364                 }
365                 else {
366                     strncat(current, exp+strlen(exp)+pl, (unsigned)(-pl));
367                 }
368             }
369         }
370 
371         if (new_trans->length != 0) {
372             if (strlcat(trans, ",", bufsize) >= bufsize) {
373                 retval = KRB5KRB_AP_ERR_ILL_CR_TKT;
374                 goto fail;
375             }
376         }
377         if (strlcat(trans, current, bufsize) >= bufsize) {
378             retval = KRB5KRB_AP_ERR_ILL_CR_TKT;
379             goto fail;
380         }
381         new_trans->length = strlen(trans);
382 
383         strncpy(prev, exp, sizeof(prev) - 1);
384         prev[sizeof(prev) - 1] = '\0';
385         strncpy(current, next, sizeof(current) - 1);
386         current[sizeof(current) - 1] = '\0';
387     }
388 
389     if (!added) {
390         if (new_trans->length != 0) {
391             if (strlcat(trans, ",", bufsize) >= bufsize) {
392                 retval = KRB5KRB_AP_ERR_ILL_CR_TKT;
393                 goto fail;
394             }
395         }
396         if((realm[0] == '/') && trans[0]) {
397             if (strlcat(trans, " ", bufsize) >= bufsize) {
398                 retval = KRB5KRB_AP_ERR_ILL_CR_TKT;
399                 goto fail;
400             }
401         }
402         if (strlcat(trans, realm, bufsize) >= bufsize) {
403             retval = KRB5KRB_AP_ERR_ILL_CR_TKT;
404             goto fail;
405         }
406         new_trans->length = strlen(trans);
407     }
408 
409     retval = 0;
410 fail:
411     free(realm);
412     free(otrans_ptr);
413     return (retval);
414 }
415