]> CyberLeo.Net >> Repos - FreeBSD/releng/9.2.git/blob - crypto/heimdal/lib/kadm5/log.c
- Copy stable/9 to releng/9.2 as part of the 9.2-RELEASE cycle.
[FreeBSD/releng/9.2.git] / crypto / heimdal / lib / kadm5 / log.c
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 }