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