1*0a6a1f1dSLionel Sambuc /* $NetBSD: connect.c,v 1.1.1.2 2014/04/24 12:45:27 pettai Exp $ */
2ebfedea0SLionel Sambuc
3ebfedea0SLionel Sambuc /*
4ebfedea0SLionel Sambuc * Copyright (c) 1997-2005 Kungliga Tekniska Högskolan
5ebfedea0SLionel Sambuc * (Royal Institute of Technology, Stockholm, Sweden).
6ebfedea0SLionel Sambuc * All rights reserved.
7ebfedea0SLionel Sambuc *
8ebfedea0SLionel Sambuc * Redistribution and use in source and binary forms, with or without
9ebfedea0SLionel Sambuc * modification, are permitted provided that the following conditions
10ebfedea0SLionel Sambuc * are met:
11ebfedea0SLionel Sambuc *
12ebfedea0SLionel Sambuc * 1. Redistributions of source code must retain the above copyright
13ebfedea0SLionel Sambuc * notice, this list of conditions and the following disclaimer.
14ebfedea0SLionel Sambuc *
15ebfedea0SLionel Sambuc * 2. Redistributions in binary form must reproduce the above copyright
16ebfedea0SLionel Sambuc * notice, this list of conditions and the following disclaimer in the
17ebfedea0SLionel Sambuc * documentation and/or other materials provided with the distribution.
18ebfedea0SLionel Sambuc *
19ebfedea0SLionel Sambuc * 3. Neither the name of the Institute nor the names of its contributors
20ebfedea0SLionel Sambuc * may be used to endorse or promote products derived from this software
21ebfedea0SLionel Sambuc * without specific prior written permission.
22ebfedea0SLionel Sambuc *
23ebfedea0SLionel Sambuc * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
24ebfedea0SLionel Sambuc * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25ebfedea0SLionel Sambuc * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26ebfedea0SLionel Sambuc * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
27ebfedea0SLionel Sambuc * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28ebfedea0SLionel Sambuc * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29ebfedea0SLionel Sambuc * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30ebfedea0SLionel Sambuc * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31ebfedea0SLionel Sambuc * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32ebfedea0SLionel Sambuc * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33ebfedea0SLionel Sambuc * SUCH DAMAGE.
34ebfedea0SLionel Sambuc */
35ebfedea0SLionel Sambuc
36ebfedea0SLionel Sambuc #include "kdc_locl.h"
37ebfedea0SLionel Sambuc
38ebfedea0SLionel Sambuc /* Should we enable the HTTP hack? */
39ebfedea0SLionel Sambuc int enable_http = -1;
40ebfedea0SLionel Sambuc
41ebfedea0SLionel Sambuc /* Log over requests to the KDC */
42ebfedea0SLionel Sambuc const char *request_log;
43ebfedea0SLionel Sambuc
44ebfedea0SLionel Sambuc /* A string describing on what ports to listen */
45ebfedea0SLionel Sambuc const char *port_str;
46ebfedea0SLionel Sambuc
47ebfedea0SLionel Sambuc krb5_addresses explicit_addresses;
48ebfedea0SLionel Sambuc
49ebfedea0SLionel Sambuc size_t max_request_udp;
50ebfedea0SLionel Sambuc size_t max_request_tcp;
51ebfedea0SLionel Sambuc
52ebfedea0SLionel Sambuc /*
53ebfedea0SLionel Sambuc * a tuple describing on what to listen
54ebfedea0SLionel Sambuc */
55ebfedea0SLionel Sambuc
56ebfedea0SLionel Sambuc struct port_desc{
57ebfedea0SLionel Sambuc int family;
58ebfedea0SLionel Sambuc int type;
59ebfedea0SLionel Sambuc int port;
60ebfedea0SLionel Sambuc };
61ebfedea0SLionel Sambuc
62ebfedea0SLionel Sambuc /* the current ones */
63ebfedea0SLionel Sambuc
64ebfedea0SLionel Sambuc static struct port_desc *ports;
65*0a6a1f1dSLionel Sambuc static size_t num_ports;
66ebfedea0SLionel Sambuc
67ebfedea0SLionel Sambuc /*
68ebfedea0SLionel Sambuc * add `family, port, protocol' to the list with duplicate suppresion.
69ebfedea0SLionel Sambuc */
70ebfedea0SLionel Sambuc
71ebfedea0SLionel Sambuc static void
add_port(krb5_context context,int family,int port,const char * protocol)72ebfedea0SLionel Sambuc add_port(krb5_context context,
73ebfedea0SLionel Sambuc int family, int port, const char *protocol)
74ebfedea0SLionel Sambuc {
75ebfedea0SLionel Sambuc int type;
76*0a6a1f1dSLionel Sambuc size_t i;
77ebfedea0SLionel Sambuc
78ebfedea0SLionel Sambuc if(strcmp(protocol, "udp") == 0)
79ebfedea0SLionel Sambuc type = SOCK_DGRAM;
80ebfedea0SLionel Sambuc else if(strcmp(protocol, "tcp") == 0)
81ebfedea0SLionel Sambuc type = SOCK_STREAM;
82ebfedea0SLionel Sambuc else
83ebfedea0SLionel Sambuc return;
84ebfedea0SLionel Sambuc for(i = 0; i < num_ports; i++){
85ebfedea0SLionel Sambuc if(ports[i].type == type
86ebfedea0SLionel Sambuc && ports[i].port == port
87ebfedea0SLionel Sambuc && ports[i].family == family)
88ebfedea0SLionel Sambuc return;
89ebfedea0SLionel Sambuc }
90ebfedea0SLionel Sambuc ports = realloc(ports, (num_ports + 1) * sizeof(*ports));
91ebfedea0SLionel Sambuc if (ports == NULL)
92ebfedea0SLionel Sambuc krb5_err (context, 1, errno, "realloc");
93ebfedea0SLionel Sambuc ports[num_ports].family = family;
94ebfedea0SLionel Sambuc ports[num_ports].type = type;
95ebfedea0SLionel Sambuc ports[num_ports].port = port;
96ebfedea0SLionel Sambuc num_ports++;
97ebfedea0SLionel Sambuc }
98ebfedea0SLionel Sambuc
99ebfedea0SLionel Sambuc /*
100ebfedea0SLionel Sambuc * add a triple but with service -> port lookup
101ebfedea0SLionel Sambuc * (this prints warnings for stuff that does not exist)
102ebfedea0SLionel Sambuc */
103ebfedea0SLionel Sambuc
104ebfedea0SLionel Sambuc static void
add_port_service(krb5_context context,int family,const char * service,int port,const char * protocol)105ebfedea0SLionel Sambuc add_port_service(krb5_context context,
106ebfedea0SLionel Sambuc int family, const char *service, int port,
107ebfedea0SLionel Sambuc const char *protocol)
108ebfedea0SLionel Sambuc {
109ebfedea0SLionel Sambuc port = krb5_getportbyname (context, service, protocol, port);
110ebfedea0SLionel Sambuc add_port (context, family, port, protocol);
111ebfedea0SLionel Sambuc }
112ebfedea0SLionel Sambuc
113ebfedea0SLionel Sambuc /*
114ebfedea0SLionel Sambuc * add the port with service -> port lookup or string -> number
115ebfedea0SLionel Sambuc * (no warning is printed)
116ebfedea0SLionel Sambuc */
117ebfedea0SLionel Sambuc
118ebfedea0SLionel Sambuc static void
add_port_string(krb5_context context,int family,const char * str,const char * protocol)119ebfedea0SLionel Sambuc add_port_string (krb5_context context,
120ebfedea0SLionel Sambuc int family, const char *str, const char *protocol)
121ebfedea0SLionel Sambuc {
122ebfedea0SLionel Sambuc struct servent *sp;
123ebfedea0SLionel Sambuc int port;
124ebfedea0SLionel Sambuc
125ebfedea0SLionel Sambuc sp = roken_getservbyname (str, protocol);
126ebfedea0SLionel Sambuc if (sp != NULL) {
127ebfedea0SLionel Sambuc port = sp->s_port;
128ebfedea0SLionel Sambuc } else {
129ebfedea0SLionel Sambuc char *end;
130ebfedea0SLionel Sambuc
131ebfedea0SLionel Sambuc port = htons(strtol(str, &end, 0));
132ebfedea0SLionel Sambuc if (end == str)
133ebfedea0SLionel Sambuc return;
134ebfedea0SLionel Sambuc }
135ebfedea0SLionel Sambuc add_port (context, family, port, protocol);
136ebfedea0SLionel Sambuc }
137ebfedea0SLionel Sambuc
138ebfedea0SLionel Sambuc /*
139ebfedea0SLionel Sambuc * add the standard collection of ports for `family'
140ebfedea0SLionel Sambuc */
141ebfedea0SLionel Sambuc
142ebfedea0SLionel Sambuc static void
add_standard_ports(krb5_context context,krb5_kdc_configuration * config,int family)143ebfedea0SLionel Sambuc add_standard_ports (krb5_context context,
144ebfedea0SLionel Sambuc krb5_kdc_configuration *config,
145ebfedea0SLionel Sambuc int family)
146ebfedea0SLionel Sambuc {
147ebfedea0SLionel Sambuc add_port_service(context, family, "kerberos", 88, "udp");
148ebfedea0SLionel Sambuc add_port_service(context, family, "kerberos", 88, "tcp");
149ebfedea0SLionel Sambuc add_port_service(context, family, "kerberos-sec", 88, "udp");
150ebfedea0SLionel Sambuc add_port_service(context, family, "kerberos-sec", 88, "tcp");
151ebfedea0SLionel Sambuc if(enable_http)
152ebfedea0SLionel Sambuc add_port_service(context, family, "http", 80, "tcp");
153ebfedea0SLionel Sambuc if(config->enable_kx509) {
154ebfedea0SLionel Sambuc add_port_service(context, family, "kca_service", 9878, "udp");
155ebfedea0SLionel Sambuc add_port_service(context, family, "kca_service", 9878, "tcp");
156ebfedea0SLionel Sambuc }
157ebfedea0SLionel Sambuc
158ebfedea0SLionel Sambuc }
159ebfedea0SLionel Sambuc
160ebfedea0SLionel Sambuc /*
161ebfedea0SLionel Sambuc * parse the set of space-delimited ports in `str' and add them.
162ebfedea0SLionel Sambuc * "+" => all the standard ones
163ebfedea0SLionel Sambuc * otherwise it's port|service[/protocol]
164ebfedea0SLionel Sambuc */
165ebfedea0SLionel Sambuc
166ebfedea0SLionel Sambuc static void
parse_ports(krb5_context context,krb5_kdc_configuration * config,const char * str)167ebfedea0SLionel Sambuc parse_ports(krb5_context context,
168ebfedea0SLionel Sambuc krb5_kdc_configuration *config,
169ebfedea0SLionel Sambuc const char *str)
170ebfedea0SLionel Sambuc {
171ebfedea0SLionel Sambuc char *pos = NULL;
172ebfedea0SLionel Sambuc char *p;
173ebfedea0SLionel Sambuc char *str_copy = strdup (str);
174ebfedea0SLionel Sambuc
175ebfedea0SLionel Sambuc p = strtok_r(str_copy, " \t", &pos);
176ebfedea0SLionel Sambuc while(p != NULL) {
177ebfedea0SLionel Sambuc if(strcmp(p, "+") == 0) {
178ebfedea0SLionel Sambuc #ifdef HAVE_IPV6
179ebfedea0SLionel Sambuc add_standard_ports(context, config, AF_INET6);
180ebfedea0SLionel Sambuc #endif
181ebfedea0SLionel Sambuc add_standard_ports(context, config, AF_INET);
182ebfedea0SLionel Sambuc } else {
183ebfedea0SLionel Sambuc char *q = strchr(p, '/');
184ebfedea0SLionel Sambuc if(q){
185ebfedea0SLionel Sambuc *q++ = 0;
186ebfedea0SLionel Sambuc #ifdef HAVE_IPV6
187ebfedea0SLionel Sambuc add_port_string(context, AF_INET6, p, q);
188ebfedea0SLionel Sambuc #endif
189ebfedea0SLionel Sambuc add_port_string(context, AF_INET, p, q);
190ebfedea0SLionel Sambuc }else {
191ebfedea0SLionel Sambuc #ifdef HAVE_IPV6
192ebfedea0SLionel Sambuc add_port_string(context, AF_INET6, p, "udp");
193ebfedea0SLionel Sambuc add_port_string(context, AF_INET6, p, "tcp");
194ebfedea0SLionel Sambuc #endif
195ebfedea0SLionel Sambuc add_port_string(context, AF_INET, p, "udp");
196ebfedea0SLionel Sambuc add_port_string(context, AF_INET, p, "tcp");
197ebfedea0SLionel Sambuc }
198ebfedea0SLionel Sambuc }
199ebfedea0SLionel Sambuc
200ebfedea0SLionel Sambuc p = strtok_r(NULL, " \t", &pos);
201ebfedea0SLionel Sambuc }
202ebfedea0SLionel Sambuc free (str_copy);
203ebfedea0SLionel Sambuc }
204ebfedea0SLionel Sambuc
205ebfedea0SLionel Sambuc /*
206ebfedea0SLionel Sambuc * every socket we listen on
207ebfedea0SLionel Sambuc */
208ebfedea0SLionel Sambuc
209ebfedea0SLionel Sambuc struct descr {
210ebfedea0SLionel Sambuc krb5_socket_t s;
211ebfedea0SLionel Sambuc int type;
212ebfedea0SLionel Sambuc int port;
213ebfedea0SLionel Sambuc unsigned char *buf;
214ebfedea0SLionel Sambuc size_t size;
215ebfedea0SLionel Sambuc size_t len;
216ebfedea0SLionel Sambuc time_t timeout;
217ebfedea0SLionel Sambuc struct sockaddr_storage __ss;
218ebfedea0SLionel Sambuc struct sockaddr *sa;
219ebfedea0SLionel Sambuc socklen_t sock_len;
220ebfedea0SLionel Sambuc char addr_string[128];
221ebfedea0SLionel Sambuc };
222ebfedea0SLionel Sambuc
223ebfedea0SLionel Sambuc static void
init_descr(struct descr * d)224ebfedea0SLionel Sambuc init_descr(struct descr *d)
225ebfedea0SLionel Sambuc {
226ebfedea0SLionel Sambuc memset(d, 0, sizeof(*d));
227ebfedea0SLionel Sambuc d->sa = (struct sockaddr *)&d->__ss;
228ebfedea0SLionel Sambuc d->s = rk_INVALID_SOCKET;
229ebfedea0SLionel Sambuc }
230ebfedea0SLionel Sambuc
231ebfedea0SLionel Sambuc /*
232ebfedea0SLionel Sambuc * re-initialize all `n' ->sa in `d'.
233ebfedea0SLionel Sambuc */
234ebfedea0SLionel Sambuc
235ebfedea0SLionel Sambuc static void
reinit_descrs(struct descr * d,int n)236ebfedea0SLionel Sambuc reinit_descrs (struct descr *d, int n)
237ebfedea0SLionel Sambuc {
238ebfedea0SLionel Sambuc int i;
239ebfedea0SLionel Sambuc
240ebfedea0SLionel Sambuc for (i = 0; i < n; ++i)
241ebfedea0SLionel Sambuc d[i].sa = (struct sockaddr *)&d[i].__ss;
242ebfedea0SLionel Sambuc }
243ebfedea0SLionel Sambuc
244ebfedea0SLionel Sambuc /*
245ebfedea0SLionel Sambuc * Create the socket (family, type, port) in `d'
246ebfedea0SLionel Sambuc */
247ebfedea0SLionel Sambuc
248ebfedea0SLionel Sambuc static void
init_socket(krb5_context context,krb5_kdc_configuration * config,struct descr * d,krb5_address * a,int family,int type,int port)249ebfedea0SLionel Sambuc init_socket(krb5_context context,
250ebfedea0SLionel Sambuc krb5_kdc_configuration *config,
251ebfedea0SLionel Sambuc struct descr *d, krb5_address *a, int family, int type, int port)
252ebfedea0SLionel Sambuc {
253ebfedea0SLionel Sambuc krb5_error_code ret;
254ebfedea0SLionel Sambuc struct sockaddr_storage __ss;
255ebfedea0SLionel Sambuc struct sockaddr *sa = (struct sockaddr *)&__ss;
256ebfedea0SLionel Sambuc krb5_socklen_t sa_size = sizeof(__ss);
257ebfedea0SLionel Sambuc
258ebfedea0SLionel Sambuc init_descr (d);
259ebfedea0SLionel Sambuc
260ebfedea0SLionel Sambuc ret = krb5_addr2sockaddr (context, a, sa, &sa_size, port);
261ebfedea0SLionel Sambuc if (ret) {
262ebfedea0SLionel Sambuc krb5_warn(context, ret, "krb5_addr2sockaddr");
263ebfedea0SLionel Sambuc rk_closesocket(d->s);
264ebfedea0SLionel Sambuc d->s = rk_INVALID_SOCKET;
265ebfedea0SLionel Sambuc return;
266ebfedea0SLionel Sambuc }
267ebfedea0SLionel Sambuc
268ebfedea0SLionel Sambuc if (sa->sa_family != family)
269ebfedea0SLionel Sambuc return;
270ebfedea0SLionel Sambuc
271ebfedea0SLionel Sambuc d->s = socket(family, type, 0);
272ebfedea0SLionel Sambuc if(rk_IS_BAD_SOCKET(d->s)){
273ebfedea0SLionel Sambuc krb5_warn(context, errno, "socket(%d, %d, 0)", family, type);
274ebfedea0SLionel Sambuc d->s = rk_INVALID_SOCKET;
275ebfedea0SLionel Sambuc return;
276ebfedea0SLionel Sambuc }
277ebfedea0SLionel Sambuc #if defined(HAVE_SETSOCKOPT) && defined(SOL_SOCKET) && defined(SO_REUSEADDR)
278ebfedea0SLionel Sambuc {
279ebfedea0SLionel Sambuc int one = 1;
280ebfedea0SLionel Sambuc setsockopt(d->s, SOL_SOCKET, SO_REUSEADDR, (void *)&one, sizeof(one));
281ebfedea0SLionel Sambuc }
282ebfedea0SLionel Sambuc #endif
283ebfedea0SLionel Sambuc d->type = type;
284ebfedea0SLionel Sambuc d->port = port;
285ebfedea0SLionel Sambuc
286ebfedea0SLionel Sambuc if(rk_IS_SOCKET_ERROR(bind(d->s, sa, sa_size))){
287ebfedea0SLionel Sambuc char a_str[256];
288ebfedea0SLionel Sambuc size_t len;
289ebfedea0SLionel Sambuc
290ebfedea0SLionel Sambuc krb5_print_address (a, a_str, sizeof(a_str), &len);
291ebfedea0SLionel Sambuc krb5_warn(context, errno, "bind %s/%d", a_str, ntohs(port));
292ebfedea0SLionel Sambuc rk_closesocket(d->s);
293ebfedea0SLionel Sambuc d->s = rk_INVALID_SOCKET;
294ebfedea0SLionel Sambuc return;
295ebfedea0SLionel Sambuc }
296ebfedea0SLionel Sambuc if(type == SOCK_STREAM && rk_IS_SOCKET_ERROR(listen(d->s, SOMAXCONN))){
297ebfedea0SLionel Sambuc char a_str[256];
298ebfedea0SLionel Sambuc size_t len;
299ebfedea0SLionel Sambuc
300ebfedea0SLionel Sambuc krb5_print_address (a, a_str, sizeof(a_str), &len);
301ebfedea0SLionel Sambuc krb5_warn(context, errno, "listen %s/%d", a_str, ntohs(port));
302ebfedea0SLionel Sambuc rk_closesocket(d->s);
303ebfedea0SLionel Sambuc d->s = rk_INVALID_SOCKET;
304ebfedea0SLionel Sambuc return;
305ebfedea0SLionel Sambuc }
306ebfedea0SLionel Sambuc }
307ebfedea0SLionel Sambuc
308ebfedea0SLionel Sambuc /*
309ebfedea0SLionel Sambuc * Allocate descriptors for all the sockets that we should listen on
310ebfedea0SLionel Sambuc * and return the number of them.
311ebfedea0SLionel Sambuc */
312ebfedea0SLionel Sambuc
313ebfedea0SLionel Sambuc static int
init_sockets(krb5_context context,krb5_kdc_configuration * config,struct descr ** desc)314ebfedea0SLionel Sambuc init_sockets(krb5_context context,
315ebfedea0SLionel Sambuc krb5_kdc_configuration *config,
316ebfedea0SLionel Sambuc struct descr **desc)
317ebfedea0SLionel Sambuc {
318ebfedea0SLionel Sambuc krb5_error_code ret;
319*0a6a1f1dSLionel Sambuc size_t i, j;
320ebfedea0SLionel Sambuc struct descr *d;
321ebfedea0SLionel Sambuc int num = 0;
322ebfedea0SLionel Sambuc krb5_addresses addresses;
323ebfedea0SLionel Sambuc
324ebfedea0SLionel Sambuc if (explicit_addresses.len) {
325ebfedea0SLionel Sambuc addresses = explicit_addresses;
326ebfedea0SLionel Sambuc } else {
327ebfedea0SLionel Sambuc ret = krb5_get_all_server_addrs (context, &addresses);
328ebfedea0SLionel Sambuc if (ret)
329ebfedea0SLionel Sambuc krb5_err (context, 1, ret, "krb5_get_all_server_addrs");
330ebfedea0SLionel Sambuc }
331ebfedea0SLionel Sambuc parse_ports(context, config, port_str);
332ebfedea0SLionel Sambuc d = malloc(addresses.len * num_ports * sizeof(*d));
333ebfedea0SLionel Sambuc if (d == NULL)
334ebfedea0SLionel Sambuc krb5_errx(context, 1, "malloc(%lu) failed",
335ebfedea0SLionel Sambuc (unsigned long)num_ports * sizeof(*d));
336ebfedea0SLionel Sambuc
337ebfedea0SLionel Sambuc for (i = 0; i < num_ports; i++){
338ebfedea0SLionel Sambuc for (j = 0; j < addresses.len; ++j) {
339ebfedea0SLionel Sambuc init_socket(context, config, &d[num], &addresses.val[j],
340ebfedea0SLionel Sambuc ports[i].family, ports[i].type, ports[i].port);
341ebfedea0SLionel Sambuc if(d[num].s != rk_INVALID_SOCKET){
342ebfedea0SLionel Sambuc char a_str[80];
343ebfedea0SLionel Sambuc size_t len;
344ebfedea0SLionel Sambuc
345ebfedea0SLionel Sambuc krb5_print_address (&addresses.val[j], a_str,
346ebfedea0SLionel Sambuc sizeof(a_str), &len);
347ebfedea0SLionel Sambuc
348ebfedea0SLionel Sambuc kdc_log(context, config, 5, "listening on %s port %u/%s",
349ebfedea0SLionel Sambuc a_str,
350ebfedea0SLionel Sambuc ntohs(ports[i].port),
351ebfedea0SLionel Sambuc (ports[i].type == SOCK_STREAM) ? "tcp" : "udp");
352ebfedea0SLionel Sambuc /* XXX */
353ebfedea0SLionel Sambuc num++;
354ebfedea0SLionel Sambuc }
355ebfedea0SLionel Sambuc }
356ebfedea0SLionel Sambuc }
357ebfedea0SLionel Sambuc krb5_free_addresses (context, &addresses);
358ebfedea0SLionel Sambuc d = realloc(d, num * sizeof(*d));
359ebfedea0SLionel Sambuc if (d == NULL && num != 0)
360ebfedea0SLionel Sambuc krb5_errx(context, 1, "realloc(%lu) failed",
361ebfedea0SLionel Sambuc (unsigned long)num * sizeof(*d));
362ebfedea0SLionel Sambuc reinit_descrs (d, num);
363ebfedea0SLionel Sambuc *desc = d;
364ebfedea0SLionel Sambuc return num;
365ebfedea0SLionel Sambuc }
366ebfedea0SLionel Sambuc
367ebfedea0SLionel Sambuc /*
368ebfedea0SLionel Sambuc *
369ebfedea0SLionel Sambuc */
370ebfedea0SLionel Sambuc
371ebfedea0SLionel Sambuc static const char *
descr_type(struct descr * d)372ebfedea0SLionel Sambuc descr_type(struct descr *d)
373ebfedea0SLionel Sambuc {
374ebfedea0SLionel Sambuc if (d->type == SOCK_DGRAM)
375ebfedea0SLionel Sambuc return "udp";
376ebfedea0SLionel Sambuc else if (d->type == SOCK_STREAM)
377ebfedea0SLionel Sambuc return "tcp";
378ebfedea0SLionel Sambuc return "unknown";
379ebfedea0SLionel Sambuc }
380ebfedea0SLionel Sambuc
381ebfedea0SLionel Sambuc static void
addr_to_string(krb5_context context,struct sockaddr * addr,size_t addr_len,char * str,size_t len)382ebfedea0SLionel Sambuc addr_to_string(krb5_context context,
383ebfedea0SLionel Sambuc struct sockaddr *addr, size_t addr_len, char *str, size_t len)
384ebfedea0SLionel Sambuc {
385ebfedea0SLionel Sambuc krb5_address a;
386ebfedea0SLionel Sambuc if(krb5_sockaddr2address(context, addr, &a) == 0) {
387ebfedea0SLionel Sambuc if(krb5_print_address(&a, str, len, &len) == 0) {
388ebfedea0SLionel Sambuc krb5_free_address(context, &a);
389ebfedea0SLionel Sambuc return;
390ebfedea0SLionel Sambuc }
391ebfedea0SLionel Sambuc krb5_free_address(context, &a);
392ebfedea0SLionel Sambuc }
393ebfedea0SLionel Sambuc snprintf(str, len, "<family=%d>", addr->sa_family);
394ebfedea0SLionel Sambuc }
395ebfedea0SLionel Sambuc
396ebfedea0SLionel Sambuc /*
397ebfedea0SLionel Sambuc *
398ebfedea0SLionel Sambuc */
399ebfedea0SLionel Sambuc
400ebfedea0SLionel Sambuc static void
send_reply(krb5_context context,krb5_kdc_configuration * config,krb5_boolean prependlength,struct descr * d,krb5_data * reply)401ebfedea0SLionel Sambuc send_reply(krb5_context context,
402ebfedea0SLionel Sambuc krb5_kdc_configuration *config,
403ebfedea0SLionel Sambuc krb5_boolean prependlength,
404ebfedea0SLionel Sambuc struct descr *d,
405ebfedea0SLionel Sambuc krb5_data *reply)
406ebfedea0SLionel Sambuc {
407ebfedea0SLionel Sambuc kdc_log(context, config, 5,
408ebfedea0SLionel Sambuc "sending %lu bytes to %s", (unsigned long)reply->length,
409ebfedea0SLionel Sambuc d->addr_string);
410ebfedea0SLionel Sambuc if(prependlength){
411ebfedea0SLionel Sambuc unsigned char l[4];
412ebfedea0SLionel Sambuc l[0] = (reply->length >> 24) & 0xff;
413ebfedea0SLionel Sambuc l[1] = (reply->length >> 16) & 0xff;
414ebfedea0SLionel Sambuc l[2] = (reply->length >> 8) & 0xff;
415ebfedea0SLionel Sambuc l[3] = reply->length & 0xff;
416ebfedea0SLionel Sambuc if(rk_IS_SOCKET_ERROR(sendto(d->s, l, sizeof(l), 0, d->sa, d->sock_len))) {
417ebfedea0SLionel Sambuc kdc_log (context, config,
418ebfedea0SLionel Sambuc 0, "sendto(%s): %s", d->addr_string,
419ebfedea0SLionel Sambuc strerror(rk_SOCK_ERRNO));
420ebfedea0SLionel Sambuc return;
421ebfedea0SLionel Sambuc }
422ebfedea0SLionel Sambuc }
423ebfedea0SLionel Sambuc if(rk_IS_SOCKET_ERROR(sendto(d->s, reply->data, reply->length, 0, d->sa, d->sock_len))) {
424ebfedea0SLionel Sambuc kdc_log (context, config, 0, "sendto(%s): %s", d->addr_string,
425ebfedea0SLionel Sambuc strerror(rk_SOCK_ERRNO));
426ebfedea0SLionel Sambuc return;
427ebfedea0SLionel Sambuc }
428ebfedea0SLionel Sambuc }
429ebfedea0SLionel Sambuc
430ebfedea0SLionel Sambuc /*
431ebfedea0SLionel Sambuc * Handle the request in `buf, len' to socket `d'
432ebfedea0SLionel Sambuc */
433ebfedea0SLionel Sambuc
434ebfedea0SLionel Sambuc static void
do_request(krb5_context context,krb5_kdc_configuration * config,void * buf,size_t len,krb5_boolean prependlength,struct descr * d)435ebfedea0SLionel Sambuc do_request(krb5_context context,
436ebfedea0SLionel Sambuc krb5_kdc_configuration *config,
437ebfedea0SLionel Sambuc void *buf, size_t len, krb5_boolean prependlength,
438ebfedea0SLionel Sambuc struct descr *d)
439ebfedea0SLionel Sambuc {
440ebfedea0SLionel Sambuc krb5_error_code ret;
441ebfedea0SLionel Sambuc krb5_data reply;
442ebfedea0SLionel Sambuc int datagram_reply = (d->type == SOCK_DGRAM);
443ebfedea0SLionel Sambuc
444ebfedea0SLionel Sambuc krb5_kdc_update_time(NULL);
445ebfedea0SLionel Sambuc
446ebfedea0SLionel Sambuc krb5_data_zero(&reply);
447ebfedea0SLionel Sambuc ret = krb5_kdc_process_request(context, config,
448ebfedea0SLionel Sambuc buf, len, &reply, &prependlength,
449ebfedea0SLionel Sambuc d->addr_string, d->sa,
450ebfedea0SLionel Sambuc datagram_reply);
451ebfedea0SLionel Sambuc if(request_log)
452ebfedea0SLionel Sambuc krb5_kdc_save_request(context, request_log, buf, len, &reply, d->sa);
453ebfedea0SLionel Sambuc if(reply.length){
454ebfedea0SLionel Sambuc send_reply(context, config, prependlength, d, &reply);
455ebfedea0SLionel Sambuc krb5_data_free(&reply);
456ebfedea0SLionel Sambuc }
457ebfedea0SLionel Sambuc if(ret)
458ebfedea0SLionel Sambuc kdc_log(context, config, 0,
459ebfedea0SLionel Sambuc "Failed processing %lu byte request from %s",
460ebfedea0SLionel Sambuc (unsigned long)len, d->addr_string);
461ebfedea0SLionel Sambuc }
462ebfedea0SLionel Sambuc
463ebfedea0SLionel Sambuc /*
464ebfedea0SLionel Sambuc * Handle incoming data to the UDP socket in `d'
465ebfedea0SLionel Sambuc */
466ebfedea0SLionel Sambuc
467ebfedea0SLionel Sambuc static void
handle_udp(krb5_context context,krb5_kdc_configuration * config,struct descr * d)468ebfedea0SLionel Sambuc handle_udp(krb5_context context,
469ebfedea0SLionel Sambuc krb5_kdc_configuration *config,
470ebfedea0SLionel Sambuc struct descr *d)
471ebfedea0SLionel Sambuc {
472ebfedea0SLionel Sambuc unsigned char *buf;
473*0a6a1f1dSLionel Sambuc ssize_t n;
474ebfedea0SLionel Sambuc
475ebfedea0SLionel Sambuc buf = malloc(max_request_udp);
476ebfedea0SLionel Sambuc if(buf == NULL){
477ebfedea0SLionel Sambuc kdc_log(context, config, 0, "Failed to allocate %lu bytes", (unsigned long)max_request_udp);
478ebfedea0SLionel Sambuc return;
479ebfedea0SLionel Sambuc }
480ebfedea0SLionel Sambuc
481ebfedea0SLionel Sambuc d->sock_len = sizeof(d->__ss);
482ebfedea0SLionel Sambuc n = recvfrom(d->s, buf, max_request_udp, 0, d->sa, &d->sock_len);
483ebfedea0SLionel Sambuc if(rk_IS_SOCKET_ERROR(n))
484ebfedea0SLionel Sambuc krb5_warn(context, rk_SOCK_ERRNO, "recvfrom");
485ebfedea0SLionel Sambuc else {
486ebfedea0SLionel Sambuc addr_to_string (context, d->sa, d->sock_len,
487ebfedea0SLionel Sambuc d->addr_string, sizeof(d->addr_string));
488*0a6a1f1dSLionel Sambuc if ((size_t)n == max_request_udp) {
489ebfedea0SLionel Sambuc krb5_data data;
490ebfedea0SLionel Sambuc krb5_warn(context, errno,
491ebfedea0SLionel Sambuc "recvfrom: truncated packet from %s, asking for TCP",
492ebfedea0SLionel Sambuc d->addr_string);
493ebfedea0SLionel Sambuc krb5_mk_error(context,
494ebfedea0SLionel Sambuc KRB5KRB_ERR_RESPONSE_TOO_BIG,
495ebfedea0SLionel Sambuc NULL,
496ebfedea0SLionel Sambuc NULL,
497ebfedea0SLionel Sambuc NULL,
498ebfedea0SLionel Sambuc NULL,
499ebfedea0SLionel Sambuc NULL,
500ebfedea0SLionel Sambuc NULL,
501ebfedea0SLionel Sambuc &data);
502ebfedea0SLionel Sambuc send_reply(context, config, FALSE, d, &data);
503ebfedea0SLionel Sambuc krb5_data_free(&data);
504ebfedea0SLionel Sambuc } else {
505ebfedea0SLionel Sambuc do_request(context, config, buf, n, FALSE, d);
506ebfedea0SLionel Sambuc }
507ebfedea0SLionel Sambuc }
508ebfedea0SLionel Sambuc free (buf);
509ebfedea0SLionel Sambuc }
510ebfedea0SLionel Sambuc
511ebfedea0SLionel Sambuc static void
clear_descr(struct descr * d)512ebfedea0SLionel Sambuc clear_descr(struct descr *d)
513ebfedea0SLionel Sambuc {
514ebfedea0SLionel Sambuc if(d->buf)
515ebfedea0SLionel Sambuc memset(d->buf, 0, d->size);
516ebfedea0SLionel Sambuc d->len = 0;
517ebfedea0SLionel Sambuc if(d->s != rk_INVALID_SOCKET)
518ebfedea0SLionel Sambuc rk_closesocket(d->s);
519ebfedea0SLionel Sambuc d->s = rk_INVALID_SOCKET;
520ebfedea0SLionel Sambuc }
521ebfedea0SLionel Sambuc
522ebfedea0SLionel Sambuc
523ebfedea0SLionel Sambuc /* remove HTTP %-quoting from buf */
524ebfedea0SLionel Sambuc static int
de_http(char * buf)525ebfedea0SLionel Sambuc de_http(char *buf)
526ebfedea0SLionel Sambuc {
527ebfedea0SLionel Sambuc unsigned char *p, *q;
528ebfedea0SLionel Sambuc for(p = q = (unsigned char *)buf; *p; p++, q++) {
529ebfedea0SLionel Sambuc if(*p == '%' && isxdigit(p[1]) && isxdigit(p[2])) {
530ebfedea0SLionel Sambuc unsigned int x;
531ebfedea0SLionel Sambuc if(sscanf((char *)p + 1, "%2x", &x) != 1)
532ebfedea0SLionel Sambuc return -1;
533ebfedea0SLionel Sambuc *q = x;
534ebfedea0SLionel Sambuc p += 2;
535ebfedea0SLionel Sambuc } else
536ebfedea0SLionel Sambuc *q = *p;
537ebfedea0SLionel Sambuc }
538ebfedea0SLionel Sambuc *q = '\0';
539ebfedea0SLionel Sambuc return 0;
540ebfedea0SLionel Sambuc }
541ebfedea0SLionel Sambuc
542ebfedea0SLionel Sambuc #define TCP_TIMEOUT 4
543ebfedea0SLionel Sambuc
544ebfedea0SLionel Sambuc /*
545ebfedea0SLionel Sambuc * accept a new TCP connection on `d[parent]' and store it in `d[child]'
546ebfedea0SLionel Sambuc */
547ebfedea0SLionel Sambuc
548ebfedea0SLionel Sambuc static void
add_new_tcp(krb5_context context,krb5_kdc_configuration * config,struct descr * d,int parent,int child)549ebfedea0SLionel Sambuc add_new_tcp (krb5_context context,
550ebfedea0SLionel Sambuc krb5_kdc_configuration *config,
551ebfedea0SLionel Sambuc struct descr *d, int parent, int child)
552ebfedea0SLionel Sambuc {
553ebfedea0SLionel Sambuc krb5_socket_t s;
554ebfedea0SLionel Sambuc
555ebfedea0SLionel Sambuc if (child == -1)
556ebfedea0SLionel Sambuc return;
557ebfedea0SLionel Sambuc
558ebfedea0SLionel Sambuc d[child].sock_len = sizeof(d[child].__ss);
559ebfedea0SLionel Sambuc s = accept(d[parent].s, d[child].sa, &d[child].sock_len);
560ebfedea0SLionel Sambuc if(rk_IS_BAD_SOCKET(s)) {
561ebfedea0SLionel Sambuc krb5_warn(context, rk_SOCK_ERRNO, "accept");
562ebfedea0SLionel Sambuc return;
563ebfedea0SLionel Sambuc }
564ebfedea0SLionel Sambuc
565ebfedea0SLionel Sambuc #ifdef FD_SETSIZE
566ebfedea0SLionel Sambuc if (s >= FD_SETSIZE) {
567ebfedea0SLionel Sambuc krb5_warnx(context, "socket FD too large");
568ebfedea0SLionel Sambuc rk_closesocket (s);
569ebfedea0SLionel Sambuc return;
570ebfedea0SLionel Sambuc }
571ebfedea0SLionel Sambuc #endif
572ebfedea0SLionel Sambuc
573ebfedea0SLionel Sambuc d[child].s = s;
574ebfedea0SLionel Sambuc d[child].timeout = time(NULL) + TCP_TIMEOUT;
575ebfedea0SLionel Sambuc d[child].type = SOCK_STREAM;
576ebfedea0SLionel Sambuc addr_to_string (context,
577ebfedea0SLionel Sambuc d[child].sa, d[child].sock_len,
578ebfedea0SLionel Sambuc d[child].addr_string, sizeof(d[child].addr_string));
579ebfedea0SLionel Sambuc }
580ebfedea0SLionel Sambuc
581ebfedea0SLionel Sambuc /*
582ebfedea0SLionel Sambuc * Grow `d' to handle at least `n'.
583ebfedea0SLionel Sambuc * Return != 0 if fails
584ebfedea0SLionel Sambuc */
585ebfedea0SLionel Sambuc
586ebfedea0SLionel Sambuc static int
grow_descr(krb5_context context,krb5_kdc_configuration * config,struct descr * d,size_t n)587ebfedea0SLionel Sambuc grow_descr (krb5_context context,
588ebfedea0SLionel Sambuc krb5_kdc_configuration *config,
589ebfedea0SLionel Sambuc struct descr *d, size_t n)
590ebfedea0SLionel Sambuc {
591ebfedea0SLionel Sambuc if (d->size - d->len < n) {
592ebfedea0SLionel Sambuc unsigned char *tmp;
593ebfedea0SLionel Sambuc size_t grow;
594ebfedea0SLionel Sambuc
595ebfedea0SLionel Sambuc grow = max(1024, d->len + n);
596ebfedea0SLionel Sambuc if (d->size + grow > max_request_tcp) {
597ebfedea0SLionel Sambuc kdc_log(context, config, 0, "Request exceeds max request size (%lu bytes).",
598ebfedea0SLionel Sambuc (unsigned long)d->size + grow);
599ebfedea0SLionel Sambuc clear_descr(d);
600ebfedea0SLionel Sambuc return -1;
601ebfedea0SLionel Sambuc }
602ebfedea0SLionel Sambuc tmp = realloc (d->buf, d->size + grow);
603ebfedea0SLionel Sambuc if (tmp == NULL) {
604ebfedea0SLionel Sambuc kdc_log(context, config, 0, "Failed to re-allocate %lu bytes.",
605ebfedea0SLionel Sambuc (unsigned long)d->size + grow);
606ebfedea0SLionel Sambuc clear_descr(d);
607ebfedea0SLionel Sambuc return -1;
608ebfedea0SLionel Sambuc }
609ebfedea0SLionel Sambuc d->size += grow;
610ebfedea0SLionel Sambuc d->buf = tmp;
611ebfedea0SLionel Sambuc }
612ebfedea0SLionel Sambuc return 0;
613ebfedea0SLionel Sambuc }
614ebfedea0SLionel Sambuc
615ebfedea0SLionel Sambuc /*
616ebfedea0SLionel Sambuc * Try to handle the TCP data at `d->buf, d->len'.
617ebfedea0SLionel Sambuc * Return -1 if failed, 0 if succesful, and 1 if data is complete.
618ebfedea0SLionel Sambuc */
619ebfedea0SLionel Sambuc
620ebfedea0SLionel Sambuc static int
handle_vanilla_tcp(krb5_context context,krb5_kdc_configuration * config,struct descr * d)621ebfedea0SLionel Sambuc handle_vanilla_tcp (krb5_context context,
622ebfedea0SLionel Sambuc krb5_kdc_configuration *config,
623ebfedea0SLionel Sambuc struct descr *d)
624ebfedea0SLionel Sambuc {
625ebfedea0SLionel Sambuc krb5_storage *sp;
626ebfedea0SLionel Sambuc uint32_t len;
627ebfedea0SLionel Sambuc
628ebfedea0SLionel Sambuc sp = krb5_storage_from_mem(d->buf, d->len);
629ebfedea0SLionel Sambuc if (sp == NULL) {
630ebfedea0SLionel Sambuc kdc_log (context, config, 0, "krb5_storage_from_mem failed");
631ebfedea0SLionel Sambuc return -1;
632ebfedea0SLionel Sambuc }
633ebfedea0SLionel Sambuc krb5_ret_uint32(sp, &len);
634ebfedea0SLionel Sambuc krb5_storage_free(sp);
635ebfedea0SLionel Sambuc if(d->len - 4 >= len) {
636ebfedea0SLionel Sambuc memmove(d->buf, d->buf + 4, d->len - 4);
637ebfedea0SLionel Sambuc d->len -= 4;
638ebfedea0SLionel Sambuc return 1;
639ebfedea0SLionel Sambuc }
640ebfedea0SLionel Sambuc return 0;
641ebfedea0SLionel Sambuc }
642ebfedea0SLionel Sambuc
643ebfedea0SLionel Sambuc /*
644ebfedea0SLionel Sambuc * Try to handle the TCP/HTTP data at `d->buf, d->len'.
645ebfedea0SLionel Sambuc * Return -1 if failed, 0 if succesful, and 1 if data is complete.
646ebfedea0SLionel Sambuc */
647ebfedea0SLionel Sambuc
648ebfedea0SLionel Sambuc static int
handle_http_tcp(krb5_context context,krb5_kdc_configuration * config,struct descr * d)649ebfedea0SLionel Sambuc handle_http_tcp (krb5_context context,
650ebfedea0SLionel Sambuc krb5_kdc_configuration *config,
651ebfedea0SLionel Sambuc struct descr *d)
652ebfedea0SLionel Sambuc {
653ebfedea0SLionel Sambuc char *s, *p, *t;
654ebfedea0SLionel Sambuc void *data;
655ebfedea0SLionel Sambuc char *proto;
656ebfedea0SLionel Sambuc int len;
657ebfedea0SLionel Sambuc
658ebfedea0SLionel Sambuc s = (char *)d->buf;
659ebfedea0SLionel Sambuc
660ebfedea0SLionel Sambuc /* If its a multi line query, truncate off the first line */
661ebfedea0SLionel Sambuc p = strstr(s, "\r\n");
662ebfedea0SLionel Sambuc if (p)
663ebfedea0SLionel Sambuc *p = 0;
664ebfedea0SLionel Sambuc
665ebfedea0SLionel Sambuc p = NULL;
666ebfedea0SLionel Sambuc t = strtok_r(s, " \t", &p);
667ebfedea0SLionel Sambuc if (t == NULL) {
668ebfedea0SLionel Sambuc kdc_log(context, config, 0,
669ebfedea0SLionel Sambuc "Missing HTTP operand (GET) request from %s", d->addr_string);
670ebfedea0SLionel Sambuc return -1;
671ebfedea0SLionel Sambuc }
672ebfedea0SLionel Sambuc
673ebfedea0SLionel Sambuc t = strtok_r(NULL, " \t", &p);
674ebfedea0SLionel Sambuc if(t == NULL) {
675ebfedea0SLionel Sambuc kdc_log(context, config, 0,
676ebfedea0SLionel Sambuc "Missing HTTP GET data in request from %s", d->addr_string);
677ebfedea0SLionel Sambuc return -1;
678ebfedea0SLionel Sambuc }
679ebfedea0SLionel Sambuc
680ebfedea0SLionel Sambuc data = malloc(strlen(t));
681ebfedea0SLionel Sambuc if (data == NULL) {
682ebfedea0SLionel Sambuc kdc_log(context, config, 0, "Failed to allocate %lu bytes",
683ebfedea0SLionel Sambuc (unsigned long)strlen(t));
684ebfedea0SLionel Sambuc return -1;
685ebfedea0SLionel Sambuc }
686ebfedea0SLionel Sambuc if(*t == '/')
687ebfedea0SLionel Sambuc t++;
688ebfedea0SLionel Sambuc if(de_http(t) != 0) {
689ebfedea0SLionel Sambuc kdc_log(context, config, 0, "Malformed HTTP request from %s", d->addr_string);
690ebfedea0SLionel Sambuc kdc_log(context, config, 5, "HTTP request: %s", t);
691ebfedea0SLionel Sambuc free(data);
692ebfedea0SLionel Sambuc return -1;
693ebfedea0SLionel Sambuc }
694ebfedea0SLionel Sambuc proto = strtok_r(NULL, " \t", &p);
695ebfedea0SLionel Sambuc if (proto == NULL) {
696ebfedea0SLionel Sambuc kdc_log(context, config, 0, "Malformed HTTP request from %s", d->addr_string);
697ebfedea0SLionel Sambuc free(data);
698ebfedea0SLionel Sambuc return -1;
699ebfedea0SLionel Sambuc }
700ebfedea0SLionel Sambuc len = base64_decode(t, data);
701ebfedea0SLionel Sambuc if(len <= 0){
702ebfedea0SLionel Sambuc const char *msg =
703ebfedea0SLionel Sambuc " 404 Not found\r\n"
704ebfedea0SLionel Sambuc "Server: Heimdal/" VERSION "\r\n"
705ebfedea0SLionel Sambuc "Cache-Control: no-cache\r\n"
706ebfedea0SLionel Sambuc "Pragma: no-cache\r\n"
707ebfedea0SLionel Sambuc "Content-type: text/html\r\n"
708ebfedea0SLionel Sambuc "Content-transfer-encoding: 8bit\r\n\r\n"
709ebfedea0SLionel Sambuc "<TITLE>404 Not found</TITLE>\r\n"
710ebfedea0SLionel Sambuc "<H1>404 Not found</H1>\r\n"
711ebfedea0SLionel Sambuc "That page doesn't exist, maybe you are looking for "
712ebfedea0SLionel Sambuc "<A HREF=\"http://www.h5l.org/\">Heimdal</A>?\r\n";
713ebfedea0SLionel Sambuc kdc_log(context, config, 0, "HTTP request from %s is non KDC request", d->addr_string);
714ebfedea0SLionel Sambuc kdc_log(context, config, 5, "HTTP request: %s", t);
715ebfedea0SLionel Sambuc free(data);
716ebfedea0SLionel Sambuc if (rk_IS_SOCKET_ERROR(send(d->s, proto, strlen(proto), 0))) {
717ebfedea0SLionel Sambuc kdc_log(context, config, 0, "HTTP write failed: %s: %s",
718ebfedea0SLionel Sambuc d->addr_string, strerror(rk_SOCK_ERRNO));
719ebfedea0SLionel Sambuc return -1;
720ebfedea0SLionel Sambuc }
721ebfedea0SLionel Sambuc if (rk_IS_SOCKET_ERROR(send(d->s, msg, strlen(msg), 0))) {
722ebfedea0SLionel Sambuc kdc_log(context, config, 0, "HTTP write failed: %s: %s",
723ebfedea0SLionel Sambuc d->addr_string, strerror(rk_SOCK_ERRNO));
724ebfedea0SLionel Sambuc return -1;
725ebfedea0SLionel Sambuc }
726ebfedea0SLionel Sambuc return -1;
727ebfedea0SLionel Sambuc }
728ebfedea0SLionel Sambuc {
729ebfedea0SLionel Sambuc const char *msg =
730ebfedea0SLionel Sambuc " 200 OK\r\n"
731ebfedea0SLionel Sambuc "Server: Heimdal/" VERSION "\r\n"
732ebfedea0SLionel Sambuc "Cache-Control: no-cache\r\n"
733ebfedea0SLionel Sambuc "Pragma: no-cache\r\n"
734ebfedea0SLionel Sambuc "Content-type: application/octet-stream\r\n"
735ebfedea0SLionel Sambuc "Content-transfer-encoding: binary\r\n\r\n";
736ebfedea0SLionel Sambuc if (rk_IS_SOCKET_ERROR(send(d->s, proto, strlen(proto), 0))) {
737ebfedea0SLionel Sambuc free(data);
738ebfedea0SLionel Sambuc kdc_log(context, config, 0, "HTTP write failed: %s: %s",
739ebfedea0SLionel Sambuc d->addr_string, strerror(rk_SOCK_ERRNO));
740ebfedea0SLionel Sambuc return -1;
741ebfedea0SLionel Sambuc }
742ebfedea0SLionel Sambuc if (rk_IS_SOCKET_ERROR(send(d->s, msg, strlen(msg), 0))) {
743ebfedea0SLionel Sambuc free(data);
744ebfedea0SLionel Sambuc kdc_log(context, config, 0, "HTTP write failed: %s: %s",
745ebfedea0SLionel Sambuc d->addr_string, strerror(rk_SOCK_ERRNO));
746ebfedea0SLionel Sambuc return -1;
747ebfedea0SLionel Sambuc }
748ebfedea0SLionel Sambuc }
749*0a6a1f1dSLionel Sambuc if ((size_t)len > d->len)
750ebfedea0SLionel Sambuc len = d->len;
751ebfedea0SLionel Sambuc memcpy(d->buf, data, len);
752ebfedea0SLionel Sambuc d->len = len;
753ebfedea0SLionel Sambuc free(data);
754ebfedea0SLionel Sambuc return 1;
755ebfedea0SLionel Sambuc }
756ebfedea0SLionel Sambuc
757ebfedea0SLionel Sambuc /*
758ebfedea0SLionel Sambuc * Handle incoming data to the TCP socket in `d[index]'
759ebfedea0SLionel Sambuc */
760ebfedea0SLionel Sambuc
761ebfedea0SLionel Sambuc static void
handle_tcp(krb5_context context,krb5_kdc_configuration * config,struct descr * d,int idx,int min_free)762ebfedea0SLionel Sambuc handle_tcp(krb5_context context,
763ebfedea0SLionel Sambuc krb5_kdc_configuration *config,
764ebfedea0SLionel Sambuc struct descr *d, int idx, int min_free)
765ebfedea0SLionel Sambuc {
766ebfedea0SLionel Sambuc unsigned char buf[1024];
767ebfedea0SLionel Sambuc int n;
768ebfedea0SLionel Sambuc int ret = 0;
769ebfedea0SLionel Sambuc
770ebfedea0SLionel Sambuc if (d[idx].timeout == 0) {
771ebfedea0SLionel Sambuc add_new_tcp (context, config, d, idx, min_free);
772ebfedea0SLionel Sambuc return;
773ebfedea0SLionel Sambuc }
774ebfedea0SLionel Sambuc
775ebfedea0SLionel Sambuc n = recvfrom(d[idx].s, buf, sizeof(buf), 0, NULL, NULL);
776ebfedea0SLionel Sambuc if(rk_IS_SOCKET_ERROR(n)){
777ebfedea0SLionel Sambuc krb5_warn(context, rk_SOCK_ERRNO, "recvfrom failed from %s to %s/%d",
778ebfedea0SLionel Sambuc d[idx].addr_string, descr_type(d + idx),
779ebfedea0SLionel Sambuc ntohs(d[idx].port));
780ebfedea0SLionel Sambuc return;
781ebfedea0SLionel Sambuc } else if (n == 0) {
782ebfedea0SLionel Sambuc krb5_warnx(context, "connection closed before end of data after %lu "
783ebfedea0SLionel Sambuc "bytes from %s to %s/%d", (unsigned long)d[idx].len,
784ebfedea0SLionel Sambuc d[idx].addr_string, descr_type(d + idx),
785ebfedea0SLionel Sambuc ntohs(d[idx].port));
786ebfedea0SLionel Sambuc clear_descr (d + idx);
787ebfedea0SLionel Sambuc return;
788ebfedea0SLionel Sambuc }
789ebfedea0SLionel Sambuc if (grow_descr (context, config, &d[idx], n))
790ebfedea0SLionel Sambuc return;
791ebfedea0SLionel Sambuc memcpy(d[idx].buf + d[idx].len, buf, n);
792ebfedea0SLionel Sambuc d[idx].len += n;
793ebfedea0SLionel Sambuc if(d[idx].len > 4 && d[idx].buf[0] == 0) {
794ebfedea0SLionel Sambuc ret = handle_vanilla_tcp (context, config, &d[idx]);
795ebfedea0SLionel Sambuc } else if(enable_http &&
796ebfedea0SLionel Sambuc d[idx].len >= 4 &&
797ebfedea0SLionel Sambuc strncmp((char *)d[idx].buf, "GET ", 4) == 0 &&
798ebfedea0SLionel Sambuc strncmp((char *)d[idx].buf + d[idx].len - 4,
799ebfedea0SLionel Sambuc "\r\n\r\n", 4) == 0) {
800ebfedea0SLionel Sambuc
801ebfedea0SLionel Sambuc /* remove the trailing \r\n\r\n so the string is NUL terminated */
802ebfedea0SLionel Sambuc d[idx].buf[d[idx].len - 4] = '\0';
803ebfedea0SLionel Sambuc
804ebfedea0SLionel Sambuc ret = handle_http_tcp (context, config, &d[idx]);
805ebfedea0SLionel Sambuc if (ret < 0)
806ebfedea0SLionel Sambuc clear_descr (d + idx);
807ebfedea0SLionel Sambuc } else if (d[idx].len > 4) {
808ebfedea0SLionel Sambuc kdc_log (context, config,
809ebfedea0SLionel Sambuc 0, "TCP data of strange type from %s to %s/%d",
810ebfedea0SLionel Sambuc d[idx].addr_string, descr_type(d + idx),
811ebfedea0SLionel Sambuc ntohs(d[idx].port));
812ebfedea0SLionel Sambuc if (d[idx].buf[0] & 0x80) {
813ebfedea0SLionel Sambuc krb5_data reply;
814ebfedea0SLionel Sambuc
815ebfedea0SLionel Sambuc kdc_log (context, config, 0, "TCP extension not supported");
816ebfedea0SLionel Sambuc
817ebfedea0SLionel Sambuc ret = krb5_mk_error(context,
818ebfedea0SLionel Sambuc KRB5KRB_ERR_FIELD_TOOLONG,
819ebfedea0SLionel Sambuc NULL,
820ebfedea0SLionel Sambuc NULL,
821ebfedea0SLionel Sambuc NULL,
822ebfedea0SLionel Sambuc NULL,
823ebfedea0SLionel Sambuc NULL,
824ebfedea0SLionel Sambuc NULL,
825ebfedea0SLionel Sambuc &reply);
826ebfedea0SLionel Sambuc if (ret == 0) {
827ebfedea0SLionel Sambuc send_reply(context, config, TRUE, d + idx, &reply);
828ebfedea0SLionel Sambuc krb5_data_free(&reply);
829ebfedea0SLionel Sambuc }
830ebfedea0SLionel Sambuc }
831ebfedea0SLionel Sambuc clear_descr(d + idx);
832ebfedea0SLionel Sambuc return;
833ebfedea0SLionel Sambuc }
834ebfedea0SLionel Sambuc if (ret < 0)
835ebfedea0SLionel Sambuc return;
836ebfedea0SLionel Sambuc else if (ret == 1) {
837ebfedea0SLionel Sambuc do_request(context, config,
838ebfedea0SLionel Sambuc d[idx].buf, d[idx].len, TRUE, &d[idx]);
839ebfedea0SLionel Sambuc clear_descr(d + idx);
840ebfedea0SLionel Sambuc }
841ebfedea0SLionel Sambuc }
842ebfedea0SLionel Sambuc
843*0a6a1f1dSLionel Sambuc krb5_boolean
realloc_descrs(struct descr ** d,unsigned int * ndescr)844*0a6a1f1dSLionel Sambuc realloc_descrs(struct descr **d, unsigned int *ndescr)
845*0a6a1f1dSLionel Sambuc {
846*0a6a1f1dSLionel Sambuc struct descr *tmp;
847*0a6a1f1dSLionel Sambuc size_t i;
848*0a6a1f1dSLionel Sambuc
849*0a6a1f1dSLionel Sambuc tmp = realloc(*d, (*ndescr + 4) * sizeof(**d));
850*0a6a1f1dSLionel Sambuc if(tmp == NULL)
851*0a6a1f1dSLionel Sambuc return FALSE;
852*0a6a1f1dSLionel Sambuc
853*0a6a1f1dSLionel Sambuc *d = tmp;
854*0a6a1f1dSLionel Sambuc reinit_descrs (*d, *ndescr);
855*0a6a1f1dSLionel Sambuc memset(*d + *ndescr, 0, 4 * sizeof(**d));
856*0a6a1f1dSLionel Sambuc for(i = *ndescr; i < *ndescr + 4; i++)
857*0a6a1f1dSLionel Sambuc init_descr (*d + i);
858*0a6a1f1dSLionel Sambuc
859*0a6a1f1dSLionel Sambuc *ndescr += 4;
860*0a6a1f1dSLionel Sambuc
861*0a6a1f1dSLionel Sambuc return TRUE;
862*0a6a1f1dSLionel Sambuc }
863*0a6a1f1dSLionel Sambuc
864*0a6a1f1dSLionel Sambuc int
next_min_free(krb5_context context,struct descr ** d,unsigned int * ndescr)865*0a6a1f1dSLionel Sambuc next_min_free(krb5_context context, struct descr **d, unsigned int *ndescr)
866*0a6a1f1dSLionel Sambuc {
867*0a6a1f1dSLionel Sambuc size_t i;
868*0a6a1f1dSLionel Sambuc int min_free;
869*0a6a1f1dSLionel Sambuc
870*0a6a1f1dSLionel Sambuc for(i = 0; i < *ndescr; i++) {
871*0a6a1f1dSLionel Sambuc int s = (*d + i)->s;
872*0a6a1f1dSLionel Sambuc if(rk_IS_BAD_SOCKET(s))
873*0a6a1f1dSLionel Sambuc return i;
874*0a6a1f1dSLionel Sambuc }
875*0a6a1f1dSLionel Sambuc
876*0a6a1f1dSLionel Sambuc min_free = *ndescr;
877*0a6a1f1dSLionel Sambuc if(!realloc_descrs(d, ndescr)) {
878*0a6a1f1dSLionel Sambuc min_free = -1;
879*0a6a1f1dSLionel Sambuc krb5_warnx(context, "No memory");
880*0a6a1f1dSLionel Sambuc }
881*0a6a1f1dSLionel Sambuc
882*0a6a1f1dSLionel Sambuc return min_free;
883*0a6a1f1dSLionel Sambuc }
884*0a6a1f1dSLionel Sambuc
885ebfedea0SLionel Sambuc void
loop(krb5_context context,krb5_kdc_configuration * config)886ebfedea0SLionel Sambuc loop(krb5_context context,
887ebfedea0SLionel Sambuc krb5_kdc_configuration *config)
888ebfedea0SLionel Sambuc {
889ebfedea0SLionel Sambuc struct descr *d;
890ebfedea0SLionel Sambuc unsigned int ndescr;
891ebfedea0SLionel Sambuc
892ebfedea0SLionel Sambuc ndescr = init_sockets(context, config, &d);
893ebfedea0SLionel Sambuc if(ndescr <= 0)
894ebfedea0SLionel Sambuc krb5_errx(context, 1, "No sockets!");
895ebfedea0SLionel Sambuc kdc_log(context, config, 0, "KDC started");
896ebfedea0SLionel Sambuc while(exit_flag == 0){
897ebfedea0SLionel Sambuc struct timeval tmout;
898ebfedea0SLionel Sambuc fd_set fds;
899ebfedea0SLionel Sambuc int min_free = -1;
900ebfedea0SLionel Sambuc int max_fd = 0;
901*0a6a1f1dSLionel Sambuc size_t i;
902ebfedea0SLionel Sambuc
903ebfedea0SLionel Sambuc FD_ZERO(&fds);
904ebfedea0SLionel Sambuc for(i = 0; i < ndescr; i++) {
905ebfedea0SLionel Sambuc if(!rk_IS_BAD_SOCKET(d[i].s)){
906ebfedea0SLionel Sambuc if(d[i].type == SOCK_STREAM &&
907ebfedea0SLionel Sambuc d[i].timeout && d[i].timeout < time(NULL)) {
908ebfedea0SLionel Sambuc kdc_log(context, config, 1,
909ebfedea0SLionel Sambuc "TCP-connection from %s expired after %lu bytes",
910ebfedea0SLionel Sambuc d[i].addr_string, (unsigned long)d[i].len);
911ebfedea0SLionel Sambuc clear_descr(&d[i]);
912ebfedea0SLionel Sambuc continue;
913ebfedea0SLionel Sambuc }
914ebfedea0SLionel Sambuc #ifndef NO_LIMIT_FD_SETSIZE
915ebfedea0SLionel Sambuc if(max_fd < d[i].s)
916ebfedea0SLionel Sambuc max_fd = d[i].s;
917ebfedea0SLionel Sambuc #ifdef FD_SETSIZE
918ebfedea0SLionel Sambuc if (max_fd >= FD_SETSIZE)
919ebfedea0SLionel Sambuc krb5_errx(context, 1, "fd too large");
920ebfedea0SLionel Sambuc #endif
921ebfedea0SLionel Sambuc #endif
922ebfedea0SLionel Sambuc FD_SET(d[i].s, &fds);
923ebfedea0SLionel Sambuc }
924ebfedea0SLionel Sambuc }
925ebfedea0SLionel Sambuc
926ebfedea0SLionel Sambuc tmout.tv_sec = TCP_TIMEOUT;
927ebfedea0SLionel Sambuc tmout.tv_usec = 0;
928ebfedea0SLionel Sambuc switch(select(max_fd + 1, &fds, 0, 0, &tmout)){
929ebfedea0SLionel Sambuc case 0:
930ebfedea0SLionel Sambuc break;
931ebfedea0SLionel Sambuc case -1:
932ebfedea0SLionel Sambuc if (errno != EINTR)
933ebfedea0SLionel Sambuc krb5_warn(context, rk_SOCK_ERRNO, "select");
934ebfedea0SLionel Sambuc break;
935ebfedea0SLionel Sambuc default:
936ebfedea0SLionel Sambuc for(i = 0; i < ndescr; i++)
937ebfedea0SLionel Sambuc if(!rk_IS_BAD_SOCKET(d[i].s) && FD_ISSET(d[i].s, &fds)) {
938*0a6a1f1dSLionel Sambuc min_free = next_min_free(context, &d, &ndescr);
939*0a6a1f1dSLionel Sambuc
940ebfedea0SLionel Sambuc if(d[i].type == SOCK_DGRAM)
941ebfedea0SLionel Sambuc handle_udp(context, config, &d[i]);
942ebfedea0SLionel Sambuc else if(d[i].type == SOCK_STREAM)
943ebfedea0SLionel Sambuc handle_tcp(context, config, d, i, min_free);
944ebfedea0SLionel Sambuc }
945ebfedea0SLionel Sambuc }
946ebfedea0SLionel Sambuc }
947ebfedea0SLionel Sambuc if (0);
948ebfedea0SLionel Sambuc #ifdef SIGXCPU
949ebfedea0SLionel Sambuc else if(exit_flag == SIGXCPU)
950ebfedea0SLionel Sambuc kdc_log(context, config, 0, "CPU time limit exceeded");
951ebfedea0SLionel Sambuc #endif
952ebfedea0SLionel Sambuc else if(exit_flag == SIGINT || exit_flag == SIGTERM)
953ebfedea0SLionel Sambuc kdc_log(context, config, 0, "Terminated");
954ebfedea0SLionel Sambuc else
955ebfedea0SLionel Sambuc kdc_log(context, config, 0, "Unexpected exit reason: %d", exit_flag);
956ebfedea0SLionel Sambuc free (d);
957ebfedea0SLionel Sambuc }
958