remove gcc34
[dragonfly.git] / crypto / heimdal-0.6.3 / lib / krb5 / keytab_krb4.c
1 /*
2  * Copyright (c) 1997 - 2002 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_krb4.c,v 1.10 2002/04/18 14:04:46 joda Exp $");
37
38 struct krb4_kt_data {
39     char *filename;
40 };
41
42 static krb5_error_code
43 krb4_kt_resolve(krb5_context context, const char *name, krb5_keytab id)
44 {
45     struct krb4_kt_data *d;
46
47     d = malloc (sizeof(*d));
48     if (d == NULL) {
49         krb5_set_error_string (context, "malloc: out of memory");
50         return ENOMEM;
51     }
52     d->filename = strdup (name);
53     if (d->filename == NULL) {
54         free(d);
55         krb5_set_error_string (context, "malloc: out of memory");
56         return ENOMEM;
57     }
58     id->data = d;
59     return 0;
60 }
61
62 static krb5_error_code
63 krb4_kt_get_name (krb5_context context,
64                   krb5_keytab id,
65                   char *name,
66                   size_t name_sz)
67 {
68     struct krb4_kt_data *d = id->data;
69
70     strlcpy (name, d->filename, name_sz);
71     return 0;
72 }
73
74 static krb5_error_code
75 krb4_kt_close (krb5_context context,
76                krb5_keytab id)
77 {
78     struct krb4_kt_data *d = id->data;
79
80     free (d->filename);
81     free (d);
82     return 0;
83 }
84
85 struct krb4_cursor_extra_data {
86     krb5_keytab_entry entry;
87     int num;
88 };
89
90 static int
91 open_flock(const char *filename, int flags, int mode)
92 {
93     int lock_mode;
94     int tries = 0;
95     int fd = open(filename, flags, mode);
96     if(fd < 0)
97         return fd;
98     if((flags & O_ACCMODE) == O_RDONLY)
99         lock_mode = LOCK_SH | LOCK_NB;
100     else
101         lock_mode = LOCK_EX | LOCK_NB;
102     while(flock(fd, lock_mode) < 0) {
103         if(++tries < 5) {
104             sleep(1);
105         } else {
106             close(fd);
107             return -1;
108         }
109     }
110     return fd;
111 }
112
113
114
115 static krb5_error_code
116 krb4_kt_start_seq_get_int (krb5_context context,
117                            krb5_keytab id,
118                            int flags,
119                            krb5_kt_cursor *c)
120 {
121     struct krb4_kt_data *d = id->data;
122     struct krb4_cursor_extra_data *ed;
123     int ret;
124
125     ed = malloc (sizeof(*ed));
126     if (ed == NULL) {
127         krb5_set_error_string (context, "malloc: out of memory");
128         return ENOMEM;
129     }
130     ed->entry.principal = NULL;
131     ed->num = -1;
132     c->data = ed;
133     c->fd = open_flock (d->filename, flags, 0);
134     if (c->fd < 0) {
135         ret = errno;
136         free (ed);
137         krb5_set_error_string(context, "open(%s): %s", d->filename,
138                               strerror(ret));
139         return ret;
140     }
141     c->sp = krb5_storage_from_fd(c->fd);
142     krb5_storage_set_eof_code(c->sp, KRB5_KT_END);
143     return 0;
144 }
145
146 static krb5_error_code
147 krb4_kt_start_seq_get (krb5_context context,
148                        krb5_keytab id,
149                        krb5_kt_cursor *c)
150 {
151     return krb4_kt_start_seq_get_int (context, id, O_BINARY | O_RDONLY, c);
152 }
153
154 static krb5_error_code
155 read_v4_entry (krb5_context context,
156                struct krb4_kt_data *d,
157                krb5_kt_cursor *c,
158                struct krb4_cursor_extra_data *ed)
159 {
160     krb5_error_code ret;
161     char *service, *instance, *realm;
162     int8_t kvno;
163     des_cblock key;
164
165     ret = krb5_ret_stringz(c->sp, &service);
166     if (ret)
167         return ret;
168     ret = krb5_ret_stringz(c->sp, &instance);
169     if (ret) {
170         free (service);
171         return ret;
172     }
173     ret = krb5_ret_stringz(c->sp, &realm);
174     if (ret) {
175         free (service);
176         free (instance);
177         return ret;
178     }
179     ret = krb5_425_conv_principal (context, service, instance, realm,
180                                    &ed->entry.principal);
181     free (service);
182     free (instance);
183     free (realm);
184     if (ret)
185         return ret;
186     ret = krb5_ret_int8(c->sp, &kvno);
187     if (ret) {
188         krb5_free_principal (context, ed->entry.principal);
189         return ret;
190     }
191     ret = krb5_storage_read(c->sp, key, 8);
192     if (ret < 0) {
193         krb5_free_principal(context, ed->entry.principal);
194         return ret;
195     }
196     if (ret < 8) {
197         krb5_free_principal(context, ed->entry.principal);
198         return EINVAL;
199     }
200     ed->entry.vno = kvno;
201     ret = krb5_data_copy (&ed->entry.keyblock.keyvalue,
202                           key, 8);
203     if (ret)
204         return ret;
205     ed->entry.timestamp = time(NULL);
206     ed->num = 0;
207     return 0;
208 }
209
210 static krb5_error_code
211 krb4_kt_next_entry (krb5_context context,
212                     krb5_keytab id,
213                     krb5_keytab_entry *entry,
214                     krb5_kt_cursor *c)
215 {
216     krb5_error_code ret;
217     struct krb4_kt_data *d = id->data;
218     struct krb4_cursor_extra_data *ed = c->data;
219     const krb5_enctype keytypes[] = {ETYPE_DES_CBC_MD5,
220                                      ETYPE_DES_CBC_MD4,
221                                      ETYPE_DES_CBC_CRC};
222
223     if (ed->num == -1) {
224         ret = read_v4_entry (context, d, c, ed);
225         if (ret)
226             return ret;
227     }
228     ret = krb5_kt_copy_entry_contents (context,
229                                        &ed->entry,
230                                        entry);
231     if (ret)
232         return ret;
233     entry->keyblock.keytype = keytypes[ed->num];
234     if (++ed->num == 3) {
235         krb5_kt_free_entry (context, &ed->entry);
236         ed->num = -1;
237     }
238     return 0;
239 }
240
241 static krb5_error_code
242 krb4_kt_end_seq_get (krb5_context context,
243                      krb5_keytab id,
244                      krb5_kt_cursor *c)
245 {
246     struct krb4_cursor_extra_data *ed = c->data;
247
248     krb5_storage_free (c->sp);
249     if (ed->num != -1)
250         krb5_kt_free_entry (context, &ed->entry);
251     free (c->data);
252     close (c->fd);
253     return 0;
254 }
255
256 static krb5_error_code
257 krb4_store_keytab_entry(krb5_context context, 
258                         krb5_keytab_entry *entry, 
259                         krb5_storage *sp)
260 {
261     krb5_error_code ret;
262 #define ANAME_SZ 40
263 #define INST_SZ 40
264 #define REALM_SZ 40
265     char service[ANAME_SZ];
266     char instance[INST_SZ];
267     char realm[REALM_SZ];
268     ret = krb5_524_conv_principal (context, entry->principal,
269                                    service, instance, realm);
270     if (ret)
271         return ret;
272     if (entry->keyblock.keyvalue.length == 8
273         && entry->keyblock.keytype == ETYPE_DES_CBC_MD5) {
274         ret = krb5_store_stringz(sp, service);
275         ret = krb5_store_stringz(sp, instance);
276         ret = krb5_store_stringz(sp, realm);
277         ret = krb5_store_int8(sp, entry->vno);
278         ret = krb5_storage_write(sp, entry->keyblock.keyvalue.data, 8);
279     }
280     return 0;
281 }
282
283 static krb5_error_code
284 krb4_kt_add_entry (krb5_context context,
285                    krb5_keytab id,
286                    krb5_keytab_entry *entry)
287 {
288     struct krb4_kt_data *d = id->data;
289     krb5_storage *sp;
290     krb5_error_code ret;
291     int fd;
292
293     fd = open_flock (d->filename, O_WRONLY | O_APPEND | O_BINARY, 0);
294     if (fd < 0) {
295         fd = open_flock (d->filename,
296                    O_WRONLY | O_APPEND | O_BINARY | O_CREAT, 0600);
297         if (fd < 0) {
298             ret = errno;
299             krb5_set_error_string(context, "open(%s): %s", d->filename,
300                                   strerror(ret));
301             return ret;
302         }
303     }
304     sp = krb5_storage_from_fd(fd);
305     krb5_storage_set_eof_code(sp, KRB5_KT_END);
306     if(sp == NULL) {
307         close(fd);
308         return ENOMEM;
309     }
310     ret = krb4_store_keytab_entry(context, entry, sp);
311     krb5_storage_free(sp);
312     if(close (fd) < 0)
313         return errno;
314     return ret;
315 }
316
317 static krb5_error_code
318 krb4_kt_remove_entry(krb5_context context,
319                  krb5_keytab id,
320                  krb5_keytab_entry *entry)
321 {
322     struct krb4_kt_data *d = id->data;
323     krb5_error_code ret;
324     krb5_keytab_entry e;
325     krb5_kt_cursor cursor;
326     krb5_storage *sp;
327     int remove_flag = 0;
328     
329     sp = krb5_storage_emem();
330     ret = krb5_kt_start_seq_get(context, id, &cursor);
331     while(krb5_kt_next_entry(context, id, &e, &cursor) == 0) {
332         if(!krb5_kt_compare(context, &e, entry->principal, 
333                             entry->vno, entry->keyblock.keytype)) {
334             ret = krb4_store_keytab_entry(context, &e, sp);
335             if(ret) {
336                 krb5_storage_free(sp);
337                 return ret;
338             }
339         } else
340             remove_flag = 1;
341     }
342     krb5_kt_end_seq_get(context, id, &cursor);
343     if(remove_flag) {
344         int fd;
345         unsigned char buf[1024];
346         ssize_t n;
347         krb5_data data;
348         struct stat st;
349
350         krb5_storage_to_data(sp, &data);
351         krb5_storage_free(sp);
352
353         fd = open_flock (d->filename, O_RDWR | O_BINARY, 0);
354         if(fd < 0) {
355             memset(data.data, 0, data.length);
356             krb5_data_free(&data);
357             if(errno == EACCES || errno == EROFS) 
358                 return KRB5_KT_NOWRITE;
359             return errno;
360         }
361
362         if(write(fd, data.data, data.length) != data.length) {
363             memset(data.data, 0, data.length);
364             close(fd);
365             krb5_set_error_string(context, "failed writing to \"%s\"", d->filename);
366             return errno;
367         }
368         memset(data.data, 0, data.length);
369         if(fstat(fd, &st) < 0) {
370             close(fd);
371             krb5_set_error_string(context, "failed getting size of \"%s\"", d->filename);
372             return errno;
373         }
374         st.st_size -= data.length;
375         memset(buf, 0, sizeof(buf));
376         while(st.st_size > 0) {
377             n = min(st.st_size, sizeof(buf));
378             n = write(fd, buf, n);
379             if(n <= 0) {
380                 close(fd);
381                 krb5_set_error_string(context, "failed writing to \"%s\"", d->filename);
382                 return errno;
383                 
384             }
385             st.st_size -= n;
386         }
387         if(ftruncate(fd, data.length) < 0) {
388             close(fd);
389             krb5_set_error_string(context, "failed truncating \"%s\"", d->filename);
390             return errno;
391         }
392         krb5_data_free(&data);
393         if(close(fd) < 0) {
394             krb5_set_error_string(context, "error closing \"%s\"", d->filename);
395             return errno;
396         }
397         return 0;
398     } else
399         return KRB5_KT_NOTFOUND;
400 }
401
402
403 const krb5_kt_ops krb4_fkt_ops = {
404     "krb4",
405     krb4_kt_resolve,
406     krb4_kt_get_name,
407     krb4_kt_close,
408     NULL,                       /* get */
409     krb4_kt_start_seq_get,
410     krb4_kt_next_entry,
411     krb4_kt_end_seq_get,
412     krb4_kt_add_entry,          /* add_entry */
413     krb4_kt_remove_entry        /* remove_entry */
414 };
415
416 const krb5_kt_ops krb5_srvtab_fkt_ops = {
417     "SRVTAB",
418     krb4_kt_resolve,
419     krb4_kt_get_name,
420     krb4_kt_close,
421     NULL,                       /* get */
422     krb4_kt_start_seq_get,
423     krb4_kt_next_entry,
424     krb4_kt_end_seq_get,
425     krb4_kt_add_entry,          /* add_entry */
426     krb4_kt_remove_entry        /* remove_entry */
427 };