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 "kadm5_locl.h" 35 36 RCSID("$Id: log.c,v 1.13 1999/12/04 19:50:35 assar Exp $"); 37 38 /* 39 * A log record consists of: 40 * 41 * version number 4 bytes 42 * time in seconds 4 bytes 43 * operation (enum kadm_ops) 4 bytes 44 * length of record 4 bytes 45 * data... n bytes 46 * length of record 4 bytes 47 * version number 4 bytes 48 * 49 */ 50 51 kadm5_ret_t 52 kadm5_log_get_version (int fd, 53 u_int32_t *ver) 54 { 55 int ret; 56 krb5_storage *sp; 57 int32_t old_version; 58 59 ret = lseek (fd, 0, SEEK_END); 60 if(ret < 0) 61 return errno; 62 if(ret == 0) { 63 *ver = 0; 64 return 0; 65 } 66 sp = krb5_storage_from_fd (fd); 67 sp->seek(sp, -4, SEEK_CUR); 68 krb5_ret_int32 (sp, &old_version); 69 *ver = old_version; 70 krb5_storage_free(sp); 71 lseek (fd, 0, SEEK_END); 72 return 0; 73 } 74 75 kadm5_ret_t 76 kadm5_log_init (kadm5_server_context *context) 77 { 78 int fd; 79 kadm5_ret_t ret; 80 kadm5_log_context *log_context = &context->log_context; 81 82 if (log_context->log_fd != -1) 83 return 0; 84 fd = open (log_context->log_file, O_RDWR | O_CREAT, 0600); 85 if (fd < 0) 86 return errno; 87 if (flock (fd, LOCK_EX) < 0) { 88 close (fd); 89 return errno; 90 } 91 92 ret = kadm5_log_get_version (fd, &log_context->version); 93 if (ret) 94 return ret; 95 96 log_context->log_fd = fd; 97 return 0; 98 } 99 100 kadm5_ret_t 101 kadm5_log_end (kadm5_server_context *context) 102 { 103 kadm5_log_context *log_context = &context->log_context; 104 int fd = log_context->log_fd; 105 106 flock (fd, LOCK_UN); 107 close(fd); 108 log_context->log_fd = -1; 109 return 0; 110 } 111 112 static kadm5_ret_t 113 kadm5_log_preamble (kadm5_server_context *context, 114 krb5_storage *sp, 115 enum kadm_ops op) 116 { 117 kadm5_log_context *log_context = &context->log_context; 118 kadm5_ret_t kadm_ret; 119 120 kadm_ret = kadm5_log_init (context); 121 if (kadm_ret) 122 return kadm_ret; 123 124 krb5_store_int32 (sp, ++log_context->version); 125 krb5_store_int32 (sp, time(NULL)); 126 krb5_store_int32 (sp, op); 127 return 0; 128 } 129 130 static kadm5_ret_t 131 kadm5_log_postamble (kadm5_log_context *context, 132 krb5_storage *sp) 133 { 134 krb5_store_int32 (sp, context->version); 135 return 0; 136 } 137 138 /* 139 * flush the log record in `sp'. 140 */ 141 142 static kadm5_ret_t 143 kadm5_log_flush (kadm5_log_context *log_context, 144 krb5_storage *sp) 145 { 146 krb5_data data; 147 size_t len; 148 int ret; 149 150 krb5_storage_to_data(sp, &data); 151 len = data.length; 152 ret = write (log_context->log_fd, data.data, len); 153 if (ret != len) { 154 krb5_data_free(&data); 155 return errno; 156 } 157 if (fsync (log_context->log_fd) < 0) { 158 krb5_data_free(&data); 159 return errno; 160 } 161 /* 162 * Try to send a signal to any running `ipropd-master' 163 */ 164 sendto (log_context->socket_fd, 165 (void *)&log_context->version, 166 sizeof(log_context->version), 167 0, 168 (struct sockaddr *)&log_context->socket_name, 169 sizeof(log_context->socket_name)); 170 171 krb5_data_free(&data); 172 return 0; 173 } 174 175 /* 176 * Add a `create' operation to the log. 177 */ 178 179 kadm5_ret_t 180 kadm5_log_create (kadm5_server_context *context, 181 hdb_entry *ent) 182 { 183 krb5_storage *sp; 184 kadm5_ret_t ret; 185 krb5_data value; 186 kadm5_log_context *log_context = &context->log_context; 187 188 sp = krb5_storage_emem(); 189 ret = hdb_entry2value (context->context, ent, &value); 190 if (ret) { 191 krb5_storage_free(sp); 192 return ret; 193 } 194 ret = kadm5_log_preamble (context, sp, kadm_create); 195 if (ret) { 196 krb5_data_free (&value); 197 krb5_storage_free(sp); 198 return ret; 199 } 200 krb5_store_int32 (sp, value.length); 201 sp->store(sp, value.data, value.length); 202 krb5_store_int32 (sp, value.length); 203 krb5_data_free (&value); 204 ret = kadm5_log_postamble (log_context, sp); 205 if (ret) { 206 krb5_storage_free (sp); 207 return ret; 208 } 209 ret = kadm5_log_flush (log_context, sp); 210 krb5_storage_free (sp); 211 if (ret) 212 return ret; 213 ret = kadm5_log_end (context); 214 return ret; 215 } 216 217 /* 218 * Read the data of a create log record from `sp' and change the 219 * database. 220 */ 221 222 kadm5_ret_t 223 kadm5_log_replay_create (kadm5_server_context *context, 224 u_int32_t ver, 225 u_int32_t len, 226 krb5_storage *sp) 227 { 228 krb5_error_code ret; 229 krb5_data data; 230 hdb_entry ent; 231 232 krb5_data_alloc (&data, len); 233 sp->fetch (sp, data.data, len); 234 ret = hdb_value2entry (context->context, &data, &ent); 235 krb5_data_free(&data); 236 if (ret) 237 return ret; 238 ret = context->db->store(context->context, context->db, 0, &ent); 239 hdb_free_entry (context->context, &ent); 240 return ret; 241 } 242 243 /* 244 * Add a `delete' operation to the log. 245 */ 246 247 kadm5_ret_t 248 kadm5_log_delete (kadm5_server_context *context, 249 krb5_principal princ) 250 { 251 krb5_storage *sp; 252 kadm5_ret_t ret; 253 off_t off; 254 off_t len; 255 kadm5_log_context *log_context = &context->log_context; 256 257 sp = krb5_storage_emem(); 258 ret = kadm5_log_preamble (context, sp, kadm_delete); 259 if (ret) { 260 krb5_storage_free(sp); 261 return ret; 262 } 263 krb5_store_int32 (sp, 0); 264 off = sp->seek (sp, 0, SEEK_CUR); 265 krb5_store_principal (sp, princ); 266 len = sp->seek (sp, 0, SEEK_CUR) - off; 267 sp->seek(sp, -(len + 4), SEEK_CUR); 268 krb5_store_int32 (sp, len); 269 sp->seek(sp, len, SEEK_CUR); 270 krb5_store_int32 (sp, len); 271 if (ret) { 272 krb5_storage_free (sp); 273 return ret; 274 } 275 ret = kadm5_log_postamble (log_context, sp); 276 if (ret) { 277 krb5_storage_free (sp); 278 return ret; 279 } 280 ret = kadm5_log_flush (log_context, sp); 281 krb5_storage_free (sp); 282 if (ret) 283 return ret; 284 ret = kadm5_log_end (context); 285 return ret; 286 } 287 288 /* 289 * Read a `delete' log operation from `sp' and apply it. 290 */ 291 292 kadm5_ret_t 293 kadm5_log_replay_delete (kadm5_server_context *context, 294 u_int32_t ver, 295 u_int32_t len, 296 krb5_storage *sp) 297 { 298 krb5_error_code ret; 299 hdb_entry ent; 300 301 krb5_ret_principal (sp, &ent.principal); 302 303 ret = context->db->remove(context->context, context->db, &ent); 304 krb5_free_principal (context->context, ent.principal); 305 return ret; 306 } 307 308 /* 309 * Add a `rename' operation to the log. 310 */ 311 312 kadm5_ret_t 313 kadm5_log_rename (kadm5_server_context *context, 314 krb5_principal source, 315 hdb_entry *ent) 316 { 317 krb5_storage *sp; 318 kadm5_ret_t ret; 319 off_t off; 320 off_t len; 321 krb5_data value; 322 kadm5_log_context *log_context = &context->log_context; 323 324 sp = krb5_storage_emem(); 325 ret = hdb_entry2value (context->context, ent, &value); 326 if (ret) { 327 krb5_storage_free(sp); 328 return ret; 329 } 330 ret = kadm5_log_preamble (context, sp, kadm_rename); 331 if (ret) { 332 krb5_storage_free(sp); 333 krb5_data_free (&value); 334 return ret; 335 } 336 krb5_store_int32 (sp, 0); 337 off = sp->seek (sp, 0, SEEK_CUR); 338 krb5_store_principal (sp, source); 339 sp->store(sp, value.data, value.length); 340 krb5_data_free (&value); 341 len = sp->seek (sp, 0, SEEK_CUR) - off; 342 343 sp->seek(sp, -(len + 4), SEEK_CUR); 344 krb5_store_int32 (sp, len); 345 sp->seek(sp, len, SEEK_CUR); 346 krb5_store_int32 (sp, len); 347 if (ret) { 348 krb5_storage_free (sp); 349 return ret; 350 } 351 ret = kadm5_log_postamble (log_context, sp); 352 if (ret) { 353 krb5_storage_free (sp); 354 return ret; 355 } 356 ret = kadm5_log_flush (log_context, sp); 357 krb5_storage_free (sp); 358 if (ret) 359 return ret; 360 ret = kadm5_log_end (context); 361 return ret; 362 } 363 364 /* 365 * Read a `rename' log operation from `sp' and apply it. 366 */ 367 368 kadm5_ret_t 369 kadm5_log_replay_rename (kadm5_server_context *context, 370 u_int32_t ver, 371 u_int32_t len, 372 krb5_storage *sp) 373 { 374 krb5_error_code ret; 375 krb5_principal source; 376 hdb_entry source_ent, target_ent; 377 krb5_data value; 378 off_t off; 379 size_t princ_len, data_len; 380 381 off = sp->seek(sp, 0, SEEK_CUR); 382 krb5_ret_principal (sp, &source); 383 princ_len = sp->seek(sp, 0, SEEK_CUR) - off; 384 data_len = len - princ_len; 385 krb5_data_alloc (&value, data_len); 386 sp->fetch (sp, value.data, data_len); 387 ret = hdb_value2entry (context->context, &value, &target_ent); 388 krb5_data_free(&value); 389 if (ret) { 390 krb5_free_principal (context->context, source); 391 return ret; 392 } 393 ret = context->db->store (context->context, context->db, 0, &target_ent); 394 hdb_free_entry (context->context, &target_ent); 395 if (ret) { 396 krb5_free_principal (context->context, source); 397 return ret; 398 } 399 source_ent.principal = source; 400 ret = context->db->remove (context->context, context->db, &source_ent); 401 krb5_free_principal (context->context, source); 402 return ret; 403 } 404 405 406 /* 407 * Add a `modify' operation to the log. 408 */ 409 410 kadm5_ret_t 411 kadm5_log_modify (kadm5_server_context *context, 412 hdb_entry *ent, 413 u_int32_t mask) 414 { 415 krb5_storage *sp; 416 kadm5_ret_t ret; 417 krb5_data value; 418 u_int32_t len; 419 kadm5_log_context *log_context = &context->log_context; 420 421 sp = krb5_storage_emem(); 422 ret = hdb_entry2value (context->context, ent, &value); 423 if (ret) { 424 krb5_storage_free(sp); 425 return ret; 426 } 427 ret = kadm5_log_preamble (context, sp, kadm_modify); 428 if (ret) { 429 krb5_data_free (&value); 430 krb5_storage_free(sp); 431 return ret; 432 } 433 len = value.length + 4; 434 krb5_store_int32 (sp, len); 435 krb5_store_int32 (sp, mask); 436 sp->store(sp, value.data, value.length); 437 krb5_data_free (&value); 438 krb5_store_int32 (sp, len); 439 if (ret) { 440 krb5_storage_free (sp); 441 return ret; 442 } 443 ret = kadm5_log_postamble (log_context, sp); 444 if (ret) { 445 krb5_storage_free (sp); 446 return ret; 447 } 448 ret = kadm5_log_flush (log_context, sp); 449 krb5_storage_free (sp); 450 if (ret) 451 return ret; 452 ret = kadm5_log_end (context); 453 return ret; 454 } 455 456 /* 457 * Read a `modify' log operation from `sp' and apply it. 458 */ 459 460 kadm5_ret_t 461 kadm5_log_replay_modify (kadm5_server_context *context, 462 u_int32_t ver, 463 u_int32_t len, 464 krb5_storage *sp) 465 { 466 krb5_error_code ret; 467 int32_t mask; 468 krb5_data value; 469 hdb_entry ent, log_ent; 470 471 krb5_ret_int32 (sp, &mask); 472 len -= 4; 473 krb5_data_alloc (&value, len); 474 sp->fetch (sp, value.data, len); 475 ret = hdb_value2entry (context->context, &value, &log_ent); 476 krb5_data_free(&value); 477 if (ret) 478 return ret; 479 ent.principal = log_ent.principal; 480 log_ent.principal = NULL; 481 ret = context->db->fetch(context->context, context->db, 482 HDB_F_DECRYPT, &ent); 483 if (ret) 484 return ret; 485 if (mask & KADM5_PRINC_EXPIRE_TIME) { 486 if (ent.valid_end == NULL) 487 ent.valid_end = malloc(sizeof(*ent.valid_end)); 488 *ent.valid_end = *log_ent.valid_end; 489 } 490 if (mask & KADM5_PW_EXPIRATION) { 491 if (ent.pw_end == NULL) 492 ent.pw_end = malloc(sizeof(*ent.pw_end)); 493 *ent.pw_end = *log_ent.pw_end; 494 } 495 if (mask & KADM5_LAST_PWD_CHANGE) { 496 abort (); /* XXX */ 497 } 498 if (mask & KADM5_ATTRIBUTES) { 499 ent.flags = log_ent.flags; 500 } 501 if (mask & KADM5_MAX_LIFE) { 502 if (ent.max_life == NULL) 503 ent.max_life = malloc (sizeof(*ent.max_life)); 504 *ent.max_life = *log_ent.max_life; 505 } 506 if ((mask & KADM5_MOD_TIME) && (mask & KADM5_MOD_NAME)) { 507 if (ent.modified_by == NULL) { 508 ent.modified_by = malloc(sizeof(*ent.modified_by)); 509 } else 510 free_Event(ent.modified_by); 511 copy_Event(log_ent.modified_by, ent.modified_by); 512 } 513 if (mask & KADM5_KVNO) { 514 ent.kvno = log_ent.kvno; 515 } 516 if (mask & KADM5_MKVNO) { 517 abort (); /* XXX */ 518 } 519 if (mask & KADM5_AUX_ATTRIBUTES) { 520 abort (); /* XXX */ 521 } 522 if (mask & KADM5_POLICY) { 523 abort (); /* XXX */ 524 } 525 if (mask & KADM5_POLICY_CLR) { 526 abort (); /* XXX */ 527 } 528 if (mask & KADM5_MAX_RLIFE) { 529 if (ent.max_renew == NULL) 530 ent.max_renew = malloc (sizeof(*ent.max_renew)); 531 *ent.max_renew = *log_ent.max_renew; 532 } 533 if (mask & KADM5_LAST_SUCCESS) { 534 abort (); /* XXX */ 535 } 536 if (mask & KADM5_LAST_FAILED) { 537 abort (); /* XXX */ 538 } 539 if (mask & KADM5_FAIL_AUTH_COUNT) { 540 abort (); /* XXX */ 541 } 542 if (mask & KADM5_KEY_DATA) { 543 size_t len; 544 int i; 545 546 for (i = 0; i < ent.keys.len; ++i) 547 free_Key(&ent.keys.val[i]); 548 free (ent.keys.val); 549 550 len = log_ent.keys.len; 551 552 ent.keys.len = len; 553 ent.keys.val = malloc(len * sizeof(*ent.keys.val)); 554 for (i = 0; i < ent.keys.len; ++i) 555 copy_Key(&log_ent.keys.val[i], 556 &ent.keys.val[i]); 557 } 558 ret = context->db->store(context->context, context->db, 559 HDB_F_REPLACE, &ent); 560 hdb_free_entry (context->context, &ent); 561 hdb_free_entry (context->context, &log_ent); 562 return ret; 563 } 564 565 /* 566 * Call `func' for each log record in the log in `context' 567 */ 568 569 kadm5_ret_t 570 kadm5_log_foreach (kadm5_server_context *context, 571 void (*func)(kadm5_server_context *server_context, 572 u_int32_t ver, 573 time_t timestamp, 574 enum kadm_ops op, 575 u_int32_t len, 576 krb5_storage *sp)) 577 { 578 int fd = context->log_context.log_fd; 579 krb5_storage *sp; 580 581 lseek (fd, 0, SEEK_SET); 582 sp = krb5_storage_from_fd (fd); 583 for (;;) { 584 int32_t ver, timestamp, op, len; 585 586 if(krb5_ret_int32 (sp, &ver) != 0) 587 break; 588 krb5_ret_int32 (sp, ×tamp); 589 krb5_ret_int32 (sp, &op); 590 krb5_ret_int32 (sp, &len); 591 (*func)(context, ver, timestamp, op, len, sp); 592 sp->seek(sp, 8, SEEK_CUR); 593 } 594 return 0; 595 } 596 597 /* 598 * Go to end of log. 599 */ 600 601 krb5_storage * 602 kadm5_log_goto_end (int fd) 603 { 604 krb5_storage *sp; 605 606 sp = krb5_storage_from_fd (fd); 607 sp->seek(sp, 0, SEEK_END); 608 return sp; 609 } 610 611 /* 612 * Return previous log entry. 613 */ 614 615 kadm5_ret_t 616 kadm5_log_previous (krb5_storage *sp, 617 u_int32_t *ver, 618 time_t *timestamp, 619 enum kadm_ops *op, 620 u_int32_t *len) 621 { 622 off_t off; 623 int32_t tmp; 624 625 sp->seek(sp, -8, SEEK_CUR); 626 krb5_ret_int32 (sp, &tmp); 627 *len = tmp; 628 krb5_ret_int32 (sp, &tmp); 629 *ver = tmp; 630 off = 24 + *len; 631 sp->seek(sp, -off, SEEK_CUR); 632 krb5_ret_int32 (sp, &tmp); 633 assert(tmp == *ver); 634 krb5_ret_int32 (sp, &tmp); 635 *timestamp = tmp; 636 krb5_ret_int32 (sp, &tmp); 637 *op = tmp; 638 krb5_ret_int32 (sp, &tmp); 639 assert(tmp == *len); 640 return 0; 641 } 642 643 /* 644 * Replay a record from the log 645 */ 646 647 kadm5_ret_t 648 kadm5_log_replay (kadm5_server_context *context, 649 enum kadm_ops op, 650 u_int32_t ver, 651 u_int32_t len, 652 krb5_storage *sp) 653 { 654 switch (op) { 655 case kadm_create : 656 return kadm5_log_replay_create (context, ver, len, sp); 657 case kadm_delete : 658 return kadm5_log_replay_delete (context, ver, len, sp); 659 case kadm_rename : 660 return kadm5_log_replay_rename (context, ver, len, sp); 661 case kadm_modify : 662 return kadm5_log_replay_modify (context, ver, len, sp); 663 default : 664 return KADM5_FAILURE; 665 } 666 } 667