/* [<][>][^][v][top][bottom][index][help] */
DEFINITIONS
This source file includes following definitions.
- get_cell_and_realm
- akf_resolve
- akf_close
- akf_get_name
- akf_start_seq_get
- akf_next_entry
- akf_end_seq_get
- akf_add_entry
1 /*
2 * Copyright (c) 1997 - 2007 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
36 RCSID("$Id$");
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)
/* [<][>][^][v][top][bottom][index][help] */
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
134 akf_resolve(krb5_context context, const char *name, krb5_keytab id)
/* [<][>][^][v][top][bottom][index][help] */
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
170 akf_close(krb5_context context, krb5_keytab id)
/* [<][>][^][v][top][bottom][index][help] */
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
185 akf_get_name(krb5_context context,
/* [<][>][^][v][top][bottom][index][help] */
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
201 akf_start_seq_get(krb5_context context,
/* [<][>][^][v][top][bottom][index][help] */
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
232 akf_next_entry(krb5_context context,
/* [<][>][^][v][top][bottom][index][help] */
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
279 out:
280 krb5_storage_seek(cursor->sp, pos + 4 + 8, SEEK_SET);
281 return ret;
282 }
283
284 static krb5_error_code
285 akf_end_seq_get(krb5_context context,
/* [<][>][^][v][top][bottom][index][help] */
286 krb5_keytab id,
287 krb5_kt_cursor *cursor)
288 {
289 krb5_storage_free(cursor->sp);
290 close(cursor->fd);
291 return 0;
292 }
293
294 static krb5_error_code
295 akf_add_entry(krb5_context context,
/* [<][>][^][v][top][bottom][index][help] */
296 krb5_keytab id,
297 krb5_keytab_entry *entry)
298 {
299 struct akf_data *d = id->data;
300 int fd, created = 0;
301 krb5_error_code ret;
302 int32_t len;
303 krb5_storage *sp;
304
305
306 if (entry->keyblock.keyvalue.length != 8)
307 return 0;
308 switch(entry->keyblock.keytype) {
309 case ETYPE_DES_CBC_CRC:
310 case ETYPE_DES_CBC_MD4:
311 case ETYPE_DES_CBC_MD5:
312 break;
313 default:
314 return 0;
315 }
316
317 fd = open (d->filename, O_RDWR | O_BINARY | O_CLOEXEC);
318 if (fd < 0) {
319 fd = open (d->filename,
320 O_RDWR | O_BINARY | O_CREAT | O_EXCL | O_CLOEXEC, 0600);
321 if (fd < 0) {
322 ret = errno;
323 krb5_set_error_message(context, ret,
324 N_("open keyfile(%s): %s", ""),
325 d->filename,
326 strerror(ret));
327 return ret;
328 }
329 created = 1;
330 }
331
332 sp = krb5_storage_from_fd(fd);
333 if(sp == NULL) {
334 close(fd);
335 krb5_set_error_message(context, ENOMEM,
336 N_("malloc: out of memory", ""));
337 return ENOMEM;
338 }
339 if (created)
340 len = 0;
341 else {
342 if(krb5_storage_seek(sp, 0, SEEK_SET) < 0) {
343 ret = errno;
344 krb5_storage_free(sp);
345 close(fd);
346 krb5_set_error_message(context, ret,
347 N_("seeking in keyfile: %s", ""),
348 strerror(ret));
349 return ret;
350 }
351
352 ret = krb5_ret_int32(sp, &len);
353 if(ret) {
354 krb5_storage_free(sp);
355 close(fd);
356 return ret;
357 }
358 }
359
360 /*
361 * Make sure we don't add the entry twice, assumes the DES
362 * encryption types are all the same key.
363 */
364 if (len > 0) {
365 int32_t kvno;
366 int i;
367
368 for (i = 0; i < len; i++) {
369 ret = krb5_ret_int32(sp, &kvno);
370 if (ret) {
371 krb5_set_error_message (context, ret,
372 N_("Failed getting kvno from keyfile", ""));
373 goto out;
374 }
375 if(krb5_storage_seek(sp, 8, SEEK_CUR) < 0) {
376 ret = errno;
377 krb5_set_error_message (context, ret,
378 N_("Failed seeing in keyfile: %s", ""),
379 strerror(ret));
380 goto out;
381 }
382 if (kvno == entry->vno) {
383 ret = 0;
384 goto out;
385 }
386 }
387 }
388
389 len++;
390
391 if(krb5_storage_seek(sp, 0, SEEK_SET) < 0) {
392 ret = errno;
393 krb5_set_error_message (context, ret,
394 N_("Failed seeing in keyfile: %s", ""),
395 strerror(ret));
396 goto out;
397 }
398
399 ret = krb5_store_int32(sp, len);
400 if(ret) {
401 ret = errno;
402 krb5_set_error_message (context, ret,
403 N_("keytab keyfile failed new length", ""));
404 return ret;
405 }
406
407 if(krb5_storage_seek(sp, (len - 1) * (8 + 4), SEEK_CUR) < 0) {
408 ret = errno;
409 krb5_set_error_message (context, ret,
410 N_("seek to end: %s", ""), strerror(ret));
411 goto out;
412 }
413
414 ret = krb5_store_int32(sp, entry->vno);
415 if(ret) {
416 krb5_set_error_message(context, ret,
417 N_("keytab keyfile failed store kvno", ""));
418 goto out;
419 }
420 ret = krb5_storage_write(sp, entry->keyblock.keyvalue.data,
421 entry->keyblock.keyvalue.length);
422 if(ret != entry->keyblock.keyvalue.length) {
423 if (ret < 0)
424 ret = errno;
425 else
426 ret = ENOTTY;
427 krb5_set_error_message(context, ret,
428 N_("keytab keyfile failed to add key", ""));
429 goto out;
430 }
431 ret = 0;
432 out:
433 krb5_storage_free(sp);
434 close (fd);
435 return ret;
436 }
437
438 const krb5_kt_ops krb5_akf_ops = {
439 "AFSKEYFILE",
440 akf_resolve,
441 akf_get_name,
442 akf_close,
443 NULL, /* get */
444 akf_start_seq_get,
445 akf_next_entry,
446 akf_end_seq_get,
447 akf_add_entry,
448 NULL /* remove */
449 };
450
451 #endif /* HEIMDAL_SMALLER */