1/*
2 * Copyright (c) 1997-2005 Kungliga Tekniska Högskolan
3 * (Royal Institute of Technology, Stockholm, Sweden).
4 *
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 *
11 * 1. Redistributions of source code must retain the above copyright
12 *    notice, this list of conditions and the following disclaimer.
13 *
14 * 2. Redistributions in binary form must reproduce the above copyright
15 *    notice, this list of conditions and the following disclaimer in the
16 *    documentation and/or other materials provided with the distribution.
17 *
18 * 3. Neither the name of the Institute nor the names of its contributors
19 *    may be used to endorse or promote products derived from this software
20 *    without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 * SUCH DAMAGE.
33 */
34
35#include "kdc_locl.h"
36
37/*
38 *
39 */
40
41void
42krb5_kdc_update_time(struct timeval *tv)
43{
44    if (tv == NULL)
45	gettimeofday(&_kdc_now, NULL);
46    else
47	_kdc_now = *tv;
48}
49
50static krb5_error_code
51kdc_as_req(krb5_context context,
52	   krb5_kdc_configuration *config,
53	   krb5_data *req_buffer,
54	   krb5_data *reply,
55	   const char *from,
56	   struct sockaddr *addr,
57	   size_t max_reply_size,
58	   int *claim)
59{
60    struct kdc_request_desc r;
61    krb5_error_code ret;
62    size_t len;
63
64    memset(&r, 0, sizeof(r));
65
66    ret = decode_AS_REQ(req_buffer->data, req_buffer->length, &r.req, &len);
67    if (ret)
68	return ret;
69
70    r.context = context;
71    r.config = config;
72    r.request.data = req_buffer->data;
73    r.request.length = req_buffer->length;
74
75    *claim = 1;
76
77    ret = _kdc_as_rep(&r, reply, from, addr, max_reply_size);
78    free_AS_REQ(&r.req);
79
80    return ret;
81}
82
83
84static krb5_error_code
85kdc_tgs_req(krb5_context context,
86	    krb5_kdc_configuration *config,
87	    krb5_data *req_buffer,
88	    krb5_data *reply,
89	    const char *from,
90	    struct sockaddr *addr,
91	    size_t max_reply_size,
92	    int *claim)
93{
94    struct kdc_request_desc r;
95    krb5_error_code ret;
96    size_t len;
97
98    memset(&r, 0, sizeof(r));
99
100    ret = decode_TGS_REQ(req_buffer->data, req_buffer->length, &r.req, &len);
101    if (ret)
102	return ret;
103
104    r.context = context;
105    r.config = config;
106    r.request.data = req_buffer->data;
107    r.request.length = req_buffer->length;
108
109    *claim = 1;
110
111    ret = _kdc_tgs_rep(&r, reply, from, addr, max_reply_size);
112
113    free_TGS_REQ(&r.req);
114
115    return ret;
116}
117
118#ifdef DIGEST
119
120static krb5_error_code
121kdc_digest(krb5_context context,
122	   krb5_kdc_configuration *config,
123	   krb5_data *req_buffer,
124	   krb5_data *reply,
125	   const char *from,
126	   struct sockaddr *addr,
127	   size_t max_reply_size,
128	   int *claim)
129{
130    DigestREQ digestreq;
131    krb5_error_code ret;
132    size_t len;
133
134    ret = decode_DigestREQ(req_buffer->data, req_buffer->length,
135			   &digestreq, &len);
136    if (ret)
137	return ret;
138
139    *claim = 1;
140
141    ret = _kdc_do_digest(context, config, &digestreq, reply, from, addr);
142    free_DigestREQ(&digestreq);
143    return ret;
144}
145
146#endif
147
148#ifdef KX509
149
150static krb5_error_code
151kdc_kx509(krb5_context context,
152	  krb5_kdc_configuration *config,
153	  krb5_data *req_buffer,
154	  krb5_data *reply,
155	  const char *from,
156	  struct sockaddr *addr,
157	  size_t max_reply_size,
158	  int *claim)
159{
160    Kx509Request kx509req;
161    krb5_error_code ret;
162    size_t len;
163
164    ret = _kdc_try_kx509_request(req_buffer->data, req_buffer->length,
165				 &kx509req, &len);
166    if (ret)
167	return ret;
168
169    *claim = 1;
170
171    ret = _kdc_do_kx509(context, config, &kx509req, reply, from, addr);
172    free_Kx509Request(&kx509req);
173    return ret;
174}
175
176#endif
177
178static struct krb5_kdc_service services[] =  {
179    { KS_KRB5,		kdc_as_req },
180    { KS_KRB5,		kdc_tgs_req },
181#ifdef DIGEST
182    { 0,		kdc_digest },
183#endif
184#ifdef KX509
185    { 0,		kdc_kx509 },
186#endif
187    { 0, NULL }
188};
189
190/*
191 * handle the request in `buf, len', from `addr' (or `from' as a string),
192 * sending a reply in `reply'.
193 */
194
195int
196krb5_kdc_process_request(krb5_context context,
197			 krb5_kdc_configuration *config,
198			 unsigned char *buf,
199			 size_t len,
200			 krb5_data *reply,
201			 const char *from,
202			 struct sockaddr *addr,
203			 int datagram_reply)
204{
205    krb5_error_code ret;
206    unsigned int i;
207    krb5_data req_buffer;
208    int claim = 0;
209    size_t max_reply_size = 0;
210
211    if (datagram_reply)
212	max_reply_size = config->max_datagram_reply_length;
213
214    req_buffer.data = buf;
215    req_buffer.length = len;
216
217    for (i = 0; services[i].process != NULL; i++) {
218	ret = (*services[i].process)(context, config, &req_buffer,
219				     reply, from, addr, max_reply_size,
220				     &claim);
221	if (claim) {
222	    return ret;
223	}
224    }
225
226    return -1;
227}
228
229/*
230 * handle the request in `buf, len', from `addr' (or `from' as a string),
231 * sending a reply in `reply'.
232 *
233 * This only processes krb5 requests
234 */
235
236int
237krb5_kdc_process_krb5_request(krb5_context context,
238			      krb5_kdc_configuration *config,
239			      unsigned char *buf,
240			      size_t len,
241			      krb5_data *reply,
242			      const char *from,
243			      struct sockaddr *addr,
244			      int datagram_reply)
245{
246    krb5_error_code ret;
247    unsigned int i;
248    krb5_data req_buffer;
249    int claim = 0;
250    size_t max_reply_size = 0;
251
252    if (datagram_reply)
253	max_reply_size = config->max_datagram_reply_length;
254
255    req_buffer.data = buf;
256    req_buffer.length = len;
257
258    for (i = 0; services[i].process != NULL; i++) {
259	if ((services[i].flags & KS_KRB5) == 0)
260	    continue;
261	ret = (*services[i].process)(context, config, &req_buffer,
262				     reply, from, addr, max_reply_size,
263				     &claim);
264	if (claim)
265	    return ret;
266    }
267
268    return -1;
269}
270
271/*
272 *
273 */
274
275int
276krb5_kdc_save_request(krb5_context context,
277		      const char *fn,
278		      const unsigned char *buf,
279		      size_t len,
280		      const krb5_data *reply,
281		      const struct sockaddr *sa)
282{
283    krb5_storage *sp;
284    krb5_address a;
285    int fd, ret;
286    time_t t;
287    krb5_data d;
288
289    memset(&a, 0, sizeof(a));
290
291    d.data = rk_UNCONST(buf);
292    d.length = len;
293    t = _kdc_now.tv_sec;
294
295    fd = open(fn, O_WRONLY|O_CREAT|O_APPEND, 0600);
296    if (fd < 0) {
297	int saved_errno = errno;
298	krb5_set_error_message(context, saved_errno, "Failed to open: %s", fn);
299	return saved_errno;
300    }
301
302    sp = krb5_storage_from_fd(fd);
303    close(fd);
304    if (sp == NULL) {
305	krb5_set_error_message(context, ENOMEM, "Storage failed to open fd");
306	return ENOMEM;
307    }
308
309    ret = krb5_sockaddr2address(context, sa, &a);
310    if (ret)
311	goto out;
312
313    krb5_store_uint32(sp, 1);
314    krb5_store_uint32(sp, (uint32_t)t);
315    krb5_store_address(sp, a);
316    krb5_store_data(sp, d);
317    {
318	Der_class cl;
319	Der_type ty;
320	unsigned int tag;
321	ret = der_get_tag (reply->data, reply->length,
322			   &cl, &ty, &tag, NULL);
323	if (ret) {
324	    krb5_store_uint32(sp, 0xffffffff);
325	    krb5_store_uint32(sp, 0xffffffff);
326	} else {
327	    krb5_store_uint32(sp, MAKE_TAG(cl, ty, 0));
328	    krb5_store_uint32(sp, tag);
329	}
330    }
331
332    krb5_free_address(context, &a);
333out:
334    krb5_storage_free(sp);
335
336    return 0;
337}
338