]> CyberLeo.Net >> Repos - FreeBSD/releng/9.2.git/blob - crypto/heimdal/lib/krb5/keytab_file.c
- Copy stable/9 to releng/9.2 as part of the 9.2-RELEASE cycle.
[FreeBSD/releng/9.2.git] / crypto / heimdal / lib / krb5 / keytab_file.c
1 /*
2  * Copyright (c) 1997 - 2005 Kungliga Tekniska Högskolan
3  * (Royal Institute of Technology, Stockholm, Sweden). 
4  * All rights reserved. 
5  *
6  * Redistribution and use in source and binary forms, with or without 
7  * modification, are permitted provided that the following conditions 
8  * are met: 
9  *
10  * 1. Redistributions of source code must retain the above copyright 
11  *    notice, this list of conditions and the following disclaimer. 
12  *
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. 
16  *
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. 
20  *
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. 
32  */
33
34 #include "krb5_locl.h"
35
36 RCSID("$Id: keytab_file.c 17457 2006-05-05 12:36:57Z lha $");
37
38 #define KRB5_KT_VNO_1 1
39 #define KRB5_KT_VNO_2 2
40 #define KRB5_KT_VNO   KRB5_KT_VNO_2
41
42 #define KRB5_KT_FL_JAVA 1
43
44
45 /* file operations -------------------------------------------- */
46
47 struct fkt_data {
48     char *filename;
49     int flags;
50 };
51
52 static krb5_error_code
53 krb5_kt_ret_data(krb5_context context,
54                  krb5_storage *sp,
55                  krb5_data *data)
56 {
57     int ret;
58     int16_t size;
59     ret = krb5_ret_int16(sp, &size);
60     if(ret)
61         return ret;
62     data->length = size;
63     data->data = malloc(size);
64     if (data->data == NULL) {
65         krb5_set_error_string (context, "malloc: out of memory");
66         return ENOMEM;
67     }
68     ret = krb5_storage_read(sp, data->data, size);
69     if(ret != size)
70         return (ret < 0)? errno : KRB5_KT_END;
71     return 0;
72 }
73
74 static krb5_error_code
75 krb5_kt_ret_string(krb5_context context,
76                    krb5_storage *sp,
77                    heim_general_string *data)
78 {
79     int ret;
80     int16_t size;
81     ret = krb5_ret_int16(sp, &size);
82     if(ret)
83         return ret;
84     *data = malloc(size + 1);
85     if (*data == NULL) {
86         krb5_set_error_string (context, "malloc: out of memory");
87         return ENOMEM;
88     }
89     ret = krb5_storage_read(sp, *data, size);
90     (*data)[size] = '\0';
91     if(ret != size)
92         return (ret < 0)? errno : KRB5_KT_END;
93     return 0;
94 }
95
96 static krb5_error_code
97 krb5_kt_store_data(krb5_context context,
98                    krb5_storage *sp,
99                    krb5_data data)
100 {
101     int ret;
102     ret = krb5_store_int16(sp, data.length);
103     if(ret < 0)
104         return ret;
105     ret = krb5_storage_write(sp, data.data, data.length);
106     if(ret != data.length){
107         if(ret < 0)
108             return errno;
109         return KRB5_KT_END;
110     }
111     return 0;
112 }
113
114 static krb5_error_code
115 krb5_kt_store_string(krb5_storage *sp,
116                      heim_general_string data)
117 {
118     int ret;
119     size_t len = strlen(data);
120     ret = krb5_store_int16(sp, len);
121     if(ret < 0)
122         return ret;
123     ret = krb5_storage_write(sp, data, len);
124     if(ret != len){
125         if(ret < 0)
126             return errno;
127         return KRB5_KT_END;
128     }
129     return 0;
130 }
131
132 static krb5_error_code
133 krb5_kt_ret_keyblock(krb5_context context, krb5_storage *sp, krb5_keyblock *p)
134 {
135     int ret;
136     int16_t tmp;
137
138     ret = krb5_ret_int16(sp, &tmp); /* keytype + etype */
139     if(ret) return ret;
140     p->keytype = tmp;
141     ret = krb5_kt_ret_data(context, sp, &p->keyvalue);
142     return ret;
143 }
144
145 static krb5_error_code
146 krb5_kt_store_keyblock(krb5_context context,
147                        krb5_storage *sp, 
148                        krb5_keyblock *p)
149 {
150     int ret;
151
152     ret = krb5_store_int16(sp, p->keytype); /* keytype + etype */
153     if(ret) return ret;
154     ret = krb5_kt_store_data(context, sp, p->keyvalue);
155     return ret;
156 }
157
158
159 static krb5_error_code
160 krb5_kt_ret_principal(krb5_context context,
161                       krb5_storage *sp,
162                       krb5_principal *princ)
163 {
164     int i;
165     int ret;
166     krb5_principal p;
167     int16_t len;
168     
169     ALLOC(p, 1);
170     if(p == NULL) {
171         krb5_set_error_string (context, "malloc: out of memory");
172         return ENOMEM;
173     }
174
175     ret = krb5_ret_int16(sp, &len);
176     if(ret) {
177         krb5_set_error_string(context,
178                               "Failed decoding length of keytab principal");
179         goto out;
180     }
181     if(krb5_storage_is_flags(sp, KRB5_STORAGE_PRINCIPAL_WRONG_NUM_COMPONENTS))
182         len--;
183     if (len < 0) {
184         krb5_set_error_string(context, 
185                               "Keytab principal contains invalid length");
186         ret = KRB5_KT_END;
187         goto out;
188     }
189     ret = krb5_kt_ret_string(context, sp, &p->realm);
190     if(ret)
191         goto out;
192     p->name.name_string.val = calloc(len, sizeof(*p->name.name_string.val));
193     if(p->name.name_string.val == NULL) {
194         krb5_set_error_string (context, "malloc: out of memory");
195         ret = ENOMEM;
196         goto out;
197     }
198     p->name.name_string.len = len;
199     for(i = 0; i < p->name.name_string.len; i++){
200         ret = krb5_kt_ret_string(context, sp, p->name.name_string.val + i);
201         if(ret)
202             goto out;
203     }
204     if (krb5_storage_is_flags(sp, KRB5_STORAGE_PRINCIPAL_NO_NAME_TYPE))
205         p->name.name_type = KRB5_NT_UNKNOWN;
206     else {
207         int32_t tmp32;
208         ret = krb5_ret_int32(sp, &tmp32);
209         p->name.name_type = tmp32;
210         if (ret)
211             goto out;
212     }
213     *princ = p;
214     return 0;
215 out:
216     krb5_free_principal(context, p);
217     return ret;
218 }
219
220 static krb5_error_code
221 krb5_kt_store_principal(krb5_context context,
222                         krb5_storage *sp,
223                         krb5_principal p)
224 {
225     int i;
226     int ret;
227     
228     if(krb5_storage_is_flags(sp, KRB5_STORAGE_PRINCIPAL_WRONG_NUM_COMPONENTS))
229         ret = krb5_store_int16(sp, p->name.name_string.len + 1);
230     else
231         ret = krb5_store_int16(sp, p->name.name_string.len);
232     if(ret) return ret;
233     ret = krb5_kt_store_string(sp, p->realm);
234     if(ret) return ret;
235     for(i = 0; i < p->name.name_string.len; i++){
236         ret = krb5_kt_store_string(sp, p->name.name_string.val[i]);
237         if(ret)
238             return ret;
239     }
240     if(!krb5_storage_is_flags(sp, KRB5_STORAGE_PRINCIPAL_NO_NAME_TYPE)) {
241         ret = krb5_store_int32(sp, p->name.name_type);
242         if(ret)
243             return ret;
244     }
245
246     return 0;
247 }
248
249 static krb5_error_code
250 fkt_resolve(krb5_context context, const char *name, krb5_keytab id)
251 {
252     struct fkt_data *d;
253
254     d = malloc(sizeof(*d));
255     if(d == NULL) {
256         krb5_set_error_string (context, "malloc: out of memory");
257         return ENOMEM;
258     }
259     d->filename = strdup(name);
260     if(d->filename == NULL) {
261         free(d);
262         krb5_set_error_string (context, "malloc: out of memory");
263         return ENOMEM;
264     }
265     d->flags = 0;
266     id->data = d;
267     return 0;
268 }
269
270 static krb5_error_code
271 fkt_resolve_java14(krb5_context context, const char *name, krb5_keytab id)
272 {
273     krb5_error_code ret;
274
275     ret = fkt_resolve(context, name, id);
276     if (ret == 0) {
277         struct fkt_data *d = id->data;
278         d->flags |= KRB5_KT_FL_JAVA;
279     }
280     return ret;
281 }
282
283 static krb5_error_code
284 fkt_close(krb5_context context, krb5_keytab id)
285 {
286     struct fkt_data *d = id->data;
287     free(d->filename);
288     free(d);
289     return 0;
290 }
291
292 static krb5_error_code 
293 fkt_get_name(krb5_context context, 
294              krb5_keytab id, 
295              char *name, 
296              size_t namesize)
297 {
298     /* This function is XXX */
299     struct fkt_data *d = id->data;
300     strlcpy(name, d->filename, namesize);
301     return 0;
302 }
303
304 static void
305 storage_set_flags(krb5_context context, krb5_storage *sp, int vno)
306 {
307     int flags = 0;
308     switch(vno) {
309     case KRB5_KT_VNO_1:
310         flags |= KRB5_STORAGE_PRINCIPAL_WRONG_NUM_COMPONENTS;
311         flags |= KRB5_STORAGE_PRINCIPAL_NO_NAME_TYPE;
312         flags |= KRB5_STORAGE_HOST_BYTEORDER;
313         break;
314     case KRB5_KT_VNO_2:
315         break;
316     default:
317         krb5_warnx(context, 
318                    "storage_set_flags called with bad vno (%d)", vno);
319     }
320     krb5_storage_set_flags(sp, flags);
321 }
322
323 static krb5_error_code
324 fkt_start_seq_get_int(krb5_context context, 
325                       krb5_keytab id, 
326                       int flags,
327                       int exclusive,
328                       krb5_kt_cursor *c)
329 {
330     int8_t pvno, tag;
331     krb5_error_code ret;
332     struct fkt_data *d = id->data;
333     
334     c->fd = open (d->filename, flags);
335     if (c->fd < 0) {
336         ret = errno;
337         krb5_set_error_string(context, "%s: %s", d->filename,
338                               strerror(ret));
339         return ret;
340     }
341     ret = _krb5_xlock(context, c->fd, exclusive, d->filename);
342     if (ret) {
343         close(c->fd);
344         return ret;
345     }
346     c->sp = krb5_storage_from_fd(c->fd);
347     if (c->sp == NULL) {
348         _krb5_xunlock(context, c->fd);
349         close(c->fd);
350         krb5_set_error_string (context, "malloc: out of memory");
351         return ENOMEM;
352     }
353     krb5_storage_set_eof_code(c->sp, KRB5_KT_END);
354     ret = krb5_ret_int8(c->sp, &pvno);
355     if(ret) {
356         krb5_storage_free(c->sp);
357         _krb5_xunlock(context, c->fd);
358         close(c->fd);
359         krb5_clear_error_string(context);
360         return ret;
361     }
362     if(pvno != 5) {
363         krb5_storage_free(c->sp);
364         _krb5_xunlock(context, c->fd);
365         close(c->fd);
366         krb5_clear_error_string (context);
367         return KRB5_KEYTAB_BADVNO;
368     }
369     ret = krb5_ret_int8(c->sp, &tag);
370     if (ret) {
371         krb5_storage_free(c->sp);
372         _krb5_xunlock(context, c->fd);
373         close(c->fd);
374         krb5_clear_error_string(context);
375         return ret;
376     }
377     id->version = tag;
378     storage_set_flags(context, c->sp, id->version);
379     return 0;
380 }
381
382 static krb5_error_code
383 fkt_start_seq_get(krb5_context context, 
384                   krb5_keytab id, 
385                   krb5_kt_cursor *c)
386 {
387     return fkt_start_seq_get_int(context, id, O_RDONLY | O_BINARY, 0, c);
388 }
389
390 static krb5_error_code
391 fkt_next_entry_int(krb5_context context, 
392                    krb5_keytab id, 
393                    krb5_keytab_entry *entry, 
394                    krb5_kt_cursor *cursor,
395                    off_t *start,
396                    off_t *end)
397 {
398     int32_t len;
399     int ret;
400     int8_t tmp8;
401     int32_t tmp32;
402     off_t pos, curpos;
403
404     pos = krb5_storage_seek(cursor->sp, 0, SEEK_CUR);
405 loop:
406     ret = krb5_ret_int32(cursor->sp, &len);
407     if (ret)
408         return ret;
409     if(len < 0) {
410         pos = krb5_storage_seek(cursor->sp, -len, SEEK_CUR);
411         goto loop;
412     }
413     ret = krb5_kt_ret_principal (context, cursor->sp, &entry->principal);
414     if (ret)
415         goto out;
416     ret = krb5_ret_int32(cursor->sp, &tmp32);
417     entry->timestamp = tmp32;
418     if (ret)
419         goto out;
420     ret = krb5_ret_int8(cursor->sp, &tmp8);
421     if (ret)
422         goto out;
423     entry->vno = tmp8;
424     ret = krb5_kt_ret_keyblock (context, cursor->sp, &entry->keyblock);
425     if (ret)
426         goto out;
427     /* there might be a 32 bit kvno here
428      * if it's zero, assume that the 8bit one was right,
429      * otherwise trust the new value */
430     curpos = krb5_storage_seek(cursor->sp, 0, SEEK_CUR);
431     if(len + 4 + pos - curpos >= 4) {
432         ret = krb5_ret_int32(cursor->sp, &tmp32);
433         if (ret == 0 && tmp32 != 0) {
434             entry->vno = tmp32;
435         }
436     }
437     if(start) *start = pos;
438     if(end) *end = pos + 4 + len;
439  out:
440     krb5_storage_seek(cursor->sp, pos + 4 + len, SEEK_SET);
441     return ret;
442 }
443
444 static krb5_error_code
445 fkt_next_entry(krb5_context context, 
446                krb5_keytab id, 
447                krb5_keytab_entry *entry, 
448                krb5_kt_cursor *cursor)
449 {
450     return fkt_next_entry_int(context, id, entry, cursor, NULL, NULL);
451 }
452
453 static krb5_error_code
454 fkt_end_seq_get(krb5_context context, 
455                 krb5_keytab id,
456                 krb5_kt_cursor *cursor)
457 {
458     krb5_storage_free(cursor->sp);
459     _krb5_xunlock(context, cursor->fd);
460     close(cursor->fd);
461     return 0;
462 }
463
464 static krb5_error_code
465 fkt_setup_keytab(krb5_context context,
466                  krb5_keytab id,
467                  krb5_storage *sp)
468 {
469     krb5_error_code ret;
470     ret = krb5_store_int8(sp, 5);
471     if(ret)
472         return ret;
473     if(id->version == 0)
474         id->version = KRB5_KT_VNO;
475     return krb5_store_int8 (sp, id->version);
476 }
477                  
478 static krb5_error_code
479 fkt_add_entry(krb5_context context,
480               krb5_keytab id,
481               krb5_keytab_entry *entry)
482 {
483     int ret;
484     int fd;
485     krb5_storage *sp;
486     struct fkt_data *d = id->data;
487     krb5_data keytab;
488     int32_t len;
489     
490     fd = open (d->filename, O_RDWR | O_BINARY);
491     if (fd < 0) {
492         fd = open (d->filename, O_RDWR | O_CREAT | O_EXCL | O_BINARY, 0600);
493         if (fd < 0) {
494             ret = errno;
495             krb5_set_error_string(context, "open(%s): %s", d->filename,
496                                   strerror(ret));
497             return ret;
498         }
499         ret = _krb5_xlock(context, fd, 1, d->filename);
500         if (ret) {
501             close(fd);
502             return ret;
503         }
504         sp = krb5_storage_from_fd(fd);
505         krb5_storage_set_eof_code(sp, KRB5_KT_END);
506         ret = fkt_setup_keytab(context, id, sp);
507         if(ret) {
508             goto out;
509         }
510         storage_set_flags(context, sp, id->version);
511     } else {
512         int8_t pvno, tag;
513         ret = _krb5_xlock(context, fd, 1, d->filename);
514         if (ret) {
515             close(fd);
516             return ret;
517         }
518         sp = krb5_storage_from_fd(fd);
519         krb5_storage_set_eof_code(sp, KRB5_KT_END);
520         ret = krb5_ret_int8(sp, &pvno);
521         if(ret) {
522             /* we probably have a zero byte file, so try to set it up
523                properly */
524             ret = fkt_setup_keytab(context, id, sp);
525             if(ret) {
526                 krb5_set_error_string(context, "%s: keytab is corrupted: %s", 
527                                       d->filename, strerror(ret));
528                 goto out;
529             }
530             storage_set_flags(context, sp, id->version);
531         } else {
532             if(pvno != 5) {
533                 ret = KRB5_KEYTAB_BADVNO;
534                 krb5_set_error_string(context, "%s: %s", 
535                                       d->filename, strerror(ret));
536                 goto out;
537             }
538             ret = krb5_ret_int8 (sp, &tag);
539             if (ret) {
540                 krb5_set_error_string(context, "%s: reading tag: %s", 
541                                       d->filename, strerror(ret));
542                 goto out;
543             }
544             id->version = tag;
545             storage_set_flags(context, sp, id->version);
546         }
547     }
548
549     {
550         krb5_storage *emem;
551         emem = krb5_storage_emem();
552         if(emem == NULL) {
553             ret = ENOMEM;
554             krb5_set_error_string (context, "malloc: out of memory");
555             goto out;
556         }
557         ret = krb5_kt_store_principal(context, emem, entry->principal);
558         if(ret) {
559             krb5_storage_free(emem);
560             goto out;
561         }
562         ret = krb5_store_int32 (emem, entry->timestamp);
563         if(ret) {
564             krb5_storage_free(emem);
565             goto out;
566         }
567         ret = krb5_store_int8 (emem, entry->vno % 256);
568         if(ret) {
569             krb5_storage_free(emem);
570             goto out;
571         }
572         ret = krb5_kt_store_keyblock (context, emem, &entry->keyblock);
573         if(ret) {
574             krb5_storage_free(emem);
575             goto out;
576         }
577         if ((d->flags & KRB5_KT_FL_JAVA) == 0) {
578             ret = krb5_store_int32 (emem, entry->vno);
579             if (ret) {
580                 krb5_storage_free(emem);
581                 goto out;
582             }
583         }
584
585         ret = krb5_storage_to_data(emem, &keytab);
586         krb5_storage_free(emem);
587         if(ret)
588             goto out;
589     }
590     
591     while(1) {
592         ret = krb5_ret_int32(sp, &len);
593         if(ret == KRB5_KT_END) {
594             len = keytab.length;
595             break;
596         }
597         if(len < 0) {
598             len = -len;
599             if(len >= keytab.length) {
600                 krb5_storage_seek(sp, -4, SEEK_CUR);
601                 break;
602             }
603         }
604         krb5_storage_seek(sp, len, SEEK_CUR);
605     }
606     ret = krb5_store_int32(sp, len);
607     if(krb5_storage_write(sp, keytab.data, keytab.length) < 0)
608         ret = errno;
609     memset(keytab.data, 0, keytab.length);
610     krb5_data_free(&keytab);
611   out:
612     krb5_storage_free(sp);
613     _krb5_xunlock(context, fd);
614     close(fd);
615     return ret;
616 }
617
618 static krb5_error_code
619 fkt_remove_entry(krb5_context context,
620                  krb5_keytab id,
621                  krb5_keytab_entry *entry)
622 {
623     krb5_keytab_entry e;
624     krb5_kt_cursor cursor;
625     off_t pos_start, pos_end;
626     int found = 0;
627     krb5_error_code ret;
628     
629     ret = fkt_start_seq_get_int(context, id, O_RDWR | O_BINARY, 1, &cursor);
630     if(ret != 0) 
631         goto out; /* return other error here? */
632     while(fkt_next_entry_int(context, id, &e, &cursor, 
633                              &pos_start, &pos_end) == 0) {
634         if(krb5_kt_compare(context, &e, entry->principal, 
635                            entry->vno, entry->keyblock.keytype)) {
636             int32_t len;
637             unsigned char buf[128];
638             found = 1;
639             krb5_storage_seek(cursor.sp, pos_start, SEEK_SET);
640             len = pos_end - pos_start - 4;
641             krb5_store_int32(cursor.sp, -len);
642             memset(buf, 0, sizeof(buf));
643             while(len > 0) {
644                 krb5_storage_write(cursor.sp, buf, min(len, sizeof(buf)));
645                 len -= min(len, sizeof(buf));
646             }
647         }
648         krb5_kt_free_entry(context, &e);
649     }
650     krb5_kt_end_seq_get(context, id, &cursor);
651   out:
652     if (!found) {
653         krb5_clear_error_string (context);
654         return KRB5_KT_NOTFOUND;
655     }
656     return 0;
657 }
658
659 const krb5_kt_ops krb5_fkt_ops = {
660     "FILE",
661     fkt_resolve,
662     fkt_get_name,
663     fkt_close,
664     NULL, /* get */
665     fkt_start_seq_get,
666     fkt_next_entry,
667     fkt_end_seq_get,
668     fkt_add_entry,
669     fkt_remove_entry
670 };
671
672 const krb5_kt_ops krb5_wrfkt_ops = {
673     "WRFILE",
674     fkt_resolve,
675     fkt_get_name,
676     fkt_close,
677     NULL, /* get */
678     fkt_start_seq_get,
679     fkt_next_entry,
680     fkt_end_seq_get,
681     fkt_add_entry,
682     fkt_remove_entry
683 };
684
685 const krb5_kt_ops krb5_javakt_ops = {
686     "JAVA14",
687     fkt_resolve_java14,
688     fkt_get_name,
689     fkt_close,
690     NULL, /* get */
691     fkt_start_seq_get,
692     fkt_next_entry,
693     fkt_end_seq_get,
694     fkt_add_entry,
695     fkt_remove_entry
696 };