xref: /freebsd/crypto/heimdal/lib/kadm5/log.c (revision 1669d8afc64812c8d2d1d147ae1fd42ff441e1b1)
1 /*
2  * Copyright (c) 1997 - 2003 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 
36 RCSID("$Id: log.c,v 1.20 2003/04/16 17:56:55 lha Exp $");
37 
38 /*
39  * A log record consists of:
40  *
41  * version number		4 bytes
42  * time in seconds		4 bytes
43  * operation (enum kadm_ops)	4 bytes
44  * length of record		4 bytes
45  * data...			n bytes
46  * length of record		4 bytes
47  * version number		4 bytes
48  *
49  */
50 
51 kadm5_ret_t
52 kadm5_log_get_version_fd (int fd,
53 			  u_int32_t *ver)
54 {
55     int ret;
56     krb5_storage *sp;
57     int32_t old_version;
58 
59     ret = lseek (fd, 0, SEEK_END);
60     if(ret < 0)
61 	return errno;
62     if(ret == 0) {
63 	*ver = 0;
64 	return 0;
65     }
66     sp = krb5_storage_from_fd (fd);
67     krb5_storage_seek(sp, -4, SEEK_CUR);
68     krb5_ret_int32 (sp, &old_version);
69     *ver = old_version;
70     krb5_storage_free(sp);
71     lseek (fd, 0, SEEK_END);
72     return 0;
73 }
74 
75 kadm5_ret_t
76 kadm5_log_get_version (kadm5_server_context *context, u_int32_t *ver)
77 {
78     return kadm5_log_get_version_fd (context->log_context.log_fd, ver);
79 }
80 
81 kadm5_ret_t
82 kadm5_log_set_version (kadm5_server_context *context, u_int32_t vno)
83 {
84     kadm5_log_context *log_context = &context->log_context;
85 
86     log_context->version = vno;
87     return 0;
88 }
89 
90 kadm5_ret_t
91 kadm5_log_init (kadm5_server_context *context)
92 {
93     int fd;
94     kadm5_ret_t ret;
95     kadm5_log_context *log_context = &context->log_context;
96 
97     if (log_context->log_fd != -1)
98 	return 0;
99     fd = open (log_context->log_file, O_RDWR | O_CREAT, 0600);
100     if (fd < 0)
101 	return errno;
102     if (flock (fd, LOCK_EX) < 0) {
103 	close (fd);
104 	return errno;
105     }
106 
107     ret = kadm5_log_get_version_fd (fd, &log_context->version);
108     if (ret)
109 	return ret;
110 
111     log_context->log_fd  = fd;
112     return 0;
113 }
114 
115 kadm5_ret_t
116 kadm5_log_reinit (kadm5_server_context *context)
117 {
118     int fd;
119     kadm5_log_context *log_context = &context->log_context;
120 
121     if (log_context->log_fd != -1) {
122 	close (log_context->log_fd);
123 	log_context->log_fd = -1;
124     }
125     fd = open (log_context->log_file, O_RDWR | O_CREAT | O_TRUNC, 0600);
126     if (fd < 0)
127 	return errno;
128     if (flock (fd, LOCK_EX) < 0) {
129 	close (fd);
130 	return errno;
131     }
132 
133     log_context->version = 0;
134     log_context->log_fd  = fd;
135     return 0;
136 }
137 
138 
139 kadm5_ret_t
140 kadm5_log_end (kadm5_server_context *context)
141 {
142     kadm5_log_context *log_context = &context->log_context;
143     int fd = log_context->log_fd;
144 
145     flock (fd, LOCK_UN);
146     close(fd);
147     log_context->log_fd = -1;
148     return 0;
149 }
150 
151 static kadm5_ret_t
152 kadm5_log_preamble (kadm5_server_context *context,
153 		    krb5_storage *sp,
154 		    enum kadm_ops op)
155 {
156     kadm5_log_context *log_context = &context->log_context;
157     kadm5_ret_t kadm_ret;
158 
159     kadm_ret = kadm5_log_init (context);
160     if (kadm_ret)
161 	return kadm_ret;
162 
163     krb5_store_int32 (sp, ++log_context->version);
164     krb5_store_int32 (sp, time(NULL));
165     krb5_store_int32 (sp, op);
166     return 0;
167 }
168 
169 static kadm5_ret_t
170 kadm5_log_postamble (kadm5_log_context *context,
171 		     krb5_storage *sp)
172 {
173     krb5_store_int32 (sp, context->version);
174     return 0;
175 }
176 
177 /*
178  * flush the log record in `sp'.
179  */
180 
181 static kadm5_ret_t
182 kadm5_log_flush (kadm5_log_context *log_context,
183 		 krb5_storage *sp)
184 {
185     krb5_data data;
186     size_t len;
187     int ret;
188 
189     krb5_storage_to_data(sp, &data);
190     len = data.length;
191     ret = write (log_context->log_fd, data.data, len);
192     if (ret != len) {
193 	krb5_data_free(&data);
194 	return errno;
195     }
196     if (fsync (log_context->log_fd) < 0) {
197 	krb5_data_free(&data);
198 	return errno;
199     }
200     /*
201      * Try to send a signal to any running `ipropd-master'
202      */
203     sendto (log_context->socket_fd,
204 	    (void *)&log_context->version,
205 	    sizeof(log_context->version),
206 	    0,
207 	    (struct sockaddr *)&log_context->socket_name,
208 	    sizeof(log_context->socket_name));
209 
210     krb5_data_free(&data);
211     return 0;
212 }
213 
214 /*
215  * Add a `create' operation to the log.
216  */
217 
218 kadm5_ret_t
219 kadm5_log_create (kadm5_server_context *context,
220 		  hdb_entry *ent)
221 {
222     krb5_storage *sp;
223     kadm5_ret_t ret;
224     krb5_data value;
225     kadm5_log_context *log_context = &context->log_context;
226 
227     sp = krb5_storage_emem();
228     ret = hdb_entry2value (context->context, ent, &value);
229     if (ret) {
230 	krb5_storage_free(sp);
231 	return ret;
232     }
233     ret = kadm5_log_preamble (context, sp, kadm_create);
234     if (ret) {
235 	krb5_data_free (&value);
236 	krb5_storage_free(sp);
237 	return ret;
238     }
239     krb5_store_int32 (sp, value.length);
240     krb5_storage_write(sp, value.data, value.length);
241     krb5_store_int32 (sp, value.length);
242     krb5_data_free (&value);
243     ret = kadm5_log_postamble (log_context, sp);
244     if (ret) {
245 	krb5_storage_free (sp);
246 	return ret;
247     }
248     ret = kadm5_log_flush (log_context, sp);
249     krb5_storage_free (sp);
250     if (ret)
251 	return ret;
252     ret = kadm5_log_end (context);
253     return ret;
254 }
255 
256 /*
257  * Read the data of a create log record from `sp' and change the
258  * database.
259  */
260 
261 kadm5_ret_t
262 kadm5_log_replay_create (kadm5_server_context *context,
263 			 u_int32_t ver,
264 			 u_int32_t len,
265 			 krb5_storage *sp)
266 {
267     krb5_error_code ret;
268     krb5_data data;
269     hdb_entry ent;
270 
271     ret = krb5_data_alloc (&data, len);
272     if (ret)
273 	return ret;
274     krb5_storage_read (sp, data.data, len);
275     ret = hdb_value2entry (context->context, &data, &ent);
276     krb5_data_free(&data);
277     if (ret)
278 	return ret;
279     ret = context->db->store(context->context, context->db, 0, &ent);
280     hdb_free_entry (context->context, &ent);
281     return ret;
282 }
283 
284 /*
285  * Add a `delete' operation to the log.
286  */
287 
288 kadm5_ret_t
289 kadm5_log_delete (kadm5_server_context *context,
290 		  krb5_principal princ)
291 {
292     krb5_storage *sp;
293     kadm5_ret_t ret;
294     off_t off;
295     off_t len;
296     kadm5_log_context *log_context = &context->log_context;
297 
298     sp = krb5_storage_emem();
299     ret = kadm5_log_preamble (context, sp, kadm_delete);
300     if (ret) {
301 	krb5_storage_free(sp);
302 	return ret;
303     }
304     krb5_store_int32 (sp, 0);
305     off = krb5_storage_seek (sp, 0, SEEK_CUR);
306     krb5_store_principal (sp, princ);
307     len = krb5_storage_seek (sp, 0, SEEK_CUR) - off;
308     krb5_storage_seek(sp, -(len + 4), SEEK_CUR);
309     krb5_store_int32 (sp, len);
310     krb5_storage_seek(sp, len, SEEK_CUR);
311     krb5_store_int32 (sp, len);
312     if (ret) {
313 	krb5_storage_free (sp);
314 	return ret;
315     }
316     ret = kadm5_log_postamble (log_context, sp);
317     if (ret) {
318 	krb5_storage_free (sp);
319 	return ret;
320     }
321     ret = kadm5_log_flush (log_context, sp);
322     krb5_storage_free (sp);
323     if (ret)
324 	return ret;
325     ret = kadm5_log_end (context);
326     return ret;
327 }
328 
329 /*
330  * Read a `delete' log operation from `sp' and apply it.
331  */
332 
333 kadm5_ret_t
334 kadm5_log_replay_delete (kadm5_server_context *context,
335 			 u_int32_t ver,
336 			 u_int32_t len,
337 			 krb5_storage *sp)
338 {
339     krb5_error_code ret;
340     hdb_entry ent;
341 
342     krb5_ret_principal (sp, &ent.principal);
343 
344     ret = context->db->remove(context->context, context->db, &ent);
345     krb5_free_principal (context->context, ent.principal);
346     return ret;
347 }
348 
349 /*
350  * Add a `rename' operation to the log.
351  */
352 
353 kadm5_ret_t
354 kadm5_log_rename (kadm5_server_context *context,
355 		  krb5_principal source,
356 		  hdb_entry *ent)
357 {
358     krb5_storage *sp;
359     kadm5_ret_t ret;
360     off_t off;
361     off_t len;
362     krb5_data value;
363     kadm5_log_context *log_context = &context->log_context;
364 
365     sp = krb5_storage_emem();
366     ret = hdb_entry2value (context->context, ent, &value);
367     if (ret) {
368 	krb5_storage_free(sp);
369 	return ret;
370     }
371     ret = kadm5_log_preamble (context, sp, kadm_rename);
372     if (ret) {
373 	krb5_storage_free(sp);
374 	krb5_data_free (&value);
375 	return ret;
376     }
377     krb5_store_int32 (sp, 0);
378     off = krb5_storage_seek (sp, 0, SEEK_CUR);
379     krb5_store_principal (sp, source);
380     krb5_storage_write(sp, value.data, value.length);
381     krb5_data_free (&value);
382     len = krb5_storage_seek (sp, 0, SEEK_CUR) - off;
383 
384     krb5_storage_seek(sp, -(len + 4), SEEK_CUR);
385     krb5_store_int32 (sp, len);
386     krb5_storage_seek(sp, len, SEEK_CUR);
387     krb5_store_int32 (sp, len);
388     if (ret) {
389 	krb5_storage_free (sp);
390 	return ret;
391     }
392     ret = kadm5_log_postamble (log_context, sp);
393     if (ret) {
394 	krb5_storage_free (sp);
395 	return ret;
396     }
397     ret = kadm5_log_flush (log_context, sp);
398     krb5_storage_free (sp);
399     if (ret)
400 	return ret;
401     ret = kadm5_log_end (context);
402     return ret;
403 }
404 
405 /*
406  * Read a `rename' log operation from `sp' and apply it.
407  */
408 
409 kadm5_ret_t
410 kadm5_log_replay_rename (kadm5_server_context *context,
411 			 u_int32_t ver,
412 			 u_int32_t len,
413 			 krb5_storage *sp)
414 {
415     krb5_error_code ret;
416     krb5_principal source;
417     hdb_entry source_ent, target_ent;
418     krb5_data value;
419     off_t off;
420     size_t princ_len, data_len;
421 
422     off = krb5_storage_seek(sp, 0, SEEK_CUR);
423     krb5_ret_principal (sp, &source);
424     princ_len = krb5_storage_seek(sp, 0, SEEK_CUR) - off;
425     data_len = len - princ_len;
426     ret = krb5_data_alloc (&value, data_len);
427     if (ret) {
428 	krb5_free_principal (context->context, source);
429 	return ret;
430     }
431     krb5_storage_read (sp, value.data, data_len);
432     ret = hdb_value2entry (context->context, &value, &target_ent);
433     krb5_data_free(&value);
434     if (ret) {
435 	krb5_free_principal (context->context, source);
436 	return ret;
437     }
438     ret = context->db->store (context->context, context->db, 0, &target_ent);
439     hdb_free_entry (context->context, &target_ent);
440     if (ret) {
441 	krb5_free_principal (context->context, source);
442 	return ret;
443     }
444     source_ent.principal = source;
445     ret = context->db->remove (context->context, context->db, &source_ent);
446     krb5_free_principal (context->context, source);
447     return ret;
448 }
449 
450 
451 /*
452  * Add a `modify' operation to the log.
453  */
454 
455 kadm5_ret_t
456 kadm5_log_modify (kadm5_server_context *context,
457 		  hdb_entry *ent,
458 		  u_int32_t mask)
459 {
460     krb5_storage *sp;
461     kadm5_ret_t ret;
462     krb5_data value;
463     u_int32_t len;
464     kadm5_log_context *log_context = &context->log_context;
465 
466     sp = krb5_storage_emem();
467     ret = hdb_entry2value (context->context, ent, &value);
468     if (ret) {
469 	krb5_storage_free(sp);
470 	return ret;
471     }
472     ret = kadm5_log_preamble (context, sp, kadm_modify);
473     if (ret) {
474 	krb5_data_free (&value);
475 	krb5_storage_free(sp);
476 	return ret;
477     }
478     len = value.length + 4;
479     krb5_store_int32 (sp, len);
480     krb5_store_int32 (sp, mask);
481     krb5_storage_write (sp, value.data, value.length);
482     krb5_data_free (&value);
483     krb5_store_int32 (sp, len);
484     if (ret) {
485 	krb5_storage_free (sp);
486 	return ret;
487     }
488     ret = kadm5_log_postamble (log_context, sp);
489     if (ret) {
490 	krb5_storage_free (sp);
491 	return ret;
492     }
493     ret = kadm5_log_flush (log_context, sp);
494     krb5_storage_free (sp);
495     if (ret)
496 	return ret;
497     ret = kadm5_log_end (context);
498     return ret;
499 }
500 
501 /*
502  * Read a `modify' log operation from `sp' and apply it.
503  */
504 
505 kadm5_ret_t
506 kadm5_log_replay_modify (kadm5_server_context *context,
507 			 u_int32_t ver,
508 			 u_int32_t len,
509 			 krb5_storage *sp)
510 {
511     krb5_error_code ret;
512     int32_t mask;
513     krb5_data value;
514     hdb_entry ent, log_ent;
515 
516     krb5_ret_int32 (sp, &mask);
517     len -= 4;
518     ret = krb5_data_alloc (&value, len);
519     if (ret)
520 	return ret;
521     krb5_storage_read (sp, value.data, len);
522     ret = hdb_value2entry (context->context, &value, &log_ent);
523     krb5_data_free(&value);
524     if (ret)
525 	return ret;
526     ent.principal = log_ent.principal;
527     log_ent.principal = NULL;
528     ret = context->db->fetch(context->context, context->db,
529 			     HDB_F_DECRYPT, &ent);
530     if (ret)
531 	return ret;
532     if (mask & KADM5_PRINC_EXPIRE_TIME) {
533 	if (log_ent.valid_end == NULL) {
534 	    ent.valid_end = NULL;
535 	} else {
536 	    if (ent.valid_end == NULL)
537 		ent.valid_end = malloc(sizeof(*ent.valid_end));
538 	    *ent.valid_end = *log_ent.valid_end;
539 	}
540     }
541     if (mask & KADM5_PW_EXPIRATION) {
542 	if (log_ent.pw_end == NULL) {
543 	    ent.pw_end = NULL;
544 	} else {
545 	    if (ent.pw_end == NULL)
546 		ent.pw_end = malloc(sizeof(*ent.pw_end));
547 	    *ent.pw_end = *log_ent.pw_end;
548 	}
549     }
550     if (mask & KADM5_LAST_PWD_CHANGE) {
551 	abort ();		/* XXX */
552     }
553     if (mask & KADM5_ATTRIBUTES) {
554 	ent.flags = log_ent.flags;
555     }
556     if (mask & KADM5_MAX_LIFE) {
557 	if (log_ent.max_life == NULL) {
558 	    ent.max_life = NULL;
559 	} else {
560 	    if (ent.max_life == NULL)
561 		ent.max_life = malloc (sizeof(*ent.max_life));
562 	    *ent.max_life = *log_ent.max_life;
563 	}
564     }
565     if ((mask & KADM5_MOD_TIME) && (mask & KADM5_MOD_NAME)) {
566 	if (ent.modified_by == NULL) {
567 	    ent.modified_by = malloc(sizeof(*ent.modified_by));
568 	} else
569 	    free_Event(ent.modified_by);
570 	copy_Event(log_ent.modified_by, ent.modified_by);
571     }
572     if (mask & KADM5_KVNO) {
573 	ent.kvno = log_ent.kvno;
574     }
575     if (mask & KADM5_MKVNO) {
576 	abort ();		/* XXX */
577     }
578     if (mask & KADM5_AUX_ATTRIBUTES) {
579 	abort ();		/* XXX */
580     }
581     if (mask & KADM5_POLICY) {
582 	abort ();		/* XXX */
583     }
584     if (mask & KADM5_POLICY_CLR) {
585 	abort ();		/* XXX */
586     }
587     if (mask & KADM5_MAX_RLIFE) {
588 	if (log_ent.max_renew == NULL) {
589 	    ent.max_renew = NULL;
590 	} else {
591 	    if (ent.max_renew == NULL)
592 		ent.max_renew = malloc (sizeof(*ent.max_renew));
593 	    *ent.max_renew = *log_ent.max_renew;
594 	}
595     }
596     if (mask & KADM5_LAST_SUCCESS) {
597 	abort ();		/* XXX */
598     }
599     if (mask & KADM5_LAST_FAILED) {
600 	abort ();		/* XXX */
601     }
602     if (mask & KADM5_FAIL_AUTH_COUNT) {
603 	abort ();		/* XXX */
604     }
605     if (mask & KADM5_KEY_DATA) {
606 	size_t len;
607 	int i;
608 
609 	for (i = 0; i < ent.keys.len; ++i)
610 	    free_Key(&ent.keys.val[i]);
611 	free (ent.keys.val);
612 
613 	len = log_ent.keys.len;
614 
615 	ent.keys.len = len;
616 	ent.keys.val = malloc(len * sizeof(*ent.keys.val));
617 	for (i = 0; i < ent.keys.len; ++i)
618 	    copy_Key(&log_ent.keys.val[i],
619 		     &ent.keys.val[i]);
620     }
621     ret = context->db->store(context->context, context->db,
622 			     HDB_F_REPLACE, &ent);
623     hdb_free_entry (context->context, &ent);
624     hdb_free_entry (context->context, &log_ent);
625     return ret;
626 }
627 
628 /*
629  * Add a `nop' operation to the log.
630  */
631 
632 kadm5_ret_t
633 kadm5_log_nop (kadm5_server_context *context)
634 {
635     krb5_storage *sp;
636     kadm5_ret_t ret;
637     kadm5_log_context *log_context = &context->log_context;
638 
639     sp = krb5_storage_emem();
640     ret = kadm5_log_preamble (context, sp, kadm_nop);
641     if (ret) {
642 	krb5_storage_free (sp);
643 	return ret;
644     }
645     krb5_store_int32 (sp, 0);
646     krb5_store_int32 (sp, 0);
647     ret = kadm5_log_postamble (log_context, sp);
648     if (ret) {
649 	krb5_storage_free (sp);
650 	return ret;
651     }
652     ret = kadm5_log_flush (log_context, sp);
653     krb5_storage_free (sp);
654     if (ret)
655 	return ret;
656     ret = kadm5_log_end (context);
657     return ret;
658 }
659 
660 /*
661  * Read a `nop' log operation from `sp' and apply it.
662  */
663 
664 kadm5_ret_t
665 kadm5_log_replay_nop (kadm5_server_context *context,
666 		      u_int32_t ver,
667 		      u_int32_t len,
668 		      krb5_storage *sp)
669 {
670     return 0;
671 }
672 
673 /*
674  * Call `func' for each log record in the log in `context'
675  */
676 
677 kadm5_ret_t
678 kadm5_log_foreach (kadm5_server_context *context,
679 		   void (*func)(kadm5_server_context *server_context,
680 				u_int32_t ver,
681 				time_t timestamp,
682 				enum kadm_ops op,
683 				u_int32_t len,
684 				krb5_storage *sp))
685 {
686     int fd = context->log_context.log_fd;
687     krb5_storage *sp;
688 
689     lseek (fd, 0, SEEK_SET);
690     sp = krb5_storage_from_fd (fd);
691     for (;;) {
692 	int32_t ver, timestamp, op, len;
693 
694 	if(krb5_ret_int32 (sp, &ver) != 0)
695 	    break;
696 	krb5_ret_int32 (sp, &timestamp);
697 	krb5_ret_int32 (sp, &op);
698 	krb5_ret_int32 (sp, &len);
699 	(*func)(context, ver, timestamp, op, len, sp);
700 	krb5_storage_seek(sp, 8, SEEK_CUR);
701     }
702     return 0;
703 }
704 
705 /*
706  * Go to end of log.
707  */
708 
709 krb5_storage *
710 kadm5_log_goto_end (int fd)
711 {
712     krb5_storage *sp;
713 
714     sp = krb5_storage_from_fd (fd);
715     krb5_storage_seek(sp, 0, SEEK_END);
716     return sp;
717 }
718 
719 /*
720  * Return previous log entry.
721  */
722 
723 kadm5_ret_t
724 kadm5_log_previous (krb5_storage *sp,
725 		    u_int32_t *ver,
726 		    time_t *timestamp,
727 		    enum kadm_ops *op,
728 		    u_int32_t *len)
729 {
730     off_t off;
731     int32_t tmp;
732 
733     krb5_storage_seek(sp, -8, SEEK_CUR);
734     krb5_ret_int32 (sp, &tmp);
735     *len = tmp;
736     krb5_ret_int32 (sp, &tmp);
737     *ver = tmp;
738     off = 24 + *len;
739     krb5_storage_seek(sp, -off, SEEK_CUR);
740     krb5_ret_int32 (sp, &tmp);
741     assert(tmp == *ver);
742     krb5_ret_int32 (sp, &tmp);
743     *timestamp = tmp;
744     krb5_ret_int32 (sp, &tmp);
745     *op = tmp;
746     krb5_ret_int32 (sp, &tmp);
747     assert(tmp == *len);
748     return 0;
749 }
750 
751 /*
752  * Replay a record from the log
753  */
754 
755 kadm5_ret_t
756 kadm5_log_replay (kadm5_server_context *context,
757 		  enum kadm_ops op,
758 		  u_int32_t ver,
759 		  u_int32_t len,
760 		  krb5_storage *sp)
761 {
762     switch (op) {
763     case kadm_create :
764 	return kadm5_log_replay_create (context, ver, len, sp);
765     case kadm_delete :
766 	return kadm5_log_replay_delete (context, ver, len, sp);
767     case kadm_rename :
768 	return kadm5_log_replay_rename (context, ver, len, sp);
769     case kadm_modify :
770 	return kadm5_log_replay_modify (context, ver, len, sp);
771     case kadm_nop :
772 	return kadm5_log_replay_nop (context, ver, len, sp);
773     default :
774 	return KADM5_FAILURE;
775     }
776 }
777 
778 /*
779  * truncate the log - i.e. create an empty file with just (nop vno + 2)
780  */
781 
782 kadm5_ret_t
783 kadm5_log_truncate (kadm5_server_context *server_context)
784 {
785     kadm5_ret_t ret;
786     u_int32_t vno;
787 
788     ret = kadm5_log_init (server_context);
789     if (ret)
790 	return ret;
791 
792     ret = kadm5_log_get_version (server_context, &vno);
793     if (ret)
794 	return ret;
795 
796     ret = kadm5_log_reinit (server_context);
797     if (ret)
798 	return ret;
799 
800     ret = kadm5_log_set_version (server_context, vno + 1);
801     if (ret)
802 	return ret;
803 
804     ret = kadm5_log_nop (server_context);
805     if (ret)
806 	return ret;
807 
808     ret = kadm5_log_end (server_context);
809     if (ret)
810 	return ret;
811     return 0;
812 
813 }
814