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