Fix autoconf 2.70 compatibility
[platform/upstream/krb5.git] / src / lib / krb5 / os / sendto_kdc.c
1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /* lib/krb5/os/sendto_kdc.c */
3 /*
4  * Copyright 1990,1991,2001,2002,2004,2005,2007,2008 by the Massachusetts Institute of Technology.
5  * All Rights Reserved.
6  *
7  * Export of this software from the United States of America may
8  *   require a specific license from the United States Government.
9  *   It is the responsibility of any person or organization contemplating
10  *   export to obtain such a license before exporting.
11  *
12  * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
13  * distribute this software and its documentation for any purpose and
14  * without fee is hereby granted, provided that the above copyright
15  * notice appear in all copies and that both that copyright notice and
16  * this permission notice appear in supporting documentation, and that
17  * the name of M.I.T. not be used in advertising or publicity pertaining
18  * to distribution of the software without specific, written prior
19  * permission.  Furthermore if you modify this software you must label
20  * your software as modified software and not distribute it in such a
21  * fashion that it might be confused with the original M.I.T. software.
22  * M.I.T. makes no representations about the suitability of
23  * this software for any purpose.  It is provided "as is" without express
24  * or implied warranty.
25  */
26 /*
27  * MS-KKDCP implementation Copyright 2013,2014 Red Hat, Inc.
28  *
29  * Redistribution and use in source and binary forms, with or without
30  * modification, are permitted provided that the following conditions are met:
31  *
32  *    1. Redistributions of source code must retain the above copyright
33  *       notice, this list of conditions and the following disclaimer.
34  *
35  *    2. Redistributions in binary form must reproduce the above copyright
36  *       notice, this list of conditions and the following disclaimer in
37  *       the documentation and/or other materials provided with the
38  *       distribution.
39  *
40  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
41  * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
42  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
43  * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
44  * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
45  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
46  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
47  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
48  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
49  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
50  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
51  */
52
53 /* Send packet to KDC for realm; wait for response, retransmitting
54  * as necessary. */
55
56 #include "k5-int.h"
57 #include "k5-tls.h"
58 #include "fake-addrinfo.h"
59
60 #include "os-proto.h"
61
62 #if defined(HAVE_POLL_H)
63 #include <poll.h>
64 #define USE_POLL
65 #define MAX_POLLFDS 1024
66 #elif defined(HAVE_SYS_SELECT_H)
67 #include <sys/select.h>
68 #endif
69
70 #ifndef _WIN32
71 /* For FIONBIO.  */
72 #include <sys/ioctl.h>
73 #ifdef HAVE_SYS_FILIO_H
74 #include <sys/filio.h>
75 #endif
76 #endif
77
78 #define MAX_PASS                    3
79 #define DEFAULT_UDP_PREF_LIMIT   1465
80 #define HARD_UDP_LIMIT          32700 /* could probably do 64K-epsilon ? */
81 #define PORT_LENGTH                 6 /* decimal repr of UINT16_MAX */
82
83 /* Select state flags.  */
84 #define SSF_READ 0x01
85 #define SSF_WRITE 0x02
86 #define SSF_EXCEPTION 0x04
87
88 typedef int64_t time_ms;
89
90 /* This can be pretty large, so should not be stack-allocated. */
91 struct select_state {
92 #ifdef USE_POLL
93     struct pollfd fds[MAX_POLLFDS];
94 #else
95     int max;
96     fd_set rfds, wfds, xfds;
97 #endif
98     int nfds;
99 };
100
101 /* connection states */
102 enum conn_states { INITIALIZING, CONNECTING, WRITING, READING, FAILED };
103 struct incoming_message {
104     size_t bufsizebytes_read;
105     size_t bufsize;
106     size_t pos;
107     char *buf;
108     unsigned char bufsizebytes[4];
109     size_t n_left;
110 };
111
112 struct outgoing_message {
113     sg_buf sgbuf[2];
114     sg_buf *sgp;
115     int sg_count;
116     unsigned char msg_len_buf[4];
117 };
118
119 struct conn_state;
120 typedef krb5_boolean fd_handler_fn(krb5_context context,
121                                    const krb5_data *realm,
122                                    struct conn_state *conn,
123                                    struct select_state *selstate);
124
125 struct conn_state {
126     SOCKET fd;
127     enum conn_states state;
128     fd_handler_fn *service_connect;
129     fd_handler_fn *service_write;
130     fd_handler_fn *service_read;
131     struct remote_address addr;
132     struct incoming_message in;
133     struct outgoing_message out;
134     krb5_data callback_buffer;
135     size_t server_index;
136     struct conn_state *next;
137     time_ms endtime;
138     krb5_boolean defer;
139     struct {
140         const char *uri_path;
141         const char *servername;
142         char port[PORT_LENGTH];
143         char *https_request;
144         k5_tls_handle tls;
145     } http;
146 };
147
148 /* Set up context->tls.  On allocation failure, return ENOMEM.  On plugin load
149  * failure, set context->tls to point to a nulled vtable and return 0. */
150 static krb5_error_code
151 init_tls_vtable(krb5_context context)
152 {
153     krb5_plugin_initvt_fn initfn;
154     krb5_error_code ret;
155
156     if (context->tls != NULL)
157         return 0;
158
159     context->tls = calloc(1, sizeof(*context->tls));
160     if (context->tls == NULL)
161         return ENOMEM;
162
163     /* Attempt to load the module; just let it stay nulled out on failure. */
164     k5_plugin_register_dyn(context, PLUGIN_INTERFACE_TLS, "k5tls", "tls");
165     ret = k5_plugin_load(context, PLUGIN_INTERFACE_TLS, "k5tls", &initfn);
166     if (!ret)
167         (*initfn)(context, 0, 0, (krb5_plugin_vtable)context->tls);
168     else
169         TRACE_SENDTO_KDC_K5TLS_LOAD_ERROR(context, ret);
170
171     return 0;
172 }
173
174 /* Get current time in milliseconds. */
175 static krb5_error_code
176 get_curtime_ms(time_ms *time_out)
177 {
178     struct timeval tv;
179
180     *time_out = 0;
181
182     if (gettimeofday(&tv, 0))
183         return errno;
184     *time_out = (time_ms)tv.tv_sec * 1000 + tv.tv_usec / 1000;
185     return 0;
186 }
187
188 static void
189 free_http_tls_data(krb5_context context, struct conn_state *state)
190 {
191     if (state->http.tls != NULL)
192         context->tls->free_handle(context, state->http.tls);
193     state->http.tls = NULL;
194     free(state->http.https_request);
195     state->http.https_request = NULL;
196 }
197
198 #ifdef USE_POLL
199
200 /* Find a pollfd in selstate by fd, or abort if we can't find it. */
201 static inline struct pollfd *
202 find_pollfd(struct select_state *selstate, int fd)
203 {
204     int i;
205
206     for (i = 0; i < selstate->nfds; i++) {
207         if (selstate->fds[i].fd == fd)
208             return &selstate->fds[i];
209     }
210     abort();
211 }
212
213 static void
214 cm_init_selstate(struct select_state *selstate)
215 {
216     selstate->nfds = 0;
217 }
218
219 static krb5_boolean
220 cm_add_fd(struct select_state *selstate, int fd)
221 {
222     if (selstate->nfds >= MAX_POLLFDS)
223         return FALSE;
224     selstate->fds[selstate->nfds].fd = fd;
225     selstate->fds[selstate->nfds].events = 0;
226     selstate->nfds++;
227     return TRUE;
228 }
229
230 static void
231 cm_remove_fd(struct select_state *selstate, int fd)
232 {
233     struct pollfd *pfd = find_pollfd(selstate, fd);
234
235     *pfd = selstate->fds[selstate->nfds - 1];
236     selstate->nfds--;
237 }
238
239 /* Poll for reading (and not writing) on fd the next time we poll. */
240 static void
241 cm_read(struct select_state *selstate, int fd)
242 {
243     find_pollfd(selstate, fd)->events = POLLIN;
244 }
245
246 /* Poll for writing (and not reading) on fd the next time we poll. */
247 static void
248 cm_write(struct select_state *selstate, int fd)
249 {
250     find_pollfd(selstate, fd)->events = POLLOUT;
251 }
252
253 /* Get the output events for fd in the form of ssflags. */
254 static unsigned int
255 cm_get_ssflags(struct select_state *selstate, int fd)
256 {
257     struct pollfd *pfd = find_pollfd(selstate, fd);
258
259     /*
260      * macOS sets POLLHUP without POLLOUT on connection error.  Catch this as
261      * well as other error events such as POLLNVAL, but only if POLLIN and
262      * POLLOUT aren't set, as we can get POLLHUP along with POLLIN with TCP
263      * data still to be read.
264      */
265     if (pfd->revents != 0 && !(pfd->revents & (POLLIN | POLLOUT)))
266         return SSF_EXCEPTION;
267
268     return ((pfd->revents & POLLIN) ? SSF_READ : 0) |
269         ((pfd->revents & POLLOUT) ? SSF_WRITE : 0) |
270         ((pfd->revents & POLLERR) ? SSF_EXCEPTION : 0);
271 }
272
273 #else /* not USE_POLL */
274
275 static void
276 cm_init_selstate(struct select_state *selstate)
277 {
278     selstate->nfds = 0;
279     selstate->max = 0;
280     FD_ZERO(&selstate->rfds);
281     FD_ZERO(&selstate->wfds);
282     FD_ZERO(&selstate->xfds);
283 }
284
285 static krb5_boolean
286 cm_add_fd(struct select_state *selstate, int fd)
287 {
288 #ifndef _WIN32  /* On Windows FD_SETSIZE is a count, not a max value. */
289     if (fd >= FD_SETSIZE)
290         return FALSE;
291 #endif
292     FD_SET(fd, &selstate->xfds);
293     if (selstate->max <= fd)
294         selstate->max = fd + 1;
295     selstate->nfds++;
296     return TRUE;
297 }
298
299 static void
300 cm_remove_fd(struct select_state *selstate, int fd)
301 {
302     FD_CLR(fd, &selstate->rfds);
303     FD_CLR(fd, &selstate->wfds);
304     FD_CLR(fd, &selstate->xfds);
305     if (selstate->max == fd + 1) {
306         while (selstate->max > 0 &&
307                !FD_ISSET(selstate->max - 1, &selstate->rfds) &&
308                !FD_ISSET(selstate->max - 1, &selstate->wfds) &&
309                !FD_ISSET(selstate->max - 1, &selstate->xfds))
310             selstate->max--;
311     }
312     selstate->nfds--;
313 }
314
315 /* Select for reading (and not writing) on fd the next time we select. */
316 static void
317 cm_read(struct select_state *selstate, int fd)
318 {
319     FD_SET(fd, &selstate->rfds);
320     FD_CLR(fd, &selstate->wfds);
321 }
322
323 /* Select for writing (and not reading) on fd the next time we select. */
324 static void
325 cm_write(struct select_state *selstate, int fd)
326 {
327     FD_CLR(fd, &selstate->rfds);
328     FD_SET(fd, &selstate->wfds);
329 }
330
331 /* Get the events for fd from selstate after a select. */
332 static unsigned int
333 cm_get_ssflags(struct select_state *selstate, int fd)
334 {
335     return (FD_ISSET(fd, &selstate->rfds) ? SSF_READ : 0) |
336         (FD_ISSET(fd, &selstate->wfds) ? SSF_WRITE : 0) |
337         (FD_ISSET(fd, &selstate->xfds) ? SSF_EXCEPTION : 0);
338 }
339
340 #endif /* not USE_POLL */
341
342 static krb5_error_code
343 cm_select_or_poll(const struct select_state *in, time_ms endtime,
344                   struct select_state *out, int *sret)
345 {
346 #ifndef USE_POLL
347     struct timeval tv;
348 #endif
349     krb5_error_code retval;
350     time_ms curtime, interval;
351
352     retval = get_curtime_ms(&curtime);
353     if (retval != 0)
354         return retval;
355     interval = (curtime < endtime) ? endtime - curtime : 0;
356
357     /* We don't need a separate copy of the selstate for poll, but use one for
358      * consistency with how we use select. */
359     *out = *in;
360
361 #ifdef USE_POLL
362     *sret = poll(out->fds, out->nfds, interval);
363 #else
364     tv.tv_sec = interval / 1000;
365     tv.tv_usec = interval % 1000 * 1000;
366     *sret = select(out->max, &out->rfds, &out->wfds, &out->xfds, &tv);
367 #endif
368
369     return (*sret < 0) ? SOCKET_ERRNO : 0;
370 }
371
372 static int
373 socktype_for_transport(k5_transport transport)
374 {
375     switch (transport) {
376     case UDP:
377         return SOCK_DGRAM;
378     case TCP:
379     case HTTPS:
380         return SOCK_STREAM;
381     default:
382         return 0;
383     }
384 }
385
386 static int
387 check_for_svc_unavailable (krb5_context context,
388                            const krb5_data *reply,
389                            void *msg_handler_data)
390 {
391     krb5_error_code *retval = (krb5_error_code *)msg_handler_data;
392
393     *retval = 0;
394
395     if (krb5_is_krb_error(reply)) {
396         krb5_error *err_reply;
397
398         if (decode_krb5_error(reply, &err_reply) == 0) {
399             *retval = err_reply->error;
400             krb5_free_error(context, err_reply);
401
402             /* Returning 0 means continue to next KDC */
403             return (*retval != KDC_ERR_SVC_UNAVAILABLE);
404         }
405     }
406
407     return 1;
408 }
409
410 void KRB5_CALLCONV
411 krb5_set_kdc_send_hook(krb5_context context, krb5_pre_send_fn send_hook,
412                        void *data)
413 {
414     context->kdc_send_hook = send_hook;
415     context->kdc_send_hook_data = data;
416 }
417
418 void KRB5_CALLCONV
419 krb5_set_kdc_recv_hook(krb5_context context, krb5_post_recv_fn recv_hook,
420                        void *data)
421 {
422     context->kdc_recv_hook = recv_hook;
423     context->kdc_recv_hook_data = data;
424 }
425
426 /*
427  * send the formatted request 'message' to a KDC for realm 'realm' and
428  * return the response (if any) in 'reply'.
429  *
430  * If the message is sent and a response is received, 0 is returned,
431  * otherwise an error code is returned.
432  *
433  * The storage for 'reply' is allocated and should be freed by the caller
434  * when finished.
435  */
436
437 krb5_error_code
438 krb5_sendto_kdc(krb5_context context, const krb5_data *message,
439                 const krb5_data *realm, krb5_data *reply_out, int *use_master,
440                 int no_udp)
441 {
442     krb5_error_code retval, oldret, err;
443     struct serverlist servers;
444     int server_used;
445     k5_transport_strategy strategy;
446     krb5_data reply = empty_data(), *hook_message = NULL, *hook_reply = NULL;
447
448     *reply_out = empty_data();
449
450     /*
451      * find KDC location(s) for realm
452      */
453
454     /*
455      * BUG: This code won't return "interesting" errors (e.g., out of mem,
456      * bad config file) from locate_kdc.  KRB5_REALM_CANT_RESOLVE can be
457      * ignored from one query of two, but if only one query is done, or
458      * both return that error, it should be returned to the caller.  Also,
459      * "interesting" errors (not KRB5_KDC_UNREACH) from sendto_{udp,tcp}
460      * should probably be returned as well.
461      */
462
463     TRACE_SENDTO_KDC(context, message->length, realm, *use_master, no_udp);
464
465     if (!no_udp && context->udp_pref_limit < 0) {
466         int tmp;
467         retval = profile_get_integer(context->profile,
468                                      KRB5_CONF_LIBDEFAULTS, KRB5_CONF_UDP_PREFERENCE_LIMIT, 0,
469                                      DEFAULT_UDP_PREF_LIMIT, &tmp);
470         if (retval)
471             return retval;
472         if (tmp < 0)
473             tmp = DEFAULT_UDP_PREF_LIMIT;
474         else if (tmp > HARD_UDP_LIMIT)
475             /* In the unlikely case that a *really* big value is
476                given, let 'em use as big as we think we can
477                support.  */
478             tmp = HARD_UDP_LIMIT;
479         context->udp_pref_limit = tmp;
480     }
481
482     if (no_udp)
483         strategy = NO_UDP;
484     else if (message->length <= (unsigned int) context->udp_pref_limit)
485         strategy = UDP_FIRST;
486     else
487         strategy = UDP_LAST;
488
489     retval = k5_locate_kdc(context, realm, &servers, *use_master, no_udp);
490     if (retval)
491         return retval;
492
493     if (context->kdc_send_hook != NULL) {
494         retval = context->kdc_send_hook(context, context->kdc_send_hook_data,
495                                         realm, message, &hook_message,
496                                         &hook_reply);
497         if (retval)
498             goto cleanup;
499
500         if (hook_reply != NULL) {
501             *reply_out = *hook_reply;
502             free(hook_reply);
503             goto cleanup;
504         }
505
506         if (hook_message != NULL)
507             message = hook_message;
508     }
509
510     err = 0;
511     retval = k5_sendto(context, message, realm, &servers, strategy, NULL,
512                        &reply, NULL, NULL, &server_used,
513                        check_for_svc_unavailable, &err);
514     if (retval == KRB5_KDC_UNREACH) {
515         if (err == KDC_ERR_SVC_UNAVAILABLE) {
516             retval = KRB5KDC_ERR_SVC_UNAVAILABLE;
517         } else {
518             k5_setmsg(context, retval,
519                       _("Cannot contact any KDC for realm '%.*s'"),
520                       realm->length, realm->data);
521         }
522     }
523
524     if (context->kdc_recv_hook != NULL) {
525         oldret = retval;
526         retval = context->kdc_recv_hook(context, context->kdc_recv_hook_data,
527                                         retval, realm, message, &reply,
528                                         &hook_reply);
529         if (oldret && !retval) {
530             /* The hook must set a reply if it overrides an error from
531              * k5_sendto().  Treat this reply as coming from the master KDC. */
532             assert(hook_reply != NULL);
533             *use_master = 1;
534         }
535     }
536     if (retval)
537         goto cleanup;
538
539     if (hook_reply != NULL) {
540         *reply_out = *hook_reply;
541         free(hook_reply);
542     } else {
543         *reply_out = reply;
544         reply = empty_data();
545     }
546
547     /* Set use_master to 1 if we ended up talking to a master when we didn't
548      * explicitly request to. */
549     if (*use_master == 0) {
550         *use_master = k5_kdc_is_master(context, realm,
551                                        &servers.servers[server_used]);
552         TRACE_SENDTO_KDC_MASTER(context, *use_master);
553     }
554
555 cleanup:
556     krb5_free_data(context, hook_message);
557     krb5_free_data_contents(context, &reply);
558     k5_free_serverlist(&servers);
559     return retval;
560 }
561
562 /*
563  * Notes:
564  *
565  * Getting "connection refused" on a connected UDP socket causes
566  * select to indicate write capability on UNIX, but only shows up
567  * as an exception on Windows.  (I don't think any UNIX system flags
568  * the error as an exception.)  So we check for both, or make it
569  * system-specific.
570  *
571  * Always watch for responses from *any* of the servers.  Eventually
572  * fix the UDP code to do the same.
573  *
574  * To do:
575  * - TCP NOPUSH/CORK socket options?
576  * - error codes that don't suck
577  * - getsockopt(SO_ERROR) to check connect status
578  * - handle error RESPONSE_TOO_BIG from UDP server and use TCP
579  *   connections already in progress
580  */
581
582 static fd_handler_fn service_tcp_connect;
583 static fd_handler_fn service_tcp_write;
584 static fd_handler_fn service_tcp_read;
585 static fd_handler_fn service_udp_read;
586 static fd_handler_fn service_https_write;
587 static fd_handler_fn service_https_read;
588
589 static krb5_error_code
590 make_proxy_request(struct conn_state *state, const krb5_data *realm,
591                    const krb5_data *message, char **req_out, size_t *len_out)
592 {
593     krb5_kkdcp_message pm;
594     krb5_data *encoded_pm = NULL;
595     struct k5buf buf;
596     const char *uri_path;
597     krb5_error_code ret;
598
599     *req_out = NULL;
600     *len_out = 0;
601
602     /*
603      * Stuff the message length in at the front of the kerb_message field
604      * before encoding.  The proxied messages are actually the payload we'd
605      * be sending and receiving if we were using plain TCP.
606      */
607     memset(&pm, 0, sizeof(pm));
608     ret = alloc_data(&pm.kerb_message, message->length + 4);
609     if (ret != 0)
610         goto cleanup;
611     store_32_be(message->length, pm.kerb_message.data);
612     memcpy(pm.kerb_message.data + 4, message->data, message->length);
613     pm.target_domain = *realm;
614     ret = encode_krb5_kkdcp_message(&pm, &encoded_pm);
615     if (ret != 0)
616         goto cleanup;
617
618     /* Build the request to transmit: the headers + the proxy message. */
619     k5_buf_init_dynamic(&buf);
620     uri_path = (state->http.uri_path != NULL) ? state->http.uri_path : "";
621     k5_buf_add_fmt(&buf, "POST /%s HTTP/1.0\r\n", uri_path);
622     k5_buf_add_fmt(&buf, "Host: %s:%s\r\n", state->http.servername,
623                    state->http.port);
624     k5_buf_add(&buf, "Cache-Control: no-cache\r\n");
625     k5_buf_add(&buf, "Pragma: no-cache\r\n");
626     k5_buf_add(&buf, "User-Agent: kerberos/1.0\r\n");
627     k5_buf_add(&buf, "Content-type: application/kerberos\r\n");
628     k5_buf_add_fmt(&buf, "Content-Length: %d\r\n\r\n", encoded_pm->length);
629     k5_buf_add_len(&buf, encoded_pm->data, encoded_pm->length);
630     if (k5_buf_status(&buf) != 0) {
631         ret = ENOMEM;
632         goto cleanup;
633     }
634
635     *req_out = buf.data;
636     *len_out = buf.len;
637
638 cleanup:
639     krb5_free_data_contents(NULL, &pm.kerb_message);
640     krb5_free_data(NULL, encoded_pm);
641     return ret;
642 }
643
644 /* Set up the actual message we will send across the underlying transport to
645  * communicate the payload message, using one or both of state->out.sgbuf. */
646 static krb5_error_code
647 set_transport_message(struct conn_state *state, const krb5_data *realm,
648                       const krb5_data *message)
649 {
650     struct outgoing_message *out = &state->out;
651     char *req = NULL;
652     size_t reqlen;
653     krb5_error_code ret;
654
655     if (message == NULL || message->length == 0)
656         return 0;
657
658     if (state->addr.transport == TCP) {
659         store_32_be(message->length, out->msg_len_buf);
660         SG_SET(&out->sgbuf[0], out->msg_len_buf, 4);
661         SG_SET(&out->sgbuf[1], message->data, message->length);
662         out->sg_count = 2;
663         return 0;
664     } else if (state->addr.transport == HTTPS) {
665         ret = make_proxy_request(state, realm, message, &req, &reqlen);
666         if (ret != 0)
667             return ret;
668         SG_SET(&state->out.sgbuf[0], req, reqlen);
669         SG_SET(&state->out.sgbuf[1], 0, 0);
670         state->out.sg_count = 1;
671         free(state->http.https_request);
672         state->http.https_request = req;
673         return 0;
674     } else {
675         SG_SET(&out->sgbuf[0], message->data, message->length);
676         SG_SET(&out->sgbuf[1], NULL, 0);
677         out->sg_count = 1;
678         return 0;
679     }
680 }
681
682 static krb5_error_code
683 add_connection(struct conn_state **conns, k5_transport transport,
684                krb5_boolean defer, struct addrinfo *ai, size_t server_index,
685                const krb5_data *realm, const char *hostname,
686                const char *port, const char *uri_path, char **udpbufp)
687 {
688     struct conn_state *state, **tailptr;
689
690     state = calloc(1, sizeof(*state));
691     if (state == NULL)
692         return ENOMEM;
693     state->state = INITIALIZING;
694     state->out.sgp = state->out.sgbuf;
695     state->addr.transport = transport;
696     state->addr.family = ai->ai_family;
697     state->addr.len = ai->ai_addrlen;
698     memcpy(&state->addr.saddr, ai->ai_addr, ai->ai_addrlen);
699     state->defer = defer;
700     state->fd = INVALID_SOCKET;
701     state->server_index = server_index;
702     SG_SET(&state->out.sgbuf[1], NULL, 0);
703     if (transport == TCP) {
704         state->service_connect = service_tcp_connect;
705         state->service_write = service_tcp_write;
706         state->service_read = service_tcp_read;
707     } else if (transport == HTTPS) {
708         assert(hostname != NULL && port != NULL);
709         state->service_connect = service_tcp_connect;
710         state->service_write = service_https_write;
711         state->service_read = service_https_read;
712         state->http.uri_path = uri_path;
713         state->http.servername = hostname;
714         strlcpy(state->http.port, port, PORT_LENGTH);
715     } else {
716         state->service_connect = NULL;
717         state->service_write = NULL;
718         state->service_read = service_udp_read;
719
720         if (*udpbufp == NULL) {
721             *udpbufp = malloc(MAX_DGRAM_SIZE);
722             if (*udpbufp == 0)
723                 return ENOMEM;
724         }
725         state->in.buf = *udpbufp;
726         state->in.bufsize = MAX_DGRAM_SIZE;
727     }
728
729     /* Chain the new state onto the tail of the list. */
730     for (tailptr = conns; *tailptr != NULL; tailptr = &(*tailptr)->next);
731     *tailptr = state;
732
733     return 0;
734 }
735
736 static int
737 translate_ai_error (int err)
738 {
739     switch (err) {
740     case 0:
741         return 0;
742     case EAI_BADFLAGS:
743     case EAI_FAMILY:
744     case EAI_SOCKTYPE:
745     case EAI_SERVICE:
746         /* All of these indicate bad inputs to getaddrinfo.  */
747         return EINVAL;
748     case EAI_AGAIN:
749         /* Translate to standard errno code.  */
750         return EAGAIN;
751     case EAI_MEMORY:
752         /* Translate to standard errno code.  */
753         return ENOMEM;
754 #ifdef EAI_ADDRFAMILY
755     case EAI_ADDRFAMILY:
756 #endif
757 #if defined(EAI_NODATA) && EAI_NODATA != EAI_NONAME
758     case EAI_NODATA:
759 #endif
760     case EAI_NONAME:
761         /* Name not known or no address data, but no error.  Do
762            nothing more.  */
763         return 0;
764 #ifdef EAI_OVERFLOW
765     case EAI_OVERFLOW:
766         /* An argument buffer overflowed.  */
767         return EINVAL;          /* XXX */
768 #endif
769 #ifdef EAI_SYSTEM
770     case EAI_SYSTEM:
771         /* System error, obviously.  */
772         return errno;
773 #endif
774     default:
775         /* An error code we haven't handled?  */
776         return EINVAL;
777     }
778 }
779
780 /*
781  * Resolve the entry in servers with index ind, adding connections to the list
782  * *conns.  Connections are added for each of socktype1 and (if not zero)
783  * socktype2.  message and udpbufp are used to initialize the connections; see
784  * add_connection above.  If no addresses are available for an entry but no
785  * internal name resolution failure occurs, return 0 without adding any new
786  * connections.
787  */
788 static krb5_error_code
789 resolve_server(krb5_context context, const krb5_data *realm,
790                const struct serverlist *servers, size_t ind,
791                k5_transport_strategy strategy, const krb5_data *message,
792                char **udpbufp, struct conn_state **conns)
793 {
794     krb5_error_code retval;
795     struct server_entry *entry = &servers->servers[ind];
796     k5_transport transport;
797     struct addrinfo *addrs, *a, hint, ai;
798     krb5_boolean defer = FALSE;
799     int err, result;
800     char portbuf[PORT_LENGTH];
801
802     /* Skip UDP entries if we don't want UDP. */
803     if (strategy == NO_UDP && entry->transport == UDP)
804         return 0;
805
806     transport = (strategy == UDP_FIRST) ? UDP : TCP;
807     if (entry->hostname == NULL) {
808         /* Added by a module, so transport is either TCP or UDP. */
809         ai.ai_socktype = socktype_for_transport(entry->transport);
810         ai.ai_family = entry->family;
811         ai.ai_addrlen = entry->addrlen;
812         ai.ai_addr = (struct sockaddr *)&entry->addr;
813         defer = (entry->transport != transport);
814         return add_connection(conns, entry->transport, defer, &ai, ind, realm,
815                               NULL, NULL, entry->uri_path, udpbufp);
816     }
817
818     /* If the entry has a specified transport, use it, but possibly defer the
819      * addresses we add based on the strategy. */
820     if (entry->transport != TCP_OR_UDP) {
821         transport = entry->transport;
822         defer = (entry->transport == TCP && strategy == UDP_FIRST) ||
823             (entry->transport == UDP && strategy == UDP_LAST);
824     }
825
826     memset(&hint, 0, sizeof(hint));
827     hint.ai_family = entry->family;
828     hint.ai_socktype = socktype_for_transport(transport);
829     hint.ai_flags = AI_ADDRCONFIG;
830 #ifdef AI_NUMERICSERV
831     hint.ai_flags |= AI_NUMERICSERV;
832 #endif
833     result = snprintf(portbuf, sizeof(portbuf), "%d", entry->port);
834     if (SNPRINTF_OVERFLOW(result, sizeof(portbuf)))
835         return EINVAL;
836     TRACE_SENDTO_KDC_RESOLVING(context, entry->hostname);
837     err = getaddrinfo(entry->hostname, portbuf, &hint, &addrs);
838     if (err)
839         return translate_ai_error(err);
840
841     /* Add each address with the specified or preferred transport. */
842     retval = 0;
843     for (a = addrs; a != 0 && retval == 0; a = a->ai_next) {
844         retval = add_connection(conns, transport, defer, a, ind, realm,
845                                 entry->hostname, portbuf, entry->uri_path,
846                                 udpbufp);
847     }
848
849     /* For TCP_OR_UDP entries, add each address again with the non-preferred
850      * transport, unless we are avoiding UDP.  Flag these as deferred. */
851     if (retval == 0 && entry->transport == TCP_OR_UDP && strategy != NO_UDP) {
852         transport = (strategy == UDP_FIRST) ? TCP : UDP;
853         for (a = addrs; a != 0 && retval == 0; a = a->ai_next) {
854             a->ai_socktype = socktype_for_transport(transport);
855             retval = add_connection(conns, transport, TRUE, a, ind, realm,
856                                     entry->hostname, portbuf,
857                                     entry->uri_path, udpbufp);
858         }
859     }
860     freeaddrinfo(addrs);
861     return retval;
862 }
863
864 static int
865 start_connection(krb5_context context, struct conn_state *state,
866                  const krb5_data *message, struct select_state *selstate,
867                  const krb5_data *realm,
868                  struct sendto_callback_info *callback_info)
869 {
870     int fd, e, type;
871     static const int one = 1;
872     static const struct linger lopt = { 0, 0 };
873
874     type = socktype_for_transport(state->addr.transport);
875     fd = socket(state->addr.family, type, 0);
876     if (fd == INVALID_SOCKET)
877         return -1;              /* try other hosts */
878     set_cloexec_fd(fd);
879     /* Make it non-blocking.  */
880     ioctlsocket(fd, FIONBIO, (const void *) &one);
881     if (state->addr.transport == TCP) {
882         setsockopt(fd, SOL_SOCKET, SO_LINGER, &lopt, sizeof(lopt));
883         TRACE_SENDTO_KDC_TCP_CONNECT(context, &state->addr);
884     }
885
886     /* Start connecting to KDC.  */
887     e = SOCKET_CONNECT(fd, (struct sockaddr *)&state->addr.saddr,
888                        state->addr.len);
889     if (e != 0) {
890         /*
891          * This is the path that should be followed for non-blocking
892          * connections.
893          */
894         if (SOCKET_ERRNO == EINPROGRESS || SOCKET_ERRNO == EWOULDBLOCK) {
895             state->state = CONNECTING;
896             state->fd = fd;
897         } else {
898             (void) closesocket(fd);
899             state->state = FAILED;
900             return -2;
901         }
902     } else {
903         /*
904          * Connect returned zero even though we made it non-blocking.  This
905          * happens normally for UDP sockets, and can perhaps also happen for
906          * TCP sockets connecting to localhost.
907          */
908         state->state = WRITING;
909         state->fd = fd;
910     }
911
912     /*
913      * Here's where KPASSWD callback gets the socket information it needs for
914      * a kpasswd request
915      */
916     if (callback_info) {
917
918         e = callback_info->pfn_callback(state->fd, callback_info->data,
919                                         &state->callback_buffer);
920         if (e != 0) {
921             (void) closesocket(fd);
922             state->fd = INVALID_SOCKET;
923             state->state = FAILED;
924             return -3;
925         }
926
927         message = &state->callback_buffer;
928     }
929
930     e = set_transport_message(state, realm, message);
931     if (e != 0) {
932         TRACE_SENDTO_KDC_ERROR_SET_MESSAGE(context, &state->addr, e);
933         (void) closesocket(state->fd);
934         state->fd = INVALID_SOCKET;
935         state->state = FAILED;
936         return -4;
937     }
938
939     if (state->addr.transport == UDP) {
940         /* Send it now.  */
941         ssize_t ret;
942         sg_buf *sg = &state->out.sgbuf[0];
943
944         TRACE_SENDTO_KDC_UDP_SEND_INITIAL(context, &state->addr);
945         ret = send(state->fd, SG_BUF(sg), SG_LEN(sg), 0);
946         if (ret < 0 || (size_t) ret != SG_LEN(sg)) {
947             TRACE_SENDTO_KDC_UDP_ERROR_SEND_INITIAL(context, &state->addr,
948                                                     SOCKET_ERRNO);
949             (void) closesocket(state->fd);
950             state->fd = INVALID_SOCKET;
951             state->state = FAILED;
952             return -5;
953         } else {
954             state->state = READING;
955         }
956     }
957
958     if (!cm_add_fd(selstate, state->fd)) {
959         (void) closesocket(state->fd);
960         state->fd = INVALID_SOCKET;
961         state->state = FAILED;
962         return -1;
963     }
964     if (state->state == CONNECTING || state->state == WRITING)
965         cm_write(selstate, state->fd);
966     else
967         cm_read(selstate, state->fd);
968
969     return 0;
970 }
971
972 /* Return 0 if we sent something, non-0 otherwise.
973    If 0 is returned, the caller should delay waiting for a response.
974    Otherwise, the caller should immediately move on to process the
975    next connection.  */
976 static int
977 maybe_send(krb5_context context, struct conn_state *conn,
978            const krb5_data *message, struct select_state *selstate,
979            const krb5_data *realm,
980            struct sendto_callback_info *callback_info)
981 {
982     sg_buf *sg;
983     ssize_t ret;
984
985     if (conn->state == INITIALIZING) {
986         return start_connection(context, conn, message, selstate,
987                                 realm, callback_info);
988     }
989
990     /* Did we already shut down this channel?  */
991     if (conn->state == FAILED) {
992         return -1;
993     }
994
995     if (conn->addr.transport != UDP) {
996         /* The select callback will handle flushing any data we
997            haven't written yet, and we only write it once.  */
998         return -1;
999     }
1000
1001     /* UDP - retransmit after a previous attempt timed out. */
1002     sg = &conn->out.sgbuf[0];
1003     TRACE_SENDTO_KDC_UDP_SEND_RETRY(context, &conn->addr);
1004     ret = send(conn->fd, SG_BUF(sg), SG_LEN(sg), 0);
1005     if (ret < 0 || (size_t) ret != SG_LEN(sg)) {
1006         TRACE_SENDTO_KDC_UDP_ERROR_SEND_RETRY(context, &conn->addr,
1007                                               SOCKET_ERRNO);
1008         /* Keep connection alive, we'll try again next pass.
1009
1010            Is this likely to catch any errors we didn't get from the
1011            select callbacks?  */
1012         return -1;
1013     }
1014     /* Yay, it worked.  */
1015     return 0;
1016 }
1017
1018 static void
1019 kill_conn(krb5_context context, struct conn_state *conn,
1020           struct select_state *selstate)
1021 {
1022     free_http_tls_data(context, conn);
1023
1024     if (socktype_for_transport(conn->addr.transport) == SOCK_STREAM)
1025         TRACE_SENDTO_KDC_TCP_DISCONNECT(context, &conn->addr);
1026     cm_remove_fd(selstate, conn->fd);
1027
1028     closesocket(conn->fd);
1029     conn->fd = INVALID_SOCKET;
1030     conn->state = FAILED;
1031 }
1032
1033 /* Check socket for error.  */
1034 static int
1035 get_so_error(int fd)
1036 {
1037     int e, sockerr;
1038     socklen_t sockerrlen;
1039
1040     sockerr = 0;
1041     sockerrlen = sizeof(sockerr);
1042     e = getsockopt(fd, SOL_SOCKET, SO_ERROR, &sockerr, &sockerrlen);
1043     if (e != 0) {
1044         /* What to do now?  */
1045         e = SOCKET_ERRNO;
1046         return e;
1047     }
1048     return sockerr;
1049 }
1050
1051 /* Perform next step in sending.  Return true on usable data. */
1052 static krb5_boolean
1053 service_dispatch(krb5_context context, const krb5_data *realm,
1054                  struct conn_state *conn, struct select_state *selstate,
1055                  int ssflags)
1056 {
1057     /* Check for a socket exception. */
1058     if (ssflags & SSF_EXCEPTION) {
1059         kill_conn(context, conn, selstate);
1060         return FALSE;
1061     }
1062
1063     switch (conn->state) {
1064     case CONNECTING:
1065         assert(conn->service_connect != NULL);
1066         return conn->service_connect(context, realm, conn, selstate);
1067     case WRITING:
1068         assert(conn->service_write != NULL);
1069         return conn->service_write(context, realm, conn, selstate);
1070     case READING:
1071         assert(conn->service_read != NULL);
1072         return conn->service_read(context, realm, conn, selstate);
1073     default:
1074         abort();
1075     }
1076 }
1077
1078 /* Initialize TCP transport. */
1079 static krb5_boolean
1080 service_tcp_connect(krb5_context context, const krb5_data *realm,
1081                     struct conn_state *conn, struct select_state *selstate)
1082 {
1083     /* Check whether the connection succeeded. */
1084     int e = get_so_error(conn->fd);
1085
1086     if (e) {
1087         TRACE_SENDTO_KDC_TCP_ERROR_CONNECT(context, &conn->addr, e);
1088         kill_conn(context, conn, selstate);
1089         return FALSE;
1090     }
1091
1092     conn->state = WRITING;
1093
1094     /* Record this connection's timeout for service_fds. */
1095     if (get_curtime_ms(&conn->endtime) == 0)
1096         conn->endtime += 10000;
1097
1098     return conn->service_write(context, realm, conn, selstate);
1099 }
1100
1101 /* Sets conn->state to READING when done. */
1102 static krb5_boolean
1103 service_tcp_write(krb5_context context, const krb5_data *realm,
1104                   struct conn_state *conn, struct select_state *selstate)
1105 {
1106     ssize_t nwritten;
1107     SOCKET_WRITEV_TEMP tmp;
1108
1109     TRACE_SENDTO_KDC_TCP_SEND(context, &conn->addr);
1110     nwritten = SOCKET_WRITEV(conn->fd, conn->out.sgp, conn->out.sg_count, tmp);
1111     if (nwritten < 0) {
1112         TRACE_SENDTO_KDC_TCP_ERROR_SEND(context, &conn->addr, SOCKET_ERRNO);
1113         kill_conn(context, conn, selstate);
1114         return FALSE;
1115     }
1116     while (nwritten) {
1117         sg_buf *sgp = conn->out.sgp;
1118         if ((size_t)nwritten < SG_LEN(sgp)) {
1119             SG_ADVANCE(sgp, (size_t)nwritten);
1120             nwritten = 0;
1121         } else {
1122             nwritten -= SG_LEN(sgp);
1123             conn->out.sgp++;
1124             conn->out.sg_count--;
1125         }
1126     }
1127     if (conn->out.sg_count == 0) {
1128         /* Done writing, switch to reading. */
1129         cm_read(selstate, conn->fd);
1130         conn->state = READING;
1131     }
1132     return FALSE;
1133 }
1134
1135 /* Return true on usable data. */
1136 static krb5_boolean
1137 service_tcp_read(krb5_context context, const krb5_data *realm,
1138                  struct conn_state *conn, struct select_state *selstate)
1139 {
1140     ssize_t nread;
1141     int e = 0;
1142     struct incoming_message *in = &conn->in;
1143
1144     if (in->bufsizebytes_read == 4) {
1145         /* Reading data.  */
1146         nread = SOCKET_READ(conn->fd, &in->buf[in->pos], in->n_left);
1147         if (nread <= 0) {
1148             e = nread ? SOCKET_ERRNO : ECONNRESET;
1149             TRACE_SENDTO_KDC_TCP_ERROR_RECV(context, &conn->addr, e);
1150             kill_conn(context, conn, selstate);
1151             return FALSE;
1152         }
1153         in->n_left -= nread;
1154         in->pos += nread;
1155         if (in->n_left <= 0)
1156             return TRUE;
1157     } else {
1158         /* Reading length.  */
1159         nread = SOCKET_READ(conn->fd, in->bufsizebytes + in->bufsizebytes_read,
1160                             4 - in->bufsizebytes_read);
1161         if (nread <= 0) {
1162             e = nread ? SOCKET_ERRNO : ECONNRESET;
1163             TRACE_SENDTO_KDC_TCP_ERROR_RECV_LEN(context, &conn->addr, e);
1164             kill_conn(context, conn, selstate);
1165             return FALSE;
1166         }
1167         in->bufsizebytes_read += nread;
1168         if (in->bufsizebytes_read == 4) {
1169             unsigned long len = load_32_be(in->bufsizebytes);
1170             /* Arbitrary 1M cap.  */
1171             if (len > 1 * 1024 * 1024) {
1172                 kill_conn(context, conn, selstate);
1173                 return FALSE;
1174             }
1175             in->bufsize = in->n_left = len;
1176             in->pos = 0;
1177             in->buf = malloc(len);
1178             if (in->buf == NULL) {
1179                 kill_conn(context, conn, selstate);
1180                 return FALSE;
1181             }
1182         }
1183     }
1184     return FALSE;
1185 }
1186
1187 /* Process events on a UDP socket.  Return true if we get a reply. */
1188 static krb5_boolean
1189 service_udp_read(krb5_context context, const krb5_data *realm,
1190                  struct conn_state *conn, struct select_state *selstate)
1191 {
1192     int nread;
1193
1194     nread = recv(conn->fd, conn->in.buf, conn->in.bufsize, 0);
1195     if (nread < 0) {
1196         TRACE_SENDTO_KDC_UDP_ERROR_RECV(context, &conn->addr, SOCKET_ERRNO);
1197         kill_conn(context, conn, selstate);
1198         return FALSE;
1199     }
1200     conn->in.pos = nread;
1201     return TRUE;
1202 }
1203
1204 /* Set up conn->http.tls.  Return true on success. */
1205 static krb5_boolean
1206 setup_tls(krb5_context context, const krb5_data *realm,
1207           struct conn_state *conn, struct select_state *selstate)
1208 {
1209     krb5_error_code ret;
1210     krb5_boolean ok = FALSE;
1211     char **anchors = NULL, *realmstr = NULL;
1212     const char *names[4];
1213
1214     if (init_tls_vtable(context) != 0 || context->tls->setup == NULL)
1215         return FALSE;
1216
1217     realmstr = k5memdup0(realm->data, realm->length, &ret);
1218     if (realmstr == NULL)
1219         goto cleanup;
1220
1221     /* Load the configured anchors. */
1222     names[0] = KRB5_CONF_REALMS;
1223     names[1] = realmstr;
1224     names[2] = KRB5_CONF_HTTP_ANCHORS;
1225     names[3] = NULL;
1226     ret = profile_get_values(context->profile, names, &anchors);
1227     if (ret != 0 && ret != PROF_NO_RELATION)
1228         goto cleanup;
1229
1230     if (context->tls->setup(context, conn->fd, conn->http.servername, anchors,
1231                             &conn->http.tls) != 0) {
1232         TRACE_SENDTO_KDC_HTTPS_ERROR_CONNECT(context, &conn->addr);
1233         goto cleanup;
1234     }
1235
1236     ok = TRUE;
1237
1238 cleanup:
1239     free(realmstr);
1240     profile_free_list(anchors);
1241     return ok;
1242 }
1243
1244 /* Set conn->state to READING when done; otherwise, call a cm_set_. */
1245 static krb5_boolean
1246 service_https_write(krb5_context context, const krb5_data *realm,
1247                     struct conn_state *conn, struct select_state *selstate)
1248 {
1249     k5_tls_status st;
1250
1251     /* If this is our first time in here, set up the SSL context. */
1252     if (conn->http.tls == NULL && !setup_tls(context, realm, conn, selstate)) {
1253         kill_conn(context, conn, selstate);
1254         return FALSE;
1255     }
1256
1257     /* Try to transmit our request to the server. */
1258     st = context->tls->write(context, conn->http.tls, SG_BUF(conn->out.sgp),
1259                              SG_LEN(conn->out.sgbuf));
1260     if (st == DONE) {
1261         TRACE_SENDTO_KDC_HTTPS_SEND(context, &conn->addr);
1262         cm_read(selstate, conn->fd);
1263         conn->state = READING;
1264     } else if (st == WANT_READ) {
1265         cm_read(selstate, conn->fd);
1266     } else if (st == WANT_WRITE) {
1267         cm_write(selstate, conn->fd);
1268     } else if (st == ERROR_TLS) {
1269         TRACE_SENDTO_KDC_HTTPS_ERROR_SEND(context, &conn->addr);
1270         kill_conn(context, conn, selstate);
1271     }
1272
1273     return FALSE;
1274 }
1275
1276 /* Return true on finished data.  Call a cm_read/write function and return
1277  * false if the TLS layer needs it.  Kill the connection on error. */
1278 static krb5_boolean
1279 https_read_bytes(krb5_context context, struct conn_state *conn,
1280                  struct select_state *selstate)
1281 {
1282     size_t bufsize, nread;
1283     k5_tls_status st;
1284     char *tmp;
1285     struct incoming_message *in = &conn->in;
1286
1287     for (;;) {
1288         if (in->buf == NULL || in->bufsize - in->pos < 1024) {
1289             bufsize = in->bufsize ? in->bufsize * 2 : 8192;
1290             if (bufsize > 1024 * 1024) {
1291                 kill_conn(context, conn, selstate);
1292                 return FALSE;
1293             }
1294             tmp = realloc(in->buf, bufsize);
1295             if (tmp == NULL) {
1296                 kill_conn(context, conn, selstate);
1297                 return FALSE;
1298             }
1299             in->buf = tmp;
1300             in->bufsize = bufsize;
1301         }
1302
1303         st = context->tls->read(context, conn->http.tls, &in->buf[in->pos],
1304                                 in->bufsize - in->pos - 1, &nread);
1305         if (st != DATA_READ)
1306             break;
1307
1308         in->pos += nread;
1309         in->buf[in->pos] = '\0';
1310     }
1311
1312     if (st == DONE)
1313         return TRUE;
1314
1315     if (st == WANT_READ) {
1316         cm_read(selstate, conn->fd);
1317     } else if (st == WANT_WRITE) {
1318         cm_write(selstate, conn->fd);
1319     } else if (st == ERROR_TLS) {
1320         TRACE_SENDTO_KDC_HTTPS_ERROR_RECV(context, &conn->addr);
1321         kill_conn(context, conn, selstate);
1322     }
1323     return FALSE;
1324 }
1325
1326 /* Return true on readable, valid KKDCPP data. */
1327 static krb5_boolean
1328 service_https_read(krb5_context context, const krb5_data *realm,
1329                    struct conn_state *conn, struct select_state *selstate)
1330 {
1331     krb5_kkdcp_message *pm = NULL;
1332     krb5_data buf;
1333     const char *rep;
1334     struct incoming_message *in = &conn->in;
1335
1336     /* Read data through the encryption layer. */
1337     if (!https_read_bytes(context, conn, selstate))
1338         return FALSE;
1339
1340     /* Find the beginning of the response body. */
1341     rep = strstr(in->buf, "\r\n\r\n");
1342     if (rep == NULL)
1343         goto kill_conn;
1344     rep += 4;
1345
1346     /* Decode the response. */
1347     buf = make_data((char *)rep, in->pos - (rep - in->buf));
1348     if (decode_krb5_kkdcp_message(&buf, &pm) != 0)
1349         goto kill_conn;
1350
1351     /* Check and discard the message length at the front of the kerb_message
1352      * field after decoding.  If it's wrong or missing, something broke. */
1353     if (pm->kerb_message.length < 4 ||
1354         load_32_be(pm->kerb_message.data) != pm->kerb_message.length - 4) {
1355         goto kill_conn;
1356     }
1357
1358     /* Replace all of the content that we read back with just the message. */
1359     memcpy(in->buf, pm->kerb_message.data + 4, pm->kerb_message.length - 4);
1360     in->pos = pm->kerb_message.length - 4;
1361     k5_free_kkdcp_message(context, pm);
1362
1363     return TRUE;
1364
1365 kill_conn:
1366     TRACE_SENDTO_KDC_HTTPS_ERROR(context, in->buf);
1367     k5_free_kkdcp_message(context, pm);
1368     kill_conn(context, conn, selstate);
1369     return FALSE;
1370 }
1371
1372 /* Return the maximum of endtime and the endtime fields of all currently active
1373  * TCP connections. */
1374 static time_ms
1375 get_endtime(time_ms endtime, struct conn_state *conns)
1376 {
1377     struct conn_state *state;
1378
1379     for (state = conns; state != NULL; state = state->next) {
1380         if ((state->state == READING || state->state == WRITING) &&
1381             state->endtime > endtime)
1382             endtime = state->endtime;
1383     }
1384     return endtime;
1385 }
1386
1387 static krb5_boolean
1388 service_fds(krb5_context context, struct select_state *selstate,
1389             time_ms interval, struct conn_state *conns,
1390             struct select_state *seltemp, const krb5_data *realm,
1391             int (*msg_handler)(krb5_context, const krb5_data *, void *),
1392             void *msg_handler_data, struct conn_state **winner_out)
1393 {
1394     int e, selret = 0;
1395     time_ms endtime;
1396     struct conn_state *state;
1397
1398     *winner_out = NULL;
1399
1400     e = get_curtime_ms(&endtime);
1401     if (e)
1402         return TRUE;
1403     endtime += interval;
1404
1405     e = 0;
1406     while (selstate->nfds > 0) {
1407         e = cm_select_or_poll(selstate, get_endtime(endtime, conns),
1408                               seltemp, &selret);
1409         if (e == EINTR)
1410             continue;
1411         if (e != 0)
1412             break;
1413
1414         if (selret == 0)
1415             /* Timeout, return to caller.  */
1416             return FALSE;
1417
1418         /* Got something on a socket, process it.  */
1419         for (state = conns; state != NULL; state = state->next) {
1420             int ssflags;
1421
1422             if (state->fd == INVALID_SOCKET)
1423                 continue;
1424             ssflags = cm_get_ssflags(seltemp, state->fd);
1425             if (!ssflags)
1426                 continue;
1427
1428             if (service_dispatch(context, realm, state, selstate, ssflags)) {
1429                 int stop = 1;
1430
1431                 if (msg_handler != NULL) {
1432                     krb5_data reply = make_data(state->in.buf, state->in.pos);
1433
1434                     stop = (msg_handler(context, &reply, msg_handler_data) != 0);
1435                 }
1436
1437                 if (stop) {
1438                     *winner_out = state;
1439                     return TRUE;
1440                 }
1441             }
1442         }
1443     }
1444     if (e != 0)
1445         return TRUE;
1446     return FALSE;
1447 }
1448
1449 /*
1450  * Current worst-case timeout behavior:
1451  *
1452  * First pass, 1s per udp or tcp server, plus 2s at end.
1453  * Second pass, 1s per udp server, plus 4s.
1454  * Third pass, 1s per udp server, plus 8s.
1455  * Fourth => 16s, etc.
1456  *
1457  * Restated:
1458  * Per UDP server, 1s per pass.
1459  * Per TCP server, 1s.
1460  * Backoff delay, 2**(P+1) - 2, where P is total number of passes.
1461  *
1462  * Total = 2**(P+1) + U*P + T - 2.
1463  *
1464  * If P=3, Total = 3*U + T + 14.
1465  * If P=4, Total = 4*U + T + 30.
1466  *
1467  * Note that if you try to reach two ports on one server, it counts as two.
1468  *
1469  * There is one exception to the above rules.  Whenever a TCP connection is
1470  * established, we wait up to ten seconds for it to finish or fail before
1471  * moving on.  This reduces network traffic significantly in a TCP environment.
1472  */
1473
1474 krb5_error_code
1475 k5_sendto(krb5_context context, const krb5_data *message,
1476           const krb5_data *realm, const struct serverlist *servers,
1477           k5_transport_strategy strategy,
1478           struct sendto_callback_info* callback_info, krb5_data *reply,
1479           struct sockaddr *remoteaddr, socklen_t *remoteaddrlen,
1480           int *server_used,
1481           /* return 0 -> keep going, 1 -> quit */
1482           int (*msg_handler)(krb5_context, const krb5_data *, void *),
1483           void *msg_handler_data)
1484 {
1485     int pass;
1486     time_ms delay;
1487     krb5_error_code retval;
1488     struct conn_state *conns = NULL, *state, **tailptr, *next, *winner;
1489     size_t s;
1490     struct select_state *sel_state = NULL, *seltemp;
1491     char *udpbuf = NULL;
1492     krb5_boolean done = FALSE;
1493
1494     *reply = empty_data();
1495
1496     /* One for use here, listing all our fds in use, and one for
1497      * temporary use in service_fds, for the fds of interest.  */
1498     sel_state = malloc(2 * sizeof(*sel_state));
1499     if (sel_state == NULL) {
1500         retval = ENOMEM;
1501         goto cleanup;
1502     }
1503     seltemp = &sel_state[1];
1504     cm_init_selstate(sel_state);
1505
1506     /* First pass: resolve server hosts, communicate with resulting addresses
1507      * of the preferred transport, and wait 1s for an answer from each. */
1508     for (s = 0; s < servers->nservers && !done; s++) {
1509         /* Find the current tail pointer. */
1510         for (tailptr = &conns; *tailptr != NULL; tailptr = &(*tailptr)->next);
1511         retval = resolve_server(context, realm, servers, s, strategy, message,
1512                                 &udpbuf, &conns);
1513         if (retval)
1514             goto cleanup;
1515         for (state = *tailptr; state != NULL && !done; state = state->next) {
1516             /* Contact each new connection, deferring those which use the
1517              * non-preferred RFC 4120 transport. */
1518             if (state->defer)
1519                 continue;
1520             if (maybe_send(context, state, message, sel_state, realm,
1521                            callback_info))
1522                 continue;
1523             done = service_fds(context, sel_state, 1000, conns, seltemp,
1524                                realm, msg_handler, msg_handler_data, &winner);
1525         }
1526     }
1527
1528     /* Complete the first pass by contacting servers of the non-preferred RFC
1529      * 4120 transport (if given), waiting 1s for an answer from each. */
1530     for (state = conns; state != NULL && !done; state = state->next) {
1531         if (!state->defer)
1532             continue;
1533         if (maybe_send(context, state, message, sel_state, realm,
1534                        callback_info))
1535             continue;
1536         done = service_fds(context, sel_state, 1000, conns, seltemp,
1537                            realm, msg_handler, msg_handler_data, &winner);
1538     }
1539
1540     /* Wait for two seconds at the end of the first pass. */
1541     if (!done) {
1542         done = service_fds(context, sel_state, 2000, conns, seltemp,
1543                            realm, msg_handler, msg_handler_data, &winner);
1544     }
1545
1546     /* Make remaining passes over all of the connections. */
1547     delay = 4000;
1548     for (pass = 1; pass < MAX_PASS && !done; pass++) {
1549         for (state = conns; state != NULL && !done; state = state->next) {
1550             if (maybe_send(context, state, message, sel_state, realm,
1551                            callback_info))
1552                 continue;
1553             done = service_fds(context, sel_state, 1000, conns, seltemp,
1554                                realm, msg_handler, msg_handler_data, &winner);
1555             if (sel_state->nfds == 0)
1556                 break;
1557         }
1558         /* Wait for the delay backoff at the end of this pass. */
1559         if (!done) {
1560             done = service_fds(context, sel_state, delay, conns, seltemp,
1561                                realm, msg_handler, msg_handler_data, &winner);
1562         }
1563         if (sel_state->nfds == 0)
1564             break;
1565         delay *= 2;
1566     }
1567
1568     if (sel_state->nfds == 0 || !done || winner == NULL) {
1569         retval = KRB5_KDC_UNREACH;
1570         goto cleanup;
1571     }
1572     /* Success!  */
1573     *reply = make_data(winner->in.buf, winner->in.pos);
1574     retval = 0;
1575     winner->in.buf = NULL;
1576     if (server_used != NULL)
1577         *server_used = winner->server_index;
1578     if (remoteaddr != NULL && remoteaddrlen != 0 && *remoteaddrlen > 0)
1579         (void)getpeername(winner->fd, remoteaddr, remoteaddrlen);
1580     TRACE_SENDTO_KDC_RESPONSE(context, reply->length, &winner->addr);
1581
1582 cleanup:
1583     for (state = conns; state != NULL; state = next) {
1584         next = state->next;
1585         if (state->fd != INVALID_SOCKET) {
1586             if (socktype_for_transport(state->addr.transport) == SOCK_STREAM)
1587                 TRACE_SENDTO_KDC_TCP_DISCONNECT(context, &state->addr);
1588             closesocket(state->fd);
1589             free_http_tls_data(context, state);
1590         }
1591         if (state->in.buf != udpbuf)
1592             free(state->in.buf);
1593         if (callback_info) {
1594             callback_info->pfn_cleanup(callback_info->data,
1595                                        &state->callback_buffer);
1596         }
1597         free(state);
1598     }
1599
1600     if (reply->data != udpbuf)
1601         free(udpbuf);
1602     free(sel_state);
1603     return retval;
1604 }