155682Smarkm/* 2233294Sstas * Copyright (c) 1997 - 2007 Kungliga Tekniska H��gskolan 3233294Sstas * (Royal Institute of Technology, Stockholm, Sweden). 4233294Sstas * All rights reserved. 555682Smarkm * 6233294Sstas * Redistribution and use in source and binary forms, with or without 7233294Sstas * modification, are permitted provided that the following conditions 8233294Sstas * are met: 955682Smarkm * 10233294Sstas * 1. Redistributions of source code must retain the above copyright 11233294Sstas * notice, this list of conditions and the following disclaimer. 1255682Smarkm * 13233294Sstas * 2. Redistributions in binary form must reproduce the above copyright 14233294Sstas * notice, this list of conditions and the following disclaimer in the 15233294Sstas * documentation and/or other materials provided with the distribution. 1655682Smarkm * 17233294Sstas * 3. Neither the name of the Institute nor the names of its contributors 18233294Sstas * may be used to endorse or promote products derived from this software 19233294Sstas * without specific prior written permission. 2055682Smarkm * 21233294Sstas * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND 22233294Sstas * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23233294Sstas * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24233294Sstas * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE 25233294Sstas * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26233294Sstas * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27233294Sstas * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28233294Sstas * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29233294Sstas * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30233294Sstas * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31233294Sstas * SUCH DAMAGE. 3255682Smarkm */ 3355682Smarkm 3455682Smarkm#include "kadm5_locl.h" 35178825Sdfr#include "heim_threads.h" 3655682Smarkm 37233294SstasRCSID("$Id$"); 3855682Smarkm 3955682Smarkm/* 4055682Smarkm * A log record consists of: 4155682Smarkm * 4255682Smarkm * version number 4 bytes 4355682Smarkm * time in seconds 4 bytes 4455682Smarkm * operation (enum kadm_ops) 4 bytes 4555682Smarkm * length of record 4 bytes 4655682Smarkm * data... n bytes 4755682Smarkm * length of record 4 bytes 4855682Smarkm * version number 4 bytes 4955682Smarkm * 5055682Smarkm */ 5155682Smarkm 5255682Smarkmkadm5_ret_t 5372445Sassarkadm5_log_get_version_fd (int fd, 54178825Sdfr uint32_t *ver) 5555682Smarkm{ 5655682Smarkm int ret; 5755682Smarkm krb5_storage *sp; 5855682Smarkm int32_t old_version; 5955682Smarkm 6055682Smarkm ret = lseek (fd, 0, SEEK_END); 6155682Smarkm if(ret < 0) 6255682Smarkm return errno; 6355682Smarkm if(ret == 0) { 6455682Smarkm *ver = 0; 6555682Smarkm return 0; 6655682Smarkm } 6755682Smarkm sp = krb5_storage_from_fd (fd); 68102644Snectar krb5_storage_seek(sp, -4, SEEK_CUR); 6955682Smarkm krb5_ret_int32 (sp, &old_version); 7055682Smarkm *ver = old_version; 7155682Smarkm krb5_storage_free(sp); 7255682Smarkm lseek (fd, 0, SEEK_END); 7355682Smarkm return 0; 7455682Smarkm} 7555682Smarkm 7655682Smarkmkadm5_ret_t 77178825Sdfrkadm5_log_get_version (kadm5_server_context *context, uint32_t *ver) 7872445Sassar{ 7972445Sassar return kadm5_log_get_version_fd (context->log_context.log_fd, ver); 8072445Sassar} 8172445Sassar 8272445Sassarkadm5_ret_t 83178825Sdfrkadm5_log_set_version (kadm5_server_context *context, uint32_t vno) 8472445Sassar{ 8572445Sassar kadm5_log_context *log_context = &context->log_context; 8672445Sassar 8772445Sassar log_context->version = vno; 8872445Sassar return 0; 8972445Sassar} 9072445Sassar 9172445Sassarkadm5_ret_t 9255682Smarkmkadm5_log_init (kadm5_server_context *context) 9355682Smarkm{ 9455682Smarkm int fd; 9555682Smarkm kadm5_ret_t ret; 9655682Smarkm kadm5_log_context *log_context = &context->log_context; 9755682Smarkm 9855682Smarkm if (log_context->log_fd != -1) 9955682Smarkm return 0; 10055682Smarkm fd = open (log_context->log_file, O_RDWR | O_CREAT, 0600); 101178825Sdfr if (fd < 0) { 102233294Sstas ret = errno; 103233294Sstas krb5_set_error_message(context->context, ret, "kadm5_log_init: open %s", 104178825Sdfr log_context->log_file); 105233294Sstas return ret; 106178825Sdfr } 10755682Smarkm if (flock (fd, LOCK_EX) < 0) { 108233294Sstas ret = errno; 109233294Sstas krb5_set_error_message(context->context, ret, "kadm5_log_init: flock %s", 110233294Sstas log_context->log_file); 11155682Smarkm close (fd); 11255682Smarkm return errno; 11355682Smarkm } 11455682Smarkm 11572445Sassar ret = kadm5_log_get_version_fd (fd, &log_context->version); 11655682Smarkm if (ret) 11755682Smarkm return ret; 11855682Smarkm 11955682Smarkm log_context->log_fd = fd; 12055682Smarkm return 0; 12155682Smarkm} 12255682Smarkm 12355682Smarkmkadm5_ret_t 12472445Sassarkadm5_log_reinit (kadm5_server_context *context) 12572445Sassar{ 12672445Sassar int fd; 12772445Sassar kadm5_log_context *log_context = &context->log_context; 12872445Sassar 12972445Sassar if (log_context->log_fd != -1) { 130178825Sdfr flock (log_context->log_fd, LOCK_UN); 13172445Sassar close (log_context->log_fd); 13272445Sassar log_context->log_fd = -1; 13372445Sassar } 13472445Sassar fd = open (log_context->log_file, O_RDWR | O_CREAT | O_TRUNC, 0600); 13572445Sassar if (fd < 0) 13672445Sassar return errno; 13772445Sassar if (flock (fd, LOCK_EX) < 0) { 13872445Sassar close (fd); 13972445Sassar return errno; 14072445Sassar } 14172445Sassar 14272445Sassar log_context->version = 0; 14372445Sassar log_context->log_fd = fd; 14472445Sassar return 0; 14572445Sassar} 14672445Sassar 14772445Sassar 14872445Sassarkadm5_ret_t 14955682Smarkmkadm5_log_end (kadm5_server_context *context) 15055682Smarkm{ 15155682Smarkm kadm5_log_context *log_context = &context->log_context; 15255682Smarkm int fd = log_context->log_fd; 15355682Smarkm 15455682Smarkm flock (fd, LOCK_UN); 15555682Smarkm close(fd); 15655682Smarkm log_context->log_fd = -1; 15755682Smarkm return 0; 15855682Smarkm} 15955682Smarkm 16055682Smarkmstatic kadm5_ret_t 16155682Smarkmkadm5_log_preamble (kadm5_server_context *context, 16255682Smarkm krb5_storage *sp, 16355682Smarkm enum kadm_ops op) 16455682Smarkm{ 16555682Smarkm kadm5_log_context *log_context = &context->log_context; 16655682Smarkm kadm5_ret_t kadm_ret; 16755682Smarkm 16855682Smarkm kadm_ret = kadm5_log_init (context); 16955682Smarkm if (kadm_ret) 17055682Smarkm return kadm_ret; 17155682Smarkm 17255682Smarkm krb5_store_int32 (sp, ++log_context->version); 17355682Smarkm krb5_store_int32 (sp, time(NULL)); 17455682Smarkm krb5_store_int32 (sp, op); 17555682Smarkm return 0; 17655682Smarkm} 17755682Smarkm 17855682Smarkmstatic kadm5_ret_t 17955682Smarkmkadm5_log_postamble (kadm5_log_context *context, 18055682Smarkm krb5_storage *sp) 18155682Smarkm{ 18255682Smarkm krb5_store_int32 (sp, context->version); 18355682Smarkm return 0; 18455682Smarkm} 18555682Smarkm 18655682Smarkm/* 18755682Smarkm * flush the log record in `sp'. 18855682Smarkm */ 18955682Smarkm 19055682Smarkmstatic kadm5_ret_t 19155682Smarkmkadm5_log_flush (kadm5_log_context *log_context, 19255682Smarkm krb5_storage *sp) 19355682Smarkm{ 19455682Smarkm krb5_data data; 19555682Smarkm size_t len; 196233294Sstas ssize_t ret; 19755682Smarkm 19855682Smarkm krb5_storage_to_data(sp, &data); 19955682Smarkm len = data.length; 20055682Smarkm ret = write (log_context->log_fd, data.data, len); 201233294Sstas if (ret < 0 || (size_t)ret != len) { 20255682Smarkm krb5_data_free(&data); 20355682Smarkm return errno; 20455682Smarkm } 20555682Smarkm if (fsync (log_context->log_fd) < 0) { 20655682Smarkm krb5_data_free(&data); 20755682Smarkm return errno; 20855682Smarkm } 209233294Sstas 21055682Smarkm /* 21155682Smarkm * Try to send a signal to any running `ipropd-master' 21255682Smarkm */ 213233294Sstas#ifndef NO_UNIX_SOCKETS 21455682Smarkm sendto (log_context->socket_fd, 21555682Smarkm (void *)&log_context->version, 21655682Smarkm sizeof(log_context->version), 21755682Smarkm 0, 21855682Smarkm (struct sockaddr *)&log_context->socket_name, 21955682Smarkm sizeof(log_context->socket_name)); 220233294Sstas#else 221233294Sstas sendto (log_context->socket_fd, 222233294Sstas (void *)&log_context->version, 223233294Sstas sizeof(log_context->version), 224233294Sstas 0, 225233294Sstas log_context->socket_info->ai_addr, 226233294Sstas log_context->socket_info->ai_addrlen); 227233294Sstas#endif 22855682Smarkm 22955682Smarkm krb5_data_free(&data); 23055682Smarkm return 0; 23155682Smarkm} 23255682Smarkm 23355682Smarkm/* 23455682Smarkm * Add a `create' operation to the log. 23555682Smarkm */ 23655682Smarkm 23755682Smarkmkadm5_ret_t 23855682Smarkmkadm5_log_create (kadm5_server_context *context, 23955682Smarkm hdb_entry *ent) 24055682Smarkm{ 24155682Smarkm krb5_storage *sp; 24255682Smarkm kadm5_ret_t ret; 24355682Smarkm krb5_data value; 24455682Smarkm kadm5_log_context *log_context = &context->log_context; 24555682Smarkm 24655682Smarkm sp = krb5_storage_emem(); 24755682Smarkm ret = hdb_entry2value (context->context, ent, &value); 24855682Smarkm if (ret) { 24955682Smarkm krb5_storage_free(sp); 25055682Smarkm return ret; 25155682Smarkm } 25255682Smarkm ret = kadm5_log_preamble (context, sp, kadm_create); 25355682Smarkm if (ret) { 25455682Smarkm krb5_data_free (&value); 25555682Smarkm krb5_storage_free(sp); 25655682Smarkm return ret; 25755682Smarkm } 25855682Smarkm krb5_store_int32 (sp, value.length); 259102644Snectar krb5_storage_write(sp, value.data, value.length); 26055682Smarkm krb5_store_int32 (sp, value.length); 26155682Smarkm krb5_data_free (&value); 26255682Smarkm ret = kadm5_log_postamble (log_context, sp); 26355682Smarkm if (ret) { 26455682Smarkm krb5_storage_free (sp); 26555682Smarkm return ret; 26655682Smarkm } 26755682Smarkm ret = kadm5_log_flush (log_context, sp); 26855682Smarkm krb5_storage_free (sp); 26955682Smarkm if (ret) 27055682Smarkm return ret; 27155682Smarkm ret = kadm5_log_end (context); 27255682Smarkm return ret; 27355682Smarkm} 27455682Smarkm 27555682Smarkm/* 27655682Smarkm * Read the data of a create log record from `sp' and change the 27755682Smarkm * database. 27855682Smarkm */ 27955682Smarkm 280178825Sdfrstatic kadm5_ret_t 28155682Smarkmkadm5_log_replay_create (kadm5_server_context *context, 282178825Sdfr uint32_t ver, 283178825Sdfr uint32_t len, 28455682Smarkm krb5_storage *sp) 28555682Smarkm{ 28655682Smarkm krb5_error_code ret; 28755682Smarkm krb5_data data; 288178825Sdfr hdb_entry_ex ent; 28955682Smarkm 290178825Sdfr memset(&ent, 0, sizeof(ent)); 291178825Sdfr 292120945Snectar ret = krb5_data_alloc (&data, len); 293178825Sdfr if (ret) { 294233294Sstas krb5_set_error_message(context->context, ret, "out of memory"); 295120945Snectar return ret; 296178825Sdfr } 297102644Snectar krb5_storage_read (sp, data.data, len); 298178825Sdfr ret = hdb_value2entry (context->context, &data, &ent.entry); 29955682Smarkm krb5_data_free(&data); 300178825Sdfr if (ret) { 301233294Sstas krb5_set_error_message(context->context, ret, 302233294Sstas "Unmarshaling hdb entry failed"); 30355682Smarkm return ret; 304178825Sdfr } 305178825Sdfr ret = context->db->hdb_store(context->context, context->db, 0, &ent); 30655682Smarkm hdb_free_entry (context->context, &ent); 30755682Smarkm return ret; 30855682Smarkm} 30955682Smarkm 31055682Smarkm/* 31155682Smarkm * Add a `delete' operation to the log. 31255682Smarkm */ 31355682Smarkm 31455682Smarkmkadm5_ret_t 31555682Smarkmkadm5_log_delete (kadm5_server_context *context, 31655682Smarkm krb5_principal princ) 31755682Smarkm{ 31855682Smarkm krb5_storage *sp; 31955682Smarkm kadm5_ret_t ret; 32055682Smarkm off_t off; 32155682Smarkm off_t len; 32255682Smarkm kadm5_log_context *log_context = &context->log_context; 32355682Smarkm 32455682Smarkm sp = krb5_storage_emem(); 325178825Sdfr if (sp == NULL) 326178825Sdfr return ENOMEM; 32755682Smarkm ret = kadm5_log_preamble (context, sp, kadm_delete); 328178825Sdfr if (ret) 329178825Sdfr goto out; 330178825Sdfr ret = krb5_store_int32 (sp, 0); 331178825Sdfr if (ret) 332178825Sdfr goto out; 333102644Snectar off = krb5_storage_seek (sp, 0, SEEK_CUR); 334178825Sdfr ret = krb5_store_principal (sp, princ); 335178825Sdfr if (ret) 336178825Sdfr goto out; 337102644Snectar len = krb5_storage_seek (sp, 0, SEEK_CUR) - off; 338102644Snectar krb5_storage_seek(sp, -(len + 4), SEEK_CUR); 339178825Sdfr ret = krb5_store_int32 (sp, len); 340178825Sdfr if (ret) 341178825Sdfr goto out; 342102644Snectar krb5_storage_seek(sp, len, SEEK_CUR); 343178825Sdfr ret = krb5_store_int32 (sp, len); 344178825Sdfr if (ret) 345178825Sdfr goto out; 34655682Smarkm ret = kadm5_log_postamble (log_context, sp); 347178825Sdfr if (ret) 348178825Sdfr goto out; 34955682Smarkm ret = kadm5_log_flush (log_context, sp); 35055682Smarkm if (ret) 351178825Sdfr goto out; 35255682Smarkm ret = kadm5_log_end (context); 353178825Sdfrout: 354178825Sdfr krb5_storage_free (sp); 35555682Smarkm return ret; 35655682Smarkm} 35755682Smarkm 35855682Smarkm/* 35955682Smarkm * Read a `delete' log operation from `sp' and apply it. 36055682Smarkm */ 36155682Smarkm 362178825Sdfrstatic kadm5_ret_t 36355682Smarkmkadm5_log_replay_delete (kadm5_server_context *context, 364178825Sdfr uint32_t ver, 365178825Sdfr uint32_t len, 36655682Smarkm krb5_storage *sp) 36755682Smarkm{ 36855682Smarkm krb5_error_code ret; 369178825Sdfr krb5_principal principal; 37055682Smarkm 371178825Sdfr ret = krb5_ret_principal (sp, &principal); 372178825Sdfr if (ret) { 373233294Sstas krb5_set_error_message(context->context, ret, "Failed to read deleted " 374233294Sstas "principal from log version: %ld", (long)ver); 375178825Sdfr return ret; 376178825Sdfr } 37755682Smarkm 378178825Sdfr ret = context->db->hdb_remove(context->context, context->db, principal); 379178825Sdfr krb5_free_principal (context->context, principal); 38055682Smarkm return ret; 38155682Smarkm} 38255682Smarkm 38355682Smarkm/* 38455682Smarkm * Add a `rename' operation to the log. 38555682Smarkm */ 38655682Smarkm 38755682Smarkmkadm5_ret_t 38855682Smarkmkadm5_log_rename (kadm5_server_context *context, 38955682Smarkm krb5_principal source, 39055682Smarkm hdb_entry *ent) 39155682Smarkm{ 39255682Smarkm krb5_storage *sp; 39355682Smarkm kadm5_ret_t ret; 39455682Smarkm off_t off; 39555682Smarkm off_t len; 39655682Smarkm krb5_data value; 39755682Smarkm kadm5_log_context *log_context = &context->log_context; 39855682Smarkm 399178825Sdfr krb5_data_zero(&value); 400178825Sdfr 40155682Smarkm sp = krb5_storage_emem(); 40255682Smarkm ret = hdb_entry2value (context->context, ent, &value); 403178825Sdfr if (ret) 404178825Sdfr goto failed; 405178825Sdfr 40655682Smarkm ret = kadm5_log_preamble (context, sp, kadm_rename); 407178825Sdfr if (ret) 408178825Sdfr goto failed; 409178825Sdfr 410178825Sdfr ret = krb5_store_int32 (sp, 0); 411178825Sdfr if (ret) 412178825Sdfr goto failed; 413102644Snectar off = krb5_storage_seek (sp, 0, SEEK_CUR); 414178825Sdfr ret = krb5_store_principal (sp, source); 415178825Sdfr if (ret) 416178825Sdfr goto failed; 417178825Sdfr 418102644Snectar krb5_storage_write(sp, value.data, value.length); 419102644Snectar len = krb5_storage_seek (sp, 0, SEEK_CUR) - off; 42055682Smarkm 421102644Snectar krb5_storage_seek(sp, -(len + 4), SEEK_CUR); 422178825Sdfr ret = krb5_store_int32 (sp, len); 423178825Sdfr if (ret) 424178825Sdfr goto failed; 425178825Sdfr 426102644Snectar krb5_storage_seek(sp, len, SEEK_CUR); 427178825Sdfr ret = krb5_store_int32 (sp, len); 428178825Sdfr if (ret) 429178825Sdfr goto failed; 430178825Sdfr 43155682Smarkm ret = kadm5_log_postamble (log_context, sp); 432178825Sdfr if (ret) 433178825Sdfr goto failed; 434178825Sdfr 43555682Smarkm ret = kadm5_log_flush (log_context, sp); 436178825Sdfr if (ret) 437178825Sdfr goto failed; 43855682Smarkm krb5_storage_free (sp); 439178825Sdfr krb5_data_free (&value); 440178825Sdfr 441178825Sdfr return kadm5_log_end (context); 442178825Sdfr 443178825Sdfrfailed: 444178825Sdfr krb5_data_free(&value); 445178825Sdfr krb5_storage_free(sp); 44655682Smarkm return ret; 44755682Smarkm} 44855682Smarkm 44955682Smarkm/* 45055682Smarkm * Read a `rename' log operation from `sp' and apply it. 45155682Smarkm */ 45255682Smarkm 453178825Sdfrstatic kadm5_ret_t 45455682Smarkmkadm5_log_replay_rename (kadm5_server_context *context, 455178825Sdfr uint32_t ver, 456178825Sdfr uint32_t len, 45755682Smarkm krb5_storage *sp) 45855682Smarkm{ 45955682Smarkm krb5_error_code ret; 46055682Smarkm krb5_principal source; 461178825Sdfr hdb_entry_ex target_ent; 46255682Smarkm krb5_data value; 46355682Smarkm off_t off; 46455682Smarkm size_t princ_len, data_len; 46555682Smarkm 466178825Sdfr memset(&target_ent, 0, sizeof(target_ent)); 467178825Sdfr 468102644Snectar off = krb5_storage_seek(sp, 0, SEEK_CUR); 469178825Sdfr ret = krb5_ret_principal (sp, &source); 470178825Sdfr if (ret) { 471233294Sstas krb5_set_error_message(context->context, ret, "Failed to read renamed " 472233294Sstas "principal in log, version: %ld", (long)ver); 473178825Sdfr return ret; 474178825Sdfr } 475102644Snectar princ_len = krb5_storage_seek(sp, 0, SEEK_CUR) - off; 47655682Smarkm data_len = len - princ_len; 477120945Snectar ret = krb5_data_alloc (&value, data_len); 478120945Snectar if (ret) { 479120945Snectar krb5_free_principal (context->context, source); 480120945Snectar return ret; 481120945Snectar } 482102644Snectar krb5_storage_read (sp, value.data, data_len); 483178825Sdfr ret = hdb_value2entry (context->context, &value, &target_ent.entry); 48455682Smarkm krb5_data_free(&value); 48555682Smarkm if (ret) { 48655682Smarkm krb5_free_principal (context->context, source); 48755682Smarkm return ret; 48855682Smarkm } 489233294Sstas ret = context->db->hdb_store (context->context, context->db, 490178825Sdfr 0, &target_ent); 49155682Smarkm hdb_free_entry (context->context, &target_ent); 49255682Smarkm if (ret) { 49355682Smarkm krb5_free_principal (context->context, source); 49455682Smarkm return ret; 49555682Smarkm } 496178825Sdfr ret = context->db->hdb_remove (context->context, context->db, source); 49755682Smarkm krb5_free_principal (context->context, source); 49855682Smarkm return ret; 49955682Smarkm} 50055682Smarkm 50155682Smarkm 50255682Smarkm/* 50355682Smarkm * Add a `modify' operation to the log. 50455682Smarkm */ 50555682Smarkm 50655682Smarkmkadm5_ret_t 50755682Smarkmkadm5_log_modify (kadm5_server_context *context, 50855682Smarkm hdb_entry *ent, 509178825Sdfr uint32_t mask) 51055682Smarkm{ 51155682Smarkm krb5_storage *sp; 51255682Smarkm kadm5_ret_t ret; 51355682Smarkm krb5_data value; 514178825Sdfr uint32_t len; 51555682Smarkm kadm5_log_context *log_context = &context->log_context; 51655682Smarkm 517178825Sdfr krb5_data_zero(&value); 518178825Sdfr 51955682Smarkm sp = krb5_storage_emem(); 52055682Smarkm ret = hdb_entry2value (context->context, ent, &value); 521178825Sdfr if (ret) 522178825Sdfr goto failed; 523178825Sdfr 52455682Smarkm ret = kadm5_log_preamble (context, sp, kadm_modify); 525178825Sdfr if (ret) 526178825Sdfr goto failed; 527178825Sdfr 52855682Smarkm len = value.length + 4; 529178825Sdfr ret = krb5_store_int32 (sp, len); 530178825Sdfr if (ret) 531178825Sdfr goto failed; 532178825Sdfr ret = krb5_store_int32 (sp, mask); 533178825Sdfr if (ret) 534178825Sdfr goto failed; 535102644Snectar krb5_storage_write (sp, value.data, value.length); 536178825Sdfr 537178825Sdfr ret = krb5_store_int32 (sp, len); 538178825Sdfr if (ret) 539178825Sdfr goto failed; 54055682Smarkm ret = kadm5_log_postamble (log_context, sp); 541178825Sdfr if (ret) 542178825Sdfr goto failed; 54355682Smarkm ret = kadm5_log_flush (log_context, sp); 544178825Sdfr if (ret) 545178825Sdfr goto failed; 546178825Sdfr krb5_data_free(&value); 54755682Smarkm krb5_storage_free (sp); 548178825Sdfr return kadm5_log_end (context); 549178825Sdfrfailed: 550178825Sdfr krb5_data_free(&value); 551178825Sdfr krb5_storage_free(sp); 55255682Smarkm return ret; 55355682Smarkm} 55455682Smarkm 55555682Smarkm/* 55655682Smarkm * Read a `modify' log operation from `sp' and apply it. 55755682Smarkm */ 55855682Smarkm 559178825Sdfrstatic kadm5_ret_t 56055682Smarkmkadm5_log_replay_modify (kadm5_server_context *context, 561178825Sdfr uint32_t ver, 562178825Sdfr uint32_t len, 56355682Smarkm krb5_storage *sp) 56455682Smarkm{ 56555682Smarkm krb5_error_code ret; 56655682Smarkm int32_t mask; 56755682Smarkm krb5_data value; 568178825Sdfr hdb_entry_ex ent, log_ent; 56955682Smarkm 570178825Sdfr memset(&log_ent, 0, sizeof(log_ent)); 571178825Sdfr 57255682Smarkm krb5_ret_int32 (sp, &mask); 57355682Smarkm len -= 4; 574120945Snectar ret = krb5_data_alloc (&value, len); 575178825Sdfr if (ret) { 576233294Sstas krb5_set_error_message(context->context, ret, "out of memory"); 577120945Snectar return ret; 578178825Sdfr } 579102644Snectar krb5_storage_read (sp, value.data, len); 580178825Sdfr ret = hdb_value2entry (context->context, &value, &log_ent.entry); 58155682Smarkm krb5_data_free(&value); 58255682Smarkm if (ret) 58355682Smarkm return ret; 584178825Sdfr 585178825Sdfr memset(&ent, 0, sizeof(ent)); 586233294Sstas ret = context->db->hdb_fetch_kvno(context->context, context->db, 587233294Sstas log_ent.entry.principal, 588233294Sstas HDB_F_DECRYPT|HDB_F_GET_ANY|HDB_F_ADMIN_DATA, 0, &ent); 58955682Smarkm if (ret) 590178825Sdfr goto out; 59155682Smarkm if (mask & KADM5_PRINC_EXPIRE_TIME) { 592178825Sdfr if (log_ent.entry.valid_end == NULL) { 593178825Sdfr ent.entry.valid_end = NULL; 59472445Sassar } else { 595178825Sdfr if (ent.entry.valid_end == NULL) { 596178825Sdfr ent.entry.valid_end = malloc(sizeof(*ent.entry.valid_end)); 597178825Sdfr if (ent.entry.valid_end == NULL) { 598178825Sdfr ret = ENOMEM; 599233294Sstas krb5_set_error_message(context->context, ret, "out of memory"); 600178825Sdfr goto out; 601178825Sdfr } 602178825Sdfr } 603178825Sdfr *ent.entry.valid_end = *log_ent.entry.valid_end; 60472445Sassar } 60555682Smarkm } 60655682Smarkm if (mask & KADM5_PW_EXPIRATION) { 607178825Sdfr if (log_ent.entry.pw_end == NULL) { 608178825Sdfr ent.entry.pw_end = NULL; 60972445Sassar } else { 610178825Sdfr if (ent.entry.pw_end == NULL) { 611178825Sdfr ent.entry.pw_end = malloc(sizeof(*ent.entry.pw_end)); 612178825Sdfr if (ent.entry.pw_end == NULL) { 613178825Sdfr ret = ENOMEM; 614233294Sstas krb5_set_error_message(context->context, ret, "out of memory"); 615178825Sdfr goto out; 616178825Sdfr } 617178825Sdfr } 618178825Sdfr *ent.entry.pw_end = *log_ent.entry.pw_end; 61972445Sassar } 62055682Smarkm } 62155682Smarkm if (mask & KADM5_LAST_PWD_CHANGE) { 62255682Smarkm abort (); /* XXX */ 62355682Smarkm } 62455682Smarkm if (mask & KADM5_ATTRIBUTES) { 625178825Sdfr ent.entry.flags = log_ent.entry.flags; 62655682Smarkm } 62755682Smarkm if (mask & KADM5_MAX_LIFE) { 628178825Sdfr if (log_ent.entry.max_life == NULL) { 629178825Sdfr ent.entry.max_life = NULL; 63072445Sassar } else { 631178825Sdfr if (ent.entry.max_life == NULL) { 632178825Sdfr ent.entry.max_life = malloc (sizeof(*ent.entry.max_life)); 633178825Sdfr if (ent.entry.max_life == NULL) { 634178825Sdfr ret = ENOMEM; 635233294Sstas krb5_set_error_message(context->context, ret, "out of memory"); 636178825Sdfr goto out; 637178825Sdfr } 638178825Sdfr } 639178825Sdfr *ent.entry.max_life = *log_ent.entry.max_life; 64072445Sassar } 64155682Smarkm } 64255682Smarkm if ((mask & KADM5_MOD_TIME) && (mask & KADM5_MOD_NAME)) { 643178825Sdfr if (ent.entry.modified_by == NULL) { 644178825Sdfr ent.entry.modified_by = malloc(sizeof(*ent.entry.modified_by)); 645178825Sdfr if (ent.entry.modified_by == NULL) { 646178825Sdfr ret = ENOMEM; 647233294Sstas krb5_set_error_message(context->context, ret, "out of memory"); 648178825Sdfr goto out; 649178825Sdfr } 65055682Smarkm } else 651178825Sdfr free_Event(ent.entry.modified_by); 652178825Sdfr ret = copy_Event(log_ent.entry.modified_by, ent.entry.modified_by); 653178825Sdfr if (ret) { 654233294Sstas krb5_set_error_message(context->context, ret, "out of memory"); 655178825Sdfr goto out; 656178825Sdfr } 65755682Smarkm } 65855682Smarkm if (mask & KADM5_KVNO) { 659178825Sdfr ent.entry.kvno = log_ent.entry.kvno; 66055682Smarkm } 66155682Smarkm if (mask & KADM5_MKVNO) { 66255682Smarkm abort (); /* XXX */ 66355682Smarkm } 66455682Smarkm if (mask & KADM5_AUX_ATTRIBUTES) { 66555682Smarkm abort (); /* XXX */ 66655682Smarkm } 66755682Smarkm if (mask & KADM5_POLICY) { 66855682Smarkm abort (); /* XXX */ 66955682Smarkm } 67055682Smarkm if (mask & KADM5_POLICY_CLR) { 67155682Smarkm abort (); /* XXX */ 67255682Smarkm } 67355682Smarkm if (mask & KADM5_MAX_RLIFE) { 674178825Sdfr if (log_ent.entry.max_renew == NULL) { 675178825Sdfr ent.entry.max_renew = NULL; 67672445Sassar } else { 677178825Sdfr if (ent.entry.max_renew == NULL) { 678178825Sdfr ent.entry.max_renew = malloc (sizeof(*ent.entry.max_renew)); 679178825Sdfr if (ent.entry.max_renew == NULL) { 680178825Sdfr ret = ENOMEM; 681233294Sstas krb5_set_error_message(context->context, ret, "out of memory"); 682178825Sdfr goto out; 683178825Sdfr } 684178825Sdfr } 685178825Sdfr *ent.entry.max_renew = *log_ent.entry.max_renew; 68672445Sassar } 68755682Smarkm } 68855682Smarkm if (mask & KADM5_LAST_SUCCESS) { 68955682Smarkm abort (); /* XXX */ 69055682Smarkm } 69155682Smarkm if (mask & KADM5_LAST_FAILED) { 69255682Smarkm abort (); /* XXX */ 69355682Smarkm } 69455682Smarkm if (mask & KADM5_FAIL_AUTH_COUNT) { 69555682Smarkm abort (); /* XXX */ 69655682Smarkm } 69755682Smarkm if (mask & KADM5_KEY_DATA) { 698178825Sdfr size_t num; 699233294Sstas size_t i; 70055682Smarkm 701178825Sdfr for (i = 0; i < ent.entry.keys.len; ++i) 702178825Sdfr free_Key(&ent.entry.keys.val[i]); 703178825Sdfr free (ent.entry.keys.val); 70455682Smarkm 705178825Sdfr num = log_ent.entry.keys.len; 70655682Smarkm 707178825Sdfr ent.entry.keys.len = num; 708178825Sdfr ent.entry.keys.val = malloc(len * sizeof(*ent.entry.keys.val)); 709178825Sdfr if (ent.entry.keys.val == NULL) { 710233294Sstas krb5_set_error_message(context->context, ENOMEM, "out of memory"); 711178825Sdfr return ENOMEM; 712178825Sdfr } 713178825Sdfr for (i = 0; i < ent.entry.keys.len; ++i) { 714178825Sdfr ret = copy_Key(&log_ent.entry.keys.val[i], 715178825Sdfr &ent.entry.keys.val[i]); 716178825Sdfr if (ret) { 717233294Sstas krb5_set_error_message(context->context, ret, "out of memory"); 718178825Sdfr goto out; 719178825Sdfr } 720178825Sdfr } 72155682Smarkm } 722178825Sdfr if ((mask & KADM5_TL_DATA) && log_ent.entry.extensions) { 723178825Sdfr HDB_extensions *es = ent.entry.extensions; 724178825Sdfr 725178825Sdfr ent.entry.extensions = calloc(1, sizeof(*ent.entry.extensions)); 726178825Sdfr if (ent.entry.extensions == NULL) 727178825Sdfr goto out; 728178825Sdfr 729178825Sdfr ret = copy_HDB_extensions(log_ent.entry.extensions, 730178825Sdfr ent.entry.extensions); 731178825Sdfr if (ret) { 732233294Sstas krb5_set_error_message(context->context, ret, "out of memory"); 733178825Sdfr free(ent.entry.extensions); 734178825Sdfr ent.entry.extensions = es; 735178825Sdfr goto out; 736178825Sdfr } 737178825Sdfr if (es) { 738178825Sdfr free_HDB_extensions(es); 739178825Sdfr free(es); 740178825Sdfr } 741178825Sdfr } 742233294Sstas ret = context->db->hdb_store(context->context, context->db, 743178825Sdfr HDB_F_REPLACE, &ent); 744178825Sdfr out: 74555682Smarkm hdb_free_entry (context->context, &ent); 74655682Smarkm hdb_free_entry (context->context, &log_ent); 74755682Smarkm return ret; 74855682Smarkm} 74955682Smarkm 75055682Smarkm/* 751178825Sdfr * Add a `nop' operation to the log. Does not close the log. 75272445Sassar */ 75372445Sassar 75472445Sassarkadm5_ret_t 75572445Sassarkadm5_log_nop (kadm5_server_context *context) 75672445Sassar{ 75772445Sassar krb5_storage *sp; 75872445Sassar kadm5_ret_t ret; 75972445Sassar kadm5_log_context *log_context = &context->log_context; 76072445Sassar 76172445Sassar sp = krb5_storage_emem(); 76272445Sassar ret = kadm5_log_preamble (context, sp, kadm_nop); 76372445Sassar if (ret) { 76472445Sassar krb5_storage_free (sp); 76572445Sassar return ret; 76672445Sassar } 76772445Sassar krb5_store_int32 (sp, 0); 76872445Sassar krb5_store_int32 (sp, 0); 76972445Sassar ret = kadm5_log_postamble (log_context, sp); 77072445Sassar if (ret) { 77172445Sassar krb5_storage_free (sp); 77272445Sassar return ret; 77372445Sassar } 77472445Sassar ret = kadm5_log_flush (log_context, sp); 77572445Sassar krb5_storage_free (sp); 776178825Sdfr 77772445Sassar return ret; 77872445Sassar} 77972445Sassar 78072445Sassar/* 78172445Sassar * Read a `nop' log operation from `sp' and apply it. 78272445Sassar */ 78372445Sassar 784178825Sdfrstatic kadm5_ret_t 78572445Sassarkadm5_log_replay_nop (kadm5_server_context *context, 786178825Sdfr uint32_t ver, 787178825Sdfr uint32_t len, 78872445Sassar krb5_storage *sp) 78972445Sassar{ 79072445Sassar return 0; 79172445Sassar} 79272445Sassar 79372445Sassar/* 79455682Smarkm * Call `func' for each log record in the log in `context' 79555682Smarkm */ 79655682Smarkm 79755682Smarkmkadm5_ret_t 79855682Smarkmkadm5_log_foreach (kadm5_server_context *context, 79955682Smarkm void (*func)(kadm5_server_context *server_context, 800178825Sdfr uint32_t ver, 80155682Smarkm time_t timestamp, 80255682Smarkm enum kadm_ops op, 803178825Sdfr uint32_t len, 804178825Sdfr krb5_storage *, 805178825Sdfr void *), 806178825Sdfr void *ctx) 80755682Smarkm{ 80855682Smarkm int fd = context->log_context.log_fd; 80955682Smarkm krb5_storage *sp; 81055682Smarkm 81155682Smarkm lseek (fd, 0, SEEK_SET); 81255682Smarkm sp = krb5_storage_from_fd (fd); 81355682Smarkm for (;;) { 814178825Sdfr int32_t ver, timestamp, op, len, len2, ver2; 81555682Smarkm 81655682Smarkm if(krb5_ret_int32 (sp, &ver) != 0) 81755682Smarkm break; 81855682Smarkm krb5_ret_int32 (sp, ×tamp); 81955682Smarkm krb5_ret_int32 (sp, &op); 82055682Smarkm krb5_ret_int32 (sp, &len); 821178825Sdfr (*func)(context, ver, timestamp, op, len, sp, ctx); 822178825Sdfr krb5_ret_int32 (sp, &len2); 823178825Sdfr krb5_ret_int32 (sp, &ver2); 824178825Sdfr if (len != len2) 825178825Sdfr abort(); 826178825Sdfr if (ver != ver2) 827178825Sdfr abort(); 82855682Smarkm } 829178825Sdfr krb5_storage_free(sp); 83055682Smarkm return 0; 83155682Smarkm} 83255682Smarkm 83355682Smarkm/* 83455682Smarkm * Go to end of log. 83555682Smarkm */ 83655682Smarkm 83755682Smarkmkrb5_storage * 83855682Smarkmkadm5_log_goto_end (int fd) 83955682Smarkm{ 84055682Smarkm krb5_storage *sp; 84155682Smarkm 84255682Smarkm sp = krb5_storage_from_fd (fd); 843102644Snectar krb5_storage_seek(sp, 0, SEEK_END); 84455682Smarkm return sp; 84555682Smarkm} 84655682Smarkm 84755682Smarkm/* 84855682Smarkm * Return previous log entry. 849233294Sstas * 850233294Sstas * The pointer in `sp�� is assumed to be at the top of the entry before 851233294Sstas * previous entry. On success, the `sp�� pointer is set to data portion 852178825Sdfr * of previous entry. In case of error, it's not changed at all. 85355682Smarkm */ 85455682Smarkm 85555682Smarkmkadm5_ret_t 856178825Sdfrkadm5_log_previous (krb5_context context, 857178825Sdfr krb5_storage *sp, 858178825Sdfr uint32_t *ver, 85955682Smarkm time_t *timestamp, 86055682Smarkm enum kadm_ops *op, 861178825Sdfr uint32_t *len) 86255682Smarkm{ 863178825Sdfr krb5_error_code ret; 864178825Sdfr off_t off, oldoff; 86555682Smarkm int32_t tmp; 86655682Smarkm 867178825Sdfr oldoff = krb5_storage_seek(sp, 0, SEEK_CUR); 868178825Sdfr 869102644Snectar krb5_storage_seek(sp, -8, SEEK_CUR); 870178825Sdfr ret = krb5_ret_int32 (sp, &tmp); 871178825Sdfr if (ret) 872178825Sdfr goto end_of_storage; 87355682Smarkm *len = tmp; 874178825Sdfr ret = krb5_ret_int32 (sp, &tmp); 875233294Sstas if (ret) 876233294Sstas goto end_of_storage; 87755682Smarkm *ver = tmp; 87855682Smarkm off = 24 + *len; 879102644Snectar krb5_storage_seek(sp, -off, SEEK_CUR); 880178825Sdfr ret = krb5_ret_int32 (sp, &tmp); 881178825Sdfr if (ret) 882178825Sdfr goto end_of_storage; 883233294Sstas if ((uint32_t)tmp != *ver) { 884178825Sdfr krb5_storage_seek(sp, oldoff, SEEK_SET); 885233294Sstas krb5_set_error_message(context, KADM5_BAD_DB, 886233294Sstas "kadm5_log_previous: log entry " 887233294Sstas "have consistency failure, version number wrong " 888233294Sstas "(tmp %lu ver %lu)", 889233294Sstas (unsigned long)tmp, 890233294Sstas (unsigned long)*ver); 891178825Sdfr return KADM5_BAD_DB; 892178825Sdfr } 893178825Sdfr ret = krb5_ret_int32 (sp, &tmp); 894178825Sdfr if (ret) 895178825Sdfr goto end_of_storage; 89655682Smarkm *timestamp = tmp; 897178825Sdfr ret = krb5_ret_int32 (sp, &tmp); 898233294Sstas if (ret) 899233294Sstas goto end_of_storage; 90055682Smarkm *op = tmp; 901178825Sdfr ret = krb5_ret_int32 (sp, &tmp); 902178825Sdfr if (ret) 903178825Sdfr goto end_of_storage; 904233294Sstas if ((uint32_t)tmp != *len) { 905178825Sdfr krb5_storage_seek(sp, oldoff, SEEK_SET); 906233294Sstas krb5_set_error_message(context, KADM5_BAD_DB, 907233294Sstas "kadm5_log_previous: log entry " 908233294Sstas "have consistency failure, length wrong"); 909178825Sdfr return KADM5_BAD_DB; 910178825Sdfr } 91155682Smarkm return 0; 912178825Sdfr 913178825Sdfr end_of_storage: 914178825Sdfr krb5_storage_seek(sp, oldoff, SEEK_SET); 915233294Sstas krb5_set_error_message(context, ret, "kadm5_log_previous: end of storage " 916233294Sstas "reached before end"); 917178825Sdfr return ret; 91855682Smarkm} 91955682Smarkm 92055682Smarkm/* 92155682Smarkm * Replay a record from the log 92255682Smarkm */ 92355682Smarkm 92455682Smarkmkadm5_ret_t 92555682Smarkmkadm5_log_replay (kadm5_server_context *context, 92655682Smarkm enum kadm_ops op, 927178825Sdfr uint32_t ver, 928178825Sdfr uint32_t len, 92955682Smarkm krb5_storage *sp) 93055682Smarkm{ 93155682Smarkm switch (op) { 93255682Smarkm case kadm_create : 93355682Smarkm return kadm5_log_replay_create (context, ver, len, sp); 93455682Smarkm case kadm_delete : 93555682Smarkm return kadm5_log_replay_delete (context, ver, len, sp); 93655682Smarkm case kadm_rename : 93755682Smarkm return kadm5_log_replay_rename (context, ver, len, sp); 93855682Smarkm case kadm_modify : 93955682Smarkm return kadm5_log_replay_modify (context, ver, len, sp); 94072445Sassar case kadm_nop : 94172445Sassar return kadm5_log_replay_nop (context, ver, len, sp); 94255682Smarkm default : 943233294Sstas krb5_set_error_message(context->context, KADM5_FAILURE, 944233294Sstas "Unsupported replay op %d", (int)op); 94555682Smarkm return KADM5_FAILURE; 94655682Smarkm } 94755682Smarkm} 94872445Sassar 94972445Sassar/* 95072445Sassar * truncate the log - i.e. create an empty file with just (nop vno + 2) 95172445Sassar */ 95272445Sassar 95372445Sassarkadm5_ret_t 95472445Sassarkadm5_log_truncate (kadm5_server_context *server_context) 95572445Sassar{ 95672445Sassar kadm5_ret_t ret; 957178825Sdfr uint32_t vno; 95872445Sassar 95972445Sassar ret = kadm5_log_init (server_context); 96072445Sassar if (ret) 96172445Sassar return ret; 96272445Sassar 96372445Sassar ret = kadm5_log_get_version (server_context, &vno); 96472445Sassar if (ret) 96572445Sassar return ret; 96672445Sassar 96772445Sassar ret = kadm5_log_reinit (server_context); 96872445Sassar if (ret) 96972445Sassar return ret; 97072445Sassar 971178825Sdfr ret = kadm5_log_set_version (server_context, vno); 97272445Sassar if (ret) 97372445Sassar return ret; 97472445Sassar 97572445Sassar ret = kadm5_log_nop (server_context); 97672445Sassar if (ret) 97772445Sassar return ret; 97872445Sassar 97972445Sassar ret = kadm5_log_end (server_context); 98072445Sassar if (ret) 98172445Sassar return ret; 98272445Sassar return 0; 98372445Sassar 98472445Sassar} 985178825Sdfr 986233294Sstas#ifndef NO_UNIX_SOCKETS 987233294Sstas 988178825Sdfrstatic char *default_signal = NULL; 989178825Sdfrstatic HEIMDAL_MUTEX signal_mutex = HEIMDAL_MUTEX_INITIALIZER; 990178825Sdfr 991178825Sdfrconst char * 992178825Sdfrkadm5_log_signal_socket(krb5_context context) 993178825Sdfr{ 994178825Sdfr HEIMDAL_MUTEX_lock(&signal_mutex); 995178825Sdfr if (!default_signal) 996178825Sdfr asprintf(&default_signal, "%s/signal", hdb_db_dir(context)); 997178825Sdfr HEIMDAL_MUTEX_unlock(&signal_mutex); 998178825Sdfr 999178825Sdfr return krb5_config_get_string_default(context, 1000178825Sdfr NULL, 1001178825Sdfr default_signal, 1002178825Sdfr "kdc", 1003178825Sdfr "signal_socket", 1004178825Sdfr NULL); 1005178825Sdfr} 1006233294Sstas 1007233294Sstas#else /* NO_UNIX_SOCKETS */ 1008233294Sstas 1009233294Sstas#define SIGNAL_SOCKET_HOST "127.0.0.1" 1010233294Sstas#define SIGNAL_SOCKET_PORT "12701" 1011233294Sstas 1012233294Sstaskadm5_ret_t 1013233294Sstaskadm5_log_signal_socket_info(krb5_context context, 1014233294Sstas int server_end, 1015233294Sstas struct addrinfo **ret_addrs) 1016233294Sstas{ 1017233294Sstas struct addrinfo hints; 1018233294Sstas struct addrinfo *addrs = NULL; 1019233294Sstas kadm5_ret_t ret = KADM5_FAILURE; 1020233294Sstas int wsret; 1021233294Sstas 1022233294Sstas memset(&hints, 0, sizeof(hints)); 1023233294Sstas 1024233294Sstas hints.ai_flags = AI_NUMERICHOST; 1025233294Sstas if (server_end) 1026233294Sstas hints.ai_flags |= AI_PASSIVE; 1027233294Sstas hints.ai_family = AF_INET; 1028233294Sstas hints.ai_socktype = SOCK_STREAM; 1029233294Sstas hints.ai_protocol = IPPROTO_TCP; 1030233294Sstas 1031233294Sstas wsret = getaddrinfo(SIGNAL_SOCKET_HOST, 1032233294Sstas SIGNAL_SOCKET_PORT, 1033233294Sstas &hints, &addrs); 1034233294Sstas 1035233294Sstas if (wsret != 0) { 1036233294Sstas krb5_set_error_message(context, KADM5_FAILURE, 1037233294Sstas "%s", gai_strerror(wsret)); 1038233294Sstas goto done; 1039233294Sstas } 1040233294Sstas 1041233294Sstas if (addrs == NULL) { 1042233294Sstas krb5_set_error_message(context, KADM5_FAILURE, 1043233294Sstas "getaddrinfo() failed to return address list"); 1044233294Sstas goto done; 1045233294Sstas } 1046233294Sstas 1047233294Sstas *ret_addrs = addrs; 1048233294Sstas addrs = NULL; 1049233294Sstas ret = 0; 1050233294Sstas 1051233294Sstas done: 1052233294Sstas if (addrs) 1053233294Sstas freeaddrinfo(addrs); 1054233294Sstas return ret; 1055233294Sstas} 1056233294Sstas 1057233294Sstas#endif 1058