xref: /minix3/crypto/external/bsd/heimdal/dist/lib/kadm5/log.c (revision ebfedea0ce5bbe81e252ddf32d732e40fb633fae)
1 /*	$NetBSD: log.c,v 1.1.1.1 2011/04/13 18:15:30 elric Exp $	*/
2 
3 /*
4  * Copyright (c) 1997 - 2007 Kungliga Tekniska Högskolan
5  * (Royal Institute of Technology, Stockholm, Sweden).
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  *
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  *
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  *
19  * 3. Neither the name of the Institute nor the names of its contributors
20  *    may be used to endorse or promote products derived from this software
21  *    without specific prior written permission.
22  *
23  * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
24  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26  * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
27  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33  * SUCH DAMAGE.
34  */
35 
36 #include "kadm5_locl.h"
37 #include "heim_threads.h"
38 
39 __RCSID("$NetBSD: log.c,v 1.1.1.1 2011/04/13 18:15:30 elric Exp $");
40 
41 /*
42  * A log record consists of:
43  *
44  * version number		4 bytes
45  * time in seconds		4 bytes
46  * operation (enum kadm_ops)	4 bytes
47  * length of record		4 bytes
48  * data...			n bytes
49  * length of record		4 bytes
50  * version number		4 bytes
51  *
52  */
53 
54 kadm5_ret_t
55 kadm5_log_get_version_fd (int fd,
56 			  uint32_t *ver)
57 {
58     int ret;
59     krb5_storage *sp;
60     int32_t old_version;
61 
62     ret = lseek (fd, 0, SEEK_END);
63     if(ret < 0)
64 	return errno;
65     if(ret == 0) {
66 	*ver = 0;
67 	return 0;
68     }
69     sp = krb5_storage_from_fd (fd);
70     krb5_storage_seek(sp, -4, SEEK_CUR);
71     krb5_ret_int32 (sp, &old_version);
72     *ver = old_version;
73     krb5_storage_free(sp);
74     lseek (fd, 0, SEEK_END);
75     return 0;
76 }
77 
78 kadm5_ret_t
79 kadm5_log_get_version (kadm5_server_context *context, uint32_t *ver)
80 {
81     return kadm5_log_get_version_fd (context->log_context.log_fd, ver);
82 }
83 
84 kadm5_ret_t
85 kadm5_log_set_version (kadm5_server_context *context, uint32_t vno)
86 {
87     kadm5_log_context *log_context = &context->log_context;
88 
89     log_context->version = vno;
90     return 0;
91 }
92 
93 kadm5_ret_t
94 kadm5_log_init (kadm5_server_context *context)
95 {
96     int fd;
97     kadm5_ret_t ret;
98     kadm5_log_context *log_context = &context->log_context;
99 
100     if (log_context->log_fd != -1)
101 	return 0;
102     fd = open (log_context->log_file, O_RDWR | O_CREAT, 0600);
103     if (fd < 0) {
104 	ret = errno;
105 	krb5_set_error_message(context->context, ret, "kadm5_log_init: open %s",
106 			      log_context->log_file);
107 	return ret;
108     }
109     if (flock (fd, LOCK_EX) < 0) {
110 	ret = errno;
111 	krb5_set_error_message(context->context, ret, "kadm5_log_init: flock %s",
112 			       log_context->log_file);
113 	close (fd);
114 	return errno;
115     }
116 
117     ret = kadm5_log_get_version_fd (fd, &log_context->version);
118     if (ret)
119 	return ret;
120 
121     log_context->log_fd  = fd;
122     return 0;
123 }
124 
125 kadm5_ret_t
126 kadm5_log_reinit (kadm5_server_context *context)
127 {
128     int fd;
129     kadm5_log_context *log_context = &context->log_context;
130 
131     if (log_context->log_fd != -1) {
132 	flock (log_context->log_fd, LOCK_UN);
133 	close (log_context->log_fd);
134 	log_context->log_fd = -1;
135     }
136     fd = open (log_context->log_file, O_RDWR | O_CREAT | O_TRUNC, 0600);
137     if (fd < 0)
138 	return errno;
139     if (flock (fd, LOCK_EX) < 0) {
140 	close (fd);
141 	return errno;
142     }
143 
144     log_context->version = 0;
145     log_context->log_fd  = fd;
146     return 0;
147 }
148 
149 
150 kadm5_ret_t
151 kadm5_log_end (kadm5_server_context *context)
152 {
153     kadm5_log_context *log_context = &context->log_context;
154     int fd = log_context->log_fd;
155 
156     flock (fd, LOCK_UN);
157     close(fd);
158     log_context->log_fd = -1;
159     return 0;
160 }
161 
162 static kadm5_ret_t
163 kadm5_log_preamble (kadm5_server_context *context,
164 		    krb5_storage *sp,
165 		    enum kadm_ops op)
166 {
167     kadm5_log_context *log_context = &context->log_context;
168     kadm5_ret_t kadm_ret;
169 
170     kadm_ret = kadm5_log_init (context);
171     if (kadm_ret)
172 	return kadm_ret;
173 
174     krb5_store_int32 (sp, ++log_context->version);
175     krb5_store_int32 (sp, time(NULL));
176     krb5_store_int32 (sp, op);
177     return 0;
178 }
179 
180 static kadm5_ret_t
181 kadm5_log_postamble (kadm5_log_context *context,
182 		     krb5_storage *sp)
183 {
184     krb5_store_int32 (sp, context->version);
185     return 0;
186 }
187 
188 /*
189  * flush the log record in `sp'.
190  */
191 
192 static kadm5_ret_t
193 kadm5_log_flush (kadm5_log_context *log_context,
194 		 krb5_storage *sp)
195 {
196     krb5_data data;
197     size_t len;
198     int ret;
199 
200     krb5_storage_to_data(sp, &data);
201     len = data.length;
202     ret = write (log_context->log_fd, data.data, len);
203     if (ret != len) {
204 	krb5_data_free(&data);
205 	return errno;
206     }
207     if (fsync (log_context->log_fd) < 0) {
208 	krb5_data_free(&data);
209 	return errno;
210     }
211 
212     /*
213      * Try to send a signal to any running `ipropd-master'
214      */
215 #ifndef NO_UNIX_SOCKETS
216     sendto (log_context->socket_fd,
217 	    (void *)&log_context->version,
218 	    sizeof(log_context->version),
219 	    0,
220 	    (struct sockaddr *)&log_context->socket_name,
221 	    sizeof(log_context->socket_name));
222 #else
223     sendto (log_context->socket_fd,
224 	    (void *)&log_context->version,
225 	    sizeof(log_context->version),
226 	    0,
227 	    log_context->socket_info->ai_addr,
228 	    log_context->socket_info->ai_addrlen);
229 #endif
230 
231     krb5_data_free(&data);
232     return 0;
233 }
234 
235 /*
236  * Add a `create' operation to the log.
237  */
238 
239 kadm5_ret_t
240 kadm5_log_create (kadm5_server_context *context,
241 		  hdb_entry *ent)
242 {
243     krb5_storage *sp;
244     kadm5_ret_t ret;
245     krb5_data value;
246     kadm5_log_context *log_context = &context->log_context;
247 
248     sp = krb5_storage_emem();
249     ret = hdb_entry2value (context->context, ent, &value);
250     if (ret) {
251 	krb5_storage_free(sp);
252 	return ret;
253     }
254     ret = kadm5_log_preamble (context, sp, kadm_create);
255     if (ret) {
256 	krb5_data_free (&value);
257 	krb5_storage_free(sp);
258 	return ret;
259     }
260     krb5_store_int32 (sp, value.length);
261     krb5_storage_write(sp, value.data, value.length);
262     krb5_store_int32 (sp, value.length);
263     krb5_data_free (&value);
264     ret = kadm5_log_postamble (log_context, sp);
265     if (ret) {
266 	krb5_storage_free (sp);
267 	return ret;
268     }
269     ret = kadm5_log_flush (log_context, sp);
270     krb5_storage_free (sp);
271     if (ret)
272 	return ret;
273     ret = kadm5_log_end (context);
274     return ret;
275 }
276 
277 /*
278  * Read the data of a create log record from `sp' and change the
279  * database.
280  */
281 
282 static kadm5_ret_t
283 kadm5_log_replay_create (kadm5_server_context *context,
284 			 uint32_t ver,
285 			 uint32_t len,
286 			 krb5_storage *sp)
287 {
288     krb5_error_code ret;
289     krb5_data data;
290     hdb_entry_ex ent;
291 
292     memset(&ent, 0, sizeof(ent));
293 
294     ret = krb5_data_alloc (&data, len);
295     if (ret) {
296 	krb5_set_error_message(context->context, ret, "out of memory");
297 	return ret;
298     }
299     krb5_storage_read (sp, data.data, len);
300     ret = hdb_value2entry (context->context, &data, &ent.entry);
301     krb5_data_free(&data);
302     if (ret) {
303 	krb5_set_error_message(context->context, ret,
304 			       "Unmarshaling hdb entry failed");
305 	return ret;
306     }
307     ret = context->db->hdb_store(context->context, context->db, 0, &ent);
308     hdb_free_entry (context->context, &ent);
309     return ret;
310 }
311 
312 /*
313  * Add a `delete' operation to the log.
314  */
315 
316 kadm5_ret_t
317 kadm5_log_delete (kadm5_server_context *context,
318 		  krb5_principal princ)
319 {
320     krb5_storage *sp;
321     kadm5_ret_t ret;
322     off_t off;
323     off_t len;
324     kadm5_log_context *log_context = &context->log_context;
325 
326     sp = krb5_storage_emem();
327     if (sp == NULL)
328 	return ENOMEM;
329     ret = kadm5_log_preamble (context, sp, kadm_delete);
330     if (ret)
331 	goto out;
332     ret = krb5_store_int32 (sp, 0);
333     if (ret)
334 	goto out;
335     off = krb5_storage_seek (sp, 0, SEEK_CUR);
336     ret = krb5_store_principal (sp, princ);
337     if (ret)
338 	goto out;
339     len = krb5_storage_seek (sp, 0, SEEK_CUR) - off;
340     krb5_storage_seek(sp, -(len + 4), SEEK_CUR);
341     ret = krb5_store_int32 (sp, len);
342     if (ret)
343 	goto out;
344     krb5_storage_seek(sp, len, SEEK_CUR);
345     ret = krb5_store_int32 (sp, len);
346     if (ret)
347 	goto out;
348     ret = kadm5_log_postamble (log_context, sp);
349     if (ret)
350 	goto out;
351     ret = kadm5_log_flush (log_context, sp);
352     if (ret)
353 	goto out;
354     ret = kadm5_log_end (context);
355 out:
356     krb5_storage_free (sp);
357     return ret;
358 }
359 
360 /*
361  * Read a `delete' log operation from `sp' and apply it.
362  */
363 
364 static kadm5_ret_t
365 kadm5_log_replay_delete (kadm5_server_context *context,
366 			 uint32_t ver,
367 			 uint32_t len,
368 			 krb5_storage *sp)
369 {
370     krb5_error_code ret;
371     krb5_principal principal;
372 
373     ret = krb5_ret_principal (sp, &principal);
374     if (ret) {
375 	krb5_set_error_message(context->context,  ret, "Failed to read deleted "
376 			       "principal from log version: %ld",  (long)ver);
377 	return ret;
378     }
379 
380     ret = context->db->hdb_remove(context->context, context->db, principal);
381     krb5_free_principal (context->context, principal);
382     return ret;
383 }
384 
385 /*
386  * Add a `rename' operation to the log.
387  */
388 
389 kadm5_ret_t
390 kadm5_log_rename (kadm5_server_context *context,
391 		  krb5_principal source,
392 		  hdb_entry *ent)
393 {
394     krb5_storage *sp;
395     kadm5_ret_t ret;
396     off_t off;
397     off_t len;
398     krb5_data value;
399     kadm5_log_context *log_context = &context->log_context;
400 
401     krb5_data_zero(&value);
402 
403     sp = krb5_storage_emem();
404     ret = hdb_entry2value (context->context, ent, &value);
405     if (ret)
406 	goto failed;
407 
408     ret = kadm5_log_preamble (context, sp, kadm_rename);
409     if (ret)
410 	goto failed;
411 
412     ret = krb5_store_int32 (sp, 0);
413     if (ret)
414 	goto failed;
415     off = krb5_storage_seek (sp, 0, SEEK_CUR);
416     ret = krb5_store_principal (sp, source);
417     if (ret)
418 	goto failed;
419 
420     krb5_storage_write(sp, value.data, value.length);
421     len = krb5_storage_seek (sp, 0, SEEK_CUR) - off;
422 
423     krb5_storage_seek(sp, -(len + 4), SEEK_CUR);
424     ret = krb5_store_int32 (sp, len);
425     if (ret)
426 	goto failed;
427 
428     krb5_storage_seek(sp, len, SEEK_CUR);
429     ret = krb5_store_int32 (sp, len);
430     if (ret)
431 	goto failed;
432 
433     ret = kadm5_log_postamble (log_context, sp);
434     if (ret)
435 	goto failed;
436 
437     ret = kadm5_log_flush (log_context, sp);
438     if (ret)
439 	goto failed;
440     krb5_storage_free (sp);
441     krb5_data_free (&value);
442 
443     return kadm5_log_end (context);
444 
445 failed:
446     krb5_data_free(&value);
447     krb5_storage_free(sp);
448     return ret;
449 }
450 
451 /*
452  * Read a `rename' log operation from `sp' and apply it.
453  */
454 
455 static kadm5_ret_t
456 kadm5_log_replay_rename (kadm5_server_context *context,
457 			 uint32_t ver,
458 			 uint32_t len,
459 			 krb5_storage *sp)
460 {
461     krb5_error_code ret;
462     krb5_principal source;
463     hdb_entry_ex target_ent;
464     krb5_data value;
465     off_t off;
466     size_t princ_len, data_len;
467 
468     memset(&target_ent, 0, sizeof(target_ent));
469 
470     off = krb5_storage_seek(sp, 0, SEEK_CUR);
471     ret = krb5_ret_principal (sp, &source);
472     if (ret) {
473 	krb5_set_error_message(context->context, ret, "Failed to read renamed "
474 			       "principal in log, version: %ld", (long)ver);
475 	return ret;
476     }
477     princ_len = krb5_storage_seek(sp, 0, SEEK_CUR) - off;
478     data_len = len - princ_len;
479     ret = krb5_data_alloc (&value, data_len);
480     if (ret) {
481 	krb5_free_principal (context->context, source);
482 	return ret;
483     }
484     krb5_storage_read (sp, value.data, data_len);
485     ret = hdb_value2entry (context->context, &value, &target_ent.entry);
486     krb5_data_free(&value);
487     if (ret) {
488 	krb5_free_principal (context->context, source);
489 	return ret;
490     }
491     ret = context->db->hdb_store (context->context, context->db,
492 				  0, &target_ent);
493     hdb_free_entry (context->context, &target_ent);
494     if (ret) {
495 	krb5_free_principal (context->context, source);
496 	return ret;
497     }
498     ret = context->db->hdb_remove (context->context, context->db, source);
499     krb5_free_principal (context->context, source);
500     return ret;
501 }
502 
503 
504 /*
505  * Add a `modify' operation to the log.
506  */
507 
508 kadm5_ret_t
509 kadm5_log_modify (kadm5_server_context *context,
510 		  hdb_entry *ent,
511 		  uint32_t mask)
512 {
513     krb5_storage *sp;
514     kadm5_ret_t ret;
515     krb5_data value;
516     uint32_t len;
517     kadm5_log_context *log_context = &context->log_context;
518 
519     krb5_data_zero(&value);
520 
521     sp = krb5_storage_emem();
522     ret = hdb_entry2value (context->context, ent, &value);
523     if (ret)
524 	goto failed;
525 
526     ret = kadm5_log_preamble (context, sp, kadm_modify);
527     if (ret)
528 	goto failed;
529 
530     len = value.length + 4;
531     ret = krb5_store_int32 (sp, len);
532     if (ret)
533 	goto failed;
534     ret = krb5_store_int32 (sp, mask);
535     if (ret)
536 	goto failed;
537     krb5_storage_write (sp, value.data, value.length);
538 
539     ret = krb5_store_int32 (sp, len);
540     if (ret)
541 	goto failed;
542     ret = kadm5_log_postamble (log_context, sp);
543     if (ret)
544 	goto failed;
545     ret = kadm5_log_flush (log_context, sp);
546     if (ret)
547 	goto failed;
548     krb5_data_free(&value);
549     krb5_storage_free (sp);
550     return kadm5_log_end (context);
551 failed:
552     krb5_data_free(&value);
553     krb5_storage_free(sp);
554     return ret;
555 }
556 
557 /*
558  * Read a `modify' log operation from `sp' and apply it.
559  */
560 
561 static kadm5_ret_t
562 kadm5_log_replay_modify (kadm5_server_context *context,
563 			 uint32_t ver,
564 			 uint32_t len,
565 			 krb5_storage *sp)
566 {
567     krb5_error_code ret;
568     int32_t mask;
569     krb5_data value;
570     hdb_entry_ex ent, log_ent;
571 
572     memset(&log_ent, 0, sizeof(log_ent));
573 
574     krb5_ret_int32 (sp, &mask);
575     len -= 4;
576     ret = krb5_data_alloc (&value, len);
577     if (ret) {
578 	krb5_set_error_message(context->context, ret, "out of memory");
579 	return ret;
580     }
581     krb5_storage_read (sp, value.data, len);
582     ret = hdb_value2entry (context->context, &value, &log_ent.entry);
583     krb5_data_free(&value);
584     if (ret)
585 	return ret;
586 
587     memset(&ent, 0, sizeof(ent));
588     ret = context->db->hdb_fetch_kvno(context->context, context->db,
589 				      log_ent.entry.principal,
590 				      HDB_F_DECRYPT|HDB_F_GET_ANY|HDB_F_ADMIN_DATA, 0, &ent);
591     if (ret)
592 	goto out;
593     if (mask & KADM5_PRINC_EXPIRE_TIME) {
594 	if (log_ent.entry.valid_end == NULL) {
595 	    ent.entry.valid_end = NULL;
596 	} else {
597 	    if (ent.entry.valid_end == NULL) {
598 		ent.entry.valid_end = malloc(sizeof(*ent.entry.valid_end));
599 		if (ent.entry.valid_end == NULL) {
600 		    ret = ENOMEM;
601 		    krb5_set_error_message(context->context, ret, "out of memory");
602 		    goto out;
603 		}
604 	    }
605 	    *ent.entry.valid_end = *log_ent.entry.valid_end;
606 	}
607     }
608     if (mask & KADM5_PW_EXPIRATION) {
609 	if (log_ent.entry.pw_end == NULL) {
610 	    ent.entry.pw_end = NULL;
611 	} else {
612 	    if (ent.entry.pw_end == NULL) {
613 		ent.entry.pw_end = malloc(sizeof(*ent.entry.pw_end));
614 		if (ent.entry.pw_end == NULL) {
615 		    ret = ENOMEM;
616 		    krb5_set_error_message(context->context, ret, "out of memory");
617 		    goto out;
618 		}
619 	    }
620 	    *ent.entry.pw_end = *log_ent.entry.pw_end;
621 	}
622     }
623     if (mask & KADM5_LAST_PWD_CHANGE) {
624 	abort ();		/* XXX */
625     }
626     if (mask & KADM5_ATTRIBUTES) {
627 	ent.entry.flags = log_ent.entry.flags;
628     }
629     if (mask & KADM5_MAX_LIFE) {
630 	if (log_ent.entry.max_life == NULL) {
631 	    ent.entry.max_life = NULL;
632 	} else {
633 	    if (ent.entry.max_life == NULL) {
634 		ent.entry.max_life = malloc (sizeof(*ent.entry.max_life));
635 		if (ent.entry.max_life == NULL) {
636 		    ret = ENOMEM;
637 		    krb5_set_error_message(context->context, ret, "out of memory");
638 		    goto out;
639 		}
640 	    }
641 	    *ent.entry.max_life = *log_ent.entry.max_life;
642 	}
643     }
644     if ((mask & KADM5_MOD_TIME) && (mask & KADM5_MOD_NAME)) {
645 	if (ent.entry.modified_by == NULL) {
646 	    ent.entry.modified_by = malloc(sizeof(*ent.entry.modified_by));
647 	    if (ent.entry.modified_by == NULL) {
648 		ret = ENOMEM;
649 		krb5_set_error_message(context->context, ret, "out of memory");
650 		goto out;
651 	    }
652 	} else
653 	    free_Event(ent.entry.modified_by);
654 	ret = copy_Event(log_ent.entry.modified_by, ent.entry.modified_by);
655 	if (ret) {
656 	    krb5_set_error_message(context->context, ret, "out of memory");
657 	    goto out;
658 	}
659     }
660     if (mask & KADM5_KVNO) {
661 	ent.entry.kvno = log_ent.entry.kvno;
662     }
663     if (mask & KADM5_MKVNO) {
664 	abort ();		/* XXX */
665     }
666     if (mask & KADM5_AUX_ATTRIBUTES) {
667 	abort ();		/* XXX */
668     }
669     if (mask & KADM5_POLICY) {
670 	abort ();		/* XXX */
671     }
672     if (mask & KADM5_POLICY_CLR) {
673 	abort ();		/* XXX */
674     }
675     if (mask & KADM5_MAX_RLIFE) {
676 	if (log_ent.entry.max_renew == NULL) {
677 	    ent.entry.max_renew = NULL;
678 	} else {
679 	    if (ent.entry.max_renew == NULL) {
680 		ent.entry.max_renew = malloc (sizeof(*ent.entry.max_renew));
681 		if (ent.entry.max_renew == NULL) {
682 		    ret = ENOMEM;
683 		    krb5_set_error_message(context->context, ret, "out of memory");
684 		    goto out;
685 		}
686 	    }
687 	    *ent.entry.max_renew = *log_ent.entry.max_renew;
688 	}
689     }
690     if (mask & KADM5_LAST_SUCCESS) {
691 	abort ();		/* XXX */
692     }
693     if (mask & KADM5_LAST_FAILED) {
694 	abort ();		/* XXX */
695     }
696     if (mask & KADM5_FAIL_AUTH_COUNT) {
697 	abort ();		/* XXX */
698     }
699     if (mask & KADM5_KEY_DATA) {
700 	size_t num;
701 	int i;
702 
703 	for (i = 0; i < ent.entry.keys.len; ++i)
704 	    free_Key(&ent.entry.keys.val[i]);
705 	free (ent.entry.keys.val);
706 
707 	num = log_ent.entry.keys.len;
708 
709 	ent.entry.keys.len = num;
710 	ent.entry.keys.val = malloc(len * sizeof(*ent.entry.keys.val));
711 	if (ent.entry.keys.val == NULL) {
712 	    krb5_set_error_message(context->context, ENOMEM, "out of memory");
713 	    return ENOMEM;
714 	}
715 	for (i = 0; i < ent.entry.keys.len; ++i) {
716 	    ret = copy_Key(&log_ent.entry.keys.val[i],
717 			   &ent.entry.keys.val[i]);
718 	    if (ret) {
719 		krb5_set_error_message(context->context, ret, "out of memory");
720 		goto out;
721 	    }
722 	}
723     }
724     if ((mask & KADM5_TL_DATA) && log_ent.entry.extensions) {
725 	HDB_extensions *es = ent.entry.extensions;
726 
727 	ent.entry.extensions = calloc(1, sizeof(*ent.entry.extensions));
728 	if (ent.entry.extensions == NULL)
729 	    goto out;
730 
731 	ret = copy_HDB_extensions(log_ent.entry.extensions,
732 				  ent.entry.extensions);
733 	if (ret) {
734 	    krb5_set_error_message(context->context, ret, "out of memory");
735 	    free(ent.entry.extensions);
736 	    ent.entry.extensions = es;
737 	    goto out;
738 	}
739 	if (es) {
740 	    free_HDB_extensions(es);
741 	    free(es);
742 	}
743     }
744     ret = context->db->hdb_store(context->context, context->db,
745 				 HDB_F_REPLACE, &ent);
746  out:
747     hdb_free_entry (context->context, &ent);
748     hdb_free_entry (context->context, &log_ent);
749     return ret;
750 }
751 
752 /*
753  * Add a `nop' operation to the log. Does not close the log.
754  */
755 
756 kadm5_ret_t
757 kadm5_log_nop (kadm5_server_context *context)
758 {
759     krb5_storage *sp;
760     kadm5_ret_t ret;
761     kadm5_log_context *log_context = &context->log_context;
762 
763     sp = krb5_storage_emem();
764     ret = kadm5_log_preamble (context, sp, kadm_nop);
765     if (ret) {
766 	krb5_storage_free (sp);
767 	return ret;
768     }
769     krb5_store_int32 (sp, 0);
770     krb5_store_int32 (sp, 0);
771     ret = kadm5_log_postamble (log_context, sp);
772     if (ret) {
773 	krb5_storage_free (sp);
774 	return ret;
775     }
776     ret = kadm5_log_flush (log_context, sp);
777     krb5_storage_free (sp);
778 
779     return ret;
780 }
781 
782 /*
783  * Read a `nop' log operation from `sp' and apply it.
784  */
785 
786 static kadm5_ret_t
787 kadm5_log_replay_nop (kadm5_server_context *context,
788 		      uint32_t ver,
789 		      uint32_t len,
790 		      krb5_storage *sp)
791 {
792     return 0;
793 }
794 
795 /*
796  * Call `func' for each log record in the log in `context'
797  */
798 
799 kadm5_ret_t
800 kadm5_log_foreach (kadm5_server_context *context,
801 		   void (*func)(kadm5_server_context *server_context,
802 				uint32_t ver,
803 				time_t timestamp,
804 				enum kadm_ops op,
805 				uint32_t len,
806 				krb5_storage *,
807 				void *),
808 		   void *ctx)
809 {
810     int fd = context->log_context.log_fd;
811     krb5_storage *sp;
812 
813     lseek (fd, 0, SEEK_SET);
814     sp = krb5_storage_from_fd (fd);
815     for (;;) {
816 	int32_t ver, timestamp, op, len, len2, ver2;
817 
818 	if(krb5_ret_int32 (sp, &ver) != 0)
819 	    break;
820 	krb5_ret_int32 (sp, &timestamp);
821 	krb5_ret_int32 (sp, &op);
822 	krb5_ret_int32 (sp, &len);
823 	(*func)(context, ver, timestamp, op, len, sp, ctx);
824 	krb5_ret_int32 (sp, &len2);
825 	krb5_ret_int32 (sp, &ver2);
826 	if (len != len2)
827 	    abort();
828 	if (ver != ver2)
829 	    abort();
830     }
831     krb5_storage_free(sp);
832     return 0;
833 }
834 
835 /*
836  * Go to end of log.
837  */
838 
839 krb5_storage *
840 kadm5_log_goto_end (int fd)
841 {
842     krb5_storage *sp;
843 
844     sp = krb5_storage_from_fd (fd);
845     krb5_storage_seek(sp, 0, SEEK_END);
846     return sp;
847 }
848 
849 /*
850  * Return previous log entry.
851  *
852  * The pointer in `sp´ is assumed to be at the top of the entry before
853  * previous entry. On success, the `sp´ pointer is set to data portion
854  * of previous entry. In case of error, it's not changed at all.
855  */
856 
857 kadm5_ret_t
858 kadm5_log_previous (krb5_context context,
859 		    krb5_storage *sp,
860 		    uint32_t *ver,
861 		    time_t *timestamp,
862 		    enum kadm_ops *op,
863 		    uint32_t *len)
864 {
865     krb5_error_code ret;
866     off_t off, oldoff;
867     int32_t tmp;
868 
869     oldoff = krb5_storage_seek(sp, 0, SEEK_CUR);
870 
871     krb5_storage_seek(sp, -8, SEEK_CUR);
872     ret = krb5_ret_int32 (sp, &tmp);
873     if (ret)
874 	goto end_of_storage;
875     *len = tmp;
876     ret = krb5_ret_int32 (sp, &tmp);
877     if (ret)
878 	goto end_of_storage;
879     *ver = tmp;
880     off = 24 + *len;
881     krb5_storage_seek(sp, -off, SEEK_CUR);
882     ret = krb5_ret_int32 (sp, &tmp);
883     if (ret)
884 	goto end_of_storage;
885     if (tmp != *ver) {
886 	krb5_storage_seek(sp, oldoff, SEEK_SET);
887 	krb5_set_error_message(context, KADM5_BAD_DB,
888 			       "kadm5_log_previous: log entry "
889 			       "have consistency failure, version number wrong "
890 			       "(tmp %lu ver %lu)",
891 			       (unsigned long)tmp,
892 			       (unsigned long)*ver);
893 	return KADM5_BAD_DB;
894     }
895     ret = krb5_ret_int32 (sp, &tmp);
896     if (ret)
897 	goto end_of_storage;
898     *timestamp = tmp;
899     ret = krb5_ret_int32 (sp, &tmp);
900     if (ret)
901 	goto end_of_storage;
902     *op = tmp;
903     ret = krb5_ret_int32 (sp, &tmp);
904     if (ret)
905 	goto end_of_storage;
906     if (tmp != *len) {
907 	krb5_storage_seek(sp, oldoff, SEEK_SET);
908 	krb5_set_error_message(context, KADM5_BAD_DB,
909 			       "kadm5_log_previous: log entry "
910 			       "have consistency failure, length wrong");
911 	return KADM5_BAD_DB;
912     }
913     return 0;
914 
915  end_of_storage:
916     krb5_storage_seek(sp, oldoff, SEEK_SET);
917     krb5_set_error_message(context, ret, "kadm5_log_previous: end of storage "
918 			   "reached before end");
919     return ret;
920 }
921 
922 /*
923  * Replay a record from the log
924  */
925 
926 kadm5_ret_t
927 kadm5_log_replay (kadm5_server_context *context,
928 		  enum kadm_ops op,
929 		  uint32_t ver,
930 		  uint32_t len,
931 		  krb5_storage *sp)
932 {
933     switch (op) {
934     case kadm_create :
935 	return kadm5_log_replay_create (context, ver, len, sp);
936     case kadm_delete :
937 	return kadm5_log_replay_delete (context, ver, len, sp);
938     case kadm_rename :
939 	return kadm5_log_replay_rename (context, ver, len, sp);
940     case kadm_modify :
941 	return kadm5_log_replay_modify (context, ver, len, sp);
942     case kadm_nop :
943 	return kadm5_log_replay_nop (context, ver, len, sp);
944     default :
945 	krb5_set_error_message(context->context, KADM5_FAILURE,
946 			       "Unsupported replay op %d", (int)op);
947 	return KADM5_FAILURE;
948     }
949 }
950 
951 /*
952  * truncate the log - i.e. create an empty file with just (nop vno + 2)
953  */
954 
955 kadm5_ret_t
956 kadm5_log_truncate (kadm5_server_context *server_context)
957 {
958     kadm5_ret_t ret;
959     uint32_t vno;
960 
961     ret = kadm5_log_init (server_context);
962     if (ret)
963 	return ret;
964 
965     ret = kadm5_log_get_version (server_context, &vno);
966     if (ret)
967 	return ret;
968 
969     ret = kadm5_log_reinit (server_context);
970     if (ret)
971 	return ret;
972 
973     ret = kadm5_log_set_version (server_context, vno);
974     if (ret)
975 	return ret;
976 
977     ret = kadm5_log_nop (server_context);
978     if (ret)
979 	return ret;
980 
981     ret = kadm5_log_end (server_context);
982     if (ret)
983 	return ret;
984     return 0;
985 
986 }
987 
988 #ifndef NO_UNIX_SOCKETS
989 
990 static char *default_signal = NULL;
991 static HEIMDAL_MUTEX signal_mutex = HEIMDAL_MUTEX_INITIALIZER;
992 
993 const char *
994 kadm5_log_signal_socket(krb5_context context)
995 {
996     HEIMDAL_MUTEX_lock(&signal_mutex);
997     if (!default_signal)
998 	asprintf(&default_signal, "%s/signal", hdb_db_dir(context));
999     HEIMDAL_MUTEX_unlock(&signal_mutex);
1000 
1001     return krb5_config_get_string_default(context,
1002 					  NULL,
1003 					  default_signal,
1004 					  "kdc",
1005 					  "signal_socket",
1006 					  NULL);
1007 }
1008 
1009 #else  /* NO_UNIX_SOCKETS */
1010 
1011 #define SIGNAL_SOCKET_HOST "127.0.0.1"
1012 #define SIGNAL_SOCKET_PORT "12701"
1013 
1014 kadm5_ret_t
1015 kadm5_log_signal_socket_info(krb5_context context,
1016 			     int server_end,
1017 			     struct addrinfo **ret_addrs)
1018 {
1019     struct addrinfo hints;
1020     struct addrinfo *addrs = NULL;
1021     kadm5_ret_t ret = KADM5_FAILURE;
1022     int wsret;
1023 
1024     memset(&hints, 0, sizeof(hints));
1025 
1026     hints.ai_flags = AI_NUMERICHOST;
1027     if (server_end)
1028 	hints.ai_flags |= AI_PASSIVE;
1029     hints.ai_family = AF_INET;
1030     hints.ai_socktype = SOCK_STREAM;
1031     hints.ai_protocol = IPPROTO_TCP;
1032 
1033     wsret = getaddrinfo(SIGNAL_SOCKET_HOST,
1034 			SIGNAL_SOCKET_PORT,
1035 			&hints, &addrs);
1036 
1037     if (wsret != 0) {
1038 	krb5_set_error_message(context, KADM5_FAILURE,
1039 			       "%s", gai_strerror(wsret));
1040 	goto done;
1041     }
1042 
1043     if (addrs == NULL) {
1044 	krb5_set_error_message(context, KADM5_FAILURE,
1045 			       "getaddrinfo() failed to return address list");
1046 	goto done;
1047     }
1048 
1049     *ret_addrs = addrs;
1050     addrs = NULL;
1051     ret = 0;
1052 
1053  done:
1054     if (addrs)
1055 	freeaddrinfo(addrs);
1056     return ret;
1057 }
1058 
1059 #endif
1060