2 * Copyright (c) 1997 - 2007 Kungliga Tekniska Högskolan
3 * (Royal Institute of Technology, Stockholm, Sweden).
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
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.
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.
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
34 #include "kadm5_locl.h"
35 #include "heim_threads.h"
37 RCSID("$Id: log.c 22211 2007-12-07 19:27:27Z lha $");
40 * A log record consists of:
42 * version number 4 bytes
43 * time in seconds 4 bytes
44 * operation (enum kadm_ops) 4 bytes
45 * length of record 4 bytes
47 * length of record 4 bytes
48 * version number 4 bytes
53 kadm5_log_get_version_fd (int fd,
60 ret = lseek (fd, 0, SEEK_END);
67 sp = krb5_storage_from_fd (fd);
68 krb5_storage_seek(sp, -4, SEEK_CUR);
69 krb5_ret_int32 (sp, &old_version);
71 krb5_storage_free(sp);
72 lseek (fd, 0, SEEK_END);
77 kadm5_log_get_version (kadm5_server_context *context, uint32_t *ver)
79 return kadm5_log_get_version_fd (context->log_context.log_fd, ver);
83 kadm5_log_set_version (kadm5_server_context *context, uint32_t vno)
85 kadm5_log_context *log_context = &context->log_context;
87 log_context->version = vno;
92 kadm5_log_init (kadm5_server_context *context)
96 kadm5_log_context *log_context = &context->log_context;
98 if (log_context->log_fd != -1)
100 fd = open (log_context->log_file, O_RDWR | O_CREAT, 0600);
102 krb5_set_error_string(context->context, "kadm5_log_init: open %s",
103 log_context->log_file);
106 if (flock (fd, LOCK_EX) < 0) {
107 krb5_set_error_string(context->context, "kadm5_log_init: flock %s",
108 log_context->log_file);
113 ret = kadm5_log_get_version_fd (fd, &log_context->version);
117 log_context->log_fd = fd;
122 kadm5_log_reinit (kadm5_server_context *context)
125 kadm5_log_context *log_context = &context->log_context;
127 if (log_context->log_fd != -1) {
128 flock (log_context->log_fd, LOCK_UN);
129 close (log_context->log_fd);
130 log_context->log_fd = -1;
132 fd = open (log_context->log_file, O_RDWR | O_CREAT | O_TRUNC, 0600);
135 if (flock (fd, LOCK_EX) < 0) {
140 log_context->version = 0;
141 log_context->log_fd = fd;
147 kadm5_log_end (kadm5_server_context *context)
149 kadm5_log_context *log_context = &context->log_context;
150 int fd = log_context->log_fd;
154 log_context->log_fd = -1;
159 kadm5_log_preamble (kadm5_server_context *context,
163 kadm5_log_context *log_context = &context->log_context;
164 kadm5_ret_t kadm_ret;
166 kadm_ret = kadm5_log_init (context);
170 krb5_store_int32 (sp, ++log_context->version);
171 krb5_store_int32 (sp, time(NULL));
172 krb5_store_int32 (sp, op);
177 kadm5_log_postamble (kadm5_log_context *context,
180 krb5_store_int32 (sp, context->version);
185 * flush the log record in `sp'.
189 kadm5_log_flush (kadm5_log_context *log_context,
196 krb5_storage_to_data(sp, &data);
198 ret = write (log_context->log_fd, data.data, len);
200 krb5_data_free(&data);
203 if (fsync (log_context->log_fd) < 0) {
204 krb5_data_free(&data);
208 * Try to send a signal to any running `ipropd-master'
210 sendto (log_context->socket_fd,
211 (void *)&log_context->version,
212 sizeof(log_context->version),
214 (struct sockaddr *)&log_context->socket_name,
215 sizeof(log_context->socket_name));
217 krb5_data_free(&data);
222 * Add a `create' operation to the log.
226 kadm5_log_create (kadm5_server_context *context,
232 kadm5_log_context *log_context = &context->log_context;
234 sp = krb5_storage_emem();
235 ret = hdb_entry2value (context->context, ent, &value);
237 krb5_storage_free(sp);
240 ret = kadm5_log_preamble (context, sp, kadm_create);
242 krb5_data_free (&value);
243 krb5_storage_free(sp);
246 krb5_store_int32 (sp, value.length);
247 krb5_storage_write(sp, value.data, value.length);
248 krb5_store_int32 (sp, value.length);
249 krb5_data_free (&value);
250 ret = kadm5_log_postamble (log_context, sp);
252 krb5_storage_free (sp);
255 ret = kadm5_log_flush (log_context, sp);
256 krb5_storage_free (sp);
259 ret = kadm5_log_end (context);
264 * Read the data of a create log record from `sp' and change the
269 kadm5_log_replay_create (kadm5_server_context *context,
278 memset(&ent, 0, sizeof(ent));
280 ret = krb5_data_alloc (&data, len);
282 krb5_set_error_string(context->context, "out of memory");
285 krb5_storage_read (sp, data.data, len);
286 ret = hdb_value2entry (context->context, &data, &ent.entry);
287 krb5_data_free(&data);
289 krb5_set_error_string(context->context,
290 "Unmarshaling hdb entry failed");
293 ret = context->db->hdb_store(context->context, context->db, 0, &ent);
294 hdb_free_entry (context->context, &ent);
299 * Add a `delete' operation to the log.
303 kadm5_log_delete (kadm5_server_context *context,
304 krb5_principal princ)
310 kadm5_log_context *log_context = &context->log_context;
312 sp = krb5_storage_emem();
315 ret = kadm5_log_preamble (context, sp, kadm_delete);
318 ret = krb5_store_int32 (sp, 0);
321 off = krb5_storage_seek (sp, 0, SEEK_CUR);
322 ret = krb5_store_principal (sp, princ);
325 len = krb5_storage_seek (sp, 0, SEEK_CUR) - off;
326 krb5_storage_seek(sp, -(len + 4), SEEK_CUR);
327 ret = krb5_store_int32 (sp, len);
330 krb5_storage_seek(sp, len, SEEK_CUR);
331 ret = krb5_store_int32 (sp, len);
334 ret = kadm5_log_postamble (log_context, sp);
337 ret = kadm5_log_flush (log_context, sp);
340 ret = kadm5_log_end (context);
342 krb5_storage_free (sp);
347 * Read a `delete' log operation from `sp' and apply it.
351 kadm5_log_replay_delete (kadm5_server_context *context,
357 krb5_principal principal;
359 ret = krb5_ret_principal (sp, &principal);
361 krb5_set_error_string(context->context, "Failed to read deleted "
362 "principal from log version: %ld", (long)ver);
366 ret = context->db->hdb_remove(context->context, context->db, principal);
367 krb5_free_principal (context->context, principal);
372 * Add a `rename' operation to the log.
376 kadm5_log_rename (kadm5_server_context *context,
377 krb5_principal source,
385 kadm5_log_context *log_context = &context->log_context;
387 krb5_data_zero(&value);
389 sp = krb5_storage_emem();
390 ret = hdb_entry2value (context->context, ent, &value);
394 ret = kadm5_log_preamble (context, sp, kadm_rename);
398 ret = krb5_store_int32 (sp, 0);
401 off = krb5_storage_seek (sp, 0, SEEK_CUR);
402 ret = krb5_store_principal (sp, source);
406 krb5_storage_write(sp, value.data, value.length);
407 len = krb5_storage_seek (sp, 0, SEEK_CUR) - off;
409 krb5_storage_seek(sp, -(len + 4), SEEK_CUR);
410 ret = krb5_store_int32 (sp, len);
414 krb5_storage_seek(sp, len, SEEK_CUR);
415 ret = krb5_store_int32 (sp, len);
419 ret = kadm5_log_postamble (log_context, sp);
423 ret = kadm5_log_flush (log_context, sp);
426 krb5_storage_free (sp);
427 krb5_data_free (&value);
429 return kadm5_log_end (context);
432 krb5_data_free(&value);
433 krb5_storage_free(sp);
438 * Read a `rename' log operation from `sp' and apply it.
442 kadm5_log_replay_rename (kadm5_server_context *context,
448 krb5_principal source;
449 hdb_entry_ex target_ent;
452 size_t princ_len, data_len;
454 memset(&target_ent, 0, sizeof(target_ent));
456 off = krb5_storage_seek(sp, 0, SEEK_CUR);
457 ret = krb5_ret_principal (sp, &source);
459 krb5_set_error_string(context->context, "Failed to read renamed "
460 "principal in log, version: %ld", (long)ver);
463 princ_len = krb5_storage_seek(sp, 0, SEEK_CUR) - off;
464 data_len = len - princ_len;
465 ret = krb5_data_alloc (&value, data_len);
467 krb5_free_principal (context->context, source);
470 krb5_storage_read (sp, value.data, data_len);
471 ret = hdb_value2entry (context->context, &value, &target_ent.entry);
472 krb5_data_free(&value);
474 krb5_free_principal (context->context, source);
477 ret = context->db->hdb_store (context->context, context->db,
479 hdb_free_entry (context->context, &target_ent);
481 krb5_free_principal (context->context, source);
484 ret = context->db->hdb_remove (context->context, context->db, source);
485 krb5_free_principal (context->context, source);
491 * Add a `modify' operation to the log.
495 kadm5_log_modify (kadm5_server_context *context,
503 kadm5_log_context *log_context = &context->log_context;
505 krb5_data_zero(&value);
507 sp = krb5_storage_emem();
508 ret = hdb_entry2value (context->context, ent, &value);
512 ret = kadm5_log_preamble (context, sp, kadm_modify);
516 len = value.length + 4;
517 ret = krb5_store_int32 (sp, len);
520 ret = krb5_store_int32 (sp, mask);
523 krb5_storage_write (sp, value.data, value.length);
525 ret = krb5_store_int32 (sp, len);
528 ret = kadm5_log_postamble (log_context, sp);
531 ret = kadm5_log_flush (log_context, sp);
534 krb5_data_free(&value);
535 krb5_storage_free (sp);
536 return kadm5_log_end (context);
538 krb5_data_free(&value);
539 krb5_storage_free(sp);
544 * Read a `modify' log operation from `sp' and apply it.
548 kadm5_log_replay_modify (kadm5_server_context *context,
556 hdb_entry_ex ent, log_ent;
558 memset(&log_ent, 0, sizeof(log_ent));
560 krb5_ret_int32 (sp, &mask);
562 ret = krb5_data_alloc (&value, len);
564 krb5_set_error_string(context->context, "out of memory");
567 krb5_storage_read (sp, value.data, len);
568 ret = hdb_value2entry (context->context, &value, &log_ent.entry);
569 krb5_data_free(&value);
573 memset(&ent, 0, sizeof(ent));
574 ret = context->db->hdb_fetch(context->context, context->db,
575 log_ent.entry.principal,
576 HDB_F_DECRYPT|HDB_F_GET_ANY, &ent);
579 if (mask & KADM5_PRINC_EXPIRE_TIME) {
580 if (log_ent.entry.valid_end == NULL) {
581 ent.entry.valid_end = NULL;
583 if (ent.entry.valid_end == NULL) {
584 ent.entry.valid_end = malloc(sizeof(*ent.entry.valid_end));
585 if (ent.entry.valid_end == NULL) {
586 krb5_set_error_string(context->context, "out of memory");
591 *ent.entry.valid_end = *log_ent.entry.valid_end;
594 if (mask & KADM5_PW_EXPIRATION) {
595 if (log_ent.entry.pw_end == NULL) {
596 ent.entry.pw_end = NULL;
598 if (ent.entry.pw_end == NULL) {
599 ent.entry.pw_end = malloc(sizeof(*ent.entry.pw_end));
600 if (ent.entry.pw_end == NULL) {
601 krb5_set_error_string(context->context, "out of memory");
606 *ent.entry.pw_end = *log_ent.entry.pw_end;
609 if (mask & KADM5_LAST_PWD_CHANGE) {
612 if (mask & KADM5_ATTRIBUTES) {
613 ent.entry.flags = log_ent.entry.flags;
615 if (mask & KADM5_MAX_LIFE) {
616 if (log_ent.entry.max_life == NULL) {
617 ent.entry.max_life = NULL;
619 if (ent.entry.max_life == NULL) {
620 ent.entry.max_life = malloc (sizeof(*ent.entry.max_life));
621 if (ent.entry.max_life == NULL) {
622 krb5_set_error_string(context->context, "out of memory");
627 *ent.entry.max_life = *log_ent.entry.max_life;
630 if ((mask & KADM5_MOD_TIME) && (mask & KADM5_MOD_NAME)) {
631 if (ent.entry.modified_by == NULL) {
632 ent.entry.modified_by = malloc(sizeof(*ent.entry.modified_by));
633 if (ent.entry.modified_by == NULL) {
634 krb5_set_error_string(context->context, "out of memory");
639 free_Event(ent.entry.modified_by);
640 ret = copy_Event(log_ent.entry.modified_by, ent.entry.modified_by);
642 krb5_set_error_string(context->context, "out of memory");
646 if (mask & KADM5_KVNO) {
647 ent.entry.kvno = log_ent.entry.kvno;
649 if (mask & KADM5_MKVNO) {
652 if (mask & KADM5_AUX_ATTRIBUTES) {
655 if (mask & KADM5_POLICY) {
658 if (mask & KADM5_POLICY_CLR) {
661 if (mask & KADM5_MAX_RLIFE) {
662 if (log_ent.entry.max_renew == NULL) {
663 ent.entry.max_renew = NULL;
665 if (ent.entry.max_renew == NULL) {
666 ent.entry.max_renew = malloc (sizeof(*ent.entry.max_renew));
667 if (ent.entry.max_renew == NULL) {
668 krb5_set_error_string(context->context, "out of memory");
673 *ent.entry.max_renew = *log_ent.entry.max_renew;
676 if (mask & KADM5_LAST_SUCCESS) {
679 if (mask & KADM5_LAST_FAILED) {
682 if (mask & KADM5_FAIL_AUTH_COUNT) {
685 if (mask & KADM5_KEY_DATA) {
689 for (i = 0; i < ent.entry.keys.len; ++i)
690 free_Key(&ent.entry.keys.val[i]);
691 free (ent.entry.keys.val);
693 num = log_ent.entry.keys.len;
695 ent.entry.keys.len = num;
696 ent.entry.keys.val = malloc(len * sizeof(*ent.entry.keys.val));
697 if (ent.entry.keys.val == NULL) {
698 krb5_set_error_string(context->context, "out of memory");
701 for (i = 0; i < ent.entry.keys.len; ++i) {
702 ret = copy_Key(&log_ent.entry.keys.val[i],
703 &ent.entry.keys.val[i]);
705 krb5_set_error_string(context->context, "out of memory");
710 if ((mask & KADM5_TL_DATA) && log_ent.entry.extensions) {
711 HDB_extensions *es = ent.entry.extensions;
713 ent.entry.extensions = calloc(1, sizeof(*ent.entry.extensions));
714 if (ent.entry.extensions == NULL)
717 ret = copy_HDB_extensions(log_ent.entry.extensions,
718 ent.entry.extensions);
720 krb5_set_error_string(context->context, "out of memory");
721 free(ent.entry.extensions);
722 ent.entry.extensions = es;
726 free_HDB_extensions(es);
730 ret = context->db->hdb_store(context->context, context->db,
731 HDB_F_REPLACE, &ent);
733 hdb_free_entry (context->context, &ent);
734 hdb_free_entry (context->context, &log_ent);
739 * Add a `nop' operation to the log. Does not close the log.
743 kadm5_log_nop (kadm5_server_context *context)
747 kadm5_log_context *log_context = &context->log_context;
749 sp = krb5_storage_emem();
750 ret = kadm5_log_preamble (context, sp, kadm_nop);
752 krb5_storage_free (sp);
755 krb5_store_int32 (sp, 0);
756 krb5_store_int32 (sp, 0);
757 ret = kadm5_log_postamble (log_context, sp);
759 krb5_storage_free (sp);
762 ret = kadm5_log_flush (log_context, sp);
763 krb5_storage_free (sp);
769 * Read a `nop' log operation from `sp' and apply it.
773 kadm5_log_replay_nop (kadm5_server_context *context,
782 * Call `func' for each log record in the log in `context'
786 kadm5_log_foreach (kadm5_server_context *context,
787 void (*func)(kadm5_server_context *server_context,
796 int fd = context->log_context.log_fd;
799 lseek (fd, 0, SEEK_SET);
800 sp = krb5_storage_from_fd (fd);
802 int32_t ver, timestamp, op, len, len2, ver2;
804 if(krb5_ret_int32 (sp, &ver) != 0)
806 krb5_ret_int32 (sp, ×tamp);
807 krb5_ret_int32 (sp, &op);
808 krb5_ret_int32 (sp, &len);
809 (*func)(context, ver, timestamp, op, len, sp, ctx);
810 krb5_ret_int32 (sp, &len2);
811 krb5_ret_int32 (sp, &ver2);
817 krb5_storage_free(sp);
826 kadm5_log_goto_end (int fd)
830 sp = krb5_storage_from_fd (fd);
831 krb5_storage_seek(sp, 0, SEEK_END);
836 * Return previous log entry.
838 * The pointer in `sp´ is assumed to be at the top of the entry before
839 * previous entry. On success, the `sp´ pointer is set to data portion
840 * of previous entry. In case of error, it's not changed at all.
844 kadm5_log_previous (krb5_context context,
855 oldoff = krb5_storage_seek(sp, 0, SEEK_CUR);
857 krb5_storage_seek(sp, -8, SEEK_CUR);
858 ret = krb5_ret_int32 (sp, &tmp);
862 ret = krb5_ret_int32 (sp, &tmp);
865 krb5_storage_seek(sp, -off, SEEK_CUR);
866 ret = krb5_ret_int32 (sp, &tmp);
870 krb5_storage_seek(sp, oldoff, SEEK_SET);
871 krb5_set_error_string(context, "kadm5_log_previous: log entry "
872 "have consistency failure, version number wrong");
875 ret = krb5_ret_int32 (sp, &tmp);
879 ret = krb5_ret_int32 (sp, &tmp);
881 ret = krb5_ret_int32 (sp, &tmp);
885 krb5_storage_seek(sp, oldoff, SEEK_SET);
886 krb5_set_error_string(context, "kadm5_log_previous: log entry "
887 "have consistency failure, length wrong");
893 krb5_storage_seek(sp, oldoff, SEEK_SET);
894 krb5_set_error_string(context, "kadm5_log_previous: end of storage "
895 "reached before end");
900 * Replay a record from the log
904 kadm5_log_replay (kadm5_server_context *context,
912 return kadm5_log_replay_create (context, ver, len, sp);
914 return kadm5_log_replay_delete (context, ver, len, sp);
916 return kadm5_log_replay_rename (context, ver, len, sp);
918 return kadm5_log_replay_modify (context, ver, len, sp);
920 return kadm5_log_replay_nop (context, ver, len, sp);
922 krb5_set_error_string(context->context,
923 "Unsupported replay op %d", (int)op);
924 return KADM5_FAILURE;
929 * truncate the log - i.e. create an empty file with just (nop vno + 2)
933 kadm5_log_truncate (kadm5_server_context *server_context)
938 ret = kadm5_log_init (server_context);
942 ret = kadm5_log_get_version (server_context, &vno);
946 ret = kadm5_log_reinit (server_context);
950 ret = kadm5_log_set_version (server_context, vno);
954 ret = kadm5_log_nop (server_context);
958 ret = kadm5_log_end (server_context);
965 static char *default_signal = NULL;
966 static HEIMDAL_MUTEX signal_mutex = HEIMDAL_MUTEX_INITIALIZER;
969 kadm5_log_signal_socket(krb5_context context)
971 HEIMDAL_MUTEX_lock(&signal_mutex);
973 asprintf(&default_signal, "%s/signal", hdb_db_dir(context));
974 HEIMDAL_MUTEX_unlock(&signal_mutex);
976 return krb5_config_get_string_default(context,