1 /* 2 * Copyright (c) 1997, 1998, 1999 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_file.c,v 1.6 2000/01/02 00:20:22 assar Exp $"); 37 38 #define KRB5_KT_VNO_1 1 39 #define KRB5_KT_VNO_2 2 40 #define KRB5_KT_VNO KRB5_KT_VNO_2 41 42 /* file operations -------------------------------------------- */ 43 44 struct fkt_data { 45 char *filename; 46 }; 47 48 static krb5_error_code 49 krb5_kt_ret_data(krb5_storage *sp, 50 krb5_data *data) 51 { 52 int ret; 53 int16_t size; 54 ret = krb5_ret_int16(sp, &size); 55 if(ret) 56 return ret; 57 data->length = size; 58 data->data = malloc(size); 59 if (data->data == NULL) 60 return ENOMEM; 61 ret = sp->fetch(sp, data->data, size); 62 if(ret != size) 63 return (ret < 0)? errno : KRB5_KT_END; 64 return 0; 65 } 66 67 static krb5_error_code 68 krb5_kt_ret_string(krb5_storage *sp, 69 general_string *data) 70 { 71 int ret; 72 int16_t size; 73 ret = krb5_ret_int16(sp, &size); 74 if(ret) 75 return ret; 76 *data = malloc(size + 1); 77 if (*data == NULL) 78 return ENOMEM; 79 ret = sp->fetch(sp, *data, size); 80 (*data)[size] = '\0'; 81 if(ret != size) 82 return (ret < 0)? errno : KRB5_KT_END; 83 return 0; 84 } 85 86 static krb5_error_code 87 krb5_kt_store_data(krb5_storage *sp, 88 krb5_data data) 89 { 90 int ret; 91 ret = krb5_store_int16(sp, data.length); 92 if(ret < 0) 93 return ret; 94 ret = sp->store(sp, data.data, data.length); 95 if(ret != data.length){ 96 if(ret < 0) 97 return errno; 98 return KRB5_KT_END; 99 } 100 return 0; 101 } 102 103 static krb5_error_code 104 krb5_kt_store_string(krb5_storage *sp, 105 general_string data) 106 { 107 int ret; 108 size_t len = strlen(data); 109 ret = krb5_store_int16(sp, len); 110 if(ret < 0) 111 return ret; 112 ret = sp->store(sp, data, len); 113 if(ret != len){ 114 if(ret < 0) 115 return errno; 116 return KRB5_KT_END; 117 } 118 return 0; 119 } 120 121 static krb5_error_code 122 krb5_kt_ret_keyblock(krb5_storage *sp, krb5_keyblock *p) 123 { 124 int ret; 125 int16_t tmp; 126 127 ret = krb5_ret_int16(sp, &tmp); /* keytype + etype */ 128 if(ret) return ret; 129 p->keytype = tmp; 130 ret = krb5_kt_ret_data(sp, &p->keyvalue); 131 return ret; 132 } 133 134 static krb5_error_code 135 krb5_kt_store_keyblock(krb5_storage *sp, 136 krb5_keyblock *p) 137 { 138 int ret; 139 140 ret = krb5_store_int16(sp, p->keytype); /* keytype + etype */ 141 if(ret) return ret; 142 ret = krb5_kt_store_data(sp, p->keyvalue); 143 return ret; 144 } 145 146 147 static krb5_error_code 148 krb5_kt_ret_principal(krb5_storage *sp, 149 krb5_principal *princ) 150 { 151 int i; 152 int ret; 153 krb5_principal p; 154 int16_t tmp; 155 156 ALLOC(p, 1); 157 if(p == NULL) 158 return ENOMEM; 159 160 ret = krb5_ret_int16(sp, &tmp); 161 if(ret) 162 return ret; 163 if (sp->flags & KRB5_STORAGE_PRINCIPAL_WRONG_NUM_COMPONENTS) 164 tmp--; 165 p->name.name_string.len = tmp; 166 ret = krb5_kt_ret_string(sp, &p->realm); 167 if(ret) return ret; 168 p->name.name_string.val = calloc(p->name.name_string.len, 169 sizeof(*p->name.name_string.val)); 170 if(p->name.name_string.val == NULL) 171 return ENOMEM; 172 for(i = 0; i < p->name.name_string.len; i++){ 173 ret = krb5_kt_ret_string(sp, p->name.name_string.val + i); 174 if(ret) return ret; 175 } 176 if (krb5_storage_is_flags(sp, KRB5_STORAGE_PRINCIPAL_NO_NAME_TYPE)) 177 p->name.name_type = KRB5_NT_UNKNOWN; 178 else { 179 int32_t tmp32; 180 ret = krb5_ret_int32(sp, &tmp32); 181 p->name.name_type = tmp32; 182 if (ret) 183 return ret; 184 } 185 *princ = p; 186 return 0; 187 } 188 189 static krb5_error_code 190 krb5_kt_store_principal(krb5_storage *sp, 191 krb5_principal p) 192 { 193 int i; 194 int ret; 195 196 if(krb5_storage_is_flags(sp, KRB5_STORAGE_PRINCIPAL_WRONG_NUM_COMPONENTS)) 197 ret = krb5_store_int16(sp, p->name.name_string.len + 1); 198 else 199 ret = krb5_store_int16(sp, p->name.name_string.len); 200 if(ret) return ret; 201 ret = krb5_kt_store_string(sp, p->realm); 202 if(ret) return ret; 203 for(i = 0; i < p->name.name_string.len; i++){ 204 ret = krb5_kt_store_string(sp, p->name.name_string.val[i]); 205 if(ret) return ret; 206 } 207 if(!krb5_storage_is_flags(sp, KRB5_STORAGE_PRINCIPAL_NO_NAME_TYPE)) { 208 ret = krb5_store_int32(sp, p->name.name_type); 209 if(ret) 210 return ret; 211 } 212 213 return 0; 214 } 215 216 static krb5_error_code 217 fkt_resolve(krb5_context context, const char *name, krb5_keytab id) 218 { 219 struct fkt_data *d; 220 d = malloc(sizeof(*d)); 221 if(d == NULL) 222 return ENOMEM; 223 d->filename = strdup(name); 224 if(d->filename == NULL) { 225 free(d); 226 return ENOMEM; 227 } 228 id->data = d; 229 return 0; 230 } 231 232 static krb5_error_code 233 fkt_close(krb5_context context, krb5_keytab id) 234 { 235 struct fkt_data *d = id->data; 236 free(d->filename); 237 free(d); 238 return 0; 239 } 240 241 static krb5_error_code 242 fkt_get_name(krb5_context context, 243 krb5_keytab id, 244 char *name, 245 size_t namesize) 246 { 247 /* This function is XXX */ 248 struct fkt_data *d = id->data; 249 strlcpy(name, d->filename, namesize); 250 return 0; 251 } 252 253 static void 254 storage_set_flags(krb5_context context, krb5_storage *sp, int vno) 255 { 256 int flags = 0; 257 switch(vno) { 258 case KRB5_KT_VNO_1: 259 flags |= KRB5_STORAGE_PRINCIPAL_WRONG_NUM_COMPONENTS; 260 flags |= KRB5_STORAGE_PRINCIPAL_NO_NAME_TYPE; 261 flags |= KRB5_STORAGE_HOST_BYTEORDER; 262 break; 263 case KRB5_KT_VNO_2: 264 break; 265 default: 266 krb5_abortx(context, 267 "storage_set_flags called with bad vno (%x)", vno); 268 } 269 krb5_storage_set_flags(sp, flags); 270 } 271 272 static krb5_error_code 273 fkt_start_seq_get_int(krb5_context context, 274 krb5_keytab id, 275 int flags, 276 krb5_kt_cursor *c) 277 { 278 int8_t pvno, tag; 279 krb5_error_code ret; 280 struct fkt_data *d = id->data; 281 282 c->fd = open (d->filename, flags); 283 if (c->fd < 0) 284 return errno; 285 c->sp = krb5_storage_from_fd(c->fd); 286 ret = krb5_ret_int8(c->sp, &pvno); 287 if(ret) { 288 krb5_storage_free(c->sp); 289 close(c->fd); 290 return ret; 291 } 292 if(pvno != 5) { 293 krb5_storage_free(c->sp); 294 close(c->fd); 295 return KRB5_KEYTAB_BADVNO; 296 } 297 ret = krb5_ret_int8(c->sp, &tag); 298 if (ret) { 299 krb5_storage_free(c->sp); 300 close(c->fd); 301 return ret; 302 } 303 id->version = tag; 304 storage_set_flags(context, c->sp, id->version); 305 return 0; 306 } 307 308 static krb5_error_code 309 fkt_start_seq_get(krb5_context context, 310 krb5_keytab id, 311 krb5_kt_cursor *c) 312 { 313 return fkt_start_seq_get_int(context, id, O_RDONLY | O_BINARY, c); 314 } 315 316 static krb5_error_code 317 fkt_next_entry_int(krb5_context context, 318 krb5_keytab id, 319 krb5_keytab_entry *entry, 320 krb5_kt_cursor *cursor, 321 off_t *start, 322 off_t *end) 323 { 324 int32_t len; 325 int ret; 326 int8_t tmp8; 327 int32_t tmp32; 328 off_t pos; 329 330 pos = cursor->sp->seek(cursor->sp, 0, SEEK_CUR); 331 loop: 332 ret = krb5_ret_int32(cursor->sp, &len); 333 if (ret) 334 return ret; 335 if(len < 0) { 336 pos = cursor->sp->seek(cursor->sp, -len, SEEK_CUR); 337 goto loop; 338 } 339 ret = krb5_kt_ret_principal (cursor->sp, &entry->principal); 340 if (ret) 341 goto out; 342 ret = krb5_ret_int32(cursor->sp, &tmp32); 343 entry->timestamp = tmp32; 344 if (ret) 345 goto out; 346 ret = krb5_ret_int8(cursor->sp, &tmp8); 347 if (ret) 348 goto out; 349 entry->vno = tmp8; 350 ret = krb5_kt_ret_keyblock (cursor->sp, &entry->keyblock); 351 if (ret) 352 goto out; 353 if(start) *start = pos; 354 if(end) *end = *start + 4 + len; 355 out: 356 cursor->sp->seek(cursor->sp, pos + 4 + len, SEEK_SET); 357 return ret; 358 } 359 360 static krb5_error_code 361 fkt_next_entry(krb5_context context, 362 krb5_keytab id, 363 krb5_keytab_entry *entry, 364 krb5_kt_cursor *cursor) 365 { 366 return fkt_next_entry_int(context, id, entry, cursor, NULL, NULL); 367 } 368 369 static krb5_error_code 370 fkt_end_seq_get(krb5_context context, 371 krb5_keytab id, 372 krb5_kt_cursor *cursor) 373 { 374 krb5_storage_free(cursor->sp); 375 close(cursor->fd); 376 return 0; 377 } 378 379 static krb5_error_code 380 fkt_add_entry(krb5_context context, 381 krb5_keytab id, 382 krb5_keytab_entry *entry) 383 { 384 int ret; 385 int fd; 386 krb5_storage *sp; 387 struct fkt_data *d = id->data; 388 krb5_data keytab; 389 int32_t len; 390 391 fd = open (d->filename, O_RDWR | O_BINARY); 392 if (fd < 0) { 393 fd = open (d->filename, O_RDWR | O_CREAT | O_BINARY, 0600); 394 if (fd < 0) 395 return errno; 396 sp = krb5_storage_from_fd(fd); 397 ret = krb5_store_int8(sp, 5); 398 if(ret) { 399 krb5_storage_free(sp); 400 close(fd); 401 return ret; 402 } 403 if(id->version == 0) 404 id->version = KRB5_KT_VNO; 405 ret = krb5_store_int8 (sp, id->version); 406 if (ret) { 407 krb5_storage_free(sp); 408 close(fd); 409 return ret; 410 } 411 storage_set_flags(context, sp, id->version); 412 } else { 413 int8_t pvno, tag; 414 sp = krb5_storage_from_fd(fd); 415 ret = krb5_ret_int8(sp, &pvno); 416 if(ret) { 417 krb5_storage_free(sp); 418 close(fd); 419 return ret; 420 } 421 if(pvno != 5) { 422 krb5_storage_free(sp); 423 close(fd); 424 return KRB5_KEYTAB_BADVNO; 425 } 426 ret = krb5_ret_int8 (sp, &tag); 427 if (ret) { 428 krb5_storage_free(sp); 429 close(fd); 430 return ret; 431 } 432 id->version = tag; 433 storage_set_flags(context, sp, id->version); 434 } 435 436 { 437 krb5_storage *emem; 438 emem = krb5_storage_emem(); 439 if(emem == NULL) { 440 ret = ENOMEM; 441 goto out; 442 } 443 ret = krb5_kt_store_principal(emem, entry->principal); 444 if(ret) { 445 krb5_storage_free(emem); 446 goto out; 447 } 448 ret = krb5_store_int32 (emem, entry->timestamp); 449 if(ret) { 450 krb5_storage_free(emem); 451 goto out; 452 } 453 ret = krb5_store_int8 (emem, entry->vno); 454 if(ret) { 455 krb5_storage_free(emem); 456 goto out; 457 } 458 ret = krb5_kt_store_keyblock (emem, &entry->keyblock); 459 if(ret) { 460 krb5_storage_free(emem); 461 goto out; 462 } 463 ret = krb5_storage_to_data(emem, &keytab); 464 krb5_storage_free(emem); 465 if(ret) 466 goto out; 467 } 468 469 while(1) { 470 ret = krb5_ret_int32(sp, &len); 471 if(ret == KRB5_CC_END) { 472 len = keytab.length; 473 break; 474 } 475 if(len < 0) { 476 len = -len; 477 if(len >= keytab.length) { 478 sp->seek(sp, -4, SEEK_CUR); 479 break; 480 } 481 } 482 sp->seek(sp, len, SEEK_CUR); 483 } 484 ret = krb5_store_int32(sp, len); 485 if(sp->store(sp, keytab.data, keytab.length) < 0) 486 ret = errno; 487 memset(keytab.data, 0, keytab.length); 488 krb5_data_free(&keytab); 489 out: 490 krb5_storage_free(sp); 491 close(fd); 492 return ret; 493 } 494 495 static krb5_error_code 496 fkt_remove_entry(krb5_context context, 497 krb5_keytab id, 498 krb5_keytab_entry *entry) 499 { 500 krb5_keytab_entry e; 501 krb5_kt_cursor cursor; 502 off_t pos_start, pos_end; 503 int found = 0; 504 505 fkt_start_seq_get_int(context, id, O_RDWR | O_BINARY, &cursor); 506 while(fkt_next_entry_int(context, id, &e, &cursor, 507 &pos_start, &pos_end) == 0) { 508 if(krb5_kt_compare(context, &e, entry->principal, 509 entry->vno, entry->keyblock.keytype)) { 510 int32_t len; 511 unsigned char buf[128]; 512 found = 1; 513 cursor.sp->seek(cursor.sp, pos_start, SEEK_SET); 514 len = pos_end - pos_start - 4; 515 krb5_store_int32(cursor.sp, -len); 516 memset(buf, 0, sizeof(buf)); 517 while(len > 0) { 518 cursor.sp->store(cursor.sp, buf, min(len, sizeof(buf))); 519 len -= min(len, sizeof(buf)); 520 } 521 } 522 } 523 krb5_kt_end_seq_get(context, id, &cursor); 524 if (!found) 525 return KRB5_KT_NOTFOUND; 526 return 0; 527 } 528 529 const krb5_kt_ops krb5_fkt_ops = { 530 "FILE", 531 fkt_resolve, 532 fkt_get_name, 533 fkt_close, 534 NULL, /* get */ 535 fkt_start_seq_get, 536 fkt_next_entry, 537 fkt_end_seq_get, 538 fkt_add_entry, 539 fkt_remove_entry 540 }; 541