xref: /freebsd/crypto/heimdal/lib/krb5/keytab_keyfile.c (revision adb0ddaeac0a71a08d6af3a711387b59efcc94b6)
1b528cefcSMark Murray /*
2adb0ddaeSAssar Westerlund  * Copyright (c) 1997 - 2001 Kungliga Tekniska H�gskolan
3b528cefcSMark Murray  * (Royal Institute of Technology, Stockholm, Sweden).
4b528cefcSMark Murray  * All rights reserved.
5b528cefcSMark Murray  *
6b528cefcSMark Murray  * Redistribution and use in source and binary forms, with or without
7b528cefcSMark Murray  * modification, are permitted provided that the following conditions
8b528cefcSMark Murray  * are met:
9b528cefcSMark Murray  *
10b528cefcSMark Murray  * 1. Redistributions of source code must retain the above copyright
11b528cefcSMark Murray  *    notice, this list of conditions and the following disclaimer.
12b528cefcSMark Murray  *
13b528cefcSMark Murray  * 2. Redistributions in binary form must reproduce the above copyright
14b528cefcSMark Murray  *    notice, this list of conditions and the following disclaimer in the
15b528cefcSMark Murray  *    documentation and/or other materials provided with the distribution.
16b528cefcSMark Murray  *
17b528cefcSMark Murray  * 3. Neither the name of the Institute nor the names of its contributors
18b528cefcSMark Murray  *    may be used to endorse or promote products derived from this software
19b528cefcSMark Murray  *    without specific prior written permission.
20b528cefcSMark Murray  *
21b528cefcSMark Murray  * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
22b528cefcSMark Murray  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23b528cefcSMark Murray  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24b528cefcSMark Murray  * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
25b528cefcSMark Murray  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26b528cefcSMark Murray  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27b528cefcSMark Murray  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28b528cefcSMark Murray  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29b528cefcSMark Murray  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30b528cefcSMark Murray  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31b528cefcSMark Murray  * SUCH DAMAGE.
32b528cefcSMark Murray  */
33b528cefcSMark Murray 
34b528cefcSMark Murray #include "krb5_locl.h"
35b528cefcSMark Murray 
36adb0ddaeSAssar Westerlund RCSID("$Id: keytab_keyfile.c,v 1.11 2001/05/14 06:14:49 assar Exp $");
37b528cefcSMark Murray 
38b528cefcSMark Murray /* afs keyfile operations --------------------------------------- */
39b528cefcSMark Murray 
40b528cefcSMark Murray /*
41b528cefcSMark Murray  * Minimum tools to handle the AFS KeyFile.
42b528cefcSMark Murray  *
43b528cefcSMark Murray  * Format of the KeyFile is:
44b528cefcSMark Murray  * <int32_t numkeys> {[<int32_t kvno> <char[8] deskey>] * numkeys}
45b528cefcSMark Murray  *
46b528cefcSMark Murray  * It just adds to the end of the keyfile, deleting isn't implemented.
47b528cefcSMark Murray  * Use your favorite text/hex editor to delete keys.
48b528cefcSMark Murray  *
49b528cefcSMark Murray  */
50b528cefcSMark Murray 
51b528cefcSMark Murray #define AFS_SERVERTHISCELL "/usr/afs/etc/ThisCell"
52b528cefcSMark Murray #define AFS_SERVERMAGICKRBCONF "/usr/afs/etc/krb.conf"
53b528cefcSMark Murray 
54b528cefcSMark Murray struct akf_data {
55b528cefcSMark Murray     int num_entries;
56b528cefcSMark Murray     char *filename;
57b528cefcSMark Murray     char *cell;
58b528cefcSMark Murray     char *realm;
59b528cefcSMark Murray };
60b528cefcSMark Murray 
61b528cefcSMark Murray /*
62b528cefcSMark Murray  * set `d->cell' and `d->realm'
63b528cefcSMark Murray  */
64b528cefcSMark Murray 
65b528cefcSMark Murray static int
66adb0ddaeSAssar Westerlund get_cell_and_realm (krb5_context context,
67adb0ddaeSAssar Westerlund 		    struct akf_data *d)
68b528cefcSMark Murray {
69b528cefcSMark Murray     FILE *f;
70b528cefcSMark Murray     char buf[BUFSIZ], *cp;
71adb0ddaeSAssar Westerlund     int ret;
72b528cefcSMark Murray 
73b528cefcSMark Murray     f = fopen (AFS_SERVERTHISCELL, "r");
74adb0ddaeSAssar Westerlund     if (f == NULL) {
75adb0ddaeSAssar Westerlund 	ret = errno;
76adb0ddaeSAssar Westerlund 	krb5_set_error_string (context, "open %s: %s", AFS_SERVERTHISCELL,
77adb0ddaeSAssar Westerlund 			       strerror(ret));
78adb0ddaeSAssar Westerlund 	return ret;
79adb0ddaeSAssar Westerlund     }
80b528cefcSMark Murray     if (fgets (buf, sizeof(buf), f) == NULL) {
81b528cefcSMark Murray 	fclose (f);
82adb0ddaeSAssar Westerlund 	krb5_set_error_string (context, "no cell in %s", AFS_SERVERTHISCELL);
83b528cefcSMark Murray 	return EINVAL;
84b528cefcSMark Murray     }
85b528cefcSMark Murray     if (buf[strlen(buf) - 1] == '\n')
86b528cefcSMark Murray 	buf[strlen(buf) - 1] = '\0';
87b528cefcSMark Murray     fclose(f);
88b528cefcSMark Murray 
89b528cefcSMark Murray     d->cell = strdup (buf);
90adb0ddaeSAssar Westerlund     if (d->cell == NULL) {
91adb0ddaeSAssar Westerlund 	krb5_set_error_string (context, "malloc: out of memory");
92adb0ddaeSAssar Westerlund 	return ENOMEM;
93adb0ddaeSAssar Westerlund     }
94b528cefcSMark Murray 
95b528cefcSMark Murray     f = fopen (AFS_SERVERMAGICKRBCONF, "r");
96b528cefcSMark Murray     if (f != NULL) {
97b528cefcSMark Murray 	if (fgets (buf, sizeof(buf), f) == NULL) {
98b528cefcSMark Murray 	    fclose (f);
99adb0ddaeSAssar Westerlund 	    krb5_set_error_string (context, "no realm in %s",
100adb0ddaeSAssar Westerlund 				   AFS_SERVERMAGICKRBCONF);
101b528cefcSMark Murray 	    return EINVAL;
102b528cefcSMark Murray 	}
103b528cefcSMark Murray 	if (buf[strlen(buf)-1] == '\n')
104b528cefcSMark Murray 	    buf[strlen(buf)-1] = '\0';
105b528cefcSMark Murray 	fclose(f);
106b528cefcSMark Murray     }
107b528cefcSMark Murray     /* uppercase */
108b528cefcSMark Murray     for (cp = buf; *cp != '\0'; cp++)
109b528cefcSMark Murray 	*cp = toupper(*cp);
110b528cefcSMark Murray 
111b528cefcSMark Murray     d->realm = strdup (buf);
112b528cefcSMark Murray     if (d->realm == NULL) {
113b528cefcSMark Murray 	free (d->cell);
114adb0ddaeSAssar Westerlund 	krb5_set_error_string (context, "malloc: out of memory");
115adb0ddaeSAssar Westerlund 	return ENOMEM;
116b528cefcSMark Murray     }
117b528cefcSMark Murray     return 0;
118b528cefcSMark Murray }
119b528cefcSMark Murray 
120b528cefcSMark Murray /*
121b528cefcSMark Murray  * init and get filename
122b528cefcSMark Murray  */
123b528cefcSMark Murray 
124b528cefcSMark Murray static krb5_error_code
125b528cefcSMark Murray akf_resolve(krb5_context context, const char *name, krb5_keytab id)
126b528cefcSMark Murray {
127b528cefcSMark Murray     int ret;
128b528cefcSMark Murray     struct akf_data *d = malloc(sizeof (struct akf_data));
129b528cefcSMark Murray 
130adb0ddaeSAssar Westerlund     if (d == NULL) {
131adb0ddaeSAssar Westerlund 	krb5_set_error_string (context, "malloc: out of memory");
132adb0ddaeSAssar Westerlund 	return ENOMEM;
133adb0ddaeSAssar Westerlund     }
134b528cefcSMark Murray 
135b528cefcSMark Murray     d->num_entries = 0;
136adb0ddaeSAssar Westerlund     ret = get_cell_and_realm (context, d);
137b528cefcSMark Murray     if (ret) {
138b528cefcSMark Murray 	free (d);
139b528cefcSMark Murray 	return ret;
140b528cefcSMark Murray     }
141b528cefcSMark Murray     d->filename = strdup (name);
142b528cefcSMark Murray     if (d->filename == NULL) {
143b528cefcSMark Murray 	free (d->cell);
144b528cefcSMark Murray 	free (d->realm);
145b528cefcSMark Murray 	free (d);
146adb0ddaeSAssar Westerlund 	krb5_set_error_string (context, "malloc: out of memory");
147b528cefcSMark Murray 	return ENOMEM;
148b528cefcSMark Murray     }
149b528cefcSMark Murray     id->data = d;
150b528cefcSMark Murray 
151b528cefcSMark Murray     return 0;
152b528cefcSMark Murray }
153b528cefcSMark Murray 
154b528cefcSMark Murray /*
155b528cefcSMark Murray  * cleanup
156b528cefcSMark Murray  */
157b528cefcSMark Murray 
158b528cefcSMark Murray static krb5_error_code
159b528cefcSMark Murray akf_close(krb5_context context, krb5_keytab id)
160b528cefcSMark Murray {
161b528cefcSMark Murray     struct akf_data *d = id->data;
162b528cefcSMark Murray 
163b528cefcSMark Murray     free (d->filename);
164b528cefcSMark Murray     free (d->cell);
165b528cefcSMark Murray     free (d);
166b528cefcSMark Murray     return 0;
167b528cefcSMark Murray }
168b528cefcSMark Murray 
169b528cefcSMark Murray /*
170b528cefcSMark Murray  * Return filename
171b528cefcSMark Murray  */
172b528cefcSMark Murray 
173b528cefcSMark Murray static krb5_error_code
174b528cefcSMark Murray akf_get_name(krb5_context context,
175b528cefcSMark Murray 	     krb5_keytab id,
176b528cefcSMark Murray 	     char *name,
177b528cefcSMark Murray 	     size_t name_sz)
178b528cefcSMark Murray {
179b528cefcSMark Murray     struct akf_data *d = id->data;
180b528cefcSMark Murray 
181b528cefcSMark Murray     strlcpy (name, d->filename, name_sz);
182b528cefcSMark Murray     return 0;
183b528cefcSMark Murray }
184b528cefcSMark Murray 
185b528cefcSMark Murray /*
186b528cefcSMark Murray  * Init
187b528cefcSMark Murray  */
188b528cefcSMark Murray 
189b528cefcSMark Murray static krb5_error_code
190b528cefcSMark Murray akf_start_seq_get(krb5_context context,
191b528cefcSMark Murray 		  krb5_keytab id,
192b528cefcSMark Murray 		  krb5_kt_cursor *c)
193b528cefcSMark Murray {
194b528cefcSMark Murray     int32_t ret;
195b528cefcSMark Murray     struct akf_data *d = id->data;
196b528cefcSMark Murray 
197b528cefcSMark Murray     c->fd = open (d->filename, O_RDONLY|O_BINARY, 0600);
198adb0ddaeSAssar Westerlund     if (c->fd < 0) {
199adb0ddaeSAssar Westerlund 	ret = errno;
200adb0ddaeSAssar Westerlund 	krb5_set_error_string(context, "open(%s): %s", d->filename,
201adb0ddaeSAssar Westerlund 			      strerror(ret));
202adb0ddaeSAssar Westerlund 	return ret;
203adb0ddaeSAssar Westerlund     }
204b528cefcSMark Murray 
205b528cefcSMark Murray     c->sp = krb5_storage_from_fd(c->fd);
206b528cefcSMark Murray     ret = krb5_ret_int32(c->sp, &d->num_entries);
207b528cefcSMark Murray     if(ret) {
208b528cefcSMark Murray 	krb5_storage_free(c->sp);
209b528cefcSMark Murray 	close(c->fd);
210adb0ddaeSAssar Westerlund 	krb5_clear_error_string (context);
211adb0ddaeSAssar Westerlund 	if(ret == KRB5_CC_END)
212adb0ddaeSAssar Westerlund 	    return KRB5_KT_NOTFOUND;
213b528cefcSMark Murray 	return ret;
214b528cefcSMark Murray     }
215b528cefcSMark Murray 
216b528cefcSMark Murray     return 0;
217b528cefcSMark Murray }
218b528cefcSMark Murray 
219b528cefcSMark Murray static krb5_error_code
220b528cefcSMark Murray akf_next_entry(krb5_context context,
221b528cefcSMark Murray 	       krb5_keytab id,
222b528cefcSMark Murray 	       krb5_keytab_entry *entry,
223b528cefcSMark Murray 	       krb5_kt_cursor *cursor)
224b528cefcSMark Murray {
225b528cefcSMark Murray     struct akf_data *d = id->data;
226b528cefcSMark Murray     int32_t kvno;
227b528cefcSMark Murray     off_t pos;
228b528cefcSMark Murray     int ret;
229b528cefcSMark Murray 
230b528cefcSMark Murray     pos = cursor->sp->seek(cursor->sp, 0, SEEK_CUR);
231b528cefcSMark Murray 
232b528cefcSMark Murray     if ((pos - 4) / (4 + 8) >= d->num_entries)
233b528cefcSMark Murray 	return KRB5_KT_END;
234b528cefcSMark Murray 
235b528cefcSMark Murray     ret = krb5_make_principal (context, &entry->principal,
236b528cefcSMark Murray 			       d->realm, "afs", d->cell, NULL);
237b528cefcSMark Murray     if (ret)
238b528cefcSMark Murray 	goto out;
239b528cefcSMark Murray 
240b528cefcSMark Murray     ret = krb5_ret_int32(cursor->sp, &kvno);
241b528cefcSMark Murray     if (ret) {
242b528cefcSMark Murray 	krb5_free_principal (context, entry->principal);
243b528cefcSMark Murray 	goto out;
244b528cefcSMark Murray     }
245b528cefcSMark Murray 
2465e9cd1aeSAssar Westerlund     entry->vno = kvno;
247b528cefcSMark Murray 
248b528cefcSMark Murray     entry->keyblock.keytype         = ETYPE_DES_CBC_MD5;
249b528cefcSMark Murray     entry->keyblock.keyvalue.length = 8;
250b528cefcSMark Murray     entry->keyblock.keyvalue.data   = malloc (8);
251b528cefcSMark Murray     if (entry->keyblock.keyvalue.data == NULL) {
252b528cefcSMark Murray 	krb5_free_principal (context, entry->principal);
253adb0ddaeSAssar Westerlund 	krb5_set_error_string (context, "malloc: out of memory");
254b528cefcSMark Murray 	ret = ENOMEM;
255b528cefcSMark Murray 	goto out;
256b528cefcSMark Murray     }
257b528cefcSMark Murray 
258b528cefcSMark Murray     ret = cursor->sp->fetch(cursor->sp, entry->keyblock.keyvalue.data, 8);
259b528cefcSMark Murray     if(ret != 8)
260b528cefcSMark Murray 	ret = (ret < 0) ? errno : KRB5_KT_END;
2615e9cd1aeSAssar Westerlund     else
2625e9cd1aeSAssar Westerlund 	ret = 0;
263b528cefcSMark Murray 
264b528cefcSMark Murray     entry->timestamp = time(NULL);
265b528cefcSMark Murray 
266b528cefcSMark Murray  out:
267b528cefcSMark Murray     cursor->sp->seek(cursor->sp, pos + 4 + 8, SEEK_SET);
268b528cefcSMark Murray     return ret;
269b528cefcSMark Murray }
270b528cefcSMark Murray 
271b528cefcSMark Murray static krb5_error_code
272b528cefcSMark Murray akf_end_seq_get(krb5_context context,
273b528cefcSMark Murray 		krb5_keytab id,
274b528cefcSMark Murray 		krb5_kt_cursor *cursor)
275b528cefcSMark Murray {
276b528cefcSMark Murray     krb5_storage_free(cursor->sp);
277b528cefcSMark Murray     close(cursor->fd);
278b528cefcSMark Murray     return 0;
279b528cefcSMark Murray }
280b528cefcSMark Murray 
281b528cefcSMark Murray static krb5_error_code
282b528cefcSMark Murray akf_add_entry(krb5_context context,
283b528cefcSMark Murray 	      krb5_keytab id,
284b528cefcSMark Murray 	      krb5_keytab_entry *entry)
285b528cefcSMark Murray {
286b528cefcSMark Murray     struct akf_data *d = id->data;
287b528cefcSMark Murray     int fd, created = 0;
2885e9cd1aeSAssar Westerlund     krb5_error_code ret;
289b528cefcSMark Murray 
290b528cefcSMark Murray     fd = open (d->filename, O_RDWR | O_BINARY);
291b528cefcSMark Murray     if (fd < 0) {
292b528cefcSMark Murray 	fd = open (d->filename,
293b528cefcSMark Murray 		   O_RDWR | O_BINARY | O_CREAT, 0600);
294adb0ddaeSAssar Westerlund 	if (fd < 0) {
295adb0ddaeSAssar Westerlund 	    ret = errno;
296adb0ddaeSAssar Westerlund 	    krb5_set_error_string(context, "open(%s): %s", d->filename,
297adb0ddaeSAssar Westerlund 				  strerror(ret));
298adb0ddaeSAssar Westerlund 	    return ret;
299adb0ddaeSAssar Westerlund 	}
300b528cefcSMark Murray 	created = 1;
301b528cefcSMark Murray     }
302b528cefcSMark Murray 
303b528cefcSMark Murray     if (entry->keyblock.keyvalue.length == 8
304b528cefcSMark Murray 	&& entry->keyblock.keytype == ETYPE_DES_CBC_MD5) {
305b528cefcSMark Murray 
3065e9cd1aeSAssar Westerlund 	int32_t len;
3075e9cd1aeSAssar Westerlund 	krb5_storage *sp;
308b528cefcSMark Murray 
3095e9cd1aeSAssar Westerlund 	sp = krb5_storage_from_fd(fd);
3105e9cd1aeSAssar Westerlund 	if(sp == NULL) {
3115e9cd1aeSAssar Westerlund 	    close(fd);
312adb0ddaeSAssar Westerlund 	    krb5_set_error_string (context, "malloc: out of memory");
3135e9cd1aeSAssar Westerlund 	    return ENOMEM;
3145e9cd1aeSAssar Westerlund 	}
3155e9cd1aeSAssar Westerlund 	if (created)
3165e9cd1aeSAssar Westerlund 	    len = 0;
3175e9cd1aeSAssar Westerlund 	else {
3185e9cd1aeSAssar Westerlund 	    if((*sp->seek)(sp, 0, SEEK_SET) < 0) {
319adb0ddaeSAssar Westerlund 		ret = errno;
3205e9cd1aeSAssar Westerlund 		krb5_storage_free(sp);
3215e9cd1aeSAssar Westerlund 		close(fd);
322adb0ddaeSAssar Westerlund 		krb5_set_error_string (context, "seek: %s", strerror(ret));
323adb0ddaeSAssar Westerlund 		return ret;
324b528cefcSMark Murray 	    }
325b528cefcSMark Murray 
3265e9cd1aeSAssar Westerlund 	    ret = krb5_ret_int32(sp, &len);
3275e9cd1aeSAssar Westerlund 	    if(ret) {
3285e9cd1aeSAssar Westerlund 		krb5_storage_free(sp);
3295e9cd1aeSAssar Westerlund 		close(fd);
3305e9cd1aeSAssar Westerlund 		return ret;
3315e9cd1aeSAssar Westerlund 	    }
3325e9cd1aeSAssar Westerlund 	}
3335e9cd1aeSAssar Westerlund 	len++;
3345e9cd1aeSAssar Westerlund 
3355e9cd1aeSAssar Westerlund 	if((*sp->seek)(sp, 0, SEEK_SET) < 0) {
336adb0ddaeSAssar Westerlund 	    ret = errno;
3375e9cd1aeSAssar Westerlund 	    krb5_storage_free(sp);
3385e9cd1aeSAssar Westerlund 	    close(fd);
339adb0ddaeSAssar Westerlund 	    krb5_set_error_string (context, "seek: %s", strerror(ret));
340adb0ddaeSAssar Westerlund 	    return ret;
3415e9cd1aeSAssar Westerlund 	}
342b528cefcSMark Murray 
3435e9cd1aeSAssar Westerlund 	ret = krb5_store_int32(sp, len);
3445e9cd1aeSAssar Westerlund 	if(ret) {
3455e9cd1aeSAssar Westerlund 	    krb5_storage_free(sp);
3465e9cd1aeSAssar Westerlund 	    close(fd);
3475e9cd1aeSAssar Westerlund 	    return ret;
3485e9cd1aeSAssar Westerlund 	}
3495e9cd1aeSAssar Westerlund 
3505e9cd1aeSAssar Westerlund 
3515e9cd1aeSAssar Westerlund 	if((*sp->seek)(sp, (len - 1) * (8 + 4), SEEK_CUR) < 0) {
352adb0ddaeSAssar Westerlund 	    ret = errno;
3535e9cd1aeSAssar Westerlund 	    krb5_storage_free(sp);
3545e9cd1aeSAssar Westerlund 	    close(fd);
355adb0ddaeSAssar Westerlund 	    krb5_set_error_string (context, "seek: %s", strerror(ret));
356adb0ddaeSAssar Westerlund 	    return ret;
3575e9cd1aeSAssar Westerlund 	}
358b528cefcSMark Murray 
3595e9cd1aeSAssar Westerlund 	ret = krb5_store_int32(sp, entry->vno);
3605e9cd1aeSAssar Westerlund 	if(ret) {
3615e9cd1aeSAssar Westerlund 	    krb5_storage_free(sp);
3625e9cd1aeSAssar Westerlund 	    close(fd);
3635e9cd1aeSAssar Westerlund 	    return ret;
3645e9cd1aeSAssar Westerlund 	}
3655e9cd1aeSAssar Westerlund 	ret = sp->store(sp, entry->keyblock.keyvalue.data,
3665e9cd1aeSAssar Westerlund 			entry->keyblock.keyvalue.length);
3675e9cd1aeSAssar Westerlund 	if(ret != entry->keyblock.keyvalue.length) {
3685e9cd1aeSAssar Westerlund 	    krb5_storage_free(sp);
3695e9cd1aeSAssar Westerlund 	    close(fd);
3705e9cd1aeSAssar Westerlund 	    if(ret < 0)
371b528cefcSMark Murray 		return errno;
3725e9cd1aeSAssar Westerlund 	    return ENOTTY;
3735e9cd1aeSAssar Westerlund 	}
3745e9cd1aeSAssar Westerlund 	krb5_storage_free(sp);
375b528cefcSMark Murray     }
376b528cefcSMark Murray     close (fd);
377b528cefcSMark Murray     return 0;
378b528cefcSMark Murray }
379b528cefcSMark Murray 
380b528cefcSMark Murray const krb5_kt_ops krb5_akf_ops = {
381b528cefcSMark Murray     "AFSKEYFILE",
382b528cefcSMark Murray     akf_resolve,
383b528cefcSMark Murray     akf_get_name,
384b528cefcSMark Murray     akf_close,
385b528cefcSMark Murray     NULL, /* get */
386b528cefcSMark Murray     akf_start_seq_get,
387b528cefcSMark Murray     akf_next_entry,
388b528cefcSMark Murray     akf_end_seq_get,
389b528cefcSMark Murray     akf_add_entry,
390b528cefcSMark Murray     NULL /* remove */
391b528cefcSMark Murray };
392