155682Smarkm/* 2233294Sstas * Copyright (c) 1997 - 2002 Kungliga Tekniska H��gskolan 3233294Sstas * (Royal Institute of Technology, Stockholm, Sweden). 4233294Sstas * All rights reserved. 555682Smarkm * 6233294Sstas * Redistribution and use in source and binary forms, with or without 7233294Sstas * modification, are permitted provided that the following conditions 8233294Sstas * are met: 955682Smarkm * 10233294Sstas * 1. Redistributions of source code must retain the above copyright 11233294Sstas * notice, this list of conditions and the following disclaimer. 1255682Smarkm * 13233294Sstas * 2. Redistributions in binary form must reproduce the above copyright 14233294Sstas * notice, this list of conditions and the following disclaimer in the 15233294Sstas * documentation and/or other materials provided with the distribution. 1655682Smarkm * 17233294Sstas * 3. Neither the name of the Institute nor the names of its contributors 18233294Sstas * may be used to endorse or promote products derived from this software 19233294Sstas * without specific prior written permission. 2055682Smarkm * 21233294Sstas * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND 22233294Sstas * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23233294Sstas * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24233294Sstas * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE 25233294Sstas * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26233294Sstas * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27233294Sstas * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28233294Sstas * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29233294Sstas * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30233294Sstas * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31233294Sstas * SUCH DAMAGE. 3255682Smarkm */ 3355682Smarkm 3455682Smarkm#include "krb5_locl.h" 35233294Sstas#include "send_to_kdc_plugin.h" 3655682Smarkm 37178825Sdfrstruct send_to_kdc { 38178825Sdfr krb5_send_to_kdc_func func; 39178825Sdfr void *data; 40178825Sdfr}; 41178825Sdfr 4255682Smarkm/* 4355682Smarkm * send the data in `req' on the socket `fd' (which is datagram iff udp) 4455682Smarkm * waiting `tmout' for a reply and returning the reply in `rep'. 4555682Smarkm * iff limit read up to this many bytes 4655682Smarkm * returns 0 and data in `rep' if succesful, otherwise -1 4755682Smarkm */ 4855682Smarkm 4955682Smarkmstatic int 50233294Sstasrecv_loop (krb5_socket_t fd, 5155682Smarkm time_t tmout, 5255682Smarkm int udp, 5355682Smarkm size_t limit, 5455682Smarkm krb5_data *rep) 5555682Smarkm{ 5655682Smarkm fd_set fdset; 5755682Smarkm struct timeval timeout; 5855682Smarkm int ret; 5955682Smarkm int nbytes; 6055682Smarkm 61233294Sstas#ifndef NO_LIMIT_FD_SETSIZE 6272445Sassar if (fd >= FD_SETSIZE) { 6372445Sassar return -1; 6472445Sassar } 65233294Sstas#endif 6672445Sassar 6755682Smarkm krb5_data_zero(rep); 6855682Smarkm do { 6955682Smarkm FD_ZERO(&fdset); 7055682Smarkm FD_SET(fd, &fdset); 7155682Smarkm timeout.tv_sec = tmout; 7255682Smarkm timeout.tv_usec = 0; 7355682Smarkm ret = select (fd + 1, &fdset, NULL, NULL, &timeout); 7455682Smarkm if (ret < 0) { 7555682Smarkm if (errno == EINTR) 7655682Smarkm continue; 7755682Smarkm return -1; 7855682Smarkm } else if (ret == 0) { 7955682Smarkm return 0; 8055682Smarkm } else { 8155682Smarkm void *tmp; 8255682Smarkm 83233294Sstas if (rk_SOCK_IOCTL (fd, FIONREAD, &nbytes) < 0) { 8455682Smarkm krb5_data_free (rep); 8555682Smarkm return -1; 8655682Smarkm } 87178825Sdfr if(nbytes <= 0) 8855682Smarkm return 0; 8955682Smarkm 9055682Smarkm if (limit) 91233294Sstas nbytes = min((size_t)nbytes, limit - rep->length); 9255682Smarkm 9355682Smarkm tmp = realloc (rep->data, rep->length + nbytes); 9455682Smarkm if (tmp == NULL) { 9555682Smarkm krb5_data_free (rep); 9655682Smarkm return -1; 9755682Smarkm } 9855682Smarkm rep->data = tmp; 9955682Smarkm ret = recv (fd, (char*)tmp + rep->length, nbytes, 0); 10055682Smarkm if (ret < 0) { 10155682Smarkm krb5_data_free (rep); 10255682Smarkm return -1; 10355682Smarkm } 10455682Smarkm rep->length += ret; 10555682Smarkm } 10655682Smarkm } while(!udp && (limit == 0 || rep->length < limit)); 10755682Smarkm return 0; 10855682Smarkm} 10955682Smarkm 11055682Smarkm/* 11155682Smarkm * Send kerberos requests and receive a reply on a udp or any other kind 11255682Smarkm * of a datagram socket. See `recv_loop'. 11355682Smarkm */ 11455682Smarkm 11555682Smarkmstatic int 116233294Sstassend_and_recv_udp(krb5_socket_t fd, 11755682Smarkm time_t tmout, 11855682Smarkm const krb5_data *req, 11955682Smarkm krb5_data *rep) 12055682Smarkm{ 12155682Smarkm if (send (fd, req->data, req->length, 0) < 0) 12255682Smarkm return -1; 12355682Smarkm 12455682Smarkm return recv_loop(fd, tmout, 1, 0, rep); 12555682Smarkm} 12655682Smarkm 12755682Smarkm/* 12855682Smarkm * `send_and_recv' for a TCP (or any other stream) socket. 12955682Smarkm * Since there are no record limits on a stream socket the protocol here 13055682Smarkm * is to prepend the request with 4 bytes of its length and the reply 13155682Smarkm * is similarly encoded. 13255682Smarkm */ 13355682Smarkm 13455682Smarkmstatic int 135233294Sstassend_and_recv_tcp(krb5_socket_t fd, 13655682Smarkm time_t tmout, 13755682Smarkm const krb5_data *req, 13855682Smarkm krb5_data *rep) 13955682Smarkm{ 14055682Smarkm unsigned char len[4]; 14155682Smarkm unsigned long rep_len; 14255682Smarkm krb5_data len_data; 14355682Smarkm 14455682Smarkm _krb5_put_int(len, req->length, 4); 145233294Sstas if(net_write (fd, len, sizeof(len)) < 0) 14655682Smarkm return -1; 147233294Sstas if(net_write (fd, req->data, req->length) < 0) 14855682Smarkm return -1; 14955682Smarkm if (recv_loop (fd, tmout, 0, 4, &len_data) < 0) 15055682Smarkm return -1; 15155682Smarkm if (len_data.length != 4) { 15255682Smarkm krb5_data_free (&len_data); 15355682Smarkm return -1; 15455682Smarkm } 15555682Smarkm _krb5_get_int(len_data.data, &rep_len, 4); 15655682Smarkm krb5_data_free (&len_data); 15755682Smarkm if (recv_loop (fd, tmout, 0, rep_len, rep) < 0) 15855682Smarkm return -1; 15955682Smarkm if(rep->length != rep_len) { 16055682Smarkm krb5_data_free (rep); 16155682Smarkm return -1; 16255682Smarkm } 16355682Smarkm return 0; 16455682Smarkm} 16555682Smarkm 166178825Sdfrint 167233294Sstas_krb5_send_and_recv_tcp(krb5_socket_t fd, 168178825Sdfr time_t tmout, 169178825Sdfr const krb5_data *req, 170178825Sdfr krb5_data *rep) 171178825Sdfr{ 172178825Sdfr return send_and_recv_tcp(fd, tmout, req, rep); 173178825Sdfr} 174178825Sdfr 17555682Smarkm/* 17655682Smarkm * `send_and_recv' tailored for the HTTP protocol. 17755682Smarkm */ 17855682Smarkm 17955682Smarkmstatic int 180233294Sstassend_and_recv_http(krb5_socket_t fd, 18155682Smarkm time_t tmout, 18255682Smarkm const char *prefix, 18355682Smarkm const krb5_data *req, 18455682Smarkm krb5_data *rep) 18555682Smarkm{ 186233294Sstas char *request = NULL; 18755682Smarkm char *str; 18855682Smarkm int ret; 18955682Smarkm int len = base64_encode(req->data, req->length, &str); 19055682Smarkm 19155682Smarkm if(len < 0) 19255682Smarkm return -1; 193233294Sstas ret = asprintf(&request, "GET %s%s HTTP/1.0\r\n\r\n", prefix, str); 19455682Smarkm free(str); 195233294Sstas if (ret < 0 || request == NULL) 19655682Smarkm return -1; 19755682Smarkm ret = net_write (fd, request, strlen(request)); 19855682Smarkm free (request); 19955682Smarkm if (ret < 0) 20055682Smarkm return ret; 20155682Smarkm ret = recv_loop(fd, tmout, 0, 0, rep); 20255682Smarkm if(ret) 20355682Smarkm return ret; 20455682Smarkm { 20555682Smarkm unsigned long rep_len; 20655682Smarkm char *s, *p; 20755682Smarkm 20855682Smarkm s = realloc(rep->data, rep->length + 1); 20955682Smarkm if (s == NULL) { 21055682Smarkm krb5_data_free (rep); 21155682Smarkm return -1; 21255682Smarkm } 21355682Smarkm s[rep->length] = 0; 21455682Smarkm p = strstr(s, "\r\n\r\n"); 21555682Smarkm if(p == NULL) { 216178825Sdfr krb5_data_zero(rep); 21755682Smarkm free(s); 21855682Smarkm return -1; 21955682Smarkm } 22055682Smarkm p += 4; 22155682Smarkm rep->data = s; 22255682Smarkm rep->length -= p - s; 22355682Smarkm if(rep->length < 4) { /* remove length */ 224178825Sdfr krb5_data_zero(rep); 22555682Smarkm free(s); 22655682Smarkm return -1; 22755682Smarkm } 22855682Smarkm rep->length -= 4; 22955682Smarkm _krb5_get_int(p, &rep_len, 4); 23055682Smarkm if (rep_len != rep->length) { 231178825Sdfr krb5_data_zero(rep); 23255682Smarkm free(s); 23355682Smarkm return -1; 23455682Smarkm } 23555682Smarkm memmove(rep->data, p + 4, rep->length); 23655682Smarkm } 23755682Smarkm return 0; 23855682Smarkm} 23955682Smarkm 24055682Smarkmstatic int 24155682Smarkminit_port(const char *s, int fallback) 24255682Smarkm{ 24355682Smarkm if (s) { 24455682Smarkm int tmp; 24555682Smarkm 24655682Smarkm sscanf (s, "%d", &tmp); 24755682Smarkm return htons(tmp); 24855682Smarkm } else 24955682Smarkm return fallback; 25055682Smarkm} 25155682Smarkm 25255682Smarkm/* 25355682Smarkm * Return 0 if succesful, otherwise 1 25455682Smarkm */ 25555682Smarkm 25655682Smarkmstatic int 25755682Smarkmsend_via_proxy (krb5_context context, 25890926Snectar const krb5_krbhst_info *hi, 259102644Snectar const krb5_data *send_data, 26055682Smarkm krb5_data *receive) 26155682Smarkm{ 26272445Sassar char *proxy2 = strdup(context->http_proxy); 26372445Sassar char *proxy = proxy2; 264233294Sstas char *prefix = NULL; 26555682Smarkm char *colon; 26655682Smarkm struct addrinfo hints; 26755682Smarkm struct addrinfo *ai, *a; 26855682Smarkm int ret; 269233294Sstas krb5_socket_t s = rk_INVALID_SOCKET; 27055682Smarkm char portstr[NI_MAXSERV]; 271233294Sstas 27272445Sassar if (proxy == NULL) 27372445Sassar return ENOMEM; 27472445Sassar if (strncmp (proxy, "http://", 7) == 0) 27572445Sassar proxy += 7; 27672445Sassar 27755682Smarkm colon = strchr(proxy, ':'); 27855682Smarkm if(colon != NULL) 27955682Smarkm *colon++ = '\0'; 28055682Smarkm memset (&hints, 0, sizeof(hints)); 28155682Smarkm hints.ai_family = PF_UNSPEC; 28255682Smarkm hints.ai_socktype = SOCK_STREAM; 28355682Smarkm snprintf (portstr, sizeof(portstr), "%d", 28455682Smarkm ntohs(init_port (colon, htons(80)))); 28572445Sassar ret = getaddrinfo (proxy, portstr, &hints, &ai); 28672445Sassar free (proxy2); 28755682Smarkm if (ret) 28878527Sassar return krb5_eai_to_heim_errno(ret, errno); 28955682Smarkm 29055682Smarkm for (a = ai; a != NULL; a = a->ai_next) { 291233294Sstas s = socket (a->ai_family, a->ai_socktype | SOCK_CLOEXEC, a->ai_protocol); 29255682Smarkm if (s < 0) 29355682Smarkm continue; 294233294Sstas rk_cloexec(s); 29555682Smarkm if (connect (s, a->ai_addr, a->ai_addrlen) < 0) { 296233294Sstas rk_closesocket (s); 29755682Smarkm continue; 29855682Smarkm } 29955682Smarkm break; 30055682Smarkm } 30155682Smarkm if (a == NULL) { 30255682Smarkm freeaddrinfo (ai); 30355682Smarkm return 1; 30455682Smarkm } 30555682Smarkm freeaddrinfo (ai); 30655682Smarkm 307233294Sstas ret = asprintf(&prefix, "http://%s/", hi->hostname); 308233294Sstas if(ret < 0 || prefix == NULL) { 30955682Smarkm close(s); 31055682Smarkm return 1; 31155682Smarkm } 31255682Smarkm ret = send_and_recv_http(s, context->kdc_timeout, 313102644Snectar prefix, send_data, receive); 314233294Sstas rk_closesocket (s); 31555682Smarkm free(prefix); 31655682Smarkm if(ret == 0 && receive->length != 0) 31755682Smarkm return 0; 31855682Smarkm return 1; 31955682Smarkm} 32055682Smarkm 321233294Sstasstatic krb5_error_code 322233294Sstassend_via_plugin(krb5_context context, 323233294Sstas krb5_krbhst_info *hi, 324233294Sstas time_t timeout, 325233294Sstas const krb5_data *send_data, 326233294Sstas krb5_data *receive) 327233294Sstas{ 328233294Sstas struct krb5_plugin *list = NULL, *e; 329233294Sstas krb5_error_code ret; 330233294Sstas 331233294Sstas ret = _krb5_plugin_find(context, PLUGIN_TYPE_DATA, KRB5_PLUGIN_SEND_TO_KDC, &list); 332233294Sstas if(ret != 0 || list == NULL) 333233294Sstas return KRB5_PLUGIN_NO_HANDLE; 334233294Sstas 335233294Sstas for (e = list; e != NULL; e = _krb5_plugin_get_next(e)) { 336233294Sstas krb5plugin_send_to_kdc_ftable *service; 337233294Sstas void *ctx; 338233294Sstas 339233294Sstas service = _krb5_plugin_get_symbol(e); 340233294Sstas if (service->minor_version != 0) 341233294Sstas continue; 342233294Sstas 343233294Sstas (*service->init)(context, &ctx); 344233294Sstas ret = (*service->send_to_kdc)(context, ctx, hi, 345233294Sstas timeout, send_data, receive); 346233294Sstas (*service->fini)(ctx); 347233294Sstas if (ret == 0) 348233294Sstas break; 349233294Sstas if (ret != KRB5_PLUGIN_NO_HANDLE) { 350233294Sstas krb5_set_error_message(context, ret, 351233294Sstas N_("Plugin send_to_kdc failed to " 352233294Sstas "lookup with error: %d", ""), ret); 353233294Sstas break; 354233294Sstas } 355233294Sstas } 356233294Sstas _krb5_plugin_free(list); 357233294Sstas return KRB5_PLUGIN_NO_HANDLE; 358233294Sstas} 359233294Sstas 360233294Sstas 36155682Smarkm/* 36290926Snectar * Send the data `send' to one host from `handle` and get back the reply 36355682Smarkm * in `receive'. 36455682Smarkm */ 36555682Smarkm 366233294SstasKRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 36772445Sassarkrb5_sendto (krb5_context context, 368102644Snectar const krb5_data *send_data, 369233294Sstas krb5_krbhst_handle handle, 37072445Sassar krb5_data *receive) 37155682Smarkm{ 372178825Sdfr krb5_error_code ret; 373233294Sstas krb5_socket_t fd; 374233294Sstas size_t i; 37555682Smarkm 376178825Sdfr krb5_data_zero(receive); 377178825Sdfr 37878527Sassar for (i = 0; i < context->max_retries; ++i) { 37990926Snectar krb5_krbhst_info *hi; 38090926Snectar 38190926Snectar while (krb5_krbhst_next(context, handle, &hi) == 0) { 38255682Smarkm struct addrinfo *ai, *a; 38355682Smarkm 384233294Sstas _krb5_debug(context, 2, 385233294Sstas "trying to communicate with host %s in realm %s", 386233294Sstas hi->hostname, _krb5_krbhst_get_realm(handle)); 387233294Sstas 388178825Sdfr if (context->send_to_kdc) { 389178825Sdfr struct send_to_kdc *s = context->send_to_kdc; 390178825Sdfr 391233294Sstas ret = (*s->func)(context, s->data, hi, 392233294Sstas context->kdc_timeout, send_data, receive); 393178825Sdfr if (ret == 0 && receive->length != 0) 394178825Sdfr goto out; 395178825Sdfr continue; 396178825Sdfr } 397178825Sdfr 398233294Sstas ret = send_via_plugin(context, hi, context->kdc_timeout, 399233294Sstas send_data, receive); 400233294Sstas if (ret == 0 && receive->length != 0) 401233294Sstas goto out; 402233294Sstas else if (ret != KRB5_PLUGIN_NO_HANDLE) 403233294Sstas continue; 404233294Sstas 40590926Snectar if(hi->proto == KRB5_KRBHST_HTTP && context->http_proxy) { 406178825Sdfr if (send_via_proxy (context, hi, send_data, receive) == 0) { 407178825Sdfr ret = 0; 40855682Smarkm goto out; 409178825Sdfr } 410178825Sdfr continue; 41155682Smarkm } 41255682Smarkm 41390926Snectar ret = krb5_krbhst_get_addrinfo(context, hi, &ai); 41455682Smarkm if (ret) 41555682Smarkm continue; 41690926Snectar 41755682Smarkm for (a = ai; a != NULL; a = a->ai_next) { 418233294Sstas fd = socket (a->ai_family, a->ai_socktype | SOCK_CLOEXEC, a->ai_protocol); 419233294Sstas if (rk_IS_BAD_SOCKET(fd)) 42055682Smarkm continue; 421233294Sstas rk_cloexec(fd); 42255682Smarkm if (connect (fd, a->ai_addr, a->ai_addrlen) < 0) { 423233294Sstas rk_closesocket (fd); 42455682Smarkm continue; 42555682Smarkm } 42690926Snectar switch (hi->proto) { 42790926Snectar case KRB5_KRBHST_HTTP : 42878527Sassar ret = send_and_recv_http(fd, context->kdc_timeout, 429102644Snectar "", send_data, receive); 43090926Snectar break; 43190926Snectar case KRB5_KRBHST_TCP : 43278527Sassar ret = send_and_recv_tcp (fd, context->kdc_timeout, 433102644Snectar send_data, receive); 43490926Snectar break; 43590926Snectar case KRB5_KRBHST_UDP : 43678527Sassar ret = send_and_recv_udp (fd, context->kdc_timeout, 437102644Snectar send_data, receive); 43890926Snectar break; 43990926Snectar } 440233294Sstas rk_closesocket (fd); 44190926Snectar if(ret == 0 && receive->length != 0) 44278527Sassar goto out; 44355682Smarkm } 44455682Smarkm } 44590926Snectar krb5_krbhst_reset(context, handle); 44678527Sassar } 447233294Sstas krb5_clear_error_message (context); 44855682Smarkm ret = KRB5_KDC_UNREACH; 44955682Smarkmout: 450233294Sstas _krb5_debug(context, 2, 451233294Sstas "result of trying to talk to realm %s = %d", 452233294Sstas _krb5_krbhst_get_realm(handle), ret); 45355682Smarkm return ret; 45455682Smarkm} 45572445Sassar 456233294SstasKRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 457178825Sdfrkrb5_sendto_kdc(krb5_context context, 458178825Sdfr const krb5_data *send_data, 459178825Sdfr const krb5_realm *realm, 460178825Sdfr krb5_data *receive) 46172445Sassar{ 462178825Sdfr return krb5_sendto_kdc_flags(context, send_data, realm, receive, 0); 463178825Sdfr} 464178825Sdfr 465233294SstasKRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 466178825Sdfrkrb5_sendto_kdc_flags(krb5_context context, 467178825Sdfr const krb5_data *send_data, 468178825Sdfr const krb5_realm *realm, 469178825Sdfr krb5_data *receive, 470178825Sdfr int flags) 471178825Sdfr{ 47272445Sassar krb5_error_code ret; 473178825Sdfr krb5_sendto_ctx ctx; 47490926Snectar 475178825Sdfr ret = krb5_sendto_ctx_alloc(context, &ctx); 47672445Sassar if (ret) 47772445Sassar return ret; 478178825Sdfr krb5_sendto_ctx_add_flags(ctx, flags); 479178825Sdfr krb5_sendto_ctx_set_func(ctx, _krb5_kdc_retry, NULL); 48090926Snectar 481178825Sdfr ret = krb5_sendto_context(context, ctx, send_data, *realm, receive); 482178825Sdfr krb5_sendto_ctx_free(context, ctx); 483178825Sdfr return ret; 484178825Sdfr} 485178825Sdfr 486233294SstasKRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 487233294Sstaskrb5_set_send_to_kdc_func(krb5_context context, 488178825Sdfr krb5_send_to_kdc_func func, 489178825Sdfr void *data) 490178825Sdfr{ 491178825Sdfr free(context->send_to_kdc); 492178825Sdfr if (func == NULL) { 493178825Sdfr context->send_to_kdc = NULL; 494178825Sdfr return 0; 495178825Sdfr } 496178825Sdfr 497178825Sdfr context->send_to_kdc = malloc(sizeof(*context->send_to_kdc)); 498178825Sdfr if (context->send_to_kdc == NULL) { 499233294Sstas krb5_set_error_message(context, ENOMEM, 500233294Sstas N_("malloc: out of memory", "")); 501178825Sdfr return ENOMEM; 502178825Sdfr } 503178825Sdfr 504178825Sdfr context->send_to_kdc->func = func; 505178825Sdfr context->send_to_kdc->data = data; 506178825Sdfr return 0; 507178825Sdfr} 508178825Sdfr 509233294SstasKRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 510233294Sstas_krb5_copy_send_to_kdc_func(krb5_context context, krb5_context to) 511233294Sstas{ 512233294Sstas if (context->send_to_kdc) 513233294Sstas return krb5_set_send_to_kdc_func(to, 514233294Sstas context->send_to_kdc->func, 515233294Sstas context->send_to_kdc->data); 516233294Sstas else 517233294Sstas return krb5_set_send_to_kdc_func(to, NULL, NULL); 518233294Sstas} 519233294Sstas 520233294Sstas 521233294Sstas 522178825Sdfrstruct krb5_sendto_ctx_data { 523178825Sdfr int flags; 524178825Sdfr int type; 525178825Sdfr krb5_sendto_ctx_func func; 526178825Sdfr void *data; 527178825Sdfr}; 528178825Sdfr 529233294SstasKRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 530178825Sdfrkrb5_sendto_ctx_alloc(krb5_context context, krb5_sendto_ctx *ctx) 531178825Sdfr{ 532178825Sdfr *ctx = calloc(1, sizeof(**ctx)); 533178825Sdfr if (*ctx == NULL) { 534233294Sstas krb5_set_error_message(context, ENOMEM, 535233294Sstas N_("malloc: out of memory", "")); 536178825Sdfr return ENOMEM; 537178825Sdfr } 538178825Sdfr return 0; 539178825Sdfr} 540178825Sdfr 541233294SstasKRB5_LIB_FUNCTION void KRB5_LIB_CALL 542178825Sdfrkrb5_sendto_ctx_add_flags(krb5_sendto_ctx ctx, int flags) 543178825Sdfr{ 544178825Sdfr ctx->flags |= flags; 545178825Sdfr} 546178825Sdfr 547233294SstasKRB5_LIB_FUNCTION int KRB5_LIB_CALL 548178825Sdfrkrb5_sendto_ctx_get_flags(krb5_sendto_ctx ctx) 549178825Sdfr{ 550178825Sdfr return ctx->flags; 551178825Sdfr} 552178825Sdfr 553233294SstasKRB5_LIB_FUNCTION void KRB5_LIB_CALL 554178825Sdfrkrb5_sendto_ctx_set_type(krb5_sendto_ctx ctx, int type) 555178825Sdfr{ 556178825Sdfr ctx->type = type; 557178825Sdfr} 558178825Sdfr 559178825Sdfr 560233294SstasKRB5_LIB_FUNCTION void KRB5_LIB_CALL 561178825Sdfrkrb5_sendto_ctx_set_func(krb5_sendto_ctx ctx, 562178825Sdfr krb5_sendto_ctx_func func, 563178825Sdfr void *data) 564178825Sdfr{ 565178825Sdfr ctx->func = func; 566178825Sdfr ctx->data = data; 567178825Sdfr} 568178825Sdfr 569233294SstasKRB5_LIB_FUNCTION void KRB5_LIB_CALL 570178825Sdfrkrb5_sendto_ctx_free(krb5_context context, krb5_sendto_ctx ctx) 571178825Sdfr{ 572178825Sdfr memset(ctx, 0, sizeof(*ctx)); 573178825Sdfr free(ctx); 574178825Sdfr} 575178825Sdfr 576233294SstasKRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 577178825Sdfrkrb5_sendto_context(krb5_context context, 578178825Sdfr krb5_sendto_ctx ctx, 579178825Sdfr const krb5_data *send_data, 580178825Sdfr const krb5_realm realm, 581178825Sdfr krb5_data *receive) 582178825Sdfr{ 583178825Sdfr krb5_error_code ret; 584178825Sdfr krb5_krbhst_handle handle = NULL; 585178825Sdfr int type, freectx = 0; 586178825Sdfr int action; 587178825Sdfr 588178825Sdfr krb5_data_zero(receive); 589178825Sdfr 590178825Sdfr if (ctx == NULL) { 591178825Sdfr freectx = 1; 592178825Sdfr ret = krb5_sendto_ctx_alloc(context, &ctx); 593178825Sdfr if (ret) 594178825Sdfr return ret; 595178825Sdfr } 596178825Sdfr 597178825Sdfr type = ctx->type; 598178825Sdfr if (type == 0) { 599178825Sdfr if ((ctx->flags & KRB5_KRBHST_FLAGS_MASTER) || context->use_admin_kdc) 600178825Sdfr type = KRB5_KRBHST_ADMIN; 601178825Sdfr else 602178825Sdfr type = KRB5_KRBHST_KDC; 603178825Sdfr } 604178825Sdfr 605233294Sstas if ((int)send_data->length > context->large_msg_size) 606178825Sdfr ctx->flags |= KRB5_KRBHST_FLAGS_LARGE_MSG; 607178825Sdfr 608178825Sdfr /* loop until we get back a appropriate response */ 609178825Sdfr 610178825Sdfr do { 611178825Sdfr action = KRB5_SENDTO_DONE; 612178825Sdfr 613178825Sdfr krb5_data_free(receive); 614178825Sdfr 615178825Sdfr if (handle == NULL) { 616233294Sstas ret = krb5_krbhst_init_flags(context, realm, type, 617178825Sdfr ctx->flags, &handle); 618178825Sdfr if (ret) { 619178825Sdfr if (freectx) 620178825Sdfr krb5_sendto_ctx_free(context, ctx); 621178825Sdfr return ret; 622178825Sdfr } 623178825Sdfr } 624233294Sstas 625178825Sdfr ret = krb5_sendto(context, send_data, handle, receive); 626178825Sdfr if (ret) 627178825Sdfr break; 628178825Sdfr if (ctx->func) { 629178825Sdfr ret = (*ctx->func)(context, ctx, ctx->data, receive, &action); 630178825Sdfr if (ret) 631178825Sdfr break; 632178825Sdfr } 633178825Sdfr if (action != KRB5_SENDTO_CONTINUE) { 634178825Sdfr krb5_krbhst_free(context, handle); 635178825Sdfr handle = NULL; 636178825Sdfr } 637178825Sdfr } while (action != KRB5_SENDTO_DONE); 638178825Sdfr if (handle) 639178825Sdfr krb5_krbhst_free(context, handle); 64078527Sassar if (ret == KRB5_KDC_UNREACH) 641233294Sstas krb5_set_error_message(context, ret, 642233294Sstas N_("unable to reach any KDC in realm %s", ""), 643233294Sstas realm); 644178825Sdfr if (ret) 645178825Sdfr krb5_data_free(receive); 646178825Sdfr if (freectx) 647178825Sdfr krb5_sendto_ctx_free(context, ctx); 64872445Sassar return ret; 64972445Sassar} 65072445Sassar 651233294Sstaskrb5_error_code KRB5_CALLCONV 652178825Sdfr_krb5_kdc_retry(krb5_context context, krb5_sendto_ctx ctx, void *data, 653178825Sdfr const krb5_data *reply, int *action) 65472445Sassar{ 655178825Sdfr krb5_error_code ret; 656178825Sdfr KRB_ERROR error; 657178825Sdfr 658178825Sdfr if(krb5_rd_error(context, reply, &error)) 659178825Sdfr return 0; 660178825Sdfr 661178825Sdfr ret = krb5_error_from_rd_error(context, &error, NULL); 662178825Sdfr krb5_free_error_contents(context, &error); 663178825Sdfr 664178825Sdfr switch(ret) { 665178825Sdfr case KRB5KRB_ERR_RESPONSE_TOO_BIG: { 666178825Sdfr if (krb5_sendto_ctx_get_flags(ctx) & KRB5_KRBHST_FLAGS_LARGE_MSG) 667178825Sdfr break; 668178825Sdfr krb5_sendto_ctx_add_flags(ctx, KRB5_KRBHST_FLAGS_LARGE_MSG); 669178825Sdfr *action = KRB5_SENDTO_RESTART; 670178825Sdfr break; 671178825Sdfr } 672178825Sdfr case KRB5KDC_ERR_SVC_UNAVAILABLE: 673178825Sdfr *action = KRB5_SENDTO_CONTINUE; 674178825Sdfr break; 675178825Sdfr } 676178825Sdfr return 0; 67772445Sassar} 678