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