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