No empty .Rs/.Re
[netbsd-mini2440.git] / crypto / dist / heimdal / lib / krb5 / keytab_file.c
blob5a3749ddd7ac5efc957a58cb96411d78f0b8e7ea
1 /*
2 * Copyright (c) 1997 - 2005 Kungliga Tekniska Högskolan
3 * (Royal Institute of Technology, Stockholm, Sweden).
4 * All rights reserved.
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
17 * 3. Neither the name of the Institute nor the names of its contributors
18 * may be used to endorse or promote products derived from this software
19 * without specific prior written permission.
21 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
34 #include "krb5_locl.h"
36 __RCSID("$Heimdal: keytab_file.c 17457 2006-05-05 12:36:57Z lha $"
37 "$NetBSD$");
39 #define KRB5_KT_VNO_1 1
40 #define KRB5_KT_VNO_2 2
41 #define KRB5_KT_VNO KRB5_KT_VNO_2
43 #define KRB5_KT_FL_JAVA 1
46 /* file operations -------------------------------------------- */
48 struct fkt_data {
49 char *filename;
50 int flags;
53 static krb5_error_code
54 krb5_kt_ret_data(krb5_context context,
55 krb5_storage *sp,
56 krb5_data *data)
58 int ret;
59 int16_t size;
60 ret = krb5_ret_int16(sp, &size);
61 if(ret)
62 return ret;
63 data->length = size;
64 data->data = malloc(size);
65 if (data->data == NULL) {
66 krb5_set_error_string (context, "malloc: out of memory");
67 return ENOMEM;
69 ret = krb5_storage_read(sp, data->data, size);
70 if(ret != size)
71 return (ret < 0)? errno : KRB5_KT_END;
72 return 0;
75 static krb5_error_code
76 krb5_kt_ret_string(krb5_context context,
77 krb5_storage *sp,
78 heim_general_string *data)
80 int ret;
81 int16_t size;
82 ret = krb5_ret_int16(sp, &size);
83 if(ret)
84 return ret;
85 *data = malloc(size + 1);
86 if (*data == NULL) {
87 krb5_set_error_string (context, "malloc: out of memory");
88 return ENOMEM;
90 ret = krb5_storage_read(sp, *data, size);
91 (*data)[size] = '\0';
92 if(ret != size)
93 return (ret < 0)? errno : KRB5_KT_END;
94 return 0;
97 static krb5_error_code
98 krb5_kt_store_data(krb5_context context,
99 krb5_storage *sp,
100 krb5_data data)
102 int ret;
103 ret = krb5_store_int16(sp, data.length);
104 if(ret < 0)
105 return ret;
106 ret = krb5_storage_write(sp, data.data, data.length);
107 if(ret != data.length){
108 if(ret < 0)
109 return errno;
110 return KRB5_KT_END;
112 return 0;
115 static krb5_error_code
116 krb5_kt_store_string(krb5_storage *sp,
117 heim_general_string data)
119 int ret;
120 size_t len = strlen(data);
121 ret = krb5_store_int16(sp, len);
122 if(ret < 0)
123 return ret;
124 ret = krb5_storage_write(sp, data, len);
125 if(ret != len){
126 if(ret < 0)
127 return errno;
128 return KRB5_KT_END;
130 return 0;
133 static krb5_error_code
134 krb5_kt_ret_keyblock(krb5_context context, krb5_storage *sp, krb5_keyblock *p)
136 int ret;
137 int16_t tmp;
139 ret = krb5_ret_int16(sp, &tmp); /* keytype + etype */
140 if(ret) return ret;
141 p->keytype = tmp;
142 ret = krb5_kt_ret_data(context, sp, &p->keyvalue);
143 return ret;
146 static krb5_error_code
147 krb5_kt_store_keyblock(krb5_context context,
148 krb5_storage *sp,
149 krb5_keyblock *p)
151 int ret;
153 ret = krb5_store_int16(sp, p->keytype); /* keytype + etype */
154 if(ret) return ret;
155 ret = krb5_kt_store_data(context, sp, p->keyvalue);
156 return ret;
160 static krb5_error_code
161 krb5_kt_ret_principal(krb5_context context,
162 krb5_storage *sp,
163 krb5_principal *princ)
165 int i;
166 int ret;
167 krb5_principal p;
168 int16_t len;
170 ALLOC(p, 1);
171 if(p == NULL) {
172 krb5_set_error_string (context, "malloc: out of memory");
173 return ENOMEM;
176 ret = krb5_ret_int16(sp, &len);
177 if(ret) {
178 krb5_set_error_string(context,
179 "Failed decoding length of keytab principal");
180 goto out;
182 if(krb5_storage_is_flags(sp, KRB5_STORAGE_PRINCIPAL_WRONG_NUM_COMPONENTS))
183 len--;
184 if (len < 0) {
185 krb5_set_error_string(context,
186 "Keytab principal contains invalid length");
187 ret = KRB5_KT_END;
188 goto out;
190 ret = krb5_kt_ret_string(context, sp, &p->realm);
191 if(ret)
192 goto out;
193 p->name.name_string.val = calloc(len, sizeof(*p->name.name_string.val));
194 if(p->name.name_string.val == NULL) {
195 krb5_set_error_string (context, "malloc: out of memory");
196 ret = ENOMEM;
197 goto out;
199 p->name.name_string.len = len;
200 for(i = 0; i < p->name.name_string.len; i++){
201 ret = krb5_kt_ret_string(context, sp, p->name.name_string.val + i);
202 if(ret)
203 goto out;
205 if (krb5_storage_is_flags(sp, KRB5_STORAGE_PRINCIPAL_NO_NAME_TYPE))
206 p->name.name_type = KRB5_NT_UNKNOWN;
207 else {
208 int32_t tmp32;
209 ret = krb5_ret_int32(sp, &tmp32);
210 p->name.name_type = tmp32;
211 if (ret)
212 goto out;
214 *princ = p;
215 return 0;
216 out:
217 krb5_free_principal(context, p);
218 return ret;
221 static krb5_error_code
222 krb5_kt_store_principal(krb5_context context,
223 krb5_storage *sp,
224 krb5_principal p)
226 int i;
227 int ret;
229 if(krb5_storage_is_flags(sp, KRB5_STORAGE_PRINCIPAL_WRONG_NUM_COMPONENTS))
230 ret = krb5_store_int16(sp, p->name.name_string.len + 1);
231 else
232 ret = krb5_store_int16(sp, p->name.name_string.len);
233 if(ret) return ret;
234 ret = krb5_kt_store_string(sp, p->realm);
235 if(ret) return ret;
236 for(i = 0; i < p->name.name_string.len; i++){
237 ret = krb5_kt_store_string(sp, p->name.name_string.val[i]);
238 if(ret)
239 return ret;
241 if(!krb5_storage_is_flags(sp, KRB5_STORAGE_PRINCIPAL_NO_NAME_TYPE)) {
242 ret = krb5_store_int32(sp, p->name.name_type);
243 if(ret)
244 return ret;
247 return 0;
250 static krb5_error_code
251 fkt_resolve(krb5_context context, const char *name, krb5_keytab id)
253 struct fkt_data *d;
255 d = malloc(sizeof(*d));
256 if(d == NULL) {
257 krb5_set_error_string (context, "malloc: out of memory");
258 return ENOMEM;
260 d->filename = strdup(name);
261 if(d->filename == NULL) {
262 free(d);
263 krb5_set_error_string (context, "malloc: out of memory");
264 return ENOMEM;
266 d->flags = 0;
267 id->data = d;
268 return 0;
271 static krb5_error_code
272 fkt_resolve_java14(krb5_context context, const char *name, krb5_keytab id)
274 krb5_error_code ret;
276 ret = fkt_resolve(context, name, id);
277 if (ret == 0) {
278 struct fkt_data *d = id->data;
279 d->flags |= KRB5_KT_FL_JAVA;
281 return ret;
284 static krb5_error_code
285 fkt_close(krb5_context context, krb5_keytab id)
287 struct fkt_data *d = id->data;
288 free(d->filename);
289 free(d);
290 return 0;
293 static krb5_error_code
294 fkt_get_name(krb5_context context,
295 krb5_keytab id,
296 char *name,
297 size_t namesize)
299 /* This function is XXX */
300 struct fkt_data *d = id->data;
301 strlcpy(name, d->filename, namesize);
302 return 0;
305 static void
306 storage_set_flags(krb5_context context, krb5_storage *sp, int vno)
308 int flags = 0;
309 switch(vno) {
310 case KRB5_KT_VNO_1:
311 flags |= KRB5_STORAGE_PRINCIPAL_WRONG_NUM_COMPONENTS;
312 flags |= KRB5_STORAGE_PRINCIPAL_NO_NAME_TYPE;
313 flags |= KRB5_STORAGE_HOST_BYTEORDER;
314 break;
315 case KRB5_KT_VNO_2:
316 break;
317 default:
318 krb5_warnx(context,
319 "storage_set_flags called with bad vno (%d)", vno);
321 krb5_storage_set_flags(sp, flags);
324 static krb5_error_code
325 fkt_start_seq_get_int(krb5_context context,
326 krb5_keytab id,
327 int flags,
328 int exclusive,
329 krb5_kt_cursor *c)
331 int8_t pvno, tag;
332 krb5_error_code ret;
333 struct fkt_data *d = id->data;
335 c->fd = open (d->filename, flags);
336 if (c->fd < 0) {
337 ret = errno;
338 krb5_set_error_string(context, "%s: %s", d->filename,
339 strerror(ret));
340 return ret;
342 ret = _krb5_xlock(context, c->fd, exclusive, d->filename);
343 if (ret) {
344 close(c->fd);
345 return ret;
347 c->sp = krb5_storage_from_fd(c->fd);
348 if (c->sp == NULL) {
349 _krb5_xunlock(context, c->fd);
350 close(c->fd);
351 krb5_set_error_string (context, "malloc: out of memory");
352 return ENOMEM;
354 krb5_storage_set_eof_code(c->sp, KRB5_KT_END);
355 ret = krb5_ret_int8(c->sp, &pvno);
356 if(ret) {
357 krb5_storage_free(c->sp);
358 _krb5_xunlock(context, c->fd);
359 close(c->fd);
360 krb5_clear_error_string(context);
361 return ret;
363 if(pvno != 5) {
364 krb5_storage_free(c->sp);
365 _krb5_xunlock(context, c->fd);
366 close(c->fd);
367 krb5_clear_error_string (context);
368 return KRB5_KEYTAB_BADVNO;
370 ret = krb5_ret_int8(c->sp, &tag);
371 if (ret) {
372 krb5_storage_free(c->sp);
373 _krb5_xunlock(context, c->fd);
374 close(c->fd);
375 krb5_clear_error_string(context);
376 return ret;
378 id->version = tag;
379 storage_set_flags(context, c->sp, id->version);
380 return 0;
383 static krb5_error_code
384 fkt_start_seq_get(krb5_context context,
385 krb5_keytab id,
386 krb5_kt_cursor *c)
388 return fkt_start_seq_get_int(context, id, O_RDONLY | O_BINARY, 0, c);
391 static krb5_error_code
392 fkt_next_entry_int(krb5_context context,
393 krb5_keytab id,
394 krb5_keytab_entry *entry,
395 krb5_kt_cursor *cursor,
396 off_t *start,
397 off_t *end)
399 int32_t len;
400 int ret;
401 int8_t tmp8;
402 int32_t tmp32;
403 off_t pos, curpos;
405 pos = krb5_storage_seek(cursor->sp, 0, SEEK_CUR);
406 loop:
407 ret = krb5_ret_int32(cursor->sp, &len);
408 if (ret)
409 return ret;
410 if(len < 0) {
411 pos = krb5_storage_seek(cursor->sp, -len, SEEK_CUR);
412 goto loop;
414 ret = krb5_kt_ret_principal (context, cursor->sp, &entry->principal);
415 if (ret)
416 goto out;
417 ret = krb5_ret_int32(cursor->sp, &tmp32);
418 entry->timestamp = tmp32;
419 if (ret)
420 goto out;
421 ret = krb5_ret_int8(cursor->sp, &tmp8);
422 if (ret)
423 goto out;
424 entry->vno = tmp8;
425 ret = krb5_kt_ret_keyblock (context, cursor->sp, &entry->keyblock);
426 if (ret)
427 goto out;
428 /* there might be a 32 bit kvno here
429 * if it's zero, assume that the 8bit one was right,
430 * otherwise trust the new value */
431 curpos = krb5_storage_seek(cursor->sp, 0, SEEK_CUR);
432 if(len + 4 + pos - curpos >= 4) {
433 ret = krb5_ret_int32(cursor->sp, &tmp32);
434 if (ret == 0 && tmp32 != 0) {
435 entry->vno = tmp32;
438 if(start) *start = pos;
439 if(end) *end = pos + 4 + len;
440 out:
441 krb5_storage_seek(cursor->sp, pos + 4 + len, SEEK_SET);
442 return ret;
445 static krb5_error_code
446 fkt_next_entry(krb5_context context,
447 krb5_keytab id,
448 krb5_keytab_entry *entry,
449 krb5_kt_cursor *cursor)
451 return fkt_next_entry_int(context, id, entry, cursor, NULL, NULL);
454 static krb5_error_code
455 fkt_end_seq_get(krb5_context context,
456 krb5_keytab id,
457 krb5_kt_cursor *cursor)
459 krb5_storage_free(cursor->sp);
460 _krb5_xunlock(context, cursor->fd);
461 close(cursor->fd);
462 return 0;
465 static krb5_error_code
466 fkt_setup_keytab(krb5_context context,
467 krb5_keytab id,
468 krb5_storage *sp)
470 krb5_error_code ret;
471 ret = krb5_store_int8(sp, 5);
472 if(ret)
473 return ret;
474 if(id->version == 0)
475 id->version = KRB5_KT_VNO;
476 return krb5_store_int8 (sp, id->version);
479 static krb5_error_code
480 fkt_add_entry(krb5_context context,
481 krb5_keytab id,
482 krb5_keytab_entry *entry)
484 int ret;
485 int fd;
486 krb5_storage *sp;
487 struct fkt_data *d = id->data;
488 krb5_data keytab;
489 int32_t len;
491 fd = open (d->filename, O_RDWR | O_BINARY);
492 if (fd < 0) {
493 fd = open (d->filename, O_RDWR | O_CREAT | O_EXCL | O_BINARY, 0600);
494 if (fd < 0) {
495 ret = errno;
496 krb5_set_error_string(context, "open(%s): %s", d->filename,
497 strerror(ret));
498 return ret;
500 ret = _krb5_xlock(context, fd, 1, d->filename);
501 if (ret) {
502 close(fd);
503 return ret;
505 sp = krb5_storage_from_fd(fd);
506 krb5_storage_set_eof_code(sp, KRB5_KT_END);
507 ret = fkt_setup_keytab(context, id, sp);
508 if(ret) {
509 goto out;
511 storage_set_flags(context, sp, id->version);
512 } else {
513 int8_t pvno, tag;
514 ret = _krb5_xlock(context, fd, 1, d->filename);
515 if (ret) {
516 close(fd);
517 return ret;
519 sp = krb5_storage_from_fd(fd);
520 krb5_storage_set_eof_code(sp, KRB5_KT_END);
521 ret = krb5_ret_int8(sp, &pvno);
522 if(ret) {
523 /* we probably have a zero byte file, so try to set it up
524 properly */
525 ret = fkt_setup_keytab(context, id, sp);
526 if(ret) {
527 krb5_set_error_string(context, "%s: keytab is corrupted: %s",
528 d->filename, strerror(ret));
529 goto out;
531 storage_set_flags(context, sp, id->version);
532 } else {
533 if(pvno != 5) {
534 ret = KRB5_KEYTAB_BADVNO;
535 krb5_set_error_string(context, "%s: %s",
536 d->filename, strerror(ret));
537 goto out;
539 ret = krb5_ret_int8 (sp, &tag);
540 if (ret) {
541 krb5_set_error_string(context, "%s: reading tag: %s",
542 d->filename, strerror(ret));
543 goto out;
545 id->version = tag;
546 storage_set_flags(context, sp, id->version);
551 krb5_storage *emem;
552 emem = krb5_storage_emem();
553 if(emem == NULL) {
554 ret = ENOMEM;
555 krb5_set_error_string (context, "malloc: out of memory");
556 goto out;
558 ret = krb5_kt_store_principal(context, emem, entry->principal);
559 if(ret) {
560 krb5_storage_free(emem);
561 goto out;
563 ret = krb5_store_int32 (emem, entry->timestamp);
564 if(ret) {
565 krb5_storage_free(emem);
566 goto out;
568 ret = krb5_store_int8 (emem, entry->vno % 256);
569 if(ret) {
570 krb5_storage_free(emem);
571 goto out;
573 ret = krb5_kt_store_keyblock (context, emem, &entry->keyblock);
574 if(ret) {
575 krb5_storage_free(emem);
576 goto out;
578 if ((d->flags & KRB5_KT_FL_JAVA) == 0) {
579 ret = krb5_store_int32 (emem, entry->vno);
580 if (ret) {
581 krb5_storage_free(emem);
582 goto out;
586 ret = krb5_storage_to_data(emem, &keytab);
587 krb5_storage_free(emem);
588 if(ret)
589 goto out;
592 while(1) {
593 ret = krb5_ret_int32(sp, &len);
594 if(ret == KRB5_KT_END) {
595 len = keytab.length;
596 break;
598 if(len < 0) {
599 len = -len;
600 if(len >= keytab.length) {
601 krb5_storage_seek(sp, -4, SEEK_CUR);
602 break;
605 krb5_storage_seek(sp, len, SEEK_CUR);
607 ret = krb5_store_int32(sp, len);
608 if(krb5_storage_write(sp, keytab.data, keytab.length) < 0)
609 ret = errno;
610 memset(keytab.data, 0, keytab.length);
611 krb5_data_free(&keytab);
612 out:
613 krb5_storage_free(sp);
614 _krb5_xunlock(context, fd);
615 close(fd);
616 return ret;
619 static krb5_error_code
620 fkt_remove_entry(krb5_context context,
621 krb5_keytab id,
622 krb5_keytab_entry *entry)
624 krb5_keytab_entry e;
625 krb5_kt_cursor cursor;
626 off_t pos_start, pos_end;
627 int found = 0;
628 krb5_error_code ret;
630 ret = fkt_start_seq_get_int(context, id, O_RDWR | O_BINARY, 1, &cursor);
631 if(ret != 0)
632 goto out; /* return other error here? */
633 while(fkt_next_entry_int(context, id, &e, &cursor,
634 &pos_start, &pos_end) == 0) {
635 if(krb5_kt_compare(context, &e, entry->principal,
636 entry->vno, entry->keyblock.keytype)) {
637 int32_t len;
638 unsigned char buf[128];
639 found = 1;
640 krb5_storage_seek(cursor.sp, pos_start, SEEK_SET);
641 len = pos_end - pos_start - 4;
642 krb5_store_int32(cursor.sp, -len);
643 memset(buf, 0, sizeof(buf));
644 while(len > 0) {
645 krb5_storage_write(cursor.sp, buf, min(len, sizeof(buf)));
646 len -= min(len, sizeof(buf));
649 krb5_kt_free_entry(context, &e);
651 krb5_kt_end_seq_get(context, id, &cursor);
652 out:
653 if (!found) {
654 krb5_clear_error_string (context);
655 return KRB5_KT_NOTFOUND;
657 return 0;
660 const krb5_kt_ops krb5_fkt_ops = {
661 "FILE",
662 fkt_resolve,
663 fkt_get_name,
664 fkt_close,
665 NULL, /* get */
666 fkt_start_seq_get,
667 fkt_next_entry,
668 fkt_end_seq_get,
669 fkt_add_entry,
670 fkt_remove_entry
673 const krb5_kt_ops krb5_wrfkt_ops = {
674 "WRFILE",
675 fkt_resolve,
676 fkt_get_name,
677 fkt_close,
678 NULL, /* get */
679 fkt_start_seq_get,
680 fkt_next_entry,
681 fkt_end_seq_get,
682 fkt_add_entry,
683 fkt_remove_entry
686 const krb5_kt_ops krb5_javakt_ops = {
687 "JAVA14",
688 fkt_resolve_java14,
689 fkt_get_name,
690 fkt_close,
691 NULL, /* get */
692 fkt_start_seq_get,
693 fkt_next_entry,
694 fkt_end_seq_get,
695 fkt_add_entry,
696 fkt_remove_entry