Merge from vendor branch LIBSTDC++:
[dragonfly.git] / crypto / heimdal / lib / krb5 / fcache.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: fcache.c,v 1.34 2002/04/18 14:01:29 joda Exp $");
37
38 typedef struct krb5_fcache{
39     char *filename;
40     int version;
41 }krb5_fcache;
42
43 struct fcc_cursor {
44     int fd;
45     krb5_storage *sp;
46 };
47
48 #define KRB5_FCC_FVNO_1 1
49 #define KRB5_FCC_FVNO_2 2
50 #define KRB5_FCC_FVNO_3 3
51 #define KRB5_FCC_FVNO_4 4
52
53 #define FCC_TAG_DELTATIME 1
54
55 #define FCACHE(X) ((krb5_fcache*)(X)->data.data)
56
57 #define FILENAME(X) (FCACHE(X)->filename)
58
59 #define FCC_CURSOR(C) ((struct fcc_cursor*)(C))
60
61 static const char*
62 fcc_get_name(krb5_context context,
63              krb5_ccache id)
64 {
65     return FILENAME(id);
66 }
67
68 static krb5_error_code
69 fcc_resolve(krb5_context context, krb5_ccache *id, const char *res)
70 {
71     krb5_fcache *f;
72     f = malloc(sizeof(*f));
73     if(f == NULL) {
74         krb5_set_error_string(context, "malloc: out of memory");
75         return KRB5_CC_NOMEM;
76     }
77     f->filename = strdup(res);
78     if(f->filename == NULL){
79         free(f);
80         krb5_set_error_string(context, "malloc: out of memory");
81         return KRB5_CC_NOMEM;
82     }
83     f->version = 0;
84     (*id)->data.data = f;
85     (*id)->data.length = sizeof(*f);
86     return 0;
87 }
88
89 /*
90  * Try to scrub the contents of `filename' safely.
91  */
92
93 static int
94 scrub_file (int fd)
95 {
96     off_t pos;
97     char buf[128];
98
99     pos = lseek(fd, 0, SEEK_END);
100     if (pos < 0)
101         return errno;
102     if (lseek(fd, 0, SEEK_SET) < 0)
103         return errno;
104     memset(buf, 0, sizeof(buf));
105     while(pos > 0) {
106         ssize_t tmp = write(fd, buf, min(sizeof(buf), pos));
107
108         if (tmp < 0)
109             return errno;
110         pos -= tmp;
111     }
112     fsync (fd);
113     return 0;
114 }
115
116 /*
117  * Erase `filename' if it exists, trying to remove the contents if
118  * it's `safe'.  We always try to remove the file, it it exists.  It's
119  * only overwritten if it's a regular file (not a symlink and not a
120  * hardlink)
121  */
122
123 static krb5_error_code
124 erase_file(const char *filename)
125 {
126     int fd;
127     struct stat sb1, sb2;
128     int ret;
129
130     ret = lstat (filename, &sb1);
131     if (ret < 0)
132         return errno;
133
134     fd = open(filename, O_RDWR | O_BINARY);
135     if(fd < 0) {
136         if(errno == ENOENT)
137             return 0;
138         else
139             return errno;
140     }
141     if (unlink(filename) < 0) {
142         close (fd);
143         return errno;
144     }
145
146     ret = fstat (fd, &sb2);
147     if (ret < 0) {
148         close (fd);
149         return errno;
150     }
151
152     /* check if someone was playing with symlinks */
153
154     if (sb1.st_dev != sb2.st_dev || sb1.st_ino != sb2.st_ino) {
155         close (fd);
156         return EPERM;
157     }
158
159     /* there are still hard links to this file */
160
161     if (sb2.st_nlink != 0) {
162         close (fd);
163         return 0;
164     }
165
166     ret = scrub_file (fd);
167     close (fd);
168     return ret;
169 }
170
171 static krb5_error_code
172 fcc_gen_new(krb5_context context, krb5_ccache *id)
173 {
174     krb5_fcache *f;
175     int fd;
176     char *file;
177
178     f = malloc(sizeof(*f));
179     if(f == NULL) {
180         krb5_set_error_string(context, "malloc: out of memory");
181         return KRB5_CC_NOMEM;
182     }
183     asprintf (&file, "%sXXXXXX", KRB5_DEFAULT_CCFILE_ROOT);
184     if(file == NULL) {
185         free(f);
186         krb5_set_error_string(context, "malloc: out of memory");
187         return KRB5_CC_NOMEM;
188     }
189     fd = mkstemp(file);
190     if(fd < 0) {
191         free(f);
192         free(file);
193         krb5_set_error_string(context, "mkstemp %s", file);
194         return errno;
195     }
196     close(fd);
197     f->filename = file;
198     f->version = 0;
199     (*id)->data.data = f;
200     (*id)->data.length = sizeof(*f);
201     return 0;
202 }
203
204 static void
205 storage_set_flags(krb5_context context, krb5_storage *sp, int vno)
206 {
207     int flags = 0;
208     switch(vno) {
209     case KRB5_FCC_FVNO_1:
210         flags |= KRB5_STORAGE_PRINCIPAL_WRONG_NUM_COMPONENTS;
211         flags |= KRB5_STORAGE_PRINCIPAL_NO_NAME_TYPE;
212         flags |= KRB5_STORAGE_HOST_BYTEORDER;
213         break;
214     case KRB5_FCC_FVNO_2:
215         flags |= KRB5_STORAGE_HOST_BYTEORDER;
216         break;
217     case KRB5_FCC_FVNO_3:
218         flags |= KRB5_STORAGE_KEYBLOCK_KEYTYPE_TWICE;
219         break;
220     case KRB5_FCC_FVNO_4:
221         break;
222     default:
223         krb5_abortx(context, 
224                     "storage_set_flags called with bad vno (%x)", vno);
225     }
226     krb5_storage_set_flags(sp, flags);
227 }
228
229 static krb5_error_code
230 fcc_initialize(krb5_context context,
231                krb5_ccache id,
232                krb5_principal primary_principal)
233 {
234     krb5_fcache *f = FCACHE(id);
235     int ret = 0;
236     int fd;
237     char *filename = f->filename;
238
239     unlink (filename);
240   
241     fd = open(filename, O_RDWR | O_CREAT | O_EXCL | O_BINARY, 0600);
242     if(fd == -1) {
243         ret = errno;
244         krb5_set_error_string(context, "open(%s): %s", filename,
245                               strerror(ret));
246         return ret;
247     }
248     {
249         krb5_storage *sp;    
250         sp = krb5_storage_from_fd(fd);
251         krb5_storage_set_eof_code(sp, KRB5_CC_END);
252         if(context->fcache_vno != 0)
253             f->version = context->fcache_vno;
254         else
255             f->version = KRB5_FCC_FVNO_4;
256         ret |= krb5_store_int8(sp, 5);
257         ret |= krb5_store_int8(sp, f->version);
258         storage_set_flags(context, sp, f->version);
259         if(f->version == KRB5_FCC_FVNO_4 && ret == 0) {
260             /* V4 stuff */
261             if (context->kdc_sec_offset) {
262                 ret |= krb5_store_int16 (sp, 12); /* length */
263                 ret |= krb5_store_int16 (sp, FCC_TAG_DELTATIME); /* Tag */
264                 ret |= krb5_store_int16 (sp, 8); /* length of data */
265                 ret |= krb5_store_int32 (sp, context->kdc_sec_offset);
266                 ret |= krb5_store_int32 (sp, context->kdc_usec_offset);
267             } else {
268                 ret |= krb5_store_int16 (sp, 0);
269             }
270         }
271         ret |= krb5_store_principal(sp, primary_principal);
272         krb5_storage_free(sp);
273     }
274     if(close(fd) < 0)
275         if (ret == 0) {
276             ret = errno;
277             krb5_set_error_string (context, "close %s: %s", filename,
278                                    strerror(ret));
279         }
280         
281     return ret;
282 }
283
284 static krb5_error_code
285 fcc_close(krb5_context context,
286           krb5_ccache id)
287 {
288     free (FILENAME(id));
289     krb5_data_free(&id->data);
290     return 0;
291 }
292
293 static krb5_error_code
294 fcc_destroy(krb5_context context,
295             krb5_ccache id)
296 {
297     char *f;
298     f = FILENAME(id);
299
300     erase_file(f);
301   
302     return 0;
303 }
304
305 static krb5_error_code
306 fcc_store_cred(krb5_context context,
307                krb5_ccache id,
308                krb5_creds *creds)
309 {
310     int ret;
311     int fd;
312     char *f;
313
314     f = FILENAME(id);
315
316     fd = open(f, O_WRONLY | O_APPEND | O_BINARY);
317     if(fd < 0) {
318         ret = errno;
319         krb5_set_error_string (context, "open(%s): %s", f, strerror(ret));
320         return ret;
321     }
322     {
323         krb5_storage *sp;
324         sp = krb5_storage_from_fd(fd);
325         krb5_storage_set_eof_code(sp, KRB5_CC_END);
326         storage_set_flags(context, sp, FCACHE(id)->version);
327         ret = krb5_store_creds(sp, creds);
328         krb5_storage_free(sp);
329     }
330     if (close(fd) < 0)
331         if (ret == 0) {
332             ret = errno;
333             krb5_set_error_string (context, "close %s: %s", f, strerror(ret));
334         }
335     return ret;
336 }
337
338 static krb5_error_code
339 fcc_read_cred (krb5_context context,
340                krb5_fcache *fc,
341                krb5_storage *sp,
342                krb5_creds *creds)
343 {
344     krb5_error_code ret;
345
346     storage_set_flags(context, sp, fc->version);
347     
348     ret = krb5_ret_creds(sp, creds);
349     return ret;
350 }
351
352 static krb5_error_code
353 init_fcc (krb5_context context,
354           krb5_fcache *fcache,
355           krb5_storage **ret_sp,
356           int *ret_fd)
357 {
358     int fd;
359     int8_t pvno, tag;
360     krb5_storage *sp;
361     krb5_error_code ret;
362
363     fd = open(fcache->filename, O_RDONLY | O_BINARY);
364     if(fd < 0) {
365         ret = errno;
366         krb5_set_error_string(context, "open(%s): %s", fcache->filename,
367                               strerror(ret));
368         return ret;
369     }
370     sp = krb5_storage_from_fd(fd);
371     krb5_storage_set_eof_code(sp, KRB5_CC_END);
372     ret = krb5_ret_int8(sp, &pvno);
373     if(ret == KRB5_CC_END)
374         return ENOENT;
375     if(ret)
376         return ret;
377     if(pvno != 5) {
378         krb5_storage_free(sp);
379         close(fd);
380         return KRB5_CCACHE_BADVNO;
381     }
382     krb5_ret_int8(sp, &tag); /* should not be host byte order */
383     fcache->version = tag;
384     storage_set_flags(context, sp, fcache->version);
385     switch (tag) {
386     case KRB5_FCC_FVNO_4: {
387         int16_t length;
388
389         krb5_ret_int16 (sp, &length);
390         while(length > 0) {
391             int16_t tag, data_len;
392             int i;
393             int8_t dummy;
394
395             krb5_ret_int16 (sp, &tag);
396             krb5_ret_int16 (sp, &data_len);
397             switch (tag) {
398             case FCC_TAG_DELTATIME :
399                 krb5_ret_int32 (sp, &context->kdc_sec_offset);
400                 krb5_ret_int32 (sp, &context->kdc_usec_offset);
401                 break;
402             default :
403                 for (i = 0; i < data_len; ++i)
404                     krb5_ret_int8 (sp, &dummy);
405                 break;
406             }
407             length -= 4 + data_len;
408         }
409         break;
410     }
411     case KRB5_FCC_FVNO_3:
412     case KRB5_FCC_FVNO_2:
413     case KRB5_FCC_FVNO_1:
414         break;
415     default :
416         krb5_storage_free (sp);
417         close (fd);
418         return KRB5_CCACHE_BADVNO;
419     }
420     *ret_sp = sp;
421     *ret_fd = fd;
422     return 0;
423 }
424
425 static krb5_error_code
426 fcc_get_principal(krb5_context context,
427                   krb5_ccache id,
428                   krb5_principal *principal)
429 {
430     krb5_error_code ret;
431     krb5_fcache *f = FCACHE(id);
432     int fd;
433     krb5_storage *sp;
434
435     ret = init_fcc (context, f, &sp, &fd);
436     if (ret)
437         return ret;
438     ret = krb5_ret_principal(sp, principal);
439     krb5_storage_free(sp);
440     close(fd);
441     return ret;
442 }
443
444 static krb5_error_code
445 fcc_get_first (krb5_context context,
446                krb5_ccache id,
447                krb5_cc_cursor *cursor)
448 {
449     krb5_error_code ret;
450     krb5_principal principal;
451     krb5_fcache *f = FCACHE(id);
452
453     *cursor = malloc(sizeof(struct fcc_cursor));
454
455     ret = init_fcc (context, f, &FCC_CURSOR(*cursor)->sp, 
456                     &FCC_CURSOR(*cursor)->fd);
457     if (ret)
458         return ret;
459     krb5_ret_principal (FCC_CURSOR(*cursor)->sp, &principal);
460     krb5_free_principal (context, principal);
461     return 0;
462 }
463
464 static krb5_error_code
465 fcc_get_next (krb5_context context,
466               krb5_ccache id,
467               krb5_cc_cursor *cursor,
468               krb5_creds *creds)
469 {
470     return fcc_read_cred (context, FCACHE(id), FCC_CURSOR(*cursor)->sp, creds);
471 }
472
473 static krb5_error_code
474 fcc_end_get (krb5_context context,
475              krb5_ccache id,
476              krb5_cc_cursor *cursor)
477 {
478     krb5_storage_free(FCC_CURSOR(*cursor)->sp);
479     close (FCC_CURSOR(*cursor)->fd);
480     free(*cursor);
481     return 0;
482 }
483
484 static krb5_error_code
485 fcc_remove_cred(krb5_context context,
486                  krb5_ccache id,
487                  krb5_flags which,
488                  krb5_creds *cred)
489 {
490     return 0; /* XXX */
491 }
492
493 static krb5_error_code
494 fcc_set_flags(krb5_context context,
495               krb5_ccache id,
496               krb5_flags flags)
497 {
498     return 0; /* XXX */
499 }
500
501 static krb5_error_code
502 fcc_get_version(krb5_context context,
503                 krb5_ccache id)
504 {
505     return FCACHE(id)->version;
506 }
507                     
508 const krb5_cc_ops krb5_fcc_ops = {
509     "FILE",
510     fcc_get_name,
511     fcc_resolve,
512     fcc_gen_new,
513     fcc_initialize,
514     fcc_destroy,
515     fcc_close,
516     fcc_store_cred,
517     NULL, /* fcc_retrieve */
518     fcc_get_principal,
519     fcc_get_first,
520     fcc_get_next,
521     fcc_end_get,
522     fcc_remove_cred,
523     fcc_set_flags,
524     fcc_get_version
525 };