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