1 #pragma ident "%Z%%M% %I% %E% SMI" 2 /* 3 * lib/krb5/krb/chk_trans.c 4 * 5 * Copyright 2001 by the Massachusetts Institute of Technology. 6 * All Rights Reserved. 7 * 8 * Export of this software from the United States of America may 9 * require a specific license from the United States Government. 10 * It is the responsibility of any person or organization contemplating 11 * export to obtain such a license before exporting. 12 * 13 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and 14 * distribute this software and its documentation for any purpose and 15 * without fee is hereby granted, provided that the above copyright 16 * notice appear in all copies and that both that copyright notice and 17 * this permission notice appear in supporting documentation, and that 18 * the name of M.I.T. not be used in advertising or publicity pertaining 19 * to distribution of the software without specific, written prior 20 * permission. Furthermore if you modify this software you must label 21 * your software as modified software and not distribute it in such a 22 * fashion that it might be confused with the original M.I.T. software. 23 * M.I.T. makes no representations about the suitability of 24 * this software for any purpose. It is provided "as is" without express 25 * or implied warranty. 26 * 27 * 28 * krb5_check_transited_list() 29 */ 30 #include <k5-int.h> 31 #include <stdarg.h> 32 33 #define MAXLEN 512 34 35 static krb5_error_code 36 process_intermediates (krb5_error_code (*fn)(krb5_data *, void *), void *data, 37 const krb5_data *n1, const krb5_data *n2) { 38 unsigned int len1, len2, i; 39 char *p1, *p2; 40 41 len1 = n1->length; 42 len2 = n2->length; 43 44 /* Simplify... */ 45 if (len1 > len2) { 46 const krb5_data *p; 47 int tmp = len1; 48 len1 = len2; 49 len2 = tmp; 50 p = n1; 51 n1 = n2; 52 n2 = p; 53 } 54 /* Okay, now len1 is always shorter or equal. */ 55 if (len1 == len2) { 56 if (memcmp (n1->data, n2->data, len1)) { 57 return KRB5KRB_AP_ERR_ILL_CR_TKT; 58 } 59 return 0; 60 } 61 /* Now len1 is always shorter. */ 62 if (len1 == 0) 63 /* Shouldn't be possible. Internal error? */ 64 return KRB5KRB_AP_ERR_ILL_CR_TKT; 65 p1 = n1->data; 66 p2 = n2->data; 67 if (p1[0] == '/') { 68 /* X.500 style names, with common prefix. */ 69 if (p2[0] != '/') { 70 return KRB5KRB_AP_ERR_ILL_CR_TKT; 71 } 72 if (memcmp (p1, p2, len1)) { 73 return KRB5KRB_AP_ERR_ILL_CR_TKT; 74 } 75 for (i = len1 + 1; i < len2; i++) 76 if (p2[i] == '/') { 77 krb5_data d; 78 krb5_error_code r; 79 80 d.data = p2; 81 d.length = i; 82 r = (*fn) (&d, data); 83 if (r) 84 return r; 85 } 86 } else { 87 /* Domain style names, with common suffix. */ 88 if (p2[0] == '/') { 89 return KRB5KRB_AP_ERR_ILL_CR_TKT; 90 } 91 if (memcmp (p1, p2 + (len2 - len1), len1)) { 92 return KRB5KRB_AP_ERR_ILL_CR_TKT; 93 } 94 for (i = len2 - len1 - 1; i > 0; i--) { 95 if (p2[i-1] == '.') { 96 krb5_data d; 97 krb5_error_code r; 98 99 d.data = p2+i; 100 d.length = len2 - i; 101 r = (*fn) (&d, data); 102 if (r) 103 return r; 104 } 105 } 106 } 107 return 0; 108 } 109 110 static krb5_error_code 111 maybe_join (krb5_data *last, krb5_data *buf, int bufsiz) 112 { 113 if (buf->length == 0) 114 return 0; 115 if (buf->data[0] == '/') { 116 if (last->length + buf->length > bufsiz) { 117 return KRB5KRB_AP_ERR_ILL_CR_TKT; 118 } 119 memmove (buf->data+last->length, buf->data, buf->length); 120 memcpy (buf->data, last->data, last->length); 121 buf->length += last->length; 122 } else if (buf->data[buf->length-1] == '.') { 123 /* We can ignore the case where the previous component was 124 empty; the strcat will be a no-op. It should probably 125 be an error case, but let's be flexible. */ 126 if (last->length+buf->length > bufsiz) { 127 return KRB5KRB_AP_ERR_ILL_CR_TKT; 128 } 129 memcpy (buf->data + buf->length, last->data, last->length); 130 buf->length += last->length; 131 } 132 /* Otherwise, do nothing. */ 133 return 0; 134 } 135 136 /* The input strings cannot contain any \0 bytes, according to the 137 spec, but our API is such that they may not be \0 terminated 138 either. Thus we keep on treating them as krb5_data objects instead 139 of C strings. */ 140 static krb5_error_code 141 foreach_realm (krb5_error_code (*fn)(krb5_data *comp,void *data), void *data, 142 const krb5_data *crealm, const krb5_data *srealm, 143 const krb5_data *transit) 144 { 145 char buf[MAXLEN], last[MAXLEN]; 146 char *p, *bufp; 147 int next_lit, intermediates, l; 148 krb5_data this_component; 149 krb5_error_code r; 150 krb5_data last_component; 151 152 /* Invariants: 153 - last_component points to last[] 154 - this_component points to buf[] 155 - last_component has length of last 156 - this_component has length of buf when calling out 157 Keep these consistent, and we should be okay. */ 158 159 next_lit = 0; 160 intermediates = 0; 161 memset (buf, 0, sizeof (buf)); 162 163 this_component.data = buf; 164 last_component.data = last; 165 last_component.length = 0; 166 167 if (transit->length == 0) { 168 return 0; 169 } 170 171 bufp = buf; 172 for (p = transit->data, l = transit->length; l; p++, l--) { 173 if (next_lit) { 174 *bufp++ = *p; 175 if (bufp == buf+sizeof(buf)) 176 return KRB5KRB_AP_ERR_ILL_CR_TKT; 177 next_lit = 0; 178 } else if (*p == '\\') { 179 next_lit = 1; 180 } else if (*p == ',') { 181 if (bufp != buf) { 182 this_component.length = bufp - buf; 183 r = maybe_join (&last_component, &this_component, sizeof(buf)); 184 if (r) 185 return r; 186 r = (*fn) (&this_component, data); 187 if (r) 188 return r; 189 if (intermediates) { 190 if (p == transit->data) 191 r = process_intermediates (fn, data, 192 &this_component, crealm); 193 else { 194 r = process_intermediates (fn, data, &this_component, 195 &last_component); 196 } 197 if (r) 198 return r; 199 } 200 intermediates = 0; 201 memcpy (last, buf, sizeof (buf)); 202 last_component.length = this_component.length; 203 memset (buf, 0, sizeof (buf)); 204 bufp = buf; 205 } else { 206 intermediates = 1; 207 if (p == transit->data) { 208 if (crealm->length >= MAXLEN) 209 return KRB5KRB_AP_ERR_ILL_CR_TKT; 210 memcpy (last, crealm->data, crealm->length); 211 last[crealm->length] = '\0'; 212 last_component.length = crealm->length; 213 } 214 } 215 } else if (*p == ' ' && bufp == buf) { 216 /* This next component stands alone, even if it has a 217 trailing dot or leading slash. */ 218 memset (last, 0, sizeof (last)); 219 last_component.length = 0; 220 } else { 221 /* Not a special character; literal. */ 222 *bufp++ = *p; 223 if (bufp == buf+sizeof(buf)) 224 return KRB5KRB_AP_ERR_ILL_CR_TKT; 225 } 226 } 227 /* At end. Must be normal state. */ 228 /* Process trailing element or comma. */ 229 if (bufp == buf) { 230 /* Trailing comma. */ 231 r = process_intermediates (fn, data, &last_component, srealm); 232 } else { 233 /* Trailing component. */ 234 this_component.length = bufp - buf; 235 r = maybe_join (&last_component, &this_component, sizeof(buf)); 236 if (r) 237 return r; 238 r = (*fn) (&this_component, data); 239 if (r) 240 return r; 241 if (intermediates) 242 r = process_intermediates (fn, data, &this_component, 243 &last_component); 244 } 245 if (r != 0) 246 return r; 247 return 0; 248 } 249 250 251 struct check_data { 252 krb5_context ctx; 253 krb5_principal *tgs; 254 }; 255 256 static int 257 same_data (krb5_data *d1, krb5_data *d2) 258 { 259 return (d1->length == d2->length 260 && !memcmp (d1->data, d2->data, d1->length)); 261 } 262 263 static krb5_error_code 264 check_realm_in_list (krb5_data *realm, void *data) 265 { 266 struct check_data *cdata = data; 267 int i; 268 269 for (i = 0; cdata->tgs[i]; i++) { 270 if (same_data (krb5_princ_realm (cdata->ctx, cdata->tgs[i]), realm)) 271 return 0; 272 } 273 return KRB5KRB_AP_ERR_ILL_CR_TKT; 274 } 275 276 krb5_error_code 277 krb5_check_transited_list (krb5_context ctx, const krb5_data *trans_in, 278 const krb5_data *crealm, const krb5_data *srealm) 279 { 280 krb5_data trans; 281 struct check_data cdata; 282 krb5_error_code r; 283 284 /* 285 * Work around buggy implementations that include NULL terminator in length. 286 */ 287 trans.length = trans_in->length; 288 trans.data = (char *) trans_in->data; 289 if (trans.length && (trans.data[trans.length-1] == '\0')) 290 trans.length--; 291 292 if (trans.length == 0) 293 return 0; 294 295 r = krb5_walk_realm_tree (ctx, crealm, srealm, &cdata.tgs, 296 KRB5_REALM_BRANCH_CHAR); 297 if (r) { 298 return r; 299 } 300 #ifdef DEBUG /* avoid compiler warning about 'd' unused */ 301 { 302 int i; 303 for (i = 0; cdata.tgs[i]; i++) { 304 char *name; 305 r = krb5_unparse_name (ctx, cdata.tgs[i], &name); 306 free (name); 307 } 308 } 309 #endif 310 cdata.ctx = ctx; 311 r = foreach_realm (check_realm_in_list, &cdata, crealm, srealm, &trans); 312 krb5_free_realm_tree (ctx, cdata.tgs); 313 return r; 314 } 315