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