xref: /illumos-gate/usr/src/lib/krb5/kdb/kdb_log.c (revision 67c90040)
1 /*
2  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
3  * Use is subject to license terms.
4  */
5 
6 #pragma ident	"%Z%%M%	%I%	%E% SMI"
7 
8 #include <sys/stat.h>
9 #include <sys/types.h>
10 #include <unistd.h>
11 #include <fcntl.h>
12 #include <sys/mman.h>
13 #include <k5-int.h>
14 #include <stdlib.h>
15 #include <limits.h>
16 #include <syslog.h>
17 #include "kdb_log.h"
18 
19 /*
20  * This modules includes all the necessary functions that create and
21  * modify the Kerberos principal update and header logs.
22  */
23 
24 #define	getpagesize()	sysconf(_SC_PAGESIZE)
25 
26 static int		pagesize = 0;
27 
28 #define	INIT_ULOG(ctx)	log_ctx = ctx->kdblog_context; \
29 			ulog = log_ctx->ulog
30 /*
31  * Sync update entry to disk.
32  */
33 krb5_error_code
34 ulog_sync_update(kdb_hlog_t *ulog, kdb_ent_header_t *upd)
35 {
36 	ulong_t		start, end, size;
37 	krb5_error_code	retval;
38 
39 	if (ulog == NULL)
40 		return (KRB5_LOG_ERROR);
41 
42 	if (!pagesize)
43 		pagesize = getpagesize();
44 
45 	start = ((ulong_t)upd) & (~(pagesize-1));
46 
47 	end = (((ulong_t)upd) + ulog->kdb_block +
48 	    (pagesize-1)) & (~(pagesize-1));
49 
50 	size = end - start;
51 	if (retval = msync((caddr_t)start, size, MS_SYNC)) {
52 		return (retval);
53 	}
54 
55 	return (0);
56 }
57 
58 /*
59  * Sync memory to disk for the update log header.
60  */
61 void
62 ulog_sync_header(kdb_hlog_t *ulog)
63 {
64 
65 	if (!pagesize)
66 		pagesize = getpagesize();
67 
68 	if (msync((caddr_t)ulog, pagesize, MS_SYNC)) {
69 		/*
70 		 * Couldn't sync to disk, let's panic
71 		 */
72 		syslog(LOG_ERR, "ulog_sync_header: could not sync to disk");
73 		abort();
74 	}
75 }
76 
77 /*
78  * Resizes the array elements.  We reinitialize the update log rather than
79  * unrolling the the log and copying it over to a temporary log for obvious
80  * performance reasons.  Slaves will subsequently do a full resync, but
81  * the need for resizing should be very small.
82  */
83 krb5_error_code
84 ulog_resize(kdb_hlog_t *ulog, uint32_t ulogentries, int ulogfd, uint_t recsize)
85 {
86 	uint_t		new_block, new_size;
87 
88 	if (ulog == NULL)
89 		return (KRB5_LOG_ERROR);
90 
91 	new_size = sizeof (kdb_hlog_t);
92 
93 	new_block = (recsize / ULOG_BLOCK) + 1;
94 	new_block *= ULOG_BLOCK;
95 
96 	new_size += ulogentries * new_block;
97 
98 	if (new_size <= MAXLOGLEN) {
99 		/*
100 		 * Reinit log with new block size
101 		 */
102 		(void) memset(ulog, 0, sizeof (kdb_hlog_t));
103 
104 		ulog->kdb_hmagic = KDB_HMAGIC;
105 		ulog->db_version_num = KDB_VERSION;
106 		ulog->kdb_state = KDB_STABLE;
107 		ulog->kdb_block = new_block;
108 
109 		ulog_sync_header(ulog);
110 
111 		/*
112 		 * Time to expand log considering new block size
113 		 */
114 		if (lseek(ulogfd, new_size, SEEK_SET) == -1) {
115 			return (errno);
116 		}
117 
118 		if (write(ulogfd, "+", 1) != 1) {
119 			return (errno);
120 		}
121 	} else {
122 		/*
123 		 * Can't map into file larger than MAXLOGLEN
124 		 */
125 		return (KRB5_LOG_ERROR);
126 	}
127 
128 	return (0);
129 }
130 
131 /*
132  * Adds an entry to the update log.
133  * The layout of the update log looks like:
134  *
135  * header log -> [ update header -> xdr(kdb_incr_update_t) ], ...
136  */
137 krb5_error_code
138 ulog_add_update(krb5_context context, kdb_incr_update_t *upd)
139 {
140 	XDR		xdrs;
141 	kdbe_time_t	ktime;
142 	struct timeval	timestamp;
143 	kdb_ent_header_t *indx_log;
144 	uint_t		i, recsize;
145 	ulong_t		upd_size;
146 	krb5_error_code	retval;
147 	kdb_sno_t	cur_sno;
148 	kdb_log_context	*log_ctx;
149 	kdb_hlog_t	*ulog = NULL;
150 	uint32_t	ulogentries;
151 	int		ulogfd;
152 
153 	INIT_ULOG(context);
154 	ulogentries = log_ctx->ulogentries;
155 	ulogfd = log_ctx->ulogfd;
156 
157 	if (upd == NULL)
158 		return (KRB5_LOG_ERROR);
159 
160 	(void) gettimeofday(&timestamp, NULL);
161 	ktime.seconds = timestamp.tv_sec;
162 	ktime.useconds = timestamp.tv_usec;
163 
164 	upd_size = xdr_sizeof((xdrproc_t)xdr_kdb_incr_update_t, upd);
165 
166 	recsize = sizeof (kdb_ent_header_t) + upd_size;
167 
168 	if (recsize > ulog->kdb_block) {
169 		if (retval = ulog_resize(ulog, ulogentries, ulogfd, recsize)) {
170 			/* Resize element array failed */
171 			return (retval);
172 		}
173 	}
174 
175 	cur_sno = ulog->kdb_last_sno;
176 
177 	/*
178 	 * We need to overflow our sno, replicas will do full
179 	 * resyncs once they see their sno > than the masters.
180 	 */
181 	if (cur_sno == ULONG_MAX)
182 		cur_sno = 1;
183 	else
184 		cur_sno++;
185 
186 	/*
187 	 * We squirrel this away for finish_update() to index
188 	 */
189 	upd->kdb_entry_sno = cur_sno;
190 
191 	i = (cur_sno - 1) % ulogentries;
192 
193 	indx_log = (kdb_ent_header_t *)INDEX(ulog, i);
194 
195 	(void) memset(indx_log, 0, ulog->kdb_block);
196 
197 	indx_log->kdb_umagic = KDB_UMAGIC;
198 	indx_log->kdb_entry_size = upd_size;
199 	indx_log->kdb_entry_sno = cur_sno;
200 	indx_log->kdb_time = upd->kdb_time = ktime;
201 	indx_log->kdb_commit = upd->kdb_commit = FALSE;
202 
203 	ulog->kdb_state = KDB_UNSTABLE;
204 
205 	xdrmem_create(&xdrs, (char *)indx_log->entry_data,
206 	    indx_log->kdb_entry_size, XDR_ENCODE);
207 	if (!xdr_kdb_incr_update_t(&xdrs, upd))
208 		return (KRB5_LOG_CONV);
209 
210 	if (retval = ulog_sync_update(ulog, indx_log))
211 		return (retval);
212 
213 	if (ulog->kdb_num < ulogentries)
214 		ulog->kdb_num++;
215 
216 	ulog->kdb_last_sno = cur_sno;
217 	ulog->kdb_last_time = ktime;
218 
219 	/*
220 	 * Since this is a circular array, once we circled, kdb_first_sno is
221 	 * always kdb_entry_sno + 1.
222 	 */
223 	if (cur_sno > ulogentries) {
224 		i = upd->kdb_entry_sno % ulogentries;
225 		indx_log = (kdb_ent_header_t *)INDEX(ulog, i);
226 		ulog->kdb_first_sno = indx_log->kdb_entry_sno;
227 		ulog->kdb_first_time = indx_log->kdb_time;
228 	} else if (cur_sno == 1) {
229 		ulog->kdb_first_sno = 1;
230 		ulog->kdb_first_time = indx_log->kdb_time;
231 	}
232 
233 	ulog_sync_header(ulog);
234 
235 	return (0);
236 }
237 
238 /*
239  * Mark the log entry as committed and sync the memory mapped log
240  * to file.
241  */
242 krb5_error_code
243 ulog_finish_update(krb5_context context, kdb_incr_update_t *upd)
244 {
245 	krb5_error_code		retval;
246 	kdb_ent_header_t	*indx_log;
247 	uint_t			i;
248 	kdb_log_context		*log_ctx;
249 	kdb_hlog_t		*ulog = NULL;
250 	uint32_t		ulogentries;
251 
252 	INIT_ULOG(context);
253 	ulogentries = log_ctx->ulogentries;
254 
255 	i = (upd->kdb_entry_sno - 1) % ulogentries;
256 
257 	indx_log = (kdb_ent_header_t *)INDEX(ulog, i);
258 
259 	indx_log->kdb_commit = TRUE;
260 
261 	ulog->kdb_state = KDB_STABLE;
262 
263 	if (retval = ulog_sync_update(ulog, indx_log))
264 		return (retval);
265 
266 	ulog_sync_header(ulog);
267 
268 	return (0);
269 }
270 
271 /*
272  * Set the header log details on the slave and sync it to file.
273  */
274 void
275 ulog_finish_update_slave(kdb_hlog_t *ulog, kdb_last_t lastentry)
276 {
277 
278 	ulog->kdb_last_sno = lastentry.last_sno;
279 	ulog->kdb_last_time = lastentry.last_time;
280 
281 	ulog_sync_header(ulog);
282 }
283 
284 /*
285  * Delete an entry to the update log.
286  */
287 krb5_error_code
288 ulog_delete_update(krb5_context context, kdb_incr_update_t *upd)
289 {
290 
291 	upd->kdb_deleted = TRUE;
292 
293 	return (ulog_add_update(context, upd));
294 }
295 
296 /*
297  * Used by the slave or master (during ulog_check) to update it's hash db from
298  * the incr update log.
299  */
300 krb5_error_code
301 ulog_replay(krb5_context context, kdb_incr_result_t *incr_ret)
302 {
303 	krb5_db_entry		*entry = NULL;
304 	kdb_incr_update_t	*upd = NULL, *fupd;
305 	int			i, no_of_updates;
306 	krb5_error_code		retval;
307 	krb5_principal		dbprinc = NULL;
308 	kdb_last_t		errlast;
309 	char			*dbprincstr = NULL;
310 	kdb_log_context		*log_ctx;
311 	kdb_hlog_t		*ulog = NULL;
312 	bool_t			fini = FALSE;
313 
314 	INIT_ULOG(context);
315 
316 	no_of_updates = incr_ret->updates.kdb_ulog_t_len;
317 	upd = incr_ret->updates.kdb_ulog_t_val;
318 	fupd = upd;
319 
320 	/*
321 	 * We reset last_sno and last_time to 0, if krb5_db_put_principal
322 	 * or krb5_db_delete_principal fail.
323 	 */
324 	errlast.last_sno = (unsigned int)0;
325 	errlast.last_time.seconds = (unsigned int)0;
326 	errlast.last_time.useconds = (unsigned int)0;
327 
328 	if (krb5_db_inited(context)) {
329 		retval = krb5_db_open(context, NULL,
330 		    KRB5_KDB_OPEN_RW | KRB5_KDB_SRV_TYPE_ADMIN);
331 		if (retval != 0)
332 			goto cleanup;
333 		fini = TRUE;
334 	}
335 
336 	for (i = 0; i < no_of_updates; i++) {
337 		int nentry = 1;
338 
339 		if (!upd->kdb_commit)
340 			continue;
341 
342 		if (upd->kdb_deleted) {
343 			dbprincstr = malloc((upd->kdb_princ_name.utf8str_t_len
344 			    + 1) * sizeof (char));
345 
346 			if (dbprincstr == NULL) {
347 				retval = ENOMEM;
348 				goto cleanup;
349 			}
350 
351 			(void) strlcpy(dbprincstr,
352 			    (char *)upd->kdb_princ_name.utf8str_t_val,
353 			    (upd->kdb_princ_name.utf8str_t_len + 1));
354 
355 			if (retval = krb5_parse_name(context, dbprincstr,
356 			    &dbprinc)) {
357 				goto cleanup;
358 			}
359 
360 			if (dbprincstr)
361 				free(dbprincstr);
362 
363 			retval = krb5_db_delete_principal(context,
364 			    dbprinc, &nentry);
365 
366 			if (dbprinc)
367 				krb5_free_principal(context, dbprinc);
368 
369 			if (retval)
370 				goto cleanup;
371 		} else {
372 			entry = (krb5_db_entry *)malloc(sizeof (krb5_db_entry));
373 
374 			if (!entry) {
375 				retval = errno;
376 				goto cleanup;
377 			}
378 
379 			(void) memset(entry, 0, sizeof (krb5_db_entry));
380 
381 			if (retval = ulog_conv_2dbentry(context, entry, upd, 1))
382 				goto cleanup;
383 
384 			retval = krb5_db_put_principal(context, entry,
385 			    &nentry);
386 
387 			if (entry) {
388 				krb5_db_free_principal(context, entry, nentry);
389 				free(entry);
390 				entry = NULL;
391 			}
392 			if (retval)
393 				goto cleanup;
394 		}
395 
396 		upd++;
397 	}
398 
399 cleanup:
400 	if (fupd)
401 		ulog_free_entries(fupd, no_of_updates);
402 
403 	if (log_ctx && (log_ctx->iproprole == IPROP_SLAVE)) {
404 		if (retval)
405 			ulog_finish_update_slave(ulog, errlast);
406 		else
407 			ulog_finish_update_slave(ulog, incr_ret->lastentry);
408 	}
409 
410 	if (fini == TRUE)
411 		krb5_db_fini(context);
412 
413 	return (retval);
414 }
415 
416 /*
417  * Validate the log file and resync any uncommitted update entries
418  * to the principal database.
419  */
420 krb5_error_code
421 ulog_check(krb5_context context, kdb_hlog_t *ulog)
422 {
423 	XDR			xdrs;
424 	krb5_error_code		retval = 0;
425 	int			i;
426 	kdb_ent_header_t	*indx_log;
427 	kdb_incr_update_t	*upd = NULL;
428 	kdb_incr_result_t	*incr_ret = NULL;
429 
430 	ulog->kdb_state = KDB_STABLE;
431 
432 	for (i = 0; i < ulog->kdb_num; i++) {
433 		indx_log = (kdb_ent_header_t *)INDEX(ulog, i);
434 
435 		if (indx_log->kdb_umagic != KDB_UMAGIC) {
436 			/*
437 			 * Update entry corrupted we should scream and die
438 			 */
439 			ulog->kdb_state = KDB_CORRUPT;
440 			retval = KRB5_LOG_CORRUPT;
441 			break;
442 		}
443 
444 		if (indx_log->kdb_commit == FALSE) {
445 			ulog->kdb_state = KDB_UNSTABLE;
446 
447 			incr_ret = (kdb_incr_result_t *)
448 			    malloc(sizeof (kdb_incr_result_t));
449 			if (incr_ret == NULL) {
450 				retval = errno;
451 				goto error;
452 			}
453 
454 			upd = (kdb_incr_update_t *)
455 			    malloc(sizeof (kdb_incr_update_t));
456 			if (upd == NULL) {
457 				retval = errno;
458 				goto error;
459 			}
460 
461 			(void) memset(upd, 0, sizeof (kdb_incr_update_t));
462 			xdrmem_create(&xdrs, (char *)indx_log->entry_data,
463 			    indx_log->kdb_entry_size, XDR_DECODE);
464 			if (!xdr_kdb_incr_update_t(&xdrs, upd)) {
465 				retval = KRB5_LOG_CONV;
466 				goto error;
467 			}
468 
469 			incr_ret->updates.kdb_ulog_t_len = 1;
470 			incr_ret->updates.kdb_ulog_t_val = upd;
471 
472 			upd->kdb_commit = TRUE;
473 
474 			/*
475 			 * We don't want to readd this update and just use the
476 			 * existing update to be propagated later on
477 			 */
478 			ulog_set_role(context, IPROP_NULL);
479 			retval = ulog_replay(context, incr_ret);
480 
481 			/*
482 			 * upd was freed by ulog_replay, we NULL
483 			 * the pointer in case we subsequently break from loop.
484 			 */
485 			upd = NULL;
486 			if (incr_ret) {
487 				free(incr_ret);
488 				incr_ret = NULL;
489 			}
490 			ulog_set_role(context, IPROP_MASTER);
491 
492 			if (retval)
493 				goto error;
494 
495 			/*
496 			 * We flag this as committed since this was
497 			 * the last entry before kadmind crashed, ergo
498 			 * the slaves have not seen this update before
499 			 */
500 			indx_log->kdb_commit = TRUE;
501 			retval = ulog_sync_update(ulog, indx_log);
502 			if (retval)
503 				goto error;
504 
505 			ulog->kdb_state = KDB_STABLE;
506 		}
507 	}
508 
509 error:
510 	if (upd)
511 		ulog_free_entries(upd, 1);
512 
513 	if (incr_ret)
514 		free(incr_ret);
515 
516 	ulog_sync_header(ulog);
517 
518 	return (retval);
519 }
520 
521 /*
522  * Map the log file to memory for performance and simplicity.
523  *
524  * Called by: if iprop_enabled then ulog_map();
525  * Assumes that the caller will terminate on ulog_map, hence munmap and
526  * closing of the fd are implicitly performed by the caller.
527  * Returns 0 on success else failure.
528  */
529 krb5_error_code
530 ulog_map(krb5_context context, kadm5_config_params *params, int caller)
531 {
532 	struct stat	st;
533 	krb5_error_code	retval;
534 	uint32_t	ulog_filesize;
535 	char		logname[MAX_FILENAME];
536 	kdb_log_context	*log_ctx;
537 	kdb_hlog_t	*ulog = NULL;
538 	uint32_t	ulogentries;
539 	int		ulogfd = -1;
540 
541 	if ((caller == FKADMIND) || (caller == FKCOMMAND))
542 		ulogentries = params->iprop_ulogsize;
543 
544 	ulog_filesize = sizeof (kdb_hlog_t);
545 
546 	if (strlcpy(logname, params->dbname, MAX_FILENAME) >= MAX_FILENAME)
547 		return (KRB5_LOG_ERROR);
548 	if (strlcat(logname, ".ulog", MAX_FILENAME) >= MAX_FILENAME)
549 		return (KRB5_LOG_ERROR);
550 
551 	if (stat(logname, &st) == -1) {
552 
553 		if (caller == FKPROPLOG) {
554 			/*
555 			 * File doesn't exist so we exit with kproplog
556 			 */
557 			return (errno);
558 		}
559 
560 		if ((ulogfd = open(logname, O_RDWR+O_CREAT, 0600)) == -1) {
561 			return (errno);
562 		}
563 
564 		if (lseek(ulogfd, 0L, SEEK_CUR) == -1) {
565 			return (errno);
566 		}
567 
568 		if ((caller == FKADMIND) || (caller == FKCOMMAND))
569 			ulog_filesize += ulogentries * ULOG_BLOCK;
570 
571 		if (lseek(ulogfd, ulog_filesize, SEEK_SET) == -1) {
572 			return (errno);
573 		}
574 
575 		if (write(ulogfd, "+", 1) != 1) {
576 			return (errno);
577 		}
578 
579 	} else {
580 
581 		if ((ulogfd = open(logname, O_RDWR, 0600)) == -1) {
582 			/*
583 			 * Can't open existing log file
584 			 */
585 			return (errno);
586 		}
587 	}
588 
589 	if (caller == FKPROPLOG) {
590 		fstat(ulogfd, &st);
591 		ulog_filesize = st.st_size;
592 
593 		ulog = (kdb_hlog_t *)mmap(0, ulog_filesize,
594 		    PROT_READ+PROT_WRITE, MAP_PRIVATE, ulogfd, 0);
595 	} else {
596 		/*
597 		 * else kadmind, kpropd, & kcommands should udpate stores
598 		 */
599 		ulog = (kdb_hlog_t *)mmap(0, MAXLOGLEN,
600 		    PROT_READ+PROT_WRITE, MAP_SHARED, ulogfd, 0);
601 	}
602 
603 	if ((int)(ulog) == -1) {
604 		/*
605 		 * Can't map update log file to memory
606 		 */
607 		return (errno);
608 	}
609 
610 	if (!context->kdblog_context) {
611 		if (!(log_ctx = malloc(sizeof (kdb_log_context))))
612 			return (errno);
613 		context->kdblog_context = (void *)log_ctx;
614 	} else
615 		log_ctx = context->kdblog_context;
616 	log_ctx->ulog = ulog;
617 	log_ctx->ulogentries = ulogentries;
618 	log_ctx->ulogfd = ulogfd;
619 
620 	if (ulog->kdb_hmagic != KDB_HMAGIC) {
621 		if (ulog->kdb_hmagic == 0) {
622 			/*
623 			 * New update log
624 			 */
625 			(void) memset(ulog, 0, sizeof (kdb_hlog_t));
626 
627 			ulog->kdb_hmagic = KDB_HMAGIC;
628 			ulog->db_version_num = KDB_VERSION;
629 			ulog->kdb_state = KDB_STABLE;
630 			ulog->kdb_block = ULOG_BLOCK;
631 			if (!(caller == FKPROPLOG))
632 				ulog_sync_header(ulog);
633 		} else {
634 			return (KRB5_LOG_CORRUPT);
635 		}
636 	}
637 
638 	if (caller == FKADMIND) {
639 		switch (ulog->kdb_state) {
640 			case KDB_STABLE:
641 			case KDB_UNSTABLE:
642 				/*
643 				 * Log is currently un/stable, check anyway
644 				 */
645 				retval = ulog_check(context, ulog);
646 				if (retval == KRB5_LOG_CORRUPT) {
647 					return (retval);
648 				}
649 				break;
650 			case KDB_CORRUPT:
651 				return (KRB5_LOG_CORRUPT);
652 			default:
653 				/*
654 				 * Invalid db state
655 				 */
656 				return (KRB5_LOG_ERROR);
657 		}
658 	} else if ((caller == FKPROPLOG) || (caller == FKPROPD)) {
659 		/*
660 		 * kproplog and kpropd don't need to do anything else
661 		 */
662 		return (0);
663 	}
664 
665 	/*
666 	 * Reinit ulog if the log is being truncated or expanded after
667 	 * we have circled.
668 	 */
669 	if (ulog->kdb_num != ulogentries) {
670 		if ((ulog->kdb_num != 0) &&
671 		    ((ulog->kdb_last_sno > ulog->kdb_num) ||
672 		    (ulog->kdb_num > ulogentries))) {
673 			(void) memset(ulog, 0, sizeof (kdb_hlog_t));
674 
675 			ulog->kdb_hmagic = KDB_HMAGIC;
676 			ulog->db_version_num = KDB_VERSION;
677 			ulog->kdb_state = KDB_STABLE;
678 			ulog->kdb_block = ULOG_BLOCK;
679 
680 			ulog_sync_header(ulog);
681 		}
682 
683 		/*
684 		 * Expand ulog if we have specified a greater size
685 		 */
686 		if (ulog->kdb_num < ulogentries) {
687 			ulog_filesize += ulogentries * ulog->kdb_block;
688 
689 			if (lseek(ulogfd, ulog_filesize, SEEK_SET) == -1) {
690 				return (errno);
691 			}
692 
693 			if (write(ulogfd, "+", 1) != 1) {
694 				return (errno);
695 			}
696 		}
697 	}
698 
699 	return (0);
700 }
701 
702 /*
703  * Get the last set of updates seen, (last+1) to n is returned.
704  */
705 krb5_error_code
706 ulog_get_entries(
707 	krb5_context context,		/* input - krb5 lib config */
708 	kdb_last_t last,		/* input - slave's last sno */
709 	kdb_incr_result_t *ulog_handle)	/* output - incr result for slave */
710 {
711 	XDR			xdrs;
712 	kdb_ent_header_t	*indx_log;
713 	kdb_incr_update_t	*upd;
714 	uint_t			indx, count, tdiff;
715 	uint32_t		sno;
716 	krb5_error_code		retval;
717 	struct timeval		timestamp;
718 	kdb_log_context		*log_ctx;
719 	kdb_hlog_t		*ulog = NULL;
720 	uint32_t		ulogentries;
721 
722 	INIT_ULOG(context);
723 	ulogentries = log_ctx->ulogentries;
724 
725 	/*
726 	 * Check to make sure we don't have a corrupt ulog first.
727 	 */
728 	if (ulog->kdb_state == KDB_CORRUPT) {
729 		ulog_handle->ret = UPDATE_ERROR;
730 		return (KRB5_LOG_CORRUPT);
731 	}
732 
733 	gettimeofday(&timestamp, NULL);
734 
735 	tdiff = timestamp.tv_sec - ulog->kdb_last_time.seconds;
736 	if (tdiff <= ULOG_IDLE_TIME) {
737 		ulog_handle->ret = UPDATE_BUSY;
738 		return (0);
739 	}
740 
741 	/*
742 	 * We need to lock out other processes here, such as kadmin.local,
743 	 * since we are looking at the last_sno and looking up updates.  So
744 	 * we can share with other readers.
745 	 */
746 	retval = krb5_db_lock(context, KRB5_LOCKMODE_SHARED);
747 	if (retval)
748 		return (retval);
749 
750 	/*
751 	 * We may have overflowed the update log or we shrunk the log, or
752 	 * the client's ulog has just been created.
753 	 */
754 	if ((last.last_sno > ulog->kdb_last_sno) ||
755 	    (last.last_sno < ulog->kdb_first_sno) ||
756 	    (last.last_sno == 0)) {
757 		ulog_handle->lastentry.last_sno = ulog->kdb_last_sno;
758 		(void) krb5_db_unlock(context);
759 		ulog_handle->ret = UPDATE_FULL_RESYNC_NEEDED;
760 		return (0);
761 	} else if (last.last_sno <= ulog->kdb_last_sno) {
762 		sno = last.last_sno;
763 
764 		indx = (sno - 1) % ulogentries;
765 
766 		indx_log = (kdb_ent_header_t *)INDEX(ulog, indx);
767 
768 		/*
769 		 * Validate the time stamp just to make sure it was the same sno
770 		 */
771 		if ((indx_log->kdb_time.seconds == last.last_time.seconds) &&
772 		    (indx_log->kdb_time.useconds == last.last_time.useconds)) {
773 
774 			/*
775 			 * If we have the same sno we return success
776 			 */
777 			if (last.last_sno == ulog->kdb_last_sno) {
778 				(void) krb5_db_unlock(context);
779 				ulog_handle->ret = UPDATE_NIL;
780 				return (0);
781 			}
782 
783 			count = ulog->kdb_last_sno - sno;
784 
785 			ulog_handle->updates.kdb_ulog_t_val =
786 			    (kdb_incr_update_t *)malloc(
787 			    sizeof (kdb_incr_update_t) * count);
788 
789 			upd = ulog_handle->updates.kdb_ulog_t_val;
790 
791 			if (upd == NULL) {
792 				(void) krb5_db_unlock(context);
793 				ulog_handle->ret = UPDATE_ERROR;
794 				return (errno);
795 			}
796 
797 			while (sno < ulog->kdb_last_sno) {
798 				indx = sno % ulogentries;
799 
800 				indx_log = (kdb_ent_header_t *)
801 				    INDEX(ulog, indx);
802 
803 				(void) memset(upd, 0,
804 				    sizeof (kdb_incr_update_t));
805 				xdrmem_create(&xdrs,
806 				    (char *)indx_log->entry_data,
807 				    indx_log->kdb_entry_size, XDR_DECODE);
808 				if (!xdr_kdb_incr_update_t(&xdrs, upd)) {
809 					(void) krb5_db_unlock(context);
810 					ulog_handle->ret = UPDATE_ERROR;
811 					return (KRB5_LOG_CONV);
812 				}
813 				/*
814 				 * Mark commitment since we didn't
815 				 * want to decode and encode the
816 				 * incr update record the first time.
817 				 */
818 				upd->kdb_commit = indx_log->kdb_commit;
819 
820 				upd++;
821 				sno++;
822 			} /* while */
823 
824 			ulog_handle->updates.kdb_ulog_t_len = count;
825 
826 			ulog_handle->lastentry.last_sno = ulog->kdb_last_sno;
827 			ulog_handle->lastentry.last_time.seconds =
828 			    ulog->kdb_last_time.seconds;
829 			ulog_handle->lastentry.last_time.useconds =
830 			    ulog->kdb_last_time.useconds;
831 			ulog_handle->ret = UPDATE_OK;
832 
833 			(void) krb5_db_unlock(context);
834 
835 			return (0);
836 		} else {
837 			/*
838 			 * We have time stamp mismatch or we no longer have
839 			 * the slave's last sno, so we brute force it
840 			 */
841 			(void) krb5_db_unlock(context);
842 			ulog_handle->ret = UPDATE_FULL_RESYNC_NEEDED;
843 
844 			return (0);
845 		}
846 	}
847 
848 	/*
849 	 * Should never get here, return error
850 	 */
851 	ulog_handle->ret = UPDATE_ERROR;
852 	return (KRB5_LOG_ERROR);
853 }
854 
855 krb5_error_code
856 ulog_set_role(krb5_context ctx, iprop_role role)
857 {
858 	kdb_log_context	*log_ctx;
859 
860 	if (!ctx->kdblog_context) {
861 		if (!(log_ctx = malloc(sizeof (kdb_log_context))))
862 			return (errno);
863 		ctx->kdblog_context = (void *)log_ctx;
864 	} else
865 		log_ctx = ctx->kdblog_context;
866 
867 	log_ctx->iproprole = role;
868 
869 	return (0);
870 }
871