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);
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
,