1 /* 2 * Copyright (c) 1997 - 2007 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 "kadm5_locl.h" 35 #include "heim_threads.h" 36 37 RCSID("$Id: log.c 22211 2007-12-07 19:27:27Z lha $"); 38 39 /* 40 * A log record consists of: 41 * 42 * version number 4 bytes 43 * time in seconds 4 bytes 44 * operation (enum kadm_ops) 4 bytes 45 * length of record 4 bytes 46 * data... n bytes 47 * length of record 4 bytes 48 * version number 4 bytes 49 * 50 */ 51 52 kadm5_ret_t 53 kadm5_log_get_version_fd (int fd, 54 uint32_t *ver) 55 { 56 int ret; 57 krb5_storage *sp; 58 int32_t old_version; 59 60 ret = lseek (fd, 0, SEEK_END); 61 if(ret < 0) 62 return errno; 63 if(ret == 0) { 64 *ver = 0; 65 return 0; 66 } 67 sp = krb5_storage_from_fd (fd); 68 krb5_storage_seek(sp, -4, SEEK_CUR); 69 krb5_ret_int32 (sp, &old_version); 70 *ver = old_version; 71 krb5_storage_free(sp); 72 lseek (fd, 0, SEEK_END); 73 return 0; 74 } 75 76 kadm5_ret_t 77 kadm5_log_get_version (kadm5_server_context *context, uint32_t *ver) 78 { 79 return kadm5_log_get_version_fd (context->log_context.log_fd, ver); 80 } 81 82 kadm5_ret_t 83 kadm5_log_set_version (kadm5_server_context *context, uint32_t vno) 84 { 85 kadm5_log_context *log_context = &context->log_context; 86 87 log_context->version = vno; 88 return 0; 89 } 90 91 kadm5_ret_t 92 kadm5_log_init (kadm5_server_context *context) 93 { 94 int fd; 95 kadm5_ret_t ret; 96 kadm5_log_context *log_context = &context->log_context; 97 98 if (log_context->log_fd != -1) 99 return 0; 100 fd = open (log_context->log_file, O_RDWR | O_CREAT, 0600); 101 if (fd < 0) { 102 krb5_set_error_string(context->context, "kadm5_log_init: open %s", 103 log_context->log_file); 104 return errno; 105 } 106 if (flock (fd, LOCK_EX) < 0) { 107 krb5_set_error_string(context->context, "kadm5_log_init: flock %s", 108 log_context->log_file); 109 close (fd); 110 return errno; 111 } 112 113 ret = kadm5_log_get_version_fd (fd, &log_context->version); 114 if (ret) 115 return ret; 116 117 log_context->log_fd = fd; 118 return 0; 119 } 120 121 kadm5_ret_t 122 kadm5_log_reinit (kadm5_server_context *context) 123 { 124 int fd; 125 kadm5_log_context *log_context = &context->log_context; 126 127 if (log_context->log_fd != -1) { 128 flock (log_context->log_fd, LOCK_UN); 129 close (log_context->log_fd); 130 log_context->log_fd = -1; 131 } 132 fd = open (log_context->log_file, O_RDWR | O_CREAT | O_TRUNC, 0600); 133 if (fd < 0) 134 return errno; 135 if (flock (fd, LOCK_EX) < 0) { 136 close (fd); 137 return errno; 138 } 139 140 log_context->version = 0; 141 log_context->log_fd = fd; 142 return 0; 143 } 144 145 146 kadm5_ret_t 147 kadm5_log_end (kadm5_server_context *context) 148 { 149 kadm5_log_context *log_context = &context->log_context; 150 int fd = log_context->log_fd; 151 152 flock (fd, LOCK_UN); 153 close(fd); 154 log_context->log_fd = -1; 155 return 0; 156 } 157 158 static kadm5_ret_t 159 kadm5_log_preamble (kadm5_server_context *context, 160 krb5_storage *sp, 161 enum kadm_ops op) 162 { 163 kadm5_log_context *log_context = &context->log_context; 164 kadm5_ret_t kadm_ret; 165 166 kadm_ret = kadm5_log_init (context); 167 if (kadm_ret) 168 return kadm_ret; 169 170 krb5_store_int32 (sp, ++log_context->version); 171 krb5_store_int32 (sp, time(NULL)); 172 krb5_store_int32 (sp, op); 173 return 0; 174 } 175 176 static kadm5_ret_t 177 kadm5_log_postamble (kadm5_log_context *context, 178 krb5_storage *sp) 179 { 180 krb5_store_int32 (sp, context->version); 181 return 0; 182 } 183 184 /* 185 * flush the log record in `sp'. 186 */ 187 188 static kadm5_ret_t 189 kadm5_log_flush (kadm5_log_context *log_context, 190 krb5_storage *sp) 191 { 192 krb5_data data; 193 size_t len; 194 int ret; 195 196 krb5_storage_to_data(sp, &data); 197 len = data.length; 198 ret = write (log_context->log_fd, data.data, len); 199 if (ret != len) { 200 krb5_data_free(&data); 201 return errno; 202 } 203 if (fsync (log_context->log_fd) < 0) { 204 krb5_data_free(&data); 205 return errno; 206 } 207 /* 208 * Try to send a signal to any running `ipropd-master' 209 */ 210 sendto (log_context->socket_fd, 211 (void *)&log_context->version, 212 sizeof(log_context->version), 213 0, 214 (struct sockaddr *)&log_context->socket_name, 215 sizeof(log_context->socket_name)); 216 217 krb5_data_free(&data); 218 return 0; 219 } 220 221 /* 222 * Add a `create' operation to the log. 223 */ 224 225 kadm5_ret_t 226 kadm5_log_create (kadm5_server_context *context, 227 hdb_entry *ent) 228 { 229 krb5_storage *sp; 230 kadm5_ret_t ret; 231 krb5_data value; 232 kadm5_log_context *log_context = &context->log_context; 233 234 sp = krb5_storage_emem(); 235 ret = hdb_entry2value (context->context, ent, &value); 236 if (ret) { 237 krb5_storage_free(sp); 238 return ret; 239 } 240 ret = kadm5_log_preamble (context, sp, kadm_create); 241 if (ret) { 242 krb5_data_free (&value); 243 krb5_storage_free(sp); 244 return ret; 245 } 246 krb5_store_int32 (sp, value.length); 247 krb5_storage_write(sp, value.data, value.length); 248 krb5_store_int32 (sp, value.length); 249 krb5_data_free (&value); 250 ret = kadm5_log_postamble (log_context, sp); 251 if (ret) { 252 krb5_storage_free (sp); 253 return ret; 254 } 255 ret = kadm5_log_flush (log_context, sp); 256 krb5_storage_free (sp); 257 if (ret) 258 return ret; 259 ret = kadm5_log_end (context); 260 return ret; 261 } 262 263 /* 264 * Read the data of a create log record from `sp' and change the 265 * database. 266 */ 267 268 static kadm5_ret_t 269 kadm5_log_replay_create (kadm5_server_context *context, 270 uint32_t ver, 271 uint32_t len, 272 krb5_storage *sp) 273 { 274 krb5_error_code ret; 275 krb5_data data; 276 hdb_entry_ex ent; 277 278 memset(&ent, 0, sizeof(ent)); 279 280 ret = krb5_data_alloc (&data, len); 281 if (ret) { 282 krb5_set_error_string(context->context, "out of memory"); 283 return ret; 284 } 285 krb5_storage_read (sp, data.data, len); 286 ret = hdb_value2entry (context->context, &data, &ent.entry); 287 krb5_data_free(&data); 288 if (ret) { 289 krb5_set_error_string(context->context, 290 "Unmarshaling hdb entry failed"); 291 return ret; 292 } 293 ret = context->db->hdb_store(context->context, context->db, 0, &ent); 294 hdb_free_entry (context->context, &ent); 295 return ret; 296 } 297 298 /* 299 * Add a `delete' operation to the log. 300 */ 301 302 kadm5_ret_t 303 kadm5_log_delete (kadm5_server_context *context, 304 krb5_principal princ) 305 { 306 krb5_storage *sp; 307 kadm5_ret_t ret; 308 off_t off; 309 off_t len; 310 kadm5_log_context *log_context = &context->log_context; 311 312 sp = krb5_storage_emem(); 313 if (sp == NULL) 314 return ENOMEM; 315 ret = kadm5_log_preamble (context, sp, kadm_delete); 316 if (ret) 317 goto out; 318 ret = krb5_store_int32 (sp, 0); 319 if (ret) 320 goto out; 321 off = krb5_storage_seek (sp, 0, SEEK_CUR); 322 ret = krb5_store_principal (sp, princ); 323 if (ret) 324 goto out; 325 len = krb5_storage_seek (sp, 0, SEEK_CUR) - off; 326 krb5_storage_seek(sp, -(len + 4), SEEK_CUR); 327 ret = krb5_store_int32 (sp, len); 328 if (ret) 329 goto out; 330 krb5_storage_seek(sp, len, SEEK_CUR); 331 ret = krb5_store_int32 (sp, len); 332 if (ret) 333 goto out; 334 ret = kadm5_log_postamble (log_context, sp); 335 if (ret) 336 goto out; 337 ret = kadm5_log_flush (log_context, sp); 338 if (ret) 339 goto out; 340 ret = kadm5_log_end (context); 341 out: 342 krb5_storage_free (sp); 343 return ret; 344 } 345 346 /* 347 * Read a `delete' log operation from `sp' and apply it. 348 */ 349 350 static kadm5_ret_t 351 kadm5_log_replay_delete (kadm5_server_context *context, 352 uint32_t ver, 353 uint32_t len, 354 krb5_storage *sp) 355 { 356 krb5_error_code ret; 357 krb5_principal principal; 358 359 ret = krb5_ret_principal (sp, &principal); 360 if (ret) { 361 krb5_set_error_string(context->context, "Failed to read deleted " 362 "principal from log version: %ld", (long)ver); 363 return ret; 364 } 365 366 ret = context->db->hdb_remove(context->context, context->db, principal); 367 krb5_free_principal (context->context, principal); 368 return ret; 369 } 370 371 /* 372 * Add a `rename' operation to the log. 373 */ 374 375 kadm5_ret_t 376 kadm5_log_rename (kadm5_server_context *context, 377 krb5_principal source, 378 hdb_entry *ent) 379 { 380 krb5_storage *sp; 381 kadm5_ret_t ret; 382 off_t off; 383 off_t len; 384 krb5_data value; 385 kadm5_log_context *log_context = &context->log_context; 386 387 krb5_data_zero(&value); 388 389 sp = krb5_storage_emem(); 390 ret = hdb_entry2value (context->context, ent, &value); 391 if (ret) 392 goto failed; 393 394 ret = kadm5_log_preamble (context, sp, kadm_rename); 395 if (ret) 396 goto failed; 397 398 ret = krb5_store_int32 (sp, 0); 399 if (ret) 400 goto failed; 401 off = krb5_storage_seek (sp, 0, SEEK_CUR); 402 ret = krb5_store_principal (sp, source); 403 if (ret) 404 goto failed; 405 406 krb5_storage_write(sp, value.data, value.length); 407 len = krb5_storage_seek (sp, 0, SEEK_CUR) - off; 408 409 krb5_storage_seek(sp, -(len + 4), SEEK_CUR); 410 ret = krb5_store_int32 (sp, len); 411 if (ret) 412 goto failed; 413 414 krb5_storage_seek(sp, len, SEEK_CUR); 415 ret = krb5_store_int32 (sp, len); 416 if (ret) 417 goto failed; 418 419 ret = kadm5_log_postamble (log_context, sp); 420 if (ret) 421 goto failed; 422 423 ret = kadm5_log_flush (log_context, sp); 424 if (ret) 425 goto failed; 426 krb5_storage_free (sp); 427 krb5_data_free (&value); 428 429 return kadm5_log_end (context); 430 431 failed: 432 krb5_data_free(&value); 433 krb5_storage_free(sp); 434 return ret; 435 } 436 437 /* 438 * Read a `rename' log operation from `sp' and apply it. 439 */ 440 441 static kadm5_ret_t 442 kadm5_log_replay_rename (kadm5_server_context *context, 443 uint32_t ver, 444 uint32_t len, 445 krb5_storage *sp) 446 { 447 krb5_error_code ret; 448 krb5_principal source; 449 hdb_entry_ex target_ent; 450 krb5_data value; 451 off_t off; 452 size_t princ_len, data_len; 453 454 memset(&target_ent, 0, sizeof(target_ent)); 455 456 off = krb5_storage_seek(sp, 0, SEEK_CUR); 457 ret = krb5_ret_principal (sp, &source); 458 if (ret) { 459 krb5_set_error_string(context->context, "Failed to read renamed " 460 "principal in log, version: %ld", (long)ver); 461 return ret; 462 } 463 princ_len = krb5_storage_seek(sp, 0, SEEK_CUR) - off; 464 data_len = len - princ_len; 465 ret = krb5_data_alloc (&value, data_len); 466 if (ret) { 467 krb5_free_principal (context->context, source); 468 return ret; 469 } 470 krb5_storage_read (sp, value.data, data_len); 471 ret = hdb_value2entry (context->context, &value, &target_ent.entry); 472 krb5_data_free(&value); 473 if (ret) { 474 krb5_free_principal (context->context, source); 475 return ret; 476 } 477 ret = context->db->hdb_store (context->context, context->db, 478 0, &target_ent); 479 hdb_free_entry (context->context, &target_ent); 480 if (ret) { 481 krb5_free_principal (context->context, source); 482 return ret; 483 } 484 ret = context->db->hdb_remove (context->context, context->db, source); 485 krb5_free_principal (context->context, source); 486 return ret; 487 } 488 489 490 /* 491 * Add a `modify' operation to the log. 492 */ 493 494 kadm5_ret_t 495 kadm5_log_modify (kadm5_server_context *context, 496 hdb_entry *ent, 497 uint32_t mask) 498 { 499 krb5_storage *sp; 500 kadm5_ret_t ret; 501 krb5_data value; 502 uint32_t len; 503 kadm5_log_context *log_context = &context->log_context; 504 505 krb5_data_zero(&value); 506 507 sp = krb5_storage_emem(); 508 ret = hdb_entry2value (context->context, ent, &value); 509 if (ret) 510 goto failed; 511 512 ret = kadm5_log_preamble (context, sp, kadm_modify); 513 if (ret) 514 goto failed; 515 516 len = value.length + 4; 517 ret = krb5_store_int32 (sp, len); 518 if (ret) 519 goto failed; 520 ret = krb5_store_int32 (sp, mask); 521 if (ret) 522 goto failed; 523 krb5_storage_write (sp, value.data, value.length); 524 525 ret = krb5_store_int32 (sp, len); 526 if (ret) 527 goto failed; 528 ret = kadm5_log_postamble (log_context, sp); 529 if (ret) 530 goto failed; 531 ret = kadm5_log_flush (log_context, sp); 532 if (ret) 533 goto failed; 534 krb5_data_free(&value); 535 krb5_storage_free (sp); 536 return kadm5_log_end (context); 537 failed: 538 krb5_data_free(&value); 539 krb5_storage_free(sp); 540 return ret; 541 } 542 543 /* 544 * Read a `modify' log operation from `sp' and apply it. 545 */ 546 547 static kadm5_ret_t 548 kadm5_log_replay_modify (kadm5_server_context *context, 549 uint32_t ver, 550 uint32_t len, 551 krb5_storage *sp) 552 { 553 krb5_error_code ret; 554 int32_t mask; 555 krb5_data value; 556 hdb_entry_ex ent, log_ent; 557 558 memset(&log_ent, 0, sizeof(log_ent)); 559 560 krb5_ret_int32 (sp, &mask); 561 len -= 4; 562 ret = krb5_data_alloc (&value, len); 563 if (ret) { 564 krb5_set_error_string(context->context, "out of memory"); 565 return ret; 566 } 567 krb5_storage_read (sp, value.data, len); 568 ret = hdb_value2entry (context->context, &value, &log_ent.entry); 569 krb5_data_free(&value); 570 if (ret) 571 return ret; 572 573 memset(&ent, 0, sizeof(ent)); 574 ret = context->db->hdb_fetch(context->context, context->db, 575 log_ent.entry.principal, 576 HDB_F_DECRYPT|HDB_F_GET_ANY, &ent); 577 if (ret) 578 goto out; 579 if (mask & KADM5_PRINC_EXPIRE_TIME) { 580 if (log_ent.entry.valid_end == NULL) { 581 ent.entry.valid_end = NULL; 582 } else { 583 if (ent.entry.valid_end == NULL) { 584 ent.entry.valid_end = malloc(sizeof(*ent.entry.valid_end)); 585 if (ent.entry.valid_end == NULL) { 586 krb5_set_error_string(context->context, "out of memory"); 587 ret = ENOMEM; 588 goto out; 589 } 590 } 591 *ent.entry.valid_end = *log_ent.entry.valid_end; 592 } 593 } 594 if (mask & KADM5_PW_EXPIRATION) { 595 if (log_ent.entry.pw_end == NULL) { 596 ent.entry.pw_end = NULL; 597 } else { 598 if (ent.entry.pw_end == NULL) { 599 ent.entry.pw_end = malloc(sizeof(*ent.entry.pw_end)); 600 if (ent.entry.pw_end == NULL) { 601 krb5_set_error_string(context->context, "out of memory"); 602 ret = ENOMEM; 603 goto out; 604 } 605 } 606 *ent.entry.pw_end = *log_ent.entry.pw_end; 607 } 608 } 609 if (mask & KADM5_LAST_PWD_CHANGE) { 610 abort (); /* XXX */ 611 } 612 if (mask & KADM5_ATTRIBUTES) { 613 ent.entry.flags = log_ent.entry.flags; 614 } 615 if (mask & KADM5_MAX_LIFE) { 616 if (log_ent.entry.max_life == NULL) { 617 ent.entry.max_life = NULL; 618 } else { 619 if (ent.entry.max_life == NULL) { 620 ent.entry.max_life = malloc (sizeof(*ent.entry.max_life)); 621 if (ent.entry.max_life == NULL) { 622 krb5_set_error_string(context->context, "out of memory"); 623 ret = ENOMEM; 624 goto out; 625 } 626 } 627 *ent.entry.max_life = *log_ent.entry.max_life; 628 } 629 } 630 if ((mask & KADM5_MOD_TIME) && (mask & KADM5_MOD_NAME)) { 631 if (ent.entry.modified_by == NULL) { 632 ent.entry.modified_by = malloc(sizeof(*ent.entry.modified_by)); 633 if (ent.entry.modified_by == NULL) { 634 krb5_set_error_string(context->context, "out of memory"); 635 ret = ENOMEM; 636 goto out; 637 } 638 } else 639 free_Event(ent.entry.modified_by); 640 ret = copy_Event(log_ent.entry.modified_by, ent.entry.modified_by); 641 if (ret) { 642 krb5_set_error_string(context->context, "out of memory"); 643 goto out; 644 } 645 } 646 if (mask & KADM5_KVNO) { 647 ent.entry.kvno = log_ent.entry.kvno; 648 } 649 if (mask & KADM5_MKVNO) { 650 abort (); /* XXX */ 651 } 652 if (mask & KADM5_AUX_ATTRIBUTES) { 653 abort (); /* XXX */ 654 } 655 if (mask & KADM5_POLICY) { 656 abort (); /* XXX */ 657 } 658 if (mask & KADM5_POLICY_CLR) { 659 abort (); /* XXX */ 660 } 661 if (mask & KADM5_MAX_RLIFE) { 662 if (log_ent.entry.max_renew == NULL) { 663 ent.entry.max_renew = NULL; 664 } else { 665 if (ent.entry.max_renew == NULL) { 666 ent.entry.max_renew = malloc (sizeof(*ent.entry.max_renew)); 667 if (ent.entry.max_renew == NULL) { 668 krb5_set_error_string(context->context, "out of memory"); 669 ret = ENOMEM; 670 goto out; 671 } 672 } 673 *ent.entry.max_renew = *log_ent.entry.max_renew; 674 } 675 } 676 if (mask & KADM5_LAST_SUCCESS) { 677 abort (); /* XXX */ 678 } 679 if (mask & KADM5_LAST_FAILED) { 680 abort (); /* XXX */ 681 } 682 if (mask & KADM5_FAIL_AUTH_COUNT) { 683 abort (); /* XXX */ 684 } 685 if (mask & KADM5_KEY_DATA) { 686 size_t num; 687 int i; 688 689 for (i = 0; i < ent.entry.keys.len; ++i) 690 free_Key(&ent.entry.keys.val[i]); 691 free (ent.entry.keys.val); 692 693 num = log_ent.entry.keys.len; 694 695 ent.entry.keys.len = num; 696 ent.entry.keys.val = malloc(len * sizeof(*ent.entry.keys.val)); 697 if (ent.entry.keys.val == NULL) { 698 krb5_set_error_string(context->context, "out of memory"); 699 return ENOMEM; 700 } 701 for (i = 0; i < ent.entry.keys.len; ++i) { 702 ret = copy_Key(&log_ent.entry.keys.val[i], 703 &ent.entry.keys.val[i]); 704 if (ret) { 705 krb5_set_error_string(context->context, "out of memory"); 706 goto out; 707 } 708 } 709 } 710 if ((mask & KADM5_TL_DATA) && log_ent.entry.extensions) { 711 HDB_extensions *es = ent.entry.extensions; 712 713 ent.entry.extensions = calloc(1, sizeof(*ent.entry.extensions)); 714 if (ent.entry.extensions == NULL) 715 goto out; 716 717 ret = copy_HDB_extensions(log_ent.entry.extensions, 718 ent.entry.extensions); 719 if (ret) { 720 krb5_set_error_string(context->context, "out of memory"); 721 free(ent.entry.extensions); 722 ent.entry.extensions = es; 723 goto out; 724 } 725 if (es) { 726 free_HDB_extensions(es); 727 free(es); 728 } 729 } 730 ret = context->db->hdb_store(context->context, context->db, 731 HDB_F_REPLACE, &ent); 732 out: 733 hdb_free_entry (context->context, &ent); 734 hdb_free_entry (context->context, &log_ent); 735 return ret; 736 } 737 738 /* 739 * Add a `nop' operation to the log. Does not close the log. 740 */ 741 742 kadm5_ret_t 743 kadm5_log_nop (kadm5_server_context *context) 744 { 745 krb5_storage *sp; 746 kadm5_ret_t ret; 747 kadm5_log_context *log_context = &context->log_context; 748 749 sp = krb5_storage_emem(); 750 ret = kadm5_log_preamble (context, sp, kadm_nop); 751 if (ret) { 752 krb5_storage_free (sp); 753 return ret; 754 } 755 krb5_store_int32 (sp, 0); 756 krb5_store_int32 (sp, 0); 757 ret = kadm5_log_postamble (log_context, sp); 758 if (ret) { 759 krb5_storage_free (sp); 760 return ret; 761 } 762 ret = kadm5_log_flush (log_context, sp); 763 krb5_storage_free (sp); 764 765 return ret; 766 } 767 768 /* 769 * Read a `nop' log operation from `sp' and apply it. 770 */ 771 772 static kadm5_ret_t 773 kadm5_log_replay_nop (kadm5_server_context *context, 774 uint32_t ver, 775 uint32_t len, 776 krb5_storage *sp) 777 { 778 return 0; 779 } 780 781 /* 782 * Call `func' for each log record in the log in `context' 783 */ 784 785 kadm5_ret_t 786 kadm5_log_foreach (kadm5_server_context *context, 787 void (*func)(kadm5_server_context *server_context, 788 uint32_t ver, 789 time_t timestamp, 790 enum kadm_ops op, 791 uint32_t len, 792 krb5_storage *, 793 void *), 794 void *ctx) 795 { 796 int fd = context->log_context.log_fd; 797 krb5_storage *sp; 798 799 lseek (fd, 0, SEEK_SET); 800 sp = krb5_storage_from_fd (fd); 801 for (;;) { 802 int32_t ver, timestamp, op, len, len2, ver2; 803 804 if(krb5_ret_int32 (sp, &ver) != 0) 805 break; 806 krb5_ret_int32 (sp, ×tamp); 807 krb5_ret_int32 (sp, &op); 808 krb5_ret_int32 (sp, &len); 809 (*func)(context, ver, timestamp, op, len, sp, ctx); 810 krb5_ret_int32 (sp, &len2); 811 krb5_ret_int32 (sp, &ver2); 812 if (len != len2) 813 abort(); 814 if (ver != ver2) 815 abort(); 816 } 817 krb5_storage_free(sp); 818 return 0; 819 } 820 821 /* 822 * Go to end of log. 823 */ 824 825 krb5_storage * 826 kadm5_log_goto_end (int fd) 827 { 828 krb5_storage *sp; 829 830 sp = krb5_storage_from_fd (fd); 831 krb5_storage_seek(sp, 0, SEEK_END); 832 return sp; 833 } 834 835 /* 836 * Return previous log entry. 837 * 838 * The pointer in `sp� is assumed to be at the top of the entry before 839 * previous entry. On success, the `sp� pointer is set to data portion 840 * of previous entry. In case of error, it's not changed at all. 841 */ 842 843 kadm5_ret_t 844 kadm5_log_previous (krb5_context context, 845 krb5_storage *sp, 846 uint32_t *ver, 847 time_t *timestamp, 848 enum kadm_ops *op, 849 uint32_t *len) 850 { 851 krb5_error_code ret; 852 off_t off, oldoff; 853 int32_t tmp; 854 855 oldoff = krb5_storage_seek(sp, 0, SEEK_CUR); 856 857 krb5_storage_seek(sp, -8, SEEK_CUR); 858 ret = krb5_ret_int32 (sp, &tmp); 859 if (ret) 860 goto end_of_storage; 861 *len = tmp; 862 ret = krb5_ret_int32 (sp, &tmp); 863 *ver = tmp; 864 off = 24 + *len; 865 krb5_storage_seek(sp, -off, SEEK_CUR); 866 ret = krb5_ret_int32 (sp, &tmp); 867 if (ret) 868 goto end_of_storage; 869 if (tmp != *ver) { 870 krb5_storage_seek(sp, oldoff, SEEK_SET); 871 krb5_set_error_string(context, "kadm5_log_previous: log entry " 872 "have consistency failure, version number wrong"); 873 return KADM5_BAD_DB; 874 } 875 ret = krb5_ret_int32 (sp, &tmp); 876 if (ret) 877 goto end_of_storage; 878 *timestamp = tmp; 879 ret = krb5_ret_int32 (sp, &tmp); 880 *op = tmp; 881 ret = krb5_ret_int32 (sp, &tmp); 882 if (ret) 883 goto end_of_storage; 884 if (tmp != *len) { 885 krb5_storage_seek(sp, oldoff, SEEK_SET); 886 krb5_set_error_string(context, "kadm5_log_previous: log entry " 887 "have consistency failure, length wrong"); 888 return KADM5_BAD_DB; 889 } 890 return 0; 891 892 end_of_storage: 893 krb5_storage_seek(sp, oldoff, SEEK_SET); 894 krb5_set_error_string(context, "kadm5_log_previous: end of storage " 895 "reached before end"); 896 return ret; 897 } 898 899 /* 900 * Replay a record from the log 901 */ 902 903 kadm5_ret_t 904 kadm5_log_replay (kadm5_server_context *context, 905 enum kadm_ops op, 906 uint32_t ver, 907 uint32_t len, 908 krb5_storage *sp) 909 { 910 switch (op) { 911 case kadm_create : 912 return kadm5_log_replay_create (context, ver, len, sp); 913 case kadm_delete : 914 return kadm5_log_replay_delete (context, ver, len, sp); 915 case kadm_rename : 916 return kadm5_log_replay_rename (context, ver, len, sp); 917 case kadm_modify : 918 return kadm5_log_replay_modify (context, ver, len, sp); 919 case kadm_nop : 920 return kadm5_log_replay_nop (context, ver, len, sp); 921 default : 922 krb5_set_error_string(context->context, 923 "Unsupported replay op %d", (int)op); 924 return KADM5_FAILURE; 925 } 926 } 927 928 /* 929 * truncate the log - i.e. create an empty file with just (nop vno + 2) 930 */ 931 932 kadm5_ret_t 933 kadm5_log_truncate (kadm5_server_context *server_context) 934 { 935 kadm5_ret_t ret; 936 uint32_t vno; 937 938 ret = kadm5_log_init (server_context); 939 if (ret) 940 return ret; 941 942 ret = kadm5_log_get_version (server_context, &vno); 943 if (ret) 944 return ret; 945 946 ret = kadm5_log_reinit (server_context); 947 if (ret) 948 return ret; 949 950 ret = kadm5_log_set_version (server_context, vno); 951 if (ret) 952 return ret; 953 954 ret = kadm5_log_nop (server_context); 955 if (ret) 956 return ret; 957 958 ret = kadm5_log_end (server_context); 959 if (ret) 960 return ret; 961 return 0; 962 963 } 964 965 static char *default_signal = NULL; 966 static HEIMDAL_MUTEX signal_mutex = HEIMDAL_MUTEX_INITIALIZER; 967 968 const char * 969 kadm5_log_signal_socket(krb5_context context) 970 { 971 HEIMDAL_MUTEX_lock(&signal_mutex); 972 if (!default_signal) 973 asprintf(&default_signal, "%s/signal", hdb_db_dir(context)); 974 HEIMDAL_MUTEX_unlock(&signal_mutex); 975 976 return krb5_config_get_string_default(context, 977 NULL, 978 default_signal, 979 "kdc", 980 "signal_socket", 981 NULL); 982 } 983