1 /*
2  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
3  * Use is subject to license terms.
4  */
5 
6 /*
7  * lib/krb5/keytab/kt_file.c
8  *
9  * Copyright 1990,1991,1995 by the Massachusetts Institute of Technology.
10  * All Rights Reserved.
11  *
12  * Export of this software from the United States of America may
13  *   require a specific license from the United States Government.
14  *   It is the responsibility of any person or organization contemplating
15  *   export to obtain such a license before exporting.
16  *
17  * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
18  * distribute this software and its documentation for any purpose and
19  * without fee is hereby granted, provided that the above copyright
20  * notice appear in all copies and that both that copyright notice and
21  * this permission notice appear in supporting documentation, and that
22  * the name of M.I.T. not be used in advertising or publicity pertaining
23  * to distribution of the software without specific, written prior
24  * permission.  Furthermore if you modify this software you must label
25  * your software as modified software and not distribute it in such a
26  * fashion that it might be confused with the original M.I.T. software.
27  * M.I.T. makes no representations about the suitability of
28  * this software for any purpose.  It is provided "as is" without express
29  * or implied warranty.
30  *
31  */
32 
33 #include "k5-int.h"
34 #include <stdio.h>
35 
36 /*
37  * Information needed by internal routines of the file-based ticket
38  * cache implementation.
39  */
40 
41 
42 /*
43  * Constants
44  */
45 #define IGNORE_VNO 0
46 #define IGNORE_ENCTYPE 0
47 
48 #define KRB5_KT_VNO_1	0x0501	/* krb v5, keytab version 1 (DCE compat) */
49 #define KRB5_KT_VNO	0x0502	/* krb v5, keytab version 2 (standard)  */
50 
51 #define KRB5_KT_DEFAULT_VNO KRB5_KT_VNO
52 
53 /*
54  * Types
55  */
56 typedef struct _krb5_ktfile_data {
57     char *name;			/* Name of the file */
58     FILE *openf;		/* open file, if any. */
59     char iobuf[BUFSIZ];		/* so we can zap it later */
60     int	version;		/* Version number of keytab */
61     k5_mutex_t lock;		/* Protect openf, version */
62 } krb5_ktfile_data;
63 
64 /*
65  * Macros
66  */
67 #define KTPRIVATE(id) ((krb5_ktfile_data *)(id)->data)
68 #define KTFILENAME(id) (((krb5_ktfile_data *)(id)->data)->name)
69 #define KTFILEP(id) (((krb5_ktfile_data *)(id)->data)->openf)
70 #define KTFILEBUFP(id) (((krb5_ktfile_data *)(id)->data)->iobuf)
71 #define KTVERSION(id) (((krb5_ktfile_data *)(id)->data)->version)
72 #define KTLOCK(id) k5_mutex_lock(&((krb5_ktfile_data *)(id)->data)->lock)
73 #define KTUNLOCK(id) k5_mutex_unlock(&((krb5_ktfile_data *)(id)->data)->lock)
74 #define KTCHECKLOCK(id) k5_mutex_assert_locked(&((krb5_ktfile_data *)(id)->data)->lock)
75 
76 extern const struct _krb5_kt_ops krb5_ktf_ops;
77 extern const struct _krb5_kt_ops krb5_ktf_writable_ops;
78 
79 extern krb5_boolean KRB5_CALLCONV
80 __krb5_principal_compare_case_ins(krb5_context context,
81     krb5_const_principal princ1, krb5_const_principal princ2);
82 
83 krb5_error_code KRB5_CALLCONV krb5_ktfile_resolve
84 	(krb5_context,
85 		   const char *,
86 		   krb5_keytab *);
87 
88 krb5_error_code KRB5_CALLCONV krb5_ktfile_wresolve
89 	(krb5_context,
90 		   const char *,
91 		   krb5_keytab *);
92 
93 krb5_error_code KRB5_CALLCONV krb5_ktfile_get_name
94 	(krb5_context,
95 		   krb5_keytab,
96 		   char *,
97 		   unsigned int);
98 
99 krb5_error_code KRB5_CALLCONV krb5_ktfile_close
100 	(krb5_context,
101 		   krb5_keytab);
102 
103 krb5_error_code KRB5_CALLCONV krb5_ktfile_get_entry
104 	(krb5_context,
105 		   krb5_keytab,
106 		   krb5_const_principal,
107 		   krb5_kvno,
108 		   krb5_enctype,
109 		   krb5_keytab_entry *);
110 
111 krb5_error_code KRB5_CALLCONV krb5_ktfile_start_seq_get
112 	(krb5_context,
113 		   krb5_keytab,
114 		   krb5_kt_cursor *);
115 
116 krb5_error_code KRB5_CALLCONV krb5_ktfile_get_next
117 	(krb5_context,
118 		   krb5_keytab,
119 		   krb5_keytab_entry *,
120 		   krb5_kt_cursor *);
121 
122 krb5_error_code KRB5_CALLCONV krb5_ktfile_end_get
123 	(krb5_context,
124 		   krb5_keytab,
125 		   krb5_kt_cursor *);
126 
127 /* routines to be included on extended version (write routines) */
128 krb5_error_code KRB5_CALLCONV krb5_ktfile_add
129 	(krb5_context,
130 		   krb5_keytab,
131 		   krb5_keytab_entry *);
132 
133 krb5_error_code KRB5_CALLCONV krb5_ktfile_remove
134 	(krb5_context,
135 		   krb5_keytab,
136 		   krb5_keytab_entry *);
137 
138 krb5_error_code krb5_ktfileint_openr
139 	(krb5_context,
140 		   krb5_keytab);
141 
142 krb5_error_code krb5_ktfileint_openw
143 	(krb5_context,
144 		   krb5_keytab);
145 
146 krb5_error_code krb5_ktfileint_close
147 	(krb5_context,
148 		   krb5_keytab);
149 
150 krb5_error_code krb5_ktfileint_read_entry
151 	(krb5_context,
152 		   krb5_keytab,
153 		   krb5_keytab_entry *);
154 
155 krb5_error_code krb5_ktfileint_write_entry
156 	(krb5_context,
157 		   krb5_keytab,
158 		   krb5_keytab_entry *);
159 
160 krb5_error_code krb5_ktfileint_delete_entry
161 	(krb5_context,
162 		   krb5_keytab,
163                    krb5_int32);
164 
165 krb5_error_code krb5_ktfileint_internal_read_entry
166 	(krb5_context,
167 		   krb5_keytab,
168 		   krb5_keytab_entry *,
169                    krb5_int32 *);
170 
171 krb5_error_code krb5_ktfileint_size_entry
172 	(krb5_context,
173 		   krb5_keytab_entry *,
174                    krb5_int32 *);
175 
176 krb5_error_code krb5_ktfileint_find_slot
177 	(krb5_context,
178 		   krb5_keytab,
179                    krb5_int32 *,
180                    krb5_int32 *);
181 
182 
183 /*
184  * This is an implementation specific resolver.  It returns a keytab id
185  * initialized with file keytab routines.
186  */
187 
188 krb5_error_code KRB5_CALLCONV
189 krb5_ktfile_resolve(krb5_context context, const char *name, krb5_keytab *id)
190 {
191     krb5_ktfile_data *data;
192     krb5_error_code err;
193 
194     if ((*id = (krb5_keytab) malloc(sizeof(**id))) == NULL)
195 	return(ENOMEM);
196 
197     (*id)->ops = &krb5_ktf_ops;
198     if ((data = (krb5_ktfile_data *)malloc(sizeof(krb5_ktfile_data))) == NULL) {
199 	krb5_xfree(*id);
200 	return(ENOMEM);
201     }
202 
203     err = k5_mutex_init(&data->lock);
204     if (err) {
205 	krb5_xfree(data);
206 	krb5_xfree(*id);
207 	return err;
208     }
209 
210     if ((data->name = (char *)calloc(strlen(name) + 1, sizeof(char))) == NULL) {
211 	k5_mutex_destroy(&data->lock);
212 	krb5_xfree(data);
213 	krb5_xfree(*id);
214 	return(ENOMEM);
215     }
216 
217     (void) strcpy(data->name, name);
218     data->openf = 0;
219     data->version = 0;
220 
221     (*id)->data = (krb5_pointer)data;
222     (*id)->magic = KV5M_KEYTAB;
223     return(0);
224 }
225 
226 
227 /*
228  * "Close" a file-based keytab and invalidate the id.  This means
229  * free memory hidden in the structures.
230  */
231 
232 krb5_error_code KRB5_CALLCONV
233 krb5_ktfile_close(krb5_context context, krb5_keytab id)
234   /*
235    * This routine is responsible for freeing all memory allocated
236    * for this keytab.  There are no system resources that need
237    * to be freed nor are there any open files.
238    *
239    * This routine should undo anything done by krb5_ktfile_resolve().
240    */
241 {
242     krb5_xfree(KTFILENAME(id));
243     zap(KTFILEBUFP(id), BUFSIZ);
244     k5_mutex_destroy(&((krb5_ktfile_data *)id->data)->lock);
245     krb5_xfree(id->data);
246     id->ops = 0;
247     krb5_xfree(id);
248     return (0);
249 }
250 
251 /*
252  * This is the get_entry routine for the file based keytab implementation.
253  * It opens the keytab file, and either retrieves the entry or returns
254  * an error.
255  */
256 
257 krb5_error_code KRB5_CALLCONV
258 krb5_ktfile_get_entry(krb5_context context, krb5_keytab id,
259 		      krb5_const_principal principal, krb5_kvno kvno,
260 		      krb5_enctype enctype, krb5_keytab_entry *entry)
261 {
262     krb5_keytab_entry cur_entry, new_entry;
263     krb5_error_code kerror = 0;
264     int found_wrong_kvno = 0;
265     krb5_boolean similar;
266     int kvno_offset = 0;
267 
268     kerror = KTLOCK(id);
269     if (kerror)
270 	return kerror;
271 
272     /* Open the keyfile for reading */
273     if ((kerror = krb5_ktfileint_openr(context, id))) {
274 	KTUNLOCK(id);
275 	return(kerror);
276     }
277 
278     /*
279      * For efficiency and simplicity, we'll use a while true that
280      * is exited with a break statement.
281      */
282     cur_entry.principal = 0;
283     cur_entry.vno = 0;
284     cur_entry.key.contents = 0;
285 
286     while (TRUE) {
287 	if ((kerror = krb5_ktfileint_read_entry(context, id, &new_entry)))
288 	    break;
289 
290 	/* by the time this loop exits, it must either free cur_entry,
291 	   and copy new_entry there, or free new_entry.  Otherwise, it
292 	   leaks. */
293 
294 	/* if the principal isn't the one requested, free new_entry
295 	   and continue to the next. */
296 
297 	/*
298 	 * Solaris Kerberos: MS Interop requires that case insensitive
299 	 * comparisons of service and host components are performed for key
300 	 * table lookup, etc.  Only called if the private environment variable
301 	 * MS_INTEROP is defined.
302 	 */
303 	if (krb5_getenv("MS_INTEROP")) {
304 	  if (!__krb5_principal_compare_case_ins(context, principal,
305 	    new_entry.principal)) {
306 	    	krb5_kt_free_entry(context, &new_entry);
307 	    	continue;
308 	  }
309 	} else if (!krb5_principal_compare(context, principal,
310 	  new_entry.principal)) {
311 	    krb5_kt_free_entry(context, &new_entry);
312 	    continue;
313 	}
314 
315 	/* if the enctype is not ignored and doesn't match, free new_entry
316 	   and continue to the next */
317 
318 	if (enctype != IGNORE_ENCTYPE) {
319 	    if ((kerror = krb5_c_enctype_compare(context, enctype,
320 						 new_entry.key.enctype,
321 						 &similar))) {
322 		krb5_kt_free_entry(context, &new_entry);
323 		break;
324 	    }
325 
326 	    if (!similar) {
327 		krb5_kt_free_entry(context, &new_entry);
328 		continue;
329 	    }
330 	    /*
331 	     * Coerce the enctype of the output keyblock in case we
332 	     * got an inexact match on the enctype.
333 	     */
334 	    new_entry.key.enctype = enctype;
335 
336 	}
337 
338 	if (kvno == IGNORE_VNO) {
339 	    /* if this is the first match, or if the new vno is
340 	       bigger, free the current and keep the new.  Otherwise,
341 	       free the new. */
342 	    /* A 1.2.x keytab contains only the low 8 bits of the key
343 	       version number.  Since it can be much bigger, and thus
344 	       the 8-bit value can wrap, we need some heuristics to
345 	       figure out the "highest" numbered key if some numbers
346 	       close to 255 and some near 0 are used.
347 
348 	       The heuristic here:
349 
350 	       If we have any keys with versions over 240, then assume
351 	       that all version numbers 0-127 refer to 256+N instead.
352 	       Not perfect, but maybe good enough?  */
353 
354 #define M(VNO) (((VNO) - kvno_offset + 256) % 256)
355 
356 	    if (new_entry.vno > 240)
357 		kvno_offset = 128;
358 	    if (! cur_entry.principal ||
359 		M(new_entry.vno) > M(cur_entry.vno)) {
360 		krb5_kt_free_entry(context, &cur_entry);
361 		cur_entry = new_entry;
362 	    } else {
363 		krb5_kt_free_entry(context, &new_entry);
364 	    }
365 	} else {
366 	    /* if this kvno matches, free the current (will there ever
367 	       be one?), keep the new, and break out.  Otherwise, remember
368 	       that we were here so we can return the right error, and
369 	       free the new */
370 	    /* Yuck.  The krb5-1.2.x keytab format only stores one byte
371 	       for the kvno, so we're toast if the kvno requested is
372 	       higher than that.  Short-term workaround: only compare
373 	       the low 8 bits.  */
374 
375 	    if (new_entry.vno == (kvno & 0xff)) {
376 		krb5_kt_free_entry(context, &cur_entry);
377 		cur_entry = new_entry;
378 		break;
379 	    } else {
380 		found_wrong_kvno++;
381 		krb5_kt_free_entry(context, &new_entry);
382 	    }
383 	}
384     }
385 
386     if (kerror == KRB5_KT_END) {
387 	 if (cur_entry.principal)
388 	      kerror = 0;
389 	 else if (found_wrong_kvno)
390 	      kerror = KRB5_KT_KVNONOTFOUND;
391 	 else
392 	      kerror = KRB5_KT_NOTFOUND;
393     }
394     if (kerror) {
395 	(void) krb5_ktfileint_close(context, id);
396 	KTUNLOCK(id);
397 	krb5_kt_free_entry(context, &cur_entry);
398 	return kerror;
399     }
400     if ((kerror = krb5_ktfileint_close(context, id)) != 0) {
401 	KTUNLOCK(id);
402 	krb5_kt_free_entry(context, &cur_entry);
403 	return kerror;
404     }
405     KTUNLOCK(id);
406     *entry = cur_entry;
407     return 0;
408 }
409 
410 /*
411  * Get the name of the file containing a file-based keytab.
412  */
413 
414 krb5_error_code KRB5_CALLCONV
415 krb5_ktfile_get_name(krb5_context context, krb5_keytab id, char *name, unsigned int len)
416   /*
417    * This routine returns the name of the name of the file associated with
418    * this file-based keytab.  name is zeroed and the filename is truncated
419    * to fit in name if necessary.  The name is prefixed with PREFIX:, so that
420    * trt will happen if the name is passed back to resolve.
421    */
422 {
423     memset(name, 0, len);
424 
425     if (len < strlen(id->ops->prefix)+2)
426 	return(KRB5_KT_NAME_TOOLONG);
427     strcpy(name, id->ops->prefix);
428     name += strlen(id->ops->prefix);
429     name[0] = ':';
430     name++;
431     len -= strlen(id->ops->prefix)+1;
432 
433     /* Solaris Kerberos */
434     if (len < strlen(KTFILENAME(id))+1)
435 	return(KRB5_KT_NAME_TOOLONG);
436     strcpy(name, KTFILENAME(id));
437     /* strcpy will NUL-terminate the destination */
438 
439     return(0);
440 }
441 
442 /*
443  * krb5_ktfile_start_seq_get()
444  */
445 
446 krb5_error_code KRB5_CALLCONV
447 krb5_ktfile_start_seq_get(krb5_context context, krb5_keytab id, krb5_kt_cursor *cursorp)
448 {
449     krb5_error_code retval;
450     long *fileoff;
451 
452     retval = KTLOCK(id);
453     if (retval)
454 	return retval;
455 
456     if ((retval = krb5_ktfileint_openr(context, id))) {
457 	KTUNLOCK(id);
458 	return retval;
459     }
460 
461     if (!(fileoff = (long *)malloc(sizeof(*fileoff)))) {
462 	krb5_ktfileint_close(context, id);
463 	KTUNLOCK(id);
464 	return ENOMEM;
465     }
466     *fileoff = ftell(KTFILEP(id));
467     *cursorp = (krb5_kt_cursor)fileoff;
468     KTUNLOCK(id);
469 
470     return 0;
471 }
472 
473 /*
474  * krb5_ktfile_get_next()
475  */
476 
477 krb5_error_code KRB5_CALLCONV
478 krb5_ktfile_get_next(krb5_context context, krb5_keytab id, krb5_keytab_entry *entry, krb5_kt_cursor *cursor)
479 {
480     long *fileoff = (long *)*cursor;
481     krb5_keytab_entry cur_entry;
482     krb5_error_code kerror;
483 
484     kerror = KTLOCK(id);
485     if (kerror)
486 	return kerror;
487     if (KTFILEP(id) == NULL) {
488 	KTUNLOCK(id);
489 	return KRB5_KT_IOERR;
490     }
491     if (fseek(KTFILEP(id), *fileoff, 0) == -1) {
492 	KTUNLOCK(id);
493 	return KRB5_KT_END;
494     }
495     if ((kerror = krb5_ktfileint_read_entry(context, id, &cur_entry))) {
496 	KTUNLOCK(id);
497 	return kerror;
498     }
499     *fileoff = ftell(KTFILEP(id));
500     *entry = cur_entry;
501     KTUNLOCK(id);
502     return 0;
503 }
504 
505 /*
506  * krb5_ktfile_end_get()
507  */
508 
509 krb5_error_code KRB5_CALLCONV
510 krb5_ktfile_end_get(krb5_context context, krb5_keytab id, krb5_kt_cursor *cursor)
511 {
512     krb5_error_code kerror;
513 
514     krb5_xfree(*cursor);
515     KTLOCK(id);
516     kerror = krb5_ktfileint_close(context, id);
517     KTUNLOCK(id);
518     return kerror;
519 }
520 
521 /*
522  * ser_ktf.c - Serialize keytab file context for subsequent reopen.
523  */
524 
525 static const char ktfile_def_name[] = ".";
526 
527 /*
528  * Routines to deal with externalizing krb5_keytab for [WR]FILE: variants.
529  *	krb5_ktf_keytab_size();
530  *	krb5_ktf_keytab_externalize();
531  *	krb5_ktf_keytab_internalize();
532  */
533 static krb5_error_code krb5_ktf_keytab_size
534 	(krb5_context, krb5_pointer, size_t *);
535 static krb5_error_code krb5_ktf_keytab_externalize
536 	(krb5_context, krb5_pointer, krb5_octet **, size_t *);
537 static krb5_error_code krb5_ktf_keytab_internalize
538 	(krb5_context,krb5_pointer *, krb5_octet **, size_t *);
539 
540 /*
541  * Serialization entry for this type.
542  */
543 const krb5_ser_entry krb5_ktfile_ser_entry = {
544     KV5M_KEYTAB,			/* Type			*/
545     krb5_ktf_keytab_size,		/* Sizer routine	*/
546     krb5_ktf_keytab_externalize,	/* Externalize routine	*/
547     krb5_ktf_keytab_internalize		/* Internalize routine	*/
548 };
549 
550 /*
551  * krb5_ktf_keytab_size()	- Determine the size required to externalize
552  *				  this krb5_keytab variant.
553  */
554 static krb5_error_code
555 krb5_ktf_keytab_size(krb5_context kcontext, krb5_pointer arg, size_t *sizep)
556 {
557     krb5_error_code	kret;
558     krb5_keytab		keytab;
559     size_t		required;
560     krb5_ktfile_data	*ktdata;
561 
562     kret = EINVAL;
563     if ((keytab = (krb5_keytab) arg)) {
564 	/*
565 	 * Saving FILE: variants of krb5_keytab requires at minimum:
566 	 *	krb5_int32	for KV5M_KEYTAB
567 	 *	krb5_int32	for length of keytab name.
568 	 *	krb5_int32	for file status.
569 	 *	krb5_int32	for file position.
570 	 *	krb5_int32	for file position.
571 	 *	krb5_int32	for version.
572 	 *	krb5_int32	for KV5M_KEYTAB
573 	 */
574 	required = sizeof(krb5_int32) * 7;
575 	if (keytab->ops && keytab->ops->prefix)
576 	    required += (strlen(keytab->ops->prefix)+1);
577 
578 	/*
579 	 * The keytab name is formed as follows:
580 	 *	<prefix>:<name>
581 	 * If there's no name, we use a default name so that we have something
582 	 * to call krb5_keytab_resolve with.
583 	 */
584 	ktdata = (krb5_ktfile_data *) keytab->data;
585 	required += strlen((ktdata && ktdata->name) ?
586 			   ktdata->name : ktfile_def_name);
587 	kret = 0;
588 
589 	if (!kret)
590 	    *sizep += required;
591     }
592     return(kret);
593 }
594 
595 /*
596  * krb5_ktf_keytab_externalize()	- Externalize the krb5_keytab.
597  */
598 static krb5_error_code
599 krb5_ktf_keytab_externalize(krb5_context kcontext, krb5_pointer arg, krb5_octet **buffer, size_t *lenremain)
600 {
601     krb5_error_code	kret;
602     krb5_keytab		keytab;
603     size_t		required;
604     krb5_octet		*bp;
605     size_t		remain;
606     krb5_ktfile_data	*ktdata;
607     krb5_int32		file_is_open;
608     krb5_int64		file_pos;
609     char		*ktname;
610     size_t		namelen;
611     const char		*fnamep;
612 
613     required = 0;
614     bp = *buffer;
615     remain = *lenremain;
616     kret = EINVAL;
617     if ((keytab = (krb5_keytab) arg)) {
618 	kret = ENOMEM;
619 	if (!krb5_ktf_keytab_size(kcontext, arg, &required) &&
620 	    (required <= remain)) {
621 	    /* Our identifier */
622 	    (void) krb5_ser_pack_int32(KV5M_KEYTAB, &bp, &remain);
623 
624 	    ktdata = (krb5_ktfile_data *) keytab->data;
625 	    file_is_open = 0;
626 	    file_pos = 0;
627 
628 	    /* Calculate the length of the name */
629 	    namelen = (keytab->ops && keytab->ops->prefix) ?
630 		strlen(keytab->ops->prefix)+1 : 0;
631 	    if (ktdata && ktdata->name)
632 		fnamep = ktdata->name;
633 	    else
634 		fnamep = ktfile_def_name;
635 	    namelen += (strlen(fnamep)+1);
636 
637 	    if ((ktname = (char *) malloc(namelen))) {
638 		/* Format the keytab name. */
639 		if (keytab->ops && keytab->ops->prefix)
640 		    sprintf(ktname, "%s:%s", keytab->ops->prefix, fnamep);
641 
642 		else
643 		    strcpy(ktname, fnamep);
644 
645 		/* Fill in the file-specific keytab information. */
646 		if (ktdata) {
647 		    if (ktdata->openf) {
648 			long	fpos;
649 			int	fflags = 0;
650 
651 			file_is_open = 1;
652 #if !defined(_WIN32)
653 			fflags = fcntl(fileno(ktdata->openf), F_GETFL, 0);
654 			if (fflags > 0)
655 			    file_is_open |= ((fflags & O_ACCMODE) << 1);
656 #else
657 			file_is_open = 0;
658 #endif
659 			fpos = ftell(ktdata->openf);
660 			file_pos = fpos; /* XX range check? */
661 		    }
662 		}
663 
664 		/* Put the length of the file name */
665 		(void) krb5_ser_pack_int32((krb5_int32) strlen(ktname),
666 					   &bp, &remain);
667 
668 		/* Put the name */
669 		(void) krb5_ser_pack_bytes((krb5_octet *) ktname,
670 					   strlen(ktname),
671 					   &bp, &remain);
672 
673 		/* Put the file open flag */
674 		(void) krb5_ser_pack_int32(file_is_open, &bp, &remain);
675 
676 		/* Put the file position */
677 		(void) krb5_ser_pack_int64(file_pos, &bp, &remain);
678 
679 		/* Put the version */
680 		(void) krb5_ser_pack_int32((krb5_int32) ((ktdata) ?
681 							 ktdata->version : 0),
682 					   &bp, &remain);
683 
684 		/* Put the trailer */
685 		(void) krb5_ser_pack_int32(KV5M_KEYTAB, &bp, &remain);
686 		kret = 0;
687 		*buffer = bp;
688 		*lenremain = remain;
689 		free(ktname);
690 	    }
691 	}
692     }
693     return(kret);
694 }
695 
696 /*
697  * krb5_ktf_keytab_internalize()	- Internalize the krb5_ktf_keytab.
698  */
699 static krb5_error_code
700 krb5_ktf_keytab_internalize(krb5_context kcontext, krb5_pointer *argp, krb5_octet **buffer, size_t *lenremain)
701 {
702     krb5_error_code	kret;
703     krb5_keytab		keytab;
704     krb5_int32		ibuf;
705     krb5_octet		*bp;
706     size_t		remain;
707     char		*ktname;
708     krb5_ktfile_data	*ktdata;
709     krb5_int32		file_is_open;
710     krb5_int64		foff;
711 
712     bp = *buffer;
713     remain = *lenremain;
714     kret = EINVAL;
715     /* Read our magic number */
716     if (krb5_ser_unpack_int32(&ibuf, &bp, &remain))
717 	ibuf = 0;
718     if (ibuf == KV5M_KEYTAB) {
719 	kret = ENOMEM;
720 
721 	/* Get the length of the keytab name */
722 	kret = krb5_ser_unpack_int32(&ibuf, &bp, &remain);
723 
724 	if (!kret &&
725 	    (ktname = (char *) malloc((size_t) (ibuf+1))) &&
726 	    !(kret = krb5_ser_unpack_bytes((krb5_octet *) ktname,
727 					   (size_t) ibuf,
728 					   &bp, &remain))) {
729 	    ktname[ibuf] = '\0';
730 	    kret = krb5_kt_resolve(kcontext, ktname, &keytab);
731 	    if (!kret) {
732 		kret = ENOMEM;
733 		ktdata = (krb5_ktfile_data *) keytab->data;
734 		if (!ktdata) {
735 		    /* XXX */
736 		    keytab->data = (void *) malloc(sizeof(krb5_ktfile_data));
737 		    ktdata = (krb5_ktfile_data *) keytab->data;
738 		    memset(ktdata, 0, sizeof(krb5_ktfile_data));
739 		    if (strchr(ktname, (int) ':'))
740 			ktdata->name = strdup(strchr(ktname, (int) ':')+1);
741 		    else
742 			ktdata->name = strdup(ktname);
743 		}
744 		if (ktdata) {
745 		    if (remain >= (sizeof(krb5_int32)*5)) {
746 			(void) krb5_ser_unpack_int32(&file_is_open,
747 						     &bp, &remain);
748 			(void) krb5_ser_unpack_int64(&foff, &bp, &remain);
749 			(void) krb5_ser_unpack_int32(&ibuf, &bp, &remain);
750 			ktdata->version = (int) ibuf;
751 
752 			(void) krb5_ser_unpack_int32(&ibuf, &bp, &remain);
753 			if (ibuf == KV5M_KEYTAB) {
754 			    if (file_is_open) {
755 				int 	fmode;
756 				long	fpos;
757 
758 #if !defined(_WIN32)
759 				fmode = (file_is_open >> 1) & O_ACCMODE;
760 #else
761 				fmode = 0;
762 #endif
763 				if (fmode)
764 				    kret = krb5_ktfileint_openw(kcontext,
765 								keytab);
766 				else
767 				    kret = krb5_ktfileint_openr(kcontext,
768 								keytab);
769 				if (!kret) {
770 				    fpos = foff; /* XX range check? */
771 				    fseek(KTFILEP(keytab), fpos, SEEK_SET);
772 				}
773 			    }
774 			    kret = 0;
775 			}
776 			else
777 			    kret = EINVAL;
778 		    }
779 		}
780 		if (kret) {
781 		    if (keytab->data) {
782 			if (KTFILENAME(keytab))
783 			    krb5_xfree(KTFILENAME(keytab));
784 			krb5_xfree(keytab->data);
785 		    }
786 		    krb5_xfree(keytab);
787 		}
788 		else {
789 		    *buffer = bp;
790 		    *lenremain = remain;
791 		    *argp = (krb5_pointer) keytab;
792 		}
793 	    }
794 	    free(ktname);
795 	}
796     }
797     return(kret);
798 }
799 
800 /*
801  * This is an implementation specific resolver.  It returns a keytab id
802  * initialized with file keytab routines.
803  */
804 
805 krb5_error_code KRB5_CALLCONV
806 krb5_ktfile_wresolve(krb5_context context, const char *name, krb5_keytab *id)
807 {
808     krb5_ktfile_data *data;
809     krb5_error_code err;
810 
811     if ((*id = (krb5_keytab) malloc(sizeof(**id))) == NULL)
812 	return(ENOMEM);
813 
814     (*id)->ops = &krb5_ktf_writable_ops;
815     if ((data = (krb5_ktfile_data *)malloc(sizeof(krb5_ktfile_data))) == NULL) {
816 	krb5_xfree(*id);
817 	return(ENOMEM);
818     }
819 
820     err = k5_mutex_init(&data->lock);
821     if (err) {
822 	krb5_xfree(data);
823 	krb5_xfree(*id);
824 	return err;
825     }
826 
827     if ((data->name = (char *)calloc(strlen(name) + 1, sizeof(char))) == NULL) {
828 	k5_mutex_destroy(&data->lock);
829 	krb5_xfree(data);
830 	krb5_xfree(*id);
831 	return(ENOMEM);
832     }
833 
834     (void) strcpy(data->name, name);
835     data->openf = 0;
836     data->version = 0;
837 
838     (*id)->data = (krb5_pointer)data;
839     (*id)->magic = KV5M_KEYTAB;
840     return(0);
841 }
842 
843 
844 /*
845  * krb5_ktfile_add()
846  */
847 
848 krb5_error_code KRB5_CALLCONV
849 krb5_ktfile_add(krb5_context context, krb5_keytab id, krb5_keytab_entry *entry)
850 {
851     krb5_error_code retval;
852 
853     retval = KTLOCK(id);
854     if (retval)
855 	return retval;
856     if ((retval = krb5_ktfileint_openw(context, id))) {
857 	KTUNLOCK(id);
858 	return retval;
859     }
860     if (fseek(KTFILEP(id), 0, 2) == -1) {
861 	KTUNLOCK(id);
862 	return KRB5_KT_END;
863     }
864     retval = krb5_ktfileint_write_entry(context, id, entry);
865     krb5_ktfileint_close(context, id);
866     KTUNLOCK(id);
867     return retval;
868 }
869 
870 /*
871  * krb5_ktfile_remove()
872  */
873 
874 krb5_error_code KRB5_CALLCONV
875 krb5_ktfile_remove(krb5_context context, krb5_keytab id, krb5_keytab_entry *entry)
876 {
877     krb5_keytab_entry   cur_entry;
878     krb5_error_code     kerror;
879     krb5_int32          delete_point;
880 
881     kerror = KTLOCK(id);
882     if (kerror)
883 	return kerror;
884 
885     if ((kerror = krb5_ktfileint_openw(context, id))) {
886 	KTUNLOCK(id);
887 	return kerror;
888     }
889 
890     /*
891      * For efficiency and simplicity, we'll use a while true that
892      * is exited with a break statement.
893      */
894     while (TRUE) {
895 	if ((kerror = krb5_ktfileint_internal_read_entry(context, id,
896 							 &cur_entry,
897 							 &delete_point)))
898   	    break;
899 
900 	if ((entry->vno == cur_entry.vno) &&
901             (entry->key.enctype == cur_entry.key.enctype) &&
902 	    krb5_principal_compare(context, entry->principal, cur_entry.principal)) {
903 	    /* found a match */
904             krb5_kt_free_entry(context, &cur_entry);
905 	    break;
906 	}
907 	krb5_kt_free_entry(context, &cur_entry);
908     }
909 
910     if (kerror == KRB5_KT_END)
911 	kerror = KRB5_KT_NOTFOUND;
912 
913     if (kerror) {
914 	(void) krb5_ktfileint_close(context, id);
915 	KTUNLOCK(id);
916 	return kerror;
917     }
918 
919     kerror = krb5_ktfileint_delete_entry(context, id, delete_point);
920 
921     if (kerror) {
922 	(void) krb5_ktfileint_close(context, id);
923     } else {
924         kerror = krb5_ktfileint_close(context, id);
925     }
926     KTUNLOCK(id);
927     return kerror;
928 }
929 
930 /*
931  * krb5_ktf_ops
932  */
933 
934 const struct _krb5_kt_ops krb5_ktf_ops = {
935     0,
936     "FILE", 	/* Prefix -- this string should not appear anywhere else! */
937     krb5_ktfile_resolve,
938     krb5_ktfile_get_name,
939     krb5_ktfile_close,
940     krb5_ktfile_get_entry,
941     krb5_ktfile_start_seq_get,
942     krb5_ktfile_get_next,
943     krb5_ktfile_end_get,
944     0,
945     0,
946     &krb5_ktfile_ser_entry
947 };
948 
949 /*
950  * krb5_ktf_writable_ops
951  */
952 
953 const struct _krb5_kt_ops krb5_ktf_writable_ops = {
954     0,
955     "WRFILE", 	/* Prefix -- this string should not appear anywhere else! */
956     krb5_ktfile_wresolve,
957     krb5_ktfile_get_name,
958     krb5_ktfile_close,
959     krb5_ktfile_get_entry,
960     krb5_ktfile_start_seq_get,
961     krb5_ktfile_get_next,
962     krb5_ktfile_end_get,
963     krb5_ktfile_add,
964     krb5_ktfile_remove,
965     &krb5_ktfile_ser_entry
966 };
967 
968 /*
969  * krb5_kt_dfl_ops
970  */
971 
972 const krb5_kt_ops krb5_kt_dfl_ops = {
973     0,
974     "FILE", 	/* Prefix -- this string should not appear anywhere else! */
975     krb5_ktfile_resolve,
976     krb5_ktfile_get_name,
977     krb5_ktfile_close,
978     krb5_ktfile_get_entry,
979     krb5_ktfile_start_seq_get,
980     krb5_ktfile_get_next,
981     krb5_ktfile_end_get,
982     0,
983     0,
984     &krb5_ktfile_ser_entry
985 };
986 
987 /*
988  * lib/krb5/keytab/file/ktf_util.c
989  *
990  * Copyright (c) Hewlett-Packard Company 1991
991  * Released to the Massachusetts Institute of Technology for inclusion
992  * in the Kerberos source code distribution.
993  *
994  * Copyright 1990,1991 by the Massachusetts Institute of Technology.
995  * All Rights Reserved.
996  *
997  * Export of this software from the United States of America may
998  *   require a specific license from the United States Government.
999  *   It is the responsibility of any person or organization contemplating
1000  *   export to obtain such a license before exporting.
1001  *
1002  * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
1003  * distribute this software and its documentation for any purpose and
1004  * without fee is hereby granted, provided that the above copyright
1005  * notice appear in all copies and that both that copyright notice and
1006  * this permission notice appear in supporting documentation, and that
1007  * the name of M.I.T. not be used in advertising or publicity pertaining
1008  * to distribution of the software without specific, written prior
1009  * permission.  Furthermore if you modify this software you must label
1010  * your software as modified software and not distribute it in such a
1011  * fashion that it might be confused with the original M.I.T. software.
1012  * M.I.T. makes no representations about the suitability of
1013  * this software for any purpose.  It is provided "as is" without express
1014  * or implied warranty.
1015  *
1016  *
1017  * This function contains utilities for the file based implementation of
1018  * the keytab.  There are no public functions in this file.
1019  *
1020  * This file is the only one that has knowledge of the format of a
1021  * keytab file.
1022  *
1023  * The format is as follows:
1024  *
1025  * <file format vno>
1026  * <record length>
1027  * principal timestamp vno key
1028  * <record length>
1029  * principal timestamp vno key
1030  * ....
1031  *
1032  * A length field (sizeof(krb5_int32)) exists between entries.  When this
1033  * length is positive it indicates an active entry, when negative a hole.
1034  * The length indicates the size of the block in the file (this may be
1035  * larger than the size of the next record, since we are using a first
1036  * fit algorithm for re-using holes and the first fit may be larger than
1037  * the entry we are writing).  Another (compatible) implementation could
1038  * break up holes when allocating them to smaller entries to minimize
1039  * wasted space.  (Such an implementation should also coalesce adjacent
1040  * holes to reduce fragmentation).  This implementation does neither.
1041  *
1042  * There are no separators between fields of an entry.
1043  * A principal is a length-encoded array of length-encoded strings.  The
1044  * length is a krb5_int16 in each case.  The specific format, then, is
1045  * multiple entries concatinated with no separators.  An entry has this
1046  * exact format:
1047  *
1048  * sizeof(krb5_int16) bytes for number of components in the principal;
1049  * then, each component listed in ordser.
1050  * For each component, sizeof(krb5_int16) bytes for the number of bytes
1051  * in the component, followed by the component.
1052  * sizeof(krb5_int32) for the principal type (for KEYTAB V2 and higher)
1053  * sizeof(krb5_int32) bytes for the timestamp
1054  * sizeof(krb5_octet) bytes for the key version number
1055  * sizeof(krb5_int16) bytes for the enctype
1056  * sizeof(krb5_int32) bytes for the key length, followed by the key
1057  */
1058 
1059 #ifndef SEEK_SET
1060 #define SEEK_SET 0
1061 #define SEEK_CUR 1
1062 #endif
1063 
1064 typedef krb5_int16  krb5_kt_vno;
1065 
1066 #define krb5_kt_default_vno ((krb5_kt_vno)KRB5_KT_DEFAULT_VNO)
1067 
1068 #define xfwrite(a, b, c, d) fwrite((char *)a, b, (unsigned) c, d)
1069 #define xfread(a, b, c, d) fread((char *)a, b, (unsigned) c, d)
1070 
1071 #ifdef ANSI_STDIO
1072 /* Solaris Kerberos */
1073 static char *const fopen_mode_rbplus= "rb+F";
1074 static char *const fopen_mode_rb = "rbF";
1075 #else
1076 /* Solaris Kerberos */
1077 static char *const fopen_mode_rbplus= "r+F";
1078 static char *const fopen_mode_rb = "rF";
1079 #endif
1080 
1081 static krb5_error_code
1082 krb5_ktfileint_open(krb5_context context, krb5_keytab id, int mode)
1083 {
1084     krb5_error_code kerror;
1085     krb5_kt_vno kt_vno;
1086     int writevno = 0;
1087 
1088     KTCHECKLOCK(id);
1089     errno = 0;
1090     KTFILEP(id) = fopen(KTFILENAME(id),
1091 			(mode == KRB5_LOCKMODE_EXCLUSIVE) ?
1092 			  fopen_mode_rbplus : fopen_mode_rb);
1093     if (!KTFILEP(id)) {
1094 	if ((mode == KRB5_LOCKMODE_EXCLUSIVE) && (errno == ENOENT)) {
1095 	    /* try making it first time around */
1096             krb5_create_secure_file(context, KTFILENAME(id));
1097 	    errno = 0;
1098 	    KTFILEP(id) = fopen(KTFILENAME(id), fopen_mode_rbplus);
1099 	    if (!KTFILEP(id))
1100 		return errno ? errno : EMFILE;
1101 	    writevno = 1;
1102 	} else				/* some other error */
1103 	    return errno ? errno : EMFILE;
1104     }
1105     if ((kerror = krb5_lock_file(context, fileno(KTFILEP(id)), mode))) {
1106 	(void) fclose(KTFILEP(id));
1107 	KTFILEP(id) = 0;
1108 	return kerror;
1109     }
1110     /* assume ANSI or BSD-style stdio */
1111     setbuf(KTFILEP(id), KTFILEBUFP(id));
1112 
1113     /* get the vno and verify it */
1114     if (writevno) {
1115 	kt_vno = htons(krb5_kt_default_vno);
1116 	KTVERSION(id) = krb5_kt_default_vno;
1117 	if (!xfwrite(&kt_vno, sizeof(kt_vno), 1, KTFILEP(id))) {
1118 	    kerror = errno;
1119 	    (void) krb5_unlock_file(context, fileno(KTFILEP(id)));
1120 	    (void) fclose(KTFILEP(id));
1121 	    return kerror;
1122 	}
1123     } else {
1124 	/* gotta verify it instead... */
1125 	if (!xfread(&kt_vno, sizeof(kt_vno), 1, KTFILEP(id))) {
1126 	    if (feof(KTFILEP(id)))
1127 		kerror = KRB5_KEYTAB_BADVNO;
1128 	    else
1129 		kerror = errno;
1130 	    (void) krb5_unlock_file(context, fileno(KTFILEP(id)));
1131 	    (void) fclose(KTFILEP(id));
1132 	    return kerror;
1133 	}
1134 	kt_vno = KTVERSION(id) = ntohs(kt_vno);
1135 	if ((kt_vno != KRB5_KT_VNO) &&
1136 	    (kt_vno != KRB5_KT_VNO_1)) {
1137 	    (void) krb5_unlock_file(context, fileno(KTFILEP(id)));
1138 	    (void) fclose(KTFILEP(id));
1139 	    return KRB5_KEYTAB_BADVNO;
1140 	}
1141     }
1142     return 0;
1143 }
1144 
1145 krb5_error_code
1146 krb5_ktfileint_openr(krb5_context context, krb5_keytab id)
1147 {
1148     return krb5_ktfileint_open(context, id, KRB5_LOCKMODE_SHARED);
1149 }
1150 
1151 krb5_error_code
1152 krb5_ktfileint_openw(krb5_context context, krb5_keytab id)
1153 {
1154     return krb5_ktfileint_open(context, id, KRB5_LOCKMODE_EXCLUSIVE);
1155 }
1156 
1157 krb5_error_code
1158 krb5_ktfileint_close(krb5_context context, krb5_keytab id)
1159 {
1160     krb5_error_code kerror;
1161 
1162     KTCHECKLOCK(id);
1163     if (!KTFILEP(id))
1164 	return 0;
1165     kerror = krb5_unlock_file(context, fileno(KTFILEP(id)));
1166     (void) fclose(KTFILEP(id));
1167     KTFILEP(id) = 0;
1168     return kerror;
1169 }
1170 
1171 krb5_error_code
1172 krb5_ktfileint_delete_entry(krb5_context context, krb5_keytab id, krb5_int32 delete_point)
1173 {
1174     krb5_int32  size;
1175     krb5_int32  len;
1176     char        iobuf[BUFSIZ];
1177 
1178     KTCHECKLOCK(id);
1179     if (fseek(KTFILEP(id), delete_point, SEEK_SET)) {
1180         return errno;
1181     }
1182     if (!xfread(&size, sizeof(size), 1, KTFILEP(id))) {
1183         return KRB5_KT_END;
1184     }
1185     if (KTVERSION(id) != KRB5_KT_VNO_1)
1186 	size = ntohl(size);
1187 
1188     if (size > 0) {
1189         krb5_int32 minus_size = -size;
1190 	if (KTVERSION(id) != KRB5_KT_VNO_1)
1191 	    minus_size = htonl(minus_size);
1192 
1193         if (fseek(KTFILEP(id), delete_point, SEEK_SET)) {
1194             return errno;
1195         }
1196 
1197         if (!xfwrite(&minus_size, sizeof(minus_size), 1, KTFILEP(id))) {
1198             return KRB5_KT_IOERR;
1199         }
1200 
1201         if (size < BUFSIZ) {
1202             len = size;
1203         } else {
1204             len = BUFSIZ;
1205         }
1206 
1207         memset(iobuf, 0, (size_t) len);
1208         while (size > 0) {
1209             xfwrite(iobuf, 1, (size_t) len, KTFILEP(id));
1210             size -= len;
1211             if (size < len) {
1212                 len = size;
1213             }
1214         }
1215 
1216         return krb5_sync_disk_file(context, KTFILEP(id));
1217     }
1218 
1219     return 0;
1220 }
1221 
1222 krb5_error_code
1223 krb5_ktfileint_internal_read_entry(krb5_context context, krb5_keytab id, krb5_keytab_entry *ret_entry, krb5_int32 *delete_point)
1224 {
1225     krb5_octet vno;
1226     krb5_int16 count;
1227     unsigned int u_count, u_princ_size;
1228     krb5_int16 enctype;
1229     krb5_int16 princ_size;
1230     register int i;
1231     krb5_int32 size;
1232     krb5_int32 start_pos;
1233     krb5_error_code error;
1234     char	*tmpdata;
1235     krb5_data	*princ;
1236 
1237     KTCHECKLOCK(id);
1238     memset(ret_entry, 0, sizeof(krb5_keytab_entry));
1239     ret_entry->magic = KV5M_KEYTAB_ENTRY;
1240 
1241     /* fseek to synchronise buffered I/O on the key table. */
1242 
1243     if (fseek(KTFILEP(id), 0L, SEEK_CUR) < 0)
1244     {
1245         return errno;
1246     }
1247 
1248     do {
1249         *delete_point = ftell(KTFILEP(id));
1250         if (!xfread(&size, sizeof(size), 1, KTFILEP(id))) {
1251             return KRB5_KT_END;
1252         }
1253 	if (KTVERSION(id) != KRB5_KT_VNO_1)
1254 		size = ntohl(size);
1255 
1256         if (size < 0) {
1257             if (fseek(KTFILEP(id), -size, SEEK_CUR)) {
1258                 return errno;
1259             }
1260         }
1261     } while (size < 0);
1262 
1263     if (size == 0) {
1264         return KRB5_KT_END;
1265     }
1266 
1267     start_pos = ftell(KTFILEP(id));
1268 
1269     /* deal with guts of parsing... */
1270 
1271     /* first, int16 with #princ components */
1272     if (!xfread(&count, sizeof(count), 1, KTFILEP(id)))
1273 	return KRB5_KT_END;
1274     if (KTVERSION(id) == KRB5_KT_VNO_1) {
1275 	    count -= 1;		/* V1 includes the realm in the count */
1276     } else {
1277 	    count = ntohs(count);
1278     }
1279     if (!count || (count < 0))
1280 	return KRB5_KT_END;
1281     ret_entry->principal = (krb5_principal)malloc(sizeof(krb5_principal_data));
1282     if (!ret_entry->principal)
1283         return ENOMEM;
1284 
1285     u_count = count;
1286     ret_entry->principal->magic = KV5M_PRINCIPAL;
1287     ret_entry->principal->length = u_count;
1288     ret_entry->principal->data = (krb5_data *)
1289                                  calloc(u_count, sizeof(krb5_data));
1290     if (!ret_entry->principal->data) {
1291 	free(ret_entry->principal);
1292 	ret_entry->principal = 0;
1293 	return ENOMEM;
1294     }
1295 
1296     /* Now, get the realm data */
1297     if (!xfread(&princ_size, sizeof(princ_size), 1, KTFILEP(id))) {
1298 	    error = KRB5_KT_END;
1299 	    goto fail;
1300     }
1301     if (KTVERSION(id) != KRB5_KT_VNO_1)
1302 	    princ_size = ntohs(princ_size);
1303     if (!princ_size || (princ_size < 0)) {
1304 	    error = KRB5_KT_END;
1305 	    goto fail;
1306     }
1307     u_princ_size = princ_size;
1308 
1309     krb5_princ_set_realm_length(context, ret_entry->principal, u_princ_size);
1310     tmpdata = malloc(u_princ_size+1);
1311     if (!tmpdata) {
1312 	    error = ENOMEM;
1313 	    goto fail;
1314     }
1315     if (fread(tmpdata, 1, u_princ_size, KTFILEP(id)) != (size_t) princ_size) {
1316 	    free(tmpdata);
1317 	    error = KRB5_KT_END;
1318 	    goto fail;
1319     }
1320     tmpdata[princ_size] = 0;	/* Some things might be expecting null */
1321 				/* termination...  ``Be conservative in */
1322 				/* what you send out'' */
1323     krb5_princ_set_realm_data(context, ret_entry->principal, tmpdata);
1324 
1325     for (i = 0; i < count; i++) {
1326 	princ = krb5_princ_component(context, ret_entry->principal, i);
1327 	if (!xfread(&princ_size, sizeof(princ_size), 1, KTFILEP(id))) {
1328 	    error = KRB5_KT_END;
1329 	    goto fail;
1330         }
1331 	if (KTVERSION(id) != KRB5_KT_VNO_1)
1332 	    princ_size = ntohs(princ_size);
1333 	if (!princ_size || (princ_size < 0)) {
1334 	    error = KRB5_KT_END;
1335 	    goto fail;
1336         }
1337 
1338 	u_princ_size = princ_size;
1339 	princ->length = u_princ_size;
1340 	princ->data = malloc(u_princ_size+1);
1341 	if (!princ->data) {
1342 	    error = ENOMEM;
1343 	    goto fail;
1344         }
1345 	if (!xfread(princ->data, sizeof(char), u_princ_size, KTFILEP(id))) {
1346 	    error = KRB5_KT_END;
1347 	    goto fail;
1348         }
1349 	princ->data[princ_size] = 0; /* Null terminate */
1350     }
1351 
1352     /* read in the principal type, if we can get it */
1353     if (KTVERSION(id) != KRB5_KT_VNO_1) {
1354 	    if (!xfread(&ret_entry->principal->type,
1355 			sizeof(ret_entry->principal->type), 1, KTFILEP(id))) {
1356 		    error = KRB5_KT_END;
1357 		    goto fail;
1358 	    }
1359 	    ret_entry->principal->type = ntohl(ret_entry->principal->type);
1360     }
1361 
1362     /* read in the timestamp */
1363     if (!xfread(&ret_entry->timestamp, sizeof(ret_entry->timestamp), 1, KTFILEP(id))) {
1364 	error = KRB5_KT_END;
1365 	goto fail;
1366     }
1367     if (KTVERSION(id) != KRB5_KT_VNO_1)
1368 	ret_entry->timestamp = ntohl(ret_entry->timestamp);
1369 
1370     /* read in the version number */
1371     if (!xfread(&vno, sizeof(vno), 1, KTFILEP(id))) {
1372 	error = KRB5_KT_END;
1373 	goto fail;
1374     }
1375     ret_entry->vno = (krb5_kvno)vno;
1376 
1377     /* key type */
1378     if (!xfread(&enctype, sizeof(enctype), 1, KTFILEP(id))) {
1379 	error = KRB5_KT_END;
1380 	goto fail;
1381     }
1382     ret_entry->key.enctype = (krb5_enctype)enctype;
1383 
1384     if (KTVERSION(id) != KRB5_KT_VNO_1)
1385 	ret_entry->key.enctype = ntohs(ret_entry->key.enctype);
1386 
1387     /* key contents */
1388     ret_entry->key.magic = KV5M_KEYBLOCK;
1389 
1390     if (!xfread(&count, sizeof(count), 1, KTFILEP(id))) {
1391 	error = KRB5_KT_END;
1392 	goto fail;
1393     }
1394     if (KTVERSION(id) != KRB5_KT_VNO_1)
1395 	count = ntohs(count);
1396     if (!count || (count < 0)) {
1397 	error = KRB5_KT_END;
1398 	goto fail;
1399     }
1400 
1401     u_count = count;
1402     ret_entry->key.length = u_count;
1403 
1404     ret_entry->key.contents = (krb5_octet *)malloc(u_count);
1405     if (!ret_entry->key.contents) {
1406 	error = ENOMEM;
1407 	goto fail;
1408     }
1409     if (!xfread(ret_entry->key.contents, sizeof(krb5_octet), count,
1410 		KTFILEP(id))) {
1411 	error = KRB5_KT_END;
1412 	goto fail;
1413     }
1414 
1415     /*
1416      * Reposition file pointer to the next inter-record length field.
1417      */
1418     fseek(KTFILEP(id), start_pos + size, SEEK_SET);
1419     return 0;
1420 fail:
1421 
1422     for (i = 0; i < krb5_princ_size(context, ret_entry->principal); i++) {
1423 	    princ = krb5_princ_component(context, ret_entry->principal, i);
1424 	    if (princ->data)
1425 		    free(princ->data);
1426     }
1427     free(ret_entry->principal->data);
1428     ret_entry->principal->data = 0;
1429     free(ret_entry->principal);
1430     ret_entry->principal = 0;
1431     return error;
1432 }
1433 
1434 krb5_error_code
1435 krb5_ktfileint_read_entry(krb5_context context, krb5_keytab id, krb5_keytab_entry *entryp)
1436 {
1437     krb5_int32 delete_point;
1438 
1439     return krb5_ktfileint_internal_read_entry(context, id, entryp, &delete_point);
1440 }
1441 
1442 krb5_error_code
1443 krb5_ktfileint_write_entry(krb5_context context, krb5_keytab id, krb5_keytab_entry *entry)
1444 {
1445     krb5_octet vno;
1446     krb5_data *princ;
1447     krb5_int16 count, size, enctype;
1448     krb5_error_code retval = 0;
1449     krb5_timestamp timestamp;
1450     krb5_int32	princ_type;
1451     krb5_int32  size_needed;
1452     krb5_int32  commit_point;
1453     int		i;
1454 
1455     KTCHECKLOCK(id);
1456     retval = krb5_ktfileint_size_entry(context, entry, &size_needed);
1457     if (retval)
1458         return retval;
1459     retval = krb5_ktfileint_find_slot(context, id, &size_needed, &commit_point);
1460     if (retval)
1461         return retval;
1462 
1463     /* fseek to synchronise buffered I/O on the key table. */
1464     /* XXX Without the weird setbuf crock, can we get rid of this now?  */
1465     if (fseek(KTFILEP(id), 0L, SEEK_CUR) < 0)
1466     {
1467         return errno;
1468     }
1469 
1470     if (KTVERSION(id) == KRB5_KT_VNO_1) {
1471 	    count = (krb5_int16) krb5_princ_size(context, entry->principal) + 1;
1472     } else {
1473 	    count = htons((u_short) krb5_princ_size(context, entry->principal));
1474     }
1475 
1476     if (!xfwrite(&count, sizeof(count), 1, KTFILEP(id))) {
1477     abend:
1478 	return KRB5_KT_IOERR;
1479     }
1480     size = krb5_princ_realm(context, entry->principal)->length;
1481     if (KTVERSION(id) != KRB5_KT_VNO_1)
1482 	    size = htons(size);
1483     if (!xfwrite(&size, sizeof(size), 1, KTFILEP(id))) {
1484 	    goto abend;
1485     }
1486     if (!xfwrite(krb5_princ_realm(context, entry->principal)->data, sizeof(char),
1487 		 krb5_princ_realm(context, entry->principal)->length, KTFILEP(id))) {
1488 	    goto abend;
1489     }
1490 
1491     count = (krb5_int16) krb5_princ_size(context, entry->principal);
1492     for (i = 0; i < count; i++) {
1493 	princ = krb5_princ_component(context, entry->principal, i);
1494 	size = princ->length;
1495 	if (KTVERSION(id) != KRB5_KT_VNO_1)
1496 		size = htons(size);
1497 	if (!xfwrite(&size, sizeof(size), 1, KTFILEP(id))) {
1498 	    goto abend;
1499 	}
1500 	if (!xfwrite(princ->data, sizeof(char), princ->length, KTFILEP(id))) {
1501 	    goto abend;
1502 	}
1503     }
1504 
1505     /*
1506      * Write out the principal type
1507      */
1508     if (KTVERSION(id) != KRB5_KT_VNO_1) {
1509 	    princ_type = htonl(krb5_princ_type(context, entry->principal));
1510 	    if (!xfwrite(&princ_type, sizeof(princ_type), 1, KTFILEP(id))) {
1511 		    goto abend;
1512 	    }
1513     }
1514 
1515     /*
1516      * Fill in the time of day the entry was written to the keytab.
1517      */
1518     if (krb5_timeofday(context, &entry->timestamp)) {
1519         entry->timestamp = 0;
1520     }
1521     if (KTVERSION(id) == KRB5_KT_VNO_1)
1522 	    timestamp = entry->timestamp;
1523     else
1524 	    timestamp = htonl(entry->timestamp);
1525     if (!xfwrite(&timestamp, sizeof(timestamp), 1, KTFILEP(id))) {
1526 	goto abend;
1527     }
1528 
1529     /* key version number */
1530     vno = (krb5_octet)entry->vno;
1531     if (!xfwrite(&vno, sizeof(vno), 1, KTFILEP(id))) {
1532 	goto abend;
1533     }
1534     /* key type */
1535     if (KTVERSION(id) == KRB5_KT_VNO_1)
1536 	    enctype = entry->key.enctype;
1537     else
1538 	    enctype = htons(entry->key.enctype);
1539     if (!xfwrite(&enctype, sizeof(enctype), 1, KTFILEP(id))) {
1540 	goto abend;
1541     }
1542     /* key length */
1543     if (KTVERSION(id) == KRB5_KT_VNO_1)
1544 	    size = entry->key.length;
1545     else
1546 	    size = htons(entry->key.length);
1547     if (!xfwrite(&size, sizeof(size), 1, KTFILEP(id))) {
1548 	goto abend;
1549     }
1550     if (!xfwrite(entry->key.contents, sizeof(krb5_octet),
1551 		 entry->key.length, KTFILEP(id))) {
1552 	goto abend;
1553     }
1554 
1555     if (fflush(KTFILEP(id)))
1556 	goto abend;
1557 
1558     retval = krb5_sync_disk_file(context, KTFILEP(id));
1559 
1560     if (retval) {
1561         return retval;
1562     }
1563 
1564     if (fseek(KTFILEP(id), commit_point, SEEK_SET)) {
1565         return errno;
1566     }
1567     if (KTVERSION(id) != KRB5_KT_VNO_1)
1568 	    size_needed = htonl(size_needed);
1569     if (!xfwrite(&size_needed, sizeof(size_needed), 1, KTFILEP(id))) {
1570         goto abend;
1571     }
1572     if (fflush(KTFILEP(id)))
1573 	goto abend;
1574     retval = krb5_sync_disk_file(context, KTFILEP(id));
1575 
1576     return retval;
1577 }
1578 
1579 /*
1580  * Determine the size needed for a file entry for the given
1581  * keytab entry.
1582  */
1583 krb5_error_code
1584 krb5_ktfileint_size_entry(krb5_context context, krb5_keytab_entry *entry, krb5_int32 *size_needed)
1585 {
1586     krb5_int16 count;
1587     krb5_int32 total_size, i;
1588     krb5_error_code retval = 0;
1589 
1590     count = (krb5_int16) krb5_princ_size(context, entry->principal);
1591 
1592     total_size = sizeof(count);
1593     total_size += krb5_princ_realm(context, entry->principal)->length + (sizeof(krb5_int16));
1594 
1595     for (i = 0; i < count; i++) {
1596 	    total_size += krb5_princ_component(context, entry->principal,i)->length
1597 		    + (sizeof(krb5_int16));
1598     }
1599 
1600     total_size += sizeof(entry->principal->type);
1601     total_size += sizeof(entry->timestamp);
1602     total_size += sizeof(krb5_octet);
1603     total_size += sizeof(krb5_int16);
1604     total_size += sizeof(krb5_int16) + entry->key.length;
1605 
1606     *size_needed = total_size;
1607     return retval;
1608 }
1609 
1610 /*
1611  * Find and reserve a slot in the file for an entry of the needed size.
1612  * The commit point will be set to the position in the file where the
1613  * the length (sizeof(krb5_int32) bytes) of this node should be written
1614  * when commiting the write.  The file position left as a result of this
1615  * call is the position where the actual data should be written.
1616  *
1617  * The size_needed argument may be adjusted if we find a hole that is
1618  * larger than the size needed.  (Recall that size_needed will be used
1619  * to commit the write, but that this field must indicate the size of the
1620  * block in the file rather than the size of the actual entry)
1621  */
1622 krb5_error_code
1623 krb5_ktfileint_find_slot(krb5_context context, krb5_keytab id, krb5_int32 *size_needed, krb5_int32 *commit_point)
1624 {
1625     krb5_int32      size;
1626     krb5_int32      remainder;
1627     krb5_int32      zero_point;
1628     krb5_kt_vno     kt_vno;
1629     krb5_boolean    found = FALSE;
1630     char            iobuf[BUFSIZ];
1631 
1632     KTCHECKLOCK(id);
1633     /*
1634      * Skip over file version number
1635      */
1636     if (fseek(KTFILEP(id), 0, SEEK_SET)) {
1637         return errno;
1638     }
1639     if (!xfread(&kt_vno, sizeof(kt_vno), 1, KTFILEP(id))) {
1640         return KRB5_KT_IOERR;
1641     }
1642 
1643     while (!found) {
1644         *commit_point = ftell(KTFILEP(id));
1645         if (!xfread(&size, sizeof(size), 1, KTFILEP(id))) {
1646             /*
1647              * Hit the end of file, reserve this slot.
1648              */
1649             size = 0;
1650 
1651             /* fseek to synchronise buffered I/O on the key table. */
1652 	    /* XXX Without the weird setbuf hack, can we nuke this now?  */
1653             if (fseek(KTFILEP(id), 0L, SEEK_CUR) < 0)
1654             {
1655                 return errno;
1656             }
1657 
1658 #ifdef notdef
1659 	    /* We don't have to do this because htonl(0) == 0 */
1660 	    if (KTVERSION(id) != KRB5_KT_VNO_1)
1661 		    size = htonl(size);
1662 #endif
1663 
1664             if (!xfwrite(&size, sizeof(size), 1, KTFILEP(id))) {
1665                 return KRB5_KT_IOERR;
1666             }
1667             found = TRUE;
1668         }
1669 
1670 	if (KTVERSION(id) != KRB5_KT_VNO_1)
1671 		size = ntohl(size);
1672 
1673         if (size > 0) {
1674             if (fseek(KTFILEP(id), size, SEEK_CUR)) {
1675                 return errno;
1676             }
1677         } else if (!found) {
1678             size = -size;
1679             if (size >= *size_needed) {
1680                 *size_needed = size;
1681                 found = TRUE;
1682             } else if (size > 0) {
1683                 /*
1684                  * The current hole is not large enough, so skip it
1685                  */
1686                 if (fseek(KTFILEP(id), size, SEEK_CUR)) {
1687                     return errno;
1688                 }
1689             } else {
1690 
1691                  /* fseek to synchronise buffered I/O on the key table. */
1692 
1693                  if (fseek(KTFILEP(id), 0L, SEEK_CUR) < 0)
1694                  {
1695                      return errno;
1696                  }
1697 
1698                 /*
1699                  * Found the end of the file (marked by a 0 length buffer)
1700                  * Make sure we zero any trailing data.
1701                  */
1702                 zero_point = ftell(KTFILEP(id));
1703                 while ((size = xfread(iobuf, 1, sizeof(iobuf), KTFILEP(id)))) {
1704                     if (size != sizeof(iobuf)) {
1705                         remainder = size % sizeof(krb5_int32);
1706                         if (remainder) {
1707                             size += sizeof(krb5_int32) - remainder;
1708                         }
1709                     }
1710 
1711                     if (fseek(KTFILEP(id), 0L, SEEK_CUR) < 0)
1712                     {
1713                         return errno;
1714                     }
1715 
1716                     memset(iobuf, 0, (size_t) size);
1717                     xfwrite(iobuf, 1, (size_t) size, KTFILEP(id));
1718 		    fflush(KTFILEP(id));
1719                     if (feof(KTFILEP(id))) {
1720                         break;
1721                     }
1722 
1723                     if (fseek(KTFILEP(id), 0L, SEEK_CUR) < 0)
1724                     {
1725                         return errno;
1726                     }
1727 
1728                 }
1729                 if (fseek(KTFILEP(id), zero_point, SEEK_SET)) {
1730                     return errno;
1731                 }
1732             }
1733         }
1734     }
1735 
1736     return 0;
1737 }
1738