xref: /freebsd/crypto/krb5/src/lib/kdb/kdb_log.c (revision f1c4c3daccbaf3820f0e2224de53df12fc952fcc)
1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /*
3  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
4  * Use is subject to license terms.
5  */
6 
7 #include <sys/stat.h>
8 #include <sys/types.h>
9 #include <unistd.h>
10 #include <fcntl.h>
11 #include <sys/mman.h>
12 #include <k5-int.h>
13 #include <stdlib.h>
14 #include <limits.h>
15 #include <syslog.h>
16 #include "kdb5.h"
17 #include "kdb_log.h"
18 #include "kdb5int.h"
19 
20 #ifndef MAP_FAILED
21 #define MAP_FAILED ((void *)-1)
22 #endif
23 
24 /* This module includes all the necessary functions that create and modify the
25  * Kerberos principal update and header logs. */
26 
27 #define getpagesize() sysconf(_SC_PAGESIZE)
28 
29 static int pagesize = 0;
30 
31 #define INIT_ULOG(ctx)                          \
32     log_ctx = ctx->kdblog_context;              \
33     assert(log_ctx != NULL);                    \
34     ulog = log_ctx->ulog;                       \
35     assert(ulog != NULL)
36 
37 /* Initialize context->kdblog_context if it does not yet exist, and return it.
38  * Return NULL on allocation failure. */
39 static kdb_log_context *
create_log_context(krb5_context context)40 create_log_context(krb5_context context)
41 {
42     kdb_log_context *log_ctx;
43 
44     if (context->kdblog_context != NULL)
45         return context->kdblog_context;
46     log_ctx = calloc(1, sizeof(*log_ctx));
47     if (log_ctx == NULL)
48         return NULL;
49     log_ctx->ulogfd = -1;
50     context->kdblog_context = log_ctx;
51     return log_ctx;
52 }
53 
54 static inline krb5_boolean
time_equal(const kdbe_time_t * a,const kdbe_time_t * b)55 time_equal(const kdbe_time_t *a, const kdbe_time_t *b)
56 {
57     return a->seconds == b->seconds && a->useconds == b->useconds;
58 }
59 
60 static void
time_current(kdbe_time_t * out)61 time_current(kdbe_time_t *out)
62 {
63     struct timeval timestamp;
64 
65     (void)gettimeofday(&timestamp, NULL);
66     out->seconds = timestamp.tv_sec;
67     out->useconds = timestamp.tv_usec;
68 }
69 
70 /* Sync update entry to disk. */
71 static void
sync_update(kdb_hlog_t * ulog,kdb_ent_header_t * upd)72 sync_update(kdb_hlog_t *ulog, kdb_ent_header_t *upd)
73 {
74     unsigned long start, end, size;
75 
76     if (!pagesize)
77         pagesize = getpagesize();
78 
79     start = (unsigned long)upd & ~(pagesize - 1);
80 
81     end = ((unsigned long)upd + ulog->kdb_block + (pagesize - 1)) &
82         ~(pagesize - 1);
83 
84     size = end - start;
85     if (msync((caddr_t)start, size, MS_SYNC)) {
86         /* Couldn't sync to disk, let's panic. */
87         syslog(LOG_ERR, _("could not sync ulog update to disk"));
88         abort();
89     }
90 }
91 
92 /* Sync memory to disk for the update log header. */
93 static void
sync_header(kdb_hlog_t * ulog)94 sync_header(kdb_hlog_t *ulog)
95 {
96     if (!pagesize)
97         pagesize = getpagesize();
98 
99     if (msync((caddr_t)ulog, pagesize, MS_SYNC)) {
100         /* Couldn't sync to disk, let's panic. */
101         syslog(LOG_ERR, _("could not sync ulog header to disk"));
102         abort();
103     }
104 }
105 
106 /* Sync memory to disk for the entire ulog. */
107 static void
sync_ulog(kdb_hlog_t * ulog,uint32_t ulogentries)108 sync_ulog(kdb_hlog_t *ulog, uint32_t ulogentries)
109 {
110     size_t len;
111 
112     if (!pagesize)
113         pagesize = getpagesize();
114 
115     len = (sizeof(kdb_hlog_t) + ulogentries * ulog->kdb_block +
116            (pagesize - 1)) & ~(pagesize - 1);
117     if (msync(ulog, len, MS_SYNC)) {
118         /* Couldn't sync to disk, let's panic. */
119         syslog(LOG_ERR, _("could not sync the whole ulog to disk"));
120         abort();
121     }
122 }
123 
124 /* Return true if the ulog entry for sno matches sno and timestamp. */
125 static krb5_boolean
check_sno(kdb_log_context * log_ctx,kdb_sno_t sno,const kdbe_time_t * timestamp)126 check_sno(kdb_log_context *log_ctx, kdb_sno_t sno,
127           const kdbe_time_t *timestamp)
128 {
129     unsigned int indx = (sno - 1) % log_ctx->ulogentries;
130     kdb_ent_header_t *ent = ulog_index(log_ctx->ulog, indx);
131 
132     return ent->kdb_entry_sno == sno && time_equal(&ent->kdb_time, timestamp);
133 }
134 
135 /*
136  * Check last against our ulog and determine whether it is up to date
137  * (UPDATE_NIL), so far out of date that a full dump is required
138  * (UPDATE_FULL_RESYNC_NEEDED), or okay to update with ulog entries
139  * (UPDATE_OK).
140  */
141 static update_status_t
get_sno_status(kdb_log_context * log_ctx,const kdb_last_t * last)142 get_sno_status(kdb_log_context *log_ctx, const kdb_last_t *last)
143 {
144     kdb_hlog_t *ulog = log_ctx->ulog;
145 
146     /* If last matches the ulog's last serial number and time exactly, it are
147      * up to date even if the ulog is empty. */
148     if (last->last_sno == ulog->kdb_last_sno &&
149         time_equal(&last->last_time, &ulog->kdb_last_time))
150         return UPDATE_NIL;
151 
152     /* If our ulog is empty or does not contain last_sno, a full resync is
153      * required. */
154     if (ulog->kdb_num == 0 || last->last_sno > ulog->kdb_last_sno ||
155         last->last_sno < ulog->kdb_first_sno)
156         return UPDATE_FULL_RESYNC_NEEDED;
157 
158     /* If the timestamp in our ulog entry does not match last, then sno was
159      * reused and a full resync is required. */
160     if (!check_sno(log_ctx, last->last_sno, &last->last_time))
161         return UPDATE_FULL_RESYNC_NEEDED;
162 
163     /* last is not fully up to date, but can be updated using our ulog. */
164     return UPDATE_OK;
165 }
166 
167 /* Extend update log file. */
168 static krb5_error_code
extend_file_to(int fd,unsigned int new_size)169 extend_file_to(int fd, unsigned int new_size)
170 {
171     off_t current_offset;
172     static const char zero[512];
173     ssize_t wrote_size;
174     size_t write_size;
175 
176     current_offset = lseek(fd, 0, SEEK_END);
177     if (current_offset < 0)
178         return errno;
179     if (new_size > INT_MAX)
180         return EINVAL;
181     while (current_offset < (off_t)new_size) {
182         write_size = new_size - current_offset;
183         if (write_size > 512)
184             write_size = 512;
185         wrote_size = write(fd, zero, write_size);
186         if (wrote_size < 0)
187             return errno;
188         if (wrote_size == 0)
189             return EINVAL;
190         current_offset += wrote_size;
191         write_size = new_size - current_offset;
192     }
193     return 0;
194 }
195 
196 /* Resize the array elements of ulog to be at least as large as recsize.  Move
197  * the existing elements into the proper offsets for the new block size. */
198 static krb5_error_code
resize(kdb_hlog_t * ulog,uint32_t ulogentries,int ulogfd,unsigned int recsize,const kdb_incr_update_t * upd)199 resize(kdb_hlog_t *ulog, uint32_t ulogentries, int ulogfd,
200        unsigned int recsize, const kdb_incr_update_t *upd)
201 {
202     size_t old_block = ulog->kdb_block, new_block, new_size;
203     krb5_error_code retval;
204     uint8_t *old_ent, *new_ent;
205     uint32_t i;
206 
207     if (ulog == NULL)
208         return KRB5_LOG_ERROR;
209 
210     new_size = sizeof(kdb_hlog_t);
211     new_block = (recsize / ULOG_BLOCK) + 1;
212     new_block *= ULOG_BLOCK;
213     new_size += ulogentries * new_block;
214 
215     if (new_block > UINT16_MAX) {
216         syslog(LOG_ERR, _("ulog overflow caused by principal %.*s"),
217                upd->kdb_princ_name.utf8str_t_len,
218                upd->kdb_princ_name.utf8str_t_val);
219         return KRB5_LOG_ERROR;
220     }
221     if (new_size > MAXLOGLEN)
222         return KRB5_LOG_ERROR;
223 
224     /* Expand log considering new block size. */
225     retval = extend_file_to(ulogfd, new_size);
226     if (retval)
227         return retval;
228 
229     /* Copy each record into its new location and zero out the unused areas.
230      * The area is overlapping, so we have to iterate backwards. */
231     for (i = ulogentries; i > 0; i--) {
232         old_ent = ulog_record_ptr(ulog, i - 1, old_block);
233         new_ent = ulog_record_ptr(ulog, i - 1, new_block);
234         memmove(new_ent, old_ent, old_block);
235         memset(new_ent + old_block, 0, new_block - old_block);
236     }
237 
238     syslog(LOG_INFO, _("ulog block size has been resized from %lu to %lu"),
239            (unsigned long)old_block, (unsigned long)new_block);
240     ulog->kdb_block = new_block;
241     sync_ulog(ulog, ulogentries);
242     return 0;
243 }
244 
245 /* Set the ulog to contain only a dummy entry with the given serial number and
246  * timestamp. */
247 static void
set_dummy(kdb_log_context * log_ctx,kdb_sno_t sno,const kdbe_time_t * kdb_time)248 set_dummy(kdb_log_context *log_ctx, kdb_sno_t sno, const kdbe_time_t *kdb_time)
249 {
250     kdb_hlog_t *ulog = log_ctx->ulog;
251     kdb_ent_header_t *ent = ulog_index(ulog, (sno - 1) % log_ctx->ulogentries);
252 
253     memset(ent, 0, sizeof(*ent));
254     ent->kdb_umagic = KDB_ULOG_MAGIC;
255     ent->kdb_entry_sno = sno;
256     ent->kdb_time = *kdb_time;
257     sync_update(ulog, ent);
258 
259     ulog->kdb_num = 1;
260     ulog->kdb_first_sno = ulog->kdb_last_sno = sno;
261     ulog->kdb_first_time = ulog->kdb_last_time = *kdb_time;
262 }
263 
264 /* Reinitialize the ulog header, starting from sno 1 with the current time. */
265 static void
reset_ulog(kdb_log_context * log_ctx)266 reset_ulog(kdb_log_context *log_ctx)
267 {
268     kdbe_time_t kdb_time;
269     kdb_hlog_t *ulog = log_ctx->ulog;
270 
271     memset(ulog, 0, sizeof(*ulog));
272     ulog->kdb_hmagic = KDB_ULOG_HDR_MAGIC;
273     ulog->db_version_num = KDB_VERSION;
274     ulog->kdb_block = ULOG_BLOCK;
275 
276     /* Create a dummy entry to remember the timestamp for downstreams. */
277     time_current(&kdb_time);
278     set_dummy(log_ctx, 1, &kdb_time);
279     ulog->kdb_state = KDB_STABLE;
280     sync_header(ulog);
281 }
282 
283 /*
284  * If any database operations will be invoked while the ulog lock is held, the
285  * caller must explicitly lock the database before locking the ulog, or
286  * deadlock may result.
287  */
288 static krb5_error_code
lock_ulog(krb5_context context,int mode)289 lock_ulog(krb5_context context, int mode)
290 {
291     kdb_log_context *log_ctx = NULL;
292     kdb_hlog_t *ulog = NULL;
293 
294     INIT_ULOG(context);
295     return krb5_lock_file(context, log_ctx->ulogfd, mode);
296 }
297 
298 static void
unlock_ulog(krb5_context context)299 unlock_ulog(krb5_context context)
300 {
301     (void)lock_ulog(context, KRB5_LOCKMODE_UNLOCK);
302 }
303 
304 /*
305  * Add an update to the log.  The update's kdb_entry_sno and kdb_time fields
306  * must already be set.  The layout of the update log looks like:
307  *
308  * header log -> [ update header -> xdr(kdb_incr_update_t) ], ...
309  */
310 static krb5_error_code
store_update(kdb_log_context * log_ctx,kdb_incr_update_t * upd)311 store_update(kdb_log_context *log_ctx, kdb_incr_update_t *upd)
312 {
313     XDR xdrs;
314     kdb_ent_header_t *indx_log;
315     unsigned int i, recsize;
316     unsigned long upd_size;
317     krb5_error_code retval;
318     kdb_hlog_t *ulog = log_ctx->ulog;
319     uint32_t ulogentries = log_ctx->ulogentries;
320 
321     upd_size = xdr_sizeof((xdrproc_t)xdr_kdb_incr_update_t, upd);
322 
323     recsize = sizeof(kdb_ent_header_t) + upd_size;
324 
325     if (recsize > ulog->kdb_block) {
326         retval = resize(ulog, ulogentries, log_ctx->ulogfd, recsize, upd);
327         if (retval)
328             return retval;
329     }
330 
331     ulog->kdb_state = KDB_UNSTABLE;
332 
333     i = (upd->kdb_entry_sno - 1) % ulogentries;
334     indx_log = ulog_index(ulog, i);
335 
336     memset(indx_log, 0, ulog->kdb_block);
337     indx_log->kdb_umagic = KDB_ULOG_MAGIC;
338     indx_log->kdb_entry_size = upd_size;
339     indx_log->kdb_entry_sno = upd->kdb_entry_sno;
340     indx_log->kdb_time = upd->kdb_time;
341     indx_log->kdb_commit = FALSE;
342 
343     xdrmem_create(&xdrs, (char *)indx_log->entry_data,
344                   indx_log->kdb_entry_size, XDR_ENCODE);
345     if (!xdr_kdb_incr_update_t(&xdrs, upd))
346         return KRB5_LOG_CONV;
347 
348     indx_log->kdb_commit = TRUE;
349     sync_update(ulog, indx_log);
350 
351     /* Modify the ulog header to reflect the new update. */
352     ulog->kdb_last_sno = upd->kdb_entry_sno;
353     ulog->kdb_last_time = upd->kdb_time;
354     if (ulog->kdb_num == 0) {
355         /* We should only see this in old ulogs. */
356         ulog->kdb_num = 1;
357         ulog->kdb_first_sno = upd->kdb_entry_sno;
358         ulog->kdb_first_time = upd->kdb_time;
359     } else if (ulog->kdb_num < ulogentries) {
360         ulog->kdb_num++;
361     } else {
362         /* We are circling; set kdb_first_sno and time to the next update. */
363         i = upd->kdb_entry_sno % ulogentries;
364         indx_log = ulog_index(ulog, i);
365         ulog->kdb_first_sno = indx_log->kdb_entry_sno;
366         ulog->kdb_first_time = indx_log->kdb_time;
367     }
368 
369     ulog->kdb_state = KDB_STABLE;
370     sync_header(ulog);
371     return 0;
372 }
373 
374 /* Add an entry to the update log. */
375 krb5_error_code
ulog_add_update(krb5_context context,kdb_incr_update_t * upd)376 ulog_add_update(krb5_context context, kdb_incr_update_t *upd)
377 {
378     krb5_error_code ret;
379     kdb_log_context *log_ctx;
380     kdb_hlog_t *ulog;
381 
382     INIT_ULOG(context);
383     ret = lock_ulog(context, KRB5_LOCKMODE_EXCLUSIVE);
384     if (ret)
385         return ret;
386 
387     /* If we have reached the last possible serial number, reinitialize the
388      * ulog and start over.  Replicas will do a full resync. */
389     if (ulog->kdb_last_sno == (kdb_sno_t)-1)
390         reset_ulog(log_ctx);
391 
392     upd->kdb_entry_sno = ulog->kdb_last_sno + 1;
393     time_current(&upd->kdb_time);
394     ret = store_update(log_ctx, upd);
395     unlock_ulog(context);
396     return ret;
397 }
398 
399 /* Used by the replica to update its hash db from the incr update log. */
400 krb5_error_code
ulog_replay(krb5_context context,kdb_incr_result_t * incr_ret,char ** db_args)401 ulog_replay(krb5_context context, kdb_incr_result_t *incr_ret, char **db_args)
402 {
403     krb5_db_entry *entry = NULL;
404     kdb_incr_update_t *upd = NULL, *fupd;
405     int i, no_of_updates;
406     krb5_error_code retval;
407     krb5_principal dbprinc;
408     char *dbprincstr;
409     kdb_log_context *log_ctx;
410     kdb_hlog_t *ulog = NULL;
411 
412     INIT_ULOG(context);
413 
414     retval = krb5_db_open(context, db_args,
415                           KRB5_KDB_OPEN_RW | KRB5_KDB_SRV_TYPE_ADMIN);
416     if (retval)
417         return retval;
418 
419     no_of_updates = incr_ret->updates.kdb_ulog_t_len;
420     upd = incr_ret->updates.kdb_ulog_t_val;
421     fupd = upd;
422 
423     for (i = 0; i < no_of_updates; i++) {
424         if (!upd->kdb_commit)
425             continue;
426 
427         /* Replay this update in the database. */
428         if (upd->kdb_deleted) {
429             dbprincstr = k5memdup0(upd->kdb_princ_name.utf8str_t_val,
430                                    upd->kdb_princ_name.utf8str_t_len, &retval);
431             if (dbprincstr == NULL)
432                 goto cleanup;
433 
434             retval = krb5_parse_name(context, dbprincstr, &dbprinc);
435             free(dbprincstr);
436             if (retval)
437                 goto cleanup;
438 
439             retval = krb5int_delete_principal_no_log(context, dbprinc);
440             krb5_free_principal(context, dbprinc);
441             if (retval == KRB5_KDB_NOENTRY)
442                 retval = 0;
443             if (retval)
444                 goto cleanup;
445         } else {
446             retval = ulog_conv_2dbentry(context, &entry, upd);
447             if (retval)
448                 goto cleanup;
449 
450             retval = krb5int_put_principal_no_log(context, entry);
451             krb5_db_free_principal(context, entry);
452             if (retval)
453                 goto cleanup;
454         }
455 
456         retval = lock_ulog(context, KRB5_LOCKMODE_EXCLUSIVE);
457         if (retval)
458             goto cleanup;
459 
460         /* If (unexpectedly) this update does not follow the last one we
461          * stored, discard any previous ulog state. */
462         if (ulog->kdb_num != 0 && upd->kdb_entry_sno != ulog->kdb_last_sno + 1)
463             reset_ulog(log_ctx);
464 
465         /* Store this update in the ulog for any downstream KDCs. */
466         retval = store_update(log_ctx, upd);
467         unlock_ulog(context);
468         if (retval)
469             goto cleanup;
470 
471         upd++;
472     }
473 
474 cleanup:
475     if (retval)
476         (void)ulog_init_header(context);
477     if (fupd)
478         ulog_free_entries(fupd, no_of_updates);
479     return retval;
480 }
481 
482 /* Reinitialize the log header. */
483 krb5_error_code
ulog_init_header(krb5_context context)484 ulog_init_header(krb5_context context)
485 {
486     krb5_error_code ret;
487     kdb_log_context *log_ctx;
488     kdb_hlog_t *ulog;
489 
490     INIT_ULOG(context);
491     ret = lock_ulog(context, KRB5_LOCKMODE_EXCLUSIVE);
492     if (ret)
493         return ret;
494     reset_ulog(log_ctx);
495     unlock_ulog(context);
496     return 0;
497 }
498 
499 /* Map the log file to memory for performance and simplicity. */
500 krb5_error_code
ulog_map(krb5_context context,const char * logname,uint32_t ulogentries)501 ulog_map(krb5_context context, const char *logname, uint32_t ulogentries)
502 {
503     struct stat st;
504     krb5_error_code retval;
505     uint32_t filesize;
506     kdb_log_context *log_ctx;
507     kdb_hlog_t *ulog = NULL;
508     krb5_boolean locked = FALSE;
509 
510     log_ctx = create_log_context(context);
511     if (log_ctx == NULL)
512         return ENOMEM;
513 
514     if (stat(logname, &st) == -1) {
515         log_ctx->ulogfd = open(logname, O_RDWR | O_CREAT, 0600);
516         if (log_ctx->ulogfd == -1) {
517             retval = errno;
518             goto cleanup;
519         }
520 
521         filesize = sizeof(kdb_hlog_t) + ulogentries * ULOG_BLOCK;
522         retval = extend_file_to(log_ctx->ulogfd, filesize);
523         if (retval)
524             goto cleanup;
525     } else {
526         log_ctx->ulogfd = open(logname, O_RDWR, 0600);
527         if (log_ctx->ulogfd == -1) {
528             retval = errno;
529             goto cleanup;
530         }
531     }
532 
533     ulog = mmap(0, MAXLOGLEN, PROT_READ | PROT_WRITE, MAP_SHARED,
534                 log_ctx->ulogfd, 0);
535     if (ulog == MAP_FAILED) {
536         retval = errno;
537         goto cleanup;
538     }
539     log_ctx->ulog = ulog;
540     log_ctx->ulogentries = ulogentries;
541 
542     retval = lock_ulog(context, KRB5_LOCKMODE_EXCLUSIVE);
543     if (retval)
544         goto cleanup;
545     locked = TRUE;
546 
547     if (ulog->kdb_hmagic != KDB_ULOG_HDR_MAGIC) {
548         if (ulog->kdb_hmagic != 0) {
549             retval = KRB5_LOG_CORRUPT;
550             goto cleanup;
551         }
552         reset_ulog(log_ctx);
553     }
554 
555     /* Reinit ulog if ulogentries changed such that we have too many entries or
556      * our first or last entry was written to the wrong location. */
557     if (ulog->kdb_num != 0 &&
558         (ulog->kdb_num > ulogentries ||
559          !check_sno(log_ctx, ulog->kdb_first_sno, &ulog->kdb_first_time) ||
560          !check_sno(log_ctx, ulog->kdb_last_sno, &ulog->kdb_last_time)))
561         reset_ulog(log_ctx);
562 
563     if (ulog->kdb_num != ulogentries) {
564         /* Expand the ulog file if it isn't big enough. */
565         filesize = sizeof(kdb_hlog_t) + ulogentries * ulog->kdb_block;
566         retval = extend_file_to(log_ctx->ulogfd, filesize);
567         if (retval)
568             goto cleanup;
569     }
570 
571 cleanup:
572     if (locked)
573         unlock_ulog(context);
574     if (retval)
575         ulog_fini(context);
576     return retval;
577 }
578 
579 /* Get the last set of updates seen, (last+1) to n is returned. */
580 krb5_error_code
ulog_get_entries(krb5_context context,const kdb_last_t * last,kdb_incr_result_t * ulog_handle)581 ulog_get_entries(krb5_context context, const kdb_last_t *last,
582                  kdb_incr_result_t *ulog_handle)
583 {
584     XDR xdrs;
585     kdb_ent_header_t *indx_log;
586     kdb_incr_update_t *upd;
587     unsigned int indx, count;
588     uint32_t sno;
589     krb5_error_code retval;
590     kdb_log_context *log_ctx;
591     kdb_hlog_t *ulog = NULL;
592     uint32_t ulogentries;
593 
594     INIT_ULOG(context);
595     ulogentries = log_ctx->ulogentries;
596 
597     retval = lock_ulog(context, KRB5_LOCKMODE_SHARED);
598     if (retval)
599         return retval;
600 
601     /* If another process terminated mid-update, reset the ulog and force full
602      * resyncs. */
603     if (ulog->kdb_state != KDB_STABLE)
604         reset_ulog(log_ctx);
605 
606     ulog_handle->ret = get_sno_status(log_ctx, last);
607     if (ulog_handle->ret != UPDATE_OK)
608         goto cleanup;
609 
610     sno = last->last_sno;
611     count = ulog->kdb_last_sno - sno;
612     upd = calloc(count, sizeof(kdb_incr_update_t));
613     if (upd == NULL) {
614         ulog_handle->ret = UPDATE_ERROR;
615         retval = ENOMEM;
616         goto cleanup;
617     }
618     ulog_handle->updates.kdb_ulog_t_val = upd;
619 
620     for (; sno < ulog->kdb_last_sno; sno++) {
621         indx = sno % ulogentries;
622         indx_log = ulog_index(ulog, indx);
623 
624         memset(upd, 0, sizeof(kdb_incr_update_t));
625         xdrmem_create(&xdrs, (char *)indx_log->entry_data,
626                       indx_log->kdb_entry_size, XDR_DECODE);
627         if (!xdr_kdb_incr_update_t(&xdrs, upd)) {
628             ulog_handle->ret = UPDATE_ERROR;
629             retval = KRB5_LOG_CONV;
630             goto cleanup;
631         }
632 
633         /* Mark commitment since we didn't want to decode and encode the incr
634          * update record the first time. */
635         upd->kdb_commit = indx_log->kdb_commit;
636         upd++;
637     }
638 
639     ulog_handle->updates.kdb_ulog_t_len = count;
640 
641     ulog_handle->lastentry.last_sno = ulog->kdb_last_sno;
642     ulog_handle->lastentry.last_time.seconds = ulog->kdb_last_time.seconds;
643     ulog_handle->lastentry.last_time.useconds = ulog->kdb_last_time.useconds;
644     ulog_handle->ret = UPDATE_OK;
645 
646 cleanup:
647     unlock_ulog(context);
648     return retval;
649 }
650 
651 krb5_error_code
ulog_set_role(krb5_context ctx,iprop_role role)652 ulog_set_role(krb5_context ctx, iprop_role role)
653 {
654     if (create_log_context(ctx) == NULL)
655         return ENOMEM;
656     ctx->kdblog_context->iproprole = role;
657     return 0;
658 }
659 
660 update_status_t
ulog_get_sno_status(krb5_context context,const kdb_last_t * last)661 ulog_get_sno_status(krb5_context context, const kdb_last_t *last)
662 {
663     update_status_t status;
664 
665     if (lock_ulog(context, KRB5_LOCKMODE_SHARED) != 0)
666         return UPDATE_ERROR;
667     status = get_sno_status(context->kdblog_context, last);
668     unlock_ulog(context);
669     return status;
670 }
671 
672 krb5_error_code
ulog_get_last(krb5_context context,kdb_last_t * last_out)673 ulog_get_last(krb5_context context, kdb_last_t *last_out)
674 {
675     krb5_error_code ret;
676     kdb_log_context *log_ctx;
677     kdb_hlog_t *ulog;
678 
679     INIT_ULOG(context);
680     ret = lock_ulog(context, KRB5_LOCKMODE_SHARED);
681     if (ret)
682         return ret;
683     last_out->last_sno = log_ctx->ulog->kdb_last_sno;
684     last_out->last_time = log_ctx->ulog->kdb_last_time;
685     unlock_ulog(context);
686     return 0;
687 }
688 
689 krb5_error_code
ulog_set_last(krb5_context context,const kdb_last_t * last)690 ulog_set_last(krb5_context context, const kdb_last_t *last)
691 {
692     krb5_error_code ret;
693     kdb_log_context *log_ctx;
694     kdb_hlog_t *ulog;
695 
696     INIT_ULOG(context);
697     ret = lock_ulog(context, KRB5_LOCKMODE_EXCLUSIVE);
698     if (ret)
699         return ret;
700 
701     set_dummy(log_ctx, last->last_sno, &last->last_time);
702     sync_header(ulog);
703     unlock_ulog(context);
704     return 0;
705 }
706 
707 void
ulog_fini(krb5_context context)708 ulog_fini(krb5_context context)
709 {
710     kdb_log_context *log_ctx = context->kdblog_context;
711 
712     if (log_ctx == NULL)
713         return;
714     if (log_ctx->ulog != NULL)
715         munmap(log_ctx->ulog, MAXLOGLEN);
716     if (log_ctx->ulogfd != -1)
717         close(log_ctx->ulogfd);
718     free(log_ctx);
719     context->kdblog_context = NULL;
720 }
721