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
26static 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 */
33krb5_error_code
34ulog_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 */
61void
62ulog_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 */
83krb5_error_code
84ulog_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 */
137krb5_error_code
138ulog_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 */
242krb5_error_code
243ulog_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 */
274void
275ulog_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 */
287krb5_error_code
288ulog_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 */
300krb5_error_code
301ulog_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
399cleanup:
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 */
420krb5_error_code
421ulog_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
509error:
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 */
529krb5_error_code
530ulog_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 */
705krb5_error_code
706ulog_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
855krb5_error_code
856ulog_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