xref: /freebsd/crypto/heimdal/lib/krb5/keytab_file.c (revision daf1cffce2e07931f27c6c6998652e90df6ba87e)
1 /*
2  * Copyright (c) 1997, 1998, 1999 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,v 1.6 2000/01/02 00:20:22 assar Exp $");
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 /* file operations -------------------------------------------- */
43 
44 struct fkt_data {
45     char *filename;
46 };
47 
48 static krb5_error_code
49 krb5_kt_ret_data(krb5_storage *sp,
50 		 krb5_data *data)
51 {
52     int ret;
53     int16_t size;
54     ret = krb5_ret_int16(sp, &size);
55     if(ret)
56 	return ret;
57     data->length = size;
58     data->data = malloc(size);
59     if (data->data == NULL)
60 	return ENOMEM;
61     ret = sp->fetch(sp, data->data, size);
62     if(ret != size)
63 	return (ret < 0)? errno : KRB5_KT_END;
64     return 0;
65 }
66 
67 static krb5_error_code
68 krb5_kt_ret_string(krb5_storage *sp,
69 		   general_string *data)
70 {
71     int ret;
72     int16_t size;
73     ret = krb5_ret_int16(sp, &size);
74     if(ret)
75 	return ret;
76     *data = malloc(size + 1);
77     if (*data == NULL)
78 	return ENOMEM;
79     ret = sp->fetch(sp, *data, size);
80     (*data)[size] = '\0';
81     if(ret != size)
82 	return (ret < 0)? errno : KRB5_KT_END;
83     return 0;
84 }
85 
86 static krb5_error_code
87 krb5_kt_store_data(krb5_storage *sp,
88 		   krb5_data data)
89 {
90     int ret;
91     ret = krb5_store_int16(sp, data.length);
92     if(ret < 0)
93 	return ret;
94     ret = sp->store(sp, data.data, data.length);
95     if(ret != data.length){
96 	if(ret < 0)
97 	    return errno;
98 	return KRB5_KT_END;
99     }
100     return 0;
101 }
102 
103 static krb5_error_code
104 krb5_kt_store_string(krb5_storage *sp,
105 		     general_string data)
106 {
107     int ret;
108     size_t len = strlen(data);
109     ret = krb5_store_int16(sp, len);
110     if(ret < 0)
111 	return ret;
112     ret = sp->store(sp, data, len);
113     if(ret != len){
114 	if(ret < 0)
115 	    return errno;
116 	return KRB5_KT_END;
117     }
118     return 0;
119 }
120 
121 static krb5_error_code
122 krb5_kt_ret_keyblock(krb5_storage *sp, krb5_keyblock *p)
123 {
124     int ret;
125     int16_t tmp;
126 
127     ret = krb5_ret_int16(sp, &tmp); /* keytype + etype */
128     if(ret) return ret;
129     p->keytype = tmp;
130     ret = krb5_kt_ret_data(sp, &p->keyvalue);
131     return ret;
132 }
133 
134 static krb5_error_code
135 krb5_kt_store_keyblock(krb5_storage *sp,
136 		       krb5_keyblock *p)
137 {
138     int ret;
139 
140     ret = krb5_store_int16(sp, p->keytype); /* keytype + etype */
141     if(ret) return ret;
142     ret = krb5_kt_store_data(sp, p->keyvalue);
143     return ret;
144 }
145 
146 
147 static krb5_error_code
148 krb5_kt_ret_principal(krb5_storage *sp,
149 		      krb5_principal *princ)
150 {
151     int i;
152     int ret;
153     krb5_principal p;
154     int16_t tmp;
155 
156     ALLOC(p, 1);
157     if(p == NULL)
158 	return ENOMEM;
159 
160     ret = krb5_ret_int16(sp, &tmp);
161     if(ret)
162 	return ret;
163     if (sp->flags & KRB5_STORAGE_PRINCIPAL_WRONG_NUM_COMPONENTS)
164 	tmp--;
165     p->name.name_string.len = tmp;
166     ret = krb5_kt_ret_string(sp, &p->realm);
167     if(ret) return ret;
168     p->name.name_string.val = calloc(p->name.name_string.len,
169 				     sizeof(*p->name.name_string.val));
170     if(p->name.name_string.val == NULL)
171 	return ENOMEM;
172     for(i = 0; i < p->name.name_string.len; i++){
173 	ret = krb5_kt_ret_string(sp, p->name.name_string.val + i);
174 	if(ret) return ret;
175     }
176     if (krb5_storage_is_flags(sp, KRB5_STORAGE_PRINCIPAL_NO_NAME_TYPE))
177 	p->name.name_type = KRB5_NT_UNKNOWN;
178     else {
179 	int32_t tmp32;
180 	ret = krb5_ret_int32(sp, &tmp32);
181 	p->name.name_type = tmp32;
182 	if (ret)
183 	    return ret;
184     }
185     *princ = p;
186     return 0;
187 }
188 
189 static krb5_error_code
190 krb5_kt_store_principal(krb5_storage *sp,
191 			krb5_principal p)
192 {
193     int i;
194     int ret;
195 
196     if(krb5_storage_is_flags(sp, KRB5_STORAGE_PRINCIPAL_WRONG_NUM_COMPONENTS))
197 	ret = krb5_store_int16(sp, p->name.name_string.len + 1);
198     else
199 	ret = krb5_store_int16(sp, p->name.name_string.len);
200     if(ret) return ret;
201     ret = krb5_kt_store_string(sp, p->realm);
202     if(ret) return ret;
203     for(i = 0; i < p->name.name_string.len; i++){
204 	ret = krb5_kt_store_string(sp, p->name.name_string.val[i]);
205 	if(ret) return ret;
206     }
207     if(!krb5_storage_is_flags(sp, KRB5_STORAGE_PRINCIPAL_NO_NAME_TYPE)) {
208 	ret = krb5_store_int32(sp, p->name.name_type);
209 	if(ret)
210 	    return ret;
211     }
212 
213     return 0;
214 }
215 
216 static krb5_error_code
217 fkt_resolve(krb5_context context, const char *name, krb5_keytab id)
218 {
219     struct fkt_data *d;
220     d = malloc(sizeof(*d));
221     if(d == NULL)
222 	return ENOMEM;
223     d->filename = strdup(name);
224     if(d->filename == NULL) {
225 	free(d);
226 	return ENOMEM;
227     }
228     id->data = d;
229     return 0;
230 }
231 
232 static krb5_error_code
233 fkt_close(krb5_context context, krb5_keytab id)
234 {
235     struct fkt_data *d = id->data;
236     free(d->filename);
237     free(d);
238     return 0;
239 }
240 
241 static krb5_error_code
242 fkt_get_name(krb5_context context,
243 	     krb5_keytab id,
244 	     char *name,
245 	     size_t namesize)
246 {
247     /* This function is XXX */
248     struct fkt_data *d = id->data;
249     strlcpy(name, d->filename, namesize);
250     return 0;
251 }
252 
253 static void
254 storage_set_flags(krb5_context context, krb5_storage *sp, int vno)
255 {
256     int flags = 0;
257     switch(vno) {
258     case KRB5_KT_VNO_1:
259 	flags |= KRB5_STORAGE_PRINCIPAL_WRONG_NUM_COMPONENTS;
260 	flags |= KRB5_STORAGE_PRINCIPAL_NO_NAME_TYPE;
261 	flags |= KRB5_STORAGE_HOST_BYTEORDER;
262 	break;
263     case KRB5_KT_VNO_2:
264 	break;
265     default:
266 	krb5_abortx(context,
267 		    "storage_set_flags called with bad vno (%x)", vno);
268     }
269     krb5_storage_set_flags(sp, flags);
270 }
271 
272 static krb5_error_code
273 fkt_start_seq_get_int(krb5_context context,
274 		      krb5_keytab id,
275 		      int flags,
276 		      krb5_kt_cursor *c)
277 {
278     int8_t pvno, tag;
279     krb5_error_code ret;
280     struct fkt_data *d = id->data;
281 
282     c->fd = open (d->filename, flags);
283     if (c->fd < 0)
284 	return errno;
285     c->sp = krb5_storage_from_fd(c->fd);
286     ret = krb5_ret_int8(c->sp, &pvno);
287     if(ret) {
288 	krb5_storage_free(c->sp);
289 	close(c->fd);
290 	return ret;
291     }
292     if(pvno != 5) {
293 	krb5_storage_free(c->sp);
294 	close(c->fd);
295 	return KRB5_KEYTAB_BADVNO;
296     }
297     ret = krb5_ret_int8(c->sp, &tag);
298     if (ret) {
299 	krb5_storage_free(c->sp);
300 	close(c->fd);
301 	return ret;
302     }
303     id->version = tag;
304     storage_set_flags(context, c->sp, id->version);
305     return 0;
306 }
307 
308 static krb5_error_code
309 fkt_start_seq_get(krb5_context context,
310 		  krb5_keytab id,
311 		  krb5_kt_cursor *c)
312 {
313     return fkt_start_seq_get_int(context, id, O_RDONLY | O_BINARY, c);
314 }
315 
316 static krb5_error_code
317 fkt_next_entry_int(krb5_context context,
318 		   krb5_keytab id,
319 		   krb5_keytab_entry *entry,
320 		   krb5_kt_cursor *cursor,
321 		   off_t *start,
322 		   off_t *end)
323 {
324     int32_t len;
325     int ret;
326     int8_t tmp8;
327     int32_t tmp32;
328     off_t pos;
329 
330     pos = cursor->sp->seek(cursor->sp, 0, SEEK_CUR);
331 loop:
332     ret = krb5_ret_int32(cursor->sp, &len);
333     if (ret)
334 	return ret;
335     if(len < 0) {
336 	pos = cursor->sp->seek(cursor->sp, -len, SEEK_CUR);
337 	goto loop;
338     }
339     ret = krb5_kt_ret_principal (cursor->sp, &entry->principal);
340     if (ret)
341 	goto out;
342     ret = krb5_ret_int32(cursor->sp, &tmp32);
343     entry->timestamp = tmp32;
344     if (ret)
345 	goto out;
346     ret = krb5_ret_int8(cursor->sp, &tmp8);
347     if (ret)
348 	goto out;
349     entry->vno = tmp8;
350     ret = krb5_kt_ret_keyblock (cursor->sp, &entry->keyblock);
351     if (ret)
352 	goto out;
353     if(start) *start = pos;
354     if(end) *end = *start + 4 + len;
355  out:
356     cursor->sp->seek(cursor->sp, pos + 4 + len, SEEK_SET);
357     return ret;
358 }
359 
360 static krb5_error_code
361 fkt_next_entry(krb5_context context,
362 	       krb5_keytab id,
363 	       krb5_keytab_entry *entry,
364 	       krb5_kt_cursor *cursor)
365 {
366     return fkt_next_entry_int(context, id, entry, cursor, NULL, NULL);
367 }
368 
369 static krb5_error_code
370 fkt_end_seq_get(krb5_context context,
371 		krb5_keytab id,
372 		krb5_kt_cursor *cursor)
373 {
374     krb5_storage_free(cursor->sp);
375     close(cursor->fd);
376     return 0;
377 }
378 
379 static krb5_error_code
380 fkt_add_entry(krb5_context context,
381 	      krb5_keytab id,
382 	      krb5_keytab_entry *entry)
383 {
384     int ret;
385     int fd;
386     krb5_storage *sp;
387     struct fkt_data *d = id->data;
388     krb5_data keytab;
389     int32_t len;
390 
391     fd = open (d->filename, O_RDWR | O_BINARY);
392     if (fd < 0) {
393 	fd = open (d->filename, O_RDWR | O_CREAT | O_BINARY, 0600);
394 	if (fd < 0)
395 	    return errno;
396 	sp = krb5_storage_from_fd(fd);
397 	ret = krb5_store_int8(sp, 5);
398 	if(ret) {
399 	    krb5_storage_free(sp);
400 	    close(fd);
401 	    return ret;
402 	}
403 	if(id->version == 0)
404 	    id->version = KRB5_KT_VNO;
405 	ret = krb5_store_int8 (sp, id->version);
406 	if (ret) {
407 	    krb5_storage_free(sp);
408 	    close(fd);
409 	    return ret;
410 	}
411 	storage_set_flags(context, sp, id->version);
412     } else {
413 	int8_t pvno, tag;
414 	sp = krb5_storage_from_fd(fd);
415 	ret = krb5_ret_int8(sp, &pvno);
416 	if(ret) {
417 	    krb5_storage_free(sp);
418 	    close(fd);
419 	    return ret;
420 	}
421 	if(pvno != 5) {
422 	    krb5_storage_free(sp);
423 	    close(fd);
424 	    return KRB5_KEYTAB_BADVNO;
425 	}
426 	ret = krb5_ret_int8 (sp, &tag);
427 	if (ret) {
428 	    krb5_storage_free(sp);
429 	    close(fd);
430 	    return ret;
431 	}
432 	id->version = tag;
433 	storage_set_flags(context, sp, id->version);
434     }
435 
436     {
437 	krb5_storage *emem;
438 	emem = krb5_storage_emem();
439 	if(emem == NULL) {
440 	    ret = ENOMEM;
441 	    goto out;
442 	}
443 	ret = krb5_kt_store_principal(emem, entry->principal);
444 	if(ret) {
445 	    krb5_storage_free(emem);
446 	    goto out;
447 	}
448 	ret = krb5_store_int32 (emem, entry->timestamp);
449 	if(ret) {
450 	    krb5_storage_free(emem);
451 	    goto out;
452 	}
453 	ret = krb5_store_int8 (emem, entry->vno);
454 	if(ret) {
455 	    krb5_storage_free(emem);
456 	    goto out;
457 	}
458 	ret = krb5_kt_store_keyblock (emem, &entry->keyblock);
459 	if(ret) {
460 	    krb5_storage_free(emem);
461 	    goto out;
462 	}
463 	ret = krb5_storage_to_data(emem, &keytab);
464 	krb5_storage_free(emem);
465 	if(ret)
466 	    goto out;
467     }
468 
469     while(1) {
470 	ret = krb5_ret_int32(sp, &len);
471 	if(ret == KRB5_CC_END) {
472 	    len = keytab.length;
473 	    break;
474 	}
475 	if(len < 0) {
476 	    len = -len;
477 	    if(len >= keytab.length) {
478 		sp->seek(sp, -4, SEEK_CUR);
479 		break;
480 	    }
481 	}
482 	sp->seek(sp, len, SEEK_CUR);
483     }
484     ret = krb5_store_int32(sp, len);
485     if(sp->store(sp, keytab.data, keytab.length) < 0)
486 	ret = errno;
487     memset(keytab.data, 0, keytab.length);
488     krb5_data_free(&keytab);
489  out:
490     krb5_storage_free(sp);
491     close(fd);
492     return ret;
493 }
494 
495 static krb5_error_code
496 fkt_remove_entry(krb5_context context,
497 		 krb5_keytab id,
498 		 krb5_keytab_entry *entry)
499 {
500     krb5_keytab_entry e;
501     krb5_kt_cursor cursor;
502     off_t pos_start, pos_end;
503     int found = 0;
504 
505     fkt_start_seq_get_int(context, id, O_RDWR | O_BINARY, &cursor);
506     while(fkt_next_entry_int(context, id, &e, &cursor,
507 			     &pos_start, &pos_end) == 0) {
508 	if(krb5_kt_compare(context, &e, entry->principal,
509 			   entry->vno, entry->keyblock.keytype)) {
510 	    int32_t len;
511 	    unsigned char buf[128];
512 	    found = 1;
513 	    cursor.sp->seek(cursor.sp, pos_start, SEEK_SET);
514 	    len = pos_end - pos_start - 4;
515 	    krb5_store_int32(cursor.sp, -len);
516 	    memset(buf, 0, sizeof(buf));
517 	    while(len > 0) {
518 		cursor.sp->store(cursor.sp, buf, min(len, sizeof(buf)));
519 		len -= min(len, sizeof(buf));
520 	    }
521 	}
522     }
523     krb5_kt_end_seq_get(context, id, &cursor);
524     if (!found)
525 	return KRB5_KT_NOTFOUND;
526     return 0;
527 }
528 
529 const krb5_kt_ops krb5_fkt_ops = {
530     "FILE",
531     fkt_resolve,
532     fkt_get_name,
533     fkt_close,
534     NULL, /* get */
535     fkt_start_seq_get,
536     fkt_next_entry,
537     fkt_end_seq_get,
538     fkt_add_entry,
539     fkt_remove_entry
540 };
541