root/usr/src/lib/krb5/kdb/kdb_log.c
/*
 * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <k5-int.h>
#include <stdlib.h>
#include <limits.h>
#include <syslog.h>
#include "kdb_log.h"

/*
 * This modules includes all the necessary functions that create and
 * modify the Kerberos principal update and header logs.
 */

#define getpagesize()   sysconf(_SC_PAGESIZE)

static int              pagesize = 0;

#define INIT_ULOG(ctx)  log_ctx = ctx->kdblog_context; \
                        ulog = log_ctx->ulog
/*
 * Sync update entry to disk.
 */
krb5_error_code
ulog_sync_update(kdb_hlog_t *ulog, kdb_ent_header_t *upd)
{
        ulong_t         start, end, size;
        krb5_error_code retval;

        if (ulog == NULL)
                return (KRB5_LOG_ERROR);

        if (!pagesize)
                pagesize = getpagesize();

        start = ((ulong_t)upd) & (~(pagesize-1));

        end = (((ulong_t)upd) + ulog->kdb_block +
            (pagesize-1)) & (~(pagesize-1));

        size = end - start;
        if (retval = msync((caddr_t)start, size, MS_SYNC)) {
                return (retval);
        }

        return (0);
}

/*
 * Sync memory to disk for the update log header.
 */
void
ulog_sync_header(kdb_hlog_t *ulog)
{

        if (!pagesize)
                pagesize = getpagesize();

        if (msync((caddr_t)ulog, pagesize, MS_SYNC)) {
                /*
                 * Couldn't sync to disk, let's panic
                 */
                syslog(LOG_ERR, "ulog_sync_header: could not sync to disk");
                abort();
        }
}

/*
 * Resizes the array elements.  We reinitialize the update log rather than
 * unrolling the the log and copying it over to a temporary log for obvious
 * performance reasons.  Slaves will subsequently do a full resync, but
 * the need for resizing should be very small.
 */
krb5_error_code
ulog_resize(kdb_hlog_t *ulog, uint32_t ulogentries, int ulogfd, uint_t recsize)
{
        uint_t          new_block, new_size;

        if (ulog == NULL)
                return (KRB5_LOG_ERROR);

        new_size = sizeof (kdb_hlog_t);

        new_block = (recsize / ULOG_BLOCK) + 1;
        new_block *= ULOG_BLOCK;

        new_size += ulogentries * new_block;

        if (new_size <= MAXLOGLEN) {
                /*
                 * Reinit log with new block size
                 */
                (void) memset(ulog, 0, sizeof (kdb_hlog_t));

                ulog->kdb_hmagic = KDB_HMAGIC;
                ulog->db_version_num = KDB_VERSION;
                ulog->kdb_state = KDB_STABLE;
                ulog->kdb_block = new_block;

                ulog_sync_header(ulog);

                /*
                 * Time to expand log considering new block size
                 */
                if (lseek(ulogfd, new_size, SEEK_SET) == -1) {
                        return (errno);
                }

                if (write(ulogfd, "+", 1) != 1) {
                        return (errno);
                }
        } else {
                /*
                 * Can't map into file larger than MAXLOGLEN
                 */
                return (KRB5_LOG_ERROR);
        }

        return (0);
}

/*
 * Adds an entry to the update log.
 * The layout of the update log looks like:
 *
 * header log -> [ update header -> xdr(kdb_incr_update_t) ], ...
 */
krb5_error_code
ulog_add_update(krb5_context context, kdb_incr_update_t *upd)
{
        XDR             xdrs;
        kdbe_time_t     ktime;
        struct timeval  timestamp;
        kdb_ent_header_t *indx_log;
        uint_t          i, recsize;
        ulong_t         upd_size;
        krb5_error_code retval;
        kdb_sno_t       cur_sno;
        kdb_log_context *log_ctx;
        kdb_hlog_t      *ulog = NULL;
        uint32_t        ulogentries;
        int             ulogfd;

        INIT_ULOG(context);
        ulogentries = log_ctx->ulogentries;
        ulogfd = log_ctx->ulogfd;

        if (upd == NULL)
                return (KRB5_LOG_ERROR);

        (void) gettimeofday(&timestamp, NULL);
        ktime.seconds = timestamp.tv_sec;
        ktime.useconds = timestamp.tv_usec;

        upd_size = xdr_sizeof((xdrproc_t)xdr_kdb_incr_update_t, upd);

        recsize = sizeof (kdb_ent_header_t) + upd_size;

        if (recsize > ulog->kdb_block) {
                if (retval = ulog_resize(ulog, ulogentries, ulogfd, recsize)) {
                        /* Resize element array failed */
                        return (retval);
                }
        }

        cur_sno = ulog->kdb_last_sno;

        /*
         * We need to overflow our sno, replicas will do full
         * resyncs once they see their sno > than the masters.
         */
        if (cur_sno == ULONG_MAX)
                cur_sno = 1;
        else
                cur_sno++;

        /*
         * We squirrel this away for finish_update() to index
         */
        upd->kdb_entry_sno = cur_sno;

        i = (cur_sno - 1) % ulogentries;

        indx_log = (kdb_ent_header_t *)INDEX(ulog, i);

        (void) memset(indx_log, 0, ulog->kdb_block);

        indx_log->kdb_umagic = KDB_UMAGIC;
        indx_log->kdb_entry_size = upd_size;
        indx_log->kdb_entry_sno = cur_sno;
        indx_log->kdb_time = upd->kdb_time = ktime;
        indx_log->kdb_commit = upd->kdb_commit = FALSE;

        ulog->kdb_state = KDB_UNSTABLE;

        xdrmem_create(&xdrs, (char *)indx_log->entry_data,
            indx_log->kdb_entry_size, XDR_ENCODE);
        if (!xdr_kdb_incr_update_t(&xdrs, upd))
                return (KRB5_LOG_CONV);

        if (retval = ulog_sync_update(ulog, indx_log))
                return (retval);

        if (ulog->kdb_num < ulogentries)
                ulog->kdb_num++;

        ulog->kdb_last_sno = cur_sno;
        ulog->kdb_last_time = ktime;

        /*
         * Since this is a circular array, once we circled, kdb_first_sno is
         * always kdb_entry_sno + 1.
         */
        if (cur_sno > ulogentries) {
                i = upd->kdb_entry_sno % ulogentries;
                indx_log = (kdb_ent_header_t *)INDEX(ulog, i);
                ulog->kdb_first_sno = indx_log->kdb_entry_sno;
                ulog->kdb_first_time = indx_log->kdb_time;
        } else if (cur_sno == 1) {
                ulog->kdb_first_sno = 1;
                ulog->kdb_first_time = indx_log->kdb_time;
        }

        ulog_sync_header(ulog);

        return (0);
}

/*
 * Mark the log entry as committed and sync the memory mapped log
 * to file.
 */
krb5_error_code
ulog_finish_update(krb5_context context, kdb_incr_update_t *upd)
{
        krb5_error_code         retval;
        kdb_ent_header_t        *indx_log;
        uint_t                  i;
        kdb_log_context         *log_ctx;
        kdb_hlog_t              *ulog = NULL;
        uint32_t                ulogentries;

        INIT_ULOG(context);
        ulogentries = log_ctx->ulogentries;

        i = (upd->kdb_entry_sno - 1) % ulogentries;

        indx_log = (kdb_ent_header_t *)INDEX(ulog, i);

        indx_log->kdb_commit = TRUE;

        ulog->kdb_state = KDB_STABLE;

        if (retval = ulog_sync_update(ulog, indx_log))
                return (retval);

        ulog_sync_header(ulog);

        return (0);
}

/*
 * Set the header log details on the slave and sync it to file.
 */
void
ulog_finish_update_slave(kdb_hlog_t *ulog, kdb_last_t lastentry)
{

        ulog->kdb_last_sno = lastentry.last_sno;
        ulog->kdb_last_time = lastentry.last_time;

        ulog_sync_header(ulog);
}

/*
 * Delete an entry to the update log.
 */
krb5_error_code
ulog_delete_update(krb5_context context, kdb_incr_update_t *upd)
{

        upd->kdb_deleted = TRUE;

        return (ulog_add_update(context, upd));
}

/*
 * Used by the slave or master (during ulog_check) to update it's hash db from
 * the incr update log.
 */
krb5_error_code
ulog_replay(krb5_context context, kdb_incr_result_t *incr_ret)
{
        krb5_db_entry           *entry = NULL;
        kdb_incr_update_t       *upd = NULL, *fupd;
        int                     i, no_of_updates;
        krb5_error_code         retval;
        krb5_principal          dbprinc = NULL;
        kdb_last_t              errlast;
        char                    *dbprincstr = NULL;
        kdb_log_context         *log_ctx;
        kdb_hlog_t              *ulog = NULL;
        bool_t                  fini = FALSE;

        INIT_ULOG(context);

        no_of_updates = incr_ret->updates.kdb_ulog_t_len;
        upd = incr_ret->updates.kdb_ulog_t_val;
        fupd = upd;

        /*
         * We reset last_sno and last_time to 0, if krb5_db_put_principal
         * or krb5_db_delete_principal fail.
         */
        errlast.last_sno = (unsigned int)0;
        errlast.last_time.seconds = (unsigned int)0;
        errlast.last_time.useconds = (unsigned int)0;

        if (krb5_db_inited(context)) {
                retval = krb5_db_open(context, NULL,
                    KRB5_KDB_OPEN_RW | KRB5_KDB_SRV_TYPE_ADMIN);
                if (retval != 0)
                        goto cleanup;
                fini = TRUE;
        }

        for (i = 0; i < no_of_updates; i++) {
                int nentry = 1;

                if (!upd->kdb_commit)
                        continue;

                if (upd->kdb_deleted) {
                        dbprincstr = malloc((upd->kdb_princ_name.utf8str_t_len
                            + 1) * sizeof (char));

                        if (dbprincstr == NULL) {
                                retval = ENOMEM;
                                goto cleanup;
                        }

                        (void) strlcpy(dbprincstr,
                            (char *)upd->kdb_princ_name.utf8str_t_val,
                            (upd->kdb_princ_name.utf8str_t_len + 1));

                        if (retval = krb5_parse_name(context, dbprincstr,
                            &dbprinc)) {
                                goto cleanup;
                        }

                        if (dbprincstr)
                                free(dbprincstr);

                        retval = krb5_db_delete_principal(context,
                            dbprinc, &nentry);

                        if (dbprinc)
                                krb5_free_principal(context, dbprinc);

                        if (retval)
                                goto cleanup;
                } else {
                        entry = (krb5_db_entry *)malloc(sizeof (krb5_db_entry));

                        if (!entry) {
                                retval = errno;
                                goto cleanup;
                        }

                        (void) memset(entry, 0, sizeof (krb5_db_entry));

                        if (retval = ulog_conv_2dbentry(context, entry, upd, 1))
                                goto cleanup;

                        retval = krb5_db_put_principal(context, entry,
                            &nentry);

                        if (entry) {
                                krb5_db_free_principal(context, entry, nentry);
                                free(entry);
                                entry = NULL;
                        }
                        if (retval)
                                goto cleanup;
                }

                upd++;
        }

cleanup:
        if (fupd)
                ulog_free_entries(fupd, no_of_updates);

        if (log_ctx && (log_ctx->iproprole == IPROP_SLAVE)) {
                if (retval)
                        ulog_finish_update_slave(ulog, errlast);
                else
                        ulog_finish_update_slave(ulog, incr_ret->lastentry);
        }

        if (fini == TRUE)
                krb5_db_fini(context);

        return (retval);
}

/*
 * Validate the log file and resync any uncommitted update entries
 * to the principal database.
 */
krb5_error_code
ulog_check(krb5_context context, kdb_hlog_t *ulog)
{
        XDR                     xdrs;
        krb5_error_code         retval = 0;
        int                     i;
        kdb_ent_header_t        *indx_log;
        kdb_incr_update_t       *upd = NULL;
        kdb_incr_result_t       *incr_ret = NULL;

        ulog->kdb_state = KDB_STABLE;

        for (i = 0; i < ulog->kdb_num; i++) {
                indx_log = (kdb_ent_header_t *)INDEX(ulog, i);

                if (indx_log->kdb_umagic != KDB_UMAGIC) {
                        /*
                         * Update entry corrupted we should scream and die
                         */
                        ulog->kdb_state = KDB_CORRUPT;
                        retval = KRB5_LOG_CORRUPT;
                        break;
                }

                if (indx_log->kdb_commit == FALSE) {
                        ulog->kdb_state = KDB_UNSTABLE;

                        incr_ret = (kdb_incr_result_t *)
                            malloc(sizeof (kdb_incr_result_t));
                        if (incr_ret == NULL) {
                                retval = errno;
                                goto error;
                        }

                        upd = (kdb_incr_update_t *)
                            malloc(sizeof (kdb_incr_update_t));
                        if (upd == NULL) {
                                retval = errno;
                                goto error;
                        }

                        (void) memset(upd, 0, sizeof (kdb_incr_update_t));
                        xdrmem_create(&xdrs, (char *)indx_log->entry_data,
                            indx_log->kdb_entry_size, XDR_DECODE);
                        if (!xdr_kdb_incr_update_t(&xdrs, upd)) {
                                retval = KRB5_LOG_CONV;
                                goto error;
                        }

                        incr_ret->updates.kdb_ulog_t_len = 1;
                        incr_ret->updates.kdb_ulog_t_val = upd;

                        upd->kdb_commit = TRUE;

                        /*
                         * We don't want to readd this update and just use the
                         * existing update to be propagated later on
                         */
                        ulog_set_role(context, IPROP_NULL);
                        retval = ulog_replay(context, incr_ret);

                        /*
                         * upd was freed by ulog_replay, we NULL
                         * the pointer in case we subsequently break from loop.
                         */
                        upd = NULL;
                        if (incr_ret) {
                                free(incr_ret);
                                incr_ret = NULL;
                        }
                        ulog_set_role(context, IPROP_MASTER);

                        if (retval)
                                goto error;

                        /*
                         * We flag this as committed since this was
                         * the last entry before kadmind crashed, ergo
                         * the slaves have not seen this update before
                         */
                        indx_log->kdb_commit = TRUE;
                        retval = ulog_sync_update(ulog, indx_log);
                        if (retval)
                                goto error;

                        ulog->kdb_state = KDB_STABLE;
                }
        }

error:
        if (upd)
                ulog_free_entries(upd, 1);

        if (incr_ret)
                free(incr_ret);

        ulog_sync_header(ulog);

        return (retval);
}

/*
 * Map the log file to memory for performance and simplicity.
 *
 * Called by: if iprop_enabled then ulog_map();
 * Assumes that the caller will terminate on ulog_map, hence munmap and
 * closing of the fd are implicitly performed by the caller.
 * Returns 0 on success else failure.
 */
krb5_error_code
ulog_map(krb5_context context, kadm5_config_params *params, int caller)
{
        struct stat     st;
        krb5_error_code retval;
        uint32_t        ulog_filesize;
        char            logname[MAX_FILENAME];
        kdb_log_context *log_ctx;
        kdb_hlog_t      *ulog = NULL;
        uint32_t        ulogentries;
        int             ulogfd = -1;

        if ((caller == FKADMIND) || (caller == FKCOMMAND))
                ulogentries = params->iprop_ulogsize;

        ulog_filesize = sizeof (kdb_hlog_t);

        if (strlcpy(logname, params->dbname, MAX_FILENAME) >= MAX_FILENAME)
                return (KRB5_LOG_ERROR);
        if (strlcat(logname, ".ulog", MAX_FILENAME) >= MAX_FILENAME)
                return (KRB5_LOG_ERROR);

        if (stat(logname, &st) == -1) {

                if (caller == FKPROPLOG) {
                        /*
                         * File doesn't exist so we exit with kproplog
                         */
                        return (errno);
                }

                if ((ulogfd = open(logname, O_RDWR+O_CREAT, 0600)) == -1) {
                        return (errno);
                }

                if (lseek(ulogfd, 0L, SEEK_CUR) == -1) {
                        return (errno);
                }

                if ((caller == FKADMIND) || (caller == FKCOMMAND))
                        ulog_filesize += ulogentries * ULOG_BLOCK;

                if (lseek(ulogfd, ulog_filesize, SEEK_SET) == -1) {
                        return (errno);
                }

                if (write(ulogfd, "+", 1) != 1) {
                        return (errno);
                }

        } else {

                if ((ulogfd = open(logname, O_RDWR, 0600)) == -1) {
                        /*
                         * Can't open existing log file
                         */
                        return (errno);
                }
        }

        if (caller == FKPROPLOG) {
                fstat(ulogfd, &st);
                ulog_filesize = st.st_size;

                ulog = (kdb_hlog_t *)mmap(0, ulog_filesize,
                    PROT_READ+PROT_WRITE, MAP_PRIVATE, ulogfd, 0);
        } else {
                /*
                 * else kadmind, kpropd, & kcommands should udpate stores
                 */
                ulog = (kdb_hlog_t *)mmap(0, MAXLOGLEN,
                    PROT_READ+PROT_WRITE, MAP_SHARED, ulogfd, 0);
        }

        if ((int)(ulog) == -1) {
                /*
                 * Can't map update log file to memory
                 */
                return (errno);
        }

        if (!context->kdblog_context) {
                if (!(log_ctx = malloc(sizeof (kdb_log_context))))
                        return (errno);
                context->kdblog_context = (void *)log_ctx;
        } else
                log_ctx = context->kdblog_context;
        log_ctx->ulog = ulog;
        log_ctx->ulogentries = ulogentries;
        log_ctx->ulogfd = ulogfd;

        if (ulog->kdb_hmagic != KDB_HMAGIC) {
                if (ulog->kdb_hmagic == 0) {
                        /*
                         * New update log
                         */
                        (void) memset(ulog, 0, sizeof (kdb_hlog_t));

                        ulog->kdb_hmagic = KDB_HMAGIC;
                        ulog->db_version_num = KDB_VERSION;
                        ulog->kdb_state = KDB_STABLE;
                        ulog->kdb_block = ULOG_BLOCK;
                        if (!(caller == FKPROPLOG))
                                ulog_sync_header(ulog);
                } else {
                        return (KRB5_LOG_CORRUPT);
                }
        }

        if (caller == FKADMIND) {
                switch (ulog->kdb_state) {
                        case KDB_STABLE:
                        case KDB_UNSTABLE:
                                /*
                                 * Log is currently un/stable, check anyway
                                 */
                                retval = ulog_check(context, ulog);
                                if (retval == KRB5_LOG_CORRUPT) {
                                        return (retval);
                                }
                                break;
                        case KDB_CORRUPT:
                                return (KRB5_LOG_CORRUPT);
                        default:
                                /*
                                 * Invalid db state
                                 */
                                return (KRB5_LOG_ERROR);
                }
        } else if ((caller == FKPROPLOG) || (caller == FKPROPD)) {
                /*
                 * kproplog and kpropd don't need to do anything else
                 */
                return (0);
        }

        /*
         * Reinit ulog if the log is being truncated or expanded after
         * we have circled.
         */
        if (ulog->kdb_num != ulogentries) {
                if ((ulog->kdb_num != 0) &&
                    ((ulog->kdb_last_sno > ulog->kdb_num) ||
                    (ulog->kdb_num > ulogentries))) {
                        (void) memset(ulog, 0, sizeof (kdb_hlog_t));

                        ulog->kdb_hmagic = KDB_HMAGIC;
                        ulog->db_version_num = KDB_VERSION;
                        ulog->kdb_state = KDB_STABLE;
                        ulog->kdb_block = ULOG_BLOCK;

                        ulog_sync_header(ulog);
                }

                /*
                 * Expand ulog if we have specified a greater size
                 */
                if (ulog->kdb_num < ulogentries) {
                        ulog_filesize += ulogentries * ulog->kdb_block;

                        if (lseek(ulogfd, ulog_filesize, SEEK_SET) == -1) {
                                return (errno);
                        }

                        if (write(ulogfd, "+", 1) != 1) {
                                return (errno);
                        }
                }
        }

        return (0);
}

/*
 * Get the last set of updates seen, (last+1) to n is returned.
 */
krb5_error_code
ulog_get_entries(
        krb5_context context,           /* input - krb5 lib config */
        kdb_last_t last,                /* input - slave's last sno */
        kdb_incr_result_t *ulog_handle) /* output - incr result for slave */
{
        XDR                     xdrs;
        kdb_ent_header_t        *indx_log;
        kdb_incr_update_t       *upd;
        uint_t                  indx, count, tdiff;
        uint32_t                sno;
        krb5_error_code         retval;
        struct timeval          timestamp;
        kdb_log_context         *log_ctx;
        kdb_hlog_t              *ulog = NULL;
        uint32_t                ulogentries;

        INIT_ULOG(context);
        ulogentries = log_ctx->ulogentries;

        /*
         * Check to make sure we don't have a corrupt ulog first.
         */
        if (ulog->kdb_state == KDB_CORRUPT) {
                ulog_handle->ret = UPDATE_ERROR;
                return (KRB5_LOG_CORRUPT);
        }

        gettimeofday(&timestamp, NULL);

        tdiff = timestamp.tv_sec - ulog->kdb_last_time.seconds;
        if (tdiff <= ULOG_IDLE_TIME) {
                ulog_handle->ret = UPDATE_BUSY;
                return (0);
        }

        /*
         * We need to lock out other processes here, such as kadmin.local,
         * since we are looking at the last_sno and looking up updates.  So
         * we can share with other readers.
         */
        retval = krb5_db_lock(context, KRB5_LOCKMODE_SHARED);
        if (retval)
                return (retval);

        /*
         * We may have overflowed the update log or we shrunk the log, or
         * the client's ulog has just been created.
         */
        if ((last.last_sno > ulog->kdb_last_sno) ||
            (last.last_sno < ulog->kdb_first_sno) ||
            (last.last_sno == 0)) {
                ulog_handle->lastentry.last_sno = ulog->kdb_last_sno;
                (void) krb5_db_unlock(context);
                ulog_handle->ret = UPDATE_FULL_RESYNC_NEEDED;
                return (0);
        } else if (last.last_sno <= ulog->kdb_last_sno) {
                sno = last.last_sno;

                indx = (sno - 1) % ulogentries;

                indx_log = (kdb_ent_header_t *)INDEX(ulog, indx);

                /*
                 * Validate the time stamp just to make sure it was the same sno
                 */
                if ((indx_log->kdb_time.seconds == last.last_time.seconds) &&
                    (indx_log->kdb_time.useconds == last.last_time.useconds)) {

                        /*
                         * If we have the same sno we return success
                         */
                        if (last.last_sno == ulog->kdb_last_sno) {
                                (void) krb5_db_unlock(context);
                                ulog_handle->ret = UPDATE_NIL;
                                return (0);
                        }

                        count = ulog->kdb_last_sno - sno;

                        ulog_handle->updates.kdb_ulog_t_val =
                            (kdb_incr_update_t *)malloc(
                            sizeof (kdb_incr_update_t) * count);

                        upd = ulog_handle->updates.kdb_ulog_t_val;

                        if (upd == NULL) {
                                (void) krb5_db_unlock(context);
                                ulog_handle->ret = UPDATE_ERROR;
                                return (errno);
                        }

                        while (sno < ulog->kdb_last_sno) {
                                indx = sno % ulogentries;

                                indx_log = (kdb_ent_header_t *)
                                    INDEX(ulog, indx);

                                (void) memset(upd, 0,
                                    sizeof (kdb_incr_update_t));
                                xdrmem_create(&xdrs,
                                    (char *)indx_log->entry_data,
                                    indx_log->kdb_entry_size, XDR_DECODE);
                                if (!xdr_kdb_incr_update_t(&xdrs, upd)) {
                                        (void) krb5_db_unlock(context);
                                        ulog_handle->ret = UPDATE_ERROR;
                                        return (KRB5_LOG_CONV);
                                }
                                /*
                                 * Mark commitment since we didn't
                                 * want to decode and encode the
                                 * incr update record the first time.
                                 */
                                upd->kdb_commit = indx_log->kdb_commit;

                                upd++;
                                sno++;
                        } /* while */

                        ulog_handle->updates.kdb_ulog_t_len = count;

                        ulog_handle->lastentry.last_sno = ulog->kdb_last_sno;
                        ulog_handle->lastentry.last_time.seconds =
                            ulog->kdb_last_time.seconds;
                        ulog_handle->lastentry.last_time.useconds =
                            ulog->kdb_last_time.useconds;
                        ulog_handle->ret = UPDATE_OK;

                        (void) krb5_db_unlock(context);

                        return (0);
                } else {
                        /*
                         * We have time stamp mismatch or we no longer have
                         * the slave's last sno, so we brute force it
                         */
                        (void) krb5_db_unlock(context);
                        ulog_handle->ret = UPDATE_FULL_RESYNC_NEEDED;

                        return (0);
                }
        }

        /*
         * Should never get here, return error
         */
        ulog_handle->ret = UPDATE_ERROR;
        return (KRB5_LOG_ERROR);
}

krb5_error_code
ulog_set_role(krb5_context ctx, iprop_role role)
{
        kdb_log_context *log_ctx;

        if (!ctx->kdblog_context) {
                if (!(log_ctx = malloc(sizeof (kdb_log_context))))
                        return (errno);
                ctx->kdblog_context = (void *)log_ctx;
        } else
                log_ctx = ctx->kdblog_context;

        log_ctx->iproprole = role;

        return (0);
}