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