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"
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);
103 krb5_set_error_message(context
->context
, ret
, "kadm5_log_init: open %s",
104 log_context
->log_file
);
107 if (flock (fd
, LOCK_EX
) < 0) {
109 krb5_set_error_message(context
->context
, ret
, "kadm5_log_init: flock %s",
110 log_context
->log_file
);
115 ret
= kadm5_log_get_version_fd (fd
, &log_context
->version
);
119 log_context
->log_fd
= fd
;
124 kadm5_log_reinit (kadm5_server_context
*context
)
127 kadm5_log_context
*log_context
= &context
->log_context
;
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;
134 fd
= open (log_context
->log_file
, O_RDWR
| O_CREAT
| O_TRUNC
, 0600);
137 if (flock (fd
, LOCK_EX
) < 0) {
142 log_context
->version
= 0;
143 log_context
->log_fd
= fd
;
149 kadm5_log_end (kadm5_server_context
*context
)
151 kadm5_log_context
*log_context
= &context
->log_context
;
152 int fd
= log_context
->log_fd
;
156 log_context
->log_fd
= -1;
161 kadm5_log_preamble (kadm5_server_context
*context
,
165 kadm5_log_context
*log_context
= &context
->log_context
;
166 kadm5_ret_t kadm_ret
;
168 kadm_ret
= kadm5_log_init (context
);
172 krb5_store_int32 (sp
, ++log_context
->version
);
173 krb5_store_int32 (sp
, time(NULL
));
174 krb5_store_int32 (sp
, op
);
179 kadm5_log_postamble (kadm5_log_context
*context
,
182 krb5_store_int32 (sp
, context
->version
);
187 * flush the log record in `sp'.
191 kadm5_log_flush (kadm5_log_context
*log_context
,
198 krb5_storage_to_data(sp
, &data
);
200 ret
= write (log_context
->log_fd
, data
.data
, len
);
202 krb5_data_free(&data
);
205 if (fsync (log_context
->log_fd
) < 0) {
206 krb5_data_free(&data
);
210 * Try to send a signal to any running `ipropd-master'
212 sendto (log_context
->socket_fd
,
213 (void *)&log_context
->version
,
214 sizeof(log_context
->version
),
216 (struct sockaddr
*)&log_context
->socket_name
,
217 sizeof(log_context
->socket_name
));
219 krb5_data_free(&data
);
224 * Add a `create' operation to the log.
228 kadm5_log_create (kadm5_server_context
*context
,
234 kadm5_log_context
*log_context
= &context
->log_context
;
236 sp
= krb5_storage_emem();
237 ret
= hdb_entry2value (context
->context
, ent
, &value
);
239 krb5_storage_free(sp
);
242 ret
= kadm5_log_preamble (context
, sp
, kadm_create
);
244 krb5_data_free (&value
);
245 krb5_storage_free(sp
);
248 krb5_store_int32 (sp
, value
.length
);
249 krb5_storage_write(sp
, value
.data
, value
.length
);
250 krb5_store_int32 (sp
, value
.length
);
251 krb5_data_free (&value
);
252 ret
= kadm5_log_postamble (log_context
, sp
);
254 krb5_storage_free (sp
);
257 ret
= kadm5_log_flush (log_context
, sp
);
258 krb5_storage_free (sp
);
261 ret
= kadm5_log_end (context
);
266 * Read the data of a create log record from `sp' and change the
271 kadm5_log_replay_create (kadm5_server_context
*context
,
280 memset(&ent
, 0, sizeof(ent
));
282 ret
= krb5_data_alloc (&data
, len
);
284 krb5_set_error_message(context
->context
, ret
, "out of memory");
287 krb5_storage_read (sp
, data
.data
, len
);
288 ret
= hdb_value2entry (context
->context
, &data
, &ent
.entry
);
289 krb5_data_free(&data
);
291 krb5_set_error_message(context
->context
, ret
,
292 "Unmarshaling hdb entry failed");
295 ret
= context
->db
->hdb_store(context
->context
, context
->db
, 0, &ent
);
296 hdb_free_entry (context
->context
, &ent
);
301 * Add a `delete' operation to the log.
305 kadm5_log_delete (kadm5_server_context
*context
,
306 krb5_principal princ
)
312 kadm5_log_context
*log_context
= &context
->log_context
;
314 sp
= krb5_storage_emem();
317 ret
= kadm5_log_preamble (context
, sp
, kadm_delete
);
320 ret
= krb5_store_int32 (sp
, 0);
323 off
= krb5_storage_seek (sp
, 0, SEEK_CUR
);
324 ret
= krb5_store_principal (sp
, princ
);
327 len
= krb5_storage_seek (sp
, 0, SEEK_CUR
) - off
;
328 krb5_storage_seek(sp
, -(len
+ 4), SEEK_CUR
);
329 ret
= krb5_store_int32 (sp
, len
);
332 krb5_storage_seek(sp
, len
, SEEK_CUR
);
333 ret
= krb5_store_int32 (sp
, len
);
336 ret
= kadm5_log_postamble (log_context
, sp
);
339 ret
= kadm5_log_flush (log_context
, sp
);
342 ret
= kadm5_log_end (context
);
344 krb5_storage_free (sp
);
349 * Read a `delete' log operation from `sp' and apply it.
353 kadm5_log_replay_delete (kadm5_server_context
*context
,
359 krb5_principal principal
;
361 ret
= krb5_ret_principal (sp
, &principal
);
363 krb5_set_error_message(context
->context
, ret
, "Failed to read deleted "
364 "principal from log version: %ld", (long)ver
);
368 ret
= context
->db
->hdb_remove(context
->context
, context
->db
, principal
);
369 krb5_free_principal (context
->context
, principal
);
374 * Add a `rename' operation to the log.
378 kadm5_log_rename (kadm5_server_context
*context
,
379 krb5_principal source
,
387 kadm5_log_context
*log_context
= &context
->log_context
;
389 krb5_data_zero(&value
);
391 sp
= krb5_storage_emem();
392 ret
= hdb_entry2value (context
->context
, ent
, &value
);
396 ret
= kadm5_log_preamble (context
, sp
, kadm_rename
);
400 ret
= krb5_store_int32 (sp
, 0);
403 off
= krb5_storage_seek (sp
, 0, SEEK_CUR
);
404 ret
= krb5_store_principal (sp
, source
);
408 krb5_storage_write(sp
, value
.data
, value
.length
);
409 len
= krb5_storage_seek (sp
, 0, SEEK_CUR
) - off
;
411 krb5_storage_seek(sp
, -(len
+ 4), SEEK_CUR
);
412 ret
= krb5_store_int32 (sp
, len
);
416 krb5_storage_seek(sp
, len
, SEEK_CUR
);
417 ret
= krb5_store_int32 (sp
, len
);
421 ret
= kadm5_log_postamble (log_context
, sp
);
425 ret
= kadm5_log_flush (log_context
, sp
);
428 krb5_storage_free (sp
);
429 krb5_data_free (&value
);
431 return kadm5_log_end (context
);
434 krb5_data_free(&value
);
435 krb5_storage_free(sp
);
440 * Read a `rename' log operation from `sp' and apply it.
444 kadm5_log_replay_rename (kadm5_server_context
*context
,
450 krb5_principal source
;
451 hdb_entry_ex target_ent
;
454 size_t princ_len
, data_len
;
456 memset(&target_ent
, 0, sizeof(target_ent
));
458 off
= krb5_storage_seek(sp
, 0, SEEK_CUR
);
459 ret
= krb5_ret_principal (sp
, &source
);
461 krb5_set_error_message(context
->context
, ret
, "Failed to read renamed "
462 "principal in log, version: %ld", (long)ver
);
465 princ_len
= krb5_storage_seek(sp
, 0, SEEK_CUR
) - off
;
466 data_len
= len
- princ_len
;
467 ret
= krb5_data_alloc (&value
, data_len
);
469 krb5_free_principal (context
->context
, source
);
472 krb5_storage_read (sp
, value
.data
, data_len
);
473 ret
= hdb_value2entry (context
->context
, &value
, &target_ent
.entry
);
474 krb5_data_free(&value
);
476 krb5_free_principal (context
->context
, source
);
479 ret
= context
->db
->hdb_store (context
->context
, context
->db
,
481 hdb_free_entry (context
->context
, &target_ent
);
483 krb5_free_principal (context
->context
, source
);
486 ret
= context
->db
->hdb_remove (context
->context
, context
->db
, source
);
487 krb5_free_principal (context
->context
, source
);
493 * Add a `modify' operation to the log.
497 kadm5_log_modify (kadm5_server_context
*context
,
505 kadm5_log_context
*log_context
= &context
->log_context
;
507 krb5_data_zero(&value
);
509 sp
= krb5_storage_emem();
510 ret
= hdb_entry2value (context
->context
, ent
, &value
);
514 ret
= kadm5_log_preamble (context
, sp
, kadm_modify
);
518 len
= value
.length
+ 4;
519 ret
= krb5_store_int32 (sp
, len
);
522 ret
= krb5_store_int32 (sp
, mask
);
525 krb5_storage_write (sp
, value
.data
, value
.length
);
527 ret
= krb5_store_int32 (sp
, len
);
530 ret
= kadm5_log_postamble (log_context
, sp
);
533 ret
= kadm5_log_flush (log_context
, sp
);
536 krb5_data_free(&value
);
537 krb5_storage_free (sp
);
538 return kadm5_log_end (context
);
540 krb5_data_free(&value
);
541 krb5_storage_free(sp
);
546 * Read a `modify' log operation from `sp' and apply it.
550 kadm5_log_replay_modify (kadm5_server_context
*context
,
558 hdb_entry_ex ent
, log_ent
;
560 memset(&log_ent
, 0, sizeof(log_ent
));
562 krb5_ret_int32 (sp
, &mask
);
564 ret
= krb5_data_alloc (&value
, len
);
566 krb5_set_error_message(context
->context
, ret
, "out of memory");
569 krb5_storage_read (sp
, value
.data
, len
);
570 ret
= hdb_value2entry (context
->context
, &value
, &log_ent
.entry
);
571 krb5_data_free(&value
);
575 memset(&ent
, 0, sizeof(ent
));
576 ret
= context
->db
->hdb_fetch(context
->context
, context
->db
,
577 log_ent
.entry
.principal
,
578 HDB_F_DECRYPT
|HDB_F_GET_ANY
|HDB_F_ADMIN_DATA
, &ent
);
581 if (mask
& KADM5_PRINC_EXPIRE_TIME
) {
582 if (log_ent
.entry
.valid_end
== NULL
) {
583 ent
.entry
.valid_end
= NULL
;
585 if (ent
.entry
.valid_end
== NULL
) {
586 ent
.entry
.valid_end
= malloc(sizeof(*ent
.entry
.valid_end
));
587 if (ent
.entry
.valid_end
== NULL
) {
589 krb5_set_error_message(context
->context
, ret
, "out of memory");
593 *ent
.entry
.valid_end
= *log_ent
.entry
.valid_end
;
596 if (mask
& KADM5_PW_EXPIRATION
) {
597 if (log_ent
.entry
.pw_end
== NULL
) {
598 ent
.entry
.pw_end
= NULL
;
600 if (ent
.entry
.pw_end
== NULL
) {
601 ent
.entry
.pw_end
= malloc(sizeof(*ent
.entry
.pw_end
));
602 if (ent
.entry
.pw_end
== NULL
) {
604 krb5_set_error_message(context
->context
, ret
, "out of memory");
608 *ent
.entry
.pw_end
= *log_ent
.entry
.pw_end
;
611 if (mask
& KADM5_LAST_PWD_CHANGE
) {
614 if (mask
& KADM5_ATTRIBUTES
) {
615 ent
.entry
.flags
= log_ent
.entry
.flags
;
617 if (mask
& KADM5_MAX_LIFE
) {
618 if (log_ent
.entry
.max_life
== NULL
) {
619 ent
.entry
.max_life
= NULL
;
621 if (ent
.entry
.max_life
== NULL
) {
622 ent
.entry
.max_life
= malloc (sizeof(*ent
.entry
.max_life
));
623 if (ent
.entry
.max_life
== NULL
) {
625 krb5_set_error_message(context
->context
, ret
, "out of memory");
629 *ent
.entry
.max_life
= *log_ent
.entry
.max_life
;
632 if ((mask
& KADM5_MOD_TIME
) && (mask
& KADM5_MOD_NAME
)) {
633 if (ent
.entry
.modified_by
== NULL
) {
634 ent
.entry
.modified_by
= malloc(sizeof(*ent
.entry
.modified_by
));
635 if (ent
.entry
.modified_by
== NULL
) {
637 krb5_set_error_message(context
->context
, ret
, "out of memory");
641 free_Event(ent
.entry
.modified_by
);
642 ret
= copy_Event(log_ent
.entry
.modified_by
, ent
.entry
.modified_by
);
644 krb5_set_error_message(context
->context
, ret
, "out of memory");
648 if (mask
& KADM5_KVNO
) {
649 ent
.entry
.kvno
= log_ent
.entry
.kvno
;
651 if (mask
& KADM5_MKVNO
) {
654 if (mask
& KADM5_AUX_ATTRIBUTES
) {
657 if (mask
& KADM5_POLICY
) {
660 if (mask
& KADM5_POLICY_CLR
) {
663 if (mask
& KADM5_MAX_RLIFE
) {
664 if (log_ent
.entry
.max_renew
== NULL
) {
665 ent
.entry
.max_renew
= NULL
;
667 if (ent
.entry
.max_renew
== NULL
) {
668 ent
.entry
.max_renew
= malloc (sizeof(*ent
.entry
.max_renew
));
669 if (ent
.entry
.max_renew
== NULL
) {
671 krb5_set_error_message(context
->context
, ret
, "out of memory");
675 *ent
.entry
.max_renew
= *log_ent
.entry
.max_renew
;
678 if (mask
& KADM5_LAST_SUCCESS
) {
681 if (mask
& KADM5_LAST_FAILED
) {
684 if (mask
& KADM5_FAIL_AUTH_COUNT
) {
687 if (mask
& KADM5_KEY_DATA
) {
691 for (i
= 0; i
< ent
.entry
.keys
.len
; ++i
)
692 free_Key(&ent
.entry
.keys
.val
[i
]);
693 free (ent
.entry
.keys
.val
);
695 num
= log_ent
.entry
.keys
.len
;
697 ent
.entry
.keys
.len
= num
;
698 ent
.entry
.keys
.val
= malloc(len
* sizeof(*ent
.entry
.keys
.val
));
699 if (ent
.entry
.keys
.val
== NULL
) {
700 krb5_set_error_message(context
->context
, ENOMEM
, "out of memory");
703 for (i
= 0; i
< ent
.entry
.keys
.len
; ++i
) {
704 ret
= copy_Key(&log_ent
.entry
.keys
.val
[i
],
705 &ent
.entry
.keys
.val
[i
]);
707 krb5_set_error_message(context
->context
, ret
, "out of memory");
712 if ((mask
& KADM5_TL_DATA
) && log_ent
.entry
.extensions
) {
713 HDB_extensions
*es
= ent
.entry
.extensions
;
715 ent
.entry
.extensions
= calloc(1, sizeof(*ent
.entry
.extensions
));
716 if (ent
.entry
.extensions
== NULL
)
719 ret
= copy_HDB_extensions(log_ent
.entry
.extensions
,
720 ent
.entry
.extensions
);
722 krb5_set_error_message(context
->context
, ret
, "out of memory");
723 free(ent
.entry
.extensions
);
724 ent
.entry
.extensions
= es
;
728 free_HDB_extensions(es
);
732 ret
= context
->db
->hdb_store(context
->context
, context
->db
,
733 HDB_F_REPLACE
, &ent
);
735 hdb_free_entry (context
->context
, &ent
);
736 hdb_free_entry (context
->context
, &log_ent
);
741 * Add a `nop' operation to the log. Does not close the log.
745 kadm5_log_nop (kadm5_server_context
*context
)
749 kadm5_log_context
*log_context
= &context
->log_context
;
751 sp
= krb5_storage_emem();
752 ret
= kadm5_log_preamble (context
, sp
, kadm_nop
);
754 krb5_storage_free (sp
);
757 krb5_store_int32 (sp
, 0);
758 krb5_store_int32 (sp
, 0);
759 ret
= kadm5_log_postamble (log_context
, sp
);
761 krb5_storage_free (sp
);
764 ret
= kadm5_log_flush (log_context
, sp
);
765 krb5_storage_free (sp
);
771 * Read a `nop' log operation from `sp' and apply it.
775 kadm5_log_replay_nop (kadm5_server_context
*context
,
784 * Call `func' for each log record in the log in `context'
788 kadm5_log_foreach (kadm5_server_context
*context
,
789 void (*func
)(kadm5_server_context
*server_context
,
798 int fd
= context
->log_context
.log_fd
;
801 lseek (fd
, 0, SEEK_SET
);
802 sp
= krb5_storage_from_fd (fd
);
804 int32_t ver
, timestamp
, op
, len
, len2
, ver2
;
806 if(krb5_ret_int32 (sp
, &ver
) != 0)
808 krb5_ret_int32 (sp
, ×tamp
);
809 krb5_ret_int32 (sp
, &op
);
810 krb5_ret_int32 (sp
, &len
);
811 (*func
)(context
, ver
, timestamp
, op
, len
, sp
, ctx
);
812 krb5_ret_int32 (sp
, &len2
);
813 krb5_ret_int32 (sp
, &ver2
);
819 krb5_storage_free(sp
);
828 kadm5_log_goto_end (int fd
)
832 sp
= krb5_storage_from_fd (fd
);
833 krb5_storage_seek(sp
, 0, SEEK_END
);
838 * Return previous log entry.
840 * The pointer in `sp´ is assumed to be at the top of the entry before
841 * previous entry. On success, the `sp´ pointer is set to data portion
842 * of previous entry. In case of error, it's not changed at all.
846 kadm5_log_previous (krb5_context context
,
857 oldoff
= krb5_storage_seek(sp
, 0, SEEK_CUR
);
859 krb5_storage_seek(sp
, -8, SEEK_CUR
);
860 ret
= krb5_ret_int32 (sp
, &tmp
);
864 ret
= krb5_ret_int32 (sp
, &tmp
);
869 krb5_storage_seek(sp
, -off
, SEEK_CUR
);
870 ret
= krb5_ret_int32 (sp
, &tmp
);
874 krb5_storage_seek(sp
, oldoff
, SEEK_SET
);
875 krb5_set_error_message(context
, KADM5_BAD_DB
,
876 "kadm5_log_previous: log entry "
877 "have consistency failure, version number wrong");
880 ret
= krb5_ret_int32 (sp
, &tmp
);
884 ret
= krb5_ret_int32 (sp
, &tmp
);
888 ret
= krb5_ret_int32 (sp
, &tmp
);
892 krb5_storage_seek(sp
, oldoff
, SEEK_SET
);
893 krb5_set_error_message(context
, KADM5_BAD_DB
,
894 "kadm5_log_previous: log entry "
895 "have consistency failure, length wrong");
901 krb5_storage_seek(sp
, oldoff
, SEEK_SET
);
902 krb5_set_error_message(context
, ret
, "kadm5_log_previous: end of storage "
903 "reached before end");
908 * Replay a record from the log
912 kadm5_log_replay (kadm5_server_context
*context
,
920 return kadm5_log_replay_create (context
, ver
, len
, sp
);
922 return kadm5_log_replay_delete (context
, ver
, len
, sp
);
924 return kadm5_log_replay_rename (context
, ver
, len
, sp
);
926 return kadm5_log_replay_modify (context
, ver
, len
, sp
);
928 return kadm5_log_replay_nop (context
, ver
, len
, sp
);
930 krb5_set_error_message(context
->context
, KADM5_FAILURE
,
931 "Unsupported replay op %d", (int)op
);
932 return KADM5_FAILURE
;
937 * truncate the log - i.e. create an empty file with just (nop vno + 2)
941 kadm5_log_truncate (kadm5_server_context
*server_context
)
946 ret
= kadm5_log_init (server_context
);
950 ret
= kadm5_log_get_version (server_context
, &vno
);
954 ret
= kadm5_log_reinit (server_context
);
958 ret
= kadm5_log_set_version (server_context
, vno
);
962 ret
= kadm5_log_nop (server_context
);
966 ret
= kadm5_log_end (server_context
);
973 static char *default_signal
= NULL
;
974 static HEIMDAL_MUTEX signal_mutex
= HEIMDAL_MUTEX_INITIALIZER
;
977 kadm5_log_signal_socket(krb5_context context
)
979 HEIMDAL_MUTEX_lock(&signal_mutex
);
981 asprintf(&default_signal
, "%s/signal", hdb_db_dir(context
));
982 HEIMDAL_MUTEX_unlock(&signal_mutex
);
984 return krb5_config_get_string_default(context
,