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