keytab_keyfile.c revision 78527
1/*
2 * Copyright (c) 1997 - 2001 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
36RCSID("$Id: keytab_keyfile.c,v 1.11 2001/05/14 06:14:49 assar Exp $");
37
38/* afs keyfile operations --------------------------------------- */
39
40/*
41 * Minimum tools to handle the AFS KeyFile.
42 *
43 * Format of the KeyFile is:
44 * <int32_t numkeys> {[<int32_t kvno> <char[8] deskey>] * numkeys}
45 *
46 * It just adds to the end of the keyfile, deleting isn't implemented.
47 * Use your favorite text/hex editor to delete keys.
48 *
49 */
50
51#define AFS_SERVERTHISCELL "/usr/afs/etc/ThisCell"
52#define AFS_SERVERMAGICKRBCONF "/usr/afs/etc/krb.conf"
53
54struct akf_data {
55    int num_entries;
56    char *filename;
57    char *cell;
58    char *realm;
59};
60
61/*
62 * set `d->cell' and `d->realm'
63 */
64
65static int
66get_cell_and_realm (krb5_context context,
67		    struct akf_data *d)
68{
69    FILE *f;
70    char buf[BUFSIZ], *cp;
71    int ret;
72
73    f = fopen (AFS_SERVERTHISCELL, "r");
74    if (f == NULL) {
75	ret = errno;
76	krb5_set_error_string (context, "open %s: %s", AFS_SERVERTHISCELL,
77			       strerror(ret));
78	return ret;
79    }
80    if (fgets (buf, sizeof(buf), f) == NULL) {
81	fclose (f);
82	krb5_set_error_string (context, "no cell in %s", AFS_SERVERTHISCELL);
83	return EINVAL;
84    }
85    if (buf[strlen(buf) - 1] == '\n')
86	buf[strlen(buf) - 1] = '\0';
87    fclose(f);
88
89    d->cell = strdup (buf);
90    if (d->cell == NULL) {
91	krb5_set_error_string (context, "malloc: out of memory");
92	return ENOMEM;
93    }
94
95    f = fopen (AFS_SERVERMAGICKRBCONF, "r");
96    if (f != NULL) {
97	if (fgets (buf, sizeof(buf), f) == NULL) {
98	    fclose (f);
99	    krb5_set_error_string (context, "no realm in %s",
100				   AFS_SERVERMAGICKRBCONF);
101	    return EINVAL;
102	}
103	if (buf[strlen(buf)-1] == '\n')
104	    buf[strlen(buf)-1] = '\0';
105	fclose(f);
106    }
107    /* uppercase */
108    for (cp = buf; *cp != '\0'; cp++)
109	*cp = toupper(*cp);
110
111    d->realm = strdup (buf);
112    if (d->realm == NULL) {
113	free (d->cell);
114	krb5_set_error_string (context, "malloc: out of memory");
115	return ENOMEM;
116    }
117    return 0;
118}
119
120/*
121 * init and get filename
122 */
123
124static krb5_error_code
125akf_resolve(krb5_context context, const char *name, krb5_keytab id)
126{
127    int ret;
128    struct akf_data *d = malloc(sizeof (struct akf_data));
129
130    if (d == NULL) {
131	krb5_set_error_string (context, "malloc: out of memory");
132	return ENOMEM;
133    }
134
135    d->num_entries = 0;
136    ret = get_cell_and_realm (context, d);
137    if (ret) {
138	free (d);
139	return ret;
140    }
141    d->filename = strdup (name);
142    if (d->filename == NULL) {
143	free (d->cell);
144	free (d->realm);
145	free (d);
146	krb5_set_error_string (context, "malloc: out of memory");
147	return ENOMEM;
148    }
149    id->data = d;
150
151    return 0;
152}
153
154/*
155 * cleanup
156 */
157
158static krb5_error_code
159akf_close(krb5_context context, krb5_keytab id)
160{
161    struct akf_data *d = id->data;
162
163    free (d->filename);
164    free (d->cell);
165    free (d);
166    return 0;
167}
168
169/*
170 * Return filename
171 */
172
173static krb5_error_code
174akf_get_name(krb5_context context,
175	     krb5_keytab id,
176	     char *name,
177	     size_t name_sz)
178{
179    struct akf_data *d = id->data;
180
181    strlcpy (name, d->filename, name_sz);
182    return 0;
183}
184
185/*
186 * Init
187 */
188
189static krb5_error_code
190akf_start_seq_get(krb5_context context,
191		  krb5_keytab id,
192		  krb5_kt_cursor *c)
193{
194    int32_t ret;
195    struct akf_data *d = id->data;
196
197    c->fd = open (d->filename, O_RDONLY|O_BINARY, 0600);
198    if (c->fd < 0) {
199	ret = errno;
200	krb5_set_error_string(context, "open(%s): %s", d->filename,
201			      strerror(ret));
202	return ret;
203    }
204
205    c->sp = krb5_storage_from_fd(c->fd);
206    ret = krb5_ret_int32(c->sp, &d->num_entries);
207    if(ret) {
208	krb5_storage_free(c->sp);
209	close(c->fd);
210	krb5_clear_error_string (context);
211	if(ret == KRB5_CC_END)
212	    return KRB5_KT_NOTFOUND;
213	return ret;
214    }
215
216    return 0;
217}
218
219static krb5_error_code
220akf_next_entry(krb5_context context,
221	       krb5_keytab id,
222	       krb5_keytab_entry *entry,
223	       krb5_kt_cursor *cursor)
224{
225    struct akf_data *d = id->data;
226    int32_t kvno;
227    off_t pos;
228    int ret;
229
230    pos = cursor->sp->seek(cursor->sp, 0, SEEK_CUR);
231
232    if ((pos - 4) / (4 + 8) >= d->num_entries)
233	return KRB5_KT_END;
234
235    ret = krb5_make_principal (context, &entry->principal,
236			       d->realm, "afs", d->cell, NULL);
237    if (ret)
238	goto out;
239
240    ret = krb5_ret_int32(cursor->sp, &kvno);
241    if (ret) {
242	krb5_free_principal (context, entry->principal);
243	goto out;
244    }
245
246    entry->vno = kvno;
247
248    entry->keyblock.keytype         = ETYPE_DES_CBC_MD5;
249    entry->keyblock.keyvalue.length = 8;
250    entry->keyblock.keyvalue.data   = malloc (8);
251    if (entry->keyblock.keyvalue.data == NULL) {
252	krb5_free_principal (context, entry->principal);
253	krb5_set_error_string (context, "malloc: out of memory");
254	ret = ENOMEM;
255	goto out;
256    }
257
258    ret = cursor->sp->fetch(cursor->sp, entry->keyblock.keyvalue.data, 8);
259    if(ret != 8)
260	ret = (ret < 0) ? errno : KRB5_KT_END;
261    else
262	ret = 0;
263
264    entry->timestamp = time(NULL);
265
266 out:
267    cursor->sp->seek(cursor->sp, pos + 4 + 8, SEEK_SET);
268    return ret;
269}
270
271static krb5_error_code
272akf_end_seq_get(krb5_context context,
273		krb5_keytab id,
274		krb5_kt_cursor *cursor)
275{
276    krb5_storage_free(cursor->sp);
277    close(cursor->fd);
278    return 0;
279}
280
281static krb5_error_code
282akf_add_entry(krb5_context context,
283	      krb5_keytab id,
284	      krb5_keytab_entry *entry)
285{
286    struct akf_data *d = id->data;
287    int fd, created = 0;
288    krb5_error_code ret;
289
290    fd = open (d->filename, O_RDWR | O_BINARY);
291    if (fd < 0) {
292	fd = open (d->filename,
293		   O_RDWR | O_BINARY | O_CREAT, 0600);
294	if (fd < 0) {
295	    ret = errno;
296	    krb5_set_error_string(context, "open(%s): %s", d->filename,
297				  strerror(ret));
298	    return ret;
299	}
300	created = 1;
301    }
302
303    if (entry->keyblock.keyvalue.length == 8
304	&& entry->keyblock.keytype == ETYPE_DES_CBC_MD5) {
305
306	int32_t len;
307	krb5_storage *sp;
308
309	sp = krb5_storage_from_fd(fd);
310	if(sp == NULL) {
311	    close(fd);
312	    krb5_set_error_string (context, "malloc: out of memory");
313	    return ENOMEM;
314	}
315	if (created)
316	    len = 0;
317	else {
318	    if((*sp->seek)(sp, 0, SEEK_SET) < 0) {
319		ret = errno;
320		krb5_storage_free(sp);
321		close(fd);
322		krb5_set_error_string (context, "seek: %s", strerror(ret));
323		return ret;
324	    }
325
326	    ret = krb5_ret_int32(sp, &len);
327	    if(ret) {
328		krb5_storage_free(sp);
329		close(fd);
330		return ret;
331	    }
332	}
333	len++;
334
335	if((*sp->seek)(sp, 0, SEEK_SET) < 0) {
336	    ret = errno;
337	    krb5_storage_free(sp);
338	    close(fd);
339	    krb5_set_error_string (context, "seek: %s", strerror(ret));
340	    return ret;
341	}
342
343	ret = krb5_store_int32(sp, len);
344	if(ret) {
345	    krb5_storage_free(sp);
346	    close(fd);
347	    return ret;
348	}
349
350
351	if((*sp->seek)(sp, (len - 1) * (8 + 4), SEEK_CUR) < 0) {
352	    ret = errno;
353	    krb5_storage_free(sp);
354	    close(fd);
355	    krb5_set_error_string (context, "seek: %s", strerror(ret));
356	    return ret;
357	}
358
359	ret = krb5_store_int32(sp, entry->vno);
360	if(ret) {
361	    krb5_storage_free(sp);
362	    close(fd);
363	    return ret;
364	}
365	ret = sp->store(sp, entry->keyblock.keyvalue.data,
366			entry->keyblock.keyvalue.length);
367	if(ret != entry->keyblock.keyvalue.length) {
368	    krb5_storage_free(sp);
369	    close(fd);
370	    if(ret < 0)
371		return errno;
372	    return ENOTTY;
373	}
374	krb5_storage_free(sp);
375    }
376    close (fd);
377    return 0;
378}
379
380const krb5_kt_ops krb5_akf_ops = {
381    "AFSKEYFILE",
382    akf_resolve,
383    akf_get_name,
384    akf_close,
385    NULL, /* get */
386    akf_start_seq_get,
387    akf_next_entry,
388    akf_end_seq_get,
389    akf_add_entry,
390    NULL /* remove */
391};
392