1505d05c7Sgtb /*
25e01956fSGlenn Barry  * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
3505d05c7Sgtb  */
4505d05c7Sgtb 
5505d05c7Sgtb /*
6505d05c7Sgtb  * lib/krb5/keytab/kt_file.c
7505d05c7Sgtb  *
8505d05c7Sgtb  * Copyright 1990,1991,1995 by the Massachusetts Institute of Technology.
9505d05c7Sgtb  * All Rights Reserved.
10505d05c7Sgtb  *
11505d05c7Sgtb  * Export of this software from the United States of America may
12505d05c7Sgtb  *   require a specific license from the United States Government.
13505d05c7Sgtb  *   It is the responsibility of any person or organization contemplating
14505d05c7Sgtb  *   export to obtain such a license before exporting.
15*55fea89dSDan Cross  *
16505d05c7Sgtb  * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
17505d05c7Sgtb  * distribute this software and its documentation for any purpose and
18505d05c7Sgtb  * without fee is hereby granted, provided that the above copyright
19505d05c7Sgtb  * notice appear in all copies and that both that copyright notice and
20505d05c7Sgtb  * this permission notice appear in supporting documentation, and that
21505d05c7Sgtb  * the name of M.I.T. not be used in advertising or publicity pertaining
22505d05c7Sgtb  * to distribution of the software without specific, written prior
23505d05c7Sgtb  * permission.  Furthermore if you modify this software you must label
24505d05c7Sgtb  * your software as modified software and not distribute it in such a
25505d05c7Sgtb  * fashion that it might be confused with the original M.I.T. software.
26505d05c7Sgtb  * M.I.T. makes no representations about the suitability of
27505d05c7Sgtb  * this software for any purpose.  It is provided "as is" without express
28505d05c7Sgtb  * or implied warranty.
29*55fea89dSDan Cross  *
30505d05c7Sgtb  */
31505d05c7Sgtb 
32505d05c7Sgtb #include "k5-int.h"
33505d05c7Sgtb #include <stdio.h>
345e01956fSGlenn Barry #include <locale.h>
355e01956fSGlenn Barry #include <syslog.h>
36505d05c7Sgtb 
37505d05c7Sgtb /*
38505d05c7Sgtb  * Information needed by internal routines of the file-based ticket
39505d05c7Sgtb  * cache implementation.
40505d05c7Sgtb  */
41505d05c7Sgtb 
42505d05c7Sgtb 
43505d05c7Sgtb /*
44505d05c7Sgtb  * Constants
45505d05c7Sgtb  */
46505d05c7Sgtb #define IGNORE_VNO 0
47505d05c7Sgtb #define IGNORE_ENCTYPE 0
48505d05c7Sgtb 
49505d05c7Sgtb #define KRB5_KT_VNO_1	0x0501	/* krb v5, keytab version 1 (DCE compat) */
50505d05c7Sgtb #define KRB5_KT_VNO	0x0502	/* krb v5, keytab version 2 (standard)  */
51505d05c7Sgtb 
52505d05c7Sgtb #define KRB5_KT_DEFAULT_VNO KRB5_KT_VNO
53505d05c7Sgtb 
54*55fea89dSDan Cross /*
55505d05c7Sgtb  * Types
56505d05c7Sgtb  */
57505d05c7Sgtb typedef struct _krb5_ktfile_data {
58505d05c7Sgtb     char *name;			/* Name of the file */
59505d05c7Sgtb     FILE *openf;		/* open file, if any. */
60505d05c7Sgtb     char iobuf[BUFSIZ];		/* so we can zap it later */
61505d05c7Sgtb     int	version;		/* Version number of keytab */
62505d05c7Sgtb     k5_mutex_t lock;		/* Protect openf, version */
63505d05c7Sgtb } krb5_ktfile_data;
64505d05c7Sgtb 
65505d05c7Sgtb /*
66505d05c7Sgtb  * Macros
67505d05c7Sgtb  */
68505d05c7Sgtb #define KTPRIVATE(id) ((krb5_ktfile_data *)(id)->data)
69505d05c7Sgtb #define KTFILENAME(id) (((krb5_ktfile_data *)(id)->data)->name)
70505d05c7Sgtb #define KTFILEP(id) (((krb5_ktfile_data *)(id)->data)->openf)
71505d05c7Sgtb #define KTFILEBUFP(id) (((krb5_ktfile_data *)(id)->data)->iobuf)
72505d05c7Sgtb #define KTVERSION(id) (((krb5_ktfile_data *)(id)->data)->version)
73505d05c7Sgtb #define KTLOCK(id) k5_mutex_lock(&((krb5_ktfile_data *)(id)->data)->lock)
74505d05c7Sgtb #define KTUNLOCK(id) k5_mutex_unlock(&((krb5_ktfile_data *)(id)->data)->lock)
75505d05c7Sgtb #define KTCHECKLOCK(id) k5_mutex_assert_locked(&((krb5_ktfile_data *)(id)->data)->lock)
76505d05c7Sgtb 
77505d05c7Sgtb extern const struct _krb5_kt_ops krb5_ktf_ops;
78505d05c7Sgtb extern const struct _krb5_kt_ops krb5_ktf_writable_ops;
79505d05c7Sgtb 
8072f0806aSShawn Emery extern krb5_boolean KRB5_CALLCONV
8172f0806aSShawn Emery __krb5_principal_compare_case_ins(krb5_context context,
8272f0806aSShawn Emery     krb5_const_principal princ1, krb5_const_principal princ2);
8372f0806aSShawn Emery 
84*55fea89dSDan Cross krb5_error_code KRB5_CALLCONV krb5_ktfile_resolve
85505d05c7Sgtb 	(krb5_context,
86505d05c7Sgtb 		   const char *,
87505d05c7Sgtb 		   krb5_keytab *);
88505d05c7Sgtb 
89*55fea89dSDan Cross krb5_error_code KRB5_CALLCONV krb5_ktfile_wresolve
90505d05c7Sgtb 	(krb5_context,
91505d05c7Sgtb 		   const char *,
92505d05c7Sgtb 		   krb5_keytab *);
93505d05c7Sgtb 
94*55fea89dSDan Cross krb5_error_code KRB5_CALLCONV krb5_ktfile_get_name
95505d05c7Sgtb 	(krb5_context,
96505d05c7Sgtb 		   krb5_keytab,
97505d05c7Sgtb 		   char *,
98505d05c7Sgtb 		   unsigned int);
99505d05c7Sgtb 
100*55fea89dSDan Cross krb5_error_code KRB5_CALLCONV krb5_ktfile_close
101505d05c7Sgtb 	(krb5_context,
102505d05c7Sgtb 		   krb5_keytab);
103505d05c7Sgtb 
104*55fea89dSDan Cross krb5_error_code KRB5_CALLCONV krb5_ktfile_get_entry
105505d05c7Sgtb 	(krb5_context,
106505d05c7Sgtb 		   krb5_keytab,
107505d05c7Sgtb 		   krb5_const_principal,
108505d05c7Sgtb 		   krb5_kvno,
109505d05c7Sgtb 		   krb5_enctype,
110505d05c7Sgtb 		   krb5_keytab_entry *);
111505d05c7Sgtb 
112*55fea89dSDan Cross krb5_error_code KRB5_CALLCONV krb5_ktfile_start_seq_get
113505d05c7Sgtb 	(krb5_context,
114505d05c7Sgtb 		   krb5_keytab,
115505d05c7Sgtb 		   krb5_kt_cursor *);
116505d05c7Sgtb 
117*55fea89dSDan Cross krb5_error_code KRB5_CALLCONV krb5_ktfile_get_next
118505d05c7Sgtb 	(krb5_context,
119505d05c7Sgtb 		   krb5_keytab,
120505d05c7Sgtb 		   krb5_keytab_entry *,
121505d05c7Sgtb 		   krb5_kt_cursor *);
122505d05c7Sgtb 
123*55fea89dSDan Cross krb5_error_code KRB5_CALLCONV krb5_ktfile_end_get
124505d05c7Sgtb 	(krb5_context,
125505d05c7Sgtb 		   krb5_keytab,
126505d05c7Sgtb 		   krb5_kt_cursor *);
127505d05c7Sgtb 
128505d05c7Sgtb /* routines to be included on extended version (write routines) */
129*55fea89dSDan Cross krb5_error_code KRB5_CALLCONV krb5_ktfile_add
130505d05c7Sgtb 	(krb5_context,
131505d05c7Sgtb 		   krb5_keytab,
132505d05c7Sgtb 		   krb5_keytab_entry *);
133505d05c7Sgtb 
134*55fea89dSDan Cross krb5_error_code KRB5_CALLCONV krb5_ktfile_remove
135505d05c7Sgtb 	(krb5_context,
136505d05c7Sgtb 		   krb5_keytab,
137505d05c7Sgtb 		   krb5_keytab_entry *);
138505d05c7Sgtb 
139*55fea89dSDan Cross krb5_error_code krb5_ktfileint_openr
140505d05c7Sgtb 	(krb5_context,
141505d05c7Sgtb 		   krb5_keytab);
142505d05c7Sgtb 
143*55fea89dSDan Cross krb5_error_code krb5_ktfileint_openw
144505d05c7Sgtb 	(krb5_context,
145505d05c7Sgtb 		   krb5_keytab);
146505d05c7Sgtb 
147*55fea89dSDan Cross krb5_error_code krb5_ktfileint_close
148505d05c7Sgtb 	(krb5_context,
149505d05c7Sgtb 		   krb5_keytab);
150505d05c7Sgtb 
151*55fea89dSDan Cross krb5_error_code krb5_ktfileint_read_entry
152505d05c7Sgtb 	(krb5_context,
153505d05c7Sgtb 		   krb5_keytab,
154505d05c7Sgtb 		   krb5_keytab_entry *);
155505d05c7Sgtb 
156*55fea89dSDan Cross krb5_error_code krb5_ktfileint_write_entry
157505d05c7Sgtb 	(krb5_context,
158505d05c7Sgtb 		   krb5_keytab,
159505d05c7Sgtb 		   krb5_keytab_entry *);
160505d05c7Sgtb 
161*55fea89dSDan Cross krb5_error_code krb5_ktfileint_delete_entry
162505d05c7Sgtb 	(krb5_context,
163505d05c7Sgtb 		   krb5_keytab,
164505d05c7Sgtb                    krb5_int32);
165505d05c7Sgtb 
166*55fea89dSDan Cross krb5_error_code krb5_ktfileint_internal_read_entry
167505d05c7Sgtb 	(krb5_context,
168505d05c7Sgtb 		   krb5_keytab,
169505d05c7Sgtb 		   krb5_keytab_entry *,
170505d05c7Sgtb                    krb5_int32 *);
171505d05c7Sgtb 
172*55fea89dSDan Cross krb5_error_code krb5_ktfileint_size_entry
173505d05c7Sgtb 	(krb5_context,
174505d05c7Sgtb 		   krb5_keytab_entry *,
175505d05c7Sgtb                    krb5_int32 *);
176505d05c7Sgtb 
177*55fea89dSDan Cross krb5_error_code krb5_ktfileint_find_slot
178505d05c7Sgtb 	(krb5_context,
179505d05c7Sgtb 		   krb5_keytab,
180505d05c7Sgtb                    krb5_int32 *,
181505d05c7Sgtb                    krb5_int32 *);
182505d05c7Sgtb 
183505d05c7Sgtb 
184505d05c7Sgtb /*
185*55fea89dSDan Cross  * This is an implementation specific resolver.  It returns a keytab id
186505d05c7Sgtb  * initialized with file keytab routines.
187505d05c7Sgtb  */
188505d05c7Sgtb 
189*55fea89dSDan Cross krb5_error_code KRB5_CALLCONV
krb5_ktfile_resolve(krb5_context context,const char * name,krb5_keytab * id)190505d05c7Sgtb krb5_ktfile_resolve(krb5_context context, const char *name, krb5_keytab *id)
191505d05c7Sgtb {
192505d05c7Sgtb     krb5_ktfile_data *data;
193505d05c7Sgtb     krb5_error_code err;
194505d05c7Sgtb 
195505d05c7Sgtb     if ((*id = (krb5_keytab) malloc(sizeof(**id))) == NULL)
196505d05c7Sgtb 	return(ENOMEM);
197*55fea89dSDan Cross 
198505d05c7Sgtb     (*id)->ops = &krb5_ktf_ops;
199505d05c7Sgtb     if ((data = (krb5_ktfile_data *)malloc(sizeof(krb5_ktfile_data))) == NULL) {
200505d05c7Sgtb 	krb5_xfree(*id);
201505d05c7Sgtb 	return(ENOMEM);
202505d05c7Sgtb     }
203505d05c7Sgtb 
204505d05c7Sgtb     err = k5_mutex_init(&data->lock);
205505d05c7Sgtb     if (err) {
206159d09a2SMark Phalan 	krb5_xfree(data);
207505d05c7Sgtb 	krb5_xfree(*id);
208505d05c7Sgtb 	return err;
209505d05c7Sgtb     }
210505d05c7Sgtb 
211505d05c7Sgtb     if ((data->name = (char *)calloc(strlen(name) + 1, sizeof(char))) == NULL) {
212505d05c7Sgtb 	k5_mutex_destroy(&data->lock);
213505d05c7Sgtb 	krb5_xfree(data);
214505d05c7Sgtb 	krb5_xfree(*id);
215505d05c7Sgtb 	return(ENOMEM);
216505d05c7Sgtb     }
217505d05c7Sgtb 
218505d05c7Sgtb     (void) strcpy(data->name, name);
219505d05c7Sgtb     data->openf = 0;
220505d05c7Sgtb     data->version = 0;
221505d05c7Sgtb 
222505d05c7Sgtb     (*id)->data = (krb5_pointer)data;
223505d05c7Sgtb     (*id)->magic = KV5M_KEYTAB;
224505d05c7Sgtb     return(0);
225505d05c7Sgtb }
226505d05c7Sgtb 
227505d05c7Sgtb 
228505d05c7Sgtb /*
229505d05c7Sgtb  * "Close" a file-based keytab and invalidate the id.  This means
230505d05c7Sgtb  * free memory hidden in the structures.
231505d05c7Sgtb  */
232505d05c7Sgtb 
233*55fea89dSDan Cross krb5_error_code KRB5_CALLCONV
krb5_ktfile_close(krb5_context context,krb5_keytab id)234505d05c7Sgtb krb5_ktfile_close(krb5_context context, krb5_keytab id)
235505d05c7Sgtb   /*
236*55fea89dSDan Cross    * This routine is responsible for freeing all memory allocated
237505d05c7Sgtb    * for this keytab.  There are no system resources that need
238505d05c7Sgtb    * to be freed nor are there any open files.
239505d05c7Sgtb    *
240505d05c7Sgtb    * This routine should undo anything done by krb5_ktfile_resolve().
241505d05c7Sgtb    */
242505d05c7Sgtb {
243505d05c7Sgtb     krb5_xfree(KTFILENAME(id));
244505d05c7Sgtb     zap(KTFILEBUFP(id), BUFSIZ);
245505d05c7Sgtb     k5_mutex_destroy(&((krb5_ktfile_data *)id->data)->lock);
246505d05c7Sgtb     krb5_xfree(id->data);
247505d05c7Sgtb     id->ops = 0;
248505d05c7Sgtb     krb5_xfree(id);
249505d05c7Sgtb     return (0);
250505d05c7Sgtb }
251505d05c7Sgtb 
252505d05c7Sgtb /*
253505d05c7Sgtb  * This is the get_entry routine for the file based keytab implementation.
254505d05c7Sgtb  * It opens the keytab file, and either retrieves the entry or returns
255505d05c7Sgtb  * an error.
256505d05c7Sgtb  */
257505d05c7Sgtb 
258505d05c7Sgtb krb5_error_code KRB5_CALLCONV
krb5_ktfile_get_entry(krb5_context context,krb5_keytab id,krb5_const_principal principal,krb5_kvno kvno,krb5_enctype enctype,krb5_keytab_entry * entry)259505d05c7Sgtb krb5_ktfile_get_entry(krb5_context context, krb5_keytab id,
260505d05c7Sgtb 		      krb5_const_principal principal, krb5_kvno kvno,
261505d05c7Sgtb 		      krb5_enctype enctype, krb5_keytab_entry *entry)
262505d05c7Sgtb {
263505d05c7Sgtb     krb5_keytab_entry cur_entry, new_entry;
264505d05c7Sgtb     krb5_error_code kerror = 0;
265505d05c7Sgtb     int found_wrong_kvno = 0;
266505d05c7Sgtb     krb5_boolean similar;
267505d05c7Sgtb     int kvno_offset = 0;
268505d05c7Sgtb 
269505d05c7Sgtb     kerror = KTLOCK(id);
270505d05c7Sgtb     if (kerror)
271505d05c7Sgtb 	return kerror;
272505d05c7Sgtb 
273505d05c7Sgtb     /* Open the keyfile for reading */
274505d05c7Sgtb     if ((kerror = krb5_ktfileint_openr(context, id))) {
275505d05c7Sgtb 	KTUNLOCK(id);
276505d05c7Sgtb 	return(kerror);
277505d05c7Sgtb     }
278*55fea89dSDan Cross 
279*55fea89dSDan Cross     /*
280*55fea89dSDan Cross      * For efficiency and simplicity, we'll use a while true that
281505d05c7Sgtb      * is exited with a break statement.
282505d05c7Sgtb      */
283505d05c7Sgtb     cur_entry.principal = 0;
284505d05c7Sgtb     cur_entry.vno = 0;
285505d05c7Sgtb     cur_entry.key.contents = 0;
286505d05c7Sgtb 
287505d05c7Sgtb     while (TRUE) {
288505d05c7Sgtb 	if ((kerror = krb5_ktfileint_read_entry(context, id, &new_entry)))
289505d05c7Sgtb 	    break;
290505d05c7Sgtb 
291505d05c7Sgtb 	/* by the time this loop exits, it must either free cur_entry,
292505d05c7Sgtb 	   and copy new_entry there, or free new_entry.  Otherwise, it
293505d05c7Sgtb 	   leaks. */
294505d05c7Sgtb 
295505d05c7Sgtb 	/* if the principal isn't the one requested, free new_entry
296505d05c7Sgtb 	   and continue to the next. */
297505d05c7Sgtb 
29872f0806aSShawn Emery 	/*
29972f0806aSShawn Emery 	 * Solaris Kerberos: MS Interop requires that case insensitive
30072f0806aSShawn Emery 	 * comparisons of service and host components are performed for key
30172f0806aSShawn Emery 	 * table lookup, etc.  Only called if the private environment variable
30272f0806aSShawn Emery 	 * MS_INTEROP is defined.
30372f0806aSShawn Emery 	 */
30472f0806aSShawn Emery 	if (krb5_getenv("MS_INTEROP")) {
30572f0806aSShawn Emery 	  if (!__krb5_principal_compare_case_ins(context, principal,
30672f0806aSShawn Emery 	    new_entry.principal)) {
30772f0806aSShawn Emery 	    	krb5_kt_free_entry(context, &new_entry);
30872f0806aSShawn Emery 	    	continue;
30972f0806aSShawn Emery 	  }
31072f0806aSShawn Emery 	} else if (!krb5_principal_compare(context, principal,
31172f0806aSShawn Emery 	  new_entry.principal)) {
312505d05c7Sgtb 	    krb5_kt_free_entry(context, &new_entry);
313505d05c7Sgtb 	    continue;
314505d05c7Sgtb 	}
315505d05c7Sgtb 
316505d05c7Sgtb 	/* if the enctype is not ignored and doesn't match, free new_entry
317505d05c7Sgtb 	   and continue to the next */
318505d05c7Sgtb 
319505d05c7Sgtb 	if (enctype != IGNORE_ENCTYPE) {
320*55fea89dSDan Cross 	    if ((kerror = krb5_c_enctype_compare(context, enctype,
321505d05c7Sgtb 						 new_entry.key.enctype,
322505d05c7Sgtb 						 &similar))) {
323505d05c7Sgtb 		krb5_kt_free_entry(context, &new_entry);
324505d05c7Sgtb 		break;
325505d05c7Sgtb 	    }
326505d05c7Sgtb 
327505d05c7Sgtb 	    if (!similar) {
328505d05c7Sgtb 		krb5_kt_free_entry(context, &new_entry);
329505d05c7Sgtb 		continue;
330505d05c7Sgtb 	    }
331505d05c7Sgtb 	    /*
332505d05c7Sgtb 	     * Coerce the enctype of the output keyblock in case we
333505d05c7Sgtb 	     * got an inexact match on the enctype.
334505d05c7Sgtb 	     */
335505d05c7Sgtb 	    new_entry.key.enctype = enctype;
336505d05c7Sgtb 
337505d05c7Sgtb 	}
338505d05c7Sgtb 
339505d05c7Sgtb 	if (kvno == IGNORE_VNO) {
340505d05c7Sgtb 	    /* if this is the first match, or if the new vno is
341505d05c7Sgtb 	       bigger, free the current and keep the new.  Otherwise,
342505d05c7Sgtb 	       free the new. */
343505d05c7Sgtb 	    /* A 1.2.x keytab contains only the low 8 bits of the key
344505d05c7Sgtb 	       version number.  Since it can be much bigger, and thus
345505d05c7Sgtb 	       the 8-bit value can wrap, we need some heuristics to
346505d05c7Sgtb 	       figure out the "highest" numbered key if some numbers
347505d05c7Sgtb 	       close to 255 and some near 0 are used.
348505d05c7Sgtb 
349505d05c7Sgtb 	       The heuristic here:
350505d05c7Sgtb 
351505d05c7Sgtb 	       If we have any keys with versions over 240, then assume
352505d05c7Sgtb 	       that all version numbers 0-127 refer to 256+N instead.
353505d05c7Sgtb 	       Not perfect, but maybe good enough?  */
354505d05c7Sgtb 
355505d05c7Sgtb #define M(VNO) (((VNO) - kvno_offset + 256) % 256)
356505d05c7Sgtb 
357505d05c7Sgtb 	    if (new_entry.vno > 240)
358505d05c7Sgtb 		kvno_offset = 128;
359505d05c7Sgtb 	    if (! cur_entry.principal ||
360505d05c7Sgtb 		M(new_entry.vno) > M(cur_entry.vno)) {
361505d05c7Sgtb 		krb5_kt_free_entry(context, &cur_entry);
362505d05c7Sgtb 		cur_entry = new_entry;
363505d05c7Sgtb 	    } else {
364505d05c7Sgtb 		krb5_kt_free_entry(context, &new_entry);
365505d05c7Sgtb 	    }
366505d05c7Sgtb 	} else {
367505d05c7Sgtb 	    /* if this kvno matches, free the current (will there ever
368505d05c7Sgtb 	       be one?), keep the new, and break out.  Otherwise, remember
369505d05c7Sgtb 	       that we were here so we can return the right error, and
370505d05c7Sgtb 	       free the new */
371505d05c7Sgtb 	    /* Yuck.  The krb5-1.2.x keytab format only stores one byte
372505d05c7Sgtb 	       for the kvno, so we're toast if the kvno requested is
373505d05c7Sgtb 	       higher than that.  Short-term workaround: only compare
374505d05c7Sgtb 	       the low 8 bits.  */
375505d05c7Sgtb 
376505d05c7Sgtb 	    if (new_entry.vno == (kvno & 0xff)) {
377505d05c7Sgtb 		krb5_kt_free_entry(context, &cur_entry);
378505d05c7Sgtb 		cur_entry = new_entry;
379505d05c7Sgtb 		break;
380505d05c7Sgtb 	    } else {
381505d05c7Sgtb 		found_wrong_kvno++;
382505d05c7Sgtb 		krb5_kt_free_entry(context, &new_entry);
383505d05c7Sgtb 	    }
384505d05c7Sgtb 	}
385505d05c7Sgtb     }
386505d05c7Sgtb 
387505d05c7Sgtb     if (kerror == KRB5_KT_END) {
388505d05c7Sgtb 	 if (cur_entry.principal)
389505d05c7Sgtb 	      kerror = 0;
390505d05c7Sgtb 	 else if (found_wrong_kvno)
391505d05c7Sgtb 	      kerror = KRB5_KT_KVNONOTFOUND;
392505d05c7Sgtb 	 else
393505d05c7Sgtb 	      kerror = KRB5_KT_NOTFOUND;
394505d05c7Sgtb     }
395505d05c7Sgtb     if (kerror) {
396505d05c7Sgtb 	(void) krb5_ktfileint_close(context, id);
397505d05c7Sgtb 	KTUNLOCK(id);
398505d05c7Sgtb 	krb5_kt_free_entry(context, &cur_entry);
399505d05c7Sgtb 	return kerror;
400505d05c7Sgtb     }
401505d05c7Sgtb     if ((kerror = krb5_ktfileint_close(context, id)) != 0) {
402505d05c7Sgtb 	KTUNLOCK(id);
403505d05c7Sgtb 	krb5_kt_free_entry(context, &cur_entry);
404505d05c7Sgtb 	return kerror;
405505d05c7Sgtb     }
406505d05c7Sgtb     KTUNLOCK(id);
407505d05c7Sgtb     *entry = cur_entry;
408505d05c7Sgtb     return 0;
409505d05c7Sgtb }
410505d05c7Sgtb 
411505d05c7Sgtb /*
412505d05c7Sgtb  * Get the name of the file containing a file-based keytab.
413505d05c7Sgtb  */
414505d05c7Sgtb 
415505d05c7Sgtb krb5_error_code KRB5_CALLCONV
krb5_ktfile_get_name(krb5_context context,krb5_keytab id,char * name,unsigned int len)416505d05c7Sgtb krb5_ktfile_get_name(krb5_context context, krb5_keytab id, char *name, unsigned int len)
417*55fea89dSDan Cross   /*
418505d05c7Sgtb    * This routine returns the name of the name of the file associated with
419505d05c7Sgtb    * this file-based keytab.  name is zeroed and the filename is truncated
420505d05c7Sgtb    * to fit in name if necessary.  The name is prefixed with PREFIX:, so that
421505d05c7Sgtb    * trt will happen if the name is passed back to resolve.
422505d05c7Sgtb    */
423505d05c7Sgtb {
424505d05c7Sgtb     memset(name, 0, len);
425505d05c7Sgtb 
426505d05c7Sgtb     if (len < strlen(id->ops->prefix)+2)
427505d05c7Sgtb 	return(KRB5_KT_NAME_TOOLONG);
428505d05c7Sgtb     strcpy(name, id->ops->prefix);
429505d05c7Sgtb     name += strlen(id->ops->prefix);
430505d05c7Sgtb     name[0] = ':';
431505d05c7Sgtb     name++;
432505d05c7Sgtb     len -= strlen(id->ops->prefix)+1;
433505d05c7Sgtb 
434159d09a2SMark Phalan     /* Solaris Kerberos */
43554925bf6Swillf     if (len < strlen(KTFILENAME(id))+1)
436505d05c7Sgtb 	return(KRB5_KT_NAME_TOOLONG);
437505d05c7Sgtb     strcpy(name, KTFILENAME(id));
438505d05c7Sgtb     /* strcpy will NUL-terminate the destination */
439505d05c7Sgtb 
440505d05c7Sgtb     return(0);
441505d05c7Sgtb }
442505d05c7Sgtb 
443505d05c7Sgtb /*
444505d05c7Sgtb  * krb5_ktfile_start_seq_get()
445505d05c7Sgtb  */
446505d05c7Sgtb 
447505d05c7Sgtb krb5_error_code KRB5_CALLCONV
krb5_ktfile_start_seq_get(krb5_context context,krb5_keytab id,krb5_kt_cursor * cursorp)448505d05c7Sgtb krb5_ktfile_start_seq_get(krb5_context context, krb5_keytab id, krb5_kt_cursor *cursorp)
449505d05c7Sgtb {
450505d05c7Sgtb     krb5_error_code retval;
451505d05c7Sgtb     long *fileoff;
452505d05c7Sgtb 
453505d05c7Sgtb     retval = KTLOCK(id);
454505d05c7Sgtb     if (retval)
455505d05c7Sgtb 	return retval;
456505d05c7Sgtb 
457505d05c7Sgtb     if ((retval = krb5_ktfileint_openr(context, id))) {
458505d05c7Sgtb 	KTUNLOCK(id);
459505d05c7Sgtb 	return retval;
460505d05c7Sgtb     }
461505d05c7Sgtb 
462505d05c7Sgtb     if (!(fileoff = (long *)malloc(sizeof(*fileoff)))) {
463505d05c7Sgtb 	krb5_ktfileint_close(context, id);
464505d05c7Sgtb 	KTUNLOCK(id);
465505d05c7Sgtb 	return ENOMEM;
466505d05c7Sgtb     }
467505d05c7Sgtb     *fileoff = ftell(KTFILEP(id));
468505d05c7Sgtb     *cursorp = (krb5_kt_cursor)fileoff;
469505d05c7Sgtb     KTUNLOCK(id);
470505d05c7Sgtb 
471505d05c7Sgtb     return 0;
472505d05c7Sgtb }
473505d05c7Sgtb 
474505d05c7Sgtb /*
475505d05c7Sgtb  * krb5_ktfile_get_next()
476505d05c7Sgtb  */
477505d05c7Sgtb 
478*55fea89dSDan Cross krb5_error_code KRB5_CALLCONV
krb5_ktfile_get_next(krb5_context context,krb5_keytab id,krb5_keytab_entry * entry,krb5_kt_cursor * cursor)479505d05c7Sgtb krb5_ktfile_get_next(krb5_context context, krb5_keytab id, krb5_keytab_entry *entry, krb5_kt_cursor *cursor)
480505d05c7Sgtb {
481505d05c7Sgtb     long *fileoff = (long *)*cursor;
482505d05c7Sgtb     krb5_keytab_entry cur_entry;
483505d05c7Sgtb     krb5_error_code kerror;
484505d05c7Sgtb 
485505d05c7Sgtb     kerror = KTLOCK(id);
486505d05c7Sgtb     if (kerror)
487505d05c7Sgtb 	return kerror;
488159d09a2SMark Phalan     if (KTFILEP(id) == NULL) {
489159d09a2SMark Phalan 	KTUNLOCK(id);
490159d09a2SMark Phalan 	return KRB5_KT_IOERR;
491159d09a2SMark Phalan     }
492505d05c7Sgtb     if (fseek(KTFILEP(id), *fileoff, 0) == -1) {
493505d05c7Sgtb 	KTUNLOCK(id);
494505d05c7Sgtb 	return KRB5_KT_END;
495505d05c7Sgtb     }
496505d05c7Sgtb     if ((kerror = krb5_ktfileint_read_entry(context, id, &cur_entry))) {
497505d05c7Sgtb 	KTUNLOCK(id);
498505d05c7Sgtb 	return kerror;
499505d05c7Sgtb     }
500505d05c7Sgtb     *fileoff = ftell(KTFILEP(id));
501505d05c7Sgtb     *entry = cur_entry;
502505d05c7Sgtb     KTUNLOCK(id);
503505d05c7Sgtb     return 0;
504505d05c7Sgtb }
505505d05c7Sgtb 
506505d05c7Sgtb /*
507505d05c7Sgtb  * krb5_ktfile_end_get()
508505d05c7Sgtb  */
509505d05c7Sgtb 
510*55fea89dSDan Cross krb5_error_code KRB5_CALLCONV
krb5_ktfile_end_get(krb5_context context,krb5_keytab id,krb5_kt_cursor * cursor)511505d05c7Sgtb krb5_ktfile_end_get(krb5_context context, krb5_keytab id, krb5_kt_cursor *cursor)
512505d05c7Sgtb {
513505d05c7Sgtb     krb5_error_code kerror;
514505d05c7Sgtb 
515505d05c7Sgtb     krb5_xfree(*cursor);
516505d05c7Sgtb     KTLOCK(id);
517505d05c7Sgtb     kerror = krb5_ktfileint_close(context, id);
518505d05c7Sgtb     KTUNLOCK(id);
519505d05c7Sgtb     return kerror;
520505d05c7Sgtb }
521505d05c7Sgtb 
522505d05c7Sgtb /*
523505d05c7Sgtb  * ser_ktf.c - Serialize keytab file context for subsequent reopen.
524505d05c7Sgtb  */
525505d05c7Sgtb 
526505d05c7Sgtb static const char ktfile_def_name[] = ".";
527505d05c7Sgtb 
528505d05c7Sgtb /*
529505d05c7Sgtb  * Routines to deal with externalizing krb5_keytab for [WR]FILE: variants.
530505d05c7Sgtb  *	krb5_ktf_keytab_size();
531505d05c7Sgtb  *	krb5_ktf_keytab_externalize();
532505d05c7Sgtb  *	krb5_ktf_keytab_internalize();
533505d05c7Sgtb  */
534505d05c7Sgtb static krb5_error_code krb5_ktf_keytab_size
535505d05c7Sgtb 	(krb5_context, krb5_pointer, size_t *);
536505d05c7Sgtb static krb5_error_code krb5_ktf_keytab_externalize
537505d05c7Sgtb 	(krb5_context, krb5_pointer, krb5_octet **, size_t *);
538505d05c7Sgtb static krb5_error_code krb5_ktf_keytab_internalize
539505d05c7Sgtb 	(krb5_context,krb5_pointer *, krb5_octet **, size_t *);
540505d05c7Sgtb 
541505d05c7Sgtb /*
542505d05c7Sgtb  * Serialization entry for this type.
543505d05c7Sgtb  */
544505d05c7Sgtb const krb5_ser_entry krb5_ktfile_ser_entry = {
545505d05c7Sgtb     KV5M_KEYTAB,			/* Type			*/
546505d05c7Sgtb     krb5_ktf_keytab_size,		/* Sizer routine	*/
547505d05c7Sgtb     krb5_ktf_keytab_externalize,	/* Externalize routine	*/
548505d05c7Sgtb     krb5_ktf_keytab_internalize		/* Internalize routine	*/
549505d05c7Sgtb };
550505d05c7Sgtb 
551505d05c7Sgtb /*
552505d05c7Sgtb  * krb5_ktf_keytab_size()	- Determine the size required to externalize
553505d05c7Sgtb  *				  this krb5_keytab variant.
554505d05c7Sgtb  */
555505d05c7Sgtb static krb5_error_code
krb5_ktf_keytab_size(krb5_context kcontext,krb5_pointer arg,size_t * sizep)556505d05c7Sgtb krb5_ktf_keytab_size(krb5_context kcontext, krb5_pointer arg, size_t *sizep)
557505d05c7Sgtb {
558505d05c7Sgtb     krb5_error_code	kret;
559505d05c7Sgtb     krb5_keytab		keytab;
560505d05c7Sgtb     size_t		required;
561505d05c7Sgtb     krb5_ktfile_data	*ktdata;
562505d05c7Sgtb 
563505d05c7Sgtb     kret = EINVAL;
564505d05c7Sgtb     if ((keytab = (krb5_keytab) arg)) {
565505d05c7Sgtb 	/*
566505d05c7Sgtb 	 * Saving FILE: variants of krb5_keytab requires at minimum:
567505d05c7Sgtb 	 *	krb5_int32	for KV5M_KEYTAB
568505d05c7Sgtb 	 *	krb5_int32	for length of keytab name.
569505d05c7Sgtb 	 *	krb5_int32	for file status.
570505d05c7Sgtb 	 *	krb5_int32	for file position.
571505d05c7Sgtb 	 *	krb5_int32	for file position.
572505d05c7Sgtb 	 *	krb5_int32	for version.
573505d05c7Sgtb 	 *	krb5_int32	for KV5M_KEYTAB
574505d05c7Sgtb 	 */
575505d05c7Sgtb 	required = sizeof(krb5_int32) * 7;
576505d05c7Sgtb 	if (keytab->ops && keytab->ops->prefix)
577505d05c7Sgtb 	    required += (strlen(keytab->ops->prefix)+1);
578505d05c7Sgtb 
579505d05c7Sgtb 	/*
580505d05c7Sgtb 	 * The keytab name is formed as follows:
581505d05c7Sgtb 	 *	<prefix>:<name>
582505d05c7Sgtb 	 * If there's no name, we use a default name so that we have something
583505d05c7Sgtb 	 * to call krb5_keytab_resolve with.
584505d05c7Sgtb 	 */
585505d05c7Sgtb 	ktdata = (krb5_ktfile_data *) keytab->data;
586505d05c7Sgtb 	required += strlen((ktdata && ktdata->name) ?
587505d05c7Sgtb 			   ktdata->name : ktfile_def_name);
588505d05c7Sgtb 	kret = 0;
589505d05c7Sgtb 
590159d09a2SMark Phalan 	if (!kret)
591159d09a2SMark Phalan 	    *sizep += required;
592505d05c7Sgtb     }
593505d05c7Sgtb     return(kret);
594505d05c7Sgtb }
595505d05c7Sgtb 
596505d05c7Sgtb /*
597505d05c7Sgtb  * krb5_ktf_keytab_externalize()	- Externalize the krb5_keytab.
598505d05c7Sgtb  */
599505d05c7Sgtb static krb5_error_code
krb5_ktf_keytab_externalize(krb5_context kcontext,krb5_pointer arg,krb5_octet ** buffer,size_t * lenremain)600505d05c7Sgtb krb5_ktf_keytab_externalize(krb5_context kcontext, krb5_pointer arg, krb5_octet **buffer, size_t *lenremain)
601505d05c7Sgtb {
602505d05c7Sgtb     krb5_error_code	kret;
603505d05c7Sgtb     krb5_keytab		keytab;
604505d05c7Sgtb     size_t		required;
605505d05c7Sgtb     krb5_octet		*bp;
606505d05c7Sgtb     size_t		remain;
607505d05c7Sgtb     krb5_ktfile_data	*ktdata;
608505d05c7Sgtb     krb5_int32		file_is_open;
609159d09a2SMark Phalan     krb5_int64		file_pos;
610505d05c7Sgtb     char		*ktname;
611505d05c7Sgtb     size_t		namelen;
612505d05c7Sgtb     const char		*fnamep;
613505d05c7Sgtb 
614505d05c7Sgtb     required = 0;
615505d05c7Sgtb     bp = *buffer;
616505d05c7Sgtb     remain = *lenremain;
617505d05c7Sgtb     kret = EINVAL;
618505d05c7Sgtb     if ((keytab = (krb5_keytab) arg)) {
619505d05c7Sgtb 	kret = ENOMEM;
620505d05c7Sgtb 	if (!krb5_ktf_keytab_size(kcontext, arg, &required) &&
621505d05c7Sgtb 	    (required <= remain)) {
622505d05c7Sgtb 	    /* Our identifier */
623505d05c7Sgtb 	    (void) krb5_ser_pack_int32(KV5M_KEYTAB, &bp, &remain);
624505d05c7Sgtb 
625505d05c7Sgtb 	    ktdata = (krb5_ktfile_data *) keytab->data;
626505d05c7Sgtb 	    file_is_open = 0;
627159d09a2SMark Phalan 	    file_pos = 0;
628505d05c7Sgtb 
629505d05c7Sgtb 	    /* Calculate the length of the name */
630505d05c7Sgtb 	    namelen = (keytab->ops && keytab->ops->prefix) ?
631505d05c7Sgtb 		strlen(keytab->ops->prefix)+1 : 0;
632505d05c7Sgtb 	    if (ktdata && ktdata->name)
633505d05c7Sgtb 		fnamep = ktdata->name;
634505d05c7Sgtb 	    else
635505d05c7Sgtb 		fnamep = ktfile_def_name;
636505d05c7Sgtb 	    namelen += (strlen(fnamep)+1);
637505d05c7Sgtb 
638505d05c7Sgtb 	    if ((ktname = (char *) malloc(namelen))) {
639505d05c7Sgtb 		/* Format the keytab name. */
640505d05c7Sgtb 		if (keytab->ops && keytab->ops->prefix)
641505d05c7Sgtb 		    sprintf(ktname, "%s:%s", keytab->ops->prefix, fnamep);
642505d05c7Sgtb 
643505d05c7Sgtb 		else
644505d05c7Sgtb 		    strcpy(ktname, fnamep);
645505d05c7Sgtb 
646505d05c7Sgtb 		/* Fill in the file-specific keytab information. */
647505d05c7Sgtb 		if (ktdata) {
648505d05c7Sgtb 		    if (ktdata->openf) {
649505d05c7Sgtb 			long	fpos;
650505d05c7Sgtb 			int	fflags = 0;
651505d05c7Sgtb 
652505d05c7Sgtb 			file_is_open = 1;
653505d05c7Sgtb #if !defined(_WIN32)
654505d05c7Sgtb 			fflags = fcntl(fileno(ktdata->openf), F_GETFL, 0);
655505d05c7Sgtb 			if (fflags > 0)
656505d05c7Sgtb 			    file_is_open |= ((fflags & O_ACCMODE) << 1);
657505d05c7Sgtb #else
658505d05c7Sgtb 			file_is_open = 0;
659505d05c7Sgtb #endif
660505d05c7Sgtb 			fpos = ftell(ktdata->openf);
661159d09a2SMark Phalan 			file_pos = fpos; /* XX range check? */
662505d05c7Sgtb 		    }
663505d05c7Sgtb 		}
664505d05c7Sgtb 
665505d05c7Sgtb 		/* Put the length of the file name */
666505d05c7Sgtb 		(void) krb5_ser_pack_int32((krb5_int32) strlen(ktname),
667505d05c7Sgtb 					   &bp, &remain);
668*55fea89dSDan Cross 
669505d05c7Sgtb 		/* Put the name */
670505d05c7Sgtb 		(void) krb5_ser_pack_bytes((krb5_octet *) ktname,
671505d05c7Sgtb 					   strlen(ktname),
672505d05c7Sgtb 					   &bp, &remain);
673505d05c7Sgtb 
674505d05c7Sgtb 		/* Put the file open flag */
675505d05c7Sgtb 		(void) krb5_ser_pack_int32(file_is_open, &bp, &remain);
676505d05c7Sgtb 
677505d05c7Sgtb 		/* Put the file position */
678159d09a2SMark Phalan 		(void) krb5_ser_pack_int64(file_pos, &bp, &remain);
679505d05c7Sgtb 
680505d05c7Sgtb 		/* Put the version */
681505d05c7Sgtb 		(void) krb5_ser_pack_int32((krb5_int32) ((ktdata) ?
682505d05c7Sgtb 							 ktdata->version : 0),
683505d05c7Sgtb 					   &bp, &remain);
684505d05c7Sgtb 
685505d05c7Sgtb 		/* Put the trailer */
686505d05c7Sgtb 		(void) krb5_ser_pack_int32(KV5M_KEYTAB, &bp, &remain);
687505d05c7Sgtb 		kret = 0;
688505d05c7Sgtb 		*buffer = bp;
689505d05c7Sgtb 		*lenremain = remain;
690505d05c7Sgtb 		free(ktname);
691505d05c7Sgtb 	    }
692505d05c7Sgtb 	}
693505d05c7Sgtb     }
694505d05c7Sgtb     return(kret);
695505d05c7Sgtb }
696505d05c7Sgtb 
697505d05c7Sgtb /*
698505d05c7Sgtb  * krb5_ktf_keytab_internalize()	- Internalize the krb5_ktf_keytab.
699505d05c7Sgtb  */
700505d05c7Sgtb static krb5_error_code
krb5_ktf_keytab_internalize(krb5_context kcontext,krb5_pointer * argp,krb5_octet ** buffer,size_t * lenremain)701505d05c7Sgtb krb5_ktf_keytab_internalize(krb5_context kcontext, krb5_pointer *argp, krb5_octet **buffer, size_t *lenremain)
702505d05c7Sgtb {
703505d05c7Sgtb     krb5_error_code	kret;
704505d05c7Sgtb     krb5_keytab		keytab;
705505d05c7Sgtb     krb5_int32		ibuf;
706505d05c7Sgtb     krb5_octet		*bp;
707505d05c7Sgtb     size_t		remain;
708505d05c7Sgtb     char		*ktname;
709505d05c7Sgtb     krb5_ktfile_data	*ktdata;
710505d05c7Sgtb     krb5_int32		file_is_open;
711159d09a2SMark Phalan     krb5_int64		foff;
712505d05c7Sgtb 
713505d05c7Sgtb     bp = *buffer;
714505d05c7Sgtb     remain = *lenremain;
715505d05c7Sgtb     kret = EINVAL;
716505d05c7Sgtb     /* Read our magic number */
717505d05c7Sgtb     if (krb5_ser_unpack_int32(&ibuf, &bp, &remain))
718505d05c7Sgtb 	ibuf = 0;
719505d05c7Sgtb     if (ibuf == KV5M_KEYTAB) {
720505d05c7Sgtb 	kret = ENOMEM;
721505d05c7Sgtb 
722505d05c7Sgtb 	/* Get the length of the keytab name */
723505d05c7Sgtb 	kret = krb5_ser_unpack_int32(&ibuf, &bp, &remain);
724505d05c7Sgtb 
725505d05c7Sgtb 	if (!kret &&
726505d05c7Sgtb 	    (ktname = (char *) malloc((size_t) (ibuf+1))) &&
727505d05c7Sgtb 	    !(kret = krb5_ser_unpack_bytes((krb5_octet *) ktname,
728505d05c7Sgtb 					   (size_t) ibuf,
729505d05c7Sgtb 					   &bp, &remain))) {
730505d05c7Sgtb 	    ktname[ibuf] = '\0';
731505d05c7Sgtb 	    kret = krb5_kt_resolve(kcontext, ktname, &keytab);
732505d05c7Sgtb 	    if (!kret) {
733505d05c7Sgtb 		kret = ENOMEM;
734505d05c7Sgtb 		ktdata = (krb5_ktfile_data *) keytab->data;
735505d05c7Sgtb 		if (!ktdata) {
736505d05c7Sgtb 		    /* XXX */
737505d05c7Sgtb 		    keytab->data = (void *) malloc(sizeof(krb5_ktfile_data));
738505d05c7Sgtb 		    ktdata = (krb5_ktfile_data *) keytab->data;
739505d05c7Sgtb 		    memset(ktdata, 0, sizeof(krb5_ktfile_data));
740505d05c7Sgtb 		    if (strchr(ktname, (int) ':'))
741505d05c7Sgtb 			ktdata->name = strdup(strchr(ktname, (int) ':')+1);
742505d05c7Sgtb 		    else
743505d05c7Sgtb 			ktdata->name = strdup(ktname);
744505d05c7Sgtb 		}
745505d05c7Sgtb 		if (ktdata) {
746505d05c7Sgtb 		    if (remain >= (sizeof(krb5_int32)*5)) {
747505d05c7Sgtb 			(void) krb5_ser_unpack_int32(&file_is_open,
748505d05c7Sgtb 						     &bp, &remain);
749159d09a2SMark Phalan 			(void) krb5_ser_unpack_int64(&foff, &bp, &remain);
750505d05c7Sgtb 			(void) krb5_ser_unpack_int32(&ibuf, &bp, &remain);
751505d05c7Sgtb 			ktdata->version = (int) ibuf;
752505d05c7Sgtb 
753505d05c7Sgtb 			(void) krb5_ser_unpack_int32(&ibuf, &bp, &remain);
754505d05c7Sgtb 			if (ibuf == KV5M_KEYTAB) {
755505d05c7Sgtb 			    if (file_is_open) {
756505d05c7Sgtb 				int 	fmode;
757505d05c7Sgtb 				long	fpos;
758505d05c7Sgtb 
759505d05c7Sgtb #if !defined(_WIN32)
760505d05c7Sgtb 				fmode = (file_is_open >> 1) & O_ACCMODE;
761505d05c7Sgtb #else
762505d05c7Sgtb 				fmode = 0;
763505d05c7Sgtb #endif
764505d05c7Sgtb 				if (fmode)
765505d05c7Sgtb 				    kret = krb5_ktfileint_openw(kcontext,
766505d05c7Sgtb 								keytab);
767505d05c7Sgtb 				else
768505d05c7Sgtb 				    kret = krb5_ktfileint_openr(kcontext,
769505d05c7Sgtb 								keytab);
770505d05c7Sgtb 				if (!kret) {
771159d09a2SMark Phalan 				    fpos = foff; /* XX range check? */
772505d05c7Sgtb 				    fseek(KTFILEP(keytab), fpos, SEEK_SET);
773505d05c7Sgtb 				}
774505d05c7Sgtb 			    }
775505d05c7Sgtb 			    kret = 0;
776505d05c7Sgtb 			}
777505d05c7Sgtb 			else
778505d05c7Sgtb 			    kret = EINVAL;
779505d05c7Sgtb 		    }
780505d05c7Sgtb 		}
781505d05c7Sgtb 		if (kret) {
782505d05c7Sgtb 		    if (keytab->data) {
783505d05c7Sgtb 			if (KTFILENAME(keytab))
784505d05c7Sgtb 			    krb5_xfree(KTFILENAME(keytab));
785505d05c7Sgtb 			krb5_xfree(keytab->data);
786505d05c7Sgtb 		    }
787505d05c7Sgtb 		    krb5_xfree(keytab);
788505d05c7Sgtb 		}
789505d05c7Sgtb 		else {
790505d05c7Sgtb 		    *buffer = bp;
791505d05c7Sgtb 		    *lenremain = remain;
792505d05c7Sgtb 		    *argp = (krb5_pointer) keytab;
793505d05c7Sgtb 		}
794505d05c7Sgtb 	    }
795505d05c7Sgtb 	    free(ktname);
796505d05c7Sgtb 	}
797505d05c7Sgtb     }
798505d05c7Sgtb     return(kret);
799505d05c7Sgtb }
800505d05c7Sgtb 
801505d05c7Sgtb /*
802*55fea89dSDan Cross  * This is an implementation specific resolver.  It returns a keytab id
803505d05c7Sgtb  * initialized with file keytab routines.
804505d05c7Sgtb  */
805505d05c7Sgtb 
806505d05c7Sgtb krb5_error_code KRB5_CALLCONV
krb5_ktfile_wresolve(krb5_context context,const char * name,krb5_keytab * id)807505d05c7Sgtb krb5_ktfile_wresolve(krb5_context context, const char *name, krb5_keytab *id)
808505d05c7Sgtb {
809505d05c7Sgtb     krb5_ktfile_data *data;
810505d05c7Sgtb     krb5_error_code err;
811505d05c7Sgtb 
812505d05c7Sgtb     if ((*id = (krb5_keytab) malloc(sizeof(**id))) == NULL)
813505d05c7Sgtb 	return(ENOMEM);
814*55fea89dSDan Cross 
815505d05c7Sgtb     (*id)->ops = &krb5_ktf_writable_ops;
816505d05c7Sgtb     if ((data = (krb5_ktfile_data *)malloc(sizeof(krb5_ktfile_data))) == NULL) {
817505d05c7Sgtb 	krb5_xfree(*id);
818505d05c7Sgtb 	return(ENOMEM);
819505d05c7Sgtb     }
820505d05c7Sgtb 
821505d05c7Sgtb     err = k5_mutex_init(&data->lock);
822505d05c7Sgtb     if (err) {
823159d09a2SMark Phalan 	krb5_xfree(data);
824505d05c7Sgtb 	krb5_xfree(*id);
825505d05c7Sgtb 	return err;
826505d05c7Sgtb     }
827505d05c7Sgtb 
828505d05c7Sgtb     if ((data->name = (char *)calloc(strlen(name) + 1, sizeof(char))) == NULL) {
829505d05c7Sgtb 	k5_mutex_destroy(&data->lock);
830505d05c7Sgtb 	krb5_xfree(data);
831505d05c7Sgtb 	krb5_xfree(*id);
832505d05c7Sgtb 	return(ENOMEM);
833505d05c7Sgtb     }
834505d05c7Sgtb 
835505d05c7Sgtb     (void) strcpy(data->name, name);
836505d05c7Sgtb     data->openf = 0;
837505d05c7Sgtb     data->version = 0;
838505d05c7Sgtb 
839505d05c7Sgtb     (*id)->data = (krb5_pointer)data;
840505d05c7Sgtb     (*id)->magic = KV5M_KEYTAB;
841505d05c7Sgtb     return(0);
842505d05c7Sgtb }
843505d05c7Sgtb 
844505d05c7Sgtb 
845505d05c7Sgtb /*
846505d05c7Sgtb  * krb5_ktfile_add()
847505d05c7Sgtb  */
848505d05c7Sgtb 
849*55fea89dSDan Cross krb5_error_code KRB5_CALLCONV
krb5_ktfile_add(krb5_context context,krb5_keytab id,krb5_keytab_entry * entry)850505d05c7Sgtb krb5_ktfile_add(krb5_context context, krb5_keytab id, krb5_keytab_entry *entry)
851505d05c7Sgtb {
852505d05c7Sgtb     krb5_error_code retval;
853505d05c7Sgtb 
854505d05c7Sgtb     retval = KTLOCK(id);
855505d05c7Sgtb     if (retval)
856505d05c7Sgtb 	return retval;
857505d05c7Sgtb     if ((retval = krb5_ktfileint_openw(context, id))) {
858505d05c7Sgtb 	KTUNLOCK(id);
859505d05c7Sgtb 	return retval;
860505d05c7Sgtb     }
861505d05c7Sgtb     if (fseek(KTFILEP(id), 0, 2) == -1) {
862505d05c7Sgtb 	KTUNLOCK(id);
863505d05c7Sgtb 	return KRB5_KT_END;
864505d05c7Sgtb     }
865505d05c7Sgtb     retval = krb5_ktfileint_write_entry(context, id, entry);
866505d05c7Sgtb     krb5_ktfileint_close(context, id);
867505d05c7Sgtb     KTUNLOCK(id);
868505d05c7Sgtb     return retval;
869505d05c7Sgtb }
870505d05c7Sgtb 
871505d05c7Sgtb /*
872505d05c7Sgtb  * krb5_ktfile_remove()
873505d05c7Sgtb  */
874505d05c7Sgtb 
875*55fea89dSDan Cross krb5_error_code KRB5_CALLCONV
krb5_ktfile_remove(krb5_context context,krb5_keytab id,krb5_keytab_entry * entry)876505d05c7Sgtb krb5_ktfile_remove(krb5_context context, krb5_keytab id, krb5_keytab_entry *entry)
877505d05c7Sgtb {
878505d05c7Sgtb     krb5_keytab_entry   cur_entry;
879505d05c7Sgtb     krb5_error_code     kerror;
880505d05c7Sgtb     krb5_int32          delete_point;
881505d05c7Sgtb 
882505d05c7Sgtb     kerror = KTLOCK(id);
883505d05c7Sgtb     if (kerror)
884505d05c7Sgtb 	return kerror;
885505d05c7Sgtb 
886505d05c7Sgtb     if ((kerror = krb5_ktfileint_openw(context, id))) {
887505d05c7Sgtb 	KTUNLOCK(id);
888505d05c7Sgtb 	return kerror;
889505d05c7Sgtb     }
890505d05c7Sgtb 
891*55fea89dSDan Cross     /*
892*55fea89dSDan Cross      * For efficiency and simplicity, we'll use a while true that
893505d05c7Sgtb      * is exited with a break statement.
894505d05c7Sgtb      */
895505d05c7Sgtb     while (TRUE) {
896505d05c7Sgtb 	if ((kerror = krb5_ktfileint_internal_read_entry(context, id,
897505d05c7Sgtb 							 &cur_entry,
898505d05c7Sgtb 							 &delete_point)))
899505d05c7Sgtb   	    break;
900505d05c7Sgtb 
901505d05c7Sgtb 	if ((entry->vno == cur_entry.vno) &&
902505d05c7Sgtb             (entry->key.enctype == cur_entry.key.enctype) &&
903505d05c7Sgtb 	    krb5_principal_compare(context, entry->principal, cur_entry.principal)) {
904505d05c7Sgtb 	    /* found a match */
905505d05c7Sgtb             krb5_kt_free_entry(context, &cur_entry);
906505d05c7Sgtb 	    break;
907505d05c7Sgtb 	}
908505d05c7Sgtb 	krb5_kt_free_entry(context, &cur_entry);
909505d05c7Sgtb     }
910505d05c7Sgtb 
911505d05c7Sgtb     if (kerror == KRB5_KT_END)
912505d05c7Sgtb 	kerror = KRB5_KT_NOTFOUND;
913505d05c7Sgtb 
914505d05c7Sgtb     if (kerror) {
915505d05c7Sgtb 	(void) krb5_ktfileint_close(context, id);
916505d05c7Sgtb 	KTUNLOCK(id);
917505d05c7Sgtb 	return kerror;
918505d05c7Sgtb     }
919505d05c7Sgtb 
920505d05c7Sgtb     kerror = krb5_ktfileint_delete_entry(context, id, delete_point);
921505d05c7Sgtb 
922505d05c7Sgtb     if (kerror) {
923505d05c7Sgtb 	(void) krb5_ktfileint_close(context, id);
924505d05c7Sgtb     } else {
925505d05c7Sgtb         kerror = krb5_ktfileint_close(context, id);
926505d05c7Sgtb     }
927505d05c7Sgtb     KTUNLOCK(id);
928505d05c7Sgtb     return kerror;
929505d05c7Sgtb }
930505d05c7Sgtb 
931505d05c7Sgtb /*
932505d05c7Sgtb  * krb5_ktf_ops
933505d05c7Sgtb  */
934505d05c7Sgtb 
935505d05c7Sgtb const struct _krb5_kt_ops krb5_ktf_ops = {
936505d05c7Sgtb     0,
937505d05c7Sgtb     "FILE", 	/* Prefix -- this string should not appear anywhere else! */
938505d05c7Sgtb     krb5_ktfile_resolve,
939*55fea89dSDan Cross     krb5_ktfile_get_name,
940505d05c7Sgtb     krb5_ktfile_close,
941505d05c7Sgtb     krb5_ktfile_get_entry,
942505d05c7Sgtb     krb5_ktfile_start_seq_get,
943505d05c7Sgtb     krb5_ktfile_get_next,
944505d05c7Sgtb     krb5_ktfile_end_get,
945505d05c7Sgtb     0,
946505d05c7Sgtb     0,
947505d05c7Sgtb     &krb5_ktfile_ser_entry
948505d05c7Sgtb };
949505d05c7Sgtb 
950505d05c7Sgtb /*
951505d05c7Sgtb  * krb5_ktf_writable_ops
952505d05c7Sgtb  */
953505d05c7Sgtb 
954505d05c7Sgtb const struct _krb5_kt_ops krb5_ktf_writable_ops = {
955505d05c7Sgtb     0,
956505d05c7Sgtb     "WRFILE", 	/* Prefix -- this string should not appear anywhere else! */
957505d05c7Sgtb     krb5_ktfile_wresolve,
958*55fea89dSDan Cross     krb5_ktfile_get_name,
959505d05c7Sgtb     krb5_ktfile_close,
960505d05c7Sgtb     krb5_ktfile_get_entry,
961505d05c7Sgtb     krb5_ktfile_start_seq_get,
962505d05c7Sgtb     krb5_ktfile_get_next,
963505d05c7Sgtb     krb5_ktfile_end_get,
964505d05c7Sgtb     krb5_ktfile_add,
965505d05c7Sgtb     krb5_ktfile_remove,
966505d05c7Sgtb     &krb5_ktfile_ser_entry
967505d05c7Sgtb };
968505d05c7Sgtb 
969505d05c7Sgtb /*
970505d05c7Sgtb  * krb5_kt_dfl_ops
971505d05c7Sgtb  */
972505d05c7Sgtb 
973505d05c7Sgtb const krb5_kt_ops krb5_kt_dfl_ops = {
974505d05c7Sgtb     0,
975505d05c7Sgtb     "FILE", 	/* Prefix -- this string should not appear anywhere else! */
976505d05c7Sgtb     krb5_ktfile_resolve,
977*55fea89dSDan Cross     krb5_ktfile_get_name,
978505d05c7Sgtb     krb5_ktfile_close,
979505d05c7Sgtb     krb5_ktfile_get_entry,
980505d05c7Sgtb     krb5_ktfile_start_seq_get,
981505d05c7Sgtb     krb5_ktfile_get_next,
982505d05c7Sgtb     krb5_ktfile_end_get,
983505d05c7Sgtb     0,
984505d05c7Sgtb     0,
985505d05c7Sgtb     &krb5_ktfile_ser_entry
986505d05c7Sgtb };
987505d05c7Sgtb 
988505d05c7Sgtb /*
989505d05c7Sgtb  * lib/krb5/keytab/file/ktf_util.c
990505d05c7Sgtb  *
991505d05c7Sgtb  * Copyright (c) Hewlett-Packard Company 1991
992505d05c7Sgtb  * Released to the Massachusetts Institute of Technology for inclusion
993505d05c7Sgtb  * in the Kerberos source code distribution.
994505d05c7Sgtb  *
995505d05c7Sgtb  * Copyright 1990,1991 by the Massachusetts Institute of Technology.
996505d05c7Sgtb  * All Rights Reserved.
997505d05c7Sgtb  *
998505d05c7Sgtb  * Export of this software from the United States of America may
999505d05c7Sgtb  *   require a specific license from the United States Government.
1000505d05c7Sgtb  *   It is the responsibility of any person or organization contemplating
1001505d05c7Sgtb  *   export to obtain such a license before exporting.
1002*55fea89dSDan Cross  *
1003505d05c7Sgtb  * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
1004505d05c7Sgtb  * distribute this software and its documentation for any purpose and
1005505d05c7Sgtb  * without fee is hereby granted, provided that the above copyright
1006505d05c7Sgtb  * notice appear in all copies and that both that copyright notice and
1007505d05c7Sgtb  * this permission notice appear in supporting documentation, and that
1008505d05c7Sgtb  * the name of M.I.T. not be used in advertising or publicity pertaining
1009505d05c7Sgtb  * to distribution of the software without specific, written prior
1010505d05c7Sgtb  * permission.  Furthermore if you modify this software you must label
1011505d05c7Sgtb  * your software as modified software and not distribute it in such a
1012505d05c7Sgtb  * fashion that it might be confused with the original M.I.T. software.
1013505d05c7Sgtb  * M.I.T. makes no representations about the suitability of
1014505d05c7Sgtb  * this software for any purpose.  It is provided "as is" without express
1015505d05c7Sgtb  * or implied warranty.
1016505d05c7Sgtb  *
1017*55fea89dSDan Cross  *
1018*55fea89dSDan Cross  * This function contains utilities for the file based implementation of
1019505d05c7Sgtb  * the keytab.  There are no public functions in this file.
1020505d05c7Sgtb  *
1021505d05c7Sgtb  * This file is the only one that has knowledge of the format of a
1022505d05c7Sgtb  * keytab file.
1023505d05c7Sgtb  *
1024505d05c7Sgtb  * The format is as follows:
1025*55fea89dSDan Cross  *
1026505d05c7Sgtb  * <file format vno>
1027505d05c7Sgtb  * <record length>
1028505d05c7Sgtb  * principal timestamp vno key
1029505d05c7Sgtb  * <record length>
1030505d05c7Sgtb  * principal timestamp vno key
1031505d05c7Sgtb  * ....
1032505d05c7Sgtb  *
1033505d05c7Sgtb  * A length field (sizeof(krb5_int32)) exists between entries.  When this
1034505d05c7Sgtb  * length is positive it indicates an active entry, when negative a hole.
1035*55fea89dSDan Cross  * The length indicates the size of the block in the file (this may be
1036505d05c7Sgtb  * larger than the size of the next record, since we are using a first
1037505d05c7Sgtb  * fit algorithm for re-using holes and the first fit may be larger than
1038505d05c7Sgtb  * the entry we are writing).  Another (compatible) implementation could
1039*55fea89dSDan Cross  * break up holes when allocating them to smaller entries to minimize
1040505d05c7Sgtb  * wasted space.  (Such an implementation should also coalesce adjacent
1041505d05c7Sgtb  * holes to reduce fragmentation).  This implementation does neither.
1042505d05c7Sgtb  *
1043*55fea89dSDan Cross  * There are no separators between fields of an entry.
1044505d05c7Sgtb  * A principal is a length-encoded array of length-encoded strings.  The
1045*55fea89dSDan Cross  * length is a krb5_int16 in each case.  The specific format, then, is
1046*55fea89dSDan Cross  * multiple entries concatinated with no separators.  An entry has this
1047505d05c7Sgtb  * exact format:
1048505d05c7Sgtb  *
1049*55fea89dSDan Cross  * sizeof(krb5_int16) bytes for number of components in the principal;
1050505d05c7Sgtb  * then, each component listed in ordser.
1051505d05c7Sgtb  * For each component, sizeof(krb5_int16) bytes for the number of bytes
1052505d05c7Sgtb  * in the component, followed by the component.
1053505d05c7Sgtb  * sizeof(krb5_int32) for the principal type (for KEYTAB V2 and higher)
1054505d05c7Sgtb  * sizeof(krb5_int32) bytes for the timestamp
1055505d05c7Sgtb  * sizeof(krb5_octet) bytes for the key version number
1056505d05c7Sgtb  * sizeof(krb5_int16) bytes for the enctype
1057505d05c7Sgtb  * sizeof(krb5_int32) bytes for the key length, followed by the key
1058505d05c7Sgtb  */
1059505d05c7Sgtb 
1060505d05c7Sgtb #ifndef SEEK_SET
1061505d05c7Sgtb #define SEEK_SET 0
1062505d05c7Sgtb #define SEEK_CUR 1
1063505d05c7Sgtb #endif
1064505d05c7Sgtb 
1065505d05c7Sgtb typedef krb5_int16  krb5_kt_vno;
1066505d05c7Sgtb 
1067505d05c7Sgtb #define krb5_kt_default_vno ((krb5_kt_vno)KRB5_KT_DEFAULT_VNO)
1068505d05c7Sgtb 
1069505d05c7Sgtb #define xfwrite(a, b, c, d) fwrite((char *)a, b, (unsigned) c, d)
1070505d05c7Sgtb #define xfread(a, b, c, d) fread((char *)a, b, (unsigned) c, d)
1071505d05c7Sgtb 
1072505d05c7Sgtb #ifdef ANSI_STDIO
1073159d09a2SMark Phalan /* Solaris Kerberos */
1074004388ebScasper static char *const fopen_mode_rbplus= "rb+F";
1075004388ebScasper static char *const fopen_mode_rb = "rbF";
1076505d05c7Sgtb #else
1077159d09a2SMark Phalan /* Solaris Kerberos */
1078004388ebScasper static char *const fopen_mode_rbplus= "r+F";
1079004388ebScasper static char *const fopen_mode_rb = "rF";
1080505d05c7Sgtb #endif
1081505d05c7Sgtb 
1082505d05c7Sgtb static krb5_error_code
krb5_ktfileint_open(krb5_context context,krb5_keytab id,int mode)1083505d05c7Sgtb krb5_ktfileint_open(krb5_context context, krb5_keytab id, int mode)
1084505d05c7Sgtb {
1085505d05c7Sgtb     krb5_error_code kerror;
1086505d05c7Sgtb     krb5_kt_vno kt_vno;
1087505d05c7Sgtb     int writevno = 0;
1088505d05c7Sgtb 
1089505d05c7Sgtb     KTCHECKLOCK(id);
1090505d05c7Sgtb     errno = 0;
1091505d05c7Sgtb     KTFILEP(id) = fopen(KTFILENAME(id),
1092505d05c7Sgtb 			(mode == KRB5_LOCKMODE_EXCLUSIVE) ?
1093505d05c7Sgtb 			  fopen_mode_rbplus : fopen_mode_rb);
1094505d05c7Sgtb     if (!KTFILEP(id)) {
1095505d05c7Sgtb 	if ((mode == KRB5_LOCKMODE_EXCLUSIVE) && (errno == ENOENT)) {
1096505d05c7Sgtb 	    /* try making it first time around */
1097505d05c7Sgtb             krb5_create_secure_file(context, KTFILENAME(id));
1098505d05c7Sgtb 	    errno = 0;
1099505d05c7Sgtb 	    KTFILEP(id) = fopen(KTFILENAME(id), fopen_mode_rbplus);
1100505d05c7Sgtb 	    if (!KTFILEP(id))
11015e01956fSGlenn Barry 		goto report_errno;
1102505d05c7Sgtb 	    writevno = 1;
11035e01956fSGlenn Barry 	} else {
11045e01956fSGlenn Barry         report_errno:
11055e01956fSGlenn Barry             switch (errno) {
11065e01956fSGlenn Barry             case 0:
11075e01956fSGlenn Barry                 /* XXX */
11085e01956fSGlenn Barry                 return EMFILE;
11095e01956fSGlenn Barry             case ENOENT:
11105e01956fSGlenn Barry                 krb5_set_error_message(context, ENOENT,
11115e01956fSGlenn Barry 				       /* Solaris Kerberos - added dgettext */
11125e01956fSGlenn Barry                                        dgettext(TEXT_DOMAIN,
11135e01956fSGlenn Barry 					   "Key table file '%s' not found"),
11145e01956fSGlenn Barry                                        KTFILENAME(id));
11155e01956fSGlenn Barry                 return ENOENT;
11165e01956fSGlenn Barry             default:
11175e01956fSGlenn Barry                 return errno;
11185e01956fSGlenn Barry             }
11195e01956fSGlenn Barry         }
1120505d05c7Sgtb     }
1121505d05c7Sgtb     if ((kerror = krb5_lock_file(context, fileno(KTFILEP(id)), mode))) {
1122505d05c7Sgtb 	(void) fclose(KTFILEP(id));
1123505d05c7Sgtb 	KTFILEP(id) = 0;
1124505d05c7Sgtb 	return kerror;
1125505d05c7Sgtb     }
1126505d05c7Sgtb     /* assume ANSI or BSD-style stdio */
1127505d05c7Sgtb     setbuf(KTFILEP(id), KTFILEBUFP(id));
1128505d05c7Sgtb 
1129505d05c7Sgtb     /* get the vno and verify it */
1130505d05c7Sgtb     if (writevno) {
1131505d05c7Sgtb 	kt_vno = htons(krb5_kt_default_vno);
1132505d05c7Sgtb 	KTVERSION(id) = krb5_kt_default_vno;
1133505d05c7Sgtb 	if (!xfwrite(&kt_vno, sizeof(kt_vno), 1, KTFILEP(id))) {
1134505d05c7Sgtb 	    kerror = errno;
1135505d05c7Sgtb 	    (void) krb5_unlock_file(context, fileno(KTFILEP(id)));
1136505d05c7Sgtb 	    (void) fclose(KTFILEP(id));
1137505d05c7Sgtb 	    return kerror;
1138505d05c7Sgtb 	}
1139505d05c7Sgtb     } else {
1140505d05c7Sgtb 	/* gotta verify it instead... */
1141505d05c7Sgtb 	if (!xfread(&kt_vno, sizeof(kt_vno), 1, KTFILEP(id))) {
1142159d09a2SMark Phalan 	    if (feof(KTFILEP(id)))
1143159d09a2SMark Phalan 		kerror = KRB5_KEYTAB_BADVNO;
1144159d09a2SMark Phalan 	    else
1145159d09a2SMark Phalan 		kerror = errno;
1146505d05c7Sgtb 	    (void) krb5_unlock_file(context, fileno(KTFILEP(id)));
1147505d05c7Sgtb 	    (void) fclose(KTFILEP(id));
1148505d05c7Sgtb 	    return kerror;
1149505d05c7Sgtb 	}
1150505d05c7Sgtb 	kt_vno = KTVERSION(id) = ntohs(kt_vno);
1151505d05c7Sgtb 	if ((kt_vno != KRB5_KT_VNO) &&
1152505d05c7Sgtb 	    (kt_vno != KRB5_KT_VNO_1)) {
1153505d05c7Sgtb 	    (void) krb5_unlock_file(context, fileno(KTFILEP(id)));
1154505d05c7Sgtb 	    (void) fclose(KTFILEP(id));
1155505d05c7Sgtb 	    return KRB5_KEYTAB_BADVNO;
1156505d05c7Sgtb 	}
1157505d05c7Sgtb     }
1158505d05c7Sgtb     return 0;
1159505d05c7Sgtb }
1160505d05c7Sgtb 
1161505d05c7Sgtb krb5_error_code
krb5_ktfileint_openr(krb5_context context,krb5_keytab id)1162505d05c7Sgtb krb5_ktfileint_openr(krb5_context context, krb5_keytab id)
1163505d05c7Sgtb {
1164505d05c7Sgtb     return krb5_ktfileint_open(context, id, KRB5_LOCKMODE_SHARED);
1165505d05c7Sgtb }
1166505d05c7Sgtb 
1167505d05c7Sgtb krb5_error_code
krb5_ktfileint_openw(krb5_context context,krb5_keytab id)1168505d05c7Sgtb krb5_ktfileint_openw(krb5_context context, krb5_keytab id)
1169505d05c7Sgtb {
1170505d05c7Sgtb     return krb5_ktfileint_open(context, id, KRB5_LOCKMODE_EXCLUSIVE);
1171505d05c7Sgtb }
1172505d05c7Sgtb 
1173505d05c7Sgtb krb5_error_code
krb5_ktfileint_close(krb5_context context,krb5_keytab id)1174505d05c7Sgtb krb5_ktfileint_close(krb5_context context, krb5_keytab id)
1175505d05c7Sgtb {
1176505d05c7Sgtb     krb5_error_code kerror;
1177505d05c7Sgtb 
1178505d05c7Sgtb     KTCHECKLOCK(id);
1179505d05c7Sgtb     if (!KTFILEP(id))
1180505d05c7Sgtb 	return 0;
1181505d05c7Sgtb     kerror = krb5_unlock_file(context, fileno(KTFILEP(id)));
1182505d05c7Sgtb     (void) fclose(KTFILEP(id));
1183505d05c7Sgtb     KTFILEP(id) = 0;
1184505d05c7Sgtb     return kerror;
1185505d05c7Sgtb }
1186505d05c7Sgtb 
1187505d05c7Sgtb krb5_error_code
krb5_ktfileint_delete_entry(krb5_context context,krb5_keytab id,krb5_int32 delete_point)1188505d05c7Sgtb krb5_ktfileint_delete_entry(krb5_context context, krb5_keytab id, krb5_int32 delete_point)
1189505d05c7Sgtb {
1190505d05c7Sgtb     krb5_int32  size;
1191505d05c7Sgtb     krb5_int32  len;
1192505d05c7Sgtb     char        iobuf[BUFSIZ];
1193505d05c7Sgtb 
1194505d05c7Sgtb     KTCHECKLOCK(id);
1195505d05c7Sgtb     if (fseek(KTFILEP(id), delete_point, SEEK_SET)) {
1196505d05c7Sgtb         return errno;
1197505d05c7Sgtb     }
1198505d05c7Sgtb     if (!xfread(&size, sizeof(size), 1, KTFILEP(id))) {
1199505d05c7Sgtb         return KRB5_KT_END;
1200505d05c7Sgtb     }
1201505d05c7Sgtb     if (KTVERSION(id) != KRB5_KT_VNO_1)
1202505d05c7Sgtb 	size = ntohl(size);
1203505d05c7Sgtb 
1204505d05c7Sgtb     if (size > 0) {
1205505d05c7Sgtb         krb5_int32 minus_size = -size;
1206505d05c7Sgtb 	if (KTVERSION(id) != KRB5_KT_VNO_1)
1207505d05c7Sgtb 	    minus_size = htonl(minus_size);
1208505d05c7Sgtb 
1209505d05c7Sgtb         if (fseek(KTFILEP(id), delete_point, SEEK_SET)) {
1210505d05c7Sgtb             return errno;
1211505d05c7Sgtb         }
1212505d05c7Sgtb 
1213505d05c7Sgtb         if (!xfwrite(&minus_size, sizeof(minus_size), 1, KTFILEP(id))) {
1214505d05c7Sgtb             return KRB5_KT_IOERR;
1215505d05c7Sgtb         }
1216505d05c7Sgtb 
1217505d05c7Sgtb         if (size < BUFSIZ) {
1218505d05c7Sgtb             len = size;
1219505d05c7Sgtb         } else {
1220505d05c7Sgtb             len = BUFSIZ;
1221505d05c7Sgtb         }
1222505d05c7Sgtb 
1223505d05c7Sgtb         memset(iobuf, 0, (size_t) len);
1224505d05c7Sgtb         while (size > 0) {
1225505d05c7Sgtb             xfwrite(iobuf, 1, (size_t) len, KTFILEP(id));
1226505d05c7Sgtb             size -= len;
1227505d05c7Sgtb             if (size < len) {
1228505d05c7Sgtb                 len = size;
1229505d05c7Sgtb             }
1230505d05c7Sgtb         }
1231505d05c7Sgtb 
1232505d05c7Sgtb         return krb5_sync_disk_file(context, KTFILEP(id));
1233505d05c7Sgtb     }
1234505d05c7Sgtb 
1235505d05c7Sgtb     return 0;
1236505d05c7Sgtb }
1237505d05c7Sgtb 
1238505d05c7Sgtb krb5_error_code
krb5_ktfileint_internal_read_entry(krb5_context context,krb5_keytab id,krb5_keytab_entry * ret_entry,krb5_int32 * delete_point)1239505d05c7Sgtb krb5_ktfileint_internal_read_entry(krb5_context context, krb5_keytab id, krb5_keytab_entry *ret_entry, krb5_int32 *delete_point)
1240505d05c7Sgtb {
1241505d05c7Sgtb     krb5_octet vno;
1242505d05c7Sgtb     krb5_int16 count;
1243505d05c7Sgtb     unsigned int u_count, u_princ_size;
1244505d05c7Sgtb     krb5_int16 enctype;
1245505d05c7Sgtb     krb5_int16 princ_size;
1246505d05c7Sgtb     register int i;
1247505d05c7Sgtb     krb5_int32 size;
1248505d05c7Sgtb     krb5_int32 start_pos;
1249505d05c7Sgtb     krb5_error_code error;
1250505d05c7Sgtb     char	*tmpdata;
1251505d05c7Sgtb     krb5_data	*princ;
1252505d05c7Sgtb 
1253505d05c7Sgtb     KTCHECKLOCK(id);
1254505d05c7Sgtb     memset(ret_entry, 0, sizeof(krb5_keytab_entry));
1255505d05c7Sgtb     ret_entry->magic = KV5M_KEYTAB_ENTRY;
1256505d05c7Sgtb 
1257505d05c7Sgtb     /* fseek to synchronise buffered I/O on the key table. */
1258505d05c7Sgtb 
1259505d05c7Sgtb     if (fseek(KTFILEP(id), 0L, SEEK_CUR) < 0)
1260505d05c7Sgtb     {
1261505d05c7Sgtb         return errno;
1262505d05c7Sgtb     }
1263505d05c7Sgtb 
1264505d05c7Sgtb     do {
1265505d05c7Sgtb         *delete_point = ftell(KTFILEP(id));
1266505d05c7Sgtb         if (!xfread(&size, sizeof(size), 1, KTFILEP(id))) {
1267505d05c7Sgtb             return KRB5_KT_END;
1268505d05c7Sgtb         }
1269505d05c7Sgtb 	if (KTVERSION(id) != KRB5_KT_VNO_1)
1270505d05c7Sgtb 		size = ntohl(size);
1271505d05c7Sgtb 
1272505d05c7Sgtb         if (size < 0) {
1273505d05c7Sgtb             if (fseek(KTFILEP(id), -size, SEEK_CUR)) {
1274505d05c7Sgtb                 return errno;
1275505d05c7Sgtb             }
1276505d05c7Sgtb         }
1277505d05c7Sgtb     } while (size < 0);
1278505d05c7Sgtb 
1279505d05c7Sgtb     if (size == 0) {
1280505d05c7Sgtb         return KRB5_KT_END;
1281505d05c7Sgtb     }
1282505d05c7Sgtb 
1283505d05c7Sgtb     start_pos = ftell(KTFILEP(id));
1284505d05c7Sgtb 
1285505d05c7Sgtb     /* deal with guts of parsing... */
1286505d05c7Sgtb 
1287505d05c7Sgtb     /* first, int16 with #princ components */
1288505d05c7Sgtb     if (!xfread(&count, sizeof(count), 1, KTFILEP(id)))
1289505d05c7Sgtb 	return KRB5_KT_END;
1290505d05c7Sgtb     if (KTVERSION(id) == KRB5_KT_VNO_1) {
1291505d05c7Sgtb 	    count -= 1;		/* V1 includes the realm in the count */
1292505d05c7Sgtb     } else {
1293505d05c7Sgtb 	    count = ntohs(count);
1294505d05c7Sgtb     }
1295505d05c7Sgtb     if (!count || (count < 0))
1296505d05c7Sgtb 	return KRB5_KT_END;
1297505d05c7Sgtb     ret_entry->principal = (krb5_principal)malloc(sizeof(krb5_principal_data));
1298505d05c7Sgtb     if (!ret_entry->principal)
1299505d05c7Sgtb         return ENOMEM;
1300*55fea89dSDan Cross 
1301505d05c7Sgtb     u_count = count;
1302505d05c7Sgtb     ret_entry->principal->magic = KV5M_PRINCIPAL;
1303505d05c7Sgtb     ret_entry->principal->length = u_count;
1304*55fea89dSDan Cross     ret_entry->principal->data = (krb5_data *)
1305505d05c7Sgtb                                  calloc(u_count, sizeof(krb5_data));
1306505d05c7Sgtb     if (!ret_entry->principal->data) {
1307505d05c7Sgtb 	free(ret_entry->principal);
1308505d05c7Sgtb 	ret_entry->principal = 0;
1309505d05c7Sgtb 	return ENOMEM;
1310505d05c7Sgtb     }
1311505d05c7Sgtb 
1312505d05c7Sgtb     /* Now, get the realm data */
1313505d05c7Sgtb     if (!xfread(&princ_size, sizeof(princ_size), 1, KTFILEP(id))) {
1314505d05c7Sgtb 	    error = KRB5_KT_END;
1315505d05c7Sgtb 	    goto fail;
1316505d05c7Sgtb     }
1317505d05c7Sgtb     if (KTVERSION(id) != KRB5_KT_VNO_1)
1318505d05c7Sgtb 	    princ_size = ntohs(princ_size);
1319505d05c7Sgtb     if (!princ_size || (princ_size < 0)) {
1320505d05c7Sgtb 	    error = KRB5_KT_END;
1321505d05c7Sgtb 	    goto fail;
1322505d05c7Sgtb     }
1323505d05c7Sgtb     u_princ_size = princ_size;
1324505d05c7Sgtb 
1325505d05c7Sgtb     krb5_princ_set_realm_length(context, ret_entry->principal, u_princ_size);
1326505d05c7Sgtb     tmpdata = malloc(u_princ_size+1);
1327505d05c7Sgtb     if (!tmpdata) {
1328505d05c7Sgtb 	    error = ENOMEM;
1329505d05c7Sgtb 	    goto fail;
1330505d05c7Sgtb     }
1331505d05c7Sgtb     if (fread(tmpdata, 1, u_princ_size, KTFILEP(id)) != (size_t) princ_size) {
1332505d05c7Sgtb 	    free(tmpdata);
1333505d05c7Sgtb 	    error = KRB5_KT_END;
1334505d05c7Sgtb 	    goto fail;
1335505d05c7Sgtb     }
1336505d05c7Sgtb     tmpdata[princ_size] = 0;	/* Some things might be expecting null */
1337505d05c7Sgtb 				/* termination...  ``Be conservative in */
1338505d05c7Sgtb 				/* what you send out'' */
1339505d05c7Sgtb     krb5_princ_set_realm_data(context, ret_entry->principal, tmpdata);
1340*55fea89dSDan Cross 
1341505d05c7Sgtb     for (i = 0; i < count; i++) {
1342505d05c7Sgtb 	princ = krb5_princ_component(context, ret_entry->principal, i);
1343505d05c7Sgtb 	if (!xfread(&princ_size, sizeof(princ_size), 1, KTFILEP(id))) {
1344505d05c7Sgtb 	    error = KRB5_KT_END;
1345505d05c7Sgtb 	    goto fail;
1346505d05c7Sgtb         }
1347505d05c7Sgtb 	if (KTVERSION(id) != KRB5_KT_VNO_1)
1348505d05c7Sgtb 	    princ_size = ntohs(princ_size);
1349505d05c7Sgtb 	if (!princ_size || (princ_size < 0)) {
1350505d05c7Sgtb 	    error = KRB5_KT_END;
1351505d05c7Sgtb 	    goto fail;
1352505d05c7Sgtb         }
1353505d05c7Sgtb 
1354*55fea89dSDan Cross 	u_princ_size = princ_size;
1355505d05c7Sgtb 	princ->length = u_princ_size;
1356505d05c7Sgtb 	princ->data = malloc(u_princ_size+1);
1357505d05c7Sgtb 	if (!princ->data) {
1358505d05c7Sgtb 	    error = ENOMEM;
1359505d05c7Sgtb 	    goto fail;
1360505d05c7Sgtb         }
1361505d05c7Sgtb 	if (!xfread(princ->data, sizeof(char), u_princ_size, KTFILEP(id))) {
1362505d05c7Sgtb 	    error = KRB5_KT_END;
1363505d05c7Sgtb 	    goto fail;
1364505d05c7Sgtb         }
1365505d05c7Sgtb 	princ->data[princ_size] = 0; /* Null terminate */
1366505d05c7Sgtb     }
1367505d05c7Sgtb 
1368505d05c7Sgtb     /* read in the principal type, if we can get it */
1369505d05c7Sgtb     if (KTVERSION(id) != KRB5_KT_VNO_1) {
1370505d05c7Sgtb 	    if (!xfread(&ret_entry->principal->type,
1371505d05c7Sgtb 			sizeof(ret_entry->principal->type), 1, KTFILEP(id))) {
1372505d05c7Sgtb 		    error = KRB5_KT_END;
1373505d05c7Sgtb 		    goto fail;
1374505d05c7Sgtb 	    }
1375505d05c7Sgtb 	    ret_entry->principal->type = ntohl(ret_entry->principal->type);
1376505d05c7Sgtb     }
1377*55fea89dSDan Cross 
1378505d05c7Sgtb     /* read in the timestamp */
1379505d05c7Sgtb     if (!xfread(&ret_entry->timestamp, sizeof(ret_entry->timestamp), 1, KTFILEP(id))) {
1380505d05c7Sgtb 	error = KRB5_KT_END;
1381505d05c7Sgtb 	goto fail;
1382505d05c7Sgtb     }
1383505d05c7Sgtb     if (KTVERSION(id) != KRB5_KT_VNO_1)
1384505d05c7Sgtb 	ret_entry->timestamp = ntohl(ret_entry->timestamp);
1385*55fea89dSDan Cross 
1386505d05c7Sgtb     /* read in the version number */
1387505d05c7Sgtb     if (!xfread(&vno, sizeof(vno), 1, KTFILEP(id))) {
1388505d05c7Sgtb 	error = KRB5_KT_END;
1389505d05c7Sgtb 	goto fail;
1390505d05c7Sgtb     }
1391505d05c7Sgtb     ret_entry->vno = (krb5_kvno)vno;
1392*55fea89dSDan Cross 
1393505d05c7Sgtb     /* key type */
1394505d05c7Sgtb     if (!xfread(&enctype, sizeof(enctype), 1, KTFILEP(id))) {
1395505d05c7Sgtb 	error = KRB5_KT_END;
1396505d05c7Sgtb 	goto fail;
1397505d05c7Sgtb     }
1398505d05c7Sgtb     ret_entry->key.enctype = (krb5_enctype)enctype;
1399505d05c7Sgtb 
1400505d05c7Sgtb     if (KTVERSION(id) != KRB5_KT_VNO_1)
1401505d05c7Sgtb 	ret_entry->key.enctype = ntohs(ret_entry->key.enctype);
1402*55fea89dSDan Cross 
1403505d05c7Sgtb     /* key contents */
1404505d05c7Sgtb     ret_entry->key.magic = KV5M_KEYBLOCK;
1405*55fea89dSDan Cross 
1406505d05c7Sgtb     if (!xfread(&count, sizeof(count), 1, KTFILEP(id))) {
1407505d05c7Sgtb 	error = KRB5_KT_END;
1408505d05c7Sgtb 	goto fail;
1409505d05c7Sgtb     }
1410505d05c7Sgtb     if (KTVERSION(id) != KRB5_KT_VNO_1)
1411505d05c7Sgtb 	count = ntohs(count);
1412505d05c7Sgtb     if (!count || (count < 0)) {
1413505d05c7Sgtb 	error = KRB5_KT_END;
1414505d05c7Sgtb 	goto fail;
1415505d05c7Sgtb     }
1416505d05c7Sgtb 
1417505d05c7Sgtb     u_count = count;
1418505d05c7Sgtb     ret_entry->key.length = u_count;
1419*55fea89dSDan Cross 
1420505d05c7Sgtb     ret_entry->key.contents = (krb5_octet *)malloc(u_count);
1421505d05c7Sgtb     if (!ret_entry->key.contents) {
1422505d05c7Sgtb 	error = ENOMEM;
1423505d05c7Sgtb 	goto fail;
1424*55fea89dSDan Cross     }
1425505d05c7Sgtb     if (!xfread(ret_entry->key.contents, sizeof(krb5_octet), count,
1426505d05c7Sgtb 		KTFILEP(id))) {
1427505d05c7Sgtb 	error = KRB5_KT_END;
1428505d05c7Sgtb 	goto fail;
1429505d05c7Sgtb     }
1430505d05c7Sgtb 
1431505d05c7Sgtb     /*
1432505d05c7Sgtb      * Reposition file pointer to the next inter-record length field.
1433505d05c7Sgtb      */
1434505d05c7Sgtb     fseek(KTFILEP(id), start_pos + size, SEEK_SET);
1435505d05c7Sgtb     return 0;
1436505d05c7Sgtb fail:
1437*55fea89dSDan Cross 
1438505d05c7Sgtb     for (i = 0; i < krb5_princ_size(context, ret_entry->principal); i++) {
1439505d05c7Sgtb 	    princ = krb5_princ_component(context, ret_entry->principal, i);
1440505d05c7Sgtb 	    if (princ->data)
1441505d05c7Sgtb 		    free(princ->data);
1442505d05c7Sgtb     }
1443505d05c7Sgtb     free(ret_entry->principal->data);
1444505d05c7Sgtb     ret_entry->principal->data = 0;
1445505d05c7Sgtb     free(ret_entry->principal);
1446505d05c7Sgtb     ret_entry->principal = 0;
1447505d05c7Sgtb     return error;
1448505d05c7Sgtb }
1449505d05c7Sgtb 
1450505d05c7Sgtb krb5_error_code
krb5_ktfileint_read_entry(krb5_context context,krb5_keytab id,krb5_keytab_entry * entryp)1451505d05c7Sgtb krb5_ktfileint_read_entry(krb5_context context, krb5_keytab id, krb5_keytab_entry *entryp)
1452505d05c7Sgtb {
1453505d05c7Sgtb     krb5_int32 delete_point;
1454505d05c7Sgtb 
1455505d05c7Sgtb     return krb5_ktfileint_internal_read_entry(context, id, entryp, &delete_point);
1456505d05c7Sgtb }
1457505d05c7Sgtb 
1458505d05c7Sgtb krb5_error_code
krb5_ktfileint_write_entry(krb5_context context,krb5_keytab id,krb5_keytab_entry * entry)1459505d05c7Sgtb krb5_ktfileint_write_entry(krb5_context context, krb5_keytab id, krb5_keytab_entry *entry)
1460505d05c7Sgtb {
1461505d05c7Sgtb     krb5_octet vno;
1462505d05c7Sgtb     krb5_data *princ;
1463505d05c7Sgtb     krb5_int16 count, size, enctype;
1464505d05c7Sgtb     krb5_error_code retval = 0;
1465505d05c7Sgtb     krb5_timestamp timestamp;
1466505d05c7Sgtb     krb5_int32	princ_type;
1467505d05c7Sgtb     krb5_int32  size_needed;
1468505d05c7Sgtb     krb5_int32  commit_point;
1469505d05c7Sgtb     int		i;
1470505d05c7Sgtb 
1471505d05c7Sgtb     KTCHECKLOCK(id);
1472505d05c7Sgtb     retval = krb5_ktfileint_size_entry(context, entry, &size_needed);
1473505d05c7Sgtb     if (retval)
1474505d05c7Sgtb         return retval;
1475505d05c7Sgtb     retval = krb5_ktfileint_find_slot(context, id, &size_needed, &commit_point);
1476505d05c7Sgtb     if (retval)
1477505d05c7Sgtb         return retval;
1478505d05c7Sgtb 
1479505d05c7Sgtb     /* fseek to synchronise buffered I/O on the key table. */
1480505d05c7Sgtb     /* XXX Without the weird setbuf crock, can we get rid of this now?  */
1481505d05c7Sgtb     if (fseek(KTFILEP(id), 0L, SEEK_CUR) < 0)
1482505d05c7Sgtb     {
1483505d05c7Sgtb         return errno;
1484505d05c7Sgtb     }
1485505d05c7Sgtb 
1486505d05c7Sgtb     if (KTVERSION(id) == KRB5_KT_VNO_1) {
1487505d05c7Sgtb 	    count = (krb5_int16) krb5_princ_size(context, entry->principal) + 1;
1488505d05c7Sgtb     } else {
1489505d05c7Sgtb 	    count = htons((u_short) krb5_princ_size(context, entry->principal));
1490505d05c7Sgtb     }
1491*55fea89dSDan Cross 
1492505d05c7Sgtb     if (!xfwrite(&count, sizeof(count), 1, KTFILEP(id))) {
1493505d05c7Sgtb     abend:
1494505d05c7Sgtb 	return KRB5_KT_IOERR;
1495505d05c7Sgtb     }
1496505d05c7Sgtb     size = krb5_princ_realm(context, entry->principal)->length;
1497505d05c7Sgtb     if (KTVERSION(id) != KRB5_KT_VNO_1)
1498505d05c7Sgtb 	    size = htons(size);
1499505d05c7Sgtb     if (!xfwrite(&size, sizeof(size), 1, KTFILEP(id))) {
1500505d05c7Sgtb 	    goto abend;
1501505d05c7Sgtb     }
1502505d05c7Sgtb     if (!xfwrite(krb5_princ_realm(context, entry->principal)->data, sizeof(char),
1503505d05c7Sgtb 		 krb5_princ_realm(context, entry->principal)->length, KTFILEP(id))) {
1504505d05c7Sgtb 	    goto abend;
1505505d05c7Sgtb     }
1506505d05c7Sgtb 
1507505d05c7Sgtb     count = (krb5_int16) krb5_princ_size(context, entry->principal);
1508505d05c7Sgtb     for (i = 0; i < count; i++) {
1509505d05c7Sgtb 	princ = krb5_princ_component(context, entry->principal, i);
1510505d05c7Sgtb 	size = princ->length;
1511505d05c7Sgtb 	if (KTVERSION(id) != KRB5_KT_VNO_1)
1512505d05c7Sgtb 		size = htons(size);
1513505d05c7Sgtb 	if (!xfwrite(&size, sizeof(size), 1, KTFILEP(id))) {
1514505d05c7Sgtb 	    goto abend;
1515505d05c7Sgtb 	}
1516505d05c7Sgtb 	if (!xfwrite(princ->data, sizeof(char), princ->length, KTFILEP(id))) {
1517505d05c7Sgtb 	    goto abend;
1518505d05c7Sgtb 	}
1519505d05c7Sgtb     }
1520505d05c7Sgtb 
1521505d05c7Sgtb     /*
1522505d05c7Sgtb      * Write out the principal type
1523505d05c7Sgtb      */
1524505d05c7Sgtb     if (KTVERSION(id) != KRB5_KT_VNO_1) {
1525505d05c7Sgtb 	    princ_type = htonl(krb5_princ_type(context, entry->principal));
1526505d05c7Sgtb 	    if (!xfwrite(&princ_type, sizeof(princ_type), 1, KTFILEP(id))) {
1527505d05c7Sgtb 		    goto abend;
1528505d05c7Sgtb 	    }
1529505d05c7Sgtb     }
1530*55fea89dSDan Cross 
1531505d05c7Sgtb     /*
1532505d05c7Sgtb      * Fill in the time of day the entry was written to the keytab.
1533505d05c7Sgtb      */
1534505d05c7Sgtb     if (krb5_timeofday(context, &entry->timestamp)) {
1535505d05c7Sgtb         entry->timestamp = 0;
1536505d05c7Sgtb     }
1537505d05c7Sgtb     if (KTVERSION(id) == KRB5_KT_VNO_1)
1538505d05c7Sgtb 	    timestamp = entry->timestamp;
1539505d05c7Sgtb     else
1540505d05c7Sgtb 	    timestamp = htonl(entry->timestamp);
1541505d05c7Sgtb     if (!xfwrite(&timestamp, sizeof(timestamp), 1, KTFILEP(id))) {
1542505d05c7Sgtb 	goto abend;
1543505d05c7Sgtb     }
1544*55fea89dSDan Cross 
1545505d05c7Sgtb     /* key version number */
1546505d05c7Sgtb     vno = (krb5_octet)entry->vno;
1547505d05c7Sgtb     if (!xfwrite(&vno, sizeof(vno), 1, KTFILEP(id))) {
1548505d05c7Sgtb 	goto abend;
1549505d05c7Sgtb     }
1550505d05c7Sgtb     /* key type */
1551505d05c7Sgtb     if (KTVERSION(id) == KRB5_KT_VNO_1)
1552505d05c7Sgtb 	    enctype = entry->key.enctype;
1553505d05c7Sgtb     else
1554505d05c7Sgtb 	    enctype = htons(entry->key.enctype);
1555505d05c7Sgtb     if (!xfwrite(&enctype, sizeof(enctype), 1, KTFILEP(id))) {
1556505d05c7Sgtb 	goto abend;
1557505d05c7Sgtb     }
1558505d05c7Sgtb     /* key length */
1559505d05c7Sgtb     if (KTVERSION(id) == KRB5_KT_VNO_1)
1560505d05c7Sgtb 	    size = entry->key.length;
1561505d05c7Sgtb     else
1562505d05c7Sgtb 	    size = htons(entry->key.length);
1563505d05c7Sgtb     if (!xfwrite(&size, sizeof(size), 1, KTFILEP(id))) {
1564505d05c7Sgtb 	goto abend;
1565505d05c7Sgtb     }
1566505d05c7Sgtb     if (!xfwrite(entry->key.contents, sizeof(krb5_octet),
1567505d05c7Sgtb 		 entry->key.length, KTFILEP(id))) {
1568505d05c7Sgtb 	goto abend;
1569*55fea89dSDan Cross     }
1570505d05c7Sgtb 
1571505d05c7Sgtb     if (fflush(KTFILEP(id)))
1572505d05c7Sgtb 	goto abend;
1573505d05c7Sgtb 
1574505d05c7Sgtb     retval = krb5_sync_disk_file(context, KTFILEP(id));
1575505d05c7Sgtb 
1576505d05c7Sgtb     if (retval) {
1577505d05c7Sgtb         return retval;
1578505d05c7Sgtb     }
1579505d05c7Sgtb 
1580505d05c7Sgtb     if (fseek(KTFILEP(id), commit_point, SEEK_SET)) {
1581505d05c7Sgtb         return errno;
1582505d05c7Sgtb     }
1583505d05c7Sgtb     if (KTVERSION(id) != KRB5_KT_VNO_1)
1584505d05c7Sgtb 	    size_needed = htonl(size_needed);
1585505d05c7Sgtb     if (!xfwrite(&size_needed, sizeof(size_needed), 1, KTFILEP(id))) {
1586505d05c7Sgtb         goto abend;
1587505d05c7Sgtb     }
1588505d05c7Sgtb     if (fflush(KTFILEP(id)))
1589505d05c7Sgtb 	goto abend;
1590505d05c7Sgtb     retval = krb5_sync_disk_file(context, KTFILEP(id));
1591505d05c7Sgtb 
1592505d05c7Sgtb     return retval;
1593505d05c7Sgtb }
1594505d05c7Sgtb 
1595505d05c7Sgtb /*
1596505d05c7Sgtb  * Determine the size needed for a file entry for the given
1597505d05c7Sgtb  * keytab entry.
1598505d05c7Sgtb  */
1599505d05c7Sgtb krb5_error_code
krb5_ktfileint_size_entry(krb5_context context,krb5_keytab_entry * entry,krb5_int32 * size_needed)1600505d05c7Sgtb krb5_ktfileint_size_entry(krb5_context context, krb5_keytab_entry *entry, krb5_int32 *size_needed)
1601505d05c7Sgtb {
1602505d05c7Sgtb     krb5_int16 count;
1603505d05c7Sgtb     krb5_int32 total_size, i;
1604505d05c7Sgtb     krb5_error_code retval = 0;
1605505d05c7Sgtb 
1606505d05c7Sgtb     count = (krb5_int16) krb5_princ_size(context, entry->principal);
1607*55fea89dSDan Cross 
1608505d05c7Sgtb     total_size = sizeof(count);
1609505d05c7Sgtb     total_size += krb5_princ_realm(context, entry->principal)->length + (sizeof(krb5_int16));
1610*55fea89dSDan Cross 
1611505d05c7Sgtb     for (i = 0; i < count; i++) {
1612505d05c7Sgtb 	    total_size += krb5_princ_component(context, entry->principal,i)->length
1613505d05c7Sgtb 		    + (sizeof(krb5_int16));
1614505d05c7Sgtb     }
1615505d05c7Sgtb 
1616505d05c7Sgtb     total_size += sizeof(entry->principal->type);
1617505d05c7Sgtb     total_size += sizeof(entry->timestamp);
1618505d05c7Sgtb     total_size += sizeof(krb5_octet);
1619505d05c7Sgtb     total_size += sizeof(krb5_int16);
1620505d05c7Sgtb     total_size += sizeof(krb5_int16) + entry->key.length;
1621505d05c7Sgtb 
1622505d05c7Sgtb     *size_needed = total_size;
1623505d05c7Sgtb     return retval;
1624505d05c7Sgtb }
1625505d05c7Sgtb 
1626505d05c7Sgtb /*
1627505d05c7Sgtb  * Find and reserve a slot in the file for an entry of the needed size.
1628505d05c7Sgtb  * The commit point will be set to the position in the file where the
1629505d05c7Sgtb  * the length (sizeof(krb5_int32) bytes) of this node should be written
1630505d05c7Sgtb  * when commiting the write.  The file position left as a result of this
1631505d05c7Sgtb  * call is the position where the actual data should be written.
1632505d05c7Sgtb  *
1633505d05c7Sgtb  * The size_needed argument may be adjusted if we find a hole that is
1634505d05c7Sgtb  * larger than the size needed.  (Recall that size_needed will be used
1635505d05c7Sgtb  * to commit the write, but that this field must indicate the size of the
1636*55fea89dSDan Cross  * block in the file rather than the size of the actual entry)
1637505d05c7Sgtb  */
1638505d05c7Sgtb krb5_error_code
krb5_ktfileint_find_slot(krb5_context context,krb5_keytab id,krb5_int32 * size_needed,krb5_int32 * commit_point)1639505d05c7Sgtb krb5_ktfileint_find_slot(krb5_context context, krb5_keytab id, krb5_int32 *size_needed, krb5_int32 *commit_point)
1640505d05c7Sgtb {
1641505d05c7Sgtb     krb5_int32      size;
1642505d05c7Sgtb     krb5_int32      remainder;
1643505d05c7Sgtb     krb5_int32      zero_point;
1644505d05c7Sgtb     krb5_kt_vno     kt_vno;
1645505d05c7Sgtb     krb5_boolean    found = FALSE;
1646505d05c7Sgtb     char            iobuf[BUFSIZ];
1647505d05c7Sgtb 
1648505d05c7Sgtb     KTCHECKLOCK(id);
1649505d05c7Sgtb     /*
1650505d05c7Sgtb      * Skip over file version number
1651505d05c7Sgtb      */
1652505d05c7Sgtb     if (fseek(KTFILEP(id), 0, SEEK_SET)) {
1653505d05c7Sgtb         return errno;
1654505d05c7Sgtb     }
1655505d05c7Sgtb     if (!xfread(&kt_vno, sizeof(kt_vno), 1, KTFILEP(id))) {
1656505d05c7Sgtb         return KRB5_KT_IOERR;
1657505d05c7Sgtb     }
1658505d05c7Sgtb 
1659505d05c7Sgtb     while (!found) {
1660505d05c7Sgtb         *commit_point = ftell(KTFILEP(id));
1661505d05c7Sgtb         if (!xfread(&size, sizeof(size), 1, KTFILEP(id))) {
1662505d05c7Sgtb             /*
1663505d05c7Sgtb              * Hit the end of file, reserve this slot.
1664505d05c7Sgtb              */
1665505d05c7Sgtb             size = 0;
1666505d05c7Sgtb 
1667505d05c7Sgtb             /* fseek to synchronise buffered I/O on the key table. */
1668505d05c7Sgtb 	    /* XXX Without the weird setbuf hack, can we nuke this now?  */
1669505d05c7Sgtb             if (fseek(KTFILEP(id), 0L, SEEK_CUR) < 0)
1670505d05c7Sgtb             {
1671505d05c7Sgtb                 return errno;
1672505d05c7Sgtb             }
1673*55fea89dSDan Cross 
1674505d05c7Sgtb #ifdef notdef
1675505d05c7Sgtb 	    /* We don't have to do this because htonl(0) == 0 */
1676505d05c7Sgtb 	    if (KTVERSION(id) != KRB5_KT_VNO_1)
1677505d05c7Sgtb 		    size = htonl(size);
1678505d05c7Sgtb #endif
1679*55fea89dSDan Cross 
1680505d05c7Sgtb             if (!xfwrite(&size, sizeof(size), 1, KTFILEP(id))) {
1681505d05c7Sgtb                 return KRB5_KT_IOERR;
1682505d05c7Sgtb             }
1683505d05c7Sgtb             found = TRUE;
1684505d05c7Sgtb         }
1685505d05c7Sgtb 
1686505d05c7Sgtb 	if (KTVERSION(id) != KRB5_KT_VNO_1)
1687505d05c7Sgtb 		size = ntohl(size);
1688505d05c7Sgtb 
1689505d05c7Sgtb         if (size > 0) {
1690505d05c7Sgtb             if (fseek(KTFILEP(id), size, SEEK_CUR)) {
1691505d05c7Sgtb                 return errno;
1692505d05c7Sgtb             }
1693505d05c7Sgtb         } else if (!found) {
1694505d05c7Sgtb             size = -size;
1695505d05c7Sgtb             if (size >= *size_needed) {
1696505d05c7Sgtb                 *size_needed = size;
1697*55fea89dSDan Cross                 found = TRUE;
1698505d05c7Sgtb             } else if (size > 0) {
1699505d05c7Sgtb                 /*
1700505d05c7Sgtb                  * The current hole is not large enough, so skip it
1701505d05c7Sgtb                  */
1702505d05c7Sgtb                 if (fseek(KTFILEP(id), size, SEEK_CUR)) {
1703505d05c7Sgtb                     return errno;
1704505d05c7Sgtb                 }
1705505d05c7Sgtb             } else {
1706505d05c7Sgtb 
1707505d05c7Sgtb                  /* fseek to synchronise buffered I/O on the key table. */
1708505d05c7Sgtb 
1709505d05c7Sgtb                  if (fseek(KTFILEP(id), 0L, SEEK_CUR) < 0)
1710505d05c7Sgtb                  {
1711505d05c7Sgtb                      return errno;
1712505d05c7Sgtb                  }
1713505d05c7Sgtb 
1714505d05c7Sgtb                 /*
1715505d05c7Sgtb                  * Found the end of the file (marked by a 0 length buffer)
1716505d05c7Sgtb                  * Make sure we zero any trailing data.
1717505d05c7Sgtb                  */
1718505d05c7Sgtb                 zero_point = ftell(KTFILEP(id));
1719505d05c7Sgtb                 while ((size = xfread(iobuf, 1, sizeof(iobuf), KTFILEP(id)))) {
1720505d05c7Sgtb                     if (size != sizeof(iobuf)) {
1721505d05c7Sgtb                         remainder = size % sizeof(krb5_int32);
1722505d05c7Sgtb                         if (remainder) {
1723505d05c7Sgtb                             size += sizeof(krb5_int32) - remainder;
1724505d05c7Sgtb                         }
1725505d05c7Sgtb                     }
1726505d05c7Sgtb 
1727505d05c7Sgtb                     if (fseek(KTFILEP(id), 0L, SEEK_CUR) < 0)
1728505d05c7Sgtb                     {
1729505d05c7Sgtb                         return errno;
1730505d05c7Sgtb                     }
1731505d05c7Sgtb 
1732505d05c7Sgtb                     memset(iobuf, 0, (size_t) size);
1733505d05c7Sgtb                     xfwrite(iobuf, 1, (size_t) size, KTFILEP(id));
1734505d05c7Sgtb 		    fflush(KTFILEP(id));
1735505d05c7Sgtb                     if (feof(KTFILEP(id))) {
1736505d05c7Sgtb                         break;
1737505d05c7Sgtb                     }
1738505d05c7Sgtb 
1739505d05c7Sgtb                     if (fseek(KTFILEP(id), 0L, SEEK_CUR) < 0)
1740505d05c7Sgtb                     {
1741505d05c7Sgtb                         return errno;
1742505d05c7Sgtb                     }
1743505d05c7Sgtb 
1744505d05c7Sgtb                 }
1745505d05c7Sgtb                 if (fseek(KTFILEP(id), zero_point, SEEK_SET)) {
1746505d05c7Sgtb                     return errno;
1747505d05c7Sgtb                 }
1748505d05c7Sgtb             }
1749505d05c7Sgtb         }
1750505d05c7Sgtb     }
1751505d05c7Sgtb 
1752505d05c7Sgtb     return 0;
1753505d05c7Sgtb }
1754