1 /*	$NetBSD: keytab_keyfile.c,v 1.1.1.1 2011/04/13 18:15:34 elric 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->sp = krb5_storage_from_fd(c->fd);
218     ret = krb5_ret_uint32(c->sp, &d->num_entries);
219     if(ret) {
220 	krb5_storage_free(c->sp);
221 	close(c->fd);
222 	krb5_clear_error_message (context);
223 	if(ret == KRB5_KT_END)
224 	    return KRB5_KT_NOTFOUND;
225 	return ret;
226     }
227 
228     return 0;
229 }
230 
231 static krb5_error_code KRB5_CALLCONV
232 akf_next_entry(krb5_context context,
233 	       krb5_keytab id,
234 	       krb5_keytab_entry *entry,
235 	       krb5_kt_cursor *cursor)
236 {
237     struct akf_data *d = id->data;
238     int32_t kvno;
239     off_t pos;
240     int ret;
241 
242     pos = krb5_storage_seek(cursor->sp, 0, SEEK_CUR);
243 
244     if ((pos - 4) / (4 + 8) >= d->num_entries)
245 	return KRB5_KT_END;
246 
247     ret = krb5_make_principal (context, &entry->principal,
248 			       d->realm, "afs", d->cell, NULL);
249     if (ret)
250 	goto out;
251 
252     ret = krb5_ret_int32(cursor->sp, &kvno);
253     if (ret) {
254 	krb5_free_principal (context, entry->principal);
255 	goto out;
256     }
257 
258     entry->vno = kvno;
259 
260     entry->keyblock.keytype         = ETYPE_DES_CBC_MD5;
261     entry->keyblock.keyvalue.length = 8;
262     entry->keyblock.keyvalue.data   = malloc (8);
263     if (entry->keyblock.keyvalue.data == NULL) {
264 	krb5_free_principal (context, entry->principal);
265 	krb5_set_error_message(context, ENOMEM,
266 			       N_("malloc: out of memory", ""));
267 	ret = ENOMEM;
268 	goto out;
269     }
270 
271     ret = krb5_storage_read(cursor->sp, entry->keyblock.keyvalue.data, 8);
272     if(ret != 8)
273 	ret = (ret < 0) ? errno : KRB5_KT_END;
274     else
275 	ret = 0;
276 
277     entry->timestamp = time(NULL);
278     entry->flags = 0;
279     entry->aliases = NULL;
280 
281  out:
282     krb5_storage_seek(cursor->sp, pos + 4 + 8, SEEK_SET);
283     return ret;
284 }
285 
286 static krb5_error_code KRB5_CALLCONV
287 akf_end_seq_get(krb5_context context,
288 		krb5_keytab id,
289 		krb5_kt_cursor *cursor)
290 {
291     krb5_storage_free(cursor->sp);
292     close(cursor->fd);
293     return 0;
294 }
295 
296 static krb5_error_code KRB5_CALLCONV
297 akf_add_entry(krb5_context context,
298 	      krb5_keytab id,
299 	      krb5_keytab_entry *entry)
300 {
301     struct akf_data *d = id->data;
302     int fd, created = 0;
303     krb5_error_code ret;
304     int32_t len;
305     krb5_storage *sp;
306 
307 
308     if (entry->keyblock.keyvalue.length != 8)
309 	return 0;
310     switch(entry->keyblock.keytype) {
311     case ETYPE_DES_CBC_CRC:
312     case ETYPE_DES_CBC_MD4:
313     case ETYPE_DES_CBC_MD5:
314 	break;
315     default:
316 	return 0;
317     }
318 
319     fd = open (d->filename, O_RDWR | O_BINARY | O_CLOEXEC);
320     if (fd < 0) {
321 	fd = open (d->filename,
322 		   O_RDWR | O_BINARY | O_CREAT | O_EXCL | O_CLOEXEC, 0600);
323 	if (fd < 0) {
324 	    ret = errno;
325 	    krb5_set_error_message(context, ret,
326 				   N_("open keyfile(%s): %s", ""),
327 				   d->filename,
328 				   strerror(ret));
329 	    return ret;
330 	}
331 	created = 1;
332     }
333 
334     sp = krb5_storage_from_fd(fd);
335     if(sp == NULL) {
336 	close(fd);
337 	krb5_set_error_message(context, ENOMEM,
338 			       N_("malloc: out of memory", ""));
339 	return ENOMEM;
340     }
341     if (created)
342 	len = 0;
343     else {
344 	if(krb5_storage_seek(sp, 0, SEEK_SET) < 0) {
345 	    ret = errno;
346 	    krb5_storage_free(sp);
347 	    close(fd);
348 	    krb5_set_error_message(context, ret,
349 				   N_("seeking in keyfile: %s", ""),
350 				   strerror(ret));
351 	    return ret;
352 	}
353 
354 	ret = krb5_ret_int32(sp, &len);
355 	if(ret) {
356 	    krb5_storage_free(sp);
357 	    close(fd);
358 	    return ret;
359 	}
360     }
361 
362     /*
363      * Make sure we don't add the entry twice, assumes the DES
364      * encryption types are all the same key.
365      */
366     if (len > 0) {
367 	int32_t kvno;
368 	int i;
369 
370 	for (i = 0; i < len; i++) {
371 	    ret = krb5_ret_int32(sp, &kvno);
372 	    if (ret) {
373 		krb5_set_error_message (context, ret,
374 					N_("Failed getting kvno from keyfile", ""));
375 		goto out;
376 	    }
377 	    if(krb5_storage_seek(sp, 8, SEEK_CUR) < 0) {
378 		ret = errno;
379 		krb5_set_error_message (context, ret,
380 					N_("Failed seeing in keyfile: %s", ""),
381 					strerror(ret));
382 		goto out;
383 	    }
384 	    if (kvno == entry->vno) {
385 		ret = 0;
386 		goto out;
387 	    }
388 	}
389     }
390 
391     len++;
392 
393     if(krb5_storage_seek(sp, 0, SEEK_SET) < 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 
401     ret = krb5_store_int32(sp, len);
402     if(ret) {
403 	ret = errno;
404 	krb5_set_error_message (context, ret,
405 				N_("keytab keyfile failed new length", ""));
406 	return ret;
407     }
408 
409     if(krb5_storage_seek(sp, (len - 1) * (8 + 4), SEEK_CUR) < 0) {
410 	ret = errno;
411 	krb5_set_error_message (context, ret,
412 				N_("seek to end: %s", ""), strerror(ret));
413 	goto out;
414     }
415 
416     ret = krb5_store_int32(sp, entry->vno);
417     if(ret) {
418 	krb5_set_error_message(context, ret,
419 			       N_("keytab keyfile failed store kvno", ""));
420 	goto out;
421     }
422     ret = krb5_storage_write(sp, entry->keyblock.keyvalue.data,
423 			     entry->keyblock.keyvalue.length);
424     if(ret != entry->keyblock.keyvalue.length) {
425 	if (ret < 0)
426 	    ret = errno;
427 	else
428 	    ret = ENOTTY;
429 	krb5_set_error_message(context, ret,
430 			       N_("keytab keyfile failed to add key", ""));
431 	goto out;
432     }
433     ret = 0;
434 out:
435     krb5_storage_free(sp);
436     close (fd);
437     return ret;
438 }
439 
440 const krb5_kt_ops krb5_akf_ops = {
441     "AFSKEYFILE",
442     akf_resolve,
443     akf_get_name,
444     akf_close,
445     NULL, /* destroy */
446     NULL, /* get */
447     akf_start_seq_get,
448     akf_next_entry,
449     akf_end_seq_get,
450     akf_add_entry,
451     NULL /* remove */
452 };
453 
454 #endif /* HEIMDAL_SMALLER */
455