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