log.c revision 120945
1157016Sdes/*
2157016Sdes * Copyright (c) 1997 - 2003 Kungliga Tekniska H�gskolan
3157016Sdes * (Royal Institute of Technology, Stockholm, Sweden).
4157016Sdes * All rights reserved.
5157016Sdes *
6157016Sdes * Redistribution and use in source and binary forms, with or without
7157016Sdes * modification, are permitted provided that the following conditions
8157016Sdes * are met:
9157016Sdes *
10157016Sdes * 1. Redistributions of source code must retain the above copyright
11157016Sdes *    notice, this list of conditions and the following disclaimer.
12157016Sdes *
13157016Sdes * 2. Redistributions in binary form must reproduce the above copyright
14157016Sdes *    notice, this list of conditions and the following disclaimer in the
15157016Sdes *    documentation and/or other materials provided with the distribution.
16157016Sdes *
17157016Sdes * 3. Neither the name of the Institute nor the names of its contributors
18157016Sdes *    may be used to endorse or promote products derived from this software
19162852Sdes *    without specific prior written permission.
20162852Sdes *
21162852Sdes * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
22162852Sdes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23162852Sdes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24162852Sdes * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
25162852Sdes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26162852Sdes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27162852Sdes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28162852Sdes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29162852Sdes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30162852Sdes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31162852Sdes * SUCH DAMAGE.
32181111Sdes */
33157016Sdes
34157016Sdes#include "kadm5_locl.h"
35295367Sdes
36162852SdesRCSID("$Id: log.c,v 1.20 2003/04/16 17:56:55 lha Exp $");
37295367Sdes
38157016Sdes/*
39157016Sdes * A log record consists of:
40157016Sdes *
41157016Sdes * version number		4 bytes
42157016Sdes * time in seconds		4 bytes
43157016Sdes * operation (enum kadm_ops)	4 bytes
44157016Sdes * length of record		4 bytes
45162852Sdes * data...			n bytes
46157016Sdes * length of record		4 bytes
47157016Sdes * version number		4 bytes
48157016Sdes *
49157016Sdes */
50157016Sdes
51157016Sdeskadm5_ret_t
52157016Sdeskadm5_log_get_version_fd (int fd,
53157016Sdes			  u_int32_t *ver)
54157016Sdes{
55157016Sdes    int ret;
56157016Sdes    krb5_storage *sp;
57157016Sdes    int32_t old_version;
58157016Sdes
59157016Sdes    ret = lseek (fd, 0, SEEK_END);
60157016Sdes    if(ret < 0)
61157016Sdes	return errno;
62157016Sdes    if(ret == 0) {
63157016Sdes	*ver = 0;
64157016Sdes	return 0;
65157016Sdes    }
66157016Sdes    sp = krb5_storage_from_fd (fd);
67157016Sdes    krb5_storage_seek(sp, -4, SEEK_CUR);
68157016Sdes    krb5_ret_int32 (sp, &old_version);
69157016Sdes    *ver = old_version;
70157016Sdes    krb5_storage_free(sp);
71157016Sdes    lseek (fd, 0, SEEK_END);
72157016Sdes    return 0;
73157016Sdes}
74157016Sdes
75157016Sdeskadm5_ret_t
76157016Sdeskadm5_log_get_version (kadm5_server_context *context, u_int32_t *ver)
77157016Sdes{
78157016Sdes    return kadm5_log_get_version_fd (context->log_context.log_fd, ver);
79157016Sdes}
80157016Sdes
81157016Sdeskadm5_ret_t
82157016Sdeskadm5_log_set_version (kadm5_server_context *context, u_int32_t vno)
83157016Sdes{
84157016Sdes    kadm5_log_context *log_context = &context->log_context;
85157016Sdes
86157016Sdes    log_context->version = vno;
87157016Sdes    return 0;
88157016Sdes}
89157016Sdes
90157016Sdeskadm5_ret_t
91157016Sdeskadm5_log_init (kadm5_server_context *context)
92157016Sdes{
93157016Sdes    int fd;
94157016Sdes    kadm5_ret_t ret;
95157016Sdes    kadm5_log_context *log_context = &context->log_context;
96157016Sdes
97157016Sdes    if (log_context->log_fd != -1)
98157016Sdes	return 0;
99157016Sdes    fd = open (log_context->log_file, O_RDWR | O_CREAT, 0600);
100157016Sdes    if (fd < 0)
101157016Sdes	return errno;
102157016Sdes    if (flock (fd, LOCK_EX) < 0) {
103157016Sdes	close (fd);
104157016Sdes	return errno;
105157016Sdes    }
106157016Sdes
107157016Sdes    ret = kadm5_log_get_version_fd (fd, &log_context->version);
108157016Sdes    if (ret)
109157016Sdes	return ret;
110157016Sdes
111157016Sdes    log_context->log_fd  = fd;
112157016Sdes    return 0;
113162852Sdes}
114162852Sdes
115157016Sdeskadm5_ret_t
116162852Sdeskadm5_log_reinit (kadm5_server_context *context)
117157016Sdes{
118157016Sdes    int fd;
119157016Sdes    kadm5_log_context *log_context = &context->log_context;
120157016Sdes
121157016Sdes    if (log_context->log_fd != -1) {
122157016Sdes	close (log_context->log_fd);
123157016Sdes	log_context->log_fd = -1;
124157016Sdes    }
125157016Sdes    fd = open (log_context->log_file, O_RDWR | O_CREAT | O_TRUNC, 0600);
126157016Sdes    if (fd < 0)
127157016Sdes	return errno;
128157016Sdes    if (flock (fd, LOCK_EX) < 0) {
129157016Sdes	close (fd);
130157016Sdes	return errno;
131157016Sdes    }
132157016Sdes
133157016Sdes    log_context->version = 0;
134157016Sdes    log_context->log_fd  = fd;
135157016Sdes    return 0;
136157016Sdes}
137157016Sdes
138157016Sdes
139157016Sdeskadm5_ret_t
140157016Sdeskadm5_log_end (kadm5_server_context *context)
141157016Sdes{
142157016Sdes    kadm5_log_context *log_context = &context->log_context;
143157016Sdes    int fd = log_context->log_fd;
144157016Sdes
145157016Sdes    flock (fd, LOCK_UN);
146157016Sdes    close(fd);
147157016Sdes    log_context->log_fd = -1;
148157016Sdes    return 0;
149157016Sdes}
150157016Sdes
151157016Sdesstatic kadm5_ret_t
152157016Sdeskadm5_log_preamble (kadm5_server_context *context,
153157016Sdes		    krb5_storage *sp,
154157016Sdes		    enum kadm_ops op)
155157016Sdes{
156157016Sdes    kadm5_log_context *log_context = &context->log_context;
157157016Sdes    kadm5_ret_t kadm_ret;
158157016Sdes
159157016Sdes    kadm_ret = kadm5_log_init (context);
160157016Sdes    if (kadm_ret)
161157016Sdes	return kadm_ret;
162157016Sdes
163157016Sdes    krb5_store_int32 (sp, ++log_context->version);
164157016Sdes    krb5_store_int32 (sp, time(NULL));
165157016Sdes    krb5_store_int32 (sp, op);
166157016Sdes    return 0;
167157016Sdes}
168157016Sdes
169157016Sdesstatic kadm5_ret_t
170157016Sdeskadm5_log_postamble (kadm5_log_context *context,
171157016Sdes		     krb5_storage *sp)
172157016Sdes{
173157016Sdes    krb5_store_int32 (sp, context->version);
174157016Sdes    return 0;
175157016Sdes}
176157016Sdes
177215116Sdes/*
178215116Sdes * flush the log record in `sp'.
179215116Sdes */
180215116Sdes
181215116Sdesstatic kadm5_ret_t
182157016Sdeskadm5_log_flush (kadm5_log_context *log_context,
183157016Sdes		 krb5_storage *sp)
184157016Sdes{
185157016Sdes    krb5_data data;
186157016Sdes    size_t len;
187157016Sdes    int ret;
188157016Sdes
189157016Sdes    krb5_storage_to_data(sp, &data);
190157016Sdes    len = data.length;
191157016Sdes    ret = write (log_context->log_fd, data.data, len);
192157016Sdes    if (ret != len) {
193157016Sdes	krb5_data_free(&data);
194157016Sdes	return errno;
195157016Sdes    }
196157016Sdes    if (fsync (log_context->log_fd) < 0) {
197157016Sdes	krb5_data_free(&data);
198157016Sdes	return errno;
199157016Sdes    }
200157016Sdes    /*
201157016Sdes     * Try to send a signal to any running `ipropd-master'
202157016Sdes     */
203157016Sdes    sendto (log_context->socket_fd,
204157016Sdes	    (void *)&log_context->version,
205157016Sdes	    sizeof(log_context->version),
206157016Sdes	    0,
207157016Sdes	    (struct sockaddr *)&log_context->socket_name,
208157016Sdes	    sizeof(log_context->socket_name));
209157016Sdes
210157016Sdes    krb5_data_free(&data);
211157016Sdes    return 0;
212157016Sdes}
213157016Sdes
214295367Sdes/*
215157016Sdes * Add a `create' operation to the log.
216157016Sdes */
217157016Sdes
218157016Sdeskadm5_ret_t
219157016Sdeskadm5_log_create (kadm5_server_context *context,
220157016Sdes		  hdb_entry *ent)
221157016Sdes{
222157016Sdes    krb5_storage *sp;
223157016Sdes    kadm5_ret_t ret;
224157016Sdes    krb5_data value;
225157016Sdes    kadm5_log_context *log_context = &context->log_context;
226157016Sdes
227157016Sdes    sp = krb5_storage_emem();
228157016Sdes    ret = hdb_entry2value (context->context, ent, &value);
229157016Sdes    if (ret) {
230157016Sdes	krb5_storage_free(sp);
231157016Sdes	return ret;
232157016Sdes    }
233157016Sdes    ret = kadm5_log_preamble (context, sp, kadm_create);
234157016Sdes    if (ret) {
235157016Sdes	krb5_data_free (&value);
236157016Sdes	krb5_storage_free(sp);
237157016Sdes	return ret;
238157016Sdes    }
239157016Sdes    krb5_store_int32 (sp, value.length);
240157016Sdes    krb5_storage_write(sp, value.data, value.length);
241157016Sdes    krb5_store_int32 (sp, value.length);
242157016Sdes    krb5_data_free (&value);
243157016Sdes    ret = kadm5_log_postamble (log_context, sp);
244157016Sdes    if (ret) {
245157016Sdes	krb5_storage_free (sp);
246157016Sdes	return ret;
247295367Sdes    }
248295367Sdes    ret = kadm5_log_flush (log_context, sp);
249157016Sdes    krb5_storage_free (sp);
250157016Sdes    if (ret)
251157016Sdes	return ret;
252157016Sdes    ret = kadm5_log_end (context);
253157016Sdes    return ret;
254157016Sdes}
255157016Sdes
256157016Sdes/*
257295367Sdes * Read the data of a create log record from `sp' and change the
258295367Sdes * database.
259157016Sdes */
260295367Sdes
261295367Sdeskadm5_ret_t
262295367Sdeskadm5_log_replay_create (kadm5_server_context *context,
263295367Sdes			 u_int32_t ver,
264295367Sdes			 u_int32_t len,
265157016Sdes			 krb5_storage *sp)
266157016Sdes{
267157016Sdes    krb5_error_code ret;
268157016Sdes    krb5_data data;
269157016Sdes    hdb_entry ent;
270157016Sdes
271157016Sdes    ret = krb5_data_alloc (&data, len);
272157016Sdes    if (ret)
273157016Sdes	return ret;
274157016Sdes    krb5_storage_read (sp, data.data, len);
275157016Sdes    ret = hdb_value2entry (context->context, &data, &ent);
276157016Sdes    krb5_data_free(&data);
277157016Sdes    if (ret)
278157016Sdes	return ret;
279157016Sdes    ret = context->db->store(context->context, context->db, 0, &ent);
280157016Sdes    hdb_free_entry (context->context, &ent);
281157016Sdes    return ret;
282157016Sdes}
283
284/*
285 * Add a `delete' operation to the log.
286 */
287
288kadm5_ret_t
289kadm5_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
333kadm5_ret_t
334kadm5_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
353kadm5_ret_t
354kadm5_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
409kadm5_ret_t
410kadm5_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
455kadm5_ret_t
456kadm5_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
505kadm5_ret_t
506kadm5_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
632kadm5_ret_t
633kadm5_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
664kadm5_ret_t
665kadm5_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
677kadm5_ret_t
678kadm5_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
709krb5_storage *
710kadm5_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
723kadm5_ret_t
724kadm5_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
755kadm5_ret_t
756kadm5_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
782kadm5_ret_t
783kadm5_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