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
54kadm5_ret_t
55kadm5_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
78kadm5_ret_t
79kadm5_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
84kadm5_ret_t
85kadm5_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
93kadm5_ret_t
94kadm5_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
125kadm5_ret_t
126kadm5_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
150kadm5_ret_t
151kadm5_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
162static kadm5_ret_t
163kadm5_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
180static kadm5_ret_t
181kadm5_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
192static kadm5_ret_t
193kadm5_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
239kadm5_ret_t
240kadm5_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
282static kadm5_ret_t
283kadm5_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
316kadm5_ret_t
317kadm5_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);
355out:
356    krb5_storage_free (sp);
357    return ret;
358}
359
360/*
361 * Read a `delete' log operation from `sp' and apply it.
362 */
363
364static kadm5_ret_t
365kadm5_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
389kadm5_ret_t
390kadm5_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
445failed:
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
455static kadm5_ret_t
456kadm5_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
508kadm5_ret_t
509kadm5_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);
551failed:
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
561static kadm5_ret_t
562kadm5_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
756kadm5_ret_t
757kadm5_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
786static kadm5_ret_t
787kadm5_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
799kadm5_ret_t
800kadm5_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
839krb5_storage *
840kadm5_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
857kadm5_ret_t
858kadm5_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
926kadm5_ret_t
927kadm5_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
955kadm5_ret_t
956kadm5_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
990static char *default_signal = NULL;
991static HEIMDAL_MUTEX signal_mutex = HEIMDAL_MUTEX_INITIALIZER;
992
993const char *
994kadm5_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
1014kadm5_ret_t
1015kadm5_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