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
37RCSID("$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
52kadm5_ret_t
53kadm5_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
76kadm5_ret_t
77kadm5_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
82kadm5_ret_t
83kadm5_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
91kadm5_ret_t
92kadm5_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
121kadm5_ret_t
122kadm5_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
146kadm5_ret_t
147kadm5_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
158static kadm5_ret_t
159kadm5_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
176static kadm5_ret_t
177kadm5_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
188static kadm5_ret_t
189kadm5_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
225kadm5_ret_t
226kadm5_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
268static kadm5_ret_t
269kadm5_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
302kadm5_ret_t
303kadm5_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);
341out:
342    krb5_storage_free (sp);
343    return ret;
344}
345
346/*
347 * Read a `delete' log operation from `sp' and apply it.
348 */
349
350static kadm5_ret_t
351kadm5_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
375kadm5_ret_t
376kadm5_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
431failed:
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
441static kadm5_ret_t
442kadm5_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
494kadm5_ret_t
495kadm5_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);
537failed:
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
547static kadm5_ret_t
548kadm5_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
742kadm5_ret_t
743kadm5_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
772static kadm5_ret_t
773kadm5_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
785kadm5_ret_t
786kadm5_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
825krb5_storage *
826kadm5_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
843kadm5_ret_t
844kadm5_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
903kadm5_ret_t
904kadm5_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
932kadm5_ret_t
933kadm5_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
965static char *default_signal = NULL;
966static HEIMDAL_MUTEX signal_mutex = HEIMDAL_MUTEX_INITIALIZER;
967
968const char *
969kadm5_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}
983