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