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