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