Line data Source code
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 : #ifndef HEIMDAL_SMALLER
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 :
54 : struct akf_data {
55 : uint32_t num_entries;
56 : char *filename;
57 : char *cell;
58 : char *realm;
59 : };
60 :
61 : /*
62 : * set `d->cell' and `d->realm'
63 : */
64 :
65 : static int
66 0 : get_cell_and_realm (krb5_context context, struct akf_data *d)
67 : {
68 0 : FILE *f;
69 0 : char buf[BUFSIZ], *cp;
70 0 : int ret;
71 :
72 0 : f = fopen (AFS_SERVERTHISCELL, "r");
73 0 : if (f == NULL) {
74 0 : ret = errno;
75 0 : krb5_set_error_message (context, ret,
76 0 : N_("Open ThisCell %s: %s", ""),
77 : AFS_SERVERTHISCELL,
78 : strerror(ret));
79 0 : return ret;
80 : }
81 0 : if (fgets (buf, sizeof(buf), f) == NULL) {
82 0 : fclose (f);
83 0 : krb5_set_error_message (context, EINVAL,
84 0 : N_("No cell in ThisCell file %s", ""),
85 : AFS_SERVERTHISCELL);
86 0 : return EINVAL;
87 : }
88 0 : buf[strcspn(buf, "\n")] = '\0';
89 0 : fclose(f);
90 :
91 0 : d->cell = strdup (buf);
92 0 : if (d->cell == NULL)
93 0 : return krb5_enomem(context);
94 :
95 0 : f = fopen (AFS_SERVERMAGICKRBCONF, "r");
96 0 : if (f != NULL) {
97 0 : if (fgets (buf, sizeof(buf), f) == NULL) {
98 0 : free (d->cell);
99 0 : d->cell = NULL;
100 0 : fclose (f);
101 0 : krb5_set_error_message (context, EINVAL,
102 0 : N_("No realm in ThisCell file %s", ""),
103 : AFS_SERVERMAGICKRBCONF);
104 0 : return EINVAL;
105 : }
106 0 : buf[strcspn(buf, "\n")] = '\0';
107 0 : fclose(f);
108 : }
109 : /* uppercase */
110 0 : for (cp = buf; *cp != '\0'; cp++)
111 0 : *cp = toupper((unsigned char)*cp);
112 :
113 0 : d->realm = strdup (buf);
114 0 : if (d->realm == NULL) {
115 0 : free (d->cell);
116 0 : d->cell = NULL;
117 0 : return krb5_enomem(context);
118 : }
119 0 : return 0;
120 : }
121 :
122 : /*
123 : * init and get filename
124 : */
125 :
126 : static krb5_error_code KRB5_CALLCONV
127 0 : akf_resolve(krb5_context context, const char *name, krb5_keytab id)
128 : {
129 0 : int ret;
130 0 : struct akf_data *d = calloc(1, sizeof (struct akf_data));
131 :
132 0 : if (d == NULL)
133 0 : return krb5_enomem(context);
134 :
135 0 : d->num_entries = 0;
136 0 : ret = get_cell_and_realm (context, d);
137 0 : if (ret) {
138 0 : free (d);
139 0 : return ret;
140 : }
141 0 : d->filename = strdup (name);
142 0 : if (d->filename == NULL) {
143 0 : free (d->cell);
144 0 : free (d->realm);
145 0 : free (d);
146 0 : return krb5_enomem(context);
147 : }
148 0 : id->data = d;
149 :
150 0 : return 0;
151 : }
152 :
153 : /*
154 : * cleanup
155 : */
156 :
157 : static krb5_error_code KRB5_CALLCONV
158 0 : akf_close(krb5_context context, krb5_keytab id)
159 : {
160 0 : struct akf_data *d = id->data;
161 :
162 0 : free (d->filename);
163 0 : free (d->cell);
164 0 : free (d);
165 0 : return 0;
166 : }
167 :
168 : /*
169 : * Return filename
170 : */
171 :
172 : static krb5_error_code KRB5_CALLCONV
173 0 : akf_get_name(krb5_context context,
174 : krb5_keytab id,
175 : char *name,
176 : size_t name_sz)
177 : {
178 0 : struct akf_data *d = id->data;
179 :
180 0 : strlcpy (name, d->filename, name_sz);
181 0 : return 0;
182 : }
183 :
184 : /*
185 : * Init
186 : */
187 :
188 : static krb5_error_code KRB5_CALLCONV
189 0 : akf_start_seq_get(krb5_context context,
190 : krb5_keytab id,
191 : krb5_kt_cursor *c)
192 : {
193 0 : int32_t ret;
194 0 : struct akf_data *d = id->data;
195 :
196 0 : c->fd = open (d->filename, O_RDONLY | O_BINARY | O_CLOEXEC, 0600);
197 0 : if (c->fd < 0) {
198 0 : ret = errno;
199 0 : krb5_set_error_message(context, ret,
200 0 : N_("keytab afs keyfile open %s failed: %s", ""),
201 : d->filename, strerror(ret));
202 0 : return ret;
203 : }
204 :
205 0 : c->data = NULL;
206 0 : c->sp = krb5_storage_from_fd(c->fd);
207 0 : if (c->sp == NULL) {
208 0 : close(c->fd);
209 0 : krb5_clear_error_message (context);
210 0 : return KRB5_KT_NOTFOUND;
211 : }
212 0 : krb5_storage_set_eof_code(c->sp, KRB5_KT_END);
213 :
214 0 : ret = krb5_ret_uint32(c->sp, &d->num_entries);
215 0 : if(ret || d->num_entries > INT_MAX / 8) {
216 0 : krb5_storage_free(c->sp);
217 0 : close(c->fd);
218 0 : krb5_clear_error_message (context);
219 0 : if(ret == KRB5_KT_END)
220 0 : return KRB5_KT_NOTFOUND;
221 0 : return ret;
222 : }
223 :
224 0 : return 0;
225 : }
226 :
227 : static krb5_error_code KRB5_CALLCONV
228 0 : akf_next_entry(krb5_context context,
229 : krb5_keytab id,
230 : krb5_keytab_entry *entry,
231 : krb5_kt_cursor *cursor)
232 : {
233 0 : struct akf_data *d = id->data;
234 0 : int32_t kvno;
235 0 : off_t pos;
236 0 : int ret;
237 :
238 0 : pos = krb5_storage_seek(cursor->sp, 0, SEEK_CUR);
239 :
240 0 : if ((pos - 4) / (4 + 8) >= d->num_entries)
241 0 : return KRB5_KT_END;
242 :
243 0 : ret = krb5_make_principal (context, &entry->principal,
244 0 : d->realm, "afs", d->cell, NULL);
245 0 : if (ret)
246 0 : goto out;
247 :
248 0 : ret = krb5_ret_int32(cursor->sp, &kvno);
249 0 : if (ret) {
250 0 : krb5_free_principal (context, entry->principal);
251 0 : goto out;
252 : }
253 :
254 0 : entry->vno = kvno;
255 :
256 0 : if (cursor->data)
257 0 : entry->keyblock.keytype = ETYPE_DES_CBC_MD5;
258 : else
259 0 : entry->keyblock.keytype = ETYPE_DES_CBC_CRC;
260 0 : entry->keyblock.keyvalue.length = 8;
261 0 : entry->keyblock.keyvalue.data = malloc (8);
262 0 : if (entry->keyblock.keyvalue.data == NULL) {
263 0 : krb5_free_principal (context, entry->principal);
264 0 : ret = krb5_enomem(context);
265 0 : goto out;
266 : }
267 :
268 0 : ret = krb5_storage_read(cursor->sp, entry->keyblock.keyvalue.data, 8);
269 0 : if(ret != 8)
270 0 : ret = (ret < 0) ? errno : KRB5_KT_END;
271 : else
272 0 : ret = 0;
273 :
274 0 : entry->timestamp = time(NULL);
275 0 : entry->flags = 0;
276 0 : entry->aliases = NULL;
277 :
278 0 : out:
279 0 : if (cursor->data) {
280 0 : krb5_storage_seek(cursor->sp, pos + 4 + 8, SEEK_SET);
281 0 : cursor->data = NULL;
282 : } else
283 0 : cursor->data = cursor;
284 0 : return ret;
285 : }
286 :
287 : static krb5_error_code KRB5_CALLCONV
288 0 : akf_end_seq_get(krb5_context context,
289 : krb5_keytab id,
290 : krb5_kt_cursor *cursor)
291 : {
292 0 : krb5_storage_free(cursor->sp);
293 0 : close(cursor->fd);
294 0 : cursor->data = NULL;
295 0 : return 0;
296 : }
297 :
298 : static krb5_error_code KRB5_CALLCONV
299 0 : akf_add_entry(krb5_context context,
300 : krb5_keytab id,
301 : krb5_keytab_entry *entry)
302 : {
303 0 : struct akf_data *d = id->data;
304 0 : int fd, created = 0;
305 0 : krb5_error_code ret;
306 0 : int32_t len;
307 0 : krb5_storage *sp;
308 :
309 :
310 0 : if (entry->keyblock.keyvalue.length != 8)
311 0 : return 0;
312 0 : switch(entry->keyblock.keytype) {
313 0 : case ETYPE_DES_CBC_CRC:
314 : case ETYPE_DES_CBC_MD4:
315 : case ETYPE_DES_CBC_MD5:
316 0 : break;
317 0 : default:
318 0 : return 0;
319 : }
320 :
321 0 : fd = open (d->filename, O_RDWR | O_BINARY | O_CLOEXEC);
322 0 : if (fd < 0) {
323 0 : fd = open (d->filename,
324 : O_RDWR | O_BINARY | O_CREAT | O_EXCL | O_CLOEXEC, 0600);
325 0 : if (fd < 0) {
326 0 : ret = errno;
327 0 : krb5_set_error_message(context, ret,
328 0 : N_("open keyfile(%s): %s", ""),
329 : d->filename,
330 : strerror(ret));
331 0 : return ret;
332 : }
333 0 : created = 1;
334 : }
335 :
336 0 : sp = krb5_storage_from_fd(fd);
337 0 : if(sp == NULL) {
338 0 : close(fd);
339 0 : return krb5_enomem(context);
340 : }
341 0 : if (created)
342 0 : len = 0;
343 : else {
344 0 : if(krb5_storage_seek(sp, 0, SEEK_SET) < 0) {
345 0 : ret = errno;
346 0 : krb5_storage_free(sp);
347 0 : close(fd);
348 0 : krb5_set_error_message(context, ret,
349 0 : N_("seeking in keyfile: %s", ""),
350 : strerror(ret));
351 0 : return ret;
352 : }
353 :
354 0 : ret = krb5_ret_int32(sp, &len);
355 0 : if(ret) {
356 0 : krb5_storage_free(sp);
357 0 : close(fd);
358 0 : return ret;
359 : }
360 : }
361 :
362 : /*
363 : * Make sure we don't add the entry twice, assumes the DES
364 : * encryption types are all the same key.
365 : */
366 0 : if (len > 0) {
367 : int32_t kvno;
368 : int i;
369 :
370 0 : for (i = 0; i < len; i++) {
371 0 : ret = krb5_ret_int32(sp, &kvno);
372 0 : if (ret) {
373 0 : krb5_set_error_message (context, ret,
374 0 : N_("Failed getting kvno from keyfile", ""));
375 0 : goto out;
376 : }
377 0 : if(krb5_storage_seek(sp, 8, SEEK_CUR) < 0) {
378 0 : ret = errno;
379 0 : krb5_set_error_message (context, ret,
380 0 : N_("Failed seeing in keyfile: %s", ""),
381 : strerror(ret));
382 0 : goto out;
383 : }
384 0 : if (kvno == entry->vno) {
385 0 : ret = 0;
386 0 : goto out;
387 : }
388 : }
389 : }
390 :
391 0 : len++;
392 :
393 0 : if(krb5_storage_seek(sp, 0, SEEK_SET) < 0) {
394 0 : ret = errno;
395 0 : krb5_set_error_message (context, ret,
396 0 : N_("Failed seeing in keyfile: %s", ""),
397 : strerror(ret));
398 0 : goto out;
399 : }
400 :
401 0 : ret = krb5_store_int32(sp, len);
402 0 : if(ret) {
403 0 : ret = errno;
404 0 : krb5_set_error_message (context, ret,
405 0 : N_("keytab keyfile failed new length", ""));
406 0 : goto out;
407 : }
408 :
409 0 : if(krb5_storage_seek(sp, (len - 1) * (8 + 4), SEEK_CUR) < 0) {
410 0 : ret = errno;
411 0 : krb5_set_error_message (context, ret,
412 0 : N_("seek to end: %s", ""), strerror(ret));
413 0 : goto out;
414 : }
415 :
416 0 : ret = krb5_store_int32(sp, entry->vno);
417 0 : if(ret) {
418 0 : krb5_set_error_message(context, ret,
419 0 : N_("keytab keyfile failed store kvno", ""));
420 0 : goto out;
421 : }
422 0 : ret = krb5_storage_write(sp, entry->keyblock.keyvalue.data,
423 : entry->keyblock.keyvalue.length);
424 0 : if(ret != entry->keyblock.keyvalue.length) {
425 0 : if (ret < 0)
426 0 : ret = errno;
427 : else
428 0 : ret = ENOTTY;
429 0 : krb5_set_error_message(context, ret,
430 0 : N_("keytab keyfile failed to add key", ""));
431 0 : goto out;
432 : }
433 0 : ret = 0;
434 0 : out:
435 0 : krb5_storage_free(sp);
436 0 : close (fd);
437 0 : return ret;
438 : }
439 :
440 : const krb5_kt_ops krb5_akf_ops = {
441 : "AFSKEYFILE",
442 : akf_resolve,
443 : akf_get_name,
444 : akf_close,
445 : NULL, /* destroy */
446 : NULL, /* get */
447 : akf_start_seq_get,
448 : akf_next_entry,
449 : akf_end_seq_get,
450 : akf_add_entry,
451 : NULL, /* remove */
452 : NULL,
453 : 0
454 : };
455 :
456 : #endif /* HEIMDAL_SMALLER */
|