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