1 /* $NetBSD: send_to_kdc.c,v 1.1.1.2 2014/04/24 12:45:51 pettai Exp $ */ 2 3 /* 4 * Copyright (c) 1997 - 2002 Kungliga Tekniska Högskolan 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 "krb5_locl.h" 37 #include "send_to_kdc_plugin.h" 38 39 struct send_to_kdc { 40 krb5_send_to_kdc_func func; 41 void *data; 42 }; 43 44 /* 45 * connect to a remote host and in the case of stream sockets, provide 46 * a timeout for the connexion. 47 */ 48 49 static int 50 timed_connect(int s, struct addrinfo *addr, time_t tmout) 51 { 52 #ifdef HAVE_POLL 53 socklen_t sl; 54 int err; 55 int flags; 56 int ret; 57 58 if (addr->ai_socktype != SOCK_STREAM) 59 return connect(s, addr->ai_addr, addr->ai_addrlen); 60 61 flags = fcntl(s, F_GETFL); 62 if (flags == -1) 63 return -1; 64 65 fcntl(s, F_SETFL, flags | O_NONBLOCK); 66 ret = connect(s, addr->ai_addr, addr->ai_addrlen); 67 if (ret == -1 && errno != EINPROGRESS) 68 return -1; 69 70 for (;;) { 71 struct pollfd fds; 72 73 fds.fd = s; 74 fds.events = POLLIN | POLLOUT; 75 fds.revents = 0; 76 77 ret = poll(&fds, 1, tmout * 1000); 78 if (ret != -1 || errno != EINTR) 79 break; 80 } 81 fcntl(s, F_SETFL, flags); 82 83 if (ret != 1) 84 return -1; 85 86 sl = sizeof(err); 87 ret = getsockopt(s, SOL_SOCKET, SO_ERROR, &err, &sl); 88 if (ret == -1) 89 return -1; 90 if (err != 0) 91 return -1; 92 93 return 0; 94 #else 95 return connect(s, addr->ai_addr, addr->ai_addrlen); 96 #endif 97 } 98 99 /* 100 * send the data in `req' on the socket `fd' (which is datagram iff udp) 101 * waiting `tmout' for a reply and returning the reply in `rep'. 102 * iff limit read up to this many bytes 103 * returns 0 and data in `rep' if succesful, otherwise -1 104 */ 105 106 static int 107 recv_loop (krb5_socket_t fd, 108 time_t tmout, 109 int udp, 110 size_t limit, 111 krb5_data *rep) 112 { 113 fd_set fdset; 114 struct timeval timeout; 115 int ret; 116 int nbytes; 117 118 #ifndef NO_LIMIT_FD_SETSIZE 119 if (fd >= FD_SETSIZE) { 120 return -1; 121 } 122 #endif 123 124 krb5_data_zero(rep); 125 do { 126 FD_ZERO(&fdset); 127 FD_SET(fd, &fdset); 128 timeout.tv_sec = tmout; 129 timeout.tv_usec = 0; 130 ret = select (fd + 1, &fdset, NULL, NULL, &timeout); 131 if (ret < 0) { 132 if (errno == EINTR) 133 continue; 134 return -1; 135 } else if (ret == 0) { 136 return 0; 137 } else { 138 void *tmp; 139 140 if (rk_SOCK_IOCTL (fd, FIONREAD, &nbytes) < 0) { 141 krb5_data_free (rep); 142 return -1; 143 } 144 if(nbytes <= 0) 145 return 0; 146 147 if (limit) 148 nbytes = min((size_t)nbytes, limit - rep->length); 149 150 tmp = realloc (rep->data, rep->length + nbytes); 151 if (tmp == NULL) { 152 krb5_data_free (rep); 153 return -1; 154 } 155 rep->data = tmp; 156 ret = recv (fd, (char*)tmp + rep->length, nbytes, 0); 157 if (ret < 0) { 158 krb5_data_free (rep); 159 return -1; 160 } 161 rep->length += ret; 162 } 163 } while(!udp && (limit == 0 || rep->length < limit)); 164 return 0; 165 } 166 167 /* 168 * Send kerberos requests and receive a reply on a udp or any other kind 169 * of a datagram socket. See `recv_loop'. 170 */ 171 172 static int 173 send_and_recv_udp(krb5_socket_t fd, 174 time_t tmout, 175 const krb5_data *req, 176 krb5_data *rep) 177 { 178 if (send (fd, req->data, req->length, 0) < 0) 179 return -1; 180 181 return recv_loop(fd, tmout, 1, 0, rep); 182 } 183 184 /* 185 * `send_and_recv' for a TCP (or any other stream) socket. 186 * Since there are no record limits on a stream socket the protocol here 187 * is to prepend the request with 4 bytes of its length and the reply 188 * is similarly encoded. 189 */ 190 191 static int 192 send_and_recv_tcp(krb5_socket_t fd, 193 time_t tmout, 194 const krb5_data *req, 195 krb5_data *rep) 196 { 197 unsigned char len[4]; 198 unsigned long rep_len; 199 krb5_data len_data; 200 201 _krb5_put_int(len, req->length, 4); 202 if(net_write (fd, len, sizeof(len)) < 0) 203 return -1; 204 if(net_write (fd, req->data, req->length) < 0) 205 return -1; 206 if (recv_loop (fd, tmout, 0, 4, &len_data) < 0) 207 return -1; 208 if (len_data.length != 4) { 209 krb5_data_free (&len_data); 210 return -1; 211 } 212 _krb5_get_int(len_data.data, &rep_len, 4); 213 krb5_data_free (&len_data); 214 if (recv_loop (fd, tmout, 0, rep_len, rep) < 0) 215 return -1; 216 if(rep->length != rep_len) { 217 krb5_data_free (rep); 218 return -1; 219 } 220 return 0; 221 } 222 223 int 224 _krb5_send_and_recv_tcp(krb5_socket_t fd, 225 time_t tmout, 226 const krb5_data *req, 227 krb5_data *rep) 228 { 229 return send_and_recv_tcp(fd, tmout, req, rep); 230 } 231 232 /* 233 * `send_and_recv' tailored for the HTTP protocol. 234 */ 235 236 static int 237 send_and_recv_http(krb5_socket_t fd, 238 time_t tmout, 239 const char *prefix, 240 const krb5_data *req, 241 krb5_data *rep) 242 { 243 char *request = NULL; 244 char *str; 245 int ret; 246 int len = base64_encode(req->data, req->length, &str); 247 248 if(len < 0) 249 return -1; 250 ret = asprintf(&request, "GET %s%s HTTP/1.0\r\n\r\n", prefix, str); 251 free(str); 252 if (ret < 0 || request == NULL) 253 return -1; 254 ret = net_write (fd, request, strlen(request)); 255 free (request); 256 if (ret < 0) 257 return ret; 258 ret = recv_loop(fd, tmout, 0, 0, rep); 259 if(ret) 260 return ret; 261 { 262 unsigned long rep_len; 263 char *s, *p; 264 265 s = realloc(rep->data, rep->length + 1); 266 if (s == NULL) { 267 krb5_data_free (rep); 268 return -1; 269 } 270 s[rep->length] = 0; 271 p = strstr(s, "\r\n\r\n"); 272 if(p == NULL) { 273 krb5_data_zero(rep); 274 free(s); 275 return -1; 276 } 277 p += 4; 278 rep->data = s; 279 rep->length -= p - s; 280 if(rep->length < 4) { /* remove length */ 281 krb5_data_zero(rep); 282 free(s); 283 return -1; 284 } 285 rep->length -= 4; 286 _krb5_get_int(p, &rep_len, 4); 287 if (rep_len != rep->length) { 288 krb5_data_zero(rep); 289 free(s); 290 return -1; 291 } 292 memmove(rep->data, p + 4, rep->length); 293 } 294 return 0; 295 } 296 297 static int 298 init_port(const char *s, int fallback) 299 { 300 if (s) { 301 int tmp; 302 303 sscanf (s, "%d", &tmp); 304 return htons(tmp); 305 } else 306 return fallback; 307 } 308 309 /* 310 * Return 0 if succesful, otherwise 1 311 */ 312 313 static int 314 send_via_proxy (krb5_context context, 315 const krb5_krbhst_info *hi, 316 const krb5_data *send_data, 317 krb5_data *receive) 318 { 319 char *proxy2 = strdup(context->http_proxy); 320 char *proxy = proxy2; 321 char *prefix = NULL; 322 char *colon; 323 struct addrinfo hints; 324 struct addrinfo *ai, *a; 325 int ret; 326 krb5_socket_t s = rk_INVALID_SOCKET; 327 char portstr[NI_MAXSERV]; 328 329 if (proxy == NULL) 330 return ENOMEM; 331 if (strncmp (proxy, "http://", 7) == 0) 332 proxy += 7; 333 334 colon = strchr(proxy, ':'); 335 if(colon != NULL) 336 *colon++ = '\0'; 337 memset (&hints, 0, sizeof(hints)); 338 hints.ai_family = PF_UNSPEC; 339 hints.ai_socktype = SOCK_STREAM; 340 snprintf (portstr, sizeof(portstr), "%d", 341 ntohs(init_port (colon, htons(80)))); 342 ret = getaddrinfo (proxy, portstr, &hints, &ai); 343 free (proxy2); 344 if (ret) 345 return krb5_eai_to_heim_errno(ret, errno); 346 347 for (a = ai; a != NULL; a = a->ai_next) { 348 s = socket (a->ai_family, a->ai_socktype | SOCK_CLOEXEC, a->ai_protocol); 349 if (s < 0) 350 continue; 351 rk_cloexec(s); 352 if (timed_connect (s, a, context->kdc_timeout) < 0) { 353 rk_closesocket (s); 354 continue; 355 } 356 break; 357 } 358 if (a == NULL) { 359 freeaddrinfo (ai); 360 return 1; 361 } 362 freeaddrinfo (ai); 363 364 ret = asprintf(&prefix, "http://%s/", hi->hostname); 365 if(ret < 0 || prefix == NULL) { 366 close(s); 367 return 1; 368 } 369 ret = send_and_recv_http(s, context->kdc_timeout, 370 prefix, send_data, receive); 371 rk_closesocket (s); 372 free(prefix); 373 if(ret == 0 && receive->length != 0) 374 return 0; 375 return 1; 376 } 377 378 static krb5_error_code 379 send_via_plugin(krb5_context context, 380 krb5_krbhst_info *hi, 381 time_t timeout, 382 const krb5_data *send_data, 383 krb5_data *receive) 384 { 385 struct krb5_plugin *list = NULL, *e; 386 krb5_error_code ret; 387 388 ret = _krb5_plugin_find(context, PLUGIN_TYPE_DATA, KRB5_PLUGIN_SEND_TO_KDC, &list); 389 if(ret != 0 || list == NULL) 390 return KRB5_PLUGIN_NO_HANDLE; 391 392 for (e = list; e != NULL; e = _krb5_plugin_get_next(e)) { 393 krb5plugin_send_to_kdc_ftable *service; 394 void *ctx; 395 396 service = _krb5_plugin_get_symbol(e); 397 if (service->minor_version != 0) 398 continue; 399 400 (*service->init)(context, &ctx); 401 ret = (*service->send_to_kdc)(context, ctx, hi, 402 timeout, send_data, receive); 403 (*service->fini)(ctx); 404 if (ret == 0) 405 break; 406 if (ret != KRB5_PLUGIN_NO_HANDLE) { 407 krb5_set_error_message(context, ret, 408 N_("Plugin send_to_kdc failed to " 409 "lookup with error: %d", ""), ret); 410 break; 411 } 412 } 413 _krb5_plugin_free(list); 414 return KRB5_PLUGIN_NO_HANDLE; 415 } 416 417 418 /* 419 * Send the data `send' to one host from `handle` and get back the reply 420 * in `receive'. 421 */ 422 423 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 424 krb5_sendto (krb5_context context, 425 const krb5_data *send_data, 426 krb5_krbhst_handle handle, 427 krb5_data *receive) 428 { 429 krb5_error_code ret; 430 krb5_socket_t fd; 431 size_t i; 432 433 krb5_data_zero(receive); 434 435 for (i = 0; i < context->max_retries; ++i) { 436 krb5_krbhst_info *hi; 437 438 while (krb5_krbhst_next(context, handle, &hi) == 0) { 439 struct addrinfo *ai, *a; 440 441 _krb5_debug(context, 2, 442 "trying to communicate with host %s in realm %s", 443 hi->hostname, _krb5_krbhst_get_realm(handle)); 444 445 if (context->send_to_kdc) { 446 struct send_to_kdc *s = context->send_to_kdc; 447 448 ret = (*s->func)(context, s->data, hi, 449 context->kdc_timeout, send_data, receive); 450 if (ret == 0 && receive->length != 0) 451 goto out; 452 continue; 453 } 454 455 ret = send_via_plugin(context, hi, context->kdc_timeout, 456 send_data, receive); 457 if (ret == 0 && receive->length != 0) 458 goto out; 459 else if (ret != KRB5_PLUGIN_NO_HANDLE) 460 continue; 461 462 if(hi->proto == KRB5_KRBHST_HTTP && context->http_proxy) { 463 if (send_via_proxy (context, hi, send_data, receive) == 0) { 464 ret = 0; 465 goto out; 466 } 467 continue; 468 } 469 470 ret = krb5_krbhst_get_addrinfo(context, hi, &ai); 471 if (ret) 472 continue; 473 474 for (a = ai; a != NULL; a = a->ai_next) { 475 fd = socket (a->ai_family, a->ai_socktype | SOCK_CLOEXEC, a->ai_protocol); 476 if (rk_IS_BAD_SOCKET(fd)) 477 continue; 478 rk_cloexec(fd); 479 if (timed_connect (fd, a, context->kdc_timeout) < 0) { 480 rk_closesocket (fd); 481 continue; 482 } 483 switch (hi->proto) { 484 case KRB5_KRBHST_HTTP : 485 ret = send_and_recv_http(fd, context->kdc_timeout, 486 "", send_data, receive); 487 break; 488 case KRB5_KRBHST_TCP : 489 ret = send_and_recv_tcp (fd, context->kdc_timeout, 490 send_data, receive); 491 break; 492 case KRB5_KRBHST_UDP : 493 ret = send_and_recv_udp (fd, context->kdc_timeout, 494 send_data, receive); 495 break; 496 } 497 rk_closesocket (fd); 498 if(ret == 0 && receive->length != 0) 499 goto out; 500 } 501 } 502 krb5_krbhst_reset(context, handle); 503 } 504 krb5_clear_error_message (context); 505 ret = KRB5_KDC_UNREACH; 506 out: 507 _krb5_debug(context, 2, 508 "result of trying to talk to realm %s = %d", 509 _krb5_krbhst_get_realm(handle), ret); 510 return ret; 511 } 512 513 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 514 krb5_sendto_kdc(krb5_context context, 515 const krb5_data *send_data, 516 const krb5_realm *realm, 517 krb5_data *receive) 518 { 519 return krb5_sendto_kdc_flags(context, send_data, realm, receive, 0); 520 } 521 522 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 523 krb5_sendto_kdc_flags(krb5_context context, 524 const krb5_data *send_data, 525 const krb5_realm *realm, 526 krb5_data *receive, 527 int flags) 528 { 529 krb5_error_code ret; 530 krb5_sendto_ctx ctx; 531 532 ret = krb5_sendto_ctx_alloc(context, &ctx); 533 if (ret) 534 return ret; 535 krb5_sendto_ctx_add_flags(ctx, flags); 536 krb5_sendto_ctx_set_func(ctx, _krb5_kdc_retry, NULL); 537 538 ret = krb5_sendto_context(context, ctx, send_data, *realm, receive); 539 krb5_sendto_ctx_free(context, ctx); 540 return ret; 541 } 542 543 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 544 krb5_set_send_to_kdc_func(krb5_context context, 545 krb5_send_to_kdc_func func, 546 void *data) 547 { 548 free(context->send_to_kdc); 549 if (func == NULL) { 550 context->send_to_kdc = NULL; 551 return 0; 552 } 553 554 context->send_to_kdc = malloc(sizeof(*context->send_to_kdc)); 555 if (context->send_to_kdc == NULL) { 556 krb5_set_error_message(context, ENOMEM, 557 N_("malloc: out of memory", "")); 558 return ENOMEM; 559 } 560 561 context->send_to_kdc->func = func; 562 context->send_to_kdc->data = data; 563 return 0; 564 } 565 566 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 567 _krb5_copy_send_to_kdc_func(krb5_context context, krb5_context to) 568 { 569 if (context->send_to_kdc) 570 return krb5_set_send_to_kdc_func(to, 571 context->send_to_kdc->func, 572 context->send_to_kdc->data); 573 else 574 return krb5_set_send_to_kdc_func(to, NULL, NULL); 575 } 576 577 578 579 struct krb5_sendto_ctx_data { 580 int flags; 581 int type; 582 krb5_sendto_ctx_func func; 583 void *data; 584 }; 585 586 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 587 krb5_sendto_ctx_alloc(krb5_context context, krb5_sendto_ctx *ctx) 588 { 589 *ctx = calloc(1, sizeof(**ctx)); 590 if (*ctx == NULL) { 591 krb5_set_error_message(context, ENOMEM, 592 N_("malloc: out of memory", "")); 593 return ENOMEM; 594 } 595 return 0; 596 } 597 598 KRB5_LIB_FUNCTION void KRB5_LIB_CALL 599 krb5_sendto_ctx_add_flags(krb5_sendto_ctx ctx, int flags) 600 { 601 ctx->flags |= flags; 602 } 603 604 KRB5_LIB_FUNCTION int KRB5_LIB_CALL 605 krb5_sendto_ctx_get_flags(krb5_sendto_ctx ctx) 606 { 607 return ctx->flags; 608 } 609 610 KRB5_LIB_FUNCTION void KRB5_LIB_CALL 611 krb5_sendto_ctx_set_type(krb5_sendto_ctx ctx, int type) 612 { 613 ctx->type = type; 614 } 615 616 617 KRB5_LIB_FUNCTION void KRB5_LIB_CALL 618 krb5_sendto_ctx_set_func(krb5_sendto_ctx ctx, 619 krb5_sendto_ctx_func func, 620 void *data) 621 { 622 ctx->func = func; 623 ctx->data = data; 624 } 625 626 KRB5_LIB_FUNCTION void KRB5_LIB_CALL 627 krb5_sendto_ctx_free(krb5_context context, krb5_sendto_ctx ctx) 628 { 629 memset(ctx, 0, sizeof(*ctx)); 630 free(ctx); 631 } 632 633 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 634 krb5_sendto_context(krb5_context context, 635 krb5_sendto_ctx ctx, 636 const krb5_data *send_data, 637 const krb5_realm realm, 638 krb5_data *receive) 639 { 640 krb5_error_code ret; 641 krb5_krbhst_handle handle = NULL; 642 int type, freectx = 0; 643 int action; 644 645 krb5_data_zero(receive); 646 647 if (ctx == NULL) { 648 freectx = 1; 649 ret = krb5_sendto_ctx_alloc(context, &ctx); 650 if (ret) 651 return ret; 652 } 653 654 type = ctx->type; 655 if (type == 0) { 656 if ((ctx->flags & KRB5_KRBHST_FLAGS_MASTER) || context->use_admin_kdc) 657 type = KRB5_KRBHST_ADMIN; 658 else 659 type = KRB5_KRBHST_KDC; 660 } 661 662 if ((int)send_data->length > context->large_msg_size) 663 ctx->flags |= KRB5_KRBHST_FLAGS_LARGE_MSG; 664 665 /* loop until we get back a appropriate response */ 666 667 do { 668 action = KRB5_SENDTO_DONE; 669 670 krb5_data_free(receive); 671 672 if (handle == NULL) { 673 ret = krb5_krbhst_init_flags(context, realm, type, 674 ctx->flags, &handle); 675 if (ret) { 676 if (freectx) 677 krb5_sendto_ctx_free(context, ctx); 678 return ret; 679 } 680 } 681 682 ret = krb5_sendto(context, send_data, handle, receive); 683 if (ret) 684 break; 685 if (ctx->func) { 686 ret = (*ctx->func)(context, ctx, ctx->data, receive, &action); 687 if (ret) 688 break; 689 } 690 if (action != KRB5_SENDTO_CONTINUE) { 691 krb5_krbhst_free(context, handle); 692 handle = NULL; 693 } 694 } while (action != KRB5_SENDTO_DONE); 695 if (handle) 696 krb5_krbhst_free(context, handle); 697 if (ret == KRB5_KDC_UNREACH) 698 krb5_set_error_message(context, ret, 699 N_("unable to reach any KDC in realm %s", ""), 700 realm); 701 if (ret) 702 krb5_data_free(receive); 703 if (freectx) 704 krb5_sendto_ctx_free(context, ctx); 705 return ret; 706 } 707 708 krb5_error_code KRB5_CALLCONV 709 _krb5_kdc_retry(krb5_context context, krb5_sendto_ctx ctx, void *data, 710 const krb5_data *reply, int *action) 711 { 712 krb5_error_code ret; 713 KRB_ERROR error; 714 715 if(krb5_rd_error(context, reply, &error)) 716 return 0; 717 718 ret = krb5_error_from_rd_error(context, &error, NULL); 719 krb5_free_error_contents(context, &error); 720 721 switch(ret) { 722 case KRB5KRB_ERR_RESPONSE_TOO_BIG: { 723 if (krb5_sendto_ctx_get_flags(ctx) & KRB5_KRBHST_FLAGS_LARGE_MSG) 724 break; 725 krb5_sendto_ctx_add_flags(ctx, KRB5_KRBHST_FLAGS_LARGE_MSG); 726 *action = KRB5_SENDTO_RESTART; 727 break; 728 } 729 case KRB5KDC_ERR_SVC_UNAVAILABLE: 730 *action = KRB5_SENDTO_CONTINUE; 731 break; 732 } 733 return 0; 734 } 735