log.c revision 102644
1321936Shselasky/*
2321936Shselasky * Copyright (c) 1997 - 2002 Kungliga Tekniska H�gskolan
3321936Shselasky * (Royal Institute of Technology, Stockholm, Sweden).
4321936Shselasky * All rights reserved.
5321936Shselasky *
6321936Shselasky * Redistribution and use in source and binary forms, with or without
7321936Shselasky * 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
36RCSID("$Id: log.c,v 1.19 2002/05/24 15:19:21 joda 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
51kadm5_ret_t
52kadm5_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
75kadm5_ret_t
76kadm5_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
81kadm5_ret_t
82kadm5_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
90kadm5_ret_t
91kadm5_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
115kadm5_ret_t
116kadm5_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
139kadm5_ret_t
140kadm5_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
151static kadm5_ret_t
152kadm5_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
169static kadm5_ret_t
170kadm5_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
181static kadm5_ret_t
182kadm5_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
218kadm5_ret_t
219kadm5_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
261kadm5_ret_t
262kadm5_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    krb5_data_alloc (&data, len);
272    krb5_storage_read (sp, data.data, len);
273    ret = hdb_value2entry (context->context, &data, &ent);
274    krb5_data_free(&data);
275    if (ret)
276	return ret;
277    ret = context->db->store(context->context, context->db, 0, &ent);
278    hdb_free_entry (context->context, &ent);
279    return ret;
280}
281
282/*
283 * Add a `delete' operation to the log.
284 */
285
286kadm5_ret_t
287kadm5_log_delete (kadm5_server_context *context,
288		  krb5_principal princ)
289{
290    krb5_storage *sp;
291    kadm5_ret_t ret;
292    off_t off;
293    off_t len;
294    kadm5_log_context *log_context = &context->log_context;
295
296    sp = krb5_storage_emem();
297    ret = kadm5_log_preamble (context, sp, kadm_delete);
298    if (ret) {
299	krb5_storage_free(sp);
300	return ret;
301    }
302    krb5_store_int32 (sp, 0);
303    off = krb5_storage_seek (sp, 0, SEEK_CUR);
304    krb5_store_principal (sp, princ);
305    len = krb5_storage_seek (sp, 0, SEEK_CUR) - off;
306    krb5_storage_seek(sp, -(len + 4), SEEK_CUR);
307    krb5_store_int32 (sp, len);
308    krb5_storage_seek(sp, len, SEEK_CUR);
309    krb5_store_int32 (sp, len);
310    if (ret) {
311	krb5_storage_free (sp);
312	return ret;
313    }
314    ret = kadm5_log_postamble (log_context, sp);
315    if (ret) {
316	krb5_storage_free (sp);
317	return ret;
318    }
319    ret = kadm5_log_flush (log_context, sp);
320    krb5_storage_free (sp);
321    if (ret)
322	return ret;
323    ret = kadm5_log_end (context);
324    return ret;
325}
326
327/*
328 * Read a `delete' log operation from `sp' and apply it.
329 */
330
331kadm5_ret_t
332kadm5_log_replay_delete (kadm5_server_context *context,
333			 u_int32_t ver,
334			 u_int32_t len,
335			 krb5_storage *sp)
336{
337    krb5_error_code ret;
338    hdb_entry ent;
339
340    krb5_ret_principal (sp, &ent.principal);
341
342    ret = context->db->remove(context->context, context->db, &ent);
343    krb5_free_principal (context->context, ent.principal);
344    return ret;
345}
346
347/*
348 * Add a `rename' operation to the log.
349 */
350
351kadm5_ret_t
352kadm5_log_rename (kadm5_server_context *context,
353		  krb5_principal source,
354		  hdb_entry *ent)
355{
356    krb5_storage *sp;
357    kadm5_ret_t ret;
358    off_t off;
359    off_t len;
360    krb5_data value;
361    kadm5_log_context *log_context = &context->log_context;
362
363    sp = krb5_storage_emem();
364    ret = hdb_entry2value (context->context, ent, &value);
365    if (ret) {
366	krb5_storage_free(sp);
367	return ret;
368    }
369    ret = kadm5_log_preamble (context, sp, kadm_rename);
370    if (ret) {
371	krb5_storage_free(sp);
372	krb5_data_free (&value);
373	return ret;
374    }
375    krb5_store_int32 (sp, 0);
376    off = krb5_storage_seek (sp, 0, SEEK_CUR);
377    krb5_store_principal (sp, source);
378    krb5_storage_write(sp, value.data, value.length);
379    krb5_data_free (&value);
380    len = krb5_storage_seek (sp, 0, SEEK_CUR) - off;
381
382    krb5_storage_seek(sp, -(len + 4), SEEK_CUR);
383    krb5_store_int32 (sp, len);
384    krb5_storage_seek(sp, len, SEEK_CUR);
385    krb5_store_int32 (sp, len);
386    if (ret) {
387	krb5_storage_free (sp);
388	return ret;
389    }
390    ret = kadm5_log_postamble (log_context, sp);
391    if (ret) {
392	krb5_storage_free (sp);
393	return ret;
394    }
395    ret = kadm5_log_flush (log_context, sp);
396    krb5_storage_free (sp);
397    if (ret)
398	return ret;
399    ret = kadm5_log_end (context);
400    return ret;
401}
402
403/*
404 * Read a `rename' log operation from `sp' and apply it.
405 */
406
407kadm5_ret_t
408kadm5_log_replay_rename (kadm5_server_context *context,
409			 u_int32_t ver,
410			 u_int32_t len,
411			 krb5_storage *sp)
412{
413    krb5_error_code ret;
414    krb5_principal source;
415    hdb_entry source_ent, target_ent;
416    krb5_data value;
417    off_t off;
418    size_t princ_len, data_len;
419
420    off = krb5_storage_seek(sp, 0, SEEK_CUR);
421    krb5_ret_principal (sp, &source);
422    princ_len = krb5_storage_seek(sp, 0, SEEK_CUR) - off;
423    data_len = len - princ_len;
424    krb5_data_alloc (&value, data_len);
425    krb5_storage_read (sp, value.data, data_len);
426    ret = hdb_value2entry (context->context, &value, &target_ent);
427    krb5_data_free(&value);
428    if (ret) {
429	krb5_free_principal (context->context, source);
430	return ret;
431    }
432    ret = context->db->store (context->context, context->db, 0, &target_ent);
433    hdb_free_entry (context->context, &target_ent);
434    if (ret) {
435	krb5_free_principal (context->context, source);
436	return ret;
437    }
438    source_ent.principal = source;
439    ret = context->db->remove (context->context, context->db, &source_ent);
440    krb5_free_principal (context->context, source);
441    return ret;
442}
443
444
445/*
446 * Add a `modify' operation to the log.
447 */
448
449kadm5_ret_t
450kadm5_log_modify (kadm5_server_context *context,
451		  hdb_entry *ent,
452		  u_int32_t mask)
453{
454    krb5_storage *sp;
455    kadm5_ret_t ret;
456    krb5_data value;
457    u_int32_t len;
458    kadm5_log_context *log_context = &context->log_context;
459
460    sp = krb5_storage_emem();
461    ret = hdb_entry2value (context->context, ent, &value);
462    if (ret) {
463	krb5_storage_free(sp);
464	return ret;
465    }
466    ret = kadm5_log_preamble (context, sp, kadm_modify);
467    if (ret) {
468	krb5_data_free (&value);
469	krb5_storage_free(sp);
470	return ret;
471    }
472    len = value.length + 4;
473    krb5_store_int32 (sp, len);
474    krb5_store_int32 (sp, mask);
475    krb5_storage_write (sp, value.data, value.length);
476    krb5_data_free (&value);
477    krb5_store_int32 (sp, len);
478    if (ret) {
479	krb5_storage_free (sp);
480	return ret;
481    }
482    ret = kadm5_log_postamble (log_context, sp);
483    if (ret) {
484	krb5_storage_free (sp);
485	return ret;
486    }
487    ret = kadm5_log_flush (log_context, sp);
488    krb5_storage_free (sp);
489    if (ret)
490	return ret;
491    ret = kadm5_log_end (context);
492    return ret;
493}
494
495/*
496 * Read a `modify' log operation from `sp' and apply it.
497 */
498
499kadm5_ret_t
500kadm5_log_replay_modify (kadm5_server_context *context,
501			 u_int32_t ver,
502			 u_int32_t len,
503			 krb5_storage *sp)
504{
505    krb5_error_code ret;
506    int32_t mask;
507    krb5_data value;
508    hdb_entry ent, log_ent;
509
510    krb5_ret_int32 (sp, &mask);
511    len -= 4;
512    krb5_data_alloc (&value, len);
513    krb5_storage_read (sp, value.data, len);
514    ret = hdb_value2entry (context->context, &value, &log_ent);
515    krb5_data_free(&value);
516    if (ret)
517	return ret;
518    ent.principal = log_ent.principal;
519    log_ent.principal = NULL;
520    ret = context->db->fetch(context->context, context->db,
521			     HDB_F_DECRYPT, &ent);
522    if (ret)
523	return ret;
524    if (mask & KADM5_PRINC_EXPIRE_TIME) {
525	if (log_ent.valid_end == NULL) {
526	    ent.valid_end = NULL;
527	} else {
528	    if (ent.valid_end == NULL)
529		ent.valid_end = malloc(sizeof(*ent.valid_end));
530	    *ent.valid_end = *log_ent.valid_end;
531	}
532    }
533    if (mask & KADM5_PW_EXPIRATION) {
534	if (log_ent.pw_end == NULL) {
535	    ent.pw_end = NULL;
536	} else {
537	    if (ent.pw_end == NULL)
538		ent.pw_end = malloc(sizeof(*ent.pw_end));
539	    *ent.pw_end = *log_ent.pw_end;
540	}
541    }
542    if (mask & KADM5_LAST_PWD_CHANGE) {
543	abort ();		/* XXX */
544    }
545    if (mask & KADM5_ATTRIBUTES) {
546	ent.flags = log_ent.flags;
547    }
548    if (mask & KADM5_MAX_LIFE) {
549	if (log_ent.max_life == NULL) {
550	    ent.max_life = NULL;
551	} else {
552	    if (ent.max_life == NULL)
553		ent.max_life = malloc (sizeof(*ent.max_life));
554	    *ent.max_life = *log_ent.max_life;
555	}
556    }
557    if ((mask & KADM5_MOD_TIME) && (mask & KADM5_MOD_NAME)) {
558	if (ent.modified_by == NULL) {
559	    ent.modified_by = malloc(sizeof(*ent.modified_by));
560	} else
561	    free_Event(ent.modified_by);
562	copy_Event(log_ent.modified_by, ent.modified_by);
563    }
564    if (mask & KADM5_KVNO) {
565	ent.kvno = log_ent.kvno;
566    }
567    if (mask & KADM5_MKVNO) {
568	abort ();		/* XXX */
569    }
570    if (mask & KADM5_AUX_ATTRIBUTES) {
571	abort ();		/* XXX */
572    }
573    if (mask & KADM5_POLICY) {
574	abort ();		/* XXX */
575    }
576    if (mask & KADM5_POLICY_CLR) {
577	abort ();		/* XXX */
578    }
579    if (mask & KADM5_MAX_RLIFE) {
580	if (log_ent.max_renew == NULL) {
581	    ent.max_renew = NULL;
582	} else {
583	    if (ent.max_renew == NULL)
584		ent.max_renew = malloc (sizeof(*ent.max_renew));
585	    *ent.max_renew = *log_ent.max_renew;
586	}
587    }
588    if (mask & KADM5_LAST_SUCCESS) {
589	abort ();		/* XXX */
590    }
591    if (mask & KADM5_LAST_FAILED) {
592	abort ();		/* XXX */
593    }
594    if (mask & KADM5_FAIL_AUTH_COUNT) {
595	abort ();		/* XXX */
596    }
597    if (mask & KADM5_KEY_DATA) {
598	size_t len;
599	int i;
600
601	for (i = 0; i < ent.keys.len; ++i)
602	    free_Key(&ent.keys.val[i]);
603	free (ent.keys.val);
604
605	len = log_ent.keys.len;
606
607	ent.keys.len = len;
608	ent.keys.val = malloc(len * sizeof(*ent.keys.val));
609	for (i = 0; i < ent.keys.len; ++i)
610	    copy_Key(&log_ent.keys.val[i],
611		     &ent.keys.val[i]);
612    }
613    ret = context->db->store(context->context, context->db,
614			     HDB_F_REPLACE, &ent);
615    hdb_free_entry (context->context, &ent);
616    hdb_free_entry (context->context, &log_ent);
617    return ret;
618}
619
620/*
621 * Add a `nop' operation to the log.
622 */
623
624kadm5_ret_t
625kadm5_log_nop (kadm5_server_context *context)
626{
627    krb5_storage *sp;
628    kadm5_ret_t ret;
629    kadm5_log_context *log_context = &context->log_context;
630
631    sp = krb5_storage_emem();
632    ret = kadm5_log_preamble (context, sp, kadm_nop);
633    if (ret) {
634	krb5_storage_free (sp);
635	return ret;
636    }
637    krb5_store_int32 (sp, 0);
638    krb5_store_int32 (sp, 0);
639    ret = kadm5_log_postamble (log_context, sp);
640    if (ret) {
641	krb5_storage_free (sp);
642	return ret;
643    }
644    ret = kadm5_log_flush (log_context, sp);
645    krb5_storage_free (sp);
646    if (ret)
647	return ret;
648    ret = kadm5_log_end (context);
649    return ret;
650}
651
652/*
653 * Read a `nop' log operation from `sp' and apply it.
654 */
655
656kadm5_ret_t
657kadm5_log_replay_nop (kadm5_server_context *context,
658		      u_int32_t ver,
659		      u_int32_t len,
660		      krb5_storage *sp)
661{
662    return 0;
663}
664
665/*
666 * Call `func' for each log record in the log in `context'
667 */
668
669kadm5_ret_t
670kadm5_log_foreach (kadm5_server_context *context,
671		   void (*func)(kadm5_server_context *server_context,
672				u_int32_t ver,
673				time_t timestamp,
674				enum kadm_ops op,
675				u_int32_t len,
676				krb5_storage *sp))
677{
678    int fd = context->log_context.log_fd;
679    krb5_storage *sp;
680
681    lseek (fd, 0, SEEK_SET);
682    sp = krb5_storage_from_fd (fd);
683    for (;;) {
684	int32_t ver, timestamp, op, len;
685
686	if(krb5_ret_int32 (sp, &ver) != 0)
687	    break;
688	krb5_ret_int32 (sp, &timestamp);
689	krb5_ret_int32 (sp, &op);
690	krb5_ret_int32 (sp, &len);
691	(*func)(context, ver, timestamp, op, len, sp);
692	krb5_storage_seek(sp, 8, SEEK_CUR);
693    }
694    return 0;
695}
696
697/*
698 * Go to end of log.
699 */
700
701krb5_storage *
702kadm5_log_goto_end (int fd)
703{
704    krb5_storage *sp;
705
706    sp = krb5_storage_from_fd (fd);
707    krb5_storage_seek(sp, 0, SEEK_END);
708    return sp;
709}
710
711/*
712 * Return previous log entry.
713 */
714
715kadm5_ret_t
716kadm5_log_previous (krb5_storage *sp,
717		    u_int32_t *ver,
718		    time_t *timestamp,
719		    enum kadm_ops *op,
720		    u_int32_t *len)
721{
722    off_t off;
723    int32_t tmp;
724
725    krb5_storage_seek(sp, -8, SEEK_CUR);
726    krb5_ret_int32 (sp, &tmp);
727    *len = tmp;
728    krb5_ret_int32 (sp, &tmp);
729    *ver = tmp;
730    off = 24 + *len;
731    krb5_storage_seek(sp, -off, SEEK_CUR);
732    krb5_ret_int32 (sp, &tmp);
733    assert(tmp == *ver);
734    krb5_ret_int32 (sp, &tmp);
735    *timestamp = tmp;
736    krb5_ret_int32 (sp, &tmp);
737    *op = tmp;
738    krb5_ret_int32 (sp, &tmp);
739    assert(tmp == *len);
740    return 0;
741}
742
743/*
744 * Replay a record from the log
745 */
746
747kadm5_ret_t
748kadm5_log_replay (kadm5_server_context *context,
749		  enum kadm_ops op,
750		  u_int32_t ver,
751		  u_int32_t len,
752		  krb5_storage *sp)
753{
754    switch (op) {
755    case kadm_create :
756	return kadm5_log_replay_create (context, ver, len, sp);
757    case kadm_delete :
758	return kadm5_log_replay_delete (context, ver, len, sp);
759    case kadm_rename :
760	return kadm5_log_replay_rename (context, ver, len, sp);
761    case kadm_modify :
762	return kadm5_log_replay_modify (context, ver, len, sp);
763    case kadm_nop :
764	return kadm5_log_replay_nop (context, ver, len, sp);
765    default :
766	return KADM5_FAILURE;
767    }
768}
769
770/*
771 * truncate the log - i.e. create an empty file with just (nop vno + 2)
772 */
773
774kadm5_ret_t
775kadm5_log_truncate (kadm5_server_context *server_context)
776{
777    kadm5_ret_t ret;
778    u_int32_t vno;
779
780    ret = kadm5_log_init (server_context);
781    if (ret)
782	return ret;
783
784    ret = kadm5_log_get_version (server_context, &vno);
785    if (ret)
786	return ret;
787
788    ret = kadm5_log_reinit (server_context);
789    if (ret)
790	return ret;
791
792    ret = kadm5_log_set_version (server_context, vno + 1);
793    if (ret)
794	return ret;
795
796    ret = kadm5_log_nop (server_context);
797    if (ret)
798	return ret;
799
800    ret = kadm5_log_end (server_context);
801    if (ret)
802	return ret;
803    return 0;
804
805}
806