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