1/*
2 * Copyright (c) 1997-2001 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#include <vis.h>
36
37RCSID("$Id: replay.c 17047 2006-04-10 17:13:49Z lha $");
38
39struct krb5_rcache_data {
40    char *name;
41};
42
43krb5_error_code KRB5_LIB_FUNCTION
44krb5_rc_resolve(krb5_context context,
45		krb5_rcache id,
46		const char *name)
47{
48    id->name = strdup(name);
49    if(id->name == NULL) {
50	krb5_set_error_string (context, "malloc: out of memory");
51	return KRB5_RC_MALLOC;
52    }
53    return 0;
54}
55
56krb5_error_code KRB5_LIB_FUNCTION
57krb5_rc_resolve_type(krb5_context context,
58		     krb5_rcache *id,
59		     const char *type)
60{
61    *id = NULL;
62    if(strcmp(type, "FILE")) {
63	krb5_set_error_string (context, "replay cache type %s not supported",
64			       type);
65	return KRB5_RC_TYPE_NOTFOUND;
66    }
67    *id = calloc(1, sizeof(**id));
68    if(*id == NULL) {
69	krb5_set_error_string (context, "malloc: out of memory");
70	return KRB5_RC_MALLOC;
71    }
72    return 0;
73}
74
75krb5_error_code KRB5_LIB_FUNCTION
76krb5_rc_resolve_full(krb5_context context,
77		     krb5_rcache *id,
78		     const char *string_name)
79{
80    krb5_error_code ret;
81
82    *id = NULL;
83
84    if(strncmp(string_name, "FILE:", 5)) {
85	krb5_set_error_string (context, "replay cache type %s not supported",
86			       string_name);
87	return KRB5_RC_TYPE_NOTFOUND;
88    }
89    ret = krb5_rc_resolve_type(context, id, "FILE");
90    if(ret)
91	return ret;
92    ret = krb5_rc_resolve(context, *id, string_name + 5);
93    if (ret) {
94	krb5_rc_close(context, *id);
95	*id = NULL;
96    }
97    return ret;
98}
99
100const char* KRB5_LIB_FUNCTION
101krb5_rc_default_name(krb5_context context)
102{
103    return "FILE:/var/run/default_rcache";
104}
105
106const char* KRB5_LIB_FUNCTION
107krb5_rc_default_type(krb5_context context)
108{
109    return "FILE";
110}
111
112krb5_error_code KRB5_LIB_FUNCTION
113krb5_rc_default(krb5_context context,
114		krb5_rcache *id)
115{
116    return krb5_rc_resolve_full(context, id, krb5_rc_default_name(context));
117}
118
119struct rc_entry{
120    time_t stamp;
121    unsigned char data[16];
122};
123
124krb5_error_code KRB5_LIB_FUNCTION
125krb5_rc_initialize(krb5_context context,
126		   krb5_rcache id,
127		   krb5_deltat auth_lifespan)
128{
129    FILE *f = fopen(id->name, "w");
130    struct rc_entry tmp;
131    int ret;
132
133    if(f == NULL) {
134	ret = errno;
135	krb5_set_error_string (context, "open(%s): %s", id->name,
136			       strerror(ret));
137	return ret;
138    }
139    tmp.stamp = auth_lifespan;
140    fwrite(&tmp, 1, sizeof(tmp), f);
141    fclose(f);
142    return 0;
143}
144
145krb5_error_code KRB5_LIB_FUNCTION
146krb5_rc_recover(krb5_context context,
147		krb5_rcache id)
148{
149    return 0;
150}
151
152krb5_error_code KRB5_LIB_FUNCTION
153krb5_rc_destroy(krb5_context context,
154		krb5_rcache id)
155{
156    int ret;
157
158    if(remove(id->name) < 0) {
159	ret = errno;
160	krb5_set_error_string (context, "remove(%s): %s", id->name,
161			       strerror(ret));
162	return ret;
163    }
164    return krb5_rc_close(context, id);
165}
166
167krb5_error_code KRB5_LIB_FUNCTION
168krb5_rc_close(krb5_context context,
169	      krb5_rcache id)
170{
171    free(id->name);
172    free(id);
173    return 0;
174}
175
176static void
177checksum_authenticator(Authenticator *auth, void *data)
178{
179    MD5_CTX md5;
180    int i;
181
182    MD5_Init (&md5);
183    MD5_Update (&md5, auth->crealm, strlen(auth->crealm));
184    for(i = 0; i < auth->cname.name_string.len; i++)
185	MD5_Update(&md5, auth->cname.name_string.val[i],
186		   strlen(auth->cname.name_string.val[i]));
187    MD5_Update (&md5, &auth->ctime, sizeof(auth->ctime));
188    MD5_Update (&md5, &auth->cusec, sizeof(auth->cusec));
189    MD5_Final (data, &md5);
190}
191
192krb5_error_code KRB5_LIB_FUNCTION
193krb5_rc_store(krb5_context context,
194	      krb5_rcache id,
195	      krb5_donot_replay *rep)
196{
197    struct rc_entry ent, tmp;
198    time_t t;
199    FILE *f;
200    int ret;
201
202    ent.stamp = time(NULL);
203    checksum_authenticator(rep, ent.data);
204    f = fopen(id->name, "r");
205    if(f == NULL) {
206	ret = errno;
207	krb5_set_error_string (context, "open(%s): %s", id->name,
208			       strerror(ret));
209	return ret;
210    }
211    fread(&tmp, sizeof(ent), 1, f);
212    t = ent.stamp - tmp.stamp;
213    while(fread(&tmp, sizeof(ent), 1, f)){
214	if(tmp.stamp < t)
215	    continue;
216	if(memcmp(tmp.data, ent.data, sizeof(ent.data)) == 0){
217	    fclose(f);
218	    krb5_clear_error_string (context);
219	    return KRB5_RC_REPLAY;
220	}
221    }
222    if(ferror(f)){
223	ret = errno;
224	fclose(f);
225	krb5_set_error_string (context, "%s: %s", id->name, strerror(ret));
226	return ret;
227    }
228    fclose(f);
229    f = fopen(id->name, "a");
230    if(f == NULL) {
231	krb5_set_error_string (context, "open(%s): %s", id->name,
232			       strerror(errno));
233	return KRB5_RC_IO_UNKNOWN;
234    }
235    fwrite(&ent, 1, sizeof(ent), f);
236    fclose(f);
237    return 0;
238}
239
240krb5_error_code KRB5_LIB_FUNCTION
241krb5_rc_expunge(krb5_context context,
242		krb5_rcache id)
243{
244    return 0;
245}
246
247krb5_error_code KRB5_LIB_FUNCTION
248krb5_rc_get_lifespan(krb5_context context,
249		     krb5_rcache id,
250		     krb5_deltat *auth_lifespan)
251{
252    FILE *f = fopen(id->name, "r");
253    int r;
254    struct rc_entry ent;
255    r = fread(&ent, sizeof(ent), 1, f);
256    fclose(f);
257    if(r){
258	*auth_lifespan = ent.stamp;
259	return 0;
260    }
261    krb5_clear_error_string (context);
262    return KRB5_RC_IO_UNKNOWN;
263}
264
265const char* KRB5_LIB_FUNCTION
266krb5_rc_get_name(krb5_context context,
267		 krb5_rcache id)
268{
269    return id->name;
270}
271
272const char* KRB5_LIB_FUNCTION
273krb5_rc_get_type(krb5_context context,
274		 krb5_rcache id)
275{
276    return "FILE";
277}
278
279krb5_error_code KRB5_LIB_FUNCTION
280krb5_get_server_rcache(krb5_context context,
281		       const krb5_data *piece,
282		       krb5_rcache *id)
283{
284    krb5_rcache rcache;
285    krb5_error_code ret;
286
287    char *tmp = malloc(4 * piece->length + 1);
288    char *name;
289
290    if(tmp == NULL) {
291	krb5_set_error_string (context, "malloc: out of memory");
292	return ENOMEM;
293    }
294    strvisx(tmp, piece->data, piece->length, VIS_WHITE | VIS_OCTAL);
295#ifdef HAVE_GETEUID
296    asprintf(&name, "FILE:rc_%s_%u", tmp, (unsigned)geteuid());
297#else
298    asprintf(&name, "FILE:rc_%s", tmp);
299#endif
300    free(tmp);
301    if(name == NULL) {
302	krb5_set_error_string (context, "malloc: out of memory");
303	return ENOMEM;
304    }
305
306    ret = krb5_rc_resolve_full(context, &rcache, name);
307    free(name);
308    if(ret)
309	return ret;
310    *id = rcache;
311    return ret;
312}
313