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