1/* 2 * Copyright (c) 1997-2005 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/* Should we enable the HTTP hack? */ 37int enable_http = -1; 38 39/* Log over requests to the KDC */ 40const char *request_log; 41 42/* A string describing on what ports to listen */ 43const char *port_str; 44 45krb5_addresses explicit_addresses; 46 47size_t max_request_udp; 48size_t max_request_tcp; 49 50/* 51 * a tuple describing on what to listen 52 */ 53 54struct port_desc{ 55 int family; 56 int type; 57 int port; 58}; 59 60/* the current ones */ 61 62static struct port_desc *ports; 63static size_t num_ports; 64 65static void 66kdc_service(void *ctx, const heim_idata *req, 67 const heim_icred cred, 68 heim_ipc_complete complete, 69 heim_sipc_call cctx); 70 71 72 73/* 74 * add `family, port, protocol' to the list with duplicate suppresion. 75 */ 76 77static void 78add_port(krb5_context context, 79 int family, int port, const char *protocol) 80{ 81 int type; 82 size_t i; 83 84 if(strcmp(protocol, "udp") == 0) 85 type = SOCK_DGRAM; 86 else if(strcmp(protocol, "tcp") == 0) 87 type = SOCK_STREAM; 88 else 89 return; 90 for(i = 0; i < num_ports; i++){ 91 if(ports[i].type == type 92 && ports[i].port == port 93 && ports[i].family == family) 94 return; 95 } 96 ports = realloc(ports, (num_ports + 1) * sizeof(*ports)); 97 if (ports == NULL) 98 krb5_err (context, 1, errno, "realloc"); 99 ports[num_ports].family = family; 100 ports[num_ports].type = type; 101 ports[num_ports].port = port; 102 num_ports++; 103} 104 105/* 106 * add a triple but with service -> port lookup 107 * (this prints warnings for stuff that does not exist) 108 */ 109 110static void 111add_port_service(krb5_context context, 112 int family, const char *service, int port, 113 const char *protocol) 114{ 115 port = krb5_getportbyname (context, service, protocol, port); 116 add_port (context, family, port, protocol); 117} 118 119/* 120 * add the port with service -> port lookup or string -> number 121 * (no warning is printed) 122 */ 123 124static void 125add_port_string (krb5_context context, 126 int family, const char *str, const char *protocol) 127{ 128 struct servent *sp; 129 int port; 130 131 sp = roken_getservbyname (str, protocol); 132 if (sp != NULL) { 133 port = sp->s_port; 134 } else { 135 char *end; 136 137 port = htons(strtol(str, &end, 0)); 138 if (end == str) 139 return; 140 } 141 add_port (context, family, port, protocol); 142} 143 144/* 145 * add the standard collection of ports for `family' 146 */ 147 148static void 149add_standard_ports (krb5_context context, 150 krb5_kdc_configuration *config, 151 int family) 152{ 153 add_port_service(context, family, "kerberos", 88, "udp"); 154 add_port_service(context, family, "kerberos", 88, "tcp"); 155 add_port_service(context, family, "kerberos-sec", 88, "udp"); 156 add_port_service(context, family, "kerberos-sec", 88, "tcp"); 157 if(enable_http) 158 add_port_service(context, family, "http", 80, "tcp"); 159 if(config->enable_kx509) { 160 add_port_service(context, family, "kca_service", 9878, "udp"); 161 add_port_service(context, family, "kca_service", 9878, "tcp"); 162 } 163 164} 165 166/* 167 * parse the set of space-delimited ports in `str' and add them. 168 * "+" => all the standard ones 169 * otherwise it's port|service[/protocol] 170 */ 171 172static void 173parse_ports(krb5_context context, 174 krb5_kdc_configuration *config, 175 const char *str) 176{ 177 char *pos = NULL; 178 char *p; 179 char *str_copy = strdup (str); 180 181 p = strtok_r(str_copy, " \t", &pos); 182 while(p != NULL) { 183 if(strcmp(p, "+") == 0) { 184#ifdef HAVE_IPV6 185 add_standard_ports(context, config, AF_INET6); 186#endif 187 add_standard_ports(context, config, AF_INET); 188 } else { 189 char *q = strchr(p, '/'); 190 if(q){ 191 *q++ = 0; 192#ifdef HAVE_IPV6 193 add_port_string(context, AF_INET6, p, q); 194#endif 195 add_port_string(context, AF_INET, p, q); 196 }else { 197#ifdef HAVE_IPV6 198 add_port_string(context, AF_INET6, p, "udp"); 199 add_port_string(context, AF_INET6, p, "tcp"); 200#endif 201 add_port_string(context, AF_INET, p, "udp"); 202 add_port_string(context, AF_INET, p, "tcp"); 203 } 204 } 205 206 p = strtok_r(NULL, " \t", &pos); 207 } 208 free (str_copy); 209} 210 211/* 212 * every socket we listen on 213 */ 214 215struct descr { 216 krb5_socket_t s; 217 int type; 218 int port; 219 unsigned char *buf; 220 size_t size; 221 size_t len; 222 time_t timeout; 223 struct sockaddr_storage __ss; 224 struct sockaddr *sa; 225 socklen_t sock_len; 226 char addr_string[128]; 227 heim_sipc u; 228}; 229 230static void 231init_descr(struct descr *d) 232{ 233 memset(d, 0, sizeof(*d)); 234 d->sa = (struct sockaddr *)&d->__ss; 235 d->s = rk_INVALID_SOCKET; 236} 237 238/* 239 * re-initialize all `n' ->sa in `d'. 240 */ 241 242static void 243reinit_descrs (struct descr *d, int n) 244{ 245 int i; 246 247 for (i = 0; i < n; ++i) 248 d[i].sa = (struct sockaddr *)&d[i].__ss; 249} 250 251/* 252 * Create the socket (family, type, port) in `d' 253 */ 254 255static void 256init_socket(krb5_context context, 257 krb5_kdc_configuration *config, 258 struct descr *d, krb5_address *a, int family, int type, int port) 259{ 260 krb5_error_code ret; 261 struct sockaddr_storage __ss; 262 struct sockaddr *sa = (struct sockaddr *)&__ss; 263 krb5_socklen_t sa_size = sizeof(__ss); 264 int http_flag = 0; 265 266 if (enable_http == 1) 267 http_flag = HEIM_SIPC_TYPE_HTTP; 268 269 init_descr (d); 270 271 ret = krb5_addr2sockaddr (context, a, sa, &sa_size, port); 272 if (ret) { 273 krb5_warn(context, ret, "krb5_addr2sockaddr"); 274 rk_closesocket(d->s); 275 d->s = rk_INVALID_SOCKET; 276 return; 277 } 278 279 if (sa->sa_family != family) 280 return; 281 282 d->s = socket(family, type, 0); 283 if(rk_IS_BAD_SOCKET(d->s)){ 284 krb5_warn(context, errno, "socket(%d, %d, 0)", family, type); 285 d->s = rk_INVALID_SOCKET; 286 return; 287 } 288 socket_set_reuseaddr(d->s, 1); 289 socket_set_nopipe(d->s, 1); 290#ifdef HAVE_IPV6 291 if (family == AF_INET6) 292 socket_set_ipv6only(d->s, 1); 293#endif 294 d->type = type; 295 d->port = port; 296 297 if(rk_IS_SOCKET_ERROR(bind(d->s, sa, sa_size))){ 298 char a_str[256]; 299 size_t len; 300 301 krb5_print_address (a, a_str, sizeof(a_str), &len); 302 krb5_warn(context, errno, "bind %s/%d", a_str, ntohs(port)); 303 rk_closesocket(d->s); 304 d->s = rk_INVALID_SOCKET; 305 return; 306 } 307 308 if(type == SOCK_STREAM && listen(d->s, SOMAXCONN) < 0){ 309 char a_str[256]; 310 size_t len; 311 312 krb5_print_address (a, a_str, sizeof(a_str), &len); 313 krb5_warn(context, errno, "listen %s/%d", a_str, ntohs(port)); 314 rk_closesocket(d->s); 315 d->s = rk_INVALID_SOCKET; 316 return; 317 } 318 319 if (type == SOCK_STREAM) { 320 ret = heim_sipc_stream_listener(d->s, 321 HEIM_SIPC_TYPE_UINT32|http_flag|HEIM_SIPC_TYPE_ONE_REQUEST, 322 kdc_service, d, &d->u); 323 if (ret) 324 errx(1, "heim_sipc_stream_listener: %d", ret); 325 } else { 326 ret = heim_sipc_service_dgram(d->s, 0, 327 kdc_service, d, &d->u); 328 if (ret) 329 errx(1, "heim_sipc_service_dgram: %d", ret); 330 } 331} 332 333/* 334 * Allocate descriptors for all the sockets that we should listen on 335 * and return the number of them. 336 */ 337 338static int 339init_sockets(krb5_context context, 340 krb5_kdc_configuration *config, 341 struct descr **desc) 342{ 343 krb5_error_code ret; 344 size_t i, j; 345 struct descr *d; 346 int num = 0; 347 krb5_addresses addresses; 348 349 if (explicit_addresses.len) { 350 addresses = explicit_addresses; 351 } else { 352#if defined(IPV6_PKTINFO) && defined(IP_PKTINFO) 353 ret = krb5_get_all_any_addrs(context, &addresses); 354#else 355 ret = krb5_get_all_server_addrs(context, &addresses); 356#endif 357 if (ret) 358 krb5_err (context, 1, ret, "krb5_get_all_{server,any}_addrs"); 359 } 360 361 parse_ports(context, config, port_str); 362 d = calloc(addresses.len * num_ports, sizeof(*d)); 363 if (d == NULL) 364 krb5_errx(context, 1, "malloc(%lu) failed", 365 (unsigned long)num_ports * sizeof(*d)); 366 367 for (i = 0; i < num_ports; i++){ 368 for (j = 0; j < addresses.len; ++j) { 369 char a_str[80]; 370 size_t len; 371 372 init_socket(context, config, &d[num], &addresses.val[j], 373 ports[i].family, ports[i].type, ports[i].port); 374 krb5_print_address (&addresses.val[j], a_str, 375 sizeof(a_str), &len); 376 377 kdc_log(context, config, 5, "%slistening on %s port %u/%s", 378 d[num].s != rk_INVALID_SOCKET ? "" : "FAILED ", 379 a_str, 380 ntohs(ports[i].port), 381 (ports[i].type == SOCK_STREAM) ? "tcp" : "udp"); 382 383 if(d[num].s != rk_INVALID_SOCKET) 384 num++; 385 } 386 } 387 krb5_free_addresses (context, &addresses); 388 d = realloc(d, num * sizeof(*d)); 389 if (d == NULL && num != 0) 390 krb5_errx(context, 1, "realloc(%lu) failed", 391 (unsigned long)num * sizeof(*d)); 392 reinit_descrs (d, num); 393 *desc = d; 394 return num; 395} 396 397/* 398 * 399 */ 400 401static krb5_context kdc_context; 402static krb5_kdc_configuration *kdc_config; 403 404/* 405 * 406 */ 407 408static void 409kdc_service(void *ctx, const heim_idata *req, 410 const heim_icred cred, 411 heim_ipc_complete complete, 412 heim_sipc_call cctx) 413{ 414 krb5_socklen_t sasize; 415 struct sockaddr *sa; 416 struct descr *d = ctx; 417 int datagram_reply = (d->type == SOCK_DGRAM); 418 krb5_data reply; 419 krb5_error_code ret; 420 char addr[NI_MAXHOST], port[NI_MAXSERV]; 421 422 krb5_kdc_update_time(NULL); 423 krb5_data_zero(&reply); 424 425 sa = heim_ipc_cred_get_client_address(cred, &sasize); 426 427 if (sa == NULL || getnameinfo(sa, sasize, addr, sizeof(addr), port, sizeof(port), NI_NUMERICHOST|NI_NUMERICSERV) != 0) 428 strlcpy(addr, "unknown network address", sizeof(addr)); 429 else { 430 strlcat(addr, ":", sizeof(addr)); 431 strlcat(addr, port, sizeof(addr)); 432 } 433 434 ret = krb5_kdc_process_request(kdc_context, kdc_config, 435 req->data, req->length, 436 &reply, 437 addr, sa, 438 datagram_reply); 439 if(request_log) 440 krb5_kdc_save_request(kdc_context, request_log, 441 req->data, req->length, &reply, d->sa); 442 443 (*complete)(cctx, ret, &reply); 444 krb5_data_free(&reply); 445} 446 447static void 448kdc_local(void *ctx, const heim_idata *req, 449 const heim_icred cred, 450 heim_ipc_complete complete, 451 heim_sipc_call cctx) 452{ 453 krb5_error_code ret; 454 krb5_data reply; 455 456 krb5_kdc_update_time(NULL); 457 krb5_data_zero(&reply); 458 459 ret = krb5_kdc_process_request(kdc_context, kdc_config, 460 req->data, req->length, 461 &reply, 462 "local-ipc", NULL, 0); 463 (*complete)(cctx, ret, &reply); 464 krb5_data_free(&reply); 465} 466 467 468 469static struct descr *kdc_descrs; 470static unsigned int kdc_ndescr; 471 472void 473setup_listeners(krb5_context context, 474 krb5_kdc_configuration *config, 475 int ipc, int network) 476{ 477 kdc_context = context; 478 kdc_config = config; 479 480 if (network) { 481 kdc_ndescr = init_sockets(context, config, &kdc_descrs); 482 if(kdc_ndescr <= 0) 483 krb5_errx(context, 1, "No sockets!"); 484 } 485 486#ifdef __APPLE__ 487 if (ipc) { 488 heim_sipc mach; 489 heim_sipc_launchd_mach_init("org.h5l.kdc", kdc_local, NULL, &mach); 490 } 491#endif 492 kdc_log(context, config, 0, "KDC started"); 493} 494