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