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