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