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("$Heimdal: log.c 22211 2007-12-07 19:27:27Z lha $"
41 * A log record consists of:
43 * version number 4 bytes
44 * time in seconds 4 bytes
45 * operation (enum kadm_ops) 4 bytes
46 * length of record 4 bytes
48 * length of record 4 bytes
49 * version number 4 bytes
54 kadm5_log_get_version_fd (int fd
,
61 ret
= lseek (fd
, 0, SEEK_END
);
68 sp
= krb5_storage_from_fd (fd
);
69 krb5_storage_seek(sp
, -4, SEEK_CUR
);
70 krb5_ret_int32 (sp
, &old_version
);
72 krb5_storage_free(sp
);
73 lseek (fd
, 0, SEEK_END
);
78 kadm5_log_get_version (kadm5_server_context
*context
, uint32_t *ver
)
80 return kadm5_log_get_version_fd (context
->log_context
.log_fd
, ver
);
84 kadm5_log_set_version (kadm5_server_context
*context
, uint32_t vno
)
86 kadm5_log_context
*log_context
= &context
->log_context
;
88 log_context
->version
= vno
;
93 kadm5_log_init (kadm5_server_context
*context
)
97 kadm5_log_context
*log_context
= &context
->log_context
;
99 if (log_context
->log_fd
!= -1)
101 fd
= open (log_context
->log_file
, O_RDWR
| O_CREAT
, 0600);
103 krb5_set_error_string(context
->context
, "kadm5_log_init: open %s",
104 log_context
->log_file
);
107 if (flock (fd
, LOCK_EX
) < 0) {
108 krb5_set_error_string(context
->context
, "kadm5_log_init: flock %s",
109 log_context
->log_file
);
114 ret
= kadm5_log_get_version_fd (fd
, &log_context
->version
);
118 log_context
->log_fd
= fd
;
123 kadm5_log_reinit (kadm5_server_context
*context
)
126 kadm5_log_context
*log_context
= &context
->log_context
;
128 if (log_context
->log_fd
!= -1) {
129 flock (log_context
->log_fd
, LOCK_UN
);
130 close (log_context
->log_fd
);
131 log_context
->log_fd
= -1;
133 fd
= open (log_context
->log_file
, O_RDWR
| O_CREAT
| O_TRUNC
, 0600);
136 if (flock (fd
, LOCK_EX
) < 0) {
141 log_context
->version
= 0;
142 log_context
->log_fd
= fd
;
148 kadm5_log_end (kadm5_server_context
*context
)
150 kadm5_log_context
*log_context
= &context
->log_context
;
151 int fd
= log_context
->log_fd
;
155 log_context
->log_fd
= -1;
160 kadm5_log_preamble (kadm5_server_context
*context
,
164 kadm5_log_context
*log_context
= &context
->log_context
;
165 kadm5_ret_t kadm_ret
;
167 kadm_ret
= kadm5_log_init (context
);
171 krb5_store_int32 (sp
, ++log_context
->version
);
172 krb5_store_int32 (sp
, time(NULL
));
173 krb5_store_int32 (sp
, op
);
178 kadm5_log_postamble (kadm5_log_context
*context
,
181 krb5_store_int32 (sp
, context
->version
);
186 * flush the log record in `sp'.
190 kadm5_log_flush (kadm5_log_context
*log_context
,
197 krb5_storage_to_data(sp
, &data
);
199 ret
= write (log_context
->log_fd
, data
.data
, len
);
201 krb5_data_free(&data
);
204 if (fsync (log_context
->log_fd
) < 0) {
205 krb5_data_free(&data
);
209 * Try to send a signal to any running `ipropd-master'
211 sendto (log_context
->socket_fd
,
212 (void *)&log_context
->version
,
213 sizeof(log_context
->version
),
215 (struct sockaddr
*)&log_context
->socket_name
,
216 sizeof(log_context
->socket_name
));
218 krb5_data_free(&data
);
223 * Add a `create' operation to the log.
227 kadm5_log_create (kadm5_server_context
*context
,
233 kadm5_log_context
*log_context
= &context
->log_context
;
235 sp
= krb5_storage_emem();
236 ret
= hdb_entry2value (context
->context
, ent
, &value
);
238 krb5_storage_free(sp
);
241 ret
= kadm5_log_preamble (context
, sp
, kadm_create
);
243 krb5_data_free (&value
);
244 krb5_storage_free(sp
);
247 krb5_store_int32 (sp
, value
.length
);
248 krb5_storage_write(sp
, value
.data
, value
.length
);
249 krb5_store_int32 (sp
, value
.length
);
250 krb5_data_free (&value
);
251 ret
= kadm5_log_postamble (log_context
, sp
);
253 krb5_storage_free (sp
);
256 ret
= kadm5_log_flush (log_context
, sp
);
257 krb5_storage_free (sp
);
260 ret
= kadm5_log_end (context
);
265 * Read the data of a create log record from `sp' and change the
270 kadm5_log_replay_create (kadm5_server_context
*context
,
279 memset(&ent
, 0, sizeof(ent
));
281 ret
= krb5_data_alloc (&data
, len
);
283 krb5_set_error_string(context
->context
, "out of memory");
286 krb5_storage_read (sp
, data
.data
, len
);
287 ret
= hdb_value2entry (context
->context
, &data
, &ent
.entry
);
288 krb5_data_free(&data
);
290 krb5_set_error_string(context
->context
,
291 "Unmarshaling hdb entry failed");
294 ret
= context
->db
->hdb_store(context
->context
, context
->db
, 0, &ent
);
295 hdb_free_entry (context
->context
, &ent
);
300 * Add a `delete' operation to the log.
304 kadm5_log_delete (kadm5_server_context
*context
,
305 krb5_principal princ
)
311 kadm5_log_context
*log_context
= &context
->log_context
;
313 sp
= krb5_storage_emem();
316 ret
= kadm5_log_preamble (context
, sp
, kadm_delete
);
319 ret
= krb5_store_int32 (sp
, 0);
322 off
= krb5_storage_seek (sp
, 0, SEEK_CUR
);
323 ret
= krb5_store_principal (sp
, princ
);
326 len
= krb5_storage_seek (sp
, 0, SEEK_CUR
) - off
;
327 krb5_storage_seek(sp
, -(len
+ 4), SEEK_CUR
);
328 ret
= krb5_store_int32 (sp
, len
);
331 krb5_storage_seek(sp
, len
, SEEK_CUR
);
332 ret
= krb5_store_int32 (sp
, len
);
335 ret
= kadm5_log_postamble (log_context
, sp
);
338 ret
= kadm5_log_flush (log_context
, sp
);
341 ret
= kadm5_log_end (context
);
343 krb5_storage_free (sp
);
348 * Read a `delete' log operation from `sp' and apply it.
352 kadm5_log_replay_delete (kadm5_server_context
*context
,
358 krb5_principal principal
;
360 ret
= krb5_ret_principal (sp
, &principal
);
362 krb5_set_error_string(context
->context
, "Failed to read deleted "
363 "principal from log version: %ld", (long)ver
);
367 ret
= context
->db
->hdb_remove(context
->context
, context
->db
, principal
);
368 krb5_free_principal (context
->context
, principal
);
373 * Add a `rename' operation to the log.
377 kadm5_log_rename (kadm5_server_context
*context
,
378 krb5_principal source
,
386 kadm5_log_context
*log_context
= &context
->log_context
;
388 krb5_data_zero(&value
);
390 sp
= krb5_storage_emem();
391 ret
= hdb_entry2value (context
->context
, ent
, &value
);
395 ret
= kadm5_log_preamble (context
, sp
, kadm_rename
);
399 ret
= krb5_store_int32 (sp
, 0);
402 off
= krb5_storage_seek (sp
, 0, SEEK_CUR
);
403 ret
= krb5_store_principal (sp
, source
);
407 krb5_storage_write(sp
, value
.data
, value
.length
);
408 len
= krb5_storage_seek (sp
, 0, SEEK_CUR
) - off
;
410 krb5_storage_seek(sp
, -(len
+ 4), SEEK_CUR
);
411 ret
= krb5_store_int32 (sp
, len
);
415 krb5_storage_seek(sp
, len
, SEEK_CUR
);
416 ret
= krb5_store_int32 (sp
, len
);
420 ret
= kadm5_log_postamble (log_context
, sp
);
424 ret
= kadm5_log_flush (log_context
, sp
);
427 krb5_storage_free (sp
);
428 krb5_data_free (&value
);
430 return kadm5_log_end (context
);
433 krb5_data_free(&value
);
434 krb5_storage_free(sp
);
439 * Read a `rename' log operation from `sp' and apply it.
443 kadm5_log_replay_rename (kadm5_server_context
*context
,
449 krb5_principal source
;
450 hdb_entry_ex target_ent
;
453 size_t princ_len
, data_len
;
455 memset(&target_ent
, 0, sizeof(target_ent
));
457 off
= krb5_storage_seek(sp
, 0, SEEK_CUR
);
458 ret
= krb5_ret_principal (sp
, &source
);
460 krb5_set_error_string(context
->context
, "Failed to read renamed "
461 "principal in log, version: %ld", (long)ver
);
464 princ_len
= krb5_storage_seek(sp
, 0, SEEK_CUR
) - off
;
465 data_len
= len
- princ_len
;
466 ret
= krb5_data_alloc (&value
, data_len
);
468 krb5_free_principal (context
->context
, source
);
471 krb5_storage_read (sp
, value
.data
, data_len
);
472 ret
= hdb_value2entry (context
->context
, &value
, &target_ent
.entry
);
473 krb5_data_free(&value
);
475 krb5_free_principal (context
->context
, source
);
478 ret
= context
->db
->hdb_store (context
->context
, context
->db
,
480 hdb_free_entry (context
->context
, &target_ent
);
482 krb5_free_principal (context
->context
, source
);
485 ret
= context
->db
->hdb_remove (context
->context
, context
->db
, source
);
486 krb5_free_principal (context
->context
, source
);
492 * Add a `modify' operation to the log.
496 kadm5_log_modify (kadm5_server_context
*context
,
504 kadm5_log_context
*log_context
= &context
->log_context
;
506 krb5_data_zero(&value
);
508 sp
= krb5_storage_emem();
509 ret
= hdb_entry2value (context
->context
, ent
, &value
);
513 ret
= kadm5_log_preamble (context
, sp
, kadm_modify
);
517 len
= value
.length
+ 4;
518 ret
= krb5_store_int32 (sp
, len
);
521 ret
= krb5_store_int32 (sp
, mask
);
524 krb5_storage_write (sp
, value
.data
, value
.length
);
526 ret
= krb5_store_int32 (sp
, len
);
529 ret
= kadm5_log_postamble (log_context
, sp
);
532 ret
= kadm5_log_flush (log_context
, sp
);
535 krb5_data_free(&value
);
536 krb5_storage_free (sp
);
537 return kadm5_log_end (context
);
539 krb5_data_free(&value
);
540 krb5_storage_free(sp
);
545 * Read a `modify' log operation from `sp' and apply it.
549 kadm5_log_replay_modify (kadm5_server_context
*context
,
557 hdb_entry_ex ent
, log_ent
;
559 memset(&log_ent
, 0, sizeof(log_ent
));
561 krb5_ret_int32 (sp
, &mask
);
563 ret
= krb5_data_alloc (&value
, len
);
565 krb5_set_error_string(context
->context
, "out of memory");
568 krb5_storage_read (sp
, value
.data
, len
);
569 ret
= hdb_value2entry (context
->context
, &value
, &log_ent
.entry
);
570 krb5_data_free(&value
);
574 memset(&ent
, 0, sizeof(ent
));
575 ret
= context
->db
->hdb_fetch(context
->context
, context
->db
,
576 log_ent
.entry
.principal
,
577 HDB_F_DECRYPT
|HDB_F_GET_ANY
, &ent
);
580 if (mask
& KADM5_PRINC_EXPIRE_TIME
) {
581 if (log_ent
.entry
.valid_end
== NULL
) {
582 ent
.entry
.valid_end
= NULL
;
584 if (ent
.entry
.valid_end
== NULL
) {
585 ent
.entry
.valid_end
= malloc(sizeof(*ent
.entry
.valid_end
));
586 if (ent
.entry
.valid_end
== NULL
) {
587 krb5_set_error_string(context
->context
, "out of memory");
592 *ent
.entry
.valid_end
= *log_ent
.entry
.valid_end
;
595 if (mask
& KADM5_PW_EXPIRATION
) {
596 if (log_ent
.entry
.pw_end
== NULL
) {
597 ent
.entry
.pw_end
= NULL
;
599 if (ent
.entry
.pw_end
== NULL
) {
600 ent
.entry
.pw_end
= malloc(sizeof(*ent
.entry
.pw_end
));
601 if (ent
.entry
.pw_end
== NULL
) {
602 krb5_set_error_string(context
->context
, "out of memory");
607 *ent
.entry
.pw_end
= *log_ent
.entry
.pw_end
;
610 if (mask
& KADM5_LAST_PWD_CHANGE
) {
613 if (mask
& KADM5_ATTRIBUTES
) {
614 ent
.entry
.flags
= log_ent
.entry
.flags
;
616 if (mask
& KADM5_MAX_LIFE
) {
617 if (log_ent
.entry
.max_life
== NULL
) {
618 ent
.entry
.max_life
= NULL
;
620 if (ent
.entry
.max_life
== NULL
) {
621 ent
.entry
.max_life
= malloc (sizeof(*ent
.entry
.max_life
));
622 if (ent
.entry
.max_life
== NULL
) {
623 krb5_set_error_string(context
->context
, "out of memory");
628 *ent
.entry
.max_life
= *log_ent
.entry
.max_life
;
631 if ((mask
& KADM5_MOD_TIME
) && (mask
& KADM5_MOD_NAME
)) {
632 if (ent
.entry
.modified_by
== NULL
) {
633 ent
.entry
.modified_by
= malloc(sizeof(*ent
.entry
.modified_by
));
634 if (ent
.entry
.modified_by
== NULL
) {
635 krb5_set_error_string(context
->context
, "out of memory");
640 free_Event(ent
.entry
.modified_by
);
641 ret
= copy_Event(log_ent
.entry
.modified_by
, ent
.entry
.modified_by
);
643 krb5_set_error_string(context
->context
, "out of memory");
647 if (mask
& KADM5_KVNO
) {
648 ent
.entry
.kvno
= log_ent
.entry
.kvno
;
650 if (mask
& KADM5_MKVNO
) {
653 if (mask
& KADM5_AUX_ATTRIBUTES
) {
656 if (mask
& KADM5_POLICY
) {
659 if (mask
& KADM5_POLICY_CLR
) {
662 if (mask
& KADM5_MAX_RLIFE
) {
663 if (log_ent
.entry
.max_renew
== NULL
) {
664 ent
.entry
.max_renew
= NULL
;
666 if (ent
.entry
.max_renew
== NULL
) {
667 ent
.entry
.max_renew
= malloc (sizeof(*ent
.entry
.max_renew
));
668 if (ent
.entry
.max_renew
== NULL
) {
669 krb5_set_error_string(context
->context
, "out of memory");
674 *ent
.entry
.max_renew
= *log_ent
.entry
.max_renew
;
677 if (mask
& KADM5_LAST_SUCCESS
) {
680 if (mask
& KADM5_LAST_FAILED
) {
683 if (mask
& KADM5_FAIL_AUTH_COUNT
) {
686 if (mask
& KADM5_KEY_DATA
) {
690 for (i
= 0; i
< ent
.entry
.keys
.len
; ++i
)
691 free_Key(&ent
.entry
.keys
.val
[i
]);
692 free (ent
.entry
.keys
.val
);
694 num
= log_ent
.entry
.keys
.len
;
696 ent
.entry
.keys
.len
= num
;
697 ent
.entry
.keys
.val
= malloc(len
* sizeof(*ent
.entry
.keys
.val
));
698 if (ent
.entry
.keys
.val
== NULL
) {
699 krb5_set_error_string(context
->context
, "out of memory");
702 for (i
= 0; i
< ent
.entry
.keys
.len
; ++i
) {
703 ret
= copy_Key(&log_ent
.entry
.keys
.val
[i
],
704 &ent
.entry
.keys
.val
[i
]);
706 krb5_set_error_string(context
->context
, "out of memory");
711 if ((mask
& KADM5_TL_DATA
) && log_ent
.entry
.extensions
) {
712 HDB_extensions
*es
= ent
.entry
.extensions
;
714 ent
.entry
.extensions
= calloc(1, sizeof(*ent
.entry
.extensions
));
715 if (ent
.entry
.extensions
== NULL
)
718 ret
= copy_HDB_extensions(log_ent
.entry
.extensions
,
719 ent
.entry
.extensions
);
721 krb5_set_error_string(context
->context
, "out of memory");
722 free(ent
.entry
.extensions
);
723 ent
.entry
.extensions
= es
;
727 free_HDB_extensions(es
);
731 ret
= context
->db
->hdb_store(context
->context
, context
->db
,
732 HDB_F_REPLACE
, &ent
);
734 hdb_free_entry (context
->context
, &ent
);
735 hdb_free_entry (context
->context
, &log_ent
);
740 * Add a `nop' operation to the log. Does not close the log.
744 kadm5_log_nop (kadm5_server_context
*context
)
748 kadm5_log_context
*log_context
= &context
->log_context
;
750 sp
= krb5_storage_emem();
751 ret
= kadm5_log_preamble (context
, sp
, kadm_nop
);
753 krb5_storage_free (sp
);
756 krb5_store_int32 (sp
, 0);
757 krb5_store_int32 (sp
, 0);
758 ret
= kadm5_log_postamble (log_context
, sp
);
760 krb5_storage_free (sp
);
763 ret
= kadm5_log_flush (log_context
, sp
);
764 krb5_storage_free (sp
);
770 * Read a `nop' log operation from `sp' and apply it.
774 kadm5_log_replay_nop (kadm5_server_context
*context
,
783 * Call `func' for each log record in the log in `context'
787 kadm5_log_foreach (kadm5_server_context
*context
,
788 void (*func
)(kadm5_server_context
*server_context
,
797 int fd
= context
->log_context
.log_fd
;
800 lseek (fd
, 0, SEEK_SET
);
801 sp
= krb5_storage_from_fd (fd
);
803 int32_t ver
, timestamp
, op
, len
, len2
, ver2
;
805 if(krb5_ret_int32 (sp
, &ver
) != 0)
807 krb5_ret_int32 (sp
, ×tamp
);
808 krb5_ret_int32 (sp
, &op
);
809 krb5_ret_int32 (sp
, &len
);
810 (*func
)(context
, ver
, timestamp
, op
, len
, sp
, ctx
);
811 krb5_ret_int32 (sp
, &len2
);
812 krb5_ret_int32 (sp
, &ver2
);
818 krb5_storage_free(sp
);
827 kadm5_log_goto_end (int fd
)
831 sp
= krb5_storage_from_fd (fd
);
832 krb5_storage_seek(sp
, 0, SEEK_END
);
837 * Return previous log entry.
839 * The pointer in `sp´ is assumed to be at the top of the entry before
840 * previous entry. On success, the `sp´ pointer is set to data portion
841 * of previous entry. In case of error, it's not changed at all.
845 kadm5_log_previous (krb5_context context
,
856 oldoff
= krb5_storage_seek(sp
, 0, SEEK_CUR
);
858 krb5_storage_seek(sp
, -8, SEEK_CUR
);
859 ret
= krb5_ret_int32 (sp
, &tmp
);
863 ret
= krb5_ret_int32 (sp
, &tmp
);
866 krb5_storage_seek(sp
, -off
, SEEK_CUR
);
867 ret
= krb5_ret_int32 (sp
, &tmp
);
871 krb5_storage_seek(sp
, oldoff
, SEEK_SET
);
872 krb5_set_error_string(context
, "kadm5_log_previous: log entry "
873 "have consistency failure, version number wrong");
876 ret
= krb5_ret_int32 (sp
, &tmp
);
880 ret
= krb5_ret_int32 (sp
, &tmp
);
882 ret
= krb5_ret_int32 (sp
, &tmp
);
886 krb5_storage_seek(sp
, oldoff
, SEEK_SET
);
887 krb5_set_error_string(context
, "kadm5_log_previous: log entry "
888 "have consistency failure, length wrong");
894 krb5_storage_seek(sp
, oldoff
, SEEK_SET
);
895 krb5_set_error_string(context
, "kadm5_log_previous: end of storage "
896 "reached before end");
901 * Replay a record from the log
905 kadm5_log_replay (kadm5_server_context
*context
,
913 return kadm5_log_replay_create (context
, ver
, len
, sp
);
915 return kadm5_log_replay_delete (context
, ver
, len
, sp
);
917 return kadm5_log_replay_rename (context
, ver
, len
, sp
);
919 return kadm5_log_replay_modify (context
, ver
, len
, sp
);
921 return kadm5_log_replay_nop (context
, ver
, len
, sp
);
923 krb5_set_error_string(context
->context
,
924 "Unsupported replay op %d", (int)op
);
925 return KADM5_FAILURE
;
930 * truncate the log - i.e. create an empty file with just (nop vno + 2)
934 kadm5_log_truncate (kadm5_server_context
*server_context
)
939 ret
= kadm5_log_init (server_context
);
943 ret
= kadm5_log_get_version (server_context
, &vno
);
947 ret
= kadm5_log_reinit (server_context
);
951 ret
= kadm5_log_set_version (server_context
, vno
);
955 ret
= kadm5_log_nop (server_context
);
959 ret
= kadm5_log_end (server_context
);
966 static char *default_signal
= NULL
;
967 static HEIMDAL_MUTEX signal_mutex
= HEIMDAL_MUTEX_INITIALIZER
;
970 kadm5_log_signal_socket(krb5_context context
)
972 HEIMDAL_MUTEX_lock(&signal_mutex
);
974 asprintf(&default_signal
, "%s/signal", hdb_db_dir(context
));
975 HEIMDAL_MUTEX_unlock(&signal_mutex
);
977 return krb5_config_get_string_default(context
,