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