155682Smarkm/*
2178825Sdfr * Copyright (c) 1997 - 2007 Kungliga Tekniska H�gskolan
355682Smarkm * (Royal Institute of Technology, Stockholm, Sweden).
455682Smarkm * All rights reserved.
555682Smarkm *
655682Smarkm * Redistribution and use in source and binary forms, with or without
755682Smarkm * modification, are permitted provided that the following conditions
855682Smarkm * are met:
955682Smarkm *
1055682Smarkm * 1. Redistributions of source code must retain the above copyright
1155682Smarkm *    notice, this list of conditions and the following disclaimer.
1255682Smarkm *
1355682Smarkm * 2. Redistributions in binary form must reproduce the above copyright
1455682Smarkm *    notice, this list of conditions and the following disclaimer in the
1555682Smarkm *    documentation and/or other materials provided with the distribution.
1655682Smarkm *
1755682Smarkm * 3. Neither the name of the Institute nor the names of its contributors
1855682Smarkm *    may be used to endorse or promote products derived from this software
1955682Smarkm *    without specific prior written permission.
2055682Smarkm *
2155682Smarkm * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
2255682Smarkm * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2355682Smarkm * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2455682Smarkm * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
2555682Smarkm * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2655682Smarkm * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2755682Smarkm * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2855682Smarkm * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2955682Smarkm * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3055682Smarkm * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3155682Smarkm * SUCH DAMAGE.
3255682Smarkm */
3355682Smarkm
3455682Smarkm#include "kadm5_locl.h"
35178825Sdfr#include "heim_threads.h"
3655682Smarkm
37178825SdfrRCSID("$Id: log.c 22211 2007-12-07 19:27:27Z lha $");
3855682Smarkm
3955682Smarkm/*
4055682Smarkm * A log record consists of:
4155682Smarkm *
4255682Smarkm * version number		4 bytes
4355682Smarkm * time in seconds		4 bytes
4455682Smarkm * operation (enum kadm_ops)	4 bytes
4555682Smarkm * length of record		4 bytes
4655682Smarkm * data...			n bytes
4755682Smarkm * length of record		4 bytes
4855682Smarkm * version number		4 bytes
4955682Smarkm *
5055682Smarkm */
5155682Smarkm
5255682Smarkmkadm5_ret_t
5372445Sassarkadm5_log_get_version_fd (int fd,
54178825Sdfr			  uint32_t *ver)
5555682Smarkm{
5655682Smarkm    int ret;
5755682Smarkm    krb5_storage *sp;
5855682Smarkm    int32_t old_version;
5955682Smarkm
6055682Smarkm    ret = lseek (fd, 0, SEEK_END);
6155682Smarkm    if(ret < 0)
6255682Smarkm	return errno;
6355682Smarkm    if(ret == 0) {
6455682Smarkm	*ver = 0;
6555682Smarkm	return 0;
6655682Smarkm    }
6755682Smarkm    sp = krb5_storage_from_fd (fd);
68102644Snectar    krb5_storage_seek(sp, -4, SEEK_CUR);
6955682Smarkm    krb5_ret_int32 (sp, &old_version);
7055682Smarkm    *ver = old_version;
7155682Smarkm    krb5_storage_free(sp);
7255682Smarkm    lseek (fd, 0, SEEK_END);
7355682Smarkm    return 0;
7455682Smarkm}
7555682Smarkm
7655682Smarkmkadm5_ret_t
77178825Sdfrkadm5_log_get_version (kadm5_server_context *context, uint32_t *ver)
7872445Sassar{
7972445Sassar    return kadm5_log_get_version_fd (context->log_context.log_fd, ver);
8072445Sassar}
8172445Sassar
8272445Sassarkadm5_ret_t
83178825Sdfrkadm5_log_set_version (kadm5_server_context *context, uint32_t vno)
8472445Sassar{
8572445Sassar    kadm5_log_context *log_context = &context->log_context;
8672445Sassar
8772445Sassar    log_context->version = vno;
8872445Sassar    return 0;
8972445Sassar}
9072445Sassar
9172445Sassarkadm5_ret_t
9255682Smarkmkadm5_log_init (kadm5_server_context *context)
9355682Smarkm{
9455682Smarkm    int fd;
9555682Smarkm    kadm5_ret_t ret;
9655682Smarkm    kadm5_log_context *log_context = &context->log_context;
9755682Smarkm
9855682Smarkm    if (log_context->log_fd != -1)
9955682Smarkm	return 0;
10055682Smarkm    fd = open (log_context->log_file, O_RDWR | O_CREAT, 0600);
101178825Sdfr    if (fd < 0) {
102178825Sdfr	krb5_set_error_string(context->context, "kadm5_log_init: open %s",
103178825Sdfr			      log_context->log_file);
10455682Smarkm	return errno;
105178825Sdfr    }
10655682Smarkm    if (flock (fd, LOCK_EX) < 0) {
107178825Sdfr	krb5_set_error_string(context->context, "kadm5_log_init: flock %s",
108178825Sdfr			      log_context->log_file);
10955682Smarkm	close (fd);
11055682Smarkm	return errno;
11155682Smarkm    }
11255682Smarkm
11372445Sassar    ret = kadm5_log_get_version_fd (fd, &log_context->version);
11455682Smarkm    if (ret)
11555682Smarkm	return ret;
11655682Smarkm
11755682Smarkm    log_context->log_fd  = fd;
11855682Smarkm    return 0;
11955682Smarkm}
12055682Smarkm
12155682Smarkmkadm5_ret_t
12272445Sassarkadm5_log_reinit (kadm5_server_context *context)
12372445Sassar{
12472445Sassar    int fd;
12572445Sassar    kadm5_log_context *log_context = &context->log_context;
12672445Sassar
12772445Sassar    if (log_context->log_fd != -1) {
128178825Sdfr	flock (log_context->log_fd, LOCK_UN);
12972445Sassar	close (log_context->log_fd);
13072445Sassar	log_context->log_fd = -1;
13172445Sassar    }
13272445Sassar    fd = open (log_context->log_file, O_RDWR | O_CREAT | O_TRUNC, 0600);
13372445Sassar    if (fd < 0)
13472445Sassar	return errno;
13572445Sassar    if (flock (fd, LOCK_EX) < 0) {
13672445Sassar	close (fd);
13772445Sassar	return errno;
13872445Sassar    }
13972445Sassar
14072445Sassar    log_context->version = 0;
14172445Sassar    log_context->log_fd  = fd;
14272445Sassar    return 0;
14372445Sassar}
14472445Sassar
14572445Sassar
14672445Sassarkadm5_ret_t
14755682Smarkmkadm5_log_end (kadm5_server_context *context)
14855682Smarkm{
14955682Smarkm    kadm5_log_context *log_context = &context->log_context;
15055682Smarkm    int fd = log_context->log_fd;
15155682Smarkm
15255682Smarkm    flock (fd, LOCK_UN);
15355682Smarkm    close(fd);
15455682Smarkm    log_context->log_fd = -1;
15555682Smarkm    return 0;
15655682Smarkm}
15755682Smarkm
15855682Smarkmstatic kadm5_ret_t
15955682Smarkmkadm5_log_preamble (kadm5_server_context *context,
16055682Smarkm		    krb5_storage *sp,
16155682Smarkm		    enum kadm_ops op)
16255682Smarkm{
16355682Smarkm    kadm5_log_context *log_context = &context->log_context;
16455682Smarkm    kadm5_ret_t kadm_ret;
16555682Smarkm
16655682Smarkm    kadm_ret = kadm5_log_init (context);
16755682Smarkm    if (kadm_ret)
16855682Smarkm	return kadm_ret;
16955682Smarkm
17055682Smarkm    krb5_store_int32 (sp, ++log_context->version);
17155682Smarkm    krb5_store_int32 (sp, time(NULL));
17255682Smarkm    krb5_store_int32 (sp, op);
17355682Smarkm    return 0;
17455682Smarkm}
17555682Smarkm
17655682Smarkmstatic kadm5_ret_t
17755682Smarkmkadm5_log_postamble (kadm5_log_context *context,
17855682Smarkm		     krb5_storage *sp)
17955682Smarkm{
18055682Smarkm    krb5_store_int32 (sp, context->version);
18155682Smarkm    return 0;
18255682Smarkm}
18355682Smarkm
18455682Smarkm/*
18555682Smarkm * flush the log record in `sp'.
18655682Smarkm */
18755682Smarkm
18855682Smarkmstatic kadm5_ret_t
18955682Smarkmkadm5_log_flush (kadm5_log_context *log_context,
19055682Smarkm		 krb5_storage *sp)
19155682Smarkm{
19255682Smarkm    krb5_data data;
19355682Smarkm    size_t len;
19455682Smarkm    int ret;
19555682Smarkm
19655682Smarkm    krb5_storage_to_data(sp, &data);
19755682Smarkm    len = data.length;
19855682Smarkm    ret = write (log_context->log_fd, data.data, len);
19955682Smarkm    if (ret != len) {
20055682Smarkm	krb5_data_free(&data);
20155682Smarkm	return errno;
20255682Smarkm    }
20355682Smarkm    if (fsync (log_context->log_fd) < 0) {
20455682Smarkm	krb5_data_free(&data);
20555682Smarkm	return errno;
20655682Smarkm    }
20755682Smarkm    /*
20855682Smarkm     * Try to send a signal to any running `ipropd-master'
20955682Smarkm     */
21055682Smarkm    sendto (log_context->socket_fd,
21155682Smarkm	    (void *)&log_context->version,
21255682Smarkm	    sizeof(log_context->version),
21355682Smarkm	    0,
21455682Smarkm	    (struct sockaddr *)&log_context->socket_name,
21555682Smarkm	    sizeof(log_context->socket_name));
21655682Smarkm
21755682Smarkm    krb5_data_free(&data);
21855682Smarkm    return 0;
21955682Smarkm}
22055682Smarkm
22155682Smarkm/*
22255682Smarkm * Add a `create' operation to the log.
22355682Smarkm */
22455682Smarkm
22555682Smarkmkadm5_ret_t
22655682Smarkmkadm5_log_create (kadm5_server_context *context,
22755682Smarkm		  hdb_entry *ent)
22855682Smarkm{
22955682Smarkm    krb5_storage *sp;
23055682Smarkm    kadm5_ret_t ret;
23155682Smarkm    krb5_data value;
23255682Smarkm    kadm5_log_context *log_context = &context->log_context;
23355682Smarkm
23455682Smarkm    sp = krb5_storage_emem();
23555682Smarkm    ret = hdb_entry2value (context->context, ent, &value);
23655682Smarkm    if (ret) {
23755682Smarkm	krb5_storage_free(sp);
23855682Smarkm	return ret;
23955682Smarkm    }
24055682Smarkm    ret = kadm5_log_preamble (context, sp, kadm_create);
24155682Smarkm    if (ret) {
24255682Smarkm	krb5_data_free (&value);
24355682Smarkm	krb5_storage_free(sp);
24455682Smarkm	return ret;
24555682Smarkm    }
24655682Smarkm    krb5_store_int32 (sp, value.length);
247102644Snectar    krb5_storage_write(sp, value.data, value.length);
24855682Smarkm    krb5_store_int32 (sp, value.length);
24955682Smarkm    krb5_data_free (&value);
25055682Smarkm    ret = kadm5_log_postamble (log_context, sp);
25155682Smarkm    if (ret) {
25255682Smarkm	krb5_storage_free (sp);
25355682Smarkm	return ret;
25455682Smarkm    }
25555682Smarkm    ret = kadm5_log_flush (log_context, sp);
25655682Smarkm    krb5_storage_free (sp);
25755682Smarkm    if (ret)
25855682Smarkm	return ret;
25955682Smarkm    ret = kadm5_log_end (context);
26055682Smarkm    return ret;
26155682Smarkm}
26255682Smarkm
26355682Smarkm/*
26455682Smarkm * Read the data of a create log record from `sp' and change the
26555682Smarkm * database.
26655682Smarkm */
26755682Smarkm
268178825Sdfrstatic kadm5_ret_t
26955682Smarkmkadm5_log_replay_create (kadm5_server_context *context,
270178825Sdfr			 uint32_t ver,
271178825Sdfr			 uint32_t len,
27255682Smarkm			 krb5_storage *sp)
27355682Smarkm{
27455682Smarkm    krb5_error_code ret;
27555682Smarkm    krb5_data data;
276178825Sdfr    hdb_entry_ex ent;
27755682Smarkm
278178825Sdfr    memset(&ent, 0, sizeof(ent));
279178825Sdfr
280120945Snectar    ret = krb5_data_alloc (&data, len);
281178825Sdfr    if (ret) {
282178825Sdfr	krb5_set_error_string(context->context, "out of memory");
283120945Snectar	return ret;
284178825Sdfr    }
285102644Snectar    krb5_storage_read (sp, data.data, len);
286178825Sdfr    ret = hdb_value2entry (context->context, &data, &ent.entry);
28755682Smarkm    krb5_data_free(&data);
288178825Sdfr    if (ret) {
289178825Sdfr	krb5_set_error_string(context->context,
290178825Sdfr			      "Unmarshaling hdb entry failed");
29155682Smarkm	return ret;
292178825Sdfr    }
293178825Sdfr    ret = context->db->hdb_store(context->context, context->db, 0, &ent);
29455682Smarkm    hdb_free_entry (context->context, &ent);
29555682Smarkm    return ret;
29655682Smarkm}
29755682Smarkm
29855682Smarkm/*
29955682Smarkm * Add a `delete' operation to the log.
30055682Smarkm */
30155682Smarkm
30255682Smarkmkadm5_ret_t
30355682Smarkmkadm5_log_delete (kadm5_server_context *context,
30455682Smarkm		  krb5_principal princ)
30555682Smarkm{
30655682Smarkm    krb5_storage *sp;
30755682Smarkm    kadm5_ret_t ret;
30855682Smarkm    off_t off;
30955682Smarkm    off_t len;
31055682Smarkm    kadm5_log_context *log_context = &context->log_context;
31155682Smarkm
31255682Smarkm    sp = krb5_storage_emem();
313178825Sdfr    if (sp == NULL)
314178825Sdfr	return ENOMEM;
31555682Smarkm    ret = kadm5_log_preamble (context, sp, kadm_delete);
316178825Sdfr    if (ret)
317178825Sdfr	goto out;
318178825Sdfr    ret = krb5_store_int32 (sp, 0);
319178825Sdfr    if (ret)
320178825Sdfr	goto out;
321102644Snectar    off = krb5_storage_seek (sp, 0, SEEK_CUR);
322178825Sdfr    ret = krb5_store_principal (sp, princ);
323178825Sdfr    if (ret)
324178825Sdfr	goto out;
325102644Snectar    len = krb5_storage_seek (sp, 0, SEEK_CUR) - off;
326102644Snectar    krb5_storage_seek(sp, -(len + 4), SEEK_CUR);
327178825Sdfr    ret = krb5_store_int32 (sp, len);
328178825Sdfr    if (ret)
329178825Sdfr	goto out;
330102644Snectar    krb5_storage_seek(sp, len, SEEK_CUR);
331178825Sdfr    ret = krb5_store_int32 (sp, len);
332178825Sdfr    if (ret)
333178825Sdfr	goto out;
33455682Smarkm    ret = kadm5_log_postamble (log_context, sp);
335178825Sdfr    if (ret)
336178825Sdfr	goto out;
33755682Smarkm    ret = kadm5_log_flush (log_context, sp);
33855682Smarkm    if (ret)
339178825Sdfr	goto out;
34055682Smarkm    ret = kadm5_log_end (context);
341178825Sdfrout:
342178825Sdfr    krb5_storage_free (sp);
34355682Smarkm    return ret;
34455682Smarkm}
34555682Smarkm
34655682Smarkm/*
34755682Smarkm * Read a `delete' log operation from `sp' and apply it.
34855682Smarkm */
34955682Smarkm
350178825Sdfrstatic kadm5_ret_t
35155682Smarkmkadm5_log_replay_delete (kadm5_server_context *context,
352178825Sdfr			 uint32_t ver,
353178825Sdfr			 uint32_t len,
35455682Smarkm			 krb5_storage *sp)
35555682Smarkm{
35655682Smarkm    krb5_error_code ret;
357178825Sdfr    krb5_principal principal;
35855682Smarkm
359178825Sdfr    ret = krb5_ret_principal (sp, &principal);
360178825Sdfr    if (ret) {
361178825Sdfr	krb5_set_error_string(context->context,  "Failed to read deleted "
362178825Sdfr			      "principal from log version: %ld",  (long)ver);
363178825Sdfr	return ret;
364178825Sdfr    }
36555682Smarkm
366178825Sdfr    ret = context->db->hdb_remove(context->context, context->db, principal);
367178825Sdfr    krb5_free_principal (context->context, principal);
36855682Smarkm    return ret;
36955682Smarkm}
37055682Smarkm
37155682Smarkm/*
37255682Smarkm * Add a `rename' operation to the log.
37355682Smarkm */
37455682Smarkm
37555682Smarkmkadm5_ret_t
37655682Smarkmkadm5_log_rename (kadm5_server_context *context,
37755682Smarkm		  krb5_principal source,
37855682Smarkm		  hdb_entry *ent)
37955682Smarkm{
38055682Smarkm    krb5_storage *sp;
38155682Smarkm    kadm5_ret_t ret;
38255682Smarkm    off_t off;
38355682Smarkm    off_t len;
38455682Smarkm    krb5_data value;
38555682Smarkm    kadm5_log_context *log_context = &context->log_context;
38655682Smarkm
387178825Sdfr    krb5_data_zero(&value);
388178825Sdfr
38955682Smarkm    sp = krb5_storage_emem();
39055682Smarkm    ret = hdb_entry2value (context->context, ent, &value);
391178825Sdfr    if (ret)
392178825Sdfr	goto failed;
393178825Sdfr
39455682Smarkm    ret = kadm5_log_preamble (context, sp, kadm_rename);
395178825Sdfr    if (ret)
396178825Sdfr	goto failed;
397178825Sdfr
398178825Sdfr    ret = krb5_store_int32 (sp, 0);
399178825Sdfr    if (ret)
400178825Sdfr	goto failed;
401102644Snectar    off = krb5_storage_seek (sp, 0, SEEK_CUR);
402178825Sdfr    ret = krb5_store_principal (sp, source);
403178825Sdfr    if (ret)
404178825Sdfr	goto failed;
405178825Sdfr
406102644Snectar    krb5_storage_write(sp, value.data, value.length);
407102644Snectar    len = krb5_storage_seek (sp, 0, SEEK_CUR) - off;
40855682Smarkm
409102644Snectar    krb5_storage_seek(sp, -(len + 4), SEEK_CUR);
410178825Sdfr    ret = krb5_store_int32 (sp, len);
411178825Sdfr    if (ret)
412178825Sdfr	goto failed;
413178825Sdfr
414102644Snectar    krb5_storage_seek(sp, len, SEEK_CUR);
415178825Sdfr    ret = krb5_store_int32 (sp, len);
416178825Sdfr    if (ret)
417178825Sdfr	goto failed;
418178825Sdfr
41955682Smarkm    ret = kadm5_log_postamble (log_context, sp);
420178825Sdfr    if (ret)
421178825Sdfr	goto failed;
422178825Sdfr
42355682Smarkm    ret = kadm5_log_flush (log_context, sp);
424178825Sdfr    if (ret)
425178825Sdfr	goto failed;
42655682Smarkm    krb5_storage_free (sp);
427178825Sdfr    krb5_data_free (&value);
428178825Sdfr
429178825Sdfr    return kadm5_log_end (context);
430178825Sdfr
431178825Sdfrfailed:
432178825Sdfr    krb5_data_free(&value);
433178825Sdfr    krb5_storage_free(sp);
43455682Smarkm    return ret;
43555682Smarkm}
43655682Smarkm
43755682Smarkm/*
43855682Smarkm * Read a `rename' log operation from `sp' and apply it.
43955682Smarkm */
44055682Smarkm
441178825Sdfrstatic kadm5_ret_t
44255682Smarkmkadm5_log_replay_rename (kadm5_server_context *context,
443178825Sdfr			 uint32_t ver,
444178825Sdfr			 uint32_t len,
44555682Smarkm			 krb5_storage *sp)
44655682Smarkm{
44755682Smarkm    krb5_error_code ret;
44855682Smarkm    krb5_principal source;
449178825Sdfr    hdb_entry_ex target_ent;
45055682Smarkm    krb5_data value;
45155682Smarkm    off_t off;
45255682Smarkm    size_t princ_len, data_len;
45355682Smarkm
454178825Sdfr    memset(&target_ent, 0, sizeof(target_ent));
455178825Sdfr
456102644Snectar    off = krb5_storage_seek(sp, 0, SEEK_CUR);
457178825Sdfr    ret = krb5_ret_principal (sp, &source);
458178825Sdfr    if (ret) {
459178825Sdfr	krb5_set_error_string(context->context, "Failed to read renamed "
460178825Sdfr			      "principal in log, version: %ld", (long)ver);
461178825Sdfr	return ret;
462178825Sdfr    }
463102644Snectar    princ_len = krb5_storage_seek(sp, 0, SEEK_CUR) - off;
46455682Smarkm    data_len = len - princ_len;
465120945Snectar    ret = krb5_data_alloc (&value, data_len);
466120945Snectar    if (ret) {
467120945Snectar	krb5_free_principal (context->context, source);
468120945Snectar	return ret;
469120945Snectar    }
470102644Snectar    krb5_storage_read (sp, value.data, data_len);
471178825Sdfr    ret = hdb_value2entry (context->context, &value, &target_ent.entry);
47255682Smarkm    krb5_data_free(&value);
47355682Smarkm    if (ret) {
47455682Smarkm	krb5_free_principal (context->context, source);
47555682Smarkm	return ret;
47655682Smarkm    }
477178825Sdfr    ret = context->db->hdb_store (context->context, context->db,
478178825Sdfr				  0, &target_ent);
47955682Smarkm    hdb_free_entry (context->context, &target_ent);
48055682Smarkm    if (ret) {
48155682Smarkm	krb5_free_principal (context->context, source);
48255682Smarkm	return ret;
48355682Smarkm    }
484178825Sdfr    ret = context->db->hdb_remove (context->context, context->db, source);
48555682Smarkm    krb5_free_principal (context->context, source);
48655682Smarkm    return ret;
48755682Smarkm}
48855682Smarkm
48955682Smarkm
49055682Smarkm/*
49155682Smarkm * Add a `modify' operation to the log.
49255682Smarkm */
49355682Smarkm
49455682Smarkmkadm5_ret_t
49555682Smarkmkadm5_log_modify (kadm5_server_context *context,
49655682Smarkm		  hdb_entry *ent,
497178825Sdfr		  uint32_t mask)
49855682Smarkm{
49955682Smarkm    krb5_storage *sp;
50055682Smarkm    kadm5_ret_t ret;
50155682Smarkm    krb5_data value;
502178825Sdfr    uint32_t len;
50355682Smarkm    kadm5_log_context *log_context = &context->log_context;
50455682Smarkm
505178825Sdfr    krb5_data_zero(&value);
506178825Sdfr
50755682Smarkm    sp = krb5_storage_emem();
50855682Smarkm    ret = hdb_entry2value (context->context, ent, &value);
509178825Sdfr    if (ret)
510178825Sdfr	goto failed;
511178825Sdfr
51255682Smarkm    ret = kadm5_log_preamble (context, sp, kadm_modify);
513178825Sdfr    if (ret)
514178825Sdfr	goto failed;
515178825Sdfr
51655682Smarkm    len = value.length + 4;
517178825Sdfr    ret = krb5_store_int32 (sp, len);
518178825Sdfr    if (ret)
519178825Sdfr	goto failed;
520178825Sdfr    ret = krb5_store_int32 (sp, mask);
521178825Sdfr    if (ret)
522178825Sdfr	goto failed;
523102644Snectar    krb5_storage_write (sp, value.data, value.length);
524178825Sdfr
525178825Sdfr    ret = krb5_store_int32 (sp, len);
526178825Sdfr    if (ret)
527178825Sdfr	goto failed;
52855682Smarkm    ret = kadm5_log_postamble (log_context, sp);
529178825Sdfr    if (ret)
530178825Sdfr	goto failed;
53155682Smarkm    ret = kadm5_log_flush (log_context, sp);
532178825Sdfr    if (ret)
533178825Sdfr	goto failed;
534178825Sdfr    krb5_data_free(&value);
53555682Smarkm    krb5_storage_free (sp);
536178825Sdfr    return kadm5_log_end (context);
537178825Sdfrfailed:
538178825Sdfr    krb5_data_free(&value);
539178825Sdfr    krb5_storage_free(sp);
54055682Smarkm    return ret;
54155682Smarkm}
54255682Smarkm
54355682Smarkm/*
54455682Smarkm * Read a `modify' log operation from `sp' and apply it.
54555682Smarkm */
54655682Smarkm
547178825Sdfrstatic kadm5_ret_t
54855682Smarkmkadm5_log_replay_modify (kadm5_server_context *context,
549178825Sdfr			 uint32_t ver,
550178825Sdfr			 uint32_t len,
55155682Smarkm			 krb5_storage *sp)
55255682Smarkm{
55355682Smarkm    krb5_error_code ret;
55455682Smarkm    int32_t mask;
55555682Smarkm    krb5_data value;
556178825Sdfr    hdb_entry_ex ent, log_ent;
55755682Smarkm
558178825Sdfr    memset(&log_ent, 0, sizeof(log_ent));
559178825Sdfr
56055682Smarkm    krb5_ret_int32 (sp, &mask);
56155682Smarkm    len -= 4;
562120945Snectar    ret = krb5_data_alloc (&value, len);
563178825Sdfr    if (ret) {
564178825Sdfr	krb5_set_error_string(context->context, "out of memory");
565120945Snectar	return ret;
566178825Sdfr    }
567102644Snectar    krb5_storage_read (sp, value.data, len);
568178825Sdfr    ret = hdb_value2entry (context->context, &value, &log_ent.entry);
56955682Smarkm    krb5_data_free(&value);
57055682Smarkm    if (ret)
57155682Smarkm	return ret;
572178825Sdfr
573178825Sdfr    memset(&ent, 0, sizeof(ent));
574178825Sdfr    ret = context->db->hdb_fetch(context->context, context->db,
575178825Sdfr				 log_ent.entry.principal,
576178825Sdfr				 HDB_F_DECRYPT|HDB_F_GET_ANY, &ent);
57755682Smarkm    if (ret)
578178825Sdfr	goto out;
57955682Smarkm    if (mask & KADM5_PRINC_EXPIRE_TIME) {
580178825Sdfr	if (log_ent.entry.valid_end == NULL) {
581178825Sdfr	    ent.entry.valid_end = NULL;
58272445Sassar	} else {
583178825Sdfr	    if (ent.entry.valid_end == NULL) {
584178825Sdfr		ent.entry.valid_end = malloc(sizeof(*ent.entry.valid_end));
585178825Sdfr		if (ent.entry.valid_end == NULL) {
586178825Sdfr		    krb5_set_error_string(context->context, "out of memory");
587178825Sdfr		    ret = ENOMEM;
588178825Sdfr		    goto out;
589178825Sdfr		}
590178825Sdfr	    }
591178825Sdfr	    *ent.entry.valid_end = *log_ent.entry.valid_end;
59272445Sassar	}
59355682Smarkm    }
59455682Smarkm    if (mask & KADM5_PW_EXPIRATION) {
595178825Sdfr	if (log_ent.entry.pw_end == NULL) {
596178825Sdfr	    ent.entry.pw_end = NULL;
59772445Sassar	} else {
598178825Sdfr	    if (ent.entry.pw_end == NULL) {
599178825Sdfr		ent.entry.pw_end = malloc(sizeof(*ent.entry.pw_end));
600178825Sdfr		if (ent.entry.pw_end == NULL) {
601178825Sdfr		    krb5_set_error_string(context->context, "out of memory");
602178825Sdfr		    ret = ENOMEM;
603178825Sdfr		    goto out;
604178825Sdfr		}
605178825Sdfr	    }
606178825Sdfr	    *ent.entry.pw_end = *log_ent.entry.pw_end;
60772445Sassar	}
60855682Smarkm    }
60955682Smarkm    if (mask & KADM5_LAST_PWD_CHANGE) {
61055682Smarkm	abort ();		/* XXX */
61155682Smarkm    }
61255682Smarkm    if (mask & KADM5_ATTRIBUTES) {
613178825Sdfr	ent.entry.flags = log_ent.entry.flags;
61455682Smarkm    }
61555682Smarkm    if (mask & KADM5_MAX_LIFE) {
616178825Sdfr	if (log_ent.entry.max_life == NULL) {
617178825Sdfr	    ent.entry.max_life = NULL;
61872445Sassar	} else {
619178825Sdfr	    if (ent.entry.max_life == NULL) {
620178825Sdfr		ent.entry.max_life = malloc (sizeof(*ent.entry.max_life));
621178825Sdfr		if (ent.entry.max_life == NULL) {
622178825Sdfr		    krb5_set_error_string(context->context, "out of memory");
623178825Sdfr		    ret = ENOMEM;
624178825Sdfr		    goto out;
625178825Sdfr		}
626178825Sdfr	    }
627178825Sdfr	    *ent.entry.max_life = *log_ent.entry.max_life;
62872445Sassar	}
62955682Smarkm    }
63055682Smarkm    if ((mask & KADM5_MOD_TIME) && (mask & KADM5_MOD_NAME)) {
631178825Sdfr	if (ent.entry.modified_by == NULL) {
632178825Sdfr	    ent.entry.modified_by = malloc(sizeof(*ent.entry.modified_by));
633178825Sdfr	    if (ent.entry.modified_by == NULL) {
634178825Sdfr		krb5_set_error_string(context->context, "out of memory");
635178825Sdfr		ret = ENOMEM;
636178825Sdfr		goto out;
637178825Sdfr	    }
63855682Smarkm	} else
639178825Sdfr	    free_Event(ent.entry.modified_by);
640178825Sdfr	ret = copy_Event(log_ent.entry.modified_by, ent.entry.modified_by);
641178825Sdfr	if (ret) {
642178825Sdfr	    krb5_set_error_string(context->context, "out of memory");
643178825Sdfr	    goto out;
644178825Sdfr	}
64555682Smarkm    }
64655682Smarkm    if (mask & KADM5_KVNO) {
647178825Sdfr	ent.entry.kvno = log_ent.entry.kvno;
64855682Smarkm    }
64955682Smarkm    if (mask & KADM5_MKVNO) {
65055682Smarkm	abort ();		/* XXX */
65155682Smarkm    }
65255682Smarkm    if (mask & KADM5_AUX_ATTRIBUTES) {
65355682Smarkm	abort ();		/* XXX */
65455682Smarkm    }
65555682Smarkm    if (mask & KADM5_POLICY) {
65655682Smarkm	abort ();		/* XXX */
65755682Smarkm    }
65855682Smarkm    if (mask & KADM5_POLICY_CLR) {
65955682Smarkm	abort ();		/* XXX */
66055682Smarkm    }
66155682Smarkm    if (mask & KADM5_MAX_RLIFE) {
662178825Sdfr	if (log_ent.entry.max_renew == NULL) {
663178825Sdfr	    ent.entry.max_renew = NULL;
66472445Sassar	} else {
665178825Sdfr	    if (ent.entry.max_renew == NULL) {
666178825Sdfr		ent.entry.max_renew = malloc (sizeof(*ent.entry.max_renew));
667178825Sdfr		if (ent.entry.max_renew == NULL) {
668178825Sdfr		    krb5_set_error_string(context->context, "out of memory");
669178825Sdfr		    ret = ENOMEM;
670178825Sdfr		    goto out;
671178825Sdfr		}
672178825Sdfr	    }
673178825Sdfr	    *ent.entry.max_renew = *log_ent.entry.max_renew;
67472445Sassar	}
67555682Smarkm    }
67655682Smarkm    if (mask & KADM5_LAST_SUCCESS) {
67755682Smarkm	abort ();		/* XXX */
67855682Smarkm    }
67955682Smarkm    if (mask & KADM5_LAST_FAILED) {
68055682Smarkm	abort ();		/* XXX */
68155682Smarkm    }
68255682Smarkm    if (mask & KADM5_FAIL_AUTH_COUNT) {
68355682Smarkm	abort ();		/* XXX */
68455682Smarkm    }
68555682Smarkm    if (mask & KADM5_KEY_DATA) {
686178825Sdfr	size_t num;
68755682Smarkm	int i;
68855682Smarkm
689178825Sdfr	for (i = 0; i < ent.entry.keys.len; ++i)
690178825Sdfr	    free_Key(&ent.entry.keys.val[i]);
691178825Sdfr	free (ent.entry.keys.val);
69255682Smarkm
693178825Sdfr	num = log_ent.entry.keys.len;
69455682Smarkm
695178825Sdfr	ent.entry.keys.len = num;
696178825Sdfr	ent.entry.keys.val = malloc(len * sizeof(*ent.entry.keys.val));
697178825Sdfr	if (ent.entry.keys.val == NULL) {
698178825Sdfr	    krb5_set_error_string(context->context, "out of memory");
699178825Sdfr	    return ENOMEM;
700178825Sdfr	}
701178825Sdfr	for (i = 0; i < ent.entry.keys.len; ++i) {
702178825Sdfr	    ret = copy_Key(&log_ent.entry.keys.val[i],
703178825Sdfr			   &ent.entry.keys.val[i]);
704178825Sdfr	    if (ret) {
705178825Sdfr		krb5_set_error_string(context->context, "out of memory");
706178825Sdfr		goto out;
707178825Sdfr	    }
708178825Sdfr	}
70955682Smarkm    }
710178825Sdfr    if ((mask & KADM5_TL_DATA) && log_ent.entry.extensions) {
711178825Sdfr	HDB_extensions *es = ent.entry.extensions;
712178825Sdfr
713178825Sdfr	ent.entry.extensions = calloc(1, sizeof(*ent.entry.extensions));
714178825Sdfr	if (ent.entry.extensions == NULL)
715178825Sdfr	    goto out;
716178825Sdfr
717178825Sdfr	ret = copy_HDB_extensions(log_ent.entry.extensions,
718178825Sdfr				  ent.entry.extensions);
719178825Sdfr	if (ret) {
720178825Sdfr	    krb5_set_error_string(context->context, "out of memory");
721178825Sdfr	    free(ent.entry.extensions);
722178825Sdfr	    ent.entry.extensions = es;
723178825Sdfr	    goto out;
724178825Sdfr	}
725178825Sdfr	if (es) {
726178825Sdfr	    free_HDB_extensions(es);
727178825Sdfr	    free(es);
728178825Sdfr	}
729178825Sdfr    }
730178825Sdfr    ret = context->db->hdb_store(context->context, context->db,
731178825Sdfr				 HDB_F_REPLACE, &ent);
732178825Sdfr out:
73355682Smarkm    hdb_free_entry (context->context, &ent);
73455682Smarkm    hdb_free_entry (context->context, &log_ent);
73555682Smarkm    return ret;
73655682Smarkm}
73755682Smarkm
73855682Smarkm/*
739178825Sdfr * Add a `nop' operation to the log. Does not close the log.
74072445Sassar */
74172445Sassar
74272445Sassarkadm5_ret_t
74372445Sassarkadm5_log_nop (kadm5_server_context *context)
74472445Sassar{
74572445Sassar    krb5_storage *sp;
74672445Sassar    kadm5_ret_t ret;
74772445Sassar    kadm5_log_context *log_context = &context->log_context;
74872445Sassar
74972445Sassar    sp = krb5_storage_emem();
75072445Sassar    ret = kadm5_log_preamble (context, sp, kadm_nop);
75172445Sassar    if (ret) {
75272445Sassar	krb5_storage_free (sp);
75372445Sassar	return ret;
75472445Sassar    }
75572445Sassar    krb5_store_int32 (sp, 0);
75672445Sassar    krb5_store_int32 (sp, 0);
75772445Sassar    ret = kadm5_log_postamble (log_context, sp);
75872445Sassar    if (ret) {
75972445Sassar	krb5_storage_free (sp);
76072445Sassar	return ret;
76172445Sassar    }
76272445Sassar    ret = kadm5_log_flush (log_context, sp);
76372445Sassar    krb5_storage_free (sp);
764178825Sdfr
76572445Sassar    return ret;
76672445Sassar}
76772445Sassar
76872445Sassar/*
76972445Sassar * Read a `nop' log operation from `sp' and apply it.
77072445Sassar */
77172445Sassar
772178825Sdfrstatic kadm5_ret_t
77372445Sassarkadm5_log_replay_nop (kadm5_server_context *context,
774178825Sdfr		      uint32_t ver,
775178825Sdfr		      uint32_t len,
77672445Sassar		      krb5_storage *sp)
77772445Sassar{
77872445Sassar    return 0;
77972445Sassar}
78072445Sassar
78172445Sassar/*
78255682Smarkm * Call `func' for each log record in the log in `context'
78355682Smarkm */
78455682Smarkm
78555682Smarkmkadm5_ret_t
78655682Smarkmkadm5_log_foreach (kadm5_server_context *context,
78755682Smarkm		   void (*func)(kadm5_server_context *server_context,
788178825Sdfr				uint32_t ver,
78955682Smarkm				time_t timestamp,
79055682Smarkm				enum kadm_ops op,
791178825Sdfr				uint32_t len,
792178825Sdfr				krb5_storage *,
793178825Sdfr				void *),
794178825Sdfr		   void *ctx)
79555682Smarkm{
79655682Smarkm    int fd = context->log_context.log_fd;
79755682Smarkm    krb5_storage *sp;
79855682Smarkm
79955682Smarkm    lseek (fd, 0, SEEK_SET);
80055682Smarkm    sp = krb5_storage_from_fd (fd);
80155682Smarkm    for (;;) {
802178825Sdfr	int32_t ver, timestamp, op, len, len2, ver2;
80355682Smarkm
80455682Smarkm	if(krb5_ret_int32 (sp, &ver) != 0)
80555682Smarkm	    break;
80655682Smarkm	krb5_ret_int32 (sp, &timestamp);
80755682Smarkm	krb5_ret_int32 (sp, &op);
80855682Smarkm	krb5_ret_int32 (sp, &len);
809178825Sdfr	(*func)(context, ver, timestamp, op, len, sp, ctx);
810178825Sdfr	krb5_ret_int32 (sp, &len2);
811178825Sdfr	krb5_ret_int32 (sp, &ver2);
812178825Sdfr	if (len != len2)
813178825Sdfr	    abort();
814178825Sdfr	if (ver != ver2)
815178825Sdfr	    abort();
81655682Smarkm    }
817178825Sdfr    krb5_storage_free(sp);
81855682Smarkm    return 0;
81955682Smarkm}
82055682Smarkm
82155682Smarkm/*
82255682Smarkm * Go to end of log.
82355682Smarkm */
82455682Smarkm
82555682Smarkmkrb5_storage *
82655682Smarkmkadm5_log_goto_end (int fd)
82755682Smarkm{
82855682Smarkm    krb5_storage *sp;
82955682Smarkm
83055682Smarkm    sp = krb5_storage_from_fd (fd);
831102644Snectar    krb5_storage_seek(sp, 0, SEEK_END);
83255682Smarkm    return sp;
83355682Smarkm}
83455682Smarkm
83555682Smarkm/*
83655682Smarkm * Return previous log entry.
837178825Sdfr *
838178825Sdfr * The pointer in `sp� is assumed to be at the top of the entry before
839178825Sdfr * previous entry. On success, the `sp� pointer is set to data portion
840178825Sdfr * of previous entry. In case of error, it's not changed at all.
84155682Smarkm */
84255682Smarkm
84355682Smarkmkadm5_ret_t
844178825Sdfrkadm5_log_previous (krb5_context context,
845178825Sdfr		    krb5_storage *sp,
846178825Sdfr		    uint32_t *ver,
84755682Smarkm		    time_t *timestamp,
84855682Smarkm		    enum kadm_ops *op,
849178825Sdfr		    uint32_t *len)
85055682Smarkm{
851178825Sdfr    krb5_error_code ret;
852178825Sdfr    off_t off, oldoff;
85355682Smarkm    int32_t tmp;
85455682Smarkm
855178825Sdfr    oldoff = krb5_storage_seek(sp, 0, SEEK_CUR);
856178825Sdfr
857102644Snectar    krb5_storage_seek(sp, -8, SEEK_CUR);
858178825Sdfr    ret = krb5_ret_int32 (sp, &tmp);
859178825Sdfr    if (ret)
860178825Sdfr	goto end_of_storage;
86155682Smarkm    *len = tmp;
862178825Sdfr    ret = krb5_ret_int32 (sp, &tmp);
86355682Smarkm    *ver = tmp;
86455682Smarkm    off = 24 + *len;
865102644Snectar    krb5_storage_seek(sp, -off, SEEK_CUR);
866178825Sdfr    ret = krb5_ret_int32 (sp, &tmp);
867178825Sdfr    if (ret)
868178825Sdfr	goto end_of_storage;
869178825Sdfr    if (tmp != *ver) {
870178825Sdfr	krb5_storage_seek(sp, oldoff, SEEK_SET);
871178825Sdfr	krb5_set_error_string(context, "kadm5_log_previous: log entry "
872178825Sdfr			      "have consistency failure, version number wrong");
873178825Sdfr	return KADM5_BAD_DB;
874178825Sdfr    }
875178825Sdfr    ret = krb5_ret_int32 (sp, &tmp);
876178825Sdfr    if (ret)
877178825Sdfr	goto end_of_storage;
87855682Smarkm    *timestamp = tmp;
879178825Sdfr    ret = krb5_ret_int32 (sp, &tmp);
88055682Smarkm    *op = tmp;
881178825Sdfr    ret = krb5_ret_int32 (sp, &tmp);
882178825Sdfr    if (ret)
883178825Sdfr	goto end_of_storage;
884178825Sdfr    if (tmp != *len) {
885178825Sdfr	krb5_storage_seek(sp, oldoff, SEEK_SET);
886178825Sdfr	krb5_set_error_string(context, "kadm5_log_previous: log entry "
887178825Sdfr			      "have consistency failure, length wrong");
888178825Sdfr	return KADM5_BAD_DB;
889178825Sdfr    }
89055682Smarkm    return 0;
891178825Sdfr
892178825Sdfr end_of_storage:
893178825Sdfr    krb5_storage_seek(sp, oldoff, SEEK_SET);
894178825Sdfr    krb5_set_error_string(context, "kadm5_log_previous: end of storage "
895178825Sdfr			  "reached before end");
896178825Sdfr    return ret;
89755682Smarkm}
89855682Smarkm
89955682Smarkm/*
90055682Smarkm * Replay a record from the log
90155682Smarkm */
90255682Smarkm
90355682Smarkmkadm5_ret_t
90455682Smarkmkadm5_log_replay (kadm5_server_context *context,
90555682Smarkm		  enum kadm_ops op,
906178825Sdfr		  uint32_t ver,
907178825Sdfr		  uint32_t len,
90855682Smarkm		  krb5_storage *sp)
90955682Smarkm{
91055682Smarkm    switch (op) {
91155682Smarkm    case kadm_create :
91255682Smarkm	return kadm5_log_replay_create (context, ver, len, sp);
91355682Smarkm    case kadm_delete :
91455682Smarkm	return kadm5_log_replay_delete (context, ver, len, sp);
91555682Smarkm    case kadm_rename :
91655682Smarkm	return kadm5_log_replay_rename (context, ver, len, sp);
91755682Smarkm    case kadm_modify :
91855682Smarkm	return kadm5_log_replay_modify (context, ver, len, sp);
91972445Sassar    case kadm_nop :
92072445Sassar	return kadm5_log_replay_nop (context, ver, len, sp);
92155682Smarkm    default :
922178825Sdfr	krb5_set_error_string(context->context,
923178825Sdfr			      "Unsupported replay op %d", (int)op);
92455682Smarkm	return KADM5_FAILURE;
92555682Smarkm    }
92655682Smarkm}
92772445Sassar
92872445Sassar/*
92972445Sassar * truncate the log - i.e. create an empty file with just (nop vno + 2)
93072445Sassar */
93172445Sassar
93272445Sassarkadm5_ret_t
93372445Sassarkadm5_log_truncate (kadm5_server_context *server_context)
93472445Sassar{
93572445Sassar    kadm5_ret_t ret;
936178825Sdfr    uint32_t vno;
93772445Sassar
93872445Sassar    ret = kadm5_log_init (server_context);
93972445Sassar    if (ret)
94072445Sassar	return ret;
94172445Sassar
94272445Sassar    ret = kadm5_log_get_version (server_context, &vno);
94372445Sassar    if (ret)
94472445Sassar	return ret;
94572445Sassar
94672445Sassar    ret = kadm5_log_reinit (server_context);
94772445Sassar    if (ret)
94872445Sassar	return ret;
94972445Sassar
950178825Sdfr    ret = kadm5_log_set_version (server_context, vno);
95172445Sassar    if (ret)
95272445Sassar	return ret;
95372445Sassar
95472445Sassar    ret = kadm5_log_nop (server_context);
95572445Sassar    if (ret)
95672445Sassar	return ret;
95772445Sassar
95872445Sassar    ret = kadm5_log_end (server_context);
95972445Sassar    if (ret)
96072445Sassar	return ret;
96172445Sassar    return 0;
96272445Sassar
96372445Sassar}
964178825Sdfr
965178825Sdfrstatic char *default_signal = NULL;
966178825Sdfrstatic HEIMDAL_MUTEX signal_mutex = HEIMDAL_MUTEX_INITIALIZER;
967178825Sdfr
968178825Sdfrconst char *
969178825Sdfrkadm5_log_signal_socket(krb5_context context)
970178825Sdfr{
971178825Sdfr    HEIMDAL_MUTEX_lock(&signal_mutex);
972178825Sdfr    if (!default_signal)
973178825Sdfr	asprintf(&default_signal, "%s/signal", hdb_db_dir(context));
974178825Sdfr    HEIMDAL_MUTEX_unlock(&signal_mutex);
975178825Sdfr
976178825Sdfr    return krb5_config_get_string_default(context,
977178825Sdfr					  NULL,
978178825Sdfr					  default_signal,
979178825Sdfr					  "kdc",
980178825Sdfr					  "signal_socket",
981178825Sdfr					  NULL);
982178825Sdfr}
983