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
get_cell_and_realm(krb5_context context,struct akf_data * d)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
akf_resolve(krb5_context context,const char * name,krb5_keytab id)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
akf_close(krb5_context context,krb5_keytab id)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
akf_get_name(krb5_context context,krb5_keytab id,char * name,size_t name_sz)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
akf_start_seq_get(krb5_context context,krb5_keytab id,krb5_kt_cursor * c)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
akf_next_entry(krb5_context context,krb5_keytab id,krb5_keytab_entry * entry,krb5_kt_cursor * cursor)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
akf_end_seq_get(krb5_context context,krb5_keytab id,krb5_kt_cursor * cursor)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
akf_add_entry(krb5_context context,krb5_keytab id,krb5_keytab_entry * entry)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