1 /* 2 * Copyright (c) 1997 - 2005 Kungliga Tekniska H�gskolan 3 * (Royal Institute of Technology, Stockholm, Sweden). 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 17 * 3. Neither the name of the Institute nor the names of its contributors 18 * may be used to endorse or promote products derived from this software 19 * without specific prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND 22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE 25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31 * SUCH DAMAGE. 32 */ 33 34 #include "krb5_locl.h" 35 36 RCSID("$Id: keytab.c 20211 2007-02-09 07:11:03Z lha $"); 37 38 /* 39 * Register a new keytab in `ops' 40 * Return 0 or an error. 41 */ 42 43 krb5_error_code KRB5_LIB_FUNCTION 44 krb5_kt_register(krb5_context context, 45 const krb5_kt_ops *ops) 46 { 47 struct krb5_keytab_data *tmp; 48 49 if (strlen(ops->prefix) > KRB5_KT_PREFIX_MAX_LEN - 1) { 50 krb5_set_error_string(context, "krb5_kt_register; prefix too long"); 51 return KRB5_KT_BADNAME; 52 } 53 54 tmp = realloc(context->kt_types, 55 (context->num_kt_types + 1) * sizeof(*context->kt_types)); 56 if(tmp == NULL) { 57 krb5_set_error_string(context, "malloc: out of memory"); 58 return ENOMEM; 59 } 60 memcpy(&tmp[context->num_kt_types], ops, 61 sizeof(tmp[context->num_kt_types])); 62 context->kt_types = tmp; 63 context->num_kt_types++; 64 return 0; 65 } 66 67 /* 68 * Resolve the keytab name (of the form `type:residual') in `name' 69 * into a keytab in `id'. 70 * Return 0 or an error 71 */ 72 73 krb5_error_code KRB5_LIB_FUNCTION 74 krb5_kt_resolve(krb5_context context, 75 const char *name, 76 krb5_keytab *id) 77 { 78 krb5_keytab k; 79 int i; 80 const char *type, *residual; 81 size_t type_len; 82 krb5_error_code ret; 83 84 residual = strchr(name, ':'); 85 if(residual == NULL) { 86 type = "FILE"; 87 type_len = strlen(type); 88 residual = name; 89 } else { 90 type = name; 91 type_len = residual - name; 92 residual++; 93 } 94 95 for(i = 0; i < context->num_kt_types; i++) { 96 if(strncasecmp(type, context->kt_types[i].prefix, type_len) == 0) 97 break; 98 } 99 if(i == context->num_kt_types) { 100 krb5_set_error_string(context, "unknown keytab type %.*s", 101 (int)type_len, type); 102 return KRB5_KT_UNKNOWN_TYPE; 103 } 104 105 k = malloc (sizeof(*k)); 106 if (k == NULL) { 107 krb5_set_error_string(context, "malloc: out of memory"); 108 return ENOMEM; 109 } 110 memcpy(k, &context->kt_types[i], sizeof(*k)); 111 k->data = NULL; 112 ret = (*k->resolve)(context, residual, k); 113 if(ret) { 114 free(k); 115 k = NULL; 116 } 117 *id = k; 118 return ret; 119 } 120 121 /* 122 * copy the name of the default keytab into `name'. 123 * Return 0 or KRB5_CONFIG_NOTENUFSPACE if `namesize' is too short. 124 */ 125 126 krb5_error_code KRB5_LIB_FUNCTION 127 krb5_kt_default_name(krb5_context context, char *name, size_t namesize) 128 { 129 if (strlcpy (name, context->default_keytab, namesize) >= namesize) { 130 krb5_clear_error_string (context); 131 return KRB5_CONFIG_NOTENUFSPACE; 132 } 133 return 0; 134 } 135 136 /* 137 * copy the name of the default modify keytab into `name'. 138 * Return 0 or KRB5_CONFIG_NOTENUFSPACE if `namesize' is too short. 139 */ 140 141 krb5_error_code KRB5_LIB_FUNCTION 142 krb5_kt_default_modify_name(krb5_context context, char *name, size_t namesize) 143 { 144 const char *kt = NULL; 145 if(context->default_keytab_modify == NULL) { 146 if(strncasecmp(context->default_keytab, "ANY:", 4) != 0) 147 kt = context->default_keytab; 148 else { 149 size_t len = strcspn(context->default_keytab + 4, ","); 150 if(len >= namesize) { 151 krb5_clear_error_string(context); 152 return KRB5_CONFIG_NOTENUFSPACE; 153 } 154 strlcpy(name, context->default_keytab + 4, namesize); 155 name[len] = '\0'; 156 return 0; 157 } 158 } else 159 kt = context->default_keytab_modify; 160 if (strlcpy (name, kt, namesize) >= namesize) { 161 krb5_clear_error_string (context); 162 return KRB5_CONFIG_NOTENUFSPACE; 163 } 164 return 0; 165 } 166 167 /* 168 * Set `id' to the default keytab. 169 * Return 0 or an error. 170 */ 171 172 krb5_error_code KRB5_LIB_FUNCTION 173 krb5_kt_default(krb5_context context, krb5_keytab *id) 174 { 175 return krb5_kt_resolve (context, context->default_keytab, id); 176 } 177 178 /* 179 * Read the key identified by `(principal, vno, enctype)' from the 180 * keytab in `keyprocarg' (the default if == NULL) into `*key'. 181 * Return 0 or an error. 182 */ 183 184 krb5_error_code KRB5_LIB_FUNCTION 185 krb5_kt_read_service_key(krb5_context context, 186 krb5_pointer keyprocarg, 187 krb5_principal principal, 188 krb5_kvno vno, 189 krb5_enctype enctype, 190 krb5_keyblock **key) 191 { 192 krb5_keytab keytab; 193 krb5_keytab_entry entry; 194 krb5_error_code ret; 195 196 if (keyprocarg) 197 ret = krb5_kt_resolve (context, keyprocarg, &keytab); 198 else 199 ret = krb5_kt_default (context, &keytab); 200 201 if (ret) 202 return ret; 203 204 ret = krb5_kt_get_entry (context, keytab, principal, vno, enctype, &entry); 205 krb5_kt_close (context, keytab); 206 if (ret) 207 return ret; 208 ret = krb5_copy_keyblock (context, &entry.keyblock, key); 209 krb5_kt_free_entry(context, &entry); 210 return ret; 211 } 212 213 /* 214 * Return the type of the `keytab' in the string `prefix of length 215 * `prefixsize'. 216 */ 217 218 krb5_error_code KRB5_LIB_FUNCTION 219 krb5_kt_get_type(krb5_context context, 220 krb5_keytab keytab, 221 char *prefix, 222 size_t prefixsize) 223 { 224 strlcpy(prefix, keytab->prefix, prefixsize); 225 return 0; 226 } 227 228 /* 229 * Retrieve the name of the keytab `keytab' into `name', `namesize' 230 * Return 0 or an error. 231 */ 232 233 krb5_error_code KRB5_LIB_FUNCTION 234 krb5_kt_get_name(krb5_context context, 235 krb5_keytab keytab, 236 char *name, 237 size_t namesize) 238 { 239 return (*keytab->get_name)(context, keytab, name, namesize); 240 } 241 242 /* 243 * Retrieve the full name of the keytab `keytab' and store the name in 244 * `str'. `str' needs to be freed by the caller using free(3). 245 * Returns 0 or an error. On error, *str is set to NULL. 246 */ 247 248 krb5_error_code KRB5_LIB_FUNCTION 249 krb5_kt_get_full_name(krb5_context context, 250 krb5_keytab keytab, 251 char **str) 252 { 253 char type[KRB5_KT_PREFIX_MAX_LEN]; 254 char name[MAXPATHLEN]; 255 krb5_error_code ret; 256 257 *str = NULL; 258 259 ret = krb5_kt_get_type(context, keytab, type, sizeof(type)); 260 if (ret) 261 return ret; 262 263 ret = krb5_kt_get_name(context, keytab, name, sizeof(name)); 264 if (ret) 265 return ret; 266 267 if (asprintf(str, "%s:%s", type, name) == -1) { 268 krb5_set_error_string(context, "malloc - out of memory"); 269 *str = NULL; 270 return ENOMEM; 271 } 272 273 return 0; 274 } 275 276 /* 277 * Finish using the keytab in `id'. All resources will be released, 278 * even on errors. Return 0 or an error. 279 */ 280 281 krb5_error_code KRB5_LIB_FUNCTION 282 krb5_kt_close(krb5_context context, 283 krb5_keytab id) 284 { 285 krb5_error_code ret; 286 287 ret = (*id->close)(context, id); 288 memset(id, 0, sizeof(*id)); 289 free(id); 290 return ret; 291 } 292 293 /* 294 * Compare `entry' against `principal, vno, enctype'. 295 * Any of `principal, vno, enctype' might be 0 which acts as a wildcard. 296 * Return TRUE if they compare the same, FALSE otherwise. 297 */ 298 299 krb5_boolean KRB5_LIB_FUNCTION 300 krb5_kt_compare(krb5_context context, 301 krb5_keytab_entry *entry, 302 krb5_const_principal principal, 303 krb5_kvno vno, 304 krb5_enctype enctype) 305 { 306 if(principal != NULL && 307 !krb5_principal_compare(context, entry->principal, principal)) 308 return FALSE; 309 if(vno && vno != entry->vno) 310 return FALSE; 311 if(enctype && enctype != entry->keyblock.keytype) 312 return FALSE; 313 return TRUE; 314 } 315 316 /* 317 * Retrieve the keytab entry for `principal, kvno, enctype' into `entry' 318 * from the keytab `id'. 319 * kvno == 0 is a wildcard and gives the keytab with the highest vno. 320 * Return 0 or an error. 321 */ 322 323 krb5_error_code KRB5_LIB_FUNCTION 324 krb5_kt_get_entry(krb5_context context, 325 krb5_keytab id, 326 krb5_const_principal principal, 327 krb5_kvno kvno, 328 krb5_enctype enctype, 329 krb5_keytab_entry *entry) 330 { 331 krb5_keytab_entry tmp; 332 krb5_error_code ret; 333 krb5_kt_cursor cursor; 334 335 if(id->get) 336 return (*id->get)(context, id, principal, kvno, enctype, entry); 337 338 ret = krb5_kt_start_seq_get (context, id, &cursor); 339 if (ret) { 340 krb5_clear_error_string(context); 341 return KRB5_KT_NOTFOUND; /* XXX i.e. file not found */ 342 } 343 344 entry->vno = 0; 345 while (krb5_kt_next_entry(context, id, &tmp, &cursor) == 0) { 346 if (krb5_kt_compare(context, &tmp, principal, 0, enctype)) { 347 /* the file keytab might only store the lower 8 bits of 348 the kvno, so only compare those bits */ 349 if (kvno == tmp.vno 350 || (tmp.vno < 256 && kvno % 256 == tmp.vno)) { 351 krb5_kt_copy_entry_contents (context, &tmp, entry); 352 krb5_kt_free_entry (context, &tmp); 353 krb5_kt_end_seq_get(context, id, &cursor); 354 return 0; 355 } else if (kvno == 0 && tmp.vno > entry->vno) { 356 if (entry->vno) 357 krb5_kt_free_entry (context, entry); 358 krb5_kt_copy_entry_contents (context, &tmp, entry); 359 } 360 } 361 krb5_kt_free_entry(context, &tmp); 362 } 363 krb5_kt_end_seq_get (context, id, &cursor); 364 if (entry->vno) { 365 return 0; 366 } else { 367 char princ[256], kvno_str[25], *kt_name; 368 char *enctype_str = NULL; 369 370 krb5_unparse_name_fixed (context, principal, princ, sizeof(princ)); 371 krb5_kt_get_full_name (context, id, &kt_name); 372 krb5_enctype_to_string(context, enctype, &enctype_str); 373 374 if (kvno) 375 snprintf(kvno_str, sizeof(kvno_str), "(kvno %d)", kvno); 376 else 377 kvno_str[0] = '\0'; 378 379 krb5_set_error_string (context, 380 "Failed to find %s%s in keytab %s (%s)", 381 princ, 382 kvno_str, 383 kt_name ? kt_name : "unknown keytab", 384 enctype_str ? enctype_str : "unknown enctype"); 385 free(kt_name); 386 free(enctype_str); 387 return KRB5_KT_NOTFOUND; 388 } 389 } 390 391 /* 392 * Copy the contents of `in' into `out'. 393 * Return 0 or an error. */ 394 395 krb5_error_code KRB5_LIB_FUNCTION 396 krb5_kt_copy_entry_contents(krb5_context context, 397 const krb5_keytab_entry *in, 398 krb5_keytab_entry *out) 399 { 400 krb5_error_code ret; 401 402 memset(out, 0, sizeof(*out)); 403 out->vno = in->vno; 404 405 ret = krb5_copy_principal (context, in->principal, &out->principal); 406 if (ret) 407 goto fail; 408 ret = krb5_copy_keyblock_contents (context, 409 &in->keyblock, 410 &out->keyblock); 411 if (ret) 412 goto fail; 413 out->timestamp = in->timestamp; 414 return 0; 415 fail: 416 krb5_kt_free_entry (context, out); 417 return ret; 418 } 419 420 /* 421 * Free the contents of `entry'. 422 */ 423 424 krb5_error_code KRB5_LIB_FUNCTION 425 krb5_kt_free_entry(krb5_context context, 426 krb5_keytab_entry *entry) 427 { 428 krb5_free_principal (context, entry->principal); 429 krb5_free_keyblock_contents (context, &entry->keyblock); 430 memset(entry, 0, sizeof(*entry)); 431 return 0; 432 } 433 434 /* 435 * Set `cursor' to point at the beginning of `id'. 436 * Return 0 or an error. 437 */ 438 439 krb5_error_code KRB5_LIB_FUNCTION 440 krb5_kt_start_seq_get(krb5_context context, 441 krb5_keytab id, 442 krb5_kt_cursor *cursor) 443 { 444 if(id->start_seq_get == NULL) { 445 krb5_set_error_string(context, 446 "start_seq_get is not supported in the %s " 447 " keytab", id->prefix); 448 return HEIM_ERR_OPNOTSUPP; 449 } 450 return (*id->start_seq_get)(context, id, cursor); 451 } 452 453 /* 454 * Get the next entry from `id' pointed to by `cursor' and advance the 455 * `cursor'. 456 * Return 0 or an error. 457 */ 458 459 krb5_error_code KRB5_LIB_FUNCTION 460 krb5_kt_next_entry(krb5_context context, 461 krb5_keytab id, 462 krb5_keytab_entry *entry, 463 krb5_kt_cursor *cursor) 464 { 465 if(id->next_entry == NULL) { 466 krb5_set_error_string(context, 467 "next_entry is not supported in the %s " 468 " keytab", id->prefix); 469 return HEIM_ERR_OPNOTSUPP; 470 } 471 return (*id->next_entry)(context, id, entry, cursor); 472 } 473 474 /* 475 * Release all resources associated with `cursor'. 476 */ 477 478 krb5_error_code KRB5_LIB_FUNCTION 479 krb5_kt_end_seq_get(krb5_context context, 480 krb5_keytab id, 481 krb5_kt_cursor *cursor) 482 { 483 if(id->end_seq_get == NULL) { 484 krb5_set_error_string(context, 485 "end_seq_get is not supported in the %s " 486 " keytab", id->prefix); 487 return HEIM_ERR_OPNOTSUPP; 488 } 489 return (*id->end_seq_get)(context, id, cursor); 490 } 491 492 /* 493 * Add the entry in `entry' to the keytab `id'. 494 * Return 0 or an error. 495 */ 496 497 krb5_error_code KRB5_LIB_FUNCTION 498 krb5_kt_add_entry(krb5_context context, 499 krb5_keytab id, 500 krb5_keytab_entry *entry) 501 { 502 if(id->add == NULL) { 503 krb5_set_error_string(context, "Add is not supported in the %s keytab", 504 id->prefix); 505 return KRB5_KT_NOWRITE; 506 } 507 entry->timestamp = time(NULL); 508 return (*id->add)(context, id,entry); 509 } 510 511 /* 512 * Remove the entry `entry' from the keytab `id'. 513 * Return 0 or an error. 514 */ 515 516 krb5_error_code KRB5_LIB_FUNCTION 517 krb5_kt_remove_entry(krb5_context context, 518 krb5_keytab id, 519 krb5_keytab_entry *entry) 520 { 521 if(id->remove == NULL) { 522 krb5_set_error_string(context, 523 "Remove is not supported in the %s keytab", 524 id->prefix); 525 return KRB5_KT_NOWRITE; 526 } 527 return (*id->remove)(context, id, entry); 528 } 529