1 /* 2 * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 3 * Use is subject to license terms. 4 */ 5 6 /* 7 * lib/krb5/krb/parse.c 8 * 9 * Copyright 1990,1991,2008 by the Massachusetts Institute of Technology. 10 * All Rights Reserved. 11 * 12 * Export of this software from the United States of America may 13 * require a specific license from the United States Government. 14 * It is the responsibility of any person or organization contemplating 15 * export to obtain such a license before exporting. 16 * 17 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and 18 * distribute this software and its documentation for any purpose and 19 * without fee is hereby granted, provided that the above copyright 20 * notice appear in all copies and that both that copyright notice and 21 * this permission notice appear in supporting documentation, and that 22 * the name of M.I.T. not be used in advertising or publicity pertaining 23 * to distribution of the software without specific, written prior 24 * permission. Furthermore if you modify this software you must label 25 * your software as modified software and not distribute it in such a 26 * fashion that it might be confused with the original M.I.T. software. 27 * M.I.T. makes no representations about the suitability of 28 * this software for any purpose. It is provided "as is" without express 29 * or implied warranty. 30 * 31 * 32 * krb5_parse_name() routine. 33 * 34 * Rewritten by Theodore Ts'o to properly handle arbitrary quoted 35 * characters in the principal name. 36 */ 37 38 39 #include "k5-int.h" 40 41 /* 42 * converts a single-string representation of the name to the 43 * multi-part principal format used in the protocols. 44 * 45 * principal will point to allocated storage which should be freed by 46 * the caller (using krb5_free_principal) after use. 47 * 48 * Conventions: / is used to separate components. If @ is present in the 49 * string, then the rest of the string after it represents the realm name. 50 * Otherwise the local realm name is used. 51 * 52 * error return: 53 * KRB5_PARSE_MALFORMED badly formatted string 54 * 55 * also returns system errors: 56 * ENOMEM malloc failed/out of memory 57 * 58 * get_default_realm() is called; it may return other errors. 59 */ 60 61 #define REALM_SEP '@' 62 #define COMPONENT_SEP '/' 63 #define QUOTECHAR '\\' 64 65 #define FCOMPNUM 10 66 67 /* 68 * May the fleas of a thousand camels infest the ISO, they who think 69 * that arbitrarily large multi-component names are a Good Thing..... 70 */ 71 static krb5_error_code 72 /*LINTED*/ 73 k5_parse_name(krb5_context context, const char *name, 74 int flags, krb5_principal *nprincipal) 75 { 76 register const char *cp; 77 register char *q; 78 register int i,c,size; 79 int components = 0; 80 const char *parsed_realm = NULL; 81 int fcompsize[FCOMPNUM]; 82 unsigned int realmsize = 0; 83 #ifndef _KERNEL 84 char *default_realm = NULL; 85 int default_realm_size = 0; 86 krb5_error_code retval; 87 #endif 88 char *tmpdata; 89 krb5_principal principal; 90 unsigned int enterprise = (flags & KRB5_PRINCIPAL_PARSE_ENTERPRISE); 91 int first_at; 92 93 *nprincipal = NULL; 94 95 /* 96 * Pass 1. Find out how many components there are to the name, 97 * and get string sizes for the first FCOMPNUM components. For 98 * enterprise principal names (UPNs), there is only a single 99 * component. 100 */ 101 size = 0; 102 /*LINTED*/ 103 for (i=0,cp = name, first_at = 1; (c = *cp); cp++) { 104 if (c == QUOTECHAR) { 105 cp++; 106 /*LINTED*/ 107 if (!(c = *cp)) 108 /* 109 * QUOTECHAR can't be at the last 110 * character of the name! 111 */ 112 return(KRB5_PARSE_MALFORMED); 113 size++; 114 continue; 115 } else if (c == COMPONENT_SEP && !enterprise) { 116 if (parsed_realm) 117 /* 118 * Shouldn't see a component separator 119 * after we've parsed out the realm name! 120 */ 121 return(KRB5_PARSE_MALFORMED); 122 if (i < FCOMPNUM) { 123 fcompsize[i] = size; 124 } 125 size = 0; 126 i++; 127 } else if (c == REALM_SEP && (!enterprise || !first_at)) { 128 if (parsed_realm) 129 /* 130 * Multiple realm separaters 131 * not allowed; zero-length realms are. 132 */ 133 return(KRB5_PARSE_MALFORMED); 134 parsed_realm = cp + 1; 135 if (i < FCOMPNUM) { 136 fcompsize[i] = size; 137 } 138 size = 0; 139 } else { 140 if (c == REALM_SEP && enterprise && first_at) 141 first_at = 0; 142 143 size++; 144 } 145 } 146 if (parsed_realm != NULL) 147 realmsize = size; 148 else if (i < FCOMPNUM) 149 fcompsize[i] = size; 150 components = i + 1; 151 /* 152 * Now, we allocate the principal structure and all of its 153 * component pieces 154 */ 155 principal = (krb5_principal)MALLOC(sizeof(krb5_principal_data)); 156 if (principal == NULL) { 157 return(ENOMEM); 158 } 159 principal->data = (krb5_data *)MALLOC(sizeof(krb5_data) * components); 160 if (principal->data == NULL) { 161 krb5_xfree_wrap(principal, sizeof(krb5_principal_data)); 162 return ENOMEM; 163 } 164 principal->length = components; 165 166 /* 167 * If a realm was not found, then use the default realm, unless 168 * KRB5_PRINCIPAL_PARSE_NO_REALM was specified in which case the 169 * realm will be empty. 170 */ 171 #ifndef _KERNEL 172 if (!parsed_realm) { 173 if (flags & KRB5_PRINCIPAL_PARSE_REQUIRE_REALM) { 174 krb5_set_error_message(context, KRB5_PARSE_MALFORMED, 175 "Principal %s is missing required realm", name); 176 krb5_xfree_wrap(principal->data, principal->length); 177 krb5_xfree_wrap(principal, sizeof(krb5_principal_data)); 178 return KRB5_PARSE_MALFORMED; 179 } 180 if (!default_realm && (flags & KRB5_PRINCIPAL_PARSE_NO_REALM) == 0) { 181 retval = krb5_get_default_realm(context, &default_realm); 182 if (retval) { 183 krb5_xfree_wrap(principal->data, principal->length); 184 krb5_xfree_wrap(principal, sizeof(krb5_principal_data)); 185 return(retval); 186 } 187 default_realm_size = strlen(default_realm); 188 } 189 realmsize = default_realm_size; 190 } else if (flags & KRB5_PRINCIPAL_PARSE_NO_REALM) { 191 krb5_set_error_message(context, KRB5_PARSE_MALFORMED, 192 "Principal %s has realm present", name); 193 krb5_xfree_wrap(principal->data, principal->length); 194 krb5_xfree_wrap(principal, sizeof(krb5_principal_data)); 195 return KRB5_PARSE_MALFORMED; 196 } 197 #endif 198 199 /* 200 * Pass 2. Happens only if there were more than FCOMPNUM 201 * component; if this happens, someone should be shot 202 * immediately. Nevertheless, we will attempt to handle said 203 * case..... <martyred sigh> 204 */ 205 if (components >= FCOMPNUM) { 206 size = 0; 207 parsed_realm = NULL; 208 /*LINTED*/ 209 for (i=0,cp = name; (c = *cp); cp++) { 210 if (c == QUOTECHAR) { 211 cp++; 212 size++; 213 } else if (c == COMPONENT_SEP) { 214 if (krb5_princ_size(context, principal) > i) 215 krb5_princ_component(context, principal, i)->length = size; 216 size = 0; 217 i++; 218 } else if (c == REALM_SEP) { 219 if (krb5_princ_size(context, principal) > i) 220 krb5_princ_component(context, principal, i)->length = size; 221 size = 0; 222 parsed_realm = cp+1; 223 } else 224 size++; 225 } 226 if (parsed_realm) 227 krb5_princ_realm(context, principal)->length = size; 228 else 229 if (krb5_princ_size(context, principal) > i) 230 krb5_princ_component(context, principal, i)->length = size; 231 if (i + 1 != components) { 232 #ifndef _KERNEL 233 #if !defined(_WIN32) 234 fprintf(stderr, 235 "Programming error in krb5_parse_name!"); 236 #endif 237 ASSERT(i + 1 == components); 238 abort(); 239 #else 240 ASSERT(i + 1 == components); 241 #endif /* !_KERNEL */ 242 243 } 244 } else { 245 /* 246 * If there were fewer than FCOMPSIZE components (the 247 * usual case), then just copy the sizes to the 248 * principal structure 249 */ 250 for (i=0; i < components; i++) 251 krb5_princ_component(context, principal, i)->length = fcompsize[i]; 252 } 253 /* 254 * Now, we need to allocate the space for the strings themselves..... 255 */ 256 tmpdata = MALLOC(realmsize + 1); 257 if (tmpdata == 0) { 258 krb5_xfree_wrap(principal->data, principal->length); 259 krb5_xfree_wrap(principal, sizeof(krb5_principal_data)); 260 #ifndef _KERNEL 261 krb5_xfree_wrap(default_realm, strlen(default_realm)); 262 #endif 263 return ENOMEM; 264 } 265 krb5_princ_set_realm_length(context, principal, realmsize); 266 krb5_princ_set_realm_data(context, principal, tmpdata); 267 for (i=0; i < components; i++) { 268 char *tmpdata2 = 269 MALLOC(krb5_princ_component(context, principal, i)->length + 1); 270 if (tmpdata2 == NULL) { 271 for (i--; i >= 0; i--) 272 krb5_xfree_wrap(krb5_princ_component(context, 273 principal, i)->data, 274 krb5_princ_component(context, 275 principal, i)->length + 1); 276 277 krb5_xfree_wrap(krb5_princ_realm(context, 278 principal)->data, krb5_princ_realm(context, 279 principal)->length + 1); 280 281 krb5_xfree_wrap(principal->data, principal->length); 282 krb5_xfree_wrap(principal, sizeof(krb5_principal_data)); 283 #ifndef _KERNEL 284 krb5_xfree_wrap(default_realm, strlen(default_realm)); 285 #endif 286 return(ENOMEM); 287 } 288 krb5_princ_component(context, principal, i)->data = tmpdata2; 289 krb5_princ_component(context, principal, i)->magic = KV5M_DATA; 290 } 291 292 /* 293 * Pass 3. Now we go through the string a *third* time, this 294 * time filling in the krb5_principal structure which we just 295 * allocated. 296 */ 297 q = krb5_princ_component(context, principal, 0)->data; 298 /*LINTED*/ 299 for (i=0,cp = name, first_at = 1; (c = *cp); cp++) { 300 if (c == QUOTECHAR) { 301 cp++; 302 switch (c = *cp) { 303 case 'n': 304 *q++ = '\n'; 305 break; 306 case 't': 307 *q++ = '\t'; 308 break; 309 case 'b': 310 *q++ = '\b'; 311 break; 312 case '0': 313 *q++ = '\0'; 314 break; 315 default: 316 *q++ = c; 317 break; 318 } 319 } else if (c == COMPONENT_SEP && !enterprise) { 320 i++; 321 *q++ = '\0'; 322 q = krb5_princ_component(context, principal, i)->data; 323 } else if (c == REALM_SEP && (!enterprise || !first_at)) { 324 i++; 325 *q++ = '\0'; 326 q = krb5_princ_realm(context, principal)->data; 327 } else { 328 if (c == REALM_SEP && enterprise && first_at) 329 first_at = 0; 330 331 *q++ = c; 332 } 333 } 334 *q++ = '\0'; 335 /*LINTED*/ 336 if (!parsed_realm) { 337 #ifndef _KERNEL 338 339 if (flags & KRB5_PRINCIPAL_PARSE_NO_REALM) 340 (krb5_princ_realm(context, principal)->data)[0] = '\0'; 341 else 342 strlcpy(krb5_princ_realm(context, principal)->data, default_realm, realmsize+1); 343 #endif 344 } 345 /* 346 * Alright, we're done. Now stuff a pointer to this monstrosity 347 * into the return variable, and let's get out of here. 348 */ 349 if (enterprise) 350 krb5_princ_type(context, principal) = KRB5_NT_ENTERPRISE_PRINCIPAL; 351 else 352 krb5_princ_type(context, principal) = KRB5_NT_PRINCIPAL; 353 principal->magic = KV5M_PRINCIPAL; 354 principal->realm.magic = KV5M_DATA; 355 *nprincipal = principal; 356 357 #ifndef _KERNEL 358 if (default_realm != NULL) 359 krb5_xfree_wrap(default_realm, strlen(default_realm)); 360 #endif 361 362 return(0); 363 } 364 365 krb5_error_code KRB5_CALLCONV 366 krb5_parse_name(krb5_context context, const char *name, krb5_principal *nprincipal) 367 { 368 return k5_parse_name(context, name, 0, nprincipal); 369 } 370 371 krb5_error_code KRB5_CALLCONV 372 krb5_parse_name_flags(krb5_context context, const char *name, 373 int flags, krb5_principal *nprincipal) 374 { 375 return k5_parse_name(context, name, flags, nprincipal); 376 } 377