xref: /freebsd/crypto/heimdal/kdc/connect.c (revision b528cefc6b8f9670b31a865051741d946cb37085)
1 /*
2  * Copyright (c) 1997-1999 Kungliga Tekniska H�gskolan
3  * (Royal Institute of Technology, Stockholm, Sweden).
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  *
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  *
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  *
17  * 3. Neither the name of the Institute nor the names of its contributors
18  *    may be used to endorse or promote products derived from this software
19  *    without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  */
33 
34 #include "kdc_locl.h"
35 
36 RCSID("$Id: connect.c,v 1.68 1999/12/02 17:04:58 joda Exp $");
37 
38 struct port_desc{
39     int family;
40     int type;
41     int port;
42 };
43 
44 static struct port_desc *ports;
45 static int num_ports;
46 
47 static void
48 add_port(int family, int port, const char *protocol)
49 {
50     int type;
51     int i;
52 
53     if(strcmp(protocol, "udp") == 0)
54 	type = SOCK_DGRAM;
55     else if(strcmp(protocol, "tcp") == 0)
56 	type = SOCK_STREAM;
57     else
58 	return;
59     for(i = 0; i < num_ports; i++){
60 	if(ports[i].type == type
61 	   && ports[i].port == port
62 	   && ports[i].family == family)
63 	    return;
64     }
65     ports = realloc(ports, (num_ports + 1) * sizeof(*ports));
66     ports[num_ports].family = family;
67     ports[num_ports].type   = type;
68     ports[num_ports].port   = port;
69     num_ports++;
70 }
71 
72 static void
73 add_port_service(int family, const char *service, int port,
74 		 const char *protocol)
75 {
76     port = krb5_getportbyname (context, service, protocol, port);
77     add_port (family, port, protocol);
78 }
79 
80 static void
81 add_port_string (int family, const char *port_str, const char *protocol)
82 {
83     struct servent *sp;
84     int port;
85 
86     sp = roken_getservbyname (port_str, protocol);
87     if (sp != NULL) {
88 	port = sp->s_port;
89     } else {
90 	char *end;
91 
92 	port = htons(strtol(port_str, &end, 0));
93 	if (end == port_str)
94 	    return;
95     }
96     add_port (family, port, protocol);
97 }
98 
99 static void
100 add_standard_ports (int family)
101 {
102     add_port_service(family, "kerberos", 88, "udp");
103     add_port_service(family, "kerberos", 88, "tcp");
104     add_port_service(family, "kerberos-sec", 88, "udp");
105     add_port_service(family, "kerberos-sec", 88, "tcp");
106     add_port_service(family, "kerberos-iv", 750, "udp");
107     add_port_service(family, "kerberos-iv", 750, "tcp");
108     if(enable_http)
109 	add_port_service(family, "http", 80, "tcp");
110 #ifdef KASERVER
111     if (enable_kaserver)
112 	add_port_service(family, "afs3-kaserver", 7004, "udp");
113 #endif
114 }
115 
116 static void
117 parse_ports(const char *str)
118 {
119     char *pos = NULL;
120     char *p;
121     char *str_copy = strdup (str);
122 
123     p = strtok_r(str_copy, " \t", &pos);
124     while(p != NULL) {
125 	if(strcmp(p, "+") == 0) {
126 #ifdef HAVE_IPV6
127 	    add_standard_ports(AF_INET6);
128 #endif
129 	    add_standard_ports(AF_INET);
130 	} else {
131 	    char *q = strchr(p, '/');
132 	    if(q){
133 		*q++ = 0;
134 #ifdef HAVE_IPV6
135 		add_port_string(AF_INET6, p, q);
136 #endif
137 		add_port_string(AF_INET, p, q);
138 	    }else {
139 #ifdef HAVE_IPV6
140 		add_port_string(AF_INET6, p, "udp");
141 		add_port_string(AF_INET6, p, "tcp");
142 #endif
143 		add_port_string(AF_INET, p, "udp");
144 		add_port_string(AF_INET, p, "tcp");
145 	    }
146 	}
147 
148 	p = strtok_r(NULL, " \t", &pos);
149     }
150     free (str_copy);
151 }
152 
153 struct descr {
154     int s;
155     int type;
156     unsigned char *buf;
157     size_t size;
158     size_t len;
159     time_t timeout;
160 };
161 
162 /*
163  * Create the socket (family, type, port) in `d'
164  */
165 
166 static void
167 init_socket(struct descr *d, krb5_address *a, int family, int type, int port)
168 {
169     krb5_error_code ret;
170     struct sockaddr_storage __ss;
171     struct sockaddr *sa = (struct sockaddr *)&__ss;
172     int sa_size;
173 
174     memset(d, 0, sizeof(*d));
175     d->s = -1;
176 
177     ret = krb5_addr2sockaddr (a, sa, &sa_size, port);
178     if (ret) {
179 	krb5_warn(context, ret, "krb5_anyaddr");
180 	close(d->s);
181 	d->s = -1;
182 	return;
183     }
184 
185     if (sa->sa_family != family)
186 	return;
187 
188     d->s = socket(family, type, 0);
189     if(d->s < 0){
190 	krb5_warn(context, errno, "socket(%d, %d, 0)", family, type);
191 	d->s = -1;
192 	return;
193     }
194 #if defined(HAVE_SETSOCKOPT) && defined(SOL_SOCKET) && defined(SO_REUSEADDR)
195     {
196 	int one = 1;
197 	setsockopt(d->s, SOL_SOCKET, SO_REUSEADDR, (void *)&one, sizeof(one));
198     }
199 #endif
200     d->type = type;
201 
202     if(bind(d->s, sa, sa_size) < 0){
203 	krb5_warn(context, errno, "bind(%d)", ntohs(port));
204 	close(d->s);
205 	d->s = -1;
206 	return;
207     }
208     if(type == SOCK_STREAM && listen(d->s, SOMAXCONN) < 0){
209 	krb5_warn(context, errno, "listen");
210 	close(d->s);
211 	return;
212     }
213 }
214 
215 /*
216  * Allocate descriptors for all the sockets that we should listen on
217  * and return the number of them.
218  */
219 
220 static int
221 init_sockets(struct descr **desc)
222 {
223     krb5_error_code ret;
224     int i, j;
225     struct descr *d;
226     int num = 0;
227     krb5_addresses addresses;
228 
229     ret = krb5_get_all_server_addrs (context, &addresses);
230     if (ret)
231 	krb5_err (context, 1, ret, "krb5_get_all_server_addrs");
232     parse_ports(port_str);
233     d = malloc(addresses.len * num_ports * sizeof(*d));
234     if (d == NULL)
235 	krb5_errx(context, 1, "malloc(%u) failed", num_ports * sizeof(*d));
236 
237     for (i = 0; i < num_ports; i++){
238 	for (j = 0; j < addresses.len; ++j) {
239 	    init_socket(&d[num], &addresses.val[j],
240 			ports[i].family, ports[i].type, ports[i].port);
241 	    if(d[num].s != -1){
242 		char a_str[80];
243 		size_t len;
244 
245 		krb5_print_address (&addresses.val[j], a_str,
246 				    sizeof(a_str), &len);
247 
248 		kdc_log(5, "listening on %s port %u/%s",
249 			a_str,
250 			ntohs(ports[i].port),
251 			(ports[i].type == SOCK_STREAM) ? "tcp" : "udp");
252 		/* XXX */
253 		num++;
254 	    }
255 	}
256     }
257     krb5_free_addresses (context, &addresses);
258     d = realloc(d, num * sizeof(*d));
259     if (d == NULL && num != 0)
260 	krb5_errx(context, 1, "realloc(%u) failed", num * sizeof(*d));
261     *desc = d;
262     return num;
263 }
264 
265 
266 static int
267 process_request(unsigned char *buf,
268 		size_t len,
269 		krb5_data *reply,
270 		int *sendlength,
271 		const char *from,
272 		struct sockaddr *addr)
273 {
274     KDC_REQ req;
275 #ifdef KRB4
276     Ticket ticket;
277 #endif
278     krb5_error_code ret;
279     size_t i;
280 
281     gettimeofday(&now, NULL);
282     if(decode_AS_REQ(buf, len, &req, &i) == 0){
283 	ret = as_rep(&req, reply, from, addr);
284 	free_AS_REQ(&req);
285 	return ret;
286     }else if(decode_TGS_REQ(buf, len, &req, &i) == 0){
287 	ret = tgs_rep(&req, reply, from, addr);
288 	free_TGS_REQ(&req);
289 	return ret;
290     }
291 #ifdef KRB4
292     else if(maybe_version4(buf, len)){
293 	*sendlength = 0; /* elbitapmoc sdrawkcab XXX */
294 	do_version4(buf, len, reply, from, (struct sockaddr_in*)addr);
295 	return 0;
296     }else if(decode_Ticket(buf, len, &ticket, &i) == 0){
297 	ret = do_524(&ticket, reply, from, addr);
298 	free_Ticket(&ticket);
299 	return ret;
300     }
301 #endif
302 #ifdef KASERVER
303     else if (enable_kaserver) {
304 	ret = do_kaserver (buf, len, reply, from, (struct sockaddr_in*)addr);
305 	return ret;
306     }
307 #endif
308 
309     return -1;
310 }
311 
312 static void
313 addr_to_string(struct sockaddr *addr, size_t addr_len, char *str, size_t len)
314 {
315     krb5_address a;
316     krb5_sockaddr2address(addr, &a);
317     if(krb5_print_address(&a, str, len, &len) == 0) {
318 	krb5_free_address(context, &a);
319 	return;
320     }
321     krb5_free_address(context, &a);
322     snprintf(str, len, "<family=%d>", addr->sa_family);
323 }
324 
325 static void
326 do_request(void *buf, size_t len, int sendlength,
327 	   int socket, struct sockaddr *from, size_t from_len)
328 {
329     krb5_error_code ret;
330     krb5_data reply;
331     char addr[128];
332 
333     addr_to_string(from, from_len, addr, sizeof(addr));
334 
335     reply.length = 0;
336     ret = process_request(buf, len, &reply, &sendlength, addr, from);
337     if(reply.length){
338 	kdc_log(5, "sending %d bytes to %s", reply.length, addr);
339 	if(sendlength){
340 	    unsigned char len[4];
341 	    len[0] = (reply.length >> 24) & 0xff;
342 	    len[1] = (reply.length >> 16) & 0xff;
343 	    len[2] = (reply.length >> 8) & 0xff;
344 	    len[3] = reply.length & 0xff;
345 	    if(sendto(socket, len, sizeof(len), 0, from, from_len) < 0) {
346 		kdc_log (0, "sendto(%s): %s", addr, strerror(errno));
347 		krb5_data_free(&reply);
348 		return;
349 	    }
350 	}
351 	if(sendto(socket, reply.data, reply.length, 0, from, from_len) < 0) {
352 	    kdc_log (0, "sendto(%s): %s", addr, strerror(errno));
353 	    krb5_data_free(&reply);
354 	    return;
355 	}
356 	krb5_data_free(&reply);
357     }
358     if(ret)
359 	kdc_log(0, "Failed processing %lu byte request from %s",
360 		(unsigned long)len, addr);
361 }
362 
363 static void
364 handle_udp(struct descr *d)
365 {
366     unsigned char *buf;
367     struct sockaddr_storage __ss;
368     struct sockaddr *sa = (struct sockaddr *)&__ss;
369     int from_len;
370     int n;
371 
372     buf = malloc(max_request);
373     if(buf == NULL){
374 	kdc_log(0, "Failed to allocate %u bytes", max_request);
375 	return;
376     }
377 
378     from_len = sizeof(__ss);
379     n = recvfrom(d->s, buf, max_request, 0,
380 		 sa, &from_len);
381     if(n < 0){
382 	krb5_warn(context, errno, "recvfrom");
383 	goto out;
384     }
385     if(n == 0) {
386 	goto out;
387     }
388     do_request(buf, n, 0, d->s, sa, from_len);
389 out:
390     free (buf);
391 }
392 
393 static void
394 clear_descr(struct descr *d)
395 {
396     if(d->buf)
397 	memset(d->buf, 0, d->size);
398     d->len = 0;
399     if(d->s != -1)
400 	close(d->s);
401     d->s = -1;
402 }
403 
404 
405 /* remove HTTP %-quoting from buf */
406 static int
407 de_http(char *buf)
408 {
409     char *p, *q;
410     for(p = q = buf; *p; p++, q++) {
411 	if(*p == '%') {
412 	    unsigned int x;
413 	    if(sscanf(p + 1, "%2x", &x) != 1)
414 		return -1;
415 	    *q = x;
416 	    p += 2;
417 	} else
418 	    *q = *p;
419     }
420     *q = '\0';
421     return 0;
422 }
423 
424 #define TCP_TIMEOUT 4
425 
426 /*
427  * accept a new TCP connection on `d[index]'
428  */
429 
430 static void
431 add_new_tcp (struct descr *d, int index, int min_free)
432 {
433     struct sockaddr_storage __ss;
434     struct sockaddr *sa = (struct sockaddr *)&__ss;
435     int s;
436     int from_len;
437 
438     from_len = sizeof(__ss);
439     s = accept(d[index].s, sa, &from_len);
440     if(s < 0){
441 	krb5_warn(context, errno, "accept");
442 	return;
443     }
444     if(min_free == -1){
445 	close(s);
446 	return;
447     }
448 
449     d[min_free].s = s;
450     d[min_free].timeout = time(NULL) + TCP_TIMEOUT;
451     d[min_free].type = SOCK_STREAM;
452 }
453 
454 /*
455  * Grow `d' to handle at least `n'.
456  * Return != 0 if fails
457  */
458 
459 static int
460 grow_descr (struct descr *d, size_t n)
461 {
462     if (d->size - d->len < n) {
463 	unsigned char *tmp;
464 
465 	d->size += max(1024, d->len + n);
466 	if (d->size >= max_request) {
467 	    kdc_log(0, "Request exceeds max request size (%u bytes).",
468 		    d->size);
469 	    clear_descr(d);
470 	    return -1;
471 	}
472 	tmp = realloc (d->buf, d->size);
473 	if (tmp == NULL) {
474 	    kdc_log(0, "Failed to re-allocate %u bytes.", d->size);
475 	    clear_descr(d);
476 	    return -1;
477 	}
478 	d->buf = tmp;
479     }
480     return 0;
481 }
482 
483 /*
484  * Try to handle the TCP data at `d->buf, d->len'.
485  * Return -1 if failed, 0 if succesful, and 1 if data is complete.
486  */
487 
488 static int
489 handle_vanilla_tcp (struct descr *d)
490 {
491     krb5_storage *sp;
492     int32_t len;
493 
494     sp = krb5_storage_from_mem(d->buf, d->len);
495     if (sp == NULL) {
496 	kdc_log (0, "krb5_storage_from_mem failed");
497 	return -1;
498     }
499     krb5_ret_int32(sp, &len);
500     krb5_storage_free(sp);
501     if(d->len - 4 >= len) {
502 	memcpy(d->buf, d->buf + 4, d->len - 4);
503 	return 1;
504     }
505     return 0;
506 }
507 
508 /*
509  * Try to handle the TCP/HTTP data at `d->buf, d->len'.
510  * Return -1 if failed, 0 if succesful, and 1 if data is complete.
511  */
512 
513 static int
514 handle_http_tcp (struct descr *d, const char *addr)
515 {
516     char *s, *p, *t;
517     void *data;
518     char *proto;
519     int len;
520 
521     s = (char *)d->buf;
522 
523     p = strstr(s, "\r\n");
524     if (p == NULL) {
525 	kdc_log(0, "Malformed HTTP request from %s", addr);
526 	return -1;
527     }
528     *p = 0;
529 
530     p = NULL;
531     t = strtok_r(s, " \t", &p);
532     if (t == NULL) {
533 	kdc_log(0, "Malformed HTTP request from %s", addr);
534 	return -1;
535     }
536     t = strtok_r(NULL, " \t", &p);
537     if(t == NULL) {
538 	kdc_log(0, "Malformed HTTP request from %s", addr);
539 	return -1;
540     }
541     data = malloc(strlen(t));
542     if (data == NULL) {
543 	kdc_log(0, "Failed to allocate %u bytes", strlen(t));
544 	return -1;
545     }
546     if(*t == '/')
547 	t++;
548     if(de_http(t) != 0) {
549 	kdc_log(0, "Malformed HTTP request from %s", addr);
550 	kdc_log(5, "Request: %s", t);
551 	free(data);
552 	return -1;
553     }
554     proto = strtok_r(NULL, " \t", &p);
555     if (proto == NULL) {
556 	kdc_log(0, "Malformed HTTP request from %s", addr);
557 	free(data);
558 	return -1;
559     }
560     len = base64_decode(t, data);
561     if(len <= 0){
562 	const char *msg =
563 	    " 404 Not found\r\n"
564 	    "Server: Heimdal/" VERSION "\r\n"
565 	    "Content-type: text/html\r\n"
566 	    "Content-transfer-encoding: 8bit\r\n\r\n"
567 	    "<TITLE>404 Not found</TITLE>\r\n"
568 	    "<H1>404 Not found</H1>\r\n"
569 	    "That page doesn't exist, maybe you are looking for "
570 	    "<A HREF=\"http://www.pdc.kth.se/heimdal\">Heimdal</A>?\r\n";
571 	write(d->s, proto, strlen(proto));
572 	write(d->s, msg, strlen(msg));
573 	kdc_log(0, "HTTP request from %s is non KDC request", addr);
574 	kdc_log(5, "Request: %s", t);
575 	free(data);
576 	return -1;
577     }
578     {
579 	const char *msg =
580 	    " 200 OK\r\n"
581 	    "Server: Heimdal/" VERSION "\r\n"
582 	    "Content-type: application/octet-stream\r\n"
583 	    "Content-transfer-encoding: binary\r\n\r\n";
584 	write(d->s, proto, strlen(proto));
585 	write(d->s, msg, strlen(msg));
586     }
587     memcpy(d->buf, data, len);
588     d->len = len;
589     free(data);
590     return 1;
591 }
592 
593 /*
594  * Handle incoming data to the TCP socket in `d[index]'
595  */
596 
597 static void
598 handle_tcp(struct descr *d, int index, int min_free)
599 {
600     unsigned char buf[1024];
601     char addr[32];
602     struct sockaddr_storage __ss;
603     struct sockaddr *sa = (struct sockaddr *)&__ss;
604     int from_len;
605     int n;
606     int ret = 0;
607 
608     if (d[index].timeout == 0) {
609 	add_new_tcp (d, index, min_free);
610 	return;
611     }
612 
613     /*
614      * We can't trust recvfrom to return an address so we always call
615      * getpeername.
616      */
617 
618     n = recvfrom(d[index].s, buf, sizeof(buf), 0, NULL, NULL);
619     if(n < 0){
620 	krb5_warn(context, errno, "recvfrom");
621 	return;
622     }
623     from_len = sizeof(__ss);
624     if (getpeername(d[index].s, sa, &from_len) < 0) {
625 	krb5_warn(context, errno, "getpeername");
626 	return;
627     }
628     addr_to_string(sa, from_len, addr, sizeof(addr));
629     if (grow_descr (&d[index], n))
630 	return;
631     memcpy(d[index].buf + d[index].len, buf, n);
632     d[index].len += n;
633     if(d[index].len > 4 && d[index].buf[0] == 0) {
634 	ret = handle_vanilla_tcp (&d[index]);
635     } else if(enable_http &&
636 	      d[index].len >= 4 &&
637 	      strncmp((char *)d[index].buf, "GET ", 4) == 0 &&
638 	      strncmp((char *)d[index].buf + d[index].len - 4,
639 		      "\r\n\r\n", 4) == 0) {
640 	ret = handle_http_tcp (&d[index], addr);
641 	if (ret < 0)
642 	    clear_descr (d + index);
643     } else if (d[index].len > 4) {
644 	kdc_log (0, "TCP data of strange type from %s", addr);
645 	return;
646     }
647     if (ret < 0)
648 	return;
649     else if (ret == 1) {
650 	do_request(d[index].buf, d[index].len, 1,
651 		   d[index].s, sa, from_len);
652 	clear_descr(d + index);
653     }
654 }
655 
656 void
657 loop(void)
658 {
659     struct descr *d;
660     int ndescr;
661 
662     ndescr = init_sockets(&d);
663     if(ndescr <= 0)
664 	krb5_errx(context, 1, "No sockets!");
665     while(exit_flag == 0){
666 	struct timeval tmout;
667 	fd_set fds;
668 	int min_free = -1;
669 	int max_fd = 0;
670 	int i;
671 	FD_ZERO(&fds);
672 	for(i = 0; i < ndescr; i++){
673 	    if(d[i].s >= 0){
674 		if(d[i].type == SOCK_STREAM &&
675 		   d[i].timeout && d[i].timeout < time(NULL)){
676 		    struct sockaddr sa;
677 		    int salen = sizeof(sa);
678 		    char addr[32];
679 
680 		    getpeername(d[i].s, &sa, &salen);
681 		    addr_to_string(&sa, salen, addr, sizeof(addr));
682 		    kdc_log(1, "TCP-connection from %s expired after %u bytes",
683 			    addr, d[i].len);
684 		    clear_descr(&d[i]);
685 		    continue;
686 		}
687 		if(max_fd < d[i].s)
688 		    max_fd = d[i].s;
689 		FD_SET(d[i].s, &fds);
690 	    }else if(min_free < 0 || i < min_free)
691 		min_free = i;
692 	}
693 	if(min_free == -1){
694 	    struct descr *tmp;
695 	    tmp = realloc(d, (ndescr + 4) * sizeof(*d));
696 	    if(tmp == NULL)
697 		krb5_warnx(context, "No memory");
698 	    else{
699 		d = tmp;
700 		memset(d + ndescr, 0, 4 * sizeof(*d));
701 		for(i = ndescr; i < ndescr + 4; i++)
702 		    d[i].s = -1;
703 		min_free = ndescr;
704 		ndescr += 4;
705 	    }
706 	}
707 
708 	tmout.tv_sec = TCP_TIMEOUT;
709 	tmout.tv_usec = 0;
710 	switch(select(max_fd + 1, &fds, 0, 0, &tmout)){
711 	case 0:
712 	    break;
713 	case -1:
714 	    krb5_warn(context, errno, "select");
715 	    break;
716 	default:
717 	    for(i = 0; i < ndescr; i++)
718 		if(d[i].s >= 0 && FD_ISSET(d[i].s, &fds)) {
719 		    if(d[i].type == SOCK_DGRAM)
720 			handle_udp(&d[i]);
721 		    else if(d[i].type == SOCK_STREAM)
722 			handle_tcp(d, i, min_free);
723 		}
724 	}
725     }
726     free (d);
727 }
728