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