155682Smarkm/*
2233294Sstas * Copyright (c) 1997-2005 Kungliga Tekniska H��gskolan
3233294Sstas * (Royal Institute of Technology, Stockholm, Sweden).
4233294Sstas * All rights reserved.
555682Smarkm *
6233294Sstas * Redistribution and use in source and binary forms, with or without
7233294Sstas * modification, are permitted provided that the following conditions
8233294Sstas * are met:
955682Smarkm *
10233294Sstas * 1. Redistributions of source code must retain the above copyright
11233294Sstas *    notice, this list of conditions and the following disclaimer.
1255682Smarkm *
13233294Sstas * 2. Redistributions in binary form must reproduce the above copyright
14233294Sstas *    notice, this list of conditions and the following disclaimer in the
15233294Sstas *    documentation and/or other materials provided with the distribution.
1655682Smarkm *
17233294Sstas * 3. Neither the name of the Institute nor the names of its contributors
18233294Sstas *    may be used to endorse or promote products derived from this software
19233294Sstas *    without specific prior written permission.
2055682Smarkm *
21233294Sstas * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
22233294Sstas * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23233294Sstas * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24233294Sstas * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
25233294Sstas * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26233294Sstas * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27233294Sstas * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28233294Sstas * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29233294Sstas * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30233294Sstas * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31233294Sstas * SUCH DAMAGE.
3255682Smarkm */
3355682Smarkm
3455682Smarkm#include "kdc_locl.h"
3555682Smarkm
36178825Sdfr/* Should we enable the HTTP hack? */
37178825Sdfrint enable_http = -1;
38178825Sdfr
39178825Sdfr/* Log over requests to the KDC */
40178825Sdfrconst char *request_log;
41178825Sdfr
42178825Sdfr/* A string describing on what ports to listen */
43178825Sdfrconst char *port_str;
44178825Sdfr
45178825Sdfrkrb5_addresses explicit_addresses;
46178825Sdfr
47233294Sstassize_t max_request_udp;
48233294Sstassize_t max_request_tcp;
49178825Sdfr
5057419Smarkm/*
5157419Smarkm * a tuple describing on what to listen
5257419Smarkm */
5357419Smarkm
5455682Smarkmstruct port_desc{
5555682Smarkm    int family;
5655682Smarkm    int type;
5755682Smarkm    int port;
5855682Smarkm};
5955682Smarkm
6057419Smarkm/* the current ones */
6157419Smarkm
6255682Smarkmstatic struct port_desc *ports;
63233294Sstasstatic size_t num_ports;
6455682Smarkm
6557419Smarkm/*
6657419Smarkm * add `family, port, protocol' to the list with duplicate suppresion.
6757419Smarkm */
6857419Smarkm
6955682Smarkmstatic void
70233294Sstasadd_port(krb5_context context,
71178825Sdfr	 int family, int port, const char *protocol)
7255682Smarkm{
7355682Smarkm    int type;
74233294Sstas    size_t i;
7555682Smarkm
7655682Smarkm    if(strcmp(protocol, "udp") == 0)
7755682Smarkm	type = SOCK_DGRAM;
7855682Smarkm    else if(strcmp(protocol, "tcp") == 0)
7955682Smarkm	type = SOCK_STREAM;
8055682Smarkm    else
8155682Smarkm	return;
8255682Smarkm    for(i = 0; i < num_ports; i++){
8355682Smarkm	if(ports[i].type == type
8455682Smarkm	   && ports[i].port == port
8555682Smarkm	   && ports[i].family == family)
8655682Smarkm	    return;
8755682Smarkm    }
8855682Smarkm    ports = realloc(ports, (num_ports + 1) * sizeof(*ports));
8957419Smarkm    if (ports == NULL)
9057419Smarkm	krb5_err (context, 1, errno, "realloc");
9155682Smarkm    ports[num_ports].family = family;
9255682Smarkm    ports[num_ports].type   = type;
9355682Smarkm    ports[num_ports].port   = port;
9455682Smarkm    num_ports++;
9555682Smarkm}
9655682Smarkm
9757419Smarkm/*
9857419Smarkm * add a triple but with service -> port lookup
9957419Smarkm * (this prints warnings for stuff that does not exist)
10057419Smarkm */
10157419Smarkm
10255682Smarkmstatic void
103233294Sstasadd_port_service(krb5_context context,
104178825Sdfr		 int family, const char *service, int port,
10555682Smarkm		 const char *protocol)
10655682Smarkm{
10755682Smarkm    port = krb5_getportbyname (context, service, protocol, port);
108178825Sdfr    add_port (context, family, port, protocol);
10955682Smarkm}
11055682Smarkm
11157419Smarkm/*
11257419Smarkm * add the port with service -> port lookup or string -> number
11357419Smarkm * (no warning is printed)
11457419Smarkm */
11557419Smarkm
11655682Smarkmstatic void
117233294Sstasadd_port_string (krb5_context context,
118178825Sdfr		 int family, const char *str, const char *protocol)
11955682Smarkm{
12055682Smarkm    struct servent *sp;
12155682Smarkm    int port;
12255682Smarkm
123178825Sdfr    sp = roken_getservbyname (str, protocol);
12455682Smarkm    if (sp != NULL) {
12555682Smarkm	port = sp->s_port;
12655682Smarkm    } else {
12755682Smarkm	char *end;
12855682Smarkm
129178825Sdfr	port = htons(strtol(str, &end, 0));
130178825Sdfr	if (end == str)
13155682Smarkm	    return;
13255682Smarkm    }
133178825Sdfr    add_port (context, family, port, protocol);
13455682Smarkm}
13555682Smarkm
13657419Smarkm/*
13757419Smarkm * add the standard collection of ports for `family'
13857419Smarkm */
13957419Smarkm
14055682Smarkmstatic void
141233294Sstasadd_standard_ports (krb5_context context,
142178825Sdfr		    krb5_kdc_configuration *config,
143178825Sdfr		    int family)
14455682Smarkm{
145178825Sdfr    add_port_service(context, family, "kerberos", 88, "udp");
146178825Sdfr    add_port_service(context, family, "kerberos", 88, "tcp");
147178825Sdfr    add_port_service(context, family, "kerberos-sec", 88, "udp");
148178825Sdfr    add_port_service(context, family, "kerberos-sec", 88, "tcp");
14955682Smarkm    if(enable_http)
150178825Sdfr	add_port_service(context, family, "http", 80, "tcp");
151178825Sdfr    if(config->enable_kx509) {
152178825Sdfr	add_port_service(context, family, "kca_service", 9878, "udp");
153178825Sdfr	add_port_service(context, family, "kca_service", 9878, "tcp");
154178825Sdfr    }
155178825Sdfr
15655682Smarkm}
15755682Smarkm
15857419Smarkm/*
15957419Smarkm * parse the set of space-delimited ports in `str' and add them.
16057419Smarkm * "+" => all the standard ones
16157419Smarkm * otherwise it's port|service[/protocol]
16257419Smarkm */
16357419Smarkm
16455682Smarkmstatic void
165233294Sstasparse_ports(krb5_context context,
166178825Sdfr	    krb5_kdc_configuration *config,
167178825Sdfr	    const char *str)
16855682Smarkm{
16955682Smarkm    char *pos = NULL;
17055682Smarkm    char *p;
17155682Smarkm    char *str_copy = strdup (str);
17255682Smarkm
17355682Smarkm    p = strtok_r(str_copy, " \t", &pos);
17455682Smarkm    while(p != NULL) {
17555682Smarkm	if(strcmp(p, "+") == 0) {
17655682Smarkm#ifdef HAVE_IPV6
177178825Sdfr	    add_standard_ports(context, config, AF_INET6);
17855682Smarkm#endif
179178825Sdfr	    add_standard_ports(context, config, AF_INET);
18055682Smarkm	} else {
18155682Smarkm	    char *q = strchr(p, '/');
18255682Smarkm	    if(q){
18355682Smarkm		*q++ = 0;
18455682Smarkm#ifdef HAVE_IPV6
185178825Sdfr		add_port_string(context, AF_INET6, p, q);
18655682Smarkm#endif
187178825Sdfr		add_port_string(context, AF_INET, p, q);
18855682Smarkm	    }else {
18955682Smarkm#ifdef HAVE_IPV6
190178825Sdfr		add_port_string(context, AF_INET6, p, "udp");
191178825Sdfr		add_port_string(context, AF_INET6, p, "tcp");
19255682Smarkm#endif
193178825Sdfr		add_port_string(context, AF_INET, p, "udp");
194178825Sdfr		add_port_string(context, AF_INET, p, "tcp");
19555682Smarkm	    }
19655682Smarkm	}
197233294Sstas
19855682Smarkm	p = strtok_r(NULL, " \t", &pos);
19955682Smarkm    }
20055682Smarkm    free (str_copy);
20155682Smarkm}
20255682Smarkm
20357419Smarkm/*
20457419Smarkm * every socket we listen on
20557419Smarkm */
20657419Smarkm
20755682Smarkmstruct descr {
208233294Sstas    krb5_socket_t s;
20955682Smarkm    int type;
210178825Sdfr    int port;
21155682Smarkm    unsigned char *buf;
21255682Smarkm    size_t size;
21355682Smarkm    size_t len;
21455682Smarkm    time_t timeout;
21557422Smarkm    struct sockaddr_storage __ss;
21657422Smarkm    struct sockaddr *sa;
21772445Sassar    socklen_t sock_len;
21857422Smarkm    char addr_string[128];
21955682Smarkm};
22055682Smarkm
22172445Sassarstatic void
22272445Sassarinit_descr(struct descr *d)
22372445Sassar{
22472445Sassar    memset(d, 0, sizeof(*d));
22572445Sassar    d->sa = (struct sockaddr *)&d->__ss;
226233294Sstas    d->s = rk_INVALID_SOCKET;
22772445Sassar}
22872445Sassar
22955682Smarkm/*
230120945Snectar * re-initialize all `n' ->sa in `d'.
23172445Sassar */
23272445Sassar
23372445Sassarstatic void
23472445Sassarreinit_descrs (struct descr *d, int n)
23572445Sassar{
23672445Sassar    int i;
23772445Sassar
23872445Sassar    for (i = 0; i < n; ++i)
23972445Sassar	d[i].sa = (struct sockaddr *)&d[i].__ss;
24072445Sassar}
24172445Sassar
24272445Sassar/*
24355682Smarkm * Create the socket (family, type, port) in `d'
24455682Smarkm */
24555682Smarkm
246233294Sstasstatic void
247233294Sstasinit_socket(krb5_context context,
248178825Sdfr	    krb5_kdc_configuration *config,
249178825Sdfr	    struct descr *d, krb5_address *a, int family, int type, int port)
25055682Smarkm{
25155682Smarkm    krb5_error_code ret;
25255682Smarkm    struct sockaddr_storage __ss;
25355682Smarkm    struct sockaddr *sa = (struct sockaddr *)&__ss;
254178825Sdfr    krb5_socklen_t sa_size = sizeof(__ss);
25555682Smarkm
25672445Sassar    init_descr (d);
25755682Smarkm
25878527Sassar    ret = krb5_addr2sockaddr (context, a, sa, &sa_size, port);
25955682Smarkm    if (ret) {
26057419Smarkm	krb5_warn(context, ret, "krb5_addr2sockaddr");
261233294Sstas	rk_closesocket(d->s);
262233294Sstas	d->s = rk_INVALID_SOCKET;
26355682Smarkm	return;
26455682Smarkm    }
26555682Smarkm
26655682Smarkm    if (sa->sa_family != family)
26755682Smarkm	return;
26855682Smarkm
26955682Smarkm    d->s = socket(family, type, 0);
270233294Sstas    if(rk_IS_BAD_SOCKET(d->s)){
27155682Smarkm	krb5_warn(context, errno, "socket(%d, %d, 0)", family, type);
272233294Sstas	d->s = rk_INVALID_SOCKET;
27355682Smarkm	return;
27455682Smarkm    }
27555682Smarkm#if defined(HAVE_SETSOCKOPT) && defined(SOL_SOCKET) && defined(SO_REUSEADDR)
27655682Smarkm    {
27755682Smarkm	int one = 1;
27855682Smarkm	setsockopt(d->s, SOL_SOCKET, SO_REUSEADDR, (void *)&one, sizeof(one));
27955682Smarkm    }
28055682Smarkm#endif
28155682Smarkm    d->type = type;
282178825Sdfr    d->port = port;
28355682Smarkm
284233294Sstas    if(rk_IS_SOCKET_ERROR(bind(d->s, sa, sa_size))){
28557419Smarkm	char a_str[256];
28657419Smarkm	size_t len;
28757419Smarkm
28857419Smarkm	krb5_print_address (a, a_str, sizeof(a_str), &len);
28957419Smarkm	krb5_warn(context, errno, "bind %s/%d", a_str, ntohs(port));
290233294Sstas	rk_closesocket(d->s);
291233294Sstas	d->s = rk_INVALID_SOCKET;
29255682Smarkm	return;
29355682Smarkm    }
294233294Sstas    if(type == SOCK_STREAM && rk_IS_SOCKET_ERROR(listen(d->s, SOMAXCONN))){
29557419Smarkm	char a_str[256];
29657419Smarkm	size_t len;
29757419Smarkm
29857419Smarkm	krb5_print_address (a, a_str, sizeof(a_str), &len);
29957419Smarkm	krb5_warn(context, errno, "listen %s/%d", a_str, ntohs(port));
300233294Sstas	rk_closesocket(d->s);
301233294Sstas	d->s = rk_INVALID_SOCKET;
30255682Smarkm	return;
30355682Smarkm    }
30455682Smarkm}
30555682Smarkm
30655682Smarkm/*
30755682Smarkm * Allocate descriptors for all the sockets that we should listen on
30855682Smarkm * and return the number of them.
30955682Smarkm */
31055682Smarkm
31155682Smarkmstatic int
312233294Sstasinit_sockets(krb5_context context,
313178825Sdfr	     krb5_kdc_configuration *config,
314178825Sdfr	     struct descr **desc)
31555682Smarkm{
31655682Smarkm    krb5_error_code ret;
317233294Sstas    size_t i, j;
31855682Smarkm    struct descr *d;
31955682Smarkm    int num = 0;
32055682Smarkm    krb5_addresses addresses;
32155682Smarkm
32257419Smarkm    if (explicit_addresses.len) {
32357419Smarkm	addresses = explicit_addresses;
32457419Smarkm    } else {
32557419Smarkm	ret = krb5_get_all_server_addrs (context, &addresses);
32657419Smarkm	if (ret)
32757419Smarkm	    krb5_err (context, 1, ret, "krb5_get_all_server_addrs");
32857419Smarkm    }
329178825Sdfr    parse_ports(context, config, port_str);
33055682Smarkm    d = malloc(addresses.len * num_ports * sizeof(*d));
33155682Smarkm    if (d == NULL)
33272445Sassar	krb5_errx(context, 1, "malloc(%lu) failed",
33372445Sassar		  (unsigned long)num_ports * sizeof(*d));
33455682Smarkm
33555682Smarkm    for (i = 0; i < num_ports; i++){
33655682Smarkm	for (j = 0; j < addresses.len; ++j) {
337178825Sdfr	    init_socket(context, config, &d[num], &addresses.val[j],
33855682Smarkm			ports[i].family, ports[i].type, ports[i].port);
339233294Sstas	    if(d[num].s != rk_INVALID_SOCKET){
34055682Smarkm		char a_str[80];
34155682Smarkm		size_t len;
34255682Smarkm
34355682Smarkm		krb5_print_address (&addresses.val[j], a_str,
34455682Smarkm				    sizeof(a_str), &len);
34555682Smarkm
346178825Sdfr		kdc_log(context, config, 5, "listening on %s port %u/%s",
34755682Smarkm			a_str,
348233294Sstas			ntohs(ports[i].port),
34955682Smarkm			(ports[i].type == SOCK_STREAM) ? "tcp" : "udp");
35055682Smarkm		/* XXX */
35155682Smarkm		num++;
35255682Smarkm	    }
35355682Smarkm	}
35455682Smarkm    }
35555682Smarkm    krb5_free_addresses (context, &addresses);
35655682Smarkm    d = realloc(d, num * sizeof(*d));
35755682Smarkm    if (d == NULL && num != 0)
35872445Sassar	krb5_errx(context, 1, "realloc(%lu) failed",
35972445Sassar		  (unsigned long)num * sizeof(*d));
36072445Sassar    reinit_descrs (d, num);
36155682Smarkm    *desc = d;
36255682Smarkm    return num;
36355682Smarkm}
36455682Smarkm
36557419Smarkm/*
366178825Sdfr *
36757419Smarkm */
36857419Smarkm
369178825Sdfrstatic const char *
370178825Sdfrdescr_type(struct descr *d)
37155682Smarkm{
372178825Sdfr    if (d->type == SOCK_DGRAM)
373178825Sdfr	return "udp";
374178825Sdfr    else if (d->type == SOCK_STREAM)
375178825Sdfr	return "tcp";
376178825Sdfr    return "unknown";
37755682Smarkm}
37855682Smarkm
37955682Smarkmstatic void
380233294Sstasaddr_to_string(krb5_context context,
381178825Sdfr	       struct sockaddr *addr, size_t addr_len, char *str, size_t len)
38255682Smarkm{
38355682Smarkm    krb5_address a;
384102644Snectar    if(krb5_sockaddr2address(context, addr, &a) == 0) {
385102644Snectar	if(krb5_print_address(&a, str, len, &len) == 0) {
386102644Snectar	    krb5_free_address(context, &a);
387102644Snectar	    return;
388102644Snectar	}
38955682Smarkm	krb5_free_address(context, &a);
39055682Smarkm    }
39155682Smarkm    snprintf(str, len, "<family=%d>", addr->sa_family);
39255682Smarkm}
39355682Smarkm
39457422Smarkm/*
395178825Sdfr *
396178825Sdfr */
397178825Sdfr
398178825Sdfrstatic void
399233294Sstassend_reply(krb5_context context,
400178825Sdfr	   krb5_kdc_configuration *config,
401178825Sdfr	   krb5_boolean prependlength,
402178825Sdfr	   struct descr *d,
403178825Sdfr	   krb5_data *reply)
404178825Sdfr{
405178825Sdfr    kdc_log(context, config, 5,
406178825Sdfr	    "sending %lu bytes to %s", (unsigned long)reply->length,
407178825Sdfr	    d->addr_string);
408178825Sdfr    if(prependlength){
409178825Sdfr	unsigned char l[4];
410178825Sdfr	l[0] = (reply->length >> 24) & 0xff;
411178825Sdfr	l[1] = (reply->length >> 16) & 0xff;
412178825Sdfr	l[2] = (reply->length >> 8) & 0xff;
413178825Sdfr	l[3] = reply->length & 0xff;
414233294Sstas	if(rk_IS_SOCKET_ERROR(sendto(d->s, l, sizeof(l), 0, d->sa, d->sock_len))) {
415233294Sstas	    kdc_log (context, config,
416233294Sstas		     0, "sendto(%s): %s", d->addr_string,
417233294Sstas		     strerror(rk_SOCK_ERRNO));
418178825Sdfr	    return;
419178825Sdfr	}
420178825Sdfr    }
421233294Sstas    if(rk_IS_SOCKET_ERROR(sendto(d->s, reply->data, reply->length, 0, d->sa, d->sock_len))) {
422233294Sstas	kdc_log (context, config, 0, "sendto(%s): %s", d->addr_string,
423233294Sstas		 strerror(rk_SOCK_ERRNO));
424178825Sdfr	return;
425178825Sdfr    }
426178825Sdfr}
427178825Sdfr
428178825Sdfr/*
42957422Smarkm * Handle the request in `buf, len' to socket `d'
43057422Smarkm */
43157422Smarkm
43255682Smarkmstatic void
433233294Sstasdo_request(krb5_context context,
434178825Sdfr	   krb5_kdc_configuration *config,
435178825Sdfr	   void *buf, size_t len, krb5_boolean prependlength,
43657422Smarkm	   struct descr *d)
43755682Smarkm{
43855682Smarkm    krb5_error_code ret;
43955682Smarkm    krb5_data reply;
440178825Sdfr    int datagram_reply = (d->type == SOCK_DGRAM);
441178825Sdfr
442178825Sdfr    krb5_kdc_update_time(NULL);
443178825Sdfr
444178825Sdfr    krb5_data_zero(&reply);
445233294Sstas    ret = krb5_kdc_process_request(context, config,
446178825Sdfr				   buf, len, &reply, &prependlength,
447178825Sdfr				   d->addr_string, d->sa,
448178825Sdfr				   datagram_reply);
449178825Sdfr    if(request_log)
450178825Sdfr	krb5_kdc_save_request(context, request_log, buf, len, &reply, d->sa);
45155682Smarkm    if(reply.length){
452178825Sdfr	send_reply(context, config, prependlength, d, &reply);
45355682Smarkm	krb5_data_free(&reply);
45455682Smarkm    }
45555682Smarkm    if(ret)
456233294Sstas	kdc_log(context, config, 0,
457233294Sstas		"Failed processing %lu byte request from %s",
45857422Smarkm		(unsigned long)len, d->addr_string);
45955682Smarkm}
46055682Smarkm
46157422Smarkm/*
46257422Smarkm * Handle incoming data to the UDP socket in `d'
46357422Smarkm */
46457422Smarkm
46555682Smarkmstatic void
466233294Sstashandle_udp(krb5_context context,
467178825Sdfr	   krb5_kdc_configuration *config,
468178825Sdfr	   struct descr *d)
46955682Smarkm{
47055682Smarkm    unsigned char *buf;
471233294Sstas    ssize_t n;
47255682Smarkm
473233294Sstas    buf = malloc(max_request_udp);
47455682Smarkm    if(buf == NULL){
475233294Sstas	kdc_log(context, config, 0, "Failed to allocate %lu bytes", (unsigned long)max_request_udp);
47655682Smarkm	return;
47755682Smarkm    }
47855682Smarkm
47957422Smarkm    d->sock_len = sizeof(d->__ss);
480233294Sstas    n = recvfrom(d->s, buf, max_request_udp, 0, d->sa, &d->sock_len);
481233294Sstas    if(rk_IS_SOCKET_ERROR(n))
482233294Sstas	krb5_warn(context, rk_SOCK_ERRNO, "recvfrom");
48357422Smarkm    else {
484178825Sdfr	addr_to_string (context, d->sa, d->sock_len,
48557422Smarkm			d->addr_string, sizeof(d->addr_string));
486233294Sstas	if ((size_t)n == max_request_udp) {
487233294Sstas	    krb5_data data;
488233294Sstas	    krb5_warn(context, errno,
489233294Sstas		      "recvfrom: truncated packet from %s, asking for TCP",
490233294Sstas		      d->addr_string);
491233294Sstas	    krb5_mk_error(context,
492233294Sstas			  KRB5KRB_ERR_RESPONSE_TOO_BIG,
493233294Sstas			  NULL,
494233294Sstas			  NULL,
495233294Sstas			  NULL,
496233294Sstas			  NULL,
497233294Sstas			  NULL,
498233294Sstas			  NULL,
499233294Sstas			  &data);
500233294Sstas	    send_reply(context, config, FALSE, d, &data);
501233294Sstas	    krb5_data_free(&data);
502233294Sstas	} else {
503233294Sstas	    do_request(context, config, buf, n, FALSE, d);
504233294Sstas	}
50555682Smarkm    }
50655682Smarkm    free (buf);
50755682Smarkm}
50855682Smarkm
50955682Smarkmstatic void
51055682Smarkmclear_descr(struct descr *d)
51155682Smarkm{
51255682Smarkm    if(d->buf)
51355682Smarkm	memset(d->buf, 0, d->size);
51455682Smarkm    d->len = 0;
515233294Sstas    if(d->s != rk_INVALID_SOCKET)
516233294Sstas	rk_closesocket(d->s);
517233294Sstas    d->s = rk_INVALID_SOCKET;
51855682Smarkm}
51955682Smarkm
52055682Smarkm
52155682Smarkm/* remove HTTP %-quoting from buf */
52255682Smarkmstatic int
52355682Smarkmde_http(char *buf)
52455682Smarkm{
525178825Sdfr    unsigned char *p, *q;
526178825Sdfr    for(p = q = (unsigned char *)buf; *p; p++, q++) {
527107207Snectar	if(*p == '%' && isxdigit(p[1]) && isxdigit(p[2])) {
52855682Smarkm	    unsigned int x;
529178825Sdfr	    if(sscanf((char *)p + 1, "%2x", &x) != 1)
53055682Smarkm		return -1;
53155682Smarkm	    *q = x;
53255682Smarkm	    p += 2;
53355682Smarkm	} else
53455682Smarkm	    *q = *p;
53555682Smarkm    }
53655682Smarkm    *q = '\0';
53755682Smarkm    return 0;
53855682Smarkm}
53955682Smarkm
54055682Smarkm#define TCP_TIMEOUT 4
54155682Smarkm
54255682Smarkm/*
54372445Sassar * accept a new TCP connection on `d[parent]' and store it in `d[child]'
54455682Smarkm */
54555682Smarkm
54655682Smarkmstatic void
547233294Sstasadd_new_tcp (krb5_context context,
548178825Sdfr	     krb5_kdc_configuration *config,
549178825Sdfr	     struct descr *d, int parent, int child)
55055682Smarkm{
551233294Sstas    krb5_socket_t s;
55255682Smarkm
55372445Sassar    if (child == -1)
55472445Sassar	return;
55572445Sassar
55672445Sassar    d[child].sock_len = sizeof(d[child].__ss);
55772445Sassar    s = accept(d[parent].s, d[child].sa, &d[child].sock_len);
558233294Sstas    if(rk_IS_BAD_SOCKET(s)) {
559233294Sstas	krb5_warn(context, rk_SOCK_ERRNO, "accept");
56055682Smarkm	return;
56155682Smarkm    }
562233294Sstas
563233294Sstas#ifdef FD_SETSIZE
56472445Sassar    if (s >= FD_SETSIZE) {
56572445Sassar	krb5_warnx(context, "socket FD too large");
566233294Sstas	rk_closesocket (s);
56755682Smarkm	return;
56855682Smarkm    }
569233294Sstas#endif
57072445Sassar
57172445Sassar    d[child].s = s;
57272445Sassar    d[child].timeout = time(NULL) + TCP_TIMEOUT;
57372445Sassar    d[child].type = SOCK_STREAM;
574233294Sstas    addr_to_string (context,
575178825Sdfr		    d[child].sa, d[child].sock_len,
57672445Sassar		    d[child].addr_string, sizeof(d[child].addr_string));
57755682Smarkm}
57855682Smarkm
57955682Smarkm/*
58055682Smarkm * Grow `d' to handle at least `n'.
58155682Smarkm * Return != 0 if fails
58255682Smarkm */
58355682Smarkm
58455682Smarkmstatic int
585233294Sstasgrow_descr (krb5_context context,
586178825Sdfr	    krb5_kdc_configuration *config,
587178825Sdfr	    struct descr *d, size_t n)
58855682Smarkm{
58955682Smarkm    if (d->size - d->len < n) {
59055682Smarkm	unsigned char *tmp;
591233294Sstas	size_t grow;
59255682Smarkm
593127808Snectar	grow = max(1024, d->len + n);
594233294Sstas	if (d->size + grow > max_request_tcp) {
595178825Sdfr	    kdc_log(context, config, 0, "Request exceeds max request size (%lu bytes).",
596127808Snectar		    (unsigned long)d->size + grow);
59755682Smarkm	    clear_descr(d);
59855682Smarkm	    return -1;
59955682Smarkm	}
600127808Snectar	tmp = realloc (d->buf, d->size + grow);
60155682Smarkm	if (tmp == NULL) {
602178825Sdfr	    kdc_log(context, config, 0, "Failed to re-allocate %lu bytes.",
603127808Snectar		    (unsigned long)d->size + grow);
60455682Smarkm	    clear_descr(d);
60555682Smarkm	    return -1;
60655682Smarkm	}
607127808Snectar	d->size += grow;
60855682Smarkm	d->buf = tmp;
60955682Smarkm    }
61055682Smarkm    return 0;
61155682Smarkm}
61255682Smarkm
61355682Smarkm/*
61455682Smarkm * Try to handle the TCP data at `d->buf, d->len'.
61555682Smarkm * Return -1 if failed, 0 if succesful, and 1 if data is complete.
61655682Smarkm */
61755682Smarkm
61855682Smarkmstatic int
619233294Sstashandle_vanilla_tcp (krb5_context context,
620178825Sdfr		    krb5_kdc_configuration *config,
621178825Sdfr		    struct descr *d)
62255682Smarkm{
62355682Smarkm    krb5_storage *sp;
624178825Sdfr    uint32_t len;
62555682Smarkm
62655682Smarkm    sp = krb5_storage_from_mem(d->buf, d->len);
62755682Smarkm    if (sp == NULL) {
628178825Sdfr	kdc_log (context, config, 0, "krb5_storage_from_mem failed");
62955682Smarkm	return -1;
63055682Smarkm    }
631178825Sdfr    krb5_ret_uint32(sp, &len);
63255682Smarkm    krb5_storage_free(sp);
63355682Smarkm    if(d->len - 4 >= len) {
63472445Sassar	memmove(d->buf, d->buf + 4, d->len - 4);
635178825Sdfr	d->len -= 4;
63655682Smarkm	return 1;
63755682Smarkm    }
63855682Smarkm    return 0;
63955682Smarkm}
64055682Smarkm
64155682Smarkm/*
64255682Smarkm * Try to handle the TCP/HTTP data at `d->buf, d->len'.
64355682Smarkm * Return -1 if failed, 0 if succesful, and 1 if data is complete.
64455682Smarkm */
64555682Smarkm
64655682Smarkmstatic int
647233294Sstashandle_http_tcp (krb5_context context,
648178825Sdfr		 krb5_kdc_configuration *config,
649178825Sdfr		 struct descr *d)
65055682Smarkm{
65155682Smarkm    char *s, *p, *t;
65255682Smarkm    void *data;
65355682Smarkm    char *proto;
65455682Smarkm    int len;
65555682Smarkm
65655682Smarkm    s = (char *)d->buf;
65755682Smarkm
658233294Sstas    /* If its a multi line query, truncate off the first line */
65955682Smarkm    p = strstr(s, "\r\n");
660233294Sstas    if (p)
661233294Sstas	*p = 0;
66255682Smarkm
66355682Smarkm    p = NULL;
66455682Smarkm    t = strtok_r(s, " \t", &p);
66555682Smarkm    if (t == NULL) {
666233294Sstas	kdc_log(context, config, 0,
667233294Sstas		"Missing HTTP operand (GET) request from %s", d->addr_string);
66855682Smarkm	return -1;
66955682Smarkm    }
670233294Sstas
67155682Smarkm    t = strtok_r(NULL, " \t", &p);
67255682Smarkm    if(t == NULL) {
673233294Sstas	kdc_log(context, config, 0,
674233294Sstas		"Missing HTTP GET data in request from %s", d->addr_string);
67555682Smarkm	return -1;
67655682Smarkm    }
677233294Sstas
67855682Smarkm    data = malloc(strlen(t));
67955682Smarkm    if (data == NULL) {
680178825Sdfr	kdc_log(context, config, 0, "Failed to allocate %lu bytes",
68178527Sassar		(unsigned long)strlen(t));
68255682Smarkm	return -1;
68355682Smarkm    }
68455682Smarkm    if(*t == '/')
68555682Smarkm	t++;
68655682Smarkm    if(de_http(t) != 0) {
687178825Sdfr	kdc_log(context, config, 0, "Malformed HTTP request from %s", d->addr_string);
688178825Sdfr	kdc_log(context, config, 5, "HTTP request: %s", t);
68955682Smarkm	free(data);
69055682Smarkm	return -1;
69155682Smarkm    }
69255682Smarkm    proto = strtok_r(NULL, " \t", &p);
69355682Smarkm    if (proto == NULL) {
694178825Sdfr	kdc_log(context, config, 0, "Malformed HTTP request from %s", d->addr_string);
69555682Smarkm	free(data);
69655682Smarkm	return -1;
69755682Smarkm    }
69855682Smarkm    len = base64_decode(t, data);
69955682Smarkm    if(len <= 0){
700233294Sstas	const char *msg =
70155682Smarkm	    " 404 Not found\r\n"
70255682Smarkm	    "Server: Heimdal/" VERSION "\r\n"
703102644Snectar	    "Cache-Control: no-cache\r\n"
704102644Snectar	    "Pragma: no-cache\r\n"
70555682Smarkm	    "Content-type: text/html\r\n"
70655682Smarkm	    "Content-transfer-encoding: 8bit\r\n\r\n"
70755682Smarkm	    "<TITLE>404 Not found</TITLE>\r\n"
70855682Smarkm	    "<H1>404 Not found</H1>\r\n"
70955682Smarkm	    "That page doesn't exist, maybe you are looking for "
710178825Sdfr	    "<A HREF=\"http://www.h5l.org/\">Heimdal</A>?\r\n";
711178825Sdfr	kdc_log(context, config, 0, "HTTP request from %s is non KDC request", d->addr_string);
712178825Sdfr	kdc_log(context, config, 5, "HTTP request: %s", t);
71355682Smarkm	free(data);
714233294Sstas	if (rk_IS_SOCKET_ERROR(send(d->s, proto, strlen(proto), 0))) {
715233294Sstas	    kdc_log(context, config, 0, "HTTP write failed: %s: %s",
716233294Sstas		    d->addr_string, strerror(rk_SOCK_ERRNO));
717178825Sdfr	    return -1;
718178825Sdfr	}
719233294Sstas	if (rk_IS_SOCKET_ERROR(send(d->s, msg, strlen(msg), 0))) {
720233294Sstas	    kdc_log(context, config, 0, "HTTP write failed: %s: %s",
721233294Sstas		    d->addr_string, strerror(rk_SOCK_ERRNO));
722178825Sdfr	    return -1;
723178825Sdfr	}
72455682Smarkm	return -1;
72555682Smarkm    }
72655682Smarkm    {
727233294Sstas	const char *msg =
72855682Smarkm	    " 200 OK\r\n"
72955682Smarkm	    "Server: Heimdal/" VERSION "\r\n"
730102644Snectar	    "Cache-Control: no-cache\r\n"
731102644Snectar	    "Pragma: no-cache\r\n"
73255682Smarkm	    "Content-type: application/octet-stream\r\n"
73355682Smarkm	    "Content-transfer-encoding: binary\r\n\r\n";
734233294Sstas	if (rk_IS_SOCKET_ERROR(send(d->s, proto, strlen(proto), 0))) {
735233294Sstas	    free(data);
736233294Sstas	    kdc_log(context, config, 0, "HTTP write failed: %s: %s",
737233294Sstas		    d->addr_string, strerror(rk_SOCK_ERRNO));
738178825Sdfr	    return -1;
739178825Sdfr	}
740233294Sstas	if (rk_IS_SOCKET_ERROR(send(d->s, msg, strlen(msg), 0))) {
741233294Sstas	    free(data);
742233294Sstas	    kdc_log(context, config, 0, "HTTP write failed: %s: %s",
743233294Sstas		    d->addr_string, strerror(rk_SOCK_ERRNO));
744178825Sdfr	    return -1;
745178825Sdfr	}
74655682Smarkm    }
747233294Sstas    if ((size_t)len > d->len)
748233294Sstas        len = d->len;
74955682Smarkm    memcpy(d->buf, data, len);
75055682Smarkm    d->len = len;
75155682Smarkm    free(data);
75255682Smarkm    return 1;
75355682Smarkm}
75455682Smarkm
75555682Smarkm/*
75655682Smarkm * Handle incoming data to the TCP socket in `d[index]'
75755682Smarkm */
75855682Smarkm
75955682Smarkmstatic void
760233294Sstashandle_tcp(krb5_context context,
761178825Sdfr	   krb5_kdc_configuration *config,
762178825Sdfr	   struct descr *d, int idx, int min_free)
76355682Smarkm{
76455682Smarkm    unsigned char buf[1024];
76555682Smarkm    int n;
76655682Smarkm    int ret = 0;
76755682Smarkm
768178825Sdfr    if (d[idx].timeout == 0) {
769178825Sdfr	add_new_tcp (context, config, d, idx, min_free);
77055682Smarkm	return;
77155682Smarkm    }
77255682Smarkm
773178825Sdfr    n = recvfrom(d[idx].s, buf, sizeof(buf), 0, NULL, NULL);
774233294Sstas    if(rk_IS_SOCKET_ERROR(n)){
775233294Sstas	krb5_warn(context, rk_SOCK_ERRNO, "recvfrom failed from %s to %s/%d",
776233294Sstas		  d[idx].addr_string, descr_type(d + idx),
777178825Sdfr		  ntohs(d[idx].port));
77855682Smarkm	return;
779127808Snectar    } else if (n == 0) {
780142403Snectar	krb5_warnx(context, "connection closed before end of data after %lu "
781233294Sstas		   "bytes from %s to %s/%d", (unsigned long)d[idx].len,
782233294Sstas		   d[idx].addr_string, descr_type(d + idx),
783178825Sdfr		   ntohs(d[idx].port));
784178825Sdfr	clear_descr (d + idx);
785127808Snectar	return;
78655682Smarkm    }
787178825Sdfr    if (grow_descr (context, config, &d[idx], n))
78855682Smarkm	return;
789178825Sdfr    memcpy(d[idx].buf + d[idx].len, buf, n);
790178825Sdfr    d[idx].len += n;
791178825Sdfr    if(d[idx].len > 4 && d[idx].buf[0] == 0) {
792178825Sdfr	ret = handle_vanilla_tcp (context, config, &d[idx]);
79355682Smarkm    } else if(enable_http &&
794178825Sdfr	      d[idx].len >= 4 &&
795233294Sstas	      strncmp((char *)d[idx].buf, "GET ", 4) == 0 &&
796178825Sdfr	      strncmp((char *)d[idx].buf + d[idx].len - 4,
79755682Smarkm		      "\r\n\r\n", 4) == 0) {
798233294Sstas
799233294Sstas        /* remove the trailing \r\n\r\n so the string is NUL terminated */
800233294Sstas        d[idx].buf[d[idx].len - 4] = '\0';
801233294Sstas
802178825Sdfr	ret = handle_http_tcp (context, config, &d[idx]);
80355682Smarkm	if (ret < 0)
804178825Sdfr	    clear_descr (d + idx);
805178825Sdfr    } else if (d[idx].len > 4) {
806233294Sstas	kdc_log (context, config,
807178825Sdfr		 0, "TCP data of strange type from %s to %s/%d",
808233294Sstas		 d[idx].addr_string, descr_type(d + idx),
809178825Sdfr		 ntohs(d[idx].port));
810178825Sdfr	if (d[idx].buf[0] & 0x80) {
811178825Sdfr	    krb5_data reply;
812178825Sdfr
813178825Sdfr	    kdc_log (context, config, 0, "TCP extension not supported");
814178825Sdfr
815178825Sdfr	    ret = krb5_mk_error(context,
816178825Sdfr				KRB5KRB_ERR_FIELD_TOOLONG,
817178825Sdfr				NULL,
818178825Sdfr				NULL,
819178825Sdfr				NULL,
820178825Sdfr				NULL,
821178825Sdfr				NULL,
822178825Sdfr				NULL,
823178825Sdfr				&reply);
824178825Sdfr	    if (ret == 0) {
825178825Sdfr		send_reply(context, config, TRUE, d + idx, &reply);
826178825Sdfr		krb5_data_free(&reply);
827178825Sdfr	    }
828178825Sdfr	}
829178825Sdfr	clear_descr(d + idx);
83055682Smarkm	return;
83155682Smarkm    }
83255682Smarkm    if (ret < 0)
83355682Smarkm	return;
83455682Smarkm    else if (ret == 1) {
835233294Sstas	do_request(context, config,
836178825Sdfr		   d[idx].buf, d[idx].len, TRUE, &d[idx]);
837178825Sdfr	clear_descr(d + idx);
83855682Smarkm    }
83955682Smarkm}
84055682Smarkm
84155682Smarkmvoid
842233294Sstasloop(krb5_context context,
843178825Sdfr     krb5_kdc_configuration *config)
84455682Smarkm{
84555682Smarkm    struct descr *d;
846233294Sstas    unsigned int ndescr;
84755682Smarkm
848178825Sdfr    ndescr = init_sockets(context, config, &d);
84955682Smarkm    if(ndescr <= 0)
85055682Smarkm	krb5_errx(context, 1, "No sockets!");
851178825Sdfr    kdc_log(context, config, 0, "KDC started");
85255682Smarkm    while(exit_flag == 0){
85355682Smarkm	struct timeval tmout;
85455682Smarkm	fd_set fds;
85555682Smarkm	int min_free = -1;
85655682Smarkm	int max_fd = 0;
857233294Sstas	size_t i;
85872445Sassar
85955682Smarkm	FD_ZERO(&fds);
86072445Sassar	for(i = 0; i < ndescr; i++) {
861233294Sstas	    if(!rk_IS_BAD_SOCKET(d[i].s)){
862233294Sstas		if(d[i].type == SOCK_STREAM &&
86357422Smarkm		   d[i].timeout && d[i].timeout < time(NULL)) {
864233294Sstas		    kdc_log(context, config, 1,
865178825Sdfr			    "TCP-connection from %s expired after %lu bytes",
86678527Sassar			    d[i].addr_string, (unsigned long)d[i].len);
86755682Smarkm		    clear_descr(&d[i]);
86855682Smarkm		    continue;
86955682Smarkm		}
870233294Sstas#ifndef NO_LIMIT_FD_SETSIZE
87155682Smarkm		if(max_fd < d[i].s)
87255682Smarkm		    max_fd = d[i].s;
873233294Sstas#ifdef FD_SETSIZE
87472445Sassar		if (max_fd >= FD_SETSIZE)
87572445Sassar		    krb5_errx(context, 1, "fd too large");
876233294Sstas#endif
877233294Sstas#endif
87855682Smarkm		FD_SET(d[i].s, &fds);
879233294Sstas	    } else if(min_free < 0 || i < (size_t)min_free)
88055682Smarkm		min_free = i;
88155682Smarkm	}
88255682Smarkm	if(min_free == -1){
88355682Smarkm	    struct descr *tmp;
88455682Smarkm	    tmp = realloc(d, (ndescr + 4) * sizeof(*d));
88555682Smarkm	    if(tmp == NULL)
88655682Smarkm		krb5_warnx(context, "No memory");
88772445Sassar	    else {
88855682Smarkm		d = tmp;
88972445Sassar		reinit_descrs (d, ndescr);
89055682Smarkm		memset(d + ndescr, 0, 4 * sizeof(*d));
89155682Smarkm		for(i = ndescr; i < ndescr + 4; i++)
89272445Sassar		    init_descr (&d[i]);
89355682Smarkm		min_free = ndescr;
89455682Smarkm		ndescr += 4;
89555682Smarkm	    }
89655682Smarkm	}
897233294Sstas
89855682Smarkm	tmout.tv_sec = TCP_TIMEOUT;
89955682Smarkm	tmout.tv_usec = 0;
90055682Smarkm	switch(select(max_fd + 1, &fds, 0, 0, &tmout)){
90155682Smarkm	case 0:
90255682Smarkm	    break;
90355682Smarkm	case -1:
90457419Smarkm	    if (errno != EINTR)
905233294Sstas		krb5_warn(context, rk_SOCK_ERRNO, "select");
90655682Smarkm	    break;
90755682Smarkm	default:
90855682Smarkm	    for(i = 0; i < ndescr; i++)
909233294Sstas		if(!rk_IS_BAD_SOCKET(d[i].s) && FD_ISSET(d[i].s, &fds)) {
91055682Smarkm		    if(d[i].type == SOCK_DGRAM)
911178825Sdfr			handle_udp(context, config, &d[i]);
91255682Smarkm		    else if(d[i].type == SOCK_STREAM)
913178825Sdfr			handle_tcp(context, config, d, i, min_free);
91455682Smarkm		}
91555682Smarkm	}
91655682Smarkm    }
917233294Sstas    if (0);
918233294Sstas#ifdef SIGXCPU
919233294Sstas    else if(exit_flag == SIGXCPU)
920178825Sdfr	kdc_log(context, config, 0, "CPU time limit exceeded");
921233294Sstas#endif
922178825Sdfr    else if(exit_flag == SIGINT || exit_flag == SIGTERM)
923178825Sdfr	kdc_log(context, config, 0, "Terminated");
924178825Sdfr    else
925178825Sdfr	kdc_log(context, config, 0, "Unexpected exit reason: %d", exit_flag);
92655682Smarkm    free (d);
92755682Smarkm}
928