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