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 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 * 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 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