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