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    krb5_error_code ret;
95    KDC_REQ req;
96    size_t len;
97
98    ret = decode_TGS_REQ(req_buffer->data, req_buffer->length, &req, &len);
99    if (ret)
100	return ret;
101
102    *claim = 1;
103
104    ret = _kdc_tgs_rep(context, config, &req, reply,
105		       from, addr, max_reply_size);
106    free_TGS_REQ(&req);
107    return ret;
108}
109
110#ifdef DIGEST
111
112static krb5_error_code
113kdc_digest(krb5_context context,
114	   krb5_kdc_configuration *config,
115	   krb5_data *req_buffer,
116	   krb5_data *reply,
117	   const char *from,
118	   struct sockaddr *addr,
119	   size_t max_reply_size,
120	   int *claim)
121{
122    DigestREQ digestreq;
123    krb5_error_code ret;
124    size_t len;
125
126    ret = decode_DigestREQ(req_buffer->data, req_buffer->length,
127			   &digestreq, &len);
128    if (ret)
129	return ret;
130
131    *claim = 1;
132
133    ret = _kdc_do_digest(context, config, &digestreq, reply, from, addr);
134    free_DigestREQ(&digestreq);
135    return ret;
136}
137
138#endif
139
140#ifdef KX509
141
142static krb5_error_code
143kdc_kx509(krb5_context context,
144	  krb5_kdc_configuration *config,
145	  krb5_data *req_buffer,
146	  krb5_data *reply,
147	  const char *from,
148	  struct sockaddr *addr,
149	  size_t max_reply_size,
150	  int *claim)
151{
152    Kx509Request kx509req;
153    krb5_error_code ret;
154    size_t len;
155
156    ret = _kdc_try_kx509_request(req_buffer->data, req_buffer->length,
157				 &kx509req, &len);
158    if (ret)
159	return ret;
160
161    *claim = 1;
162
163    ret = _kdc_do_kx509(context, config, &kx509req, reply, from, addr);
164    free_Kx509Request(&kx509req);
165    return ret;
166}
167
168#endif
169
170static struct krb5_kdc_service services[] =  {
171    { KS_KRB5,		kdc_as_req },
172    { KS_KRB5,		kdc_tgs_req },
173#ifdef DIGEST
174    { 0,		kdc_digest },
175#endif
176#ifdef KX509
177    { 0,		kdc_kx509 },
178#endif
179    { 0, NULL }
180};
181
182/*
183 * handle the request in `buf, len', from `addr' (or `from' as a string),
184 * sending a reply in `reply'.
185 */
186
187int
188krb5_kdc_process_request(krb5_context context,
189			 krb5_kdc_configuration *config,
190			 unsigned char *buf,
191			 size_t len,
192			 krb5_data *reply,
193			 const char *from,
194			 struct sockaddr *addr,
195			 int datagram_reply)
196{
197    krb5_error_code ret;
198    unsigned int i;
199    krb5_data req_buffer;
200    int claim = 0;
201    size_t max_reply_size = 0;
202
203    if (datagram_reply)
204	max_reply_size = config->max_datagram_reply_length;
205
206    req_buffer.data = buf;
207    req_buffer.length = len;
208
209    for (i = 0; services[i].process != NULL; i++) {
210	ret = (*services[i].process)(context, config, &req_buffer,
211				     reply, from, addr, max_reply_size,
212				     &claim);
213	if (claim) {
214	    return ret;
215	}
216    }
217
218    return -1;
219}
220
221/*
222 * handle the request in `buf, len', from `addr' (or `from' as a string),
223 * sending a reply in `reply'.
224 *
225 * This only processes krb5 requests
226 */
227
228int
229krb5_kdc_process_krb5_request(krb5_context context,
230			      krb5_kdc_configuration *config,
231			      unsigned char *buf,
232			      size_t len,
233			      krb5_data *reply,
234			      const char *from,
235			      struct sockaddr *addr,
236			      int datagram_reply)
237{
238    krb5_error_code ret;
239    unsigned int i;
240    krb5_data req_buffer;
241    int claim = 0;
242    size_t max_reply_size = 0;
243
244    if (datagram_reply)
245	max_reply_size = config->max_datagram_reply_length;
246
247    req_buffer.data = buf;
248    req_buffer.length = len;
249
250    for (i = 0; services[i].process != NULL; i++) {
251	if ((services[i].flags & KS_KRB5) == 0)
252	    continue;
253	ret = (*services[i].process)(context, config, &req_buffer,
254				     reply, from, addr, max_reply_size,
255				     &claim);
256	if (claim)
257	    return ret;
258    }
259
260    return -1;
261}
262
263/*
264 *
265 */
266
267int
268krb5_kdc_save_request(krb5_context context,
269		      const char *fn,
270		      const unsigned char *buf,
271		      size_t len,
272		      const krb5_data *reply,
273		      const struct sockaddr *sa)
274{
275    krb5_storage *sp;
276    krb5_address a;
277    int fd, ret;
278    time_t t;
279    krb5_data d;
280
281    memset(&a, 0, sizeof(a));
282
283    d.data = rk_UNCONST(buf);
284    d.length = len;
285    t = _kdc_now.tv_sec;
286
287    fd = open(fn, O_WRONLY|O_CREAT|O_APPEND, 0600);
288    if (fd < 0) {
289	int saved_errno = errno;
290	krb5_set_error_message(context, saved_errno, "Failed to open: %s", fn);
291	return saved_errno;
292    }
293
294    sp = krb5_storage_from_fd(fd);
295    close(fd);
296    if (sp == NULL) {
297	krb5_set_error_message(context, ENOMEM, "Storage failed to open fd");
298	return ENOMEM;
299    }
300
301    ret = krb5_sockaddr2address(context, sa, &a);
302    if (ret)
303	goto out;
304
305    krb5_store_uint32(sp, 1);
306    krb5_store_uint32(sp, (uint32_t)t);
307    krb5_store_address(sp, a);
308    krb5_store_data(sp, d);
309    {
310	Der_class cl;
311	Der_type ty;
312	unsigned int tag;
313	ret = der_get_tag (reply->data, reply->length,
314			   &cl, &ty, &tag, NULL);
315	if (ret) {
316	    krb5_store_uint32(sp, 0xffffffff);
317	    krb5_store_uint32(sp, 0xffffffff);
318	} else {
319	    krb5_store_uint32(sp, MAKE_TAG(cl, ty, 0));
320	    krb5_store_uint32(sp, tag);
321	}
322    }
323
324    krb5_free_address(context, &a);
325out:
326    krb5_storage_free(sp);
327
328    return 0;
329}
330