etc/services - sync with NetBSD-8
[minix.git] / crypto / external / bsd / heimdal / dist / lib / krb5 / keytab_keyfile.c
blobeeb691bd686afdbffecc60ad8cec26fa4c929f21
1 /* $NetBSD: keytab_keyfile.c,v 1.1.1.2 2014/04/24 12:45:50 pettai Exp $ */
3 /*
4 * Copyright (c) 1997 - 2007 Kungliga Tekniska Högskolan
5 * (Royal Institute of Technology, Stockholm, Sweden).
6 * All rights reserved.
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
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.
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.
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.
36 #include "krb5_locl.h"
38 #ifndef HEIMDAL_SMALLER
40 /* afs keyfile operations --------------------------------------- */
43 * Minimum tools to handle the AFS KeyFile.
45 * Format of the KeyFile is:
46 * <int32_t numkeys> {[<int32_t kvno> <char[8] deskey>] * numkeys}
48 * It just adds to the end of the keyfile, deleting isn't implemented.
49 * Use your favorite text/hex editor to delete keys.
53 #define AFS_SERVERTHISCELL "/usr/afs/etc/ThisCell"
54 #define AFS_SERVERMAGICKRBCONF "/usr/afs/etc/krb.conf"
56 struct akf_data {
57 uint32_t num_entries;
58 char *filename;
59 char *cell;
60 char *realm;
64 * set `d->cell' and `d->realm'
67 static int
68 get_cell_and_realm (krb5_context context, struct akf_data *d)
70 FILE *f;
71 char buf[BUFSIZ], *cp;
72 int ret;
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;
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;
90 buf[strcspn(buf, "\n")] = '\0';
91 fclose(f);
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;
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;
111 buf[strcspn(buf, "\n")] = '\0';
112 fclose(f);
114 /* uppercase */
115 for (cp = buf; *cp != '\0'; cp++)
116 *cp = toupper((unsigned char)*cp);
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;
126 return 0;
130 * init and get filename
133 static krb5_error_code KRB5_CALLCONV
134 akf_resolve(krb5_context context, const char *name, krb5_keytab id)
136 int ret;
137 struct akf_data *d = malloc(sizeof (struct akf_data));
139 if (d == NULL) {
140 krb5_set_error_message(context, ENOMEM,
141 N_("malloc: out of memory", ""));
142 return ENOMEM;
145 d->num_entries = 0;
146 ret = get_cell_and_realm (context, d);
147 if (ret) {
148 free (d);
149 return ret;
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;
160 id->data = d;
162 return 0;
166 * cleanup
169 static krb5_error_code KRB5_CALLCONV
170 akf_close(krb5_context context, krb5_keytab id)
172 struct akf_data *d = id->data;
174 free (d->filename);
175 free (d->cell);
176 free (d);
177 return 0;
181 * Return filename
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)
190 struct akf_data *d = id->data;
192 strlcpy (name, d->filename, name_sz);
193 return 0;
197 * Init
200 static krb5_error_code KRB5_CALLCONV
201 akf_start_seq_get(krb5_context context,
202 krb5_keytab id,
203 krb5_kt_cursor *c)
205 int32_t ret;
206 struct akf_data *d = id->data;
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;
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;
224 krb5_storage_set_eof_code(c->sp, KRB5_KT_END);
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;
236 return 0;
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)
245 struct akf_data *d = id->data;
246 int32_t kvno;
247 off_t pos;
248 int ret;
250 pos = krb5_storage_seek(cursor->sp, 0, SEEK_CUR);
252 if ((pos - 4) / (4 + 8) >= d->num_entries)
253 return KRB5_KT_END;
255 ret = krb5_make_principal (context, &entry->principal,
256 d->realm, "afs", d->cell, NULL);
257 if (ret)
258 goto out;
260 ret = krb5_ret_int32(cursor->sp, &kvno);
261 if (ret) {
262 krb5_free_principal (context, entry->principal);
263 goto out;
266 entry->vno = kvno;
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;
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;
288 entry->timestamp = time(NULL);
289 entry->flags = 0;
290 entry->aliases = NULL;
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;
301 static krb5_error_code KRB5_CALLCONV
302 akf_end_seq_get(krb5_context context,
303 krb5_keytab id,
304 krb5_kt_cursor *cursor)
306 krb5_storage_free(cursor->sp);
307 close(cursor->fd);
308 cursor->data = NULL;
309 return 0;
312 static krb5_error_code KRB5_CALLCONV
313 akf_add_entry(krb5_context context,
314 krb5_keytab id,
315 krb5_keytab_entry *entry)
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;
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;
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;
347 created = 1;
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;
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;
370 ret = krb5_ret_int32(sp, &len);
371 if(ret) {
372 krb5_storage_free(sp);
373 close(fd);
374 return ret;
379 * Make sure we don't add the entry twice, assumes the DES
380 * encryption types are all the same key.
382 if (len > 0) {
383 int32_t kvno;
384 int i;
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;
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;
400 if (kvno == entry->vno) {
401 ret = 0;
402 goto out;
407 len++;
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;
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;
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;
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;
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;
449 ret = 0;
450 out:
451 krb5_storage_free(sp);
452 close (fd);
453 return ret;
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 */
470 #endif /* HEIMDAL_SMALLER */