xref: /freebsd/crypto/heimdal/lib/krb5/fcache.c (revision 23f282aa31e9b6fceacd449020e936e98d6f2298)
1 /*
2  * Copyright (c) 1997, 1998, 1999 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.22 1999/12/02 17:05:09 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 static krb5_error_code
87 erase_file(const char *filename)
88 {
89     int fd;
90     off_t pos;
91     char buf[128];
92 
93     fd = open(filename, O_RDWR | O_BINARY);
94     if(fd < 0){
95 	if(errno == ENOENT)
96 	    return 0;
97 	else
98 	    return errno;
99     }
100     pos = lseek(fd, 0, SEEK_END);
101     lseek(fd, 0, SEEK_SET);
102     memset(buf, 0, sizeof(buf));
103     while(pos > 0)
104 	pos -= write(fd, buf, sizeof(buf));
105     close(fd);
106     unlink(filename);
107     return 0;
108 }
109 
110 static krb5_error_code
111 fcc_gen_new(krb5_context context, krb5_ccache *id)
112 {
113     krb5_fcache *f;
114     int fd;
115     char *file;
116     f = malloc(sizeof(*f));
117     if(f == NULL)
118 	return KRB5_CC_NOMEM;
119     asprintf(&file, "/tmp/krb5cc_XXXXXX"); /* XXX */
120     if(file == NULL) {
121 	free(f);
122 	return KRB5_CC_NOMEM;
123     }
124     fd = mkstemp(file);
125     if(fd < 0) {
126 	free(f);
127 	free(file);
128 	return errno;
129     }
130     close(fd);
131     f->filename = file;
132     f->version = 0;
133     (*id)->data.data = f;
134     (*id)->data.length = sizeof(*f);
135     return 0;
136 }
137 
138 static void
139 storage_set_flags(krb5_context context, krb5_storage *sp, int vno)
140 {
141     int flags = 0;
142     switch(vno) {
143     case KRB5_FCC_FVNO_1:
144 	flags |= KRB5_STORAGE_PRINCIPAL_WRONG_NUM_COMPONENTS;
145 	flags |= KRB5_STORAGE_PRINCIPAL_NO_NAME_TYPE;
146 	flags |= KRB5_STORAGE_HOST_BYTEORDER;
147 	break;
148     case KRB5_FCC_FVNO_2:
149 	flags |= KRB5_STORAGE_HOST_BYTEORDER;
150 	break;
151     case KRB5_FCC_FVNO_3:
152 	flags |= KRB5_STORAGE_KEYBLOCK_KEYTYPE_TWICE;
153 	break;
154     case KRB5_FCC_FVNO_4:
155 	break;
156     default:
157 	krb5_abortx(context,
158 		    "storage_set_flags called with bad vno (%x)", vno);
159     }
160     krb5_storage_set_flags(sp, flags);
161 }
162 
163 static krb5_error_code
164 fcc_initialize(krb5_context context,
165 	       krb5_ccache id,
166 	       krb5_principal primary_principal)
167 {
168     krb5_fcache *f = FCACHE(id);
169     int ret;
170     int fd;
171     char *filename = f->filename;
172 
173     if((ret = erase_file(filename)))
174 	return ret;
175 
176     fd = open(filename, O_RDWR | O_CREAT | O_EXCL | O_BINARY, 0600);
177     if(fd == -1)
178 	return errno;
179     {
180 	krb5_storage *sp;
181 	sp = krb5_storage_from_fd(fd);
182 	if(context->fcache_vno != 0)
183 	    f->version = context->fcache_vno;
184 	else
185 	    f->version = KRB5_FCC_FVNO_4;
186 	krb5_store_int8(sp, 5);
187 	krb5_store_int8(sp, f->version);
188 	storage_set_flags(context, sp, f->version);
189 	if(f->version == KRB5_FCC_FVNO_4) {
190 	    /* V4 stuff */
191 	    if (context->kdc_sec_offset) {
192 		krb5_store_int16 (sp, 12); /* length */
193 		krb5_store_int16 (sp, FCC_TAG_DELTATIME); /* Tag */
194 		krb5_store_int16 (sp, 8); /* length of data */
195 		krb5_store_int32 (sp, context->kdc_sec_offset);
196 		krb5_store_int32 (sp, context->kdc_usec_offset);
197 	    } else {
198 		krb5_store_int16 (sp, 0);
199 	    }
200 	}
201 	krb5_store_principal(sp, primary_principal);
202 	krb5_storage_free(sp);
203     }
204     close(fd);
205 
206     return 0;
207 }
208 
209 static krb5_error_code
210 fcc_close(krb5_context context,
211 	  krb5_ccache id)
212 {
213     free (FILENAME(id));
214     krb5_data_free(&id->data);
215     return 0;
216 }
217 
218 static krb5_error_code
219 fcc_destroy(krb5_context context,
220 	    krb5_ccache id)
221 {
222     char *f;
223     f = FILENAME(id);
224 
225     erase_file(f);
226 
227     return 0;
228 }
229 
230 static krb5_error_code
231 fcc_store_cred(krb5_context context,
232 	       krb5_ccache id,
233 	       krb5_creds *creds)
234 {
235     int fd;
236     char *f;
237 
238     f = FILENAME(id);
239 
240     fd = open(f, O_WRONLY | O_APPEND | O_BINARY);
241     if(fd < 0)
242 	return errno;
243     {
244 	krb5_storage *sp;
245 	sp = krb5_storage_from_fd(fd);
246 	storage_set_flags(context, sp, FCACHE(id)->version);
247 	krb5_store_creds(sp, creds);
248 	krb5_storage_free(sp);
249     }
250     close(fd);
251     return 0; /* XXX */
252 }
253 
254 static krb5_error_code
255 fcc_read_cred (krb5_context context,
256 	       krb5_fcache *fc,
257 	       krb5_storage *sp,
258 	       krb5_creds *creds)
259 {
260     krb5_error_code ret;
261 
262     storage_set_flags(context, sp, fc->version);
263 
264     ret = krb5_ret_creds(sp, creds);
265     return ret;
266 }
267 
268 static krb5_error_code
269 init_fcc (krb5_context context,
270 	  krb5_fcache *fcache,
271 	  krb5_storage **ret_sp,
272 	  int *ret_fd)
273 {
274     int fd;
275     int8_t pvno, tag;
276     krb5_storage *sp;
277 
278     fd = open(fcache->filename, O_RDONLY | O_BINARY);
279     if(fd < 0)
280 	return errno;
281     sp = krb5_storage_from_fd(fd);
282     krb5_ret_int8(sp, &pvno);
283     if(pvno != 5) {
284 	krb5_storage_free(sp);
285 	close(fd);
286 	return KRB5_CCACHE_BADVNO;
287     }
288     krb5_ret_int8(sp, &tag); /* should not be host byte order */
289     fcache->version = tag;
290     storage_set_flags(context, sp, fcache->version);
291     switch (tag) {
292     case KRB5_FCC_FVNO_4: {
293 	int16_t length;
294 
295 	krb5_ret_int16 (sp, &length);
296 	while(length > 0) {
297 	    int16_t tag, data_len;
298 	    int i;
299 	    int8_t dummy;
300 
301 	    krb5_ret_int16 (sp, &tag);
302 	    krb5_ret_int16 (sp, &data_len);
303 	    switch (tag) {
304 	    case FCC_TAG_DELTATIME :
305 		krb5_ret_int32 (sp, &context->kdc_sec_offset);
306 		krb5_ret_int32 (sp, &context->kdc_usec_offset);
307 		break;
308 	    default :
309 		for (i = 0; i < data_len; ++i)
310 		    krb5_ret_int8 (sp, &dummy);
311 		break;
312 	    }
313 	    length -= 4 + data_len;
314 	}
315 	break;
316     }
317     case KRB5_FCC_FVNO_3:
318     case KRB5_FCC_FVNO_2:
319     case KRB5_FCC_FVNO_1:
320 	break;
321     default :
322 	krb5_storage_free (sp);
323 	close (fd);
324 	return KRB5_CCACHE_BADVNO;
325     }
326     *ret_sp = sp;
327     *ret_fd = fd;
328     return 0;
329 }
330 
331 static krb5_error_code
332 fcc_get_principal(krb5_context context,
333 		  krb5_ccache id,
334 		  krb5_principal *principal)
335 {
336     krb5_error_code ret;
337     krb5_fcache *f = FCACHE(id);
338     int fd;
339     krb5_storage *sp;
340 
341     ret = init_fcc (context, f, &sp, &fd);
342     if (ret)
343 	return ret;
344     krb5_ret_principal(sp, principal);
345     krb5_storage_free(sp);
346     close(fd);
347     return 0;
348 }
349 
350 static krb5_error_code
351 fcc_get_first (krb5_context context,
352 	       krb5_ccache id,
353 	       krb5_cc_cursor *cursor)
354 {
355     krb5_error_code ret;
356     krb5_principal principal;
357     krb5_fcache *f = FCACHE(id);
358 
359     *cursor = malloc(sizeof(struct fcc_cursor));
360 
361     ret = init_fcc (context, f, &FCC_CURSOR(*cursor)->sp,
362 		    &FCC_CURSOR(*cursor)->fd);
363     if (ret)
364 	return ret;
365     krb5_ret_principal (FCC_CURSOR(*cursor)->sp, &principal);
366     krb5_free_principal (context, principal);
367     return 0;
368 }
369 
370 static krb5_error_code
371 fcc_get_next (krb5_context context,
372 	      krb5_ccache id,
373 	      krb5_cc_cursor *cursor,
374 	      krb5_creds *creds)
375 {
376     return fcc_read_cred (context, FCACHE(id), FCC_CURSOR(*cursor)->sp, creds);
377 }
378 
379 static krb5_error_code
380 fcc_end_get (krb5_context context,
381 	     krb5_ccache id,
382 	     krb5_cc_cursor *cursor)
383 {
384     krb5_storage_free(FCC_CURSOR(*cursor)->sp);
385     close (FCC_CURSOR(*cursor)->fd);
386     free(*cursor);
387     return 0;
388 }
389 
390 static krb5_error_code
391 fcc_remove_cred(krb5_context context,
392 		 krb5_ccache id,
393 		 krb5_flags which,
394 		 krb5_creds *cred)
395 {
396     return 0; /* XXX */
397 }
398 
399 static krb5_error_code
400 fcc_set_flags(krb5_context context,
401 	      krb5_ccache id,
402 	      krb5_flags flags)
403 {
404     return 0; /* XXX */
405 }
406 
407 static krb5_error_code
408 fcc_get_version(krb5_context context,
409 		krb5_ccache id)
410 {
411     return FCACHE(id)->version;
412 }
413 
414 const krb5_cc_ops krb5_fcc_ops = {
415     "FILE",
416     fcc_get_name,
417     fcc_resolve,
418     fcc_gen_new,
419     fcc_initialize,
420     fcc_destroy,
421     fcc_close,
422     fcc_store_cred,
423     NULL, /* fcc_retrieve */
424     fcc_get_principal,
425     fcc_get_first,
426     fcc_get_next,
427     fcc_end_get,
428     fcc_remove_cred,
429     fcc_set_flags,
430     fcc_get_version
431 };
432