concat
[heimdal.git] / kdc / connect.c
blob533c59800b67093cc0afa4eed0999c5d009f4a19
1 /*
2 * Copyright (c) 1997 Kungliga Tekniska Högskolan
3 * (Royal Institute of Technology, Stockholm, Sweden).
4 * All rights reserved.
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
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.
17 * 3. All advertising materials mentioning features or use of this software
18 * must display the following acknowledgement:
19 * This product includes software developed by Kungliga Tekniska
20 * Högskolan and its contributors.
22 * 4. Neither the name of the Institute nor the names of its contributors
23 * may be used to endorse or promote products derived from this software
24 * without specific prior written permission.
26 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
27 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
28 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
29 * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
30 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
31 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
32 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
33 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
34 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
35 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
36 * SUCH DAMAGE.
39 #include "kdc_locl.h"
41 RCSID("$Id$");
43 struct port_desc{
44 int family;
45 int type;
46 int port;
49 static struct port_desc *ports;
50 static int num_ports;
52 static void
53 add_port(int family, const char *port_str, const char *protocol)
55 struct servent *sp;
56 int type;
57 int port;
58 int i;
60 sp = roken_getservbyname(port_str, protocol);
61 if(sp){
62 port = sp->s_port;
63 }else{
64 char *end;
65 port = htons(strtol(port_str, &end, 0));
66 if(end == port_str)
67 return;
69 if(strcmp(protocol, "udp") == 0)
70 type = SOCK_DGRAM;
71 else if(strcmp(protocol, "tcp") == 0)
72 type = SOCK_STREAM;
73 else
74 return;
75 for(i = 0; i < num_ports; i++){
76 if(ports[i].type == type
77 && ports[i].port == port
78 && ports[i].family == family)
79 return;
81 ports = realloc(ports, (num_ports + 1) * sizeof(*ports));
82 ports[num_ports].family = family;
83 ports[num_ports].type = type;
84 ports[num_ports].port = port;
85 num_ports++;
88 static void
89 add_standard_ports (int family)
91 add_port(family, "kerberos", "udp");
92 add_port(family, "kerberos", "tcp");
93 add_port(family, "kerberos-sec", "udp");
94 add_port(family, "kerberos-sec", "tcp");
95 add_port(family, "kerberos-iv", "udp");
96 add_port(family, "kerberos-iv", "tcp");
97 if(enable_http)
98 add_port(family, "http", "tcp");
99 #ifdef KASERVER
100 add_port(family, "7004", "udp");
101 #endif
104 static void
105 parse_ports(char *str)
107 char *pos = NULL;
108 char *p;
109 p = strtok_r(str, " \t", &pos);
110 while(p){
111 if(strcmp(p, "+") == 0) {
112 #if defined(AF_INET6) && defined(HAVE_STRUCT_SOCKADDR_IN6)
113 add_standard_ports(AF_INET6);
114 #else
115 add_standard_ports(AF_INET);
116 #endif
117 } else {
118 char *q = strchr(p, '/');
119 if(q){
120 *q++ = 0;
121 #if defined(AF_INET6) && defined(HAVE_STRUCT_SOCKADDR_IN6)
122 add_port(AF_INET6, p, q);
123 #else
124 add_port(AF_INET, p, q);
125 #endif
126 }else {
127 #if defined(AF_INET6) && defined(HAVE_STRUCT_SOCKADDR_IN6)
128 add_port(AF_INET6, p, "udp");
129 add_port(AF_INET6, p, "tcp");
130 #else
131 add_port(AF_INET, p, "udp");
132 add_port(AF_INET, p, "tcp");
133 #endif
137 p = strtok_r(NULL, " \t", &pos);
141 struct descr {
142 int s;
143 int type;
144 unsigned char *buf;
145 size_t size;
146 size_t len;
147 time_t timeout;
150 static void
151 init_socket(struct descr *d, int family, int type, int port)
153 krb5_error_code ret;
154 struct sockaddr *sa;
155 void *sa_buf;
156 int sa_size;
158 sa_size = krb5_max_sockaddr_size ();
159 sa_buf = malloc(sa_size);
160 if (sa_buf == NULL) {
161 kdc_log(0, "Failed to allocate %u bytes", sa_size);
162 return;
164 sa = (struct sockaddr *)sa_buf;
166 memset(d, 0, sizeof(*d));
167 d->s = socket(family, type, 0);
168 if(d->s < 0){
169 krb5_warn(context, errno, "socket(%d, %d, 0)", family, type);
170 d->s = -1;
171 goto out;
173 #if defined(HAVE_SETSOCKOPT) && defined(SOL_SOCKET) && defined(SO_REUSEADDR)
175 int one = 1;
176 setsockopt(d->s, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));
178 #endif
179 d->type = type;
180 ret = krb5_anyaddr (family, sa, &sa_size, port);
181 if (ret) {
182 krb5_warn(context, ret, "krb5_anyaddr");
183 close(d->s);
184 d->s = -1;
185 goto out;
188 if(bind(d->s, sa, sa_size) < 0){
189 krb5_warn(context, errno, "bind(%d)", ntohs(port));
190 close(d->s);
191 d->s = -1;
192 goto out;
194 if(type == SOCK_STREAM && listen(d->s, SOMAXCONN) < 0){
195 krb5_warn(context, errno, "listen");
196 close(d->s);
197 d->s = -1;
199 out:
200 free (sa_buf);
203 static int
204 init_sockets(struct descr **desc)
206 int i;
207 struct descr *d;
208 int num = 0;
210 parse_ports(port_str);
211 d = malloc(num_ports * sizeof(*d));
212 if (d == NULL)
213 krb5_errx(context, 1, "malloc(%u) failed", num_ports * sizeof(*d));
215 for (i = 0; i < num_ports; i++){
216 init_socket(&d[num], ports[i].family, ports[i].type, ports[i].port);
217 if(d[num].s != -1){
218 kdc_log(5, "listening to port %u/%s", ntohs(ports[i].port),
219 (ports[i].type == SOCK_STREAM) ? "tcp" : "udp"); /* XXX */
220 num++;
223 d = realloc(d, num * sizeof(*d));
224 if (d == NULL)
225 krb5_errx(context, 1, "realloc(%u) failed", num * sizeof(*d));
226 *desc = d;
227 return num;
231 static int
232 process_request(unsigned char *buf,
233 size_t len,
234 krb5_data *reply,
235 int *sendlength,
236 const char *from,
237 struct sockaddr *addr)
239 KDC_REQ req;
240 #ifdef KRB4
241 Ticket ticket;
242 #endif
243 krb5_error_code ret;
244 size_t i;
246 gettimeofday(&now, NULL);
247 if(decode_AS_REQ(buf, len, &req, &i) == 0){
248 ret = as_rep(&req, reply, from);
249 free_AS_REQ(&req);
250 return ret;
251 }else if(decode_TGS_REQ(buf, len, &req, &i) == 0){
252 ret = tgs_rep(&req, reply, from);
253 free_TGS_REQ(&req);
254 return ret;
256 #ifdef KRB4
257 else if(maybe_version4(buf, len)){
258 *sendlength = 0; /* elbitapmoc sdrawkcab XXX */
259 do_version4(buf, len, reply, from, (struct sockaddr_in*)addr);
260 }else if(decode_Ticket(buf, len, &ticket, &i) == 0){
261 ret = do_524(&ticket, reply, from);
262 free_Ticket(&ticket);
263 return ret;
265 #endif
266 #ifdef KASERVER
267 else {
268 ret = do_kaserver (buf, len, reply, from, (struct sockaddr_in*)addr);
269 return ret;
271 #endif
273 return -1;
276 static void
277 addr_to_string(struct sockaddr *addr, size_t addr_len, char *str, size_t len)
279 switch(addr->sa_family){
280 case AF_INET:
281 strncpy(str, inet_ntoa(((struct sockaddr_in*)addr)->sin_addr), len);
282 break;
283 #if defined(AF_INET6) && defined(HAVE_STRUCT_SOCKADDR_IN6) && defined(HAVE_INET_NTOP)
284 case AF_INET6 :
285 inet_ntop(AF_INET6, &((struct sockaddr_in6*)addr)->sin6_addr,
286 str, len);
287 break;
288 #endif
289 default:
290 snprintf(str, len, "<%d addr>", addr->sa_family);
292 str[len - 1] = 0;
295 static void
296 do_request(void *buf, size_t len, int sendlength,
297 int socket, struct sockaddr *from, size_t from_len)
299 krb5_error_code ret;
300 krb5_data reply;
302 char addr[128];
303 addr_to_string(from, from_len, addr, sizeof(addr));
305 reply.length = 0;
306 ret = process_request(buf, len, &reply, &sendlength, addr, from);
307 if(reply.length){
308 kdc_log(5, "sending %d bytes to %s", reply.length, addr);
309 if(sendlength){
310 unsigned char len[4];
311 len[0] = (reply.length >> 24) & 0xff;
312 len[1] = (reply.length >> 16) & 0xff;
313 len[2] = (reply.length >> 8) & 0xff;
314 len[3] = reply.length & 0xff;
315 sendto(socket, len, sizeof(len), 0, from, from_len);
317 sendto(socket, reply.data, reply.length, 0, from, from_len);
318 krb5_data_free(&reply);
320 if(ret)
321 kdc_log(0, "Failed processing %lu byte request from %s",
322 (unsigned long)len, addr);
325 static void
326 handle_udp(struct descr *d)
328 unsigned char *buf;
329 struct sockaddr *sa;
330 void *sa_buf;
331 int sa_size;
332 int from_len;
333 size_t n;
335 sa_size = krb5_max_sockaddr_size ();
336 sa_buf = malloc(sa_size);
337 if (sa_buf == NULL) {
338 kdc_log(0, "Failed to allocate %u bytes", sa_size);
339 return;
341 sa = (struct sockaddr *)sa_buf;
343 buf = malloc(max_request);
344 if(buf == NULL){
345 kdc_log(0, "Failed to allocate %u bytes", max_request);
346 free (sa_buf);
347 return;
350 from_len = sa_size;
351 n = recvfrom(d->s, buf, max_request, 0,
352 sa, &from_len);
353 if(n < 0){
354 krb5_warn(context, errno, "recvfrom");
355 goto out;
357 if(n == 0){
358 goto out;
360 do_request(buf, n, 0, d->s, sa, from_len);
361 out:
362 free (buf);
363 free (sa_buf);
366 static void
367 clear_descr(struct descr *d)
369 if(d->buf)
370 memset(d->buf, 0, d->size);
371 d->len = 0;
372 if(d->s != -1)
373 close(d->s);
374 d->s = -1;
377 #define TCP_TIMEOUT 4
379 static void
380 handle_tcp(struct descr *d, int index, int min_free)
382 unsigned char buf[1024];
383 char addr[32];
384 void *sa_buf;
385 struct sockaddr *sa;
386 int sa_size;
387 int from_len;
388 size_t n;
390 sa_size = krb5_max_sockaddr_size ();
391 sa_buf = malloc(sa_size);
392 if (sa_buf == NULL) {
393 kdc_log(0, "Failed to allocate %u bytes", sa_size);
394 return;
396 sa = (struct sockaddr *)sa_buf;
398 if(d[index].timeout == 0){
399 int s;
401 from_len = sa_size;
402 s = accept(d[index].s, sa, &from_len);
403 if(s < 0){
404 krb5_warn(context, errno, "accept");
405 goto out;
407 if(min_free == -1){
408 close(s);
409 goto out;
412 d[min_free].s = s;
413 d[min_free].timeout = time(NULL) + TCP_TIMEOUT;
414 d[min_free].type = SOCK_STREAM;
415 goto out;
417 from_len = sa_size;
418 n = recvfrom(d[index].s, buf, sizeof(buf), 0,
419 sa, &from_len);
420 if(n < 0){
421 krb5_warn(context, errno, "recvfrom");
422 goto out;
424 /* sometimes recvfrom doesn't return an address */
425 if(from_len == 0){
426 from_len = sa_size;
427 getpeername(d[index].s, sa, &from_len);
429 addr_to_string(sa, from_len, addr, sizeof(addr));
430 if(d[index].size - d[index].len < n){
431 unsigned char *tmp;
432 d[index].size += 1024;
433 if(d[index].size >= max_request){
434 kdc_log(0, "Request exceeds max request size (%u bytes).",
435 d[index].size);
436 clear_descr(d + index);
437 goto out;
439 tmp = realloc(d[index].buf, d[index].size);
440 if(tmp == NULL){
441 kdc_log(0, "Failed to re-allocate %u bytes.", d[index].size);
442 clear_descr(d + index);
443 goto out;
445 d[index].buf = tmp;
447 memcpy(d[index].buf + d[index].len, buf, n);
448 d[index].len += n;
449 if(d[index].len > 4 && d[index].buf[0] == 0){
450 krb5_storage *sp;
451 int32_t len;
452 sp = krb5_storage_from_mem(d[index].buf, d[index].len);
453 krb5_ret_int32(sp, &len);
454 krb5_storage_free(sp);
455 if(d[index].len - 4 >= len){
456 memcpy(d[index].buf, d[index].buf + 4, d[index].len - 4);
457 n = 0;
460 else if(enable_http &&
461 strncmp((char *)d[index].buf, "GET ", 4) == 0 &&
462 strncmp((char *)d[index].buf + d[index].len - 4,
463 "\r\n\r\n", 4) == 0){
464 char *s, *p, *t;
465 void *data;
466 int len;
467 s = (char *)d[index].buf;
468 p = strstr(s, "\r\n");
469 *p = 0;
470 p = NULL;
471 strtok_r(s, " \t", &p);
472 t = strtok_r(NULL, " \t", &p);
473 if(t == NULL){
474 kdc_log(0, "Malformed HTTP request from %s", addr);
475 clear_descr(d + index);
476 goto out;
478 data = malloc(strlen(t));
479 if (data == NULL) {
480 kdc_log(0, "Failed to allocate %u bytes", strlen(t));
481 goto out;
483 if(*t == '/')
484 t++;
485 len = base64_decode(t, data);
486 if(len <= 0){
487 const char *msg =
488 "HTTP/1.1 404 Not found\r\n"
489 "Server: Heimdal/" VERSION "\r\n"
490 "Content-type: text/html\r\n"
491 "Content-transfer-encoding: 8bit\r\n\r\n"
492 "<TITLE>404 Not found</TITLE>\r\n"
493 "<H1>404 Not found</H1>\r\n"
494 "That page doesn't exist, maybe you are looking for "
495 "<A HREF=\"http://www.pdc.kth.se/heimdal\">Heimdal</A>?\r\n";
496 write(d[index].s, msg, strlen(msg));
497 free(data);
498 clear_descr(d + index);
499 kdc_log(0, "HTTP request from %s is non KDC request", addr);
500 goto out;
503 const char *msg =
504 "HTTP/1.1 200 OK\r\n"
505 "Server: Heimdal/" VERSION "\r\n"
506 "Content-type: application/octet-stream\r\n"
507 "Content-transfer-encoding: binary\r\n\r\n";
508 write(d[index].s, msg, strlen(msg));
510 memcpy(d[index].buf, data, len);
511 d[index].len = len;
512 n = 0;
513 free(data);
515 if(n == 0){
516 do_request(d[index].buf, d[index].len, 1,
517 d[index].s, sa, from_len);
518 clear_descr(d + index);
520 out:
521 free (sa_buf);
522 return;
525 void
526 loop(void)
528 struct descr *d;
529 int ndescr;
530 ndescr = init_sockets(&d);
531 if(ndescr <= 0)
532 krb5_errx(context, 1, "No sockets!");
533 while(exit_flag == 0){
534 struct timeval tmout;
535 fd_set fds;
536 int min_free = -1;
537 int max_fd = 0;
538 int i;
539 FD_ZERO(&fds);
540 for(i = 0; i < ndescr; i++){
541 if(d[i].s >= 0){
542 if(d[i].type == SOCK_STREAM &&
543 d[i].timeout && d[i].timeout < time(NULL)){
544 struct sockaddr sa;
545 int salen = sizeof(sa);
546 char addr[32];
548 getpeername(d[i].s, &sa, &salen);
549 addr_to_string(&sa, salen, addr, sizeof(addr));
550 kdc_log(1, "TCP-connection from %s expired after %u bytes",
551 addr, d[i].len);
552 clear_descr(&d[i]);
553 continue;
555 if(max_fd < d[i].s)
556 max_fd = d[i].s;
557 FD_SET(d[i].s, &fds);
558 }else if(min_free < 0 || i < min_free)
559 min_free = i;
561 if(min_free == -1){
562 struct descr *tmp;
563 tmp = realloc(d, (ndescr + 4) * sizeof(*d));
564 if(tmp == NULL)
565 krb5_warnx(context, "No memory");
566 else{
567 d = tmp;
568 memset(d + ndescr, 0, 4 * sizeof(*d));
569 for(i = ndescr; i < ndescr + 4; i++)
570 d[i].s = -1;
571 min_free = ndescr;
572 ndescr += 4;
576 tmout.tv_sec = TCP_TIMEOUT;
577 tmout.tv_usec = 0;
578 switch(select(max_fd + 1, &fds, 0, 0, &tmout)){
579 case 0:
580 break;
581 case -1:
582 krb5_warn(context, errno, "select");
583 break;
584 default:
585 for(i = 0; i < ndescr; i++)
586 if(d[i].s >= 0 && FD_ISSET(d[i].s, &fds))
587 if(d[i].type == SOCK_DGRAM)
588 handle_udp(&d[i]);
589 else if(d[i].type == SOCK_STREAM)
590 handle_tcp(d, i, min_free);
593 free (d);