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