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(×tamp, 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