1 /* 2 * Copyright (c) 1997 - 2003 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,v 1.55 2003/03/27 03:45:01 lha Exp $"); 37 38 /* 39 * Register a new keytab in `ops' 40 * Return 0 or an error. 41 */ 42 43 krb5_error_code 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_NAME_TOOLONG; 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 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 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 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 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 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 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 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 * Finish using the keytab in `id'. All resources will be released. 244 * Return 0 or an error. 245 */ 246 247 krb5_error_code 248 krb5_kt_close(krb5_context context, 249 krb5_keytab id) 250 { 251 krb5_error_code ret; 252 253 ret = (*id->close)(context, id); 254 if(ret == 0) 255 free(id); 256 return ret; 257 } 258 259 /* 260 * Compare `entry' against `principal, vno, enctype'. 261 * Any of `principal, vno, enctype' might be 0 which acts as a wildcard. 262 * Return TRUE if they compare the same, FALSE otherwise. 263 */ 264 265 krb5_boolean 266 krb5_kt_compare(krb5_context context, 267 krb5_keytab_entry *entry, 268 krb5_const_principal principal, 269 krb5_kvno vno, 270 krb5_enctype enctype) 271 { 272 if(principal != NULL && 273 !krb5_principal_compare(context, entry->principal, principal)) 274 return FALSE; 275 if(vno && vno != entry->vno) 276 return FALSE; 277 if(enctype && enctype != entry->keyblock.keytype) 278 return FALSE; 279 return TRUE; 280 } 281 282 /* 283 * Retrieve the keytab entry for `principal, kvno, enctype' into `entry' 284 * from the keytab `id'. 285 * kvno == 0 is a wildcard and gives the keytab with the highest vno. 286 * Return 0 or an error. 287 */ 288 289 krb5_error_code 290 krb5_kt_get_entry(krb5_context context, 291 krb5_keytab id, 292 krb5_const_principal principal, 293 krb5_kvno kvno, 294 krb5_enctype enctype, 295 krb5_keytab_entry *entry) 296 { 297 krb5_keytab_entry tmp; 298 krb5_error_code ret; 299 krb5_kt_cursor cursor; 300 301 if(id->get) 302 return (*id->get)(context, id, principal, kvno, enctype, entry); 303 304 ret = krb5_kt_start_seq_get (context, id, &cursor); 305 if (ret) 306 return KRB5_KT_NOTFOUND; /* XXX i.e. file not found */ 307 308 entry->vno = 0; 309 while (krb5_kt_next_entry(context, id, &tmp, &cursor) == 0) { 310 if (krb5_kt_compare(context, &tmp, principal, 0, enctype)) { 311 /* the file keytab might only store the lower 8 bits of 312 the kvno, so only compare those bits */ 313 if (kvno == tmp.vno 314 || (tmp.vno < 256 && kvno % 256 == tmp.vno)) { 315 krb5_kt_copy_entry_contents (context, &tmp, entry); 316 krb5_kt_free_entry (context, &tmp); 317 krb5_kt_end_seq_get(context, id, &cursor); 318 return 0; 319 } else if (kvno == 0 && tmp.vno > entry->vno) { 320 if (entry->vno) 321 krb5_kt_free_entry (context, entry); 322 krb5_kt_copy_entry_contents (context, &tmp, entry); 323 } 324 } 325 krb5_kt_free_entry(context, &tmp); 326 } 327 krb5_kt_end_seq_get (context, id, &cursor); 328 if (entry->vno) { 329 return 0; 330 } else { 331 char princ[256], kt_name[256], kvno_str[25]; 332 333 krb5_unparse_name_fixed (context, principal, princ, sizeof(princ)); 334 krb5_kt_get_name (context, id, kt_name, sizeof(kt_name)); 335 336 if (kvno) 337 snprintf(kvno_str, sizeof(kvno_str), "(kvno %d)", kvno); 338 else 339 kvno_str[0] = '\0'; 340 341 krb5_set_error_string (context, 342 "failed to find %s%s in keytab %s", 343 princ, 344 kvno_str, 345 kt_name); 346 return KRB5_KT_NOTFOUND; 347 } 348 } 349 350 /* 351 * Copy the contents of `in' into `out'. 352 * Return 0 or an error. */ 353 354 krb5_error_code 355 krb5_kt_copy_entry_contents(krb5_context context, 356 const krb5_keytab_entry *in, 357 krb5_keytab_entry *out) 358 { 359 krb5_error_code ret; 360 361 memset(out, 0, sizeof(*out)); 362 out->vno = in->vno; 363 364 ret = krb5_copy_principal (context, in->principal, &out->principal); 365 if (ret) 366 goto fail; 367 ret = krb5_copy_keyblock_contents (context, 368 &in->keyblock, 369 &out->keyblock); 370 if (ret) 371 goto fail; 372 out->timestamp = in->timestamp; 373 return 0; 374 fail: 375 krb5_kt_free_entry (context, out); 376 return ret; 377 } 378 379 /* 380 * Free the contents of `entry'. 381 */ 382 383 krb5_error_code 384 krb5_kt_free_entry(krb5_context context, 385 krb5_keytab_entry *entry) 386 { 387 krb5_free_principal (context, entry->principal); 388 krb5_free_keyblock_contents (context, &entry->keyblock); 389 return 0; 390 } 391 392 #if 0 393 static int 394 xxxlock(int fd, int write) 395 { 396 if(flock(fd, (write ? LOCK_EX : LOCK_SH) | LOCK_NB) < 0) { 397 sleep(1); 398 if(flock(fd, (write ? LOCK_EX : LOCK_SH) | LOCK_NB) < 0) 399 return -1; 400 } 401 return 0; 402 } 403 404 static void 405 xxxunlock(int fd) 406 { 407 flock(fd, LOCK_UN); 408 } 409 #endif 410 411 /* 412 * Set `cursor' to point at the beginning of `id'. 413 * Return 0 or an error. 414 */ 415 416 krb5_error_code 417 krb5_kt_start_seq_get(krb5_context context, 418 krb5_keytab id, 419 krb5_kt_cursor *cursor) 420 { 421 if(id->start_seq_get == NULL) { 422 krb5_set_error_string(context, 423 "start_seq_get is not supported in the %s " 424 " keytab", id->prefix); 425 return HEIM_ERR_OPNOTSUPP; 426 } 427 return (*id->start_seq_get)(context, id, cursor); 428 } 429 430 /* 431 * Get the next entry from `id' pointed to by `cursor' and advance the 432 * `cursor'. 433 * Return 0 or an error. 434 */ 435 436 krb5_error_code 437 krb5_kt_next_entry(krb5_context context, 438 krb5_keytab id, 439 krb5_keytab_entry *entry, 440 krb5_kt_cursor *cursor) 441 { 442 if(id->next_entry == NULL) { 443 krb5_set_error_string(context, 444 "next_entry is not supported in the %s " 445 " keytab", id->prefix); 446 return HEIM_ERR_OPNOTSUPP; 447 } 448 return (*id->next_entry)(context, id, entry, cursor); 449 } 450 451 /* 452 * Release all resources associated with `cursor'. 453 */ 454 455 krb5_error_code 456 krb5_kt_end_seq_get(krb5_context context, 457 krb5_keytab id, 458 krb5_kt_cursor *cursor) 459 { 460 if(id->end_seq_get == NULL) { 461 krb5_set_error_string(context, 462 "end_seq_get is not supported in the %s " 463 " keytab", id->prefix); 464 return HEIM_ERR_OPNOTSUPP; 465 } 466 return (*id->end_seq_get)(context, id, cursor); 467 } 468 469 /* 470 * Add the entry in `entry' to the keytab `id'. 471 * Return 0 or an error. 472 */ 473 474 krb5_error_code 475 krb5_kt_add_entry(krb5_context context, 476 krb5_keytab id, 477 krb5_keytab_entry *entry) 478 { 479 if(id->add == NULL) { 480 krb5_set_error_string(context, "Add is not supported in the %s keytab", 481 id->prefix); 482 return KRB5_KT_NOWRITE; 483 } 484 entry->timestamp = time(NULL); 485 return (*id->add)(context, id,entry); 486 } 487 488 /* 489 * Remove the entry `entry' from the keytab `id'. 490 * Return 0 or an error. 491 */ 492 493 krb5_error_code 494 krb5_kt_remove_entry(krb5_context context, 495 krb5_keytab id, 496 krb5_keytab_entry *entry) 497 { 498 if(id->remove == NULL) { 499 krb5_set_error_string(context, 500 "Remove is not supported in the %s keytab", 501 id->prefix); 502 return KRB5_KT_NOWRITE; 503 } 504 return (*id->remove)(context, id, entry); 505 } 506