xref: /freebsd/crypto/heimdal/lib/kadm5/log.c (revision aa0a1e58)
1 /*
2  * Copyright (c) 1997 - 2007 Kungliga Tekniska H�gskolan
3  * (Royal Institute of Technology, Stockholm, Sweden).
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  *
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  *
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.
16  *
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.
20  *
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
31  * SUCH DAMAGE.
32  */
33 
34 #include "kadm5_locl.h"
35 #include "heim_threads.h"
36 
37 RCSID("$Id: log.c 22211 2007-12-07 19:27:27Z lha $");
38 
39 /*
40  * A log record consists of:
41  *
42  * version number		4 bytes
43  * time in seconds		4 bytes
44  * operation (enum kadm_ops)	4 bytes
45  * length of record		4 bytes
46  * data...			n bytes
47  * length of record		4 bytes
48  * version number		4 bytes
49  *
50  */
51 
52 kadm5_ret_t
53 kadm5_log_get_version_fd (int fd,
54 			  uint32_t *ver)
55 {
56     int ret;
57     krb5_storage *sp;
58     int32_t old_version;
59 
60     ret = lseek (fd, 0, SEEK_END);
61     if(ret < 0)
62 	return errno;
63     if(ret == 0) {
64 	*ver = 0;
65 	return 0;
66     }
67     sp = krb5_storage_from_fd (fd);
68     krb5_storage_seek(sp, -4, SEEK_CUR);
69     krb5_ret_int32 (sp, &old_version);
70     *ver = old_version;
71     krb5_storage_free(sp);
72     lseek (fd, 0, SEEK_END);
73     return 0;
74 }
75 
76 kadm5_ret_t
77 kadm5_log_get_version (kadm5_server_context *context, uint32_t *ver)
78 {
79     return kadm5_log_get_version_fd (context->log_context.log_fd, ver);
80 }
81 
82 kadm5_ret_t
83 kadm5_log_set_version (kadm5_server_context *context, uint32_t vno)
84 {
85     kadm5_log_context *log_context = &context->log_context;
86 
87     log_context->version = vno;
88     return 0;
89 }
90 
91 kadm5_ret_t
92 kadm5_log_init (kadm5_server_context *context)
93 {
94     int fd;
95     kadm5_ret_t ret;
96     kadm5_log_context *log_context = &context->log_context;
97 
98     if (log_context->log_fd != -1)
99 	return 0;
100     fd = open (log_context->log_file, O_RDWR | O_CREAT, 0600);
101     if (fd < 0) {
102 	krb5_set_error_string(context->context, "kadm5_log_init: open %s",
103 			      log_context->log_file);
104 	return errno;
105     }
106     if (flock (fd, LOCK_EX) < 0) {
107 	krb5_set_error_string(context->context, "kadm5_log_init: flock %s",
108 			      log_context->log_file);
109 	close (fd);
110 	return errno;
111     }
112 
113     ret = kadm5_log_get_version_fd (fd, &log_context->version);
114     if (ret)
115 	return ret;
116 
117     log_context->log_fd  = fd;
118     return 0;
119 }
120 
121 kadm5_ret_t
122 kadm5_log_reinit (kadm5_server_context *context)
123 {
124     int fd;
125     kadm5_log_context *log_context = &context->log_context;
126 
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;
131     }
132     fd = open (log_context->log_file, O_RDWR | O_CREAT | O_TRUNC, 0600);
133     if (fd < 0)
134 	return errno;
135     if (flock (fd, LOCK_EX) < 0) {
136 	close (fd);
137 	return errno;
138     }
139 
140     log_context->version = 0;
141     log_context->log_fd  = fd;
142     return 0;
143 }
144 
145 
146 kadm5_ret_t
147 kadm5_log_end (kadm5_server_context *context)
148 {
149     kadm5_log_context *log_context = &context->log_context;
150     int fd = log_context->log_fd;
151 
152     flock (fd, LOCK_UN);
153     close(fd);
154     log_context->log_fd = -1;
155     return 0;
156 }
157 
158 static kadm5_ret_t
159 kadm5_log_preamble (kadm5_server_context *context,
160 		    krb5_storage *sp,
161 		    enum kadm_ops op)
162 {
163     kadm5_log_context *log_context = &context->log_context;
164     kadm5_ret_t kadm_ret;
165 
166     kadm_ret = kadm5_log_init (context);
167     if (kadm_ret)
168 	return kadm_ret;
169 
170     krb5_store_int32 (sp, ++log_context->version);
171     krb5_store_int32 (sp, time(NULL));
172     krb5_store_int32 (sp, op);
173     return 0;
174 }
175 
176 static kadm5_ret_t
177 kadm5_log_postamble (kadm5_log_context *context,
178 		     krb5_storage *sp)
179 {
180     krb5_store_int32 (sp, context->version);
181     return 0;
182 }
183 
184 /*
185  * flush the log record in `sp'.
186  */
187 
188 static kadm5_ret_t
189 kadm5_log_flush (kadm5_log_context *log_context,
190 		 krb5_storage *sp)
191 {
192     krb5_data data;
193     size_t len;
194     int ret;
195 
196     krb5_storage_to_data(sp, &data);
197     len = data.length;
198     ret = write (log_context->log_fd, data.data, len);
199     if (ret != len) {
200 	krb5_data_free(&data);
201 	return errno;
202     }
203     if (fsync (log_context->log_fd) < 0) {
204 	krb5_data_free(&data);
205 	return errno;
206     }
207     /*
208      * Try to send a signal to any running `ipropd-master'
209      */
210     sendto (log_context->socket_fd,
211 	    (void *)&log_context->version,
212 	    sizeof(log_context->version),
213 	    0,
214 	    (struct sockaddr *)&log_context->socket_name,
215 	    sizeof(log_context->socket_name));
216 
217     krb5_data_free(&data);
218     return 0;
219 }
220 
221 /*
222  * Add a `create' operation to the log.
223  */
224 
225 kadm5_ret_t
226 kadm5_log_create (kadm5_server_context *context,
227 		  hdb_entry *ent)
228 {
229     krb5_storage *sp;
230     kadm5_ret_t ret;
231     krb5_data value;
232     kadm5_log_context *log_context = &context->log_context;
233 
234     sp = krb5_storage_emem();
235     ret = hdb_entry2value (context->context, ent, &value);
236     if (ret) {
237 	krb5_storage_free(sp);
238 	return ret;
239     }
240     ret = kadm5_log_preamble (context, sp, kadm_create);
241     if (ret) {
242 	krb5_data_free (&value);
243 	krb5_storage_free(sp);
244 	return ret;
245     }
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);
251     if (ret) {
252 	krb5_storage_free (sp);
253 	return ret;
254     }
255     ret = kadm5_log_flush (log_context, sp);
256     krb5_storage_free (sp);
257     if (ret)
258 	return ret;
259     ret = kadm5_log_end (context);
260     return ret;
261 }
262 
263 /*
264  * Read the data of a create log record from `sp' and change the
265  * database.
266  */
267 
268 static kadm5_ret_t
269 kadm5_log_replay_create (kadm5_server_context *context,
270 			 uint32_t ver,
271 			 uint32_t len,
272 			 krb5_storage *sp)
273 {
274     krb5_error_code ret;
275     krb5_data data;
276     hdb_entry_ex ent;
277 
278     memset(&ent, 0, sizeof(ent));
279 
280     ret = krb5_data_alloc (&data, len);
281     if (ret) {
282 	krb5_set_error_string(context->context, "out of memory");
283 	return ret;
284     }
285     krb5_storage_read (sp, data.data, len);
286     ret = hdb_value2entry (context->context, &data, &ent.entry);
287     krb5_data_free(&data);
288     if (ret) {
289 	krb5_set_error_string(context->context,
290 			      "Unmarshaling hdb entry failed");
291 	return ret;
292     }
293     ret = context->db->hdb_store(context->context, context->db, 0, &ent);
294     hdb_free_entry (context->context, &ent);
295     return ret;
296 }
297 
298 /*
299  * Add a `delete' operation to the log.
300  */
301 
302 kadm5_ret_t
303 kadm5_log_delete (kadm5_server_context *context,
304 		  krb5_principal princ)
305 {
306     krb5_storage *sp;
307     kadm5_ret_t ret;
308     off_t off;
309     off_t len;
310     kadm5_log_context *log_context = &context->log_context;
311 
312     sp = krb5_storage_emem();
313     if (sp == NULL)
314 	return ENOMEM;
315     ret = kadm5_log_preamble (context, sp, kadm_delete);
316     if (ret)
317 	goto out;
318     ret = krb5_store_int32 (sp, 0);
319     if (ret)
320 	goto out;
321     off = krb5_storage_seek (sp, 0, SEEK_CUR);
322     ret = krb5_store_principal (sp, princ);
323     if (ret)
324 	goto out;
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);
328     if (ret)
329 	goto out;
330     krb5_storage_seek(sp, len, SEEK_CUR);
331     ret = krb5_store_int32 (sp, len);
332     if (ret)
333 	goto out;
334     ret = kadm5_log_postamble (log_context, sp);
335     if (ret)
336 	goto out;
337     ret = kadm5_log_flush (log_context, sp);
338     if (ret)
339 	goto out;
340     ret = kadm5_log_end (context);
341 out:
342     krb5_storage_free (sp);
343     return ret;
344 }
345 
346 /*
347  * Read a `delete' log operation from `sp' and apply it.
348  */
349 
350 static kadm5_ret_t
351 kadm5_log_replay_delete (kadm5_server_context *context,
352 			 uint32_t ver,
353 			 uint32_t len,
354 			 krb5_storage *sp)
355 {
356     krb5_error_code ret;
357     krb5_principal principal;
358 
359     ret = krb5_ret_principal (sp, &principal);
360     if (ret) {
361 	krb5_set_error_string(context->context,  "Failed to read deleted "
362 			      "principal from log version: %ld",  (long)ver);
363 	return ret;
364     }
365 
366     ret = context->db->hdb_remove(context->context, context->db, principal);
367     krb5_free_principal (context->context, principal);
368     return ret;
369 }
370 
371 /*
372  * Add a `rename' operation to the log.
373  */
374 
375 kadm5_ret_t
376 kadm5_log_rename (kadm5_server_context *context,
377 		  krb5_principal source,
378 		  hdb_entry *ent)
379 {
380     krb5_storage *sp;
381     kadm5_ret_t ret;
382     off_t off;
383     off_t len;
384     krb5_data value;
385     kadm5_log_context *log_context = &context->log_context;
386 
387     krb5_data_zero(&value);
388 
389     sp = krb5_storage_emem();
390     ret = hdb_entry2value (context->context, ent, &value);
391     if (ret)
392 	goto failed;
393 
394     ret = kadm5_log_preamble (context, sp, kadm_rename);
395     if (ret)
396 	goto failed;
397 
398     ret = krb5_store_int32 (sp, 0);
399     if (ret)
400 	goto failed;
401     off = krb5_storage_seek (sp, 0, SEEK_CUR);
402     ret = krb5_store_principal (sp, source);
403     if (ret)
404 	goto failed;
405 
406     krb5_storage_write(sp, value.data, value.length);
407     len = krb5_storage_seek (sp, 0, SEEK_CUR) - off;
408 
409     krb5_storage_seek(sp, -(len + 4), SEEK_CUR);
410     ret = krb5_store_int32 (sp, len);
411     if (ret)
412 	goto failed;
413 
414     krb5_storage_seek(sp, len, SEEK_CUR);
415     ret = krb5_store_int32 (sp, len);
416     if (ret)
417 	goto failed;
418 
419     ret = kadm5_log_postamble (log_context, sp);
420     if (ret)
421 	goto failed;
422 
423     ret = kadm5_log_flush (log_context, sp);
424     if (ret)
425 	goto failed;
426     krb5_storage_free (sp);
427     krb5_data_free (&value);
428 
429     return kadm5_log_end (context);
430 
431 failed:
432     krb5_data_free(&value);
433     krb5_storage_free(sp);
434     return ret;
435 }
436 
437 /*
438  * Read a `rename' log operation from `sp' and apply it.
439  */
440 
441 static kadm5_ret_t
442 kadm5_log_replay_rename (kadm5_server_context *context,
443 			 uint32_t ver,
444 			 uint32_t len,
445 			 krb5_storage *sp)
446 {
447     krb5_error_code ret;
448     krb5_principal source;
449     hdb_entry_ex target_ent;
450     krb5_data value;
451     off_t off;
452     size_t princ_len, data_len;
453 
454     memset(&target_ent, 0, sizeof(target_ent));
455 
456     off = krb5_storage_seek(sp, 0, SEEK_CUR);
457     ret = krb5_ret_principal (sp, &source);
458     if (ret) {
459 	krb5_set_error_string(context->context, "Failed to read renamed "
460 			      "principal in log, version: %ld", (long)ver);
461 	return ret;
462     }
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);
466     if (ret) {
467 	krb5_free_principal (context->context, source);
468 	return ret;
469     }
470     krb5_storage_read (sp, value.data, data_len);
471     ret = hdb_value2entry (context->context, &value, &target_ent.entry);
472     krb5_data_free(&value);
473     if (ret) {
474 	krb5_free_principal (context->context, source);
475 	return ret;
476     }
477     ret = context->db->hdb_store (context->context, context->db,
478 				  0, &target_ent);
479     hdb_free_entry (context->context, &target_ent);
480     if (ret) {
481 	krb5_free_principal (context->context, source);
482 	return ret;
483     }
484     ret = context->db->hdb_remove (context->context, context->db, source);
485     krb5_free_principal (context->context, source);
486     return ret;
487 }
488 
489 
490 /*
491  * Add a `modify' operation to the log.
492  */
493 
494 kadm5_ret_t
495 kadm5_log_modify (kadm5_server_context *context,
496 		  hdb_entry *ent,
497 		  uint32_t mask)
498 {
499     krb5_storage *sp;
500     kadm5_ret_t ret;
501     krb5_data value;
502     uint32_t len;
503     kadm5_log_context *log_context = &context->log_context;
504 
505     krb5_data_zero(&value);
506 
507     sp = krb5_storage_emem();
508     ret = hdb_entry2value (context->context, ent, &value);
509     if (ret)
510 	goto failed;
511 
512     ret = kadm5_log_preamble (context, sp, kadm_modify);
513     if (ret)
514 	goto failed;
515 
516     len = value.length + 4;
517     ret = krb5_store_int32 (sp, len);
518     if (ret)
519 	goto failed;
520     ret = krb5_store_int32 (sp, mask);
521     if (ret)
522 	goto failed;
523     krb5_storage_write (sp, value.data, value.length);
524 
525     ret = krb5_store_int32 (sp, len);
526     if (ret)
527 	goto failed;
528     ret = kadm5_log_postamble (log_context, sp);
529     if (ret)
530 	goto failed;
531     ret = kadm5_log_flush (log_context, sp);
532     if (ret)
533 	goto failed;
534     krb5_data_free(&value);
535     krb5_storage_free (sp);
536     return kadm5_log_end (context);
537 failed:
538     krb5_data_free(&value);
539     krb5_storage_free(sp);
540     return ret;
541 }
542 
543 /*
544  * Read a `modify' log operation from `sp' and apply it.
545  */
546 
547 static kadm5_ret_t
548 kadm5_log_replay_modify (kadm5_server_context *context,
549 			 uint32_t ver,
550 			 uint32_t len,
551 			 krb5_storage *sp)
552 {
553     krb5_error_code ret;
554     int32_t mask;
555     krb5_data value;
556     hdb_entry_ex ent, log_ent;
557 
558     memset(&log_ent, 0, sizeof(log_ent));
559 
560     krb5_ret_int32 (sp, &mask);
561     len -= 4;
562     ret = krb5_data_alloc (&value, len);
563     if (ret) {
564 	krb5_set_error_string(context->context, "out of memory");
565 	return ret;
566     }
567     krb5_storage_read (sp, value.data, len);
568     ret = hdb_value2entry (context->context, &value, &log_ent.entry);
569     krb5_data_free(&value);
570     if (ret)
571 	return ret;
572 
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);
577     if (ret)
578 	goto out;
579     if (mask & KADM5_PRINC_EXPIRE_TIME) {
580 	if (log_ent.entry.valid_end == NULL) {
581 	    ent.entry.valid_end = NULL;
582 	} else {
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");
587 		    ret = ENOMEM;
588 		    goto out;
589 		}
590 	    }
591 	    *ent.entry.valid_end = *log_ent.entry.valid_end;
592 	}
593     }
594     if (mask & KADM5_PW_EXPIRATION) {
595 	if (log_ent.entry.pw_end == NULL) {
596 	    ent.entry.pw_end = NULL;
597 	} else {
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");
602 		    ret = ENOMEM;
603 		    goto out;
604 		}
605 	    }
606 	    *ent.entry.pw_end = *log_ent.entry.pw_end;
607 	}
608     }
609     if (mask & KADM5_LAST_PWD_CHANGE) {
610 	abort ();		/* XXX */
611     }
612     if (mask & KADM5_ATTRIBUTES) {
613 	ent.entry.flags = log_ent.entry.flags;
614     }
615     if (mask & KADM5_MAX_LIFE) {
616 	if (log_ent.entry.max_life == NULL) {
617 	    ent.entry.max_life = NULL;
618 	} else {
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");
623 		    ret = ENOMEM;
624 		    goto out;
625 		}
626 	    }
627 	    *ent.entry.max_life = *log_ent.entry.max_life;
628 	}
629     }
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");
635 		ret = ENOMEM;
636 		goto out;
637 	    }
638 	} else
639 	    free_Event(ent.entry.modified_by);
640 	ret = copy_Event(log_ent.entry.modified_by, ent.entry.modified_by);
641 	if (ret) {
642 	    krb5_set_error_string(context->context, "out of memory");
643 	    goto out;
644 	}
645     }
646     if (mask & KADM5_KVNO) {
647 	ent.entry.kvno = log_ent.entry.kvno;
648     }
649     if (mask & KADM5_MKVNO) {
650 	abort ();		/* XXX */
651     }
652     if (mask & KADM5_AUX_ATTRIBUTES) {
653 	abort ();		/* XXX */
654     }
655     if (mask & KADM5_POLICY) {
656 	abort ();		/* XXX */
657     }
658     if (mask & KADM5_POLICY_CLR) {
659 	abort ();		/* XXX */
660     }
661     if (mask & KADM5_MAX_RLIFE) {
662 	if (log_ent.entry.max_renew == NULL) {
663 	    ent.entry.max_renew = NULL;
664 	} else {
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");
669 		    ret = ENOMEM;
670 		    goto out;
671 		}
672 	    }
673 	    *ent.entry.max_renew = *log_ent.entry.max_renew;
674 	}
675     }
676     if (mask & KADM5_LAST_SUCCESS) {
677 	abort ();		/* XXX */
678     }
679     if (mask & KADM5_LAST_FAILED) {
680 	abort ();		/* XXX */
681     }
682     if (mask & KADM5_FAIL_AUTH_COUNT) {
683 	abort ();		/* XXX */
684     }
685     if (mask & KADM5_KEY_DATA) {
686 	size_t num;
687 	int i;
688 
689 	for (i = 0; i < ent.entry.keys.len; ++i)
690 	    free_Key(&ent.entry.keys.val[i]);
691 	free (ent.entry.keys.val);
692 
693 	num = log_ent.entry.keys.len;
694 
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");
699 	    return ENOMEM;
700 	}
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]);
704 	    if (ret) {
705 		krb5_set_error_string(context->context, "out of memory");
706 		goto out;
707 	    }
708 	}
709     }
710     if ((mask & KADM5_TL_DATA) && log_ent.entry.extensions) {
711 	HDB_extensions *es = ent.entry.extensions;
712 
713 	ent.entry.extensions = calloc(1, sizeof(*ent.entry.extensions));
714 	if (ent.entry.extensions == NULL)
715 	    goto out;
716 
717 	ret = copy_HDB_extensions(log_ent.entry.extensions,
718 				  ent.entry.extensions);
719 	if (ret) {
720 	    krb5_set_error_string(context->context, "out of memory");
721 	    free(ent.entry.extensions);
722 	    ent.entry.extensions = es;
723 	    goto out;
724 	}
725 	if (es) {
726 	    free_HDB_extensions(es);
727 	    free(es);
728 	}
729     }
730     ret = context->db->hdb_store(context->context, context->db,
731 				 HDB_F_REPLACE, &ent);
732  out:
733     hdb_free_entry (context->context, &ent);
734     hdb_free_entry (context->context, &log_ent);
735     return ret;
736 }
737 
738 /*
739  * Add a `nop' operation to the log. Does not close the log.
740  */
741 
742 kadm5_ret_t
743 kadm5_log_nop (kadm5_server_context *context)
744 {
745     krb5_storage *sp;
746     kadm5_ret_t ret;
747     kadm5_log_context *log_context = &context->log_context;
748 
749     sp = krb5_storage_emem();
750     ret = kadm5_log_preamble (context, sp, kadm_nop);
751     if (ret) {
752 	krb5_storage_free (sp);
753 	return ret;
754     }
755     krb5_store_int32 (sp, 0);
756     krb5_store_int32 (sp, 0);
757     ret = kadm5_log_postamble (log_context, sp);
758     if (ret) {
759 	krb5_storage_free (sp);
760 	return ret;
761     }
762     ret = kadm5_log_flush (log_context, sp);
763     krb5_storage_free (sp);
764 
765     return ret;
766 }
767 
768 /*
769  * Read a `nop' log operation from `sp' and apply it.
770  */
771 
772 static kadm5_ret_t
773 kadm5_log_replay_nop (kadm5_server_context *context,
774 		      uint32_t ver,
775 		      uint32_t len,
776 		      krb5_storage *sp)
777 {
778     return 0;
779 }
780 
781 /*
782  * Call `func' for each log record in the log in `context'
783  */
784 
785 kadm5_ret_t
786 kadm5_log_foreach (kadm5_server_context *context,
787 		   void (*func)(kadm5_server_context *server_context,
788 				uint32_t ver,
789 				time_t timestamp,
790 				enum kadm_ops op,
791 				uint32_t len,
792 				krb5_storage *,
793 				void *),
794 		   void *ctx)
795 {
796     int fd = context->log_context.log_fd;
797     krb5_storage *sp;
798 
799     lseek (fd, 0, SEEK_SET);
800     sp = krb5_storage_from_fd (fd);
801     for (;;) {
802 	int32_t ver, timestamp, op, len, len2, ver2;
803 
804 	if(krb5_ret_int32 (sp, &ver) != 0)
805 	    break;
806 	krb5_ret_int32 (sp, &timestamp);
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);
812 	if (len != len2)
813 	    abort();
814 	if (ver != ver2)
815 	    abort();
816     }
817     krb5_storage_free(sp);
818     return 0;
819 }
820 
821 /*
822  * Go to end of log.
823  */
824 
825 krb5_storage *
826 kadm5_log_goto_end (int fd)
827 {
828     krb5_storage *sp;
829 
830     sp = krb5_storage_from_fd (fd);
831     krb5_storage_seek(sp, 0, SEEK_END);
832     return sp;
833 }
834 
835 /*
836  * Return previous log entry.
837  *
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.
841  */
842 
843 kadm5_ret_t
844 kadm5_log_previous (krb5_context context,
845 		    krb5_storage *sp,
846 		    uint32_t *ver,
847 		    time_t *timestamp,
848 		    enum kadm_ops *op,
849 		    uint32_t *len)
850 {
851     krb5_error_code ret;
852     off_t off, oldoff;
853     int32_t tmp;
854 
855     oldoff = krb5_storage_seek(sp, 0, SEEK_CUR);
856 
857     krb5_storage_seek(sp, -8, SEEK_CUR);
858     ret = krb5_ret_int32 (sp, &tmp);
859     if (ret)
860 	goto end_of_storage;
861     *len = tmp;
862     ret = krb5_ret_int32 (sp, &tmp);
863     *ver = tmp;
864     off = 24 + *len;
865     krb5_storage_seek(sp, -off, SEEK_CUR);
866     ret = krb5_ret_int32 (sp, &tmp);
867     if (ret)
868 	goto end_of_storage;
869     if (tmp != *ver) {
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");
873 	return KADM5_BAD_DB;
874     }
875     ret = krb5_ret_int32 (sp, &tmp);
876     if (ret)
877 	goto end_of_storage;
878     *timestamp = tmp;
879     ret = krb5_ret_int32 (sp, &tmp);
880     *op = tmp;
881     ret = krb5_ret_int32 (sp, &tmp);
882     if (ret)
883 	goto end_of_storage;
884     if (tmp != *len) {
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");
888 	return KADM5_BAD_DB;
889     }
890     return 0;
891 
892  end_of_storage:
893     krb5_storage_seek(sp, oldoff, SEEK_SET);
894     krb5_set_error_string(context, "kadm5_log_previous: end of storage "
895 			  "reached before end");
896     return ret;
897 }
898 
899 /*
900  * Replay a record from the log
901  */
902 
903 kadm5_ret_t
904 kadm5_log_replay (kadm5_server_context *context,
905 		  enum kadm_ops op,
906 		  uint32_t ver,
907 		  uint32_t len,
908 		  krb5_storage *sp)
909 {
910     switch (op) {
911     case kadm_create :
912 	return kadm5_log_replay_create (context, ver, len, sp);
913     case kadm_delete :
914 	return kadm5_log_replay_delete (context, ver, len, sp);
915     case kadm_rename :
916 	return kadm5_log_replay_rename (context, ver, len, sp);
917     case kadm_modify :
918 	return kadm5_log_replay_modify (context, ver, len, sp);
919     case kadm_nop :
920 	return kadm5_log_replay_nop (context, ver, len, sp);
921     default :
922 	krb5_set_error_string(context->context,
923 			      "Unsupported replay op %d", (int)op);
924 	return KADM5_FAILURE;
925     }
926 }
927 
928 /*
929  * truncate the log - i.e. create an empty file with just (nop vno + 2)
930  */
931 
932 kadm5_ret_t
933 kadm5_log_truncate (kadm5_server_context *server_context)
934 {
935     kadm5_ret_t ret;
936     uint32_t vno;
937 
938     ret = kadm5_log_init (server_context);
939     if (ret)
940 	return ret;
941 
942     ret = kadm5_log_get_version (server_context, &vno);
943     if (ret)
944 	return ret;
945 
946     ret = kadm5_log_reinit (server_context);
947     if (ret)
948 	return ret;
949 
950     ret = kadm5_log_set_version (server_context, vno);
951     if (ret)
952 	return ret;
953 
954     ret = kadm5_log_nop (server_context);
955     if (ret)
956 	return ret;
957 
958     ret = kadm5_log_end (server_context);
959     if (ret)
960 	return ret;
961     return 0;
962 
963 }
964 
965 static char *default_signal = NULL;
966 static HEIMDAL_MUTEX signal_mutex = HEIMDAL_MUTEX_INITIALIZER;
967 
968 const char *
969 kadm5_log_signal_socket(krb5_context context)
970 {
971     HEIMDAL_MUTEX_lock(&signal_mutex);
972     if (!default_signal)
973 	asprintf(&default_signal, "%s/signal", hdb_db_dir(context));
974     HEIMDAL_MUTEX_unlock(&signal_mutex);
975 
976     return krb5_config_get_string_default(context,
977 					  NULL,
978 					  default_signal,
979 					  "kdc",
980 					  "signal_socket",
981 					  NULL);
982 }
983