1 /*	$NetBSD: keytab_keyfile.c,v 1.1.1.2 2014/04/24 12:45:50 pettai Exp $	*/
2 
3 /*
4  * Copyright (c) 1997 - 2007 Kungliga Tekniska Högskolan
5  * (Royal Institute of Technology, Stockholm, Sweden).
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  *
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  *
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  *
19  * 3. Neither the name of the Institute nor the names of its contributors
20  *    may be used to endorse or promote products derived from this software
21  *    without specific prior written permission.
22  *
23  * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
24  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26  * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
27  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33  * SUCH DAMAGE.
34  */
35 
36 #include "krb5_locl.h"
37 
38 #ifndef HEIMDAL_SMALLER
39 
40 /* afs keyfile operations --------------------------------------- */
41 
42 /*
43  * Minimum tools to handle the AFS KeyFile.
44  *
45  * Format of the KeyFile is:
46  * <int32_t numkeys> {[<int32_t kvno> <char[8] deskey>] * numkeys}
47  *
48  * It just adds to the end of the keyfile, deleting isn't implemented.
49  * Use your favorite text/hex editor to delete keys.
50  *
51  */
52 
53 #define AFS_SERVERTHISCELL "/usr/afs/etc/ThisCell"
54 #define AFS_SERVERMAGICKRBCONF "/usr/afs/etc/krb.conf"
55 
56 struct akf_data {
57     uint32_t num_entries;
58     char *filename;
59     char *cell;
60     char *realm;
61 };
62 
63 /*
64  * set `d->cell' and `d->realm'
65  */
66 
67 static int
68 get_cell_and_realm (krb5_context context, struct akf_data *d)
69 {
70     FILE *f;
71     char buf[BUFSIZ], *cp;
72     int ret;
73 
74     f = fopen (AFS_SERVERTHISCELL, "r");
75     if (f == NULL) {
76 	ret = errno;
77 	krb5_set_error_message (context, ret,
78 				N_("Open ThisCell %s: %s", ""),
79 				AFS_SERVERTHISCELL,
80 				strerror(ret));
81 	return ret;
82     }
83     if (fgets (buf, sizeof(buf), f) == NULL) {
84 	fclose (f);
85 	krb5_set_error_message (context, EINVAL,
86 				N_("No cell in ThisCell file %s", ""),
87 				AFS_SERVERTHISCELL);
88 	return EINVAL;
89     }
90     buf[strcspn(buf, "\n")] = '\0';
91     fclose(f);
92 
93     d->cell = strdup (buf);
94     if (d->cell == NULL) {
95 	krb5_set_error_message(context, ENOMEM,
96 			       N_("malloc: out of memory", ""));
97 	return ENOMEM;
98     }
99 
100     f = fopen (AFS_SERVERMAGICKRBCONF, "r");
101     if (f != NULL) {
102 	if (fgets (buf, sizeof(buf), f) == NULL) {
103 	    free (d->cell);
104 	    d->cell = NULL;
105 	    fclose (f);
106 	    krb5_set_error_message (context, EINVAL,
107 				    N_("No realm in ThisCell file %s", ""),
108 				    AFS_SERVERMAGICKRBCONF);
109 	    return EINVAL;
110 	}
111 	buf[strcspn(buf, "\n")] = '\0';
112 	fclose(f);
113     }
114     /* uppercase */
115     for (cp = buf; *cp != '\0'; cp++)
116 	*cp = toupper((unsigned char)*cp);
117 
118     d->realm = strdup (buf);
119     if (d->realm == NULL) {
120 	free (d->cell);
121 	d->cell = NULL;
122 	krb5_set_error_message(context, ENOMEM,
123 			       N_("malloc: out of memory", ""));
124 	return ENOMEM;
125     }
126     return 0;
127 }
128 
129 /*
130  * init and get filename
131  */
132 
133 static krb5_error_code KRB5_CALLCONV
134 akf_resolve(krb5_context context, const char *name, krb5_keytab id)
135 {
136     int ret;
137     struct akf_data *d = malloc(sizeof (struct akf_data));
138 
139     if (d == NULL) {
140 	krb5_set_error_message(context, ENOMEM,
141 			       N_("malloc: out of memory", ""));
142 	return ENOMEM;
143     }
144 
145     d->num_entries = 0;
146     ret = get_cell_and_realm (context, d);
147     if (ret) {
148 	free (d);
149 	return ret;
150     }
151     d->filename = strdup (name);
152     if (d->filename == NULL) {
153 	free (d->cell);
154 	free (d->realm);
155 	free (d);
156 	krb5_set_error_message(context, ENOMEM,
157 			       N_("malloc: out of memory", ""));
158 	return ENOMEM;
159     }
160     id->data = d;
161 
162     return 0;
163 }
164 
165 /*
166  * cleanup
167  */
168 
169 static krb5_error_code KRB5_CALLCONV
170 akf_close(krb5_context context, krb5_keytab id)
171 {
172     struct akf_data *d = id->data;
173 
174     free (d->filename);
175     free (d->cell);
176     free (d);
177     return 0;
178 }
179 
180 /*
181  * Return filename
182  */
183 
184 static krb5_error_code KRB5_CALLCONV
185 akf_get_name(krb5_context context,
186 	     krb5_keytab id,
187 	     char *name,
188 	     size_t name_sz)
189 {
190     struct akf_data *d = id->data;
191 
192     strlcpy (name, d->filename, name_sz);
193     return 0;
194 }
195 
196 /*
197  * Init
198  */
199 
200 static krb5_error_code KRB5_CALLCONV
201 akf_start_seq_get(krb5_context context,
202 		  krb5_keytab id,
203 		  krb5_kt_cursor *c)
204 {
205     int32_t ret;
206     struct akf_data *d = id->data;
207 
208     c->fd = open (d->filename, O_RDONLY | O_BINARY | O_CLOEXEC, 0600);
209     if (c->fd < 0) {
210 	ret = errno;
211 	krb5_set_error_message(context, ret,
212 			       N_("keytab afs keyfile open %s failed: %s", ""),
213 			       d->filename, strerror(ret));
214 	return ret;
215     }
216 
217     c->data = NULL;
218     c->sp = krb5_storage_from_fd(c->fd);
219     if (c->sp == NULL) {
220 	close(c->fd);
221 	krb5_clear_error_message (context);
222 	return KRB5_KT_NOTFOUND;
223     }
224     krb5_storage_set_eof_code(c->sp, KRB5_KT_END);
225 
226     ret = krb5_ret_uint32(c->sp, &d->num_entries);
227     if(ret || d->num_entries > INT_MAX / 8) {
228 	krb5_storage_free(c->sp);
229 	close(c->fd);
230 	krb5_clear_error_message (context);
231 	if(ret == KRB5_KT_END)
232 	    return KRB5_KT_NOTFOUND;
233 	return ret;
234     }
235 
236     return 0;
237 }
238 
239 static krb5_error_code KRB5_CALLCONV
240 akf_next_entry(krb5_context context,
241 	       krb5_keytab id,
242 	       krb5_keytab_entry *entry,
243 	       krb5_kt_cursor *cursor)
244 {
245     struct akf_data *d = id->data;
246     int32_t kvno;
247     off_t pos;
248     int ret;
249 
250     pos = krb5_storage_seek(cursor->sp, 0, SEEK_CUR);
251 
252     if ((pos - 4) / (4 + 8) >= d->num_entries)
253 	return KRB5_KT_END;
254 
255     ret = krb5_make_principal (context, &entry->principal,
256 			       d->realm, "afs", d->cell, NULL);
257     if (ret)
258 	goto out;
259 
260     ret = krb5_ret_int32(cursor->sp, &kvno);
261     if (ret) {
262 	krb5_free_principal (context, entry->principal);
263 	goto out;
264     }
265 
266     entry->vno = kvno;
267 
268     if (cursor->data)
269 	entry->keyblock.keytype         = ETYPE_DES_CBC_MD5;
270     else
271 	entry->keyblock.keytype         = ETYPE_DES_CBC_CRC;
272     entry->keyblock.keyvalue.length = 8;
273     entry->keyblock.keyvalue.data   = malloc (8);
274     if (entry->keyblock.keyvalue.data == NULL) {
275 	krb5_free_principal (context, entry->principal);
276 	krb5_set_error_message(context, ENOMEM,
277 			       N_("malloc: out of memory", ""));
278 	ret = ENOMEM;
279 	goto out;
280     }
281 
282     ret = krb5_storage_read(cursor->sp, entry->keyblock.keyvalue.data, 8);
283     if(ret != 8)
284 	ret = (ret < 0) ? errno : KRB5_KT_END;
285     else
286 	ret = 0;
287 
288     entry->timestamp = time(NULL);
289     entry->flags = 0;
290     entry->aliases = NULL;
291 
292  out:
293     if (cursor->data) {
294 	krb5_storage_seek(cursor->sp, pos + 4 + 8, SEEK_SET);
295 	cursor->data = NULL;
296     } else
297 	cursor->data = cursor;
298     return ret;
299 }
300 
301 static krb5_error_code KRB5_CALLCONV
302 akf_end_seq_get(krb5_context context,
303 		krb5_keytab id,
304 		krb5_kt_cursor *cursor)
305 {
306     krb5_storage_free(cursor->sp);
307     close(cursor->fd);
308     cursor->data = NULL;
309     return 0;
310 }
311 
312 static krb5_error_code KRB5_CALLCONV
313 akf_add_entry(krb5_context context,
314 	      krb5_keytab id,
315 	      krb5_keytab_entry *entry)
316 {
317     struct akf_data *d = id->data;
318     int fd, created = 0;
319     krb5_error_code ret;
320     int32_t len;
321     krb5_storage *sp;
322 
323 
324     if (entry->keyblock.keyvalue.length != 8)
325 	return 0;
326     switch(entry->keyblock.keytype) {
327     case ETYPE_DES_CBC_CRC:
328     case ETYPE_DES_CBC_MD4:
329     case ETYPE_DES_CBC_MD5:
330 	break;
331     default:
332 	return 0;
333     }
334 
335     fd = open (d->filename, O_RDWR | O_BINARY | O_CLOEXEC);
336     if (fd < 0) {
337 	fd = open (d->filename,
338 		   O_RDWR | O_BINARY | O_CREAT | O_EXCL | O_CLOEXEC, 0600);
339 	if (fd < 0) {
340 	    ret = errno;
341 	    krb5_set_error_message(context, ret,
342 				   N_("open keyfile(%s): %s", ""),
343 				   d->filename,
344 				   strerror(ret));
345 	    return ret;
346 	}
347 	created = 1;
348     }
349 
350     sp = krb5_storage_from_fd(fd);
351     if(sp == NULL) {
352 	close(fd);
353 	krb5_set_error_message(context, ENOMEM,
354 			       N_("malloc: out of memory", ""));
355 	return ENOMEM;
356     }
357     if (created)
358 	len = 0;
359     else {
360 	if(krb5_storage_seek(sp, 0, SEEK_SET) < 0) {
361 	    ret = errno;
362 	    krb5_storage_free(sp);
363 	    close(fd);
364 	    krb5_set_error_message(context, ret,
365 				   N_("seeking in keyfile: %s", ""),
366 				   strerror(ret));
367 	    return ret;
368 	}
369 
370 	ret = krb5_ret_int32(sp, &len);
371 	if(ret) {
372 	    krb5_storage_free(sp);
373 	    close(fd);
374 	    return ret;
375 	}
376     }
377 
378     /*
379      * Make sure we don't add the entry twice, assumes the DES
380      * encryption types are all the same key.
381      */
382     if (len > 0) {
383 	int32_t kvno;
384 	int i;
385 
386 	for (i = 0; i < len; i++) {
387 	    ret = krb5_ret_int32(sp, &kvno);
388 	    if (ret) {
389 		krb5_set_error_message (context, ret,
390 					N_("Failed getting kvno from keyfile", ""));
391 		goto out;
392 	    }
393 	    if(krb5_storage_seek(sp, 8, SEEK_CUR) < 0) {
394 		ret = errno;
395 		krb5_set_error_message (context, ret,
396 					N_("Failed seeing in keyfile: %s", ""),
397 					strerror(ret));
398 		goto out;
399 	    }
400 	    if (kvno == entry->vno) {
401 		ret = 0;
402 		goto out;
403 	    }
404 	}
405     }
406 
407     len++;
408 
409     if(krb5_storage_seek(sp, 0, SEEK_SET) < 0) {
410 	ret = errno;
411 	krb5_set_error_message (context, ret,
412 				N_("Failed seeing in keyfile: %s", ""),
413 				strerror(ret));
414 	goto out;
415     }
416 
417     ret = krb5_store_int32(sp, len);
418     if(ret) {
419 	ret = errno;
420 	krb5_set_error_message (context, ret,
421 				N_("keytab keyfile failed new length", ""));
422 	return ret;
423     }
424 
425     if(krb5_storage_seek(sp, (len - 1) * (8 + 4), SEEK_CUR) < 0) {
426 	ret = errno;
427 	krb5_set_error_message (context, ret,
428 				N_("seek to end: %s", ""), strerror(ret));
429 	goto out;
430     }
431 
432     ret = krb5_store_int32(sp, entry->vno);
433     if(ret) {
434 	krb5_set_error_message(context, ret,
435 			       N_("keytab keyfile failed store kvno", ""));
436 	goto out;
437     }
438     ret = krb5_storage_write(sp, entry->keyblock.keyvalue.data,
439 			     entry->keyblock.keyvalue.length);
440     if(ret != entry->keyblock.keyvalue.length) {
441 	if (ret < 0)
442 	    ret = errno;
443 	else
444 	    ret = ENOTTY;
445 	krb5_set_error_message(context, ret,
446 			       N_("keytab keyfile failed to add key", ""));
447 	goto out;
448     }
449     ret = 0;
450 out:
451     krb5_storage_free(sp);
452     close (fd);
453     return ret;
454 }
455 
456 const krb5_kt_ops krb5_akf_ops = {
457     "AFSKEYFILE",
458     akf_resolve,
459     akf_get_name,
460     akf_close,
461     NULL, /* destroy */
462     NULL, /* get */
463     akf_start_seq_get,
464     akf_next_entry,
465     akf_end_seq_get,
466     akf_add_entry,
467     NULL /* remove */
468 };
469 
470 #endif /* HEIMDAL_SMALLER */
471